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,1067 @@
|
|
|
1
|
+
---
|
|
2
|
+
category: banking
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
<!-- doc-type: pitfall-reference -->
|
|
6
|
+
|
|
7
|
+
# Memory Banking Pitfalls
|
|
8
|
+
|
|
9
|
+
The C64 has three independent banking layers: $01 selects which ROMs the CPU
|
|
10
|
+
reads, $DD00 selects which 16 KB window VIC uses, and $D018 positions assets
|
|
11
|
+
within that window. Each layer has its own addressing logic, reset defaults, and
|
|
12
|
+
failure mode. The pitfalls here cover the most costly traps: the char ROM
|
|
13
|
+
invisible to the CPU under I/O mapping, the VIC 16 KB constraint that silently
|
|
14
|
+
invalidates all asset pointers when you move data between banks, the
|
|
15
|
+
read/write asymmetry that makes RAM under BASIC or KERNAL appear to vanish, and
|
|
16
|
+
the high-level-language trap where a growing code section silently runs into a
|
|
17
|
+
hardcoded charset/bitmap blit address.
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## charset_under_io_invisible_to_cpu — Char ROM readable from CPU only when I/O is swapped out
|
|
22
|
+
|
|
23
|
+
**Severity:** medium
|
|
24
|
+
**Region:** both
|
|
25
|
+
**Triggered by techniques:** char_rom_under_vic, cpu_io_port_bank, big_font_2x2, charset_copy_rom_to_ram
|
|
26
|
+
|
|
27
|
+
### Symptom
|
|
28
|
+
|
|
29
|
+
Code that tries to copy the built-in C64 character font from $D000-$D7FF reads
|
|
30
|
+
back all zeros or garbage bytes. The first 2 KB of the copy contains corrupted
|
|
31
|
+
data despite a loop that looks correct. The same loop copies fine from any other
|
|
32
|
+
address in the $0000-$CFFF range. On some rigs the bytes are not zero but shift
|
|
33
|
+
by one byte per iteration, suggesting a read from the SID or VIC shadow registers
|
|
34
|
+
instead of font data.
|
|
35
|
+
|
|
36
|
+
### Mechanism
|
|
37
|
+
|
|
38
|
+
The $D000-$DFFF range is what hardware engineers call a "multiplexed window." At
|
|
39
|
+
any given moment it can contain one of three things:
|
|
40
|
+
|
|
41
|
+
1. The I/O devices: VIC-II registers ($D000-$D3FF), SID ($D400-$D7FF), Color
|
|
42
|
+
RAM ($D800-$DBFF), CIA #1 ($DC00-$DCFF), CIA #2 ($DD00-$DDFF), and cartridge
|
|
43
|
+
expansion I/O ($DE00-$DFFF).
|
|
44
|
+
2. The 4 KB character ROM, containing the two built-in C64 fonts.
|
|
45
|
+
3. The underlying 4 KB of DRAM, when both ROM and I/O are banked out.
|
|
46
|
+
|
|
47
|
+
Which of those three wins is determined by bits 0-2 of the CPU I/O port at $0001.
|
|
48
|
+
Bit 2 = CHAREN: when set (default $37), I/O wins. When clear and at least HIRAM
|
|
49
|
+
or LORAM is set, character ROM wins. When CHAREN=0 and both LORAM=0 and HIRAM=0,
|
|
50
|
+
RAM wins. $01 = $33 (CHAREN=0, HIRAM=1, LORAM=1) is the canonical value for
|
|
51
|
+
reading the char ROM from the CPU.
|
|
52
|
+
|
|
53
|
+
The C64 resets with $01 = $37. With CHAREN = 1, reading $D000 returns a VIC-II
|
|
54
|
+
register byte, not font data.
|
|
55
|
+
|
|
56
|
+
The character ROM never appears to the CPU unless CHAREN (bit 2 of $01) is
|
|
57
|
+
cleared AND at least one of HIRAM (bit 1) or LORAM (bit 0) is set. The
|
|
58
|
+
combination $01 = $33 (binary 00110011) satisfies this: CHAREN=0, HIRAM=1,
|
|
59
|
+
LORAM=1. In that state the character ROM is readable at $D000-$DFFF by the CPU,
|
|
60
|
+
and the default BASIC + KERNAL ROMs remain banked in at $A000-$BFFF and
|
|
61
|
+
$E000-$FFFF respectively.
|
|
62
|
+
|
|
63
|
+
The VIC-II is not subject to this constraint. VIC has its own separate hardware
|
|
64
|
+
pathway to the character ROM, routed through the PLA independently of the CPU
|
|
65
|
+
banking bits. In VIC banks 0 and 2, the PLA automatically redirects VIC's
|
|
66
|
+
character generator fetches to the char ROM at the appropriate offset ($1000-$1FFF
|
|
67
|
+
in bank 0, $9000-$9FFF in bank 2) regardless of $01. This is why the default
|
|
68
|
+
screen works correctly out of reset — VIC reads the char ROM via its own path
|
|
69
|
+
while the CPU cannot see it at all.
|
|
70
|
+
|
|
71
|
+
The second trap is interrupt safety. While $01 = $33, I/O is invisible: any IRQ
|
|
72
|
+
that fires — the KERNAL's own 60 Hz CIA-1 timer interrupt as much as a raster
|
|
73
|
+
IRQ you set up — cannot acknowledge its source. The KERNAL handler's LDA $DC0D
|
|
74
|
+
at $EA7E reads character ROM instead of the CIA, and a raster handler's write to
|
|
75
|
+
$D019 lands in the RAM under the ROM instead of the VIC. The flag stays set,
|
|
76
|
+
/IRQ stays low, the handler re-enters after every RTI and the main program never
|
|
77
|
+
runs another instruction (measured in VICE x64sc: with $01 = $33, no raster IRQ
|
|
78
|
+
configured and a counting $0314 handler, 28,695 handler passes and 0 main-loop
|
|
79
|
+
iterations in 20M cycles, against 1,028 passes and 31,183 iterations with
|
|
80
|
+
$01 = $37). An earlier version of this paragraph blamed only a raster IRQ's
|
|
81
|
+
$D019 acknowledge, which read as if SEI were optional without one; it is not.
|
|
82
|
+
Always SEI before swapping I/O out.
|
|
83
|
+
|
|
84
|
+
### Fix
|
|
85
|
+
|
|
86
|
+
SEI before swapping I/O out. Always read-modify-write $01 to preserve bits 3-5
|
|
87
|
+
(datasette motor/write/sense). Pattern: SEI → read $01 → mask bits 0-2 → OR $03
|
|
88
|
+
($33 result) → write → copy $D000-$D7FF → read $01 → mask bits 0-2 → OR $07
|
|
89
|
+
($37 result) → write → CLI. The DDR at $0000 is $2F after KERNAL init; bits 0-2
|
|
90
|
+
are already outputs, so no DDR change is needed.
|
|
91
|
+
|
|
92
|
+
### Worked example
|
|
93
|
+
|
|
94
|
+
```kick
|
|
95
|
+
// Destination: RAM at $2000 (example: custom charset location)
|
|
96
|
+
// Source: built-in font from char ROM at $D000-$D7FF (2 KB)
|
|
97
|
+
//
|
|
98
|
+
// ─── BAD: reads VIC registers, not char ROM ──────────────────────────────────
|
|
99
|
+
bad_copy:
|
|
100
|
+
// $01 is $37 at this point — I/O is mapped. Reading $D000 gives
|
|
101
|
+
// VIC register 0 (sprite 0 X position), not font data.
|
|
102
|
+
ldx #0
|
|
103
|
+
!: lda $d000, x // Returns VIC/SID register byte, not font pixel!
|
|
104
|
+
sta $2000, x
|
|
105
|
+
inx
|
|
106
|
+
bne !- // All 256 bytes are I/O register garbage
|
|
107
|
+
|
|
108
|
+
// ─── GOOD: temporarily bank char ROM into $D000-$DFFF for the CPU ────────────
|
|
109
|
+
copy_charset_from_rom:
|
|
110
|
+
sei // Disable IRQs — I/O is about to disappear
|
|
111
|
+
lda $01
|
|
112
|
+
and #%11111000 // Preserve bits 3-5 (cassette lines), clear bits 0-2
|
|
113
|
+
ora #%00000011 // $33: CHAREN=0, HIRAM=1, LORAM=1 → char ROM visible
|
|
114
|
+
sta $01
|
|
115
|
+
|
|
116
|
+
// Primary font: $D000-$D7FF = eight 256-byte pages (256 chars × 8 bytes)
|
|
117
|
+
ldx #0
|
|
118
|
+
copy_page:
|
|
119
|
+
lda $d000, x // Now reads char ROM glyph data
|
|
120
|
+
sta $2000, x // Store into custom RAM charset location
|
|
121
|
+
lda $d100, x
|
|
122
|
+
sta $2100, x
|
|
123
|
+
lda $d200, x
|
|
124
|
+
sta $2200, x
|
|
125
|
+
lda $d300, x
|
|
126
|
+
sta $2300, x
|
|
127
|
+
lda $d400, x
|
|
128
|
+
sta $2400, x
|
|
129
|
+
lda $d500, x
|
|
130
|
+
sta $2500, x
|
|
131
|
+
lda $d600, x
|
|
132
|
+
sta $2600, x
|
|
133
|
+
lda $d700, x
|
|
134
|
+
sta $2700, x
|
|
135
|
+
inx
|
|
136
|
+
bne copy_page
|
|
137
|
+
|
|
138
|
+
// Restore I/O layout
|
|
139
|
+
lda $01
|
|
140
|
+
and #%11111000 // Preserve cassette bits
|
|
141
|
+
ora #%00000111 // $37: CHAREN=1, HIRAM=1, LORAM=1 → I/O visible again
|
|
142
|
+
sta $01
|
|
143
|
+
cli // IRQs safe to re-enable now
|
|
144
|
+
rts
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
A full copy of the primary font is 2 KB (256 characters × 8 bytes = eight
|
|
148
|
+
256-byte pages, $D000-$D7FF), which is why the loop above has eight load/store
|
|
149
|
+
pairs; a single-page or two-page loop copies only the first 32 or 64 characters.
|
|
150
|
+
An earlier version of this listing had two pages and claimed to copy the whole
|
|
151
|
+
font — it moved 512 bytes, and the remaining 1,536 were never copied (measured in
|
|
152
|
+
VICE x64sc: 174 mismatches against char ROM in $2200-$27FF; the eight-pair loop
|
|
153
|
+
leaves $2000-$27FF byte-identical). In Oscar64 or cc65, a 16-bit pointer loop
|
|
154
|
+
over 2048 bytes does the same job.
|
|
155
|
+
|
|
156
|
+
### Cross-references
|
|
157
|
+
|
|
158
|
+
- Memory region [$0000-$0001 — Processor I/O port](../hardware/c64-memory-map.md#0000-0001--processor-io-port) — bits 0-2 = LORAM/HIRAM/CHAREN; resolvable via `c64_memory_map 0001`, not `c64_register_lookup` (the KB has no Register node for the CPU port).
|
|
159
|
+
- Register `D018` — VIC video matrix and charset base; independent of CPU char ROM visibility.
|
|
160
|
+
- Register `DD00` — CIA2 port A; selects the VIC 16 KB bank.
|
|
161
|
+
- Technique `char_rom_under_vic` — char ROM shadow in VIC banks 0 and 2.
|
|
162
|
+
- Technique `cpu_io_port_bank` — all seven CPU memory configurations.
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
## vic_bank_visibility_collision — VIC's 16 KB window invalidates all asset pointers simultaneously
|
|
167
|
+
|
|
168
|
+
**Severity:** high
|
|
169
|
+
**Region:** both
|
|
170
|
+
**Triggered by registers:** DD00, D018
|
|
171
|
+
**Triggered by techniques:** vic_bank_select, char_rom_under_vic, screen_ram_relocation, screen_double_buffer_d018, bitmap_relocation, standard_bitmap, multicolor_bitmap, koala_format, fli_image, afli_image, ifli_image, mci_interlace_bitmap, charset_animation, big_font_2x2, dycp_scroller, sprite_cache_flip, sprite_animation_table, wireframe_pipeline, eight_way_scroll_double_buffer, hires_plot, bresenham_line, solid_vector_3d
|
|
172
|
+
|
|
173
|
+
### Symptom
|
|
174
|
+
|
|
175
|
+
Sprites that display correctly in one VIC bank become invisible or show corrupt
|
|
176
|
+
graphics when the VIC bank is changed. The screen fills with garbage characters
|
|
177
|
+
or goes black. Moving a sprite's shape data to a new RAM location produces no
|
|
178
|
+
visible change, while the old — now incorrect — graphics continue to appear.
|
|
179
|
+
Setting up a bitmap in bank 1 while leaving the screen matrix in bank 0 produces
|
|
180
|
+
a garbled display that looks nothing like the intended bitmap. Setting sprite
|
|
181
|
+
pointer values ($07F8-$07FF or their equivalent in the current screen RAM) to
|
|
182
|
+
seemingly correct offsets results in the wrong sprite image appearing.
|
|
183
|
+
|
|
184
|
+
All of these symptoms share the same root cause: a mismatch between the 16 KB
|
|
185
|
+
window VIC is looking through and the actual RAM location of the assets it is
|
|
186
|
+
supposed to fetch.
|
|
187
|
+
|
|
188
|
+
### Mechanism
|
|
189
|
+
|
|
190
|
+
The VIC-II chip has a 14-bit address bus. It cannot independently access all
|
|
191
|
+
64 KB of the C64's RAM — it can only see 16,384 bytes at a time. CIA2 port A
|
|
192
|
+
bits 0-1 (register $DD00) act as the upper two bits of VIC's address, selecting
|
|
193
|
+
which 16 KB "bank" the VIC sees:
|
|
194
|
+
|
|
195
|
+
| $DD00 bits 1-0 | VIC bank | CPU address range | Notes |
|
|
196
|
+
|----------------|----------|-------------------|-------------------------------|
|
|
197
|
+
| 11 (default) | 0 | $0000-$3FFF | Default boot bank |
|
|
198
|
+
| 10 | 1 | $4000-$7FFF | No char ROM shadow here |
|
|
199
|
+
| 01 | 2 | $8000-$BFFF | Char ROM shadow at $9000 |
|
|
200
|
+
| 00 | 3 | $C000-$FFFF | No char ROM shadow here |
|
|
201
|
+
|
|
202
|
+
The bit patterns are inverted: `11` selects bank 0, `00` selects bank 3. This
|
|
203
|
+
inversion comes from inverting buffers between CIA2 PA0-PA1 and VIC's VA14-VA15
|
|
204
|
+
lines on the motherboard. Writing the raw bank number directly to $DD00 selects
|
|
205
|
+
the wrong bank — always invert and mask.
|
|
206
|
+
|
|
207
|
+
Within its current 16 KB window, VIC interprets every pointer as a bank-relative
|
|
208
|
+
offset, not an absolute address. Every VIC data structure is affected at once:
|
|
209
|
+
|
|
210
|
+
- **Screen matrix (video matrix):** The high nibble of $D018 selects which 1 KB
|
|
211
|
+
block within the VIC bank holds the screen character codes. A value of `%0001`
|
|
212
|
+
means offset $0400, which is $0000+$0400 in bank 0 (absolute $0400), or
|
|
213
|
+
$4000+$0400 in bank 1 (absolute $4400), and so on.
|
|
214
|
+
|
|
215
|
+
- **Character generator / bitmap base:** The low nibble of $D018 selects which
|
|
216
|
+
2 KB block within the VIC bank holds the character font or bitmap data (in
|
|
217
|
+
text mode) or the high bit of the low nibble selects the 8 KB bitmap base
|
|
218
|
+
(in bitmap mode). These offsets are bank-relative.
|
|
219
|
+
|
|
220
|
+
- **Sprite data pointers:** Each of the 8 sprite pointer bytes at the end of
|
|
221
|
+
the screen matrix (at screen_base + $03F8 through screen_base + $03FF, i.e.
|
|
222
|
+
$07F8-$07FF in the default bank-0 layout) contains an index into VIC's bank
|
|
223
|
+
in 64-byte blocks. Sprite pointer 0 = $07F8; the value N means the sprite
|
|
224
|
+
shape data starts at (vic_bank_base + N × 64).
|
|
225
|
+
|
|
226
|
+
The most common collision scenario: a developer moves asset data to bank 1 for
|
|
227
|
+
more RAM, writes $DD00 to select bank 1, but leaves the screen matrix at $0400
|
|
228
|
+
and sprite pointers at $07F8-$07FF. VIC now reads its screen data from $4400
|
|
229
|
+
(bank 1 offset $0400), not $0400. Everything that was working in bank 0 is now
|
|
230
|
+
reading from a completely different area of RAM that may be uninitialized.
|
|
231
|
+
|
|
232
|
+
A subtler variant: screen matrix moved to bank 1 correctly, but sprite shape data
|
|
233
|
+
left in bank 0. Sprite pointer N × 64 now addresses bank 1 RAM at $4000 + N×64,
|
|
234
|
+
not the bank 0 shapes.
|
|
235
|
+
|
|
236
|
+
Bank 2 adds a char ROM shadow at VIC-relative $1000-$1FFF ($9000-$9FFF in CPU
|
|
237
|
+
view). Placing a custom charset there means VIC reads ROM instead. Banks 1 and 3
|
|
238
|
+
have no char ROM shadow and are preferred for fully custom graphics.
|
|
239
|
+
|
|
240
|
+
### Fix
|
|
241
|
+
|
|
242
|
+
Keep all VIC-readable assets (screen matrix, charset/bitmap, sprite shapes) in
|
|
243
|
+
the same 16 KB bank. For custom layouts prefer bank 1 ($4000-$7FFF) — no char
|
|
244
|
+
ROM shadow. When switching:
|
|
245
|
+
|
|
246
|
+
1. Pick the VIC bank; use bank 1 or 3 for custom graphics (no char ROM shadow).
|
|
247
|
+
2. Write $DD00: `lda $DD00 / and #$FC / ora #<inverted_bank_bits> / sta $DD00`.
|
|
248
|
+
Inverted codes: bank 0 = %11, bank 1 = %10, bank 2 = %01, bank 3 = %00.
|
|
249
|
+
3. Set $D018: hi-nibble = screen_offset / $0400; low nibble =
|
|
250
|
+
(charset_offset / $0800) << 1 — the 3-bit charset field occupies bits 1-3 and
|
|
251
|
+
bit 0 is unused (reads as 1). In bitmap mode only bit 3 matters:
|
|
252
|
+
(bitmap_offset / $2000) << 3. An earlier version of this step omitted the
|
|
253
|
+
shift, which for the example below yields $D018 = $11 and a charset fetched
|
|
254
|
+
from bank offset $0000, not $0800.
|
|
255
|
+
4. Compute sprite pointer bytes: `ptr = (shape_addr - bank_base) / 64`.
|
|
256
|
+
Store at `screen_base + $03F8` through `+ $03FF`.
|
|
257
|
+
5. Make bank changes during vertical blank to avoid mid-frame tears.
|
|
258
|
+
|
|
259
|
+
### Worked example
|
|
260
|
+
|
|
261
|
+
```kick
|
|
262
|
+
// Target layout: VIC bank 1 ($4000-$7FFF)
|
|
263
|
+
// Screen RAM at $4400 → D018 hi-nibble = %0001
|
|
264
|
+
// Charset at $4800 → D018 lo-nibble = %0010 (offset $0800 / $0800 = 1, << 1)
|
|
265
|
+
// Sprite 0 at $4FC0 → sprite ptr = ($4FC0-$4000)/64 = 63 = $3F
|
|
266
|
+
// Sprite pointer table at screen_base + $03F8 = $47F8
|
|
267
|
+
|
|
268
|
+
// ─── BAD: changed DD00 but forgot to move screen and sprite data ──────────────
|
|
269
|
+
lda $dd00
|
|
270
|
+
and #%11111100
|
|
271
|
+
ora #%00000010 // Bank 1 selected (inverted: %10 = bank 1)
|
|
272
|
+
sta $dd00
|
|
273
|
+
// $D018 hi-nibble still %0001 → VIC reads screen from $4400 (uninitialized)
|
|
274
|
+
// Sprite pointers still written at $07F8 (bank 0) → VIC reads ptrs from $47F8 (garbage)
|
|
275
|
+
|
|
276
|
+
// ─── GOOD: full bank-1 layout ────────────────────────────────────────────────
|
|
277
|
+
setup_bank1:
|
|
278
|
+
lda $dd00
|
|
279
|
+
and #%11111100
|
|
280
|
+
ora #%00000010 // Bank 1: inverted bits = %10
|
|
281
|
+
sta $dd00
|
|
282
|
+
|
|
283
|
+
lda #%00010010 // hi=%0001 → screen at $4400; lo=%0010 → charset at $4800
|
|
284
|
+
// ($12 — an earlier version wrote $14, which selects
|
|
285
|
+
// bank offset $1000 = $5000, verified in VICE)
|
|
286
|
+
sta $d018
|
|
287
|
+
|
|
288
|
+
// Clear screen RAM at $4400
|
|
289
|
+
lda #$20
|
|
290
|
+
ldx #0
|
|
291
|
+
!: sta $4400, x
|
|
292
|
+
sta $4500, x
|
|
293
|
+
sta $4600, x
|
|
294
|
+
sta $4700, x
|
|
295
|
+
inx
|
|
296
|
+
bne !-
|
|
297
|
+
|
|
298
|
+
// Sprite 0 pointer: $4FC0 is 63 blocks of 64 bytes into bank 1
|
|
299
|
+
lda #$3f
|
|
300
|
+
sta $47f8 // Sprite ptr table lives at screen_base($4400)+$03F8
|
|
301
|
+
|
|
302
|
+
lda #%00000001
|
|
303
|
+
sta $d015 // Enable sprite 0
|
|
304
|
+
rts
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
### Cross-references
|
|
308
|
+
|
|
309
|
+
- Register `DD00` — CIA2 port A; bits 0-1 select VIC bank (inverted encoding).
|
|
310
|
+
- Register `D018` — video matrix and char base; nibble values are bank-relative.
|
|
311
|
+
- Technique `vic_bank_select` — bank selection mechanism and inverting buffer.
|
|
312
|
+
- Technique `char_rom_under_vic` — char ROM shadow in VIC banks 0 and 2.
|
|
313
|
+
- Technique `screen_ram_relocation` — $D018 hi-nibble address calculations.
|
|
314
|
+
|
|
315
|
+
---
|
|
316
|
+
|
|
317
|
+
## ram_under_rom_traps — Writes go to RAM under ROM; reads return ROM bytes
|
|
318
|
+
|
|
319
|
+
**Severity:** medium
|
|
320
|
+
**Region:** both
|
|
321
|
+
**Triggered by techniques:** cpu_io_port_bank, ram_under_kernal, bitmap_relocation, speedcode_generation, irq_owns_processor_port, cartridge_save, cartridge_bank_easyflash, basic_rom_float_calls
|
|
322
|
+
|
|
323
|
+
### Symptom
|
|
324
|
+
|
|
325
|
+
Code or data written to $A000-$BFFF or $E000-$FFFF appears to vanish. A store
|
|
326
|
+
loop runs without error, but reading back the same addresses returns the original
|
|
327
|
+
BASIC or KERNAL ROM contents rather than the stored data. Self-modifying code
|
|
328
|
+
placed at $E000 seems to have no effect. A sprite shape table written to $A000
|
|
329
|
+
reads back as floating-point math routines. Checksumming RAM that was just written
|
|
330
|
+
with known values returns the KERNAL ROM checksum instead.
|
|
331
|
+
|
|
332
|
+
The converse symptom also occurs: a developer banks ROM out with $01 = $35 to
|
|
333
|
+
reclaim RAM at $E000-$FFFF and then is surprised that the memory was already
|
|
334
|
+
populated — because a previous write through the ROM window succeeded silently
|
|
335
|
+
and the bytes were waiting in RAM all along.
|
|
336
|
+
|
|
337
|
+
### Mechanism
|
|
338
|
+
|
|
339
|
+
The PLA enforces an asymmetric rule: reads honour the banking state (ROM wins
|
|
340
|
+
when banked in), but a write to a ROM-mapped range reaches the underlying DRAM —
|
|
341
|
+
$A000-$BFFF and $E000-$FFFF whatever $01 says, and $D000-$DFFF while character
|
|
342
|
+
ROM is mapped there (CHAREN = 0 with LORAM or HIRAM set, e.g. $33). It is NOT
|
|
343
|
+
true of $D000-$DFFF while I/O is mapped ($35/$36/$37): there the write lands in
|
|
344
|
+
the VIC/SID/CIA/colour-RAM register and the RAM beneath is untouched (measured in
|
|
345
|
+
VICE x64sc: RAM under $D000 seeded $A5 at $34, $2D written at $37 read back $2D —
|
|
346
|
+
the sprite-0 X register — and the RAM still read $A5 once I/O was banked out; the
|
|
347
|
+
same write at $A000 and $E000 landed in RAM). An earlier version of this
|
|
348
|
+
paragraph said writes reach RAM "regardless" of the banking state, which is
|
|
349
|
+
false for the I/O window. To put data under I/O, bank it out first ($34, or $33
|
|
350
|
+
if char ROM is acceptable) with interrupts disabled. See
|
|
351
|
+
techniques/memory-banking.md and hardware/c64-memory-map.md, which record the
|
|
352
|
+
same measurement. This means:
|
|
353
|
+
|
|
354
|
+
```kick
|
|
355
|
+
// $01 = $37 — BASIC ROM at $A000
|
|
356
|
+
lda #$42
|
|
357
|
+
sta $a000 // Byte stored in DRAM at $A000 — the write succeeds
|
|
358
|
+
lda $a000 // Returns BASIC ROM content, not $42 — the ROM wins the read
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
| Range | ROM content | Bank-out bit | Safe $01 value |
|
|
362
|
+
|---------------|---------------|----------------------|----------------|
|
|
363
|
+
| $A000-$BFFF | BASIC (8 KB) | LORAM (bit 0) = 0 | $36 or $34 |
|
|
364
|
+
| $D000-$DFFF | I/O (default), or char ROM when CHAREN=0 | LORAM=0 AND HIRAM=0 (CHAREN then ignored) | $30 or $34 |
|
|
365
|
+
| $E000-$FFFF | KERNAL (8 KB) | HIRAM (bit 1) = 0 | $35 or $34 |
|
|
366
|
+
|
|
367
|
+
$33 is the value for READING the char ROM at $D000 (see above), not for reaching
|
|
368
|
+
the RAM under it — an earlier version of this table listed $33 on the $D000 row,
|
|
369
|
+
which banks the char ROM *in* (measured in VICE x64sc: $D000 under $31/$32/$33
|
|
370
|
+
reads $3C, glyph '@' row 0; RAM seeded $A5 under $D000 is seen only at $30 and
|
|
371
|
+
$34). $30/$34 also bank out KERNAL, BASIC and all I/O, so SEI first and touch no
|
|
372
|
+
VIC/SID/CIA register until $01 is restored. The write asymmetry in this section
|
|
373
|
+
applies to the ROM windows only: with I/O banked in ($35-$37) a write to
|
|
374
|
+
$D000-$DFFF goes to the I/O chip, not to the RAM beneath; with the char ROM
|
|
375
|
+
banked in ($31-$33) the write does reach RAM.
|
|
376
|
+
|
|
377
|
+
Banking KERNAL out moves the CPU's interrupt vectors ($FFFA-$FFFF) from ROM to
|
|
378
|
+
the RAM underneath. Pre-write custom IRQ/NMI addresses to those RAM locations
|
|
379
|
+
while KERNAL is still visible (writes go to RAM), then SEI and switch.
|
|
380
|
+
|
|
381
|
+
Cartridge ROM at $8000-$BFFF (ROML and ROMH) is a fourth ROM-mapped range with
|
|
382
|
+
the same asymmetry: while a cartridge maps it, a write lands in the RAM beneath
|
|
383
|
+
and a read returns the cartridge byte (recorded under `cartridge_bank_easyflash`
|
|
384
|
+
in techniques/memory-banking.md; not re-measured here).
|
|
385
|
+
|
|
386
|
+
### Fix
|
|
387
|
+
|
|
388
|
+
BASIC zone: SEI → `lda $01 / and #%11111000 / ora #%00000110 / sta $01` ($36:
|
|
389
|
+
BASIC out, I/O + KERNAL remain) → access $A000-$BFFF → restore $01 → CLI.
|
|
390
|
+
|
|
391
|
+
KERNAL zone: Write IRQ/NMI vectors to $FFFE/$FFFA while KERNAL is banked in
|
|
392
|
+
(the writes reach RAM). Then SEI → `$01 = ($01 & %11111000) | %00000101` ($35)
|
|
393
|
+
→ access $E000-$FFFF → restore or stay in $35 for a permanent KERNAL replacement.
|
|
394
|
+
|
|
395
|
+
### Worked example
|
|
396
|
+
|
|
397
|
+
```kick
|
|
398
|
+
// Demonstrate the read/write asymmetry at $A000, then fix it.
|
|
399
|
+
|
|
400
|
+
// ─── BAD: write to $A000 while BASIC ROM is banked in ────────────────────────
|
|
401
|
+
bad_ram_write:
|
|
402
|
+
// $01 = $37 (default). BASIC ROM visible at $A000-$BFFF.
|
|
403
|
+
lda #$42
|
|
404
|
+
sta $a000 // Write: lands in RAM under BASIC. Byte stored.
|
|
405
|
+
lda $a000 // Read: returns BASIC ROM byte ($94 = STY, the first
|
|
406
|
+
// byte of the BASIC cold-start vector table) — NOT $42!
|
|
407
|
+
// Accumulator contains BASIC ROM content, not $42. Surprise.
|
|
408
|
+
|
|
409
|
+
// ─── GOOD: bank BASIC out before reading ─────────────────────────────────────
|
|
410
|
+
safe_ram_under_basic:
|
|
411
|
+
sei
|
|
412
|
+
lda #$42
|
|
413
|
+
sta $a000 // Write lands in RAM regardless
|
|
414
|
+
|
|
415
|
+
lda $01
|
|
416
|
+
and #%11111000
|
|
417
|
+
ora #%00000110 // $36: LORAM=0 — BASIC banked out, I/O + KERNAL remain
|
|
418
|
+
sta $01
|
|
419
|
+
|
|
420
|
+
lda $a000 // Now reads RAM: returns $42
|
|
421
|
+
// ... use $A000-$BFFF freely ...
|
|
422
|
+
|
|
423
|
+
lda $01
|
|
424
|
+
and #%11111000
|
|
425
|
+
ora #%00000111 // $37: restore BASIC
|
|
426
|
+
sta $01
|
|
427
|
+
cli
|
|
428
|
+
rts
|
|
429
|
+
|
|
430
|
+
// ─── Permanent KERNAL replacement ────────────────────────────────────────────
|
|
431
|
+
install_custom_kernal:
|
|
432
|
+
sei
|
|
433
|
+
// Write IRQ/NMI vectors to RAM while KERNAL ROM is still visible.
|
|
434
|
+
// The ROM is write-transparent — bytes land in the RAM underneath.
|
|
435
|
+
lda #<custom_irq
|
|
436
|
+
sta $fffe
|
|
437
|
+
lda #>custom_irq
|
|
438
|
+
sta $ffff
|
|
439
|
+
lda #<custom_nmi
|
|
440
|
+
sta $fffa
|
|
441
|
+
lda #>custom_nmi
|
|
442
|
+
sta $fffb
|
|
443
|
+
|
|
444
|
+
lda $01
|
|
445
|
+
and #%11111000
|
|
446
|
+
ora #%00000101 // $35: HIRAM=0 — KERNAL banked out, RAM vectors now live
|
|
447
|
+
sta $01
|
|
448
|
+
cli
|
|
449
|
+
rts
|
|
450
|
+
|
|
451
|
+
custom_irq:
|
|
452
|
+
pha
|
|
453
|
+
lda #$ff
|
|
454
|
+
sta $d019 // Ack all VIC interrupt sources
|
|
455
|
+
pla
|
|
456
|
+
rti
|
|
457
|
+
|
|
458
|
+
custom_nmi:
|
|
459
|
+
rti // Suppress RESTORE key NMI
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
### Cross-references
|
|
463
|
+
|
|
464
|
+
- Memory region [$0000-$0001 — Processor I/O port](../hardware/c64-memory-map.md#0000-0001--processor-io-port) — bits 0-2 are LORAM, HIRAM, CHAREN; the
|
|
465
|
+
read-modify-write pattern for bits 3-5 (datasette lines) must be preserved.
|
|
466
|
+
Resolvable via `c64_memory_map 0001`, not `c64_register_lookup` (the KB has no
|
|
467
|
+
Register node for the CPU port).
|
|
468
|
+
- Technique `cpu_io_port_bank` — the full table of all seven CPU memory
|
|
469
|
+
configurations, including the exact $01 values for each combination of banked
|
|
470
|
+
ROMs. The technique doc has the full discussion of the PLA's write-transparency
|
|
471
|
+
behavior and the interrupt-vector pre-write requirement.
|
|
472
|
+
- Technique `ram_under_kernal` — specifically covers using $E000-$FFFF as RAM,
|
|
473
|
+
the custom interrupt vector setup, and cycle budget implications of losing the
|
|
474
|
+
KERNAL IRQ chain.
|
|
475
|
+
|
|
476
|
+
---
|
|
477
|
+
|
|
478
|
+
## charset_blit_overruns_grown_code — A hardcoded charset/bitmap address collides with a code section that grew into it
|
|
479
|
+
|
|
480
|
+
**Severity:** high
|
|
481
|
+
**Region:** both
|
|
482
|
+
**Triggered by registers:** D018
|
|
483
|
+
**Triggered by techniques:** screen_double_buffer_d018, bitmap_relocation, speedcode_generation, charset_animation, big_font_2x2, dycp_scroller, char_bullets, charset_parallax, destructible_char_terrain, hires_plot
|
|
484
|
+
**Mitigated by techniques:** memory_layout_plan
|
|
485
|
+
|
|
486
|
+
### Symptom
|
|
487
|
+
|
|
488
|
+
A C64 program built with a high-level toolchain (Oscar64, cc65) boots, runs its
|
|
489
|
+
init, then crashes — often back to the BASIC `READY.` prompt — after a code
|
|
490
|
+
change that only touched logic far from the boot path. The init runs far enough
|
|
491
|
+
to set early globals (a sentinel byte you poll reads as initialised), but the
|
|
492
|
+
main loop never starts. Adding *more* unrelated code makes the crash appear or
|
|
493
|
+
worsen; reverting a few hundred bytes of code makes it vanish. The crash is
|
|
494
|
+
sensitive to total program size, not to the content of the change.
|
|
495
|
+
|
|
496
|
+
### Mechanism
|
|
497
|
+
|
|
498
|
+
C compilers place code and read-only data contiguously from the load address
|
|
499
|
+
upward; the linker grows the code/data image toward higher addresses as you add
|
|
500
|
+
functions and string literals. Custom-graphics C64 programs frequently blit a
|
|
501
|
+
charset to a *hardcoded* VIC address (commonly `$3000` or `$3800` in bank 0) and
|
|
502
|
+
sprite/bitmap data to other fixed addresses, deliberately time-sharing those
|
|
503
|
+
addresses with const data (e.g. a title bitmap that was already copied elsewhere
|
|
504
|
+
at startup). This works only while an **implicit invariant** holds: the code
|
|
505
|
+
section ends *below* the lowest hardcoded blit address.
|
|
506
|
+
|
|
507
|
+
Nothing enforces that invariant. The compiler does not know `$3000` is special —
|
|
508
|
+
it is just a literal pointer in a `memcpy`. When the code section grows past the
|
|
509
|
+
blit address, the runtime blit (e.g. `init_charset()` writing 2 KB of glyph data
|
|
510
|
+
to `$3000`) overwrites *live executable code* with charset bytes. Execution
|
|
511
|
+
survives until the CPU calls a function that now lives in the clobbered range and
|
|
512
|
+
runs glyph data as instructions — typically a `JAM`/`BRK` storm or a stray `RTS`
|
|
513
|
+
that unwinds into the KERNAL warm-start, i.e. `READY.`.
|
|
514
|
+
|
|
515
|
+
`$D018` is the register that makes the blit address load-bearing: its low nibble
|
|
516
|
+
selects the charset/bitmap offset within the VIC bank, so the asset *must* live
|
|
517
|
+
at that fixed 2 KB-aligned (charset) or 8 KB-aligned (bitmap) address — it cannot
|
|
518
|
+
simply be relocated to wherever the linker has free space.
|
|
519
|
+
|
|
520
|
+
### Fix
|
|
521
|
+
|
|
522
|
+
1. **Diagnose with the linker map.** Oscar64 emits a `.MAP` on every build; its
|
|
523
|
+
`sections` list shows `<start> - <end> : DATA, code` (address range first,
|
|
524
|
+
then the name — an earlier version of this step had the order reversed, so a
|
|
525
|
+
grep for `DATA, code :` finds nothing). Compare the code-section end
|
|
526
|
+
against every hardcoded blit address. If `code_end > charset_addr`, that is the
|
|
527
|
+
bug. (cc65: read the map's segment list the same way.)
|
|
528
|
+
2. **Move the blit targets to the top of the bank**, above the projected code
|
|
529
|
+
ceiling: put the charset at the highest 2 KB-aligned slot in the bank (bank 0
|
|
530
|
+
→ `$3800`) and any sprite/extra data just below it at the right alignment.
|
|
531
|
+
Update the asset pointer register write — with Oscar64's `vic_setmode(mode,
|
|
532
|
+
screen, charset)` this is automatic; in assembly recompute `$D018` and the
|
|
533
|
+
sprite-pointer bytes by hand.
|
|
534
|
+
3. **Document the surviving invariant** ("code must end below `$3780`") next to
|
|
535
|
+
the address constants, because step 2 only raises the ceiling — it does not
|
|
536
|
+
remove it. The durable cure is to move the graphics to a VIC bank that does
|
|
537
|
+
not overlap the program image at all, or to shrink the time-shared const.
|
|
538
|
+
|
|
539
|
+
### Worked example
|
|
540
|
+
|
|
541
|
+
```c
|
|
542
|
+
// BAD: charset hardcoded at $3000. Works until the code section grows past it,
|
|
543
|
+
// after which init_charset() overwrites live code at $3000+ → crash to READY.
|
|
544
|
+
#define CHARSET ((unsigned char *)0x3000u)
|
|
545
|
+
// .MAP after a feature lands: 0880 - 3050 : DATA, code ← code end $3050 > $3000
|
|
546
|
+
|
|
547
|
+
// GOOD: charset at the top 2KB of bank 0; sprite just below; vic_setmode
|
|
548
|
+
// recomputes $D018. Invariant becomes "code must end below $3780".
|
|
549
|
+
#define CHARSET ((unsigned char *)0x3800u) // $3800-$3FFF
|
|
550
|
+
#define SELSPRITE_DATA_ADDR 0x3780u // 64-byte aligned
|
|
551
|
+
#define SELSPRITE_PTR_VALUE 0xdeu // $3780 / 64
|
|
552
|
+
// vic_setmode(VICM_TEXT, SCREEN, CHARSET) → writes $D018 = $1E (screen $0400, charset $3800;
|
|
553
|
+
// reads back as $1F because bit 0 always reads 1)
|
|
554
|
+
```
|
|
555
|
+
|
|
556
|
+
### Cross-references
|
|
557
|
+
|
|
558
|
+
- Register `D018` — VMCSB; high nibble = screen offset, low nibble bits 1-3 =
|
|
559
|
+
charset offset (× $0800), bit 3 = bitmap offset (× $2000). Forces graphics
|
|
560
|
+
assets to fixed bank-relative addresses.
|
|
561
|
+
- Pitfall `vic_bank_visibility_collision` — the other half of the layout story:
|
|
562
|
+
once assets are correctly placed, they must all share one 16 KB VIC bank.
|
|
563
|
+
- Toolchain `oscar64-reference` — the `.MAP` "objects by size" / region list is
|
|
564
|
+
the primary diagnostic for an over-large image; Oscar64 places BSS/heap/stack
|
|
565
|
+
above the code+data image, so a growing image walks toward fixed asset
|
|
566
|
+
addresses with no build-time warning.
|
|
567
|
+
|
|
568
|
+
---
|
|
569
|
+
|
|
570
|
+
## irq_during_charen_window — An interrupt taken while the char ROM covers I/O can never acknowledge itself
|
|
571
|
+
|
|
572
|
+
**Severity:** high
|
|
573
|
+
**Region:** both
|
|
574
|
+
**Triggered by registers:** DC0D, D019
|
|
575
|
+
**Triggered by techniques:** charset_copy_rom_to_ram, char_rom_under_vic, cpu_io_port_bank, irq_owns_processor_port
|
|
576
|
+
|
|
577
|
+
### Symptom
|
|
578
|
+
|
|
579
|
+
The program stops dead somewhere inside a character ROM copy, or just
|
|
580
|
+
after one. No crash to `READY.`, no garbage, no border flash: the
|
|
581
|
+
screen stays as it was. If the copy was meant to be followed by a
|
|
582
|
+
`$D018` write, the font never changes. A machine in this state does not
|
|
583
|
+
respond to keys, and on a real C64 RUN/STOP-RESTORE may or may not get
|
|
584
|
+
it back, depending on what the NMI handler touches.
|
|
585
|
+
|
|
586
|
+
### Mechanism
|
|
587
|
+
|
|
588
|
+
With `$01` = `$33` the `$D000-$DFFF` window is the character ROM. An
|
|
589
|
+
interrupt that arrives in that state runs a handler written for the
|
|
590
|
+
normal map. The KERNAL IRQ handler ends with `LDA $DC0D` to clear CIA1's
|
|
591
|
+
interrupt flag; a raster handler writes `$D019` to clear the VIC's. Both
|
|
592
|
+
now address font bytes. The read does not reach the CIA, the write goes
|
|
593
|
+
to the RAM under the ROM, and the source's flag stays set. `/IRQ`
|
|
594
|
+
stays low, `RTI` restores a clear I flag, the CPU takes the interrupt
|
|
595
|
+
again on the next instruction boundary, and the copy loop between
|
|
596
|
+
those instruction boundaries gets nothing. Everything the handler does
|
|
597
|
+
before the failed acknowledge also happens against the wrong chip: the
|
|
598
|
+
KERNAL keyboard scan writes its column selects into RAM and reads its
|
|
599
|
+
row byte from the ROM, and decodes whatever glyph row that is as keys.
|
|
600
|
+
|
|
601
|
+
Measured in VICE x64sc 3.10 (`charset-copy-rom-to-ram` recipe, PAL): a
|
|
602
|
+
4 KB copy started with interrupts enabled ran 30 of 256 loop iterations
|
|
603
|
+
before the KERNAL IRQ fell due, then the handler ran 26 times in the
|
|
604
|
+
61,000 cycles a watchdog allowed and the loop counter never moved; one
|
|
605
|
+
byte was in the keyboard buffer afterwards. NTSC: 7 iterations, 27
|
|
606
|
+
handler passes, one key. Both runs reproduce byte for byte. The
|
|
607
|
+
iteration count depends only on where in the timer period the copy
|
|
608
|
+
began (CIA1 timer A is latched with `$4025` on PAL, `$4295` on NTSC,
|
|
609
|
+
from the KERNAL image; about 16,400 and 17,000 cycles), so a copy that
|
|
610
|
+
happens to start just after a tick can get through several thousand
|
|
611
|
+
bytes and still hang.
|
|
612
|
+
|
|
613
|
+
`charset_under_io_invisible_to_cpu` on this page has the other half of
|
|
614
|
+
this trap and a longer run of the same hang; that pitfall is about
|
|
615
|
+
reading the wrong bytes when `$01` is left at `$37`, this one is about
|
|
616
|
+
what happens when `$01` is set right and interrupts are left on.
|
|
617
|
+
|
|
618
|
+
### Fix
|
|
619
|
+
|
|
620
|
+
`SEI` before the `$01` write that maps the ROM; `CLI` after the write
|
|
621
|
+
that restores I/O. Nothing in between may enable interrupts, which
|
|
622
|
+
rules out KERNAL calls (`kernal_assumes_sei_cleared`). If a raster IRQ
|
|
623
|
+
is live and the copy is long, stop it rather than defer it: clear
|
|
624
|
+
`$D01A` bit 0 and acknowledge `$D019` before the switch, restart after.
|
|
625
|
+
A program that has to take an interrupt while the ROM is mapped (rare;
|
|
626
|
+
a sample player, say) needs a handler that restores `$37` as its first
|
|
627
|
+
instruction and puts `$33` back before `RTI`, and that handler must be
|
|
628
|
+
in RAM or KERNAL, never under `$D000`.
|
|
629
|
+
|
|
630
|
+
The recipe's watchdog is a debugging aid, not a fix: a CIA2 timer NMI
|
|
631
|
+
that restores `$37`, acknowledges both CIAs and unwinds the stack turns
|
|
632
|
+
the silent hang into a reported failure while the copy is being
|
|
633
|
+
developed.
|
|
634
|
+
|
|
635
|
+
### Worked example
|
|
636
|
+
|
|
637
|
+
```kick
|
|
638
|
+
// BAD: the ROM is mapped and the KERNAL IRQ is still armed.
|
|
639
|
+
lda $01
|
|
640
|
+
and #$f8
|
|
641
|
+
ora #$03
|
|
642
|
+
sta $01 // char ROM at $D000
|
|
643
|
+
ldx #0
|
|
644
|
+
!: lda $d000, x // ~30 iterations later on PAL the IRQ fires,
|
|
645
|
+
sta $3000, x // LDA $DC0D reads a font byte, and the handler
|
|
646
|
+
inx // re-enters for ever
|
|
647
|
+
bne !-
|
|
648
|
+
|
|
649
|
+
// GOOD: no interrupt can run while I/O is out of the map.
|
|
650
|
+
sei
|
|
651
|
+
lda $01
|
|
652
|
+
and #$f8
|
|
653
|
+
ora #$03
|
|
654
|
+
sta $01
|
|
655
|
+
ldx #0
|
|
656
|
+
!: lda $d000, x
|
|
657
|
+
sta $3000, x
|
|
658
|
+
inx
|
|
659
|
+
bne !-
|
|
660
|
+
lda $01
|
|
661
|
+
and #$f8
|
|
662
|
+
ora #$07
|
|
663
|
+
sta $01 // I/O back
|
|
664
|
+
cli // the deferred IRQ runs now, and acknowledges
|
|
665
|
+
```
|
|
666
|
+
|
|
667
|
+
### Cross-references
|
|
668
|
+
|
|
669
|
+
- Technique `charset_copy_rom_to_ram`: the copy done safely, timed, and done once unsafely under a watchdog.
|
|
670
|
+
- Technique `cpu_io_port_bank`: the `$01` values and the bits to preserve.
|
|
671
|
+
- Technique `char_rom_under_vic`: where the VIC sees the ROM without any copy.
|
|
672
|
+
- Pitfall `charset_under_io_invisible_to_cpu`: the copy made with I/O still mapped.
|
|
673
|
+
- Pitfall `kernal_assumes_sei_cleared` (kernal-and-io.md): why a KERNAL call inside the window would defeat the `SEI`.
|
|
674
|
+
- Register `DC0D`: CIA1 interrupt control; the acknowledge the KERNAL handler cannot make.
|
|
675
|
+
- Register `D019`: VIC interrupt flags; the acknowledge a raster handler cannot make.
|
|
676
|
+
- Recipe `recipes/kickassembler/charset-copy-rom-to-ram.md`.
|
|
677
|
+
|
|
678
|
+
---
|
|
679
|
+
|
|
680
|
+
## decruncher_overwrites_kernal_zero_page — A self-extractor that runs from the zero page leaves the KERNAL's variables full of decruncher code
|
|
681
|
+
|
|
682
|
+
**Severity:** high
|
|
683
|
+
**Region:** both
|
|
684
|
+
**Triggered by kernal:** CHROUT
|
|
685
|
+
**Triggered by techniques:** zx0_lzsa_decrunchers, pucrunch_decruncher, doynax_decruncher, zero_page_burst, byteboozer_packer
|
|
686
|
+
**Mitigated by techniques:** cpu_io_port_bank
|
|
687
|
+
|
|
688
|
+
### Symptom
|
|
689
|
+
|
|
690
|
+
A program that runs from its own PRG stops working the moment it is
|
|
691
|
+
wrapped in a self-extracting cruncher. Nothing is reported. The two shapes
|
|
692
|
+
measured here, on a payload that prints one line through `CHROUT` and
|
|
693
|
+
then sets a byte:
|
|
694
|
+
|
|
695
|
+
- Under Dali 0.3.5 `--sfx --small`, the payload is entered and never
|
|
696
|
+
comes back from its first `JSR $FFD2`. No text, no later store, the
|
|
697
|
+
border colour it sets afterwards never appears.
|
|
698
|
+
- Under bitfire's `zx0 --sfx`, every `CHROUT` call returns, the later
|
|
699
|
+
store happens and the border changes, but the line is not on the
|
|
700
|
+
screen. The eleven screen codes were found at `$4CC7`, an address in
|
|
701
|
+
otherwise unused RAM well above the payload (which ends at `$1433`):
|
|
702
|
+
it is nothing more than the two decruncher bytes left in the editor's
|
|
703
|
+
line pointer `$D1/$D2` (`$4CBA`) plus its column `$D3` (`$0D`).
|
|
704
|
+
|
|
705
|
+
The same payload crunched with Dali's standard `--sfx` and with pucrunch
|
|
706
|
+
`-c64` printed its line where the uncrunched build did.
|
|
707
|
+
|
|
708
|
+
### Mechanism
|
|
709
|
+
|
|
710
|
+
The zero page is the cheapest place to put a decruncher: zero-page
|
|
711
|
+
addressing saves a byte and a cycle on every operand, `(zp),Y` is the
|
|
712
|
+
only indirect mode the 6502 has, and the page is free of anything the
|
|
713
|
+
decruncher itself needs. Dali's and bitfire's self-extractors both copy
|
|
714
|
+
their decruncher into it from the top of the copy down to `$01`, and the
|
|
715
|
+
byte that lands at `$01` banks every ROM out so the whole 64 KB can be a
|
|
716
|
+
decrunch target: `$34` under Dali `--small`, `$38` under bitfire, both
|
|
717
|
+
with bits 0 to 2 clear (measured with a store trace on `$0001`; neither
|
|
718
|
+
stub writes the port again during the decrunch). The stream is moved to the top of
|
|
719
|
+
memory under the KERNAL and decrunched forwards from the payload's load
|
|
720
|
+
address. The page they overwrite is where the KERNAL and BASIC keep
|
|
721
|
+
their state.
|
|
722
|
+
|
|
723
|
+
Measured in the windowless x64sc build of VICE 3.10, PAL, 2026-09-23. A
|
|
724
|
+
tracepoint at the payload's entry (`SYS 2061`, `$080D`) dumped `$0000`
|
|
725
|
+
to `$00FF`, and each dump was compared with the same dump from the
|
|
726
|
+
uncrunched payload. The stopwatch at entry was 2,970,597 cycles for the
|
|
727
|
+
plain build and 3,032,679 to 3,180,431 for the four crunched builds, so
|
|
728
|
+
each decrunch cost under a quarter of a second of emulated time.
|
|
729
|
+
|
|
730
|
+
| Build | Zero-page bytes changed at entry | `$01` | `$9A` | Printed |
|
|
731
|
+
|---|---|---|---|---|
|
|
732
|
+
| uncrunched | 0 | `$37` | `$03` | yes |
|
|
733
|
+
| Dali `--sfx` | 20 | `$37` | `$03` | yes |
|
|
734
|
+
| pucrunch `-c64` | 16 | `$37` | `$03` | yes |
|
|
735
|
+
| Dali `--sfx --small` | 178 (`$01` to `$B7`) | `$34` | `$02` | no |
|
|
736
|
+
| bitfire `zx0 --sfx` | 201 (`$02` to `$D4`) | `$37` | `$A6` | no |
|
|
737
|
+
|
|
738
|
+
The two failures have different fatal bytes, and neither is the one a
|
|
739
|
+
first guess would name:
|
|
740
|
+
|
|
741
|
+
- **Dali `--small`** leaves `$01` at `$34`. `JSR $FFD2` then executes
|
|
742
|
+
the last bytes of the crunched stream, which the stub parked under
|
|
743
|
+
the KERNAL. Putting `$37` back is not enough on its own: measured,
|
|
744
|
+
the payload still hung. The byte that hangs it is `$9A`, the default
|
|
745
|
+
output device (DFLTO), which the decruncher code left at `$02`.
|
|
746
|
+
`CHROUT` reads `$9A` before it does anything else, and `2` is the
|
|
747
|
+
RS-232 device. Traced: `CHROUT` hands the byte to the RS-232 output
|
|
748
|
+
routine, which stores it through the unopened output buffer pointer
|
|
749
|
+
at `$F9/$FA` (zero on a clean boot, so the byte lands in `$0000`),
|
|
750
|
+
starts CIA2's timer and enables its NMI; the NMI handler at `$FE47`
|
|
751
|
+
then re-enters about every 75 cycles, the stack pointer falls six
|
|
752
|
+
bytes per entry, and the payload never runs again. It is not a wait
|
|
753
|
+
loop: the routine's buffer check at `$F017` ran once. Restoring `$01` and `$9A` (with `$99` for
|
|
754
|
+
good measure) and nothing else made the payload print on the
|
|
755
|
+
original screen, at the row `RUN` left the cursor on.
|
|
756
|
+
- **bitfire** hands over with the KERNAL mapped: the last thing its
|
|
757
|
+
zero-page decruncher does before the jump is `DEC $01`, thirteen
|
|
758
|
+
cycles before entry, which turns its `$38` into `$37` (measured at
|
|
759
|
+
the store, not read from its source; Dali `--small` leaves `$34`
|
|
760
|
+
there). It leaves `$9A` at `$A6`, so
|
|
761
|
+
`CHROUT` treats the output as a serial-bus device and returns after
|
|
762
|
+
the bus times out. That is why its calls come back. With `$9A` put
|
|
763
|
+
back to `3`, the calls reach the screen editor, and the text still
|
|
764
|
+
does not appear: the editor's line pointer `$D1/$D2` (PNT) reads
|
|
765
|
+
`$4CBA` and its column `$D3` reads `$0D`, so the characters go to
|
|
766
|
+
`$4CC7`. `$C7`, the reverse-video flag, reads `$85`, so once the
|
|
767
|
+
pointer is right the line comes out in reverse video. Three of the
|
|
768
|
+
KERNAL's variables are wrong in three different ways, and the list
|
|
769
|
+
stops there only because the payload calls nothing else.
|
|
770
|
+
|
|
771
|
+
The two stubs that pass are not clean either, and the difference is
|
|
772
|
+
what they touch. Dali's standard stub pushes the zero page onto the
|
|
773
|
+
stack before the copy and pops it back before the jump; it entered the
|
|
774
|
+
payload with the stack pointer at `$FF`, ten bytes of its own exit code
|
|
775
|
+
still at `$E3` to `$EC` (in the screen line link table), and BASIC's
|
|
776
|
+
pointers at `$2D` to `$32`, `$39/$3A` and `$AE/$AF` rewritten, none of
|
|
777
|
+
which the print path reads. Pucrunch's decruncher sits at `$F7` to `$FF`
|
|
778
|
+
and leaves those nine bytes changed, plus the same BASIC pointers; the
|
|
779
|
+
KERNAL's own variables are below `$F7` and the RS-232 pointers at `$F7`
|
|
780
|
+
to `$FA` are idle. Pucrunch is on the Triggered-by line for those nine
|
|
781
|
+
unsaved bytes all the same: `$F7` to `$FA` are the RS-232 buffer
|
|
782
|
+
pointers, `$FB` to `$FE` the free zero page a payload may already be
|
|
783
|
+
using, `$FF` BASIC's float-to-ASCII workspace, and `$2D/$2E` BASIC's
|
|
784
|
+
end-of-program pointer, so a payload that expects any of them to hold
|
|
785
|
+
what they held before the stub ran meets the same mechanism on a
|
|
786
|
+
narrower front; this payload did not. The Doynax self-extractor copies its depacker to
|
|
787
|
+
`$00C2` and up (its technique entry says so); whether it saves what it
|
|
788
|
+
covers was not measured here.
|
|
789
|
+
|
|
790
|
+
### Fix
|
|
791
|
+
|
|
792
|
+
One of three, in order of cost:
|
|
793
|
+
|
|
794
|
+
1. **Choose a stub that saves.** Dali's standard `--sfx` and pucrunch's
|
|
795
|
+
default decruncher both let a KERNAL-calling payload run unchanged
|
|
796
|
+
here. The `--small` flag bought 59 bytes of file on this payload and cost
|
|
797
|
+
the machine state; take it only for a payload that owns the machine.
|
|
798
|
+
2. **Re-initialise in the payload's prologue.** Before the first KERNAL
|
|
799
|
+
call: write `$37` to `$01` (`cpu_io_port_bank`), then `JSR $FF84`
|
|
800
|
+
(IOINIT) and `JSR $FF81` (CINT). CINT resets the screen editor's
|
|
801
|
+
pointers, sets `$9A` back to the screen and `$99` to the keyboard,
|
|
802
|
+
and clears the screen; it does not clear `$C7`, so store zero there
|
|
803
|
+
as well. Measured: the payload printed its line at row 0 under both
|
|
804
|
+
failing stubs. A payload that must keep the screen contents restores
|
|
805
|
+
the specific bytes instead: for Dali `--small` that was `$01` and
|
|
806
|
+
`$9A`; for bitfire it was `$9A`, `$C7`, `$D3` and `$D1/$D2`.
|
|
807
|
+
Measured: a build that stored `3` to `$9A`, `0` to `$C7` and `$D3`
|
|
808
|
+
and `$0518` to `$D1/$D2`, and nothing else, printed its line on row
|
|
809
|
+
7 in normal video under bitfire. `$99` was left as the decruncher
|
|
810
|
+
left it and `$D4`, the quote-mode flag, stayed at `$08`; plain
|
|
811
|
+
letters were unaffected, though a payload that prints colour or
|
|
812
|
+
cursor codes would see them as reversed glyphs under that flag (the
|
|
813
|
+
editor's quote-mode rule, not measured here). The line pointer is
|
|
814
|
+
`$0400` plus 40 times the row, and CINT rebuilds it if the screen
|
|
815
|
+
contents do not matter.
|
|
816
|
+
3. **Keep the payload's own zero-page use above the stub's span.** The
|
|
817
|
+
stub's copy runs down to `$01`, so nothing of the payload's survives
|
|
818
|
+
below `$B8` (Dali `--small`) or `$D5` (bitfire); values the payload
|
|
819
|
+
needs at start must be in its body, not in the zero page it was
|
|
820
|
+
crunched with.
|
|
821
|
+
|
|
822
|
+
A program that sets up its own interrupts, screen and I/O and never
|
|
823
|
+
returns to BASIC needs none of this, which is why the `--small` stub
|
|
824
|
+
exists.
|
|
825
|
+
|
|
826
|
+
### Worked example
|
|
827
|
+
|
|
828
|
+
The good prologue was measured with and without the `$C7` store: with
|
|
829
|
+
it the line reads normally under both stubs; without it, under bitfire,
|
|
830
|
+
row 0 is eleven reverse-video glyphs.
|
|
831
|
+
|
|
832
|
+
```kick
|
|
833
|
+
// BAD: the payload's first act is a KERNAL call. Under a stub that
|
|
834
|
+
// decrunches from the zero page, $01 may be $34 and $9A is whatever
|
|
835
|
+
// opcode the decruncher left there. Measured: hangs (Dali --small)
|
|
836
|
+
// or prints to $4CC7 (bitfire).
|
|
837
|
+
entry_bad:
|
|
838
|
+
ldx #$00
|
|
839
|
+
bad_loop:
|
|
840
|
+
lda msg, x
|
|
841
|
+
beq bad_done
|
|
842
|
+
jsr $ffd2
|
|
843
|
+
inx
|
|
844
|
+
bne bad_loop
|
|
845
|
+
bad_done:
|
|
846
|
+
rts
|
|
847
|
+
|
|
848
|
+
// GOOD: bank the KERNAL in, re-initialise I/O and the screen editor,
|
|
849
|
+
// clear the one flag CINT leaves alone. Measured: prints under both.
|
|
850
|
+
entry_good:
|
|
851
|
+
lda #$37
|
|
852
|
+
sta $01 // KERNAL, BASIC and I/O back in the map
|
|
853
|
+
jsr $ff84 // IOINIT: CIAs, timers, $01 direction bits
|
|
854
|
+
jsr $ff81 // CINT: editor pointers, $99/$9A, clears screen
|
|
855
|
+
lda #$00
|
|
856
|
+
sta $c7 // reverse-video flag is not CINT's to clear
|
|
857
|
+
ldx #$00
|
|
858
|
+
good_loop:
|
|
859
|
+
lda msg, x
|
|
860
|
+
beq good_done
|
|
861
|
+
jsr $ffd2
|
|
862
|
+
inx
|
|
863
|
+
bne good_loop
|
|
864
|
+
good_done:
|
|
865
|
+
rts
|
|
866
|
+
|
|
867
|
+
msg:
|
|
868
|
+
.text "DECRUNCH OK"
|
|
869
|
+
.byte $0d, $00
|
|
870
|
+
```
|
|
871
|
+
|
|
872
|
+
The diff that names the bytes, from the bitfire run (address, value in
|
|
873
|
+
the uncrunched build, value at entry after decrunch; the lines the print
|
|
874
|
+
path reads, out of 201 that changed):
|
|
875
|
+
|
|
876
|
+
```text
|
|
877
|
+
01: 37 > 37 port: $38 during the decrunch, DEC $01 before the jump; $34 under Dali --small
|
|
878
|
+
99: 00 > 00 DFLTN untouched
|
|
879
|
+
9a: 03 > a6 DFLTO output device is now "serial device $A6"
|
|
880
|
+
c7: 00 > 85 RVS reverse video on
|
|
881
|
+
d1: 18 > ba PNT lo screen line pointer was $0518 (row 7), now $4CBA
|
|
882
|
+
d2: 05 > 4c PNT hi
|
|
883
|
+
d3: 00 > 0d column 13
|
|
884
|
+
d4: 00 > 08 quote-mode flag set
|
|
885
|
+
```
|
|
886
|
+
|
|
887
|
+
### Cross-references
|
|
888
|
+
|
|
889
|
+
- Technique `zx0_lzsa_decrunchers`: the stubs' layout, the copy loop's span (`$EC` and `$D4` down to `$01`) and the measured footprints.
|
|
890
|
+
- Technique `pucrunch_decruncher`: a decruncher that sits at `$F7` and up and leaves the KERNAL's variables alone.
|
|
891
|
+
- Technique `doynax_decruncher` (loaders-packers.md): a depacker at `$00C2` and up; its saving behaviour is not measured here.
|
|
892
|
+
- Technique `cpu_io_port_bank`: the `$01` values; `$34` is the all-RAM map the stubs decrunch under.
|
|
893
|
+
- Technique `memory_layout_plan`: where the payload's own zero-page claims should be written down, so the stub's span is checked against them.
|
|
894
|
+
- Pitfall `ram_under_rom_traps`: the other consequence of a `$01` left at `$34`, reads that return RAM where ROM was expected.
|
|
895
|
+
- Hardware `c64-memory-map.md`: `$0099`/`$009A` (DFLTN/DFLTO), `$00C7`, `$00D1`-`$00D4`.
|
|
896
|
+
|
|
897
|
+
---
|
|
898
|
+
|
|
899
|
+
## cartridge_bank_switch_under_executing_pc — A bank or mode write executed from the window it switches
|
|
900
|
+
|
|
901
|
+
**Severity:** high
|
|
902
|
+
**Region:** both
|
|
903
|
+
**Triggered by techniques:** cartridge_bank_easyflash
|
|
904
|
+
|
|
905
|
+
`$DE00` and `$DE02` are not Register nodes in this knowledge base (the
|
|
906
|
+
hardware pages carry `$DE00-$DEFF` as a memory region, not as registers),
|
|
907
|
+
so this entry anchors on the technique alone.
|
|
908
|
+
|
|
909
|
+
### Symptom
|
|
910
|
+
|
|
911
|
+
The first bank switch of an EasyFlash program is also its last sane
|
|
912
|
+
instruction. Code that runs to the `STA $DE00` and then crashes, or runs a
|
|
913
|
+
routine nobody called, or leaves the screen and border colours the previous
|
|
914
|
+
bank's code would never have chosen. The same routine works when the test
|
|
915
|
+
build copies it to RAM and runs it there, and fails again the moment it is
|
|
916
|
+
run from the cartridge. Debugging is confusing because the store itself is
|
|
917
|
+
fine: a monitor confirms the bank register took the value.
|
|
918
|
+
|
|
919
|
+
The mode-register form is worse. A `STA $DE02` written in bank 0 HIROM at
|
|
920
|
+
`$E000` (where the cartridge boots, in Ultimax mode) swaps the KERNAL ROM in
|
|
921
|
+
under the program counter, and the CPU runs the KERNAL's floating-point
|
|
922
|
+
code from wherever the PC happened to be.
|
|
923
|
+
|
|
924
|
+
### Mechanism
|
|
925
|
+
|
|
926
|
+
The 6510 does not know a bank switch happened. `STA $DE00` is four cycles:
|
|
927
|
+
opcode, operand low, operand high, write. The cartridge latches the new bank
|
|
928
|
+
number on the write, which is the instruction's last cycle, so the store
|
|
929
|
+
always completes and the accumulator keeps its value. The very next cycle is
|
|
930
|
+
the next opcode fetch, at the next address, and if that address is inside
|
|
931
|
+
`$8000-$9FFF` (or `$A000-$BFFF` in 16 KB mode) the fetch is served by the
|
|
932
|
+
NEW bank. Whatever bytes the new bank holds at that offset are executed as
|
|
933
|
+
code. Nothing is skipped and nothing is delayed by an instruction: the
|
|
934
|
+
switch lands on the first fetch after the write.
|
|
935
|
+
|
|
936
|
+
Measured in VICE x64sc 3.10 (windowless build, PAL and NTSC, same bytes in
|
|
937
|
+
both) with a two-bank type `$0020` EasyFlash `.crt`. Bank 0 LOROM at `$8000`
|
|
938
|
+
held `LDX #0 / LDA #1 / STA $DE00 / LDA #$41 / STA $0400 / STA $0401 / STX $0402 / INC $D020`.
|
|
939
|
+
Bank 1 LOROM held `$FF` bytes up to `$8006` and, at `$8007`, `LDX #$42 /
|
|
940
|
+
STX $0400 / STA $0401 / STX $0402 / LDA #6 / STA $D020`. The monitor trace
|
|
941
|
+
(`trace exec 8000 8020`, which prints the bytes actually fetched):
|
|
942
|
+
|
|
943
|
+
```text
|
|
944
|
+
.C:8004 8D 00 DE STA $DE00 - A:01 X:00 cycle 193
|
|
945
|
+
.C:8007 A2 42 LDX #$42 - A:01 X:00 cycle 197 <- bank 1's bytes
|
|
946
|
+
.C:8009 8E 00 04 STX $0400 - A:01 X:42 cycle 199
|
|
947
|
+
```
|
|
948
|
+
|
|
949
|
+
The store began at cycle 193, wrote on cycle 196 (arithmetic, four-cycle
|
|
950
|
+
STA), and the opcode fetched at cycle 197 was `$A2`, bank 1's byte, not the
|
|
951
|
+
`$A9` that bank 0 holds at `$8007`. Final memory, from `m 0400 0403` and
|
|
952
|
+
`m d020 d020` on the last store:
|
|
953
|
+
|
|
954
|
+
```text
|
|
955
|
+
run $0400 $0401 $0402 $D020 border in the screenshot
|
|
956
|
+
BAD: switch from $8004 42 01 42 F6 (blue) (44, 61, 236)
|
|
957
|
+
CONTROL: STA $DE00 with #0 41 41 00 FF (grey) (205, 205, 205)
|
|
958
|
+
FIX: sequence run from $0200 41 41 00 FF (grey) (205, 205, 205)
|
|
959
|
+
FIX: bank 1 identical here 41 41 00 FF (grey) (205, 205, 205)
|
|
960
|
+
```
|
|
961
|
+
|
|
962
|
+
`$0400` never reads `$41` in the bad build: the `LDA #$41` that follows the
|
|
963
|
+
store in bank 0 was never fetched. `$0401` reads `$01`, the accumulator as
|
|
964
|
+
the STA left it, so the write itself was whole. The control build writes
|
|
965
|
+
bank 0 back into `$DE00` and reads `$41`, `$41`, `$00`, which is the same
|
|
966
|
+
instruction stream with the same store and no switch, so the fault is the
|
|
967
|
+
switch and not the store. (`$D020` reads with its upper nibble set, `$F6`
|
|
968
|
+
for blue and `$FF` for `$0F`, light grey.)
|
|
969
|
+
|
|
970
|
+
The mode write was measured the same way. Bank 0 HIROM at `$E022` ran
|
|
971
|
+
`LDA #$07 / STA $DE02 / LDA #$41 / STA $0400 / INC $D020` from Ultimax mode:
|
|
972
|
+
|
|
973
|
+
```text
|
|
974
|
+
.C:e024 8D 02 DE STA $DE02 - A:07 X:00 cycle 52
|
|
975
|
+
.C:e027 10 F5 BPL $E01E - A:07 X:00 cycle 56 <- KERNAL's bytes
|
|
976
|
+
.C:e026 CA DEX
|
|
977
|
+
.C:e029 A5 56 LDA $56
|
|
978
|
+
.C:e02d 20 53 B8 JSR $B853
|
|
979
|
+
```
|
|
980
|
+
|
|
981
|
+
The bytes fetched from `$E027` onwards (`10 F5 ... A5 56 85 70 20 53 B8`)
|
|
982
|
+
are the KERNAL ROM's (901227-03, offsets `$0027-$002F`), not the
|
|
983
|
+
cartridge's. `$0400` stayed `$00` and the border stayed light blue: nothing
|
|
984
|
+
after the store in HIROM ever ran.
|
|
985
|
+
|
|
986
|
+
The whole of the pitfall is therefore in the bytes at the ADDRESS AFTER the
|
|
987
|
+
store. Two things make it safe: the code after the store lives in RAM, which
|
|
988
|
+
no bank register touches; or every bank that can be selected carries the
|
|
989
|
+
same bytes at that address, so it does not matter which one serves the
|
|
990
|
+
fetch. The fix build that made bank 1 a byte-for-byte copy of bank 0 at the
|
|
991
|
+
switch site read `$41` like the RAM build, and its trace shows `A9 41` at
|
|
992
|
+
`$8007` served by bank 1.
|
|
993
|
+
|
|
994
|
+
### Fix
|
|
995
|
+
|
|
996
|
+
Run the switch from RAM. Copy a stub of `LDA #bank / STA $DE00 / JMP entry`
|
|
997
|
+
to a page the cartridge does not map (`$0200`, or the `$DF00` cartridge RAM
|
|
998
|
+
if the program already uses it), jump to the stub, and let it jump into the
|
|
999
|
+
new bank at an address the new bank guarantees is code. Do the same for
|
|
1000
|
+
`$DE02`: the EasyFlash start-up stub in the recipe copies its main code to
|
|
1001
|
+
RAM before it writes `$07`, for this reason.
|
|
1002
|
+
|
|
1003
|
+
If the switch must stay in ROM, put the switch routine at the same offset in
|
|
1004
|
+
every bank, byte for byte, in the linker or the assembler's segment layout,
|
|
1005
|
+
and check that claim with a byte compare of the built `.crt` rather than by
|
|
1006
|
+
reading the source. A common shape is a small shared trampoline in the top
|
|
1007
|
+
page of LOROM present in all banks.
|
|
1008
|
+
|
|
1009
|
+
Never put the store at the end of a routine and rely on the `RTS` after it:
|
|
1010
|
+
the `RTS` is fetched from the new bank too. The same holds for a subroutine
|
|
1011
|
+
in RAM that switches banks and returns to a caller in ROM: the return
|
|
1012
|
+
address is in the old bank's code, and the new bank's bytes are there now
|
|
1013
|
+
(not measured here; it follows from the fetch order above).
|
|
1014
|
+
|
|
1015
|
+
### Worked example
|
|
1016
|
+
|
|
1017
|
+
```kick
|
|
1018
|
+
// BAD: the switch runs from the LOROM it switches. Measured: after the
|
|
1019
|
+
// STA the next opcode fetch, at switch_from_rom+5, already comes from
|
|
1020
|
+
// bank 1, and the LDA #$41 below is never executed.
|
|
1021
|
+
switch_from_rom:
|
|
1022
|
+
lda #$01
|
|
1023
|
+
sta $de00 // latches bank 1 on this instruction's last cycle
|
|
1024
|
+
lda #$41 // bank 1's byte at this address runs instead
|
|
1025
|
+
sta $0400
|
|
1026
|
+
rts
|
|
1027
|
+
|
|
1028
|
+
// GOOD: copy the switch and the entry jump to RAM and run them there.
|
|
1029
|
+
// Measured: $0400 reads $41 with the same two banks in the cartridge.
|
|
1030
|
+
switch_from_ram:
|
|
1031
|
+
ldx #$00
|
|
1032
|
+
copy_stub:
|
|
1033
|
+
lda stub, x
|
|
1034
|
+
sta $0200, x
|
|
1035
|
+
inx
|
|
1036
|
+
cpx #stub_end - stub
|
|
1037
|
+
bne copy_stub
|
|
1038
|
+
jmp $0200
|
|
1039
|
+
stub:
|
|
1040
|
+
.pseudopc $0200 {
|
|
1041
|
+
lda #$01
|
|
1042
|
+
sta $de00 // fetched from RAM: what $8000 shows no longer matters
|
|
1043
|
+
jmp $8000 // an address that is code in bank 1
|
|
1044
|
+
}
|
|
1045
|
+
stub_end:
|
|
1046
|
+
```
|
|
1047
|
+
|
|
1048
|
+
The alternative fix is not code but layout: the bytes at the switch site
|
|
1049
|
+
must be the same in every bank. That is a property of the built `.crt`, so
|
|
1050
|
+
check it there.
|
|
1051
|
+
|
|
1052
|
+
### Cross-references
|
|
1053
|
+
|
|
1054
|
+
- Technique `cartridge_bank_easyflash` (techniques/memory-banking.md): the
|
|
1055
|
+
bank and mode registers, the Ultimax boot, and its Cycle budget section,
|
|
1056
|
+
which states this hazard; the measurement here is the one that section
|
|
1057
|
+
did not have.
|
|
1058
|
+
- Recipe `recipes/kickassembler/easyflash-save.md`: a boot stub that copies
|
|
1059
|
+
its main code to `$0800` before writing `$DE02`, and a two-bank `.crt`
|
|
1060
|
+
emitted from one listing (the rig above was built the same way).
|
|
1061
|
+
- Technique `cartridge_save` (techniques/file-io.md): keeps its flash
|
|
1062
|
+
writing code in RAM below `$1000` because Ultimax mode maps nothing else,
|
|
1063
|
+
which also keeps it clear of this pitfall; it is not on the Triggered-by
|
|
1064
|
+
line because its text never switches banks from cartridge code.
|
|
1065
|
+
- Pitfall `ram_under_rom_traps`: the other direction of the same PLA rule,
|
|
1066
|
+
a write that reaches the RAM under `$8000-$BFFF` while a read returns the
|
|
1067
|
+
cartridge byte.
|