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,1281 @@
|
|
|
1
|
+
# CIA Reference (MOS 6526 — CIA1 and CIA2)
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
The Commodore 64 contains two MOS Technology 6526 Complex Interface Adapter
|
|
6
|
+
(CIA) chips. They are physically and electrically identical — the same mask,
|
|
7
|
+
the same datasheet, the same 16-register interface. What differs is *where*
|
|
8
|
+
each chip is wired into the C64 motherboard, *what* its pins connect to, and
|
|
9
|
+
*which* CPU interrupt line its IRQ output drives.
|
|
10
|
+
|
|
11
|
+
- **CIA1** lives at `$DC00-$DC0F` (with the rest of page `$DC00-$DCFF` being
|
|
12
|
+
16-byte mirrors of the same registers). CIA1 is the user-input chip:
|
|
13
|
+
keyboard matrix, control-port 1 and 2 (joysticks, paddles, light pen),
|
|
14
|
+
and the paddle-fire button lines. Its `/IRQ` output pin drives the 6510
|
|
15
|
+
`/IRQ` line. CIA1 also generates the KERNAL jiffy interrupt — about
|
|
16
|
+
60 Hz on BOTH PAL and NTSC machines, not once per frame — that drives
|
|
17
|
+
the KERNAL `IRQ` handler and the TI/TI$ jiffy clock. (An earlier
|
|
18
|
+
version of this page said 50 Hz on PAL; the ROM and a VICE x64sc
|
|
19
|
+
measurement say otherwise — see `$DC04`.)
|
|
20
|
+
- **CIA2** lives at `$DD00-$DD0F` (with `$DD00-$DDFF` again being 16-byte
|
|
21
|
+
mirrors). CIA2 is the system-interface chip: it picks the 16 KiB VIC-II
|
|
22
|
+
bank (bits 0-1 of port A), drives the serial-IEC bus (disk drive,
|
|
23
|
+
printer) on port A bits 3-7, drives the RS-232 user-port lines, and
|
|
24
|
+
exposes the user-port PB pins. Its `/IRQ` output pin drives the 6510
|
|
25
|
+
`/NMI` line, which the keyboard's `RESTORE` key also pulls low through
|
|
26
|
+
its own one-shot (one half of the 556 dual timer at U20 on the 1982-86
|
|
27
|
+
boards; this page used to give the pulse as "100 ms", a figure that is
|
|
28
|
+
not measured here).
|
|
29
|
+
|
|
30
|
+
The naming is unfortunate: from the 6526's point of view both chips
|
|
31
|
+
generate IRQs — the chip has no idea one of its IRQ outputs is wired to
|
|
32
|
+
the CPU's NMI line. Code that programs CIA2's interrupt-control register
|
|
33
|
+
(`$DD0D`) must remember it is enabling NMIs, not IRQs.
|
|
34
|
+
|
|
35
|
+
Both chips share the same internal layout:
|
|
36
|
+
|
|
37
|
+
- Two 8-bit parallel I/O ports (port A on pins PA0-PA7, port B on pins
|
|
38
|
+
PB0-PB7), each with an independent data-direction register (DDR).
|
|
39
|
+
- Two 16-bit down-counting timers, Timer A and Timer B. Each timer can
|
|
40
|
+
count system phi-2 clocks, rising (positive) edges on the CNT pin,
|
|
41
|
+
Timer A underflows (Timer B only), or Timer A underflows while CNT is
|
|
42
|
+
high (Timer B only). An earlier revision of this bullet said falling
|
|
43
|
+
edges; the CRA/CRB tables and the Count-modes section below were
|
|
44
|
+
already right. The polarity is the 6526 datasheet's, not measured
|
|
45
|
+
here: CNT is a user-port pin, and VICE does not count edges the CIA
|
|
46
|
+
itself puts out in SP-output mode.
|
|
47
|
+
- A 24-bit BCD time-of-day (TOD) clock with hours/minutes/seconds/tenths,
|
|
48
|
+
plus an alarm register that fires on match.
|
|
49
|
+
- An 8-bit synchronous serial shift register (SDR/SP), clocked by CNT.
|
|
50
|
+
The C64 KERNAL uses it on neither chip — its RS-232 driver is
|
|
51
|
+
bit-banged (see `$DD0C`).
|
|
52
|
+
- A single interrupt-control register that masks five interrupt sources
|
|
53
|
+
(Timer A underflow, Timer B underflow, TOD alarm match, serial register
|
|
54
|
+
full/empty, FLAG pin transition) and aggregates them into the chip's
|
|
55
|
+
single `/IRQ` output pin.
|
|
56
|
+
|
|
57
|
+
This document covers both chips in one file because they are the same chip
|
|
58
|
+
with different jobs. Every register section is tagged with
|
|
59
|
+
`**Chip:** CIA1` or `**Chip:** CIA2` so the entity extractor in
|
|
60
|
+
`src/graph/extract.ts` can disambiguate registers that share a name (e.g.
|
|
61
|
+
both chips have a "Data Port A" but at different addresses, with different
|
|
62
|
+
external wiring).
|
|
63
|
+
|
|
64
|
+
### IRQ vs NMI — the critical distinction
|
|
65
|
+
|
|
66
|
+
The 6510 has two interrupt pins, `/IRQ` (maskable) and `/NMI`
|
|
67
|
+
(non-maskable). The 6526 has no idea which of the two it is wired to.
|
|
68
|
+
|
|
69
|
+
| CIA | Address | CPU pin driven | Vector RAM addr | Typical user |
|
|
70
|
+
|------|---------------|----------------|-----------------|-----------------------------------------|
|
|
71
|
+
| CIA1 | `$DC00-$DC0F` | `/IRQ` | `$0314-$0315` | KERNAL 60 Hz jiffy IRQ, keyboard scan |
|
|
72
|
+
| CIA2 | `$DD00-$DD0F` | `/NMI` | `$0318-$0319` | RESTORE key, RS-232 timing |
|
|
73
|
+
|
|
74
|
+
`/IRQ` is maskable with the 6502 `I` flag (`SEI`/`CLI`). `/NMI` is
|
|
75
|
+
**not** maskable — when CIA2's `/IRQ` output goes low, the CPU will
|
|
76
|
+
service an NMI as soon as it finishes the current instruction, regardless
|
|
77
|
+
of `I`. The only way to "mask" an NMI from CIA2 is to disable the
|
|
78
|
+
interrupt-source bits in `$DD0D`.
|
|
79
|
+
|
|
80
|
+
The `RESTORE` key is wired to the CPU's `/NMI` pin *in parallel* with
|
|
81
|
+
CIA2's `/IRQ` output, through a one-shot — one half of the 556 dual
|
|
82
|
+
timer at U20 on the 1982-86 boards (this page used to say "a 555"). This
|
|
83
|
+
means pressing `RESTORE` will fire `/NMI` even with all CIA2 interrupts
|
|
84
|
+
disabled, and any code that uses CIA2 to generate NMIs must coexist
|
|
85
|
+
with the RESTORE wiring.
|
|
86
|
+
|
|
87
|
+
## CIA1 quick reference
|
|
88
|
+
|
|
89
|
+
CIA1 occupies `$DC00-$DC0F` with 16-byte mirrors filling the rest of
|
|
90
|
+
`$DC00-$DCFF`. Reads and writes to `$DC10`, `$DC20`, ... `$DCF0` access
|
|
91
|
+
the same registers as `$DC00`.
|
|
92
|
+
|
|
93
|
+
| Addr | Name | R/W | Function |
|
|
94
|
+
|---------|----------|-----|---------------------------------------------------------|
|
|
95
|
+
| `$DC00` | `DC00` | RW | Data port A — keyboard matrix col, joystick 2 pins |
|
|
96
|
+
| `$DC01` | `DC01` | RW | Data port B — keyboard matrix row, joystick 1 pins |
|
|
97
|
+
| `$DC02` | `DC02` | RW | Data direction register A (0=input, 1=output) |
|
|
98
|
+
| `$DC03` | `DC03` | RW | Data direction register B (0=input, 1=output) |
|
|
99
|
+
| `$DC04` | `DC04` | RW | Timer A low byte (write = latch, read = current) |
|
|
100
|
+
| `$DC05` | `DC05` | RW | Timer A high byte (write = latch, read = current) |
|
|
101
|
+
| `$DC06` | `DC06` | RW | Timer B low byte (write = latch, read = current) |
|
|
102
|
+
| `$DC07` | `DC07` | RW | Timer B high byte (write = latch, read = current) |
|
|
103
|
+
| `$DC08` | `DC08` | RW | Time-of-day tenths of a second (BCD) |
|
|
104
|
+
| `$DC09` | `DC09` | RW | Time-of-day seconds (BCD) |
|
|
105
|
+
| `$DC0A` | `DC0A` | RW | Time-of-day minutes (BCD) |
|
|
106
|
+
| `$DC0B` | `DC0B` | RW | Time-of-day hours (BCD, bit 7 = AM/PM) |
|
|
107
|
+
| `$DC0C` | `DC0C` | RW | Serial shift register (SDR) |
|
|
108
|
+
| `$DC0D` | `DC0D` | RW | Interrupt-control register (drives 6510 /IRQ) |
|
|
109
|
+
| `$DC0E` | `DC0E` | RW | Control register A — Timer A mode |
|
|
110
|
+
| `$DC0F` | `DC0F` | RW | Control register B — Timer B mode |
|
|
111
|
+
|
|
112
|
+
## CIA2 quick reference
|
|
113
|
+
|
|
114
|
+
CIA2 occupies `$DD00-$DD0F` with 16-byte mirrors filling `$DD00-$DDFF`.
|
|
115
|
+
|
|
116
|
+
| Addr | Name | R/W | Function |
|
|
117
|
+
|---------|----------|-----|----------------------------------------------------------|
|
|
118
|
+
| `$DD00` | `DD00` | RW | Data port A — VIC bank (0-1), serial bus (3-7), RS-232 |
|
|
119
|
+
| `$DD01` | `DD01` | RW | Data port B — user-port PB lines, RS-232 data |
|
|
120
|
+
| `$DD02` | `DD02` | RW | Data direction register A |
|
|
121
|
+
| `$DD03` | `DD03` | RW | Data direction register B |
|
|
122
|
+
| `$DD04` | `DD04` | RW | Timer A low byte |
|
|
123
|
+
| `$DD05` | `DD05` | RW | Timer A high byte |
|
|
124
|
+
| `$DD06` | `DD06` | RW | Timer B low byte |
|
|
125
|
+
| `$DD07` | `DD07` | RW | Timer B high byte |
|
|
126
|
+
| `$DD08` | `DD08` | RW | Time-of-day tenths (BCD) |
|
|
127
|
+
| `$DD09` | `DD09` | RW | Time-of-day seconds (BCD) |
|
|
128
|
+
| `$DD0A` | `DD0A` | RW | Time-of-day minutes (BCD) |
|
|
129
|
+
| `$DD0B` | `DD0B` | RW | Time-of-day hours (BCD) |
|
|
130
|
+
| `$DD0C` | `DD0C` | RW | Serial shift register (unused by the KERNAL) |
|
|
131
|
+
| `$DD0D` | `DD0D` | RW | Interrupt-control register (drives 6510 /NMI) |
|
|
132
|
+
| `$DD0E` | `DD0E` | RW | Control register A — Timer A mode |
|
|
133
|
+
| `$DD0F` | `DD0F` | RW | Control register B — Timer B mode |
|
|
134
|
+
|
|
135
|
+
## CIA1 register details
|
|
136
|
+
|
|
137
|
+
### $DC00 — DC00 — Data Port A (RW)
|
|
138
|
+
|
|
139
|
+
**Chip:** CIA1
|
|
140
|
+
|
|
141
|
+
Data port A. Output pins are PA0-PA7. On the C64 these pins are wired to:
|
|
142
|
+
|
|
143
|
+
- The keyboard matrix **column drive** (PA0-PA7 drive columns 0-7).
|
|
144
|
+
- Control port 2 ("joy 2"): joystick directions, fire button, paddle
|
|
145
|
+
switches share PA0-PA4 with the keyboard columns.
|
|
146
|
+
- Paddle-X / paddle-Y selection (bits 6-7) which the SID's POTX/POTY
|
|
147
|
+
ADC reads.
|
|
148
|
+
|
|
149
|
+
| Bit | Function |
|
|
150
|
+
|-----|----------------------------------------------------------------|
|
|
151
|
+
| 0 | Keyboard column 0 drive / joy2 UP |
|
|
152
|
+
| 1 | Keyboard column 1 drive / joy2 DOWN |
|
|
153
|
+
| 2 | Keyboard column 2 drive / joy2 LEFT |
|
|
154
|
+
| 3 | Keyboard column 3 drive / joy2 RIGHT |
|
|
155
|
+
| 4 | Keyboard column 4 drive / joy2 FIRE |
|
|
156
|
+
| 5 | Keyboard column 5 drive |
|
|
157
|
+
| 6 | Keyboard column 6 drive / paddle select bit 0 |
|
|
158
|
+
| 7 | Keyboard column 7 drive / paddle select bit 1 |
|
|
159
|
+
|
|
160
|
+
To scan a column, clear the chosen column's bit in `$DC00` and set the
|
|
161
|
+
other seven (the KERNAL walks `$FE`, `$FD`, ... `$7F`), then read
|
|
162
|
+
`$DC01` to see which rows were pulled low.
|
|
163
|
+
The control-port 2 joystick is wired in parallel with the column lines,
|
|
164
|
+
so a pushed direction grounds PA0-PA4 externally. On its own that
|
|
165
|
+
produces no phantom key: the KERNAL scanner's no-key test writes `$00`
|
|
166
|
+
to `$DC00` and reads `$DC01`, and nothing on port A can lower a row line
|
|
167
|
+
unless a key is closed. (An earlier version said joy 2 up read as a
|
|
168
|
+
phantom INST/DEL; corrected from the SCNKEY code at `$EA87-$EADD` and
|
|
169
|
+
measured in VICE x64sc.)
|
|
170
|
+
|
|
171
|
+
### $DC01 — DC01 — Data Port B (RW)
|
|
172
|
+
|
|
173
|
+
**Chip:** CIA1
|
|
174
|
+
|
|
175
|
+
Data port B. Pins PB0-PB7. On the C64:
|
|
176
|
+
|
|
177
|
+
- Keyboard matrix **row read** (PB0-PB7 read rows 0-7).
|
|
178
|
+
- Control port 1 ("joy 1"): joy1 directions and fire share PB0-PB4
|
|
179
|
+
with the keyboard rows.
|
|
180
|
+
- Timer A and Timer B can optionally drive PB6/PB7 as output (see the
|
|
181
|
+
PBON bit of `$DC0E`/`$DC0F`).
|
|
182
|
+
- Light-pen trigger: bit 4 connects to the VIC-II `/LP` line via the
|
|
183
|
+
control-port 1 fire-button pin.
|
|
184
|
+
|
|
185
|
+
| Bit | Function |
|
|
186
|
+
|-----|----------------------------------------------------------------|
|
|
187
|
+
| 0 | Keyboard row 0 read / joy1 UP |
|
|
188
|
+
| 1 | Keyboard row 1 read / joy1 DOWN |
|
|
189
|
+
| 2 | Keyboard row 2 read / joy1 LEFT |
|
|
190
|
+
| 3 | Keyboard row 3 read / joy1 RIGHT |
|
|
191
|
+
| 4 | Keyboard row 4 read / joy1 FIRE / light pen trigger |
|
|
192
|
+
| 5 | Keyboard row 5 read |
|
|
193
|
+
| 6 | Keyboard row 6 read / Timer A PBON output (if enabled) |
|
|
194
|
+
| 7 | Keyboard row 7 read / Timer B PBON output (if enabled) |
|
|
195
|
+
|
|
196
|
+
Reading `$DC01` gives `1` for rows that are pulled high (no key pressed,
|
|
197
|
+
joystick not in that direction) and `0` for rows pulled low through
|
|
198
|
+
either the matrix or the joystick switches.
|
|
199
|
+
|
|
200
|
+
### $DC02 — DC02 — Data Direction Register A (RW)
|
|
201
|
+
|
|
202
|
+
**Chip:** CIA1
|
|
203
|
+
|
|
204
|
+
Data-direction register for port A. Each bit controls the direction of
|
|
205
|
+
the corresponding port-A pin:
|
|
206
|
+
|
|
207
|
+
- `0` = pin is input (high-impedance, internally pulled up)
|
|
208
|
+
- `1` = pin is output (driven by the corresponding bit of `$DC00`)
|
|
209
|
+
|
|
210
|
+
IOINIT (`$FDA3`; the store is `STX $DC02` at `$FDC8` with X = `$FF`)
|
|
211
|
+
sets `$DC02` to `$FF` — at reset, again on RUN/STOP+RESTORE (the NMI
|
|
212
|
+
handler calls IOINIT at `$FE69`), and whenever `$FF84` is called. The
|
|
213
|
+
keyboard scanner (SCNKEY, `$EA87`) relies on that and only ever writes
|
|
214
|
+
`$DC00`; it never touches a DDR, so if you clear `$DC02` to read
|
|
215
|
+
joystick 2 you must restore `$FF` yourself — the jiffy scan will not.
|
|
216
|
+
The chip's own reset leaves the DDR at `$00` (all input): measured in
|
|
217
|
+
VICE x64sc 3.10 from a cartridge cold start, which the RESET entry runs
|
|
218
|
+
before IOINIT. An earlier version of this page said the scanner set
|
|
219
|
+
`$DC02` and that `$FF` was the reset default; both were the KERNAL's
|
|
220
|
+
doing.
|
|
221
|
+
|
|
222
|
+
### $DC03 — DC03 — Data Direction Register B (RW)
|
|
223
|
+
|
|
224
|
+
**Chip:** CIA1
|
|
225
|
+
|
|
226
|
+
Data-direction register for port B. The KERNAL keyboard scanner sets
|
|
227
|
+
this to `$00` so PB0-PB7 are all inputs (rows being read). After
|
|
228
|
+
RESET the default is `$00`.
|
|
229
|
+
|
|
230
|
+
Note: if you enable Timer A or Timer B output to PB6/PB7 via the PBON
|
|
231
|
+
bit of `$DC0E`/`$DC0F`, those bits of `$DC03` are forced to output
|
|
232
|
+
regardless of what you write here.
|
|
233
|
+
|
|
234
|
+
### $DC04 — DC04 — Timer A Low Byte (RW)
|
|
235
|
+
|
|
236
|
+
**Chip:** CIA1
|
|
237
|
+
|
|
238
|
+
Timer A is a 16-bit down counter. `$DC04` is its low byte.
|
|
239
|
+
|
|
240
|
+
- **Write**: writes go to the 16-bit *latch*, not the counter. The
|
|
241
|
+
counter is reloaded from the latch on underflow or when the LOAD bit
|
|
242
|
+
of `$DC0E` is set.
|
|
243
|
+
- **Read**: reads return the *current counter value*, not the latch.
|
|
244
|
+
|
|
245
|
+
Reading `$DC04` and `$DC05` to capture a 16-bit timer value is racy
|
|
246
|
+
because the counter decrements between the two byte reads. The standard
|
|
247
|
+
trick is: stop Timer A with `STA $DC0E` (clear START bit), then read,
|
|
248
|
+
then restart.
|
|
249
|
+
|
|
250
|
+
After RESET the latch is `$FFFF`.
|
|
251
|
+
|
|
252
|
+
The KERNAL programs CIA1 Timer A for the jiffy IRQ from the code at
|
|
253
|
+
`$FDDD` (inside IOINIT, and entered again from the tail of CINT at
|
|
254
|
+
`$FF5B` once it has set the PAL/NTSC flag at `$02A6`, which is the write
|
|
255
|
+
that sticks: 1 = PAL, 0 = NTSC). It writes the latch `$4025` on PAL
|
|
256
|
+
(period 16,422 cycles; 985,248 / 16,422 = 59.996 Hz) and `$4295` on
|
|
257
|
+
NTSC (period 17,046 cycles; 1,022,727 / 17,046 = 59.998 Hz) — so one
|
|
258
|
+
jiffy is 1/60 s in both regions and only the VIC frame rate differs. An
|
|
259
|
+
earlier version of this page had the two values swapped and said the
|
|
260
|
+
timer matched the frame rate. Values read from kernal-901227-03.bin at
|
|
261
|
+
`$FDDD-$FDF6` and confirmed in VICE x64sc by reading the latch back
|
|
262
|
+
(stop + LOAD strobe) and by counting 299 jiffies in 250 PAL frames; the
|
|
263
|
+
+1 in the period is the 6526's count-to-zero-then-reload behaviour, not
|
|
264
|
+
measured separately here.
|
|
265
|
+
|
|
266
|
+
### $DC05 — DC05 — Timer A High Byte (RW)
|
|
267
|
+
|
|
268
|
+
**Chip:** CIA1
|
|
269
|
+
|
|
270
|
+
Timer A high byte. Same semantics as `$DC04`: writes set the latch,
|
|
271
|
+
reads return the current counter.
|
|
272
|
+
|
|
273
|
+
Writing the *high* byte while Timer A is stopped also force-loads the
|
|
274
|
+
counter from the latch (this is a 6526 quirk — useful for setting up a
|
|
275
|
+
new value without an explicit LOAD).
|
|
276
|
+
|
|
277
|
+
### $DC06 — DC06 — Timer B Low Byte (RW)
|
|
278
|
+
|
|
279
|
+
**Chip:** CIA1
|
|
280
|
+
|
|
281
|
+
Timer B low byte. Same semantics as Timer A's `$DC04`. Writes go to the
|
|
282
|
+
latch, reads return the counter.
|
|
283
|
+
|
|
284
|
+
CIA1 Timer B is free for application use only while no KERNAL
|
|
285
|
+
serial-bus or tape I/O is running. Every byte the KERNAL sends on the
|
|
286
|
+
IEC bus (LISTEN/TALK command bytes, secondary addresses, CIOUT data)
|
|
287
|
+
ends with `LDA #$04 / STA $DC07 / LDA #$19 / STA $DC0F` at `$ED92` and a
|
|
288
|
+
poll of `$DC0D` bit 1 at `$ED9F` (the listener-acknowledge timeout), and
|
|
289
|
+
every byte ACPTR receives starts with the same sequence using `#$01` at
|
|
290
|
+
`$EE20`, polled at `$EE30` (the EOI timeout). Only the high latch byte
|
|
291
|
+
is written; the low byte is whatever was there before (`$FF` from reset
|
|
292
|
+
— IOINIT never touches `$DC06` — so the one-shots are `$04FF` and
|
|
293
|
+
`$01FF` cycles, about 1.3 ms and 0.5 ms, not `$0400`/`$0100`). CRB is
|
|
294
|
+
rewritten wholesale to `$19`: a continuous timer becomes one-shot and
|
|
295
|
+
stops after its next underflow, PBON, OUTMODE, INMODE and the ALARM
|
|
296
|
+
select are cleared, and the `$DC0D` reads discard every pending CIA1
|
|
297
|
+
flag, not just Timer B's. Measured in VICE x64sc with a true-emulated
|
|
298
|
+
1541: a Timer B programmed to `$1234`, continuous and running, read back
|
|
299
|
+
CRB = `$08` (one-shot, stopped), counter `$0434` and `$DC0D` bit 1
|
|
300
|
+
pending after a single KERNAL TALK/TKSA/ACPTR/UNTLK. The tape code owns
|
|
301
|
+
the timer outright: tape write runs on Timer B underflow interrupts
|
|
302
|
+
(mask `$82` written at `$F87A`; half-pulse latches `$0060`/`$00B0` set
|
|
303
|
+
at `$FBB1-$FBBC`) and tape read measures pulse widths on it at `$F92C`
|
|
304
|
+
and restarts it from a `$FFFF` latch at `$F93D-$F945`. A music or
|
|
305
|
+
raster timer on CIA1 Timer B does not survive a KERNAL LOAD or SAVE
|
|
306
|
+
over IEC or tape. (An earlier version of this page said the KERNAL
|
|
307
|
+
never used it; read from kernal-901227-03.bin and measured in VICE.)
|
|
308
|
+
|
|
309
|
+
### $DC07 — DC07 — Timer B High Byte (RW)
|
|
310
|
+
|
|
311
|
+
**Chip:** CIA1
|
|
312
|
+
|
|
313
|
+
Timer B high byte. Writes to the latch; reads from the counter.
|
|
314
|
+
|
|
315
|
+
### $DC08 — DC08 — Time-of-Day Tenths (RW)
|
|
316
|
+
|
|
317
|
+
**Chip:** CIA1
|
|
318
|
+
|
|
319
|
+
Time-of-day clock — tenths of a second, encoded in 4-bit BCD. Only
|
|
320
|
+
bits 0-3 are used; bits 4-7 read zero.
|
|
321
|
+
|
|
322
|
+
The TOD clock is driven by the **TOD pin** on the 6526, which on the
|
|
323
|
+
C64 is wired to the AC mains 50 Hz (PAL) or 60 Hz (NTSC) line via the
|
|
324
|
+
power supply. The CRA bit 7 of `$DC0E` selects 50 Hz vs 60 Hz division.
|
|
325
|
+
|
|
326
|
+
**Reading `$DC0B` (hours) latches** all four TOD registers
|
|
327
|
+
(`$DC08`-`$DC0B`) so a multi-byte read sees a coherent time; the latch
|
|
328
|
+
is released by reading `$DC08` (tenths). Read order: hours, minutes,
|
|
329
|
+
seconds, tenths. The counter keeps running underneath the latch, so a
|
|
330
|
+
read of tenths after a long-held latch jumps straight to the live time.
|
|
331
|
+
(An earlier version of this page had the two registers swapped;
|
|
332
|
+
measured in VICE x64sc; the probe is in `pitfalls/cia.md`,
|
|
333
|
+
`tod_read_order_latch`.)
|
|
334
|
+
|
|
335
|
+
**Writes** go to the running clock when the ALARM bit of `$DC0F` is 0
|
|
336
|
+
and to the alarm when it is 1. With ALARM = 0, writing `$DC0B` (hours)
|
|
337
|
+
STOPS the clock so all four registers can be set coherently, and writing
|
|
338
|
+
`$DC08` (tenths) restarts it; set hours, minutes, seconds, tenths in
|
|
339
|
+
that order — a clock whose tenths are never written stays stopped
|
|
340
|
+
(VICE's TOD is stopped at power-up until the first tenths write). With
|
|
341
|
+
ALARM = 1 the same four writes program the alarm and leave the clock
|
|
342
|
+
running; there is no separate arming step, the alarm compare is always
|
|
343
|
+
live. (An earlier version of this page said the hours write started the
|
|
344
|
+
clock; measured in VICE x64sc.)
|
|
345
|
+
|
|
346
|
+
### $DC09 — DC09 — Time-of-Day Seconds (RW)
|
|
347
|
+
|
|
348
|
+
**Chip:** CIA1
|
|
349
|
+
|
|
350
|
+
Seconds, BCD. Bits 0-3 are the units digit, bits 4-6 are the tens
|
|
351
|
+
digit (0-5). Bit 7 reads zero.
|
|
352
|
+
|
|
353
|
+
### $DC0A — DC0A — Time-of-Day Minutes (RW)
|
|
354
|
+
|
|
355
|
+
**Chip:** CIA1
|
|
356
|
+
|
|
357
|
+
Minutes, BCD. Bits 0-3 = units, bits 4-6 = tens (0-5), bit 7 = 0.
|
|
358
|
+
|
|
359
|
+
### $DC0B — DC0B — Time-of-Day Hours (RW)
|
|
360
|
+
|
|
361
|
+
**Chip:** CIA1
|
|
362
|
+
|
|
363
|
+
Hours, BCD. Bits 0-3 = units, bit 4 = tens digit (0 or 1), bits 5-6 = 0,
|
|
364
|
+
bit 7 = AM/PM flag (0 = AM, 1 = PM).
|
|
365
|
+
|
|
366
|
+
Writing `$DC0B` with `$DC0F` bit 7 = 0 stops the clock; it does not run
|
|
367
|
+
again until `$DC08` (tenths) is written. With bit 7 = 1 the write sets
|
|
368
|
+
the alarm hours and the clock keeps running (measured in VICE x64sc;
|
|
369
|
+
this page used to say the hours write started the clock).
|
|
370
|
+
|
|
371
|
+
Reading `$DC0B` latches all four TOD registers; the latch is released
|
|
372
|
+
by a later read of `$DC08` (tenths). If you peek at the hours and never
|
|
373
|
+
read tenths, every subsequent TOD read returns the frozen value.
|
|
374
|
+
(Previously stated the other way round; measured in VICE x64sc.)
|
|
375
|
+
|
|
376
|
+
### $DC0C — DC0C — Serial Shift Register (RW)
|
|
377
|
+
|
|
378
|
+
**Chip:** CIA1
|
|
379
|
+
|
|
380
|
+
8-bit synchronous serial shift register. Clock comes from CNT, data
|
|
381
|
+
from SP. Direction (input/output) is selected by the SPMODE bit of
|
|
382
|
+
`$DC0E`.
|
|
383
|
+
|
|
384
|
+
CIA1's SDR is **not used by the KERNAL**. Its SP1 and CNT1 pins do
|
|
385
|
+
reach the user port (pins 5 and 4, beside CIA2's SP2/CNT2 on 7 and 6 —
|
|
386
|
+
the C64 user-port pinout, not measured here), so third-party hardware
|
|
387
|
+
can drive it. An earlier version of this page said the pins had no
|
|
388
|
+
external wiring.
|
|
389
|
+
|
|
390
|
+
After a full byte has been shifted in or out, the SDR bit of the ICR
|
|
391
|
+
(`$DC0D` bit 3) goes high.
|
|
392
|
+
|
|
393
|
+
### $DC0D — DC0D — Interrupt Control Register (RW)
|
|
394
|
+
|
|
395
|
+
**Chip:** CIA1
|
|
396
|
+
|
|
397
|
+
Five interrupt sources, one summary flag. Reads and writes use
|
|
398
|
+
different bit layouts.
|
|
399
|
+
|
|
400
|
+
**Read** of `$DC0D`: returns pending interrupt flags, then **clears all
|
|
401
|
+
flags as a side effect** and de-asserts `/IRQ`.
|
|
402
|
+
|
|
403
|
+
| Bit | Read meaning |
|
|
404
|
+
|-----|----------------------------------------------------|
|
|
405
|
+
| 0 | Timer A underflow occurred |
|
|
406
|
+
| 1 | Timer B underflow occurred |
|
|
407
|
+
| 2 | TOD alarm matched |
|
|
408
|
+
| 3 | Serial shift register full/empty |
|
|
409
|
+
| 4 | FLAG pin transition (negative edge) |
|
|
410
|
+
| 5-6 | always 0 |
|
|
411
|
+
| 7 | IRQ — set if any of bits 0-4 are set AND its mask |
|
|
412
|
+
|
|
413
|
+
**Write** to `$DC0D`: sets or clears interrupt-enable mask bits.
|
|
414
|
+
|
|
415
|
+
| Bit | Write meaning |
|
|
416
|
+
|-----|---------------------------------------------------------------|
|
|
417
|
+
| 0 | Enable/disable Timer A underflow IRQ |
|
|
418
|
+
| 1 | Enable/disable Timer B underflow IRQ |
|
|
419
|
+
| 2 | Enable/disable TOD alarm IRQ |
|
|
420
|
+
| 3 | Enable/disable serial shift IRQ |
|
|
421
|
+
| 4 | Enable/disable FLAG-pin IRQ |
|
|
422
|
+
| 5-6 | reserved (write 0) |
|
|
423
|
+
| 7 | SET/CLEAR — `1` = bits 0-4 *set* selected masks; |
|
|
424
|
+
| | `0` = bits 0-4 *clear* selected masks |
|
|
425
|
+
|
|
426
|
+
Idiom: to enable Timer A IRQ only, write `%10000001` = `$81`.
|
|
427
|
+
To disable Timer A IRQ only, write `%00000001` = `$01`.
|
|
428
|
+
|
|
429
|
+
When `/IRQ` is asserted by CIA1, the 6510's `/IRQ` pin goes low. With
|
|
430
|
+
`I` clear, this jumps through the IRQ vector at `$FFFE-$FFFF` (which
|
|
431
|
+
in KERNAL ROM mode points at `$FF48`, the KERNAL IRQ handler, which
|
|
432
|
+
in turn calls the indirect vector at `$0314-$0315`).
|
|
433
|
+
|
|
434
|
+
A custom IRQ handler **must** read `$DC0D` to acknowledge — otherwise
|
|
435
|
+
the chip will keep `/IRQ` asserted and the handler will re-enter
|
|
436
|
+
immediately after `RTI`.
|
|
437
|
+
|
|
438
|
+
### $DC0E — DC0E — Control Register A (Timer A) (RW)
|
|
439
|
+
|
|
440
|
+
**Chip:** CIA1
|
|
441
|
+
|
|
442
|
+
Controls Timer A.
|
|
443
|
+
|
|
444
|
+
| Bit | Name | Function |
|
|
445
|
+
|-----|------------|---------------------------------------------------------|
|
|
446
|
+
| 0 | START | 1 = start Timer A counting, 0 = stop |
|
|
447
|
+
| 1 | PBON | 1 = drive Timer A output (toggle or pulse) to PB6 |
|
|
448
|
+
| 2 | OUTMODE | 0 = pulse PB6 for one cycle; 1 = toggle PB6 on underflow|
|
|
449
|
+
| 3 | RUNMODE | 0 = continuous (auto-reload); 1 = one-shot (stop) |
|
|
450
|
+
| 4 | LOAD | 1 = strobe: force-load counter from latch (no storage) |
|
|
451
|
+
| 5 | INMODE | 0 = count phi-2 clocks; 1 = count CNT positive edges |
|
|
452
|
+
| 6 | SPMODE | 0 = SDR input; 1 = SDR output (Timer A clocks SP) |
|
|
453
|
+
| 7 | TODIN | 0 = TOD counts 60 Hz; 1 = TOD counts 50 Hz |
|
|
454
|
+
|
|
455
|
+
The KERNAL never sets TODIN. IOINIT (`$FDAE`) writes `$08` to `$DC0E`,
|
|
456
|
+
`$DC0F`, `$DD0E` and `$DD0F` on every machine; the jiffy-timer tail at
|
|
457
|
+
`$FF6E` then rewrites `$DC0E` as (`$DC0E` AND `$80`) OR `$11`, which
|
|
458
|
+
keeps the 0 IOINIT put there. So after boot `$DC0E` reads `$01` and
|
|
459
|
+
`$DD0E` reads `$08` on PAL and NTSC alike, and both TOD clocks are in
|
|
460
|
+
60 Hz mode even on a PAL C64. A PAL program that wants a correct TOD
|
|
461
|
+
must set bit 7 itself, and must do it read-modify-write
|
|
462
|
+
(`LDA $DC0E : ORA #$80 : STA $DC0E`) because a plain store would clear
|
|
463
|
+
bit 0 and stop the Timer A jiffy IRQ that drives the keyboard scan. Left
|
|
464
|
+
at 0 on a 50 Hz supply the clock counts 5/6 of real time (measured in
|
|
465
|
+
VICE x64sc: 4.1 s on the TOD for 4.9 s elapsed); the mirror case,
|
|
466
|
+
TODIN = 1 on an NTSC machine, runs 6/5 fast. (An earlier version said
|
|
467
|
+
the KERNAL set this bit by region; read from kernal-901227-03.bin
|
|
468
|
+
`$FDAE`/`$FF6E` and confirmed in VICE x64sc on both models.)
|
|
469
|
+
|
|
470
|
+
LOAD is a write-only strobe; reading bit 4 always returns 0.
|
|
471
|
+
|
|
472
|
+
### $DC0F — DC0F — Control Register B (Timer B) (RW)
|
|
473
|
+
|
|
474
|
+
**Chip:** CIA1
|
|
475
|
+
|
|
476
|
+
Controls Timer B.
|
|
477
|
+
|
|
478
|
+
| Bit | Name | Function |
|
|
479
|
+
|-----|------------|----------------------------------------------------------|
|
|
480
|
+
| 0 | START | 1 = start Timer B counting |
|
|
481
|
+
| 1 | PBON | 1 = drive Timer B output to PB7 |
|
|
482
|
+
| 2 | OUTMODE | 0 = pulse PB7 once; 1 = toggle PB7 |
|
|
483
|
+
| 3 | RUNMODE | 0 = continuous; 1 = one-shot |
|
|
484
|
+
| 4 | LOAD | 1 = strobe: force-load counter from latch |
|
|
485
|
+
| 5-6 | INMODE | 00 = phi-2; 01 = rising CNT edges; 10 = Timer A underflows; |
|
|
486
|
+
| | | 11 = Timer A underflows gated by CNT high |
|
|
487
|
+
| 7 | ALARM | 0 = TOD writes set clock; 1 = TOD writes set alarm |
|
|
488
|
+
|
|
489
|
+
The INMODE `10` setting (count Timer A underflows) lets you chain
|
|
490
|
+
the two timers into a 32-bit timer. Set Timer A as the low 16 bits,
|
|
491
|
+
Timer B as the high 16 bits, and Timer B will tick once per Timer A
|
|
492
|
+
underflow — giving a period of up to 2^32 phi-2 cycles, roughly
|
|
493
|
+
4300 seconds at 1 MHz.
|
|
494
|
+
|
|
495
|
+
## CIA2 register details
|
|
496
|
+
|
|
497
|
+
### $DD00 — DD00 — Data Port A — VIC bank + IEC (RW)
|
|
498
|
+
|
|
499
|
+
**Chip:** CIA2
|
|
500
|
+
|
|
501
|
+
CIA2's port A is the most heavily multiplexed register on the C64.
|
|
502
|
+
Bits 0-1 select the VIC-II's 16 KiB bank. Bits 3-7 drive (or read)
|
|
503
|
+
the serial-IEC bus to the disk drive and printer. Bit 2 is the
|
|
504
|
+
RS-232 TXD output on the user port.
|
|
505
|
+
|
|
506
|
+
| Bit | Direction | Function |
|
|
507
|
+
|-----|-----------|-----------------------------------------------------------|
|
|
508
|
+
| 0 | Output | VA14 — VIC bank select bit 0 (inverted: see below) |
|
|
509
|
+
| 1 | Output | VA15 — VIC bank select bit 1 (inverted) |
|
|
510
|
+
| 2 | Output | User-port RS-232 TXD (transmit data) |
|
|
511
|
+
| 3 | Output | Serial IEC ATN OUT (drive ATN low) |
|
|
512
|
+
| 4 | Output | Serial IEC CLK OUT |
|
|
513
|
+
| 5 | Output | Serial IEC DATA OUT |
|
|
514
|
+
| 6 | Input | Serial IEC CLK IN (sensed from bus) |
|
|
515
|
+
| 7 | Input | Serial IEC DATA IN (sensed from bus) |
|
|
516
|
+
|
|
517
|
+
**VIC bank selection (bits 0-1):**
|
|
518
|
+
|
|
519
|
+
These two bits are *inverted* on the way to VA14/VA15. The mapping is:
|
|
520
|
+
|
|
521
|
+
| Bits 1-0 | Bank | VIC sees |
|
|
522
|
+
|----------|------|-------------------------|
|
|
523
|
+
| `11` | 0 | `$0000-$3FFF` |
|
|
524
|
+
| `10` | 1 | `$4000-$7FFF` |
|
|
525
|
+
| `01` | 2 | `$8000-$BFFF` |
|
|
526
|
+
| `00` | 3 | `$C000-$FFFF` |
|
|
527
|
+
|
|
528
|
+
After RESET both bits are `1`, so the VIC sees bank 0 (`$0000-$3FFF`).
|
|
529
|
+
The KERNAL leaves the default at bank 0. Bank 1 and bank 3 do *not* see
|
|
530
|
+
character ROM (it only appears at `$1000-$1FFF` and `$9000-$9FFF`); to
|
|
531
|
+
use custom charsets in banks 1 or 3, copy the ROM character set to RAM
|
|
532
|
+
first.
|
|
533
|
+
|
|
534
|
+
The IEC input bits (6, 7) are *not* inverted at the register: bit 6
|
|
535
|
+
(CLK IN) and bit 7 (DATA IN) read `1` when the line is high (released)
|
|
536
|
+
and `0` when this C64 or any device is pulling it low. Only the output
|
|
537
|
+
side is inverted: writing `1` to bits 3, 4, 5 pulls ATN/CLK/DATA low
|
|
538
|
+
(the KERNAL's CLKLO at `$EE8E` is `ORA #$10`, CLKHI at `$EE85` is
|
|
539
|
+
`AND #$EF`). The KERNAL relies on the input polarity: LISTEN's
|
|
540
|
+
device-not-present test at `$ED47` is `BCS` on bit 7 after `ASL` (DATA
|
|
541
|
+
still high after releasing it under ATN = nobody home), and ACPTR at
|
|
542
|
+
`$EE1B` waits for bit 6 = 1, the talker releasing CLK. Measured in VICE
|
|
543
|
+
x64sc: with all three outputs released `$DD00` reads `$C7`; setting
|
|
544
|
+
bit 5 makes bit 7 read `0`; with a 1541 attached and ATN asserted the
|
|
545
|
+
drive pulls DATA and bit 7 reads `0`. After boot it reads `$97` because
|
|
546
|
+
the KERNAL leaves CLK OUT (bit 4) set, so bit 6 is `0` while the C64
|
|
547
|
+
holds CLK low. (An earlier version of this page had the inputs inverted
|
|
548
|
+
as well as the outputs.)
|
|
549
|
+
|
|
550
|
+
### $DD01 — DD01 — Data Port B — user port (RW)
|
|
551
|
+
|
|
552
|
+
**Chip:** CIA2
|
|
553
|
+
|
|
554
|
+
Port B is wired to the C64 user port (the rear edge connector), pins
|
|
555
|
+
C-L. These are general-purpose I/O pins commonly used for:
|
|
556
|
+
|
|
557
|
+
- RS-232 receive data (bit 0 is RXD)
|
|
558
|
+
- RS-232 modem-control signals (RTS, DTR, RI, DCD, CTS, DSR)
|
|
559
|
+
- User-port parallel cables (1541 fastloaders, 1571 burst mode)
|
|
560
|
+
- Hardware MIDI interfaces
|
|
561
|
+
- Centronics printer cables
|
|
562
|
+
|
|
563
|
+
The KERNAL RS-232 driver uses CIA2 Timer A as the transmit bit-clock
|
|
564
|
+
and Timer B as the receive bit-clock, bit-banging TXD on `$DD00` bit 2
|
|
565
|
+
and sampling RXD on `$DD01` bit 0 (`$FED6`).
|
|
566
|
+
|
|
567
|
+
After RESET all bits are inputs (`$DD03` = `$00`).
|
|
568
|
+
|
|
569
|
+
### $DD02 — DD02 — Data Direction Register A (RW)
|
|
570
|
+
|
|
571
|
+
**Chip:** CIA2
|
|
572
|
+
|
|
573
|
+
DDR for CIA2 port A. The KERNAL sets this to `$3F` so that bits 0-5
|
|
574
|
+
are outputs (VIC bank, RS-232 TXD, IEC ATN/CLK/DATA OUT) and bits 6-7
|
|
575
|
+
are inputs (IEC CLK IN, IEC DATA IN).
|
|
576
|
+
|
|
577
|
+
If you accidentally clear `$DD02` to `$00`, the VIC bank-select bits
|
|
578
|
+
become inputs and the VIC will see whatever the bus floats to —
|
|
579
|
+
typically `$3FFF` lines all-high, putting the VIC in bank 0. More
|
|
580
|
+
importantly, writing to `$DD00` then has no effect until you restore
|
|
581
|
+
the DDR.
|
|
582
|
+
|
|
583
|
+
### $DD03 — DD03 — Data Direction Register B (RW)
|
|
584
|
+
|
|
585
|
+
**Chip:** CIA2
|
|
586
|
+
|
|
587
|
+
DDR for CIA2 port B (user port). After RESET this is `$00` (all input).
|
|
588
|
+
RS-232 transmit code sets specific bits to output as needed.
|
|
589
|
+
|
|
590
|
+
### $DD04 — DD04 — Timer A Low Byte (RW)
|
|
591
|
+
|
|
592
|
+
**Chip:** CIA2
|
|
593
|
+
|
|
594
|
+
Timer A low byte. Same semantics as CIA1's `$DC04`: writes to latch,
|
|
595
|
+
reads from counter.
|
|
596
|
+
|
|
597
|
+
The KERNAL uses CIA2 Timer A as the RS-232 bit-rate generator. When
|
|
598
|
+
RS-232 is not in use, Timer A is free for application timing.
|
|
599
|
+
|
|
600
|
+
### $DD05 — DD05 — Timer A High Byte (RW)
|
|
601
|
+
|
|
602
|
+
**Chip:** CIA2
|
|
603
|
+
|
|
604
|
+
Timer A high byte.
|
|
605
|
+
|
|
606
|
+
### $DD06 — DD06 — Timer B Low Byte (RW)
|
|
607
|
+
|
|
608
|
+
**Chip:** CIA2
|
|
609
|
+
|
|
610
|
+
Timer B low byte.
|
|
611
|
+
|
|
612
|
+
### $DD07 — DD07 — Timer B High Byte (RW)
|
|
613
|
+
|
|
614
|
+
**Chip:** CIA2
|
|
615
|
+
|
|
616
|
+
Timer B high byte.
|
|
617
|
+
|
|
618
|
+
### $DD08 — DD08 — Time-of-Day Tenths (RW)
|
|
619
|
+
|
|
620
|
+
**Chip:** CIA2
|
|
621
|
+
|
|
622
|
+
CIA2 has a fully independent TOD clock from CIA1's. It is driven by the
|
|
623
|
+
same AC mains line through the same TOD pin, so both clocks tick at the
|
|
624
|
+
same external rate, but they can hold different values.
|
|
625
|
+
|
|
626
|
+
CIA2's TOD is largely unused by the C64 KERNAL.
|
|
627
|
+
|
|
628
|
+
### $DD09 — DD09 — Time-of-Day Seconds (RW)
|
|
629
|
+
|
|
630
|
+
**Chip:** CIA2
|
|
631
|
+
|
|
632
|
+
Seconds, BCD.
|
|
633
|
+
|
|
634
|
+
### $DD0A — DD0A — Time-of-Day Minutes (RW)
|
|
635
|
+
|
|
636
|
+
**Chip:** CIA2
|
|
637
|
+
|
|
638
|
+
Minutes, BCD.
|
|
639
|
+
|
|
640
|
+
### $DD0B — DD0B — Time-of-Day Hours (RW)
|
|
641
|
+
|
|
642
|
+
**Chip:** CIA2
|
|
643
|
+
|
|
644
|
+
Hours, BCD with AM/PM in bit 7.
|
|
645
|
+
|
|
646
|
+
### $DD0C — DD0C — Serial Shift Register (RW)
|
|
647
|
+
|
|
648
|
+
**Chip:** CIA2
|
|
649
|
+
|
|
650
|
+
CIA2's SDR. The KERNAL never touches either CIA's shift register —
|
|
651
|
+
there is no reference to `$DC0C` or `$DD0C` anywhere in the 901227-03
|
|
652
|
+
KERNAL or 901226-01 BASIC ROMs (byte census, any alignment). KERNAL
|
|
653
|
+
RS-232 is bit-banged under NMI: CIA2 Timer A paces transmit on PA2
|
|
654
|
+
(`$F047` writes `$11` to `$DD0E`), the FLAG input catches the start bit
|
|
655
|
+
(`$EF7E` writes `$90` to `$DD0D`), and Timer B paces receive sampling
|
|
656
|
+
of PB0 (`$FED6`: `LDA $DD01 / AND #$01 / STA $A7`; `$FEF1`/`$FF13`
|
|
657
|
+
write `$11` to `$DD0F`). There is no "high-speed synchronous" RS-232
|
|
658
|
+
mode in the KERNAL. The SP2/CNT2 pins reach the user port, so
|
|
659
|
+
third-party serial hardware and drivers can use the SDR; for
|
|
660
|
+
KERNAL-only code it is free. (An earlier version of this section said
|
|
661
|
+
the KERNAL used the SDR in shift-in mode for a synchronous RS-232
|
|
662
|
+
receive; it does not — read from kernal-901227-03.bin.)
|
|
663
|
+
|
|
664
|
+
### $DD0D — DD0D — Interrupt Control Register (drives /NMI) (RW)
|
|
665
|
+
|
|
666
|
+
**Chip:** CIA2
|
|
667
|
+
|
|
668
|
+
Same bit layout as CIA1's `$DC0D`. The crucial difference is that
|
|
669
|
+
CIA2's `/IRQ` output is wired to the 6510's `/NMI` pin, **not** `/IRQ`.
|
|
670
|
+
|
|
671
|
+
| Bit | Read meaning | Write meaning |
|
|
672
|
+
|-----|---------------------------------------------------|----------------------------|
|
|
673
|
+
| 0 | Timer A underflow occurred | Mask Timer A NMI |
|
|
674
|
+
| 1 | Timer B underflow occurred | Mask Timer B NMI |
|
|
675
|
+
| 2 | TOD alarm matched | Mask TOD-alarm NMI |
|
|
676
|
+
| 3 | Serial shift register full/empty | Mask SDR NMI |
|
|
677
|
+
| 4 | FLAG pin transition | Mask FLAG NMI |
|
|
678
|
+
| 5-6 | always 0 | reserved (write 0) |
|
|
679
|
+
| 7 | NMI summary flag (any enabled bit 0-4 set) | SET/CLEAR direction |
|
|
680
|
+
|
|
681
|
+
The chip itself still calls it an "IRQ" in datasheet language. The
|
|
682
|
+
NMI/IRQ distinction is purely a function of which CPU pin the output
|
|
683
|
+
is wired to — see Overview.
|
|
684
|
+
|
|
685
|
+
Reading `$DD0D` clears all pending NMI flags and de-asserts the chip's
|
|
686
|
+
output pin. An NMI handler **must** read `$DD0D` (or otherwise
|
|
687
|
+
acknowledge) before `RTI`; otherwise `/NMI` stays low and, because the
|
|
688
|
+
6510 takes NMI on a falling edge only, no further CIA2 NMI — and no
|
|
689
|
+
RESTORE press — can be taken until something reads `$DD0D`. (An
|
|
690
|
+
earlier version of this page said the handler would re-enter on `RTI`;
|
|
691
|
+
that is the IRQ symptom, not the NMI one. Measured in VICE x64sc 3.10:
|
|
692
|
+
CIA2 Timer A one-shot NMI with a handler that never reads `$DD0D` —
|
|
693
|
+
entered once; a second underflow while `/NMI` stayed low produced no
|
|
694
|
+
NMI; one `LDA $DD0D` and the next underflow produced one. See
|
|
695
|
+
Pitfalls.)
|
|
696
|
+
|
|
697
|
+
The KERNAL initializes `$DD0D` to `$7F` (clear all masks) followed by
|
|
698
|
+
no explicit enables, leaving CIA2 NMIs disabled by default.
|
|
699
|
+
|
|
700
|
+
### $DD0E — DD0E — Control Register A (Timer A) (RW)
|
|
701
|
+
|
|
702
|
+
**Chip:** CIA2
|
|
703
|
+
|
|
704
|
+
Same bit layout as CIA1's `$DC0E`. Same notes about the LOAD strobe and
|
|
705
|
+
TODIN frequency. The KERNAL leaves CIA2's TODIN at 0 as well — IOINIT
|
|
706
|
+
writes `$08` here and nothing later touches bit 7 (the only other
|
|
707
|
+
KERNAL writes, `$F030`/`$F049` in the RS-232 code, store `$10`/`$11`)
|
|
708
|
+
— so a PAL program using CIA2's TOD must OR in bit 7 itself. (An
|
|
709
|
+
earlier version said the KERNAL set it by region.)
|
|
710
|
+
|
|
711
|
+
### $DD0F — DD0F — Control Register B (Timer B) (RW)
|
|
712
|
+
|
|
713
|
+
**Chip:** CIA2
|
|
714
|
+
|
|
715
|
+
Same bit layout as CIA1's `$DC0F`. Same INMODE choices (phi-2 / CNT /
|
|
716
|
+
Timer A underflows / Timer A + CNT gated). Same ALARM bit selecting
|
|
717
|
+
between writing the clock vs writing the alarm.
|
|
718
|
+
|
|
719
|
+
## Keyboard scanning (CIA1 ports A/B)
|
|
720
|
+
|
|
721
|
+
The C64 keyboard is an 8x8 matrix of 64 key switches. Two extra
|
|
722
|
+
non-matrix switches (`RESTORE` to NMI, `SHIFT LOCK` mechanically tied
|
|
723
|
+
to LEFT-SHIFT) exist outside the matrix.
|
|
724
|
+
|
|
725
|
+
Technique: scanning the matrix from a program without the KERNAL, and
|
|
726
|
+
why a main-loop scan interferes with joystick port 1, is
|
|
727
|
+
`keyboard_matrix_scan` in `techniques/input.md`.
|
|
728
|
+
|
|
729
|
+
**Wiring:**
|
|
730
|
+
|
|
731
|
+
- CIA1 port A (`$DC00`) drives the 8 *columns* (output, all `1`s except
|
|
732
|
+
the column being scanned, which is `0`).
|
|
733
|
+
- CIA1 port B (`$DC01`) reads the 8 *rows* (input with internal pull-ups
|
|
734
|
+
to `1`). A pressed key in the active column pulls its row to `0`.
|
|
735
|
+
|
|
736
|
+
The KERNAL keyboard scanner runs once per jiffy interrupt (about 60 Hz
|
|
737
|
+
on both PAL and NTSC — see `$DC04`). A minimal scanner in the KERNAL's
|
|
738
|
+
style (the ROM's SCNKEY sets the DDRs nowhere; IOINIT did that at
|
|
739
|
+
boot):
|
|
740
|
+
|
|
741
|
+
```asm
|
|
742
|
+
LDA #$FF
|
|
743
|
+
STA $DC02 ; port A = output
|
|
744
|
+
LDA #$00
|
|
745
|
+
STA $DC03 ; port B = input
|
|
746
|
+
LDX #$00
|
|
747
|
+
loop: LDA col_mask,X ; one zero-bit walking across $FE..$7F
|
|
748
|
+
STA $DC00 ; drive that column low
|
|
749
|
+
LDA $DC01 ; read rows
|
|
750
|
+
STA matrix,X ; save row bits
|
|
751
|
+
INX
|
|
752
|
+
CPX #$08
|
|
753
|
+
BNE loop
|
|
754
|
+
```
|
|
755
|
+
|
|
756
|
+
The classic full-matrix layout (column, row 0..7):
|
|
757
|
+
|
|
758
|
+
| Col | Row 0 | Row 1 | Row 2 | Row 3 | Row 4 | Row 5 | Row 6 | Row 7 |
|
|
759
|
+
|-----|-----------|-----------|-----------|-----------|-----------|-----------|-----------|-----------|
|
|
760
|
+
| 0 | INST/DEL | RETURN | CRSR L/R | F7/F8 | F1/F2 | F3/F4 | F5/F6 | CRSR U/D |
|
|
761
|
+
| 1 | 3 # | W | A | 4 $ | Z | S | E | L-SHIFT |
|
|
762
|
+
| 2 | 5 % | R | D | 6 & | C | F | T | X |
|
|
763
|
+
| 3 | 7 ' | Y | G | 8 ( | B | H | U | V |
|
|
764
|
+
| 4 | 9 ) | I | J | 0 | M | K | O | N |
|
|
765
|
+
| 5 | + | P | L | - | . > | : [ | @ | , < |
|
|
766
|
+
| 6 | £ | * | ; ] | CLR/HOME | R-SHIFT | = | UP-ARROW | / ? |
|
|
767
|
+
| 7 | 1 ! | LEFT-ARROW| CTRL | 2 " | SPACE | C= | Q | RUN/STOP |
|
|
768
|
+
|
|
769
|
+
**Detecting "no key pressed"**: with all columns driven low (write `$00`
|
|
770
|
+
to `$DC00`) and DDR-A = `$FF`, read `$DC01` and check for `$FF`.
|
|
771
|
+
|
|
772
|
+
**Detecting multiple key combinations**: scan column by column and OR
|
|
773
|
+
the result. Standard caveat: three keys whose row+column rectangle forms
|
|
774
|
+
a triangle produce a "ghost" reading on the fourth corner — there are
|
|
775
|
+
no diodes in the matrix.
|
|
776
|
+
|
|
777
|
+
**Joystick interference**: Joystick 2 alone cannot make the KERNAL type
|
|
778
|
+
anything: its lines are columns, and the scanner's no-key test
|
|
779
|
+
(`$DC00` = `$00`, `$DC01` == `$FF`) looks only at rows. Measured in
|
|
780
|
+
VICE x64sc 3.10 with PA0, and then PA0-PA4, held low through six
|
|
781
|
+
seconds of scanning: SFDX stays `$40`, the buffer stays empty. It is
|
|
782
|
+
joystick 1, on the row lines PB0-PB4, that the scanner mistakes for
|
|
783
|
+
keys — and not quite as folklore has it. The column loop
|
|
784
|
+
(`$EAA3-$EADA`) walks `$DC00` from `$FE` to `$7F` and keeps the LAST
|
|
785
|
+
matching matrix index (`$EAC9`: `STY $CB`), then makes one more pass
|
|
786
|
+
with `$DC00` = `$FF` (the mask is rotated with carry set and
|
|
787
|
+
`BNE $EAA8` is taken) that tests row 0 once more at index 64 — the
|
|
788
|
+
tables' 65th entry, `$FF`, "no key". A real key cannot appear there
|
|
789
|
+
because no column is selected; a joystick-grounded row does. With the
|
|
790
|
+
default tables: UP is swallowed (index 64 → no key); DOWN types
|
|
791
|
+
LEFT-ARROW (`$5F`); LEFT lands on CTRL (col 7, row 2), which the
|
|
792
|
+
scanner treats as a modifier (`$028D` = 4), so the key kept is `;`
|
|
793
|
+
(col 6) read through the CTRL table: `$1D`, cursor right; RIGHT types
|
|
794
|
+
`2`; FIRE passes R-SHIFT (col 6, row 4) before SPACE, so SHFLAG = 1 and
|
|
795
|
+
the shifted table gives `$A0`, shift-space, which prints blank — the
|
|
796
|
+
"joystick in port 1 types spaces" effect. All five measured in VICE
|
|
797
|
+
x64sc 3.10 by holding the single port-B line low while the KERNAL
|
|
798
|
+
scanner ran. Joystick 2 held while a real key on rows 1-7 of column 0
|
|
799
|
+
is pressed makes that key read as its column-7 neighbour (RETURN reads
|
|
800
|
+
as LEFT-ARROW), because the grounded column shows in every column pass;
|
|
801
|
+
from the code, not measured. Games read `$DC00`/`$DC01` directly and
|
|
802
|
+
keep the KERNAL scanner out of the way during play.
|
|
803
|
+
|
|
804
|
+
## Joystick reading (CIA1 port A = joy2, port B = joy1)
|
|
805
|
+
|
|
806
|
+
The C64 has two control ports (the DE-9 connectors on the right side of
|
|
807
|
+
the case). Each port has 5 active-low switch lines (up, down, left,
|
|
808
|
+
right, fire) plus two analog "potentiometer" lines.
|
|
809
|
+
|
|
810
|
+
Technique: turning the port byte into press events, held frames and
|
|
811
|
+
delayed auto-repeat is `joystick_edge_detect` and `joystick_autorepeat`
|
|
812
|
+
in `techniques/input.md`, with the recipe
|
|
813
|
+
`recipes/oscar64/joystick-input.md`.
|
|
814
|
+
|
|
815
|
+
- **Joy 2** (control port 2) → CIA1 port A, bits 0-4.
|
|
816
|
+
- **Joy 1** (control port 1) → CIA1 port B, bits 0-4.
|
|
817
|
+
|
|
818
|
+
Yes, port 1 is on CIA1 port *B*. This crossed wiring is the most
|
|
819
|
+
common surprise for new C64 developers.
|
|
820
|
+
|
|
821
|
+
**Bit assignments** (both ports use the same bit layout, just on
|
|
822
|
+
different CIA1 ports):
|
|
823
|
+
|
|
824
|
+
| Bit | Direction |
|
|
825
|
+
|-----|-----------|
|
|
826
|
+
| 0 | UP |
|
|
827
|
+
| 1 | DOWN |
|
|
828
|
+
| 2 | LEFT |
|
|
829
|
+
| 3 | RIGHT |
|
|
830
|
+
| 4 | FIRE |
|
|
831
|
+
| 5 | (unused — analog POT-Y on the port pin, not in this bit) |
|
|
832
|
+
| 6 | (analog POT-X on the port pin, not in this bit) |
|
|
833
|
+
| 7 | (unused) |
|
|
834
|
+
|
|
835
|
+
To read joy 2:
|
|
836
|
+
|
|
837
|
+
```asm
|
|
838
|
+
LDA $DC00 ; joy 2: bits 0-4, 0 = pressed (UP, DOWN, LEFT, RIGHT, FIRE)
|
|
839
|
+
AND #$1F ; idle reads $7F, not $FF: bit 7 is the KERNAL's column-7 drive
|
|
840
|
+
```
|
|
841
|
+
|
|
842
|
+
No DDR change is needed. The KERNAL leaves `$DC02` = `$FF` and `$DC00` =
|
|
843
|
+
`$7F` between scans (IOINIT `$FDC8`, SCNKEY exit `$EB42`), the CIA
|
|
844
|
+
returns the pin level rather than the output latch, and a closed switch
|
|
845
|
+
holds a pin low whether or not it is configured as an output (measured
|
|
846
|
+
in VICE x64sc with an external device on control port 2: the same bytes
|
|
847
|
+
read under DDR = `$FF`/PRA = `$7F` as under DDR = `$00`). An earlier
|
|
848
|
+
revision of this section wrote `$00` to `$DC02` "so the joystick can
|
|
849
|
+
pull lines low"; that was the wrong reason.
|
|
850
|
+
|
|
851
|
+
To read joy 1:
|
|
852
|
+
|
|
853
|
+
```asm
|
|
854
|
+
LDA $DC01 ; port B is already input by default
|
|
855
|
+
; bit 0 = 0 means UP, etc.
|
|
856
|
+
```
|
|
857
|
+
|
|
858
|
+
If you do change `$DC02`, restore `$FF` before the next jiffy IRQ.
|
|
859
|
+
SCNKEY never rewrites the DDR (the only writes in the KERNAL are
|
|
860
|
+
IOINIT's), so with `$DC02` = `$00` no column is ever driven low, `$DC01`
|
|
861
|
+
reads `$FF` on every pass, and the keyboard — RUN/STOP included, which
|
|
862
|
+
UDTIM tests through the same port — is dead until something writes
|
|
863
|
+
`$FF` back.
|
|
864
|
+
|
|
865
|
+
**Paddles** (POT-X / POT-Y) are read through the SID chip's
|
|
866
|
+
`$D419`/`$D41A` registers. CIA1 port A bits 6-7 select which of the
|
|
867
|
+
two paddle pairs (port 1 or port 2) the SID samples. Setting bits 6-7
|
|
868
|
+
of `$DC00` to `01` selects paddle pair 1; `10` selects pair 2.
|
|
869
|
+
|
|
870
|
+
## Timer A / Timer B
|
|
871
|
+
|
|
872
|
+
Both CIAs have identical Timer A and Timer B blocks. Each is a 16-bit
|
|
873
|
+
down counter with a 16-bit reload latch.
|
|
874
|
+
|
|
875
|
+
### Count modes (input source)
|
|
876
|
+
|
|
877
|
+
**Timer A** has two count modes, selected by bit 5 of CRA (`$DC0E`
|
|
878
|
+
or `$DD0E`):
|
|
879
|
+
|
|
880
|
+
- `0`: count system phi-2 clocks (~1 MHz on a stock C64 — slightly
|
|
881
|
+
different PAL/NTSC).
|
|
882
|
+
- `1`: count rising edges on the CNT pin.
|
|
883
|
+
|
|
884
|
+
**Timer B** has four count modes, selected by bits 5-6 of CRB:
|
|
885
|
+
|
|
886
|
+
- `00`: count phi-2 clocks.
|
|
887
|
+
- `01`: count rising edges on the CNT pin.
|
|
888
|
+
- `10`: count Timer A underflows.
|
|
889
|
+
- `11`: count Timer A underflows while CNT is high.
|
|
890
|
+
|
|
891
|
+
Mode `10` enables 32-bit cascade: Timer A is the low 16 bits, Timer B
|
|
892
|
+
is the high 16 bits.
|
|
893
|
+
|
|
894
|
+
### Run modes (what happens on underflow)
|
|
895
|
+
|
|
896
|
+
Bit 3 of CRA/CRB controls run mode:
|
|
897
|
+
|
|
898
|
+
- `0` continuous: counter reloads from latch and keeps counting.
|
|
899
|
+
- `1` one-shot: counter reloads from latch, START bit is cleared, the
|
|
900
|
+
timer stops.
|
|
901
|
+
|
|
902
|
+
### Output to port B
|
|
903
|
+
|
|
904
|
+
Bit 1 (PBON) of CRA/CRB enables driving Timer A output to PB6 or Timer B
|
|
905
|
+
output to PB7. The driven bit of `$DC03`/`$DD03` is forced to output
|
|
906
|
+
mode regardless of what you wrote. Bit 2 (OUTMODE) picks pulse vs
|
|
907
|
+
toggle:
|
|
908
|
+
|
|
909
|
+
- OUTMODE = 0: PB6/PB7 *pulses* for one phi-2 cycle on each underflow.
|
|
910
|
+
- OUTMODE = 1: PB6/PB7 *toggles* on each underflow.
|
|
911
|
+
|
|
912
|
+
This is most often used on CIA1 to generate a tone at a precise
|
|
913
|
+
frequency, but the C64 doesn't route PB6/PB7 to anything audible by
|
|
914
|
+
default — the cassette port writes a pulse train to record bytes, but
|
|
915
|
+
that's CASS WRITE on the 6510 itself, not the CIA.
|
|
916
|
+
|
|
917
|
+
### IRQ / NMI on underflow
|
|
918
|
+
|
|
919
|
+
When a timer underflows, the corresponding bit in `$DC0D` / `$DD0D`
|
|
920
|
+
goes high. If that bit's mask is set (via a write to `$DC0D` /
|
|
921
|
+
`$DD0D` with bit 7 = 1), the chip asserts `/IRQ`:
|
|
922
|
+
|
|
923
|
+
- CIA1 → 6510 `/IRQ` (maskable).
|
|
924
|
+
- CIA2 → 6510 `/NMI` (non-maskable).
|
|
925
|
+
|
|
926
|
+
### Reading a running timer
|
|
927
|
+
|
|
928
|
+
Reads of timer low/high return the *current counter*, not the latch.
|
|
929
|
+
The counter is decrementing one count per phi-2 cycle (in phi-2 mode),
|
|
930
|
+
so reading the low byte and then the high byte will sometimes catch a
|
|
931
|
+
borrow in between, giving a value one tick off.
|
|
932
|
+
|
|
933
|
+
Standard idioms:
|
|
934
|
+
|
|
935
|
+
1. **Stop, read, restart**: `LDA $DC0E / AND #$FE / STA $DC0E` to stop,
|
|
936
|
+
`LDA $DC04 / LDX $DC05`, then `LDA $DC0E / ORA #$01 / STA $DC0E` to
|
|
937
|
+
restart. Loses a few cycles of count.
|
|
938
|
+
2. **Read high, read low, read high**: if the second high read matches
|
|
939
|
+
the first, the low read was valid. Otherwise retry.
|
|
940
|
+
3. There is no atomic two-byte timer read on the 6526. (An earlier
|
|
941
|
+
version of this list said the 6526A revision added one; no datasheet
|
|
942
|
+
on this machine supports that, and the claim is withdrawn.)
|
|
943
|
+
|
|
944
|
+
### Setting up a timer IRQ
|
|
945
|
+
|
|
946
|
+
```asm
|
|
947
|
+
SEI
|
|
948
|
+
LDA #<period
|
|
949
|
+
STA $DC04
|
|
950
|
+
LDA #>period
|
|
951
|
+
STA $DC05 ; latch period
|
|
952
|
+
LDA #$11 ; START=1, PBON=0, RUNMODE=0 (continuous), LOAD=1
|
|
953
|
+
STA $DC0E ; LOAD + START Timer A
|
|
954
|
+
LDA #$81 ; SET mask, enable Timer A
|
|
955
|
+
STA $DC0D ; enable Timer A IRQ
|
|
956
|
+
CLI
|
|
957
|
+
```
|
|
958
|
+
|
|
959
|
+
The LOAD strobe (bit 4) forces the counter to reload from the latch
|
|
960
|
+
without waiting for the next underflow — useful on initial setup.
|
|
961
|
+
|
|
962
|
+
## IRQ/NMI control ($DC0D → IRQ, $DD0D → NMI)
|
|
963
|
+
|
|
964
|
+
The two ICRs share a bit layout but drive different CPU pins. The
|
|
965
|
+
sections above for `$DC0D` and `$DD0D` cover the bit details. Here's the
|
|
966
|
+
operational rules:
|
|
967
|
+
|
|
968
|
+
### Acknowledgement
|
|
969
|
+
|
|
970
|
+
**An interrupt handler MUST read its CIA's `$xx0D` to acknowledge.**
|
|
971
|
+
The 6526 latches an asserted `/IRQ` line; it stays low until the ICR
|
|
972
|
+
read clears the flags. For CIA1, on the level-sensitive `/IRQ` pin, an
|
|
973
|
+
`RTI` without reading the ICR means the CPU immediately re-enters the
|
|
974
|
+
handler (measured in VICE x64sc 3.10: a CIA1 Timer B one-shot with a
|
|
975
|
+
`$0314` handler that never read `$DC0D` was entered 100 times, the cap
|
|
976
|
+
at which the handler finally acked).
|
|
977
|
+
|
|
978
|
+
A CIA2 NMI handler must read `$DD0D` too, but the failure mode is
|
|
979
|
+
silence, not re-entry: the 6510 takes NMI on a falling edge, so a line
|
|
980
|
+
left low cannot produce another one. Measured in the same run: CIA2
|
|
981
|
+
Timer A one-shot NMI with a handler that never reads `$DD0D` — entered
|
|
982
|
+
once; a second underflow while `/NMI` stayed low produced no NMI; one
|
|
983
|
+
`LDA $DD0D` and the next underflow produced one; in continuous mode 1
|
|
984
|
+
entry without ack against 99 with ack in the same window. (An earlier
|
|
985
|
+
version of this section said NMIs re-entered like IRQs and that RESTORE
|
|
986
|
+
needed separate handling — see Pitfalls.)
|
|
987
|
+
|
|
988
|
+
### Mask-write semantics
|
|
989
|
+
|
|
990
|
+
The SET/CLEAR bit (bit 7) of a write decides whether the 1-bits in
|
|
991
|
+
bits 0-4 *set* or *clear* the mask:
|
|
992
|
+
|
|
993
|
+
- `STA $DC0D` with `A = $81`: enable Timer A interrupt.
|
|
994
|
+
- `STA $DC0D` with `A = $01`: disable Timer A interrupt.
|
|
995
|
+
- `STA $DC0D` with `A = $7F`: disable ALL interrupts.
|
|
996
|
+
|
|
997
|
+
A common bug is forgetting the SET/CLEAR bit, e.g. writing `$01`
|
|
998
|
+
intending to enable Timer A but actually disabling it.
|
|
999
|
+
|
|
1000
|
+
### IRQ vector (CIA1)
|
|
1001
|
+
|
|
1002
|
+
CIA1's `/IRQ` reaches the 6510 `/IRQ` pin. With KERNAL ROM mapped in,
|
|
1003
|
+
the CPU jumps through `$FFFE-$FFFF` to `$FF48`, which:
|
|
1004
|
+
|
|
1005
|
+
1. Pushes A, X, Y.
|
|
1006
|
+
2. `TSX / LDA $0104,X / AND #$10` — reads the status byte pushed by the
|
|
1007
|
+
interrupt and tests the B flag; a BRK goes through `($0316)`, a
|
|
1008
|
+
hardware IRQ through `($0314)`. (`$01` is the 6510 port and is not
|
|
1009
|
+
involved; an earlier version of this page said `$FF48` read it.)
|
|
1010
|
+
3. For IRQ, jumps through `($0314)` (default = `$EA31`, the KERNAL
|
|
1011
|
+
IRQ handler that scans keyboard and updates the jiffy clock).
|
|
1012
|
+
4. KERNAL handler ends by reading `$DC0D` and `RTI`.
|
|
1013
|
+
|
|
1014
|
+
To install a custom IRQ, replace `$0314-$0315` with your handler's
|
|
1015
|
+
address. Your handler must read `$DC0D` to ack, then either chain to
|
|
1016
|
+
`$EA31` for full KERNAL services or `JMP $EA81` to skip the keyboard
|
|
1017
|
+
scan and just `RTI`.
|
|
1018
|
+
|
|
1019
|
+
### NMI vector (CIA2 + RESTORE)
|
|
1020
|
+
|
|
1021
|
+
CIA2's `/IRQ` and the RESTORE key both reach the 6510 `/NMI` pin. The
|
|
1022
|
+
CPU jumps through `$FFFA-$FFFB` to `$FE43`, which is only
|
|
1023
|
+
`SEI / JMP ($0318)` — nothing is pushed there. The default `($0318)`
|
|
1024
|
+
target is `$FE47`, and that is where the register saves live (read from
|
|
1025
|
+
kernal-901227-03.bin at `$FE43`: `78 6C 18 03 48 8A 48 98 48`):
|
|
1026
|
+
|
|
1027
|
+
1. `$FE47`: pushes A, X, Y (`PHA / TXA / PHA / TYA / PHA`) — after the
|
|
1028
|
+
vector, not before it, which is exactly why a bare `RTI` at `$0318`
|
|
1029
|
+
is valid.
|
|
1030
|
+
2. Writes `$7F` to `$DD0D` (disable every CIA2 source) and reads
|
|
1031
|
+
`$DD0D` into Y, clearing the pending flags.
|
|
1032
|
+
3. `BMI $FE72`: if a CIA2 source was pending, runs the RS-232 code (if
|
|
1033
|
+
an enabled CIA2 source — Timer A for transmit, Timer B or FLAG for
|
|
1034
|
+
receive — is pending; `$FE72` tests ICR bits 0, 1 and 4 only, never
|
|
1035
|
+
the SDR bit 3). RESTORE alone falls through the RS-232 dispatcher at
|
|
1036
|
+
`$FE72` with no enabled bit set, rewrites `$DD0D` from the enable
|
|
1037
|
+
mirror at `$02A1`, restores Y, X, A and `RTI`s (`$FEBC-$FEC1`).
|
|
1038
|
+
4. Otherwise `JSR $FD02` (cartridge check: compares `$8004-$8008` with
|
|
1039
|
+
`CBM80`); if a cartridge answers, `JMP ($8002)`.
|
|
1040
|
+
5. `JSR $F6BC` (UDTIM, which is what samples the keyboard row that
|
|
1041
|
+
`$FFE1` then reads from `$91`), then `JSR $FFE1` (STOP test); if STOP
|
|
1042
|
+
is not held, restore the registers and `RTI`.
|
|
1043
|
+
6. RUN/STOP + RESTORE: `JSR $FD15` (RESTOR), `JSR $FDA3` (IOINIT),
|
|
1044
|
+
`JSR $E518` (CINT), `JMP ($A002)` — the BASIC warm-start vector.
|
|
1045
|
+
|
|
1046
|
+
There is no BRK test anywhere in it. (An earlier version of this page
|
|
1047
|
+
said `$FE43` saved A/X/Y before jumping through `($0318)`, that the
|
|
1048
|
+
handler checked for BRK first, and that the RS-232 code ran when the
|
|
1049
|
+
SDR triggered; a `$0318` handler written to that description would pull
|
|
1050
|
+
three registers nothing had pushed. Read from kernal-901227-03.bin
|
|
1051
|
+
`$FE43-$FEC1`.)
|
|
1052
|
+
|
|
1053
|
+
To install a custom NMI, replace `$0318-$0319`. Your NMI handler must
|
|
1054
|
+
read `$DD0D` to acknowledge a CIA2 NMI. A RESTORE press is one falling
|
|
1055
|
+
edge on `/NMI`, so one NMI, however long the pulse; there is nothing in
|
|
1056
|
+
the CIA to acknowledge for it, and the pulse width itself is not
|
|
1057
|
+
measured here. (Earlier text said the 555 held `/NMI` low for ~100 ms
|
|
1058
|
+
and that this needed handling; see Pitfalls for what a held-low line
|
|
1059
|
+
actually does.)
|
|
1060
|
+
|
|
1061
|
+
**Disabling RESTORE.** It cannot be done in `$DD0D`: the key's pulse never
|
|
1062
|
+
passes through the CIA, and the KERNAL handler above recognises RESTORE
|
|
1063
|
+
precisely by finding *no* CIA2 flag after its `LDY $DD0D` (the branch is
|
|
1064
|
+
`BMI` at `$FE54`, from the ROM bytes). Two things work: point `$0318` at a
|
|
1065
|
+
handler that ends in `RTI`, or hold an unacknowledged CIA2 NMI so `/NMI`
|
|
1066
|
+
never returns high — the 6510's NMI input is edge-triggered, so a line held
|
|
1067
|
+
low is not re-entered (the "NMI lock": measured in VICE x64sc 3.10, a
|
|
1068
|
+
second Timer A underflow with `/NMI` already low produced no second NMI;
|
|
1069
|
+
the key itself was not pressed in that run, so its case follows from the
|
|
1070
|
+
edge-triggered input rather than from a measurement). Both, with their
|
|
1071
|
+
costs and the RUN/STOP detail, are in `pitfalls/kernal-and-io.md` →
|
|
1072
|
+
`restore_nmi_not_maskable`.
|
|
1073
|
+
|
|
1074
|
+
## VIC bank selection (CIA2 port A bits 0-1)
|
|
1075
|
+
|
|
1076
|
+
The VIC-II has a 14-bit address bus (16 KiB) but the C64 has 64 KiB of
|
|
1077
|
+
RAM. CIA2 port A bits 0-1 select which 16 KiB *bank* the VIC sees.
|
|
1078
|
+
|
|
1079
|
+
The bits are **inverted** between CIA2 and the VIC:
|
|
1080
|
+
|
|
1081
|
+
| `$DD00` bits 1-0 | VIC sees | Char ROM available? |
|
|
1082
|
+
|------------------|----------------|---------------------|
|
|
1083
|
+
| `11` | `$0000-$3FFF` | Yes — at `$1000-$1FFF` |
|
|
1084
|
+
| `10` | `$4000-$7FFF` | No (RAM only) |
|
|
1085
|
+
| `01` | `$8000-$BFFF` | Yes — at `$9000-$9FFF` |
|
|
1086
|
+
| `00` | `$C000-$FFFF` | No (RAM only) |
|
|
1087
|
+
|
|
1088
|
+
After RESET, both bits are `1`, so the VIC sees bank 0 (`$0000-$3FFF`)
|
|
1089
|
+
and the screen RAM at `$0400` is in the default location.
|
|
1090
|
+
|
|
1091
|
+
To switch the VIC to bank 1 while preserving the other port-A bits:
|
|
1092
|
+
|
|
1093
|
+
```asm
|
|
1094
|
+
LDA $DD00
|
|
1095
|
+
AND #$FC ; clear bits 0-1
|
|
1096
|
+
ORA #$02 ; set bit 1, clear bit 0 (binary 10)
|
|
1097
|
+
STA $DD00
|
|
1098
|
+
```
|
|
1099
|
+
|
|
1100
|
+
Then update `$D018` (VIC memory pointers) to tell the VIC where in the
|
|
1101
|
+
new 16 KiB bank to find screen RAM and the character set.
|
|
1102
|
+
|
|
1103
|
+
**Why the inversion?** It's a circuit-design optimization: bits 0-1 of
|
|
1104
|
+
CIA2 PA are inverted by the buffers between CIA2 and the VIC's
|
|
1105
|
+
VA14/VA15 inputs. The effect is that after reset (when CIA2 PA is `$FF`
|
|
1106
|
+
and bits 0-1 are high), the VIC sees bank 0. The all-high reset state
|
|
1107
|
+
of an open-collector / TTL bus matches the "no special change" default
|
|
1108
|
+
that makes screen RAM appear at `$0400` out of the box.
|
|
1109
|
+
|
|
1110
|
+
**Character ROM caveat**: the character ROM is mirrored only into VIC
|
|
1111
|
+
banks 0 and 2 (where addresses `$x000-$x1FFF` of the VIC's view map to
|
|
1112
|
+
the C64's `$D000-$DFFF` I/O area, which when accessed by the VIC
|
|
1113
|
+
returns the character ROM). In banks 1 and 3, the VIC sees pure RAM at
|
|
1114
|
+
that range. To use a custom charset in bank 1 or 3, copy the char ROM
|
|
1115
|
+
to RAM in the new bank first.
|
|
1116
|
+
|
|
1117
|
+
## Serial bus (CIA2 port A bits 3-7)
|
|
1118
|
+
|
|
1119
|
+
The C64's serial-IEC bus is a 4-wire (plus ground) bus carrying ATN,
|
|
1120
|
+
CLOCK, DATA, and SRQ. ATN/CLOCK/DATA each have one driver per device
|
|
1121
|
+
plus a pull-up to +5V — a device drives the line by pulling it to
|
|
1122
|
+
ground (open-collector), and the line reads "high" when no device is
|
|
1123
|
+
pulling.
|
|
1124
|
+
|
|
1125
|
+
**CIA2 PA bit assignments for IEC:**
|
|
1126
|
+
|
|
1127
|
+
| Bit | Direction | IEC role |
|
|
1128
|
+
|-----|-----------|--------------------------------|
|
|
1129
|
+
| 3 | Output | ATN OUT (1 = drive ATN low) |
|
|
1130
|
+
| 4 | Output | CLK OUT (1 = drive CLK low) |
|
|
1131
|
+
| 5 | Output | DATA OUT (1 = drive DATA low) |
|
|
1132
|
+
| 6 | Input | CLK IN (0 = CLK pulled low by some device, 1 = released) |
|
|
1133
|
+
| 7 | Input | DATA IN (0 = DATA pulled low by some device, 1 = released) |
|
|
1134
|
+
|
|
1135
|
+
Note the asymmetry: writing `1` to bit 3 pulls ATN to ground, but
|
|
1136
|
+
reading `1` from bit 6 means CLK is *released* (high); reading `0`
|
|
1137
|
+
means it is being held low, either by this C64 or by a peripheral.
|
|
1138
|
+
(An earlier version of this table had the inputs inverted too — see
|
|
1139
|
+
`$DD00`.)
|
|
1140
|
+
|
|
1141
|
+
**ATN** (attention) is the talker/listener-select line. The C64
|
|
1142
|
+
pulls ATN low to broadcast a command (e.g. "drive 8, talk channel 0"),
|
|
1143
|
+
all devices listen, then C64 releases ATN and the talker starts
|
|
1144
|
+
clocking out data.
|
|
1145
|
+
|
|
1146
|
+
**Slow loader** (KERNAL-default IEC): software-driven on every bit,
|
|
1147
|
+
roughly 300–400 bytes per second on a 1541, about 2.5 ms per byte end
|
|
1148
|
+
to end — measured in VICE x64sc with true drive emulation, where an
|
|
1149
|
+
8,002-byte KERNAL LOAD took 20.1 M PAL cycles (20.4 s, ~390 bytes/s),
|
|
1150
|
+
the same range the loader and IEC pages use. An earlier revision of
|
|
1151
|
+
this page said ~50 bytes per second, which its own 400 µs-per-bit
|
|
1152
|
+
figure contradicted. A 50 KB part takes over two minutes. Most
|
|
1153
|
+
games and demos replace the KERNAL loader with a fastloader that
|
|
1154
|
+
uses Timer A on CIA1 or CIA2 to pace high-speed bit transfers,
|
|
1155
|
+
either by reusing CLK/DATA in non-standard timing or by adding new
|
|
1156
|
+
wires through the serial port's extra pins.
|
|
1157
|
+
|
|
1158
|
+
**Fastloaders** typically take over the IRQ vector or NMI vector,
|
|
1159
|
+
saturate the bus with a custom 2-bit-per-clock protocol, and require
|
|
1160
|
+
matching code in the 1541 disk drive's 6502. Examples: JiffyDOS, Final
|
|
1161
|
+
Cartridge III, Action Replay, EXOS, Krill's loader.
|
|
1162
|
+
|
|
1163
|
+
The SRQ line is not used by the standard C64 KERNAL.
|
|
1164
|
+
|
|
1165
|
+
## Pitfalls
|
|
1166
|
+
|
|
1167
|
+
- **CIA1 vs CIA2 confusion**: both chips have the same register layout,
|
|
1168
|
+
but `$DC00` is keyboard/joystick (drives IRQ) and `$DD00` is VIC
|
|
1169
|
+
bank/IEC (drives NMI). Mixing them up is the most common CIA bug —
|
|
1170
|
+
e.g. writing the VIC bank to `$DC00`, or enabling a "Timer A IRQ" on
|
|
1171
|
+
`$DD0D` when you wanted CIA1 (you'll get an NMI instead, which won't
|
|
1172
|
+
be masked by `SEI`).
|
|
1173
|
+
- **Timer latch order**: writing the low byte of a timer (`$DC04`,
|
|
1174
|
+
`$DC06`, `$DD04`, `$DD06`) only updates the latch's low byte; the
|
|
1175
|
+
counter is not reloaded until you write the high byte (which loads
|
|
1176
|
+
the counter from the latch if the timer is stopped) or strobe LOAD
|
|
1177
|
+
via bit 4 of the CRA/CRB. Writing high then low does NOT load the
|
|
1178
|
+
counter — you must finish with the high byte or with a LOAD strobe.
|
|
1179
|
+
- **An un-acknowledged CIA2 NMI silences NMI**: the hazard is lost
|
|
1180
|
+
NMIs, not re-entry. The 6510 takes NMI on a falling edge; a CIA2 NMI
|
|
1181
|
+
handler that returns without reading `$DD0D` leaves `/NMI` low, and
|
|
1182
|
+
no later CIA2 event can produce another edge until something reads
|
|
1183
|
+
the ICR. Measured in VICE x64sc 3.10: CIA2 Timer A one-shot NMI with a
|
|
1184
|
+
handler that never reads `$DD0D` — entered once; a second underflow
|
|
1185
|
+
while `/NMI` stayed low produced no NMI; one `LDA $DD0D` and the next
|
|
1186
|
+
underflow produced one; in continuous mode 1 entry without ack
|
|
1187
|
+
against 99 with ack in the same window. The demo trick to kill
|
|
1188
|
+
RESTORE is exactly this: fire a CIA2 Timer A one-shot NMI into a
|
|
1189
|
+
handler that never reads `$DD0D`, and the line stays low. RESTORE
|
|
1190
|
+
itself was not measured here (VICE headless cannot press it); that a
|
|
1191
|
+
held-low CIA2 line also swallows RESTORE presses is the standard
|
|
1192
|
+
scene trick and follows from the shared pin, not a measurement. The
|
|
1193
|
+
other option, pointing `$0318` at an `RTI`, still *takes* the NMI —
|
|
1194
|
+
the 7-cycle interrupt sequence, `SEI`, `JMP ($0318)` and the `RTI`,
|
|
1195
|
+
about 20 cycles of jitter per press with the ROM in; with the KERNAL
|
|
1196
|
+
banked out it is `$FFFA/$FFFB` that must point at the `RTI` — whereas
|
|
1197
|
+
the held-low trick prevents the interrupt from being taken at all.
|
|
1198
|
+
(An earlier version of this bullet described NMI re-entrancy on a
|
|
1199
|
+
~100 ms RESTORE pulse and offered `LDA #$00 / STA $DD0E` or counting
|
|
1200
|
+
out the pulse with Timer A as workarounds; the mechanism was the IRQ
|
|
1201
|
+
one, and neither workaround touches the ICR — measured: that handler
|
|
1202
|
+
leaves the line low and the next event is still lost.)
|
|
1203
|
+
The dispatch table, the 20-cycle cost and the lock, measured, are in
|
|
1204
|
+
`pitfalls/kernal-and-io.md` → `restore_nmi_not_maskable`.
|
|
1205
|
+
- **Reading a running timer gives torn values**: low byte decrements
|
|
1206
|
+
between the two-byte read. Stop the timer before reading, or use the
|
|
1207
|
+
read-high / read-low / re-read-high consistency loop. (An earlier
|
|
1208
|
+
version of this bullet said the 6526A added a latched-read mode; it
|
|
1209
|
+
is withdrawn, unsupported by anything on this machine.)
|
|
1210
|
+
- **Forgetting to acknowledge an IRQ**: a custom IRQ handler must read
|
|
1211
|
+
`$DC0D` (CIA1) or `$DD0D` (CIA2) to clear the pending flags and
|
|
1212
|
+
release the `/IRQ`/`/NMI` line. Without this a CIA1 `/IRQ` handler
|
|
1213
|
+
will re-enter immediately after `RTI` and the CPU will appear to hang
|
|
1214
|
+
in the handler; for CIA2 the symptom is the opposite: NMIs stop
|
|
1215
|
+
arriving (see the bullet above). (Earlier text gave the re-entry
|
|
1216
|
+
symptom for both chips.)
|
|
1217
|
+
- **ICR write SET/CLEAR confusion**: bit 7 of an ICR write is the
|
|
1218
|
+
SET/CLEAR control bit, not an interrupt-enable. `STA $DC0D` with
|
|
1219
|
+
`A = $01` *disables* Timer A IRQ; you want `$81` to enable it. A
|
|
1220
|
+
common pattern that confuses this is "write `$7F` to mask all" — `$7F`
|
|
1221
|
+
has bit 7 = 0 (CLEAR), bits 0-6 = 1 (every source) → "clear masks for
|
|
1222
|
+
all sources" → disable all. Conversely `$FF` would *enable* all
|
|
1223
|
+
sources.
|
|
1224
|
+
- **Keyboard scan vs joystick 1**: the KERNAL scanner reads rows; a
|
|
1225
|
+
joystick in port 1 grounds row lines and is decoded as keys — DOWN as
|
|
1226
|
+
LEFT-ARROW, LEFT as CTRL+; (cursor right), RIGHT as `2`, FIRE as
|
|
1227
|
+
shift-space; UP is swallowed by the scanner's final no-column pass.
|
|
1228
|
+
Joystick 2 alone types nothing, but held together with a column-0 key
|
|
1229
|
+
it moves that key to column 7 (RETURN reads as LEFT-ARROW). If your
|
|
1230
|
+
program uses the KERNAL scanner and a joystick, read the joystick
|
|
1231
|
+
from a raster or timer interrupt with the scanner disabled, or clear
|
|
1232
|
+
the buffer. (Earlier text blamed joy 2 for a phantom INST/DEL and
|
|
1233
|
+
said the contention could stress the CIA on early boards; wrong port
|
|
1234
|
+
and wrong key — see the SCNKEY reading in the keyboard section — and
|
|
1235
|
+
the electrical claim has no source in this repo, so it is dropped.)
|
|
1236
|
+
- **VIC bank bits are inverted**: setting `$DD00` bits 1-0 to `00`
|
|
1237
|
+
selects bank 3 (`$C000-$FFFF`), not bank 0. The bit-to-bank table is
|
|
1238
|
+
in the VIC bank section.
|
|
1239
|
+
- **DDR before data**: writing `$DD00` to change VIC bank has no effect
|
|
1240
|
+
if `$DD02` doesn't have bits 0-1 set to output. After RESET the
|
|
1241
|
+
KERNAL sets `$DD02 = $3F` which covers VIC bank + IEC outputs, but
|
|
1242
|
+
if your code clobbered `$DD02` you must restore it.
|
|
1243
|
+
- **TOD pin needs AC mains**: the TOD clock counts the 50/60 Hz AC
|
|
1244
|
+
zero-crossing from the C64's power supply. In emulators that don't
|
|
1245
|
+
emulate the mains line (most), the TOD clock is faked from phi-2 or
|
|
1246
|
+
doesn't tick at all. Don't rely on TOD for precise wall-clock timing
|
|
1247
|
+
in cross-emulator code.
|
|
1248
|
+
- **TOD latch on read**: reading `$DC0B` (or `$DD0B`, hours) latches
|
|
1249
|
+
all four TOD registers so a multi-byte read is coherent; reading
|
|
1250
|
+
`$DC08` (tenths) releases it. If you read hours and then never read
|
|
1251
|
+
tenths, the TOD registers appear frozen on subsequent reads while the
|
|
1252
|
+
clock keeps counting underneath (earlier text had tenths latching and
|
|
1253
|
+
hours releasing; measured in VICE x64sc, PAL and NTSC). The probe and the write-side twin —
|
|
1254
|
+
hours-write stops the clock, tenths-write starts it — are in
|
|
1255
|
+
`pitfalls/cia.md` (`tod_read_order_latch`).
|
|
1256
|
+
- **TOD BCD**: TOD registers are BCD-encoded. Code that increments
|
|
1257
|
+
them via `INC` will produce invalid BCD digits (e.g. `$09` + 1 =
|
|
1258
|
+
`$0A`, not `$10`). Either write decoded values or use ADC with the
|
|
1259
|
+
decimal-mode flag.
|
|
1260
|
+
- **CIA chip revision differences**: original 6526 and the later 6526A
|
|
1261
|
+
/ 8521 have subtly different behavior on edge cases: latch loading
|
|
1262
|
+
timing, TOD start glitch, and serial-shift-register synchronization.
|
|
1263
|
+
Demoscene code that exploits cycle-exact CIA behavior may break on a
|
|
1264
|
+
different chip revision. The "Lorenz CIA test suite" is the gold
|
|
1265
|
+
standard for testing whether your emulator or replacement chip
|
|
1266
|
+
matches a real 6526.
|
|
1267
|
+
- **NMI cannot be masked by SEI**: `SEI` masks the 6510 `/IRQ` line,
|
|
1268
|
+
not `/NMI`. Code that disables CIA1 IRQs with `SEI` to do a
|
|
1269
|
+
time-critical operation can still be interrupted by a CIA2 NMI or a
|
|
1270
|
+
RESTORE press. To truly disable all interrupts, mask CIA2's ICR
|
|
1271
|
+
(`STA $DD0D` with `A = $7F`) AND set `I` AND ideally point
|
|
1272
|
+
`($0318)` at a `RTI` to absorb a RESTORE NMI.
|
|
1273
|
+
- **The "all-output trick" on $DC02**: setting `$DC02 = $FF` and
|
|
1274
|
+
`$DC00 = $00` drives all eight keyboard columns low. Reading
|
|
1275
|
+
`$DC01` then returns the OR of every pressed-key row — useful for
|
|
1276
|
+
fast "any key pressed?" tests but doesn't tell you *which* key.
|
|
1277
|
+
- **Joystick port 1 is on port B, port 2 is on port A**: this is the
|
|
1278
|
+
most-warned pitfall in C64 game-dev tutorials, and yet every new C64
|
|
1279
|
+
coder gets bitten by it. Joy 2 on `$DC00`, joy 1 on `$DC01`.
|
|
1280
|
+
|
|
1281
|
+
<!-- doc-type: hardware-reference -->
|