Browse Source

compiler: multi-name declarations, and an enforced 4 KiB code window

Two of the four "Next steps" items, both in the declaration parser and
the emitter.

Multi-name declarations (v-TP3-MULTIDECL).  TP3 reads a whole
comma-separated list of NAMES and then one `: type' (TPSRC7
rdvarlst/rdvloop for VAR, prfcnt/ccomma for formal parameters), groups
separated by `;'.  DefVar and ProcFunc now do the same: each name is
created as a placeholder and given the group's single cls/size/elem, and
parmOff advances once per name.  The older `name : type, name : type'
spelling still parses, since both reach the same symbols.

The bug that fix exposed is the `GetWord'-does-not-Skip class again:
after MatchDelim(',') the cursor sits on the space, so the first name of
a VAR group parsed and every later name became `" B"'.  t42_multidecl
prints every name and is the fixture that made it red.

The 4 KiB code window (v-TP3-CODEWINDOW).  dataBase is rtSz + 1000H, so
code and data share one 64 KiB segment and code may not reach it.  Ebyte
now reports TP3's error 98 ("Memory overflow") at pc >= dataBase, gated
by a new imgReady flag because dataBase is 0 until Compile lays it out.
With the gate disabled t43_codewindow compiles OK -- the silent overrun
-- which is what makes the fixture non-vacuous.

Gates: compile matrix 44/44, run_com_exec 39/39, exec86 39/39, runtime
entries 51/51, OVERALL: ALL PASS.  tpshell 191104 bytes.

Both tags point at this commit: v-TP3-MULTIDECL, v-TP3-CODEWINDOW.
Eric Streit 1 day ago
parent
commit
f0830dc5dd

+ 31 - 22
SUMMARY.md

@@ -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** |
 | 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 *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)** |
 | 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
 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
@@ -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
         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`,
 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
 which is the expected phase-1 rollup that the recipe tolerates — not a real
 error. `shell/Makefile` is the single authoritative build recipe.
 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
 scanner fix: the suite went red with exit 1, and came back green only when the
 fix was restored.
 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.
 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 ·
 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
 `t39` `s : string [10]`, assigned a literal and written — globally and from
 inside a procedure, so both address paths run ·
 inside a procedure, so both address paths run ·
 `t40` `readln` into a global *and* a local `string` ·
 `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
 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
@@ -157,11 +161,13 @@ 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; 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.
 `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*
 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
 failed`. That last part was itself a bug fix — see "the two restated
 constants" below.
 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_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 |
 | `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, 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 |
 | `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
 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`
 ### Execution under qemu — `tests/run_com_exec.py`
 
 
 The check that cannot be written as a byte comparison, so also the one that
 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
 and their serial output compared to a committed `.out` file **exactly** — CRLF
 included — plus the exit code passed to `INT 21h AH=4Ch`.
 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
             value conditions  : = x6  <> x2  < x5  >= x3  <= x2  > x16
             branch conditions : IF / REPEAT x9  CASE x2  FOR downto x1  FOR to x3
             branch conditions : IF / REPEAT x9  CASE x2  FOR downto x1  FOR to x3
             runtime: 616 bytes, 330 swept, 0 0F-prefixed
             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
             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: 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
 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
 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
 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
 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
 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),
   prove the *path* exists (compile → poke → run → report → no file written),
   which it does with eight assertions. The other 37 are covered by
   which it does with eight assertions. The other 37 are covered by
   `run_exec86.py`, which calls the same interpreter directly.
   `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,
   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
   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,
   *never executed* — `t08` const, `t09` if/then/else, `t10` while, `t11` for/to,
   `t12` repeat/until, `t13` procedure + value param, `t15` label + goto,
   `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`,
    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
    point of use (`t14`), `case` with subrange labels.
    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.
    (`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 —
    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,
    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
    and `Exec86` deliberately implements only the three functions the runtime
    calls.
    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.

+ 49 - 6
TP3-COMPILER.md

@@ -106,6 +106,46 @@ Three related defects fixed alongside:
   is attached to, so `Compound` then demanded a `;` that does not exist in
   is attached to, so `Compound` then demanded a `;` that does not exist in
   `1: x := 1`.  `Statmnt` now parses the labelled statement.
   `1: x := 1`.  `Statmnt` now parses the labelled statement.
 
 
