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.
Files changed (970) hide show
  1. package/CHANGELOG.md +2082 -0
  2. package/LICENSE +29 -0
  3. package/README.md +559 -0
  4. package/VERSION +25 -0
  5. package/dist/cli/setup.js +66 -0
  6. package/dist/cli/setup.js.map +1 -0
  7. package/dist/cli-memorize-audit.js +97 -0
  8. package/dist/cli-memorize-audit.js.map +1 -0
  9. package/dist/cli.js +335 -0
  10. package/dist/cli.js.map +1 -0
  11. package/dist/config.js +83 -0
  12. package/dist/config.js.map +1 -0
  13. package/dist/context.js +53 -0
  14. package/dist/context.js.map +1 -0
  15. package/dist/domain/budget-notes.js +104 -0
  16. package/dist/domain/budget-notes.js.map +1 -0
  17. package/dist/domain/budget.js +417 -0
  18. package/dist/domain/budget.js.map +1 -0
  19. package/dist/domain/game-design.js +77 -0
  20. package/dist/domain/game-design.js.map +1 -0
  21. package/dist/domain/timing.js +39 -0
  22. package/dist/domain/timing.js.map +1 -0
  23. package/dist/graph/apply.js +132 -0
  24. package/dist/graph/apply.js.map +1 -0
  25. package/dist/graph/claims.js +231 -0
  26. package/dist/graph/claims.js.map +1 -0
  27. package/dist/graph/extract/archetype.js +118 -0
  28. package/dist/graph/extract/archetype.js.map +1 -0
  29. package/dist/graph/extract/common.js +69 -0
  30. package/dist/graph/extract/common.js.map +1 -0
  31. package/dist/graph/extract/failure.js +37 -0
  32. package/dist/graph/extract/failure.js.map +1 -0
  33. package/dist/graph/extract/game-design.js +189 -0
  34. package/dist/graph/extract/game-design.js.map +1 -0
  35. package/dist/graph/extract/hardware.js +124 -0
  36. package/dist/graph/extract/hardware.js.map +1 -0
  37. package/dist/graph/extract/pitfall.js +62 -0
  38. package/dist/graph/extract/pitfall.js.map +1 -0
  39. package/dist/graph/extract/raster-band.js +56 -0
  40. package/dist/graph/extract/raster-band.js.map +1 -0
  41. package/dist/graph/extract/recipe.js +71 -0
  42. package/dist/graph/extract/recipe.js.map +1 -0
  43. package/dist/graph/extract/technique-entities.js +180 -0
  44. package/dist/graph/extract/technique-entities.js.map +1 -0
  45. package/dist/graph/extract/technique.js +179 -0
  46. package/dist/graph/extract/technique.js.map +1 -0
  47. package/dist/graph/extract/toolchain.js +67 -0
  48. package/dist/graph/extract/toolchain.js.map +1 -0
  49. package/dist/graph/extract/types.js +3 -0
  50. package/dist/graph/extract/types.js.map +1 -0
  51. package/dist/graph/extract/vocabulary.js +65 -0
  52. package/dist/graph/extract/vocabulary.js.map +1 -0
  53. package/dist/graph/extract.js +43 -0
  54. package/dist/graph/extract.js.map +1 -0
  55. package/dist/graph/kernal-clobbers.js +64 -0
  56. package/dist/graph/kernal-clobbers.js.map +1 -0
  57. package/dist/graph/machine-variants.js +208 -0
  58. package/dist/graph/machine-variants.js.map +1 -0
  59. package/dist/ingest/files.js +99 -0
  60. package/dist/ingest/files.js.map +1 -0
  61. package/dist/ingest/passes.js +104 -0
  62. package/dist/ingest/passes.js.map +1 -0
  63. package/dist/ingest/points.js +50 -0
  64. package/dist/ingest/points.js.map +1 -0
  65. package/dist/ingest/report.js +140 -0
  66. package/dist/ingest/report.js.map +1 -0
  67. package/dist/ingest/tally.js +81 -0
  68. package/dist/ingest/tally.js.map +1 -0
  69. package/dist/ingest/verified-on.js +43 -0
  70. package/dist/ingest/verified-on.js.map +1 -0
  71. package/dist/ingest.js +135 -0
  72. package/dist/ingest.js.map +1 -0
  73. package/dist/schemas/cost-basis.js +5 -0
  74. package/dist/schemas/cost-basis.js.map +1 -0
  75. package/dist/schemas/machine-variant.js +25 -0
  76. package/dist/schemas/machine-variant.js.map +1 -0
  77. package/dist/schemas/plan-budget.js +121 -0
  78. package/dist/schemas/plan-budget.js.map +1 -0
  79. package/dist/schemas/tool-outputs.js +526 -0
  80. package/dist/schemas/tool-outputs.js.map +1 -0
  81. package/dist/server/define-tool.js +52 -0
  82. package/dist/server/define-tool.js.map +1 -0
  83. package/dist/server/defined-only.js +9 -0
  84. package/dist/server/defined-only.js.map +1 -0
  85. package/dist/server/tools-briefings.js +70 -0
  86. package/dist/server/tools-briefings.js.map +1 -0
  87. package/dist/server/tools-lookup.js +180 -0
  88. package/dist/server/tools-lookup.js.map +1 -0
  89. package/dist/server/tools-maintenance.js +116 -0
  90. package/dist/server/tools-maintenance.js.map +1 -0
  91. package/dist/server/tools-pitfalls.js +89 -0
  92. package/dist/server/tools-pitfalls.js.map +1 -0
  93. package/dist/server/tools-recipes.js +296 -0
  94. package/dist/server/tools-recipes.js.map +1 -0
  95. package/dist/server/tools-runtime.js +48 -0
  96. package/dist/server/tools-runtime.js.map +1 -0
  97. package/dist/server/tools.js +43 -0
  98. package/dist/server/tools.js.map +1 -0
  99. package/dist/server.js +95 -0
  100. package/dist/server.js.map +1 -0
  101. package/dist/services/analytics/schema.js +78 -0
  102. package/dist/services/analytics/schema.js.map +1 -0
  103. package/dist/services/analytics/statements.js +99 -0
  104. package/dist/services/analytics/statements.js.map +1 -0
  105. package/dist/services/analytics.js +195 -0
  106. package/dist/services/analytics.js.map +1 -0
  107. package/dist/services/bm25.js +158 -0
  108. package/dist/services/bm25.js.map +1 -0
  109. package/dist/services/chunker.js +81 -0
  110. package/dist/services/chunker.js.map +1 -0
  111. package/dist/services/embeddings.js +58 -0
  112. package/dist/services/embeddings.js.map +1 -0
  113. package/dist/services/falkor/base.js +85 -0
  114. package/dist/services/falkor/base.js.map +1 -0
  115. package/dist/services/falkor/links.js +361 -0
  116. package/dist/services/falkor/links.js.map +1 -0
  117. package/dist/services/falkor/nodes.js +183 -0
  118. package/dist/services/falkor/nodes.js.map +1 -0
  119. package/dist/services/falkor/params.js +45 -0
  120. package/dist/services/falkor/params.js.map +1 -0
  121. package/dist/services/falkor/schema.js +77 -0
  122. package/dist/services/falkor/schema.js.map +1 -0
  123. package/dist/services/falkor.js +100 -0
  124. package/dist/services/falkor.js.map +1 -0
  125. package/dist/services/qdrant.js +238 -0
  126. package/dist/services/qdrant.js.map +1 -0
  127. package/dist/services/versions.js +73 -0
  128. package/dist/services/versions.js.map +1 -0
  129. package/dist/services/vice-bin.js +51 -0
  130. package/dist/services/vice-bin.js.map +1 -0
  131. package/dist/tools/briefings/archetype.js +174 -0
  132. package/dist/tools/briefings/archetype.js.map +1 -0
  133. package/dist/tools/briefings/budget.js +177 -0
  134. package/dist/tools/briefings/budget.js.map +1 -0
  135. package/dist/tools/briefings/build-order.js +135 -0
  136. package/dist/tools/briefings/build-order.js.map +1 -0
  137. package/dist/tools/briefings/build.js +185 -0
  138. package/dist/tools/briefings/build.js.map +1 -0
  139. package/dist/tools/briefings/discovery.js +290 -0
  140. package/dist/tools/briefings/discovery.js.map +1 -0
  141. package/dist/tools/briefings/plan-pitfalls.js +69 -0
  142. package/dist/tools/briefings/plan-pitfalls.js.map +1 -0
  143. package/dist/tools/briefings/render.js +151 -0
  144. package/dist/tools/briefings/render.js.map +1 -0
  145. package/dist/tools/briefings/rows.js +41 -0
  146. package/dist/tools/briefings/rows.js.map +1 -0
  147. package/dist/tools/briefings/toolchain.js +63 -0
  148. package/dist/tools/briefings/toolchain.js.map +1 -0
  149. package/dist/tools/briefings/why-proposed.js +69 -0
  150. package/dist/tools/briefings/why-proposed.js.map +1 -0
  151. package/dist/tools/briefings.js +44 -0
  152. package/dist/tools/briefings.js.map +1 -0
  153. package/dist/tools/feedback.js +97 -0
  154. package/dist/tools/feedback.js.map +1 -0
  155. package/dist/tools/hydrate.js +75 -0
  156. package/dist/tools/hydrate.js.map +1 -0
  157. package/dist/tools/intelligence.js +67 -0
  158. package/dist/tools/intelligence.js.map +1 -0
  159. package/dist/tools/lint/asm-decimal.js +86 -0
  160. package/dist/tools/lint/asm-decimal.js.map +1 -0
  161. package/dist/tools/lint/asm-rules.js +221 -0
  162. package/dist/tools/lint/asm-rules.js.map +1 -0
  163. package/dist/tools/lint/asm-shared.js +3 -0
  164. package/dist/tools/lint/asm-shared.js.map +1 -0
  165. package/dist/tools/lint/c-rules.js +215 -0
  166. package/dist/tools/lint/c-rules.js.map +1 -0
  167. package/dist/tools/lint/text.js +122 -0
  168. package/dist/tools/lint/text.js.map +1 -0
  169. package/dist/tools/lint/types.js +40 -0
  170. package/dist/tools/lint/types.js.map +1 -0
  171. package/dist/tools/lint.js +76 -0
  172. package/dist/tools/lint.js.map +1 -0
  173. package/dist/tools/memorization-mcp.js +111 -0
  174. package/dist/tools/memorization-mcp.js.map +1 -0
  175. package/dist/tools/pitfalls/bm25.js +53 -0
  176. package/dist/tools/pitfalls/bm25.js.map +1 -0
  177. package/dist/tools/pitfalls/format.js +58 -0
  178. package/dist/tools/pitfalls/format.js.map +1 -0
  179. package/dist/tools/pitfalls/graph.js +124 -0
  180. package/dist/tools/pitfalls/graph.js.map +1 -0
  181. package/dist/tools/pitfalls.js +134 -0
  182. package/dist/tools/pitfalls.js.map +1 -0
  183. package/dist/tools/prompts.js +81 -0
  184. package/dist/tools/prompts.js.map +1 -0
  185. package/dist/tools/query/compatibility/closure.js +62 -0
  186. package/dist/tools/query/compatibility/closure.js.map +1 -0
  187. package/dist/tools/query/compatibility/facts.js +28 -0
  188. package/dist/tools/query/compatibility/facts.js.map +1 -0
  189. package/dist/tools/query/compatibility/fetch.js +164 -0
  190. package/dist/tools/query/compatibility/fetch.js.map +1 -0
  191. package/dist/tools/query/compatibility/hard-rules.js +159 -0
  192. package/dist/tools/query/compatibility/hard-rules.js.map +1 -0
  193. package/dist/tools/query/compatibility/index.js +27 -0
  194. package/dist/tools/query/compatibility/index.js.map +1 -0
  195. package/dist/tools/query/compatibility/kernal-zp-rule.js +90 -0
  196. package/dist/tools/query/compatibility/kernal-zp-rule.js.map +1 -0
  197. package/dist/tools/query/compatibility/render.js +106 -0
  198. package/dist/tools/query/compatibility/render.js.map +1 -0
  199. package/dist/tools/query/compatibility/rules.js +337 -0
  200. package/dist/tools/query/compatibility/rules.js.map +1 -0
  201. package/dist/tools/query/compatibility/unit-rules.js +253 -0
  202. package/dist/tools/query/compatibility/unit-rules.js.map +1 -0
  203. package/dist/tools/query/game-design.js +83 -0
  204. package/dist/tools/query/game-design.js.map +1 -0
  205. package/dist/tools/query/lookups.js +220 -0
  206. package/dist/tools/query/lookups.js.map +1 -0
  207. package/dist/tools/query/plan-budget.js +266 -0
  208. package/dist/tools/query/plan-budget.js.map +1 -0
  209. package/dist/tools/query/recipes.js +214 -0
  210. package/dist/tools/query/recipes.js.map +1 -0
  211. package/dist/tools/query/retrieval.js +128 -0
  212. package/dist/tools/query/retrieval.js.map +1 -0
  213. package/dist/tools/query/shared.js +121 -0
  214. package/dist/tools/query/shared.js.map +1 -0
  215. package/dist/tools/query/techniques.js +331 -0
  216. package/dist/tools/query/techniques.js.map +1 -0
  217. package/dist/tools/query/timing.js +106 -0
  218. package/dist/tools/query/timing.js.map +1 -0
  219. package/dist/tools/query/types.js +11 -0
  220. package/dist/tools/query/types.js.map +1 -0
  221. package/dist/tools/query.js +21 -0
  222. package/dist/tools/query.js.map +1 -0
  223. package/dist/tools/resources.js +186 -0
  224. package/dist/tools/resources.js.map +1 -0
  225. package/dist/tools/run-game.js +249 -0
  226. package/dist/tools/run-game.js.map +1 -0
  227. package/dist/tools/selfimprovement.js +367 -0
  228. package/dist/tools/selfimprovement.js.map +1 -0
  229. package/docker-compose.yml +35 -0
  230. package/docs/ARCHITECTURE.md +164 -0
  231. package/docs/CONVENTIONS-archetypes.md +158 -0
  232. package/docs/CONVENTIONS-failures.md +46 -0
  233. package/docs/CONVENTIONS-game-designs.md +75 -0
  234. package/docs/CONVENTIONS-hardware-reference.md +184 -0
  235. package/docs/CONVENTIONS-pitfalls.md +91 -0
  236. package/docs/CONVENTIONS-recipes.md +106 -0
  237. package/docs/CONVENTIONS-techniques.md +309 -0
  238. package/docs/CONVENTIONS-toolchain-reference.md +89 -0
  239. package/docs/ONTOLOGY.md +655 -0
  240. package/docs/art/art-production-reference.md +157 -0
  241. package/docs/art/asset-pipelines.md +586 -0
  242. package/docs/c64-failure-patterns.md +693 -0
  243. package/docs/demo-design/demo-composition.md +349 -0
  244. package/docs/demo-design/demo-design-philosophy.md +320 -0
  245. package/docs/demo-design/intro-cracktro-patterns.md +435 -0
  246. package/docs/figures/ctm-spd-embed-probe.png +0 -0
  247. package/docs/figures/dot-flag-mid-5000000.png +0 -0
  248. package/docs/figures/dot-flag-no-erase-12000000.png +0 -0
  249. package/docs/figures/edges.json +1 -0
  250. package/docs/figures/fig6-architecture.png +0 -0
  251. package/docs/figures/fig7-ontology.png +0 -0
  252. package/docs/figures/screen-dissolve-mid-frame25.png +0 -0
  253. package/docs/figures/screen-dissolve-wipe-frame25.png +0 -0
  254. package/docs/figures/spindle-two-parts-part1.png +0 -0
  255. package/docs/figures/spindle-two-parts-part2.png +0 -0
  256. package/docs/figures/starters/action-puzzle.png +0 -0
  257. package/docs/figures/starters/adventure.png +0 -0
  258. package/docs/figures/starters/beat-em-up.png +0 -0
  259. package/docs/figures/starters/demo.png +0 -0
  260. package/docs/figures/starters/platformer.png +0 -0
  261. package/docs/figures/starters/shmup-vertical.png +0 -0
  262. package/docs/figures/tape-kernal-master-ntsc.png +0 -0
  263. package/docs/figures/tape-kernal-master-pal.png +0 -0
  264. package/docs/figures/tape-turbo-loader-ntsc.png +0 -0
  265. package/docs/figures/tape-turbo-loader-pal.png +0 -0
  266. package/docs/figures/tape-turbo-master-ntsc.png +0 -0
  267. package/docs/figures/tape-turbo-master-pal.png +0 -0
  268. package/docs/figures/tech-tech-pad2-12000000.png +0 -0
  269. package/docs/figures/tech-tech-pad4-12000000.png +0 -0
  270. package/docs/figures/tech-tech-seven-12000000.png +0 -0
  271. package/docs/figures/twister-mid-5000000.png +0 -0
  272. package/docs/figures/twister-straight-12000000.png +0 -0
  273. package/docs/figures/vector-balls-nosort-ntsc.png +0 -0
  274. package/docs/figures/vector-balls-nosort.png +0 -0
  275. package/docs/formats/c64-file-formats.md +1212 -0
  276. package/docs/formats/iec-disk-reference.md +754 -0
  277. package/docs/game-design/c64-game-archetypes.md +273 -0
  278. package/docs/game-design/designs/falling-blocks.md +54 -0
  279. package/docs/game-design/designs/platformer-scaffold.md +48 -0
  280. package/docs/game-design/designs/simple-shmup.md +27 -0
  281. package/docs/game-design/enemy-behaviour-and-difficulty.md +349 -0
  282. package/docs/game-design/game-design-patterns.md +1189 -0
  283. package/docs/game-design/game-structure.md +354 -0
  284. package/docs/game-design/production-planning.md +349 -0
  285. package/docs/game-design/reference-game-sources.md +90 -0
  286. package/docs/hardware/6502-illegal-opcodes.md +1353 -0
  287. package/docs/hardware/6510-cpu-reference.md +2112 -0
  288. package/docs/hardware/c64-memory-map.md +3184 -0
  289. package/docs/hardware/c64-registers-reference.md +996 -0
  290. package/docs/hardware/cia-reference.md +1281 -0
  291. package/docs/hardware/kernal-routines-reference.md +1836 -0
  292. package/docs/hardware/pal-ntsc-reference.md +813 -0
  293. package/docs/hardware/sid-reference.md +1137 -0
  294. package/docs/hardware/vic-ii-reference.md +1714 -0
  295. package/docs/music/music-production-reference.md +403 -0
  296. package/docs/pitfalls/banking.md +1067 -0
  297. package/docs/pitfalls/cia.md +1147 -0
  298. package/docs/pitfalls/cpu.md +874 -0
  299. package/docs/pitfalls/input.md +282 -0
  300. package/docs/pitfalls/kernal-and-io.md +1615 -0
  301. package/docs/pitfalls/loader.md +1191 -0
  302. package/docs/pitfalls/logic.md +94 -0
  303. package/docs/pitfalls/maths.md +187 -0
  304. package/docs/pitfalls/raster-and-badline.md +721 -0
  305. package/docs/pitfalls/region-timing.md +660 -0
  306. package/docs/pitfalls/scroll.md +207 -0
  307. package/docs/pitfalls/sid.md +738 -0
  308. package/docs/pitfalls/sprite.md +979 -0
  309. package/docs/pitfalls/text-mode-render.md +1172 -0
  310. package/docs/recipes/cc65/cartridge-8k.md +398 -0
  311. package/docs/recipes/cc65/hello-world-conio.md +76 -0
  312. package/docs/recipes/cc65/memory-layout.md +312 -0
  313. package/docs/recipes/cc65/screenshots/README.md +14 -0
  314. package/docs/recipes/cc65/screenshots/cartridge-8k-ntsc.png +0 -0
  315. package/docs/recipes/cc65/screenshots/cartridge-8k.png +0 -0
  316. package/docs/recipes/cc65/screenshots/hello-world-conio.png +0 -0
  317. package/docs/recipes/cc65/screenshots/memory-layout-ntsc.png +0 -0
  318. package/docs/recipes/cc65/screenshots/memory-layout.png +0 -0
  319. package/docs/recipes/kickassembler/basic-float-calls.md +578 -0
  320. package/docs/recipes/kickassembler/basic-wedge.md +398 -0
  321. package/docs/recipes/kickassembler/big-font-scroller.md +620 -0
  322. package/docs/recipes/kickassembler/charset-copy-rom-to-ram.md +623 -0
  323. package/docs/recipes/kickassembler/cia-revision-detect.md +366 -0
  324. package/docs/recipes/kickassembler/colour-cycling.md +343 -0
  325. package/docs/recipes/kickassembler/colour-fade.md +303 -0
  326. package/docs/recipes/kickassembler/compare-16bit-signed.md +1007 -0
  327. package/docs/recipes/kickassembler/cracktro-template.md +526 -0
  328. package/docs/recipes/kickassembler/crt-banked.md +276 -0
  329. package/docs/recipes/kickassembler/dos-error-codes.md +466 -0
  330. package/docs/recipes/kickassembler/dot-flag.md +576 -0
  331. package/docs/recipes/kickassembler/drive-job-queue.md +667 -0
  332. package/docs/recipes/kickassembler/drive-via-probe.md +690 -0
  333. package/docs/recipes/kickassembler/dycp-scroller.md +568 -0
  334. package/docs/recipes/kickassembler/dypp-sprite-scroller.md +681 -0
  335. package/docs/recipes/kickassembler/dysp.md +670 -0
  336. package/docs/recipes/kickassembler/easyflash-save.md +800 -0
  337. package/docs/recipes/kickassembler/eight-way-scroll.md +1484 -0
  338. package/docs/recipes/kickassembler/file-io-roundtrip.md +546 -0
  339. package/docs/recipes/kickassembler/fire-effect.md +420 -0
  340. package/docs/recipes/kickassembler/fld.md +414 -0
  341. package/docs/recipes/kickassembler/fli-image.md +336 -0
  342. package/docs/recipes/kickassembler/four-player-read.md +672 -0
  343. package/docs/recipes/kickassembler/headless-verify.md +228 -0
  344. package/docs/recipes/kickassembler/hello-world.md +98 -0
  345. package/docs/recipes/kickassembler/high-score-insert.md +660 -0
  346. package/docs/recipes/kickassembler/hires-plot-line.md +723 -0
  347. package/docs/recipes/kickassembler/irq-chain.md +316 -0
  348. package/docs/recipes/kickassembler/irq-owns-port.md +668 -0
  349. package/docs/recipes/kickassembler/isometric-room.md +1145 -0
  350. package/docs/recipes/kickassembler/light-pen-read.md +926 -0
  351. package/docs/recipes/kickassembler/logic-rate-decoupling.md +670 -0
  352. package/docs/recipes/kickassembler/mci-interlace.md +543 -0
  353. package/docs/recipes/kickassembler/memory-layout.md +277 -0
  354. package/docs/recipes/kickassembler/mouse-1351-read.md +801 -0
  355. package/docs/recipes/kickassembler/nmi-timer-tick.md +562 -0
  356. package/docs/recipes/kickassembler/own-keyscan.md +881 -0
  357. package/docs/recipes/kickassembler/paddle-read.md +751 -0
  358. package/docs/recipes/kickassembler/palette-cells.md +219 -0
  359. package/docs/recipes/kickassembler/pseudo-3d-road.md +930 -0
  360. package/docs/recipes/kickassembler/pwm-digi.md +463 -0
  361. package/docs/recipes/kickassembler/raster-bars.md +264 -0
  362. package/docs/recipes/kickassembler/reu-dma.md +1019 -0
  363. package/docs/recipes/kickassembler/runtime-relocation.md +743 -0
  364. package/docs/recipes/kickassembler/screen-dissolve.md +545 -0
  365. package/docs/recipes/kickassembler/screen-wipe.md +408 -0
  366. package/docs/recipes/kickassembler/screenshots/README.md +72 -0
  367. package/docs/recipes/kickassembler/screenshots/basic-float-calls-ntsc.png +0 -0
  368. package/docs/recipes/kickassembler/screenshots/basic-float-calls.png +0 -0
  369. package/docs/recipes/kickassembler/screenshots/basic-wedge-ntsc.png +0 -0
  370. package/docs/recipes/kickassembler/screenshots/basic-wedge.png +0 -0
  371. package/docs/recipes/kickassembler/screenshots/big-font-scroller-ntsc.png +0 -0
  372. package/docs/recipes/kickassembler/screenshots/big-font-scroller.png +0 -0
  373. package/docs/recipes/kickassembler/screenshots/charset-copy-rom-to-ram-ntsc.png +0 -0
  374. package/docs/recipes/kickassembler/screenshots/charset-copy-rom-to-ram.png +0 -0
  375. package/docs/recipes/kickassembler/screenshots/cia-revision-detect-ntsc.png +0 -0
  376. package/docs/recipes/kickassembler/screenshots/cia-revision-detect.png +0 -0
  377. package/docs/recipes/kickassembler/screenshots/colour-cycling-ntsc.png +0 -0
  378. package/docs/recipes/kickassembler/screenshots/colour-cycling.png +0 -0
  379. package/docs/recipes/kickassembler/screenshots/colour-fade-ntsc.png +0 -0
  380. package/docs/recipes/kickassembler/screenshots/colour-fade.png +0 -0
  381. package/docs/recipes/kickassembler/screenshots/compare-16bit-signed-ntsc.png +0 -0
  382. package/docs/recipes/kickassembler/screenshots/compare-16bit-signed.png +0 -0
  383. package/docs/recipes/kickassembler/screenshots/cracktro-template.png +0 -0
  384. package/docs/recipes/kickassembler/screenshots/crt-banked-ntsc.png +0 -0
  385. package/docs/recipes/kickassembler/screenshots/crt-banked.png +0 -0
  386. package/docs/recipes/kickassembler/screenshots/dos-error-codes-ntsc.png +0 -0
  387. package/docs/recipes/kickassembler/screenshots/dos-error-codes.png +0 -0
  388. package/docs/recipes/kickassembler/screenshots/dot-flag-ntsc.png +0 -0
  389. package/docs/recipes/kickassembler/screenshots/dot-flag.png +0 -0
  390. package/docs/recipes/kickassembler/screenshots/drive-job-queue-ntsc.png +0 -0
  391. package/docs/recipes/kickassembler/screenshots/drive-job-queue.png +0 -0
  392. package/docs/recipes/kickassembler/screenshots/drive-via-probe-ntsc.png +0 -0
  393. package/docs/recipes/kickassembler/screenshots/drive-via-probe.png +0 -0
  394. package/docs/recipes/kickassembler/screenshots/dycp-scroller-ntsc.png +0 -0
  395. package/docs/recipes/kickassembler/screenshots/dycp-scroller.png +0 -0
  396. package/docs/recipes/kickassembler/screenshots/dypp-sprite-scroller-ntsc.png +0 -0
  397. package/docs/recipes/kickassembler/screenshots/dypp-sprite-scroller.png +0 -0
  398. package/docs/recipes/kickassembler/screenshots/dysp-ntsc.png +0 -0
  399. package/docs/recipes/kickassembler/screenshots/dysp.png +0 -0
  400. package/docs/recipes/kickassembler/screenshots/easyflash-save-ntsc-run2.png +0 -0
  401. package/docs/recipes/kickassembler/screenshots/easyflash-save-ntsc.png +0 -0
  402. package/docs/recipes/kickassembler/screenshots/easyflash-save-run2.png +0 -0
  403. package/docs/recipes/kickassembler/screenshots/easyflash-save.png +0 -0
  404. package/docs/recipes/kickassembler/screenshots/eight-way-scroll-ntsc.png +0 -0
  405. package/docs/recipes/kickassembler/screenshots/eight-way-scroll.png +0 -0
  406. package/docs/recipes/kickassembler/screenshots/file-io-roundtrip-ntsc.png +0 -0
  407. package/docs/recipes/kickassembler/screenshots/file-io-roundtrip.png +0 -0
  408. package/docs/recipes/kickassembler/screenshots/fire-effect-ntsc.png +0 -0
  409. package/docs/recipes/kickassembler/screenshots/fire-effect.png +0 -0
  410. package/docs/recipes/kickassembler/screenshots/fld-ntsc.png +0 -0
  411. package/docs/recipes/kickassembler/screenshots/fld.png +0 -0
  412. package/docs/recipes/kickassembler/screenshots/fli-image.png +0 -0
  413. package/docs/recipes/kickassembler/screenshots/four-player-read-ntsc.png +0 -0
  414. package/docs/recipes/kickassembler/screenshots/four-player-read.png +0 -0
  415. package/docs/recipes/kickassembler/screenshots/headless-verify-ntsc.png +0 -0
  416. package/docs/recipes/kickassembler/screenshots/headless-verify.png +0 -0
  417. package/docs/recipes/kickassembler/screenshots/hello-world.png +0 -0
  418. package/docs/recipes/kickassembler/screenshots/high-score-insert-ntsc.png +0 -0
  419. package/docs/recipes/kickassembler/screenshots/high-score-insert.png +0 -0
  420. package/docs/recipes/kickassembler/screenshots/hires-plot-line-ntsc.png +0 -0
  421. package/docs/recipes/kickassembler/screenshots/hires-plot-line.png +0 -0
  422. package/docs/recipes/kickassembler/screenshots/irq-chain-ntsc.png +0 -0
  423. package/docs/recipes/kickassembler/screenshots/irq-chain.png +0 -0
  424. package/docs/recipes/kickassembler/screenshots/irq-owns-port-ntsc.png +0 -0
  425. package/docs/recipes/kickassembler/screenshots/irq-owns-port.png +0 -0
  426. package/docs/recipes/kickassembler/screenshots/isometric-room-ntsc.png +0 -0
  427. package/docs/recipes/kickassembler/screenshots/isometric-room.png +0 -0
  428. package/docs/recipes/kickassembler/screenshots/light-pen-read-ntsc.png +0 -0
  429. package/docs/recipes/kickassembler/screenshots/light-pen-read.png +0 -0
  430. package/docs/recipes/kickassembler/screenshots/logic-rate-decoupling-ntsc.png +0 -0
  431. package/docs/recipes/kickassembler/screenshots/logic-rate-decoupling.png +0 -0
  432. package/docs/recipes/kickassembler/screenshots/mci-interlace-average.png +0 -0
  433. package/docs/recipes/kickassembler/screenshots/mci-interlace-frame-b.png +0 -0
  434. package/docs/recipes/kickassembler/screenshots/mci-interlace-ntsc.png +0 -0
  435. package/docs/recipes/kickassembler/screenshots/mci-interlace.png +0 -0
  436. package/docs/recipes/kickassembler/screenshots/memory-layout-ntsc.png +0 -0
  437. package/docs/recipes/kickassembler/screenshots/memory-layout.png +0 -0
  438. package/docs/recipes/kickassembler/screenshots/mouse-1351-read-ntsc.png +0 -0
  439. package/docs/recipes/kickassembler/screenshots/mouse-1351-read.png +0 -0
  440. package/docs/recipes/kickassembler/screenshots/nmi-timer-tick-ntsc.png +0 -0
  441. package/docs/recipes/kickassembler/screenshots/nmi-timer-tick.png +0 -0
  442. package/docs/recipes/kickassembler/screenshots/own-keyscan-ntsc.png +0 -0
  443. package/docs/recipes/kickassembler/screenshots/own-keyscan.png +0 -0
  444. package/docs/recipes/kickassembler/screenshots/paddle-read-ntsc.png +0 -0
  445. package/docs/recipes/kickassembler/screenshots/paddle-read.png +0 -0
  446. package/docs/recipes/kickassembler/screenshots/palette-cells-ntsc.png +0 -0
  447. package/docs/recipes/kickassembler/screenshots/palette-cells.png +0 -0
  448. package/docs/recipes/kickassembler/screenshots/pseudo-3d-road-ntsc.png +0 -0
  449. package/docs/recipes/kickassembler/screenshots/pseudo-3d-road.png +0 -0
  450. package/docs/recipes/kickassembler/screenshots/pwm-digi-ntsc.png +0 -0
  451. package/docs/recipes/kickassembler/screenshots/pwm-digi.png +0 -0
  452. package/docs/recipes/kickassembler/screenshots/raster-bars.png +0 -0
  453. package/docs/recipes/kickassembler/screenshots/reu-dma-ntsc.png +0 -0
  454. package/docs/recipes/kickassembler/screenshots/reu-dma.png +0 -0
  455. package/docs/recipes/kickassembler/screenshots/runtime-relocation-ntsc.png +0 -0
  456. package/docs/recipes/kickassembler/screenshots/runtime-relocation.png +0 -0
  457. package/docs/recipes/kickassembler/screenshots/screen-dissolve-ntsc.png +0 -0
  458. package/docs/recipes/kickassembler/screenshots/screen-dissolve.png +0 -0
  459. package/docs/recipes/kickassembler/screenshots/screen-wipe-ntsc.png +0 -0
  460. package/docs/recipes/kickassembler/screenshots/screen-wipe.png +0 -0
  461. package/docs/recipes/kickassembler/screenshots/scroll-panel-split-ntsc.png +0 -0
  462. package/docs/recipes/kickassembler/screenshots/scroll-panel-split.png +0 -0
  463. package/docs/recipes/kickassembler/screenshots/sfx-in-player-ntsc.png +0 -0
  464. package/docs/recipes/kickassembler/screenshots/sfx-in-player.png +0 -0
  465. package/docs/recipes/kickassembler/screenshots/shadebobs-ntsc.png +0 -0
  466. package/docs/recipes/kickassembler/screenshots/shadebobs.png +0 -0
  467. package/docs/recipes/kickassembler/screenshots/sideborder-open.png +0 -0
  468. package/docs/recipes/kickassembler/screenshots/sine-scroller.png +0 -0
  469. package/docs/recipes/kickassembler/screenshots/sine-table-runtime-ntsc.png +0 -0
  470. package/docs/recipes/kickassembler/screenshots/sine-table-runtime.png +0 -0
  471. package/docs/recipes/kickassembler/screenshots/software-sprite-preshifted-ntsc.png +0 -0
  472. package/docs/recipes/kickassembler/screenshots/software-sprite-preshifted.png +0 -0
  473. package/docs/recipes/kickassembler/screenshots/speedcode-generator-ntsc.png +0 -0
  474. package/docs/recipes/kickassembler/screenshots/speedcode-generator.png +0 -0
  475. package/docs/recipes/kickassembler/screenshots/sprite-border-scroller-ntsc.png +0 -0
  476. package/docs/recipes/kickassembler/screenshots/sprite-border-scroller.png +0 -0
  477. package/docs/recipes/kickassembler/screenshots/sprite-cache-flip-ntsc.png +0 -0
  478. package/docs/recipes/kickassembler/screenshots/sprite-cache-flip.png +0 -0
  479. package/docs/recipes/kickassembler/screenshots/sprite-multiplex-24.png +0 -0
  480. package/docs/recipes/kickassembler/screenshots/sprite-multiplex-game-ntsc.png +0 -0
  481. package/docs/recipes/kickassembler/screenshots/sprite-multiplex-game.png +0 -0
  482. package/docs/recipes/kickassembler/screenshots/sprite-priority-classes-ntsc.png +0 -0
  483. package/docs/recipes/kickassembler/screenshots/sprite-priority-classes.png +0 -0
  484. package/docs/recipes/kickassembler/screenshots/sprite-sine-chain-ntsc.png +0 -0
  485. package/docs/recipes/kickassembler/screenshots/sprite-sine-chain.png +0 -0
  486. package/docs/recipes/kickassembler/screenshots/sprite-stretcher-ntsc.png +0 -0
  487. package/docs/recipes/kickassembler/screenshots/sprite-stretcher.png +0 -0
  488. package/docs/recipes/kickassembler/screenshots/sprites-only-screen-ntsc.png +0 -0
  489. package/docs/recipes/kickassembler/screenshots/sprites-only-screen.png +0 -0
  490. package/docs/recipes/kickassembler/screenshots/sqrt-atan2-ntsc.png +0 -0
  491. package/docs/recipes/kickassembler/screenshots/sqrt-atan2.png +0 -0
  492. package/docs/recipes/kickassembler/screenshots/stable-raster-irq-control.png +0 -0
  493. package/docs/recipes/kickassembler/screenshots/stable-raster-irq.png +0 -0
  494. package/docs/recipes/kickassembler/screenshots/tech-tech-ntsc.png +0 -0
  495. package/docs/recipes/kickassembler/screenshots/tech-tech.png +0 -0
  496. package/docs/recipes/kickassembler/screenshots/tod-alarm-ntsc.png +0 -0
  497. package/docs/recipes/kickassembler/screenshots/tod-alarm.png +0 -0
  498. package/docs/recipes/kickassembler/screenshots/topbottom-border-open.png +0 -0
  499. package/docs/recipes/kickassembler/screenshots/twister-ntsc.png +0 -0
  500. package/docs/recipes/kickassembler/screenshots/twister.png +0 -0
  501. package/docs/recipes/kickassembler/screenshots/unit-test-driver-ntsc.png +0 -0
  502. package/docs/recipes/kickassembler/screenshots/unit-test-driver.png +0 -0
  503. package/docs/recipes/kickassembler/screenshots/vector-balls-ntsc.png +0 -0
  504. package/docs/recipes/kickassembler/screenshots/vector-balls.png +0 -0
  505. package/docs/recipes/kickassembler/screenshots/wireframe-ships-ntsc.png +0 -0
  506. package/docs/recipes/kickassembler/screenshots/wireframe-ships.png +0 -0
  507. package/docs/recipes/kickassembler/scroll-panel-split.md +381 -0
  508. package/docs/recipes/kickassembler/sfx-in-player.md +907 -0
  509. package/docs/recipes/kickassembler/shadebobs.md +522 -0
  510. package/docs/recipes/kickassembler/sideborder-open.md +338 -0
  511. package/docs/recipes/kickassembler/sine-scroller.md +321 -0
  512. package/docs/recipes/kickassembler/sine-table-runtime.md +815 -0
  513. package/docs/recipes/kickassembler/software-sprite-preshifted.md +725 -0
  514. package/docs/recipes/kickassembler/speedcode-generator.md +606 -0
  515. package/docs/recipes/kickassembler/sprite-border-scroller.md +696 -0
  516. package/docs/recipes/kickassembler/sprite-cache-flip.md +1071 -0
  517. package/docs/recipes/kickassembler/sprite-multiplex-24.md +326 -0
  518. package/docs/recipes/kickassembler/sprite-multiplex-game.md +986 -0
  519. package/docs/recipes/kickassembler/sprite-priority-classes.md +463 -0
  520. package/docs/recipes/kickassembler/sprite-sine-chain.md +338 -0
  521. package/docs/recipes/kickassembler/sprite-stretcher.md +501 -0
  522. package/docs/recipes/kickassembler/sprites-only-screen.md +550 -0
  523. package/docs/recipes/kickassembler/sqrt-atan2.md +1014 -0
  524. package/docs/recipes/kickassembler/stable-raster-irq.md +360 -0
  525. package/docs/recipes/kickassembler/tape-turbo-loader.md +875 -0
  526. package/docs/recipes/kickassembler/tech-tech.md +679 -0
  527. package/docs/recipes/kickassembler/tod-alarm.md +754 -0
  528. package/docs/recipes/kickassembler/topbottom-border-open.md +407 -0
  529. package/docs/recipes/kickassembler/twister.md +518 -0
  530. package/docs/recipes/kickassembler/unit-test-driver.md +314 -0
  531. package/docs/recipes/kickassembler/vector-balls.md +545 -0
  532. package/docs/recipes/kickassembler/wireframe-ships.md +2324 -0
  533. package/docs/recipes/oscar64/actor-activation-window.md +807 -0
  534. package/docs/recipes/oscar64/adventure-engine.md +1094 -0
  535. package/docs/recipes/oscar64/attract-replay.md +465 -0
  536. package/docs/recipes/oscar64/beat-em-up-lanes.md +563 -0
  537. package/docs/recipes/oscar64/bfs-distance-map.md +723 -0
  538. package/docs/recipes/oscar64/bitmap-koala-viewer.md +355 -0
  539. package/docs/recipes/oscar64/car-contact.md +950 -0
  540. package/docs/recipes/oscar64/cave-scan.md +609 -0
  541. package/docs/recipes/oscar64/char-bullets.md +477 -0
  542. package/docs/recipes/oscar64/charset-animation.md +383 -0
  543. package/docs/recipes/oscar64/charset-parallax.md +401 -0
  544. package/docs/recipes/oscar64/destructible-terrain.md +1099 -0
  545. package/docs/recipes/oscar64/difficulty-tables.md +654 -0
  546. package/docs/recipes/oscar64/dig-and-guards.md +975 -0
  547. package/docs/recipes/oscar64/directory-reader.md +535 -0
  548. package/docs/recipes/oscar64/divide-check.md +581 -0
  549. package/docs/recipes/oscar64/double-buffer-nomirror.md +205 -0
  550. package/docs/recipes/oscar64/double-buffer.md +255 -0
  551. package/docs/recipes/oscar64/falling-blocks.md +874 -0
  552. package/docs/recipes/oscar64/fixed-point-jump-velocity.md +289 -0
  553. package/docs/recipes/oscar64/fixed-point-jump.md +210 -0
  554. package/docs/recipes/oscar64/flip-screen-rooms.md +793 -0
  555. package/docs/recipes/oscar64/frame-sync-loop-overrun.md +214 -0
  556. package/docs/recipes/oscar64/frame-sync-loop.md +254 -0
  557. package/docs/recipes/oscar64/game-tree-search.md +743 -0
  558. package/docs/recipes/oscar64/ghost-targeting.md +867 -0
  559. package/docs/recipes/oscar64/headless-verify.md +345 -0
  560. package/docs/recipes/oscar64/hello-world.md +83 -0
  561. package/docs/recipes/oscar64/high-score-persist.md +772 -0
  562. package/docs/recipes/oscar64/iffl-kernal-skip.md +421 -0
  563. package/docs/recipes/oscar64/joystick-input.md +358 -0
  564. package/docs/recipes/oscar64/lane-pursuit.md +1028 -0
  565. package/docs/recipes/oscar64/level-rle-decoder.md +620 -0
  566. package/docs/recipes/oscar64/lfsr-random-seed2.md +338 -0
  567. package/docs/recipes/oscar64/lfsr-random.md +403 -0
  568. package/docs/recipes/oscar64/load-asset-runtime.md +492 -0
  569. package/docs/recipes/oscar64/memory-layout.md +232 -0
  570. package/docs/recipes/oscar64/mixed-fighters.md +781 -0
  571. package/docs/recipes/oscar64/multi-sprite-object.md +533 -0
  572. package/docs/recipes/oscar64/nav-area-pathfinding.md +881 -0
  573. package/docs/recipes/oscar64/object-pool.md +572 -0
  574. package/docs/recipes/oscar64/pal-ntsc-detect.md +257 -0
  575. package/docs/recipes/oscar64/password-state.md +532 -0
  576. package/docs/recipes/oscar64/per-frame-hitbox.md +651 -0
  577. package/docs/recipes/oscar64/petscii-screen-codes.md +428 -0
  578. package/docs/recipes/oscar64/platformer-scaffold.md +1467 -0
  579. package/docs/recipes/oscar64/print-number.md +367 -0
  580. package/docs/recipes/oscar64/raster-bars.md +239 -0
  581. package/docs/recipes/oscar64/raster-profile-bars.md +404 -0
  582. package/docs/recipes/oscar64/rel-side-sectors.md +391 -0
  583. package/docs/recipes/oscar64/relative-file-records.md +378 -0
  584. package/docs/recipes/oscar64/save-load-seq-file.md +365 -0
  585. package/docs/recipes/oscar64/screenshots/README.md +99 -0
  586. package/docs/recipes/oscar64/screenshots/actor-activation-window-ntsc.png +0 -0
  587. package/docs/recipes/oscar64/screenshots/actor-activation-window.png +0 -0
  588. package/docs/recipes/oscar64/screenshots/adventure-engine-ntsc.png +0 -0
  589. package/docs/recipes/oscar64/screenshots/adventure-engine.png +0 -0
  590. package/docs/recipes/oscar64/screenshots/attract-replay-ntsc.png +0 -0
  591. package/docs/recipes/oscar64/screenshots/attract-replay.png +0 -0
  592. package/docs/recipes/oscar64/screenshots/beat-em-up-lanes-ntsc.png +0 -0
  593. package/docs/recipes/oscar64/screenshots/beat-em-up-lanes.png +0 -0
  594. package/docs/recipes/oscar64/screenshots/bfs-distance-map-ntsc.png +0 -0
  595. package/docs/recipes/oscar64/screenshots/bfs-distance-map.png +0 -0
  596. package/docs/recipes/oscar64/screenshots/bitmap-koala-viewer.png +0 -0
  597. package/docs/recipes/oscar64/screenshots/car-contact-ntsc.png +0 -0
  598. package/docs/recipes/oscar64/screenshots/car-contact.png +0 -0
  599. package/docs/recipes/oscar64/screenshots/cave-scan-ntsc.png +0 -0
  600. package/docs/recipes/oscar64/screenshots/cave-scan.png +0 -0
  601. package/docs/recipes/oscar64/screenshots/char-bullets-ntsc.png +0 -0
  602. package/docs/recipes/oscar64/screenshots/char-bullets.png +0 -0
  603. package/docs/recipes/oscar64/screenshots/charset-animation-ntsc.png +0 -0
  604. package/docs/recipes/oscar64/screenshots/charset-animation.png +0 -0
  605. package/docs/recipes/oscar64/screenshots/charset-parallax-ntsc.png +0 -0
  606. package/docs/recipes/oscar64/screenshots/charset-parallax.png +0 -0
  607. package/docs/recipes/oscar64/screenshots/destructible-terrain-ntsc.png +0 -0
  608. package/docs/recipes/oscar64/screenshots/destructible-terrain.png +0 -0
  609. package/docs/recipes/oscar64/screenshots/difficulty-tables-ntsc.png +0 -0
  610. package/docs/recipes/oscar64/screenshots/difficulty-tables.png +0 -0
  611. package/docs/recipes/oscar64/screenshots/dig-and-guards-ntsc.png +0 -0
  612. package/docs/recipes/oscar64/screenshots/dig-and-guards.png +0 -0
  613. package/docs/recipes/oscar64/screenshots/directory-reader-ntsc.png +0 -0
  614. package/docs/recipes/oscar64/screenshots/directory-reader.png +0 -0
  615. package/docs/recipes/oscar64/screenshots/divide-check-ntsc.png +0 -0
  616. package/docs/recipes/oscar64/screenshots/divide-check.png +0 -0
  617. package/docs/recipes/oscar64/screenshots/double-buffer-nomirror.png +0 -0
  618. package/docs/recipes/oscar64/screenshots/double-buffer-ntsc.png +0 -0
  619. package/docs/recipes/oscar64/screenshots/double-buffer.png +0 -0
  620. package/docs/recipes/oscar64/screenshots/falling-blocks-ntsc.png +0 -0
  621. package/docs/recipes/oscar64/screenshots/falling-blocks.png +0 -0
  622. package/docs/recipes/oscar64/screenshots/fixed-point-jump-ntsc.png +0 -0
  623. package/docs/recipes/oscar64/screenshots/fixed-point-jump-velocity-ntsc.png +0 -0
  624. package/docs/recipes/oscar64/screenshots/fixed-point-jump-velocity.png +0 -0
  625. package/docs/recipes/oscar64/screenshots/fixed-point-jump.png +0 -0
  626. package/docs/recipes/oscar64/screenshots/flip-screen-rooms-ntsc.png +0 -0
  627. package/docs/recipes/oscar64/screenshots/flip-screen-rooms.png +0 -0
  628. package/docs/recipes/oscar64/screenshots/frame-sync-loop-ntsc.png +0 -0
  629. package/docs/recipes/oscar64/screenshots/frame-sync-loop-overrun.png +0 -0
  630. package/docs/recipes/oscar64/screenshots/frame-sync-loop.png +0 -0
  631. package/docs/recipes/oscar64/screenshots/game-tree-search-ntsc.png +0 -0
  632. package/docs/recipes/oscar64/screenshots/game-tree-search.png +0 -0
  633. package/docs/recipes/oscar64/screenshots/ghost-targeting-ntsc.png +0 -0
  634. package/docs/recipes/oscar64/screenshots/ghost-targeting.png +0 -0
  635. package/docs/recipes/oscar64/screenshots/headless-verify-ntsc.png +0 -0
  636. package/docs/recipes/oscar64/screenshots/headless-verify.png +0 -0
  637. package/docs/recipes/oscar64/screenshots/hello-world.png +0 -0
  638. package/docs/recipes/oscar64/screenshots/high-score-persist-ntsc.png +0 -0
  639. package/docs/recipes/oscar64/screenshots/high-score-persist.png +0 -0
  640. package/docs/recipes/oscar64/screenshots/iffl-kernal-skip-ntsc.png +0 -0
  641. package/docs/recipes/oscar64/screenshots/iffl-kernal-skip.png +0 -0
  642. package/docs/recipes/oscar64/screenshots/joystick-input-ntsc.png +0 -0
  643. package/docs/recipes/oscar64/screenshots/joystick-input.png +0 -0
  644. package/docs/recipes/oscar64/screenshots/lane-pursuit-ntsc.png +0 -0
  645. package/docs/recipes/oscar64/screenshots/lane-pursuit.png +0 -0
  646. package/docs/recipes/oscar64/screenshots/level-rle-decoder-ntsc.png +0 -0
  647. package/docs/recipes/oscar64/screenshots/level-rle-decoder.png +0 -0
  648. package/docs/recipes/oscar64/screenshots/lfsr-random-ntsc.png +0 -0
  649. package/docs/recipes/oscar64/screenshots/lfsr-random-seed2.png +0 -0
  650. package/docs/recipes/oscar64/screenshots/lfsr-random.png +0 -0
  651. package/docs/recipes/oscar64/screenshots/load-asset-runtime-ntsc.png +0 -0
  652. package/docs/recipes/oscar64/screenshots/load-asset-runtime.png +0 -0
  653. package/docs/recipes/oscar64/screenshots/memory-layout-ntsc.png +0 -0
  654. package/docs/recipes/oscar64/screenshots/memory-layout.png +0 -0
  655. package/docs/recipes/oscar64/screenshots/mixed-fighters-ntsc.png +0 -0
  656. package/docs/recipes/oscar64/screenshots/mixed-fighters.png +0 -0
  657. package/docs/recipes/oscar64/screenshots/multi-sprite-object-ntsc.png +0 -0
  658. package/docs/recipes/oscar64/screenshots/multi-sprite-object.png +0 -0
  659. package/docs/recipes/oscar64/screenshots/nav-area-pathfinding-ntsc.png +0 -0
  660. package/docs/recipes/oscar64/screenshots/nav-area-pathfinding.png +0 -0
  661. package/docs/recipes/oscar64/screenshots/object-pool-ntsc.png +0 -0
  662. package/docs/recipes/oscar64/screenshots/object-pool.png +0 -0
  663. package/docs/recipes/oscar64/screenshots/pal-ntsc-detect-ntsc.png +0 -0
  664. package/docs/recipes/oscar64/screenshots/pal-ntsc-detect-oldntsc.png +0 -0
  665. package/docs/recipes/oscar64/screenshots/pal-ntsc-detect-pal.png +0 -0
  666. package/docs/recipes/oscar64/screenshots/password-state-ntsc.png +0 -0
  667. package/docs/recipes/oscar64/screenshots/password-state.png +0 -0
  668. package/docs/recipes/oscar64/screenshots/per-frame-hitbox-ntsc.png +0 -0
  669. package/docs/recipes/oscar64/screenshots/per-frame-hitbox.png +0 -0
  670. package/docs/recipes/oscar64/screenshots/petscii-screen-codes-ntsc.png +0 -0
  671. package/docs/recipes/oscar64/screenshots/petscii-screen-codes.png +0 -0
  672. package/docs/recipes/oscar64/screenshots/platformer-scaffold-d64-ntsc.png +0 -0
  673. package/docs/recipes/oscar64/screenshots/platformer-scaffold-d64.png +0 -0
  674. package/docs/recipes/oscar64/screenshots/platformer-scaffold-ntsc.png +0 -0
  675. package/docs/recipes/oscar64/screenshots/platformer-scaffold.png +0 -0
  676. package/docs/recipes/oscar64/screenshots/print-number-ntsc.png +0 -0
  677. package/docs/recipes/oscar64/screenshots/print-number.png +0 -0
  678. package/docs/recipes/oscar64/screenshots/raster-bars.png +0 -0
  679. package/docs/recipes/oscar64/screenshots/raster-profile-bars-ntsc.png +0 -0
  680. package/docs/recipes/oscar64/screenshots/raster-profile-bars.png +0 -0
  681. package/docs/recipes/oscar64/screenshots/rel-side-sectors-ntsc.png +0 -0
  682. package/docs/recipes/oscar64/screenshots/rel-side-sectors.png +0 -0
  683. package/docs/recipes/oscar64/screenshots/relative-file-records-ntsc.png +0 -0
  684. package/docs/recipes/oscar64/screenshots/relative-file-records.png +0 -0
  685. package/docs/recipes/oscar64/screenshots/save-load-seq-file-ntsc.png +0 -0
  686. package/docs/recipes/oscar64/screenshots/save-load-seq-file.png +0 -0
  687. package/docs/recipes/oscar64/screenshots/seeded-level-fill-ntsc.png +0 -0
  688. package/docs/recipes/oscar64/screenshots/seeded-level-fill.png +0 -0
  689. package/docs/recipes/oscar64/screenshots/sfx-engine-ntsc.png +0 -0
  690. package/docs/recipes/oscar64/screenshots/sfx-engine.png +0 -0
  691. package/docs/recipes/oscar64/screenshots/sid-music-player.png +0 -0
  692. package/docs/recipes/oscar64/screenshots/simple-shmup.png +0 -0
  693. package/docs/recipes/oscar64/screenshots/slope-collision-ntsc.png +0 -0
  694. package/docs/recipes/oscar64/screenshots/slope-collision.png +0 -0
  695. package/docs/recipes/oscar64/screenshots/soft-scroll-h.png +0 -0
  696. package/docs/recipes/oscar64/screenshots/sprite-animation-table-ntsc.png +0 -0
  697. package/docs/recipes/oscar64/screenshots/sprite-animation-table.png +0 -0
  698. package/docs/recipes/oscar64/screenshots/sprite-multiplex-8.png +0 -0
  699. package/docs/recipes/oscar64/screenshots/stable-raster-irq.png +0 -0
  700. package/docs/recipes/oscar64/screenshots/text-input-ntsc.png +0 -0
  701. package/docs/recipes/oscar64/screenshots/text-input.png +0 -0
  702. package/docs/recipes/oscar64/screenshots/text-overlay-playfield-gameover.png +0 -0
  703. package/docs/recipes/oscar64/screenshots/text-overlay-playfield-notrail-control.png +0 -0
  704. package/docs/recipes/oscar64/screenshots/text-overlay-playfield-ntsc.png +0 -0
  705. package/docs/recipes/oscar64/screenshots/text-overlay-playfield-ungated-control.png +0 -0
  706. package/docs/recipes/oscar64/screenshots/text-overlay-playfield.png +0 -0
  707. package/docs/recipes/oscar64/screenshots/text-window-menu-ntsc.png +0 -0
  708. package/docs/recipes/oscar64/screenshots/text-window-menu.png +0 -0
  709. package/docs/recipes/oscar64/screenshots/tile-grid-collision-ntsc.png +0 -0
  710. package/docs/recipes/oscar64/screenshots/tile-grid-collision.png +0 -0
  711. package/docs/recipes/oscar64/screenshots/tile-map-render-ntsc.png +0 -0
  712. package/docs/recipes/oscar64/screenshots/tile-map-render.png +0 -0
  713. package/docs/recipes/oscar64/screenshots/two-player-ntsc.png +0 -0
  714. package/docs/recipes/oscar64/screenshots/two-player.png +0 -0
  715. package/docs/recipes/oscar64/screenshots/two-word-parser-ntsc.png +0 -0
  716. package/docs/recipes/oscar64/screenshots/two-word-parser.png +0 -0
  717. package/docs/recipes/oscar64/screenshots/vehicle-control-ntsc.png +0 -0
  718. package/docs/recipes/oscar64/screenshots/vehicle-control.png +0 -0
  719. package/docs/recipes/oscar64/screenshots/wave-director-ntsc.png +0 -0
  720. package/docs/recipes/oscar64/screenshots/wave-director.png +0 -0
  721. package/docs/recipes/oscar64/seeded-level-fill.md +364 -0
  722. package/docs/recipes/oscar64/sfx-engine.md +514 -0
  723. package/docs/recipes/oscar64/sid-music-player.md +441 -0
  724. package/docs/recipes/oscar64/simple-shmup.md +1000 -0
  725. package/docs/recipes/oscar64/slope-collision.md +610 -0
  726. package/docs/recipes/oscar64/soft-scroll-h.md +261 -0
  727. package/docs/recipes/oscar64/sprite-animation-table.md +671 -0
  728. package/docs/recipes/oscar64/sprite-multiplex-8.md +314 -0
  729. package/docs/recipes/oscar64/stable-raster-irq.md +236 -0
  730. package/docs/recipes/oscar64/text-input.md +274 -0
  731. package/docs/recipes/oscar64/text-overlay-playfield.md +617 -0
  732. package/docs/recipes/oscar64/text-window-menu.md +551 -0
  733. package/docs/recipes/oscar64/tile-grid-collision.md +563 -0
  734. package/docs/recipes/oscar64/tile-map-render.md +395 -0
  735. package/docs/recipes/oscar64/two-player.md +528 -0
  736. package/docs/recipes/oscar64/two-word-parser.md +510 -0
  737. package/docs/recipes/oscar64/vehicle-control.md +771 -0
  738. package/docs/recipes/oscar64/wave-director.md +787 -0
  739. package/docs/recipes/runs.json +1606 -0
  740. package/docs/runtime/sim6502-reference.md +466 -0
  741. package/docs/runtime/vice-mcp-reference.md +351 -0
  742. package/docs/runtime/vice-reference.md +1393 -0
  743. package/docs/techniques/bitmap-modes.md +685 -0
  744. package/docs/techniques/cpu-cycle-tricks.md +1570 -0
  745. package/docs/techniques/effects-vector-3d.md +1431 -0
  746. package/docs/techniques/file-io.md +1427 -0
  747. package/docs/techniques/input.md +1072 -0
  748. package/docs/techniques/loaders-packers.md +768 -0
  749. package/docs/techniques/logic.md +3091 -0
  750. package/docs/techniques/maths.md +1524 -0
  751. package/docs/techniques/memory-banking.md +1686 -0
  752. package/docs/techniques/music-sid.md +1144 -0
  753. package/docs/techniques/raster.md +1543 -0
  754. package/docs/techniques/scroll.md +1292 -0
  755. package/docs/techniques/sprite.md +2566 -0
  756. package/docs/techniques/text-mode-render.md +717 -0
  757. package/docs/techniques/text.md +1095 -0
  758. package/docs/techniques/transitions.md +405 -0
  759. package/docs/toolchains/cartconv-reference.md +262 -0
  760. package/docs/toolchains/cc1541-reference.md +316 -0
  761. package/docs/toolchains/cc65-reference.md +667 -0
  762. package/docs/toolchains/error-sources/cc65/README.md +1 -0
  763. package/docs/toolchains/error-sources/cc65/missing-setcpu.s +4 -0
  764. package/docs/toolchains/error-sources/cc65/range-error.s +4 -0
  765. package/docs/toolchains/error-sources/cc65/segment-overflow.c +6 -0
  766. package/docs/toolchains/error-sources/cc65/unresolved-external.c +6 -0
  767. package/docs/toolchains/error-sources/kickassembler/README.md +1 -0
  768. package/docs/toolchains/error-sources/kickassembler/branch-out-of-range.asm +4 -0
  769. package/docs/toolchains/error-sources/kickassembler/failed-assert.asm +3 -0
  770. package/docs/toolchains/error-sources/kickassembler/flat-lib.asm +4 -0
  771. package/docs/toolchains/error-sources/kickassembler/import-source-no-namespace.asm +5 -0
  772. package/docs/toolchains/error-sources/kickassembler/macro-without-parentheses.asm +6 -0
  773. package/docs/toolchains/error-sources/kickassembler/memory-block-overlap.asm +4 -0
  774. package/docs/toolchains/error-sources/kickassembler/symbol-clash-lib.asm +5 -0
  775. package/docs/toolchains/error-sources/kickassembler/symbol-clash-main.asm +6 -0
  776. package/docs/toolchains/error-sources/kickassembler/unknown-symbol.asm +4 -0
  777. package/docs/toolchains/error-sources/oscar64/README.md +1 -0
  778. package/docs/toolchains/error-sources/oscar64/asm-addressing-mode.c +7 -0
  779. package/docs/toolchains/error-sources/oscar64/region-overflow.c +6 -0
  780. package/docs/toolchains/error-sources/oscar64/undefined-extern-var.c +5 -0
  781. package/docs/toolchains/error-sources/oscar64/undefined-extern.c +6 -0
  782. package/docs/toolchains/error-sources/oscar64/unknown-vic-field.c +6 -0
  783. package/docs/toolchains/error-sources/oscar64/void-main.c +3 -0
  784. package/docs/toolchains/kickassembler-reference.md +1020 -0
  785. package/docs/toolchains/memory-layout-planning.md +362 -0
  786. package/docs/toolchains/oscar64-headers-reference.md +583 -0
  787. package/docs/toolchains/oscar64-reference.md +892 -0
  788. package/docs/toolchains/petcat-reference.md +290 -0
  789. package/docs/toolchains/png2prg.md +188 -0
  790. package/docs/toolchains/release-disk.md +314 -0
  791. package/docs/toolchains/sidreloc.md +236 -0
  792. package/docs/toolchains/spindle-reference.md +487 -0
  793. package/docs/toolchains/tape-mastering.md +592 -0
  794. package/docs/toolchains/unit-testing-6502.md +281 -0
  795. package/docs/workflow/agent-harness.md +478 -0
  796. package/package.json +105 -0
  797. package/templates/_harness/CLAUDE.md.template +91 -0
  798. package/templates/_harness/PLAN.md.template +72 -0
  799. package/templates/_harness/check.py +442 -0
  800. package/templates/_harness/claude/settings.json +17 -0
  801. package/templates/_harness/gen-asm-header.py +96 -0
  802. package/templates/_harness/harness.mk +273 -0
  803. package/templates/_harness/hooks/plan-gate.py +185 -0
  804. package/templates/_harness/meter/frame_meter.asm +311 -0
  805. package/templates/_harness/meter/frame_meter.c +155 -0
  806. package/templates/_harness/meter/frame_meter.h +80 -0
  807. package/templates/_harness/zp-used.py +104 -0
  808. package/templates/action-puzzle/.claude/settings.json +17 -0
  809. package/templates/action-puzzle/.mcp.json +10 -0
  810. package/templates/action-puzzle/CLAUDE.md +120 -0
  811. package/templates/action-puzzle/Makefile +125 -0
  812. package/templates/action-puzzle/PLAN.md +388 -0
  813. package/templates/action-puzzle/README.md +240 -0
  814. package/templates/action-puzzle/expect.json +25 -0
  815. package/templates/action-puzzle/src/cave.c +280 -0
  816. package/templates/action-puzzle/src/cave.h +80 -0
  817. package/templates/action-puzzle/src/gen_autopilot.h +20 -0
  818. package/templates/action-puzzle/src/gen_caves.h +59 -0
  819. package/templates/action-puzzle/src/gen_notes.h +16 -0
  820. package/templates/action-puzzle/src/hiscore.c +152 -0
  821. package/templates/action-puzzle/src/hiscore.h +41 -0
  822. package/templates/action-puzzle/src/level.c +64 -0
  823. package/templates/action-puzzle/src/level.h +24 -0
  824. package/templates/action-puzzle/src/main.c +599 -0
  825. package/templates/action-puzzle/src/render.c +239 -0
  826. package/templates/action-puzzle/src/render.h +31 -0
  827. package/templates/action-puzzle/src/sound.c +158 -0
  828. package/templates/action-puzzle/src/sound.h +22 -0
  829. package/templates/action-puzzle/tools/disk_check.py +77 -0
  830. package/templates/action-puzzle/tools/drive.py +106 -0
  831. package/templates/action-puzzle/tools/gen.py +454 -0
  832. package/templates/action-puzzle/tools/model_check.py +49 -0
  833. package/templates/action-puzzle/tools/verdict_codes.py +24 -0
  834. package/templates/adventure/.claude/settings.json +17 -0
  835. package/templates/adventure/.mcp.json +10 -0
  836. package/templates/adventure/CLAUDE.md +118 -0
  837. package/templates/adventure/Makefile +113 -0
  838. package/templates/adventure/PLAN.md +396 -0
  839. package/templates/adventure/README.md +277 -0
  840. package/templates/adventure/expect.json +25 -0
  841. package/templates/adventure/src/engine.c +543 -0
  842. package/templates/adventure/src/engine.h +48 -0
  843. package/templates/adventure/src/gen_script.h +141 -0
  844. package/templates/adventure/src/gen_world.h +511 -0
  845. package/templates/adventure/src/main.c +439 -0
  846. package/templates/adventure/src/picture.c +83 -0
  847. package/templates/adventure/src/picture.h +16 -0
  848. package/templates/adventure/src/save.c +159 -0
  849. package/templates/adventure/src/save.h +25 -0
  850. package/templates/adventure/src/sound.c +71 -0
  851. package/templates/adventure/src/sound.h +16 -0
  852. package/templates/adventure/src/text.c +349 -0
  853. package/templates/adventure/src/text.h +40 -0
  854. package/templates/adventure/tools/disk_check.py +128 -0
  855. package/templates/adventure/tools/drive.py +119 -0
  856. package/templates/adventure/tools/gen.py +1033 -0
  857. package/templates/adventure/tools/world.py +356 -0
  858. package/templates/beat-em-up/.claude/settings.json +17 -0
  859. package/templates/beat-em-up/.mcp.json +10 -0
  860. package/templates/beat-em-up/CLAUDE.md +136 -0
  861. package/templates/beat-em-up/Makefile +96 -0
  862. package/templates/beat-em-up/PLAN.md +502 -0
  863. package/templates/beat-em-up/README.md +315 -0
  864. package/templates/beat-em-up/expect-gameover.json +8 -0
  865. package/templates/beat-em-up/expect.json +29 -0
  866. package/templates/beat-em-up/src/anim.c +70 -0
  867. package/templates/beat-em-up/src/art.c +344 -0
  868. package/templates/beat-em-up/src/autopilot.h +154 -0
  869. package/templates/beat-em-up/src/brute.c +273 -0
  870. package/templates/beat-em-up/src/enemy.c +281 -0
  871. package/templates/beat-em-up/src/engine.asm +512 -0
  872. package/templates/beat-em-up/src/fighter.c +384 -0
  873. package/templates/beat-em-up/src/game.h +269 -0
  874. package/templates/beat-em-up/src/hud.c +135 -0
  875. package/templates/beat-em-up/src/main.c +341 -0
  876. package/templates/beat-em-up/src/sound.c +44 -0
  877. package/templates/beat-em-up/src/street.c +93 -0
  878. package/templates/beat-em-up/src/verdict.h +523 -0
  879. package/templates/beat-em-up/src/view.c +362 -0
  880. package/templates/beat-em-up/tools/drive.py +105 -0
  881. package/templates/beat-em-up/tools/flickercheck.py +273 -0
  882. package/templates/demo/.claude/settings.json +17 -0
  883. package/templates/demo/.mcp.json +10 -0
  884. package/templates/demo/CLAUDE.md +129 -0
  885. package/templates/demo/Makefile +66 -0
  886. package/templates/demo/PLAN.md +401 -0
  887. package/templates/demo/README.md +322 -0
  888. package/templates/demo/expect.json +945 -0
  889. package/templates/demo/src/config.asm +80 -0
  890. package/templates/demo/src/framework.asm +390 -0
  891. package/templates/demo/src/main.asm +139 -0
  892. package/templates/demo/src/music.asm +197 -0
  893. package/templates/demo/src/part_main.asm +279 -0
  894. package/templates/demo/src/part_title.asm +79 -0
  895. package/templates/demo/src/parts.asm +43 -0
  896. package/templates/demo/src/tables.asm +73 -0
  897. package/templates/demo/src/verdict.asm +179 -0
  898. package/templates/demo/tools/audio.py +80 -0
  899. package/templates/demo/tools/gen_expect.py +167 -0
  900. package/templates/demo/tools/probe.py +95 -0
  901. package/templates/hello/.claude/settings.json +17 -0
  902. package/templates/hello/.mcp.json +10 -0
  903. package/templates/hello/CLAUDE.md +100 -0
  904. package/templates/hello/Makefile +38 -0
  905. package/templates/hello/PLAN.md +200 -0
  906. package/templates/hello/expect.json +20 -0
  907. package/templates/hello/src/main.c +161 -0
  908. package/templates/hello/src/sprite.asm +27 -0
  909. package/templates/hello-kick/.claude/settings.json +17 -0
  910. package/templates/hello-kick/.mcp.json +10 -0
  911. package/templates/hello-kick/CLAUDE.md +100 -0
  912. package/templates/hello-kick/Makefile +17 -0
  913. package/templates/hello-kick/PLAN.md +199 -0
  914. package/templates/hello-kick/expect.json +20 -0
  915. package/templates/hello-kick/src/main.asm +233 -0
  916. package/templates/platformer/.claude/settings.json +17 -0
  917. package/templates/platformer/.mcp.json +10 -0
  918. package/templates/platformer/CLAUDE.md +125 -0
  919. package/templates/platformer/Makefile +86 -0
  920. package/templates/platformer/PLAN.md +360 -0
  921. package/templates/platformer/README.md +277 -0
  922. package/templates/platformer/expect.json +32 -0
  923. package/templates/platformer/src/actors.c +289 -0
  924. package/templates/platformer/src/anim.c +51 -0
  925. package/templates/platformer/src/art.c +212 -0
  926. package/templates/platformer/src/autopilot.h +30 -0
  927. package/templates/platformer/src/engine.asm +253 -0
  928. package/templates/platformer/src/game.h +248 -0
  929. package/templates/platformer/src/hud.c +90 -0
  930. package/templates/platformer/src/level.c +184 -0
  931. package/templates/platformer/src/main.c +306 -0
  932. package/templates/platformer/src/player.c +268 -0
  933. package/templates/platformer/src/sound.c +42 -0
  934. package/templates/platformer/src/verdict.h +206 -0
  935. package/templates/platformer/src/view.c +331 -0
  936. package/templates/platformer/stage-expect.json +6 -0
  937. package/templates/platformer/tools/drive.py +112 -0
  938. package/templates/platformer/tools/tearcheck.py +229 -0
  939. package/templates/shmup-vertical/.claude/settings.json +17 -0
  940. package/templates/shmup-vertical/.mcp.json +10 -0
  941. package/templates/shmup-vertical/CLAUDE.md +137 -0
  942. package/templates/shmup-vertical/Makefile +131 -0
  943. package/templates/shmup-vertical/PLAN.md +569 -0
  944. package/templates/shmup-vertical/README.md +189 -0
  945. package/templates/shmup-vertical/expect.json +38 -0
  946. package/templates/shmup-vertical/src/bullets.c +158 -0
  947. package/templates/shmup-vertical/src/bullets.h +35 -0
  948. package/templates/shmup-vertical/src/display.c +239 -0
  949. package/templates/shmup-vertical/src/display.h +40 -0
  950. package/templates/shmup-vertical/src/game.h +85 -0
  951. package/templates/shmup-vertical/src/glyph.asm +358 -0
  952. package/templates/shmup-vertical/src/hiscore.c +141 -0
  953. package/templates/shmup-vertical/src/hiscore.h +16 -0
  954. package/templates/shmup-vertical/src/hit.asm +180 -0
  955. package/templates/shmup-vertical/src/hitbox.c +59 -0
  956. package/templates/shmup-vertical/src/hitbox.h +25 -0
  957. package/templates/shmup-vertical/src/kernel.asm +330 -0
  958. package/templates/shmup-vertical/src/level.c +174 -0
  959. package/templates/shmup-vertical/src/level.h +27 -0
  960. package/templates/shmup-vertical/src/main.c +824 -0
  961. package/templates/shmup-vertical/src/mux.asm +321 -0
  962. package/templates/shmup-vertical/src/sound.asm +255 -0
  963. package/templates/shmup-vertical/src/step.asm +169 -0
  964. package/templates/shmup-vertical/src/waves.c +195 -0
  965. package/templates/shmup-vertical/src/waves.h +33 -0
  966. package/templates/shmup-vertical/stage-expect.json +6 -0
  967. package/templates/shmup-vertical/tools/drive.py +152 -0
  968. package/templates/shmup-vertical/tools/joytest.py +159 -0
  969. package/templates/shmup-vertical/tools/meter.py +15 -0
  970. package/templates/shmup-vertical/tools/phases.py +73 -0
