Files
SDD af7d3565a5 dis34: print long relative JR as jr and surface B-file SP as b15
The 16-bit-displacement jump (low byte 0x00, two words) is JRcc, not the
three-word JAcc (low byte 0x80).  Printing it as ja made linear
disassembly report the wrong control flow and made every such branch grow
by a word when reassembled.

Register 15 is the stack pointer in either file: 0x0F is A15 and 0x1F is
B15.  The disassembler printed both as sp, hiding B-file stack-pointer
code.  Print b15 when the R bit selects the B file.

Validated with the 1987 TI GSPA/GSPLNK/GSPROM round trip: the 32 KB region
at bit 3F208000 still reassembles byte-for-byte (md5
43adb6669571041985f3e26fd9ff7cb9), as do the 4 KB and 16 KB spans and the
0x8000/3F240000 region.  The converter no longer needs its ja->jr or
sp->b15 rewrites (5 fixups -> 3); a whole-ROM diff contains only those two
output categories.  The ROM is unchanged
(b46a3e442ccb86134d2356bd70d22fd2).
2026-10-18 00:54:34 +03:00
..

Round-trip validation for the two dis34 fixes

This directory proves the two corrections in tools/smart34010/dis34.c against the 1987 TI TMS34010 toolchain (GSPA/GSPLNK/GSPROM under DOSBox), using the same method as reference/tms34010-sdk.md. The ROM is only read; its md5 must stay b46a3e442ccb86134d2356bd70d22fd2.

The encodings (from docs/TMS34010-users-guide.txt)

Long relative jump. The Program Control summary (PDF p. A-16) gives:

JAcc Address   Jump Absolute Conditional   3   3,4   1 100 code   1000 0000
JRcc Address   Jump Relative Conditional   2   3,2   1100 code    0000 0000
JRcc Address   Jump Relative Conditional - Short  2,1   1 100 code   xxxx xxxx

The ±32K-word JRcc description (p. 12-96) says:

The assembler calculates the offset as (Address - PC')/16 and inserts the resulting offset into the second instruction word of the opcode. The range for this form of the JRcc instruction is -32,768 to +32,767 words.

So low byte 0x00 + a 16-bit displacement is JRcc, not JAcc; only low byte 0x80 + a 32-bit address is JAcc.

B-file SP. Section 5.1.3 (p. 5-8):

The TMS34010 contains only a single SP. However, this SP can be addressed as a member of either register file, as register A15 or register B15.

and the MMFM description (p. 12-109):

The R bit (bit 4) in the first word indicates which register file is affected.

0x0F is A15 (sp); 0x1F is B15 (b15). GSPA accepts b15 and rejects a15.

Fixups required by the converter, before and after

reference/tms34010-sdk.md lists five fixups for dis34 output. The old converter (harness/sdk-scratch/dis2as34.py) implemented all five:

# fixup before after
1 hex constants (0x220 → 0220h) needed needed
2 MOVI/ADDI/CMPI/SUBI short vs long (,W / ,L) needed needed
3 B-file SP (rl …, sp → b15) needed gone — dis34 prints b15
4 long relative ja<cc> → jr<cc> (2 words) needed gone — dis34 prints jr<cc>
5 branch target not an instruction boundary → .word needed needed

The shipped converter here (dis2as34.py) drops 3 and 4, so its fixup list is 5 → 3. The JR one is the expected disappearance; the SP one disappears as a direct consequence of surfacing B15. (harness/sdk-scratch/ is left untouched as the record of the pre-fix method.)

Results (new dis34 + dis2as34.py with fixups 3 and 4 removed)

region (file offset) base (bit) size differing md5
0x1000 3F208000 4 KB 0 a4018fed12e062658bc81957f149265b
0x1000 3F208000 16 KB 0 0a2e679bc864d52d32c59740142c5b96
0x1000 3F208000 32 KB 0 43adb6669571041985f3e26fd9ff7cb9
0x8000 3F240000 4 KB 0 438e743431d56e0c12ededce1c36952a

The same 32 KB region disassembled by the old dis34 and fed through the converter with the removed rewrites fails (assembled 32912 bytes, original 32768, 27247 differing): the long relative branches grow by a word and the rl … sp destination is encoded as A15. With the fixed dis34 the converter source is equivalent to the old one: only 3 lines change sp to b15 and 2 differ in whitespace (rl 09h,b15 vs rl 09h, b15); assembly is byte-identical.

Proof that the fixes change nothing else

verify_dis.py disassembles the whole ROM in 32 KB chunks with both a control binary and the fixed binary and classifies every changed line:

$ ./verify_dis.py work/dis34.orig work/dis34.sponly ../../vt1200_v22.bin
long-relative JA->JR changes: 0
B-file SP sp->b15 changes:    2623
all differences are the two intended categories

$ ./verify_dis.py work/dis34.orig ../../tools/smart34010/dis34 ../../vt1200_v22.bin
long-relative JA->JR changes: 6235
B-file SP sp->b15 changes:    2623
all differences are the two intended categories

The SP-only comparison is the additivity check: zero mnemonic changes, and every change is exactly an sp operand becoming b15 on an instruction whose encoding can name B15 (bit 4 set, or the cross-file MOVE Rs,Rd dest). The full-fix comparison adds only the JA→JR mnemonic changes on 2-word 0xC?00 instructions. Labels and line counts are unchanged (labels do not depend on either fix).

Reproducing

cd harness/sdk-roundtrip
./prepare_work.sh                       # builds work/dis34.orig and dis34.sponly
./roundtrip.sh 0x1000 32768 3F208000    # prints BYTE-EXACT + md5
DIS34=$PWD/work/dis34.orig ./roundtrip.sh 0x1000 32768 3F208000   # expected FAIL
./verify_dis.py work/dis34.orig work/dis34.sponly ../../vt1200_v22.bin
./verify_dis.py work/dis34.orig ../../tools/smart34010/dis34 ../../vt1200_v22.bin

work/ also holds the pre-fix source/binary (dis34.orig*) and an SP-only binary (dis34.sponly*) used for the isolation checks. The SDK image is extracted from tools/tms34010-sdk/ into work/ on first use.

Cached disassemblies elsewhere (reference/dis34-a-v22.txt.gz, harness/dis/*.txt) were produced by the buggy binary and still show ja<cc> for the 2-word relative jumps; regenerate them with the fixed dis34 before trusting their control flow.