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
@@ -5,7 +5,7 @@ module Badline
5
5
  # A .g64 image: a 1541 disk's flux as raw GCR, track by track, for the
6
6
  # true drive to read the way it reads a real disk, copy protection and
7
7
  # all. It has no file system of its own, so only the true drive
8
- # (Drive1541::Disk.from_g64) can use it. A .g71 holds a 1571 disk the
8
+ # (Drives::Model1541::Disk.from_g64) can use it. A .g71 holds a 1571 disk the
9
9
  # same way, with "GCR-1571" for its signature and 168 half tracks,
10
10
  # the first side's 84 and then the second's.
11
11
  #
@@ -21,7 +21,7 @@ module Badline
21
21
  # A larger one is the offset of a speed map, two bits a byte, four
22
22
  # track bytes to a map byte, the first byte's in the top two bits, for
23
23
  # a track whose zone changes along it. The drive passes each byte
24
- # under the head at its own zone's rate (Drive1541::Track).
24
+ # under the head at its own zone's rate (Drives::Model1541::Track).
25
25
  #
26
26
  # The drive writes a track at one zone, so a track written back takes
27
27
  # that zone as its speed entry. Writing it puts it in its own block,
@@ -66,15 +66,20 @@ module Badline
66
66
  [bytes.length].pack("v") + bytes.pack("C*").ljust(size, "\x00".b)
67
67
  end
68
68
 
69
- attr_reader :half_tracks, :max_track_size
69
+ attr_reader :half_tracks, :max_track_size, :path
70
70
 
71
- def initialize(path, read_only: false)
71
+ # `bytes` stands in for the host file's contents, as a snapshot
72
+ # restores the image as it was.
73
+ def initialize(path, read_only: false, bytes: nil)
72
74
  @path = path
73
75
  @read_only = read_only
74
- @data = File.binread(path)
76
+ @data = bytes ? bytes.pack("C*") : File.binread(path)
75
77
  parse
76
78
  end
77
79
 
80
+ # The image as its file holds it.
81
+ def contents = @data.bytes
82
+
78
83
  # Whether the image was opened write-protected.
79
84
  def read_only? = @read_only
80
85
 
@@ -17,11 +17,6 @@ module Badline
17
17
  @path = path
18
18
  end
19
19
 
20
- # What Storage.reopen needs to open the directory again.
21
- def save_setup(out)
22
- out.int(HOST_DIRECTORY).string(File.expand_path(@path))
23
- end
24
-
25
20
  def read_file(name, **)
26
21
  entry = find(name)
27
22
  return unless entry
@@ -20,14 +20,8 @@ module Badline
20
20
  raise FormatError, "Missing T64 signature" unless @bytes[0, 3] == SIGNATURE
21
21
  end
22
22
 
23
- # The host file's path, as it was opened.
24
- attr_reader :path
25
-
26
- # What Storage.reopen needs to open the archive again: its path and
27
- # its bytes.
28
- def save_setup(out)
29
- out.int(T64_ARCHIVE).string(File.expand_path(@path)).blob(@bytes)
30
- end
23
+ # The host file's path, as it was opened, and its bytes.
24
+ attr_reader :path, :bytes
31
25
 
32
26
  def read_file(name, **)
33
27
  pattern = Storage.matcher(name)
@@ -5,11 +5,14 @@ module Badline
5
5
  # Raw datasette pulse stream. The header holds the signature, the
6
6
  # version at $0c, the platform at $0d (0 C64, 1 VIC-20, 2 C16) and the
7
7
  # data size at $10; the data runs from $14, every byte the time to the
8
- # next falling edge on the tape read line, in units of 8 cycles.
8
+ # next falling edge on the tape read line, in units of 8 cycles. A
9
+ # version 2 tape, the C16's and the Plus/4's, times half waves instead,
10
+ # so it takes two bytes to the next falling edge.
9
11
  class TAP
10
12
  class FormatError < StandardError; end
11
13
 
12
14
  SIGNATURE = "C64-TAPE-RAW".bytes.freeze
15
+ C16_SIGNATURE = "C16-TAPE-RAW".bytes.freeze
13
16
  HEADER_SIZE = 0x14
14
17
  OVERFLOW = 0x100 * 8
15
18
 
