|
@@ -35,6 +35,7 @@ manual, not guessed.
|
|
|
| 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)** |
|
|
| 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)** |
|
|
| 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)** |
|
|
|
| Variable string subscripts — `s[i]` with a *computed* index | `v-TP3-STRIDX-VAR` | done, **the address of `s[i]` is now computed at run time, so a variable index, a computed one (`i+1`) and a nested one all work — `v-TP3-STRINDEX` accepted a constant only. `ParseSub` evaluates the index into `AX`, then `LEA DI,[base]` / `ADD DI,AX` puts the element's address in `DI` and `r.kind` drops to `5`; `LoadAtom` reads `[DI]`, `Assignment` `PUSH DI`es the destination across its right-hand side (so `g[i] := h[j]` stores where it read — TPSRC8 `asnscal2`/`asnscal3`'s `flgpshdi`, and `SaveLeft` materialises a `DI`-addressed left operand *before* parsing the right), and `IoCall` pushes `DI` for `readln (s[i])`. No runtime entry: the arithmetic is compile-time emitted, so the runtime image is unchanged. Five new one-line emitters are name-audited (`AddDiAx`, `LdAlDi`, `StDiAl`, `PushDi`, `PopDi`) and `LeaDi` is the one documented computed emitter. `t46_strindex_var` reads, writes, compares, passes as an argument and assigns both-indexed (`g[i] := h[j]`), globally and locally; `t47_strindex_var_read` is `readln (g[i])` (48/48 compile, 43/43 executed)** |
|
|
| Variable string subscripts — `s[i]` with a *computed* index | `v-TP3-STRIDX-VAR` | done, **the address of `s[i]` is now computed at run time, so a variable index, a computed one (`i+1`) and a nested one all work — `v-TP3-STRINDEX` accepted a constant only. `ParseSub` evaluates the index into `AX`, then `LEA DI,[base]` / `ADD DI,AX` puts the element's address in `DI` and `r.kind` drops to `5`; `LoadAtom` reads `[DI]`, `Assignment` `PUSH DI`es the destination across its right-hand side (so `g[i] := h[j]` stores where it read — TPSRC8 `asnscal2`/`asnscal3`'s `flgpshdi`, and `SaveLeft` materialises a `DI`-addressed left operand *before* parsing the right), and `IoCall` pushes `DI` for `readln (s[i])`. No runtime entry: the arithmetic is compile-time emitted, so the runtime image is unchanged. Five new one-line emitters are name-audited (`AddDiAx`, `LdAlDi`, `StDiAl`, `PushDi`, `PopDi`) and `LeaDi` is the one documented computed emitter. `t46_strindex_var` reads, writes, compares, passes as an argument and assigns both-indexed (`g[i] := h[j]`), globally and locally; `t47_strindex_var_read` is `readln (g[i])` (48/48 compile, 43/43 executed)** |
|
|
|
|
|
+| String comparison — `= <> < <= > >=` with both operands strings | `v-TP3-STRCMP` | done, **`ParseCmp` gets a string arm ahead of the constant-fold arm: two addresses are pushed (`EmPushStrAddr`), `cmpstr` — runtime selector 18 — returns `AX = -1/0/+1`, and the shared `SETcc` table turns the sign into a Boolean. `cmpstr` (83 bytes, `0249h`..`029Bh`) compares `min(lenL,lenR)` bytes **unsigned** — first difference wins — and breaks a tie on the lengths, so a proper prefix is *less* (`'ab' < 'abc'`, `'' < everything`); the compiler's test on `-1` is **signed** (`SETL`, not `SETB`: `-1` is `0FFFFh`). It returns a value rather than flags because the caller drops the two addresses with `ADD SP,4`, which destroys the flags (TP3's own `cmpstr` removes its arguments itself and so can leave them). A string literal travels inline in the code stream (`JMP` over `<len><chars>`, address = the length byte) because a `.COM` carries no initialised data; a one-character literal is a `CHAR`, so `s = 'a'` is `ETypeErr` 56 while `''` is the empty string. The first epilogue saved `AX`, which restored the *caller's* `AX` (the right operand's address) and inverted every ordering comparison — only executing `t48` could see it. `t48_strcmp` weights the six operators 1/2/4/8/16/32 so the printed sum identifies which held; `t49_strcmp_lit` is the literal operand; `t50_strcmp_type` is the `CHAR` refusal (51/51 compile, 45/45 executed). Running `tests/nonvacuity.sh` also exposed three mutation anchors `v-TP3-STRIDX-VAR` had silently invalidated, and a `LOOP` that the runtime's branch scan had never checked; both fixed** |
|
|
|
|
|
|
|
|
Every row that names a tag has one, and every tag points at a commit on
|
|
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
|
|
`master`; verified by diffing the rows against `git tag -l`, which is how
|
|
@@ -128,11 +129,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
|
|
scanner fix: the suite went red with exit 1, and came back green only when the
|
|
|
fix was restored.
|
|
fix was restored.
|
|
|
|
|
|
|
|
-**43 of 48 fixtures compile clean**, up from 1 (the empty program) when the
|
|
|
|
|
|
|
+**45 of 51 fixtures compile clean**, up from 1 (the empty program) when the
|
|
|
direct harness was first built.
|
|
direct harness was first built.
|
|
|
|
|
|
|
|
```
|
|
```
|
|
|
-compile matrix: 48 passed, 0 failed (of 48)
|
|
|
|
|
|
|
+compile matrix: 51 passed, 0 failed (of 51)
|
|
|
```
|
|
```
|
|
|
|
|
|
|
|
Compiling: `t01` minimal · `t04` var+assign+`writeln` · `t06` two args ·
|
|
Compiling: `t01` minimal · `t04` var+assign+`writeln` · `t06` two args ·
|
|
@@ -160,21 +161,27 @@ each name landing on its own slot ·
|
|
|
`t45` `readln (s[i])` storing one character at position `i` ·
|
|
`t45` `readln (s[i])` storing one character at position `i` ·
|
|
|
`t46` `s[i]` with a **variable** index — read, write, compare, an argument and
|
|
`t46` `s[i]` with a **variable** index — read, write, compare, an argument and
|
|
|
`g[i] := h[j]`, globally and locally ·
|
|
`g[i] := h[j]`, globally and locally ·
|
|
|
-`t47` `readln (g[i])` with the index in a variable.
|
|
|
|
|
|
|
+`t47` `readln (g[i])` with the index in a variable ·
|
|
|
|
|
+`t48` all six comparisons between two string **variables**, as branches and as
|
|
|
|
|
+values ·
|
|
|
|
|
+`t49` the same six with a string **literal** as one or both operands, the empty
|
|
|
|
|
+string included.
|
|
|
|
|
|
|
|
Failing, all deliberately: `t14` `array [1..5] of integer` at its point of use
|
|
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
|
|
and `t25` a string literal assigned to an *integer* (`i := 'ab'`, the reverse
|
|
|
direction is still unimplemented) both → `ENoLib`
|
|
direction is still unimplemented) both → `ENoLib`
|
|
|
(102), the original's "not implemented" path; `t38` `readln (b)` of a
|
|
(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
|
|
`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
|
|
|
|
|
|
|
+refuses the whole class because the runtime has no boolean reader at all;
|
|
|
`t43_codewindow` a program whose code exceeds the 4 KiB window → error 98,
|
|
`t43_codewindow` a program whose code exceeds the 4 KiB window → error 98,
|
|
|
-"Memory overflow", rather than a corrupt image; and
|
|
|
|
|
|
|
+"Memory overflow", rather than a corrupt image;
|
|
|
|
|
+`t50_strcmp_type` `s = 'a'` → error 56, because a one-character literal is a
|
|
|
|
|
+`CHAR` and not a string; and
|
|
|
`uierror` is a deliberate syntax error (41) used by the UI test.
|
|
`uierror` is a deliberate syntax error (41) used by the UI test.
|
|
|
|
|
|
|
|
-`run_com_tests.sh` additionally links **all 43 that compile** to a real `.COM`
|
|
|
|
|
|
|
+`run_com_tests.sh` additionally links **all 45 that compile** to a real `.COM`
|
|
|
and re-verifies the bytes with an independent Python checker that *measures*
|
|
and re-verifies the bytes with an independent Python checker that *measures*
|
|
|
-the layout instead of restating it: `independent .COM check: 43 checked, 0
|
|
|
|
|
|
|
+the layout instead of restating it: `independent .COM check: 45 checked, 0
|
|
|
failed`. That last part was itself a bug fix — see "the two restated
|
|
failed`. That last part was itself a bug fix — see "the two restated
|
|
|
constants" below.
|
|
constants" below.
|
|
|
|
|
|
|
@@ -213,13 +220,13 @@ proves each one can go red.
|
|
|
|---|---|---|
|
|
|---|---|---|
|
|
|
| `probe/run_modrm19.py` | the mod=00/01/10 effective addresses, by **executing** 23 cases on a real 8086 under qemu and scanning for where the marker landed | mod=11 — see below |
|
|
| `probe/run_modrm19.py` | the mod=00/01/10 effective addresses, by **executing** 23 cases on a real 8086 under qemu and scanning for where the marker landed | mod=11 — see below |
|
|
|
| `probe/modrm11.py` | the mod=11 register identities, by **encoding** with GNU `as` and decoding with FCML, against hard-coded bytes | the table agreeing with itself |
|
|
| `probe/modrm11.py` | the mod=11 register identities, by **encoding** with GNU `as` and decoding with FCML, against hard-coded bytes | the table agreeing with itself |
|
|
|
-| `audit_helpers.py` | every one-line emitter in **both** `Runtime.mod` and `Compiler.mod` decodes to what its *name* says — **110/110**, from an inventory scanned independently of the parser | anything longer than one instruction |
|
|
|
|
|
|
|
+| `audit_helpers.py` | every one-line emitter in **both** `Runtime.mod` and `Compiler.mod` decodes to what its *name* says — **112/112**, from an inventory scanned independently of the parser | anything longer than one instruction |
|
|
|
| `check_comimage.py` | a linked image's layout is described in **exactly one file**: `comimage.py` defines each part once, every reader imports it, no second `find_header` exists anywhere in `tests/` | a check that *hand-rolls* the layout from literals (`hdr = 3 + rt_size`), which defines none of those names — the readers catch that a different way: each states the layout twice, from two independent sources, and requires the two to agree |
|
|
| `check_comimage.py` | a linked image's layout is described in **exactly one file**: `comimage.py` defines each part once, every reader imports it, no second `find_header` exists anywhere in `tests/` | a check that *hand-rolls* the layout from literals (`hdr = 3 + rt_size`), which defines none of those names — the readers catch that a different way: each states the layout twice, from two independent sources, and requires the two to agree |
|
|
|
-| `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_runtime.py` + `runtime.golden` | the built runtime's code region (668 bytes, code 0..667) sweeps cleanly through FCML, every entry and all branch targets — `Jcc`, `JMP`, `CALL` and `LOOP` — 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 |
|
|
| `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_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, 43/43 | 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 |
|
|
|
|
|
|
|
+| `run_exec86.py` | the same image runs in a **second, independent 8086** and agrees with qemu byte for byte, 45/45 | 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, 51 cases plus a pre-flight, 52/52 — 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
|
|
Two of these deserve the detail, because the reason they exist is the reason
|
|
|
they are hard.
|
|
they are hard.
|
|
@@ -323,7 +330,7 @@ signature (`hdrFlag = 1`, `hdrDS == hdrOff + 1000h + bias`, `hdrHeap > hdrDS`,
|
|
|
`prologAt` and `dataBase` per file:
|
|
`prologAt` and `dataBase` per file:
|
|
|
|
|
|
|
|
```
|
|
```
|
|
|
-measured runtime size: 616 bytes (header at image offset 619)
|
|
|
|
|
|
|
+measured runtime size: 699 bytes (header at image offset 702)
|
|
|
```
|
|
```
|
|
|
|
|
|
|
|
A restated constant that has drifted is worse than a derived one, and the two
|
|
A restated constant that has drifted is worse than a derived one, and the two
|
|
@@ -347,13 +354,13 @@ The only check that asks a question about the *target* rather than about the
|
|
|
compiler: **does the 8086 have this instruction at all?**
|
|
compiler: **does the 8086 have this instruction at all?**
|
|
|
|
|
|
|
|
```
|
|
```
|
|
|
-8086 check: 38 comparison sites, 34 lowered to a Boolean value, 13 lowered to a branch
|
|
|
|
|
- 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: 43 of 43 fixtures swept end to end, 5697 bytes
|
|
|
|
|
|
|
+8086 check: 39 comparison sites, 35 lowered to a Boolean value, 57 lowered to a branch
|
|
|
|
|
+ value conditions : = x7 <> x2 < x5 >= x3 <= x2 > x16
|
|
|
|
|
+ branch conditions : IF / REPEAT x53 CASE x2 FOR downto x1 FOR to x3
|
|
|
|
|
+ runtime: 699 bytes, 376 swept, 0 0F-prefixed
|
|
|
|
|
+ program code: 45 of 45 fixtures swept end to end, 8576 bytes
|
|
|
t33_cmpops: 13 comparisons matched against their source operators, in order
|
|
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
|
|
|
|
|
|
|
+ clause H: 10 of 10 fixtures matched the branch conditions read off their source
|
|
|
```
|
|
```
|
|
|
|
|
|
|
|
It exists because bugs 32 and 33 got past everything else, and because **no
|
|
It exists because bugs 32 and 33 got past everything else, and because **no
|
|
@@ -986,7 +993,7 @@ initialised past it (`MOV pc,#$2D7C`). Same shape here, and now actually done:
|
|
|
image-absolute. **No relocation pass is needed** — worth having paid for, since
|
|
image-absolute. **No relocation pass is needed** — worth having paid for, since
|
|
|
a linker that has to walk fixups is a linker that can get them wrong.
|
|
a linker that has to walk fixups is a linker that can get them wrong.
|
|
|
|
|
|
|
|
-Current blob: **616 bytes**, 18 entries, 51 branch targets, offsets read back
|
|
|
|
|
|
|
+Current blob: **699 bytes**, 19 entries, 62 branch targets, offsets read back
|
|
|
out of the assembled bytes by `tests/check_runtime.py` rather than asserted by
|
|
out of the assembled bytes by `tests/check_runtime.py` rather than asserted by
|
|
|
hand:
|
|
hand:
|
|
|
|
|
|
|
@@ -999,11 +1006,13 @@ hand:
|
|
|
| | | `wrln` | 144 | `rdln` | 411 |
|
|
| | | `wrln` | 144 | `rdln` | 411 |
|
|
|
| | | `wrtinl` | 152 | `rdstr` | 466 |
|
|
| | | `wrtinl` | 152 | `rdstr` | 466 |
|
|
|
| | | `wrstr` | 171 | `strstore` | 530 |
|
|
| | | `wrstr` | 171 | `strstore` | 530 |
|
|
|
|
|
+| | | | | `cmpstr` | 585 |
|
|
|
|
|
|
|
|
Every number in that table is measured, and that is the point: it was last
|
|
Every number in that table is measured, and that is the point: it was last
|
|
|
correct when the blob was 391 bytes, and the entries quietly moved as it grew
|
|
correct when the blob was 391 bytes, and the entries quietly moved as it grew
|
|
|
past 436 to 465, to 497 (`wrstr` joined, `v-TP3-STRVAR`) and to 616 (`rdstr`
|
|
past 436 to 465, to 497 (`wrstr` joined, `v-TP3-STRVAR`) and to 616 (`rdstr`
|
|
|
-and `strstore` joined, `v-TP3-STRREAD-ASSIGN`) — six of the offsets in an
|
|
|
|
|
|
|
+and `strstore` joined, `v-TP3-STRREAD-ASSIGN`), and to 699 (`cmpstr` joined,
|
|
|
|
|
+`v-TP3-STRCMP`) — six of the offsets in an
|
|
|
earlier version of this table were stale, and the size was stale too. A
|
|
earlier version of this table were stale, and the size was stale too. A
|
|
|
restated constant that has drifted is worse than a derived one, which is why
|
|
restated constant that has drifted is worse than a derived one, which is why
|
|
|
the compiler asks `Runtime.RT_Entry` instead (below) and why the checkers
|
|
the compiler asks `Runtime.RT_Entry` instead (below) and why the checkers
|
|
@@ -1107,9 +1116,9 @@ assertion, rather than asking the compiler where it thinks the header is.
|
|
|
last dot, padded with a zero gap to `max(pc, dc)`; its stack sits at the segment
|
|
last dot, padded with a zero gap to `max(pc, dc)`; its stack sits at the segment
|
|
|
top (`SS = SP = CS:FFFE`), which is where DOS puts it.
|
|
top (`SS = SP = CS:FFFE`), which is where DOS puts it.
|
|
|
|
|
|
|
|
-**Known limit:** the data area starts at a fixed `rtSz + 1000H` (616 + 4096 =
|
|
|
|
|
-4712), so a program whose code exceeds 4 KiB runs into its own data. Every
|
|
|
|
|
-fixture's image is between 4719 and 4766 bytes. Documented rather than fixed,
|
|
|
|
|
|
|
+**Known limit:** the data area starts at a fixed `rtSz + 1000H` (699 + 4096 =
|
|
|
|
|
+4795), so a program whose code exceeds 4 KiB runs into its own data. Every
|
|
|
|
|
+fixture's image is between 4802 and 4849 bytes. Documented rather than fixed,
|
|
|
because the
|
|
because the
|
|
|
original has the same fixed-offset behaviour.
|
|
original has the same fixed-offset behaviour.
|
|
|
|
|
|
|
@@ -1242,11 +1251,11 @@ Details that are deliberate, not incidental:
|
|
|
(`7x 03 E9`, searching for the `E9`) and by `t22_case`'s execution, but the
|
|
(`7x 03 E9`, searching for the `E9`) and by `t22_case`'s execution, but the
|
|
|
label immediate itself is unchecked.
|
|
label immediate itself is unchecked.
|
|
|
- **The runtime's entries are now each called directly, and two of its
|
|
- **The runtime's entries are now each called directly, and two of its
|
|
|
- invariants are stated rather than implied.** 616 bytes, 18 entries, 105
|
|
|
|
|
|
|
+ invariants are stated rather than implied.** 699 bytes, 19 entries, 112
|
|
|
emitter helpers decoded against their own names across both modules, the
|
|
emitter helpers decoded against their own names across both modules, the
|
|
|
- whole code region golden-pinned, all branch targets on instruction
|
|
|
|
|
- boundaries — and 50 cases that call the entries one at a time under qemu
|
|
|
|
|
- (`rt_exec.py`, 51/51 with its BP pre-flight). What is *not* covered is what a direct call cannot
|
|
|
|
|
|
|
+ whole code region golden-pinned, every `Jcc`/`JMP`/`CALL`/`LOOP` target on an
|
|
|
|
|
+ instruction boundary — and 51 cases that call the entries one at a time under
|
|
|
|
|
+ qemu (`rt_exec.py`, 52/52 with its BP pre-flight). What is *not* covered is what a direct call cannot
|
|
|
see: `wrtinl` is checked, but only from the harness's side of the calling
|
|
see: `wrtinl` is checked, but only from the harness's side of the calling
|
|
|
contract; `wrstr` only from its seeded-address cases. (The nine fixtures that compiled but were never executed used to sit
|
|
contract; `wrstr` only from its seeded-address cases. (The nine fixtures that compiled but were never executed used to sit
|
|
|
here as an extra gap; they are now run, so every fixture that reaches the
|
|
here as an extra gap; they are now run, so every fixture that reaches the
|
|
@@ -2004,12 +2013,14 @@ independently-scanned inventory at all.
|
|
|
|
|
|
|
|
1. The rest of the string language. `v-TP3-STRVAR`/`v-TP3-STRREAD-ASSIGN` cover
|
|
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)`
|
|
string variables, `s := 'literal'`, `writeln (s)`, `readln (s)`/`read (s)`
|
|
|
- and `s := t`; `v-TP3-STRINDEX` covers `s[i]` with a constant index and
|
|
|
|
|
|
|
+ and `s := t`; `v-TP3-STRINDEX` covers `s[i]` with a constant index,
|
|
|
`v-TP3-STRIDX-VAR` covers a **variable/computed** one — read, write,
|
|
`v-TP3-STRIDX-VAR` covers a **variable/computed** one — read, write,
|
|
|
`readln (s[i])`, comparison against a char, use as an argument, and
|
|
`readln (s[i])`, comparison against a char, use as an argument, and
|
|
|
- `g[i] := h[j]`. Still `ENoLib`: the `+` and the whole-string comparison
|
|
|
|
|
- operators (`cmpstr`/`xconcat`, TPSRC2 — the latter needs a runtime entry,
|
|
|
|
|
- the next selector is 18).
|
|
|
|
|
|
|
+ `g[i] := h[j]` — and `v-TP3-STRCMP` covers the six **whole-string
|
|
|
|
|
+ comparison** operators (`cmpstr`, runtime selector 18). Still `ENoLib`:
|
|
|
|
|
+ concatenation `+` (`xconcat`/`addstr`, TPSRC2 — needs a runtime entry, the
|
|
|
|
|
+ next selector is 19; note TP3 makes `s + 'a'` legal by converting the
|
|
|
|
|
+ one-character operand, the very case `t50` rejects for `=`).
|
|
|
2. Nested procedures / recursion, `var` parameters (the `SEG:OFF` push from
|
|
2. Nested procedures / recursion, `var` parameters (the `SEG:OFF` push from
|
|
|
RESUME-TP3.md §3.11), range/index checks (`TU_RANGE_CHECK`,
|
|
RESUME-TP3.md §3.11), range/index checks (`TU_RANGE_CHECK`,
|
|
|
`TU_INDEX_CHECK`), typed constants (RESUME-TP3.md §3.14), `array` at its
|
|
`TU_INDEX_CHECK`), typed constants (RESUME-TP3.md §3.14), `array` at its
|