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,289 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "badline/c64/reu/ram"
4
+ require "badline/c64/reu/dma"
5
+ require "badline/c64/reu/trigger"
6
+ require "badline/c64/reu/keyed_state"
7
+
8
+ module Badline
9
+ class C64
10
+ # A RAM Expansion Unit: the Commodore 1700 (128K), 1764 (256K) and 1750
11
+ # (512K), and the bigger units built around the same 8726 RAM Expansion
12
+ # Controller (REC), such as the CMD 1750XL, up to 16M.
13
+ #
14
+ # The REC's eleven registers sit in I/O 2 from $DF00, repeated every 32
15
+ # bytes up to $DFFF, as the 1764 and 1750 manuals map them. It moves
16
+ # bytes between the C64 and its RAM by DMA, one bus cycle each, with the
17
+ # CPU held off the bus. A command with the execute bit set starts a
18
+ # transfer, or with bit 4 clear arms the REC to start one on the next
19
+ # write to $FF00, so a program can bank the ROMs out first. A transfer
20
+ # waits for the CPU's next read cycle with BA high, and C64 then
21
+ # clocks it through #dma_cycle! until it hands the bus back.
22
+ class REU
23
+ SIZES_KB = [128, 256, 512, 1024, 2048, 4096, 8192, 16_384].freeze
24
+
25
+ # The registers, by offset.
26
+ STATUS = 0x00
27
+ COMMAND = 0x01
28
+ C64_LOW = 0x02
29
+ C64_HIGH = 0x03
30
+ EXPANSION_LOW = 0x04
31
+ EXPANSION_HIGH = 0x05
32
+ EXPANSION_BANK = 0x06
33
+ LENGTH_LOW = 0x07
34
+ LENGTH_HIGH = 0x08
35
+ INTERRUPT_MASK = 0x09
36
+ ADDRESS_CONTROL = 0x0a
37
+
38
+ # Status: an interrupt is pending, the block ended, a verify found a
39
+ # difference (the manual's fault bit), and the unit is built from 256K
40
+ # chips, which only the 1700 isn't. Reading the status clears the top
41
+ # three.
42
+ INTERRUPT_PENDING = 0x80
43
+ END_OF_BLOCK = 0x40
44
+ FAULT = 0x20
45
+ CHIPS_256K = 0x10
46
+
47
+ # Command: execute, reload the registers after the transfer, don't wait
48
+ # for $FF00, and the transfer type in the bottom two bits.
49
+ EXECUTE = 0x80
50
+ AUTOLOAD = 0x20
51
+ NO_FF00_TRIGGER = 0x10
52
+ TRANSFER_TYPE = 0x03
53
+
54
+ # Interrupt mask: interrupts on at all, then on the end of a block and
55
+ # on a fault. The low five bits aren't there and read as 1.
56
+ INTERRUPTS_ON = 0x80
57
+ INTERRUPT_ON_END = 0x40
58
+ INTERRUPT_ON_FAULT = 0x20
59
+
60
+ # Address control: hold the C64 address or the expansion address where
61
+ # it is. The low six bits aren't there and read as 1.
62
+ FIX_C64 = 0x80
63
+ FIX_EXPANSION = 0x40
64
+
65
+ # The REC's bank register has three bits, and the other five read as
66
+ # 1. A unit past 512K latches all eight onto its own address lines.
67
+ BANK_STUCK = 0xf8
68
+
69
+ attr_reader :trigger
70
+
71
+ # bus is the bus the REC reaches C64 memory through, and vic the VIC,
72
+ # whose phi1 fetch is what a register read finds on the bus during a
73
+ # transfer.
74
+ def initialize(size_kb, bus:, vic:)
75
+ raise ArgumentError, "No #{size_kb}K REU. Pick one of #{SIZES_KB.join(', ')}." unless SIZES_KB.include?(size_kb)
76
+
77
+ # The 1700's REC counts 17 address bits, the others' 19.
78
+ span = size_kb == 128 ? 0x20000 : 0x80000
79
+ @size_kb = size_kb
80
+ size = size_kb * 1024
81
+ @address_mask = [size, span].max - 1
82
+ @bank_bits = size > span ? 0xff : 0x07
83
+ @chips = size_kb == 128 ? 0 : CHIPS_256K
84
+ @ram = RAM.new(size, span)
85
+ @dma = DMA.new(@ram, span, bus)
86
+ @trigger = Trigger.new(self)
87
+ @vic = vic
88
+ @on_irq_change = nil
89
+ @on_dma = nil
90
+ @irq = false
91
+ reset!
92
+ end
93
+
94
+ def on_irq_change(&block)
95
+ @on_irq_change = block
96
+ end
97
+
98
+ # Called when the REC asks for the bus.
99
+ def on_dma(&block)
100
+ @on_dma = block
101
+ end
102
+
103
+ def irq? = @irq
104
+
105
+ # From the moment the REC asks for the bus until it hands it back.
106
+ def dma? = @requested || @running
107
+
108
+ # Whether the REC ran a transfer on the last cycle clocked, which it
109
+ # goes on doing until the cycle it lets go of the bus.
110
+ def holds_bus? = @running
111
+
112
+ def reset!
113
+ @status = @chips
114
+ @command = NO_FF00_TRIGGER
115
+ @c64 = @c64_start = 0
116
+ @expansion = @expansion_start = 0
117
+ @bank = @bank_start = 0
118
+ @length = @length_start = 0xffff
119
+ @interrupt_mask = 0x1f
120
+ @address_control = 0x3f
121
+ @armed = @requested = @running = @ba_was_low = false
122
+ interrupt(false)
123
+ end
124
+
125
+ def peek(addr)
126
+ return @vic.phi1_data if @running
127
+
128
+ register = addr & 0x1f
129
+ value = register_value(register)
130
+ read_status if register == STATUS
131
+ value
132
+ end
133
+
134
+ def poke(addr, value)
135
+ return if @running
136
+
137
+ register = addr & 0x1f
138
+ case register
139
+ when COMMAND then write_command(value)
140
+ when C64_LOW, C64_HIGH then @c64 = @c64_start = with_byte(@c64_start, value, register == C64_HIGH)
141
+ when EXPANSION_LOW, EXPANSION_HIGH
142
+ @expansion = @expansion_start = with_byte(@expansion_start, value, register == EXPANSION_HIGH)
143
+ when EXPANSION_BANK then @bank = @bank_start = value & @bank_bits
144
+ when LENGTH_LOW, LENGTH_HIGH
145
+ @length = @length_start = with_byte(@length_start, value, register == LENGTH_HIGH)
146
+ when INTERRUPT_MASK then write_interrupt_mask(value)
147
+ when ADDRESS_CONTROL then @address_control = value | 0x3f
148
+ end
149
+ end
150
+
151
+ # A write to $FF00 starts the transfer the command register armed.
152
+ def ff00_written
153
+ return unless @armed
154
+
155
+ @armed = false
156
+ request_bus
157
+ end
158
+
159
+ # Clocks the REC for a cycle, given the VIC's BA line. A requested
160
+ # transfer starts on the first cycle with BA high as the REC sees it,
161
+ # even one the CPU writes on. Pinned by REU/rmw-trigger. A fetch
162
+ # requested on the cycle before BA falls writes its first byte on the
163
+ # first BA-low cycle as well, and again when it starts. Pinned by
164
+ # REU/badoublewrite.
165
+ def dma_cycle!(ba_low)
166
+ late = ba_low && @vic.reu_ba_late?
167
+ if @requested
168
+ return hold_off if ba_low && !late
169
+
170
+ start_transfer
171
+ end
172
+ @dma.cycle!(ba_low, late, ba_low && @vic.reu_ba_handed_on?)
173
+ end_transfer unless @dma.holds_bus?
174
+ end
175
+
176
+ # The REU's RAM, at an address as the REC's registers give it.
177
+ def ram_peek(addr) = @ram.peek(addr)
178
+
179
+ def ram_poke(addr, value) = @ram.poke(addr, value)
180
+
181
+ private
182
+
183
+ def register_value(register)
184
+ case register
185
+ when STATUS then @status
186
+ when COMMAND then @command
187
+ when C64_LOW then @c64 & 0xff
188
+ when C64_HIGH then @c64 >> 8
189
+ when EXPANSION_LOW then @expansion & 0xff
190
+ when EXPANSION_HIGH then @expansion >> 8
191
+ when EXPANSION_BANK then @bank | BANK_STUCK
192
+ when LENGTH_LOW then @length & 0xff
193
+ when LENGTH_HIGH then @length >> 8
194
+ when INTERRUPT_MASK then @interrupt_mask
195
+ when ADDRESS_CONTROL then @address_control
196
+ else 0xff
197
+ end
198
+ end
199
+
200
+ def read_status
201
+ @status &= CHIPS_256K
202
+ interrupt(false)
203
+ end
204
+
205
+ # Writing either byte of an address or the length sets the value the
206
+ # transfer starts from, and puts the counter back to it.
207
+ def with_byte(word, value, high)
208
+ high ? (word & 0x00ff) | (value << 8) : (word & 0xff00) | value
209
+ end
210
+
211
+ def write_command(value)
212
+ @command = value
213
+ return if value.nobits?(EXECUTE)
214
+
215
+ @armed = value.nobits?(NO_FF00_TRIGGER)
216
+ request_bus unless @armed
217
+ end
218
+
219
+ # An interrupt enabled over a block that already ended, or a fault
220
+ # already found, fires at once.
221
+ def write_interrupt_mask(value)
222
+ @interrupt_mask = value | 0x1f
223
+ interrupt_on(@status)
224
+ end
225
+
226
+ def interrupt_on(events)
227
+ return unless @interrupt_mask.anybits?(INTERRUPTS_ON)
228
+ return unless (events.anybits?(END_OF_BLOCK) && @interrupt_mask.anybits?(INTERRUPT_ON_END)) ||
229
+ (events.anybits?(FAULT) && @interrupt_mask.anybits?(INTERRUPT_ON_FAULT))
230
+
231
+ @status |= INTERRUPT_PENDING
232
+ interrupt(true)
233
+ end
234
+
235
+ def interrupt(level)
236
+ return if @irq == level
237
+
238
+ @irq = level
239
+ @on_irq_change&.call(level)
240
+ end
241
+
242
+ def request_bus
243
+ @requested = true
244
+ @ba_was_low = @vic.ba_low?
245
+ @on_dma&.call
246
+ end
247
+
248
+ def hold_off
249
+ @dma.write_ahead(@command & TRANSFER_TYPE, @c64, @expansion | (@bank << 16)) unless @ba_was_low
250
+ @ba_was_low = true
251
+ end
252
+
253
+ def start_transfer
254
+ @requested = false
255
+ @running = true
256
+ @dma.start(@command & TRANSFER_TYPE, @c64, @expansion | (@bank << 16), @length, @address_control)
257
+ end
258
+
259
+ # The execute bit drops and the $FF00 trigger turns off. With autoload
260
+ # the addresses and the length go back to where the transfer started,
261
+ # and otherwise they stay where it stopped.
262
+ def end_transfer
263
+ @running = false
264
+ @command = (@command & ~EXECUTE & 0xff) | NO_FF00_TRIGGER
265
+ events = @dma.events
266
+ @status |= events
267
+ @command.anybits?(AUTOLOAD) ? reload : keep_counters
268
+ interrupt_on(events)
269
+ end
270
+
271
+ def reload
272
+ @c64 = @c64_start
273
+ @expansion = @expansion_start
274
+ @bank = @bank_start
275
+ @length = @length_start
276
+ end
277
+
278
+ def keep_counters
279
+ @c64 = @dma.c64 if @address_control.nobits?(FIX_C64)
280
+ @length = @dma.length & 0xffff
281
+ return if @address_control.anybits?(FIX_EXPANSION)
282
+
283
+ address = @dma.expansion & @address_mask
284
+ @expansion = address & 0xffff
285
+ @bank = (address >> 16) & 0xff
286
+ end
287
+ end
288
+ end
289
+ end
@@ -0,0 +1,120 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ class C64
5
+ # The true drives on the machine's serial bus: a Drives::Model1541, and a
6
+ # Drives::Model1581 (Drives::Model1581::Slot), each in a field of its own. C64,
7
+ # Vic20 and C128 each include it.
8
+ module TrueDrives
9
+ include Drives::Model1581::Slot
10
+
11
+ # The true drive models a snapshot names.
12
+ DRIVE_MODELS = %w[1541 1571 1581].freeze
13
+
14
+ # Plugs in a Drives::Model1541, which then runs alongside the machine on its
15
+ # own clock and talks to it over the serial bus. The serial traps stop
16
+ # answering the drive's device number, so the KERNAL's TALK, LISTEN and
17
+ # byte transfers reach the drive. The LOAD and SAVE traps stay, and
18
+ # still serve a mounted image.
19
+ def attach_drive1541(drive)
20
+ @iec_bus.detach(@drive1541) if @drive1541
21
+ drive.host_clock_hz = timing.clock_hz
22
+ @drive1541 = drive
23
+ drive.connect(@iec_bus)
24
+ @serial_trap&.device = serial_trap_device
25
+ drive1541_attached
26
+ end
27
+
28
+ # The true drive on the serial bus: the Drives::Model1581 ahead of the
29
+ # Drives::Model1541, or nil.
30
+ def true_drive = @drive1581 || @drive1541
31
+
32
+ # Plugs a Drives::Model1541 in as device 8, in place of a 1581 there, and
33
+ # returns it.
34
+ def plug_true_drive
35
+ detach_drive1581 if drive1581_on_device8?
36
+ Drives::Model1541.new.tap { |drive| attach_drive1541(drive) }
37
+ end
38
+
39
+ # The true drives as keyed snapshot modules, each named from its device
40
+ # number: BL-D8, BL-D8-CPU and so on for device 8.
41
+ def save_drive_modules(sections)
42
+ drive1541 = @drive1541
43
+ drive1541&.save_modules(sections, "BL-D#{drive1541.device}")
44
+ drive1581 = @drive1581
45
+ drive1581&.save_modules(sections, "BL-D#{drive1581.device}")
46
+ save_drive1571_modules(sections)
47
+ sections
48
+ end
49
+
50
+ # The true drives a snapshot's modules hold, built afresh unless the
51
+ # same model is on the bus as the same device. A drive the snapshot
52
+ # doesn't have comes off the bus.
53
+ def load_drive_modules(modules)
54
+ models = []
55
+ 8.upto(11) do |device|
56
+ input = modules.optional("BL-D#{device}")
57
+ next unless input
58
+
59
+ model = input.choice("model", DRIVE_MODELS, "1541")
60
+ load_drive_model(modules, input, device, model)
61
+ models << model
62
+ end
63
+ detach_drive1541 unless models.include?("1541")
64
+ detach_drive1581 unless models.include?("1581")
65
+ detach_drive1571 unless models.include?("1571")
66
+ end
67
+
68
+ # Unplugs the Drives::Model1541, leaving the serial bus with nothing on it.
69
+ def detach_drive1541
70
+ return unless @drive1541
71
+
72
+ @iec_bus.detach(@drive1541)
73
+ @drive1541 = nil
74
+ @serial_trap&.device = serial_trap_device
75
+ end
76
+
77
+ private
78
+
79
+ # The device number the serial traps answer: device 8, unless a true
80
+ # drive is on the bus as device 8.
81
+ def serial_trap_device
82
+ device = KernalTrap::Routine::DEVICE
83
+ @drive1541&.device == device || drive1581_on_device8? ? nil : device
84
+ end
85
+
86
+ # What the machine does as a 1541 goes on its bus. The VIC-20 waits
87
+ # for its DOS to boot before typing.
88
+ def drive1541_attached = nil
89
+
90
+ def load_drive_model(modules, input, device, model)
91
+ case model
92
+ when "1581" then load_drive1581_modules(modules, input, device)
93
+ when "1571" then load_drive1571_modules(modules, input, device)
94
+ else load_drive1541_modules(modules, input, device)
95
+ end
96
+ end
97
+
98
+ def load_drive1541_modules(modules, input, device)
99
+ detach_drive1541 if @drive1541 && @drive1541.device != device
100
+ attach_drive1541(Drives::Model1541.new(device:)) unless @drive1541
101
+ @drive1541.load_modules(modules, input, "BL-D#{device}")
102
+ end
103
+
104
+ def load_drive1581_modules(modules, input, device)
105
+ detach_drive1581 if @drive1581 && @drive1581.device != device
106
+ attach_drive1581(Drives::Model1581.new(device:)) unless @drive1581
107
+ @drive1581.load_modules(modules, input, "BL-D#{device}")
108
+ end
109
+
110
+ # The C128D's 1571, which only the C128 has (C128::Drives).
111
+ def save_drive1571_modules(sections) = sections
112
+
113
+ def load_drive1571_modules(_modules, input, _device)
114
+ input.adjust("a 1571 doesn't plug into this machine, left out")
115
+ end
116
+
117
+ def detach_drive1571 = nil
118
+ end
119
+ end
120
+ end
@@ -1,20 +1,21 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "badline/computer/common"
4
- require "badline/computer/true_drives"
5
- require "badline/computer/attachments"
6
- require "badline/computer/kernal_traps"
7
- require "badline/computer/saved_state"
3
+ require "badline/c64/common"
4
+ require "badline/c64/true_drives"
5
+ require "badline/c64/attachments"
6
+ require "badline/c64/kernal_traps"
7
+ require "badline/c64/keyed_state"
8
+ require "badline/c64/keyed_modules"
8
9
 
