badline 0.12.0 → 0.13.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 (516) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +25 -0
  3. data/CLAUDE.md +59 -31
  4. data/README.md +1 -1
  5. data/doc/library.md +9 -9
  6. data/doc/pinned-behaviour.md +105 -105
  7. data/doc/snapshots.md +1 -4
  8. data/lib/badline/audio/bare_player.rb +4 -4
  9. data/lib/badline/audio/cli.rb +6 -6
  10. data/lib/badline/audio/jukebox.rb +2 -2
  11. data/lib/badline/audio/machine_player.rb +2 -2
  12. data/lib/badline/{media → audio}/queue.rb +1 -1
  13. data/lib/badline/audio/queued_tune.rb +6 -6
  14. data/lib/badline/audio/renderer.rb +5 -5
  15. data/lib/badline/audio/stereo.rb +1 -1
  16. data/lib/badline/c128/{bus_state.rb → bus/saved_state.rb} +6 -6
  17. data/lib/badline/c128/bus.rb +30 -51
  18. data/lib/badline/c128/color_lines.rb +2 -2
  19. data/lib/badline/c128/cpu.rb +1 -1
  20. data/lib/badline/c128/drives.rb +10 -11
  21. data/lib/badline/c128/fast_serial.rb +5 -5
  22. data/lib/badline/c128/mmu_pages.rb +1 -1
  23. data/lib/badline/c128/modes.rb +2 -2
  24. data/lib/badline/c128/saved_state.rb +21 -47
  25. data/lib/badline/c128/z80.rb +2 -2
  26. data/lib/badline/c128.rb +18 -72
  27. data/lib/badline/{computer → c64}/attachments.rb +16 -47
  28. data/lib/badline/c64/banked_ram/banking.rb +32 -0
  29. data/lib/badline/c64/banked_ram/plus256k.rb +56 -0
  30. data/lib/badline/c64/banked_ram/plus60k.rb +45 -0
  31. data/lib/badline/c64/banked_ram/unexpanded.rb +30 -0
  32. data/lib/badline/c64/banked_ram.rb +23 -0
  33. data/lib/badline/c64/bus/extra_sids.rb +32 -0
  34. data/lib/badline/c64/bus/fittings.rb +40 -0
  35. data/lib/badline/c64/bus/pla.rb +149 -0
  36. data/lib/badline/c64/bus/roms.rb +41 -0
  37. data/lib/badline/c64/bus/saved_state.rb +68 -0
  38. data/lib/badline/c64/bus/sid_slots.rb +44 -0
  39. data/lib/badline/c64/bus.rb +218 -0
  40. data/lib/badline/c64/cartridge/action_replay.rb +128 -0
  41. data/lib/badline/c64/cartridge/atomic_power.rb +26 -0
  42. data/lib/badline/c64/cartridge/bank.rb +83 -0
  43. data/lib/badline/c64/cartridge/comal80.rb +52 -0
  44. data/lib/badline/c64/cartridge/dinamic.rb +36 -0
  45. data/lib/badline/c64/cartridge/easy_flash.rb +147 -0
  46. data/lib/badline/c64/cartridge/epyx_fastload.rb +81 -0
  47. data/lib/badline/c64/cartridge/final_cartridge3.rb +91 -0
  48. data/lib/badline/c64/cartridge/flash/saved_state.rb +40 -0
  49. data/lib/badline/c64/cartridge/flash.rb +279 -0
  50. data/lib/badline/c64/cartridge/freezer.rb +35 -0
  51. data/lib/badline/c64/cartridge/fun_play.rb +40 -0
  52. data/lib/badline/c64/cartridge/g_mod2.rb +76 -0
  53. data/lib/badline/c64/cartridge/game_system.rb +42 -0
  54. data/lib/badline/c64/cartridge/geo_ram.rb +81 -0
  55. data/lib/badline/c64/cartridge/kcs_power.rb +77 -0
  56. data/lib/badline/c64/cartridge/mach5.rb +34 -0
  57. data/lib/badline/c64/cartridge/magic_desk.rb +32 -0
  58. data/lib/badline/c64/cartridge/ocean.rb +37 -0
  59. data/lib/badline/c64/cartridge/pagefox.rb +70 -0
  60. data/lib/badline/c64/cartridge/retro_replay/saved_state.rb +47 -0
  61. data/lib/badline/c64/cartridge/retro_replay.rb +286 -0
  62. data/lib/badline/c64/cartridge/rex_utility.rb +31 -0
  63. data/lib/badline/c64/cartridge/rgcd.rb +60 -0
  64. data/lib/badline/c64/cartridge/saved_state.rb +63 -0
  65. data/lib/badline/c64/cartridge/simons_basic.rb +37 -0
  66. data/lib/badline/c64/cartridge/standard.rb +17 -0
  67. data/lib/badline/c64/cartridge/super_games.rb +50 -0
  68. data/lib/badline/c64/cartridge/westermann.rb +32 -0
  69. data/lib/badline/c64/cartridge/zaxxon.rb +68 -0
  70. data/lib/badline/c64/cartridge.rb +258 -0
  71. data/lib/badline/c64/color_memory.rb +27 -0
  72. data/lib/badline/c64/common.rb +34 -0
  73. data/lib/badline/c64/kernal_traps.rb +89 -0
  74. data/lib/badline/c64/reu/dma/saved_state.rb +52 -0
  75. data/lib/badline/c64/reu/dma.rb +275 -0
  76. data/lib/badline/c64/reu/ram.rb +109 -0
  77. data/lib/badline/c64/reu/saved_state.rb +55 -0
  78. data/lib/badline/c64/reu/trigger.rb +34 -0
  79. data/lib/badline/c64/reu.rb +291 -0
  80. data/lib/badline/{computer → c64}/saved_state.rb +13 -18
  81. data/lib/badline/{computer → c64}/true_drives.rb +20 -31
  82. data/lib/badline/{computer.rb → c64.rb} +22 -80
  83. data/lib/badline/chips/cia/fast_forward.rb +62 -0
  84. data/lib/badline/chips/cia/interrupt_register.rb +145 -0
  85. data/lib/badline/chips/cia/saved_state.rb +49 -0
  86. data/lib/badline/chips/cia/serial/saved_state.rb +45 -0
  87. data/lib/badline/chips/cia/serial.rb +250 -0
  88. data/lib/badline/chips/cia/time_of_day.rb +232 -0
  89. data/lib/badline/chips/cia/timer.rb +208 -0
  90. data/lib/badline/chips/cia.rb +281 -0
  91. data/lib/badline/chips/cpu/addressing.rb +133 -0
  92. data/lib/badline/chips/cpu/boundary.rb +56 -0
  93. data/lib/badline/chips/cpu/core.rb +167 -0
  94. data/lib/badline/chips/cpu/instruction.rb +338 -0
  95. data/lib/badline/chips/cpu/instruction_set/arithmetic.rb +118 -0
  96. data/lib/badline/chips/cpu/instruction_set/bitwise.rb +130 -0
  97. data/lib/badline/chips/cpu/instruction_set/branch.rb +83 -0
  98. data/lib/badline/chips/cpu/instruction_set/flag.rb +67 -0
  99. data/lib/badline/chips/cpu/instruction_set/illegal.rb +274 -0
  100. data/lib/badline/chips/cpu/instruction_set/inc_dec.rb +73 -0
  101. data/lib/badline/chips/cpu/instruction_set/stack.rb +56 -0
  102. data/lib/badline/chips/cpu/instruction_set/transfer.rb +141 -0
  103. data/lib/badline/chips/cpu/instruction_set.rb +59 -0
  104. data/lib/badline/chips/cpu/interrupts.rb +108 -0
  105. data/lib/badline/chips/cpu/microcode.rb +179 -0
  106. data/lib/badline/chips/cpu/operations.rb +90 -0
  107. data/lib/badline/chips/cpu/port.rb +116 -0
  108. data/lib/badline/chips/cpu/saved_state.rb +117 -0
  109. data/lib/badline/chips/cpu/stack_operations.rb +84 -0
  110. data/lib/badline/chips/cpu/traps.rb +23 -0
  111. data/lib/badline/chips/cpu.rb +12 -0
  112. data/lib/badline/chips/sid/catch_up.rb +122 -0
  113. data/lib/badline/chips/sid/decimator.rb +66 -0
  114. data/lib/badline/chips/sid/envelope/saved_state.rb +70 -0
  115. data/lib/badline/chips/sid/envelope.rb +241 -0
  116. data/lib/badline/chips/sid/filter/saved_state.rb +42 -0
  117. data/lib/badline/chips/sid/filter.rb +241 -0
  118. data/lib/badline/chips/sid/saved_state.rb +52 -0
  119. data/lib/badline/chips/sid/voice.rb +79 -0
  120. data/lib/badline/chips/sid/waveform/combined.rb +128 -0
  121. data/lib/badline/chips/sid/waveform/fast_forward.rb +107 -0
  122. data/lib/badline/chips/sid/waveform/noise_writeback.rb +124 -0
  123. data/lib/badline/chips/sid/waveform/saved_state.rb +68 -0
  124. data/lib/badline/chips/sid/waveform.rb +307 -0
  125. data/lib/badline/chips/sid.rb +265 -0
  126. data/lib/badline/{c128 → chips}/vdc/memory.rb +1 -1
  127. data/lib/badline/{c128 → chips}/vdc/painter.rb +1 -1
  128. data/lib/badline/{c128 → chips}/vdc/raster.rb +1 -1
  129. data/lib/badline/chips/vdc/saved_state.rb +36 -0
  130. data/lib/badline/{c128 → chips}/vdc/window.rb +1 -1
  131. data/lib/badline/{c128 → chips}/vdc.rb +7 -31
  132. data/lib/badline/chips/via/control_lines.rb +124 -0
  133. data/lib/badline/chips/via/fast_forward.rb +55 -0
  134. data/lib/badline/chips/via/interrupt_register.rb +69 -0
  135. data/lib/badline/chips/via/saved_state.rb +44 -0
  136. data/lib/badline/chips/via/shift_register.rb +182 -0
  137. data/lib/badline/chips/via/timer1.rb +133 -0
  138. data/lib/badline/chips/via/timer2.rb +135 -0
  139. data/lib/badline/chips/via.rb +268 -0
  140. data/lib/badline/chips/vic/bank.rb +135 -0
  141. data/lib/badline/chips/vic/border_mask.rb +88 -0
  142. data/lib/badline/chips/vic/collisions.rb +117 -0
  143. data/lib/badline/chips/vic/color_patches.rb +49 -0
  144. data/lib/badline/chips/vic/display_state.rb +187 -0
  145. data/lib/badline/chips/vic/graphics_mode.rb +166 -0
  146. data/lib/badline/chips/vic/graphics_shifter/saved_state.rb +40 -0
  147. data/lib/badline/chips/vic/graphics_shifter.rb +187 -0
  148. data/lib/badline/chips/vic/register_log.rb +83 -0
  149. data/lib/badline/chips/vic/registers.rb +197 -0
  150. data/lib/badline/chips/vic/saved_state.rb +82 -0
  151. data/lib/badline/chips/vic/sequencer/saved_state.rb +91 -0
  152. data/lib/badline/chips/vic/sequencer.rb +277 -0
  153. data/lib/badline/chips/vic/sequencer_output.rb +118 -0
  154. data/lib/badline/chips/vic/sprite/internal_bus.rb +70 -0
  155. data/lib/badline/chips/vic/sprite/saved_state.rb +66 -0
  156. data/lib/badline/chips/vic/sprite/shifter.rb +148 -0
  157. data/lib/badline/chips/vic/sprite/timing.rb +58 -0
  158. data/lib/badline/chips/vic/sprite.rb +339 -0
  159. data/lib/badline/chips/vic/sprites/saved_state.rb +59 -0
  160. data/lib/badline/chips/vic/sprites.rb +284 -0
  161. data/lib/badline/chips/vic.rb +783 -0
  162. data/lib/badline/chips/vic_i/painter/saved_state.rb +40 -0
  163. data/lib/badline/{vic20/vic → chips/vic_i}/painter.rb +6 -30
  164. data/lib/badline/{vic20/vic → chips/vic_i}/saved_state.rb +2 -2
  165. data/lib/badline/chips/vic_i/sound/output.rb +85 -0
  166. data/lib/badline/chips/vic_i/sound.rb +235 -0
  167. data/lib/badline/{vic20/vic → chips/vic_i}/text_window.rb +2 -2
  168. data/lib/badline/{vic20/vic.rb → chips/vic_i.rb} +5 -5
  169. data/lib/badline/chips/z80/arithmetic.rb +119 -0
  170. data/lib/badline/chips/z80/bit_page.rb +57 -0
  171. data/lib/badline/chips/z80/block_io.rb +60 -0
  172. data/lib/badline/chips/z80/block_transfers.rb +61 -0
  173. data/lib/badline/chips/z80/bus_cycles.rb +113 -0
  174. data/lib/badline/chips/z80/core.rb +115 -0
  175. data/lib/badline/chips/z80/exchanges.rb +52 -0
  176. data/lib/badline/chips/z80/extended_page.rb +91 -0
  177. data/lib/badline/chips/z80/flags.rb +39 -0
  178. data/lib/badline/chips/z80/interrupt_acceptance.rb +48 -0
  179. data/lib/badline/chips/z80/jumps.rb +84 -0
  180. data/lib/badline/chips/z80/loads.rb +100 -0
  181. data/lib/badline/chips/z80/main_page.rb +136 -0
  182. data/lib/badline/chips/z80/registers.rb +122 -0
  183. data/lib/badline/chips/z80/saved_state.rb +71 -0
  184. data/lib/badline/chips/z80/shifts.rb +66 -0
  185. data/lib/badline/chips/z80.rb +13 -0
  186. data/lib/badline/core.rb +27 -29
  187. data/lib/badline/drives/datasette/saved_state.rb +37 -0
  188. data/lib/badline/drives/datasette.rb +140 -0
  189. data/lib/badline/drives/engine/core.rb +149 -0
  190. data/lib/badline/drives/engine/fast_serial.rb +75 -0
  191. data/lib/badline/drives/engine/idle.rb +127 -0
  192. data/lib/badline/drives/engine/idle_cpu.rb +30 -0
  193. data/lib/badline/drives/engine/orbit.rb +81 -0
  194. data/lib/badline/drives/engine/serial_lines.rb +75 -0
  195. data/lib/badline/drives/engine/sleep.rb +114 -0
  196. data/lib/badline/drives/engine/vias.rb +86 -0
  197. data/lib/badline/drives/engine/watch.rb +152 -0
  198. data/lib/badline/drives/engine.rb +11 -0
  199. data/lib/badline/drives/model1541/bus.rb +88 -0
  200. data/lib/badline/drives/model1541/byte_ready.rb +50 -0
  201. data/lib/badline/drives/model1541/cpu.rb +13 -0
  202. data/lib/badline/drives/model1541/disk/saved_state.rb +88 -0
  203. data/lib/badline/drives/model1541/disk.rb +317 -0
  204. data/lib/badline/drives/model1541/disk_via.rb +38 -0
  205. data/lib/badline/drives/model1541/gcr.rb +59 -0
  206. data/lib/badline/drives/model1541/mechanism/saved_state.rb +72 -0
  207. data/lib/badline/drives/model1541/mechanism.rb +315 -0
  208. data/lib/badline/drives/model1541/rotation.rb +123 -0
  209. data/lib/badline/drives/model1541/saved_state.rb +44 -0
  210. data/lib/badline/drives/model1541/sector_reader.rb +78 -0
  211. data/lib/badline/drives/model1541/serial_port.rb +20 -0
  212. data/lib/badline/drives/model1541/track.rb +100 -0
  213. data/lib/badline/drives/model1541.rb +81 -0
  214. data/lib/badline/drives/model1571/bus.rb +113 -0
  215. data/lib/badline/drives/model1571/cpu.rb +13 -0
  216. data/lib/badline/drives/model1571/saved_state.rb +47 -0
  217. data/lib/badline/drives/model1571/serial_port.rb +39 -0
  218. data/lib/badline/drives/model1571/serial_via.rb +27 -0
  219. data/lib/badline/drives/model1571/wd1770.rb +122 -0
  220. data/lib/badline/drives/model1571.rb +142 -0
  221. data/lib/badline/drives/model1581/bus.rb +139 -0
  222. data/lib/badline/drives/model1581/cpu.rb +13 -0
  223. data/lib/badline/drives/model1581/disk/saved_state.rb +89 -0
  224. data/lib/badline/drives/model1581/disk.rb +137 -0
  225. data/lib/badline/drives/model1581/mechanism.rb +119 -0
  226. data/lib/badline/drives/model1581/saved_state.rb +42 -0
  227. data/lib/badline/drives/model1581/serial_port.rb +54 -0
  228. data/lib/badline/drives/model1581/slot.rb +79 -0
  229. data/lib/badline/drives/model1581/track.rb +102 -0
  230. data/lib/badline/drives/model1581/track_bytes.rb +77 -0
  231. data/lib/badline/drives/model1581/wd1772/registers.rb +91 -0
  232. data/lib/badline/drives/model1581/wd1772/saved_state.rb +68 -0
  233. data/lib/badline/drives/model1581/wd1772/sector_writes.rb +75 -0
  234. data/lib/badline/drives/model1581/wd1772/sectors.rb +152 -0
  235. data/lib/badline/drives/model1581/wd1772/stepping.rb +111 -0
  236. data/lib/badline/drives/model1581/wd1772/tracks.rb +127 -0
  237. data/lib/badline/drives/model1581/wd1772/turn.rb +75 -0
  238. data/lib/badline/drives/model1581/wd1772.rb +218 -0
  239. data/lib/badline/drives/model1581.rb +206 -0
  240. data/lib/badline/drives/serial_bus.rb +161 -0
  241. data/lib/badline/frontend/boot.rb +10 -61
  242. data/lib/badline/frontend/machine_window.rb +3 -1
  243. data/lib/badline/frontend/player/filter_view.rb +1 -1
  244. data/lib/badline/frontend/player/player_controller.rb +2 -2
  245. data/lib/badline/frontend/player/player_screen.rb +1 -0
  246. data/lib/badline/frontend/player/theme.rb +7 -7
  247. data/lib/badline/frontend/timeline.rb +3 -3
  248. data/lib/badline/input/control_ports.rb +78 -0
  249. data/lib/badline/input/joystick.rb +27 -0
  250. data/lib/badline/input/keyboard.rb +114 -0
  251. data/lib/badline/input.rb +3 -0
  252. data/lib/badline/kernal_trap/{drive → dos}/block_commands.rb +1 -1
  253. data/lib/badline/kernal_trap/dos/channel.rb +183 -0
  254. data/lib/badline/kernal_trap/{drive → dos}/channels.rb +1 -1
  255. data/lib/badline/kernal_trap/{drive → dos}/memory.rb +1 -1
  256. data/lib/badline/kernal_trap/{drive → dos}/parameters.rb +1 -1
  257. data/lib/badline/kernal_trap/{drive → dos}/status.rb +1 -1
  258. data/lib/badline/kernal_trap/{drive → dos}/write_file.rb +1 -1
  259. data/lib/badline/kernal_trap/{drive → dos}/writes.rb +1 -1
  260. data/lib/badline/kernal_trap/{drive.rb → dos.rb} +11 -10
  261. data/lib/badline/kernal_trap/{file.rb → file_routine.rb} +1 -1
  262. data/lib/badline/kernal_trap/load.rb +6 -6
  263. data/lib/badline/kernal_trap/save.rb +5 -5
  264. data/lib/badline/kernal_trap/serial.rb +7 -7
  265. data/lib/badline/kernal_trap.rb +2 -3
  266. data/lib/badline/machine/clocking.rb +50 -0
  267. data/lib/badline/machine.rb +63 -7
  268. data/lib/badline/media/boot_disk.rb +2 -2
  269. data/lib/badline/media/disk_list.rb +3 -3
  270. data/lib/badline/media/disk_set.rb +1 -1
  271. data/lib/badline/media/extensions.rb +33 -0
  272. data/lib/badline/media/program.rb +28 -0
  273. data/lib/badline/media/true_drive.rb +4 -3
  274. data/lib/badline/media/tune.rb +35 -0
  275. data/lib/badline/media/vic20_cartridge.rb +1 -7
  276. data/lib/badline/media/vic20_media.rb +38 -80
  277. data/lib/badline/media.rb +84 -81
  278. data/lib/badline/model.rb +1 -1
  279. data/lib/badline/options/event.rb +3 -1
  280. data/lib/badline/roms/README +6 -6
  281. data/lib/badline/sdl.rb +1 -0
  282. data/lib/badline/{storage/sid_file.rb → sid_tune/file.rb} +3 -3
  283. data/lib/badline/{storage → sid_tune}/hvsc.rb +7 -7
  284. data/lib/badline/{storage → sid_tune}/song_lengths.rb +2 -2
  285. data/lib/badline/{storage → sid_tune}/stil/credit.rb +1 -1
  286. data/lib/badline/{storage → sid_tune}/stil.rb +3 -3
  287. data/lib/badline/sid_tune.rb +6 -0
  288. data/lib/badline/snapshot/c128_setup.rb +43 -0
  289. data/lib/badline/snapshot/c64_setup.rb +79 -0
  290. data/lib/badline/snapshot/image.rb +5 -25
  291. data/lib/badline/snapshot/machine_state.rb +10 -2
  292. data/lib/badline/snapshot/setup.rb +22 -66
  293. data/lib/badline/snapshot/state.rb +8 -17
  294. data/lib/badline/snapshot/storage_setup.rb +68 -0
  295. data/lib/badline/snapshot/vic20_setup.rb +37 -0
  296. data/lib/badline/snapshot/vice/c64_mem.rb +1 -1
  297. data/lib/badline/snapshot/vice/restore.rb +1 -1
  298. data/lib/badline/snapshot/vice/reu1764.rb +1 -1
  299. data/lib/badline/snapshot/vice/reu_fields.rb +40 -0
  300. data/lib/badline/snapshot/vice.rb +7 -5
  301. data/lib/badline/snapshot.rb +13 -12
  302. data/lib/badline/storage/d64_image.rb +15 -9
  303. data/lib/badline/storage/d71_image.rb +0 -2
  304. data/lib/badline/storage/d81_image.rb +0 -2
  305. data/lib/badline/storage/disk_image/writing.rb +2 -2
  306. data/lib/badline/storage/disk_image.rb +3 -6
  307. data/lib/badline/storage/g64_image.rb +2 -2
  308. data/lib/badline/storage/host_directory.rb +0 -5
  309. data/lib/badline/storage/t64.rb +2 -8
  310. data/lib/badline/storage/unavailable.rb +1 -1
  311. data/lib/badline/storage.rb +0 -37
  312. data/lib/badline/support/c128_checkpoint.rb +39 -0
  313. data/lib/badline/support/checkpoint.rb +108 -0
  314. data/lib/badline/support/vic20_checkpoint.rb +38 -0
  315. data/lib/badline/support.rb +5 -0
  316. data/lib/badline/version.rb +1 -1
  317. data/lib/badline/vic20/attachments.rb +11 -81
  318. data/lib/badline/vic20/bus/saved_state.rb +35 -0
  319. data/lib/badline/vic20/bus.rb +3 -25
  320. data/lib/badline/vic20/cpu.rb +1 -1
  321. data/lib/badline/vic20/port_wiring.rb +4 -4
  322. data/lib/badline/vic20/saved_state.rb +16 -60
  323. data/lib/badline/vic20/user_via_ports.rb +2 -2
  324. data/lib/badline/vic20.rb +20 -51
  325. metadata +250 -221
  326. data/lib/badline/address_bus/extra_sids.rb +0 -30
  327. data/lib/badline/address_bus/fittings.rb +0 -38
  328. data/lib/badline/address_bus/pla.rb +0 -147
  329. data/lib/badline/address_bus/roms.rb +0 -39
  330. data/lib/badline/address_bus/saved_state.rb +0 -72
  331. data/lib/badline/address_bus/sid_slots.rb +0 -42
  332. data/lib/badline/address_bus.rb +0 -235
  333. data/lib/badline/cartridge/action_replay.rb +0 -126
  334. data/lib/badline/cartridge/atomic_power.rb +0 -24
  335. data/lib/badline/cartridge/bank.rb +0 -81
  336. data/lib/badline/cartridge/comal80.rb +0 -50
  337. data/lib/badline/cartridge/dinamic.rb +0 -34
  338. data/lib/badline/cartridge/easy_flash.rb +0 -145
  339. data/lib/badline/cartridge/epyx_fastload.rb +0 -79
  340. data/lib/badline/cartridge/final_cartridge3.rb +0 -89
  341. data/lib/badline/cartridge/flash.rb +0 -306
  342. data/lib/badline/cartridge/freezer.rb +0 -33
  343. data/lib/badline/cartridge/fun_play.rb +0 -38
  344. data/lib/badline/cartridge/g_mod2.rb +0 -74
  345. data/lib/badline/cartridge/game_system.rb +0 -40
  346. data/lib/badline/cartridge/geo_ram.rb +0 -79
  347. data/lib/badline/cartridge/kcs_power.rb +0 -75
  348. data/lib/badline/cartridge/mach5.rb +0 -32
  349. data/lib/badline/cartridge/magic_desk.rb +0 -30
  350. data/lib/badline/cartridge/ocean.rb +0 -35
  351. data/lib/badline/cartridge/pagefox.rb +0 -68
  352. data/lib/badline/cartridge/retro_replay.rb +0 -320
  353. data/lib/badline/cartridge/rex_utility.rb +0 -29
  354. data/lib/badline/cartridge/rgcd.rb +0 -58
  355. data/lib/badline/cartridge/simons_basic.rb +0 -35
  356. data/lib/badline/cartridge/standard.rb +0 -15
  357. data/lib/badline/cartridge/super_games.rb +0 -48
  358. data/lib/badline/cartridge/westermann.rb +0 -30
  359. data/lib/badline/cartridge/zaxxon.rb +0 -66
  360. data/lib/badline/cartridge.rb +0 -303
  361. data/lib/badline/checkpoint.rb +0 -98
  362. data/lib/badline/cia/fast_forward.rb +0 -60
  363. data/lib/badline/cia/interrupt_register.rb +0 -143
  364. data/lib/badline/cia/serial.rb +0 -275
  365. data/lib/badline/cia/state.rb +0 -47
  366. data/lib/badline/cia/timer.rb +0 -206
  367. data/lib/badline/cia.rb +0 -279
  368. data/lib/badline/color_memory.rb +0 -25
  369. data/lib/badline/computer/kernal_traps.rb +0 -29
  370. data/lib/badline/control_ports.rb +0 -76
  371. data/lib/badline/cpu/addressing.rb +0 -131
  372. data/lib/badline/cpu/boundary.rb +0 -54
  373. data/lib/badline/cpu/core.rb +0 -165
  374. data/lib/badline/cpu/microcode.rb +0 -177
  375. data/lib/badline/cpu/operations.rb +0 -88
  376. data/lib/badline/cpu/saved_state.rb +0 -115
  377. data/lib/badline/cpu/stack_operations.rb +0 -82
  378. data/lib/badline/cpu.rb +0 -10
  379. data/lib/badline/cycleable.rb +0 -38
  380. data/lib/badline/datasette.rb +0 -161
  381. data/lib/badline/drive/core.rb +0 -147
  382. data/lib/badline/drive/fast_serial.rb +0 -73
  383. data/lib/badline/drive/idle.rb +0 -125
  384. data/lib/badline/drive/idle_cpu.rb +0 -28
  385. data/lib/badline/drive/orbit.rb +0 -79
  386. data/lib/badline/drive/serial_lines.rb +0 -73
  387. data/lib/badline/drive/sleep.rb +0 -112
  388. data/lib/badline/drive/vias.rb +0 -84
  389. data/lib/badline/drive/watch.rb +0 -150
  390. data/lib/badline/drive.rb +0 -11
  391. data/lib/badline/drive1541/bus.rb +0 -86
  392. data/lib/badline/drive1541/byte_ready.rb +0 -48
  393. data/lib/badline/drive1541/cpu.rb +0 -11
  394. data/lib/badline/drive1541/disk.rb +0 -318
  395. data/lib/badline/drive1541/disk_state.rb +0 -94
  396. data/lib/badline/drive1541/disk_via.rb +0 -36
  397. data/lib/badline/drive1541/gcr.rb +0 -57
  398. data/lib/badline/drive1541/mechanism.rb +0 -313
  399. data/lib/badline/drive1541/mechanism_state.rb +0 -68
  400. data/lib/badline/drive1541/rotation.rb +0 -121
  401. data/lib/badline/drive1541/sector_reader.rb +0 -76
  402. data/lib/badline/drive1541/serial_port.rb +0 -18
  403. data/lib/badline/drive1541/track.rb +0 -98
  404. data/lib/badline/drive1541.rb +0 -111
  405. data/lib/badline/drive1571/bus.rb +0 -111
  406. data/lib/badline/drive1571/cpu.rb +0 -11
  407. data/lib/badline/drive1571/serial_port.rb +0 -37
  408. data/lib/badline/drive1571/serial_via.rb +0 -25
  409. data/lib/badline/drive1571/wd1770.rb +0 -120
  410. data/lib/badline/drive1571.rb +0 -175
  411. data/lib/badline/drive1581/bus.rb +0 -137
  412. data/lib/badline/drive1581/cpu.rb +0 -11
  413. data/lib/badline/drive1581/disk.rb +0 -135
  414. data/lib/badline/drive1581/disk_state.rb +0 -87
  415. data/lib/badline/drive1581/mechanism.rb +0 -117
  416. data/lib/badline/drive1581/serial_port.rb +0 -52
  417. data/lib/badline/drive1581/slot.rb +0 -78
  418. data/lib/badline/drive1581/track.rb +0 -100
  419. data/lib/badline/drive1581/track_bytes.rb +0 -75
  420. data/lib/badline/drive1581/wd1772/registers.rb +0 -89
  421. data/lib/badline/drive1581/wd1772/sector_writes.rb +0 -73
  422. data/lib/badline/drive1581/wd1772/sectors.rb +0 -150
  423. data/lib/badline/drive1581/wd1772/state.rb +0 -66
  424. data/lib/badline/drive1581/wd1772/stepping.rb +0 -109
  425. data/lib/badline/drive1581/wd1772/tracks.rb +0 -125
  426. data/lib/badline/drive1581/wd1772/turn.rb +0 -73
  427. data/lib/badline/drive1581/wd1772.rb +0 -216
  428. data/lib/badline/drive1581.rb +0 -233
  429. data/lib/badline/iec_bus.rb +0 -159
  430. data/lib/badline/instruction.rb +0 -334
  431. data/lib/badline/instruction_set/arithmetic.rb +0 -114
  432. data/lib/badline/instruction_set/bitwise.rb +0 -126
  433. data/lib/badline/instruction_set/branch.rb +0 -79
  434. data/lib/badline/instruction_set/flag.rb +0 -63
  435. data/lib/badline/instruction_set/illegal.rb +0 -270
  436. data/lib/badline/instruction_set/inc_dec.rb +0 -69
  437. data/lib/badline/instruction_set/stack.rb +0 -52
  438. data/lib/badline/instruction_set/transfer.rb +0 -137
  439. data/lib/badline/instruction_set.rb +0 -55
  440. data/lib/badline/interrupts.rb +0 -104
  441. data/lib/badline/joystick.rb +0 -25
  442. data/lib/badline/kernal_trap/channel.rb +0 -181
  443. data/lib/badline/keyboard.rb +0 -112
  444. data/lib/badline/ram_expansion/banking.rb +0 -30
  445. data/lib/badline/ram_expansion/plus256k.rb +0 -54
  446. data/lib/badline/ram_expansion/plus60k.rb +0 -43
  447. data/lib/badline/ram_expansion/unexpanded.rb +0 -28
  448. data/lib/badline/ram_expansion.rb +0 -21
  449. data/lib/badline/reu/dma/saved_state.rb +0 -50
  450. data/lib/badline/reu/dma.rb +0 -273
  451. data/lib/badline/reu/ram.rb +0 -107
  452. data/lib/badline/reu/saved_state.rb +0 -80
  453. data/lib/badline/reu/trigger.rb +0 -32
  454. data/lib/badline/reu.rb +0 -289
  455. data/lib/badline/sid/catch_up.rb +0 -120
  456. data/lib/badline/sid/decimator.rb +0 -64
  457. data/lib/badline/sid/envelope/state.rb +0 -68
  458. data/lib/badline/sid/envelope.rb +0 -239
  459. data/lib/badline/sid/filter.rb +0 -263
  460. data/lib/badline/sid/voice.rb +0 -77
  461. data/lib/badline/sid/waveform/combined.rb +0 -126
  462. data/lib/badline/sid/waveform/fast_forward.rb +0 -105
  463. data/lib/badline/sid/waveform/noise_writeback.rb +0 -122
  464. data/lib/badline/sid/waveform/state.rb +0 -66
  465. data/lib/badline/sid/waveform.rb +0 -305
  466. data/lib/badline/sid.rb +0 -303
  467. data/lib/badline/time_of_day.rb +0 -228
  468. data/lib/badline/traps.rb +0 -19
  469. data/lib/badline/via/control_lines.rb +0 -122
  470. data/lib/badline/via/fast_forward.rb +0 -53
  471. data/lib/badline/via/interrupt_register.rb +0 -67
  472. data/lib/badline/via/shift_register.rb +0 -180
  473. data/lib/badline/via/state.rb +0 -42
  474. data/lib/badline/via/timer1.rb +0 -131
  475. data/lib/badline/via/timer2.rb +0 -133
  476. data/lib/badline/via.rb +0 -266
  477. data/lib/badline/vic/bank.rb +0 -133
  478. data/lib/badline/vic/border_mask.rb +0 -86
  479. data/lib/badline/vic/collisions.rb +0 -115
  480. data/lib/badline/vic/color_patches.rb +0 -47
  481. data/lib/badline/vic/display_state.rb +0 -185
  482. data/lib/badline/vic/graphics_mode.rb +0 -164
  483. data/lib/badline/vic/graphics_shifter.rb +0 -207
  484. data/lib/badline/vic/register_log.rb +0 -81
  485. data/lib/badline/vic/registers.rb +0 -195
  486. data/lib/badline/vic/sequencer.rb +0 -275
  487. data/lib/badline/vic/sequencer_output.rb +0 -116
  488. data/lib/badline/vic/sequencer_state.rb +0 -89
  489. data/lib/badline/vic/sprite/internal_bus.rb +0 -68
  490. data/lib/badline/vic/sprite/saved_state.rb +0 -64
  491. data/lib/badline/vic/sprite/shifter.rb +0 -146
  492. data/lib/badline/vic/sprite/timing.rb +0 -56
  493. data/lib/badline/vic/sprite.rb +0 -337
  494. data/lib/badline/vic/sprites.rb +0 -282
  495. data/lib/badline/vic/sprites_state.rb +0 -57
  496. data/lib/badline/vic.rb +0 -852
  497. data/lib/badline/vic20/sound/output.rb +0 -83
  498. data/lib/badline/vic20/sound.rb +0 -233
  499. data/lib/badline/z80/arithmetic.rb +0 -117
  500. data/lib/badline/z80/bit_page.rb +0 -55
  501. data/lib/badline/z80/block_io.rb +0 -58
  502. data/lib/badline/z80/block_transfers.rb +0 -59
  503. data/lib/badline/z80/bus_cycles.rb +0 -111
  504. data/lib/badline/z80/core.rb +0 -113
  505. data/lib/badline/z80/exchanges.rb +0 -50
  506. data/lib/badline/z80/extended_page.rb +0 -89
  507. data/lib/badline/z80/flags.rb +0 -37
  508. data/lib/badline/z80/interrupt_acceptance.rb +0 -46
  509. data/lib/badline/z80/jumps.rb +0 -82
  510. data/lib/badline/z80/loads.rb +0 -98
  511. data/lib/badline/z80/main_page.rb +0 -134
  512. data/lib/badline/z80/registers.rb +0 -120
  513. data/lib/badline/z80/saved_state.rb +0 -69
  514. data/lib/badline/z80/shifts.rb +0 -64
  515. data/lib/badline/z80.rb +0 -11
  516. /data/lib/badline/snapshot/{fields.rb → vice/fields.rb} +0 -0
