Ver código fonte

Add parser statement emission and the Power C reference set (E04-S06).

Statement emission now runs from parser pass 2: assignment, byte + and -, and
print reach codegen.s through emit.s, and the statements fixture executes the
emitted bytes in x128. Restoring the parser trace fixture's length arithmetic
(it emitted between the two halves of the subtraction, which clears carry)
brings the 41-fixture parser verification back to green after emit.s joined
the link.

Also check in the vim clone's improved C128 build and the Power C
reverse-engineering notes and tools.

Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
main
Daniel Covington 4 dias atrás
pai
commit
30f37a0e09
59 arquivos alterados com 4470 adições e 32 exclusões
  1. +31
    -0
      newLang/docs/architecture.md
  2. +1
    -0
      newLang/docs/decisions.md
  3. +17
    -0
      newLang/docs/memory-map.md
  4. +114
    -0
      newLang/planning/claims/E04-S06.md
  5. +2
    -0
      newLang/scripts/build-hello-test.ps1
  6. +2
    -0
      newLang/scripts/build-parser-test.ps1
  7. +34
    -0
      newLang/scripts/build-statements-test.ps1
  8. +55
    -0
      newLang/scripts/verify-statements-test.ps1
  9. +21
    -0
      newLang/src/common/emit.inc
  10. +21
    -0
      newLang/src/compiler/codegen.s
  11. +447
    -0
      newLang/src/compiler/emit.s
  12. +113
    -8
      newLang/src/compiler/parser.s
  13. +227
    -0
      newLang/tests/compiler/statements-test.s
  14. +10
    -0
      newLang/tests/compiler/statements.n8
  15. +4
    -2
      newLang/tests/parser/parser-test.s
  16. +11
    -0
      reference/powerc-re/.gitignore
  17. +1
    -0
      reference/powerc-re/POWERc.d81.sha256
  18. +112
    -0
      reference/powerc-re/README.md
  19. BIN
      reference/powerc-re/cc.sh.prg
  20. BIN
      reference/powerc-re/compiler.prg
  21. BIN
      reference/powerc-re/link.sh.prg
  22. BIN
      reference/powerc-re/shell.prg
  23. +61
    -0
      reference/powerc-re/src/cmulti.s
  24. +81
    -0
      reference/powerc-re/src/div_patch.py
  25. +48
    -0
      reference/powerc-re/tests/bench.c
  26. +80
    -0
      reference/powerc-re/tests/sem.c
  27. +7
    -0
      reference/powerc-re/tests/t1.c
  28. +11
    -0
      reference/powerc-re/tests/t2.c
  29. +15
    -0
      reference/powerc-re/tests/t3.c
  30. +5
    -0
      reference/powerc-re/tests/t4.c
  31. +42
    -0
      reference/powerc-re/tests/t5.c
  32. +13
    -0
      reference/powerc-re/tests/uge.c
  33. +49
    -0
      reference/powerc-re/tools/classify.py
  34. +204
    -0
      reference/powerc-re/tools/cpu6502.py
  35. +25
    -0
      reference/powerc-re/tools/dis.py
  36. +10
    -0
      reference/powerc-re/tools/dump.py
  37. +55
    -0
      reference/powerc-re/tools/helpertest.py
  38. +57
    -0
      reference/powerc-re/tools/objfmt.py
  39. +61
    -0
      reference/powerc-re/tools/pc-compile.ps1
  40. +47
    -0
      reference/powerc-re/tools/pc-run.ps1
  41. +161
    -0
      reference/powerc-re/tools/pcasm.py
  42. +41
    -0
      reference/powerc-re/tools/pcbuild.ps1
  43. +915
    -0
      reference/powerc-re/tools/pcopt.py
  44. +29
    -0
      reference/powerc-re/tools/regress.py
  45. +75
    -0
      reference/powerc-re/tools/setup.py
  46. +39
    -0
      reference/powerc-re/tools/t_c105.py
  47. +36
    -0
      reference/powerc-re/tools/t_div.py
  48. +32
    -0
      reference/powerc-re/tools/t_multi.py
  49. BIN
      reference/powerc-re/translator.prg
  50. +18
    -0
      vim/NOTES.md
  51. +17
    -0
      vim/build-improved.bat
  52. +1
    -1
      vim/build.ps1
  53. +5
    -2
      vim/make-guide.py
  54. +9
    -0
      vim/run-improved.bat
  55. +66
    -0
      vim/vdir.c
  56. BIN
      vim/vim-improved.d81
  57. +17
    -17
      vim/vim-user-guide.pdf
  58. +8
    -2
      vim/vim.c
  59. +907
    -0
      vim/vim128.map

+ 31
- 0
newLang/docs/architecture.md Ver arquivo

@@ -549,6 +549,37 @@ rejected before code generation: the parser reports `ACC_PRIVATE_MEMBER`
(E120) for a private field or method reached outside its class, so this layer
never sees one.

### 7.11 Statement emission (E04-S06, in progress)

`src/compiler/emit.s` is the first emitter driven by the parser itself. Pass 2
parses statements for type checking; when `em_on` is set, the same parse also
emits bytes into the shared `codegen.s` buffer. `parse_file` resets the
emitter and the buffer at the start of pass 2, and pass 1 assigns the same
frame slots without emitting, so the two passes agree by construction.

Parameters take `FP+2` upward in declaration order and locals `FP-1` downward,
with one byte for boolean/byte/char and two for integer/word. A slot is not
addressed with `LDY #offset` / `(FRAME_FP),Y`: indexed-indirect addressing adds
Y to the pointer, so a negative offset would address a page above `FP`.
`em_point_slot` instead emits `EM_LP = FRAME_FP + offset` (low byte plus carry,
high byte plus the offset's sign byte), leaves Y = 0, and the load or store uses
`(EM_LP),Y`. The store keeps its value on the hardware stack across that
sequence.

Byte `+` and `-` push the left operand, evaluate the right, then combine
through `EM_TMP`. `print` emits through `em_pr_text`, `em_pr_byte`, and
`em_pr_cr`, which the caller fills; each call goes through a `JMP (cell)`
trampoline, so the test records what the program asked to print instead of
depending on CHROUT's mapping. A `print` that captured a string literal passes
`gen_text` and its length; otherwise it passes the byte in A.

The `statements.n8` fixture parses two locals, three assignments, an
addition, and two prints, then JSRs the 138 emitted bytes in place. It checks
`a = 10`, `b = 7`, and the recorded stream `SUM`, CR, `$FF`, `$0A`, CR. Control
flow, calls, field stores, word values, arrays, the frame prologue, and
reporting `em_bad` as a diagnostic remain open; E04-S07 owns sizing and pass-3
addresses.

## 8. Heap and Ownership

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


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

@@ -63,6 +63,7 @@ documented default. `Proposed` means a later change still needs approval.
| D135 | E05-S03 construction and deletion live in `src/runtime/construct.s` above the heap and descriptor layers. `obj_new` takes A/X = descriptor, reads the instance size (zero is `OBJ_ERR_BAD`), allocates it, writes the descriptor pointer at object+0, then calls the caller-selected `obj_init_ptr` entry once with A/X = self and the constructor arguments still on the software frame stack at `FP+2`; the caller discards them with `frame_discard`. Allocation failure returns C=1 with `OBJ_ERR_HEAP` and never calls `init`. `obj_delete` takes A/X = object: null is a successful no-op, otherwise the descriptor's optional destructor runs once with A/X = self and the complete block goes back through `heap_free`. Both calls use `obj_call`, which takes its entry from zero-page `co_vec`, preserves A/X for `self`, pushes one synthetic return address, and ends with `JMP (co_vec)`; `co_vec` must not straddle a page. | Gives generated `new`/`delete` a testable runtime contract without freezing heap placement or the E06 dispatch. Carrying the entry in `co_vec` instead of A/X is what lets the callee still receive `self` in A/X, and the same shape serves method-table dispatch later. `construct.s` reports `OBJ_ERR_HEAP` rather than printing; codegen still maps that to R005. Implemented by `construct.s`, the `OBJ_*` codes in `class.inc`, and the object fixture. |
| D134 | E05-S02 class descriptors are 8-byte packed records: 2-byte instance size (including the object header), 2-byte destructor address (0 = none), 1-byte interface count, 2-byte interface-directory pointer (0 when empty), and a reserved flags byte that must be 0. There is no parent-class slot. Objects begin with a 2-byte descriptor pointer and fields follow at +2. Interface-directory entries are 4 bytes: interface-descriptor pointer and method-table pointer. `objects.s` provides read-only accessors; `output.s` writes `CLASS name size destroy count ifdir` records after sections. | Makes object identity, size/deletion, and the future interface path deterministic before construction/dispatch. Flags are reserved for non-inheritance metadata; interface descriptor contents and generated `new` remain E05-S03/E06. Implemented by `class.inc`, `objects.s`, and the class fixture. |
| D136 | E05-S04 member emission lives in `src/compiler/members.s`. A generated method body starts with `mb_self_store`, recording the incoming self in zero-page `MB_SELF` (`$F9-$FA`). `self.field` emits `LDY #offset` plus `LDA (MB_SELF),Y` or `STA (MB_SELF),Y`, with a second byte for word fields; the word store moves the high byte with `TXA` because the 6502 has no `STX (zp),Y`. `mb_point` loads any two-byte object reference into `MB_SELF` for a field or call on another receiver. A concrete call emits `mb_self_load`, `JSR target`, then `frame_discard` for the argument bytes, preserving a byte or word result. `codegen.s` gains `cg_ldy_imm`, the zero-page forms, and the indexed-indirect forms. | Gives generated methods the member half of the D129 ABI without freezing parser integration or interface dispatch. One receiver pointer keeps field access at four bytes per byte field and stays clear of `$FB-$FE` (link-time assertion). Private access never reaches this layer: the parser reports `ACC_PRIVATE_MEMBER` (E120) first. Implemented by `members.s`, `members.inc`, `codegen.s`, and the member fixture. |
| D137 | E04-S06 emission is driven from parser pass 2: with `em_on` set, the parser calls `emit.s` as it type-checks each statement, and `parse_file` resets the emitter (and the code buffer) at the start of pass 2; pass 1 only assigns the same slots. Parameters take `FP+2` upward in declaration order, locals `FP-1` downward, one byte for byte-sized types and two for words. A slot is reached by emitting `EM_LP = FRAME_FP + offset` with the offset's sign byte, because `(FP),Y` cannot address below `FP`; `EM_LP` is `$F5-$F6` and the combine temp `EM_TMP` is `$F7`. Byte `+`/`-` keep the left operand on the hardware stack. Print calls go through the `em_pr_text/byte/cr` cells and three `JMP (cell)` trampolines, so a fixture can record output without CHROUT. Emitter failures set `em_bad` (`EM_ERR_SIZE`, `EM_ERR_TARGET`, `EM_ERR_OP`, `EM_ERR_STATE`) and stop emitting; nothing is written outside the code buffer. | Gives the first parser-to-codegen path a testable shape without freezing pass 3: the fixture executes the emitted bytes in place and checks locals and recorded output. The 255-byte buffer, one captured string literal, and the fixture-owned frame prologue are known limits for E04-S07, which owns sizing, section addresses, and `frame_enter`. Control flow, calls, fields, word values, arrays, and diagnostic reporting remain E04-S06 work. Implemented by `emit.s`, `emit.inc`, the `parser.s` hooks, `codegen.s`, and the statements fixture. |

## Deferred



+ 17
- 0
newLang/docs/memory-map.md Ver arquivo

@@ -57,6 +57,23 @@ install this mapping and call `frame_init` when it starts emitting user code
`FRAME_SP` nor `FRAME_FP`. Field offsets are the parser's layout offsets, with
the first field at +2, so generated code needs no runtime translation.

## Statement Emission (E04-S06)

| Element | Value |
|---|---|
| Slot pointer | `EM_LP` = `$F5-$F6`, set to `FP + signed offset` |
| Combine temp | `EM_TMP` = `$F7` |
| Parameters | `FP+2` upward, declaration order |
| Locals | `FP-1` downward, declaration order |
| Code buffer | `cg_code`, 255 bytes (E04-S03) |
| Print entries | `em_pr_text`/`em_pr_byte`/`em_pr_cr` cells with `JMP (cell)` trampolines |

`EM_LP` and `EM_TMP` sit below `MB_SELF` (`$F9-$FA`) and clear of the frame
ABI at `$FB-$FE`; `emit.s` asserts at link time that `EM_TMP` does not overlap
`MB_SELF`. Slot offsets are 8-bit frame displacements, so a function's locals
and parameters together must stay under 127 bytes until pass-3 sizing assigns
frame sizes.

## Heap Fixture (E05-S01)

| Element | Value |


+ 114
- 0
newLang/planning/claims/E04-S06.md Ver arquivo

@@ -0,0 +1,114 @@
---
story: E04-S06
owner: codebuff
status: in-progress
started: 2026-11-09
depends-on: [E04-S04, E04-S05, E05-S04, E03-S04]
---

# E04-S06 Claim

## Intended change

Give the parser a first real emission path: while pass 2 type-checks a body,
emit code for byte locals and parameters, number and char constants, `+`/`-`,
assignment, and `print`. The emitted bytes run in place in x128, so a fixture
can parse a source file and check both the resulting locals and the bytes the
program asked to print.

## File area

- `src/compiler/emit.s`, `src/common/emit.inc` (new)
- `src/compiler/parser.s` (pass-2 hooks), `src/compiler/codegen.s` (forms)
- `tests/compiler/statements.n8`, `tests/compiler/statements-test.s`
- `scripts/build-statements-test.ps1`, `scripts/verify-statements-test.ps1`
- parser-linking build scripts (link `codegen.s` and `emit.s`)
- `docs/decisions.md`, `docs/architecture.md`, `docs/memory-map.md`

## Acceptance evidence

- [x] Partly: statements emit through `codegen.s` — assignment, `+`/`-`, and
`print`. `if`/`else`, `while`, `for`, and `return` do not emit yet.
- [x] Partly: temporaries follow D129 for byte values (left operand on the
hardware stack, combine through `EM_TMP`); results are in A. Parameters
get `FP+2` slots in declaration order; argument pushing and discard are
not emitted yet.
- [ ] `self.field` and concrete calls through `members.s` — not emitted yet.
`members.s` (E05-S04) and the codegen forms it needs are in place.
- [x] Partly: emitter failures set `em_bad` (`EM_ERR_SIZE`, `EM_ERR_TARGET`,
`EM_ERR_OP`, `EM_ERR_STATE`) and `cg_err` keeps the buffer detail; a
failed emit writes nothing outside `cg_code`. No diagnostic is reported
through `diag_report` yet.
- [x] Partly: a source program with statements and expressions runs in x128
with an expected signature (see below). The concrete call in the
criterion is not part of it yet.

## Work log

- `pwsh -File scripts/verify-statements-test.ps1` (final): PASS — 138 bytes
emitted, `a=10`, `b=7`, `cg_err=0`, no diagnostics, recorded output
`SUM 0D FF 0A 0D`.
- `verify-member-test`, `verify-control-test`, `verify-scalar-test`: PASS
unchanged.
- Bugs found while bringing the path up: `(FRAME_FP),Y` cannot address `FP-1`
(it adds 255), so slots are reached through `EM_LP = FP + signed offset`;
`em_stmt_begin` must not clear the target `em_target` captured; the store
must keep A across the pointer setup (`PHA`/`PLA`); `cg_len` was
uninitialized and pass 1 also emitted, so `em_reset` now calls `cg_init` and
assignment emission is gated on pass 2.
- Parser pass-2 growth pushed `beq @group` out of range; it now uses the
branch-over-`JMP` form.
- `build-hello-test.ps1` and `build-parser-test.ps1` were rewritten with the
`codegen.s`/`emit.s` entries (a sed pass had corrupted their path strings).
- `verify-hello-test`: PASS — diagnostics 0, one 18-byte print, HELLO.PRG 118
bytes, `HELLO FROM NOVA128` on row 13.
- `verify-parser-test` failed at first on the expression-trace fixture
(`prec.n8`, record 28): its `$FB` trace length came back as `$FF92`, so the
host walker ran off the end of the dump. The trace itself was fine — a
captured dump shows the payload starting `FF 06 00 03 07 0B 0F 9A 98`.
- Root cause in the fixture: `dump_trace` emitted the low length byte between
the two `sbc`s, and `emit` clears carry, so the high byte lost its borrow
($97-$05=$92 then $6A-$6A-0 = `$FF` instead of `$00`). Both length bytes
are now computed before either is emitted.
- `verify-parser-test`: PASS — 41 fixtures, `prec.n8` reports 15 traced
statements.

## Completion

- Tests run: statements, member, control, scalar, hello, and parser fixtures
all pass.
- Results: statement emission runs and is deterministic; 138 bytes for the
fixture source.
- Files changed: as listed above.
- Remaining risks: the code buffer is still 255 bytes; the print path reads
the single `gen_text` capture, so two string literals in one program do not
work; the frame prologue is the fixture's, not emitted; nested expressions
mix one `EM_TMP`, so deeper shapes need a spill rule; no field stores, no
calls, no word values, no arrays.

## Next stepControl flow through `flow.s`, then calls and `self.field` through
`members.s`, then report `em_bad` as a diagnostic.

Design notes for the control-flow step, worked out while reading `flow.s`:

- `flow.s` already writes into the shared code buffer, so `em_reset` can call
`fl_init(FL_MODE_EMIT)` instead of `cg_init` and reuse its labels, fixed
five-byte branches, and `JMP` fixups for `if`/`while`. `em_end_func` then
calls `fl_finish` before the final `RTS`.
- Label ids cannot be indexed by block depth alone: a second `if` at the same
depth would redefine the label and `fl_label` rejects that. `flow.s` needs
`fl_release` (resolve and patch the pending fixups that name a label, then
clear its definition) so each closed block hands its two ids back. With two
ids per block and `PARSE_MAX_DEPTH = 16`, `FL_LABELS` has to grow from 16 to
at least 33.
- Conditions should not be materialised as 0/1 in A. `em_bin` can emit `CMP`
and remember the comparison in `em_flags`; `em_cond_end` then emits the
branch straight from those flags (`>` needs two false branches, `BCC` then
`BEQ`), which is also what `while` needs at the top of the loop.
- `return` is a plain `RTS` while no frame prologue is emitted (E04-S07 owns
`frame_leave`); with no prologue an inline `RTS` is the correct exit.
- `for` is not covered by the above: the spec evaluates the end and step
expressions once before the first iteration, and the comparison direction
depends on the step's runtime sign, so it needs two per-depth temporaries
plus a runtime sign test before the loop condition. E04-S07 then owns
`sizing.s`, pass-3 addresses, and the emitted `frame_enter`.

+ 2
- 0
newLang/scripts/build-hello-test.ps1 Ver arquivo

@@ -17,6 +17,8 @@ $link = Invoke-Nova128Link -Name "hello-test" -BuildDir $out `
(Join-Path $project "src\compiler\names.s"),
(Join-Path $project "src\compiler\diagnostics.s"),
(Join-Path $project "src\compiler\parser.s"),
(Join-Path $project "src\compiler\codegen.s"),
(Join-Path $project "src\compiler\emit.s"),
(Join-Path $project "src\compiler\interfaces.s"),
(Join-Path $project "src\compiler\types.s"),
(Join-Path $project "src\compiler\output.s"),


+ 2
- 0
newLang/scripts/build-parser-test.ps1 Ver arquivo

@@ -66,6 +66,8 @@ $link = Invoke-Nova128Link -Name "parser-test" -BuildDir $out `
(Join-Path $project "src\compiler\names.s"),
(Join-Path $project "src\compiler\diagnostics.s"),
(Join-Path $project "src\compiler\parser.s"),
(Join-Path $project "src\compiler\codegen.s"),
(Join-Path $project "src\compiler\emit.s"),
(Join-Path $project "src\compiler\interfaces.s"),
(Join-Path $project "src\compiler\types.s"),
(Join-Path $project "tests\parser\parser-test.s"))


+ 34
- 0
newLang/scripts/build-statements-test.ps1 Ver arquivo

@@ -0,0 +1,34 @@
# E04-S06: build the statement-emission fixture and a D81 holding STMTS.N8.
[CmdletBinding()]
param()

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

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

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

+ 55
- 0
newLang/scripts/verify-statements-test.ps1 Ver arquivo

@@ -0,0 +1,55 @@
# E04-S06: parse STMTS.N8 in x128, run the emitted body, and check the locals
# and the bytes the generated print calls asked for.
[CmdletBinding()]
param([int]$Port = 6560, [int]$TimeoutSeconds = 180)

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

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

$lbl = Get-Content -Raw -LiteralPath (Join-Path $out "statements-test.lbl")
if ($lbl -notmatch "([0-9A-Fa-f]{6})\s+\.rec_buf\b") { throw "rec_buf not in the label file" }
$recBuf = [Convert]::ToInt32($matches[1], 16)

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

if ($mb[2] -ne 0x02) { $failures += ("producer $($mb[2]) expected 2 (compiler)") }
if ($mb[3] -ne 0xA5) {
$failures += ("fixture status {0:X2} failed at stage {1}" -f $mb[3], $mb[4])
}
if ($mb[8] -ne 0) { $failures += "diagnostics present: $($mb[8])" }
if ($mb[9] -ne 0) { $failures += "emitter error $($mb[9])" }
if ($mb[10] -ne 10) { $failures += ("a = {0}, expected 10" -f $mb[10]) }
if ($mb[11] -ne 7) { $failures += ("b = {0}, expected 7" -f $mb[11]) }
if ($mb[12] -lt 60) { $failures += "emitted only $($mb[12]) bytes" }
if ($mb[13] -ne 0) { $failures += "codegen error $($mb[13])" }

# print "sum" records SUM + CR; print a records $FF, $0A, CR.
$expect = @(0x53, 0x55, 0x4D, 0x0D, 0xFF, 0x0A, 0x0D)
if ($mb[14] -ne $expect.Count) {
$failures += ("recorded {0} bytes, expected {1}" -f $mb[14], $expect.Count)
} else {
for ($i = 0; $i -lt $expect.Count; $i++) {
if ($log[$i] -ne $expect[$i]) {
$failures += ("recorded byte {0} is {1:X2}, expected {2:X2}" -f $i, $log[$i], $expect[$i])
}
}
}

if ($failures) { throw ("statements verification failed:`n " + ($failures -join "`n ")) }
Write-Host ("emitted {0} bytes; a=10, b=7; print recorded SUM, 10" -f $mb[12])
Write-Host "verified parser pass-2 statement emission executes in x128"

+ 21
- 0
newLang/src/common/emit.inc Ver arquivo

@@ -0,0 +1,21 @@
; emit.inc - parser-to-codegen statement emission (E04-S06).
;
; The emitter writes into the shared code buffer (codegen.s). Offsets are
; frame displacements relative to FP (frames.inc): locals at FP-1 downward and
; parameters at FP+2 upward.

.ifndef EMIT_INC
EMIT_INC = 1

EM_TMP = $F7 ; two-byte combine temp; below MB_SELF ($F9)
EM_LP = $F5 ; two-byte slot pointer; below MB_SELF

EM_ERR_SIZE = 1 ; a value or slot is not one byte
EM_ERR_TARGET = 2 ; the assignment target is not a scalar local
EM_ERR_OP = 3 ; operator the emitter does not handle yet
EM_ERR_STATE = 4 ; missing runtime entry or bad emitter state

EM_KIND_LOCAL = 0
EM_KIND_PARAM = 1

.endif

+ 21
- 0
newLang/src/compiler/codegen.s Ver arquivo

@@ -17,6 +17,9 @@
; cg_stx_zp A = zero-page address
; cg_lda_zpy A = zero-page pointer address, indexed by Y
; cg_sta_zpy A = zero-page pointer address, indexed by Y
; cg_adc_imm A = value (ADC #value)
; cg_adc_zp A = zero-page address (CLC/ADC (zp))
; cg_sbc_zp A = zero-page address (SEC/SBC (zp))
; (there is no LDX (zp),Y: use LDA/TAX for a high byte)
; cg_lda_abs A/X = address
; cg_ldx_abs A/X = address
@@ -48,6 +51,7 @@
.export cg_set_count, cg_count
.export cg_lda_imm, cg_ldx_imm, cg_ora_imm, cg_ldy_imm, cg_lda_abs, cg_ldx_abs
.export cg_lda_zp, cg_ldx_zp, cg_sta_zp, cg_stx_zp, cg_lda_zpy, cg_sta_zpy
.export cg_adc_zp, cg_sbc_zp, cg_adc_imm
.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
@@ -151,6 +155,12 @@ cg_sta_zp:
cg_stx_zp:
ldy #$86
jmp cg_emit_zp
cg_adc_zp:
ldy #$65
jmp cg_emit_zp
cg_sbc_zp:
ldy #$E5
jmp cg_emit_zp
cg_lda_zpy:
ldy #$B1
jmp cg_emit_zp
@@ -182,6 +192,17 @@ cg_sta_abs:
cg_stx_abs:
ldy #$8E
jmp cg_emit_addr
cg_adc_imm:
pha
lda #$69
jsr cg_put
bcs @out
pla
jmp cg_put
@out: pla
sec
rts

cg_adc_abs:
ldy #$6D
jmp cg_emit_addr


+ 447
- 0
newLang/src/compiler/emit.s Ver arquivo

@@ -0,0 +1,447 @@
; emit.s - statement emission from the parser (E04-S06).
;
; The parser calls emit.s while pass 2 parses a body, so statements emit as
; they are type-checked. Bytes go into the shared code buffer (codegen.s);
; the caller sets em_on and parse_file resets the emitter at the start of
; pass 2.
;
; Covered here: byte locals and parameters, number and char constants, name
; loads, byte `+` and `-`, assignment to a scalar local, print of one captured
; string or a byte, and the end of a function. Frame prologue sizing, control
; flow, calls, field access, word values, and arrays remain.
;
; Locals live at FP-1 downward and parameters at FP+2 upward (frames.inc), so
; a slot is addressed with LDY #offset / LDA (FRAME_FP),Y. Offsets are
; assigned in both passes, so pass 1 and pass 2 agree on every slot.
;
; Print output calls the runtime entry points directly (print.s), which the
; link step supplies.

.setcpu "6502"
.include "../common/tokens.inc"
.include "../common/parser.inc"
.include "../common/codegen.inc"
.include "../common/frames.inc"
.include "../common/emit.inc"
.include "../common/members.inc"

.import cg_init, cg_put, cg_lda_imm, cg_ldx_imm, cg_ldy_imm, cg_lda_zp, cg_lda_zpy, cg_sta_zpy
.import cg_sta_zp, cg_adc_zp, cg_sbc_zp, cg_adc_imm, cg_clc, cg_sec, cg_jsr_abs, cg_rts
.import gen_text, gen_text_len

.export em_on, em_ctx, em_bad, em_last_kind
.export em_reset, em_begin_func, em_begin_params, em_local, em_param
.export em_expr_name, em_expr_const, em_push_left, em_bin
.export em_stmt_begin, em_stmt_end, em_expr_off, em_begin_print
.export em_target, em_store_target, em_print, em_end_func
.export em_pr_text, em_pr_byte, em_pr_cr

.assert EM_TMP + 1 < MB_SELF, error, "EM_TMP overlaps MB_SELF"

.segment "CODE"

; Pass 2 start: empty code buffer, no errors, no function, no slots.
em_reset:
jsr cg_init
lda #$00
sta em_bad
sta em_ctx
sta em_last_kind
sta em_tgt
sta em_size
sta em_left_size
sta em_locals
sta em_par_bytes
lda #$FF
sta em_loc_next
lda #$02
sta em_par_next
rts

; A body starts: fresh local slots. Parameter offsets were assigned while the
; signature was parsed and are kept.
em_begin_func:
lda #$FF
sta em_loc_next
lda #$00
sta em_locals
sta em_ctx
sta em_last_kind
sta em_tgt
rts

; A signature starts: parameters begin at FP+2.
em_begin_params:
lda #$02
sta em_par_next
sta em_par_bytes
rts

; A = slot bytes, X = name ID of a local: assign the next local slot.
em_local:
ldy #EM_KIND_LOCAL
jmp em_decl

; A = slot bytes, X = name ID of a parameter: assign the next parameter slot.
em_param:
ldy #EM_KIND_PARAM
jmp em_decl

; A = slot bytes, X = name ID, Y = EM_KIND_*. A zero size means the type has
; no frame slot here; the name is simply not addressable.
em_decl:
stx em_nid
sta em_size
sty em_kind
lda em_size
beq @done
lda em_kind
bne @param
lda em_loc_next ; locals grow down from FP-1
sec
sbc em_size
sta em_loc_next
ldx em_nid
sta em_off,x
lda em_locals
clc
adc em_size
sta em_locals
jmp @size
@param: ldx em_nid
lda em_par_next ; parameters grow up from FP+2
sta em_off,x
clc
adc em_size
sta em_par_next
sta em_par_bytes
@size: ldx em_nid
lda em_size
sta em_sz,x
@done: rts

; A statement's value starts: operands emit. The target captured by
; em_target just before this call is kept until the statement ends.
em_stmt_begin:
lda #$01
sta em_ctx
sta em_last_kind
rts

; The statement is complete: no more operand emission. The target stays set
; until em_store_target consumes it.
em_stmt_end:
lda #$00
sta em_ctx
rts

em_expr_off:
lda #$00
sta em_ctx
rts

; Remember where the print text buffer stood, so em_print can tell a captured
; string literal from a value.
em_begin_print:
lda gen_text_len
sta em_txt_before
rts

; X = name ID. Record it as the last primary, and load it when emitting.
em_expr_name:
stx em_last_nid
lda #$01
sta em_last_kind
lda em_ctx
beq @done
lda em_sz,x
beq @size
cmp #$01
bne @size
lda em_off,x
jsr em_point_slot
bcs @fail
lda #EM_LP
jsr cg_lda_zpy
bcs @fail
lda #$01
sta em_size
@done: rts
@size: lda #EM_ERR_SIZE
sta em_bad
clc
rts
@fail: jmp em_fail

; A/X = a constant's low/high byte. A two-byte constant does not fit a byte
; expression yet.
em_expr_const:
sta em_low
stx em_high
lda em_ctx
beq @done
lda em_high
bne @size
lda em_low
jsr cg_lda_imm
bcs @fail
lda #$01
sta em_size
@done: rts
@size: lda #EM_ERR_SIZE
sta em_bad
clc
rts
@fail: jmp em_fail

; The left operand is complete (in A at run time): keep it on the stack while
; the right operand is emitted.
em_push_left:
lda em_ctx
beq @done
lda em_size
beq @size
cmp #$01
bne @size
lda em_size
sta em_left_size
lda #$48 ; PHA
jsr cg_put
bcs @fail
clc
rts
@size: lda #EM_ERR_SIZE
sta em_bad
clc
rts
@fail: jmp em_fail
@done: rts

; A = operator token. The right operand is in A at run time and the left is on
; the hardware stack; combine byte values only.
em_bin:
sta em_op
lda em_ctx
beq @done
lda em_left_size
beq @size
lda em_size
beq @size
lda #EM_TMP
jsr cg_sta_zp
bcs @fail
lda #$68 ; PLA
jsr cg_put
bcs @fail
lda em_op
cmp #TOK_PLUS
beq @plus
cmp #TOK_MINUS
beq @minus
lda #EM_ERR_OP
sta em_bad
clc
rts
@plus: jsr cg_clc
bcs @fail
lda #EM_TMP
jsr cg_adc_zp
bcs @fail
jmp @ok
@minus: jsr cg_sec
bcs @fail
lda #EM_TMP
jsr cg_sbc_zp
bcs @fail
@ok: lda #$01
sta em_size
clc
rts
@size: lda #EM_ERR_SIZE
sta em_bad
clc
rts
@fail: jmp em_fail
@done: rts

; A = slot offset: emit EM_LP = FRAME_FP + offset, sign extending a negative
; low byte, and leave Y = 0. Locals sit below FP, which indexed-indirect
; addressing cannot reach with a plain LDY #offset.
em_point_slot:
sta em_off_b
lda #FRAME_FP
jsr cg_lda_zp
bcs @out
jsr cg_clc
bcs @out
lda em_off_b
jsr cg_adc_imm
bcs @out
lda #EM_LP
jsr cg_sta_zp
bcs @out
lda #FRAME_FP + 1
jsr cg_lda_zp
bcs @out
lda em_off_b
bmi @negative
lda #$00
jmp @sign
@negative:
lda #$FF
@sign: jsr cg_adc_imm
bcs @out
lda #EM_LP + 1
jsr cg_sta_zp
bcs @out
lda #$00
jmp cg_ldy_imm
@out: sec
rts

; The assignment target is the statement's first primary (em_last_kind /
; em_last_nid). Accept a one-byte local only.
em_target:
lda em_on
beq @done
lda em_last_kind
cmp #$01
bne @bad
ldx em_last_nid
beq @bad
lda em_sz,x
beq @bad
cmp #$01
bne @bad
stx em_tgt
clc
rts
@bad: lda #EM_ERR_TARGET
sta em_bad
@done: clc
rts

