Explorar el Código

Add the generated PRG and MAP writer (E04-S01).

Emit a BASIC-stub PRG with ordered sections and a MAP entry/section table,
refuse existing output names, and scratch partial files on failure. Include
a runnable startup that reports through the test mailbox.

Generated with Codebuff 🤖
Co-Authored-By: Codebuff <noreply@codebuff.com>
main
Daniel Covington hace 4 días
padre
commit
e34bbc79d0
Se han modificado 12 ficheros con 1527 adiciones y 5 borrados
  1. +35
    -3
      newLang/docs/architecture.md
  2. +1
    -0
      newLang/docs/decisions.md
  3. +1
    -0
      newLang/docs/diagnostics.md
  4. +18
    -2
      newLang/docs/memory-map.md
  5. +117
    -0
      newLang/planning/claims/E04-S01.md
  6. +29
    -0
      newLang/scripts/build-output-test.ps1
  7. +124
    -0
      newLang/scripts/verify-output-test.ps1
  8. +1
    -0
      newLang/src/common/diagcat.inc
  9. +27
    -0
      newLang/src/common/formats.inc
  10. +855
    -0
      newLang/src/compiler/output.s
  11. +71
    -0
      newLang/src/runtime/startup.s
  12. +248
    -0
      newLang/tests/codegen/output-test.s

+ 35
- 3
newLang/docs/architecture.md Ver fichero

@@ -8,8 +8,9 @@ version: 0.1-draft
# Nova128 Compiler and Runtime Architecture

Status: approved version 1 baseline. The compiler memory map is frozen by
E01-S05 (section 4.4, `docs/memory-map.md`). Generated-program addresses are
still deliberately absent until the E04 stories validate them.
E01-S05 (section 4.4, `docs/memory-map.md`). E04-S01 defines the generated
PRG/MAP container and low-memory startup; the higher generated-program map
(banking, heap, software stack) remains open until E04-S02.

## 1. System Boundary

@@ -47,7 +48,10 @@ C64 mode or the Z80.
| `NAME.MAP` | Symbols, addresses, class layouts, interface slots, and section sizes |

Output replacement must be explicit. The compiler must not silently overwrite
an existing PRG after a failed compilation.
an existing PRG after a failed compilation. E04-S01 therefore refuses an
existing `NAME.PRG` or `NAME.MAP` with E202; the user erases the old output
before rebuilding. Files created by the current run are scratched on abort or
failure.

## 3. Repository Module Plan

@@ -162,6 +166,26 @@ speed bit is cleared before returning to BASIC, and any uncertain mode falls
back to 1 MHz. Direct VDC screen memory is display-only and is not compiler
semantic storage. See `docs/platform-display-clock.md` for evidence.

### 2.3 Generated program container (E04-S01)

The generated PRG begins with load address `$1C01`, the 12-byte BASIC 7 stub
`10 SYS 7181`, and machine entry `$1C0D`. Sections are emitted in ascending
address order; a gap before a section is filled with zero bytes. The entry
point and each section's start/size are recorded in `NAME.MAP` as
CR-terminated text:

```text
NOVA128 MAP 1
ENTRY 1C0D
SECTION STUB 1C01 000C
SECTION START 1C0D 004A
```

`src/runtime/startup.s` supplies the fixed stub and startup bytes. The startup
initializes the test mailbox, reports producer `$03` and PASS, and returns to
BASIC. The test fixture keeps its sections below `$4000`, where BASIC's mapping
still exposes RAM; banking, heap, and software-stack placement are E04-S02.

## 5. Compiler Passes

The compiler reparses a buffered/streamed source rather than retaining a large
@@ -277,6 +301,14 @@ memory.

## 7. Generated Program ABI

### 7.0 Container and entry (E04-S01)

- Load address `$1C01`; BASIC 7 stub `10 SYS 7181`; entry `$1C0D`.
- Sections are contiguous and ascending; gaps are zero-filled.
- `NAME.MAP` records `ENTRY` and every `SECTION name start size` in hex.
- The startup writes the mailbox with producer `$03`, then PASS, and returns
to BASIC. It does not yet install a runtime mapping or frames.

### 7.1 Scalar representation

- Multi-byte values are little-endian.


+ 1
- 0
newLang/docs/decisions.md Ver fichero

@@ -54,6 +54,7 @@ documented default. `Proposed` means a later change still needs approval.
| D125 | An array index may be an integer or a value assignable to word; a constant index is also checked against the declared length. | Spec 3.3 gives arrays but not the index type. Byte indexes widen; an out-of-range constant is E089 at compile time. Implemented by `check_index` in `parser.s`. |
| D126 | Arrays are fields or locals only: never parameters, results, or whole values. A bare array is E088, and a parameter or result declaring one is E094. | Keeps array storage and copying out of the ABI (D108); indexing is the only way to read or write one. Implemented by `no_array_sig` and `ty_assign`. |
| D127 | `return` with a value in a program is E151. A method that declares a result must end every path in `return`; an `if` satisfies this only when both branches do, a `while` never does, and a missing return is reported at `end` as E150. | Spec 7 permits `return` but does not say what a program return means; a program has no caller or result. Deterministic and observable, and lets E04 emit a single exit path. Implemented by `close_block` and `set_ret`. |
| D128 | The generated PRG container loads at `$1C01` with the same 12-byte BASIC 7 stub as the compiler and enters at `$1C0D`. Sections follow in ascending address order; gaps are zero-filled. `NAME.MAP` is CR-terminated text: `NOVA128 MAP 1`, `ENTRY hhhh`, then `SECTION name start size` in hex. Existing `NAME.PRG`/`NAME.MAP` is refused with E202 instead of overwritten; outputs created by the run are scratched on abort or failure. The higher generated-program map (banking, heap, software stack) remains E04-S02. | Gives E04-S01 a deterministic, testable container while preserving a previously successful build. Refusing an existing output is explicit; the user must erase it before recompiling. Implemented by `output.s`, `startup.s`, and `formats.inc`. |

## Deferred



+ 1
- 0
newLang/docs/diagnostics.md Ver fichero

