badline 0.12.1 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (565) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +66 -1
  3. data/CLAUDE.md +86 -38
  4. data/CONTRIBUTING.md +4 -3
  5. data/README.md +9 -6
  6. data/doc/library.md +9 -9
  7. data/doc/media.md +2 -1
  8. data/doc/pinned-behaviour.md +134 -105
  9. data/doc/snapshot-format.md +1047 -0
  10. data/doc/snapshots.md +73 -43
  11. data/lib/badline/audio/bare_player.rb +53 -20
  12. data/lib/badline/audio/cli.rb +6 -6
  13. data/lib/badline/audio/jukebox.rb +2 -2
  14. data/lib/badline/audio/machine_player.rb +16 -10
  15. data/lib/badline/{media → audio}/queue.rb +1 -1
  16. data/lib/badline/audio/queued_tune.rb +6 -6
  17. data/lib/badline/audio/renderer.rb +33 -22
  18. data/lib/badline/audio/stereo.rb +1 -1
  19. data/lib/badline/c128/banks.rb +6 -3
  20. data/lib/badline/c128/bus/keyed_state.rb +45 -0
  21. data/lib/badline/c128/bus.rb +35 -20
  22. data/lib/badline/c128/color_lines.rb +2 -2
  23. data/lib/badline/c128/cpu.rb +1 -1
  24. data/lib/badline/c128/drives.rb +14 -34
  25. data/lib/badline/c128/fast_serial.rb +5 -5
  26. data/lib/badline/c128/keyed_modules.rb +117 -0
  27. data/lib/badline/c128/keyed_state.rb +91 -0
  28. data/lib/badline/c128/keys.rb +9 -0
  29. data/lib/badline/c128/mmu.rb +9 -8
  30. data/lib/badline/c128/mmu_pages.rb +21 -1
  31. data/lib/badline/c128/modes.rb +21 -11
  32. data/lib/badline/c128/z80.rb +2 -2
  33. data/lib/badline/c128/z80_turns.rb +1 -1
  34. data/lib/badline/c128.rb +21 -16
  35. data/lib/badline/c64/attachments.rb +42 -0
  36. data/lib/badline/c64/banked_ram/banking.rb +32 -0
  37. data/lib/badline/c64/banked_ram/plus256k.rb +64 -0
  38. data/lib/badline/c64/banked_ram/plus60k.rb +47 -0
  39. data/lib/badline/c64/banked_ram/unexpanded.rb +30 -0
  40. data/lib/badline/c64/banked_ram.rb +23 -0
  41. data/lib/badline/c64/bus/debug_register.rb +32 -0
  42. data/lib/badline/c64/bus/extra_sids.rb +32 -0
  43. data/lib/badline/c64/bus/fittings.rb +40 -0
  44. data/lib/badline/c64/bus/keyed_state.rb +41 -0
  45. data/lib/badline/c64/bus/pla.rb +149 -0
  46. data/lib/badline/c64/bus/roms.rb +41 -0
  47. data/lib/badline/c64/bus/sid_slots.rb +44 -0
  48. data/lib/badline/c64/bus.rb +219 -0
  49. data/lib/badline/c64/cartridge/action_replay.rb +115 -0
  50. data/lib/badline/c64/cartridge/atomic_power.rb +26 -0
  51. data/lib/badline/c64/cartridge/bank.rb +83 -0
  52. data/lib/badline/c64/cartridge/comal80.rb +52 -0
  53. data/lib/badline/c64/cartridge/dinamic.rb +36 -0
  54. data/lib/badline/c64/cartridge/easy_flash.rb +132 -0
  55. data/lib/badline/c64/cartridge/epyx_fastload.rb +73 -0
  56. data/lib/badline/c64/cartridge/final_cartridge3.rb +79 -0
  57. data/lib/badline/c64/cartridge/flash.rb +279 -0
  58. data/lib/badline/c64/cartridge/freezer.rb +35 -0
  59. data/lib/badline/c64/cartridge/fun_play.rb +40 -0
  60. data/lib/badline/c64/cartridge/g_mod2.rb +64 -0
  61. data/lib/badline/c64/cartridge/game_system.rb +42 -0
  62. data/lib/badline/c64/cartridge/geo_ram.rb +65 -0
  63. data/lib/badline/c64/cartridge/kcs_power.rb +69 -0
  64. data/lib/badline/c64/cartridge/keyed_state.rb +393 -0
  65. data/lib/badline/c64/cartridge/mach5.rb +34 -0
  66. data/lib/badline/c64/cartridge/magic_desk.rb +32 -0
  67. data/lib/badline/c64/cartridge/ocean.rb +37 -0
  68. data/lib/badline/c64/cartridge/pagefox.rb +60 -0
  69. data/lib/badline/c64/cartridge/retro_replay.rb +278 -0
  70. data/lib/badline/c64/cartridge/rex_utility.rb +31 -0
  71. data/lib/badline/c64/cartridge/rgcd.rb +50 -0
  72. data/lib/badline/c64/cartridge/simons_basic.rb +37 -0
  73. data/lib/badline/c64/cartridge/standard.rb +17 -0
  74. data/lib/badline/c64/cartridge/super_games.rb +40 -0
  75. data/lib/badline/c64/cartridge/westermann.rb +32 -0
  76. data/lib/badline/c64/cartridge/zaxxon.rb +52 -0
  77. data/lib/badline/c64/cartridge.rb +232 -0
  78. data/lib/badline/c64/color_memory.rb +27 -0
  79. data/lib/badline/{computer → c64}/common.rb +2 -2
  80. data/lib/badline/{computer → c64}/kernal_traps.rb +39 -36
  81. data/lib/badline/c64/keyed_modules.rb +111 -0
  82. data/lib/badline/c64/keyed_state.rb +68 -0
  83. data/lib/badline/c64/reu/dma.rb +271 -0
  84. data/lib/badline/c64/reu/keyed_state.rb +140 -0
  85. data/lib/badline/c64/reu/ram.rb +94 -0
  86. data/lib/badline/c64/reu/trigger.rb +34 -0
  87. data/lib/badline/c64/reu.rb +289 -0
  88. data/lib/badline/c64/true_drives.rb +120 -0
  89. data/lib/badline/{computer.rb → c64.rb} +24 -15
  90. data/lib/badline/chips/cia/fast_forward.rb +62 -0
  91. data/lib/badline/chips/cia/interrupt_register.rb +128 -0
  92. data/lib/badline/chips/cia/keyed_parts.rb +213 -0
  93. data/lib/badline/chips/cia/serial.rb +246 -0
  94. data/lib/badline/chips/cia/time_of_day.rb +210 -0
  95. data/lib/badline/chips/cia/timer.rb +188 -0
  96. data/lib/badline/chips/cia.rb +281 -0
  97. data/lib/badline/chips/cpu/addressing.rb +133 -0
  98. data/lib/badline/chips/cpu/boundary.rb +56 -0
  99. data/lib/badline/chips/cpu/core.rb +183 -0
  100. data/lib/badline/chips/cpu/instruction.rb +338 -0
  101. data/lib/badline/chips/cpu/instruction_set/arithmetic.rb +118 -0
  102. data/lib/badline/chips/cpu/instruction_set/bitwise.rb +130 -0
  103. data/lib/badline/chips/cpu/instruction_set/branch.rb +83 -0
  104. data/lib/badline/chips/cpu/instruction_set/flag.rb +67 -0
  105. data/lib/badline/chips/cpu/instruction_set/illegal.rb +274 -0
  106. data/lib/badline/chips/cpu/instruction_set/inc_dec.rb +73 -0
  107. data/lib/badline/chips/cpu/instruction_set/stack.rb +56 -0
  108. data/lib/badline/chips/cpu/instruction_set/transfer.rb +141 -0
  109. data/lib/badline/chips/cpu/instruction_set.rb +59 -0
  110. data/lib/badline/chips/cpu/interrupts.rb +108 -0
  111. data/lib/badline/chips/cpu/keyed_state.rb +177 -0
  112. data/lib/badline/chips/cpu/microcode.rb +195 -0
  113. data/lib/badline/chips/cpu/operations.rb +90 -0
  114. data/lib/badline/chips/cpu/port.rb +133 -0
  115. data/lib/badline/chips/cpu/stack_operations.rb +84 -0
  116. data/lib/badline/chips/cpu/traps.rb +23 -0
  117. data/lib/badline/chips/cpu.rb +17 -0
  118. data/lib/badline/chips/sid/catch_up.rb +122 -0
  119. data/lib/badline/chips/sid/decimator.rb +68 -0
  120. data/lib/badline/chips/sid/envelope.rb +236 -0
  121. data/lib/badline/chips/sid/filter.rb +227 -0
  122. data/lib/badline/chips/sid/keyed_parts.rb +286 -0
  123. data/lib/badline/chips/sid/voice.rb +69 -0
  124. data/lib/badline/chips/sid/waveform/combined.rb +128 -0
  125. data/lib/badline/chips/sid/waveform/fast_forward.rb +107 -0
  126. data/lib/badline/chips/sid/waveform/noise_writeback.rb +124 -0
  127. data/lib/badline/chips/sid/waveform.rb +305 -0
  128. data/lib/badline/chips/sid.rb +264 -0
  129. data/lib/badline/chips/ted/beam.rb +119 -0
  130. data/lib/badline/chips/ted/timers.rb +66 -0
  131. data/lib/badline/chips/ted.rb +143 -0
  132. data/lib/badline/chips/tia.rb +49 -0
  133. data/lib/badline/chips/vdc/keyed_parts.rb +105 -0
  134. data/lib/badline/{c128 → chips}/vdc/memory.rb +1 -1
  135. data/lib/badline/{c128 → chips}/vdc/painter.rb +1 -13
  136. data/lib/badline/{c128 → chips}/vdc/raster.rb +1 -17
  137. data/lib/badline/{c128 → chips}/vdc/window.rb +1 -10
  138. data/lib/badline/{c128 → chips}/vdc.rb +6 -31
  139. data/lib/badline/chips/via/control_lines.rb +109 -0
  140. data/lib/badline/chips/via/fast_forward.rb +55 -0
  141. data/lib/badline/chips/via/interrupt_register.rb +60 -0
  142. data/lib/badline/chips/via/keyed_parts.rb +163 -0
  143. data/lib/badline/chips/via/shift_register.rb +168 -0
  144. data/lib/badline/chips/via/timer1.rb +120 -0
  145. data/lib/badline/chips/via/timer2.rb +117 -0
  146. data/lib/badline/chips/via.rb +267 -0
  147. data/lib/badline/chips/vic/bank.rb +135 -0
  148. data/lib/badline/chips/vic/border_mask.rb +78 -0
  149. data/lib/badline/chips/vic/collisions.rb +106 -0
  150. data/lib/badline/chips/vic/color_patches.rb +38 -0
  151. data/lib/badline/chips/vic/display_state.rb +168 -0
  152. data/lib/badline/chips/vic/graphics_mode.rb +166 -0
  153. data/lib/badline/chips/vic/graphics_shifter.rb +183 -0
  154. data/lib/badline/chips/vic/keyed_parts.rb +228 -0
  155. data/lib/badline/chips/vic/keyed_sprites.rb +225 -0
  156. data/lib/badline/chips/vic/keyed_state.rb +102 -0
  157. data/lib/badline/chips/vic/register_log.rb +65 -0
  158. data/lib/badline/chips/vic/registers.rb +188 -0
  159. data/lib/badline/chips/vic/sequencer.rb +275 -0
  160. data/lib/badline/chips/vic/sequencer_output.rb +118 -0
  161. data/lib/badline/chips/vic/sprite/internal_bus.rb +59 -0
  162. data/lib/badline/chips/vic/sprite/shifter.rb +148 -0
  163. data/lib/badline/chips/vic/sprite/timing.rb +58 -0
  164. data/lib/badline/chips/vic/sprite.rb +337 -0
  165. data/lib/badline/chips/vic/sprites.rb +281 -0
  166. data/lib/badline/chips/vic.rb +787 -0
  167. data/lib/badline/chips/vic_i/keyed_parts.rb +142 -0
  168. data/lib/badline/{vic20/vic → chips/vic_i}/painter.rb +2 -30
  169. data/lib/badline/chips/vic_i/sound/output.rb +85 -0
  170. data/lib/badline/chips/vic_i/sound.rb +227 -0
  171. data/lib/badline/{vic20/vic → chips/vic_i}/text_window.rb +2 -2
  172. data/lib/badline/{vic20/vic.rb → chips/vic_i.rb} +6 -6
  173. data/lib/badline/chips/z80/arithmetic.rb +119 -0
  174. data/lib/badline/chips/z80/bit_page.rb +57 -0
  175. data/lib/badline/chips/z80/block_io.rb +60 -0
  176. data/lib/badline/chips/z80/block_transfers.rb +61 -0
  177. data/lib/badline/chips/z80/bus_cycles.rb +113 -0
  178. data/lib/badline/chips/z80/core.rb +115 -0
  179. data/lib/badline/chips/z80/exchanges.rb +52 -0
  180. data/lib/badline/chips/z80/extended_page.rb +91 -0
  181. data/lib/badline/chips/z80/flags.rb +39 -0
  182. data/lib/badline/chips/z80/interrupt_acceptance.rb +48 -0
  183. data/lib/badline/chips/z80/jumps.rb +84 -0
  184. data/lib/badline/chips/z80/keyed_state.rb +73 -0
  185. data/lib/badline/chips/z80/loads.rb +100 -0
  186. data/lib/badline/chips/z80/main_page.rb +136 -0
  187. data/lib/badline/chips/z80/registers.rb +122 -0
  188. data/lib/badline/chips/z80/shifts.rb +66 -0
  189. data/lib/badline/chips/z80.rb +13 -0
  190. data/lib/badline/chips.rb +10 -0
  191. data/lib/badline/core.rb +13 -32
  192. data/lib/badline/drives/datasette/keyed_state.rb +38 -0
  193. data/lib/badline/drives/datasette.rb +173 -0
  194. data/lib/badline/drives/engine/core.rb +152 -0
  195. data/lib/badline/drives/engine/fast_serial.rb +75 -0
  196. data/lib/badline/drives/engine/idle.rb +127 -0
  197. data/lib/badline/drives/engine/idle_cpu.rb +30 -0
  198. data/lib/badline/drives/engine/orbit.rb +81 -0
  199. data/lib/badline/drives/engine/serial_lines.rb +72 -0
  200. data/lib/badline/drives/engine/sleep.rb +114 -0
  201. data/lib/badline/drives/engine/vias.rb +86 -0
  202. data/lib/badline/drives/engine/watch.rb +152 -0
  203. data/lib/badline/drives/engine.rb +11 -0
  204. data/lib/badline/drives/model1541/bus.rb +75 -0
  205. data/lib/badline/drives/model1541/byte_ready.rb +50 -0
  206. data/lib/badline/drives/model1541/cpu.rb +13 -0
  207. data/lib/badline/drives/model1541/disk.rb +318 -0
  208. data/lib/badline/drives/model1541/disk_via.rb +38 -0
  209. data/lib/badline/drives/model1541/gcr.rb +59 -0
  210. data/lib/badline/drives/model1541/keyed_state.rb +251 -0
  211. data/lib/badline/drives/model1541/mechanism.rb +313 -0
  212. data/lib/badline/drives/model1541/rotation.rb +123 -0
  213. data/lib/badline/drives/model1541/sector_reader.rb +78 -0
  214. data/lib/badline/drives/model1541/serial_port.rb +20 -0
  215. data/lib/badline/drives/model1541/track.rb +100 -0
  216. data/lib/badline/drives/model1541.rb +80 -0
  217. data/lib/badline/drives/model1571/bus.rb +100 -0
  218. data/lib/badline/drives/model1571/cpu.rb +13 -0
  219. data/lib/badline/drives/model1571/keyed_state.rb +108 -0
  220. data/lib/badline/drives/model1571/serial_port.rb +39 -0
  221. data/lib/badline/drives/model1571/serial_via.rb +27 -0
  222. data/lib/badline/drives/model1571/wd1770.rb +108 -0
  223. data/lib/badline/drives/model1571.rb +141 -0
  224. data/lib/badline/drives/model1581/bus.rb +126 -0
  225. data/lib/badline/drives/model1581/cpu.rb +13 -0
  226. data/lib/badline/drives/model1581/disk.rb +133 -0
  227. data/lib/badline/drives/model1581/keyed_state.rb +280 -0
  228. data/lib/badline/drives/model1581/mechanism.rb +98 -0
  229. data/lib/badline/drives/model1581/serial_port.rb +54 -0
  230. data/lib/badline/drives/model1581/slot.rb +61 -0
  231. data/lib/badline/drives/model1581/track.rb +102 -0
  232. data/lib/badline/drives/model1581/track_bytes.rb +77 -0
  233. data/lib/badline/drives/model1581/wd1772/registers.rb +91 -0
  234. data/lib/badline/drives/model1581/wd1772/sector_writes.rb +75 -0
  235. data/lib/badline/drives/model1581/wd1772/sectors.rb +152 -0
  236. data/lib/badline/drives/model1581/wd1772/stepping.rb +111 -0
  237. data/lib/badline/drives/model1581/wd1772/tracks.rb +127 -0
  238. data/lib/badline/drives/model1581/wd1772/turn.rb +75 -0
  239. data/lib/badline/drives/model1581/wd1772.rb +216 -0
  240. data/lib/badline/drives/model1581.rb +206 -0
  241. data/lib/badline/drives/serial_bus.rb +161 -0
  242. data/lib/badline/drives.rb +8 -0
  243. data/lib/badline/frontend/app.rb +3 -3
  244. data/lib/badline/frontend/boot.rb +4 -4
  245. data/lib/badline/frontend/controls.rb +19 -3
  246. data/lib/badline/frontend/drive_led.rb +44 -21
  247. data/lib/badline/frontend/machine_window.rb +3 -1
  248. data/lib/badline/frontend/menu/menu_dialogs.rb +2 -2
  249. data/lib/badline/frontend/player/filter_view.rb +1 -1
  250. data/lib/badline/frontend/player/player_controller.rb +2 -2
  251. data/lib/badline/frontend/player/player_screen.rb +1 -0
  252. data/lib/badline/frontend/player/theme.rb +7 -7
  253. data/lib/badline/frontend/snapshots.rb +12 -1
  254. data/lib/badline/frontend/timeline.rb +30 -12
  255. data/lib/badline/input/control_ports.rb +78 -0
  256. data/lib/badline/input/joystick.rb +27 -0
  257. data/lib/badline/input/keyboard.rb +114 -0
  258. data/lib/badline/input/matrix_typist.rb +98 -0
  259. data/lib/badline/input.rb +4 -0
  260. data/lib/badline/kernal_trap/chrout.rb +45 -0
  261. data/lib/badline/kernal_trap/{drive → dos}/block_commands.rb +1 -1
  262. data/lib/badline/kernal_trap/dos/channel.rb +156 -0
  263. data/lib/badline/kernal_trap/{drive → dos}/channels.rb +1 -26
  264. data/lib/badline/kernal_trap/{drive → dos}/memory.rb +1 -10
  265. data/lib/badline/kernal_trap/{drive → dos}/parameters.rb +1 -1
  266. data/lib/badline/kernal_trap/{drive → dos}/status.rb +1 -9
  267. data/lib/badline/kernal_trap/{drive → dos}/write_file.rb +1 -19
  268. data/lib/badline/kernal_trap/{drive → dos}/writes.rb +1 -1
  269. data/lib/badline/kernal_trap/{drive.rb → dos.rb} +9 -27
  270. data/lib/badline/kernal_trap/file_routine.rb +4 -4
  271. data/lib/badline/kernal_trap/keyed_state.rb +150 -0
  272. data/lib/badline/kernal_trap/layout.rb +75 -11
  273. data/lib/badline/kernal_trap/load.rb +19 -21
  274. data/lib/badline/kernal_trap/routine.rb +12 -0
  275. data/lib/badline/kernal_trap/save.rb +12 -22
  276. data/lib/badline/kernal_trap/serial.rb +8 -24
  277. data/lib/badline/kernal_trap.rb +3 -2
  278. data/lib/badline/machine/clocking.rb +1 -1
  279. data/lib/badline/machine/keyboard_buffer.rb +49 -0
  280. data/lib/badline/machine/settling.rb +29 -0
  281. data/lib/badline/machine.rb +10 -7
  282. data/lib/badline/media/boot_disk.rb +14 -2
  283. data/lib/badline/media/extensions.rb +10 -0
  284. data/lib/badline/media/true_drive.rb +1 -1
  285. data/lib/badline/media/tune.rb +2 -2
  286. data/lib/badline/media.rb +3 -3
  287. data/lib/badline/memory.rb +3 -6
  288. data/lib/badline/model.rb +1 -1
  289. data/lib/badline/options/family.rb +5 -2
  290. data/lib/badline/options/help.rb +42 -16
  291. data/lib/badline/options/table.rb +2 -0
  292. data/lib/badline/options/validation.rb +2 -0
  293. data/lib/badline/options.rb +6 -1
  294. data/lib/badline/plus4/acia.rb +45 -0
  295. data/lib/badline/plus4/bus.rb +312 -0
  296. data/lib/badline/plus4/cpu.rb +10 -0
  297. data/lib/badline/plus4/cpu_port.rb +93 -0
  298. data/lib/badline/plus4/keyboard_port.rb +51 -0
  299. data/lib/badline/plus4/model.rb +38 -0
  300. data/lib/badline/plus4/user_port.rb +31 -0
  301. data/lib/badline/plus4.rb +164 -0
  302. data/lib/badline/roms/README +44 -6
  303. data/lib/badline/roms/plus4/basic.rom +0 -0
  304. data/lib/badline/roms/plus4/function-high.rom +0 -0
  305. data/lib/badline/roms/plus4/function-low.rom +0 -0
  306. data/lib/badline/roms/plus4/kernal-ntsc.rom +0 -0
  307. data/lib/badline/roms/plus4/kernal-pal.rom +0 -0
  308. data/lib/badline/sdl.rb +1 -0
  309. data/lib/badline/{storage/sid_file.rb → sid_tune/file.rb} +3 -3
  310. data/lib/badline/{storage → sid_tune}/hvsc.rb +7 -7
  311. data/lib/badline/{storage → sid_tune}/song_lengths.rb +2 -2
  312. data/lib/badline/{storage → sid_tune}/stil/credit.rb +1 -1
  313. data/lib/badline/{storage → sid_tune}/stil.rb +3 -3
  314. data/lib/badline/sid_tune.rb +6 -0
  315. data/lib/badline/snapshot/c128_setup.rb +32 -0
  316. data/lib/badline/snapshot/c64_setup.rb +83 -0
  317. data/lib/badline/snapshot/container.rb +4 -0
  318. data/lib/badline/snapshot/field_doc/markdown.rb +112 -0
  319. data/lib/badline/snapshot/field_doc.rb +109 -0
  320. data/lib/badline/snapshot/image.rb +132 -46
  321. data/lib/badline/snapshot/keyed/cursor.rb +40 -0
  322. data/lib/badline/snapshot/keyed/modules.rb +66 -0
  323. data/lib/badline/snapshot/keyed/reader.rb +188 -0
  324. data/lib/badline/snapshot/keyed/recorder.rb +88 -0
  325. data/lib/badline/snapshot/keyed/report.rb +26 -0
  326. data/lib/badline/snapshot/keyed/writer.rb +55 -0
  327. data/lib/badline/snapshot/keyed.rb +64 -0
  328. data/lib/badline/snapshot/machine_state.rb +17 -64
  329. data/lib/badline/snapshot/storage_setup.rb +63 -0
  330. data/lib/badline/snapshot/vic20_setup.rb +28 -0
  331. data/lib/badline/snapshot/vice/c64_mem.rb +1 -1
  332. data/lib/badline/snapshot/vice/restore.rb +1 -1
  333. data/lib/badline/snapshot/vice/reu1764.rb +3 -3
  334. data/lib/badline/snapshot/vice/reu_fields.rb +40 -0
  335. data/lib/badline/snapshot/vice.rb +13 -35
  336. data/lib/badline/snapshot.rb +44 -22
  337. data/lib/badline/storage/crt_file.rb +3 -1
  338. data/lib/badline/storage/d64_image.rb +0 -2
  339. data/lib/badline/storage/d71_image.rb +0 -2
  340. data/lib/badline/storage/d81_image.rb +0 -2
  341. data/lib/badline/storage/disk_image.rb +3 -6
  342. data/lib/badline/storage/g64_image.rb +10 -5
  343. data/lib/badline/storage/host_directory.rb +0 -5
  344. data/lib/badline/storage/t64.rb +2 -8
  345. data/lib/badline/storage/tap.rb +43 -15
  346. data/lib/badline/storage/unavailable.rb +1 -1
  347. data/lib/badline/storage.rb +0 -37
  348. data/lib/badline/support/c128_checkpoint.rb +39 -0
  349. data/lib/badline/support/checkpoint.rb +108 -0
  350. data/lib/badline/support/vic20_checkpoint.rb +38 -0
  351. data/lib/badline/support.rb +5 -0
  352. data/lib/badline/version.rb +1 -1
  353. data/lib/badline/vic20/attachments.rb +8 -5
  354. data/lib/badline/vic20/bus/keyed_state.rb +52 -0
  355. data/lib/badline/vic20/bus.rb +2 -25
  356. data/lib/badline/vic20/cpu.rb +1 -1
  357. data/lib/badline/vic20/keyed_modules.rb +73 -0
  358. data/lib/badline/vic20/keyed_state.rb +45 -0
  359. data/lib/badline/vic20/port_wiring.rb +4 -4
  360. data/lib/badline/vic20/user_via_ports.rb +2 -2
  361. data/lib/badline/vic20.rb +22 -17
  362. data/lib/badline.rb +1 -0
  363. metadata +271 -228
  364. data/lib/badline/address_bus/extra_sids.rb +0 -30
  365. data/lib/badline/address_bus/fittings.rb +0 -38
  366. data/lib/badline/address_bus/pla.rb +0 -147
  367. data/lib/badline/address_bus/roms.rb +0 -39
  368. data/lib/badline/address_bus/saved_state.rb +0 -66
  369. data/lib/badline/address_bus/sid_slots.rb +0 -42
  370. data/lib/badline/address_bus.rb +0 -216
  371. data/lib/badline/c128/bus_state.rb +0 -53
  372. data/lib/badline/c128/saved_state.rb +0 -143
  373. data/lib/badline/cartridge/action_replay.rb +0 -126
  374. data/lib/badline/cartridge/atomic_power.rb +0 -24
  375. data/lib/badline/cartridge/bank.rb +0 -81
  376. data/lib/badline/cartridge/comal80.rb +0 -50
  377. data/lib/badline/cartridge/dinamic.rb +0 -34
  378. data/lib/badline/cartridge/easy_flash.rb +0 -145
  379. data/lib/badline/cartridge/epyx_fastload.rb +0 -79
  380. data/lib/badline/cartridge/final_cartridge3.rb +0 -89
  381. data/lib/badline/cartridge/flash.rb +0 -306
  382. data/lib/badline/cartridge/freezer.rb +0 -33
  383. data/lib/badline/cartridge/fun_play.rb +0 -38
  384. data/lib/badline/cartridge/g_mod2.rb +0 -74
  385. data/lib/badline/cartridge/game_system.rb +0 -40
  386. data/lib/badline/cartridge/geo_ram.rb +0 -79
  387. data/lib/badline/cartridge/kcs_power.rb +0 -75
  388. data/lib/badline/cartridge/mach5.rb +0 -32
  389. data/lib/badline/cartridge/magic_desk.rb +0 -30
  390. data/lib/badline/cartridge/ocean.rb +0 -35
  391. data/lib/badline/cartridge/pagefox.rb +0 -68
  392. data/lib/badline/cartridge/retro_replay.rb +0 -320
  393. data/lib/badline/cartridge/rex_utility.rb +0 -29
  394. data/lib/badline/cartridge/rgcd.rb +0 -58
  395. data/lib/badline/cartridge/simons_basic.rb +0 -35
  396. data/lib/badline/cartridge/standard.rb +0 -15
  397. data/lib/badline/cartridge/super_games.rb +0 -48
  398. data/lib/badline/cartridge/westermann.rb +0 -30
  399. data/lib/badline/cartridge/zaxxon.rb +0 -66
  400. data/lib/badline/cartridge.rb +0 -303
  401. data/lib/badline/checkpoint.rb +0 -98
  402. data/lib/badline/chrout_trap.rb +0 -43
  403. data/lib/badline/cia/fast_forward.rb +0 -60
  404. data/lib/badline/cia/interrupt_register.rb +0 -143
  405. data/lib/badline/cia/serial.rb +0 -275
  406. data/lib/badline/cia/state.rb +0 -47
  407. data/lib/badline/cia/timer.rb +0 -206
  408. data/lib/badline/cia.rb +0 -279
  409. data/lib/badline/color_memory.rb +0 -25
  410. data/lib/badline/computer/attachments.rb +0 -88
  411. data/lib/badline/computer/saved_state.rb +0 -117
  412. data/lib/badline/computer/true_drives.rb +0 -69
  413. data/lib/badline/control_ports.rb +0 -76
  414. data/lib/badline/cpu/addressing.rb +0 -131
  415. data/lib/badline/cpu/boundary.rb +0 -54
  416. data/lib/badline/cpu/core.rb +0 -165
  417. data/lib/badline/cpu/microcode.rb +0 -177
  418. data/lib/badline/cpu/operations.rb +0 -88
  419. data/lib/badline/cpu/saved_state.rb +0 -115
  420. data/lib/badline/cpu/stack_operations.rb +0 -82
  421. data/lib/badline/cpu.rb +0 -10
  422. data/lib/badline/cpu_port.rb +0 -112
  423. data/lib/badline/datasette.rb +0 -161
  424. data/lib/badline/debug_register.rb +0 -28
  425. data/lib/badline/drive/core.rb +0 -147
  426. data/lib/badline/drive/fast_serial.rb +0 -73
  427. data/lib/badline/drive/idle.rb +0 -125
  428. data/lib/badline/drive/idle_cpu.rb +0 -28
  429. data/lib/badline/drive/orbit.rb +0 -79
  430. data/lib/badline/drive/serial_lines.rb +0 -73
  431. data/lib/badline/drive/sleep.rb +0 -112
  432. data/lib/badline/drive/vias.rb +0 -84
  433. data/lib/badline/drive/watch.rb +0 -150
  434. data/lib/badline/drive.rb +0 -11
  435. data/lib/badline/drive1541/bus.rb +0 -86
  436. data/lib/badline/drive1541/byte_ready.rb +0 -48
  437. data/lib/badline/drive1541/cpu.rb +0 -11
  438. data/lib/badline/drive1541/disk.rb +0 -315
  439. data/lib/badline/drive1541/disk_state.rb +0 -86
  440. data/lib/badline/drive1541/disk_via.rb +0 -36
  441. data/lib/badline/drive1541/gcr.rb +0 -57
  442. data/lib/badline/drive1541/mechanism.rb +0 -313
  443. data/lib/badline/drive1541/mechanism_state.rb +0 -68
  444. data/lib/badline/drive1541/rotation.rb +0 -121
  445. data/lib/badline/drive1541/sector_reader.rb +0 -76
  446. data/lib/badline/drive1541/serial_port.rb +0 -18
  447. data/lib/badline/drive1541/track.rb +0 -98
  448. data/lib/badline/drive1541.rb +0 -111
  449. data/lib/badline/drive1571/bus.rb +0 -111
  450. data/lib/badline/drive1571/cpu.rb +0 -11
  451. data/lib/badline/drive1571/serial_port.rb +0 -37
  452. data/lib/badline/drive1571/serial_via.rb +0 -25
  453. data/lib/badline/drive1571/wd1770.rb +0 -120
  454. data/lib/badline/drive1571.rb +0 -175
  455. data/lib/badline/drive1581/bus.rb +0 -137
  456. data/lib/badline/drive1581/cpu.rb +0 -11
  457. data/lib/badline/drive1581/disk.rb +0 -135
  458. data/lib/badline/drive1581/disk_state.rb +0 -87
  459. data/lib/badline/drive1581/mechanism.rb +0 -117
  460. data/lib/badline/drive1581/serial_port.rb +0 -52
  461. data/lib/badline/drive1581/slot.rb +0 -78
  462. data/lib/badline/drive1581/track.rb +0 -100
  463. data/lib/badline/drive1581/track_bytes.rb +0 -75
  464. data/lib/badline/drive1581/wd1772/registers.rb +0 -89
  465. data/lib/badline/drive1581/wd1772/sector_writes.rb +0 -73
  466. data/lib/badline/drive1581/wd1772/sectors.rb +0 -150
  467. data/lib/badline/drive1581/wd1772/state.rb +0 -66
  468. data/lib/badline/drive1581/wd1772/stepping.rb +0 -109
  469. data/lib/badline/drive1581/wd1772/tracks.rb +0 -125
  470. data/lib/badline/drive1581/wd1772/turn.rb +0 -73
  471. data/lib/badline/drive1581/wd1772.rb +0 -216
  472. data/lib/badline/drive1581.rb +0 -233
  473. data/lib/badline/iec_bus.rb +0 -159
  474. data/lib/badline/instruction.rb +0 -334
  475. data/lib/badline/instruction_set/arithmetic.rb +0 -114
  476. data/lib/badline/instruction_set/bitwise.rb +0 -126
  477. data/lib/badline/instruction_set/branch.rb +0 -79
  478. data/lib/badline/instruction_set/flag.rb +0 -63
  479. data/lib/badline/instruction_set/illegal.rb +0 -270
  480. data/lib/badline/instruction_set/inc_dec.rb +0 -69
  481. data/lib/badline/instruction_set/stack.rb +0 -52
  482. data/lib/badline/instruction_set/transfer.rb +0 -137
  483. data/lib/badline/instruction_set.rb +0 -55
  484. data/lib/badline/interrupts.rb +0 -104
  485. data/lib/badline/joystick.rb +0 -25
  486. data/lib/badline/kernal_trap/channel.rb +0 -181
  487. data/lib/badline/keyboard.rb +0 -112
  488. data/lib/badline/keyboard_buffer.rb +0 -33
  489. data/lib/badline/ram_expansion/banking.rb +0 -30
  490. data/lib/badline/ram_expansion/plus256k.rb +0 -54
  491. data/lib/badline/ram_expansion/plus60k.rb +0 -43
  492. data/lib/badline/ram_expansion/unexpanded.rb +0 -28
  493. data/lib/badline/ram_expansion.rb +0 -21
  494. data/lib/badline/reu/dma/saved_state.rb +0 -50
  495. data/lib/badline/reu/dma.rb +0 -273
  496. data/lib/badline/reu/ram.rb +0 -107
  497. data/lib/badline/reu/saved_state.rb +0 -80
  498. data/lib/badline/reu/trigger.rb +0 -32
  499. data/lib/badline/reu.rb +0 -289
  500. data/lib/badline/sid/catch_up.rb +0 -120
  501. data/lib/badline/sid/decimator.rb +0 -64
  502. data/lib/badline/sid/envelope/state.rb +0 -68
  503. data/lib/badline/sid/envelope.rb +0 -239
  504. data/lib/badline/sid/filter.rb +0 -263
  505. data/lib/badline/sid/voice.rb +0 -77
  506. data/lib/badline/sid/waveform/combined.rb +0 -126
  507. data/lib/badline/sid/waveform/fast_forward.rb +0 -105
  508. data/lib/badline/sid/waveform/noise_writeback.rb +0 -122
  509. data/lib/badline/sid/waveform/state.rb +0 -66
  510. data/lib/badline/sid/waveform.rb +0 -305
  511. data/lib/badline/sid.rb +0 -303
  512. data/lib/badline/snapshot/setup.rb +0 -75
  513. data/lib/badline/snapshot/state.rb +0 -189
  514. data/lib/badline/time_of_day.rb +0 -228
  515. data/lib/badline/traps.rb +0 -19
  516. data/lib/badline/via/control_lines.rb +0 -122
  517. data/lib/badline/via/fast_forward.rb +0 -53
  518. data/lib/badline/via/interrupt_register.rb +0 -67
  519. data/lib/badline/via/shift_register.rb +0 -180
  520. data/lib/badline/via/state.rb +0 -42
  521. data/lib/badline/via/timer1.rb +0 -131
  522. data/lib/badline/via/timer2.rb +0 -133
  523. data/lib/badline/via.rb +0 -266
  524. data/lib/badline/vic/bank.rb +0 -133
  525. data/lib/badline/vic/border_mask.rb +0 -86
  526. data/lib/badline/vic/collisions.rb +0 -115
  527. data/lib/badline/vic/color_patches.rb +0 -47
  528. data/lib/badline/vic/display_state.rb +0 -185
  529. data/lib/badline/vic/graphics_mode.rb +0 -164
  530. data/lib/badline/vic/graphics_shifter.rb +0 -207
  531. data/lib/badline/vic/register_log.rb +0 -81
  532. data/lib/badline/vic/registers.rb +0 -195
  533. data/lib/badline/vic/sequencer.rb +0 -275
  534. data/lib/badline/vic/sequencer_output.rb +0 -116
  535. data/lib/badline/vic/sequencer_state.rb +0 -89
  536. data/lib/badline/vic/sprite/internal_bus.rb +0 -68
  537. data/lib/badline/vic/sprite/saved_state.rb +0 -64
  538. data/lib/badline/vic/sprite/shifter.rb +0 -146
  539. data/lib/badline/vic/sprite/timing.rb +0 -56
  540. data/lib/badline/vic/sprite.rb +0 -337
  541. data/lib/badline/vic/sprites.rb +0 -282
  542. data/lib/badline/vic/sprites_state.rb +0 -57
  543. data/lib/badline/vic.rb +0 -853
  544. data/lib/badline/vic20/saved_state.rb +0 -124
  545. data/lib/badline/vic20/sound/output.rb +0 -83
  546. data/lib/badline/vic20/sound.rb +0 -233
  547. data/lib/badline/vic20/vic/saved_state.rb +0 -55
  548. data/lib/badline/z80/arithmetic.rb +0 -117
  549. data/lib/badline/z80/bit_page.rb +0 -55
  550. data/lib/badline/z80/block_io.rb +0 -58
  551. data/lib/badline/z80/block_transfers.rb +0 -59
  552. data/lib/badline/z80/bus_cycles.rb +0 -111
  553. data/lib/badline/z80/core.rb +0 -113
  554. data/lib/badline/z80/exchanges.rb +0 -50
  555. data/lib/badline/z80/extended_page.rb +0 -89
  556. data/lib/badline/z80/flags.rb +0 -37
  557. data/lib/badline/z80/interrupt_acceptance.rb +0 -46
  558. data/lib/badline/z80/jumps.rb +0 -82
  559. data/lib/badline/z80/loads.rb +0 -98
  560. data/lib/badline/z80/main_page.rb +0 -134
  561. data/lib/badline/z80/registers.rb +0 -120
  562. data/lib/badline/z80/saved_state.rb +0 -69
  563. data/lib/badline/z80/shifts.rb +0 -64
  564. data/lib/badline/z80.rb +0 -11
  565. /data/lib/badline/snapshot/{fields.rb → vice/fields.rb} +0 -0
