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,1000 @@
1
+ ---
2
+ recipe: simple-shmup
3
+ toolchain: oscar64
4
+ output_format: PRG
5
+ region: both
6
+ techniques: [sprite_multiplex_8, soft_scroll_v, sid_play_routine_pattern]
7
+ file_formats: [PRG]
8
+ uses_registers: [D015, D000, D001, D010, D027, D028, D029, D02A, D02B, D02C, D02D, D02E, D01E, D01F, D011, D016, D012, D019, D01A, D400, D401, D402, D403, D404, D405, D406, D407, D408, D409, D40A, D40B, D40C, D40D, D40E, D40F, D410, D411, D412, D413, D414, D415, D416, D417, D418]
9
+ uses_kernal: []
10
+ scaffolds: [vertical_shmup, horizontal_shmup]
11
+ ---
12
+
13
+ <!-- doc-type: recipe -->
14
+
15
+ # Oscar64 Simple Vertical-Scrolling Shoot-Em-Up
16
+
17
+ ## Synopsis
18
+
19
+ A minimal but complete vertical-scrolling shoot-em-up implemented in Oscar64 C.
20
+ The player controls a ship via joystick port 2, fires bullets upward, and must
21
+ avoid enemies that enter from the top in sine-wave formations. A starfield scrolls
22
+ downward using `$D011` YSCROLL (one-pixel-per-frame soft scroll; an earlier
23
+ version of this sentence said `$D016`, which is XSCROLL). Eight hardware
24
+ sprites are multiplexed via `vspr_*` to display the player, four enemies, and up
25
+ to three active bullets simultaneously. Collisions are software bounding-box
26
+ tests; `$D01E`/`$D01F` are read only to clear them (see "Collision detection"
27
+ below — an earlier version of this sentence said collisions were detected
28
+ through `$D01E`). A background SID stub drives the play-routine pattern once per
29
+ frame from a raster IRQ. The program is fully playable: load in VICE, push joystick
30
+ port 2, and shoot the enemies.
31
+
32
+ This is the hero recipe of Phase 4. It demonstrates the full agent-loop
33
+ capability: an agent reading this recipe and the cross-referenced technique docs
34
+ can reconstruct a working shmup shell from scratch. Cross-references:
35
+ `docs/recipes/oscar64/sprite-multiplex-8.md` for `vspr_*` idioms,
36
+ `docs/recipes/oscar64/sid-music-player.md` for the play-routine pattern,
37
+ `docs/recipes/oscar64/soft-scroll-h.md` for `$D016` XSCROLL mechanics.
38
+
39
+ The `techniques` list names `soft_scroll_v`, the scroll this listing does
40
+ with `$D011` YSCROLL. An earlier version listed `soft_scroll_h` and
41
+ `sprite_collision_detect`; the listing never scrolls with `$D016`, and it
42
+ reads `$D01E` only to clear it, so neither technique is implemented here.
43
+
44
+ ## Source
45
+
46
+ ```c
47
+ // simple-shmup.c
48
+ //
49
+ // Minimal vertical-scrolling shoot-em-up for stock C64 PAL/NTSC.
50
+ //
51
+ // Logical sprite assignments (vspr indices):
52
+ // 0 player ship
53
+ // 1 bullet 0
54
+ // 2 bullet 1
55
+ // 3 bullet 2
56
+ // 4-7 enemies 0-3
57
+ //
58
+ // Hardware sprites: 8 slots multiplexed by vspr_* over two vertical passes.
59
+ //
60
+ // Screen layout:
61
+ // Row 0: HUD (score)
62
+ // Rows 1-24: play field with downward-scrolling starfield
63
+ //
64
+ // Compile:
65
+ // oscar64 -O2 -o=simple-shmup.prg -tf=prg simple-shmup.c
66
+ //
67
+ #include <c64/vic.h>
68
+ #include <c64/sid.h>
69
+ #include <c64/sprites.h>
70
+ #include <c64/rasterirq.h>
71
+ #include <c64/joystick.h>
72
+ #include <c64/memmap.h>
73
+ #include <string.h>
74
+ #include <stdlib.h>
75
+
76
+ // ============================================================================
77
+ // Memory layout
78
+ // ============================================================================
79
+ // Code + data below $2000
80
+ #pragma region( lower, 0x0a00, 0x2000, , , {code, data} )
81
+
82
+ // Sprite data at $2000 (32 x 64-byte blocks = 2048 bytes)
83
+ #pragma section( spriteset, 0 )
84
+ #pragma region( spriteset_region, 0x2000, 0x2800, , , {spriteset} )
85
+
86
+ // Everything else above $2800
87
+ #pragma region( main, 0x2800, 0xa000, , , {code, data, bss, heap, stack} )
88
+
89
+ // ============================================================================
90
+ // Sprite bitmaps
91
+ // ============================================================================
92
+ // Block layout within $2000:
93
+ // Block 0: player ship
94
+ // Block 1: bullet
95
+ // Block 2: enemy
96
+ // Block 3: explosion frame 0
97
+ // Block 4: explosion frame 1
98
+ //
99
+ // Each block is 64 bytes; bytes 0-62 are the 21-row x 3-byte bitmap; byte 63 = 0.
100
+ //
101
+ // These bitmaps are generated at startup rather than embedded as binary because
102
+ // the recipe should compile standalone. A real game would use:
103
+ // static const char spriteset_data[] = { #embed spd_sprites "sprites.spd" };
104
+
105
+ #pragma data(spriteset)
106
+ static char sprite_blocks[2048]; // 32 x 64; only first 5 used
107
+ #pragma data(data)
108
+
109
+ // Draw sprite shapes into sprite_blocks at startup.
110
+ static void sprites_build(void)
111
+ {
112
+ // Block 0: player ship — pointed nose, wide body
113
+ {
114
+ char *b = sprite_blocks;
115
+ static const char ship[21*3] = {
116
+ 0,3,0, 0,7,128, 0,15,192, 0,31,224,
117
+ 0,63,240,0,127,248,1,255,252,3,255,254,
118
+ 7,255,255,3,255,254,1,231,252,0,119,248,
119
+ 0,119,248,0,119,248,0,63,240,0,63,240,
120
+ 0,28,0, 0,28,0, 0,28,0, 0,8,0, 0,0,0
121
+ };
122
+ for (char i = 0; i < 63; i++) b[i] = ship[i];
123
+ b[63] = 0;
124
+ }
125
+
126
+ // Block 1: bullet — narrow vertical line
127
+ {
128
+ char *b = sprite_blocks + 64;
129
+ for (char r = 0; r < 21; r++) {
130
+ b[r*3+0] = 0;
131
+ b[r*3+1] = (r < 7) ? 0x18 : 0;
132
+ b[r*3+2] = 0;
133
+ }
134
+ b[63] = 0;
135
+ }
136
+
137
+ // Block 2: enemy — diamond shape
138
+ {
139
+ char *b = sprite_blocks + 128;
140
+ static const char enemy[21*3] = {
141
+ 0,0,0, 0,24,0, 0,60,0, 0,126,0,
142
+ 0,255,0,1,255,128,3,255,192,7,255,224,
143
+ 15,255,240,7,255,224,3,255,192,1,255,128,
144
+ 0,255,0,0,126,0,0,60,0,0,24,0,
145
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
146
+ };
147
+ for (char i = 0; i < 63; i++) b[i] = enemy[i];
148
+ b[63] = 0;
149
+ }
150
+
151
+ // Block 3: explosion frame 0 — scattered dots
152
+ {
153
+ char *b = sprite_blocks + 192;
154
+ for (char r = 0; r < 21; r++) {
155
+ b[r*3+0] = (r & 3) == 0 ? 0x55 : 0;
156
+ b[r*3+1] = (r & 3) == 1 ? 0xAA : 0;
157
+ b[r*3+2] = (r & 3) == 2 ? 0x55 : 0;
158
+ }
159
+ b[63] = 0;
160
+ }
161
+
162
+ // Block 4: explosion frame 1 — expanding ring
163
+ {
164
+ char *b = sprite_blocks + 256;
165
+ for (char r = 0; r < 21; r++) {
166
+ char v = (r == 3 || r == 17) ? 0xFF : (r == 10) ? 0x81 : 0;
167
+ b[r*3+0] = 0;
168
+ b[r*3+1] = v;
169
+ b[r*3+2] = 0;
170
+ }
171
+ b[63] = 0;
172
+ }
173
+ }
174
+
175
+ // ============================================================================
176
+ // Screen and color RAM
177
+ // ============================================================================
178
+ #define Screen ((byte *)0x0400)
179
+ #define Color ((byte *)0xd800)
180
+ #define COLS 40
181
+ #define ROWS 25
182
+
183
+ // Sprite data blocks start at $2000; block number = address / 64
184
+ #define SPRITE_BASE_BLOCK ((unsigned)sprite_blocks / 64) // = $2000/64 = 128
185
+
186
+ // ============================================================================
187
+ // Starfield
188
+ // ============================================================================
189
+ // Stars are single space-inverted characters (block character) placed on the
190
+ // char layer. Each star has an (x, row) position. Every 8 frames the YSCROLL
191
+ // wraps and the star rows shift down one character; every frame the YSCROLL
192
+ // pixel offset moves the whole field downward by 1 px.
193
+ //
194
+ // This is the soft_scroll_h pattern adapted for vertical scrolling:
195
+ // $D011 bits 2-0 = YSCROLL (0-7): vertical fine scroll.
196
+ // Decrement each frame for downward scroll; at 0 -> carry the char layer.
197
+
198
+ #define NUM_STARS 16
199
+
200
+ static char star_x[NUM_STARS];
201
+ static char star_row[NUM_STARS];
202
+ static char yscroll; // current YSCROLL value, 0-7
203
+
204
+ // Star character: solid block (screen code $A0 = filled block in PETSCII)
205
+ #define STAR_CHAR 0xA0
206
+ #define SPACE_CHAR 0x20
207
+
208
+ // Initialize star positions.
209
+ static void stars_init(void)
210
+ {
211
+ yscroll = 7;
212
+ for (char i = 0; i < NUM_STARS; i++) {
213
+ star_x[i] = (char)(rand() % 40);
214
+ star_row[i] = (char)(1 + rand() % 24); // rows 1-24 (row 0 = HUD)
215
+ Screen[star_row[i] * COLS + star_x[i]] = STAR_CHAR;
216
+ Color [star_row[i] * COLS + star_x[i]] = VCOL_WHITE;
217
+ }
218
+ }
219
+
220
+ // Called once per frame. Soft-scrolls the star layer downward.
221
+ static void stars_update(void)
222
+ {
223
+ if (yscroll > 0) {
224
+ yscroll--;
225
+ } else {
226
+ // Pixel wrap: shift all star rows down one character.
227
+ yscroll = 7;
228
+ for (char i = 0; i < NUM_STARS; i++) {
229
+ // Erase from current position
230
+ Screen[star_row[i] * COLS + star_x[i]] = SPACE_CHAR;
231
+ // Advance row (wrap at bottom back to row 1, skipping HUD row 0)
232
+ star_row[i]++;
233
+ if (star_row[i] >= ROWS)
234
+ star_row[i] = 1;
235
+ // Draw at new position (only if not occupied by a sprite cell)
236
+ Screen[star_row[i] * COLS + star_x[i]] = STAR_CHAR;
237
+ Color [star_row[i] * COLS + star_x[i]] = VCOL_WHITE;
238
+ }
239
+ }
240
+ // Write YSCROLL into $D011, preserving DEN, BMM, ECM and RSEL but NOT
241
+ // bit 7: on a read, bit 7 is the CURRENT raster line's MSB, and on a
242
+ // write it is bit 8 of the raster-compare value. This runs right after
243
+ // rirq_wait(), i.e. with the beam past line 255, so a read-modify-write
244
+ // that kept bit 7 (mask 0xF8, as an earlier version had) moved the
245
+ // compare to line 256+ and the raster IRQ never fired again: the game
246
+ // froze after exactly one frame. Mask with 0x78.
247
+ // YSCROLL is bits 2-0; default KERNAL value is 3 (center).
248
+ vic.ctrl1 = (vic.ctrl1 & 0x78) | yscroll;
249
+ }
250
+
251
+ // ============================================================================
252
+ // Game objects: player, bullets, enemies
253
+ // ============================================================================
254
+
255
+ // --- Player ---
256
+ static int player_x; // screen pixels, range 24-319 (sprite X hardware)
257
+ static char player_y; // screen pixels, range 50-250 (sprite Y hardware)
258
+ static bool player_alive;
259
+ static char player_explode_timer;
260
+
261
+ #define PLAYER_INITIAL_X 160
262
+ #define PLAYER_INITIAL_Y 220
263
+ #define PLAYER_SPEED 2
264
+ #define PLAYER_SPRITE 0 // vspr index
265
+
266
+ // --- Bullets ---
267
+ #define MAX_BULLETS 3
268
+ #define BULLET_SPEED 3
269
+
270
+ struct Bullet {
271
+ int x;
272
+ char y;
273
+ bool active;
274
+ };
275
+
276
+ static struct Bullet bullets[MAX_BULLETS];
277
+ static char fire_cooldown;
278
+
279
+ // --- Enemies ---
280
+ #define MAX_ENEMIES 4
281
+ #define ENEMY_SPEED 1
282
+
283
+ struct Enemy {
284
+ int x;
285
+ char y;
286
+ bool active;
287
+ char explode_timer;
288
+ };
289
+
290
+ static struct Enemy enemies[MAX_ENEMIES];
291
+ static char enemy_wave_timer;
292
+ static char enemy_sine_phase;
293
+
294
+ // Sine table: 64 entries, range -32..+32 (amplitude of enemy X oscillation)
295
+ static char sine_tab[64];
296
+
297
+ // --- Score (displayed on HUD row 0) ---
298
+ static char score_digits[6]; // BCD-ish: each is 0-9, leftmost = most significant
299
+
300
+ static void score_add(char val)
301
+ {
302
+ // Add val to the 6-digit BCD score (rightmost digit first)
303
+ char carry = val;
304
+ for (char d = 5; d != 255 && carry; d--) {
305
+ char n = score_digits[d] + carry;
306
+ score_digits[d] = n % 10;
307
+ carry = n / 10;
308
+ }
309
+ }
310
+
311
+ static void hud_draw(void)
312
+ {
313
+ // Row 0: "SCORE: XXXXXX"
314
+ // Screen-code note: ':' is 58 (NOT 26 — that's the letter 'Z').
315
+ // Letters A-Z map to 1-26 but punctuation lives in the 32-63 range
316
+ // matching ASCII positions.
317
+ static const char hud_label[] = {
318
+ // S C O R E : spc
319
+ 19, 3, 15, 18, 5, 58, 32
320
+ };
321
+ for (char i = 0; i < 7; i++) {
322
+ Screen[i] = hud_label[i];
323
+ Color[i] = VCOL_YELLOW;
324
+ }
325
+ for (char d = 0; d < 6; d++) {
326
+ Screen[7 + d] = score_digits[d] + 48; // screen code for '0'..'9'
327
+ Color [7 + d] = VCOL_WHITE;
328
+ }
329
+ // Fill rest of HUD row
330
+ for (char i = 13; i < 40; i++) {
331
+ Screen[i] = SPACE_CHAR;
332
+ Color[i] = VCOL_BLACK;
333
+ }
334
+ }
335
+
336
+ // ============================================================================
337
+ // Game init
338
+ // ============================================================================
339
+ static void game_init(void)
340
+ {
341
+ // Clear play field
342
+ for (int i = 40; i < 1000; i++) {
343
+ Screen[i] = SPACE_CHAR;
344
+ Color[i] = VCOL_BLACK;
345
+ }
346
+
347
+ // Player
348
+ player_x = PLAYER_INITIAL_X;
349
+ player_y = PLAYER_INITIAL_Y;
350
+ player_alive = true;
351
+ player_explode_timer = 0;
352
+
353
+ // Bullets
354
+ for (char b = 0; b < MAX_BULLETS; b++)
355
+ bullets[b].active = false;
356
+ fire_cooldown = 0;
357
+
358
+ // Enemies
359
+ for (char e = 0; e < MAX_ENEMIES; e++)
360
+ enemies[e].active = false;
361
+ enemy_wave_timer = 0;
362
+ enemy_sine_phase = 0;
363
+
364
+ // Score
365
+ for (char d = 0; d < 6; d++)
366
+ score_digits[d] = 0;
367
+
368
+ // Build sine table for enemy X oscillation
369
+ // Use a simple integer sine approximation: half-wave quadrant fold
370
+ for (char i = 0; i < 64; i++) {
371
+ // Maps i to an approximate sine in range -32..+32
372
+ // Quarter-wave: 0..15 -> 0..32 (linear ramp as an approximation)
373
+ char q = i & 15;
374
+ char half = (i & 32) ? 1 : 0;
375
+ char asc = (i & 16) ? (15 - q) : q;
376
+ char amp = (char)(asc * 2); // 0..30
377
+ sine_tab[i] = half ? -(sbyte)amp : (sbyte)amp;
378
+ }
379
+ }
380
+
381
+ // ============================================================================
382
+ // Update: player
383
+ // ============================================================================
384
+ static void update_player(void)
385
+ {
386
+ if (!player_alive) {
387
+ if (player_explode_timer) {
388
+ player_explode_timer--;
389
+ // Animate explosion: alternate between explosion frames
390
+ char blk = SPRITE_BASE_BLOCK + 3 + (player_explode_timer & 1);
391
+ vspr_image(PLAYER_SPRITE, blk);
392
+ if (!player_explode_timer) {
393
+ vspr_hide(PLAYER_SPRITE);
394
+ // Restart after a short delay (handled outside this function
395
+ // by checking player_alive and respawning after some frames)
396
+ }
397
+ }
398
+ return;
399
+ }
400
+
401
+ // Read joystick port 2. Oscar64's joy_poll(n) reads $DC00+n, and $DC00
402
+ // (CIA1 port A) is joystick PORT 2; $DC01 is port 1. So joy_poll(0) is
403
+ // port 2, the usual player-1 port. An earlier version of this listing
404
+ // called joy_poll(1) here and read port 1 while claiming port 2.
405
+ joy_poll(0);
406
+
407
+ // Move player horizontally: clamp to sprite display range (24..311 for X)
408
+ player_x += (int)(joyx[0]) * PLAYER_SPEED;
409
+ if (player_x < 24) player_x = 24;
410
+ if (player_x > 311) player_x = 311;
411
+
412
+ // Move player vertically: clamp to rows 1-24 (Y 58..234 roughly)
413
+ player_y += (char)(joyy[0]) * (char)PLAYER_SPEED;
414
+ if (player_y < 58) player_y = 58;
415
+ if (player_y > 234) player_y = 234;
416
+
417
+ vspr_move(PLAYER_SPRITE, player_x, (int)player_y);
418
+
419
+ // Fire bullet on button press, with cooldown
420
+ if (fire_cooldown)
421
+ fire_cooldown--;
422
+ else if (joyb[0]) {
423
+ for (char b = 0; b < MAX_BULLETS; b++) {
424
+ if (!bullets[b].active) {
425
+ bullets[b].x = player_x;
426
+ bullets[b].y = player_y - 22; // spawn above ship
427
+ bullets[b].active = true;
428
+ vspr_set(b + 1, player_x, (int)bullets[b].y,
429
+ SPRITE_BASE_BLOCK + 1, VCOL_LT_GREEN);
430
+ // Trigger a short fire sound on SID voice 2
431
+ sid.voices[1].freq = SID_FREQ_PAL(1200);
432
+ sid.voices[1].attdec = SID_ATK_2 | SID_DKY_6;
433
+ sid.voices[1].susrel = 0;
434
+ sid.voices[1].ctrl = SID_CTRL_NOISE | SID_CTRL_GATE;
435
+ fire_cooldown = 8;
436
+ break;
437
+ }
438
+ }
439
+ }
440
+ }
441
+
442
+ // ============================================================================
443
+ // Update: bullets
444
+ // ============================================================================
445
+ static void update_bullets(void)
446
+ {
447
+ for (char b = 0; b < MAX_BULLETS; b++) {
448
+ if (!bullets[b].active)
449
+ continue;
450
+
451
+ bullets[b].y -= BULLET_SPEED;
452
+
453
+ // Deactivate when above top of play area (above raster line ~58)
454
+ if (bullets[b].y < 50) {
455
+ bullets[b].active = false;
456
+ vspr_hide(b + 1);
457
+ } else {
458
+ vspr_move(b + 1, bullets[b].x, (int)bullets[b].y);
459
+ }
460
+ }
461
+ }
462
+
463
+ // ============================================================================
464
+ // Update: enemies
465
+ // ============================================================================
466
+ static void update_enemies(void)
467
+ {
468
+ // Spawn a new wave every 120 frames if fewer than MAX_ENEMIES are active
469
+ if (enemy_wave_timer) {
470
+ enemy_wave_timer--;
471
+ } else {
472
+ // Count active enemies
473
+ char active = 0;
474
+ for (char e = 0; e < MAX_ENEMIES; e++)
475
+ if (enemies[e].active) active++;
476
+
477
+ if (active == 0) {
478
+ // Spawn all 4 enemies in a horizontal formation at the top
479
+ for (char e = 0; e < MAX_ENEMIES; e++) {
480
+ enemies[e].x = 60 + (int)e * 64;
481
+ enemies[e].y = 55; // just below top border
482
+ enemies[e].active = true;
483
+ enemies[e].explode_timer = 0;
484
+ vspr_set(4 + e, enemies[e].x, (int)enemies[e].y,
485
+ SPRITE_BASE_BLOCK + 2, VCOL_RED);
486
+ }
487
+ enemy_wave_timer = 120;
488
+ enemy_sine_phase = 0;
489
+ }
490
+ }
491
+
492
+ // Advance sine phase
493
+ enemy_sine_phase = (enemy_sine_phase + 1) & 63;
494
+
495
+ for (char e = 0; e < MAX_ENEMIES; e++) {
496
+ if (!enemies[e].active)
497
+ continue;
498
+
499
+ if (enemies[e].explode_timer) {
500
+ enemies[e].explode_timer--;
501
+ char blk = SPRITE_BASE_BLOCK + 3 + (enemies[e].explode_timer & 1);
502
+ vspr_image(4 + e, blk);
503
+ vspr_color(4 + e, VCOL_YELLOW);
504
+ if (!enemies[e].explode_timer) {
505
+ enemies[e].active = false;
506
+ vspr_hide(4 + e);
507
+ }
508
+ continue;
509
+ }
510
+
511
+ // Descend and oscillate horizontally
512
+ enemies[e].y += ENEMY_SPEED;
513
+
514
+ // Sine offset per enemy: stagger phase by 16 per enemy
515
+ char phase = (enemy_sine_phase + (char)(e << 4)) & 63;
516
+ enemies[e].x = 60 + (int)e * 64 + (int)(sbyte)sine_tab[phase] * 2;
517
+
518
+ // Clamp to display area
519
+ if (enemies[e].x < 24) enemies[e].x = 24;
520
+ if (enemies[e].x > 311) enemies[e].x = 311;
521
+
522
+ // Remove enemy when it exits the bottom
523
+ if (enemies[e].y > 250) {
524
+ enemies[e].active = false;
525
+ vspr_hide(4 + e);
526
+ } else {
527
+ vspr_move(4 + e, enemies[e].x, (int)enemies[e].y);
528
+ }
529
+ }
530
+ }
531
+
532
+ // ============================================================================
533
+ // Collision detection via $D01E and $D01F
534
+ // ============================================================================
535
+ // Sprite index layout:
536
+ // vspr 0 = player -> hardware sprite 0 (after vspr_sort by Y)
537
+ // vspr 1-3 = bullets 0-2
538
+ // vspr 4-7 = enemies 0-3
539
+ //
540
+ // After vspr_sort and vspr_update, the vspr system maps logical sprites to
541
+ // hardware slots by ascending Y order. $D01E bits report which HARDWARE
542
+ // sprite slots collided -- we cannot directly match vspr index to hardware
543
+ // slot after sorting.
544
+ //
545
+ // Simpler approach for a shell game: use bounding-box collision in software.
546
+ // $D01E is still read and cleared each frame to prevent stale bits.
547
+ // For bullet-enemy we do AABB (axis-aligned bounding box) checks.
548
+ // For player-enemy we read $D01F (sprite-background) as a proxy indicator
549
+ // and also do AABB.
550
+ //
551
+ // Bounding box half-sizes (in screen pixels):
552
+ #define BULLET_HW 2
553
+ #define BULLET_HH 6
554
+ #define ENEMY_HW 10
555
+ #define ENEMY_HH 10
556
+ #define PLAYER_HW 11
557
+ #define PLAYER_HH 10
558
+
559
+ static void check_collisions(void)
560
+ {
561
+ // Read and clear collision registers (read-to-clear).
562
+ // We don't use the raw bits here but must clear them each frame.
563
+ // (GCC's __attribute__((unused)) is not Oscar64 C; the casts to void
564
+ // keep the reads and say the values are deliberately dropped. The field
565
+ // names are Oscar64's: spr_sprcol is $D01E, spr_backcol is $D01F.)
566
+ byte sps = vic.spr_sprcol;
567
+ byte spb = vic.spr_backcol;
568
+ (void)sps;
569
+ (void)spb;
570
+
571
+ if (!player_alive)
572
+ return;
573
+
574
+ for (char e = 0; e < MAX_ENEMIES; e++) {
575
+ if (!enemies[e].active || enemies[e].explode_timer)
576
+ continue;
577
+
578
+ // --- Bullet vs enemy AABB ---
579
+ for (char b = 0; b < MAX_BULLETS; b++) {
580
+ if (!bullets[b].active)
581
+ continue;
582
+
583
+ int dx = bullets[b].x - enemies[e].x;
584
+ char dy;
585
+ if (bullets[b].y > enemies[e].y)
586
+ dy = bullets[b].y - enemies[e].y;
587
+ else
588
+ dy = enemies[e].y - bullets[b].y;
589
+
590
+ if (dx < 0) dx = -dx;
591
+
592
+ // Compare dx as an int: |dx| can reach 287, and a (char) cast
593
+ // here (as an earlier version had) would fold 256..277 down to a
594
+ // false hit across the screen.
595
+ if (dx <= (BULLET_HW + ENEMY_HW) && dy <= (BULLET_HH + ENEMY_HH)) {
596
+ // Hit!
597
+ bullets[b].active = false;
598
+ vspr_hide(b + 1);
599
+
600
+ enemies[e].explode_timer = 12;
601
+
602
+ // Score
603
+ score_add(10);
604
+ hud_draw();
605
+
606
+ // Explosion sound on SID voice 2
607
+ sid.voices[1].freq = SID_FREQ_PAL(220);
608
+ sid.voices[1].attdec = SID_ATK_2 | SID_DKY_6;
609
+ sid.voices[1].susrel = (char)(0xF0 | SID_DKY_300);
610
+ sid.voices[1].ctrl = SID_CTRL_NOISE | SID_CTRL_GATE;
611
+ }
612
+ }
613
+
614
+ // --- Player vs enemy AABB ---
615
+ if (!enemies[e].explode_timer) {
616
+ int dx = player_x - enemies[e].x;
617
+ char dy;
618
+ if (player_y > enemies[e].y)
619
+ dy = player_y - enemies[e].y;
620
+ else
621
+ dy = enemies[e].y - player_y;
622
+
623
+ if (dx < 0) dx = -dx;
624
+
625
+ if (dx <= (PLAYER_HW + ENEMY_HW) && dy <= (PLAYER_HH + ENEMY_HH)) {
626
+ // Player hit!
627
+ player_alive = false;
628
+ player_explode_timer = 24;
629
+ enemies[e].explode_timer = 12;
630
+
631
+ vspr_image(PLAYER_SPRITE, SPRITE_BASE_BLOCK + 3);
632
+ vspr_color(PLAYER_SPRITE, VCOL_ORANGE);
633
+
634
+ sid.voices[1].freq = SID_FREQ_PAL(110);
635
+ sid.voices[1].attdec = SID_ATK_8 | SID_DKY_114;
636
+ sid.voices[1].susrel = (char)(0xF0 | SID_DKY_1500);
637
+ sid.voices[1].ctrl = SID_CTRL_NOISE | SID_CTRL_GATE;
638
+ }
639
+ }
640
+ }
641
+ }
642
+
643
+ // ============================================================================
644
+ // SID background music stub (play-routine pattern)
645
+ // ============================================================================
646
+ // A real game would embed a SID tune binary and call its play entry here.
647
+ // This stub plays a simple arpeggiated chord on voice 0 to demonstrate the
648
+ // per-frame play-routine pattern (sid_play_routine_pattern technique).
649
+ //
650
+ // The arpeggio cycles through three notes at 8 Hz (every 6 PAL frames).
651
+ static char music_tick = 0;
652
+ static char music_note = 0;
653
+ static const word music_arp[3] = {
654
+ NOTE_C(3), // C3
655
+ NOTE_E(3), // E3
656
+ NOTE_G(3) // G3
657
+ };
658
+
659
+ // Called once per frame from the raster IRQ (via rirq_call) or from
660
+ // main loop after rirq_wait(). Drives voice 0 arpeggio.
661
+ static void music_play(void)
662
+ {
663
+ music_tick++;
664
+ if (music_tick >= 6) {
665
+ music_tick = 0;
666
+ music_note = (music_note + 1) % 3;
667
+ sid.voices[0].freq = music_arp[music_note];
668
+ sid.voices[0].attdec = SID_ATK_2 | SID_DKY_24;
669
+ sid.voices[0].susrel = (char)(0x80 | SID_DKY_300);
670
+ sid.voices[0].ctrl = SID_CTRL_TRI | SID_CTRL_GATE;
671
+ }
672
+ }
673
+
674
+ // Raster IRQ code block for the music play call
675
+ RIRQCode music_rirq;
676
+
677
+ // ============================================================================
678
+ // Respawn
679
+ // ============================================================================
680
+ static char respawn_timer = 0;
681
+
682
+ static void check_respawn(void)
683
+ {
684
+ if (!player_alive && !player_explode_timer) {
685
+ if (respawn_timer < 90) {
686
+ respawn_timer++;
687
+ } else {
688
+ respawn_timer = 0;
689
+ player_x = PLAYER_INITIAL_X;
690
+ player_y = PLAYER_INITIAL_Y;
691
+ player_alive = true;
692
+ vspr_set(PLAYER_SPRITE, player_x, (int)player_y,
693
+ SPRITE_BASE_BLOCK + 0, VCOL_LT_BLUE);
694
+ }
695
+ }
696
+ }
697
+
698
+ // ============================================================================
699
+ // main
700
+ // ============================================================================
701
+ int main(void)
702
+ {
703
+ mmap_trampoline();
704
+ mmap_set(MMAP_NO_BASIC);
705
+
706
+ // --- Build sprite bitmaps ---
707
+ sprites_build();
708
+
709
+ // --- Screen setup ---
710
+ vic.color_border = VCOL_BLACK;
711
+ vic.color_back = VCOL_BLACK;
712
+ for (int i = 0; i < 1000; i++) {
713
+ Screen[i] = SPACE_CHAR;
714
+ Color[i] = VCOL_BLACK;
715
+ }
716
+
717
+ // --- Initialize game state ---
718
+ game_init();
719
+ stars_init();
720
+ hud_draw();
721
+
722
+ // --- Initialize raster IRQ tables and the vspr multiplexer ---
723
+ // rirq_init MUST come first: vspr_init does not call it (sprites.c),
724
+ // and without it rirq_wait() never returns because the IRQ never reaches
725
+ // the rasterirq dispatcher. An earlier version of this listing omitted
726
+ // it and froze on the first rirq_wait() -- the screenshot never moved.
727
+ // true = KERNAL IRQ vector at $0314; KERNAL is still mapped under
728
+ // MMAP_NO_BASIC.
729
+ rirq_init(true);
730
+
731
+ // vspr_init reserves rirq slots 0..(VSPRITES_MAX-8) -- with the default
732
+ // VSPRITES_MAX=16 that is slots 0-7 (one reuse IRQ per sprite of the
733
+ // second pass, rows 80+4*i) plus slot 8 (a sync IRQ at row 250).
734
+ // vspr_update() re-arms or clears slots 0-7 EVERY frame.
735
+ vspr_init(Screen);
736
+
737
+ // Assign all 8 logical sprites initial positions (off-screen or visible)
738
+ vspr_set(PLAYER_SPRITE, player_x, (int)player_y,
739
+ SPRITE_BASE_BLOCK + 0, VCOL_LT_BLUE);
740
+ for (char b = 0; b < MAX_BULLETS; b++)
741
+ vspr_hide(b + 1);
742
+ for (char e = 0; e < MAX_ENEMIES; e++)
743
+ vspr_hide(4 + e);
744
+
745
+ vspr_sort();
746
+ vspr_update();
747
+
748
+ // --- Raster IRQ for music (first slot after the vspr slots) ---
749
+ // vspr_init owns slots 0..8 with the default VSPRITES_MAX=16, so the
750
+ // music IRQ goes in slot 9 (NUM_IRQS is 16). An earlier version used
751
+ // slot 2, which is one of the multiplexer's reuse slots: vspr_update()
752
+ // calls rirq_clear() on every unused reuse slot each frame, so the music
753
+ // call was silently cleared after the first frame.
754
+ rirq_build(&music_rirq, 1);
755
+ rirq_call(&music_rirq, 0, (void *)music_play);
756
+ // Note rasterirq programs row-1 into $D012, so row 0 is compare 255,
757
+ // i.e. the call physically lands at line 255, after the vspr sync IRQ
758
+ // at 250, not at the top of the frame.
759
+ rirq_set(9, 0, &music_rirq);
760
+
761
+ rirq_sort();
762
+ rirq_start();
763
+
764
+ // --- SID initialization ---
765
+ for (char r = 0; r < 25; r++)
766
+ ((volatile byte *)0xd400)[r] = 0;
767
+ sid.fmodevol = 15; // master volume 15
768
+
769
+ // Seed voice 0 for the music arpeggio
770
+ sid.voices[0].pwm = 0x0800;
771
+
772
+ // Voice 1 (SFX) initial state
773
+ sid.voices[1].attdec = SID_ATK_2 | SID_DKY_6;
774
+ sid.voices[1].susrel = 0;
775
+
776
+ // ===================================================================
777
+ // Main loop
778
+ // ===================================================================
779
+ for (;;)
780
+ {
781
+ // 1. Wait for all raster IRQs of this frame to finish.
782
+ // This is the safe point to update vspr arrays: the sprite
783
+ // reuse IRQ has fired and will not read vspr data again until
784
+ // vspr_update reprograms it for the next frame.
785
+ rirq_wait();
786
+
787
+ // 2. Update game logic
788
+ update_player();
789
+ update_bullets();
790
+ update_enemies();
791
+ check_collisions();
792
+ check_respawn();
793
+
794
+ // 3. Stars (soft vertical scroll via $D011 YSCROLL)
795
+ stars_update();
796
+
797
+ // 4. Sprite multiplexer: sort -> update -> re-sort rirq slots
798
+ vspr_sort();
799
+ vspr_update();
800
+ rirq_sort();
801
+
802
+ // (music_play runs automatically from the music_rirq slot)
803
+ }
804
+
805
+ return 0;
806
+ }
807
+ ```
808
+
809
+ ## Build
810
+
811
+ ```bash
812
+ oscar64 -O2 -o=simple-shmup.prg -tf=prg simple-shmup.c
813
+ ```
814
+
815
+ Outputs: `simple-shmup.prg`, `.map`, `.asm`, `.lbl`.
816
+
817
+ Load in VICE: `LOAD"SIMPLE-SHMUP",8,1` then `RUN`, or use `-autostart
818
+ simple-shmup.prg`. Joystick port 2. Push to move; fire button to shoot.
819
+
820
+ ## Expected output
821
+
822
+ Black screen with a top HUD row reading `SCORE: 000000` in yellow and white. A
823
+ light-blue player ship sprite sits at the bottom-center (sprite X 160, Y 220).
824
+ Sixteen white stars, each a solid 8x8 block character (`$A0`, not a dot), scroll
825
+ smoothly downward. Press the fire button: up to three light-green bullet sprites
826
+ advance upward and vanish at the top. Four red enemy diamond sprites are
827
+ spawned on the very first frame (the wave timer starts at 0 and no enemy is
828
+ active; an earlier version said "after 120 frames") and descend one pixel per
829
+ loop iteration while oscillating horizontally in a triangle-wave approximation
830
+ of a sine. When a bullet overlaps an enemy, the bullet vanishes, the enemy
831
+ flickers yellow (`VCOL_YELLOW`, not orange as an earlier version said) for 12
832
+ frames, and the score increments by 10. If an enemy reaches the player sprite,
833
+ both explode (the player in orange), and the player respawns after 90 frames. A
834
+ simple C-major-triad arpeggio plays on SID voice 0 throughout (not listened to
835
+ here).
836
+
837
+ Measured in headless VICE (PAL, 8,000,000 cycles, no joystick input): the
838
+ verify screenshot shows the HUD, the player at the bottom centre and four red
839
+ diamonds in a row about two-thirds of the way down the play field (sprite Y
840
+ about 178), with the star
841
+ blocks displaced from their start rows. In an instrumented build the main loop
842
+ had run 123 times while the music call had run about 245 times over the same
843
+ interval; whether the loop runs every second frame or the two counters measure
844
+ different intervals was not settled here.
845
+
846
+ ## Why this works
847
+
848
+ ### Game loop structure
849
+
850
+ The main loop follows the Oscar64 pattern shown in the `sprites.h` header
851
+ comment and in `samples/sprites/multiplexer.c` and `sprmux32.c` (an earlier
852
+ version pointed at `samples/games/breakout.c` and `hscrollshmup.c`, which do
853
+ not use `vspr_*` or `rirq_wait` at all). Both samples call `rirq_init()`
854
+ before `vspr_init()`; `vspr_init()` does not call it for you:
855
+
856
+ ```
857
+ rirq_wait(); // sync: all IRQs for last frame are done
858
+ update_*(); // advance all game state
859
+ vspr_sort(); // order logical sprites by Y
860
+ vspr_update(); // reprogram hardware sprites + reuse IRQ slots
861
+ rirq_sort(); // re-sort IRQ slot table after vspr_update moved slots
862
+ ```
863
+
864
+ `rirq_wait()` blocks until the last raster IRQ of the previous frame has fired.
865
+ This guarantees that `vspr_update()` is never called while the multiplexer IRQ
866
+ is reading the sprite slot table — a race condition that would corrupt sprite
867
+ positions. Everything between `rirq_wait()` and the bottom of the loop executes
868
+ in the vertical blank or during the active display period (for long games), but
869
+ the critical `vspr_update()` must happen before the raster beam reaches the first
870
+ reuse IRQ trigger line.
871
+
872
+ ### Sprite multiplexer: eight slots for nine objects
873
+
874
+ `vspr_init(Screen)` initializes the `vspr_*` layer and claims raster IRQ slots
875
+ 0 to `VSPRITES_MAX-8` for the multiplexer's internal use: with the default
876
+ `VSPRITES_MAX = 16` that is slots 0-7, one reuse IRQ per second-pass sprite
877
+ (initially rows 80, 84, ... 108), plus slot 8 for a sync IRQ at row 250 (read
878
+ from `sprites.c`; an earlier version of this paragraph said "slots 0 and 1").
879
+ The multiplexer supports up to 16 logical sprites displayed via two passes of 8
880
+ hardware sprites each. This recipe uses 8 logical sprites: one player, three
881
+ bullets, four enemies. Eight logical sprites fit in a single hardware pass, so
882
+ `vspr_update()` finds no ninth sprite and calls `rirq_clear()` on every reuse
883
+ slot each frame; only the sync IRQ fires. Anything you put in slots 0-7 is
884
+ therefore cleared on the next `vspr_update()`, which is why the music slot is 9.
885
+ With a second wave of enemies adding up to eight more sprites, the reuse IRQs
886
+ would automatically activate to handle the second group.
887
+
888
+ The sprite pointer block at `Screen + $3F8` (address `$07F8`) holds one byte per
889
+ hardware slot telling the VIC which 64-byte block of sprite data that slot
890
+ displays. `vspr_update()` writes this block along with the Y positions, X low
891
+ bytes, combined X-MSB byte (`$D010`), and color bytes (`$D027-$D02E`). The
892
+ linker region pragma places `sprite_blocks` at `$2000`; dividing by 64 gives
893
+ block number 128. `vspr_set(sp, x, y, 128+n, color)` assigns logical sprite
894
+ `sp` to display block `128+n`.
895
+
896
+ ### Starfield via `$D011` YSCROLL soft scroll
897
+
898
+ `$D011` bits 2-0 (YSCROLL) shift the entire character display downward by
899
+ 0-7 pixels. Incrementing YSCROLL each frame scrolls the display downward at
900
+ 1 pixel per frame. When YSCROLL wraps from 7 back to 0, the character grid
901
+ snaps back 8 pixels upward; to cancel this visible snap, the character layer is
902
+ shifted down by one row simultaneously. This is the same carry mechanic described
903
+ in `docs/recipes/oscar64/soft-scroll-h.md` applied to the Y axis.
904
+
905
+ The star positions come from the C library's `rand()`, which is fine for a
906
+ demonstration and deterministic from one run to the next. A game wants a
907
+ seeded generator that costs a few cycles a call; that is `lfsr_random` in
908
+ `docs/techniques/maths.md`, with the recipe `lfsr-random.md` beside this one.
909
+
910
+ The star positions are tracked in the `star_row[]` array. Each star is a solid
911
+ block character (screen code `$A0`) drawn directly into the screen RAM. When the
912
+ carry fires (`yscroll` wraps), each star's row is incremented modulo 24 (rows
913
+ 1-24, preserving HUD row 0). The write to `vic.ctrl1 = (vic.ctrl1 & 0x78) |
914
+ yscroll` preserves the DEN bit (display enable), the BMM bit (0 = text mode),
915
+ ECM and RSEL while updating YSCROLL — and deliberately drops bit 7. On a read
916
+ bit 7 is the current raster line's bit 8, on a write it is bit 8 of the raster
917
+ compare (`docs/hardware/vic-ii-reference.md`, `docs/pitfalls/raster-and-badline.md`).
918
+ This write runs just after `rirq_wait()` returns, with the beam past line 255,
919
+ so an earlier version of this listing that masked with `0xF8` and "preserved
920
+ the raster MSB" wrote a 1 back into the compare, moved the raster IRQ to line
921
+ 256+, and froze the game after exactly one frame (measured: the exit screenshot
922
+ was identical at 8 and 12 million cycles). The read-modify-write is a single
923
+ expression thanks to Oscar64's volatile struct field access.
924
+
925
+ The cost per frame is one write to `$D011` plus (on carry frames, once per 8
926
+ frames) `NUM_STARS` screen RAM updates. At 16 stars that is 32 byte writes
927
+ per carry — negligible compared to the sprite update cost.
928
+
929
+ ### Collision detection
930
+
931
+ The recipe uses software AABB (axis-aligned bounding box) collision because it
932
+ is more reliable than reading `$D01E` after `vspr_sort` has remapped logical
933
+ sprites to hardware slots. The hardware `$D01E` register reports which hardware
934
+ slot indices collided; after `vspr_sort`, logical sprite 0 (player) may be
935
+ mapped to hardware slot 3 or 7 depending on Y ordering. Mapping hardware bits
936
+ back to logical sprites requires tracking the sort-order mapping, which
937
+ `vspr_*` does not expose. Instead, `$D01E` and `$D01F` are read and discarded
938
+ each frame purely to reset the latches (preventing stale collision bits from
939
+ persisting across frames). The AABB tests are then performed directly on the
940
+ `player_x/y` and `enemies[e].x/y` coordinates, which are always in the correct
941
+ coordinate space.
942
+
943
+ AABB half-widths and heights are defined as constants matching the visual
944
+ extents of each sprite: the player ship is roughly 22x20 pixels, so `PLAYER_HW
945
+ = 11` and `PLAYER_HH = 10`. Enemy diamonds are roughly 20x20 pixels. Bullet
946
+ lines are thin (`BULLET_HW = 2`, `BULLET_HH = 6`). These values give
947
+ collisions that match the visual overlap to within a few pixels.
948
+
949
+ For a production game: read `$D01E` via a VIC collision IRQ (bit 1 of `$D01A`)
950
+ for sub-frame latency, use the bitmask to quickly narrow which hardware sprite
951
+ pairs are involved, then do an AABB test only for those pairs. This avoids
952
+ testing all O(N*M) pairs every frame.
953
+
954
+ ### Enemy wave and sine oscillation
955
+
956
+ Enemies enter in waves of four when no enemies are active. The sine oscillation
957
+ is approximated with a 64-entry table generated at startup using integer linear
958
+ ramp: each quarter of the table ramps from 0 to 30 and back, giving a triangle
959
+ approximation to a sine with amplitude ±30 pixels. Each enemy is phase-shifted
960
+ by 16 table entries (90 degrees of the 64-entry cycle) so adjacent enemies
961
+ oscillate in a visually separated pattern rather than all moving together.
962
+
963
+ A real game would use an actual `sin()` lookup precomputed at compile time or
964
+ fetched from a table in `docs/techniques/effects-vector-3d.md`. The triangle
965
+ approximation suffices for the recipe's purpose: demonstrating the state machine
966
+ and entry/removal logic clearly.
967
+
968
+ ### Music via `rirq_call` and the play-routine pattern
969
+
970
+ The arpeggio function `music_play()` is installed as a raster IRQ call in slot
971
+ 9 with row 0 using `rirq_call` (rasterirq programs row-1 into `$D012`, so the
972
+ call actually lands at line 255, after the vspr sync IRQ at 250). This is the `sid_play_routine_pattern` technique from
973
+ `docs/techniques/music-sid.md` applied to a hand-written play stub rather than
974
+ an embedded SID binary. Each call advances `music_tick`; every 6 frames it
975
+ writes a new note frequency to SID voice 0 and re-gates the envelope. The call
976
+ runs at the top of every frame in the raster IRQ context, identical to how a
977
+ GoatTracker-generated play routine would be invoked.
978
+
979
+ To replace the stub arpeggio with a real SID tune, add the memory layout and
980
+ function-pointer infrastructure from `docs/recipes/oscar64/sid-music-player.md`,
981
+ point `rirq_call` at the tune's play address, and call the tune's init address
982
+ after `rirq_start()`. The rest of the shmup is unchanged.
983
+
984
+ ### `__striped` alternative for larger games
985
+
986
+ This recipe uses separate struct arrays (`struct Bullet bullets[3]`,
987
+ `struct Enemy enemies[4]`) whose fields are accessed by struct dereference.
988
+ Oscar64's optimizer generates efficient code for small structs (up to 8 bytes)
989
+ because it can hold the base pointer in zero-page and access fields directly.
990
+ For larger arrays (16+ enemies), the idiomatic Oscar64 pattern is `__striped`:
991
+
992
+ ```c
993
+ __striped struct Enemy { int x; char y; bool active; char timer; } enemies[16];
994
+ ```
995
+
996
+ With `__striped`, all `enemies[i].x` values are contiguous in memory, then all
997
+ `enemies[i].y`, etc. The compiler accesses `enemies[i].x` as `LDA x_base, Y`
998
+ without a multiply, cutting the per-element access cost from 4+ cycles to 2.
999
+ The current recipe's 4-enemy array is small enough that the difference is not
1000
+ measurable; `__striped` becomes important when enemy counts exceed 8.