@@ -28,10 +31,11 @@ module Badline
28
31
  def initialize(path, bytes: nil)
29
32
  @path = File.expand_path(path)
30
33
  @bytes = bytes || File.binread(path).bytes
31
- raise FormatError, "Missing TAP signature" unless @bytes[0, SIGNATURE.length] == SIGNATURE
34
+ signature = @bytes[0, SIGNATURE.length]
35
+ raise FormatError, "Missing TAP signature" unless [SIGNATURE, C16_SIGNATURE].include?(signature)
32
36
 
33
37
  @version = @bytes[0x0c].to_i
34
- raise FormatError, "Unsupported TAP version #{@version}" if @version > 1
38
+ raise FormatError, "Unsupported TAP version #{@version}" if @version > 2
35
39
 
36
40
  @platform = @bytes[0x0d].to_i
37
41
 
@@ -55,17 +59,16 @@ module Badline
55
59
  # Records a pulse `cycles` long at the position, in place of the rest
56
60
  # of the tape: a byte of units of 8 cycles, or a zero and the 24-bit
57
61
  # cycle count for a long one (a lone zero, an overflow, on a version 0
58
- # tape).
62
+ # tape). A version 2 tape records it as two equal halves.
59
63
  def record_pulse(cycles)
60
- units = (cycles + 4) / 8
61
- encoded = if units.between?(1, 0xff) then [units]
62
- elsif version.zero? then [0]
63
- else
64
- count = [cycles, 0xffffff].min
65
- [0, count & 0xff, (count >> 8) & 0xff, count >> 16]
66
- end
67
64
  @bytes.slice!(@pos..)
68
- @bytes.concat(encoded)
65
+ if version == 2
66
+ half = cycles / 2
67
+ @bytes.concat(encode(cycles - half))
68
+ @bytes.concat(encode(half))
69
+ else
70
+ @bytes.concat(encode(cycles))
71
+ end
69
72
  @pos = @end = @bytes.length
70
73
  end
71
74
 
@@ -77,20 +80,45 @@ module Badline
77
80
  File.binwrite(@path, @bytes.pack("C*"))
78
81
  end
79
82
 
83
+ # Whether each byte times a half wave, as on a version 2 tape.
84
+ def half_waves? = version == 2
85
+
80
86
  # Cycles until the next falling edge, or nil once the tape has run out.
87
+ # On a version 2 tape that is two half waves, or one left at the end.
81
88
  def next_pulse
82
89
  return if end?
90
+ return next_time unless half_waves?
91
+
92
+ first = next_time
93
+ end? ? first : first + next_time
94
+ end
95
+
96
+ # Cycles until the read line's next edge on a version 2 tape, or nil
97
+ # once the tape has run out.
98
+ def next_half_wave
99
+ next_time unless end?
100
+ end
83
101
 
102
+ private
103
+
104
+ def next_time
84
105
  byte = @bytes[@pos]
85
106
  @pos += 1
86
107
  return byte * 8 unless byte.zero?
87
108
 
88
- # $00 is an overflow in version 0 and a 24-bit cycle count in
89
- # version 1.
109
+ # $00 is an overflow in version 0 and a 24-bit cycle count from
110
+ # version 1 on.
90
111
  version.zero? ? OVERFLOW : long_pulse
91
112
  end
92
113
 
93
- private
114
+ def encode(cycles)
115
+ units = (cycles + 4) / 8
116
+ return [units] if units.between?(1, 0xff)
117
+ return [0] if version.zero?
118
+
119
+ count = [cycles, 0xffffff].min
120
+ [0, count & 0xff, (count >> 8) & 0xff, count >> 16]
121
+ end
94
122
 
95
123
  # The stated size disagrees with the file in the wild, and a few dumps
96
124
  # leave it zeroed altogether.
@@ -3,7 +3,7 @@
3
3
  module Badline
4
4
  module Storage
5
5
  # Stands in for a directory a detached restore leaves out
6
- # (Snapshot::StateReader): it finds no files and refuses every write
6
+ # (Snapshot::StorageSetup): it finds no files and refuses every write
7
7
  # as a write-protected disk does.
8
8
  class Unavailable
9
9
  def path = ""
