Kaynağa Gözat

Add Nova128 pass-3 code relocation and counter fixture.

Lets pass 2 resolve absolute flow targets against a final CODE base and verifies a source program through deterministic PRG/MAP output and a real x128 run. Count-mode sizing, method/class MAP records, and unresolved-reference diagnostics remain in-progress.
main
Daniel Covington 4 gün önce
ebeveyn
işleme
0f8ec390c9
12 değiştirilmiş dosya ile 623 ekleme ve 10 silme
  1. +19
    -0
      newLang/docs/architecture.md
  2. +2
    -0
      newLang/docs/decisions.md
  3. +4
    -2
      newLang/docs/memory-map.md
  4. +74
    -0
      newLang/planning/claims/E04-S07.md
  5. +37
    -0
      newLang/scripts/build-counter-test.ps1
  6. +90
    -0
      newLang/scripts/verify-counter-test.ps1
  7. +19
    -2
      newLang/src/compiler/emit.s
  8. +23
    -4
      newLang/src/compiler/flow.s
  9. +11
    -2
      newLang/src/compiler/parser.s
  10. +31
    -0
      newLang/src/compiler/sizing.s
  11. +305
    -0
      newLang/tests/compiler/counter-test.s
  12. +8
    -0
      newLang/tests/compiler/counter.n8

+ 19
- 0
newLang/docs/architecture.md Dosyayı Görüntüle

@@ -607,6 +607,25 @@ prologue, and sizing/pass-3 addresses remain unimplemented. `for` currently
requires a constant byte end and step 1 and leaves the loop variable one past
the end value. E04-S07 owns those limits and the 255-byte code buffer.

### 7.12 Pass-3 relocation and counter (E04-S07, partial)

`sizing.s` computes the final CODE base from `PRG_ENTRY_ADDR` plus the bytes
before the section and installs it through `parse_file_base`. `flow.s` resolves
absolute `JMP` and `JSR` operands against that base; relative branches remain
offset-based. Normal `parse_file` clears the base request, so the in-place
statement and call fixtures keep using `cg_code`.

`counter.n8` is a two-local program: `total = 0`, then `for i = 1 to 5` adds
`i` to `total`. Its fixture emits `COUNTER.PRG` and `COUNTB.PRG` with final
`STUB`, `START`, and `CODE` addresses, checks the two files are byte-identical,
and runs `COUNTER.PRG` in a second x128 session. The fixture-owned wrapper
selects CR `$0E`, points `FRAME_FP` at the software frame area, calls the code,
reports `total`, and restores the entry mapping. The observed result is 15.

This closes final code relocation and the first real source-to-PRG run, but not
E04-S07's count-mode routine sizing, method/class MAP records, or a single
unresolved-reference diagnostic.

## 8. Heap and Ownership

- The generated-program heap initially resides in the same RAM bank as program


+ 2
- 0
newLang/docs/decisions.md Dosyayı Görüntüle