@@ -117,6 +117,7 @@ After 20 errors the next one is replaced by E221 and compilation stops.
|---|---|---|
| E200 | `DSK_SOURCE_UNREADABLE` | CANNOT READ SOURCE; DOS % |
| E201 | `DSK_ERR_FILE_UNWRITABLE` | CANNOT WRITE ERROR FILE; DOS % |
| E202 | `DSK_OUTPUT_EXISTS` | OUTPUT FILE ALREADY EXISTS |

## Resource limits (E220-E239)



+ 18
- 2
newLang/docs/memory-map.md Ver fichero

@@ -5,8 +5,24 @@ Status: frozen for the compiler (D117, source cache D118); verified in VICE
x128 3.10
Authoritative source: `cfg/compiler.cfg`. This document explains it.

The generated-program memory map is **not** frozen here. It belongs to E04-S01
and E04-S02, which must reuse the test mailbox below.
E04-S01 defines the generated PRG/MAP container below; the higher
generated-program map (banking, heap, software stack) still belongs to E04-S02.

## Generated Program Container (E04-S01)

| Element | Value |
|---|---|
| Load address | `$1C01` |
| BASIC 7 stub | 12 bytes, `10 SYS 7181` |
| Entry | `$1C0D` |
| Sections | ascending addresses, zero-filled gaps |
| MAP | `NOVA128 MAP 1`, `ENTRY hhhh`, `SECTION name start size` |
| Startup | clears mailbox, producer `$03`, status PASS, then `RTS` |

The fixed startup is copied from `src/runtime/startup.s`; `output.s` refuses
existing `NAME.PRG`/`NAME.MAP` and scratches outputs it created on failure.
Test sections stay below `$4000` until E04-S02 freezes the runtime mapping.
The generated program still uses the test mailbox below.

## Mappings



+ 117
- 0
newLang/planning/claims/E04-S01.md Ver fichero

@@ -0,0 +1,117 @@
---
story: E04-S01
owner: e04_output
status: done
started: 2026-11-09
depends-on: [E01-S05, E02-S04]
---

# E04-S01 Claim

## Intended change

Add the generated-program container and writer. `src/compiler/output.s` accepts
a source base name, writes a PRG with a valid load-address header and ordered
sections, records each section's start/size plus the entry point in a MAP file,
and emits the fixed BASIC 7 stub and startup path from
`src/runtime/startup.s`. The generated program initializes the test mailbox,
reports pass, and returns cleanly to BASIC.

An existing `NAME.PRG` or `NAME.MAP` is refused with the new disk diagnostic
E202 instead of being overwritten. Outputs created by the current run are
scratched if emission fails or the caller aborts, so no partial final file is
left behind.

## File area

- `src/compiler/output.s` (new), `src/runtime/startup.s` (new)
- `src/common/formats.inc` (new), `src/common/diagcat.inc`, `docs/diagnostics.md`
- `tests/codegen/output-test.s`
- `scripts/build-output-test.ps1`, `scripts/verify-output-test.ps1`
- `docs/decisions.md`, `docs/architecture.md`, `docs/memory-map.md`
- `planning/claims/E04-S01.md`
- generated files under `build/`

## Acceptance evidence

- [x] Writes a valid load-address header and deterministic sections: the
generated `generate.prg` is 133 bytes, starts with `01 1C`, contains the
verified 12-byte `10 SYS 7181` stub at `$1C01`, 74 startup bytes at `$1C0D`,
zero padding through `$1C7F`, and `DE AD BE EF` at `$1C80`.
- [x] Emits a runnable startup path and clean termination: the extracted PRG
autostarts in x128, writes producer `$03`, status `$A5`, code 0, empty data,
and returns to BASIC.
- [x] Refuses/handles output-name collision according to the approved policy:
the pre-existing `collide.prg`/`collide.map` fixtures cause
`OUT_ERR_EXISTS`; both files remain byte-for-byte unchanged because nothing
is opened before the check.
- [x] Removes or marks partial output after any failure: the `order.prg`
section-order failure and the `abort.prg` partial-section run both call
`out_abort`; neither PRG nor MAP exists afterwards.
- [x] MAP records section start, size, and entry point: `generate.map` is the
exact CR-terminated sequence `NOVA128 MAP 1`, `ENTRY 1C0D`,
`SECTION STUB 1C01 000C`, `SECTION START 1C0D 004A`,
`SECTION PAD 1C80 0004`.

## Coordination notes

- E01-S05 and E02-S04 are `done`; no other claim overlapped this area.
- D128 records the provisional container and refusal policy. E04-S02 still
owns the higher generated-program map (banking, heap, software stack);
this story's fixture keeps sections below `$4000` where BASIC's mapping
exposes RAM.
- `output.s` returns `OUT_ERR_EXISTS`; E202 is in the stable catalog for the
compiler shell (a later story) to report. No shell integration was invented
here.
- Production and abort paths use the existing `diskio.s` wrappers; no module
except `mmu.s` touches the MMU.

## Work log

- Built the writer around a compact section-record table (name pointer, length,
start, size), a load-address header, and a MAP written after the PRG closes.
- First build failed because `out_rec_ptr` was in BSS; 6502 `(ptr),y` requires
zero page. Moved it to `ZEROPAGE`.
- Long branches in `out_begin` and `out_write_map` needed short-branch/`jmp`
trampolines.
- First x128 result was `OUT_ERR_ORDER`: the load-address header had advanced
`out_current` to `$1C03`, so the `$1C01` STUB section looked backward. The
writer now rewinds to `out_load` after the header.
- The next fixture exposed every PRG byte as `01`: `out_byte` loaded the section
flag into A before calling `out_put`. It now saves the byte first.
- Collision fixtures originally used PETSCII uppercase names while the writer
preserved the source's PETSCII lowercase names, so the writer legitimately
did not see them. Fixtures and host reads now use the same lowercase names.
- The abort fixture initially emitted a byte after `out_startup` had ended its
section, producing `OUT_ERR_STATE`; it now opens an explicit `X` section and
writes `$AA` before aborting.
- A stale parser-test x128 held port 6530 and caused empty/dropped monitor
replies. Verification identifies test-image processes and stops only those
before running.

## Completion

