c64-kb 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +2082 -0
- package/LICENSE +29 -0
- package/README.md +559 -0
- package/VERSION +25 -0
- package/dist/cli/setup.js +66 -0
- package/dist/cli/setup.js.map +1 -0
- package/dist/cli-memorize-audit.js +97 -0
- package/dist/cli-memorize-audit.js.map +1 -0
- package/dist/cli.js +335 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.js +83 -0
- package/dist/config.js.map +1 -0
- package/dist/context.js +53 -0
- package/dist/context.js.map +1 -0
- package/dist/domain/budget-notes.js +104 -0
- package/dist/domain/budget-notes.js.map +1 -0
- package/dist/domain/budget.js +417 -0
- package/dist/domain/budget.js.map +1 -0
- package/dist/domain/game-design.js +77 -0
- package/dist/domain/game-design.js.map +1 -0
- package/dist/domain/timing.js +39 -0
- package/dist/domain/timing.js.map +1 -0
- package/dist/graph/apply.js +132 -0
- package/dist/graph/apply.js.map +1 -0
- package/dist/graph/claims.js +231 -0
- package/dist/graph/claims.js.map +1 -0
- package/dist/graph/extract/archetype.js +118 -0
- package/dist/graph/extract/archetype.js.map +1 -0
- package/dist/graph/extract/common.js +69 -0
- package/dist/graph/extract/common.js.map +1 -0
- package/dist/graph/extract/failure.js +37 -0
- package/dist/graph/extract/failure.js.map +1 -0
- package/dist/graph/extract/game-design.js +189 -0
- package/dist/graph/extract/game-design.js.map +1 -0
- package/dist/graph/extract/hardware.js +124 -0
- package/dist/graph/extract/hardware.js.map +1 -0
- package/dist/graph/extract/pitfall.js +62 -0
- package/dist/graph/extract/pitfall.js.map +1 -0
- package/dist/graph/extract/raster-band.js +56 -0
- package/dist/graph/extract/raster-band.js.map +1 -0
- package/dist/graph/extract/recipe.js +71 -0
- package/dist/graph/extract/recipe.js.map +1 -0
- package/dist/graph/extract/technique-entities.js +180 -0
- package/dist/graph/extract/technique-entities.js.map +1 -0
- package/dist/graph/extract/technique.js +179 -0
- package/dist/graph/extract/technique.js.map +1 -0
- package/dist/graph/extract/toolchain.js +67 -0
- package/dist/graph/extract/toolchain.js.map +1 -0
- package/dist/graph/extract/types.js +3 -0
- package/dist/graph/extract/types.js.map +1 -0
- package/dist/graph/extract/vocabulary.js +65 -0
- package/dist/graph/extract/vocabulary.js.map +1 -0
- package/dist/graph/extract.js +43 -0
- package/dist/graph/extract.js.map +1 -0
- package/dist/graph/kernal-clobbers.js +64 -0
- package/dist/graph/kernal-clobbers.js.map +1 -0
- package/dist/graph/machine-variants.js +208 -0
- package/dist/graph/machine-variants.js.map +1 -0
- package/dist/ingest/files.js +99 -0
- package/dist/ingest/files.js.map +1 -0
- package/dist/ingest/passes.js +104 -0
- package/dist/ingest/passes.js.map +1 -0
- package/dist/ingest/points.js +50 -0
- package/dist/ingest/points.js.map +1 -0
- package/dist/ingest/report.js +140 -0
- package/dist/ingest/report.js.map +1 -0
- package/dist/ingest/tally.js +81 -0
- package/dist/ingest/tally.js.map +1 -0
- package/dist/ingest/verified-on.js +43 -0
- package/dist/ingest/verified-on.js.map +1 -0
- package/dist/ingest.js +135 -0
- package/dist/ingest.js.map +1 -0
- package/dist/schemas/cost-basis.js +5 -0
- package/dist/schemas/cost-basis.js.map +1 -0
- package/dist/schemas/machine-variant.js +25 -0
- package/dist/schemas/machine-variant.js.map +1 -0
- package/dist/schemas/plan-budget.js +121 -0
- package/dist/schemas/plan-budget.js.map +1 -0
- package/dist/schemas/tool-outputs.js +526 -0
- package/dist/schemas/tool-outputs.js.map +1 -0
- package/dist/server/define-tool.js +52 -0
- package/dist/server/define-tool.js.map +1 -0
- package/dist/server/defined-only.js +9 -0
- package/dist/server/defined-only.js.map +1 -0
- package/dist/server/tools-briefings.js +70 -0
- package/dist/server/tools-briefings.js.map +1 -0
- package/dist/server/tools-lookup.js +180 -0
- package/dist/server/tools-lookup.js.map +1 -0
- package/dist/server/tools-maintenance.js +116 -0
- package/dist/server/tools-maintenance.js.map +1 -0
- package/dist/server/tools-pitfalls.js +89 -0
- package/dist/server/tools-pitfalls.js.map +1 -0
- package/dist/server/tools-recipes.js +296 -0
- package/dist/server/tools-recipes.js.map +1 -0
- package/dist/server/tools-runtime.js +48 -0
- package/dist/server/tools-runtime.js.map +1 -0
- package/dist/server/tools.js +43 -0
- package/dist/server/tools.js.map +1 -0
- package/dist/server.js +95 -0
- package/dist/server.js.map +1 -0
- package/dist/services/analytics/schema.js +78 -0
- package/dist/services/analytics/schema.js.map +1 -0
- package/dist/services/analytics/statements.js +99 -0
- package/dist/services/analytics/statements.js.map +1 -0
- package/dist/services/analytics.js +195 -0
- package/dist/services/analytics.js.map +1 -0
- package/dist/services/bm25.js +158 -0
- package/dist/services/bm25.js.map +1 -0
- package/dist/services/chunker.js +81 -0
- package/dist/services/chunker.js.map +1 -0
- package/dist/services/embeddings.js +58 -0
- package/dist/services/embeddings.js.map +1 -0
- package/dist/services/falkor/base.js +85 -0
- package/dist/services/falkor/base.js.map +1 -0
- package/dist/services/falkor/links.js +361 -0
- package/dist/services/falkor/links.js.map +1 -0
- package/dist/services/falkor/nodes.js +183 -0
- package/dist/services/falkor/nodes.js.map +1 -0
- package/dist/services/falkor/params.js +45 -0
- package/dist/services/falkor/params.js.map +1 -0
- package/dist/services/falkor/schema.js +77 -0
- package/dist/services/falkor/schema.js.map +1 -0
- package/dist/services/falkor.js +100 -0
- package/dist/services/falkor.js.map +1 -0
- package/dist/services/qdrant.js +238 -0
- package/dist/services/qdrant.js.map +1 -0
- package/dist/services/versions.js +73 -0
- package/dist/services/versions.js.map +1 -0
- package/dist/services/vice-bin.js +51 -0
- package/dist/services/vice-bin.js.map +1 -0
- package/dist/tools/briefings/archetype.js +174 -0
- package/dist/tools/briefings/archetype.js.map +1 -0
- package/dist/tools/briefings/budget.js +177 -0
- package/dist/tools/briefings/budget.js.map +1 -0
- package/dist/tools/briefings/build-order.js +135 -0
- package/dist/tools/briefings/build-order.js.map +1 -0
- package/dist/tools/briefings/build.js +185 -0
- package/dist/tools/briefings/build.js.map +1 -0
- package/dist/tools/briefings/discovery.js +290 -0
- package/dist/tools/briefings/discovery.js.map +1 -0
- package/dist/tools/briefings/plan-pitfalls.js +69 -0
- package/dist/tools/briefings/plan-pitfalls.js.map +1 -0
- package/dist/tools/briefings/render.js +151 -0
- package/dist/tools/briefings/render.js.map +1 -0
- package/dist/tools/briefings/rows.js +41 -0
- package/dist/tools/briefings/rows.js.map +1 -0
- package/dist/tools/briefings/toolchain.js +63 -0
- package/dist/tools/briefings/toolchain.js.map +1 -0
- package/dist/tools/briefings/why-proposed.js +69 -0
- package/dist/tools/briefings/why-proposed.js.map +1 -0
- package/dist/tools/briefings.js +44 -0
- package/dist/tools/briefings.js.map +1 -0
- package/dist/tools/feedback.js +97 -0
- package/dist/tools/feedback.js.map +1 -0
- package/dist/tools/hydrate.js +75 -0
- package/dist/tools/hydrate.js.map +1 -0
- package/dist/tools/intelligence.js +67 -0
- package/dist/tools/intelligence.js.map +1 -0
- package/dist/tools/lint/asm-decimal.js +86 -0
- package/dist/tools/lint/asm-decimal.js.map +1 -0
- package/dist/tools/lint/asm-rules.js +221 -0
- package/dist/tools/lint/asm-rules.js.map +1 -0
- package/dist/tools/lint/asm-shared.js +3 -0
- package/dist/tools/lint/asm-shared.js.map +1 -0
- package/dist/tools/lint/c-rules.js +215 -0
- package/dist/tools/lint/c-rules.js.map +1 -0
- package/dist/tools/lint/text.js +122 -0
- package/dist/tools/lint/text.js.map +1 -0
- package/dist/tools/lint/types.js +40 -0
- package/dist/tools/lint/types.js.map +1 -0
- package/dist/tools/lint.js +76 -0
- package/dist/tools/lint.js.map +1 -0
- package/dist/tools/memorization-mcp.js +111 -0
- package/dist/tools/memorization-mcp.js.map +1 -0
- package/dist/tools/pitfalls/bm25.js +53 -0
- package/dist/tools/pitfalls/bm25.js.map +1 -0
- package/dist/tools/pitfalls/format.js +58 -0
- package/dist/tools/pitfalls/format.js.map +1 -0
- package/dist/tools/pitfalls/graph.js +124 -0
- package/dist/tools/pitfalls/graph.js.map +1 -0
- package/dist/tools/pitfalls.js +134 -0
- package/dist/tools/pitfalls.js.map +1 -0
- package/dist/tools/prompts.js +81 -0
- package/dist/tools/prompts.js.map +1 -0
- package/dist/tools/query/compatibility/closure.js +62 -0
- package/dist/tools/query/compatibility/closure.js.map +1 -0
- package/dist/tools/query/compatibility/facts.js +28 -0
- package/dist/tools/query/compatibility/facts.js.map +1 -0
- package/dist/tools/query/compatibility/fetch.js +164 -0
- package/dist/tools/query/compatibility/fetch.js.map +1 -0
- package/dist/tools/query/compatibility/hard-rules.js +159 -0
- package/dist/tools/query/compatibility/hard-rules.js.map +1 -0
- package/dist/tools/query/compatibility/index.js +27 -0
- package/dist/tools/query/compatibility/index.js.map +1 -0
- package/dist/tools/query/compatibility/kernal-zp-rule.js +90 -0
- package/dist/tools/query/compatibility/kernal-zp-rule.js.map +1 -0
- package/dist/tools/query/compatibility/render.js +106 -0
- package/dist/tools/query/compatibility/render.js.map +1 -0
- package/dist/tools/query/compatibility/rules.js +337 -0
- package/dist/tools/query/compatibility/rules.js.map +1 -0
- package/dist/tools/query/compatibility/unit-rules.js +253 -0
- package/dist/tools/query/compatibility/unit-rules.js.map +1 -0
- package/dist/tools/query/game-design.js +83 -0
- package/dist/tools/query/game-design.js.map +1 -0
- package/dist/tools/query/lookups.js +220 -0
- package/dist/tools/query/lookups.js.map +1 -0
- package/dist/tools/query/plan-budget.js +266 -0
- package/dist/tools/query/plan-budget.js.map +1 -0
- package/dist/tools/query/recipes.js +214 -0
- package/dist/tools/query/recipes.js.map +1 -0
- package/dist/tools/query/retrieval.js +128 -0
- package/dist/tools/query/retrieval.js.map +1 -0
- package/dist/tools/query/shared.js +121 -0
- package/dist/tools/query/shared.js.map +1 -0
- package/dist/tools/query/techniques.js +331 -0
- package/dist/tools/query/techniques.js.map +1 -0
- package/dist/tools/query/timing.js +106 -0
- package/dist/tools/query/timing.js.map +1 -0
- package/dist/tools/query/types.js +11 -0
- package/dist/tools/query/types.js.map +1 -0
- package/dist/tools/query.js +21 -0
- package/dist/tools/query.js.map +1 -0
- package/dist/tools/resources.js +186 -0
- package/dist/tools/resources.js.map +1 -0
- package/dist/tools/run-game.js +249 -0
- package/dist/tools/run-game.js.map +1 -0
- package/dist/tools/selfimprovement.js +367 -0
- package/dist/tools/selfimprovement.js.map +1 -0
- package/docker-compose.yml +35 -0
- package/docs/ARCHITECTURE.md +164 -0
- package/docs/CONVENTIONS-archetypes.md +158 -0
- package/docs/CONVENTIONS-failures.md +46 -0
- package/docs/CONVENTIONS-game-designs.md +75 -0
- package/docs/CONVENTIONS-hardware-reference.md +184 -0
- package/docs/CONVENTIONS-pitfalls.md +91 -0
- package/docs/CONVENTIONS-recipes.md +106 -0
- package/docs/CONVENTIONS-techniques.md +309 -0
- package/docs/CONVENTIONS-toolchain-reference.md +89 -0
- package/docs/ONTOLOGY.md +655 -0
- package/docs/art/art-production-reference.md +157 -0
- package/docs/art/asset-pipelines.md +586 -0
- package/docs/c64-failure-patterns.md +693 -0
- package/docs/demo-design/demo-composition.md +349 -0
- package/docs/demo-design/demo-design-philosophy.md +320 -0
- package/docs/demo-design/intro-cracktro-patterns.md +435 -0
- package/docs/figures/ctm-spd-embed-probe.png +0 -0
- package/docs/figures/dot-flag-mid-5000000.png +0 -0
- package/docs/figures/dot-flag-no-erase-12000000.png +0 -0
- package/docs/figures/edges.json +1 -0
- package/docs/figures/fig6-architecture.png +0 -0
- package/docs/figures/fig7-ontology.png +0 -0
- package/docs/figures/screen-dissolve-mid-frame25.png +0 -0
- package/docs/figures/screen-dissolve-wipe-frame25.png +0 -0
- package/docs/figures/spindle-two-parts-part1.png +0 -0
- package/docs/figures/spindle-two-parts-part2.png +0 -0
- package/docs/figures/starters/action-puzzle.png +0 -0
- package/docs/figures/starters/adventure.png +0 -0
- package/docs/figures/starters/beat-em-up.png +0 -0
- package/docs/figures/starters/demo.png +0 -0
- package/docs/figures/starters/platformer.png +0 -0
- package/docs/figures/starters/shmup-vertical.png +0 -0
- package/docs/figures/tape-kernal-master-ntsc.png +0 -0
- package/docs/figures/tape-kernal-master-pal.png +0 -0
- package/docs/figures/tape-turbo-loader-ntsc.png +0 -0
- package/docs/figures/tape-turbo-loader-pal.png +0 -0
- package/docs/figures/tape-turbo-master-ntsc.png +0 -0
- package/docs/figures/tape-turbo-master-pal.png +0 -0
- package/docs/figures/tech-tech-pad2-12000000.png +0 -0
- package/docs/figures/tech-tech-pad4-12000000.png +0 -0
- package/docs/figures/tech-tech-seven-12000000.png +0 -0
- package/docs/figures/twister-mid-5000000.png +0 -0
- package/docs/figures/twister-straight-12000000.png +0 -0
- package/docs/figures/vector-balls-nosort-ntsc.png +0 -0
- package/docs/figures/vector-balls-nosort.png +0 -0
- package/docs/formats/c64-file-formats.md +1212 -0
- package/docs/formats/iec-disk-reference.md +754 -0
- package/docs/game-design/c64-game-archetypes.md +273 -0
- package/docs/game-design/designs/falling-blocks.md +54 -0
- package/docs/game-design/designs/platformer-scaffold.md +48 -0
- package/docs/game-design/designs/simple-shmup.md +27 -0
- package/docs/game-design/enemy-behaviour-and-difficulty.md +349 -0
- package/docs/game-design/game-design-patterns.md +1189 -0
- package/docs/game-design/game-structure.md +354 -0
- package/docs/game-design/production-planning.md +349 -0
- package/docs/game-design/reference-game-sources.md +90 -0
- package/docs/hardware/6502-illegal-opcodes.md +1353 -0
- package/docs/hardware/6510-cpu-reference.md +2112 -0
- package/docs/hardware/c64-memory-map.md +3184 -0
- package/docs/hardware/c64-registers-reference.md +996 -0
- package/docs/hardware/cia-reference.md +1281 -0
- package/docs/hardware/kernal-routines-reference.md +1836 -0
- package/docs/hardware/pal-ntsc-reference.md +813 -0
- package/docs/hardware/sid-reference.md +1137 -0
- package/docs/hardware/vic-ii-reference.md +1714 -0
- package/docs/music/music-production-reference.md +403 -0
- package/docs/pitfalls/banking.md +1067 -0
- package/docs/pitfalls/cia.md +1147 -0
- package/docs/pitfalls/cpu.md +874 -0
- package/docs/pitfalls/input.md +282 -0
- package/docs/pitfalls/kernal-and-io.md +1615 -0
- package/docs/pitfalls/loader.md +1191 -0
- package/docs/pitfalls/logic.md +94 -0
- package/docs/pitfalls/maths.md +187 -0
- package/docs/pitfalls/raster-and-badline.md +721 -0
- package/docs/pitfalls/region-timing.md +660 -0
- package/docs/pitfalls/scroll.md +207 -0
- package/docs/pitfalls/sid.md +738 -0
- package/docs/pitfalls/sprite.md +979 -0
- package/docs/pitfalls/text-mode-render.md +1172 -0
- package/docs/recipes/cc65/cartridge-8k.md +398 -0
- package/docs/recipes/cc65/hello-world-conio.md +76 -0
- package/docs/recipes/cc65/memory-layout.md +312 -0
- package/docs/recipes/cc65/screenshots/README.md +14 -0
- package/docs/recipes/cc65/screenshots/cartridge-8k-ntsc.png +0 -0
- package/docs/recipes/cc65/screenshots/cartridge-8k.png +0 -0
- package/docs/recipes/cc65/screenshots/hello-world-conio.png +0 -0
- package/docs/recipes/cc65/screenshots/memory-layout-ntsc.png +0 -0
- package/docs/recipes/cc65/screenshots/memory-layout.png +0 -0
- package/docs/recipes/kickassembler/basic-float-calls.md +578 -0
- package/docs/recipes/kickassembler/basic-wedge.md +398 -0
- package/docs/recipes/kickassembler/big-font-scroller.md +620 -0
- package/docs/recipes/kickassembler/charset-copy-rom-to-ram.md +623 -0
- package/docs/recipes/kickassembler/cia-revision-detect.md +366 -0
- package/docs/recipes/kickassembler/colour-cycling.md +343 -0
- package/docs/recipes/kickassembler/colour-fade.md +303 -0
- package/docs/recipes/kickassembler/compare-16bit-signed.md +1007 -0
- package/docs/recipes/kickassembler/cracktro-template.md +526 -0
- package/docs/recipes/kickassembler/crt-banked.md +276 -0
- package/docs/recipes/kickassembler/dos-error-codes.md +466 -0
- package/docs/recipes/kickassembler/dot-flag.md +576 -0
- package/docs/recipes/kickassembler/drive-job-queue.md +667 -0
- package/docs/recipes/kickassembler/drive-via-probe.md +690 -0
- package/docs/recipes/kickassembler/dycp-scroller.md +568 -0
- package/docs/recipes/kickassembler/dypp-sprite-scroller.md +681 -0
- package/docs/recipes/kickassembler/dysp.md +670 -0
- package/docs/recipes/kickassembler/easyflash-save.md +800 -0
- package/docs/recipes/kickassembler/eight-way-scroll.md +1484 -0
- package/docs/recipes/kickassembler/file-io-roundtrip.md +546 -0
- package/docs/recipes/kickassembler/fire-effect.md +420 -0
- package/docs/recipes/kickassembler/fld.md +414 -0
- package/docs/recipes/kickassembler/fli-image.md +336 -0
- package/docs/recipes/kickassembler/four-player-read.md +672 -0
- package/docs/recipes/kickassembler/headless-verify.md +228 -0
- package/docs/recipes/kickassembler/hello-world.md +98 -0
- package/docs/recipes/kickassembler/high-score-insert.md +660 -0
- package/docs/recipes/kickassembler/hires-plot-line.md +723 -0
- package/docs/recipes/kickassembler/irq-chain.md +316 -0
- package/docs/recipes/kickassembler/irq-owns-port.md +668 -0
- package/docs/recipes/kickassembler/isometric-room.md +1145 -0
- package/docs/recipes/kickassembler/light-pen-read.md +926 -0
- package/docs/recipes/kickassembler/logic-rate-decoupling.md +670 -0
- package/docs/recipes/kickassembler/mci-interlace.md +543 -0
- package/docs/recipes/kickassembler/memory-layout.md +277 -0
- package/docs/recipes/kickassembler/mouse-1351-read.md +801 -0
- package/docs/recipes/kickassembler/nmi-timer-tick.md +562 -0
- package/docs/recipes/kickassembler/own-keyscan.md +881 -0
- package/docs/recipes/kickassembler/paddle-read.md +751 -0
- package/docs/recipes/kickassembler/palette-cells.md +219 -0
- package/docs/recipes/kickassembler/pseudo-3d-road.md +930 -0
- package/docs/recipes/kickassembler/pwm-digi.md +463 -0
- package/docs/recipes/kickassembler/raster-bars.md +264 -0
- package/docs/recipes/kickassembler/reu-dma.md +1019 -0
- package/docs/recipes/kickassembler/runtime-relocation.md +743 -0
- package/docs/recipes/kickassembler/screen-dissolve.md +545 -0
- package/docs/recipes/kickassembler/screen-wipe.md +408 -0
- package/docs/recipes/kickassembler/screenshots/README.md +72 -0
- package/docs/recipes/kickassembler/screenshots/basic-float-calls-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/basic-float-calls.png +0 -0
- package/docs/recipes/kickassembler/screenshots/basic-wedge-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/basic-wedge.png +0 -0
- package/docs/recipes/kickassembler/screenshots/big-font-scroller-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/big-font-scroller.png +0 -0
- package/docs/recipes/kickassembler/screenshots/charset-copy-rom-to-ram-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/charset-copy-rom-to-ram.png +0 -0
- package/docs/recipes/kickassembler/screenshots/cia-revision-detect-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/cia-revision-detect.png +0 -0
- package/docs/recipes/kickassembler/screenshots/colour-cycling-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/colour-cycling.png +0 -0
- package/docs/recipes/kickassembler/screenshots/colour-fade-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/colour-fade.png +0 -0
- package/docs/recipes/kickassembler/screenshots/compare-16bit-signed-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/compare-16bit-signed.png +0 -0
- package/docs/recipes/kickassembler/screenshots/cracktro-template.png +0 -0
- package/docs/recipes/kickassembler/screenshots/crt-banked-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/crt-banked.png +0 -0
- package/docs/recipes/kickassembler/screenshots/dos-error-codes-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/dos-error-codes.png +0 -0
- package/docs/recipes/kickassembler/screenshots/dot-flag-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/dot-flag.png +0 -0
- package/docs/recipes/kickassembler/screenshots/drive-job-queue-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/drive-job-queue.png +0 -0
- package/docs/recipes/kickassembler/screenshots/drive-via-probe-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/drive-via-probe.png +0 -0
- package/docs/recipes/kickassembler/screenshots/dycp-scroller-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/dycp-scroller.png +0 -0
- package/docs/recipes/kickassembler/screenshots/dypp-sprite-scroller-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/dypp-sprite-scroller.png +0 -0
- package/docs/recipes/kickassembler/screenshots/dysp-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/dysp.png +0 -0
- package/docs/recipes/kickassembler/screenshots/easyflash-save-ntsc-run2.png +0 -0
- package/docs/recipes/kickassembler/screenshots/easyflash-save-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/easyflash-save-run2.png +0 -0
- package/docs/recipes/kickassembler/screenshots/easyflash-save.png +0 -0
- package/docs/recipes/kickassembler/screenshots/eight-way-scroll-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/eight-way-scroll.png +0 -0
- package/docs/recipes/kickassembler/screenshots/file-io-roundtrip-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/file-io-roundtrip.png +0 -0
- package/docs/recipes/kickassembler/screenshots/fire-effect-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/fire-effect.png +0 -0
- package/docs/recipes/kickassembler/screenshots/fld-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/fld.png +0 -0
- package/docs/recipes/kickassembler/screenshots/fli-image.png +0 -0
- package/docs/recipes/kickassembler/screenshots/four-player-read-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/four-player-read.png +0 -0
- package/docs/recipes/kickassembler/screenshots/headless-verify-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/headless-verify.png +0 -0
- package/docs/recipes/kickassembler/screenshots/hello-world.png +0 -0
- package/docs/recipes/kickassembler/screenshots/high-score-insert-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/high-score-insert.png +0 -0
- package/docs/recipes/kickassembler/screenshots/hires-plot-line-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/hires-plot-line.png +0 -0
- package/docs/recipes/kickassembler/screenshots/irq-chain-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/irq-chain.png +0 -0
- package/docs/recipes/kickassembler/screenshots/irq-owns-port-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/irq-owns-port.png +0 -0
- package/docs/recipes/kickassembler/screenshots/isometric-room-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/isometric-room.png +0 -0
- package/docs/recipes/kickassembler/screenshots/light-pen-read-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/light-pen-read.png +0 -0
- package/docs/recipes/kickassembler/screenshots/logic-rate-decoupling-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/logic-rate-decoupling.png +0 -0
- package/docs/recipes/kickassembler/screenshots/mci-interlace-average.png +0 -0
- package/docs/recipes/kickassembler/screenshots/mci-interlace-frame-b.png +0 -0
- package/docs/recipes/kickassembler/screenshots/mci-interlace-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/mci-interlace.png +0 -0
- package/docs/recipes/kickassembler/screenshots/memory-layout-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/memory-layout.png +0 -0
- package/docs/recipes/kickassembler/screenshots/mouse-1351-read-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/mouse-1351-read.png +0 -0
- package/docs/recipes/kickassembler/screenshots/nmi-timer-tick-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/nmi-timer-tick.png +0 -0
- package/docs/recipes/kickassembler/screenshots/own-keyscan-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/own-keyscan.png +0 -0
- package/docs/recipes/kickassembler/screenshots/paddle-read-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/paddle-read.png +0 -0
- package/docs/recipes/kickassembler/screenshots/palette-cells-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/palette-cells.png +0 -0
- package/docs/recipes/kickassembler/screenshots/pseudo-3d-road-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/pseudo-3d-road.png +0 -0
- package/docs/recipes/kickassembler/screenshots/pwm-digi-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/pwm-digi.png +0 -0
- package/docs/recipes/kickassembler/screenshots/raster-bars.png +0 -0
- package/docs/recipes/kickassembler/screenshots/reu-dma-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/reu-dma.png +0 -0
- package/docs/recipes/kickassembler/screenshots/runtime-relocation-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/runtime-relocation.png +0 -0
- package/docs/recipes/kickassembler/screenshots/screen-dissolve-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/screen-dissolve.png +0 -0
- package/docs/recipes/kickassembler/screenshots/screen-wipe-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/screen-wipe.png +0 -0
- package/docs/recipes/kickassembler/screenshots/scroll-panel-split-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/scroll-panel-split.png +0 -0
- package/docs/recipes/kickassembler/screenshots/sfx-in-player-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/sfx-in-player.png +0 -0
- package/docs/recipes/kickassembler/screenshots/shadebobs-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/shadebobs.png +0 -0
- package/docs/recipes/kickassembler/screenshots/sideborder-open.png +0 -0
- package/docs/recipes/kickassembler/screenshots/sine-scroller.png +0 -0
- package/docs/recipes/kickassembler/screenshots/sine-table-runtime-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/sine-table-runtime.png +0 -0
- package/docs/recipes/kickassembler/screenshots/software-sprite-preshifted-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/software-sprite-preshifted.png +0 -0
- package/docs/recipes/kickassembler/screenshots/speedcode-generator-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/speedcode-generator.png +0 -0
- package/docs/recipes/kickassembler/screenshots/sprite-border-scroller-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/sprite-border-scroller.png +0 -0
- package/docs/recipes/kickassembler/screenshots/sprite-cache-flip-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/sprite-cache-flip.png +0 -0
- package/docs/recipes/kickassembler/screenshots/sprite-multiplex-24.png +0 -0
- package/docs/recipes/kickassembler/screenshots/sprite-multiplex-game-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/sprite-multiplex-game.png +0 -0
- package/docs/recipes/kickassembler/screenshots/sprite-priority-classes-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/sprite-priority-classes.png +0 -0
- package/docs/recipes/kickassembler/screenshots/sprite-sine-chain-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/sprite-sine-chain.png +0 -0
- package/docs/recipes/kickassembler/screenshots/sprite-stretcher-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/sprite-stretcher.png +0 -0
- package/docs/recipes/kickassembler/screenshots/sprites-only-screen-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/sprites-only-screen.png +0 -0
- package/docs/recipes/kickassembler/screenshots/sqrt-atan2-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/sqrt-atan2.png +0 -0
- package/docs/recipes/kickassembler/screenshots/stable-raster-irq-control.png +0 -0
- package/docs/recipes/kickassembler/screenshots/stable-raster-irq.png +0 -0
- package/docs/recipes/kickassembler/screenshots/tech-tech-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/tech-tech.png +0 -0
- package/docs/recipes/kickassembler/screenshots/tod-alarm-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/tod-alarm.png +0 -0
- package/docs/recipes/kickassembler/screenshots/topbottom-border-open.png +0 -0
- package/docs/recipes/kickassembler/screenshots/twister-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/twister.png +0 -0
- package/docs/recipes/kickassembler/screenshots/unit-test-driver-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/unit-test-driver.png +0 -0
- package/docs/recipes/kickassembler/screenshots/vector-balls-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/vector-balls.png +0 -0
- package/docs/recipes/kickassembler/screenshots/wireframe-ships-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/wireframe-ships.png +0 -0
- package/docs/recipes/kickassembler/scroll-panel-split.md +381 -0
- package/docs/recipes/kickassembler/sfx-in-player.md +907 -0
- package/docs/recipes/kickassembler/shadebobs.md +522 -0
- package/docs/recipes/kickassembler/sideborder-open.md +338 -0
- package/docs/recipes/kickassembler/sine-scroller.md +321 -0
- package/docs/recipes/kickassembler/sine-table-runtime.md +815 -0
- package/docs/recipes/kickassembler/software-sprite-preshifted.md +725 -0
- package/docs/recipes/kickassembler/speedcode-generator.md +606 -0
- package/docs/recipes/kickassembler/sprite-border-scroller.md +696 -0
- package/docs/recipes/kickassembler/sprite-cache-flip.md +1071 -0
- package/docs/recipes/kickassembler/sprite-multiplex-24.md +326 -0
- package/docs/recipes/kickassembler/sprite-multiplex-game.md +986 -0
- package/docs/recipes/kickassembler/sprite-priority-classes.md +463 -0
- package/docs/recipes/kickassembler/sprite-sine-chain.md +338 -0
- package/docs/recipes/kickassembler/sprite-stretcher.md +501 -0
- package/docs/recipes/kickassembler/sprites-only-screen.md +550 -0
- package/docs/recipes/kickassembler/sqrt-atan2.md +1014 -0
- package/docs/recipes/kickassembler/stable-raster-irq.md +360 -0
- package/docs/recipes/kickassembler/tape-turbo-loader.md +875 -0
- package/docs/recipes/kickassembler/tech-tech.md +679 -0
- package/docs/recipes/kickassembler/tod-alarm.md +754 -0
- package/docs/recipes/kickassembler/topbottom-border-open.md +407 -0
- package/docs/recipes/kickassembler/twister.md +518 -0
- package/docs/recipes/kickassembler/unit-test-driver.md +314 -0
- package/docs/recipes/kickassembler/vector-balls.md +545 -0
- package/docs/recipes/kickassembler/wireframe-ships.md +2324 -0
- package/docs/recipes/oscar64/actor-activation-window.md +807 -0
- package/docs/recipes/oscar64/adventure-engine.md +1094 -0
- package/docs/recipes/oscar64/attract-replay.md +465 -0
- package/docs/recipes/oscar64/beat-em-up-lanes.md +563 -0
- package/docs/recipes/oscar64/bfs-distance-map.md +723 -0
- package/docs/recipes/oscar64/bitmap-koala-viewer.md +355 -0
- package/docs/recipes/oscar64/car-contact.md +950 -0
- package/docs/recipes/oscar64/cave-scan.md +609 -0
- package/docs/recipes/oscar64/char-bullets.md +477 -0
- package/docs/recipes/oscar64/charset-animation.md +383 -0
- package/docs/recipes/oscar64/charset-parallax.md +401 -0
- package/docs/recipes/oscar64/destructible-terrain.md +1099 -0
- package/docs/recipes/oscar64/difficulty-tables.md +654 -0
- package/docs/recipes/oscar64/dig-and-guards.md +975 -0
- package/docs/recipes/oscar64/directory-reader.md +535 -0
- package/docs/recipes/oscar64/divide-check.md +581 -0
- package/docs/recipes/oscar64/double-buffer-nomirror.md +205 -0
- package/docs/recipes/oscar64/double-buffer.md +255 -0
- package/docs/recipes/oscar64/falling-blocks.md +874 -0
- package/docs/recipes/oscar64/fixed-point-jump-velocity.md +289 -0
- package/docs/recipes/oscar64/fixed-point-jump.md +210 -0
- package/docs/recipes/oscar64/flip-screen-rooms.md +793 -0
- package/docs/recipes/oscar64/frame-sync-loop-overrun.md +214 -0
- package/docs/recipes/oscar64/frame-sync-loop.md +254 -0
- package/docs/recipes/oscar64/game-tree-search.md +743 -0
- package/docs/recipes/oscar64/ghost-targeting.md +867 -0
- package/docs/recipes/oscar64/headless-verify.md +345 -0
- package/docs/recipes/oscar64/hello-world.md +83 -0
- package/docs/recipes/oscar64/high-score-persist.md +772 -0
- package/docs/recipes/oscar64/iffl-kernal-skip.md +421 -0
- package/docs/recipes/oscar64/joystick-input.md +358 -0
- package/docs/recipes/oscar64/lane-pursuit.md +1028 -0
- package/docs/recipes/oscar64/level-rle-decoder.md +620 -0
- package/docs/recipes/oscar64/lfsr-random-seed2.md +338 -0
- package/docs/recipes/oscar64/lfsr-random.md +403 -0
- package/docs/recipes/oscar64/load-asset-runtime.md +492 -0
- package/docs/recipes/oscar64/memory-layout.md +232 -0
- package/docs/recipes/oscar64/mixed-fighters.md +781 -0
- package/docs/recipes/oscar64/multi-sprite-object.md +533 -0
- package/docs/recipes/oscar64/nav-area-pathfinding.md +881 -0
- package/docs/recipes/oscar64/object-pool.md +572 -0
- package/docs/recipes/oscar64/pal-ntsc-detect.md +257 -0
- package/docs/recipes/oscar64/password-state.md +532 -0
- package/docs/recipes/oscar64/per-frame-hitbox.md +651 -0
- package/docs/recipes/oscar64/petscii-screen-codes.md +428 -0
- package/docs/recipes/oscar64/platformer-scaffold.md +1467 -0
- package/docs/recipes/oscar64/print-number.md +367 -0
- package/docs/recipes/oscar64/raster-bars.md +239 -0
- package/docs/recipes/oscar64/raster-profile-bars.md +404 -0
- package/docs/recipes/oscar64/rel-side-sectors.md +391 -0
- package/docs/recipes/oscar64/relative-file-records.md +378 -0
- package/docs/recipes/oscar64/save-load-seq-file.md +365 -0
- package/docs/recipes/oscar64/screenshots/README.md +99 -0
- package/docs/recipes/oscar64/screenshots/actor-activation-window-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/actor-activation-window.png +0 -0
- package/docs/recipes/oscar64/screenshots/adventure-engine-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/adventure-engine.png +0 -0
- package/docs/recipes/oscar64/screenshots/attract-replay-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/attract-replay.png +0 -0
- package/docs/recipes/oscar64/screenshots/beat-em-up-lanes-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/beat-em-up-lanes.png +0 -0
- package/docs/recipes/oscar64/screenshots/bfs-distance-map-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/bfs-distance-map.png +0 -0
- package/docs/recipes/oscar64/screenshots/bitmap-koala-viewer.png +0 -0
- package/docs/recipes/oscar64/screenshots/car-contact-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/car-contact.png +0 -0
- package/docs/recipes/oscar64/screenshots/cave-scan-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/cave-scan.png +0 -0
- package/docs/recipes/oscar64/screenshots/char-bullets-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/char-bullets.png +0 -0
- package/docs/recipes/oscar64/screenshots/charset-animation-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/charset-animation.png +0 -0
- package/docs/recipes/oscar64/screenshots/charset-parallax-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/charset-parallax.png +0 -0
- package/docs/recipes/oscar64/screenshots/destructible-terrain-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/destructible-terrain.png +0 -0
- package/docs/recipes/oscar64/screenshots/difficulty-tables-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/difficulty-tables.png +0 -0
- package/docs/recipes/oscar64/screenshots/dig-and-guards-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/dig-and-guards.png +0 -0
- package/docs/recipes/oscar64/screenshots/directory-reader-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/directory-reader.png +0 -0
- package/docs/recipes/oscar64/screenshots/divide-check-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/divide-check.png +0 -0
- package/docs/recipes/oscar64/screenshots/double-buffer-nomirror.png +0 -0
- package/docs/recipes/oscar64/screenshots/double-buffer-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/double-buffer.png +0 -0
- package/docs/recipes/oscar64/screenshots/falling-blocks-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/falling-blocks.png +0 -0
- package/docs/recipes/oscar64/screenshots/fixed-point-jump-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/fixed-point-jump-velocity-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/fixed-point-jump-velocity.png +0 -0
- package/docs/recipes/oscar64/screenshots/fixed-point-jump.png +0 -0
- package/docs/recipes/oscar64/screenshots/flip-screen-rooms-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/flip-screen-rooms.png +0 -0
- package/docs/recipes/oscar64/screenshots/frame-sync-loop-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/frame-sync-loop-overrun.png +0 -0
- package/docs/recipes/oscar64/screenshots/frame-sync-loop.png +0 -0
- package/docs/recipes/oscar64/screenshots/game-tree-search-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/game-tree-search.png +0 -0
- package/docs/recipes/oscar64/screenshots/ghost-targeting-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/ghost-targeting.png +0 -0
- package/docs/recipes/oscar64/screenshots/headless-verify-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/headless-verify.png +0 -0
- package/docs/recipes/oscar64/screenshots/hello-world.png +0 -0
- package/docs/recipes/oscar64/screenshots/high-score-persist-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/high-score-persist.png +0 -0
- package/docs/recipes/oscar64/screenshots/iffl-kernal-skip-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/iffl-kernal-skip.png +0 -0
- package/docs/recipes/oscar64/screenshots/joystick-input-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/joystick-input.png +0 -0
- package/docs/recipes/oscar64/screenshots/lane-pursuit-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/lane-pursuit.png +0 -0
- package/docs/recipes/oscar64/screenshots/level-rle-decoder-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/level-rle-decoder.png +0 -0
- package/docs/recipes/oscar64/screenshots/lfsr-random-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/lfsr-random-seed2.png +0 -0
- package/docs/recipes/oscar64/screenshots/lfsr-random.png +0 -0
- package/docs/recipes/oscar64/screenshots/load-asset-runtime-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/load-asset-runtime.png +0 -0
- package/docs/recipes/oscar64/screenshots/memory-layout-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/memory-layout.png +0 -0
- package/docs/recipes/oscar64/screenshots/mixed-fighters-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/mixed-fighters.png +0 -0
- package/docs/recipes/oscar64/screenshots/multi-sprite-object-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/multi-sprite-object.png +0 -0
- package/docs/recipes/oscar64/screenshots/nav-area-pathfinding-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/nav-area-pathfinding.png +0 -0
- package/docs/recipes/oscar64/screenshots/object-pool-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/object-pool.png +0 -0
- package/docs/recipes/oscar64/screenshots/pal-ntsc-detect-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/pal-ntsc-detect-oldntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/pal-ntsc-detect-pal.png +0 -0
- package/docs/recipes/oscar64/screenshots/password-state-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/password-state.png +0 -0
- package/docs/recipes/oscar64/screenshots/per-frame-hitbox-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/per-frame-hitbox.png +0 -0
- package/docs/recipes/oscar64/screenshots/petscii-screen-codes-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/petscii-screen-codes.png +0 -0
- package/docs/recipes/oscar64/screenshots/platformer-scaffold-d64-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/platformer-scaffold-d64.png +0 -0
- package/docs/recipes/oscar64/screenshots/platformer-scaffold-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/platformer-scaffold.png +0 -0
- package/docs/recipes/oscar64/screenshots/print-number-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/print-number.png +0 -0
- package/docs/recipes/oscar64/screenshots/raster-bars.png +0 -0
- package/docs/recipes/oscar64/screenshots/raster-profile-bars-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/raster-profile-bars.png +0 -0
- package/docs/recipes/oscar64/screenshots/rel-side-sectors-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/rel-side-sectors.png +0 -0
- package/docs/recipes/oscar64/screenshots/relative-file-records-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/relative-file-records.png +0 -0
- package/docs/recipes/oscar64/screenshots/save-load-seq-file-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/save-load-seq-file.png +0 -0
- package/docs/recipes/oscar64/screenshots/seeded-level-fill-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/seeded-level-fill.png +0 -0
- package/docs/recipes/oscar64/screenshots/sfx-engine-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/sfx-engine.png +0 -0
- package/docs/recipes/oscar64/screenshots/sid-music-player.png +0 -0
- package/docs/recipes/oscar64/screenshots/simple-shmup.png +0 -0
- package/docs/recipes/oscar64/screenshots/slope-collision-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/slope-collision.png +0 -0
- package/docs/recipes/oscar64/screenshots/soft-scroll-h.png +0 -0
- package/docs/recipes/oscar64/screenshots/sprite-animation-table-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/sprite-animation-table.png +0 -0
- package/docs/recipes/oscar64/screenshots/sprite-multiplex-8.png +0 -0
- package/docs/recipes/oscar64/screenshots/stable-raster-irq.png +0 -0
- package/docs/recipes/oscar64/screenshots/text-input-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/text-input.png +0 -0
- package/docs/recipes/oscar64/screenshots/text-overlay-playfield-gameover.png +0 -0
- package/docs/recipes/oscar64/screenshots/text-overlay-playfield-notrail-control.png +0 -0
- package/docs/recipes/oscar64/screenshots/text-overlay-playfield-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/text-overlay-playfield-ungated-control.png +0 -0
- package/docs/recipes/oscar64/screenshots/text-overlay-playfield.png +0 -0
- package/docs/recipes/oscar64/screenshots/text-window-menu-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/text-window-menu.png +0 -0
- package/docs/recipes/oscar64/screenshots/tile-grid-collision-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/tile-grid-collision.png +0 -0
- package/docs/recipes/oscar64/screenshots/tile-map-render-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/tile-map-render.png +0 -0
- package/docs/recipes/oscar64/screenshots/two-player-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/two-player.png +0 -0
- package/docs/recipes/oscar64/screenshots/two-word-parser-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/two-word-parser.png +0 -0
- package/docs/recipes/oscar64/screenshots/vehicle-control-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/vehicle-control.png +0 -0
- package/docs/recipes/oscar64/screenshots/wave-director-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/wave-director.png +0 -0
- package/docs/recipes/oscar64/seeded-level-fill.md +364 -0
- package/docs/recipes/oscar64/sfx-engine.md +514 -0
- package/docs/recipes/oscar64/sid-music-player.md +441 -0
- package/docs/recipes/oscar64/simple-shmup.md +1000 -0
- package/docs/recipes/oscar64/slope-collision.md +610 -0
- package/docs/recipes/oscar64/soft-scroll-h.md +261 -0
- package/docs/recipes/oscar64/sprite-animation-table.md +671 -0
- package/docs/recipes/oscar64/sprite-multiplex-8.md +314 -0
- package/docs/recipes/oscar64/stable-raster-irq.md +236 -0
- package/docs/recipes/oscar64/text-input.md +274 -0
- package/docs/recipes/oscar64/text-overlay-playfield.md +617 -0
- package/docs/recipes/oscar64/text-window-menu.md +551 -0
- package/docs/recipes/oscar64/tile-grid-collision.md +563 -0
- package/docs/recipes/oscar64/tile-map-render.md +395 -0
- package/docs/recipes/oscar64/two-player.md +528 -0
- package/docs/recipes/oscar64/two-word-parser.md +510 -0
- package/docs/recipes/oscar64/vehicle-control.md +771 -0
- package/docs/recipes/oscar64/wave-director.md +787 -0
- package/docs/recipes/runs.json +1606 -0
- package/docs/runtime/sim6502-reference.md +466 -0
- package/docs/runtime/vice-mcp-reference.md +351 -0
- package/docs/runtime/vice-reference.md +1393 -0
- package/docs/techniques/bitmap-modes.md +685 -0
- package/docs/techniques/cpu-cycle-tricks.md +1570 -0
- package/docs/techniques/effects-vector-3d.md +1431 -0
- package/docs/techniques/file-io.md +1427 -0
- package/docs/techniques/input.md +1072 -0
- package/docs/techniques/loaders-packers.md +768 -0
- package/docs/techniques/logic.md +3091 -0
- package/docs/techniques/maths.md +1524 -0
- package/docs/techniques/memory-banking.md +1686 -0
- package/docs/techniques/music-sid.md +1144 -0
- package/docs/techniques/raster.md +1543 -0
- package/docs/techniques/scroll.md +1292 -0
- package/docs/techniques/sprite.md +2566 -0
- package/docs/techniques/text-mode-render.md +717 -0
- package/docs/techniques/text.md +1095 -0
- package/docs/techniques/transitions.md +405 -0
- package/docs/toolchains/cartconv-reference.md +262 -0
- package/docs/toolchains/cc1541-reference.md +316 -0
- package/docs/toolchains/cc65-reference.md +667 -0
- package/docs/toolchains/error-sources/cc65/README.md +1 -0
- package/docs/toolchains/error-sources/cc65/missing-setcpu.s +4 -0
- package/docs/toolchains/error-sources/cc65/range-error.s +4 -0
- package/docs/toolchains/error-sources/cc65/segment-overflow.c +6 -0
- package/docs/toolchains/error-sources/cc65/unresolved-external.c +6 -0
- package/docs/toolchains/error-sources/kickassembler/README.md +1 -0
- package/docs/toolchains/error-sources/kickassembler/branch-out-of-range.asm +4 -0
- package/docs/toolchains/error-sources/kickassembler/failed-assert.asm +3 -0
- package/docs/toolchains/error-sources/kickassembler/flat-lib.asm +4 -0
- package/docs/toolchains/error-sources/kickassembler/import-source-no-namespace.asm +5 -0
- package/docs/toolchains/error-sources/kickassembler/macro-without-parentheses.asm +6 -0
- package/docs/toolchains/error-sources/kickassembler/memory-block-overlap.asm +4 -0
- package/docs/toolchains/error-sources/kickassembler/symbol-clash-lib.asm +5 -0
- package/docs/toolchains/error-sources/kickassembler/symbol-clash-main.asm +6 -0
- package/docs/toolchains/error-sources/kickassembler/unknown-symbol.asm +4 -0
- package/docs/toolchains/error-sources/oscar64/README.md +1 -0
- package/docs/toolchains/error-sources/oscar64/asm-addressing-mode.c +7 -0
- package/docs/toolchains/error-sources/oscar64/region-overflow.c +6 -0
- package/docs/toolchains/error-sources/oscar64/undefined-extern-var.c +5 -0
- package/docs/toolchains/error-sources/oscar64/undefined-extern.c +6 -0
- package/docs/toolchains/error-sources/oscar64/unknown-vic-field.c +6 -0
- package/docs/toolchains/error-sources/oscar64/void-main.c +3 -0
- package/docs/toolchains/kickassembler-reference.md +1020 -0
- package/docs/toolchains/memory-layout-planning.md +362 -0
- package/docs/toolchains/oscar64-headers-reference.md +583 -0
- package/docs/toolchains/oscar64-reference.md +892 -0
- package/docs/toolchains/petcat-reference.md +290 -0
- package/docs/toolchains/png2prg.md +188 -0
- package/docs/toolchains/release-disk.md +314 -0
- package/docs/toolchains/sidreloc.md +236 -0
- package/docs/toolchains/spindle-reference.md +487 -0
- package/docs/toolchains/tape-mastering.md +592 -0
- package/docs/toolchains/unit-testing-6502.md +281 -0
- package/docs/workflow/agent-harness.md +478 -0
- package/package.json +105 -0
- package/templates/_harness/CLAUDE.md.template +91 -0
- package/templates/_harness/PLAN.md.template +72 -0
- package/templates/_harness/check.py +442 -0
- package/templates/_harness/claude/settings.json +17 -0
- package/templates/_harness/gen-asm-header.py +96 -0
- package/templates/_harness/harness.mk +273 -0
- package/templates/_harness/hooks/plan-gate.py +185 -0
- package/templates/_harness/meter/frame_meter.asm +311 -0
- package/templates/_harness/meter/frame_meter.c +155 -0
- package/templates/_harness/meter/frame_meter.h +80 -0
- package/templates/_harness/zp-used.py +104 -0
- package/templates/action-puzzle/.claude/settings.json +17 -0
- package/templates/action-puzzle/.mcp.json +10 -0
- package/templates/action-puzzle/CLAUDE.md +120 -0
- package/templates/action-puzzle/Makefile +125 -0
- package/templates/action-puzzle/PLAN.md +388 -0
- package/templates/action-puzzle/README.md +240 -0
- package/templates/action-puzzle/expect.json +25 -0
- package/templates/action-puzzle/src/cave.c +280 -0
- package/templates/action-puzzle/src/cave.h +80 -0
- package/templates/action-puzzle/src/gen_autopilot.h +20 -0
- package/templates/action-puzzle/src/gen_caves.h +59 -0
- package/templates/action-puzzle/src/gen_notes.h +16 -0
- package/templates/action-puzzle/src/hiscore.c +152 -0
- package/templates/action-puzzle/src/hiscore.h +41 -0
- package/templates/action-puzzle/src/level.c +64 -0
- package/templates/action-puzzle/src/level.h +24 -0
- package/templates/action-puzzle/src/main.c +599 -0
- package/templates/action-puzzle/src/render.c +239 -0
- package/templates/action-puzzle/src/render.h +31 -0
- package/templates/action-puzzle/src/sound.c +158 -0
- package/templates/action-puzzle/src/sound.h +22 -0
- package/templates/action-puzzle/tools/disk_check.py +77 -0
- package/templates/action-puzzle/tools/drive.py +106 -0
- package/templates/action-puzzle/tools/gen.py +454 -0
- package/templates/action-puzzle/tools/model_check.py +49 -0
- package/templates/action-puzzle/tools/verdict_codes.py +24 -0
- package/templates/adventure/.claude/settings.json +17 -0
- package/templates/adventure/.mcp.json +10 -0
- package/templates/adventure/CLAUDE.md +118 -0
- package/templates/adventure/Makefile +113 -0
- package/templates/adventure/PLAN.md +396 -0
- package/templates/adventure/README.md +277 -0
- package/templates/adventure/expect.json +25 -0
- package/templates/adventure/src/engine.c +543 -0
- package/templates/adventure/src/engine.h +48 -0
- package/templates/adventure/src/gen_script.h +141 -0
- package/templates/adventure/src/gen_world.h +511 -0
- package/templates/adventure/src/main.c +439 -0
- package/templates/adventure/src/picture.c +83 -0
- package/templates/adventure/src/picture.h +16 -0
- package/templates/adventure/src/save.c +159 -0
- package/templates/adventure/src/save.h +25 -0
- package/templates/adventure/src/sound.c +71 -0
- package/templates/adventure/src/sound.h +16 -0
- package/templates/adventure/src/text.c +349 -0
- package/templates/adventure/src/text.h +40 -0
- package/templates/adventure/tools/disk_check.py +128 -0
- package/templates/adventure/tools/drive.py +119 -0
- package/templates/adventure/tools/gen.py +1033 -0
- package/templates/adventure/tools/world.py +356 -0
- package/templates/beat-em-up/.claude/settings.json +17 -0
- package/templates/beat-em-up/.mcp.json +10 -0
- package/templates/beat-em-up/CLAUDE.md +136 -0
- package/templates/beat-em-up/Makefile +96 -0
- package/templates/beat-em-up/PLAN.md +502 -0
- package/templates/beat-em-up/README.md +315 -0
- package/templates/beat-em-up/expect-gameover.json +8 -0
- package/templates/beat-em-up/expect.json +29 -0
- package/templates/beat-em-up/src/anim.c +70 -0
- package/templates/beat-em-up/src/art.c +344 -0
- package/templates/beat-em-up/src/autopilot.h +154 -0
- package/templates/beat-em-up/src/brute.c +273 -0
- package/templates/beat-em-up/src/enemy.c +281 -0
- package/templates/beat-em-up/src/engine.asm +512 -0
- package/templates/beat-em-up/src/fighter.c +384 -0
- package/templates/beat-em-up/src/game.h +269 -0
- package/templates/beat-em-up/src/hud.c +135 -0
- package/templates/beat-em-up/src/main.c +341 -0
- package/templates/beat-em-up/src/sound.c +44 -0
- package/templates/beat-em-up/src/street.c +93 -0
- package/templates/beat-em-up/src/verdict.h +523 -0
- package/templates/beat-em-up/src/view.c +362 -0
- package/templates/beat-em-up/tools/drive.py +105 -0
- package/templates/beat-em-up/tools/flickercheck.py +273 -0
- package/templates/demo/.claude/settings.json +17 -0
- package/templates/demo/.mcp.json +10 -0
- package/templates/demo/CLAUDE.md +129 -0
- package/templates/demo/Makefile +66 -0
- package/templates/demo/PLAN.md +401 -0
- package/templates/demo/README.md +322 -0
- package/templates/demo/expect.json +945 -0
- package/templates/demo/src/config.asm +80 -0
- package/templates/demo/src/framework.asm +390 -0
- package/templates/demo/src/main.asm +139 -0
- package/templates/demo/src/music.asm +197 -0
- package/templates/demo/src/part_main.asm +279 -0
- package/templates/demo/src/part_title.asm +79 -0
- package/templates/demo/src/parts.asm +43 -0
- package/templates/demo/src/tables.asm +73 -0
- package/templates/demo/src/verdict.asm +179 -0
- package/templates/demo/tools/audio.py +80 -0
- package/templates/demo/tools/gen_expect.py +167 -0
- package/templates/demo/tools/probe.py +95 -0
- package/templates/hello/.claude/settings.json +17 -0
- package/templates/hello/.mcp.json +10 -0
- package/templates/hello/CLAUDE.md +100 -0
- package/templates/hello/Makefile +38 -0
- package/templates/hello/PLAN.md +200 -0
- package/templates/hello/expect.json +20 -0
- package/templates/hello/src/main.c +161 -0
- package/templates/hello/src/sprite.asm +27 -0
- package/templates/hello-kick/.claude/settings.json +17 -0
- package/templates/hello-kick/.mcp.json +10 -0
- package/templates/hello-kick/CLAUDE.md +100 -0
- package/templates/hello-kick/Makefile +17 -0
- package/templates/hello-kick/PLAN.md +199 -0
- package/templates/hello-kick/expect.json +20 -0
- package/templates/hello-kick/src/main.asm +233 -0
- package/templates/platformer/.claude/settings.json +17 -0
- package/templates/platformer/.mcp.json +10 -0
- package/templates/platformer/CLAUDE.md +125 -0
- package/templates/platformer/Makefile +86 -0
- package/templates/platformer/PLAN.md +360 -0
- package/templates/platformer/README.md +277 -0
- package/templates/platformer/expect.json +32 -0
- package/templates/platformer/src/actors.c +289 -0
- package/templates/platformer/src/anim.c +51 -0
- package/templates/platformer/src/art.c +212 -0
- package/templates/platformer/src/autopilot.h +30 -0
- package/templates/platformer/src/engine.asm +253 -0
- package/templates/platformer/src/game.h +248 -0
- package/templates/platformer/src/hud.c +90 -0
- package/templates/platformer/src/level.c +184 -0
- package/templates/platformer/src/main.c +306 -0
- package/templates/platformer/src/player.c +268 -0
- package/templates/platformer/src/sound.c +42 -0
- package/templates/platformer/src/verdict.h +206 -0
- package/templates/platformer/src/view.c +331 -0
- package/templates/platformer/stage-expect.json +6 -0
- package/templates/platformer/tools/drive.py +112 -0
- package/templates/platformer/tools/tearcheck.py +229 -0
- package/templates/shmup-vertical/.claude/settings.json +17 -0
- package/templates/shmup-vertical/.mcp.json +10 -0
- package/templates/shmup-vertical/CLAUDE.md +137 -0
- package/templates/shmup-vertical/Makefile +131 -0
- package/templates/shmup-vertical/PLAN.md +569 -0
- package/templates/shmup-vertical/README.md +189 -0
- package/templates/shmup-vertical/expect.json +38 -0
- package/templates/shmup-vertical/src/bullets.c +158 -0
- package/templates/shmup-vertical/src/bullets.h +35 -0
- package/templates/shmup-vertical/src/display.c +239 -0
- package/templates/shmup-vertical/src/display.h +40 -0
- package/templates/shmup-vertical/src/game.h +85 -0
- package/templates/shmup-vertical/src/glyph.asm +358 -0
- package/templates/shmup-vertical/src/hiscore.c +141 -0
- package/templates/shmup-vertical/src/hiscore.h +16 -0
- package/templates/shmup-vertical/src/hit.asm +180 -0
- package/templates/shmup-vertical/src/hitbox.c +59 -0
- package/templates/shmup-vertical/src/hitbox.h +25 -0
- package/templates/shmup-vertical/src/kernel.asm +330 -0
- package/templates/shmup-vertical/src/level.c +174 -0
- package/templates/shmup-vertical/src/level.h +27 -0
- package/templates/shmup-vertical/src/main.c +824 -0
- package/templates/shmup-vertical/src/mux.asm +321 -0
- package/templates/shmup-vertical/src/sound.asm +255 -0
- package/templates/shmup-vertical/src/step.asm +169 -0
- package/templates/shmup-vertical/src/waves.c +195 -0
- package/templates/shmup-vertical/src/waves.h +33 -0
- package/templates/shmup-vertical/stage-expect.json +6 -0
- package/templates/shmup-vertical/tools/drive.py +152 -0
- package/templates/shmup-vertical/tools/joytest.py +159 -0
- package/templates/shmup-vertical/tools/meter.py +15 -0
- package/templates/shmup-vertical/tools/phases.py +73 -0
|
@@ -0,0 +1,1836 @@
|
|
|
1
|
+
# KERNAL Routines Reference
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
The Commodore 64 KERNAL is the 8 KiB ROM at `$E000-$FFFF` that provides the
|
|
6
|
+
machine's operating-system layer: character I/O, file I/O over the IEC serial
|
|
7
|
+
bus, the screen editor, the 60/50 Hz IRQ jiffy clock, the STOP-key check, and
|
|
8
|
+
the cold-start / warm-start sequences. User programs do not call KERNAL code at
|
|
9
|
+
its real ROM addresses — those moved between Commodore machines (VIC-20, PET,
|
|
10
|
+
C64, C128, Plus/4). Instead, the KERNAL exposes a **jump table** in the last
|
|
11
|
+
128 bytes of the address space, from `$FF81` upward, where each entry is a
|
|
12
|
+
3-byte `JMP abs` instruction to the real routine. Commodore promised this
|
|
13
|
+
table would be source-compatible across machines; in practice the shared
|
|
14
|
+
part is narrower than the whole table. The block from `$FFC0` (OPEN) to
|
|
15
|
+
`$FFEA` (UDTIM) sits at the same positions on every machine from the PET
|
|
16
|
+
onward; BASIC 4 PETs also carry `$FF93-$FFBD`; the VIC-20's table starts at
|
|
17
|
+
`$FF8A` (RESTOR) and holds 36 of the 39 entries slot-for-slot with the C64's,
|
|
18
|
+
with `$FF7F-$FF84` occupied by its IRQ/BRK `JMP ($0316)`/`JMP ($0314)` tails;
|
|
19
|
+
SCREEN/PLOT/IOBASE at `$FFED-$FFF3` first appear on the VIC-20 and
|
|
20
|
+
CINT/IOINIT/RAMTAS at `$FF81-$FF87` first on the C64. The Plus/4 and C128
|
|
21
|
+
keep all 39 entries at the C64 positions. (An earlier version of this page
|
|
22
|
+
said every Commodore 8-bit shared the table from `$FF81`; the PET and VIC-20
|
|
23
|
+
ROMs do not. Read from the VICE ROM images: PET 901439-04-07 / 901465-03 /
|
|
24
|
+
901465-22, VIC-20 901486-07, C64 901227-03, Plus/4 318004-05, C128
|
|
25
|
+
318020-05.)
|
|
26
|
+
|
|
27
|
+
There are **39 jump-table entries** spanning `$FF81-$FFF3`. The portion from
|
|
28
|
+
`$FFC0-$FFF5` is the historically-documented "user jump table" that the
|
|
29
|
+
*Commodore 64 Programmer's Reference Guide* and the *KERNAL Reference Manual*
|
|
30
|
+
encourage application programmers to call directly. The entries from
|
|
31
|
+
`$FF81-$FFBD` are lower-level — most are used internally by the higher-level
|
|
32
|
+
routines, but a handful (SETLFS, SETNAM, READST, MEMTOP, MEMBOT) are essential
|
|
33
|
+
for application code as well.
|
|
34
|
+
|
|
35
|
+
Every routine in this document is documented at its **jump-table address**,
|
|
36
|
+
not its real ROM address. Calling code should always `JSR $FFD2` (CHROUT),
|
|
37
|
+
never `JSR $F1CA` (CHROUT's own body, reached through the `$0326` vector) or
|
|
38
|
+
`JSR $E716` (the screen-output routine CHROUT dispatches to when the output
|
|
39
|
+
device is 3; device 4 and up goes to IECOUT at `$EDDD`). An earlier version
|
|
40
|
+
of this page named `$E716` as CHROUT's ROM address; it is the screen editor's
|
|
41
|
+
print routine — `$0326` defaults to `$F1CA` in every 901227 ROM (read from
|
|
42
|
+
the ROM images). The jump-table addresses are the stable contract; the
|
|
43
|
+
ROM-internal addresses are private implementation detail and can move
|
|
44
|
+
between KERNAL revisions and do move between Commodore machines. (The three
|
|
45
|
+
C64 revisions 901227-01, -02 and -03 differ in 277 bytes of in-place patches
|
|
46
|
+
spread over several dozen byte ranges — e.g. `$E4AC-$E4FF`, `$F428-$F44C`,
|
|
47
|
+
`$FF5B-$FF80` — and no entry address moved; `$F1CA`, `$E716` and `$F49E` are
|
|
48
|
+
at the same addresses in all three, so the examples here illustrate the
|
|
49
|
+
contract, not a case where it bit. Other Commodore machines put every
|
|
50
|
+
routine elsewhere.)
|
|
51
|
+
|
|
52
|
+
The 39 routines group naturally into eight categories:
|
|
53
|
+
|
|
54
|
+
1. **Initialization** — CINT, IOINIT, RAMTAS, RESTOR, VECTOR. These run at
|
|
55
|
+
power-on and reset; an application normally calls them only to recover
|
|
56
|
+
after corrupting state.
|
|
57
|
+
2. **High-level file I/O** — SETLFS, SETNAM, OPEN, CLOSE, CHKIN, CHKOUT,
|
|
58
|
+
CLRCHN, CLALL, LOAD, SAVE. The supported way to talk to disk, tape, and
|
|
59
|
+
printer.
|
|
60
|
+
3. **Character I/O** — CHRIN, CHROUT, GETIN. The screen editor and keyboard
|
|
61
|
+
queue funnel through these.
|
|
62
|
+
4. **Low-level IEC bus** — LISTEN, TALK, SECOND, TKSA, IECIN, IECOUT, UNLSN,
|
|
63
|
+
UNTLK, READST. Direct serial-bus protocol for fast loaders and bus
|
|
64
|
+
custom code.
|
|
65
|
+
5. **Screen + cursor** — PLOT, SCREEN.
|
|
66
|
+
6. **Time + STOP key** — RDTIM, SETTIM, UDTIM, STOP.
|
|
67
|
+
7. **Memory** — MEMTOP, MEMBOT (BASIC top-of-memory and bottom-of-memory
|
|
68
|
+
pointers).
|
|
69
|
+
8. **Misc** — SETMSG (KERNAL message control), SETTMO (IEEE timeout — no-op
|
|
70
|
+
on C64), SCNKEY (keyboard scan), IOBASE (I/O base address for
|
|
71
|
+
self-relocating code).
|
|
72
|
+
|
|
73
|
+
The KERNAL also exposes a set of **RAM vectors** at `$0314-$0333`
|
|
74
|
+
(IRQ, BRK, NMI, OPEN, CLOSE, CHKIN, CHKOUT, CLRCHN, CHRIN, CHROUT, STOP,
|
|
75
|
+
GETIN, CLALL, USRCMD, LOAD, SAVE) that the jump-table routines indirect
|
|
76
|
+
through. Patching these vectors is how programs hook character I/O (e.g.
|
|
77
|
+
to capture screen output for printer redirection) or replace the IRQ
|
|
78
|
+
handler (e.g. to drive a player routine). The VECTOR jump-table entry
|
|
79
|
+
(`$FF8D`) reads or writes all of them in one call. See [Vectors](#vectors)
|
|
80
|
+
below.
|
|
81
|
+
|
|
82
|
+
### KERNAL ROM revisions
|
|
83
|
+
|
|
84
|
+
Three production-run KERNAL ROMs shipped in the C64 lifetime, identified
|
|
85
|
+
by the Commodore part number printed on the ROM chip:
|
|
86
|
+
|
|
87
|
+
- **901227-01** — the original KERNAL. No PAL/NTSC detection: CINT's
|
|
88
|
+
entry is `JMP $E518` and IOINIT hard-codes the jiffy timer at `$411B`;
|
|
89
|
+
the screen clear fills colour RAM with white (`LDA #$01` at `$EA0B`);
|
|
90
|
+
after the tape FOUND message it waits for any key (`$F761`).
|
|
91
|
+
`$FF5B-$FF80` is unused filler.
|
|
92
|
+
- **901227-02** — adds PAL/NTSC detection: CINT's entry becomes a
|
|
93
|
+
wrapper at `$FF5B` that runs the old `$E518`, watches for raster line
|
|
94
|
+
311 (the VIC init table now sets `$D011`/`$D012` to `$9B`/`$37`),
|
|
95
|
+
stores the result in `$02A6` and lets IOINIT pick a jiffy timer of
|
|
96
|
+
`$4025` (PAL) or `$4295` (NTSC); RS-232 gains a PAL baud table at
|
|
97
|
+
`$E4EC` and region-aware OPEN/NMI code; BASIC's CHKOUT wrapper
|
|
98
|
+
(`$E118`) preserves A via a patch at `$E4AD`; the screen clear fills
|
|
99
|
+
colour RAM with the background colour (`$E4DA` = `LDA $D021`); the wait
|
|
100
|
+
after the tape FOUND message becomes a timed wait on the jiffy clock
|
|
101
|
+
that a keypress also ends (`$E4E0`). This is the only cassette change;
|
|
102
|
+
no tape timing constant differs (`$F767-$FCFB` identical).
|
|
103
|
+
- **901227-03** — the most common ROM, shipped in the bulk of C64
|
|
104
|
+
units sold from mid-1983 onward, and the source of all addresses in
|
|
105
|
+
this doc and the source most other documentation cites by default.
|
|
106
|
+
The screen clear fills colour RAM with the current text colour
|
|
107
|
+
(`$E4DA` = `LDA $0286`), plus small screen-editor (`$E57C-$E599`,
|
|
108
|
+
`$E621`) and RS-232 (`$EF94` -> `$E4D3`) patches.
|
|
109
|
+
|
|
110
|
+
Identify the revision from the byte at `$FF80`: `PEEK(65408)` returns
|
|
111
|
+
170 (-01), 0 (-02) or 3 (-03).
|
|
112
|
+
|
|
113
|
+
All three expose **the same 39-entry jump table at the same
|
|
114
|
+
addresses**, and the differences between them are in-place patches:
|
|
115
|
+
-01 and -03 differ in 277 bytes (-01/-02 227, -02/-03 57; `cmp` on the
|
|
116
|
+
VICE ROM images). No routine's entry address moved — 38 of 39
|
|
117
|
+
jump-table targets and all sixteen default RAM vectors are identical,
|
|
118
|
+
and the one changed entry (CINT) points at a wrapper that still calls
|
|
119
|
+
`$E518`. The IEC bus routines, RAMTAS, LOAD, CHROUT's body (`$F1CA`)
|
|
120
|
+
and the screen-output routine (`$E716`) are byte-identical in all
|
|
121
|
+
three, so `JSR $F1CA`, `JSR $E716` or `JSR $F49E` would in fact work on
|
|
122
|
+
every 901227 revision. Use the jump table anyway: it is the contract
|
|
123
|
+
Commodore kept across machines (the VIC-20, C128 and Plus/4 internals
|
|
124
|
+
are elsewhere), and it survives the RAM-vector hooks that direct calls
|
|
125
|
+
bypass. An earlier version of this section said -01 had a slower IEC
|
|
126
|
+
protocol, that -02 fixed a RAM-test bug, that internal addresses moved
|
|
127
|
+
between revisions and that `JSR $E716` was "silently broken" on another
|
|
128
|
+
revision; none of that is in the ROM bytes.
|
|
129
|
+
|
|
130
|
+
The C128 in C64 mode maps a plain C64 KERNAL image, not the C128
|
|
131
|
+
KERNAL. An earlier revision of this page named 318020-05 here; that
|
|
132
|
+
part is the C128-mode KERNAL/editor ROM, a different program (its jump
|
|
133
|
+
table points into `$C000`/`$E1xx` and its reset vector is `$FF3D`).
|
|
134
|
+
VICE x128 loads `kernal64-901227-03.bin` for C64 mode, byte-identical
|
|
135
|
+
to the C64's 901227-03 — measured by dumping the `c64rom` bank from
|
|
136
|
+
x128 3.10 in `-go64` mode. VICE also ships two variants for that slot,
|
|
137
|
+
`kernal64-325179-01` and `-325182-01`, differing from 901227-03 in 54
|
|
138
|
+
and 24 bytes (table bytes and one small patch; none in the
|
|
139
|
+
`$FF81-$FFF4` jump table). The part number on a real C128's C64-ROM
|
|
140
|
+
chip (usually given as 251913-01, a combined BASIC+KERNAL mask) is from
|
|
141
|
+
documentation, not measured here. Either way the 39-entry jump table is
|
|
142
|
+
at the same addresses with identical semantics — programs that use only
|
|
143
|
+
the jump table run unchanged on a C128 in C64 mode.
|
|
144
|
+
|
|
145
|
+
### Zero page each routine writes
|
|
146
|
+
|
|
147
|
+
Each routine below carries `**Clobbers zero page:**` lines. They matter to
|
|
148
|
+
any program that keeps its own variables in zero page and still calls the
|
|
149
|
+
KERNAL.
|
|
150
|
+
|
|
151
|
+
- `(may; ROM walk from $FFxx, power-on vectors)` is an upper bound:
|
|
152
|
+
every byte `$00-$FF` that code reachable from the jump-table slot can
|
|
153
|
+
store to (STA, STX, STY, INC, DEC and the shifts; an indexed store
|
|
154
|
+
counts the index range read off the ROM at that site). Error paths
|
|
155
|
+
count. So does a tape IRQ handler the routine installs at `$0314`
|
|
156
|
+
while it runs (the four in the table at `$FD9B`).
|
|
157
|
+
Vectors are followed through their power-on values: RESTOR's table at
|
|
158
|
+
`$FD30`, and `$028F` → `$EB48`, which CINT sets. A program that repoints
|
|
159
|
+
a vector, such as `$0326` for CHROUT, changes what the routine runs, and
|
|
160
|
+
the line no longer applies. "Also stores through (`$AC`)" means the
|
|
161
|
+
routine writes where that pointer points, not to `$AC` itself.
|
|
162
|
+
`scripts/kernal-zp-walk.ts` writes these lines from the 901227-03 image,
|
|
163
|
+
and `npm test` fails if the page and the walk disagree.
|
|
164
|
+
- `(must; VICE x64sc store trace, <call>)` is what that one call wrote,
|
|
165
|
+
measured by `scripts/kernal-zp-trace.ts` with every interrupt source
|
|
166
|
+
masked. It is a lower bound for that call only. Every traced byte lies
|
|
167
|
+
inside the may set; the walk's check enforces it.
|
|
168
|
+
|
|
169
|
+
The largest routines (OPEN, CLOSE, CHKIN, CHKOUT, CHRIN, CHROUT, GETIN,
|
|
170
|
+
LOAD, SAVE) may write `$D9-$F2`, the screen editor's line-link table. The
|
|
171
|
+
traced CHROUT, printing enough lines to scroll the screen, wrote all of
|
|
172
|
+
`$D9-$F4` and `$AC-$AF`. A loader or a routine that keeps state in
|
|
173
|
+
`$E0-$EF` and also prints through CHROUT loses it.
|
|
174
|
+
|
|
175
|
+
The IRQ and NMI services are not jump-table routines. With interrupts
|
|
176
|
+
enabled they run between any two instructions of a program, so their
|
|
177
|
+
writes apply everywhere. The NMI walk reaches the RUN/STOP-RESTORE warm
|
|
178
|
+
start, which leaves through `JMP ($A002)` into BASIC (or `JMP ($8002)`
|
|
179
|
+
into a cartridge) and does not return.
|
|
180
|
+
|
|
181
|
+
<!-- kernal-zp-walk: services (generated by scripts/kernal-zp-walk.ts --write) -->
|
|
182
|
+
| Entry | May write |
|
|
183
|
+
|---|---|
|
|
184
|
+
| IRQ `$FF48`, through `$0314` to `$EA31` | $01, $91, $A0-$A2, $C0, $C5-$C6, $CB, $CD-$CF, $F3-$F6 (may; ROM walk from $FF48, power-on vectors; also stores through ($D1), ($F3)) |
|
|
185
|
+
| NMI `$FE43`, through `$0318` to `$FE47` | $00-$01, $90-$91, $94-$95, $99-$9A, $A3, $A5, $A7-$AB, $B4-$B6, $BD, $C3-$C4, $C6, $CC-$CD, $CF, $D1-$D3, $D5-$D6, $D9-$F4 (may; ROM walk from $FE43, power-on vectors; not followed: JMP ($8002), JMP ($A002); also stores through ($C3), ($D1), ($F3), ($F7)) |
|
|
186
|
+
|
|
187
|
+
No jump-table routine but RAMTAS, and neither service, may write $02-$8F, $B2-$B3, $FB-$FF.
|
|
188
|
+
<!-- /kernal-zp-walk -->
|
|
189
|
+
|
|
190
|
+
The walk covers the KERNAL, not BASIC, which uses much of `$02-$8F`.
|
|
191
|
+
|
|
192
|
+
### Sources
|
|
193
|
+
|
|
194
|
+
- *Commodore 64 Programmer's Reference Guide* (1982), Appendix B — KERNAL ROM machine language subroutines
|
|
195
|
+
- *Commodore 64 KERNAL Reference Manual* (1983)
|
|
196
|
+
- C64-Wiki: KERNAL Jump Table — https://www.c64-wiki.com/wiki/KERNAL_Jump_Table
|
|
197
|
+
- sta.c64.org: CBM 64 KERNAL routines — https://sta.c64.org/cbm64krnfunc.html
|
|
198
|
+
- pagetable.com: annotated C64 KERNAL disassembly — https://www.pagetable.com/c64ref/c64disasm/
|
|
199
|
+
- Mapping the Commodore 64 (Sheldon Leemon), KERNAL section — https://www.zimmers.net/anonftp/pub/cbm/c64/manuals/mapping-c64.txt
|
|
200
|
+
|
|
201
|
+
## Quick reference
|
|
202
|
+
|
|
203
|
+
The full 39-entry jump table, in address order. Each entry is a 3-byte
|
|
204
|
+
`JMP abs` instruction.
|
|
205
|
+
|
|
206
|
+
| Addr | Name | One-line summary |
|
|
207
|
+
|---------|---------|--------------------------------------------------------------------|
|
|
208
|
+
| `$FF81` | CINT | Initialize screen editor + VIC-II (cold-screen setup) |
|
|
209
|
+
| `$FF84` | IOINIT | Initialize CIA1/CIA2/SID; set up jiffy IRQ |
|
|
210
|
+
| `$FF87` | RAMTAS | RAM test, set top/bottom of memory, clear `$0002-$00FF` and `$0200-$03FF` (not the stack page) |
|
|
211
|
+
| `$FF8A` | RESTOR | Restore RAM vectors at `$0314-$0333` to KERNAL defaults |
|
|
212
|
+
| `$FF8D` | VECTOR | Read or write all RAM vectors as a block |
|
|
213
|
+
| `$FF90` | SETMSG | Control KERNAL error / control-message verbosity |
|
|
214
|
+
| `$FF93` | SECOND | Send secondary address after LISTEN |
|
|
215
|
+
| `$FF96` | TKSA | Send secondary address after TALK |
|
|
216
|
+
| `$FF99` | MEMTOP | Read or set top of RAM (used by BASIC + KERNAL buffers) |
|
|
217
|
+
| `$FF9C` | MEMBOT | Read or set bottom of RAM (start of BASIC text area) |
|
|
218
|
+
| `$FF9F` | SCNKEY | Scan the keyboard matrix, push key into queue |
|
|
219
|
+
| `$FFA2` | SETTMO | Set IEEE-488 timeout flag (no effect on C64) |
|
|
220
|
+
| `$FFA5` | IECIN | Receive one byte from serial bus (alias ACPTR) |
|
|
221
|
+
| `$FFA8` | IECOUT | Send one byte to serial bus (alias CIOUT) |
|
|
222
|
+
| `$FFAB` | UNTLK | Send UNTALK on serial bus |
|
|
223
|
+
| `$FFAE` | UNLSN | Send UNLISTEN on serial bus |
|
|
224
|
+
| `$FFB1` | LISTEN | Send LISTEN command to a device on the serial bus |
|
|
225
|
+
| `$FFB4` | TALK | Send TALK command to a device on the serial bus |
|
|
226
|
+
| `$FFB7` | READST | Read the KERNAL serial-bus status byte |
|
|
227
|
+
| `$FFBA` | SETLFS | Set logical file number, device, and secondary address |
|
|
228
|
+
| `$FFBD` | SETNAM | Set filename length and pointer |
|
|
229
|
+
| `$FFC0` | OPEN | Open a logical file (uses SETLFS+SETNAM state) |
|
|
230
|
+
| `$FFC3` | CLOSE | Close a logical file |
|
|
231
|
+
| `$FFC6` | CHKIN | Redirect input from a previously opened logical file |
|
|
232
|
+
| `$FFC9` | CHKOUT | Redirect output to a previously opened logical file |
|
|
233
|
+
| `$FFCC` | CLRCHN | Reset default I/O channels (input=keyboard, output=screen) |
|
|
234
|
+
| `$FFCF` | CHRIN | Read one character from current input channel |
|
|
235
|
+
| `$FFD2` | CHROUT | Write one character to current output channel |
|
|
236
|
+
| `$FFD5` | LOAD | Load or verify a file from device into memory |
|
|
237
|
+
| `$FFD8` | SAVE | Save memory range to device |
|
|
238
|
+
| `$FFDB` | SETTIM | Set the 24-bit jiffy clock |
|
|
239
|
+
| `$FFDE` | RDTIM | Read the 24-bit jiffy clock |
|
|
240
|
+
| `$FFE1` | STOP | Test RUN/STOP key, return Z=1 if pressed |
|
|
241
|
+
| `$FFE4` | GETIN | Get one character from keyboard queue (non-blocking) |
|
|
242
|
+
| `$FFE7` | CLALL | Close all open files (calls CLRCHN internally) |
|
|
243
|
+
| `$FFEA` | UDTIM | Update the jiffy clock; called from IRQ handler at `$EA31` |
|
|
244
|
+
| `$FFED` | SCREEN | Return screen dimensions (40 columns, 25 rows on stock C64) |
|
|
245
|
+
| `$FFF0` | PLOT | Read or set cursor row/column |
|
|
246
|
+
| `$FFF3` | IOBASE | Return base address of I/O block (always `$DC00` on C64) |
|
|
247
|
+
|
|
248
|
+
Three categorical points are non-obvious from the names:
|
|
249
|
+
|
|
250
|
+
- **`IECIN`/`IECOUT`** are documented in the original Commodore 64
|
|
251
|
+
*Programmer's Reference Guide* under the older PET-era names `ACPTR`
|
|
252
|
+
(accept byte from serial bus) and `CIOUT` (Commodore IEEE/IEC output).
|
|
253
|
+
Most modern documentation uses the IEC-prefixed names.
|
|
254
|
+
- **`CHRIN`/`CHROUT`** are also documented as `BASIN`/`BSOUT` in some
|
|
255
|
+
Commodore manuals. Same routine, different name.
|
|
256
|
+
- **`SETTMO`** (`$FFA2`) was meaningful on the PET's IEEE-488 bus; on
|
|
257
|
+
the C64's IEC serial bus the timeout flag is hard-wired and the
|
|
258
|
+
routine does nothing visible.
|
|
259
|
+
|
|
260
|
+
## File I/O routines
|
|
261
|
+
|
|
262
|
+
The high-level file I/O surface is a stateful three-step protocol:
|
|
263
|
+
|
|
264
|
+
1. **Configure** the next file operation with SETLFS (logical file, device,
|
|
265
|
+
secondary address) and SETNAM (filename buffer pointer, length).
|
|
266
|
+
2. **Open** the file with OPEN, LOAD, or SAVE.
|
|
267
|
+
3. **Use** it: redirect input with CHKIN, redirect output with CHKOUT, read
|
|
268
|
+
with CHRIN / GETIN, write with CHROUT. Reset to defaults with CLRCHN.
|
|
269
|
+
4. **Close** with CLOSE or CLALL.
|
|
270
|
+
|
|
271
|
+
This is the same protocol the BASIC `OPEN`, `PRINT#`, `INPUT#`, and `CLOSE`
|
|
272
|
+
statements drive. A machine-language program that wants to open and read
|
|
273
|
+
a sequential file from drive 8 does:
|
|
274
|
+
|
|
275
|
+
```asm
|
|
276
|
+
; OPEN 2,8,2,"FILE,S,R"
|
|
277
|
+
lda #2 ; logical file number
|
|
278
|
+
ldx #8 ; device 8 (first disk drive)
|
|
279
|
+
ldy #2 ; secondary address = 2 (channel)
|
|
280
|
+
jsr $FFBA ; SETLFS
|
|
281
|
+
lda #fname_end-fname
|
|
282
|
+
ldx #<fname
|
|
283
|
+
ldy #>fname
|
|
284
|
+
jsr $FFBD ; SETNAM
|
|
285
|
+
jsr $FFC0 ; OPEN
|
|
286
|
+
bcs open_error ; C=1 on failure, A = error code
|
|
287
|
+
|
|
288
|
+
ldx #2 ; logical file from above
|
|
289
|
+
jsr $FFC6 ; CHKIN (redirect input)
|
|
290
|
+
read_loop:
|
|
291
|
+
jsr $FFCF ; CHRIN
|
|
292
|
+
; ... process A ...
|
|
293
|
+
jsr $FFB7 ; READST
|
|
294
|
+
beq read_loop ; ST=0 means more bytes available
|
|
295
|
+
|
|
296
|
+
lda #2
|
|
297
|
+
jsr $FFC3 ; CLOSE
|
|
298
|
+
jsr $FFCC ; CLRCHN
|
|
299
|
+
rts
|
|
300
|
+
|
|
301
|
+
fname: .byte "FILE,S,R"
|
|
302
|
+
fname_end:
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
Listings in this document are generic 6502 assembler syntax — `;`
|
|
306
|
+
comments and `.byte "…"` strings, as ca65 accepts them — not
|
|
307
|
+
KickAssembler fragments, and `npm run check:listings` does not build
|
|
308
|
+
them (it assembles only `asm` fences written in KickAssembler syntax
|
|
309
|
+
with no `;` lines). In KickAssembler use `//` comments and `.text "…"`;
|
|
310
|
+
Kick's default `.text` encoding is screen codes, which for upper-case
|
|
311
|
+
letters, digits and punctuation coincide with the unshifted PETSCII a
|
|
312
|
+
drive expects (`FILE,S,R` -> `46 49 4C 45 2C 53 2C 52`; `petscii_mixed`
|
|
313
|
+
would give `C6 C9 CC C5 …`). Under ca65 `-t c64` the same literal
|
|
314
|
+
assembles to shifted PETSCII `C6 C9 CC C5 …`, so set the filename bytes
|
|
315
|
+
deliberately in whichever assembler you use (both byte sequences
|
|
316
|
+
measured with KickAssembler 5.25 and ca65 2.19).
|
|
317
|
+
|
|
318
|
+
Three rules trip up first-time KERNAL programmers:
|
|
319
|
+
|
|
320
|
+
- **SETLFS + SETNAM state survives across calls** until you overwrite it.
|
|
321
|
+
If you call SETLFS once with secondary=2 and then call OPEN a second
|
|
322
|
+
time without calling SETLFS again, the second OPEN will reuse
|
|
323
|
+
secondary=2.
|
|
324
|
+
- **CHKIN/CHKOUT require a prior successful OPEN.** They return C=1 +
|
|
325
|
+
error code 3 ("file not open") if the logical file number doesn't
|
|
326
|
+
refer to an open file.
|
|
327
|
+
- **CLRCHN must be called before CLOSE.** If you `CLOSE` while CHKIN
|
|
328
|
+
has redirected input to that file, the next CHRIN reads from a
|
|
329
|
+
closed file and returns garbage. Always: `CLRCHN`, then `CLOSE`.
|
|
330
|
+
|
|
331
|
+
### $FFBA — SETLFS — Set logical file parameters
|
|
332
|
+
|
|
333
|
+
**Input:** A = logical file number (1-255), X = device number (0-31), Y = secondary address (0-31 or `$FF`)
|
|
334
|
+
**Output:** None
|
|
335
|
+
**Affects:** None (parameters stored in zero-page workspace `$B8`, `$BA`, `$B9`)
|
|
336
|
+
**Clobbers zero page:** $B8-$BA (may; ROM walk from $FFBA, power-on vectors)
|
|
337
|
+
**Clobbers zero page:** $B8-$BA (must; VICE x64sc store trace, SETLFS 2,8,2)
|
|
338
|
+
**Pairs with:** SETNAM, OPEN, LOAD, SAVE
|
|
339
|
+
**Description:** Stores the three file parameters that the next OPEN, LOAD,
|
|
340
|
+
or SAVE will use. The logical file number (A) is a program-chosen tag in the
|
|
341
|
+
range 1-255; it appears in subsequent CHKIN/CHKOUT/CLOSE calls to identify
|
|
342
|
+
this file. The KERNAL stores the number in `$B8` and never interprets
|
|
343
|
+
it. The familiar rule that file numbers 128-255 get a linefeed after
|
|
344
|
+
every carriage return is BASIC's, not the KERNAL's: PRINT#, CMD and any
|
|
345
|
+
PRINT while CMD is active send `$0A` after `$0D` when bit 7 of the
|
|
346
|
+
current channel byte `$13` is set (BASIC ROM `$AAD7`, `BIT $13`). CHROUT
|
|
347
|
+
adds no byte of its own whatever the file number — measured in VICE
|
|
348
|
+
x64sc: `SETLFS` 200 followed by `CHROUT $0D` delivers `$0D` alone, while
|
|
349
|
+
BASIC's `PRINT#200` hands CHROUT `$0D,$0A` and `PRINT#100` hands `$0D`.
|
|
350
|
+
(An earlier revision placed this rule under SETLFS as if the KERNAL
|
|
351
|
+
applied it.)
|
|
352
|
+
The device number (X) selects the bus device: 0=keyboard, 1=tape (Datasette),
|
|
353
|
+
2=RS-232 (user port), 3=screen, 4-7=printer/plotter, 8-30=IEC disk/printer
|
|
354
|
+
units. Secondary address (Y) is device-specific: for the 1541 disk drive,
|
|
355
|
+
0=load PRG, 1=save PRG, 2-14=open named channel, 15=command channel. `$FF`
|
|
356
|
+
(any value with bit 7 set) means "send no secondary address" on the
|
|
357
|
+
serial bus: OPEN, CHKIN, CHKOUT and CLOSE then skip the
|
|
358
|
+
secondary-address byte, and OPEN sends nothing at all — not even the
|
|
359
|
+
filename — so it suits an unnamed channel such as a printer, not a named
|
|
360
|
+
disk file. It is what BASIC's OPEN supplies for device 3 and above when
|
|
361
|
+
the third parameter is omitted; for tape and RS-232 BASIC defaults to 0.
|
|
362
|
+
Tape reads the secondary address: for OPEN, 0 = read, 1 = write, 2 =
|
|
363
|
+
write followed by an end-of-tape marker at CLOSE (measured in VICE
|
|
364
|
+
x64sc: SA `$FF` on device 1 takes the write path and prompts PRESS
|
|
365
|
+
RECORD & PLAY ON TAPE); for SAVE, bit 0 = absolute-address header, bit 1
|
|
366
|
+
= write an end-of-tape marker after the data; for LOAD, see `$FFD5`
|
|
367
|
+
below. An earlier revision said `$FF` was "used by tape"; it is not.
|
|
368
|
+
|
|
369
|
+
### $FFBD — SETNAM — Set filename
|
|
370
|
+
|
|
371
|
+
**Input:** A = filename length (0-16, longer accepted but truncated by device), X = low byte of filename pointer, Y = high byte of filename pointer
|
|
372
|
+
**Output:** None
|
|
373
|
+
**Affects:** None (length stored at `$B7`, pointer at `$BB`/`$BC`)
|
|
374
|
+
**Clobbers zero page:** $B7, $BB-$BC (may; ROM walk from $FFBD, power-on vectors)
|
|
375
|
+
**Clobbers zero page:** $B7, $BB-$BC (must; VICE x64sc store trace, SETNAM with a 4-byte name)
|
|
376
|
+
**Pairs with:** SETLFS, OPEN, LOAD, SAVE
|
|
377
|
+
**Description:** Records the filename for the next OPEN/LOAD/SAVE. The
|
|
378
|
+
filename is *not* copied; only its address is stored, so the buffer must
|
|
379
|
+
remain valid until OPEN/LOAD/SAVE returns. A=0 means "no filename" and is
|
|
380
|
+
legal for LOAD/SAVE on tape (loads the first PRG it finds) and for OPEN
|
|
381
|
+
on the printer or screen. Filenames sent to a disk drive may carry
|
|
382
|
+
embedded commas separating filename / file-type / mode, e.g.
|
|
383
|
+
`"DATA,S,R"` (sequential, read).
|
|
384
|
+
|
|
385
|
+
### $FFC0 — OPEN — Open a logical file
|
|
386
|
+
|
|
387
|
+
**Input:** SETLFS and SETNAM state must be set first
|
|
388
|
+
**Output:** C=0 on success; C=1 on error, A = error code
|
|
389
|
+
**Affects:** A, X, Y, C
|
|
390
|
+
**Clobbers zero page:** $01, $90-$96, $98-$9C, $9E-$B1, $B4-$B6, $B9, $BD-$C2, $C5-$C7, $C9, $CB, $CD-$FA (may; ROM walk from $FFC0, power-on vectors; also stores through ($AC), ($B2), ($D1), ($F3), ($F9))
|
|
391
|
+
**Clobbers zero page:** $90, $94-$95, $98, $A3, $A5, $B9 (must; VICE x64sc store trace, OPEN of a PRG file on drive 8 (true drive emulation))
|
|
392
|
+
**Pairs with:** SETLFS, SETNAM, CLOSE, CHKIN, CHKOUT
|
|
393
|
+
**Description:** Opens a logical file using the parameters previously
|
|
394
|
+
stored by SETLFS and SETNAM. For disk devices, sends LISTEN +
|
|
395
|
+
secondary-address (`$F0 | sec`) over the IEC bus, then sends the
|
|
396
|
+
filename bytes one at a time via IECOUT, then UNLISTEN. On success the
|
|
397
|
+
KERNAL records the logical file number in its open-file table
|
|
398
|
+
(`$0259-$0262` for LF, `$0263-$026C` for device, `$026D-$0276` for
|
|
399
|
+
secondary). Errors: 1=too many open files (10 already open), 2=logical
|
|
400
|
+
file number already open, 5=device not present, 6=logical file number 0
|
|
401
|
+
(the only 6 OPEN returns; OPEN never returns 7 — 6 and 7 as direction
|
|
402
|
+
errors belong to CHKIN/CHKOUT), 9=illegal device (tape OPEN with the
|
|
403
|
+
tape-buffer pointer `$B2/$B3` below `$0200`). OPEN's own code-4 exit is
|
|
404
|
+
on the tape branch only and is reached only through the STOP key during
|
|
405
|
+
the header search, and then only when `$93` (the load/verify flag left
|
|
406
|
+
by the last LOAD or VERIFY) is non-zero; with `$93` = 0 the same STOP
|
|
407
|
+
returns C=1, A=0. A name that is not on the tape does not produce 4: the
|
|
408
|
+
search reads on until an end-of-tape marker, which OPEN returns as C=1,
|
|
409
|
+
A=5 (BASIC prints ?DEVICE NOT PRESENT). Disk OPEN never reports a
|
|
410
|
+
missing file — read the error channel. Errors 1, 2 and 6 are detected
|
|
411
|
+
before the table entry is stored; for 4, 5 and 9 the entry has already
|
|
412
|
+
been added (`$98` incremented, LFN/device/secondary stored at
|
|
413
|
+
`$0259/$0263/$026D,X`) and stays there, so a retry with the same logical
|
|
414
|
+
file number returns 2 — CLOSE it (or CLALL) first. (An earlier version
|
|
415
|
+
of this entry listed 6 and 7 as direction errors, called 4 LOAD-only and
|
|
416
|
+
said a failed OPEN left no table entry; all three were checked against
|
|
417
|
+
the KERNAL ROM bytes and in VICE x64sc.) OPEN on tape (device 1) also
|
|
418
|
+
returns C=1 with A=0 if RUN/STOP is pressed while it waits for
|
|
419
|
+
PLAY/RECORD or during the header search or write (`$F399`/`$F3B8` ->
|
|
420
|
+
`$F3D4`); the serial OPEN path has no STOP check (ROM bytes).
|
|
421
|
+
|
|
422
|
+
### $FFC3 — CLOSE — Close a logical file
|
|
423
|
+
|
|
424
|
+
**Input:** A = logical file number
|
|
425
|
+
**Output:** C=0 on success; C=1 on error, A = error code
|
|
426
|
+
**Affects:** A, X, Y, C
|
|
427
|
+
**Clobbers zero page:** $01, $90-$96, $98-$9C, $9E-$B1, $B4-$B6, $B8-$BA, $BD-$C2, $C5-$C7, $C9, $CB, $CD-$F6, $F8, $FA (may; ROM walk from $FFC3, power-on vectors; also stores through ($AC), ($B2), ($D1), ($F3), ($F9))
|
|
428
|
+
**Clobbers zero page:** $95, $98, $A5, $B8-$BA (must; VICE x64sc store trace, CLOSE of that file)
|
|
429
|
+
**Pairs with:** OPEN, CLRCHN
|
|
430
|
+
**Description:** Closes the named logical file. For IEC devices, sends
|
|
431
|
+
LISTEN + close-secondary (`$E0 | sec`), then UNLISTEN. The KERNAL
|
|
432
|
+
removes the entry from its open-file table. Calling CLOSE on a
|
|
433
|
+
logical file number that isn't open returns silently (no error). If
|
|
434
|
+
the file is currently the active input or output channel, CLOSE does
|
|
435
|
+
*not* reset the channel — call CLRCHN first.
|
|
436
|
+
|
|
437
|
+
### $FFC6 — CHKIN — Redirect input to logical file
|
|
438
|
+
|
|
439
|
+
**Input:** X = logical file number (must already be OPEN)
|
|
440
|
+
**Output:** C=0 on success; C=1 on error, A = error code
|
|
441
|
+
**Affects:** A, X, C
|
|
442
|
+
**Clobbers zero page:** $01, $90-$96, $99-$9C, $9E-$B1, $B4-$B6, $B8-$BA, $BD-$C2, $C5-$C7, $C9, $CB, $CD-$F6 (may; ROM walk from $FFC6, power-on vectors; also stores through ($AC), ($B2), ($D1), ($F3), ($F9))
|
|
443
|
+
**Clobbers zero page:** $90, $95, $99, $A5, $B8-$BA (must; VICE x64sc store trace, CHKIN on that file)
|
|
444
|
+
**Pairs with:** OPEN, CHRIN, GETIN, CLRCHN
|
|
445
|
+
**Description:** Tells the KERNAL that subsequent CHRIN / GETIN calls
|
|
446
|
+
should read from the named logical file rather than the keyboard. For
|
|
447
|
+
IEC devices, sends TALK + secondary (`$60 | sec`). Errors: 3=file not
|
|
448
|
+
open; 6=not input file, raised only for a tape file whose secondary
|
|
449
|
+
address is not 0 (opened for write; `$F22A-$F230` compares the stored SA
|
|
450
|
+
with `$60`). CHKIN does not check direction on a serial device: on a
|
|
451
|
+
disk channel opened `,S,R` it returns C=0 (measured in VICE x64sc).
|
|
452
|
+
There is a device-not-present exit (5) at `$F24D` but it is not
|
|
453
|
+
reachable for a missing serial device: TKSA falls through into the bus
|
|
454
|
+
turnaround at `$EDCC`, whose wait for the talker's clock at `$EDD6` has
|
|
455
|
+
no timeout, so CHKIN on a serial device that does not answer hangs the
|
|
456
|
+
machine rather than returning (measured in VICE x64sc: an
|
|
457
|
+
open-file-table entry for device 9 hung CHKIN both on an empty bus and
|
|
458
|
+
with a 1541 on device 8). An earlier version of this entry said 6 meant
|
|
459
|
+
the device was opened for write; that is the tape rule only.
|
|
460
|
+
|
|
461
|
+
### $FFC9 — CHKOUT — Redirect output to logical file
|
|
462
|
+
|
|
463
|
+
**Input:** X = logical file number (must already be OPEN)
|
|
464
|
+
**Output:** C=0 on success; C=1 on error, A = error code
|
|
465
|
+
**Affects:** A, X, C
|
|
466
|
+
**Clobbers zero page:** $01, $90-$96, $99-$9C, $9E-$B1, $B4-$B6, $B8-$BA, $BD-$C2, $C5-$C7, $C9, $CB, $CD-$F6 (may; ROM walk from $FFC9, power-on vectors; also stores through ($AC), ($B2), ($D1), ($F3), ($F9))
|
|
467
|
+
**Pairs with:** OPEN, CHROUT, CLRCHN
|
|
468
|
+
**Description:** Tells the KERNAL that subsequent CHROUT calls should
|
|
469
|
+
write to the named logical file rather than the screen. For IEC
|
|
470
|
+
devices, sends LISTEN + secondary (`$60 | sec`). Errors: 3=file not
|
|
471
|
+
open; 5=device not present (no serial device pulled DATA low in answer
|
|
472
|
+
to ATN during LISTEN/SECOND, `$ED40-$ED47`, ST bit 7; measured C=1, A=5
|
|
473
|
+
in VICE x64sc both on an empty bus and with another drive present);
|
|
474
|
+
7=not output file, raised for the keyboard (device 0) and for a tape
|
|
475
|
+
file opened with secondary address 0 (read). Disk channels are not
|
|
476
|
+
direction-checked: CHKOUT on a channel opened `,S,R` returns C=0 (A=8),
|
|
477
|
+
the bytes you CHROUT afterwards are accepted, and with the 1541-II DOS
|
|
478
|
+
in VICE the drive's error channel still read 00 afterwards, so nothing
|
|
479
|
+
reports the mistake. An earlier version said 7 meant the device was
|
|
480
|
+
opened for read; that is the tape rule only.
|
|
481
|
+
|
|
482
|
+
### $FFCC — CLRCHN — Reset default I/O channels
|
|
483
|
+
|
|
484
|
+
**Input:** None
|
|
485
|
+
**Output:** None
|
|
486
|
+
**Affects:** A, X
|
|
487
|
+
**Clobbers zero page:** $90, $94-$95, $99-$9A, $A3, $A5 (may; ROM walk from $FFCC, power-on vectors)
|
|
488
|
+
**Clobbers zero page:** $95, $99-$9A, $A5 (must; VICE x64sc store trace, CLRCHN with that file as input)
|
|
489
|
+
**Pairs with:** CHKIN, CHKOUT, CLOSE
|
|
490
|
+
**Description:** Resets the current input channel to the keyboard
|
|
491
|
+
(device 0) and the current output channel to the screen (device 3).
|
|
492
|
+
If a CHKOUT had redirected output to an IEC device, CLRCHN sends
|
|
493
|
+
UNLISTEN; if a CHKIN had redirected input to an IEC device, CLRCHN
|
|
494
|
+
sends UNTALK. It does *not* close any logical file; the files remain
|
|
495
|
+
open and can be re-selected with another CHKIN/CHKOUT. Always call
|
|
496
|
+
CLRCHN between operations on different files.
|
|
497
|
+
|
|
498
|
+
### $FFE7 — CLALL — Close all logical files
|
|
499
|
+
|
|
500
|
+
**Input:** None
|
|
501
|
+
**Output:** None
|
|
502
|
+
**Affects:** A, X
|
|
503
|
+
**Clobbers zero page:** $90, $94-$95, $98-$9A, $A3, $A5 (may; ROM walk from $FFE7, power-on vectors)
|
|
504
|
+
**Pairs with:** CLRCHN
|
|
505
|
+
**Description:** Closes all open logical files at once by zeroing the
|
|
506
|
+
KERNAL's open-file count (`$0098`). This does *not* send proper CLOSE
|
|
507
|
+
commands to IEC devices — it just discards the KERNAL's open-file
|
|
508
|
+
table. CLALL also calls CLRCHN, so default channels are reset. Use
|
|
509
|
+
CLALL when you need to reset I/O state from an unknown starting
|
|
510
|
+
point (e.g. in an error handler), and accept that any disk-side state
|
|
511
|
+
(open relative-file channels with dirty buffers) will be left
|
|
512
|
+
dangling until you UNLISTEN. The jump-table entry for CLALL is at
|
|
513
|
+
`$FFE7`, despite its conceptual pair with CLOSE.
|
|
514
|
+
|
|
515
|
+
### $FFD5 — LOAD — Load or verify a file
|
|
516
|
+
|
|
517
|
+
**Input:** A = 0 to load, 1 to verify; X = low byte of start address (used only if SETLFS Y=0); Y = high byte of start address; SETLFS + SETNAM state required
|
|
518
|
+
**Output:** C=0 on success, X+Y = end-address+1 (one past the last byte loaded); C=1 on error, A = error code
|
|
519
|
+
**Affects:** A, X, Y, C
|
|
520
|
+
**Clobbers zero page:** $01, $90-$96, $99-$9C, $9E-$B1, $B4-$B6, $B9, $BD-$C7, $C9, $CB, $CD-$F6 (may; ROM walk from $FFD5, power-on vectors; also stores through ($AC), ($AE), ($B2), ($D1), ($F3), ($F9))
|
|
521
|
+
**Clobbers zero page:** $90, $93-$95, $A3-$A5, $AE-$AF, $B9, $C3-$C4 (must; VICE x64sc store trace, LOAD of a 3-block PRG from drive 8 to its own address)
|
|
522
|
+
**Pairs with:** SETLFS, SETNAM
|
|
523
|
+
**Description:** Loads a file from the device specified by SETLFS into
|
|
524
|
+
RAM. If the secondary address from SETLFS is 0, the file is loaded at
|
|
525
|
+
the address in X/Y (passed in by the caller). If secondary is 1
|
|
526
|
+
(non-zero), the file's first two bytes are used as the load address
|
|
527
|
+
(this is how `LOAD "FILE",8,1` works in BASIC). On the C64 the load
|
|
528
|
+
goes into RAM even where BASIC or KERNAL ROM is mapped: the serial path
|
|
529
|
+
stores each byte with `STA ($AE),Y` through the zero-page pointer
|
|
530
|
+
`$AE/$AF` (EAL/EAH, the store at `$F51C`; the tape path stores through
|
|
531
|
+
`$AC/$AD` at `$FB41`), and a 6510 write to a ROM-mapped address always
|
|
532
|
+
lands in the RAM beneath. An earlier version of this page said the
|
|
533
|
+
pointer was in page 1 — that is the stack. The rule does *not* extend
|
|
534
|
+
to `$D000-$DFFF`: LOAD never touches `$01`, so it runs with I/O mapped
|
|
535
|
+
in, and a file whose load address falls there is written into the
|
|
536
|
+
VIC/SID/CIA registers or colour RAM while the RAM underneath is left
|
|
537
|
+
untouched (measured in VICE x64sc: a `,8,1` load of two bytes to
|
|
538
|
+
`$D020` set the border and background registers and left the RAM
|
|
539
|
+
beneath at its prefill). You cannot bank I/O out around the call — the
|
|
540
|
+
serial routines drive the IEC bus through CIA2 at `$DD00` and the tape
|
|
541
|
+
routines time pulses through CIA1 — so LOAD such data elsewhere and
|
|
542
|
+
copy it under I/O yourself with interrupts disabled. Loading straight
|
|
543
|
+
into colour RAM at `$D800` is the one case where writing the chips is
|
|
544
|
+
what you want. After a successful load, X/Y hold the address
|
|
545
|
+
immediately *past* the last byte loaded. With A=1, LOAD compares the
|
|
546
|
+
file against memory instead of writing — sets the status byte's "verify
|
|
547
|
+
mismatch" bit on differences. Errors: 4=file not found, 5=device not
|
|
548
|
+
present, 8=missing filename, 9=illegal device. A C=1 return with A=0 is
|
|
549
|
+
none of these: RUN/STOP aborted the transfer. The serial loop calls
|
|
550
|
+
STOP before every byte (`$F4F9`); the tape path polls it while waiting
|
|
551
|
+
for PLAY and throughout the IRQ-driven block transfer, so pressing STOP
|
|
552
|
+
at the PRESS PLAY prompt returns the same way. The KERNAL has already
|
|
553
|
+
closed the serial channel or stopped the tape motor. BASIC reports this
|
|
554
|
+
return as ?BREAK ERROR; Commodore's own KERNAL error table numbers it
|
|
555
|
+
0, "routine terminated by the STOP key" (Programmer's Reference Guide,
|
|
556
|
+
not verified here). Test A=0 before indexing an error-message table.
|
|
557
|
+
(Measured in VICE x64sc 3.10: LOAD from device 8 with STOP forced true
|
|
558
|
+
on its 10th poll returned A=`$00`, C=1 after nine bytes; ROM bytes
|
|
559
|
+
`$F4F9`/`$F633`.)
|
|
560
|
+
|
|
561
|
+
### $FFD8 — SAVE — Save memory to file
|
|
562
|
+
|
|
563
|
+
**Input:** A = zero-page pointer to start address (e.g. A=`$2B` means start = ($2B/$2C) ); X = low byte of end-address+1; Y = high byte of end-address+1; SETLFS + SETNAM state required
|
|
564
|
+
**Output:** C=0 on success; C=1 on error, A = error code
|
|
565
|
+
**Affects:** A, X, Y, C
|
|
566
|
+
**Clobbers zero page:** $01, $90-$96, $99-$9C, $9E-$B1, $B4-$B6, $B9, $BD-$C2, $C5-$C7, $C9, $CB, $CD-$F6 (may; ROM walk from $FFD8, power-on vectors; also stores through ($AC), ($B2), ($D1), ($F3), ($F9))
|
|
567
|
+
**Pairs with:** SETLFS, SETNAM
|
|
568
|
+
**Description:** Saves the memory range [start, end+1) to the device.
|
|
569
|
+
The start address is read indirectly through the zero-page byte
|
|
570
|
+
pointed to by A (the BASIC start-of-program pointer at `$2B/$2C`
|
|
571
|
+
is the conventional value, which is why BASIC `SAVE` saves the
|
|
572
|
+
current program). The end-address+1 is passed directly in X/Y.
|
|
573
|
+
On the serial bus the first two bytes sent are the start address (low,
|
|
574
|
+
high), then the data — the format LOAD expects. On tape the addresses
|
|
575
|
+
are not in the data stream at all: the KERNAL writes a separate
|
|
576
|
+
192-byte header block first (`$F76A`: type byte — 1 relocatable, 3
|
|
577
|
+
non-relocatable when the secondary address has bit 0 set — then start
|
|
578
|
+
address, end-address+1, and the filename, space-padded), and the data
|
|
579
|
+
block that follows is the raw memory bytes with no address prefix. An
|
|
580
|
+
earlier version of this page said both tape and serial began with the
|
|
581
|
+
load address; only serial does (KERNAL 901227-03, `$F617-$F621` vs
|
|
582
|
+
`$F76A`/`$F867`). Errors: 5=device not present, 8=missing filename,
|
|
583
|
+
9=illegal device. As with LOAD, C=1 with A=0 means RUN/STOP aborted the
|
|
584
|
+
transfer — polled before every byte on the serial bus (`$F62E`), and
|
|
585
|
+
while waiting for RECORD/PLAY and during the block write on tape — not
|
|
586
|
+
an I/O error; BASIC reports it as ?BREAK ERROR (ROM bytes
|
|
587
|
+
`$F62E`/`$F633`, `$F8D0`).
|
|
588
|
+
|
|
589
|
+
## Character I/O
|
|
590
|
+
|
|
591
|
+
The C64's character-I/O layer has three entry points: CHROUT for writing,
|
|
592
|
+
CHRIN for blocking reads, and GETIN for non-blocking reads. All three
|
|
593
|
+
indirect through RAM vectors at `$0326` (BSOUT), `$0324` (CHRIN), and
|
|
594
|
+
`$032A` (GETIN) — patching those vectors hooks every character that
|
|
595
|
+
flows through the KERNAL.
|
|
596
|
+
|
|
597
|
+
### $FFD2 — CHROUT — Output a character
|
|
598
|
+
|
|
599
|
+
**Input:** A = PETSCII byte to print
|
|
600
|
+
**Output:** None
|
|
601
|
+
**Affects:** A (preserved on success), C
|
|
602
|
+
**Clobbers zero page:** $01, $90-$96, $99-$9C, $9E-$B1, $B4-$B6, $BD-$C2, $C5-$C7, $C9, $CB, $CD-$F6 (may; ROM walk from $FFD2, power-on vectors; also stores through ($AC), ($B2), ($D1), ($F3), ($F9))
|
|
603
|
+
**Clobbers zero page:** $AC-$AF, $C7, $C9, $CD, $D0-$F4 (must; VICE x64sc store trace, CHROUT printing 33 lines, 30 of them 50 characters, so the screen scrolls)
|
|
604
|
+
**Pairs with:** CHKOUT, CLRCHN
|
|
605
|
+
**Description:** Writes one byte to the current output channel (screen
|
|
606
|
+
by default; a logical file if CHKOUT was called). The byte is
|
|
607
|
+
interpreted as PETSCII: printable codes (`$20-$5F`, `$60-$7F`,
|
|
608
|
+
`$A0-$FF`) display the corresponding character; control codes do
|
|
609
|
+
their named action — `$0D` = carriage return, `$11` = cursor down,
|
|
610
|
+
`$13` = home, `$14` = delete, `$93` = clear screen, `$05/$1C/$1E/$1F/$81/$90/$95/$9F` etc.
|
|
611
|
+
= color changes, `$0E` = lower/upper case, `$8E` = upper/graphics
|
|
612
|
+
case. When writing to the screen, CHROUT *does* modify VIC-II state:
|
|
613
|
+
PETSCII `$0E` and `$8E` write to `$D018` to switch the character
|
|
614
|
+
ROM source between charset 1 and charset 2; color changes write to
|
|
615
|
+
the current-color byte at `$0286` (page 2, not zero page — earlier
|
|
616
|
+
text called it zero-page; the KERNAL stores it with an absolute
|
|
617
|
+
`STX $0286` at `$E8D6`, measured in VICE x64sc: CHROUT `$1C` leaves
|
|
618
|
+
`$0286` = 2). To suppress these side
|
|
619
|
+
effects, write directly to screen RAM (`$0400`) and color RAM
|
|
620
|
+
(`$D800`) instead. A and the carry flag are conventionally preserved
|
|
621
|
+
on success.
|
|
622
|
+
|
|
623
|
+
### $FFCF — CHRIN — Read a character (blocking)
|
|
624
|
+
|
|
625
|
+
**Input:** None (current input channel must be set; default is keyboard)
|
|
626
|
+
**Output:** A = PETSCII byte read; C=0 on success, C=1 on error
|
|
627
|
+
**Affects:** A, X, Y, C
|
|
628
|
+
**Clobbers zero page:** $01, $90-$97, $99-$9C, $9E-$B1, $B4-$B6, $BD-$C2, $C5-$F6 (may; ROM walk from $FFCF, power-on vectors; also stores through ($AC), ($B2), ($D1), ($F3), ($F9))
|
|
629
|
+
**Clobbers zero page:** $A4-$A5 (must; VICE x64sc store trace, 20 CHRIN calls reading that file)
|
|
630
|
+
**Clobbers zero page:** $C6, $C8-$CA, $CC-$CD, $D0, $D3-$D4, $D7, $F3-$F4 (must; VICE x64sc store trace, CHRIN from the keyboard reading a 2-character line)
|
|
631
|
+
**Pairs with:** CHKIN, CLRCHN, READST
|
|
632
|
+
**Description:** Reads one PETSCII byte from the current input
|
|
633
|
+
channel. With the default keyboard channel, CHRIN runs the screen
|
|
634
|
+
editor: it blinks the cursor, waits for the user to type a line and
|
|
635
|
+
press RETURN, then returns characters one at a time until the
|
|
636
|
+
RETURN (`$0D`) is delivered. This is the BASIC `INPUT` mechanism.
|
|
637
|
+
With an IEC device active (after CHKIN), CHRIN reads one byte from
|
|
638
|
+
the bus via IECIN, sets the status byte on EOI or error, and returns.
|
|
639
|
+
With tape, CHRIN reads one byte from the tape buffer. Use READST
|
|
640
|
+
after each CHRIN to check end-of-file: status byte `$40` indicates
|
|
641
|
+
EOI (last byte of file just delivered).
|
|
642
|
+
|
|
643
|
+
### $FFE4 — GETIN — Get a character (non-blocking)
|
|
644
|
+
|
|
645
|
+
A line-entry loop on top of GETIN (echo, DEL, RETURN, length cap, blinking
|
|
646
|
+
cursor) is `text_input_line` in `../techniques/text.md`.
|
|
647
|
+
|
|
648
|
+
**Input:** None
|
|
649
|
+
**Output:** A = PETSCII byte (0 if no byte available); C=0 on success
|
|
650
|
+
**Affects:** A, X, Y, C
|
|
651
|
+
**Clobbers zero page:** $01, $90-$97, $99-$9C, $9E-$B1, $B4-$B6, $BD-$C2, $C5-$C9, $CB-$F6 (may; ROM walk from $FFE4, power-on vectors; also stores through ($AC), ($B2), ($D1), ($F3), ($F9))
|
|
652
|
+
**Clobbers zero page:** $C6 (must; VICE x64sc store trace, 5 GETIN calls on a 4-key buffer)
|
|
653
|
+
**Pairs with:** CHKIN, SCNKEY, STOP
|
|
654
|
+
**Description:** Returns immediately. With the default keyboard
|
|
655
|
+
channel, GETIN reads one byte from the keyboard queue (`$0277-$0280`)
|
|
656
|
+
maintained by the IRQ handler; if the queue is empty, A=0. With an
|
|
657
|
+
IEC device active (after CHKIN), GETIN behaves like CHRIN — it
|
|
658
|
+
*does* block on the bus, because the IEC protocol has no peek-ahead.
|
|
659
|
+
The non-blocking property only applies to the keyboard. GETIN is
|
|
660
|
+
the standard primitive for game loops and any code that must remain
|
|
661
|
+
responsive while polling input.
|
|
662
|
+
|
|
663
|
+
## Screen and cursor
|
|
664
|
+
|
|
665
|
+
### $FFED — SCREEN — Read screen dimensions
|
|
666
|
+
|
|
667
|
+
**Input:** None
|
|
668
|
+
**Output:** X = number of columns, Y = number of rows
|
|
669
|
+
**Affects:** X, Y
|
|
670
|
+
**Clobbers zero page:** none (may; ROM walk from $FFED, power-on vectors)
|
|
671
|
+
**Pairs with:** PLOT
|
|
672
|
+
**Description:** Returns the physical dimensions of the screen. On a
|
|
673
|
+
stock C64 this is always X=40, Y=25. The routine exists so that
|
|
674
|
+
programs targeting the whole Commodore-8-bit family (which includes
|
|
675
|
+
80-column machines like the C128) can adapt to the screen they're
|
|
676
|
+
running on without hard-coding 40x25.
|
|
677
|
+
|
|
678
|
+
### $FFF0 — PLOT — Get or set cursor position
|
|
679
|
+
|
|
680
|
+
**Input:** C=0 to set: X = row (0-24), Y = column (0-39); C=1 to read: input ignored
|
|
681
|
+
**Output:** With C=1 on entry: X = row, Y = column
|
|
682
|
+
**Affects:** A, X, Y
|
|
683
|
+
**Clobbers zero page:** $D1-$D3, $D5-$D6, $F3-$F4 (may; ROM walk from $FFF0, power-on vectors)
|
|
684
|
+
**Clobbers zero page:** $D1-$D3, $D5-$D6, $F3-$F4 (must; VICE x64sc store trace, PLOT set, then read)
|
|
685
|
+
**Pairs with:** CHROUT, SCREEN
|
|
686
|
+
**Description:** Reads or writes the cursor position used by the
|
|
687
|
+
screen editor. With C=1 (read mode), returns the current row in X
|
|
688
|
+
and current column in Y. With C=0 (write mode), moves the cursor
|
|
689
|
+
to the supplied row/column. PLOT does not draw anything; it just
|
|
690
|
+
positions the cursor so the next CHROUT writes there. Note the
|
|
691
|
+
unusual axis order: X holds the *row* (0-24) and Y holds the
|
|
692
|
+
*column* (0-39), which is the opposite of typical (x,y) plotting
|
|
693
|
+
conventions.
|
|
694
|
+
|
|
695
|
+
### $FF81 — CINT — Initialize screen editor
|
|
696
|
+
|
|
697
|
+
**Input:** None
|
|
698
|
+
**Output:** None
|
|
699
|
+
**Affects:** A, X, Y
|
|
700
|
+
**Clobbers zero page:** $99-$9A, $CC-$CD, $CF, $D1-$D3, $D5-$D6, $D9-$F4 (may; ROM walk from $FF81, power-on vectors; also stores through ($D1), ($F3))
|
|
701
|
+
**Pairs with:** IOINIT, RAMTAS
|
|
702
|
+
**Description:** Performs the screen-editor portion of the cold-start
|
|
703
|
+
sequence: programs the VIC-II registers for 25-row x 40-column text
|
|
704
|
+
mode, sets screen RAM to `$0400-$07E7` and color RAM to `$D800-$DBE7`,
|
|
705
|
+
fills screen with `$20` (space) and color RAM with the current
|
|
706
|
+
foreground color, sets the cursor to row 0 column 0, sets the
|
|
707
|
+
keyboard-decode vector `$028F/$0290` to `$EB48`, the keyboard-buffer
|
|
708
|
+
size `$0289` = 10, the key-repeat delay `$028C` = 10 and speed `$028B`
|
|
709
|
+
= 4, and the default character colour `$0286` = 14 (light blue), and
|
|
710
|
+
initializes the IRQ-driven keyboard queue. (An earlier version of this
|
|
711
|
+
page said CINT also set the keyboard-table pointer `$F5/$F6`; it does
|
|
712
|
+
not — read from the 901227-03 ROM and confirmed in VICE, `$E518-$E598`
|
|
713
|
+
never stores to `$F5/$F6`. That pointer is written by SCNKEY only on a
|
|
714
|
+
scan that finds a key held: first to `$EB81` at `$EA9D/$EAA1`, then
|
|
715
|
+
re-selected by shift state through the `$028F` vector at `$EB48`; an
|
|
716
|
+
idle scan leaves it alone.) CINT is
|
|
717
|
+
called once at power-on after RAMTAS and IOINIT. Applications can
|
|
718
|
+
re-invoke it to recover from screen corruption (e.g. after a wild
|
|
719
|
+
write to `$D000` zeroed half the VIC registers), but doing so
|
|
720
|
+
overwrites screen and color RAM.
|
|
721
|
+
|
|
722
|
+
## Stop key
|
|
723
|
+
|
|
724
|
+
### $FFE1 — STOP — Test the RUN/STOP key
|
|
725
|
+
|
|
726
|
+
**Input:** None
|
|
727
|
+
**Output:** Z=1 if `$91` = `$7F` (the last UDTIM sample of the STOP-key column showed RUN/STOP down with no shift key), Z=0 otherwise. A is always overwritten: when STOP is not detected A = `$91`, the raw column-7 row byte (`$FF` with nothing in that column held; the *Programmer's Reference Guide* documents using it to test the other keys in that column); when STOP is detected A = 0, because CLRCHN's final `LDA #0` is what is left in A. An earlier version of this page said A = `$7F` when pressed and unchanged otherwise, and that STOP latched "since the last call" — measured in VICE x64sc against the ROM bytes, none of that holds: `$FFE1` -> `$F6ED` = `LDA $91 / CMP #$7F / BNE / PHP / JSR CLRCHN / STA $C6 / PLP / RTS`, and the only writer of `$91` in the ROMs is UDTIM at `$F6DA`, so nothing is consumed by reading it.
|
|
728
|
+
**Affects:** A, N, Z, C (the flags are those of `CMP #$7F` against `$91`). When STOP is detected it also calls CLRCHN (`$FFCC`): input device `$99` is reset to 0 and output device `$9A` to 3, with UNTALK/UNLISTEN sent first only if the current device number was above 3, and the keyboard queue is emptied (`$C6` = 0).
|
|
729
|
+
**Clobbers zero page:** $90, $94-$95, $99-$9A, $A3, $A5, $C6 (may; ROM walk from $FFE1, power-on vectors)
|
|
730
|
+
**Clobbers zero page:** none (must; VICE x64sc store trace, STOP with no key down)
|
|
731
|
+
**Pairs with:** GETIN, UDTIM
|
|
732
|
+
**Description:** Reads the STOP-key flag (zero page `$91`, set by UDTIM
|
|
733
|
+
(`$FFEA`), not SCNKEY, to `$7F` when STOP is held in its matrix column)
|
|
734
|
+
and returns Z=1 if STOP is currently pressed. The
|
|
735
|
+
canonical interruptible-loop pattern is:
|
|
736
|
+
|
|
737
|
+
```asm
|
|
738
|
+
loop: jsr $FFE1 ; STOP
|
|
739
|
+
beq abort ; Z=1 means STOP pressed
|
|
740
|
+
; ... work ...
|
|
741
|
+
jmp loop
|
|
742
|
+
abort: ; restore state, exit
|
|
743
|
+
rts
|
|
744
|
+
```
|
|
745
|
+
|
|
746
|
+
STOP reads `$91`, not the keyboard matrix directly, so it depends on
|
|
747
|
+
the IRQ handler running. If the user has disabled IRQs (`SEI` without
|
|
748
|
+
re-enabling), STOP will never return Z=1. To make STOP work in an
|
|
749
|
+
IRQ-disabled context, `JSR $FFEA` (UDTIM) inside the loop (this also
|
|
750
|
+
advances the jiffy clock, so call it at most once per frame if `TI$`
|
|
751
|
+
matters). An earlier version of this page said to call SCNKEY (`$FF9F`)
|
|
752
|
+
here; SCNKEY never writes `$91` — the only store to `$91` in the KERNAL
|
|
753
|
+
is UDTIM's at `$F6DA` — so that advice could not have worked. UDTIM
|
|
754
|
+
reads the STOP column through `$DC01` without selecting it, relying on
|
|
755
|
+
`$DC00` still holding `$7F` as SCNKEY and the KERNAL IRQ leave it. STOP
|
|
756
|
+
also serves a secondary purpose in some KERNAL routines: when called
|
|
757
|
+
from inside disk I/O, it aborts the operation, and when called from
|
|
758
|
+
the cassette routines, it aborts the tape transfer.
|
|
759
|
+
|
|
760
|
+
## Time and jiffy clock
|
|
761
|
+
|
|
762
|
+
The C64 maintains a 24-bit "jiffy clock" — a counter of `1/60`-second
|
|
763
|
+
ticks (1/50 in PAL territory, despite the name) that wraps every
|
|
764
|
+
24 hours. The counter lives at `$A0/$A1/$A2` (high/mid/low byte) and
|
|
765
|
+
is incremented by UDTIM, which is called from the IRQ handler every
|
|
766
|
+
jiffy. BASIC exposes the counter via the `TI` (numeric) and `TI$`
|
|
767
|
+
(string `HHMMSS`) reserved variables.
|
|
768
|
+
|
|
769
|
+
### $FFDB — SETTIM — Set the jiffy clock
|
|
770
|
+
|
|
771
|
+
**Input:** A = jiffy-clock high byte (will go to `$A0`), X = mid byte (`$A1`), Y = low byte (`$A2`)
|
|
772
|
+
**Output:** None
|
|
773
|
+
**Affects:** None (writes `$A0`, `$A1`, `$A2`)
|
|
774
|
+
**Clobbers zero page:** $A0-$A2 (may; ROM walk from $FFDB, power-on vectors)
|
|
775
|
+
**Clobbers zero page:** $A0-$A2 (must; VICE x64sc store trace, SETTIM)
|
|
776
|
+
**Pairs with:** RDTIM, UDTIM
|
|
777
|
+
**Description:** Stores the supplied 24-bit value into the jiffy-clock
|
|
778
|
+
counter. Note the high-byte-first ordering, which is opposite the
|
|
779
|
+
6502's natural little-endian. This is the routine that BASIC's
|
|
780
|
+
`TI$ = "000000"` translates to. SETTIM disables IRQs while writing
|
|
781
|
+
the three bytes, so the IRQ handler can't see a half-updated value.
|
|
782
|
+
|
|
783
|
+
### $FFDE — RDTIM — Read the jiffy clock
|
|
784
|
+
|
|
785
|
+
**Input:** None
|
|
786
|
+
**Output:** A = high byte, X = mid byte, Y = low byte
|
|
787
|
+
**Affects:** A, X, Y
|
|
788
|
+
**Clobbers zero page:** $A0-$A2 (may; ROM walk from $FFDE, power-on vectors)
|
|
789
|
+
**Clobbers zero page:** $A0-$A2 (must; VICE x64sc store trace, RDTIM)
|
|
790
|
+
**Pairs with:** SETTIM, UDTIM
|
|
791
|
+
**Description:** Reads the three-byte jiffy counter and returns it in
|
|
792
|
+
A/X/Y (high/mid/low). Disables IRQs during the read so the value is
|
|
793
|
+
atomic. For a millisecond-ish elapsed-time stopwatch, call RDTIM
|
|
794
|
+
twice and subtract; one jiffy = 1/60 s NTSC or 1/50 s PAL.
|
|
795
|
+
|
|
796
|
+
### $FFEA — UDTIM — Increment jiffy clock + check STOP
|
|
797
|
+
|
|
798
|
+
**Input:** None
|
|
799
|
+
**Output:** None
|
|
800
|
+
**Affects:** A, X
|
|
801
|
+
**Clobbers zero page:** $91, $A0-$A2 (may; ROM walk from $FFEA, power-on vectors)
|
|
802
|
+
**Clobbers zero page:** $91, $A2 (must; VICE x64sc store trace, UDTIM once)
|
|
803
|
+
**Pairs with:** RDTIM, SETTIM, STOP
|
|
804
|
+
**Description:** Increments the 24-bit jiffy-clock counter at
|
|
805
|
+
`$A0/$A1/$A2` by one. Wraps to zero after `$4F1A00` (24 hours of
|
|
806
|
+
1/60 s ticks) or `$4A6800` (24 hours of 1/50 s ticks; KERNAL uses
|
|
807
|
+
the NTSC constant unless explicitly told otherwise, so PAL drifts
|
|
808
|
+
slightly). UDTIM also reads the keyboard-matrix row that contains
|
|
809
|
+
the STOP key (column at port `$DC00`, row at port `$DC01`) and
|
|
810
|
+
sets `$91` to `$7F` if STOP is pressed, which is what makes the
|
|
811
|
+
STOP routine work. UDTIM is called from the IRQ handler at `$EA31`
|
|
812
|
+
every jiffy — if you replace the IRQ vector with your own code,
|
|
813
|
+
you must `JSR $FFEA` somewhere in your handler or the jiffy clock
|
|
814
|
+
and STOP will freeze.
|
|
815
|
+
|
|
816
|
+
## Memory
|
|
817
|
+
|
|
818
|
+
The two memory routines manipulate the KERNAL's notion of where RAM
|
|
819
|
+
starts and ends. They share a unified read/write convention:
|
|
820
|
+
**carry-flag = direction**. C=0 means "write the supplied value into
|
|
821
|
+
the KERNAL pointer"; C=1 means "read the current value into the
|
|
822
|
+
return registers".
|
|
823
|
+
|
|
824
|
+
### $FF99 — MEMTOP — Read or set top of RAM
|
|
825
|
+
|
|
826
|
+
**Input:** C=1 to read (input ignored); C=0 to set, X = low byte, Y = high byte of new top
|
|
827
|
+
**Output:** With C=1: X = low byte, Y = high byte of current top
|
|
828
|
+
**Affects:** A, X, Y
|
|
829
|
+
**Clobbers zero page:** none (may; ROM walk from $FF99, power-on vectors)
|
|
830
|
+
**Pairs with:** MEMBOT, RAMTAS
|
|
831
|
+
**Description:** Reads or writes the KERNAL's top-of-memory pointer,
|
|
832
|
+
stored at `$0283-$0284`. On a stock 38911-byte BASIC system, the
|
|
833
|
+
default value is `$A000` (`$00`/`$A0`) — BASIC strings grow downward
|
|
834
|
+
from this address, and BASIC's free-memory message reports
|
|
835
|
+
`top - vartab`. Lowering MEMTOP reserves a block at the top of RAM
|
|
836
|
+
that BASIC will not touch; for example, setting it to `$C000` keeps
|
|
837
|
+
the 4 KiB at `$C000-$CFFF` free for machine-language code that
|
|
838
|
+
coexists with BASIC. Most programs use this protect-from-BASIC
|
|
839
|
+
mechanism by writing MEMTOP early in their startup. Note that
|
|
840
|
+
lowering MEMTOP does *not* shrink memory available to ML programs;
|
|
841
|
+
it only signals BASIC to stay below the new ceiling.
|
|
842
|
+
|
|
843
|
+
### $FF9C — MEMBOT — Read or set bottom of RAM
|
|
844
|
+
|
|
845
|
+
**Input:** C=1 to read (input ignored); C=0 to set, X = low byte, Y = high byte of new bottom
|
|
846
|
+
**Output:** With C=1: X = low byte, Y = high byte of current bottom
|
|
847
|
+
**Affects:** A, X, Y
|
|
848
|
+
**Clobbers zero page:** none (may; ROM walk from $FF9C, power-on vectors)
|
|
849
|
+
**Pairs with:** MEMTOP, RAMTAS
|
|
850
|
+
**Description:** Reads or writes the KERNAL's bottom-of-memory
|
|
851
|
+
pointer, stored at `$0281-$0282`. The default value is `$0800`,
|
|
852
|
+
placing the bottom of the BASIC text area at `$0801` (the byte at
|
|
853
|
+
`$0800` is a required zero terminator). Raising MEMBOT reserves a
|
|
854
|
+
block at the bottom of RAM for non-BASIC use. Setting MEMBOT does
|
|
855
|
+
*not* relocate the existing BASIC program; if BASIC has already
|
|
856
|
+
loaded a program, you must move it manually. Application programs
|
|
857
|
+
that need a small RAM scratchpad often raise MEMBOT to `$0900` or
|
|
858
|
+
`$0A00`, leaving the cassette buffer at `$033C-$03FB` free if
|
|
859
|
+
they need more space without disturbing BASIC.
|
|
860
|
+
|
|
861
|
+
## Vectors
|
|
862
|
+
|
|
863
|
+
The KERNAL exposes 16 indirect RAM vectors at `$0314-$0333` that the
|
|
864
|
+
high-level KERNAL routines and the CPU's IRQ/BRK/NMI hardware
|
|
865
|
+
dispatch through. Patching a vector replaces the corresponding
|
|
866
|
+
KERNAL routine.
|
|
867
|
+
|
|
868
|
+
| Addr | Vector | Purpose |
|
|
869
|
+
|---------------|---------|----------------------------------------------------------|
|
|
870
|
+
| `$0314-$0315` | CINV | IRQ handler (CPU vector indirects here from `$FFFE`) |
|
|
871
|
+
| `$0316-$0317` | CBINV | BRK handler |
|
|
872
|
+
| `$0318-$0319` | NMINV | NMI handler (CPU vector indirects here from `$FFFA`) |
|
|
873
|
+
| `$031A-$031B` | IOPEN | OPEN (called by `$FFC0`) |
|
|
874
|
+
| `$031C-$031D` | ICLOSE | CLOSE (called by `$FFC3`) |
|
|
875
|
+
| `$031E-$031F` | ICHKIN | CHKIN (called by `$FFC6`) |
|
|
876
|
+
| `$0320-$0321` | ICKOUT | CHKOUT (called by `$FFC9`) |
|
|
877
|
+
| `$0322-$0323` | ICLRCH | CLRCHN (called by `$FFCC`) |
|
|
878
|
+
| `$0324-$0325` | IBASIN | CHRIN (called by `$FFCF`) |
|
|
879
|
+
| `$0326-$0327` | IBSOUT | CHROUT (called by `$FFD2`) |
|
|
880
|
+
| `$0328-$0329` | ISTOP | STOP (called by `$FFE1`) |
|
|
881
|
+
| `$032A-$032B` | IGETIN | GETIN (called by `$FFE4`) |
|
|
882
|
+
| `$032C-$032D` | ICLALL | CLALL (called by `$FFE7`) |
|
|
883
|
+
| `$032E-$032F` | USRCMD | User function (unused by KERNAL; BASIC `USR` hooks here) |
|
|
884
|
+
| `$0330-$0331` | ILOAD | LOAD (called by `$FFD5`) |
|
|
885
|
+
| `$0332-$0333` | ISAVE | SAVE (called by `$FFD8`) |
|
|
886
|
+
|
|
887
|
+
The four initialization routines below populate or restore these
|
|
888
|
+
vectors and other related state.
|
|
889
|
+
|
|
890
|
+
### $FF84 — IOINIT — Initialize I/O chips
|
|
891
|
+
|
|
892
|
+
**Input:** None
|
|
893
|
+
**Output:** None
|
|
894
|
+
**Affects:** A, X, Y
|
|
895
|
+
**Clobbers zero page:** $00-$01 (may; ROM walk from $FF84, power-on vectors)
|
|
896
|
+
**Pairs with:** CINT, RAMTAS, RESTOR
|
|
897
|
+
**Description:** Initializes the two CIA chips (sets DDRs, programs
|
|
898
|
+
Timer A on CIA1 for the 60/50 Hz jiffy IRQ), initializes the SID
|
|
899
|
+
(silences all three voices), sets the IEC bus lines to idle, and
|
|
900
|
+
clears the CIA interrupt-control registers. Called once at power-on
|
|
901
|
+
between RAMTAS and CINT. Application code can call IOINIT to recover
|
|
902
|
+
from chip-state corruption, but doing so will silence any in-progress
|
|
903
|
+
sound and reset the keyboard-scan IRQ rate to the KERNAL default.
|
|
904
|
+
|
|
905
|
+
### $FF87 — RAMTAS — RAM test and clear
|
|
906
|
+
|
|
907
|
+
**Input:** None
|
|
908
|
+
**Output:** None
|
|
909
|
+
**Affects:** A, X, Y
|
|
910
|
+
**Clobbers zero page:** $02-$FF (may; ROM walk from $FF87, power-on vectors; also stores through ($C1))
|
|
911
|
+
**Pairs with:** IOINIT, CINT, MEMTOP, MEMBOT
|
|
912
|
+
**Description:** Performs the RAM-test portion of cold start: walks
|
|
913
|
+
through each page from `$0800` upward writing `$55` then `$AA` then
|
|
914
|
+
reading back, until it finds a page that doesn't echo back the
|
|
915
|
+
written value, which becomes the top-of-RAM. Zeroes `$0002-$00FF` and
|
|
916
|
+
`$0200-$03FF` (zero page below the stack, the BASIC input buffer and
|
|
917
|
+
the screen-editor work area); the stack page is not cleared apart from
|
|
918
|
+
the two bytes `$0100-$0101` that the `STA $0002,Y` loop spills into.
|
|
919
|
+
Earlier versions of this page said "pages 0/1/2/3" in the table and
|
|
920
|
+
"pages 2 and 3" here; the ROM loop at `$FD50` is `STA $0002,Y / STA
|
|
921
|
+
$0200,Y / STA $0300,Y` with Y 0-255, confirmed in VICE x64sc 3.10. It
|
|
922
|
+
sets MEMTOP to the discovered top and MEMBOT to `$0800`,
|
|
923
|
+
clears the cassette buffer at `$033C-$03FB`. RAMTAS is destructive
|
|
924
|
+
and is normally called only at power-on. Calling it from a running
|
|
925
|
+
program will erase the BASIC input buffer and the open-file table.
|
|
926
|
+
|
|
927
|
+
### $FF8A — RESTOR — Restore default vectors
|
|
928
|
+
|
|
929
|
+
**Input:** None
|
|
930
|
+
**Output:** None
|
|
931
|
+
**Affects:** A, X, Y
|
|
932
|
+
**Clobbers zero page:** $C3-$C4 (may; ROM walk from $FF8A, power-on vectors; also stores through ($C3))
|
|
933
|
+
**Pairs with:** VECTOR, IOINIT
|
|
934
|
+
**Description:** Copies the KERNAL's default vector table (16 vectors
|
|
935
|
+
starting at `$FD30` in ROM) into the RAM vectors at `$0314-$0333`.
|
|
936
|
+
Use RESTOR after any program that has patched the KERNAL vectors
|
|
937
|
+
(e.g. a custom IRQ handler, a CHROUT wedge, a fast-load patch) to
|
|
938
|
+
return to known defaults. RESTOR is equivalent to calling VECTOR
|
|
939
|
+
with C=0 and a pointer to the ROM default table.
|
|
940
|
+
|
|
941
|
+
### $FF8D — VECTOR — Read or write all RAM vectors
|
|
942
|
+
|
|
943
|
+
**Input:** C=0 to write vectors from supplied table; C=1 to read vectors into supplied buffer. X = low byte of buffer/table, Y = high byte
|
|
944
|
+
**Output:** With C=1: buffer at (X,Y) filled with 32 bytes (16 vectors)
|
|
945
|
+
**Affects:** A, X, Y
|
|
946
|
+
**Clobbers zero page:** $C3-$C4 (may; ROM walk from $FF8D, power-on vectors; also stores through ($C3))
|
|
947
|
+
**Pairs with:** RESTOR
|
|
948
|
+
**Description:** Block-copies the 16 RAM vectors between memory and
|
|
949
|
+
the KERNAL's vector area at `$0314-$0333`. With C=1, copies *from*
|
|
950
|
+
the vector area into the supplied buffer (snapshot the current vector
|
|
951
|
+
state). With C=0, copies *into* the vector area from the supplied
|
|
952
|
+
table (install a complete vector set in one call). Typical use:
|
|
953
|
+
snapshot with C=1, patch one or two entries, install with C=0 — but
|
|
954
|
+
in practice it's cheaper to just write the two bytes of the one
|
|
955
|
+
vector you care about directly to `$0326`/`$0327` etc.
|
|
956
|
+
|
|
957
|
+
### $FF90 — SETMSG — Set KERNAL message verbosity
|
|
958
|
+
|
|
959
|
+
**Input:** A = message-control bits (bit 7 = print KERNAL error messages, bit 6 = print KERNAL control messages like "SEARCHING", "LOADING", "PRESS PLAY ON TAPE")
|
|
960
|
+
**Output:** None
|
|
961
|
+
**Affects:** None (stores A at `$009D`)
|
|
962
|
+
**Clobbers zero page:** $90, $9D (may; ROM walk from $FF90, power-on vectors)
|
|
963
|
+
**Pairs with:** OPEN, LOAD, SAVE
|
|
964
|
+
**Description:** Controls whether the KERNAL prints status messages
|
|
965
|
+
to the screen during file operations. A=`$80` (bit 7 only) enables
|
|
966
|
+
error messages but suppresses control messages — useful when an
|
|
967
|
+
application wants to handle "press play on tape" prompts itself.
|
|
968
|
+
A=`$C0` enables both (the default for BASIC). A=`$00` suppresses
|
|
969
|
+
everything — useful for headless tools that drive the KERNAL from
|
|
970
|
+
machine code and don't want stray text appearing on screen.
|
|
971
|
+
|
|
972
|
+
## IEC bus low-level
|
|
973
|
+
|
|
974
|
+
The eight routines in this section drive the C64's serial IEC bus
|
|
975
|
+
directly, byte by byte. Most application code never calls them
|
|
976
|
+
because OPEN/CLOSE/LOAD/SAVE wrap them, but custom bus protocols
|
|
977
|
+
(fast loaders, IEEE-488 adapters, custom drive commands) use them.
|
|
978
|
+
|
|
979
|
+
The IEC protocol is a five-state sequence:
|
|
980
|
+
|
|
981
|
+
1. **LISTEN/TALK** sends a device-address byte with the ATN line low
|
|
982
|
+
to announce which device is being addressed.
|
|
983
|
+
2. **SECOND/TKSA** sends a secondary-address byte (still with ATN
|
|
984
|
+
low) — typically a file-channel number on disk drives, or a
|
|
985
|
+
format command.
|
|
986
|
+
3. **IECOUT/IECIN** transfers data bytes one at a time, with ATN
|
|
987
|
+
high — IECOUT writes (after LISTEN+SECOND), IECIN reads (after
|
|
988
|
+
TALK+TKSA).
|
|
989
|
+
4. **UNLSN/UNTLK** ends the transfer by releasing the bus.
|
|
990
|
+
|
|
991
|
+
### $FFB1 — LISTEN — Send LISTEN command
|
|
992
|
+
|
|
993
|
+
**Input:** A = device number (0-31)
|
|
994
|
+
**Output:** None (status byte set on error)
|
|
995
|
+
**Affects:** A
|
|
996
|
+
**Clobbers zero page:** $90, $94-$95, $A3, $A5 (may; ROM walk from $FFB1, power-on vectors)
|
|
997
|
+
**Pairs with:** SECOND, IECOUT, UNLSN
|
|
998
|
+
**Description:** Sends the LISTEN command byte (`$20 | device`) on
|
|
999
|
+
the IEC bus with the ATN line asserted. After LISTEN, all subsequent
|
|
1000
|
+
data sent via IECOUT goes to the addressed device until UNLSN is
|
|
1001
|
+
sent. The status byte (READST) is set to `$80` if the device does
|
|
1002
|
+
not acknowledge — device not present. Internally the routine
|
|
1003
|
+
manipulates the data line (`$DD00` bit 5), clock line (`$DD00`
|
|
1004
|
+
bit 4), and ATN line (`$DD00` bit 3) of CIA2 port A to drive the
|
|
1005
|
+
serial bus signals; on a real C64 the entire byte takes about
|
|
1006
|
+
1 ms. Multiple LISTEN commands can be sent in sequence to address
|
|
1007
|
+
multiple listeners simultaneously, but only one device can talk
|
|
1008
|
+
at a time. The device argument is just the device number 0-30;
|
|
1009
|
+
the `$20 | device` encoding is done internally.
|
|
1010
|
+
|
|
1011
|
+
### $FFB4 — TALK — Send TALK command
|
|
1012
|
+
|
|
1013
|
+
**Input:** A = device number (0-31)
|
|
1014
|
+
**Output:** None (status byte set on error)
|
|
1015
|
+
**Affects:** A
|
|
1016
|
+
**Clobbers zero page:** $90, $94-$95, $A3, $A5 (may; ROM walk from $FFB4, power-on vectors)
|
|
1017
|
+
**Pairs with:** TKSA, IECIN, UNTLK
|
|
1018
|
+
**Description:** Sends the TALK command byte (`$40 | device`) on the
|
|
1019
|
+
IEC bus with ATN asserted. After TALK, the addressed device becomes
|
|
1020
|
+
the bus talker, and the C64 will receive its data via IECIN until
|
|
1021
|
+
UNTLK is sent. Status byte set to `$80` on no-acknowledge. Only one
|
|
1022
|
+
device on the bus can be the talker at any time, so a TALK command
|
|
1023
|
+
implicitly silences any previous talker. After the TALK byte goes
|
|
1024
|
+
out, the routine releases ATN and the device begins to drive the
|
|
1025
|
+
data line; the first IECIN call then reads the first byte the
|
|
1026
|
+
device produces. If you want to read from a specific channel of a
|
|
1027
|
+
disk drive (e.g. the error channel at secondary 15), follow TALK
|
|
1028
|
+
with TKSA.
|
|
1029
|
+
|
|
1030
|
+
### $FF93 — SECOND — Send secondary address after LISTEN
|
|
1031
|
+
|
|
1032
|
+
**Input:** A = secondary-address byte (typically `$60 | channel` for open channel, `$F0 | channel` to open a file, `$E0 | channel` to close)
|
|
1033
|
+
**Output:** None
|
|
1034
|
+
**Affects:** A
|
|
1035
|
+
**Clobbers zero page:** $90, $95, $A5 (may; ROM walk from $FF93, power-on vectors)
|
|
1036
|
+
**Pairs with:** LISTEN, IECOUT
|
|
1037
|
+
**Description:** Sends a secondary-address byte after a LISTEN. The
|
|
1038
|
+
secondary address selects the file channel or command on the
|
|
1039
|
+
addressed device. For disk drives, secondary `$60 | n` opens
|
|
1040
|
+
channel n for data transfer, `$F0 | n` opens a file (followed by
|
|
1041
|
+
filename bytes via IECOUT), and `$E0 | n` closes channel n. SECOND
|
|
1042
|
+
keeps ATN asserted while sending so the device sees the byte as a
|
|
1043
|
+
command, not data.
|
|
1044
|
+
|
|
1045
|
+
### $FF96 — TKSA — Send secondary address after TALK
|
|
1046
|
+
|
|
1047
|
+
**Input:** A = secondary-address byte
|
|
1048
|
+
**Output:** None
|
|
1049
|
+
**Affects:** A
|
|
1050
|
+
**Clobbers zero page:** $90, $95, $A5 (may; ROM walk from $FF96, power-on vectors)
|
|
1051
|
+
**Pairs with:** TALK, IECIN
|
|
1052
|
+
**Description:** Sends a secondary-address byte after a TALK, same
|
|
1053
|
+
encoding as SECOND. Use TKSA to tell the talking device which
|
|
1054
|
+
channel to read from. Like SECOND, keeps ATN asserted during the
|
|
1055
|
+
byte to mark it as a command.
|
|
1056
|
+
|
|
1057
|
+
### $FFA5 — IECIN — Receive one byte from serial bus
|
|
1058
|
+
|
|
1059
|
+
**Input:** None (TALK + TKSA must have been called)
|
|
1060
|
+
**Output:** A = byte received; status byte updated on EOI / error
|
|
1061
|
+
**Affects:** A
|
|
1062
|
+
**Clobbers zero page:** $90, $95, $A4-$A5 (may; ROM walk from $FFA5, power-on vectors)
|
|
1063
|
+
**Pairs with:** TALK, TKSA, UNTLK, READST
|
|
1064
|
+
**Description:** Clocks one byte off the IEC bus from the currently
|
|
1065
|
+
talking device. On the last byte of a transfer (EOI), the device
|
|
1066
|
+
holds the data line low for an extended period before the eighth
|
|
1067
|
+
bit; the KERNAL detects this and sets status byte bit 6 (`$40`).
|
|
1068
|
+
Call READST after each IECIN to detect EOI and error conditions
|
|
1069
|
+
(`$01` = timeout writing, `$02` = timeout reading, `$80` = device
|
|
1070
|
+
not present). Historical name: ACPTR.
|
|
1071
|
+
|
|
1072
|
+
### $FFA8 — IECOUT — Send one byte to serial bus
|
|
1073
|
+
|
|
1074
|
+
**Input:** A = byte to send
|
|
1075
|
+
**Output:** None (status byte updated on error)
|
|
1076
|
+
**Affects:** None (A preserved)
|
|
1077
|
+
**Clobbers zero page:** $90, $94-$95, $A5 (may; ROM walk from $FFA8, power-on vectors)
|
|
1078
|
+
**Pairs with:** LISTEN, SECOND, UNLSN, READST
|
|
1079
|
+
**Description:** Sends one byte to the currently listening device.
|
|
1080
|
+
The byte is clocked out using the C64's bit-banged IEC protocol;
|
|
1081
|
+
total time per byte is about 1 ms on a real drive. Errors
|
|
1082
|
+
(`$01`/`$02`/`$80`) are recorded in the status byte. Historical
|
|
1083
|
+
name: CIOUT.
|
|
1084
|
+
|
|
1085
|
+
### $FFAB — UNTLK — Send UNTALK
|
|
1086
|
+
|
|
1087
|
+
**Input:** None
|
|
1088
|
+
**Output:** None
|
|
1089
|
+
**Affects:** A
|
|
1090
|
+
**Clobbers zero page:** $90, $94-$95, $A3, $A5 (may; ROM walk from $FFAB, power-on vectors)
|
|
1091
|
+
**Pairs with:** TALK, TKSA, IECIN
|
|
1092
|
+
**Description:** Sends the UNTALK command (`$5F`) with ATN
|
|
1093
|
+
asserted. The current talking device, if any, releases the data
|
|
1094
|
+
line and the bus returns to idle. The command is broadcast, so
|
|
1095
|
+
all listeners and the (single) talker simultaneously hear it; the
|
|
1096
|
+
talker stops talking, the listeners stop listening for that
|
|
1097
|
+
talker. Use UNTLK to end a TALK transaction. Calling UNTLK with
|
|
1098
|
+
no active talker is harmless — the command is sent to all
|
|
1099
|
+
devices but none act on it.
|
|
1100
|
+
|
|
1101
|
+
### $FFAE — UNLSN — Send UNLISTEN
|
|
1102
|
+
|
|
1103
|
+
**Input:** None
|
|
1104
|
+
**Output:** None
|
|
1105
|
+
**Affects:** A
|
|
1106
|
+
**Clobbers zero page:** $90, $94-$95, $A3, $A5 (may; ROM walk from $FFAE, power-on vectors)
|
|
1107
|
+
**Pairs with:** LISTEN, SECOND, IECOUT
|
|
1108
|
+
**Description:** Sends the UNLISTEN command (`$3F`) with ATN
|
|
1109
|
+
asserted. All bus-listening devices stop receiving data. Use
|
|
1110
|
+
UNLSN to end a LISTEN transaction. On a disk drive, UNLSN with
|
|
1111
|
+
secondary `$F0` (file-open) pending tells the drive to finalize
|
|
1112
|
+
the OPEN — the drive parses the filename it received since
|
|
1113
|
+
LISTEN+SECOND, locates the file, and is ready for subsequent
|
|
1114
|
+
IECIN/IECOUT against the opened channel. Without UNLSN the
|
|
1115
|
+
drive doesn't know the filename is complete and won't open the
|
|
1116
|
+
file. This is why OPEN always ends with UNLSN even though no
|
|
1117
|
+
filename byte follows.
|
|
1118
|
+
|
|
1119
|
+
### $FFB7 — READST — Read serial bus status
|
|
1120
|
+
|
|
1121
|
+
**Input:** None
|
|
1122
|
+
**Output:** A = status byte (alias for the zero-page `$90` ST byte)
|
|
1123
|
+
**Affects:** A
|
|
1124
|
+
**Clobbers zero page:** $90 (may; ROM walk from $FFB7, power-on vectors)
|
|
1125
|
+
**Clobbers zero page:** $90 (must; VICE x64sc store trace, READST)
|
|
1126
|
+
**Pairs with:** IECIN, IECOUT, CHRIN, CHROUT
|
|
1127
|
+
**Description:** Reads and clears the KERNAL's serial-bus status
|
|
1128
|
+
byte. Bit values:
|
|
1129
|
+
|
|
1130
|
+
| Bit | Hex | Meaning (cassette) | Meaning (serial bus) |
|
|
1131
|
+
|-----|-------|---------------------------------|-----------------------------------|
|
|
1132
|
+
| 0 | `$01` | Unused | Timeout while writing |
|
|
1133
|
+
| 1 | `$02` | Unused | Timeout while reading |
|
|
1134
|
+
| 2 | `$04` | Short block (tape) | Unused |
|
|
1135
|
+
| 3 | `$08` | Long block (tape) | Unused |
|
|
1136
|
+
| 4 | `$10` | Unrecoverable read error (tape) | Verify error |
|
|
1137
|
+
| 5 | `$20` | Checksum error (tape) | Unused |
|
|
1138
|
+
| 6 | `$40` | End of file (tape) | EOI on input (last byte of file) |
|
|
1139
|
+
| 7 | `$80` | End of tape | Device not present |
|
|
1140
|
+
|
|
1141
|
+
A status of `$00` after a CHRIN means "more data available". `$40`
|
|
1142
|
+
after a CHRIN means "the byte just delivered was the last byte of
|
|
1143
|
+
the file" (the file is now at EOF). `$80` indicates a missing
|
|
1144
|
+
device and is set by LISTEN/TALK if no device acknowledges.
|
|
1145
|
+
|
|
1146
|
+
### $FF9F — SCNKEY — Scan keyboard
|
|
1147
|
+
|
|
1148
|
+
**Input:** None
|
|
1149
|
+
**Output:** None (keyboard queue at `$0277-$0280` may gain new entries)
|
|
1150
|
+
**Affects:** A, X, Y
|
|
1151
|
+
**Clobbers zero page:** $C5-$C6, $CB, $F5-$F6 (may; ROM walk from $FF9F, power-on vectors)
|
|
1152
|
+
**Clobbers zero page:** $C5, $CB (must; VICE x64sc store trace, SCNKEY with no key down)
|
|
1153
|
+
**Pairs with:** GETIN, STOP
|
|
1154
|
+
**Description:** Scans the 8x8 keyboard matrix via CIA1 ports A
|
|
1155
|
+
and B, decodes the pressed key against the current keyboard table
|
|
1156
|
+
(four tables: unshifted, shifted, Commodore-shifted, control), and
|
|
1157
|
+
pushes the resulting PETSCII byte into the keyboard queue. SCNKEY
|
|
1158
|
+
also tracks the shift/Commodore/CTRL state at `$028D`. It does not
|
|
1159
|
+
touch the STOP flag at `$91` (an earlier version of this page said it
|
|
1160
|
+
did; the only store to `$91` in the KERNAL is in UDTIM at `$F6DA`).
|
|
1161
|
+
SCNKEY is called from the IRQ handler at `$EA31`, after UDTIM, and
|
|
1162
|
+
exits with `$DC00` = `$7F`, which is the column drive UDTIM's `$DC01`
|
|
1163
|
+
read relies on. An application that disables IRQs must call SCNKEY
|
|
1164
|
+
itself to keep the keyboard queue filling and UDTIM (`$FFEA`) to keep
|
|
1165
|
+
the STOP flag and jiffy clock updating.
|
|
1166
|
+
|
|
1167
|
+
### $FFA2 — SETTMO — Set IEEE timeout flag
|
|
1168
|
+
|
|
1169
|
+
**Input:** A = timeout flag (bit 7 = enable timeouts)
|
|
1170
|
+
**Output:** None
|
|
1171
|
+
**Affects:** None
|
|
1172
|
+
**Clobbers zero page:** none (may; ROM walk from $FFA2, power-on vectors)
|
|
1173
|
+
**Pairs with:** READST
|
|
1174
|
+
**Description:** On the C64 this routine is a no-op. It exists for
|
|
1175
|
+
source compatibility with the PET, where it controlled the timeout
|
|
1176
|
+
behavior of the IEEE-488 bus. The C64's IEC serial bus has its own
|
|
1177
|
+
fixed timeout logic that cannot be disabled. Code can call SETTMO
|
|
1178
|
+
without effect; the routine just returns. The C64 ROM does contain
|
|
1179
|
+
a SETTMO entry point for compatibility with code originally written
|
|
1180
|
+
for the VIC-1541 IEEE adapter and PET — on those machines the
|
|
1181
|
+
input A controls whether the bus driver times out after about 64 ms
|
|
1182
|
+
or waits forever. On a stock C64 with only IEC devices, the
|
|
1183
|
+
timeouts are wired in: the KERNAL's IEC driver gives up after
|
|
1184
|
+
about 64 ms of clock-low time and sets the status byte to `$02`
|
|
1185
|
+
(read timeout) or `$01` (write timeout). Reading READST after a
|
|
1186
|
+
suspicious IECIN/IECOUT is the C64 substitute for SETTMO.
|
|
1187
|
+
|
|
1188
|
+
### $FFF3 — IOBASE — Get I/O block base address
|
|
1189
|
+
|
|
1190
|
+
**Input:** None
|
|
1191
|
+
**Output:** X = low byte, Y = high byte of I/O base (always `$00`/`$DC` on C64)
|
|
1192
|
+
**Affects:** X, Y
|
|
1193
|
+
**Clobbers zero page:** none (may; ROM walk from $FFF3, power-on vectors)
|
|
1194
|
+
**Pairs with:** SCREEN
|
|
1195
|
+
**Description:** Returns the base address of the I/O block, which
|
|
1196
|
+
on the C64 is always `$DC00` (the start of CIA1). Self-relocating
|
|
1197
|
+
code that wants to address CIAs / SID / VIC-II by offset from
|
|
1198
|
+
this base can use IOBASE so that it remains portable to other
|
|
1199
|
+
Commodore machines where the I/O block lives elsewhere. On the
|
|
1200
|
+
C64 the value is fixed in ROM and never changes. The original
|
|
1201
|
+
intent was to let one program binary run on C64, C128, B-series,
|
|
1202
|
+
and Plus/4 by replacing all `LDA $DC00` constants with
|
|
1203
|
+
`LDY ($IOBASE_VEC),Y` indirect-Y addressing through an
|
|
1204
|
+
IOBASE-derived pointer. In practice almost no C64 software
|
|
1205
|
+
took advantage — the I/O addresses are so deeply hard-coded
|
|
1206
|
+
in tutorials and listings that compatibility was lost long
|
|
1207
|
+
before IOBASE was needed.
|
|
1208
|
+
|
|
1209
|
+
## Pairs and contracts
|
|
1210
|
+
|
|
1211
|
+
KERNAL routines compose into stateful sequences. The graph below
|
|
1212
|
+
captures which calls must precede which.
|
|
1213
|
+
|
|
1214
|
+
```
|
|
1215
|
+
SETLFS ─┐
|
|
1216
|
+
├─► OPEN ─► CHKIN ─► CHRIN ─┐
|
|
1217
|
+
SETNAM ─┘ │ CHKOUT ─► CHROUT ─┤
|
|
1218
|
+
│ │
|
|
1219
|
+
├─► LOAD │
|
|
1220
|
+
├─► SAVE │
|
|
1221
|
+
└─► CLOSE ◄── CLRCHN ─────┘
|
|
1222
|
+
```
|
|
1223
|
+
|
|
1224
|
+
### File-I/O setup chain
|
|
1225
|
+
|
|
1226
|
+
| Order | Routine | Why |
|
|
1227
|
+
|-------|---------|-----------------------------------------------------------|
|
|
1228
|
+
| 1 | SETLFS | Stores logical file / device / secondary in zero page |
|
|
1229
|
+
| 2 | SETNAM | Stores filename pointer + length in zero page |
|
|
1230
|
+
| 3 | OPEN | Reads both, sends LISTEN+SECOND+filename+UNLISTEN |
|
|
1231
|
+
| 4 | CHKIN | (or CHKOUT) — selects input (or output) channel |
|
|
1232
|
+
| 5 | CHRIN | (or CHROUT, GETIN) — transfers data |
|
|
1233
|
+
| 6 | CLRCHN | Releases channel (sends UNTALK or UNLISTEN) |
|
|
1234
|
+
| 7 | CLOSE | Tells device to close its end of the channel |
|
|
1235
|
+
|
|
1236
|
+
Each step is independent — the KERNAL doesn't enforce ordering — but
|
|
1237
|
+
skipping or reordering creates predictable bugs:
|
|
1238
|
+
|
|
1239
|
+
- **Skipping SETLFS before OPEN** uses stale parameters from the
|
|
1240
|
+
previous SETLFS call. Calling OPEN twice in a row will reuse the
|
|
1241
|
+
last set of parameters, which is usually wrong.
|
|
1242
|
+
- **Skipping SETNAM** is legal for some devices (printer, screen,
|
|
1243
|
+
tape with no name) but produces error 8 ("missing filename") for
|
|
1244
|
+
disk OPENs that need a filename.
|
|
1245
|
+
- **CHRIN/CHROUT without CHKIN/CHKOUT** acts on the default channel
|
|
1246
|
+
(keyboard in, screen out). This is sometimes intentional, but it's
|
|
1247
|
+
a frequent bug when programmers forget that CHKIN/CHKOUT are
|
|
1248
|
+
required to redirect.
|
|
1249
|
+
- **CLOSE without CLRCHN** leaves the channel selected as the active
|
|
1250
|
+
input or output. The next CHRIN/CHROUT will operate on a
|
|
1251
|
+
freshly-closed file and fail. Always: CLRCHN, then CLOSE.
|
|
1252
|
+
|
|
1253
|
+
### IEC raw-bus chain
|
|
1254
|
+
|
|
1255
|
+
The low-level routines parallel the high-level ones:
|
|
1256
|
+
|
|
1257
|
+
| To write to a device | To read from a device |
|
|
1258
|
+
|---------------------------|---------------------------|
|
|
1259
|
+
| 1. LISTEN (with device #) | 1. TALK (with device #) |
|
|
1260
|
+
| 2. SECOND (with secondary)| 2. TKSA (with secondary) |
|
|
1261
|
+
| 3. IECOUT (repeatedly) | 3. IECIN (repeatedly) |
|
|
1262
|
+
| 4. UNLSN | 4. UNTLK |
|
|
1263
|
+
|
|
1264
|
+
LISTEN/TALK can be combined: send LISTEN to one device, then send a
|
|
1265
|
+
sequence of SECOND+IECOUT to write commands to it, then LISTEN to a
|
|
1266
|
+
second device for further writes, then UNLSN at the end to release
|
|
1267
|
+
all listeners. The bus supports up to 31 devices simultaneously
|
|
1268
|
+
(addresses 0-30; address 31 is reserved).
|
|
1269
|
+
|
|
1270
|
+
### Vector-patching chain
|
|
1271
|
+
|
|
1272
|
+
To install a custom IRQ handler with vector save/restore:
|
|
1273
|
+
|
|
1274
|
+
```asm
|
|
1275
|
+
sei
|
|
1276
|
+
lda $0314 ; save current IRQ vector
|
|
1277
|
+
sta old_irq_lo
|
|
1278
|
+
lda $0315
|
|
1279
|
+
sta old_irq_hi
|
|
1280
|
+
lda #<my_irq
|
|
1281
|
+
sta $0314
|
|
1282
|
+
lda #>my_irq
|
|
1283
|
+
sta $0315
|
|
1284
|
+
cli
|
|
1285
|
+
; ... run ...
|
|
1286
|
+
sei
|
|
1287
|
+
lda old_irq_lo
|
|
1288
|
+
sta $0314
|
|
1289
|
+
lda old_irq_hi
|
|
1290
|
+
sta $0315
|
|
1291
|
+
cli
|
|
1292
|
+
```
|
|
1293
|
+
|
|
1294
|
+
Or, equivalently, before exit:
|
|
1295
|
+
|
|
1296
|
+
```asm
|
|
1297
|
+
jsr $FF8A ; RESTOR — restores all 16 vectors at once
|
|
1298
|
+
```
|
|
1299
|
+
|
|
1300
|
+
RESTOR is the brute-force option. If your program patched only the
|
|
1301
|
+
IRQ vector, RESTOR is fine. If something else (e.g. a wedge that
|
|
1302
|
+
patched IBSOUT to filter screen output) was already running, RESTOR
|
|
1303
|
+
will erase its patches too.
|
|
1304
|
+
|
|
1305
|
+
### Time-clock pairing
|
|
1306
|
+
|
|
1307
|
+
`SETTIM`/`RDTIM` operate on the three-byte counter that `UDTIM`
|
|
1308
|
+
increments. Programs that disable IRQs and then read the jiffy clock
|
|
1309
|
+
will see a frozen value; either re-enable IRQs or call UDTIM manually
|
|
1310
|
+
inside the critical section to keep the counter advancing.
|
|
1311
|
+
|
|
1312
|
+
### Cold-start / warm-start sequence
|
|
1313
|
+
|
|
1314
|
+
The KERNAL's reset vector (`$FFFC`) points at the cold-start routine
|
|
1315
|
+
that runs this sequence:
|
|
1316
|
+
|
|
1317
|
+
```
|
|
1318
|
+
RESET → STX $D016 ; harmless write to anchor the stack
|
|
1319
|
+
→ JSR $FDA3 (IOINIT)
|
|
1320
|
+
→ JSR $FD50 (RAMTAS)
|
|
1321
|
+
→ JSR $FD15 (RESTOR)
|
|
1322
|
+
→ JSR $FF5B (CINT)
|
|
1323
|
+
→ JMP ($A000) ; cold-start BASIC
|
|
1324
|
+
```
|
|
1325
|
+
|
|
1326
|
+
In jump-table terms: IOINIT → RAMTAS → RESTOR → CINT, then jump to
|
|
1327
|
+
BASIC. An application that wants to restart "from scratch" without
|
|
1328
|
+
a hard reset can call the same four routines (in the same order)
|
|
1329
|
+
followed by `JMP $A000` (or its own entry point).
|
|
1330
|
+
|
|
1331
|
+
### Status-byte interaction with file I/O
|
|
1332
|
+
|
|
1333
|
+
Every file-I/O routine that touches the IEC or cassette bus updates
|
|
1334
|
+
the status byte at `$90`. Reading the status byte via READST is the
|
|
1335
|
+
*only* reliable way to detect end-of-file and bus errors — the
|
|
1336
|
+
carry flag returned from CHRIN/CHROUT/IECIN/IECOUT signals only
|
|
1337
|
+
"could not complete this operation", not "end of file".
|
|
1338
|
+
|
|
1339
|
+
The canonical end-of-file read loop:
|
|
1340
|
+
|
|
1341
|
+
```asm
|
|
1342
|
+
read_loop:
|
|
1343
|
+
jsr $FFCF ; CHRIN
|
|
1344
|
+
sta buffer,y
|
|
1345
|
+
iny
|
|
1346
|
+
jsr $FFB7 ; READST
|
|
1347
|
+
beq read_loop ; ST == 0 means more data
|
|
1348
|
+
and #$40 ; EOI bit?
|
|
1349
|
+
bne ok ; ok, file just ended
|
|
1350
|
+
; non-zero ST without EOI bit = real error
|
|
1351
|
+
jmp error
|
|
1352
|
+
ok: rts
|
|
1353
|
+
```
|
|
1354
|
+
|
|
1355
|
+
A common mistake is to use `bcc` after CHRIN instead of READST.
|
|
1356
|
+
CHRIN clears carry on a successful read *including the last byte
|
|
1357
|
+
of the file*, then sets EOI in the status byte. The carry doesn't
|
|
1358
|
+
become set until the byte *after* EOI, by which point you've
|
|
1359
|
+
already read past end-of-file. Always test READST, not carry, for
|
|
1360
|
+
end-of-file.
|
|
1361
|
+
|
|
1362
|
+
### Worked examples
|
|
1363
|
+
|
|
1364
|
+
These are complete, runnable snippets that show the canonical
|
|
1365
|
+
KERNAL call sequences. All examples assume the assembler's
|
|
1366
|
+
default segment starts somewhere safe (e.g. `$0801` with a BASIC
|
|
1367
|
+
SYS stub, or `$C000` for a standalone ML program).
|
|
1368
|
+
|
|
1369
|
+
#### Reading the disk error channel
|
|
1370
|
+
|
|
1371
|
+
As a technique with a measured round-trip: `error_channel_check` in `../techniques/file-io.md` and `../recipes/kickassembler/file-io-roundtrip.md`.
|
|
1372
|
+
|
|
1373
|
+
A common "is my disk command happy?" check — open the command
|
|
1374
|
+
channel (secondary 15), read the response into a buffer until
|
|
1375
|
+
end-of-file or CR, then close.
|
|
1376
|
+
|
|
1377
|
+
```asm
|
|
1378
|
+
read_error:
|
|
1379
|
+
lda #15 ; logical file
|
|
1380
|
+
ldx #8 ; device
|
|
1381
|
+
ldy #15 ; secondary = command channel
|
|
1382
|
+
jsr $FFBA ; SETLFS
|
|
1383
|
+
lda #0 ; no filename
|
|
1384
|
+
jsr $FFBD ; SETNAM
|
|
1385
|
+
jsr $FFC0 ; OPEN
|
|
1386
|
+
bcs err_open
|
|
1387
|
+
|
|
1388
|
+
ldx #15
|
|
1389
|
+
jsr $FFC6 ; CHKIN
|
|
1390
|
+
bcs err_chkin
|
|
1391
|
+
|
|
1392
|
+
ldy #0
|
|
1393
|
+
@loop: jsr $FFCF ; CHRIN
|
|
1394
|
+
sta buf,y
|
|
1395
|
+
iny
|
|
1396
|
+
jsr $FFB7 ; READST
|
|
1397
|
+
beq @loop
|
|
1398
|
+
; status non-zero — either EOI or error
|
|
1399
|
+
and #$BF ; mask off EOI bit
|
|
1400
|
+
bne @err
|
|
1401
|
+
|
|
1402
|
+
jsr $FFCC ; CLRCHN
|
|
1403
|
+
lda #15
|
|
1404
|
+
jsr $FFC3 ; CLOSE
|
|
1405
|
+
rts
|
|
1406
|
+
|
|
1407
|
+
@err: jsr $FFCC
|
|
1408
|
+
lda #15
|
|
1409
|
+
jsr $FFC3
|
|
1410
|
+
sec
|
|
1411
|
+
rts
|
|
1412
|
+
```
|
|
1413
|
+
|
|
1414
|
+
After this call, `buf` contains the drive status line like
|
|
1415
|
+
"00, OK,00,00" or "21,READ ERROR,18,01" (terminated by CR).
|
|
1416
|
+
|
|
1417
|
+
#### Writing a sequential disk file
|
|
1418
|
+
|
|
1419
|
+
As techniques with a measured round-trip: `kernal_file_write_seq` and `kernal_file_read_seq` in `../techniques/file-io.md`.
|
|
1420
|
+
|
|
1421
|
+
Write a small block to a new disk file. Two SETLFS+SETNAM
|
|
1422
|
+
sequences are used: one to scratch any existing file with the
|
|
1423
|
+
same name (via the command channel), then one to create the
|
|
1424
|
+
new file.
|
|
1425
|
+
|
|
1426
|
+
```asm
|
|
1427
|
+
; scratch FILE first
|
|
1428
|
+
lda #15
|
|
1429
|
+
ldx #8
|
|
1430
|
+
ldy #15
|
|
1431
|
+
jsr $FFBA ; SETLFS for command channel
|
|
1432
|
+
lda #scr_end-scr
|
|
1433
|
+
ldx #<scr
|
|
1434
|
+
ldy #>scr
|
|
1435
|
+
jsr $FFBD ; SETNAM (command string)
|
|
1436
|
+
jsr $FFC0 ; OPEN (sends the command)
|
|
1437
|
+
bcs err
|
|
1438
|
+
|
|
1439
|
+
lda #15
|
|
1440
|
+
jsr $FFC3 ; CLOSE command channel
|
|
1441
|
+
|
|
1442
|
+
; now create FILE for write
|
|
1443
|
+
lda #2 ; logical
|
|
1444
|
+
ldx #8
|
|
1445
|
+
ldy #2 ; secondary = open channel
|
|
1446
|
+
jsr $FFBA ; SETLFS
|
|
1447
|
+
lda #fn_end-fn
|
|
1448
|
+
ldx #<fn
|
|
1449
|
+
ldy #>fn
|
|
1450
|
+
jsr $FFBD ; SETNAM
|
|
1451
|
+
jsr $FFC0 ; OPEN
|
|
1452
|
+
bcs err
|
|
1453
|
+
|
|
1454
|
+
ldx #2
|
|
1455
|
+
jsr $FFC9 ; CHKOUT (redirect output)
|
|
1456
|
+
bcs err
|
|
1457
|
+
|
|
1458
|
+
ldy #0
|
|
1459
|
+
@loop: lda data,y
|
|
1460
|
+
jsr $FFD2 ; CHROUT
|
|
1461
|
+
iny
|
|
1462
|
+
cpy #data_end-data
|
|
1463
|
+
bne @loop
|
|
1464
|
+
|
|
1465
|
+
jsr $FFCC ; CLRCHN
|
|
1466
|
+
lda #2
|
|
1467
|
+
jsr $FFC3 ; CLOSE
|
|
1468
|
+
rts
|
|
1469
|
+
|
|
1470
|
+
err: jsr $FFCC
|
|
1471
|
+
lda #2
|
|
1472
|
+
jsr $FFC3
|
|
1473
|
+
sec
|
|
1474
|
+
rts
|
|
1475
|
+
|
|
1476
|
+
scr: .byte "S0:FILE"
|
|
1477
|
+
scr_end:
|
|
1478
|
+
fn: .byte "FILE,S,W"
|
|
1479
|
+
fn_end:
|
|
1480
|
+
data: .byte "HELLO, WORLD", 13
|
|
1481
|
+
data_end:
|
|
1482
|
+
```
|
|
1483
|
+
|
|
1484
|
+
#### Custom IRQ handler that calls UDTIM
|
|
1485
|
+
|
|
1486
|
+
Replace the system IRQ handler with one that does custom work
|
|
1487
|
+
each jiffy but still calls UDTIM so the jiffy clock and STOP
|
|
1488
|
+
key keep working.
|
|
1489
|
+
|
|
1490
|
+
```asm
|
|
1491
|
+
install:
|
|
1492
|
+
sei
|
|
1493
|
+
lda $0314
|
|
1494
|
+
sta old_lo
|
|
1495
|
+
lda $0315
|
|
1496
|
+
sta old_hi
|
|
1497
|
+
lda #<my_irq
|
|
1498
|
+
sta $0314
|
|
1499
|
+
lda #>my_irq
|
|
1500
|
+
sta $0315
|
|
1501
|
+
cli
|
|
1502
|
+
rts
|
|
1503
|
+
|
|
1504
|
+
my_irq:
|
|
1505
|
+
; ... my custom work, fast ...
|
|
1506
|
+
lda $D019 ; ack VIC IRQs
|
|
1507
|
+
sta $D019
|
|
1508
|
+
jsr $FFEA ; UDTIM — keep jiffy clock + STOP working
|
|
1509
|
+
jmp (old_irq_target)
|
|
1510
|
+
old_irq_target = $EA31 ; default KERNAL IRQ entry, or use stashed vector
|
|
1511
|
+
old_lo: .byte 0
|
|
1512
|
+
old_hi: .byte 0
|
|
1513
|
+
```
|
|
1514
|
+
|
|
1515
|
+
#### Polling input non-blocking in a game loop
|
|
1516
|
+
|
|
1517
|
+
The canonical game-loop input pattern:
|
|
1518
|
+
|
|
1519
|
+
```asm
|
|
1520
|
+
game_loop:
|
|
1521
|
+
jsr $FFE4 ; GETIN
|
|
1522
|
+
beq @no_key
|
|
1523
|
+
cmp #$20 ; space?
|
|
1524
|
+
beq do_fire
|
|
1525
|
+
cmp #'A'
|
|
1526
|
+
beq turn_left
|
|
1527
|
+
cmp #'D'
|
|
1528
|
+
beq turn_right
|
|
1529
|
+
@no_key:
|
|
1530
|
+
jsr $FFE1 ; STOP
|
|
1531
|
+
beq quit
|
|
1532
|
+
; ... per-frame logic ...
|
|
1533
|
+
jmp game_loop
|
|
1534
|
+
quit: rts
|
|
1535
|
+
```
|
|
1536
|
+
|
|
1537
|
+
GETIN doesn't block on the keyboard, so the loop runs every
|
|
1538
|
+
frame regardless of input. STOP is checked at the end of each
|
|
1539
|
+
iteration. This is the same loop structure BASIC programs use
|
|
1540
|
+
when they alternate between `GET A$` and game logic, except in
|
|
1541
|
+
ML it runs hundreds of times faster.
|
|
1542
|
+
|
|
1543
|
+
#### Reading the jiffy clock for timing
|
|
1544
|
+
|
|
1545
|
+
A simple "wait 30 jiffies" delay using RDTIM:
|
|
1546
|
+
|
|
1547
|
+
```asm
|
|
1548
|
+
jsr $FFDE ; RDTIM — A=high, X=mid, Y=low
|
|
1549
|
+
sty start_lo
|
|
1550
|
+
; assume the wait is < 256 jiffies so we ignore mid/high
|
|
1551
|
+
@wait: jsr $FFDE
|
|
1552
|
+
sec
|
|
1553
|
+
tya
|
|
1554
|
+
sbc start_lo
|
|
1555
|
+
cmp #30
|
|
1556
|
+
bcc @wait
|
|
1557
|
+
rts
|
|
1558
|
+
start_lo: .byte 0
|
|
1559
|
+
```
|
|
1560
|
+
|
|
1561
|
+
For longer waits, store all three RDTIM bytes and do 24-bit
|
|
1562
|
+
subtraction. Note that RDTIM reads atomically (with IRQs briefly
|
|
1563
|
+
disabled), so the three bytes are always consistent.
|
|
1564
|
+
|
|
1565
|
+
### Pair-with notation
|
|
1566
|
+
|
|
1567
|
+
The `**Pairs with:**` lines on each routine identify routines that
|
|
1568
|
+
typically appear together in correct code. The pairing has three
|
|
1569
|
+
flavors:
|
|
1570
|
+
|
|
1571
|
+
- **Setup pairing** — must call routine X before routine Y for Y
|
|
1572
|
+
to have valid input (e.g. SETLFS pairs with OPEN; LISTEN pairs
|
|
1573
|
+
with IECOUT).
|
|
1574
|
+
- **Cleanup pairing** — must call routine X after routine Y to
|
|
1575
|
+
release state (e.g. CLRCHN pairs with CHKIN/CHKOUT; UNLSN
|
|
1576
|
+
pairs with LISTEN+IECOUT).
|
|
1577
|
+
- **Symmetric pairing** — routines that read and write the same
|
|
1578
|
+
state (e.g. MEMTOP and RAMTAS; SETTIM and RDTIM).
|
|
1579
|
+
|
|
1580
|
+
The graph extractor reads these lines and produces `PAIRS_WITH`
|
|
1581
|
+
edges in the knowledge graph, so a developer asking "what do I
|
|
1582
|
+
need to call before OPEN?" can navigate from OPEN to its
|
|
1583
|
+
SETLFS+SETNAM dependencies in one query.
|
|
1584
|
+
|
|
1585
|
+
## Pitfalls
|
|
1586
|
+
|
|
1587
|
+
- **CHROUT modifies VIC-II state.** Writing PETSCII `$0E` (charset 2
|
|
1588
|
+
/ lower case) or `$8E` (charset 1 / upper-graphics) causes CHROUT
|
|
1589
|
+
to write to `$D018`, changing the character ROM source. If your
|
|
1590
|
+
program has set up a custom bitmap or a charset other than the
|
|
1591
|
+
KERNAL defaults, sending a `$0E` or `$8E` byte will revert it.
|
|
1592
|
+
Color-code PETSCII bytes (`$05`, `$1C`-`$1F`, `$81`, `$90`-`$9F`)
|
|
1593
|
+
similarly write to the current-color byte at `$0286` (page 2, not
|
|
1594
|
+
zero page) and change the foreground color of subsequent character
|
|
1595
|
+
writes. To
|
|
1596
|
+
send a literal `$0E` to a file (e.g. when dumping binary to disk),
|
|
1597
|
+
use IECOUT directly rather than CHROUT after CHKOUT.
|
|
1598
|
+
|
|
1599
|
+
- **CHKIN/CHKOUT require a prior successful OPEN.** They return
|
|
1600
|
+
C=1 + error code 3 ("file not open") if the logical file isn't
|
|
1601
|
+
in the open-file table. A common pattern bug is to call OPEN,
|
|
1602
|
+
check carry, jump to error on failure, then unconditionally call
|
|
1603
|
+
CHKIN on the (un-opened) logical file — which then fails with
|
|
1604
|
+
the misleading error 3. Always re-check carry after each
|
|
1605
|
+
KERNAL call.
|
|
1606
|
+
|
|
1607
|
+
- **CHKOUT to a disk file opened for read does NOT fail.** Measured
|
|
1608
|
+
in VICE x64sc: `OPEN 2,8,2,"FILE,S,R"` then CHKOUT 2 returns C=0; the
|
|
1609
|
+
bytes are accepted and, with the 1541-II DOS, the error channel still
|
|
1610
|
+
reads 00. Error 7 comes only from the keyboard (device 0) or a tape
|
|
1611
|
+
file opened for read. Use CHKIN for a read channel; nothing will tell
|
|
1612
|
+
you if you do not. (Earlier text said CHKOUT returned error 7 here;
|
|
1613
|
+
it does not.)
|
|
1614
|
+
|
|
1615
|
+
- **CLOSE without CLRCHN leaves a dangling channel.** Subsequent
|
|
1616
|
+
CHRIN/CHROUT will read/write to a closed file's slot. Symptoms:
|
|
1617
|
+
garbage bytes, frozen reads, status byte not updating. The cure
|
|
1618
|
+
is unconditional: `JSR $FFCC` (CLRCHN) immediately after every
|
|
1619
|
+
CHRIN/CHROUT loop, before any CLOSE.
|
|
1620
|
+
|
|
1621
|
+
- **CLALL doesn't tell IEC devices to close.** Unlike CLOSE, CLALL
|
|
1622
|
+
just zeros the KERNAL's open-file table. Disk-side state (channel
|
|
1623
|
+
buffers, dirty relative-file blocks) is left untouched until the
|
|
1624
|
+
device sees an UNLISTEN. After CLALL, if you re-OPEN a file
|
|
1625
|
+
with the same secondary on the same device, the drive may return
|
|
1626
|
+
stale data from the abandoned channel.
|
|
1627
|
+
|
|
1628
|
+
- **LOAD destination is X/Y on the call, but X/Y on return mean
|
|
1629
|
+
end-address.** A programmer who calls LOAD then expects X/Y to
|
|
1630
|
+
still hold the load address will see the value after the last
|
|
1631
|
+
byte loaded — often 65535-ish for a long load that fills memory
|
|
1632
|
+
to the top. The output-X/Y convention is *end-address + 1*, so
|
|
1633
|
+
for a file loaded to `$1000-$1FFF` the call returns X=`$00`,
|
|
1634
|
+
Y=`$20` (i.e. `$2000`).
|
|
1635
|
+
|
|
1636
|
+
- **SAVE end-address is exclusive.** The end-address+1 passed to
|
|
1637
|
+
SAVE in X/Y is the byte after the last one to save. Saving
|
|
1638
|
+
`$1000-$1FFF` requires X=`$00`, Y=`$20`. A common bug is passing
|
|
1639
|
+
the inclusive end-address `$1FFF`, which produces a file one byte
|
|
1640
|
+
short.
|
|
1641
|
+
|
|
1642
|
+
- **SAVE start-address is indirect.** A holds a *zero-page byte
|
|
1643
|
+
number*, not an address. The KERNAL reads two bytes starting at
|
|
1644
|
+
that zero-page byte to get the actual start address. BASIC uses
|
|
1645
|
+
`$2B` because BASIC's start-of-program pointer lives at
|
|
1646
|
+
`$2B/$2C`. To save a custom range from ML, store the start
|
|
1647
|
+
address into two consecutive zero-page bytes (e.g. `$FB/$FC`)
|
|
1648
|
+
and pass A=`$FB`.
|
|
1649
|
+
|
|
1650
|
+
- **STOP depends on the IRQ handler.** Reading `$91` only returns
|
|
1651
|
+
`$7F` if UDTIM (`$FFEA`) has been sampling the STOP column each
|
|
1652
|
+
jiffy — the IRQ handler at `$EA31` calls it before SCNKEY. In an
|
|
1653
|
+
SEI-protected critical section, STOP will never trigger. To make
|
|
1654
|
+
STOP work inside SEI code, `JSR $FFEA` explicitly inside your loop
|
|
1655
|
+
(at most once per frame if `TI$` matters, since it also advances
|
|
1656
|
+
the jiffy clock). An earlier version of this bullet said to call
|
|
1657
|
+
SCNKEY; SCNKEY does not write `$91` — the only store to it in the
|
|
1658
|
+
KERNAL is UDTIM's at `$F6DA`.
|
|
1659
|
+
|
|
1660
|
+
- **UDTIM is required by STOP.** If you install a custom IRQ
|
|
1661
|
+
handler that doesn't `JSR $FFEA`, the jiffy clock will stop and
|
|
1662
|
+
the STOP-key detection will stop working as a side effect (since
|
|
1663
|
+
STOP reads `$91`, which UDTIM updates via the keyboard-row read
|
|
1664
|
+
buried in its code).
|
|
1665
|
+
|
|
1666
|
+
- **PAL machines drift on TI$.** The KERNAL uses the same constants
|
|
1667
|
+
for the jiffy-clock wrap regardless of region. On PAL machines
|
|
1668
|
+
the clock ticks at 50 Hz but wraps at the NTSC-calibrated 24-hour
|
|
1669
|
+
value, so `TI$` slowly drifts behind wall-clock time. For
|
|
1670
|
+
accurate timing on PAL, use a CIA timer A in 50 Hz mode and
|
|
1671
|
+
ignore TI$.
|
|
1672
|
+
|
|
1673
|
+
- **GETIN blocks on IEC.** Only the keyboard channel makes GETIN
|
|
1674
|
+
non-blocking. After a CHKIN to an IEC device, GETIN waits for a
|
|
1675
|
+
byte from the bus just like CHRIN does. If you need a polling
|
|
1676
|
+
read from a disk file, you must implement timeouts yourself
|
|
1677
|
+
using a CIA timer.
|
|
1678
|
+
|
|
1679
|
+
- **SECOND/TKSA encoding is non-obvious.** The secondary-address
|
|
1680
|
+
byte is not just the secondary number — it's `$60 | sec` for an
|
|
1681
|
+
open channel, `$F0 | sec` for open-file, `$E0 | sec` for close.
|
|
1682
|
+
Passing the raw secondary number to SECOND will address the
|
|
1683
|
+
wrong command bits.
|
|
1684
|
+
|
|
1685
|
+
- **IOBASE on the C64 is always `$DC00`.** Code that uses IOBASE
|
|
1686
|
+
to access VIC-II (`$D000-$D02E`) or SID (`$D400-$D41C`) needs to
|
|
1687
|
+
subtract from `$DC00`, not add. The convention exists for C128
|
|
1688
|
+
/ PET compatibility where I/O lives at different addresses, but
|
|
1689
|
+
on the C64 the offset arithmetic is non-trivial.
|
|
1690
|
+
|
|
1691
|
+
- **SETTMO is a no-op.** It accepts a parameter and returns. If
|
|
1692
|
+
you're chasing a real timeout misbehavior on the IEC bus, SETTMO
|
|
1693
|
+
isn't the answer — the C64's bus timeouts are wired in and
|
|
1694
|
+
cannot be changed from software. The right fix is usually
|
|
1695
|
+
retrying the operation after the status byte reports `$01`
|
|
1696
|
+
(write timeout) or `$02` (read timeout).
|
|
1697
|
+
|
|
1698
|
+
- **MEMTOP doesn't protect RAM from ML code.** Lowering MEMTOP
|
|
1699
|
+
only tells BASIC to stay below the new ceiling. Direct
|
|
1700
|
+
pokes from ML, including the KERNAL's own LOAD into RAM
|
|
1701
|
+
beyond MEMTOP, ignore it. To truly protect RAM from
|
|
1702
|
+
KERNAL+BASIC, you must also avoid `LOAD` calls that would
|
|
1703
|
+
span the protected region.
|
|
1704
|
+
|
|
1705
|
+
- **MEMBOT doesn't relocate the BASIC program.** Raising MEMBOT
|
|
1706
|
+
after BASIC has loaded a program leaves the program at the
|
|
1707
|
+
old address; BASIC will then misread its own start pointer.
|
|
1708
|
+
Set MEMBOT before BASIC loads anything (or before any
|
|
1709
|
+
CHRGET-based BASIC operation runs), or accept that you must
|
|
1710
|
+
also move the program manually.
|
|
1711
|
+
|
|
1712
|
+
- **RAMTAS is destructive.** Never call RAMTAS from a running
|
|
1713
|
+
program unless you want to lose the BASIC input buffer,
|
|
1714
|
+
the cassette buffer at `$033C-$03FB`, and the open-file
|
|
1715
|
+
table. The cold-start sequence calls RAMTAS exactly once,
|
|
1716
|
+
before any application state exists.
|
|
1717
|
+
|
|
1718
|
+
- **CINT clears the screen.** Calling CINT from an application
|
|
1719
|
+
will fill screen RAM with spaces and color RAM with the
|
|
1720
|
+
current foreground color. If you need to re-init the VIC-II
|
|
1721
|
+
for text mode without clearing the screen, write to the
|
|
1722
|
+
VIC-II registers directly rather than calling CINT.
|
|
1723
|
+
|
|
1724
|
+
- **VECTOR with C=0 installs all 16 vectors.** Don't use VECTOR
|
|
1725
|
+
to patch one vector — pointing the supplied table at random
|
|
1726
|
+
memory will overwrite the other 15 KERNAL vectors with
|
|
1727
|
+
garbage and crash the machine on the next IRQ. Patch single
|
|
1728
|
+
vectors by writing directly to `$0314`-`$0333`.
|
|
1729
|
+
|
|
1730
|
+
- **SETMSG bit 7 alone suppresses control messages but allows
|
|
1731
|
+
errors.** A `$80` value is the standard "no chatty messages
|
|
1732
|
+
but tell me about real errors" setting for applications.
|
|
1733
|
+
`$C0` is the default BASIC-style verbose setting. `$00` is
|
|
1734
|
+
silent — the KERNAL will not print anything during file
|
|
1735
|
+
operations even if the device is missing, so the
|
|
1736
|
+
application must check carry/status itself.
|
|
1737
|
+
|
|
1738
|
+
- **The fast-load problem.** The KERNAL's IEC bus protocol is
|
|
1739
|
+
notoriously slow — about 400-800 bytes/sec on a 1541. Every
|
|
1740
|
+
successful commercial fast-loader (Action Replay, Final
|
|
1741
|
+
Cartridge, Krakout, JiffyDOS, Epyx Fastload, etc.) replaces
|
|
1742
|
+
the KERNAL's IECIN/IECOUT bit-banging with custom code that
|
|
1743
|
+
uploads a small handler to the drive's 6502 and uses
|
|
1744
|
+
non-standard line timing for 5-15x speedup. Such fast-loaders
|
|
1745
|
+
typically patch IBSOUT, ILOAD, and ISAVE vectors to call into
|
|
1746
|
+
the cartridge code. After running with a fast-loader cart,
|
|
1747
|
+
vector state is non-default and RESTOR is necessary before
|
|
1748
|
+
removing the cartridge or returning to BASIC.
|
|
1749
|
+
|
|
1750
|
+
- **CHKIN on the screen returns success silently.** CHKIN on
|
|
1751
|
+
device 3 (screen) succeeds with C=0 but then CHRIN returns
|
|
1752
|
+
the screen contents at the cursor row, byte by byte, in a
|
|
1753
|
+
PETSCII-encoded form. This is the legacy "read the screen as
|
|
1754
|
+
if it were input" mechanism that early Commodore BASIC used
|
|
1755
|
+
to implement the screen editor. Modern code that accidentally
|
|
1756
|
+
invokes this by re-using a logical file number tied to the
|
|
1757
|
+
screen sees mysterious "input" arriving from nowhere.
|
|
1758
|
+
|
|
1759
|
+
- **A logical-file collision is a silent error in some calls.**
|
|
1760
|
+
OPEN with a logical file number that is already open returns
|
|
1761
|
+
error 2 ("file already open"). But CLOSE on a logical file
|
|
1762
|
+
that is not open returns C=0 (success). This asymmetry can
|
|
1763
|
+
mask state bugs — a CLOSE that should report "wasn't open"
|
|
1764
|
+
silently succeeds. The cure is to track open-file state in
|
|
1765
|
+
the application rather than relying on the KERNAL to detect
|
|
1766
|
+
double-closes.
|
|
1767
|
+
|
|
1768
|
+
- **Zero-page locations the KERNAL routines use.** Many KERNAL
|
|
1769
|
+
routines use specific zero-page bytes as workspace. Programs
|
|
1770
|
+
that themselves use the same zero-page locations and call
|
|
1771
|
+
the KERNAL between writes will see their values clobbered.
|
|
1772
|
+
The most-touched locations:
|
|
1773
|
+
|
|
1774
|
+
| Addr | Used by |
|
|
1775
|
+
|-----------|----------------------------------------------------|
|
|
1776
|
+
| `$90` | Status byte (READST) |
|
|
1777
|
+
| `$91` | STOP-key flag (UDTIM, STOP) |
|
|
1778
|
+
| `$93` | LOAD/VERIFY flag |
|
|
1779
|
+
| `$95` | Buffered character (CHRIN/CHROUT) |
|
|
1780
|
+
| `$98` | Number of open files |
|
|
1781
|
+
| `$99-$9A` | Default input/output devices |
|
|
1782
|
+
| `$9D` | KERNAL message control (SETMSG) |
|
|
1783
|
+
| `$A0-$A2` | Jiffy clock (SETTIM, RDTIM, UDTIM) |
|
|
1784
|
+
| `$AC-$AF` | LOAD/SAVE start and end addresses |
|
|
1785
|
+
| `$B7` | Filename length (SETNAM) |
|
|
1786
|
+
| `$B8` | Logical file number (SETLFS) |
|
|
1787
|
+
| `$B9` | Secondary address (SETLFS) |
|
|
1788
|
+
| `$BA` | Device number (SETLFS) |
|
|
1789
|
+
| `$BB-$BC` | Filename pointer (SETNAM) |
|
|
1790
|
+
| `$C5` | Current key pressed (SCNKEY) |
|
|
1791
|
+
| `$C6` | Number of chars in keyboard queue |
|
|
1792
|
+
| `$D1-$D2` | Pointer to start of current screen line (PLOT) |
|
|
1793
|
+
| `$D3` | Cursor column (PLOT) |
|
|
1794
|
+
| `$D6` | Cursor row (PLOT) |
|
|
1795
|
+
|
|
1796
|
+
An ML program that needs these zero-page locations for its
|
|
1797
|
+
own use must avoid calling any KERNAL routine that touches
|
|
1798
|
+
them, or save and restore the affected bytes around each
|
|
1799
|
+
KERNAL call. The screen-editor zero-page locations
|
|
1800
|
+
(`$D1-$F2`) are particularly aggressive — calling CHROUT
|
|
1801
|
+
modifies a dozen of them.
|
|
1802
|
+
|
|
1803
|
+
- **Banking and KERNAL calls.** KERNAL ROM is mapped in at
|
|
1804
|
+
`$E000-$FFFF` only when `$01` bit 1 (HIRAM) is set. When
|
|
1805
|
+
HIRAM is cleared (e.g. to expose the underlying RAM at
|
|
1806
|
+
`$E000-$FFFF`), KERNAL jump-table calls become "JMP to
|
|
1807
|
+
whatever's in RAM at `$FFD2`" — typically garbage. The
|
|
1808
|
+
conventional pattern is to save `$01`, set HIRAM, call the
|
|
1809
|
+
KERNAL routine, restore `$01`. The CPU's IRQ/NMI vectors
|
|
1810
|
+
also live in the KERNAL bank at `$FFFA-$FFFF`, so disabling
|
|
1811
|
+
HIRAM without first disabling interrupts is a fast crash:
|
|
1812
|
+
|
|
1813
|
+
```asm
|
|
1814
|
+
pha
|
|
1815
|
+
lda $01
|
|
1816
|
+
pha
|
|
1817
|
+
ora #$02 ; set HIRAM
|
|
1818
|
+
sta $01
|
|
1819
|
+
jsr $FFD2 ; CHROUT
|
|
1820
|
+
pla
|
|
1821
|
+
sta $01
|
|
1822
|
+
pla
|
|
1823
|
+
rts
|
|
1824
|
+
```
|
|
1825
|
+
|
|
1826
|
+
- **Cross-reference:** See [c64-memory-map.md](c64-memory-map.md)
|
|
1827
|
+
for the meanings of zero-page locations `$90` (status), `$91`
|
|
1828
|
+
(STOP flag), `$A0-$A2` (jiffy clock), `$0314-$0333` (RAM
|
|
1829
|
+
vectors), and `$0259-$0276` (open-file tables). See
|
|
1830
|
+
[cia-reference.md](cia-reference.md) for the CIA1 timer
|
|
1831
|
+
programming that drives the 60/50 Hz IRQ that calls UDTIM.
|
|
1832
|
+
See [vic-ii-reference.md](vic-ii-reference.md) for the
|
|
1833
|
+
`$D018` register that CHROUT writes when processing PETSCII
|
|
1834
|
+
case-toggle bytes (`$0E`, `$8E`).
|
|
1835
|
+
|
|
1836
|
+
<!-- doc-type: hardware-reference -->
|