badline 0.12.1 → 0.14.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 (565) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +66 -1
  3. data/CLAUDE.md +86 -38
  4. data/CONTRIBUTING.md +4 -3
  5. data/README.md +9 -6
  6. data/doc/library.md +9 -9
  7. data/doc/media.md +2 -1
  8. data/doc/pinned-behaviour.md +134 -105
  9. data/doc/snapshot-format.md +1047 -0
  10. data/doc/snapshots.md +73 -43
  11. data/lib/badline/audio/bare_player.rb +53 -20
  12. data/lib/badline/audio/cli.rb +6 -6
  13. data/lib/badline/audio/jukebox.rb +2 -2
  14. data/lib/badline/audio/machine_player.rb +16 -10
  15. data/lib/badline/{media → audio}/queue.rb +1 -1
  16. data/lib/badline/audio/queued_tune.rb +6 -6
  17. data/lib/badline/audio/renderer.rb +33 -22
  18. data/lib/badline/audio/stereo.rb +1 -1
  19. data/lib/badline/c128/banks.rb +6 -3
  20. data/lib/badline/c128/bus/keyed_state.rb +45 -0
  21. data/lib/badline/c128/bus.rb +35 -20
  22. data/lib/badline/c128/color_lines.rb +2 -2
  23. data/lib/badline/c128/cpu.rb +1 -1
  24. data/lib/badline/c128/drives.rb +14 -34
  25. data/lib/badline/c128/fast_serial.rb +5 -5
  26. data/lib/badline/c128/keyed_modules.rb +117 -0
  27. data/lib/badline/c128/keyed_state.rb +91 -0
  28. data/lib/badline/c128/keys.rb +9 -0
  29. data/lib/badline/c128/mmu.rb +9 -8
  30. data/lib/badline/c128/mmu_pages.rb +21 -1
  31. data/lib/badline/c128/modes.rb +21 -11
  32. data/lib/badline/c128/z80.rb +2 -2
  33. data/lib/badline/c128/z80_turns.rb +1 -1
  34. data/lib/badline/c128.rb +21 -16
  35. data/lib/badline/c64/attachments.rb +42 -0
  36. data/lib/badline/c64/banked_ram/banking.rb +32 -0
  37. data/lib/badline/c64/banked_ram/plus256k.rb +64 -0
  38. data/lib/badline/c64/banked_ram/plus60k.rb +47 -0
  39. data/lib/badline/c64/banked_ram/unexpanded.rb +30 -0
  40. data/lib/badline/c64/banked_ram.rb +23 -0
  41. data/lib/badline/c64/bus/debug_register.rb +32 -0
  42. data/lib/badline/c64/bus/extra_sids.rb +32 -0
  43. data/lib/badline/c64/bus/fittings.rb +40 -0
  44. data/lib/badline/c64/bus/keyed_state.rb +41 -0
  45. data/lib/badline/c64/bus/pla.rb +149 -0
  46. data/lib/badline/c64/bus/roms.rb +41 -0
  47. data/lib/badline/c64/bus/sid_slots.rb +44 -0
  48. data/lib/badline/c64/bus.rb +219 -0
  49. data/lib/badline/c64/cartridge/action_replay.rb +115 -0
  50. data/lib/badline/c64/cartridge/atomic_power.rb +26 -0
  51. data/lib/badline/c64/cartridge/bank.rb +83 -0
  52. data/lib/badline/c64/cartridge/comal80.rb +52 -0
  53. data/lib/badline/c64/cartridge/dinamic.rb +36 -0
  54. data/lib/badline/c64/cartridge/easy_flash.rb +132 -0
  55. data/lib/badline/c64/cartridge/epyx_fastload.rb +73 -0
  56. data/lib/badline/c64/cartridge/final_cartridge3.rb +79 -0
  57. data/lib/badline/c64/cartridge/flash.rb +279 -0
  58. data/lib/badline/c64/cartridge/freezer.rb +35 -0
  59. data/lib/badline/c64/cartridge/fun_play.rb +40 -0
  60. data/lib/badline/c64/cartridge/g_mod2.rb +64 -0
  61. data/lib/badline/c64/cartridge/game_system.rb +42 -0
  62. data/lib/badline/c64/cartridge/geo_ram.rb +65 -0
  63. data/lib/badline/c64/cartridge/kcs_power.rb +69 -0
  64. data/lib/badline/c64/cartridge/keyed_state.rb +393 -0
  65. data/lib/badline/c64/cartridge/mach5.rb +34 -0
  66. data/lib/badline/c64/cartridge/magic_desk.rb +32 -0
  67. data/lib/badline/c64/cartridge/ocean.rb +37 -0
  68. data/lib/badline/c64/cartridge/pagefox.rb +60 -0
  69. data/lib/badline/c64/cartridge/retro_replay.rb +278 -0
  70. data/lib/badline/c64/cartridge/rex_utility.rb +31 -0
  71. data/lib/badline/c64/cartridge/rgcd.rb +50 -0
  72. data/lib/badline/c64/cartridge/simons_basic.rb +37 -0
  73. data/lib/badline/c64/cartridge/standard.rb +17 -0
  74. data/lib/badline/c64/cartridge/super_games.rb +40 -0
  75. data/lib/badline/c64/cartridge/westermann.rb +32 -0
  76. data/lib/badline/c64/cartridge/zaxxon.rb +52 -0
  77. data/lib/badline/c64/cartridge.rb +232 -0
  78. data/lib/badline/c64/color_memory.rb +27 -0
  79. data/lib/badline/{computer → c64}/common.rb +2 -2
  80. data/lib/badline/{computer → c64}/kernal_traps.rb +39 -36
  81. data/lib/badline/c64/keyed_modules.rb +111 -0
  82. data/lib/badline/c64/keyed_state.rb +68 -0
  83. data/lib/badline/c64/reu/dma.rb +271 -0
  84. data/lib/badline/c64/reu/keyed_state.rb +140 -0
  85. data/lib/badline/c64/reu/ram.rb +94 -0
  86. data/lib/badline/c64/reu/trigger.rb +34 -0
  87. data/lib/badline/c64/reu.rb +289 -0
  88. data/lib/badline/c64/true_drives.rb +120 -0
  89. data/lib/badline/{computer.rb → c64.rb} +24 -15
  90. data/lib/badline/chips/cia/fast_forward.rb +62 -0
  91. data/lib/badline/chips/cia/interrupt_register.rb +128 -0
  92. data/lib/badline/chips/cia/keyed_parts.rb +213 -0
  93. data/lib/badline/chips/cia/serial.rb +246 -0
  94. data/lib/badline/chips/cia/time_of_day.rb +210 -0
  95. data/lib/badline/chips/cia/timer.rb +188 -0
  96. data/lib/badline/chips/cia.rb +281 -0
  97. data/lib/badline/chips/cpu/addressing.rb +133 -0
  98. data/lib/badline/chips/cpu/boundary.rb +56 -0
  99. data/lib/badline/chips/cpu/core.rb +183 -0
  100. data/lib/badline/chips/cpu/instruction.rb +338 -0
  101. data/lib/badline/chips/cpu/instruction_set/arithmetic.rb +118 -0
  102. data/lib/badline/chips/cpu/instruction_set/bitwise.rb +130 -0
  103. data/lib/badline/chips/cpu/instruction_set/branch.rb +83 -0
  104. data/lib/badline/chips/cpu/instruction_set/flag.rb +67 -0
  105. data/lib/badline/chips/cpu/instruction_set/illegal.rb +274 -0
  106. data/lib/badline/chips/cpu/instruction_set/inc_dec.rb +73 -0
  107. data/lib/badline/chips/cpu/instruction_set/stack.rb +56 -0
  108. data/lib/badline/chips/cpu/instruction_set/transfer.rb +141 -0
  109. data/lib/badline/chips/cpu/instruction_set.rb +59 -0
  110. data/lib/badline/chips/cpu/interrupts.rb +108 -0
  111. data/lib/badline/chips/cpu/keyed_state.rb +177 -0
  112. data/lib/badline/chips/cpu/microcode.rb +195 -0
  113. data/lib/badline/chips/cpu/operations.rb +90 -0
  114. data/lib/badline/chips/cpu/port.rb +133 -0
  115. data/lib/badline/chips/cpu/stack_operations.rb +84 -0
  116. data/lib/badline/chips/cpu/traps.rb +23 -0
  117. data/lib/badline/chips/cpu.rb +17 -0
  118. data/lib/badline/chips/sid/catch_up.rb +122 -0
  119. data/lib/badline/chips/sid/decimator.rb +68 -0
  120. data/lib/badline/chips/sid/envelope.rb +236 -0
  121. data/lib/badline/chips/sid/filter.rb +227 -0
  122. data/lib/badline/chips/sid/keyed_parts.rb +286 -0
  123. data/lib/badline/chips/sid/voice.rb +69 -0
  124. data/lib/badline/chips/sid/waveform/combined.rb +128 -0
  125. data/lib/badline/chips/sid/waveform/fast_forward.rb +107 -0
  126. data/lib/badline/chips/sid/waveform/noise_writeback.rb +124 -0
  127. data/lib/badline/chips/sid/waveform.rb +305 -0
  128. data/lib/badline/chips/sid.rb +264 -0
  129. data/lib/badline/chips/ted/beam.rb +119 -0
  130. data/lib/badline/chips/ted/timers.rb +66 -0
  131. data/lib/badline/chips/ted.rb +143 -0
  132. data/lib/badline/chips/tia.rb +49 -0
  133. data/lib/badline/chips/vdc/keyed_parts.rb +105 -0
  134. data/lib/badline/{c128 → chips}/vdc/memory.rb +1 -1
  135. data/lib/badline/{c128 → chips}/vdc/painter.rb +1 -13
  136. data/lib/badline/{c128 → chips}/vdc/raster.rb +1 -17
  137. data/lib/badline/{c128 → chips}/vdc/window.rb +1 -10
  138. data/lib/badline/{c128 → chips}/vdc.rb +6 -31
  139. data/lib/badline/chips/via/control_lines.rb +109 -0
  140. data/lib/badline/chips/via/fast_forward.rb +55 -0
  141. data/lib/badline/chips/via/interrupt_register.rb +60 -0
  142. data/lib/badline/chips/via/keyed_parts.rb +163 -0
  143. data/lib/badline/chips/via/shift_register.rb +168 -0
  144. data/lib/badline/chips/via/timer1.rb +120 -0
  145. data/lib/badline/chips/via/timer2.rb +117 -0
  146. data/lib/badline/chips/via.rb +267 -0
  147. data/lib/badline/chips/vic/bank.rb +135 -0
  148. data/lib/badline/chips/vic/border_mask.rb +78 -0
  149. data/lib/badline/chips/vic/collisions.rb +106 -0
  150. data/lib/badline/chips/vic/color_patches.rb +38 -0
  151. data/lib/badline/chips/vic/display_state.rb +168 -0
  152. data/lib/badline/chips/vic/graphics_mode.rb +166 -0
  153. data/lib/badline/chips/vic/graphics_shifter.rb +183 -0
  154. data/lib/badline/chips/vic/keyed_parts.rb +228 -0
  155. data/lib/badline/chips/vic/keyed_sprites.rb +225 -0
  156. data/lib/badline/chips/vic/keyed_state.rb +102 -0
  157. data/lib/badline/chips/vic/register_log.rb +65 -0
  158. data/lib/badline/chips/vic/registers.rb +188 -0
  159. data/lib/badline/chips/vic/sequencer.rb +275 -0
  160. data/lib/badline/chips/vic/sequencer_output.rb +118 -0
  161. data/lib/badline/chips/vic/sprite/internal_bus.rb +59 -0
  162. data/lib/badline/chips/vic/sprite/shifter.rb +148 -0
  163. data/lib/badline/chips/vic/sprite/timing.rb +58 -0
  164. data/lib/badline/chips/vic/sprite.rb +337 -0
  165. data/lib/badline/chips/vic/sprites.rb +281 -0
  166. data/lib/badline/chips/vic.rb +787 -0
  167. data/lib/badline/chips/vic_i/keyed_parts.rb +142 -0
  168. data/lib/badline/{vic20/vic → chips/vic_i}/painter.rb +2 -30
  169. data/lib/badline/chips/vic_i/sound/output.rb +85 -0
  170. data/lib/badline/chips/vic_i/sound.rb +227 -0
  171. data/lib/badline/{vic20/vic → chips/vic_i}/text_window.rb +2 -2
  172. data/lib/badline/{vic20/vic.rb → chips/vic_i.rb} +6 -6
  173. data/lib/badline/chips/z80/arithmetic.rb +119 -0
  174. data/lib/badline/chips/z80/bit_page.rb +57 -0
  175. data/lib/badline/chips/z80/block_io.rb +60 -0
  176. data/lib/badline/chips/z80/block_transfers.rb +61 -0
  177. data/lib/badline/chips/z80/bus_cycles.rb +113 -0
  178. data/lib/badline/chips/z80/core.rb +115 -0
  179. data/lib/badline/chips/z80/exchanges.rb +52 -0
  180. data/lib/badline/chips/z80/extended_page.rb +91 -0
  181. data/lib/badline/chips/z80/flags.rb +39 -0
  182. data/lib/badline/chips/z80/interrupt_acceptance.rb +48 -0
  183. data/lib/badline/chips/z80/jumps.rb +84 -0
  184. data/lib/badline/chips/z80/keyed_state.rb +73 -0
  185. data/lib/badline/chips/z80/loads.rb +100 -0
  186. data/lib/badline/chips/z80/main_page.rb +136 -0
  187. data/lib/badline/chips/z80/registers.rb +122 -0
  188. data/lib/badline/chips/z80/shifts.rb +66 -0
  189. data/lib/badline/chips/z80.rb +13 -0
  190. data/lib/badline/chips.rb +10 -0
  191. data/lib/badline/core.rb +13 -32
  192. data/lib/badline/drives/datasette/keyed_state.rb +38 -0
  193. data/lib/badline/drives/datasette.rb +173 -0
  194. data/lib/badline/drives/engine/core.rb +152 -0
  195. data/lib/badline/drives/engine/fast_serial.rb +75 -0
  196. data/lib/badline/drives/engine/idle.rb +127 -0
  197. data/lib/badline/drives/engine/idle_cpu.rb +30 -0
  198. data/lib/badline/drives/engine/orbit.rb +81 -0
  199. data/lib/badline/drives/engine/serial_lines.rb +72 -0
  200. data/lib/badline/drives/engine/sleep.rb +114 -0
  201. data/lib/badline/drives/engine/vias.rb +86 -0
  202. data/lib/badline/drives/engine/watch.rb +152 -0
  203. data/lib/badline/drives/engine.rb +11 -0
  204. data/lib/badline/drives/model1541/bus.rb +75 -0
  205. data/lib/badline/drives/model1541/byte_ready.rb +50 -0
  206. data/lib/badline/drives/model1541/cpu.rb +13 -0
  207. data/lib/badline/drives/model1541/disk.rb +318 -0
  208. data/lib/badline/drives/model1541/disk_via.rb +38 -0
  209. data/lib/badline/drives/model1541/gcr.rb +59 -0
  210. data/lib/badline/drives/model1541/keyed_state.rb +251 -0
  211. data/lib/badline/drives/model1541/mechanism.rb +313 -0
  212. data/lib/badline/drives/model1541/rotation.rb +123 -0
  213. data/lib/badline/drives/model1541/sector_reader.rb +78 -0
  214. data/lib/badline/drives/model1541/serial_port.rb +20 -0
  215. data/lib/badline/drives/model1541/track.rb +100 -0
  216. data/lib/badline/drives/model1541.rb +80 -0
  217. data/lib/badline/drives/model1571/bus.rb +100 -0
  218. data/lib/badline/drives/model1571/cpu.rb +13 -0
  219. data/lib/badline/drives/model1571/keyed_state.rb +108 -0
  220. data/lib/badline/drives/model1571/serial_port.rb +39 -0
  221. data/lib/badline/drives/model1571/serial_via.rb +27 -0
  222. data/lib/badline/drives/model1571/wd1770.rb +108 -0
  223. data/lib/badline/drives/model1571.rb +141 -0
  224. data/lib/badline/drives/model1581/bus.rb +126 -0
  225. data/lib/badline/drives/model1581/cpu.rb +13 -0
  226. data/lib/badline/drives/model1581/disk.rb +133 -0
  227. data/lib/badline/drives/model1581/keyed_state.rb +280 -0
  228. data/lib/badline/drives/model1581/mechanism.rb +98 -0
  229. data/lib/badline/drives/model1581/serial_port.rb +54 -0
  230. data/lib/badline/drives/model1581/slot.rb +61 -0
  231. data/lib/badline/drives/model1581/track.rb +102 -0
  232. data/lib/badline/drives/model1581/track_bytes.rb +77 -0
  233. data/lib/badline/drives/model1581/wd1772/registers.rb +91 -0
  234. data/lib/badline/drives/model1581/wd1772/sector_writes.rb +75 -0
  235. data/lib/badline/drives/model1581/wd1772/sectors.rb +152 -0
  236. data/lib/badline/drives/model1581/wd1772/stepping.rb +111 -0
  237. data/lib/badline/drives/model1581/wd1772/tracks.rb +127 -0
  238. data/lib/badline/drives/model1581/wd1772/turn.rb +75 -0
  239. data/lib/badline/drives/model1581/wd1772.rb +216 -0
  240. data/lib/badline/drives/model1581.rb +206 -0
  241. data/lib/badline/drives/serial_bus.rb +161 -0
  242. data/lib/badline/drives.rb +8 -0
  243. data/lib/badline/frontend/app.rb +3 -3
  244. data/lib/badline/frontend/boot.rb +4 -4
  245. data/lib/badline/frontend/controls.rb +19 -3
  246. data/lib/badline/frontend/drive_led.rb +44 -21
  247. data/lib/badline/frontend/machine_window.rb +3 -1
  248. data/lib/badline/frontend/menu/menu_dialogs.rb +2 -2
  249. data/lib/badline/frontend/player/filter_view.rb +1 -1
  250. data/lib/badline/frontend/player/player_controller.rb +2 -2
  251. data/lib/badline/frontend/player/player_screen.rb +1 -0
  252. data/lib/badline/frontend/player/theme.rb +7 -7
  253. data/lib/badline/frontend/snapshots.rb +12 -1
  254. data/lib/badline/frontend/timeline.rb +30 -12
  255. data/lib/badline/input/control_ports.rb +78 -0
  256. data/lib/badline/input/joystick.rb +27 -0
  257. data/lib/badline/input/keyboard.rb +114 -0
  258. data/lib/badline/input/matrix_typist.rb +98 -0
  259. data/lib/badline/input.rb +4 -0
  260. data/lib/badline/kernal_trap/chrout.rb +45 -0
  261. data/lib/badline/kernal_trap/{drive → dos}/block_commands.rb +1 -1
  262. data/lib/badline/kernal_trap/dos/channel.rb +156 -0
  263. data/lib/badline/kernal_trap/{drive → dos}/channels.rb +1 -26
  264. data/lib/badline/kernal_trap/{drive → dos}/memory.rb +1 -10
  265. data/lib/badline/kernal_trap/{drive → dos}/parameters.rb +1 -1
  266. data/lib/badline/kernal_trap/{drive → dos}/status.rb +1 -9
  267. data/lib/badline/kernal_trap/{drive → dos}/write_file.rb +1 -19
  268. data/lib/badline/kernal_trap/{drive → dos}/writes.rb +1 -1
  269. data/lib/badline/kernal_trap/{drive.rb → dos.rb} +9 -27
  270. data/lib/badline/kernal_trap/file_routine.rb +4 -4
  271. data/lib/badline/kernal_trap/keyed_state.rb +150 -0
  272. data/lib/badline/kernal_trap/layout.rb +75 -11
  273. data/lib/badline/kernal_trap/load.rb +19 -21
  274. data/lib/badline/kernal_trap/routine.rb +12 -0
  275. data/lib/badline/kernal_trap/save.rb +12 -22
  276. data/lib/badline/kernal_trap/serial.rb +8 -24
  277. data/lib/badline/kernal_trap.rb +3 -2
  278. data/lib/badline/machine/clocking.rb +1 -1
  279. data/lib/badline/machine/keyboard_buffer.rb +49 -0
  280. data/lib/badline/machine/settling.rb +29 -0
  281. data/lib/badline/machine.rb +10 -7
  282. data/lib/badline/media/boot_disk.rb +14 -2
  283. data/lib/badline/media/extensions.rb +10 -0
  284. data/lib/badline/media/true_drive.rb +1 -1
  285. data/lib/badline/media/tune.rb +2 -2
  286. data/lib/badline/media.rb +3 -3
  287. data/lib/badline/memory.rb +3 -6
  288. data/lib/badline/model.rb +1 -1
  289. data/lib/badline/options/family.rb +5 -2
  290. data/lib/badline/options/help.rb +42 -16
  291. data/lib/badline/options/table.rb +2 -0
  292. data/lib/badline/options/validation.rb +2 -0
  293. data/lib/badline/options.rb +6 -1
  294. data/lib/badline/plus4/acia.rb +45 -0
  295. data/lib/badline/plus4/bus.rb +312 -0
  296. data/lib/badline/plus4/cpu.rb +10 -0
  297. data/lib/badline/plus4/cpu_port.rb +93 -0
  298. data/lib/badline/plus4/keyboard_port.rb +51 -0
  299. data/lib/badline/plus4/model.rb +38 -0
  300. data/lib/badline/plus4/user_port.rb +31 -0
  301. data/lib/badline/plus4.rb +164 -0
  302. data/lib/badline/roms/README +44 -6
  303. data/lib/badline/roms/plus4/basic.rom +0 -0
  304. data/lib/badline/roms/plus4/function-high.rom +0 -0
  305. data/lib/badline/roms/plus4/function-low.rom +0 -0
  306. data/lib/badline/roms/plus4/kernal-ntsc.rom +0 -0
  307. data/lib/badline/roms/plus4/kernal-pal.rom +0 -0
  308. data/lib/badline/sdl.rb +1 -0
  309. data/lib/badline/{storage/sid_file.rb → sid_tune/file.rb} +3 -3
  310. data/lib/badline/{storage → sid_tune}/hvsc.rb +7 -7
  311. data/lib/badline/{storage → sid_tune}/song_lengths.rb +2 -2
  312. data/lib/badline/{storage → sid_tune}/stil/credit.rb +1 -1
  313. data/lib/badline/{storage → sid_tune}/stil.rb +3 -3
  314. data/lib/badline/sid_tune.rb +6 -0
  315. data/lib/badline/snapshot/c128_setup.rb +32 -0
  316. data/lib/badline/snapshot/c64_setup.rb +83 -0
  317. data/lib/badline/snapshot/container.rb +4 -0
  318. data/lib/badline/snapshot/field_doc/markdown.rb +112 -0
  319. data/lib/badline/snapshot/field_doc.rb +109 -0
  320. data/lib/badline/snapshot/image.rb +132 -46
  321. data/lib/badline/snapshot/keyed/cursor.rb +40 -0
  322. data/lib/badline/snapshot/keyed/modules.rb +66 -0
  323. data/lib/badline/snapshot/keyed/reader.rb +188 -0
  324. data/lib/badline/snapshot/keyed/recorder.rb +88 -0
  325. data/lib/badline/snapshot/keyed/report.rb +26 -0
  326. data/lib/badline/snapshot/keyed/writer.rb +55 -0
  327. data/lib/badline/snapshot/keyed.rb +64 -0
  328. data/lib/badline/snapshot/machine_state.rb +17 -64
  329. data/lib/badline/snapshot/storage_setup.rb +63 -0
  330. data/lib/badline/snapshot/vic20_setup.rb +28 -0
  331. data/lib/badline/snapshot/vice/c64_mem.rb +1 -1
  332. data/lib/badline/snapshot/vice/restore.rb +1 -1
  333. data/lib/badline/snapshot/vice/reu1764.rb +3 -3
  334. data/lib/badline/snapshot/vice/reu_fields.rb +40 -0
  335. data/lib/badline/snapshot/vice.rb +13 -35
  336. data/lib/badline/snapshot.rb +44 -22
  337. data/lib/badline/storage/crt_file.rb +3 -1
  338. data/lib/badline/storage/d64_image.rb +0 -2
  339. data/lib/badline/storage/d71_image.rb +0 -2
  340. data/lib/badline/storage/d81_image.rb +0 -2
  341. data/lib/badline/storage/disk_image.rb +3 -6
  342. data/lib/badline/storage/g64_image.rb +10 -5
  343. data/lib/badline/storage/host_directory.rb +0 -5
  344. data/lib/badline/storage/t64.rb +2 -8
  345. data/lib/badline/storage/tap.rb +43 -15
  346. data/lib/badline/storage/unavailable.rb +1 -1
  347. data/lib/badline/storage.rb +0 -37
  348. data/lib/badline/support/c128_checkpoint.rb +39 -0
  349. data/lib/badline/support/checkpoint.rb +108 -0
  350. data/lib/badline/support/vic20_checkpoint.rb +38 -0
  351. data/lib/badline/support.rb +5 -0
  352. data/lib/badline/version.rb +1 -1
  353. data/lib/badline/vic20/attachments.rb +8 -5
  354. data/lib/badline/vic20/bus/keyed_state.rb +52 -0
  355. data/lib/badline/vic20/bus.rb +2 -25
  356. data/lib/badline/vic20/cpu.rb +1 -1
  357. data/lib/badline/vic20/keyed_modules.rb +73 -0
  358. data/lib/badline/vic20/keyed_state.rb +45 -0
  359. data/lib/badline/vic20/port_wiring.rb +4 -4
  360. data/lib/badline/vic20/user_via_ports.rb +2 -2
  361. data/lib/badline/vic20.rb +22 -17
  362. data/lib/badline.rb +1 -0
  363. metadata +271 -228
  364. data/lib/badline/address_bus/extra_sids.rb +0 -30
  365. data/lib/badline/address_bus/fittings.rb +0 -38
  366. data/lib/badline/address_bus/pla.rb +0 -147
  367. data/lib/badline/address_bus/roms.rb +0 -39
  368. data/lib/badline/address_bus/saved_state.rb +0 -66
  369. data/lib/badline/address_bus/sid_slots.rb +0 -42
  370. data/lib/badline/address_bus.rb +0 -216
  371. data/lib/badline/c128/bus_state.rb +0 -53
  372. data/lib/badline/c128/saved_state.rb +0 -143
  373. data/lib/badline/cartridge/action_replay.rb +0 -126
  374. data/lib/badline/cartridge/atomic_power.rb +0 -24
  375. data/lib/badline/cartridge/bank.rb +0 -81
  376. data/lib/badline/cartridge/comal80.rb +0 -50
  377. data/lib/badline/cartridge/dinamic.rb +0 -34
  378. data/lib/badline/cartridge/easy_flash.rb +0 -145
  379. data/lib/badline/cartridge/epyx_fastload.rb +0 -79
  380. data/lib/badline/cartridge/final_cartridge3.rb +0 -89
  381. data/lib/badline/cartridge/flash.rb +0 -306
  382. data/lib/badline/cartridge/freezer.rb +0 -33
  383. data/lib/badline/cartridge/fun_play.rb +0 -38
  384. data/lib/badline/cartridge/g_mod2.rb +0 -74
  385. data/lib/badline/cartridge/game_system.rb +0 -40
  386. data/lib/badline/cartridge/geo_ram.rb +0 -79
  387. data/lib/badline/cartridge/kcs_power.rb +0 -75
  388. data/lib/badline/cartridge/mach5.rb +0 -32
  389. data/lib/badline/cartridge/magic_desk.rb +0 -30
  390. data/lib/badline/cartridge/ocean.rb +0 -35
  391. data/lib/badline/cartridge/pagefox.rb +0 -68
  392. data/lib/badline/cartridge/retro_replay.rb +0 -320
  393. data/lib/badline/cartridge/rex_utility.rb +0 -29
  394. data/lib/badline/cartridge/rgcd.rb +0 -58
  395. data/lib/badline/cartridge/simons_basic.rb +0 -35
  396. data/lib/badline/cartridge/standard.rb +0 -15
  397. data/lib/badline/cartridge/super_games.rb +0 -48
  398. data/lib/badline/cartridge/westermann.rb +0 -30
  399. data/lib/badline/cartridge/zaxxon.rb +0 -66
  400. data/lib/badline/cartridge.rb +0 -303
  401. data/lib/badline/checkpoint.rb +0 -98
  402. data/lib/badline/chrout_trap.rb +0 -43
  403. data/lib/badline/cia/fast_forward.rb +0 -60
  404. data/lib/badline/cia/interrupt_register.rb +0 -143
  405. data/lib/badline/cia/serial.rb +0 -275
  406. data/lib/badline/cia/state.rb +0 -47
  407. data/lib/badline/cia/timer.rb +0 -206
  408. data/lib/badline/cia.rb +0 -279
  409. data/lib/badline/color_memory.rb +0 -25
  410. data/lib/badline/computer/attachments.rb +0 -88
  411. data/lib/badline/computer/saved_state.rb +0 -117
  412. data/lib/badline/computer/true_drives.rb +0 -69
  413. data/lib/badline/control_ports.rb +0 -76
  414. data/lib/badline/cpu/addressing.rb +0 -131
  415. data/lib/badline/cpu/boundary.rb +0 -54
  416. data/lib/badline/cpu/core.rb +0 -165
  417. data/lib/badline/cpu/microcode.rb +0 -177
  418. data/lib/badline/cpu/operations.rb +0 -88
  419. data/lib/badline/cpu/saved_state.rb +0 -115
  420. data/lib/badline/cpu/stack_operations.rb +0 -82
  421. data/lib/badline/cpu.rb +0 -10
  422. data/lib/badline/cpu_port.rb +0 -112
  423. data/lib/badline/datasette.rb +0 -161
  424. data/lib/badline/debug_register.rb +0 -28
  425. data/lib/badline/drive/core.rb +0 -147
  426. data/lib/badline/drive/fast_serial.rb +0 -73
  427. data/lib/badline/drive/idle.rb +0 -125
  428. data/lib/badline/drive/idle_cpu.rb +0 -28
  429. data/lib/badline/drive/orbit.rb +0 -79
  430. data/lib/badline/drive/serial_lines.rb +0 -73
  431. data/lib/badline/drive/sleep.rb +0 -112
  432. data/lib/badline/drive/vias.rb +0 -84
  433. data/lib/badline/drive/watch.rb +0 -150
  434. data/lib/badline/drive.rb +0 -11
  435. data/lib/badline/drive1541/bus.rb +0 -86
  436. data/lib/badline/drive1541/byte_ready.rb +0 -48
  437. data/lib/badline/drive1541/cpu.rb +0 -11
  438. data/lib/badline/drive1541/disk.rb +0 -315
  439. data/lib/badline/drive1541/disk_state.rb +0 -86
  440. data/lib/badline/drive1541/disk_via.rb +0 -36
  441. data/lib/badline/drive1541/gcr.rb +0 -57
  442. data/lib/badline/drive1541/mechanism.rb +0 -313
  443. data/lib/badline/drive1541/mechanism_state.rb +0 -68
  444. data/lib/badline/drive1541/rotation.rb +0 -121
  445. data/lib/badline/drive1541/sector_reader.rb +0 -76
  446. data/lib/badline/drive1541/serial_port.rb +0 -18
  447. data/lib/badline/drive1541/track.rb +0 -98
  448. data/lib/badline/drive1541.rb +0 -111
  449. data/lib/badline/drive1571/bus.rb +0 -111
  450. data/lib/badline/drive1571/cpu.rb +0 -11
  451. data/lib/badline/drive1571/serial_port.rb +0 -37
  452. data/lib/badline/drive1571/serial_via.rb +0 -25
  453. data/lib/badline/drive1571/wd1770.rb +0 -120
  454. data/lib/badline/drive1571.rb +0 -175
  455. data/lib/badline/drive1581/bus.rb +0 -137
  456. data/lib/badline/drive1581/cpu.rb +0 -11
  457. data/lib/badline/drive1581/disk.rb +0 -135
  458. data/lib/badline/drive1581/disk_state.rb +0 -87
  459. data/lib/badline/drive1581/mechanism.rb +0 -117
  460. data/lib/badline/drive1581/serial_port.rb +0 -52
  461. data/lib/badline/drive1581/slot.rb +0 -78
  462. data/lib/badline/drive1581/track.rb +0 -100
  463. data/lib/badline/drive1581/track_bytes.rb +0 -75
  464. data/lib/badline/drive1581/wd1772/registers.rb +0 -89
  465. data/lib/badline/drive1581/wd1772/sector_writes.rb +0 -73
  466. data/lib/badline/drive1581/wd1772/sectors.rb +0 -150
  467. data/lib/badline/drive1581/wd1772/state.rb +0 -66
  468. data/lib/badline/drive1581/wd1772/stepping.rb +0 -109
  469. data/lib/badline/drive1581/wd1772/tracks.rb +0 -125
  470. data/lib/badline/drive1581/wd1772/turn.rb +0 -73
  471. data/lib/badline/drive1581/wd1772.rb +0 -216
  472. data/lib/badline/drive1581.rb +0 -233
  473. data/lib/badline/iec_bus.rb +0 -159
  474. data/lib/badline/instruction.rb +0 -334
  475. data/lib/badline/instruction_set/arithmetic.rb +0 -114
  476. data/lib/badline/instruction_set/bitwise.rb +0 -126
  477. data/lib/badline/instruction_set/branch.rb +0 -79
  478. data/lib/badline/instruction_set/flag.rb +0 -63
  479. data/lib/badline/instruction_set/illegal.rb +0 -270
  480. data/lib/badline/instruction_set/inc_dec.rb +0 -69
  481. data/lib/badline/instruction_set/stack.rb +0 -52
  482. data/lib/badline/instruction_set/transfer.rb +0 -137
  483. data/lib/badline/instruction_set.rb +0 -55
  484. data/lib/badline/interrupts.rb +0 -104
  485. data/lib/badline/joystick.rb +0 -25
  486. data/lib/badline/kernal_trap/channel.rb +0 -181
  487. data/lib/badline/keyboard.rb +0 -112
  488. data/lib/badline/keyboard_buffer.rb +0 -33
  489. data/lib/badline/ram_expansion/banking.rb +0 -30
  490. data/lib/badline/ram_expansion/plus256k.rb +0 -54
  491. data/lib/badline/ram_expansion/plus60k.rb +0 -43
  492. data/lib/badline/ram_expansion/unexpanded.rb +0 -28
  493. data/lib/badline/ram_expansion.rb +0 -21
  494. data/lib/badline/reu/dma/saved_state.rb +0 -50
  495. data/lib/badline/reu/dma.rb +0 -273
  496. data/lib/badline/reu/ram.rb +0 -107
  497. data/lib/badline/reu/saved_state.rb +0 -80
  498. data/lib/badline/reu/trigger.rb +0 -32
  499. data/lib/badline/reu.rb +0 -289
  500. data/lib/badline/sid/catch_up.rb +0 -120
  501. data/lib/badline/sid/decimator.rb +0 -64
  502. data/lib/badline/sid/envelope/state.rb +0 -68
  503. data/lib/badline/sid/envelope.rb +0 -239
  504. data/lib/badline/sid/filter.rb +0 -263
  505. data/lib/badline/sid/voice.rb +0 -77
  506. data/lib/badline/sid/waveform/combined.rb +0 -126
  507. data/lib/badline/sid/waveform/fast_forward.rb +0 -105
  508. data/lib/badline/sid/waveform/noise_writeback.rb +0 -122
  509. data/lib/badline/sid/waveform/state.rb +0 -66
  510. data/lib/badline/sid/waveform.rb +0 -305
  511. data/lib/badline/sid.rb +0 -303
  512. data/lib/badline/snapshot/setup.rb +0 -75
  513. data/lib/badline/snapshot/state.rb +0 -189
  514. data/lib/badline/time_of_day.rb +0 -228
  515. data/lib/badline/traps.rb +0 -19
  516. data/lib/badline/via/control_lines.rb +0 -122
  517. data/lib/badline/via/fast_forward.rb +0 -53
  518. data/lib/badline/via/interrupt_register.rb +0 -67
  519. data/lib/badline/via/shift_register.rb +0 -180
  520. data/lib/badline/via/state.rb +0 -42
  521. data/lib/badline/via/timer1.rb +0 -131
  522. data/lib/badline/via/timer2.rb +0 -133
  523. data/lib/badline/via.rb +0 -266
  524. data/lib/badline/vic/bank.rb +0 -133
  525. data/lib/badline/vic/border_mask.rb +0 -86
  526. data/lib/badline/vic/collisions.rb +0 -115
  527. data/lib/badline/vic/color_patches.rb +0 -47
  528. data/lib/badline/vic/display_state.rb +0 -185
  529. data/lib/badline/vic/graphics_mode.rb +0 -164
  530. data/lib/badline/vic/graphics_shifter.rb +0 -207
  531. data/lib/badline/vic/register_log.rb +0 -81
  532. data/lib/badline/vic/registers.rb +0 -195
  533. data/lib/badline/vic/sequencer.rb +0 -275
  534. data/lib/badline/vic/sequencer_output.rb +0 -116
  535. data/lib/badline/vic/sequencer_state.rb +0 -89
  536. data/lib/badline/vic/sprite/internal_bus.rb +0 -68
  537. data/lib/badline/vic/sprite/saved_state.rb +0 -64
  538. data/lib/badline/vic/sprite/shifter.rb +0 -146
  539. data/lib/badline/vic/sprite/timing.rb +0 -56
  540. data/lib/badline/vic/sprite.rb +0 -337
  541. data/lib/badline/vic/sprites.rb +0 -282
  542. data/lib/badline/vic/sprites_state.rb +0 -57
  543. data/lib/badline/vic.rb +0 -853
  544. data/lib/badline/vic20/saved_state.rb +0 -124
  545. data/lib/badline/vic20/sound/output.rb +0 -83
  546. data/lib/badline/vic20/sound.rb +0 -233
  547. data/lib/badline/vic20/vic/saved_state.rb +0 -55
  548. data/lib/badline/z80/arithmetic.rb +0 -117
  549. data/lib/badline/z80/bit_page.rb +0 -55
  550. data/lib/badline/z80/block_io.rb +0 -58
  551. data/lib/badline/z80/block_transfers.rb +0 -59
  552. data/lib/badline/z80/bus_cycles.rb +0 -111
  553. data/lib/badline/z80/core.rb +0 -113
  554. data/lib/badline/z80/exchanges.rb +0 -50
  555. data/lib/badline/z80/extended_page.rb +0 -89
  556. data/lib/badline/z80/flags.rb +0 -37
  557. data/lib/badline/z80/interrupt_acceptance.rb +0 -46
  558. data/lib/badline/z80/jumps.rb +0 -82
  559. data/lib/badline/z80/loads.rb +0 -98
  560. data/lib/badline/z80/main_page.rb +0 -134
  561. data/lib/badline/z80/registers.rb +0 -120
  562. data/lib/badline/z80/saved_state.rb +0 -69
  563. data/lib/badline/z80/shifts.rb +0 -64
  564. data/lib/badline/z80.rb +0 -11
  565. /data/lib/badline/snapshot/{fields.rb → vice/fields.rb} +0 -0