- Tests run:
- `scripts/build-output-test.ps1`
- `scripts/verify-output-test.ps1 -Port 6530 -RunPort 6531`
- `scripts/build-diag-test.ps1 -UpdateDocs`
- `scripts/verify-diag-test.ps1 -Port 6530`
- `scripts/verify-parser-test.ps1 -Port 6530`
- `git diff --check`
- Results: writer fixture PASS with results finish=0, collision=1, order=2,
abort=0; PRG and MAP bytes match; generated PRG autostarts with producer 3
and PASS; collision fixtures unchanged; abort/order outputs absent.
Diagnostics: catalog 71 entries, docs in sync, 19 screen rows and ERR files
exact. Parser: all 41 fixtures pass, lowest expression SP `$A7`.
- Files changed: `src/compiler/output.s`, `src/runtime/startup.s`,
`src/common/formats.inc`, `src/common/diagcat.inc`, `docs/diagnostics.md`,
`tests/codegen/output-test.s`, `scripts/build-output-test.ps1`,
`scripts/verify-output-test.ps1`, `docs/decisions.md`,
`docs/architecture.md`, `docs/memory-map.md`, this claim.
- Remaining risks: an existing output must be erased before recompiling
(refusal policy, D128); a power loss during direct PRG writing can leave a
partial file because there is no atomic rename; at most 8 sections and
12-character section names; the MAP has no symbols/class/interface entries
yet; the startup is only the mailbox/termination path and does not install
a runtime mapping; sections above `$3FFF` are E04-S02 work; `out_load` is
caller-controlled rather than validated against the fixed container.

+ 29
- 0
newLang/scripts/build-output-test.ps1 Ver fichero

@@ -0,0 +1,29 @@
# E04-S01: build the PRG/MAP writer test and a disk with collision fixtures.
[CmdletBinding()]
param()

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

$out = Join-Path $project "build\output-test"
$link = Invoke-Nova128Link -Name "output-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\output.s"),
(Join-Path $project "src\runtime\startup.s"),
(Join-Path $project "tests\codegen\output-test.s"))

$oldPrg = Join-Path $out "old-collide.prg"
$oldMap = Join-Path $out "old-collide.map"
[IO.File]::WriteAllBytes($oldPrg, [byte[]](0x4B, 0x45, 0x45, 0x50)) # "KEEP"
[IO.File]::WriteAllBytes($oldMap, [byte[]](0x4B, 0x45, 0x45, 0x50, 0x4D, 0x41, 0x50)) # "KEEPMAP"

New-Nova128Disk -Path (Join-Path $out "output-test.d81") -Files @(
@{ Name = "OUTPUTTEST"; Path = $link.Program; Type = "prg" },
@{ Name = "collide.prg"; Path = $oldPrg; Type = "prg" },
@{ Name = "collide.map"; Path = $oldMap; Type = "seq" })

Write-Host ("built {0}" -f $link.Program)
Write-Host ("disk {0}" -f (Join-Path $out "output-test.d81"))

+ 124
- 0
newLang/scripts/verify-output-test.ps1 Ver fichero

@@ -0,0 +1,124 @@
# E04-S01: run the PRG/MAP writer test in x128, inspect the disk artifacts,
# and autostart the generated PRG to prove the startup path and termination.
[CmdletBinding()]
param(
[int]$Port = 6530,
[int]$RunPort = 6531,
[int]$TimeoutSeconds = 90
)

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

$out = Join-Path $project "build\output-test"
$disk = Join-Path $out "output-test.d81"
$c1541 = Get-ViceTool "c1541"

$session = Start-Nova128X128 -Autostart $disk -Drive8Image $disk -Port $Port `
-Screenshot (Join-Path $out "output-test-vicii.png")
try {
$mb = Wait-Nova128Mailbox $session -TimeoutSeconds $TimeoutSeconds
} finally {
Stop-Nova128X128 $session
}
Write-Host ('mailbox: ' + (($mb | ForEach-Object { $_.ToString('X2') }) -join ' '))
Write-Host ('results: finish={0} collision={1} order={2} abort={3}' -f $mb[8], $mb[9], $mb[10], $mb[11])

$failures = @()
if ($mb[2] -ne 0x01) { $failures += "producer $($mb[2]) is not the platform test" }
if ($mb[3] -ne 0xA5) { $failures += "fixture status is not PASS" }
$want = @(0, 1, 2, 0)
for ($i = 0; $i -lt $want.Count; $i++) {
if ($mb[8 + $i] -ne $want[$i]) {
$failures += "result $i is $($mb[8 + $i]), expected $($want[$i])"
}
}
if ($mb[4] -ne 0 -or $mb[5] -ne 0) {
$failures += "code is $($mb[4] + 256 * $mb[5])"
}

function Read-DiskFile([string]$Name, [string]$Dest, [bool]$Seq) {
if (Test-Path -LiteralPath $Dest) { Remove-Item -LiteralPath $Dest -Force }
$spec = if ($Seq) { "$Name,s" } else { $Name }
& $c1541 -attach $disk -read $spec $Dest 2>&1 | Out-Null
if (-not (Test-Path -LiteralPath $Dest)) { throw "could not read $Name from the test disk" }
}

$prgPath = Join-Path $out "generate.prg"
$mapPath = Join-Path $out "generate.map"
$collidePrg = Join-Path $out "collide-check.prg"
$collideMap = Join-Path $out "collide-check.map"
Read-DiskFile "generate.prg" $prgPath $false
Read-DiskFile "generate.map" $mapPath $true
Read-DiskFile "collide.prg" $collidePrg $false
Read-DiskFile "collide.map" $collideMap $true

$bytes = [IO.File]::ReadAllBytes($prgPath)
if ($bytes.Length -ne 133) { $failures += "GENERATE.PRG is $($bytes.Length) bytes, expected 133" }
if ($bytes[0] -ne 0x01 -or $bytes[1] -ne 0x1C) {
$failures += ("load address is ${0:X2}{1:X2}, expected 011C" -f $bytes[1], $bytes[0])
}
$stub = [byte[]](0x0B, 0x1C, 0x0A, 0x00, 0x9E, 0x37, 0x31, 0x38, 0x31, 0x00, 0x00, 0x00)
for ($i = 0; $i -lt $stub.Length; $i++) {
if ($bytes[2 + $i] -ne $stub[$i]) {
$failures += ("BASIC stub byte {0} is {1:X2}, expected {2:X2}" -f $i, $bytes[2 + $i], $stub[$i])
}
}
$padOffset = 2 + (0x1C80 - 0x1C01)
$pad = @(0xDE, 0xAD, 0xBE, 0xEF)
for ($i = 0; $i -lt $pad.Count; $i++) {
if ($bytes[$padOffset + $i] -ne $pad[$i]) {
$failures += ("PAD byte {0} is {1:X2}, expected {2:X2}" -f $i, $bytes[$padOffset + $i], $pad[$i])
}
}
for ($i = 2 + 12 + 80; $i -lt $padOffset; $i++) {
if ($bytes[$i] -ne 0) { $failures += "gap byte at $i is not zero"; break }
}

$mapLines = ([IO.File]::ReadAllText($mapPath)).TrimEnd("`r", "`n") -split "`r`n|`r|`n"
$expectedMap = @(
"NOVA128 MAP 1",
"ENTRY 1C0D",
"SECTION STUB 1C01 000C",
"SECTION START 1C0D 004A",
"SECTION PAD 1C80 0004")
if (($mapLines -join "`n") -ne ($expectedMap -join "`n")) {
$failures += ("GENERATE.MAP differs:`n got: " + ($mapLines -join "`n ") +
"`n want: " + ($expectedMap -join "`n "))
}

$keep = [Text.Encoding]::ASCII.GetString([IO.File]::ReadAllBytes($collidePrg))
if ($keep -cne "KEEP") { $failures += "COLLIDE.PRG changed to '$keep'" }
$keepMap = [Text.Encoding]::ASCII.GetString([IO.File]::ReadAllBytes($collideMap)).TrimEnd("`r", "`n")
if ($keepMap -cne "KEEPMAP") { $failures += "COLLIDE.MAP changed to '$keepMap'" }

$listing = (& $c1541 -attach $disk -list 2>&1) -join "`n"
foreach ($absent in @("abort.prg", "abort.map", "order.prg", "order.map")) {
if ($listing -match ('"' + [regex]::Escape($absent) + '"')) {
$failures += "$absent exists after abort/failure"
}
}