; Store the value in A into the captured target.
em_store_target:
lda em_on
beq @done
lda em_tgt
beq @done
lda #$48 ; PHA: em_point_slot clobbers A
jsr cg_put
bcs @fail
lda em_tgt
tax
lda em_off,x
jsr em_point_slot
bcs @fail
lda #$68 ; PLA: the value, kept over the pointer
jsr cg_put
bcs @fail
lda #EM_LP
jsr cg_sta_zpy
bcs @fail
lda #$00
sta em_tgt
@done: clc
rts
@fail: jmp em_fail

; Print the value just parsed: the captured string literal when this statement
; captured one, else the byte in A.
em_print:
lda em_on
beq @done
lda gen_text_len
cmp em_txt_before
beq @byte
lda em_pr_text
ora em_pr_text+1
beq @state
lda #<gen_text
jsr cg_lda_imm
bcs @fail
lda #>gen_text
jsr cg_ldx_imm
bcs @fail
lda gen_text_len
jsr cg_ldy_imm
bcs @fail
lda #<em_tramp_text
ldx #>em_tramp_text
jsr cg_jsr_abs
bcs @fail
jmp @cr
@byte: lda em_pr_byte
ora em_pr_byte+1
beq @state
lda #<em_tramp_byte
ldx #>em_tramp_byte
jsr cg_jsr_abs
bcs @fail
@cr: lda em_pr_cr
ora em_pr_cr+1
beq @state
lda #<em_tramp_cr
ldx #>em_tramp_cr
jsr cg_jsr_abs
bcs @fail
clc
rts
@state: lda #EM_ERR_STATE
sta em_bad
clc
rts
@done: clc
rts
@fail: jmp em_fail

; The runtime print entry points live in cells the caller fills, so a test
; can record what the generated code asked to print.
em_tramp_text: jmp (em_pr_text)
em_tramp_byte: jmp (em_pr_byte)
em_tramp_cr: jmp (em_pr_cr)

; End of a body: return to the caller.
em_end_func:
lda em_on
beq @done
jsr cg_rts
bcs @fail
@done: clc
rts
@fail: jmp em_fail

; An emitter failure is the caller's signal; cg_err holds the detail.
em_fail:
lda #EM_ERR_STATE
sta em_bad
clc
rts

.segment "BSS"
.align 2 ; jmp (cell) must not straddle a page
em_pr_text: .res 2
em_pr_byte: .res 2
em_pr_cr: .res 2
em_on: .res 1
em_ctx: .res 1
em_bad: .res 1
em_last_kind: .res 1
em_last_nid: .res 1
em_tgt: .res 1
em_nid: .res 1
em_kind: .res 1
em_size: .res 1
em_left_size: .res 1
em_loc_next: .res 1
em_par_next: .res 1
em_locals: .res 1
em_par_bytes: .res 1
em_low: .res 1
em_high: .res 1
em_op: .res 1
em_txt_before: .res 1
em_off_b: .res 1
em_off: .res 256
em_sz: .res 256

+ 113
- 8
newLang/src/compiler/parser.s Ver arquivo

@@ -56,6 +56,11 @@
.import ty_assign, ty_binary, ty_unary
.import ty_arg_sc1, ty_arg_sc2, ty_arg_name1, ty_arg_num2
.import assign_slots, check_interfaces
.import em_reset, em_on, em_ctx, em_last_kind
.import em_begin_func, em_begin_params, em_local, em_param
.import em_stmt_begin, em_stmt_end, em_expr_off, em_begin_print
.import em_expr_name, em_expr_const, em_push_left, em_bin
.import em_target, em_store_target, em_print, em_end_func

.export parse_file, tb_get, tb_set, mem_count, par_count
.export gen_text, gen_text_len, gen_print_count, gen_bad
@@ -112,6 +117,7 @@ parse_file:
rts
: lda #$02
sta pass
jsr em_reset
jsr parse_pass
rts

@@ -403,6 +409,7 @@ parse_program:
; C=1 if the header had an error (the line has been skipped).
parse_signature:
jsr scope_clear
jsr em_begin_params
lda #$00
sta sig_params
sta ret_kind ; RET_UNKNOWN until the header parses
@@ -478,6 +485,9 @@ parse_signature:
bcs @skip
jsr no_array_sig
jsr set_local_type
jsr type_bytes
ldx last_local
jsr em_param
jsr add_param
IFTOK TOK_COMMA, @more
IFTOK TOK_RPAREN, @close
@@ -921,6 +931,7 @@ parse_body:
lda #$01
sta depth
sta refs_on ; names resolve, except in the methods of
jsr em_begin_func
lda block_stack ; a class whose name failed to parse
cmp #KW_METHOD
bne @loop
@@ -978,7 +989,9 @@ parse_body:
sta depth
@matched:
dec depth
jsr close_block
bne :+
jsr em_end_func
: jsr close_block
jsr next
jsr expect_eol
lda depth
@@ -1088,6 +1101,9 @@ parse_var:
bcc :+
jmp skip_line
: jsr set_local_type
jsr type_bytes
ldx last_local
jsr em_local
jmp expect_eol

; ----------------------------------------------------------- statements
@@ -1133,6 +1149,17 @@ parse_statement:
sta gen_in_print
lda gen_text_len
sta gen_len_before
lda em_on
beq @print_value
jsr em_begin_print
jsr em_stmt_begin
jsr value
jsr em_stmt_end
jsr check_print
lda #$00
sta gen_in_print
jsr em_print
jmp end_stmt
@print_value:
jsr value
jsr check_print
@@ -1241,6 +1268,7 @@ end_stmt:

; Assignment `target = expr` or a call `expr.method(args)`.
stmt_expr:
jsr em_expr_off ; the target is not a value
jsr parse_postfix
lda tok_kind
cmp #TOK_EQ
@@ -1260,8 +1288,23 @@ stmt_expr:
jmp syn_error_col
: ldx #$00 ; slot 0: the target's type
jsr save_slot
lda pass ; pass 1 only checks the types
cmp #$02
bne @assign_pass1
jsr em_target
jsr em_stmt_begin
jsr value
jsr em_stmt_end
jmp @assign_typed
@assign_pass1:
jsr value
@assign_typed:
jsr check_slot0
lda pass
cmp #$02
bne @assign_done
jsr em_store_target
@assign_done:
jmp end_stmt

; Skip the keyword or `=` and parse the expression after it (val_col).
@@ -1449,6 +1492,7 @@ parse_expr:
sta ex_prec,x
jsr note_op
jsr save_slot ; the left operand
jsr em_push_left
jsr next
ldx ex_depth
lda ex_prec,x
@@ -1460,6 +1504,9 @@ parse_expr:
ora #TR_BINARY
jsr trace_byte
ldx ex_depth
lda ex_op,x
jsr em_bin
ldx ex_depth
jsr load_tt
ldx ex_depth
lda ex_op,x
@@ -1549,7 +1596,9 @@ parse_postfix:
jsr chain_none
lda tok_kind
cmp #TOK_LPAREN ; a group is traced by its contents
beq @group
bne :+
jmp @group
:
lda tok_col ; trace: the operand, by its column
jsr trace_byte
lda tok_kind
@@ -1588,7 +1637,25 @@ parse_postfix:
jsr gen_capture
pla
tax
: lda lit_types,x
: lda #$00 ; a literal ends a name chain
sta em_last_kind
lda em_ctx
beq @lit_type
lda tok_kind
cmp #TOK_NUMBER
beq @lit_const
cmp #TOK_CHAR
bne @lit_type
@lit_const:
txa
pha
lda tok_value
ldx tok_value+1
jsr em_expr_const
pla
tax
@lit_type:
lda lit_types,x
sta sc_base
lda tok_value ; a number's value (others ignore it)
sta sc_val
@@ -1599,12 +1666,18 @@ parse_postfix:
@name: jsr resolve_name
lda #EX_PLACE
sta ex_last
ldx rn_id
jsr em_expr_name
jsr next
jmp @post
@self: jsr self_ref
@self: lda #$00
sta em_last_kind
jsr self_ref
jsr next
jmp @post
@group: jsr next ; the group has its contents' type
@group: lda #$00
sta em_last_kind
jsr next ; the group has its contents' type
jsr parse_full
lda #EX_OTHER
sta ex_last
@@ -1615,7 +1688,9 @@ parse_postfix:
jmp syn_error
: jsr next
jmp @post
@new: jsr next ; new Name(args)
@new: lda #$00
sta em_last_kind
jsr next ; new Name(args)
lda tok_kind
cmp #TOK_IDENT
beq :+
@@ -1642,6 +1717,8 @@ parse_postfix:
beq @index
rts
@member:
lda #$00
sta em_last_kind
jsr next
lda tok_kind ; member names may be keywords (D121)
cmp #TOK_IDENT
@@ -1659,7 +1736,9 @@ parse_postfix:
lda #EX_PLACE
sta ex_last
jmp @post
@call: ldx ex_depth ; sc = the result, kept over the arguments
@call: lda #$00
sta em_last_kind
ldx ex_depth ; sc = the result, kept over the arguments
jsr save_slot
jsr parse_args
ldx ex_depth
@@ -1667,7 +1746,9 @@ parse_postfix:
lda #EX_CALL
sta ex_last
jmp @post
@index: jsr index_base ; sc = the array, or unknown
@index: lda #$00
sta em_last_kind
jsr index_base ; sc = the array, or unknown
ldx ex_depth
jsr save_slot
jsr next
@@ -2439,6 +2520,30 @@ set_local_type:
sta loc_tlen_hi,y
@done: rts

; A = frame slot bytes for ty_*: byte-sized types take one, words two, and
; everything else (arrays, references) none here.
type_bytes:
lda ty_len
ora ty_len+1
bne @zero
lda ty_base
cmp #TY_BOOLEAN
beq @one
cmp #TY_BYTE
beq @one
cmp #TY_CHAR
beq @one
cmp #TY_INTEGER
beq @two
cmp #TY_WORD
beq @two
@zero: lda #$00
rts
@one: lda #$01
rts
@two: lda #$02
rts

; A = name ID. Y = scope byte, X = bit; A = that bit if set (Z=0) else 0.
scope_test:
tax


+ 227
- 0
newLang/tests/compiler/statements-test.s Ver arquivo

@@ -0,0 +1,227 @@
; E04-S06: parse statements.n8, execute the code the parser emitted, and
; report the resulting locals and printed bytes.
;
; The fixture runs in the compiler mapping, loads STMTS.N8, parses it with
; emission on, and JSRs cg_code in place. The emitted body reaches its locals
; through FRAME_FP, so the fixture points FP at its own buffer. Print calls go
; through the em_pr_* cells, which the fixture fills with recorders: the test
; sees exactly which bytes the generated code asked to print, without
; depending on CHROUT or the screen mapping.
;
; Mailbox data: +0 diagnostics, +1 em_bad, +2 a, +3 b, +4 cg_len, +5 cg_err,
; +6 recorded length. FAIL code is the stage: 1 load, 2 diagnostics, 3 emitter,
; 4 cg.
;
; The frame prologue is not emitted yet, so the fixture owns FP; E04-S07's
; sizing pass will emit frame_enter with the measured local bytes.

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

.import mmu_init, mmu_bank0, mmu_shutdown
.import src_load, src_dos_code, src_kernal_err
.import names_init
.import diag_init, diag_finish, diag_screen
.import parse_file
.import em_on, em_bad, em_pr_text, em_pr_byte, em_pr_cr
.import cg_code, cg_len, cg_err

; A conditional branch to a far error path: near branch over an absolute JMP.
.macro ON_ZERO target
beq :+
jmp target
:
.endmacro

.segment "LOADADDR"
.word $1C01

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

.segment "MAILBOX"
.res MB_SIZE

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

jsr mmu_init
jsr mmu_bank0

lda #<name_src
ldx #>name_src
ldy #name_src_len
jsr src_load
cmp #$00
ON_ZERO @fail_load

jsr names_init
lda #<name_src
ldx #>name_src
ldy #name_src_len
jsr diag_init
lda #$00
sta diag_screen
lda #$01
sta em_on ; pass 2 emits statements
lda #<rec_text ; record what the program prints
sta em_pr_text
lda #>rec_text
sta em_pr_text+1
lda #<rec_byte
sta em_pr_byte
lda #>rec_byte
sta em_pr_byte+1
lda #<rec_cr
sta em_pr_cr
lda #>rec_cr
sta em_pr_cr+1
lda #$00
sta rec_len

jsr parse_file
jsr diag_finish
sta diag_count

lda diag_count
sta MB_DATA+0
lda em_bad
sta MB_DATA+1
lda cg_len
sta MB_DATA+4
lda cg_err
sta MB_DATA+5
lda rec_len
sta MB_DATA+6

lda diag_count
ON_ZERO @fail_diag
lda em_bad
ON_ZERO @fail_emit
lda cg_err
ON_ZERO @fail_cg

lda #<em_fp ; the fixture owns the frame base
sta FRAME_FP
lda #>em_fp
sta FRAME_FP+1

jsr cg_code ; run the emitted body

lda em_fp - 2 ; a: the first local (FP-2)
sta MB_DATA+2
lda em_fp - 3 ; b: the second local (FP-3)
sta MB_DATA+3
lda rec_len
sta MB_DATA+6
lda #MB_ST_PASS
sta MB_STATUS
jmp @done
@fail_load:
sta MB_DATA+5 ; src_load result
lda src_dos_code
sta MB_DATA+6
lda src_kernal_err
sta MB_DATA+7
lda #1
jmp @fail
@fail_diag:
lda #2
jmp @fail
@fail_emit:
lda #3
jmp @fail
@fail_cg:
lda #4
@fail: sta MB_CODE
lda #$00
sta MB_CODE+1
lda #MB_ST_FAIL
sta MB_STATUS
@done: jsr mmu_shutdown
plp
rts

; ------------------------------------------------------------- recorders
; A/X = address, Y = length: record the literal bytes.
rec_text:
sta rec_ptr
stx rec_ptr+1
sty rec_cnt
ldy #$00
@loop: cpy rec_cnt
beq @done
lda (rec_ptr),y
sty rec_y
jsr rec_log
ldy rec_y
iny
bne @loop
@done: rts

; A = byte value: record $FF and the value, so a value print is distinct from
; text.
rec_byte:
pha
lda #$FF
jsr rec_log
pla
jmp rec_log

; A newline follows every print statement.
rec_cr:
lda #$0D
jmp rec_log

; A = byte to append.
rec_log:
ldy rec_len
cpy #32
bcs @full
sta rec_buf,y
inc rec_len
@full: rts

.segment "ZEROPAGE"
rec_ptr: .res 2

.segment "RODATA"
name_src: .byte "STMTS.N8"
name_src_len = * - name_src

.segment "BSS"
diag_count: .res 1
rec_cnt: .res 1
rec_y: .res 1
.export rec_len
rec_len: .res 1
.export rec_buf
rec_buf: .res 32
em_frame: .res 128
em_fp = em_frame + 64
.export em_fp

+ 10
- 0
newLang/tests/compiler/statements.n8 Ver arquivo

@@ -0,0 +1,10 @@
// E04-S06 emission fixture: byte locals, assignment, arithmetic, print.
program stmts
var a as byte
var b as byte
a = 7
b = a
a = a + 3
print "sum"
print a
end program

+ 4
- 2
newLang/tests/parser/parser-test.s Ver arquivo

@@ -435,14 +435,16 @@ count_fields:
dump_trace:
lda #$FB
jsr emit
sec
sec ; both bytes before emit: it clears C
lda ex_tp
sbc #<trace_buf
sta trace_len
jsr emit
lda ex_tp+1
sbc #>trace_buf
sta trace_len+1
lda trace_len
jsr emit
lda trace_len+1
jsr emit
lda #<trace_buf
sta fx_tmp


+ 11
- 0
reference/powerc-re/.gitignore Ver arquivo

@@ -0,0 +1,11 @@
work/
out/
opt/
optlib/
build/
res_*/
lib/
*.pet
*.png
POWERc-opt.d81
__pycache__/

+ 1
- 0
reference/powerc-re/POWERc.d81.sha256 Ver arquivo

@@ -0,0 +1 @@
e7f60c3277cab57332cb61910a263d5709c52b1a62dadc89dafc22af85b4c1a8 *reference/POWERc.d81

+ 112
- 0
reference/powerc-re/README.md Ver arquivo

@@ -0,0 +1,112 @@
# Power C — reverse engineering and improvements

Goal: faster code and smaller programs from the Spinnaker **Power C** compiler on
`reference/POWERc.d81`, **without touching the original**. Everything here works on copies
(`reference/POWERc.d81` is only ever read; its SHA-256 is in `POWERc.d81.sha256`).

Result (all verified in VICE, programs print identical output before/after):

| program | original | improved | |
|---|---|---|---|
| `vim` editor (5 units) | 22,506 bytes | 19,793 bytes | −12.1 % |
| `tests/sem.c` (semantics test) | 6,428 | 5,943 | −7.5 % |
| `tests/bench.c` loop benchmark, jiffies (lower is better) | calls 28, array 27, branch 20, arith 30 | calls 26, array 17, branch 11, arith 18 | −7 % / −37 % / −45 % / −40 % |

It also found and fixed a **bug in the original runtime**: the unsigned `>=` helper (`c0077.obj`)
has a wrong opcode (`$80` instead of `$90`), so `unsigned a, b; r = a >= b;` yields 1 for 1 >= 5
(`tests/uge.c`). Comparisons inside `if` were unaffected; value contexts were.

## Quick start

```
python reference\powerc-re\tools\setup.py # once: extract lib objects, build optlib\ (add --disk for POWERc-opt.d81)
powershell -NoProfile -File reference\powerc-re\tools\pcbuild.ps1 -Src hello.c -Name hello -Run '\n'
python reference\powerc-re\tools\regress.py # optimizer on/off, compares the screens
```

`pcbuild.ps1` compiles with the original `cc` inside VICE, runs `pcopt.py` on every `.o`, links with
the original linker using the improved runtime objects, and (with `-Run`) starts the program and
saves the final screen. `-NoOpt` gives the original behaviour for comparison. Sources are plain
ASCII; the scripts convert to PETSCII. Remember Power C is K&R C and cannot type `||`
(see `skills/powerc/SKILL.md`).

## How the toolchain works (reverse engineered)

* `cc.sh` runs two passes through temp files: `compiler` (source → intermediate) and `translator`
(intermediate → `.o`). The result is **native 6502 code**, generated by naive template expansion:
every value is a 16-bit pair in X (low) / Y (high); temporaries live in a software "temp stack"
at `$033C` up; locals live in zero page from `$2B`; every operator on non-trivial values calls a
tiny runtime helper `C$00nn` that works on that temp stack.
* Calls use a software frame (`$14/$15` frame pointer, `$16/$17` argument pointer, `$1A/$1B` push
stack). `C$105` (prolog) and `C$106` (epilogue) in `c105.obj` copy an 8-byte header plus all of
the callee's zero-page locals each call — about 450 + 530 cycles, which dominates call-heavy code.
* `C$RETURN` (inside `c105.obj`) is the 5-byte return-value slot; `C$0112` copies a value into it.
* The linker pulls objects by name from the `stdlib.l` / `syslib.l` index files (`symbol NUL file NUL`
pairs), so replacing e.g. `cmulti.obj` on a disk copy replaces that routine in every program.

### Object file format (`.o` / `.obj`) — `tools/objfmt.py` round-trips every object on the disk

```
u16 code_len ; code[] native code, data (globals, strings) embedded in it
u16 n ; u16 off[n] opcode offsets of instructions whose 16-bit operand is a code-relative
address (linker adds the load base): local jmp/jsr, pointers to data
u16 n ; { name\0 u8 kind u16 off } definitions (functions, globals, string constants)
u16 n ; { name\0 u16 kind u16 site } references to other objects; site = opcode offset
kind 0 = abs16, 1 = high byte (#>), 2 = low byte (#<), 4 = abs16 + 1
u16 n ; { name\0 u16 size } reserved (uninitialised) data blocks
```
Relocation placeholders in the code are the opcode byte repeated (`20 20 20` = `jsr extern`).
The linker overwrites them. Names may contain odd bytes (string constants are `<2 garbage>S$<n>`).

## The tools

| file | purpose |
|---|---|
| `tools/pcopt.py` | peephole optimizer for `.o` files (see below) |
| `tools/objfmt.py` | object format reader/writer; `python objfmt.py x.obj` checks the round trip |
| `tools/pcasm.py` | tiny assembler that writes Power C objects directly (used for `src/cmulti.s`) |
| `tools/cpu6502.py`, `helpertest.py`, `t_*.py`, `classify.py` | 6502 emulator + harnesses that prove a rewritten helper behaves exactly like the original (random and edge-case inputs, cycle counts) |
| `tools/dis.py` | disassemble an object with its relocations/references |
| `tools/pc-compile.ps1`, `pc-run.ps1`, `pcbuild.ps1` | drive the original `cc`/`link` in VICE on throw-away disk copies |
| `src/cmulti.s`, `src/div_patch.py` | the replacement runtime routines |

### What `pcopt.py` does

It decodes each compiled function (found via the `sta $FB` prolog and the `jmp C$106` epilogue),
rewrites it with a liveness analysis (A/X/Y/C/N/Z/V, plus temp-stack memory) and re-emits the object
with every offset, branch, relocation and definition fixed up. Anything it cannot decode with
certainty is left untouched; with `--no-rules` it reproduces the input byte for byte.

Rules: inline the `<`, `>`, `<=`, `>=`, `==`, `!=` helpers (signed and unsigned) when they feed an
`if`/loop test, including `&&` chains; return values stored straight into `C$RETURN`; reload
elimination (`stx/sty` then `ldx/ldy` of the same slot); `ldx #K;ldy #K;stx;sty` → `lda #K;sta;sta`;
16-bit `+1` / `+K`; jump-to-next and branch-over-jump removal; `==`/`!=` against a constant as two
branches instead of a branch plus `jmp`.

### Faster runtime routines (`optlib/`, built by `setup.py`)

* `cmulti.obj` — multiply that needs only 8 shift/add steps when an operand is < 256
(the other 8 steps of the original merely shifted the 32-bit register by one byte);
~40 % faster for small factors, +69 bytes of runtime when `*` is used.
* `c0047.obj` — divide/remainder that normalises the divisor only as far as the dividend needs;
same quotient and remainder, e.g. 100/7 is ~2× faster. (30000/3 is ~10 % slower.)
* `c0077.obj` — the opcode fix described above.

## Safety

Equivalence of the replaced runtime routines is checked in the emulator against the originals over
thousands of operand pairs (`tools/t_multi.py`, `t_div.py`). The optimizer's rewrites rely on the
compiler's temp-stack discipline (operand slots are dead after the operation that consumed them) and
on the calling convention (A/X/Y are arguments, flags never are); programs are validated end to end
(`regress.py`, the `vim` editing scenario). A bug found that way (an `&&` chain read a
result slot the inlined compare no longer wrote) is covered by `tests/sem.c`.

## Not done / ideas

* Prolog/epilogue (`c105.obj`) — dominates call cost but its frame layout is shared with
`setjmp`/`longjmp`; micro-optimising the copy loops would buy roughly 10–15 %.
* A real fix needs a better code generator (patching `translator`): keep values in registers,
stop saving zero-page locals on every call.
* Inline expansion of `C$1102` (push) and the pointer load/store helpers (`C$1021…`): faster but larger.
* Arrays/long expressions: only the idioms above are matched, so gains shrink for code that
is mostly helper calls.

BIN
reference/powerc-re/cc.sh.prg Ver arquivo


BIN
reference/powerc-re/compiler.prg Ver arquivo


BIN
reference/powerc-re/link.sh.prg Ver arquivo


BIN
reference/powerc-re/shell.prg Ver arquivo


+ 61
- 0
reference/powerc-re/src/cmulti.s Ver arquivo

@@ -0,0 +1,61 @@
; C$MULTI - 16x16 shift/add multiply, drop-in replacement for the original.
; in : $5A/$5B multiplicand, $5C/$5D multiplier, $5E/$5F accumulator (callers pass 0)
; out: product in $5D:$5C (low word); $5A/$5B preserved
; When the multiplier (or, with an empty accumulator, the multiplicand) is below 256,
; only 8 shift/add passes are needed: the other 8 passes of the original just moved the
; 32 bit register right by one byte, which is done directly instead.
.def C$MULTI
entry: ldy #1
ldx #8
lda $5d
beq loop
lda $5b
bne full
lda $5e
ora $5f
bne full
lda $5a ; multiplicand < 256: swap the operands, run, swap back
pha
ldx $5c
ldy $5d
lda $5a
sta $5c
lda #0
sta $5d
stx $5a
sty $5b
jsr entry
pla
sta $5a
lda #0
sta $5b
rts
full: ldy #0
ldx #16
loop: lda $5c
and #1
beq skip
clc
lda $5e
adc $5a
sta $5e
lda $5f
adc $5b
sta $5f
skip: lsr $5f
ror $5e
ror $5d
ror $5c
dex
bne loop
tya
beq done
lda $5d
sta $5c
lda $5e
sta $5d
lda $5f
sta $5e
lda #0
sta $5f
done: rts

+ 81
- 0
reference/powerc-re/src/div_patch.py Ver arquivo

