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,286 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ module Chips
5
+ class SID
6
+ class Waveform
7
+ # An oscillator's fields in a keyed snapshot module, under `prefix`:
8
+ # the phase, the noise register and its pipeline, the delayed
9
+ # shapers, the sync edge, the floating output and the pulse level.
10
+ # What the registers set comes from them on load.
11
+ module KeyedState
12
+ # The fields reSID keeps for an oscillator between cycles, for VICE's
13
+ # SIDEXTENDED module: the phase, the noise register, its shift pipeline
14
+ # and reset countdown, the floating output's countdown and the pulse
15
+ # level.
16
+ def resid_fields
17
+ [@accumulator, @shift_register, @shift_pipeline, @shift_register_reset, @floating_ttl, @pulse]
18
+ end
19
+
20
+ # Takes the fields resid_fields gives from `fields`, from `at` on.
21
+ def restore_resid_fields(fields, at)
22
+ @accumulator = fields[at]
23
+ @shift_register = fields[at + 1]
24
+ @shift_pipeline = fields[at + 2]
25
+ @shift_register_reset = fields[at + 3]
26
+ @floating_ttl = fields[at + 4]
27
+ @pulse = fields[at + 5]
28
+ @stale = true
29
+ end
30
+
31
+ def save_fields(out, prefix)
32
+ out.int("#{prefix}accumulator", @accumulator).int("#{prefix}shift_register", @shift_register)
33
+ out.int("#{prefix}shift_register_reset", @shift_register_reset)
34
+ out.int("#{prefix}shift_pipeline", @shift_pipeline).int("#{prefix}delayed_triangle", @delayed_triangle)
35
+ out.int("#{prefix}delayed_sawtooth", @delayed_sawtooth).bool("#{prefix}msb_rising", @msb_rising)
36
+ out.int("#{prefix}floating", @floating).int("#{prefix}floating_ttl", @floating_ttl)
37
+ out.int("#{prefix}pulse", @pulse)
38
+ end
39
+
40
+ def load_fields(input, prefix)
41
+ @accumulator = input.masked("#{prefix}accumulator", @accumulator, 0xffffff)
42
+ @shift_register = input.masked("#{prefix}shift_register", @shift_register, 0x7fffff)
43
+ @shift_register_reset = input.int("#{prefix}shift_register_reset", @shift_register_reset)
44
+ @shift_pipeline = input.int("#{prefix}shift_pipeline", @shift_pipeline)
45
+ @delayed_triangle = input.masked("#{prefix}delayed_triangle", @delayed_triangle, 0xfff)
46
+ @delayed_sawtooth = input.masked("#{prefix}delayed_sawtooth", @delayed_sawtooth, 0xfff)
47
+ @msb_rising = input.bool("#{prefix}msb_rising", @msb_rising)
48
+ @floating = input.masked("#{prefix}floating", @floating, 0xfff)
49
+ @floating_ttl = input.int("#{prefix}floating_ttl", @floating_ttl)
50
+ @pulse = input.masked("#{prefix}pulse", @pulse, 0xfff)
51
+ end
52
+
53
+ # The frequency, pulse width and control bits as the registers
54
+ # hold them, without the test bit's or the waveform change's
55
+ # effects on the noise register, which already happened.
56
+ def restore_registers(frequency, pulse_width, control)
57
+ @frequency = frequency
58
+ @pulse_width = pulse_width & 0xfff
59
+ @selected = (control >> 4) & 0x0f
60
+ @test = control.anybits?(0x08)
61
+ @ring = control.anybits?(0x04)
62
+ @sync = control.anybits?(0x02)
63
+ @stale = true
64
+ end
65
+ end
66
+
67
+ include KeyedState
68
+ end
69
+
70
+ class Envelope
71
+ # The envelope generator's fields in a keyed snapshot module, under
72
+ # `prefix`: the state, the counters and pipelines between them. The
73
+ # rates and the gate come from the registers on load.
74
+ module KeyedState
75
+ # The fields reSID keeps for an envelope between cycles, for VICE's
76
+ # SIDEXTENDED module: the rate and exponential counters, the level, the
77
+ # state as an index into STATES, whether it holds at zero, the two
78
+ # periods and the pipeline.
79
+ def resid_fields
80
+ [@rate_counter, @exponential_counter, @counter, STATES.index(@state).to_i, @hold_zero ? 1 : 0,
81
+ @rate_period, @exponential_period, @envelope_pipeline]
82
+ end
83
+
84
+ # Takes the fields resid_fields gives from `fields`, from `at` on.
85
+ def restore_resid_fields(fields, at)
86
+ @rate_counter = fields[at]
87
+ @exponential_counter = fields[at + 1]
88
+ @counter = @env3 = fields[at + 2]
89
+ state = fields[at + 3]
90
+ @state = @next_state = STATES.fetch(state.clamp(0, STATES.length - 1)).to_sym
91
+ @hold_zero = fields[at + 4] != 0
92
+ @rate_period = fields[at + 5]
93
+ @exponential_period = fields[at + 6]
94
+ @envelope_pipeline = fields[at + 7]
95
+ end
96
+
97
+ def save_fields(out, prefix)
98
+ out.text("#{prefix}state", @state.to_s).text("#{prefix}next_state", @next_state.to_s)
99
+ out.int("#{prefix}counter", @counter).int("#{prefix}env3", @env3)
100
+ out.int("#{prefix}rate_counter", @rate_counter)
101
+ out.int("#{prefix}rate_period", @rate_period).bool("#{prefix}reset_rate_counter", @reset_rate_counter)
102
+ out.int("#{prefix}exponential_counter", @exponential_counter)
103
+ out.int("#{prefix}exponential_period", @exponential_period)
104
+ out.int("#{prefix}state_pipeline", @state_pipeline).int("#{prefix}envelope_pipeline", @envelope_pipeline)
105
+ out.int("#{prefix}exponential_pipeline", @exponential_pipeline).bool("#{prefix}hold_zero", @hold_zero)
106
+ end
107
+
108
+ def load_fields(input, prefix)
109
+ @state = state_named(input.choice("#{prefix}state", STATE_NAMES, @state.to_s))
110
+ @next_state = state_named(input.choice("#{prefix}next_state", STATE_NAMES, @next_state.to_s))
111
+ @counter = input.byte("#{prefix}counter", @counter)
112
+ @env3 = input.byte("#{prefix}env3", @env3)
113
+ @rate_counter = input.masked("#{prefix}rate_counter", @rate_counter, 0x7fff)
114
+ @rate_period = input.int("#{prefix}rate_period", @rate_period)
115
+ @reset_rate_counter = input.bool("#{prefix}reset_rate_counter", @reset_rate_counter)
116
+ load_pipeline_fields(input, prefix)
117
+ end
118
+
119
+ # The rates and the gate as the registers hold them.
120
+ def restore_registers(attack_decay, sustain_release, control)
121
+ @attack = (attack_decay >> 4) & 0x0f
122
+ @decay = attack_decay & 0x0f
123
+ @sustain = (sustain_release >> 4) & 0x0f
124
+ @release = sustain_release & 0x0f
125
+ @gate = control.anybits?(0x01)
126
+ end
127
+
128
+ private
129
+
130
+ def load_pipeline_fields(input, prefix)
131
+ @exponential_counter = input.int("#{prefix}exponential_counter", @exponential_counter)
132
+ @exponential_period = input.int("#{prefix}exponential_period", @exponential_period)
133
+ @state_pipeline = input.int("#{prefix}state_pipeline", @state_pipeline)
134
+ @envelope_pipeline = input.int("#{prefix}envelope_pipeline", @envelope_pipeline)
135
+ @exponential_pipeline = input.int("#{prefix}exponential_pipeline", @exponential_pipeline)
136
+ @hold_zero = input.bool("#{prefix}hold_zero", @hold_zero)
137
+ end
138
+
139
+ def state_named(name)
140
+ case name
141
+ when "attack" then :attack
142
+ when "decay_sustain" then :decay_sustain
143
+ else :release
144
+ end
145
+ end
146
+ end
147
+
148
+ STATE_NAMES = STATES.map(&:to_s).freeze
149
+
150
+ include KeyedState
151
+ end
152
+
153
+ class Voice
154
+ # A voice's oscillator under `prefix`wave. and its envelope under
155
+ # `prefix`env.
156
+ module KeyedState
157
+ def save_fields(out, prefix)
158
+ @waveform.save_fields(out, "#{prefix}wave.")
159
+ @envelope.save_fields(out, "#{prefix}env.")
160
+ end
161
+
162
+ def load_fields(input, prefix)
163
+ @waveform.load_fields(input, "#{prefix}wave.")
164
+ @envelope.load_fields(input, "#{prefix}env.")
165
+ end
166
+ end
167
+
168
+ include KeyedState
169
+ end
170
+
171
+ class Filter
172
+ class External
173
+ # The output filter's integrators and output, under `prefix`.
174
+ module KeyedState
175
+ def save_fields(out, prefix)
176
+ out.int("#{prefix}lowpass", @lowpass).int("#{prefix}highpass", @highpass).int("#{prefix}output", @output)
177
+ end
178
+
179
+ def load_fields(input, prefix)
180
+ @lowpass = input.int("#{prefix}lowpass", @lowpass)
181
+ @highpass = input.int("#{prefix}highpass", @highpass)
182
+ @output = input.int("#{prefix}output", @output)
183
+ end
184
+ end
185
+
186
+ include KeyedState
187
+ end
188
+
189
+ # The filter's integrators and its last input, under `prefix`, and
190
+ # the output filter's under ext. The cutoff, resonance, routing,
191
+ # mode and volume come from the registers on load.
192
+ module KeyedState
193
+ def save_fields(out, prefix)
194
+ out.int("#{prefix}highpass", @highpass).int("#{prefix}bandpass", @bandpass)
195
+ out.int("#{prefix}lowpass", @lowpass).int("#{prefix}unfiltered", @unfiltered).int("#{prefix}input", @input)
196
+ @external.save_fields(out, "ext.")
197
+ end
198
+
199
+ def load_fields(input, prefix)
200
+ @highpass = input.int("#{prefix}highpass", @highpass)
201
+ @bandpass = input.int("#{prefix}bandpass", @bandpass)
202
+ @lowpass = input.int("#{prefix}lowpass", @lowpass)
203
+ @unfiltered = input.int("#{prefix}unfiltered", @unfiltered)
204
+ @input = input.int("#{prefix}input", @input)
205
+ @external.load_fields(input, "ext.")
206
+ end
207
+
208
+ # $D415 to $D418 as written.
209
+ def restore_registers(cutoff_low, cutoff_high, resonance, mode_volume)
210
+ self.cutoff = (cutoff_high << 3) | (cutoff_low & 0x07)
211
+ write_resonance(resonance)
212
+ write_mode_volume(mode_volume)
213
+ end
214
+ end
215
+
216
+ include KeyedState
217
+ end
218
+
219
+ # The SID's fields in a keyed snapshot module (Snapshot::Keyed): the
220
+ # registers as written, the data bus latch and its decay, each
221
+ # voice's under voice1. to voice3., and the filter's under filter.
222
+ # and ext. Saving catches the DSP up first, so no span or deferred
223
+ # write is left to save. What the registers decode to is worked out
224
+ # from them on load.
225
+ module KeyedState
226
+ def save_fields(out)
227
+ catch_up
228
+ out.bytes("registers", @registers.read(0, 0x20)).int("bus_value", @bus_value).int("bus_ttl", @bus_ttl)
229
+ @voice1.save_fields(out, "voice1.")
230
+ @voice2.save_fields(out, "voice2.")
231
+ @voice3.save_fields(out, "voice3.")
232
+ @filter.save_fields(out, "filter.")
233
+ out
234
+ end
235
+
236
+ def load_fields(input)
237
+ @registers.write(0, input.bytes_into("registers", @registers.read(0, 0x20)))
238
+ @bus_value = input.byte("bus_value", @bus_value)
239
+ @bus_ttl = input.int("bus_ttl", @bus_ttl)
240
+ @pending_cycles = 0
241
+ @deferred_writes.clear
242
+ @voice1.load_fields(input, "voice1.")
243
+ @voice2.load_fields(input, "voice2.")
244
+ @voice3.load_fields(input, "voice3.")
245
+ @filter.load_fields(input, "filter.")
246
+ end
247
+
248
+ # How far the recording has got into the sample it is averaging,
249
+ # under `prefix`, for a host that carries on recording from a
250
+ # snapshot: it calls #record, then #load_recording_fields.
251
+ def save_recording_fields(out, prefix)
252
+ decimator = @decimator
253
+ raise ArgumentError, "the SID isn't recording" if decimator.nil?
254
+
255
+ decimator.save_fields(out, prefix)
256
+ end
257
+
258
+ def load_recording_fields(input, prefix)
259
+ decimator = @decimator
260
+ raise ArgumentError, "the SID isn't recording" if decimator.nil?
261
+
262
+ decimator.load_fields(input, prefix)
263
+ end
264
+
265
+ def restored!
266
+ restore_voice(@voice1, 0x00)
267
+ restore_voice(@voice2, 0x07)
268
+ restore_voice(@voice3, 0x0e)
269
+ @filter.restore_registers(register(0x15), register(0x16), register(0x17), register(0x18))
270
+ update_span_rules
271
+ end
272
+
273
+ private
274
+
275
+ def restore_voice(voice, base)
276
+ control = register(base + 4)
277
+ voice.waveform.restore_registers(register(base) | (register(base + 1) << 8),
278
+ register(base + 2) | (register(base + 3) << 8), control)
279
+ voice.envelope.restore_registers(register(base + 5), register(base + 6), control)
280
+ end
281
+ end
282
+
283
+ include KeyedState
284
+ end
285
+ end
286
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ module Chips
5
+ class SID
6
+ # One of the three voices: a waveform generator amplitude-modulated by
7
+ # its envelope.
8
+ #
9
+ # The 6581 waveform DAC swings around a midpoint well above ground, so
10
+ # even a silent voice carries a large DC offset into the mixer. The
11
+ # 8580's is centred and carries none.
12
+ class Voice
13
+ WAVE_ZERO = { mos6581: 0x380, mos8580: 0x800 }.freeze
14
+ DC_OFFSET = { mos6581: 0x800 * 0xff, mos8580: 0 }.freeze
15
+
16
+ attr_reader :waveform, :envelope
17
+
18
+ # Each voice hard-syncs and ring-modulates against the previous one,
19
+ # wrapping from the first back round to the last.
20
+ def self.linked(count, model:)
21
+ voices = Array.new(count) { new(model:) }
22
+ waveforms = voices.map(&:waveform)
23
+ waveforms.each_with_index do |waveform, i|
24
+ waveform.sync_source = waveforms[i - 1]
25
+ waveform.sync_dest = waveforms[(i + 1) % count]
26
+ end
27
+ voices
28
+ end
29
+
30
+ def initialize(model: :mos6581)
31
+ @waveform = Waveform.new(model:)
32
+ @envelope = Envelope.new
33
+ @wave_zero = WAVE_ZERO.fetch(model)
34
+ @dc_offset = DC_OFFSET.fetch(model)
35
+ end
36
+
37
+ def model=(model)
38
+ @waveform.model = model
39
+ @wave_zero = WAVE_ZERO.fetch(model)
40
+ @dc_offset = DC_OFFSET.fetch(model)
41
+ end
42
+
43
+ def reset!
44
+ @waveform.reset!
45
+ @envelope = Envelope.new
46
+ end
47
+
48
+ def control=(value)
49
+ @waveform.control = value
50
+ @envelope.control = value
51
+ end
52
+
53
+ def cycle!
54
+ @envelope.cycle!
55
+ @waveform.cycle!
56
+ end
57
+
58
+ def fast_forward(cycles)
59
+ @envelope.fast_forward(cycles)
60
+ @waveform.fast_forward(cycles)
61
+ end
62
+
63
+ def output
64
+ ((@waveform.output - @wave_zero) * @envelope.output) + @dc_offset
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,128 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ module Chips
5
+ class SID
6
+ class Waveform
7
+ # The shapes the chip puts out when more than one waveform is selected.
8
+ #
9
+ # Selecting several shapers connects their outputs to the same twelve
10
+ # DAC lines. Each line settles somewhere between the levels the
11
+ # shapers drive it to, and couples to the lines around it through the
12
+ # resistances between them, so a bit reads high only when enough of
13
+ # its own drive and its neighbours' is high. Modelled here as a
14
+ # threshold on a weighted sum: each selected shaper adds its bit times
15
+ # its strength, a high pulse adds its strength to every line, and the
16
+ # neighbours contribute with one weight per distance below and one per
17
+ # distance above.
18
+ #
19
+ # A low pulse grounds every line.
20
+ #
21
+ # The parameters come from bin/sidwavefit, fitted against OSC3 dumps of
22
+ # real chips in SID/resid-test (oscsample0/1-6581/8580wf30/50/60/70).
23
+ # The tables are built from them once per chip model.
24
+ module Combined
25
+ BITS = 12
26
+
27
+ # Per chip and selection (triangle $1, sawtooth $2, pulse $4): the
28
+ # triangle's strength next to a sawtooth (the sawtooth, or a lone
29
+ # triangle, has strength 1), the pulse's strength, the threshold as
30
+ # a fraction of the full drive, then the weights of the lines one to
31
+ # eleven bits below and one to eleven bits above.
32
+ PARAMETERS = {
33
+ mos6581: {
34
+ 0x3 => [0.522, 6.4087, 0.9062,
35
+ 0.5304, 0.278, 0.1473, 0.0773, 0.0, 0.0184, 0.0113, 0.0251, 0.0594, 0.2001, 0.2001,
36
+ 0.4103, 0.1756, 0.0687, 0.0282, 0.0115, 0.0047, 0.0031, 0.2001, 0.2001, 0.2001, 0.2001],
37
+ 0x5 => [5.871, 0.0, 0.7485,
38
+ 0.6522, 0.5913, 0.5404, 0.4828, 0.428, 0.361, 0.2955, 0.2453, 0.1558, 0.0501, 0.0016,
39
+ 0.7106, 0.6667, 0.6426, 0.6376, 0.5761, 0.6639, 0.7193, 0.4728, 0.4306, 0.392, 0.357],
40
+ 0x6 => [3.8329, 4.7751, 0.9565,
41
+ 0.815, 0.8302, 0.8204, 0.8107, 0.8511, 0.8416, 0.8322, 0.8728, 0.8636, 0.8545, 0.8954,
42
+ 0.8071, 0.7979, 0.7465, 0.7771, 0.7643, 0.7572, 0.7054, 0.4585, 0.4159, 0.3773, 0.3421],
43
+ 0x7 => [5.5703, 0.2543, 0.9575,
44
+ 0.7177, 0.515, 0.3696, 0.2653, 0.1904, 0.1367, 0.0606, 0.0953, 0.0506, 0.0363, 0.2261,
45
+ 0.6347, 0.286, 0.203, 0.0817, 0.0438, 0.1235, 0.2001, 0.2001, 0.2001, 0.2001, 0.2001]
46
+ },
47
+ mos8580: {
48
+ 0x3 => [0.2223, 7.7741, 0.8869,
49
+ 0.4626, 0.2025, 0.0911, 0.041, 0.0185, 0.0, 0.0008, 0.0, 0.0, 0.0127, 0.0063,
50
+ 0.5633, 0.311, 0.2099, 0.0881, 0.0255, 0.0, 0.0431, 0.2002, 0.2002, 0.2002, 0.2002],
51
+ 0x5 => [3.1022, 1.1282, 0.9157,
52
+ 0.2731, 0.2748, 0.0031, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
53
+ 0.6228, 0.7375, 0.6094, 0.6667, 0.4381, 0.4714, 0.3648, 0.2669, 0.2264, 0.2001, 0.2001],
54
+ 0x6 => [0.9477, 0.0012, 0.7837,
55
+ 0.2565, 0.2289, 0.1095, 0.0805, 0.0282, 0.0281, 0.0, 0.0, 0.0, 0.0, 0.0063,
56
+ 0.3382, 0.3131, 0.3129, 0.2898, 0.233, 0.1762, 0.2135, 0.2002, 0.2002, 0.2002, 0.2002],
57
+ 0x7 => [1.219, 1.1111, 0.9473,
58
+ 0.3829, 0.1466, 0.0577, 0.0215, 0.0082, 0.0032, 0.0, 0.0, 0.0, 0.0, 0.0,
59
+ 0.7273, 0.6845, 0.566, 0.4807, 0.6, 0.1954, 0.4651, 0.2194, 0.2002, 0.2002, 0.2002]
60
+ }
61
+ }.freeze
62
+
63
+ # A compiled parameter set: the weight matrix, and per line the sum
64
+ # of its weights and the threshold against the full drive.
65
+ class Network
66
+ attr_reader :triangle_strength, :pulse_strength
67
+
68
+ def initialize(params)
69
+ @triangle_strength, @pulse_strength, threshold = params[0, 3]
70
+ below = [1.0] + params[3, BITS - 1]
71
+ above = [1.0] + params[3 + BITS - 1, BITS - 1]
72
+ @weights = Array.new(BITS) do |i|
73
+ Array.new(BITS) { |j| j <= i ? below[i - j] : above[j - i] }.freeze
74
+ end.freeze
75
+ @norms = @weights.map(&:sum).freeze
76
+ @threshold = threshold
77
+ end
78
+
79
+ # `drives` holds [bits, strength] for each selected shaper; a high
80
+ # pulse is passed separately, as it drives every line alike.
81
+ def shape(drives, pulse)
82
+ total = drives.sum { |_, strength| strength }
83
+ total += @pulse_strength if pulse
84
+ out = 0
85
+ BITS.times do |i|
86
+ level = pulse ? @pulse_strength * @norms[i] : 0.0
87
+ weights = @weights[i]
88
+ drives.each do |bits, strength|
89
+ BITS.times { |j| level += weights[j] * strength if bits[j] == 1 }
90
+ end
91
+ out |= 1 << i if level >= @threshold * @norms[i] * total
92
+ end
93
+ out
94
+ end
95
+ end
96
+
97
+ module_function
98
+
99
+ # The pulse-high shapes of the four combinations without noise.
100
+ def tables(model)
101
+ @tables ||= {}
102
+ @tables[model] ||= PARAMETERS.fetch(model).to_h do |selected, params|
103
+ [selected, table(selected, params)]
104
+ end.freeze
105
+ end
106
+
107
+ # One combination's shapes, indexed by the phase (the sawtooth), or
108
+ # for triangle and pulse by the triangle. A triangle combined with a
109
+ # sawtooth is never folded, so it is the phase shifted left one bit.
110
+ def table(selected, params)
111
+ network = Network.new(params)
112
+ Array.new(1 << BITS) do |index|
113
+ triangle = selected == 0x5 ? index : (index << 1) & 0xfff
114
+ network.shape(drives(selected, network, index, triangle), selected.anybits?(0x4))
115
+ end.freeze
116
+ end
117
+
118
+ def drives(selected, network, saw, triangle)
119
+ drives = []
120
+ drives << [saw, 1.0] if selected.anybits?(0x2)
121
+ drives << [triangle, selected.anybits?(0x2) ? network.triangle_strength : 1.0] if selected.anybits?(0x1)
122
+ drives
123
+ end
124
+ end
125
+ end
126
+ end
127
+ end
128
+ end
@@ -0,0 +1,107 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ module Chips
5
+ class SID
6
+ class Waveform
7
+ # Running an oscillator many cycles in one step, for the catch-ups that
8
+ # fast-forward the voices between whole cycles.
9
+ module FastForward
10
+ # Runs `cycles` cycles as #cycle! would, for an oscillator that is not
11
+ # feeding back. What those cycles latch onto the output is left to the
12
+ # whole cycle that follows, which latches over it.
13
+ def fast_forward(cycles)
14
+ drain_floating(cycles) if @selected.zero?
15
+ if @test
16
+ bleed_shift_register(cycles)
17
+ else
18
+ while @shift_pipeline != 0 && cycles != 0
19
+ advance
20
+ cycles -= 1
21
+ end
22
+ skip_ahead(cycles) if cycles != 0 && @frequency != 0
23
+ compare_pulse(((@accumulator - @frequency) & 0xffffff) >> 12)
24
+ end
25
+ @stale = true
26
+ end
27
+
28
+ # The cycle on which the accumulator MSB next rises, counting the next
29
+ # one as 1, or nil if it never will. The test bit leaves the last
30
+ # rise standing for as long as it is held.
31
+ def cycles_to_msb_rise
32
+ return (@msb_rising ? 1 : nil) if @test
33
+ return if @frequency.zero?
34
+
35
+ distance = (MSB - @accumulator) & 0xffffff
36
+ distance = 0x1000000 if distance.zero?
37
+ (distance + @frequency - 1) / @frequency
38
+ end
39
+
40
+ # A combined waveform with sawtooth (on the 6581) or noise in the mix
41
+ # writes back into the oscillator every cycle, so it cannot be
42
+ # fast-forwarded. Triangle and pulse alone write nothing back.
43
+ def feedback?
44
+ combined_waveform? && (@selected.anybits?(0x8) || (@topbit_feedback && @selected.anybits?(0x2)))
45
+ end
46
+
47
+ # Whether a catch-up has to run this oscillator a cycle at a time.
48
+ # While synthesizing, every combined waveform does, so the filter
49
+ # integrates it a cycle at a time.
50
+ def stepped?(synthesizing) = synthesizing ? combined_waveform? : feedback?
51
+
52
+ private
53
+
54
+ def combined_waveform? = !@test && combined?(@selected)
55
+
56
+ # Advances an accumulator with no shift in flight. Each rise of bit 19
57
+ # shifts the LFSR two cycles on, so a rise in the last two cycles
58
+ # leaves its shift pending for the cycles after.
59
+ def skip_ahead(cycles)
60
+ previous = @accumulator
61
+ @accumulator += @frequency * cycles
62
+ rises = bit19_rises(previous, @accumulator)
63
+ if rises != 0
64
+ @shift_pipeline = pipeline_after(cycles)
65
+ rises -= 1 if @shift_pipeline != 0
66
+ rises.times { shift_noise }
67
+ end
68
+ @accumulator &= 0xffffff
69
+ end
70
+
71
+ # Bit 19 rises at most once a cycle, on each odd multiple of 2^19,
72
+ # and at most once in any eight.
73
+ def bit19_rises(from, to) = ((to + 0x80000) >> 20) - ((from + 0x80000) >> 20)
74
+
75
+ # How far a rise on one of the last two cycles has come.
76
+ def pipeline_after(cycles)
77
+ last = @accumulator - @frequency
78
+ return 2 if bit19_rises(last, @accumulator) != 0
79
+ return 1 if cycles > 1 && bit19_rises(last - @frequency, last) != 0
80
+
81
+ 0
82
+ end
83
+
84
+ def drain_floating(cycles)
85
+ return if @floating_ttl.zero?
86
+
87
+ @floating_ttl -= cycles
88
+ return unless @floating_ttl <= 0
89
+
90
+ @floating_ttl = 0
91
+ @floating = 0x000
92
+ end
93
+
94
+ def bleed_shift_register(cycles = 1)
95
+ return if @shift_register_reset.zero?
96
+
97
+ @shift_register_reset -= cycles
98
+ return unless @shift_register_reset <= 0
99
+
100
+ @shift_register_reset = 0
101
+ @shift_register = SHIFT_REGISTER_RESET
102
+ end
103
+ end
104
+ end
105
+ end
106
+ end
107
+ end