if ($failures) { throw ("output verification failed:`n " + ($failures -join "`n ")) }

# ---------------------------------------------------------- generated run
$run = Start-Nova128X128 -Autostart $prgPath -Port $RunPort
try {
$gmb = Wait-Nova128Mailbox $run -TimeoutSeconds $TimeoutSeconds
} finally {
Stop-Nova128X128 $run
}
if ($gmb[2] -ne 0x03) { throw "generated producer is $($gmb[2]), expected 3" }
if ($gmb[3] -ne 0xA5) { throw "generated program did not report PASS" }
if ($gmb[4] -ne 0 -or $gmb[5] -ne 0) { throw "generated code is $($gmb[4] + 256 * $gmb[5])" }
for ($i = 8; $i -lt 16; $i++) {
if ($gmb[$i] -ne 0) { throw "generated data byte $i is not zero" }
}

Write-Host "verified PRG header, BASIC stub, PAD section, and MAP:"
$expectedMap | ForEach-Object { Write-Host " |$_" }
Write-Host "COLLIDE.PRG/MAP unchanged; ABORT/ORDER left no output"
Write-Host "generated PRG autostarted: producer 3, PASS, code 0"
Write-Host "PRG bytes: $($bytes.Length); screenshots not requested"

+ 1
- 0
newLang/src/common/diagcat.inc Ver fichero

@@ -94,6 +94,7 @@
; ------------------------------------------------------------------- disk
DIAGNOSTIC DSK_SOURCE_UNREADABLE, 200, "CANNOT READ SOURCE; DOS %"
DIAGNOSTIC DSK_ERR_FILE_UNWRITABLE, 201, "CANNOT WRITE ERROR FILE; DOS %"
DIAGNOSTIC DSK_OUTPUT_EXISTS, 202, "OUTPUT FILE ALREADY EXISTS"

; ------------------------------------------------------- resource limits
DIAGNOSTIC RES_TOO_MANY_NAMES, 220, "MORE THAN 255 NAMES"


+ 27
- 0
newLang/src/common/formats.inc Ver fichero

@@ -0,0 +1,27 @@
; formats.inc - generated-program container constants (E04-S01).
;
; The container is deliberately small: a C128 BASIC 7 stub at $1C01, the
; machine entry at $1C0D, and contiguous sections loaded in ascending order.
; The higher generated-program map (banking, heap, software stack) is E04-S02.

.ifndef FORMATS_INC
FORMATS_INC = 1

PRG_LOAD_ADDR = $1C01
PRG_ENTRY_ADDR = $1C0D
PRG_STUB_SIZE = 12

OUT_MAX_SECTIONS = 8
OUT_SECTION_NAME_MAX = 12
OUT_RECORD_SIZE = 7 ; name ptr(2), name len(1), start(2), size(2)

; out_begin/out_finish/out_abort result codes (out_err).
OUT_OK = 0
OUT_ERR_EXISTS = 1 ; NAME.PRG or NAME.MAP already exists
OUT_ERR_ORDER = 2 ; section starts below the current address
OUT_ERR_SECTIONS = 3 ; more than OUT_MAX_SECTIONS
OUT_ERR_NAME = 4 ; base or section name too long
OUT_ERR_DISK = 5 ; KERNAL or DOS failure
OUT_ERR_STATE = 6 ; byte emitted outside a section

.endif

+ 855
- 0
newLang/src/compiler/output.s Ver fichero

@@ -0,0 +1,855 @@
; output.s - generated PRG and MAP writer (E04-S01).
;
; Container (D128):
; PRG starts with the load address PRG_LOAD_ADDR, then ordered sections.
; A gap before a section is filled with zero bytes. The MAP is PETSCII text:
;
; NOVA128 MAP 1
; ENTRY hhhh
; SECTION name start size
;
; API (all calls in the bank-0 compiler state):
; out_begin A/X = source name (bank 0), Y = length. Sets out_load and
; out_entry first. Refuses an existing NAME.PRG or NAME.MAP
; with OUT_ERR_EXISTS; no existing output is overwritten.
; out_startup Emits the BASIC 7 stub at $1C01 and the startup code at
; $1C0D from src/runtime/startup.s.
; out_section A/X = section name, Y = length; out_start = start address.
; out_byte A = byte; requires an open section.
; out_bytes A/X = data, Y = count (0-255).
; out_end_section
; out_finish Writes the MAP, closes both files, and returns C=0 on success.
; out_abort Closes and scratches only files created by this run.
;
; Every failure sets out_err and returns C=1; the caller must call out_abort.
; DOS/KERNAL errors are OUT_ERR_DISK; the detailed DOS code is not retained.

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