@@ -0,0 +1,81 @@
"""Build a faster c0047.obj (division / remainder helpers) by patching the original's shift/subtract loop.

The original normalises the divisor all the way up to bit 15 and then runs one subtract step per
shift, so even 100/7 costs ~15 wasted steps. The new loop normalises only as far as the dividend
needs (2*D <= N); the extra leading steps of the original only ever produced zero quotient bits.
Quotient ($53/$54) and remainder ($51/$52) are identical. Run: python src/div_patch.py in.obj out.obj
"""
import sys, os
sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', 'tools'))
from objfmt import Obj
from pcasm import assemble

START, END = 0xC7, 0xF5

REGION = """
ldx #1
norm: lda $50
bmi go
lda $4f
asl a
tay
lda $50
rol a
cmp $52
bcc dosh
bne go
cpy $51
beq dosh
bcs go
dosh: sty $4f
sta $50
inx
bne norm
go: asl $53
rol $54
sec
lda $51
sbc $4f
tay
lda $52
sbc $50
bcc nosub
sty $51
sta $52
inc $53
nosub: lsr $50
ror $4f
dex
bne go
rts
"""

def patch(o):
c = bytes(o.code)
assert c[START] == 0xA2 and c[END - 1] == 0x60, 'unexpected layout'
new, _ = assemble(REGION, base=START)
delta = len(new.code) - (END - START)
def fix(v):
assert not (START <= v < END), 'reference into the replaced region'
return v + delta if v >= END else v
o2 = Obj()
o2.code = bytearray(c[:START]) + new.code + bytearray(c[END:])
reloc = []
for off in o.reloc:
if START <= off < END:
continue # the old jmp norm
n = fix(off)
reloc.append(n)
# fix the 16 bit operand value (code relative) in the moved copy
v = o2.code[n + 1] | o2.code[n + 2] << 8
v = fix(v) if v >= END else v
o2.code[n + 1] = v & 255; o2.code[n + 2] = v >> 8
o2.reloc = sorted(reloc + [r for r in new.reloc])
o2.defs = [[nm, k, fix(off)] for nm, k, off in o.defs]
o2.refs = [[nm, k, fix(off)] for nm, k, off in o.refs]
o2.res = o.res; o2.trailing = o.trailing
return o2

if __name__ == '__main__':
o = Obj.parse(open(sys.argv[1], 'rb').read())
open(sys.argv[2], 'wb').write(patch(o).build())

+ 48
- 0
reference/powerc-re/tests/bench.c Ver arquivo

@@ -0,0 +1,48 @@
#include <stdio.h>
int arr[64];
char *jp;
unsigned now()
{
return (jp[0] << 8) + jp[1];
}
add(a, b)
int a, b;
{
return a + b;
}
main()
{
unsigned t0, t1;
int i, j, s;

jp = (char *) 0xa1;
t0 = now();
s = 0;
for (i = 0; i < 400; i++)
s = add(s, i);
t1 = now();
printf("calls %u s=%d\n", t1 - t0, s);

t0 = now();
for (j = 0; j < 20; j++)
for (i = 0; i < 64; i++)
arr[i] = arr[i] + i + j;
t1 = now();
printf("array %u a=%d\n", t1 - t0, arr[10]);

t0 = now();
s = 0;
for (i = 0; i < 1000; i++) {
if (i > 500) s = s + 1;
else s = s + 2;
}
t1 = now();
printf("branch %u s=%d\n", t1 - t0, s);

t0 = now();
s = 1;
for (i = 0; i < 300; i++)
s = (s * 3 + i) % 1000;
t1 = now();
printf("arith %u s=%d\n", t1 - t0, s);
}

+ 80
- 0
reference/powerc-re/tests/sem.c Ver arquivo

@@ -0,0 +1,80 @@
#include <stdio.h>
int sv[8];
unsigned uv[8];
char buf[40];
int cnt[16];
int fib(n)
int n;
{
if (n < 2) return n;
return fib(n - 1) + fib(n - 2);
}
int sgn(a)
int a;
{
if (a < 0) return 0 - 1;
if (a == 0) return 0;
return 1;
}
unsigned umax(a, b)
unsigned a, b;
{
if (a > b) return a;
return b;
}
strl(s)
char *s;
{
int n;
n = 0;
while (*s) { n++; s++; }
return n;
}
main()
{
int i, j, k, a, b;
unsigned int uu, vv;
int t;

sv[0] = 0 - 32767; sv[1] = 0 - 5; sv[2] = 0 - 1; sv[3] = 0;
sv[4] = 1; sv[5] = 5; sv[6] = 255; sv[7] = 32767;
uv[0] = 0; uv[1] = 1; uv[2] = 5; uv[3] = 255; uv[4] = 256; uv[5] = 32767; uv[6] = 32768; uv[7] = 65535;
for (k = 0; k < 16; k++) cnt[k] = 0;
for (i = 0; i < 8; i++)
for (j = 0; j < 8; j++) {
a = sv[i]; b = sv[j];
if (a < b) cnt[0]++;
if (a > b) cnt[1]++;
if (a <= b) cnt[2]++;
if (a >= b) cnt[3]++;
if (a == b) cnt[4]++;
if (a != b) cnt[5]++;
uu = uv[i]; vv = uv[j];
if (uu < vv) cnt[6]++;
if (uu > vv) cnt[7]++;
if (uu <= vv) cnt[8]++;
if (uu == vv) cnt[10]++;
if (uu != vv) cnt[11]++;
if (a < b && uu < vv) cnt[12]++;
if (a > b) cnt[13]++; else if (uu > vv) cnt[13]++;
if (a < 5) cnt[14]++;
if (uu > 255) cnt[15]++;
}
for (k = 0; k < 16; k++) printf("%d ", cnt[k]);
printf("\n");
t = 0;
for (i = 0; i < 8; i++) t = t + sgn(sv[i]) * (i + 1);
printf("sgn %d fib %d umax %u\n", t, fib(10), umax(40000, 3));
strcpy(buf, "hello world");
printf("len %d %s\n", strl(buf), buf);
t = 0;
for (i = 0; i < 100; i++) {
switch (i % 5) {
case 0: t = t + 1; break;
case 1: t = t + 2; break;
case 3: t = t - 1; break;
default: t = t + 3;
}
}
printf("sw %d mul %d %d %d div %d %d\n", t, 123 * 45, 7 * (0 - 9), 300 * 200, 1000 / 7, 1000 % 7);
}

+ 7
- 0
reference/powerc-re/tests/t1.c Ver arquivo

@@ -0,0 +1,7 @@
int g;
int h;
char c;
f1()
{
g = 5;
}

+ 11
- 0
reference/powerc-re/tests/t2.c Ver arquivo

@@ -0,0 +1,11 @@
int g;
extern int ext;
f2(a)
int a;
{
int i;
i = a + 1;
g = i;
if (i > 3) g = 0;
ext = g;
}

+ 15
- 0
reference/powerc-re/tests/t3.c Ver arquivo

@@ -0,0 +1,15 @@
#include <stdio.h>
extern int ext2[];
int big[100];
char buf[300];
int init = 7;
char *msg = "hi there";
f3(a)
int a;
{
ext2[2] = a;
big[a] = ext2[3];
printf("val %d\n", a);
buf[1] = 5;
return init;
}

+ 5
- 0
reference/powerc-re/tests/t4.c Ver arquivo

@@ -0,0 +1,5 @@
#include <stdio.h>
main()
{
printf("hello\n");
}

+ 42
- 0
reference/powerc-re/tests/t5.c Ver arquivo

@@ -0,0 +1,42 @@
/* powerc-hello.c - first program for Power C on the C64
*
* Prints a small table and demonstrates 16-bit int wrap-around,
* so it exercises printf(), a counting loop and int arithmetic.
*
* Build on the Power C disk (examples/powerc-hello.d81 already holds
* the compiled result):
*
* cc hello.c
* link -s
* > hello.o
* > ^ (pull in stdlib.l + syslib.l)
* > (empty RETURN)
* output file name: hello
*
* This file is plain ASCII; Power C needs PETSCII source with CR line
* endings, so convert it before writing it to the image:
*
* c1541 powerc-hello.d81 -write hello.pet "hello.c,s"
*
* Then run it with -autostart "powerc-hello.d81:hello", or from BASIC:
* LOAD"HELLO",8 : RUN
*/

#include <stdio.h>

main()
{
int i, v;

printf("power c on the c64\n");
printf("==================\n\n");

for (i = 1; i <= 5; i++) {
v = i * i;
printf("%d squared is %d\n", i, v);
}

v = 32767;
v = v + 1;
printf("\nint is 16-bit: 32767 + 1 = %d\n", v);
}

+ 13
- 0
reference/powerc-re/tests/uge.c Ver arquivo

@@ -0,0 +1,13 @@
#include <stdio.h>
unsigned a, b;
main()
{
int r1, r2, r3;
a = 1; b = 5;
r1 = a >= b;
r2 = b >= a;
r3 = a >= a;
printf("uge: 1>=5=%d 5>=1=%d 1>=1=%d\n", r1, r2, r3);
if (a >= b) printf("BUG: 1>=5 taken\n");
else printf("ok: 1>=5 not taken\n");
}

+ 49
- 0
reference/powerc-re/tools/classify.py Ver arquivo

@@ -0,0 +1,49 @@
import sys, os, glob, itertools, random
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from helpertest import *
from objfmt import Obj, pn
from cpu6502 import CPU

OPS = {
'lt': lambda a,b: int(a<b), 'gt': lambda a,b: int(a>b), 'le': lambda a,b: int(a<=b), 'ge': lambda a,b: int(a>=b),
'eq': lambda a,b: int(a==b), 'ne': lambda a,b: int(a!=b),
'add': lambda a,b: (a+b)&0xFFFF, 'sub': lambda a,b: (a-b)&0xFFFF, 'and': lambda a,b: a&b, 'or': lambda a,b: a|b, 'xor': lambda a,b: a^b,
'mul': lambda a,b: (a*b)&0xFFFF,
}
def s16(v): return v-65536 if v>=32768 else v
vals=[0,1,2,3,5,100,255,256,257,1000,32767,32768,40000,65535,65534,12345]
def run(h, name, a, b):
cpu = CPU(); h.install(cpu)
cpu.m[0x14]=0; cpu.m[0x15]=0x80; cpu.sp=0xF0
for i,v in enumerate((a&255,a>>8,b&255,b>>8)): cpu.m[0x33C+i]=v
cpu.a=4; cpu.x=0; cpu.y=0
cpu.call(h.addr(name))
return cpu.m[0x33C]|cpu.m[0x33D]<<8, cpu
for f in sorted(glob.glob('lib/c*.obj')):
o = Obj.parse(open(f,'rb').read())
if f.endswith('c105.obj') or 'cstart' in f or 'conv' in f or 'localdefs' in f: continue
if any(bytes(n).startswith(b'C$') is False for n,k,off in o.defs): pass
ext={}
try:
h = Helper(o, 0x6000, {nm.decode('latin1') if False else bytes(nm): 0xD000 for nm,k,off in o.refs})
except Exception as ex:
print(os.path.basename(f), 'load fail', ex); continue
for nm,k,off in o.defs:
if not bytes(nm).startswith(b'C$'): continue
res = {}
try:
for signed in (False, True):
match = {}
for opn,fn in OPS.items():
ok = True
for a,b in itertools.product(vals, vals):
r,_ = run(h, nm, a, b)
A,B = (s16(a),s16(b)) if signed else (a,b)
exp = fn(A,B)
exp &= 0xFFFF
if r != exp: ok = False; break
if ok: match[opn]=True
if match: res[signed] = list(match)
except Exception as ex:
res = 'ERR %s' % ex
print('%-14s %-10s size %3d %s' % (os.path.basename(f), pn(nm), len(o.code), res))

+ 204
- 0
reference/powerc-re/tools/cpu6502.py Ver arquivo

@@ -0,0 +1,204 @@
"""Minimal NMOS 6502 emulator (official opcodes, binary-mode ADC/SBC) with cycle counts.

Used to check that a rewritten runtime helper behaves exactly like the original one
(same zero page / memory / registers afterwards) and to compare their cycle costs.
"""
import sys, os
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from pcopt import DEC, SIZE

BASECYC = {
'imp': 2, 'acc': 2, 'imm': 2, 'zp': 3, 'zpx': 4, 'zpy': 4, 'abs': 4, 'abx': 4, 'aby': 4,
'izx': 6, 'izy': 5, 'rel': 2, 'ind': 5,
}
# read-modify-write and store extra cycles
RMW = {'asl', 'lsr', 'rol', 'ror', 'inc', 'dec'}
STORE = {'sta', 'stx', 'sty'}


class CPU:
def __init__(s):
s.m = bytearray(65536)
s.a = s.x = s.y = 0
s.sp = 0xFF
s.pc = 0
s.c = s.z = s.n = s.v = 0
s.cyc = 0
s.i = 0; s.d = 0
s.watch = None

# --- helpers
def rd(s, a):
return s.m[a & 0xFFFF]

def wr(s, a, v):
s.m[a & 0xFFFF] = v & 255

def push(s, v):
s.m[0x100 + s.sp] = v & 255
s.sp = (s.sp - 1) & 255

def pull(s):
s.sp = (s.sp + 1) & 255
return s.m[0x100 + s.sp]

def nz(s, v):
v &= 255
s.z = 1 if v == 0 else 0
s.n = v >> 7
return v

def flags(s):
return s.c | s.z << 1 | s.i << 2 | s.d << 3 | 0x30 | s.v << 6 | s.n << 7

def setflags(s, p):
s.c = p & 1; s.z = p >> 1 & 1; s.i = p >> 2 & 1; s.d = p >> 3 & 1; s.v = p >> 6 & 1; s.n = p >> 7 & 1

def ea(s, md, pc):
"""effective address, extra-cycle flag"""
m = s.m
if md == 'zp': return m[pc + 1], 0
if md == 'zpx': return (m[pc + 1] + s.x) & 255, 0
if md == 'zpy': return (m[pc + 1] + s.y) & 255, 0
base = m[pc + 1] | m[pc + 2] << 8
if md == 'abs': return base, 0
if md == 'abx':
a = (base + s.x) & 0xFFFF; return a, (a >> 8) != (base >> 8)
if md == 'aby':
a = (base + s.y) & 0xFFFF; return a, (a >> 8) != (base >> 8)
if md == 'izx':
p = (m[pc + 1] + s.x) & 255
return m[p] | m[(p + 1) & 255] << 8, 0
if md == 'izy':
p = m[pc + 1]
b = m[p] | m[(p + 1) & 255] << 8
a = (b + s.y) & 0xFFFF; return a, (a >> 8) != (b >> 8)
raise ValueError(md)

def step(s):
pc = s.pc
op = s.m[pc]
if op == 0x80: # undocumented NOP #imm (2 bytes, 2 cycles) on the NMOS 6502
s.pc = (pc + 2) & 0xFFFF
s.cyc += 2
return
if op not in DEC:
raise ValueError('bad opcode %02x at %04x' % (op, pc))
mn, md = DEC[op]
n = SIZE[md]
cyc = BASECYC[md]
s.pc = (pc + n) & 0xFFFF
if md == 'imm':
val = s.m[pc + 1]; addr = None
elif md in ('imp', 'acc', 'rel', 'ind'):
val = None; addr = None
else:
addr, cross = s.ea(md, pc)
val = None
if mn in RMW or mn in STORE:
if md in ('abx', 'aby', 'izy'): cyc += 1 # always penalised
if mn in RMW: cyc += 2
if mn in STORE and md == 'abs': pass
elif cross:
cyc += 1
if mn in RMW and md in ('zp', 'zpx', 'abs', 'abx'):
pass
rdv = lambda: s.m[addr] if addr is not None else val
if mn == 'lda': s.a = s.nz(rdv())
elif mn == 'ldx': s.x = s.nz(rdv())
elif mn == 'ldy': s.y = s.nz(rdv())
elif mn == 'sta': s.wr(addr, s.a)
elif mn == 'stx': s.wr(addr, s.x)
elif mn == 'sty': s.wr(addr, s.y)
elif mn == 'tax': s.x = s.nz(s.a)
elif mn == 'tay': s.y = s.nz(s.a)
elif mn == 'txa': s.a = s.nz(s.x)
elif mn == 'tya': s.a = s.nz(s.y)
elif mn == 'tsx': s.x = s.nz(s.sp)
elif mn == 'txs': s.sp = s.x
elif mn == 'adc':
b = rdv(); t = s.a + b + s.c
s.v = 1 if (~(s.a ^ b) & (s.a ^ t) & 0x80) else 0
s.c = 1 if t > 255 else 0
s.a = s.nz(t)
elif mn == 'sbc':
b = rdv() ^ 255; t = s.a + b + s.c
s.v = 1 if (~(s.a ^ b) & (s.a ^ t) & 0x80) else 0
s.c = 1 if t > 255 else 0
s.a = s.nz(t)
elif mn == 'and': s.a = s.nz(s.a & rdv())
elif mn == 'ora': s.a = s.nz(s.a | rdv())
elif mn == 'eor': s.a = s.nz(s.a ^ rdv())
elif mn in ('cmp', 'cpx', 'cpy'):
r = {'cmp': s.a, 'cpx': s.x, 'cpy': s.y}[mn]
t = r - rdv()
s.c = 1 if t >= 0 else 0
s.nz(t)
elif mn == 'bit':
b = rdv(); s.z = 1 if (s.a & b) == 0 else 0; s.n = b >> 7; s.v = b >> 6 & 1
elif mn in ('asl', 'lsr', 'rol', 'ror'):
v = s.a if md == 'acc' else s.m[addr]
if mn == 'asl': c = v >> 7; v = v << 1
elif mn == 'lsr': c = v & 1; v = v >> 1
elif mn == 'rol': c = v >> 7; v = v << 1 | s.c
else: c = v & 1; v = v >> 1 | s.c << 7
s.c = c; v = s.nz(v)
if md == 'acc': s.a = v
else: s.wr(addr, v)
elif mn == 'inc': s.wr(addr, s.nz(s.m[addr] + 1))
elif mn == 'dec': s.wr(addr, s.nz(s.m[addr] - 1))
elif mn == 'inx': s.x = s.nz(s.x + 1)
elif mn == 'dex': s.x = s.nz(s.x - 1)
elif mn == 'iny': s.y = s.nz(s.y + 1)
elif mn == 'dey': s.y = s.nz(s.y - 1)
elif mn == 'clc': s.c = 0
elif mn == 'sec': s.c = 1
elif mn == 'cld': s.d = 0
elif mn == 'sed': s.d = 1
elif mn == 'cli': s.i = 0
elif mn == 'sei': s.i = 1
elif mn == 'clv': s.v = 0
elif mn == 'nop': pass
elif mn == 'pha': s.push(s.a); cyc = 3
elif mn == 'pla': s.a = s.nz(s.pull()); cyc = 4
elif mn == 'php': s.push(s.flags()); cyc = 3
elif mn == 'plp': s.setflags(s.pull()); cyc = 4
elif mn in ('bpl', 'bmi', 'bvc', 'bvs', 'bcc', 'bcs', 'bne', 'beq'):
cond = {'bpl': not s.n, 'bmi': s.n, 'bvc': not s.v, 'bvs': s.v, 'bcc': not s.c, 'bcs': s.c,
'bne': not s.z, 'beq': s.z}[mn]
if cond:
off = s.m[pc + 1]
if off > 127: off -= 256
t = (s.pc + off) & 0xFFFF
cyc += 1 + ((t >> 8) != (s.pc >> 8))
s.pc = t
elif mn == 'jmp':
if md == 'abs':
s.pc = s.m[pc + 1] | s.m[pc + 2] << 8; cyc = 3
else:
p = s.m[pc + 1] | s.m[pc + 2] << 8
s.pc = s.m[p] | s.m[(p & 0xFF00) | ((p + 1) & 0xFF)] << 8
elif mn == 'jsr':
r = (pc + 2) & 0xFFFF
s.push(r >> 8); s.push(r & 255)
s.pc = s.m[pc + 1] | s.m[pc + 2] << 8; cyc = 6
elif mn == 'rts':
lo = s.pull(); hi = s.pull()
s.pc = ((hi << 8 | lo) + 1) & 0xFFFF; cyc = 6
else:
raise ValueError('unsupported ' + mn)
s.cyc += cyc

def call(s, addr, maxsteps=200000):
"""JSR addr and run until it returns; returns cycles used (excluding the JSR itself)"""
SENT = 0xFFF0
s.push((SENT - 1) >> 8); s.push((SENT - 1) & 255)
s.pc = addr
c0 = s.cyc
steps = 0
while s.pc != SENT:
s.step()
steps += 1
if steps > maxsteps:
raise RuntimeError('runaway')
return s.cyc - c0

+ 25
- 0
reference/powerc-re/tools/dis.py Ver arquivo

@@ -0,0 +1,25 @@
import sys, os
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from objfmt import *
from pcopt import DEC, SIZE
def dis(o, a=0, b=None):
c = o.code; b = len(c) if b is None else b
refs = {off: (pn(n), k) for n, k, off in o.refs}
names = {off: pn(n) for n, k, off in o.defs}
p = a
while p < b:
if p in names: print('%s:' % names[p])
op = c[p]
if op not in DEC: print('%04x %02x .byte' % (p, op)); p += 1; continue
mn, md = DEC[op]; n = SIZE[md]; bs = c[p:p+n]
v = bs[1] if n == 2 else (bs[1] | bs[2] << 8 if n == 3 else 0)
if md == 'rel': v = p + 2 + (v - 256 if v > 127 else v)
opnd = {'imp': '', 'acc': 'a', 'imm': '#$%02x' % v, 'zp': '$%02x' % v, 'zpx': '$%02x,x' % v, 'zpy': '$%02x,y' % v, 'abs': '$%04x' % v, 'abx': '$%04x,x' % v, 'aby': '$%04x,y' % v, 'izx': '($%02x,x)' % v, 'izy': '($%02x),y' % v, 'rel': 'L%04x' % v, 'ind': '($%04x)' % v}[md]
tag = ''
if p in refs: tag += ' ; ref %s' % (refs[p],)
if p in o.reloc: tag += ' ; reloc'
print('%04x %-9s %s %s%s' % (p, bs.hex(' '), mn, opnd, tag))
p += n
if __name__ == '__main__':
o = Obj.parse(open(sys.argv[1], 'rb').read())
dis(o)

+ 10
- 0
reference/powerc-re/tools/dump.py Ver arquivo

@@ -0,0 +1,10 @@
import sys
def show(f):
d=open(f,'rb').read()
n=d[0]|d[1]<<8
print(f,'len',n,'file',len(d))
print('code:',d[2:2+n].hex(' '))
t=d[2+n:]
print('tail:',t.hex(' '))
print('asc :',''.join(chr(b) if 32<=b<127 else '.' for b in t))
for f in sys.argv[1:]: show(f)

+ 55
- 0
reference/powerc-re/tools/helpertest.py Ver arquivo

@@ -0,0 +1,55 @@
"""Load Power C runtime-helper objects into the 6502 emulator and compare implementations.

usage (library): h = Helper(obj, base=0x6000, ext={'C$HIGHMEM': 0xC000, ...})
h.addr('C$105')
"""
import sys, os, random
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from objfmt import Obj
from cpu6502 import CPU


class Helper:
def __init__(self, obj, base, ext):
self.o = obj
self.base = base
self.code = bytearray(obj.code)
for off in obj.reloc: # local absolute operands
v = self.code[off + 1] | self.code[off + 2] << 8
v = (v + base) & 0xFFFF
self.code[off + 1] = v & 255; self.code[off + 2] = v >> 8
syms = {nm: base + off for nm, k, off in obj.defs}
syms.update({k.encode() if isinstance(k, str) else k: v for k, v in ext.items()})
for nm, kind, off in obj.refs:
a = syms[bytes(nm)]
if kind == 0:
self.code[off + 1] = a & 255; self.code[off + 2] = a >> 8
elif kind == 4:
a += 1; self.code[off + 1] = a & 255; self.code[off + 2] = a >> 8
elif kind == 2:
self.code[off + 1] = a & 255
elif kind == 1:
self.code[off + 1] = a >> 8
else:
raise ValueError('kind %d' % kind)
self.syms = syms

def addr(self, name):
return self.syms[name.encode() if isinstance(name, str) else name]

def install(self, cpu):
cpu.m[self.base:self.base + len(self.code)] = self.code


def snapshot(cpu, skip=()):
m = bytes(cpu.m)
return m


def diff_mem(a, b, skip):
out = []
for i in range(65536):
if a[i] != b[i] and not any(lo <= i < hi for lo, hi in skip):
out.append((i, a[i], b[i]))
if len(out) > 8: break
return out

+ 57
- 0
reference/powerc-re/tools/objfmt.py Ver arquivo

@@ -0,0 +1,57 @@
"""Power C object file (.o/.obj) reader/writer.

u16 code_len; code
u16 n_reloc; u16 off[n] (u16 at off+1 is code-relative, += load base)
u16 n_def; {name\0 u8 kind u16 off}
u16 n_ref; {name\0 u16 kind u16 site} kind 0=abs16 1=hi 2=lo 4=abs+1 (site = opcode offset)
u16 n_res; {name\0 u16 size}
"""
import struct

class Obj:
def __init__(s):
s.code=bytearray(); s.reloc=[]; s.defs=[]; s.refs=[]; s.res=[]
@staticmethod
def parse(data):
o=Obj(); p=0
def u16():
nonlocal p
v=data[p]|data[p+1]<<8; p+=2; return v
def name():
nonlocal p
e=data.index(0,p); n=bytes(data[p:e]); p=e+1; return n
n=u16(); o.code=bytearray(data[p:p+n]); p+=n
k=u16(); o.reloc=[u16() for _ in range(k)]
k=u16()
for _ in range(k):
nm=name(); kind=data[p]; p+=1; off=u16(); o.defs.append([nm,kind,off])
k=u16()
for _ in range(k):
nm=name(); kind=u16(); off=u16(); o.refs.append([nm,kind,off])
k=u16()
for _ in range(k):
nm=name(); sz=u16(); o.res.append([nm,sz])
o.trailing=bytes(data[p:])
return o
def build(o):
out=bytearray()
def u16(v): out.extend((v&255,v>>8&255))
u16(len(o.code)); out+=o.code
u16(len(o.reloc)); [u16(x) for x in o.reloc]
u16(len(o.defs))
for nm,kind,off in o.defs: out+=nm+b'\0'+bytes([kind]); u16(off)
u16(len(o.refs))
for nm,kind,off in o.refs: out+=nm+b'\0'; u16(kind); u16(off)
u16(len(o.res))
for nm,sz in o.res: out+=nm+b'\0'; u16(sz)
out+=o.trailing
return bytes(out)

def pn(b): return ''.join(chr(c) if 32<=c<127 else '<%02x>'%c for c in b)

if __name__=='__main__':
import sys
for f in sys.argv[1:]:
d=open(f,'rb').read(); o=Obj.parse(d)
assert o.build()==d,'roundtrip mismatch '+f
print(f,'code',len(o.code),'reloc',len(o.reloc),'defs',[(pn(a),b,hex(c)) for a,b,c in o.defs],'refs',len(o.refs),'res',o.res,'trail',len(o.trailing))

+ 61
- 0
reference/powerc-re/tools/pc-compile.ps1 Ver arquivo

@@ -0,0 +1,61 @@
# pc-compile.ps1 - compile C sources with the ORIGINAL Power C in VICE, on a throwaway
# copy of reference/POWERc.d81 (the original image is never written).
# pc-compile.ps1 -Src a.c,b.c -Out outdir [-Link name] [-Run keys]
# Produces outdir\a.o ... ; with -Link, links all objects (+libs) into outdir\<name>.prg
param([string[]]$Src, [string]$Out, [string]$Link = "", [int]$Delay = 700, [int]$Pad = 20)
$ErrorActionPreference = "Continue"
$vice = "C:\Program Files\GTK3VICE-3.10-win64\bin"
$x64 = Join-Path $vice "x64sc.exe"; $c1541 = Join-Path $vice "c1541.exe"
$Src = @($Src | % { $_ -split "," } | % { (Resolve-Path $_).Path })
New-Item -ItemType Directory -Force -Path $Out | Out-Null
$Out = (Resolve-Path $Out).Path
$here = $PSScriptRoot
$base = Join-Path $here "..\..\POWERc.d81"
$wd = Join-Path $here "..\work"
New-Item -ItemType Directory -Force -Path $wd, $Out | Out-Null
Set-Location $wd
$img = "t.d81"
Copy-Item $base $img -Force