@@ -11,10 +11,6 @@ require "badline/storage/g64_image"
11
11
  require "badline/storage/t64"
12
12
  require "badline/storage/tap"
13
13
  require "badline/storage/crt_file"
14
- require "badline/storage/sid_file"
15
- require "badline/storage/hvsc"
16
- require "badline/storage/song_lengths"
17
- require "badline/storage/stil"
18
14
  require "badline/storage/unavailable"
19
15
 
20
16
  module Badline
@@ -36,29 +32,7 @@ module Badline
36
32
  end
37
33
  end
38
34
 
39
- # The storage a snapshot's device 8 serves, by the number save_setup
40
- # writes first.
41
- HOST_DIRECTORY = 0
42
- D64 = 1
43
- D71 = 2
44
- D81 = 3
45
- T64_ARCHIVE = 4
46
-
47
35
  class << self
48
- # Opens the storage a save_setup wrote: the same directory by its
49
- # path, an archive with the bytes it held, or a disk image with the
50
- # bytes it held, writes and all. A detached reader gets the image
51
- # write-protected, and Unavailable for a directory.
52
- def reopen(input)
53
- kind = input.int
54
- path = input.string
55
- case kind
56
- when HOST_DIRECTORY then input.detached? ? Unavailable.new : HostDirectory.new(path)
57
- when T64_ARCHIVE then T64.new(path, bytes: input.blob)
58
- else reopen_image(kind, path, input)
59
- end
60
- end
61
-
62
36
  # Folds shifted PETSCII letters to their ASCII equivalents.
63
37
  def ascii(bytes)
64
38
  bytes.map { |b| b.between?(0xc1, 0xda) ? b - 0x80 : b }.pack("C*")
@@ -83,17 +57,6 @@ module Badline
83
57
  [base.to_s, FILE_TYPES[type.to_s.strip[0]&.upcase]]
84
58
  end
85
59
 
86
- def reopen_image(kind, path, input)
87
- read_only = input.boolean? || input.detached?
88
- bytes = input.blob
89
- case kind
90
- when D64 then D64Image.new(path, read_only:, bytes:)
91
- when D71 then D71Image.new(path, read_only:, bytes:)
92
- when D81 then D81Image.new(path, read_only:, bytes:)
93
- else raise Snapshot::FormatError, "unknown storage #{kind} in the state"
94
- end
95
- end
96
-
97
60
  # CBM-style filename pattern: "*" and "?" wildcards, case-insensitive.
98
61
  # The DOS stops comparing at the first "*", so whatever follows it
