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,874 @@
|
|
|
1
|
+
---
|
|
2
|
+
category: cpu
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
<!-- doc-type: pitfall-reference -->
|
|
6
|
+
|
|
7
|
+
# CPU Pitfalls
|
|
8
|
+
|
|
9
|
+
Pitfalls in the 6510's own behaviour, and in what a toolchain does to code
|
|
10
|
+
that leans on it: behaviour that is locally correct but globally surprising,
|
|
11
|
+
such as a branch cycle count that changes with binary placement, illegal
|
|
12
|
+
opcodes that disappear on CMOS silicon, an indirect-jump address fetch that
|
|
13
|
+
wraps at page boundaries, a signed compare that turns over, an LFSR that
|
|
14
|
+
never leaves zero, and an assembler optimiser that separates a patch from
|
|
15
|
+
the instruction it patches. Each has caused cycle-tight and portable C64
|
|
16
|
+
code to fail in production. (An earlier version of this paragraph counted
|
|
17
|
+
three.)
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## branch_page_cross_extra_cycle — Conditional branch costs +1 cycle when crossing a page boundary
|
|
22
|
+
|
|
23
|
+
**Severity:** high
|
|
24
|
+
**Region:** both
|
|
25
|
+
**Triggered by techniques:** stable_raster_irq, self_modifying_code, unrolled_loops, double_irq, sideborder_open, fli_image, charset_copy_rom_to_ram, isqrt_16bit, atan2_8bit, bresenham_line, zero_page_burst
|
|
26
|
+
**Mitigated by techniques:** bit_test_trick
|
|
27
|
+
|
|
28
|
+
### Symptom
|
|
29
|
+
|
|
30
|
+
Cycle-counted raster IRQ code drifts by exactly one cycle depending on where
|
|
31
|
+
the linker places the assembled output. Color bars that were pixel-perfect in
|
|
32
|
+
development land on the wrong line in the final build. A stable-raster handler
|
|
33
|
+
that was verified working at $2000 breaks when the code is relocated to $2FF0
|
|
34
|
+
and a branch straddles the $3000 boundary. The symptom is a one-cycle jitter
|
|
35
|
+
that is completely reproducible — it is not random, it just changes with the
|
|
36
|
+
binary's load address.
|
|
37
|
+
|
|
38
|
+
### Mechanism
|
|
39
|
+
|
|
40
|
+
All conditional branches on the 6510 — BCC, BCS, BEQ, BMI, BNE, BPL, BVC,
|
|
41
|
+
BVS — use the relative addressing mode. The branch instruction is 2 bytes
|
|
42
|
+
(opcode + signed displacement). Cycle cost follows three cases:
|
|
43
|
+
|
|
44
|
+
| Outcome | Cycles |
|
|
45
|
+
|---------|--------|
|
|
46
|
+
| Branch not taken | 2 |
|
|
47
|
+
| Branch taken, target on same page as the instruction after the branch | 3 |
|
|
48
|
+
| Branch taken, target on a different page than the instruction after the branch | 4 |
|
|
49
|
+
|
|
50
|
+
The "page" check compares the high byte of `PC + 2` (where the CPU is after
|
|
51
|
+
fetching the 2-byte branch instruction) with the high byte of the branch target.
|
|
52
|
+
If they differ, the CPU performs an extra internal cycle to fix up the high byte
|
|
53
|
+
of the program counter — a "page-crossing penalty."
|
|
54
|
+
|
|
55
|
+
For code that does not need cycle precision, this is irrelevant — one extra
|
|
56
|
+
cycle out of thousands is noise. For stable-raster IRQ handlers, per-line
|
|
57
|
+
raster effects, or sprite multiplexers where the cycle budget per scanline is
|
|
58
|
+
counted in single digits, a one-cycle slip means the write to `$D020` or
|
|
59
|
+
`$D001` lands on the wrong scanline. Because the penalty depends on the
|
|
60
|
+
branch target's page rather than any runtime value, the same source assembles
|
|
61
|
+
to different cycle counts depending on the `.o` file's placement in the binary.
|
|
62
|
+
|
|
63
|
+
The pitfall is most acute in routines that loop across page boundaries. A
|
|
64
|
+
`BNE loop` at `$20FD` branching back into `$20xx` costs 3 cycles. The
|
|
65
|
+
identical instruction at `$20FF` branching to the same target costs 4 cycles
|
|
66
|
+
because `$20FF + 2 = $2101`, whose high byte differs from `$20xx`.
|
|
67
|
+
|
|
68
|
+
### Fix
|
|
69
|
+
|
|
70
|
+
**Alignment.** The standard fix for KickAssembler is a `.align $100` directive
|
|
71
|
+
before the branch target. This forces the target to the start of the next
|
|
72
|
+
page, guaranteeing that the branch and its target share the same high byte for
|
|
73
|
+
any branch instruction within the first 127 bytes of that page:
|
|
74
|
+
|
|
75
|
+
```kick
|
|
76
|
+
.align $100 // force raster_loop to $xx00
|
|
77
|
+
raster_loop:
|
|
78
|
+
lda $d012 // 4 cycles (absolute read)
|
|
79
|
+
cmp #TARGET // 2 cycles
|
|
80
|
+
bne raster_loop // 3 cycles — same page guaranteed by .align
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
**Eliminate the branch.** Branchless equivalents remove the variable entirely.
|
|
84
|
+
Tight polling loops can be replaced with NOP chains (`NOP` = 2 cycles; the
|
|
85
|
+
undocumented one-byte NOPs $1A/$3A/$5A are also 2 cycles and 1 byte, so they
|
|
86
|
+
buy nothing over `NOP` except a CMOS incompatibility — an earlier version of
|
|
87
|
+
this sentence recommended them; on a 65816 $1A/$3A/$5A are `INC A`/`DEC A`/
|
|
88
|
+
`PHY`, measured in xscpu64, so the third one corrupts the stack — see
|
|
89
|
+
illegal_opcode_portability below. For a 3-cycle pad use the
|
|
90
|
+
legal `bit zp`, or `nop zp` ($04) if the flags must survive — noting that $04
|
|
91
|
+
is itself undocumented and carries the same portability caveat; see the
|
|
92
|
+
padding section of `docs/hardware/6502-illegal-opcodes.md`) or the `BIT $abs`
|
|
93
|
+
skip trick. A spin-wait with `DEC zp / BNE` that straddles a page boundary
|
|
94
|
+
can be replaced with an unrolled NOP sequence calibrated at link time.
|
|
95
|
+
|
|
96
|
+
**Empirical rule for stable raster IRQs.** Place the entire timing-critical
|
|
97
|
+
section in a 256-byte aligned region using `.align $100`. Two separate
|
|
98
|
+
constraints apply. Page: a taken branch pays the +1 only when the high byte of
|
|
99
|
+
PC+2 differs from the target's, so inside a page-aligned section a branch to a
|
|
100
|
+
target in that section crosses only if its opcode sits on the page's last two
|
|
101
|
+
bytes (offset $FE or $FF) — a branch at offset $F0 back to offset $80 still
|
|
102
|
+
costs 3. Range: the displacement is -128..+127 from PC+2, so a backward branch
|
|
103
|
+
to the aligned start can sit no further than offset 126. Keeping the whole
|
|
104
|
+
section under 128 bytes satisfies both at once, which is why it is a safe rule
|
|
105
|
+
of thumb, but it is the range limit, not the page rule, that the 128 figure
|
|
106
|
+
comes from (an earlier version of this paragraph attributed it to the page
|
|
107
|
+
rule; measured in x64sc: `BNE` at $2FFD to $3000 costs 4, at $20FD back to
|
|
108
|
+
$20EE costs 3, at $21FF back to $21D0 costs 4). Verify the final cycle count
|
|
109
|
+
in VICE x64sc using the `cpuhistory` monitor command.
|
|
110
|
+
|
|
111
|
+
### Worked example
|
|
112
|
+
|
|
113
|
+
```kick
|
|
114
|
+
// BAD: branch target may or may not be on the same page as the BNE
|
|
115
|
+
// Cycle cost of BNE depends on the binary's load address — unpredictable.
|
|
116
|
+
|
|
117
|
+
stable_raster_entry:
|
|
118
|
+
pha
|
|
119
|
+
txa
|
|
120
|
+
pha
|
|
121
|
+
tya
|
|
122
|
+
pha
|
|
123
|
+
|
|
124
|
+
// Double-IRQ jitter removal loop
|
|
125
|
+
// If this loop straddles a page boundary, the BNE costs 4 cycles on
|
|
126
|
+
// some runs and 3 on others — raster hits the wrong line.
|
|
127
|
+
jitter_loop:
|
|
128
|
+
lda $d012 // 4 cycles
|
|
129
|
+
cmp #TARGET_LINE // 2 cycles
|
|
130
|
+
bne jitter_loop // 3 OR 4 cycles — page-dependent
|
|
131
|
+
|
|
132
|
+
lda #BLACK
|
|
133
|
+
sta $d020 // Lands on wrong line if BNE cost was 4 not 3
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
// GOOD: force both jitter_loop and the BNE target to the same page.
|
|
137
|
+
|
|
138
|
+
.align $100
|
|
139
|
+
stable_raster_entry_fixed:
|
|
140
|
+
pha
|
|
141
|
+
txa
|
|
142
|
+
pha
|
|
143
|
+
tya
|
|
144
|
+
pha
|
|
145
|
+
|
|
146
|
+
jitter_loop_fixed:
|
|
147
|
+
lda $d012 // 4 cycles
|
|
148
|
+
cmp #TARGET_LINE // 2 cycles
|
|
149
|
+
bne jitter_loop_fixed // Always 3 cycles — same page guaranteed
|
|
150
|
+
|
|
151
|
+
lda #BLACK
|
|
152
|
+
sta $d020 // Arrives on the correct scanline, every time
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Cross-references
|
|
156
|
+
|
|
157
|
+
- Technique `stable_raster_irq` (`docs/techniques/raster.md`) — the double-IRQ
|
|
158
|
+
jitter removal loop where this pitfall most commonly strikes
|
|
159
|
+
- Technique `self_modifying_code` (`docs/techniques/cpu-cycle-tricks.md`) —
|
|
160
|
+
SMC blocks with tight backward branches need page-aligned placement
|
|
161
|
+
- Technique `unrolled_loops` (`docs/techniques/cpu-cycle-tricks.md`) — remaining
|
|
162
|
+
loop-exit branches must be checked for page-crossing after unrolling
|
|
163
|
+
- Register `D012` — the raster compare register read in every polling loop
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
## illegal_opcode_portability — Illegal opcodes behave correctly on NMOS 6510 but break on CMOS and SuperCPU targets
|
|
168
|
+
|
|
169
|
+
**Severity:** medium
|
|
170
|
+
**Region:** both
|
|
171
|
+
**Triggered by techniques:** illegal_opcode_tricks, bit_test_trick
|
|
172
|
+
|
|
173
|
+
### Symptom
|
|
174
|
+
|
|
175
|
+
Code using LAX, SAX, AXS, ALR, ARR, DCP, or the RMW family works perfectly on
|
|
176
|
+
real C64 hardware and under VICE. The same code loaded into a SuperCPU-equipped
|
|
177
|
+
machine executes differently or hangs. A sim6502 unit test in strict CMOS mode
|
|
178
|
+
fails on every illegal opcode. An assembler in 65C02 mode refuses the
|
|
179
|
+
mnemonics outright (KickAssembler `.cpu _65c02`: "Pseudo command 'lax' not
|
|
180
|
+
defined"; ca65 `.setcpu "65C02"`: error) — but a routine emitted with `.byte`
|
|
181
|
+
sails through and mis-executes on the CMOS part with no error at all. (An
|
|
182
|
+
earlier version of this sentence said the assembler silently treats the bytes
|
|
183
|
+
as NOPs; it does not — measured with KickAssembler 5.25 and ca65.) The
|
|
184
|
+
assumption that the target CPU is NMOS was never documented.
|
|
185
|
+
|
|
186
|
+
### Mechanism
|
|
187
|
+
|
|
188
|
+
Illegal opcodes arise from the NMOS 6502 decode matrix. Each opcode byte selects
|
|
189
|
+
a column (addressing mode) and a row (operation). The "illegal" cells are
|
|
190
|
+
undefined combinations where two operation strobes activate simultaneously —
|
|
191
|
+
producing fused instructions that the MOS engineers never designed but that the
|
|
192
|
+
silicon executes deterministically. The safe tier (LAX, SAX, ANC, ALR, ARR,
|
|
193
|
+
AXS, DCP, ISC, SLO, RLA, SRE, RRA) is consistent across every NMOS 6502 and
|
|
194
|
+
6510 ever manufactured.
|
|
195
|
+
|
|
196
|
+
The portability boundary is the NMOS / CMOS divide:
|
|
197
|
+
|
|
198
|
+
**65C02 (CMOS revision):** All undefined opcode slots were deliberately filled
|
|
199
|
+
with explicit NOPs of varying byte lengths and cycle counts. The decode matrix
|
|
200
|
+
was redesigned. On a 65C02 the byte $A7 (LAX zero-page) is either a NOP of
|
|
201
|
+
vendor-specific length or, on Rockwell/WDC parts, a bit-manipulation
|
|
202
|
+
instruction (the $x7 column is SMB/RMB there); either way A and X are not
|
|
203
|
+
loaded and the program continues from the wrong state. (An earlier version of
|
|
204
|
+
this paragraph stated flatly "a 2-cycle 2-byte NOP"; that varies by 65C02
|
|
205
|
+
vendor and was not measured here.) On the 6510, $A7 executes `A = X = M[zp]`
|
|
206
|
+
— a completely different side effect.
|
|
207
|
+
|
|
208
|
+
**65816 (WDC 16-bit extension, used in SuperCPU):** The SuperCPU accelerator for
|
|
209
|
+
the C64 fits a 65816 CPU and runs C64 code in emulation mode. The 65816 has no
|
|
210
|
+
undefined opcodes: every NMOS illegal-opcode byte is a live 65816 instruction
|
|
211
|
+
with its own memory, stack or register side effects, not a silent NOP. (An
|
|
212
|
+
earlier version of this paragraph and the table below said these bytes execute
|
|
213
|
+
as NOPs on the SuperCPU; measured in xscpu64, VICE 3.10: $A7 $F0 loaded A
|
|
214
|
+
through the 24-bit pointer at $F0 and left X unchanged, $87 $F0 stored A
|
|
215
|
+
through that pointer, $C7 $F0 compared without decrementing, $4B pushed one
|
|
216
|
+
byte, $1A incremented A, $EB swapped A with B.) Code that runs identically on
|
|
217
|
+
6510 and 8500 will misbehave on SuperCPU.
|
|
218
|
+
|
|
219
|
+
**8500 (late C64 and C64C):** The 8500 is the same NMOS microarchitecture as the
|
|
220
|
+
6510, shrunk to a smaller process. The safe illegal opcodes behave identically
|
|
221
|
+
to the 6510. This part is fine.
|
|
222
|
+
|
|
223
|
+
**VICE default mode (x64sc):** VICE in its default cycle-exact mode (x64sc)
|
|
224
|
+
implements the safe NMOS illegal opcodes faithfully. Development under VICE is
|
|
225
|
+
not sufficient to catch CMOS portability issues — VICE matches the real 6510.
|
|
226
|
+
|
|
227
|
+
**sim6502:** Behavior depends on the configuration. sim6502 may run in either
|
|
228
|
+
NMOS or strict mode. Code that relies on illegal opcodes must be tested under the
|
|
229
|
+
same configuration that the project ships for.
|
|
230
|
+
|
|
231
|
+
The second portability concern is the **unstable tier:** XAA (ANE), LAX #imm
|
|
232
|
+
($AB), AHX, TAS, SHX, SHY. These involve a floating internal bus whose value
|
|
233
|
+
varies by chip revision and thermal state. They must never appear in shipping
|
|
234
|
+
code.
|
|
235
|
+
|
|
236
|
+
| Opcode | NMOS 6510 | 65C02 (varies by vendor; not measured here) | 65816 / SuperCPU (xscpu64 unless marked) | Notes |
|
|
237
|
+
|--------|-----------|------------------|------------------|-------|
|
|
238
|
+
| LAX zp ($A7) | A=X=M | NOP or SMB/RMB | `LDA [dp]` — 24-bit pointer read, X untouched (measured) | Safe NMOS only |
|
|
239
|
+
| SAX zp ($87) | M=A&X | NOP or SMB/RMB | `STA [dp]` — writes A through a 24-bit pointer (measured) | Safe NMOS only |
|
|
240
|
+
| ALR #imm ($4B) | A=(A&imm)>>1 | NOP | `PHK` — pushes one byte (measured) | Safe NMOS only |
|
|
241
|
+
| ARR #imm ($6B) | A=ROR(A&imm) quirky flags | NOP | `RTL` — pops a 24-bit return address and jumps there (measured) | Safe NMOS only |
|
|
242
|
+
| AXS #imm ($CB) | X=(A&X)-imm | NOP | `WAI` — stalls until the next IRQ/NMI assertion, even with I set; permanent only if no source is running (measured: 58-line stall) | Safe NMOS only |
|
|
243
|
+
| DCP zp ($C7) | M--; CMP A,M | NOP or SMB/RMB | `CMP [dp]` — compares, no decrement (measured) | Safe NMOS only |
|
|
244
|
+
| SLO/RLA/SRE/RRA zp ($07/$27/$47/$67) | RMW+combine | NOP or RMB | `ORA [dp]` / `AND [dp]` / `EOR [dp]` / `ADC [dp]` — plain reads through a 24-bit pointer, no RMW ($07 measured; the other three from the opcode map) | Safe NMOS only |
|
|
245
|
+
| XAA #imm ($8B) | unstable | NOP | `PHB` — pushes the data bank (measured) | **NEVER USE** |
|
|
246
|
+
| LAX #imm ($AB) | unstable | NOP | `PLB` — pulls one byte into the data bank, A untouched (measured) | **NEVER USE** |
|
|
247
|
+
|
|
248
|
+
The table's third column used to read "NOP" for every row under a single
|
|
249
|
+
"65C02 / SuperCPU" heading; the SuperCPU column was wrong in every row, and the
|
|
250
|
+
65C02 column depends on which vendor's part is fitted. The one-byte NOPs are
|
|
251
|
+
not exempt either: $1A is `INC A` and $EB is `XBA` on the 65816 (measured).
|
|
252
|
+
|
|
253
|
+
### Fix
|
|
254
|
+
|
|
255
|
+
**Document the dependency.** At minimum, add a file-level comment to any source
|
|
256
|
+
file that uses illegal opcodes. The comment should state which opcodes are used,
|
|
257
|
+
why, and that the code requires a stock C64 NMOS 6510 or 8500 CPU:
|
|
258
|
+
|
|
259
|
+
```kick
|
|
260
|
+
// This file uses LAX ($A7), DCP ($C7), and SLO ($07) for cycle savings
|
|
261
|
+
// in the sprite multiplexer inner loop. These are NMOS-only opcodes.
|
|
262
|
+
// They work on: 6510 (C64), 8500 (C64C), 8502 (C128 native mode).
|
|
263
|
+
// They misbehave on: SuperCPU (65816 — each byte is a live instruction
|
|
264
|
+
// with side effects), any CMOS board, 65C02.
|
|
265
|
+
// Verify under x64sc (VICE) before shipping. Do not run in sim6502 strict mode.
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
**Verify in the target emulator.** If the project uses sim6502 for unit tests,
|
|
269
|
+
confirm that the sim6502 configuration matches NMOS behavior. If it does not,
|
|
270
|
+
either add a separate test harness for the illegal-opcode routines that runs
|
|
271
|
+
under VICE, or provide legal fallback paths for the test environment.
|
|
272
|
+
|
|
273
|
+
**Replace with legal equivalents when portability matters.** The cost is 1–3
|
|
274
|
+
extra cycles and 1–2 extra bytes per site. See `illegal_opcode_tricks` in
|
|
275
|
+
`cpu-cycle-tricks.md` for the equivalent legal sequence for each opcode.
|
|
276
|
+
|
|
277
|
+
### Worked example
|
|
278
|
+
|
|
279
|
+
```kick
|
|
280
|
+
// BAD: uses LAX and DCP without documentation
|
|
281
|
+
// Works on real C64 and VICE. Misbehaves on SuperCPU — the bytes are not
|
|
282
|
+
// skipped, they execute as 65816 instructions with other side effects.
|
|
283
|
+
|
|
284
|
+
multiplex_loop:
|
|
285
|
+
lax sprite_y,y // A = X = sprite_y[y] — NMOS only ($B7: LAX zp,Y; there is no zp,X form)
|
|
286
|
+
dcp compare_y // compare_y-- ; cmp A, new compare_y — NMOS only ($C7: DCP zp)
|
|
287
|
+
bcc multiplex_done
|
|
288
|
+
iny
|
|
289
|
+
bne multiplex_loop
|
|
290
|
+
|
|
291
|
+
multiplex_done:
|
|
292
|
+
sty active_sprites
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
// GOOD: same logic with documentation and legal fallback comments
|
|
296
|
+
|
|
297
|
+
// NMOS-ONLY section: LAX ($B7 zp,Y) and DCP ($C7 zp) used for cycle savings.
|
|
298
|
+
// Tested on: 6510 (real HW), 8500 (real HW), VICE x64sc PAL.
|
|
299
|
+
// Misbehaves on 65C02 and 65816/SuperCPU (see table above).
|
|
300
|
+
|
|
301
|
+
multiplex_loop_nmos:
|
|
302
|
+
lax sprite_y,y // A = X = sprite_y[Y] — $B7 zp,Y, 4 cycles / 2 bytes
|
|
303
|
+
dcp compare_y // compare_y-- ; flags vs A — $C7 zp, 5 cycles / 2 bytes
|
|
304
|
+
bcc multiplex_done_nmos
|
|
305
|
+
iny
|
|
306
|
+
bne multiplex_loop_nmos
|
|
307
|
+
|
|
308
|
+
multiplex_done_nmos:
|
|
309
|
+
sty active_sprites
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
An earlier version of the GOOD listing wrapped both instructions in `.byte`
|
|
313
|
+
macros ("assembler won't accept LAX zp,Y natively") and used $D7 (DCP zp,X)
|
|
314
|
+
where the BAD listing used $C7 (DCP zp) — different addressing, different
|
|
315
|
+
logic, and a 6-cycle count that belonged to the wrong mode. KickAssembler 5.25
|
|
316
|
+
assembles `lax sprite_y,y` to `B7` and `dcp compare_y` to `C7` natively
|
|
317
|
+
(measured), so the macros were unnecessary; worse, `.byte $b7, addr` with a
|
|
318
|
+
non-zero-page `addr` silently truncated the label to its low byte with no
|
|
319
|
+
error. If a `.byte` escape is ever kept, guard it with
|
|
320
|
+
`.errorif addr >= $100, "operand not in zero page"`, not `.assert` — measured
|
|
321
|
+
on KickAssembler 5.25, a failed `.assert` still writes the PRG and exits 0,
|
|
322
|
+
while `.errorif` aborts with exit 1 and no output file.
|
|
323
|
+
|
|
324
|
+
### Cross-references
|
|
325
|
+
|
|
326
|
+
- Technique `illegal_opcode_tricks` (`docs/techniques/cpu-cycle-tricks.md`) —
|
|
327
|
+
per-opcode semantics, cycle counts, and stable vs unstable tier classification
|
|
328
|
+
- Doc `docs/hardware/6502-illegal-opcodes.md` — per-opcode reference with flag
|
|
329
|
+
effects and per-revision availability
|
|
330
|
+
- VICE x64sc — the cycle-exact NMOS-faithful emulator; use as verification platform
|
|
331
|
+
|
|
332
|
+
---
|
|
333
|
+
|
|
334
|
+
## jmp_indirect_page_boundary_bug — JMP ($xxFF) fetches the high byte from $xx00 instead of $(xx+1)00
|
|
335
|
+
|
|
336
|
+
**Severity:** high
|
|
337
|
+
**Region:** both
|
|
338
|
+
**Triggered by techniques:** jump_table_dispatch, basic_extension_wedge
|
|
339
|
+
**Mitigated by techniques:** jump_table_dispatch
|
|
340
|
+
|
|
341
|
+
### Symptom
|
|
342
|
+
|
|
343
|
+
A jump table dispatch jumps to a completely wrong address. Changing the target
|
|
344
|
+
address in the table has no effect. Moving the table by one byte in memory
|
|
345
|
+
makes the bug disappear; moving it back brings it back. Moving it by two does
|
|
346
|
+
not help in general — a 2-byte stride keeps every entry's parity, so the fault
|
|
347
|
+
just shifts to the neighbouring entry (it only clears if that neighbour would
|
|
348
|
+
fall off the end of the table); an earlier version of this sentence said one
|
|
349
|
+
or two bytes. The bug is
|
|
350
|
+
deterministic: it fires whenever a jump table entry's low-byte slot falls at
|
|
351
|
+
an address ending in $FF. The same bug occurs when `JMP ($addr)` is used
|
|
352
|
+
directly and the vector is placed at $xxFF.
|
|
353
|
+
|
|
354
|
+
### Mechanism
|
|
355
|
+
|
|
356
|
+
`JMP ($addr)` (opcode $6C) is the 6510's indirect jump. It takes the 16-bit
|
|
357
|
+
address stored at the 2-byte operand location and jumps there. The fetch works
|
|
358
|
+
as two separate byte reads:
|
|
359
|
+
|
|
360
|
+
1. Low byte of destination: fetched from `$addr`.
|
|
361
|
+
2. High byte of destination: fetched from `$addr + 1`.
|
|
362
|
+
|
|
363
|
+
The 6510 uses an 8-bit adder with no carry propagation for the second-byte
|
|
364
|
+
fetch. The high byte of the address is not incremented when `$addr + 1` would
|
|
365
|
+
cross a page boundary. Concretely:
|
|
366
|
+
|
|
367
|
+
- If `$addr` = $10FE, the low byte is read from $10FE and the high byte from
|
|
368
|
+
$10FF. Correct.
|
|
369
|
+
- If `$addr` = $10FF, the low byte is read from $10FF and the high byte from
|
|
370
|
+
$1000 (not $1100). **Wrong.**
|
|
371
|
+
|
|
372
|
+
The CPU wraps the low byte of the pointer address within the same page. This is
|
|
373
|
+
not a timing issue or an edge case in the address decoder — it is the documented
|
|
374
|
+
behavior of the original 6502 silicon, reproduced faithfully in every NMOS
|
|
375
|
+
6510 and 8500. It is sometimes called the "JMP indirect page-wrap bug" or the
|
|
376
|
+
"6502 JMP indirect bug."
|
|
377
|
+
|
|
378
|
+
The bug surfaces whenever an entry's low-byte slot lands at $xxFF. With a
|
|
379
|
+
page-aligned table at $xx00 and 2-byte strides, all slots are at even offsets
|
|
380
|
+
from $xx00 and none land at $xxFF. The only dangerous placement is when the
|
|
381
|
+
assembler places the table at an offset that puts any entry's low-byte at an
|
|
382
|
+
address ending in $FF.
|
|
383
|
+
|
|
384
|
+
`jump_table_dispatch` is on both metadata lines above for that reason: the
|
|
385
|
+
bug arises in the naive form of the technique, a `JMP ($abs)` through an
|
|
386
|
+
unaligned table, and the technique's store-then-jump form (self-modified
|
|
387
|
+
`JMP $abs`, or a page-aligned table) is what sidesteps it.
|
|
388
|
+
|
|
389
|
+
**CMOS note:** The 65C02 corrects this bug — it always fetches the high byte
|
|
390
|
+
from `$addr + 1` with correct carry. Code targeting both 6510 and 65C02 must
|
|
391
|
+
still avoid $xxFF placement to be safe on the 6510.
|
|
392
|
+
|
|
393
|
+
### Fix
|
|
394
|
+
|
|
395
|
+
**Never place a JMP indirect vector or jump table entry at a $xxFF address.**
|
|
396
|
+
|
|
397
|
+
The safest approach is page-alignment. The bug fires when the low-byte slot
|
|
398
|
+
of a jump table entry is at an address ending in $FF — i.e., when
|
|
399
|
+
`table_base + (2 * index)` == $xxFF. For a page-aligned table at $xx00,
|
|
400
|
+
entries land at $xx00, $xx02, $xx04, ... $xxFE — all even, none at $xxFF.
|
|
401
|
+
Page alignment with a 2-byte stride is a complete fix.
|
|
402
|
+
|
|
403
|
+
```kick
|
|
404
|
+
// FIXED: page-aligned jump table, no entry at $xxFF
|
|
405
|
+
|
|
406
|
+
.align $100
|
|
407
|
+
dispatch_table:
|
|
408
|
+
.word handler_0 // vector at $xx00 — safe
|
|
409
|
+
.word handler_1 // vector at $xx02 — safe
|
|
410
|
+
.word handler_2 // vector at $xx04 — safe
|
|
411
|
+
// ... entries continue at $xx06, $xx08, ... $xxFE — all safe
|
|
412
|
+
|
|
413
|
+
dispatch:
|
|
414
|
+
lda current_state
|
|
415
|
+
asl
|
|
416
|
+
tax
|
|
417
|
+
lda dispatch_table,x
|
|
418
|
+
sta jmp_indir+1
|
|
419
|
+
lda dispatch_table+1,x
|
|
420
|
+
sta jmp_indir+2
|
|
421
|
+
jmp_indir:
|
|
422
|
+
jmp $0000 // Self-modified; avoids JMP ($abs) entirely
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
The example above uses a self-modified `JMP $abs` rather than `JMP ($abs)`.
|
|
426
|
+
The self-modified form is immune to the page-wrap bug because it uses the
|
|
427
|
+
absolute addressing mode ($4C), not the indirect mode ($6C). The two-step
|
|
428
|
+
store-then-jump pattern from `jump_table_dispatch` in `cpu-cycle-tricks.md`
|
|
429
|
+
sidesteps the bug entirely.
|
|
430
|
+
|
|
431
|
+
If `JMP ($abs)` must be used directly (for code-size reasons, or when the
|
|
432
|
+
vector table is not under the programmer's control), add a build-time assertion:
|
|
433
|
+
|
|
434
|
+
```kick
|
|
435
|
+
// KickAssembler build-time guard: refuse to build if the vector sits at $xxFF
|
|
436
|
+
.errorif (jmp_vector & $FF) == $FF, "jmp_vector at $xxFF: JMP ($abs) would fetch the wrong high byte"
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
This turns a silent runtime misfire into a failed build. Use `.errorif`,
|
|
440
|
+
not `.assert`: measured on KickAssembler 5.25, a failed `.assert` prints a
|
|
441
|
+
message but still writes the PRG and exits 0, so a build script never sees
|
|
442
|
+
it, while `.errorif` aborts with exit 1 and no output file (an earlier
|
|
443
|
+
version of this guard used `.assert`).
|
|
444
|
+
|
|
445
|
+
### Worked example
|
|
446
|
+
|
|
447
|
+
```kick
|
|
448
|
+
// BAD: vector table placed without alignment check
|
|
449
|
+
// If any table entry lands at $xxFF, the dispatch goes to a random address.
|
|
450
|
+
|
|
451
|
+
// Suppose the assembler places this table at $27C0 (example)
|
|
452
|
+
dispatch_table_bad:
|
|
453
|
+
.word handler_0 // at $27C0 — safe
|
|
454
|
+
.word handler_1 // at $27C2 — safe
|
|
455
|
+
.word handler_2 // at $27C4 — safe
|
|
456
|
+
// ... 15 more entries ...
|
|
457
|
+
.word handler_17 // at $27E2 — safe
|
|
458
|
+
.word handler_18 // at $27E4 — safe
|
|
459
|
+
// If the table started at $27C1 instead, handler_31 would be at $27FF
|
|
460
|
+
// ($27C1 + $3E) — the bug would fire silently. (An earlier version said
|
|
461
|
+
// $27C2, which puts handler_31 at $2800: an even base can never land a
|
|
462
|
+
// 2-byte-stride entry on $xxFF.)
|
|
463
|
+
|
|
464
|
+
// The 6510 has no JMP (abs,X) — that is a 65C02 instruction, and an earlier
|
|
465
|
+
// version of this example used it. The 6502 idiom that hits the bug is an
|
|
466
|
+
// indirect JMP whose operand is patched to point at the table entry:
|
|
467
|
+
dispatch_bad:
|
|
468
|
+
lda current_state
|
|
469
|
+
asl
|
|
470
|
+
clc
|
|
471
|
+
adc #<dispatch_table_bad
|
|
472
|
+
sta jmp_ind+1
|
|
473
|
+
lda #>dispatch_table_bad
|
|
474
|
+
adc #0
|
|
475
|
+
sta jmp_ind+2
|
|
476
|
+
jmp_ind:
|
|
477
|
+
jmp ($0000) // reads the vector from the table entry; if that
|
|
478
|
+
// entry sits at $xxFF the high byte comes from $xx00
|
|
479
|
+
|
|
480
|
+
|
|
481
|
+
// GOOD: page-aligned table + self-modified absolute JMP
|
|
482
|
+
|
|
483
|
+
.align $100
|
|
484
|
+
dispatch_table_good:
|
|
485
|
+
.word handler_0 // at $2800 — safe
|
|
486
|
+
.word handler_1 // at $2802 — safe
|
|
487
|
+
.word handler_2 // at $2804 — safe
|
|
488
|
+
.word handler_3 // at $2806 — safe
|
|
489
|
+
// All entries at even offsets from $2800 — none at $xxFF
|
|
490
|
+
|
|
491
|
+
dispatch_good:
|
|
492
|
+
lda current_state // 4 cycles
|
|
493
|
+
asl // 2 cycles — word offset
|
|
494
|
+
tax // 2 cycles
|
|
495
|
+
lda dispatch_table_good,x // 4 cycles — low byte
|
|
496
|
+
sta jmp_abs+1 // 4 cycles — patch low byte of JMP operand
|
|
497
|
+
lda dispatch_table_good+1,x // 4 cycles — high byte
|
|
498
|
+
sta jmp_abs+2 // 4 cycles — patch high byte
|
|
499
|
+
jmp_abs:
|
|
500
|
+
jmp $0000 // 3 cycles — absolute, not indirect; no page-wrap bug
|
|
501
|
+
|
|
502
|
+
|
|
503
|
+
// BUILD-TIME GUARD: if you must use JMP ($abs), refuse to build an unsafe address.
|
|
504
|
+
// Place this near any JMP ($abs) in the codebase. (.errorif aborts the build;
|
|
505
|
+
// .assert, which an earlier version used here, only prints and still emits the PRG.)
|
|
506
|
+
.var MY_VECTOR = $2802
|
|
507
|
+
.errorif (MY_VECTOR & $FF) == $FF, "MY_VECTOR at $xxFF: JMP indirect page-wrap bug"
|
|
508
|
+
```
|
|
509
|
+
|
|
510
|
+
### Cross-references
|
|
511
|
+
|
|
512
|
+
- Technique `jump_table_dispatch` in `docs/techniques/cpu-cycle-tricks.md` —
|
|
513
|
+
the self-modified `JMP $abs` pattern avoids this bug by design; prefer it
|
|
514
|
+
over `JMP ($abs)` in new code
|
|
515
|
+
- Doc `docs/hardware/6510-cpu-reference.md` — the Indirect addressing mode
|
|
516
|
+
section documents the bug; only opcode $6C (`JMP ($abs)`) triggers the wrap,
|
|
517
|
+
not `JMP $abs` ($4C)
|
|
518
|
+
|
|
519
|
+
## lfsr_zero_state_lockup — An LFSR seeded with zero outputs zero for ever
|
|
520
|
+
|
|
521
|
+
**Severity:** medium
|
|
522
|
+
**Region:** both
|
|
523
|
+
**Triggered by techniques:** lfsr_random, attract_mode_input_replay, difficulty_ramp_tables, ghost_target_tile_ai, seeded_level_fill, starfield, procedural_seed_universe, fire_effect, screen_dissolve_lfsr
|
|
524
|
+
**Mitigated by techniques:** lfsr_random
|
|
525
|
+
|
|
526
|
+
### Symptom
|
|
527
|
+
|
|
528
|
+
Every "random" value the game produces is zero: enemies spawn in the
|
|
529
|
+
same corner, the starfield is one column, the noise pattern is blank.
|
|
530
|
+
It works on the developer's machine and fails on another, or fails
|
|
531
|
+
only after a reset, because the seed happened to be zero there.
|
|
532
|
+
|
|
533
|
+
### Mechanism
|
|
534
|
+
|
|
535
|
+
A Galois LFSR shifts its state right and XORs the tap mask in when the
|
|
536
|
+
bit that fell out was 1. From state zero the bit that falls out is 0,
|
|
537
|
+
nothing is XORed in, and the state is zero again; the map fixes zero
|
|
538
|
+
and never leaves it. The 2^n - 1 non-zero states form the one cycle
|
|
539
|
+
the period figures on `lfsr_random` describe (255 and 65535, measured
|
|
540
|
+
in VICE x64sc 3.10); zero is not on it. Any seed source can deliver
|
|
541
|
+
zero: two `$D41B` reads that both return `$00`, a timer read at a
|
|
542
|
+
phase where its low and high bytes happen to be zero, a frame count
|
|
543
|
+
of zero because the player pressed fire on the first frame, or a
|
|
544
|
+
variable the loader never initialised. The technique is on both
|
|
545
|
+
metadata lines above because the lockup arises in a naive seeding of
|
|
546
|
+
`lfsr_random` and the seed check the technique specifies cures it.
|
|
547
|
+
|
|
548
|
+
### Fix
|
|
549
|
+
|
|
550
|
+
Test the seed before the first step and replace zero with a non-zero
|
|
551
|
+
constant. Test the 8-bit and 16-bit registers separately: a 16-bit
|
|
552
|
+
seed can be non-zero while its low byte, used to seed an 8-bit
|
|
553
|
+
register, is zero.
|
|
554
|
+
|
|
555
|
+
### Worked example
|
|
556
|
+
|
|
557
|
+
```c
|
|
558
|
+
// BAD: whatever the sources gave is the seed
|
|
559
|
+
seed = (sid.random << 8) | sid.random;
|
|
560
|
+
s16 = seed; // zero stays zero for ever
|
|
561
|
+
|
|
562
|
+
// FIXED: zero is replaced before the first step
|
|
563
|
+
seed = (sid.random << 8) | sid.random;
|
|
564
|
+
if (seed == 0)
|
|
565
|
+
seed = 0xACE1;
|
|
566
|
+
s16 = seed;
|
|
567
|
+
seed8 = (char)seed;
|
|
568
|
+
if (seed8 == 0)
|
|
569
|
+
seed8 = 0x01;
|
|
570
|
+
```
|
|
571
|
+
|
|
572
|
+
The 6502 form is `lda seed / ora seed+1 / bne ok / lda #$e1 / sta seed /
|
|
573
|
+
lda #$ac / sta seed+1 / ok:` (rung 3, not timed).
|
|
574
|
+
|
|
575
|
+
### Cross-references
|
|
576
|
+
|
|
577
|
+
- Technique `lfsr_random` in `docs/techniques/maths.md` — taps, periods,
|
|
578
|
+
seeding from `$D41B`, a CIA timer and player input
|
|
579
|
+
- Recipe `docs/recipes/oscar64/lfsr-random.md` — the seed check in a
|
|
580
|
+
built and run listing
|
|
581
|
+
- `docs/hardware/sid-reference.md` (`$D41B`) — the noise register drifts
|
|
582
|
+
to all ones under TEST and never reads zero there, but a running noise
|
|
583
|
+
voice can return `$00`
|
|
584
|
+
|
|
585
|
+
## signed_compare_bmi_overflow — BMI after a subtract gives the wrong order when the difference overflows
|
|
586
|
+
|
|
587
|
+
**Severity:** high
|
|
588
|
+
**Region:** both
|
|
589
|
+
**Triggered by techniques:** compare_16bit_and_signed, fixed_point_8_8, tile_grid_collision, slope_collision, nav_area_pathfinding, atan2_8bit, game_tree_search, bresenham_line, solid_vector_3d, voxel_landscape, car_contact_response, lane_pursuit_ai
|
|
590
|
+
**Mitigated by techniques:** compare_16bit_and_signed
|
|
591
|
+
|
|
592
|
+
### Symptom
|
|
593
|
+
|
|
594
|
+
A signed limit check works through every test and fails on one input:
|
|
595
|
+
a sprite falling at a large negative velocity is treated as rising, an
|
|
596
|
+
object far to the left of a boundary is placed to its right, a
|
|
597
|
+
platformer's jump never terminates once the velocity passes a certain
|
|
598
|
+
size. The failing cases are the pairs of opposite sign that are far
|
|
599
|
+
apart, so a small test level never shows them.
|
|
600
|
+
|
|
601
|
+
### Mechanism
|
|
602
|
+
|
|
603
|
+
`SEC / SBC b` leaves `a - b` in `A` and `N` is bit 7 of that byte. Bit 7
|
|
604
|
+
is the sign of the true difference only when the difference fits in
|
|
605
|
+
-128 to 127. `-128 - 127` is `-255`; the byte is `$01`, `N` is clear,
|
|
606
|
+
and `BMI` says -128 is not less than 127. `127 - (-128)` is `255`; the
|
|
607
|
+
byte is `$FF`, `N` is set, and `BMI` says 127 is less than -128. The
|
|
608
|
+
CPU reports each of these with `V` set. Measured in VICE x64sc 3.10 over
|
|
609
|
+
all 65,536 signed byte pairs: the bare `BMI` disagrees with the
|
|
610
|
+
corrected compare on 16,384, one pair in four, and every one of them
|
|
611
|
+
has `V` set (`recipes/kickassembler/compare-16bit-signed.md`). The
|
|
612
|
+
technique is on both metadata lines above because the fault is the
|
|
613
|
+
naive form of its signed compare and its `BVC` / `EOR #$80` form cures
|
|
614
|
+
it.
|
|
615
|
+
|
|
616
|
+
### Fix
|
|
617
|
+
|
|
618
|
+
After the `SBC`, branch on `V`: if it is clear `N` is right; if it is
|
|
619
|
+
set flip bit 7 with `EOR #$80`, which also resets `N`. For 16-bit
|
|
620
|
+
values do the `SBC` on the high byte with the borrow from a `CMP` of
|
|
621
|
+
the low bytes. Where the test is against a constant in the same half
|
|
622
|
+
of the range, or the values can be biased by `$80`, an unsigned `CMP`
|
|
623
|
+
needs no fix-up at all.
|
|
624
|
+
|
|
625
|
+
### Worked example
|
|
626
|
+
|
|
627
|
+
```asm
|
|
628
|
+
// BAD: right until a - b leaves -128..127
|
|
629
|
+
lda vel
|
|
630
|
+
sec
|
|
631
|
+
sbc limit
|
|
632
|
+
bmi below // wrong for 16,384 of 65,536 pairs
|
|
633
|
+
|
|
634
|
+
// FIXED: four more bytes, 3 to 4 more cycles
|
|
635
|
+
lda vel
|
|
636
|
+
sec
|
|
637
|
+
sbc limit
|
|
638
|
+
bvc !+
|
|
639
|
+
eor #$80
|
|
640
|
+
!: bmi below
|
|
641
|
+
```
|
|
642
|
+
|
|
643
|
+
Measured cost of the fixed 8-bit compare with absolute operands: 13 or
|
|
644
|
+
14 cycles before the `BMI` (13 when the subtract did not overflow and
|
|
645
|
+
the `BVC` is taken, 14 when it falls through into the `EOR`), against
|
|
646
|
+
10 for the bad one (rung 1 for the first pair, rung 3 for the second).
|
|
647
|
+
|
|
648
|
+
### Cross-references
|
|
649
|
+
|
|
650
|
+
- Technique `compare_16bit_and_signed` in `docs/techniques/maths.md` —
|
|
651
|
+
the flag table on the boundary pairs, the 16-bit form, and what
|
|
652
|
+
Oscar64 emits instead
|
|
653
|
+
- Technique `fixed_point_8_8` in `docs/techniques/maths.md` — signed
|
|
654
|
+
velocities in the high byte, the place this bites first
|
|
655
|
+
- Recipe `docs/recipes/kickassembler/compare-16bit-signed.md` — the
|
|
656
|
+
sweep that counts the 16,384 misses
|
|
657
|
+
|
|
658
|
+
---
|
|
659
|
+
|
|
660
|
+
## asm_optimiser_moves_self_modified_instruction — Oscar64's assembler optimiser at -O2 rewrites a non-volatile __asm block, so a store into an instruction's operand byte lands in a copy that never runs
|
|
661
|
+
|
|
662
|
+
**Severity:** high
|
|
663
|
+
**Region:** both
|
|
664
|
+
**Triggered by techniques:** table_multiply_8x8
|
|
665
|
+
|
|
666
|
+
### Symptom
|
|
667
|
+
|
|
668
|
+
A routine written as an `__asm` block that patches its own operand
|
|
669
|
+
bytes (`sta m1 + 1` into a following `lda table,y`) gives the right
|
|
670
|
+
answer at `-O0` and the wrong answer at `-O2`, with no warning from the
|
|
671
|
+
compiler. The `.asm` listing looks correct at the point a reader opens
|
|
672
|
+
it first, because the instruction sequence as written is still in the
|
|
673
|
+
file; it is just not the sequence that runs.
|
|
674
|
+
|
|
675
|
+
Measured on the quarter-square multiply harness from
|
|
676
|
+
`techniques/maths.md` (`fpcheck.c`, Oscar64 `-tm=c64 -O2`, VICE x64sc
|
|
677
|
+
3.10, PAL, screen decoded with the character ROM). The only change
|
|
678
|
+
between the two builds is the word `volatile` on the multiply's block:
|
|
679
|
+
|
|
680
|
+
```
|
|
681
|
+
__asm volatile mul cs 0f09 exp 0f09 miss 00000 pass
|
|
682
|
+
__asm mul cs c524 exp 0f09 miss 00000 fail
|
|
683
|
+
```
|
|
684
|
+
|
|
685
|
+
The miss counter is the trap inside the trap. It reads `00000` in the
|
|
686
|
+
failing build because every one of the 65,536 products is wrong and a
|
|
687
|
+
16-bit counter of 65,536 misses wraps to zero. The checksum is the only
|
|
688
|
+
tell. Recomputing the checksum in Python from the instruction sequence
|
|
689
|
+
that actually executes (below) gives `C524` and 65,536 misses, so the
|
|
690
|
+
number on screen is fully accounted for by the mechanism.
|
|
691
|
+
|
|
692
|
+
### Mechanism
|
|
693
|
+
|
|
694
|
+
At `-O2` and above Oscar64 runs an assembler optimiser over inline
|
|
695
|
+
assembly that is not marked `volatile`. It is free to change addressing
|
|
696
|
+
modes and instruction order, and it may emit more than one copy of a
|
|
697
|
+
block. It does not know that a store whose target is `label + 1` is
|
|
698
|
+
writing an operand byte, so it resolves that store against wherever the
|
|
699
|
+
label ended up and optimises the instruction the label points at as if
|
|
700
|
+
its operand were the constant the source shows.
|
|
701
|
+
|
|
702
|
+
In the measured build the executed copy at `$0C00` had `ldy / lda ,y`
|
|
703
|
+
rewritten as `ldx / lda ,x` and `sec` moved down one instruction. The
|
|
704
|
+
four patch stores in that copy resolve to `$0C3E`, `$0C47`, `$0C41`
|
|
705
|
+
and `$0C4A`. Those addresses are inside a second, byte-for-byte
|
|
706
|
+
original copy of the block that the assembler placed after the `RTS` at
|
|
707
|
+
`$0C27`; nothing jumps to it. The running instructions keep the operands
|
|
708
|
+
they were assembled with, `sqr_lo + 0` and `nsq_lo + 0`, which is the
|
|
709
|
+
routine with `a = 0` patched into the `sqr` reads and `255 - a = 0`
|
|
710
|
+
into the `nsq` reads. Every product is therefore `q(b) - q(|b - 255|)`
|
|
711
|
+
instead of `q(a + b) - q(|a - b|)`; for `0 * 0` that is
|
|
712
|
+
`0 - 16256 = $C080`, the figure the technique page recorded.
|
|
713
|
+
|
|
714
|
+
Executed copy, from the `.asm` listing of the non-volatile build:
|
|
715
|
+
|
|
716
|
+
```
|
|
717
|
+
0c00 LDA $0bff ; mul_a
|
|
718
|
+
0c03 STA $0c3e ; m1 + 1, but in the dead copy below
|
|
719
|
+
0c06 STA $0c47 ; m2 + 1
|
|
720
|
+
0c09 EOR #$ff
|
|
721
|
+
0c0b STA $0c41 ; m3 + 1
|
|
722
|
+
0c0e STA $0c4a ; m4 + 1
|
|
723
|
+
0c11 LDX $0e9d ; mul_b, now X
|
|
724
|
+
0c14 LDA $0f00,x ; sqr_lo + 0: operand never patched
|
|
725
|
+
0c17 SEC
|
|
726
|
+
0c18 SBC $1300,x ; nsq_lo + 0
|
|
727
|
+
0c1b STA $0e9e ; mul_r
|
|
728
|
+
0c1e LDA $1100,x
|
|
729
|
+
0c21 SBC $1500,x
|
|
730
|
+
0c24 STA $0e9f
|
|
731
|
+
0c27 RTS
|
|
732
|
+
```
|
|
733
|
+
|
|
734
|
+
Dead copy, immediately after it (no reference to `$0C28` anywhere in
|
|
735
|
+
the listing):
|
|
736
|
+
|
|
737
|
+
```
|
|
738
|
+
0c28 LDA $0bff
|
|
739
|
+
0c2b STA $0c3e
|
|
740
|
+
0c2e STA $0c47
|
|
741
|
+
0c31 EOR #$ff
|
|
742
|
+
0c33 STA $0c41
|
|
743
|
+
0c36 STA $0c4a
|
|
744
|
+
0c39 LDY $0e9d
|
|
745
|
+
0c3c SEC
|
|
746
|
+
0c3d LDA $0f00,y ; $0c3e is the low operand byte: this is m1 + 1
|
|
747
|
+
0c40 SBC $1300,y ; $0c41 is m3 + 1
|
|
748
|
+
0c43 STA $0e9e
|
|
749
|
+
0c46 LDA $1100,y ; $0c47 is m2 + 1
|
|
750
|
+
0c49 SBC $1500,y ; $0c4a is m4 + 1
|
|
751
|
+
0c4c STA $0e9f
|
|
752
|
+
0c4f RTS
|
|
753
|
+
```
|
|
754
|
+
|
|
755
|
+
With `volatile` the block is emitted once, as written, and the four
|
|
756
|
+
stores resolve to `$0C16`, `$0C1F`, `$0C19` and `$0C22`, which are the
|
|
757
|
+
operand bytes of the four indexed reads at `$0C15`, `$0C1E`, `$0C18`
|
|
758
|
+
and `$0C21` in the same copy. The `-O2` volatile build and the `-O0`
|
|
759
|
+
non-volatile build both pass; `-O0` is a control, not a fix, because
|
|
760
|
+
the optimiser is simply not run at that level.
|
|
761
|
+
|
|
762
|
+
### Fix
|
|
763
|
+
|
|
764
|
+
Any one of these keeps the patch and the patched instruction in the
|
|
765
|
+
same copy of the code. All three were built and run here.
|
|
766
|
+
|
|
767
|
+
1. Write the block `__asm volatile { ... }`. Measured: pass, 52 cycles
|
|
768
|
+
per call, the figure on the technique page.
|
|
769
|
+
2. Wrap the function in `#pragma optimize(push)` / `#pragma
|
|
770
|
+
optimize(noasm)` / `#pragma optimize(pop)` and leave the block
|
|
771
|
+
plain. Measured: pass, and the listing is the same instruction
|
|
772
|
+
sequence as the volatile build.
|
|
773
|
+
3. Do not patch code at all. Put the operand in a zero-page pointer and
|
|
774
|
+
index through it with `lda (zp),y`; the store is to a variable, which
|
|
775
|
+
the optimiser treats as data. Measured: pass, 66 cycles per call
|
|
776
|
+
against 52 (arithmetic from the same harness: 11,504 less the 4,904
|
|
777
|
+
empty-loop baseline, over 100 calls). The cost is the four `lda # /
|
|
778
|
+
sta zp` pairs that load the table page bytes, plus the slower
|
|
779
|
+
addressing mode.
|
|
780
|
+
|
|
781
|
+
Moving the routine to a separate assembler source file is not an option
|
|
782
|
+
in Oscar64: the compiler has no object linker and no external symbol
|
|
783
|
+
resolution (`toolchains/oscar64-reference.md`, "Calling KickAssembler
|
|
784
|
+
code from Oscar64"), so a hand-assembled routine has to be embedded as
|
|
785
|
+
bytes at a fixed address and called by `jsr`. That does put it beyond
|
|
786
|
+
the optimiser's reach, but it was not measured here.
|
|
787
|
+
|
|
788
|
+
### Worked example
|
|
789
|
+
|
|
790
|
+
The C shape that fails, with the two results. Nothing about the
|
|
791
|
+
pattern is specific to the multiply: any store to `label + 1` or
|
|
792
|
+
`label + 2` where `label` is an instruction inside the same block is
|
|
793
|
+
exposed.
|
|
794
|
+
|
|
795
|
+
```text
|
|
796
|
+
__noinline void qmul(void)
|
|
797
|
+
{
|
|
798
|
+
__asm // BAD at -O2: mul cs c524, miss 00000 (wrapped)
|
|
799
|
+
{
|
|
800
|
+
lda mul_a
|
|
801
|
+
sta m1 + 1 // patches an operand byte of m1 ...
|
|
802
|
+
sta m2 + 1
|
|
803
|
+
eor #$ff
|
|
804
|
+
sta m3 + 1
|
|
805
|
+
sta m4 + 1
|
|
806
|
+
ldy mul_b
|
|
807
|
+
sec
|
|
808
|
+
m1: lda sqr_lo, y // ... but the m1 that runs is a rewritten copy
|
|
809
|
+
m3: sbc nsq_lo, y
|
|
810
|
+
sta mul_r
|
|
811
|
+
m2: lda sqr_hi, y
|
|
812
|
+
m4: sbc nsq_hi, y
|
|
813
|
+
sta mul_r + 1
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
__noinline void qmul(void)
|
|
818
|
+
{
|
|
819
|
+
__asm volatile // FIXED: mul cs 0f09, miss 00000, 52 cycles
|
|
820
|
+
{
|
|
821
|
+
... same body ...
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
```
|
|
825
|
+
|
|
826
|
+
The data-patch form (fix 3), which is safe in a plain block because it
|
|
827
|
+
never writes into code. Shown in KickAssembler syntax; the Oscar64
|
|
828
|
+
version is the same instructions with `__zeropage char *` pointers in
|
|
829
|
+
place of the two `.label` lines:
|
|
830
|
+
|
|
831
|
+
```asm
|
|
832
|
+
.label zp_sqr = $f7 // two zero-page pointers
|
|
833
|
+
.label zp_nsq = $f9
|
|
834
|
+
lda mul_a
|
|
835
|
+
sta zp_sqr // low byte = a
|
|
836
|
+
eor #$ff
|
|
837
|
+
sta zp_nsq // low byte = 255 - a
|
|
838
|
+
ldy mul_b
|
|
839
|
+
sec
|
|
840
|
+
lda #>sqr_lo
|
|
841
|
+
sta zp_sqr + 1 // a store to a variable, not to an instruction
|
|
842
|
+
lda #>nsq_lo
|
|
843
|
+
sta zp_nsq + 1
|
|
844
|
+
lda (zp_sqr),y
|
|
845
|
+
sbc (zp_nsq),y
|
|
846
|
+
sta mul_r
|
|
847
|
+
lda #>sqr_hi
|
|
848
|
+
sta zp_sqr + 1
|
|
849
|
+
lda #>nsq_hi
|
|
850
|
+
sta zp_nsq + 1
|
|
851
|
+
lda (zp_sqr),y
|
|
852
|
+
sbc (zp_nsq),y
|
|
853
|
+
sta mul_r + 1
|
|
854
|
+
```
|
|
855
|
+
|
|
856
|
+
A grep of the Oscar64 recipes for stores to `label + 1` inside
|
|
857
|
+
non-volatile `__asm` blocks found only stores into C variables
|
|
858
|
+
(`load-asset-runtime.md` writes `ld_end + 1`, a `static unsigned`), so
|
|
859
|
+
no shipped recipe carries the failing shape; the technique page is the
|
|
860
|
+
one place it was written and caught.
|
|
861
|
+
|
|
862
|
+
### Cross-references
|
|
863
|
+
|
|
864
|
+
- Technique `table_multiply_8x8` in `docs/techniques/maths.md`: the
|
|
865
|
+
routine, its 52-cycle figure, and "The measuring program" whose
|
|
866
|
+
checksum caught this
|
|
867
|
+
- `docs/toolchains/oscar64-reference.md`: the assembler optimiser
|
|
868
|
+
paragraph (`__asm volatile`, `#pragma optimize(noasm)`) and the
|
|
869
|
+
section on calling KickAssembler code, which is why a separate `.s`
|
|
870
|
+
file is not a remedy here
|
|
871
|
+
- Pitfall `getchx_petscii_remaps_return` in
|
|
872
|
+
`docs/pitfalls/kernal-and-io.md`: the other Oscar64-specific pitfall,
|
|
873
|
+
for the same "the source is right, the toolchain did something else"
|
|
874
|
+
reading habit
|