badline 0.12.0 → 0.13.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 (516) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +25 -0
  3. data/CLAUDE.md +59 -31
  4. data/README.md +1 -1
  5. data/doc/library.md +9 -9
  6. data/doc/pinned-behaviour.md +105 -105
  7. data/doc/snapshots.md +1 -4
  8. data/lib/badline/audio/bare_player.rb +4 -4
  9. data/lib/badline/audio/cli.rb +6 -6
  10. data/lib/badline/audio/jukebox.rb +2 -2
  11. data/lib/badline/audio/machine_player.rb +2 -2
  12. data/lib/badline/{media → audio}/queue.rb +1 -1
  13. data/lib/badline/audio/queued_tune.rb +6 -6
  14. data/lib/badline/audio/renderer.rb +5 -5
  15. data/lib/badline/audio/stereo.rb +1 -1
  16. data/lib/badline/c128/{bus_state.rb → bus/saved_state.rb} +6 -6
  17. data/lib/badline/c128/bus.rb +30 -51
  18. data/lib/badline/c128/color_lines.rb +2 -2
  19. data/lib/badline/c128/cpu.rb +1 -1
  20. data/lib/badline/c128/drives.rb +10 -11
  21. data/lib/badline/c128/fast_serial.rb +5 -5
  22. data/lib/badline/c128/mmu_pages.rb +1 -1
  23. data/lib/badline/c128/modes.rb +2 -2
  24. data/lib/badline/c128/saved_state.rb +21 -47
  25. data/lib/badline/c128/z80.rb +2 -2
  26. data/lib/badline/c128.rb +18 -72
  27. data/lib/badline/{computer → c64}/attachments.rb +16 -47
  28. data/lib/badline/c64/banked_ram/banking.rb +32 -0
  29. data/lib/badline/c64/banked_ram/plus256k.rb +56 -0
  30. data/lib/badline/c64/banked_ram/plus60k.rb +45 -0
  31. data/lib/badline/c64/banked_ram/unexpanded.rb +30 -0
  32. data/lib/badline/c64/banked_ram.rb +23 -0
  33. data/lib/badline/c64/bus/extra_sids.rb +32 -0
  34. data/lib/badline/c64/bus/fittings.rb +40 -0
  35. data/lib/badline/c64/bus/pla.rb +149 -0
  36. data/lib/badline/c64/bus/roms.rb +41 -0
  37. data/lib/badline/c64/bus/saved_state.rb +68 -0
  38. data/lib/badline/c64/bus/sid_slots.rb +44 -0
  39. data/lib/badline/c64/bus.rb +218 -0
  40. data/lib/badline/c64/cartridge/action_replay.rb +128 -0
  41. data/lib/badline/c64/cartridge/atomic_power.rb +26 -0
  42. data/lib/badline/c64/cartridge/bank.rb +83 -0
  43. data/lib/badline/c64/cartridge/comal80.rb +52 -0
  44. data/lib/badline/c64/cartridge/dinamic.rb +36 -0
  45. data/lib/badline/c64/cartridge/easy_flash.rb +147 -0
  46. data/lib/badline/c64/cartridge/epyx_fastload.rb +81 -0
  47. data/lib/badline/c64/cartridge/final_cartridge3.rb +91 -0
  48. data/lib/badline/c64/cartridge/flash/saved_state.rb +40 -0
  49. data/lib/badline/c64/cartridge/flash.rb +279 -0
  50. data/lib/badline/c64/cartridge/freezer.rb +35 -0
  51. data/lib/badline/c64/cartridge/fun_play.rb +40 -0
  52. data/lib/badline/c64/cartridge/g_mod2.rb +76 -0
  53. data/lib/badline/c64/cartridge/game_system.rb +42 -0
  54. data/lib/badline/c64/cartridge/geo_ram.rb +81 -0
  55. data/lib/badline/c64/cartridge/kcs_power.rb +77 -0
  56. data/lib/badline/c64/cartridge/mach5.rb +34 -0
  57. data/lib/badline/c64/cartridge/magic_desk.rb +32 -0
  58. data/lib/badline/c64/cartridge/ocean.rb +37 -0
  59. data/lib/badline/c64/cartridge/pagefox.rb +70 -0
  60. data/lib/badline/c64/cartridge/retro_replay/saved_state.rb +47 -0
  61. data/lib/badline/c64/cartridge/retro_replay.rb +286 -0
  62. data/lib/badline/c64/cartridge/rex_utility.rb +31 -0
  63. data/lib/badline/c64/cartridge/rgcd.rb +60 -0
  64. data/lib/badline/c64/cartridge/saved_state.rb +63 -0
  65. data/lib/badline/c64/cartridge/simons_basic.rb +37 -0
  66. data/lib/badline/c64/cartridge/standard.rb +17 -0
  67. data/lib/badline/c64/cartridge/super_games.rb +50 -0
  68. data/lib/badline/c64/cartridge/westermann.rb +32 -0
  69. data/lib/badline/c64/cartridge/zaxxon.rb +68 -0
  70. data/lib/badline/c64/cartridge.rb +258 -0
  71. data/lib/badline/c64/color_memory.rb +27 -0
  72. data/lib/badline/c64/common.rb +34 -0
  73. data/lib/badline/c64/kernal_traps.rb +89 -0
  74. data/lib/badline/c64/reu/dma/saved_state.rb +52 -0
  75. data/lib/badline/c64/reu/dma.rb +275 -0
  76. data/lib/badline/c64/reu/ram.rb +109 -0
  77. data/lib/badline/c64/reu/saved_state.rb +55 -0
  78. data/lib/badline/c64/reu/trigger.rb +34 -0
  79. data/lib/badline/c64/reu.rb +291 -0
  80. data/lib/badline/{computer → c64}/saved_state.rb +13 -18
  81. data/lib/badline/{computer → c64}/true_drives.rb +20 -31
  82. data/lib/badline/{computer.rb → c64.rb} +22 -80
  83. data/lib/badline/chips/cia/fast_forward.rb +62 -0
  84. data/lib/badline/chips/cia/interrupt_register.rb +145 -0
  85. data/lib/badline/chips/cia/saved_state.rb +49 -0
  86. data/lib/badline/chips/cia/serial/saved_state.rb +45 -0
  87. data/lib/badline/chips/cia/serial.rb +250 -0
  88. data/lib/badline/chips/cia/time_of_day.rb +232 -0
  89. data/lib/badline/chips/cia/timer.rb +208 -0
  90. data/lib/badline/chips/cia.rb +281 -0
  91. data/lib/badline/chips/cpu/addressing.rb +133 -0
  92. data/lib/badline/chips/cpu/boundary.rb +56 -0
  93. data/lib/badline/chips/cpu/core.rb +167 -0
  94. data/lib/badline/chips/cpu/instruction.rb +338 -0
  95. data/lib/badline/chips/cpu/instruction_set/arithmetic.rb +118 -0
  96. data/lib/badline/chips/cpu/instruction_set/bitwise.rb +130 -0
  97. data/lib/badline/chips/cpu/instruction_set/branch.rb +83 -0
  98. data/lib/badline/chips/cpu/instruction_set/flag.rb +67 -0
  99. data/lib/badline/chips/cpu/instruction_set/illegal.rb +274 -0
  100. data/lib/badline/chips/cpu/instruction_set/inc_dec.rb +73 -0
  101. data/lib/badline/chips/cpu/instruction_set/stack.rb +56 -0
  102. data/lib/badline/chips/cpu/instruction_set/transfer.rb +141 -0
  103. data/lib/badline/chips/cpu/instruction_set.rb +59 -0
  104. data/lib/badline/chips/cpu/interrupts.rb +108 -0
  105. data/lib/badline/chips/cpu/microcode.rb +179 -0
  106. data/lib/badline/chips/cpu/operations.rb +90 -0
  107. data/lib/badline/chips/cpu/port.rb +116 -0
  108. data/lib/badline/chips/cpu/saved_state.rb +117 -0
  109. data/lib/badline/chips/cpu/stack_operations.rb +84 -0
  110. data/lib/badline/chips/cpu/traps.rb +23 -0
  111. data/lib/badline/chips/cpu.rb +12 -0
  112. data/lib/badline/chips/sid/catch_up.rb +122 -0
  113. data/lib/badline/chips/sid/decimator.rb +66 -0
  114. data/lib/badline/chips/sid/envelope/saved_state.rb +70 -0
  115. data/lib/badline/chips/sid/envelope.rb +241 -0
  116. data/lib/badline/chips/sid/filter/saved_state.rb +42 -0
  117. data/lib/badline/chips/sid/filter.rb +241 -0
  118. data/lib/badline/chips/sid/saved_state.rb +52 -0
  119. data/lib/badline/chips/sid/voice.rb +79 -0
  120. data/lib/badline/chips/sid/waveform/combined.rb +128 -0
  121. data/lib/badline/chips/sid/waveform/fast_forward.rb +107 -0
  122. data/lib/badline/chips/sid/waveform/noise_writeback.rb +124 -0
  123. data/lib/badline/chips/sid/waveform/saved_state.rb +68 -0
  124. data/lib/badline/chips/sid/waveform.rb +307 -0
  125. data/lib/badline/chips/sid.rb +265 -0
  126. data/lib/badline/{c128 → chips}/vdc/memory.rb +1 -1
  127. data/lib/badline/{c128 → chips}/vdc/painter.rb +1 -1
  128. data/lib/badline/{c128 → chips}/vdc/raster.rb +1 -1
  129. data/lib/badline/chips/vdc/saved_state.rb +36 -0
  130. data/lib/badline/{c128 → chips}/vdc/window.rb +1 -1
  131. data/lib/badline/{c128 → chips}/vdc.rb +7 -31
  132. data/lib/badline/chips/via/control_lines.rb +124 -0
  133. data/lib/badline/chips/via/fast_forward.rb +55 -0
  134. data/lib/badline/chips/via/interrupt_register.rb +69 -0
  135. data/lib/badline/chips/via/saved_state.rb +44 -0
  136. data/lib/badline/chips/via/shift_register.rb +182 -0
  137. data/lib/badline/chips/via/timer1.rb +133 -0
  138. data/lib/badline/chips/via/timer2.rb +135 -0
  139. data/lib/badline/chips/via.rb +268 -0
  140. data/lib/badline/chips/vic/bank.rb +135 -0
  141. data/lib/badline/chips/vic/border_mask.rb +88 -0
  142. data/lib/badline/chips/vic/collisions.rb +117 -0
  143. data/lib/badline/chips/vic/color_patches.rb +49 -0
  144. data/lib/badline/chips/vic/display_state.rb +187 -0
  145. data/lib/badline/chips/vic/graphics_mode.rb +166 -0
  146. data/lib/badline/chips/vic/graphics_shifter/saved_state.rb +40 -0
  147. data/lib/badline/chips/vic/graphics_shifter.rb +187 -0
  148. data/lib/badline/chips/vic/register_log.rb +83 -0
  149. data/lib/badline/chips/vic/registers.rb +197 -0
  150. data/lib/badline/chips/vic/saved_state.rb +82 -0
  151. data/lib/badline/chips/vic/sequencer/saved_state.rb +91 -0
  152. data/lib/badline/chips/vic/sequencer.rb +277 -0
  153. data/lib/badline/chips/vic/sequencer_output.rb +118 -0
  154. data/lib/badline/chips/vic/sprite/internal_bus.rb +70 -0
  155. data/lib/badline/chips/vic/sprite/saved_state.rb +66 -0
  156. data/lib/badline/chips/vic/sprite/shifter.rb +148 -0
  157. data/lib/badline/chips/vic/sprite/timing.rb +58 -0
  158. data/lib/badline/chips/vic/sprite.rb +339 -0
  159. data/lib/badline/chips/vic/sprites/saved_state.rb +59 -0
  160. data/lib/badline/chips/vic/sprites.rb +284 -0
  161. data/lib/badline/chips/vic.rb +783 -0
  162. data/lib/badline/chips/vic_i/painter/saved_state.rb +40 -0
  163. data/lib/badline/{vic20/vic → chips/vic_i}/painter.rb +6 -30
  164. data/lib/badline/{vic20/vic → chips/vic_i}/saved_state.rb +2 -2
  165. data/lib/badline/chips/vic_i/sound/output.rb +85 -0
  166. data/lib/badline/chips/vic_i/sound.rb +235 -0
  167. data/lib/badline/{vic20/vic → chips/vic_i}/text_window.rb +2 -2
  168. data/lib/badline/{vic20/vic.rb → chips/vic_i.rb} +5 -5
  169. data/lib/badline/chips/z80/arithmetic.rb +119 -0
  170. data/lib/badline/chips/z80/bit_page.rb +57 -0
  171. data/lib/badline/chips/z80/block_io.rb +60 -0
  172. data/lib/badline/chips/z80/block_transfers.rb +61 -0
  173. data/lib/badline/chips/z80/bus_cycles.rb +113 -0
  174. data/lib/badline/chips/z80/core.rb +115 -0
  175. data/lib/badline/chips/z80/exchanges.rb +52 -0
  176. data/lib/badline/chips/z80/extended_page.rb +91 -0
  177. data/lib/badline/chips/z80/flags.rb +39 -0
  178. data/lib/badline/chips/z80/interrupt_acceptance.rb +48 -0
  179. data/lib/badline/chips/z80/jumps.rb +84 -0
  180. data/lib/badline/chips/z80/loads.rb +100 -0
  181. data/lib/badline/chips/z80/main_page.rb +136 -0
  182. data/lib/badline/chips/z80/registers.rb +122 -0
  183. data/lib/badline/chips/z80/saved_state.rb +71 -0
  184. data/lib/badline/chips/z80/shifts.rb +66 -0
  185. data/lib/badline/chips/z80.rb +13 -0
  186. data/lib/badline/core.rb +27 -29
  187. data/lib/badline/drives/datasette/saved_state.rb +37 -0
  188. data/lib/badline/drives/datasette.rb +140 -0
  189. data/lib/badline/drives/engine/core.rb +149 -0
  190. data/lib/badline/drives/engine/fast_serial.rb +75 -0
  191. data/lib/badline/drives/engine/idle.rb +127 -0
  192. data/lib/badline/drives/engine/idle_cpu.rb +30 -0
  193. data/lib/badline/drives/engine/orbit.rb +81 -0
  194. data/lib/badline/drives/engine/serial_lines.rb +75 -0
  195. data/lib/badline/drives/engine/sleep.rb +114 -0
  196. data/lib/badline/drives/engine/vias.rb +86 -0
  197. data/lib/badline/drives/engine/watch.rb +152 -0
  198. data/lib/badline/drives/engine.rb +11 -0
  199. data/lib/badline/drives/model1541/bus.rb +88 -0
  200. data/lib/badline/drives/model1541/byte_ready.rb +50 -0
  201. data/lib/badline/drives/model1541/cpu.rb +13 -0
  202. data/lib/badline/drives/model1541/disk/saved_state.rb +88 -0
  203. data/lib/badline/drives/model1541/disk.rb +317 -0
  204. data/lib/badline/drives/model1541/disk_via.rb +38 -0
  205. data/lib/badline/drives/model1541/gcr.rb +59 -0
  206. data/lib/badline/drives/model1541/mechanism/saved_state.rb +72 -0
  207. data/lib/badline/drives/model1541/mechanism.rb +315 -0
  208. data/lib/badline/drives/model1541/rotation.rb +123 -0
  209. data/lib/badline/drives/model1541/saved_state.rb +44 -0
  210. data/lib/badline/drives/model1541/sector_reader.rb +78 -0
  211. data/lib/badline/drives/model1541/serial_port.rb +20 -0
  212. data/lib/badline/drives/model1541/track.rb +100 -0
  213. data/lib/badline/drives/model1541.rb +81 -0
  214. data/lib/badline/drives/model1571/bus.rb +113 -0
  215. data/lib/badline/drives/model1571/cpu.rb +13 -0
  216. data/lib/badline/drives/model1571/saved_state.rb +47 -0
  217. data/lib/badline/drives/model1571/serial_port.rb +39 -0
  218. data/lib/badline/drives/model1571/serial_via.rb +27 -0
  219. data/lib/badline/drives/model1571/wd1770.rb +122 -0
  220. data/lib/badline/drives/model1571.rb +142 -0
  221. data/lib/badline/drives/model1581/bus.rb +139 -0
  222. data/lib/badline/drives/model1581/cpu.rb +13 -0
  223. data/lib/badline/drives/model1581/disk/saved_state.rb +89 -0
  224. data/lib/badline/drives/model1581/disk.rb +137 -0
  225. data/lib/badline/drives/model1581/mechanism.rb +119 -0
  226. data/lib/badline/drives/model1581/saved_state.rb +42 -0
  227. data/lib/badline/drives/model1581/serial_port.rb +54 -0
  228. data/lib/badline/drives/model1581/slot.rb +79 -0
  229. data/lib/badline/drives/model1581/track.rb +102 -0
  230. data/lib/badline/drives/model1581/track_bytes.rb +77 -0
  231. data/lib/badline/drives/model1581/wd1772/registers.rb +91 -0
  232. data/lib/badline/drives/model1581/wd1772/saved_state.rb +68 -0
  233. data/lib/badline/drives/model1581/wd1772/sector_writes.rb +75 -0
  234. data/lib/badline/drives/model1581/wd1772/sectors.rb +152 -0
  235. data/lib/badline/drives/model1581/wd1772/stepping.rb +111 -0
  236. data/lib/badline/drives/model1581/wd1772/tracks.rb +127 -0
  237. data/lib/badline/drives/model1581/wd1772/turn.rb +75 -0
  238. data/lib/badline/drives/model1581/wd1772.rb +218 -0
  239. data/lib/badline/drives/model1581.rb +206 -0
  240. data/lib/badline/drives/serial_bus.rb +161 -0
  241. data/lib/badline/frontend/boot.rb +10 -61
  242. data/lib/badline/frontend/machine_window.rb +3 -1
  243. data/lib/badline/frontend/player/filter_view.rb +1 -1
  244. data/lib/badline/frontend/player/player_controller.rb +2 -2
  245. data/lib/badline/frontend/player/player_screen.rb +1 -0
  246. data/lib/badline/frontend/player/theme.rb +7 -7
  247. data/lib/badline/frontend/timeline.rb +3 -3
  248. data/lib/badline/input/control_ports.rb +78 -0
  249. data/lib/badline/input/joystick.rb +27 -0
  250. data/lib/badline/input/keyboard.rb +114 -0
  251. data/lib/badline/input.rb +3 -0
  252. data/lib/badline/kernal_trap/{drive → dos}/block_commands.rb +1 -1
  253. data/lib/badline/kernal_trap/dos/channel.rb +183 -0
  254. data/lib/badline/kernal_trap/{drive → dos}/channels.rb +1 -1
  255. data/lib/badline/kernal_trap/{drive → dos}/memory.rb +1 -1
  256. data/lib/badline/kernal_trap/{drive → dos}/parameters.rb +1 -1
  257. data/lib/badline/kernal_trap/{drive → dos}/status.rb +1 -1
  258. data/lib/badline/kernal_trap/{drive → dos}/write_file.rb +1 -1
  259. data/lib/badline/kernal_trap/{drive → dos}/writes.rb +1 -1
  260. data/lib/badline/kernal_trap/{drive.rb → dos.rb} +11 -10
  261. data/lib/badline/kernal_trap/{file.rb → file_routine.rb} +1 -1
  262. data/lib/badline/kernal_trap/load.rb +6 -6
  263. data/lib/badline/kernal_trap/save.rb +5 -5
  264. data/lib/badline/kernal_trap/serial.rb +7 -7
  265. data/lib/badline/kernal_trap.rb +2 -3
  266. data/lib/badline/machine/clocking.rb +50 -0
  267. data/lib/badline/machine.rb +63 -7
  268. data/lib/badline/media/boot_disk.rb +2 -2
  269. data/lib/badline/media/disk_list.rb +3 -3
  270. data/lib/badline/media/disk_set.rb +1 -1
  271. data/lib/badline/media/extensions.rb +33 -0
  272. data/lib/badline/media/program.rb +28 -0
  273. data/lib/badline/media/true_drive.rb +4 -3
  274. data/lib/badline/media/tune.rb +35 -0
  275. data/lib/badline/media/vic20_cartridge.rb +1 -7
  276. data/lib/badline/media/vic20_media.rb +38 -80
  277. data/lib/badline/media.rb +84 -81
  278. data/lib/badline/model.rb +1 -1
  279. data/lib/badline/options/event.rb +3 -1
  280. data/lib/badline/roms/README +6 -6
  281. data/lib/badline/sdl.rb +1 -0
  282. data/lib/badline/{storage/sid_file.rb → sid_tune/file.rb} +3 -3
  283. data/lib/badline/{storage → sid_tune}/hvsc.rb +7 -7
  284. data/lib/badline/{storage → sid_tune}/song_lengths.rb +2 -2
  285. data/lib/badline/{storage → sid_tune}/stil/credit.rb +1 -1
  286. data/lib/badline/{storage → sid_tune}/stil.rb +3 -3
  287. data/lib/badline/sid_tune.rb +6 -0
  288. data/lib/badline/snapshot/c128_setup.rb +43 -0
  289. data/lib/badline/snapshot/c64_setup.rb +79 -0
  290. data/lib/badline/snapshot/image.rb +5 -25
  291. data/lib/badline/snapshot/machine_state.rb +10 -2
  292. data/lib/badline/snapshot/setup.rb +22 -66
  293. data/lib/badline/snapshot/state.rb +8 -17
  294. data/lib/badline/snapshot/storage_setup.rb +68 -0
  295. data/lib/badline/snapshot/vic20_setup.rb +37 -0
  296. data/lib/badline/snapshot/vice/c64_mem.rb +1 -1
  297. data/lib/badline/snapshot/vice/restore.rb +1 -1
  298. data/lib/badline/snapshot/vice/reu1764.rb +1 -1
  299. data/lib/badline/snapshot/vice/reu_fields.rb +40 -0
  300. data/lib/badline/snapshot/vice.rb +7 -5
  301. data/lib/badline/snapshot.rb +13 -12
  302. data/lib/badline/storage/d64_image.rb +15 -9
  303. data/lib/badline/storage/d71_image.rb +0 -2
  304. data/lib/badline/storage/d81_image.rb +0 -2
  305. data/lib/badline/storage/disk_image/writing.rb +2 -2
  306. data/lib/badline/storage/disk_image.rb +3 -6
  307. data/lib/badline/storage/g64_image.rb +2 -2
  308. data/lib/badline/storage/host_directory.rb +0 -5
  309. data/lib/badline/storage/t64.rb +2 -8
  310. data/lib/badline/storage/unavailable.rb +1 -1
  311. data/lib/badline/storage.rb +0 -37
  312. data/lib/badline/support/c128_checkpoint.rb +39 -0
  313. data/lib/badline/support/checkpoint.rb +108 -0
  314. data/lib/badline/support/vic20_checkpoint.rb +38 -0
  315. data/lib/badline/support.rb +5 -0
  316. data/lib/badline/version.rb +1 -1
  317. data/lib/badline/vic20/attachments.rb +11 -81
  318. data/lib/badline/vic20/bus/saved_state.rb +35 -0
  319. data/lib/badline/vic20/bus.rb +3 -25
  320. data/lib/badline/vic20/cpu.rb +1 -1
  321. data/lib/badline/vic20/port_wiring.rb +4 -4
  322. data/lib/badline/vic20/saved_state.rb +16 -60
  323. data/lib/badline/vic20/user_via_ports.rb +2 -2
  324. data/lib/badline/vic20.rb +20 -51
  325. metadata +250 -221
  326. data/lib/badline/address_bus/extra_sids.rb +0 -30
  327. data/lib/badline/address_bus/fittings.rb +0 -38
  328. data/lib/badline/address_bus/pla.rb +0 -147
  329. data/lib/badline/address_bus/roms.rb +0 -39
  330. data/lib/badline/address_bus/saved_state.rb +0 -72
  331. data/lib/badline/address_bus/sid_slots.rb +0 -42
  332. data/lib/badline/address_bus.rb +0 -235
  333. data/lib/badline/cartridge/action_replay.rb +0 -126
  334. data/lib/badline/cartridge/atomic_power.rb +0 -24
  335. data/lib/badline/cartridge/bank.rb +0 -81
  336. data/lib/badline/cartridge/comal80.rb +0 -50
  337. data/lib/badline/cartridge/dinamic.rb +0 -34
  338. data/lib/badline/cartridge/easy_flash.rb +0 -145
  339. data/lib/badline/cartridge/epyx_fastload.rb +0 -79
  340. data/lib/badline/cartridge/final_cartridge3.rb +0 -89
  341. data/lib/badline/cartridge/flash.rb +0 -306
  342. data/lib/badline/cartridge/freezer.rb +0 -33
  343. data/lib/badline/cartridge/fun_play.rb +0 -38
  344. data/lib/badline/cartridge/g_mod2.rb +0 -74
  345. data/lib/badline/cartridge/game_system.rb +0 -40
  346. data/lib/badline/cartridge/geo_ram.rb +0 -79
  347. data/lib/badline/cartridge/kcs_power.rb +0 -75
  348. data/lib/badline/cartridge/mach5.rb +0 -32
  349. data/lib/badline/cartridge/magic_desk.rb +0 -30
  350. data/lib/badline/cartridge/ocean.rb +0 -35
  351. data/lib/badline/cartridge/pagefox.rb +0 -68
  352. data/lib/badline/cartridge/retro_replay.rb +0 -320
  353. data/lib/badline/cartridge/rex_utility.rb +0 -29
  354. data/lib/badline/cartridge/rgcd.rb +0 -58
  355. data/lib/badline/cartridge/simons_basic.rb +0 -35
  356. data/lib/badline/cartridge/standard.rb +0 -15
  357. data/lib/badline/cartridge/super_games.rb +0 -48
  358. data/lib/badline/cartridge/westermann.rb +0 -30
  359. data/lib/badline/cartridge/zaxxon.rb +0 -66
  360. data/lib/badline/cartridge.rb +0 -303
  361. data/lib/badline/checkpoint.rb +0 -98
  362. data/lib/badline/cia/fast_forward.rb +0 -60
  363. data/lib/badline/cia/interrupt_register.rb +0 -143
  364. data/lib/badline/cia/serial.rb +0 -275
  365. data/lib/badline/cia/state.rb +0 -47
  366. data/lib/badline/cia/timer.rb +0 -206
  367. data/lib/badline/cia.rb +0 -279
  368. data/lib/badline/color_memory.rb +0 -25
  369. data/lib/badline/computer/kernal_traps.rb +0 -29
  370. data/lib/badline/control_ports.rb +0 -76
  371. data/lib/badline/cpu/addressing.rb +0 -131
  372. data/lib/badline/cpu/boundary.rb +0 -54
  373. data/lib/badline/cpu/core.rb +0 -165
  374. data/lib/badline/cpu/microcode.rb +0 -177
  375. data/lib/badline/cpu/operations.rb +0 -88
  376. data/lib/badline/cpu/saved_state.rb +0 -115
  377. data/lib/badline/cpu/stack_operations.rb +0 -82
  378. data/lib/badline/cpu.rb +0 -10
  379. data/lib/badline/cycleable.rb +0 -38
  380. data/lib/badline/datasette.rb +0 -161
  381. data/lib/badline/drive/core.rb +0 -147
  382. data/lib/badline/drive/fast_serial.rb +0 -73
  383. data/lib/badline/drive/idle.rb +0 -125
  384. data/lib/badline/drive/idle_cpu.rb +0 -28
  385. data/lib/badline/drive/orbit.rb +0 -79
  386. data/lib/badline/drive/serial_lines.rb +0 -73
  387. data/lib/badline/drive/sleep.rb +0 -112
  388. data/lib/badline/drive/vias.rb +0 -84
  389. data/lib/badline/drive/watch.rb +0 -150
  390. data/lib/badline/drive.rb +0 -11
  391. data/lib/badline/drive1541/bus.rb +0 -86
  392. data/lib/badline/drive1541/byte_ready.rb +0 -48
  393. data/lib/badline/drive1541/cpu.rb +0 -11
  394. data/lib/badline/drive1541/disk.rb +0 -318
  395. data/lib/badline/drive1541/disk_state.rb +0 -94
  396. data/lib/badline/drive1541/disk_via.rb +0 -36
  397. data/lib/badline/drive1541/gcr.rb +0 -57
  398. data/lib/badline/drive1541/mechanism.rb +0 -313
  399. data/lib/badline/drive1541/mechanism_state.rb +0 -68
  400. data/lib/badline/drive1541/rotation.rb +0 -121
  401. data/lib/badline/drive1541/sector_reader.rb +0 -76
  402. data/lib/badline/drive1541/serial_port.rb +0 -18
  403. data/lib/badline/drive1541/track.rb +0 -98
  404. data/lib/badline/drive1541.rb +0 -111
  405. data/lib/badline/drive1571/bus.rb +0 -111
  406. data/lib/badline/drive1571/cpu.rb +0 -11
  407. data/lib/badline/drive1571/serial_port.rb +0 -37
  408. data/lib/badline/drive1571/serial_via.rb +0 -25
  409. data/lib/badline/drive1571/wd1770.rb +0 -120
  410. data/lib/badline/drive1571.rb +0 -175
  411. data/lib/badline/drive1581/bus.rb +0 -137
  412. data/lib/badline/drive1581/cpu.rb +0 -11
  413. data/lib/badline/drive1581/disk.rb +0 -135
  414. data/lib/badline/drive1581/disk_state.rb +0 -87
  415. data/lib/badline/drive1581/mechanism.rb +0 -117
  416. data/lib/badline/drive1581/serial_port.rb +0 -52
  417. data/lib/badline/drive1581/slot.rb +0 -78
  418. data/lib/badline/drive1581/track.rb +0 -100
  419. data/lib/badline/drive1581/track_bytes.rb +0 -75
  420. data/lib/badline/drive1581/wd1772/registers.rb +0 -89
  421. data/lib/badline/drive1581/wd1772/sector_writes.rb +0 -73
  422. data/lib/badline/drive1581/wd1772/sectors.rb +0 -150
  423. data/lib/badline/drive1581/wd1772/state.rb +0 -66
  424. data/lib/badline/drive1581/wd1772/stepping.rb +0 -109
  425. data/lib/badline/drive1581/wd1772/tracks.rb +0 -125
  426. data/lib/badline/drive1581/wd1772/turn.rb +0 -73
  427. data/lib/badline/drive1581/wd1772.rb +0 -216
  428. data/lib/badline/drive1581.rb +0 -233
  429. data/lib/badline/iec_bus.rb +0 -159
  430. data/lib/badline/instruction.rb +0 -334
  431. data/lib/badline/instruction_set/arithmetic.rb +0 -114
  432. data/lib/badline/instruction_set/bitwise.rb +0 -126
  433. data/lib/badline/instruction_set/branch.rb +0 -79
  434. data/lib/badline/instruction_set/flag.rb +0 -63
  435. data/lib/badline/instruction_set/illegal.rb +0 -270
  436. data/lib/badline/instruction_set/inc_dec.rb +0 -69
  437. data/lib/badline/instruction_set/stack.rb +0 -52
  438. data/lib/badline/instruction_set/transfer.rb +0 -137
  439. data/lib/badline/instruction_set.rb +0 -55
  440. data/lib/badline/interrupts.rb +0 -104
  441. data/lib/badline/joystick.rb +0 -25
  442. data/lib/badline/kernal_trap/channel.rb +0 -181
  443. data/lib/badline/keyboard.rb +0 -112
  444. data/lib/badline/ram_expansion/banking.rb +0 -30
  445. data/lib/badline/ram_expansion/plus256k.rb +0 -54
  446. data/lib/badline/ram_expansion/plus60k.rb +0 -43
  447. data/lib/badline/ram_expansion/unexpanded.rb +0 -28
  448. data/lib/badline/ram_expansion.rb +0 -21
  449. data/lib/badline/reu/dma/saved_state.rb +0 -50
  450. data/lib/badline/reu/dma.rb +0 -273
  451. data/lib/badline/reu/ram.rb +0 -107
  452. data/lib/badline/reu/saved_state.rb +0 -80
  453. data/lib/badline/reu/trigger.rb +0 -32
  454. data/lib/badline/reu.rb +0 -289
  455. data/lib/badline/sid/catch_up.rb +0 -120
  456. data/lib/badline/sid/decimator.rb +0 -64
  457. data/lib/badline/sid/envelope/state.rb +0 -68
  458. data/lib/badline/sid/envelope.rb +0 -239
  459. data/lib/badline/sid/filter.rb +0 -263
  460. data/lib/badline/sid/voice.rb +0 -77
  461. data/lib/badline/sid/waveform/combined.rb +0 -126
  462. data/lib/badline/sid/waveform/fast_forward.rb +0 -105
  463. data/lib/badline/sid/waveform/noise_writeback.rb +0 -122
  464. data/lib/badline/sid/waveform/state.rb +0 -66
  465. data/lib/badline/sid/waveform.rb +0 -305
  466. data/lib/badline/sid.rb +0 -303
  467. data/lib/badline/time_of_day.rb +0 -228
  468. data/lib/badline/traps.rb +0 -19
  469. data/lib/badline/via/control_lines.rb +0 -122
  470. data/lib/badline/via/fast_forward.rb +0 -53
  471. data/lib/badline/via/interrupt_register.rb +0 -67
  472. data/lib/badline/via/shift_register.rb +0 -180
  473. data/lib/badline/via/state.rb +0 -42
  474. data/lib/badline/via/timer1.rb +0 -131
  475. data/lib/badline/via/timer2.rb +0 -133
  476. data/lib/badline/via.rb +0 -266
  477. data/lib/badline/vic/bank.rb +0 -133
  478. data/lib/badline/vic/border_mask.rb +0 -86
  479. data/lib/badline/vic/collisions.rb +0 -115
  480. data/lib/badline/vic/color_patches.rb +0 -47
  481. data/lib/badline/vic/display_state.rb +0 -185
  482. data/lib/badline/vic/graphics_mode.rb +0 -164
  483. data/lib/badline/vic/graphics_shifter.rb +0 -207
  484. data/lib/badline/vic/register_log.rb +0 -81
  485. data/lib/badline/vic/registers.rb +0 -195
  486. data/lib/badline/vic/sequencer.rb +0 -275
  487. data/lib/badline/vic/sequencer_output.rb +0 -116
  488. data/lib/badline/vic/sequencer_state.rb +0 -89
  489. data/lib/badline/vic/sprite/internal_bus.rb +0 -68
  490. data/lib/badline/vic/sprite/saved_state.rb +0 -64
  491. data/lib/badline/vic/sprite/shifter.rb +0 -146
  492. data/lib/badline/vic/sprite/timing.rb +0 -56
  493. data/lib/badline/vic/sprite.rb +0 -337
  494. data/lib/badline/vic/sprites.rb +0 -282
  495. data/lib/badline/vic/sprites_state.rb +0 -57
  496. data/lib/badline/vic.rb +0 -852
  497. data/lib/badline/vic20/sound/output.rb +0 -83
  498. data/lib/badline/vic20/sound.rb +0 -233
  499. data/lib/badline/z80/arithmetic.rb +0 -117
  500. data/lib/badline/z80/bit_page.rb +0 -55
  501. data/lib/badline/z80/block_io.rb +0 -58
  502. data/lib/badline/z80/block_transfers.rb +0 -59
  503. data/lib/badline/z80/bus_cycles.rb +0 -111
  504. data/lib/badline/z80/core.rb +0 -113
  505. data/lib/badline/z80/exchanges.rb +0 -50
  506. data/lib/badline/z80/extended_page.rb +0 -89
  507. data/lib/badline/z80/flags.rb +0 -37
  508. data/lib/badline/z80/interrupt_acceptance.rb +0 -46
  509. data/lib/badline/z80/jumps.rb +0 -82
  510. data/lib/badline/z80/loads.rb +0 -98
  511. data/lib/badline/z80/main_page.rb +0 -134
  512. data/lib/badline/z80/registers.rb +0 -120
  513. data/lib/badline/z80/saved_state.rb +0 -69
  514. data/lib/badline/z80/shifts.rb +0 -64
  515. data/lib/badline/z80.rb +0 -11
  516. /data/lib/badline/snapshot/{fields.rb → vice/fields.rb} +0 -0