99
62
  # is ignored.
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ module Support
5
+ # A Checkpoint of a C128, in either mode: a C64's components, with the
6
+ # whole 128K of RAM in place of bank 0 and the colour RAM bank the 8502
7
+ # sees, and besides them the VDC's registers and beam, the VDC's RAM, the
8
+ # MMU's registers and the Z80.
9
+ module C128Checkpoint
10
+ COMPONENTS = %w[cpu ram color_ram display vic cia1 cia2 sid vdc vdc_ram mmu z80].freeze
11
+
12
+ def self.take(machine)
13
+ Checkpoint.new(machine.cycles, [
14
+ Checkpoint.fnv1a(Checkpoint.cpu_state(machine.cpu)),
15
+ Checkpoint.fnv1a(Checkpoint.ram(machine.ram, 0x20000)),
16
+ Checkpoint.fnv1a(Checkpoint.color_ram(machine.address_bus.color_ram, 0xd800)),
17
+ Checkpoint.fnv1a(machine.vic.display),
18
+ Checkpoint.fnv1a(Checkpoint.vic_state(machine.vic)),
19
+ Checkpoint.fnv1a(Checkpoint.cia_state(machine.cia1)),
20
+ Checkpoint.fnv1a(Checkpoint.cia_state(machine.cia2)),
21
+ Checkpoint.fnv1a(Array.new(0x20) { |reg| machine.sid.register(reg) }),
22
+ Checkpoint.fnv1a(machine.vdc.registers + [machine.vdc.frame]),
23
+ Checkpoint.fnv1a(machine.vdc.ram),
24
+ Checkpoint.fnv1a(machine.mmu.registers),
25
+ Checkpoint.fnv1a(z80_state(machine.z80))
26
+ ], COMPONENTS)
27
+ end
28
+
29
+ def self.parse(line) = Checkpoint.parse(line, COMPONENTS)
30
+
31
+ def self.z80_state(z80)
32
+ [z80.pc, z80.sp, z80.af, z80.bc, z80.de, z80.hl, z80.ix, z80.iy, z80.af_alt, z80.bc_alt, z80.de_alt,
33
+ z80.hl_alt, z80.i, z80.r, z80.im, flag(z80.iff1), flag(z80.iff2), flag(z80.halted), z80.cycles]
34
+ end
35
+
36
+ def self.flag(value) = value == true ? 1 : 0
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,108 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ module Support
5
+ # A digest of the machine's state at one cycle, for comparing two runs of
6
+ # the same scenario, on two builds or two revisions. Each component is
7
+ # hashed on its own with 32-bit FNV-1a, so a mismatch names the part of the
8
+ # machine that went its own way. Nothing is read through a chip's bus
9
+ # interface, so taking a checkpoint leaves the machine as it was.
10
+ #
11
+ # Checkpoint.take digests a C64's components, COMPONENTS.
12
+ # Vic20Checkpoint and C128Checkpoint digest the other families', each
13
+ # with a list of its own, which a checkpoint carries as +components+.
14
+ #
15
+ # Colour RAM is digested as the four bits each cell stores: the upper
16
+ # nibble a CPU read sees comes off the open bus.
17
+ class Checkpoint
18
+ COMPONENTS = %w[cpu ram color_ram display vic cia1 cia2 sid].freeze
19
+
20
+ FNV_OFFSET = 0x811c9dc5
21
+ FNV_PRIME = 0x01000193
22
+
23
+ attr_reader :cycle, :digests, :components
24
+
25
+ class << self
26
+ def take(computer)
27
+ new(computer.cycles, [
28
+ fnv1a(cpu_state(computer.cpu)),
29
+ fnv1a(ram(computer.ram, 0x10000)),
30
+ fnv1a(color_ram(computer.address_bus.color_ram, 0xd800)),
31
+ fnv1a(computer.vic.display),
32
+ fnv1a(vic_state(computer.vic)),
33
+ fnv1a(cia_state(computer.cia1)),
34
+ fnv1a(cia_state(computer.cia2)),
35
+ fnv1a(Array.new(0x20) { |reg| computer.sid.register(reg) })
36
+ ])
37
+ end
38
+
39
+ # Reads a line written by #to_s, with a digest for each of
40
+ # `components`.
41
+ def parse(line, components = COMPONENTS)
42
+ fields = line.split
43
+ digests = components.map do |name|
44
+ field = fields.find { |f| f.start_with?("#{name}=") }
45
+ raise ArgumentError, "no #{name} digest in #{line.inspect}" unless field
46
+
47
+ field.split("=").last.to_i(16)
48
+ end
49
+ new(fields.first.to_i, digests, components)
50
+ end
51
+
52
+ def fnv1a(values)
53
+ hash = FNV_OFFSET
54
+ values.each { |value| hash = ((hash ^ (value & 0xffffffff)) * FNV_PRIME) & 0xffffffff }
55
+ hash
56
+ end
57
+
58
+ # The registers and cycle count of a 6502 or an 8502.
59
+ def cpu_state(cpu)
60
+ [cpu.program_counter, cpu.a, cpu.x, cpu.y, cpu.stack_pointer, cpu.p, cpu.cycles]
61
+ end
62
+
63
+ # The first `length` bytes of `memory`.
64
+ def ram(memory, length)
65
+ Array.new(length) { |addr| memory.peek(addr) }
66
+ end
67
+
68
+ # The 1K of colour RAM at `start`.
69
+ def color_ram(memory, start)
70
+ Array.new(0x400) { |offset| memory.nibble(start + offset) }
71
+ end
72
+
73
+ # A VIC-II's or a VIC-I's registers and beam position.
74
+ def vic_state(vic)
75
+ vic.register_file + [vic.rasterline, vic.column]
76
+ end
77
+
78
+ def cia_state(cia)
79
+ start = cia.start
80
+ [cia.peek(start), cia.peek(start + 1), cia.peek(start + 2), cia.peek(start + 3),
81
+ cia.timer_a, cia.timer_a_latch, cia.timer_b, cia.timer_b_latch, cia.serial.data,
82
+ cia.control_a.value, cia.control_b.value, cia.interrupt_status.value,
83
+ cia.interrupt_control.value] + cia.time_of_day.registers
84
+ end
85
+ end
86
+
87
+ def initialize(cycle, digests, components = COMPONENTS)
88
+ @cycle = cycle
89
+ @digests = digests
90
+ @components = components
91
+ end
92
+
93
+ # The components whose digests differ from `other`'s.
94
+ def differences(other)
95
+ components.each_index.reject { |i| digests[i] == other.digests[i] }.map { |i| components[i] }
96
+ end
97
+
98
+ def ==(other)
99
+ other.is_a?(Checkpoint) && cycle == other.cycle && components == other.components && digests == other.digests
100
+ end
101
+
102
+ def to_s
103
+ fields = components.each_index.map { |i| "#{components[i]}=#{digests[i].to_s(16).rjust(8, '0')}" }
104
+ "#{cycle} #{fields.join(' ')}"
105
+ end
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ module Support
5
+ # A Checkpoint of a VIC-20: its CPU, the 48K of RAM the bus decodes, the
6
+ # colour RAM, the VIC-I's display and registers, both VIAs and the
7
+ # sound. The sound's shift registers and noise LFSR move only while it
8
+ # records, so a run that wants them digested records it.
9
+ module Vic20Checkpoint
10
+ COMPONENTS = %w[cpu ram color_ram display vic via1 via2 sound].freeze
11
+
12
+ def self.take(machine)
13
+ Checkpoint.new(machine.cycles, [
14
+ Checkpoint.fnv1a(Checkpoint.cpu_state(machine.cpu)),
15
+ Checkpoint.fnv1a(Checkpoint.ram(machine.ram, 0xc000)),
16
+ Checkpoint.fnv1a(Checkpoint.color_ram(machine.bus.color_ram, 0)),
17
+ Checkpoint.fnv1a(machine.vic.display),
18
+ Checkpoint.fnv1a(Checkpoint.vic_state(machine.vic)),
19
+ Checkpoint.fnv1a(via_state(machine.via1)),
20
+ Checkpoint.fnv1a(via_state(machine.via2)),
21
+ Checkpoint.fnv1a(sound_state(machine.sound))
22
+ ], COMPONENTS)
23
+ end
24
+
25
+ def self.parse(line) = Checkpoint.parse(line, COMPONENTS)
26
+
27
+ def self.via_state(via)
28
+ [via.port_a_output, via.port_b_output, via.timer1, via.timer1_latch, via.timer2,
29
+ via.shift_register.data, via.acr, via.pcr, via.interrupt_flags, via.interrupt_enable]
30
+ end
31
+
32
+ def self.sound_state(sound)
33
+ [sound.volume, sound.shift_register(0), sound.shift_register(1), sound.shift_register(2),
34
+ sound.shift_register(3), sound.lfsr]
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "badline/support/checkpoint"
4
+ require "badline/support/vic20_checkpoint"
5
+ require "badline/support/c128_checkpoint"
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Badline
4
- VERSION = "0.12.1"
4
+ VERSION = "0.14.0"
5
5
  end