@@ -66,6 +66,8 @@ documented default. `Proposed` means a later change still needs approval.
| D137 | E04-S06 emission is driven from parser pass 2: with `em_on` set, the parser calls `emit.s` as it type-checks each statement, and `parse_file` resets the emitter (and the code buffer) at the start of pass 2; pass 1 only assigns the same slots. Parameters take `FP+2` upward in declaration order, locals `FP-1` downward, one byte for byte-sized types and two for words. A slot is reached by emitting `EM_LP = FRAME_FP + offset` with the offset's sign byte, because `(FP),Y` cannot address below `FP`; `EM_LP` is `$F5-$F6` and the combine temp `EM_TMP` is `$F7`. Byte `+`/`-` keep the left operand on the hardware stack. Print calls go through the `em_pr_text/byte/cr` cells and three `JMP (cell)` trampolines, so a fixture can record output without CHROUT. Emitter failures set `em_bad` (`EM_ERR_SIZE`, `EM_ERR_TARGET`, `EM_ERR_OP`, `EM_ERR_STATE`) and stop emitting; nothing is written outside the code buffer. | Gives the first parser-to-codegen path a testable shape without freezing pass 3: the fixture executes the emitted bytes in place and checks locals and recorded output. The 255-byte buffer, one captured string literal, and the fixture-owned frame prologue are known limits for E04-S07, which owns sizing, section addresses, and `frame_enter`. Control flow, calls, fields, word values, arrays, and diagnostic reporting remain E04-S06 work. Implemented by `emit.s`, `emit.inc`, the `parser.s` hooks, `codegen.s`, and the statements fixture. |
| D138 | E04-S06 completes the pass-2 emission path above D137. `flow.s` grows to 64 labels and 128 fixups; `if`/`else`, `while`, a constant-byte-end positive-step-1 `for`, and `return` emit through five-byte branches, `JMP` back edges, and `JSR` fixups. `fl_release` remains a successful no-op because released labels could still have unresolved fixups. Each method gets a stable entry label allocated by member row, so a call written before the method body resolves forward; `em_entry` records the program body's offset in `cg_code` so a fixture runs the program rather than a class method. `em_bad` is converted to `E_RES_EMIT_FAILED` (E227) after pass 2. The three print vectors are absolute zero-page cells at `$C0-$C5` (`em_pr_text`, `em_pr_byte`, `em_pr_cr`), so their low byte is never `$FF` and the `JMP (cell)` trampolines avoid the 6502 indirect-JMP page bug; six reserved BSS bytes preserve the existing layout of later emitter state. | Closes the E04-S06 byte-code path without changing the generated container. `for` ends one past its end value and currently requires a constant byte end and step 1; `return` is a bare `RTS` because the frame prologue is still fixture-owned. Word values, call arguments, arrays, method result forwarding, and the sizing/pass-3 address pass remain explicitly unimplemented and belong to E04-S07. Implemented by `emit.s`, `flow.s`, `parser.s`, `codegen.s`, `diagcat.inc`, the statements fixture, and the calls fixture. |

| D139 | E04-S07 begins the pass-3 address path. `flow.s` resolves absolute `JMP`/`JSR` operands against `fl_code_base`, which defaults to `cg_code`; `sizing.s` computes the final code base as `PRG_ENTRY_ADDR + bytes before CODE` and installs it with `em_set_base` before pass 2. `counter.n8` aliases two local bytes, loops `i = 1 to 5`, and sums into `total`; its fixture writes `COUNTER.PRG`/`COUNTB.PRG` with STUB, START, and CODE sections. The generated wrapper selects CR `$0E`, sets `FRAME_FP` to `$BF80`, calls the code base, reads `total`, restores CR, and reports PASS. | Proves final section addresses and generated-code relocation in a real x128 run while keeping existing in-place emitter fixtures unchanged. Count-mode routine sizing/mismatch accounting, method and class records in the MAP, unresolved-reference diagnostics, and general pass-3 scheduling remain E04-S07 work. Implemented by `flow.s`, `emit.s`, `sizing.s`, `counter.n8`, the counter fixture, and its scripts. |

## Deferred

- Native C128/80-column Vim port


+ 4
- 2
newLang/docs/memory-map.md Dosyayı Görüntüle

@@ -70,6 +70,7 @@ the first field at +2, so generated code needs no runtime translation.
| Method labels | 256 bytes, one entry label by member row |
| Program entry | `em_entry` = offset of the program body in `cg_code` |
| Code buffer | `cg_code`, 255 bytes (E04-S03) |
| Final code base | `fl_code_base`, default `cg_code`; pass 3 uses `PRG_ENTRY_ADDR + pre-CODE bytes` |
| Print entries | `em_pr_text`/`em_pr_byte`/`em_pr_cr` cells at `$C0-$C5`, with `JMP (cell)` trampolines |

`EM_LP` and `EM_TMP` sit below `MB_SELF` (`$F9-$FA`) and clear of the frame
@@ -81,8 +82,9 @@ frame sizes.
Control-flow labels use the `flow.s` pool; `if`/`while`/`for` labels are
released only after their fixups resolve. Method entry labels are allocated by
member row, so a forward self-call can target a method body parsed later. The
255-byte code buffer and the one-byte `em_entry` bound the current emitted
program; E04-S07 replaces both with the sizing and pass-3 address pass.
255-byte code buffer and the one-byte `em_entry` still bound the current
emitted program; pass 3 currently relocates absolute targets but does not yet
replace either limit with count-mode sizing.

## Heap Fixture (E05-S01)



+ 74
- 0
newLang/planning/claims/E04-S07.md Dosyayı Görüntüle

@@ -0,0 +1,74 @@
---
story: E04-S07
owner: codebuff
status: in-progress
started: 2026-11-10
depends-on: [E04-S06]
---