9
10
  module Badline
10
- class Computer
11
+ class C64
11
12
  include IntegerHelper
12
- include KeyboardBuffer
13
+ include Machine::KeyboardBuffer
13
14
  include Machine::Clocking
15
+ include Machine::Settling
14
16
  include Common
15
17
  include Attachments
16
18
  include KernalTraps
17
- include SavedState
18
19
 
19
20
  attr_reader :address_bus, :cpu, :cycles, :drive1541
20
21
 
@@ -32,6 +33,11 @@ module Badline
32
33
 
33
34
  def ram = address_bus.ram
34
35
 
36
+ # Reads and writes memory as the CPU sees it, through the bus.
37
+ def peek(addr) = address_bus.peek(addr)
38
+
39
+ def poke(addr, value) = address_bus.poke(addr, value)
40
+
35
41
  def keyboard = address_bus.keyboard
36
42
 
37
43
  def joystick1 = address_bus.joystick1
@@ -45,18 +51,18 @@ module Badline
45
51
  def install_debug_register(&) = address_bus.install_debug_register(&)
46
52
 
47
53
  # The machine options (sid_model:, cia_model:, vic_model:, region: and
48
- # ram_expansion:) configure the AddressBus. The region sets the clock,
54
+ # ram_expansion:) configure the C64::Bus. The region sets the clock,
49
55
  # the VIC's raster and sprite timing, and the mains frequency the CIAs'
