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,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ module Chips
5
+ class CPU
6
+ module Traps
7
+ def install_trap(addr, &handler)
8
+ (@traps ||= {})[addr] = handler
9
+ end
10
+
11
+ def remove_trap(addr)
12
+ @traps&.delete(addr)
13
+ end
14
+
15
+ private
16
+
17
+ def run_traps
18
+ @traps[@program_counter]&.call if @traps
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "badline/chips/cpu/core"
4
+
5
+ module Badline
6
+ module Chips
7
+ # The C64's 6510, on the C64::Bus.
8
+ class CPU
9
+ include Core
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,122 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ module Chips
5
+ class SID
6
+ # Running the DSP over the cycles that have passed since it last caught
7
+ # up, in spans that fast-forward the voices and end on a whole cycle.
8
+ module CatchUp
9
+ private
10
+
11
+ # Runs the cycles that have passed, landing each queued write on the
12
+ # cycle the CPU wrote it on.
13
+ def catch_up
14
+ replayed = 0
15
+ i = 0
16
+ while i < @deferred_writes.length
17
+ cycle = @deferred_writes[i]
18
+ run(cycle - replayed)
19
+ replayed = cycle
20
+ apply_write(@deferred_writes[i + 1], @deferred_writes[i + 2])
21
+ i += 3
22
+ end
23
+ run(@pending_cycles - replayed)
24
+ @deferred_writes.clear
25
+ @pending_cycles = 0
26
+ end
27
+
28
+ def run(cycles)
29
+ while cycles != 0
30
+ span = span(cycles)
31
+ fast_forward(span - 1) if span > 1
32
+ clock!(span)
33
+ cycles -= span
34
+ end
35
+ end
36
+
37
+ # The longest stretch that can be fast-forwarded. A combined waveform
38
+ # feeding back into its own oscillator steps cycle by cycle, and a span
39
+ # never runs past an MSB rise that hard-syncs the next voice, so both
40
+ # land on a whole cycle.
41
+ def span(cycles)
42
+ return 1 if @stepped
43
+
44
+ span = @synthesizing ? synthesis_span(cycles) : cycles
45
+ return span unless @synced
46
+
47
+ span = sync_span(@waveform1, span)
48
+ span = sync_span(@waveform2, span)
49
+ sync_span(@waveform3, span)
50
+ end
51
+
52
+ # The filter steps at most @filter_chunk cycles at a time, and never
53
+ # across the end of a recorded sample.
54
+ def synthesis_span(cycles)
55
+ span = [cycles, @filter_chunk].min
56
+ return span unless @decimator
57
+
58
+ [@decimator.cycles_to_close, span].min
59
+ end
60
+
61
+ # Whether a span has to step, or watch for hard sync, only changes with
62
+ # a voice's control register.
63
+ def update_span_rules
64
+ @stepped = @voices.any? { |voice| voice.waveform.stepped?(@synthesizing) }
65
+ @synced = @voices.any? { |voice| voice.waveform.sync? }
66
+ end
67
+
68
+ def sync_span(waveform, span)
69
+ return span unless waveform.sync_dest.sync?
70
+
71
+ rise = waveform.cycles_to_msb_rise
72
+ rise && rise < span ? rise : span
73
+ end
74
+
75
+ # Unrolled over the three voices: the per-cycle block calls cost
76
+ # measurably on the synthesis path.
77
+ def fast_forward(cycles)
78
+ @voice1.fast_forward(cycles)
79
+ @voice2.fast_forward(cycles)
80
+ @voice3.fast_forward(cycles)
81
+ end
82
+
83
+ # The last cycle of a span, run whole. The filter integrates the span
84
+ # from the voices' output at its end.
85
+ def clock!(span)
86
+ @voice1.cycle!
87
+ @voice2.cycle!
88
+ @voice3.cycle!
89
+ @waveform1.synchronize!
90
+ @waveform2.synchronize!
91
+ @waveform3.synchronize!
92
+ return unless @synthesizing
93
+
94
+ @filter.cycle!(@voices, span)
95
+ record_sample(span) if @decimator
96
+ end
97
+
98
+ def record_sample(span)
99
+ sample = @decimator.push(current_sample, span)
100
+ return unless sample
101
+
102
+ @samples << sample
103
+ record_voices if @voice_samples
104
+ end
105
+
106
+ def record_voices
107
+ voice_samples = @voice_samples
108
+ voice_samples << (@voice1.output >> 7)
109
+ voice_samples << (@voice2.output >> 7)
110
+ voice_samples << (@voice3.output >> 7)
111
+ end
112
+
113
+ def current_sample
114
+ sample = @filter.output / SAMPLE_DIVISOR
115
+ return sample unless sample < -0x8000 || sample > 0x7fff
116
+
117
+ sample.clamp(-0x8000, 0x7fff)
118
+ end
119
+ end
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ module Chips
5
+ class SID
6
+ # Reduces the SID's one-sample-per-cycle stream to an audio rate by
7
+ # averaging each window. Point sampling would be cheaper, but the pulse
8
+ # and noise waveforms carry enough energy above the output Nyquist to
9
+ # alias audibly.
10
+ #
11
+ # A sample can stand for several cycles at once, when the DSP has run
12
+ # them as one step.
13
+ class Decimator
14
+ def initialize(clock_hz:, rate:)
15
+ @clock_hz = clock_hz
16
+ @rate = rate
17
+ @phase = 0
18
+ @sum = 0
19
+ @count = 0
20
+ @cycles_to_close = window_cycles
21
+ end
22
+
23
+ # Cycles until the current window closes, counting the next one as 1.
24
+ attr_reader :cycles_to_close
25
+
26
+ # How far the current window has got. The clock and the rate are the
27
+ # host's.
28
+ def save_state(out)
29
+ out.int(@phase).int(@sum).int(@count).int(@cycles_to_close)
30
+ end
31
+
32
+ def load_state(input)
33
+ @phase = input.int
34
+ @sum = input.int
35
+ @count = input.int
36
+ @cycles_to_close = input.int
37
+ end
38
+
39
+ # Returns the averaged sample when these cycles close a window, and
40
+ # nil otherwise. They must not run past the window's end.
41
+ def push(sample, cycles = 1)
42
+ @sum += sample * cycles
43
+ @count += cycles
44
+ @phase += @rate * cycles
45
+ @cycles_to_close -= cycles
46
+ return unless @phase >= @clock_hz
47
+
48
+ @phase -= @clock_hz
49
+ @cycles_to_close = window_cycles
50
+ average
51
+ end
52
+
53
+ private
54
+
55
+ def window_cycles = (@clock_hz - @phase + @rate - 1) / @rate
56
+
57
+ def average
58
+ value = @sum.fdiv(@count).round
59
+ @sum = 0
60
+ @count = 0
61
+ value
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ module Chips
5
+ class SID
6
+ class Envelope
7
+ # A snapshot of an envelope: its rates, the gate, the state and the
8
+ # counters and pipelines between them, and the same fields as reSID
9
+ # keeps them, for VICE's SIDEXTENDED module.
10
+ module SavedState
11
+ # The fields reSID keeps for an envelope between cycles: the rate and
12
+ # exponential counters, the level, the state as an index into STATES,
13
+ # whether it holds at zero, the two periods and the pipeline.
14
+ def resid_fields
15
+ [@rate_counter, @exponential_counter, @counter, STATES.index(@state).to_i, @hold_zero ? 1 : 0,
16
+ @rate_period, @exponential_period, @envelope_pipeline]
17
+ end
18
+
19
+ # Takes the fields resid_fields gives from `fields`, from `at` on.
20
+ def restore_resid_fields(fields, at)
21
+ @rate_counter = fields[at]
22
+ @exponential_counter = fields[at + 1]
23
+ @counter = @env3 = fields[at + 2]
24
+ state = fields[at + 3]
25
+ @state = @next_state = STATES.fetch(state.clamp(0, STATES.length - 1)).to_sym
26
+ @hold_zero = fields[at + 4] != 0
27
+ @rate_period = fields[at + 5]
28
+ @exponential_period = fields[at + 6]
29
+ @envelope_pipeline = fields[at + 7]
30
+ end
31
+
32
+ def save_state(out)
33
+ [@attack, @decay, @sustain, @release].each { |rate| out.int(rate) }
34
+ out.boolean(@gate).int(STATES.index(@state)).int(STATES.index(@next_state))
35
+ out.int(@counter).int(@env3).int(@rate_counter).int(@rate_period).boolean(@reset_rate_counter)
36
+ out.int(@exponential_counter).int(@exponential_period)
37
+ out.int(@state_pipeline).int(@envelope_pipeline).int(@exponential_pipeline).boolean(@hold_zero)
38
+ end
39
+
40
+ def load_state(input)
41
+ @attack = input.int
42
+ @decay = input.int
43
+ @sustain = input.int
44
+ @release = input.int
45
+ @gate = input.boolean?
46
+ @state = STATES.fetch(input.int).to_sym
47
+ @next_state = STATES.fetch(input.int).to_sym
48
+ load_counters(input)
49
+ end
50
+
51
+ private
52
+
53
+ def load_counters(input)
54
+ @counter = input.int
55
+ @env3 = input.int
56
+ @rate_counter = input.int
57
+ @rate_period = input.int
58
+ @reset_rate_counter = input.boolean?
59
+ @exponential_counter = input.int
60
+ @exponential_period = input.int
61
+ @state_pipeline = input.int
62
+ @envelope_pipeline = input.int
63
+ @exponential_pipeline = input.int
64
+ @hold_zero = input.boolean?
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,241 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "badline/chips/sid/envelope/saved_state"
4
+
5
+ module Badline
6
+ module Chips
7
+ class SID
8
+ # ADSR envelope generator.
9
+ #
10
+ # A 15-bit rate counter divides the 1 MHz clock down to one envelope step
11
+ # per period. Attack steps linearly; decay and release run through a
12
+ # second, level-dependent divider that approximates an exponential
13
+ # curve.
14
+ #
15
+ # Each stage runs a cycle or more behind the one feeding it, after
16
+ # reSID 1.0's single-cycle pipeline: the rate counter resets the cycle
17
+ # after it matches, the envelope counter steps two cycles after that,
18
+ # and a gate edge only switches the rate over on its second cycle.
19
+ class Envelope
20
+ # Rate counter comparison values for each ADSR nibble. The counter
21
+ # takes a cycle to reset, so the period is one cycle longer (2ms to
22
+ # 8s over the attack range).
23
+ PERIODS = [8, 31, 62, 94, 148, 219, 266, 312,
24
+ 391, 976, 1953, 3125, 3906, 11_719, 19_531, 31_250].freeze
25
+
26
+ # Envelope levels where the exponential divider changes, and the
27
+ # divider it changes to.
28
+ EXPONENTIAL_PERIODS = { 0xff => 1, 0x5d => 2, 0x36 => 4, 0x1a => 8,
29
+ 0x0e => 16, 0x06 => 30, 0x00 => 1 }.freeze
30
+
31
+ # The states a snapshot names by index.
32
+ STATES = %i[attack decay_sustain release].freeze
33
+
34
+ include SavedState
35
+
36
+ attr_reader :state, :counter, :env3
37
+
38
+ def initialize
39
+ @attack = @decay = @sustain = @release = 0x0
40
+ @gate = false
41
+ @state = @next_state = :release
42
+ @counter = @env3 = 0x00
43
+ @rate_counter = 0
44
+ @rate_period = PERIODS[0]
45
+ @reset_rate_counter = false
46
+ @exponential_counter = 0
47
+ @exponential_period = 1
48
+ @state_pipeline = @envelope_pipeline = @exponential_pipeline = 0
49
+ @hold_zero = true
50
+ end
51
+
52
+ def output = @counter
53
+
54
+ # The gate edge switches the state over two cycles. Rising, it runs
55
+ # the decay rate for the first of them. The rate counter is left
56
+ # running, so the first step lands somewhere inside the current
57
+ # period.
58
+ def control=(value)
59
+ gate = value.anybits?(0x01)
60
+ return if gate == @gate
61
+
62
+ @gate = gate
63
+ gate ? gate_on : gate_off
64
+ end
65
+
66
+ def attack_decay=(value)
67
+ @attack = (value >> 4) & 0x0f
68
+ @decay = value & 0x0f
69
+ @rate_period = PERIODS[@attack] if @state == :attack
70
+ @rate_period = PERIODS[@decay] if @state == :decay_sustain
71
+ end
72
+
73
+ def sustain_release=(value)
74
+ @sustain = (value >> 4) & 0x0f
75
+ @release = value & 0x0f
76
+ @rate_period = PERIODS[@release] if @state == :release
77
+ end
78
+
79
+ # ENV3 reads the counter as it stood at the start of the cycle.
80
+ #
81
+ # Lowering the rate period below the current counter sends it the long
82
+ # way round through 2^15 before the envelope can step, the ADSR delay
83
+ # bug that hard restarts rely on. Counting from 0x7fff wraps to 1.
84
+ def cycle!
85
+ @env3 = @counter
86
+ run_pipeline if pipelined?
87
+ return @reset_rate_counter = true if @rate_counter == @rate_period
88
+
89
+ @rate_counter += 1
90
+ @rate_counter = 1 if @rate_counter == 0x8000
91
+ end
92
+
93
+ # Runs `cycles` cycles as #cycle! would. Between steps only the rate
94
+ # counter moves, so it jumps from one match to the next and the cycles
95
+ # around each step run whole. Frozen at zero, a period changes
96
+ # nothing, so whole periods are counted off at once.
97
+ def fast_forward(cycles)
98
+ while cycles.positive?
99
+ if pipelined?
100
+ cycle!
101
+ cycles -= 1
102
+ next
103
+ end
104
+
105
+ @env3 = @counter
106
+ idle = cycles_to_match
107
+ return advance_rate_counter(cycles) if cycles < idle
108
+
109
+ advance_rate_counter(idle)
110
+ cycles = skip_frozen_periods(cycles - idle)
111
+ end
112
+ end
113
+
114
+ private
115
+
116
+ def land_step
117
+ @envelope_pipeline -= 1
118
+ step if @envelope_pipeline.zero? && !@hold_zero
119
+ end
120
+
121
+ def gate_on
122
+ @next_state = :attack
123
+ @state = :decay_sustain
124
+ @rate_period = PERIODS[@decay]
125
+ @state_pipeline = 2
126
+ if @reset_rate_counter || @exponential_pipeline == 2
127
+ @envelope_pipeline = @exponential_period == 1 || @exponential_pipeline == 2 ? 2 : 4
128
+ elsif @exponential_pipeline == 1
129
+ @state_pipeline = 3
130
+ end
131
+ end
132
+
133
+ def gate_off
134
+ @next_state = :release
135
+ @state_pipeline = @envelope_pipeline.positive? ? 3 : 2
136
+ end
137
+
138
+ def state_change
139
+ @state_pipeline -= 1
140
+ if @next_state == :attack
141
+ enter_attack if @state_pipeline.zero?
142
+ elsif (@state == :attack && @state_pipeline.zero?) ||
143
+ (@state == :decay_sustain && @state_pipeline == 1)
144
+ @state = :release
145
+ @rate_period = PERIODS[@release]
146
+ end
147
+ end
148
+
149
+ def enter_attack
150
+ @state = :attack
151
+ @rate_period = PERIODS[@attack]
152
+ @hold_zero = false
153
+ end
154
+
155
+ # The first attack step also resets the exponential divider.
156
+ def reset_rate_counter
157
+ @rate_counter = 0
158
+ @reset_rate_counter = false
159
+ if @state == :attack
160
+ @exponential_counter = 0
161
+ @envelope_pipeline = 2
162
+ elsif !@hold_zero && (@exponential_counter += 1) == @exponential_period
163
+ @exponential_pipeline = @exponential_period == 1 ? 1 : 2
164
+ end
165
+ end
166
+
167
+ def exponential_step
168
+ @exponential_counter = 0
169
+ return unless @state == :release || (@state == :decay_sustain && @counter != sustain_level)
170
+
171
+ @envelope_pipeline = 1
172
+ end
173
+
174
+ def pipelined?
175
+ @reset_rate_counter || (@state_pipeline | @envelope_pipeline | @exponential_pipeline) != 0
176
+ end
177
+
178
+ def run_pipeline
179
+ state_change if @state_pipeline.positive?
180
+ land_step if @envelope_pipeline.positive?
181
+ if @exponential_pipeline.positive? && (@exponential_pipeline -= 1).zero?
182
+ exponential_step
183
+ elsif @reset_rate_counter
184
+ reset_rate_counter
185
+ end
186
+ end
187
+
188
+ # Cycles that only count before the rate counter matches its period.
189
+ def cycles_to_match
190
+ return @rate_period - @rate_counter if @rate_counter <= @rate_period
191
+
192
+ 0x7fff - @rate_counter + @rate_period
193
+ end
194
+
195
+ def advance_rate_counter(cycles)
196
+ @rate_counter += cycles
197
+ @rate_counter -= 0x7fff if @rate_counter > 0x7fff
198
+ end
199
+
200
+ # With the counter on its period, a frozen envelope comes back to the
201
+ # same state every period plus one cycles. Otherwise the next cycle
202
+ # starts a step.
203
+ def skip_frozen_periods(cycles)
204
+ cycles %= @rate_period + 1 if @hold_zero && @state != :attack
205
+ return cycles unless cycles.positive?
206
+
207
+ cycle!
208
+ cycles - 1
209
+ end
210
+
211
+ def step
212
+ if @state == :attack
213
+ attack_step
214
+ else
215
+ @counter = (@counter - 1) & 0xff
216
+ end
217
+ set_exponential_period
218
+ end
219
+
220
+ def attack_step
221
+ @counter = (@counter + 1) & 0xff
222
+ return unless @counter == 0xff
223
+
224
+ @state = :decay_sustain
225
+ @rate_period = PERIODS[@decay]
226
+ end
227
+
228
+ def sustain_level = (@sustain << 4) | @sustain
229
+
230
+ # Reaching zero freezes the envelope until the gate is raised again.
231
+ def set_exponential_period
232
+ period = EXPONENTIAL_PERIODS[@counter]
233
+ return unless period
234
+
235
+ @exponential_period = period
236
+ @hold_zero = true if @counter.zero?
237
+ end
238
+ end
239
+ end
240
+ end
241
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ module Chips
5
+ class SID
6
+ class Filter
7
+ # The filter's state for a snapshot.
8
+ module SavedState
9
+ # The registers as the filter decoded them and its integrators. The
10
+ # W0 table is the model's.
11
+ def save_state(out)
12
+ out.int(@cutoff).int(@routing).int(@mode).int(@volume).boolean(@voice3_off).int(@w0).int(@resonance)
13
+ [@highpass, @bandpass, @lowpass, @unfiltered, @input].each { |value| out.int(value) }
14
+ @external.save_state(out)
15
+ end
16
+
17
+ def load_state(input)
18
+ @cutoff = input.int
19
+ @routing = input.int
20
+ @mode = input.int
21
+ @volume = input.int
22
+ @voice3_off = input.boolean?
23
+ @w0 = input.int
24
+ @resonance = input.int
25
+ load_integrators(input)
26
+ end
27
+
28
+ private
29
+
30
+ def load_integrators(input)
31
+ @highpass = input.int
32
+ @bandpass = input.int
33
+ @lowpass = input.int
34
+ @unfiltered = input.int
35
+ @input = input.int
36
+ @external.load_state(input)
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end