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,142 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ module Chips
5
+ class VICI
6
+ class Sound
7
+ # The sound's fields in a keyed snapshot module, under `prefix`:
8
+ # the voices' shift registers, the noise LFSR, the level, and when
9
+ # each voice next shifts, in cycles from the machine's clock, with
10
+ # how far behind the clock the voices last ran. The volume and the
11
+ # voices' frequencies come from the VIC's registers on load.
12
+ module KeyedState
13
+ def save_fields(out, prefix)
14
+ now = @clock.cycles
15
+ run_to(now)
16
+ out.ints("#{prefix}shift_registers", @shift_registers).int("#{prefix}lfsr", @lfsr)
17
+ out.int("#{prefix}level", @level).int("#{prefix}ran_ago", now - @time)
18
+ out.ints("#{prefix}next_shifts", @next_shifts.map { |due| due - now })
19
+ end
20
+
21
+ def load_fields(input, prefix)
22
+ now = @clock.cycles
23
+ input.ints_into("#{prefix}shift_registers", @shift_registers)
24
+ @lfsr = input.masked("#{prefix}lfsr", @lfsr, 0xffff)
25
+ @level = input.int("#{prefix}level", @level)
26
+ @time = now - input.int("#{prefix}ran_ago", now - @time)
27
+ relative = input.ints("#{prefix}next_shifts", @next_shifts.map { |due| due - now })
28
+ VOICES.times { |voice| @next_shifts[voice] = now + relative.fetch(voice, 0) }
29
+ end
30
+
31
+ # $900A to $900E as written.
32
+ def restore_registers(registers)
33
+ @volume = registers[0x0e] & 0x0f
34
+ VOICES.times do |voice|
35
+ value = registers[0x0a + voice]
36
+ @frequencies[voice] = value & 0x7f
37
+ @enabled[voice] = value >= 0x80
38
+ end
39
+ end
40
+ end
41
+
42
+ include KeyedState
43
+ end
44
+
45
+ class Painter
46
+ # The lines' fetches and the line going out, as this painter lays
47
+ # them out (VICI::LINE_LAYOUT), and the picture.
48
+ module KeyedState
49
+ def save_fields(out)
50
+ out.int("line.row", @row).int("line.painted", @painted).bool("line.written", @written)
51
+ out.bytes("line.patterns", @patterns).bytes("line.colors", @colors)
52
+ out.ints("line.counts", @counts).ints("line.starts", @starts).bytes("display", @display)
53
+ end
54
+
55
+ def load_fields(input, layout)
56
+ input.bytes_into("display", @display)
57
+ return unless layout
58
+
59
+ @row = input.int("line.row", @row)
60
+ @painted = input.int("line.painted", @painted)
61
+ @written = input.bool("line.written", @written)
62
+ input.bytes_into("line.patterns", @patterns)
63
+ input.bytes_into("line.colors", @colors)
64
+ input.ints_into("line.counts", @counts)
65
+ input.ints_into("line.starts", @starts)
66
+ end
67
+
68
+ # The colours as $900E and $900F hold them, every line to be
69
+ # painted afresh, and the whole display changed.
70
+ def restored!(registers)
71
+ @border = registers[0x0f] & 0x07
72
+ @background = registers[0x0f] >> 4
73
+ @reverse = registers[0x0f].nobits?(0x08)
74
+ @aux = registers[0x0e] >> 4
75
+ @stale.fill(true)
76
+ @memo_keys.fill(-1)
77
+ @dirty_lines.fill(true)
78
+ end
79
+ end
80
+
81
+ include KeyedState
82
+ end
83
+
84
+ # The shape of the painter's per-line fields. A snapshot whose
85
+ # line.layout differs leaves them at power-on.
86
+ LINE_LAYOUT = 1
87
+
88
+ # The VIC-I's fields in a keyed snapshot module (Snapshot::Keyed): the
89
+ # registers, the beam, the text window's counters and the fetch in
90
+ # flight, the painter's lines under line.*, the picture, and the sound
91
+ # under sound.* What the registers decode to, where the line's fetches
92
+ # end and the next event are worked out on load.
93
+ module KeyedState
94
+ def save_fields(out)
95
+ out.bytes("registers", @registers).int("raster", @rasterline).int("column", @column)
96
+ out.bool("window_open", @window_open).bool("window_done", @window_done).int("row_start", @row_start)
97
+ out.int("matrix", @matrix).int("char_line", @char_line).int("text_rows", @text_rows)
98
+ out.int("fetch_from", @fetch_from).int("fetched", @fetched).int("code", @code)
99
+ out.int("line.layout", LINE_LAYOUT)
100
+ @painter.save_fields(out)
101
+ @sound&.save_fields(out, "sound.")
102
+ out
103
+ end
104
+
105
+ def load_fields(input)
106
+ input.bytes_into("registers", @registers)
107
+ @rasterline = input.int("raster", @rasterline)
108
+ @column = input.int("column", @column)
109
+ @window_open = input.bool("window_open", @window_open)
110
+ @window_done = input.bool("window_done", @window_done)
111
+ @row_start = input.int("row_start", @row_start)
112
+ @matrix = input.int("matrix", @matrix)
113
+ @char_line = input.int("char_line", @char_line)
114
+ @text_rows = input.int("text_rows", @text_rows)
115
+ @fetch_from = input.int("fetch_from", @fetch_from)
116
+ @fetched = input.int("fetched", @fetched)
117
+ @code = input.byte("code", @code)
118
+ line = input.int("line.layout", 0) == LINE_LAYOUT
119
+ input.adjust("line in progress from another renderer, restarted") unless line || input.missing?
120
+ @painter.load_fields(input, line)
121
+ @sound&.load_fields(input, "sound.")
122
+ end
123
+
124
+ def restored!
125
+ @origin_x = @registers[0] & 0x7f
126
+ @columns = @registers[2] & 0x7f
127
+ @origin_y = @registers[1]
128
+ rows_written(@registers[3])
129
+ bases_written
130
+ @slot = @rasterline * Painter::SLOTS
131
+ @fetch_to = 0
132
+ fetches_end if @window_open
133
+ schedule
134
+ @painter.restored!(@registers)
135
+ @sound&.restore_registers(@registers)
136
+ end
137
+ end
138
+
139
+ include KeyedState
140
+ end
141
+ end
142
+ end
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Badline
4
- class Vic20
5
- class VIC
4
+ module Chips
5
+ class VICI
6
6
  # Paints the VIC-I's display a line at a time from what the VIC