function ConvertTo-Petscii([string]$in, [string]$out) {
$bytes = [IO.File]::ReadAllBytes($in); $o = New-Object System.Collections.Generic.List[byte]
foreach ($b in $bytes) {
if ($b -eq 13) { continue }
if ($b -eq 10) { $o.Add([byte]13); continue }
if ($b -ge 0x61 -and $b -le 0x7A) { $o.Add([byte]($b - 0x20)); continue }
if ($b -ge 0x41 -and $b -le 0x5A) { $o.Add([byte]($b + 0x80)); continue }
if ($b -ge 0x7B -and $b -le 0x7F) { $o.Add([byte]($b + 0x60)); continue }
$o.Add([byte]$b)
}
[IO.File]::WriteAllBytes($out, $o.ToArray())
}
function Quote-Arg([string]$a) {
if ($a -ne "" -and $a -notmatch '[\s"]') { return $a }
$e = $a -replace '(\\*)"', '$1$1\"'; $e = $e -replace '(\\+)$', '$1$1'; return '"' + $e + '"'
}
$kb = ""; $names = @()
foreach ($s in $Src) {
$n = [IO.Path]::GetFileNameWithoutExtension($s); $names += $n
$pet = Join-Path $wd "$n.pet"
ConvertTo-Petscii $s $pet
$null = & $c1541 $img -delete "$n.c" "$n.o" 2>$null
$null = & $c1541 $img -write $pet "$n.c,s" 2>$null
$kb += "cc $n.c\n" + ("\n" * 3) + ("\n" * $Pad)
}
if ($Link -ne "") {
$kb += "link -s\n"
foreach ($n in $names) { $kb += "$n.o\n" }
$kb += "^\n\n$Link\n" + ("\n" * 8)
}
$shot = "last.png"; if (Test-Path $shot) { Remove-Item $shot -Force }
$cycles = [long](($kb.Length * $Delay / 1000.0 + 20) * 13000000); if ($cycles -gt 2000000000) { $cycles = 2000000000 }
$argv = @("-8", $img, "-autostart", "${img}:shell", "-autostart-warp", "-warp", "-sounddev", "dummy",
"-keybuf-delay", "$Delay", "-keybuf", $kb, "-limitcycles", "$cycles", "-exitscreenshot", $shot)
$psi = New-Object System.Diagnostics.ProcessStartInfo
$psi.WorkingDirectory = (Get-Location).Path; $psi.FileName = $x64; $psi.Arguments = ($argv | % { Quote-Arg $_ }) -join ' '; $psi.UseShellExecute = $false
$p = [Diagnostics.Process]::Start($psi); $p.WaitForExit(); Start-Sleep -Milliseconds 900
foreach ($n in $names) {
& $c1541 $img -read "$n.o,s" (Join-Path $Out "$n.o") 2>$null | Out-Null
if (Test-Path (Join-Path $Out "$n.o")) { Write-Host "ok $n.o" } else { Write-Host "FAIL $n.o (see work\last.png)" }
}
if ($Link -ne "") { & $c1541 $img -read $Link (Join-Path $Out "$Link.prg") 2>$null | Out-Null }

+ 47
- 0
reference/powerc-re/tools/pc-run.ps1 Ver arquivo

@@ -0,0 +1,47 @@
# pc-run.ps1 - link given object files with the ORIGINAL Power C linker (copy of POWERc.d81),
# optionally run the program and type keys at it. The original disk image is never written.
# pc-run.ps1 -Obj a.o,b.o -Name prog -Dir outdir [-Keys 'text\n'] [-Data file1,file2]
param([string[]]$Obj, [string]$Name = "prog", [string]$Dir, [string]$Keys = "", [string[]]$Data = @(), [string[]]$LibObj = @(),
[int]$Delay = 900, [int]$Tail = 8, [int]$Settle = 15, [string]$Shot = "run.png")
$ErrorActionPreference = "Continue"
$vice = "C:\Program Files\GTK3VICE-3.10-win64\bin"
$x64 = Join-Path $vice "x64sc.exe"; $c1541 = Join-Path $vice "c1541.exe"
$Obj = @($Obj | % { $_ -split "," } | % { (Resolve-Path $_).Path })
$LibObj = @($LibObj | ? { $_ } | % { $_ -split "," } | % { (Resolve-Path $_).Path })
$Data = @($Data | ? { $_ } | % { $_ -split "," } | % { (Resolve-Path $_).Path })
New-Item -ItemType Directory -Force -Path $Dir | Out-Null
$Dir = (Resolve-Path $Dir).Path
$here = $PSScriptRoot
$base = (Resolve-Path (Join-Path $here "..\..\POWERc.d81")).Path
$wd = Join-Path $here "..\work"; New-Item -ItemType Directory -Force -Path $wd | Out-Null; $wd = (Resolve-Path $wd).Path
Set-Location $wd
$img = "r.d81"; Copy-Item $base $img -Force
$names = @()
foreach ($o in $Obj) {
$n = [IO.Path]::GetFileNameWithoutExtension($o); $names += $n
$null = & $c1541 $img -delete "$n.o" 2>$null
$null = & $c1541 $img -write $o "$n.o,s" 2>$null
}
foreach ($l in $LibObj) { $ln = [IO.Path]::GetFileName($l); $null = & $c1541 $img -delete $ln 2>$null; $null = & $c1541 $img -write $l "$ln,s" 2>$null }
foreach ($d in $Data) { $null = & $c1541 $img -write $d ([IO.Path]::GetFileName($d) + ",s") 2>$null }
function Quote-Arg([string]$a) {
if ($a -ne "" -and $a -notmatch '[\s"]') { return $a }
$e = $a -replace '(\\*)"', '$1$1\"'; $e = $e -replace '(\\+)$', '$1$1'; return '"' + $e + '"'
}
$kb = "link -s\n"
foreach ($n in $names) { $kb += "$n.o\n" }
$kb += "^\n\n$Name\n" + ("\n" * $Tail)
$haveKeys = $Keys -ne ""
if ($haveKeys) {
$kb += "bye\n" + ("\n" * 6) + 'load"' + $Name + '",8,1' + "\n" + ("\n" * 10) + "run\n" + $Keys
}
$shotp = Join-Path $Dir $Shot; if (Test-Path $shotp) { Remove-Item $shotp -Force }
$cycles = [long](($kb.Length * $Delay / 1000.0 + $Settle) * 13000000); if ($cycles -gt 2000000000) { $cycles = 2000000000 }
$argv = @("-8", $img, "-autostart", "${img}:shell", "-autostart-warp", "-warp", "-sounddev", "dummy",
"-keybuf-delay", "$Delay", "-keybuf", $kb, "-limitcycles", "$cycles", "-exitscreenshot", $shotp)
$psi = New-Object System.Diagnostics.ProcessStartInfo
$psi.WorkingDirectory = $wd; $psi.FileName = $x64; $psi.Arguments = ($argv | % { Quote-Arg $_ }) -join ' '; $psi.UseShellExecute = $false
$p = [Diagnostics.Process]::Start($psi); $p.WaitForExit(); Start-Sleep -Milliseconds 900
$prg = Join-Path $Dir "$Name.prg"; if (Test-Path $prg) { Remove-Item $prg -Force }
& $c1541 $img -read $Name $prg 2>$null | Out-Null
if (Test-Path $prg) { Write-Host ("linked {0}: {1} bytes" -f $Name, (Get-Item $prg).Length) } else { Write-Host "LINK FAILED" }

+ 161
- 0
reference/powerc-re/tools/pcasm.py Ver arquivo

@@ -0,0 +1,161 @@
"""Tiny assembler that writes Power C object files (.o/.obj) directly.

Syntax (one statement per line, ';' comments):
label: local label
.def NAME export NAME at this offset (def kind 1)
.byte 1,2,$ff raw bytes
lda #$12 lda $14 lda $0123,x lda ($14),y lda ($14,x)
bne label jmp label jsr label (jmp/jsr/abs operands on local labels are relocated)
jsr ext:NAME reference to an external symbol (absolute)
lda ext:NAME+1 external symbol + 1
ldx #<ext:NAME low byte of an external symbol (#> for the high byte)
lda label,x absolute operand on a local label (relocated)
"""
import re, sys, os
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from objfmt import Obj
from pcopt import ENC, SIZE, BRANCH


def num(t):
t = t.strip()
if t.startswith('$'):
return int(t[1:], 16)
return int(t, 0)


def parse_operand(s):
"""returns (mode, value-or-label, ext) ; ext = (name, kind) for external symbols"""
s = s.strip()
if not s:
return 'imp', None, None
if s == 'a':
return 'acc', None, None
if s.startswith('#'):
t = s[1:]
part = None
if t.startswith('<') or t.startswith('>'):
part = t[0]
t = t[1:]
if t.startswith('ext:'):
return 'imm', 0, (t[4:].encode(), 2 if part == '<' else 1)
v = num(t)
if part == '<':
v &= 255
elif part == '>':
v = v >> 8 & 255
return 'imm', v, None
m = re.match(r'^\((.+),x\)$', s)
if m:
return 'izx', num(m.group(1)), None
m = re.match(r'^\((.+)\),y$', s)
if m:
return 'izy', num(m.group(1)), None
idx = ''
m = re.match(r'^(.*),([xy])$', s)
if m:
s, idx = m.group(1), m.group(2)
if s.startswith('ext:'):
t = s[4:]
kind = 0
m = re.match(r'^(.*)\+1$', t)
if m:
t, kind = m.group(1), 4
return ('abs' + idx if idx else 'abs'), 0, (t.encode(), kind)
try:
v = num(s)
md = 'zp' if v < 256 else 'abs'
return (md + idx if idx else md), v, None
except ValueError:
return ('abs' + idx if idx else 'abs'), s, None # local label


def assemble(text, base=0):
lines = []
for ln in text.splitlines():
ln = ln.split(';')[0].strip()
m = re.match(r'^([A-Za-z_]\w*):\s*(.*)$', ln)
while m:
lines.append(m.group(1) + ':')
ln = m.group(2).strip()
m = re.match(r'^([A-Za-z_]\w*):\s*(.*)$', ln)
if ln:
lines.append(ln)
# pass 1: sizes
items = [] # ('label',name) ('def',name) ('bytes',[...]) ('ins',mn,md,val,ext)
for ln in lines:
if ln.endswith(':'):
items.append(('label', ln[:-1]))
continue
if ln.startswith('.def'):
items.append(('def', ln.split()[1]))
continue
if ln.startswith('.byte'):
items.append(('bytes', [num(x) for x in ln[5:].split(',')]))
continue
parts = ln.split(None, 1)
mn = parts[0].lower()
md, val, ext = parse_operand(parts[1] if len(parts) > 1 else '')
if mn in BRANCH:
md = 'rel'
elif mn in ('jmp', 'jsr'):
md = 'abs'
elif md == 'zp' and (mn, 'zp') not in ENC and (mn, 'abs') in ENC:
md = 'abs'
elif md == 'zpx' and (mn, 'zpx') not in ENC and (mn, 'abx') in ENC:
md = 'abx'
elif md == 'zpy' and (mn, 'zpy') not in ENC and (mn, 'aby') in ENC:
md = 'aby'
if (mn, md) not in ENC:
raise ValueError('bad instruction: ' + ln)
items.append(('ins', mn, md, val, ext))
labels, defs = {}, []
pos = base
for it in items:
if it[0] == 'label':
labels[it[1]] = pos
elif it[0] == 'def':
defs.append([it[1].encode(), 1, pos])
elif it[0] == 'bytes':
pos += len(it[1])
else:
pos += SIZE[it[2]]
code = bytearray()
reloc, refs = [], []
for it in items:
if it[0] == 'bytes':
code += bytes(it[1])
elif it[0] == 'ins':
_, mn, md, val, ext = it
at = base + len(code)
code.append(ENC[(mn, md)])
n = SIZE[md]
if md == 'rel':
t = labels[val]
rel = t - (at + 2)
if not -128 <= rel <= 127:
raise ValueError('branch out of range at %x' % at)
code.append(rel & 255)
elif n == 2:
if ext:
refs.append([ext[0], ext[1], at])
val = ENC[(mn, md)]
code.append(val & 255)
elif n == 3:
if ext:
refs.append([ext[0], ext[1], at])
v = ENC[(mn, md)] * 0x101
elif isinstance(val, str):
v = labels[val]
reloc.append(at)
else:
v = val
code += bytes((v & 255, v >> 8 & 255))
o = Obj()
o.code = code
o.reloc = reloc
o.defs = defs
o.refs = sorted(refs, key=lambda r: r[2])
o.res = []
o.trailing = b''
return o, labels

+ 41
- 0
reference/powerc-re/tools/pcbuild.ps1 Ver arquivo

@@ -0,0 +1,41 @@
# pcbuild.ps1 - build a Power C program with the improved toolchain.
#
# powershell -NoProfile -File reference\powerc-re\tools\pcbuild.ps1 -Src a.c,b.c -Name prog [-Dir build] [-Run 'keys\n'] [-NoOpt]
#
# 1. compiles every source with the ORIGINAL Power C compiler running in VICE (copy of POWERc.d81)
# 2. runs pcopt.py on each object file (peephole optimizer; -NoOpt skips this)
# 3. links with the original linker, using the improved runtime objects from optlib\
# (faster multiply / divide, fixed unsigned >=), again on a copy of the disk
# 4. with -Run, loads the program in BASIC, runs it and types the given keys; the final
# screen is saved as <Dir>\run.png
# Prerequisite (once): python reference\powerc-re\tools\setup.py
# The original reference\POWERc.d81 is never written.
param([string[]]$Src, [string]$Name = "prog", [string]$Dir = "build", [string]$Run = "", [switch]$NoOpt,
[int]$Settle = 30)
$ErrorActionPreference = "Stop"
$here = $PSScriptRoot
$root = (Resolve-Path (Join-Path $here "..")).Path
$Src = @($Src | % { $_ -split "," } | % { (Resolve-Path $_).Path })
New-Item -ItemType Directory -Force -Path $Dir | Out-Null
$Dir = (Resolve-Path $Dir).Path
$obj = Join-Path $Dir "obj"; $opt = Join-Path $Dir "opt"
New-Item -ItemType Directory -Force -Path $obj, $opt | Out-Null
if (-not (Test-Path (Join-Path $root "optlib\cmulti.obj"))) { throw "run tools\setup.py first" }

& (Join-Path $here "pc-compile.ps1") -Src $Src -Out $obj | Where-Object { $_ -match "^(ok|FAIL)" }
$names = $Src | % { [IO.Path]::GetFileNameWithoutExtension($_) }
foreach ($n in $names) { if (-not (Test-Path (Join-Path $obj "$n.o"))) { throw "$n.c did not compile - see work\last.png" } }

$final = @()
foreach ($n in $names) {
$in = Join-Path $obj "$n.o"
if ($NoOpt) { $final += $in; continue }
$out = Join-Path $opt "$n.o"
& python (Join-Path $here "pcopt.py") $in $out --lib (Join-Path $root "lib")
$final += $out
}
$lib = (Get-ChildItem (Join-Path $root "optlib") -Filter *.obj | % { $_.FullName }) -join ","
$p = @{ Obj = ($final -join ","); Name = $Name; Dir = $Dir; Settle = $Settle }
if (-not $NoOpt) { $p.LibObj = $lib }
if ($Run -ne "") { $p.Keys = $Run }
& (Join-Path $here "pc-run.ps1") @p | Where-Object { $_ -match "linked|FAILED" }

+ 915
- 0
reference/powerc-re/tools/pcopt.py Ver arquivo

@@ -0,0 +1,915 @@
#!/usr/bin/env python3
"""pcopt - peephole optimizer for Power C object files (.o).

Reads an object produced by the original Power C `cc`, decodes every compiled
function into 6502 instructions, applies rewrites that are checked against a
register/flag liveness analysis, and writes a smaller/faster object with all
offsets, relocations, labels and branches fixed up. Anything it cannot decode
with certainty is left untouched. The original compiler, linker and libraries
are never modified.

usage: pcopt.py in.o out.o [--lib libdir] [--no-rules] [-v]
"""
import sys, os, glob
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from objfmt import Obj, pn

# ------------------------------------------------------------------ 6502 table
_T = """
00 brk imp;01 ora izx;05 ora zp;06 asl zp;08 php imp;09 ora imm;0a asl acc;0d ora abs;0e asl abs;
10 bpl rel;11 ora izy;15 ora zpx;16 asl zpx;18 clc imp;19 ora aby;1d ora abx;1e asl abx;
20 jsr abs;21 and izx;24 bit zp;25 and zp;26 rol zp;28 plp imp;29 and imm;2a rol acc;2c bit abs;2d and abs;2e rol abs;
30 bmi rel;31 and izy;35 and zpx;36 rol zpx;38 sec imp;39 and aby;3d and abx;3e rol abx;
40 rti imp;41 eor izx;45 eor zp;46 lsr zp;48 pha imp;49 eor imm;4a lsr acc;4c jmp abs;4d eor abs;4e lsr abs;
50 bvc rel;51 eor izy;55 eor zpx;56 lsr zpx;58 cli imp;59 eor aby;5d eor abx;5e lsr abx;
60 rts imp;61 adc izx;65 adc zp;66 ror zp;68 pla imp;69 adc imm;6a ror acc;6c jmp ind;6d adc abs;6e ror abs;
70 bvs rel;71 adc izy;75 adc zpx;76 ror zpx;78 sei imp;79 adc aby;7d adc abx;7e ror abx;
81 sta izx;84 sty zp;85 sta zp;86 stx zp;88 dey imp;8a txa imp;8c sty abs;8d sta abs;8e stx abs;
90 bcc rel;91 sta izy;94 sty zpx;95 sta zpx;96 stx zpy;98 tya imp;99 sta aby;9a txs imp;9d sta abx;
a0 ldy imm;a1 lda izx;a2 ldx imm;a4 ldy zp;a5 lda zp;a6 ldx zp;a8 tay imp;a9 lda imm;aa tax imp;ac ldy abs;ad lda abs;ae ldx abs;
b0 bcs rel;b1 lda izy;b4 ldy zpx;b5 lda zpx;b6 ldx zpy;b8 clv imp;b9 lda aby;ba tsx imp;bc ldy abx;bd lda abx;be ldx aby;
c0 cpy imm;c1 cmp izx;c4 cpy zp;c5 cmp zp;c6 dec zp;c8 iny imp;c9 cmp imm;ca dex imp;cc cpy abs;cd cmp abs;ce dec abs;
d0 bne rel;d1 cmp izy;d5 cmp zpx;d6 dec zpx;d8 cld imp;d9 cmp aby;dd cmp abx;de dec abx;
e0 cpx imm;e1 sbc izx;e4 cpx zp;e5 sbc zp;e6 inc zp;e8 inx imp;e9 sbc imm;ea nop imp;ec cpx abs;ed sbc abs;ee inc abs;
f0 beq rel;f1 sbc izy;f5 sbc zpx;f6 inc zpx;f8 sed imp;f9 sbc aby;fd sbc abx;fe inc abx
"""
DEC = {} # opcode -> (mnemonic, mode)
ENC = {} # (mnemonic, mode) -> opcode
for _item in _T.replace('\n', '').split(';'):
_item = _item.strip()
if not _item:
continue
_c, _mn, _md = _item.split()
DEC[int(_c, 16)] = (_mn, _md)
ENC[(_mn, _md)] = int(_c, 16)
SIZE = {'imp': 1, 'acc': 1, 'imm': 2, 'zp': 2, 'zpx': 2, 'zpy': 2, 'izx': 2, 'izy': 2, 'rel': 2,
'abs': 3, 'abx': 3, 'aby': 3, 'ind': 3}
BRANCH = {'bpl', 'bmi', 'bvc', 'bvs', 'bcc', 'bcs', 'bne', 'beq'}
INVERT = {'bpl': 'bmi', 'bmi': 'bpl', 'bvc': 'bvs', 'bvs': 'bvc', 'bcc': 'bcs', 'bcs': 'bcc', 'bne': 'beq', 'beq': 'bne'}
REGS = {'A', 'X', 'Y', 'C', 'N', 'Z', 'V'}
NZ = {'N', 'Z'}


class I:
"""one instruction. tgt = target instruction for branches and local jmp/jsr"""
__slots__ = ('mn', 'md', 'val', 'ref', 'reloc', 'tgt', 'old', 'new')

def __init__(s, mn, md, val=0, ref=None, reloc=False, tgt=None, old=None):
s.mn, s.md, s.val, s.ref, s.reloc, s.tgt, s.old = mn, md, val, ref, reloc, tgt, old
s.new = None

@property
def size(s):
return SIZE[s.md]

def __repr__(s):
return '%s %s %s%s%s' % (s.mn, s.md, hex(s.val), ' ref=%s' % (s.ref,) if s.ref else '', ' R' if s.reloc else '')


def replace(insts, k, n, new):
"""replace insts[k:k+n] by the list new, retargeting anything that pointed at the first old one"""
first = insts[k]
if new:
for x in insts:
if x.tgt is first:
x.tgt = new[0]
else:
nxt = insts[k + n] if k + n < len(insts) else None
for x in insts:
if x.tgt is first:
x.tgt = nxt
insts[k:k + n] = new


# ------------------------------------------------------------------ effects
def effects(i):
"""(reads, writes) over A X Y C N Z V for one non-flow-ending instruction"""
mn, md = i.mn, i.md
r, w = set(), set()
if md in ('abx', 'zpx', 'izx'):
r.add('X')
if md in ('aby', 'zpy', 'izy'):
r.add('Y')
if mn == 'lda': w |= {'A', 'N', 'Z'}
elif mn == 'ldx': w |= {'X', 'N', 'Z'}
elif mn == 'ldy': w |= {'Y', 'N', 'Z'}
elif mn == 'sta': r.add('A')
elif mn == 'stx': r.add('X')
elif mn == 'sty': r.add('Y')
elif mn == 'tax': r.add('A'); w |= {'X', 'N', 'Z'}
elif mn == 'tay': r.add('A'); w |= {'Y', 'N', 'Z'}
elif mn == 'txa': r.add('X'); w |= {'A', 'N', 'Z'}
elif mn == 'tya': r.add('Y'); w |= {'A', 'N', 'Z'}
elif mn in ('adc', 'sbc'): r |= {'A', 'C'}; w |= {'A', 'C', 'N', 'Z', 'V'}
elif mn in ('and', 'ora', 'eor'): r.add('A'); w |= {'A', 'N', 'Z'}
elif mn == 'cmp': r.add('A'); w |= {'C', 'N', 'Z'}
elif mn == 'cpx': r.add('X'); w |= {'C', 'N', 'Z'}
elif mn == 'cpy': r.add('Y'); w |= {'C', 'N', 'Z'}
elif mn == 'bit': r.add('A'); w |= {'N', 'Z', 'V'}
elif mn in ('asl', 'lsr', 'rol', 'ror'):
if md == 'acc':
r.add('A'); w.add('A')
if mn in ('rol', 'ror'):
r.add('C')
w |= {'C', 'N', 'Z'}
elif mn in ('inc', 'dec'): w |= NZ
elif mn in ('inx', 'dex'): r.add('X'); w |= {'X', 'N', 'Z'}
elif mn in ('iny', 'dey'): r.add('Y'); w |= {'Y', 'N', 'Z'}
elif mn in ('clc', 'sec'): w.add('C')
elif mn in ('bne', 'beq'): r.add('Z')
elif mn in ('bpl', 'bmi'): r.add('N')
elif mn in ('bcc', 'bcs'): r.add('C')
elif mn in ('bvc', 'bvs'): r.add('V')
elif mn == 'pha': r.add('A')
elif mn == 'pla': w |= {'A', 'N', 'Z'}
elif mn == 'nop': pass
else:
raise ValueError('effects: unhandled ' + mn)
return r, w


TEMP = 0x033C
HELPER_READS = {} # runtime symbol -> registers it may read on entry
CALL_READS = {'A', 'X', 'Y'}


def helper_reads(code, start, refs_at=None, reloc=None):
"""registers a runtime helper may read (on some path) before writing them.
Explores all paths through the helper; jsr/jmp to unknown code counts as a read of A,X,Y."""
result = set()
for r in REGS:
seen = set()
stack = [start]
live = False
steps = 0
while stack and not live:
p = stack.pop()
while True:
steps += 1
if steps > 3000 or p >= len(code) or p in seen:
live = live or (steps > 3000 or p >= len(code))
break
seen.add(p)
op = code[p]
if op not in DEC:
live = True
break
mn, md = DEC[op]
n = SIZE[md]
if mn == 'rts':
break
if mn in ('jmp', 'jsr'):
if md == 'ind':
live = True
break
tgt = code[p + 1] | code[p + 2] << 8
if refs_at is not None and p in refs_at:
if r in CALL_READS:
live = True
if mn == 'jmp':
break
p += n
continue
if reloc is not None and p in reloc: # a jump/call inside this object
if mn == 'jmp':
p = tgt
continue
if mn == 'jsr': # nested call: A,X,Y are its arguments, flags never are
if r in CALL_READS:
live = True
break
p += n
continue
live = True # unresolved target: be conservative
break
if mn in ('rti', 'brk', 'php', 'plp', 'tsx', 'txs', 'sei', 'cli', 'sed', 'cld') or md == 'ind':
live = True
break
try:
rr, ww = effects(I(mn, md))
except ValueError:
live = True
break
if r in rr:
live = True
break
if r in ww:
break
if mn in BRANCH:
off = code[p + 1]
stack.append(p + 2 + (off - 256 if off > 127 else off))
p += n
if live:
result.add(r)
return result


def load_lib(libdir):
for f in glob.glob(os.path.join(libdir, '*.obj')):
try:
o = Obj.parse(open(f, 'rb').read())
except Exception:
continue
for nm, kind, off in o.defs:
if nm.startswith(b'C$') and kind == 1 and off < len(o.code):
HELPER_READS[bytes(nm)] = helper_reads(o.code, off, set(x[2] for x in o.refs), set(o.reloc))


# ------------------------------------------------------------------ function discovery / decode
def find_functions(o):
code = o.code
starts = sorted(set(off for nm, k, off in o.defs
if off + 1 < len(code) and code[off] == 0x85 and code[off + 1] == 0xfb))
ret_sites = sorted(off for nm, k, off in o.refs if bytes(nm) == b'C$106' and k == 0)
funcs = []
for n, s in enumerate(starts):
limit = starts[n + 1] if n + 1 < len(starts) else len(code)
cand = [r for r in ret_sites if s <= r < limit]
if not cand:
continue
end = cand[-1] + 3
if end > limit:
continue
funcs.append((s, end))
return funcs


def decode_func(o, start, end):
code = o.code
refs_at = {}
for nm, kind, off in o.refs:
if start <= off < end:
refs_at.setdefault(off, []).append((bytes(nm), kind))
reloc_at = set(x for x in o.reloc if start <= x < end)
insts, pos, at = [], start, {}
while pos < end:
op = code[pos]
if op not in DEC:
return None
mn, md = DEC[op]
n = SIZE[md]
if pos + n > end:
return None
v = 0
if n == 2: v = code[pos + 1]
elif n == 3: v = code[pos + 1] | code[pos + 2] << 8
refs = refs_at.get(pos, [])
if len(refs) > 1:
return None
ref = refs[0] if refs else None
if md == 'ind' or mn in ('rti', 'brk', 'php', 'plp', 'pha', 'pla', 'txs', 'tsx', 'sei', 'cli', 'sed', 'cld'):
return None
i = I(mn, md, v, ref, pos in reloc_at, None, pos)
if ref and md not in ('abs', 'abx', 'aby', 'imm'):
return None
if i.reloc and md not in ('abs', 'abx', 'aby'):
return None
at[pos] = i
insts.append(i)
pos += n
if pos != end:
return None
for p in refs_at:
if p not in at: return None
for p in reloc_at:
if p not in at: return None
for i in insts:
if i.md == 'rel':
t = i.old + 2 + (i.val - 256 if i.val > 127 else i.val)
if t not in at: return None
i.tgt = at[t]
elif i.reloc and i.mn in ('jmp', 'jsr'):
if start <= i.val < end:
if i.val not in at: return None
i.tgt = at[i.val]
elif i.reloc and start <= i.val < end:
return None
return insts


# ------------------------------------------------------------------ analysis
def targets_of(insts):
return set(id(i.tgt) for i in insts if i.tgt is not None)


def dead_after(insts, k, regs, tset=None):
"""subset of regs that no path leaving insts[k] can read before it is rewritten (conservative:
unknown control flow counts as a read)"""
return dead_from(insts, [k + 1], regs)


def dead_from(insts, starts, regs):
"""like dead_after but from explicit start indices (all of them must be dead)"""
index = {id(x): n for n, x in enumerate(insts)}
out = set()
for r in regs:
seen = set()
stack = list(starts)
live = False
steps = 0
while stack and not live:
j = stack.pop()
while True:
steps += 1
if steps > 4000:
live = True
break
if j >= len(insts):
live = True
break
if j in seen:
break
seen.add(j)
i = insts[j]
mn = i.mn
if mn == 'rts':
live = True
break
if mn == 'jmp':
if i.tgt is not None:
j = index[id(i.tgt)]
continue
rd = HELPER_READS.get(i.ref[0]) if (i.ref and i.ref[1] == 0) else None
if rd is None:
rd = CALL_READS # unknown callee: A,X,Y are arguments, flags never are
if r in rd:
live = True
break
if mn == 'jsr':
rd = HELPER_READS.get(i.ref[0]) if (i.ref and i.ref[1] == 0) else None
if rd is None:
rd = CALL_READS
if r in rd:
live = True
break
j += 1
continue
rr, ww = effects(i)
if r in rr:
live = True
break
if r in ww:
break # killed on this path
if mn in BRANCH:
stack.append(index[id(i.tgt)])
j += 1
if not live:
out.add(r)
return out


def temp_dead_after(insts, k, lo, hi):
"""is temp-stack memory [lo,hi) provably not read on any path after insts[k]?
Reaching the function epilogue (jmp C$106) rewrites the temp stack, so that ends a path."""
return temp_dead_from(insts, [k + 1], lo, hi)