# E04-S07 Claim

## Intended change

Begin the pass-3 address path: relocate absolute flow targets against the
final CODE base, add `sizing.s` address planning, and prove a real source file
compiles to deterministic PRGs and runs in x128.

## File area

- `src/compiler/flow.s`, `src/compiler/emit.s`, `src/compiler/sizing.s`
- `tests/compiler/counter.n8`, `tests/compiler/counter-test.s`
- `scripts/build-counter-test.ps1`, `scripts/verify-counter-test.ps1`
- `docs/decisions.md`, `docs/architecture.md`, `docs/memory-map.md`

## Acceptance evidence

- [x] Pass 3 can patch absolute labels/JMP/JSR against a final CODE base:
`counter.n8`'s 175-byte routine runs from `CODE 1C4F`.
- [x] `counter.n8` compiles from source and runs in x128: the generated PRG
reports `total = 15` through the mailbox.
- [x] Recompiling the same source is byte-identical: `COUNTER.PRG` and
`COUNTB.PRG` compare equal.
- [x] MAP `ENTRY` and SECTION records carry final addresses for STUB, START,
and CODE.
- [ ] Count-mode sizing for every routine with a zero mismatch counter.
- [ ] MAP class-descriptor and method-entry address records.
- [ ] One diagnostic for an unresolved method/class reference.
- [ ] Reconfirm E202 overwrite refusal in the pass-3 path.

## Coordination notes

- This is intentionally a partial claim; the story is not marked done.
- Existing statement/call fixtures still use `cg_code` as the base, so their
in-place execution behavior is unchanged.
- The counter fixture owns its startup wrapper and frame base, like the
E04-S05 hello fixture. General runtime startup remains open.

## Work log

- Added `fl_set_base`; absolute fixups now add the selected base instead of
hard-coding `cg_code`.
- Added `em_set_base`/`em_base_set`, applied at `em_reset`.
- Added `sizing.s` to compute `PRG_ENTRY_ADDR + bytes-before-CODE`.
- Added `counter.n8` and a compiler fixture that writes two PRGs and a MAP.
- The first output attempt had `out_load`/`out_entry` zero, producing a 7 KB
padded file; initializing them fixed the container and MAP entry.
- The first run used `$CF80` as a frame while BASIC ROM was mapped; switched
to CR `$0E` with `$BF80`, matching the D129 frame ABI.
- Added `parse_file_base`: ordinary `parse_file` clears the requested base so
existing in-place fixtures never see uninitialized BSS, while the counter
installs its final code base explicitly.
- Made repeated definition of a label at the same offset harmless; this keeps
the forward-call fixture stable across BSS layout changes.
- Counter verification passes with 175 code bytes and `total = 15`.

## Completion

- Tests run: statements, calls, member, control, scalar, hello, parser, and
counter suites all PASS after the BSS-layout move.
- Results: counter emits 175 bytes; `COUNTER.PRG` and `COUNTB.PRG` are
byte-identical; MAP contains `ENTRY 1C0D`, `START 1C0D 0042`, and
`CODE 1C4F 00AF`; the generated PRG reports `total = 15`.
- Remaining risks: code buffer remains 255 bytes, `em_entry` remains an
offset, and the MAP still lacks method/class address records. No unresolved
reference path or count-mode mismatch ledger exists yet.

+ 37
- 0
newLang/scripts/build-counter-test.ps1 Dosyayı Görüntüle

@@ -0,0 +1,37 @@
# E04-S07: build the counter compiler fixture and a D81 holding COUNTER.N8.
[CmdletBinding()]
param()

$ErrorActionPreference = "Stop"
$project = Split-Path $PSScriptRoot -Parent
Import-Module (Join-Path $PSScriptRoot "lib\Nova128Test.psm1") -Force
$out = Join-Path $project "build\counter-test"
New-Item -ItemType Directory -Force -Path $out | Out-Null