@@ -4,11 +4,11 @@ module Badline
4
4
  class Vic20
5
5
  # What plugs into the VIC-20 besides its chips: cartridge ROM in the
6
6
  # expansion blocks, the disk device 8 serves through the KERNAL traps
7
- # (Computer::KernalTraps), and a true 1541 or 1581 on the serial bus
8
- # (Computer::TrueDrives).
7
+ # (C64::KernalTraps), and a true 1541 or 1581 on the serial bus
8
+ # (C64::TrueDrives).
9
9
  module Attachments
10
- include Computer::TrueDrives
11
- include Computer::KernalTraps
10
+ include C64::TrueDrives
11
+ include C64::KernalTraps
12
12
 
13
13
  # The cycle by which a 1541 switched on with the machine has run its
14
14
  # DOS's reset, the RAM test and the ROM checksum, and waits on the
@@ -34,6 +34,9 @@ module Badline
34
34
  # The VIC-20's KERNAL, which the traps stand in for.
35
35
  def trap_layout = KernalTrap::VIC20_LAYOUT
36
36
 
37
+ # The VIC-20's KERNAL keeps its keyboard buffer where the C64's does.
38
+ def keyboard_buffer = Machine::KeyboardBuffer::C64_LAYOUT
39
+
37
40
  def trap_bus = @bus