def temp_dead_from(insts, starts, lo, hi):
index = {id(x): n for n, x in enumerate(insts)}
seen = set()
stack = list(starts)
steps = 0
while stack:
j = stack.pop()
while True:
steps += 1
if steps > 4000 or j >= len(insts):
return False
if j in seen:
break
seen.add(j)
i = insts[j]
mn = i.mn
if mn == 'jmp':
if i.tgt is not None:
j = index[id(i.tgt)]
continue
if i.ref and i.ref[0] == b'C$106' and i.ref[1] == 0:
break
return False
if mn == 'rts':
return False
if mn == 'jsr':
# a helper/function reads only the top A bytes of the temp stack
prev = insts[j - 1] if j > 0 else None
if (prev is not None and prev.mn == 'lda' and prev.md == 'imm' and not prev.ref
and TEMP + prev.val <= lo):
j += 1
continue
return False
if i.md in ('abs', 'abx', 'aby') and not i.ref and not i.reloc and lo <= i.val < hi:
if i.mn in ('sta', 'stx', 'sty') and i.md == 'abs':
break # overwritten before any read on this path
return False
if i.md in ('abx', 'aby') and not i.ref and not i.reloc and i.val < hi and i.val + 255 >= lo:
return False
if mn in BRANCH:
stack.append(index[id(i.tgt)])
j += 1
return True


def mem_key(i):
return (i.md, i.val, i.ref, i.reloc)


# ------------------------------------------------------------------ rules
def rule_forward(insts, stats):
"""X/Y already hold the value of a memory slot: drop the reload / use txa,tya"""
tset = targets_of(insts)
xmem, ymem = set(), set()
for k, i in enumerate(insts):
if id(i) in tset:
xmem.clear(); ymem.clear()
mn, md = i.mn, i.md
if md in ('abs', 'zp') and not i.ref and not i.reloc:
key = mem_key(i)
if mn == 'ldx' and key in xmem or mn == 'ldy' and key in ymem:
if dead_after(insts, k, NZ, tset) == NZ:
replace(insts, k, 1, [])
stats['reload'] += 1
return True
if mn == 'lda' and (key in xmem or key in ymem):
n = I('txa' if key in xmem else 'tya', 'imp', old=i.old)
replace(insts, k, 1, [n])
stats['lda->t'] += 1
return True
if mn in ('jsr', 'jmp'):
xmem.clear(); ymem.clear()
continue
if mn in ('sta', 'stx', 'sty') and md in ('izx', 'izy', 'abx', 'aby', 'zpx', 'zpy'):
xmem.clear(); ymem.clear() # may alias anything
continue
r, w = effects(i)
if 'X' in w: xmem.clear()
if 'Y' in w: ymem.clear()
if mn in ('stx', 'sty', 'sta', 'inc', 'dec', 'asl', 'lsr', 'rol', 'ror') and md in ('abs', 'zp'):
key = mem_key(i)
xmem.discard(key); ymem.discard(key)
if not i.ref and not i.reloc:
if mn == 'stx': xmem.add(key)
if mn == 'sty': ymem.add(key)
elif mn in ('stx', 'sty') or i.reloc or i.ref:
# a relocated store to a variable: the slot is still a fixed address
if mn == 'stx': xmem.add(key)
if mn == 'sty': ymem.add(key)
return False


def rule_jmp_next(insts, stats):
for k in range(len(insts) - 1):
i = insts[k]
if i.mn == 'jmp' and i.tgt is not None and i.tgt is insts[k + 1] and not i.ref:
replace(insts, k, 1, [])
stats['jmp-next'] += 1
return True
return False


def rule_zero_pair(insts, stats):
"""ldx #K; ldy #K; stx M; sty N (X,Y,A dead after) -> lda #K; sta M; sta N"""
tset = targets_of(insts)
for k in range(len(insts) - 3):
a, b, c, d = insts[k:k + 4]
if not (a.mn == 'ldx' and a.md == 'imm' and b.mn == 'ldy' and b.md == 'imm' and not a.ref and not b.ref):
continue
if a.val != b.val:
continue
if not (c.mn == 'stx' and d.mn == 'sty' and c.md in ('abs', 'zp') and d.md in ('abs', 'zp')):
continue
if any(id(x) in tset for x in (b, c, d)):
continue
want = {'A', 'X', 'Y', 'N', 'Z'}
if dead_after(insts, k + 3, want, tset) != want:
continue
new = [I('lda', 'imm', a.val, old=a.old),
I('sta', c.md, c.val, c.ref, c.reloc, old=c.old),
I('sta', d.md, d.val, d.ref, d.reloc, old=d.old)]
# c and d may be branch targets only if they were in tset (checked); a was handled by replace()
replace(insts, k, 4, new)
stats['zero-pair'] += 1
return True
return False


def rule_inc16(insts, stats):
"""clc;txa;adc #1;tax;tya;adc #0;tay -> inx;bne +1;iny (A,C,N,Z,V dead after)"""
pat = [('clc', 'imp'), ('txa', 'imp'), ('adc', 'imm'), ('tax', 'imp'), ('tya', 'imp'), ('adc', 'imm'), ('tay', 'imp')]
tset = targets_of(insts)
for k in range(len(insts) - 7):
w = insts[k:k + 7]
if any((x.mn, x.md) != p for x, p in zip(w, pat)):
continue
if w[2].val != 1 or w[5].val != 0 or any(x.ref or x.reloc for x in w):
continue
if any(id(x) in tset for x in w[1:]):
continue
want = {'A', 'C', 'N', 'Z', 'V'}
if dead_after(insts, k + 6, want, tset) != want:
continue
nxt = insts[k + 7]
inx = I('inx', 'imp', old=w[0].old)
iny = I('iny', 'imp')
bne = I('bne', 'rel', 0, tgt=nxt)
replace(insts, k, 7, [inx, bne, iny])
stats['inc16'] += 1
return True
return False


def rule_dup_ldy(insts, stats):
tset = targets_of(insts)
for k in range(len(insts) - 1):
a, b = insts[k], insts[k + 1]
if (a.mn == b.mn and a.mn in ('ldy', 'ldx') and a.md == b.md == 'imm' and a.val == b.val
and not a.ref and not b.ref and id(b) not in tset):
replace(insts, k + 1, 1, [])
stats['dup-ld'] += 1
return True
return False


def layout_of(insts, base):
lay, p = {}, base
for i in insts:
lay[id(i)] = p
p += i.size
return lay


def rule_branch_over_jmp(insts, base, stats):
"""bXX L; jmp T; L: -> b!XX T when T is in branch range"""
tset = targets_of(insts)
lay = layout_of(insts, base)
for k in range(len(insts) - 2):
b, j, l = insts[k], insts[k + 1], insts[k + 2]
if b.mn in BRANCH and b.tgt is l and j.mn == 'jmp' and j.tgt is not None and not j.ref and id(j) not in tset:
rel = lay[id(j.tgt)] - (lay[id(b)] + 2)
if -120 <= rel <= 120:
nb = I(INVERT[b.mn], 'rel', 0, tgt=j.tgt, old=b.old)
replace(insts, k, 2, [nb])
stats['bra-over-jmp'] += 1
return True
return False


# ------------------------------------------------------------------ inline the compare helpers
CMP = {b'C$0063': ('s', 'lt'), b'C$0067': ('s', 'gt'), b'C$0071': ('s', 'le'), b'C$0075': ('s', 'ge'),
b'C$0065': ('u', 'lt'), b'C$0069': ('u', 'gt'), b'C$0073': ('u', 'le'), b'C$0077': ('u', 'ge'),
b'C$0079': ('e', 'eq'), b'C$0082': ('e', 'ne')}
ALLREGS = {'A', 'X', 'Y', 'C', 'N', 'Z', 'V'}


def opd(i):
"""operand descriptor of a load instruction"""
return (i.md, i.val, i.ref, i.reloc)


def E(mn, d):
return I(mn, d[0], d[1], d[2], d[3])


def imm_src(i):
"""a source that is stable (not temp-stack memory)"""
if i.md == 'imm':
return True
if i.md in ('abs', 'zp'):
return i.ref is not None or i.reloc or not (TEMP <= i.val < 0x0400)
return False


def emit_compare(kind, op, A, B, T, F):
"""inline code: falls into T when the comparison holds, else jumps to F"""
def jf():
return I('jmp', 'abs', 0, None, True, F)
if kind == 'e':
if op == 'eq':
j = jf()
return [E('lda', A[0]), E('cmp', B[0]), I('bne', 'rel', 0, tgt=j), E('lda', A[1]), E('cmp', B[1]),
I('beq', 'rel', 0, tgt=T), j]
return [E('lda', A[0]), E('cmp', B[0]), I('bne', 'rel', 0, tgt=T), E('lda', A[1]), E('cmp', B[1]),
I('bne', 'rel', 0, tgt=T), jf()]
swap = op in ('gt', 'le')
p, q = (B, A) if swap else (A, B)
out = [E('lda', p[0]), E('cmp', q[0]), E('lda', p[1]), E('sbc', q[1])]
if kind == 's':
br = I('bmi' if op in ('lt', 'gt') else 'bpl', 'rel', 0, tgt=T)
out += [I('bvc', 'rel', 0, tgt=br), I('eor', 'imm', 0x80), br, jf()]
else:
br = I('bcc' if op in ('lt', 'gt') else 'bcs', 'rel', 0, tgt=T)
out += [br, jf()]
return out


def match_consumer(insts, c):
"""the ldx/ldy/txa/bne.. idioms that test a 0/1 result stored at some base; (base, length) or None"""
def slot(i, mn):
return i.mn == mn and i.md == 'abs' and not i.ref and not i.reloc
n = len(insts)
if c + 7 < n:
w = insts[c:c + 7]
if (slot(w[0], 'ldx') and slot(w[1], 'ldy') and w[1].val == w[0].val + 1 and w[2].mn == 'txa'
and w[3].mn == 'bne' and w[4].mn == 'tya' and w[5].mn == 'bne' and w[6].mn == 'jmp'
and w[3].tgt is w[5].tgt and w[6].tgt is not None and not w[6].ref
and w[3].tgt is insts[c + 7]):
return w[0].val, 7
if c + 5 < n:
w = insts[c:c + 5]
if (slot(w[0], 'lda') and slot(w[2], 'lda') and w[0].val == w[2].val + 1 and w[1].mn == 'bne'
and w[3].mn == 'bne' and w[4].mn == 'jmp' and w[1].tgt is w[3].tgt and w[4].tgt is not None
and not w[4].ref and w[1].tgt is insts[c + 5]):
return w[2].val, 5
return None


def push_pair(w, s0):
"""ldx src; ldy src; stx s0; sty s0+1 with stable sources -> (lo, hi) operand descriptors"""
if not (w[0].mn == 'ldx' and w[1].mn == 'ldy' and w[2].mn == 'stx' and w[3].mn == 'sty'):
return None
if not (w[2].md == 'abs' and not w[2].ref and not w[2].reloc and w[2].val == s0 and
w[3].md == 'abs' and not w[3].ref and not w[3].reloc and w[3].val == s0 + 1):
return None
if not (imm_src(w[0]) and imm_src(w[1])):
return None
return opd(w[0]), opd(w[1])


def rule_compare(insts, stats):
tset = targets_of(insts)
for j, jm in enumerate(insts):
if jm.mn != 'jsr' or not jm.ref or jm.ref[1] != 0 or jm.ref[0] not in CMP or j == 0:
continue
ld = insts[j - 1]
if not (ld.mn == 'lda' and ld.md == 'imm' and not ld.ref and ld.val >= 4):
continue
base = TEMP + ld.val - 4
m = match_consumer(insts, j + 1)
if not m or m[0] != base:
continue
last = j + m[1] # index of the final jmp F
T = insts[last + 1]
F = insts[last].tgt
if id(jm) in tset or any(id(x) in tset for x in insts[j + 2:last + 1]):
continue
keep = id(insts[j + 1]) in tset # the result test is a join point (&&, ||): keep it
kind, op = CMP[jm.ref[0]]
start = j - 1 # the lda #n
A = [('abs', base, None, False), ('abs', base + 1, None, False)]
B = [('abs', base + 2, None, False), ('abs', base + 3, None, False)]
combined = False
if j - 1 >= 8:
pre = insts[j - 9:j - 1]
pa = push_pair(pre[0:4], base)
pb = push_pair(pre[4:8], base + 2)
if pa and pb and not any(id(x) in tset for x in pre[1:]):
A, B = list(pa), list(pb)
start = j - 9
combined = True
new = emit_compare(kind, op, A, B, T, F)
idxT = last + 1
idxF = [n for n, x in enumerate(insts) if x is F][0]
if dead_from(insts, [idxT, idxF], ALLREGS) != ALLREGS:
continue
# the 0/1 result is no longer stored in the temp stack: nobody may read it afterwards
if not temp_dead_from(insts, [idxT, idxF], base, base + 2):
continue
replace(insts, start, (j if keep else last) + 1 - start, new)
stats['cmp-inline' if combined else 'cmp-slots'] += 1
if keep:
stats['cmp-keep'] += 1
return True
return False


RET_SYM = b'C$RETURN'


def rule_return_copy(insts, stats):
"""stx T; sty T+1; lda #2; ldx #2; jsr C$0112 (copy to the return slot) -> stx RET; sty RET+1"""
tset = targets_of(insts)
for k in range(len(insts) - 4):
w = insts[k:k + 5]
if not (w[0].mn == 'stx' and w[1].mn == 'sty' and w[2].mn == 'lda' and w[3].mn == 'ldx' and w[4].mn == 'jsr'):
continue
if not (w[4].ref and w[4].ref[0] == b'C$0112' and w[4].ref[1] == 0):
continue
if not (w[2].md == 'imm' and w[2].val == 2 and not w[2].ref and w[3].md == 'imm' and w[3].val == 2 and not w[3].ref):
continue
if not all(x.md == 'abs' and not x.ref and not x.reloc for x in (w[0], w[1])):
continue
if w[0].val != TEMP or w[1].val != TEMP + 1:
continue
if any(id(x) in tset for x in w[1:]):
continue
if dead_after(insts, k + 4, ALLREGS) != ALLREGS:
continue
if not temp_dead_after(insts, k + 4, TEMP, TEMP + 0x40):
continue
replace(insts, k, 5, [I('stx', 'abs', 0, (RET_SYM, 0), False), I('sty', 'abs', 0, (RET_SYM, 4), False)])
stats['ret-copy'] += 1
return True
return False


def rule_eq_branch(insts, base, stats):
"""16 bit ==/!= against a constant: shorten
cx; bne J; cy; beq T; J: jmp F; T: -> cx; bne F; cy; bne F; T:
cx; bne T; cy; bne T; jmp F; T: -> cx; bne T; cy; beq F; T: (F within branch range)"""
tset = targets_of(insts)
lay = layout_of(insts, base)
CMPS = ('cpx', 'cpy', 'cmp')
for k in range(len(insts) - 5):
w = insts[k:k + 6]
if not (w[0].mn in CMPS and w[1].mn == 'bne' and w[2].mn in CMPS and w[3].mn in ('beq', 'bne')
and w[4].mn == 'jmp' and w[4].tgt is not None and not w[4].ref):
continue
F = w[4].tgt
if id(w[2]) in tset or id(w[4]) in tset and w[1].tgt is not w[4]:
continue
rel_ok = lambda src: -120 <= lay[id(F)] - (lay[id(src)] + 2) <= 120
if w[3].mn == 'beq' and w[1].tgt is w[4] and w[3].tgt is w[5]:
# the jmp is only reachable through the first bne
if sum(1 for x in insts if x.tgt is w[4]) != 1 or not (rel_ok(w[1]) and rel_ok(w[3])):
continue
nb1 = I('bne', 'rel', 0, tgt=F, old=w[1].old)
nb3 = I('bne', 'rel', 0, tgt=F, old=w[3].old)
replace(insts, k + 1, 4, [nb1, w[2], nb3])
# w[2] kept in place; fix up the replaced slice: [bne, cy, bne]
stats['eq-branch'] += 1
return True
if w[3].mn == 'bne' and w[1].tgt is w[5] and w[3].tgt is w[5] and id(w[4]) not in tset:
if not rel_ok(w[3]):
continue
nb3 = I('beq', 'rel', 0, tgt=F, old=w[3].old)
replace(insts, k + 3, 2, [nb3])
stats['ne-branch'] += 1
return True
return False


def rule_add_const(insts, stats):
"""clc;txa;adc #K;tax;tya;adc #0;tay -> clc;txa;adc #K;tax;bcc +1;iny (A,C,N,Z,V dead after)"""
pat = [('clc', 'imp'), ('txa', 'imp'), ('adc', 'imm'), ('tax', 'imp'), ('tya', 'imp'), ('adc', 'imm'), ('tay', 'imp')]
tset = targets_of(insts)
for k in range(len(insts) - 7):
w = insts[k:k + 7]
if any((x.mn, x.md) != p for x, p in zip(w, pat)):
continue
if w[5].val != 0 or w[5].ref or w[2].ref or w[2].reloc or w[2].val == 1:
continue
if any(id(x) in tset for x in w[1:]):
continue
if dead_after(insts, k + 6, {'A', 'C', 'N', 'Z', 'V'}) != {'A', 'C', 'N', 'Z', 'V'}:
continue
nxt = insts[k + 7]
bcc = I('bcc', 'rel', 0, tgt=nxt)
iny = I('iny', 'imp')
replace(insts, k + 4, 3, [bcc, iny])
stats['add-const'] += 1
return True
return False


def optimize_func(insts, base, stats):
while True:
if rule_compare(insts, stats): continue
if rule_return_copy(insts, stats): continue
if rule_forward(insts, stats): continue
if rule_jmp_next(insts, stats): continue
if rule_zero_pair(insts, stats): continue
if rule_inc16(insts, stats): continue
if rule_dup_ldy(insts, stats): continue
if rule_eq_branch(insts, base, stats): continue
if rule_add_const(insts, stats): continue
if rule_branch_over_jmp(insts, base, stats): continue
break


# ------------------------------------------------------------------ object rewrite
def map_addr(newpos, v):
"""new value of a code-relative address operand; tolerates small negative/positive addends (array-1)"""
if v in newpos:
return newpos[v]
for d in (1, 2, 3, 4): # address = target - d (e.g. a[0]-1 indexed with x)
if (v + d) & 0xFFFF in newpos:
return (newpos[(v + d) & 0xFFFF] - d) & 0xFFFF
for d in (1, 2, 3, 4): # address = target + d
if v - d in newpos:
return newpos[v - d] + d
raise ValueError('cannot map address %x' % v)


def optimize_obj(o, rules=True, verbose=False):
stats = {k: 0 for k in ('reload', 'lda->t', 'jmp-next', 'zero-pair', 'inc16', 'dup-ld', 'bra-over-jmp',
'cmp-inline', 'cmp-slots', 'cmp-keep', 'ret-copy', 'eq-branch', 'ne-branch', 'add-const')}
code = o.code
plan_in = []
for s, e in find_functions(o):
insts = decode_func(o, s, e)
if insts is None:
if verbose: print(' cannot decode function at', hex(s))
continue
plan_in.append((s, e, insts))
if not plan_in:
return o, stats
for s, e, insts in plan_in:
if rules:
optimize_func(insts, s, stats)
# lay out the new code
newcode = bytearray()
newpos = {}
plan = []
pos = 0
for s, e, insts in plan_in:
for b in range(pos, s):
newpos[b] = len(newcode) + (b - pos)
newcode += code[pos:s]
base = len(newcode)
p = base
for i in insts:
i.new = p
p += i.size
for i in insts:
if i.old is not None:
newpos.setdefault(i.old, i.new)
plan.append((s, e, insts, base))
newcode += bytes(p - base)
pos = e
for b in range(pos, len(code)):
newpos[b] = len(newcode) + (b - pos)
newcode += code[pos:]
newpos[len(code)] = len(newcode)
# old instruction starts whose instruction vanished map to the next surviving one
for s, e, insts, base in plan:
pass
inside = lambda off: any(s <= off < e for s, e, _, _ in plan)
new_reloc, new_refs = [], []
for s, e, insts, base in plan:
for i in insts:
b = bytearray([ENC[(i.mn, i.md)]])
if i.md == 'rel':
rel = i.tgt.new - (i.new + 2)
if not -128 <= rel <= 127:
raise ValueError('branch out of range')
b.append(rel & 255)
elif i.size == 2:
b.append(i.val & 255)
elif i.size == 3:
v = i.val
if i.reloc:
v = i.tgt.new if i.tgt is not None else map_addr(newpos, v)
b += bytes((v & 255, v >> 8 & 255))
newcode[i.new:i.new + len(b)] = b
if i.reloc:
new_reloc.append(i.new)
if i.ref:
new_refs.append([i.ref[0], i.ref[1], i.new])
# relocated words that live in data (pointers into the code)
for off in o.reloc:
if inside(off):
continue
n_off = newpos[off]
old_v = code[off + 1] | code[off + 2] << 8
nv = newpos[old_v]
newcode[n_off + 1] = nv & 255
newcode[n_off + 2] = nv >> 8
new_reloc.append(n_off)
for nm, kind, off in o.refs:
if not inside(off):
new_refs.append([nm, kind, newpos[off]])
n = Obj()
n.code = newcode
n.reloc = sorted(new_reloc)
n.defs = [[nm, k, newpos[off]] for nm, k, off in o.defs]
n.refs = sorted(new_refs, key=lambda r: r[2])
n.res = o.res
n.trailing = o.trailing
return n, stats


def main():
args = sys.argv[1:]
verbose = '-v' in args
args = [a for a in args if a != '-v']
rules = '--no-rules' not in args
args = [a for a in args if a != '--no-rules']
if '--lib' in args:
k = args.index('--lib')
load_lib(args[k + 1])
del args[k:k + 2]
src, dst = args[0], args[1]
o = Obj.parse(open(src, 'rb').read())
try:
n, st = optimize_obj(o, rules, verbose)
except Exception as ex:
print('%s: UNCHANGED (%s)' % (os.path.basename(src), ex))
open(dst, 'wb').write(open(src, 'rb').read())
return
open(dst, 'wb').write(n.build())
tot = {k: v for k, v in st.items() if v}
print('%s: code %d -> %d bytes (%d saved) %s' % (os.path.basename(src), len(o.code), len(n.code),
len(o.code) - len(n.code), tot))


if __name__ == '__main__':
main()

+ 29
- 0
reference/powerc-re/tools/regress.py Ver arquivo

@@ -0,0 +1,29 @@
#!/usr/bin/env python3
"""regress.py - build each test program with and without the optimizer, run both in VICE and
compare the final screens (ignoring the blinking cursor cell). Needs PIL (pip install pillow).

python tools/regress.py [tests/sem.c tests/uge.c ...]
"""
import os, sys, subprocess
from PIL import Image, ImageChops
HERE = os.path.dirname(os.path.abspath(__file__))
ROOT = os.path.normpath(os.path.join(HERE, '..'))
tests = sys.argv[1:] or [os.path.join(ROOT, 'tests', t) for t in ('sem.c',)]
ok = True
for src in tests:
name = os.path.splitext(os.path.basename(src))[0]
shots = {}
for mode in ('orig', 'opt'):
d = os.path.join(ROOT, 'work', 'regress_%s_%s' % (name, mode))
cmd = ['powershell', '-NoProfile', '-File', os.path.join(HERE, 'pcbuild.ps1'), '-Src', src, '-Name', name,
'-Dir', d, '-Run', '\n', '-Settle', '60'] + (['-NoOpt'] if mode == 'orig' else [])
r = subprocess.run(cmd, capture_output=True, text=True)
size = [l for l in r.stdout.splitlines() if 'linked' in l]
shots[mode] = (os.path.join(d, 'run.png'), size[-1] if size else 'LINK FAILED')
a = Image.open(shots['orig'][0]).convert('RGB'); b = Image.open(shots['opt'][0]).convert('RGB')
diff = ImageChops.difference(a, b).getbbox()
# the blinking cursor is a single 8x8 cell
same = diff is None or (diff[2] - diff[0] <= 8 and diff[3] - diff[1] <= 8)
print('%-8s orig: %-24s opt: %-24s screens %s' % (name, shots['orig'][1], shots['opt'][1], 'IDENTICAL' if same else 'DIFFER %s' % (diff,)))
ok &= same
sys.exit(0 if ok else 1)

+ 75
- 0
reference/powerc-re/tools/setup.py Ver arquivo

@@ -0,0 +1,75 @@
#!/usr/bin/env python3
"""setup.py - prepare the Power C improvement toolchain (the original disk is only read).

python tools/setup.py extract the runtime library objects into lib/ and build optlib/
python tools/setup.py --disk also write POWERc-opt.d81 (original + improved runtime objects)

optlib/ gets drop-in replacements (same file names as on POWERc.d81):
cmulti.obj faster 16x16 multiply
c0047.obj faster divide / remainder (same results, fewer shift steps)
c0077.obj the unsigned >= helper with its broken branch opcode ($80 -> $90) fixed
"""
import os, sys, subprocess, shutil, hashlib
HERE = os.path.dirname(os.path.abspath(__file__))
ROOT = os.path.normpath(os.path.join(HERE, '..'))
sys.path.insert(0, HERE)
from objfmt import Obj
from pcasm import assemble
sys.path.insert(0, os.path.join(ROOT, 'src'))
import div_patch

C1541 = r'C:\Program Files\GTK3VICE-3.10-win64\bin\c1541.exe'
ORIG = os.path.normpath(os.path.join(ROOT, '..', 'POWERc.d81'))


def run(*a):
return subprocess.run([C1541] + list(a), capture_output=True, text=True)


def extract():
os.makedirs(os.path.join(ROOT, 'lib'), exist_ok=True)
out = run(ORIG, '-list').stdout
names = []
for ln in out.splitlines():
if '"' in ln and ln.rstrip().endswith('seq'):
nm = ln.split('"')[1]
if nm.endswith(('.obj', '.o', '.l')):
names.append(nm)
args = []
for nm in sorted(set(names)):
args += ['-read', nm + ',s', os.path.join(ROOT, 'lib', nm)]
run(ORIG, *args)
print('extracted %d files into lib/' % len(set(names)))


def build():
os.makedirs(os.path.join(ROOT, 'optlib'), exist_ok=True)
cm, _ = assemble(open(os.path.join(ROOT, 'src', 'cmulti.s')).read())
open(os.path.join(ROOT, 'optlib', 'cmulti.obj'), 'wb').write(cm.build())
o = Obj.parse(open(os.path.join(ROOT, 'lib', 'c0047.obj'), 'rb').read())
open(os.path.join(ROOT, 'optlib', 'c0047.obj'), 'wb').write(div_patch.patch(o).build())
o = Obj.parse(open(os.path.join(ROOT, 'lib', 'c0077.obj'), 'rb').read())
assert o.code[0x1d] == 0x80, 'c0077.obj is not the expected original'
o.code[0x1d] = 0x90
open(os.path.join(ROOT, 'optlib', 'c0077.obj'), 'wb').write(o.build())
print('built optlib/cmulti.obj c0047.obj c0077.obj')


def disk():
dst = os.path.join(ROOT, 'POWERc-opt.d81')
shutil.copyfile(ORIG, dst)
for nm in ('cmulti.obj', 'c0047.obj', 'c0077.obj'):
run(dst, '-delete', nm)
run(dst, '-write', os.path.join(ROOT, 'optlib', nm), nm + ',s')
print('wrote', dst)


if __name__ == '__main__':
h = hashlib.sha256(open(ORIG, 'rb').read()).hexdigest()
exp = open(os.path.join(ROOT, 'POWERc.d81.sha256')).read().split()[0]
if h != exp:
print('WARNING: reference/POWERc.d81 differs from the recorded original hash')
extract()
build()
if '--disk' in sys.argv:
disk()

+ 39
- 0
reference/powerc-re/tools/t_c105.py Ver arquivo

@@ -0,0 +1,39 @@
import sys, os, random
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from helpertest import *
from objfmt import Obj

def make(objfile, base=0x6000):
o = Obj.parse(open(objfile, 'rb').read())
return Helper(o, base, {'C$HIGHMEM': 0xC000, 'C$ERROR': 0xFFF0})

def setup(seed, cpu):
rnd = random.Random(seed)
for i in range(0x0000, 0x0800): cpu.m[i] = rnd.randrange(256)
for i in range(0x8000, 0x8400): cpu.m[i] = rnd.randrange(256)
cpu.m[0xC000] = 0x00; cpu.m[0xC001] = 0xC0 # HIGHMEM = $C000
fp = 0x8000 + rnd.randrange(0, 0x100) # $14/15 frame pointer
cpu.m[0x14] = fp & 255; cpu.m[0x15] = fp >> 8
sp1 = 0x9000 + rnd.randrange(1, 0x100)
cpu.m[0x1A] = sp1 & 255; cpu.m[0x1B] = sp1 >> 8
cpu.m[sp1 - 1] = rnd.randrange(0, 8) * 2 # popped byte $FC
cpu.m[0xFB] = rnd.choice([0, 2, 4, 6, 8, 10])
cpu.sp = 0xF0
cpu.a = rnd.choice([0, 2, 4, 6, 8, 12, 20]) # locals
cpu.x = rnd.choice([0, 2, 4, 10]); cpu.y = rnd.choice([0, 0, 1]) # frame size
return rnd