7
7
  # fetched for it: each character's pattern byte and colour nibble, the
8
8
  # pixel its first character starts at and how many it fetched. Each
@@ -70,34 +70,6 @@ module Badline
70
70
  @dirty_lines.fill(false)
71
71
  end
72
72
 
73
- # The colours, the line being painted and how far, what each line
74
- # fetched, and the display, for a snapshot. Whether it renders is
75
- # the host's, and a restored display is all changed.
76
- def save_state(out)
77
- out.int(@border).int(@background).int(@aux).boolean(@reverse)
78
- out.int(@row).int(@painted).boolean(@written)
79
- out.blob(@display).blob(@patterns).blob(@colors)
80
- out.ints(@counts).ints(@starts).booleans(@stale).ints(@memo_keys)
81
- end
82
-
83
- def load_state(input)
84
- @border = input.int
85
- @background = input.int
86
- @aux = input.int
87
- @reverse = input.boolean?
88
- @row = input.int
89
- @painted = input.int
90
- @written = input.boolean?
91
- input.blob_into(@display)
92
- input.blob_into(@patterns)
93
- input.blob_into(@colors)
94
- input.ints_into(@counts)
95
- input.ints_into(@starts)
96
- input.booleans_into(@stale)
97
- input.ints_into(@memo_keys)
98
- @dirty_lines.fill(true)
99
- end
100
-
101
73
  # Painting can be turned off, and once it's back on every line is
102
74
  # painted afresh.
103
75
  def render=(value)
