Industrializing the disassembly of an undocumented processor from a raw binary is a complex task that can be broken down into four key steps:
- Verify that the binary does not belong to a known processor.
- Verify that the binary is not obfuscated, compressed, or encrypted code for a known processor.
- Build an undocumented processor generator.
- Create the analysis pipeline and custom disassembler generation process.
For the first phase of this project, the goal is to build dedicated, lightweight disassemblers—since, for bare-metal binaries, tools like Ghidra require manual processor target selection before analysis can begin.
1. Why Build a Custom Disassembler?
To determine whether a binary was compiled for a specific architecture, the strategy consists of disassembling the binary (both statically and dynamically) against candidate instruction sets until:
- One or more bytes fail to match any valid instruction for that architecture, allowing us to rule it out.
- The disassembly succeeds completely. (Note: a successful disassembly does not guarantee that the binary was originally intended for that CPU; control flow validity must also be verified).
Static disassembly is the first line of defense. However, if it fails due to obfuscation, compression, or encryption, we must escalate to dynamic execution and analysis.
Only after systematically eliminating all known architectures can we confidently conclude that we are dealing with a custom or undocumented processor.
2. How to Build Your Custom Disassembler
Before deploying heavy machinery for undocumented processors, the logical first step was to check against known architectures.
Approach 1: Ghidra and SLAgh
Ghidra relies on the SLAgh specification language and maintains an extensive library of processor definitions. The original plan was to leverage its API to extract a normalized opcode mapping table.
However, after several attempts, Ghidra proved unsuitable for this specific pipeline for two reasons:
- Operand Type Loss: Detailed metadata regarding operand types is lost or abstract during Ghidra's generic disassembly phase.
-
Lack of Specification Standardization: Across different processor modules,
.slaspecfiles are not uniformly structured.
Ghidra is a remarkable tool, and its underlying codebase is a work of art. But when required metadata is missing from the .slaspec or .pspec definitions, it must be added manually. At that point, implementing a dedicated, lightweight disassembler becomes a far more practical alternative.
Approach 2: Native 8051 Disassemblers (dis51)
The second attempt involved generating a raw .HEX file containing all possible byte combinations and passing it through dis51.
This approach failed because dis51 is an execution-tracing disassembler: it follows control flow rather than performing linear sweeping. If a JMP instruction branches backward, any bytes immediately following the jump that are not reached by other execution paths are categorized as raw data blocks. To effectively use dis51, one cannot simply feed it a linear array of opcodes; it expects a valid, structured program flow.
Approach 3: LLM-Assisted Table Normalization (Successful)
The winning strategy was prompting Gemini to generate the normalized instruction mapping table. After refining the prompt, the model generated a Python script containing the full opcode mapping alongside a processor-specific lookup table for Special Function Registers (SFR).
Using this generated table, writing the functional static disassembler took under an hour. It also laid the foundation for the dynamic disassembly simulator. While the table contained a few minor bugs, the time saved was substantial.
Although this approach is not fully generic out-of-the-box, it is straightforward. The core structure of the disassembler and execution loop remains virtually identical when porting to other architectures. Furthermore, building small, single-purpose utilities makes parallelizing multi-architecture scanning trivial.
3. Epilogue
This experiment provides a clear demonstration of how Large Language Models (LLMs) can accelerate lower-level systems development and reverse engineering tasks by handling structural boilerplate without sacrificing control over execution logic.
4. Disassembler Python Code
python
import re
INSTRUCTION_TABLE = {
# -------------------------------------------------------------------------
# 1 BYTE INSTRUCTIONS
# -------------------------------------------------------------------------
0x00: (1, "NOP", "NOP"),
0x03: (1, "RR A", "RR A"),
0x04: (1, "INC A", "INC A"),
0x06: (1, "INC @R0", "INC @R0"),
0x07: (1, "INC @R1", "INC @R1"),
0x08: (1, "INC R0", "INC register"),
0x09: (1, "INC R1", "INC register"),
0x0A: (1, "INC R2", "INC register"),
0x0B: (1, "INC R3", "INC register"),
0x0C: (1, "INC R4", "INC register"),
0x0D: (1, "INC R5", "INC register"),
0x0E: (1, "INC R6", "INC register"),
0x0F: (1, "INC R7", "INC register"),
0x13: (1, "RRC A", "RRC A"),
0x14: (1, "DEC A", "DEC A"),
0x16: (1, "DEC @R0", "DEC @R0"),
0x17: (1, "DEC @R1", "DEC @R1"),
0x18: (1, "DEC R0", "DEC register"),
0x19: (1, "DEC R1", "DEC register"),
0x1A: (1, "DEC R2", "DEC register"),
0x1B: (1, "DEC R3", "DEC register"),
0x1C: (1, "DEC R4", "DEC register"),
0x1D: (1, "DEC R5", "DEC register"),
0x1E: (1, "DEC R6", "DEC register"),
0x1F: (1, "DEC R7", "DEC register"),
0x22: (1, "RET", "RET"),
0x23: (1, "RL A", "RL A"),
0x26: (1, "ADD A, @R0", "ADD A, @R0"),
0x27: (1, "ADD A, @R1", "ADD A, @R1"),
0x28: (1, "ADD A, R0", "ADD A, register"),
0x29: (1, "ADD A, R1", "ADD A, register"),
0x2A: (1, "ADD A, R2", "ADD A, register"),
0x2B: (1, "ADD A, R3", "ADD A, register"),
0x2C: (1, "ADD A, R4", "ADD A, register"),
0x2D: (1, "ADD A, R5", "ADD A, register"),
0x2E: (1, "ADD A, R6", "ADD A, register"),
0x2F: (1, "ADD A, R7", "ADD A, register"),
0x32: (1, "RETI", "RETI"),
0x33: (1, "RLC A", "RLC A"),
0x36: (1, "ADDC A, @R0", "ADDC A, @R0"),
0x37: (1, "ADDC A, @R1", "ADDC A, @R1"),
0x38: (1, "ADDC A, R0", "ADDC A, register"),
0x39: (1, "ADDC A, R1", "ADDC A, register"),
0x3A: (1, "ADDC A, R2", "ADDC A, register"),
0x3B: (1, "ADDC A, R3", "ADDC A, register"),
0x3C: (1, "ADDC A, R4", "ADDC A, register"),
0x3D: (1, "ADDC A, R5", "ADDC A, register"),
0x3E: (1, "ADDC A, R6", "ADDC A, register"),
0x3F: (1, "ADDC A, R7", "ADDC A, register"),
0x46: (1, "ORL A, @R0", "ORL A, @R0"),
0x47: (1, "ORL A, @R1", "ORL A, @R1"),
0x48: (1, "ORL A, R0", "ORL A, register"),
0x49: (1, "ORL A, R1", "ORL A, register"),
0x4A: (1, "ORL A, R2", "ORL A, register"),
0x4B: (1, "ORL A, R3", "ORL A, register"),
0x4C: (1, "ORL A, R4", "ORL A, register"),
0x4D: (1, "ORL A, R5", "ORL A, register"),
0x4E: (1, "ORL A, R6", "ORL A, register"),
0x4F: (1, "ORL A, R7", "ORL A, register"),
0x56: (1, "ANL A, @R0", "ANL A, @R0"),
0x57: (1, "ANL A, @R1", "ANL A, @R1"),
0x58: (1, "ANL A, R0", "ANL A, register"),
0x59: (1, "ANL A, R1", "ANL A, register"),
0x5A: (1, "ANL A, R2", "ANL A, register"),
0x5B: (1, "ANL A, R3", "ANL A, register"),
0x5C: (1, "ANL A, R4", "ANL A, register"),
0x5D: (1, "ANL A, R5", "ANL A, register"),
0x5E: (1, "ANL A, R6", "ANL A, register"),
0x5F: (1, "ANL A, R7", "ANL A, register"),
0x66: (1, "XRL A, @R0", "XRL A, @R0"),
0x67: (1, "XRL A, @R1", "XRL A, @R1"),
0x68: (1, "XRL A, R0", "XRL A, register"),
0x69: (1, "XRL A, R1", "XRL A, register"),
0x6A: (1, "XRL A, R2", "XRL A, register"),
0x6B: (1, "XRL A, R3", "XRL A, register"),
0x6C: (1, "XRL A, R4", "XRL A, register"),
0x6D: (1, "XRL A, R5", "XRL A, register"),
0x6E: (1, "XRL A, R6", "XRL A, register"),
0x6F: (1, "XRL A, R7", "XRL A, register"),
0x73: (1, "JMP @A+DPTR", "JMP @A+DPTR"),
0x83: (1, "MOVC A, @A+PC", "MOVC A, @A+PC"),
0x84: (1, "DIV AB", "DIV AB"),
0x93: (1, "MOVC A, @A+DPTR", "MOVC A, @A+DPTR"),
0x96: (1, "SUBB A, @R0", "SUBB A, @R0"),
0x97: (1, "SUBB A, @R1", "SUBB A, @R1"),
0x98: (1, "SUBB A, R0", "SUBB A, register"),
0x99: (1, "SUBB A, R1", "SUBB A, register"),
0x9A: (1, "SUBB A, R2", "SUBB A, register"),
0x9B: (1, "SUBB A, R3", "SUBB A, register"),
0x9C: (1, "SUBB A, R4", "SUBB A, register"),
0x9D: (1, "SUBB A, R5", "SUBB A, register"),
0x9E: (1, "SUBB A, R6", "SUBB A, register"),
0x9F: (1, "SUBB A, R7", "SUBB A, register"),
0xA3: (1, "INC DPTR", "INC register"),
0xA4: (1, "MUL AB", "MUL AB"),
0xC3: (1, "CLR C", "CLR C"),
0xC4: (1, "SWAP A", "SWAP A"),
0xC6: (1, "XCH A, @R0", "XCH A, @R0"),
0xC7: (1, "XCH A, @R1", "XCH A, @R1"),
0xC8: (1, "XCH A, R0", "XCH A, register"),
0xC9: (1, "XCH A, R1", "XCH A, register"),
0xCA: (1, "XCH A, R2", "XCH A, register"),
0xCB: (1, "XCH A, R3", "XCH A, register"),
0xCC: (1, "XCH A, R4", "XCH A, register"),
0xCD: (1, "XCH A, R5", "XCH A, register"),
0xCE: (1, "XCH A, R6", "XCH A, register"),
0xCF: (1, "XCH A, R7", "XCH A, register"),
0xD3: (1, "SETB C", "SETB C"),
0xD4: (1, "DA A", "DA A"),
0xD6: (1, "XCHD A, @R0", "XCHD A, @R0"),
0xD7: (1, "XCHD A, @R1", "XCHD A, @R1"),
0xE4: (1, "CLR A", "CLR A"),
0xE6: (1, "MOV A, @R0", "MOV A, @R0"),
0xE7: (1, "MOV A, @R1", "MOV A, @R1"),
0xE8: (1, "MOV A, R0", "MOV A, register"),
0xE9: (1, "MOV A, R1", "MOV A, register"),
0xEA: (1, "MOV A, R2", "MOV A, register"),
0xEB: (1, "MOV A, R3", "MOV A, register"),
0xEC: (1, "MOV A, R4", "MOV A, register"),
0xED: (1, "MOV A, R5", "MOV A, register"),
0xEE: (1, "MOV A, R6", "MOV A, register"),
0xEF: (1, "MOV A, R7", "MOV A, register"),
0xF4: (1, "CPL A", "CPL A"),
0xF6: (1, "MOV @R0, A", "MOV @R0, A"),
0xF7: (1, "MOV @R1, A", "MOV @R1, A"),
0xF8: (1, "MOV R0, A", "MOV register, A"),
0xF9: (1, "MOV R1, A", "MOV register, A"),
0xFA: (1, "MOV R2, A", "MOV register, A"),
0xFB: (1, "MOV R3, A", "MOV register, A"),
0xFC: (1, "MOV R4, A", "MOV register, A"),
0xFD: (1, "MOV R5, A", "MOV register, A"),
0xFE: (1, "MOV R6, A", "MOV register, A"),
0xFF: (1, "MOV R7, A", "MOV register, A"),
# -------------------------------------------------------------------------
# 2 BYTE INSTRUCTIONS
# -------------------------------------------------------------------------
0x05: (2, lambda b: f"INC {b[1]:02X}h", "INC direct"),
0x15: (2, lambda b: f"DEC {b[1]:02X}h", "DEC direct"),
0x24: (2, lambda b: f"ADD A, #{b[1]:02X}h", "ADD A, #data"),
0x25: (2, lambda b: f"ADD A, {b[1]:02X}h", "ADD A, direct"),
0x34: (2, lambda b: f"ADDC A, #{b[1]:02X}h", "ADDC A, #data"),
0x35: (2, lambda b: f"ADDC A, {b[1]:02X}h", "ADDC A, direct"),
0x40: (2, lambda b: f"JC {b[1]:02X}h", "JC offset"),
0x44: (2, lambda b: f"ORL A, #{b[1]:02X}h", "ORL A, #data"),
0x45: (2, lambda b: f"ORL A, {b[1]:02X}h", "ORL A, direct"),
0x50: (2, lambda b: f"JNC {b[1]:02X}h", "JNC offset"),
0x54: (2, lambda b: f"ANL A, #{b[1]:02X}h", "ANL A, #data"),
0x55: (2, lambda b: f"ANL A, {b[1]:02X}h", "ANL A, direct"),
0x60: (2, lambda b: f"JZ {b[1]:02X}h", "JZ offset"),
0x64: (2, lambda b: f"XRL A, #{b[1]:02X}h", "XRL A, #data"),
0x65: (2, lambda b: f"XRL A, {b[1]:02X}h", "XRL A, direct"),
0x70: (2, lambda b: f"JNZ {b[1]:02X}h", "JNZ offset"),
0x74: (2, lambda b: f"MOV A, #{b[1]:02X}h", "MOV A, #data"),
0x76: (2, lambda b: f"MOV @R0, #{b[1]:02X}h", "MOV @R0, #data"),
0x77: (2, lambda b: f"MOV @R1, #{b[1]:02X}h", "MOV @R1, #data"),
0x78: (2, lambda b: f"MOV R0, #{b[1]:02X}h", "MOV register, #data"),
0x79: (2, lambda b: f"MOV R1, #{b[1]:02X}h", "MOV register, #data"),
0x7A: (2, lambda b: f"MOV R2, #{b[1]:02X}h", "MOV register, #data"),
0x7B: (2, lambda b: f"MOV R3, #{b[1]:02X}h", "MOV register, #data"),
0x7C: (2, lambda b: f"MOV R4, #{b[1]:02X}h", "MOV register, #data"),
0x7D: (2, lambda b: f"MOV R5, #{b[1]:02X}h", "MOV register, #data"),
0x7E: (2, lambda b: f"MOV R6, #{b[1]:02X}h", "MOV register, #data"),
0x7F: (2, lambda b: f"MOV R7, #{b[1]:02X}h", "MOV register, #data"),
0x80: (2, lambda b: f"SJMP {b[1]:02X}h", "SJMP offset"),
0x82: (2, lambda b: f"ANL C, {b[1]:02X}h", "ANL C, bit"),
0x86: (2, lambda b: f"MOV R0, {b[1]:02X}h", "MOV register, direct"),
0x87: (2, lambda b: f"MOV R1, {b[1]:02X}h", "MOV register, direct"),
0x88: (2, lambda b: f"MOV {b[1]:02X}h, R0", "MOV direct, register"),
0x89: (2, lambda b: f"MOV {b[1]:02X}h, R1", "MOV direct, register"),
0x8A: (2, lambda b: f"MOV {b[1]:02X}h, R2", "MOV direct, register"),
0x8B: (2, lambda b: f"MOV {b[1]:02X}h, R3", "MOV direct, register"),
0x8C: (2, lambda b: f"MOV {b[1]:02X}h, R4", "MOV direct, register"),
0x8D: (2, lambda b: f"MOV {b[1]:02X}h, R5", "MOV direct, register"),
0x8E: (2, lambda b: f"MOV {b[1]:02X}h, R6", "MOV direct, register"),
0x8F: (2, lambda b: f"MOV {b[1]:02X}h, R7", "MOV direct, register"),
0x92: (2, lambda b: f"MOV {b[1]:02X}h, C", "MOV bit, C"),
0x94: (2, lambda b: f"SUBB A, #{b[1]:02X}h", "SUBB A, #data"),
0x95: (2, lambda b: f"SUBB A, {b[1]:02X}h", "SUBB A, direct"),
0xA0: (2, lambda b: f"ORL C, /{b[1]:02X}h", "ORL C, /bit"),
0xA2: (2, lambda b: f"MOV C, {b[1]:02X}h", "MOV C, bit"),
0xA5: (1, "RESERVED (0xA5)", "RESERVED"), # Unassigned Intel Opcode
0xA6: (2, lambda b: f"MOV @R0, {b[1]:02X}h", "MOV @R0, direct"),
0xA7: (2, lambda b: f"MOV @R1, {b[1]:02X}h", "MOV @R1, direct"),
0xA8: (2, lambda b: f"MOV R0, {b[1]:02X}h", "MOV R0, direct"),
0xA9: (2, lambda b: f"MOV R1, {b[1]:02X}h", "MOV R1, direct"),
0xAA: (2, lambda b: f"MOV R2, {b[1]:02X}h", "MOV R2, direct"),
0xAB: (2, lambda b: f"MOV R3, {b[1]:02X}h", "MOV R3, direct"),
0xAC: (2, lambda b: f"MOV R4, {b[1]:02X}h", "MOV R4, direct"),
0xAD: (2, lambda b: f"MOV R5, {b[1]:02X}h", "MOV R5, direct"),
0xAE: (2, lambda b: f"MOV R6, {b[1]:02X}h", "MOV R6, direct"),
0xAF: (2, lambda b: f"MOV R7, {b[1]:02X}h", "MOV R7, direct"),
0xB0: (2, lambda b: f"ANL C, /{b[1]:02X}h", "ANL C, /bit"),
0xB2: (2, lambda b: f"CPL {b[1]:02X}h", "CPL bit"),
0xB3: (1, "CPL C", "CPL C"),
0xC0: (2, lambda b: f"PUSH {b[1]:02X}h", "PUSH direct"),
0xC2: (2, lambda b: f"CLR {b[1]:02X}h", "CLR bit"),
0xC5: (2, lambda b: f"XCH A, {b[1]:02X}h", "XCH A, direct"),
0xD0: (2, lambda b: f"POP {b[1]:02X}h", "POP direct"),
0xD2: (2, lambda b: f"SETB {b[1]:02X}h", "SETB bit"),
0xD8: (2, lambda b: f"DJNZ R0, {b[1]:02X}h", "DJNZ register, offset"),
0xD9: (2, lambda b: f"DJNZ R1, {b[1]:02X}h", "DJNZ register, offset"),
0xDA: (2, lambda b: f"DJNZ R2, {b[1]:02X}h", "DJNZ register, offset"),
0xDB: (2, lambda b: f"DJNZ R3, {b[1]:02X}h", "DJNZ register, offset"),
0xDC: (2, lambda b: f"DJNZ R4, {b[1]:02X}h", "DJNZ register, offset"),
0xDD: (2, lambda b: f"DJNZ R5, {b[1]:02X}h", "DJNZ register, offset"),
0xDE: (2, lambda b: f"DJNZ R6, {b[1]:02X}h", "DJNZ register, offset"),
0xDF: (2, lambda b: f"DJNZ R7, {b[1]:02X}h", "DJNZ register, offset"),
0xE0: (1, "MOVX A, @DPTR", "MOVX A, @DPTR"),
0xE2: (1, "MOVX A, @R0", "MOVX A, @R0"),
0xE3: (1, "MOVX A, @R1", "MOVX A, @R1"),
0xE5: (2, lambda b: f"MOV A, {b[1]:02X}h", "MOV A, direct"),
0xF0: (1, "MOVX @DPTR, A", "MOVX @DPTR, A"),
0xF2: (1, "MOVX @R0, A", "MOVX @R0, A"),
0xF3: (1, "MOVX @R1, A", "MOVX @R1, A"),
0xF5: (2, lambda b: f"MOV {b[1]:02X}h, A", "MOV direct, A"),
# -------------------------------------------------------------------------
# 3 BYTE INSTRUCTIONS (16-bit addresses & 3-parameter instructions)
# -------------------------------------------------------------------------
0x02: (3, lambda b: f"LJMP {b[1]:02X}{b[2]:02X}h", "LJMP addr16"),
0x10: (3, lambda b: f"JBC {b[1]:02X}h, {b[2]:02X}h", "JBC bit, offset"),
0x12: (3, lambda b: f"LCALL {b[1]:02X}{b[2]:02X}h", "LCALL addr16"),
0x20: (3, lambda b: f"JB {b[1]:02X}h, {b[2]:02X}h", "JB bit, offset"),
0x30: (3, lambda b: f"JNB {b[1]:02X}h, {b[2]:02X}h", "JNB bit, offset"),
0x42: (2, lambda b: f"ORL {b[1]:02X}h, A", "ORL direct, A"),
0x43: (3, lambda b: f"ORL {b[1]:02X}h, #{b[2]:02X}h", "ORL direct, #data"),
0x52: (2, lambda b: f"ANL {b[1]:02X}h, A", "ANL direct, A"),
0x53: (3, lambda b: f"ANL {b[1]:02X}h, #{b[2]:02X}h", "ANL direct, #data"),
0x62: (2, lambda b: f"XRL {b[1]:02X}h, A", "XRL direct, A"),
0x63: (3, lambda b: f"XRL {b[1]:02X}h, #{b[2]:02X}h", "XRL direct, #data"),
0x72: (2, lambda b: f"ORL C, {b[1]:02X}h", "ORL C, bit"),
0x75: (3, lambda b: f"MOV {b[1]:02X}h, #{b[2]:02X}h", "MOV direct, #data"),
0x85: (3, lambda b: f"MOV {b[2]:02X}h, {b[1]:02X}h", "MOV direct, direct"),
0x90: (3, lambda b: f"MOV DPTR, #{b[1]:02X}{b[2]:02X}h", "MOV DPTR, #data16"),
0xB4: (3, lambda b: f"CJNE A, #{b[1]:02X}h, {b[2]:02X}h", "CJNE A, #data, offset"),
0xB5: (3, lambda b: f"CJNE A, {b[1]:02X}h, {b[2]:02X}h", "CJNE A, direct, offset"),
0xB6: (3, lambda b: f"CJNE @R0, #{b[1]:02X}h, {b[2]:02X}h", "CJNE @R0, #data, offset"),
0xB7: (3, lambda b: f"CJNE @R1, #{b[1]:02X}h, {b[2]:02X}h", "CJNE @R1, #data, offset"),
0xB8: (3, lambda b: f"CJNE R0, #{b[1]:02X}h, {b[2]:02X}h", "CJNE register, #data, offset"),
0xB9: (3, lambda b: f"CJNE R1, #{b[1]:02X}h, {b[2]:02X}h", "CJNE register, #data, offset"),
0xBA: (3, lambda b: f"CJNE R2, #{b[1]:02X}h, {b[2]:02X}h", "CJNE register, #data, offset"),
0xBB: (3, lambda b: f"CJNE R3, #{b[1]:02X}h, {b[2]:02X}h", "CJNE register, #data, offset"),
0xBC: (3, lambda b: f"CJNE R4, #{b[1]:02X}h, {b[2]:02X}h", "CJNE register, #data, offset"),
0xBD: (3, lambda b: f"CJNE R5, #{b[1]:02X}h, {b[2]:02X}h", "CJNE register, #data, offset"),
0xBE: (3, lambda b: f"CJNE R6, #{b[1]:02X}h, {b[2]:02X}h", "CJNE register, #data, offset"),
0xBF: (3, lambda b: f"CJNE R7, #{b[1]:02X}h, {b[2]:02X}h", "CJNE register, #data, offset"),
0xD5: (3, lambda b: f"DJNZ {b[1]:02X}h, {b[2]:02X}h", "DJNZ direct, offset"),
}
sfr_dict = {
0x80: "P0", # Port 0
0x81: "SP", # Stack Pointer
0x82: "DPL", # Data Pointer Low
0x83: "DPH", # Data Pointer High
0x87: "PCON", # Power Control
0x88: "TCON", # Timer Control
0x89: "TMOD", # Timer Mode
0x8A: "TL0", # Timer 0 Low
0x8B: "TL1", # Timer 1 Low
0x8C: "TH0", # Timer 0 High
0x8D: "TH1", # Timer 1 High
0x90: "P1", # Port 1
0x98: "SCON", # Serial Control
0x99: "SBUF", # Serial Buffer
0xA0: "P2", # Port 2
0xA8: "IE", # Interrupt Enable
0xB0: "P3", # Port 3
0xB8: "IP", # Interrupt Priority
0xD0: "PSW", # Program Status Word
0xE0: "ACC", # Accumulator (A)
0xF0: "B", # B Register
}
def decode_bytes(byte_list):
"""Decodes a byte sequence according to the 8051 instruction set."""
first_byte = byte_list[0]
# Handle AJMP / ACALL instructions (Page addresses encoded in the upper opcode bits)
if (first_byte & 0x1F) == 0x01:
addr = ((first_byte & 0xE0) << 3) | byte_list[1]
return 2, f"AJMP {addr:04X}h", "AJMP addr11"
if (first_byte & 0x1F) == 0x11:
addr = ((first_byte & 0xE0) << 3) | byte_list[1]
return 2, f"ACALL {addr:04X}h", "ACALL addr11"
if first_byte in INSTRUCTION_TABLE:
length, asm, generic = INSTRUCTION_TABLE[first_byte]
if callable(asm):
asm = asm(byte_list)
return length, asm, generic
return len(byte_list), "UNKNOWN", "UNKNOWN"
def int_to_hex_4(number):
if not (0 <= number <= 65535):
raise ValueError("Number must be between 0 and 65535")
return format(number, '04x')
def buffer_to_hex_text(buffer: bytes) -> str:
return " ".join(f"{b:02X}" for b in buffer)
def process_opcodes(buffer, reset_vector=0):
rows = []
idx = 0
done = False
while not done:
address = reset_vector + idx
opcode = buffer[reset_vector + idx]
if opcode in INSTRUCTION_TABLE:
length, asm, generic = INSTRUCTION_TABLE[opcode]
byte_vals = buffer[reset_vector + idx : reset_vector + idx + length]
opcodes_str = buffer_to_hex_text(byte_vals)
length, asm, generic = decode_bytes(byte_vals)
rows.append({
"address": address,
"opcodes": opcodes_str,
"asm": asm,
"generic": generic
})
idx += length
if idx >= len(buffer):
done = True
else:
print(f"Opcode 0x{opcode:02X} not found in INSTRUCTION_TABLE")
done = True
return rows
if __name__ == "__main__":
input_file = "./test1_8051.rom"
with open(input_file, "rb") as fs:
buffer = fs.read()
result_rows = process_opcodes(buffer)
line_format = "{:<10} {:<20} {:<30}"
for r in result_rows:
print(line_format.format(int_to_hex_4(r['address']), r['opcodes'], r['asm']))
Top comments (0)