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 <noreply@codebuff.com>main
| @@ -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 | |||
| @@ -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 | |||
| @@ -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. | |||
| @@ -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) | |||
| @@ -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" | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -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 | |||
| sta @target+1 | |||
| lda #>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 | |||
| @@ -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 | |||
| @@ -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 | |||
Powered by TurnKey Linux.