38
41
 
39
42
  # A drive that powers on with the machine takes longer to boot than
@@ -46,7 +49,7 @@ module Badline
46
49
  # A 1581 plugged in, as a 1541 does, holds the machine's on_init
47
50
  # handlers back until its DOS has booted.
48
51
  def drive1581_attached
49
- @init_threshold = [@init_threshold, Drive1581::BOOT_CYCLES].max if @cycles < @init_threshold
52
+ @init_threshold = [@init_threshold, Drives::Model1581::BOOT_CYCLES].max if @cycles < @init_threshold
50
53
  end
51
54
  end
52
55
  end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ class Vic20
5
+ class Bus
6
+ class ColorRAM
7
+ # The cells in a keyed snapshot module, under `key`.
8
+ def save_fields(out, key) = out.bytes(key, @cells)
9
+
10
+ def load_fields(input, key) = input.bytes_into(key, @cells)
11
+ end
12
+
13
+ # The bytes each side's data lines hold, under `prefix`, and a
14
+ # cartridge's ROM chips as rom<N>.start and rom<N>.bytes, in a keyed
15
+ # snapshot module (Snapshot::Keyed). The pages are mapped again once
16
+ # they are loaded.
17
+ module KeyedState
18
+ def save_fields(out, prefix)
19
+ out.int("#{prefix}data", @data).int("#{prefix}video_data", @video_data)
20
+ end
21
+
22
+ def load_fields(input, prefix)
23
+ @data = input.byte("#{prefix}data", @data)
24
+ @video_data = input.byte("#{prefix}video_data", @video_data)
25
+ end
26
+
27
+ # Whether a cartridge's ROM chips are in the expansion blocks.
28
+ def cartridge? = !@roms.empty?
29
+
30
+ def save_rom_fields(out)
31
+ out.int("roms", @roms.length)
32
+ @roms.each_with_index do |rom, number|
33
+ out.int("rom#{number}.start", rom.start)
34
+ rom.save_fields(out, "rom#{number}.bytes")
35
+ end
36
+ end
37
+
38
+ def load_rom_fields(input)
39
+ @roms = []
40
+ input.int("roms", 0).times do |number|
41
+ bytes = input.bytes("rom#{number}.bytes", "".b).bytes
42
+ @roms << ROM.new(bytes, length: bytes.length, start: input.word("rom#{number}.start", 0))
43
+ end
44
+ end
45
+
46
+ def restored! = map_pages
47
+ end
48
+
49
+ include KeyedState
50
+ end
51
+ end
52
+ end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "badline/vic20/bus/keyed_state"
4
+
3
5
  module Badline
4
6
  class Vic20
5
7
  # The CPU's bus:
@@ -215,31 +217,6 @@ module Badline
215
217
  @ram.clear!(RAM_POWER_ON)
216
218
  end
217
219
 
218
- # The RAM, the colour RAM, the byte each side's data lines hold and the
219
- # cartridge's ROM chips, for a snapshot. The expansion blocks are how
220
- # the machine was built.
221
- def save_state(out)
222
- out.marker("VIC20 BUS")
223
- @ram.save_state(out)
224
- out.blob(@color_ram.cells).int(@data).int(@video_data).int(@roms.length)
225
- @roms.each do |rom|
226
- out.int(rom.start)
227
- rom.save_state(out)
228
- end
229
- end
230
-
231
- def load_state(input)
232
- input.marker("VIC20 BUS")
233
- @ram.load_state(input)
234
- input.blob_into(@color_ram.cells)
235
- @data = input.int
236
- @video_data = input.int
237
- chips = Array.new(input.int) { [input.int, input.blob] }
238
- @roms = []
239
- chips.each { |chip| @roms << ROM.new(chip[1], length: chip[1].length, start: chip[0]) }
240
- map_pages
241
- end
242
-
243
220
  # Whether +block+ (BLOCKS) holds RAM.