50
56
  # TOD clocks count. The KERNAL tells PAL from NTSC by the raster, so
51
57
  # every region boots the same ROMs. reu plugs an REU of that many K
52
58
  # into the expansion port, where it drives the IRQ line and takes the
53
59
  # bus for its transfers. kernal:, datasette: and board: fit the bus
54
- # afterwards (AddressBus::Fittings): the KERNAL ROM, the cassette port,
60
+ # afterwards (C64::Bus::Fittings): the KERNAL ROM, the cassette port,
55
61
  # which is empty on the SX-64, and the board and case.
56
62
  def initialize(debug: false, reu: nil, **machine)
57
- @address_bus = AddressBus.new(**machine.except(*AddressBus::Fittings::NAMES))
58
- @address_bus.fit(**machine.slice(*AddressBus::Fittings::NAMES))
59
- @cpu = CPU.new(@address_bus, debug:)
63
+ @address_bus = Bus.new(**machine.except(*Bus::Fittings::NAMES))
64
+ @address_bus.fit(**machine.slice(*Bus::Fittings::NAMES))
65
+ @cpu = Chips::CPU.new(@address_bus, debug:)
60
66
  @vic = @address_bus.vic
61
67
  @vic.open_bus = -> { @address_bus.ram.peek(@cpu.program_counter) }
