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,1147 @@
|
|
|
1
|
+
---
|
|
2
|
+
category: cia
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
<!-- doc-type: pitfall-reference -->
|
|
6
|
+
|
|
7
|
+
# CIA Pitfalls
|
|
8
|
+
|
|
9
|
+
Pitfalls in the two 6526 CIAs that are not about reading the keyboard or
|
|
10
|
+
joysticks — those are in `pitfalls/input.md`. The entries here are about
|
|
11
|
+
the chip's own sequencing rules: registers whose *order* of access is
|
|
12
|
+
part of their contract, so that code which touches them in the wrong
|
|
13
|
+
order gets a wrong answer without any error to show for it.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## tod_read_order_latch — Read TOD hours first and tenths last; write hours first and tenths last
|
|
18
|
+
|
|
19
|
+
**Severity:** high
|
|
20
|
+
**Region:** both
|
|
21
|
+
**Triggered by registers:** DC08, DC0B, DC0F
|
|
22
|
+
**Triggered by techniques:** tod_alarm_interrupt
|
|
23
|
+
|
|
24
|
+
### Symptom
|
|
25
|
+
|
|
26
|
+
Four different failures, one cause. Which one you get depends on which
|
|
27
|
+
order your code touched `$DC08`-`$DC0B` in.
|
|
28
|
+
|
|
29
|
+
1. **A tenths digit that lags.** A clock display polled once a second
|
|
30
|
+
shows seconds, minutes and hours moving while the tenths digit is
|
|
31
|
+
always one poll stale, or sits on a value that makes no sense next to
|
|
32
|
+
the seconds. The code reads tenths first and hours last.
|
|
33
|
+
2. **A frozen clock.** Every read of the four TOD registers returns the
|
|
34
|
+
same time, forever, although the game has been running for minutes.
|
|
35
|
+
Somewhere the code read `$DC0B` — often a lone peek at the hours or
|
|
36
|
+
the AM/PM bit — and never read `$DC08` afterwards.
|
|
37
|
+
3. **A clock that never starts.** The program sets the time and the
|
|
38
|
+
registers read back exactly what was written, an hour later. The
|
|
39
|
+
code wrote tenths first and hours last.
|
|
40
|
+
4. **A time that will not set, and an alarm interrupt later.** The
|
|
41
|
+
program follows "set `$DC0F` bit 7, then write hours..tenths"; the
|
|
42
|
+
clock keeps its old time and keeps running, and some time later ICR
|
|
43
|
+
bit 2 sets. The writes armed the alarm, not the clock.
|
|
44
|
+
|
|
45
|
+
Until 2026-09-21 two pages in this repository led straight into these:
|
|
46
|
+
`hardware/cia-reference.md` had the first three the wrong way round (a
|
|
47
|
+
read of tenths latched, a read of hours released, a write of hours
|
|
48
|
+
started the clock), and the CIA1 quick-lookup row in
|
|
49
|
+
`hardware/c64-registers-reference.md` prescribed the fourth (set the
|
|
50
|
+
clock with bit 7 of `$DC0F` *set*). Both are corrected; the measurement
|
|
51
|
+
that corrected them is below.
|
|
52
|
+
|
|
53
|
+
### Mechanism
|
|
54
|
+
|
|
55
|
+
The 6526's time-of-day clock is four BCD registers — tenths (`$DC08`),
|
|
56
|
+
seconds (`$DC09`), minutes (`$DC0A`), hours with the AM/PM flag in bit 7
|
|
57
|
+
(`$DC0B`) — counting a 50 Hz or 60 Hz input on the chip's TOD pin. Two
|
|
58
|
+
sequencing rules are built into the silicon, and both key on the
|
|
59
|
+
*hours* register at one end and the *tenths* register at the other:
|
|
60
|
+
|
|
61
|
+
- **Reading.** A read of `$DC0B` latches all four registers: from that
|
|
62
|
+
moment every read of `$DC08`-`$DC0B` returns the values as they stood
|
|
63
|
+
at the hours read. The latch is released by a read of `$DC08`. The
|
|
64
|
+
counter keeps running underneath the latch the whole time; only what
|
|
65
|
+
you *see* is frozen. This exists so that a four-byte read can never
|
|
66
|
+
straddle a carry (59.9 to 00.0) — provided it starts at hours and
|
|
67
|
+
ends at tenths.
|
|
68
|
+
- **Writing.** A write to `$DC0B` stops the clock. It does not run
|
|
69
|
+
again until `$DC08` is written. This exists so that a four-byte
|
|
70
|
+
*write* cannot be overtaken by a tick halfway through — provided it
|
|
71
|
+
starts at hours and ends at tenths. Written the other way round, the
|
|
72
|
+
clock is stopped by the last write and stays stopped.
|
|
73
|
+
- **Clock or alarm.** Bit 7 of `$DC0F` (CRB, ALARM) chooses what a TOD
|
|
74
|
+
*write* lands in: 0 = the clock, 1 = the alarm. Reads always return
|
|
75
|
+
the clock, whatever bit 7 says (the datasheet's statement; the read
|
|
76
|
+
side of that was not measured here). The alarm registers sit at the
|
|
77
|
+
same four addresses, so "set the clock with bit 7 = 1" silently
|
|
78
|
+
programs the alarm and leaves the clock alone — neither set nor
|
|
79
|
+
stopped, in VICE (rows E, G and H below). One caveat on the "nor
|
|
80
|
+
stopped": the datasheet's "stopped whenever a write to the Hours
|
|
81
|
+
register occurs" is not conditioned on bit 7, so a 6526 may pause
|
|
82
|
+
the clock on an alarm-side hours write until the alarm-side tenths
|
|
83
|
+
write that follows it a few microseconds later. That is not
|
|
84
|
+
measurable here and makes no difference to a four-write sequence;
|
|
85
|
+
it would matter only to code that writes the alarm's hours alone.
|
|
86
|
+
- **Rate.** Bit 7 of `$DC0E` (CRA, TODIN) tells the chip whether the
|
|
87
|
+
TOD pin carries 50 Hz (1) or 60 Hz (0), i.e. whether to divide by 5
|
|
88
|
+
or by 6 to make a tenth. The KERNAL does not set this for you:
|
|
89
|
+
IOINIT writes `$08` to `$DC0E` (`LDA #$08` at `$FDAE`, `STA $DC0E`
|
|
90
|
+
at `$FDB0`, in KERNAL 901227-03) and the same `$08` to `$DD0E` at
|
|
91
|
+
`$FDB3`, which leaves bit 7 clear on both CIAs, and the
|
|
92
|
+
region-dependent timer setup at `$FF6E` reads `$DC0E` back, masks
|
|
93
|
+
with `#$80` and ORs in `$11` — it preserves whatever bit 7 was, it
|
|
94
|
+
never sets it. On a PAL machine the TOD therefore counts a 50 Hz
|
|
95
|
+
input with the 60 Hz divider, five-sixths of true speed, until a
|
|
96
|
+
program sets the bit. (ROM bytes read from the 901227-03 image,
|
|
97
|
+
rung 1 — an earlier version of this page placed the `LDA`/`STA` pair
|
|
98
|
+
at `$FDB0` alone; the five-sixths is arithmetic from the datasheet's
|
|
99
|
+
divider, and the VICE run in the measurement section below confirms
|
|
100
|
+
the ratio.)
|
|
101
|
+
|
|
102
|
+
Everything in the first three bullets is what the MOS 6526 datasheet
|
|
103
|
+
says in its "Time of Day Clock" section, and it is what VICE x64sc 3.10
|
|
104
|
+
does when measured (rung 1, below). It is not what two pages in this
|
|
105
|
+
repository said, which is why this entry exists: a reader who trusted
|
|
106
|
+
them wrote code with the read latch and the write halt on the wrong
|
|
107
|
+
registers.
|
|
108
|
+
|
|
109
|
+
One consequence worth stating because it follows from the rules rather
|
|
110
|
+
than from any measurement: the latch is chip state, not per-caller
|
|
111
|
+
state. If an interrupt handler reads `$DC08` while the main loop is
|
|
112
|
+
between its hours read and its tenths read, the main loop's latch is
|
|
113
|
+
gone and its remaining reads are live — a torn time with the correct
|
|
114
|
+
order on both sides. Keep all TOD reads in one context, or bracket the
|
|
115
|
+
four reads with `SEI`/`CLI` when an IRQ handler also reads the clock.
|
|
116
|
+
`SEI` does not hold off an NMI, so an NMI handler (RESTORE, or anything
|
|
117
|
+
raised through CIA2) must leave `$DC08`-`$DC0B` alone altogether.
|
|
118
|
+
|
|
119
|
+
### Fix
|
|
120
|
+
|
|
121
|
+
- **Read** in the order `$DC0B`, `$DC0A`, `$DC09`, `$DC08` — hours
|
|
122
|
+
first, tenths last. If you only want one register and it is hours,
|
|
123
|
+
read `$DC08` afterwards anyway to drop the latch. Any other single
|
|
124
|
+
register can be read on its own.
|
|
125
|
+
- **Write** in the order `$DC0B`, `$DC0A`, `$DC09`, `$DC08` with
|
|
126
|
+
`$DC0F` bit 7 = 0. The clock is stopped from the first write to the
|
|
127
|
+
last and starts at exactly the time you set. To program the alarm,
|
|
128
|
+
do the same four writes with bit 7 = 1, then put bit 7 back to 0
|
|
129
|
+
before any code that expects to set the clock.
|
|
130
|
+
- **Set the rate** yourself: `$DC0E` bit 7 = 1 on a PAL machine (50 Hz
|
|
131
|
+
mains), 0 on NTSC (60 Hz). The KERNAL leaves it 0 on both.
|
|
132
|
+
- Values are BCD; the hours register carries AM/PM in bit 7.
|
|
133
|
+
|
|
134
|
+
### Worked example
|
|
135
|
+
|
|
136
|
+
The first two routines are the orders an earlier version of the two
|
|
137
|
+
hardware pages implied; the last two are the correct ones. This fence
|
|
138
|
+
is assembled by `npm run check:listings`.
|
|
139
|
+
|
|
140
|
+
```kick
|
|
141
|
+
// WRONG — tenths first "to latch", hours last "to release" (the order
|
|
142
|
+
// cia-reference.md gave before 2026-09-21). On the first pass nothing
|
|
143
|
+
// is latched while these four reads happen, so a carry between them
|
|
144
|
+
// tears the time. The hours read at the end DOES latch, so on every
|
|
145
|
+
// later pass the first read returns the tenths as they stood at the
|
|
146
|
+
// previous pass's hours read and then releases; only the last three
|
|
147
|
+
// reads of each pass are live.
|
|
148
|
+
read_tod_wrong:
|
|
149
|
+
lda $dc08
|
|
150
|
+
sta tod_tenths
|
|
151
|
+
lda $dc09
|
|
152
|
+
sta tod_secs
|
|
153
|
+
lda $dc0a
|
|
154
|
+
sta tod_mins
|
|
155
|
+
lda $dc0b // latches all four here; nothing releases it
|
|
156
|
+
sta tod_hours
|
|
157
|
+
rts
|
|
158
|
+
|
|
159
|
+
// WRONG — "$DC0F bit 7 = 1, then write hours..tenths" (the quick-lookup
|
|
160
|
+
// row before 2026-09-21). Bit 7 = 1 routes the writes to the ALARM; the
|
|
161
|
+
// clock is neither set nor stopped (rows E and G in the measurement).
|
|
162
|
+
set_tod_wrong:
|
|
163
|
+
lda $dc0f
|
|
164
|
+
ora #$80
|
|
165
|
+
sta $dc0f
|
|
166
|
+
lda new_hours
|
|
167
|
+
sta $dc0b
|
|
168
|
+
lda new_mins
|
|
169
|
+
sta $dc0a
|
|
170
|
+
lda new_secs
|
|
171
|
+
sta $dc09
|
|
172
|
+
lda new_tenths
|
|
173
|
+
sta $dc08 // the alarm is armed for new_*; the clock never changed
|
|
174
|
+
rts
|
|
175
|
+
|
|
176
|
+
// RIGHT — hours first (latches all four), tenths last (releases).
|
|
177
|
+
read_tod:
|
|
178
|
+
lda $dc0b
|
|
179
|
+
sta tod_hours
|
|
180
|
+
lda $dc0a
|
|
181
|
+
sta tod_mins
|
|
182
|
+
lda $dc09
|
|
183
|
+
sta tod_secs
|
|
184
|
+
lda $dc08 // releases the latch; the counter never stopped
|
|
185
|
+
sta tod_tenths
|
|
186
|
+
rts
|
|
187
|
+
|
|
188
|
+
// RIGHT — mains rate in CRA bit 7, clock (not alarm) in CRB bit 7,
|
|
189
|
+
// hours first (stops the clock), tenths last (starts it at the set time).
|
|
190
|
+
set_tod:
|
|
191
|
+
lda $dc0e
|
|
192
|
+
ora #$80 // TODIN = 1: 50 Hz mains (PAL). NTSC: and #$7f
|
|
193
|
+
sta $dc0e
|
|
194
|
+
lda $dc0f
|
|
195
|
+
and #$7f // ALARM = 0: the four writes below reach the clock
|
|
196
|
+
sta $dc0f
|
|
197
|
+
lda new_hours // BCD, bit 7 = PM
|
|
198
|
+
sta $dc0b // clock stops
|
|
199
|
+
lda new_mins
|
|
200
|
+
sta $dc0a
|
|
201
|
+
lda new_secs
|
|
202
|
+
sta $dc09
|
|
203
|
+
lda new_tenths
|
|
204
|
+
sta $dc08 // clock starts
|
|
205
|
+
rts
|
|
206
|
+
|
|
207
|
+
tod_hours: .byte 0
|
|
208
|
+
tod_mins: .byte 0
|
|
209
|
+
tod_secs: .byte 0
|
|
210
|
+
tod_tenths: .byte 0
|
|
211
|
+
new_hours: .byte $09 // 09:41:23.5 AM
|
|
212
|
+
new_mins: .byte $41
|
|
213
|
+
new_secs: .byte $23
|
|
214
|
+
new_tenths: .byte $05
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### How this was measured
|
|
218
|
+
|
|
219
|
+
Instrument: VICE x64sc 3.10 (`*** VICE Version 3.10 ***` in its own
|
|
220
|
+
startup log; the Homebrew cellar path says the same), headless, `-warp`,
|
|
221
|
+
`-autostartprgmode 1`, `-limitcycles 20000000` (16,000,000 until three
|
|
222
|
+
rows were added on 2026-09-22; the probe now waits about 12 s of C64
|
|
223
|
+
time after boot), `-exitscreenshot`. PAL
|
|
224
|
+
(VICE's default C64C: 8565, 8580, CIA 8521) with CRA bit 7 = 1; NTSC (`-model ntsc`, 6567R8) with CRA bit
|
|
225
|
+
7 = 0. Nothing here was run on a 6526 on a bench. An earlier version said
|
|
226
|
+
the PAL run was a 6569; `x64sc -default` is the C64C, whose CIAs are 8521s,
|
|
227
|
+
while the NTSC run's are 6526s. VICE derives the TOD
|
|
228
|
+
tick from emulated cycles, not from the host clock — the probe runs
|
|
229
|
+
under `-warp` and the clock still advances 1.5 s in 75 PAL frames, and
|
|
230
|
+
`src/core/ciacore.c` in the VICE tree schedules the tick as an alarm
|
|
231
|
+
`todticks` cycles apart — so the readings below are about the chip
|
|
232
|
+
model VICE implements, at emulated speed.
|
|
233
|
+
|
|
234
|
+
The probe (listing below, exactly as run) clears the screen, sets the
|
|
235
|
+
clock to 00:00:00.0 in the correct order, waits a known number of
|
|
236
|
+
frames by watching `$D012`, and stores a reverse-space (`$A0`) on a
|
|
237
|
+
fixed screen row at the column equal to the tenths value it read. One
|
|
238
|
+
test per row, so the screenshot *is* the result. Decoded with PIL,
|
|
239
|
+
which first finds the display area as the run of non-border pixels
|
|
240
|
+
(border red, background blue): the PAL screenshot is 384×272 with the
|
|
241
|
+
display at y 35-234, so screen row 0 is at PNG y = 35, and the NTSC one
|
|
242
|
+
384×247 with the display at y 23-222, row 0 at y = 23; the display
|
|
243
|
+
spans x 32-351 in both, so screen column c is x = 32 + 8c (this geometry,
|
|
244
|
+
the palette values per model and a decode snippet are now collected in
|
|
245
|
+
`../runtime/vice-reference.md`, "Reading the exit screenshot"). The decoder
|
|
246
|
+
then demands exactly one lit cell per test row, all 64 of its pixels
|
|
247
|
+
lit, and no lit cell anywhere else on the screen; every run below
|
|
248
|
+
passed that. The runs (2026-09-22, after rows G, H and I were added;
|
|
249
|
+
the first seven rows re-measured and reading what the page's first
|
|
250
|
+
version had):
|
|
251
|
+
|
|
252
|
+
| Row | Test | PAL 1 | PAL 2 | PAL 3 | NTSC 1 | NTSC 2 | Reading |
|
|
253
|
+
|-----|------|------:|------:|------:|-------:|-------:|---------|
|
|
254
|
+
| 6 | **I** hours written (clock stopped at 0); tenths *read* at ~0.3 s; tenths read again ~1.5 s later | 0 | 0 | 0 | 0 | 0 | still 0: **reading tenths does not restart a stopped clock**, only the tenths write does |
|
|
255
|
+
| 8 | **H** running from 0; at ~0.3 s CRB bit 7 = 1, hours written alone, bit 7 = 0; tenths read ~1.5 s later | 8 | 8 | 8 | 8 | 8 | ~1.8 s elapsed: **an alarm-side hours write did not stop the clock** (a stop would read 2 or 3) |
|
|
256
|
+
| 10 | **G** as E, then a further ~0.3 s before the tenths read | 8 | 8 | 8 | 8 | 8 | ~1.8 s elapsed: **the clock kept running after the alarm-routed writes** (a stop would read 4 or 5) |
|
|
257
|
+
| 12 | **A** hours read at ~0.3 s, tenths read at ~1.5 s | 3 | 3 | 3 | 3 | 3 | tenths froze at the hours read: **hours read latches** |
|
|
258
|
+
| 14 | **B** tenths written, *then* hours; tenths read ~1.5 s later | 0 | 0 | 0 | 0 | 0 | never moved: **hours write stops the clock** |
|
|
259
|
+
| 16 | **C** live tenths at ~1.5 s (control) | 5 | 5 | 5 | 5 | 5 | clock runs at the right rate |
|
|
260
|
+
| 18 | **D** live tenths at ~0.3 s (control) | 2 | 2 | 3 | 2 | 3 | |
|
|
261
|
+
| 20 | **E** CRB bit 7 = 1, then 00:00:00.0 written hours..tenths at ~1.5 s; tenths read at once | 5 | 5 | 5 | 5 | 5 | not 0: **bit 7 = 1 writes the alarm, the clock was not set** |
|
|
262
|
+
| 22 | **F1** hours read at once after start; first tenths read at ~0.66 s | 0 | 0 | 0 | 0 | 0 | the latched value |
|
|
263
|
+
| 24 | **F2** second tenths read, back to back with F1 | 6 | 6 | 6 | 6 | 6 | live: **the tenths read released the latch** |
|
|
264
|
+
|
|
265
|
+
The seven tests (A, B, E, F1, G, H, I) gave the same digit on every
|
|
266
|
+
run. The two controls vary by one between runs, and the cause was
|
|
267
|
+
tested rather than guessed: VICE's autostart delay is randomised by
|
|
268
|
+
default, and two PAL runs with `+autostart-delay-random` came out
|
|
269
|
+
identical (C = 5, D = 2, every test digit as above; the page's first
|
|
270
|
+
version made the same check on its seven-row probe and read C = 5,
|
|
271
|
+
D = 3 twice). The program therefore starts at a different point
|
|
272
|
+
relative to the emulated tick from run to run, a 75-frame wait is
|
|
273
|
+
1.48-1.50 s depending on where in a frame it began, and a live reading
|
|
274
|
+
taken near 1.5 s or 0.3 s lands either side of a digit boundary. The
|
|
275
|
+
tests do not sit near a boundary; the controls do. Which control
|
|
276
|
+
wobbles is itself run-dependent: the first version's five runs read
|
|
277
|
+
C = 4/5/4/5/5 with D = 3/2/3/2/2, the five above read C = 5 every time
|
|
278
|
+
with D = 2/2/3/2/3. A decoder that demands C = 5 or D = 2 exactly will
|
|
279
|
+
fail on some runs and prove nothing when it does. (This also accounts
|
|
280
|
+
for an earlier run of the same design, before this page, reading C = 5
|
|
281
|
+
where the next read 4.)
|
|
282
|
+
|
|
283
|
+
Row B also shows what the earlier text would have predicted wrongly: if
|
|
284
|
+
the hours write started the clock, B would read 4 or 5 like C. Row A
|
|
285
|
+
shows the other half: if the tenths read latched and the hours read
|
|
286
|
+
released, A would read 4 or 5 too. Row I settles a third reading that
|
|
287
|
+
is in circulation (C64-Wiki's `$DC0B` entry has a stopped clock wait
|
|
288
|
+
for a *read* of the tenths register): the tenths read at 0.3 s did not
|
|
289
|
+
restart it, the clock still stood at 0 a second and a half later, and
|
|
290
|
+
only the tenths write in `reset_tod` set it going again — the
|
|
291
|
+
datasheet's rule, in VICE.
|
|
292
|
+
|
|
293
|
+
One more PAL run with CRA bit 7 left at 0 — the state the KERNAL leaves
|
|
294
|
+
it in; the listing's PAL branch with its `ora #$80` changed to
|
|
295
|
+
`and #$7f` — reads C = 2, D = 2, E = 2, A = 2, F2 = 5, G = 5 and H = 5,
|
|
296
|
+
with B, I and F1 still 0: 1.5 s of waiting advanced the clock about
|
|
297
|
+
1.25 s and 1.8 s about 1.5 s, the five-sixths ratio above (rung 1 for
|
|
298
|
+
the emulator's divider; the datasheet describes the same divide-by-6).
|
|
299
|
+
|
|
300
|
+
```kick
|
|
301
|
+
// TOD semantics probe, extended from tod3 (same encoding: a reverse-space at
|
|
302
|
+
// screen column = value read, one test per row). Original code for c64-kb.
|
|
303
|
+
//
|
|
304
|
+
// row 6: I hours written (clock stopped); tenths READ at ~0.3 s; tenths
|
|
305
|
+
// read again ~1.5 s later restart-by-read test
|
|
306
|
+
// row 8: H clock running from 0; at ~0.3 s CRB bit 7 = 1, hours written
|
|
307
|
+
// (alarm side, nothing after it), bit 7 = 0; tenths read ~1.5 s
|
|
308
|
+
// later alarm-hours halt test
|
|
309
|
+
// row 10: G as E, then a further ~0.3 s wait before the tenths read
|
|
310
|
+
// still-running test
|
|
311
|
+
// row 12: A hours read at ~0.3 s, tenths read at ~1.5 s latch test
|
|
312
|
+
// row 14: B tenths then hours written, tenths read ~1.5 s later halt test
|
|
313
|
+
// row 16: C live tenths at ~1.5 s control
|
|
314
|
+
// row 18: D live tenths at ~0.3 s control
|
|
315
|
+
// row 20: E CRB bit 7 = 1, then 00:00:00.0 written hours..tenths while
|
|
316
|
+
// the clock reads ~1.5 s; tenths read at once alarm-select test
|
|
317
|
+
// row 22: F1 hours read right after start; first tenths read at ~0.66 s
|
|
318
|
+
// row 24: F2 second tenths read, back to back with F1 latch release test
|
|
319
|
+
//
|
|
320
|
+
// Border red, background blue, so the decoder can read the display area's
|
|
321
|
+
// top edge (text row 0) off the same PNG instead of assuming it; the screen
|
|
322
|
+
// is cleared first so a lit cell can only be a result.
|
|
323
|
+
//
|
|
324
|
+
// Build PAL: java -jar KickAss.jar tod6.asm -o tod6_pal.prg
|
|
325
|
+
// Build NTSC: java -jar KickAss.jar tod6.asm -define NTSC -o tod6_ntsc.prg
|
|
326
|
+
// (NTSC: CRA bit 7 = 0 so the TOD divides 60 Hz; frame counts rescaled
|
|
327
|
+
// to the 6567R8's 17,095-cycle frame so the seconds waited stay the same)
|
|
328
|
+
|
|
329
|
+
#if NTSC
|
|
330
|
+
.const F03 = 18 // ~0.30 s
|
|
331
|
+
.const F06 = 40 // ~0.67 s
|
|
332
|
+
.const F12 = 72 // ~1.20 s
|
|
333
|
+
.const F15 = 90 // ~1.50 s
|
|
334
|
+
#else
|
|
335
|
+
.const F03 = 15 // ~0.30 s
|
|
336
|
+
.const F06 = 33 // ~0.66 s
|
|
337
|
+
.const F12 = 60 // ~1.20 s
|
|
338
|
+
.const F15 = 75 // ~1.50 s
|
|
339
|
+
#endif
|
|
340
|
+
|
|
341
|
+
.label col = $02
|
|
342
|
+
.label lo = $03
|
|
343
|
+
.label hi = $04
|
|
344
|
+
|
|
345
|
+
* = $0801
|
|
346
|
+
.byte $0b,$08,$0a,$00,$9e,$32,$30,$36,$31,$00,$00,$00 // 10 SYS2061
|
|
347
|
+
* = $080d
|
|
348
|
+
sei
|
|
349
|
+
lda #$02
|
|
350
|
+
sta $d020 // border red
|
|
351
|
+
lda #$06
|
|
352
|
+
sta $d021 // background blue
|
|
353
|
+
ldx #$00
|
|
354
|
+
lda #$20
|
|
355
|
+
cls: sta $0400,x // clear the screen: the boot banner and the typed RUN
|
|
356
|
+
sta $0500,x // would otherwise sit on rows 1, 3, 5 and 6
|
|
357
|
+
sta $0600,x
|
|
358
|
+
sta $0700,x
|
|
359
|
+
inx
|
|
360
|
+
bne cls
|
|
361
|
+
#if NTSC
|
|
362
|
+
lda $dc0e
|
|
363
|
+
and #$7f // TODIN = 0: 60 Hz
|
|
364
|
+
sta $dc0e
|
|
365
|
+
#else
|
|
366
|
+
lda $dc0e
|
|
367
|
+
ora #$80 // TODIN = 1: 50 Hz
|
|
368
|
+
sta $dc0e
|
|
369
|
+
#endif
|
|
370
|
+
lda $dc0f
|
|
371
|
+
and #$7f // ALARM = 0: TOD writes go to the clock
|
|
372
|
+
sta $dc0f
|
|
373
|
+
|
|
374
|
+
// D: live tenths at ~0.3 s (control)
|
|
375
|
+
jsr reset_tod
|
|
376
|
+
ldx #F03
|
|
377
|
+
jsr waitx
|
|
378
|
+
lda $dc08
|
|
379
|
+
ldy #18
|
|
380
|
+
jsr plot
|
|
381
|
+
|
|
382
|
+
// A: latch test — hours read at ~0.3 s, tenths read at ~1.5 s
|
|
383
|
+
jsr reset_tod
|
|
384
|
+
ldx #F03
|
|
385
|
+
jsr waitx
|
|
386
|
+
lda $dc0b // hours read: datasheet says this latches all four
|
|
387
|
+
ldx #F12
|
|
388
|
+
jsr waitx // now ~1.5 s
|
|
389
|
+
lda $dc08
|
|
390
|
+
ldy #12
|
|
391
|
+
jsr plot
|
|
392
|
+
lda $dc08 // release, in case the latch is still held
|
|
393
|
+
|
|
394
|
+
// C: live tenths at ~1.5 s (control)
|
|
395
|
+
jsr reset_tod
|
|
396
|
+
ldx #F15
|
|
397
|
+
jsr waitx
|
|
398
|
+
lda $dc08
|
|
399
|
+
ldy #16
|
|
400
|
+
jsr plot
|
|
401
|
+
|
|
402
|
+
// B: halt test — clock running, then hours written LAST
|
|
403
|
+
jsr reset_tod // ends with the tenths write: running
|
|
404
|
+
lda #$00
|
|
405
|
+
sta $dc0b // hours written after tenths
|
|
406
|
+
ldx #F15
|
|
407
|
+
jsr waitx
|
|
408
|
+
lda $dc08
|
|
409
|
+
ldy #14
|
|
410
|
+
jsr plot
|
|
411
|
+
|
|
412
|
+
// E: alarm-select test — the registers page's "set the clock" row
|
|
413
|
+
jsr reset_tod // running from 00:00:00.0
|
|
414
|
+
ldx #F15
|
|
415
|
+
jsr waitx // clock reads ~1.5 s
|
|
416
|
+
lda $dc0f
|
|
417
|
+
ora #$80 // ALARM = 1
|
|
418
|
+
sta $dc0f
|
|
419
|
+
lda #$00
|
|
420
|
+
sta $dc0b // "set the clock to 00:00:00.0"
|
|
421
|
+
sta $dc0a
|
|
422
|
+
sta $dc09
|
|
423
|
+
sta $dc08
|
|
424
|
+
lda $dc0f
|
|
425
|
+
and #$7f // ALARM = 0 again
|
|
426
|
+
sta $dc0f
|
|
427
|
+
lda $dc08 // if the clock had been set this reads 0
|
|
428
|
+
ldy #20
|
|
429
|
+
jsr plot
|
|
430
|
+
|
|
431
|
+
// G: still-running test — E again, then wait before reading
|
|
432
|
+
jsr reset_tod // running from 00:00:00.0
|
|
433
|
+
ldx #F15
|
|
434
|
+
jsr waitx // clock reads ~1.5 s
|
|
435
|
+
lda $dc0f
|
|
436
|
+
ora #$80 // ALARM = 1
|
|
437
|
+
sta $dc0f
|
|
438
|
+
lda #$00
|
|
439
|
+
sta $dc0b
|
|
440
|
+
sta $dc0a
|
|
441
|
+
sta $dc09
|
|
442
|
+
sta $dc08
|
|
443
|
+
lda $dc0f
|
|
444
|
+
and #$7f // ALARM = 0 again
|
|
445
|
+
sta $dc0f
|
|
446
|
+
ldx #F03
|
|
447
|
+
jsr waitx // a further ~0.3 s
|
|
448
|
+
lda $dc08 // still running: ~1.8 s -> 8 or 7; stopped by the writes: 4 or 5
|
|
449
|
+
ldy #10
|
|
450
|
+
jsr plot
|
|
451
|
+
|
|
452
|
+
// H: alarm-hours halt test — one alarm-side hours write, nothing after it
|
|
453
|
+
jsr reset_tod // running from 00:00:00.0
|
|
454
|
+
ldx #F03
|
|
455
|
+
jsr waitx // ~0.3 s
|
|
456
|
+
lda $dc0f
|
|
457
|
+
ora #$80 // ALARM = 1
|
|
458
|
+
sta $dc0f
|
|
459
|
+
lda #$00
|
|
460
|
+
sta $dc0b // hours, alarm side; no tenths write follows
|
|
461
|
+
lda $dc0f
|
|
462
|
+
and #$7f // ALARM = 0 again
|
|
463
|
+
sta $dc0f
|
|
464
|
+
ldx #F15
|
|
465
|
+
jsr waitx // ~1.5 s more
|
|
466
|
+
lda $dc08 // still running: ~1.8 s -> 8 or 7; stopped at the write: 2 or 3
|
|
467
|
+
ldy #8
|
|
468
|
+
jsr plot
|
|
469
|
+
|
|
470
|
+
// I: restart-by-read test — does reading tenths restart a write-stopped clock?
|
|
471
|
+
jsr reset_tod // running from 00:00:00.0
|
|
472
|
+
lda #$00
|
|
473
|
+
sta $dc0b // hours written: clock stops at 0
|
|
474
|
+
ldx #F03
|
|
475
|
+
jsr waitx // ~0.3 s
|
|
476
|
+
lda $dc08 // tenths read while stopped (reads 0 either way)
|
|
477
|
+
ldx #F15
|
|
478
|
+
jsr waitx // ~1.5 s more
|
|
479
|
+
lda $dc08 // 0 if only a tenths WRITE restarts it; 4 or 5 if that read did
|
|
480
|
+
ldy #6
|
|
481
|
+
jsr plot
|
|
482
|
+
|
|
483
|
+
// F: latch then two back-to-back tenths reads
|
|
484
|
+
jsr reset_tod // running from 00:00:00.0
|
|
485
|
+
lda $dc0b // hours read at once: latched tenths = 0
|
|
486
|
+
ldx #F06
|
|
487
|
+
jsr waitx // ~0.66 s
|
|
488
|
+
lda $dc08 // first tenths read: the latched value
|
|
489
|
+
and #$0f
|
|
490
|
+
pha
|
|
491
|
+
lda $dc08 // second tenths read: live, latch released
|
|
492
|
+
ldy #24
|
|
493
|
+
jsr plot
|
|
494
|
+
pla
|
|
495
|
+
ldy #22
|
|
496
|
+
jsr plot
|
|
497
|
+
loop: jmp loop
|
|
498
|
+
|
|
499
|
+
// A = raw register value, Y = screen row (0-24). Reverse-space at column (A & 15).
|
|
500
|
+
plot: and #$0f
|
|
501
|
+
sta col
|
|
502
|
+
lda #<$0400
|
|
503
|
+
sta lo
|
|
504
|
+
lda #>$0400
|
|
505
|
+
sta hi
|
|
506
|
+
tya
|
|
507
|
+
beq pdone
|
|
508
|
+
tax
|
|
509
|
+
padd: lda lo
|
|
510
|
+
clc
|
|
511
|
+
adc #40
|
|
512
|
+
sta lo
|
|
513
|
+
bcc pnext
|
|
514
|
+
inc hi
|
|
515
|
+
pnext: dex
|
|
516
|
+
bne padd
|
|
517
|
+
pdone: ldy col
|
|
518
|
+
lda #$a0
|
|
519
|
+
sta (lo),y
|
|
520
|
+
rts
|
|
521
|
+
|
|
522
|
+
reset_tod:
|
|
523
|
+
lda #$00
|
|
524
|
+
sta $dc0b // hours first: stops the clock
|
|
525
|
+
sta $dc0a
|
|
526
|
+
sta $dc09
|
|
527
|
+
sta $dc08 // tenths last: starts it
|
|
528
|
+
rts
|
|
529
|
+
|
|
530
|
+
// wait X frames, synchronising to raster line $FF each time
|
|
531
|
+
waitx:
|
|
532
|
+
w1: lda #$ff
|
|
533
|
+
w2: cmp $d012
|
|
534
|
+
bne w2
|
|
535
|
+
w3: cmp $d012
|
|
536
|
+
beq w3
|
|
537
|
+
dex
|
|
538
|
+
bne w1
|
|
539
|
+
rts
|
|
540
|
+
```
|
|
541
|
+
|
|
542
|
+
Run with
|
|
543
|
+
`x64sc -default -warp +sound -autostartprgmode 1 -limitcycles 20000000 -exitscreenshot out.png -autostart tod6_pal.prg`
|
|
544
|
+
(add `-model ntsc` and build with `-define NTSC` for the NTSC column),
|
|
545
|
+
then find the single lit eight-pixel cell on each of rows 6, 8, 10, 12,
|
|
546
|
+
14, 16, 18, 20, 22 and 24 and read its column. The NTSC frame counts
|
|
547
|
+
are for the 6567R8 (65 × 263 = 17,095 cycles a frame, 59.83 Hz); on a
|
|
548
|
+
6567R56A (64 × 262 = 16,768 cycles, 60.99 Hz) the same seconds would
|
|
549
|
+
need 18, 41, 73 and 91 frames — arithmetic from the settled line
|
|
550
|
+
counts, rung 3, not run.
|
|
551
|
+
|
|
552
|
+
### Cross-references
|
|
553
|
+
|
|
554
|
+
- `hardware/cia-reference.md` — `$DC08`, `$DC0B` and `$DC0F`
|
|
555
|
+
(corrected 2026-09-21 to match this entry), `$DC0E` bit 7 (TODIN),
|
|
556
|
+
and the "TOD latch on read" bullet in its Pitfalls list.
|
|
557
|
+
- `hardware/c64-registers-reference.md` — the CIA1 quick-lookup rows
|
|
558
|
+
"Read the TOD clock" and "Set the TOD clock" (the second corrected
|
|
559
|
+
2026-09-21), and the "TOD clock latching" bullet in its Pitfalls,
|
|
560
|
+
which had the read side right all along.
|
|
561
|
+
- Register `DC0E` — TODIN, bit 7, which the KERNAL leaves clear; the
|
|
562
|
+
same is true of CIA2's `DD0E` (its `hardware/cia-reference.md` entry
|
|
563
|
+
said otherwise until 2026-09-22).
|
|
564
|
+
- `pitfalls/input.md` — the other CIA1 pitfall family (shared pins).
|
|
565
|
+
- `recipes/kickassembler/tod-alarm.md` — sets the clock and the alarm
|
|
566
|
+
in this order, reads the four registers in the handler in this order,
|
|
567
|
+
sets TODIN from the detected model, and quotes the drift with the bit
|
|
568
|
+
wrong (this bullet said no recipe used the TOD clock until
|
|
569
|
+
2026-09-23). Technique: `tod_alarm_interrupt`,
|
|
570
|
+
`techniques/cpu-cycle-tricks.md`.
|
|
571
|
+
|
|
572
|
+
### Sources
|
|
573
|
+
|
|
574
|
+
- *6526 Complex Interface Adapter (CIA)* datasheet, Commodore
|
|
575
|
+
Semiconductor Group / MOS Technology — "Time of Day Clock (TOD)" and
|
|
576
|
+
"Control Registers" sections. Read in the re-typeset copy
|
|
577
|
+
`mos_6526_cia_recreated.pdf` at 6502.org
|
|
578
|
+
(https://6502.org/documents/datasheets/mos/mos_6526_cia_recreated.pdf);
|
|
579
|
+
that copy carries no date. Paraphrased, not quoted. The server
|
|
580
|
+
answers a bare `curl` with 404 and a browser user-agent string with
|
|
581
|
+
200 (checked 2026-09-22; the same file either way), so a link checker
|
|
582
|
+
will call this URL dead when it is not.
|
|
583
|
+
- VICE 3.10, `x64sc` — the instrument for every measured digit above;
|
|
584
|
+
and `vice/src/core/ciacore.c` (VICE-Team `svn-mirror` on GitHub),
|
|
585
|
+
read to learn how the emulator schedules TOD ticks, and re-read on
|
|
586
|
+
2026-09-22 for the alarm case: its store routine sets the stopped
|
|
587
|
+
flag on an hours write, and clears it on a tenths write, only inside
|
|
588
|
+
the branch taken when CRB bit 7 is clear, which is why rows G and H
|
|
589
|
+
come out as they do. That reading is about the instrument, not about
|
|
590
|
+
a chip.
|
|
591
|
+
- C64 KERNAL ROM 901227-03 (image shipped with VICE) — bytes at
|
|
592
|
+
`$FDA3-$FDBC` and `$FF6E-$FF7C` for what the KERNAL writes to `$DC0E`,
|
|
593
|
+
and `$F02E-$F030`, `$F047-$F049` and `$FDB3` for the three stores to
|
|
594
|
+
`$DD0E`.
|
|
595
|
+
- C64-Wiki, "CIA" (https://www.c64-wiki.com/wiki/CIA) — consulted for
|
|
596
|
+
its register-bit wording; it agrees with the datasheet on CRB bit 7
|
|
597
|
+
and on the hours-write stop, but its `$DC0B` text has the stopped
|
|
598
|
+
clock restart on a *read* of the tenths register where the datasheet
|
|
599
|
+
says a write. Row I above measured that difference in VICE and the
|
|
600
|
+
datasheet is what the emulator does; do not "correct" this page
|
|
601
|
+
toward the wiki on that point.
|
|
602
|
+
|
|
603
|
+
---
|
|
604
|
+
|
|
605
|
+
## kernal_nmi_handler_runs_stop_check — The KERNAL NMI handler warm-starts BASIC on RUN/STOP+RESTORE, and a handler that skips the $DD0D read locks every NMI out
|
|
606
|
+
|
|
607
|
+
**Severity:** high
|
|
608
|
+
**Region:** both
|
|
609
|
+
**Triggered by registers:** DD0D
|
|
610
|
+
**Triggered by techniques:** nmi_handler_and_restore_key
|
|
611
|
+
**Mitigated by techniques:** nmi_handler_and_restore_key
|
|
612
|
+
|
|
613
|
+
### Symptom
|
|
614
|
+
|
|
615
|
+
Two failures with opposite causes.
|
|
616
|
+
|
|
617
|
+
1. **The game is gone.** The player presses RUN/STOP+RESTORE, by
|
|
618
|
+
accident or on purpose, and the screen clears to `READY.` with the
|
|
619
|
+
program's code still in memory. The program never touched `$0318`.
|
|
620
|
+
Its interrupt vector, its `$01` setting and its VIC mode were all put
|
|
621
|
+
back to the KERNAL's defaults on the way.
|
|
622
|
+
2. **The tick stopped.** A program with its own NMI handler, driven by a
|
|
623
|
+
CIA2 timer, plays or counts exactly once and then never again. The
|
|
624
|
+
timer is still running; `$DD0D` reads `$81` if anything reads it at
|
|
625
|
+
all. RESTORE does nothing either. The handler ends in `RTI` without
|
|
626
|
+
having read `$DD0D`.
|
|
627
|
+
|
|
628
|
+
### Mechanism
|
|
629
|
+
|
|
630
|
+
This entry sits in the CIA pitfalls because both halves turn on one
|
|
631
|
+
register access, the read of `$DD0D`, and on what CIA2's interrupt
|
|
632
|
+
output does around it. The KERNAL's dispatch through the vector and the
|
|
633
|
+
per-board wiring of the key are in `pitfalls/kernal-and-io.md`,
|
|
634
|
+
`restore_nmi_not_maskable`; this entry is the short form for a program
|
|
635
|
+
that is installing a handler. The technique is on both lines above
|
|
636
|
+
because the pitfall arises inside a naive version of it (no vector
|
|
637
|
+
taken, or a handler with no `$DD0D` read) and the correct version cures
|
|
638
|
+
it.
|
|
639
|
+
|
|
640
|
+
**The STOP check.** The KERNAL's handler at `$FE47` (the default
|
|
641
|
+
`$0318` target) reads `$DD0D` and branches on bit 7. Set means a CIA2
|
|
642
|
+
source: it runs the RS-232 code. Clear means the NMI came from
|
|
643
|
+
somewhere else, and the only somewhere else is the RESTORE key. It then
|
|
644
|
+
samples the keyboard row that holds RUN/STOP through `$F6BC` and tests
|
|
645
|
+
it through `$FFE1`; if the key is down it falls into `$FE66`: RESTOR,
|
|
646
|
+
IOINIT, CINT and `JMP ($A002)`, the BASIC warm start. There is no flag a
|
|
647
|
+
program can set to opt out; the decision is made from the CIA2 flag
|
|
648
|
+
being absent, which is exactly the state a RESTORE press produces. This
|
|
649
|
+
is from the ROM bytes of `kernal-901227-03.bin`, read for
|
|
650
|
+
`restore_nmi_not_maskable`; the warm-start branch itself needs RUN/STOP
|
|
651
|
+
held and was not run headless.
|
|
652
|
+
|
|
653
|
+
**The lock.** CIA2 drives the 6510's `/NMI` pin, and holds it low while
|
|
654
|
+
any enabled flag in its interrupt control register is set. A read of
|
|
655
|
+
`$DD0D` clears every flag and lets the pin rise. The 6510 takes an NMI
|
|
656
|
+
on the falling edge of the pin and not on its level, so a handler that
|
|
657
|
+
returns without the read leaves `/NMI` low, and no later event on the
|
|
658
|
+
pin, timer or key, can make an edge. Measured in VICE x64sc 3.10 on PAL
|
|
659
|
+
and NTSC: CIA2 Timer A ticking every 10,000 cycles for 1,005,000 cycles
|
|
660
|
+
entered a handler that reads `$DD0D` 100 times and a handler that does
|
|
661
|
+
not exactly once; a single `LDA $DD0D` from the main loop then bought
|
|
662
|
+
exactly one more entry before the second handler locked the line again
|
|
663
|
+
(`recipes/kickassembler/nmi-timer-tick.md`).
|
|
664
|
+
|
|
665
|
+
### Fix
|
|
666
|
+
|
|
667
|
+
For the first symptom, install a handler at `$0318` before the game
|
|
668
|
+
starts. One `RTI` is a complete handler; `$FE43` runs only `SEI` before
|
|
669
|
+
the vector and pushes nothing. Reinstall it after anything that calls
|
|
670
|
+
RESTOR (`$FF8A`) or VECTOR (`$FF8D`) with an old table, since both put
|
|
671
|
+
`$FE47` back.
|
|
672
|
+
|
|
673
|
+
For the second, read `$DD0D` in every handler that services a CIA2
|
|
674
|
+
source. `BIT $DD0D` costs 4 cycles and touches no register. Do the read
|
|
675
|
+
only when the handler is meant to consume the event: a RESTORE-only
|
|
676
|
+
stub that also reads `$DD0D` discards a timer or RS-232 flag that
|
|
677
|
+
arrived in the same instant.
|
|
678
|
+
|
|
679
|
+
A program that wants both, a live CIA2 tick and a dead RESTORE key,
|
|
680
|
+
takes the vector, acknowledges in the handler, and either ignores the
|
|
681
|
+
extra entry a press produces or tests bit 7 of `$DD0D` before acting.
|
|
682
|
+
|
|
683
|
+
### Worked example
|
|
684
|
+
|
|
685
|
+
The handler that locks. It counts one tick and then nothing, and the
|
|
686
|
+
key is dead with it:
|
|
687
|
+
|
|
688
|
+
```asm
|
|
689
|
+
// LOCKS AFTER ONE TICK: no read of $DD0D, so /NMI never rises again.
|
|
690
|
+
nmi_bad:
|
|
691
|
+
inc tick_count
|
|
692
|
+
rti
|
|
693
|
+
|
|
694
|
+
tick_count:
|
|
695
|
+
.byte $00
|
|
696
|
+
```
|
|
697
|
+
|
|
698
|
+
The same handler with the acknowledge. Every tick is taken, and a
|
|
699
|
+
RESTORE press is one extra entry the counter also sees:
|
|
700
|
+
|
|
701
|
+
```asm
|
|
702
|
+
// Correct: the read clears the CIA2 flag and re-arms the edge.
|
|
703
|
+
nmi_good:
|
|
704
|
+
inc tick_count
|
|
705
|
+
bit $dd0d
|
|
706
|
+
rti
|
|
707
|
+
|
|
708
|
+
tick_count:
|
|
709
|
+
.byte $00
|
|
710
|
+
```
|
|
711
|
+
|
|
712
|
+
For a program with no CIA2 use, the stub that disarms the key; it costs
|
|
713
|
+
20 cycles per press and nothing between presses (measured in VICE x64sc
|
|
714
|
+
3.10, `recipes/kickassembler/nmi-timer-tick.md`):
|
|
715
|
+
|
|
716
|
+
```asm
|
|
717
|
+
install_stub:
|
|
718
|
+
sei
|
|
719
|
+
lda #<nmi_stub
|
|
720
|
+
sta $0318
|
|
721
|
+
lda #>nmi_stub
|
|
722
|
+
sta $0319
|
|
723
|
+
cli
|
|
724
|
+
rts
|
|
725
|
+
|
|
726
|
+
nmi_stub:
|
|
727
|
+
rti
|
|
728
|
+
```
|
|
729
|
+
|
|
730
|
+
### Cross-references
|
|
731
|
+
|
|
732
|
+
- Technique `nmi_handler_and_restore_key` (`techniques/cpu-cycle-tricks.md`),
|
|
733
|
+
the entry this pitfall guards and the one that fixes it: the vector,
|
|
734
|
+
the acknowledge and the tick, with the measurements.
|
|
735
|
+
- Pitfall `restore_nmi_not_maskable` (`pitfalls/kernal-and-io.md`), the
|
|
736
|
+
long form: the wiring of the key, the KERNAL path byte by byte, the
|
|
737
|
+
189-cycle cost of a press with the KERNAL handler in place, and RESTOR
|
|
738
|
+
and VECTOR undoing the vector.
|
|
739
|
+
- `hardware/cia-reference.md`, `$DD0D` and "NMI vector (CIA2 +
|
|
740
|
+
RESTORE)".
|
|
741
|
+
- `hardware/c64-memory-map.md`, `$0318-$0319` and `$FE43-$FF42`.
|
|
742
|
+
- Recipe `recipes/kickassembler/nmi-timer-tick.md`.
|
|
743
|
+
|
|
744
|
+
### Sources
|
|
745
|
+
|
|
746
|
+
- Commodore 64 KERNAL ROM 901227-03 (`kernal-901227-03.bin` as shipped
|
|
747
|
+
with VICE), as read for `restore_nmi_not_maskable`; not re-read for
|
|
748
|
+
this entry.
|
|
749
|
+
- VICE x64sc 3.10, the pinned runs of `nmi-timer-tick.prg` on PAL and
|
|
750
|
+
NTSC, screen cells decoded against `chargen-901225-01.bin`.
|
|
751
|
+
- C64-Wiki, "RESTORE (Key)", https://www.c64-wiki.com/wiki/RESTORE_(Key),
|
|
752
|
+
consulted for the name of the key's connection to the CPU only.
|
|
753
|
+
|
|
754
|
+
---
|
|
755
|
+
|
|
756
|
+
## cia_revision_irq_one_cycle_late — An old 6526 raises its timer interrupt one cycle later than a 6526A or 8521
|
|
757
|
+
|
|
758
|
+
**Severity:** medium
|
|
759
|
+
**Region:** both
|
|
760
|
+
**Triggered by registers:** DC04, DC05, DC0D, DC0E, DD0D
|
|
761
|
+
**Triggered by techniques:** double_irq
|
|
762
|
+
|
|
763
|
+
### Symptom
|
|
764
|
+
|
|
765
|
+
Code whose timing is set by a CIA timer interrupt is right on one
|
|
766
|
+
machine and one cycle out on another, with nothing else changed.
|
|
767
|
+
|
|
768
|
+
- A raster routine that uses a CIA timer to place its second interrupt
|
|
769
|
+
a counted number of cycles after the first (the "CIA timer second
|
|
770
|
+
stage" of `double_irq`) lands one cycle late on an older board: a
|
|
771
|
+
colour split moves eight pixels right, or a routine that was stable
|
|
772
|
+
jitters by one cycle on every frame.
|
|
773
|
+
- A loader whose receive loop is tuned so that a timer interrupt or a
|
|
774
|
+
timer-polled read falls in a fixed bit cell misses the cell on the
|
|
775
|
+
other revision and reads a wrong bit, so the same disk that loads on
|
|
776
|
+
the development machine fails on another.
|
|
777
|
+
|
|
778
|
+
Which machine is "right" is whichever the code was tuned on. Two
|
|
779
|
+
revisions of the chip shipped in the C64 and both are common.
|
|
780
|
+
|
|
781
|
+
### Mechanism
|
|
782
|
+
|
|
783
|
+
The C64 carried two CIA revisions over its life: the original 6526,
|
|
784
|
+
and the later 6526A, which the 8521 in the C64C is an HMOS version of
|
|
785
|
+
(board history from the C64-Wiki CIA page, which says only that later
|
|
786
|
+
boards "may also use the 6526A or the 8521"; not verified here). On a
|
|
787
|
+
timer underflow every revision sets the timer's bit in the interrupt
|
|
788
|
+
control register in the same cycle. What differs is when `/IRQ` (or
|
|
789
|
+
`/NMI` for CIA2) follows: the old 6526 pulls the pin one cycle after
|
|
790
|
+
the flag, the newer parts in the same cycle. The 6510 samples the
|
|
791
|
+
interrupt line once per instruction, so a one-cycle change on the pin
|
|
792
|
+
either changes nothing or moves the handler's entry by the length of
|
|
793
|
+
the instruction that was running.
|
|
794
|
+
|
|
795
|
+
VICE 3.10 models this. `x64sc -help` lists `-ciamodel <0|1>` (both
|
|
796
|
+
chips), `-cia1model` and `-cia2model`, with 0 the "old 6526" and 1 the
|
|
797
|
+
"new 8521"; a run with no flag behaved as model 1 here. In the
|
|
798
|
+
emulator's source (`src/core/ciacore.c`, `cia_run_ifr_cycle`, read at
|
|
799
|
+
the VICE-Team GitHub mirror) the old-model branch always schedules the
|
|
800
|
+
interrupt raise one cycle on, and the new-model branch raises it in
|
|
801
|
+
the same cycle unless the ICR was read in the cycle before. That
|
|
802
|
+
source is the named authority for the real-hardware claim; no 6526 was
|
|
803
|
+
put on a bench for this entry, and the one-cycle figure for hardware
|
|
804
|
+
stands at rung 4.
|
|
805
|
+
|
|
806
|
+
Measured in VICE x64sc 3.10, the difference is exactly what the model
|
|
807
|
+
says. A probe starts CIA1 Timer B free-running from `$FFFF` and, four
|
|
808
|
+
cycles later, Timer A one-shot from latch `$40 + k` for eight phases
|
|
809
|
+
`k`, then executes 400 cycles of 2-cycle `NOP`s. The handler's first
|
|
810
|
+
instruction is `lda $DC06`. The screen shows `E - N`, where `E` is
|
|
811
|
+
`$FFFF` less the Timer B value read and `N` the Timer A latch; the
|
|
812
|
+
display is blanked (`$D011` bit 4 clear) for the whole test so no
|
|
813
|
+
badline can move anything, and the KERNAL is banked out so `$FFFE`
|
|
814
|
+
reaches the handler directly. Same figures on PAL and NTSC:
|
|
815
|
+
|
|
816
|
+
| `-ciamodel` | k = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
|
|
817
|
+
|---|---|---|---|---|---|---|---|---|
|
|
818
|
+
| 0, old 6526 | $12 | $11 | $12 | $11 | $12 | $11 | $12 | $11 |
|
|
819
|
+
| 1, new 8521 | $10 | $11 | $10 | $11 | $10 | $11 | $10 | $11 |
|
|
820
|
+
| none (default) | $10 | $11 | $10 | $11 | $10 | $11 | $10 | $11 |
|
|
821
|
+
|
|
822
|
+
`-cia1model 0 -cia2model 1` reads as the old row and `-cia1model 1
|
|
823
|
+
-cia2model 0` as the new row, so the flag for the chip under test is
|
|
824
|
+
the one that matters. At every even phase the old part's handler runs
|
|
825
|
+
two cycles later than the new part's, at every odd phase at the same
|
|
826
|
+
cycle; over the two phases the mean difference is one cycle, and the
|
|
827
|
+
alternation is the `NOP` boundary, not the chip. A control read of Timer B eight cycles after
|
|
828
|
+
its start write returned `$FFFA` on both models, so the timers' own
|
|
829
|
+
start delay does not differ between them. Subtracting the fixed parts
|
|
830
|
+
of the path (Timer A's four-cycle later start, the `N + 1` counts to
|
|
831
|
+
underflow, the three cycles from the handler's opcode fetch to the
|
|
832
|
+
`lda`'s read) puts the new part's underflow-to-handler-fetch at 8 or 9
|
|
833
|
+
cycles and the old part's at 9 or 10; that subtraction is arithmetic on
|
|
834
|
+
the timer pipeline, rung 3, and the difference between the rows is the
|
|
835
|
+
measurement.
|
|
836
|
+
|
|
837
|
+
Not measured here: CIA2 and `/NMI` (the source has one path for both
|
|
838
|
+
chips, so the same one-cycle rule is expected, rung 4), the Timer B
|
|
839
|
+
bug VICE also gates on the old model (an ICR read in the cycle before
|
|
840
|
+
a Timer B underflow loses the flag), and any real chip.
|
|
841
|
+
|
|
842
|
+
### Fix
|
|
843
|
+
|
|
844
|
+
Two remedies; use both.
|
|
845
|
+
|
|
846
|
+
1. **Detect the revision once at start and branch on it.** Run the
|
|
847
|
+
two-phase form of the probe with interrupts otherwise off, before
|
|
848
|
+
any timing-critical code is installed, and store a byte: `00` old
|
|
849
|
+
6526, `01` 6526A or 8521. `recipes/kickassembler/cia-revision-detect.md`
|
|
850
|
+
is that routine, verdict at `$02FE`. A timer-timed second stage then
|
|
851
|
+
loads a latch one count shorter on the old part, or the loader picks
|
|
852
|
+
the delay table for the part it found. The detection is a short
|
|
853
|
+
routine and costs a little over two raster frames.
|
|
854
|
+
2. **Do not put a bit cell or a pixel on a one-cycle edge.** Where a
|
|
855
|
+
timer interrupt sets the phase, follow it with a raster-synchronised
|
|
856
|
+
entry (`stable_raster_irq`) or a self-timing sequence so the CIA's
|
|
857
|
+
delay is absorbed; where a loader polls a timer, centre the read in
|
|
858
|
+
the bit cell rather than at its edge. A routine that tolerates one
|
|
859
|
+
cycle either way is right on both revisions without detection.
|
|
860
|
+
|
|
861
|
+
### Worked example
|
|
862
|
+
|
|
863
|
+
The bad pattern: a second-stage interrupt whose latch was tuned on one
|
|
864
|
+
machine.
|
|
865
|
+
|
|
866
|
+
```text
|
|
867
|
+
; first IRQ, raster-triggered, jitter up to 7 cycles
|
|
868
|
+
lda #<DELAY ; a value found by trial on the author's C64
|
|
869
|
+
sta $dc04
|
|
870
|
+
lda #>DELAY
|
|
871
|
+
sta $dc05
|
|
872
|
+
lda #$19 ; one-shot, load, start
|
|
873
|
+
sta $dc0e
|
|
874
|
+
; ... second handler writes $D020 at "the" cycle
|
|
875
|
+
```
|
|
876
|
+
|
|
877
|
+
On the other revision the write lands one cycle away from where it was
|
|
878
|
+
tuned, and because the 6510 quantises the entry to an instruction
|
|
879
|
+
boundary the visible error is 0 or 2 cycles depending on which
|
|
880
|
+
instruction the interrupt met.
|
|
881
|
+
|
|
882
|
+
The fix, in outline (the full routine is the recipe):
|
|
883
|
+
|
|
884
|
+
```text
|
|
885
|
+
; at start-up, interrupts masked, display blanked
|
|
886
|
+
; run the probe at latch $40 and $41; E-N pairs:
|
|
887
|
+
; $12,$11 -> old 6526 -> revision = 0
|
|
888
|
+
; $10,$11 -> 6526A / 8521 -> revision = 1
|
|
889
|
+
; later:
|
|
890
|
+
lda revision
|
|
891
|
+
beq old_part
|
|
892
|
+
lda #<DELAY
|
|
893
|
+
bne set_latch
|
|
894
|
+
old_part:
|
|
895
|
+
lda #<DELAY-1 ; the old part raises one cycle later
|
|
896
|
+
set_latch:
|
|
897
|
+
sta $dc04
|
|
898
|
+
```
|
|
899
|
+
|
|
900
|
+
Per-model runs of the recipe (VICE x64sc 3.10, `-ciamodel 0` and `1`,
|
|
901
|
+
PAL and NTSC, four runs): row 2 of the screen `12 11` and verdict `OLD
|
|
902
|
+
6526` under model 0, `10 11` and `NEW 8521 OR 6526A` under model 1,
|
|
903
|
+
`$02FF` = `01` and a green border in all four.
|
|
904
|
+
|
|
905
|
+
### Cross-references
|
|
906
|
+
|
|
907
|
+
- Recipe `recipes/kickassembler/cia-revision-detect.md`, the detection
|
|
908
|
+
routine, pinned under `-ciamodel 0`.
|
|
909
|
+
- Technique `double_irq` (`techniques/raster.md`), whose "CIA timer
|
|
910
|
+
second stage" is the raster case above; `stable_raster_irq` in the
|
|
911
|
+
same file is the raster-synchronised entry that absorbs the delay.
|
|
912
|
+
- Techniques `krill_loader_integration` and `sparkle_irq_loader`
|
|
913
|
+
(`techniques/loaders-packers.md`): the loader case. Their pages
|
|
914
|
+
describe finished loaders and nothing in their text turns on a
|
|
915
|
+
cycle-exact timer interrupt, so they are not on the Triggered-by
|
|
916
|
+
line; the pitfall is for a loader written from scratch with a timer
|
|
917
|
+
in its bit loop. Whether either loader detects the revision was not
|
|
918
|
+
checked here.
|
|
919
|
+
- `hardware/cia-reference.md`, "IRQ / NMI on underflow" and the
|
|
920
|
+
"CIA chip revision differences" bullet under its Pitfalls list, which
|
|
921
|
+
names the revisions without a figure; this entry supplies the one
|
|
922
|
+
figure that was measured.
|
|
923
|
+
- Pitfall `raster_irq_first_line_jitter` (`pitfalls/raster-and-badline.md`)
|
|
924
|
+
for the instruction-boundary quantisation that turns one cycle into
|
|
925
|
+
0 or 2.
|
|
926
|
+
|
|
927
|
+
### Sources
|
|
928
|
+
|
|
929
|
+
- VICE x64sc 3.10, the eight-phase probe and the recipe, run under
|
|
930
|
+
`-ciamodel 0`, `-ciamodel 1`, no flag, and the `-cia1model` /
|
|
931
|
+
`-cia2model` pairs, PAL and NTSC, screen cells decoded against
|
|
932
|
+
`chargen-901225-01.bin`.
|
|
933
|
+
- VICE source, `vice/src/core/ciacore.c` at
|
|
934
|
+
https://github.com/VICE-Team/svn-mirror, function `cia_run_ifr_cycle`,
|
|
935
|
+
read for the model branches only.
|
|
936
|
+
- `x64sc -help`, VICE 3.10, for the option names and values.
|
|
937
|
+
- C64-Wiki, "CIA", https://www.c64-wiki.com/wiki/CIA, for the sentence
|
|
938
|
+
on which revisions later boards carried; it gives no timing figure.
|
|
939
|
+
|
|
940
|
+
---
|
|
941
|
+
|
|
942
|
+
## cia_icr_read_clears_all_flags — One read of $DC0D or $DD0D clears every pending flag, not just the one you tested
|
|
943
|
+
|
|
944
|
+
**Severity:** high
|
|
945
|
+
**Region:** both
|
|
946
|
+
**Triggered by registers:** DC0D, DD0D
|
|
947
|
+
**Triggered by techniques:** tape_turbo_loader, nmi_handler_and_restore_key, irq_chain_table, tod_alarm_interrupt, irq_keyboard_own_scan, irq_owns_processor_port
|
|
948
|
+
|
|
949
|
+
### Symptom
|
|
950
|
+
|
|
951
|
+
Two routines share one CIA. Each reads the interrupt control register
|
|
952
|
+
to look for its own event, and one of them never sees it.
|
|
953
|
+
|
|
954
|
+
- A tape or serial routine spins on bit 4 of `$DC0D` waiting for a
|
|
955
|
+
FLAG edge. A timer on the same chip underflows while it spins. The
|
|
956
|
+
routine that later checks the timer bit finds it clear, and its
|
|
957
|
+
timeout, its tick or its bit-cell clock is simply gone.
|
|
958
|
+
- The other way round: a timer poll runs first and a FLAG edge that
|
|
959
|
+
arrived during it is consumed by the timer poll. The FLAG waiter
|
|
960
|
+
then waits for an edge that has already been and gone.
|
|
961
|
+
- On CIA2, a read of `$DD0D` from the main program while a Timer A
|
|
962
|
+
underflow is arriving leaves the NMI handler with nothing to
|
|
963
|
+
dispatch on, and at one phase the NMI itself is not raised: the read
|
|
964
|
+
returns `$01`, the handler never runs, and the tick is lost.
|
|
965
|
+
|
|
966
|
+
Nothing errors. The register just reads `$00` the second time.
|
|
967
|
+
|
|
968
|
+
### Mechanism
|
|
969
|
+
|
|
970
|
+
The 6526's interrupt control register at offset `$0D` is two registers
|
|
971
|
+
at one address. A write sets or clears mask bits. A read returns the
|
|
972
|
+
five event flags in bits 0..4 (Timer A, Timer B, TOD alarm, serial,
|
|
973
|
+
FLAG) with bit 7 set if any flagged event is also enabled in the mask,
|
|
974
|
+
and the read clears all of them together. There is no way to read one
|
|
975
|
+
flag and leave the others standing. The data sheet's word for this is
|
|
976
|
+
that the register is cleared on read; the consequence is that whoever
|
|
977
|
+
reads it first owns every event that had arrived by then.
|
|
978
|
+
|
|
979
|
+
Measured in VICE x64sc 3.10, PAL and NTSC, default CIA model and
|
|
980
|
+
`-ciamodel 0`, same bytes in all three runs unless a row says otherwise.
|
|
981
|
+
Interrupts held off with `SEI` for the CIA1 tests so the flags could
|
|
982
|
+
be read rather than taken; mask bit 7 with bit 0 (`$81`) written so
|
|
983
|
+
the pending bit would show.
|
|
984
|
+
|
|
985
|
+
1. **One read takes both.** CIA1 Timer A one-shot from latch `$0060`,
|
|
986
|
+
then twenty back-to-back reads of `$DC0D` stored to RAM, 16 cycles
|
|
987
|
+
apart. Reads one to six returned `$00`; the seventh returned `$81`
|
|
988
|
+
(bit 7 pending, bit 0 Timer A, bit 4 FLAG clear); reads eight to
|
|
989
|
+
twenty returned `$00`. The underflow was seen once, in a read that
|
|
990
|
+
was looking for FLAG, and never again.
|
|
991
|
+
2. **The losing pattern.** The same timer, then a FLAG poll of twenty
|
|
992
|
+
reads testing only bit 4, then a "has the timer fired" read. The
|
|
993
|
+
check read returned `$00` (bit 0 clear) and a further read straight
|
|
994
|
+
after it `$00`; the control, the same timer with a delay loop that
|
|
995
|
+
reads `$D020` instead of the ICR, returned `$81`. The poll took the
|
|
996
|
+
underflow on its way past and left nothing for the check.
|
|
997
|
+
3. **The fix pattern.** The same poll, but each read is ORed into a
|
|
998
|
+
byte in RAM before bit 4 is tested. After the poll that byte was
|
|
999
|
+
`$81`; the timer check, made on the copy, saw `$01`; a fresh read of
|
|
1000
|
+
`$DC0D` afterwards returned `$00`, so the copy was the only place
|
|
1001
|
+
the event still existed.
|
|
1002
|
+
4. **CIA2, NMI masked.** Timer A of CIA2 one-shot from `$0060` with
|
|
1003
|
+
`$7F` written to `$DD0D` first. After the underflow `$DD0D` read
|
|
1004
|
+
`$01` (flag set, bit 7 clear because nothing was enabled) and the
|
|
1005
|
+
next read `$00`. Masking an interrupt does not stop its flag from
|
|
1006
|
+
being set, and does not stop a read from clearing it.
|
|
1007
|
+
5. **CIA2, NMI enabled, handler reads once.** Vector at `$0318`,
|
|
1008
|
+
handler `LDA $DD0D` into a byte. One NMI counted, the handler's
|
|
1009
|
+
copy `$81`, and a main-program read after it `$00`.
|
|
1010
|
+
6. **Two timers, one read.** CIA1 Timer A and Timer B one-shot from
|
|
1011
|
+
`$0020` together, mask `$83`. The first read of `$DC0D` returned
|
|
1012
|
+
`$83`; the second `$00`.
|
|
1013
|
+
7. **A stray read racing the NMI.** CIA2 Timer A one-shot from latch
|
|
1014
|
+
`$60 + k` for eight phases k while the main program reads `$DD0D`
|
|
1015
|
+
in a 16-cycle loop and the handler of test 5 is installed. PAL,
|
|
1016
|
+
default model: phases 0..3, the main loop read `$81` and the
|
|
1017
|
+
handler read `$00`; phases 4..7, the handler read `$81` and the
|
|
1018
|
+
main loop `$00`; one NMI counted at every phase. NTSC default model
|
|
1019
|
+
and PAL `-ciamodel 0`: the same, except that at phase 3 the main
|
|
1020
|
+
loop read `$01`, the handler read `$00` and **no NMI was counted**.
|
|
1021
|
+
A read that lands in the cycle the chip would raise the line in
|
|
1022
|
+
takes the flag before the line follows it, and bit 7 never sets.
|
|
1023
|
+
Which phase does that depends on where the loop's reads fall
|
|
1024
|
+
against the timer, so treat the phase number as this probe's and
|
|
1025
|
+
the loss as the finding.
|
|
1026
|
+
|
|
1027
|
+
Not measured here: any real 6526 or 8521; a FLAG edge itself (no
|
|
1028
|
+
cassette or serial input was driven, so bit 4 was never set and the
|
|
1029
|
+
lost-FLAG case is the mirror of the lost-timer case, rung 3).
|
|
1030
|
+
|
|
1031
|
+
### Fix
|
|
1032
|
+
|
|
1033
|
+
One reader per interrupt control register, and one read per event.
|
|
1034
|
+
|
|
1035
|
+
1. **Read once into RAM and test the copy.** Whoever polls reads the
|
|
1036
|
+
ICR into a byte, ORs it into a pending byte, and every routine on
|
|
1037
|
+
that chip tests and clears its own bit in the pending byte, never
|
|
1038
|
+
the register. Test 3 above is that pattern; the timer bit survived
|
|
1039
|
+
the FLAG poll in it.
|
|
1040
|
+
2. **Give the register one owner.** If the chip has an interrupt
|
|
1041
|
+
handler, the handler is the only code that reads its ICR. It reads
|
|
1042
|
+
once, stores the byte, and dispatches on the copy. Main-program
|
|
1043
|
+
code that wants to know about a CIA event reads the handler's copy,
|
|
1044
|
+
not the chip. Test 7 is what a second reader costs: at some phases
|
|
1045
|
+
the handler sees nothing, and at one the interrupt itself is gone.
|
|
1046
|
+
3. **Do not read an ICR you do not own to "be safe".** A stray
|
|
1047
|
+
`LDA $DC0D` in a loader's clean-up, or a `BIT $DD0D` added to a
|
|
1048
|
+
routine that is not the NMI handler, acknowledges events that
|
|
1049
|
+
belong to someone else. The KERNAL's own IRQ exit at `$EA7E` reads
|
|
1050
|
+
`$DC0D`; a raster handler that falls through to it while a CIA1
|
|
1051
|
+
timer is in use hands the timer's flag to the KERNAL.
|
|
1052
|
+
|
|
1053
|
+
Setting up is still a read: `$7F` to the ICR and one read to clear
|
|
1054
|
+
whatever was standing is right at install time, when nothing owns the
|
|
1055
|
+
chip yet. The rule is about steady state.
|
|
1056
|
+
|
|
1057
|
+
### Worked example
|
|
1058
|
+
|
|
1059
|
+
The losing pattern, two routines and one register:
|
|
1060
|
+
|
|
1061
|
+
```asm
|
|
1062
|
+
// LOSES THE TIMER: the FLAG poll's read cleared bit 0 on the way past.
|
|
1063
|
+
wait_flag:
|
|
1064
|
+
lda $dc0d
|
|
1065
|
+
and #$10
|
|
1066
|
+
beq wait_flag // measured: an underflow during this loop is gone
|
|
1067
|
+
rts
|
|
1068
|
+
|
|
1069
|
+
check_timer:
|
|
1070
|
+
lda $dc0d // read here: $00 after the poll, $81 without it
|
|
1071
|
+
and #$01
|
|
1072
|
+
rts
|
|
1073
|
+
```
|
|
1074
|
+
|
|
1075
|
+
The fix, one read kept in RAM:
|
|
1076
|
+
|
|
1077
|
+
```asm
|
|
1078
|
+
// Correct: every read is banked into a pending byte; routines test the copy.
|
|
1079
|
+
poll_icr:
|
|
1080
|
+
lda $dc0d // the only read of $DC0D outside install
|
|
1081
|
+
ora pending
|
|
1082
|
+
sta pending
|
|
1083
|
+
rts
|
|
1084
|
+
|
|
1085
|
+
wait_flag_ok:
|
|
1086
|
+
jsr poll_icr
|
|
1087
|
+
lda pending
|
|
1088
|
+
and #$10
|
|
1089
|
+
beq wait_flag_ok
|
|
1090
|
+
lda pending
|
|
1091
|
+
and #$ef // consume FLAG, leave the timer bit
|
|
1092
|
+
sta pending
|
|
1093
|
+
rts
|
|
1094
|
+
|
|
1095
|
+
check_timer_ok:
|
|
1096
|
+
lda pending // measured: $81 after the poll, so bit 0 is here
|
|
1097
|
+
and #$01
|
|
1098
|
+
beq no_tick
|
|
1099
|
+
lda pending
|
|
1100
|
+
and #$fe
|
|
1101
|
+
sta pending
|
|
1102
|
+
no_tick:
|
|
1103
|
+
rts
|
|
1104
|
+
|
|
1105
|
+
pending:
|
|
1106
|
+
.byte $00
|
|
1107
|
+
```
|
|
1108
|
+
|
|
1109
|
+
For a chip with a handler, the handler is `poll_icr`: it reads once,
|
|
1110
|
+
stores, and dispatches on the stored byte; nothing else touches the
|
|
1111
|
+
register. On CIA2 that read is also the acknowledge that lets `/NMI`
|
|
1112
|
+
rise again (`kernal_nmi_handler_runs_stop_check`).
|
|
1113
|
+
|
|
1114
|
+
### Cross-references
|
|
1115
|
+
|
|
1116
|
+
- Pitfall `cia_revision_irq_one_cycle_late` (this page): both models
|
|
1117
|
+
have a one-cycle window in which a read takes the flag before the
|
|
1118
|
+
line follows it, the old part because its raise is a cycle later,
|
|
1119
|
+
the new part when the read lands in the cycle before the raise.
|
|
1120
|
+
Which phase of test 7 falls in that window is a matter of
|
|
1121
|
+
alignment, which is why NTSC default and PAL `-ciamodel 0` lost
|
|
1122
|
+
phase 3 and PAL default lost none of the eight tried.
|
|
1123
|
+
- Pitfall `kernal_nmi_handler_runs_stop_check` (this page) and
|
|
1124
|
+
`restore_nmi_not_maskable` (`pitfalls/kernal-and-io.md`): the
|
|
1125
|
+
opposite failure, a handler that never reads `$DD0D`. The two rules
|
|
1126
|
+
together: exactly one read, in the handler.
|
|
1127
|
+
- Pitfall `tape_bit_is_a_pulse_pair_not_a_pulse` (`pitfalls/loader.md`)
|
|
1128
|
+
and technique `tape_turbo_loader` (`techniques/file-io.md`): the
|
|
1129
|
+
loader whose FLAG spin on bit 4 met this. Its Timer B is read from
|
|
1130
|
+
`$DC06`/`$DC07`, not from the ICR, which is why it survives; a
|
|
1131
|
+
variant that timed out on the Timer B flag would not.
|
|
1132
|
+
- Technique `frame_sync_loop` (`techniques/raster.md`) is not on the
|
|
1133
|
+
Triggered-by line: its text waits on `$D012`, and no ICR read
|
|
1134
|
+
appears in it.
|
|
1135
|
+
- `hardware/cia-reference.md`, the interrupt control register.
|
|
1136
|
+
|
|
1137
|
+
### Sources
|
|
1138
|
+
|
|
1139
|
+
- VICE x64sc 3.10 (windowless build), the seven-test probe above,
|
|
1140
|
+
run PAL default model, NTSC default model, PAL `-ciamodel 0`, screen
|
|
1141
|
+
cells decoded against `chargen-901225-01.bin`. Tests 1 to 4 were
|
|
1142
|
+
repeated in a smaller probe with the results dumped from RAM by a
|
|
1143
|
+
monitor tracepoint, same three runs; the test 2 bytes are that
|
|
1144
|
+
probe's.
|
|
1145
|
+
- MOS 6526 data sheet, interrupt control register: flags cleared on
|
|
1146
|
+
read, bit 7 the pending summary; the bit assignments above are the
|
|
1147
|
+
sheet's.
|