From 26422557ac21c083762f28d42ae59550c9a2378d Mon Sep 17 00:00:00 2001 From: Daniel Covington Date: Mon, 9 Nov 2026 07:34:44 -0500 Subject: [PATCH] Add the scalar instruction emitter (E04-S03). MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Emit bounded documented instructions for scalar assignments, word and integer arithmetic, comparisons, and short-circuit booleans, with checked forward branch fixups. Add multiply and signed/unsigned divide helpers with a stable divide-by-zero result, plus a reviewed golden byte stream and disassembly. Generated with Codebuff 🤖 Co-Authored-By: Codebuff --- newLang/docs/architecture.md | 18 + newLang/docs/decisions.md | 1 + newLang/planning/claims/E04-S03.md | 98 +++ newLang/scripts/build-scalar-test.ps1 | 16 + newLang/scripts/verify-scalar-test.ps1 | 99 +++ newLang/src/common/arithmetic.inc | 14 + newLang/src/common/codegen.inc | 12 + newLang/src/compiler/codegen.s | 216 +++++++ newLang/src/runtime/arithmetic.s | 169 +++++ newLang/tests/codegen/scalar-test.s | 743 ++++++++++++++++++++++ newLang/tests/codegen/scalar.expected.dis | 95 +++ newLang/tests/codegen/scalar.expected.hex | 1 + 12 files changed, 1482 insertions(+) create mode 100644 newLang/planning/claims/E04-S03.md create mode 100644 newLang/scripts/build-scalar-test.ps1 create mode 100644 newLang/scripts/verify-scalar-test.ps1 create mode 100644 newLang/src/common/arithmetic.inc create mode 100644 newLang/src/common/codegen.inc create mode 100644 newLang/src/compiler/codegen.s create mode 100644 newLang/src/runtime/arithmetic.s create mode 100644 newLang/tests/codegen/scalar-test.s create mode 100644 newLang/tests/codegen/scalar.expected.dis create mode 100644 newLang/tests/codegen/scalar.expected.hex diff --git a/newLang/docs/architecture.md b/newLang/docs/architecture.md index 1ed8df7..e4a3d9c 100644 --- a/newLang/docs/architecture.md +++ b/newLang/docs/architecture.md @@ -406,6 +406,24 @@ generation relies on it. `delete` through an interface uses the object pointer and its class descriptor, not the interface table, to find the optional destructor and object size. +### 7.6 Scalar code generation (E04-S03) + +`src/compiler/codegen.s` emits documented 6502 instructions into a bounded +256-byte buffer. One-byte values are in A; two-byte values are A (low) and X +(high). The emitter provides immediate/absolute loads and stores, absolute +`ADC`/`SBC`/`CMP` operands, `JSR`, and forward relative branches with checked +fixups. Forward branches are limited to +0..+127 bytes; E04-S04 supplies the +control-flow and long-branch expansion. + +Multiplication and signed/unsigned division use `src/runtime/arithmetic.s`. +Operands are in `ar_a`/`ar_b`, the result is A/X, and division leaves its +remainder in `ar_rem`. Division by zero sets provisional `ar_fault` to +`AR_FAULT_DIV0`, returns A=X=0 with C=1, and is mapped to the final runtime +fault namespace by E04-S05. The scalar fixture executes the emitted shapes, +checks short-circuit evaluation, and compares a reviewed golden byte stream +and da65 disassembly. Parser integration into statements and frames is +E04-S04's responsibility. + ## 8. Heap and Ownership - The generated-program heap initially resides in the same RAM bank as program diff --git a/newLang/docs/decisions.md b/newLang/docs/decisions.md index 14e3e0f..609adfb 100644 --- a/newLang/docs/decisions.md +++ b/newLang/docs/decisions.md @@ -56,6 +56,7 @@ documented default. `Proposed` means a later change still needs approval. | 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`. | | D129 | Generated code executes with CR `$0E` and IRQs masked, then restores the entry CR. The software stack is bank 0 `$B800-$BFFF`, growing down from `$C000`; `SP`/`FP` use `$FB-$FE`. A frame stores the previous FP at `FP`, arguments from `FP+2`, and locals below `FP`; callers push arguments right-to-left and discard them after return. Results are A or A/X. `frame_enter` reserves the old FP and locals transactionally, refuses when the 6502 stack pointer is below `$40`, and returns C=1/`FRAME_ERR_STACK` with `SP`/`FP` restored on overflow. | Makes recursion and nested calls testable without consuming BASIC's zero page or the compiler's private stack. The $40 hardware-stack guard prevents a JAM before the software bound, and the transactional failure lets callers unwind cleanly. The 2 KB software stack, guard threshold, and mapping install are provisional until codegen and later runtime stories. Implemented by `frames.s` and `frames.inc`. | +| D130 | Scalar codegen emits documented 6502 instructions into a bounded 256-byte buffer. Values are A (one byte) or A/X low/high (two bytes); assignments use absolute stores, comparisons produce a 0/1 byte, and forward branches use checked fixups. Multiplication and signed/unsigned division call runtime helpers with operands in `ar_a`/`ar_b`; results are A/X, division remainder is `ar_rem`, and division by zero sets provisional `ar_fault=AR_FAULT_DIV0`, A=X=0, C=1. E04-S04 owns parser-to-emitter integration, control flow, and long-branch expansion. | Gives the backend a small, directly testable instruction layer and a deterministic golden stream before the control-flow shell exists. The 256-byte buffer and forward-only branches are deliberate limits; E04-S05 maps the provisional arithmetic fault to the final R-code namespace. Implemented by `codegen.s`, `codegen.inc`, `arithmetic.s`, and `arithmetic.inc`. | ## Deferred diff --git a/newLang/planning/claims/E04-S03.md b/newLang/planning/claims/E04-S03.md new file mode 100644 index 0000000..e93a8aa --- /dev/null +++ b/newLang/planning/claims/E04-S03.md @@ -0,0 +1,98 @@ +--- +story: E04-S03 +owner: e04_scalar +status: done +started: 2026-11-09 +depends-on: [E04-S02] +--- + +# E04-S03 Claim + +## Intended change + +Add the scalar instruction emitter in `src/compiler/codegen.s` and the +provisional arithmetic helpers in `src/runtime/arithmetic.s`. The emitter +writes documented 6502 instructions into a bounded code buffer, supports +absolute loads/stores and arithmetic operands, immediates, calls, forward +branches with checked fixups, and returns the byte length for golden tests. + +The x128 fixture drives the emitter directly (parser integration is E04-S04) +to build representative byte, word, integer, boolean, comparison, assignment, +and short-circuit shapes, copies the golden byte stream, and executes it. It +also calls the multiply/divide helpers, including unsigned and signed +division, and verifies that division by zero sets a stable fault result. + +## File area + +- `src/compiler/codegen.s` (new), `src/runtime/arithmetic.s` (new) +- `src/common/codegen.inc`, `src/common/arithmetic.inc` (new) +- `tests/codegen/scalar-test.s` +- `tests/codegen/scalar.expected.dis`, `tests/codegen/scalar.expected.hex` +- `scripts/build-scalar-test.ps1`, `scripts/verify-scalar-test.ps1` +- `docs/decisions.md`, `docs/architecture.md` +- `planning/claims/E04-S03.md` +- generated files under `build/` + +## Acceptance evidence + +- [x] Emits correct byte, word, integer, boolean, comparison, and assignment + code: the fixture executes byte assignment/widening, word addition, integer + subtraction, six comparisons, and boolean short-circuit shapes. Results: + `$5A`, `$0E`, `$2221`, `$ECBB`. +- [x] Short-circuit `and`/`or` avoids evaluating the unused operand: both + shapes leave `V_EVAL=0` while producing the correct `V_FLAG`. +- [x] Division by zero faults predictably: the generated call to `ar_div_u16` + returns C=1, sets `ar_fault=AR_FAULT_DIV0`, and stores a zero result. +- [x] Golden disassembly covers representative expression shapes: a reviewed + 231-byte stream and canonical da65 listing are committed as + `tests/codegen/scalar.expected.hex` and `.dis`; the verifier compares both. + +## Coordination notes + +- E04-S02 is `done`; this story adds the scalar backend primitives but does not + integrate them into the parser/control-flow shell. +- D130 records the provisional scalar emitter interface and arithmetic helper + data flow. +- Runtime arithmetic faults use a provisional `AR_FAULT_DIV0` value until + E04-S05 freezes the R-code namespace. +- The fixture runs in the compiler mapping; its emitter buffer is writable BSS + below `$A000`. Generated code is executed directly in that buffer. + +## Work log + +- Added `cg_*` byte emitters, absolute operand forms, forward branch fixups, + and a 256-byte bounded buffer. +- Added 16-bit unsigned multiply, restoring unsigned divide, and signed divide + with truncation toward zero. +- Fixed ca65 operator precedence in the fixture macros: `#>addr+1` became + `(>addr)+1`, so high-byte operands pointed at `$1Cxx` instead of `$1Bxx`. + Parenthesized `#<(addr)`/`#>(addr)` fixed word high bytes and the apparent + OR-tail misalignment. +- Increased the fixup table to 32 entries because comparison branches remain + recorded after patching. +- Corrected fixture expectations: widened `$5A` is `$005A`, and the short- + circuit cases now set their left operands explicitly. + +## Completion + +- Tests run: + - `scripts/build-scalar-test.ps1` + - `scripts/verify-scalar-test.ps1 -Port 6530` + - `scripts/verify-output-test.ps1 -Port 6530 -RunPort 6531` + - `scripts/verify-frame-test.ps1 -Port 6530` + - `scripts/verify-diag-test.ps1 -Port 6530` + - `scripts/verify-parser-test.ps1 -Port 6530` + - `git diff --check` +- Results: scalar mailbox PASS, data `5A 0E 21 22 BB EC E0 01`; + golden 231 bytes; canonical disassembly and hex match committed files. +- Files changed: `src/compiler/codegen.s`, `src/runtime/arithmetic.s`, + `src/common/codegen.inc`, `src/common/arithmetic.inc`, + `tests/codegen/scalar-test.s`, golden expectations, + `scripts/build-scalar-test.ps1`, `scripts/verify-scalar-test.ps1`, + `docs/decisions.md`, `docs/architecture.md`, this claim. +- Remaining risks: parser/statement integration and long-branch expansion are + E04-S04; the emitter buffer is 255 bytes and only forward branches are + supported; signed remainder is a magnitude, not sign-extended; byte + multiply/divide and integer comparisons are not separately exercised; the + divide fault is provisional until E04-S05; generated code is currently + executed in the compiler mapping, not installed by a generated startup. diff --git a/newLang/scripts/build-scalar-test.ps1 b/newLang/scripts/build-scalar-test.ps1 new file mode 100644 index 0000000..b184e55 --- /dev/null +++ b/newLang/scripts/build-scalar-test.ps1 @@ -0,0 +1,16 @@ +# E04-S03: build the scalar emitter/arithmetic fixture. +[CmdletBinding()] +param() + +$ErrorActionPreference = "Stop" +$project = Split-Path $PSScriptRoot -Parent +Import-Module (Join-Path $PSScriptRoot "lib\Nova128Test.psm1") -Force + +$out = Join-Path $project "build\scalar-test" +$link = Invoke-Nova128Link -Name "scalar-test" -BuildDir $out ` + -Config (Join-Path $project "cfg\compiler.cfg") -Sources @( + (Join-Path $project "src\compiler\codegen.s"), + (Join-Path $project "src\runtime\arithmetic.s"), + (Join-Path $project "tests\codegen\scalar-test.s")) + +Write-Host ("built {0}" -f $link.Program) diff --git a/newLang/scripts/verify-scalar-test.ps1 b/newLang/scripts/verify-scalar-test.ps1 new file mode 100644 index 0000000..8689030 --- /dev/null +++ b/newLang/scripts/verify-scalar-test.ps1 @@ -0,0 +1,99 @@ +# E04-S03: run the scalar emitter fixture, check results, capture golden bytes, +# disassemble them with da65, and compare committed expectations when present. +[CmdletBinding()] +param( + [int]$Port = 6530, + [int]$TimeoutSeconds = 90 +) + +$ErrorActionPreference = "Stop" +$project = Split-Path $PSScriptRoot -Parent +Import-Module (Join-Path $PSScriptRoot "lib\Nova128Test.psm1") -Force +& (Join-Path $PSScriptRoot "build-scalar-test.ps1") + +$out = Join-Path $project "build\scalar-test" +$prg = Join-Path $out "scalar-test.prg" +$labels = Join-Path $out "scalar-test.lbl" +$session = Start-Nova128X128 -Autostart $prg -Port $Port ` + -Screenshot (Join-Path $out "scalar-test-vicii.png") +try { + $mb = Wait-Nova128Mailbox $session -TimeoutSeconds $TimeoutSeconds + $lenAddr = Get-Nova128Label -Labels $labels -Name "golden_len" + $copyAddr = Get-Nova128Label -Labels $labels -Name "golden_copy" + $lenBytes = Read-Nova128Memory -Session $session -Address $lenAddr -Count 1 + $goldenLen = [int]$lenBytes[0] + if ($goldenLen -eq 0) { throw "golden_len is zero" } + $golden = Read-Nova128Memory -Session $session -Address $copyAddr -Count $goldenLen +} finally { + Stop-Nova128X128 $session +} + +Write-Host ('mailbox: ' + (($mb | ForEach-Object { $_.ToString('X2') }) -join ' ')) +Write-Host ("results: byte={0:X2} cmp={1:X2} add={2:X2}{3:X2} sub={4:X2}{5:X2} mul={6:X2} fault={7}" -f ` + $mb[8], $mb[9], $mb[11], $mb[10], $mb[13], $mb[12], $mb[14], $mb[15]) + +$failures = @() +if ($mb[2] -ne 0x03) { $failures += "producer $($mb[2]) is not a generated program" } +if ($mb[3] -ne 0xA5) { $failures += "fixture status is not PASS" } +if ($mb[4] -ne 0 -or $mb[5] -ne 0) { $failures += "code is $($mb[4] + 256 * $mb[5])" } +$want = [ordered]@{ + byte = 0x5A + cmp = 0x0E + addLow = 0x21; addHigh = 0x22 + subLow = 0xBB; subHigh = 0xEC + mulLow = 0xE0 + fault = 1 +} +if ($mb[8] -ne $want.byte) { $failures += ('byte assignment is {0:X2}, expected {1:X2}' -f $mb[8], $want.byte) } +if ($mb[9] -ne $want.cmp) { $failures += ('comparisons are {0:X2}, expected {1:X2}' -f $mb[9], $want.cmp) } +if ($mb[10] -ne $want.addLow -or $mb[11] -ne $want.addHigh) { + $failures += ('add is {0:X2}{1:X2}, expected {2:X2}{3:X2}' -f $mb[11], $mb[10], $want.addHigh, $want.addLow) +} +if ($mb[12] -ne $want.subLow -or $mb[13] -ne $want.subHigh) { + $failures += ('sub is {0:X2}{1:X2}, expected {2:X2}{3:X2}' -f $mb[13], $mb[12], $want.subHigh, $want.subLow) +} +if ($mb[14] -ne $want.mulLow) { $failures += ('mul low is {0:X2}, expected {1:X2}' -f $mb[14], $want.mulLow) } +if ($mb[15] -ne $want.fault) { $failures += "fault byte is $($mb[15]), expected 1" } + +$binPath = Join-Path $out "scalar.golden.bin" +[IO.File]::WriteAllBytes($binPath, $golden) +$disPath = Join-Path $out "scalar.golden.dis" +$da65 = (Get-Command da65 -ErrorAction Stop).Source +& $da65 --cpu 6502 --start-addr 0000 -o $disPath $binPath 2>&1 | Out-Null +if ($LASTEXITCODE -ne 0) { throw "da65 failed on $binPath" } +# Remove da65's timestamp/path header so the committed listing is stable. +$disLines = Get-Content -LiteralPath $disPath | Where-Object { + $_.Trim() -ne "" -and -not $_.StartsWith(";") +} +[IO.File]::WriteAllText($disPath, (($disLines -join "`n") + "`n")) + +$hex = (($golden | ForEach-Object { $_.ToString("X2") }) -join " ") + "`n" +$actualHex = Join-Path $out "scalar.golden.hex" +[IO.File]::WriteAllText($actualHex, $hex) +Write-Host "golden bytes: $goldenLen" +Write-Host "hex: $actualHex" +Write-Host "disassembly: $disPath" + +$expectedHexPath = Join-Path $project "tests\codegen\scalar.expected.hex" +if (Test-Path -LiteralPath $expectedHexPath) { + $expectedHex = ([IO.File]::ReadAllText($expectedHexPath)).Trim() + if ($hex.Trim() -ne $expectedHex) { + $failures += "golden bytes differ from tests/codegen/scalar.expected.hex" + } +} else { + Write-Host "no tests/codegen/scalar.expected.hex yet" +} + +$expectedDisPath = Join-Path $project "tests\codegen\scalar.expected.dis" +if (Test-Path -LiteralPath $expectedDisPath) { + $expectedDis = ([IO.File]::ReadAllText($expectedDisPath)).Replace("`r`n", "`n").TrimEnd() + $actualDis = ([IO.File]::ReadAllText($disPath)).Replace("`r`n", "`n").TrimEnd() + if ($actualDis -ne $expectedDis) { + $failures += "golden disassembly differs from tests/codegen/scalar.expected.dis" + } +} else { + Write-Host "no tests/codegen/scalar.expected.dis yet" +} + +if ($failures) { throw ("scalar verification failed:`n " + ($failures -join "`n ")) } +Write-Host "verified scalar assignments, comparisons, short-circuit, arithmetic, and divide-by-zero" diff --git a/newLang/src/common/arithmetic.inc b/newLang/src/common/arithmetic.inc new file mode 100644 index 0000000..ae7a0ed --- /dev/null +++ b/newLang/src/common/arithmetic.inc @@ -0,0 +1,14 @@ +; arithmetic.inc - provisional runtime scalar arithmetic (E04-S03). +; +; The operands live in ar_a/ar_b. The 16-bit result is A (low), X (high). +; Division also leaves the remainder in ar_rem. On division by zero the helper +; sets ar_fault=AR_FAULT_DIV0, returns A=X=0 and C=1. E04-S05 maps this to the +; final stable R-code namespace. + +.ifndef ARITHMETIC_INC +ARITHMETIC_INC = 1 + +AR_FAULT_NONE = 0 +AR_FAULT_DIV0 = 1 + +.endif diff --git a/newLang/src/common/codegen.inc b/newLang/src/common/codegen.inc new file mode 100644 index 0000000..ebc11a1 --- /dev/null +++ b/newLang/src/common/codegen.inc @@ -0,0 +1,12 @@ +; codegen.inc - scalar instruction emitter limits (E04-S03). + +.ifndef CODEGEN_INC +CODEGEN_INC = 1 + +CG_MAX = 255 ; bytes in the emitter buffer (len is 1 byte) +CG_FIX_MAX = 32 ; forward branches per buffer +CG_ERR_FULL = 1 ; buffer full +CG_ERR_FIX = 2 ; fixup table full or bad index +CG_ERR_RANGE = 3 ; relative branch out of range + +.endif diff --git a/newLang/src/compiler/codegen.s b/newLang/src/compiler/codegen.s new file mode 100644 index 0000000..be35e94 --- /dev/null +++ b/newLang/src/compiler/codegen.s @@ -0,0 +1,216 @@ +; codegen.s - documented scalar instruction emitter (E04-S03). +; +; The emitter writes into cg_code, a 256-byte RAM buffer. cg_len is the byte +; count (0-254); every failure sets cg_err and returns C=1. It does not emit a +; whole function yet: parser/codegen integration is E04-S04. +; +; API: +; cg_init +; cg_put A = byte +; cg_lda_imm A = value +; cg_ldx_imm A = value +; cg_ora_imm A = value +; cg_lda_abs A/X = address +; cg_ldx_abs A/X = address +; cg_sta_abs A/X = address +; cg_stx_abs A/X = address +; cg_adc_abs A/X = address +; cg_sbc_abs A/X = address +; cg_cmp_abs A/X = address +; cg_jsr_abs A/X = address +; cg_clc, cg_sec, cg_rts +; cg_branch_fwd A = branch opcode; emits opcode + placeholder, returns +; C=0 and A = fixup index +; cg_patch_here A = fixup index; target is the current end of buffer +; cg_here returns A = current length +; +; Only forward relative branches within +0..+127 bytes are accepted. This is +; enough for the scalar shapes and gives E04-S04 the checked primitive it +; needs for a later long-branch expansion. + +.setcpu "6502" +.include "../common/codegen.inc" + +.export cg_init, cg_put, cg_here, cg_branch_fwd, cg_patch_here +.export cg_lda_imm, cg_ldx_imm, cg_ora_imm, cg_lda_abs, cg_ldx_abs +.export cg_sta_abs, cg_stx_abs, cg_adc_abs, cg_sbc_abs, cg_cmp_abs +.export cg_jsr_abs, cg_clc, cg_sec, cg_rts +.export cg_code, cg_len, cg_err + +.segment "CODE" + +cg_init: + lda #$00 + sta cg_len + sta cg_err + sta cg_fix_count + clc + rts + +cg_here: + lda cg_len + rts + +; A = byte to append. C=1 on overflow. +cg_put: + ldy cg_len + cpy #CG_MAX + bcs @full + sta cg_code,y + inc cg_len + clc + rts +@full: lda #CG_ERR_FULL + sta cg_err + sec + rts + +; Immediate forms. +cg_lda_imm: + pha + lda #$A9 + jsr cg_put + bcs @out + pla + jmp cg_put +@out: pla + sec + rts + +cg_ldx_imm: + pha + lda #$A2 + jsr cg_put + bcs @out + pla + jmp cg_put +@out: pla + sec + rts + +cg_ora_imm: + pha + lda #$09 + jsr cg_put + bcs @out + pla + jmp cg_put +@out: pla + sec + rts + +; Absolute-address forms. Each wrapper supplies the opcode in Y; the shared +; routine stores the address before Y is reused by cg_put. +cg_lda_abs: + ldy #$AD + jmp cg_emit_addr +cg_ldx_abs: + ldy #$AE + jmp cg_emit_addr +cg_sta_abs: + ldy #$8D + jmp cg_emit_addr +cg_stx_abs: + ldy #$8E + jmp cg_emit_addr +cg_adc_abs: + ldy #$6D + jmp cg_emit_addr +cg_sbc_abs: + ldy #$ED + jmp cg_emit_addr +cg_cmp_abs: + ldy #$CD + jmp cg_emit_addr +cg_jsr_abs: + ldy #$20 + jmp cg_emit_addr + +cg_emit_addr: + sta cg_addr + stx cg_addr+1 + tya + jsr cg_put + bcs @out + lda cg_addr + jsr cg_put + bcs @out + lda cg_addr+1 + jmp cg_put +@out: sec + rts + +cg_clc: + lda #$18 + jmp cg_put +cg_sec: + lda #$38 + jmp cg_put +cg_rts: + lda #$60 + jmp cg_put + +; A = branch opcode; forward target must be patched later. +cg_branch_fwd: + sta cg_op + lda cg_fix_count + cmp #CG_FIX_MAX + bcs @fix_full + sta cg_idx + lda cg_op + jsr cg_put + bcs @out + lda cg_len + sta cg_pos + ldy cg_idx + sta cg_fixpos,y + inc cg_fix_count + lda #$00 + jsr cg_put + bcs @out + lda cg_idx + clc + rts +@fix_full: + lda #CG_ERR_FIX + sta cg_err + sec + rts +@out: sec + rts + +; A = fixup index. Patch its placeholder so the branch targets cg_len. +cg_patch_here: + cmp cg_fix_count + bcs @bad + tax + lda cg_len + sec + sbc cg_fixpos,x + sec + sbc #1 + cmp #128 + bcs @range + ldy cg_fixpos,x + sta cg_code,y + clc + rts +@bad: lda #CG_ERR_FIX + sta cg_err + sec + rts +@range: lda #CG_ERR_RANGE + sta cg_err + sec + rts + +.segment "BSS" +cg_code: .res 256 +cg_len: .res 1 +cg_err: .res 1 +cg_fix_count: .res 1 +cg_fixpos: .res CG_FIX_MAX +cg_addr: .res 2 +cg_op: .res 1 +cg_idx: .res 1 +cg_pos: .res 1 diff --git a/newLang/src/runtime/arithmetic.s b/newLang/src/runtime/arithmetic.s new file mode 100644 index 0000000..a5bd6fd --- /dev/null +++ b/newLang/src/runtime/arithmetic.s @@ -0,0 +1,169 @@ +; arithmetic.s - provisional 16-bit scalar arithmetic (E04-S03). +; +; Operands: ar_a (left/dividend), ar_b (right/divisor), little-endian words. +; Results: A=low, X=high; division remainder in ar_rem. +; Multiplication wraps to 16 bits. Unsigned and signed division truncate toward +; zero. Division by zero sets ar_fault=AR_FAULT_DIV0, returns A=X=0 and C=1. +; +; The helpers modify ar_a and ar_b while shifting; callers must reload +; operands for a second operation. The signed remainder is left as a magnitude +; for now; quotient behavior is the tested contract. + +.setcpu "6502" +.include "../common/arithmetic.inc" + +.export ar_mul_u16, ar_div_u16, ar_div_i16 +.export ar_a, ar_b, ar_rem, ar_fault + +.segment "CODE" + +; ar_a * ar_b -> A/X (low 16 bits). +ar_mul_u16: + lda #AR_FAULT_NONE + sta ar_fault + lda #$00 + sta ar_res + sta ar_res+1 + ldx #16 +@loop: lda ar_b + and #$01 + beq @noadd + clc + lda ar_res + adc ar_a + sta ar_res + lda ar_res+1 + adc ar_a+1 + sta ar_res+1 +@noadd: + asl ar_a + rol ar_a+1 + lsr ar_b+1 + ror ar_b + dex + bne @loop + lda ar_res + ldx ar_res+1 + rts + +; ar_a / ar_b -> A/X, remainder ar_rem. C=1 on divide by zero. +ar_div_u16: + lda ar_b + ora ar_b+1 + bne @nonzero + lda #AR_FAULT_DIV0 + sta ar_fault + lda #$00 + sta ar_rem + sta ar_rem+1 + sta ar_res + sta ar_res+1 + ldx #$00 + sec + rts + +@nonzero: + lda #$00 + sta ar_rem + sta ar_rem+1 + sta ar_res + sta ar_res+1 + ldx #16 +@loop: + ; rem = (rem << 1) | (dividend >> 15) + asl ar_rem + rol ar_rem+1 + asl ar_a + rol ar_a+1 + bcc @nobit + inc ar_rem +@nobit: + ; quotient <<= 1 + asl ar_res + rol ar_res+1 + ; if rem >= divisor, subtract and set quotient bit 0 + lda ar_rem+1 + cmp ar_b+1 + bcc @next + bne @sub + lda ar_rem + cmp ar_b + bcc @next +@sub: sec + lda ar_rem + sbc ar_b + sta ar_rem + lda ar_rem+1 + sbc ar_b+1 + sta ar_rem+1 + inc ar_res +@next: dex + bne @loop + lda #AR_FAULT_NONE + sta ar_fault + lda ar_res + ldx ar_res+1 + clc + rts + +; Signed ar_a / ar_b -> A/X, quotient truncated toward zero. +ar_div_i16: + lda ar_b + ora ar_b+1 + bne @nonzero + jmp ar_div_u16 +@nonzero: + lda ar_a+1 + eor ar_b+1 + and #$80 + sta ar_sign + jsr ar_abs_a + jsr ar_abs_b + jsr ar_div_u16 + sta ar_res + stx ar_res+1 + lda ar_sign + bpl @done + sec + lda #$00 + sbc ar_res + sta ar_res + lda #$00 + sbc ar_res+1 + sta ar_res+1 +@done: lda ar_res + ldx ar_res+1 + clc + rts + +ar_abs_a: + lda ar_a+1 + bpl @done + sec + lda #$00 + sbc ar_a + sta ar_a + lda #$00 + sbc ar_a+1 + sta ar_a+1 +@done: rts + +ar_abs_b: + lda ar_b+1 + bpl @done + sec + lda #$00 + sbc ar_b + sta ar_b + lda #$00 + sbc ar_b+1 + sta ar_b+1 +@done: rts + +.segment "BSS" +ar_a: .res 2 +ar_b: .res 2 +ar_rem: .res 2 +ar_res: .res 2 +ar_fault: .res 1 +ar_sign: .res 1 diff --git a/newLang/tests/codegen/scalar-test.s b/newLang/tests/codegen/scalar-test.s new file mode 100644 index 0000000..be2f16a --- /dev/null +++ b/newLang/tests/codegen/scalar-test.s @@ -0,0 +1,743 @@ +; E04-S03: scalar emitter and arithmetic fixture in x128. +; +; The fixture drives codegen.s directly (parser integration is E04-S04), +; executes the emitted shapes, copies the golden bytes to golden_copy, then +; tests the arithmetic helpers. It runs in the compiler/BASIC mapping; all +; variables and the code buffer are below $A000. +; +; Mailbox data: +; +0 byte assignment (5A) +; +1 comparison bit mask (EQ..GE, expected 0E with 5,7) +; +2/+3 word add ($2221) +; +4/+5 integer sub ($ECBB) +; +6 multiply result low ($2EE0 -> E0) +; +7 arithmetic fault (1 = divide by zero) +; Failure bits: 1 emitter/assignment, 2 add/sub, 4 comparisons, +; 8 short-circuit, 16 multiply, 32 unsigned divide, 64 signed divide, +; 128 divide-by-zero. + +.setcpu "6502" +.include "../../src/common/c128.inc" +.include "../../src/common/mailbox.inc" +.include "../../src/common/codegen.inc" +.include "../../src/common/arithmetic.inc" + +.import cg_init, cg_len, cg_err, cg_code, cg_here, cg_patch_here +.import cg_lda_imm, cg_ldx_imm, cg_ora_imm +.import cg_lda_abs, cg_ldx_abs, cg_sta_abs, cg_stx_abs +.import cg_adc_abs, cg_sbc_abs, cg_cmp_abs, cg_jsr_abs +.import cg_clc, cg_sec, cg_rts, cg_branch_fwd +.import ar_mul_u16, ar_div_u16, ar_div_i16, ar_a, ar_b, ar_rem, ar_fault + +; Fixed bank-0 scratch variables (SCRATCH is unused by this fixture). +V_SRC = $1B00 +V_DST = $1B01 +V_WIDE = $1B02 +V_ADD_A = $1B04 +V_ADD_B = $1B06 +V_ADD_R = $1B08 +V_SUB_A = $1B0A +V_SUB_B = $1B0C +V_SUB_R = $1B0E +V_BYTE_A = $1B10 +V_BYTE_B = $1B11 +V_CMP = $1B12 +V_FLAG = $1B13 +V_EVAL = $1B14 +V_MUL_A = $1B16 +V_MUL_B = $1B18 +V_MUL_R = $1B1A +V_DIV_A = $1B1C +V_DIV_B = $1B1E +V_DIV_R = $1B20 +V_SDIV_A = $1B22 +V_SDIV_B = $1B24 +V_SDIV_R = $1B26 + +.macro EMIT_LDA addr + lda #<(addr) + ldx #>(addr) + jsr cg_lda_abs +.endmacro + +.macro EMIT_LDX addr + lda #<(addr) + ldx #>(addr) + jsr cg_ldx_abs +.endmacro + +.macro EMIT_STA addr + lda #<(addr) + ldx #>(addr) + jsr cg_sta_abs +.endmacro + +.macro EMIT_STX addr + lda #<(addr) + ldx #>(addr) + jsr cg_stx_abs +.endmacro + +.macro EMIT_ADC addr + lda #<(addr) + ldx #>(addr) + jsr cg_adc_abs +.endmacro + +.macro EMIT_SBC addr + lda #<(addr) + ldx #>(addr) + jsr cg_sbc_abs +.endmacro + +.macro EMIT_CMP addr + lda #<(addr) + ldx #>(addr) + jsr cg_cmp_abs +.endmacro + +.macro EMIT_JSR addr + lda #<(addr) + ldx #>(addr) + jsr cg_jsr_abs +.endmacro + +.macro EMIT_LDA_IMM value + lda #value + jsr cg_lda_imm +.endmacro + +.macro EMIT_ORA_IMM value + lda #value + jsr cg_ora_imm +.endmacro + +.macro EMIT_BIT value + EMIT_LDA V_CMP + EMIT_ORA_IMM value + EMIT_STA V_CMP +.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_PROGRAM + sta MB_PRODUCER + lda #MB_ST_RUNNING + sta MB_STATUS + lda #$00 + sta fail_mask + sta MB_CODE + sta MB_CODE+1 + + jsr init_vars + jsr cg_init + + ; ---------------------------------------------------- golden shape 1: byte assignment + EMIT_LDA V_SRC + EMIT_STA V_DST + jsr cg_rts + lda cg_len + sta s2 + + ; ---------------------------------------------------- shape 2: byte -> word widening + EMIT_LDA V_SRC + EMIT_STA V_WIDE + EMIT_LDA_IMM $00 + EMIT_STA V_WIDE+1 + jsr cg_rts + lda cg_len + sta s3 + + ; ---------------------------------------------------- shape 3: word addition + EMIT_LDA V_ADD_A + jsr cg_clc + EMIT_ADC V_ADD_B + EMIT_STA V_ADD_R + EMIT_LDA V_ADD_A+1 + EMIT_ADC V_ADD_B+1 + EMIT_STA V_ADD_R+1 + jsr cg_rts + lda cg_len + sta s4 + + ; ---------------------------------------------------- shape 4: integer subtraction + EMIT_LDA V_SUB_A + jsr cg_sec + EMIT_SBC V_SUB_B + EMIT_STA V_SUB_R + EMIT_LDA V_SUB_A+1 + EMIT_SBC V_SUB_B+1 + EMIT_STA V_SUB_R+1 + jsr cg_rts + lda cg_len + sta s5 + + ; ---------------------------------------------------- shape 5: six comparisons + EMIT_LDA_IMM $00 + EMIT_STA V_CMP + + ; EQ bit 1 + EMIT_LDA V_BYTE_A + EMIT_CMP V_BYTE_B + lda #$D0 ; BNE skip + jsr cg_branch_fwd + sta fix_eq + EMIT_BIT $01 + lda fix_eq + jsr cg_patch_here + + ; NE bit 2 + EMIT_LDA V_BYTE_A + EMIT_CMP V_BYTE_B + lda #$F0 ; BEQ skip + jsr cg_branch_fwd + sta fix_ne + EMIT_BIT $02 + lda fix_ne + jsr cg_patch_here + + ; LT bit 4 + EMIT_LDA V_BYTE_A + EMIT_CMP V_BYTE_B + lda #$B0 ; BCS skip + jsr cg_branch_fwd + sta fix_lt + EMIT_BIT $04 + lda fix_lt + jsr cg_patch_here + + ; LE bit 8: equal or below goes to set, above skips + EMIT_LDA V_BYTE_A + EMIT_CMP V_BYTE_B + lda #$F0 ; BEQ set + jsr cg_branch_fwd + sta fix_le1 + lda #$90 ; BCC set + jsr cg_branch_fwd + sta fix_le2 + lda #$B0 ; BCS skip + jsr cg_branch_fwd + sta fix_le3 + lda fix_le1 + jsr cg_patch_here + lda fix_le2 + jsr cg_patch_here + EMIT_BIT $08 + lda fix_le3 + jsr cg_patch_here + + ; GT bit 16: equal or below skips + EMIT_LDA V_BYTE_A + EMIT_CMP V_BYTE_B + lda #$F0 ; BEQ skip + jsr cg_branch_fwd + sta fix_gt1 + lda #$90 ; BCC skip + jsr cg_branch_fwd + sta fix_gt2 + EMIT_BIT $10 + lda fix_gt1 + jsr cg_patch_here + lda fix_gt2 + jsr cg_patch_here + + ; GE bit 32 + EMIT_LDA V_BYTE_A + EMIT_CMP V_BYTE_B + lda #$90 ; BCC skip + jsr cg_branch_fwd + sta fix_ge + EMIT_BIT $20 + lda fix_ge + jsr cg_patch_here + jsr cg_rts + lda cg_len + sta s6 + + ; ---------------------------------------------------- shape 6: short-circuit AND + EMIT_LDA_IMM $00 + EMIT_STA V_EVAL + EMIT_LDA V_BYTE_A + lda #$F0 ; BEQ false + jsr cg_branch_fwd + sta fix_and1 + EMIT_LDA_IMM $01 + EMIT_STA V_EVAL + EMIT_LDA V_BYTE_B + lda #$F0 ; BEQ false + jsr cg_branch_fwd + sta fix_and2 + EMIT_LDA_IMM $01 + EMIT_STA V_FLAG + jsr cg_rts + lda fix_and1 + jsr cg_patch_here + lda fix_and2 + jsr cg_patch_here + EMIT_LDA_IMM $00 + EMIT_STA V_FLAG + jsr cg_rts + lda cg_len + sta s7 + + ; ---------------------------------------------------- shape 7: short-circuit OR + EMIT_LDA_IMM $00 + EMIT_STA V_EVAL + EMIT_LDA V_BYTE_A + lda #$D0 ; BNE true + jsr cg_branch_fwd + sta fix_or1 + EMIT_LDA_IMM $01 + EMIT_STA V_EVAL + EMIT_LDA V_BYTE_B + lda #$D0 ; BNE true + jsr cg_branch_fwd + sta fix_or2 + EMIT_LDA_IMM $00 + EMIT_STA V_FLAG + jsr cg_rts + lda fix_or1 + jsr cg_patch_here + lda fix_or2 + jsr cg_patch_here + EMIT_LDA_IMM $01 + EMIT_STA V_FLAG + jsr cg_rts + + lda cg_len + sta golden_len + jsr copy_golden + lda cg_err + beq @golden_ok + lda #$01 + jsr fail + jmp @finish +@golden_ok: + + ; ---------------------------------------------------- run golden shapes + lda #$00 + jsr run_code + lda V_DST + cmp #$5A + beq @s2 + lda #$01 + jsr fail +@s2: lda s2 + jsr run_code + lda V_WIDE + cmp #$5A + bne @s2_bad + lda V_WIDE+1 + bne @s2_bad + jmp @s3 +@s2_bad: + lda #$01 + jsr fail + +@s3: lda s3 + jsr run_code + lda V_ADD_R + cmp #$21 + bne @s3_bad + lda V_ADD_R+1 + cmp #$22 + bne @s3_bad + jmp @s4 +@s3_bad: + lda #$02 + jsr fail + +@s4: lda s4 + jsr run_code + lda V_SUB_R + cmp #$BB + bne @s4_bad + lda V_SUB_R+1 + cmp #$EC + bne @s4_bad + jmp @s5 +@s4_bad: + lda #$02 + jsr fail + +@s5: lda s5 + jsr run_code + lda V_CMP + cmp #$0E + beq @s6 + lda #$04 + jsr fail + +@s6: lda #$00 + sta V_BYTE_A + lda #$01 + sta V_BYTE_B + lda s6 + jsr run_code + lda V_FLAG + bne @s6_bad + lda V_EVAL + bne @s6_bad + jmp @s7 +@s6_bad: + lda #$08 + jsr fail + +@s7: lda #$01 + sta V_BYTE_A + lda #$00 + sta V_BYTE_B + lda s7 + jsr run_code + lda V_FLAG + cmp #$01 + bne @s7_bad + lda V_EVAL + bne @s7_bad + jmp @arith +@s7_bad: + lda #$08 + jsr fail + + ; ---------------------------------------------------- arithmetic helpers +@arith: + jsr cg_init + EMIT_LDA V_MUL_A + EMIT_STA ar_a + EMIT_LDA V_MUL_A+1 + EMIT_STA ar_a+1 + EMIT_LDA V_MUL_B + EMIT_STA ar_b + EMIT_LDA V_MUL_B+1 + EMIT_STA ar_b+1 + EMIT_JSR ar_mul_u16 + EMIT_STA V_MUL_R + EMIT_STX V_MUL_R+1 + jsr cg_rts + lda cg_err + beq @mul_run + lda #$10 + jsr fail + jmp @div +@mul_run: + lda #$00 + jsr run_code + lda V_MUL_R + cmp #$E0 + bne @mul_bad + lda V_MUL_R+1 + cmp #$2E + bne @mul_bad + jmp @div +@mul_bad: + lda #$10 + jsr fail + +@div: jsr cg_init + EMIT_LDA V_DIV_A + EMIT_STA ar_a + EMIT_LDA V_DIV_A+1 + EMIT_STA ar_a+1 + EMIT_LDA V_DIV_B + EMIT_STA ar_b + EMIT_LDA V_DIV_B+1 + EMIT_STA ar_b+1 + EMIT_JSR ar_div_u16 + EMIT_STA V_DIV_R + EMIT_STX V_DIV_R+1 + jsr cg_rts + lda cg_err + beq @div_run + lda #$20 + jsr fail + jmp @sdiv +@div_run: + lda #$00 + jsr run_code + bcs @div_bad + lda V_DIV_R + cmp #$8E + bne @div_bad + lda V_DIV_R+1 + bne @div_bad + lda ar_rem + cmp #$06 + bne @div_bad + lda ar_rem+1 + bne @div_bad + jmp @sdiv +@div_bad: + lda #$20 + jsr fail + +@sdiv: jsr cg_init + EMIT_LDA V_SDIV_A + EMIT_STA ar_a + EMIT_LDA V_SDIV_A+1 + EMIT_STA ar_a+1 + EMIT_LDA V_SDIV_B + EMIT_STA ar_b + EMIT_LDA V_SDIV_B+1 + EMIT_STA ar_b+1 + EMIT_JSR ar_div_i16 + EMIT_STA V_SDIV_R + EMIT_STX V_SDIV_R+1 + jsr cg_rts + lda cg_err + beq @sdiv_run + lda #$40 + jsr fail + jmp @divzero +@sdiv_run: + lda #$00 + jsr run_code + bcs @sdiv_bad + lda V_SDIV_R + cmp #$F2 + bne @sdiv_bad + lda V_SDIV_R+1 + cmp #$FF + bne @sdiv_bad + jmp @divzero +@sdiv_bad: + lda #$40 + jsr fail + +@divzero: + lda #$00 + sta V_DIV_B + sta V_DIV_B+1 + jsr cg_init + EMIT_LDA V_DIV_A + EMIT_STA ar_a + EMIT_LDA V_DIV_A+1 + EMIT_STA ar_a+1 + EMIT_LDA V_DIV_B + EMIT_STA ar_b + EMIT_LDA V_DIV_B+1 + EMIT_STA ar_b+1 + EMIT_JSR ar_div_u16 + EMIT_STA V_DIV_R + EMIT_STX V_DIV_R+1 + jsr cg_rts + lda cg_err + beq @dz_run + lda #$80 + jsr fail + jmp @finish +@dz_run: + lda #$00 + jsr run_code + bcc @dz_bad + lda ar_fault + cmp #AR_FAULT_DIV0 + bne @dz_bad + lda V_DIV_R + ora V_DIV_R+1 + bne @dz_bad + jmp @finish +@dz_bad: + lda #$80 + jsr fail + + ; ---------------------------------------------------- report +@finish: + lda V_DST + sta MB_DATA+0 + lda V_CMP + sta MB_DATA+1 + lda V_ADD_R + sta MB_DATA+2 + lda V_ADD_R+1 + sta MB_DATA+3 + lda V_SUB_R + sta MB_DATA+4 + lda V_SUB_R+1 + sta MB_DATA+5 + lda V_MUL_R + sta MB_DATA+6 + lda ar_fault + sta MB_DATA+7 + 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: plp + rts + +; A = code-buffer offset. Patch a JSR operand and run the snippet. +run_code: + clc + adc #cg_code + adc #0 + sta @target+2 +@target: + jsr $FFFF + rts + +copy_golden: + ldx #$00 +@copy: cpx golden_len + beq @done + lda cg_code,x + sta golden_copy,x + inx + bne @copy +@done: rts + +init_vars: + lda #$5A + sta V_SRC + sta V_DST + lda #$00 + sta V_WIDE + sta V_WIDE+1 + sta V_CMP + sta V_FLAG + sta V_EVAL + sta ar_fault + + lda #$34 + sta V_ADD_A + lda #$12 + sta V_ADD_A+1 + lda #$ED + sta V_ADD_B + lda #$0F + sta V_ADD_B+1 + lda #$00 + sta V_ADD_R + sta V_ADD_R+1 + + lda #$00 + sta V_SUB_A + lda #$10 + sta V_SUB_A+1 + lda #$45 + sta V_SUB_B + lda #$23 + sta V_SUB_B+1 + lda #$00 + sta V_SUB_R + sta V_SUB_R+1 + + lda #$05 + sta V_BYTE_A + lda #$07 + sta V_BYTE_B + + lda #$2C + sta V_MUL_A + lda #$01 + sta V_MUL_A+1 + lda #$28 + sta V_MUL_B + lda #$00 + sta V_MUL_B+1 + lda #$00 + sta V_MUL_R + sta V_MUL_R+1 + + lda #$E8 + sta V_DIV_A + lda #$03 + sta V_DIV_A+1 + lda #$07 + sta V_DIV_B + lda #$00 + sta V_DIV_B+1 + lda #$00 + sta V_DIV_R + sta V_DIV_R+1 + + lda #$9C + sta V_SDIV_A + lda #$FF + sta V_SDIV_A+1 + lda #$07 + sta V_SDIV_B + lda #$00 + sta V_SDIV_B+1 + lda #$00 + sta V_SDIV_R + sta V_SDIV_R+1 + rts + +.segment "CODE" +fail: + ora fail_mask + sta fail_mask + rts + +.segment "RODATA" +message: .byte "NOVA128 SCALAR TEST DONE", $0D, $00 + +.segment "BSS" +fail_mask: .res 1 +print_y: .res 1 +s2: .res 1 +s3: .res 1 +s4: .res 1 +s5: .res 1 +s6: .res 1 +s7: .res 1 +fix_eq: .res 1 +fix_ne: .res 1 +fix_lt: .res 1 +fix_le1: .res 1 +fix_le2: .res 1 +fix_le3: .res 1 +fix_gt1: .res 1 +fix_gt2: .res 1 +fix_ge: .res 1 +fix_and1: .res 1 +fix_and2: .res 1 +fix_or1: .res 1 +fix_or2: .res 1 +.export golden_len +.export golden_copy +golden_len: .res 1 +golden_copy: .res 256 diff --git a/newLang/tests/codegen/scalar.expected.dis b/newLang/tests/codegen/scalar.expected.dis new file mode 100644 index 0000000..408d455 --- /dev/null +++ b/newLang/tests/codegen/scalar.expected.dis @@ -0,0 +1,95 @@ + .setcpu "6502" + lda $1B00 + sta $1B01 + rts + lda $1B00 + sta $1B02 + lda #$00 + sta $1B03 + rts + lda $1B04 + clc + adc $1B06 + sta $1B08 + lda $1B05 + adc $1B07 + sta $1B09 + rts + lda $1B0A + sec + sbc $1B0C + sta $1B0E + lda $1B0B + sbc $1B0D + sta $1B0F + rts + lda #$00 + sta $1B12 + lda $1B10 + cmp $1B11 + bne L0050 + lda $1B12 + ora #$01 + sta $1B12 +L0050: lda $1B10 + cmp $1B11 + beq L0060 + lda $1B12 + ora #$02 + sta $1B12 +L0060: lda $1B10 + cmp $1B11 + bcs L0070 + lda $1B12 + ora #$04 + sta $1B12 +L0070: lda $1B10 + cmp $1B11 + beq L007C + bcc L007C + bcs L0084 +L007C: lda $1B12 + ora #$08 + sta $1B12 +L0084: lda $1B10 + cmp $1B11 + beq L0096 + bcc L0096 + lda $1B12 + ora #$10 + sta $1B12 +L0096: lda $1B10 + cmp $1B11 + bcc L00A6 + lda $1B12 + ora #$20 + sta $1B12 +L00A6: rts + lda #$00 + sta $1B14 + lda $1B10 + beq L00C1 + lda #$01 + sta $1B14 + lda $1B11 + beq L00C1 + lda #$01 + sta $1B13 + rts +L00C1: lda #$00 + sta $1B13 + rts + lda #$00 + sta $1B14 + lda $1B10 + bne L00E1 + lda #$01 + sta $1B14 + lda $1B11 + bne L00E1 + lda #$00 + sta $1B13 + rts +L00E1: lda #$01 + sta $1B13 + rts diff --git a/newLang/tests/codegen/scalar.expected.hex b/newLang/tests/codegen/scalar.expected.hex new file mode 100644 index 0000000..0e58fe6 --- /dev/null +++ b/newLang/tests/codegen/scalar.expected.hex @@ -0,0 +1 @@ +AD 00 1B 8D 01 1B 60 AD 00 1B 8D 02 1B A9 00 8D 03 1B 60 AD 04 1B 18 6D 06 1B 8D 08 1B AD 05 1B 6D 07 1B 8D 09 1B 60 AD 0A 1B 38 ED 0C 1B 8D 0E 1B AD 0B 1B ED 0D 1B 8D 0F 1B 60 A9 00 8D 12 1B AD 10 1B CD 11 1B D0 08 AD 12 1B 09 01 8D 12 1B AD 10 1B CD 11 1B F0 08 AD 12 1B 09 02 8D 12 1B AD 10 1B CD 11 1B B0 08 AD 12 1B 09 04 8D 12 1B AD 10 1B CD 11 1B F0 04 90 02 B0 08 AD 12 1B 09 08 8D 12 1B AD 10 1B CD 11 1B F0 0A 90 08 AD 12 1B 09 10 8D 12 1B AD 10 1B CD 11 1B 90 08 AD 12 1B 09 20 8D 12 1B 60 A9 00 8D 14 1B AD 10 1B F0 10 A9 01 8D 14 1B AD 11 1B F0 06 A9 01 8D 13 1B 60 A9 00 8D 13 1B 60 A9 00 8D 14 1B AD 10 1B D0 10 A9 01 8D 14 1B AD 11 1B D0 06 A9 00 8D 13 1B 60 A9 01 8D 13 1B 60