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,651 @@
1
+ ---
2
+ recipe: per-frame-hitbox
3
+ toolchain: oscar64
4
+ output_format: PRG
5
+ region: both
6
+ techniques: [per_frame_hitbox]
7
+ file_formats: [PRG]
8
+ uses_registers: [D000, D001, D010, D011, D015, D01C, D01E, D020, D021, D025, D026, D027, DC06, DC07, DC0F]
9
+ uses_kernal: []
10
+ ---
11
+
12
+ <!-- doc-type: recipe -->
13
+
14
+ # Oscar64 Per-Frame Hitboxes: boxes from the animation frame, pairs from group bits
15
+
16
+ ## Synopsis
17
+
18
+ A player sprite takes three poses, stand, crouch and attack, and each
19
+ pose's animation frame carries its own collision box. The attack frame
20
+ carries two: a body in the player group and a blade in the player-bullet
21
+ group. Every frame the draw appends each actor's boxes to one list at the
22
+ position the sprite is drawn at, and the collision pass tests only the
23
+ pairs whose groups can hurt each other. Two enemies and four bullets from
24
+ both sides run a scripted scenario of three 64-frame stages, one per
25
+ pose. The same enemy bullet hits the standing player and passes over the
26
+ crouching one at the same position; enemy bullets fly through enemies
27
+ without an event; the blade hits an enemy the body does not touch; a
28
+ player bullet 256 pixels to the left of an enemy is a miss that a
29
+ low-byte X test reports as a hit. The screen prints expected and actual
30
+ events per stage, the `$D01E` value the hardware latched, and CIA1 timer B
31
+ cycles per pair and per frame. The last stage holds with every box drawn
32
+ as a yellow outline inside its sprite. `$02FF` = `01` and a green border
33
+ on a pass, `02` and red otherwise (`headless-verify.md`). This is the
34
+ `per_frame_hitbox` technique from `docs/techniques/sprite.md`.
35
+
36
+ ## Source
37
+
38
+ ```c
39
+ // per-frame-hitbox.c
40
+ //
41
+ // Collision boxes per animation frame, emitted at draw time, tested in
42
+ // pairs chosen by group bits. A player sprite takes three poses (stand,
43
+ // crouch, attack), each with its own box table entry; the attack pose
44
+ // emits two boxes, a body and a blade in the player-bullet group. Two
45
+ // enemies and four bullets from both sides move through a scripted
46
+ // scenario of three 64-frame stages, one per pose. Each stage's hit
47
+ // events are compared with the expected set. The last stage then holds
48
+ // with the boxes drawn as yellow outlines inside the sprites.
49
+ // Row 0: stage and frame. Row 1: the verdict. Rows 2-5: expected and
50
+ // actual events per stage and the $D01E value the hardware latched.
51
+ // Rows 7-12: pairs, CIA1 timer B cycles, the false hits of a low-byte X
52
+ // test, and the player's first box. $02FF = 01 and a green border when
53
+ // every stage matched and the low-byte test was caught; 02 and red
54
+ // otherwise.
55
+ //
56
+ #include <c64/vic.h>
57
+ #include <c64/cia.h>
58
+ #include <c64/sprites.h>
59
+
60
+ #define Screen ((char *)0x0400)
61
+ #define Color ((char *)0xd800)
62
+ #define SprData ((char *)0x3000) // blocks 192..197
63
+ #define SPR_BLK 192
64
+ #define RESULT (*(volatile char *)0x02ff)
65
+
66
+ // Groups, one bit each. box_mask (below) holds, for each box, the
67
+ // groups it is tested against; the relation is symmetric: player with
68
+ // enemy and enemy bullet, player bullet with enemy, and nothing else.
69
+ #define G_PLAYER 0x01
70
+ #define G_PSHOT 0x02
71
+ #define G_ENEMY 0x04
72
+ #define G_ESHOT 0x08
73
+
74
+ // --- animation frames and their boxes --------------------------------
75
+ // Offsets from the sprite's top-left, in pixels, even so they fit the
76
+ // 2-pixel columns of a multicolour sprite. A frame owns box_count boxes
77
+ // from box_first on.
78
+ #define F_STAND 0
79
+ #define F_CROUCH 1
80
+ #define F_ATTACK 2
81
+ #define F_ENEMY 3
82
+ #define F_ESHOT 4
83
+ #define F_PSHOT 5
84
+ #define FRAMES 6
85
+
86
+ struct Rect { char x, y, w, h; };
87
+
88
+ const struct Rect figure[FRAMES] = { // drawn pixels, sprite colour
89
+ { 6, 0, 12, 21 }, { 6, 10, 12, 11 }, { 4, 0, 12, 21 },
90
+ { 0, 0, 24, 21 }, { 6, 6, 12, 8 }, { 6, 6, 12, 8 }
91
+ };
92
+ const char box_first[FRAMES] = { 0, 1, 2, 4, 5, 6 };
93
+ const char box_count[FRAMES] = { 1, 1, 2, 1, 1, 1 };
94
+
95
+ const struct Rect box_rect[7] = {
96
+ { 8, 2, 8, 19 }, // stand: body
97
+ { 8, 12, 8, 9 }, // crouch: lower half
98
+ { 6, 2, 8, 19 }, { 14, 6, 10, 4 }, // attack: body, blade
99
+ { 4, 2, 16, 18 }, // enemy
100
+ { 8, 8, 8, 4 }, // enemy bullet
101
+ { 8, 8, 8, 4 } // player bullet
102
+ };
103
+ const char box_group[7] = {
104
+ G_PLAYER, G_PLAYER, G_PLAYER, G_PSHOT, G_ENEMY, G_ESHOT, G_PSHOT
105
+ };
106
+
107
+ // The groups each box is tested against.
108
+ const char box_mask[7] = {
109
+ G_ENEMY | G_ESHOT, G_ENEMY | G_ESHOT, G_ENEMY | G_ESHOT, G_ENEMY,
110
+ G_PLAYER | G_PSHOT, G_PLAYER, G_ENEMY
111
+ };
112
+
113
+ // --- actors ------------------------------------------------------------
114
+ // One hardware sprite each. X is the 9-bit sprite X, Y the sprite Y.
115
+ // Bullets take the low sprite numbers so they are drawn in front.
116
+ #define A_EB1 0
117
+ #define A_EB2 1
118
+ #define A_PB1 2
119
+ #define A_PB2 3
120
+ #define A_P 4
121
+ #define A_E1 5
122
+ #define A_E2 6
123
+ #define ACTORS 7
124
+
125
+ int ax[ACTORS];
126
+ char ay[ACTORS];
127
+ char aframe[ACTORS];
128
+ const char acol[ACTORS] = { 10, 10, 3, 3, 1, 2, 2 };
129
+
130
+ // Event bits: which actor's box hit which. Bits 1, 4 and 5 must never
131
+ // be set: body contact that is not an overlap, an enemy bullet in an
132
+ // enemy, and a player bullet whose X differs from the enemy's by 256.
133
+ #define EV_EB1_P 0x01
134
+ #define EV_E1_P 0x02
135
+ #define EV_BLADE_E1 0x04
136
+ #define EV_PB1_E2 0x08
137
+ #define EV_EB2_E2 0x10
138
+ #define EV_PB2_E2 0x20
139
+
140
+ #define PAIR(a, b) ((1 << (a)) | (1 << (b)))
141
+
142
+ char event_bit(char a, char b)
143
+ {
144
+ switch (PAIR(a, b)) {
145
+ case PAIR(A_P, A_EB1): return EV_EB1_P;
146
+ case PAIR(A_P, A_E1): return EV_E1_P; // body or blade, split below
147
+ case PAIR(A_E2, A_PB1): return EV_PB1_E2;
148
+ case PAIR(A_E2, A_EB2): return EV_EB2_E2;
149
+ case PAIR(A_E2, A_PB2): return EV_PB2_E2;
150
+ default: return 0;
151
+ }
152
+ }
153
+
154
+ // --- the box list, refilled every frame by the draw ---------------------
155
+ #define MAXB 8
156
+ char nbox;
157
+ char bll[MAXB], blh[MAXB]; // left X, 9 bits as low and high byte
158
+ char brl[MAXB], brh[MAXB]; // right X, exclusive
159
+ char bt[MAXB], bb[MAXB]; // Y, bottom edge exclusive
160
+ char bhl[MAXB], bhr[MAXB]; // X / 2, for the 8-bit variant
161
+ char bg[MAXB], bm[MAXB], bo[MAXB]; // group bit, pair mask, owner
162
+
163
+ // Draw one actor's boxes: append its current frame's boxes to the list
164
+ // at the position the sprite is drawn at.
165
+ void emit_boxes(char a)
166
+ {
167
+ unsigned x = ax[a];
168
+ char y = ay[a], f = aframe[a];
169
+ char k = box_first[f], e = k + box_count[f];
170
+ char i = nbox;
171
+ do {
172
+ unsigned l = x + box_rect[k].x, rr = l + box_rect[k].w;
173
+ bll[i] = (char)l;
174
+ blh[i] = l >> 8;
175
+ brl[i] = (char)rr;
176
+ brh[i] = rr >> 8;
177
+ bhl[i] = l >> 1;
178
+ bhr[i] = rr >> 1;
179
+ char t = y + box_rect[k].y;
180
+ bt[i] = t;
181
+ bb[i] = t + box_rect[k].h;
182
+ bg[i] = box_group[k];
183
+ bm[i] = box_mask[k];
184
+ bo[i] = a;
185
+ i++;
186
+ k++;
187
+ } while (k != e);
188
+ nbox = i;
189
+ }
190
+
191
+ // The sprite half of the draw: X is 9 bits, spr_move sets the $D010 bit.
192
+ void draw_sprite(char a)
193
+ {
194
+ spr_move(a, ax[a], ay[a]);
195
+ spr_image(a, SPR_BLK + aframe[a]);
196
+ }
197
+
198
+ // 9-bit "left of i is left of right of j": high bytes first.
199
+ static inline bool xlt(char i, char j)
200
+ {
201
+ return blh[i] < brh[j] || (blh[i] == brh[j] && bll[i] < brl[j]);
202
+ }
203
+
204
+ // The AABB test. Y first: it is one byte and in a side-scrolling
205
+ // scene most pairs are apart in Y. Each compare can end the test.
206
+ static inline bool overlap9(char i, char j)
207
+ {
208
+ return bt[i] < bb[j] && bt[j] < bb[i] && xlt(i, j) && xlt(j, i);
209
+ }
210
+
211
+ // The same test on X / 2: every coordinate one byte, 2-pixel precision.
212
+ static inline bool overlap8(char i, char j)
213
+ {
214
+ return bt[i] < bb[j] && bt[j] < bb[i] && bhl[i] < bhr[j] && bhl[j] < bhr[i];
215
+ }
216
+
217
+ // The broken variant: only the low byte of X, as when $D010's bit is
218
+ // ignored. Used only to count its false hits.
219
+ static inline bool overlap_lo(char i, char j)
220
+ {
221
+ return bt[i] < bb[j] && bt[j] < bb[i] && bll[i] < brl[j] && bll[j] < brl[i];
222
+ }
223
+
224
+ char pairs_all, pairs_tested, frame_events;
225
+
226
+ void collide(void)
227
+ {
228
+ pairs_all = 0;
229
+ pairs_tested = 0;
230
+ for (char i = 0; i + 1 < nbox; i++) {
231
+ char m = bm[i];
232
+ for (char j = i + 1; j < nbox; j++) {
233
+ pairs_all++;
234
+ if (!(m & bg[j]))
235
+ continue; // friendly or irrelevant pair
236
+ pairs_tested++;
237
+ if (overlap9(i, j)) {
238
+ char e = event_bit(bo[i], bo[j]);
239
+ if (e == EV_E1_P && (bg[i] == G_PSHOT || bg[j] == G_PSHOT))
240
+ e = EV_BLADE_E1;
241
+ frame_events |= e;
242
+ }
243
+ }
244
+ }
245
+ }
246
+
247
+ // --- CIA1 timer B harness (as tile-grid-collision.md) --------------------
248
+ static inline void timer_start(void)
249
+ {
250
+ cia1.crb = 0x00;
251
+ cia1.tb = 0xffff;
252
+ cia1.crb = 0x11;
253
+ }
254
+
255
+ static inline unsigned timer_stop(void)
256
+ {
257
+ cia1.crb = 0x00;
258
+ return 0xffff - cia1.tb;
259
+ }
260
+
261
+ // Per-pair timing: each is called 100 times through a pointer and the
262
+ // empty call is subtracted, so the figure is the test alone.
263
+ char gi, gj, gk;
264
+ volatile bool sink;
265
+ __noinline void nothing(void) { }
266
+ __noinline void pair_hit(void) { sink = overlap9(gi, gj); }
267
+ __noinline void pair_miss(void) { sink = overlap9(gi, gk); }
268
+ __noinline void pair_hit8(void) { sink = overlap8(gi, gj); }
269
+ __noinline void pair_miss8(void) { sink = overlap8(gi, gk); }
270
+
271
+ unsigned time100(void (*fn)(void))
272
+ {
273
+ __asm { sei }
274
+ timer_start();
275
+ for (char n = 0; n < 100; n++)
276
+ fn();
277
+ unsigned t = timer_stop();
278
+ __asm { cli }
279
+ return t;
280
+ }
281
+
282
+ // --- sprite images: figure in the sprite colour, boxes outlined in MC1 --
283
+ void mc_px(char *img, char cx, char row, char v)
284
+ {
285
+ char *p = img + row * 3 + (cx >> 2);
286
+ char s = (3 - (cx & 3)) * 2;
287
+ *p = (*p & ~(3 << s)) | (v << s);
288
+ }
289
+
290
+ void make_images(void)
291
+ {
292
+ for (unsigned i = 0; i < FRAMES * 64; i++)
293
+ SprData[i] = 0;
294
+ for (char f = 0; f < FRAMES; f++) {
295
+ char *img = SprData + f * 64;
296
+ const struct Rect *r = figure + f;
297
+ for (char y = r->y; y < r->y + r->h; y++)
298
+ for (char x = r->x >> 1; x < (r->x + r->w) >> 1; x++)
299
+ mc_px(img, x, y, 2);
300
+ if (f == F_ATTACK) // the blade is drawn too
301
+ for (char y = 6; y < 10; y++)
302
+ for (char x = 7; x < 12; x++)
303
+ mc_px(img, x, y, 2);
304
+ for (char k = box_first[f]; k < box_first[f] + box_count[f]; k++) {
305
+ const struct Rect *b = box_rect + k;
306
+ char x0 = b->x >> 1, x1 = ((b->x + b->w) >> 1) - 1;
307
+ char y0 = b->y, y1 = b->y + b->h - 1;
308
+ for (char x = x0; x <= x1; x++) {
309
+ mc_px(img, x, y0, 3);
310
+ mc_px(img, x, y1, 3);
311
+ }
312
+ for (char y = y0; y <= y1; y++) {
313
+ mc_px(img, x0, y, 3);
314
+ mc_px(img, x1, y, 3);
315
+ }
316
+ }
317
+ }
318
+ }
319
+
320
+ // --- text helpers (screen codes) ------------------------------------------
321
+ void put_str(char *p, const char *s)
322
+ {
323
+ while (*s)
324
+ *p++ = *s++;
325
+ }
326
+
327
+ void put_dec(char *p, unsigned v, char digits)
328
+ {
329
+ for (char i = digits; i > 0; i--) {
330
+ p[i - 1] = 0x30 + v % 10;
331
+ v /= 10;
332
+ }
333
+ }
334
+
335
+ void put_hex2(char *p, char v)
336
+ {
337
+ char d = v >> 4;
338
+ p[0] = d < 10 ? 0x30 + d : d - 9;
339
+ d = v & 15;
340
+ p[1] = d < 10 ? 0x30 + d : d - 9;
341
+ }
342
+
343
+ // --- scenario --------------------------------------------------------------
344
+ #define STAGE_FRAMES 64
345
+ #define HOLD_T 36
346
+
347
+ const char stage_pose[3] = { F_STAND, F_CROUCH, F_ATTACK };
348
+ const char stage_expect[3] = {
349
+ EV_EB1_P | EV_PB1_E2, // stand: shot in the head
350
+ EV_PB1_E2, // crouch: the shot passes over
351
+ EV_EB1_P | EV_BLADE_E1 | EV_PB1_E2 // attack: body hit, blade hits E1
352
+ };
353
+ const char *stage_name[3] = { s"stand ", s"crouch", s"attack" };
354
+
355
+ // Positions at frame t of a stage: bullets move 2 pixels a frame.
356
+ void place(char pose, char t)
357
+ {
358
+ ax[A_P] = 100; ay[A_P] = 200; aframe[A_P] = pose;
359
+ ax[A_E1] = 116; ay[A_E1] = 200; aframe[A_E1] = F_ENEMY;
360
+ ax[A_E2] = 300; ay[A_E2] = 160; aframe[A_E2] = F_ENEMY;
361
+ ax[A_EB1] = 164 - 2 * t; ay[A_EB1] = 196; aframe[A_EB1] = F_ESHOT;
362
+ ax[A_EB2] = 380 - 2 * t; ay[A_EB2] = 164; aframe[A_EB2] = F_ESHOT;
363
+ ax[A_PB1] = 220 + 2 * t; ay[A_PB1] = 156; aframe[A_PB1] = F_PSHOT;
364
+ ax[A_PB2] = 20 + 2 * t; ay[A_PB2] = 160; aframe[A_PB2] = F_PSHOT;
365
+ }
366
+
367
+ int main(void)
368
+ {
369
+ vic.color_border = 0;
370
+ vic.color_back = 0;
371
+ for (unsigned i = 0; i < 1000; i++) {
372
+ Screen[i] = 0x20;
373
+ Color[i] = 1;
374
+ }
375
+ make_images();
376
+
377
+ spr_init(Screen);
378
+ for (char a = 0; a < ACTORS; a++)
379
+ spr_set(a, false, 0, 0, SPR_BLK, acol[a], true, false, false);
380
+ vic.spr_mcolor0 = 0;
381
+ vic.spr_mcolor1 = 7; // box outlines: yellow
382
+
383
+ put_str(Screen + 0, s"per-frame hitbox stage frame");
384
+ put_str(Screen + 2 * 40, s"pose exp got d01e");
385
+ put_str(Screen + 7 * 40, s"pairs tested boxes");
386
+ put_str(Screen + 8 * 40, s"emit max collide max");
387
+ put_str(Screen + 9 * 40, s"pair 9-bit hit miss");
388
+ put_str(Screen + 10 * 40, s"pair 8-bit hit miss");
389
+ put_str(Screen + 11 * 40, s"low-byte-x false hits");
390
+ put_str(Screen + 12 * 40, s"p box l r t b");
391
+
392
+ // Per-pair cost, before the scenario: box 0 and 1 overlap, box 0 and
393
+ // 2 fail the first compare. 100 calls each, less 100 empty calls,
394
+ // with the screen blanked so no badline steals a cycle.
395
+ nbox = 0;
396
+ place(F_STAND, 32);
397
+ emit_boxes(A_P); // box 0
398
+ emit_boxes(A_EB1); // box 1: overlaps it
399
+ emit_boxes(A_E2); // box 2: apart in Y
400
+ gi = 0; gj = 1; gk = 2;
401
+ vic.ctrl1 &= ~VIC_CTRL1_DEN;
402
+ vic_waitFrame();
403
+ vic_waitFrame();
404
+ unsigned t_none = time100(nothing);
405
+ unsigned c_hit = (time100(pair_hit) - t_none) / 100;
406
+ unsigned c_miss = (time100(pair_miss) - t_none) / 100;
407
+ unsigned c_hit8 = (time100(pair_hit8) - t_none) / 100;
408
+ unsigned c_miss8 = (time100(pair_miss8) - t_none) / 100;
409
+ vic.ctrl1 |= VIC_CTRL1_DEN;
410
+ put_dec(Screen + 9 * 40 + 15, c_hit, 3);
411
+ put_dec(Screen + 9 * 40 + 25, c_miss, 3);
412
+ put_dec(Screen + 10 * 40 + 15, c_hit8, 3);
413
+ put_dec(Screen + 10 * 40 + 25, c_miss8, 3);
414
+
415
+ // Put every sprite at its first position before enabling them, or
416
+ // they meet at 0, 0 for a frame and $D01E latches that.
417
+ place(stage_pose[0], 0);
418
+ for (char a = 0; a < ACTORS; a++)
419
+ draw_sprite(a);
420
+ nbox = 0; // nothing drawn yet
421
+ vic.spr_enable = (1 << ACTORS) - 1;
422
+ char stage = 0, t = 0, fault = 0, lo_false = 0;
423
+ bool done = false;
424
+ unsigned frame = 0, d_max = 0, c_max = 0;
425
+ char stage_events = 0;
426
+ vic_waitFrame();
427
+ vic.spr_sprcol; // clear the latch
428
+
429
+ for (;;) {
430
+ vic_waitFrame();
431
+
432
+ // Collide first, on the boxes the last frame's draw emitted, so
433
+ // the test runs at the top of the blank on both models.
434
+ __asm { sei }
435
+ frame_events = 0;
436
+ timer_start();
437
+ collide();
438
+ unsigned tc = timer_stop();
439
+ place(stage_pose[stage], done ? HOLD_T : t);
440
+ timer_start();
441
+ nbox = 0;
442
+ for (char a = 0; a < ACTORS; a++)
443
+ emit_boxes(a);
444
+ unsigned td = timer_stop();
445
+ __asm { cli }
446
+ for (char a = 0; a < ACTORS; a++)
447
+ draw_sprite(a);
448
+ if (td > d_max) d_max = td;
449
+ if (tc > c_max) c_max = tc;
450
+ stage_events |= frame_events;
451
+
452
+ // The low-byte test on the pair that differs by 256 in X.
453
+ for (char i = 0; i < nbox; i++)
454
+ for (char j = 0; j < nbox; j++)
455
+ if (bo[i] == A_E2 && bo[j] == A_PB2 && overlap_lo(i, j))
456
+ lo_false++;
457
+
458
+ put_dec(Screen + 24, stage + 1, 1);
459
+ put_dec(Screen + 32, frame, 5);
460
+ put_dec(Screen + 7 * 40 + 6, pairs_all, 2);
461
+ put_dec(Screen + 7 * 40 + 16, pairs_tested, 2);
462
+ put_dec(Screen + 7 * 40 + 25, nbox, 1);
463
+ put_dec(Screen + 8 * 40 + 9, d_max, 5);
464
+ put_dec(Screen + 8 * 40 + 27, c_max, 5);
465
+ put_dec(Screen + 11 * 40 + 22, lo_false, 3);
466
+ char p = 0;
467
+ while (bo[p] != A_P)
468
+ p++; // the player's first box
469
+ put_dec(Screen + 12 * 40 + 8, bll[p] + 256 * blh[p], 3);
470
+ put_dec(Screen + 12 * 40 + 13, brl[p] + 256 * brh[p], 3);
471
+ put_dec(Screen + 12 * 40 + 18, bt[p], 3);
472
+ put_dec(Screen + 12 * 40 + 23, bb[p], 3);
473
+ frame++;
474
+
475
+ if (!done && ++t == STAGE_FRAMES) {
476
+ char hw = vic.spr_sprcol; // read once per stage, clears it
477
+ char *row = Screen + (3 + stage) * 40;
478
+ put_str(row, stage_name[stage]);
479
+ put_hex2(row + 8, stage_expect[stage]);
480
+ put_hex2(row + 12, stage_events);
481
+ put_hex2(row + 16, hw);
482
+ bool ok = stage_events == stage_expect[stage];
483
+ put_str(row + 20, ok ? s"pass" : s"fail");
484
+ if (!ok) fault = stage + 1;
485
+ stage_events = 0;
486
+ t = 0;
487
+ if (stage == 2) {
488
+ done = true;
489
+ if (lo_false == 0) fault = 4; // the 8-bit trap must fire
490
+ RESULT = fault ? 2 : 1;
491
+ vic.color_border = fault ? 2 : 5;
492
+ put_str(Screen + 40, fault ? s"result fail" : s"result pass");
493
+ Screen[40 + 12] = 0x30 + fault;
494
+ } else
495
+ stage++;
496
+ }
497
+ }
498
+ return 0;
499
+ }
500
+ ```
501
+
502
+ ## Build
503
+
504
+ ```bash
505
+ oscar64 -tm=c64 -O2 -o=per-frame-hitbox.prg per-frame-hitbox.c
506
+ ```
507
+
508
+ Produces `per-frame-hitbox.prg`, 3,711 bytes. The sprite images are
509
+ built at run time at `$3000`, blocks 192 to 197, above the program. Then
510
+ run headless in VICE (PAL):
511
+
512
+ ```bash
513
+ GSETTINGS_SCHEMA_DIR=/opt/homebrew/share/glib-2.0/schemas x64sc -default -warp +sound \
514
+ +autostart-delay-random -autostartprgmode 1 -limitcycles 8000000 \
515
+ -exitscreenshot per-frame-hitbox.png -autostart per-frame-hitbox.prg
516
+ ```
517
+
518
+ Add `-model ntsc` for the NTSC picture. The three stages take 192 frames;
519
+ at 8,000,000 cycles the verdict is posted on both models.
520
+
521
+ ## Expected output
522
+
523
+ The scene, in sprite coordinates (Y is the sprite Y register):
524
+
525
+ | Actor | Sprite | Position | Moves | Group of its box |
526
+ |---|---|---|---|---|
527
+ | enemy bullet EB1 | 0 | Y 196, X 164 at stage frame 0 | left 2 px a frame | enemy bullet |
528
+ | enemy bullet EB2 | 1 | Y 164, X 380 | left 2 px a frame | enemy bullet |
529
+ | player bullet PB1 | 2 | Y 156, X 220 | right 2 px a frame | player bullet |
530
+ | player bullet PB2 | 3 | Y 160, X 20 | right 2 px a frame | player bullet |
531
+ | player P | 4 | X 100, Y 200 | pose per stage | player (and blade: player bullet) |
532
+ | enemy E1 | 5 | X 116, Y 200 | still | enemy |
533
+ | enemy E2 | 6 | X 300, Y 160 | still | enemy |
534
+
535
+ Event bits, set when a tested pair overlaps: `01` EB1 hits the player,
536
+ `02` E1 touches the player's body, `04` the blade hits E1, `08` PB1 hits
537
+ E2, `10` EB2 hits E2, `20` PB2 hits E2. Bits `02`, `10` and `20` must
538
+ never be set: E1's box never reaches the body, enemy bullet against enemy
539
+ is not a tested pair, and PB2's X differs from E2's by 256.
540
+
541
+ Measured in VICE x64sc 3.10 with the pinned command, text decoded
542
+ against the character ROM (rows 0 to 12, PAL):
543
+
544
+ ```text
545
+ PER-FRAME HITBOX STAGE 3 FRAME 00237
546
+ RESULT PASS 0
547
+ POSE EXP GOT D01E
548
+ STAND 09 09 77 PASS
549
+ CROUCH 08 08 77 PASS
550
+ ATTACK 0D 0D 77 PASS
551
+
552
+ PAIRS 28 TESTED 10 BOXES 8
553
+ EMIT MAX 02148 COLLIDE MAX 02037
554
+ PAIR 9-BIT HIT 096 MISS 028
555
+ PAIR 8-BIT HIT 059 MISS 025
556
+ LOW-BYTE-X FALSE HITS 033
557
+ P BOX L 106R 114T 202B 221
558
+ ```
559
+
560
+ NTSC reads the same except `FRAME 00266` and `EMIT MAX 02234`. The
561
+ border is palette index 5, (98, 213, 50) on PAL and (114, 189, 103) on
562
+ NTSC.
563
+
564
+ What the numbers say:
565
+
566
+ - Stand and crouch differ only in the player's frame. EB1 crosses the
567
+ player at the same X and Y in both; its box (Y 204 to 207) meets the
568
+ stand box (Y 202 to 220) and passes over the crouch box (Y 212 to 220).
569
+ - In the attack stage the blade's box (X 114 to 123) reaches E1's box
570
+ (X 120 to 135); the body's box (X 106 to 113) does not, so the event is
571
+ `04`, never `02`.
572
+ - `D01E` is `77` in every stage: sprites 0, 1, 2, 4, 5 and 6 touched
573
+ something, and sprite 3, PB2, touched nothing. In the crouch stage the
574
+ player's bit is set by its pixels touching E1's (the figures share X
575
+ 116 to 117), though no box of the player's overlaps anything; EB2's bit
576
+ is set by E2, a pair the game ignores. The register gives no pair and
577
+ no group.
578
+ - The low-byte test found 33 frames in which PB2 and E2 "overlap": PB2's
579
+ X low byte passes E2's (304 to 319, low bytes 48 to 63). The 9-bit test
580
+ found none.
581
+ - `PAIRS 28 TESTED 10`: eight boxes make 28 pairs, and the group masks
582
+ leave 10 to test.
583
+
584
+ Cycle figures, CIA1 timer B, interrupts masked. The per-pair figures are
585
+ 100 calls of a test through a function pointer, less 100 calls of an
586
+ empty function, divided by 100 and rounded down, with the screen blanked
587
+ (`$D011` bit 4 clear) so no badline steals a cycle. A hit runs all four
588
+ compares; a miss fails the first one. `COLLIDE MAX` is the worst frame of
589
+ the whole pass over the list and `EMIT MAX` the worst frame of filling it,
590
+ eight boxes. Both run straight after `vic_waitFrame()` returns at line
591
+ 256. The collide pass fits the vertical blank on both models and reads
592
+ 2,037 on both. The emit runs after it and on NTSC crosses into the first
593
+ badlines of the next frame, which is why NTSC reads 86 cycles more; the
594
+ PAL figure is the cost.
595
+
596
+ The pictures: `screenshots/per-frame-hitbox.png` (PAL) and
597
+ `screenshots/per-frame-hitbox-ntsc.png` (NTSC), the hold state at stage
598
+ frame 36 in the attack pose. Measured with PIL on both: every one of the
599
+ eight boxes has its outline in yellow (MC1, (255, 255, 70) on PAL,
600
+ (255, 248, 141) on NTSC) at exactly the pixels its box list entry names,
601
+ X + 8 across and Y + 1 − 16 down on PAL (− 28 on NTSC). Four pixels
602
+ sampled one step outside each box are not yellow, except the one left of
603
+ the blade, which is the body's right edge.
604
+ The four bullet outlines are complete (20 of 20 pixels each). Pixels
605
+ missing from the others are covered by a sprite in front: 7 of the
606
+ body's 50 by EB1 (light red), 2 of E1's 64 by the player's blade
607
+ (white), 17 of E2's 64 by PB1 (cyan) and EB2 (light red). The + 1 is the
608
+ sprite's first line, which is one raster line below its Y register, as
609
+ `sprite_sine_chain` measured. Each model run twice gives byte-identical
610
+ PNGs.
611
+
612
+ ## Why this works
613
+
614
+ The box belongs to the animation frame, not to the actor.
615
+ `box_first[f]` and `box_count[f]` pick a frame's entries in `box_rect`,
616
+ each an offset from the sprite's top-left corner plus a width and a
617
+ height, and a group bit in `box_group`. Changing the pose changes the
618
+ sprite pointer and the boxes together, so the collision shape can never
619
+ disagree with the picture: crouching lowers the box's top by 10 pixels,
620
+ and the attack frame adds a second box in another group.
621
+
622
+ `emit_boxes` runs where the sprite is positioned and writes screen-space
623
+ edges into parallel byte arrays: left and right X as a low and a high
624
+ byte (9 bits, right edge exclusive), top and bottom Y (one byte; a box
625
+ must end above Y 256), the group bit, the mask of groups it is tested
626
+ against and the owning actor. An actor that is not drawn emits nothing
627
+ and costs nothing in the collision pass. `collide` walks the pairs
628
+ `i < j` once; `bm[i] & bg[j]` is zero for friendly and irrelevant pairs,
629
+ which is how enemy bullets pass through enemies and a player's blade
630
+ cannot hit the player. The masks are symmetric, so each pair is tested
631
+ once.
632
+
633
+ `overlap9` tests Y first because it is one byte, then the two X
634
+ conditions, each with the high bytes compared first. `xlt(i, j)` is
635
+ "i's left edge is left of j's right edge". The broken variant that
636
+ compares only the low bytes wraps at 256: 304 has low byte 48, so a
637
+ bullet at X 52 lands "inside" an enemy at 304. That is the mechanism of
638
+ `sprite_x_high_bit_wrong_register` moved from the VIC to the game's own
639
+ arithmetic. `overlap8` halves every X at emit time, so all four compares
640
+ are one byte, at 2-pixel precision; it is 37 cycles cheaper per hit
641
+ here.
642
+
643
+ The per-pose events come out right because the draw that emits the boxes
644
+ is the draw that sets the sprite pointer. The collision pass runs at the
645
+ top of the next frame on the list the last draw left, so it tests what
646
+ was on screen.
647
+
648
+ Verified: compiled with Oscar64, run headless in VICE x64sc 3.10 with the
649
+ pinned command on PAL and NTSC, each twice with byte-identical PNGs; the
650
+ text was decoded against the character ROM and the box outlines located
651
+ by colour with PIL, not by eye.