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,1189 @@
1
+ <!-- doc-type: reference -->
2
+
3
+ # C64 Game Design Patterns
4
+
5
+ Recurring mechanical patterns that C64 games use, described so that an AI
6
+ agent can match the right pattern to a brief. Each section names the
7
+ tradeoffs in C64 terms — cycle budgets, RAM budgets, and hardware
8
+ constraints — so the choice is mechanical, not aesthetic.
9
+
10
+ Cross-references: `../techniques/sprite.md` for multiplexer mechanics and
11
+ hardware sprite capabilities; `../techniques/scroll.md` for soft-scroll and
12
+ parallax; `./c64-game-archetypes.md` for which archetype pairs with which
13
+ patterns.
14
+
15
+ ---
16
+
17
+ ## Collision systems
18
+
19
+ Collision is the most expensive per-frame logic in an action game. C64
20
+ games use up to four distinct systems, sometimes in combination.
21
+
22
+ ---
23
+
24
+ ### Hardware sprite-sprite collision
25
+
26
+ Register $D01E reads back a bitmask of which sprites have overlapped
27
+ another sprite during the last frame. Reading the register clears it. The
28
+ chip sets bits automatically with zero CPU cost.
29
+
30
+ Constraints:
31
+
32
+ - **Lossy.** Only one bit per sprite pair; no record of which pair or when.
33
+ If two separate collisions involve the same sprite in one frame, the
34
+ register cannot distinguish them.
35
+ - **Position-after-the-fact.** The register reflects the state at read time,
36
+ not at the frame-render cycle where the pixels overlapped. If you read at
37
+ the top of the game loop, the collision happened somewhere in the previous
38
+ frame.
39
+ - **Multiplexed sprites break it.** When a hardware sprite is reused to
40
+ represent two logical actors via a multiplexer, $D01E reports hardware
41
+ sprite collisions, not logical actor collisions. A read after the first
42
+ multiplex band clears the register before the second band fires.
43
+
44
+ Use $D01E for: shmups where the player sprite never multiplexes and enemy
45
+ bullets are always in a known hardware sprite slot. The lossy single-bit
46
+ result is acceptable because any hit in the frame ends the player life
47
+ regardless of which bullet caused it.
48
+
49
+ ```asm
50
+ lda $D01E ; read and clear sprite-sprite register
51
+ and #%00000001 ; test bit 0 (player sprite)
52
+ bne .hit ; branch if player overlapped any sprite
53
+ ```
54
+
55
+ ---
56
+
57
+ ### Hardware sprite-background collision
58
+
59
+ Register $D01F records which sprites overlapped a non-zero (non-space)
60
+ foreground character cell during rendering. Same single-bit-per-sprite
61
+ design, same read-clears semantics.
62
+
63
+ Constraints: identical to $D01E plus one more — the collision fires on
64
+ any foreground pixel, including decorative tiles. If your map has purely
65
+ decorative "solid-looking" character cells that are not walls, you must
66
+ arrange the character set so decorative tiles appear as background-colored
67
+ pixels at the ROM/RAM level (multicolor tricks) or use a separate solid
68
+ map and ignore $D01F entirely.
69
+
70
+ Use $D01F for: shmups where the terrain is a single-color foreground
71
+ charset and any contact with it is fatal or obstructing.
72
+
73
+ ---
74
+
75
+ ### Software bounding-box collision
76
+
77
+ Compare each actor's X, Y extents against every other actor's extents in
78
+ pure 6510 arithmetic. Typical cost is 28–35 cycles per pair.
79
+
80
+ ```asm
81
+ ; Actor A at (ax, ay), width aw, height ah
82
+ ; Actor B at (bx, by), width bw, height bh
83
+ ; Returns carry set on overlap
84
+
85
+ .check_bbox:
86
+ lda ax
87
+ clc
88
+ adc aw
89
+ cmp bx ; A_right < B_left?
90
+ bcc .no_hit
91
+ lda bx
92
+ clc
93
+ adc bw
94
+ cmp ax ; B_right < A_left?
95
+ bcc .no_hit
96
+ lda ay
97
+ clc
98
+ adc ah
99
+ cmp by ; A_bottom < B_top?
100
+ bcc .no_hit
101
+ lda by
102
+ clc
103
+ adc bh
104
+ cmp ay ; B_bottom < A_top?
105
+ bcc .no_hit
106
+ sec ; overlap
107
+ rts
108
+ .no_hit:
109
+ clc
110
+ rts
111
+ ```
112
+
113
+ At 8 actors, that is 28 pair-checks worst case (n*(n-1)/2). At 35 cycles
114
+ per check, 28 pairs cost ~980 cycles — under 1 % of a PAL frame. At 16
115
+ actors the cost is ~3920 cycles, still well within budget if bounded-box
116
+ is the only collision layer.
117
+
118
+ Use software bounding-box for: platformers where you need sub-tile accuracy
119
+ for landing/hitting platforms, and for adventure games where the player
120
+ rectangle must exactly enter a doorway hitbox.
121
+
122
+ ---
123
+
124
+ ### Tilemap-grid collision
125
+
126
+ For terrain that never moves, test the map grid directly rather than every
127
+ possible object pair. Convert the actor's bounding corners to tile
128
+ coordinates and read the tile index from screen RAM or a parallel solid-bit
129
+ array.
130
+
131
+ ```asm
132
+ ; Convert pixel (px, py) to tile (tx, ty):
133
+ ; tx = px / 8 (LSR three times for 8-pixel tiles)
134
+ ; ty = py / 8 likewise
135
+ ; Screen RAM address = $0400 + ty*40 + tx
136
+
137
+ .solid_check:
138
+ lda py
139
+ lsr
140
+ lsr
141
+ lsr ; ty = py >> 3
142
+ tax
143
+ lda row_lo, x ; precomputed: row_lo[ty] = ($0400 + ty*40) lo
144
+ sta ptr
145
+ lda row_hi, x
146
+ sta ptr+1
147
+ lda px
148
+ lsr
149
+ lsr
150
+ lsr ; tx = px >> 3
151
+ tay
152
+ lda (ptr), y ; read tile index from screen RAM
153
+ cmp #solid_min ; solid tiles are >= solid_min
154
+ bcs .is_solid
155
+ rts
156
+ .is_solid:
157
+ ; handle blocked movement
158
+ ```
159
+
160
+ A parallel solid-bit array (one bit per tile, 125 bytes for a 40×25 grid)
161
+ avoids comparing character codes and survives charset reassignments. Allocate
162
+ it in zero page or page 2 for fast access.
163
+
164
+ Tilemap-grid collision costs 20–40 cycles per corner tested. A four-corner
165
+ player check is 80–160 cycles — negligible for one player.
166
+
167
+ Use tilemap-grid for: all games where the terrain is tile-based (platformers,
168
+ adventure games, top-down mazes). Combine with software bounding-box for
169
+ actor-actor hits and hardware registers only where the constraints above are
170
+ acceptable.
171
+
172
+ **Decision table:**
173
+
174
+ | Game type | Primary collision | Secondary |
175
+ |-----------------|-----------------------------|-------------------|
176
+ | Shmup | $D01E + $D01F | — |
177
+ | Platformer | Tilemap-grid (terrain) | SW bbox (actors) |
178
+ | Adventure | Tilemap-grid only | — |
179
+ | Maze/Pac-style | Tilemap-grid | — |
180
+
181
+ ---
182
+
183
+ ## Map representations
184
+
185
+ The right map format depends on whether the game fits on one screen,
186
+ how far it scrolls, and whether ROM or disk space is the binding constraint.
187
+
188
+ ---
189
+
190
+ ### Screen-aligned tilemap
191
+
192
+ When the entire playfield fits within the 40×25 character grid, screen RAM
193
+ at $0400 is the map. Color RAM at $D800 holds per-tile colors. The map is
194
+ already in the VIC-II's native format; no copy loop is needed.
195
+
196
+ Cost: 1000 bytes screen RAM + 1000 bytes color RAM = 2 KB for the visible
197
+ state. Additional pages are a direct copy-to-screen-RAM operation (1000
198
+ LDA/STA pairs, ~6000 cycles, done in the vertical blank).
199
+
200
+ Use for: single-screen games (Pac-style mazes, single-room puzzles, fixed
201
+ arena shooters).
202
+
203
+ ---
204
+
205
+ ### Scrolling tilemap
206
+
207
+ Horizontal or vertical scrolling games maintain a map buffer wider or taller
208
+ than the visible area. The classic layout for a horizontally scrolling game:
209
+
210
+ - Map buffer: 48 columns × 25 rows of tile indices (1200 bytes for the
211
+ logical map slice in RAM).
212
+ - A `scroll_col_offset` byte tracks which logical column is at the left
213
+ edge of the visible 40 columns.
214
+ - On each 8-pixel scroll step, copy the new column of tiles into screen RAM
215
+ and color RAM, then update `scroll_col_offset`.
216
+
217
+ The soft-scroll register ($D016 XSCROLL) handles the 0–7 pixel fine phase.
218
+ When XSCROLL wraps, the column copy fires. This is the canonical
219
+ soft-scroll pattern described in `../techniques/scroll.md`. Decoding the
220
+ map into that column (metatiles to characters and colour RAM, the RLE
221
+ decoder, the CharPad import path) is `tile_map_render` on the same page,
222
+ with the recipe `../recipes/oscar64/tile-map-render.md`.
223
+
224
+ Vertical scrolling uses $D011 YSCROLL the same way, with a row copy at the
225
+ 8-pixel boundary.
226
+
227
+ RAM cost for the buffer slice: 1–2 KB depending on scroll depth. For large
228
+ worlds, the full map lives on disk or in a compressed block and is paged in
229
+ as the player approaches a boundary.
230
+
231
+ ---
232
+
233
+ ### Compressed maps
234
+
235
+ A 128-room dungeon at 40×25 tiles per room is 128 KB uncompressed — well
236
+ above the 64 KB address space. Compression is mandatory.
237
+
238
+ **RLE:** Consecutive identical tile runs are stored as (count, tile). The
239
+ ratio depends on the room: three 40×22 rooms measured in
240
+ `recipes/oscar64/level-rle-decoder.md` pack 4.13:1 (a platform room),
241
+ 1.40:1 (a maze, the worst of the three) and 10.73:1 (a nearly empty
242
+ room). The decoder in that recipe is 80 bytes of 6510 code by hand, or
243
+ 149 bytes compiled from C at `-O2`, and decodes one 880-byte room in
244
+ 16,470 to 27,673 cycles by hand or 28,193 to 50,447 cycles from C
245
+ (measured in VICE x64sc 3.10). An earlier version of this paragraph said
246
+ 3:1 to 5:1, a 20 to 30 byte decoder and a few hundred cycles per screen;
247
+ none of the three had been measured, and neither the size nor the
248
+ cycle figure matches what was measured for this format.
249
+
250
+ **Dictionary / token substitution:** Repeated 2×2 or 4×4 tile blocks are
251
+ assigned token IDs. The map stores tokens; the decoder expands them into
252
+ screen RAM. Achieves 8:1 or better on repeating tilesets (castle brickwork,
253
+ cave walls). Costs more decoder RAM but compresses aggressively.
254
+
255
+ For disk-based games, the decompressor runs during the load of each level.
256
+ For cartridge or single-load games, all levels must decompress from a
257
+ banked ROM into the map buffer on demand.
258
+
259
+ ---
260
+
261
+ ## Enemy / actor state machines
262
+
263
+ All moving non-player objects are actors. The C64 enforces an implicit actor
264
+ limit through the sprite system: eight hardware sprites per frame. Multiplexed
265
+ setups handle 16–32 logical sprites; software-rendered actors (character
266
+ graphics) can go higher but at steep cycle cost.
267
+
268
+ ---
269
+
270
+ ### Slot-based actor table
271
+
272
+ Allocate a fixed array of N actor slots. Each slot holds:
273
+
274
+ ```
275
+ actor_x: .byte 0 ; pixel X (0–255 or 16-bit for wide worlds)
276
+ actor_y: .byte 0 ; pixel Y
277
+ actor_state: .byte 0 ; state machine index (0 = inactive)
278
+ actor_type: .byte 0 ; type index (indexes into behavior tables)
279
+ actor_timer: .byte 0 ; general-purpose countdown
280
+ actor_dx: .byte 0 ; signed velocity X
281
+ actor_dy: .byte 0 ; signed velocity Y
282
+ ```
283
+
284
+ Seven bytes per actor. Eight actors: 56 bytes. Sixteen actors: 112 bytes.
285
+ All actor arrays live in the same zero page / page 2 / $C000 block so
286
+ indexed addressing is fast.
287
+
288
+ The sprite multiplexer (see `../techniques/sprite.md`) draws the active
289
+ slots. Slots with `actor_state = 0` are skipped by both the update loop
290
+ and the multiplexer.
291
+
292
+ ---
293
+
294
+ ### Per-actor state byte and handler jump table
295
+
296
+ Each actor type has a jump table of state-handler addresses indexed by
297
+ `actor_state`. The game loop iterates slots, reads the type, loads the
298
+ handler pointer for the current state, and JMPs indirectly.
299
+
300
+ ```asm
301
+ .update_actors:
302
+ ldx #0
303
+ .next_slot:
304
+ lda actor_state, x
305
+ beq .skip ; state 0 = inactive
306
+ ldy actor_type, x
307
+ lda handler_lo, y ; handler table indexed by type
308
+ sta jmp_ptr
309
+ lda handler_hi, y
310
+ sta jmp_ptr+1
311
+ jmp (jmp_ptr) ; handler receives X = slot index
312
+ .skip:
313
+ inx
314
+ cpx #MAX_ACTORS
315
+ bne .next_slot
316
+ ```
317
+
318
+ Each handler reads `actor_state, x`, branches on it to the correct
319
+ sub-handler, updates position/timer, and transitions state by writing a
320
+ new value to `actor_state, x`. Transitions to state 0 return the slot to
321
+ the free pool.
322
+
323
+ Cost: one indirect JMP per actor per frame (~6 cycles) plus the handler
324
+ body. A simple patrol enemy handler (move, wall-check, reverse) runs in
325
+ ~80 cycles.
326
+
327
+ ---
328
+
329
+ ### Path-based vs reactive AI
330
+
331
+ **Path-based:** Actor follows a pre-baked waypoint list stored in ROM.
332
+ State byte indexes the current waypoint. On each update, move toward
333
+ waypoint[state]; when within 2 pixels, increment state to the next
334
+ waypoint; wrap at the end. Zero decision logic, predictable, suitable for
335
+ fixed patrol routes in platformers and maze games.
336
+
337
+ ```asm
338
+ ; Simplified path follower
339
+ .path_step:
340
+ ldy actor_timer, x ; timer = waypoint index
341
+ lda waypoint_x, y
342
+ sec
343
+ sbc actor_x, x
344
+ beq .match_x
345
+ bmi .move_left
346
+ .move_right:
347
+ inc actor_x, x
348
+ jmp .check_y
349
+ .move_left:
350
+ dec actor_x, x
351
+ jmp .check_y
352
+ .match_x:
353
+ ; advance waypoint only when X matches
354
+ inc actor_timer, x
355
+ ```
356
+
357
+ **Reactive AI:** Actor computes direction from its position to the player
358
+ each frame. Subtract player_x from actor_x to get dx; subtract player_y
359
+ from actor_y to get dy; adjust velocity by sign(dx) and sign(dy). The sign
360
+ extraction is a single BPL/BMI branch. Predictable pursuit; pair with a
361
+ short random timer that pauses the actor for variety.
362
+
363
+ Reactive AI costs ~40 cycles per actor for the subtraction and sign-branch.
364
+ Path-based costs ~20 cycles per actor. Use path-based for turrets and fixed
365
+ patrol routes; use reactive for chasers and bosses.
366
+
367
+ ---
368
+
369
+ ### Object pool: slot table, spawn and despawn
370
+
371
+ The slot table above is an object pool. The parts that recur in every
372
+ game are the allocator, the spawner that feeds it, and the tick that
373
+ walks only the live slots. All figures below are cycles measured with
374
+ CIA1 timer A in VICE x64sc 3.10, Oscar64 -O2, eight slots, net of the
375
+ call and loop around them (rung 1; the listing is
376
+ `../recipes/oscar64/object-pool.md`).
377
+
378
+ **Parallel arrays, not structs.** One array per attribute, indexed by
379
+ slot number, so every access is `lda obj_x,y` with the slot in a register.
380
+ A struct array needs a multiply or a stride-added pointer per field.
381
+ Oscar64 compiles the C below to the same absolute-indexed code.
382
+
383
+ **Find a free slot by scanning for state 0.** Cost grows with the slot
384
+ number found: 27 cycles when slot 0 is free, 64 when slot 3 is the first
385
+ free one, 124 for a full pool that refuses. About 12 cycles per slot
386
+ examined, so a 16-slot pool costs up to about 220 on a refusal (rung 3,
387
+ from the measured eight-slot figures). Refusals are the normal case in a
388
+ busy frame, so the full-pool number is the one to budget.
389
+
390
+ **Free-list alternative.** Keep a stack of free slot numbers; allocate by
391
+ popping, free by pushing. A pop and a push together measured 44 cycles,
392
+ constant whatever the pool size. The trade-offs: eight bytes plus a top
393
+ index; every free must go through the push, so a handler that clears
394
+ `obj_state` directly leaks the slot for good; and slots come back in
395
+ free order, not lowest first, which changes draw order on a multiplexer
396
+ that walks slots in index order. Use it for pools of 16 or more where the
397
+ scan's worst case matters; the scan is fine at eight.
398
+
399
+ **Spawn from a wave table.** Five bytes per entry, in frame order,
400
+ terminated by a frame byte of $ff:
401
+
402
+ | byte | meaning |
403
+ |---|---|
404
+ | 0 | frame on which the entry fires |
405
+ | 1 | type (indexes the per-type tables) |
406
+ | 2 | x of the first object |
407
+ | 3 | y |
408
+ | 4 | count; objects are placed 16 pixels apart in x |
409
+
410
+ The spawner keeps one position into the table and fires every entry
411
+ whose frame byte equals the current frame. A spawn into a full pool is
412
+ dropped, not queued: the wave loses that object and the game goes on.
413
+ The game skeleton recipe in issue #1 reuses this layout. A spawn that
414
+ fills slot 0 and frees it again measured 146 cycles, of which 27 is the
415
+ scan; the rest is six stores and, in the listing, two counters and a log
416
+ store that a game would not have.
417
+
418
+ **Despawn.** Writing 0 to `obj_state` frees the slot; the other fields
419
+ are left stale and overwritten by the next spawn. Two paths free a slot
420
+ inside the tick: y at or past the bottom of the window (the constant is
421
+ the game's choice; 250 in the listing) and a per-slot countdown timer
422
+ reaching 0. A hit does not free the slot: it sets a dying state and a
423
+ short timer, so the explosion frames play from the same slot and the
424
+ timer does the freeing. Timer 0 means no timer, for bosses and scenery.
425
+
426
+ **Iterate only active slots.** The tick tests `obj_state` and skips free
427
+ slots. Eight active slots: 380 cycles for the move, off-screen test and
428
+ timer, 47.5 per slot. Eight free slots: 106 cycles, 13.25 per slot
429
+ skipped. A 16-slot pool with 8 active is therefore about 486 cycles a
430
+ frame before any per-type behaviour runs (rung 3).
431
+
432
+ ```c
433
+ // Object pool: parallel arrays, scan allocator, wave table, per-frame tick.
434
+ #define MAX_OBJ 8
435
+ #define NO_SLOT 0xff
436
+ #define OFF_BOTTOM 250
437
+
438
+ char obj_state[MAX_OBJ]; // 0 = free; anything else is active
439
+ char obj_type[MAX_OBJ];
440
+ char obj_x[MAX_OBJ];
441
+ char obj_y[MAX_OBJ];
442
+ char obj_dy[MAX_OBJ];
443
+ char obj_timer[MAX_OBJ]; // frames left; 0 = no timer
444
+
445
+ static const char type_life[4] = { 0, 12, 12, 40 };
446
+ static const char type_dy[4] = { 0, 1, 1, 2 };
447
+
448
+ // frame, type, x, y, count; $ff ends the table. Entries in frame order.
449
+ static const char wave_table[] = {
450
+ 0, 1, 24, 50, 8,
451
+ 1, 3, 100, 70, 3,
452
+ 0xff };
453
+ static char wave_pos;
454
+
455
+ char pool_alloc(void)
456
+ {
457
+ for (char i = 0; i < MAX_OBJ; i++)
458
+ if (obj_state[i] == 0)
459
+ return i;
460
+ return NO_SLOT;
461
+ }
462
+
463
+ char spawn(char type, char x, char y)
464
+ {
465
+ char s = pool_alloc();
466
+ if (s == NO_SLOT)
467
+ return NO_SLOT; // pool full: the wave loses this one
468
+ obj_type[s] = type;
469
+ obj_x[s] = x;
470
+ obj_y[s] = y;
471
+ obj_dy[s] = type_dy[type];
472
+ obj_timer[s] = type_life[type];
473
+ obj_state[s] = 1; // last: the slot is visible once set
474
+ return s;
475
+ }
476
+
477
+ void wave_step(char frame)
478
+ {
479
+ while (wave_table[wave_pos] == frame)
480
+ {
481
+ char type = wave_table[wave_pos + 1];
482
+ char x = wave_table[wave_pos + 2];
483
+ char y = wave_table[wave_pos + 3];
484
+ char count = wave_table[wave_pos + 4];
485
+ for (char k = 0; k < count; k++)
486
+ spawn(type, x + (k << 4), y);
487
+ wave_pos += 5;
488
+ }
489
+ }
490
+
491
+ void update_all(void)
492
+ {
493
+ for (char i = 0; i < MAX_OBJ; i++)
494
+ {
495
+ if (obj_state[i] == 0)
496
+ continue; // free slot: skip
497
+ char y = obj_y[i] + obj_dy[i];
498
+ obj_y[i] = y;
499
+ if (y >= OFF_BOTTOM)
500
+ obj_state[i] = 0; // left the screen
501
+ else if (obj_timer[i] != 0 && --obj_timer[i] == 0)
502
+ obj_state[i] = 0; // timer ran out
503
+ }
504
+ }
505
+ ```
506
+
507
+ The fragment compiles with Oscar64 -O2 as written once a `main` calls
508
+ `wave_step` and `update_all` once a frame; the listing gate does not
509
+ build C fragments on this page. In the recipe's build the compiler emits
510
+ `update_all` as one absolute-indexed loop with the timer decrement
511
+ folded into an `sbc #0` that borrows from the off-screen compare, which
512
+ is the code a hand assembler would write (read from Oscar64's `.asm`
513
+ output for the recipe listing, not from this fragment). `../recipes/oscar64/simple-shmup.md` spawns
514
+ its four enemies inline into a fixed `enemies[]` array with an `active`
515
+ flag; the pool is what that becomes once counts vary per wave.
516
+
517
+ ---
518
+
519
+ ## Game loop patterns
520
+
521
+ ---
522
+
523
+ ### Single-buffer IRQ-driven scroll
524
+
525
+ The dominant C64 pattern. The game runs one logical screen buffer. A raster
526
+ IRQ fires at the bottom of the playfield to swap sprite pointers and perform
527
+ any split-screen HUD work. Scroll column copies happen in the vertical blank
528
+ IRQ. Everything is single-buffered: the CPU writes to screen RAM while the
529
+ VIC-II reads from it, which is safe because writes and reads contend only
530
+ during the 8 raster lines of badlines, producing at most one garbled
531
+ character per badline — the classic "scroll tear" that well-timed VBlank
532
+ copies avoid.
533
+
534
+ The frame structure:
535
+
536
+ ```
537
+ VBlank IRQ fires (line 0 / line 300 PAL):
538
+ - Copy new scroll column to screen RAM
539
+ - Update XSCROLL / YSCROLL register
540
+ - Update sprite positions from actor table
541
+ - Acknowledge $D019, re-arm for HUD split
542
+
543
+ [Game logic runs in main loop during active display]
544
+
545
+ Raster IRQ fires at HUD split line:
546
+ - Write HUD sprite data
547
+ - Possibly change border color for debug
548
+ - Acknowledge $D019, re-arm for next VBlank
549
+ ```
550
+
551
+ The main loop processes input, advances actor states, resolves collision,
552
+ and updates score. On PAL (50 Hz) the loop has ~19 700 cycles of CPU time
553
+ per frame after IRQ overhead. The loop's wait-for-frame, frame counter,
554
+ dropped-frame detection and border-colour budget bar are the technique
555
+ `frame_sync_loop` in `techniques/raster.md` (recipe
556
+ `recipes/oscar64/frame-sync-loop.md`); reading the controls as press
557
+ events with auto-repeat is `techniques/input.md`; sub-pixel movement and
558
+ the jump arc are `techniques/maths.md`.
559
+
560
+ ---
561
+
562
+ ### Double-buffer
563
+
564
+ Two full screen RAM areas (1 KB each: 1,000 cells plus the sprite pointer
565
+ block in the last eight bytes of the page) alternate: the CPU draws into
566
+ the back buffer while the VIC-II displays the front buffer. At VBlank,
567
+ swap the VIC-II base address ($D018) to flip. The layout, the sprite
568
+ pointer block that moves with the page, and the frame-parity discipline
569
+ are `screen_double_buffer_d018` in `../techniques/memory-banking.md`, with
570
+ the recipe `../recipes/oscar64/double-buffer.md` and a companion that shows
571
+ the corrupted sprite you get without the pointer mirror.
572
+
573
+ Memory cost: 1 KB for the second screen RAM (an earlier version of this
574
+ section said 2 KB). Color RAM ($D800–$DBFF) cannot be double-buffered
575
+ (only one color RAM exists in hardware), so double-buffering only
576
+ eliminates screen-RAM tearing, not color-RAM tearing.
577
+
578
+ Use double-buffer only when the game redraws most of the screen each frame
579
+ (3D wireframe, full-screen bitmap effects) and tearing is visually
580
+ unacceptable. Standard tile-scroll games should prefer single-buffer plus
581
+ carefully timed column copies.
582
+
583
+ ---
584
+
585
+ ### 60 Hz vs 50 Hz update rate and cross-region releases
586
+
587
+ PAL runs at 50 frames/second; NTSC runs at ~60 frames/second. A game loop
588
+ tied to the frame rate runs 20 % faster on NTSC. Three strategies:
589
+
590
+ 1. **PAL-only:** Ignore NTSC. Ship for the European market. Fastest to
591
+ implement; widely acceptable for 1980s-style games.
592
+
593
+ 2. **Dual-rate logic:** Detect region via raster line count ($D011 bit 7
594
+ cycle comparison, or count raster lines in VBlank and compare to 312
595
+ vs 263). Run game logic at the detected frame rate. Physics values
596
+ (gravity, speeds) must be tuned separately per region, stored in two
597
+ ROM tables selected at startup.
598
+
599
+ 3. **Fixed-timestep decoupled from frame rate:** Run game logic at a fixed
600
+ 50 Hz tick using a CIA timer (CIA1 Timer A), fire an NMI or flag a
601
+ semaphore, and render at the display frame rate. Correct but complex;
602
+ the extra timer interrupt costs ~35 cycles per frame of overhead. Used
603
+ by some later commercial titles.
604
+
605
+ For most games: PAL-only or dual-rate with two physics tables is the
606
+ practical choice. See `../hardware/pal-ntsc-reference.md` for region
607
+ detection details and the `c64_pal_ntsc_diff` tool for per-topic diffs.
608
+
609
+ ---
610
+
611
+ ## Memory layouts for game state
612
+
613
+ ---
614
+
615
+ ### The 38911-byte basic-free area
616
+
617
+ BASIC ROM occupies $A000–$BFFF. Switching it out (bit 0 of $0001) frees
618
+ 8 KB for code or data but loses BASIC. All action games disable BASIC. With
619
+ BASIC and KERNAL both out ($0001 = %00110101), the full $A000–$FFFF range
620
+ (minus I/O at $D000–$DFFF) is available.
621
+
622
+ Practical RAM budget for a game without KERNAL:
623
+
624
+ ```
625
+ $0002–$00FF Zero page (254 bytes) — fast scratch, counters, pointers
626
+ $0100–$01FF Stack (256 bytes, fixed)
627
+ $0200–$02FF Page 2 (256 bytes) — actor tables, IRQ flag bytes
628
+ $0300–$03FF Page 3 (256 bytes) — jump vectors; avoid overwriting $0314/$0315
629
+ $0400–$07FF Screen RAM (1000 bytes used; 96 bytes spare)
630
+ $0800–$9FFF ~38 KB — code, data, charset, music, level data
631
+ $A000–$BFFF 8 KB free when BASIC out — level data, extra code
632
+ $C000–$CFFF 4 KB — canonical "game state" block (see below)
633
+ $D000–$DFFF I/O (VIC-II, SID, CIA, Color RAM) — never overwrite without banking
634
+ $E000–$FFFF 8 KB free when KERNAL out — fast data, but lose KERNAL routines
635
+ ```
636
+
637
+ ---
638
+
639
+ ### $C000–$CFFF as the canonical game state block
640
+
641
+ The 4 KB at $C000 is above the typical code-and-data area but below the I/O
642
+ window. It is not shadowed by any ROM. Convention in C64 games:
643
+
644
+ - `$C000–$C0FF`: Current-level descriptor (tileset index, music track,
645
+ enemy spawn table pointer, starting player position).
646
+ - `$C100–$C1FF`: Persistent game state (lives, score BCD, continues,
647
+ unlocked levels, inventory flags).
648
+ - `$C200–$C2FF`: Per-frame scratch (collision result bits, input debounce
649
+ state, IRQ semaphores).
650
+ - `$C300–$CFFF`: Overflow for large actor tables or a secondary map buffer
651
+ when the main buffer at $0800 is insufficient.
652
+
653
+ This layout is a convention, not a hardware requirement. Its value is that
654
+ the MCP tools in this KB assume it when generating code, so briefings and
655
+ recipes stay consistent.
656
+
657
+ ---
658
+
659
+ ### Save state mechanisms
660
+
661
+ The C64 has no battery-backed RAM in stock hardware. Three historical
662
+ approaches:
663
+
664
+ **None.** Most arcade-style games. Progress resets on power-off. The entire
665
+ game fits in one session. Simplest; zero save-system code.
666
+
667
+ **Cassette write.** Use the KERNAL `SAVE` routine (or direct serial/tape
668
+ protocol) to write the $C100–$C1FF persistent state block to cassette.
669
+ Load it back with `LOAD`. Cassette save of 256 bytes takes ~10 seconds on
670
+ a stock 1530 datasette. Acceptable for turn-based or RPG games where the
671
+ player expects a save ritual.
672
+
673
+ **Disk write.** Open a sequential or relative file on the 1541 via the
674
+ KERNAL `OPEN`/`PRINT#`/`CLOSE` sequence and write the persistent state
675
+ block. A 256-byte block writes in under 1 second. Disk save is the standard
676
+ for C64 RPGs and adventure games. Requires the KERNAL to remain available
677
+ (do not page it out) during the save, or rebank it in for the save routine.
678
+ The call sequences, the error-channel check and a measured write-and-read
679
+ round-trip are `kernal_file_write_seq`, `kernal_file_read_seq` and
680
+ `error_channel_check` in `../techniques/file-io.md`.
681
+
682
+ ### Save-file policy: first run, replace, version, missing drive
683
+
684
+ The file calls are the small part. What a game has to decide around
685
+ them is below; every figure is from
686
+ `../recipes/oscar64/high-score-persist.md`, measured in VICE x64sc 3.10
687
+ with its 1541 emulation and a disk formatted by c1541, unless marked.
688
+ The bare call sequences are in `../recipes/oscar64/save-load-seq-file.md`
689
+ and `../recipes/kickassembler/file-io-roundtrip.md` and are not repeated
690
+ here.
691
+
692
+ **Allow for the banner.** A 1541 answers its first status read after
693
+ reset with `73,CBM DOS V2.6 1541,00,00` (measured with a probe that
694
+ reads the channel before any other command). A check that treats any
695
+ non-zero code as failure refuses a healthy drive. The recipe's first
696
+ call is the OPEN of the save file, and the `62` or `00` that raises
697
+ replaces the banner before the channel is read; a game that reads the
698
+ channel first must accept `73` as healthy. The `2.6` in that line is
699
+ the DOS version the replies below come from.
700
+
701
+ **First run.** Try to read the file before writing anything. On a disk
702
+ that has never held it, OPEN for read succeeds (ST `00`), the read
703
+ returns 0 bytes with ST `$42`, and the error channel says `62, FILE NOT
704
+ FOUND,00,00`. That is the first-run signal, not a fault: show the
705
+ default table and write it at once, so the next run finds the file.
706
+
707
+ **Replace by scratch, then write.** OPEN of `NAME,S,W` on a name that
708
+ exists is refused with `63, FILE EXISTS,00,00`, and every byte written
709
+ into that channel afterwards comes back with ST `$80`, the
710
+ device-not-present bit (`KRNIO_NODEVICE` in Oscar64's `kernalio.h`): the
711
+ drive did not acknowledge bytes on a channel it never opened, and the
712
+ disk is unchanged. `@0:NAME,S,W` asks
713
+ DOS 2.6 for save-with-replace, a command with a long-reported
714
+ corruption defect that this KB has not measured (rung 4) and does not
715
+ recommend relying on (`../techniques/file-io.md`,
716
+ `kernal_file_write_seq`). Send `S0:NAME` on the command channel
717
+ instead, read the reply on the same open channel (`01, FILES
718
+ SCRATCHED,01,00`; closing channel 15 and reopening it reads `00, OK`
719
+ instead), then OPEN for write. Scratching a name that is not there
720
+ answers `01, FILES SCRATCHED,00,00` (count `00`) and changes nothing
721
+ (measured in VICE). Either way the close must be allowed to finish: a
722
+ run cut
723
+ before it leaves a splat file (`../pitfalls/kernal-and-io.md`,
724
+ `krnio_save_leaves_splat_file`).
725
+
726
+ **Version byte and a fixed-size record.** Put a magic pair and a
727
+ version byte at the head of the record and keep the record one fixed
728
+ size. On read, accept it only when the byte count, the magic and the
729
+ version all match; otherwise say so on screen, scratch it and write the
730
+ defaults. Run against a 15-byte version-0 file planted with c1541, the
731
+ recipe printed `OLD FORMAT 15 BYTES V0: RESET`, replaced it and read a
732
+ good record back. An old save is then readable or cleanly replaced,
733
+ never half-parsed.
734
+
735
+ **When drive 8 does not answer.** Two cases, and they look different.
736
+ A drive with no disk (VICE `-default` with no `-8`, which still
737
+ emulates a 1541) lets the OPEN for read succeed, returns 0 bytes with
738
+ ST `$42`, and says `74,DRIVE NOT READY,00,00`. Treat any first-read
739
+ code other than `00` or `62` as saving off for the session: show the
740
+ defaults, say that scores will not be saved, keep playing, and do not
741
+ attempt the write. No device on the bus at all (VICE with
742
+ `-drive8type 0`) is caught one call earlier: an OPEN that sends a
743
+ filename or command returns C=1 with ST bit 7 set (`KRNIO_NODEVICE`;
744
+ the KERNAL's code for it is 5, device not present), because nothing
745
+ pulled DATA low in answer to the
746
+ LISTEN (`../hardware/kernal-routines-reference.md`, CHKOUT entry; the
747
+ recipe prints `OPEN 0 ST=80` and `NO DEVICE`). An OPEN of channel 15
748
+ with an empty name cannot see this: the KERNAL sends nothing on the bus
749
+ when the filename length is zero, so it returns success with ST `00`
750
+ whether or not a drive exists (measured: `1 ST=00` with no drive, where
751
+ `I0` on the same channel gave `0 ST=80`). The first CHKIN on such a
752
+ channel then hangs with no timeout (same page, CHKIN entry), and a
753
+ status read through `krnio_gets` starts with CHKIN; an earlier build of
754
+ the recipe did exactly that and sat with its title line alone on screen
755
+ after 24,000,000 cycles. So the OPEN whose result decides whether the
756
+ drive exists must be one that sends bytes, the save file's own OPEN for
757
+ read is the natural one, and no status read may run before it has
758
+ succeeded.
759
+
760
+ **KERNAL banked in.** Every call in the sequence is a KERNAL call and
761
+ the serial code drives CIA2 directly, so the ROM at $E000-$FFFF and
762
+ the I/O area at $D000-$DFFF must both be mapped from the first OPEN to
763
+ the last CLOSE. A game that runs with the KERNAL out banks it back in
764
+ around the save routine and keeps its own interrupts off, or its
765
+ handler reachable through the ROM's $0314 vector, while the ROM is in.
766
+
767
+ ---
768
+
769
+ ## Scoring, lives, and HUD
770
+
771
+ ---
772
+
773
+ ### HUD via raster-IRQ split
774
+
775
+ The HUD (score, lives, timer) is most commonly placed above or below the
776
+ playfield using a raster-IRQ color and charset switch. The playfield
777
+ uses one charset; at the HUD split line the IRQ switches $D018 to point
778
+ to a second charset block where the score digits are custom-drawn,
779
+ restoring the playfield charset on the line where the playfield resumes.
780
+
781
+ Top-of-screen HUD (most common in platformers):
782
+
783
+ ```
784
+ Raster IRQ at line 50 (start of playfield):
785
+ - Restore playfield $D018 charset pointer
786
+ - Restore playfield sprite colors
787
+ - Acknowledge, re-arm for VBlank
788
+
789
+ VBlank: write updated score digits to HUD screen RAM rows ($0400–$04A7)
790
+ before the beam returns
791
+ ```
792
+
793
+ Bottom-of-screen HUD (common in shmups):
794
+
795
+ ```
796
+ Raster IRQ at line 216 (after last playfield row):
797
+ - Switch $D018 to HUD charset
798
+ - Change sprite priority/colors for HUD icons
799
+ - Acknowledge, re-arm for VBlank
800
+ ```
801
+
802
+ Both HUD areas use the same physical screen RAM ($0400–$07E7). The split
803
+ just changes which charset the VIC-II uses to render the character codes in
804
+ those rows. Sprites can cross the split freely; only the character rendering
805
+ changes.
806
+
807
+ ---
808
+
809
+ ### Score update routines
810
+
811
+ Score is stored as BCD (binary-coded decimal) because display requires
812
+ per-digit output and BCD avoids a full binary-to-decimal conversion every
813
+ frame.
814
+
815
+ **Per-digit increment (simple):** Six bytes for a six-digit score. Each
816
+ byte holds two BCD digits (packed: high nibble = tens, low nibble = units).
817
+ Use the 6510 ADC instruction with the D (decimal) flag set (SED before ADC,
818
+ CLD after). Carry propagates automatically through packed BCD.
819
+
820
+ ```asm
821
+ sed ; set decimal mode
822
+ clc
823
+ lda score_lo
824
+ adc points_lo ; add points, BCD carry propagates
825
+ sta score_lo
826
+ lda score_hi
827
+ adc points_hi
828
+ sta score_hi
829
+ cld ; clear decimal mode immediately
830
+ ```
831
+
832
+ **Display:** Each score byte maps to two PETSCII or custom-charset digit
833
+ characters. Extract high nibble with LSR×4, add character-set base offset,
834
+ write to screen RAM. Extract low nibble with AND #$0F, add offset, write.
835
+
836
+ ```asm
837
+ lda score_hi
838
+ lsr
839
+ lsr
840
+ lsr
841
+ lsr ; high nibble
842
+ clc
843
+ adc #'0' ; PETSCII digit base (or custom charset offset)
844
+ sta $0402 ; write to HUD position
845
+ lda score_hi
846
+ and #$0F ; low nibble
847
+ clc
848
+ adc #'0'
849
+ sta $0403
850
+ ```
851
+
852
+ For custom charsets, replace `#'0'` with the base character index of your
853
+ digit glyphs.
854
+
855
+ **Full-number convert (BCD to screen RAM):** If points are awarded in
856
+ binary (e.g. physics-derived values), convert to BCD using the double-dabble
857
+ algorithm before storing. Double-dabble on a 16-bit value costs 875
858
+ cycles in assembly with decimal mode, including the unpack to five screen
859
+ codes (measured in VICE x64sc 3.10 with CIA1 timer A; see "Printing
860
+ numbers" below). An earlier version of this page said ~300 cycles, which
861
+ was not measured. Run it in VBlank or on score-change events, not every
862
+ frame.
863
+
864
+ ---
865
+
866
+ ### Printing numbers
867
+
868
+ A binary value (a timer, a coordinate, a physics-derived score) has to
869
+ become digits on the screen. That is two jobs: converting binary to
870
+ decimal digits, and writing those digits into screen RAM.
871
+
872
+ **Screen codes, no KERNAL.** Digits `0`-`9` are screen codes `$30`-`$39`,
873
+ the same values as their PETSCII codes, so `digit + $30` is right whether
874
+ you think in screen codes or PETSCII. Space is `$20` in both. Letters are
875
+ not the same: `A` is screen code `$01` and PETSCII `$41`, so the hex
876
+ digits `A`-`F` are `$01`-`$06` on screen. Store the byte at
877
+ `$0400 + 40 * row + column` and set colour RAM at `$D800` once at start.
878
+ CHROUT is not needed for a HUD: it costs a cursor, a colour write and a
879
+ scroll check per character.
880
+
881
+ **Subtract-powers.** For each power of ten from 10000 down to 10, count
882
+ how many times it can be subtracted from the value; the count is the
883
+ digit, and what is left after 10 is the units. A 16-bit value needs four
884
+ powers, an 8-bit value two (100 and 10). The cost is proportional to the
885
+ digit sum: 65535 takes 19 subtractions, 59999 takes 32 (the 16-bit worst
886
+ case), 10000 takes one.
887
+
888
+ **Double-dabble.** Shift the value out from the top bit into a BCD
889
+ accumulator, doubling the accumulator each time. Before each doubling,
890
+ any BCD nibble of 5 or more has 3 added so the doubling carries a ten out
891
+ of it. On the 6502 the whole adjust step disappears: in decimal mode
892
+ (`SED`) `adc` of a byte to itself doubles a packed BCD pair correctly.
893
+ Sixteen passes of `asl / rol` on the value and three `adc` on the
894
+ accumulator give five BCD digits in a fixed time, with no tables. C has
895
+ no decimal mode, so a C double-dabble has to test every nibble and is the
896
+ slowest route measured below.
897
+
898
+ **Zero suppression and right alignment.** Print into a field of fixed
899
+ width. Keep a `lead` flag set until the first non-zero digit; while it is
900
+ set, a zero digit writes `$20` instead of `$30`. The last digit always
901
+ prints, so 0 shows as `0`. Because the field is fixed and the leading
902
+ cells are spaces, the value is right-aligned with no second pass. An
903
+ arcade score with leading zeros is the same routine without the `lead`
904
+ test.
905
+
906
+ **Hexadecimal for debugging.** High nibble by four `lsr`, low nibble by
907
+ `and #$0F`, each through a 16-entry screen-code table
908
+ (`$30`-`$39`, `$01`-`$06`). Two cells per byte, 74 cycles from C
909
+ (measured below), and it never lies about the byte the way a decimal
910
+ routine with a bug can.
911
+
912
+ **Measured cost of each route.** VICE x64sc 3.10, CIA1 timer A
913
+ force-loaded from `$FFFF`, display blanked and the timing started at the
914
+ next frame so no badline steals cycles, empty call subtracted: the
915
+ figures are the body of the routine without `JSR` and `RTS` (rung 1).
916
+ PAL and NTSC gave the same figures. The C listings are in
917
+ `recipes/oscar64/print-number.md` and both KickAssembler routines are
918
+ below; the same source assembled in a harness with the timer gave these
919
+ numbers.
920
+
921
+ | Route | Value | Cycles |
922
+ |---|---|---|
923
+ | KickAssembler subtract-powers (listing below) | 65535 | 879 |
924
+ | KickAssembler subtract-powers | 59999, worst case | 1,243 |
925
+ | KickAssembler double-dabble, decimal mode (listing below) | 65535 and 59999 | 875 |
926
+ | Oscar64 C subtract-powers, `fmt_dec_sub` | 65535 | 957 |
927
+ | Oscar64 C subtract-powers, `fmt_dec_sub` | 59999, worst case | 1,361 |
928
+ | Oscar64 C double-dabble, nibble adjust, `fmt_dec_dab` | 65535 | 2,537 |
929
+ | Oscar64 C hex byte, `fmt_hex8` | $FF | 74 |
930
+
931
+ The last few cycles depend on code placement: the same double-dabble
932
+ read 860 in an earlier build where its inner loop did not cross a page,
933
+ and 875 after a 12-byte insertion moved it (fifteen taken branches at
934
+ one extra cycle each, rung 3). A first version of the harness that
935
+ blanked the display and timed at once read 9 per cent higher on NTSC,
936
+ because the VIC-II samples the DEN bit once per frame, on line `$30`
937
+ (VIC-II documentation, not measured here; what was measured is that the
938
+ wait closes the 9 per cent NTSC gap). Both decimal routes were checked
939
+ against Python over
940
+ all 65,536 values (the `PASS` lines in the recipe).
941
+
942
+ **Which to use where.**
943
+
944
+ - Score kept in BCD (the routines above): no conversion at all. Unpack
945
+ nibbles, add `$30`, write. Cheapest by far.
946
+ - 16-bit binary in assembly: double-dabble with `SED` (`dab_u16` below).
947
+ Fixed 875 cycles, no tables, 106 bytes with the unpack (the
948
+ subtract-powers routine below is 90 with its tables; both from the
949
+ assembler's symbol file, rung 1).
950
+ Subtract-powers wins only when
951
+ values are usually small, since its cost falls with the digit sum
952
+ (rung 3, not measured for small values here).
953
+ - 16-bit binary from Oscar64 C: subtract-powers. The C double-dabble is
954
+ 2.6 times slower. If the fixed cost matters, call the assembly
955
+ double-dabble through `__asm`.
956
+ - 8-bit binary: subtract-powers with two powers, or a 256-entry table of
957
+ packed BCD if the 256 bytes are spare (not measured here).
958
+ - Budget: 1,000 cycles is about 16 PAL raster lines (63 cycles per line,
959
+ rung 3). Fine once per frame in VBlank for one or two fields, and fine
960
+ on a score-change event. Do not convert inside a raster IRQ with a
961
+ deadline, and do not convert per actor per frame.
962
+
963
+ **KickAssembler routine.** `num` holds the value and is destroyed; `dst`
964
+ is a zero-page pointer to the first of five cells. The demo below prints
965
+ 65535 at the top left of the screen and returns to BASIC. This is the
966
+ exact text that measured 879 and 1,243 cycles above.
967
+
968
+ ```asm
969
+ // print_u16.asm -- 16-bit binary to five screen codes, right-aligned,
970
+ // leading zeros as spaces, written straight to screen RAM.
971
+ BasicUpstart2(start)
972
+
973
+ .label dst = $fb // zero page pointer to the 5-cell field
974
+ .label num = $fd // 16-bit value, destroyed by the routine
975
+ .label digit = $02
976
+ .label lead = $03
977
+
978
+ * = $0810
979
+ start:
980
+ lda #<65535
981
+ sta num
982
+ lda #>65535
983
+ sta num+1
984
+ lda #<$0400 // top left of the default screen
985
+ sta dst
986
+ lda #>$0400
987
+ sta dst+1
988
+ jsr print_u16
989
+ rts
990
+
991
+ // --- subtract-powers ------------------------------------------------------
992
+ // In: num (16-bit, destroyed), dst -> 5 cells. Out: value right-aligned,
993
+ // leading zeros as spaces. Uses digit, lead, A, X, Y.
994
+ print_u16:
995
+ lda #$01
996
+ sta lead
997
+ ldy #$00
998
+ ldx #$00
999
+ pu_loop:
1000
+ lda #$2f // one below '0': the first pass always increments
1001
+ sta digit
1002
+ sec
1003
+ pu_sub:
1004
+ inc digit
1005
+ lda num
1006
+ sbc pow_lo,x
1007
+ sta num
1008
+ lda num+1
1009
+ sbc pow_hi,x
1010
+ sta num+1
1011
+ bcs pu_sub // still non-negative: subtract again
1012
+ lda num // overshot by one power: add it back (C is clear)
1013
+ adc pow_lo,x
1014
+ sta num
1015
+ lda num+1
1016
+ adc pow_hi,x
1017
+ sta num+1
1018
+ pu_done:
1019
+ lda digit
1020
+ cmp #$30
1021
+ bne pu_emit // a real digit
1022
+ lda lead
1023
+ beq pu_zero // a zero after a real digit
1024
+ lda #$20 // a leading zero: space
1025
+ sta (dst),y
1026
+ bne pu_next // always
1027
+ pu_zero:
1028
+ lda #$30
1029
+ pu_emit:
1030
+ sta (dst),y
1031
+ lda #$00
1032
+ sta lead
1033
+ pu_next:
1034
+ iny
1035
+ inx
1036
+ cpx #$04
1037
+ bne pu_loop
1038
+ lda num // 0..9 remains
1039
+ ora #$30
1040
+ sta (dst),y
1041
+ rts
1042
+
1043
+ pow_lo: .byte <10000, <1000, <100, <10
1044
+ pow_hi: .byte >10000, >1000, >100, >10
1045
+ ```
1046
+
1047
+ **KickAssembler double-dabble.** The same `num` and `dst`, plus three
1048
+ zero-page bytes at `bcd` for the packed accumulator. The demo prints
1049
+ 65535 at the top left and returns to BASIC (run in VICE, rung 1). This
1050
+ is the exact text that measured 875 cycles above, and the `db_loop`
1051
+ whose page crossing moved the figure from 860.
1052
+
1053
+ ```asm
1054
+ // dab_u16.asm -- 16-bit binary to five screen codes by double-dabble in
1055
+ // decimal mode, right-aligned, leading zeros as spaces, written straight
1056
+ // to screen RAM. This is the text that measured 875 cycles.
1057
+ BasicUpstart2(start)
1058
+
1059
+ .label dst = $fb // zero page pointer to the 5-cell field
1060
+ .label num = $fd // 16-bit value, destroyed by the routine
1061
+ .label lead = $03
1062
+ .label bcd = $04 // 3 bytes packed BCD
1063
+
1064
+ * = $0810
1065
+ start:
1066
+ lda #<65535
1067
+ sta num
1068
+ lda #>65535
1069
+ sta num+1
1070
+ lda #<$0400 // top left of the default screen
1071
+ sta dst
1072
+ lda #>$0400
1073
+ sta dst+1
1074
+ jsr dab_u16
1075
+ rts
1076
+
1077
+ // --- double-dabble with decimal mode -------------------------------------
1078
+ // In: num (16-bit, destroyed), dst -> 5 cells. Doubles a 3-byte packed BCD
1079
+ // accumulator in decimal mode 16 times, shifting one bit of num in each
1080
+ // time, then unpacks with the same zero suppression.
1081
+ dab_u16:
1082
+ lda #$00
1083
+ sta bcd
1084
+ sta bcd+1
1085
+ sta bcd+2
1086
+ ldx #$10
1087
+ sed
1088
+ db_loop:
1089
+ asl num
1090
+ rol num+1 // C = next bit, MSB first
1091
+ lda bcd
1092
+ adc bcd
1093
+ sta bcd
1094
+ lda bcd+1
1095
+ adc bcd+1
1096
+ sta bcd+1
1097
+ lda bcd+2
1098
+ adc bcd+2
1099
+ sta bcd+2
1100
+ dex
1101
+ bne db_loop
1102
+ cld
1103
+ ldy #$00
1104
+ lda #$01
1105
+ sta lead
1106
+ lda bcd+2
1107
+ and #$0f
1108
+ jsr db_digit
1109
+ lda bcd+1
1110
+ lsr
1111
+ lsr
1112
+ lsr
1113
+ lsr
1114
+ jsr db_digit
1115
+ lda bcd+1
1116
+ and #$0f
1117
+ jsr db_digit
1118
+ lda bcd
1119
+ lsr
1120
+ lsr
1121
+ lsr
1122
+ lsr
1123
+ jsr db_digit
1124
+ lda bcd
1125
+ and #$0f
1126
+ ora #$30
1127
+ sta (dst),y
1128
+ rts
1129
+ db_digit: // A = 0..9
1130
+ bne db_nz
1131
+ ldx lead
1132
+ beq db_nz // zero after a real digit prints
1133
+ lda #$20
1134
+ sta (dst),y
1135
+ iny
1136
+ rts
1137
+ db_nz:
1138
+ ora #$30
1139
+ sta (dst),y
1140
+ iny
1141
+ lda #$00
1142
+ sta lead
1143
+ rts
1144
+ ```
1145
+
1146
+ **Oscar64 routine.** `fmt_dec_sub` from `recipes/oscar64/print-number.md`,
1147
+ which measured 957 and 1,361 cycles above. The recipe page carries the
1148
+ whole program, the exhaustive check and the timer harness; this is the
1149
+ routine on its own (not built by the gate on this page; it is the text
1150
+ the recipe built).
1151
+
1152
+ ```c
1153
+ // Subtract-powers. Writes five screen codes to dst: the value right-aligned,
1154
+ // leading zeros as spaces, so 0 becomes " 0" and 65535 fills the field.
1155
+ __noinline void fmt_dec_sub(unsigned v, char *dst)
1156
+ {
1157
+ static const unsigned pow10[4] = { 10000, 1000, 100, 10 };
1158
+ char lead = 1;
1159
+ for (char i = 0; i < 4; i++)
1160
+ {
1161
+ unsigned p = pow10[i];
1162
+ char d = 0x30;
1163
+ while (v >= p) { v -= p; d++; }
1164
+ if (d != 0x30 || !lead) { dst[i] = d; lead = 0; }
1165
+ else dst[i] = 0x20;
1166
+ }
1167
+ dst[4] = 0x30 + v; // v is 0..9 here
1168
+ }
1169
+ ```
1170
+
1171
+ Oscar64 -O2 keeps `v` in zero page and the digit count in a zero-page
1172
+ temporary, and the inner loop is a compare-and-subtract of about 32
1173
+ cycles per step (read from the compiler's `.asm` output, rung 1 for the
1174
+ shape, rung 3 for the per-step figure). `__noinline` is documented in the
1175
+ Oscar64 README; without it the compiler may inline the call at -O2.
1176
+
1177
+ ---
1178
+
1179
+ ## Cross-references
1180
+
1181
+ - `../techniques/sprite.md` — sprite multiplex mechanics, hardware sprite
1182
+ DMA, collision register details, sprite-background priority.
1183
+ - `../techniques/scroll.md` — soft-scroll register usage, column copy
1184
+ timing, parallax layer technique.
1185
+ - `./c64-game-archetypes.md` — which archetype (shmup, platformer,
1186
+ adventure, maze, racer) pairs with which patterns from this document,
1187
+ with recommended default pattern combinations per archetype.
1188
+ - `./game-structure.md`: the state layer above these patterns (game state machine, level end conditions, level transition, front end and attract) with per-pattern checks a harness can run.
1189
+ - `./enemy-behaviour-and-difficulty.md`: the design layer above the actor state machines in this document (Öörni's interaction patterns, enemy personality by target, attack pattern tables, the difficulty ramp and a playtest protocol), each with checks a harness can run.