@@ -0,0 +1,85 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ module Chips
5
+ class VICI
6
+ class Sound
7
+ # The sound's level averaged over each sample of the audio rate, then
8
+ # through the machine's output stage: a one-pole low-pass at 1,420 Hz
9
+ # and a one-pole high-pass at 158 Hz, both run at the audio rate.
10
+ #
11
+ # The two corners are fitted to xvic's step response: a voice turned
12
+ # on at volume 15 rises and decays back to the line as those two RC
13
+ # stages do, within 6 parts in 11,000.
14
+ class Output
15
+ LOW_PASS_HZ = 1_420
16
+ HIGH_PASS_HZ = 158
17
+
18
+ # Scales the loudest level, four voices high at volume 15, to the
19
+ # top of a signed 16-bit sample.
20
+ SCALE = 32_767.0 / 570
21
+
22
+ attr_reader :samples
23
+
24
+ # `level` is the sound's level as the recording starts, which the
25
+ # stage takes as settled.
26
+ def initialize(clock_hz:, rate:, level:)
27
+ @clock_hz = clock_hz
28
+ @rate = rate
29
+ @remainder = 0
30
+ @window = next_window
31
+ @left = @window
32
+ @sum = 0
33
+ @low = @drift = level.to_f
34
+ @low_pass = 1.0 - Math.exp(-2.0 * Math::PI * LOW_PASS_HZ / rate)
35
+ @high_pass = 1.0 - Math.exp(-2.0 * Math::PI * HIGH_PASS_HZ / rate)
36
+ @samples = []
37
+ end
38
+
39
+ # Adds `cycles` cycles at `level`, closing every sample they finish.
40
+ def hold(level, cycles)
41
+ left = @left
42
+ while cycles >= left
43
+ @sum += level * left
44
+ cycles -= left
45
+ close
46
+ left = @left
47
+ end
48
+ @sum += level * cycles
49
+ @left = left - cycles
50
+ end
51
+
52
+ # The samples closed since the last drain.
53
+ def drain
54
+ samples = @samples
55
+ @samples = []
56
+ samples
57
+ end
58
+
59
+ private
60
+
61
+ def close
62
+ @low += @low_pass * (@sum.fdiv(@window) - @low)
63
+ @drift += @high_pass * (@low - @drift)
64
+ sample = ((@low - @drift) * SCALE).round
65
+ sample = 32_767 if sample > 32_767
66
+ sample = -32_768 if sample < -32_768
67
+ @samples << sample
68
+ @sum = 0
69
+ @window = next_window
70
+ @left = @window
71
+ end
72
+
73
+ # Each window is a whole number of cycles, and they add up to the
74
+ # clock's cycles over a second.
75
+ def next_window
76
+ @remainder += @clock_hz
77
+ cycles = @remainder / @rate
78
+ @remainder -= cycles * @rate
79
+ cycles
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,227 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "badline/chips/vic_i/sound/output"
4
+
5
+ module Badline
6
+ module Chips
7
+ class VICI
8
+ # The VIC-I's sound: three square-wave voices, bass, alto and soprano at
9
+ # $900A-$900C, a noise voice at $900D, and the volume in $900E bits 0-3.
10
+ #
11
+ # Each voice has a 7-bit counter, ticked every 16, 8, 4 or 2 cycles from
12
+ # bass to noise. It counts up from the low 7 bits of its register, and
13
+ # on the tick it would reach 127 it reloads from the register instead.
14
+ # So a voice's counter comes round every 128 - ((value + 1) & 127)
15
+ # ticks, and the register's new value takes hold at the next reload.
16
+ #
17
+ # A tone voice's counter shifts an 8-bit shift register at each reload,
18
+ # bit 7 out and back into bit 0 inverted while bit 7 of the register
19
+ # enables the voice, and a zero while it doesn't. Bit 0 is the voice's
20
+ # output: a square wave of 16 shifts while the voice is on, which a
21
+ # voice turned off shifts out to silence.
22
+ #
23
+ # The noise voice's counter shifts a 16-bit LFSR instead, bit 0 in from
24
+ # the XOR of bits 3, 12, 14 and 15 while the voice is on and a one
25
+ # while it's off. Each time the LFSR's bit 0 rises, it shifts the noise
26
+ # voice's own 8-bit shift register, bit 7 back into bit 0, inverted
27
+ # while the voice is on. Its bit 0 is the voice's output while the voice
28
+ # is on, and it is silent while it's off.
29
+ #
30
+ # The output is the voices that are high, plus a little more, times the
31
+ # volume, so a volume write steps the output even with every voice off.
32
+ #
33
+ # The voices catch up to the current cycle whenever a write lands, the
34
+ # samples are drained or #catch_up! asks, one shift at a time, whether
35
+ # or not #record collects the output, so their state doesn't depend on
36
+ # whether the host plays it. Unrecorded, while every voice is off and
37
+ # shifted out to silence, they skip to the cycle at once.
38
+ class Sound
39
+ VOICES = 4
40
+ NOISE = 3
41
+
42
+ # The cycles between ticks of each voice's counter.
43
+ DIVIDERS = [16, 8, 4, 2].freeze
44
+
45
+ # What a voice that is high adds to the output's level, and what the
46
+ # output carries with every voice low, before the volume.
47
+ VOICE_LEVEL = 9
48
+ IDLE_LEVEL = 2
49
+
50
+ attr_reader :volume
51
+
52
+ # `clock` answers the machine's cycles, `clock_hz` the rate they run at.
53
+ def initialize(clock, clock_hz)
54
+ @clock = clock
55
+ @clock_hz = clock_hz
56
+ @output = nil
57
+ @time = 0
58
+ @shift_registers = Array.new(VOICES, 0)
59
+ @next_shifts = Array.new(VOICES, 0)
60
+ @frequencies = Array.new(VOICES, 0)
61
+ @enabled = Array.new(VOICES, false)
62
+ power_on!
63
+ end
64
+
65
+ def model = :mos6561
66
+
67
+ # The power-on state: every register clear and the tone voices' shift
68
+ # registers empty. The noise voice's LFSR and shift register start full
69
+ # of ones, as a noise voice left off fills them.
70
+ def power_on!
71
+ now = @clock.cycles
72
+ run_to(now)
73
+ @volume = 0
74
+ VOICES.times do |voice|
75
+ @frequencies[voice] = 0
76
+ @enabled[voice] = false
77
+ @shift_registers[voice] = 0
78
+ divider = DIVIDERS[voice]
79
+ @next_shifts[voice] = ((now / divider) + 128) * divider
80
+ end
81
+ @shift_registers[NOISE] = 0xff
82
+ @lfsr = 0xffff
83
+ @level = level_now
84
+ end
85
+
86
+ # Starts collecting the output, averaged down to `rate` samples a
87
+ # second, for #drain_samples. `clock_hz` is the rate the cycles are
88
+ # taken to run at.
89
+ def record(rate:, clock_hz: @clock_hz)
90
+ run_to(@clock.cycles)
91
+ @output = Output.new(clock_hz:, rate:, level: @level)
92
+ end
93
+
94
+ def recording? = !@output.nil?
95
+
96
+ # Runs the voices up to the current cycle.
97
+ def catch_up! = run_to(@clock.cycles)
98
+
99
+ # The samples recorded since the last drain.
100
+ def drain_samples
101
+ output = @output
102
+ return [] if output.nil?
103
+
104
+ run_to(@clock.cycles)
105
+ output.drain
106
+ end
107
+
108
+ # A write to $900A-$900E, which takes hold from the next cycle.
109
+ def write(register, value)
110
+ run_to(@clock.cycles + 1)
111
+ if register == 0x0e
112
+ @volume = value & 0x0f
113
+ else
114
+ voice = register - 0x0a
115
+ @frequencies[voice] = value & 0x7f
116
+ @enabled[voice] = value >= 0x80
117
+ end
118
+ @level = level_now
119
+ end
120
+
121
+ # A voice's shift register as of the current cycle.
122
+ def shift_register(voice)
123
+ run_to(@clock.cycles)
124
+ @shift_registers[voice]
125
+ end
126
+
127
+ def lfsr
128
+ run_to(@clock.cycles)
129
+ @lfsr
130
+ end
131
+
132
+ private
133
+
134
+ # Runs the voices up to the start of cycle `target`, the output holding
135
+ # each level for the cycles it lasts.
136
+ def run_to(target)
137
+ time = @time
138
+ return if target <= time
139
+
140
+ output = @output
141
+ return skip_to(target) if output.nil? && silent?
142
+
143
+ following = next_shift
144
+ while following < target
145
+ output&.hold(@level, following - time)
146
+ time = following
147
+ shift_due(time)
148
+ @level = level_now
149
+ following = next_shift
150
+ end
151
+ output&.hold(@level, target - time)
152
+ @time = target
153
+ end
154
+
155
+ # Whether every voice is off and shifted out to where its shifts
156
+ # change nothing: the tone voices' registers empty and the noise
157
+ # voice's LFSR full of ones.
158
+ def silent?
159
+ enabled = @enabled
160
+ registers = @shift_registers
161
+ !(enabled[0] || enabled[1] || enabled[2] || enabled[NOISE]) &&
162
+ registers[0].zero? && registers[1].zero? && registers[2].zero? && @lfsr == 0xffff
163
+ end
164
+
165
+ # Moves each shift before `target` to its next turn from there,
166
+ # keeping its phase.
167
+ def skip_to(target)
168
+ VOICES.times do |voice|
169
+ due = @next_shifts[voice]
170
+ next if due >= target
171
+
172
+ interval = period(voice) * DIVIDERS[voice]
173
+ @next_shifts[voice] = due + ((target - due + interval - 1) / interval * interval)
174
+ end
175
+ @time = target
176
+ end
177
+
178
+ def next_shift
179
+ shifts = @next_shifts
180
+ following = shifts[0]
181
+ following = shifts[1] if shifts[1] < following
182
+ following = shifts[2] if shifts[2] < following
183
+ following = shifts[3] if shifts[3] < following
184
+ following
185
+ end
186
+
187
+ def shift_due(time)
188
+ shifts = @next_shifts
189
+ VOICES.times do |voice|
190
+ next unless shifts[voice] == time
191
+
192
+ voice == NOISE ? step_noise : shift_tone(voice)
193
+ shifts[voice] = time + (period(voice) * DIVIDERS[voice])
194
+ end
195
+ end
196
+
197
+ def shift_tone(voice)
198
+ bits = @shift_registers[voice]
199
+ fill = @enabled[voice] && bits < 0x80 ? 1 : 0
200
+ @shift_registers[voice] = ((bits << 1) | fill) & 0xff
201
+ end
202
+
203
+ def step_noise
204
+ lfsr = @lfsr
205
+ enabled = @enabled[NOISE]
206
+ fill = enabled ? ((lfsr >> 3) ^ (lfsr >> 12) ^ (lfsr >> 14) ^ (lfsr >> 15)) & 1 : 1
207
+ @lfsr = ((lfsr << 1) | fill) & 0xffff
208
+ return unless fill == 1 && lfsr.even?
209
+
210
+ bits = @shift_registers[NOISE]
211
+ fill = (bits >> 7) ^ (enabled ? 1 : 0)
212
+ @shift_registers[NOISE] = ((bits << 1) | fill) & 0xff
213
+ end
214
+
215
+ # The ticks between two reloads of a voice's counter.
216
+ def period(voice) = 128 - ((@frequencies[voice] + 1) & 0x7f)
217
+
218
+ def level_now
219
+ registers = @shift_registers
220
+ high = (registers[0] & 1) + (registers[1] & 1) + (registers[2] & 1)
221
+ high += registers[NOISE] & 1 if @enabled[NOISE]
222
+ ((high * VOICE_LEVEL) + IDLE_LEVEL) * @volume
223
+ end
224
+ end
225
+ end
226
+ end
227
+ end
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Badline
4
- class Vic20
5
- class VIC
4
+ module Chips
5
+ class VICI
6
6
  # The text window's rows: where it opens and closes, the video matrix
