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,236 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ module Chips
5
+ class SID
6
+ # ADSR envelope generator.
7
+ #
8
+ # A 15-bit rate counter divides the 1 MHz clock down to one envelope step
9
+ # per period. Attack steps linearly; decay and release run through a
10
+ # second, level-dependent divider that approximates an exponential
11
+ # curve.
12
+ #
13
+ # Each stage runs a cycle or more behind the one feeding it, after
14
+ # reSID 1.0's single-cycle pipeline: the rate counter resets the cycle
15
+ # after it matches, the envelope counter steps two cycles after that,
16
+ # and a gate edge only switches the rate over on its second cycle.
17
+ class Envelope
18
+ # Rate counter comparison values for each ADSR nibble. The counter
19
+ # takes a cycle to reset, so the period is one cycle longer (2ms to
20
+ # 8s over the attack range).
21
+ PERIODS = [8, 31, 62, 94, 148, 219, 266, 312,
22
+ 391, 976, 1953, 3125, 3906, 11_719, 19_531, 31_250].freeze
23
+
24
+ # Envelope levels where the exponential divider changes, and the
25
+ # divider it changes to.
26
+ EXPONENTIAL_PERIODS = { 0xff => 1, 0x5d => 2, 0x36 => 4, 0x1a => 8,
27
+ 0x0e => 16, 0x06 => 30, 0x00 => 1 }.freeze
28
+
29
+ # The states, which VICE's SIDEXTENDED numbers by index.
30
+ STATES = %i[attack decay_sustain release].freeze
31
+ attr_reader :state, :counter, :env3
32
+
33
+ def initialize
34
+ @attack = @decay = @sustain = @release = 0x0
35
+ @gate = false
36
+ @state = @next_state = :release
37
+ @counter = @env3 = 0x00
38
+ @rate_counter = 0
39
+ @rate_period = PERIODS[0]
40
+ @reset_rate_counter = false
41
+ @exponential_counter = 0
42
+ @exponential_period = 1
43
+ @state_pipeline = @envelope_pipeline = @exponential_pipeline = 0
44
+ @hold_zero = true
45
+ end
46
+
47
+ def output = @counter
48
+
49
+ # The gate edge switches the state over two cycles. Rising, it runs
50
+ # the decay rate for the first of them. The rate counter is left
51
+ # running, so the first step lands somewhere inside the current
52
+ # period.
53
+ def control=(value)
54
+ gate = value.anybits?(0x01)
55
+ return if gate == @gate
56
+
57
+ @gate = gate
58
+ gate ? gate_on : gate_off
59
+ end
60
+
61
+ def attack_decay=(value)
62
+ @attack = (value >> 4) & 0x0f
63
+ @decay = value & 0x0f
64
+ @rate_period = PERIODS[@attack] if @state == :attack
65
+ @rate_period = PERIODS[@decay] if @state == :decay_sustain
66
+ end
67
+
68
+ def sustain_release=(value)
69
+ @sustain = (value >> 4) & 0x0f
70
+ @release = value & 0x0f
71
+ @rate_period = PERIODS[@release] if @state == :release
72
+ end
73
+
74
+ # ENV3 reads the counter as it stood at the start of the cycle.
75
+ #
76
+ # Lowering the rate period below the current counter sends it the long
77
+ # way round through 2^15 before the envelope can step, the ADSR delay
78
+ # bug that hard restarts rely on. Counting from 0x7fff wraps to 1.
79
+ def cycle!
80
+ @env3 = @counter
81
+ run_pipeline if pipelined?
82
+ return @reset_rate_counter = true if @rate_counter == @rate_period
83
+
84
+ @rate_counter += 1
85
+ @rate_counter = 1 if @rate_counter == 0x8000
86
+ end
87
+
88
+ # Runs `cycles` cycles as #cycle! would. Between steps only the rate
89
+ # counter moves, so it jumps from one match to the next and the cycles
90
+ # around each step run whole. Frozen at zero, a period changes
91
+ # nothing, so whole periods are counted off at once.
92
+ def fast_forward(cycles)
93
+ while cycles.positive?
94
+ if pipelined?
95
+ cycle!
96
+ cycles -= 1
97
+ next
98
+ end
99
+
100
+ @env3 = @counter
101
+ idle = cycles_to_match
102
+ return advance_rate_counter(cycles) if cycles < idle
103
+
104
+ advance_rate_counter(idle)
105
+ cycles = skip_frozen_periods(cycles - idle)
106
+ end
107
+ end
108
+
109
+ private
110
+
111
+ def land_step
112
+ @envelope_pipeline -= 1
113
+ step if @envelope_pipeline.zero? && !@hold_zero
114
+ end
115
+
116
+ def gate_on
117
+ @next_state = :attack
118
+ @state = :decay_sustain
119
+ @rate_period = PERIODS[@decay]
120
+ @state_pipeline = 2
121
+ if @reset_rate_counter || @exponential_pipeline == 2
122
+ @envelope_pipeline = @exponential_period == 1 || @exponential_pipeline == 2 ? 2 : 4
123
+ elsif @exponential_pipeline == 1
124
+ @state_pipeline = 3
125
+ end
126
+ end
127
+
128
+ def gate_off
129
+ @next_state = :release
130
+ @state_pipeline = @envelope_pipeline.positive? ? 3 : 2
131
+ end
132
+
133
+ def state_change
134
+ @state_pipeline -= 1
135
+ if @next_state == :attack
136
+ enter_attack if @state_pipeline.zero?
137
+ elsif (@state == :attack && @state_pipeline.zero?) ||
138
+ (@state == :decay_sustain && @state_pipeline == 1)
139
+ @state = :release
140
+ @rate_period = PERIODS[@release]
141
+ end
142
+ end
143
+
144
+ def enter_attack
145
+ @state = :attack
146
+ @rate_period = PERIODS[@attack]
147
+ @hold_zero = false
148
+ end
149
+
150
+ # The first attack step also resets the exponential divider.
151
+ def reset_rate_counter
152
+ @rate_counter = 0
153
+ @reset_rate_counter = false
154
+ if @state == :attack
155
+ @exponential_counter = 0
156
+ @envelope_pipeline = 2
157
+ elsif !@hold_zero && (@exponential_counter += 1) == @exponential_period
158
+ @exponential_pipeline = @exponential_period == 1 ? 1 : 2
159
+ end
160
+ end
161
+
162
+ def exponential_step
163
+ @exponential_counter = 0
164
+ return unless @state == :release || (@state == :decay_sustain && @counter != sustain_level)
165
+
166
+ @envelope_pipeline = 1
167
+ end
168
+
169
+ def pipelined?
170
+ @reset_rate_counter || (@state_pipeline | @envelope_pipeline | @exponential_pipeline) != 0
171
+ end
172
+
173
+ def run_pipeline
174
+ state_change if @state_pipeline.positive?
175
+ land_step if @envelope_pipeline.positive?
176
+ if @exponential_pipeline.positive? && (@exponential_pipeline -= 1).zero?
177
+ exponential_step
178
+ elsif @reset_rate_counter
179
+ reset_rate_counter
180
+ end
181
+ end
182
+
183
+ # Cycles that only count before the rate counter matches its period.
184
+ def cycles_to_match
185
+ return @rate_period - @rate_counter if @rate_counter <= @rate_period
186
+
187
+ 0x7fff - @rate_counter + @rate_period
188
+ end
189
+
190
+ def advance_rate_counter(cycles)
191
+ @rate_counter += cycles
192
+ @rate_counter -= 0x7fff if @rate_counter > 0x7fff
193
+ end
194
+
195
+ # With the counter on its period, a frozen envelope comes back to the
196
+ # same state every period plus one cycles. Otherwise the next cycle
197
+ # starts a step.
198
+ def skip_frozen_periods(cycles)
199
+ cycles %= @rate_period + 1 if @hold_zero && @state != :attack
200
+ return cycles unless cycles.positive?
201
+
202
+ cycle!
203
+ cycles - 1
204
+ end
205
+
206
+ def step
207
+ if @state == :attack
208
+ attack_step
209
+ else
210
+ @counter = (@counter - 1) & 0xff
211
+ end
212
+ set_exponential_period
213
+ end
214
+
215
+ def attack_step
216
+ @counter = (@counter + 1) & 0xff
217
+ return unless @counter == 0xff
218
+
219
+ @state = :decay_sustain
220
+ @rate_period = PERIODS[@decay]
221
+ end
222
+
223
+ def sustain_level = (@sustain << 4) | @sustain
224
+
225
+ # Reaching zero freezes the envelope until the gate is raised again.
226
+ def set_exponential_period
227
+ period = EXPONENTIAL_PERIODS[@counter]
228
+ return unless period
229
+
230
+ @exponential_period = period
231
+ @hold_zero = true if @counter.zero?
232
+ end
233
+ end
234
+ end
235
+ end
236
+ end
@@ -0,0 +1,227 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ module Chips
5
+ class SID
6
+ # The SID's multimode filter: a state-variable design integrating low-,
7
+ # band- and high-pass in parallel, with the mixer and master volume on
8
+ # its output, and the C64 board's own RC network after that.
9
+ #
10
+ # Voices are routed through it or around it per the RES/FILT register,
11
+ # and the outputs selected by MODE/VOL are summed back together with the
12
+ # unfiltered ones.
13
+ class Filter
14
+ # Cutoff frequency in Hz at each chip's breakpoints. The 6581 curve was
15
+ # sampled from a real chip by reSID and bends sharply around $300; the
16
+ # 8580's is close to a straight line from 0 to 12.5 kHz. The curve
17
+ # between breakpoints is interpolated linearly; reSID fits a spline, so
18
+ # the two differ by a few Hz mid-segment.
19
+ CUTOFF_POINTS = {
20
+ mos6581: [[0, 220], [128, 230], [256, 250], [384, 300],
21
+ [512, 420], [640, 780], [768, 1600], [832, 2300],
22
+ [896, 3200], [960, 4300], [1024, 5000], [1088, 5400],
23
+ [1152, 5800], [1216, 6000], [1280, 6200], [1344, 6400],
24
+ [1408, 6600], [1472, 6800], [1536, 7000], [1600, 7200],
25
+ [1664, 7400], [1728, 7600], [1792, 7800], [1856, 8000],
26
+ [1920, 8200], [1984, 8400], [2047, 8600]].freeze,
27
+ mos8580: [[0, 0], [128, 800], [256, 1600], [384, 2500],
28
+ [512, 3300], [640, 4100], [768, 4800], [896, 5600],
29
+ [1024, 6500], [1152, 7500], [1280, 8400], [1408, 9200],
30
+ [1536, 9800], [1664, 10_500], [1792, 11_000],
31
+ [1920, 11_700], [2047, 12_500]].freeze
32
+ }.freeze
33
+
34
+ # 2^20 / 1 MHz, so one cycle of integration is a plain shift.
35
+ SCALE = (2**20) / 1_000_000.0
36
+
37
+ # The integrator goes unstable above ~16 kHz at a one-cycle step.
38
+ W0_MAX = (2 * Math::PI * 16_000 * SCALE).to_i
39
+
40
+ # A step of several cycles scales w0 by their count. reSID keeps such
41
+ # a step stable by capping the cutoff at 4 kHz over 8 cycles; this is
42
+ # the same bound on the product, so a single cycle is never capped.
43
+ W0_STEP_MAX = (2 * Math::PI * 4_000 * 8 * SCALE).to_i
44
+
45
+ def self.build_w0(points)
46
+ table = Array.new(2048)
47
+ points.each_cons(2) do |(fc0, hz0), (fc1, hz1)|
48
+ (fc0..fc1).each do |register|
49
+ hz = hz0 + (((hz1 - hz0) * (register - fc0)).to_f / (fc1 - fc0))
50
+ table[register] = [(2 * Math::PI * hz * SCALE).to_i, W0_MAX].min
51
+ end
52
+ end
53
+ table.freeze
54
+ end
55
+ private_class_method :build_w0
56
+
57
+ W0 = CUTOFF_POINTS.transform_values { |points| build_w0(points) }.freeze
58
+
59
+ # 1024/Q, with Q sweeping from 0.707 up to 1.707 across the register.
60
+ RESONANCE = (0x0..0xf).map { |res| (1024.0 / (0.707 + (res / 15.0))).to_i }.freeze
61
+
62
+ # The mixer's own input DC offset, derived by reSID from the 6581's
63
+ # zero-volume and full-volume output levels. The 8580 has none.
64
+ MIXER_DC = { mos6581: -((0xfff * 0xff) / 18) >> 7, mos8580: 0 }.freeze
65
+
66
+ # Fractional bits carried in the integrators. At low cutoffs a cycle's
67
+ # step is only a few units, which truncating to whole units would
68
+ # swallow, and always toward minus infinity.
69
+ FRACTION = 12
70
+
71
+ # The RC network between the SID's audio pin and the C64's output jack:
72
+ # a 10kΩ/1000pF low-pass at ~16 kHz, then a 1kΩ/10µF high-pass at
73
+ # ~16 Hz that strips the 6581's DC offset off the mix.
74
+ class External
75
+ W0_LOWPASS = (100_000 * SCALE).round
76
+ W0_HIGHPASS = (100 * SCALE).round
77
+
78
+ # The high-pass step is W0_HIGHPASS/2^20 of the gap, so in whole
79
+ # units it stalls on any gap under ~10k, as reSID's does. Sixteen
80
+ # fractional bits bring that under one unit.
81
+ FRACTION = 16
82
+
83
+ # Its input arrives with the filter's fractional bits still on.
84
+ INPUT_SHIFT = FRACTION - Filter::FRACTION
85
+
86
+ attr_reader :output
87
+
88
+ def initialize
89
+ @lowpass = 0
90
+ @highpass = 0
91
+ @output = 0
92
+ end
93
+
94
+ # Takes the input in 1/2^Filter::FRACTION units, and reads the
95
+ # output out in whole ones. A step of several cycles moves each pole
96
+ # at most onto its input.
97
+ def cycle!(input, cycles = 1)
98
+ w0_lowpass = [(W0_LOWPASS >> 8) * cycles, 1 << 12].min
99
+ w0_highpass = [W0_HIGHPASS * cycles, 1 << 20].min
100
+ delta_lowpass = (w0_lowpass * ((input << INPUT_SHIFT) - @lowpass)) >> 12
101
+ delta_highpass = (w0_highpass * (@lowpass - @highpass)) >> 20
102
+ @output = (@lowpass - @highpass) >> FRACTION
103
+ @lowpass += delta_lowpass
104
+ @highpass += delta_highpass
105
+ @output
106
+ end
107
+ end
108
+
109
+ attr_reader :model, :cutoff, :routing, :mode, :volume
110
+
111
+ def initialize(model: :mos6581)
112
+ @model = model
113
+ @w0_table = W0.fetch(model)
114
+ @mixer_dc = MIXER_DC.fetch(model)
115
+ @external = External.new
116
+ reset
117
+ end
118
+
119
+ # The integrators and the registers carry over; the cutoff maps
120
+ # through the new model's curve.
121
+ def model=(model)
122
+ @model = model
123
+ @w0_table = W0.fetch(model)
124
+ @mixer_dc = MIXER_DC.fetch(model)
125
+ @w0 = @w0_table[@cutoff]
126
+ end
127
+
128
+ def reset
129
+ @cutoff = 0x000
130
+ @routing = 0x0
131
+ @mode = 0x0
132
+ @volume = 0x0
133
+ @voice3_off = false
134
+ @w0 = @w0_table[0]
135
+ @resonance = RESONANCE[0]
136
+ @lowpass = @bandpass = @highpass = 0
137
+ @input = @unfiltered = 0
138
+ end
139
+
140
+ def write(reg, value)
141
+ case reg
142
+ when 0x15 then self.cutoff = (@cutoff & 0x7f8) | (value & 0x07)
143
+ when 0x16 then self.cutoff = (value << 3) | (@cutoff & 0x007)
144
+ when 0x17 then write_resonance(value)
145
+ when 0x18 then write_mode_volume(value)
146
+ end
147
+ end
148
+
149
+ def cutoff=(value)
150
+ @cutoff = value & 0x7ff
151
+ @w0 = @w0_table[@cutoff]
152
+ end
153
+
154
+ def voice3_off? = @voice3_off
155
+
156
+ def lowpass = @lowpass >> FRACTION
157
+ def bandpass = @bandpass >> FRACTION
158
+ def highpass = @highpass >> FRACTION
159
+
160
+ # Integrates `cycles` cycles in one step, from the voices' output at
161
+ # the end of them.
162
+ def cycle!(voices, cycles = 1)
163
+ route(voices)
164
+ w0 = @w0 * cycles
165
+ w0 = W0_STEP_MAX if w0 > W0_STEP_MAX
166
+ @bandpass -= (w0 * @highpass) >> 20
167
+ @lowpass -= (w0 * @bandpass) >> 20
168
+ @highpass = ((@bandpass * @resonance) >> 10) - @lowpass - (@input << FRACTION)
169
+ @external.cycle!(fine_mix, cycles)
170
+ end
171
+
172
+ # The SID's own audio pin, before the board's RC network.
173
+ def mix = fine_mix >> FRACTION
174
+
175
+ def output = @external.output
176
+
177
+ private
178
+
179
+ def write_resonance(value)
180
+ @routing = value & 0x0f
181
+ @resonance = RESONANCE[(value >> 4) & 0x0f]
182
+ end
183
+
184
+ def write_mode_volume(value)
185
+ @volume = value & 0x0f
186
+ @mode = (value >> 4) & 0x07
187
+ @voice3_off = value.anybits?(0x80)
188
+ end
189
+
190
+ # Unrolled over the three voices, like SID#clock!. Voice 3 is silenced
191
+ # by MODE/VOL bit 7 only while it bypasses the filter.
192
+ def route(voices)
193
+ voice1, voice2, voice3 = voices
194
+ @input = 0
195
+ @unfiltered = 0
196
+ route_voice(voice1.output >> 7, 0x1)
197
+ route_voice(voice2.output >> 7, 0x2)
198
+ if @routing & 0x4 != 0
199
+ @input += voice3.output >> 7
200
+ elsif !@voice3_off
201
+ @unfiltered += voice3.output >> 7
202
+ end
203
+ end
204
+
205
+ def route_voice(output, bit)
206
+ if @routing & bit == bit
207
+ @input += output
208
+ else
209
+ @unfiltered += output
210
+ end
211
+ end
212
+
213
+ # The mix with the integrators' fractional bits kept, so the RC
214
+ # network isn't fed the filter's output floored to whole units.
215
+ def fine_mix = (((@unfiltered + @mixer_dc) << FRACTION) + filtered) * @volume
216
+
217
+ def filtered
218
+ value = 0
219
+ value += @lowpass if @mode & 0x1 != 0
220
+ value += @bandpass if @mode & 0x2 != 0
221
+ value += @highpass if @mode & 0x4 != 0
222
+ value
223
+ end
224
+ end
225
+ end
226
+ end
227
+ end