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,268 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "badline/chips/via/control_lines"
4
+ require "badline/chips/via/fast_forward"
5
+ require "badline/chips/via/interrupt_register"
6
+ require "badline/chips/via/shift_register"
7
+ require "badline/chips/via/saved_state"
8
+ require "badline/chips/via/timer1"
9
+ require "badline/chips/via/timer2"
10
+
11
+ module Badline
12
+ module Chips
13
+ # VIA (Versatile Interface Adapter): the MOS 6522, two ports with
14
+ # handshake lines, two timers and a shift register. The 1541 has two.
15
+ #
16
+ # Sixteen registers repeat across a 1 KB window. The control pins take
17
+ # levels from outside through ca1= and friends, and PB6 takes its pulses
18
+ # for timer 2 through pb6= alone: the peripheral's port B input doesn't
19
+ # feed the counter.
20
+ class VIA
21
+ include Addressable
22
+ include FastForward
23
+ include SavedState
24
+
25
+ attr_reader :start, :peripheral, :shift_register, :acr, :pcr
26
+
27
+ def timer1 = @t1.counter
28
+
29
+ def timer1_latch = @t1.latch
30
+
31
+ def timer2 = @t2.counter
32
+
33
+ def interrupt_flags = @ifr.read
34
+
35
+ def interrupt_enable = @ifr.read_enable
36
+
37
+ def initialize(start: 0, peripheral: nil)
38
+ addressable_at(start, length: 2**10)
39
+
40
+ @peripheral = peripheral
41
+ @t1 = Timer1.new
42
+ @t2 = Timer2.new
43
+ @shift_register = ShiftRegister.new
44
+ @ifr = InterruptRegister.new
45
+ @ca = ControlLines.new(@ifr, c1_flag: InterruptRegister::CA1, c2_flag: InterruptRegister::CA2,
46
+ handshake_on_read: true)
47
+ @cb = ControlLines.new(@ifr, c1_flag: InterruptRegister::CB1, c2_flag: InterruptRegister::CB2,
48
+ handshake_on_read: false)
49
+ @pb6_high = true
50
+ @acr = 0x00
51
+ reset!
52
+ end
53
+
54
+ # The RES line clears every register but the timers and the shift
55
+ # register, which carry on counting, and so releases IRQ.
56
+ def reset!
57
+ @ora = @orb = @ddra = @ddrb = 0x00
58
+ @latch_a = @latch_b = 0xff
59
+ @ifr.reset!
60
+ @ca.reset!
61
+ @cb.reset!
62
+ write_acr(0x00)
63
+ @pcr = 0x00
64
+ end
65
+
66
+ def irq? = @ifr.irq?
67
+
68
+ def cycle!
69
+ @ca.cycle! if @ca.pulsing?
70
+ @cb.cycle! if @cb.pulsing?
71
+ @ifr.set(InterruptRegister::TIMER1) if @t1.cycle!(@acr.anybits?(0x40))
72
+ @ifr.set(InterruptRegister::TIMER2) if @t2.cycle!(@sr_uses_t2)
73
+ @ifr.set(InterruptRegister::SR) if @shift_register.cycle!(@t2.low_underflowed)
74
+ end
75
+
76
+ # Port A as driven by ORA and DDRA, input lines floating high.
77
+ def port_a_output = driven_lines(@ora, @ddra)
78
+
79
+ # Port B as driven by ORB and DDRB, with PB7 handed to timer 1 by ACR
80
+ # bit 7.
81
+ def port_b_output
82
+ lines = driven_lines(@orb, @ddrb)
83
+ return lines unless @acr.anybits?(0x80)
84
+
85
+ @t1.pb7 ? lines | 0x80 : lines & 0x7f
86
+ end
87
+
88
+ # An active CA1 edge latches port A while ACR bit 0 is set.
89
+ def ca1=(high)
90
+ return if high == @ca.c1_high
91
+
92
+ @latch_a = pins_a if @acr.anybits?(0x01) && @ca.active_c1_edge?(high)
93
+ @ca.c1 = high
94
+ end
95
+
96
+ def ca2=(high)
97
+ @ca.c2 = high
98
+ end
99
+
100
+ # An active CB1 edge latches port B while ACR bit 1 is set. CB1 also
101
+ # clocks the shift register in its external clock modes.
102
+ def cb1=(high)
103
+ return if high == @cb.c1_high
104
+
105
+ @ifr.set(InterruptRegister::SR) if @shift_register.cb1_edge!(high)
106
+ @latch_b = pins_b if @acr.anybits?(0x02) && @cb.active_c1_edge?(high)
107
+ @cb.c1 = high
108
+ end
109
+
110
+ def cb2=(high)
111
+ @shift_register.cb2_input = high
112
+ @cb.c2 = high
113
+ end
114
+
115
+ # A falling edge counts a pulse while timer 2 counts them.
116
+ def pb6=(high)
117
+ fell = @pb6_high && !high
118
+ @pb6_high = high
119
+ @ifr.set(InterruptRegister::TIMER2) if fell && @t2.pulse!
120
+ end
121
+
122
+ def ca2_output = @ca.c2_output
123
+
124
+ # The shift register's clock on CB1, or nil when CB1 is an input.
125
+ def cb1_output = @shift_register.cb1_output
126
+
127
+ # The shift register takes CB2 over in its shift-out modes.
128
+ def cb2_output
129
+ level = @shift_register.cb2_output
130
+ level.nil? ? @cb.c2_output : level
131
+ end
132
+
133
+ def peek(addr)
134
+ case offset_of(addr) & 0x0f
135
+ when 0x00 then read_port_b
136
+ when 0x01 then read_port_a(handshake: true)
137
+ when 0x02 then @ddrb
138
+ when 0x03 then @ddra
139
+ when 0x04 then read_timer_low(@t1.counter, InterruptRegister::TIMER1)
140
+ when 0x05 then high_byte(@t1.counter)
141
+ when 0x06 then low_byte(@t1.latch)
142
+ when 0x07 then high_byte(@t1.latch)
143
+ when 0x08 then read_timer_low(@t2.counter, InterruptRegister::TIMER2)
144
+ when 0x09 then high_byte(@t2.counter)
145
+ when 0x0a then read_shift_register
146
+ when 0x0b then @acr
147
+ when 0x0c then @pcr
148
+ when 0x0d then @ifr.read
149
+ when 0x0e then @ifr.read_enable
150
+ else read_port_a(handshake: false)
151
+ end
152
+ end
153
+
154
+ def poke(addr, value)
155
+ case offset_of(addr) & 0x0f
156
+ when 0x00 then write_port_b(value)
157
+ when 0x01 then write_port_a(value, handshake: true)
158
+ when 0x02 then @ddrb = value
159
+ when 0x03 then @ddra = value
160
+ when 0x04, 0x06 then @t1.write_latch_low(value)
161
+ when 0x05 then write_timer1_high(value, start: true)
162
+ when 0x07 then write_timer1_high(value, start: false)
163
+ when 0x08 then @t2.latch_low = value
164
+ when 0x09 then write_timer2_high(value)
165
+ when 0x0a then write_shift_register(value)
166
+ when 0x0b then write_acr(value)
167
+ when 0x0c then write_pcr(value)
168
+ when 0x0d then @ifr.write(value)
169
+ when 0x0e then @ifr.write_enable(value)
170
+ else write_port_a(value, handshake: false)
171
+ end
172
+ end
173
+
174
+ private
175
+
176
+ # Output bits are driven from the data register; input bits float high.
177
+ # External peripherals can still pull any line low (wired-AND).
178
+ def driven_lines(register, direction)
179
+ (register & direction) | (~direction & 0xff)
180
+ end
181
+
182
+ def pins_a
183
+ lines = port_a_output
184
+ peripheral ? lines & peripheral.read_a(lines) : lines
185
+ end
186
+
187
+ def pins_b
188
+ lines = port_b_output
189
+ peripheral ? lines & peripheral.read_b(lines) : lines
190
+ end
191
+
192
+ # Port A reads the pins, or what CA1 latched off them.
193
+ def read_port_a(handshake:)
194
+ value = @acr.anybits?(0x01) ? @latch_a : pins_a
195
+ @ca.access!(false) if handshake
196
+ value
197
+ end
198
+
199
+ def write_port_a(value, handshake:)
200
+ @ora = value
201
+ @ca.access!(true) if handshake
202
+ end
203
+
204
+ # Port B reads its output bits from ORB, or from timer 1 for PB7, and
205
+ # its input bits from the pins, or from what CB1 latched off them.
206
+ def read_port_b
207
+ outputs = @acr.anybits?(0x80) ? @ddrb | 0x80 : @ddrb
208
+ inputs = @acr.anybits?(0x02) ? @latch_b : pins_b
209
+ @cb.access!(false)
210
+ (port_b_output & outputs) | (inputs & ~outputs & 0xff)
211
+ end
212
+
213
+ def write_port_b(value)
214
+ @orb = value
215
+ @cb.access!(true)
216
+ end
217
+
218
+ # A read of either timer's low counter byte acknowledges its interrupt.
219
+ def read_timer_low(counter, flag)
220
+ @ifr.clear(flag)
221
+ low_byte(counter)
222
+ end
223
+
224
+ # $5 loads and starts the counter; $7 only writes the latch. Both
225
+ # acknowledge the interrupt.
226
+ def write_timer1_high(value, start:)
227
+ @t1.write_latch_high(value)
228
+ @t1.start! if start
229
+ @ifr.clear(InterruptRegister::TIMER1)
230
+ end
231
+
232
+ def write_timer2_high(value)
233
+ @t2.load(value)
234
+ @ifr.clear(InterruptRegister::TIMER2)
235
+ end
236
+
237
+ def read_shift_register
238
+ value = @shift_register.data
239
+ @shift_register.access!
240
+ @ifr.clear(InterruptRegister::SR)
241
+ value
242
+ end
243
+
244
+ def write_shift_register(value)
245
+ @shift_register.data = value
246
+ @shift_register.access!
247
+ @ifr.clear(InterruptRegister::SR)
248
+ end
249
+
250
+ # Bits 4-2 pick the shift register's mode. Modes 1, 4 and 5 clock off
251
+ # timer 2's low byte. Bit 7 turning on hands PB7 to timer 1 high.
252
+ def write_acr(value)
253
+ @t1.pb7_enabled! if value.anybits?(0x80) && @acr.nobits?(0x80)
254
+ @acr = value
255
+ @t2.count_pulses = value.anybits?(0x20)
256
+ mode = (value >> 2) & 0x07
257
+ @shift_register.mode = mode
258
+ @sr_uses_t2 = [1, 4, 5].include?(mode)
259
+ end
260
+
261
+ def write_pcr(value)
262
+ @pcr = value
263
+ @ca.control = value & 0x0f
264
+ @cb.control = value >> 4
265
+ end
266
+ end
267
+ end
268
+ end
@@ -0,0 +1,135 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ module Chips
5
+ class VIC
6
+ # The 16K window the VIC sees into RAM, selected by CIA2 $DD00, in the
7
+ # bank a +256K picks for it. Character
8
+ # ROM shadows $1000-$1FFF in banks 0 and 2.
9
+ #
10
+ # The machine's bus pushes in what the VIC sees through #connect,
11
+ # #map_character_rom and #map whenever it changes. Until then the bank
12
+ # has a CIA 2, RAM, colour RAM and character ROM of its own.
13
+ class Bank
14
+ include Addressable
15
+
16
+ # The CIA2 port A bank bits are inverted: %11 selects bank 0.
17
+ BANK_STARTS = [0xc000, 0x8000, 0x4000, 0x0000].freeze
18
+
19
+ attr_reader :ram, :color_ram
20
+
21
+ # The bank lines as #sample_lines last saw them.
22
+ attr_accessor :lines
23
+
24
+ def initialize(vic)
25
+ addressable_at(0x0000, length: 2**14)
26
+ connect(cia2: CIA.new(start: 0xdd00), color_ram: C64::ColorMemory.new(vic))
27
+ map_character_rom(ROM.load("character.rom", 0xd000))
28
+ @base = 0
29
+ @starts = BANK_STARTS
30
+ map(Memory.new([], length: 2**16, start: 0))
31
+ @lines = 0b11
32
+ @swapped = false
33
+ end
34
+
35
+ # CIA 2, whose port A picks the bank, and the colour RAM, read from
36
+ # the address +color_base+ gives offset 0.
37
+ def connect(cia2:, color_ram:, color_base: 0xd800)
38
+ @cia2 = cia2
39
+ @color_ram = color_ram
40
+ @color_base = color_base
41
+ end
42
+
43
+ # The character ROM, read from the address +base+ gives offset 0. It
44
+ # shows in the banks whose low line is high, unless +shadow+ is false.
45
+ def map_character_rom(rom, base: 0xc000, shadow: true)
46
+ @character_rom = rom
47
+ @character_base = base
48
+ @shadow_mask = shadow ? 0b01 : 0b00
49
+ end
50
+
51
+ # The RAM the VIC's 64K starts at +base+ in, and the cartridge's
52
+ # Ultimax lines in each half of the cycle with the ROMH bank they
53
+ # show.
54
+ def map(ram, base: 0, phi1_ultimax: false, ultimax: false, romh: nil)
55
+ @ram = ram
56
+ unless base == @base
57
+ @base = base
58
+ @starts = BANK_STARTS.map { |start| base + start }.freeze
59
+ end
60
+ @phi1_ultimax = phi1_ultimax
61
+ @ultimax = ultimax
62
+ @romh = romh
63
+ end
64
+
65
+ # On the 8565, a port A write that swaps the two bank lines, driving
66
+ # one high as the other goes low, shows the VIC both lines low for the
67
+ # cycle after it: every access in that cycle reads bank 3. A swap that
68
+ # the DDR makes, releasing a line to float high, shows nothing.
69
+ # Called once a cycle, ahead of the cycle's accesses.
70
+ def sample_lines
71
+ lines = @cia2.port_a_lines & 0b11
72
+ @swapped = swap?(@lines, lines) && @cia2.port_registers[2].allbits?(0b11)
73
+ @lines = lines
74
+ end
75
+
76
+ def peek(offset)
77
+ return ultimax_peek(offset) if @phi1_ultimax
78
+
79
+ bits = bank_switch_register
80
+ if (bits & @shadow_mask) == 0b01 && (offset & 0xf000) == 0x1000
81
+ @character_rom.peek(@character_base + offset)
82
+ else
83
+ @ram.peek(@starts[bits] + offset)
84
+ end
85
+ end
86
+
87
+ # A fetch in the second half of the cycle, the c-access, sees the
88
+ # memory configuration the CPU does in that half. No cartridge drives
89
+ # Ultimax mode in the first half alone, so otherwise it reads as #peek.
90
+ def peek_phi2(offset)
91
+ @ultimax ? ultimax_peek(offset) : peek(offset)
92
+ end
93
+
94
+ def peek_color(offset)
95
+ @color_ram.nibble(@color_base + offset)
96
+ end
97
+
98
+ def poke(_addr, _value)
99
+ raise ReadOnlyMemoryError
100
+ end
101
+
102
+ # True if the VIC reads the character ROM at this offset.
103
+ def character_rom?(offset)
104
+ !@phi1_ultimax && (bank_switch_register & @shadow_mask) == 0b01 && (offset & 0xf000) == 0x1000
105
+ end
106
+
107
+ def start
108
+ BANK_STARTS[bank_switch_register]
109
+ end
110
+
111
+ private
112
+
113
+ # In Ultimax mode the cartridge ROMH replaces the character ROM
114
+ # shadow, visible at $3000-$3FFF of the window. The MAX is in
115
+ # Ultimax mode with no cartridge too.
116
+ def ultimax_peek(offset)
117
+ romh = @romh
118
+ if romh && offset.allbits?(0x3000)
119
+ romh.peek(0xe000 + (offset & 0x1fff))
120
+ else
121
+ @ram.peek(@starts[bank_switch_register] + offset)
122
+ end
123
+ end
124
+
125
+ def swap?(from, to) = (from ^ to) == 0b11 && from.anybits?(0b11) && to.anybits?(0b11)
126
+
127
+ def bank_switch_register
128
+ return 0 if @swapped
129
+
130
+ @cia2.port_a_lines & 0b11
131
+ end
132
+ end
133
+ end
134
+ end
135
+ end
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ module Chips
5
+ class VIC
6
+ # Border coverage per 8-pixel group, tracked as the sequencer outputs
7
+ # a line.
8
+ #
9
+ # The border is repainted over composited sprites from a snapshot of
10
+ # the finished line, which keeps mid-line border color splits intact.
11
+ class BorderMask
12
+ FULL = 0
13
+ NONE = 1
14
+ MIXED = 2
15
+
16
+ # The group table and per-pixel mask are exposed so the sequencer's
17
+ # pixel loops can write them without a method call per column.
18
+ attr_reader :groups, :mask
19
+
20
+ def initialize(width)
21
+ @width = width
22
+ @groups = Array.new(width / 8, FULL)
23
+ @mask = Array.new(width, true)
24
+ @snapshot = Array.new(width, 0)
25
+ end
26
+
27
+ def reset
28
+ @groups.fill(FULL)
29
+ end
30
+
31
+ def save_state(out)
32
+ out.ints(@groups).booleans(@mask).blob(@snapshot)
33
+ end
34
+
35
+ def load_state(input)
36
+ input.ints_into(@groups)
37
+ input.booleans_into(@mask)
38
+ input.blob_into(@snapshot)
39
+ end
40
+
41
+ def at?(pixel_x)
42
+ case @groups[pixel_x >> 3]
43
+ when FULL then true
44
+ when MIXED then @mask[pixel_x]
45
+ else false
46
+ end
47
+ end
48
+
49
+ def snapshot(colors)
50
+ @snapshot[0, @width] = colors
51
+ end
52
+
53
+ def restore(colors)
54
+ group = 0
55
+ while group < @groups.length
56
+ case @groups[group]
57
+ when FULL then group = restore_full(colors, group)
58
+ when MIXED then restore_mixed(colors, group * 8)
59
+ end
60
+ group += 1
61
+ end
62
+ end
63
+
64
+ private
65
+
66
+ # Copies a run of full-border groups in one splice and returns its
67
+ # last group.
68
+ def restore_full(colors, first)
69
+ last = first
70
+ last += 1 while @groups[last + 1] == FULL
71
+ x_pos = first * 8
72
+ length = (last - first + 1) * 8
73
+ colors[x_pos, length] = @snapshot[x_pos, length]
74
+ last
75
+ end
76
+
77
+ def restore_mixed(colors, x_pos)
78
+ i = 0
79
+ while i < 8
80
+ x = x_pos + i
81
+ colors[x] = @snapshot[x] if @mask[x]
82
+ i += 1
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,117 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ module Chips
5
+ class VIC
6
+ # = Sprite collisions
7
+ #
8
+ # The $D01E/$D01F latches and the per-pixel sprite coverage that feeds
9
+ # them. Collisions latch as the beam passes each covered pixel, so a
10
+ # read mid-line sees the pixels drawn before the reading cycle and none
11
+ # of the ones after it.
12
+ class Collisions
13
+ # $D019 latch bits raised by the collision registers.
14
+ SPRITE_COLLISION_IRQ = 0x04 # IMMC, mirrors $D01E
15
+ DATA_COLLISION_IRQ = 0x02 # IMBC, mirrors $D01F
16
+
17
+ # A read holds the register's reset asserted past the cycle that
18
+ # issued it, so the pixels drawn under it never reach the register.
19
+ # The read still reports only the pixels drawn before its own cycle,
20
+ # which is why the two numbers do not cancel out.
21
+ CLEAR_OVERRUN = 12
22
+
23
+ # The pixel the last fold reached, so the sprites know whether a read
24
+ # has anything left to latch.
25
+ attr_reader :folded
26
+
27
+ # One bitmask per pixel of the line, written straight by the sprite
28
+ # merge and read back here and by the composite.
29
+ attr_reader :hits
30
+
31
+ def initialize(registers, width)
32
+ @registers = registers
33
+ @width = width
34
+ @hits = Array.new(width, 0)
35
+ @folded = 0
36
+ @cleared_sprite = 0
37
+ @cleared_data = 0
38
+ end
39
+
40
+ def save_state(out)
41
+ out.blob(@hits).int(@folded).int(@cleared_sprite).int(@cleared_data)
42
+ end
43
+
44
+ def load_state(input)
45
+ input.blob_into(@hits)
46
+ @folded = input.int
47
+ @cleared_sprite = input.int
48
+ @cleared_data = input.int
49
+ end
50
+
51
+ def start_line
52
+ @folded = 0
53
+ @cleared_sprite = carry(@cleared_sprite)
54
+ @cleared_data = carry(@cleared_data)
55
+ end
56
+
57
+ # Latch every covered pixel between the last fold and the beam. The
58
+ # sprites' touched span bounds the scan; the coverage is zero outside
59
+ # it either way.
60
+ def fold(mask, upto, low, high)
61
+ from = [@folded, low].max
62
+ @folded = upto
63
+ scan(mask, from, [upto, high].min)
64
+ end
65
+
66
+ # A read resets its register for the pixels that follow the reading
67
+ # cycle as well as for everything before it.
68
+ def clear(reg, beam_x)
69
+ if reg == 0x1e
70
+ @cleared_sprite = beam_x + CLEAR_OVERRUN
71
+ else
72
+ @cleared_data = beam_x + CLEAR_OVERRUN
73
+ end
74
+ end
75
+
76
+ private
77
+
78
+ # Two or more sprites on a pixel collide with each other; any sprite
79
+ # over a foreground pixel collides with the graphics. Pixels still
80
+ # under a read's reset are dropped rather than latched.
81
+ def scan(mask, from, upto)
82
+ sprite_clash = 0
83
+ data_clash = 0
84
+ pos = from
85
+ while pos < upto
86
+ bits = @hits[pos]
87
+ unless bits.zero?
88
+ sprite_clash |= bits if pos >= @cleared_sprite && bits.anybits?(bits - 1)
89
+ data_clash |= bits if pos >= @cleared_data && mask[pos]
90
+ end
91
+ pos += 1
92
+ end
93
+ latch(sprite_clash, data_clash)
94
+ end
95
+
96
+ def latch(sprite_clash, data_clash)
97
+ latch_one(0x1e, sprite_clash, SPRITE_COLLISION_IRQ)
98
+ latch_one(0x1f, data_clash, DATA_COLLISION_IRQ)
99
+ end
100
+
101
+ # The IRQ is raised on the edge out of an empty register, so a
102
+ # collision folded on top of an unread one raises nothing.
103
+ def latch_one(reg, clash, irq)
104
+ return if clash.zero?
105
+ return unless @registers.collide!(reg, clash)
106
+
107
+ @registers.latch_irq!(irq)
108
+ end
109
+
110
+ # A reset still asserted at the line wrap carries into the new line.
111
+ def carry(cleared)
112
+ cleared > @width ? cleared - @width : 0
113
+ end
114
+ end
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ module Chips
5
+ class VIC
6
+ # A mid-line color register write becomes visible one pixel into the
7
+ # column emitted the cycle after the write. That emit paints the whole
8
+ # column with the new color, so the boundary pixel is patched back to
9
+ # the old one before the line is finished. On the 8565 the boundary
10
+ # pixel shows light grey instead, the grey dot, whatever the old and new
11
+ # colors are.
12
+ class ColorPatches
13
+ def initialize(sequencer, grey_dots: false)
14
+ @sequencer = sequencer
15
+ @grey_dots = grey_dots
16
+ @patches = []
17
+ end
18
+
19
+ def clear = @patches.clear
20
+
21
+ # Each patch as its four numbers: the boundary pixel, the register,
22
+ # the old colour and the new.
23
+ def save_state(out)
24
+ out.int(@patches.length)
25
+ @patches.each { |patch| out.ints(patch) }
26
+ end
27
+
28
+ def load_state(input)
29
+ @patches.replace(Array.new(input.int) { input.ints })
30
+ end
31
+
32
+ def log(reg, old, value, boundary_x)
33
+ @patches << [boundary_x, reg, @grey_dots ? GREY_DOT : old & 0x0f, value & 0x0f]
34
+ end
35
+
36
+ def apply(colors, fg_mask)
37
+ @patches.each do |(x, reg, old, value)|
38
+ border = @sequencer.border_at?(x)
39
+ if reg == 0x20
40
+ colors[x] = old if border
41
+ elsif !border && !fg_mask[x] && colors[x] == value
42
+ colors[x] = old
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end