v-TP3-DECL-WINDOW-STRIDX.md 9.0 KB

v-TP3-DECL-WINDOW-STRIDX — Session summary

Tag: v-TP3-DECL-WINDOW-STRIDX Commit: (the commit that adds this file) Covers: v-TP3-MULTIDECL, v-TP3-CODEWINDOW, v-TP3-STRINDEX

This is the resume point for the session that did "Next steps" items 3 and 4 in full and the first slice of item 1. It is written so the next session can pick up without re-deriving anything: every claim below is pinned by a fixture in shell/tests/fixtures/expected.tsv and by the gate suite.

What changed

  1. Multi-name declarations, both spellings (v-TP3-MULTIDECL).

TP3 reads a whole comma-separated list of NAMES and then ONE : type for the list, groups separated by ;:

   TPSRC7:744       vardef
   TPSRC7:1267-1275 rdvarlst / rdvloop   (`CALL ccomma / JZ rdvloop',
                                         then ecolon/rdvartp)
   TPSRC7:891-925   prfcnt / ccomma      (formal parameters)

So var a, b, c : integer ; and procedure p (x, y : integer ; z : integer) ; are legal, and the older this-compiler-only spelling a : integer, b : integer is NOT what TP3 accepts. Both are accepted here now, because both reach the same symbols.

DefVar creates a placeholder per name (NewSym (nm, KVar, TNone, ...)) and patches the group's one cls/size/elem into every one after ParseType; ProcFunc does the same and advances parmOff once per name. The names-then-type order is what makes the placeholder-then-patch shape necessary: the type is not known when the names are read.

  1. The 4 KiB code window is enforced (v-TP3-CODEWINDOW).

dataBase is rtSz + 1000H: code and data share one 64 KiB .COM segment, so program code may not grow into the data area. Ebyte checked only MaxCode (24000), so code past dataBase wrote over the globals and produced a plausible, wrong image with no diagnostic. Ebyte now reports TP3's error 98 ("Memory overflow" = EMemOvf, the number DefVar already uses when dc overflows), gated on a new imgReady flag because dataBase is 0 until Compile measures the runtime and lays out the data area.

  1. String subscripts s[i], constant index (v-TP3-STRINDEX).

A string [n] is a length byte at s[0] followed by the characters, so s[i] is the single byte at off+i. ParseSub folds a constant index into boff (the field an array already uses) and drops r.cls to TChar; then LoadAtom loads one byte (A0 off / 8A 46 disp) and zeroes AH, Assignment stores one byte when the symbol is TString but r.cls is TChar, IoCall already classified on arg.cls so writeln (s[i]) picked wrchar, and the reader now classifies read (s[i]) the same way and pushes off+boff. Only a constant index is accepted; a variable index is ENoLib, exactly as it already was for an array.

No new runtime entry: a constant index is pure compile-time offset arithmetic, so the runtime image is unchanged (616 bytes, 18 entries, 51 branches).

Bugs found and fixed

  1. GetWord does not Skip, so every name after the first in a multi-name VAR group was wrong. After MatchDelim (',') the cursor sits on the space, and GetWord reads CurCh as the first character, so the second name became " B" (leading space). The first name parsed (its Skip came from the group header) which is why "one name works, two do not". t42 is red before the added Skip, green after. Same class as the nine Skip sites fixed in v-TP3-PARSER-FIXES.

  2. ParseAtom clobbered every subscript. Its variable arm ended with

    ParseVar (r) ; r.boff := 0 ; (* discards the subscript ParseSub just folded in *)

ParseVar already sets boff := 0 before calling ParseSub, so the reset was pure loss: s[i] addressed off+0, the length byte, while still reporting itself as a char. Latent for a long time because an array cannot be declared yet, so no fixture had ever exercised the fold. t44 reads the length byte with the reset restored and the right bytes without it.

  1. The 4 KiB window was documented but not enforced. Not a crash but a silent corruption: an over-long program overwrote its own data and linked to a well-formed image with no error. This is the shape of failure the project treats as a bug. t43_codewindow is red-as-OK (i.e. compiles wrongly) with the gate disabled.

Scope decisions and findings

  • The declaration change is a strict SUPERSET of TP3: it accepts TP3's ;-separated groups AND the older ,-between-decls spelling. Refusing the latter would turn working programs red for no behavioural reason, and both reach the same symbols.
  • A GetWord after a ,/; must be preceded by Skip (). This is the same trap as the existing "Alpha test without Skip" class and is worth remembering for any new comma-loop.
  • The string subscript uses r.cls = TChar while the SYMBOL's class stays TString as the discriminator between "the element" and "the whole string". That is the only thing that lets s := t, writeln (s) and s[i] share one symbol without a new field on ERes.
  • Constant index only. A variable index needs the runtime address arithmetic TP3 does in indstr/ialoop (TPSRC9), and is the natural next step.
  • tpshell clean-build size is unchanged at 191104 bytes across items 3+4 and the string index. This is real, not a stale build: the two tpshell binaries differ by md5 and Compiler.o grows 75064 -> 75568 bytes, but the linked size lands on the same boundary. make was verified to rebuild Compiler.o before linking.

Fixtures added

t42_multidecl         OK   304  12   both declaration spellings
t43_codewindow        ERR   98 4978  code past dataBase, TP3 error 98
t44_strindex          OK   258  16   s[i] read + write, global + local
t45_strindex_read     OK   134  15   readln (s[i])  (input: 'X' then 'Y')

Next work

Item 1 (the rest of the string language), in the order that keeps each step self-contained:

a. Variable index s [i]. TP3 computes the address at run time:

 `indstr` / `ialoop` / `ianopop` (TPSRC9:543 `xindchk`).  A clean shape for
 this compiler is `LEA BX,[base]` (new `EmLeaBx`; the address forms already
 exist as `EmPushVarAddr`'s three encodings, minus the push), then
 `LoadAtom (index)` into AX (which must not touch BX), `ADD BX,AX` (`01 C3'),
 then `MOV AL,[BX]` / `MOV [BX],AL`.  `ParseSub` must park the index `ERes`
 on `r` until `LoadAtom`/`Assignment` runs, so `ERes` needs somewhere to
 keep a nested index (a side array indexed by a small handle, or the
 kind/imm/idx/boff of the index copied across).

b. String comparison = <> < <= > >= (cmpstr, TPSRC2:890-924, and

 `vcompare`, TPSRC9:133).  A new runtime entry taking two addresses,
 leaving flags set, is enough because `EmSetcc` reads live flags; the open
 question is the SIGNEDNESS and the prefix rule (is "ab" < "abc"?) — read
 it off `cmpstr` before trusting the `SETL`/`SETG` opcodes.  This adds a
 runtime entry, so `runtime.golden`, `check_runtime.py`, `rt_exec.py`
 (next selector is 18) and the branch count all move with it.

c. Concatenation + (xconcat, TPSRC2; addstr -> xconcat,

 TPSRC9:194).  Hardest: TP3 pushes strings inline on the stack, this model
 addresses them, so a result needs a scratch/result string and a length
 rule (TP3 caps at 255).

Item 2 (the large one) is untouched: nested procedures / recursion, var parameters (the SEG:OFF push, RESUME-TP3.md 3.11), range/index checks (TU_RANGE_CHECK, TU_INDEX_CHECK), typed constants (3.14), array at its point of use (t14), case with subrange labels.

Also still open from before this session:

d. Harden the program-header parameter loop against non-advancing input

 (`program p(1;)`) with a `BOOLEAN` flag — NOT `EXIT`, which ICEs gm2.

e. FreeDOS (freedos.qcow2, FD14-LiveCD) is still untried; it is the only

 third opinion on the `INT 21h` shim.

How to verify this session's work

cd shell && make clean && make                 # tpshell 191104 bytes, rc=0
cd shell && tests/run_all.sh                   # expect: OVERALL: ALL PASS

At this tag the suite reports:

compile matrix: 46 passed, 0 failed (of 46)
independent .COM check: 41 checked, 0 failed
execution: 41 passed, 0 failed (of 41)
exec86:    41 passed, 0 failed (of 41), cross-checked against qemu
runtime entries: 51 passed, 0 failed (of 51)
check_8086: 41 of 41 fixtures swept end to end, 4963 bytes
runtime: 616 bytes, 18 entries, 51 branches
OVERALL: ALL PASS

Each new behaviour was proven red before green: the multiply-name read (t42) fails without the Skip, the subscript read (t44) reads the length byte without the ParseAtom fix, and the code-window report (t43) turns into a silent OK with the Ebyte gate disabled.