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,2324 @@
1
+ ---
2
+ recipe: wireframe-ships
3
+ toolchain: kickassembler
4
+ output_format: PRG
5
+ region: both
6
+ techniques: [wireframe_pipeline, procedural_seed_universe]
7
+ file_formats: [PRG]
8
+ uses_registers: [D011, D016, D018, D020, D021, DD00, DC04, DC05, DC06, DC07, DC0D, DC0E, DC0F, DD04, DD05, DD06, DD07, DD0D, DD0E, DD0F]
9
+ uses_kernal: []
10
+ ---
11
+
12
+ <!-- doc-type: recipe -->
13
+
14
+ # KickAssembler — Rotating wireframe ships with back-face culling and clipping, and an Elite-style seeded galaxy, checked against a host model
15
+
16
+ ## Synopsis
17
+
18
+ Three convex wireframe objects (a cube, a square pyramid and a ship hull
19
+ of this recipe's own design) rotate in a 160 x 120 pixel window of a
20
+ hires bitmap. Each update builds a 3x3 fixed-point rotation matrix per
21
+ object from a sine table and quarter-square multiplies, transforms and
22
+ projects every vertex through a reciprocal table, culls back faces by
23
+ the winding of the projected face, rejects lines wholly outside the
24
+ window by outcodes, and draws the rest with Bresenham and EOR. A line
25
+ that crosses the window edge is clipped pixel by pixel. The previous
26
+ update's lines are erased by drawing them again. The ship crosses the
27
+ right edge and the cube the top edge.
28
+
29
+ The program runs 48 updates twice. Pass 1 counts lines and sums their
30
+ endpoints, times every line call with CIA2, and checks the counts and a
31
+ checksum of the bitmap against the Python model below. Pass 2 erases,
32
+ rewinds and runs the same 48 updates with the counters off, timing each
33
+ object with CIA1, and checks the bitmap again. It then times one matrix
34
+ build and one ship transform, generates the first eight systems of
35
+ Elite's first galaxy from its three 16-bit seeds (named from this recipe's
36
+ own letter pairs), checks the names and coordinates against the model, and prints everything under the window.
37
+ It writes `$01` to `$02FF` and turns the border green when every check
38
+ matches, `$02` and red otherwise. The techniques are
39
+ `wireframe_pipeline` in `techniques/effects-vector-3d.md` and
40
+ `procedural_seed_universe` in `techniques/maths.md`.
41
+
42
+ Coordinates: the program works in a virtual 256 x 256 screen with its
43
+ centre at (128, 128). The window is x 32-191, y 68-187; bitmap x is
44
+ virtual x + 32 and bitmap y is virtual y - 68, so the window fills
45
+ character rows 0-14, columns 8-27. Camera space has x right, y down and
46
+ z into the screen, with the camera at the origin.
47
+
48
+ ## Source
49
+
50
+ ```asm
51
+ // wireframe-ships.asm
52
+ // Three rotating convex wireframe objects (a cube, a pyramid, a ship hull)
53
+ // in a hires bitmap: 3x3 fixed-point rotation from a sine table and
54
+ // quarter-square multiplies, projection through a reciprocal table,
55
+ // back-face culling by the winding of the projected face, outcode reject,
56
+ // a per-pixel clip for lines that cross the window edge, Bresenham lines
57
+ // plotted with EOR and erased by drawing the previous frame's lines again.
58
+ // Pass 1 runs NF updates with counters on and CIA2 gated around every line
59
+ // call, then checks the line count, an endpoint sum and a bitmap checksum
60
+ // against a Python model of the same integer arithmetic (the generator is
61
+ // on the page). Pass 2 erases, rewinds and runs the same NF updates with
62
+ // the counters off, timing each object with CIA1, and checks the bitmap
63
+ // again. Then the program times a matrix build and a vertex transform,
64
+ // generates the first eight systems of Elite's first galaxy from three
65
+ // 16-bit seeds, and prints everything into the bitmap below the window.
66
+ // Verdict: $02FF = $01 and a green border when every check matches,
67
+ // $02 and red otherwise.
68
+ .encoding "screencode_upper"
69
+
70
+ .const NF = 48 // updates before the picture is frozen
71
+ .const XMIN = 32 // view window, virtual coordinates
72
+ .const XMAX = 191 // (bitmap x = vx + 32, bitmap y = vy - 68)
73
+ .const YMIN = 68
74
+ .const YMAX = 187
75
+ .const BITMAP = $6000 // VIC bank 1: bitmap at $6000
76
+ .const MATRIX = $4400 // video matrix at $4400
77
+ .const RESULT = $02ff
78
+ .const NSYS = 8
79
+
80
+ // expected values from gen.py
81
+ .const EXP_LAST = 19
82
+ .const EXP_TOTAL = 1005
83
+ .const EXP_SUM = $84ea
84
+ .const EXP_S1 = $8c
85
+ .const EXP_S2 = $99
86
+
87
+ // ---- zero page (the program never returns to BASIC) ----
88
+ .const ma = $02 // signed multiply operands
89
+ .const mb = $03
90
+ .const ua = $04 // unsigned multiply operands
91
+ .const ub = $05
92
+ .const prod = $06 // 16-bit product
93
+ .const acc = $08 // 16-bit dot-product accumulator
94
+ .const m = $0a // 3x3 matrix, row-major, 9 bytes $0a-$12
95
+ .const vxx = $13 // vertex being transformed
96
+ .const vyy = $14
97
+ .const vzz = $15
98
+ .const rowi = $16
99
+ .const obj = $17 // current object 0-2
100
+ .const tmp = $18
101
+ .const sgn = $19
102
+ .const zc = $1a
103
+ .const recf = $1b
104
+ .const vis = $1c // visible-face mask of the current object
105
+ .const cnt = $1d
106
+ .const idx = $1e
107
+ .const t1 = $1f // 16-bit
108
+ .const lx0 = $21 // line endpoints and clip flag
109
+ .const ly0 = $22
110
+ .const lx1 = $23
111
+ .const ly1 = $24
112
+ .const lclip = $25
113
+ .const ddx = $26
114
+ .const ddy = $27
115
+ .const err = $28
116
+ .const cy = $29
117
+ .const sy = $2a
118
+ .const ptr = $2b // 2 bytes
119
+ .const oc0 = $2d
120
+ .const hbase = $2e // heap slot base for the current object
121
+ .const upd = $2f // update counter
122
+ .const t0 = $30 // CIA1 32-bit reading, 4 bytes $30-$33
123
+ .const u24 = $34 // 24-bit accumulator for one update, 3 bytes
124
+ .const s0 = $37 // galaxy seeds, 6 bytes $37-$3c
125
+ .const s1 = $39
126
+ .const s2 = $3b
127
+ .const gt = $3d // 2 bytes
128
+ .const num = $3f // 24-bit number for the printers, 3 bytes
129
+ .const dig = $42
130
+ .const prow = $43 // print cursor: text row, column
131
+ .const pcol = $44
132
+ .const sptr = $45 // 2 bytes
133
+ .const dst = $47 // 2 bytes
134
+ .const src = $49 // 2 bytes
135
+ .const dvs = $4b // 16-bit divisor
136
+ .const rmd = $4d // 16-bit remainder
137
+ .const fail = $4f
138
+ .const lng = $50
139
+ .const pcount = $51
140
+ .const sysn = $52
141
+ .const gcol = $53
142
+ .const nx = $54
143
+
144
+ BasicUpstart2(start)
145
+
146
+ start:
147
+ sei
148
+ lda #$7f
149
+ sta $dc0d // no CIA1 interrupts
150
+ sta $dd0d // no CIA2 NMIs
151
+ lda $dc0d
152
+ lda $dd0d
153
+ lda #0
154
+ sta $d020
155
+ sta $d021
156
+ sta fail
157
+ jsr clear_all
158
+ lda $dd00
159
+ and #%11111100
160
+ ora #%00000010 // VIC bank 1 ($4000-$7fff)
161
+ sta $dd00
162
+ lda #$18 // matrix $4400, bitmap $6000
163
+ sta $d018
164
+ lda #$08
165
+ sta $d016
166
+ lda #$3b // bitmap mode, display on, 25 rows
167
+ sta $d011
168
+
169
+ // cycle harness: CIA2 A+B chained, paused except while a line draws
170
+ lda #0
171
+ sta $dd0e
172
+ sta $dd0f
173
+ lda #$ff
174
+ sta $dd04
175
+ sta $dd05
176
+ sta $dd06
177
+ sta $dd07
178
+ lda #$10
179
+ sta $dd0e // A: force load, stopped
180
+ lda #$51
181
+ sta $dd0f // B: counts A underflows, force load, start
182
+ jsr time_empty // CIA1 start/stop overhead
183
+ jsr pause_empty // CIA2 resume/pause overhead
184
+
185
+ lda #0
186
+ sta tot_lines
187
+ sta tot_lines+1
188
+ sta ep_sum
189
+ sta ep_sum+1
190
+ sta hcount
191
+ sta hcount+1
192
+ sta hcount+2
193
+ sta calls
194
+ sta calls+1
195
+
196
+ // pass 1, checked: counters and the CIA2 line gate are on
197
+ lda #$80
198
+ sta harness
199
+ jsr run_pass
200
+ clc
201
+ lda hcount
202
+ adc hcount+1
203
+ adc hcount+2
204
+ sta last_lines
205
+ jsr check_model
206
+ // line cycles: CIA2 total less the pause/resume overhead per call
207
+ jsr cia2_read // t0..t0+3 = cycles counted
208
+ jsr mul_ovh_sub // t0 -= calls * povh
209
+ lda t0
210
+ sta linecyc
211
+ lda t0+1
212
+ sta linecyc+1
213
+ lda t0+2
214
+ sta linecyc+2
215
+ // pass 2, timed: erase, rewind, run the same updates with the
216
+ // harness off; the picture must come out the same
217
+ lda #0
218
+ sta harness
219
+ jsr rewind
220
+ jsr run_pass
221
+ jsr check_bitmap
222
+ jmp bench
223
+
224
+ // ================= the animation =================
225
+ run_pass:
226
+ ldx #2
227
+ !: lda ang_a0,x
228
+ sta ang_a,x
229
+ lda ang_b0,x
230
+ sta ang_b,x
231
+ dex
232
+ bpl !-
233
+ lda #0
234
+ sta upd
235
+ sta worst
236
+ sta worst+1
237
+ sta worst+2
238
+ frame:
239
+ lda #0
240
+ sta u24
241
+ sta u24+1
242
+ sta u24+2
243
+ ldx #0
244
+ obj_loop:
245
+ stx obj
246
+ jsr timer_start
247
+ jsr do_object
248
+ jsr timer_stop
249
+ ldx obj // keep this object's cost for the readout
250
+ lda t0
251
+ sta ocyc_lo,x
252
+ lda t0+1
253
+ sta ocyc_mid,x
254
+ lda t0+2
255
+ sta ocyc_hi,x
256
+ clc
257
+ lda u24
258
+ adc t0
259
+ sta u24
260
+ lda u24+1
261
+ adc t0+1
262
+ sta u24+1
263
+ lda u24+2
264
+ adc t0+2
265
+ sta u24+2
266
+ ldx obj
267
+ inx
268
+ cpx #3
269
+ bne obj_loop
270
+ // worst update so far
271
+ lda u24+2
272
+ cmp worst+2
273
+ bcc !keep+
274
+ bne !new+
275
+ lda u24+1
276
+ cmp worst+1
277
+ bcc !keep+
278
+ bne !new+
279
+ lda u24
280
+ cmp worst
281
+ bcc !keep+
282
+ !new: lda u24
283
+ sta worst
284
+ lda u24+1
285
+ sta worst+1
286
+ lda u24+2
287
+ sta worst+2
288
+ !keep:
289
+ inc upd
290
+ lda upd
291
+ cmp #NF
292
+ beq !+
293
+ jmp frame
294
+ !: rts
295
+
296
+ // erase every object's heap: the bitmap returns to blank
297
+ rewind:
298
+ ldx #0
299
+ !: stx obj
300
+ jsr erase_heap
301
+ ldx obj
302
+ lda #0
303
+ sta hcount,x
304
+ inx
305
+ cpx #3
306
+ bne !-
307
+ rts
308
+
309
+ // ================= checks against the model =================
310
+ check_model:
311
+ lda last_lines
312
+ cmp #EXP_LAST
313
+ beq !+
314
+ inc fail
315
+ !: lda tot_lines
316
+ cmp #<EXP_TOTAL
317
+ bne !bad+
318
+ lda tot_lines+1
319
+ cmp #>EXP_TOTAL
320
+ bne !bad+
321
+ lda ep_sum
322
+ cmp #<EXP_SUM
323
+ bne !bad+
324
+ lda ep_sum+1
325
+ cmp #>EXP_SUM
326
+ beq !+
327
+ !bad: inc fail
328
+ !:
329
+ check_bitmap:
330
+ jsr fletcher // bitmap rows 0-15, memory order
331
+ lda fl1
332
+ cmp #EXP_S1
333
+ bne !bad+
334
+ lda fl2
335
+ cmp #EXP_S2
336
+ beq !+
337
+ !bad: inc fail
338
+ !: rts
339
+
340
+ // ================= micro-benchmarks (bitmap untouched) =================
341
+ bench:
342
+ lda #2
343
+ sta obj
344
+ jsr timer_start
345
+ jsr build_matrix
346
+ jsr timer_stop
347
+ lda t0
348
+ sta mcyc
349
+ lda t0+1
350
+ sta mcyc+1
351
+ jsr timer_start
352
+ jsr xform_all
353
+ jsr timer_stop
354
+ lda t0
355
+ sta vcyc
356
+ lda t0+1
357
+ sta vcyc+1
358
+
359
+ // ================= galaxy =================
360
+ jsr galaxy
361
+
362
+ // ================= readout =================
363
+ jsr print_all
364
+ lda fail
365
+ bne !bad+
366
+ lda #1
367
+ sta RESULT
368
+ lda #5
369
+ sta $d020
370
+ jmp *
371
+ !bad: lda #2
372
+ sta RESULT
373
+ lda #2
374
+ sta $d020
375
+ jmp *
376
+
377
+ // ---------------------------------------------------------------
378
+ // one object: advance angles, matrix, transform and project every
379
+ // vertex, cull faces, erase the old lines, draw and store the new
380
+ do_object:
381
+ ldx obj
382
+ clc
383
+ lda ang_a,x
384
+ adc stp_a,x
385
+ sta ang_a,x
386
+ clc
387
+ lda ang_b,x
388
+ adc stp_b,x
389
+ sta ang_b,x
390
+ jsr build_matrix
391
+ jsr xform_all
392
+ jsr cull
393
+ jsr erase_heap
394
+ // new lines
395
+ ldx obj
396
+ lda #0
397
+ sta hcount,x
398
+ lda o_en,x
399
+ sta cnt
400
+ lda o_est,x
401
+ sta idx
402
+ ed_loop:
403
+ ldy idx
404
+ lda ef0,y
405
+ tax
406
+ lda bit_of,x
407
+ and vis
408
+ bne ed_vis
409
+ lda ef1,y
410
+ tax
411
+ lda bit_of,x
412
+ and vis
413
+ bne ed_vis
414
+ jmp ed_next
415
+ ed_vis:
416
+ lda e0,y
417
+ tax
418
+ lda px,x
419
+ sta lx0
420
+ lda py,x
421
+ sta ly0
422
+ lda e1,y
423
+ tax
424
+ lda px,x
425
+ sta lx1
426
+ lda py,x
427
+ sta ly1
428
+ ldx lx0
429
+ ldy ly0
430
+ jsr outcode
431
+ sta oc0
432
+ ldx lx1
433
+ ldy ly1
434
+ jsr outcode
435
+ tax
436
+ and oc0
437
+ beq !+
438
+ jmp ed_next // both ends beyond one edge: reject
439
+ !:
440
+ txa
441
+ ora oc0
442
+ sta lclip // non-zero: crosses an edge, clip per pixel
443
+ // store in the heap
444
+ ldx obj
445
+ lda hslot,x
446
+ clc
447
+ adc hcount,x
448
+ tay
449
+ inc hcount,x
450
+ lda lx0
451
+ sta hx0,y
452
+ lda ly0
453
+ sta hy0,y
454
+ lda lx1
455
+ sta hx1,y
456
+ lda ly1
457
+ sta hy1,y
458
+ lda lclip
459
+ sta hcl,y
460
+ // bookkeeping for the model check, pass 1 only
461
+ bit harness
462
+ bpl ed_draw
463
+ inc tot_lines
464
+ bne !+
465
+ inc tot_lines+1
466
+ !: lda lx0
467
+ jsr add_sum
468
+ lda ly0
469
+ jsr add_sum
470
+ lda lx1
471
+ jsr add_sum
472
+ lda ly1
473
+ jsr add_sum
474
+ ed_draw:
475
+ jsr line_timed
476
+ ed_next:
477
+ inc idx
478
+ dec cnt
479
+ beq !+
480
+ jmp ed_loop
481
+ !: rts
482
+
483
+ // draw the object's stored lines again: EOR takes them off the bitmap
484
+ erase_heap:
485
+ ldx obj
486
+ lda hslot,x
487
+ sta hbase
488
+ lda hcount,x
489
+ sta cnt
490
+ beq er_done
491
+ ldy hbase
492
+ er_loop:
493
+ sty idx
494
+ lda hx0,y
495
+ sta lx0
496
+ lda hy0,y
497
+ sta ly0
498
+ lda hx1,y
499
+ sta lx1
500
+ lda hy1,y
501
+ sta ly1
502
+ lda hcl,y
503
+ sta lclip
504
+ jsr line_timed
505
+ ldy idx
506
+ iny
507
+ dec cnt
508
+ bne er_loop
509
+ er_done:
510
+ rts
511
+
512
+ add_sum:
513
+ clc
514
+ adc ep_sum
515
+ sta ep_sum
516
+ bcc !+
517
+ inc ep_sum+1
518
+ !: rts
519
+
520
+ // outcode of (X, Y): bit 0 left, 1 right, 2 above, 3 below
521
+ outcode:
522
+ lda #0
523
+ cpx #XMIN
524
+ bcs !+
525
+ ora #1
526
+ !: cpx #XMAX+1
527
+ bcc !+
528
+ ora #2
529
+ !: cpy #YMIN
530
+ bcs !+
531
+ ora #4
532
+ !: cpy #YMAX+1
533
+ bcc !+
534
+ ora #8
535
+ !: rts
536
+
537
+ // ---------------------------------------------------------------
538
+ // rotation matrix for object obj from angles a (about y) and b (about x)
539
+ // | ca 0 sa |
540
+ // | sa*sb>>6 cb -(ca*sb>>6) |
541
+ // | -(sa*cb>>6) sb ca*cb>>6 |
542
+ build_matrix:
543
+ ldx obj
544
+ ldy ang_a,x
545
+ lda sintab,y
546
+ sta m+2 // sa
547
+ lda costab,y
548
+ sta m+0 // ca
549
+ ldy ang_b,x
550
+ lda costab,y
551
+ sta m+4 // cb
552
+ lda sintab,y
553
+ sta m+7 // sb
554
+ lda #0
555
+ sta m+1
556
+ lda m+2
557
+ sta ma
558
+ lda m+7
559
+ sta mb
560
+ jsr smuls
561
+ jsr shr6
562
+ sta m+3 // sa*sb>>6
563
+ lda m+0
564
+ sta ma
565
+ jsr smuls // mb still sb
566
+ jsr shr6
567
+ eor #$ff
568
+ clc
569
+ adc #1
570
+ sta m+5 // -(ca*sb>>6)
571
+ lda m+2
572
+ sta ma
573
+ lda m+4
574
+ sta mb
575
+ jsr smuls
576
+ jsr shr6
577
+ eor #$ff
578
+ clc
579
+ adc #1
580
+ sta m+6 // -(sa*cb>>6)
581
+ lda m+0
582
+ sta ma
583
+ jsr smuls // mb still cb
584
+ jsr shr6
585
+ sta m+8 // ca*cb>>6
586
+ rts
587
+
588
+ // prod >> 6 as a signed byte: the high byte of prod << 2
589
+ shr6:
590
+ asl prod
591
+ rol prod+1
592
+ asl prod
593
+ rol prod+1
594
+ lda prod+1
595
+ rts
596
+
597
+ // signed 8x8 -> 16, valid while |ma + mb| and |ma - mb| <= 128
598
+ smuls:
599
+ lda ma
600
+ clc
601
+ adc mb
602
+ tax
603
+ lda ma
604
+ sec
605
+ sbc mb
606
+ tay
607
+ lda qslo,x
608
+ sec
609
+ sbc qslo,y
610
+ sta prod
611
+ lda qshi,x
612
+ sbc qshi,y
613
+ sta prod+1
614
+ rts
615
+
616
+ // unsigned 8x8 -> 16 by quarter squares, 512-entry tables
617
+ umul:
618
+ lda ua
619
+ clc
620
+ adc ub
621
+ tax
622
+ bcs um_hi
623
+ lda ua
624
+ sec
625
+ sbc ub
626
+ bcs !+
627
+ eor #$ff
628
+ adc #1 // carry is clear here: |ua - ub|
629
+ !: tay
630
+ lda sqlo,x
631
+ sec
632
+ sbc sqlo,y
633
+ sta prod
634
+ lda sqhi,x
635
+ sbc sqhi,y
636
+ sta prod+1
637
+ rts
638
+ um_hi: lda ua
639
+ sec
640
+ sbc ub
641
+ bcs !+
642
+ eor #$ff
643
+ adc #1
644
+ !: tay
645
+ lda sqlo+256,x
646
+ sec
647
+ sbc sqlo,y
648
+ sta prod
649
+ lda sqhi+256,x
650
+ sbc sqhi,y
651
+ sta prod+1
652
+ rts
653
+
654
+ // signed 8x8 -> 16 for any |ma|, |mb| <= 127: magnitudes, then the sign
655
+ smulg:
656
+ lda ma
657
+ eor mb
658
+ sta sgn
659
+ lda ma
660
+ bpl !+
661
+ eor #$ff
662
+ clc
663
+ adc #1
664
+ !: sta ua
665
+ lda mb
666
+ bpl !+
667
+ eor #$ff
668
+ clc
669
+ adc #1
670
+ !: sta ub
671
+ jsr umul
672
+ lda sgn
673
+ bpl !+
674
+ sec
675
+ lda #0
676
+ sbc prod
677
+ sta prod
678
+ lda #0
679
+ sbc prod+1
680
+ sta prod+1
681
+ !: rts
682
+
683
+ // row X (0, 3, 6) of the matrix times the vertex, >> 6
684
+ dot_row:
685
+ stx rowi
686
+ lda m,x
687
+ sta ma
688
+ lda vxx
689
+ sta mb
690
+ jsr smuls
691
+ lda prod
692
+ sta acc
693
+ lda prod+1
694
+ sta acc+1
695
+ ldx rowi
696
+ lda m+1,x
697
+ sta ma
698
+ lda vyy
699
+ sta mb
700
+ jsr smuls
701
+ clc
702
+ lda acc
703
+ adc prod
704
+ sta acc
705
+ lda acc+1
706
+ adc prod+1
707
+ sta acc+1
708
+ ldx rowi
709
+ lda m+2,x
710
+ sta ma
711
+ lda vzz
712
+ sta mb
713
+ jsr smuls
714
+ clc
715
+ lda acc
716
+ adc prod
717
+ sta acc
718
+ lda acc+1
719
+ adc prod+1
720
+ asl acc // (sum << 2) >> 8 = sum >> 6
721
+ rol
722
+ asl acc
723
+ rol
724
+ rts
725
+
726
+ // transform and project every vertex of obj into px[], py[]
727
+ xform_all:
728
+ ldx obj
729
+ lda o_vn,x
730
+ sta cnt
731
+ lda o_vst,x
732
+ sta idx
733
+ lda #0
734
+ sta tmp // local vertex index
735
+ xf_loop:
736
+ ldy idx
737
+ lda vx,y
738
+ sta vxx
739
+ lda vy,y
740
+ sta vyy
741
+ lda vz,y
742
+ sta vzz
743
+ ldx #6
744
+ jsr dot_row // z
745
+ ldx obj
746
+ clc
747
+ adc o_z,x
748
+ tay
749
+ lda rectab,y
750
+ sta ub
751
+ ldx #0
752
+ jsr dot_row // x
753
+ ldx obj
754
+ clc
755
+ adc o_x,x
756
+ jsr project
757
+ ldx tmp
758
+ sta px,x
759
+ ldx #3
760
+ jsr dot_row // y
761
+ ldx obj
762
+ clc
763
+ adc o_y,x
764
+ jsr project
765
+ ldx tmp
766
+ sta py,x
767
+ inc tmp
768
+ inc idx
769
+ dec cnt
770
+ bne xf_loop
771
+ rts
772
+
773
+ // A = signed camera coordinate, ub = reciprocal factor: A = 128 +- (|A|*f)>>8
774
+ project:
775
+ cmp #$80
776
+ bcs pr_neg
777
+ sta ua
778
+ jsr umul
779
+ lda prod+1
780
+ clc
781
+ adc #128
782
+ rts
783
+ pr_neg: eor #$ff
784
+ clc
785
+ adc #1
786
+ sta ua
787
+ jsr umul
788
+ lda #128
789
+ sec
790
+ sbc prod+1
791
+ rts
792
+
793
+ // visible faces: the projected winding (dx1*dy2 - dy1*dx2) is negative
794
+ cull:
795
+ lda #0
796
+ sta vis
797
+ ldx obj
798
+ lda o_fn,x
799
+ sta cnt
800
+ lda o_fst,x
801
+ sta idx
802
+ lda #0
803
+ sta sysn // local face number
804
+ cu_loop:
805
+ ldy idx
806
+ ldx fa,y
807
+ lda px,x
808
+ sta lx0
809
+ lda py,x
810
+ sta ly0
811
+ ldx fb,y
812
+ lda px,x
813
+ sec
814
+ sbc lx0
815
+ sta ddx // dx1
816
+ lda py,x
817
+ sec
818
+ sbc ly0
819
+ sta ddy // dy1
820
+ ldx fc,y
821
+ lda px,x
822
+ sec
823
+ sbc lx0
824
+ sta lx1 // dx2
825
+ lda py,x
826
+ sec
827
+ sbc ly0
828
+ sta ly1 // dy2
829
+ lda ddx
830
+ sta ma
831
+ lda ly1
832
+ sta mb
833
+ jsr smulg
834
+ lda prod
835
+ sta t1
836
+ lda prod+1
837
+ sta t1+1
838
+ lda ddy
839
+ sta ma
840
+ lda lx1
841
+ sta mb
842
+ jsr smulg
843
+ sec
844
+ lda t1
845
+ sbc prod
846
+ lda t1+1
847
+ sbc prod+1
848
+ bpl !+
849
+ ldx sysn
850
+ lda bit_of,x
851
+ ora vis
852
+ sta vis
853
+ !: inc sysn
854
+ inc idx
855
+ dec cnt
856
+ bne cu_loop
857
+ rts
858
+
859
+ // ---------------------------------------------------------------
860
+ // line with CIA2 running only while it draws
861
+ line_timed:
862
+ bit harness
863
+ bpl line // pass 2: no gate
864
+ inc calls
865
+ bne !+
866
+ inc calls+1
867
+ !: lda #$01
868
+ sta $dd0e // resume timer A
869
+ jsr line
870
+ lda #$00
871
+ sta $dd0e // pause
872
+ rts
873
+
874
+ // Bresenham from (lx0,ly0) to (lx1,ly1), EOR plot; lclip != 0 tests
875
+ // every pixel against the window
876
+ line:
877
+ lda lx1
878
+ sec
879
+ sbc lx0
880
+ bcs !+
881
+ eor #$ff
882
+ adc #1
883
+ !: sta ddx
884
+ lda ly1
885
+ sec
886
+ sbc ly0
887
+ bcs !+
888
+ eor #$ff
889
+ adc #1
890
+ !: sta ddy
891
+ cmp ddx
892
+ bcc x_major
893
+ beq x_major
894
+ jmp y_major
895
+
896
+ x_major:
897
+ lda lx1 // step x upward: swap if x0 > x1
898
+ cmp lx0
899
+ bcs !+
900
+ ldx lx0
901
+ sta lx0
902
+ stx lx1
903
+ lda ly0
904
+ ldx ly1
905
+ sta ly1
906
+ stx ly0
907
+ !: lda #1
908
+ ldx ly1
909
+ cpx ly0
910
+ bcs !+
911
+ lda #$ff
912
+ !: sta sy
913
+ lda ddx
914
+ lsr
915
+ sta err
916
+ ldy ly0
917
+ sty cy
918
+ lda ylo,y
919
+ sta ptr
920
+ lda yhi,y
921
+ sta ptr+1
922
+ ldx lx0
923
+ lda lclip
924
+ bne !+
925
+ XMAJOR(false)
926
+ rts
927
+ !: XMAJOR(true)
928
+ rts
929
+
930
+ y_major:
931
+ lda ly1 // step y downward on screen: swap if y0 > y1
932
+ cmp ly0
933
+ bcs !+
934
+ ldx ly0
935
+ sta ly0
936
+ stx ly1
937
+ lda lx0
938
+ ldx lx1
939
+ sta lx1
940
+ stx lx0
941
+ !: lda #1
942
+ ldx lx1
943
+ cpx lx0
944
+ bcs !+
945
+ lda #$ff
946
+ !: sta sy // x step
947
+ lda ddy
948
+ lsr
949
+ sta err
950
+ lda ly0
951
+ sta cy
952
+ ldx lx0
953
+ lda lclip
954
+ bne !+
955
+ YMAJOR(false)
956
+ rts
957
+ !: YMAJOR(true)
958
+ rts
959
+
960
+ .macro XMAJOR(clip) {
961
+ loop:
962
+ .if (clip) {
963
+ cpx #XMIN
964
+ bcc skip
965
+ cpx #XMAX+1
966
+ bcs skip
967
+ lda cy
968
+ cmp #YMIN
969
+ bcc skip
970
+ cmp #YMAX+1
971
+ bcs skip
972
+ }
973
+ ldy xcol,x
974
+ lda (ptr),y
975
+ eor xmask,x
976
+ sta (ptr),y
977
+ skip:
978
+ cpx lx1
979
+ beq done
980
+ inx
981
+ lda err
982
+ sec
983
+ sbc ddy
984
+ bcs nostep
985
+ adc ddx // carry clear: err - dy + dx
986
+ sta err
987
+ lda cy
988
+ clc
989
+ adc sy
990
+ sta cy
991
+ tay
992
+ lda ylo,y
993
+ sta ptr
994
+ lda yhi,y
995
+ sta ptr+1
996
+ jmp loop
997
+ nostep:
998
+ sta err
999
+ jmp loop
1000
+ done:
1001
+ }
1002
+
1003
+ .macro YMAJOR(clip) {
1004
+ loop:
1005
+ ldy cy
1006
+ lda ylo,y
1007
+ sta ptr
1008
+ lda yhi,y
1009
+ sta ptr+1
1010
+ .if (clip) {
1011
+ cpx #XMIN
1012
+ bcc skip
1013
+ cpx #XMAX+1
1014
+ bcs skip
1015
+ cpy #YMIN
1016
+ bcc skip
1017
+ cpy #YMAX+1
1018
+ bcs skip
1019
+ }
1020
+ ldy xcol,x
1021
+ lda (ptr),y
1022
+ eor xmask,x
1023
+ sta (ptr),y
1024
+ skip:
1025
+ lda cy
1026
+ cmp ly1
1027
+ beq done
1028
+ inc cy
1029
+ lda err
1030
+ sec
1031
+ sbc ddx
1032
+ bcs nostep
1033
+ adc ddy
1034
+ sta err
1035
+ txa
1036
+ clc
1037
+ adc sy
1038
+ tax
1039
+ jmp loop
1040
+ nostep:
1041
+ sta err
1042
+ jmp loop
1043
+ done:
1044
+ }
1045
+
1046
+ // ---------------------------------------------------------------
1047
+ // CIA1 timers A and B chained as a 32-bit down-counter
1048
+ timer_start:
1049
+ lda #0
1050
+ sta $dc0e
1051
+ sta $dc0f
1052
+ lda #$ff
1053
+ sta $dc04
1054
+ sta $dc05
1055
+ sta $dc06
1056
+ sta $dc07
1057
+ lda #$51
1058
+ sta $dc0f
1059
+ lda #$11
1060
+ sta $dc0e
1061
+ rts
1062
+ // stop, read, subtract the measured empty overhead: t0 = cycles
1063
+ timer_stop:
1064
+ lda #0
1065
+ sta $dc0e
1066
+ lda $dc04
1067
+ eor #$ff
1068
+ sta t0
1069
+ lda $dc05
1070
+ eor #$ff
1071
+ sta t0+1
1072
+ lda $dc06
1073
+ eor #$ff
1074
+ sta t0+2
1075
+ lda $dc07
1076
+ eor #$ff
1077
+ sta t0+3
1078
+ sec
1079
+ lda t0
1080
+ sbc ovh
1081
+ sta t0
1082
+ lda t0+1
1083
+ sbc #0
1084
+ sta t0+1
1085
+ lda t0+2
1086
+ sbc #0
1087
+ sta t0+2
1088
+ rts
1089
+ time_empty:
1090
+ lda #0
1091
+ sta ovh
1092
+ jsr timer_start
1093
+ jsr timer_stop
1094
+ lda t0
1095
+ sta ovh
1096
+ rts
1097
+ // CIA2: time one empty resume/pause pair
1098
+ pause_empty:
1099
+ lda #$01
1100
+ sta $dd0e
1101
+ lda #$00
1102
+ sta $dd0e
1103
+ jsr cia2_read
1104
+ lda t0
1105
+ sta povh
1106
+ lda #$10 // reload $ffff, stopped
1107
+ sta $dd0e
1108
+ lda #$51
1109
+ sta $dd0f
1110
+ rts
1111
+ cia2_read:
1112
+ lda $dd04
1113
+ eor #$ff
1114
+ sta t0
1115
+ lda $dd05
1116
+ eor #$ff
1117
+ sta t0+1
1118
+ lda $dd06
1119
+ eor #$ff
1120
+ sta t0+2
1121
+ lda $dd07
1122
+ eor #$ff
1123
+ sta t0+3
1124
+ rts
1125
+ // t0 (24 bits) -= calls * povh, by repeated subtraction
1126
+ mul_ovh_sub:
1127
+ lda calls
1128
+ sta dvs
1129
+ lda calls+1
1130
+ sta dvs+1
1131
+ !loop: lda dvs
1132
+ ora dvs+1
1133
+ beq !done+
1134
+ sec
1135
+ lda t0
1136
+ sbc povh
1137
+ sta t0
1138
+ lda t0+1
1139
+ sbc #0
1140
+ sta t0+1
1141
+ lda t0+2
1142
+ sbc #0
1143
+ sta t0+2
1144
+ lda dvs
1145
+ bne !+
1146
+ dec dvs+1
1147
+ !: dec dvs
1148
+ jmp !loop-
1149
+ !done: rts
1150
+
1151
+ // ---------------------------------------------------------------
1152
+ // Fletcher-style sums over bitmap rows 0-15 in memory order
1153
+ fletcher:
1154
+ lda #<BITMAP
1155
+ sta ptr
1156
+ lda #>BITMAP
1157
+ sta ptr+1
1158
+ lda #0
1159
+ sta fl1
1160
+ sta fl2
1161
+ ldx #20 // 20 pages = 5120 bytes = 16 cell rows
1162
+ ldy #0
1163
+ !: lda (ptr),y
1164
+ clc
1165
+ adc fl1
1166
+ sta fl1
1167
+ clc
1168
+ adc fl2
1169
+ sta fl2
1170
+ iny
1171
+ bne !-
1172
+ inc ptr+1
1173
+ dex
1174
+ bne !-
1175
+ rts
1176
+
1177
+ // ---------------------------------------------------------------
1178
+ // galaxy: s0' = s1, s1' = s2, s2' = s0 + s1 + s2 (16-bit), four twists
1179
+ // a system; the name takes a letter pair from bits 0-4 of s2_hi before
1180
+ // each of the first three twists (four when bit 6 of s0_lo is set)
1181
+ galaxy:
1182
+ lda #$4a
1183
+ sta s0
1184
+ lda #$5a
1185
+ sta s0+1
1186
+ lda #$48
1187
+ sta s1
1188
+ lda #$02
1189
+ sta s1+1
1190
+ lda #$53
1191
+ sta s2
1192
+ lda #$b7
1193
+ sta s2+1
1194
+ lda #0
1195
+ sta sysn
1196
+ sta gworst
1197
+ sta gworst+1
1198
+ gx_sys: jsr timer_start
1199
+ jsr make_system
1200
+ jsr timer_stop
1201
+ lda t0+1
1202
+ cmp gworst+1
1203
+ bcc !+
1204
+ bne !new+
1205
+ lda t0
1206
+ cmp gworst
1207
+ bcc !+
1208
+ !new: lda t0
1209
+ sta gworst
1210
+ lda t0+1
1211
+ sta gworst+1
1212
+ !: inc sysn
1213
+ lda sysn
1214
+ cmp #NSYS
1215
+ bne gx_sys
1216
+ rts
1217
+
1218
+ // one system: coordinates, eight name bytes at gname + 8*sysn, then
1219
+ // the seeds are left twisted four times: the next system
1220
+ make_system:
1221
+ lda sysn
1222
+ asl
1223
+ asl
1224
+ asl
1225
+ tax
1226
+ stx gcol
1227
+ ldy sysn
1228
+ lda s1+1
1229
+ sta gx,y // x = s1_hi
1230
+ lda s0+1
1231
+ lsr
1232
+ sta gy,y // y = s0_hi >> 1
1233
+ lda s0
1234
+ and #$40
1235
+ sta lng
1236
+ lda #0
1237
+ sta pcount
1238
+ ms_pair:
1239
+ lda s2+1
1240
+ and #31
1241
+ sta tmp
1242
+ jsr twist
1243
+ lda pcount
1244
+ cmp #3
1245
+ bcc !+
1246
+ lda lng
1247
+ beq ms_skip
1248
+ !: lda tmp
1249
+ beq ms_skip // pair 0 prints nothing
1250
+ asl
1251
+ tay
1252
+ ldx gcol
1253
+ lda pairs,y
1254
+ sta gname,x
1255
+ inx
1256
+ lda pairs+1,y
1257
+ cmp #'.'
1258
+ beq !+ // '.' is the silent second letter
1259
+ sta gname,x
1260
+ inx
1261
+ !: stx gcol
1262
+ ms_skip:
1263
+ inc pcount
1264
+ lda pcount
1265
+ cmp #4
1266
+ bne ms_pair
1267
+ rts
1268
+
1269
+ twist:
1270
+ clc
1271
+ lda s0
1272
+ adc s1
1273
+ sta gt
1274
+ lda s0+1
1275
+ adc s1+1
1276
+ sta gt+1 // gt = s0 + s1
1277
+ lda s1
1278
+ sta s0
1279
+ lda s1+1
1280
+ sta s0+1
1281
+ lda s2
1282
+ sta s1
1283
+ lda s2+1
1284
+ sta s1+1
1285
+ clc
1286
+ lda gt
1287
+ adc s1
1288
+ sta s2
1289
+ lda gt+1
1290
+ adc s1+1
1291
+ sta s2+1 // s2 = s0 + s1 + old s2
1292
+ rts
1293
+
1294
+ // ---------------------------------------------------------------
1295
+ // readout, printed into bitmap rows 16-24
1296
+ print_all:
1297
+ // galaxy check: names, x, y
1298
+ ldx #0
1299
+ !: lda gname,x
1300
+ cmp exp_names,x
1301
+ beq ok1
1302
+ inc gfail
1303
+ ok1: inx
1304
+ cpx #NSYS*8
1305
+ bne !-
1306
+ ldx #0
1307
+ !: lda gx,x
1308
+ cmp exp_gx,x
1309
+ bne gbad
1310
+ lda gy,x
1311
+ cmp exp_gy,x
1312
+ beq gok
1313
+ gbad: inc gfail
1314
+ gok: inx
1315
+ cpx #NSYS
1316
+ bne !-
1317
+ lda #16
1318
+ ldx #0
1319
+ jsr at
1320
+ ldx #<t_wire
1321
+ ldy #>t_wire
1322
+ jsr str
1323
+ lda fail // wireframe checks only, so far
1324
+ jsr passfail
1325
+ ldx #<t_upd
1326
+ ldy #>t_upd
1327
+ jsr str
1328
+ lda #NF
1329
+ jsr num8
1330
+ ldx #<t_lines
1331
+ ldy #>t_lines
1332
+ jsr str
1333
+ lda last_lines
1334
+ jsr num8
1335
+
1336
+ lda #17
1337
+ ldx #0
1338
+ jsr at
1339
+ ldx #<t_worst
1340
+ ldy #>t_worst
1341
+ jsr str
1342
+ lda worst
1343
+ sta num
1344
+ lda worst+1
1345
+ sta num+1
1346
+ lda worst+2
1347
+ sta num+2
1348
+ jsr num24
1349
+ ldx #<t_last
1350
+ ldy #>t_last
1351
+ jsr str
1352
+ lda u24
1353
+ sta num
1354
+ lda u24+1
1355
+ sta num+1
1356
+ lda u24+2
1357
+ sta num+2
1358
+ jsr num24
1359
+
1360
+ lda #18
1361
+ ldx #0
1362
+ jsr at
1363
+ ldx #<t_calls
1364
+ ldy #>t_calls
1365
+ jsr str
1366
+ lda calls
1367
+ sta num
1368
+ lda calls+1
1369
+ sta num+1
1370
+ lda #0
1371
+ sta num+2
1372
+ jsr num24
1373
+ ldx #<t_cpl
1374
+ ldy #>t_cpl
1375
+ jsr str
1376
+ lda linecyc
1377
+ sta num
1378
+ lda linecyc+1
1379
+ sta num+1
1380
+ lda linecyc+2
1381
+ sta num+2
1382
+ lda calls
1383
+ sta dvs
1384
+ lda calls+1
1385
+ sta dvs+1
1386
+ jsr div24
1387
+ jsr num24
1388
+
1389
+ lda #19
1390
+ ldx #0
1391
+ jsr at
1392
+ ldx #<t_mat
1393
+ ldy #>t_mat
1394
+ jsr str
1395
+ lda mcyc
1396
+ sta num
1397
+ lda mcyc+1
1398
+ sta num+1
1399
+ lda #0
1400
+ sta num+2
1401
+ jsr num24
1402
+ ldx #<t_vtx
1403
+ ldy #>t_vtx
1404
+ jsr str
1405
+ lda vcyc
1406
+ sta num
1407
+ lda vcyc+1
1408
+ sta num+1
1409
+ lda #0
1410
+ sta num+2
1411
+ lda #7 // the ship has 7 vertices
1412
+ sta dvs
1413
+ lda #0
1414
+ sta dvs+1
1415
+ jsr div24
1416
+ jsr num24
1417
+
1418
+ ldx #<t_ship
1419
+ ldy #>t_ship
1420
+ jsr str
1421
+ lda ocyc_lo+2
1422
+ sta num
1423
+ lda ocyc_mid+2
1424
+ sta num+1
1425
+ lda ocyc_hi+2
1426
+ sta num+2
1427
+ jsr num24
1428
+
1429
+ lda #20
1430
+ ldx #0
1431
+ jsr at
1432
+ ldx #<t_gal
1433
+ ldy #>t_gal
1434
+ jsr str
1435
+ lda gfail
1436
+ jsr passfail
1437
+ ldx #<t_sys
1438
+ ldy #>t_sys
1439
+ jsr str
1440
+ lda gworst
1441
+ sta num
1442
+ lda gworst+1
1443
+ sta num+1
1444
+ lda #0
1445
+ sta num+2
1446
+ jsr num24
1447
+
1448
+ // eight systems, two to a row
1449
+ lda #0
1450
+ sta sysn
1451
+ sy_loop:
1452
+ lda sysn
1453
+ lsr
1454
+ clc
1455
+ adc #21
1456
+ pha
1457
+ lda sysn
1458
+ and #1
1459
+ beq !+
1460
+ lda #20
1461
+ !: tax
1462
+ pla
1463
+ jsr at
1464
+ lda sysn
1465
+ asl
1466
+ asl
1467
+ asl
1468
+ sta idx
1469
+ lda #8
1470
+ sta cnt
1471
+ !: ldx idx
1472
+ lda gname,x
1473
+ jsr chr
1474
+ inc idx
1475
+ dec cnt
1476
+ bne !-
1477
+ lda #' '
1478
+ jsr chr
1479
+ ldx sysn
1480
+ lda gx,x
1481
+ jsr num8
1482
+ lda #','
1483
+ jsr chr
1484
+ ldx sysn
1485
+ lda gy,x
1486
+ jsr num8
1487
+ inc sysn
1488
+ lda sysn
1489
+ cmp #NSYS
1490
+ bne sy_loop
1491
+ lda gfail
1492
+ beq !+
1493
+ inc fail
1494
+ !: ldx #39 // status row colour: green PASS, red FAIL
1495
+ lda fail
1496
+ beq !+
1497
+ lda #$20
1498
+ .byte $2c // bit abs: skip the next lda
1499
+ !: lda #$50
1500
+ !: sta MATRIX+16*40,x
1501
+ dex
1502
+ bpl !-
1503
+ rts
1504
+
1505
+ passfail:
1506
+ cmp #0
1507
+ bne !+
1508
+ ldx #<t_pass
1509
+ ldy #>t_pass
1510
+ jmp str
1511
+ !: ldx #<t_fail
1512
+ ldy #>t_fail
1513
+ jmp str
1514
+
1515
+ // cursor to text row A, column X
1516
+ at: sta prow
1517
+ stx pcol
1518
+ rts
1519
+
1520
+ str: stx sptr
1521
+ sty sptr+1
1522
+ ldy #0
1523
+ !: lda (sptr),y
1524
+ beq !+
1525
+ sty tmp
1526
+ jsr chr
1527
+ ldy tmp
1528
+ iny
1529
+ bne !-
1530
+ !: rts
1531
+
1532
+ // copy the ROM glyph of screen code A into the bitmap cell at the cursor
1533
+ chr: sta src
1534
+ lda #0
1535
+ sta src+1
1536
+ asl src
1537
+ rol src+1
1538
+ asl src
1539
+ rol src+1
1540
+ asl src
1541
+ rol src+1
1542
+ lda src+1
1543
+ ora #$d0
1544
+ sta src+1
1545
+ ldx prow
1546
+ lda rowlo,x
1547
+ sta dst
1548
+ lda rowhi,x
1549
+ sta dst+1
1550
+ lda pcol
1551
+ asl
1552
+ asl
1553
+ asl
1554
+ php
1555
+ clc
1556
+ adc dst
1557
+ sta dst
1558
+ lda dst+1
1559
+ adc #0
1560
+ plp
1561
+ adc #0 // column * 8 carries past 255 from column 32
1562
+ sta dst+1
1563
+ lda #$33 // character ROM in, I/O out; IRQs are off
1564
+ sta $01
1565
+ ldy #7
1566
+ !: lda (src),y
1567
+ sta (dst),y
1568
+ dey
1569
+ bpl !-
1570
+ lda #$37
1571
+ sta $01
1572
+ inc pcol
1573
+ rts
1574
+
1575
+ // A as 3 digits, no leading zeros
1576
+ num8: sta num
1577
+ lda #0
1578
+ sta num+1
1579
+ sta num+2
1580
+ // fall through
1581
+ // 24-bit num in decimal, no leading zeros
1582
+ num24: lda #0
1583
+ sta lng // a digit has been printed
1584
+ ldx #0
1585
+ n_dig: lda #0
1586
+ sta dig
1587
+ n_sub: sec
1588
+ lda num
1589
+ sbc p10_0,x
1590
+ sta tmp
1591
+ lda num+1
1592
+ sbc p10_1,x
1593
+ sta t1
1594
+ lda num+2
1595
+ sbc p10_2,x
1596
+ bcc n_out
1597
+ sta num+2
1598
+ lda t1
1599
+ sta num+1
1600
+ lda tmp
1601
+ sta num
1602
+ inc dig
1603
+ jmp n_sub
1604
+ n_out: lda dig
1605
+ ora lng
1606
+ bne !+
1607
+ cpx #7
1608
+ bne n_next // leading zero, not the last digit
1609
+ !: sta lng
1610
+ lda dig
1611
+ ora #$30
1612
+ stx nx
1613
+ jsr chr
1614
+ ldx nx
1615
+ n_next: inx
1616
+ cpx #8
1617
+ bne n_dig
1618
+ rts
1619
+
1620
+ // num (24 bits) /= dvs (16 bits), shift and subtract
1621
+ div24: lda #0
1622
+ sta rmd
1623
+ sta rmd+1
1624
+ ldx #24
1625
+ !: asl num
1626
+ rol num+1
1627
+ rol num+2
1628
+ rol rmd
1629
+ rol rmd+1
1630
+ sec
1631
+ lda rmd
1632
+ sbc dvs
1633
+ tay
1634
+ lda rmd+1
1635
+ sbc dvs+1
1636
+ bcc !+
1637
+ sta rmd+1
1638
+ sty rmd
1639
+ inc num
1640
+ !: dex
1641
+ bne !--
1642
+ rts
1643
+
1644
+ // ---------------------------------------------------------------
1645
+ clear_all:
1646
+ lda #0
1647
+ tay
1648
+ ldx #>BITMAP
1649
+ stx ptr+1
1650
+ sta ptr
1651
+ ldx #32 // $6000-$7fff
1652
+ !: sta (ptr),y
1653
+ iny
1654
+ bne !-
1655
+ inc ptr+1
1656
+ dex
1657
+ bne !-
1658
+ ldx #0 // white on black everywhere
1659
+ lda #$10
1660
+ !: sta MATRIX,x
1661
+ sta MATRIX+$100,x
1662
+ sta MATRIX+$200,x
1663
+ sta MATRIX+$2e8,x
1664
+ inx
1665
+ bne !-
1666
+ rts
1667
+
1668
+ // ---------------------------------------------------------------
1669
+ // data
1670
+ ovh: .byte 0
1671
+ harness: .byte 0
1672
+ povh: .byte 0
1673
+ calls: .word 0
1674
+ tot_lines: .word 0
1675
+ ep_sum: .word 0
1676
+ last_lines: .byte 0
1677
+ worst: .byte 0, 0, 0
1678
+ linecyc: .byte 0, 0, 0
1679
+ mcyc: .word 0
1680
+ vcyc: .word 0
1681
+ gworst: .word 0
1682
+ gfail: .byte 0
1683
+ fl1: .byte 0
1684
+ fl2: .byte 0
1685
+ ocyc_lo: .byte 0, 0, 0
1686
+ ocyc_mid: .byte 0, 0, 0
1687
+ ocyc_hi: .byte 0, 0, 0
1688
+ hslot: .byte 0, 16, 32 // heap slots per object
1689
+ hcount: .byte 0, 0, 0
1690
+ bit_of: .byte 1, 2, 4, 8, 16, 32, 64, 128
1691
+ px: .fill 16, 0
1692
+ py: .fill 16, 0
1693
+ hx0: .fill 48, 0
1694
+ hy0: .fill 48, 0
1695
+ hx1: .fill 48, 0
1696
+ hy1: .fill 48, 0
1697
+ hcl: .fill 48, 0
1698
+ gx: .fill NSYS, 0
1699
+ gy: .fill NSYS, 0
1700
+ gname: .fill NSYS*8, $20
1701
+ p10_0: .byte <10000000, <1000000, <100000, <10000, <1000, <100, <10, <1
1702
+ p10_1: .byte >10000000, >1000000, >100000, >10000, >1000, >100, >10, >1
1703
+ p10_2: .byte (10000000 >> 16), (1000000 >> 16), (100000 >> 16), 0, 0, 0, 0, 0
1704
+ rowlo: .fill 25, <(BITMAP + i*320)
1705
+ rowhi: .fill 25, >(BITMAP + i*320)
1706
+ // this recipe's own two-letter tokens, 0 prints nothing ('.' = silent letter)
1707
+ pairs: .text "..VOKARUZEMOTINAORULPISAEKDUWEO.FEJALOBUHINEYAGUISTAMIROCUALPEXI"
1708
+ t_wire: .text "WIREFRAME @"
1709
+ t_lines: .text " LINES @"
1710
+ t_upd: .text " UPDATES @"
1711
+ t_worst: .text "WORST UPDATE @"
1712
+ t_last: .text " LAST @"
1713
+ t_calls: .text "LINE CALLS @"
1714
+ t_cpl: .text " CYC/LINE @"
1715
+ t_mat: .text "MATRIX @"
1716
+ t_vtx: .text " CYC/VERTEX @"
1717
+ t_ship: .text " SHIP @"
1718
+ t_gal: .text "GALAXY @"
1719
+ t_sys: .text " CYC/SYSTEM @"
1720
+ t_pass: .text "PASS@"
1721
+ t_fail: .text "FAIL@"
1722
+
1723
+ // object model and expected values, from gen.py
1724
+ vx: .byte -30, 30, 30, -30, -30, 30, 30, -30, -32, 32, 32, -32, 0, 0, -44, -16, 16, 44, 16, -16
1725
+ vy: .byte -30, -30, 30, 30, -30, -30, 30, 30, 24, 24, 24, 24, -40, 0, 0, -12, -12, 0, 10, 10
1726
+ vz: .byte -30, -30, -30, -30, 30, 30, 30, 30, -32, -32, 32, 32, 0, 52, -20, -20, -20, -20, -20, -20
1727
+ fa: .byte 3, 6, 7, 2, 0, 7, 1, 2, 3, 0, 3, 1, 2, 3, 4, 5, 6, 6
1728
+ fb: .byte 2, 7, 3, 6, 1, 6, 4, 4, 4, 4, 2, 2, 3, 4, 5, 6, 1, 5
1729
+ fc: .byte 1, 4, 0, 5, 5, 2, 0, 1, 2, 3, 1, 0, 0, 0, 0, 0, 0, 4
1730
+ e0: .byte 0, 0, 0, 1, 1, 2, 2, 3, 4, 4, 5, 6, 0, 0, 0, 1, 1, 2, 2, 3, 0, 0, 0, 0, 0, 0, 1, 1, 2, 3, 4, 5
1731
+ e1: .byte 1, 3, 4, 2, 5, 3, 6, 7, 5, 7, 6, 7, 1, 3, 4, 2, 4, 3, 4, 4, 1, 2, 3, 4, 5, 6, 2, 6, 3, 4, 5, 6
1732
+ ef0: .byte 0, 0, 2, 0, 3, 0, 3, 2, 1, 1, 1, 1, 0, 3, 0, 1, 0, 2, 1, 2, 0, 0, 1, 2, 3, 4, 0, 5, 1, 2, 3, 4
1733
+ ef1: .byte 4, 2, 4, 3, 4, 5, 5, 5, 4, 2, 3, 5, 4, 4, 3, 4, 1, 4, 2, 3, 5, 1, 2, 3, 4, 5, 6, 6, 6, 6, 6, 6
1734
+ o_vst: .byte 0, 8, 13
1735
+ o_vn: .byte 8, 5, 7
1736
+ o_fst: .byte 0, 6, 11
1737
+ o_fn: .byte 6, 5, 7
1738
+ o_est: .byte 0, 12, 20
1739
+ o_en: .byte 12, 8, 12
1740
+ o_x: .byte -58, -44, 72
1741
+ o_y: .byte -52, 44, 6
1742
+ o_z: .byte 196, 200, 192
1743
+ ang_a: .byte 0, 40, 100
1744
+ ang_b: .byte 20, 0, 60
1745
+ ang_a0: .byte 0, 40, 100
1746
+ ang_b0: .byte 20, 0, 60
1747
+ stp_a: .byte 3, -2, 2
1748
+ stp_b: .byte 2, 3, -3
1749
+ exp_names: .text "GUBUDUISMIBU VOVOFE TIPIRU KAMIFEEKGUHITA HIBUDUHINEYA "
1750
+ exp_gx: .byte 2, 152, 77, 83, 180, 172, 69, 20
1751
+ exp_gy: .byte 45, 102, 121, 104, 65, 88, 124, 86
1752
+
1753
+ // ---------------------------------------------------------------
1754
+ // tables, page aligned, all below the VIC bank at $4000
1755
+ .align $100
1756
+ sqlo: .fill 512, <floor(i*i/4) // unsigned quarter squares, n = 0-511
1757
+ sqhi: .fill 512, >floor(i*i/4)
1758
+ qslo: .fill 256, <floor(sv(i)*sv(i)/4) // signed index: floor(s*s/4)
1759
+ qshi: .fill 256, >floor(sv(i)*sv(i)/4)
1760
+ sintab: .fill 256, floor(64*sin(toRadians(i*360/256)) + 0.5)
1761
+ costab: .fill 256, floor(64*sin(toRadians((i+64)*360/256)) + 0.5)
1762
+ rectab: .fill 256, recip(i) // floor(32768 / z), 255 below 129
1763
+ ylo: .fill 256, <rowaddr(i)
1764
+ yhi: .fill 256, >rowaddr(i)
1765
+ xcol: .fill 256, xcolof(i)
1766
+ xmask: .fill 256, $80 >> ((i + 32) & 7)
1767
+ .assert "tables end below the VIC bank", * <= $4000, true
1768
+
1769
+ .function sv(i) {
1770
+ .if (i < 128) .return i
1771
+ .return i - 256
1772
+ }
1773
+ .function recip(z) {
1774
+ .if (z < 129) .return 255
1775
+ .return floor(32768 / z)
1776
+ }
1777
+ .function rowaddr(vy) { // bitmap address of line vy, column 0
1778
+ .if (vy < YMIN || vy > YMAX) .return BITMAP
1779
+ .var by = vy - YMIN
1780
+ .return BITMAP + floor(by / 8) * 320 + mod(by, 8)
1781
+ }
1782
+ .function xcolof(vx) { // byte offset of column vx in its line
1783
+ .if (vx < XMIN || vx > XMAX) .return 0
1784
+ .return (vx + 32) & $f8
1785
+ }
1786
+ ```
1787
+
1788
+ ## Build
1789
+
1790
+ ```bash
1791
+ java -jar $KICKASS_JAR wireframe-ships.asm -o wireframe-ships.prg
1792
+ ```
1793
+
1794
+ The object tables, the start angles and the expected values in the
1795
+ listing were printed by this script (Python 3; Pillow only for the
1796
+ optional picture). It is an integer model of the whole program: the
1797
+ same sine and reciprocal tables, the same matrix, shift and truncation
1798
+ rules, the same winding test, outcodes and Bresenham, with the same
1799
+ endpoint swap and error term, and EOR plotting. It also checks every
1800
+ face decision against a floating-point normal test. The sine,
1801
+ reciprocal and both square tables that KickAssembler builds were read
1802
+ back out of the PRG and are identical to the model's.
1803
+
1804
+ ```python
1805
+ #!/usr/bin/env python3
1806
+ # gen.py -- integer model of wireframe-ships.asm and its galaxy readout.
1807
+ # Mirrors the machine code step for step: sine table, rotation matrix,
1808
+ # transform, reciprocal projection, screen-space winding cull, outcodes,
1809
+ # Bresenham with the same endpoint swap and error term, EOR plotting.
1810
+ # Prints the KickAssembler lines for the expected values and writes
1811
+ # model.png, the expected bitmap window, for the screenshot comparison.
1812
+ import math, sys
1813
+
1814
+ NF = 48 # updates the program runs
1815
+ XMIN, XMAX, YMIN, YMAX = 32, 191, 68, 187 # view window in virtual coordinates
1816
+
1817
+ SIN = [math.floor(64 * math.sin(2 * math.pi * i / 256) + 0.5) for i in range(256)]
1818
+ def sn(a): return SIN[a & 255]
1819
+ def cs(a): return SIN[(a + 64) & 255]
1820
+ REC = [255 if z < 129 else 32768 // z for z in range(256)]
1821
+
1822
+ # ---- objects: vertices, faces (first three vertices set the winding), edges ----
1823
+ def edges_of(faces):
1824
+ e = {}
1825
+ for fi, f in enumerate(faces):
1826
+ for k in range(len(f)):
1827
+ a, b = f[k], f[(k + 1) % len(f)]
1828
+ key = (min(a, b), max(a, b))
1829
+ e.setdefault(key, []).append(fi)
1830
+ out = []
1831
+ for (a, b), fl in sorted(e.items()):
1832
+ assert len(fl) == 2, (a, b, fl)
1833
+ out.append((a, b, fl[0], fl[1]))
1834
+ return out
1835
+
1836
+ C = 30
1837
+ cube_v = [(-C,-C,-C),(C,-C,-C),(C,C,-C),(-C,C,-C),(-C,-C,C),(C,-C,C),(C,C,C),(-C,C,C)]
1838
+ cube_f = [(0,1,2,3),(5,4,7,6),(4,0,3,7),(1,5,6,2),(4,5,1,0),(3,2,6,7)]
1839
+ pyr_v = [(-32,24,-32),(32,24,-32),(32,24,32),(-32,24,32),(0,-40,0)]
1840
+ pyr_f = [(0,4,1),(1,4,2),(2,4,3),(3,4,0),(0,1,2,3)]
1841
+ # ship: a nose over a flat hexagonal stern (original design)
1842
+ ship_v = [(0,0,52),(-44,0,-20),(-16,-12,-20),(16,-12,-20),(44,0,-20),(16,10,-20),(-16,10,-20)]
1843
+ ship_f = [(0,2,1),(0,3,2),(0,4,3),(0,5,4),(0,6,5),(0,1,6),(1,2,3,4,5,6)]
1844
+
1845
+ OBJS = [ # vertices, faces, position (ox, oy, oz), start angles (a, b), steps (da, db)
1846
+ dict(v=cube_v, f=cube_f, pos=(-58,-52,196), ang=(0,20), step=(3,2)),
1847
+ dict(v=pyr_v, f=pyr_f, pos=(-44,44,200), ang=(40,0), step=(-2,3)),
1848
+ dict(v=ship_v, f=ship_f, pos=(72,6,192), ang=(100,60), step=(2,-3)),
1849
+ ]
1850
+
1851
+ def fix_winding():
1852
+ # Order each face so that the machine's rule (cross > 0 = visible) holds
1853
+ # for a face whose outward normal points at the camera: tested once at
1854
+ # angle 0 in float, face by face.
1855
+ for o in OBJS:
1856
+ cx = sum(p[0] for p in o['v']) / len(o['v'])
1857
+ cy = sum(p[1] for p in o['v']) / len(o['v'])
1858
+ cz = sum(p[2] for p in o['v']) / len(o['v'])
1859
+ nf = []
1860
+ for f in o['f']:
1861
+ a, b, c = (o['v'][i] for i in f[:3])
1862
+ u = [b[i]-a[i] for i in range(3)]; w = [c[i]-a[i] for i in range(3)]
1863
+ n = [u[1]*w[2]-u[2]*w[1], u[2]*w[0]-u[0]*w[2], u[0]*w[1]-u[1]*w[0]]
1864
+ m = [sum(o['v'][i][k] for i in f)/len(f) for k in range(3)]
1865
+ out = (m[0]-cx)*n[0] + (m[1]-cy)*n[1] + (m[2]-cz)*n[2]
1866
+ nf.append(tuple(f) if out > 0 else tuple(reversed(f)))
1867
+ o['f'] = nf
1868
+ o['e'] = edges_of(nf)
1869
+
1870
+ def matrix(a, b):
1871
+ sa, ca, sb, cb = sn(a), cs(a), sn(b), cs(b)
1872
+ return [[ca, 0, sa],
1873
+ [(sa*sb) >> 6, cb, -((ca*sb) >> 6)],
1874
+ [-((sa*cb) >> 6), sb, (ca*cb) >> 6]]
1875
+
1876
+ def project(o, M):
1877
+ ox, oy, oz = o['pos']
1878
+ pts = []
1879
+ for (x, y, z) in o['v']:
1880
+ r = [(M[i][0]*x + M[i][1]*y + M[i][2]*z) >> 6 for i in range(3)]
1881
+ for i in range(3): assert -128 <= r[i] <= 127
1882
+ xc, yc, zc = ox + r[0], oy + r[1], oz + r[2]
1883
+ assert -127 <= xc <= 127 and -127 <= yc <= 127 and 129 <= zc <= 255, (xc, yc, zc)
1884
+ f = REC[zc]
1885
+ px = (abs(xc) * f) >> 8; py = (abs(yc) * f) >> 8
1886
+ pts.append((128 + (px if xc >= 0 else -px), 128 + (py if yc >= 0 else -py), (xc, yc, zc)))
1887
+ return pts
1888
+
1889
+ def faces_visible(o, pts, M):
1890
+ mask = 0
1891
+ for fi, f in enumerate(o['f']):
1892
+ p0, p1, p2 = (pts[i] for i in f[:3])
1893
+ dx1, dy1, dx2, dy2 = p1[0]-p0[0], p1[1]-p0[1], p2[0]-p0[0], p2[1]-p0[1]
1894
+ for d in (dx1, dy1, dx2, dy2): assert -127 <= d <= 127, d
1895
+ c = dx1*dy2 - dy1*dx2
1896
+ assert -32768 <= c <= 32767
1897
+ if c < 0: mask |= 1 << fi
1898
+ return mask
1899
+
1900
+ def outcode(x, y):
1901
+ return (1 if x < XMIN else 0) | (2 if x > XMAX else 0) | (4 if y < YMIN else 0) | (8 if y > YMAX else 0)
1902
+
1903
+ def line_pixels(x0, y0, x1, y1):
1904
+ dx, dy = abs(x1-x0), abs(y1-y0)
1905
+ px = []
1906
+ if dx >= dy:
1907
+ if x0 > x1: x0, y0, x1, y1 = x1, y1, x0, y0
1908
+ sy = 1 if y1 >= y0 else -1
1909
+ err, x, y = dx >> 1, x0, y0
1910
+ while True:
1911
+ px.append((x, y))
1912
+ if x == x1: break
1913
+ err -= dy
1914
+ if err < 0: err += dx; y += sy
1915
+ x += 1
1916
+ else:
1917
+ if y0 > y1: x0, y0, x1, y1 = x1, y1, x0, y0
1918
+ sx = 1 if x1 >= x0 else -1
1919
+ err, x, y = dy >> 1, x0, y0
1920
+ while True:
1921
+ px.append((x, y))
1922
+ if y == y1: break
1923
+ err -= dx
1924
+ if err < 0: err += dy; x += sx
1925
+ y += 1
1926
+ return px
1927
+
1928
+ def lines_for(o, a, b):
1929
+ M = matrix(a, b)
1930
+ pts = project(o, M)
1931
+ vis = faces_visible(o, pts, M)
1932
+ out = []
1933
+ for (v0, v1, fa, fb) in o['e']:
1934
+ if not (vis >> fa & 1 or vis >> fb & 1): continue
1935
+ x0, y0 = pts[v0][:2]; x1, y1 = pts[v1][:2]
1936
+ c0, c1 = outcode(x0, y0), outcode(x1, y1)
1937
+ if c0 & c1: continue # trivial reject
1938
+ out.append((x0, y0, x1, y1, 1 if (c0 | c1) else 0))
1939
+ return out, vis, pts, M
1940
+
1941
+ def float_cull(o, a, b):
1942
+ # reference: outward normal . (face point - camera) < 0, in float
1943
+ ra, rb = 2*math.pi*a/256, 2*math.pi*b/256
1944
+ def rot(p):
1945
+ x, y, z = p
1946
+ x1, z1 = x*math.cos(ra) + z*math.sin(ra), -x*math.sin(ra) + z*math.cos(ra)
1947
+ return (x1, y*math.cos(rb) - z1*math.sin(rb), y*math.sin(rb) + z1*math.cos(rb))
1948
+ mask, margin = 0, []
1949
+ for fi, f in enumerate(o['f']):
1950
+ P = [rot(o['v'][i]) for i in f[:3]]
1951
+ u = [P[1][k]-P[0][k] for k in range(3)]; w = [P[2][k]-P[0][k] for k in range(3)]
1952
+ n = [u[1]*w[2]-u[2]*w[1], u[2]*w[0]-u[0]*w[2], u[0]*w[1]-u[1]*w[0]]
1953
+ q = [P[0][k] + o['pos'][k] for k in range(3)]
1954
+ d = sum(n[k]*q[k] for k in range(3)) / (math.sqrt(sum(t*t for t in n)) * math.sqrt(sum(t*t for t in q)))
1955
+ if d < 0: mask |= 1 << fi
1956
+ margin.append(abs(d))
1957
+ return mask, margin
1958
+
1959
+ def run():
1960
+ fix_winding()
1961
+ bm = {}
1962
+ total_lines = 0; ck = 0; line_calls = 0
1963
+ heaps = [[] for _ in OBJS]
1964
+ ang = [list(o['ang']) for o in OBJS]
1965
+ cull_mismatch = 0
1966
+ per_frame = []
1967
+ for fr in range(NF):
1968
+ frame_lines = 0; frame_pix = 0
1969
+ for i, o in enumerate(OBJS):
1970
+ ang[i][0] = (ang[i][0] + o['step'][0]) & 255
1971
+ ang[i][1] = (ang[i][1] + o['step'][1]) & 255
1972
+ new, vis, pts, M = lines_for(o, *ang[i])
1973
+ fm, margin = float_cull(o, *ang[i])
1974
+ if fm != vis:
1975
+ bad = [k for k in range(len(o['f'])) if (fm ^ vis) >> k & 1]
1976
+ if any(margin[k] > 0.02 for k in bad): cull_mismatch += 1 # beyond 0.02 of edge-on
1977
+ for L in heaps[i] + new: # erase old, draw new
1978
+ line_calls += 1
1979
+ for (x, y) in line_pixels(*L[:4]):
1980
+ frame_pix += 1
1981
+ if XMIN <= x <= XMAX and YMIN <= y <= YMAX:
1982
+ bm[(x, y)] = bm.get((x, y), 0) ^ 1
1983
+ heaps[i] = new
1984
+ for L in new:
1985
+ total_lines += 1; ck = (ck + L[0] + L[1] + L[2] + L[3]) & 0xFFFF
1986
+ frame_lines += len(new)
1987
+ per_frame.append((frame_lines, frame_pix))
1988
+ return bm, heaps, total_lines, ck, cull_mismatch, per_frame, line_calls, ang
1989
+
1990
+ def fletcher(bm):
1991
+ # bitmap bytes of rows 0-15 (cells), in memory order, as the program sums them
1992
+ s1 = s2 = 0; pop = 0
1993
+ for row in range(16):
1994
+ for col in range(40):
1995
+ for ln in range(8):
1996
+ by = row*8 + ln; byte = 0
1997
+ for bit in range(8):
1998
+ bx = col*8 + bit; vx, vy = bx - 32, by + 68
1999
+ if bm.get((vx, vy), 0): byte |= 0x80 >> bit; pop += 1
2000
+ s1 = (s1 + byte) & 255; s2 = (s2 + s1) & 255
2001
+ return s1, s2, pop
2002
+
2003
+ # ---- galaxy ----
2004
+ PAIRS = "..VOKARUZEMOTINAORULPISAEKDUWEO.FEJALOBUHINEYAGUISTAMIROCUALPEXI"
2005
+ def twist(s): return [s[1], s[2], (s[0] + s[1] + s[2]) & 0xFFFF]
2006
+ def galaxy(n):
2007
+ s = [0x5A4A, 0x0248, 0xB753]; out = []
2008
+ for _ in range(n):
2009
+ long_ = s[0] & 0x40; t = list(s); name = ""
2010
+ for k in range(4):
2011
+ p = (t[2] >> 8) & 31; t = twist(t)
2012
+ if (k < 3 or long_) and p: name += PAIRS[2*p:2*p+2].replace('.', '')
2013
+ out.append((name, s[1] >> 8, (s[0] >> 8) >> 1, list(s)))
2014
+ s = t
2015
+ return out
2016
+
2017
+ def emit_data():
2018
+ fix_winding()
2019
+ def row(label, vals): print("%-8s .byte %s" % (label + ":", ", ".join(str(v) for v in vals)))
2020
+ vs, fs, es = [], [], []
2021
+ row("vx", [p[0] for o in OBJS for p in o['v']])
2022
+ row("vy", [p[1] for o in OBJS for p in o['v']])
2023
+ row("vz", [p[2] for o in OBJS for p in o['v']])
2024
+ row("fa", [f[0] for o in OBJS for f in o['f']])
2025
+ row("fb", [f[1] for o in OBJS for f in o['f']])
2026
+ row("fc", [f[2] for o in OBJS for f in o['f']])
2027
+ row("e0", [e[0] for o in OBJS for e in o['e']])
2028
+ row("e1", [e[1] for o in OBJS for e in o['e']])
2029
+ row("ef0", [e[2] for o in OBJS for e in o['e']])
2030
+ row("ef1", [e[3] for o in OBJS for e in o['e']])
2031
+ acc = [0, 0, 0]; st = [[], [], []]
2032
+ for o in OBJS:
2033
+ for k, key in enumerate(('v', 'f', 'e')):
2034
+ st[k].append(acc[k]); acc[k] += len(o[key])
2035
+ row("o_vst", st[0]); row("o_vn", [len(o['v']) for o in OBJS])
2036
+ row("o_fst", st[1]); row("o_fn", [len(o['f']) for o in OBJS])
2037
+ row("o_est", st[2]); row("o_en", [len(o['e']) for o in OBJS])
2038
+ row("o_x", [o['pos'][0] for o in OBJS]); row("o_y", [o['pos'][1] for o in OBJS]); row("o_z", [o['pos'][2] for o in OBJS])
2039
+ row("ang_a", [o['ang'][0] for o in OBJS]); row("ang_b", [o['ang'][1] for o in OBJS])
2040
+ row("ang_a0", [o['ang'][0] for o in OBJS]); row("ang_b0", [o['ang'][1] for o in OBJS])
2041
+ row("stp_a", [o['step'][0] for o in OBJS]); row("stp_b", [o['step'][1] for o in OBJS])
2042
+ g = galaxy(8)
2043
+ print('exp_names: .text "%s"' % "".join(n.ljust(8) for n, x, y, s in g))
2044
+ row("exp_gx", [x for n, x, y, s in g]); row("exp_gy", [y for n, x, y, s in g])
2045
+
2046
+ if __name__ == "__main__":
2047
+ bm, heaps, total, ck, mism, pf, calls, ang = run()
2048
+ s1, s2, pop = fletcher(bm)
2049
+ last = sum(len(h) for h in heaps)
2050
+ print("// model: %d lines in the last update, %d over the run, "
2051
+ "cull mismatches beyond 0.02 of edge-on %d" % (last, total, mism))
2052
+ print("// model: %d line calls, %d pixels visited, %d set in the window" % (calls, sum(p for l, p in pf), pop))
2053
+ print(".const EXP_LAST = %d" % last)
2054
+ print(".const EXP_TOTAL = %d" % total)
2055
+ print(".const EXP_SUM = $%04x" % ck)
2056
+ print(".const EXP_S1 = $%02x" % s1)
2057
+ print(".const EXP_S2 = $%02x" % s2)
2058
+ emit_data()
2059
+ for name, x, y, s in galaxy(8):
2060
+ print("// %-8s x=%3d y=%3d seeds %s" % (name, x, y, " ".join("%04X" % v for v in s)))
2061
+ if len(sys.argv) > 1: # expected bitmap rows 0-15, 320 x 128
2062
+ from PIL import Image
2063
+ im = Image.new("L", (320, 128), 0)
2064
+ for (x, y), v in bm.items():
2065
+ if v: im.putpixel((x + 32, y - 68), 255)
2066
+ im.save(sys.argv[1])
2067
+ ```
2068
+
2069
+ `python3 gen.py model.png` prints:
2070
+
2071
+ ```text
2072
+ // model: 19 lines in the last update, 1005 over the run, cull mismatches beyond 0.02 of edge-on 0
2073
+ // model: 1991 line calls, 64728 pixels visited, 575 set in the window
2074
+ .const EXP_LAST = 19
2075
+ .const EXP_TOTAL = 1005
2076
+ .const EXP_SUM = $84ea
2077
+ .const EXP_S1 = $8c
2078
+ .const EXP_S2 = $99
2079
+ ```
2080
+
2081
+ then the data lines of the listing, then the eight systems:
2082
+
2083
+ ```text
2084
+ // GUBUDUIS x= 2 y= 45 seeds 5A4A 0248 B753
2085
+ // MIBU x=152 y=102 seeds CD80 98B8 7A1D
2086
+ // VOVOFE x= 77 y=121 seeds F32A 4D9C 211B
2087
+ // TIPIRU x= 83 y=104 seeds D098 5394 860D
2088
+ // KAMIFEEK x=180 y= 65 seeds 83DA B420 E233
2089
+ // GUHITA x=172 y= 88 seeds B080 ACE0 778D
2090
+ // HIBUDUHI x= 69 y=124 seeds F95A 45D4 141B
2091
+ // NEYA x= 20 y= 86 seeds AD38 149C 151D
2092
+ ```
2093
+
2094
+ The screenshot check compares bitmap rows 0-15 of the exit screenshot
2095
+ with `model.png` pixel by pixel and decodes the readout with the
2096
+ character ROM:
2097
+
2098
+ ```python
2099
+ #!/usr/bin/env python3
2100
+ # check_shot.py -- compare the exit screenshot's bitmap rows 0-15 with the
2101
+ # model picture from gen.py, and decode the readout rows with the char ROM.
2102
+ # usage: check_shot.py shot.png model.png pal|ntsc chargen-901225-01.bin
2103
+ import sys
2104
+ from PIL import Image
2105
+ shot, model, region, romfile = sys.argv[1:5]
2106
+ top = 35 if region == "pal" else 23 # screenshot row of raster line 51
2107
+ im = Image.open(shot).convert("RGB"); md = Image.open(model)
2108
+ bad = sum((im.getpixel((32 + x, top + y)) != (0, 0, 0)) != (md.getpixel((x, y)) > 0)
2109
+ for y in range(128) for x in range(320))
2110
+ print("pixels differing from the model:", bad)
2111
+ rom = open(romfile, "rb").read()
2112
+ glyph = {tuple(rom[c*8:c*8+8]): c for c in range(64)}
2113
+ for row in range(16, 25):
2114
+ line = ""
2115
+ for col in range(40):
2116
+ cell = tuple(sum(0x80 >> b for b in range(8)
2117
+ if im.getpixel((32 + col*8 + b, top + row*8 + n)) != (0, 0, 0))
2118
+ for n in range(8))
2119
+ c = glyph.get(cell, 63) if any(cell) else 32
2120
+ line += chr(64 + c) if 1 <= c <= 26 else chr(c) if c >= 32 else "@"
2121
+ print(line.rstrip())
2122
+ ```
2123
+
2124
+ ## Expected output
2125
+
2126
+ Border green. The window shows the three objects in white on black;
2127
+ below it, decoded from the PNG with the character ROM (the status row is
2128
+ green, the rest white):
2129
+
2130
+ ```text
2131
+ WIREFRAME PASS UPDATES 48 LINES 19
2132
+ WORST UPDATE 155708 LAST 131620
2133
+ LINE CALLS 1991 CYC/LINE 2350
2134
+ MATRIX 523 CYC/VERTEX 1179 SHIP 47467
2135
+ GALAXY PASS CYC/SYSTEM 733
2136
+ GUBUDUIS 2,45 MIBU 152,102
2137
+ VOVOFE 77,121 TIPIRU 83,104
2138
+ KAMIFEEK 180,65 GUHITA 172,88
2139
+ HIBUDUHI 69,124 NEYA 20,86
2140
+ ```
2141
+
2142
+ That is the PAL screen. The NTSC screen differs only in the timings:
2143
+ `WORST UPDATE 157212 LAST 133169`, `CYC/LINE 2370`,
2144
+ `MATRIX 522 CYC/VERTEX 1228 SHIP 48152`, `CYC/SYSTEM 733`.
2145
+
2146
+ Screenshots from the pinned run, 20,000,000 cycles:
2147
+ `screenshots/wireframe-ships.png` (PAL) and
2148
+ `screenshots/wireframe-ships-ntsc.png` (NTSC). In both, bitmap rows
2149
+ 0-15 match `model.png` with 0 differing pixels, and 575 pixels are set,
2150
+ the model's count. The border pixel (5, 5) is (98, 213, 50) on PAL and
2151
+ (114, 189, 103) on NTSC, palette index 5 in `runtime/vice-reference.md`.
2152
+ The pinned command was run twice per model and the two PNGs were
2153
+ identical bytes each time. At 17,000,000 cycles the PAL readout is not
2154
+ yet complete.
2155
+
2156
+ The figures, measured in VICE x64sc 3.10 with the display on, so every
2157
+ figure includes the cycles badlines take:
2158
+
2159
+ | Figure | PAL | NTSC | What was timed |
2160
+ |---|---|---|---|
2161
+ | Worst update of pass 2 | 155,708 | 157,212 | all three objects, CIA1 per object, summed |
2162
+ | Last update of pass 2 | 131,620 | 133,169 | the frozen picture's update |
2163
+ | Cycles per line call | 2,350 | 2,370 | CIA2 total over the 1,991 erase and draw calls of pass 1, gate overhead removed |
2164
+ | Matrix build | 523 | 522 | one `build_matrix` |
2165
+ | Transform and project, per vertex | 1,179 | 1,228 | `xform_all` on the ship's 7 vertices, divided by 7 |
2166
+ | Ship, last update | 47,467 | 48,152 | matrix, 7 vertices, 7 faces, erase and draw |
2167
+ | One galaxy system, worst of 8 | 733 | 733 | coordinates, name, four twists |
2168
+
2169
+ Derived by arithmetic from those: the worst update is 7.9 PAL frames
2170
+ (19,656 cycles) and 9.2 NTSC frames (17,095 cycles), so this code
2171
+ animates at about 6 updates a second. The model visits 64,728 pixels in
2172
+ the 1,991 line calls, 32.5 a line, so a line costs about 72 cycles a
2173
+ pixel with its set-up spread over it. Line calls average 97,500 cycles
2174
+ an update (1,991 calls at 2,350 cycles over 48 updates): 74% of the last
2175
+ update and 63% of the worst.
2176
+
2177
+ ## Why this works
2178
+
2179
+ **Rotation.** The sine table holds `round(64 * sin)`, so a matrix entry
2180
+ is a signed byte from -64 to 64. The matrix is rotation about y by
2181
+ angle `a` followed by rotation about x by angle `b`; its four products
2182
+ of two sines are shifted right 6. A row times a vertex is three signed
2183
+ products summed in 16 bits; shifting the sum left twice and taking the
2184
+ high byte is the sum shifted right 6, rounded down. The signed multiply
2185
+ is `sq[a + b] - sq[a - b]` with `sq[s] = floor(s * s / 4)` indexed by
2186
+ the signed byte `s`. It needs no sign handling, provided `a + b` and
2187
+ `a - b` stay within -128 to 128. Matrix entries are at most 64 and
2188
+ vertex coordinates at most 52, so they do; `s = 128` wraps to -128, and
2189
+ both have the same square. The two floors cancel because `a + b` and
2190
+ `a - b` have the same parity.
2191
+
2192
+ **Projection.** Camera z runs from 129 to 255 by placement (the model
2193
+ asserts it on every vertex of every update), and
2194
+ `rectab[z] = floor(32768 / z)` fits a byte there. Screen x is
2195
+ `128 + (|x| * rectab[z]) >> 8` with the sign of x put back afterwards,
2196
+ so the result truncates towards zero on both sides. That product uses
2197
+ the unsigned quarter-square tables of 512 entries, because
2198
+ `|x| + rectab[z]` reaches 382.
2199
+
2200
+ **Culling.** A face is drawn when its first three projected vertices
2201
+ turn one way on the screen (`dx1 * dy2 - dy1 * dx2 < 0`). The model
2202
+ orders every face's vertices so that this holds for a face whose outward
2203
+ normal points at the camera. With perspective this test is exact apart
2204
+ from rounding: over the 864 face tests of the run it disagreed with a
2205
+ floating-point normal test 5 times. In each, the cosine between the face
2206
+ normal and the line of sight was within 0.018 of 0, about 1 degree from
2207
+ edge-on. The model counts a disagreement only beyond 0.02 of edge-on,
2208
+ and there were none. That check is in the Python model only; the listing
2209
+ does not do it. An edge is drawn when either of its two faces is
2210
+ visible, and only once. That matters with EOR, because an edge drawn
2211
+ twice would cancel itself.
2212
+
2213
+ **Clipping.** Each endpoint gets an outcode (left, right, above, below).
2214
+ If the two outcodes share a bit, the line is wholly outside and is
2215
+ dropped. If both are 0, the line is drawn with the loop that does not
2216
+ test pixels. Otherwise it is drawn with the loop that skips pixels
2217
+ outside the window. Bresenham then yields the same inside pixels as a
2218
+ line cut at the edge, so the model needs no intersection arithmetic.
2219
+ All coordinates fit a byte because the window is smaller than the
2220
+ virtual screen.
2221
+
2222
+ **Lines.** The line steps along its longer axis, swapping the ends so
2223
+ that the step is +1. The error term starts at half the long delta. Each
2224
+ step subtracts the short delta; when that borrows, the long delta is
2225
+ added back and the short axis steps. The subtract and the add-back are
2226
+ 8-bit, and the borrow is the carry flag, so no 9-bit error is needed.
2227
+ The plot is `ptr = ylo/yhi[y]` for the row, then
2228
+ `LDY xcol,X / LDA (ptr),Y / EOR xmask,X / STA (ptr),Y`. The window's
2229
+ bitmap x never passes 255, so the column offset fits Y. The x-major
2230
+ loop reloads `ptr` only when y steps.
2231
+
2232
+ **Why the line drawer is slow.** This is a correct, fully measured
2233
+ first recipe, not a fast plotter. The figures below are arithmetic from
2234
+ the instruction table over the listing's loops, and the pixel counts
2235
+ come from the model; only the totals were measured. An x-major pixel
2236
+ without a y step costs about 43 cycles: the plot itself
2237
+ (`LDY xcol,X / LDA (ptr),Y / EOR xmask,X / STA (ptr),Y`) is about 19,
2238
+ and the rest is the error term in zero page and the `JMP` back to the
2239
+ loop, every pixel. An x-major pixel with a y step costs about 72,
2240
+ because the row pointer is reloaded. A y-major pixel costs about 66 to
2241
+ 77, because the row pointer (17 cycles) is reloaded on every pixel. The
2242
+ clip tests add about 19 cycles to each pixel of a line that needs them,
2243
+ 32% of the pixels visited, and 9% of the pixels visited are outside the
2244
+ window and are walked for nothing. The loops account for about 4.4
2245
+ million of the 4.68 million measured line cycles; set-up is about 140
2246
+ cycles a call, about 6%. The two biggest costs are the row-pointer
2247
+ reload on every y-major pixel and the read-modify-write on every pixel
2248
+ with the error term and `cy` kept in zero page, not in registers.
2249
+
2250
+ **Erase by redraw.** Each object keeps the lines it drew (four bytes and
2251
+ the clip flag each) in a heap. The next update draws them again with EOR
2252
+ before drawing the new ones, and that erases them exactly, even where
2253
+ they cross other objects' lines. EOR has one visible cost: a pixel
2254
+ where an even number of lines meet is clear. The frozen picture has 9
2255
+ such pixels, all at or beside vertices: 8 at shared endpoints, 1 where
2256
+ two shallow edges leave a vertex along the same pixel. The model has the
2257
+ same 9.
2258
+
2259
+ **Screen.** VIC bank 1: `$DD00` bits 0-1 = `%10`, `$D018 = $18` puts the
2260
+ matrix at `$4400` and the bitmap at `$6000`. Code and tables stay below
2261
+ `$4000` (an `.assert` checks it), so the bitmap never overlaps them. The
2262
+ readout copies glyphs from the character ROM into the bitmap with `$01 =
2263
+ $33`. That is safe here because interrupts are off for the whole program
2264
+ (`pitfalls/banking.md`, `charset_under_io_invisible_to_cpu` and
2265
+ `irq_during_charen_window`).
2266
+
2267
+ **Timing.** CIA1 timers A and B, chained, are a 32-bit down-counter
2268
+ started and stopped around each object; an empty start/stop pair is
2269
+ timed once and subtracted. CIA2 is chained the same way but only runs
2270
+ between the `STA $DD0E` that resumes it and the one that pauses it
2271
+ around each line; an empty resume/pause pair is timed and subtracted
2272
+ per call. Pass 2 keeps only a `BIT harness` and a taken `BPL` per line
2273
+ call and per stored line (7 cycles each by the instruction table, about
2274
+ 400 cycles in an update of 20 lines), so its update figures are the
2275
+ technique's own work.
2276
+
2277
+ **Galaxy.** One twist sets `s0 = s1`, `s1 = s2`, `s2 = s0 + s1 + s2`
2278
+ (16-bit, carries dropped). A system's x is `s1` high byte, its y is `s0`
2279
+ high byte shifted right once. Its name takes bits 0-4 of `s2` high byte
2280
+ before each of four twists; the pair is used for the first three twists,
2281
+ and for the fourth only when bit 6 of `s0` low byte is set. Pair 0 adds
2282
+ nothing, and a `.` in the pair table is a silent second letter. After
2283
+ the four twists the seeds are the next system's. The rule is Elite's, as
2284
+ Moxon documents it; the code is this recipe's. The 32 letter pairs are
2285
+ this recipe's own, so the names are not Elite's; the seeds and
2286
+ coordinates are. The table keeps Elite's shape, index 0 unused (`..`)
2287
+ and a silent second letter at index 15 (`O.`), so every name has the
2288
+ length Elite's has and the cycle counts are unchanged. An earlier
2289
+ version used Elite's own token string (QQ16), which is game data this
2290
+ repository cannot redistribute; it printed TIBEDIED, QUBE, ... LAVE.
2291
+ Moxon's worked example gives Lave's seeds as `s0 = $AD38`,
2292
+ `s1 = $149C`, `s2 = $151D` and coordinates x = 20 and y = 86. The
2293
+ program's eighth system (NEYA here) has those seeds (model) and
2294
+ coordinates (screen).
2295
+
2296
+ ## What this recipe does not show
2297
+
2298
+ No double buffer: the objects are erased and redrawn in the displayed
2299
+ bitmap, so a line is missing from the screen between its erase and its
2300
+ redraw. The technique page describes the two-bank alternative. The
2301
+ recipe does not clip by intersection: a line that crosses the edge walks
2302
+ its outside pixels too. That costs time on long lines, and it would not
2303
+ work for coordinates beyond a byte. There is no near-plane test: camera
2304
+ z is kept at 129 or more by placement, and a game must reject or clip
2305
+ anything nearer. Objects do not hide each other. The plotter recomputes
2306
+ the row pointer on every y step; an incremental plotter that steps the
2307
+ byte address and the bit mask is faster, and was not measured here. Only
2308
+ the first galaxy is generated; the other seven come from rotating each
2309
+ seed byte left by one bit (Moxon, not run here).
2310
+
2311
+ ## Sources
2312
+
2313
+ - Mark Moxon, "Twisting the system seeds":
2314
+ https://elite.bbcelite.com/deep_dives/twisting_the_system_seeds.html
2315
+ (the twist, four twists a system, the Tibedied seeds, 8 galaxies of 256).
2316
+ - Mark Moxon, "Generating system names":
2317
+ https://elite.bbcelite.com/deep_dives/generating_system_names.html
2318
+ (bits 0-4 of `s2_hi`, bit 6 of `s0_lo`, the Lave example).
2319
+ - Mark Moxon, "Generating system data":
2320
+ https://elite.bbcelite.com/deep_dives/generating_system_data.html
2321
+ (x = `s1_hi`, y = `s0_hi` shifted right once).
2322
+ - Mark Moxon, C64 Elite variable QQ16:
2323
+ https://elite.bbcelite.com/c64/main/variable/qq16.html (Elite's 32
2324
+ two-letter tokens, 128-159; not reproduced here).