+## Multi-name declarations, both spellings (the other `Skip` bug)
+
+TP3 collects a whole comma-separated list of names and then reads one `: type`
+for the list: `rdvarlst`/`rdvloop` for `VAR` (TPSRC7 ~1267, `CALL ccomma / JZ
+rdvloop`), `prfcnt`/`ccomma` for formal parameters (TPSRC7 ~891-925).  Groups
+are separated by `;`.  So both
+
+```
+var a, b, c : integer ;
+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 -- but it is accepted here too, because both reach
+the same symbols and refusing it would turn a working program red for no
+behavioural reason.  `DefVar` now creates a placeholder symbol 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 bug this exposed is the same class as the one above: `GetWord` does **not**
+`Skip` the blanks first (it reads `CurCh` as the first character), and after
+`MatchDelim (',')` the cursor sits on the space after the comma.  The first
+name of a `VAR` group parsed (its `Skip` came from the group header) and every
+later name silently became `" B"`; `t42_multidecl` prints each name's value and
+is the fixture that made it red.
+
+## The 4 KiB code window is enforced
+
+`dataBase` is `rtSz + 1000H`: code and data share one 64 KiB `.COM` segment,
+so the program's code may not grow into the data area.  `Ebyte` used to check
+only `MaxCode` (24000), so a program whose code passed `dataBase` wrote over
+its own globals and produced a plausible, wrong image with no diagnostic.
+`Ebyte` now reports TP3's error 98 ("Memory overflow") when `pc >= dataBase`,
+gated on a new `imgReady` flag because `dataBase` is still 0 until `Compile`
+measures the runtime and lays out the data area.  `t43_codewindow` emits more
+than 4 KiB of inline string bytes and must report `ERR 98`; with the gate
+disabled the same fixture compiles `OK` -- the corrupt image -- which is the
+proof the test is not vacuous.
+
 ## Tests
 ## Tests
 
 
 Several harnesses live in `shell/tests/` -- in the repo on purpose, because
 Several harnesses live in `shell/tests/` -- in the repo on purpose, because
@@ -141,7 +181,9 @@ use) and `t25` (a string literal assigned to an integer, `i := 'ab'`) → `ENoLi
 (102) -- the original's own "not implemented" path, and pinned on
 (102) -- the original's own "not implemented" path, and pinned on
 purpose so that implementing them is a deliberate change to a test rather than
 purpose so that implementing them is a deliberate change to a test rather than
 an accident; `t38` (`readln (b)` of a `boolean`) → error 66, TP3's "I/O not
 an accident; `t38` (`readln (b)` of a `boolean`) → error 66, TP3's "I/O not
-allowed here", for the same reason; and `uierror` is a deliberate syntax error
+allowed here", for the same reason; `t43_codewindow` emits more than the 4 KiB
+between the runtime and the data base → error 98, "Memory overflow", rather
+than a silently corrupt image; and `uierror` is a deliberate syntax error
 used by `uitest.py`.  (`t39_strvar` is the counter-example: a *string*
 used by `uitest.py`.  (`t39_strvar` is the counter-example: a *string*
 variable, `s : string [10]`, assigned a literal and written -- the first
 variable, `s : string [10]`, assigned a literal and written -- the first
 declared type wider than a word that compiles, and run under both oracles.)
 declared type wider than a word that compiles, and run under both oracles.)
@@ -154,13 +196,13 @@ byte is a perfectly well-formed instruction that does something else, so no
 amount of inspecting the compiler's intent will catch it.  This harness closes
 amount of inspecting the compiler's intent will catch it.  This harness closes
 that gap the only way it can be closed -- by running the thing.
 that gap the only way it can be closed -- by running the thing.
 
 
-For each of 38 fixtures it compiles a `.COM`, writes a 1.44 MB floppy with the
+For each of 39 fixtures it compiles a `.COM`, writes a 1.44 MB floppy with the
 image in it, boots `qemu-system-i386 -fda disk.img -serial out.txt`, and
 image in it, boots `qemu-system-i386 -fda disk.img -serial out.txt`, and
 compares `out.txt` to the committed `tests/fixtures/tNN.out` **exactly** --
 compares `out.txt` to the committed `tests/fixtures/tNN.out` **exactly** --
 CRLF included -- plus the exit code the program hands to `INT 21h AH=4Ch`.
 CRLF included -- plus the exit code the program hands to `INT 21h AH=4Ch`.
 
 
 ```
 ```