$link = Invoke-Nova128Link -Name "counter-test" -BuildDir $out `
-Config (Join-Path $project "cfg\compiler.cfg") -Sources @(
(Join-Path $project "src\compiler\mmu.s"),
(Join-Path $project "src\compiler\diskio.s"),
(Join-Path $project "src\compiler\source.s"),
(Join-Path $project "src\compiler\lexer.s"),
(Join-Path $project "src\compiler\names.s"),
(Join-Path $project "src\compiler\diagnostics.s"),
(Join-Path $project "src\compiler\parser.s"),
(Join-Path $project "src\compiler\codegen.s"),
(Join-Path $project "src\compiler\flow.s"),
(Join-Path $project "src\compiler\members.s"),
(Join-Path $project "src\compiler\emit.s"),
(Join-Path $project "src\compiler\sizing.s"),
(Join-Path $project "src\compiler\interfaces.s"),
(Join-Path $project "src\compiler\types.s"),
(Join-Path $project "src\compiler\output.s"),
(Join-Path $project "src\runtime\startup.s"),
(Join-Path $project "tests\compiler\counter-test.s"))
if ((Get-Nova128Label $link.Labels "test_entry") -ne 0x1C0D) { throw "test_entry is not at `$1C0D" }

$pet = Join-Path $out "counter.pet"
[IO.File]::WriteAllBytes($pet, (ConvertTo-Nova128Petscii (Get-Content -Raw -LiteralPath (Join-Path $project "tests\compiler\counter.n8"))))
New-Nova128Disk -Path (Join-Path $out "counter-test.d81") -Files @(
@{ Name = "counter-test"; Path = $link.Program; Type = "prg" },
@{ Name = "counter.n8"; Path = $pet; Type = "seq" })
Write-Host ("built {0}" -f $link.Program)

+ 90
- 0
newLang/scripts/verify-counter-test.ps1 Dosyayı Görüntüle

@@ -0,0 +1,90 @@
# E04-S07: compile COUNTER.N8, check deterministic PRG/MAP output, and run the
# generated PRG in x128.
[CmdletBinding()]
param([int]$Port = 6558, [int]$TimeoutSeconds = 180)

$ErrorActionPreference = "Stop"
$project = Split-Path $PSScriptRoot -Parent
Import-Module (Join-Path $PSScriptRoot "lib\Nova128Test.psm1") -Force
& (Join-Path $PSScriptRoot "build-counter-test.ps1")

$out = Join-Path $project "build\counter-test"
$disk = Join-Path $out "counter-test.d81"
$failures = @()

$session = Start-Nova128X128 -Autostart $disk -Drive8Image $disk -Port $Port `
-Screenshot (Join-Path $out "counter-compile-vicii.png")
try {
$mb = Wait-Nova128Mailbox $session -TimeoutSeconds $TimeoutSeconds
} finally {
Stop-Nova128X128 $session
}
Write-Host ("mailbox: " + (($mb | ForEach-Object { $_.ToString('X2') }) -join ' '))

if ($mb[2] -ne 0x02) { $failures += ("producer $($mb[2]) expected 2 (compiler)") }
if ($mb[3] -ne 0xA5) {
$failures += ("compiler status {0:X2} failed at stage {1}" -f $mb[3], $mb[4])
}
if ($mb[8] -ne 0) { $failures += "diagnostics present: $($mb[8])" }
if ($mb[9] -ne 0) { $failures += "emitter bad: $($mb[9])" }
if ($mb[10] -eq 0) { $failures += "no generated code" }
if ($mb[11] -ne 0) { $failures += "codegen error: $($mb[11])" }
if ($mb[12] -ne 0) { $failures += "flow error: $($mb[12])" }
if ($mb[13] -ne 0) { $failures += "output error: $($mb[13])" }

$c1541 = Get-ViceTool "c1541"
$prg = Join-Path $out "counter.prg"
$prg2 = Join-Path $out "countb.prg"
$map = Join-Path $out "counter.map"
foreach ($path in @($prg, $prg2, $map)) {
if (Test-Path -LiteralPath $path) { Remove-Item -LiteralPath $path -Force }
}
& $c1541 -attach $disk -read "counter.prg" $prg 2>&1 | Out-Null
& $c1541 -attach $disk -read "countb.prg" $prg2 2>&1 | Out-Null
& $c1541 -attach $disk -read "counter.map,s" $map 2>&1 | Out-Null

foreach ($path in @($prg, $prg2, $map)) {
if (-not (Test-Path -LiteralPath $path)) { $failures += "missing $(Split-Path $path -Leaf)" }
}
if (-not (Test-Path -LiteralPath $prg) -or -not (Test-Path -LiteralPath $prg2)) {
$failures += "cannot check deterministic output"
} else {
$a = [IO.File]::ReadAllBytes($prg)
$b = [IO.File]::ReadAllBytes($prg2)
if ($a.Length -ne $b.Length -or (Compare-Object $a $b)) {
$failures += "two compilations are not byte-identical"
}
if ($a.Length -lt 14 -or $a[0] -ne 0x01 -or $a[1] -ne 0x1C) {
$failures += "COUNTER.PRG does not load at `$1C01"
}
}

