Moved from the vRIO repository to live with the other hardware restoration work. Contains the PD01D221 reverse-engineering reference (FIRMWARE.md, recovered from the TMS27PC512 EPROM dump) and the working Matrix Portal S3 + HUB75 replica firmware (flashed and bench-tested 2026-07-22, including the G/B pin swap for these panels, the PIN_BUTTON_UP/DOWN fix, and power-on cursor rendering). The vPLASMA reference oracle (src/VPlasma.Core) and the VPlasma.Wire differential-test tool stay in the vRIO repo; PlasmaNew pre-move commit history is there too. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
149 lines
8.5 KiB
Python
149 lines
8.5 KiB
Python
#!/usr/bin/env python3
|
|
"""Minimal-but-practical Motorola 68HC11 disassembler for the PD01D221 ROM.
|
|
CPU address == ROM offset for $8000-$FFFF (EPROM upper 32KB maps 1:1)."""
|
|
import sys
|
|
|
|
# addressing modes and their extra operand length (beyond opcode)
|
|
INH='inh'; IMM8='imm8'; IMM16='imm16'; DIR='dir'; EXT='ext'; IDX='idx'; IDY='idy'; REL='rel'
|
|
BITDIR='bitdir'; BITIDX='bitidx'; BITIDY='bitidy' # BSET/BCLR (mask)
|
|
BRDIR='brdir'; BRIDX='bridx'; BRIDY='bridy' # BRSET/BRCLR (mask+rel)
|
|
|
|
# page 0
|
|
P0 = {
|
|
0x00:('TEST',INH),0x01:('NOP',INH),0x02:('IDIV',INH),0x03:('FDIV',INH),
|
|
0x04:('LSRD',INH),0x05:('ASLD',INH),0x06:('TAP',INH),0x07:('TPA',INH),
|
|
0x08:('INX',INH),0x09:('DEX',INH),0x0A:('CLV',INH),0x0B:('SEV',INH),
|
|
0x0C:('CLC',INH),0x0D:('SEC',INH),0x0E:('CLI',INH),0x0F:('SEI',INH),
|
|
0x10:('SBA',INH),0x11:('CBA',INH),0x12:('BRSET',BRDIR),0x13:('BRCLR',BRDIR),
|
|
0x14:('BSET',BITDIR),0x15:('BCLR',BITDIR),0x16:('TAB',INH),0x17:('TBA',INH),
|
|
0x19:('DAA',INH),0x1B:('ABA',INH),
|
|
0x1C:('BSET',BITIDX),0x1D:('BCLR',BITIDX),0x1E:('BRSET',BRIDX),0x1F:('BRCLR',BRIDX),
|
|
0x20:('BRA',REL),0x21:('BRN',REL),0x22:('BHI',REL),0x23:('BLS',REL),
|
|
0x24:('BCC',REL),0x25:('BCS',REL),0x26:('BNE',REL),0x27:('BEQ',REL),
|
|
0x28:('BVC',REL),0x29:('BVS',REL),0x2A:('BPL',REL),0x2B:('BMI',REL),
|
|
0x2C:('BGE',REL),0x2D:('BLT',REL),0x2E:('BGT',REL),0x2F:('BLE',REL),
|
|
0x30:('TSX',INH),0x31:('INS',INH),0x32:('PULA',INH),0x33:('PULB',INH),
|
|
0x34:('DES',INH),0x35:('TXS',INH),0x36:('PSHA',INH),0x37:('PSHB',INH),
|
|
0x38:('PULX',INH),0x39:('RTS',INH),0x3A:('ABX',INH),0x3B:('RTI',INH),
|
|
0x3C:('PSHX',INH),0x3D:('MUL',INH),0x3E:('WAI',INH),0x3F:('SWI',INH),
|
|
0x40:('NEGA',INH),0x43:('COMA',INH),0x44:('LSRA',INH),0x46:('RORA',INH),
|
|
0x47:('ASRA',INH),0x48:('ASLA',INH),0x49:('ROLA',INH),0x4A:('DECA',INH),
|
|
0x4C:('INCA',INH),0x4D:('TSTA',INH),0x4F:('CLRA',INH),
|
|
0x50:('NEGB',INH),0x53:('COMB',INH),0x54:('LSRB',INH),0x56:('RORB',INH),
|
|
0x57:('ASRB',INH),0x58:('ASLB',INH),0x59:('ROLB',INH),0x5A:('DECB',INH),
|
|
0x5C:('INCB',INH),0x5D:('TSTB',INH),0x5F:('CLRB',INH),
|
|
0x60:('NEG',IDX),0x63:('COM',IDX),0x64:('LSR',IDX),0x66:('ROR',IDX),
|
|
0x67:('ASR',IDX),0x68:('ASL',IDX),0x69:('ROL',IDX),0x6A:('DEC',IDX),
|
|
0x6C:('INC',IDX),0x6D:('TST',IDX),0x6E:('JMP',IDX),0x6F:('CLR',IDX),
|
|
0x70:('NEG',EXT),0x73:('COM',EXT),0x74:('LSR',EXT),0x76:('ROR',EXT),
|
|
0x77:('ASR',EXT),0x78:('ASL',EXT),0x79:('ROL',EXT),0x7A:('DEC',EXT),
|
|
0x7C:('INC',EXT),0x7D:('TST',EXT),0x7E:('JMP',EXT),0x7F:('CLR',EXT),
|
|
0x80:('SUBA',IMM8),0x81:('CMPA',IMM8),0x82:('SBCA',IMM8),0x83:('SUBD',IMM16),
|
|
0x84:('ANDA',IMM8),0x85:('BITA',IMM8),0x86:('LDAA',IMM8),0x88:('EORA',IMM8),
|
|
0x89:('ADCA',IMM8),0x8A:('ORAA',IMM8),0x8B:('ADDA',IMM8),0x8C:('CPX',IMM16),
|
|
0x8D:('BSR',REL),0x8E:('LDS',IMM16),0x8F:('XGDX',INH),
|
|
0x90:('SUBA',DIR),0x91:('CMPA',DIR),0x92:('SBCA',DIR),0x93:('SUBD',DIR),
|
|
0x94:('ANDA',DIR),0x95:('BITA',DIR),0x96:('LDAA',DIR),0x97:('STAA',DIR),
|
|
0x98:('EORA',DIR),0x99:('ADCA',DIR),0x9A:('ORAA',DIR),0x9B:('ADDA',DIR),
|
|
0x9C:('CPX',DIR),0x9D:('JSR',DIR),0x9E:('LDS',DIR),0x9F:('STS',DIR),
|
|
0xA0:('SUBA',IDX),0xA1:('CMPA',IDX),0xA2:('SBCA',IDX),0xA3:('SUBD',IDX),
|
|
0xA4:('ANDA',IDX),0xA5:('BITA',IDX),0xA6:('LDAA',IDX),0xA7:('STAA',IDX),
|
|
0xA8:('EORA',IDX),0xA9:('ADCA',IDX),0xAA:('ORAA',IDX),0xAB:('ADDA',IDX),
|
|
0xAC:('CPX',IDX),0xAD:('JSR',IDX),0xAE:('LDS',IDX),0xAF:('STS',IDX),
|
|
0xB0:('SUBA',EXT),0xB1:('CMPA',EXT),0xB2:('SBCA',EXT),0xB3:('SUBD',EXT),
|
|
0xB4:('ANDA',EXT),0xB5:('BITA',EXT),0xB6:('LDAA',EXT),0xB7:('STAA',EXT),
|
|
0xB8:('EORA',EXT),0xB9:('ADCA',EXT),0xBA:('ORAA',EXT),0xBB:('ADDA',EXT),
|
|
0xBC:('CPX',EXT),0xBD:('JSR',EXT),0xBE:('LDS',EXT),0xBF:('STS',EXT),
|
|
0xC0:('SUBB',IMM8),0xC1:('CMPB',IMM8),0xC2:('SBCB',IMM8),0xC3:('ADDD',IMM16),
|
|
0xC4:('ANDB',IMM8),0xC5:('BITB',IMM8),0xC6:('LDAB',IMM8),0xC8:('EORB',IMM8),
|
|
0xC9:('ADCB',IMM8),0xCA:('ORAB',IMM8),0xCB:('ADDB',IMM8),0xCC:('LDD',IMM16),
|
|
0xCE:('LDX',IMM16),0xCF:('STOP',INH),
|
|
0xD0:('SUBB',DIR),0xD1:('CMPB',DIR),0xD2:('SBCB',DIR),0xD3:('ADDD',DIR),
|
|
0xD4:('ANDB',DIR),0xD5:('BITB',DIR),0xD6:('LDAB',DIR),0xD7:('STAB',DIR),
|
|
0xD8:('EORB',DIR),0xD9:('ADCB',DIR),0xDA:('ORAB',DIR),0xDB:('ADDB',DIR),
|
|
0xDC:('LDD',DIR),0xDD:('STD',DIR),0xDE:('LDX',DIR),0xDF:('STX',DIR),
|
|
0xE0:('SUBB',IDX),0xE1:('CMPB',IDX),0xE2:('SBCB',IDX),0xE3:('ADDD',IDX),
|
|
0xE4:('ANDB',IDX),0xE5:('BITB',IDX),0xE6:('LDAB',IDX),0xE7:('STAB',IDX),
|
|
0xE8:('EORB',IDX),0xE9:('ADCB',IDX),0xEA:('ORAB',IDX),0xEB:('ADDB',IDX),
|
|
0xEC:('LDD',IDX),0xED:('STD',IDX),0xEE:('LDX',IDX),0xEF:('STX',IDX),
|
|
0xF0:('SUBB',EXT),0xF1:('CMPB',EXT),0xF2:('SBCB',EXT),0xF3:('ADDD',EXT),
|
|
0xF4:('ANDB',EXT),0xF5:('BITB',EXT),0xF6:('LDAB',EXT),0xF7:('STAB',EXT),
|
|
0xF8:('EORB',EXT),0xF9:('ADCB',EXT),0xFA:('ORAB',EXT),0xFB:('ADDB',EXT),
|
|
0xFC:('LDD',EXT),0xFD:('STD',EXT),0xFE:('LDX',EXT),0xFF:('STX',EXT),
|
|
}
|
|
# page 1 ($18): Y versions
|
|
P1 = {
|
|
0x08:('INY',INH),0x09:('DEY',INH),0x1C:('BSET',BITIDY),0x1D:('BCLR',BITIDY),
|
|
0x1E:('BRSET',BRIDY),0x1F:('BRCLR',BRIDY),0x30:('TSY',INH),0x35:('TYS',INH),
|
|
0x38:('PULY',INH),0x3A:('ABY',INH),0x3C:('PSHY',INH),
|
|
0x60:('NEG',IDY),0x63:('COM',IDY),0x64:('LSR',IDY),0x66:('ROR',IDY),
|
|
0x67:('ASR',IDY),0x68:('ASL',IDY),0x69:('ROL',IDY),0x6A:('DEC',IDY),
|
|
0x6C:('INC',IDY),0x6D:('TST',IDY),0x6E:('JMP',IDY),0x6F:('CLR',IDY),
|
|
0x8C:('CPY',IMM16),0x8F:('XGDY',INH),0x9C:('CPY',DIR),
|
|
0xA0:('SUBA',IDY),0xA1:('CMPA',IDY),0xA2:('SBCA',IDY),0xA3:('SUBD',IDY),
|
|
0xA4:('ANDA',IDY),0xA5:('BITA',IDY),0xA6:('LDAA',IDY),0xA7:('STAA',IDY),
|
|
0xA8:('EORA',IDY),0xA9:('ADCA',IDY),0xAA:('ORAA',IDY),0xAB:('ADDA',IDY),
|
|
0xAC:('CPY',IDY),0xAD:('JSR',IDY),0xAE:('LDS',IDY),0xAF:('STS',IDY),
|
|
0xBC:('CPY',EXT),0xCE:('LDY',IMM16),0xDE:('LDY',DIR),0xDF:('STY',DIR),
|
|
0xE0:('SUBB',IDY),0xE1:('CMPB',IDY),0xE2:('SBCB',IDY),0xE3:('ADDD',IDY),
|
|
0xE4:('ANDB',IDY),0xE5:('BITB',IDY),0xE6:('LDAB',IDY),0xE7:('STAB',IDY),
|
|
0xE8:('EORB',IDY),0xE9:('ADCB',IDY),0xEA:('ORAB',IDY),0xEB:('ADDB',IDY),
|
|
0xEC:('LDD',IDY),0xED:('STD',IDY),0xEE:('LDY',IDY),0xEF:('STY',IDY),
|
|
0xFE:('LDY',EXT),0xFF:('STY',EXT),
|
|
}
|
|
# page 2 ($1A): CPD, and X/Y cross indexed
|
|
P2 = {0x83:('CPD',IMM16),0x93:('CPD',DIR),0xA3:('CPD',IDX),0xB3:('CPD',EXT),
|
|
0xAC:('CPY',IDX),0xEE:('LDY',IDX),0xEF:('STY',IDX)}
|
|
# page 4 ($CD): X/Y cross indexed
|
|
P4 = {0xA3:('CPD',IDY),0xAC:('CPX',IDY),0xEE:('LDX',IDY),0xEF:('STX',IDY)}
|
|
|
|
def disasm(rom, addr, count):
|
|
out=[]
|
|
for _ in range(count):
|
|
start=addr
|
|
op=rom[addr]; addr+=1
|
|
tab=P0; pfx=''
|
|
if op==0x18: pfx='18 '; op=rom[addr]; addr+=1; tab=P1
|
|
elif op==0x1A: pfx='1A '; op=rom[addr]; addr+=1; tab=P2
|
|
elif op==0xCD: pfx='CD '; op=rom[addr]; addr+=1; tab=P4
|
|
ent=tab.get(op)
|
|
if ent is None:
|
|
out.append((start,f".byte ${op:02X}",'?')); continue
|
|
mn,mode=ent; operand=''; tgt=None
|
|
if mode==INH: pass
|
|
elif mode==IMM8: operand=f"#${rom[addr]:02X}"; addr+=1
|
|
elif mode==IMM16: operand=f"#${(rom[addr]<<8)|rom[addr+1]:04X}"; addr+=2
|
|
elif mode==DIR: operand=f"${rom[addr]:02X}"; addr+=1
|
|
elif mode==EXT:
|
|
v=(rom[addr]<<8)|rom[addr+1]; addr+=2; operand=f"${v:04X}"; tgt=v
|
|
elif mode==IDX: operand=f"${rom[addr]:02X},X"; addr+=1
|
|
elif mode==IDY: operand=f"${rom[addr]:02X},Y"; addr+=1
|
|
elif mode==REL:
|
|
rel=rom[addr]; addr+=1; d=rel-256 if rel>127 else rel
|
|
tgt=(addr+d)&0xFFFF; operand=f"${tgt:04X}"
|
|
elif mode==BITDIR: operand=f"${rom[addr]:02X} #${rom[addr+1]:02X}"; addr+=2
|
|
elif mode==BITIDX: operand=f"${rom[addr]:02X},X #${rom[addr+1]:02X}"; addr+=2
|
|
elif mode==BITIDY: operand=f"${rom[addr]:02X},Y #${rom[addr+1]:02X}"; addr+=2
|
|
elif mode==BRDIR:
|
|
dd=rom[addr]; mk=rom[addr+1]; rel=rom[addr+2]; addr+=3
|
|
s=rel-256 if rel>127 else rel; tgt=(addr+s)&0xFFFF
|
|
operand=f"${dd:02X} #${mk:02X} ${tgt:04X}"
|
|
elif mode==BRIDX:
|
|
dd=rom[addr]; mk=rom[addr+1]; rel=rom[addr+2]; addr+=3
|
|
s=rel-256 if rel>127 else rel; tgt=(addr+s)&0xFFFF
|
|
operand=f"${dd:02X},X #${mk:02X} ${tgt:04X}"
|
|
elif mode==BRIDY:
|
|
dd=rom[addr]; mk=rom[addr+1]; rel=rom[addr+2]; addr+=3
|
|
s=rel-256 if rel>127 else rel; tgt=(addr+s)&0xFFFF
|
|
operand=f"${dd:02X},Y #${mk:02X} ${tgt:04X}"
|
|
raw=' '.join(f'{rom[b]:02X}' for b in range(start,addr))
|
|
out.append((start,f"{pfx}{mn} {operand}".strip(), raw, mn, tgt))
|
|
return out
|
|
|
|
if __name__=='__main__':
|
|
rom=open(__import__('os').path.join(__import__('os').path.dirname(__file__),'tms27pc512.BIN'),'rb').read()
|
|
start=int(sys.argv[1],16); count=int(sys.argv[2]) if len(sys.argv)>2 else 40
|
|
for row in disasm(rom,start,count):
|
|
a=row[0]; txt=row[1]; raw=row[2]
|
|
print(f"{a:04X}: {raw:<12} {txt}")
|