if __name__ == '__main__':
f = sys.argv[1]
h = make(f)
tot_p = tot_e = 0; N = 200
for seed in range(N):
cpu = CPU(); h.install(cpu); setup(seed, cpu)
a0,x0,y0=cpu.a,cpu.x,cpu.y
c = cpu.call(h.addr('C$105'))
tot_p += c
# epilogue: A = locals, X,Y = frame size as in prolog (caller passes the same numbers)
cpu.a, cpu.x, cpu.y = a0, x0, y0
c2 = cpu.call(h.addr('C$106'))
tot_e += c2
print('prolog avg cycles %.0f epilogue avg cycles %.0f' % (tot_p / N, tot_e / N))

+ 36
- 0
reference/powerc-re/tools/t_div.py Ver arquivo

@@ -0,0 +1,36 @@
import sys, os, random
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from helpertest import *
from objfmt import Obj
orig = Obj.parse(open('lib/c0047.obj','rb').read()); new = Obj.parse(open('optlib/c0047.obj','rb').read())
ho = Helper(orig, 0x6000, {}); hn = Helper(new, 0x6000, {})
rnd = random.Random(5)
vals = [0,1,2,3,7,9,10,100,255,256,257,1000,12345,32767,32768,40000,65535]
cases = [(a,b) for a in vals for b in vals if b != 0]
cases += [(rnd.randrange(65536), rnd.randrange(1,65536)) for _ in range(500)]
cases += [(rnd.randrange(65536), rnd.randrange(1,256)) for _ in range(500)]
entries = ['C$0047','C$0049','C$0051','C$0053']
tot = {e:[0,0] for e in entries}; bad = 0
for e in entries:
for a,b in cases:
res = []
for h in (ho, hn):
cpu = CPU(); h.install(cpu)
for i,v in enumerate((a&255,a>>8,b&255,b>>8)): cpu.m[0x33C+i]=v
cpu.m[0x14]=0; cpu.m[0x15]=0x80
cpu.a = 4; cpu.x=0; cpu.y=0
c = cpu.call(h.addr(e), maxsteps=400000)
res.append((c, bytes(cpu.m[0x33C:0x350]), cpu))
tot[e][0]+=res[0][0]; tot[e][1]+=res[1][0]
if res[0][1] != res[1][1]:
bad += 1
if bad < 6: print('MISMATCH', e, a, b, res[0][1][:4].hex(), res[1][1][:4].hex())
print('cases per entry', len(cases), 'bad', bad)
for e in entries: print(e, 'avg cycles orig %.0f new %.0f' % (tot[e][0]/len(cases), tot[e][1]/len(cases)))
for a,b in ((1000,7),(300*7,1000),(99,10),(30000,3)):
out=[]
for h in (ho,hn):
cpu=CPU(); h.install(cpu)
for i,v in enumerate((a&255,a>>8,b&255,b>>8)): cpu.m[0x33C+i]=v
cpu.a=4; out.append(cpu.call(h.addr('C$0051')))
print(a,b,out)

+ 32
- 0
reference/powerc-re/tools/t_multi.py Ver arquivo

@@ -0,0 +1,32 @@
import sys, os, random
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from helpertest import *
from objfmt import Obj
from pcasm import assemble

orig = Obj.parse(open('lib/cmulti.obj','rb').read())
new, _ = assemble(open('src/cmulti.s').read())
ho = Helper(orig, 0x6000, {}); hn = Helper(new, 0x6000, {})
rnd = random.Random(1)
cases = [(a,b) for a in (0,1,2,3,7,255,256,257,1000,32767,65535) for b in (0,1,2,3,7,255,256,257,1000,32767,65535)]
cases += [(rnd.randrange(65536), rnd.randrange(65536)) for _ in range(600)]
cases += [(rnd.randrange(256), rnd.randrange(65536)) for _ in range(300)] + [(rnd.randrange(65536), rnd.randrange(256)) for _ in range(300)]
co = cn = 0; bad = 0
for a, b in cases:
for accinit in ((0,0),):
res = []
for h in (ho, hn):
cpu = CPU(); h.install(cpu)
cpu.m[0x5a], cpu.m[0x5b] = a & 255, a >> 8
cpu.m[0x5c], cpu.m[0x5d] = b & 255, b >> 8
cpu.m[0x5e], cpu.m[0x5f] = accinit
c = cpu.call(h.addr('C$MULTI'))
res.append((c, bytes(cpu.m[0x40:0x70]), cpu))
co += res[0][0]; cn += res[1][0]
lo_o = res[0][2].m[0x5c] | res[0][2].m[0x5d] << 8
lo_n = res[1][2].m[0x5c] | res[1][2].m[0x5d] << 8
if lo_o != lo_n or lo_o != (a*b) & 0xFFFF or res[0][1][:0x1a] != res[1][1][:0x1a]:
bad += 1
if bad < 5: print('MISMATCH', a, b, lo_o, lo_n, res[0][1].hex(), res[1][1].hex())
print('cases', len(cases), 'bad', bad, 'avg cycles orig %.0f new %.0f' % (co/len(cases), cn/len(cases)))
print('code size orig', len(orig.code), 'new', len(new.code))

BIN
reference/powerc-re/translator.prg Ver arquivo


+ 18
- 0
vim/NOTES.md Ver arquivo

@@ -195,3 +195,21 @@ and compared:
buffer that `:wq` creates, `n` goes back to the `file:` prompt.
- `\x03` in `-keybuf` arrives as RUN/STOP (PETSCII `$03`), so a script can
leave insert mode.

## Power C limits met while adding :dir

- **Keep each unit small.** With `:dir` added to `vim.c` (9 KB of code) the compiler emitted
`return;` in `main` as a jump to garbage (`jmp $a657`, an address outside the object), so the
editor crashed on `:q`. Moving `dirlist()` into its own unit (`vdir.c`) fixed the size, and
`main` no longer has a `return` after `sdone()` (which never returns anyway). Even at 7.5 KB the
bad jump came back until that `return` was removed - treat `return;` in a big `main` with suspicion.
- `build.ps1` lists the units: `vim`, `vscreen`, `vview`, `vedit`, `vdir`, `vml`.
- The directory is read like a file: `open(5, 8, 0, "$")`, skip the load address, then per line
link word (0,0 = end), block count word, text up to a 0 byte.

## Improved build

`build-improved.bat` builds all six units with the improved toolchain from
`reference/powerc-re` (peephole optimizer + faster runtime objects; the original Power C disk is
untouched) and writes `vim-improved.d81`; `run-improved.bat` starts it. The plain `build.bat` /
`run.bat` path (original toolchain, `work.d81`) still works.

+ 17
- 0
vim/build-improved.bat Ver arquivo

@@ -0,0 +1,17 @@
@echo off
REM build-improved.bat - build vim with the improved Power C toolchain and write vim-improved.d81.
REM original cc/link (in VICE) + reference\powerc-re optimizer (pcopt) + faster runtime objects.
REM The original reference\POWERc.d81 is never written. Takes a few minutes.
REM build-improved.bat -Run also starts it and shows the screen after typing a file name prompt.
setlocal
cd /d "%~dp0"
set "TOOLS=%~dp0..\reference\powerc-re\tools"
if not exist "%~dp0..\reference\powerc-re\optlib\cmulti.obj" python "%TOOLS%\setup.py"
set "RUNARG="
if "%1"=="-Run" set "RUNARG=-Run \n"
powershell -NoProfile -ExecutionPolicy Bypass -File "%TOOLS%\pcbuild.ps1" -Src vim.c,vscreen.c,vview.c,vedit.c,vdir.c,vml.c -Name vim -Dir "%~dp0..\reference\powerc-re\work\vimbuild" %RUNARG%
if errorlevel 1 exit /b 1
set "C1541=C:\Program Files\GTK3VICE-3.10-win64\bin\c1541.exe"
if exist vim-improved.d81 del vim-improved.d81
"%C1541%" -format "vim improved,vi" d81 vim-improved.d81 -write "%~dp0..\reference\powerc-re\work\vimbuild\vim.prg" vim >nul
echo wrote vim-improved.d81

+ 1
- 1
vim/build.ps1 Ver arquivo

@@ -45,7 +45,7 @@ Set-Location $here
$img = "work.d81"

$units = @{
vim = @("vim", "vscreen", "vview", "vedit", "vml")
vim = @("vim", "vscreen", "vview", "vedit", "vdir", "vml")
mltest = @("mltest", "vml")
}
if (-not $units.ContainsKey($Prog)) { throw "unknown program '$Prog'" }


+ 5
- 2
vim/make-guide.py Ver arquivo

