9900dis decodes the complete TMS9900 instruction set, organized into encoding formats 3.5.1 through 3.5.13. Each format uses a different opcode bit-field arrangement within the 16-bit instruction word. The disassembler tries each format handler in sequence and emits the mnemonic and operands for the first handler that recognises the opcode.Documentation Index
Fetch the complete documentation index at: https://mintlify.com/caljer1/9900dis/llms.txt
Use this file to discover all available pages before exploring further.
Addressing modes
Formats 3.5.1 and 3.5.4 encode a two-bit type field (t) alongside a four-bit register field for each operand. The param351() function in rom.py maps these to the following four addressing modes.
| Mode | Encoding (t) | Syntax | Description |
|---|---|---|---|
| 0 | 0b00 | r0 – r15 | Register direct — operand is the workspace register value |
| 1 | 0b01 | *r0 | Register indirect — operand is the word at the address held in the register |
| 2 | 0b10 | @>addr or @>addr(rN) | Symbolic / indexed — a second word in the instruction stream holds the base address; if rN is non-zero it is added as an index |
| 3 | 0b11 | *r0+ | Register indirect with autoincrement — same as mode 1, then the register is incremented by 1 (byte op) or 2 (word op) |
Instruction reference
The TMS9900 instructions decoded by 9900dis are grouped below by function. Each accordion lists the mnemonic, a brief description, and a representative output line as emitted by 9900dis.Data movement
Data movement
Instructions that copy, load, or store values without arithmetic side-effects.
| Mnemonic | Description | Example output |
|---|---|---|
MOV | Move word from source to destination | MOV r1,r2 |
MOVB | Move byte from source to destination | MOVB @>E000,r8 |
CLR | Clear operand to zero | CLR r0 |
SETO | Set operand to >FFFF (all ones) | SETO *r3 |
LI | Load immediate word into register | LI r0,>1234 |
LWPI | Load workspace pointer from immediate | LWPI >8300 |
LIMI | Load interrupt mask from immediate | LIMI >0002 |
STST | Store status register into workspace register | STST r12 |
STWP | Store workspace pointer into workspace register | STWP r13 |
SWPB | Swap high and low bytes of operand in-place | SWPB r5 |
LI, LWPI, and LIMI consume a second word from the ROM stream as their immediate operand. LWPI and LIMI belong to format 3.5.10 and emit only the immediate value, not a register.Arithmetic
Arithmetic
Instructions that perform integer arithmetic and update status bits accordingly.
| Mnemonic | Description | Example output |
|---|---|---|
A | Add word: dst = dst + src | A r1,r2 |
AB | Add byte | AB @>028A,r2 |
S | Subtract word: dst = dst - src | S r3,r4 |
SB | Subtract byte | SB r0,*r1 |
INC | Increment operand by 1 | INC r6 |
INCT | Increment operand by 2 | INCT r7 |
DEC | Decrement operand by 1 | DEC r6 |
DECT | Decrement operand by 2 | DECT r7 |
NEG | Negate (two’s complement) | NEG r2 |
ABS | Absolute value | ABS r2 |
AI | Add immediate word to register | AI r0,>FF00 |
MPY | Unsigned multiply: 32-bit product in rD:rD+1 | MPY r3,r0 |
DIV | Unsigned divide: quotient in rD, remainder in rD+1 | DIV r3,r0 |
A, AB, S, and SB are format 3.5.1 instructions (opcode in bits 15–13, byte flag in bit 12). INC through ABS are format 3.5.4 single-operand instructions. AI is format 3.5.9 and consumes an extra immediate word.Logical
Logical
Bitwise and comparison instructions.
| Mnemonic | Description | Example output |
|---|---|---|
C | Compare word: set status, no write | C r1,r2 |
CB | Compare byte | CB @>028A,r3 |
COC | Compare ones corresponding: (src AND dst) == src | COC r2,r4 |
CZC | Compare zeros corresponding: (src AND dst) == 0 | CZC r3,r4 |
XOR | Exclusive OR word: dst = dst XOR src | XOR r1,r2 |
SOC | Set ones corresponding: dst = dst OR src | SOC r1,r2 |
SOCB | Set ones corresponding byte | SOCB @>028A,r2 |
SZC | Set zeros corresponding: dst = dst AND NOT src | SZC r1,r2 |
SZCB | Set zeros corresponding byte | SZCB r0,*r1 |
ANDI | AND immediate: rN = rN AND imm | ANDI r2,>00FF |
CI | Compare immediate: set status, no write | CI r0,>1000 |
ORI | OR immediate: rN = rN OR imm | ORI r3,>8000 |
INV | Invert (bitwise NOT) operand | INV r4 |
C, CB, SOC, SOCB, SZC, and SZCB are format 3.5.1. COC, CZC, and XOR are format 3.5.2 (opcode in bits 15–10) and use a destination register field in bits 9–6. ANDI, CI, and ORI are format 3.5.9 and consume an extra immediate word.Shift
Shift
Shift instructions operate on a workspace register and a 4-bit count field. When the count field is zero the shift count is taken from the low four bits of
All four belong to format 3.5.8. The disassembler output is
r0 at runtime.| Mnemonic | Description | Example output |
|---|---|---|
SLA | Shift left arithmetic | SLA r2,4 |
SRA | Shift right arithmetic (sign-extended) | SRA r2,1 |
SRC | Shift right circular | SRC r1,3 |
SRL | Shift right logical (zero-filled) | SRL r3,8 |
{:8}r{w},{c} — register index followed by shift count.Branch and jump
Branch and jump
Instructions that alter the program counter.
| Mnemonic | Description | Example output |
|---|---|---|
B | Branch to address (absolute) | B @>6000 |
BL | Branch and link — saves PC to r11 | BL @RESET |
BLWP | Branch and load workspace pointer — context switch | BLWP @>0000 |
RTWP | Return with workspace pointer — restore context | RTWP |
X | Execute instruction at operand address | X *r11 |
JMP | Unconditional jump (relative) | JMP >6020 |
JEQ | Jump if equal (EQ status bit set) | JEQ >6010 |
JGT | Jump if greater than (AGT bit set) | JGT >6030 |
JH | Jump if high (unsigned >) | JH >6040 |
JHE | Jump if high or equal (unsigned >=) | JHE >6050 |
JL | Jump if low (unsigned <) | JL >6060 |
JLE | Jump if low or equal (unsigned <=) | JLE >6070 |
JLT | Jump if less than (signed <) | JLT >6080 |
JNC | Jump if no carry | JNC >6090 |
JNE | Jump if not equal | JNE >60A0 |
JNO | Jump if no overflow | JNO >60B0 |
JOC | Jump if overflow carry | JOC >60C0 |
JOP | Jump if odd parity | JOP >60D0 |
B, BL, BLWP, X are format 3.5.4 single-operand. RTWP is format 3.5.12 (no operands). All jump instructions are format 3.5.7 — see Jump displacement calculation below.CRU operations
CRU operations
Instructions that access the Communications Register Unit (CRU) bit-addressable I/O bus.
| Mnemonic | Description | Example output |
|---|---|---|
LDCR | Load CRU — transfer c bits from workspace register to CRU | LDCR r1,8 |
STCR | Store CRU — read c bits from CRU into workspace register | STCR r1,8 |
SBO | Set CRU bit to one | SBO 12 |
SBZ | Set CRU bit to zero | SBZ 12 |
TB | Test CRU bit — result in EQ status bit | TB 12 |
LDCR and STCR are format 3.5.5. The second operand is a bit count encoded in bits 9–6 of the instruction word. SBO, SBZ, and TB are format 3.5.6; their operand is an 8-bit signed CRU offset in bits 7–0.Miscellaneous
Miscellaneous
| Mnemonic | Description | Example output |
|---|---|---|
IDLE | Idle until an interrupt occurs | IDLE |
RSET | Reset — assert RESET to external hardware | RSET |
CKOF | Clock off — stop the CPU clock | CKOF |
CKON | Clock on — restart the CPU clock | CKON |
LREX | Load ROM and execute | LREX |
XOP | Extended operation — software trap to XOP routine | XOP r1,2 |
IDLE, RSET, CKOF, CKON, and LREX are format 3.5.13 and carry no operands. They share the same deconstruct3512() decoder as format 3.5.12 (RTWP) but key against the mne3513 dictionary. XOP is format 3.5.3 and uses the same physical layout as format 3.5.2, with the destination field interpreted as an XOP vector number (0–15) rather than a register.Jump displacement calculation
All 13 jump instructions (format 3.5.7) encode the branch target as a signed 8-bit displacement in the low byte of the instruction word. Thehandle357() function computes the absolute target address as follows:
current_pc is the address of the next instruction — the program counter has already been advanced by 2 by the time handle357() is called (readword() increments self.pc on every read). The displacement is therefore relative to the instruction after the jump, consistent with the TMS9900 hardware specification.
The signedByte() helper converts the raw unsigned byte value to a signed integer:
JMP at address >6010 with displacement byte >FE (254 unsigned, −2 signed) targets >6010 + 2 + 2 × (−2) = >600E — one instruction back, forming an infinite loop.
If a label exists in the hints file for the target address, handle357() substitutes the symbolic name via hex_or_label() instead of emitting the raw hex address.