@@ -0,0 +1,148 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ module Chips
5
+ class VIC
6
+ class Sprite
7
+ # The X comparator and the 24-bit shift register behind it: where a
8
+ # line fires the sprite, and the pixels each firing shifts out.
9
+ module Shifter
10
+ private
11
+
12
+ # The first pixel of every comparator match along the line. Writes to
13
+ # $d000/$d010 move the compare value mid-line, so a sprite can miss
14
+ # its match entirely and pick up a later one, a second one, or none.
15
+ def comparator_hits(log)
16
+ return compare_x(@registers) unless log
17
+
18
+ hits = []
19
+ log.rewind
20
+ log.advance(0)
21
+ from = 0
22
+ while from < @width
23
+ upto = [log.next_x, @width].min
24
+ compare_x(log).each do |start|
25
+ match = (start - 1) % @width
26
+ hits << start if match >= from && match < upto
27
+ end
28
+ break if upto >= @width
29
+
30
+ from = upto
31
+ log.advance(from)
32
+ end
33
+ hits
34
+ end
35
+
36
+ # The pixels the sprite starts at, one past each comparator match.
37
+ # The 6569's X counter only runs to 503, so the eight coordinates
38
+ # above it never match and the sprite stays dark — the gap the
39
+ # spritegap tests find at $1f8.
40
+ def compare_x(view)
41
+ msb = view[0x10].anybits?(@bit) ? 0x100 : 0
42
+ @timing.x_pixels[msb | view[index * 2]]
43
+ end
44
+
45
+ # Pixels from the X match to the next reload.
46
+ def reload_cut(start) = (start < @reload_x ? @reload_x : @reload_x + @width) - start
47
+
48
+ # A run that reaches the next reload is cut there, holding its last
49
+ # pixel through K+6. Returns the span that is left.
50
+ def cut_at_reload(cut, span, codes)
51
+ return span if cut > span || cut >= MAX_SPAN
52
+
53
+ RELOAD_HOLD.times { |i| codes[cut + i] = codes[cut - 1] }
54
+ cut + RELOAD_HOLD
55
+ end
56
+
57
+ # Shift one firing out into `codes`, up to the sprite's end, `cut`
58
+ # pixels from the reload.
59
+ def run(log, start, codes, bits, cut)
60
+ @sr = bits
61
+ log&.rewind
62
+ pos = start
63
+ count = 0
64
+ boundary = log ? 0 : Float::INFINITY
65
+ mc = multicolor?
66
+ expanded = x_expanded?
67
+ while count < MAX_SPAN
68
+ if pos >= boundary
69
+ log.advance(pos)
70
+ expanded = log[0x1d].anybits?(@bit)
71
+ mc = logged_multicolor(log, mc, expanded, count)
72
+ boundary = log.next_x
73
+ end
74
+ count.zero? ? prime(mc) : step(mc, expanded, count == cut - 1)
75
+ break if @latch.zero? && @sr.zero?
76
+
77
+ codes[count] = @latch
78
+ count += 1
79
+ pos += 1
80
+ end
81
+ count
82
+ end
83
+
84
+ # The X match loads the shift register and arms both flip-flops, so
85
+ # the sprite's first pixel comes straight off the fetched row.
86
+ def prime(multicolor)
87
+ @mc_flop = @new_chip && !multicolor
88
+ @xe_flop = false
89
+ @latch = (@sr >> 22) & (multicolor ? 3 : 2)
90
+ end
91
+
92
+ # The multicolor bit as the log has it at the pixel. A change after
93
+ # the first pixel reaches the 8565's flip-flop.
94
+ def logged_multicolor(log, was, expanded, count)
95
+ multicolor = log[0x1c].anybits?(@bit)
96
+ switch_multicolor(multicolor, expanded) if @new_chip && multicolor != was && count.positive?
97
+ multicolor
98
+ end
99
+
100
+ # On the 8565 a multicolor change works on the flip-flop instead of
101
+ # the next pixels. It stays as it is unless the change lands between
102
+ # the two halves of an expanded pixel, where it flips, or is set by a
103
+ # switch to hi-res.
104
+ def switch_multicolor(multicolor, expanded)
105
+ return unless expanded && !@xe_flop
106
+
107
+ @mc_flop = multicolor ? !@mc_flop : true
108
+ end
109
+
110
+ # One pixel of the sequencer. An unexpanded sprite shifts every pixel;
111
+ # an expanded one shifts every other, and the multicolor latch reloads
112
+ # every other shift, so its pairs stretch with the expansion. Both
113
+ # flip-flops sit idle while their register bit is clear, which is why
114
+ # the first pixel after a mid-sprite change repeats the last one.
115
+ def step(multicolor, expanded, last)
116
+ shift = true
117
+ if expanded
118
+ shift = @xe_flop
119
+ @xe_flop = !@xe_flop
120
+ else
121
+ @xe_flop = false
122
+ end
123
+ @mc_flop = false unless multicolor || @new_chip
124
+ return unless shift
125
+
126
+ @sr = (@sr << 1) & 0xffffff
127
+ load_latch(multicolor, last)
128
+ end
129
+
130
+ # A multicolor pair loaded on the last pixel before the reload keeps
131
+ # only its high bit, as a hi-res pixel does (spritefetchbug).
132
+ # On the 8565 a hi-res pixel with the multicolor flip-flop clear
133
+ # sets it and loads nothing, so the latch repeats its last pixel.
134
+ def load_latch(multicolor, last)
135
+ if multicolor
136
+ @latch = (@sr >> 22) & (last ? 2 : 3) if @mc_flop
137
+ @mc_flop = !@mc_flop
138
+ elsif @new_chip && !@mc_flop
139
+ @mc_flop = true
140
+ else
141
+ @latch = (@sr >> 22) & 2
142
+ end
143
+ end
144
+ end
145
+ end
146
+ end
147
+ end
148
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ module Chips
5
+ class VIC
6
+ class Sprite
7
+ # = Timing
8
+ #
9
+ # Where a region puts the sprites in the line: the cycles of their
10
+ # fetches and of the display compare, the pixels those land on, and
11
+ # the raster pixels the X counter reads each coordinate at. Where the
12
+ # fetches or the compare run a cycle later than on the 6569, what
13
+ # hangs off them moves 8 pixels with them.
14
+ class Timing
15
+ # The X counter's value where it steps back on a raster of more than
16
+ # 512 pixels, to run over the eight values below it again.
17
+ HOLD_AT = 0x188
18
+
19
+ attr_reader :columns, :sprite_cycle, :display_off_x, :x_pixels
20
+
21
+ def initialize(region)
22
+ @columns = region.cycles_per_line
23
+ @sprite_cycle = region.sprite_cycle
24
+ @shift = region.sprite_cycle - Region::PAL.sprite_cycle
25
+ @display_off_x = DISPLAY_OFF_X + (8 * (region.sprite_display_cycle - Region::PAL.sprite_display_cycle))
26
+ @x_pixels = Timing.x_pixels(@columns * 8, region.x_hold)
27
+ end
28
+
29
+ # The column BA falls in for a sprite's own fetch: 55 for sprite 0 on
30
+ # the 6569, and two later for each sprite after it.
31
+ def ba_column(index) = 55 + @shift + (2 * index)
32
+
33
+ # The raster pixel of a sprite's reload, counted from the start of
34
+ # the line it is fetched on, so past the end of it for sprites 3-7
35
+ # on the 6569.
36
+ def reload_x(index) = RELOAD_X + (8 * @shift) + (RELOAD_STEP * index)
37
+
38
+ # The X counter at a raster pixel: X_OFFSET behind it, wrapping at
39
+ # the end of the line, less the pixels it held for from HOLD_AT on,
40
+ # where it ran over $180-$187 a second time.
41
+ def self.xpos(pixel, width, hold)
42
+ count = (pixel - X_OFFSET) % width
43
+ count < HOLD_AT ? count : count - hold
44
+ end
45
+
46
+ # The raster pixels at which the X counter reads each coordinate, 512
47
+ # lists. A sprite starts at one where its X matches. On the 6569 the
48
+ # coordinates past 503 have none.
49
+ def self.x_pixels(width, hold)
50
+ pixels = Array.new(512) { [] }
51
+ width.times { |pixel| pixels[xpos(pixel, width, hold)] << pixel }
52
+ pixels.each(&:freeze).freeze
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,337 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "badline/chips/vic/sprite/internal_bus"
4
+ require "badline/chips/vic/sprite/shifter"
5
+ require "badline/chips/vic/sprite/timing"
6
+
7
+ module Badline
8
+ module Chips
9
+ class VIC
10
+ # = Sprite
11
+ #
12
+ # The DMA/display state machine and the pixel sequencer for one sprite.
13
+ # The sequencer is a 24-bit shift register clocked once per pixel: the X
14
+ # comparator starts it, the expansion flip-flop gates the shift, and the
15
+ # multicolor flip-flop gates a two-bit output latch that feeds the color
16
+ # mux. Registers written mid-sprite therefore take hold from the pixel
17
+ # they reach, rather than re-decoding the row.
18
+ class Sprite
19
+ X_OFFSET = 104
20
+
21
+ # MCBASE and MC are six-bit byte counters into the sprite's 64-byte
22
+ # block. A sprite ends when MCBASE reaches exactly 63, which is why a
23
+ # mid-line $d017 write that steps it past 63 keeps it running.
24
+ LAST_MCBASE = 63
25
+ MC_MASK = 0x3f
26
+
27
+ # 24 bits at two pixels each, plus slack for a mid-sprite expansion
28
+ # change stretching the tail.
29
+ MAX_SPAN = 64
30
+
31
+ # Latch codes are the multicolor bit pairs: 0 is transparent, 1 and 3
32
+ # are the shared colors, 2 the sprite's own.
33
+ SHARED0 = 1
34
+ OWN_COLOR = 2
35
+ SHARED1 = 3
36
+
37
+ # The s-accesses reload the shift register partway through the line,
38
+ # at raster pixel 459 for sprite 0 and 16 later for each sprite after
39
+ # it. A sprite still shifting there loses the rest of its row, and the
40
+ # comparator is ignored for the twelve pixels from it, running on into
41
+ # the next line from a reload near the end of this one (the spritescan
42
+ # dumps). Its output holds the last pixel for seven, through K+6
43
+ # (spritefetchbug).
44
+ RELOAD_X = 459
45
+ RELOAD_STEP = 16
46
+ RELOAD_DEAD = 12
47
+ RELOAD_HOLD = 7
48
+
49
+ # On the line of its last row, a sprite whose DMA ended at cycle 16
50
+ # loses its display at cycle 58, so no hit from this pixel on starts
51
+ # it (spritegap3). One already shifting runs on. A region whose
52
+ # display compare falls later moves it with the compare.
53
+ DISPLAY_OFF_X = 460
54
+
55
+ include Shifter
56
+
57
+ attr_reader :index, :leftmost, :span, :codes,
58
+ :reload_leftmost, :reload_span, :reload_codes
59
+
60
+ def initialize(index, registers, bank, bus, model: :mos6569)
61
+ @index = index
62
+ @new_chip = model == :mos8565
63
+ @registers = registers
64
+ @bank = bank
65
+ @bus = bus
66
+ @width = bus.columns * 8
67
+ @timing = bus.timing
68
+ @bit = 1 << index
69
+ @dma = false
70
+ @display_on = false
71
+ @mcbase = 0
72
+ @mc = 0
73
+ @exp_ff = true
74
+ @crunched_mc = nil
75
+ @bits = 0
76
+ @row_ready = false
77
+ @codes = Array.new(MAX_SPAN + RELOAD_HOLD, 0)
78
+ @leftmost = 0
79
+ @span = 0
80
+ @reload_codes = Array.new(MAX_SPAN + RELOAD_HOLD, 0)
81
+ @reload_leftmost = 0
82
+ @reload_span = 0
83
+ reload = @timing.reload_x(index)
84
+ @reload_x = reload % @width
85
+ @reload_next_line = reload < @width
86
+ @sr = 0
87
+ @latch = 0
88
+ @mc_flop = false
89
+ @xe_flop = false
90
+ @first_byte_lost = false
91
+ @blind = false
92
+ @show_from = 0
93
+ end
94
+
95
+ def displaying? = @dma
96
+
97
+ # True when this line has a row for the sequencer: its own, the next
98
+ # one for a sprite that reloads late in the line, or the last one for
99
+ # a sprite that reloads at its start.
100
+ def rendering?
101
+ return true if @row_ready
102
+
103
+ @reload_next_line ? @dma && @display_on : !@prev_bits.nil?
104
+ end
105
+
106
+ def enabled? = @registers[0x15].anybits?(@bit)
107
+ def multicolor? = @registers[0x1c].anybits?(@bit)
108
+ def x_expanded? = @registers[0x1d].anybits?(@bit)
109
+ def y_expanded? = @registers[0x17].anybits?(@bit)
110
+ def priority? = @registers[0x1b].anybits?(@bit)
111
+
112
+ def x
113
+ msb = @registers[0x10].anybits?(@bit) ? 0x100 : 0
114
+ msb | @registers[index * 2]
115
+ end
116
+
117
+ # The Y comparator is eight bits wide, so the PAL lines above 255
118
+ # match the coordinates 0-55 a second time (spritey).
119
+ def y_match?(line) = (line & 0xff) == @registers[(index * 2) + 1]
120
+
121
+ def color = @registers[0x27 + index] & 0x0f
122
+
123
+ # Cycle 16: MCBASE takes MC, which the three s-accesses stepped past
124
+ # the row, but only while the expansion flip-flop is set.
125
+ def advance_mcbase
126
+ mc = @crunched_mc || ((@mc + 3) & MC_MASK)
127
+ @crunched_mc = nil
128
+ @mcbase = mc if @exp_ff && @dma
129
+ end
130
+
131
+ # The end-of-sprite compare, a column after MCBASE moves. MCBASE has
132
+ # to land on 63 exactly, so a crunched sprite steps over it and runs
133
+ # on through the rest of its block. Only the DMA stops here; the
134
+ # display waits for cycle 58.
135
+ def finish_mcbase
136
+ @dma = false if @mcbase == LAST_MCBASE
137
+ end
138
+
139
+ # Clearing MxYE sets the expansion flip-flop at once. Cleared in
140
+ # cycle 15, while the flip-flop is still reset, it also crunches MC:
141
+ # the bits step to a mix of MC and MCBASE (Bauer §3.8, VICE
142
+ # `d017_store`) that cycle 16 then hands to MCBASE.
143
+ def clear_y_expansion(crunch)
144
+ return if @exp_ff
145
+
146
+ @exp_ff = true
147
+ return unless crunch && @dma
148
+
149
+ mc = (@mc + 3) & MC_MASK
150
+ @crunched_mc = (0x2a & @mcbase & mc) | (0x15 & (@mcbase | mc))
151
+ end
152
+
153
+ # Cycle 56, after the second Y compare: MxYE inverts the expansion
154
+ # flip-flop of a sprite with DMA running.
155
+ def toggle_expansion
156
+ @exp_ff = !@exp_ff if @dma && y_expanded?
157
+ end
158
+
159
+ # Cycles 55 and 56: a Y/enable match starts the DMA and rewinds
160
+ # MCBASE, and returns true. Display is enabled separately in cycle 58,
161
+ # so the rows render from the following line on.
162
+ #
163
+ # On the VIC's side, BA falls at the sprite's own column
164
+ # (Timing#ba_column) — 55 for sprite 0 on the 6569, two later for each
165
+ # sprite after it, a column behind the window the CPU sees
166
+ # (VIC#layout_columns) — or two columns after
167
+ # this compare, whichever is later. AEC follows three columns on, and
168
+ # the first of the three s-accesses runs in the column it arrives in:
169
+ # a DMA starting on the second compare therefore loses that access for
170
+ # sprite 0, alone among the eight in following the compares
171
+ # immediately (spriteenable2).
172
+ def check_dma(line, column)
173
+ return false if @dma || !enabled? || !y_match?(line)
174
+
175
+ @first_byte_lost = column + 2 > @timing.ba_column(index)
176
+ @dma = true
177
+ @mcbase = 0
178
+ @exp_ff = true
179
+ end
180
+
181
+ # Cycle 58: MC is reloaded from MCBASE for the coming row, and a
182
+ # sprite with DMA running starts displaying only while MxE and Y still
183
+ # match — a write to either between the compares and here keeps the
184
+ # data fetch running invisibly. The display goes off only here, and
185
+ # only once the DMA has, so a DMA restarted on the line of its last
186
+ # row keeps a display that was already on (spriterestart).
187
+ def check_display(line)
188
+ @mc = @mcbase
189
+ if @dma
190
+ @display_on = true if enabled? && y_match?(line)
191
+ else
192
+ @display_on = false
193
+ end
194
+ blind_fetch if @blind && @display_on
195
+ end
196
+
197
+ # The address of the s-access that falls in phi1, the middle of the
198
+ # three, or nil when the sprite's DMA is off and the VIC idles there.
199
+ def phi1_address
200
+ (pointer * 64) + ((@mc + 1) & MC_MASK) if @dma
201
+ end
202
+
203
+ # The row fetched at the end of the previous line renders on this one.
204
+ # A lost first s-access reads back the $ff the CPU is still driving.
205
+ def start_line
206
+ @span = 0
207
+ @reload_span = 0
208
+ @prev_bits = (@bits if @row_ready && @dma)
209
+ @row_ready = false
210
+ @blind = !@dma && !@reload_next_line
211
+ @show_from = 0
212
+ lost = @first_byte_lost
213
+ @first_byte_lost = false
214
+ return unless @dma && @display_on
215
+
216
+ @bits = row_bits(@mc)
217
+ @bits |= 0xff << 16 if lost
218
+ @row_ready = true
219
+ end
220
+
221
+ # Replay the line through the pixel sequencer, filling the code buffer
222
+ # from the X match on. A log of mid-line register writes makes the
223
+ # comparator and the shift register see the values each pixel was
224
+ # drawn with.
225
+ #
226
+ # The row can be shown once before the reload and once after it, so a
227
+ # sprite moved past the beam can fire a second time on the same line
228
+ # (spritex). A match inside the dead pixels shows nothing.
229
+ def sequence(log = nil)
230
+ @span = 0
231
+ @reload_span = 0
232
+ reload_bits = reload_row
233
+ return unless @row_ready || @prev_bits || reload_bits
234
+
235
+ log = nil if log.nil? || log.empty?
236
+ @stop_x = @dma ? nil : @timing.display_off_x
237
+ comparator_hits(log).each { |start| sequence_hit(log, start, reload_bits) }
238
+ end
239
+
240
+ # The color at a raster position, for a sprite sequenced with the
241
+ # registers as they stand.
242
+ def pixel(raster_x)
243
+ code = code_at(raster_x, @leftmost, @span, @codes) ||
244
+ code_at(raster_x, @reload_leftmost, @reload_span, @reload_codes)
245
+ return nil if code.nil? || code.zero?
246
+
247
+ palette(@registers, Array.new(4, 0))[code]
248
+ end
249
+
250
+ # Fill a four-entry lookup from latch code to color, against a view of
251
+ # the color registers.
252
+ def palette(view, into)
253
+ into[SHARED0] = view[0x25] & 0x0f
254
+ into[OWN_COLOR] = view[0x27 + index] & 0x0f
255
+ into[SHARED1] = view[0x26] & 0x0f
256
+ into
257
+ end
258
+
259
+ private
260
+
261
+ # On the line whose compare started the DMA, sprites 3-7 ran their
262
+ # s-accesses before it was on, so the shift register holds what the
263
+ # VIC saw on its bus instead of a row, and a hit from the display
264
+ # turning on shows it (sbsprf24).
265
+ def blind_fetch
266
+ @bits = @bus.row(index)
267
+ @row_ready = true
268
+ @show_from = @timing.display_off_x
269
+ end
270
+
271
+ def code_at(raster_x, leftmost, span, codes)
272
+ dist = raster_x - leftmost
273
+ dist += @width if dist.negative?
274
+ codes[dist] if dist < span
275
+ end
276
+
277
+ def sequence_hit(log, start, reload_bits)
278
+ return if (@stop_x && start >= @stop_x) || start < @show_from || start < @reload_x + RELOAD_DEAD - @width
279
+
280
+ if start < @reload_x
281
+ sequence_early(log, start)
282
+ elsif start >= @reload_x + RELOAD_DEAD
283
+ sequence_reloaded(log, start, reload_bits)
284
+ end
285
+ end
286
+
287
+ def sequence_early(log, start)
288
+ bits = early_row
289
+ return unless bits && @span.zero?
290
+
291
+ @leftmost = start
292
+ cut = reload_cut(start)
293
+ @span = cut_at_reload(cut, run(log, start, @codes, bits, cut), @codes)
294
+ end
295
+
296
+ def sequence_reloaded(log, start, bits)
297
+ return unless bits && @reload_span.zero?
298
+
299
+ @reload_leftmost = start
300
+ cut = reload_cut(start)
301
+ @reload_span = cut_at_reload(cut, run(log, start, @reload_codes, bits, cut), @reload_codes)
302
+ end
303
+
304
+ # The row in the shift register ahead of the reload: this line's for
305
+ # sprites 0-2, and for the others the one their reload at the start of
306
+ # the last line brought in.
307
+ def early_row = @reload_next_line ? current_row : @prev_bits
308
+
309
+ def current_row = (@bits if @row_ready)
310
+
311
+ # The row the reload brings in. Sprites 0-2 reload late in the line
312
+ # with the next line's row, so one that fires after it shows that row
313
+ # a line early (spritegap); the others reload at the start of the line
314
+ # with the row it already has.
315
+ def reload_row
316
+ return current_row unless @reload_next_line
317
+ return unless @dma && @display_on
318
+
319
+ bits = row_bits(@mc)
320
+ bits |= 0xff << 16 if @first_byte_lost
321
+ bits
322
+ end
323
+
324
+ # The three s-accesses step MC through the sprite's block, wrapping
325
+ # within it.
326
+ def row_bits(counter)
327
+ base = pointer * 64
328
+ (@bank.peek(base + counter) << 16) |
329
+ (@bank.peek(base + ((counter + 1) & MC_MASK)) << 8) |
330
+ @bank.peek(base + ((counter + 2) & MC_MASK))
331
+ end
332
+
333
+ def pointer = @bank.peek(@registers.screen_base + 0x3f8 + index)
334
+ end
335
+ end
336
+ end
337
+ end