badline 0.12.1 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (565) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +66 -1
  3. data/CLAUDE.md +86 -38
  4. data/CONTRIBUTING.md +4 -3
  5. data/README.md +9 -6
  6. data/doc/library.md +9 -9
  7. data/doc/media.md +2 -1
  8. data/doc/pinned-behaviour.md +134 -105
  9. data/doc/snapshot-format.md +1047 -0
  10. data/doc/snapshots.md +73 -43
  11. data/lib/badline/audio/bare_player.rb +53 -20
  12. data/lib/badline/audio/cli.rb +6 -6
  13. data/lib/badline/audio/jukebox.rb +2 -2
  14. data/lib/badline/audio/machine_player.rb +16 -10
  15. data/lib/badline/{media → audio}/queue.rb +1 -1
  16. data/lib/badline/audio/queued_tune.rb +6 -6
  17. data/lib/badline/audio/renderer.rb +33 -22
  18. data/lib/badline/audio/stereo.rb +1 -1
  19. data/lib/badline/c128/banks.rb +6 -3
  20. data/lib/badline/c128/bus/keyed_state.rb +45 -0
  21. data/lib/badline/c128/bus.rb +35 -20
  22. data/lib/badline/c128/color_lines.rb +2 -2
  23. data/lib/badline/c128/cpu.rb +1 -1
  24. data/lib/badline/c128/drives.rb +14 -34
  25. data/lib/badline/c128/fast_serial.rb +5 -5
  26. data/lib/badline/c128/keyed_modules.rb +117 -0
  27. data/lib/badline/c128/keyed_state.rb +91 -0
  28. data/lib/badline/c128/keys.rb +9 -0
  29. data/lib/badline/c128/mmu.rb +9 -8
  30. data/lib/badline/c128/mmu_pages.rb +21 -1
  31. data/lib/badline/c128/modes.rb +21 -11
  32. data/lib/badline/c128/z80.rb +2 -2
  33. data/lib/badline/c128/z80_turns.rb +1 -1
  34. data/lib/badline/c128.rb +21 -16
  35. data/lib/badline/c64/attachments.rb +42 -0
  36. data/lib/badline/c64/banked_ram/banking.rb +32 -0
  37. data/lib/badline/c64/banked_ram/plus256k.rb +64 -0
  38. data/lib/badline/c64/banked_ram/plus60k.rb +47 -0
  39. data/lib/badline/c64/banked_ram/unexpanded.rb +30 -0
  40. data/lib/badline/c64/banked_ram.rb +23 -0
  41. data/lib/badline/c64/bus/debug_register.rb +32 -0
  42. data/lib/badline/c64/bus/extra_sids.rb +32 -0
  43. data/lib/badline/c64/bus/fittings.rb +40 -0
  44. data/lib/badline/c64/bus/keyed_state.rb +41 -0
  45. data/lib/badline/c64/bus/pla.rb +149 -0
  46. data/lib/badline/c64/bus/roms.rb +41 -0
  47. data/lib/badline/c64/bus/sid_slots.rb +44 -0
  48. data/lib/badline/c64/bus.rb +219 -0
  49. data/lib/badline/c64/cartridge/action_replay.rb +115 -0
  50. data/lib/badline/c64/cartridge/atomic_power.rb +26 -0
  51. data/lib/badline/c64/cartridge/bank.rb +83 -0
  52. data/lib/badline/c64/cartridge/comal80.rb +52 -0
  53. data/lib/badline/c64/cartridge/dinamic.rb +36 -0
  54. data/lib/badline/c64/cartridge/easy_flash.rb +132 -0
  55. data/lib/badline/c64/cartridge/epyx_fastload.rb +73 -0
  56. data/lib/badline/c64/cartridge/final_cartridge3.rb +79 -0
  57. data/lib/badline/c64/cartridge/flash.rb +279 -0
  58. data/lib/badline/c64/cartridge/freezer.rb +35 -0
  59. data/lib/badline/c64/cartridge/fun_play.rb +40 -0
  60. data/lib/badline/c64/cartridge/g_mod2.rb +64 -0
  61. data/lib/badline/c64/cartridge/game_system.rb +42 -0
  62. data/lib/badline/c64/cartridge/geo_ram.rb +65 -0
  63. data/lib/badline/c64/cartridge/kcs_power.rb +69 -0
  64. data/lib/badline/c64/cartridge/keyed_state.rb +393 -0
  65. data/lib/badline/c64/cartridge/mach5.rb +34 -0
  66. data/lib/badline/c64/cartridge/magic_desk.rb +32 -0
  67. data/lib/badline/c64/cartridge/ocean.rb +37 -0
  68. data/lib/badline/c64/cartridge/pagefox.rb +60 -0
  69. data/lib/badline/c64/cartridge/retro_replay.rb +278 -0
  70. data/lib/badline/c64/cartridge/rex_utility.rb +31 -0
  71. data/lib/badline/c64/cartridge/rgcd.rb +50 -0
  72. data/lib/badline/c64/cartridge/simons_basic.rb +37 -0
  73. data/lib/badline/c64/cartridge/standard.rb +17 -0
  74. data/lib/badline/c64/cartridge/super_games.rb +40 -0
  75. data/lib/badline/c64/cartridge/westermann.rb +32 -0
  76. data/lib/badline/c64/cartridge/zaxxon.rb +52 -0
  77. data/lib/badline/c64/cartridge.rb +232 -0
  78. data/lib/badline/c64/color_memory.rb +27 -0
  79. data/lib/badline/{computer → c64}/common.rb +2 -2
  80. data/lib/badline/{computer → c64}/kernal_traps.rb +39 -36
  81. data/lib/badline/c64/keyed_modules.rb +111 -0
  82. data/lib/badline/c64/keyed_state.rb +68 -0
  83. data/lib/badline/c64/reu/dma.rb +271 -0
  84. data/lib/badline/c64/reu/keyed_state.rb +140 -0
  85. data/lib/badline/c64/reu/ram.rb +94 -0
  86. data/lib/badline/c64/reu/trigger.rb +34 -0
  87. data/lib/badline/c64/reu.rb +289 -0
  88. data/lib/badline/c64/true_drives.rb +120 -0
  89. data/lib/badline/{computer.rb → c64.rb} +24 -15
  90. data/lib/badline/chips/cia/fast_forward.rb +62 -0
  91. data/lib/badline/chips/cia/interrupt_register.rb +128 -0
  92. data/lib/badline/chips/cia/keyed_parts.rb +213 -0
  93. data/lib/badline/chips/cia/serial.rb +246 -0
  94. data/lib/badline/chips/cia/time_of_day.rb +210 -0
  95. data/lib/badline/chips/cia/timer.rb +188 -0
  96. data/lib/badline/chips/cia.rb +281 -0
  97. data/lib/badline/chips/cpu/addressing.rb +133 -0
  98. data/lib/badline/chips/cpu/boundary.rb +56 -0
  99. data/lib/badline/chips/cpu/core.rb +183 -0
  100. data/lib/badline/chips/cpu/instruction.rb +338 -0
  101. data/lib/badline/chips/cpu/instruction_set/arithmetic.rb +118 -0
  102. data/lib/badline/chips/cpu/instruction_set/bitwise.rb +130 -0
  103. data/lib/badline/chips/cpu/instruction_set/branch.rb +83 -0
  104. data/lib/badline/chips/cpu/instruction_set/flag.rb +67 -0
  105. data/lib/badline/chips/cpu/instruction_set/illegal.rb +274 -0
  106. data/lib/badline/chips/cpu/instruction_set/inc_dec.rb +73 -0
  107. data/lib/badline/chips/cpu/instruction_set/stack.rb +56 -0
  108. data/lib/badline/chips/cpu/instruction_set/transfer.rb +141 -0
  109. data/lib/badline/chips/cpu/instruction_set.rb +59 -0
  110. data/lib/badline/chips/cpu/interrupts.rb +108 -0
  111. data/lib/badline/chips/cpu/keyed_state.rb +177 -0
  112. data/lib/badline/chips/cpu/microcode.rb +195 -0
  113. data/lib/badline/chips/cpu/operations.rb +90 -0
  114. data/lib/badline/chips/cpu/port.rb +133 -0
  115. data/lib/badline/chips/cpu/stack_operations.rb +84 -0
  116. data/lib/badline/chips/cpu/traps.rb +23 -0
  117. data/lib/badline/chips/cpu.rb +17 -0
  118. data/lib/badline/chips/sid/catch_up.rb +122 -0
  119. data/lib/badline/chips/sid/decimator.rb +68 -0
  120. data/lib/badline/chips/sid/envelope.rb +236 -0
  121. data/lib/badline/chips/sid/filter.rb +227 -0
  122. data/lib/badline/chips/sid/keyed_parts.rb +286 -0
  123. data/lib/badline/chips/sid/voice.rb +69 -0
  124. data/lib/badline/chips/sid/waveform/combined.rb +128 -0
  125. data/lib/badline/chips/sid/waveform/fast_forward.rb +107 -0
  126. data/lib/badline/chips/sid/waveform/noise_writeback.rb +124 -0
  127. data/lib/badline/chips/sid/waveform.rb +305 -0
  128. data/lib/badline/chips/sid.rb +264 -0
  129. data/lib/badline/chips/ted/beam.rb +119 -0
  130. data/lib/badline/chips/ted/timers.rb +66 -0
  131. data/lib/badline/chips/ted.rb +143 -0
  132. data/lib/badline/chips/tia.rb +49 -0
  133. data/lib/badline/chips/vdc/keyed_parts.rb +105 -0
  134. data/lib/badline/{c128 → chips}/vdc/memory.rb +1 -1
  135. data/lib/badline/{c128 → chips}/vdc/painter.rb +1 -13
  136. data/lib/badline/{c128 → chips}/vdc/raster.rb +1 -17
  137. data/lib/badline/{c128 → chips}/vdc/window.rb +1 -10
  138. data/lib/badline/{c128 → chips}/vdc.rb +6 -31
  139. data/lib/badline/chips/via/control_lines.rb +109 -0
  140. data/lib/badline/chips/via/fast_forward.rb +55 -0
  141. data/lib/badline/chips/via/interrupt_register.rb +60 -0
  142. data/lib/badline/chips/via/keyed_parts.rb +163 -0
  143. data/lib/badline/chips/via/shift_register.rb +168 -0
  144. data/lib/badline/chips/via/timer1.rb +120 -0
  145. data/lib/badline/chips/via/timer2.rb +117 -0
  146. data/lib/badline/chips/via.rb +267 -0
  147. data/lib/badline/chips/vic/bank.rb +135 -0
  148. data/lib/badline/chips/vic/border_mask.rb +78 -0
  149. data/lib/badline/chips/vic/collisions.rb +106 -0
  150. data/lib/badline/chips/vic/color_patches.rb +38 -0
  151. data/lib/badline/chips/vic/display_state.rb +168 -0
  152. data/lib/badline/chips/vic/graphics_mode.rb +166 -0
  153. data/lib/badline/chips/vic/graphics_shifter.rb +183 -0
  154. data/lib/badline/chips/vic/keyed_parts.rb +228 -0
  155. data/lib/badline/chips/vic/keyed_sprites.rb +225 -0
  156. data/lib/badline/chips/vic/keyed_state.rb +102 -0
  157. data/lib/badline/chips/vic/register_log.rb +65 -0
  158. data/lib/badline/chips/vic/registers.rb +188 -0
  159. data/lib/badline/chips/vic/sequencer.rb +275 -0
  160. data/lib/badline/chips/vic/sequencer_output.rb +118 -0
  161. data/lib/badline/chips/vic/sprite/internal_bus.rb +59 -0
  162. data/lib/badline/chips/vic/sprite/shifter.rb +148 -0
  163. data/lib/badline/chips/vic/sprite/timing.rb +58 -0
  164. data/lib/badline/chips/vic/sprite.rb +337 -0
  165. data/lib/badline/chips/vic/sprites.rb +281 -0
  166. data/lib/badline/chips/vic.rb +787 -0
  167. data/lib/badline/chips/vic_i/keyed_parts.rb +142 -0
  168. data/lib/badline/{vic20/vic → chips/vic_i}/painter.rb +2 -30
  169. data/lib/badline/chips/vic_i/sound/output.rb +85 -0
  170. data/lib/badline/chips/vic_i/sound.rb +227 -0
  171. data/lib/badline/{vic20/vic → chips/vic_i}/text_window.rb +2 -2
  172. data/lib/badline/{vic20/vic.rb → chips/vic_i.rb} +6 -6
  173. data/lib/badline/chips/z80/arithmetic.rb +119 -0
  174. data/lib/badline/chips/z80/bit_page.rb +57 -0
  175. data/lib/badline/chips/z80/block_io.rb +60 -0
  176. data/lib/badline/chips/z80/block_transfers.rb +61 -0
  177. data/lib/badline/chips/z80/bus_cycles.rb +113 -0
  178. data/lib/badline/chips/z80/core.rb +115 -0
  179. data/lib/badline/chips/z80/exchanges.rb +52 -0
  180. data/lib/badline/chips/z80/extended_page.rb +91 -0
  181. data/lib/badline/chips/z80/flags.rb +39 -0
  182. data/lib/badline/chips/z80/interrupt_acceptance.rb +48 -0
  183. data/lib/badline/chips/z80/jumps.rb +84 -0
  184. data/lib/badline/chips/z80/keyed_state.rb +73 -0
  185. data/lib/badline/chips/z80/loads.rb +100 -0
  186. data/lib/badline/chips/z80/main_page.rb +136 -0
  187. data/lib/badline/chips/z80/registers.rb +122 -0
  188. data/lib/badline/chips/z80/shifts.rb +66 -0
  189. data/lib/badline/chips/z80.rb +13 -0
  190. data/lib/badline/chips.rb +10 -0
  191. data/lib/badline/core.rb +13 -32
  192. data/lib/badline/drives/datasette/keyed_state.rb +38 -0
  193. data/lib/badline/drives/datasette.rb +173 -0
  194. data/lib/badline/drives/engine/core.rb +152 -0
  195. data/lib/badline/drives/engine/fast_serial.rb +75 -0
  196. data/lib/badline/drives/engine/idle.rb +127 -0
  197. data/lib/badline/drives/engine/idle_cpu.rb +30 -0
  198. data/lib/badline/drives/engine/orbit.rb +81 -0
  199. data/lib/badline/drives/engine/serial_lines.rb +72 -0
  200. data/lib/badline/drives/engine/sleep.rb +114 -0
  201. data/lib/badline/drives/engine/vias.rb +86 -0
  202. data/lib/badline/drives/engine/watch.rb +152 -0
  203. data/lib/badline/drives/engine.rb +11 -0
  204. data/lib/badline/drives/model1541/bus.rb +75 -0
  205. data/lib/badline/drives/model1541/byte_ready.rb +50 -0
  206. data/lib/badline/drives/model1541/cpu.rb +13 -0
  207. data/lib/badline/drives/model1541/disk.rb +318 -0
  208. data/lib/badline/drives/model1541/disk_via.rb +38 -0
  209. data/lib/badline/drives/model1541/gcr.rb +59 -0
  210. data/lib/badline/drives/model1541/keyed_state.rb +251 -0
  211. data/lib/badline/drives/model1541/mechanism.rb +313 -0
  212. data/lib/badline/drives/model1541/rotation.rb +123 -0
  213. data/lib/badline/drives/model1541/sector_reader.rb +78 -0
  214. data/lib/badline/drives/model1541/serial_port.rb +20 -0
  215. data/lib/badline/drives/model1541/track.rb +100 -0
  216. data/lib/badline/drives/model1541.rb +80 -0
  217. data/lib/badline/drives/model1571/bus.rb +100 -0
  218. data/lib/badline/drives/model1571/cpu.rb +13 -0
  219. data/lib/badline/drives/model1571/keyed_state.rb +108 -0
  220. data/lib/badline/drives/model1571/serial_port.rb +39 -0
  221. data/lib/badline/drives/model1571/serial_via.rb +27 -0
  222. data/lib/badline/drives/model1571/wd1770.rb +108 -0
  223. data/lib/badline/drives/model1571.rb +141 -0
  224. data/lib/badline/drives/model1581/bus.rb +126 -0
  225. data/lib/badline/drives/model1581/cpu.rb +13 -0
  226. data/lib/badline/drives/model1581/disk.rb +133 -0
  227. data/lib/badline/drives/model1581/keyed_state.rb +280 -0
  228. data/lib/badline/drives/model1581/mechanism.rb +98 -0
  229. data/lib/badline/drives/model1581/serial_port.rb +54 -0
  230. data/lib/badline/drives/model1581/slot.rb +61 -0
  231. data/lib/badline/drives/model1581/track.rb +102 -0
  232. data/lib/badline/drives/model1581/track_bytes.rb +77 -0
  233. data/lib/badline/drives/model1581/wd1772/registers.rb +91 -0
  234. data/lib/badline/drives/model1581/wd1772/sector_writes.rb +75 -0
  235. data/lib/badline/drives/model1581/wd1772/sectors.rb +152 -0
  236. data/lib/badline/drives/model1581/wd1772/stepping.rb +111 -0
  237. data/lib/badline/drives/model1581/wd1772/tracks.rb +127 -0
  238. data/lib/badline/drives/model1581/wd1772/turn.rb +75 -0
  239. data/lib/badline/drives/model1581/wd1772.rb +216 -0
  240. data/lib/badline/drives/model1581.rb +206 -0
  241. data/lib/badline/drives/serial_bus.rb +161 -0
  242. data/lib/badline/drives.rb +8 -0
  243. data/lib/badline/frontend/app.rb +3 -3
  244. data/lib/badline/frontend/boot.rb +4 -4
  245. data/lib/badline/frontend/controls.rb +19 -3
  246. data/lib/badline/frontend/drive_led.rb +44 -21
  247. data/lib/badline/frontend/machine_window.rb +3 -1
  248. data/lib/badline/frontend/menu/menu_dialogs.rb +2 -2
  249. data/lib/badline/frontend/player/filter_view.rb +1 -1
  250. data/lib/badline/frontend/player/player_controller.rb +2 -2
  251. data/lib/badline/frontend/player/player_screen.rb +1 -0
  252. data/lib/badline/frontend/player/theme.rb +7 -7
  253. data/lib/badline/frontend/snapshots.rb +12 -1
  254. data/lib/badline/frontend/timeline.rb +30 -12
  255. data/lib/badline/input/control_ports.rb +78 -0
  256. data/lib/badline/input/joystick.rb +27 -0
  257. data/lib/badline/input/keyboard.rb +114 -0
  258. data/lib/badline/input/matrix_typist.rb +98 -0
  259. data/lib/badline/input.rb +4 -0
  260. data/lib/badline/kernal_trap/chrout.rb +45 -0
  261. data/lib/badline/kernal_trap/{drive → dos}/block_commands.rb +1 -1
  262. data/lib/badline/kernal_trap/dos/channel.rb +156 -0
  263. data/lib/badline/kernal_trap/{drive → dos}/channels.rb +1 -26
  264. data/lib/badline/kernal_trap/{drive → dos}/memory.rb +1 -10
  265. data/lib/badline/kernal_trap/{drive → dos}/parameters.rb +1 -1
  266. data/lib/badline/kernal_trap/{drive → dos}/status.rb +1 -9
  267. data/lib/badline/kernal_trap/{drive → dos}/write_file.rb +1 -19
  268. data/lib/badline/kernal_trap/{drive → dos}/writes.rb +1 -1
  269. data/lib/badline/kernal_trap/{drive.rb → dos.rb} +9 -27
  270. data/lib/badline/kernal_trap/file_routine.rb +4 -4
  271. data/lib/badline/kernal_trap/keyed_state.rb +150 -0
  272. data/lib/badline/kernal_trap/layout.rb +75 -11
  273. data/lib/badline/kernal_trap/load.rb +19 -21
  274. data/lib/badline/kernal_trap/routine.rb +12 -0
  275. data/lib/badline/kernal_trap/save.rb +12 -22
  276. data/lib/badline/kernal_trap/serial.rb +8 -24
  277. data/lib/badline/kernal_trap.rb +3 -2
  278. data/lib/badline/machine/clocking.rb +1 -1
  279. data/lib/badline/machine/keyboard_buffer.rb +49 -0
  280. data/lib/badline/machine/settling.rb +29 -0
  281. data/lib/badline/machine.rb +10 -7
  282. data/lib/badline/media/boot_disk.rb +14 -2
  283. data/lib/badline/media/extensions.rb +10 -0
  284. data/lib/badline/media/true_drive.rb +1 -1
  285. data/lib/badline/media/tune.rb +2 -2
  286. data/lib/badline/media.rb +3 -3
  287. data/lib/badline/memory.rb +3 -6
  288. data/lib/badline/model.rb +1 -1
  289. data/lib/badline/options/family.rb +5 -2
  290. data/lib/badline/options/help.rb +42 -16
  291. data/lib/badline/options/table.rb +2 -0
  292. data/lib/badline/options/validation.rb +2 -0
  293. data/lib/badline/options.rb +6 -1
  294. data/lib/badline/plus4/acia.rb +45 -0
  295. data/lib/badline/plus4/bus.rb +312 -0
  296. data/lib/badline/plus4/cpu.rb +10 -0
  297. data/lib/badline/plus4/cpu_port.rb +93 -0
  298. data/lib/badline/plus4/keyboard_port.rb +51 -0
  299. data/lib/badline/plus4/model.rb +38 -0
  300. data/lib/badline/plus4/user_port.rb +31 -0
  301. data/lib/badline/plus4.rb +164 -0
  302. data/lib/badline/roms/README +44 -6
  303. data/lib/badline/roms/plus4/basic.rom +0 -0
  304. data/lib/badline/roms/plus4/function-high.rom +0 -0
  305. data/lib/badline/roms/plus4/function-low.rom +0 -0
  306. data/lib/badline/roms/plus4/kernal-ntsc.rom +0 -0
  307. data/lib/badline/roms/plus4/kernal-pal.rom +0 -0
  308. data/lib/badline/sdl.rb +1 -0
  309. data/lib/badline/{storage/sid_file.rb → sid_tune/file.rb} +3 -3
  310. data/lib/badline/{storage → sid_tune}/hvsc.rb +7 -7
  311. data/lib/badline/{storage → sid_tune}/song_lengths.rb +2 -2
  312. data/lib/badline/{storage → sid_tune}/stil/credit.rb +1 -1
  313. data/lib/badline/{storage → sid_tune}/stil.rb +3 -3
  314. data/lib/badline/sid_tune.rb +6 -0
  315. data/lib/badline/snapshot/c128_setup.rb +32 -0
  316. data/lib/badline/snapshot/c64_setup.rb +83 -0
  317. data/lib/badline/snapshot/container.rb +4 -0
  318. data/lib/badline/snapshot/field_doc/markdown.rb +112 -0
  319. data/lib/badline/snapshot/field_doc.rb +109 -0
  320. data/lib/badline/snapshot/image.rb +132 -46
  321. data/lib/badline/snapshot/keyed/cursor.rb +40 -0
  322. data/lib/badline/snapshot/keyed/modules.rb +66 -0
  323. data/lib/badline/snapshot/keyed/reader.rb +188 -0
  324. data/lib/badline/snapshot/keyed/recorder.rb +88 -0
  325. data/lib/badline/snapshot/keyed/report.rb +26 -0
  326. data/lib/badline/snapshot/keyed/writer.rb +55 -0
  327. data/lib/badline/snapshot/keyed.rb +64 -0
  328. data/lib/badline/snapshot/machine_state.rb +17 -64
  329. data/lib/badline/snapshot/storage_setup.rb +63 -0
  330. data/lib/badline/snapshot/vic20_setup.rb +28 -0
  331. data/lib/badline/snapshot/vice/c64_mem.rb +1 -1
  332. data/lib/badline/snapshot/vice/restore.rb +1 -1
  333. data/lib/badline/snapshot/vice/reu1764.rb +3 -3
  334. data/lib/badline/snapshot/vice/reu_fields.rb +40 -0
  335. data/lib/badline/snapshot/vice.rb +13 -35
  336. data/lib/badline/snapshot.rb +44 -22
  337. data/lib/badline/storage/crt_file.rb +3 -1
  338. data/lib/badline/storage/d64_image.rb +0 -2
  339. data/lib/badline/storage/d71_image.rb +0 -2
  340. data/lib/badline/storage/d81_image.rb +0 -2
  341. data/lib/badline/storage/disk_image.rb +3 -6
  342. data/lib/badline/storage/g64_image.rb +10 -5
  343. data/lib/badline/storage/host_directory.rb +0 -5
  344. data/lib/badline/storage/t64.rb +2 -8
  345. data/lib/badline/storage/tap.rb +43 -15
  346. data/lib/badline/storage/unavailable.rb +1 -1
  347. data/lib/badline/storage.rb +0 -37
  348. data/lib/badline/support/c128_checkpoint.rb +39 -0
  349. data/lib/badline/support/checkpoint.rb +108 -0
  350. data/lib/badline/support/vic20_checkpoint.rb +38 -0
  351. data/lib/badline/support.rb +5 -0
  352. data/lib/badline/version.rb +1 -1
  353. data/lib/badline/vic20/attachments.rb +8 -5
  354. data/lib/badline/vic20/bus/keyed_state.rb +52 -0
  355. data/lib/badline/vic20/bus.rb +2 -25
  356. data/lib/badline/vic20/cpu.rb +1 -1
  357. data/lib/badline/vic20/keyed_modules.rb +73 -0
  358. data/lib/badline/vic20/keyed_state.rb +45 -0
  359. data/lib/badline/vic20/port_wiring.rb +4 -4
  360. data/lib/badline/vic20/user_via_ports.rb +2 -2
  361. data/lib/badline/vic20.rb +22 -17
  362. data/lib/badline.rb +1 -0
  363. metadata +271 -228
  364. data/lib/badline/address_bus/extra_sids.rb +0 -30
  365. data/lib/badline/address_bus/fittings.rb +0 -38
  366. data/lib/badline/address_bus/pla.rb +0 -147
  367. data/lib/badline/address_bus/roms.rb +0 -39
  368. data/lib/badline/address_bus/saved_state.rb +0 -66
  369. data/lib/badline/address_bus/sid_slots.rb +0 -42
  370. data/lib/badline/address_bus.rb +0 -216
  371. data/lib/badline/c128/bus_state.rb +0 -53
  372. data/lib/badline/c128/saved_state.rb +0 -143
  373. data/lib/badline/cartridge/action_replay.rb +0 -126
  374. data/lib/badline/cartridge/atomic_power.rb +0 -24
  375. data/lib/badline/cartridge/bank.rb +0 -81
  376. data/lib/badline/cartridge/comal80.rb +0 -50
  377. data/lib/badline/cartridge/dinamic.rb +0 -34
  378. data/lib/badline/cartridge/easy_flash.rb +0 -145
  379. data/lib/badline/cartridge/epyx_fastload.rb +0 -79
  380. data/lib/badline/cartridge/final_cartridge3.rb +0 -89
  381. data/lib/badline/cartridge/flash.rb +0 -306
  382. data/lib/badline/cartridge/freezer.rb +0 -33
  383. data/lib/badline/cartridge/fun_play.rb +0 -38
  384. data/lib/badline/cartridge/g_mod2.rb +0 -74
  385. data/lib/badline/cartridge/game_system.rb +0 -40
  386. data/lib/badline/cartridge/geo_ram.rb +0 -79
  387. data/lib/badline/cartridge/kcs_power.rb +0 -75
  388. data/lib/badline/cartridge/mach5.rb +0 -32
  389. data/lib/badline/cartridge/magic_desk.rb +0 -30
  390. data/lib/badline/cartridge/ocean.rb +0 -35
  391. data/lib/badline/cartridge/pagefox.rb +0 -68
  392. data/lib/badline/cartridge/retro_replay.rb +0 -320
  393. data/lib/badline/cartridge/rex_utility.rb +0 -29
  394. data/lib/badline/cartridge/rgcd.rb +0 -58
  395. data/lib/badline/cartridge/simons_basic.rb +0 -35
  396. data/lib/badline/cartridge/standard.rb +0 -15
  397. data/lib/badline/cartridge/super_games.rb +0 -48
  398. data/lib/badline/cartridge/westermann.rb +0 -30
  399. data/lib/badline/cartridge/zaxxon.rb +0 -66
  400. data/lib/badline/cartridge.rb +0 -303
  401. data/lib/badline/checkpoint.rb +0 -98
  402. data/lib/badline/chrout_trap.rb +0 -43
  403. data/lib/badline/cia/fast_forward.rb +0 -60
  404. data/lib/badline/cia/interrupt_register.rb +0 -143
  405. data/lib/badline/cia/serial.rb +0 -275
  406. data/lib/badline/cia/state.rb +0 -47
  407. data/lib/badline/cia/timer.rb +0 -206
  408. data/lib/badline/cia.rb +0 -279
  409. data/lib/badline/color_memory.rb +0 -25
  410. data/lib/badline/computer/attachments.rb +0 -88
  411. data/lib/badline/computer/saved_state.rb +0 -117
  412. data/lib/badline/computer/true_drives.rb +0 -69
  413. data/lib/badline/control_ports.rb +0 -76
  414. data/lib/badline/cpu/addressing.rb +0 -131
  415. data/lib/badline/cpu/boundary.rb +0 -54
  416. data/lib/badline/cpu/core.rb +0 -165
  417. data/lib/badline/cpu/microcode.rb +0 -177
  418. data/lib/badline/cpu/operations.rb +0 -88
  419. data/lib/badline/cpu/saved_state.rb +0 -115
  420. data/lib/badline/cpu/stack_operations.rb +0 -82
  421. data/lib/badline/cpu.rb +0 -10
  422. data/lib/badline/cpu_port.rb +0 -112
  423. data/lib/badline/datasette.rb +0 -161
  424. data/lib/badline/debug_register.rb +0 -28
  425. data/lib/badline/drive/core.rb +0 -147
  426. data/lib/badline/drive/fast_serial.rb +0 -73
  427. data/lib/badline/drive/idle.rb +0 -125
  428. data/lib/badline/drive/idle_cpu.rb +0 -28
  429. data/lib/badline/drive/orbit.rb +0 -79
  430. data/lib/badline/drive/serial_lines.rb +0 -73
  431. data/lib/badline/drive/sleep.rb +0 -112
  432. data/lib/badline/drive/vias.rb +0 -84
  433. data/lib/badline/drive/watch.rb +0 -150
  434. data/lib/badline/drive.rb +0 -11
  435. data/lib/badline/drive1541/bus.rb +0 -86
  436. data/lib/badline/drive1541/byte_ready.rb +0 -48
  437. data/lib/badline/drive1541/cpu.rb +0 -11
  438. data/lib/badline/drive1541/disk.rb +0 -315
  439. data/lib/badline/drive1541/disk_state.rb +0 -86
  440. data/lib/badline/drive1541/disk_via.rb +0 -36
  441. data/lib/badline/drive1541/gcr.rb +0 -57
  442. data/lib/badline/drive1541/mechanism.rb +0 -313
  443. data/lib/badline/drive1541/mechanism_state.rb +0 -68
  444. data/lib/badline/drive1541/rotation.rb +0 -121
  445. data/lib/badline/drive1541/sector_reader.rb +0 -76
  446. data/lib/badline/drive1541/serial_port.rb +0 -18
  447. data/lib/badline/drive1541/track.rb +0 -98
  448. data/lib/badline/drive1541.rb +0 -111
  449. data/lib/badline/drive1571/bus.rb +0 -111
  450. data/lib/badline/drive1571/cpu.rb +0 -11
  451. data/lib/badline/drive1571/serial_port.rb +0 -37
  452. data/lib/badline/drive1571/serial_via.rb +0 -25
  453. data/lib/badline/drive1571/wd1770.rb +0 -120
  454. data/lib/badline/drive1571.rb +0 -175
  455. data/lib/badline/drive1581/bus.rb +0 -137
  456. data/lib/badline/drive1581/cpu.rb +0 -11
  457. data/lib/badline/drive1581/disk.rb +0 -135
  458. data/lib/badline/drive1581/disk_state.rb +0 -87
  459. data/lib/badline/drive1581/mechanism.rb +0 -117
  460. data/lib/badline/drive1581/serial_port.rb +0 -52
  461. data/lib/badline/drive1581/slot.rb +0 -78
  462. data/lib/badline/drive1581/track.rb +0 -100
  463. data/lib/badline/drive1581/track_bytes.rb +0 -75
  464. data/lib/badline/drive1581/wd1772/registers.rb +0 -89
  465. data/lib/badline/drive1581/wd1772/sector_writes.rb +0 -73
  466. data/lib/badline/drive1581/wd1772/sectors.rb +0 -150
  467. data/lib/badline/drive1581/wd1772/state.rb +0 -66
  468. data/lib/badline/drive1581/wd1772/stepping.rb +0 -109
  469. data/lib/badline/drive1581/wd1772/tracks.rb +0 -125
  470. data/lib/badline/drive1581/wd1772/turn.rb +0 -73
  471. data/lib/badline/drive1581/wd1772.rb +0 -216
  472. data/lib/badline/drive1581.rb +0 -233
  473. data/lib/badline/iec_bus.rb +0 -159
  474. data/lib/badline/instruction.rb +0 -334
  475. data/lib/badline/instruction_set/arithmetic.rb +0 -114
  476. data/lib/badline/instruction_set/bitwise.rb +0 -126
  477. data/lib/badline/instruction_set/branch.rb +0 -79
  478. data/lib/badline/instruction_set/flag.rb +0 -63
  479. data/lib/badline/instruction_set/illegal.rb +0 -270
  480. data/lib/badline/instruction_set/inc_dec.rb +0 -69
  481. data/lib/badline/instruction_set/stack.rb +0 -52
  482. data/lib/badline/instruction_set/transfer.rb +0 -137
  483. data/lib/badline/instruction_set.rb +0 -55
  484. data/lib/badline/interrupts.rb +0 -104
  485. data/lib/badline/joystick.rb +0 -25
  486. data/lib/badline/kernal_trap/channel.rb +0 -181
  487. data/lib/badline/keyboard.rb +0 -112
  488. data/lib/badline/keyboard_buffer.rb +0 -33
  489. data/lib/badline/ram_expansion/banking.rb +0 -30
  490. data/lib/badline/ram_expansion/plus256k.rb +0 -54
  491. data/lib/badline/ram_expansion/plus60k.rb +0 -43
  492. data/lib/badline/ram_expansion/unexpanded.rb +0 -28
  493. data/lib/badline/ram_expansion.rb +0 -21
  494. data/lib/badline/reu/dma/saved_state.rb +0 -50
  495. data/lib/badline/reu/dma.rb +0 -273
  496. data/lib/badline/reu/ram.rb +0 -107
  497. data/lib/badline/reu/saved_state.rb +0 -80
  498. data/lib/badline/reu/trigger.rb +0 -32
  499. data/lib/badline/reu.rb +0 -289
  500. data/lib/badline/sid/catch_up.rb +0 -120
  501. data/lib/badline/sid/decimator.rb +0 -64
  502. data/lib/badline/sid/envelope/state.rb +0 -68
  503. data/lib/badline/sid/envelope.rb +0 -239
  504. data/lib/badline/sid/filter.rb +0 -263
  505. data/lib/badline/sid/voice.rb +0 -77
  506. data/lib/badline/sid/waveform/combined.rb +0 -126
  507. data/lib/badline/sid/waveform/fast_forward.rb +0 -105
  508. data/lib/badline/sid/waveform/noise_writeback.rb +0 -122
  509. data/lib/badline/sid/waveform/state.rb +0 -66
  510. data/lib/badline/sid/waveform.rb +0 -305
  511. data/lib/badline/sid.rb +0 -303
  512. data/lib/badline/snapshot/setup.rb +0 -75
  513. data/lib/badline/snapshot/state.rb +0 -189
  514. data/lib/badline/time_of_day.rb +0 -228
  515. data/lib/badline/traps.rb +0 -19
  516. data/lib/badline/via/control_lines.rb +0 -122
  517. data/lib/badline/via/fast_forward.rb +0 -53
  518. data/lib/badline/via/interrupt_register.rb +0 -67
  519. data/lib/badline/via/shift_register.rb +0 -180
  520. data/lib/badline/via/state.rb +0 -42
  521. data/lib/badline/via/timer1.rb +0 -131
  522. data/lib/badline/via/timer2.rb +0 -133
  523. data/lib/badline/via.rb +0 -266
  524. data/lib/badline/vic/bank.rb +0 -133
  525. data/lib/badline/vic/border_mask.rb +0 -86
  526. data/lib/badline/vic/collisions.rb +0 -115
  527. data/lib/badline/vic/color_patches.rb +0 -47
  528. data/lib/badline/vic/display_state.rb +0 -185
  529. data/lib/badline/vic/graphics_mode.rb +0 -164
  530. data/lib/badline/vic/graphics_shifter.rb +0 -207
  531. data/lib/badline/vic/register_log.rb +0 -81
  532. data/lib/badline/vic/registers.rb +0 -195
  533. data/lib/badline/vic/sequencer.rb +0 -275
  534. data/lib/badline/vic/sequencer_output.rb +0 -116
  535. data/lib/badline/vic/sequencer_state.rb +0 -89
  536. data/lib/badline/vic/sprite/internal_bus.rb +0 -68
  537. data/lib/badline/vic/sprite/saved_state.rb +0 -64
  538. data/lib/badline/vic/sprite/shifter.rb +0 -146
  539. data/lib/badline/vic/sprite/timing.rb +0 -56
  540. data/lib/badline/vic/sprite.rb +0 -337
  541. data/lib/badline/vic/sprites.rb +0 -282
  542. data/lib/badline/vic/sprites_state.rb +0 -57
  543. data/lib/badline/vic.rb +0 -853
  544. data/lib/badline/vic20/saved_state.rb +0 -124
  545. data/lib/badline/vic20/sound/output.rb +0 -83
  546. data/lib/badline/vic20/sound.rb +0 -233
  547. data/lib/badline/vic20/vic/saved_state.rb +0 -55
  548. data/lib/badline/z80/arithmetic.rb +0 -117
  549. data/lib/badline/z80/bit_page.rb +0 -55
  550. data/lib/badline/z80/block_io.rb +0 -58
  551. data/lib/badline/z80/block_transfers.rb +0 -59
  552. data/lib/badline/z80/bus_cycles.rb +0 -111
  553. data/lib/badline/z80/core.rb +0 -113
  554. data/lib/badline/z80/exchanges.rb +0 -50
  555. data/lib/badline/z80/extended_page.rb +0 -89
  556. data/lib/badline/z80/flags.rb +0 -37
  557. data/lib/badline/z80/interrupt_acceptance.rb +0 -46
  558. data/lib/badline/z80/jumps.rb +0 -82
  559. data/lib/badline/z80/loads.rb +0 -98
  560. data/lib/badline/z80/main_page.rb +0 -134
  561. data/lib/badline/z80/registers.rb +0 -120
  562. data/lib/badline/z80/saved_state.rb +0 -69
  563. data/lib/badline/z80/shifts.rb +0 -64
  564. data/lib/badline/z80.rb +0 -11
  565. /data/lib/badline/snapshot/{fields.rb → vice/fields.rb} +0 -0
