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,738 @@
|
|
|
1
|
+
---
|
|
2
|
+
category: sid
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
<!-- doc-type: pitfall-reference -->
|
|
6
|
+
|
|
7
|
+
# SID Pitfalls
|
|
8
|
+
|
|
9
|
+
The MOS 6581/8580 SID chip has a register interface that looks
|
|
10
|
+
straightforward on paper but hides several hardware-level traps that
|
|
11
|
+
catch even experienced composers and coders. These pitfalls fall into
|
|
12
|
+
two clusters: the write-only nature of the entire register bank
|
|
13
|
+
(which forces a shadow-RAM discipline on all SID code), and the
|
|
14
|
+
substantial behavioral differences between the two SID revisions
|
|
15
|
+
(which means a song or digi routine calibrated on a 6581 can fail
|
|
16
|
+
audibly on an 8580, or vice versa). The ADSR-reset bug and the voice-3
|
|
17
|
+
silent-bit divergence are both in the second cluster, and both have
|
|
18
|
+
safe workarounds that are easy to apply once you understand the
|
|
19
|
+
underlying mechanism. SID replacements that do not emulate the read side (the
|
|
20
|
+
SwinSID) are a separate case: `sid_replacement_d41b_unreadable`.
|
|
21
|
+
|
|
22
|
+
All pitfalls in this document apply equally to PAL and NTSC
|
|
23
|
+
systems; none of them are timing-region-specific. The filter cutoff
|
|
24
|
+
curve pitfall is the most musically impactful and deserves particular
|
|
25
|
+
attention when writing cross-compatible SID music.
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## sid_write_only_registers — All SID registers $D400-$D418 are write-only; reads return garbage
|
|
30
|
+
|
|
31
|
+
**Severity:** medium
|
|
32
|
+
**Region:** both
|
|
33
|
+
**Triggered by registers:** D400, D404, D40B, D412, D418
|
|
34
|
+
**Triggered by techniques:** sfx_engine_beside_music, sidfx_layered_chip, sfx_in_player, digi_4bit, sidasid_emulation_notes
|
|
35
|
+
|
|
36
|
+
### Symptom
|
|
37
|
+
|
|
38
|
+
Code that reads a SID control register expecting to see what was
|
|
39
|
+
previously written instead gets corrupted data — the last byte
|
|
40
|
+
written to (or read from) the SID, not the register's value (an
|
|
41
|
+
earlier version of this sentence said "the high byte of the read
|
|
42
|
+
address, the last byte the VIC-II fetched, or floating-bus noise";
|
|
43
|
+
neither of the first two ever appears). A read-modify-write pattern like "set bit 3 of
|
|
44
|
+
$D418 without touching the other bits" silently destroys the filter
|
|
45
|
+
mode and volume settings if no shadow register is maintained.
|
|
46
|
+
The failure mode is usually silent (the wrong register value takes
|
|
47
|
+
effect immediately, but there is no error signal) or manifests as
|
|
48
|
+
audio glitches: a filter sweep that jumps to the wrong cutoff, a
|
|
49
|
+
volume register that changes the master volume mid-note, or a voice
|
|
50
|
+
control register that clears the GATE bit unintentionally.
|
|
51
|
+
|
|
52
|
+
### Mechanism
|
|
53
|
+
|
|
54
|
+
The SID chip ($D400-$D41F) exposes 25 write-only registers at
|
|
55
|
+
$D400-$D418 (an earlier version of this sentence said 29; $D418 −
|
|
56
|
+
$D400 + 1 = 25, the same count the Fix below uses), followed by four
|
|
57
|
+
read-only registers at $D419-$D41C. The SID has no read path for
|
|
58
|
+
these registers. A read does not float the bus: the chip drives it
|
|
59
|
+
with the byte it last held — the last value written to any of its 32
|
|
60
|
+
addresses ($D400-$D41F and every mirror through $D7FF) or the last
|
|
61
|
+
value read from $D419-$D41C. That byte decays to $00 after roughly
|
|
62
|
+
7k cycles on a 6581 and roughly 660k cycles on an 8580 (measured in
|
|
63
|
+
VICE 3.10 reSID with the SID being clocked; real chips also decay but
|
|
64
|
+
were not measured here — see sid-reference.md). It is not the address
|
|
65
|
+
high byte and not a VIC-II fetch, which is what an earlier version of
|
|
66
|
+
this paragraph said; either way it is not register state and must
|
|
67
|
+
never be used as such. (For testers: in VICE, a run with sound
|
|
68
|
+
disabled (`+sound`) returns $00 from these reads via a fallback path
|
|
69
|
+
and does not emulate this behaviour, and with the dummy driver or
|
|
70
|
+
warp mode the held byte never fades.)
|
|
71
|
+
|
|
72
|
+
The four read-only registers ($D419-$D41C) are a completely different
|
|
73
|
+
story: POTX ($D419) and POTY ($D41A) return real paddle A/D values;
|
|
74
|
+
RANDOM ($D41B) returns the upper 8 bits of voice 3's oscillator
|
|
75
|
+
accumulator; ENV3 ($D41C) returns voice 3's current envelope level.
|
|
76
|
+
These are the only SID addresses where reads are meaningful.
|
|
77
|
+
|
|
78
|
+
### Fix
|
|
79
|
+
|
|
80
|
+
Maintain a complete shadow copy of the SID write registers in RAM.
|
|
81
|
+
The shadow is the single source of truth for register state. To
|
|
82
|
+
modify any SID register:
|
|
83
|
+
|
|
84
|
+
1. Read the shadow RAM location.
|
|
85
|
+
2. Apply the bit-level change to the shadow.
|
|
86
|
+
3. Write the modified shadow value to both the shadow location and the
|
|
87
|
+
real SID register.
|
|
88
|
+
|
|
89
|
+
Never read back from the SID hardware to determine the current state.
|
|
90
|
+
The shadow RAM write and the hardware write must stay in lock-step;
|
|
91
|
+
mixing in any hardware reads is the source of the bug.
|
|
92
|
+
|
|
93
|
+
Recommended layout: a contiguous block of 25 bytes in RAM you own,
|
|
94
|
+
mapped 1:1 to the SID register layout. Under BASIC/KERNAL the free
|
|
95
|
+
zero page is only $02 and $FB-$FE, so a 25-byte shadow cannot live
|
|
96
|
+
there; $033C-$03FB is the cassette buffer, free only when tape I/O is
|
|
97
|
+
not in use, or use any RAM above your program. An earlier version of
|
|
98
|
+
this paragraph recommended `$C5` through `$DD` — that block is the
|
|
99
|
+
KERNAL's keyboard and screen-editor state ($C5 LSTX, $C6 NDX, $CB-$CD
|
|
100
|
+
key/cursor, $D1-$D6 screen line and cursor, $D9-$F2 the line link
|
|
101
|
+
table; see c64-memory-map.md), and a shadow there corrupts the
|
|
102
|
+
keyboard scan and screen editor on every IRQ. No "SID player
|
|
103
|
+
convention" places a shadow there.
|
|
104
|
+
|
|
105
|
+
### Worked example
|
|
106
|
+
|
|
107
|
+
```asm
|
|
108
|
+
; WRONG: read-modify-write directly on hardware register
|
|
109
|
+
; This reads garbage from $D418 and sets an incorrect value.
|
|
110
|
+
lda $D418 ; BUG: returns the SID's last-held bus byte, not this register
|
|
111
|
+
ora #$0F ; intended: set volume to 15, keep filter bits
|
|
112
|
+
sta $D418 ; writes garbage|0x0F — wrong filter mode likely set
|
|
113
|
+
|
|
114
|
+
; CORRECT: shadow-RAM pattern
|
|
115
|
+
; Declare shadow at top of file:
|
|
116
|
+
; shadow_d418: .byte 0 ; in RAM you own, e.g. $0340 (not zero page unless you own it)
|
|
117
|
+
;
|
|
118
|
+
lda shadow_d418 ; read from RAM shadow — always coherent
|
|
119
|
+
ora #$0F ; set volume bits to 15, preserve filter/3OFF bits
|
|
120
|
+
sta shadow_d418 ; update shadow
|
|
121
|
+
sta $D418 ; write to hardware
|
|
122
|
+
|
|
123
|
+
; Bit-clear variant (e.g. clear the LP filter mode bit 4):
|
|
124
|
+
lda shadow_d418
|
|
125
|
+
and #%11101111 ; clear LP bit
|
|
126
|
+
sta shadow_d418
|
|
127
|
+
sta $D418
|
|
128
|
+
|
|
129
|
+
; Full three-voice control register update using shadows:
|
|
130
|
+
; shadow_d404: .byte 0
|
|
131
|
+
; shadow_d40b: .byte 0
|
|
132
|
+
; shadow_d412: .byte 0
|
|
133
|
+
;
|
|
134
|
+
; To gate all three voices off:
|
|
135
|
+
lda shadow_d404
|
|
136
|
+
and #$FE ; clear bit 0 (GATE)
|
|
137
|
+
sta shadow_d404
|
|
138
|
+
sta $D404
|
|
139
|
+
|
|
140
|
+
lda shadow_d40b
|
|
141
|
+
and #$FE
|
|
142
|
+
sta shadow_d40b
|
|
143
|
+
sta $D40B
|
|
144
|
+
|
|
145
|
+
lda shadow_d412
|
|
146
|
+
and #$FE
|
|
147
|
+
sta shadow_d412
|
|
148
|
+
sta $D412
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### Cross-references
|
|
152
|
+
|
|
153
|
+
- Registers: D400, D404, D40B, D412, D418
|
|
154
|
+
- Technique: `sid_voice_setup` — covers the full register layout per voice
|
|
155
|
+
- Read-only registers for legitimate reads: $D419 (POTX), $D41A (POTY),
|
|
156
|
+
$D41B (RANDOM/OSC3), $D41C (ENV3)
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
## sid_filter_chip_variation — 6581 vs 8580 filter cutoff and resonance differ wildly; same patch sounds different on each chip
|
|
161
|
+
|
|
162
|
+
**Severity:** high
|
|
163
|
+
**Region:** both
|
|
164
|
+
**Triggered by registers:** D415, D416, D417, D418
|
|
165
|
+
**Triggered by techniques:** sid_filter_routing, sid_8580_vs_6581_differences, sidasid_emulation_notes
|
|
166
|
+
|
|
167
|
+
### Symptom
|
|
168
|
+
|
|
169
|
+
A SID music patch that sounds bright and resonant on a 6581 system
|
|
170
|
+
comes out dull and flat on an 8580. A filter sweep that sounds
|
|
171
|
+
evenly-stepped across the frequency range on an 8580 sounds
|
|
172
|
+
compressed at the low end and widely-spaced at the high end on a
|
|
173
|
+
6581. Two 6581 boards of the same chip revision may produce
|
|
174
|
+
noticeably different filter sounds from the same register values.
|
|
175
|
+
The problem is invisible in code review — the register writes are
|
|
176
|
+
correct — but audible immediately on mismatched hardware.
|
|
177
|
+
|
|
178
|
+
### Mechanism
|
|
179
|
+
|
|
180
|
+
The 6581 and 8580 implement the same filter architecture (11-bit
|
|
181
|
+
cutoff, 4-bit resonance, shared LP/BP/HP modes) but use fundamentally
|
|
182
|
+
different analog circuit designs for the cutoff cell.
|
|
183
|
+
|
|
184
|
+
**6581 filter — non-linear and chip-variable.** The 6581 filter is a
|
|
185
|
+
switched-capacitor design. The relationship between the 11-bit cutoff
|
|
186
|
+
register value and the resulting cutoff frequency in Hz is
|
|
187
|
+
non-linear — roughly sigmoidal on a logarithmic frequency scale.
|
|
188
|
+
The low end of the register range (roughly $D416 = $00-$30) produces
|
|
189
|
+
almost no frequency change; the mid-range ($40-$A0) sweeps across
|
|
190
|
+
most of the audible spectrum; the upper range ($A0-$FF) compresses
|
|
191
|
+
back. Additionally, the curve varies substantially between individual
|
|
192
|
+
6581 chips from different manufacturing batches. The same $D416 value
|
|
193
|
+
of $60 may produce a cutoff of 800 Hz on one 6581 and 1200 Hz on
|
|
194
|
+
another from a different batch. This inter-chip variation is inherent
|
|
195
|
+
to the analog design and cannot be corrected in software without
|
|
196
|
+
chip-specific calibration.
|
|
197
|
+
|
|
198
|
+
**8580 filter — linear and consistent.** The 8580 redesigned the
|
|
199
|
+
filter cell for a near-linear cutoff response. A $D416 value of $80
|
|
200
|
+
is roughly half the nominal maximum cutoff (an earlier version said
|
|
201
|
+
$40, which is a quarter of the register's range); a sweep of $D416 from
|
|
202
|
+
$00 to $FF produces a perceptually even frequency sweep. The curve
|
|
203
|
+
is consistent between 8580 chips.
|
|
204
|
+
|
|
205
|
+
**Resonance.** The 4-bit resonance (RESON, $D417 bits 7-4) controls
|
|
206
|
+
Q differently between chip revisions. At RES=12-15, the 6581 often
|
|
207
|
+
distorts audibly — a characteristic "gritty" resonance that scene SID
|
|
208
|
+
musicians frequently exploit. The 8580 at RES=15 can self-oscillate
|
|
209
|
+
cleanly around the cutoff frequency, producing a sine-like tone that
|
|
210
|
+
is useful as a "fourth voice." A patch tuned at RES=15 for a specific
|
|
211
|
+
6581 timbre typically sounds clean and different on 8580.
|
|
212
|
+
|
|
213
|
+
### Fix
|
|
214
|
+
|
|
215
|
+
There is no single software fix that makes the same patch sound
|
|
216
|
+
identical on both chips. The standard approaches are:
|
|
217
|
+
|
|
218
|
+
**Per-chip cutoff tables.** Ship two cutoff frequency tables, one
|
|
219
|
+
calibrated for 6581 and one for 8580. At startup, detect the chip
|
|
220
|
+
revision (see the $D41B detection method below, and
|
|
221
|
+
`sid_8580_vs_6581_differences`) and select the appropriate
|
|
222
|
+
table. GoatTracker implements this with its "chip-select" toggle that
|
|
223
|
+
exports per-chip filter tuning. SidFactory II has similar per-chip
|
|
224
|
+
compensation.
|
|
225
|
+
|
|
226
|
+
**Target one chip explicitly.** For scene-quality demos or game
|
|
227
|
+
music, choose a target chip and tune all filter patches against that
|
|
228
|
+
chip. Document the target in the .SID header's 16-bit big-endian
|
|
229
|
+
flags word at $76-$77: bits 4-5 of byte $77 give the first SID's
|
|
230
|
+
model (00=unknown, 01=6581, 10=8580, 11=either); bits 0-1 are the
|
|
231
|
+
MUS-data and PlaySID/BASIC flags and bits 2-3 the video standard, so
|
|
232
|
+
mask `byte[$77] >> 4 & 3`, not `& 3` (an earlier version of this
|
|
233
|
+
sentence put the model in bits 0-1, which would classify MUS files as
|
|
234
|
+
6581). See formats/c64-file-formats.md. Accept that the other chip
|
|
235
|
+
will sound different.
|
|
236
|
+
|
|
237
|
+
**Use filter-independent timbres.** Unfiltered voices (FILT bits
|
|
238
|
+
clear in $D417) pass straight to the volume DAC and are not subject
|
|
239
|
+
to cutoff curve differences. For cross-compatible patches, minimize
|
|
240
|
+
filter use or use the filter only for broad tonal shaping rather than
|
|
241
|
+
a precise tuned-resonance effect.
|
|
242
|
+
|
|
243
|
+
**Chip detection at runtime.** The filter cannot be observed from
|
|
244
|
+
software: the only readable voice-3 registers are $D41B (OSC3) and
|
|
245
|
+
$D41C (ENV3), and both sit before the filter in the signal path (see
|
|
246
|
+
sid-reference.md, Filter signal flow), so no cutoff, mode or routing
|
|
247
|
+
write changes what they return. An earlier version of this section
|
|
248
|
+
proposed reading ENV3 through the filter, which cannot work — $D41B
|
|
249
|
+
and $D41C sit before the filter; run in VICE reSID, ENV3 read $FF
|
|
250
|
+
with cutoff $00, cutoff $FF, FILT3 = 0 and 3OFF = 1 alike on both chip
|
|
251
|
+
models, and the deleted listing returned "8580-like" on both chips by
|
|
252
|
+
its own comment (its value was just how far a rate-0 attack had got in
|
|
253
|
+
the ~1,000-cycle settle loop). That listing was also self-inconsistent:
|
|
254
|
+
its header comment named the same flag state for both chips and its
|
|
255
|
+
carry sense was the inverse of its own prose. Anyone who copied it got
|
|
256
|
+
both the mechanism and the branch wrong.
|
|
257
|
+
|
|
258
|
+
The standard detection routine uses $D41B instead: write $FF to
|
|
259
|
+
$D412, $D40E and $D40F, then write $20 to $D412 (sawtooth, TEST and
|
|
260
|
+
GATE cleared) and read $D41B immediately. The value differs between
|
|
261
|
+
revisions because the two chips reset and restart the accumulator
|
|
262
|
+
differently — measured in VICE x64sc reSID, `-sidmodel 0` returns 3
|
|
263
|
+
and `-sidmodel 1` returns 2; real-hardware values are unverified here,
|
|
264
|
+
treat them as the same. Detection is imprecise (some SIDs answer
|
|
265
|
+
ambiguously) — use it only to select between precomputed cutoff
|
|
266
|
+
tables, and offer a settings toggle as the fallback. When testing this
|
|
267
|
+
in headless VICE, the $D41B/$D41C reads are only meaningful with a
|
|
268
|
+
real sound sink (`-sound -sounddev wav -soundarg out.wav`); the usual
|
|
269
|
+
`+sound` invocation returns meaningless values and would make the
|
|
270
|
+
detection look non-deterministic (see `sidasid_emulation_notes` in
|
|
271
|
+
music-sid).
|
|
272
|
+
|
|
273
|
+
### Worked example
|
|
274
|
+
|
|
275
|
+
```asm
|
|
276
|
+
; SID music player with per-chip filter table selection
|
|
277
|
+
; shadow_d416 and shadow_d417 are maintained throughout playback
|
|
278
|
+
|
|
279
|
+
chip_is_8580: .byte 0 ; 0 = 6581, 1 = 8580
|
|
280
|
+
|
|
281
|
+
; Filter cutoff tables (simplified; real tables have 64 or more entries)
|
|
282
|
+
cutoff_tbl_6581:
|
|
283
|
+
; These values are pre-mapped to produce evenly-spaced Hz steps
|
|
284
|
+
; on an average 6581 (they cluster in the $30-$A0 range)
|
|
285
|
+
.byte $10, $1A, $26, $34, $44, $58, $6C, $82, $96, $AA, $BC, $CC
|
|
286
|
+
|
|
287
|
+
cutoff_tbl_8580:
|
|
288
|
+
; 8580 is linear, so these are evenly spaced $00-$FF
|
|
289
|
+
.byte $15, $2A, $40, $55, $6A, $80, $95, $AA, $BF, $D5, $EA, $FF
|
|
290
|
+
|
|
291
|
+
; In the play routine, set cutoff using the appropriate table:
|
|
292
|
+
set_cutoff:
|
|
293
|
+
; A = note/step index into the cutoff table
|
|
294
|
+
tay
|
|
295
|
+
lda chip_is_8580
|
|
296
|
+
bne use_8580
|
|
297
|
+
lda cutoff_tbl_6581,y
|
|
298
|
+
jmp write_cutoff
|
|
299
|
+
use_8580:
|
|
300
|
+
lda cutoff_tbl_8580,y
|
|
301
|
+
write_cutoff:
|
|
302
|
+
sta shadow_d416
|
|
303
|
+
sta $D416
|
|
304
|
+
rts
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
### Cross-references
|
|
308
|
+
|
|
309
|
+
- Registers: D415, D416, D417, D418
|
|
310
|
+
- Technique: `sid_filter_routing` — full filter setup and mode documentation
|
|
311
|
+
- Technique: `sid_8580_vs_6581_differences` — chip revision differences overview
|
|
312
|
+
|
|
313
|
+
---
|
|
314
|
+
|
|
315
|
+
## sid_adsr_bug_8580 — 8580 ADSR-reset bug on hard restart; the rate-counter wrap is ~33 ms on both chips
|
|
316
|
+
|
|
317
|
+
**Severity:** high
|
|
318
|
+
**Region:** both
|
|
319
|
+
**Triggered by registers:** D404, D40B, D412
|
|
320
|
+
**Triggered by techniques:** digi_8bit_hard_restart, sid_8580_vs_6581_differences, sfx_in_player, goattracker_player_api
|
|
321
|
+
|
|
322
|
+
### Symptom
|
|
323
|
+
|
|
324
|
+
A hard restart that behaves on a 6581 leaves an 8580 voice holding
|
|
325
|
+
its level for a moment before the attack starts (the 8580 reset-path
|
|
326
|
+
difference below), and on either chip a note whose AD/SR nibbles were
|
|
327
|
+
lowered while the rate counter had already passed the new period
|
|
328
|
+
starts up to 33 ms late (the rate-counter wrap). The effect is most
|
|
329
|
+
audible on fast-changing note sequences (arpeggios). An earlier version
|
|
330
|
+
of this Symptom also described digi samples coming out "louder, quieter,
|
|
331
|
+
or with a shifted peak on 8580" and players with "envelope timing drift
|
|
332
|
+
on the other chip"; both rested on a per-chip attack-step difference
|
|
333
|
+
that the Mechanism below withdraws — none has been measured.
|
|
334
|
+
|
|
335
|
+
### Mechanism
|
|
336
|
+
|
|
337
|
+
Both the 6581 and 8580 share an ADSR bug rooted in the design of
|
|
338
|
+
the 15-bit envelope rate counter. The bug triggers when the CPU
|
|
339
|
+
writes a new (smaller) rate value to $D405 or $D406 at a time when
|
|
340
|
+
the envelope's internal 15-bit rate counter has already counted past
|
|
341
|
+
the new target threshold for the current phase. In that case the
|
|
342
|
+
counter must wrap through its full 15-bit range (0-32767 steps)
|
|
343
|
+
before the envelope generator reads the new rate and produces the
|
|
344
|
+
next envelope increment. At PAL clock (985248 Hz), a full 15-bit
|
|
345
|
+
wrap takes up to 32768 cycles — approximately 33 milliseconds.
|
|
346
|
+
|
|
347
|
+
The wrap length does not depend on the chip or on the new rate: it
|
|
348
|
+
is up to 32768 cycles (~33 ms at PAL) on both the 6581 and the 8580,
|
|
349
|
+
because the 15-bit counter must come back round to the new compare
|
|
350
|
+
value however small that value is (measured in VICE reSID: writing
|
|
351
|
+
attack 0 after the counter had run 5,120 cycles stalled the envelope
|
|
352
|
+
27,627 cycles on the 6581 model and 27,240 on the 8580; writing
|
|
353
|
+
attack 1 instead gave 27,173 / 27,350; the rate-0 attack itself takes
|
|
354
|
+
~2,280 cycles, which is where the 2 ms figure belongs). An earlier
|
|
355
|
+
version of this paragraph said the 6581's wrap at rate 0 was "close
|
|
356
|
+
to the 2 ms attack time" — that confused the attack duration with the
|
|
357
|
+
wrap. Hard restart works by writing AD/SR = 0 two frames before the
|
|
358
|
+
gate: 2 × 19,705 = 39,410 cycles covers the worst-case wrap, one
|
|
359
|
+
frame (19,705) does not.
|
|
360
|
+
|
|
361
|
+
**8580 behavior and the reset bug.** The 8580 introduced an internal
|
|
362
|
+
difference in the envelope reset path. On hard restart — where the
|
|
363
|
+
code writes AD=0 and SR=0 then clears GATE — the 8580's envelope
|
|
364
|
+
can enter an intermediate "hold at level" state caused by the reset
|
|
365
|
+
bug before it begins the release phase. At rate 0 the envelope steps
|
|
366
|
+
roughly every 9 φ2 cycles on either chip (2 ms / 256 steps = 7.7 µs
|
|
367
|
+
by the datasheet; measured in VICE reSID, 6581 and 8580 models, the
|
|
368
|
+
255 steps to peak take about 2,100-2,300 cycles ≈ 2.1-2.3 ms, with
|
|
369
|
+
the two models within half a cycle per step of each other). An
|
|
370
|
+
earlier version said ~15 cycles on the 6581 and 17 on the 8580 and
|
|
371
|
+
attributed the 17 to a silicon measurement; neither number came from
|
|
372
|
+
a measurement, and the quoted formula did not evaluate to 15.
|
|
373
|
+
|
|
374
|
+
**Hard-restart digi (digi_8bit_hard_restart) cross-chip impact.**
|
|
375
|
+
The Hermit-style hard-restart digi technique depends on the voice's
|
|
376
|
+
envelope reaching a known level within a precisely-timed window. No
|
|
377
|
+
per-chip rate-0 step difference has been measured (in reSID the two
|
|
378
|
+
models reach peak within the same poll count), so an earlier version
|
|
379
|
+
of this paragraph, which derived a systematic 1-3 count envelope
|
|
380
|
+
offset on the 8580 from a slower attack step, rested on a premise
|
|
381
|
+
that has been withdrawn. Digi routines do drift between real chips,
|
|
382
|
+
but for other reasons — DC offset, the volume DAC's mixing
|
|
383
|
+
nonlinearity and the filter — not from the attack step rate.
|
|
384
|
+
|
|
385
|
+
### Fix
|
|
386
|
+
|
|
387
|
+
**Always use a hard-restart sequence.** Both chip revisions benefit
|
|
388
|
+
from a two-frame hard restart. The one shown in the worked example
|
|
389
|
+
below is the **test-bit restart** variant listed under hard-restart
|
|
390
|
+
variants in sid-reference.md: one frame with AD=0/SR=0 and GATE
|
|
391
|
+
cleared, one frame with the TEST bit set (GATE still clear) to lock
|
|
392
|
+
the oscillator, then the real note gate on the third frame, so the
|
|
393
|
+
note frame supplies the 0→1 GATE edge and the attack begins from
|
|
394
|
+
zero. The "Classic" form that sid-reference.md and music-sid.md show
|
|
395
|
+
is a different sequence: SR = $F0 (not 0) on the first frame and $09
|
|
396
|
+
= TEST+GATE on the middle frame, so the envelope is already gated at
|
|
397
|
+
the note frame (ENV3 $FF before and after the note-frame write in
|
|
398
|
+
reSID) and that write does not start a fresh attack. An earlier
|
|
399
|
+
version of this entry presented the test-bit form as "the full
|
|
400
|
+
hard-restart sequence" without distinguishing it from the classic
|
|
401
|
+
one. Do not mix the two: with AD=0/SR=0 on frame 1, a $09 middle
|
|
402
|
+
frame would let the envelope decay to zero during the TEST+GATE frame
|
|
403
|
+
and, because a GATE-held write does not retrigger the attack
|
|
404
|
+
(measured in VICE reSID), the note frame would play silently. Either
|
|
405
|
+
form forces the rate counter through its wrap before the new note
|
|
406
|
+
begins.
|
|
407
|
+
|
|
408
|
+
**Do not pick an attack rate for cross-chip consistency without
|
|
409
|
+
measuring it.** No source supports rate 1 being more consistent
|
|
410
|
+
between revisions than rate 0 (an earlier version of this paragraph
|
|
411
|
+
said it was). In VICE reSID both rates jitter by the rate counter's
|
|
412
|
+
phase at gate-on and neither shows a systematic 6581/8580 difference:
|
|
413
|
+
the rate-0 attack reached $FF in 2,080-2,300 cycles and the rate-1
|
|
414
|
+
attack in 7,460-8,070 cycles on both chip models (x64sc, `-sound
|
|
415
|
+
-sounddev wav`, 16 trials per rate; runs made with `+sound` return
|
|
416
|
+
meaningless ENV3 values, see music-sid). What does change the rate-0
|
|
417
|
+
time is the ADSR delay bug: lowering the attack nibble while the rate
|
|
418
|
+
counter has passed the new period cost one trial 33 ms (the full
|
|
419
|
+
15-bit wrap), which is why the hard-restart sequence above matters
|
|
420
|
+
more than the rate you choose.
|
|
421
|
+
|
|
422
|
+
**Chip-detect then branch.** Detect the SID revision at startup (see
|
|
423
|
+
the $D41B method in `sid_filter_chip_variation` above, or
|
|
424
|
+
`sid_8580_vs_6581_differences`) if you need per-chip filter tables.
|
|
425
|
+
An earlier version of this section followed with a listing of
|
|
426
|
+
per-chip digi timing constants (`.byte 15` for the 6581, `.byte 17
|
|
427
|
+
; empirically measured on 8580 silicon` for the 8580); neither value
|
|
428
|
+
came from a measurement, the only measured figure is one number for
|
|
429
|
+
both chips (~9 cycles per attack step at rate 0, VICE reSID), and the
|
|
430
|
+
listing has been removed rather than corrected — do not keep a
|
|
431
|
+
per-chip constant.
|
|
432
|
+
|
|
433
|
+
**For music (not digi):** the standard hard restart is sufficient
|
|
434
|
+
for music playback on both revisions. Only digi routines that
|
|
435
|
+
require sub-millisecond envelope-level precision need to care about
|
|
436
|
+
the attack step timing at all, and no per-chip difference in it has
|
|
437
|
+
been measured.
|
|
438
|
+
|
|
439
|
+
### Worked example
|
|
440
|
+
|
|
441
|
+
```asm
|
|
442
|
+
; Test-bit hard restart (sid-reference.md's "Test-bit restart" variant,
|
|
443
|
+
; not the "Classic" SR=$F0 / $09 form) with explicit cycle-count comments
|
|
444
|
+
; Applied to voice 1 two frames before the new note
|
|
445
|
+
|
|
446
|
+
; FRAME N-2: disable ADSR, gate off
|
|
447
|
+
hard_restart_frame1:
|
|
448
|
+
lda #$00
|
|
449
|
+
sta $D405 ; AD1 = 0 (fastest attack/decay) [4 cycles]
|
|
450
|
+
sta $D406 ; SR1 = 0 (no sustain, fastest release) [4 cycles]
|
|
451
|
+
lda shadow_d404
|
|
452
|
+
and #$FE ; clear GATE bit [2 cycles]
|
|
453
|
+
sta shadow_d404
|
|
454
|
+
sta $D404 ; voice 1: waveform preserved, GATE=0 [4 cycles]
|
|
455
|
+
; envelope now begins release from current level
|
|
456
|
+
rts
|
|
457
|
+
|
|
458
|
+
; FRAME N-1: set TEST bit to lock oscillator at zero phase
|
|
459
|
+
hard_restart_frame2:
|
|
460
|
+
lda shadow_d404
|
|
461
|
+
and #$FE ; ensure GATE still clear
|
|
462
|
+
ora #$08 ; set TEST bit (bit 3)
|
|
463
|
+
sta shadow_d404
|
|
464
|
+
sta $D404 ; oscillator locked at zero DC [4 cycles]
|
|
465
|
+
; GATE stays clear here (test-bit variant): the note frame
|
|
466
|
+
; supplies the 0->1 GATE edge. The classic form writes $09
|
|
467
|
+
; (TEST+GATE) instead, but only with SR=$F0 on frame 1.
|
|
468
|
+
rts
|
|
469
|
+
|
|
470
|
+
; FRAME N (note frame): release TEST, set real waveform + GATE
|
|
471
|
+
hard_restart_note:
|
|
472
|
+
; X = ADSR attack/decay byte for this note
|
|
473
|
+
; Y = ADSR sustain/release byte for this note
|
|
474
|
+
; A = waveform + GATE byte for this note
|
|
475
|
+
stx $D405 ; real attack/decay
|
|
476
|
+
sty $D406 ; real sustain/release
|
|
477
|
+
sta shadow_d404
|
|
478
|
+
sta $D404 ; clears TEST, sets waveform, sets GATE
|
|
479
|
+
; Attack begins from zero; oscillator released from phase-zero lock
|
|
480
|
+
; attack steps every ~9 cycles at rate 0 (VICE reSID, both models)
|
|
481
|
+
rts
|
|
482
|
+
```
|
|
483
|
+
|
|
484
|
+
An earlier version of this listing ended with a
|
|
485
|
+
`compute_digi_irq_interval` routine returning 15 for the 6581 and 17
|
|
486
|
+
for the 8580 as "cycles per attack step"; neither figure was measured
|
|
487
|
+
and the two chips show no difference in reSID, so it has been removed.
|
|
488
|
+
|
|
489
|
+
### Cross-references
|
|
490
|
+
|
|
491
|
+
- Registers: D404, D40B, D412 (VCREG for all three voices — hard restart
|
|
492
|
+
applies to whichever voice is being gated)
|
|
493
|
+
- Technique: `digi_8bit_hard_restart` — the hard-restart digi family and
|
|
494
|
+
the measured rate-0 envelope timing (9 φ2 cycles per attack step in
|
|
495
|
+
reSID on both chip models; the exact Hermit/Mahoney register sequences
|
|
496
|
+
are not documented in this knowledge base — an earlier version of this
|
|
497
|
+
line promised "full Hermit/Mahoney digi routine documentation")
|
|
498
|
+
- Pitfall: `sid_write_only_registers` — always use shadow for D404 read-modify-write
|
|
499
|
+
|
|
500
|
+
---
|
|
501
|
+
|
|
502
|
+
## sid_voice3_disable_silent_bit — Bit 7 of $D418 silences voice 3 on 6581 but not all 8580 revisions; relying on it for muted melodic tricks is fragile
|
|
503
|
+
|
|
504
|
+
**Severity:** medium
|
|
505
|
+
**Region:** both
|
|
506
|
+
**Triggered by registers:** D418
|
|
507
|
+
**Triggered by techniques:** sid_voice_setup, lfsr_random
|
|
508
|
+
|
|
509
|
+
### Symptom
|
|
510
|
+
|
|
511
|
+
Code that configures voice 3 as a silent LFO or random-number source
|
|
512
|
+
by setting $D418 bit 7 (the "3OFF" bit) hears voice 3 leaking
|
|
513
|
+
audibly on some 8580 boards. The leak is a faint but perceptible
|
|
514
|
+
tone or noise — particularly noticeable in quiet passages. Conversely,
|
|
515
|
+
a "muted melodic" arrangement that intentionally plays a pitched
|
|
516
|
+
voice 3 melody silenced via bit 7 (a known trick on 6581 for adding
|
|
517
|
+
a silent modulator voice) fails on certain 8580 revisions where the
|
|
518
|
+
voice is clearly audible.
|
|
519
|
+
|
|
520
|
+
### Mechanism
|
|
521
|
+
|
|
522
|
+
Bit 7 of $D418 (SIGVOL) is documented as "3OFF: disconnect voice 3
|
|
523
|
+
from audio output." The mechanism differs between chip revisions.
|
|
524
|
+
|
|
525
|
+
**6581 behavior.** On the 6581, the 3OFF bit reliably disconnects
|
|
526
|
+
voice 3 from the mixer that feeds the volume DAC. Voice 3's
|
|
527
|
+
oscillator and envelope continue running normally — the oscillator
|
|
528
|
+
output is still visible at $D41B (RANDOM/OSC3) and the envelope at
|
|
529
|
+
$D41C (ENV3) — but no audio from voice 3 reaches the output pin.
|
|
530
|
+
This is the basis for the classic pattern of using voice 3 as a free
|
|
531
|
+
LFO or random source without its sound being audible.
|
|
532
|
+
|
|
533
|
+
**8580 behavior — filter routing caveat applies to both chips.**
|
|
534
|
+
If voice 3 has its FILT3 bit set in $D417 (routing it through the
|
|
535
|
+
filter), then $D418 bit 7 does NOT silence it on either chip.
|
|
536
|
+
The 3OFF bit only disconnects the bypass path (the direct route from
|
|
537
|
+
the envelope output to the volume DAC that bypasses the filter). A
|
|
538
|
+
filtered voice 3 still sends its post-filter signal to the DAC
|
|
539
|
+
regardless of bit 7. This behavior is consistent between 6581 and
|
|
540
|
+
8580 — it is simply not always understood.
|
|
541
|
+
|
|
542
|
+
**8580 revision differences on the bypass path.** On 8580 chips
|
|
543
|
+
manufactured after approximately 1990 (typically the R5 revision and
|
|
544
|
+
later), the 3OFF bit's implementation was revised as part of broader
|
|
545
|
+
cost-reduction changes. The bit was originally undocumented and was
|
|
546
|
+
added for the "voice 3 as LFO" use case that had emerged in the C64
|
|
547
|
+
development community. Some 8580 R5 revisions implement 3OFF by
|
|
548
|
+
cutting the voice 3 output from the bypass summing node but do not
|
|
549
|
+
fully isolate the signal path, leaving a small residual. The bleed
|
|
550
|
+
is typically below -40 dB relative to a full-volume voice, but at
|
|
551
|
+
high master volume (VOL=15) and in quiet musical contexts it is
|
|
552
|
+
audible.
|
|
553
|
+
|
|
554
|
+
A secondary mechanism: voice 3 routed through the filter (FILT3=1 in
|
|
555
|
+
$D417) is entirely unaffected by 3OFF on any chip revision. Code
|
|
556
|
+
that sets FILT3 and 3OFF simultaneously and expects silence will hear
|
|
557
|
+
voice 3 through the filter on all chip revisions. This is by far the
|
|
558
|
+
more common source of the symptom compared to the 8580 R5 revision
|
|
559
|
+
bypass issue.
|
|
560
|
+
|
|
561
|
+
### Fix
|
|
562
|
+
|
|
563
|
+
**Do not rely solely on $D418 bit 7 for silence.** The safe approach
|
|
564
|
+
for voice 3 used as an LFO or modulation source is:
|
|
565
|
+
|
|
566
|
+
1. Clear FILT3 in $D417 (voice 3 bypasses the filter; bit 7 then
|
|
567
|
+
works as intended on 6581 and most 8580s).
|
|
568
|
+
2. Set the voice 3 sustain level to 0 (SUSTAIN nibble of $D414
|
|
569
|
+
= 0). With sustain at zero, the envelope decays to zero after
|
|
570
|
+
the attack and decay phases and then holds at zero. Even if
|
|
571
|
+
the bypass bleed reaches the output on some 8580 R5 chips,
|
|
572
|
+
zero envelope means zero audio.
|
|
573
|
+
3. Optionally, set $D418 bit 7 as well for belt-and-suspenders
|
|
574
|
+
protection on 6581 (where the bit works reliably).
|
|
575
|
+
|
|
576
|
+
For the voice-3-as-LFO pattern ($D41B read each frame), the
|
|
577
|
+
oscillator continues running at zero envelope perfectly well.
|
|
578
|
+
ENV3 ($D41C) is the envelope itself — if sustain=0, ENV3 reads
|
|
579
|
+
zero during the sustain phase, which means ENV3-as-LFO only
|
|
580
|
+
provides signal during the attack and decay portions of the
|
|
581
|
+
envelope. For a sustained LFO, use the oscillator output ($D41B,
|
|
582
|
+
set voice 3 to TRI or SAW waveform) rather than the envelope output.
|
|
583
|
+
|
|
584
|
+
### Worked example
|
|
585
|
+
|
|
586
|
+
```asm
|
|
587
|
+
; FRAGILE: relies only on $D418 bit 7 — fails on 8580 R5 if FILT3 is set,
|
|
588
|
+
; and may have subtle bleed on late 8580 revisions
|
|
589
|
+
|
|
590
|
+
; This sets 3OFF but forgets to clear FILT3:
|
|
591
|
+
lda shadow_d417
|
|
592
|
+
ora #$04 ; FILT3 = 1 (voice 3 through filter) -- MISTAKE
|
|
593
|
+
sta shadow_d417
|
|
594
|
+
sta $D417
|
|
595
|
+
|
|
596
|
+
lda shadow_d418
|
|
597
|
+
ora #$80 ; 3OFF = 1 -- doesn't silence filtered voice 3!
|
|
598
|
+
sta shadow_d418
|
|
599
|
+
sta $D418
|
|
600
|
+
|
|
601
|
+
; Result: voice 3 still audible on ALL chip revisions because FILT3 overrides 3OFF.
|
|
602
|
+
|
|
603
|
+
; ---------------------------------------------------------------
|
|
604
|
+
|
|
605
|
+
; SAFE: sustain-zero + clear FILT3 approach, works on all chips
|
|
606
|
+
|
|
607
|
+
; Voice 3 as triangle LFO source — silent on all revisions:
|
|
608
|
+
init_voice3_lfo:
|
|
609
|
+
lda #$10 ; low frequency: slow LFO sweep
|
|
610
|
+
sta $D40E ; FRELO3
|
|
611
|
+
lda #$00
|
|
612
|
+
sta $D40F ; FREHI3
|
|
613
|
+
|
|
614
|
+
lda #$00 ; fastest attack, fastest decay
|
|
615
|
+
sta $D413 ; ATDCY3
|
|
616
|
+
lda #$00 ; sustain 0 — envelope goes to zero after decay
|
|
617
|
+
sta $D414 ; SUREL3: sustain nibble=0, release rate=0
|
|
618
|
+
|
|
619
|
+
lda #$11 ; TRI waveform + GATE on
|
|
620
|
+
sta shadow_d412
|
|
621
|
+
sta $D412 ; voice 3 oscillator starts running
|
|
622
|
+
|
|
623
|
+
; Clear FILT3 (remove voice 3 from filter path)
|
|
624
|
+
lda shadow_d417
|
|
625
|
+
and #%11111011 ; clear bit 2 (FILT3)
|
|
626
|
+
sta shadow_d417
|
|
627
|
+
sta $D417
|
|
628
|
+
|
|
629
|
+
; Now set 3OFF for belt-and-suspenders on 6581
|
|
630
|
+
lda shadow_d418
|
|
631
|
+
ora #$80 ; 3OFF = 1
|
|
632
|
+
sta shadow_d418
|
|
633
|
+
sta $D418
|
|
634
|
+
|
|
635
|
+
; Result: voice 3 oscillator runs (readable at $D41B), envelope at 0,
|
|
636
|
+
; FILT3 clear so 3OFF is effective, sustain=0 so even if bypass bleeds
|
|
637
|
+
; the audio amplitude is zero. Safe on all chip revisions.
|
|
638
|
+
rts
|
|
639
|
+
|
|
640
|
+
; Using the LFO value each frame (e.g. in raster IRQ):
|
|
641
|
+
lfo_tick:
|
|
642
|
+
lda $D41B ; read OSC3 — triangle LFO value 0-255 cycling
|
|
643
|
+
lsr a ; scale down for pulse-width modulation
|
|
644
|
+
lsr a
|
|
645
|
+
sta shadow_d403
|
|
646
|
+
sta $D403 ; write to voice 1 PWHI — modulate pulse width
|
|
647
|
+
rts
|
|
648
|
+
```
|
|
649
|
+
|
|
650
|
+
### Cross-references
|
|
651
|
+
|
|
652
|
+
- Register: D418 (bit 7 = 3OFF, bits 4-0 = filter mode and volume)
|
|
653
|
+
- Register: D417 (bit 2 = FILT3 — must be clear for 3OFF to work)
|
|
654
|
+
- Technique: `sid_voice_setup` — full voice 3 LFO and modulation patterns
|
|
655
|
+
- Technique: `sid_filter_routing` — filter voice routing and the FILT3 interaction
|
|
656
|
+
|
|
657
|
+
---
|
|
658
|
+
|
|
659
|
+
## sid_replacement_d41b_unreadable — Some SID replacements cannot read back $D41B; a random seed from it can become constant or low-entropy
|
|
660
|
+
|
|
661
|
+
**Severity:** medium
|
|
662
|
+
**Region:** both
|
|
663
|
+
**Triggered by registers:** D41B
|
|
664
|
+
**Triggered by techniques:** lfsr_random, sid_8580_vs_6581_differences
|
|
665
|
+
|
|
666
|
+
### Symptom
|
|
667
|
+
|
|
668
|
+
A game seeded from SID voice 3 noise plays the same "random" sequence
|
|
669
|
+
on every run, or its enemies, spawns and level layouts never vary, on a
|
|
670
|
+
machine fitted with a SID replacement. The same program varies on a
|
|
671
|
+
real 6581 or 8580. Chip-detection code that reads `$D41B` reports the
|
|
672
|
+
wrong chip or none. Nothing here was run on such hardware; this page
|
|
673
|
+
has no instrument for it (rung 4, from the sources below).
|
|
674
|
+
|
|
675
|
+
### Mechanism
|
|
676
|
+
|
|
677
|
+
The seeding pattern in `lfsr_random` sets voice 3 to noise at frequency
|
|
678
|
+
`$FFFF` and reads the oscillator output at `$D41B` twice. On a real SID
|
|
679
|
+
the register changes on every read. SID replacements are
|
|
680
|
+
microcontrollers or FPGAs that emulate the chip, and not all of them
|
|
681
|
+
emulate the read side:
|
|
682
|
+
|
|
683
|
+
- **SwinSID.** C64-Wiki: "SwinSID does not support reading registers",
|
|
684
|
+
and games that "read from $D41B to generate random numbers" (it names
|
|
685
|
+
Fort Apocalypse, Uridium, Pirates! and Paradroid) "behave strangely".
|
|
686
|
+
Paddles and mice do not work either, because SwinSID has no
|
|
687
|
+
analogue-to-digital converter for `$D419`/`$D41A`.
|
|
688
|
+
- **SwinSID Nano.** The SIDDetector-II README says its `$D41B` does move,
|
|
689
|
+
but only at about 44 kHz, so back-to-back reads can return the same
|
|
690
|
+
value; SIDDetector-II uses exactly that to tell it from a real SID.
|
|
691
|
+
C64-Wiki names the SwinSID Nano as the SwinSID it describes, so the two
|
|
692
|
+
sources disagree about whether its read moves at all.
|
|
693
|
+
- **No SID, or an Ultimate II+ with its virtual SID off.** The same README
|
|
694
|
+
reports bus noise at about 44 kHz from the cartridge, which SIDDetector-II
|
|
695
|
+
cannot tell from a SwinSID Nano.
|
|
696
|
+
- **ARMSID, SwinSID Ultimate and others** echo written values in their
|
|
697
|
+
voice-3 read registers when sent an identification string (SIDDetector-II
|
|
698
|
+
README); what they return at `$D41B` during normal play is not stated
|
|
699
|
+
there.
|
|
700
|
+
|
|
701
|
+
Where the read does not move, or moves slowly, the two reads can give
|
|
702
|
+
the same bytes on every run and the seed can be constant or low-entropy;
|
|
703
|
+
an undriven read may also return open-bus data. This is an inference from
|
|
704
|
+
the sources (rung 4), not something C64-Wiki states. `$D41B`-based chip detection
|
|
705
|
+
(`sid_8580_vs_6581_differences`, "Chip detection at runtime") fails for
|
|
706
|
+
the same reason.
|
|
707
|
+
|
|
708
|
+
### Fix
|
|
709
|
+
|
|
710
|
+
Do not make `$D41B` the only source of the seed. XOR in CIA1 timer A
|
|
711
|
+
(`$DC04/$DC05`) and, better, the frame count until the player's first
|
|
712
|
+
fire press; `lfsr_random` in `techniques/maths.md` already describes
|
|
713
|
+
both. The recipe's own check can also detect the problem: it counts how
|
|
714
|
+
many of 255 consecutive `$D41B` read pairs differ (255 in VICE). A count
|
|
715
|
+
far below 255 means the SID read is not moving and the seed should come
|
|
716
|
+
from the other sources (an inference from the sources above, not
|
|
717
|
+
measured here). Keep the zero check from `lfsr_zero_state_lockup`: a bus
|
|
718
|
+
that reads `$00` gives a zero seed.
|
|
719
|
+
|
|
720
|
+
Recipes that seed from `$D41B` alone: `recipes/oscar64/lfsr-random.md`
|
|
721
|
+
(it also prints the CIA timer but does not mix it into the seed) and
|
|
722
|
+
`recipes/oscar64/platformer-scaffold.md` (two `sid.random` reads, then
|
|
723
|
+
the zero check). Both can give a constant or low-entropy seed on a SwinSID (rung 4).
|
|
724
|
+
|
|
725
|
+
### Cross-references
|
|
726
|
+
|
|
727
|
+
- Technique `lfsr_random` — seeding from SID, CIA timer and player input
|
|
728
|
+
- Technique `sid_8580_vs_6581_differences` — `$D41B` chip detection
|
|
729
|
+
- Pitfall `lfsr_zero_state_lockup` (`pitfalls/cpu.md`) — the zero seed
|
|
730
|
+
- Register `$D41B` (OSC3) — `../hardware/sid-reference.md`
|
|
731
|
+
|
|
732
|
+
**Sources.** C64-Wiki, "SwinSID": https://www.c64-wiki.com/wiki/SwinSID.
|
|
733
|
+
SIDDetector-II README (steps "SwinSID Nano" and "ARMSID / ARM2SID / Swinsid
|
|
734
|
+
Ultimate"): https://github.com/MichaelTroelsen/SIDDetector-II.
|
|
735
|
+
|
|
736
|
+
---
|
|
737
|
+
|
|
738
|
+
*Cross-references: [docs/hardware/sid-reference.md](../hardware/sid-reference.md) for the complete SID register map, ADSR table, and hard-restart programming patterns. [docs/techniques/music-sid.md](../techniques/music-sid.md) for sid_voice_setup, sid_filter_routing, sid_8580_vs_6581_differences, and digi_8bit_hard_restart technique documentation.*
|