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,1191 @@
1
+ ---
2
+ category: loader
3
+ ---
4
+
5
+ <!-- doc-type: pitfall-reference -->
6
+
7
+ # Loader Pitfalls
8
+
9
+ The pitfalls in this document share a common thread: they all arise from
10
+ the gap between what a fast loader expects and what the surrounding program
11
+ (or its build toolchain) provides. Fast loaders are invasive by design — they patch KERNAL vectors, bypass
12
+ the 1541 ROM entirely, and assume that the drive's hardware is exactly as stock as
13
+ the day it left the factory. Any code that assumes it can restore "normal" state,
14
+ any hardware that deviates from 1541 timing, and any program data layout that
15
+ conflicts with the BASIC stub can each silently corrupt loading in ways that look
16
+ like intermittent hardware failure. The pitfalls below have burned every C64
17
+ coder who encountered them for the first time: the symptoms appear random but the
18
+ mechanisms are completely deterministic.
19
+
20
+ ---
21
+
22
+ ## fastloader_kernal_dependency — Fastloader KERNAL-vector patch breaks after IRQ setup
23
+
24
+ **Severity:** high
25
+ **Region:** both
26
+ **Triggered by kernal:** LOAD
27
+ **Triggered by techniques:** multi_load_sequencing
28
+
29
+ ### Symptom
30
+
31
+ A production installs a `$FFD5`-hooking fast loader successfully during startup. The first
32
+ part loads at full speed. After the first part installs its own raster IRQ handler
33
+ — or after any block of initialization code that includes "restore KERNAL vectors"
34
+ as a housekeeping step — subsequent LOAD calls revert to the slow KERNAL serial
35
+ protocol. The second part loads in 130 seconds instead of 6 seconds. On hardware
36
+ with a serial bus that has even marginal IEC signal quality, the slow KERNAL
37
+ protocol may also produce read errors that never appeared with the fast path
38
+ active.
39
+
40
+ A subtler variant: code that calls KERNAL `RESTOR` (`$FF8A`, no arguments — an
41
+ earlier version of this page said `$FF8D, mode=0`; `$FF8D` is `VECTOR`) at the
42
+ start of initialization resets all sixteen KERNAL RAM vectors `$0314-$0333` to
43
+ their ROM defaults, including `$0330/$0331` (the LOAD vector). This silently undoes
44
+ the fastloader installation before the first LOAD call ever happens.
45
+
46
+ ### Mechanism
47
+
48
+ Loaders that interpose on the KERNAL LOAD hook `ILOAD` at `$0330/$0331` so that
49
+ `JSR $FFD5` takes the fast path; the classic cartridge fastloaders work this way.
50
+ The KERNAL jump table entry at `$FFD5` (LOAD) is `JMP $F49E`; `$F49E` saves X/Y
51
+ to `$C3/$C4` and then jumps through `$0330` (`ILOAD`, two bytes, little-endian),
52
+ whose ROM default is `$F4A5` (KERNAL 901227-03, read from the `$FD30` vector
53
+ table — rung 1; an earlier version of this page gave `$FA31`, which is inside the
54
+ tape-read code). A hooking loader overwrites `$0330/$0331` with the address of
55
+ its own C64-side receive loop, and all subsequent `JSR $FFD5` calls take the fast
56
+ path transparently.
57
+
58
+ Krill v194 does NOT work this way, and neither does Sparkle — an earlier version
59
+ of this page said both did. Krill's API (`install`/`loadraw`/`loadcompd`) is
60
+ called directly and its source contains no write to `$0330` — not even in
61
+ `LOAD_VIA_KERNAL_FALLBACK` mode, whose fallback path calls `OPEN`/`CHKIN`/`BASIN`
62
+ byte-by-byte rather than `$FFD5` (from the v194 source as read for this
63
+ correction; the source is not on this machine, so rung 4 here — the technique
64
+ page `../techniques/loaders-packers.md` "v194 concrete integration reference"
65
+ agrees that the documented usage never goes through `$FFD5`). Sparkle's IRQ
66
+ loader uses no KERNAL routine at all (see `sparkle_irq_loader`, "Uses kernal:
67
+ (none)"). For Krill and Sparkle this pitfall does not apply: restoring `$0330` or
68
+ calling `RESTOR` (`$FF8A`) leaves them working. The real Krill hazards are listed
69
+ at the end of the Fix below.
70
+
71
+ The problem arises when anything restores these vectors to their default ROM
72
+ targets. Three common culprits:
73
+
74
+ 1. **Generic "restore KERNAL" init sequences.** Many tutorials and old code
75
+ bases include a block like this as part of IRQ installation:
76
+
77
+ ```kick
78
+ sei
79
+ lda #$a5 // ROM ILOAD lo
80
+ sta $0330
81
+ lda #$f4 // ROM ILOAD hi ($F4A5 = LOAD body in KERNAL 901227-03; the $FD30 table value. $FFD5 -> $F49E does JMP ($0330))
82
+ sta $0331
83
+ cli
84
+ ```
85
+
86
+ (An earlier version of this block wrote `$FA31`, which is inside the KERNAL's
87
+ tape-read code, and spelled the mnemonics in capitals, which KickAssembler 5.25
88
+ rejects — "Pseudo command SEI not defined"; the fence had never been assembled.)
89
+
90
+ This pattern appears in generic IRQ-setup templates where the author
91
+ intended only to stabilize the IRQ vector (`$0314/$0315`) but copied a
92
+ broader vector-reset block that also clobbers the LOAD vector.
93
+
94
+ 2. **KERNAL RESTOR call.** `JSR $FF8A` restores all sixteen KERNAL RAM vectors
95
+ `$0314-$0333` to their ROM defaults in a single call; it takes no argument
96
+ (`VECTOR` at `$FF8D` with carry clear writes the same block from a
97
+ caller-supplied table at X/Y, and with carry set reads it out). An earlier
98
+ version of this page said `JSR $FF8D` with A=0 and fifteen vectors; the ROM
99
+ bytes are `$FF8A: JMP $FD15` (RESTOR: `LDX #$30 / LDY #$FD / CLC`, falling
100
+ into VECTOR) and `$FF8D: JMP $FD1A` (VECTOR, which copies `$1F`+1 = 32 bytes
101
+ and selects direction on the carry), rung 1. Any init sequence that includes
102
+ `JSR $FF8A` after a hooking loader's install routine runs will silently undo
103
+ the installation.
104
+
105
+ 3. **Cold-start or warm-start flow.** A production that jumps to `$FCE2`
106
+ (KERNAL RESET / cold start) or `$FE66` (the KERNAL RUN/STOP-RESTORE warm
107
+ start) as part of a "reset to safe state" sequence will reinitialize RAM
108
+ vectors — both paths run RESTOR (`$FD15`, the body behind `$FF8A`), which
109
+ rewrites `$0314-$0333` from the ROM table. An earlier version of this page
110
+ named `$FD15` itself as the warm start; it is the RESTOR body, rung 1 from
111
+ the ROM bytes. This is rare but occasionally appears in cracktros that chain
112
+ off a previous production's reset path.
113
+
114
+ The fastloader's drive-side code is unaffected — it remains running in the
115
+ 1541's RAM until the drive is reset. The C64 side is what breaks: the receive
116
+ loop address is gone, and `JSR $FFD5` now calls the slow ROM routine. Because
117
+ the drive is still in its fast-protocol mode, the slow ROM routine and the fast
118
+ drive protocol are completely mismatched. Depending on the loader and the
119
+ drive state, this manifests as extremely slow loading (the drive times out and
120
+ falls back to a safe state), as a hung bus (the drive is waiting for the fast
121
+ handshake that never comes), or as a "?FILE NOT FOUND" error.
122
+
123
+ ### Fix
124
+
125
+ Two approaches, depending on what the IRQ setup code actually needs:
126
+
127
+ **Option A — Save and restore the LOAD vector around IRQ install:**
128
+
129
+ ```kick
130
+ sei
131
+
132
+ // Save the current LOAD vector (fast path or ROM, whichever is live)
133
+ lda $0330
134
+ sta saved_load_lo
135
+ lda $0331
136
+ sta saved_load_hi
137
+
138
+ // Install IRQ handler into $0314/$0315 only — do NOT touch $0330/$0331
139
+ lda #<irq_handler
140
+ sta $0314
141
+ lda #>irq_handler
142
+ sta $0315
143
+
144
+ // Restore the LOAD vector in case anything above clobbered it
145
+ lda saved_load_lo
146
+ sta $0330
147
+ lda saved_load_hi
148
+ sta $0331
149
+
150
+ cli
151
+
152
+ // ...
153
+
154
+ saved_load_lo: .byte 0
155
+ saved_load_hi: .byte 0
156
+ ```
157
+
158
+ (An earlier version of this listing was in capitals, which KickAssembler 5.25
159
+ rejects; it now assembles.) Both the save and the restore sit inside the
160
+ `SEI`/`CLI` window here only to keep the example compact; in practice, save the
161
+ vector before any code that might disturb it, and restore it as soon as that code
162
+ has run — before `CLI` if an IRQ handler reads the LOAD vector. (An earlier
163
+ version of this sentence said the save was "before `SEI`", which described a
164
+ different listing.)
165
+
166
+ **Option B — Audit the init sequence and remove LOAD-vector writes:**
167
+
168
+ Search the codebase for writes to `$0330` and `$0331` and for calls to
169
+ `JSR $FF8A` (RESTOR) and `JSR $FF8D` (VECTOR, which with carry clear writes the
170
+ same block from a caller-supplied table; an earlier version of this page named
171
+ only `$FF8D`, so a search that followed it missed every real `JSR $FF8A`). Unless
172
+ there is a deliberate reason to restore the LOAD vector,
173
+ delete those writes. The IRQ vector at `$0314/$0315` can be patched without
174
+ touching the LOAD vector — they are independent RAM vectors.
175
+
176
+ **What this page used to say, and no longer does.** An earlier version carried an
177
+ "Option C — re-run Krill install after IRQ setup", claiming the installer was
178
+ idempotent and re-patched `$0330/$0331`, and a paragraph saying Sparkle's raster
179
+ IRQ dispatched through the LOAD vector on every block. Neither loader touches
180
+ `$0330`, so there is no vector to re-patch; Krill's `install` tests CIA2 DDRA for
181
+ an existing installation and returns OK without doing anything, and if that test
182
+ fails while the drive is already in loader mode a second `install` hangs on the
183
+ KERNAL serial path (rung 4 here — the Krill source is not on this machine). Both
184
+ passages were deleted.
185
+
186
+ **The real Krill hazards** are the ones `../techniques/loaders-packers.md`
187
+ already documents, and they have nothing to do with `$0330`:
188
+
189
+ - Any raw write to `$DD00`/`$DD02` (a VIC-bank switch, a generic CIA init) while
190
+ Krill is armed corrupts its bus-lock/installed-state test — see
191
+ `fastloader_dd00_write_corrupts_resident` below. This is Krill's rule only;
192
+ Sparkle prescribes a `$DD02` write for the VIC bank (an earlier version of
193
+ this bullet said "the loader", which read as a rule for every loader).
194
+ - Any KERNAL serial call (`JSR $FFD5`, a `krnio` save) while the drive is in
195
+ loader mode stalls, because the drive is no longer running DOS; call
196
+ `uninstall` first.
197
+
198
+ ### Cross-references
199
+
200
+ - KERNAL routine `LOAD` (`$FFD5` → `$F49E` → `JMP ($0330)`), RAM vector `ILOAD` at `$0330/$0331`, ROM default `$F4A5`
201
+ - KERNAL routines `RESTOR` (`$FF8A`) and `VECTOR` (`$FF8D`) — `../hardware/kernal-routines-reference.md`
202
+ - Technique `krill_loader_integration` — called directly through its own API; this pitfall does not apply to it (see Mechanism)
203
+ - Technique `sparkle_irq_loader` — uses no KERNAL routine; this pitfall does not apply to it
204
+ - Pitfall `fastloader_dd00_write_corrupts_resident` — the hazard that does apply to a resident Krill
205
+
206
+ ---
207
+
208
+ ## gcr_timing_assumes_stock_drive — GCR loaders assume stock 1541 timing; non-stock drives fail or corrupt
209
+
210
+ **Severity:** medium
211
+ **Region:** both
212
+ **Triggered by techniques:** sparkle_irq_loader, krill_loader_integration, disk_protection_tricks, iffl_single_file, drive_code_upload_and_job_queue
213
+
214
+ ### Symptom
215
+
216
+ A demo that loads cleanly on a real stock 1541 hangs indefinitely on an SD2IEC,
217
+ corrupts data silently on a 1571 running in 1541-compatibility mode, produces
218
+ random read errors on a JiffyDOS-modified 1541, or loads correctly on PAL but
219
+ not on NTSC. The loading symptom looks like a hardware fault — the drive's
220
+ activity LED may flash abnormally, or the C64 may freeze at the loading screen
221
+ with no visible error. On SD2IEC the LED typically blinks in an error pattern;
222
+ on a JiffyDOS 1541 the machine may hang at the first block receive with the
223
+ drive motor spinning continuously.
224
+
225
+ The symptom is drive-dependent and consistently reproducible: the same disk image
226
+ that fails on SD2IEC loads correctly every time on a real 1541.
227
+
228
+ ### Mechanism
229
+
230
+ GCR-level fast loaders — Krill, Sparkle, and similar — bypass the KERNAL's IEC
231
+ serial routines entirely. Instead of calling `IECIN` or `IECOUT`, they install
232
+ custom drive-side code (via `M-W`/`M-E` commands) and then communicate with that
233
+ code using direct bit-banging of CIA2 `$DD00` on the C64 side. The C64-side
234
+ receive loop is a tight, cycle-counted loop. Each iteration tests a specific bit
235
+ of `$DD00` (the CLK line, bit 6, or the DATA line, bit 7) and waits for a
236
+ transition within a hard cycle-count window.
237
+
238
+ The drive-side code sends each byte at a cadence tuned to the stock 1541's 1 MHz
239
+ 6502 clock (1,000,000 cycles per second at the drive's internal clock rate). The
240
+ C64-side loop expects each bit transition to arrive within a window of
241
+ approximately 4-6 µs. If the drive's bit timing deviates — because it is running
242
+ at a different clock speed, because it has different VIA peripheral chip
243
+ characteristics, or because its protocol is JiffyDOS rather than the Krill
244
+ protocol — the C64-side loop times out. Depending on the loader's error handling,
245
+ a timeout either hangs (spin loop) or returns a garbled byte.
246
+
247
+ Common non-stock configurations and why each fails:
248
+
249
+ **SD2IEC.** An SD card reader that emulates 1541 filesystem operations at the
250
+ Commodore DOS level. It speaks the standard KERNAL IEC serial protocol but does
251
+ not emulate GCR at the hardware level at all. `M-W`/`M-E` commands either return
252
+ an error or are silently ignored, so Krill's drive-side code never installs. The
253
+ C64 side then tries to do the fast handshake with a device that has no idea what
254
+ the fast protocol is, and the bus hangs.
255
+
256
+ **1571 in 1541-compatibility mode.** The 1571 can be addressed as device 8 in
257
+ 1541 mode, but its VIA chip timings and its internal bus arbitration differ from
258
+ the 1541. Krill's drive-side timing constants were measured on 1541 hardware; the
259
+ 1571's slightly different VIA propagation delays shift the bit window outside the
260
+ C64-side tolerance. This causes occasional bit errors that corrupt the loaded data
261
+ in ways that are not immediately obvious — the program may start but behave
262
+ incorrectly because a few bytes of code or data were flipped.
263
+
264
+ **JiffyDOS-modified 1541.** JiffyDOS replaces the 1541 ROM with a ROM that
265
+ implements the JiffyDOS burst protocol. When Krill's drive-side code is uploaded
266
+ via `M-W`/`M-E` and executed, it overwrites the JiffyDOS RAM driver in the
267
+ drive's RAM workspace. This usually causes Krill to work correctly on a JiffyDOS
268
+ machine — but only if the Krill version's timing constants were compiled for the
269
+ stock 1541 MHz clock, and only if the JiffyDOS kernel does not re-initialize the
270
+ RAM workspace between command-channel operations. Some JiffyDOS revisions
271
+ periodically restore their RAM workspace, which can corrupt the in-place Krill
272
+ drive code during a multi-part load sequence.
273
+
274
+ **NTSC timing.** The C64-side receive loop's cycle counts are valid at PAL's
275
+ 0.985 MHz system clock. NTSC runs at 1.022 MHz — approximately 3.8% faster.
276
+ Each of the four 18-cycle handshake phases in Krill's receive loop resynchronises
277
+ on an ATN edge, so the drift does not accumulate across a byte; what matters is
278
+ the fixed gap of about 10 C64 cycles between toggling ATN and reading the bus,
279
+ which the 1541 (whose 1 MHz clock does not change with the video standard) needs
280
+ up to 14 of its own cycles to beat. At 3.8% that gap shrinks by well under one
281
+ cycle — Krill's own `NTSC_COMPATIBILITY` build restores it by adding exactly one
282
+ cycle to each phase — which is enough to push a phase already at the edge of its
283
+ window over it. (An earlier version of this page said the shift was "roughly 3-4
284
+ cycles" per window; 3.8% of an 18-cycle phase is 0.7 cycles, and of the whole
285
+ 72-cycle byte 2.7 — rung 3 from the phase lengths in
286
+ `../techniques/loaders-packers.md` "Cycle budget".) Krill's build has an NTSC
287
+ switch — the `NTSC_COMPATIBILITY` define in `loaderconfig.inc` (the config file
288
+ selected with `EXTCONFIGPATH=`, or `include/config.inc`) of its cc65/ca65 build;
289
+ it is not a KickAssembler `-D` flag, and it is not a `make` command-line variable
290
+ either (the Makefile does not forward one to ca65). An earlier version of this
291
+ page said `-DNTSC=1`. Enabling it pads the C64-side transfer loop by one cycle at
292
+ each ATN sync point (and one NOP in the send routine) so a single resident works
293
+ on PAL and NTSC at a small PAL-speed cost; PAL/NTSC is not auto-detected by the
294
+ installer and a PAL-only resident gives no error on NTSC. Using the PAL binary on
295
+ NTSC hardware is reliable enough on fast, well-terminated hardware but causes
296
+ errors on borderline machines.
297
+
298
+ ### Fix
299
+
300
+ **Detect the drive before installing the fast loader.** Ask the DOS for its own
301
+ version string. `M-R` on the command channel returns raw bytes of drive memory,
302
+ and the 1541's power-on message `CBM DOS V2.6 1541` sits in ROM at
303
+ `$E5B7`–`$E5C7`, so the four bytes from `$E5C4` read `1541` — the last of them
304
+ `$B1`, `'1'` with bit 7 set, which is the message table's end marker, so strip
305
+ bit 7 before comparing. A 1571 answers `1571` from the same address, a 1581
306
+ answers `$FF $FF $FF $FF` (its ROM has nothing there), and what an SD2IEC answers
307
+ was not measured here. `1541` at `$E5C4` is rung 1 from the two 1541 ROM images
308
+ VICE 3.10 ships (325302-01+901229-05 and the 1541-II's 251968-03); the
309
+ behaviour is rung 1 in VICE x64sc 3.10 with `-drive8truedrive` on drive types
310
+ 1541, 1541-II, 1571 and 1581, where the routine below returned carry clear for
311
+ the 1541 and carry set for the 1571. What the test identifies is the firmware,
312
+ not the mechanism. A 1540 — the same mechanism, older DOS — answers `V170` from
313
+ that address (rung 1, its ROM image), and a 1541 whose ROM has been replaced
314
+ (JiffyDOS, SpeedDOS, Dolphin DOS) will not say `1541` there either (rung 4; no
315
+ such ROM ships with VICE), so the routine sends both down the KERNAL path. That
316
+ is the safe side to fail on, but note it is stricter than the Mechanism paragraph
317
+ above, which says Krill's drive code usually runs on a JiffyDOS 1541. The table of
318
+ images and addresses is in `../formats/iec-disk-reference.md`, "Identifying the
319
+ drive over the command channel".
320
+
321
+ ```kick
322
+ jsr detect_1541
323
+ bcc install_krill
324
+ jmp use_kernal_load // anything that is not a 1541: KERNAL path
325
+
326
+ // Carry clear on return: the drive says "1541" at $E5C4. Carry set: anything
327
+ // else, including a drive that never answered.
328
+ detect_1541:
329
+ lda #15
330
+ ldx #8
331
+ ldy #15
332
+ jsr $ffba // SETLFS 15,8,15
333
+ lda #cmd_end-cmd
334
+ ldx #<cmd
335
+ ldy #>cmd
336
+ jsr $ffbd // SETNAM: the command text goes where a filename would
337
+ jsr $ffc0 // OPEN sends "M-R" $C4 $E5 $04
338
+ bcs no_answer
339
+ ldx #15
340
+ jsr $ffc6 // CHKIN channel 15
341
+ ldy #0
342
+ read:
343
+ jsr $ffcf // CHRIN: one reply byte per call
344
+ and #$7f // the last byte of the ROM string has bit 7 set
345
+ sta reply,y
346
+ iny
347
+ cpy #4
348
+ bne read
349
+ jsr $ffcc // CLRCHN
350
+ lda #15
351
+ jsr $ffc3 // CLOSE
352
+ ldy #3
353
+ compare:
354
+ lda reply,y
355
+ cmp expect,y
356
+ bne mismatch
357
+ dey
358
+ bpl compare
359
+ clc
360
+ rts
361
+ mismatch:
362
+ sec
363
+ rts
364
+ no_answer:
365
+ lda #15
366
+ jsr $ffc3
367
+ sec
368
+ rts
369
+ cmd:
370
+ .byte $4d, $2d, $52 // "M-R" in PETSCII
371
+ .byte $c4, $e5 // $E5C4, low byte first
372
+ .byte $04 // four bytes
373
+ cmd_end:
374
+ expect:
375
+ .byte $31, $35, $34, $31 // "1541"
376
+ reply:
377
+ .byte 0, 0, 0, 0
378
+ ```
379
+
380
+ **Correction (2026-09-21).** The earlier text expected `$41` at `$E5C3` and called
381
+ it "the DOS version byte". `$E5C3` is `$20` — the space between `V2.6` and `1541`
382
+ — in every 1541-family ROM VICE ships (1540, 1541, 1541-II, 1570, 1571), so that
383
+ test never matched and the fast path was never installed. `$41` ("A") is the
384
+ DOS-version marker at offset 2 of the BAM sector, track 18 sector 0, which the
385
+ format routine writes to the disk; the ROM keeps that constant at `$FED5`, not at
386
+ `$E5C3`.
387
+
388
+ **Provide a KERNAL fallback branch.** The most robust approach is to ship two
389
+ load paths: the GCR fast path for stock 1541 hardware, and a `JSR $FFD5` KERNAL
390
+ LOAD fallback for everything else. Detect the drive at startup, set a flag, and
391
+ branch on the flag at each load call. This adds ~50 bytes of overhead but
392
+ eliminates drive-compatibility bugs from the entire production.
393
+
394
+ Krill's Loader has this built in: set `LOAD_VIA_KERNAL_FALLBACK=1` in
395
+ `loaderconfig.inc` and the loader transparently falls back to the KERNAL load
396
+ path when drive-code installation fails (incompatible drive such as SD2IEC, or
397
+ true-drive emulation disabled) — no hand-rolled detection branch needed. The
398
+ cost is a larger host-side stub and KERNAL-speed loading on those devices; see
399
+ `../techniques/loaders-packers.md` "v194 concrete integration reference".
400
+
401
+ **Compile NTSC binaries separately.** If the production targets both PAL and NTSC,
402
+ compile the Krill C64-side stub twice with the appropriate clock constant and
403
+ select the right binary at startup based on the CIA timer reading (standard PAL/
404
+ NTSC detection: count CIA1 timer ticks per VBL interrupt; PAL = 19,656 cycles,
405
+ NTSC = 17,095 on the 6567R8 (16,768 on the older 6567R56A) — a 15% difference
406
+ that is easy to detect reliably within a single frame. An earlier version of this
407
+ page gave NTSC as ~16,715, which is the NTSC frame time in microseconds, not its
408
+ cycle count; 65 × 263 = 17,095 and 64 × 262 = 16,768, rung 3, matching
409
+ `../hardware/pal-ntsc-reference.md`).
410
+
411
+ ### Cross-references
412
+
413
+ - Technique `sparkle_irq_loader` — PAL and NTSC, plugins included (Sparkle 3.4 manual p. 3; an earlier version of this line said PAL-only by default with NTSC needing timing constants)
414
+ - Technique `krill_loader_integration` — `NTSC_COMPATIBILITY` config define, drive detection discussion
415
+ - `docs/formats/iec-disk-reference.md` — IEC bus signal levels, timing diagrams, 1541 GCR zones
416
+
417
+ **Sources for the Fix.** The drive ROM images VICE 3.10 ships in
418
+ `/opt/homebrew/opt/vice/share/vice/DRIVES/` and the KERNAL 901227-03 image, read
419
+ byte by byte (rung 1); VICE x64sc 3.10 with `-drive8truedrive` for the runs
420
+ (rung 1); `../formats/iec-disk-reference.md` for the per-image table.
421
+
422
+ ---
423
+
424
+ ## basic_stub_collision_with_data — BASIC SYS stub at $0801 overlaps program data
425
+
426
+ **Severity:** medium
427
+ **Region:** both
428
+ **Triggered by kernal:** LOAD
429
+ **Triggered by techniques:** crunched_data_in_basic_stub, exomizer_basics
430
+
431
+ ### Symptom
432
+
433
+ A program loads without error. Typing `RUN` causes BASIC to execute the SYS stub
434
+ and launch the machine code as expected — but the machine code immediately
435
+ behaves incorrectly: the first few reads from the data area return garbage values,
436
+ a lookup table produces wrong results, a sprite shape read from `$0801` produces
437
+ a corrupt sprite, or a character set starting at `$0800` is garbled in its first
438
+ glyph. The bug manifests only when the program is launched from BASIC via `RUN`;
439
+ loading with a custom autostart that jumps directly to the entry point (bypassing
440
+ the BASIC stub execution) produces correct behavior. The data corruption is
441
+ in the first 12 bytes of the program's data area (13 where the assembler pads the
442
+ stub so code starts at `$080E`, as KickAssembler's `BasicUpstart2` does), which
443
+ coincides with the BASIC stub's footprint.
444
+
445
+ A second pattern: a program that places data at `$0801` and tests it at startup
446
+ reads the correct data in the assembler's simulation but reads BASIC stub bytes
447
+ on real hardware or in VICE, because the programmer forgot that the loaded PRG
448
+ begins with the stub at `$0801` — the data they assembled at `$0801` was
449
+ overwritten by the stub in the final PRG layout.
450
+
451
+ ### Mechanism
452
+
453
+ A C64 PRG file that auto-starts via BASIC begins at address `$0801`. The BASIC
454
+ interpreter's program area starts at `$0801`. The canonical autostart stub
455
+ occupies the first 12 bytes of that area (an earlier version of this sentence
456
+ said 13; the table below and the Option B listing's `cpx #12` both count 12):
457
+
458
+ ```
459
+ Address Byte Meaning
460
+ $0801 $0B Link to next BASIC line (lo): $0801 + $0A = $080B
461
+ $0802 $08 Link to next BASIC line (hi)
462
+ $0803 $0A Line number lo: 10
463
+ $0804 $00 Line number hi
464
+ $0805 $9E BASIC token for SYS
465
+ $0806 $32 PETSCII '2'
466
+ $0807 $30 PETSCII '0'
467
+ $0808 $36 PETSCII '6'
468
+ $0809 $34 PETSCII '4'
469
+ $080A $00 End of BASIC line
470
+ $080B $00 End of BASIC program (link lo = 0)
471
+ $080C $00 End of BASIC program (link hi = 0)
472
+ $080D ... First byte of machine code (entry point = 2061 decimal)
473
+ ```
474
+
475
+ This layout means that `$0801` through `$080C` (inclusive — 12 bytes) belong to
476
+ BASIC, and the machine code proper begins at `$080D`. Any data the programmer
477
+ places at addresses `$0801` through `$080C` will be overwritten by the stub
478
+ bytes above when the final PRG is assembled with the standard BASIC autostart.
479
+
480
+ The collision arises in two distinct ways:
481
+
482
+ **Collision type A — data intended to reside at $0801.** The programmer explicitly
483
+ places a data table, sprite shape, or character ROM copy at `$0801`, intending to
484
+ use it from machine code. Because the PRG loads to `$0801` and the BASIC stub
485
+ occupies the first 12 bytes, those 12 bytes of the programmer's data are replaced
486
+ by BASIC stub bytes. The machine code reads `$9E $32 $30 $36 $34 $00 ...` (the
487
+ SYS token and address digits) instead of the intended data values.
488
+
489
+ **Collision type B — SYS target address mismatch with data placement.** The
490
+ programmer adjusts the SYS address in the stub (e.g., changing `SYS 2064` to
491
+ `SYS 2061` or `SYS 2080`) to accommodate a slightly longer or shorter header
492
+ sequence, but does not correspondingly move the data that follows. If the SYS
493
+ target is `2064` (`$0810`) but data is placed starting at `$080D`, the region
494
+ `$080D`-`$0810` is ambiguously both stub and data and will contain the stub's
495
+ trailing null bytes.
496
+
497
+ Note that `SYS 2064` points to `$0810`, not `$080D`. The difference of three
498
+ bytes is the end-of-program null word (`$0000`) plus one byte of alignment in
499
+ some assembler output. Different assemblers and different stub templates produce
500
+ slightly different layouts; the canonical addresses to check are wherever the
501
+ PRG assembles the stub and wherever the PRG's data begins — if they overlap, the
502
+ collision exists regardless of which specific addresses are involved.
503
+
504
+ ### Fix
505
+
506
+ Three options, each with different trade-offs:
507
+
508
+ **Option A — Move data after the stub (preferred for new code).**
509
+
510
+ Place all data that needs to survive program startup at `$0820` or higher. Leave
511
+ `$0801`-`$081F` as BASIC stub + entry code. This is the safe default layout. The
512
+ cost is 31 bytes of address space before the programmer's first data byte, which
513
+ is negligible in any context except a 256-byte intro.
514
+
515
+ ```kick
516
+ * = $0801 // BASIC stub at $0801
517
+ .byte $0B, $08 // link to $080B
518
+ .byte $0A, $00 // line 10
519
+ .byte $9E // SYS token
520
+ .text "2061" // SYS target = $080D
521
+ .byte $00 // end of line
522
+ .byte $00, $00 // end of BASIC program
523
+
524
+ * = $080D // machine code entry
525
+ entry:
526
+ jmp main
527
+
528
+ * = $0820 // data safely after stub
529
+ lookup_table:
530
+ .byte $00, $01, $04, $09, $10, $19, $24, $31 // square table, safe here
531
+ ```
532
+
533
+ **Option B — Overwrite the stub at runtime (for productions that never use `RUN`
534
+ again after launch).**
535
+
536
+ The BASIC stub is only needed once: for the initial `RUN` that invokes `SYS`.
537
+ After `SYS` transfers control to machine code, the stub bytes at `$0801`-`$080C`
538
+ are dead — BASIC is no longer running and the BASIC program area is not used by
539
+ the machine code. The entry code can immediately overwrite those bytes with real
540
+ data:
541
+
542
+ ```kick
543
+ * = $0801
544
+ .byte $0B, $08, $0A, $00, $9E // SYS stub start
545
+ .text "2061"
546
+ .byte $00, $00, $00 // end markers
547
+
548
+ * = $080D
549
+ entry:
550
+ // Overwrite the now-dead BASIC stub area with real data
551
+ ldx #0
552
+ overwrite_loop:
553
+ lda real_data,x
554
+ sta $0801,x
555
+ inx
556
+ cpx #12
557
+ bne overwrite_loop
558
+ // $0801-$080C now contains real_data[0..11]
559
+ jmp main
560
+
561
+ real_data:
562
+ .byte $00, $01, $04, $09, $10, $19, $24, $31, $40, $51, $64, $79
563
+ ```
564
+
565
+ This technique is common in 256-byte intros where every available byte of address
566
+ space is used and the stub area must double as a data carrier. (The Option A and
567
+ Option B listings were in capitals until 2026-09-22, which KickAssembler 5.25
568
+ rejects — "Pseudo command JMP not defined" — so neither had ever been assembled;
569
+ both build now.)
570
+
571
+ **Option C — Use a CRT (cartridge) format to bypass the BASIC stub entirely.**
572
+
573
+ A `.CRT` image starts execution from the cartridge reset vector, bypassing BASIC
574
+ and the BASIC stub entirely. The `$0801` area is free for program use from the
575
+ moment the cartridge takes control. This option requires distributing a `.CRT`
576
+ file rather than a `.PRG`, which is appropriate for cartridge releases or EasyFlash
577
+ productions but not for disk-based demos or games that load via KERNAL `LOAD`.
578
+
579
+ **Detecting the collision before runtime.** In KickAssembler, assert that the
580
+ data placement does not overlap the stub using the `.assert` directive:
581
+
582
+ ```kick
583
+ .assert "Data must be after BASIC stub", data_start >= $080D, true
584
+ ```
585
+
586
+ If data is intended to start at exactly `$080D` (immediately after the stub), also
587
+ ensure the SYS target in the stub matches: `SYS 2061` for entry at `$080D`,
588
+ `SYS 2064` for entry at `$0810`. Mismatches between the SYS operand and the
589
+ actual entry label are a second source of subtle boot failures distinct from the
590
+ data collision described above.
591
+
592
+ ### Cross-references
593
+
594
+ - KERNAL routine `LOAD` (`$FFD5`) — the loading mechanism that delivers the PRG to `$0801`
595
+ - `docs/formats/c64-file-formats.md` — PRG file layout, load address header, CRT format
596
+
597
+ ---
598
+
599
+ ## krill_cc65_2_18_miscompile — Building Krill v194 with cc65 2.18 silently produces a broken loader
600
+
601
+ **Severity:** high
602
+ **Region:** both
603
+ **Triggered by techniques:** krill_loader_integration
604
+
605
+ ### Symptom
606
+
607
+ You build Krill's Loader from source, the build is clean (exit 0, no warnings),
608
+ the blobs are the expected size and disassemble to sane code — but at runtime the
609
+ installed loader does not work: `install` either hangs or returns
610
+ `DEVICE_NOT_PRESENT` (`$FE`), and `loadraw` never succeeds. Meanwhile Krill's own
611
+ *prebuilt* `loadertest-cNN.d64` (shipped in the archive) runs perfectly in the
612
+ same emulator, which makes it look like your integration is at fault when the real
613
+ culprit is the assembler.
614
+
615
+ ### Mechanism
616
+
617
+ Krill's Loader (repository version 194, 2022) is developed against cc65 **git
618
+ master**, not the last tagged release. The Homebrew/distro `cc65` is **V2.18**
619
+ (2018) — four years of cc65 codegen changes behind. Some construct in Krill's
620
+ drive/host code is miscompiled by 2.18 in a way that passes assembly cleanly but
621
+ breaks the cycle-exact serial protocol, so the drive never responds. Verified
622
+ 2026-05-20: Homebrew cc65 2.18 → broken loader; cc65 git **V2.19** (`cc3c40c`) →
623
+ working loader, from identical Krill sources and config.
624
+
625
+ ### Fix
626
+
627
+ Build cc65 from git and use it for the Krill build:
628
+
629
+ ```
630
+ git clone --depth 1 https://github.com/cc65/cc65 && make -C cc65
631
+ PATH=<cc65-git>/bin:$PATH CC65_HOME=<cc65-git> make -C loader/src PLATFORM=c64 prg ...
632
+ ```
633
+
634
+ Validate against Krill's prebuilt `loadertest` first: if the prebuilt works in
635
+ your emulator but your freshly-built loader does not, suspect the assembler before
636
+ your integration. Also note `make-loadersymbolsinc.pl` calls `grep -P`, so on
637
+ macOS put GNU grep ahead of BSD grep or the symbol-file step errors.
638
+
639
+ ### Cross-references
640
+
641
+ - Technique `krill_loader_integration` — full v194 build + integration reference
642
+ - `docs/techniques/loaders-packers.md` "v194 concrete integration reference (cc65 build)"
643
+
644
+ ---
645
+
646
+ ## fastloader_resident_in_kernal_workspace — Fast-loader resident at $0200-$03FF collides with KERNAL tables; install hangs
647
+
648
+ **Severity:** high
649
+ **Region:** both
650
+ **Triggered by techniques:** krill_loader_integration
651
+
652
+ ### Symptom
653
+
654
+ A fast loader whose resident/host code is placed low in RAM (e.g. `RESIDENT=$0200`)
655
+ hangs the moment `install` runs — before the first load. Move the resident
656
+ elsewhere and install completes normally.
657
+
658
+ ### Mechanism
659
+
660
+ `install` opens the drive command channel (KERNAL `SETLFS`/`SETNAM`/`OPEN`) to
661
+ upload the drive code via `M-W`. KERNAL `OPEN` writes the logical-file tables
662
+ (LAT/FAT/SAT) at **`$0259-$0276`**, and `$0200-$0258` is the BASIC/KERNAL input
663
+ buffer. A resident placed at `$0200` (e.g. `$0200-$02EC`) overlaps that workspace;
664
+ the KERNAL OPEN corrupts the resident (or vice-versa) mid-install, so the
665
+ handshake never completes. The address looks "free" because nothing visible uses
666
+ it once BASIC is out of the way — but the KERNAL serial path does.
667
+
668
+ ### Fix
669
+
670
+ Place the resident outside KERNAL/BASIC zero-page-adjacent workspace. The free
671
+ 4 KB block at `$C000-$CFFF` works well: put the (transient) installer at `$C000`
672
+ and the (persistent) resident just above it (e.g. `$CD00`). Verified 2026-05-20:
673
+ `RESIDENT=$0200` hung install; `RESIDENT=$CD00` fixed it.
674
+
675
+ ### Cross-references
676
+
677
+ - Technique `krill_loader_integration` — "Embedding into an Oscar64 program"
678
+ - `docs/hardware/c64-memory-map.md` — page 2/3 KERNAL workspace ($0200-$03FF)
679
+
680
+ ---
681
+
682
+ ## fastloader_dd00_write_corrupts_resident — Raw $DD00 writes (VIC bank switch) while a Krill or Sparkle loader is resident corrupt its bus-lock
683
+
684
+ **Severity:** high
685
+ **Region:** both
686
+ **Triggered by registers:** DD00
687
+ **Triggered by techniques:** krill_loader_integration, sparkle_irq_loader
688
+
689
+ ### Symptom
690
+
691
+ A GCR fast loader is installed and one load works. Then the program switches VIC
692
+ graphics mode or VIC bank (anything that writes CIA2 `$DD00`) — e.g. a bitmap
693
+ title screen toggling to a text play screen — and the *next* `loadraw` (or
694
+ `uninstall`) hangs. The first load worked, so the loader looks fine until the mode
695
+ switch; the hang then looks unrelated to graphics.
696
+
697
+ ### Mechanism
698
+
699
+ `$DD00` is shared: bits 0-1 select the VIC bank, bit 2 is the user-port RS-232 TXD
700
+ line, and bits 3-7 are the IEC bus lines the loader bit-bangs (3-5 ATN/CLK/DATA
701
+ out, 6-7 CLK/DATA in — an earlier version of this page said bits 2-7; see
702
+ `../hardware/cia-reference.md` §`$DD00`). While the loader is installed it owns the IEC bits and keeps a
703
+ "bus-lock" state in them. A raw write of the whole `$DD00` byte — exactly what a
704
+ VIC-bank set does, including Oscar64's `vic_setmode()` and any
705
+ `STA $DD00` / `LDA #v:STA $DD00` — overwrites the IEC bits with values the loader
706
+ did not expect, desyncing the drive protocol. The next drive op then waits forever.
707
+ For Krill, a read-modify-write that changes *only* bits 0-1 while the loader is
708
+ idle is tolerated; a full-byte write, or any write while the loader is
709
+ mid-transfer, is not. Other loaders publish different rules, some the reverse of
710
+ Krill's; the table under Fix gives each. (An earlier version of this page stated
711
+ Krill's rule as the rule for every resident loader.)
712
+
713
+ ### Fix
714
+
715
+ Don't keep the loader resident across VIC mode/bank switches. Two options:
716
+
717
+ 1. **Lazy install (robust, verified):** install only around each load batch
718
+ (probe / level load / load-game), `uninstall` immediately after, then re-assert
719
+ the VIC bank with a read-modify-write (`$DD00 = ($DD00 & $FC) | bank`). With the
720
+ loader uninstalled by default, the drive sits in DOS, so saves are plain KERNAL
721
+ `krnio` with no uninstall dance and no `vic_setmode` ever runs inside the
722
+ install→loads→uninstall window. Cost: one drive-code upload per load batch —
723
+ fine for infrequent loads. (Tideline ships this, VICE-verified 2026-05-20.)
724
+ 2. If the loader must stay resident, switch the bank the way that loader
725
+ documents. None of these was run here; each rule is from the loader's own
726
+ documentation (rung 4 here).
727
+
728
+ | Loader | VIC bank switch while resident | Arbitrary `$DD00` values |
729
+ |---|---|---|
730
+ | Krill | `SET_VIC_BANK`; never a raw `$DD00` write | `ENTER_BUS_LOCK` / `LEAVE_BUS_LOCK` |
731
+ | Sparkle 3.4 | Do not write `$DD00`: the loader may read it as a drive command and reset the drive. Write `LDA #$3C+bank : STA $DD02`, bank 0-3 (manual pp. 20-21; common issue 1, p. 29). | "Direct bus lock": `$03` (bits 3-5 clear) to `$DD02`, then any `$DD00` value; restore `$DD00` to `$38` first, then `$DD02` to `$3C`+bank (pp. 22-23). |
732
+ | Bitfire | Plain stores of `$00`-`$03` to `$DD00` "at any time, also while loading"; not a read-modify-write (`LDA $DD00 : AND #$FC : ORA #bank`) (readme, "Bank switching") | Only while idle, between the `bus_lock` and `bus_unlock` macros |
733
+
734
+ Sparkle also accepts any `$DD02` value between loader calls, as long as
735
+ `$3C`+bank is back before the next call (the "indirect bus lock", pp. 21-22).
736
+ Why the `$DD02` write selects the bank (not stated in the manual; rung 4):
737
+ the loader leaves `$DD00` bits 0-1 at 0, so a bit set to output drives 0 and a
738
+ bit set to input floats to 1, and `$3C`+bank gives the inverted bank value the
739
+ VIC reads.
740
+
741
+ **Sources.** Sparkle 3.4 user manual, Sparta (OMG), chapters "Switching VIC
742
+ banks", "Bus lock" and "Common issues", `manual/` in
743
+ https://github.com/spartaomg/SparkleCPP. Bitfire `readme.txt`, sections "Bank
744
+ switching" and the macro list, https://github.com/bboxy/bitfire. The Krill row
745
+ is unchanged from earlier versions of this page.
746
+
747
+ ### Cross-references
748
+
749
+ - Technique `krill_loader_integration` — VIC-bank / bus-lock protocol; lazy-install recipe
750
+ - Technique `sparkle_irq_loader` — bank switch through `$DD02`, direct and indirect bus lock
751
+ - Register `$DD00` (CIA2) — VIC bank select bits 0-1 vs IEC lines bits 3-7
752
+ - Pitfall `fastloader_kernal_dependency` — the other "first load works, later loads break" trap
753
+
754
+ ---
755
+
756
+ ## d64_error_byte_is_a_controller_code — The D64 error byte is the 1541 job code, not the DOS error number
757
+
758
+ **Severity:** medium
759
+ **Region:** both
760
+ **Triggered by techniques:** disk_protection_tricks, drive_code_upload_and_job_queue
761
+
762
+ ### Symptom
763
+
764
+ A tool, a loader's own D64 reader or a page reads the 683-byte error block appended to a `.d64` as DOS error numbers. It then reports "error 3" for a sector the drive would report as `21, READ ERROR`, or looks for the value `21` in the block and never finds it, so a protected disk's bad-sector signature is invisible to it. The other way round, a tool that writes the DOS number into the block produces an image whose byte `21` (`$15`) means nothing to the drive ROM or to VICE, and the protection check that expected a read error gets `0, OK`.
765
+
766
+ ### Mechanism
767
+
768
+ The 1541 has two processors' worth of logic in one 6502: the floppy controller side runs sector jobs and hands back a one-byte return code, and the DOS side turns that code into the number and text on the error channel. The D64 error block stores the controller's return code, one byte per sector in track then sector order. `$01` is a clean read. The codes an image can carry, and what the error channel prints for each, measured on the windowless x64sc build of VICE 3.10 with `-drive8truedrive -drive8type 1541` and a `U1` block read of each flagged sector:
769
+
770
+ | Byte | Controller condition | DOS number | Measured on VICE 3.10 |
771
+ |------|----------------------|-----------|-----------------------|
772
+ | `$01` | OK | none | `0, OK` |
773
+ | `$02` | header not found | 20 | `20, READ ERROR` |
774
+ | `$03` | no sync | 21 | `20, READ ERROR` for one flagged sector; `21, READ ERROR` when the whole track is flagged |
775
+ | `$04` | data block not present | 22 | `22, READ ERROR` |
776
+ | `$05` | data checksum error | 23 | `23, READ ERROR` |
777
+ | `$07` | verify error | 25 | `0, OK` (not exercised by a read) |
778
+ | `$08` | write protect on | 26 | `0, OK` (not exercised by a read) |
779
+ | `$09` | header checksum error | 27 | `20, READ ERROR` |
780
+ | `$0B` | disk ID mismatch | 29 | `20, READ ERROR` for one flagged sector; `29, DISK ID MISMATCH` when the whole track is flagged |
781
+ | `$0F` | drive not ready | 74 | `0, OK` |
782
+
783
+ VICE 3.10 honours `$02`, `$03`, `$04`, `$05`, `$09` and `$0B` by building the sector's GCR with the named field spoiled (`gcr.c` in its source: sync bytes, header block ID, data block ID, the two checksums, the header's disk ID). It ignores `$07`, `$08` and `$0F`, so a read of such a sector returns `0, OK`. Codes `$0A` and `$10` were not measured here.
784
+
785
+ Two rows say the printed number depends on how many sectors on the track are flagged. That is the drive ROM's doing, not the emulator's: VICE spoils only the flagged sector, and a ROM that cannot find the header it wants reports `20` whether the header is missing, has a wrong checksum or has a wrong ID. `21` (no sync at all) and `29` (ID mismatch) appear when every sector on the track carries the code. A first version of this measurement put all the flags on track 1 and read `29` for the `$02` and `$05` sectors and `20` for the `$0B` one: a wrong-ID header on the same track leaks into the ROM's per-track seek. Keep flagged sectors on separate tracks when you test an image tool.
786
+
787
+ ### Fix
788
+
789
+ Convert at the boundary, in both directions, and name which side a variable holds. The drive ROM's rule (see `../formats/iec-disk-reference.md`, "The 1541 DOS Error Codes") is: take the low four bits; `0` gives `24`; `15` gives `74`; anything else is ORed with `$20` and decremented twice, read as a decimal number. So `$03` gives `$23 - 2 = $21`, printed as `21`. Treat `$00` and `$01` as no error when reading an image, and write `$01` for a clean sector.
790
+
791
+ ### Worked example
792
+
793
+ Bad pattern, a Python image reader that prints the block as DOS numbers:
794
+
795
+ ```text
796
+ errs = img[174848:] # 683 bytes after the sector data
797
+ for i, b in enumerate(errs):
798
+ if b:
799
+ print(f"sector {i}: error {b}") # prints "error 3"; the drive says 21
800
+ ```
801
+
802
+ Fixed, with the ROM's conversion:
803
+
804
+ ```text
805
+ def dos_number(job):
806
+ job &= 0x0F
807
+ if job in (0, 1):
808
+ return 0 if job == 1 else 24
809
+ if job == 15:
810
+ return 74
811
+ return int(f"{(job | 0x20) - 2:x}") # $23 - 2 = $21 -> 21
812
+
813
+ errs = img[174848:]
814
+ for i, b in enumerate(errs):
815
+ if b not in (0, 1):
816
+ print(f"sector {i}: job {b:#04x} -> DOS {dos_number(b)}")
817
+ ```
818
+
819
+ The measurement itself. Build the image (a `c1541`-formatted 174,848-byte file plus 683 error bytes, one flagged sector per track, and two whole tracks flagged):
820
+
821
+ ```text
822
+ SPT = [21]*17 + [19]*7 + [18]*6 + [17]*5 # sectors per track, tracks 1..35
823
+ def index(t, s): return sum(SPT[:t-1]) + s
824
+ img = bytearray(open("disk.d64", "rb").read()) # c1541 -format "TEST,01" d64 disk.d64
825
+ errs = bytearray([0x01] * 683)
826
+ for t, code in {1:0x02, 2:0x03, 3:0x04, 4:0x05, 5:0x07, 6:0x08,
827
+ 7:0x09, 8:0x0B, 9:0x0F, 10:0x00, 11:0x01}.items():
828
+ errs[index(t, 0)] = code
829
+ for s in range(21): errs[index(12, s)] = 0x03 # whole track: no sync
830
+ for s in range(21): errs[index(13, s)] = 0x0B # whole track: wrong ID
831
+ open("err2.d64", "wb").write(img + errs) # 175,531 bytes
832
+ ```
833
+
834
+ Read each flagged sector through the command channel (BASIC, tokenised with `petcat -w2` from lowercase source):
835
+
836
+ ```text
837
+ 10 open 15,8,15:open 2,8,2,"#"
838
+ 20 for i=1 to 14:read t,s
839
+ 30 print#15,"u1 2 0";t;s
840
+ 40 input#15,e,e$,a,b
841
+ 50 print t;s;e;e$
842
+ 60 next:close 2:close 15
843
+ 70 data 1,0,2,0,3,0,4,0,5,0,6,0,7,0,8,0,9,0,10,0,11,0,8,5,12,0,13,0
844
+ ```
845
+
846
+ Run: `x64sc -default -warp +sound +autostart-delay-random -autostartprgmode 1 -limitcycles 200000000 -drive8truedrive -drive8type 1541 -8 err2.d64 -drive8wobbleamplitude 0 -drive8wobblefrequency 0 -exitscreenshot out.png -autostart read2.prg`. The screen reads, in order: `20`, `20`, `22`, `23`, `0`, `0`, `20`, `20`, `0`, `0`, `0`, then `0` for the control read of 8,5 on the ID-flagged track, `21` for track 12 and `29` for track 13. Three PAL runs gave byte-identical screenshots.
847
+
848
+ ### Cross-references
849
+
850
+ - `../formats/c64-file-formats.md`, ".D64": the error block's size, position and the same table
851
+ - `../formats/iec-disk-reference.md`, "The 1541 DOS Error Codes": the ROM's conversion and the full DOS table
852
+ - Technique `disk_protection_tricks`: bad-sector signatures are written as controller codes in a preserved image
853
+ - Pitfall `gcr_timing_assumes_stock_drive`: the other place a loader's model of the drive diverges from the drive
854
+
855
+ ---
856
+
857
+ ## tape_bit_is_a_pulse_pair_not_a_pulse — A KERNAL tape bit is a pair of pulses, and there are three pulse lengths, not two
858
+
859
+ **Severity:** high
860
+ **Region:** both
861
+ **Triggered by registers:** DC04, DC05, DC06, DC07, DC0D
862
+ **Triggered by kernal:** LOAD, SAVE
863
+ **Triggered by techniques:** tape_turbo_loader
864
+
865
+ ### Symptom
866
+
867
+ A tape tool, a TAP-to-PRG converter or a loader's own tape reader treats each pulse of a KERNAL-written tape as one bit: short means 0, long means 1. It reads garbage. The leader decodes as thousands of zero bits, the filename comes out as noise, no block ever ends where the header says it should, and a checksum written from the same model never matches. A tool built the other way round, one that writes a bit as one pulse, produces a TAP the KERNAL cannot LOAD: it never finds a byte marker and sits at `SEARCHING` until the tape runs out.
868
+
869
+ ### Mechanism
870
+
871
+ The KERNAL tape stream has three pulse lengths, and a bit is a pair of them. Measured from a SAVE recorded on the windowless x64sc build of VICE 3.10 (PAL), one TAP entry per full pulse: short pulses centre on `$2F` (376 cycles, about 382 µs), medium on `$43` (536 cycles, about 544 µs) and long on `$58` (704 cycles, about 715 µs), each cluster a few units wide because the write interrupt reprograms the timer from software. Short then medium is a 0, medium then short is a 1, long then medium is the byte marker that opens every byte, and long then short closes a block copy. A byte is twenty pulses: the marker pair, eight data-bit pairs least significant bit first, and a parity pair that makes the count of ones in the nine bits odd. Every block is written twice, each copy opened by a countdown, `$89` down to `$81` before the first and `$09` down to `$01` before the second, and closed by a one-byte XOR checksum of the data and the end-of-block marker.
872
+
873
+ A single-pulse model cannot see any of that. It has no symbol for the medium pulse, so it lumps it with one neighbour or the other, and it has no byte boundary, so any timing hiccup shifts every later bit. The confusion is understandable: the leader before a block really is a run of single short pulses, so the first ten seconds of a tape look like a one-pulse-per-bit stream of zeros, and the medium and long pulses only appear once data starts.
874
+
875
+ ### Fix
876
+
877
+ Decode in two stages. First classify every TAP entry into S, M or L by threshold (below `$3A`, `$3A`–`$4B`, above `$4B` worked on the VICE recording; a real cassette needs wider bands). Then walk the symbols in pairs, starting at the first L: `LM` opens a byte, then take nine pairs, `SM` as 0 and `MS` as 1, check that the nine bits hold an odd number of ones, and stop the copy at `LS`. Skip the nine countdown bytes, XOR the rest, and compare with the last byte. Read the second copy the same way when the first copy has a parity or checksum failure. When writing, emit the same pairs, and put a leader of short pulses in front of each copy: the KERNAL wrote 27,137 shorts before the header block and 5,376 before the program block.
878
+
879
+ ### Worked example
880
+
881
+ The bad pattern, a Python converter that calls each pulse a bit:
882
+
883
+ ```text
884
+ raw = open("save.tap", "rb").read()[20:]
885
+ bits = [0 if b < 0x40 else 1 for b in raw if b] # one pulse, one bit: wrong
886
+ bytes_out = [sum(bits[i+k] << k for k in range(8)) for i in range(0, len(bits) - 8, 8)]
887
+ # prints thousands of $00 for the leader, then noise; no $89 countdown, no filename
888
+ ```
889
+
890
+ The fix, on the same file. The first twenty pulses after the leader in the recording were `57 41 43 2E 2F 42 2E 43 42 2F 2F 42 2F 42 30 43 42 2E 2F 41`:
891
+
892
+ ```text
893
+ def sym(b):
894
+ return "S" if b < 0x3A else "M" if b < 0x4C else "L"
895
+
896
+ def read_byte(s, i): # s: symbol string, i: index of an 'L'
897
+ if s[i:i+2] != "LM":
898
+ return None # 'LS' is the end of this copy
899
+ bits = []
900
+ for k in range(9):
901
+ pair = s[i+2+2*k : i+4+2*k]
902
+ bits.append({"SM": 0, "MS": 1}[pair])
903
+ assert sum(bits) % 2 == 1 # odd parity over data + parity bit
904
+ return sum(bits[k] << k for k in range(8)), i + 20
905
+
906
+ syms, j = [], 0
907
+ while j < len(raw):
908
+ if raw[j]:
909
+ syms.append(sym(raw[j])); j += 1
910
+ else:
911
+ syms.append("Z"); j += 4 # a zero byte carries a 24-bit count: skip the whole entry
912
+ s = "".join(syms)
913
+ i = s.index("L")
914
+ val, i = read_byte(s, i) # LM MS SM SM MS SM SM SM MS SM -> $89, the first countdown byte
915
+ ```
916
+
917
+ Running that over the recording gives `$89 $88 ... $81`, then `$01 $01 $08 $0D $08 $54 $20 ...` for the header (type 1, load `$0801`, end `$080D`, name `T`), and the second copy opens with `$09 $08 ... $01`. The 12-byte program block that follows XORs to `$E6`, the checksum byte the KERNAL wrote after it.
918
+
919
+ ### Cross-references
920
+
921
+ - `../formats/c64-file-formats.md`, ".TAP", "KERNAL bit encoding": the measured pulse table, byte layout and block structure
922
+ - `../hardware/cia-reference.md`, the Timer B and `$DC0D` notes: tape write runs on Timer B underflow interrupts and tape read measures pulse widths on the same timer
923
+ - `../hardware/c64-memory-map.md`, `$01`: bit 3 is the write line the pulses come out of, bit 4 the button sense, bit 5 the motor
924
+ - Technique `cpu_io_port_bank`: why a banking write to `$01` must preserve bits 3 to 5 while tape code runs
925
+ - Pitfall `gcr_timing_assumes_stock_drive`: the disk-side pitfall of modelling a medium's timing from the wrong assumption
926
+
927
+ ---
928
+
929
+ ## exomizer3_proto_flags_mismatch — Exomizer 3 streams need a decruncher built for the same -P bits; a classic decruncher needs -P0 at crunch time
930
+
931
+ **Severity:** high
932
+ **Region:** both
933
+ **Triggered by techniques:** exomizer_basics, crunched_data_in_basic_stub
934
+
935
+ ### Symptom
936
+
937
+ A file crunched with Exomizer 3's `mem`, `raw` or `level` command is fed to a decruncher that does not match it: a decruncher written for Exomizer 2, a copy of `exodecrunch.s` lifted from an older production or a tutorial, or the shipped decruncher assembled with a different set of `#define` switches than the `-P` flags used at crunch time. What happens depends on which bits disagree. Measured here on the windowless x64sc build of VICE 3.10: a stream crunched with `-P0` and read by the shipped decruncher at its defaults writes its output straight past the start of the destination, overwrites its own crunched input, and lands in the KERNAL's BRK handler; the machine comes back to a cleared screen and `READY.` with no message. A stream that differs in bit 5 only decrunches to completion and leaves wrong bytes behind, with nothing to say so; which bytes are wrong varies from run to run, because the misread references pull from RAM the image never wrote. The `sfx` command is not affected, because the cruncher embeds a decruncher generated for the same flags.
938
+
939
+ ### Mechanism
940
+
941
+ Exomizer 3.0.0 (2018-05-16) changed the crunched bit stream to make the 6502 decruncher faster, and its changelog says the change is incompatible. Which shape the stream takes is set by `-P<bitfield>`, a value from 0 to 63. `exo31info.txt` gives the bits; in our words:
942
+
943
+ | Bit | Value | What it changes in the stream |
944
+ |-----|-------|-------------------------------|
945
+ | 0 | 1 | Bit order inside the stream: set reads most significant bit first, clear is the Exomizer 2 order |
946
+ | 1 | 2 | A read of more than 7 bits is split into a short shift plus a whole byte instead of shifting every bit |
947
+ | 2 | 4 | The first literal byte is implicit, with no flag bit in front of it |
948
+ | 3 | 8 | The stream is aligned toward its start without a shift flag bit |
949
+ | 4 | 16 | Sequences of length 3 get an offset table of their own, three tables instead of two; the decrunch table grows from 156 to 204 bytes |
950
+ | 5 | 32 | Lets a match repeat the offset of the match before it when only one literal byte or one literal run separates them (new in 3.1.0, 2020-12-22) |
951
+
952
+ `raw`, `mem` and `level` defaulted to `-P7` in 3.0 and to `-P39` from 3.1. `-P0` clears every bit and writes the Exomizer 2 format. Since 3.1 the flag also takes relative forms: `-P+16` sets bit 4 and leaves the rest, `-P-32` clears bit 5, and they chain (`-P-32+16`). `sfx` accepts `-P` for bits 2 to 5 and forces bits 0 and 1 on, with a warning; the stub it emits always matches the payload.
953
+
954
+ The stream carries no byte that says which bits it was written with. The decruncher's assumptions are assembled in. In the shipped 6502 decrunchers (`exodecrs/exodecrunch.s` for ca65, and the `kick`, `acme` and `dasm` copies) two of the bits have a switch: `EXTRA_TABLE_ENTRY_FOR_LENGTH_THREE` must be defined for a stream crunched with `-P+16`, and `DONT_REUSE_OFFSET` for one crunched with `-P-32`. Bits 0 to 3 have no switch: those sources read the 3.x form only, so they cannot read a `-P0` stream at all, and a decruncher that reads the `-P0` form cannot read theirs. The readme in `exodecrs/` says the two streaming decrunchers, `exostreamdecr1.s` and `exostreamdecr2.s`, still read the old layout, so streams for them are crunched with `-P0`. The decruncher source also names `DECRUNCH_FORWARDS` for a stream crunched with `-f`, `LITERAL_SEQUENCES_NOT_USED` for `-c` and `MAX_SEQUENCE_LENGTH_256` for `-M256`; the last two are optional size savings, the first is a hard requirement like the `-P` pair.
955
+
956
+ Why the `-P0` case crashes rather than stalling: the decruncher first builds its tables from the encoding at the head of the stream, then copies literals and back-references downward from the end address the stream names. Read with the wrong bit order, the lengths and offsets are noise. In the measured run the output pointer crossed below the destination start with a store to `$2FFF` at 3,100,944 cycles and wrote another 9,747 bytes, one per address, down through the embedded crunched stream (`$0A8B` to `$0C23` in that build) and the decruncher's own 156-byte table (`$09EF` to `$0A8A`) to `$09EC`, before the CPU executed a `$00` byte and reached `$FE66`, the KERNAL's BRK entry, at 3,377,428 cycles. The KERNAL's BRK path goes through the BASIC warm-start vector, which clears the screen and prints `READY.`; the border colour the harness had set was reset along with it, which is the tell that distinguishes this from a hang.
957
+
958
+ ### Fix
959
+
960
+ Crunch with the flags the decruncher was built for, and keep the two from the same Exomizer release:
961
+
962
+ - A decruncher from Exomizer 2, a hand-copied one from an older source, or `exostreamdecr1.s`/`exostreamdecr2.s`: add `-P0` to the `mem`, `raw` or `level` command line.
963
+ - The shipped 3.x `exodecrunch` source: use the default flags, or if you add `-P+16` also define `EXTRA_TABLE_ENTRY_FOR_LENGTH_THREE`, and if you add `-P-32` also define `DONT_REUSE_OFFSET`. Match `-f` with `DECRUNCH_FORWARDS`.
964
+ - A third-party loader with Exomizer decrunching built in (Krill's `loadcompd`, for example): read its release notes for the Exomizer version and flags it expects and crunch with those.
965
+ - `sfx` output needs nothing; the stub always matches its payload.
966
+
967
+ When a decrunch misbehaves and the cruncher's version is in doubt, `exomizer -v` prints it, and the stream itself has a crude tell: in our `mem -l auto` files of the same input the third byte was `$01` under the defaults and `$80` under `-P0`. That is an observation from one input, not a documented signature.
968
+
969
+ ### Worked example
970
+
971
+ The measurements below were made with Exomizer 3.1.3b0, built here from the author's zlib-licensed source (Bitbucket `magli143/exomizer`, commit `ba91318`, 2025-05-01) with `make` in `src/`, and the KickAssembler decruncher `exodecrs/kick/exodecrunch.asm` from the same tree, assembled with KickAssembler 5.25. The subject is a 4,287-byte PRG that sets the border and background green, writes a marker to `$02FF`, and carries about 4 KB of patterned filler so there is something to crunch.
972
+
973
+ Self-extracting form; this is the tutorial command and it works as written:
974
+
975
+ ```text
976
+ exomizer sfx sys -o green_sfx.prg green.prg # 4,287 bytes in, 724 bytes out
977
+ ```
978
+
979
+ Run in the windowless x64sc (`-default -warp +sound +autostart-delay-random -autostartprgmode 1 -limitcycles 6000000 -exitscreenshot out.png -autostart green_sfx.prg`), the exit screenshot has a green border and reads `GREEN BORDER OK`; a second run gave a byte-identical PNG.
980
+
981
+ The same file through `mem`, with the defaults and with `-P0`:
982
+
983
+ ```text
984
+ exomizer mem -o mem_def.exo green.prg # 412 bytes; identical to -P39
985
+ exomizer mem -P0 -o mem_p0.exo green.prg # 411 bytes
986
+ exomizer mem -P7 -o mem_p7.exo green.prg # 411 bytes (the 3.0 default)
987
+ exomizer mem -P55 -o mem_p55.exo green.prg # 419 bytes (-P+16: the third table)
988
+ ```
989
+
990
+ Both `mem_def.exo` and `mem_p0.exo` begin with the load address `$07FE` and end with the decrunched end address `$18BE`; 86 of the 411 bytes between them differ, and the third byte is `$01` in one and `$80` in the other.
991
+
992
+ The decrunch matrix. A harness at `$0801` embeds one stream (crunched with `mem -l none` and the input relocated to `$3000`), calls `exod_decrunch`, then sums the 4,285 output bytes and compares the sum with the expected `$30F1`: border green and `$02FF = $A5` on a match, border red and `$02FF = $E5` on a mismatch, border yellow if it never returns. Each row is one run at 8,000,000 cycles; the first two were repeated and gave byte-identical screenshots.
993
+
994
+ | Decruncher build | Stream | Result |
995
+ |------------------|--------|--------|
996
+ | defaults | `-P39` (default) | green; `30F1 30F1` on screen |
997
+ | defaults | `-P0` | cleared screen, `READY.`, light-blue border: BRK at `$FE66` after 3,377,428 cycles |
998
+ | defaults | `-P7` | the same crash |
999
+ | defaults | `-P55` (`-P+16`) | the same crash |
1000
+ | `EXTRA_TABLE_ENTRY_FOR_LENGTH_THREE` | `-P55` | green; `30F1 30F1` |
1001
+ | `DONT_REUSE_OFFSET` | `-P7` (`-P-32`) | green; `30F1 30F1` |
1002
+ | `DONT_REUSE_OFFSET` | `-P39` | red; finished, wrong bytes, no crash. The wrong sum differs from run to run (three default runs gave `3874`, `3752` and `3612`): the misread back-references pull from RAM outside the decrunched image, which VICE fills with a random component by default. With `-raminitrandomchance 0` added, two runs were byte-identical and read `385F 30F1` |
1003
+
1004
+ The harness's crunched-byte reader is the shipped `main.asm` pattern, a self-modifying `lda $ffff` walked downward from the label after the embedded data. Nothing else in the harness depends on the flags; every difference in the table comes from the `-P` value and the two `#define` lines.
1005
+
1006
+ Not measured here: a genuine Exomizer 2 decruncher against a 3.x stream. The shipped 3.x source has no `-P0` build, and no Exomizer 2 tree was fetched. The `exodecrs/` readme's statement that such a decruncher needs `-P0` is read, not run.
1007
+
1008
+ ### Cross-references
1009
+
1010
+ - Technique `exomizer_basics`: the commands, and the version they hold for
1011
+ - Technique `crunched_data_in_basic_stub`: where a hand-rolled or borrowed depacker meets a cruncher's output
1012
+ - Pitfall `krill_cc65_2_18_miscompile`: the other loader-side failure that comes from mismatched tool versions
1013
+ - `exo31info.txt` and `exodecrs/README_exo3.txt` in the Exomizer source tree: the bit definitions and the -P0 note, read for facts only
1014
+
1015
+ ---
1016
+
1017
+ ## atn_assert_drives_data_low_via_atna — Asserting ATN pulls the drive's DATA line low through the 1541's ATNA gate, whatever the drive program writes
1018
+
1019
+ **Severity:** high
1020
+ **Region:** both
1021
+ **Triggered by registers:** DD00
1022
+ **Triggered by techniques:** drive_code_upload_and_job_queue, krill_loader_integration, sparkle_irq_loader
1023
+
1024
+ ### Symptom
1025
+
1026
+ A drive program is uploaded with `M-W`, started with `M-E`, and takes over
1027
+ the bus with a protocol of its own. The host side uses ATN as a strobe or
1028
+ as a clock and reads the drive's reply on DATA IN. The bits sampled under
1029
+ ATN never change; whole bytes come back fixed when the drive's data is
1030
+ constant, and garbled when it is not. Looked at one bit at a time, DATA IN
1031
+ is 0 for as long as ATN is held, whatever the drive program puts in its
1032
+ DATA OUT bit, and follows DATA OUT only while ATN is released; or, if the
1033
+ drive program set its ATNA bit, the other way round (the control rows in
1034
+ the table below show the released-phase reading following DATA OUT). The
1035
+ drive code is correct in isolation and the host code
1036
+ is correct in isolation. The case that prompted this entry was a loader
1037
+ that strobed bit pairs with ATN and read a fixed `$22` for every byte
1038
+ (reported; not measured here).
1039
+
1040
+ ### Mechanism
1041
+
1042
+ The 1541 does not connect its DATA OUT bit straight to the bus. The DATA
1043
+ line's driver is fed by DATA OUT or-ed with a second term, ATN IN
1044
+ exclusive-or ATNA, where ATNA is bit 4 of `$1800`, the "attention
1045
+ acknowledge" output of the serial VIA. Whenever the level of the ATN line
1046
+ differs from the ATNA bit, that term is 1 and the hardware pulls DATA low.
1047
+ This is how a 1541 answers ATN before any code has run: ATN falls, ATNA is
1048
+ still 0, DATA drops at once. The DOS's ATN service then sets ATNA to 1,
1049
+ which takes the hardware pull off and hands DATA back to the DATA OUT bit,
1050
+ and the idle loop clears ATNA again once ATN is released (see the ROM's
1051
+ `$EBE7` entry in `../formats/iec-disk-reference.md`, "1541 Drive ROM"). The
1052
+ gate itself is on the 1541 schematic (rung 4 here); its effect is measured
1053
+ below.
1054
+
1055
+ A resident drive program that leaves ATNA at 0 therefore gets the same
1056
+ hardware answer every time the host asserts ATN: DATA goes low, and
1057
+ nothing the program writes to DATA OUT can lift it, because the term is
1058
+ or-ed in. Set ATNA to 1 and the mirror image happens: DATA is held low the
1059
+ whole time ATN is released, and freed only while ATN is asserted. Either
1060
+ way a host that strobes ATN and reads DATA sees ATN, not data.
1061
+
1062
+ Measured in VICE x64sc 3.10, headless build, PAL and NTSC, with true drive
1063
+ emulation of a 1541 and a `TEST,01` image attached: a drive program of
1064
+ 12 or 14 bytes (34 for the last row, and that one also went up in a
1065
+ single `M-W`) uploaded to `$0500` and started with `M-E` did
1066
+ `SEI`, cleared DATA OUT, CLK OUT and ATNA in `$1800`, set the bits the row
1067
+ names, and looped for ever. The host then released its own ATN, CLK OUT and
1068
+ DATA OUT (`$DD00` bits 3 to 5 clear), waited about 25,000 cycles, read
1069
+ `$DD00` eight times some 2,600 cycles apart, asserted ATN (bit 3 set) and
1070
+ did the same, then released it and did the same again. All eight reads in
1071
+ every phase of every run were the one byte the table gives. The drive's
1072
+ `$1800` is the monitor's read (`m 8:1800`) at the start of each phase.
1073
+ On the host, `$DD00` bit 7 is DATA IN and 1 means the line is released;
1074
+ on the drive, `$1800` bit 0 is DATA IN and 1 means the line is low, bit 7
1075
+ is ATN IN and 1 means ATN is asserted. The two control rows fix the sense:
1076
+ with DATA OUT held low by the drive program, the host read bit 7 clear
1077
+ in every phase.
1078
+
1079
+ | Drive program holds | `$DD00`, ATN released | `$DD00`, ATN asserted | `$DD00`, released again | drive `$1800`, same three phases |
1080
+ |---|---|---|---|---|
1081
+ | ATNA 0, DATA OUT 0 | `$C7` (DATA released) | `$4F` (DATA low) | `$C7` | `00`, `81`, `00` |
1082
+ | ATNA 1, DATA OUT 0 | `$47` (DATA low) | `$CF` (DATA released) | `$47` | `11`, `90`, `11` |
1083
+ | ATNA 0, DATA OUT 1 (control) | `$47` | `$4F` | `$47` | `03`, `83`, `03` |
1084
+ | ATNA 1, DATA OUT 1 (control) | `$47` | `$4F` | `$47` | `13`, `93`, `13` |
1085
+ | interrupts left enabled, ATNA 0, DATA OUT 0 | `$C7` | `$4F` | `$C7` | `00`, `81`, `00` |
1086
+ | ATNA copied from ATN IN on every pass (the fix) | `$C7` | `$CF` | `$C7` | `00`, `90`, `00` |
1087
+
1088
+ PAL and NTSC gave the same bytes in the two rows run on both (the first
1089
+ two); the other rows are PAL. In the first row the drive never touched
1090
+ DATA OUT (its `$1800` bit 1 read 0 throughout) and DATA still went low
1091
+ with ATN; in the second it went low without ATN. Leaving the drive's
1092
+ interrupts enabled changes nothing: the ATN edge raised the drive's IRQ,
1093
+ which set the DOS's attention flag at `$7C` from `00` to `01` (measured,
1094
+ monitor read at each phase; it stayed `01` after ATN was released), but
1095
+ the routine that would act on it, set ATNA and take the bus runs from the
1096
+ DOS idle loop (ROM listing, rung 4), and a resident program never returns
1097
+ there. So the DOS does not "take over" a bus a resident program holds; it
1098
+ only notes that it was asked to.
1099
+
1100
+ ### Fix
1101
+
1102
+ Either keep ATN out of the data phase, or make the drive program track it.
1103
+
1104
+ 1. Do not use ATN as a data-phase signal. Run the custom protocol on CLK
1105
+ and DATA with ATN released on the host side, as the KERNAL itself does
1106
+ between the command bytes. Reserve ATN for what the DOS expects it to
1107
+ mean: get the drive's attention. This is the simple case and the one
1108
+ the job-queue recipe leaves the bus in.
1109
+ 2. If ATN is the strobe, the drive program must copy ATN IN into ATNA
1110
+ every time it changes, before it drives or reads DATA. With ATNA equal
1111
+ to the ATN level the exclusive-or term is 0 and DATA belongs to DATA
1112
+ OUT again. The last table row is that fix running: DATA read released
1113
+ in all three phases. Loaders that clock bit pairs with ATN do this
1114
+ inside their drive-side receive loop; a program that copies their host
1115
+ half and writes its own drive half without the ATNA update meets this
1116
+ pitfall on the first byte.
1117
+
1118
+ Both halves matter on the host too: the C64's `$DD00` has no such gate,
1119
+ so the host cannot see the problem from its own port; it just reads what
1120
+ the drive's hardware put on the line.
1121
+
1122
+ ### Worked example
1123
+
1124
+ Host side, the read that shows the fault. With a drive program that has
1125
+ cleared ATNA and released DATA, the first sample is `$C7` and the second
1126
+ `$4F`: DATA followed ATN. With ATNA set they are `$47` and `$CF`.
1127
+
1128
+ ```kick
1129
+ // Sample DATA IN with ATN released, then with ATN asserted.
1130
+ // $DD00 bit 7 is DATA IN (1 = released); bit 3 is ATN OUT (1 = pull low).
1131
+ lda $dd00
1132
+ and #$07 // keep VIC bank and TXD; ATN, CLK OUT, DATA OUT released
1133
+ sta $dd00
1134
+ jsr settle // a few thousand cycles
1135
+ lda $dd00
1136
+ sta samples // measured: $C7 with ATNA clear, $47 with ATNA set
1137
+ lda $dd00
1138
+ and #$07
1139
+ ora #$08 // assert ATN
1140
+ sta $dd00
1141
+ jsr settle
1142
+ lda $dd00
1143
+ sta samples+1 // measured: $4F with ATNA clear, $CF with ATNA set
1144
+ ```
1145
+
1146
+ The drive program that produced those bytes, assembled for `$0500` and
1147
+ started with `M-E` (the second `ora` selects the row):
1148
+
1149
+ ```text
1150
+ sei
1151
+ lda $1800
1152
+ and #$e5 ; DATA OUT (bit 1), CLK OUT (bit 3), ATNA (bit 4) all clear
1153
+ ora #$00 ; $10 for the ATNA-set row; $02 to hold DATA OUT low as a control
1154
+ sta $1800
1155
+ loop jmp loop
1156
+ ```
1157
+
1158
+ The fix, in the same loop: move ATN IN down to the ATNA position and write
1159
+ it back each pass, so the two never differ for longer than the loop takes.
1160
+
1161
+ ```text
1162
+ loop lda $1800
1163
+ lsr
1164
+ lsr
1165
+ lsr
1166
+ and #$10 ; ATN IN (bit 7) in the ATNA position (bit 4)
1167
+ sta $05f0
1168
+ lda $1800
1169
+ and #$ef
1170
+ ora $05f0
1171
+ sta $1800
1172
+ jmp loop
1173
+ ```
1174
+
1175
+ That loop is 22 bytes on top of the 12-byte program, and the whole 34
1176
+ bytes went to the drive in one `M-W` (the technique's 32-byte figure is
1177
+ what loaders send, not a limit the emulated DOS enforced here; 35 or more
1178
+ was not tried).
1179
+
1180
+ Not measured here: the same on a 1571 or 1581 (different ports and
1181
+ addresses), an SD2IEC (no code upload at all), and how long the hardware pull lasts after ATN
1182
+ changes when a real drive's loop is slower than the host's strobe.
1183
+
1184
+ ### Cross-references
1185
+
1186
+ - Technique `drive_code_upload_and_job_queue`: how the program gets onto the drive and what `M-E` does with it
1187
+ - Techniques `krill_loader_integration` and `sparkle_irq_loader`: protocols that clock bit pairs with ATN, and so must keep ATNA in step
1188
+ - Recipe `../recipes/kickassembler/drive-job-queue.md`: the upload, readback and execute code the measurement reused
1189
+ - `../formats/iec-disk-reference.md`, "Pin Map and Electrical Characteristics": the sense of the host's bits, which the control rows here agree with; "1541 VIA registers, measured": `$1800` bit by bit. Its ATN IN row reads `1` in every state, and until this entry it said ATN was released in all of them; here bit 7 read `0` with ATN released and `1` with it asserted in every run, both models, so the row now says `1` is the asserted level. Those rows were `M-R` reads, and the DOS runs a command while the UNLISTEN that ends it is still under ATN (ROM flow, rung 4, an inference not measured here), which is why the table saw `1`
1190
+ - Register `$DD00` (CIA2), `../hardware/cia-reference.md`: bits 3 to 5 out, 6 and 7 in
1191
+ - Pitfall `fastloader_dd00_write_corrupts_resident`: the other way a `$DD00` bit ends up meaning something the loader did not intend