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,1484 @@
1
+ ---
2
+ recipe: eight-way-scroll
3
+ toolchain: kickassembler
4
+ output_format: PRG
5
+ region: both
6
+ techniques: [eight_way_scroll_double_buffer, screen_double_buffer_d018, soft_scroll_h, soft_scroll_v, tile_map_render, irq_chain_table]
7
+ file_formats: [PRG]
8
+ uses_registers: [D011, D012, D016, D018, D019, D01A, D020, D021, DC06, DC07, DC0D, DC0F, DD0D]
9
+ uses_kernal: []
10
+ ---
11
+
12
+ <!-- doc-type: recipe -->
13
+
14
+ # KickAssembler — Eight-way tile scroll with a double-buffered screen matrix
15
+
16
+ ## Synopsis
17
+
18
+ Scrolls a 64 x 48 tile world through a 40 x 25 window in all eight
19
+ directions, one pixel every two fields, and measures what each field costs.
20
+ Sub-tile motion is the fine scroll in `$D016` and `$D011`. Whole-tile motion
21
+ is a redraw of the entire matrix into the screen page that is not on display,
22
+ followed by a `$D018` flip in the vertical blank, so no partly drawn matrix
23
+ is ever visible. The redraw is spread over at most four fields per crossing.
24
+
25
+ Colour RAM is not paged. Both halves are written in `irqColB` at raster 4
26
+ in four calls after each flip: two for the top half (rows 0 to 11) and two
27
+ for the bottom (rows 12 to 24). The first top call uses `BAND_FIRST = 5`
28
+ rows so the colour write finishes before row 6's badline even when
29
+ `irqColB` starts as late as raster 33 on NTSC. The remaining calls use
30
+ `BAND_MAX = 7`. During the four write fields, rows not yet updated show the
31
+ previous origin's colour: three displayed fields per tile crossing, about
32
+ one in four, measured at 60 ms PAL and 50 ms NTSC.
33
+
34
+ A camera path table walks the eight directions in turn, thirty-two fields
35
+ each. CIA 1 timer B measures every field. Row 1 of the screen shows the
36
+ camera position, the leg index, waited flips, the worst field in cycles,
37
+ late applies, skipped preps and first-band deadline violations. `$02FF` is
38
+ `$01` and the border green when the marker tile is where the camera says it
39
+ should be, no flip waited, no late apply occurred, no prep was skipped, no
40
+ first-band call missed the row-6 badline, and the worst field is under the
41
+ stated budget; `$02` and red otherwise. PAL and NTSC.
42
+
43
+ ## Source
44
+
45
+ ```asm
46
+ // Eight-way tile scroll over a double-buffered screen matrix.
47
+ //
48
+ // A 64 x 48 tile world is viewed through a 40 x 25 window. The camera
49
+ // moves one pixel every two fields along a fixed eight-leg path. Sub-tile
50
+ // motion is the fine scroll in $D016 and $D011; whole-tile motion is a
51
+ // redraw of the whole matrix into the page that is not on screen,
52
+ // followed by a $D018 flip in the vertical blank.
53
+ //
54
+ // Colour RAM is not paged. Both halves are written in irqColB at raster 4.
55
+ // The top half (rows 0..SPLIT-1) is written in two calls after the flip: the
56
+ // first uses BAND_FIRST rows, which fits safely even when irqColB starts as
57
+ // late as raster 33 on NTSC; the second uses BAND_MAX. The bottom half
58
+ // (rows SPLIT..24) follows in two more calls at BAND_MAX rows each. Four
59
+ // colour calls span the same four fields as the four matrix-redraw preps.
60
+
61
+ .const WORLD = $2000 // 64 x 48 tile codes
62
+ .const WCOL = $2c00 // 64 x 48 colour nybbles
63
+ .const WORLD_W = 64
64
+ .const WORLD_H = 48
65
+ .const SCR0 = $0400
66
+ .const SCR1 = $0800
67
+ .const MAX_COL = WORLD_W - 40 // 24
68
+ .const MAX_ROW = WORLD_H - 25 // 23
69
+ .const MARK_X = 30 // marker tile, world coordinates
70
+ .const MARK_Y = 24
71
+ .const MARK_CH = $2a // '*'
72
+ .const SPLIT = 12 // first row of the bottom half
73
+ .const RAS_COLB = 4 // bottom colour half: beam above row 12
74
+ .const RAS_PREP = 152 // decide and top colour half: beam below row 12
75
+ .const RAS_APPLY = 251 // registers: vertical blank
76
+ .const CHECK_AT = 320 // frame at which the verdict is published
77
+ .const BAND_MAX = 7 // matrix rows drawn per prep field (any kind)
78
+ .const BAND_FIRST = 5 // top-half colour rows on the first call after a flip
79
+ .const BUDGET = 18000 // worst-field cycle budget
80
+
81
+ // --- zero page -------------------------------------------------------
82
+ .const camx = $20 // camera in world pixels
83
+ .const camy = $21
84
+ .const ztmp2 = $22
85
+ .const visCol = $24 // tile origin of the matrix on screen
86
+ .const visRow = $25
87
+ .const offCol = $26 // tile origin the spare matrix holds
88
+ .const offRow = $27
89
+ .const pgVis = $28 // 0 = $0400 on screen, 1 = $0800
90
+ .const rdState = $29 // 1 while the spare matrix is being redrawn
91
+ .const offOK = $2a
92
+ .const sgnx = $2b
93
+ .const sgny = $2c
94
+ .const legIdx = $2d
95
+ .const legCnt = $2e
96
+ .const zrow = $2f
97
+ .const zend = $30
98
+ .const zv = $31 // word
99
+ .const ztmp = $33
100
+ .const t1 = $34
101
+ .const t2 = $35
102
+ .const wptr = $36 // word
103
+ .const wx = $38
104
+ .const wy = $39
105
+ .const wantCol = $3a
106
+ .const wantRow = $3b
107
+ .const tgtCol = $3c
108
+ .const tgtRow = $3d
109
+ .const stalls = $3e // flips wanted while the spare page was unready
110
+ .const framesX = $3f // frames until the next column crossing
111
+ .const framesY = $40
112
+ .const deadlin = $41
113
+ .const dcol = $42
114
+ .const drow = $43
115
+ .const colCol = $44 // origin the colour halves are being written for
116
+ .const colRow = $45
117
+ .const colPend = $46
118
+ .const pendVM = $47 // register values waiting for the blank
119
+ .const pendD16 = $48
120
+ .const pendD11 = $49
121
+ .const frameL = $4a
122
+ .const frameH = $4b
123
+ .const markBad = $4c
124
+ .const tNow = $4d // word
125
+ .const tEnt = $4f // word
126
+ .const frameAc = $51 // word
127
+ .const worst = $53 // word
128
+ .const colTop = $55 // word, raster at which the top half finished
129
+ .const colBot = $57 // word
130
+ .const cbSrcHi = $59
131
+ .const cbDstHi = $5a
132
+ .const cbCol = $5b
133
+ .const cbRow = $5c
134
+ .const pnI = $5d
135
+ .const pnD = $5e
136
+ .const pnTL = $5f
137
+ .const srcp = $60 // word
138
+ .const cptr = $62 // word
139
+ .const done = $64
140
+ .const fullCnt = $65
141
+ .const rdNext = $66 // next view row the spare matrix still needs
142
+ .const colWork = $67 // this field has already rewritten a colour half
143
+ .const statIdx = $68
144
+ .const rowsF = $69
145
+ .const wDbg = $6a
146
+ .const lateCol = $6b // colour halves that missed the beam
147
+ .const prepRas = $6c
148
+ .const lateApply = $6d // apply entries that fired with raster < RAS_APPLY
149
+ .const skipPrep = $6e // colB entries with no preceding prep
150
+ .const prepFired = $6f // set in irqPrep, checked and cleared in irqColB
151
+ .const stepParity = $70 // toggles each field; pathStep runs when 0
152
+ .const colTopCur = $71 // cursor into top colour half (0..SPLIT; SPLIT=done)
153
+ .const colBotCur = $72 // cursor into bottom colour half (SPLIT..25; 25=done)
154
+ .const b1Dead = $73 // first top-half calls that missed the row-6 badline
155
+
156
+ .pc = $0801 "BASIC"
157
+ :BasicUpstart2(main)
158
+
159
+ // The program sits at $1000. In VIC bank 0 the chip sees the character
160
+ // generator ROM at $1000-$1FFF, so that RAM is invisible to it and free
161
+ // for code. $0800 is the second screen matrix and must stay clear.
162
+ .pc = $1000 "Main"
163
+
164
+ main:
165
+ sei
166
+ lda #$35 // RAM under BASIC and KERNAL, I/O visible
167
+ sta $01
168
+
169
+ lda #$7f // silence both CIAs
170
+ sta $dc0d
171
+ sta $dd0d
172
+ lda $dc0d
173
+ lda $dd0d
174
+
175
+ jsr genWorld
176
+ jsr clearScreens
177
+ jsr initStatus
178
+
179
+ lda #$00
180
+ sta $d020
181
+ sta $d021
182
+
183
+ // Camera start. The sub-tile phase is a design choice, not an
184
+ // arbitrary one: with camx and camy both congruent to 2 mod 8
185
+ // the gap between successive tile crossings never falls below
186
+ // three frames, which is what lets the redraw be split in two.
187
+ lda #66
188
+ sta camx
189
+ sta camy
190
+ lda #8
191
+ sta visCol
192
+ sta visRow
193
+ sta offCol
194
+ sta offRow
195
+ lda #0
196
+ sta pgVis
197
+ sta rdState
198
+ sta offOK
199
+ sta sgnx
200
+ sta sgny
201
+ sta stalls
202
+ sta colPend
203
+ sta frameL
204
+ sta frameH
205
+ sta markBad
206
+ sta frameAc
207
+ sta frameAc+1
208
+ sta worst
209
+ sta worst+1
210
+ sta colTop
211
+ sta colTop+1
212
+ sta colBot
213
+ sta colBot+1
214
+ sta done
215
+ sta fullCnt
216
+ sta rdNext
217
+ sta colWork
218
+ sta statIdx
219
+ sta rowsF
220
+ sta wDbg
221
+ sta lateCol
222
+ sta prepRas
223
+ sta lateApply
224
+ sta skipPrep
225
+ sta b1Dead
226
+ sta stepParity
227
+ lda #1
228
+ sta prepFired // so the first irqColB does not count as a skip
229
+ lda #SPLIT
230
+ sta colTopCur // SPLIT = no top-half colour pending
231
+ lda #25
232
+ sta colBotCur // 25 = no bottom-half colour pending
233
+ lda #7
234
+ sta legIdx // so the first step loads leg 0
235
+ lda #1
236
+ sta legCnt
237
+
238
+ // Draw the opening view straight into the page that is on
239
+ // screen, and its colour with it.
240
+ lda #1
241
+ sta pgVis // aim drawMatrix at page 0
242
+ lda #0
243
+ sta zrow
244
+ lda #24
245
+ sta zend
246
+ jsr drawMatrix
247
+ lda #0
248
+ sta pgVis
249
+ lda visCol
250
+ sta colCol
251
+ lda visRow
252
+ sta colRow
253
+ lda #0
254
+ sta zrow
255
+ lda #24
256
+ sta zend
257
+ jsr drawColour
258
+
259
+ lda #$14 // matrix $0400, charset ROM at $1000
260
+ sta $d018
261
+ sta pendVM
262
+ lda #7
263
+ sta pendD16
264
+ lda #$17
265
+ sta pendD11
266
+ sta $d011
267
+ lda #7
268
+ sta $d016
269
+
270
+ lda #$ff // free-running frame timer
271
+ sta $dc06
272
+ sta $dc07
273
+ lda #$11
274
+ sta $dc0f
275
+
276
+ lda #<irqColB
277
+ sta $fffe
278
+ lda #>irqColB
279
+ sta $ffff
280
+ lda #RAS_COLB
281
+ sta $d012
282
+ lda #$01
283
+ sta $d01a
284
+ lda #$ff
285
+ sta $d019
286
+ cli
287
+
288
+ idle: jmp idle
289
+
290
+ // --- bottom colour half, beam above row 13 ---------------------------
291
+ irqColB:
292
+ pha
293
+ txa
294
+ pha
295
+ tya
296
+ pha
297
+ cld // the D flag is not cleared on IRQ entry
298
+ lda #$ff
299
+ sta $d019
300
+ lda #<irqPrep // arm the next stage first: this handler
301
+ sta $fffe // can outlast its own raster line
302
+ lda #>irqPrep
303
+ sta $ffff
304
+ lda $d011 // the split follows YSCROLL: row 12 ends at
305
+ and #7 // raster 151 + YSCROLL, so the top colour
306
+ clc // half may start one line after that
307
+ adc #RAS_PREP
308
+ sta $d012
309
+ sta prepRas
310
+
311
+ lda #0 // a new field: restart the frame timer
312
+ sta frameAc
313
+ sta frameAc+1
314
+ sta colWork
315
+ sta rowsF
316
+ jsr markIn
317
+
318
+ // skipPrep: if irqPrep never ran since the last irqColB, count it.
319
+ lda prepFired
320
+ bne cb_prepok
321
+ inc skipPrep
322
+ cb_prepok:
323
+ lda #0
324
+ sta prepFired
325
+
326
+ // Band-limited colour write: at most BAND_MAX rows per irqColB call.
327
+ // By limiting each call, irqColB always finishes well before irqPrep's
328
+ // raster even when irqColB starts late (due to irqApply overrunning the
329
+ // NTSC blank). Phase 1 writes rows from colTopCur toward SPLIT; phase 2
330
+ // writes rows from colBotCur toward 24.
331
+ lda colPend
332
+ bne cb_has_pend
333
+ jmp cb_none
334
+ cb_has_pend:
335
+ cmp #1
336
+ bne cb_bottom
337
+ // Phase 1: top half. BAND_FIRST rows on the first call (colTopCur == 0)
338
+ // so the colour finishes before the row-6 badline even when irqColB
339
+ // starts as late as raster 33 on NTSC. Subsequent calls use BAND_MAX.
340
+ lda colTopCur
341
+ sta zrow
342
+ lda colTopCur
343
+ beq cb_top_first
344
+ clc
345
+ adc #BAND_MAX-1
346
+ jmp cb_top_clamp
347
+ cb_top_first:
348
+ lda #BAND_FIRST-1
349
+ cb_top_clamp:
350
+ cmp #SPLIT-1
351
+ bcc !+
352
+ lda #SPLIT-1
353
+ !: sta zend
354
+ jsr drawColour
355
+ // First top-half call (zend == BAND_FIRST-1): check it finished before
356
+ // row 6's badline (96 + YSCROLL). If not, count a deadline violation.
357
+ lda zend
358
+ cmp #BAND_FIRST-1
359
+ bne cb_top_nodead
360
+ jsr readRaster
361
+ lda zv+1
362
+ bne cb_top_nodead // raster above 255: in blank, on time
363
+ lda $d011
364
+ and #7
365
+ clc
366
+ adc #96
367
+ cmp zv // 96+YSCROLL >= raster: on time
368
+ bcs cb_top_nodead
369
+ inc b1Dead
370
+ cb_top_nodead:
371
+ inc colWork
372
+ lda zend
373
+ cmp #SPLIT-1
374
+ bcc cb_top_more
375
+ lda #SPLIT // top half complete; start bottom half
376
+ sta colTopCur
377
+ lda #2
378
+ sta colPend
379
+ jmp cb_none
380
+ cb_top_more:
381
+ lda zend
382
+ clc
383
+ adc #1
384
+ sta colTopCur // resume here next call
385
+ jmp cb_none
386
+ cb_bottom:
387
+ // Phase 2: bottom half, BAND_MAX rows per call.
388
+ lda colBotCur
389
+ sta zrow
390
+ lda colBotCur
391
+ clc
392
+ adc #BAND_MAX-1
393
+ cmp #24
394
+ bcc !+
395
+ lda #24
396
+ !: sta zend
397
+ jsr drawColour
398
+ jsr readRaster // timing check: first band must land before row 12
399
+ lda zv
400
+ sta colBot
401
+ lda zv+1
402
+ sta colBot+1
403
+ bne cb_late // past raster 255 is far too late
404
+ lda $d011
405
+ and #7
406
+ clc
407
+ adc #144 // row 12 starts at raster 144 + YSCROLL
408
+ cmp colBot
409
+ bcs cb_intime
410
+ cb_late:
411
+ inc lateCol
412
+ cb_intime:
413
+ inc colWork
414
+ lda zend
415
+ cmp #24
416
+ bcc cb_bot_more
417
+ lda #25 // bottom half complete
418
+ sta colBotCur
419
+ lda #0
420
+ sta colPend
421
+ jmp cb_none
422
+ cb_bot_more:
423
+ lda zend
424
+ clc
425
+ adc #1
426
+ sta colBotCur // resume here next call
427
+ cb_none:
428
+ jsr markOut
429
+ jmp irqExit
430
+
431
+ // --- decide the next field, top colour half --------------------------
432
+ irqPrep:
433
+ pha
434
+ txa
435
+ pha
436
+ tya
437
+ pha
438
+ cld // the D flag is not cleared on IRQ entry
439
+ lda #$ff
440
+ sta $d019
441
+ lda #<irqApply
442
+ sta $fffe
443
+ lda #>irqApply
444
+ sta $ffff
445
+ lda #RAS_APPLY
446
+ sta $d012
447
+ jsr markIn
448
+
449
+ lda #1 // signal that prep ran (for skipPrep detection)
450
+ sta prepFired
451
+
452
+ // Half-speed camera: advance one pixel every two fields so the
453
+ // redraw always completes before the next tile crossing.
454
+ lda stepParity
455
+ eor #1
456
+ sta stepParity
457
+ beq pr_step_skip
458
+ jsr pathStep
459
+ pr_step_skip:
460
+
461
+ lda camx
462
+ lsr
463
+ lsr
464
+ lsr
465
+ sta wantCol
466
+ lda camy
467
+ lsr
468
+ lsr
469
+ lsr
470
+ sta wantRow
471
+
472
+ // Flip only if the wanted origin has moved and the spare page
473
+ // already holds exactly that origin.
474
+ lda wantCol
475
+ cmp visCol
476
+ bne pr_try
477
+ lda wantRow
478
+ cmp visRow
479
+ beq pr_fine
480
+ pr_try:
481
+ lda offOK
482
+ beq pr_stall
483
+ lda offCol
484
+ cmp wantCol
485
+ bne pr_stall
486
+ lda offRow
487
+ cmp wantRow
488
+ bne pr_stall
489
+
490
+ lda pgVis // the flip is queued for the blank
491
+ eor #1
492
+ sta pgVis
493
+ tax
494
+ lda d018T,x
495
+ sta pendVM
496
+ lda offCol
497
+ sta visCol
498
+ sta colCol
499
+ lda offRow
500
+ sta visRow
501
+ sta colRow
502
+ lda #0
503
+ sta offOK
504
+ sta colTopCur // start top-half colour write from row 0
505
+ lda #SPLIT
506
+ sta colBotCur // bottom-half cursor starts at SPLIT
507
+ lda #1
508
+ sta colPend // irqColB will write top half (phase 1), then bottom (phase 2)
509
+ jmp pr_fine
510
+ pr_stall:
511
+ lda stalls
512
+ cmp #255
513
+ beq pr_fine
514
+ inc stalls
515
+
516
+ pr_fine:
517
+ // Fine scroll for the origin that will be on screen. Both
518
+ // registers are written whole, never read-modify-write, so CSEL,
519
+ // MCM and the raster high bit cannot be clobbered.
520
+ lda visCol
521
+ asl
522
+ asl
523
+ asl
524
+ sta ztmp
525
+ lda camx
526
+ sec
527
+ sbc ztmp
528
+ jsr clamp07
529
+ sta ztmp
530
+ lda #7
531
+ sec
532
+ sbc ztmp
533
+ sta pendD16
534
+
535
+ lda visRow
536
+ asl
537
+ asl
538
+ asl
539
+ sta ztmp
540
+ lda camy
541
+ sec
542
+ sbc ztmp
543
+ jsr clamp07
544
+ sta ztmp
545
+ lda #7
546
+ sec
547
+ sbc ztmp
548
+ ora #$10 // DEN on, RSEL 0, raster high bit clear
549
+ sta pendD11
550
+
551
+ // Frames until each axis crosses a tile boundary.
552
+ lda sgnx
553
+ beq fx_never
554
+ bmi fx_left
555
+ lda camx
556
+ and #7
557
+ sta ztmp
558
+ lda #8
559
+ sec
560
+ sbc ztmp
561
+ jmp fx_put
562
+ fx_left:
563
+ lda camx
564
+ and #7
565
+ clc
566
+ adc #1
567
+ jmp fx_put
568
+ fx_never:
569
+ lda #$7f
570
+ fx_put: sta framesX
571
+
572
+ lda sgny
573
+ beq fy_never
574
+ bmi fy_up
575
+ lda camy
576
+ and #7
577
+ sta ztmp
578
+ lda #8
579
+ sec
580
+ sbc ztmp
581
+ jmp fy_put
582
+ fy_up:
583
+ lda camy
584
+ and #7
585
+ clc
586
+ adc #1
587
+ jmp fy_put
588
+ fy_never:
589
+ lda #$7f
590
+ fy_put: sta framesY
591
+
592
+ lda framesX
593
+ cmp framesY
594
+ bcc !+
595
+ lda framesY
596
+ !: sta deadlin
597
+
598
+ // The spare page is drawn for the origin after the next
599
+ // crossing, which is one axis unless both cross together.
600
+ lda #0
601
+ sta dcol
602
+ sta drow
603
+ lda framesX
604
+ cmp framesY
605
+ beq tg_both
606
+ bcc tg_x
607
+ lda sgny
608
+ sta drow
609
+ jmp tg_have
610
+ tg_x: lda sgnx
611
+ sta dcol
612
+ jmp tg_have
613
+ tg_both:
614
+ lda sgnx
615
+ sta dcol
616
+ lda sgny
617
+ sta drow
618
+ tg_have:
619
+ lda visCol
620
+ clc
621
+ adc dcol
622
+ bpl !+
623
+ lda #0
624
+ !: cmp #MAX_COL+1
625
+ bcc !+
626
+ lda #MAX_COL
627
+ !: sta tgtCol
628
+ lda visRow
629
+ clc
630
+ adc drow
631
+ bpl !+
632
+ lda #0
633
+ !: cmp #MAX_ROW+1
634
+ bcc !+
635
+ lda #MAX_ROW
636
+ !: sta tgtRow
637
+
638
+ lda offCol
639
+ cmp tgtCol
640
+ bne pr_start
641
+ lda offRow
642
+ cmp tgtRow
643
+ bne pr_start
644
+ jmp pr_step
645
+ pr_start:
646
+ lda tgtCol
647
+ sta offCol
648
+ lda tgtRow
649
+ sta offRow
650
+ lda #1
651
+ sta rdState
652
+ lda #0
653
+ sta rdNext
654
+ sta offOK
655
+ pr_step:
656
+ lda rdState
657
+ beq pr_done
658
+ lda rdNext
659
+ sta zrow
660
+ lda #BAND_MAX-1 // at most BAND_MAX rows per field regardless of kind
661
+ clc
662
+ adc rdNext
663
+ cmp #24
664
+ bcc !+
665
+ lda #24
666
+ !: sta zend
667
+ jsr drawMatrix
668
+ jsr countRows
669
+ lda zend
670
+ cmp #24
671
+ beq rs_ready
672
+ clc
673
+ adc #1
674
+ sta rdNext
675
+ jmp pr_done
676
+ rs_ready:
677
+ lda #0
678
+ sta rdState
679
+ lda #1
680
+ sta offOK
681
+ pr_done:
682
+ jsr markOut
683
+ jmp irqExit
684
+
685
+ // --- apply the registers in the blank --------------------------------
686
+ irqApply:
687
+ pha
688
+ txa
689
+ pha
690
+ tya
691
+ pha
692
+ cld // the D flag is not cleared on IRQ entry
693
+ lda #$ff
694
+ sta $d019
695
+ lda #<irqColB
696
+ sta $fffe
697
+ lda #>irqColB
698
+ sta $ffff
699
+ lda #RAS_COLB
700
+ sta $d012
701
+ jsr markIn
702
+
703
+ // Late-apply detection: if irqPrep overran, the raster has already
704
+ // advanced past RAS_APPLY into the next field. Raster bit 8 is clear
705
+ // for rasters 0-255; a raster below RAS_APPLY here means we are in
706
+ // the next field. The CPU defers the IRQ until irqPrep's rti, so a
707
+ // late apply is always at a raster below 251 with bit 8 clear.
708
+ jsr readRaster
709
+ lda zv+1
710
+ bne ap_ontime // raster > 255: still in the blank, on time
711
+ lda zv
712
+ cmp #RAS_APPLY
713
+ bcs ap_ontime
714
+ inc lateApply
715
+ ap_ontime:
716
+
717
+ lda pendVM
718
+ sta $d018
719
+ lda pendD16
720
+ sta $d016
721
+ lda pendD11
722
+ sta $d011
723
+
724
+ jsr checkMarker
725
+ jsr buildStatus // one field per frame keeps the readout cheap
726
+ jsr stampStatus
727
+
728
+ inc frameL
729
+ bne !+
730
+ inc frameH
731
+ !:
732
+ jsr markOut
733
+
734
+ lda frameAc // keep the worst field seen
735
+ cmp worst
736
+ lda frameAc+1
737
+ sbc worst+1
738
+ bcc ap_nw
739
+ lda frameAc
740
+ sta worst
741
+ lda frameAc+1
742
+ sta worst+1
743
+ lda #0
744
+ ldx colWork
745
+ beq dbg_no
746
+ dbg_l: clc
747
+ adc #100
748
+ dex
749
+ bne dbg_l
750
+ dbg_no: clc
751
+ adc rowsF
752
+ sta wDbg
753
+ ap_nw:
754
+ lda done
755
+ bne ap_end
756
+ lda frameL
757
+ cmp #<CHECK_AT
758
+ lda frameH
759
+ sbc #>CHECK_AT
760
+ bcc ap_end
761
+ jsr verdict
762
+ lda #1
763
+ sta done
764
+ ap_end:
765
+ irqExit:
766
+ pla
767
+ tay
768
+ pla
769
+ tax
770
+ pla
771
+ rti
772
+
773
+ // --- verdict ---------------------------------------------------------
774
+ verdict:
775
+ lda markBad
776
+ bne vd_fail
777
+ lda stalls
778
+ bne vd_fail
779
+ lda lateCol
780
+ bne vd_fail
781
+ lda lateApply
782
+ bne vd_fail
783
+ lda skipPrep
784
+ bne vd_fail
785
+ lda b1Dead
786
+ bne vd_fail
787
+ lda worst
788
+ cmp #<BUDGET
789
+ lda worst+1
790
+ sbc #>BUDGET
791
+ bcs vd_fail
792
+ lda #$01
793
+ sta $02ff
794
+ lda #$05 // green
795
+ sta $d020
796
+ rts
797
+ vd_fail:
798
+ lda #$02
799
+ sta $02ff
800
+ lda #$02 // red
801
+ sta $d020
802
+ rts
803
+
804
+ // The marker tile must sit where the camera origin says it does.
805
+ checkMarker:
806
+ lda #MARK_X
807
+ sec
808
+ sbc visCol
809
+ sta ztmp
810
+ lda #MARK_Y
811
+ sec
812
+ sbc visRow
813
+ tax
814
+ lda rowLoT,x
815
+ clc
816
+ adc ztmp
817
+ sta srcp
818
+ lda rowHiT,x
819
+ adc #0
820
+ ldx pgVis
821
+ clc
822
+ adc pageHiT,x
823
+ sta srcp+1
824
+ ldy #0
825
+ lda (srcp),y
826
+ cmp #MARK_CH
827
+ beq cm_ok
828
+ lda #1
829
+ sta markBad
830
+ cm_ok: rts
831
+
832
+ // --- camera path -----------------------------------------------------
833
+ pathStep:
834
+ dec legCnt
835
+ bne ps_move
836
+ inc legIdx
837
+ lda legIdx
838
+ cmp #8
839
+ bcc !+
840
+ lda #0
841
+ sta legIdx
842
+ !: tax
843
+ lda pathDX,x
844
+ sta sgnx
845
+ lda pathDY,x
846
+ sta sgny
847
+ lda pathLen,x
848
+ sta legCnt
849
+ ps_move:
850
+ lda camx
851
+ clc
852
+ adc sgnx
853
+ sta camx
854
+ lda camy
855
+ clc
856
+ adc sgny
857
+ sta camy
858
+ rts
859
+
860
+ clamp07:
861
+ bpl !+
862
+ lda #0
863
+ rts
864
+ !: cmp #8
865
+ bcc !+
866
+ lda #7
867
+ !: rts
868
+
869
+ // --- band copies -----------------------------------------------------
870
+ // Rows zrow..zend of the view, from the world into the spare matrix.
871
+ drawMatrix:
872
+ lda pgVis
873
+ eor #1
874
+ tax
875
+ lda pageHiT,x
876
+ sta cbDstHi
877
+ lda #>WORLD
878
+ sta cbSrcHi
879
+ lda offCol
880
+ sta cbCol
881
+ lda offRow
882
+ sta cbRow
883
+ jmp copyBand
884
+
885
+ // Rows zrow..zend of the view, from the colour world into colour RAM.
886
+ drawColour:
887
+ lda #$d8
888
+ sta cbDstHi
889
+ lda #>WCOL
890
+ sta cbSrcHi
891
+ lda colCol
892
+ sta cbCol
893
+ lda colRow
894
+ sta cbRow
895
+ jmp copyBand
896
+
897
+ copyBand:
898
+ cb_row:
899
+ lda cbRow
900
+ clc
901
+ adc zrow
902
+ sta zv
903
+ lsr
904
+ lsr
905
+ clc
906
+ adc cbSrcHi
907
+ sta cpSrc+2
908
+ lda zv
909
+ asl
910
+ asl
911
+ asl
912
+ asl
913
+ asl
914
+ asl
915
+ clc
916
+ adc cbCol
917
+ sta cpSrc+1
918
+ ldx zrow
919
+ lda rowLoT,x
920
+ sta cpDst+1
921
+ lda rowHiT,x
922
+ clc
923
+ adc cbDstHi
924
+ sta cpDst+2
925
+ jsr copyRow
926
+ lda zrow
927
+ cmp zend
928
+ beq cb_done
929
+ inc zrow
930
+ jmp cb_row
931
+ cb_done:
932
+ rts
933
+
934
+ copyRow:
935
+ ldx #39
936
+ cr_l:
937
+ cpSrc: lda $ffff,x
938
+ cpDst: sta $ffff,x
939
+ dex
940
+ bpl cr_l
941
+ rts
942
+
943
+ // --- frame timer -----------------------------------------------------
944
+ readTimer:
945
+ rt_l: lda $dc07
946
+ sta tNow+1
947
+ lda $dc06
948
+ sta tNow
949
+ lda $dc07
950
+ cmp tNow+1
951
+ bne rt_l
952
+ rts
953
+
954
+ markIn:
955
+ jsr readTimer
956
+ lda tNow
957
+ sta tEnt
958
+ lda tNow+1
959
+ sta tEnt+1
960
+ rts
961
+
962
+ markOut:
963
+ jsr readTimer
964
+ lda tEnt // the timer counts down
965
+ sec
966
+ sbc tNow
967
+ sta ztmp
968
+ lda tEnt+1
969
+ sbc tNow+1
970
+ sta ztmp2
971
+ lda frameAc
972
+ clc
973
+ adc ztmp
974
+ sta frameAc
975
+ lda frameAc+1
976
+ adc ztmp2
977
+ sta frameAc+1
978
+ rts
979
+
980
+ // Did the top colour half finish before the beam reached row 0 of the
981
+ // next field? A finish raster at or above the one the handler started on
982
+ // is still inside this field and always in time.
983
+ topLate:
984
+ lda colTop+1
985
+ bne tl_ok
986
+ lda colTop
987
+ cmp prepRas
988
+ bcs tl_ok
989
+ lda pendD11
990
+ and #7
991
+ clc
992
+ adc #48
993
+ cmp colTop
994
+ bcs tl_ok
995
+ inc lateCol
996
+ tl_ok: rts
997
+
998
+ countRows:
999
+ lda zend
1000
+ sec
1001
+ sbc rdNext
1002
+ clc
1003
+ adc #1
1004
+ clc
1005
+ adc rowsF
1006
+ sta rowsF
1007
+ rts
1008
+
1009
+ // The nine-bit raster line, into zv.
1010
+ readRaster:
1011
+ lda $d012
1012
+ sta zv
1013
+ lda $d011
1014
+ and #$80
1015
+ beq !+
1016
+ lda #1
1017
+ sta zv+1
1018
+ rts
1019
+ !: lda #0
1020
+ sta zv+1
1021
+ rts
1022
+
1023
+ // --- status line -----------------------------------------------------
1024
+ // Row 1 is the topmost matrix row the display window shows in full at
1025
+ // every YSCROLL value, so the digits never slide under the border.
1026
+ initStatus:
1027
+ ldx #39
1028
+ lda #$20
1029
+ !: sta statBuf,x
1030
+ dex
1031
+ bpl !-
1032
+ lda #$18 // X
1033
+ sta statBuf+1
1034
+ lda #$19 // Y
1035
+ sta statBuf+6
1036
+ lda #$0c // L
1037
+ sta statBuf+11
1038
+ lda #$13 // S
1039
+ sta statBuf+15
1040
+ lda #$17 // W
1041
+ sta statBuf+19
1042
+ lda #$14 // T
1043
+ sta statBuf+25
1044
+ lda #$02 // B
1045
+ sta statBuf+29
1046
+ lda #$04 // D
1047
+ sta statBuf+34
1048
+ rts
1049
+
1050
+ buildStatus:
1051
+ ldx statIdx
1052
+ ldy stSrc,x
1053
+ lda $0000,y
1054
+ sta zv
1055
+ lda #0
1056
+ sta zv+1
1057
+ lda stWrd,x
1058
+ beq !+
1059
+ iny
1060
+ lda $0000,y
1061
+ sta zv+1
1062
+ !: lda stDig,x
1063
+ sta pnI
1064
+ ldy stPos,x
1065
+ jsr putNum
1066
+ inc statIdx
1067
+ lda statIdx
1068
+ cmp #8
1069
+ bcc !+
1070
+ lda #0
1071
+ sta statIdx
1072
+ !: rts
1073
+
1074
+ putByte:
1075
+ sta zv
1076
+ lda #0
1077
+ sta zv+1
1078
+ lda #2
1079
+ sta pnI
1080
+ // fall through
1081
+
1082
+ // zv = value, Y = position in statBuf, pnI = first power of ten to use
1083
+ putNum:
1084
+ pn_dig: lda #0
1085
+ sta pnD
1086
+ pn_sub: ldx pnI
1087
+ lda zv
1088
+ sec
1089
+ sbc pwTabLo,x
1090
+ sta pnTL
1091
+ lda zv+1
1092
+ sbc pwTabHi,x
1093
+ bcc pn_out
1094
+ sta zv+1
1095
+ lda pnTL
1096
+ sta zv
1097
+ inc pnD
1098
+ jmp pn_sub
1099
+ pn_out: lda pnD
1100
+ clc
1101
+ adc #$30
1102
+ sta statBuf,y
1103
+ iny
1104
+ inc pnI
1105
+ lda pnI
1106
+ cmp #5
1107
+ bne pn_dig
1108
+ rts
1109
+
1110
+ stampStatus:
1111
+ ldx #39
1112
+ ss_l: lda statBuf,x
1113
+ sta SCR0+40,x
1114
+ sta SCR1+40,x
1115
+ lda #$01
1116
+ sta $d800+40,x
1117
+ dex
1118
+ bpl ss_l
1119
+ rts
1120
+
1121
+ // --- one-off setup ---------------------------------------------------
1122
+ clearScreens:
1123
+ ldx #0
1124
+ lda #$20
1125
+ !: sta SCR0,x
1126
+ sta SCR0+$100,x
1127
+ sta SCR0+$200,x
1128
+ sta SCR0+$300,x
1129
+ sta SCR1,x
1130
+ sta SCR1+$100,x
1131
+ sta SCR1+$200,x
1132
+ sta SCR1+$300,x
1133
+ inx
1134
+ bne !-
1135
+ rts
1136
+
1137
+ genWorld:
1138
+ lda #<WORLD
1139
+ sta wptr
1140
+ lda #>WORLD
1141
+ sta wptr+1
1142
+ lda #<WCOL
1143
+ sta cptr
1144
+ lda #>WCOL
1145
+ sta cptr+1
1146
+ lda #0
1147
+ sta wy
1148
+ gw_row:
1149
+ lda #0
1150
+ sta wx
1151
+ gw_col:
1152
+ lda wx
1153
+ and #7
1154
+ sta t1
1155
+ lda wy
1156
+ and #7
1157
+ sta t2
1158
+ lda #$20
1159
+ sta ztmp
1160
+ lda t2
1161
+ bne gw_x
1162
+ lda #$40 // horizontal rule
1163
+ sta ztmp
1164
+ gw_x:
1165
+ lda t1
1166
+ bne gw_b
1167
+ lda t2
1168
+ bne gw_v
1169
+ lda #$51 // ball at a grid crossing
1170
+ sta ztmp
1171
+ jmp gw_b
1172
+ gw_v:
1173
+ lda #$5d // vertical rule
1174
+ sta ztmp
1175
+ gw_b:
1176
+ lda wx
1177
+ beq gw_edge
1178
+ cmp #WORLD_W-1
1179
+ beq gw_edge
1180
+ lda wy
1181
+ beq gw_edge
1182
+ cmp #WORLD_H-1
1183
+ beq gw_edge
1184
+ jmp gw_put
1185
+ gw_edge:
1186
+ lda #$a0 // solid world edge
1187
+ sta ztmp
1188
+ gw_put:
1189
+ lda ztmp // colour follows the tile code
1190
+ ldx #2 // red edge
1191
+ cmp #$a0
1192
+ beq gw_pc
1193
+ ldx #1 // white ball
1194
+ cmp #$51
1195
+ beq gw_pc
1196
+ ldx #11 // dark grey background
1197
+ cmp #$20
1198
+ beq gw_pc
1199
+ ldx #14 // light blue rules
1200
+ gw_pc:
1201
+ ldy #0
1202
+ lda ztmp
1203
+ sta (wptr),y
1204
+ txa
1205
+ sta (cptr),y
1206
+ inc wptr
1207
+ bne !+
1208
+ inc wptr+1
1209
+ !: inc cptr
1210
+ bne !+
1211
+ inc cptr+1
1212
+ !: inc wx
1213
+ lda wx
1214
+ cmp #WORLD_W
1215
+ beq !+
1216
+ jmp gw_col
1217
+ !: inc wy
1218
+ lda wy
1219
+ cmp #WORLD_H
1220
+ beq !+
1221
+ jmp gw_row
1222
+ !:
1223
+
1224
+ lda #<(WORLD + MARK_Y*WORLD_W + MARK_X)
1225
+ sta wptr
1226
+ lda #>(WORLD + MARK_Y*WORLD_W + MARK_X)
1227
+ sta wptr+1
1228
+ ldy #0
1229
+ lda #MARK_CH
1230
+ sta (wptr),y
1231
+ lda #<(WCOL + MARK_Y*WORLD_W + MARK_X)
1232
+ sta wptr
1233
+ lda #>(WCOL + MARK_Y*WORLD_W + MARK_X)
1234
+ sta wptr+1
1235
+ lda #7 // yellow marker
1236
+ sta (wptr),y
1237
+ rts
1238
+
1239
+ // --- tables ----------------------------------------------------------
1240
+ d018T: .byte $14, $24
1241
+ pageHiT: .byte >SCR0, >SCR1
1242
+ statBuf: .fill 40, $20
1243
+ rowLoT: .fill 25, <(i*40)
1244
+ rowHiT: .fill 25, >(i*40)
1245
+ stSrc: .byte camx, camy, legIdx, stalls, worst, lateApply, skipPrep, b1Dead
1246
+ stPos: .byte 2, 7, 12, 16, 20, 26, 30, 35
1247
+ stDig: .byte 2, 2, 2, 2, 0, 2, 2, 2
1248
+ stWrd: .byte 0, 0, 0, 0, 1, 0, 0, 0
1249
+ pwTabLo: .byte <10000, <1000, <100, <10, <1
1250
+ pwTabHi: .byte >10000, >1000, >100, >10, >1
1251
+
1252
+ // right, down, left, up, down-right, down-left, up-left, up-right
1253
+ pathDX: .byte 1, 0, $ff, 0, 1, $ff, $ff, 1
1254
+ pathDY: .byte 0, 1, 0, $ff, 1, 1, $ff, $ff
1255
+ pathLen: .byte 32, 32, 32, 32, 32, 32, 32, 32
1256
+ ```
1257
+
1258
+ ## Build
1259
+
1260
+ ```bash
1261
+ java -jar KickAss.jar eight-way-scroll.asm -o eight-way-scroll.prg
1262
+ x64sc -default -warp +sound +autostart-delay-random -autostartprgmode 1 \
1263
+ -limitcycles 20946000 -exitscreenshot eight-way-scroll.png \
1264
+ -autostart eight-way-scroll.prg
1265
+ ```
1266
+
1267
+ ## Expected output
1268
+
1269
+ A grid of light blue rules on black with white balls at the crossings,
1270
+ sliding in whichever direction the current leg is going, and a yellow
1271
+ marker tile that moves with the world. Row 1 carries the readout. The
1272
+ border is green from frame 320 on.
1273
+
1274
+ The readout is sampled: one of its eight fields is rebuilt each frame, so
1275
+ a field can be up to eight frames behind the camera. Measured in the exit
1276
+ screenshots, both models at 20,946,000 cycles, in a clean window at least
1277
+ four fields past the last tile crossing:
1278
+
1279
+ | Model | Camera | Leg | Waited flips | Worst field | Late applies | Skipped preps | First-band deadline |
1280
+ |---|---|---|---|---|---|---|---|
1281
+ | PAL | 69, 127 | 5 | 0 | 13,111 | 0 | 0 | 0 |
1282
+ | NTSC | 63, 73 | 7 | 0 | 13,152 | 0 | 0 | 0 |
1283
+
1284
+ The border is green in both, so the marker check held on every frame of
1285
+ both runs. The two pictures are byte-identical across two runs of the
1286
+ same command.
1287
+
1288
+ The rules sit eight tiles apart in the world, so they land 64 pixels apart
1289
+ in the picture, and their phase is the camera's position within that
1290
+ 64-pixel period. The camera positions above are from the HUD at the exit
1291
+ screenshot; the rule geometry varies with camera position.
1292
+
1293
+ A tile crossing produces four colour-write fields: the first top call
1294
+ (`BAND_FIRST = 5` rows, rows 0 to 4), the second top call (rows 5 to 11),
1295
+ the first bottom call (rows 12 to 18) and the second bottom call (rows 19
1296
+ to 24). During those fields, rows not yet rewritten show the previous
1297
+ origin's colour alongside the new matrix. The exit screenshots above are
1298
+ taken in the clean window; a capture inside the three stale fields shows
1299
+ grey horizontal rules in the rows not yet rewritten and a dimmer HUD row
1300
+ for one field while row 1's colour updates. This is documented in "What it
1301
+ does not establish" below.
1302
+
1303
+ Screenshots from the VICE runs this page describes:
1304
+ `screenshots/eight-way-scroll.png` (PAL) and
1305
+ `screenshots/eight-way-scroll-ntsc.png`.
1306
+
1307
+ ## Why this works
1308
+
1309
+ ### Fine scroll and the tile origin
1310
+
1311
+ The camera is a position in world pixels. Its tile origin is `camx >> 3`
1312
+ and `camy >> 3`, and the sub-tile remainder is what the fine scroll has to
1313
+ absorb. XSCROLL moves the display right, so a camera moving right needs a
1314
+ decreasing XSCROLL: the listing writes `7 - (camx & 7)` and
1315
+ `7 - (camy & 7)`, which walks 7 down to 0 across a tile and wraps as the
1316
+ origin advances.
1317
+
1318
+ Both registers are written whole, never read-modify-write, so CSEL, MCM,
1319
+ the raster compare's high bit and DEN keep the values the listing intends.
1320
+ RSEL and CSEL are both left at 0. The narrow window hides the partial row
1321
+ at each edge, so a 40 x 25 matrix covers the display at every fine scroll
1322
+ value and no column or row is half drawn at the border.
1323
+
1324
+ ### Why the matrix is double buffered
1325
+
1326
+ A whole-tile step needs all 1,000 matrix bytes replaced. That copy is far
1327
+ too long for the blank on either model, so it cannot be done between
1328
+ fields; drawn into the live page it would be visible as a tear running
1329
+ down the screen. Instead it goes into the page that is not on display, and
1330
+ only the `$D018` VM nibble changes when it is complete. The flip is one
1331
+ store in the blank.
1332
+
1333
+ The flip is refused unless the spare page already holds exactly the origin
1334
+ wanted, and every refusal is counted. That counter reading zero is the
1335
+ evidence that the redraw always finished in time.
1336
+
1337
+ ### Drawing for the origin after next, not the current one
1338
+
1339
+ The spare page cannot be drawn for the origin the camera has now, because
1340
+ by the time it is ready the camera has moved on. It is drawn for the
1341
+ origin after the next tile crossing. On a diagonal the two axes cross on
1342
+ different frames, and a single spare page cannot serve two origins, so the
1343
+ listing works out which axis crosses first, from `8 - (cam & 7)` when that
1344
+ axis is increasing and `(cam & 7) + 1` when it is decreasing, and targets
1345
+ that crossing alone.
1346
+
1347
+ The gap between crossings is set by the camera's starting position within
1348
+ its tile. Starting both axes at 66 (2 within the tile) at half speed, the
1349
+ smallest gap anywhere on the path is six fields: the three-field minimum
1350
+ at one pixel per field doubles when the camera moves one pixel every two
1351
+ fields. An earlier start position gave a gap of one at full speed and the
1352
+ flip was refused on every diagonal.
1353
+
1354
+ ### Splitting the redraw
1355
+
1356
+ `BAND_MAX` rows per field is the cap that applies everywhere. With
1357
+ `BAND_MAX = 7` and a 25-row matrix, the spare page is ready in at most
1358
+ four fields. The camera advances one pixel every two fields, so tile
1359
+ crossings are sixteen fields apart on straight legs and there is always room.
1360
+
1361
+ The requirement is five fields: four fields of matrix redraw make the spare
1362
+ page ready so a flip is possible from the fifth field, and the four colour
1363
+ calls finish in the same span. The minimum gap between crossings is six
1364
+ fields at half speed, leaving one field of slack. Half speed is sufficient;
1365
+ a slower rate is not needed.
1366
+
1367
+ In the flip field, the matrix band is drawn into the page still on display
1368
+ until raster 251. `irqPrep` toggles `pgVis` at the flip decision while
1369
+ `pendVM` is applied at 251; `drawMatrix` targets `pgVis eor 1`, so the
1370
+ band drawn between raster 152 + YSCROLL and 251 goes into the page the
1371
+ beam is still showing. This is safe because `BAND_MAX = 7` keeps those
1372
+ rows (0 to 6) above the beam at that raster. Raising `BAND_MAX` past 12
1373
+ would draw into rows the beam has already reached, tearing the live page.
1374
+
1375
+ An earlier rule let a field with no colour work clear the whole debt. That
1376
+ looked equivalent and was not: a change of leg moves the target origin
1377
+ with no page flip, so a plain field started a 25-row redraw from nothing
1378
+ and the field cost 20,506 cycles. Capping every band fixed it.
1379
+
1380
+ A second wrap arriving mid-redraw cannot happen here, because the redraw
1381
+ always completes in four fields and the gap is sixteen on straight legs at half speed. If
1382
+ it did, the flip would be refused, the waited-flips counter would rise and
1383
+ the verdict would fail; the program does not try to abandon a part-drawn
1384
+ page.
1385
+
1386
+ ### Colour RAM
1387
+
1388
+ Colour RAM has one copy at `$D800` whatever `$D018` says, so it cannot be
1389
+ flipped with the matrix. Both halves are written in `irqColB` at raster 4.
1390
+
1391
+ On NTSC, `irqApply` (register writes plus readout stamp) exceeds the
1392
+ twelve-raster blank and finishes inside the next field, so `irqColB`
1393
+ starts as late as raster 33. The first top call is limited to `BAND_FIRST
1394
+ = 5` rows (rows 0 to 4) so it finishes before row 6's badline even at the
1395
+ worst-case entry raster. The remaining calls use `BAND_MAX = 7`, which
1396
+ always finishes before `irqPrep`'s raster.
1397
+
1398
+ The split at row 12 gives the top half twelve rows and the bottom thirteen.
1399
+ Rows 0 to 4 see new colour in the flip field if the first call finishes in
1400
+ time; rows 5 to 24 show old colour for one to three more fields. Over a
1401
+ 512-field loop about one field in four shows stale rows somewhere, and
1402
+ three fields in eight on the opposite-sign diagonals where crossings are
1403
+ closer together.
1404
+
1405
+ ### The IRQ chain
1406
+
1407
+ Three handlers a field: raster 4 for the band-limited colour write,
1408
+ 152 + YSCROLL for the path step, flip decision and matrix band, and 251
1409
+ for the register writes in the blank. Each arms the next one's `$D012` and
1410
+ vector as its first act, before doing any work. A handler here can run for
1411
+ well over a raster line, and if it armed its successor at the end it would
1412
+ arm a compare the beam had already passed and lose a whole field.
1413
+
1414
+ The VIC sets the raster interrupt flag regardless of the CPU's interrupt
1415
+ state. When the CPU's I flag is set because a previous handler is still
1416
+ running, the CPU defers the new interrupt and takes it at the current
1417
+ handler's `rti`. That is what lets a late `irqApply` be detected: if
1418
+ `irqPrep` overruns the 251-raster mark, `irqApply` fires after the `rti`
1419
+ at whatever raster `irqPrep` left the CPU, and reading `$D012` at
1420
+ `irqApply`'s entry gives a number below 251.
1421
+
1422
+ The `$D016` write is deliberately whole-register (`lda pendD16; sta $D016`)
1423
+ rather than read-modify-write. Two lint findings flag this as a potential
1424
+ CSEL or MCM collision, but the write is intentional: CSEL and MCM are both
1425
+ left at 0 throughout, and a read-modify-write would require an extra byte
1426
+ of state to track the stable bits.
1427
+
1428
+ `$01` is set to `$35`, so RAM is banked in under BASIC and KERNAL with I/O
1429
+ still visible and the vector at `$FFFE` is the one that runs.
1430
+
1431
+ ### Where the code lives
1432
+
1433
+ The two screen pages are `$0400` and `$0800`, both in VIC bank 0. The
1434
+ program is at `$1000`, which the chip sees as character ROM, so that RAM
1435
+ is free for code and cannot be mistaken for screen data. An earlier draft
1436
+ sat at `$0810` and the routine that clears the second screen page wrote
1437
+ over it; the program ran once, cleared itself and stopped with the BASIC
1438
+ screen still up.
1439
+
1440
+ ### What the timer measures
1441
+
1442
+ CIA 1 timer B free-runs from `$FFFF`. Each handler reads it on entry and
1443
+ on exit and adds the difference to a per-field total, which the last
1444
+ handler compares against the worst seen. The count is wall-clock, so
1445
+ badline stalls are in it: the VIC holds the CPU for about 40 to 43 cycles
1446
+ on every eighth line, and a copy loop that costs 14 cycles a byte in
1447
+ instruction terms measures 17.6 to 17.8 cycles per byte. That figure comes
1448
+ from reading the CIA timer before and after a full 25-row draw (1,000
1449
+ bytes) at start-up on both models; it is a measured average, not
1450
+ calculated from instruction timings alone.
1451
+
1452
+ The readout is inside the measured region. It builds one of its eight
1453
+ fields a frame for that reason: built whole every sixteenth frame it added
1454
+ about 3,000 cycles to whichever field it landed on, and the worst field is
1455
+ the number this page reports.
1456
+
1457
+ A PAL field is 19,656 cycles and an NTSC field 17,095. The worst field
1458
+ measured here is 13,111 on PAL and 13,152 on NTSC, both under the 18,000
1459
+ budget the verdict checks. The NTSC figure is the larger of the two:
1460
+ 3,943 cycles of margin, twenty-three per cent of the field.
1461
+
1462
+ ### What it does not establish
1463
+
1464
+ **Colour continuity across a flip.** A tile crossing writes colour to
1465
+ colour RAM over four consecutive `irqColB` calls (two for the top half,
1466
+ two for the bottom). During those fields the rows not yet rewritten show
1467
+ the previous origin's colour alongside the new matrix, producing a brief
1468
+ mismatch. This lasts three displayed fields (60 ms PAL, 50 ms NTSC) and
1469
+ is the trade-off the band cap makes. Rows 0 to 4 may show old colour for
1470
+ one field on NTSC when `irqColB` starts late enough that the first band
1471
+ finishes after row 4's badline.
1472
+
1473
+ **Camera path coverage.** `MAX_COL` and `MAX_ROW` clamp the visible
1474
+ origin so the camera never leaves the world. The path used here stays in
1475
+ columns 4 to 12 and rows 8 to 16, so those clamps are never reached.
1476
+ Whether the design is correct at the boundary is not tested.
1477
+
1478
+ **The one-pixel-per-field form.** At full speed the camera crosses a tile
1479
+ boundary every field. Tile crossings are then eight fields apart on
1480
+ straight legs, but the minimum gap on diagonals falls to three fields,
1481
+ which is less than the five-field requirement. The band at `BAND_MAX = 7`
1482
+ fits the available windows at either speed; what full speed loses is the
1483
+ crossing gap. The half-speed camera doubles the minimum gap to six fields
1484
+ and leaves one field of slack.