|
|
@@ -33,6 +33,7 @@ manual, not guessed.
|
|
|
| String *reads* and string-to-string assignment — `readln(s)`, `s := t` | `v-TP3-STRREAD-ASSIGN` | done, **`readln(s)`/`read(s)` and `s := t` on string variables. Two runtime entries carry selectors 16/17 (`rdstr`, `strstore`); the caller sets the limit in `CX` with a full `MOV CX,imm16`. `rdstr` (TPSRC4 `xrdstr`) reads a line, stops at CR/LF/^Z, leaves the terminator pending for the existing `rdln` wrapper, truncates at the limit, and stores the length last; `strstore` (TPSRC2 `strstore` + TPSRC8 `asnvar`) copies `min(len(t), size(s)-1)` characters, never writes the source, and leaves the destination tail past the new length untouched. The milestone also exposed a pre-existing frame-layout bug: a local `string` was packed with the *integer* rule, so its characters grew UP over the saved BP at `[BP]` and the return address at `[BP+2]` — `readln` into such a local then ran the string's own bytes as its return address. Locals are now packed top-anchored, which keeps every integer offset byte-for-byte and puts a `string[5]` at `BP-6..BP-1`, inside the frame (38/38 executed, 51/51 entry checks)** |
|
|
|
| Multi-name declarations, both spellings — `var a, b, c : integer`, `procedure p (x, y : integer ; z : integer)` | `v-TP3-MULTIDECL` | done, **definitions and formal parameters read a comma-separated list of NAMES and then one `: type`, exactly TPSRC7 `rdvarlst`/`rdvloop` (VAR) and `prfcnt`/`ccomma` (parameters), with `;` between parameter groups. The older `name : type, name : type` spelling still parses, because both reach the same symbols; the names are created as placeholders and given the group's one type afterwards. One `GetWord` that did not `Skip` first attached the space after a comma to the next name (`" B"`), which is why the first name worked and the second did not; `t42_multidecl` prints each name's value to prove every one landed on its own slot (43/43 compile, 39/39 executed)** |
|
|
|
| The 4 KiB code window enforced — TP3 error 98 instead of writing over the data | `v-TP3-CODEWINDOW` | done, **`dataBase` is `rtSz + 1000H`, so code and data share one 64 KiB `.COM` segment and code may not reach it. `Ebyte` now refuses past `dataBase` (guarded by `imgReady`, since `dataBase` is 0 until `Compile` measures it) and reports TP3's error 98, "Memory overflow" — the same number `DefVar` already uses when `dc` overflows. `t43_codewindow` emits more than 4 KiB of inline literal bytes and must report `ERR 98`; disabling the check turns the fixture green-as-`OK`, which is the bug (a silently corrupt image), so the matrix row is the proof (44/44 compile)** |
|
|
|
+| String subscripts — `s[i]` read, write, and `readln (s[i])` | `v-TP3-STRINDEX` | done, **a STRING is a length byte at `s[0]` followed by the characters, so `s[i]` is the single byte at `off+i`: reading it yields a `CHAR`, assigning to it stores one byte, and `readln (s[i])` reads one byte at `off+i`. A constant index folds into `boff` as an array's does and `r.cls` drops to `TChar`, so `LoadAtom`, `Assignment` and `IoCall` all see one byte. Only a constant index is accepted — a variable index is `ENoLib`, exactly as it already was for an array. The fix uncovered a latent bug: `ParseAtom` did `r.boff := 0` *after* `ParseVar`/`ParseSub`, discarding every subscript, so `s[i]` addressed `off+0` (the length byte). `ParseVar` already zeroes `boff`, so the reset was redundant and was removed. `t44_strindex` prints every indexed byte and `t45_strindex_read` feeds `readln (s[i])`; the indexed read is red before the parse fix and green after (46/46 compile, 41/41 executed)** |
|
|
|
|
|
|
Every row that names a tag has one, and every tag points at a commit on
|
|
|
`master`; verified by diffing the rows against `git tag -l`, which is how
|
|
|
@@ -126,11 +127,11 @@ needing a squint. It was checked for vacuousness by reverting the string
|
|
|
scanner fix: the suite went red with exit 1, and came back green only when the
|
|
|
fix was restored.
|
|
|
|
|
|
-**39 of 44 fixtures compile clean**, up from 1 (the empty program) when the
|
|
|
+**41 of 46 fixtures compile clean**, up from 1 (the empty program) when the
|
|
|
direct harness was first built.
|
|
|
|
|
|
```
|
|
|
-compile matrix: 44 passed, 0 failed (of 44)
|
|
|
+compile matrix: 46 passed, 0 failed (of 46)
|
|
|
```
|
|
|
|
|
|
Compiling: `t01` minimal · `t04` var+assign+`writeln` · `t06` two args ·
|
|
|
@@ -153,7 +154,9 @@ inside a procedure, so both address paths run ·
|
|
|
`t40` `readln` into a global *and* a local `string` ·
|
|
|
`t41` `s := t` between two strings, with the truncation both ways ·
|
|
|
`t42` `var a, b, c : integer` and `procedure p (x, y : integer ; z : integer)`,
|
|
|
-each name landing on its own slot.
|
|
|
+each name landing on its own slot ·
|
|
|
+`t44` `s[i]` read and write on a global and a local string ·
|
|
|
+`t45` `readln (s[i])` storing one character at position `i`.
|
|
|
|
|
|
Failing, all deliberately: `t14` `array [1..5] of integer` at its point of use
|
|
|
and `t25` a string literal assigned to an *integer* (`i := 'ab'`, the reverse
|
|
|
@@ -165,9 +168,9 @@ refuses the whole class because the runtime has no boolean reader at all; and
|
|
|
"Memory overflow", rather than a corrupt image; and
|
|
|
`uierror` is a deliberate syntax error (41) used by the UI test.
|
|
|
|
|
|
-`run_com_tests.sh` additionally links **all 39 that compile** to a real `.COM`
|
|
|
+`run_com_tests.sh` additionally links **all 41 that compile** to a real `.COM`
|
|
|
and re-verifies the bytes with an independent Python checker that *measures*
|
|
|
-the layout instead of restating it: `independent .COM check: 39 checked, 0
|
|
|
+the layout instead of restating it: `independent .COM check: 41 checked, 0
|
|
|
failed`. That last part was itself a bug fix — see "the two restated
|
|
|
constants" below.
|
|
|
|
|
|
@@ -211,7 +214,7 @@ proves each one can go red.
|
|
|
| `check_runtime.py` + `runtime.golden` | the built runtime's code region (585 bytes, code 0..584) sweeps cleanly through FCML, every entry and all branch targets land on an instruction boundary, and the whole disassembly is byte-for-byte the committed golden | whether the golden is *right* |
|
|
|
| `check_framedisp.py` | `[BP+off]` uses disp8 iff `off <= 127`, for locals (negative) and far parameters (>127) | which of the two encodings was chosen, if the other also works |
|
|
|
| `run_com_exec.py` | the **emitted image executes** and prints exactly the expected bytes | semantics the fixture never exercises |
|
|
|
-| `run_exec86.py` | the same image runs in a **second, independent 8086** and agrees with qemu byte for byte, 39/39 | the `.out` file it shares with `run_com_exec.py` — a wrong expectation fails both at once |
|
|
|
+| `run_exec86.py` | the same image runs in a **second, independent 8086** and agrees with qemu byte for byte, 41/41 | the `.out` file it shares with `run_com_exec.py` — a wrong expectation fails both at once |
|
|
|
| `rt_exec.py` | the **runtime entries themselves** are called directly, one qemu boot per call, 50 cases plus a pre-flight, 51/51 — plus a `check_bp_contract` pre-flight over the built blob: an entry that borrows BP must open with `55 8B EC` (exact) and contain a `5D` (a screen, because `5D` is also a displacement byte) | whether the *caller's* half of a contract is right, where the only witness is a case that happens to make two calls in a row |
|
|
|
|
|
|
Two of these deserve the detail, because the reason they exist is the reason
|
|
|
@@ -284,7 +287,7 @@ and `tests/check_comimage.py` asserting there is only one copy of each.
|
|
|
### Execution under qemu — `tests/run_com_exec.py`
|
|
|
|
|
|
The check that cannot be written as a byte comparison, so also the one that
|
|
|
-finds the most: 39 fixtures are compiled to `.COM`, put on a floppy, booted,
|
|
|
+finds the most: 41 fixtures are compiled to `.COM`, put on a floppy, booted,
|
|
|
and their serial output compared to a committed `.out` file **exactly** — CRLF
|
|
|
included — plus the exit code passed to `INT 21h AH=4Ch`.
|
|
|
|
|
|
@@ -344,7 +347,7 @@ compiler: **does the 8086 have this instruction at all?**
|
|
|
value conditions : = x6 <> x2 < x5 >= x3 <= x2 > x16
|
|
|
branch conditions : IF / REPEAT x9 CASE x2 FOR downto x1 FOR to x3
|
|
|
runtime: 616 bytes, 330 swept, 0 0F-prefixed
|
|
|
- program code: 39 of 39 fixtures swept end to end, 4603 bytes
|
|
|
+ program code: 41 of 41 fixtures swept end to end, 4963 bytes
|
|
|
t33_cmpops: 13 comparisons matched against their source operators, in order
|
|
|
clause H: 8 of 8 fixtures matched the branch conditions read off their source
|
|
|
```
|
|
|
@@ -587,7 +590,7 @@ checker.
|
|
|
compile to `.COM` images, are booted on a floppy by a 512-byte hand-assembled
|
|
|
boot sector, and are compared **byte for byte** against the exact output the
|
|
|
fixture demands — `tests/run_com_exec.py`, wired into `run_all.sh`, 21/21 at
|
|
|
-that tag and 39/39 now.
|
|
|
+that tag and 41/41 now.
|
|
|
|
|
|
Everything below is about establishing what *can* be believed, because the
|
|
|
first attempt at this used an emulator that was wrong, and a wrong oracle is
|
|
|
@@ -1172,7 +1175,7 @@ Details that are deliberate, not incidental:
|
|
|
prove the *path* exists (compile → poke → run → report → no file written),
|
|
|
which it does with eight assertions. The other 37 are covered by
|
|
|
`run_exec86.py`, which calls the same interpreter directly.
|
|
|
-- **Every fixture that compiles is now also run.** 39 of 44 execute; the 5 that
|
|
|
+- **Every fixture that compiles is now also run.** 41 of 46 execute; the 5 that
|
|
|
do not are `t14` and `t25` (`ENoLib`, by design), `t38` (a boolean read,
|
|
|
refused with TP3's error 66, by design), `t43` (a program whose code exceeds
|
|
|
the 4 KiB window, refused with error 98, by design) and `uierror` (a
|
|
|
@@ -1990,10 +1993,12 @@ independently-scanned inventory at all.
|
|
|
|
|
|
## Next steps
|
|
|
|
|
|
-1. The rest of the string language. `v-TP3-STRVAR` covers `s := 'literal'`
|
|
|
- and `writeln(s)`; `v-TP3-STRREAD-ASSIGN` covers `readln(s)`/`read(s)` (TP3's
|
|
|
- `xrdstr`) and `s := t` between two string variables. Still `ENoLib`:
|
|
|
- `s [i]` indexing and the `+` / comparison operators.
|
|
|
+1. The rest of the string language. `v-TP3-STRVAR`/`v-TP3-STRREAD-ASSIGN` cover
|
|
|
+ string variables, `s := 'literal'`, `writeln (s)`, `readln (s)`/`read (s)`
|
|
|
+ and `s := t`; `v-TP3-STRINDEX` covers `s[i]` with a **constant** index —
|
|
|
+ read, write and `readln (s[i])`. Still `ENoLib`: a *variable* index `s [i]`
|
|
|
+ (the runtime address arithmetic TPSRC9 `indstr`/`ialoop` does), and the `+`
|
|
|
+ / comparison operators (`cmpstr`/`xconcat`, TPSRC2).
|
|
|
2. Nested procedures / recursion, `var` parameters (the `SEG:OFF` push from
|
|
|
RESUME-TP3.md §3.11), range/index checks (`TU_RANGE_CHECK`,
|
|
|
`TU_INDEX_CHECK`), typed constants (RESUME-TP3.md §3.14), `array` at its
|