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,787 @@
1
+ ---
2
+ recipe: wave-director
3
+ toolchain: oscar64
4
+ output_format: PRG
5
+ region: both
6
+ techniques: [wave_director, object_pool]
7
+ file_formats: [PRG]
8
+ uses_registers: [DC04, DC05, DC0E, D000, D001, D010, D011, D012, D015, D020, D021, D027]
9
+ uses_kernal: []
10
+ ---
11
+
12
+ <!-- doc-type: recipe -->
13
+
14
+ # Oscar64 wave director: attack waves keyed to scroll position, with path bytecode
15
+
16
+ ## Synopsis
17
+
18
+ A scrolling shooter's attack waves, driven by the level's scroll position
19
+ and not by a frame count. A scroll counter advances one position a frame on
20
+ autopilot, and holds still for 50 frames part-way to show that the waves
21
+ wait for it. A list of six wave records, sorted by trigger position, is read
22
+ through a cursor. Each record names a path, a count, a spawn spacing in
23
+ frames and a formation (start x and x step). Enemies are hardware sprites
24
+ in an eight-slot pool. Each runs one of three paths (dive, weave, swoop)
25
+ written as a small bytecode: MOVE n steps at (dx, dy), FIRE, LOOP, END. An
26
+ enemy leaves when its path ends or it crosses a screen edge; an autopilot
27
+ gun column kills what flies through it, and a wave whose enemies were all
28
+ killed scores a bonus. After the list the level loops, the cursor resets and
29
+ the spawn spacing halves. After 710 frames the program checks every trigger
30
+ against the table and against frames computed in Python, folds the slot
31
+ table into a checksum that the Python model also computes, prints PASS or
32
+ FAIL, and prints the interpreter's cost and a worst frame's, measured with
33
+ CIA1 timer A. It
34
+ implements `wave_director` (`techniques/logic.md`) on top of `object_pool`.
35
+
36
+ ## Source
37
+
38
+ ```c
39
+ // wave-director.c
40
+ // Attack waves keyed to scroll position. A level scroll counter advances
41
+ // one step a frame on autopilot and stops for 50 frames part-way. A wave
42
+ // list sorted by scroll position is read through a cursor; each record
43
+ // names a path, a count, a spawn spacing in frames and a formation. Each
44
+ // enemy is a hardware sprite that runs a small path bytecode: MOVE n
45
+ // steps at (dx, dy), FIRE, LOOP, END. An enemy leaves when its path ends
46
+ // or it crosses a screen edge; an autopilot gun column kills what flies
47
+ // through it, and a wave whose enemies were all killed earns a bonus.
48
+ // After the list the level loops and the spacing halves. The program logs
49
+ // each trigger, checks it against the table and a Python model, and times
50
+ // the path interpreter with CIA1 timer A.
51
+ #include <c64/vic.h>
52
+ #include <c64/cia.h>
53
+
54
+ #define SCREEN ((char *)0x0400)
55
+ #define COLOUR ((char *)0xd800)
56
+ #define SPRDATA ((char *)0x0340) // sprite block 13
57
+ #define SPRPTR ((char *)0x07f8)
58
+
59
+ #define NS 8 // enemy slots, one hardware sprite each
60
+ #define NWAVES 6
61
+ #define LEVEL_LEN 600 // scroll positions per loop of the level
62
+ #define STOP0 250 // the scroll holds still for frames
63
+ #define STOP1 300 // STOP0 to STOP1 - 1
64
+ #define T_END 710 // frames the game runs before the check
65
+ #define Y_ENTRY 30 // spawn line, above the visible screen
66
+ #define GUN_X0 140 // autopilot gun column: sprite X 140-163,
67
+ #define GUN_X1 164 // at sprite Y 150 and below
68
+ #define GUN_Y 150
69
+ #define EXPECT_CHK 0xEB12 // Python fold over the final slot table
70
+
71
+ // Path bytecode. A byte below $80 is MOVE: that many steps, then signed
72
+ // dx and dy per step. The others take no frame.
73
+ #define P_END 0x80 // leave: free the slot
74
+ #define P_LOOP 0x81 // count, target: jump back count-1 times
75
+ #define P_FIRE 0x82 // fire one shot (counted here)
76
+
77
+ static const char path_dive[] = {
78
+ 127, 0, 3,
79
+ P_END };
80
+ static const char path_weave[] = {
81
+ 6, 2, 2, 12, 0xfe, 2, 6, 2, 2, // offset 0: one sway, 24 steps
82
+ P_FIRE, // offset 9
83
+ P_LOOP, 3, 0, // offset 10: three sways
84
+ 127, 0, 3, // then dive out of the bottom
85
+ P_END };
86
+ static const char path_swoop[] = {
87
+ 16, 0, 3, 8, 1, 3, 8, 2, 2, // dive, then bend right
88
+ P_FIRE,
89
+ 8, 3, 1, 8, 3, 0, 127, 3, 0xfe, // level out, climb off the side
90
+ P_END };
91
+ static const char * const paths[3] = { path_dive, path_weave, path_swoop };
92
+ static const char path_colour[3] = { VCOL_YELLOW, VCOL_CYAN, VCOL_LT_RED };
93
+
94
+ // The wave list, sorted by trigger position. Enemy k of a wave spawns at
95
+ // x0 + k * xstep on line Y_ENTRY, one every `spacing` frames.
96
+ static const unsigned wave_pos[NWAVES] = { 40, 130, 220, 310, 400, 490 };
97
+ static const char wave_path[NWAVES] = { 0, 1, 2, 0, 1, 2 };
98
+ static const char wave_count[NWAVES] = { 5, 6, 6, 4, 6, 6 };
99
+ static const char wave_spacing[NWAVES] = { 6, 10, 8, 10, 10, 8 };
100
+ static const char wave_x0[NWAVES] = { 60, 40, 30, 150, 120, 100 };
101
+ static const char wave_xstep[NWAVES] = { 30, 0, 12, 0, 0, 12 };
102
+
103
+ // Expected trigger frames from the Python model: the scroll stop delays
104
+ // waves 3 to 5 by 50 frames, and loop 1 starts again at frame 650.
105
+ static const unsigned expect_tick[7] = { 39, 129, 219, 359, 449, 539, 689 };
106
+
107
+ // Enemy slots, parallel arrays indexed by slot = sprite number.
108
+ char e_live[NS], e_x[NS], e_y[NS], e_path[NS], e_pc[NS];
109
+ char e_n[NS], e_dx[NS], e_dy[NS], e_lc[NS], e_wave[NS];
110
+
111
+ // Director state.
112
+ unsigned scroll, tick;
113
+ char loop, cursor;
114
+ char sp_wave, sp_left, sp_timer; // the one active spawner
115
+ char w_alive[NWAVES], w_kill[NWAVES], w_spawned[NWAVES];
116
+ char refused, bonus, escaped, kills, peak;
117
+ unsigned shots;
118
+
119
+ // Trigger log.
120
+ char log_n;
121
+ char log_wave[8], log_loop[8], log_spawn[8];
122
+ unsigned log_scroll[8], log_tick[8];
123
+
124
+ // ---- the path interpreter ------------------------------------------------
125
+
126
+ // One step of slot s. Returns 0 when the path has ended.
127
+ __noinline char path_step(char s)
128
+ {
129
+ const char *p = paths[e_path[s]];
130
+ while (e_n[s] == 0)
131
+ {
132
+ char pc = e_pc[s];
133
+ char op = p[pc];
134
+ if (op == P_END)
135
+ return 0;
136
+ if (op == P_FIRE)
137
+ {
138
+ shots++;
139
+ e_pc[s] = pc + 1;
140
+ }
141
+ else if (op == P_LOOP)
142
+ {
143
+ if (e_lc[s] == 0)
144
+ e_lc[s] = p[pc + 1];
145
+ if (--e_lc[s] != 0)
146
+ e_pc[s] = p[pc + 2];
147
+ else
148
+ e_pc[s] = pc + 3;
149
+ }
150
+ else
151
+ {
152
+ e_n[s] = op;
153
+ e_dx[s] = p[pc + 1];
154
+ e_dy[s] = p[pc + 2];
155
+ e_pc[s] = pc + 3;
156
+ }
157
+ }
158
+ e_x[s] += e_dx[s];
159
+ e_y[s] += e_dy[s];
160
+ e_n[s]--;
161
+ return 1;
162
+ }
163
+
164
+ // ---- the director --------------------------------------------------------
165
+
166
+ static void spawn(char w, char k)
167
+ {
168
+ for (char s = 0; s < NS; s++)
169
+ {
170
+ if (!e_live[s])
171
+ {
172
+ e_live[s] = 1;
173
+ e_x[s] = wave_x0[w] + k * wave_xstep[w];
174
+ e_y[s] = Y_ENTRY;
175
+ e_path[s] = wave_path[w];
176
+ e_pc[s] = 0;
177
+ e_n[s] = 0;
178
+ e_lc[s] = 0;
179
+ e_wave[s] = w;
180
+ w_alive[w]++;
181
+ w_spawned[w]++;
182
+ log_spawn[log_n - 1]++;
183
+ return;
184
+ }
185
+ }
186
+ refused++;
187
+ }
188
+
189
+ static void gone(char s, char killed)
190
+ {
191
+ char w = e_wave[s];
192
+ e_live[s] = 0;
193
+ w_alive[w]--;
194
+ if (killed)
195
+ {
196
+ w_kill[w]++;
197
+ kills++;
198
+ }
199
+ // The wave is over when none is alive and the spawner has finished it.
200
+ if (w_alive[w] == 0 && w_spawned[w] == wave_count[w] &&
201
+ !(sp_left != 0 && sp_wave == w))
202
+ {
203
+ if (w_kill[w] == wave_count[w])
204
+ bonus++;
205
+ else
206
+ escaped++;
207
+ }
208
+ }
209
+
210
+ // Start every wave whose position the scroll has reached. >= and not ==,
211
+ // so a scroll that moves more than one position a frame cannot skip one.
212
+ __noinline void director(void)
213
+ {
214
+ while (cursor < NWAVES && scroll >= wave_pos[cursor])
215
+ {
216
+ char w = cursor;
217
+ log_wave[log_n] = w;
218
+ log_loop[log_n] = loop;
219
+ log_scroll[log_n] = scroll;
220
+ log_tick[log_n] = tick;
221
+ log_spawn[log_n] = 0;
222
+ log_n++;
223
+ sp_wave = w;
224
+ sp_left = wave_count[w];
225
+ sp_timer = 0;
226
+ w_alive[w] = 0;
227
+ w_kill[w] = 0;
228
+ w_spawned[w] = 0;
229
+ cursor++;
230
+ }
231
+ }
232
+
233
+ static void spawner(void)
234
+ {
235
+ if (sp_left == 0)
236
+ return;
237
+ if (sp_timer == 0)
238
+ {
239
+ char w = sp_wave;
240
+ spawn(w, wave_count[w] - sp_left);
241
+ sp_left--;
242
+ char gap = wave_spacing[w] >> loop; // difficulty: loop 1 halves it
243
+ if (gap == 0)
244
+ gap = 1;
245
+ sp_timer = gap - 1;
246
+ }
247
+ else
248
+ sp_timer--;
249
+ }
250
+
251
+ // Every live enemy: one path step, the edge test, the gun column.
252
+ __noinline void update_enemies(void)
253
+ {
254
+ for (char s = 0; s < NS; s++)
255
+ {
256
+ if (!e_live[s])
257
+ continue;
258
+ if (!path_step(s))
259
+ gone(s, 0);
260
+ else if (e_x[s] >= 250 || e_y[s] >= 250) // off any edge: a byte
261
+ gone(s, 0); // that wrapped reads >= 250
262
+ else if (e_x[s] >= GUN_X0 && e_x[s] < GUN_X1 && e_y[s] >= GUN_Y)
263
+ gone(s, 1);
264
+ }
265
+ }
266
+
267
+ static void game_tick(void)
268
+ {
269
+ if (tick < STOP0 || tick >= STOP1)
270
+ {
271
+ scroll++;
272
+ if (scroll == LEVEL_LEN)
273
+ {
274
+ scroll = 0;
275
+ loop++;
276
+ cursor = 0;
277
+ }
278
+ }
279
+ director();
280
+ spawner();
281
+ update_enemies();
282
+ char n = 0;
283
+ for (char s = 0; s < NS; s++)
284
+ n += e_live[s];
285
+ if (n > peak)
286
+ peak = n;
287
+ tick++;
288
+ }
289
+
290
+ // Sprite X is the 8-bit enemy x, so the playfield is sprite X 0-249 and
291
+ // $D010 stays 0.
292
+ static void draw_sprites(void)
293
+ {
294
+ char en = 0;
295
+ for (char s = 0; s < NS; s++)
296
+ {
297
+ if (e_live[s])
298
+ {
299
+ vic.spr_pos[s].x = e_x[s];
300
+ vic.spr_pos[s].y = e_y[s];
301
+ vic.spr_color[s] = path_colour[e_path[s]];
302
+ en |= 1 << s;
303
+ }
304
+ }
305
+ vic.spr_enable = en;
306
+ }
307
+
308
+ // ---- timing subjects -----------------------------------------------------
309
+
310
+ // Each subject resets slot 0 and runs one step; `reset_only` is the
311
+ // baseline that does the reset without the step.
312
+ __noinline void reset_only(void)
313
+ {
314
+ e_path[0] = 1; e_pc[0] = 9; e_n[0] = 100; e_lc[0] = 2;
315
+ }
316
+ __noinline void step_move(void) // steps left: no fetch
317
+ {
318
+ e_path[0] = 1; e_pc[0] = 9; e_n[0] = 100; e_lc[0] = 2;
319
+ path_step(0);
320
+ }
321
+ __noinline void step_fetch(void) // fetch one MOVE, then step
322
+ {
323
+ e_path[0] = 1; e_pc[0] = 0; e_n[0] = 0; e_lc[0] = 2;
324
+ path_step(0);
325
+ }
326
+ __noinline void step_fire_loop(void) // FIRE, LOOP back, MOVE, step
327
+ {
328
+ e_path[0] = 1; e_pc[0] = 9; e_n[0] = 0; e_lc[0] = 2;
329
+ path_step(0);
330
+ }
331
+ __noinline void nothing(void)
332
+ {
333
+ }
334
+
335
+ // A frame's director work, spawner and path steps (no scroll, no drawing).
336
+ // Positions are reset each call, so nothing leaves or is shot.
337
+ // 8 moving: all eight slots mid-MOVE on the dive path, nothing due.
338
+ __noinline void reset8_only(void)
339
+ {
340
+ for (char s = 0; s < NS; s++)
341
+ {
342
+ e_live[s] = 1; e_path[s] = 0; e_pc[s] = 0; e_n[s] = 0;
343
+ e_x[s] = 100; e_y[s] = 100;
344
+ }
345
+ }
346
+ static void frame_work(void)
347
+ {
348
+ director();
349
+ spawner();
350
+ update_enemies();
351
+ }
352
+ __noinline void frame8_move(void)
353
+ {
354
+ for (char s = 0; s < NS; s++)
355
+ {
356
+ e_live[s] = 1; e_path[s] = 0; e_pc[s] = 0; e_n[s] = 100;
357
+ e_x[s] = 100; e_y[s] = 100;
358
+ }
359
+ frame_work();
360
+ }
361
+ // Worst frame: seven live enemies each on the weave path's FIRE, LOOP
362
+ // back and MOVE fetch, wave 0 due at scroll 40, and its first enemy
363
+ // spawned into the free slot 7, which then fetches its first MOVE.
364
+ __noinline void reset_worst(void)
365
+ {
366
+ for (char s = 0; s < NS; s++)
367
+ {
368
+ e_live[s] = 1; e_path[s] = 1; e_pc[s] = 9; e_n[s] = 0; e_lc[s] = 2;
369
+ e_x[s] = 100; e_y[s] = 100;
370
+ }
371
+ e_live[7] = 0;
372
+ cursor = 0; scroll = 40; loop = 0; log_n = 0;
373
+ }
374
+ __noinline void frame_worst(void)
375
+ {
376
+ for (char s = 0; s < NS; s++)
377
+ {
378
+ e_live[s] = 1; e_path[s] = 1; e_pc[s] = 9; e_n[s] = 0; e_lc[s] = 2;
379
+ e_x[s] = 100; e_y[s] = 100;
380
+ }
381
+ e_live[7] = 0;
382
+ cursor = 0; scroll = 40; loop = 0; log_n = 0;
383
+ frame_work();
384
+ }
385
+
386
+ // CIA1 timer A: force-load $FFFF, count phi2 while fn runs `calls`
387
+ // times, read. The frame subjects run 20 or 10 times so the 16-bit
388
+ // count cannot wrap.
389
+ static unsigned time_loop(void (*fn)(void), char calls)
390
+ {
391
+ cia1.cra = 0x00;
392
+ cia1.ta = 0xffff;
393
+ cia1.cra = 0x11;
394
+ char i = calls;
395
+ do
396
+ {
397
+ fn();
398
+ } while (--i);
399
+ cia1.cra = 0x00;
400
+ return 0xffff - cia1.ta;
401
+ }
402
+
403
+ // ---- screen --------------------------------------------------------------
404
+
405
+ static void put_str(char row, char col, const char *s)
406
+ {
407
+ char *p = SCREEN + 40 * row + col;
408
+ while (*s)
409
+ {
410
+ char c = *s++;
411
+ *p++ = (c >= 'a' && c <= 'z') ? c - 'a' + 1 : c;
412
+ }
413
+ }
414
+
415
+ static void put_dec(char row, char col, unsigned v, char width)
416
+ {
417
+ char *p = SCREEN + 40 * row + col + width;
418
+ do
419
+ {
420
+ *--p = '0' + v % 10;
421
+ v /= 10;
422
+ } while (--width);
423
+ }
424
+
425
+ static const char hex_glyph[16] = {
426
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 1, 2, 3, 4, 5, 6 };
427
+
428
+ static void put_hex16(char row, char col, unsigned v)
429
+ {
430
+ char *p = SCREEN + 40 * row + col;
431
+ p[0] = hex_glyph[(v >> 12) & 15];
432
+ p[1] = hex_glyph[(v >> 8) & 15];
433
+ p[2] = hex_glyph[(v >> 4) & 15];
434
+ p[3] = hex_glyph[v & 15];
435
+ }
436
+
437
+ // Net cycles per call to two decimals: (t - t0) / calls.
438
+ static void put_per_call(char row, char col, unsigned t, unsigned t0, char calls)
439
+ {
440
+ unsigned n = t - t0;
441
+ put_dec(row, col, n / calls, 4);
442
+ SCREEN[40 * row + col + 4] = '.';
443
+ put_dec(row, col + 5, (n % calls) * 100 / calls, 2);
444
+ }
445
+
446
+ static unsigned fold(unsigned cs, unsigned v)
447
+ {
448
+ return (cs ^ v) * 5 + 1;
449
+ }
450
+
451
+ int main(void)
452
+ {
453
+ for (unsigned i = 0; i < 1000; i++)
454
+ {
455
+ SCREEN[i] = 0x20;
456
+ COLOUR[i] = VCOL_WHITE;
457
+ }
458
+ vic.color_back = VCOL_BLACK;
459
+ vic.color_border = VCOL_BLACK;
460
+ for (char k = 0; k < 64; k++) // an 8 by 8 square
461
+ SPRDATA[k] = (k < 24 && k % 3 == 0) ? 0xff : 0x00;
462
+ for (char s = 0; s < NS; s++)
463
+ SPRPTR[s] = 13;
464
+ vic.spr_msbx = 0;
465
+
466
+ put_str(7, 0, "wave director 6 waves 3 paths");
467
+ put_str(8, 0, "w lp pos tick spawn");
468
+
469
+ // The game: one tick a frame, sprites written after the tick.
470
+ char shown = 0;
471
+ while (tick < T_END)
472
+ {
473
+ vic_waitFrame();
474
+ draw_sprites();
475
+ game_tick();
476
+ while (shown < log_n)
477
+ {
478
+ char r = 9 + shown;
479
+ put_dec(r, 0, log_wave[shown], 1);
480
+ put_dec(r, 2, log_loop[shown], 1);
481
+ put_dec(r, 5, log_scroll[shown], 3);
482
+ put_dec(r, 9, log_tick[shown], 4);
483
+ shown++;
484
+ }
485
+ put_str(8, 26, "scroll");
486
+ put_dec(8, 33, scroll, 3);
487
+ }
488
+ draw_sprites();
489
+
490
+ // Checks: every trigger at its table position, on the model's frame,
491
+ // with the table's count spawned.
492
+ bool pass = log_n == 7 && refused == 0;
493
+ for (char i = 0; i < log_n; i++)
494
+ {
495
+ char w = log_wave[i];
496
+ put_dec(9 + i, 16, log_spawn[i], 1);
497
+ if (w != i % NWAVES || log_scroll[i] != wave_pos[w] ||
498
+ log_tick[i] != expect_tick[i] || log_spawn[i] != wave_count[w])
499
+ pass = false;
500
+ }
501
+ put_str(16, 0, "refused peak bonus kills");
502
+ put_dec(16, 8, refused, 1);
503
+ put_dec(16, 15, peak, 1);
504
+ put_dec(16, 23, bonus, 1);
505
+ put_dec(16, 31, kills, 1);
506
+ put_str(17, 20, "shots");
507
+ put_dec(17, 26, shots, 2);
508
+
509
+ unsigned chk = 0;
510
+ for (char s = 0; s < NS; s++)
511
+ {
512
+ chk = fold(chk, e_live[s]);
513
+ chk = fold(chk, e_x[s]);
514
+ chk = fold(chk, e_y[s]);
515
+ chk = fold(chk, e_pc[s]);
516
+ chk = fold(chk, e_n[s]);
517
+ chk = fold(chk, e_lc[s]);
518
+ }
519
+ put_str(17, 0, "chk exp");
520
+ put_hex16(17, 4, chk);
521
+ put_hex16(17, 13, EXPECT_CHK);
522
+ pass = pass && chk == EXPECT_CHK;
523
+
524
+ // Timing. The picture and the checks are done, so the subjects may
525
+ // overwrite the slots. Sprites off, interrupts off, screen blanked, so
526
+ // no DMA or IRQ lands in a count.
527
+ char en = vic.spr_enable;
528
+ __asm { sei }
529
+ vic.spr_enable = 0;
530
+ vic.ctrl1 = 0x0b;
531
+ vic_waitFrame();
532
+ unsigned t0 = time_loop(nothing, 100);
533
+ unsigned tb = time_loop(reset_only, 100);
534
+ unsigned t_mv = time_loop(step_move, 100);
535
+ unsigned t_fe = time_loop(step_fetch, 100);
536
+ unsigned t_fl = time_loop(step_fire_loop, 100);
537
+ unsigned t_di = time_loop(director, 100); // cursor at 1: no trigger
538
+ unsigned tb8 = time_loop(reset8_only, 20);
539
+ unsigned t_f8m = time_loop(frame8_move, 20);
540
+ unsigned tbw = time_loop(reset_worst, 10);
541
+ unsigned t_w = time_loop(frame_worst, 10);
542
+ vic.ctrl1 = 0x1b;
543
+ vic.spr_enable = en;
544
+
545
+ put_str(18, 0, "cycles n total per call");
546
+ put_str(19, 0, "step, move");
547
+ put_dec(19, 16, 100, 3);
548
+ put_dec(19, 21, t_mv, 5);
549
+ put_per_call(19, 28, t_mv, tb, 100);
550
+ put_str(20, 0, "step, fetch");
551
+ put_dec(20, 16, 100, 3);
552
+ put_dec(20, 21, t_fe, 5);
553
+ put_per_call(20, 28, t_fe, tb, 100);
554
+ put_str(21, 0, "fire+loop+move");
555
+ put_dec(21, 16, 100, 3);
556
+ put_dec(21, 21, t_fl, 5);
557
+ put_per_call(21, 28, t_fl, tb, 100);
558
+ put_str(22, 0, "director idle");
559
+ put_dec(22, 16, 100, 3);
560
+ put_dec(22, 21, t_di, 5);
561
+ put_per_call(22, 28, t_di, t0, 100);
562
+ put_str(23, 0, "8 moving");
563
+ put_dec(23, 16, 20, 3);
564
+ put_dec(23, 21, t_f8m, 5);
565
+ put_per_call(23, 28, t_f8m, tb8, 20);
566
+ put_str(24, 0, "worst frame");
567
+ put_dec(24, 16, 10, 3);
568
+ put_dec(24, 21, t_w, 5);
569
+ put_per_call(24, 28, t_w, tbw, 10);
570
+
571
+ put_str(7, 36, pass ? "pass" : "fail");
572
+ vic.color_border = pass ? VCOL_GREEN : VCOL_RED;
573
+ for (;;)
574
+ ;
575
+ return 0;
576
+ }
577
+ ```
578
+
579
+ ## Build
580
+
581
+ ```
582
+ oscar64 -tm=c64 -O2 -o=wave-director.prg wave-director.c
583
+ GSETTINGS_SCHEMA_DIR=/opt/homebrew/share/glib-2.0/schemas timeout 180 x64sc -default -minimized -warp +sound +autostart-delay-random -autostartprgmode 1 -limitcycles 20000000 -exitscreenshot wave-director.png -autostart wave-director.prg
584
+ ```
585
+
586
+ Add `-model ntsc` for the NTSC picture. 20,000,000 cycles is the pinned
587
+ run. The game runs 710 frames, about 14,000,000 cycles on PAL and
588
+ 12,140,000 on NTSC (arithmetic: 19,656 and 17,095 cycles a frame). The PAL
589
+ screen was not yet complete at 16,000,000 and complete at 18,000,000; NTSC
590
+ was complete at 16,000,000 (measured in VICE x64sc 3.10). The PRG is 3,777
591
+ bytes.
592
+
593
+ ## Expected output
594
+
595
+ Black screen, green border, five yellow 8 by 8 squares in a diagonal above
596
+ the text. Decoded from `screenshots/wave-director.png` (PAL) with the
597
+ character ROM; rows 0 to 6 hold no text:
598
+
599
+ ```text
600
+ WAVE DIRECTOR 6 WAVES 3 PATHS PASS
601
+ W LP POS TICK SPAWN SCROLL 060
602
+ 0 0 040 0039 5
603
+ 1 0 130 0129 6
604
+ 2 0 220 0219 6
605
+ 3 0 310 0359 4
606
+ 4 0 400 0449 6
607
+ 5 0 490 0539 6
608
+ 0 1 040 0689 5
609
+ REFUSED 0 PEAK 8 BONUS 1 KILLS 5
610
+ CHK EB12 EXP EB12 SHOTS 48
611
+ CYCLES N TOTAL PER CALL
612
+ STEP, MOVE 100 14104 0080.00
613
+ STEP, FETCH 100 21904 0158.00
614
+ FIRE+LOOP+MOVE 100 35004 0289.00
615
+ DIRECTOR IDLE 100 08204 0045.00
616
+ 8 MOVING 020 31044 1170.00
617
+ WORST FRAME 010 36704 3188.00
618
+ ```
619
+
620
+ `screenshots/wave-director-ntsc.png` (`-model ntsc`) decodes to the same
621
+ text, and the cycle counts are identical on both models (the CIA counts phi2
622
+ cycles with the screen blanked). Each model's pinned run was made twice and
623
+ gave byte-identical PNGs (measured in VICE x64sc 3.10).
624
+
625
+ What each row states:
626
+
627
+ - Rows 9 to 15, the trigger log: wave, loop, scroll position at the
628
+ trigger, frame of the trigger, enemies spawned. Every scroll position
629
+ equals the table's, and every spawn count equals the table's count.
630
+ Waves 0 to 2 trigger on frames 39, 129 and 219: frame 0 already moves
631
+ the scroll to 1, and the director looks after the move. The scroll
632
+ holds for frames 250 to 299, so waves 3 to 5 trigger 50 frames later
633
+ than their position (359, 449, 539) and still at their position. The
634
+ level loops at frame 650 and wave 0 starts again at frame 689.
635
+ - Row 16: no spawn found the pool full; at most 8 enemies were live at
636
+ once; one wave (wave 3, four dives down the gun column at x 150) was
637
+ killed whole and scored the bonus; 5 kills in all (wave 3's four and
638
+ wave 0's enemy at x 150).
639
+ - Row 17: the fold over the 48 bytes of `e_live`, `e_x`, `e_y`, `e_pc`,
640
+ `e_n` and `e_lc` is $EB12 on the 6510 and in Python; 48 FIRE opcodes ran.
641
+ - Rows 19 to 24: CIA1 timer A over N calls, total and net per call. The
642
+ single-step rows subtract a subject that does the same slot reset without
643
+ the step; the director row subtracts an empty call; each frame row
644
+ subtracts a subject that does its slot reset without the frame's work.
645
+ A frame row times the director, the spawner and every live enemy's
646
+ step with its edge and gun tests. 8 moving: eight enemies mid-MOVE,
647
+ nothing due. Worst frame: seven enemies on the weave path's FIRE, LOOP
648
+ back and MOVE fetch in one step, a wave due, and its first enemy
649
+ spawned into the last free slot, which fetches its first MOVE.
650
+
651
+ The sprites, measured with PIL on both pictures. They are loop 1's wave 0,
652
+ five dives spawned every 3 frames instead of 6, so they are 9 lines apart
653
+ where loop 0's were 18. Screenshot x is sprite X plus 8; the top raster
654
+ line is the sprite Y register plus 1:
655
+
656
+ | Sprite X, Y (Python model) | Screenshot x | PAL rows | NTSC rows | Raster lines |
657
+ |---|---|---|---|---|
658
+ | 60, 93 | 68 to 75 | 78 to 85 | 66 to 73 | 94 to 101 |
659
+ | 90, 84 | 98 to 105 | 69 to 76 | 57 to 64 | 85 to 92 |
660
+ | 120, 75 | 128 to 135 | 60 to 67 | 48 to 55 | 76 to 83 |
661
+ | 150, 66 | 158 to 165 | 51 to 58 | 39 to 46 | 67 to 74 |
662
+ | 180, 57 | 188 to 195 | 42 to 49 | 30 to 37 | 58 to 65 |
663
+
664
+ Every square is where the model puts it, to the pixel, on both models. The
665
+ model of the whole run:
666
+
667
+ ```python
668
+ END, LOOP, FIRE = 0x80, 0x81, 0x82
669
+ PATHS = [[127, 0, 3, END],
670
+ [6, 2, 2, 12, 0xfe, 2, 6, 2, 2, FIRE, LOOP, 3, 0, 127, 0, 3, END],
671
+ [16, 0, 3, 8, 1, 3, 8, 2, 2, FIRE, 8, 3, 1, 8, 3, 0, 127, 3, 0xfe, END]]
672
+ WAVES = [(40, 0, 5, 6, 60, 30), (130, 1, 6, 10, 40, 0), (220, 2, 6, 8, 30, 12),
673
+ (310, 0, 4, 10, 150, 0), (400, 1, 6, 10, 120, 0), (490, 2, 6, 8, 100, 12)]
674
+ NS = 8
675
+ live, x, y, pa, pc, n, dx, dy, lc, wv = ([0] * NS for _ in range(10))
676
+ g = dict(scroll=0, loop=0, cur=0, left=0, timer=0, sw=0, refused=0,
677
+ bonus=0, kills=0, shots=0, peak=0)
678
+ alive, killed, spawned = [0] * 6, [0] * 6, [0] * 6
679
+ log = []
680
+
681
+ def step(s):
682
+ p = PATHS[pa[s]]
683
+ while n[s] == 0:
684
+ op = p[pc[s]]
685
+ if op == END:
686
+ return False
687
+ if op == FIRE:
688
+ g['shots'] += 1; pc[s] += 1
689
+ elif op == LOOP:
690
+ if lc[s] == 0: lc[s] = p[pc[s] + 1]
691
+ lc[s] -= 1
692
+ pc[s] = p[pc[s] + 2] if lc[s] else pc[s] + 3
693
+ else:
694
+ n[s], dx[s], dy[s] = op, p[pc[s] + 1], p[pc[s] + 2]; pc[s] += 3
695
+ x[s] = (x[s] + dx[s]) & 255; y[s] = (y[s] + dy[s]) & 255; n[s] -= 1
696
+ return True
697
+
698
+ def gone(s, hit):
699
+ w = wv[s]; live[s] = 0; alive[w] -= 1
700
+ if hit: killed[w] += 1; g['kills'] += 1
701
+ if alive[w] == 0 and spawned[w] == WAVES[w][2] and not (g['left'] and g['sw'] == w):
702
+ g['bonus'] += killed[w] == WAVES[w][2]
703
+
704
+ for t in range(710):
705
+ if not 250 <= t < 300:
706
+ g['scroll'] += 1
707
+ if g['scroll'] == 600:
708
+ g['scroll'], g['cur'] = 0, 0; g['loop'] += 1
709
+ while g['cur'] < 6 and g['scroll'] >= WAVES[g['cur']][0]:
710
+ w = g['cur']; log.append([w, g['loop'], g['scroll'], t, 0])
711
+ g['sw'], g['left'], g['timer'] = w, WAVES[w][2], 0
712
+ alive[w] = killed[w] = spawned[w] = 0; g['cur'] += 1
713
+ if g['left']:
714
+ if g['timer'] == 0:
715
+ w = g['sw']; pos, path, cnt, spc, x0, xs = WAVES[w]
716
+ free = [s for s in range(NS) if not live[s]]
717
+ if free:
718
+ s = free[0]
719
+ live[s], x[s], y[s], pa[s], pc[s], n[s], lc[s], wv[s] = \
720
+ 1, (x0 + (cnt - g['left']) * xs) & 255, 30, path, 0, 0, 0, w
721
+ alive[w] += 1; spawned[w] += 1; log[-1][4] += 1
722
+ else:
723
+ g['refused'] += 1
724
+ g['left'] -= 1
725
+ g['timer'] = max(1, spc >> g['loop']) - 1
726
+ else:
727
+ g['timer'] -= 1
728
+ for s in range(NS):
729
+ if not live[s]: continue
730
+ if not step(s): gone(s, False)
731
+ elif x[s] >= 250 or y[s] >= 250: gone(s, False)
732
+ elif 140 <= x[s] < 164 and y[s] >= 150: gone(s, True)
733
+ g['peak'] = max(g['peak'], sum(live))
734
+
735
+ chk = 0
736
+ for s in range(NS):
737
+ for v in (live[s], x[s], y[s], pc[s], n[s], lc[s]):
738
+ chk = ((chk ^ v) * 5 + 1) & 0xFFFF
739
+ print(log)
740
+ print(g['refused'], g['peak'], g['bonus'], g['kills'], g['shots'], hex(chk))
741
+ print([(x[s], y[s]) for s in range(NS) if live[s]])
742
+ ```
743
+
744
+ It prints the seven log rows above as lists, `0 8 1 5 48 0xeb12`, and the five
745
+ positions in the table.
746
+
747
+ ## Why this works
748
+
749
+ The trigger compares the scroll position, not the frame. The 50-frame hold
750
+ proves it: waves 3 to 5 start 50 frames late and at their exact positions,
751
+ so the enemies meet the same scenery they were placed against. A frame-keyed
752
+ table would have started them on frames 309, 399 and 489, while the scroll
753
+ stood still. The compare is `>=`, and the director loops while the next
754
+ record is due, so a scroll that jumps several positions in a frame starts
755
+ every wave it passed, in order, and never skips one. Because the list is
756
+ sorted, one compare against the record under the cursor is the whole idle
757
+ cost: 45 cycles a frame in C.
758
+
759
+ A path is data, so one interpreter moves every enemy. A MOVE costs a fetch
760
+ once and then a plain add per frame: 80 cycles a step with steps left, 158
761
+ on a step that fetches the next MOVE, 289 on one that also runs FIRE and a
762
+ LOOP back (all Oscar64 -O2, measured). The control opcodes take no frame,
763
+ so a path can fire and loop between two moves without a stall. Each enemy
764
+ carries only a path id, a program counter, a step count, a velocity and a
765
+ loop counter; the path bytes are shared by every enemy on that path.
766
+
767
+ The edge test is one compare per axis. x and y are bytes, and any byte at
768
+ 250 or more is off screen: moving right past 249 or up or left past 0 both
769
+ land there, because a byte that goes below 0 wraps to 255. The price is a
770
+ playfield of sprite X 0 to 249, so `$D010` stays 0 and cannot be forgotten
771
+ (`sprite_x_high_bit_wrong_register`). A full-width game keeps a ninth x bit
772
+ per enemy and writes `$D010` from it.
773
+
774
+ The frame's director work with all eight slots live and mid-MOVE is 1,170
775
+ cycles, about 146 per enemy with the edge and gun tests. The worst frame
776
+ measured is 3,188: seven enemies each run FIRE, a LOOP back and a MOVE
777
+ fetch in one step (the longest step these paths have), a wave triggers,
778
+ and its first enemy is spawned into the last free slot. A frame budget has
779
+ to hold that figure. It leaves out the scroll advance, the live count and
780
+ the sprite writes, and `gone()` with its end-of-wave test, which runs only
781
+ when an enemy leaves. The step and frame figures move by a few cycles
782
+ with code layout. These are
783
+ C figures; hand-written assembly would be well under them.
784
+
785
+ The difficulty rule is one shift: the spacing is `spacing >> loop`, never
786
+ less than 1. Loop 1's wave 0 therefore spawns every 3 frames, and the
787
+ picture shows the five dives 9 lines apart where loop 0 had them 18 apart.