7
7
  # counter and the character line, and the registers that place the
8
8
  # window and its memory.
@@ -1,11 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "badline/vic20/vic/painter"
4
- require "badline/vic20/vic/text_window"
5
- require "badline/vic20/vic/saved_state"
3
+ require "badline/chips/vic_i/painter"
4
+ require "badline/chips/vic_i/text_window"
5
+ require "badline/chips/vic_i/sound"
6
+ require "badline/chips/vic_i/keyed_parts"
6
7
 
7
8
  module Badline
8
- class Vic20
9
+ module Chips
9
10
  # The VIC-I, the 6561 on PAL machines: its sixteen registers, the
10
11
  # raster, the text window's fetches over the V-bus and the picture. The
11
12
  # writes to the sound's registers, $900A-$900E, go on to the Sound
@@ -39,9 +40,8 @@ module Badline
39
40
  # display's lines start 6 cycles after the raster count moves, as
40
41
  # xvic's view of the frame does, so the character fetched 16 cycles
41
42
  # into the line lands 48 pixels in. Lines 0-27 are blanked.
42
- class VIC
43
+ class VICI
43
44
  include TextWindow
44
- include SavedState
45
45
 
46
46
  # What a VIC-I sets for the machine: the clock, the raster's cycles
47
47
  # per line and lines per frame, and how many lines at the top of the