@@ -0,0 +1,187 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ module Chips
5
+ class VIC
6
+ # = VIC-II display state
7
+ #
8
+ # The bad line condition, the video counters it drives, and the DMA it
9
+ # starts. A condition that arrives mid-line opens the row late, which is
10
+ # what makes the screen scroll sideways or lose a column of cells.
11
+ class DisplayState
12
+ FIRST_LINE = 0x30 # 48: bad lines and the DEN-at-$30 latch begin here
13
+ LAST_LINE = 0xf7 # 247
14
+ COLUMNS_PER_ROW = 40
15
+
16
+ # AEC follows BA three cycles later; the c-accesses before it read
17
+ # the bus the CPU still drives.
18
+ BA_DELAY = 3
19
+
20
+ # Columns run two ahead of Bauer's cycle numbers: column 10 is his
21
+ # cycle 12. BA falls on a match in columns 10-52, the c-accesses run
22
+ # in 13-52 and the g-accesses in 14-53. VC and VMLI reload in column
23
+ # 12 and the row counter steps in column 56.
24
+ DMA_FIRST = 10
25
+ DMA_LAST = 52
26
+ FETCH_FIRST = 13
27
+ GRAPHICS_FIRST = 14
28
+ GRAPHICS_LAST = 53
29
+ LOAD_COLUMN = 12
30
+ RC_COLUMN = 56
31
+
32
+ GRAPHICS_COLUMNS = Array.new(65) { |column| column.between?(GRAPHICS_FIRST, GRAPHICS_LAST) }.freeze
33
+
34
+ attr_reader :vc_base, :vc, :vmli, :rc, :bad_lines_enabled
35
+
36
+ # The wrap column is the last column of a rasterline, 62 on the 6569,
37
+ # which already compares against the line about to start, the same
38
+ # wrap the raster IRQ latch uses.
39
+ def initialize(registers, wrap_column = 62)
40
+ @registers = registers
41
+ @wrap_column = wrap_column
42
+ @vc_base = 0
43
+ @vc = 0
44
+ @vmli = 0
45
+ @rc = 0
46
+ @display = false
47
+ @bad_lines_enabled = false
48
+ @matched = false
49
+ @ba = nil
50
+ new_line(0)
51
+ end
52
+
53
+ def display? = @display
54
+
55
+ # Sets VCBASE, VC, RC and VMLI, in that order, and the display and
56
+ # bad-line flags, as a VICE snapshot gives them.
57
+ def restore_counters(counters, display, bad_lines_enabled)
58
+ @vc_base, @vc, @rc, @vmli = counters
59
+ @display = display
60
+ @bad_lines_enabled = bad_lines_enabled
61
+ end
62
+
63
+ def idle? = !@display
64
+ def bad_line? = !@ba.nil?
65
+
66
+ # The bad line condition as the last column compared it.
67
+ def bad_line_condition? = @matched
68
+
69
+ # True once AEC has followed BA down and the VIC owns the bus.
70
+ def bus_taken?(column) = @ba ? column >= @ba + BA_DELAY : false
71
+
72
+ # The CPU kept the bus through the cycle before +column+, so on a bad
73
+ # line AEC falls BA_DELAY cycles after it at the earliest.
74
+ def keep_bus(column)
75
+ @ba &&= column
76
+ end
77
+
78
+ def fetching?(column)
79
+ @matched && column >= FETCH_FIRST && column <= DMA_LAST
80
+ end
81
+
82
+ def save_state(out)
83
+ out.int(@vc_base).int(@vc).int(@vmli).int(@rc).boolean(@display).boolean(@bad_lines_enabled)
84
+ out.boolean(@matched).optional_int(@ba).int(@line_bits).boolean(@den_line).boolean(@in_window)
85
+ end
86
+
87
+ def load_state(input)
88
+ @vc_base = input.int
89
+ @vc = input.int
90
+ @vmli = input.int
91
+ @rc = input.int
92
+ @display = input.boolean?
93
+ @bad_lines_enabled = input.boolean?
94
+ @matched = input.boolean?
95
+ @ba = input.optional_int
96
+ @line_bits = input.int
97
+ @den_line = input.boolean?
98
+ @in_window = input.boolean?
99
+ end
100
+
101
+ def new_frame
102
+ @vc_base = 0
103
+ @bad_lines_enabled = false
104
+ end
105
+
106
+ def new_line(line)
107
+ @ba = nil
108
+ raster_step(line)
109
+ end
110
+
111
+ # The raster counter moved on to +line+, at the line's start or, under
112
+ # the VIC-IIe's TEST bit, within it.
113
+ def raster_step(line)
114
+ @line_bits = line & 0b111
115
+ @den_line = line == FIRST_LINE
116
+ @in_window = line.between?(FIRST_LINE, LAST_LINE)
117
+ end
118
+
119
+ # Returns true when this compare opened display state.
120
+ def cycle(rasterline, column)
121
+ match = column == @wrap_column ? wrap_match(rasterline + 1) : line_match
122
+ @matched = match
123
+ opened = false
124
+ if match
125
+ opened = !@display
126
+ @display = true
127
+ @ba = column if @ba.nil? && column >= DMA_FIRST && column <= DMA_LAST
128
+ end
129
+ load_counters(match) if column == LOAD_COLUMN
130
+ check_row_counter(match) if column == RC_COLUMN
131
+ opened
132
+ end
133
+
134
+ # A g-access in display state consumes one buffer cell and one video
135
+ # matrix address. A row that opens mid-line makes fewer of them, and
136
+ # the shortfall carries into VCBASE.
137
+ def graphics_column?(column) = GRAPHICS_COLUMNS[column]
138
+
139
+ def graphics_access(column)
140
+ return unless graphics_column?(column)
141
+
142
+ @vc = (@vc + 1) & 0x3ff
143
+ @vmli += 1
144
+ end
145
+
146
+ private
147
+
148
+ def line_match
149
+ latch_den if @den_line
150
+ @bad_lines_enabled && @in_window && @line_bits == @registers.yscroll
151
+ end
152
+
153
+ # The wrap column compares against the line about to start. The DEN
154
+ # latch is level-sensitive across the counter's increment, so this one
155
+ # column sees both the line ending and the line starting.
156
+ def wrap_match(line)
157
+ latch_den if @den_line || line == FIRST_LINE
158
+ return false unless line.between?(FIRST_LINE, LAST_LINE)
159
+
160
+ @bad_lines_enabled && (line & 0b111) == @registers.yscroll
161
+ end
162
+
163
+ def latch_den
164
+ @bad_lines_enabled = true if @registers.display_enabled?
165
+ end
166
+
167
+ def load_counters(match)
168
+ @vc = @vc_base
169
+ @vmli = 0
170
+ @rc = 0 if match
171
+ end
172
+
173
+ # A condition still standing when the row counter wraps puts the logic
174
+ # straight back into display state, so a forced row rolls into the
175
+ # next one instead of leaving the line idle (Bauer 3.7.2 step 5).
176
+ def check_row_counter(match)
177
+ if @rc == 7
178
+ @display = false
179
+ @vc_base = @vc
180
+ end
181
+ @display ||= match
182
+ @rc = (@rc + 1) & 0b111 if @display
183
+ end
184
+ end
185
+ end
186
+ end
187
+ end
@@ -0,0 +1,166 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ module Chips
5
+ class VIC
6
+ module GraphicsMode
7
+ # Foreground masks depend only on the data byte, so each byte maps to a
8
+ # precomputed frozen pattern shared by reference instead of being
9
+ # written out pixel by pixel.
10
+ HIRES_FG = Array.new(256) do |data|
11
+ Array.new(8) { |i| data.anybits?(1 << (7 - i)) }.freeze
12
+ end.freeze
13
+
14
+ # The high bit of each 2-bit pair (10/11) is foreground.
15
+ PAIR_FG = Array.new(256) do |data|
16
+ Array.new(8) { |i| (data >> (6 - (i & ~1))).allbits?(0b10) }.freeze
17
+ end.freeze
18
+
19
+ # The same patterns as masks, all bits set for a foreground pixel.
20
+ HIRES_MASK = Array.new(256) do |data|
21
+ Array.new(8) { |i| data.anybits?(1 << (7 - i)) ? -1 : 0 }.freeze
22
+ end.freeze
23
+
24
+ NO_FG = HIRES_FG[0]
25
+
26
+ module Hires
27
+ def paint_hires(data, color, background, seq)
28
+ seq.cur_fg = HIRES_FG[data]
29
+ colors = seq.cur_colors
30
+ return colors.fill(background) if data.zero?
31
+
32
+ mask = HIRES_MASK[data]
33
+ flip = color ^ background
34
+ colors[0] = background ^ (flip & mask[0])
35
+ colors[1] = background ^ (flip & mask[1])
36
+ colors[2] = background ^ (flip & mask[2])
37
+ colors[3] = background ^ (flip & mask[3])
38
+ colors[4] = background ^ (flip & mask[4])
39
+ colors[5] = background ^ (flip & mask[5])
40
+ colors[6] = background ^ (flip & mask[6])
41
+ colors[7] = background ^ (flip & mask[7])
42
+ end
43
+ end
44
+
45
+ # Decodes 2-bit pixel pairs into double-wide pixels, each pair indexing
46
+ # the four colours in the mode's palette.
47
+ module Multicolor
48
+ def paint_pairs(data, seq)
49
+ seq.cur_fg = PAIR_FG[data]
50
+ colors = seq.cur_colors
51
+ palette = @palette
52
+ colors[0] = colors[1] = palette[data >> 6]
53
+ colors[2] = colors[3] = palette[(data >> 4) & 0b11]
54
+ colors[4] = colors[5] = palette[(data >> 2) & 0b11]
55
+ colors[6] = colors[7] = palette[data & 0b11]
56
+ end
57
+ end
58
+
59
+ # Each mode paints the byte a g-access read with the screen byte and
60
+ # colour nibble it latched. The VIC reads the byte itself, in the
61
+ # g-access column. #fg gives the foreground mask #paint would leave,
62
+ # without the colours.
63
+ class Text
64
+ include Hires
65
+
66
+ def paint(data, _screencode, color, seq)
67
+ paint_hires(data, color, seq.registers.background, seq)
68
+ end
69
+
70
+ def fg(data, _screencode, _color) = HIRES_FG[data]
71
+ end
72
+
73
+ class MulticolorText
74
+ include Hires
75
+ include Multicolor
76
+
77
+ def initialize
78
+ @palette = Array.new(4, 0)
79
+ end
80
+
81
+ def paint(data, _screencode, color, seq)
82
+ registers = seq.registers
83
+ if color.anybits?(0x08)
84
+ palette = @palette
85
+ palette[0] = registers.background(0)
86
+ palette[1] = registers.background(1)
87
+ palette[2] = registers.background(2)
88
+ palette[3] = color & 0x07
89
+ paint_pairs(data, seq)
90
+ else
91
+ paint_hires(data, color & 0x07, registers.background, seq)
92
+ end
93
+ end
94
+
95
+ def fg(data, _screencode, color) = color.anybits?(0x08) ? PAIR_FG[data] : HIRES_FG[data]
96
+ end
97
+
98
+ class ExtendedBackgroundText
99
+ include Hires
100
+
101
+ def paint(data, screencode, color, seq)
102
+ background = seq.registers.background((screencode >> 6) & 0b11)
103
+ paint_hires(data, color, background, seq)
104
+ end
105
+
106
+ def fg(data, _screencode, _color) = HIRES_FG[data]
107
+ end
108
+
109
+ class Bitmap
110
+ include Hires
111
+
112
+ def paint(data, screencode, _color, seq)
113
+ foreground = (screencode >> 4) & 0x0f
114
+ background = screencode & 0x0f
115
+ paint_hires(data, foreground, background, seq)
116
+ end
117
+
118
+ def fg(data, _screencode, _color) = HIRES_FG[data]
119
+ end
120
+
121
+ class MulticolorBitmap
122
+ include Multicolor
123
+
124
+ def initialize
125
+ @palette = Array.new(4, 0)
126
+ end
127
+
128
+ def paint(data, screencode, color, seq)
129
+ palette = @palette
130
+ palette[0] = seq.registers.background(0)
131
+ palette[1] = (screencode >> 4) & 0x0f
132
+ palette[2] = screencode & 0x0f
133
+ palette[3] = color & 0x0f
134
+ paint_pairs(data, seq)
135
+ end
136
+
137
+ def fg(data, _screencode, _color) = PAIR_FG[data]
138
+ end
139
+
140
+ class Null
141
+ def paint(_data, _screencode, _color, seq)
142
+ seq.cur_fg = NO_FG
143
+ seq.cur_colors.fill(0)
144
+ end
145
+
146
+ def fg(_data, _screencode, _color) = NO_FG
147
+ end
148
+
149
+ NULL_MODE = Null.new
150
+
151
+ # Indexed by ECM/BMM/MCM; the three invalid combinations decode to
152
+ # black.
153
+ MODES = [
154
+ Text.new, # 000 standard text
155
+ MulticolorText.new, # 001 multicolour text
156
+ Bitmap.new, # 010 standard bitmap
157
+ MulticolorBitmap.new, # 011 multicolour bitmap
158
+ ExtendedBackgroundText.new, # 100 ECM text
159
+ NULL_MODE, # 101 invalid
160
+ NULL_MODE, # 110 invalid
161
+ NULL_MODE # 111 invalid
162
+ ].freeze
163
+ end
164
+ end
165
+ end
166
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ module Chips
5
+ class VIC
6
+ class GraphicsShifter
7
+ # The shifter's state for a snapshot.
8
+ module SavedState
9
+ def save_state(out)
10
+ out.int(@next_ecm_bmm).int(@black_below).ints(@colors).booleans(@fg)
11
+ out.int(@gbuf).int(@vbuf).int(@cbuf).int(@flop).int(@pixel)
12
+ out.int(@mode_lookup).int(@mode_read).boolean(@late_mcm).boolean(@late_read)
13
+ end
14
+
15
+ def load_state(input)
16
+ @next_ecm_bmm = input.int
17
+ @black_below = input.int
18
+ input.ints_into(@colors)
19
+ input.booleans_into(@fg)
20
+ load_buffers(input)
21
+ @mode_lookup = input.int
22
+ @mode_read = input.int
23
+ @late_mcm = input.boolean?
24
+ @late_read = input.boolean?
25
+ end
26
+
27
+ private
28
+
29
+ def load_buffers(input)
30
+ @gbuf = input.int
31
+ @vbuf = input.int
32
+ @cbuf = input.int
33
+ @flop = input.int
34
+ @pixel = input.int
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,187 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "badline/chips/vic/graphics_shifter/saved_state"
4
+
5
+ module Badline
6
+ module Chips
7
+ class VIC
8
+ # The graphics shift register, pixel by pixel, for the groups where a
9
+ # mode or XSCROLL change lands (VICE x64sc `draw_graphics`). The
10
+ # sequencer paints whole bytes everywhere else, which comes to the same
11
+ # thing while nothing changes.
12
+ #
13
+ # The byte a group draws loads at pixel XSCROLL. ECM and BMM take hold at
14
+ # pixel 4 as they rise and at pixel 6 as they fall (the 6569's colour
15
+ # latency), except that BMM falls at pixel 5 out of any mode but hi-res
16
+ # bitmap. MCM takes hold at pixel 4 for the colour lookup but only at
17
+ # pixel 7 for how the register is read, where a rising MCM also resets
18
+ # the multicolour flip-flop. An MCM that falls out of the invalid
19
+ # ECM+MCM mode is a pixel later on both counts: the lookup changes at
20
+ # pixel 5 and the read at the next group's pixel 0. The mode bits are
21
+ # the sequencer's: ECM 4, BMM 2, MCM 1.
22
+ #
23
+ # The 8565 has no colour latency: ECM and BMM take hold, rising or
24
+ # falling, at the next group's pixel 0, and an MCM that falls out of
25
+ # ECM+MCM does so on time. Where that pixel 0 takes the group out of an
26
+ # invalid mode, it is still black into hi-res text, and so is a
27
+ # background pixel into multicolour bitmap.
28
+ class GraphicsShifter
29
+ include SavedState
30
+
31
+ ECM = 0b100
32
+ ECM_BMM = 0b110
33
+ MULTICOLOR_BITMAP = 0b011
34
+ ECM_MCM = 0b101
35
+ BMM = 0b010
36
+
37
+ # The group #draw painted, and which of its pixels are foreground.
38
+ attr_reader :colors, :fg
39
+
40
+ def initialize(registers, model: :mos6569)
41
+ @registers = registers
42
+ @latency = model != :mos8565
43
+ @next_ecm_bmm = 0
44
+ @black_below = 0
45
+ @colors = Array.new(8, 0)
46
+ @fg = Array.new(8, false)
47
+ @gbuf = 0
48
+ @vbuf = 0
49
+ @cbuf = 0
50
+ @flop = 0
51
+ @pixel = 0
52
+ @mode_lookup = 0
53
+ @mode_read = 0
54
+ @late_mcm = false
55
+ @late_read = false
56
+ end
57
+
58
+ # Rebuilds the state at the end of a group that painted whole bytes:
59
+ # the byte loaded at pixel shift under an unchanging mode.
60
+ def prime(data, screencode, color, shift, mode)
61
+ @mode_lookup = mode
62
+ @next_ecm_bmm = mode & ECM_BMM
63
+ @mode_read = mode & 1
64
+ @late_read = false
65
+ load(data, screencode, color)
66
+ (8 - shift).times { read_pixel }
67
+ end
68
+
69
+ # Draws one group, loading the byte at pixel shift.
70
+ def draw(data, screencode, color, shift, mode)
71
+ i = 0
72
+ while i < 8
73
+ step_mode(i, mode)
74
+ load(data, screencode, color) if i == shift
75
+ pixel = read_pixel
76
+ @colors[i] = pixel < @black_below ? 0 : lookup(pixel)
77
+ @black_below = 0
78
+ @fg[i] = pixel >= 2
79
+ i += 1
80
+ end
81
+ @next_ecm_bmm = mode & ECM_BMM
82
+ end
83
+
84
+ private
85
+
86
+ def step_mode(pixel, mode)
87
+ case pixel
88
+ when 0 then start_group
89
+ when 4 then step_lookup(mode)
90
+ when 5 then step_early_fall(mode)
91
+ when 6 then @mode_lookup &= mode | 1 if @latency
92
+ when 7 then step_read
93
+ end
94
+ end
95
+
96
+ def start_group
97
+ end_late_read
98
+ return if @latency
99
+
100
+ invalid = @mode_lookup > ECM
101
+ @mode_lookup = (@mode_lookup & ~ECM_BMM) | @next_ecm_bmm
102
+ @black_below = black_pixels_out_of_invalid if invalid
103
+ end
104
+
105
+ # The pixel values below which pixel 0 stays black as the group leaves
106
+ # an invalid mode: all four into hi-res text, the background ones into
107
+ # multicolour bitmap.
108
+ def black_pixels_out_of_invalid
109
+ case @mode_lookup
110
+ when 0 then 4
111
+ when MULTICOLOR_BITMAP then 2
112
+ else 0
113
+ end
114
+ end
115
+
116
+ def step_lookup(mode)
117
+ @late_mcm = @latency && @mode_lookup.allbits?(ECM_MCM) && mode.nobits?(1)
118
+ rising = @latency ? mode & ECM_BMM : 0
119
+ @mode_lookup = (@mode_lookup & ~1) | (mode & 1) | (@late_mcm ? 1 : 0) | rising
120
+ end
121
+
122
+ def step_early_fall(mode)
123
+ @mode_lookup &= ~1 if @late_mcm
124
+ @mode_lookup &= mode | ~BMM if @latency && @mode_lookup != BMM
125
+ end
126
+
127
+ def step_read
128
+ return @late_read = true if @late_mcm
129
+
130
+ mcm = @mode_lookup & 1
131
+ @flop = 0 if mcm == 1 && @mode_read.zero?
132
+ @mode_read = mcm
133
+ end
134
+
135
+ def end_late_read
136
+ return unless @late_read
137
+
138
+ @mode_read = 0
139
+ @late_read = false
140
+ end
141
+
142
+ def load(data, screencode, color)
143
+ @gbuf = data
144
+ @vbuf = screencode
145
+ @cbuf = color & 0x0f
146
+ @flop = 1
147
+ end
148
+
149
+ def read_pixel
150
+ multicolor = multicolor?
151
+ if @mode_read == 1 && multicolor
152
+ @pixel = @gbuf >> 6 if @flop == 1
153
+ elsif @gbuf.anybits?(0x80)
154
+ @pixel = multicolor && @mode_read.zero? ? 2 : 3
155
+ else
156
+ @pixel = 0
157
+ end
158
+ @gbuf = (@gbuf << 1) & 0xff
159
+ @flop ^= 1
160
+ @pixel
161
+ end
162
+
163
+ def multicolor? = @mode_lookup.anybits?(BMM) || @cbuf.anybits?(0x08)
164
+
165
+ def lookup(pixel)
166
+ case @mode_lookup
167
+ when 0 then pixel < 2 ? @registers.background : @cbuf
168
+ when 1 then pixel == 3 ? @cbuf & 0x07 : @registers.background(pixel)
169
+ when 2 then pixel < 2 ? @vbuf & 0x0f : @vbuf >> 4
170
+ when 3 then multicolor_bitmap(pixel)
171
+ when 4 then pixel < 2 ? @registers.background(@vbuf >> 6) : @cbuf
172
+ else 0
173
+ end
174
+ end
175
+
176
+ def multicolor_bitmap(pixel)
177
+ case pixel
178
+ when 0 then @registers.background
179
+ when 1 then @vbuf >> 4
180
+ when 2 then @vbuf & 0x0f
181
+ else @cbuf
182
+ end
183
+ end
184
+ end
185
+ end
186
+ end
187
+ end
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ module Chips
5
+ class VIC
6
+ # Mid-line writes to the sprite registers, keyed by the pixel at which
7
+ # they become visible. Compositing replays the line through the log, so
8
+ # each pixel is drawn with the register values that were in effect when
9
+ # the VIC reached it.
10
+ class RegisterLog
11
+ def initialize(registers)
12
+ @registers = registers
13
+ @entries = []
14
+ @seed = []
15
+ @values = Array.new(2**6, 0)
16
+ @cursor = 0
17
+ @next_x = 0
18
+ end
19
+
20
+ # The pixel at which the next logged write takes effect.
21
+ attr_reader :next_x
22
+
23
+ # The entries as pixel, register and value in turn, the seed with -1
24
+ # for a register the line hasn't logged, and a next pixel past the
25
+ # last entry as -1.
26
+ def save_state(out)
27
+ out.ints(@entries.flatten)
28
+ out.ints(@seed.map { |value| value.nil? ? -1 : value })
29
+ out.ints(@values).int(@cursor).int(@next_x.infinite? ? -1 : @next_x)
30
+ end
31
+
32
+ def load_state(input)
33
+ @entries.replace(input.ints.each_slice(3).to_a)
34
+ @seed.replace(input.ints.map { |value| value.negative? ? nil : value })
35
+ input.ints_into(@values)
36
+ @cursor = input.int
37
+ next_x = input.int
38
+ @next_x = next_x.negative? ? Float::INFINITY : next_x
39
+ end
40
+
41
+ def empty? = @entries.empty?
42
+ def length = @entries.length
43
+
44
+ def clear
45
+ return if @entries.empty?
46
+
47
+ @entries.clear
48
+ @seed.clear
49
+ end
50
+
51
+ def log(pixel_x, reg, old, value)
52
+ @seed[reg] = old if @seed[reg].nil?
53
+ @entries << [pixel_x, reg, value]
54
+ end
55
+
56
+ def prepare
57
+ @entries.sort_by! { |entry| entry[0] }
58
+ end
59
+
60
+ # Restart the cursor before the line's first pixel, with every logged
61
+ # register back at the value it held when the line started.
62
+ def rewind
63
+ @values.length.times { |reg| @values[reg] = @registers[reg] }
64
+ @seed.each_with_index { |value, reg| @values[reg] = value unless value.nil? }
65
+ @cursor = 0
66
+ @next_x = @entries.empty? ? Float::INFINITY : @entries[0][0]
67
+ end
68
+
69
+ def [](reg) = @values[reg]
70
+
71
+ # Apply every write visible at or before pixel_x.
72
+ def advance(pixel_x)
73
+ while @cursor < @entries.length && @entries[@cursor][0] <= pixel_x
74
+ entry = @entries[@cursor]
75
+ @values[entry[1]] = entry[2]
76
+ @cursor += 1
77
+ end
78
+ @next_x = @cursor < @entries.length ? @entries[@cursor][0] : Float::INFINITY
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end