|
|
@@ -31,6 +31,8 @@ manual, not guessed.
|
|
|
| A `BOOLEAN` read refused with TP3's own error 66 | `v-TP3-BOOLREAD` | done, **`readln (b)` of a boolean is an error, not a word store into a one-byte variable — exactly TPSRC8 `prderrtp`; the compile matrix demands `ERR 66` at the character after the variable, and the accepting dispatch is one of the in-suite reverts** |
|
|
|
| String *variables* — `s : string[10]`, `s := 'hi'`, `writeln(s)` | `v-TP3-STRVAR` | done, **the first declared type bigger than a word that is not an error. `string [n]` is n+1 bytes — a length byte and characters, the layout TPSRC8's `string` type gives a variable (`INC BX / MOV varsize,BX`) — so `DefVar`'s `size > 2` catch-all (still the answer for an `array` at its point of use) exempts `TString`. `s := 'hi'` copies the literal byte by byte and truncates a longer one exactly as TPSRC2 `strstore` does; `writeln(s)` pushes the variable's address and calls a new `wrstr` entry (TPSRC4 `xwrtstr` minus its field-width formatting), which prints the length byte's worth of characters and nothing for `''`. Both a global and a local string are exercised — the absolute and the `[BP+off]` address paths — and the `.out` is compared byte for byte under qemu and Exec86 (36/36)** |
|
|
|
| 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)** |
|
|
|
|
|
|
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
|
|
|
@@ -72,7 +74,7 @@ gm2 -fiso -fuse-module-list=modules.lst -o tpshell \
|
|
|
Shell.mod Compiler.mod Term.o TextBuf.o Posix.o Editor.o # phase B2
|
|
|
```
|
|
|
|
|
|
-Current clean build: `make clean && make` → rc=0, `tpshell` **191072 bytes**.
|
|
|
+Current clean build: `make clean && make` → rc=0, `tpshell` **191104 bytes**.
|
|
|
The one diagnostic is `./Compiler.mod: ParseExpr: too many errors in pass 3`,
|
|
|
which is the expected phase-1 rollup that the recipe tolerates — not a real
|
|
|
error. `shell/Makefile` is the single authoritative build recipe.
|
|
|
@@ -124,11 +126,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.
|
|
|
|
|
|
-**38 of 42 fixtures compile clean**, up from 1 (the empty program) when the
|
|
|
+**39 of 44 fixtures compile clean**, up from 1 (the empty program) when the
|
|
|
direct harness was first built.
|
|
|
|
|
|
```
|
|
|
-compile matrix: 42 passed, 0 failed (of 42)
|
|
|
+compile matrix: 44 passed, 0 failed (of 44)
|
|
|
```
|
|
|
|
|
|
Compiling: `t01` minimal · `t04` var+assign+`writeln` · `t06` two args ·
|
|
|
@@ -149,7 +151,9 @@ arguments are computed in various positions, each one keeping its own value. ·
|
|
|
`t39` `s : string [10]`, assigned a literal and written — globally and from
|
|
|
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.
|
|
|
+`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.
|
|
|
|
|
|
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
|
|
|
@@ -157,11 +161,13 @@ direction is still unimplemented) both → `ENoLib`
|
|
|
(102), the original's "not implemented" path; `t38` `readln (b)` of a
|
|
|
`boolean` → error 66, TP3's "I/O not allowed here" (TPSRC8 `prderrtp`), which
|
|
|
refuses the whole class because the runtime has no boolean reader at all; and
|
|
|
+`t43_codewindow` a program whose code exceeds the 4 KiB window → error 98,
|
|
|
+"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 38 that compile** to a real `.COM`
|
|
|
+`run_com_tests.sh` additionally links **all 39 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: 38 checked, 0
|
|
|
+the layout instead of restating it: `independent .COM check: 39 checked, 0
|
|
|
failed`. That last part was itself a bug fix — see "the two restated
|
|
|
constants" below.
|
|
|
|
|
|
@@ -205,7 +211,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, 38/38 | 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, 39/39 | 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
|
|
|
@@ -278,7 +284,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: 38 fixtures are compiled to `.COM`, put on a floppy, booted,
|
|
|
+finds the most: 39 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`.
|
|
|
|
|
|
@@ -338,7 +344,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: 38 of 38 fixtures swept end to end, 4315 bytes
|
|
|
+ program code: 39 of 39 fixtures swept end to end, 4603 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
|
|
|
```
|
|
|
@@ -581,7 +587,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 38/38 now.
|
|
|
+that tag and 39/39 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
|
|
|
@@ -1166,9 +1172,11 @@ 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.** 38 of 42 execute; the 4 that
|
|
|
+- **Every fixture that compiles is now also run.** 39 of 44 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) and `uierror` (a deliberate syntax
|
|
|
+ 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
|
|
|
+ deliberate syntax
|
|
|
error). The gap this replaces was nine fixtures that compiled and were
|
|
|
*never executed* — `t08` const, `t09` if/then/else, `t10` while, `t11` for/to,
|
|
|
`t12` repeat/until, `t13` procedure + value param, `t15` label + goto,
|
|
|
@@ -1990,18 +1998,19 @@ independently-scanned inventory at all.
|
|
|
RESUME-TP3.md §3.11), range/index checks (`TU_RANGE_CHECK`,
|
|
|
`TU_INDEX_CHECK`), typed constants (RESUME-TP3.md §3.14), `array` at its
|
|
|
point of use (`t14`), `case` with subrange labels.
|
|
|
-3. Make the 4 KiB code window an enforced limit rather than a documented one:
|
|
|
- report an error when `pc` reaches `dc`, instead of writing over the data.
|
|
|
-4. Both spellings of a multi-name declaration. `var i, c : integer;` is
|
|
|
- error 1 at the comma and needs two `var` lines; `procedure f (a : integer;
|
|
|
- b : integer)` is error 1 at the semicolon and needs a comma. Neither is
|
|
|
- wrong Pascal, so a program that compiles under one compiler may not under
|
|
|
- another. A parameter may also not shadow a global (`DupTest` rejects any
|
|
|
- name `Search` finds at any level), which Pascal allows.
|
|
|
-5. Harden the program-header parameter loop against non-advancing input
|
|
|
+3. Harden the program-header parameter loop against non-advancing input
|
|
|
(`program p(1;)`) with a `BOOLEAN` flag — **not** `EXIT`, which ICEs gm2.
|
|
|
-6. FreeDOS (`freedos.qcow2`, FD14-LiveCD) is still untried. Now that `R` runs
|
|
|
+4. FreeDOS (`freedos.qcow2`, FD14-LiveCD) is still untried. Now that `R` runs
|
|
|
the image in-process, a real DOS is no longer needed for any claim above —
|
|
|
but it is still the only way to get a third opinion on the `INT 21h` shim,
|
|
|
and `Exec86` deliberately implements only the three functions the runtime
|
|
|
calls.
|
|
|
+
|
|
|
+The 4 KiB code window is no longer a documented limitation but an enforced
|
|
|
+one (`v-TP3-CODEWINDOW`): `Ebyte` reports TP3's error 98 when the code
|
|
|
+reaches `dataBase`, and `t43_codewindow` is the fixture that makes it red
|
|
|
+(the run below it, `t43` not in `run_com_exec`).
|
|
|
+
|
|
|
+Multi-name declarations both ways are `v-TP3-MULTIDECL`: `var a, b, c :
|
|
|
+integer` and `procedure p (x, y : integer ; z : integer)` parse as TPSRC7
|
|
|
+does, and the older `name : type, name : type` spelling still parses.
|