@@ -0,0 +1,117 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ module Chips
5
+ class VIA
6
+ # Timer 2: a one-shot 16-bit counter that decrements on every φ2 cycle,
7
+ # or on every falling edge of PB6 when ACR bit 5 sets it counting pulses.
8
+ # Only its low byte has a latch.
9
+ #
10
+ # Timed, it follows timer 1's one-shot timing: loaded with N it reads N
11
+ # on the cycle after the write, and sets its flag on the cycle it rolls
12
+ # from 0 to $FFFF. Where timer 1 would reload, it rolls on down without
13
+ # further interrupts until the next load. Counting pulses, the flag sets
14
+ # on the pulse that takes it past zero, the N + 1st: the datasheet's
15
+ # "reaches zero" is the same underflow it means in timed mode.
16
+ #
17
+ # While the shift register clocks off it, the low byte also counts as
18
+ # an 8-bit timer of its own: on each low byte underflow it reloads from
19
+ # the latch on the next cycle, which makes the shift clock's half period
20
+ # N + 2 cycles. The borrow still reaches the high byte.
21
+ #
22
+ # ACR bit 5 reaches the count input a cycle after the write, so the
23
+ # counter takes one more step in the mode it leaves.
24
+ class Timer2
25
+ attr_accessor :counter, :latch_low
26
+
27
+ # ACR bit 5, which picks PB6 pulses over φ2.
28
+ attr_writer :count_pulses
29
+
30
+ # Whether the low byte underflowed on the last cycle.
31
+ attr_reader :low_underflowed
32
+
33
+ def initialize
34
+ @counter = 0xffff
35
+ @latch_low = 0xff
36
+ @hold = false
37
+ @low_reload = false
38
+ @armed = false
39
+ @low_underflowed = false
40
+ @count_pulses = @counting_pulses = false
41
+ end
42
+
43
+ # The high counter byte write: load the counter with it and the low
44
+ # latch, arm the interrupt and hold the count for a cycle.
45
+ def load(high)
46
+ @counter = (high << 8) | @latch_low
47
+ @hold = true
48
+ @low_reload = false
49
+ @armed = true
50
+ end
51
+
52
+ # One φ2 cycle, with whether the shift register clocks off the low
53
+ # byte. Returns true on the cycle that sets the flag.
54
+ def cycle!(sr_service)
55
+ pulses = @counting_pulses
56
+ @counting_pulses = @count_pulses
57
+ return idle! if pulses || @hold
58
+
59
+ @low_underflowed = @counter.nobits?(0xff)
60
+ @hold = @low_reload = sr_service && @low_underflowed
61
+ decrement
62
+ end
63
+
64
+ # A falling edge on PB6. Returns true when it sets the flag.
65
+ def pulse! = @counting_pulses && decrement
66
+
67
+ # How many cycles the timer can run without setting its flag, taken
68
+ # without the shift register clocking off it: until an armed timer
69
+ # underflows, and forever for one counting pulses or unarmed, which
70
+ # only rolls on. None while a load or a switch of mode is under way.
71
+ def quiet_cycles
72
+ return 0 if @hold || @counting_pulses != @count_pulses
73
+ return FastForward::QUIET if @counting_pulses || !@armed
74
+
75
+ @counter
76
+ end
77
+
78
+ # Runs +cycles+ cycles at once, as many as quiet_cycles allows. The
79
+ # low byte's underflow is set when the last cycle took it from $00.
80
+ def fast_forward(cycles)
81
+ @low_underflowed = false
82
+ return if @counting_pulses
83
+
84
+ @counter = (@counter - cycles) & 0xffff
85
+ @low_underflowed = @counter.allbits?(0xff)
86
+ end
87
+
88
+ # What the counter and the low byte's underflow don't hold, for
89
+ # comparing the timer at two points.
90
+ def state = [@latch_low, @armed, @hold, @low_reload, @count_pulses, @counting_pulses]
91
+
92
+ # Whether the next underflow sets the flag.
93
+ def armed? = @armed
94
+
95
+ # The count, for comparing the timer at two points where it matters.
96
+ def count_state = [@counter, @low_underflowed]
97
+
98
+ private
99
+
100
+ # A cycle while counting pulses, or held after a load or a low byte
101
+ # underflow: the counter stands still, but the hold still ends, so
102
+ # none carries over into timed mode.
103
+ def idle!
104
+ @counter = (@counter & 0xff00) | @latch_low if @low_reload
105
+ @hold = @low_reload = @low_underflowed = false
106
+ end
107
+
108
+ def decrement
109
+ @counter = (@counter - 1) & 0xffff
110
+ interrupt = @armed && @counter == 0xffff
111
+ @armed = false if interrupt
112
+ interrupt
113
+ end
114
+ end
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,267 @@
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/timer1"
8
+ require "badline/chips/via/timer2"
9
+ require "badline/chips/via/keyed_parts"
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
+
24
+ attr_reader :start, :peripheral, :shift_register, :acr, :pcr
25
+
26
+ def timer1 = @t1.counter
27
+
28
+ def timer1_latch = @t1.latch
29
+
30
+ def timer2 = @t2.counter
31
+
32
+ def interrupt_flags = @ifr.read
33
+
34
+ def interrupt_enable = @ifr.read_enable
35
+
36
+ def initialize(start: 0, peripheral: nil)
37
+ addressable_at(start, length: 2**10)
38
+
39
+ @peripheral = peripheral
40
+ @t1 = Timer1.new
41
+ @t2 = Timer2.new
42
+ @shift_register = ShiftRegister.new
43
+ @ifr = InterruptRegister.new
44
+ @ca = ControlLines.new(@ifr, c1_flag: InterruptRegister::CA1, c2_flag: InterruptRegister::CA2,
45
+ handshake_on_read: true)
46
+ @cb = ControlLines.new(@ifr, c1_flag: InterruptRegister::CB1, c2_flag: InterruptRegister::CB2,
47
+ handshake_on_read: false)
48
+ @pb6_high = true
49
+ @acr = 0x00
50
+ reset!
51
+ end
52
+
53
+ # The RES line clears every register but the timers and the shift
54
+ # register, which carry on counting, and so releases IRQ.
55
+ def reset!
56
+ @ora = @orb = @ddra = @ddrb = 0x00
57
+ @latch_a = @latch_b = 0xff
58
+ @ifr.reset!
59
+ @ca.reset!
60
+ @cb.reset!
61
+ write_acr(0x00)
62
+ @pcr = 0x00
63
+ end
64
+
65
+ def irq? = @ifr.irq?
66
+
67
+ def cycle!
68
+ @ca.cycle! if @ca.pulsing?
69
+ @cb.cycle! if @cb.pulsing?
70
+ @ifr.set(InterruptRegister::TIMER1) if @t1.cycle!(@acr.anybits?(0x40))
71
+ @ifr.set(InterruptRegister::TIMER2) if @t2.cycle!(@sr_uses_t2)
72
+ @ifr.set(InterruptRegister::SR) if @shift_register.cycle!(@t2.low_underflowed)
73
+ end
74
+
75
+ # Port A as driven by ORA and DDRA, input lines floating high.
76
+ def port_a_output = driven_lines(@ora, @ddra)
77
+
78
+ # Port B as driven by ORB and DDRB, with PB7 handed to timer 1 by ACR
79
+ # bit 7.
80
+ def port_b_output
81
+ lines = driven_lines(@orb, @ddrb)
82
+ return lines unless @acr.anybits?(0x80)
83
+
84
+ @t1.pb7 ? lines | 0x80 : lines & 0x7f
85
+ end
86
+
87
+ # An active CA1 edge latches port A while ACR bit 0 is set.
88
+ def ca1=(high)
89
+ return if high == @ca.c1_high
90
+
91
+ @latch_a = pins_a if @acr.anybits?(0x01) && @ca.active_c1_edge?(high)
92
+ @ca.c1 = high
93
+ end
94
+
95
+ def ca2=(high)
96
+ @ca.c2 = high
97
+ end
98
+
99
+ # An active CB1 edge latches port B while ACR bit 1 is set. CB1 also
100
+ # clocks the shift register in its external clock modes.
101
+ def cb1=(high)
102
+ return if high == @cb.c1_high
103
+
104
+ @ifr.set(InterruptRegister::SR) if @shift_register.cb1_edge!(high)
105
+ @latch_b = pins_b if @acr.anybits?(0x02) && @cb.active_c1_edge?(high)
106
+ @cb.c1 = high
107
+ end
108
+
109
+ def cb2=(high)
110
+ @shift_register.cb2_input = high
111
+ @cb.c2 = high
112
+ end
113
+
114
+ # A falling edge counts a pulse while timer 2 counts them.
115
+ def pb6=(high)
116
+ fell = @pb6_high && !high
117
+ @pb6_high = high
118
+ @ifr.set(InterruptRegister::TIMER2) if fell && @t2.pulse!
119
+ end
120
+
121
+ def ca2_output = @ca.c2_output
122
+
123
+ # The shift register's clock on CB1, or nil when CB1 is an input.
124
+ def cb1_output = @shift_register.cb1_output
125
+
126
+ # The shift register takes CB2 over in its shift-out modes.
127
+ def cb2_output
128
+ level = @shift_register.cb2_output
129
+ level.nil? ? @cb.c2_output : level
130
+ end
131
+
132
+ def peek(addr)
133
+ case offset_of(addr) & 0x0f
134
+ when 0x00 then read_port_b
135
+ when 0x01 then read_port_a(handshake: true)
136
+ when 0x02 then @ddrb
137
+ when 0x03 then @ddra
138
+ when 0x04 then read_timer_low(@t1.counter, InterruptRegister::TIMER1)
139
+ when 0x05 then high_byte(@t1.counter)
140
+ when 0x06 then low_byte(@t1.latch)
141
+ when 0x07 then high_byte(@t1.latch)
142
+ when 0x08 then read_timer_low(@t2.counter, InterruptRegister::TIMER2)
143
+ when 0x09 then high_byte(@t2.counter)
144
+ when 0x0a then read_shift_register
145
+ when 0x0b then @acr
146
+ when 0x0c then @pcr
147
+ when 0x0d then @ifr.read
148
+ when 0x0e then @ifr.read_enable
149
+ else read_port_a(handshake: false)
150
+ end
151
+ end
152
+
153
+ def poke(addr, value)
154
+ case offset_of(addr) & 0x0f
155
+ when 0x00 then write_port_b(value)
156
+ when 0x01 then write_port_a(value, handshake: true)
157
+ when 0x02 then @ddrb = value
158
+ when 0x03 then @ddra = value
159
+ when 0x04, 0x06 then @t1.write_latch_low(value)
160
+ when 0x05 then write_timer1_high(value, start: true)
161
+ when 0x07 then write_timer1_high(value, start: false)
162
+ when 0x08 then @t2.latch_low = value
163
+ when 0x09 then write_timer2_high(value)
164
+ when 0x0a then write_shift_register(value)
165
+ when 0x0b then write_acr(value)
166
+ when 0x0c then write_pcr(value)
167
+ when 0x0d then @ifr.write(value)
168
+ when 0x0e then @ifr.write_enable(value)
169
+ else write_port_a(value, handshake: false)
170
+ end
171
+ end
172
+
173
+ private
174
+
175
+ # Output bits are driven from the data register; input bits float high.
176
+ # External peripherals can still pull any line low (wired-AND).
177
+ def driven_lines(register, direction)
178
+ (register & direction) | (~direction & 0xff)
179
+ end
180
+
181
+ def pins_a
182
+ lines = port_a_output
183
+ peripheral ? lines & peripheral.read_a(lines) : lines
184
+ end
185
+
186
+ def pins_b
187
+ lines = port_b_output
188
+ peripheral ? lines & peripheral.read_b(lines) : lines
189
+ end
190
+
191
+ # Port A reads the pins, or what CA1 latched off them.
192
+ def read_port_a(handshake:)
193
+ value = @acr.anybits?(0x01) ? @latch_a : pins_a
194
+ @ca.access!(false) if handshake
195
+ value
196
+ end
197
+
198
+ def write_port_a(value, handshake:)
199
+ @ora = value
200
+ @ca.access!(true) if handshake
201
+ end
202
+
203
+ # Port B reads its output bits from ORB, or from timer 1 for PB7, and
204
+ # its input bits from the pins, or from what CB1 latched off them.
205
+ def read_port_b
206
+ outputs = @acr.anybits?(0x80) ? @ddrb | 0x80 : @ddrb
207
+ inputs = @acr.anybits?(0x02) ? @latch_b : pins_b
208
+ @cb.access!(false)
209
+ (port_b_output & outputs) | (inputs & ~outputs & 0xff)
210
+ end
211
+
212
+ def write_port_b(value)
213
+ @orb = value
214
+ @cb.access!(true)
215
+ end
216
+
217
+ # A read of either timer's low counter byte acknowledges its interrupt.
218
+ def read_timer_low(counter, flag)
219
+ @ifr.clear(flag)
220
+ low_byte(counter)
221
+ end
222
+
223
+ # $5 loads and starts the counter; $7 only writes the latch. Both
224
+ # acknowledge the interrupt.
225
+ def write_timer1_high(value, start:)
226
+ @t1.write_latch_high(value)
227
+ @t1.start! if start
228
+ @ifr.clear(InterruptRegister::TIMER1)
229
+ end
230
+
231
+ def write_timer2_high(value)
232
+ @t2.load(value)
233
+ @ifr.clear(InterruptRegister::TIMER2)
234
+ end
235
+
236
+ def read_shift_register
237
+ value = @shift_register.data
238
+ @shift_register.access!
239
+ @ifr.clear(InterruptRegister::SR)
240
+ value
241
+ end
242
+
243
+ def write_shift_register(value)
244
+ @shift_register.data = value
245
+ @shift_register.access!
246
+ @ifr.clear(InterruptRegister::SR)
247
+ end
248
+
249
+ # Bits 4-2 pick the shift register's mode. Modes 1, 4 and 5 clock off
250
+ # timer 2's low byte. Bit 7 turning on hands PB7 to timer 1 high.
251
+ def write_acr(value)
252
+ @t1.pb7_enabled! if value.anybits?(0x80) && @acr.nobits?(0x80)
253
+ @acr = value
254
+ @t2.count_pulses = value.anybits?(0x20)
255
+ mode = (value >> 2) & 0x07
256
+ @shift_register.mode = mode
257
+ @sr_uses_t2 = [1, 4, 5].include?(mode)
258
+ end
259
+
260
+ def write_pcr(value)
261
+ @pcr = value
262
+ @ca.control = value & 0x0f
263
+ @cb.control = value >> 4
264
+ end
265
+ end
266
+ end
267
+ 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,78 @@
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 at?(pixel_x)
32
+ case @groups[pixel_x >> 3]
33
+ when FULL then true
34
+ when MIXED then @mask[pixel_x]
35
+ else false
36
+ end
37
+ end
38
+
39
+ def snapshot(colors)
40
+ @snapshot[0, @width] = colors
41
+ end
42
+
43
+ def restore(colors)
44
+ group = 0
45
+ while group < @groups.length
46
+ case @groups[group]
47
+ when FULL then group = restore_full(colors, group)
48
+ when MIXED then restore_mixed(colors, group * 8)
49
+ end
50
+ group += 1
51
+ end
52
+ end
53
+
54
+ private
55
+
56
+ # Copies a run of full-border groups in one splice and returns its
57
+ # last group.
58
+ def restore_full(colors, first)
59
+ last = first
60
+ last += 1 while @groups[last + 1] == FULL
61
+ x_pos = first * 8
62
+ length = (last - first + 1) * 8
63
+ colors[x_pos, length] = @snapshot[x_pos, length]
64
+ last
65
+ end
66
+
67
+ def restore_mixed(colors, x_pos)
68
+ i = 0
69
+ while i < 8
70
+ x = x_pos + i
71
+ colors[x] = @snapshot[x] if @mask[x]
72
+ i += 1
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,106 @@
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 start_line
41
+ @folded = 0
42
+ @cleared_sprite = carry(@cleared_sprite)
43
+ @cleared_data = carry(@cleared_data)
44
+ end
45
+
46
+ # Latch every covered pixel between the last fold and the beam. The
47
+ # sprites' touched span bounds the scan; the coverage is zero outside
48
+ # it either way.
49
+ def fold(mask, upto, low, high)
50
+ from = [@folded, low].max
51
+ @folded = upto
52
+ scan(mask, from, [upto, high].min)
53
+ end
54
+
55
+ # A read resets its register for the pixels that follow the reading
56
+ # cycle as well as for everything before it.
57
+ def clear(reg, beam_x)
58
+ if reg == 0x1e
59
+ @cleared_sprite = beam_x + CLEAR_OVERRUN
60
+ else
61
+ @cleared_data = beam_x + CLEAR_OVERRUN
62
+ end
63
+ end
64
+
65
+ private
66
+
67
+ # Two or more sprites on a pixel collide with each other; any sprite
68
+ # over a foreground pixel collides with the graphics. Pixels still
69
+ # under a read's reset are dropped rather than latched.
70
+ def scan(mask, from, upto)
71
+ sprite_clash = 0
72
+ data_clash = 0
73
+ pos = from
74
+ while pos < upto
75
+ bits = @hits[pos]
76
+ unless bits.zero?
77
+ sprite_clash |= bits if pos >= @cleared_sprite && bits.anybits?(bits - 1)
78
+ data_clash |= bits if pos >= @cleared_data && mask[pos]
79
+ end
80
+ pos += 1
81
+ end
82
+ latch(sprite_clash, data_clash)
83
+ end
84
+
85
+ def latch(sprite_clash, data_clash)
86
+ latch_one(0x1e, sprite_clash, SPRITE_COLLISION_IRQ)
87
+ latch_one(0x1f, data_clash, DATA_COLLISION_IRQ)
88
+ end
89
+
90
+ # The IRQ is raised on the edge out of an empty register, so a
91
+ # collision folded on top of an unread one raises nothing.
92
+ def latch_one(reg, clash, irq)
93
+ return if clash.zero?
94
+ return unless @registers.collide!(reg, clash)
95
+
96
+ @registers.latch_irq!(irq)
97
+ end
98
+
99
+ # A reset still asserted at the line wrap carries into the new line.
100
+ def carry(cleared)
101
+ cleared > @width ? cleared - @width : 0
102
+ end
103
+ end
104
+ end
105
+ end
106
+ end