244
221
  def ram?(block) = @blocks.include?(block)
245
222
 
@@ -4,7 +4,7 @@ module Badline
4
4
  class Vic20
5
5
  # The VIC-20's 6502, on the Vic20::Bus.
6
6
  class CPU
7
- include Badline::CPU::Core
7
+ include Badline::Chips::CPU::Core
8
8
  end
9
9
  end
10
10
  end
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ class Vic20
5
+ # The whole machine as keyed snapshot modules (Snapshot::Keyed), in the
6
+ # order a load applies them, as C64::KeyedModules: BL-MACHINE with the
7
+ # RAM expansion and the bytes each side's data lines hold under bus.*,
8
+ # the memories, a cartridge's ROM chips, the VIC with its sound, both
9
+ # VIAs, the CPU, the datasette, device 8 through the traps and the true
10
+ # drives.
11
+ module KeyedModules
12
+ def save_modules
13
+ bus = @bus
14
+ sections = [Snapshot::Keyed.section("BL-MACHINE") do |out|
15
+ save_machine_fields(out)
16
+ bus.save_fields(out, "bus.")
17
+ end]
18
+ sections << Snapshot::Keyed.section("BL-RAM") { |out| bus.ram.save_fields(out, "ram") }
19
+ sections << Snapshot::Keyed.section("BL-COLORRAM") { |out| bus.color_ram.save_fields(out, "ram") }
20
+ sections << Snapshot::Keyed.section("BL-CART") { |out| bus.save_rom_fields(out) } if bus.cartridge?
21
+ sections << Snapshot::Keyed.section("BL-CPU") { |out| @cpu.save_fields(out) }
22
+ sections << Snapshot::Keyed.section("BL-VICI") { |out| @vic.save_fields(out) }
23
+ sections << Snapshot::Keyed.section("BL-VIA1") { |out| @via1.save_fields(out) }
24
+ sections << Snapshot::Keyed.section("BL-VIA2") { |out| @via2.save_fields(out) }
25
+ sections << Snapshot::Keyed.section("BL-TAPE") { |out| @datasette.save_fields(out) }
26
+ sections << Snapshot::Keyed.section("BL-TRAPDOS") { |out| save_trap_dos_fields(out) } if mounted?
27
+ save_drive_modules(sections)
28
+ end
29
+
30
+ # Raises Snapshot::FormatError for the modules of a VIC-20 with
31
+ # another RAM expansion.
32
+ def load_modules(modules)
33
+ machine = modules.machine
34
+ check_snapshot_setup(Snapshot::Vic20Setup.read_fields(machine))
35
+ load_machine_fields(machine)
36
+ bus = @bus
37
+ bus.load_fields(machine, "bus.")
38
+ bus.ram.load_fields(modules.required("BL-RAM"), "ram")
39
+ bus.color_ram.load_fields(modules.required("BL-COLORRAM"), "ram")
40
+ bus.load_rom_fields(modules.optional("BL-CART") || Snapshot::Keyed::Reader.new("BL-CART"))
41
+ bus.restored!
42
+ @vic.load_fields(modules.required("BL-VICI"))
43
+ @via1.load_fields(modules.required("BL-VIA1"))
44
+ @via2.load_fields(modules.required("BL-VIA2"))
45
+ @cpu.load_fields(modules.required("BL-CPU"))
46
+ @datasette.load_fields(modules.required("BL-TAPE"))
47
+ trap_dos = modules.optional("BL-TRAPDOS")
48
+ trap_dos ? load_trap_dos_fields(trap_dos, detached: modules.detached?) : unmount
49
+ load_drive_modules(modules)
50
+ modules_restored!
51
+ self
52
+ end
53
+
54
+ private
55
+
56
+ def modules_restored!
57
+ @cpu.restored!
58
+ @vic.restored!
59
+ @via1.restored!
60
+ @via2.restored!
61
+ machine_restored!
62
+ end
63
+
64
+ def check_snapshot_setup(setup)
65
+ return if setup.ram == @ram_configuration
66
+
67
+ raise Snapshot::FormatError, "the snapshot is of a VIC-20 with #{setup.ram} RAM, not #{@ram_configuration}"
68
+ end
69
+ end
70
+
71
+ include KeyedModules
72
+ end
73
+ end