62
68
  @cia1 = @address_bus.cia1
@@ -73,7 +79,7 @@ module Badline
73
79
  @freeze_writes = 0
74
80
  @init_handlers = []
75
81
  @pending_keys = nil
76
- @drive = nil
82
+ @dos = nil
77
83
  @serial_trap = nil
78
84
  @save_trap = nil
79
85
  @drive1541 = nil
@@ -145,7 +151,7 @@ module Badline
145
151
  address_bus.cartridge&.reset
146
152
  @reu&.reset!
147
153
  @dma = false
148
- @drive&.reset!
154
+ @dos&.reset!
149
155
  @drive1541&.reset!
150
156
  @drive1581&.reset!
151
157
  @freezing = false
@@ -183,6 +189,9 @@ module Badline
183
189
  # The KERNAL the traps stand in for (KernalTraps#mount).
184
190
  def trap_layout = KernalTrap::C64_LAYOUT
185
191
 
192
+ # The KERNAL's keyboard buffer, which #type_text feeds.
193
+ def keyboard_buffer = Machine::KeyboardBuffer::C64_LAYOUT
194
+
186
195
  def trap_bus = address_bus
187
196
 
188
197
  def plug_reu(size_kb)
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ module Chips
5
+ class CIA
6
+ # Running a CIA a stretch of cycles at once, for Drives::Engine::Idle.
7
+ module FastForward
8
+ # quiet_cycles for a CIA whose counters will never set a flag.
9
+ QUIET = 1 << 40
10
+
11
+ # Whether fast_forward can run the CIA: timer B stopped with nothing
12
+ # in its pipeline, no interrupt or acknowledge on its way, CNT where it
13
+ # was, the TOD clock stopped, and timer A either stopped, or free
14
+ # running (Timer#free_running?) with its interrupt masked and the
15
+ # serial port an idle input. Drives::Engine::Idle sleeps the 1571's CIA through
16
+ # such cycles, which its DOS leaves timer A running in.
17
+ def quiet?
18
+ return false unless @tb.idle? && @icr.quiet && @tod.quiet? && cnt_steady?
19
+
20
+ @ta.idle? ? @serial.idle : timer_a_free_running?
21
+ end
22
+
23
+ # How many cycles the CIA can run with nothing but its counters
24
+ # moving: as quiet? allows, but with timer B free running too, up to
25
+ # the cycle before it underflows. The 1581's DOS leaves timer B
26
+ # interrupting every 10 ms.
27
+ def quiet_cycles
28
+ return 0 unless @icr.quiet && @tod.quiet? && cnt_steady? && timer_a_quiet?
29
+ return QUIET if @tb.idle?
30
+ return 0 unless @tb.free_running? && @control_b.value.nobits?(0x60)
31
+
32
+ @tb.counter - 1
33
+ end
34
+
35
+ # Runs +cycles+ cycles at once while quiet? or for quiet_cycles, as
36
+ # cycle! would: timer A's underflows flag in the ICR, and reach the
37
+ # serial port's underflow line.
38
+ def fast_forward(cycles)
39
+ @tod.fast_forward(cycles)
40
+ @tb.fast_forward(cycles) unless @tb.idle?
41
+ return if @ta.idle?
42
+
43
+ @icr.flag(:timer_a) if @ta.fast_forward(cycles).positive?
44
+ @serial.follow_underflow(@ta.underflowed)
45
+ end
46
+
47
+ private
48
+
49
+ def timer_a_quiet? = @ta.idle? ? @serial.idle : timer_a_free_running?
50
+
51
+ # Timer A free running with its interrupt masked, and the serial port
52
+ # an idle input it only clocks.
53
+ def timer_a_free_running?
54
+ @ta.free_running? && !@control_a.serial_mode? && @icr.mask.value.nobits?(0x01) && @serial.drained?
55
+ end
56
+
57
+ # Whether CNT holds the level the last cycle sampled.
58
+ def cnt_steady? = @serial.cnt == @cnt_high && !@cnt_rise
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,128 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ module Chips
5
+ class CIA
6
+ # The interrupt control register: the source flags, their mask and the
7
+ # IR bit that drives the interrupt line, with the 6526's acknowledge
8
+ # timing.
9
+ class InterruptRegister
10
+ attr_reader :status, :mask, :quiet
11
+
12
+ # The 6526A is the faster of the two: see the rules below.
13
+ def initialize(model = :mos6526)
14
+ @fast = model == :mos6526a
15
+ @mask = InterruptFlags.new([:timer_a, :timer_b, :alarm, :serial, :flag, 0, 0, 0])
16
+ @status = InterruptFlags.new([:timer_a, :timer_b, :alarm, :serial, :flag, 0, 0, :interrupt])
17
+ @pending = 0
18
+ @read = @read_last_cycle = @read_two_cycles_ago = nil
19
+ @timer_b_bug = false
20
+ @quiet = true
21
+ end
22
+
23
+ # The CIA skips this while quiet: no read is recent enough to matter
24
+ # and no assert is due.
25
+ def cycle!
26
+ @read_two_cycles_ago = @read_last_cycle
27
+ @read_last_cycle = @read
28
+ @read = nil
29
+ if @pending.positive?
30
+ @pending -= 1
31
+ status.interrupt = true if @pending.zero?
32
+ end
33
+ @quiet = @pending.zero? && @read_last_cycle.nil? && @read_two_cycles_ago.nil?
34
+ end
35
+
36
+ def interrupted? = status.value >= 0x80
37
+
38
+ # Raises IR on this cycle.
39
+ def assert_now
40
+ status.interrupt = true
41
+ @pending = 0
42
+ end
43
+
44
+ # Schedules IR to rise after delay cycles, keeping an earlier one.
45
+ def assert!(delay = 1)
46
+ @pending = @pending.positive? ? [@pending, delay].min : delay
47
+ @quiet = false
48
+ end
49
+
50
+ # Latch a source, pulling the interrupt line if it is armed. The 6526
51
+ # raises IR a cycle after the flag. The 6526A raises it on the flag's
52
+ # own cycle, unless the ICR was read on the cycle before.
53
+ def flag(source)
54
+ bit = source_bit(source)
55
+ @status.value |= bit
56
+ return unless mask.value.anybits?(bit)
57
+
58
+ @fast && @read_last_cycle.nil? ? assert_now : assert!
59
+ end
60
+
61
+ # The 6526 timer B bug: an underflow on the cycle after a read still
62
+ # raises the flag, but the next read drops it unseen. The 6526A has
63
+ # no such bug.
64
+ def timer_b_underflow!
65
+ @timer_b_bug = !@fast && !@read_last_cycle.nil?
66
+ flag(:timer_b)
67
+ end
68
+
69
+ # A read releases the interrupt line at once, and cancels an assert
70
+ # due on the next cycle, but its acknowledge lands a cycle late. On
71
+ # the 6526 that holds only IR: the next cycle still reads IR set if it
72
+ # was set, or about to be. The 6526A reads an IR about to rise as set
73
+ # at once, and holds every bit it read: the next cycle reads them all
74
+ # again, along with anything flagged since.
75
+ def read
76
+ status.timer_b = false if @timer_b_bug
77
+ @timer_b_bug = false
78
+ value = status.value
79
+ value |= 0x80 if @fast && @pending == 1
80
+ @read = @pending == 1 ? value | 0x80 : value
81
+ @quiet = false
82
+ value |= held(@read_last_cycle) if @read_last_cycle
83
+ status.value = 0x0
84
+ @pending = 0
85
+ value
86
+ end
87
+
88
+ # Bit 7 picks between setting and clearing the mask bits given.
89
+ def write(value)
90
+ if value.nobits?(0x80)
91
+ @mask.value &= ~(value & 0x1f)
92
+ else
93
+ @mask.value |= (value & 0x1f)
94
+ end
95
+ if mask.value.anybits?(status.value & 0x1f)
96
+ assert!(arm_delay) unless interrupted?
97
+ elsif @pending == 1 && @read_two_cycles_ago
98
+ # On the 6526, masking the pending source cancels the assert due
99
+ # on the next cycle only while a read two cycles back is
100
+ # acknowledging.
101
+ @pending = 0
102
+ end
103
+ end
104
+
105
+ private
106
+
107
+ # Arming a pending source raises IR two cycles later on the 6526,
108
+ # and one cycle later on the 6526A.
109
+ def arm_delay = @fast ? 1 : 2
110
+
111
+ # What a read on the cycle before still holds: IR on the 6526, and
112
+ # every bit it read on the 6526A.
113
+ def held(last) = @fast ? last : last & 0x80
114
+
115
+ def source_bit(source)
116
+ case source
117
+ when :timer_a then 0x01
118
+ when :timer_b then 0x02
119
+ when :alarm then 0x04
120
+ when :serial then 0x08
121
+ when :flag then 0x10
122
+ else raise ArgumentError, "unknown interrupt source #{source}"
123
+ end
124
+ end
125
+ end
126
+ end
127
+ end
128
+ end