@@ -0,0 +1,979 @@
1
+ ---
2
+ category: sprite
3
+ ---
4
+
5
+ <!-- doc-type: pitfall-reference -->
6
+
7
+ # Sprite Pitfalls
8
+
9
+ The VIC-II provides exactly eight hardware sprites. Every sprite technique
10
+ that goes beyond this hard limit — multiplexers, Y-stretch, mid-frame
11
+ recoloring — requires tight coordination between CPU writes and the VIC's
12
+ internal state machine. The pitfalls in this document describe the most
13
+ common failures: silent drops when too many sprites share a line, visual
14
+ artifacts when expansion state changes at the wrong moment, coordinate
15
+ teleportation when the 9th X bit is forgotten, collisions that vanish
16
+ before the CPU can read them, and sprites the game counts while the border
17
+ hides them. All of them have bitten experienced C64 coders.
18
+
19
+ ---
20
+
21
+ ## sprite_dma_overflow — A logical sprite re-armed while its hardware slot is still busy, or after its Y line has passed, is silently not drawn
22
+
23
+ **Severity:** critical
24
+ **Region:** both
25
+ **Triggered by registers:** D015, D010, D000, D001
26
+ **Triggered by techniques:** sprite_multiplex_8, stable_raster_irq, sprite_multiplex_24, sprite_sine_chain, sprite_multiplex_game, dot_3d_rotator
27
+ **Mitigated by techniques:** sprite_multiplex_8
28
+
29
+ ### Symptom
30
+
31
+ A multiplexed sprite engine that is supposed to show twelve, sixteen, or
32
+ more objects on screen displays only the first eight on any given raster
33
+ line. Logical sprites whose slot was re-armed too early or too late silently
34
+ disappear; in a naively ordered multiplexer these are the later entries in
35
+ the list, which is a software ordering, not a hardware one. There is no
36
+ visible corruption or tearing — sprites simply are not drawn, as if they
37
+ were never enabled.
38
+ The bug is easy to miss when objects are spread vertically because each
39
+ frame only a few lines have more than eight sprites active simultaneously.
40
+ The effect becomes obvious when objects cluster near the same Y coordinate:
41
+ a game with many bullets or enemies at the same height suddenly loses half
42
+ its sprites.
43
+
44
+ ### Mechanism
45
+
46
+ The VIC-II has exactly eight sprite DMA channels — one per sprite index
47
+ (0 through 7). On each raster line the chip checks every enabled sprite's Y
48
+ register against the current raster line and, for any slot whose 21-line
49
+ (42 if Y-expanded) DMA is already running, fetches that slot's three data
50
+ bytes via the s-access cycles for that channel; the eight slots are
51
+ independent and no slot has priority over another for activation. There
52
+ are no additional DMA channels hiding behind software flags. The hardware
53
+ simply has eight slots, and eight is the absolute maximum simultaneously
54
+ active sprites per raster line.
55
+
56
+ A multiplexer that repositions sprites by writing new Y coordinates between
57
+ groups relies on each hardware slot being free when its next logical sprite
58
+ is due. The chip compares each enabled slot's Y with the raster line late in
59
+ every line (cycles 55-56 in Bauer's timing; measured in VICE, a Y write
60
+ landing before roughly cycle 55 of the target line still starts the sprite
61
+ on the next line) and starts the sprite's DMA only if that slot's DMA is
62
+ currently off. Two things therefore lose a sprite, without error, without a
63
+ flag, and without any signal visible to the programmer: a slot re-armed to a
64
+ Y that matches while the slot's own DMA is still running (the incoming
65
+ sprite is skipped for the rest of the frame — measured in VICE x64sc, a slot
66
+ at Y=100 rewritten on line 112 to Y=120 never re-appeared, while Y=121 did),
67
+ and a Y written after the raster has already passed it, which the compare
68
+ never matches again that frame. $D015 has only eight bits; the chip never
69
+ "sees nine enabled indices", and no index has priority over another. (An
70
+ earlier version of this paragraph said the chip walks indices 0-7 in
71
+ ascending order and always discards the highest-indexed sprite on a
72
+ contested line; the drop is per slot and depends on timing, not on index.)
73
+
74
+ `sprite_multiplex_8` is on both metadata lines above for that reason: the
75
+ overflow arises inside a naive multiplexer — one whose IRQ fires late or
76
+ whose sort leaves two groups on one line — and a correct one, sorted and
77
+ spaced as the Fix describes, is what prevents it.
78
+
79
+ ### Fix
80
+
81
+ The fix is a correct sprite multiplexer running off raster IRQs, with two
82
+ requirements that must both hold:
83
+
84
+ 1. **Y-sorted logical sprites.** Before each frame, sort the entire logical
85
+ sprite list by ascending Y position. This ensures that when you assign
86
+ the first eight logical sprites to hardware slots 0-7 and schedule the
87
+ next IRQ for the ninth sprite's Y position, the groups are already
88
+ separated along the Y axis.
89
+
90
+ 2. **Minimum gap between groups.** Each group must begin at least one
91
+ raster line below the bottom of the previous group. The VIC considers a
92
+ sprite "active" for 21 lines (or 42 lines when Y-expanded). Schedule
93
+ the reuse IRQ to fire at or after the last line of the outgoing group
94
+ plus one, so the channel is free by the time the next logical sprite's Y
95
+ is reached. Aim for 3-4 lines of slack to leave room for IRQ jitter and
96
+ the cycle cost of the register writes themselves.
97
+
98
+ ### Worked example
99
+
100
+ The following KickAssembler skeleton shows the channel-reuse pattern.
101
+ Hardware sprite slots 0-7 are assigned to logical sprites 0-7 at frame
102
+ start. When the raster counter reaches the Y of logical sprite 8, an IRQ
103
+ reprograms all eight hardware slots to serve logical sprites 8-15.
104
+
105
+ ```kickassembler
106
+ // --- Frame start (vertical blank) ---
107
+ // Write first 8 logical sprites to hardware slots 0-7
108
+ .for (var i = 0; i < 8; i++) {
109
+ lda logicY + i // Y position of logical sprite i
110
+ sta $D001 + i * 2 // hardware sprite i Y register
111
+ lda logicX + i // X position (low byte)
112
+ sta $D000 + i * 2
113
+ lda logicPtr + i
114
+ sta SPRITE_PTRS + i // video matrix + $3F8
115
+ lda logicColor + i
116
+ sta $D027 + i
117
+ }
118
+ lda #$FF
119
+ sta $D015 // enable all 8 hardware sprites
120
+
121
+ // Schedule IRQ for group 2 (logical sprites 8-15)
122
+ // Fire one line above the topmost sprite in the next group
123
+ lda logicY + 8
124
+ sec
125
+ sbc #4 // 4-line slack
126
+ sta $D012 // raster compare
127
+ lda #$01
128
+ sta $D01A // enable raster IRQ
129
+
130
+ // --- Raster IRQ handler (fires above Y of logical sprite 8) ---
131
+ irq_group2:
132
+ pha
133
+ txa
134
+ pha
135
+ tya
136
+ pha
137
+
138
+ lda #$01
139
+ sta $D019 // acknowledge VIC IRQ
140
+
141
+ .for (var i = 0; i < 8; i++) {
142
+ lda logicY + 8 + i
143
+ sta $D001 + i * 2 // repoint hardware slot i → logical sprite 8+i
144
+ lda logicX + 8 + i
145
+ sta $D000 + i * 2
146
+ lda logicPtr + 8 + i
147
+ sta SPRITE_PTRS + i
148
+ lda logicColor + 8 + i
149
+ sta $D027 + i
150
+ }
151
+ // Update MSB X bits for the new group
152
+ lda xMsbGroup2
153
+ sta $D010
154
+
155
+ pla
156
+ tay
157
+ pla
158
+ tax
159
+ pla
160
+ rti
161
+ ```
162
+
163
+ Critical invariant: every element of `logicY[8..15]` that reuses a hardware
164
+ slot must be at least 21 lines below that slot's previous Y (42 if
165
+ Y-expanded). Measured in VICE x64sc 3.10: a slot rewritten to old Y + 21
166
+ re-displays on the very next line with no gap, old Y + 20 never appears
167
+ again that frame, old Y + 22 leaves one blank line (Y-expanded: + 42
168
+ seamless, + 41 dropped). The mechanism — the outgoing sprite's DMA switches
169
+ off in cycle 16 of line Y + 21 and the Y compare that would re-arm the slot
170
+ runs in cycle 55 of that same line — is from Bauer's VIC article, not
171
+ measured here. An earlier revision of this paragraph said 22 and claimed a
172
+ 21-line gap dropped a sprite; it does not. The 3-4 lines of slack in the Fix
173
+ come on top of the 21 for a different reason: the IRQ at `logicY[8] - 4`
174
+ also rewrites the slot's X, pointer and colour, and those act on the
175
+ still-running outgoing sprite from the next line (a colour write on line 112
176
+ recolours a Y = 100 sprite's rows 113-121, measured in VICE), so with that
177
+ IRQ scheme the practical same-slot spacing is 21 plus the slack, and a
178
+ 20-line gap silently drops the incoming sprite for that frame.
179
+
180
+ ### Cross-references
181
+
182
+ - Technique: `sprite_multiplex_8` — the sorted-Y, raster-IRQ multiplexer
183
+ that prevents overflow
184
+ - Technique: `sprite_multiplex_24` — extends the pattern to 24+ sprites
185
+ across three reuse passes
186
+ - Register: `D015` — sprite enable bitmask
187
+ - Registers: `D000`–`D00F` — per-sprite X/Y position
188
+ - Register: `D010` — sprite X MSB (see also `sprite_x_high_bit_wrong_register`)
189
+ - Pitfall: `sprite_x_high_bit_wrong_register` — a related issue when moving
190
+ sprites past X=255
191
+
192
+ ---
193
+
194
+ ## sprite_y_expand_double_register_write — Clearing Y-expand mid-display crunches the sprite if the write lands on one cycle; a second write does not help
195
+
196
+ **Severity:** high
197
+ **Region:** both
198
+ **Triggered by registers:** D017
199
+ **Triggered by techniques:** stable_raster_irq, sprite_expand, sprite_y_stretch_glitch, sprite_stretcher_d017
200
+
201
+ ### Symptom
202
+
203
+ A sprite that is supposed to switch from Y-expanded to normal height
204
+ mid-frame, while it is still in the display area, is occasionally the wrong
205
+ length: it comes out of the switch with its rows out of order and ends many
206
+ lines lower than it should — the "sprite crunch". The fault depends on the
207
+ exact cycle the $D017 write lands on within the raster line, so it comes and
208
+ goes with IRQ jitter: at almost every cycle position the switch is clean
209
+ (the sprite simply continues unexpanded from its current row), and at one
210
+ cycle position it crunches. An earlier version of this section described a
211
+ one-line artifact — a single repeated or skipped row at the line of the
212
+ write — on every mid-sprite write. Measured in VICE x64sc 3.10 PAL, no such
213
+ artifact exists: a clear at 62 of 64 cycle positions gave a clean switch
214
+ with no repeated or skipped row, and the remaining 2 positions gave the
215
+ crunch (a +21-line change, rows re-fetched out of order). A sentence here
216
+ about a subtler "half-pixel shift" on the 8565 was removed as unverifiable
217
+ on this machine: every run was on one model, VICE's default C64C (VIC-II
218
+ 8565), and none was compared with a 6569. An earlier version of this
219
+ sentence said VICE was run as a 6569 only.
220
+
221
+ ### Mechanism
222
+
223
+ The VIC-II implements Y expansion with a per-sprite expansion flip-flop
224
+ (the "advance line" / expansion flip-flop of Bauer's VIC-II article;
225
+ `exp_flop` in the VICE source). While the sprite's $D017 bit is clear the
226
+ flip-flop is held set; while the bit is set and the sprite's DMA is on, it
227
+ is inverted once per line (Bauer places this in cycle 55; VICE 3.10's PAL
228
+ cycle table, as read for `sprite_y_stretch_glitch` in
229
+ `docs/techniques/sprite.md`, at cycle 56). In cycle 16 of the following
230
+ line the 6-bit sprite data counter base MCBASE is loaded from the data
231
+ counter MC — moving the sprite on to its next 3-byte row — only if the
232
+ flip-flop is set; otherwise MCBASE is left alone and the same row is
233
+ fetched again. MCBASE is a counter, not the toggle; neither it nor the
234
+ flip-flop appears in the Programmer's Reference Guide. (An earlier version
235
+ of this paragraph called the toggle itself "MCBASE".)
236
+
237
+ A $D017 clear that lands anywhere else in the line does nothing worse than
238
+ set the flip-flop: from the next cycle-16 step the sprite advances a row
239
+ every line and finishes as a plain unexpanded sprite from its current row.
240
+ The problem is one cycle only. If the clear lands on cycle 15 (VICE 3.10's
241
+ PAL cycle table) of one of the sprite's display lines after the first —
242
+ immediately before the cycle-16 MCBASE step — the chip does not handle the
243
+ transition cleanly and MCBASE is loaded with a blend of its old value and
244
+ MC rather than either; the sprite's remaining length changes once, by a
245
+ data-dependent amount, and the rows come out of order. This is the "sprite
246
+ crunch" that `sprite_y_stretch_glitch` exploits deliberately; here it is an
247
+ accidental side effect of an otherwise normal register update. Which line
248
+ the write must land on is characterised in that technique entry (it
249
+ measured the second display line of a sprite at Y=100); in the
250
+ verification of this entry the crunching clear was pinned to raster line
251
+ 169 for a Y-expanded sprite at Y=150, a line on which the expanded sprite
252
+ would otherwise have repeated its row. State the cycle; whether the line
253
+ is one on which MCBASE advances has not been measured and is not claimed.
254
+
255
+ ### Fix
256
+
257
+ The fix is to control *where in the line* the single $D017 write lands, not
258
+ to write the register twice. Fire a stable raster IRQ on the target line and
259
+ issue one `sta $D017` at a known cycle position that is not the crunch
260
+ cycle; anywhere on the sprite's first display line, or on a line before the
261
+ sprite starts, is also safe. A second write does not help: an earlier
262
+ version of this entry prescribed a "double-write trick" — two back-to-back
263
+ STAs to "resynchronize" the toggle — and said a single write produced a
264
+ one-line repeated or skipped row. A 64-position single-vs-double sweep in
265
+ VICE x64sc 3.10 PAL (a clear of one sprite's bit at every cycle position of
266
+ a display line, once as `sta $D017 / nop / nop` and once as
267
+ `sta $D017 / sta $D017` at the identical first-write cycle) found the two
268
+ identical at every position, including the crunch cycle, where the double
269
+ write produced byte-for-byte the same crunch. The earlier text also said
270
+ the second write came "one CPU cycle later"; two consecutive `sta $D017`
271
+ instructions in fact write four cycles apart (STA abs is 4 cycles and the
272
+ write is its last cycle), well past the cycle-16 step it would have needed
273
+ to influence. Do not reach for a read-modify-write (`inc`/`dec`/`asl
274
+ $D017`) to get consecutive-cycle writes either: an RMW does write twice on
275
+ consecutive cycles, but its first write is the old register value.
276
+
277
+ If the sprite's Y expand state only needs to change between frames (not
278
+ mid-frame), the simplest moment is during vertical blank, before the
279
+ sprite's Y position comes into view: the sprite's DMA is off, so the
280
+ expansion flip-flop is held set and the crunch cycle cannot be hit.
281
+
282
+ ### Worked example
283
+
284
+ The following KickAssembler snippet fires a stable raster IRQ one line
285
+ above the sprite's current top edge, then issues a single write to flip
286
+ sprite 3's Y-expand bit off cleanly. On that line the sprite's DMA has not
287
+ started, so the write cannot land on the crunch cycle. (An earlier version
288
+ of this listing wrote $D017 twice; the second write changed nothing in a
289
+ 64-position VICE sweep and has been removed.)
290
+
291
+ ```kickassembler
292
+ // Stable raster IRQ fires at rasterTarget (one line above sprite top)
293
+ // SP3_Y is the sprite's current Y position; IRQ fires at SP3_Y - 1
294
+
295
+ irq_clear_yexpand:
296
+ pha
297
+ txa
298
+ pha
299
+
300
+ lda #$01
301
+ sta $D019 // acknowledge VIC raster IRQ
302
+
303
+ // Single write: clear bit 3 of $D017 (sprite 3 Y-expand off)
304
+ lda #%11110111 // new value: sprite 3 bit clear, others unchanged
305
+ sta $D017 // one write is enough; the flip-flop is held set from here
306
+
307
+ // Re-arm IRQ for next occurrence
308
+ lda #<nextIrqLine
309
+ sta $D012
310
+
311
+ pla
312
+ tax
313
+ pla
314
+ rti
315
+ ```
316
+
317
+ If the target is to toggle expansion *on* (set the bit) rather than off,
318
+ the same single write applies with the appropriate mask. Only the *clear*
319
+ transition was swept for the crunch in the verification above; setting the
320
+ bit mid-sprite was not measured here and is not claimed to be safe at every
321
+ cycle.
322
+
323
+ For situations where multiple sprites need simultaneous Y-expand state
324
+ changes, include all sprite bits in the mask value and issue the one write.
325
+ A single STA covering all eight sprites is sufficient; one write per sprite
326
+ is wasteful and risks introducing timing skew between sprites. (An earlier
327
+ version of these two paragraphs said both transitions "require the
328
+ double-write"; see the Fix above for the measurement that retired it.)
329
+
330
+ ### Cross-references
331
+
332
+ - Technique: `stable_raster_irq` — prerequisite for placing the $D017
333
+ write at a known horizontal cycle position, away from the crunch cycle
334
+ - Technique: `sprite_y_stretch_glitch` — the intentional exploitation of
335
+ the same expansion flip-flop / MCBASE-load quirk to produce tall sprites
336
+ - Register: `D017` — sprite Y-expand control register
337
+
338
+ ---
339
+
340
+ ## sprite_x_high_bit_wrong_register — Sprite teleports to far left when X crosses 255 because $D010 MSB is not updated
341
+
342
+ **Severity:** medium
343
+ **Region:** both
344
+ **Triggered by registers:** D010
345
+ **Triggered by techniques:** sprite_sine_chain, sprite_multiplex_24, logic_rate_decoupling, sprite_multiplex_game, actor_activation_window, per_frame_hitbox, wave_director, multi_sprite_object, flip_screen_rooms, mixed_sprite_char_actors, lane_depth_engine, dot_3d_rotator, starfield, dypp_sprite_sine_scroller, sprite_border_scroller, sprites_only_screen_mode, dysp_side_border_sprites
346
+
347
+ ### Symptom
348
+
349
+ A sprite moving smoothly from left to right across the screen suddenly
350
+ teleports from near the right edge back to the far left when its X
351
+ coordinate crosses 256. The motion is otherwise smooth in both the
352
+ sub-256 and the 256+ ranges. The jump happens exactly at the X=256
353
+ boundary and reverses at the same boundary when moving right-to-left.
354
+ In a game this manifests as an enemy or projectile that vanishes off
355
+ the right side of the screen and reappears at the left at the same Y
356
+ position — as if the coordinate space wraps at 256.
357
+
358
+ ### Mechanism
359
+
360
+ The VIC-II uses a 9-bit X coordinate for each sprite. The low 8 bits live
361
+ in the per-sprite register at `$D000 + sprite * 2` (sprite 0 → $D000,
362
+ sprite 1 → $D002, ..., sprite 7 → $D00E). The 9th bit — the MSB —
363
+ does not live in those per-sprite registers. All eight MSBs are packed
364
+ into a single shared register: **$D010 (MSIGX)**. Bit 0 of $D010 is the
365
+ MSB for sprite 0, bit 1 for sprite 1, and so on through bit 7 for sprite 7.
366
+
367
+ The common mistake is to maintain a 9-bit (or 16-bit) X variable in game
368
+ code but write only the low 8 bits to `$D000 + sprite * 2`, forgetting
369
+ $D010 entirely. When X < 256 this is invisible — the MSB is 0 and $D010's
370
+ corresponding bit is already 0 (or was never set). When X reaches 256, the
371
+ low 8 bits wrap to 0 and the hardware X coordinate becomes 0 with the MSB
372
+ still clear — the sprite teleports to X=0 on screen. Setting the MSB in
373
+ $D010 when X ≥ 256 would place the sprite correctly at the screen position
374
+ corresponding to the full 9-bit value, but the write never happens.
375
+
376
+ Sprite X is a 9-bit coordinate, 0-511, in the VIC-II's own pixel space. The
377
+ 320-pixel display window spans X=24..343 (X=31..334 with CSEL=0); X=0 is 24
378
+ pixels into the left border, so a sprite needs X ≥ 24 to be fully inside
379
+ the window, and the right-hand 88 pixels of the window (X=256..343) are
380
+ reachable only with the $D010 bit set. A sprite at X=256 therefore sits
381
+ about 232 pixels from the window's left edge and is fully visible; it is
382
+ still fully visible at X=320 (occupying 320..343), begins to be covered by
383
+ the right border from X=321, and is entirely hidden in the border from
384
+ X=344 (measured in VICE x64sc: X=256 renders at VIC X 256..279, screenshot
385
+ x 264-287). Note the helper below already assumes this range (0-343). An
386
+ earlier version of this paragraph put X=0 at the window's left edge and
387
+ X=256 "slightly past the right edge", with X=256..344 clipped; both were
388
+ wrong.
389
+
390
+ ### Fix
391
+
392
+ Always update $D010 when moving any sprite across the X=256 boundary. The
393
+ correct pattern is a read-modify-write: read the current $D010 value, set
394
+ or clear the bit corresponding to the sprite being moved, then write the
395
+ modified value back. Never assume $D010 is 0 — other sprites in the same
396
+ frame may have their MSB bits set.
397
+
398
+ In C (Oscar64), a helper macro handles the 9-bit write atomically:
399
+
400
+ ```c
401
+ // Set sprite n to 9-bit X position xpos (0-343)
402
+ static inline void sprite_set_x(uint8_t n, uint16_t xpos) {
403
+ *(volatile uint8_t*)(0xD000 + n * 2) = (uint8_t)xpos; // low 8 bits
404
+ if (xpos >= 256)
405
+ *(volatile uint8_t*)0xD010 |= (1 << n); // set MSB
406
+ else
407
+ *(volatile uint8_t*)0xD010 &= ~(1 << n); // clear MSB
408
+ }
409
+ ```
410
+
411
+ In KickAssembler, a macro that operates on a 16-bit X variable stored
412
+ in zero page:
413
+
414
+ ```kickassembler
415
+ // spriteSetX: set hardware sprite .sprNum to 9-bit X in .xLo / .xHi (0 or 1)
416
+ // .sprNum = sprite index 0-7 (literal)
417
+ // xLo = zero-page address of low byte, xHi = adjacent high byte (0 or 1)
418
+ .macro spriteSetX(sprNum, xLo, xHi) {
419
+ lda xLo
420
+ sta $D000 + sprNum * 2 // write low 8 bits
421
+
422
+ lda xHi // high byte is 0 or 1 (only bit 0 relevant)
423
+ beq !setMsbZero+ // if 0, clear the MSB bit
424
+
425
+ lda $D010
426
+ ora #(1 << sprNum) // set MSB bit for this sprite
427
+ sta $D010
428
+ jmp !done+
429
+
430
+ !setMsbZero:
431
+ lda $D010
432
+ and #~(1 << sprNum) // clear MSB bit for this sprite
433
+ sta $D010
434
+
435
+ !done:
436
+ }
437
+ ```
438
+
439
+ For multiplexed sprites that reassign hardware channels mid-frame, rebuild
440
+ the entire $D010 byte during each IRQ handler pass — accumulate the MSB
441
+ bits for all eight currently-assigned logical sprites and write the combined
442
+ mask once. This avoids stale MSB bits from the previous hardware assignment
443
+ lingering in $D010.
444
+
445
+ ### Cross-references
446
+
447
+ - Register: `D010` — MSIGX, the sprite X position MSB register
448
+ - Registers: `D000`–`D00E` — per-sprite X position low bytes
449
+ - Technique: `sprite_multiplex_24` — shows `vspr_update()` accumulating
450
+ `xymask` ($D010) across all reuse passes
451
+ - Pitfall: `sprite_dma_overflow` — a related issue when more than 8 sprites
452
+ share a raster line
453
+
454
+ ---
455
+
456
+ ## sprite_priority_collision_silent — Collision events vanish because $D01E/$D01F are read-to-clear and the read happens too late
457
+
458
+ **Severity:** medium
459
+ **Region:** both
460
+ **Triggered by registers:** D01E, D01F, D019
461
+ **Triggered by techniques:** sprite_collision_detect, mob_priority
462
+
463
+ ### Symptom
464
+
465
+ The game's collision detection misses hits that visually occurred. Two
466
+ sprites clearly overlapped on screen but the game did not react — the
467
+ enemy was not destroyed, the player did not take damage. The bug is
468
+ intermittent: most collisions register correctly, but some are silently
469
+ lost, especially during busy frames with many simultaneous overlaps or
470
+ when the game logic runs late in the frame. Occasionally the reverse
471
+ happens: a collision is detected a frame after the visual overlap, making
472
+ the response feel one frame delayed.
473
+
474
+ A related symptom appears when the programmer reads $D01E or $D01F in an
475
+ interrupt handler and also checks it in the main loop — the main-loop check
476
+ always sees zero because the interrupt already cleared the register.
477
+
478
+ ### Mechanism
479
+
480
+ $D01E (SPSPCL — sprite-to-sprite collision) and $D01F (SPBGCL —
481
+ sprite-to-background collision) are latched registers. The VIC-II sets
482
+ individual bits during raster rendering as collisions are detected in
483
+ hardware. Each bit, once set, stays set until the CPU reads the register.
484
+ The act of reading the register — any read, from any address mode, at any
485
+ privilege level — clears all bits in that register simultaneously. This
486
+ is the "read-to-clear" mechanic.
487
+
488
+ The latching behavior means that collisions accumulate: if sprite 0 hits
489
+ sprite 3 on line 80 and sprite 0 hits sprite 5 on line 120, by the time
490
+ the CPU reads $D01E at the end of the frame, bit 0, bit 3, and bit 5 are
491
+ all set (sprite 5's bit is set too, since both sprites involved in each
492
+ collision have their bits latched). A single read at any point after line
493
+ 80 captures all of this correctly — provided nothing else read $D01E first.
494
+
495
+ Two patterns cause silent losses:
496
+
497
+ **Double-read:** An interrupt handler reads $D01E to check for a collision.
498
+ This clears the register. Later, the main loop reads $D01E again — it sees
499
+ zero. The collision events from that frame are gone. This is especially
500
+ common when a VIC IRQ is used to detect collisions in the IRQ handler and
501
+ the main loop independently polls the same register.
502
+
503
+ **Late read with prior clear:** If any code path reads $D01E or $D01F as a
504
+ side effect (even a "harmless" diagnostic read, or a read inside a debugger
505
+ print routine), that read destroys the accumulated collision state. A
506
+ diagnostic that works fine on its own can suppress collision detection when
507
+ enabled.
508
+
509
+ A second, subtler issue: the registers report *which sprites* were involved
510
+ in collisions, not *which specific pair*. If sprites A, B, and C all
511
+ overlap each other, bits for A, B, and C are all set, but the register
512
+ gives no information about which pairs actually touched. Games that need
513
+ pair-level resolution must infer it from the bit pattern combined with
514
+ spatial reasoning.
515
+
516
+ ### Fix
517
+
518
+ **Establish a single read point per frame.** Read $D01E and $D01F exactly
519
+ once per frame — at the start of the game loop, before any other code can
520
+ inadvertently read them — and immediately store both values in RAM variables.
521
+ All collision processing for that frame operates on the cached values.
522
+ Never read $D01E or $D01F a second time in the same frame.
523
+
524
+ The safest place is at the very start of the frame, in the vertical blank
525
+ handler or as the first action of the main game loop body after the
526
+ `rirq_wait()` call:
527
+
528
+ ```kickassembler
529
+ frameStart:
530
+ lda $D01E // read sprite-sprite collision register...
531
+ sta spSprColl // ...and cache it immediately; register is now clear
532
+ lda $D01F // read sprite-background collision register...
533
+ sta spBgColl // ...cache it; register is now clear
534
+
535
+ // All collision tests this frame use spSprColl and spBgColl, not $D01E/$D01F
536
+ ```
537
+
538
+ **For per-line IRQ-driven collision response:** Enable the collision IRQ via
539
+ $D01A bit 2 (EMMC, sprite-sprite) and bit 1 (EMBC, sprite-background) — the
540
+ same layout as $D019. (An earlier version of this sentence had the two bits
541
+ swapped.) The VIC fires an
542
+ IRQ on the same line the collision is latched. Inside the IRQ handler, read
543
+ $D019 first to identify the interrupt source (bit 2 = sprite-sprite, bit
544
+ 1 = sprite-background), then read $D01E or $D01F as appropriate. Acknowledge
545
+ with a write-1-to-clear to the matching bit of $D019. This gives sub-frame
546
+ collision timing but requires careful separation from any main-loop reads.
547
+
548
+ ```kickassembler
549
+ // IRQ handler that distinguishes collision type via $D019
550
+ irq_collision:
551
+ pha
552
+ lda $D019 // read VIC interrupt status register
553
+ and #%00000110 // mask: bit 2 = sprite-sprite, bit 1 = sprite-bg
554
+ beq !notCollision+ // if neither bit set, not a collision IRQ
555
+
556
+ tax // save interrupt cause
557
+ lda $D01E
558
+ sta spSprColl // cache sprite-sprite result (clears $D01E)
559
+ lda $D01F
560
+ sta spBgColl // cache sprite-background result (clears $D01F)
561
+
562
+ txa
563
+ sta $D019 // acknowledge: write-1-to-clear the same bits
564
+
565
+ !notCollision:
566
+ pla
567
+ rti
568
+ ```
569
+
570
+ **Do not read $D01E or $D01F in diagnostic or logging code** without
571
+ understanding that every such read is destructive. During development,
572
+ gate diagnostic reads behind a flag or read them only in a context where
573
+ the cached variable is immediately populated.
574
+
575
+ ### Cross-references
576
+
577
+ - Register: `D01E` — SPSPCL, sprite-to-sprite collision latch (read-to-clear)
578
+ - Register: `D01F` — SPBGCL, sprite-to-background collision latch (read-to-clear)
579
+ - Register: `D019` — VICIRQ, VIC interrupt status / acknowledge register
580
+ - Technique: `sprite_collision_detect` — the correct polling pattern and
581
+ single-read-per-frame discipline
582
+ - Technique: `stable_raster_irq` — prerequisite for cycle-exact IRQ-driven
583
+ collision response
584
+
585
+ ---
586
+
587
+ ## sprite_x_range_hidden_and_seam — A sprite at X below 24 or above 343 sits under the border while the game counts it alive, and on PAL X $1F8 to $1FF is never drawn at all
588
+
589
+ **Severity:** medium
590
+ **Region:** both
591
+ **Triggered by registers:** D000, D010
592
+ **Triggered by techniques:** object_pool, tile_grid_collision, sprite_multiplex_8, sprite_multiplex_24, sideborder_open, multi_sprite_object, flip_screen_rooms, lane_depth_engine, starfield, dypp_sprite_sine_scroller, sprite_border_scroller, dysp_side_border_sprites
593
+
594
+ ### Symptom
595
+
596
+ Two faces of one fact.
597
+
598
+ The HUD says three enemies are alive and the player sees two. A shot
599
+ fired at the edge of the screen never lands and never stops. The object
600
+ is there in every table the game keeps, its sprite is enabled, its
601
+ collision bit fires when something walks into it, and nobody can see it,
602
+ because its X is 10, or 350, and the border is drawn over it. A game
603
+ built blind, with the counts checked and the picture not looked at, ships
604
+ this.
605
+
606
+ The other face needs the side borders open. A sprite sliding left off
607
+ the picture, its X stepping down from $1E0 through $1FF and on to $20F
608
+ (which the chip sees as $00F), blinks out for eight steps and comes back.
609
+ On NTSC it does not blink.
610
+
611
+ ### Mechanism
612
+
613
+ Sprite X is nine bits, 0 to 511: eight in `$D000 + 2n`, the ninth in
614
+ `$D010`. Nothing in the chip clips that range to the picture. The
615
+ display window is X 24 to 343 with CSEL = 1 (31 to 334 with CSEL = 0),
616
+ and the border is the front-most layer, drawn over any sprite pixel that
617
+ falls outside the window (`mob_priority` in `techniques/sprite.md`). A
618
+ sprite at X 0 to 23 is partly or wholly under the left border; at 321 to
619
+ 343 partly and from 344 wholly under the right one. The sprite still
620
+ exists to the VIC-II: its DMA runs every line (the stall in
621
+ `recipes/kickassembler/sideborder-open.md` depends on sprites at X 344
622
+ and 500 fetching on every line) and its position registers read back.
623
+ Only the pixels are missing. Whether `$D01E` still latches a
624
+ sprite-to-sprite hit under the border was not measured here; `$D01F`
625
+ does not, since border pixels are not foreground (`mob_priority`).
626
+
627
+ The seam is a different thing. A PAL line is 63 cycles of 8 pixels, 504
628
+ positions, so the VIC-II's X counter runs 0 to $1F7 and wraps. The nine
629
+ bits can hold $1F8 to $1FF, but the counter never reaches those values,
630
+ so a sprite placed there never matches and is not drawn on any line.
631
+ Measured in VICE x64sc with the side borders open: X 503 ($1F7) draws
632
+ at picture columns 7 to 30, wrapping from the right end of the line to
633
+ the left in the middle of the sprite; X 504, 507 and 511 draw nothing;
634
+ X 0 draws at columns 8 to 31. Position $1F7 and position 0 are
635
+ neighbours, and the eight values between them are a hole. A 16-bit game
636
+ coordinate that steps through them one a frame loses the sprite for
637
+ eight frames; one that steps by 8 or more may skip the hole on one
638
+ frame and fall into it on another.
639
+
640
+ An NTSC line is 65 cycles, 520 positions (arithmetic; the cycle count
641
+ is `hardware/vic-ii-reference.md`'s). Measured in VICE x64sc with
642
+ `-model ntsc` and the borders open: every value from 488 to 511 draws,
643
+ X 511 sits at columns 7 to 30 and X 0 at 8 to 31, so $1FF and 0 are
644
+ neighbours and there is no hole in $1E0 to $20F. Where the line's eight
645
+ positions beyond 512 fall is not visible in the picture and was not
646
+ measured here.
647
+
648
+ The picture VICE writes starts at column 0 = X 496 on PAL and X 504 on
649
+ NTSC, and column 8 = X 0 on both, so a sprite at $1E0 (480) shows its
650
+ last eight columns on PAL (columns 0 to 7) and nothing on NTSC, not
651
+ because it is hidden but because it is left of the picture's edge.
652
+
653
+ ### Fix
654
+
655
+ Keep the visibility test in the game, not in the chip. An object is on
656
+ screen only while 24 <= X <= 343 (fully so up to 320); with CSEL = 0,
657
+ 31 <= X <= 334. Everything else is off screen for scoring, counting and
658
+ collision, whatever `$D015` says. Either despawn it (free the pool slot,
659
+ so the count and the picture agree) or clamp it (a player sprite stops
660
+ at 24 and 320). For X in the 256 to 343 range the test needs the ninth
661
+ bit; see `sprite_x_high_bit_wrong_register` above for getting it into
662
+ `$D010`.
663
+
664
+ With the side borders open on PAL, never write an X in $1F8 to $1FF.
665
+ Wrap the coordinate at 504, not 512: a sprite leaving to the left at
666
+ $1F7 continues at $1F7 - 504 = -1, that is, at the far right, and a
667
+ table-driven position should be built modulo 504. On NTSC the wrap is
668
+ at 512 within the range measured; a routine that has to run on both
669
+ picks the modulus from the model at start-up.
670
+
671
+ ### Worked example
672
+
673
+ The bad pattern is a count over pool slots whose state is not zero,
674
+ with no test on X. The fix is a nine-bit range test and a despawn:
675
+
676
+ ```kickassembler
677
+ // objX, objX+1: the 16-bit X the game keeps for one object; objState: 0 = free.
678
+ // The HUD counts every slot whose objState is not 0.
679
+ // Wrong: an object at X 10 or X 400 is alive to that count and under the
680
+ // border to the player. Right: an object counts only while 24 <= X <= 343.
681
+ .label objX = $02
682
+ .label objState = $04
683
+ .const X_LEFT = 24
684
+ .const X_RIGHT = 343
685
+
686
+ on_screen: // returns C = 1 on screen, C = 0 hidden
687
+ lda objX + 1
688
+ bne high_page
689
+ lda objX
690
+ cmp #X_LEFT // 0..23 hidden, 24..255 on screen
691
+ bcs yes
692
+ clc
693
+ rts
694
+ high_page:
695
+ cmp #1
696
+ bne no // 512 and up does not exist on the chip
697
+ lda objX
698
+ cmp #<(X_RIGHT + 1) // 256..343 on screen, 344..511 hidden
699
+ bcc yes
700
+ no:
701
+ clc
702
+ rts
703
+ yes:
704
+ sec
705
+ rts
706
+
707
+ despawn_if_hidden:
708
+ jsr on_screen
709
+ bcs keep
710
+ lda #0
711
+ sta objState // freed: the HUD no longer counts it
712
+ keep:
713
+ rts
714
+ ```
715
+
716
+ The measurements behind the numbers above came from one KickAssembler
717
+ program run in VICE x64sc 3.10 (`-default -warp -limitcycles 6000000`,
718
+ PAL and `-model ntsc`), eight solid 24 x 21 sprites in distinct colours
719
+ on a blue background under a black border, X values passed on the
720
+ command line, columns read from the exit screenshot with PIL. Two
721
+ arrangements: borders closed, one sprite per row at eight X values; and
722
+ borders open over a 42-line region by the method of
723
+ `recipes/kickassembler/sideborder-open.md` (all eight sprites Y-expanded
724
+ on the region, a `DEC $D016` whose second write lands on the cycle
725
+ between the two border comparisons, a `YSCROLL` rewrite each line), with
726
+ sprite 0 at the value under test and the other seven parked in the
727
+ window and at X 344. The PAL constants were the recipe's; on NTSC the
728
+ loop opened the border with two more cycles before the first `DEC` and
729
+ one more in the loop, which is a by-product and not a claim about the
730
+ cycle number. Column 8 of the picture is X 0.
731
+
732
+ Borders closed, both models, identical columns:
733
+
734
+ | X | Drawn | Picture columns |
735
+ |---|---|---|
736
+ | 0 | no | none |
737
+ | 23 | 23 of 24 columns | 32 to 54 |
738
+ | 24 | all | 32 to 55 |
739
+ | 320 | all | 328 to 351 |
740
+ | 321 | 23 of 24 | 329 to 351 |
741
+ | 343 | 1 of 24 | 351 |
742
+ | 344 | no | none |
743
+ | 511 | no | none |
744
+
745
+ Borders open, sprite 0 at the seam:
746
+
747
+ | X | PAL columns | NTSC columns |
748
+ |---|---|---|
749
+ | $1E0 (480) | 0 to 7 | none (left of the picture) |
750
+ | $1E8 (488) | not run | 0 to 7 |
751
+ | $1EF (495) | 0 to 22 | 0 to 14 |
752
+ | $1F0 (496) | 0 to 23 | 0 to 15 |
753
+ | $1F4 (500) | 4 to 27 (the side-border recipe's own picture) | 0 to 19 |
754
+ | $1F7 (503) | 7 to 30 | 0 to 22 |
755
+ | $1F8 (504) | none | 0 to 23 |
756
+ | $1FB (507) | none | 3 to 26 |
757
+ | $1FC (508) | not run | 4 to 27 |
758
+ | $1FF (511) | none | 7 to 30 |
759
+ | $200 (0) | 8 to 31 | 8 to 31 |
760
+ | $208 (8) | 16 to 39 | 16 to 39 |
761
+ | $20F (15) | 23 to 46 | 23 to 46 |
762
+
763
+ ### Cross-references
764
+
765
+ - Registers: `D000` to `D00E` and `D010`: the nine-bit X the range test
766
+ reads
767
+ - Register: `D016`: CSEL narrows the window to 31 to 334
768
+ - Pitfall: `sprite_x_high_bit_wrong_register`: the ninth bit the test
769
+ above depends on
770
+ - Technique: `sideborder_open`: the only way a sprite at X 0 to 23 or
771
+ 344 up is seen, and the only case in which the PAL hole shows
772
+ - Technique: `mob_priority`: the border is drawn over every sprite; a
773
+ sprite under it latches no `$D01F` hit
774
+ - Technique: `object_pool`: the slot state the HUD counts, and where the
775
+ despawn belongs
776
+ - Technique: `tile_grid_collision`: its column is `(x - 24) >> 3`, which
777
+ goes negative for a sprite under the left border
778
+ - Recipe: `recipes/kickassembler/sideborder-open.md`: the region loop
779
+ the measurement reused, with a sprite at X 500 in its picture
780
+ - Recipe: `recipes/kickassembler/sprite-sine-chain.md`: X 343 showing
781
+ one column, X 16 starting at column 32
782
+
783
+ ## sprite_registers_persist_across_state_change — A state's setup that writes only the sprite registers it uses inherits the previous state's enables, expansion, multicolour and latched collisions
784
+
785
+ **Severity:** medium
786
+ **Region:** both
787
+ **Triggered by registers:** D015, D01B, D01C, D01D, D01F
788
+ **Triggered by techniques:** attract_mode_input_replay, flip_screen_rooms, two_player_state_swap
789
+
790
+ ### Symptom
791
+
792
+ The title screen's pointer, or a piece of its logo, is still sitting on
793
+ the play field after the game starts. The player sprite is twice as wide
794
+ as it should be, or shows in the wrong colours, on the first game after
795
+ the title and looks right after a death. A GAME OVER banner has the
796
+ player sprite parked across it. Or nothing is visibly wrong and the game
797
+ still ends the moment play begins: the collision test fires on the first
798
+ frame from a hit the play state never had. Everything is correct when
799
+ the state is entered from a state that used no sprites, and wrong when
800
+ it is entered from one that did, so the fault seems to move about.
801
+
802
+ ### Mechanism
803
+
804
+ The VIC-II keeps every register until something writes it. Nothing about
805
+ a game-state change touches the chip: a screen clear, a new character
806
+ set or a new colour table changes what the sprites are drawn over, not
807
+ whether they are drawn. So each state inherits, from whichever state ran
808
+ before it, the enable mask in `$D015`, the expansions in `$D017` and
809
+ `$D01D`, the multicolour mask in `$D01C`, the priority mask in `$D01B`,
810
+ the X high bits in `$D010`, the shape pointers, the colours in
811
+ `$D027` to `$D02E` and the two shared multicolours in `$D025` and
812
+ `$D026`.
813
+
814
+ A state's setup routine written to set only what the state uses leaves
815
+ all of that standing. The usual shape is a play state that enables its
816
+ player with an `ORA` into `$D015` so as not to disturb anything, sets a
817
+ position and a pointer, and returns. Every other bit of every other
818
+ sprite register is the title screen's. The pointer sprite the title
819
+ enabled stays enabled, at its title position, over the field. The logo's
820
+ X expansion and multicolour bit were set on sprite 0, and the play state
821
+ reuses sprite 0 for the player, so the player is drawn 48 pixels wide
822
+ with its bit pairs read as multicolour, in a colour from `$D025` or
823
+ `$D026` that the play state never set. Nothing goes wrong in the code;
824
+ the chip is doing what it was last told.
825
+
826
+ `$D01F` and `$D01E` make it a logic fault and not only a picture. Both
827
+ are latches: a bit is set when a hit is drawn and stays set until the
828
+ register is read, and the read clears the whole register. A state that
829
+ never reads them leaves any hit it drew latched for the next state, and
830
+ a stale sprite that the next state did not know it had goes on drawing
831
+ hits of its own. The first `LDA $D01F` in the play state then returns a
832
+ non-zero value from a sprite the play state does not own, and a test of
833
+ the form "any hit means the player is dead" ends the game on its first
834
+ frame.
835
+
836
+ The same thing happens in the other direction. A play state that leaves
837
+ its sprites enabled and hands over to a text-only game-over screen has
838
+ the player drawn across the banner, and because the banner is drawn in
839
+ the foreground colour the player collides with it and `$D01F` records a
840
+ hit in a state that has no sprites at all.
841
+
842
+ ### Fix
843
+
844
+ Give every state an entry routine, and make the first thing that routine
845
+ does a VIC sprite baseline: write every sprite register to the value
846
+ this state wants, whether or not this state uses it. Write `$D015` first
847
+ with the state's own mask, so that nothing from the old state is drawn
848
+ while the rest is being set, then `$D010`, `$D017`, `$D01B`, `$D01C` and
849
+ `$D01D` as explicit values, then read `$D01E` and `$D01F` once each and
850
+ discard the result to clear the latches. Only then set the state's own
851
+ pointers, positions and colours. A state that uses no sprites calls the
852
+ same routine with a mask of zero. Do not `ORA` and `AND` a state's bits
853
+ into `$D015`; a state owns the whole register while it runs.
854
+
855
+ Sprite colours and the shared multicolours are not in the list because a
856
+ sprite that is not enabled shows none of them, but a state that reuses a
857
+ sprite number must set that sprite's colour, and a state that uses
858
+ multicolour must set `$D025` and `$D026`, for the same reason.
859
+
860
+ If the game reads the collision latches in its per-frame routine, the
861
+ read in the entry routine is still needed: the latch may have been set
862
+ between the old state's last read and the switch.
863
+
864
+ ### Worked example
865
+
866
+ The play state on the left of the measurement writes only what it uses.
867
+ The baseline on the right is what the fixed build calls at the top of
868
+ every state's entry routine, with the state's own mask in A.
869
+
870
+ ```kickassembler
871
+ // Bad: a play-state setup that touches only the bits it uses.
872
+ // $D015 bits 1 to 7, $D01B, $D01C, $D01D and $D01F are whatever the
873
+ // title screen left.
874
+ play_enter_bad:
875
+ lda #$82
876
+ sta $07f8 // player shape
877
+ lda #160
878
+ sta $d000
879
+ lda #133
880
+ sta $d001
881
+ lda #1
882
+ sta $d027
883
+ lda $d015
884
+ ora #%00000001 // "enable the player", disturb nothing else
885
+ sta $d015
886
+ rts
887
+
888
+ // Fixed: every state entry starts here. A = the sprites this state uses.
889
+ vic_sprite_baseline:
890
+ sta $d015 // this state's own mask first
891
+ lda #0
892
+ sta $d010 // X high bits
893
+ sta $d017 // Y expand
894
+ sta $d01b // priority
895
+ sta $d01c // multicolour
896
+ sta $d01d // X expand
897
+ lda $d01e // read once to clear the latches
898
+ lda $d01f
899
+ rts
900
+
901
+ play_enter_fixed:
902
+ lda #%00000001
903
+ jsr vic_sprite_baseline
904
+ lda #$82
905
+ sta $07f8
906
+ lda #160
907
+ sta $d000
908
+ lda #133
909
+ sta $d001
910
+ lda #1
911
+ sta $d027
912
+ rts
913
+
914
+ over_enter_fixed:
915
+ lda #%00000000 // a text-only state: no sprites at all
916
+ jsr vic_sprite_baseline
917
+ rts
918
+ ```
919
+
920
+ The measurement behind this entry was one KickAssembler program run in
921
+ VICE x64sc 3.10, windowless, `-default -warp -limitcycles 8000000`, on
922
+ PAL and with `-model ntsc`. Three states, sixty frames each, switched on
923
+ a frame count: a title with sprite 0 as a logo at (100, 62), X expanded
924
+ and multicolour, and sprite 1 as a pointer bar at (250, 190); a play
925
+ state with a solid character field on rows 13 to 24 and sprite 0 as the
926
+ player at (160, 133), whose bottom line 153 is one line above the
927
+ field's first line 154, so the player itself never touches the field;
928
+ and a text-only game-over state with a banner on row 12. Each state read
929
+ `$D015`, `$D01B`, `$D01C`, `$D01D` and `$D01F` once, in that order, on
930
+ its thirtieth frame, and a monitor tracepoint dumped the copies. The bad
931
+ build's play and game-over entries wrote only what they used; the fixed
932
+ build called the baseline above first. The two models gave the same
933
+ bytes and the same pixel counts throughout.
934
+
935
+ ```text
936
+ state bad build fixed build
937
+ 15 1B 1C 1D 1F 15 1B 1C 1D 1F
938
+ title 03 00 01 01 00 03 00 01 01 00
939
+ play 03 00 01 01 02 01 00 00 00 00
940
+ game over 03 00 01 01 03 00 00 00 00 00
941
+
942
+ Play state, bad: player 48 px wide, solid red (the title's $D025),
943
+ 1,008 sprite pixels; the pointer bar, 84 white
944
+ pixels, standing on the field; $D01F bit 1 is the
945
+ pointer's hit on the field. The play state drew no
946
+ collision of its own.
947
+ Play state, fixed: player 24 px wide in its 1-pixel stripe pattern,
948
+ 23 lit columns (the pattern's first is blank), 252
949
+ white pixels; no pointer; $D01F 00.
950
+ Game over, bad: 63 of the banner's 185 white pixels visible, the
951
+ other 122 under 1,008 red sprite pixels (arithmetic);
952
+ the pointer still on screen; $D01F bit 0 is the
953
+ player on the banner, bit 1 the pointer's field hit
954
+ latched after the play state's one read.
955
+ Game over, fixed: 185 white pixels, no sprite pixels, $D01F 00.
956
+ ```
957
+
958
+ ### Cross-references
959
+
960
+ - Registers: `D015`, `D010`, `D017`, `D01B`, `D01C`, `D01D`: the
961
+ baseline's write list, in the order above
962
+ - Registers: `D01E`, `D01F`: read-to-clear latches; the entry routine's
963
+ read is what stops a hit crossing a state boundary
964
+ - Registers: `D025`, `D026`, `D027` to `D02E`: the colours a state that
965
+ reuses a sprite number must set; the red player above was `$D025`
966
+ - Pitfall: `sprite_priority_collision_silent`: the same latches, read too
967
+ late within one state rather than not at all across two
968
+ - Pitfall: `render_during_state_transition_clobbers_banner` on
969
+ `text-mode-render.md`: the character-mode half of the same transition,
970
+ where the old state's renderer keeps drawing over the new screen
971
+ - Technique: `attract_mode_input_replay`: a title that starts the game
972
+ and returns to itself crosses this boundary twice per cycle
973
+ - Technique: `flip_screen_rooms`: hides the sprites in `$D015` around its
974
+ redraw, which is the correct form of the same write
975
+ - Technique: `two_player_state_swap`: a per-player block swapped on death
976
+ must swap the sprite registers with it
977
+ - Game design: `game_state_machine` in `game-design/game-structure.md`:
978
+ the entry-routine table this baseline belongs at the top of, and the
979
+ acceptance checks that read `$D015` on a state's first frame