-cd shell && python3 tests/run_com_exec.py            # all 38
+cd shell && python3 tests/run_com_exec.py            # all 39
 cd shell && python3 tests/run_com_exec.py --show     # print the serial file
 cd shell && python3 tests/run_com_exec.py --show     # print the serial file
 cd shell && python3 tests/run_com_exec.py --rebless  # rewrite the .out files
 cd shell && python3 tests/run_com_exec.py --rebless  # rewrite the .out files
 ```
 ```
@@ -189,9 +231,10 @@ partial read or a wrong sector count would have been visible, this was not.
 window at `C000h`; and the copy is executed code, so it is the last thing
 window at `C000h`; and the copy is executed code, so it is the last thing
 that touches the image and no BIOS call follows it.
 that touches the image and no BIOS call follows it.
 
 
-**38 of 42 fixtures execute.**  4 do not: `t14` and `t25` (`ENoLib`, by
-design), `t38` (a boolean read, refused with TP3's error 66, by design) and
-`uierror` (a deliberate syntax error).  The nine control-flow fixtures (`t08`
+**39 of 44 fixtures execute.**  5 do not: `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 deliberate syntax error).  The nine control-flow fixtures (`t08`
 const, `t09` if, `t10` while, `t11` for, `t12` repeat, `t13` procedure, `t15`
 const, `t09` if, `t10` while, `t11` for, `t12` repeat, `t13` procedure, `t15`
 label, `t27` five locals, `t28` 70 parameters) used to compile without a
 label, `t27` five locals, `t28` 70 parameters) used to compile without a
 `.out` and were never run at all; they now print and are covered, which closed
 `.out` and were never run at all; they now print and are covered, which closed

+ 109 - 38
shell/Compiler.mod

@@ -243,6 +243,12 @@ VAR
       prepended. *)
       prepended. *)
    rtSz, dataBase : CARDINAL ;
    rtSz, dataBase : CARDINAL ;
 
 
+   (* TRUE once Compile has computed rtSz/dataBase, i.e. once the code window
+      has been measured.  Ebyte refuses to write past dataBase (the 4 KiB code
+      window) and needs to know the window is real before it can: dataBase is
+      0 until it is set, and `pc >= 0' would abort on the very first byte. *)
+   imgReady : BOOLEAN ;
+
    (* Image offset of the entry jump's rel16 operand, patched at the end of
    (* Image offset of the entry jump's rel16 operand, patched at the end of
       Compile.  The jump is at image offset 0, so its displacement is simply
       Compile.  The jump is at image offset 0, so its displacement is simply
       the program code's end - 3. *)
       the program code's end - 3. *)
@@ -424,7 +430,13 @@ END OK ;
 
 
 PROCEDURE Ebyte (b : BYTE) ;
 PROCEDURE Ebyte (b : BYTE) ;
 BEGIN
 BEGIN
-   IF pc >= MaxCode THEN
+   IF (pc >= MaxCode) OR (imgReady AND (pc >= dataBase)) THEN
+      (* The image buffer bound, and the code window.  dataBase is a fixed
+         4 KiB above the end of the runtime, so a program whose code reaches
+         it has grown into its own data area - the two cannot share a 64 KiB
+         .COM segment.  Reported as TP3's error 98 (memory overflow) rather
+         than a silent truncation; see the layout note in Compile and
+         SUMMARY.md "Known limit". *)
       Err (EMemOvf)
       Err (EMemOvf)
    ELSE
    ELSE
       cbuf [pc] := b ;
       cbuf [pc] := b ;
@@ -3035,6 +3047,7 @@ VAR nm : ARRAY [0..MaxName] OF CHAR ;
     tok : CARDINAL ;
     tok : CARDINAL ;
     v : LONGINT ;
     v : LONGINT ;
     off : CARDINAL ;
     off : CARDINAL ;
+    first, i : CARDINAL ;
 BEGIN
 BEGIN
    LOOP
    LOOP
       Skip () ;                (* after the VAR keyword: skip blanks *)
       Skip () ;                (* after the VAR keyword: skip blanks *)
@@ -3042,15 +3055,32 @@ BEGIN
          Err (EUnknown) ;
          Err (EUnknown) ;
          RETURN
          RETURN
       END ;
       END ;
+      (* A group is `name (, name)* : type' - TPSRC7 rdvarlst/rdvloop collects
+         the whole comma-separated list of names FIRST (`CALL ccomma / JZ
+         rdvloop'), then reads one `: type' with ecolon/rdvartp, then vardef2
+         gives every name in the list that one type.  The names are created as
+         placeholders here and filled in once the type is known, which is the
+         same shape as TP3 reserving a symtab slot per name and storing the
+         type pointer into each afterwards. *)
+      first := symTop ;
       LOOP
       LOOP
+         Skip () ;             (* GetWord does not skip blanks, and the cursor
+                                  sits on the space after the ',' *)
+         IF NOT Alpha (CurCh ()) THEN
+            Err (EUnknown) ;
+            RETURN
+         END ;
          GetWord () ;
          GetWord () ;
          SaveWord (nm) ;
          SaveWord (nm) ;
          DupTest (nm) ;
          DupTest (nm) ;
-         IF NOT MatchDelim (':') THEN
-            Err (ENoSemi)
-         END ;
-         ParseType (cls, size, elem) ;
-         off := 0 ;
+         DropC (NewSym (nm, KVar, TNone, 0, 0, 0, 0, lexnest # 0)) ;
+         IF NOT MatchDelim (',') THEN
+            EXIT
+         END
+      END ;
+      ExpectDelim (':', ENoSemi) ;
+      ParseType (cls, size, elem) ;
+      IF OK () THEN
          IF lexnest = 0 THEN
          IF lexnest = 0 THEN
             IF (size > 2) AND (cls # TString) THEN
             IF (size > 2) AND (cls # TString) THEN
                (* Still the catch-all for a type whose storage this compiler does
                (* Still the catch-all for a type whose storage this compiler does
@@ -3068,14 +3098,27 @@ BEGIN
                   RdIntConst (v) ;
                   RdIntConst (v) ;
                   off := W16 (v)
                   off := W16 (v)
                ELSE
                ELSE
-                  Err (EUnknown)
+                  Err (EUnknown) ;
+                  off := 0
+               END ;
+               (* TP3's absolute path (vdstore) leaves the offset fixed, so
+                  every name in the group aliases the same address. *)
+               i := first ;
+               WHILE i < symTop DO
+                  symtab [i].cls := cls ; symtab [i].size := size ;
+                  symtab [i].elem := elem ; symtab [i].off := off ;
+                  INC (i)
                END
                END
             ELSE
             ELSE
-               off := dc ;
-               dc := dc + size
+               i := first ;
+               WHILE i < symTop DO
+                  symtab [i].cls := cls ; symtab [i].size := size ;
+                  symtab [i].elem := elem ; symtab [i].off := dc ;
+                  dc := dc + size ;
+                  INC (i)
+               END
             END ;
             END ;
-            DropC (NewSym (nm, KVar, cls, size, elem, off, 0, FALSE)) ;
-            varspc := varspc + size
+            varspc := varspc + size * (symTop - first)
          ELSE
          ELSE
             IF (size > 2) AND (cls # TString) THEN
             IF (size > 2) AND (cls # TString) THEN
                Err (ENoLib) ;
                Err (ENoLib) ;
@@ -3092,23 +3135,33 @@ BEGIN
                pre-decrement rule produced, while a string[5] local (size 6) is
                pre-decrement rule produced, while a string[5] local (size 6) is
                at -6..-1 instead of the old -2..+3 - which straddled the saved
                at -6..-1 instead of the old -2..+3 - which straddled the saved
                BP at [BP] and the return address at [BP+2] and made `readln(s)'
                BP at [BP] and the return address at [BP+2] and made `readln(s)'
-               into a local store characters over its own return address. *)
-            DropC (NewSym (nm, KVar, cls, size, elem,
-                           (locFree - size + 1) MOD 10000H, 0, TRUE)) ;
-            locFree := (locFree - size) MOD 10000H ;
-            locBytes := locBytes + size
-         END ;
-         IF NOT MatchDelim (',') THEN
-            EXIT
+               into a local store characters over its own return address.
+               Each name in the group gets its own slot. *)
+            i := first ;
+            WHILE i < symTop DO
+               symtab [i].cls := cls ; symtab [i].size := size ;
+               symtab [i].elem := elem ;
+               symtab [i].off := (locFree - size + 1) MOD 10000H ;
+               locFree := (locFree - size) MOD 10000H ;
+               INC (i)
+            END ;
+            locBytes := locBytes + size * (symTop - first)
          END
          END
       END ;
       END ;
-      IF NOT MatchDelim (';') THEN
+      IF MatchDelim (',') THEN
+         (* The older `name : type, name : type' spelling: another group
+            follows without a semicolon.  TP3's grammar is names-then-one-type
+            with ',' inside a group and ';' between groups (TPSRC7 rdvarlst /
+            rdvloop then ecolon/rdvartp), and both spellings reach the same
+            symbols, so both separators are accepted here. *)
+      ELSIF MatchDelim (';') THEN
+         PeekKw (tok) ;
+         IF tok # TkNone THEN
+            RETURN
+         END
+      ELSE
          Err (ENoSemi) ;
          Err (ENoSemi) ;
          RETURN
          RETURN
-      END ;
-      PeekKw (tok) ;
-      IF tok # TkNone THEN
-         RETURN
       END
       END
    END
    END
 END DefVar ;
 END DefVar ;
@@ -3203,7 +3256,7 @@ VAR nm : ARRAY [0..MaxName] OF CHAR ;
     saveNest, saveLoc, saveRes, saveF : CARDINAL ;
     saveNest, saveLoc, saveRes, saveF : CARDINAL ;
     saveLB, savePO : CARDINAL ;
     saveLB, savePO : CARDINAL ;
     nestMark : CARDINAL ;             (* symTop just inside this procedure *)
     nestMark : CARDINAL ;             (* symTop just inside this procedure *)
-    i : CARDINAL ;
+    i, first : CARDINAL ;
 BEGIN
 BEGIN
    isFunc := curIsFunc ;
    isFunc := curIsFunc ;
    Skip () ;                   (* after the PROCEDURE/FUNCTION keyword *)
    Skip () ;                   (* after the PROCEDURE/FUNCTION keyword *)
@@ -3246,26 +3299,43 @@ BEGIN
                (* VAR parameter recorded as value in this milestone *)
                (* VAR parameter recorded as value in this milestone *)
                DropB (MatchKey (tok))
                DropB (MatchKey (tok))
             END ;
             END ;
-            Skip () ;             (* blanks before the parameter name *)
-            IF NOT Alpha (CurCh ()) THEN
-               Err (EUnknown) ;
-               RETURN
+            first := symTop ;
+            LOOP
+               Skip () ;          (* blanks before the parameter name *)
+               IF NOT Alpha (CurCh ()) THEN
+                  Err (EUnknown) ;
+                  RETURN
+               END ;
+               GetWord () ;
+               SaveWord (parmNm) ;
+               DupTest (parmNm) ;
+               DropC (NewSym (parmNm, KVar, TNone, 0, 0, 0, 0, TRUE)) ;
+               IF NOT MatchDelim (',') THEN
+                  EXIT
+               END
             END ;
             END ;
-            GetWord () ;
-            SaveWord (parmNm) ;
-            DupTest (parmNm) ;
             ExpectDelim (':', ENoSemi) ;   (* formal is 'name : type' *)
             ExpectDelim (':', ENoSemi) ;   (* formal is 'name : type' *)
             ParseType (cls, size, elem) ;
             ParseType (cls, size, elem) ;
             IF size > 2 THEN
             IF size > 2 THEN
                Err (ENoLib) ;
                Err (ENoLib) ;
                RETURN
                RETURN
             END ;
             END ;
-            DropC (NewSym (parmNm, KVar, cls, size, elem, parmOff, 0, TRUE)) ;
-            parmOff := parmOff + 2 ;
-            IF NOT MatchDelim (',') THEN
-               IF MatchDelim (')') THEN
-                  EXIT
-               END ;
+            i := first ;
+            WHILE i < symTop DO
+               symtab [i].cls := cls ; symtab [i].size := size ;
+               symtab [i].elem := elem ;
+               symtab [i].off := parmOff ;
+               parmOff := parmOff + 2 ;
+               INC (i)
+            END ;
+            IF MatchDelim (')') THEN
+               EXIT
+            END ;
+            IF MatchDelim (';') THEN
+               (* next group *)
+            ELSIF MatchDelim (',') THEN
+               (* the older `name : type, name : type' spelling *)
+            ELSE
                Err (ENoSemi) ;
                Err (ENoSemi) ;
                EXIT
                EXIT
             END
             END
@@ -3542,6 +3612,7 @@ BEGIN
    rtSz := rt + EntSize ;
    rtSz := rt + EntSize ;
    dataBase := rtSz + 1000H ;
    dataBase := rtSz + 1000H ;
    dc := dataBase ;
    dc := dataBase ;
+   imgReady := TRUE ;
    strTop := 0 ;
    strTop := 0 ;
    strCnt := 0 ;
    strCnt := 0 ;
    rdStrX := 0 ;
    rdStrX := 0 ;

+ 16 - 0
shell/tests/fixtures/expected.tsv

@@ -290,4 +290,20 @@ t38_readbool	ERR	66	659
 t39_strvar	OK	217	15
 t39_strvar	OK	217	15
 t40_readln	OK	163	15
 t40_readln	OK	163	15
 t41_strassign	OK	263	51
 t41_strassign	OK	263	51
+#   t42_multidecl   multi-name declarations in both spellings: `var a, b, c :
+#                   integer', `procedure add3 (x, y, z : integer)' and
+#                   `procedure mix (u, v : integer ; w : integer)' (TP3's
+#                   grammar: names joined by ',', groups by ';'), plus the
+#                   older `p : integer, q : integer' form.  data=12 is the four
+#                   2-byte globals a,b,c,d plus the two @@T temporaries; the
+#                   .out proves each name landed on its own slot.
+t42_multidecl	OK	304	12
 uierror	ERR	41	331
 uierror	ERR	41	331
+#   t43_codewindow  a program whose code exceeds the 4 KiB window between the
+#                   end of the runtime and the data base (runtime + 1000h).  It
+#                   must be REPORTED as TP3 error 98 (memory overflow); before
+#                   Ebyte was bounded by dataBase it overran its own data and
+#                   produced a corrupt image with no diagnostic.  Each of the 20
+#                   writeln cases below embeds 240 bytes inline (wrtinl), so the
+#                   overflow lands inside one of them at pos 4978.
+t43_codewindow	ERR	98	4978

+ 4 - 0
shell/tests/fixtures/t42_multidecl.out

@@ -0,0 +1,4 @@
+6
+9
+1 2 3
+123

+ 43 - 0
shell/tests/fixtures/t42_multidecl.pas

@@ -0,0 +1,43 @@
+program t42;
+(* Multi-name declarations, in both spellings TP3 accepts.
+
+   TP3's grammar (TPSRC7) collects a comma-separated list of NAMES, then reads
+   ONE `: type' for the whole list (rdvarlst/rdvloop for VAR, prfcnt/ccomma for
+   parameters), and separates parameter groups with ';'.  So all three of
+
+      var a, b, c : integer ;        (names joined by ',')
+      procedure p (x, y : integer)   (names joined by ',')
+      procedure q (x : integer ; y : integer)   (groups joined by ';')
+
+   are legal, and this fixture uses each.  The older `name : type, name : type'
+   spelling is exercised too, so it keeps working.  Every variable and every
+   parameter must still land on the same slot layout as if written one per
+   line, which is what the printed values check. *)
+var
+  a, b, c : integer ;
+  d : integer ;
+procedure add3 (x, y, z : integer) ;
+begin
+  d := x + y + z
+end ;
+procedure add2 (p : integer, q : integer) ;
+begin
+  d := d + p + q
+end ;
+procedure mix (u, v : integer ; w : integer) ;
+begin
+  d := u * 100 + v * 10 + w
+end ;
+begin
+  a := 1 ;
+  b := 2 ;
+  c := 3 ;
+  add3 (a, b, c) ;
+  writeln (d) ;
+  d := 0 ;
+  add2 (4, 5) ;
+  writeln (d) ;
+  writeln (a, ' ', b, ' ', c) ;
+  mix (1, 2, 3) ;
+  writeln (d)
+end.

+ 31 - 0
shell/tests/fixtures/t43_codewindow.pas

@@ -0,0 +1,31 @@
+program t43;
+(* Code-window overflow.  TP3-comp places the data area a fixed 4 KiB above the
+   end of the runtime, so the program code and the program's data share one
+   64 KiB .COM segment and the code may NOT reach the data base.  This program
+   emits more than 4 KiB of code - each string case below is written inline
+   into the code stream by wrtinl (TPSRC4 xwrtstr's literal path) - and the
+   compiler must REPORT that (TP3 error 98, "Memory overflow") rather than
+   write over the data area.  Before the limit was enforced this fixture
+   overran dataBase silently and the image was corrupt. *)
+begin
+  writeln ('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA') ;
+  writeln ('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA') ;
+  writeln ('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA') ;
+  writeln ('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA') ;
+  writeln ('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA') ;
+  writeln ('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA') ;
+  writeln ('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA') ;
+  writeln ('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA') ;
+  writeln ('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA') ;
+  writeln ('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA') ;
+  writeln ('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA') ;
+  writeln ('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA') ;
+  writeln ('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA') ;
+  writeln ('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA') ;
+  writeln ('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA') ;
+  writeln ('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA') ;
+  writeln ('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA') ;
+  writeln ('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA') ;
+  writeln ('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA') ;
+  writeln ('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA') ;
+end.