@@ -0,0 +1,124 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ module Chips
5
+ class SID
6
+ class Waveform
7
+ # Noise combined with other waveforms: the output lines write back into
8
+ # the LFSR, and what they write is not always what OSC3 reads.
9
+ module NoiseWriteback
10
+ # The LFSR bits the noise shaper reads, paired with the output bit each
11
+ # one drives (Dag Lem's diagram in SID/noise-reset_new). A combined
12
+ # waveform drives the same lines, so a bit held low there is written
13
+ # back into the register.
14
+ NOISE_TAPS = [[0x100000, 0x800], [0x040000, 0x400], [0x004000, 0x200],
15
+ [0x000800, 0x100], [0x000200, 0x080], [0x000020, 0x040],
16
+ [0x000004, 0x020], [0x000001, 0x010]].freeze
17
+
18
+ # Pulse+noise with the pulse high: the noise lines next to the four
19
+ # below them, which nothing drives, are pulled low. The OSC3 read and
20
+ # the LFSR writeback see the same lines against different thresholds,
21
+ # so they lose a different number of bits. The 8580's pair is the one
22
+ # that reproduces SID/wf12nsr's pulse+noise row (it reads $f8, and the
23
+ # writeback leaves $fc behind), and SID/wb_testsuite's C->9 and C->E
24
+ # rows need that same $fc written at release. The 6581 reads $00 of a
25
+ # full register (SID/wf12nsr's pulse+noise row) and pulls nothing
26
+ # while the register holds: wb_testsuite's 8->C and C->C rows run
27
+ # pulse+noise and leave the register alone. Its lines only land
28
+ # part way through a shift (#write_back_shift).
29
+ PULSE_NOISE_READ = { mos6581: 0x000, mos8580: 0xf80 }.freeze
30
+ PULSE_NOISE_WRITE = { mos6581: 0xff0, mos8580: 0xfc0 }.freeze
31
+
32
+ # What a 6581 writes back when the test bit falls with only pulse+noise
33
+ # common to the old waveform and the new one (SID/wb_testsuite's D->C,
34
+ # E->C, F->C and D->E rows).
35
+ PULSE_NOISE_RELEASE = 0xf80
36
+
37
+ private
38
+
39
+ # What the lines write into the LFSR, against a lower threshold than
40
+ # OSC3 reads them by: a line left high between two low ones reads high
41
+ # (SID/noisewriteback's noise_writeback_test2) but is written low
42
+ # (SID/wf12nsr's noise+triangle and noise+sawtooth rows).
43
+ def write_back(selected, value)
44
+ return value & ((value << 1) | (value >> 1)) unless selected == 0xc
45
+ return 0x000 if @pulse.zero?
46
+
47
+ noise & @pulse_noise_write
48
+ end
49
+
50
+ # What the lines write while the register is part way through a
51
+ # shift, where the 6581's pulse+noise writes its output as read.
52
+ def write_back_mid_shift(selected, value)
53
+ return value if pulse_noise_mid_shift?(selected)
54
+
55
+ write_back(selected, value)
56
+ end
57
+
58
+ def pulse_noise_mid_shift?(selected) = @topbit_feedback && selected == 0xc
59
+
60
+ def write_shift_register(value)
61
+ NOISE_TAPS.each { |bit, line| @shift_register &= ~bit if value.nobits?(line) }
62
+ end
63
+
64
+ # The second phase of a shift writes back by the test bit release
65
+ # rule with the waveform left alone. The 6581's pulse+noise, which
66
+ # that rule leaves out, writes its lines too (SID/wf12nsr's
67
+ # pulse+noise row).
68
+ def write_back_shift
69
+ return unless pulse_noise_mid_shift?(@selected) || release_writes_back?(@selected, @selected)
70
+
71
+ write_shift_register(write_back_mid_shift(@selected, @output))
72
+ end
73
+
74
+ # Setting the test bit starts the bleed, and stalls the register part
75
+ # way through a shift, where a noise combination writes its output
76
+ # back first, from the accumulator it had. SID/noiselfsrinit's
77
+ # $f8/$80 pairs set the bit onto all four from noise alone, and
78
+ # SID/wf12nsr's pulse+noise row sets it onto pulse+noise.
79
+ def raise_test
80
+ @shift_register_reset = @shift_register_reset_delay
81
+ return unless @selected.anybits?(0x8) && combined?(@selected) && @shift_pipeline != 1
82
+
83
+ write_shift_register(write_back_mid_shift(@selected, shape(@selected)))
84
+ end
85
+
86
+ # Releasing the test bit finishes the shift it interrupted: the old
87
+ # waveform's output may be written back first, then a bit clocks in
88
+ # over the forced-high bit 22.
89
+ def release_test(previous)
90
+ value = release_write(previous)
91
+ write_shift_register(value) if value
92
+ shift_noise(1)
93
+ end
94
+
95
+ # What the release writes over the taps, or nil. On the 6581, a change
96
+ # that keeps only pulse+noise of the old waveform writes the lowest
97
+ # noise lines low. On the 8580, a change from pulse+noise to all four
98
+ # writes every line low.
99
+ def release_write(previous)
100
+ if @topbit_feedback
101
+ return noise & PULSE_NOISE_RELEASE if previous != 0xc && (previous & @selected) == 0xc
102
+ elsif previous == 0xc && @selected == 0xf
103
+ return 0x000
104
+ end
105
+ write_back(previous, shape(previous)) if release_writes_back?(previous, @selected)
106
+ end
107
+
108
+ # Which waveform changes write the old output back as the test bit
109
+ # falls (SID/wb_testsuite, after libresidfp's do_writeback). Noise has
110
+ # to have been combined before and still be combined after. Nothing
111
+ # is written back on changing to pulse+noise or from pulse+noise to
112
+ # sawtooth+noise, nor, on the 6581, on trading triangle for sawtooth
113
+ # or back.
114
+ def release_writes_back?(previous, selected)
115
+ return false if previous <= 0x8 || selected <= 0x8
116
+ return false if selected == 0xc || (previous == 0xc && selected == 0xa)
117
+
118
+ !(@topbit_feedback && [previous & 0x3, selected & 0x3].sort == [0x1, 0x2])
119
+ end
120
+ end
121
+ end
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,305 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "badline/chips/sid/waveform/combined"
4
+ require "badline/chips/sid/waveform/fast_forward"
5
+ require "badline/chips/sid/waveform/noise_writeback"
6
+
7
+ module Badline
8
+ module Chips
9
+ class SID
10
+ # Waveform generator: a 24-bit phase accumulator and the shapers that
11
+ # read it.
12
+ #
13
+ # The accumulator advances by the 16-bit frequency every cycle, so every
14
+ # waveform is a pure function of the phase. Bit 23 drives hard sync and
15
+ # the triangle fold, bit 19 clocks the noise LFSR.
16
+ #
17
+ # Selecting more than one waveform shorts the shapers onto the lines the
18
+ # oscillator itself reads back, so a combined waveform is not only a
19
+ # shape: a low top bit reaches the accumulator MSB through the sawtooth
20
+ # switch, and noise in the mix is written back into the LFSR. The shape
21
+ # itself comes from Combined's model of the lines pulling on each other.
22
+ #
23
+ # The pulse comparator's output reaches the lines a cycle after the
24
+ # accumulator it compared.
25
+ #
26
+ # With nothing selected the DAC input floats, holding the last value
27
+ # driven onto it until the charge drains away.
28
+ class Waveform
29
+ include FastForward
30
+ include NoiseWriteback
31
+
32
+ # All bits high at power up, odd ones stored inverted (SID/oscinit).
33
+ POWER_ON_ACCUMULATOR = 0x555555
34
+
35
+ # Holding the test bit bleeds every LFSR bit high; the power-on state is
36
+ # one shift further on, which is what releasing the bit leaves behind.
37
+ SHIFT_REGISTER_RESET = 0x7fffff
38
+ NOISE_SEED = 0x7ffffe
39
+
40
+ # Cycles the test bit needs to bleed the LFSR high. SID/bitfade's
41
+ # delaynoise measures ~$950000 on a real 8580. Nothing measures the
42
+ # 6581: SID/resid-test's oscsample dumps show it holding its bits
43
+ # through the ~$8800 cycles between runs, and the slow reset
44
+ # (SID/noise-reset_old) allows it a second.
45
+ SHIFT_REGISTER_RESET_DELAY = { mos6581: 0x80000, mos8580: 0x950000 }.freeze
46
+
47
+ # Cycles the floating DAC input holds its charge (SID/osc3-wave0).
48
+ FLOATING_OUTPUT_TTL = 0x4000
49
+
50
+ MSB = 0x800000
51
+
52
+ attr_accessor :sync_source, :sync_dest
53
+ attr_reader :accumulator, :shift_register, :frequency, :pulse_width, :pulse
54
+
55
+ def initialize(model: :mos6581)
56
+ use_model(model)
57
+ @tri_saw_delay = model == :mos8580
58
+ @accumulator = POWER_ON_ACCUMULATOR
59
+ @sync_source = @sync_dest = self
60
+ reset!
61
+ end
62
+
63
+ # The running state carries over. A 6581 never delays its shapers, so
64
+ # the 8580's delayed triangle and sawtooth start from the current phase.
65
+ def model=(model)
66
+ use_model(model)
67
+ if model == :mos8580 && !@tri_saw_delay
68
+ @delayed_sawtooth = sawtooth
69
+ @delayed_triangle = triangle
70
+ end
71
+ @tri_saw_delay = model == :mos8580
72
+ end
73
+
74
+ # The RES line clears the registers and reseeds the LFSR, but leaves
75
+ # the accumulator alone (SID/oscinit).
76
+ def reset!
77
+ @delayed_sawtooth = @delayed_triangle = 0x000
78
+ @shift_register = NOISE_SEED
79
+ @shift_register_reset = 0
80
+ @shift_pipeline = 0
81
+ @frequency = 0x0000
82
+ @pulse_width = 0x000
83
+ @selected = 0x0
84
+ @test = false
85
+ @ring = false
86
+ @sync = false
87
+ @msb_rising = false
88
+ @floating = 0x000
89
+ @floating_ttl = 0
90
+ @pulse = 0xfff
91
+ @output = 0x000
92
+ @stale = true
93
+ end
94
+
95
+ def sync? = @sync
96
+ def msb_rising? = @msb_rising
97
+
98
+ def frequency_low=(value)
99
+ @frequency = (@frequency & 0xff00) | value
100
+ end
101
+
102
+ def frequency_high=(value)
103
+ @frequency = (value << 8) | (@frequency & 0x00ff)
104
+ end
105
+
106
+ def pulse_width_low=(value)
107
+ @pulse_width = (@pulse_width & 0xf00) | value
108
+ @stale = true
109
+ end
110
+
111
+ def pulse_width_high=(value)
112
+ @pulse_width = ((value & 0x0f) << 8) | (@pulse_width & 0x0ff)
113
+ @stale = true
114
+ end
115
+
116
+ # The test bit clears the accumulator and stalls the LFSR half way
117
+ # through a shift, holding bit 22 high while its bits bleed up.
118
+ def control=(value)
119
+ previous = @selected
120
+ @selected = (value >> 4) & 0x0f
121
+ @ring = value.anybits?(0x04)
122
+ @sync = value.anybits?(0x02)
123
+ test = value.anybits?(0x08)
124
+ if test
125
+ raise_test unless @test
126
+ @accumulator = 0x000000
127
+ @pulse = 0xfff
128
+ @shift_pipeline = 0
129
+ elsif @test
130
+ release_test(previous)
131
+ end
132
+ @test = test
133
+ @stale = true
134
+ end
135
+
136
+ def cycle!
137
+ latch_output
138
+ compare_pulse
139
+ @test ? bleed_shift_register : advance
140
+ @stale = true
141
+ end
142
+
143
+ # Hard sync resets the next voice's accumulator when ours wraps. A
144
+ # source that is itself synced on the cycle its own MSB rises does not
145
+ # pass the sync on.
146
+ def synchronize!
147
+ return unless @msb_rising && @sync_dest.sync? &&
148
+ !(@sync && @sync_source.msb_rising?)
149
+
150
+ @sync_dest.reset_accumulator
151
+ end
152
+
153
+ def reset_accumulator
154
+ @accumulator = 0x000000
155
+ @stale = true
156
+ end
157
+
158
+ # Held for the rest of the cycle once computed, so the filter, OSC3 and
159
+ # the oscillator's own feedback all see one value per cycle.
160
+ def output
161
+ return @output unless @stale
162
+
163
+ @stale = false
164
+ @output = @selected.zero? ? @floating : shape(@selected)
165
+ end
166
+
167
+ # What OSC3 reads. The 8580 delays the triangle and sawtooth shapers
168
+ # by half a cycle, which OSC3 latches as a whole cycle late; pulse and
169
+ # noise still reach the lines on time.
170
+ def osc3
171
+ return output unless @tri_saw_delay && @selected.anybits?(0x3)
172
+
173
+ shape(@selected, @delayed_sawtooth, @delayed_triangle)
174
+ end
175
+
176
+ def sawtooth = @accumulator >> 12
177
+
178
+ # The upper half of the ramp is folded back down, shifted left one bit
179
+ # to keep the full 12-bit range.
180
+ def triangle(selected = @selected)
181
+ folded = inverted?(selected) ? @accumulator ^ 0xffffff : @accumulator
182
+ (folded >> 11) & 0xfff
183
+ end
184
+
185
+ # Eight taps off the 23-bit LFSR, gathered into a 12-bit sample.
186
+ def noise
187
+ ((@shift_register & 0x100000) >> 9) |
188
+ ((@shift_register & 0x040000) >> 8) |
189
+ ((@shift_register & 0x004000) >> 5) |
190
+ ((@shift_register & 0x000800) >> 3) |
191
+ ((@shift_register & 0x000200) >> 2) |
192
+ ((@shift_register & 0x000020) << 1) |
193
+ ((@shift_register & 0x000004) << 3) |
194
+ ((@shift_register & 0x000001) << 4)
195
+ end
196
+
197
+ private
198
+
199
+ def use_model(model)
200
+ @topbit_feedback = model != :mos8580
201
+ @pulse_noise_read = PULSE_NOISE_READ.fetch(model)
202
+ @pulse_noise_write = PULSE_NOISE_WRITE.fetch(model)
203
+ @combined = Combined.tables(model)
204
+ @shift_register_reset_delay = SHIFT_REGISTER_RESET_DELAY.fetch(model)
205
+ end
206
+
207
+ def shape(selected, saw = sawtooth, tri = triangle(selected))
208
+ case selected
209
+ when 0x1 then tri
210
+ when 0x2 then saw
211
+ when 0x4 then @pulse
212
+ when 0x8 then noise
213
+ else combined(selected, saw, tri)
214
+ end
215
+ end
216
+
217
+ # A low pulse grounds every line. Noise with pulse alone loses its
218
+ # lowest lines; noise with triangle or sawtooth is ANDed over the rest
219
+ # of the mix.
220
+ def combined(selected, saw, tri)
221
+ return 0x000 if selected.anybits?(0x4) && @pulse.zero?
222
+ return noise & @pulse_noise_read if selected == 0xc
223
+
224
+ rest = selected & 0x7
225
+ value = case rest
226
+ when 0x1 then tri
227
+ when 0x2 then saw
228
+ when 0x4 then 0xfff
229
+ else @combined[rest][rest == 0x5 ? tri : saw]
230
+ end
231
+ selected.anybits?(0x8) ? value & noise : value
232
+ end
233
+
234
+ def compare_pulse(phase = @accumulator >> 12) = (@pulse = @test || phase >= @pulse_width ? 0xfff : 0x000)
235
+
236
+ # More than one bit set in the selection.
237
+ def combined?(selected) = selected & (selected - 1) != 0
238
+
239
+ # The one point in the cycle where the waveform selector drives the DAC:
240
+ # either it latches a fresh value, or nothing drives the line and the
241
+ # charge already on it drains a little further.
242
+ def latch_output
243
+ value = output
244
+ if @tri_saw_delay
245
+ @delayed_sawtooth = sawtooth
246
+ @delayed_triangle = triangle
247
+ end
248
+ if @selected.zero?
249
+ drain_floating(1)
250
+ return
251
+ end
252
+
253
+ @floating = value
254
+ @floating_ttl = FLOATING_OUTPUT_TTL
255
+ feed_back(value) if combined?(@selected) && !@test
256
+ end
257
+
258
+ # A zero on the shared output lines travels back into the oscillator:
259
+ # through the sawtooth switch it clears the accumulator MSB on the next
260
+ # cycle, and with noise selected it lands in the LFSR, where a bit
261
+ # pulled low can never come back. The 8580 buffers the top bit behind a
262
+ # flip-flop before the sawtooth switch, so only the LFSR sees it. In the
263
+ # first phase of a shift the register bits float, so nothing is pulled
264
+ # down: the output is latched instead, for the second phase to write.
265
+ def feed_back(value)
266
+ @accumulator &= ~MSB if @topbit_feedback && @selected.anybits?(0x2) && value.nobits?(0x800)
267
+ write_shift_register(write_back(@selected, value)) if @selected.anybits?(0x8) && @shift_pipeline != 1
268
+ end
269
+
270
+ # A rise of bit 19 starts a shift that takes two more cycles
271
+ # (SID/noisewriteback's noise_writeback_test2, after libresidfp's shift
272
+ # pipeline). The first phase latches the output, and the second writes
273
+ # it over the taps before the register shifts.
274
+ def advance
275
+ previous = @accumulator
276
+ @accumulator = (previous + @frequency) & 0xffffff
277
+ @msb_rising = @accumulator & ~previous & MSB != 0
278
+ if @accumulator & ~previous & 0x080000 != 0
279
+ @shift_pipeline = 2
280
+ elsif @shift_pipeline != 0 && (@shift_pipeline -= 1).zero?
281
+ write_back_shift
282
+ shift_noise
283
+ end
284
+ end
285
+
286
+ # Bit 0 feeds back bits 22 and 17, with the test bit forcing bit 22 high.
287
+ def shift_noise(forced = 0)
288
+ bit0 = ((@shift_register >> 22) | forced) ^ (@shift_register >> 17)
289
+ @shift_register = ((@shift_register << 1) & 0x7fffff) | (bit0 & 0x1)
290
+ end
291
+
292
+ # TriXOR = !Saw & ((!V3 & Ring) ^ bit23), where V3 is the modulating
293
+ # voice's MSB (SID/ringmod).
294
+ def inverted?(selected)
295
+ return false if selected & 0x2 != 0
296
+
297
+ msb = @accumulator & MSB != 0
298
+ return msb unless @ring
299
+
300
+ msb == (@sync_source.accumulator & MSB != 0)
301
+ end
302
+ end
303
+ end
304
+ end
305
+ end
@@ -0,0 +1,264 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "badline/chips/sid/waveform"
4
+ require "badline/chips/sid/envelope"
5
+ require "badline/chips/sid/voice"
6
+ require "badline/chips/sid/filter"
7
+ require "badline/chips/sid/decimator"
8
+ require "badline/chips/sid/catch_up"
9
+ require "badline/chips/sid/keyed_parts"
10
+
11
+ module Badline
12
+ module Chips
13
+ # SID (Sound Interface Device) chip, in either the 6581 or the 8580
14
+ # revision.
15
+ #
16
+ # $D400-$D418 - Voices, filter and volume - write only
17
+ # $D419-$D41A - POTX/POTY paddle inputs - read only
18
+ # $D41B-$D41C - Voice 3 oscillator/envelope - read only
19
+ # $D41D-$D41F - Unconnected
20
+ #
21
+ # The register file mirrors every 32 bytes up to $D7FF. A second or
22
+ # third SID, which only the SID player fits, is built `at` another
23
+ # address and decodes just the 32 bytes there.
24
+ #
25
+ # Reading a write-only or unconnected register returns the last byte the
26
+ # SID saw on the data bus. That value fades to $00 as the capacitance
27
+ # holding it drains. Paddles and the 1351 mouse attach as a pots source
28
+ # responding to #pot_x and #pot_y; with none attached the lines read $FF.
29
+ #
30
+ # Three voices feed the filter and the master volume; #output and #sample
31
+ # read out the mixed result, and #record collects it at an audio rate for
32
+ # #drain_samples. The DSP is clocked lazily and catches up when something
33
+ # asks for its state.
34
+ #
35
+ # The 6581 drives its voices well above ground, so the mix carries a large
36
+ # DC offset that the RC network on the C64 board filters out.
37
+ class SID
38
+ include Addressable
39
+ include CatchUp
40
+
41
+ VOICES = 3
42
+
43
+ # Cycles the data bus holds a value, measured through SID/bitfade.
44
+ BUS_TTL = { mos6581: 0x1d00, mos8580: 0xa2000 }.freeze
45
+
46
+ # Scales the filter's 20-bit mix down to a signed 16-bit sample, as
47
+ # reSID 0.16 does: three voices at full swing and volume 15 span the
48
+ # whole range, on either chip.
49
+ SAMPLE_DIVISOR = ((0xfff * 0xff) >> 7) * 3 * 15 * 2 / (2**16)
50
+
51
+ # Writes the DSP queues between catch-ups. A full queue catches up
52
+ # there and then, so no write loses its timing.
53
+ DEFERRED_WRITES = 0x400
54
+
55
+ # Cycles the filter integrates in one step while synthesizing, after
56
+ # reSID's delta_t_flt. 1 makes the audio exact, at a per-cycle cost.
57
+ FILTER_CHUNK = 4
58
+
59
+ attr_accessor :pots
60
+ attr_reader :model
61
+
62
+ def initialize(model: :mos6581, pots: nil, filter_chunk: FILTER_CHUNK, at: 0xd400)
63
+ addressable_at(at, length: at == 0xd400 ? 2**10 : 2**5)
64
+ @model = model
65
+ @pots = pots
66
+ @bus_ttl_reset = BUS_TTL.fetch(model)
67
+ @filter = Filter.new(model:)
68
+ @synthesizing = false
69
+ @stepped = @synced = false
70
+ @pending_cycles = 0
71
+ @deferred_writes = []
72
+ @decimator = nil
73
+ @samples = []
74
+ @voice_samples = nil
75
+ @filter_chunk = filter_chunk
76
+ @voices = Voice.linked(VOICES, model:)
77
+ @voice1, @voice2, @voice3 = @voices
78
+ @waveform1, @waveform2, @waveform3 = @voices.map(&:waveform)
79
+ reset!
80
+ end
81
+
82
+ # Switches the chip, its filter and its voices to the other model's
83
+ # values. The cycles already passed run on the old model first, and the
84
+ # running state carries over.
85
+ def model=(model)
86
+ catch_up
87
+ @model = model
88
+ @bus_ttl_reset = BUS_TTL.fetch(model)
89
+ @filter.model = model
90
+ @voices.each { |voice| voice.model = model }
91
+ update_span_rules
92
+ end
93
+
94
+ # The RES line clears the registers, the data bus, the filter and the
95
+ # voices, all but their accumulators. Recording carries on across it.
96
+ def reset!
97
+ catch_up
98
+ @registers = Memory.new(length: 2**5)
99
+ @bus_value = 0x00
100
+ @bus_ttl = 0
101
+ @voices.each(&:reset!)
102
+ @filter.reset
103
+ update_span_rules
104
+ end
105
+
106
+ # The DSP only counts cycles as they pass. Whatever asks for its state
107
+ # catches it up, a span at a time: each span fast-forwards the voices
108
+ # and ends on one whole cycle, so everything the CPU can read stays
109
+ # exact. The filter only runs once something wants audio.
110
+ def synthesizing? = @synthesizing
111
+
112
+ def synthesize!
113
+ return if @synthesizing
114
+
115
+ @synthesizing = true
116
+ update_span_rules
117
+ catch_up
118
+ end
119
+
120
+ # Starts collecting the output, averaged down to `rate` samples a
121
+ # second, for #drain_samples. `filter_chunk` overrides the one the SID
122
+ # was built with, for a machine that built its own. `clock_hz` is the
123
+ # rate the SID's cycles are taken to run at.
124
+ def record(rate:, filter_chunk: @filter_chunk, clock_hz: Region::PAL.clock_hz)
125
+ synthesize!
126
+ catch_up
127
+ @filter_chunk = filter_chunk
128
+ @decimator = Decimator.new(clock_hz:, rate:)
129
+ @samples = []
130
+ end
131
+
132
+ # Also keeps each voice's own output, before the filter and the volume,
133
+ # as each recorded sample closes, for #drain_voice_samples.
134
+ def record_voices!
135
+ @voice_samples = [] if @voice_samples.nil?
136
+ end
137
+
138
+ # The voices' outputs since the last drain, three to a sample, in voice
139
+ # order, or nothing unless #record_voices! asked for them.
140
+ def drain_voice_samples
141
+ return [] if @voice_samples.nil?
142
+
143
+ catch_up
144
+ samples = @voice_samples
145
+ @voice_samples = []
146
+ samples
147
+ end
148
+
149
+ # The samples recorded since the last drain.
150
+ def drain_samples
151
+ catch_up
152
+ samples = @samples
153
+ @samples = []
154
+ samples
155
+ end
156
+
157
+ def voices
158
+ catch_up
159
+ @voices
160
+ end
161
+
162
+ # Runs the DSP up to the current cycle.
163
+ def catch_up! = catch_up
164
+
165
+ # The byte the data bus holds and the cycles until it fades.
166
+ def bus_state = [@bus_value, @bus_ttl]
167
+
168
+ def restore_bus(value, ttl)
169
+ @bus_value = value
170
+ @bus_ttl = ttl
171
+ end
172
+
173
+ def filter
174
+ catch_up
175
+ @filter
176
+ end
177
+
178
+ # Voice 3's oscillator and envelope, the only synthesis state the CPU
179
+ # can see. Programs poll OSC3 with the noise waveform selected for
180
+ # random numbers, and ENV3 to time hard restarts.
181
+ def osc3
182
+ catch_up
183
+ @waveform3.osc3 >> 4
184
+ end
185
+
186
+ def env3
187
+ catch_up
188
+ @voice3.envelope.env3
189
+ end
190
+
191
+ def output
192
+ synthesize!
193
+ catch_up
194
+ @filter.output
195
+ end
196
+
197
+ def sample
198
+ output
199
+ current_sample
200
+ end
201
+
202
+ def cycle!
203
+ age_bus if @bus_ttl != 0
204
+ @pending_cycles += 1
205
+ end
206
+
207
+ def peek(addr)
208
+ case offset_of(addr) % (2**5)
209
+ when 0x19 then latch(pots ? pots.pot_x : 0xff)
210
+ when 0x1a then latch(pots ? pots.pot_y : 0xff)
211
+ when 0x1b then latch(osc3)
212
+ when 0x1c then latch(env3)
213
+ else @bus_value
214
+ end
215
+ end
216
+
217
+ def poke(addr, value)
218
+ reg = offset_of(addr) % (2**5)
219
+ write_register(reg, value) if reg <= 0x18
220
+ latch(value)
221
+ end
222
+
223
+ # Register contents as written, for save states and debugging.
224
+ def register(reg) = @registers.peek(reg)
225
+
226
+ private
227
+
228
+ def latch(value)
229
+ @bus_ttl = @bus_ttl_reset
230
+ @bus_value = value
231
+ end
232
+
233
+ def age_bus
234
+ @bus_ttl -= 1
235
+ @bus_value = 0x00 if @bus_ttl.zero?
236
+ end
237
+
238
+ def write_register(reg, value)
239
+ @registers.poke(reg, value)
240
+ catch_up if @deferred_writes.length == DEFERRED_WRITES * 3
241
+ @deferred_writes.push(@pending_cycles, reg, value)
242
+ end
243
+
244
+ def apply_write(reg, value)
245
+ reg < 0x15 ? write_voice(reg, value) : @filter.write(reg, value)
246
+ end
247
+
248
+ def write_voice(reg, value)
249
+ voice = @voices[reg / 7]
250
+ case reg % 7
251
+ when 0 then voice.waveform.frequency_low = value
252
+ when 1 then voice.waveform.frequency_high = value
253
+ when 2 then voice.waveform.pulse_width_low = value
254
+ when 3 then voice.waveform.pulse_width_high = value
255
+ when 4
256
+ voice.control = value
257
+ update_span_rules
258
+ when 5 then voice.envelope.attack_decay = value
259
+ when 6 then voice.envelope.sustain_release = value
260
+ end
261
+ end
262
+ end
263
+ end
264
+ end