@@ -0,0 +1,119 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ module Chips
5
+ class Z80
6
+ # The ALU's 8- and 16-bit arithmetic. Bits 5 and 3 of F come from the
7
+ # result, except for CP, which takes them from the operand, and the
8
+ # 16-bit operations, which take them from the high byte.
9
+ module Arithmetic
10
+ private
11
+
12
+ # The eight operations an opcode's bits 5-3 pick: ADD ADC SUB SBC AND
13
+ # XOR OR CP.
14
+ def alu(operation, value)
15
+ case operation
16
+ when 0 then add_a(value, 0)
17
+ when 1 then add_a(value, @f & CF)
18
+ when 2 then @a = subtract(value, 0)
19
+ when 3 then @a = subtract(value, @f & CF)
20
+ when 4 then logic(@a & value, HF)
21
+ when 5 then logic(@a ^ value, 0)
22
+ when 6 then logic(@a | value, 0)
23
+ else compare(value)
24
+ end
25
+ end
26
+
27
+ def add_a(value, carry)
28
+ result = @a + value + carry
29
+ assign_flags(SZ53[result & 0xff] | (result >> 8) | ((@a ^ value ^ result) & HF) |
30
+ (((@a ^ value ^ 0x80) & (@a ^ result) & 0x80) >> 5))
31
+ @a = result & 0xff
32
+ end
33
+
34
+ def subtract(value, carry)
35
+ result = @a - value - carry
36
+ assign_flags(SZ53[result & 0xff] | NF | ((result >> 8) & CF) | ((@a ^ value ^ result) & HF) |
37
+ (((@a ^ value) & (@a ^ result) & 0x80) >> 5))
38
+ result & 0xff
39
+ end
40
+
41
+ def compare(value)
42
+ subtract(value, 0)
43
+ assign_flags((@f & ~XYF) | (value & XYF))
44
+ end
45
+
46
+ def logic(result, half)
47
+ @a = result
48
+ assign_flags(SZ53P[result] | half)
49
+ end
50
+
51
+ def increment(value)
52
+ result = (value + 1) & 0xff
53
+ assign_flags((@f & CF) | SZ53[result] | (result == 0x80 ? PF : 0) | (result.nobits?(0x0f) ? HF : 0))
54
+ result
55
+ end
56
+
57
+ def decrement(value)
58
+ result = (value - 1) & 0xff
59
+ assign_flags((@f & CF) | NF | SZ53[result] | (result == 0x7f ? PF : 0) | (value.nobits?(0x0f) ? HF : 0))
60
+ result
61
+ end
62
+
63
+ def add16(left, right)
64
+ result = left + right
65
+ @wz = (left + 1) & 0xffff
66
+ assign_flags((@f & (SF | ZF | PF)) | ((result >> 8) & XYF) | (((left ^ right ^ result) >> 8) & HF) |
67
+ (result >> 16))
68
+ result & 0xffff
69
+ end
70
+
71
+ def adc16(right)
72
+ left = hl
73
+ result = left + right + (@f & CF)
74
+ @wz = (left + 1) & 0xffff
75
+ self.hl = result & 0xffff
76
+ assign_flags(word_flags(result) | (((left ^ right ^ result) >> 8) & HF) |
77
+ (((left ^ right ^ 0x8000) & (left ^ result) & 0x8000) >> 13))
78
+ end
79
+
80
+ def sbc16(right)
81
+ left = hl
82
+ result = left - right - (@f & CF)
83
+ @wz = (left + 1) & 0xffff
84
+ self.hl = result & 0xffff
85
+ assign_flags(word_flags(result) | NF | (((left ^ right ^ result) >> 8) & HF) |
86
+ (((left ^ right) & (left ^ result) & 0x8000) >> 13))
87
+ end
88
+
89
+ def word_flags(result)
90
+ ((result >> 8) & (SF | XYF)) | (result.nobits?(0xffff) ? ZF : 0) | ((result >> 16) & CF)
91
+ end
92
+
93
+ def daa
94
+ low = @a & 0x0f
95
+ carry = @f.anybits?(CF) || @a > 0x99 ? CF : 0
96
+ correction = (carry.zero? ? 0 : 0x60) | (@f.anybits?(HF) || low > 9 ? 0x06 : 0)
97
+ subtracting = @f.anybits?(NF)
98
+ half = daa_half(low, subtracting)
99
+ @a = (subtracting ? @a - correction : @a + correction) & 0xff
100
+ assign_flags(SZ53P[@a] | (@f & NF) | carry | half)
101
+ end
102
+
103
+ def daa_half(low, subtracting)
104
+ if subtracting
105
+ @f.anybits?(HF) && low < 6 ? HF : 0
106
+ else
107
+ low > 9 ? HF : 0
108
+ end
109
+ end
110
+
111
+ def neg
112
+ value = @a
113
+ @a = 0
114
+ @a = subtract(value, 0)
115
+ end
116
+ end
117
+ end
118
+ end
119
+ end