.import dio_open_cmd, dio_open_in, dio_open_out, dio_read_status
.import dio_chkout, dio_putc, dio_clrchn, dio_close, dio_command, dio_dos_code
.import gen_stub, gen_startup, gen_startup_size

.export out_begin, out_startup, out_section, out_end_section
.export out_byte, out_bytes, out_finish, out_abort
.export out_load, out_entry, out_start, out_err

.segment "ZEROPAGE"
out_dst: .res 2
out_srcp: .res 2
out_rec_ptr: .res 2

.segment "CODE"

; Build one name from out_base plus the suffix at out_srcp (out_suffix_len).
; out_dst is advanced to the end; out_name_len is the total length.
build_name:
ldy #$00
@base: cpy out_base_len
beq @suffix
lda out_base,y
sta (out_dst),y
iny
bne @base
@suffix:
sty out_tmp
clc
lda out_dst
adc out_tmp
sta out_dst
bcc :+
inc out_dst+1
: ldy #$00
@sl: cpy out_suffix_len
beq @done
lda (out_srcp),y
sta (out_dst),y
iny
bne @sl
@done: lda out_base_len
clc
adc out_suffix_len
sta out_name_len
rts

.macro MAKE_NAME dst, suffix, slen, lenvar
lda #<dst
sta out_dst
lda #>dst
sta out_dst+1
lda #<suffix
sta out_srcp
lda #>suffix
sta out_srcp+1
lda #slen
sta out_suffix_len
jsr build_name
lda out_name_len
sta lenvar
.endmacro

; Append out_append_len bytes from out_srcp to out_dst.
append_bytes:
ldy #$00
@loop: cpy out_append_len
beq @done
lda (out_srcp),y
sta (out_dst),y
iny
bne @loop
@done: tya
clc
adc out_dst
sta out_dst
bcc :+
inc out_dst+1
: rts

; out_rec_ptr = out_sections + out_index * OUT_RECORD_SIZE.
set_rec_ptr:
lda #<out_sections
sta out_rec_ptr
lda #>out_sections
sta out_rec_ptr+1
ldx out_index
beq @done
@add: clc
lda out_rec_ptr
adc #OUT_RECORD_SIZE
sta out_rec_ptr
bcc :+
inc out_rec_ptr+1
: dex
bne @add
@done: rts

; Build and send "S0:name" on the command channel.
out_scratch:
sta out_scratch_ptr
stx out_scratch_ptr+1
sty out_scratch_len
lda #<out_cmd
sta out_dst
lda #>out_cmd
sta out_dst+1
lda #<scratch_prefix
sta out_srcp
lda #>scratch_prefix
sta out_srcp+1
lda #3
sta out_append_len
jsr append_bytes
lda out_scratch_ptr
sta out_srcp
lda out_scratch_ptr+1
sta out_srcp+1
lda out_scratch_len
sta out_append_len
jsr append_bytes
lda out_scratch_len
clc
adc #3
sta out_cmd_len
lda #<out_cmd
ldx #>out_cmd
ldy out_cmd_len
jsr dio_command
bcs @disk
lda dio_dos_code
cmp #20
bcc @ok
cmp #62 ; already absent is harmless
beq @ok
@disk: lda #OUT_ERR_DISK
sta out_err
sec
rts
@ok: clc
rts

; Test A/X name, length Y. C=1 if it exists (out_err=0) or the drive failed
; (out_err=OUT_ERR_DISK); C=0 if it does not exist.
out_exists:
jsr dio_open_in
bcs @disk
jsr dio_read_status
bcs @disk
lda dio_dos_code
cmp #62
beq @absent
cmp #20
bcs @disk
lda #DIO_LFN_IN
jsr dio_close
lda #OUT_OK
sta out_err
sec
rts
@absent:
lda #DIO_LFN_IN
jsr dio_close
lda #OUT_OK
sta out_err
clc
rts
@disk:
lda #DIO_LFN_IN
jsr dio_close
lda #OUT_ERR_DISK
sta out_err
sec
rts

out_begin:
sta out_srcp
stx out_srcp+1
sty out_name_len

lda #$00
sta out_count
sta out_in_section
sta out_prg_created
sta out_map_created
sta out_cmd_open
sta out_prg_is_open
sta out_map_is_open
sta out_err
lda out_load
sta out_current
lda out_load+1
sta out_current+1

lda out_name_len
sta out_base_len
cmp #4
bcc @base_ready
ldy out_name_len
dey
lda (out_srcp),y
cmp #'8'
bne @base_ready
dey
lda (out_srcp),y
and #$7F
cmp #'N'
bne @base_ready
dey
lda (out_srcp),y
cmp #'.'
bne @base_ready
lda out_name_len
sec
sbc #3
sta out_base_len
@base_ready:
lda out_base_len
bne @have_base
jmp @bad_name
@have_base:
cmp #13
bcc @base_ok
jmp @bad_name
@base_ok:
ldy #$00
@copy: cpy out_base_len
beq @copied
lda (out_srcp),y
sta out_base,y
iny
bne @copy
@copied:
MAKE_NAME out_prg_name, sfx_prg, 4, out_prg_len
MAKE_NAME out_map_name, sfx_map, 4, out_map_len
MAKE_NAME out_prg_open, sfx_prg_open, 8, out_prg_open_len
MAKE_NAME out_map_open, sfx_map_open, 8, out_map_open_len
MAKE_NAME out_prg_read, sfx_prg_read, 8, out_prg_read_len
MAKE_NAME out_map_read, sfx_map_read, 8, out_map_read_len

jsr dio_open_cmd
bcc :+
jmp @disk_fail
:
lda #1
sta out_cmd_open