@@ -238,7 +238,9 @@ s += [P("7. Colon (:) commands", H1),
[k(":wq") + " / " + k(":wq name"), "Write, then quit."],
[k(":x"), "Write (always, even if nothing changed), then quit."],
[k(":q"), "Quit. Refused with <i>unsaved - :q! quits</i> if there are unsaved changes."],
[k(":q!"), "Quit and throw away unsaved changes."]],
[k(":q!"), "Quit and throw away unsaved changes."],
[k(":dir"), "List the disk directory (device 8) over the editor, one page at a time. "
"Any key shows the next page, RUN/STOP stops; the editing screen then comes back unchanged."]],
[1.6 * inch, W - 1.6 * inch]),
P("<b>Quitting restarts the C64.</b> Power C uses zero-page locations BASIC depends on, so "
"instead of returning to a damaged BASIC the editor jumps to the KERNAL reset routine "
@@ -257,7 +259,8 @@ s += [P("8. Status messages", H1),
["cannot write", "The file could not be opened for writing (no disk, drive not present, ...)."],
["no file name", k(":w") + " on a buffer with no name: use " + k(":w name") + "."],
["unsaved - :q! quits", k(":q") + " with unsaved changes."],
["unknown command", "The colon command was not one of w, wq, x, q, q!."],
["unknown command", "The colon command was not one of w, wq, x, q, q!, dir."],
["cannot read directory", k(":dir") + " could not open the drive's directory."],
["line full (80)", "Insert refused: the line is already 80 characters long."],
["joined line would pass 80", "Backspace at the start of a line refused: the joined line would be too long."],
["out of memory", "The 19,000-byte buffer could not be allocated (shown at start-up)."]],


+ 9
- 0
vim/run-improved.bat Ver arquivo

@@ -0,0 +1,9 @@
@echo off
REM run-improved.bat - start the improved vim from vim-improved.d81 in VICE.
setlocal
cd /d "%~dp0"
if not exist vim-improved.d81 (
echo vim-improved.d81 not found - run build-improved.bat first
exit /b 1
)
"C:\Program Files\GTK3VICE-3.10-win64\bin\x64sc.exe" -8 vim-improved.d81 -autostart "vim-improved.d81:vim"

+ 66
- 0
vim/vdir.c Ver arquivo

@@ -0,0 +1,66 @@
/* vdir.c - the :dir command for the c64 vim clone.
*
* Kept in its own unit: power c miscompiles jumps once one object file holds more
* than 8 KB of code (a return in main jumped to garbage), so vim.c must stay small.
*/

extern int redraw;

/* :dir - show the disk directory over the editor, a page at a time.
the drive sends it as a basic program: load address, then per line a link
word, the block count, the text and a 0; a link of 0 ends it.
RUN/STOP stops early, any other key pages on. returns 1 if it listed */
dirlist()
{
int f, lo, hi, bl, bh, ch, row, col, r, p, k;
char t[8];

if (open(15, 8, 15, "") == 0) return 0; /* command channel first */
f = open(5, 8, 0, "$");
if (f == 0) {
close(15);
return 0;
}
for (r = 0; r < 25; r++) sclr(r, 0, 40);
getc(5); /* load address */
getc(5);
row = 0;
for (;;) {
lo = getc(5);
hi = getc(5);
if (lo < 0) break;
if (hi < 0) break;
if (lo == 0 && hi == 0) break; /* end of directory */
bl = getc(5);
bh = getc(5);
if (bh < 0) break;
p = dnum(t, 0, bl + bh * 256);
col = 0;
for (k = 0; k < p; k++) spc(row, col++, t[k]);
col = 5;
for (;;) {
ch = getc(5);
if (ch <= 0) break;
if (ch < 32) ch = 32; /* reverse on etc: show a blank */
if (col < 40) spc(row, col++, ch);
}
row++;
if (row == 24) {
sclr(24, 0, 40);
sps(24, 0, "-- more --");
k = skey();
for (r = 0; r < 25; r++) sclr(r, 0, 40);
row = 0;
if (k == 3) break;
}
}
close(5);
close(15); /* last: it closes everything */
if (row > 0) {
sclr(24, 0, 40);
sps(24, 0, "-- any key --");
skey();
}
redraw = 2;
return 1;
}

BIN
vim/vim-improved.d81 Ver arquivo


+ 17
- 17
vim/vim-user-guide.pdf Ver arquivo

@@ -125,7 +125,7 @@ endobj
endobj
17 0 obj
<<
/Author (Daniel Covington) /CreationDate (D:20261107083851-05'00') /Creator (\(unspecified\)) /Keywords () /ModDate (D:20261107083851-05'00') /Producer (ReportLab PDF Library - \(opensource\))
/Author (Daniel Covington) /CreationDate (D:20261109152126-05'00') /Creator (\(unspecified\)) /Keywords () /ModDate (D:20261109152126-05'00') /Producer (ReportLab PDF Library - \(opensource\))
/Subject (\(unspecified\)) /Title (vim for the Commodore 64 - User Guide) /Trapped /False
>>
endobj
@@ -136,10 +136,10 @@ endobj
endobj
19 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1114
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1129
>>
stream
GatU2gQEI6&:N^lqC0C\(8:V=r"`d\b*A4OfYl(Q^'le!Pb!`7&RbWe>/LclJk\-,'SmZ5cKTD]>;2`FB&.Z;!PFr?oSrt40OFmF7)JkGGWd-s)>,d4:8C;Pc\\AIeI52C^PWt4J%CdcE(:)lXfGj_GR/'mZ_jakU@B#+KF.S,=qAAc>[_PYrX[5&NoqK++umuu$n&a3<Yj?8dQ`lTYZ=]L`XmbWSNU%ps8:&rE1.UmJ2!gZ`_e'\ST;ak&T<!9JTnqg:(qK#`a9GB`K-c4fM.B-G>q@0EIArU\PPBl,*3I6J0Zs/<1^?e#$+Dd<h)Z\N!U\NU3^.,P!W1c*ORl@[nH%(SKENV4Pfa;L.m%t:1`(nr!>N`M2D^%&P9Pu(,8c6fd.I]*GrYN^65hMY@.89ZrL.mJ9#8`/Ss7=%Gsbc_0ui$$PuY0STc70)I*a?RN4jZKDY1W?!QE^p;;6Eh;?#nKf)h4^36oTPZb]R`;Ecmo!A#V/bi%G]6XWT4_V^2+c7;9bb,D>rW"aueaBLIKpQVu:k5-=iS"Mb]\?UP"q%tb\HP?\7&InuqLZd\ZZD7s`bdI!PpT3!>4[sk6"RNY;h`oDq\gMMmi#R3h(2+6W6I<2=tW4/99oi,-!_A"aD$di"h\Tq.0qOI;4^kFpb@-%Vfr3dhUoY\7T.-N%Hgh7]l/P[khF-:Ze$j\bIt*nfqBFkOM'DD20Q:EoPG`.p_NTUb%O+8ijbcn=kL*$&%]JskWtb>Sbd?7[**Cr,oR7+RO)"YIUY!a$Og#E#(*g_L\mQ2]<D\M'?Q<kii+u%l?U'2*i`7a.[d6_Gh1B3`1_V-btcY!FEp-Hh=IP"A'YpC%C*^/>o5@!J"W<9B6TnmV5rC$i#S`Ms2O(%?>a"N7#rR!n+T79Ts"_u&GQel+!P)PjOlsaQ<Y#p,S8G8c+UOX,oDpp=+mCU]Z"p:\h7Y>hfnRJ`j*He&XOj6VT*BrFFY0IPdr!BK]$uP@R8k3?XS50<a[,b+u%Il88K*-D-LiUQa'Y=7;s],=)Q\uZ7RVU;dhaT'=Vk:rtR-f;gLloGf.=p`]r)Wm+8?8>[EH=ctcelp66*4`oA@HB&B%O3'i/D^H]&JD?~>endstream
GatU2?'Eu#'Rf.GgntXO=bOL?bBZ`O[ZbF'9bW]`@:+sF5ZrR^Z@M#npWD,>+'WqF^jm8)]'%jiY-).tn]feVX9<c2LZof]`aQRa-AdEOn*kKaqDRW1kjT]<Wgb@JAVjl85.XbH^q/`l"G&X,(iO6tbM%MS)jo/e=m*1KTHm4h;e$gF]!8fVJb^+L^Bj@jK*Y5:e:".el,iuf>5B%um+3]o8o(.UVi1@AmcV?E#cFqn!XO^K<]J6(7d&QH8&1VNESt^)U=DYGU@8tQH2c](p$=$GX';UeUprIIP$CC;!@!U=O+G5+d8XGVML/AT$EQ2tX;DU=W]99g4\?&qQ+,8[pq\M@1KAT0H?-Uc8VTo&.;,GZchrG;;^;\Y3>$8:%uV9%+P@MI,qWuN'u&[cdB-gS$CW_%$:$W#iYJtGdeFo<!H5]!0Y-kTps*%29a.tWp[o$&S\b:"[l1G[0Dj]1bjKCWBDiak1gA_W9T$2\D*n3&:%?cDjqcaLWbf(p]';.]<0N+YUeIKPFnPekXOT40MS`"EALKga/%<b*jN_WPL4S,rD.Z%s,EXG&M&Hpc;X@:lVJob<qa_<r3j!ZoM4h.[_J+\R`OdogRblStr23k_Qr0&fDNnC^1m9I+6;"i9As(J'2%+Y-?W%G?4JFt,FQ5m[c(<bud!K8LZR:u6_5P<Cgqe5q#GcFD<ude.r,26%-#gD=f$ui=,TJ+C[V1\hCpAM+a>U3;I>]<%&BZs`Bmf2):"Y*40.jb3V.H]WGZpK2^P)_@LMBa?YWSf@CS)BU-T&'eS.d5`mrZ%jg""g6h`TV:]'LmCGS2$2pUoW4&'%e\ZKWLcGZImJe\4%XDs(dgg>8UPG5a<X2K@.A&l.5e/Rq9.:K2ORc)qefYbh:?]]8kcBfo/Kou7bmYWUidp(%33F[^n6^dk"X*ru85liuu^HprXM5cF)tF2h0H0J&'a81PnR#G1CkD2SV(I7kY=NlCjP]G,p@(#C1L"WYI;oG*5%"Ri5i&)[?\-2-Y;,pnd*'%:]KK^M^+VSV"p(lK\A,aT'eA!c#RgVD7f-$D4(EGMKIk@"phdOUL!.gQ'Vh1NhcHr%qh`Gh-3?IMfVVgPR)%WB$)rD@M2)%qo-"k=Q>'*~>endstream
endobj
20 0 obj
<<
@@ -164,24 +164,24 @@ Gb!Sm968iG&AJ$Cm%H5#>FXdPd@;1XcF?R^a-PJ<0/Nu,N%'o9+=m<_?%?is'IbA$3usA.%L=WZgBDc8
endobj
23 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 2632
/Filter [ /ASCII85Decode /FlateDecode ] /Length 2844
>>
stream
Gb!Sm>B?9+&q9SYkZ")X3@/0#JA?VS*9U;pA9/Affr\2LQSBC]`/'PdLhM;:Fq7Xt">];Fg=gT#QA1gA9jbVP1f3^4PCMCG^kWe4IaUL1@C?<`:^i_>J\0Ugb?=h_V?`,jjm.f'C6#,Vo7-O'#IL#4BtsjIXC8L/f!#i3`%)>,@_ENb&TY)a\:>Y5&)m9$>OZ^$rIijNU0f9'd1sj/$VK=/'*kkPofEL`@q]"'C4pA[Y"ihD!fCSn[Y=a@WF8C*"5Jg99bl>s;oJkRAbl=!EUo*LWB1=LjOib9d>JZYBCh'3)/!,G"00e"H>1)[1(+Kq:J&ZI)`19cJrYmh_95V830K(e$X[<6#N+pJ>?=o_FN#:rqSnLX&KIWbJZ]cQl^Ck=2'X?I2$e^2qCoPZGkVC(UCYRsN],,Ci0Bg%s7gtg2a?b.X('r&ZX?f(/d-jT8+,u994)aA/7`XW*qm[AjDo8q#pU$3E"gEu;9Zlo`"Ep^?GOVN;g8=f\.lkJmb!2?l\QmMcV#;@UQ@-mLbBnDiYBQ-b>4<qRLp.J7s"f1On]=:YUBQWc1g<tO2+sl*[-RW5L-`%U7ai'07p+t\^GI^PHN(XYJ#3C\'b9Gb]cq1'b$l;GHp8X-mFMakA9Gf:O)aRkV6K/o/;U-L;,t!Pc9]%l@:Wi:#"5bYqZ;;$grL/_jlr,Tt?VAL`_"hODBT)XOl.C@SN*qnk$8XBC_%/a6AMN0@mS4jeCJie(CWo/;h=2=n\[=LJ/N;)>UOd7P,.8jM!Q+R?Y!],@Qa@J2XAn+\P(#/DObWkcu<B0g(.6,sR@9&$+?=Y$dZNk_,=qG8kg/&[_FY7XcUs/ed-_ppS^8O=U%"%[TV$1qh8uG`LY0djVMg-HhK'e*C\I?n&o'5oHifkMTfP@DUs57jDejlEmIRBqHE\@=1Ou*R$jrX/=n?jUd5ec$\d>@kq_hF>EA?W>%oO`e6\Q6)tqrrr-g/j8Q(\PW8KUMkb'ts7,dQ@J8kC&7LuO=9*`^M0J(3]%i@,RiI>1)>/ZWW9JV$NU*R%G$A8=3F`QWo:OT@9sk5VaP6(@N[IG);tRoUHRl>L;h7TaAqTYSg*?11Pagq9T:qU<f>hF4UoY`D>XksiHk$0]UYCQOj"m$"$#L>XdTj'1F]Ga3Br0rg9?FG/eUi#bbmkkY.k7!q^#W:\nS;6]@Z@Xb((m.m##hMEHlk.Y5ERSqQtC-r3`*\0.WL`f1a0nNQ,p6GgiJ>j.iF(dba?Te^e<\+SWLi4jp8_JKKkT:]<fgRbYQnE)?\lk%Z&fBS?BrJZ&/`]HndiM;8#.]I9aUT*X.V?jaK/g:*RT(#FJ5;Li)\8"^JaRP:B&h0G^uM_r"pQAt4n8F=Ml*1rd/AlQ10&A-uJ$j-;1?GittQF#nQj8o0LljlbZ31BXlS?$2sSIp,cDWbSqV=4#Efeojgs,1DF==UA]k.8PiOM:i,J2%pEP+g(ZA8_AW/%Flq*TM:dV$[uMKh4\P7[$ubm,:Hq;;q43m=ul_N!YSa7=06,pU=Xg<0:C=-)Vn&'@V#E;gX2k<'[$d!W#(Du0nj0e#Oq/N827%"!_,cbs5K`^f[)at(G;ID*l3W$LX3H6i#@\Q'MH7Z,!i>,bh7Sob`Kk:(s'QB.R44H[RN<9r&Cr/r.dGhcXa_G+R_\t\@_ceo7b-D0g`o+-N#eErV#LL:<,O1rXoN*4=eZ:$JgfZ(DXe(4^odh.Zf_Khq`\X$esTIQ_cIUY%t`eiJrfHP6gq@.cBb<?*P5i8378L<Y$!QoF+S0DS9n"FJJT_D5Hu[`[-CGOj#1g(:u)'J7gORVD>$g7EQuPI"9GU+5C-O_ImWBTD@I6Qfd_)=V+hZGToimqOf*Y`ekiPi;Q_nd);<c5ndcZn;t$jC[<1jLH>=)M*C=05*IHNk"pA%o^k.h@%.U<@=`HH^6GsfQC\;O,i?eN5tf"C`AD]#CfN=J3N6"uL9qNSJ6q@;].'0YCKkgtka<J$Vp6\ZG+]&a)VVh,4I(muTBOj&1c8KK9p!Sd:P]u=S'pdPNu49HG<ET'"Q<'KQ$U.TDTE]-h/62k)8kqR9c2t/+bFCf'D@9@&S6`7"u7FHa<#??(-,<u`EKW-Mn15[@EfrDq&#!%6JF>PN=^>i=O?->:/g@D]9F#'Ia!j3j14l9l'))L-*_L%0'<VVkgVs?f!+515#S90aTo+"0@H-7C1?pn>s;^=XO6[/LR;$)S<&71/<"!L@Ki$$C"C7UGRTJ(FAK@N:FM<?3K31,JTG:j\+J4qE5_fi7GBK--kq:Z\0AG-)"$h8$,DB2P,9gkokqfEEn[n]cl`R7#&/`loR__Y$Z.li\:0(`biBb17aAt02e>EqkCeH80r-I4pb`9Rqss.a`Z,3i]5@,qnc!,;Qd=?sRf"2A<JL.%1R9fY@mO;bp9V2T<V7\KO/=>]n'OmUH2(!deb_tiqL3D@.Ym$6hVGmDRlbqiL6?(ag4/5?*Vm5-N)`lkGhg:"(iq@Z1LE&QW:tZRXXN\c<74GVaeh4t^LBOhe0ZHd8J@9@Y.Y.`hJ$h1N@\hFAo3c@en67s;:70KNlU<Art?'5`_4:n!\G'&BCZ9*Vk_$tO$&(#0&elO"NUGu_q)HTHL$;5^tHI'"/e(O~>endstream
Gau0E>B?9+&q9SYkYuEL.Oc5;ip&WdNcQ]5G]OL%fr\2L(Q:g!0k+Yt9XMS6F:VFt";9Ci<,=Z*I@Iruh4oOYs"sQ#hZ/%k_o3u41`b^/cj[mDq(:CGc^rI?2\*N?'rtG<9N?-c%GJSW8,>J$W1+"8n;@`L?@iWfGRV]__):+dpl`Z+/-F2V]3mAkQ3L"U(?Rhhrf62$M9L(\17XNU=7;9&g"?-1KkrHM/-e&L2+:S`a-_FPR?h@ADo4J,.:ImVOgV/4Hd,Sb5Z(SNc4mb?kaK(uTMDYbc<S'\6LJZ>UIrP:f$])K1n\0CYE[64_o8gTA@N*&VG]_#f;RWH-QQG`7&,H?R/FdDYLID=X;SV/N$j&8-NT8<;R\nm9?Kt'CW`*.'@p??5C^59?[TMsKuN,*k<Fg*k;*An:6U0W^:ma_*DieKhSHO/<Uh[FKZQ_"Z@keVo@I=8q$DB""'^o2ai-(P4BCAiOai8,M\G#!n/r,`2ME58L`jL0.,f3e2J+TC:PF1'l*-PnCm>@9KIf[F+P/81c#RMF6j-lE(Qe/D>f,&*!Z\W#=%GK*?mU*:9Vc)pCjeQ73JW\dO,LJ>chYf]%];A]p?>gT"2GT)M1Hlg8,lroKh8'/1kCa601#5dViQD#!/<]'dcboQs5QeXFUV=f'O*F87gI#L#h1E8qs=f5;HAP?7Kt&#KQ&Tr$k$3VnsAI,.Mq08I#5Qd2ST$Xr_SNl`cean<82e(411JF;4,,As(KIsdma7qHu+8,<Zb<%^9nK>P]Af0@?r!c2QK1g:I'%R>id$jHVq]pG`6o;I[HcMf!-AN@?njrKUABU>Bs6N\pZXf5ERd(Bc(<,4),AK2>]t+bG$n`?>=O@8&*L$.'o6ebE+@*lbli.P[K<?%$um]i+#p[U?,gE7b4I(G,H%$aGsU^5"0/jZdeTPXZ1Ve6p0=NTlfoU)72pq\TjK@b+t5/6D="DeZC*HG;K8JpI,*TS89&gi]Z(Fs)N*Ds77c1\4Ski"DkD=G1G"://3k6N^_>\P9!L72,ih>EC4kGZVLPA)8XDXic@Eeen4WS,'f`Ogu1r+CPk<EqHsW\[*!"dd]hCKI@1PdHo1)2LAt$h'G^<b?*,MpE3,E.<^FeLni#_-1#%GS:YCE0ENEn=hsU>uRAT6oooq"_PV&kY&a_jtKrVdki%(+9/Un;3Qu=YZA7_,Ef"lb!HXV`pFI5UkKcHjDII/Il[["AHT%gd8U<[BH$LTpoNjT:*HtGu_M;UB'P!2;dH^lJ]*"`*?]%uegR!<'%mDK^k$gN8X%;Y6t4<.".oZ3>V[n!H2N"5+8!<nJN!]TK.hah:flTqml,eOq@G7KtcDkr8b)p;]&p@*RB5;u>5JO1281@Upn%%-'G_X6qAA&u6OXC[+*;a3\kmFW!';eR&e6Q1qua)2!ApH$J_4Q2?gq$E++kg<&iCp5d<&3qCPcPptX*^$$'%`?kQg2.@[!D0M(fJ>rXl5N!iGp_V?M#C!sBL:IO2/!R.h]dk*M,L8X5@uIh_ZV\+'C^M],$MI3TjR*pEWf<kD>APrbNJgiF'#[[Gl>$./"7#pW`HO=*QXkr2m-@9j$UB>e=nrtg-,DY?:U`VFS1g"VO?W_ZB()C[G2d^`6>Y?kj8om7$(DP<Hi/1,HY[R2$n_7L94WmB1kaZ=Mh:`8maWHHB,g*]TZ#-DM[8Q[Mom.%o?2\8V]Rl9+?.69\5T(ls]1_4@@H-#0]aBrl\c"0"j%$^.6gI^k\ufce#C3>DDnph0'-W;nn_SDb5q:b`XfC,S]2pMM@t:5s!3Ko@ifp8%R@\.%:0#DXP5Dh3+-V6+id9"M,D5IseiAdBP]e^N^*;j^>$;r`^$1]LI%-#Ah.O"j/6;2ch+$P&Z_Q%Vut?CdZ$p+Q3o"mQE$2mN>_DeEr?@(@rMqaOh5Ar"?[7VB7p4K`%UV`<,<3RD1[eiicM<Ylle)nd0Gs;EegS]lrVohUuI&I\OBp'AMFCgtpj@$[+?8*_Fhojti!g&7J"=7s._u3R\21K1C$TNWSs#s+^>BTF^]RS\".ZQ\/FHL5P\9<Xr"7o&+Aob_o+n)7#MI=EQH`kTn,R0<*AUAaJ:QcoIgFQg"b:4*bXgH:M\>c:'maMg[@EfrqfUXuUMp>Bn!"W:!aNH^#NI!0-B)_GLogA4nm-H:e*(bW9+oc)/^KO,2(0LXM]nL_H12G:\$Shs.S2CDMRH+>iUBfCi]ubo2t++1)63F61V8JCoU00U$Muggci$m)V5n+('B<Jus]NY==!SS7/fKd/FUJ-gGJ\ln,H\M6W;en+s<W\/B:@/QKXt%mC?f7oC_j$*IdOoR4`Fh70c=5rV<f\<s=[14r=cRRqX`^:YF.L(=\PU)g>\FK7mtWG!n2;%=8:Y`,i1b,mL(A5P)c78`n7'/XiBPk04h1LpX]W(?BE?aKnN6ej34/$`AJ.7et^Jr0Bp%OY"Tgp'_#W(T<j'=[k-lNj9Pc#fa):2!VoERE<j^^fZhP0$r=c"Xn!Nm9GabjLG::H!pnGmZd6J3T[`9W>5_;qClc+HNLkW-[V-V/'\@*bm\:BH1e.pD!7FBqtJ&Vl8_A*NtE[lcr_+T"DjZ>X%?FK#N/eAI`[91Y-S",bsg7QRmD6g8FAcV=d,OqX86]\Yq]S@=:*&Fl#R%[`Z=/NI0[br@Ka0hP)9@GPZg"-`.I,go7<Rk7>2#R[1]<F'^9lI+3KtTqPCQVNPOP:Uf0Qm.bQ4oeXQL78jF]7J387TW%[_k3DV)83YXl_"A^eA(>DN%@_N^^JPH7N!=^D6!o+1?`ho0HI]":4&,!`8_gI_Rl+od8U4@>cHHs*ZqJU'K.&_V)tbAfc2~>endstream
endobj
24 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 2291
/Filter [ /ASCII85Decode /FlateDecode ] /Length 2164
>>
stream
Gatm<D0+Dj&H88.Z-DoQBSRGZpl4p`9q+_#fCmWL+a7u`HkEde-ar,=FpS-I<q7aNR,VG>J.^L@mJF6<5fK':hq\k%dhG8Q)JD0*(SDY'!Rkt+3B_XK.=Q9T%hlo3eeM-6-C+l'cX\I1[">o(^_eLe(d.[=/6Du<MS]N)6ftY,Jn;_8giJq"kKN\a4.&4IoY'2?A2;^NbA^'K2e7buNNGG7O:?c,3`/T#$!.jI[2i`cMIp%PKqF*:R.?2ufWFJLd8]WERZ1*fT#LR?AQ%lcR/4`d`?pU+T'.J_@inKVp5l':P+tW$Et_2bWR>!sjf?f<(B3CU2S`VFG)H^e6tjflPn"JL6PBjb@.5nEABVDUlY<Gr35&Hj=$?lSq'eu19!D]tQJQCl]7[fJ@XlL*'%6qqn'1h)rd&ZFrI+jTJIGtGg_JcZ0gN;=82hXKmlrHX+XAdtRgZcq1@WcG4/G;KHedpD(G(JBIR)MJpF_aXVi#VB!#SrsLpE*h^'epI(]Fg(k9*3QY0;:b@/NhP'\!6#e8FY.4g2Q@6e3fB2+o"<Nh4#X;[s6'.I$tkg)SU#4i&uEQP!7.YM6.Ug!e4?&Y<uql/o5Qf'P'KRRIpk*[f8JfHR.hQQfq;Q]-DcoI/6lqE1UrTh$W/*#U3_;3<De84^Ed'J0/)_!`f]2DF$q?2?#t5)9nfD;&f0RtbD$[N%Kd6$UkQla!VK[hAVX;P-C59Nnbe55d0"4:$!_Qm_2Cb_JGrn"e*rP/LYbnn8<Dj%?T3ZG\c#n=1gqfV;dsqC[s*:iuB<_m@ZF&tgO5\k>Pi>ojUs;j-S&g,?mR]tlk=iQJBcC`AqR:Ga"QAUXt3_SFD?+A:]aU+NU:ijU<hI@)a2G<,PPrspK7XWr:_J7>RVj63-qL_DTQDEja99NsH[9ILK?rZ&_Er'`2<eZBR/CKLN4/o"+_\>o5%"@;jhHn<n:P)3I;1[B[]:!Vd>Fki'Zo@7TiR]9g2$.eoNIDR)n-q84Mpb;R!$:U^X/"2.$\jTI!9i^*nlr.#,kR.^WJF:a.+kA>oM`#$73I,+)pm"`"&CIloN%UfYA`Q1-]>6t]P)uIgokm;eG,DMJQD<ZS=EL(mjI'8i)qb&.;j*ph\Sn7P!<t7>E-jUY^O8g/:4Eao91?)<0*EOW=<Oj<YWKG,*W5PRbb<8eX'EW]#P`oU!?2@Wp'.tEVjj%TB-Dtn^8)L':kH7e=U0CTfsCs+]Dgs2I2'41$,bn*W$4?25ODAg@%I)0Kd]VAIgm5..P8bXAQmQ@nEJU%?NTK7J&*O"F"hm&O?JN#C21L6U;jWi>XTI\^5.(8n_3TtZ*gZc_D5=:#p=`2m%TY8,lVh<2;Y)/74S`sjf\*#DHccX6l@"Rk%<jn\T%iT`(5H>PQ;p1$;(TV\fpTk]g!P`eGCAo.*X^sClJfA%]:LG&H86g%u.@Z';Oni"N-eGR[@40D">^mY`t(S1<4SSai*TuE+;m0/i(H/^;rQD#ubI+/s@$l""oKjJ-4A:^9o`Ei!!G17n4UD8RA2P.#LoLV^#F)1lI$>7+F;IH6[KsT/K6tm&--E^%Tni_[&OTI^Io!9`5bg;q%-VZ]q0Vn\A70;i*"9</BM>bC@+f*9TP7TDRc=+*bquU`f)Ic>HMYfiKX))S[fjC_f/r4EL_6Y%p+mZ"?Ws33Ej%I_ATG_G<KR!rooj2B("?!\.O90r(B]a9,G0XB:-E_EpgFk>9LfO&_%t7:fXK1f&2l61/"Q&[h\">mYM?cXfRm<K*a?Q"7U<=Qa>\*$m4hU2bhr*0+r4IjhRU?o/&T=I-Iq$/&^>mM0K2Mq,^#"[\\PNkNNN^3:iV?HBq.UT_?NUX\>FbGhNpXJK>,p?$b=,-:?@eJ0XIfC2dqpV'"]N4W^UUHNsX32Xn("\`XU4<bR_kscpm9N"qe0._Z7%PeY>nR7P4!JMB>/B1F*0]HMLPW:(>FBO,'a(8\8/%fXCXh3JqL(S"UJ8&c\7h2DgZGcn<(*ZSWF"G%q-5@^BMZ,(Ta&"ZS1m:R&&'U)@9pDia6:S'dnLO4C=i/SAkQ+NaA\[ILl1KMVHhQ_#JG"5o^2jr'TQQPfLp;^3,T&,=+:s_uL0Rp$8<&nGo[Ac5nW?oD6TTD\er79\MB9TJHA2Dil9e-`-JF>JL%SS6T4n:36IsQnkfYt2M7D1^?JV'a?J]"<r!%3[CK7-t%GkDnUUR7@gV8&CJ;$*-[..T%@\?n<h?pW5D-n\N>(%Ea2+DsErV7p,P\Z"Rm?Nao\mCsuT2]<M3V5D0pjCqRPQ(X\e59<~>endstream
Gau0DD0+Dj&H88.Z-CM;cjhp^"d=>t[(3&X]t60saPGV[?]6R%M/>8=%,p+$8iARE"2X6<KRg]gdd#N9ZSFed\"Q"a#iO/;HIi?<-B+g0fU<8E5N24tm-1TrilVKnX^@$&Nc#:&#1W[N1T=aref>\\e;0d(TFjk#$=ZcgMFA*b=A#6)_gAoV,_[lh%EM`j4i$9P[(g=n1:=j`XtAIS1K<-@rIL/;bP$&q(ii&)VO@:r[4T`6Vsj?L^HJflq@%MNV:FG1$&2WXC!%06L)Tt^Vc/bAfI%Z9.>o;p?3gqm_mO<khja\LfPqAF3AuRn8t..73][J](uR=]aRn;V2CrBoN]FI$Z9q^&L?;[]*Ls:m[.%Q^q9d_Bo*?hUkR&qS$'oTRn<'3\!2X'^l>f?_gkX%,N0t+P7u[#qkSebSF'G+Tbp`l1:r1Sn*r7S?f=SN&g'7.<c2;5BHO2\fpX[o6n%\P`7c/,lgeg(bWAF9.`!k!G4He5@r;HNCB8F3R_p+Gb'b%#X^XWgqG$q_Hbn\E2_2k[?D7dpNg/;X24TXbo@h<8b8Z<4l8uD6Kn-`'qHIknEgp%oXJO6sN=BfMq:(Yp/!]8.)/6Vh*E=D&i$4[L).1;=Uf.H3/I1OeWc=/>1_;VUZ:<RLgO)^1C9XfNmM/I)FhcNRVluI#1J5$3(%kY_?>P\'GM]^J)As<[d*^e*(bDAdcNXNtgfS`9s\q2@?6i>3gE!;G*I7nc<E@Fhb-F?t,KXt&pgCNa2.,NJG%Rnrb>7(:<K!P/R81X;0V]Y%k!)`.go46O.8JfJCX]dJ-?L#jV\CZEF3)U1:,&r/_4<`qqnRu![^<>S#L9@t)=AYUJOcO%,1p'WUQ\UeN`"Pk>og!Wb!rap*Y>(+[#G'@W2"!J;<.Gc0E3gCoR7Cb4/?jfcY;e1`QnbDIS:/AkLGNdT,(CJ^2UEJr2*l;G-Q%9ArGmWTEOins,/kDimigJc-[IMIDiNsb;Yfo2D*otYlIDgW62@b]\fMrpN=7:T:,Ob=e$?A&[5PZtG)aYIU$ILYBfc8pc1"4D`TYT"lgh(^W1e"r&"Z^%ElbR',pCs6n^=P.D`bG:as]25fTCUh!Zo9jWIT!0WGV<LVn_$F6-[j!'7p?G?KsDFE?G%[B'*uNj"r^@j>FW%]e;"N<2eAVS%WqU^duah5EG5NGLbgA00kAY$EO,#5Z*._&t8ks7mE%:'d#,b<^!O)V%!e$)[Sm_PpG]*CqCLaKE,Ws!C<;GW?W3FI"nLc)OE1\86s\[iq\Wj=(<-#]F'p](X&_)b6D(G<,Nj?;orAY`g!DmnnKnkm?=Za56XU0oGLMZiF$CI&R.e*D6CSZ;3j@-%8h:u+/0fA^A#8A[T%BG7V6F+6HJ'19j29D.@n_g`Wmb7(hLgcr&N'K(Y(O!/E6`Sr(QJR9b&R:Jf;.]$4tXZe:`o82`tCtpZ"P:]h=b^,3+cj,n@"9S4f@prB&mI#0VB[/,KB%ge"Z*M1p^Z?<JP?;Rl3CprR!1fEVeS3Nu7GL?7bh#0'8i1/"'&<[84N/6N!3=Qd5.)5PP64?'#[7nB'b.1VfD<P*;p>N%]&jT`i,F7JlljX`XR:joZW=691>ME+%WTFi*gp2?i(MN_aq1I#K'@J"QV=BU@;'hBfbo6G<T/H)sS1\YbD,bne@9frII;X+-3qLAMMOatI/RQhk6%hB0U5]$q37VZ`J'I0t77[RP$<0Zm9oSg^QYG)s@;HFG[^?/pKU"N9V.QLWdQsV13A^uP?PWtH[Or'BtSru4\q5'7EdMS#FNi*N\2Tf#h#:2hBg[WMN%.!g.2b-LJ:i&ha!"[*\V`g<t@PEN@G:ZC8>`#pVKH@]]Hg.(='"\f1ZE.,5@oZh.M3grgLU`P!$.[c#'oT>3+0(WD]ClT7q^29I7bt6A;A$>Xj%_m%2L?9tm74IF8W\26=5=-T@=jJq,!<MB^cs*+@+llNnqP?rYgBA>OUljUiW8&F.)HSq^U2c(RH@"+e%4N;Pf\8a<Cq.4(Ka?k8P=fcQ05!F"C6.`@@2GjHXJ\ekn&'A2L763A1.;(Q?!BmmXVBRlMUGQ/c&pBamp:O>$8T2;\ihYAmIs3,#^/]9sZO<I5h:'PR+7Ucg.%TVY2c`B$5bM::;LWh$!b:[WL>i\nbAm572''!CTNtG5~>endstream
endobj
25 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1179
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1299
>>
stream
Gat=*968iG&AII3llcVQ,JaB;9M_d"Vp6QI'<+.j!g/R5Rj;);P+qsP0#35g`o0TJILt5R\%CNMZ!ougLY;]!!V",:Y&>13#?DdO*q<Y2$#We-Ij_?F15_G%Xdo0N`;tVMZ[O_@X@;kHE7(G2krpV*,AYY1!=N$E16o7t^k4ZFX[EU?G$-<O_WhG.<sJ[=`-"W/730f>IhWHcR"!"":BCaE-U-+7;W?%Wnm!<]&#'&r?Kh2Ca3n(r!"F@63Ns&jR*Xo06K6QSp79jR_W$B_Z-t^eoN+32+(J-:D*sS"SQaU8T(B+T!*)cm$X1B.?4%0O+t;-Q%`B-8)8R\fhIa;Xj62^R`k74\(UDPbeEWsi!CU8GJC?hl_.Ef9I?>0Hf7;17bq"s9<Y2i:O>C^;,BS^nLZfA%C#C5j>EN!]<fkBP'.IT$D\eScGHfc^q-55R&1(:</+j<cH0C:S>UIcnaM!s,Ss<cVQ)XNg4FPp4<'Q:NQghAF'`n3rqUTG&R'(X9T$FU\9!MFIVDe[]PUP+n;!&39!5hD*XuqS>e6HLH"#.Vm1-d!-:%A/iRrUqITgp'l!rX%s82'CL<m/J<?./u]EaqaCE`XX`Z/DU3p!Lo5FhcViJ;54;J5'WhjS5bFS_hLMdp-O40h.0B>ss[sMQFA.R;L5TB7EBN([)ZlqLZ&NKMn3hRm;LZ!>,Q>5EdoB:=`P#%_Zg.l<S,H*\I%on>59Tm^oVfUm]6IOd5F^h;W%RN>KS2lc\R*8W%W&E>Lq@am4%t^g<:(RfP2$?64(?/S:Z9l='PYG%M-$Rq"%t(3l8$.a@2"!p<UMY8-sIs.1P_FE"M(5XOa+OTQlG[DN(@p0iRMqf&k%$G1nYEt1jDM.77KJWO%QWZ$HYZ?ATZo9m&XhO=-^H621lMUAAbR_sFCYk&B4!.:]jh]=kigI^3;R9d(7Z=.Zeim9SiS&O"<kYV2:Rs?$qrF!#*/q)u4]>i[8q4C)!lQbb\Gh9>DW`hU;]Wch@$=:PPjLAR`p48!uR0>1VCRk=I"s#J3a+2,beBZZ9/Qs7&L<Siq4PA^4lBb6in]&N%aMQ1EdgA#ML(bK1RcS+gT$dNbC,rKAQRToJ_VCKjeM>XC"RUCj:n"/Ofl0ZkIC(s%0a`JCZRq.e[D7XCnHObe^ON]/pCUR<Af)lp")X!LZ2~>endstream
Gat=*>Beg[%"@rS^dB05**Xb4=814T8XD#Ge(EDR?>,6lPXqN<dWW<25ENIp;,_?)nW?7ki!80B%KQn>J(4J$$\2TN0Y=&E?!(upqMl*0)n9.5rZ3pYZ.V[<Y`V!JTh.eKs-=2X!,^'b+$;Y_^j)1\YRqN?,DWfX$BY\;+nT1MIk!)0j#0R+3@pe1H"$3'5s3a*rO>;-AIfcT,Ls!]jH78$%\3T06E0"../#fCBFr0,^AZ+`XH04*p&e8&#l6e2>bOBOMij2[\k1g,4pI=k`(SnRO;leAguSJULrdo2n@j+(f^NWnD468TPM7C_NB[R2d5lO%Jpolh\;5tmcE:DC/-[t`)]2pn3IaK_0s-]:-Nh[`WHkJn&&HY+lEr'&Nq<d"g&p63[H+uBf#+X(G6f3Hh?71fCf"A4[hZ,&"jNI*VIEn-+;aohc?DC+<3gpQ5"sn&+'+!8'U-O7fLSllErY1jmdp;p=D]u9/mCQ\pZ&T*3'/@&']#BC"6@E"WSA2uUL4<$hFgoJ#=jnX)gR(((9o[C3We=q^f#R@H,ZV3[AYj@,RHH"'11QlAt.u43^Z`KXOIoF@c&e>\QD'`+a+jYbXt(Db*7AXQ9K/N[VU+t<MFNLIa,%ng5#35S)rlKA#pA][Mnn[^m*it.d+r"TE$erCapKNaX,F"A.Qn'[I0^s,p`gl9Zi&fEBT\F([/=>#BFg2\Qm@^YNn"n&/uRo&mModc[qu5<X1.]eGWjC4W_<^!B-@>Ol8IA[e`kB9J`7,p^fGlHkJ<f.qcgXeni$;1]&Ds>!kMGe/,2D`KeS6=W7EIoSD--q<Xns^TrU2G0,Ru+h+9SHSEs5_&&P3h.9jo:G[uY3%NsklhTXM)Gd[5q9/>Li=tNqi_G+)%I_m4*ck<gD$mq/>Hu6%@6O^7;"s#Io8*p`]8+XrD5&k`.gi@^;8*e7n+]#)SB/Z(+TWr75odMp`7U8f#^7@\3">897Y\eRkB`]:U72OJ:8=);>)%C3`1F<(I0B.9>'[#A3Z70bHEB\/grSmJPn-I7W8jV;74i?JJfD4*BQi(O]iOD=72rqe&9YS(2JISQI/DVN.l7r![G7*qr_QaBjJ=Jtq8[7XitF)!4MY"fGATo:-,kLIKronrc72q9CtWrDK);gP'@";VVCb/iG9"t?K"KL^61a5tBi1b/$FYE%(=b:<[D:N2D^l,RJP7T\C1rWC`2XcGh=]%\5?s([A]@ciG21lLXP'G[CZc$\'napCc^F3&453n"qg_]eB]]nJPZZkOaROG/]D%r!n3`.<^&!K2^Fng+Sc~>endstream
endobj
xref
0 26
@@ -205,16 +205,16 @@ xref
0000005637 00000 n
0000005945 00000 n
0000006047 00000 n
0000007253 00000 n
0000010334 00000 n
0000012885 00000 n
0000015053 00000 n
0000017777 00000 n
0000020160 00000 n
0000007268 00000 n
0000010349 00000 n
0000012900 00000 n
0000015068 00000 n
0000018004 00000 n
0000020260 00000 n
trailer
<<
/ID
[<93ff8ecba1600bc59dfd0d3f9189f269><93ff8ecba1600bc59dfd0d3f9189f269>]
[<2db4e987e0adcf5648dea723e2828700><2db4e987e0adcf5648dea723e2828700>]
% ReportLab generated PDF document -- digest (opensource)

/Info 17 0 R
@@ -222,5 +222,5 @@ trailer
/Size 26
>>
startxref
21431
21651
%%EOF

+ 8
- 2
vim/vim.c Ver arquivo

@@ -236,6 +236,11 @@ doscmd()
sclr(24, 0, 40);
if (n == 0) return 0;

if (cmd[0] == 68 && cmd[1] == 73 && cmd[2] == 82 && cmd[3] == 0) { /* dir */
if (dirlist() == 0) note = "cannot read directory";
return 0;
}

if (cmd[0] == 81) { /* q, q! */
if (cmd[1] == 33) return 1;
if (dirty) {
@@ -415,8 +420,9 @@ main()
mode = 1;
} else if (k == 58) { /* : */
if (doscmd() == 1) {
sdone();
return;
sdone(); /* resets the machine: never returns.
(a return here made power c emit a
jump to garbage - see NOTES.md) */
}
}
}


+ 907
- 0
vim/vim128.map Ver arquivo

@@ -0,0 +1,907 @@
Modules list:
-------------
vim128.c.89112.0.o:
CODE Offs=000000 Size=001F14 Align=00001 Fill=0000
RODATA Offs=000000 Size=0000CF Align=00001 Fill=0000
BSS Offs=000000 Size=004FFC Align=00001 Fill=0000
D:/Development/Commodore/cc65-snapshot-win32/lib/c128.lib(_cursor.o):
BSS Offs=004FFC Size=000001 Align=00001 Fill=0000
D:/Development/Commodore/cc65-snapshot-win32/lib/c128.lib(_oserror.o):
BSS Offs=004FFD Size=000001 Align=00001 Fill=0000
D:/Development/Commodore/cc65-snapshot-win32/lib/c128.lib(_scrsize.o):
CODE Offs=001F14 Size=00000B Align=00001 Fill=0000
D:/Development/Commodore/cc65-snapshot-win32/lib/c128.lib(add.o):
CODE Offs=001F1F Size=00001C Align=00001 Fill=0000
D:/Development/Commodore/cc65-snapshot-win32/lib/c128.lib(addeqsp.o):
CODE Offs=001F3B Size=000011 Align=00001 Fill=0000
D:/Development/Commodore/cc65-snapshot-win32/lib/c128.lib(addysp.o):
CODE Offs=001F4C Size=00000E Align=00001 Fill=0000
D:/Development/Commodore/cc65-snapshot-win32/lib/c128.lib(aslax1.o):
CODE Offs=001F5A Size=000008 Align=00001 Fill=0000
D:/Development/Commodore/cc65-snapshot-win32/lib/c128.lib(c_bsout.o):
D:/Development/Commodore/cc65-snapshot-win32/lib/c128.lib(c_close.o):
CODE Offs=001F62 Size=000004 Align=00001 Fill=0000
D:/Development/Commodore/cc65-snapshot-win32/lib/c128.lib(c_open.o):
CODE Offs=001F66 Size=000009 Align=00001 Fill=0000
D:/Development/Commodore/cc65-snapshot-win32/lib/c128.lib(c_setlfs.o):
CODE Offs=001F6F Size=00000E Align=00001 Fill=0000
D:/Development/Commodore/cc65-snapshot-win32/lib/c128.lib(c_setnam.o):
CODE Offs=001F7D Size=000013 Align=00001 Fill=0000
D:/Development/Commodore/cc65-snapshot-win32/lib/c128.lib(callmain.o):
CODE Offs=001F90 Size=000017 Align=00001 Fill=0000
DATA Offs=000000 Size=000004 Align=00001 Fill=0000
D:/Development/Commodore/cc65-snapshot-win32/lib/c128.lib(cbm_close.o):
D:/Development/Commodore/cc65-snapshot-win32/lib/c128.lib(cbm_open.o):
CODE Offs=001FA7 Size=000010 Align=00001 Fill=0000
D:/Development/Commodore/cc65-snapshot-win32/lib/c128.lib(cbm_read.o):
CODE Offs=001FB7 Size=00005A Align=00001 Fill=0000
D:/Development/Commodore/cc65-snapshot-win32/lib/c128.lib(cbm_write.o):
CODE Offs=002011 Size=000049 Align=00001 Fill=0000
D:/Development/Commodore/cc65-snapshot-win32/lib/c128.lib(cgetc.o):
CODE Offs=00205A Size=000030 Align=00001 Fill=0000
INIT Offs=000000 Size=000002 Align=00001 Fill=0000
ONCE Offs=000000 Size=000013 Align=00001 Fill=0000
D:/Development/Commodore/cc65-snapshot-win32/lib/c128.lib(clrscr.o):
D:/Development/Commodore/cc65-snapshot-win32/lib/c128.lib(color.o):
CODE Offs=00208A Size=000060 Align=00001 Fill=0000
D:/Development/Commodore/cc65-snapshot-win32/lib/c128.lib(condes.o):
CODE Offs=0020EA Size=00000C Align=00001 Fill=0000
DATA Offs=000004 Size=000025 Align=00001 Fill=0000
ONCE Offs=000013 Size=00000C Align=00001 Fill=0000
D:/Development/Commodore/cc65-snapshot-win32/lib/c128.lib(conio.o):
D:/Development/Commodore/cc65-snapshot-win32/lib/c128.lib(cputc.o):
CODE Offs=0020F6 Size=000049 Align=00001 Fill=0000
D:/Development/Commodore/cc65-snapshot-win32/lib/c128.lib(crt0.o):
BSS Offs=004FFE Size=000003 Align=00001 Fill=0000
STARTUP Offs=000000 Size=000062 Align=00001 Fill=0000
INIT Offs=000002 Size=00001A Align=00001 Fill=0000
D:/Development/Commodore/cc65-snapshot-win32/lib/c128.lib(cursor.o):
CODE Offs=00213F Size=00000A Align=00001 Fill=0000
D:/Development/Commodore/cc65-snapshot-win32/lib/c128.lib(decax1.o):
CODE Offs=002149 Size=000007 Align=00001 Fill=0000
D:/Development/Commodore/cc65-snapshot-win32/lib/c128.lib(decaxy.o):
CODE Offs=002150 Size=000009 Align=00001 Fill=0000
D:/Development/Commodore/cc65-snapshot-win32/lib/c128.lib(decsp1.o):
CODE Offs=002159 Size=000009 Align=00001 Fill=0000
D:/Development/Commodore/cc65-snapshot-win32/lib/c128.lib(decsp2.o):
CODE Offs=002162 Size=00000D Align=00001 Fill=0000
D:/Development/Commodore/cc65-snapshot-win32/lib/c128.lib(decsp4.o):
CODE Offs=00216F Size=00000D Align=00001 Fill=0000
D:/Development/Commodore/cc65-snapshot-win32/lib/c128.lib(decsp6.o):
CODE Offs=00217C Size=00000D Align=00001 Fill=0000
D:/Development/Commodore/cc65-snapshot-win32/lib/c128.lib(decsp8.o):
CODE Offs=002189 Size=00000D Align=00001 Fill=0000
D:/Development/Commodore/cc65-snapshot-win32/lib/c128.lib(div.o):
CODE Offs=002196 Size=000015 Align=00001 Fill=0000
D:/Development/Commodore/cc65-snapshot-win32/lib/c128.lib(exehdr.o):
EXEHDR Offs=000000 Size=00000C Align=00001 Fill=0000
D:/Development/Commodore/cc65-snapshot-win32/lib/c128.lib(fast.o):
CODE Offs=0021AB Size=000006 Align=00001 Fill=0000
D:/Development/Commodore/cc65-snapshot-win32/lib/c128.lib(gotoxy.o):
CODE Offs=0021B1 Size=00000D Align=00001 Fill=0000
D:/Development/Commodore/cc65-snapshot-win32/lib/c128.lib(icmp.o):
CODE Offs=0021BE Size=00002E Align=00001 Fill=0000
D:/Development/Commodore/cc65-snapshot-win32/lib/c128.lib(incax1.o):
CODE Offs=0021EC Size=000007 Align=00001 Fill=0000
D:/Development/Commodore/cc65-snapshot-win32/lib/c128.lib(incax2.o):
CODE Offs=0021F3 Size=000007 Align=00001 Fill=0000
D:/Development/Commodore/cc65-snapshot-win32/lib/c128.lib(incax8.o):
CODE Offs=0021FA Size=000005 Align=00001 Fill=0000
D:/Development/Commodore/cc65-snapshot-win32/lib/c128.lib(incaxy.o):
CODE Offs=0021FF Size=00000B Align=00001 Fill=0000
D:/Development/Commodore/cc65-snapshot-win32/lib/c128.lib(incsp2.o):
CODE Offs=00220A Size=000016 Align=00001 Fill=0000
D:/Development/Commodore/cc65-snapshot-win32/lib/c128.lib(incsp4.o):
CODE Offs=002220 Size=000005 Align=00001 Fill=0000
D:/Development/Commodore/cc65-snapshot-win32/lib/c128.lib(incsp5.o):
CODE Offs=002225 Size=000005 Align=00001 Fill=0000
D:/Development/Commodore/cc65-snapshot-win32/lib/c128.lib(incsp6.o):
CODE Offs=00222A Size=000005 Align=00001 Fill=0000
D:/Development/Commodore/cc65-snapshot-win32/lib/c128.lib(incsp8.o):
CODE Offs=00222F Size=000005 Align=00001 Fill=0000
D:/Development/Commodore/cc65-snapshot-win32/lib/c128.lib(kernal.o):
D:/Development/Commodore/cc65-snapshot-win32/lib/c128.lib(ldaxi.o):
CODE Offs=002234 Size=00000D Align=00001 Fill=0000
D:/Development/Commodore/cc65-snapshot-win32/lib/c128.lib(ldaxsp.o):
CODE Offs=002241 Size=000009 Align=00001 Fill=0000
D:/Development/Commodore/cc65-snapshot-win32/lib/c128.lib(ldptr1sp.o):
CODE Offs=00224A Size=00000C Align=00001 Fill=0000
D:/Development/Commodore/cc65-snapshot-win32/lib/c128.lib(leaaxsp.o):
CODE Offs=002256 Size=00000C Align=00001 Fill=0000
D:/Development/Commodore/cc65-snapshot-win32/lib/c128.lib(loadaddr.o):
LOADADDR Offs=000000 Size=000002 Align=00001 Fill=0000
D:/Development/Commodore/cc65-snapshot-win32/lib/c128.lib(mod.o):
CODE Offs=002262 Size=000011 Align=00001 Fill=0000
D:/Development/Commodore/cc65-snapshot-win32/lib/c128.lib(mulax10.o):
CODE Offs=002273 Size=000019 Align=00001 Fill=0000
D:/Development/Commodore/cc65-snapshot-win32/lib/c128.lib(negabs.o):
CODE Offs=00228C Size=000012 Align=00001 Fill=0000
D:/Development/Commodore/cc65-snapshot-win32/lib/c128.lib(popa.o):
CODE Offs=00229E Size=00000C Align=00001 Fill=0000
D:/Development/Commodore/cc65-snapshot-win32/lib/c128.lib(popptr1.o):
CODE Offs=0022AA Size=00000E Align=00001 Fill=0000
D:/Development/Commodore/cc65-snapshot-win32/lib/c128.lib(pusha.o):
CODE Offs=0022B8 Size=000016 Align=00001 Fill=0000
D:/Development/Commodore/cc65-snapshot-win32/lib/c128.lib(pushax.o):
CODE Offs=0022CE Size=00001A Align=00001 Fill=0000
D:/Development/Commodore/cc65-snapshot-win32/lib/c128.lib(pushwsp.o):
CODE Offs=0022E8 Size=00001C Align=00001 Fill=0000
D:/Development/Commodore/cc65-snapshot-win32/lib/c128.lib(return0.o):
CODE Offs=002304 Size=000004 Align=00001 Fill=0000
D:/Development/Commodore/cc65-snapshot-win32/lib/c128.lib(returnFFFF.o):
CODE Offs=002308 Size=000004 Align=00001 Fill=0000
D:/Development/Commodore/cc65-snapshot-win32/lib/c128.lib(revers.o):
CODE Offs=00230C Size=000014 Align=00001 Fill=0000
D:/Development/Commodore/cc65-snapshot-win32/lib/c128.lib(scrsize.o):
CODE Offs=002320 Size=000013 Align=00001 Fill=0000
D:/Development/Commodore/cc65-snapshot-win32/lib/c128.lib(shelp.o):
CODE Offs=002333 Size=000020 Align=00001 Fill=0000
D:/Development/Commodore/cc65-snapshot-win32/lib/c128.lib(slow.o):
CODE Offs=002353 Size=000006 Align=00001 Fill=0000
D:/Development/Commodore/cc65-snapshot-win32/lib/c128.lib(staspidx.o):
CODE Offs=002359 Size=000016 Align=00001 Fill=0000
D:/Development/Commodore/cc65-snapshot-win32/lib/c128.lib(staxsp.o):
CODE Offs=00236F Size=00000B Align=00001 Fill=0000
D:/Development/Commodore/cc65-snapshot-win32/lib/c128.lib(strcmp.o):
CODE Offs=00237A Size=000021 Align=00001 Fill=0000
D:/Development/Commodore/cc65-snapshot-win32/lib/c128.lib(strcpy.o):
CODE Offs=00239B Size=00001F Align=00001 Fill=0000
D:/Development/Commodore/cc65-snapshot-win32/lib/c128.lib(sub.o):
CODE Offs=0023BA Size=000015 Align=00001 Fill=0000
D:/Development/Commodore/cc65-snapshot-win32/lib/c128.lib(subeqsp.o):
CODE Offs=0023CF Size=000015 Align=00001 Fill=0000
D:/Development/Commodore/cc65-snapshot-win32/lib/c128.lib(subysp.o):
CODE Offs=0023E4 Size=00000D Align=00001 Fill=0000
D:/Development/Commodore/cc65-snapshot-win32/lib/c128.lib(udiv.o):
CODE Offs=0023F1 Size=00004F Align=00001 Fill=0000
D:/Development/Commodore/cc65-snapshot-win32/lib/c128.lib(zerobss.o):
CODE Offs=002440 Size=000023 Align=00001 Fill=0000
D:/Development/Commodore/cc65-snapshot-win32/lib/c128.lib(zeropage.o):
ZEROPAGE Offs=000000 Size=00001A Align=00001 Fill=0000


