PlasmaNew: firmware dump + reverse-engineering analysis
Adds the dumped U3 EPROM image (tms27pc512.BIN) and the analysis it enabled. Findings (FIRMWARE.md): - Memory map: code+data at CPU $8000-$FFFF (EPROM upper 32KB, 1:1); lower half unused. Valid HC11 vectors; SCI rx ISR at $B85C fills a ring buffer, main-loop parser dispatches. - Full command set recovered from the two jump tables: 58 ESC commands ($98AC) + control chars ($994C). Decoded semantics for @/G/H/K/L (confirm earlier guesses) plus NEW commands: Q=set-row, R=set-col, I=draw-page, i=display-page (10-page double-buffering), A-F=vector graphics, X/Y=pen. - 8 fonts at $BC03 (6x8, 6x10, 7x10, 12x16, 12x20); glyph bitmaps in ROM (~$C000-$DFFF, exact base TBD). - Built-in demo enabled by jumper 6 (confirms the JP1 map). Tooling: hc11dis.py, a purpose-built 68HC11 disassembler (the toolchain has no m68hc11 target), with the analysis reproducible from the addresses noted. The firmware is now the authoritative command spec, superseding the need for the Babcock programming manual. Feeds both vPLASMA and the replica firmware. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,148 @@
|
||||
# PD01D221 firmware analysis (`tms27pc512.BIN`)
|
||||
|
||||
Reverse-engineering notes for the dumped controller firmware — the 64 KB
|
||||
TI TMS27PC512 EPROM (U3) from the Babcock PD01D221. This is the authoritative
|
||||
source for the display's command set, and it feeds both [vPLASMA](../src/VPlasma.App/)
|
||||
and the planned [hardware replica](README.md).
|
||||
|
||||
Dump: `tms27pc512.BIN`, 65,536 bytes, MD5 `b775427806857f60ca4a4cc501f4b5cc`.
|
||||
Analysis tooling: [`hc11dis.py`](hc11dis.py) (a purpose-built 68HC11
|
||||
disassembler — the toolchain has no m68hc11 target).
|
||||
|
||||
## Memory map
|
||||
|
||||
- **CPU $8000–$FFFF = EPROM upper 32 KB, 1:1** (the HC11 vector table lands at
|
||||
ROM offset `$FFC0–$FFFF` and is valid, which pins the mapping). The EPROM's
|
||||
**lower 32 KB is unused** (all `$00`) — only A15-high is decoded to the ROM.
|
||||
- **Code:** `$9000–$B8xx`. **Data/tables:** `$8000–$8FFF` (demo), `$98AC+`
|
||||
(dispatch tables), `$BC03+` (font descriptors), `$C000–$DFFF` (glyph
|
||||
bitmaps + graphics).
|
||||
- **RAM (Mosel MS62256, 32 KB) at low addresses:** HC11 registers on page 0
|
||||
(`$00–$3F`; SCSR=`$2E`, SCDR=`$2F`), zero-page variables `$40–$FF`, RX ring
|
||||
buffer at `$0228`, and **ten 128×32 screen buffers** from `$0F6D` up.
|
||||
|
||||
## Vectors
|
||||
|
||||
| Vector | Target | Notes |
|
||||
|--------|--------|-------|
|
||||
| RESET | `$9059` | Init: registers, stack `$0227`, then main loop |
|
||||
| **SCI (serial rx)** | **`$B85C`** | Interrupt-driven receive → ring buffer |
|
||||
| COP watchdog | `$905E` | (re-inits) |
|
||||
| others | `$9059` | default → reset |
|
||||
|
||||
## Architecture
|
||||
|
||||
1. **SCI RX ISR (`$B85C`)** — on RDRF, reads `SCSR`/`SCDR`, stores the byte to
|
||||
a ring buffer at `$0228` (write ptr `$0228`, count `$022C`). No parsing here.
|
||||
2. **Main-loop parser (`$B7E0–$B859`)** — pulls buffered bytes and runs an
|
||||
`ESC`-state machine (flag `$AF`: bit `$10` = ESC seen, bit `$20` = operand
|
||||
pending). Dispatch is **table-driven** (below). Printable chars in the
|
||||
current font's `[first,last]` range (`$62`/`$63`) go to the character
|
||||
renderer (`$9648`), which **enqueues** a glyph to a deferred rasterizer.
|
||||
3. **Ten double-buffered screens** — descriptor table at **`$A4E0`** (10 × 6
|
||||
bytes): each screen has a **draw** pointer (`$BE`) and a **display** pointer
|
||||
(`$BC`) into SRAM, 1 KB apart. `ESC I` sets the draw target, `ESC i` sets
|
||||
what's scanned to the glass → **page-flipping / double-buffering**.
|
||||
|
||||
## Command dispatch
|
||||
|
||||
Two jump tables, indexed by byte:
|
||||
|
||||
- **`ESC` + letter → command table at `$98AC`.** Valid letters `0x30`–`0x7E`;
|
||||
index = `letter − 0x30`; null entry = ignored. **58 commands populated.**
|
||||
- **Control bytes `0x08`–`0x14` → table at `$994C`.** index = `byte − 0x08`.
|
||||
|
||||
Most command handlers share a prologue: first sighting of the letter sets the
|
||||
"operand pending" flag and returns; the **next byte is the 1-byte operand**
|
||||
(in `$C6`). Multi-operand commands (`ESC P/X/Y`) collect into a parameter
|
||||
block at `$0070`.
|
||||
|
||||
### Control characters (`$994C`)
|
||||
|
||||
| Byte | Handler | Meaning |
|
||||
|------|---------|---------|
|
||||
| `0x08` BS | `$99AF` | cursor left |
|
||||
| `0x09` HT | `$9966` | tab |
|
||||
| `0x0A` LF | `$99F3` | line feed |
|
||||
| `0x0B` VT | `$9A30` | vertical tab |
|
||||
| `0x0D` CR | `$9A55` | carriage return |
|
||||
| `0x11`–`0x14` DC1–DC4 | `$9A5C`/`$9B34`/`$9C09`/`$9CFC` | device controls (TBD) |
|
||||
| `0x0C` FF, `0x0E`–`0x10` | — | no handler |
|
||||
|
||||
### ESC commands (`$98AC`) — confirmed semantics
|
||||
|
||||
| Cmd | Handler | Meaning |
|
||||
|-----|---------|---------|
|
||||
| `ESC @` | `$9F26` | **Clear** the active draw buffer (512 bytes = 128×32÷8) |
|
||||
| `ESC G n` | `$A42B` | **Cursor mode**, low nibble of `$B4` (n = 0–7) |
|
||||
| `ESC H n` | `$A44C` | **Text attributes**, low 4 bits of `$B1` (intensity/underline/reverse/flash) |
|
||||
| `ESC K n` | `$A3EA` | **Font select** (n = 0–9; 8 real fonts) |
|
||||
| `ESC L` | `$A556` | **Home** cursor (X=0, Y=0) |
|
||||
| `ESC Q n` | `$A51C` | **Set cursor row Y** (range-checked 0–31) |
|
||||
| `ESC R n` | `$A539` | **Set cursor column X** (range-checked 0–127) |
|
||||
| `ESC I n` | `$A473` | **Select DRAW page** 0–9 (sets `$BE` from `$A4E0` table) |
|
||||
| `ESC i n` | `$A4A2` | **Select DISPLAY page** 0–9 (page-flip; sets `$BC`) |
|
||||
| `ESC P …` | `$AAF1` | **Graphics bitmap write** (multi-operand: screen,y,x,w,h,data) |
|
||||
| `ESC A`–`ESC F` | `$9FB6`–`$A13C` | **Vector/graphics primitives** (line/point/move; pen state `$B6`, coords `$58/$59`, line routine `$A16C`) |
|
||||
| `ESC X n` | `$A748` | **Set graphics pen X** (multi-op, 0–127) |
|
||||
| `ESC Y n` | `$A644` | **Set graphics pen Y** (multi-op) |
|
||||
| `ESC J` | `$A4D4` | **Toggle** mode bit `$B7.7` (orientation/display — TBD) |
|
||||
|
||||
### ESC commands — populated but not yet decoded
|
||||
|
||||
`ESC 0`–`9` (`$9E27`+, set continuations — likely custom-char / numeric entry),
|
||||
`ESC : ; =` , `ESC < > W w _` (cluster `$AEBA–$AF00`), `ESC B C D E F` variants,
|
||||
`ESC M N O` (`$A5BD/$A5C5/$A5E0`), `ESC Z ^ z ~` (cluster `$AC73–$ACA1`),
|
||||
`ESC a`–`f`, `ESC h l n p q r x`. ~30 handlers remain to label — full list with
|
||||
addresses is dumped by the tooling below.
|
||||
|
||||
## Fonts
|
||||
|
||||
Font-pointer table at **`$BC03`** (10 slots) → 12-byte descriptors. **8 real
|
||||
fonts** (slots 8–9 are junk pointers, matching the demo's "8 STORED CHARACTER
|
||||
FONTS"):
|
||||
|
||||
| Font | First–Last | W×H | Notes |
|
||||
|------|-----------|-----|-------|
|
||||
| 0 | `0x20`–`0xFF` | 6×8 | base font, full range |
|
||||
| 1 | `0x40`–`0x7F` | 6×8 | uppercase-only |
|
||||
| 2 | `0x20`–`0xFF` | 6×10 | |
|
||||
| 3 | `0x40`–`0x7F` | 6×10 | |
|
||||
| 4 | `0x20`–`0x7F` | 12×16 | large |
|
||||
| 5 | `0x20`–`0x7F` | 12×20 | largest |
|
||||
| 6 | `0x20`–`0xFF` | 7×10 | |
|
||||
| 7 | `0x40`–`0x7F` | 7×10 | |
|
||||
|
||||
Glyph bitmaps live in ROM (`~$C000–$DFFF`). **Exact glyph base + encoding
|
||||
pending** — the renderer at `$9648` enqueues to a deferred rasterizer; tracing
|
||||
that (or brute-forcing the 'A' pattern at the known stride) will extract the
|
||||
real glyphs to replace vPLASMA's public-domain 5×7 stand-in.
|
||||
|
||||
## Demo program
|
||||
|
||||
Enabled by **jumper 6** (PD3) — confirms the [JP1 map](README.md). A 10-screen
|
||||
scripted demo; the script at `$8028+` is literal command usage (pointer table
|
||||
at `$8000`). It's how the new `ESC R/I/Q/i/K` commands were first spotted, e.g.
|
||||
`1B 47 00` (ESC G 0) `1B 40` (ESC @) `1B 4C` (ESC L) `1B 52 04` (ESC R 4) …
|
||||
|
||||
## What this means
|
||||
|
||||
**For vPLASMA:** the recovered commands can replace guessed behavior. Notably
|
||||
`ESC Q`/`ESC R` are the real cursor-positioning commands (row/col), and the
|
||||
`ESC P` "screen" byte selects one of 10 double-buffered pages — vPLASMA
|
||||
currently models a single buffer.
|
||||
|
||||
**For the replica:** this *is* the spec. The firmware confirms a clean model —
|
||||
a byte-stream command parser, a 512-byte-per-page frame buffer, 10 pages with
|
||||
page-flip, 8 fonts, text attributes as 4 flags, plus vector graphics. All of
|
||||
it ports directly onto a modern MCU. The one artifact still to extract is the
|
||||
glyph bitmaps.
|
||||
|
||||
## Reproduce
|
||||
|
||||
```sh
|
||||
python hc11dis.py <hexaddr> <count> # disassemble from a CPU address
|
||||
# e.g. python hc11dis.py B7E0 70 # the command parser
|
||||
```
|
||||
Command/control tables are at `$98AC` / `$994C`; font table `$BC03`; screen
|
||||
table `$A4E0`.
|
||||
+7
-4
@@ -200,7 +200,10 @@ CDC port satisfies perfectly. Confirm the current drive path.
|
||||
|
||||
## Status
|
||||
|
||||
**Parked pending a firmware dump.** The software emulator (vPLASMA) is built
|
||||
and released; this hardware/protocol thread is blocked on getting the U3
|
||||
EPROM image (or the Babcock programming manual). Resume at the dump plan
|
||||
above once a dump is in hand.
|
||||
**Firmware dump received — analysis underway.** The U3 EPROM was dumped
|
||||
(`tms27pc512.BIN`); see [`FIRMWARE.md`](FIRMWARE.md) for the disassembly
|
||||
findings: memory map, the full command dispatch tables (58 `ESC` commands +
|
||||
control chars), the 10-page double-buffered architecture, 8 fonts, and
|
||||
decoded command semantics. Remaining: extract the glyph bitmaps and label the
|
||||
~30 not-yet-decoded commands. The Babcock programming manual is no longer on
|
||||
the critical path — the firmware is the authoritative spec.
|
||||
|
||||
@@ -0,0 +1,148 @@
|
||||
#!/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}")
|
||||
Binary file not shown.
Reference in New Issue
Block a user