lda #<out_prg_read
ldx #>out_prg_read
ldy out_prg_read_len
jsr out_exists
bcc @check_map
lda out_err
bne @close_fail
jmp @exists
@check_map:
lda #<out_map_read
ldx #>out_map_read
ldy out_map_read_len
jsr out_exists
bcc @open_prg
lda out_err
bne @close_fail
jmp @exists

@exists:
lda #OUT_ERR_EXISTS
sta out_err
jmp @close_fail

@open_prg:
lda #1
sta out_prg_is_open
sta out_prg_created
lda #<out_prg_open
ldx #>out_prg_open
ldy out_prg_open_len
jsr dio_open_out
bcs @fail_created
jsr dio_read_status
bcs @fail_created
lda dio_dos_code
cmp #20
bcs @fail_created
ldx #DIO_LFN_ERR
jsr dio_chkout
bcs @fail_created
lda out_load
jsr out_put
bcs @fail_created
lda out_load+1
jsr out_put
bcs @fail_created
lda out_load ; the header is not part of section 0
sta out_current
lda out_load+1
sta out_current+1
clc
rts

@bad_name:
lda #OUT_ERR_NAME
sta out_err
sec
rts
@disk_fail:
lda #OUT_ERR_DISK
sta out_err
sec
rts
@close_fail:
jsr out_close_cmd
sec
rts
@fail_created:
lda #OUT_ERR_DISK
sta out_err
jsr out_cleanup
sec
rts

out_startup:
lda #<PRG_LOAD_ADDR
sta out_start
lda #>PRG_LOAD_ADDR
sta out_start+1
lda #<stub_name
ldx #>stub_name
ldy #4
jsr out_section
bcs @out
lda #<gen_stub
ldx #>gen_stub
ldy #PRG_STUB_SIZE
jsr out_bytes
bcs @out
jsr out_end_section

lda #<PRG_ENTRY_ADDR
sta out_start
lda #>PRG_ENTRY_ADDR
sta out_start+1
lda #<start_name
ldx #>start_name
ldy #5
jsr out_section
bcs @out
lda #<gen_startup
ldx #>gen_startup
ldy #<gen_startup_size
jsr out_bytes
bcs @out
jmp out_end_section
@out: rts

out_section:
sta out_tmp
stx out_tmp+1
sty out_tmp_len
lda out_in_section
beq @no_prev
jsr out_end_section
@no_prev:
lda out_count
cmp #OUT_MAX_SECTIONS
bcs @too_many
lda out_tmp_len
cmp #OUT_SECTION_NAME_MAX + 1
bcs @bad_name
lda out_count
sta out_index
jsr set_rec_ptr
ldy #0
lda out_tmp
sta (out_rec_ptr),y
iny
lda out_tmp+1
sta (out_rec_ptr),y
iny
lda out_tmp_len
sta (out_rec_ptr),y
iny
lda out_start
sta (out_rec_ptr),y
iny
lda out_start+1
sta (out_rec_ptr),y
iny
lda #$00
sta (out_rec_ptr),y
iny
sta (out_rec_ptr),y

lda out_current+1
cmp out_start+1
bcc @pad
bne @order
lda out_current
cmp out_start
bcc @pad
bne @order
beq @at_start
@pad: lda out_current+1
cmp out_start+1
bcc @pad_byte
bne @at_start
lda out_current
cmp out_start
bcs @at_start
@pad_byte:
lda #$00
jsr out_put
bcs @out
jmp @pad
@at_start:
lda #1
sta out_in_section
inc out_count
clc
rts
@order:
lda #OUT_ERR_ORDER
sta out_err
sec
rts
@too_many:
lda #OUT_ERR_SECTIONS
sta out_err
sec
rts
@bad_name:
lda #OUT_ERR_NAME
sta out_err
sec
@out: rts

out_end_section:
lda out_in_section
beq @ok
ldy #5
lda out_current
sec
sbc out_start
sta (out_rec_ptr),y
iny
lda out_current+1
sbc out_start+1
sta (out_rec_ptr),y
lda #$00
sta out_in_section
@ok: clc
rts

out_byte:
sta out_byte_value
lda out_in_section
bne @write
lda #OUT_ERR_STATE
sta out_err
sec
rts
@write: lda out_byte_value
jmp out_put

; A = byte, no section check. Preserves Y; C=1 on write failure.
out_put:
sty out_y
jsr dio_putc
bcs @fail
inc out_current
bne @ok
inc out_current+1
@ok: ldy out_y
clc
rts
@fail:
lda #OUT_ERR_DISK
sta out_err
ldy out_y
sec
rts

out_bytes:
sta out_srcp
stx out_srcp+1
sty out_append_len
ldy #$00
@loop: cpy out_append_len
beq @done
lda (out_srcp),y
sty out_y
jsr out_byte
bcs @out
ldy out_y
iny
bne @loop
@done: clc
@out: rts

out_finish:
jsr out_end_section
jsr out_close_prg

lda #1
sta out_map_is_open
sta out_map_created
lda #<out_map_open
ldx #>out_map_open
ldy out_map_open_len
jsr dio_open_out
bcs @fail
jsr dio_read_status
bcs @fail
lda dio_dos_code
cmp #20
bcs @fail
ldx #DIO_LFN_ERR
jsr dio_chkout
bcs @fail
jsr out_write_map
bcs @fail
jsr out_close_map
jsr out_close_cmd
lda #OUT_OK
sta out_err
lda #$00
sta out_prg_created
sta out_map_created
clc
rts
@fail:
lda out_err
bne :+
lda #OUT_ERR_DISK
sta out_err
: jsr out_cleanup
sec
rts

out_abort:
lda #OUT_OK
sta out_err
jsr out_cleanup
lda out_err
beq @ok
sec
rts
@ok: clc
rts

; Close created outputs and scratch only files this run created.
out_cleanup:
jsr out_close_prg
jsr out_close_map
lda out_cmd_open
bne @have_cmd
jsr dio_open_cmd
bcs @done
lda #1
sta out_cmd_open
@have_cmd:
lda out_prg_created
beq @map
lda #<out_prg_name
ldx #>out_prg_name
ldy out_prg_len
jsr out_scratch
@map: lda out_map_created
beq @close
lda #<out_map_name
ldx #>out_map_name
ldy out_map_len
jsr out_scratch
@close:
jsr out_close_cmd
@done:
lda #$00
sta out_prg_created
sta out_map_created
rts

