Skip to content

Source Files

The reverse engineering effort produced several source files that document the RS-UV3 V2.4a firmware internals. All files are available in the project repository.

src/rs-uv3-v2.4.asm — 19,053 lines

The complete firmware in PIC18 assembly, annotated for readability:

  • 97 named functions with descriptive labels
  • 34 section headers dividing the code into logical regions
  • 71 RAM variable definitions with meaningful names
  • 247 ASCII character annotations on XOR comparisons
  • Byte-identical to original — verified via make verify
; Example: Command dispatcher entry
cmd_dispatch_a: ; address: 0x0042b0
movff 0x59a, FSR0L ; Load serial buffer pointer
movff 0x59b, FSR0H
movf POSTINC0, W ; Read first command character
xorlw 0x50 ; 'P'
btfss STATUS, Z
bra not_P_command
; ... handle P commands (PD, PW) ...

src/rs-uv3-v2.4-readable.c — 12,020 lines

Ghidra’s decompiled C output, cleaned up for human readability:

  • 89 function renames from FUN_CODE_XXXXXX to descriptive names
  • 130 variable renames from DAT_DATA_XXXX to register names
  • 34 section headers matching the assembly
  • 35 algorithm annotations explaining complex routines
// Example: Frequency setting function
void set_frequency(void) {
// Calculate PLL divider from frequency in kHz
math_op_a = freq_main;
math_op_b = 0x00001388; // 5000 (scaling factor)
math_multiply_32();
// Write to RDA1846S via I2C
i2c_addr = 0x29; // RX frequency register
i2c_data = math_result >> 16;
i2c_write_word();
// ...
}

analysis/annotate.py — 250 lines

Transforms raw gpdasm output into annotated assembly:

  • Applies the code/data boundary (code ends at 0x8B38)
  • Replaces misinterpreted data bytes with db directives
  • Adds ASCII character annotations to XOR/SUB instructions
  • Inserts function labels from label-mapping.py
# Key function: annotate_instruction
def annotate_instruction(line, address):
# Add ASCII annotation to xorlw/sublw
if 'xorlw' in line or 'sublw' in line:
match = re.search(r'0x([0-9a-f]{2})', line)
if match:
value = int(match.group(1), 16)
if 0x20 <= value <= 0x7E:
return f"{line.rstrip():<52}; '{chr(value)}'"
return line

analysis/c-port.py — 690 lines

Transforms Ghidra’s decompiled C into readable code:

  • Renames functions using mappings from label-mapping.py
  • Renames data references to meaningful variable names
  • Handles thunk functions (trampolines to real functions)
  • Adds section headers and documentation comments
# Thunk handling
THUNK_NAMES = {
"thunk_FUN_CODE_001344": "thunk_parse_number",
"thunk_FUN_CODE_002aee": "thunk_set_baud_rate",
# ...
}

analysis/label-mapping.py — 241 lines

Central database of all named symbols:

  • Function names — 97 entries with addresses and descriptions
  • Thunk mappings — 4 trampolines to their targets
  • RAM variables — 71 data addresses with names
  • Section boundaries — 34 code region markers
FUNCTION_NAMES = {
"function_001": ("reset_vector", "0x000000", "Reset vector - jumps to init"),
"function_002": ("high_isr", "0x000008", "High-priority interrupt"),
"function_003": ("low_isr", "0x000018", "Low-priority interrupt"),
"function_004": ("init_radio", "0x000400", "Main init and event loop"),
"function_005": ("cmd_dispatch_a", "0x0042b0", "Dispatcher A: PD,PW,RC,RR,RS,ST,CP,FD,BL"),
"function_006": ("cmd_dispatch_b", "0x005972", "Dispatcher B: AF through X1"),
# ... 91 more entries
}

src/Makefile

Builds and verifies the assembly:

TARGET = rs-uv3-v2.4.hex
ORIGBIN = ../downloads/firmware/RS-UV3_V2-4.bin
build:
gpasm -p p18f46j11 -o $(TARGET) rs-uv3-v2.4.asm
verify: build
@objcopy -I ihex -O binary $(TARGET) /tmp/rs-uv3-verify.bin
@cmp $(ORIGBIN) /tmp/rs-uv3-verify.bin && \
echo "MATCH: Output identical to original firmware" || \
(echo "MISMATCH: Differences found"; exit 1)
clean:
rm -f $(TARGET) *.lst *.cod

Run make verify to confirm the assembly produces byte-identical output.

FileDescription
analysis/command-map.mdComplete command dispatch documentation
analysis/communicator-analysis.mdWindows control software analysis
analysis/collaboration-post.mdReverse engineering project summary
PathContents
ghidra-project/RS-UV3-firmware.gprGhidra project file
ghidra-project/RS-UV3-firmware.rep/Analysis database
ghidra-project/export/Exported listings and decompilation
FileDescription
downloads/firmware/RS-UV3_V2-4.hexOriginal Intel HEX
downloads/firmware/RS-UV3_V2-4.binRaw binary (for verification)

The annotated assembly is verified byte-identical to the original firmware after every modification:

Terminal window
$ cd src && make verify
gpasm -p p18f46j11 -o rs-uv3-v2.4.hex rs-uv3-v2.4.asm
MATCH: Output identical to original firmware

Labels, comments, and section headers don’t generate machine code — they’re stripped during assembly. This allows aggressive annotation without any risk of changing the firmware behavior.

To add annotations or improve the documentation:

  1. Edit analysis/label-mapping.py to add/rename symbols
  2. Run python analysis/annotate.py to regenerate assembly
  3. Run make verify to confirm no byte changes
  4. Run python analysis/c-port.py to regenerate C port

The verification step is critical — it ensures your changes don’t accidentally affect the assembled output.