@@ -0,0 +1,241 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "badline/chips/sid/filter/saved_state"
4
+
5
+ module Badline
6
+ module Chips
7
+ class SID
8
+ # The SID's multimode filter: a state-variable design integrating low-,
9
+ # band- and high-pass in parallel, with the mixer and master volume on
10
+ # its output, and the C64 board's own RC network after that.
11
+ #
12
+ # Voices are routed through it or around it per the RES/FILT register,
13
+ # and the outputs selected by MODE/VOL are summed back together with the
14
+ # unfiltered ones.
15
+ class Filter
16
+ include SavedState
17
+
18
+ # Cutoff frequency in Hz at each chip's breakpoints. The 6581 curve was
19
+ # sampled from a real chip by reSID and bends sharply around $300; the
20
+ # 8580's is close to a straight line from 0 to 12.5 kHz. The curve
21
+ # between breakpoints is interpolated linearly; reSID fits a spline, so
22
+ # the two differ by a few Hz mid-segment.
23
+ CUTOFF_POINTS = {
24
+ mos6581: [[0, 220], [128, 230], [256, 250], [384, 300],
25
+ [512, 420], [640, 780], [768, 1600], [832, 2300],
26
+ [896, 3200], [960, 4300], [1024, 5000], [1088, 5400],
27
+ [1152, 5800], [1216, 6000], [1280, 6200], [1344, 6400],
28
+ [1408, 6600], [1472, 6800], [1536, 7000], [1600, 7200],
29
+ [1664, 7400], [1728, 7600], [1792, 7800], [1856, 8000],
30
+ [1920, 8200], [1984, 8400], [2047, 8600]].freeze,
31
+ mos8580: [[0, 0], [128, 800], [256, 1600], [384, 2500],
32
+ [512, 3300], [640, 4100], [768, 4800], [896, 5600],
33
+ [1024, 6500], [1152, 7500], [1280, 8400], [1408, 9200],
34
+ [1536, 9800], [1664, 10_500], [1792, 11_000],
35
+ [1920, 11_700], [2047, 12_500]].freeze
36
+ }.freeze
37
+
38
+ # 2^20 / 1 MHz, so one cycle of integration is a plain shift.
39
+ SCALE = (2**20) / 1_000_000.0
40
+
41
+ # The integrator goes unstable above ~16 kHz at a one-cycle step.
42
+ W0_MAX = (2 * Math::PI * 16_000 * SCALE).to_i
43
+
44
+ # A step of several cycles scales w0 by their count. reSID keeps such
45
+ # a step stable by capping the cutoff at 4 kHz over 8 cycles; this is
46
+ # the same bound on the product, so a single cycle is never capped.
47
+ W0_STEP_MAX = (2 * Math::PI * 4_000 * 8 * SCALE).to_i
48
+
49
+ def self.build_w0(points)
50
+ table = Array.new(2048)
51
+ points.each_cons(2) do |(fc0, hz0), (fc1, hz1)|
52
+ (fc0..fc1).each do |register|
53
+ hz = hz0 + (((hz1 - hz0) * (register - fc0)).to_f / (fc1 - fc0))
54
+ table[register] = [(2 * Math::PI * hz * SCALE).to_i, W0_MAX].min
55
+ end
56
+ end
57
+ table.freeze
58
+ end
59
+ private_class_method :build_w0
60
+
61
+ W0 = CUTOFF_POINTS.transform_values { |points| build_w0(points) }.freeze
62
+
63
+ # 1024/Q, with Q sweeping from 0.707 up to 1.707 across the register.
64
+ RESONANCE = (0x0..0xf).map { |res| (1024.0 / (0.707 + (res / 15.0))).to_i }.freeze
65
+
66
+ # The mixer's own input DC offset, derived by reSID from the 6581's
67
+ # zero-volume and full-volume output levels. The 8580 has none.
68
+ MIXER_DC = { mos6581: -((0xfff * 0xff) / 18) >> 7, mos8580: 0 }.freeze
69
+
70
+ # Fractional bits carried in the integrators. At low cutoffs a cycle's
71
+ # step is only a few units, which truncating to whole units would
72
+ # swallow, and always toward minus infinity.
73
+ FRACTION = 12
74
+
75
+ # The RC network between the SID's audio pin and the C64's output jack:
76
+ # a 10kΩ/1000pF low-pass at ~16 kHz, then a 1kΩ/10µF high-pass at
77
+ # ~16 Hz that strips the 6581's DC offset off the mix.
78
+ class External
79
+ W0_LOWPASS = (100_000 * SCALE).round
80
+ W0_HIGHPASS = (100 * SCALE).round
81
+
82
+ # The high-pass step is W0_HIGHPASS/2^20 of the gap, so in whole
83
+ # units it stalls on any gap under ~10k, as reSID's does. Sixteen
84
+ # fractional bits bring that under one unit.
85
+ FRACTION = 16
86
+
87
+ # Its input arrives with the filter's fractional bits still on.
88
+ INPUT_SHIFT = FRACTION - Filter::FRACTION
89
+
90
+ attr_reader :output
91
+
92
+ def initialize
93
+ @lowpass = 0
94
+ @highpass = 0
95
+ @output = 0
96
+ end
97
+
98
+ def save_state(out)
99
+ out.int(@lowpass).int(@highpass).int(@output)
100
+ end
101
+
102
+ def load_state(input)
103
+ @lowpass = input.int
104
+ @highpass = input.int
105
+ @output = input.int
106
+ end
107
+
108
+ # Takes the input in 1/2^Filter::FRACTION units, and reads the
109
+ # output out in whole ones. A step of several cycles moves each pole
110
+ # at most onto its input.
111
+ def cycle!(input, cycles = 1)
112
+ w0_lowpass = [(W0_LOWPASS >> 8) * cycles, 1 << 12].min
113
+ w0_highpass = [W0_HIGHPASS * cycles, 1 << 20].min
114
+ delta_lowpass = (w0_lowpass * ((input << INPUT_SHIFT) - @lowpass)) >> 12
115
+ delta_highpass = (w0_highpass * (@lowpass - @highpass)) >> 20
116
+ @output = (@lowpass - @highpass) >> FRACTION
117
+ @lowpass += delta_lowpass
118
+ @highpass += delta_highpass
119
+ @output
120
+ end
121
+ end
122
+
123
+ attr_reader :model, :cutoff, :routing, :mode, :volume
124
+
125
+ def initialize(model: :mos6581)
126
+ @model = model
127
+ @w0_table = W0.fetch(model)
128
+ @mixer_dc = MIXER_DC.fetch(model)
129
+ @external = External.new
130
+ reset
131
+ end
132
+
133
+ # The integrators and the registers carry over; the cutoff maps
134
+ # through the new model's curve.
135
+ def model=(model)
136
+ @model = model
137
+ @w0_table = W0.fetch(model)
138
+ @mixer_dc = MIXER_DC.fetch(model)
139
+ @w0 = @w0_table[@cutoff]
140
+ end
141
+
142
+ def reset
143
+ @cutoff = 0x000
144
+ @routing = 0x0
145
+ @mode = 0x0
146
+ @volume = 0x0
147
+ @voice3_off = false
148
+ @w0 = @w0_table[0]
149
+ @resonance = RESONANCE[0]
150
+ @lowpass = @bandpass = @highpass = 0
151
+ @input = @unfiltered = 0
152
+ end
153
+
154
+ def write(reg, value)
155
+ case reg
156
+ when 0x15 then self.cutoff = (@cutoff & 0x7f8) | (value & 0x07)
157
+ when 0x16 then self.cutoff = (value << 3) | (@cutoff & 0x007)
158
+ when 0x17 then write_resonance(value)
159
+ when 0x18 then write_mode_volume(value)
160
+ end
161
+ end
162
+
163
+ def cutoff=(value)
164
+ @cutoff = value & 0x7ff
165
+ @w0 = @w0_table[@cutoff]
166
+ end
167
+
168
+ def voice3_off? = @voice3_off
169
+
170
+ def lowpass = @lowpass >> FRACTION
171
+ def bandpass = @bandpass >> FRACTION
172
+ def highpass = @highpass >> FRACTION
173
+
174
+ # Integrates `cycles` cycles in one step, from the voices' output at
175
+ # the end of them.
176
+ def cycle!(voices, cycles = 1)
177
+ route(voices)
178
+ w0 = @w0 * cycles
179
+ w0 = W0_STEP_MAX if w0 > W0_STEP_MAX
180
+ @bandpass -= (w0 * @highpass) >> 20
181
+ @lowpass -= (w0 * @bandpass) >> 20
182
+ @highpass = ((@bandpass * @resonance) >> 10) - @lowpass - (@input << FRACTION)
183
+ @external.cycle!(fine_mix, cycles)
184
+ end
185
+
186
+ # The SID's own audio pin, before the board's RC network.
187
+ def mix = fine_mix >> FRACTION
188
+
189
+ def output = @external.output
190
+
191
+ private
192
+
193
+ def write_resonance(value)
194
+ @routing = value & 0x0f
195
+ @resonance = RESONANCE[(value >> 4) & 0x0f]
196
+ end
197
+
198
+ def write_mode_volume(value)
199
+ @volume = value & 0x0f
200
+ @mode = (value >> 4) & 0x07
201
+ @voice3_off = value.anybits?(0x80)
202
+ end
203
+
204
+ # Unrolled over the three voices, like SID#clock!. Voice 3 is silenced
205
+ # by MODE/VOL bit 7 only while it bypasses the filter.
206
+ def route(voices)
207
+ voice1, voice2, voice3 = voices
208
+ @input = 0
209
+ @unfiltered = 0
210
+ route_voice(voice1.output >> 7, 0x1)
211
+ route_voice(voice2.output >> 7, 0x2)
212
+ if @routing & 0x4 != 0
213
+ @input += voice3.output >> 7
214
+ elsif !@voice3_off
215
+ @unfiltered += voice3.output >> 7
216
+ end
217
+ end
218
+
219
+ def route_voice(output, bit)
220
+ if @routing & bit == bit
221
+ @input += output
222
+ else
223
+ @unfiltered += output
224
+ end
225
+ end
226
+
227
+ # The mix with the integrators' fractional bits kept, so the RC
228
+ # network isn't fed the filter's output floored to whole units.
229
+ def fine_mix = (((@unfiltered + @mixer_dc) << FRACTION) + filtered) * @volume
230
+
231
+ def filtered
232
+ value = 0
233
+ value += @lowpass if @mode & 0x1 != 0
234
+ value += @bandpass if @mode & 0x2 != 0
235
+ value += @highpass if @mode & 0x4 != 0
236
+ value
237
+ end
238
+ end
239
+ end
240
+ end
241
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ module Chips
5
+ class SID
6
+ # The SID's state for a snapshot.
7
+ module SavedState
8
+ # How far the recording has got into the sample it is averaging, for
9
+ # a host that carries on recording from a saved state: it calls
10
+ # #record, then #load_recording.
11
+ def save_recording(out)
12
+ decimator = @decimator
13
+ raise ArgumentError, "the SID isn't recording" if decimator.nil?
14
+
15
+ out.marker("RECORDING")
16
+ decimator.save_state(out)
17
+ end
18
+
19
+ def load_recording(input)
20
+ decimator = @decimator
21
+ raise ArgumentError, "the SID isn't recording" if decimator.nil?
22
+
23
+ input.marker("RECORDING")
24
+ decimator.load_state(input)
25
+ end
26
+
27
+ # The registers, the data bus, the voices and the filter, and the span
28
+ # the DSP has yet to catch up on, as it stands: saving leaves the SID
29
+ # as it was. Whether it synthesizes or records is the host's.
30
+ def save_state(out)
31
+ out.marker("SID")
32
+ @registers.save_state(out)
33
+ out.int(@bus_value).int(@bus_ttl).int(@pending_cycles).ints(@deferred_writes)
34
+ @voices.each { |voice| voice.save_state(out) }
35
+ @filter.save_state(out)
36
+ end
37
+
38
+ def load_state(input)
39
+ input.marker("SID")
40
+ @registers.load_state(input)
41
+ @bus_value = input.int
42
+ @bus_ttl = input.int
43
+ @pending_cycles = input.int
44
+ input.ints_into(@deferred_writes)
45
+ @voices.each { |voice| voice.load_state(input) }
46
+ @filter.load_state(input)
47
+ update_span_rules
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,79 @@
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 save_state(out)
49
+ @waveform.save_state(out)
50
+ @envelope.save_state(out)
51
+ end
52
+
53
+ def load_state(input)
54
+ @waveform.load_state(input)
55
+ @envelope.load_state(input)
56
+ end
57
+
58
+ def control=(value)
59
+ @waveform.control = value
60
+ @envelope.control = value
61
+ end
62
+
63
+ def cycle!
64
+ @envelope.cycle!
65
+ @waveform.cycle!
66
+ end
67
+
68
+ def fast_forward(cycles)
69
+ @envelope.fast_forward(cycles)
70
+ @waveform.fast_forward(cycles)
71
+ end
72
+
73
+ def output
74
+ ((@waveform.output - @wave_zero) * @envelope.output) + @dc_offset
75
+ end
76
+ end
77
+ end
78
+ end
79
+ 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