From af7d3565a50270e944f5a7e7e03f1d150908ff34 Mon Sep 17 00:00:00 2001 From: SDD Date: Sun, 18 Oct 2026 00:54:34 +0300 Subject: [PATCH] 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). --- harness/sdk-roundtrip/.gitignore | 18 ++++ harness/sdk-roundtrip/README.md | 117 ++++++++++++++++++++++++++ harness/sdk-roundtrip/dis2as34.py | 113 +++++++++++++++++++++++++ harness/sdk-roundtrip/prepare_work.sh | 23 +++++ harness/sdk-roundtrip/roundtrip.sh | 109 ++++++++++++++++++++++++ harness/sdk-roundtrip/verify_dis.py | 107 +++++++++++++++++++++++ tools/smart34010/README.md | 24 ++++-- tools/smart34010/dis34 | Bin 43296 -> 43352 bytes tools/smart34010/dis34.c | 11 ++- 9 files changed, 512 insertions(+), 10 deletions(-) create mode 100644 harness/sdk-roundtrip/.gitignore create mode 100644 harness/sdk-roundtrip/README.md create mode 100644 harness/sdk-roundtrip/dis2as34.py create mode 100755 harness/sdk-roundtrip/prepare_work.sh create mode 100755 harness/sdk-roundtrip/roundtrip.sh create mode 100755 harness/sdk-roundtrip/verify_dis.py diff --git a/harness/sdk-roundtrip/.gitignore b/harness/sdk-roundtrip/.gitignore new file mode 100644 index 0000000..a496722 --- /dev/null +++ b/harness/sdk-roundtrip/.gitignore @@ -0,0 +1,18 @@ +# The 1987 DOS toolchain is extracted here on first use, and round-trip +# products are written here by roundtrip.sh. Neither is source. +work/ +*.bin +*.obj +*.out +*.map +*.lst +*.hex +*.tag +*.HI +*.LO +*.$$$ +*.asm +*.dis +*.c +__pycache__/ +*.pyc diff --git a/harness/sdk-roundtrip/README.md b/harness/sdk-roundtrip/README.md new file mode 100644 index 0000000..8eef31e --- /dev/null +++ b/harness/sdk-roundtrip/README.md @@ -0,0 +1,117 @@ +# 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` → `jr` (2 words) | needed | **gone** — `dis34` prints `jr` | +| 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 + +```sh +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` for the 2-word relative jumps; regenerate them with the fixed +`dis34` before trusting their control flow. diff --git a/harness/sdk-roundtrip/dis2as34.py b/harness/sdk-roundtrip/dis2as34.py new file mode 100644 index 0000000..66604a6 --- /dev/null +++ b/harness/sdk-roundtrip/dis2as34.py @@ -0,0 +1,113 @@ +#!/usr/bin/env python3 +"""Convert smart34010 (dis34 -o) output into TI gspa/as34 2.00 syntax. + +Usage: dis2as34.py BASE_HEX SIZE_BYTES INPUT.ASM > OUT.ASM + +The input must be produced with the fixed ``dis34 -o`` (opcode words in a +trailing comment), which prints both relative-jump forms as ``jr`` and +the B-file stack pointer as ``b15``. The 1987 assembler is *not* +byte-faithful on its own: + +* ``MOVI`` is one mnemonic with a short (16-bit, sign-extended) and a long + (32-bit) form. gspa auto-selects by "does the value fit in 16 bits", so a + long MOVI of 0x53E0 (which the ROM encodes as a 3-word instruction) is + silently shortened -- and sign extension then changes its meaning. The + explicit ``W`` / ``L`` operand forces the form. +* the same applies to ADDI/CMPI/SUBI. +* register-indirect displacements are signed 16-bit fields. dis34 prints the + raw two's-complement bits (0xFFE0); gspa rejects those as out of bounds, so + they are rendered as signed decimal (-32). +* constants: dis34 emits ``0xHHHH``; gspa wants ``0HHHHh`` (must start with a + decimal digit, so 0xC00000B0 becomes 0C00000B0h). +* a PC-relative branch whose target is not an instruction boundary in the + region (outside it, or -- as happens in data tables -- inside a multi-word + instruction) cannot be expressed as a label. gspa also mishandles a + negative absolute ``.set`` target in a JR. The exact opcode words are + therefore emitted as ``.word`` for those branches. +""" +import re +import sys + +DISP = re.compile(r"(\*\s*[ab]\d+|\*\s*sp)\(\s*0x([0-9a-fA-F]+)\s*\)") +HEX = re.compile(r"0x([0-9a-fA-F]+)") +FORM_OPS = {"movi", "addi", "cmpi", "subi"} +RELATIVE = re.compile(r"^(jreq|jrne|jrlt|jrle|jrgt|jrge|jrc|jrnc|jrlo|jrls|jrhi|jrhs|jrnz|jrz|" + r"jruc|jrxn|jrxnn|jrxz|jrxeq|jrxgt|jrxle|jryn|jrynn|jryz|jryeq|jrygt|jryle|" + r"call|callr)$") +DSJ_OPS = {"dsj", "dsjs", "dsjeq", "dsjne"} + + +def ti_hex(digits: str) -> str: + return "0" + digits.upper() + "h" + + +def fix_disp(m): + base, digits = m.group(1), m.group(2) + v = int(digits, 16) + if v >= 0x8000: + return f"{base}({v - 0x10000})" + return f"{base}(0{digits.upper()}h)" + + +def raw_words(words): + return " .word " + ", ".join(ti_hex(w) for w in words) + + +def convert(src: str, base: int, size: int) -> str: + end = base + size * 8 + lines = src.splitlines() + defined = set() + for line in lines: + code = line.partition(";")[0] + dm = re.match(r"^(L[0-9A-Fa-f]{8}):", code) + if dm: + defined.add(dm.group(1).upper()) + + out = [] + referenced = set() + for line in lines: + code, sep, comment = line.partition(";") + for lm in re.findall(r"\b(L[0-9A-Fa-f]{8})\b", code): + referenced.add(lm.upper()) + words = re.findall(r"\b[0-9a-fA-F]{4}\b", comment) + stripped = code.rstrip() + parts = stripped.split() + op = parts[0].lower() if parts else "" + # short/long immediate forms (see docstring) + if op in FORM_OPS and words: + if len(words) == 2: + stripped += ", W" + mm = re.match(r"^(\s*\w+\s+)0x([0-9a-fA-F]+)(,.*)$", stripped) + if mm and int(mm.group(2), 16) >= 0x8000: + stripped = (mm.group(1) + str(int(mm.group(2), 16) - 0x10000) + + mm.group(3)) + elif len(words) == 3: + stripped += ", L" + # relative branch to a label that is not an instruction boundary + mm = re.match(r"^(\s*)(\w+)(\s+.*?)(L[0-9A-Fa-f]{8})\s*$", stripped) + if mm and mm.group(4).upper() not in defined: + op2 = mm.group(2).lower() + if op2 in DSJ_OPS or RELATIVE.match(op2): + stripped = raw_words(words) + # relative branch to an absolute address outside the region + mm = re.match(r"^(\s*)([a-z]+)(\s+)0x([0-9a-fA-F]+)\s*$", stripped) + if mm and RELATIVE.match(mm.group(2).lower()): + target = int(mm.group(4), 16) + if not (base <= target < end): + stripped = raw_words(words) + stripped = DISP.sub(fix_disp, stripped) + stripped = HEX.sub(lambda mm: ti_hex(mm.group(1)), stripped) + out.append(stripped) + missing = sorted(referenced - defined) + if missing: + out.append("") + for name in missing: + off = int(name[1:], 16) - base + out.append(f"{name} .set " + ("0%Xh" % off if off >= 0 else str(off))) + return "\n".join(out) + "\n" + + +if __name__ == "__main__": + base = int(sys.argv[1], 16) + size = int(sys.argv[2]) + sys.stdout.write(convert(open(sys.argv[3]).read(), base, size)) diff --git a/harness/sdk-roundtrip/prepare_work.sh b/harness/sdk-roundtrip/prepare_work.sh new file mode 100755 index 0000000..df206d1 --- /dev/null +++ b/harness/sdk-roundtrip/prepare_work.sh @@ -0,0 +1,23 @@ +#!/bin/sh +# Build the reference binaries used by verify_dis.py. +# +# work/dis34.orig - the pre-fix dis34 (from the checkpoint commit) +# work/dis34.sponly - the same source with *only* the B-file SP fix applied +# +# Usage: prepare_work.sh [GIT_REV] +set -eu + +ROOT=$(cd "$(dirname "$0")/../.." && pwd) +DIR=$(cd "$(dirname "$0")" && pwd) +WORK="$DIR/work" +REV=${1:-acb92d3e44ec} + +mkdir -p "$WORK" +git -C "$ROOT" show "$REV:tools/smart34010/dis34.c" > "$WORK/dis34.orig.c" +cc -Wall -O2 -o "$WORK/dis34.orig" "$WORK/dis34.orig.c" + +sed 's/strcpy (p, "sp");/strcpy (p, file ? "b15" : "sp");/' \ + "$WORK/dis34.orig.c" > "$WORK/dis34.sponly.c" +cc -Wall -O2 -o "$WORK/dis34.sponly" "$WORK/dis34.sponly.c" + +echo "built $WORK/dis34.orig and $WORK/dis34.sponly" diff --git a/harness/sdk-roundtrip/roundtrip.sh b/harness/sdk-roundtrip/roundtrip.sh new file mode 100755 index 0000000..a0e8f4c --- /dev/null +++ b/harness/sdk-roundtrip/roundtrip.sh @@ -0,0 +1,109 @@ +#!/bin/sh +# Assemble a region of the VT1200 ROM with the 1987 TI TMS34010 toolchain +# (GSPA/GSPLNK/GSPROM, run under DOSBox) and compare the result byte-for-byte +# with the original. +# +# Usage: roundtrip.sh OFFSET SIZE BASE_BIT +# OFFSET byte offset into vt1200_v22.bin +# SIZE region size in bytes +# BASE_BIT bit address the region is linked at (e.g. 3F208000) +# +# Example: roundtrip.sh 0x1000 16384 3F208000 +# +# The ROM is only read. All work happens in this directory. +# +# Set DIS34=/path/to/dis34 to test a different build (used for the pre-fix +# control run: it should FAIL, because the converter no longer rewrites +# long-relative JA back to JR). +set -eu + +ROOT=$(cd "$(dirname "$0")/../.." && pwd) +DIR=$(cd "$(dirname "$0")" && pwd) +WORK="$DIR/work" +ROM="$ROOT/vt1200_v22.bin" +DIS="${DIS34:-$ROOT/tools/smart34010/dis34}" +DOSBOX_CONF="$ROOT/harness/sdk-scratch/dosbox.conf" + +OFF=${1:?offset} +SIZE=${2:?size} +BASE=${3:?base bit address} +OFF=$((OFF)) +SIZE=$((SIZE)) + +mkdir -p "$WORK" +# tools: extract once from the 1987-11-06 floppy image +if [ ! -x "$WORK/GSPA.EXE" ]; then + IMG="$ROOT/tools/tms34010-sdk/1987-05 TMS34010 SDK/1987-11-06 TMS34010 Simulator-C Tools-ASM Tools.img" + mcopy -s -n -i "$IMG" "::/A2/*" "$WORK/" + mcopy -s -n -i "$IMG" "::/C2/*" "$WORK/" +fi + +# TI hex constants must begin with a decimal digit +TBASE=$BASE +case "$TBASE" in + [0-9]*) ;; + *) TBASE="0$TBASE" ;; +esac + +dd if="$ROM" of="$WORK/rt.bin" bs=1 skip="$OFF" count="$SIZE" 2>/dev/null +"$DIS" -b "$BASE" -o "$WORK/rt.bin" > "$WORK/rt.dis" +python3 "$DIR/dis2as34.py" "$BASE" "$SIZE" "$WORK/rt.dis" > "$WORK/RT.ASM" +sed -i '1i .text' "$WORK/RT.ASM" +echo '.end' >> "$WORK/RT.ASM" + +cat > "$WORK/RT.CMD" </dev/null 2>&1 + +python3 - "$WORK" "$SIZE" <<'EOF' +import sys +work, size = sys.argv[1], int(sys.argv[2]) + +def readhex(p): + d = {} + for line in open(p): + line = line.strip() + if not line.startswith(':'): + continue + b = bytes.fromhex(line[1:]) + if b[3] != 0: + continue + a = (b[1] << 8) | b[2] + for i, x in enumerate(b[4:4 + b[0]]): + d[a + i] = x + return d + +hi = readhex(work + "/RTHI.HI") +lo = readhex(work + "/RTLO.LO") +out = bytearray() +for a in sorted(hi): + out.append(lo[a]) + out.append(hi[a]) +orig = open(work + "/rt.bin", "rb").read() +n = min(len(orig), len(out)) +bad = [i for i in range(n) if orig[i] != out[i]] +print("assembled %d bytes, original %d bytes, %d differing" % (len(out), len(orig), len(bad))) +if len(out) != len(orig) or bad: + print("FAIL") + for i in bad[:5]: + print(" %#x: orig %s out %s" % (i, orig[i:i+8].hex(), out[i:i+8].hex())) + sys.exit(1) +print("md5 " + __import__('hashlib').md5(out).hexdigest() + " BYTE-EXACT") +EOF diff --git a/harness/sdk-roundtrip/verify_dis.py b/harness/sdk-roundtrip/verify_dis.py new file mode 100755 index 0000000..d27fd97 --- /dev/null +++ b/harness/sdk-roundtrip/verify_dis.py @@ -0,0 +1,107 @@ +#!/usr/bin/env python3 +"""Compare two dis34 builds over the whole ROM and classify every difference. + +Purpose: prove the two `dis34` fixes are the *only* changes to its output. + + * long-relative JR fix: the line must change from ``ja`` to ``jr`` + and carry exactly two opcode words, the first being ``0xC?00``; + * B-file SP fix: the mnemonic must be unchanged, every ``sp`` operand must + become ``b15``, and the encoding must be able to name B15: bit 4 set, or + the cross-file MOVE Rs,Rd whose destination is the other file. + +Usage: verify_dis.py OLD_DIS34 NEW_DIS34 [ROM] + +Chunks are disassembled with 16 zero bytes of padding so a multi-word +instruction at the end of a chunk cannot abort the run. The padding is +identical for both builds and its decoded lines are still compared. +""" +import os +import re +import subprocess +import sys + +HEXWORD = re.compile(r"\b[0-9a-fA-F]{4}\b") + + +def disassemble(exe, chunk, base, work): + part = os.path.join(work, "verify_chunk.bin") + with open(part, "wb") as f: + f.write(chunk + b"\0" * 16) + p = subprocess.run([exe, "-b", "%X" % base, "-o", part], + stdout=subprocess.PIPE, stderr=subprocess.PIPE, + check=True) + return p.stdout.decode("latin-1").splitlines() + + +def words(line): + code, _, comment = line.partition(";") + return HEXWORD.findall(comment) + + +def can_name_b15(w0): + """True if the instruction's register field can select B-file reg 15. + + Bit 4 (the R bit) normally selects the file. MOVE Rs,Rd cross-file + (op 0x4E00) is the exception: its *destination* is the other file + (dis34 prints ``regname(rd, rf ^ 1)``), so bit 4 = 0 still means B15 + for the destination. + """ + op = int(w0, 16) + return bool(op & 0x10) or (op & 0xfe00) == 0x4e00 + + +def main(): + old_exe, new_exe = sys.argv[1], sys.argv[2] + rom_path = sys.argv[3] if len(sys.argv) > 3 else "vt1200_v22.bin" + work = os.path.dirname(os.path.abspath(__file__)) + "/work" + os.makedirs(work, exist_ok=True) + rom = open(rom_path, "rb").read() + + chunk_size = 32768 + base0 = 0x3F200000 + n_jr = n_sp = 0 + errors = [] + for start in range(0, len(rom), chunk_size): + chunk = rom[start:start + chunk_size] + base = base0 + start * 8 + old = disassemble(old_exe, chunk, base, work) + new = disassemble(new_exe, chunk, base, work) + if len(old) != len(new): + errors.append("chunk %#x: line count %d != %d" % + (start, len(old), len(new))) + continue + for i, (a, b) in enumerate(zip(old, new)): + if a == b: + continue + w = words(a) + code_a = a.partition(";")[0].strip() + code_b = b.partition(";")[0].strip() + tok_a = code_a.split()[0].lower() if code_a.split() else "" + tok_b = code_b.split()[0].lower() if code_b.split() else "" + ja = re.fullmatch(r"ja([a-z]+)", tok_a) + if (ja and tok_b == "jr" + ja.group(1) and len(w) == 2 + and re.fullmatch(r"c[0-9a-f]00", w[0].lower())): + n_jr += 1 + continue + # SP fix: same mnemonic, only sp -> b15, R bit set + if (tok_a == tok_b and "sp" in code_a and "b15" in code_b + and code_a.replace("sp", "b15") == code_b + and w and can_name_b15(w[0])): + n_sp += 1 + continue + errors.append("chunk %#x line %d:\n - %s\n + %s" % + (start, i + 1, a, b)) + + print("long-relative JA->JR changes: %d" % n_jr) + print("B-file SP sp->b15 changes: %d" % n_sp) + if errors: + print("UNEXPECTED changes: %d" % len(errors)) + for e in errors[:10]: + print(e) + return 1 + print("all differences are the two intended categories") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/tools/smart34010/README.md b/tools/smart34010/README.md index f4e689b..912bb2d 100644 --- a/tools/smart34010/README.md +++ b/tools/smart34010/README.md @@ -42,7 +42,10 @@ Examples: - **A-file**: a0-a14, accessed when register bit 4 = 0 - **B-file**: b0-b14, accessed when register bit 4 = 1 -- **sp**: register 15 in either file (stack pointer) +- **sp**: register 15 in either file (stack pointer), encoded as `0x0F` + (A15, printed `sp`) or `0x1F` (B15, printed `b15`). Both are the same + physical stack pointer, but the encoding names a different file; a + disassembler must not collapse them. - Register encoding: 5-bit field, bit 4 = file select, bits 3:0 = register number ### Instruction Word Format @@ -299,9 +302,13 @@ Condition code in bits 11:8 of the instruction word: | 15 | nn | Not negative | Jump forms: -- **JR cc** (relative): `0xC000 | (cc<<8) | 0x00` + 16-bit displacement -- **JR cc** (short): `0xC000 | (cc<<8) | disp8` (8-bit signed displacement) -- **JA cc** (absolute): `0xC000 | (cc<<8) | 0x80` + 32-bit address +- **JR cc** (relative, 2 words): `0xC000 | (cc<<8) | 0x00` + 16-bit displacement +- **JR cc** (short, 2 words): `0xC000 | (cc<<8) | disp8` (8-bit signed displacement) +- **JA cc** (absolute, 3 words): `0xC000 | (cc<<8) | 0x80` + 32-bit address + +Only low byte `0x80` is the absolute form. Low byte `0x00` carries a 16-bit +*displacement* and is still `JRcc`; the word count must not be used to invent +an absolute mnemonic for it. Displacement formula: - 16-bit: `target = pc + 32 + (disp << 4)` @@ -337,10 +344,11 @@ Using the wrong decode gives K±1 errors. In `dis34.c`: ### Round-Trip Design Decisions -1. **`jacc` for 16-bit displacement JR** -- dis34 outputs `jacc` (not `jrcc`) - for the 16-bit displacement form of conditional jumps. This ensures as34 - emits the same 4-byte encoding. The 8-bit short form uses `jrcc`. The - 32-bit absolute form uses `jacc`. +1. **`jrcc` for both relative forms** -- the 8-bit and 16-bit displacement + forms are both `JRcc` (2 words); only the 3-word `0x80` form is `JAcc`. + (Until 2026-10-18 the 16-bit form was printed as `jacc`, which made a + linear disassembly's control flow wrong and forced the round-trip + converter to rewrite it back to `jrcc`.) 2. **Branch-only labels** -- Only branch/call targets (jr, ja, callr, calla, dsj, dsjeq, dsjne, dsjs) get labels. MOVI and other data-reference diff --git a/tools/smart34010/dis34 b/tools/smart34010/dis34 index 2004892cb68f4625af9940aabc782244a3e6f36e..f16c3473bdc271e2b831ae042af114f1707d4843 100755 GIT binary patch delta 11106 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 diff --git a/tools/smart34010/dis34.c b/tools/smart34010/dis34.c index d3dccf6..2a41c98 100644 --- a/tools/smart34010/dis34.c +++ b/tools/smart34010/dis34.c @@ -70,7 +70,12 @@ regname (int reg, int file) char *p = s[idx++ & 7]; if (reg == 15) { - strcpy (p, "sp"); + /* Register 15 is the stack pointer in *either* file: the R bit + (bit 4 of the register field) selects A15 or B15. Printing both + as "sp" hides which file the instruction actually names. GSPA + spells the B-file one "b15"; it rejects "a15", and bare "sp" is + the A-file register. */ + strcpy (p, file ? "b15" : "sp"); return p; } snprintf (p, 8, "%c%d", file ? 'b' : 'a', reg); @@ -2163,9 +2168,11 @@ main (int argc, char **argv) } else if (lo8 == 0x00) { + /* 16-bit displacement form: still JRcc (2 words), not + the 3-word absolute JAcc (lo8 == 0x80). */ int16_t disp = (int16_t) r16 (); uint32_t target = pc + 32 + ((int32_t) disp << 4); - printf ("ja%s %s", condnames[cc], fmt_target (target)); + printf ("jr%s %s", condnames[cc], fmt_target (target)); } else {