out_close_prg:
lda out_prg_is_open
beq @ok
jsr dio_clrchn
lda #DIO_LFN_ERR
jsr dio_close
lda #$00
sta out_prg_is_open
@ok: clc
rts

out_close_map:
lda out_map_is_open
beq @ok
jsr dio_clrchn
lda #DIO_LFN_ERR
jsr dio_close
lda #$00
sta out_map_is_open
@ok: clc
rts

out_close_cmd:
lda out_cmd_open
beq @ok
lda #DIO_LFN_CMD
jsr dio_close
lda #$00
sta out_cmd_open
@ok: clc
rts

; ---------------------------------------------------------------- MAP text
out_write_map:
lda #<map_header
ldx #>map_header
ldy #map_header_len
jsr out_text
bcc :+
jmp @out
:
lda #<map_entry
ldx #>map_entry
ldy #map_entry_len
jsr out_text
bcc :+
jmp @out
:
lda out_entry
sta out_num
lda out_entry+1
sta out_num+1
jsr out_hex4
bcc :+
jmp @out
:
jsr out_cr
bcc :+
jmp @out
:
lda #$00
sta out_index
@section:
lda out_index
cmp out_count
beq @done
jsr set_rec_ptr
lda #<section_prefix
ldx #>section_prefix
ldy #section_prefix_len
jsr out_text
bcs @out
ldy #0
lda (out_rec_ptr),y
sta out_srcp
iny
lda (out_rec_ptr),y
sta out_srcp+1
iny
lda (out_rec_ptr),y
sta out_text_len
ldy #0
@name: cpy out_text_len
beq @named
lda (out_srcp),y
sty out_y
jsr out_char
bcs @out
ldy out_y
iny
bne @name
@named:
lda #' '
jsr out_char
bcs @out
ldy #3
lda (out_rec_ptr),y
sta out_num
iny
lda (out_rec_ptr),y
sta out_num+1
jsr out_hex4
bcs @out
lda #' '
jsr out_char
bcs @out
ldy #5
lda (out_rec_ptr),y
sta out_num
iny
lda (out_rec_ptr),y
sta out_num+1
jsr out_hex4
bcs @out
jsr out_cr
bcs @out
inc out_index
jmp @section
@done: clc
@out: rts

; A/X = text, Y = length.
out_text:
sta out_srcp
stx out_srcp+1
sty out_text_len
ldy #$00
@loop: cpy out_text_len
beq @done
lda (out_srcp),y
sty out_y
jsr out_char
bcs @out
ldy out_y
iny
bne @loop
@done: clc
@out: rts

out_cr:
lda #$0D
jmp out_char

out_char:
sty out_y
jsr dio_putc
bcs @fail
ldy out_y
clc
rts
@fail:
lda #OUT_ERR_DISK
sta out_err
ldy out_y
sec
rts

out_hex4:
lda out_num+1
jsr out_hex_byte
bcs @out
lda out_num
jmp out_hex_byte
@out: rts

out_hex_byte:
pha
lsr a
lsr a
lsr a
lsr a
tax
lda hex_digits,x
jsr out_char
bcs @fail
pla
and #$0F
tax
lda hex_digits,x
jmp out_char
@fail: pla
sec
rts

.segment "RODATA"
sfx_prg: .byte ".PRG"
sfx_map: .byte ".MAP"
sfx_prg_open: .byte ".PRG,P,W"
sfx_map_open: .byte ".MAP,S,W"
sfx_prg_read: .byte ".PRG,P,R"
sfx_map_read: .byte ".MAP,S,R"
stub_name: .byte "STUB"
start_name: .byte "START"
scratch_prefix: .byte "S0:"
map_header: .byte "NOVA128 MAP 1", $0D
map_header_len = * - map_header
map_entry: .byte "ENTRY "
map_entry_len = * - map_entry
section_prefix: .byte "SECTION "
section_prefix_len = * - section_prefix
hex_digits: .byte "0123456789ABCDEF"

.segment "BSS"
out_base: .res 12
out_base_len: .res 1
out_name_len: .res 1
out_suffix_len: .res 1
out_prg_name: .res 16
out_prg_len: .res 1
out_map_name: .res 16
out_map_len: .res 1
out_prg_open: .res 20
out_prg_open_len: .res 1
out_map_open: .res 20
out_map_open_len: .res 1
out_prg_read: .res 20
out_prg_read_len: .res 1
out_map_read: .res 20
out_map_read_len: .res 1
out_cmd: .res 48
out_cmd_len: .res 1
out_load: .res 2
out_entry: .res 2
out_start: .res 2
out_current: .res 2
out_count: .res 1
out_index: .res 1
out_in_section: .res 1
out_prg_created: .res 1
out_map_created: .res 1
out_cmd_open: .res 1
out_prg_is_open: .res 1
out_map_is_open: .res 1
out_err: .res 1
out_sections: .res OUT_MAX_SECTIONS * OUT_RECORD_SIZE
out_tmp: .res 2
out_tmp_len: .res 1
out_scratch_ptr: .res 2
out_scratch_len: .res 1
out_append_len: .res 1
out_byte_value: .res 1
out_text_len: .res 1
out_y: .res 1
out_num: .res 2

+ 71
- 0
newLang/src/runtime/startup.s Ver fichero

@@ -0,0 +1,71 @@
; startup.s - fixed generated-program entry (E04-S01).
;
; output.s copies these bytes into the generated PRG at PRG_LOAD_ADDR and
; PRG_ENTRY_ADDR. They are position-independent except for the fixed mailbox
; addresses, which are part of the platform contract. The stub is the same
; 12-byte BASIC 7 form used by the compiler itself:
;
; $1C01 0B 1C link to the end marker at $1C0B
; $1C03 0A 00 line 10
; $1C05 9E SYS
; $1C06 "7181" $1C0D
; $1C0A 00 end of line
; $1C0B 00 00 end of program
; $1C0D startup code
;
; The startup has no caller-visible result and returns to BASIC. Later E04
; stories replace the body with runtime initialization once frames and the
; generated-program memory map exist.

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