Segment list:
-------------
Name Start End Size Align
----------------------------------------------------
ZEROPAGE 000002 00001B 00001A 00001
LOADADDR 001BFF 001C00 000002 00001
EXEHDR 001C01 001C0C 00000C 00001
STARTUP 001C0D 001C6E 000062 00001
ONCE 001C6F 001C8F 000021 00001
CODE 001C90 0040F2 002463 00001
RODATA 0040F3 0041C3 0000D1 00001
DATA 0041C4 0041EC 000029 00001
INIT 0041ED 004208 00001C 00001
BSS 004209 009209 005001 00001


Exports list by name:
---------------------
BASIN 00FFCF RLA BSOUT 00FFD2 RLA
CHKIN 00FFC6 RLA CHROUT 00FFD2 RLA
CKOUT 00FFC9 RLA CLOSE 00FFC3 RLA
CLRCH 00FFCC RLA CURS_X 0000EC RLZ
CURS_Y 0000EB RLZ OPEN 00FFC0 RLA
READST 00FFB7 RLA SETLFS 00FFBA RLA
SETNAM 00FFBD RLA __BSS_RUN__ 004209 RLA
__BSS_SIZE__ 005001 REA __CONSTRUCTOR_COUNT__ 000001 REA
__CONSTRUCTOR_TABLE__ 001C8E RLA __DESTRUCTOR_COUNT__ 000001 REA
__DESTRUCTOR_TABLE__ 0041C2 RLA __EXEHDR__ 000001 REA
__LOADADDR__ 000001 REA __MAIN_SIZE__ 009BF3 REA
__MAIN_START__ 001C0D RLA __STACKSIZE__ 000800 REA
__STARTUP__ 000001 REA ___oserror 009206 RLA
_bgcolor 003D43 RLA _bordercolor 003D69 RLA
_cbm_close 003BF2 REA _cbm_k_bsout 00FFD2 RLA
_cbm_k_close 003BF2 RLA _cbm_k_open 003BF6 RLA
_cbm_k_setlfs 003BFF RLA _cbm_k_setnam 003C0D RLA
_cbm_open 003C37 RLA _cbm_read 003C47 RLA
_cbm_write 003CA1 RLA _cgetc 003CEA RLA
_clrscr 00C142 REA _cputc 003D8B RLA
_cputcxy 003D86 RLA _cursor 003DCF RLA
_fast 003E3B RLA _gotoxy 003E44 RLA
_main 003722 RLA _revers 003F9C RLA
_screensize 003FB0 RLA _slow 003FE3 RLA
_strcmp 00400A RLA _strcpy 00402B RLA
_textcolor 003D1A RLA addeq0sp 003BCB RLA
addeqysp 003BCD RLA addysp 003BDD RLA
addysp1 003BDC RLA aslax1 003BEA RLA
c_sp 000002 RLZ callmain 003C20 RLA
cursor 009205 RLA decax1 003DD9 RLA
decaxy 003DE0 RLA decsp1 003DE9 RLA
decsp2 003DF2 RLA decsp4 003DFF RLA
decsp6 003E0C RLA decsp8 003E19 RLA
donecgetc 003D0B LAI donelib 003D7A RLA
gotoxy 003E41 RLA incax1 003E7C RLA
incax2 003E83 RLA incax4 003E8F RLA
incax8 003E8A RLA incaxy 003E91 RLA
incsp2 003EA2 RLA incsp4 003EB0 RLA
incsp5 003EB5 RLA incsp6 003EBA RLA
incsp8 003EBF RLA initcgetc 001C6F LAI
initlib 001C82 RLA ldax0sp 003ED1 RLA
ldaxi 003EC4 RLA ldaxysp 003ED3 RLA
ldptr10sp 003EDA RLA ldptr1ysp 003EDC RLA
leaa0sp 003EE6 RLA leaaxsp 003EE8 RLA
mulax10 003F03 RLA negax 003F20 RLA
plot 003DC7 RLA popa 003F2E RLA
popax 003E9A RLA popptr1 003F3A RLA
popsargsudiv16 003FC3 RLA ptr1 00000A RLZ
ptr2 00000C RLZ ptr3 00000E RLZ
ptr4 000010 RLZ push0 003F5E RLA
pusha 003F4C RLA pusha0 003F60 RLA
pushax 003F62 RLA pushw0sp 003F78 RLA
pushwysp 003F7A RLA return0 003F94 RLA
returnFFFF 003F98 RLA screensize 003BA4 RLA
sreg 000004 RLZ staspidx 003FE9 RLA
stax0sp 003FFF RLA staxysp 004001 RLA
subeq0sp 00405F RLA subeqysp 004061 RLA
subysp 004074 RLA tmp1 000012 RLZ
tmp2 000013 RLZ tosaddax 003BB1 RLA
tosdiva0 003E26 RLA tosicmp 003E50 RLA
tosicmp0 003E4E RLA tosmoda0 003EF2 RLA
tossubax 00404C RLA udiv16 004092 RLA
zerobss 0040D0 RLA


Exports list by value:
----------------------
__EXEHDR__ 000001 REA __LOADADDR__ 000001 REA
__CONSTRUCTOR_COUNT__ 000001 REA __STARTUP__ 000001 REA
__DESTRUCTOR_COUNT__ 000001 REA c_sp 000002 RLZ
sreg 000004 RLZ ptr1 00000A RLZ
ptr2 00000C RLZ ptr3 00000E RLZ
ptr4 000010 RLZ tmp1 000012 RLZ
tmp2 000013 RLZ CURS_Y 0000EB RLZ
CURS_X 0000EC RLZ __STACKSIZE__ 000800 REA
__MAIN_START__ 001C0D RLA initcgetc 001C6F LAI
initlib 001C82 RLA __CONSTRUCTOR_TABLE__ 001C8E RLA
_main 003722 RLA screensize 003BA4 RLA
tosaddax 003BB1 RLA addeq0sp 003BCB RLA
addeqysp 003BCD RLA addysp1 003BDC RLA
addysp 003BDD RLA aslax1 003BEA RLA
_cbm_close 003BF2 REA _cbm_k_close 003BF2 RLA
_cbm_k_open 003BF6 RLA _cbm_k_setlfs 003BFF RLA
_cbm_k_setnam 003C0D RLA callmain 003C20 RLA
_cbm_open 003C37 RLA _cbm_read 003C47 RLA
_cbm_write 003CA1 RLA _cgetc 003CEA RLA
donecgetc 003D0B LAI _textcolor 003D1A RLA
_bgcolor 003D43 RLA _bordercolor 003D69 RLA
donelib 003D7A RLA _cputcxy 003D86 RLA
_cputc 003D8B RLA plot 003DC7 RLA
_cursor 003DCF RLA decax1 003DD9 RLA
decaxy 003DE0 RLA decsp1 003DE9 RLA
decsp2 003DF2 RLA decsp4 003DFF RLA
decsp6 003E0C RLA decsp8 003E19 RLA
tosdiva0 003E26 RLA _fast 003E3B RLA
gotoxy 003E41 RLA _gotoxy 003E44 RLA
tosicmp0 003E4E RLA tosicmp 003E50 RLA
incax1 003E7C RLA incax2 003E83 RLA
incax8 003E8A RLA incax4 003E8F RLA
incaxy 003E91 RLA popax 003E9A RLA
incsp2 003EA2 RLA incsp4 003EB0 RLA
incsp5 003EB5 RLA incsp6 003EBA RLA
incsp8 003EBF RLA ldaxi 003EC4 RLA
ldax0sp 003ED1 RLA ldaxysp 003ED3 RLA
ldptr10sp 003EDA RLA ldptr1ysp 003EDC RLA
leaa0sp 003EE6 RLA leaaxsp 003EE8 RLA
tosmoda0 003EF2 RLA mulax10 003F03 RLA
negax 003F20 RLA popa 003F2E RLA
popptr1 003F3A RLA pusha 003F4C RLA
push0 003F5E RLA pusha0 003F60 RLA
pushax 003F62 RLA pushw0sp 003F78 RLA
pushwysp 003F7A RLA return0 003F94 RLA
returnFFFF 003F98 RLA _revers 003F9C RLA
_screensize 003FB0 RLA popsargsudiv16 003FC3 RLA
_slow 003FE3 RLA staspidx 003FE9 RLA
stax0sp 003FFF RLA staxysp 004001 RLA
_strcmp 00400A RLA _strcpy 00402B RLA
tossubax 00404C RLA subeq0sp 00405F RLA
subeqysp 004061 RLA subysp 004074 RLA
udiv16 004092 RLA zerobss 0040D0 RLA
__DESTRUCTOR_TABLE__ 0041C2 RLA __BSS_RUN__ 004209 RLA
__BSS_SIZE__ 005001 REA cursor 009205 RLA
___oserror 009206 RLA __MAIN_SIZE__ 009BF3 REA
_clrscr 00C142 REA READST 00FFB7 RLA
SETLFS 00FFBA RLA SETNAM 00FFBD RLA
OPEN 00FFC0 RLA CLOSE 00FFC3 RLA
CHKIN 00FFC6 RLA CKOUT 00FFC9 RLA
CLRCH 00FFCC RLA BASIN 00FFCF RLA
_cbm_k_bsout 00FFD2 RLA CHROUT 00FFD2 RLA
BSOUT 00FFD2 RLA


Imports list:
-------------
BASIN (kernal.o):
cbm_read.o cbm/cbm.inc:47
BSOUT (kernal.o):
crt0.o c128/crt0.s:10
cbm_write.o cbm/cbm.inc:49
c_bsout.o cbm/cbm.inc:49
CHKIN (kernal.o):
cbm_read.o cbm/cbm.inc:44
CHROUT (kernal.o):
c_bsout.o cbm/cbm.inc:50
CKOUT (kernal.o):
cbm_write.o cbm/cbm.inc:45
CLOSE (kernal.o):
c_close.o cbm/cbm.inc:36
CLRCH (kernal.o):
cbm_write.o cbm/cbm.inc:46
cbm_read.o cbm/cbm.inc:46
CURS_X (conio.o):
gotoxy.o cbm/gotoxy.s:9
CURS_Y (conio.o):
gotoxy.o cbm/gotoxy.s:9
OPEN (kernal.o):
c_open.o cbm/cbm.inc:35
READST (kernal.o):
cbm_write.o cbm/cbm.inc:32
cbm_read.o cbm/cbm.inc:32
SETLFS (kernal.o):
c_setlfs.o cbm/cbm.inc:33
SETNAM (kernal.o):
c_setnam.o cbm/cbm.inc:34
__BSS_RUN__ ([linker generated]):
zerobss.o common/zerobss.s:8
__BSS_SIZE__ ([linker generated]):
zerobss.o common/zerobss.s:8
__CONSTRUCTOR_COUNT__ ([linker generated]):
condes.o runtime/condes.s:18
__CONSTRUCTOR_TABLE__ ([linker generated]):
condes.o runtime/condes.s:18
__DESTRUCTOR_COUNT__ ([linker generated]):
condes.o runtime/condes.s:19
__DESTRUCTOR_TABLE__ ([linker generated]):
condes.o runtime/condes.s:19
__EXEHDR__ (exehdr.o):
[linker generated] D:/Development/Commodore/cc65-snapshot-win32/cfg/c128.cfg:3
__LOADADDR__ (loadaddr.o):
[linker generated] D:/Development/Commodore/cc65-snapshot-win32/cfg/c128.cfg:2
__MAIN_SIZE__ ([linker generated]):
crt0.o c128/crt0.s:11
__MAIN_START__ ([linker generated]):
crt0.o c128/crt0.s:11
__STACKSIZE__ ([linker generated]):
crt0.o c128/crt0.s:11
[linker generated] D:/Development/Commodore/cc65-snapshot-win32/cfg/c128.cfg:10
__STARTUP__ (crt0.o):
vim128.c.89112.0.o vim128.c.89112.0.s:13
___oserror (_oserror.o):
cbm_write.o cbm/cbm_write.s:36
cbm_read.o cbm/cbm_read.s:44
cbm_open.o cbm/cbm_open.s:26
_bgcolor (color.o):
vim128.c.89112.0.o vim128.c.89112.0.s:29
_bordercolor (color.o):
vim128.c.89112.0.o vim128.c.89112.0.s:30
_cbm_close (cbm_close.o):
vim128.c.89112.0.o vim128.c.89112.0.s:18
_cbm_k_bsout (c_bsout.o):
vim128.c.89112.0.o vim128.c.89112.0.s:16
_cbm_k_close (c_close.o):
cbm_close.o cbm/cbm_close.s:7
_cbm_k_open (c_open.o):
cbm_open.o cbm/cbm_open.s:25
_cbm_k_setlfs (c_setlfs.o):
cbm_open.o cbm/cbm_open.s:25
_cbm_k_setnam (c_setnam.o):
cbm_open.o cbm/cbm_open.s:25
_cbm_open (cbm_open.o):
vim128.c.89112.0.o vim128.c.89112.0.s:17
_cbm_read (cbm_read.o):
vim128.c.89112.0.o vim128.c.89112.0.s:19
_cbm_write (cbm_write.o):
vim128.c.89112.0.o vim128.c.89112.0.s:20
_cgetc (cgetc.o):
vim128.c.89112.0.o vim128.c.89112.0.s:25
_clrscr (clrscr.o):
vim128.c.89112.0.o vim128.c.89112.0.s:21
_cputc (cputc.o):
vim128.c.89112.0.o vim128.c.89112.0.s:23
_cputcxy (cputc.o):
vim128.c.89112.0.o vim128.c.89112.0.s:24
_cursor (cursor.o):
vim128.c.89112.0.o vim128.c.89112.0.s:26
_fast (fast.o):
vim128.c.89112.0.o vim128.c.89112.0.s:14
_gotoxy (gotoxy.o):
vim128.c.89112.0.o vim128.c.89112.0.s:22
_main (vim128.c.89112.0.o):
callmain.o runtime/callmain.s:11
_revers (revers.o):
vim128.c.89112.0.o vim128.c.89112.0.s:27
_screensize (scrsize.o):
vim128.c.89112.0.o vim128.c.89112.0.s:31
_slow (slow.o):
vim128.c.89112.0.o vim128.c.89112.0.s:15
_strcmp (strcmp.o):
vim128.c.89112.0.o vim128.c.89112.0.s:32
_strcpy (strcpy.o):
vim128.c.89112.0.o vim128.c.89112.0.s:33
_textcolor (color.o):
vim128.c.89112.0.o vim128.c.89112.0.s:28
addeq0sp (addeqsp.o):
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
addeqysp (addeqsp.o):
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
addysp (addysp.o):
incsp8.o runtime/incsp8.s:8
incsp6.o runtime/incsp6.s:8
incsp5.o runtime/incsp5.s:8
incsp4.o runtime/incsp4.s:8
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
addysp1 (addysp.o):
sub.o runtime/sub.s:8
aslax1 (aslax1.o):
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
c_sp (zeropage.o):
popptr1.o runtime/popptr1.s:9
subysp.o runtime/subysp.s:9
subeqsp.o runtime/subeqsp.s:8
sub.o runtime/sub.s:9
staxsp.o runtime/staxsp.s:8
staspidx.o runtime/staspidx.s:9
pushwsp.o runtime/pushwsp.s:8
pushax.o runtime/pushax.s:8
pusha.o runtime/pusha.s:8
popa.o runtime/popa.s:8
leaaxsp.o runtime/leaaxsp.s:8
ldptr1sp.o runtime/ldptr1sp.s:8
ldaxsp.o runtime/ldaxsp.s:8
incsp2.o runtime/incsp2.s:8
icmp.o runtime/icmp.s:9
decsp8.o runtime/decsp8.s:8
decsp6.o runtime/decsp6.s:8
decsp4.o runtime/decsp4.s:8
decsp2.o runtime/decsp2.s:8
decsp1.o runtime/decsp1.s:8
crt0.o /home/runner/work/cc65/cc65/asminc/zeropage.inc:11
addysp.o runtime/addysp.s:8
addeqsp.o runtime/addeqsp.s:8
add.o runtime/add.s:12
vim128.c.89112.0.o vim128.c.89112.0.s:10
callmain (callmain.o):
crt0.o c128/crt0.s:9
cursor (_cursor.o):
cursor.o conio/cursor.s:8
cgetc.o c128/cgetc.s:11
decax1 (decax1.o):
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
decaxy (decaxy.o):
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
decsp1 (decsp1.o):
vim128.c.89112.0.o vim128.c.89112.0.s:4190
decsp2 (decsp2.o):
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
decsp4 (decsp4.o):
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
decsp6 (decsp6.o):
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
decsp8 (decsp8.o):
vim128.c.89112.0.o vim128.c.89112.0.s:4190
donelib (condes.o):
crt0.o c128/crt0.s:7
gotoxy (gotoxy.o):
cputc.o c128/cputc.s:13
incax1 (incax1.o):
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
incax2 (incax2.o):
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
incax4 (incaxy.o):
vim128.c.89112.0.o vim128.c.89112.0.s:4190
incax8 (incax8.o):
vim128.c.89112.0.o vim128.c.89112.0.s:4190
incaxy (incaxy.o):
incax8.o runtime/incax8.s:8
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
incsp2 (incsp2.o):
popptr1.o runtime/popptr1.s:8
staspidx.o runtime/staspidx.s:8
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
incsp4 (incsp4.o):
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
incsp5 (incsp5.o):
vim128.c.89112.0.o vim128.c.89112.0.s:4190
incsp6 (incsp6.o):
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
incsp8 (incsp8.o):
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
initlib (condes.o):
crt0.o c128/crt0.s:7
ldax0sp (ldaxsp.o):
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
ldaxi (ldaxi.o):
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
ldaxysp (ldaxsp.o):
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
ldptr10sp (ldptr1sp.o):
vim128.c.89112.0.o vim128.c.89112.0.s:4190
ldptr1ysp (ldptr1sp.o):
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
leaa0sp (leaaxsp.o):
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
leaaxsp (leaaxsp.o):
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
mulax10 (mulax10.o):
vim128.c.89112.0.o vim128.c.89112.0.s:4190
negax (negabs.o):
shelp.o runtime/shelp.s:11
mod.o runtime/mod.s:11
div.o runtime/div.s:11
vim128.c.89112.0.o vim128.c.89112.0.s:4190
plot (cputc.o):
gotoxy.o cbm/gotoxy.s:8
popa (popa.o):
c_setlfs.o cbm/c_setlfs.s:12
gotoxy.o cbm/gotoxy.s:8
cbm_write.o cbm/cbm_write.s:35
cbm_read.o cbm/cbm_read.s:43
cbm_open.o cbm/cbm_open.s:24
popax (incsp2.o):
strcpy.o common/strcpy.s:8
shelp.o runtime/shelp.s:11
cbm_write.o cbm/cbm_write.s:35
cbm_read.o cbm/cbm_read.s:43
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
popptr1 (popptr1.o):
udiv.o runtime/udiv.s:9
strcmp.o common/strcmp.s:8
scrsize.o conio/scrsize.s:9
popsargsudiv16 (shelp.o):
mod.o runtime/mod.s:11
div.o runtime/div.s:11
ptr1 (zeropage.o):
popptr1.o runtime/popptr1.s:9
c_setnam.o cbm/c_setnam.s:10
zerobss.o common/zerobss.s:9
udiv.o runtime/udiv.s:10
strcpy.o common/strcpy.s:9
strcmp.o common/strcmp.s:9
staspidx.o runtime/staspidx.s:9
shelp.o runtime/shelp.s:12
scrsize.o conio/scrsize.s:11
mulax10.o runtime/mulax10.s:9
ldptr1sp.o runtime/ldptr1sp.s:8
ldaxi.o runtime/ldaxi.s:8
div.o runtime/div.s:12
cbm_write.o cbm/cbm_write.s:34
cbm_read.o cbm/cbm_read.s:42
vim128.c.89112.0.o vim128.c.89112.0.s:11
ptr2 (zeropage.o):
strcpy.o common/strcpy.s:9
strcmp.o common/strcmp.s:9
scrsize.o conio/scrsize.s:11
cbm_write.o cbm/cbm_write.s:34
cbm_read.o cbm/cbm_read.s:42
ptr3 (zeropage.o):
cbm_write.o cbm/cbm_write.s:34
cbm_read.o cbm/cbm_read.s:42
ptr4 (zeropage.o):
udiv.o runtime/udiv.s:10
shelp.o runtime/shelp.s:12
push0 (pushax.o):
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
pusha (pusha.o):
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
pusha0 (pushax.o):
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
pushax (pushax.o):
callmain.o runtime/callmain.s:11
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
pushw0sp (pushwsp.o):
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
pushwysp (pushwsp.o):
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
return0 (return0.o):
color.o c128/color.s:10
returnFFFF (returnFFFF.o):
cbm_write.o cbm/cbm_write.s:35
cbm_read.o cbm/cbm_read.s:43
screensize (_scrsize.o):
scrsize.o conio/scrsize.s:10
sreg (zeropage.o):
udiv.o runtime/udiv.s:10
mod.o runtime/mod.s:12
icmp.o runtime/icmp.s:9
vim128.c.89112.0.o vim128.c.89112.0.s:10
staspidx (staspidx.o):
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
stax0sp (staxsp.o):
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
staxysp (staxsp.o):
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
subeq0sp (subeqsp.o):
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
subeqysp (subeqsp.o):
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
subysp (subysp.o):
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
tmp1 (zeropage.o):
c_setlfs.o cbm/c_setlfs.s:13
staspidx.o runtime/staspidx.s:9
shelp.o runtime/shelp.s:12
mod.o runtime/mod.s:12
incaxy.o runtime/incaxy.s:8
div.o runtime/div.s:12
decaxy.o runtime/decaxy.s:8
cbm_read.o cbm/cbm_read.s:42
aslax1.o runtime/aslax1.s:8
add.o runtime/add.s:12
vim128.c.89112.0.o vim128.c.89112.0.s:11
tmp2 (zeropage.o):
shelp.o runtime/shelp.s:12
div.o runtime/div.s:12
tosaddax (add.o):
vim128.c.89112.0.o vim128.c.89112.0.s:4190
tosdiva0 (div.o):
vim128.c.89112.0.o vim128.c.89112.0.s:4190
tosicmp (icmp.o):
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
tosicmp0 (icmp.o):
vim128.c.89112.0.o vim128.c.89112.0.s:4190
vim128.c.89112.0.o vim128.c.89112.0.s:4190
tosmoda0 (mod.o):
vim128.c.89112.0.o vim128.c.89112.0.s:4190
tossubax (sub.o):
vim128.c.89112.0.o vim128.c.89112.0.s:4190
udiv16 (udiv.o):
shelp.o runtime/shelp.s:11
zerobss (zerobss.o):
crt0.o c128/crt0.s:8


Carregando…
Cancelar
Salvar

Powered by TurnKey Linux.