$codeSize = ('{0:X4}' -f $mb[10])
$codeStart = '1C4F'
if (Test-Path -LiteralPath $map) {
$text = Get-Content -Raw -LiteralPath $map
foreach ($want in @("ENTRY 1C0D", "SECTION STUB 1C01 000C", "SECTION START 1C0D 0042",
"SECTION CODE $codeStart $codeSize")) {
if ($text -notmatch [regex]::Escape($want)) { $failures += "MAP missing '$want'" }
}
}

$result = -1
if (-not $failures) {
$session = Start-Nova128X128 -Autostart $prg -Port ($Port + 1) `
-Screenshot (Join-Path $out "counter-run-vicii.png")
try {
$rmb = Wait-Nova128Mailbox $session -TimeoutSeconds 60
} finally {
Stop-Nova128X128 $session
}
if ($rmb[2] -ne 0x03) { $failures += ("program producer $($rmb[2]) expected 3") }
if ($rmb[3] -ne 0xA5) { $failures += ("program status {0:X2}" -f $rmb[3]) }
$result = $rmb[8]
if ($result -ne 15) { $failures += "program total $result, expected 15" }
}

if ($failures) { throw ("counter verification failed:`n " + ($failures -join "`n ")) }
Write-Host ("compile: {0} code bytes, deterministic PRG, MAP ENTRY 1C0D / CODE {1}" -f $mb[10], $codeStart)
Write-Host "run: generated COUNTER.PRG reported total=$result"
Write-Host "verified counter.n8 compiles from source and runs in x128"

+ 19
- 2
newLang/src/compiler/emit.s Dosyayı Görüntüle

@@ -31,13 +31,13 @@
.import cg_sta_zp, cg_adc_zp, cg_sbc_zp, cg_cmp_zp, cg_adc_imm, cg_clc, cg_sec, cg_jsr_abs, cg_rts
.import gen_text, gen_text_len
.import cg_len
.import fl_init, fl_alloc, fl_label, fl_jmp, fl_jsr, fl_branch, fl_release, fl_finish, fl_err
.import fl_init, fl_set_base, fl_alloc, fl_label, fl_jmp, fl_jsr, fl_branch, fl_release, fl_finish, fl_err
.import mb_init, mb_self_store, mb_self_load, mb_ld_field, mb_st_field
.import tb_get
.import sc_base, sc_val, sc_len

.export em_on, em_ctx, em_bad, em_last_kind
.export em_reset, em_begin_func, em_begin_params, em_local, em_param
.export em_reset, em_set_base, em_base_set, em_begin_func, em_begin_params, em_local, em_param
.export em_expr_name, em_expr_const, em_push_left, em_bin
.export em_stmt_begin, em_stmt_end, em_expr_off, em_begin_print
.export em_target, em_store_target, em_print, em_end_func
@@ -62,6 +62,12 @@
em_reset:
lda #$00
jsr fl_init
lda em_base_set
beq @base_done
lda em_code_base
ldx em_code_base+1
jsr fl_set_base
@base_done:
jsr mb_init
lda #$00
sta em_bad
@@ -86,6 +92,15 @@ em_reset:
sta em_par_next
rts

; A/X = final code-section base used when pass 3 patches absolute targets.
; The caller sets this before parse_file; em_reset applies it to flow.s.
em_set_base:
sta em_code_base
stx em_code_base+1
lda #$01
sta em_base_set
rts

; A body starts: fresh local slots. Parameter offsets were assigned while the
; signature was parsed and are kept.
em_begin_func:
@@ -1125,3 +1140,5 @@ em_off: .res 256
em_sz: .res 256
em_meth_label: .res 256
em_entry: .res 1
em_code_base: .res 2
em_base_set: .res 1

+ 23
- 4
newLang/src/compiler/flow.s Dosyayı Görüntüle

@@ -63,6 +63,7 @@
.endmacro

.export fl_init, fl_alloc, fl_label, fl_jmp, fl_jsr, fl_branch, fl_finish, fl_get_label
.export fl_set_base
.export fl_release
.export fl_branch_true, fl_branch_false
.export fl_call, fl_call_byte, fl_call_word
@@ -75,12 +76,17 @@
fl_init:
sta fl_mode
jsr cg_init
lda #<cg_code
sta fl_code_base
lda #>cg_code
sta fl_code_base+1
lda #$00
sta fl_fix_count
sta fl_err
ldx #FL_LABELS - 1
@clr: sta fl_used,x
sta fl_defined,x
sta fl_labels,x
dex
bpl @clr
lda fl_mode
@@ -88,6 +94,13 @@ fl_init:
clc
rts

; A/X = low/high of the final code-section base. Fixups resolve absolute
; jumps and calls against this address instead of the compiler's cg_code.
fl_set_base:
sta fl_code_base
stx fl_code_base+1
rts

; Reserve a label id. A label stays reserved until fl_release, so a block's
; forward targets can be defined in any order.
fl_alloc:
@@ -122,10 +135,15 @@ fl_label:
sta fl_labels,x
clc
rts
@dup: lda #FL_ERR_STATE
@dup: lda fl_labels,x
cmp cg_len
beq @same ; repeated definition at the same offset
lda #FL_ERR_STATE
sta fl_err
sec
rts
@same: clc
rts
@bad: lda #FL_ERR_ID
sta fl_err
sec
@@ -390,10 +408,10 @@ fl_resolve:
beq @bad
lda fl_labels,x
clc
adc #<cg_code
adc fl_code_base
sta fl_tlo
lda #>cg_code
adc #$00
lda #$00
adc fl_code_base+1
sta fl_thi
clc
rts
@@ -744,3 +762,4 @@ fl_fix: .res FL_FIX_MAX * FL_FIX_SIZE
fl_enter: .res 2
fl_leave: .res 2
fl_discard: .res 2
fl_code_base: .res 2

+ 11
- 2
newLang/src/compiler/parser.s Dosyayı Görüntüle

@@ -56,7 +56,7 @@
.import ty_assign, ty_binary, ty_unary
.import ty_arg_sc1, ty_arg_sc2, ty_arg_name1, ty_arg_num2
.import assign_slots, check_interfaces
.import em_reset, em_on, em_ctx, em_bad, em_last_kind
.import em_reset, em_set_base, em_base_set, em_on, em_ctx, em_bad, em_last_kind
.import em_begin_func, em_begin_params, em_local, em_param
.import em_stmt_begin, em_stmt_end, em_expr_off, em_begin_print
.import em_expr_name, em_expr_const, em_push_left, em_bin
@@ -66,7 +66,7 @@
.import em_for_var, em_for_save_end, em_for_save_step, em_for_default_step, em_for_setup
.import em_return, em_method_begin, em_program_begin

.export parse_file, tb_get, tb_set, mem_count, par_count
.export parse_file, parse_file_base, tb_get, tb_set, mem_count, par_count
.export gen_text, gen_text_len, gen_print_count, gen_bad
.export ex_trace_on, ex_min_sp
.exportzp ex_tp
@@ -95,6 +95,15 @@ ex_tp: .res 2 ; test trace pointer (ex_trace_on)
.segment "CODE"

parse_file:
lda #$00
sta em_base_set
jmp parse_start

; A/X = final code base for pass-3 absolute fixups. Clears any pending base
; before installing this one, so BSS garbage cannot affect ordinary fixtures.
parse_file_base:
jsr em_set_base
parse_start:
tsx
stx parse_sp
jsr tt_clear ; bank-1 BSS is not zeroed at load


+ 31
- 0
newLang/src/compiler/sizing.s Dosyayı Görüntüle

@@ -0,0 +1,31 @@
; sizing.s - final code-section address planning (E04-S07).
;
; Pass 3 must resolve absolute jumps and calls against the loaded program,
; not the compiler's cg_code buffer. The caller supplies the bytes preceding
; the generated code in the PRG (the BASIC stub plus the generated startup)
; and this module computes the final code base and installs it in emit.s.

.setcpu "6502"
.include "../common/formats.inc"

.import em_set_base

.export sz_plan_code, sz_code_base

.segment "CODE"

; A = bytes before the code section in the generated image.
; Sets sz_code_base = PRG_ENTRY_ADDR + A and installs it for pass 3.
sz_plan_code:
clc
adc #<PRG_ENTRY_ADDR
sta sz_code_base
lda #$00
adc #>PRG_ENTRY_ADDR
sta sz_code_base+1
lda sz_code_base
ldx sz_code_base+1
jmp em_set_base

.segment "BSS"
sz_code_base: .res 2

+ 305
- 0
newLang/tests/compiler/counter-test.s Dosyayı Görüntüle

@@ -0,0 +1,305 @@
; E04-S07: compile COUNTER.N8, assign final code addresses, and write two
; deterministic PRGs. The generated code runs from the PRG in a second x128
; session; the fixture-owned wrapper places FRAME_FP and reports the program's
; `total` local through the mailbox.

.setcpu "6502"
.include "../../src/common/c128.inc"
.include "../../src/common/mailbox.inc"
.include "../../src/common/formats.inc"
.include "../../src/common/frames.inc"

.import mmu_init, mmu_bank0, mmu_shutdown
.import src_load, src_dos_code, src_kernal_err
.import names_init
.import diag_init, diag_finish, diag_screen
.import parse_file_base
.import em_on, em_bad
.import cg_code, cg_len, cg_err
.import fl_err
.import sz_plan_code, sz_code_base
.import out_begin, out_section, out_end_section, out_byte, out_bytes
.import out_finish, out_abort, out_load, out_entry, out_start, out_err
.import gen_stub

FRAME_BASE = $BF80 ; software frame area under CR=$0E
WRAPPER_LEN = 66
CODE_ADDR = PRG_ENTRY_ADDR + WRAPPER_LEN

.macro ON_CARRY target
bcc :+
jmp target
:
.endmacro

.macro ON_NONZERO target
beq :+
jmp target
:
.endmacro

.segment "LOADADDR"
.word $1C01

.segment "EXEHDR"
.word basic_end
.word 10
.byte $9E
.byte "7181"
.byte $00
basic_end:
.word $0000

.segment "MAILBOX"
.res MB_SIZE

.segment "STARTUP"
.export test_entry
test_entry:
php
sei
ldx #MB_SIZE - 1
lda #$00
@clr: sta MB_BASE,x
dex
bpl @clr
lda #MB_MAGIC0_VALUE
sta MB_MAGIC0
lda #MB_MAGIC1_VALUE
sta MB_MAGIC1
lda #MB_PROD_COMPILER
sta MB_PRODUCER
lda #MB_ST_RUNNING
sta MB_STATUS

jsr mmu_init
jsr mmu_bank0
lda #<name_src
ldx #>name_src
ldy #name_src_len
jsr src_load
cmp #$00
ON_NONZERO fail_load ; src_load returns A=0 on success

jsr names_init
lda #<name_src
ldx #>name_src
ldy #name_src_len
jsr diag_init
lda #$00
sta diag_screen
lda #$01
sta em_on
lda #<PRG_LOAD_ADDR
sta out_load
lda #>PRG_LOAD_ADDR
sta out_load+1
lda #<PRG_ENTRY_ADDR
sta out_entry
lda #>PRG_ENTRY_ADDR
sta out_entry+1
lda #WRAPPER_LEN
jsr sz_plan_code ; final code base for absolute fixups

lda sz_code_base
ldx sz_code_base+1
jsr parse_file_base
jsr diag_finish
sta diag_count
lda diag_count
sta MB_DATA+0
lda em_bad
sta MB_DATA+1
lda cg_len
sta MB_DATA+2
lda cg_err
sta MB_DATA+3
lda fl_err
sta MB_DATA+4
lda sz_code_base
sta MB_DATA+6
lda sz_code_base+1
sta MB_DATA+7

lda diag_count
ON_NONZERO fail_diag
lda em_bad
ON_NONZERO fail_emit
lda cg_err
ON_NONZERO fail_emit
lda fl_err
ON_NONZERO fail_emit

lda #<name_counter
sta out_name_ptr
lda #>name_counter
sta out_name_ptr+1
lda #name_counter_len
sta out_name_len
jsr emit_output
bcs fail_out

lda #<name_countb
sta out_name_ptr
lda #>name_countb
sta out_name_ptr+1
lda #name_countb_len
sta out_name_len
jsr emit_output
bcs fail_out

lda #MB_ST_PASS
sta MB_STATUS
jmp done

fail_diag:
lda #2
jmp fail_common
fail_emit:
lda #3
jmp fail_common
fail_out:
lda emit_err
sta MB_DATA+5
lda #4
jmp fail_common
fail_load:
sta MB_DATA+5
lda src_dos_code
sta MB_DATA+6
lda src_kernal_err
sta MB_DATA+7
lda #1
fail_common:
sta MB_CODE
lda #$00
sta MB_CODE+1
lda #MB_ST_FAIL
sta MB_STATUS
done: jsr mmu_shutdown
plp
rts

; Write one output pair with the same generated bytes. A/X = base name,
; out_name_len = length. C=1 on any output failure.
emit_output:
lda out_name_ptr
ldx out_name_ptr+1
ldy out_name_len
jsr out_begin
ON_CARRY out_fail

lda #<PRG_LOAD_ADDR
sta out_start
lda #>PRG_LOAD_ADDR
sta out_start+1
lda #<sec_stub
ldx #>sec_stub
ldy #4
jsr out_section
ON_CARRY out_fail
lda #<gen_stub
ldx #>gen_stub
ldy #PRG_STUB_SIZE
jsr out_bytes
ON_CARRY out_fail
jsr out_end_section
ON_CARRY out_fail

lda #<PRG_ENTRY_ADDR
sta out_start
lda #>PRG_ENTRY_ADDR
sta out_start+1
lda #<sec_start
ldx #>sec_start
ldy #5
jsr out_section
ON_CARRY out_fail
lda #<wrapper
ldx #>wrapper
ldy #WRAPPER_LEN
jsr out_bytes
ON_CARRY out_fail
jsr out_end_section
ON_CARRY out_fail

lda sz_code_base
sta out_start
lda sz_code_base+1
sta out_start+1
lda #<sec_code
ldx #>sec_code
ldy #4
jsr out_section
ON_CARRY out_fail
lda #<cg_code
ldx #>cg_code
ldy cg_len
jsr out_bytes
ON_CARRY out_fail
jsr out_end_section
ON_CARRY out_fail
jsr out_finish
ON_CARRY out_fail
clc
rts
out_fail: lda out_err
sta emit_err
jsr out_abort
sec
rts

; The PRG's fixed entry, at $1C0D. It clears the mailbox, installs a frame
; base, calls the generated program body at CODE_ADDR, and reports `total`
; (the second local, FP-2) as MB_DATA.
wrapper:
lda $FF00 ; save CR, select RAM through $BFFF
pha
lda #$0E
sta $FF00
ldx #MB_SIZE - 1
lda #$00
@clr: sta MB_BASE,x
dex
bpl @clr
lda #MB_MAGIC0_VALUE
sta MB_MAGIC0
lda #MB_MAGIC1_VALUE
sta MB_MAGIC1
lda #MB_PROD_PROGRAM
sta MB_PRODUCER
lda #MB_ST_RUNNING
sta MB_STATUS
lda #<FRAME_BASE
sta FRAME_FP
lda #>FRAME_BASE
sta FRAME_FP+1
jsr CODE_ADDR
lda FRAME_BASE - 3
sta MB_DATA
pla
sta $FF00 ; restore the BASIC entry mapping
lda #MB_ST_PASS
sta MB_STATUS
rts
.assert * - wrapper = WRAPPER_LEN, error, "counter wrapper length changed"
.assert CODE_ADDR < $4000, error, "generated code must stay below BASIC ROM"

.segment "ZEROPAGE"
out_name_ptr: .res 2

.segment "RODATA"
name_src: .byte "COUNTER.N8"
name_src_len = * - name_src
name_counter: .byte "COUNTER"
name_counter_len = * - name_counter
name_countb: .byte "COUNTB"
name_countb_len = * - name_countb
sec_stub: .byte "STUB"
sec_start: .byte "START"
sec_code: .byte "CODE"

.segment "BSS"
diag_count: .res 1
out_name_len: .res 1
emit_err: .res 1

+ 8
- 0
newLang/tests/compiler/counter.n8 Dosyayı Görüntüle

@@ -0,0 +1,8 @@
program counter
var i as byte
var total as byte
total = 0
for i = 1 to 5
total = total + i
end for
end program

Yükleniyor…
İptal
Kaydet

Powered by TurnKey Linux.