.segment "RODATA"

.export gen_stub, gen_stub_size, gen_startup, gen_startup_size
gen_stub:
.byte $0B, $1C ; next BASIC line at $1C0B
.word 10
.byte $9E ; SYS
.byte "7181" ; $1C0D
.byte $00
.word $0000 ; end of BASIC program
gen_stub_end:

gen_startup:
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 #$00
sta MB_CODE
sta MB_CODE+1
sta MB_ADDR
sta MB_ADDR+1
sta MB_DATA
sta MB_DATA+1
sta MB_DATA+2
sta MB_DATA+3
sta MB_DATA+4
sta MB_DATA+5
sta MB_DATA+6
sta MB_DATA+7
lda #MB_ST_PASS
sta MB_STATUS
rts
gen_startup_end:

gen_stub_size = gen_stub_end - gen_stub
gen_startup_size = gen_startup_end - gen_startup
.assert gen_stub_size = PRG_STUB_SIZE, error, "generated BASIC stub size changed"
.assert gen_startup_size <= 255, error, "startup must fit the byte-count API"

+ 248
- 0
newLang/tests/codegen/output-test.s Ver fichero

@@ -0,0 +1,248 @@
; E04-S01: PRG/MAP writer test in x128.
;
; The fixture runs in the compiler mapping and writes GENERATE.PRG/MAP with
; the fixed startup, a PAD section at $1C80, then checks:
; +0 out_finish result (0)
; +1 collision result (OUT_ERR_EXISTS)
; +2 section-order result (OUT_ERR_ORDER)
; +3 abort result (0)
; COLLIDE.PRG/MAP already exist on the test disk; the host verifies they are
; unchanged. ABORT and ORDER must leave no output behind.

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

.import mmu_init, mmu_bank0, mmu_shutdown
.import out_begin, out_startup, out_section, out_end_section
.import out_byte, out_bytes, out_finish, out_abort
.import out_load, out_entry, out_start, out_err

.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_PLATFORM
sta MB_PRODUCER
lda #MB_ST_RUNNING
sta MB_STATUS
lda #$00
sta fail_mask
sta MB_DATA+0
sta MB_DATA+1
sta MB_DATA+2
sta MB_DATA+3

jsr mmu_init
jsr mmu_bank0

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

; ------------------------------------------------ successful output
lda #<name_generate
ldx #>name_generate
ldy #name_generate_len
jsr out_begin
bcs @gen_fail
jsr out_startup
bcs @gen_fail
lda #$80
sta out_start
lda #$1C
sta out_start+1
lda #<name_pad
ldx #>name_pad
ldy #3
jsr out_section
bcs @gen_fail
lda #<pad_bytes
ldx #>pad_bytes
ldy #4
jsr out_bytes
bcs @gen_fail
jsr out_end_section
jsr out_finish
bcs @gen_fail
lda out_err
sta MB_DATA+0
cmp #OUT_OK
beq @gen_done
lda #$01
jsr fail
jmp @gen_done
@gen_fail:
lda out_err
sta MB_DATA+0
lda #$01
jsr fail
jsr out_abort
@gen_done:

; ---------------------------------------------------- collision
lda #<name_collide
ldx #>name_collide
ldy #name_collide_len
jsr out_begin
bcc @coll_unexpected
lda out_err
sta MB_DATA+1
cmp #OUT_ERR_EXISTS
beq @coll_ok
lda #$02
jsr fail
jmp @coll_abort
@coll_unexpected:
lda #$02
jsr fail
jmp @coll_abort
@coll_ok:
@coll_abort:
jsr out_abort
@coll_done:

; ------------------------------------------------ section order
lda #<name_order
ldx #>name_order
ldy #name_order_len
jsr out_begin
bcs @order_begin_fail
lda #$00
sta out_start
lda #$1C
sta out_start+1
lda #<name_bad
ldx #>name_bad
ldy #3
jsr out_section
bcc @order_unexpected
lda out_err
sta MB_DATA+2
cmp #OUT_ERR_ORDER
beq @order_abort
lda #$04
jsr fail
jmp @order_abort
@order_begin_fail:
lda out_err
sta MB_DATA+2
lda #$04
jsr fail
jmp @order_done
@order_unexpected:
lda #$04
jsr fail
jmp @order_abort
@order_abort:
jsr out_abort
@order_done:

; -------------------------------------------------------- abort
lda #<name_abort
ldx #>name_abort
ldy #name_abort_len
jsr out_begin
bcs @abort_fail
lda #$01
sta out_start
lda #$1C
sta out_start+1
lda #<name_x
ldx #>name_x
ldy #1
jsr out_section
bcs @abort_fail
lda #$AA
jsr out_byte
bcs @abort_fail
jsr out_abort
bcs @abort_fail
lda out_err
sta MB_DATA+3
cmp #OUT_OK
beq @abort_done
@abort_fail:
lda out_err
sta MB_DATA+3
lda #$08
jsr fail
jsr out_abort
@abort_done:

jsr mmu_shutdown
lda fail_mask
sta MB_CODE
lda #$00
sta MB_CODE+1
lda #MB_ST_FAIL
ldx fail_mask
bne @status
lda #MB_ST_PASS
@status:
sta MB_STATUS
ldy #$00
@print: lda message,y
beq @end
sty print_y
jsr CHROUT
ldy print_y
iny
bne @print
@end: rts

.segment "CODE"
fail:
ora fail_mask
sta fail_mask
rts

.segment "RODATA"
name_generate: .byte "GENERATE.N8"
name_generate_len = * - name_generate
name_collide: .byte "COLLIDE.N8"
name_collide_len = * - name_collide
name_order: .byte "ORDER.N8"
name_order_len = * - name_order
name_abort: .byte "ABORT.N8"
name_abort_len = * - name_abort
name_pad: .byte "PAD"
name_bad: .byte "BAD"
name_x: .byte "X"
pad_bytes: .byte $DE, $AD, $BE, $EF
message: .byte "NOVA128 OUTPUT TEST DONE", $0D, $00

.segment "BSS"
fail_mask: .res 1
print_y: .res 1

Cargando…
Cancelar
Guardar

Powered by TurnKey Linux.