badline 0.12.1 → 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 (505) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +17 -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 +3 -3
  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} +1 -1
  17. data/lib/badline/c128/bus.rb +18 -17
  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 +14 -9
  27. data/lib/badline/{computer → c64}/attachments.rb +4 -6
  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/{computer → c64}/common.rb +2 -2
  73. data/lib/badline/{computer → c64}/kernal_traps.rb +18 -18
  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 +11 -11
  82. data/lib/badline/{computer.rb → c64.rb} +18 -13
  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 +26 -28
  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 +3 -3
  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/load.rb +5 -5
  262. data/lib/badline/kernal_trap/save.rb +4 -4
  263. data/lib/badline/kernal_trap/serial.rb +7 -7
  264. data/lib/badline/kernal_trap.rb +1 -2
  265. data/lib/badline/machine/clocking.rb +1 -1
  266. data/lib/badline/machine.rb +5 -5
  267. data/lib/badline/media/true_drive.rb +1 -1
  268. data/lib/badline/media/tune.rb +2 -2
  269. data/lib/badline/media.rb +3 -3
  270. data/lib/badline/model.rb +1 -1
  271. data/lib/badline/roms/README +6 -6
  272. data/lib/badline/sdl.rb +1 -0
  273. data/lib/badline/{storage/sid_file.rb → sid_tune/file.rb} +3 -3
  274. data/lib/badline/{storage → sid_tune}/hvsc.rb +7 -7
  275. data/lib/badline/{storage → sid_tune}/song_lengths.rb +2 -2
  276. data/lib/badline/{storage → sid_tune}/stil/credit.rb +1 -1
  277. data/lib/badline/{storage → sid_tune}/stil.rb +3 -3
  278. data/lib/badline/sid_tune.rb +6 -0
  279. data/lib/badline/snapshot/c128_setup.rb +43 -0
  280. data/lib/badline/snapshot/c64_setup.rb +79 -0
  281. data/lib/badline/snapshot/image.rb +5 -25
  282. data/lib/badline/snapshot/machine_state.rb +1 -1
  283. data/lib/badline/snapshot/setup.rb +22 -66
  284. data/lib/badline/snapshot/state.rb +8 -18
  285. data/lib/badline/snapshot/storage_setup.rb +68 -0
  286. data/lib/badline/snapshot/vic20_setup.rb +37 -0
  287. data/lib/badline/snapshot/vice/c64_mem.rb +1 -1
  288. data/lib/badline/snapshot/vice/restore.rb +1 -1
  289. data/lib/badline/snapshot/vice/reu1764.rb +1 -1
  290. data/lib/badline/snapshot/vice/reu_fields.rb +40 -0
  291. data/lib/badline/snapshot/vice.rb +7 -5
  292. data/lib/badline/snapshot.rb +3 -4
  293. data/lib/badline/storage/d64_image.rb +0 -2
  294. data/lib/badline/storage/d71_image.rb +0 -2
  295. data/lib/badline/storage/d81_image.rb +0 -2
  296. data/lib/badline/storage/disk_image.rb +3 -6
  297. data/lib/badline/storage/g64_image.rb +2 -2
  298. data/lib/badline/storage/host_directory.rb +0 -5
  299. data/lib/badline/storage/t64.rb +2 -8
  300. data/lib/badline/storage/unavailable.rb +1 -1
  301. data/lib/badline/storage.rb +0 -37
  302. data/lib/badline/support/c128_checkpoint.rb +39 -0
  303. data/lib/badline/support/checkpoint.rb +108 -0
  304. data/lib/badline/support/vic20_checkpoint.rb +38 -0
  305. data/lib/badline/support.rb +5 -0
  306. data/lib/badline/version.rb +1 -1
  307. data/lib/badline/vic20/attachments.rb +5 -5
  308. data/lib/badline/vic20/bus/saved_state.rb +35 -0
  309. data/lib/badline/vic20/bus.rb +3 -25
  310. data/lib/badline/vic20/cpu.rb +1 -1
  311. data/lib/badline/vic20/port_wiring.rb +4 -4
  312. data/lib/badline/vic20/saved_state.rb +16 -30
  313. data/lib/badline/vic20/user_via_ports.rb +2 -2
  314. data/lib/badline/vic20.rb +19 -14
  315. metadata +245 -221
  316. data/lib/badline/address_bus/extra_sids.rb +0 -30
  317. data/lib/badline/address_bus/fittings.rb +0 -38
  318. data/lib/badline/address_bus/pla.rb +0 -147
  319. data/lib/badline/address_bus/roms.rb +0 -39
  320. data/lib/badline/address_bus/saved_state.rb +0 -66
  321. data/lib/badline/address_bus/sid_slots.rb +0 -42
  322. data/lib/badline/address_bus.rb +0 -216
  323. data/lib/badline/cartridge/action_replay.rb +0 -126
  324. data/lib/badline/cartridge/atomic_power.rb +0 -24
  325. data/lib/badline/cartridge/bank.rb +0 -81
  326. data/lib/badline/cartridge/comal80.rb +0 -50
  327. data/lib/badline/cartridge/dinamic.rb +0 -34
  328. data/lib/badline/cartridge/easy_flash.rb +0 -145
  329. data/lib/badline/cartridge/epyx_fastload.rb +0 -79
  330. data/lib/badline/cartridge/final_cartridge3.rb +0 -89
  331. data/lib/badline/cartridge/flash.rb +0 -306
  332. data/lib/badline/cartridge/freezer.rb +0 -33
  333. data/lib/badline/cartridge/fun_play.rb +0 -38
  334. data/lib/badline/cartridge/g_mod2.rb +0 -74
  335. data/lib/badline/cartridge/game_system.rb +0 -40
  336. data/lib/badline/cartridge/geo_ram.rb +0 -79
  337. data/lib/badline/cartridge/kcs_power.rb +0 -75
  338. data/lib/badline/cartridge/mach5.rb +0 -32
  339. data/lib/badline/cartridge/magic_desk.rb +0 -30
  340. data/lib/badline/cartridge/ocean.rb +0 -35
  341. data/lib/badline/cartridge/pagefox.rb +0 -68
  342. data/lib/badline/cartridge/retro_replay.rb +0 -320
  343. data/lib/badline/cartridge/rex_utility.rb +0 -29
  344. data/lib/badline/cartridge/rgcd.rb +0 -58
  345. data/lib/badline/cartridge/simons_basic.rb +0 -35
  346. data/lib/badline/cartridge/standard.rb +0 -15
  347. data/lib/badline/cartridge/super_games.rb +0 -48
  348. data/lib/badline/cartridge/westermann.rb +0 -30
  349. data/lib/badline/cartridge/zaxxon.rb +0 -66
  350. data/lib/badline/cartridge.rb +0 -303
  351. data/lib/badline/checkpoint.rb +0 -98
  352. data/lib/badline/cia/fast_forward.rb +0 -60
  353. data/lib/badline/cia/interrupt_register.rb +0 -143
  354. data/lib/badline/cia/serial.rb +0 -275
  355. data/lib/badline/cia/state.rb +0 -47
  356. data/lib/badline/cia/timer.rb +0 -206
  357. data/lib/badline/cia.rb +0 -279
  358. data/lib/badline/color_memory.rb +0 -25
  359. data/lib/badline/control_ports.rb +0 -76
  360. data/lib/badline/cpu/addressing.rb +0 -131
  361. data/lib/badline/cpu/boundary.rb +0 -54
  362. data/lib/badline/cpu/core.rb +0 -165
  363. data/lib/badline/cpu/microcode.rb +0 -177
  364. data/lib/badline/cpu/operations.rb +0 -88
  365. data/lib/badline/cpu/saved_state.rb +0 -115
  366. data/lib/badline/cpu/stack_operations.rb +0 -82
  367. data/lib/badline/cpu.rb +0 -10
  368. data/lib/badline/cpu_port.rb +0 -112
  369. data/lib/badline/datasette.rb +0 -161
  370. data/lib/badline/drive/core.rb +0 -147
  371. data/lib/badline/drive/fast_serial.rb +0 -73
  372. data/lib/badline/drive/idle.rb +0 -125
  373. data/lib/badline/drive/idle_cpu.rb +0 -28
  374. data/lib/badline/drive/orbit.rb +0 -79
  375. data/lib/badline/drive/serial_lines.rb +0 -73
  376. data/lib/badline/drive/sleep.rb +0 -112
  377. data/lib/badline/drive/vias.rb +0 -84
  378. data/lib/badline/drive/watch.rb +0 -150
  379. data/lib/badline/drive.rb +0 -11
  380. data/lib/badline/drive1541/bus.rb +0 -86
  381. data/lib/badline/drive1541/byte_ready.rb +0 -48
  382. data/lib/badline/drive1541/cpu.rb +0 -11
  383. data/lib/badline/drive1541/disk.rb +0 -315
  384. data/lib/badline/drive1541/disk_state.rb +0 -86
  385. data/lib/badline/drive1541/disk_via.rb +0 -36
  386. data/lib/badline/drive1541/gcr.rb +0 -57
  387. data/lib/badline/drive1541/mechanism.rb +0 -313
  388. data/lib/badline/drive1541/mechanism_state.rb +0 -68
  389. data/lib/badline/drive1541/rotation.rb +0 -121
  390. data/lib/badline/drive1541/sector_reader.rb +0 -76
  391. data/lib/badline/drive1541/serial_port.rb +0 -18
  392. data/lib/badline/drive1541/track.rb +0 -98
  393. data/lib/badline/drive1541.rb +0 -111
  394. data/lib/badline/drive1571/bus.rb +0 -111
  395. data/lib/badline/drive1571/cpu.rb +0 -11
  396. data/lib/badline/drive1571/serial_port.rb +0 -37
  397. data/lib/badline/drive1571/serial_via.rb +0 -25
  398. data/lib/badline/drive1571/wd1770.rb +0 -120
  399. data/lib/badline/drive1571.rb +0 -175
  400. data/lib/badline/drive1581/bus.rb +0 -137
  401. data/lib/badline/drive1581/cpu.rb +0 -11
  402. data/lib/badline/drive1581/disk.rb +0 -135
  403. data/lib/badline/drive1581/disk_state.rb +0 -87
  404. data/lib/badline/drive1581/mechanism.rb +0 -117
  405. data/lib/badline/drive1581/serial_port.rb +0 -52
  406. data/lib/badline/drive1581/slot.rb +0 -78
  407. data/lib/badline/drive1581/track.rb +0 -100
  408. data/lib/badline/drive1581/track_bytes.rb +0 -75
  409. data/lib/badline/drive1581/wd1772/registers.rb +0 -89
  410. data/lib/badline/drive1581/wd1772/sector_writes.rb +0 -73
  411. data/lib/badline/drive1581/wd1772/sectors.rb +0 -150
  412. data/lib/badline/drive1581/wd1772/state.rb +0 -66
  413. data/lib/badline/drive1581/wd1772/stepping.rb +0 -109
  414. data/lib/badline/drive1581/wd1772/tracks.rb +0 -125
  415. data/lib/badline/drive1581/wd1772/turn.rb +0 -73
  416. data/lib/badline/drive1581/wd1772.rb +0 -216
  417. data/lib/badline/drive1581.rb +0 -233
  418. data/lib/badline/iec_bus.rb +0 -159
  419. data/lib/badline/instruction.rb +0 -334
  420. data/lib/badline/instruction_set/arithmetic.rb +0 -114
  421. data/lib/badline/instruction_set/bitwise.rb +0 -126
  422. data/lib/badline/instruction_set/branch.rb +0 -79
  423. data/lib/badline/instruction_set/flag.rb +0 -63
  424. data/lib/badline/instruction_set/illegal.rb +0 -270
  425. data/lib/badline/instruction_set/inc_dec.rb +0 -69
  426. data/lib/badline/instruction_set/stack.rb +0 -52
  427. data/lib/badline/instruction_set/transfer.rb +0 -137
  428. data/lib/badline/instruction_set.rb +0 -55
  429. data/lib/badline/interrupts.rb +0 -104
  430. data/lib/badline/joystick.rb +0 -25
  431. data/lib/badline/kernal_trap/channel.rb +0 -181
  432. data/lib/badline/keyboard.rb +0 -112
  433. data/lib/badline/ram_expansion/banking.rb +0 -30
  434. data/lib/badline/ram_expansion/plus256k.rb +0 -54
  435. data/lib/badline/ram_expansion/plus60k.rb +0 -43
  436. data/lib/badline/ram_expansion/unexpanded.rb +0 -28
  437. data/lib/badline/ram_expansion.rb +0 -21
  438. data/lib/badline/reu/dma/saved_state.rb +0 -50
  439. data/lib/badline/reu/dma.rb +0 -273
  440. data/lib/badline/reu/ram.rb +0 -107
  441. data/lib/badline/reu/saved_state.rb +0 -80
  442. data/lib/badline/reu/trigger.rb +0 -32
  443. data/lib/badline/reu.rb +0 -289
  444. data/lib/badline/sid/catch_up.rb +0 -120
  445. data/lib/badline/sid/decimator.rb +0 -64
  446. data/lib/badline/sid/envelope/state.rb +0 -68
  447. data/lib/badline/sid/envelope.rb +0 -239
  448. data/lib/badline/sid/filter.rb +0 -263
  449. data/lib/badline/sid/voice.rb +0 -77
  450. data/lib/badline/sid/waveform/combined.rb +0 -126
  451. data/lib/badline/sid/waveform/fast_forward.rb +0 -105
  452. data/lib/badline/sid/waveform/noise_writeback.rb +0 -122
  453. data/lib/badline/sid/waveform/state.rb +0 -66
  454. data/lib/badline/sid/waveform.rb +0 -305
  455. data/lib/badline/sid.rb +0 -303
  456. data/lib/badline/time_of_day.rb +0 -228
  457. data/lib/badline/traps.rb +0 -19
  458. data/lib/badline/via/control_lines.rb +0 -122
  459. data/lib/badline/via/fast_forward.rb +0 -53
  460. data/lib/badline/via/interrupt_register.rb +0 -67
  461. data/lib/badline/via/shift_register.rb +0 -180
  462. data/lib/badline/via/state.rb +0 -42
  463. data/lib/badline/via/timer1.rb +0 -131
  464. data/lib/badline/via/timer2.rb +0 -133
  465. data/lib/badline/via.rb +0 -266
  466. data/lib/badline/vic/bank.rb +0 -133
  467. data/lib/badline/vic/border_mask.rb +0 -86
  468. data/lib/badline/vic/collisions.rb +0 -115
  469. data/lib/badline/vic/color_patches.rb +0 -47
  470. data/lib/badline/vic/display_state.rb +0 -185
  471. data/lib/badline/vic/graphics_mode.rb +0 -164
  472. data/lib/badline/vic/graphics_shifter.rb +0 -207
  473. data/lib/badline/vic/register_log.rb +0 -81
  474. data/lib/badline/vic/registers.rb +0 -195
  475. data/lib/badline/vic/sequencer.rb +0 -275
  476. data/lib/badline/vic/sequencer_output.rb +0 -116
  477. data/lib/badline/vic/sequencer_state.rb +0 -89
  478. data/lib/badline/vic/sprite/internal_bus.rb +0 -68
  479. data/lib/badline/vic/sprite/saved_state.rb +0 -64
  480. data/lib/badline/vic/sprite/shifter.rb +0 -146
  481. data/lib/badline/vic/sprite/timing.rb +0 -56
  482. data/lib/badline/vic/sprite.rb +0 -337
  483. data/lib/badline/vic/sprites.rb +0 -282
  484. data/lib/badline/vic/sprites_state.rb +0 -57
  485. data/lib/badline/vic.rb +0 -853
  486. data/lib/badline/vic20/sound/output.rb +0 -83
  487. data/lib/badline/vic20/sound.rb +0 -233
  488. data/lib/badline/z80/arithmetic.rb +0 -117
  489. data/lib/badline/z80/bit_page.rb +0 -55
  490. data/lib/badline/z80/block_io.rb +0 -58
  491. data/lib/badline/z80/block_transfers.rb +0 -59
  492. data/lib/badline/z80/bus_cycles.rb +0 -111
  493. data/lib/badline/z80/core.rb +0 -113
  494. data/lib/badline/z80/exchanges.rb +0 -50
  495. data/lib/badline/z80/extended_page.rb +0 -89
  496. data/lib/badline/z80/flags.rb +0 -37
  497. data/lib/badline/z80/interrupt_acceptance.rb +0 -46
  498. data/lib/badline/z80/jumps.rb +0 -82
  499. data/lib/badline/z80/loads.rb +0 -98
  500. data/lib/badline/z80/main_page.rb +0 -134
  501. data/lib/badline/z80/registers.rb +0 -120
  502. data/lib/badline/z80/saved_state.rb +0 -69
  503. data/lib/badline/z80/shifts.rb +0 -64
  504. data/lib/badline/z80.rb +0 -11
  505. /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