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,874 @@
1
+ ---
2
+ category: cpu
3
+ ---
4
+
5
+ <!-- doc-type: pitfall-reference -->
6
+
7
+ # CPU Pitfalls
8
+
9
+ Pitfalls in the 6510's own behaviour, and in what a toolchain does to code
10
+ that leans on it: behaviour that is locally correct but globally surprising,
11
+ such as a branch cycle count that changes with binary placement, illegal
12
+ opcodes that disappear on CMOS silicon, an indirect-jump address fetch that
13
+ wraps at page boundaries, a signed compare that turns over, an LFSR that
14
+ never leaves zero, and an assembler optimiser that separates a patch from
15
+ the instruction it patches. Each has caused cycle-tight and portable C64
16
+ code to fail in production. (An earlier version of this paragraph counted
17
+ three.)
18
+
19
+ ---
20
+
21
+ ## branch_page_cross_extra_cycle — Conditional branch costs +1 cycle when crossing a page boundary
22
+
23
+ **Severity:** high
24
+ **Region:** both
25
+ **Triggered by techniques:** stable_raster_irq, self_modifying_code, unrolled_loops, double_irq, sideborder_open, fli_image, charset_copy_rom_to_ram, isqrt_16bit, atan2_8bit, bresenham_line, zero_page_burst
26
+ **Mitigated by techniques:** bit_test_trick
27
+
28
+ ### Symptom
29
+
30
+ Cycle-counted raster IRQ code drifts by exactly one cycle depending on where
31
+ the linker places the assembled output. Color bars that were pixel-perfect in
32
+ development land on the wrong line in the final build. A stable-raster handler
33
+ that was verified working at $2000 breaks when the code is relocated to $2FF0
34
+ and a branch straddles the $3000 boundary. The symptom is a one-cycle jitter
35
+ that is completely reproducible — it is not random, it just changes with the
36
+ binary's load address.
37
+
38
+ ### Mechanism
39
+
40
+ All conditional branches on the 6510 — BCC, BCS, BEQ, BMI, BNE, BPL, BVC,
41
+ BVS — use the relative addressing mode. The branch instruction is 2 bytes
42
+ (opcode + signed displacement). Cycle cost follows three cases:
43
+
44
+ | Outcome | Cycles |
45
+ |---------|--------|
46
+ | Branch not taken | 2 |
47
+ | Branch taken, target on same page as the instruction after the branch | 3 |
48
+ | Branch taken, target on a different page than the instruction after the branch | 4 |
49
+
50
+ The "page" check compares the high byte of `PC + 2` (where the CPU is after
51
+ fetching the 2-byte branch instruction) with the high byte of the branch target.
52
+ If they differ, the CPU performs an extra internal cycle to fix up the high byte
53
+ of the program counter — a "page-crossing penalty."
54
+
55
+ For code that does not need cycle precision, this is irrelevant — one extra
56
+ cycle out of thousands is noise. For stable-raster IRQ handlers, per-line
57
+ raster effects, or sprite multiplexers where the cycle budget per scanline is
58
+ counted in single digits, a one-cycle slip means the write to `$D020` or
59
+ `$D001` lands on the wrong scanline. Because the penalty depends on the
60
+ branch target's page rather than any runtime value, the same source assembles
61
+ to different cycle counts depending on the `.o` file's placement in the binary.
62
+
63
+ The pitfall is most acute in routines that loop across page boundaries. A
64
+ `BNE loop` at `$20FD` branching back into `$20xx` costs 3 cycles. The
65
+ identical instruction at `$20FF` branching to the same target costs 4 cycles
66
+ because `$20FF + 2 = $2101`, whose high byte differs from `$20xx`.
67
+
68
+ ### Fix
69
+
70
+ **Alignment.** The standard fix for KickAssembler is a `.align $100` directive
71
+ before the branch target. This forces the target to the start of the next
72
+ page, guaranteeing that the branch and its target share the same high byte for
73
+ any branch instruction within the first 127 bytes of that page:
74
+
75
+ ```kick
76
+ .align $100 // force raster_loop to $xx00
77
+ raster_loop:
78
+ lda $d012 // 4 cycles (absolute read)
79
+ cmp #TARGET // 2 cycles
80
+ bne raster_loop // 3 cycles — same page guaranteed by .align
81
+ ```
82
+
83
+ **Eliminate the branch.** Branchless equivalents remove the variable entirely.
84
+ Tight polling loops can be replaced with NOP chains (`NOP` = 2 cycles; the
85
+ undocumented one-byte NOPs $1A/$3A/$5A are also 2 cycles and 1 byte, so they
86
+ buy nothing over `NOP` except a CMOS incompatibility — an earlier version of
87
+ this sentence recommended them; on a 65816 $1A/$3A/$5A are `INC A`/`DEC A`/
88
+ `PHY`, measured in xscpu64, so the third one corrupts the stack — see
89
+ illegal_opcode_portability below. For a 3-cycle pad use the
90
+ legal `bit zp`, or `nop zp` ($04) if the flags must survive — noting that $04
91
+ is itself undocumented and carries the same portability caveat; see the
92
+ padding section of `docs/hardware/6502-illegal-opcodes.md`) or the `BIT $abs`
93
+ skip trick. A spin-wait with `DEC zp / BNE` that straddles a page boundary
94
+ can be replaced with an unrolled NOP sequence calibrated at link time.
95
+
96
+ **Empirical rule for stable raster IRQs.** Place the entire timing-critical
97
+ section in a 256-byte aligned region using `.align $100`. Two separate
98
+ constraints apply. Page: a taken branch pays the +1 only when the high byte of
99
+ PC+2 differs from the target's, so inside a page-aligned section a branch to a
100
+ target in that section crosses only if its opcode sits on the page's last two
101
+ bytes (offset $FE or $FF) — a branch at offset $F0 back to offset $80 still
102
+ costs 3. Range: the displacement is -128..+127 from PC+2, so a backward branch
103
+ to the aligned start can sit no further than offset 126. Keeping the whole
104
+ section under 128 bytes satisfies both at once, which is why it is a safe rule
105
+ of thumb, but it is the range limit, not the page rule, that the 128 figure
106
+ comes from (an earlier version of this paragraph attributed it to the page
107
+ rule; measured in x64sc: `BNE` at $2FFD to $3000 costs 4, at $20FD back to
108
+ $20EE costs 3, at $21FF back to $21D0 costs 4). Verify the final cycle count
109
+ in VICE x64sc using the `cpuhistory` monitor command.
110
+
111
+ ### Worked example
112
+
113
+ ```kick
114
+ // BAD: branch target may or may not be on the same page as the BNE
115
+ // Cycle cost of BNE depends on the binary's load address — unpredictable.
116
+
117
+ stable_raster_entry:
118
+ pha
119
+ txa
120
+ pha
121
+ tya
122
+ pha
123
+
124
+ // Double-IRQ jitter removal loop
125
+ // If this loop straddles a page boundary, the BNE costs 4 cycles on
126
+ // some runs and 3 on others — raster hits the wrong line.
127
+ jitter_loop:
128
+ lda $d012 // 4 cycles
129
+ cmp #TARGET_LINE // 2 cycles
130
+ bne jitter_loop // 3 OR 4 cycles — page-dependent
131
+
132
+ lda #BLACK
133
+ sta $d020 // Lands on wrong line if BNE cost was 4 not 3
134
+
135
+
136
+ // GOOD: force both jitter_loop and the BNE target to the same page.
137
+
138
+ .align $100
139
+ stable_raster_entry_fixed:
140
+ pha
141
+ txa
142
+ pha
143
+ tya
144
+ pha
145
+
146
+ jitter_loop_fixed:
147
+ lda $d012 // 4 cycles
148
+ cmp #TARGET_LINE // 2 cycles
149
+ bne jitter_loop_fixed // Always 3 cycles — same page guaranteed
150
+
151
+ lda #BLACK
152
+ sta $d020 // Arrives on the correct scanline, every time
153
+ ```
154
+
155
+ ### Cross-references
156
+
157
+ - Technique `stable_raster_irq` (`docs/techniques/raster.md`) — the double-IRQ
158
+ jitter removal loop where this pitfall most commonly strikes
159
+ - Technique `self_modifying_code` (`docs/techniques/cpu-cycle-tricks.md`) —
160
+ SMC blocks with tight backward branches need page-aligned placement
161
+ - Technique `unrolled_loops` (`docs/techniques/cpu-cycle-tricks.md`) — remaining
162
+ loop-exit branches must be checked for page-crossing after unrolling
163
+ - Register `D012` — the raster compare register read in every polling loop
164
+
165
+ ---
166
+
167
+ ## illegal_opcode_portability — Illegal opcodes behave correctly on NMOS 6510 but break on CMOS and SuperCPU targets
168
+
169
+ **Severity:** medium
170
+ **Region:** both
171
+ **Triggered by techniques:** illegal_opcode_tricks, bit_test_trick
172
+
173
+ ### Symptom
174
+
175
+ Code using LAX, SAX, AXS, ALR, ARR, DCP, or the RMW family works perfectly on
176
+ real C64 hardware and under VICE. The same code loaded into a SuperCPU-equipped
177
+ machine executes differently or hangs. A sim6502 unit test in strict CMOS mode
178
+ fails on every illegal opcode. An assembler in 65C02 mode refuses the
179
+ mnemonics outright (KickAssembler `.cpu _65c02`: "Pseudo command 'lax' not
180
+ defined"; ca65 `.setcpu "65C02"`: error) — but a routine emitted with `.byte`
181
+ sails through and mis-executes on the CMOS part with no error at all. (An
182
+ earlier version of this sentence said the assembler silently treats the bytes
183
+ as NOPs; it does not — measured with KickAssembler 5.25 and ca65.) The
184
+ assumption that the target CPU is NMOS was never documented.
185
+
186
+ ### Mechanism
187
+
188
+ Illegal opcodes arise from the NMOS 6502 decode matrix. Each opcode byte selects
189
+ a column (addressing mode) and a row (operation). The "illegal" cells are
190
+ undefined combinations where two operation strobes activate simultaneously —
191
+ producing fused instructions that the MOS engineers never designed but that the
192
+ silicon executes deterministically. The safe tier (LAX, SAX, ANC, ALR, ARR,
193
+ AXS, DCP, ISC, SLO, RLA, SRE, RRA) is consistent across every NMOS 6502 and
194
+ 6510 ever manufactured.
195
+
196
+ The portability boundary is the NMOS / CMOS divide:
197
+
198
+ **65C02 (CMOS revision):** All undefined opcode slots were deliberately filled
199
+ with explicit NOPs of varying byte lengths and cycle counts. The decode matrix
200
+ was redesigned. On a 65C02 the byte $A7 (LAX zero-page) is either a NOP of
201
+ vendor-specific length or, on Rockwell/WDC parts, a bit-manipulation
202
+ instruction (the $x7 column is SMB/RMB there); either way A and X are not
203
+ loaded and the program continues from the wrong state. (An earlier version of
204
+ this paragraph stated flatly "a 2-cycle 2-byte NOP"; that varies by 65C02
205
+ vendor and was not measured here.) On the 6510, $A7 executes `A = X = M[zp]`
206
+ — a completely different side effect.
207
+
208
+ **65816 (WDC 16-bit extension, used in SuperCPU):** The SuperCPU accelerator for
209
+ the C64 fits a 65816 CPU and runs C64 code in emulation mode. The 65816 has no
210
+ undefined opcodes: every NMOS illegal-opcode byte is a live 65816 instruction
211
+ with its own memory, stack or register side effects, not a silent NOP. (An
212
+ earlier version of this paragraph and the table below said these bytes execute
213
+ as NOPs on the SuperCPU; measured in xscpu64, VICE 3.10: $A7 $F0 loaded A
214
+ through the 24-bit pointer at $F0 and left X unchanged, $87 $F0 stored A
215
+ through that pointer, $C7 $F0 compared without decrementing, $4B pushed one
216
+ byte, $1A incremented A, $EB swapped A with B.) Code that runs identically on
217
+ 6510 and 8500 will misbehave on SuperCPU.
218
+
219
+ **8500 (late C64 and C64C):** The 8500 is the same NMOS microarchitecture as the
220
+ 6510, shrunk to a smaller process. The safe illegal opcodes behave identically
221
+ to the 6510. This part is fine.
222
+
223
+ **VICE default mode (x64sc):** VICE in its default cycle-exact mode (x64sc)
224
+ implements the safe NMOS illegal opcodes faithfully. Development under VICE is
225
+ not sufficient to catch CMOS portability issues — VICE matches the real 6510.
226
+
227
+ **sim6502:** Behavior depends on the configuration. sim6502 may run in either
228
+ NMOS or strict mode. Code that relies on illegal opcodes must be tested under the
229
+ same configuration that the project ships for.
230
+
231
+ The second portability concern is the **unstable tier:** XAA (ANE), LAX #imm
232
+ ($AB), AHX, TAS, SHX, SHY. These involve a floating internal bus whose value
233
+ varies by chip revision and thermal state. They must never appear in shipping
234
+ code.
235
+
236
+ | Opcode | NMOS 6510 | 65C02 (varies by vendor; not measured here) | 65816 / SuperCPU (xscpu64 unless marked) | Notes |
237
+ |--------|-----------|------------------|------------------|-------|
238
+ | LAX zp ($A7) | A=X=M | NOP or SMB/RMB | `LDA [dp]` — 24-bit pointer read, X untouched (measured) | Safe NMOS only |
239
+ | SAX zp ($87) | M=A&X | NOP or SMB/RMB | `STA [dp]` — writes A through a 24-bit pointer (measured) | Safe NMOS only |
240
+ | ALR #imm ($4B) | A=(A&imm)>>1 | NOP | `PHK` — pushes one byte (measured) | Safe NMOS only |
241
+ | ARR #imm ($6B) | A=ROR(A&imm) quirky flags | NOP | `RTL` — pops a 24-bit return address and jumps there (measured) | Safe NMOS only |
242
+ | AXS #imm ($CB) | X=(A&X)-imm | NOP | `WAI` — stalls until the next IRQ/NMI assertion, even with I set; permanent only if no source is running (measured: 58-line stall) | Safe NMOS only |
243
+ | DCP zp ($C7) | M--; CMP A,M | NOP or SMB/RMB | `CMP [dp]` — compares, no decrement (measured) | Safe NMOS only |
244
+ | SLO/RLA/SRE/RRA zp ($07/$27/$47/$67) | RMW+combine | NOP or RMB | `ORA [dp]` / `AND [dp]` / `EOR [dp]` / `ADC [dp]` — plain reads through a 24-bit pointer, no RMW ($07 measured; the other three from the opcode map) | Safe NMOS only |
245
+ | XAA #imm ($8B) | unstable | NOP | `PHB` — pushes the data bank (measured) | **NEVER USE** |
246
+ | LAX #imm ($AB) | unstable | NOP | `PLB` — pulls one byte into the data bank, A untouched (measured) | **NEVER USE** |
247
+
248
+ The table's third column used to read "NOP" for every row under a single
249
+ "65C02 / SuperCPU" heading; the SuperCPU column was wrong in every row, and the
250
+ 65C02 column depends on which vendor's part is fitted. The one-byte NOPs are
251
+ not exempt either: $1A is `INC A` and $EB is `XBA` on the 65816 (measured).
252
+
253
+ ### Fix
254
+
255
+ **Document the dependency.** At minimum, add a file-level comment to any source
256
+ file that uses illegal opcodes. The comment should state which opcodes are used,
257
+ why, and that the code requires a stock C64 NMOS 6510 or 8500 CPU:
258
+
259
+ ```kick
260
+ // This file uses LAX ($A7), DCP ($C7), and SLO ($07) for cycle savings
261
+ // in the sprite multiplexer inner loop. These are NMOS-only opcodes.
262
+ // They work on: 6510 (C64), 8500 (C64C), 8502 (C128 native mode).
263
+ // They misbehave on: SuperCPU (65816 — each byte is a live instruction
264
+ // with side effects), any CMOS board, 65C02.
265
+ // Verify under x64sc (VICE) before shipping. Do not run in sim6502 strict mode.
266
+ ```
267
+
268
+ **Verify in the target emulator.** If the project uses sim6502 for unit tests,
269
+ confirm that the sim6502 configuration matches NMOS behavior. If it does not,
270
+ either add a separate test harness for the illegal-opcode routines that runs
271
+ under VICE, or provide legal fallback paths for the test environment.
272
+
273
+ **Replace with legal equivalents when portability matters.** The cost is 1–3
274
+ extra cycles and 1–2 extra bytes per site. See `illegal_opcode_tricks` in
275
+ `cpu-cycle-tricks.md` for the equivalent legal sequence for each opcode.
276
+
277
+ ### Worked example
278
+
279
+ ```kick
280
+ // BAD: uses LAX and DCP without documentation
281
+ // Works on real C64 and VICE. Misbehaves on SuperCPU — the bytes are not
282
+ // skipped, they execute as 65816 instructions with other side effects.
283
+
284
+ multiplex_loop:
285
+ lax sprite_y,y // A = X = sprite_y[y] — NMOS only ($B7: LAX zp,Y; there is no zp,X form)
286
+ dcp compare_y // compare_y-- ; cmp A, new compare_y — NMOS only ($C7: DCP zp)
287
+ bcc multiplex_done
288
+ iny
289
+ bne multiplex_loop
290
+
291
+ multiplex_done:
292
+ sty active_sprites
293
+
294
+
295
+ // GOOD: same logic with documentation and legal fallback comments
296
+
297
+ // NMOS-ONLY section: LAX ($B7 zp,Y) and DCP ($C7 zp) used for cycle savings.
298
+ // Tested on: 6510 (real HW), 8500 (real HW), VICE x64sc PAL.
299
+ // Misbehaves on 65C02 and 65816/SuperCPU (see table above).
300
+
301
+ multiplex_loop_nmos:
302
+ lax sprite_y,y // A = X = sprite_y[Y] — $B7 zp,Y, 4 cycles / 2 bytes
303
+ dcp compare_y // compare_y-- ; flags vs A — $C7 zp, 5 cycles / 2 bytes
304
+ bcc multiplex_done_nmos
305
+ iny
306
+ bne multiplex_loop_nmos
307
+
308
+ multiplex_done_nmos:
309
+ sty active_sprites
310
+ ```
311
+
312
+ An earlier version of the GOOD listing wrapped both instructions in `.byte`
313
+ macros ("assembler won't accept LAX zp,Y natively") and used $D7 (DCP zp,X)
314
+ where the BAD listing used $C7 (DCP zp) — different addressing, different
315
+ logic, and a 6-cycle count that belonged to the wrong mode. KickAssembler 5.25
316
+ assembles `lax sprite_y,y` to `B7` and `dcp compare_y` to `C7` natively
317
+ (measured), so the macros were unnecessary; worse, `.byte $b7, addr` with a
318
+ non-zero-page `addr` silently truncated the label to its low byte with no
319
+ error. If a `.byte` escape is ever kept, guard it with
320
+ `.errorif addr >= $100, "operand not in zero page"`, not `.assert` — measured
321
+ on KickAssembler 5.25, a failed `.assert` still writes the PRG and exits 0,
322
+ while `.errorif` aborts with exit 1 and no output file.
323
+
324
+ ### Cross-references
325
+
326
+ - Technique `illegal_opcode_tricks` (`docs/techniques/cpu-cycle-tricks.md`) —
327
+ per-opcode semantics, cycle counts, and stable vs unstable tier classification
328
+ - Doc `docs/hardware/6502-illegal-opcodes.md` — per-opcode reference with flag
329
+ effects and per-revision availability
330
+ - VICE x64sc — the cycle-exact NMOS-faithful emulator; use as verification platform
331
+
332
+ ---
333
+
334
+ ## jmp_indirect_page_boundary_bug — JMP ($xxFF) fetches the high byte from $xx00 instead of $(xx+1)00
335
+
336
+ **Severity:** high
337
+ **Region:** both
338
+ **Triggered by techniques:** jump_table_dispatch, basic_extension_wedge
339
+ **Mitigated by techniques:** jump_table_dispatch
340
+
341
+ ### Symptom
342
+
343
+ A jump table dispatch jumps to a completely wrong address. Changing the target
344
+ address in the table has no effect. Moving the table by one byte in memory
345
+ makes the bug disappear; moving it back brings it back. Moving it by two does
346
+ not help in general — a 2-byte stride keeps every entry's parity, so the fault
347
+ just shifts to the neighbouring entry (it only clears if that neighbour would
348
+ fall off the end of the table); an earlier version of this sentence said one
349
+ or two bytes. The bug is
350
+ deterministic: it fires whenever a jump table entry's low-byte slot falls at
351
+ an address ending in $FF. The same bug occurs when `JMP ($addr)` is used
352
+ directly and the vector is placed at $xxFF.
353
+
354
+ ### Mechanism
355
+
356
+ `JMP ($addr)` (opcode $6C) is the 6510's indirect jump. It takes the 16-bit
357
+ address stored at the 2-byte operand location and jumps there. The fetch works
358
+ as two separate byte reads:
359
+
360
+ 1. Low byte of destination: fetched from `$addr`.
361
+ 2. High byte of destination: fetched from `$addr + 1`.
362
+
363
+ The 6510 uses an 8-bit adder with no carry propagation for the second-byte
364
+ fetch. The high byte of the address is not incremented when `$addr + 1` would
365
+ cross a page boundary. Concretely:
366
+
367
+ - If `$addr` = $10FE, the low byte is read from $10FE and the high byte from
368
+ $10FF. Correct.
369
+ - If `$addr` = $10FF, the low byte is read from $10FF and the high byte from
370
+ $1000 (not $1100). **Wrong.**
371
+
372
+ The CPU wraps the low byte of the pointer address within the same page. This is
373
+ not a timing issue or an edge case in the address decoder — it is the documented
374
+ behavior of the original 6502 silicon, reproduced faithfully in every NMOS
375
+ 6510 and 8500. It is sometimes called the "JMP indirect page-wrap bug" or the
376
+ "6502 JMP indirect bug."
377
+
378
+ The bug surfaces whenever an entry's low-byte slot lands at $xxFF. With a
379
+ page-aligned table at $xx00 and 2-byte strides, all slots are at even offsets
380
+ from $xx00 and none land at $xxFF. The only dangerous placement is when the
381
+ assembler places the table at an offset that puts any entry's low-byte at an
382
+ address ending in $FF.
383
+
384
+ `jump_table_dispatch` is on both metadata lines above for that reason: the
385
+ bug arises in the naive form of the technique, a `JMP ($abs)` through an
386
+ unaligned table, and the technique's store-then-jump form (self-modified
387
+ `JMP $abs`, or a page-aligned table) is what sidesteps it.
388
+
389
+ **CMOS note:** The 65C02 corrects this bug — it always fetches the high byte
390
+ from `$addr + 1` with correct carry. Code targeting both 6510 and 65C02 must
391
+ still avoid $xxFF placement to be safe on the 6510.
392
+
393
+ ### Fix
394
+
395
+ **Never place a JMP indirect vector or jump table entry at a $xxFF address.**
396
+
397
+ The safest approach is page-alignment. The bug fires when the low-byte slot
398
+ of a jump table entry is at an address ending in $FF — i.e., when
399
+ `table_base + (2 * index)` == $xxFF. For a page-aligned table at $xx00,
400
+ entries land at $xx00, $xx02, $xx04, ... $xxFE — all even, none at $xxFF.
401
+ Page alignment with a 2-byte stride is a complete fix.
402
+
403
+ ```kick
404
+ // FIXED: page-aligned jump table, no entry at $xxFF
405
+
406
+ .align $100
407
+ dispatch_table:
408
+ .word handler_0 // vector at $xx00 — safe
409
+ .word handler_1 // vector at $xx02 — safe
410
+ .word handler_2 // vector at $xx04 — safe
411
+ // ... entries continue at $xx06, $xx08, ... $xxFE — all safe
412
+
413
+ dispatch:
414
+ lda current_state
415
+ asl
416
+ tax
417
+ lda dispatch_table,x
418
+ sta jmp_indir+1
419
+ lda dispatch_table+1,x
420
+ sta jmp_indir+2
421
+ jmp_indir:
422
+ jmp $0000 // Self-modified; avoids JMP ($abs) entirely
423
+ ```
424
+
425
+ The example above uses a self-modified `JMP $abs` rather than `JMP ($abs)`.
426
+ The self-modified form is immune to the page-wrap bug because it uses the
427
+ absolute addressing mode ($4C), not the indirect mode ($6C). The two-step
428
+ store-then-jump pattern from `jump_table_dispatch` in `cpu-cycle-tricks.md`
429
+ sidesteps the bug entirely.
430
+
431
+ If `JMP ($abs)` must be used directly (for code-size reasons, or when the
432
+ vector table is not under the programmer's control), add a build-time assertion:
433
+
434
+ ```kick
435
+ // KickAssembler build-time guard: refuse to build if the vector sits at $xxFF
436
+ .errorif (jmp_vector & $FF) == $FF, "jmp_vector at $xxFF: JMP ($abs) would fetch the wrong high byte"
437
+ ```
438
+
439
+ This turns a silent runtime misfire into a failed build. Use `.errorif`,
440
+ not `.assert`: measured on KickAssembler 5.25, a failed `.assert` prints a
441
+ message but still writes the PRG and exits 0, so a build script never sees
442
+ it, while `.errorif` aborts with exit 1 and no output file (an earlier
443
+ version of this guard used `.assert`).
444
+
445
+ ### Worked example
446
+
447
+ ```kick
448
+ // BAD: vector table placed without alignment check
449
+ // If any table entry lands at $xxFF, the dispatch goes to a random address.
450
+
451
+ // Suppose the assembler places this table at $27C0 (example)
452
+ dispatch_table_bad:
453
+ .word handler_0 // at $27C0 — safe
454
+ .word handler_1 // at $27C2 — safe
455
+ .word handler_2 // at $27C4 — safe
456
+ // ... 15 more entries ...
457
+ .word handler_17 // at $27E2 — safe
458
+ .word handler_18 // at $27E4 — safe
459
+ // If the table started at $27C1 instead, handler_31 would be at $27FF
460
+ // ($27C1 + $3E) — the bug would fire silently. (An earlier version said
461
+ // $27C2, which puts handler_31 at $2800: an even base can never land a
462
+ // 2-byte-stride entry on $xxFF.)
463
+
464
+ // The 6510 has no JMP (abs,X) — that is a 65C02 instruction, and an earlier
465
+ // version of this example used it. The 6502 idiom that hits the bug is an
466
+ // indirect JMP whose operand is patched to point at the table entry:
467
+ dispatch_bad:
468
+ lda current_state
469
+ asl
470
+ clc
471
+ adc #<dispatch_table_bad
472
+ sta jmp_ind+1
473
+ lda #>dispatch_table_bad
474
+ adc #0
475
+ sta jmp_ind+2
476
+ jmp_ind:
477
+ jmp ($0000) // reads the vector from the table entry; if that
478
+ // entry sits at $xxFF the high byte comes from $xx00
479
+
480
+
481
+ // GOOD: page-aligned table + self-modified absolute JMP
482
+
483
+ .align $100
484
+ dispatch_table_good:
485
+ .word handler_0 // at $2800 — safe
486
+ .word handler_1 // at $2802 — safe
487
+ .word handler_2 // at $2804 — safe
488
+ .word handler_3 // at $2806 — safe
489
+ // All entries at even offsets from $2800 — none at $xxFF
490
+
491
+ dispatch_good:
492
+ lda current_state // 4 cycles
493
+ asl // 2 cycles — word offset
494
+ tax // 2 cycles
495
+ lda dispatch_table_good,x // 4 cycles — low byte
496
+ sta jmp_abs+1 // 4 cycles — patch low byte of JMP operand
497
+ lda dispatch_table_good+1,x // 4 cycles — high byte
498
+ sta jmp_abs+2 // 4 cycles — patch high byte
499
+ jmp_abs:
500
+ jmp $0000 // 3 cycles — absolute, not indirect; no page-wrap bug
501
+
502
+
503
+ // BUILD-TIME GUARD: if you must use JMP ($abs), refuse to build an unsafe address.
504
+ // Place this near any JMP ($abs) in the codebase. (.errorif aborts the build;
505
+ // .assert, which an earlier version used here, only prints and still emits the PRG.)
506
+ .var MY_VECTOR = $2802
507
+ .errorif (MY_VECTOR & $FF) == $FF, "MY_VECTOR at $xxFF: JMP indirect page-wrap bug"
508
+ ```
509
+
510
+ ### Cross-references
511
+
512
+ - Technique `jump_table_dispatch` in `docs/techniques/cpu-cycle-tricks.md` —
513
+ the self-modified `JMP $abs` pattern avoids this bug by design; prefer it
514
+ over `JMP ($abs)` in new code
515
+ - Doc `docs/hardware/6510-cpu-reference.md` — the Indirect addressing mode
516
+ section documents the bug; only opcode $6C (`JMP ($abs)`) triggers the wrap,
517
+ not `JMP $abs` ($4C)
518
+
519
+ ## lfsr_zero_state_lockup — An LFSR seeded with zero outputs zero for ever
520
+
521
+ **Severity:** medium
522
+ **Region:** both
523
+ **Triggered by techniques:** lfsr_random, attract_mode_input_replay, difficulty_ramp_tables, ghost_target_tile_ai, seeded_level_fill, starfield, procedural_seed_universe, fire_effect, screen_dissolve_lfsr
524
+ **Mitigated by techniques:** lfsr_random
525
+
526
+ ### Symptom
527
+
528
+ Every "random" value the game produces is zero: enemies spawn in the
529
+ same corner, the starfield is one column, the noise pattern is blank.
530
+ It works on the developer's machine and fails on another, or fails
531
+ only after a reset, because the seed happened to be zero there.
532
+
533
+ ### Mechanism
534
+
535
+ A Galois LFSR shifts its state right and XORs the tap mask in when the
536
+ bit that fell out was 1. From state zero the bit that falls out is 0,
537
+ nothing is XORed in, and the state is zero again; the map fixes zero
538
+ and never leaves it. The 2^n - 1 non-zero states form the one cycle
539
+ the period figures on `lfsr_random` describe (255 and 65535, measured
540
+ in VICE x64sc 3.10); zero is not on it. Any seed source can deliver
541
+ zero: two `$D41B` reads that both return `$00`, a timer read at a
542
+ phase where its low and high bytes happen to be zero, a frame count
543
+ of zero because the player pressed fire on the first frame, or a
544
+ variable the loader never initialised. The technique is on both
545
+ metadata lines above because the lockup arises in a naive seeding of
546
+ `lfsr_random` and the seed check the technique specifies cures it.
547
+
548
+ ### Fix
549
+
550
+ Test the seed before the first step and replace zero with a non-zero
551
+ constant. Test the 8-bit and 16-bit registers separately: a 16-bit
552
+ seed can be non-zero while its low byte, used to seed an 8-bit
553
+ register, is zero.
554
+
555
+ ### Worked example
556
+
557
+ ```c
558
+ // BAD: whatever the sources gave is the seed
559
+ seed = (sid.random << 8) | sid.random;
560
+ s16 = seed; // zero stays zero for ever
561
+
562
+ // FIXED: zero is replaced before the first step
563
+ seed = (sid.random << 8) | sid.random;
564
+ if (seed == 0)
565
+ seed = 0xACE1;
566
+ s16 = seed;
567
+ seed8 = (char)seed;
568
+ if (seed8 == 0)
569
+ seed8 = 0x01;
570
+ ```
571
+
572
+ The 6502 form is `lda seed / ora seed+1 / bne ok / lda #$e1 / sta seed /
573
+ lda #$ac / sta seed+1 / ok:` (rung 3, not timed).
574
+
575
+ ### Cross-references
576
+
577
+ - Technique `lfsr_random` in `docs/techniques/maths.md` — taps, periods,
578
+ seeding from `$D41B`, a CIA timer and player input
579
+ - Recipe `docs/recipes/oscar64/lfsr-random.md` — the seed check in a
580
+ built and run listing
581
+ - `docs/hardware/sid-reference.md` (`$D41B`) — the noise register drifts
582
+ to all ones under TEST and never reads zero there, but a running noise
583
+ voice can return `$00`
584
+
585
+ ## signed_compare_bmi_overflow — BMI after a subtract gives the wrong order when the difference overflows
586
+
587
+ **Severity:** high
588
+ **Region:** both
589
+ **Triggered by techniques:** compare_16bit_and_signed, fixed_point_8_8, tile_grid_collision, slope_collision, nav_area_pathfinding, atan2_8bit, game_tree_search, bresenham_line, solid_vector_3d, voxel_landscape, car_contact_response, lane_pursuit_ai
590
+ **Mitigated by techniques:** compare_16bit_and_signed
591
+
592
+ ### Symptom
593
+
594
+ A signed limit check works through every test and fails on one input:
595
+ a sprite falling at a large negative velocity is treated as rising, an
596
+ object far to the left of a boundary is placed to its right, a
597
+ platformer's jump never terminates once the velocity passes a certain
598
+ size. The failing cases are the pairs of opposite sign that are far
599
+ apart, so a small test level never shows them.
600
+
601
+ ### Mechanism
602
+
603
+ `SEC / SBC b` leaves `a - b` in `A` and `N` is bit 7 of that byte. Bit 7
604
+ is the sign of the true difference only when the difference fits in
605
+ -128 to 127. `-128 - 127` is `-255`; the byte is `$01`, `N` is clear,
606
+ and `BMI` says -128 is not less than 127. `127 - (-128)` is `255`; the
607
+ byte is `$FF`, `N` is set, and `BMI` says 127 is less than -128. The
608
+ CPU reports each of these with `V` set. Measured in VICE x64sc 3.10 over
609
+ all 65,536 signed byte pairs: the bare `BMI` disagrees with the
610
+ corrected compare on 16,384, one pair in four, and every one of them
611
+ has `V` set (`recipes/kickassembler/compare-16bit-signed.md`). The
612
+ technique is on both metadata lines above because the fault is the
613
+ naive form of its signed compare and its `BVC` / `EOR #$80` form cures
614
+ it.
615
+
616
+ ### Fix
617
+
618
+ After the `SBC`, branch on `V`: if it is clear `N` is right; if it is
619
+ set flip bit 7 with `EOR #$80`, which also resets `N`. For 16-bit
620
+ values do the `SBC` on the high byte with the borrow from a `CMP` of
621
+ the low bytes. Where the test is against a constant in the same half
622
+ of the range, or the values can be biased by `$80`, an unsigned `CMP`
623
+ needs no fix-up at all.
624
+
625
+ ### Worked example
626
+
627
+ ```asm
628
+ // BAD: right until a - b leaves -128..127
629
+ lda vel
630
+ sec
631
+ sbc limit
632
+ bmi below // wrong for 16,384 of 65,536 pairs
633
+
634
+ // FIXED: four more bytes, 3 to 4 more cycles
635
+ lda vel
636
+ sec
637
+ sbc limit
638
+ bvc !+
639
+ eor #$80
640
+ !: bmi below
641
+ ```
642
+
643
+ Measured cost of the fixed 8-bit compare with absolute operands: 13 or
644
+ 14 cycles before the `BMI` (13 when the subtract did not overflow and
645
+ the `BVC` is taken, 14 when it falls through into the `EOR`), against
646
+ 10 for the bad one (rung 1 for the first pair, rung 3 for the second).
647
+
648
+ ### Cross-references
649
+
650
+ - Technique `compare_16bit_and_signed` in `docs/techniques/maths.md` —
651
+ the flag table on the boundary pairs, the 16-bit form, and what
652
+ Oscar64 emits instead
653
+ - Technique `fixed_point_8_8` in `docs/techniques/maths.md` — signed
654
+ velocities in the high byte, the place this bites first
655
+ - Recipe `docs/recipes/kickassembler/compare-16bit-signed.md` — the
656
+ sweep that counts the 16,384 misses
657
+
658
+ ---
659
+
660
+ ## asm_optimiser_moves_self_modified_instruction — Oscar64's assembler optimiser at -O2 rewrites a non-volatile __asm block, so a store into an instruction's operand byte lands in a copy that never runs
661
+
662
+ **Severity:** high
663
+ **Region:** both
664
+ **Triggered by techniques:** table_multiply_8x8
665
+
666
+ ### Symptom
667
+
668
+ A routine written as an `__asm` block that patches its own operand
669
+ bytes (`sta m1 + 1` into a following `lda table,y`) gives the right
670
+ answer at `-O0` and the wrong answer at `-O2`, with no warning from the
671
+ compiler. The `.asm` listing looks correct at the point a reader opens
672
+ it first, because the instruction sequence as written is still in the
673
+ file; it is just not the sequence that runs.
674
+
675
+ Measured on the quarter-square multiply harness from
676
+ `techniques/maths.md` (`fpcheck.c`, Oscar64 `-tm=c64 -O2`, VICE x64sc
677
+ 3.10, PAL, screen decoded with the character ROM). The only change
678
+ between the two builds is the word `volatile` on the multiply's block:
679
+
680
+ ```
681
+ __asm volatile mul cs 0f09 exp 0f09 miss 00000 pass
682
+ __asm mul cs c524 exp 0f09 miss 00000 fail
683
+ ```
684
+
685
+ The miss counter is the trap inside the trap. It reads `00000` in the
686
+ failing build because every one of the 65,536 products is wrong and a
687
+ 16-bit counter of 65,536 misses wraps to zero. The checksum is the only
688
+ tell. Recomputing the checksum in Python from the instruction sequence
689
+ that actually executes (below) gives `C524` and 65,536 misses, so the
690
+ number on screen is fully accounted for by the mechanism.
691
+
692
+ ### Mechanism
693
+
694
+ At `-O2` and above Oscar64 runs an assembler optimiser over inline
695
+ assembly that is not marked `volatile`. It is free to change addressing
696
+ modes and instruction order, and it may emit more than one copy of a
697
+ block. It does not know that a store whose target is `label + 1` is
698
+ writing an operand byte, so it resolves that store against wherever the
699
+ label ended up and optimises the instruction the label points at as if
700
+ its operand were the constant the source shows.
701
+
702
+ In the measured build the executed copy at `$0C00` had `ldy / lda ,y`
703
+ rewritten as `ldx / lda ,x` and `sec` moved down one instruction. The
704
+ four patch stores in that copy resolve to `$0C3E`, `$0C47`, `$0C41`
705
+ and `$0C4A`. Those addresses are inside a second, byte-for-byte
706
+ original copy of the block that the assembler placed after the `RTS` at
707
+ `$0C27`; nothing jumps to it. The running instructions keep the operands
708
+ they were assembled with, `sqr_lo + 0` and `nsq_lo + 0`, which is the
709
+ routine with `a = 0` patched into the `sqr` reads and `255 - a = 0`
710
+ into the `nsq` reads. Every product is therefore `q(b) - q(|b - 255|)`
711
+ instead of `q(a + b) - q(|a - b|)`; for `0 * 0` that is
712
+ `0 - 16256 = $C080`, the figure the technique page recorded.
713
+
714
+ Executed copy, from the `.asm` listing of the non-volatile build:
715
+
716
+ ```
717
+ 0c00 LDA $0bff ; mul_a
718
+ 0c03 STA $0c3e ; m1 + 1, but in the dead copy below
719
+ 0c06 STA $0c47 ; m2 + 1
720
+ 0c09 EOR #$ff
721
+ 0c0b STA $0c41 ; m3 + 1
722
+ 0c0e STA $0c4a ; m4 + 1
723
+ 0c11 LDX $0e9d ; mul_b, now X
724
+ 0c14 LDA $0f00,x ; sqr_lo + 0: operand never patched
725
+ 0c17 SEC
726
+ 0c18 SBC $1300,x ; nsq_lo + 0
727
+ 0c1b STA $0e9e ; mul_r
728
+ 0c1e LDA $1100,x
729
+ 0c21 SBC $1500,x
730
+ 0c24 STA $0e9f
731
+ 0c27 RTS
732
+ ```
733
+
734
+ Dead copy, immediately after it (no reference to `$0C28` anywhere in
735
+ the listing):
736
+
737
+ ```
738
+ 0c28 LDA $0bff
739
+ 0c2b STA $0c3e
740
+ 0c2e STA $0c47
741
+ 0c31 EOR #$ff
742
+ 0c33 STA $0c41
743
+ 0c36 STA $0c4a
744
+ 0c39 LDY $0e9d
745
+ 0c3c SEC
746
+ 0c3d LDA $0f00,y ; $0c3e is the low operand byte: this is m1 + 1
747
+ 0c40 SBC $1300,y ; $0c41 is m3 + 1
748
+ 0c43 STA $0e9e
749
+ 0c46 LDA $1100,y ; $0c47 is m2 + 1
750
+ 0c49 SBC $1500,y ; $0c4a is m4 + 1
751
+ 0c4c STA $0e9f
752
+ 0c4f RTS
753
+ ```
754
+
755
+ With `volatile` the block is emitted once, as written, and the four
756
+ stores resolve to `$0C16`, `$0C1F`, `$0C19` and `$0C22`, which are the
757
+ operand bytes of the four indexed reads at `$0C15`, `$0C1E`, `$0C18`
758
+ and `$0C21` in the same copy. The `-O2` volatile build and the `-O0`
759
+ non-volatile build both pass; `-O0` is a control, not a fix, because
760
+ the optimiser is simply not run at that level.
761
+
762
+ ### Fix
763
+
764
+ Any one of these keeps the patch and the patched instruction in the
765
+ same copy of the code. All three were built and run here.
766
+
767
+ 1. Write the block `__asm volatile { ... }`. Measured: pass, 52 cycles
768
+ per call, the figure on the technique page.
769
+ 2. Wrap the function in `#pragma optimize(push)` / `#pragma
770
+ optimize(noasm)` / `#pragma optimize(pop)` and leave the block
771
+ plain. Measured: pass, and the listing is the same instruction
772
+ sequence as the volatile build.
773
+ 3. Do not patch code at all. Put the operand in a zero-page pointer and
774
+ index through it with `lda (zp),y`; the store is to a variable, which
775
+ the optimiser treats as data. Measured: pass, 66 cycles per call
776
+ against 52 (arithmetic from the same harness: 11,504 less the 4,904
777
+ empty-loop baseline, over 100 calls). The cost is the four `lda # /
778
+ sta zp` pairs that load the table page bytes, plus the slower
779
+ addressing mode.
780
+
781
+ Moving the routine to a separate assembler source file is not an option
782
+ in Oscar64: the compiler has no object linker and no external symbol
783
+ resolution (`toolchains/oscar64-reference.md`, "Calling KickAssembler
784
+ code from Oscar64"), so a hand-assembled routine has to be embedded as
785
+ bytes at a fixed address and called by `jsr`. That does put it beyond
786
+ the optimiser's reach, but it was not measured here.
787
+
788
+ ### Worked example
789
+
790
+ The C shape that fails, with the two results. Nothing about the
791
+ pattern is specific to the multiply: any store to `label + 1` or
792
+ `label + 2` where `label` is an instruction inside the same block is
793
+ exposed.
794
+
795
+ ```text
796
+ __noinline void qmul(void)
797
+ {
798
+ __asm // BAD at -O2: mul cs c524, miss 00000 (wrapped)
799
+ {
800
+ lda mul_a
801
+ sta m1 + 1 // patches an operand byte of m1 ...
802
+ sta m2 + 1
803
+ eor #$ff
804
+ sta m3 + 1
805
+ sta m4 + 1
806
+ ldy mul_b
807
+ sec
808
+ m1: lda sqr_lo, y // ... but the m1 that runs is a rewritten copy
809
+ m3: sbc nsq_lo, y
810
+ sta mul_r
811
+ m2: lda sqr_hi, y
812
+ m4: sbc nsq_hi, y
813
+ sta mul_r + 1
814
+ }
815
+ }
816
+
817
+ __noinline void qmul(void)
818
+ {
819
+ __asm volatile // FIXED: mul cs 0f09, miss 00000, 52 cycles
820
+ {
821
+ ... same body ...
822
+ }
823
+ }
824
+ ```
825
+
826
+ The data-patch form (fix 3), which is safe in a plain block because it
827
+ never writes into code. Shown in KickAssembler syntax; the Oscar64
828
+ version is the same instructions with `__zeropage char *` pointers in
829
+ place of the two `.label` lines:
830
+
831
+ ```asm
832
+ .label zp_sqr = $f7 // two zero-page pointers
833
+ .label zp_nsq = $f9
834
+ lda mul_a
835
+ sta zp_sqr // low byte = a
836
+ eor #$ff
837
+ sta zp_nsq // low byte = 255 - a
838
+ ldy mul_b
839
+ sec
840
+ lda #>sqr_lo
841
+ sta zp_sqr + 1 // a store to a variable, not to an instruction
842
+ lda #>nsq_lo
843
+ sta zp_nsq + 1
844
+ lda (zp_sqr),y
845
+ sbc (zp_nsq),y
846
+ sta mul_r
847
+ lda #>sqr_hi
848
+ sta zp_sqr + 1
849
+ lda #>nsq_hi
850
+ sta zp_nsq + 1
851
+ lda (zp_sqr),y
852
+ sbc (zp_nsq),y
853
+ sta mul_r + 1
854
+ ```
855
+
856
+ A grep of the Oscar64 recipes for stores to `label + 1` inside
857
+ non-volatile `__asm` blocks found only stores into C variables
858
+ (`load-asset-runtime.md` writes `ld_end + 1`, a `static unsigned`), so
859
+ no shipped recipe carries the failing shape; the technique page is the
860
+ one place it was written and caught.
861
+
862
+ ### Cross-references
863
+
864
+ - Technique `table_multiply_8x8` in `docs/techniques/maths.md`: the
865
+ routine, its 52-cycle figure, and "The measuring program" whose
866
+ checksum caught this
867
+ - `docs/toolchains/oscar64-reference.md`: the assembler optimiser
868
+ paragraph (`__asm volatile`, `#pragma optimize(noasm)`) and the
869
+ section on calling KickAssembler code, which is why a separate `.s`
870
+ file is not a remedy here
871
+ - Pitfall `getchx_petscii_remaps_return` in
872
+ `docs/pitfalls/kernal-and-io.md`: the other Oscar64-specific pitfall,
873
+ for the same "the source is right, the toolchain did something else"
874
+ reading habit