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,787 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "badline/chips/vic/bank"
4
+ require "badline/chips/vic/registers"
5
+ require "badline/chips/vic/display_state"
6
+ require "badline/chips/vic/sequencer"
7
+ require "badline/chips/vic/sprites"
8
+ require "badline/chips/vic/keyed_parts"
9
+
10
+ module Badline
11
+ module Chips
12
+ class VIC
13
+ include Addressable
14
+ include IntegerHelper
15
+
16
+ # The 6569 of the breadbin C64, the HMOS 8565 of the C64C, and the
17
+ # C128's VIC-IIe, the 8566 (PAL) and 8564 (NTSC). The VIC-IIe draws as
18
+ # the 8565 and adds the $D02F and $D030 registers.
19
+ MODELS = %i[mos6569 mos8565 mos8566 mos8564].freeze
20
+
21
+ # The sixteen colours #display's palette indices stand for, as RGB.
22
+ PALETTE = [
23
+ 0x000000, 0xffffff, 0x924a40, 0x84c5cc,
24
+ 0x9351b6, 0x72b14b, 0x483aaa, 0xd5df7c,
25
+ 0x675200, 0xc33d00, 0xc18178, 0x606060,
26
+ 0x8a8a8a, 0xb3ec91, 0x867ade, 0xb3b3b3
27
+ ].freeze
28
+
29
+ # The sixteen colours' luminances, out of 32, as Philip Timmermann
30
+ # (Pepto) measured them on the 6569.
31
+ LUMINANCES = [0, 32, 10, 20, 12, 16, 8, 24, 12, 8, 16, 10, 15, 24, 15, 20].freeze
32
+
33
+ # The sixteen colours on a green monochrome monitor, which shows only
34
+ # their luminance, as the PET 64's does.
35
+ GREEN_PALETTE = LUMINANCES.map do |luminance|
36
+ green = luminance * 255 / 32
37
+ ((green / 4) << 16) | (green << 8) | (green / 4)
38
+ end.freeze
39
+
40
+ attr_reader :display, :width, :height, :vic_bank, :column,
41
+ :rasterline, :dirty_lines, :model, :region
42
+
43
+ # The RGB colours #display's palette indices stand for: PALETTE, or
44
+ # GREEN_PALETTE on a monochrome monitor.
45
+ attr_accessor :palette
46
+
47
+ # The line of #display the beam draws, which is the raster line unless
48
+ # the VIC-IIe's TEST bit has moved the raster counter on since the
49
+ # last vertical sync (#test_step!).
50
+ attr_reader :output_line
51
+
52
+ # The parts a VICE snapshot reads and sets.
53
+ attr_reader :registers, :display_state, :sequencer, :sprites, :character_buffer, :color_buffer,
54
+ :fetch_d011
55
+
56
+ # Returns the byte the CPU's halted read would see, for the c-accesses
57
+ # that run before AEC. Without it they read colour RAM.
58
+ attr_writer :open_bus
59
+
60
+ LIGHTPEN_IRQ = 0x08 # $D019 latch bit
61
+ COLOR_REGISTERS = 0x20..0x2e
62
+
63
+ # What the 8565 shows on the pixel before a color register write takes
64
+ # hold, where the 6569 shows the old color: the grey dot.
65
+ GREY_DOT = 0x0f
66
+
67
+ # A g-access reaches the pixel output this many columns after it runs.
68
+ GRAPHICS_DELAY = 2
69
+
70
+ # What a column's g-access passed to the sequencer.
71
+ G_BLANK = 0
72
+ G_IDLE = 1
73
+ G_DISPLAY = 2
74
+
75
+ # The idle g-access in the column a mid-row bad line opens display
76
+ # state reads here on a 6569.
77
+ DMA_DELAY_IDLE_ADDRESS = 0x38ff
78
+
79
+ # The $d011 mode bits a g-access still sees for a cycle after they fall:
80
+ # BMM always, and ECM too when the access it leaves read the character
81
+ # ROM.
82
+ FETCH_HOLD = 0x20
83
+ FETCH_HOLD_ROM = 0x60
84
+
85
+ # The hooks a column can carry, as bits: MCBASE, the left border
86
+ # compare with the end of the sprite, the sprite DMA compare, the
87
+ # expansion flip-flop, the sprite display compare, and the start of the
88
+ # vertical border for the line after the last column.
89
+ HOOK_MCBASE = 1
90
+ HOOK_LEFT = 2
91
+ HOOK_DMA = 4
92
+ HOOK_EXPANSION = 8
93
+ HOOK_DISPLAY = 16
94
+ HOOK_LAST = 32
95
+
96
+ # The vertical sync starts this many lines into the vertical blank,
97
+ # and lasts VSYNC_LINES.
98
+ VSYNC_DELAY = 3
99
+ VSYNC_LINES = 3
100
+
101
+ # On the 6569 the DMA compares fall in columns 53 and 54 and the
102
+ # display compare in column 57. A region whose sprite fetches run later
103
+ # moves them with the fetches (Region::Profile#sprite_cycle).
104
+ DMA_COLUMN = 53
105
+ EXPANSION_COLUMN = 54
106
+
107
+ # BA falls three columns ahead of each sprite's pair of s-accesses, and
108
+ # the five-column windows step two columns apart from sprite 0 at column
109
+ # 54 on the 6569, a column later where the fetches are. From sprite 3 on
110
+ # they reach past the end of the line, so each is split: the tail
111
+ # columns fall on the line whose DMA compare started the fetch, the head
112
+ # columns on the line after it.
113
+ SPRITE_BA_FIRST = 54
114
+ SPRITE_BA_LENGTH = 5
115
+
116
+ def initialize(debug: false, model: :mos6569, region: Region::PAL)
117
+ raise ArgumentError, "unknown VIC-II model #{model}" unless MODELS.include?(model)
118
+
119
+ addressable_at(0xd000, length: 2**10)
120
+ @model = model
121
+ @iie = %i[mos8566 mos8564].include?(model)
122
+ @core = model == :mos6569 ? :mos6569 : :mos8565
123
+ @region = region
124
+ @palette = PALETTE
125
+ @lightpen_extra = @core == :mos8565 ? 1 : 2
126
+ @grey_dots = @core == :mos8565
127
+ @delayed_fetch = @core == :mos8565
128
+ @dma_delay_idle = @core == :mos6569
129
+ @bank_swaps = @core == :mos8565
130
+ @vic_bank = VIC::Bank.new(self)
131
+ @debug = debug
132
+
133
+ @columns_per_line = region.cycles_per_line
134
+ @sprite_cycle = region.sprite_cycle
135
+ @sprite_shift = region.sprite_cycle - Region::PAL.sprite_cycle
136
+ @width = @columns_per_line * 8
137
+ @height = region.lines_per_frame
138
+ @last_column = @columns_per_line - 1
139
+ @last_line = @height - 1
140
+ @vsync_line = region.vblank[0] + VSYNC_DELAY
141
+ layout_columns
142
+ @blank_columns = Array.new(@columns_per_line) { |column| Region.blanked?(column, region.hblank) }.freeze
143
+ @blank_lines = Array.new(@height) { |line| Region.blanked?(line, region.vblank) }.freeze
144
+ @display = Array.new(@width * @height, 0)
145
+ @lines = Array.new(@height) { Array.new(@width, 0) }
146
+ @dirty_lines = Array.new(@height, true)
147
+ @render = true
148
+
149
+ power_on!
150
+ end
151
+
152
+ # The VIC has no reset pin, so only a power cycle brings back the
153
+ # registers, the raster position and the fetch and sprite state it
154
+ # starts with. The display keeps its buffers, cleared to black.
155
+ def power_on!
156
+ @registers = VIC::Registers.new(iie: @iie)
157
+ @register_bytes = @registers.bytes
158
+ @display_state = VIC::DisplayState.new(@registers, @last_column)
159
+ @sequencer = VIC::Sequencer.new(@width, @registers, @vic_bank, model: @core, region: @region)
160
+ @sequencer.render = @render
161
+ @sprites = VIC::Sprites.new(@registers, @vic_bank, @width, model: @core, region: @region)
162
+ @display.fill(0)
163
+ @lines.each { |line| line.fill(0) }
164
+ @dirty_lines.fill(true)
165
+
166
+ @column = 0
167
+ @rasterline = 0
168
+ @output_line = 0
169
+
170
+ @character_buffer = Array.new(40, 0)
171
+ @color_buffer = Array.new(40, 0)
172
+ @sprite_ba = Array.new(@width / 8, false)
173
+ @g_tick = 0
174
+ @g_kind = Array.new(4, G_BLANK)
175
+ @g_kept_char = 0
176
+ @g_kept_color = 0
177
+ @g_data = @sequencer.ring_data
178
+ @g_char = @sequencer.ring_char
179
+ @g_color = @sequencer.ring_color
180
+ @g_display = false
181
+ @fetch_d011 = @register_bytes[0x11]
182
+ @lp_triggered = false
183
+ @lp_low = false
184
+ @raster_match = false
185
+ @test_wrap = false
186
+ end
187
+
188
+ def cycle!
189
+ start_line! if @column.zero?
190
+
191
+ # The g-access runs in the first half of the cycle, ahead of the bad
192
+ # line compare, and the c-access in the second half, after it.
193
+ draw!
194
+ dma_delay_idle_access if @display_state.cycle(@rasterline, @column)
195
+
196
+ fetch_character_data! if dma_active?
197
+
198
+ hooks = @hook_columns[@column]
199
+ column_hooks(hooks) if hooks
200
+
201
+ @column += 1
202
+ if @column == @columns_per_line
203
+ finish_line!
204
+ @column = 0
205
+ step_output_line
206
+ @rasterline = @rasterline == @last_line ? 0 : @rasterline + 1
207
+ check_raster_irq!(@rasterline.zero? ? @last_line : @rasterline)
208
+ end
209
+ watch_collisions
210
+ nil
211
+ end
212
+
213
+ # The sprite and border hooks that fall on named cycles, one column
214
+ # ahead of Bauer's numbering — two for MCBASE, the DMA compares and the
215
+ # expansion flip-flop, which the `spriteenable` and `spritecrunch`
216
+ # references place a column earlier still. Guarded in
217
+ # #cycle! so an ordinary column pays an array read rather than the
218
+ # dispatch.
219
+ def column_hooks(hooks)
220
+ @sprites.advance_mcbase if hooks.anybits?(HOOK_MCBASE)
221
+ if hooks.anybits?(HOOK_LEFT)
222
+ @sequencer.left_compare_vertical_border
223
+ finish_sprite_mcbase
224
+ end
225
+ check_sprite_dma if hooks.anybits?(HOOK_DMA)
226
+ @sprites.toggle_expansion if hooks.anybits?(HOOK_EXPANSION)
227
+ @sprites.check_display(@rasterline) if hooks.anybits?(HOOK_DISPLAY)
228
+ @sequencer.start_vertical_border(@rasterline == @last_line ? 0 : @rasterline + 1) if hooks.anybits?(HOOK_LAST)
229
+ end
230
+
231
+ # Latches the sprite collisions up to the beam, as a read of $D01E or
232
+ # $D01F would, so the registers stand as the hardware has them when a
233
+ # snapshot is saved.
234
+ def fold_collisions! = @sprites.collide_upto(@column * 8, @sequencer.fg)
235
+
236
+ # The IRQ line is held asserted while any enabled latch bit is set in
237
+ # $D019/$D01A, until the program acknowledges it by writing to $D019.
238
+ def interrupted?
239
+ @registers.irq_line?
240
+ end
241
+
242
+ # The VIC-IIe's $D02F lines K0-K2 and $D030 bits, for the machine to
243
+ # read. The 6569 and 8565 give 7, false and false.
244
+ def extra_keyboard_lines = @registers.extra_keyboard_lines
245
+ def fast? = @registers.fast?
246
+ def test? = @registers.test?
247
+
248
+ # $D030's FAST and TEST bits as bits 0 and 1, which the C128 reads once
249
+ # a cycle.
250
+ def clock_bits = @registers.clock_bits
251
+
252
+ # Whether the cycle just run was one of the five refresh cycles, Bauer's
253
+ # 11-15, whose phi1 half the VIC keeps in the VIC-IIe's FAST mode.
254
+ # @column has already advanced, so it is the Bauer cycle less one.
255
+ def refresh_cycle? = @column.between?(10, 14)
256
+
257
+ # In the VIC-IIe's FAST mode the 8502 drives the bus in both halves of
258
+ # the cycle, and the VIC's accesses in the cycle just run latched what
259
+ # it put there. The g-access, or the idle access, took the byte of
260
+ # phi1. A c-access took the byte of phi2 as one made before AEC does
261
+ # (#fetch_character_data!): $ff for the video matrix, unless the CPU
262
+ # was reading or writing the VIC's own registers, and the byte's low
263
+ # nibble for colour. AEC can't follow BA down while the CPU runs at
264
+ # 2 MHz, so on a bad line it falls three cycles after FAST mode ends.
265
+ def take_cpu_bus(phi1, phi2, register_access)
266
+ slot = @g_tick
267
+ @g_data[slot] = phi1 unless @g_kind[slot] == G_BLANK
268
+ display_state = @display_state
269
+ display_state.keep_bus(@column)
270
+ return unless display_state.fetching?(@column.zero? ? @last_column : @column - 1)
271
+
272
+ vmli = display_state.vmli
273
+ @character_buffer[vmli] = register_access ? phi2 : 0xff
274
+ @color_buffer[vmli] = phi2 & 0x0f
275
+ end
276
+
277
+ # The VIC-IIe's TEST bit clocks the raster counter in every cycle, and
278
+ # the C128 calls this ahead of each cycle the bit is set in. The line's
279
+ # last cycle, whose own step it falls in with, adds nothing more. The
280
+ # counter takes two steps from the frame's last line to line 0, as line
281
+ # 0 reads the last line for its first cycle.
282
+ def test_step!
283
+ return if @column == @last_column
284
+
285
+ if @rasterline == @last_line
286
+ @test_wrap = !@test_wrap
287
+ return if @test_wrap
288
+
289
+ @rasterline = 0
290
+ @display_state.new_frame
291
+ else
292
+ @test_wrap = false
293
+ @rasterline += 1
294
+ end
295
+ @display_state.raster_step(@rasterline)
296
+ check_raster_irq!
297
+ end
298
+
299
+ def peek(addr)
300
+ i = offset_of(addr) % (2**6)
301
+ case i
302
+ when 0x11 then (@registers[0x11] & 0x7f) | ((raster_counter & 0x100) >> 1)
303
+ when 0x12 then raster_counter & 0xff
304
+ when 0x19 then irq_status # Latch + master IRQ bit, unused bits read 1
305
+ when 0x1e, 0x1f then read_collision(i)
306
+ else @registers.read(i)
307
+ end.tap { |value| @sprites.bus_data(@column, value) }
308
+ end
309
+
310
+ # The register file as stored, read without a bus access's side effects.
311
+ def register_file = Array.new(0x40) { |reg| @registers[reg] }
312
+
313
+ def poke(addr, value)
314
+ reg = offset_of(addr) % (2**6)
315
+ @sprites.bus_data(@column, value)
316
+ log_register_change(reg, value)
317
+ @registers.write(reg, value)
318
+ compare_raster_writes(reg)
319
+ end
320
+
321
+ def position
322
+ (@rasterline * @width) + (@column * 8)
323
+ end
324
+
325
+ def dma_active?
326
+ @display_state.fetching?(@column)
327
+ end
328
+
329
+ # Asked once the VIC has advanced, so @column is one ahead of the cycle
330
+ # the CPU is about to run. A bad line holds BA on the condition as it
331
+ # stands rather than the latched match.
332
+ def ba_low?
333
+ return true if @sprite_ba[@column]
334
+
335
+ @display_state.bad_line_condition? &&
336
+ @column > DisplayState::DMA_FIRST && @column <= DisplayState::DMA_LAST + 1
337
+ end
338
+
339
+ # Whether this is the first cycle of sprite 0's BA window on the line
340
+ # whose raster matches its Y, where the sprite's DMA starts. An REU
341
+ # that moved a byte on the cycle before doesn't see BA fall here. Pinned by
342
+ # REU/bonzai/spritetiming.
343
+ def reu_ba_late?
344
+ @column == SPRITE_BA_FIRST + @sprite_shift && sprite_zero_starting?
345
+ end
346
+
347
+ # Whether BA passes straight from a bad line's window to sprite 0's on
348
+ # this cycle, the first of sprite 0's. An REU sees the bad line's DMA
349
+ # end here. Pinned by REU/reutiming2/e4-m2 and e6-m2.
350
+ def reu_ba_handed_on?
351
+ @column == DisplayState::DMA_LAST + 2 && @sprite_ba[@column] && @display_state.bad_line_condition?
352
+ end
353
+
354
+ # Light pen input level (CIA1 PB4). A falling edge triggers the latch.
355
+ def lightpen_level(high)
356
+ @lp_low = !high
357
+ trigger_lightpen if @lp_low
358
+ end
359
+
360
+ # The first trigger per frame latches the beam position into LPX/LPY and
361
+ # raises the LP IRQ. The latch happens one cycle after the edge, with
362
+ # the model's extra half-pixels, two on the 6569 and one on the 8565; a
363
+ # trigger on the last line is consumed without latching unless it lands
364
+ # on the line's first cycle.
365
+ def trigger_lightpen
366
+ return if @lp_triggered
367
+
368
+ @lp_triggered = true
369
+ column = @column + 1
370
+ line = @rasterline
371
+ if column == @columns_per_line
372
+ column = 0
373
+ line = line == @last_line ? 0 : line + 1
374
+ end
375
+ return if line == @last_line && column.positive?
376
+
377
+ vic_x = Sprite::Timing.xpos(column * 8, @width, @region.x_hold)
378
+ latch_lightpen((vic_x >> 1) + @lightpen_extra, line)
379
+ end
380
+
381
+ # The byte the VIC fetched in the phi1 half of the cycle the CPU is
382
+ # running, worked out from the VIC's state. The CPU cycle after column
383
+ # c is Bauer cycle c + 2, and each Bauer cycle has a fixed access (VICE
384
+ # `cycle_phi1_fetch`).
385
+ def phi1_data
386
+ vic_bank.peek(phi1_address)
387
+ end
388
+
389
+ def hblank? = @blank_columns[@column]
390
+ def vblank? = @blank_lines[@rasterline]
391
+ def blanking? = @blank_lines[@rasterline] || @blank_columns[@column]
392
+
393
+ def render? = @render
394
+
395
+ # Whether finished lines are painted into #display. A headless run that
396
+ # only reads the machine's state can turn it off: the sprite
397
+ # collisions, lightpen and every register still behave the same, but
398
+ # #display keeps the last lines painted. Turned back on, it is whole
399
+ # again from the start of the next frame.
400
+ def render=(on)
401
+ @render = on
402
+ @sequencer.render = on
403
+ end
404
+
405
+ # Puts the beam at the start of `line`.
406
+ def restore_line(line)
407
+ @rasterline = @output_line = line
408
+ @column = 0
409
+ end
410
+
411
+ # The latches a VICE snapshot holds: the raster match, the light pen's
412
+ # line held low and its trigger, as bits 0, 1 and 2.
413
+ def latch_bits = (@raster_match ? 1 : 0) | (@lp_low ? 2 : 0) | (@lp_triggered ? 4 : 0)
414
+
415
+ # Sets the latches from latch_bits, and the $D011 the next g-access
416
+ # holds.
417
+ def restore_latches(bits, fetch_d011)
418
+ @raster_match = bits.anybits?(1)
419
+ @lp_low = bits.anybits?(2)
420
+ @lp_triggered = bits.anybits?(4)
421
+ @fetch_d011 = fetch_d011
422
+ end
423
+
424
+ # Marks the columns BA is low for the sprites whose DMA runs.
425
+ def rebuild_sprite_ba
426
+ @sprite_ba.fill(false)
427
+ 8.times do |n|
428
+ next unless @sprites[n].displaying?
429
+
430
+ @sprite_ba_tail[n].each { |c| @sprite_ba[c] = true }
431
+ @sprite_ba_head[n].each { |c| @sprite_ba[c] = true }
432
+ end
433
+ end
434
+
435
+ # Reset the dirty flags once the frontend has consumed them.
436
+ def clear_dirty_lines!
437
+ @dirty_lines.fill(false)
438
+ end
439
+
440
+ private
441
+
442
+ # The raster counter as $D011/$D012 read it. Every line advances on the
443
+ # CPU cycle paired with column 62 except line 0, which the counter
444
+ # reaches a cycle later, together with the line 0 raster compare.
445
+ def raster_counter
446
+ @column.zero? && @rasterline.zero? ? @last_line : @rasterline
447
+ end
448
+
449
+ # Mid-line writes to the color and sprite registers are logged against
450
+ # the cycle after the write (the CPU runs after the VIC within a machine
451
+ # cycle, so @column already points there); each register adds its own
452
+ # pixel delay on top. On the 8565 a color register write that leaves
453
+ # the value as it was still shows its grey dot.
454
+ def log_register_change(reg, value)
455
+ old = @registers[reg]
456
+ return if old == value && !grey_dot?(reg)
457
+
458
+ if (0x20..0x24).cover?(reg)
459
+ @sequencer.colors_changed! unless reg == 0x20
460
+ @sequencer.color_patches.log(reg, old, value, @column * 8) if @render && !blanking?
461
+ else
462
+ log_sprite_change(reg, old, value)
463
+ end
464
+ end
465
+
466
+ def grey_dot?(reg) = @grey_dots && COLOR_REGISTERS.cover?(reg)
467
+
468
+ # A sprite color shows its grey dot as a write of light grey on the
469
+ # pixel before the new color.
470
+ def log_sprite_change(reg, old, value)
471
+ beam_x = @column * 8
472
+ @sprites.log_change(reg, old, GREY_DOT, beam_x - 1) if grey_dot?(reg)
473
+ @sprites.log_change(reg, old, value, beam_x)
474
+ end
475
+
476
+ # On the 6569 Bauer cycles 1-10 and 58-63 are sprite p- and s-accesses,
477
+ # 11-15 refresh, 16-55 g-accesses and 56-57 idle. Where the sprite
478
+ # fetches start later, the idle cycles before them grow, and on the
479
+ # 6567R8 cycle 10 idles too. @column has already advanced, so it is the
480
+ # Bauer cycle less one.
481
+ def phi1_address
482
+ cycle = @column + 1
483
+ slot = (cycle - @sprite_cycle) % @columns_per_line
484
+ if slot < 16
485
+ sprite_phi1_address(slot)
486
+ elsif cycle.between?(11, 15)
487
+ 0x3f00 | ((0xff - (5 * @rasterline) - (cycle - 11)) & 0xff)
488
+ elsif cycle.between?(16, 55)
489
+ graphics_phi1_address
490
+ else
491
+ 0x3fff
492
+ end
493
+ end
494
+
495
+ # Each sprite takes two cycles from sprite 0's, the p-access and then
496
+ # the middle s-access.
497
+ def sprite_phi1_address(slot)
498
+ n = slot >> 1
499
+ return @registers.screen_base + 0x3f8 + n if slot.even?
500
+
501
+ @sprites[n].phi1_address || 0x3fff
502
+ end
503
+
504
+ # The g-access of the column just run. In display state it used the
505
+ # VC and VMLI it then stepped past.
506
+ def graphics_phi1_address
507
+ return @registers.ecm.nonzero? ? 0x39ff : 0x3fff unless @g_display
508
+
509
+ graphics_address(@registers[0x11], @display_state.vmli - 1, (@display_state.vc - 1) & 0x3ff)
510
+ end
511
+
512
+ # A $d011/$d012 write is compared in the next column. A write in the
513
+ # line's second last column is left to its last, which compares the
514
+ # next line.
515
+ def compare_raster_writes(reg)
516
+ return unless (0x11..0x12).cover?(reg)
517
+ return if @column == @last_column
518
+
519
+ check_raster_irq!
520
+ @sequencer.compare_vertical_border(@rasterline) if reg == 0x11
521
+ end
522
+
523
+ def check_sprite_dma
524
+ rebuild_sprite_ba if @sprites.check_dma(@rasterline, @column)
525
+ end
526
+
527
+ # A sprite whose DMA ends here fetches nothing at the end of this line,
528
+ # so its BA tail goes too.
529
+ def finish_sprite_mcbase
530
+ @sprites.finish_mcbase
531
+ rebuild_sprite_ba if @sprites.stopped_dma?
532
+ end
533
+
534
+ # The trigger re-arms at the start of each frame; if the pen line is
535
+ # still low, the latch retriggers immediately with a fixed LPX of $d1.
536
+ def start_lightpen_frame
537
+ @lp_triggered = false
538
+ return unless @lp_low
539
+
540
+ @lp_triggered = true
541
+ latch_lightpen(0xd1, 0)
542
+ end
543
+
544
+ def latch_lightpen(lpx, line)
545
+ @registers.write(0x13, lpx & 0xff)
546
+ @registers.write(0x14, line & 0xff)
547
+ @registers.latch_irq!(LIGHTPEN_IRQ)
548
+ end
549
+
550
+ def sprite_zero_starting?
551
+ @registers[0x15].anybits?(0x01) && @registers[0x01] == (@rasterline & 0xff)
552
+ end
553
+
554
+ # Splits the sprite BA windows at the end of the line and marks the hook
555
+ # columns, for a line of the region's length: 14, 15, 53, 54 and 57, and
556
+ # the line's last column, which starts the vertical border for the line
557
+ # after it. The marks let an ordinary column cost one array read instead
558
+ # of the dispatch.
559
+ def layout_columns
560
+ last = @last_column
561
+ windows = Array.new(8) do |n|
562
+ first = SPRITE_BA_FIRST + @sprite_shift + (2 * n)
563
+ first...(first + SPRITE_BA_LENGTH)
564
+ end
565
+ @sprite_ba_tail = windows.map { |w| w.select { |c| c <= last } }.freeze
566
+ @sprite_ba_head = windows.map { |w| w.filter_map { |c| c - last - 1 if c > last } }.freeze
567
+ @hook_columns = layout_hooks.map { |hooks| hooks unless hooks.zero? }.freeze
568
+ end
569
+
570
+ def layout_hooks
571
+ hooks = Array.new(@columns_per_line, 0)
572
+ hooks[14] |= HOOK_MCBASE
573
+ hooks[15] |= HOOK_LEFT
574
+ hooks[DMA_COLUMN + @sprite_shift] |= HOOK_DMA
575
+ hooks[DMA_COLUMN + @sprite_shift + 1] |= HOOK_DMA
576
+ hooks[EXPANSION_COLUMN] |= HOOK_EXPANSION
577
+ hooks[@region.sprite_display_cycle - 1] |= HOOK_DISPLAY
578
+ hooks[@last_column] |= HOOK_LAST
579
+ hooks
580
+ end
581
+
582
+ # Runs this column's g-access and draws the one from GRAPHICS_DELAY
583
+ # columns earlier. The access reads its byte now, through the mode,
584
+ # $d018 and bank of this cycle. A column with no g-access, or one made
585
+ # while the vertical border flip-flop is set, latches nothing: it passes
586
+ # on zero data with the screen byte and colour the last g-access
587
+ # latched, which an idle g-access clears.
588
+ def draw!
589
+ @vic_bank.sample_lines if @bank_swaps
590
+ slot = @g_tick = (@g_tick + 1) & 3
591
+ display_state = @display_state
592
+ access = display_state.graphics_column?(@column) && !@sequencer.vertical_closed?
593
+ if access
594
+ latch_graphics(slot, display_state)
595
+ else
596
+ @g_kind[slot] = G_BLANK
597
+ @g_data[slot] = 0
598
+ @g_char[slot] = @g_kept_char
599
+ @g_color[slot] = @g_kept_color
600
+ end
601
+ @g_display = display_state.display?
602
+ display_state.graphics_access(@column) if @g_display
603
+ emit_graphics((slot - GRAPHICS_DELAY) & 3, @column - 16) unless blanking?
604
+ @sequencer.latch_xscroll(@register_bytes[0x16] & 0b111) if access
605
+ @fetch_d011 = @register_bytes[0x11]
606
+ end
607
+
608
+ def latch_graphics(slot, display_state)
609
+ unless display_state.display?
610
+ @g_kind[slot] = G_IDLE
611
+ @g_data[slot] = vic_bank.peek(@registers.ecm.zero? ? 0x3fff : 0x39ff)
612
+ @g_char[slot] = @g_color[slot] = @g_kept_char = @g_kept_color = 0
613
+ return
614
+ end
615
+
616
+ vmli = display_state.vmli
617
+ @g_kind[slot] = G_DISPLAY
618
+ @g_data[slot] = fetch_graphics(vmli, display_state.vc)
619
+ @g_char[slot] = @g_kept_char = @character_buffer[vmli] || 0
620
+ @g_color[slot] = @g_kept_color = @color_buffer[vmli] || 1
621
+ end
622
+
623
+ # On the 6569 a bad line that opens display state mid-row (DMA delay)
624
+ # moves the idle g-access of the column that triggers it from $3fff (or
625
+ # $39ff) to DMA_DELAY_IDLE_ADDRESS, unless YSCROLL is 0 (vsp-tester,
626
+ # colorfetchbug/main).
627
+ def dma_delay_idle_access
628
+ slot = @g_tick
629
+ return unless @dma_delay_idle && @g_kind[slot] == G_IDLE
630
+ return if @registers.yscroll.zero?
631
+
632
+ @g_data[slot] = vic_bank.peek(DMA_DELAY_IDLE_ADDRESS)
633
+ end
634
+
635
+ # The 6569's g-access sees a mode bit that falls a cycle late: it
636
+ # addresses with $d011 as this column has it, OR-ed with the held bits
637
+ # the column before had. When BMM changes and the access moves from RAM
638
+ # onto the character ROM, the low address byte still comes from the old
639
+ # mode. The 8565's addresses with $d011 as the column before had it
640
+ # (VICE x64sc `vicii_fetch_graphics`).
641
+ def fetch_graphics(vmli, counter)
642
+ d011 = @register_bytes[0x11]
643
+ last = @fetch_d011
644
+ return vic_bank.peek(graphics_address(d011, vmli, counter)) if d011 == last
645
+ return vic_bank.peek(graphics_address(last, vmli, counter)) if @delayed_fetch
646
+
647
+ from = graphics_address(last, vmli, counter)
648
+ from_rom = vic_bank.character_rom?(from)
649
+ address = graphics_address(d011 | (last & (from_rom ? FETCH_HOLD_ROM : FETCH_HOLD)), vmli, counter)
650
+ if (d011 ^ last).anybits?(0x20) && !from_rom
651
+ to = graphics_address(d011, vmli, counter)
652
+ address = (from & 0xff) | (to & 0x3f00) if vic_bank.character_rom?(to)
653
+ end
654
+ vic_bank.peek(address)
655
+ end
656
+
657
+ def graphics_address(d011, vmli, counter)
658
+ rc = @display_state.rc
659
+ case d011 & 0x60
660
+ when 0x00 then @registers.char_base | ((@character_buffer[vmli] || 0) << 3) | rc
661
+ when 0x20 then @registers.bitmap_base | (counter << 3) | rc
662
+ when 0x40 then (@registers.char_base | ((@character_buffer[vmli] || 0) << 3) | rc) & 0x39ff
663
+ else (@registers.bitmap_base | (counter << 3) | rc) & 0x39ff
664
+ end
665
+ end
666
+
667
+ def emit_graphics(slot, col)
668
+ @sequencer.emit(slot, col, @g_kind[slot] == G_DISPLAY)
669
+ end
670
+
671
+ # Fold the rest of the line's sprite collisions in — they latch whether
672
+ # or not there is a line to draw — then composite the active sprites
673
+ # over the finished background and copy the line into the frame display.
674
+ def finish_line!
675
+ return finish_blank_line if vblank? || !@render
676
+
677
+ composite = @sprites.active?
678
+ @sequencer.apply_color_patches
679
+ @sequencer.snapshot_line if composite
680
+ @sprites.finish_line(composite ? @sequencer.colors : nil, @sequencer.fg)
681
+ @sequencer.apply_border if composite
682
+
683
+ colors = @sequencer.colors
684
+ output_line = @output_line
685
+ line = @lines[output_line]
686
+ return if line == colors
687
+
688
+ line[0, @width] = colors
689
+ @display[output_line * @width, @width] = colors
690
+ @dirty_lines[output_line] = true
691
+ end
692
+
693
+ # A blanked line paints nothing, and is never shown unless the TEST
694
+ # bit has moved the beam off the raster line, where it shows black.
695
+ def finish_blank_line
696
+ @sprites.finish_line(nil, @sequencer.fg)
697
+ return if @output_line == @rasterline || !@render
698
+
699
+ line = @output_line
700
+ @lines[line].fill(0)
701
+ @display.fill(0, line * @width, @width)
702
+ @dirty_lines[line] = true
703
+ end
704
+
705
+ # The display line the beam draws next follows the one before it, and
706
+ # a line that ends in the vertical sync puts it back on the raster
707
+ # line. Only the VIC-IIe's TEST bit moves the raster counter on between
708
+ # the two, so the lines it skips shift the picture up until the next
709
+ # sync.
710
+ def step_output_line
711
+ @output_line = if @rasterline.between?(@vsync_line, @vsync_line + VSYNC_LINES - 1)
712
+ @rasterline + 1
713
+ else
714
+ @output_line == @last_line ? 0 : @output_line + 1
715
+ end
716
+ end
717
+
718
+ def video_matrix(index)
719
+ vic_bank.peek_phi2(@registers.screen_base + index)
720
+ end
721
+
722
+ def start_line!
723
+ if @rasterline.zero?
724
+ @display_state.new_frame
725
+ start_lightpen_frame
726
+ # The line 0 raster compare happens one cycle later than on all
727
+ # other lines (Bauer 3.12).
728
+ check_raster_irq!
729
+ end
730
+ @sequencer.new_line(@rasterline)
731
+ @sprites.start_line
732
+ rebuild_sprite_ba
733
+ @display_state.new_line(@rasterline)
734
+ end
735
+
736
+ # The raster compare latches the flag only as the line and the target
737
+ # come to match, so a target moved along with the raster line holds the
738
+ # match without latching again (VICE x64sc). It runs at each line step
739
+ # and after each $d011/$d012 write. The line asserts via #interrupted?
740
+ # when the mask bit in $D01A is set.
741
+ def check_raster_irq!(line = @rasterline)
742
+ match = line == @registers.raster_target
743
+ @registers.latch_raster_irq! if match && !@raster_match
744
+ @raster_match = match
745
+ end
746
+
747
+ # A collision register carries the pixels drawn up to the cycle before
748
+ # the read — the VIC runs ahead of the CPU within a machine cycle, so
749
+ # the column the read lands on has not latched yet — and the reset the
750
+ # read asserts outlives it, swallowing the pixels drawn under it.
751
+ def read_collision(reg)
752
+ beam_x = @column * 8
753
+ @sprites.collide_upto(beam_x, @sequencer.fg)
754
+ @registers.read(reg).tap { @sprites.clear_collision(reg, beam_x) }
755
+ end
756
+
757
+ # A collision raises its $D019 bit as the beam crosses the pixel rather
758
+ # than when the line is folded at its end, so while an enabled
759
+ # collision IRQ is still unlatched the fold keeps up with the beam.
760
+ def watch_collisions
761
+ return if (@registers[0x1a] & ~@registers[0x19]).nobits?(0x06)
762
+
763
+ @sprites.collide_upto(@column * 8, @sequencer.fg)
764
+ end
765
+
766
+ def irq_status
767
+ @sprites.collide_upto(@column * 8, @sequencer.fg)
768
+ (@registers[0x19] & 0x0f) | 0x70 | (interrupted? ? 0x80 : 0)
769
+ end
770
+
771
+ # A c-access that falls between BA and AEC reads a bus the CPU still
772
+ # drives: the video matrix byte comes back as $ff, and the colour
773
+ # nibble is the low nibble of the byte at the halted CPU's PC.
774
+ def fetch_character_data!
775
+ vmli = @display_state.vmli
776
+ vc = @display_state.vc
777
+ if @display_state.bus_taken?(@column)
778
+ @character_buffer[vmli] = video_matrix(vc)
779
+ @color_buffer[vmli] = vic_bank.peek_color(vc)
780
+ else
781
+ @character_buffer[vmli] = 0xff
782
+ @color_buffer[vmli] = @open_bus ? @open_bus.call & 0x0f : vic_bank.peek_color(vc)
783
+ end
784
+ end
785
+ end
786
+ end
787
+ end