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,213 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ module Chips
5
+ class CIA
6
+ class Timer
7
+ # A timer's fields in a keyed snapshot module (Snapshot::Keyed),
8
+ # under `prefix`: the counter, the latch and the pipeline between
9
+ # them. The fast-path flags are worked out again on load.
10
+ module KeyedState
11
+ def save_fields(out, prefix)
12
+ out.int("#{prefix}counter", @counter).int("#{prefix}latch", @latch).int("#{prefix}pipe", @pipe)
13
+ out.int("#{prefix}load_delay", @load_delay).bool("#{prefix}reload", @reload)
14
+ out.bool("#{prefix}underflowed", @underflowed).int("#{prefix}oneshot_linger", @oneshot_linger)
15
+ out.bool("#{prefix}toggle", @toggle)
16
+ end
17
+
18
+ def load_fields(input, prefix)
19
+ @counter = input.word("#{prefix}counter", @counter)
20
+ @latch = input.word("#{prefix}latch", @latch)
21
+ @pipe = input.masked("#{prefix}pipe", @pipe, 0b11)
22
+ @load_delay = input.int("#{prefix}load_delay", @load_delay)
23
+ @reload = input.bool("#{prefix}reload", @reload)
24
+ @underflowed = input.bool("#{prefix}underflowed", @underflowed)
25
+ @oneshot_linger = input.int("#{prefix}oneshot_linger", @oneshot_linger)
26
+ @toggle = input.bool("#{prefix}toggle", @toggle)
27
+ end
28
+
29
+ def restored! = settle
30
+ end
31
+
32
+ include KeyedState
33
+ end
34
+
35
+ class InterruptRegister
36
+ # The interrupt register's fields in a keyed snapshot module, under
37
+ # `prefix`: the mask, the flags, an assert on its way and the reads
38
+ # whose acknowledge is still landing.
39
+ module KeyedState
40
+ def save_fields(out, prefix)
41
+ out.int("#{prefix}mask", @mask.value).int("#{prefix}status", @status.value)
42
+ out.int("#{prefix}pending", @pending)
43
+ out.optional_int("#{prefix}read", @read).optional_int("#{prefix}read_last_cycle", @read_last_cycle)
44
+ out.optional_int("#{prefix}read_two_cycles_ago", @read_two_cycles_ago)
45
+ out.bool("#{prefix}timer_b_bug", @timer_b_bug)
46
+ end
47
+
48
+ def load_fields(input, prefix)
49
+ @mask.value = input.byte("#{prefix}mask", @mask.value)
50
+ @status.value = input.byte("#{prefix}status", @status.value)
51
+ @pending = input.int("#{prefix}pending", @pending)
52
+ @read = input.optional_int("#{prefix}read")
53
+ @read_last_cycle = input.optional_int("#{prefix}read_last_cycle")
54
+ @read_two_cycles_ago = input.optional_int("#{prefix}read_two_cycles_ago")
55
+ @timer_b_bug = input.bool("#{prefix}timer_b_bug", @timer_b_bug)
56
+ end
57
+
58
+ # Quiet when nothing is due and no read is recent enough to matter.
59
+ def restored!
60
+ @quiet = @pending.zero? && @read.nil? && @read_last_cycle.nil? && @read_two_cycles_ago.nil?
61
+ end
62
+ end
63
+
64
+ include KeyedState
65
+ end
66
+
67
+ class Serial
68
+ # The serial port's fields in a keyed snapshot module, under
69
+ # `prefix`: the registers, the bit count, the delay lines, the CNT
70
+ # and SP levels, and whether the next cycle can be skipped, which
71
+ # can lag what the delay lines show by a cycle.
72
+ module KeyedState
73
+ def save_fields(out, prefix)
74
+ out.int("#{prefix}data", @data).int("#{prefix}shift", @shift).int("#{prefix}steps", @steps)
75
+ out.optional_int("#{prefix}empty_in", @empty_in).optional_int("#{prefix}busy_in", @busy_in)
76
+ out.bool("#{prefix}busy", @busy).bool("#{prefix}abandoned", @abandoned)
77
+ out.optional_int("#{prefix}pending", @pending).bool("#{prefix}underflow_high", @underflow_high)
78
+ out.bool("#{prefix}in_flight", @in_flight).int("#{prefix}flight_up", @flight_up)
79
+ out.int("#{prefix}flight_down", @flight_down).bool("#{prefix}cnt", @cnt).bool("#{prefix}cnt_in", @cnt_in)
80
+ out.bool("#{prefix}sp_in", @sp_in).bool("#{prefix}sp_out", @sp_out).bool("#{prefix}idle", @idle)
81
+ end
82
+
83
+ def load_fields(input, prefix)
84
+ @data = input.byte("#{prefix}data", @data)
85
+ @shift = input.byte("#{prefix}shift", @shift)
86
+ @steps = input.int("#{prefix}steps", @steps)
87
+ @empty_in = input.optional_int("#{prefix}empty_in")
88
+ @busy_in = input.optional_int("#{prefix}busy_in")
89
+ @busy = input.bool("#{prefix}busy", @busy)
90
+ @abandoned = input.bool("#{prefix}abandoned", @abandoned)
91
+ @pending = input.optional_int("#{prefix}pending")
92
+ load_line_fields(input, prefix)
93
+ end
94
+
95
+ def restored! = nil
96
+
97
+ private
98
+
99
+ def load_line_fields(input, prefix)
100
+ @underflow_high = input.bool("#{prefix}underflow_high", @underflow_high)
101
+ @in_flight = input.bool("#{prefix}in_flight", @in_flight)
102
+ @flight_up = input.int("#{prefix}flight_up", @flight_up)
103
+ @flight_down = input.int("#{prefix}flight_down", @flight_down)
104
+ @cnt = input.bool("#{prefix}cnt", @cnt)
105
+ @cnt_in = input.bool("#{prefix}cnt_in", @cnt_in)
106
+ @sp_in = input.bool("#{prefix}sp_in", @sp_in)
107
+ @sp_out = input.bool("#{prefix}sp_out", @sp_out)
108
+ @idle = input.bool("#{prefix}idle", @idle)
109
+ end
110
+ end
111
+
112
+ include KeyedState
113
+ end
114
+
115
+ class TimeOfDay
116
+ # The TOD clock's fields in a keyed snapshot module, under `prefix`:
117
+ # the clock, the alarm and a latched read field by field, the
118
+ # pulses towards the next tenth, and the mains divider's phase in
119
+ # cycle·Hz (it reaches the clock rate when a pulse arrives). The
120
+ # 50/60 Hz divider comes from CRA bit 7 on load.
121
+ module KeyedState
122
+ def save_fields(out, prefix)
123
+ out.int("#{prefix}mains_phase", @accumulator).int("#{prefix}pulses", @pulses)
124
+ out.bool("#{prefix}stopped", @stopped).bool("#{prefix}alarm_pending", @alarm_pending)
125
+ FIELDS.each do |field|
126
+ out.int("#{prefix}clock.#{field}", @clock[field]).int("#{prefix}alarm.#{field}", @alarm[field])
127
+ end
128
+ latch = @latch
129
+ FIELDS.each { |field| out.int("#{prefix}latch.#{field}", latch[field]) } if latch
130
+ end
131
+
132
+ def load_fields(input, prefix)
133
+ @accumulator = input.int("#{prefix}mains_phase", @accumulator)
134
+ @pulses = input.int("#{prefix}pulses", @pulses)
135
+ @stopped = input.bool("#{prefix}stopped", @stopped)
136
+ @alarm_pending = input.bool("#{prefix}alarm_pending", @alarm_pending)
137
+ FIELDS.each do |field|
138
+ @clock[field] = input.byte("#{prefix}clock.#{field}", @clock[field])
139
+ @alarm[field] = input.byte("#{prefix}alarm.#{field}", @alarm[field])
140
+ end
141
+ load_latch_fields(input, prefix)
142
+ end
143
+
144
+ def restored!(fifty_hz)
145
+ self.fifty_hz = fifty_hz
146
+ end
147
+
148
+ private
149
+
150
+ def load_latch_fields(input, prefix)
151
+ return @latch = nil if input.optional_int("#{prefix}latch.hours").nil?
152
+
153
+ @latch = FIELDS.to_h { |field| [field, input.byte("#{prefix}latch.#{field}", 0)] }
154
+ end
155
+ end
156
+
157
+ include KeyedState
158
+ end
159
+
160
+ # The CIA's fields in a keyed snapshot module (Snapshot::Keyed): the
161
+ # ports, the control registers, the PB4 and CNT levels, and its parts
162
+ # under ta., tb., icr., sdr. and tod.
163
+ module KeyedState
164
+ def save_fields(out)
165
+ out.int("pra", @data_port_a).int("prb", @data_port_b).int("ddra", @data_dir_a).int("ddrb", @data_dir_b)
166
+ out.int("cra", @control_a.value).int("crb", @control_b.value)
167
+ out.bool("pb4_high", @port_b4_high).bool("pb4_driven_high", @port_b4_driven_high)
168
+ out.bool("cnt_high", @cnt_high).bool("cnt_rise", @cnt_rise)
169
+ @ta.save_fields(out, "ta.")
170
+ @tb.save_fields(out, "tb.")
171
+ @icr.save_fields(out, "icr.")
172
+ @serial.save_fields(out, "sdr.")
173
+ @tod.save_fields(out, "tod.")
174
+ out
175
+ end
176
+
177
+ def load_fields(input)
178
+ @data_port_a = input.byte("pra", @data_port_a)
179
+ @data_port_b = input.byte("prb", @data_port_b)
180
+ @data_dir_a = input.byte("ddra", @data_dir_a)
181
+ @data_dir_b = input.byte("ddrb", @data_dir_b)
182
+ @control_a.value = input.byte("cra", @control_a.value)
183
+ @control_b.value = input.byte("crb", @control_b.value)
184
+ @port_b4_high = input.bool("pb4_high", @port_b4_high)
185
+ @port_b4_driven_high = input.bool("pb4_driven_high", @port_b4_driven_high)
186
+ @cnt_high = input.bool("cnt_high", @cnt_high)
187
+ @cnt_rise = input.bool("cnt_rise", @cnt_rise)
188
+ load_part_fields(input)
189
+ end
190
+
191
+ def restored!
192
+ @ta.restored!
193
+ @tb.restored!
194
+ @icr.restored!
195
+ @serial.restored!
196
+ @tod.restored!(@control_a.clock_frequency?)
197
+ end
198
+
199
+ private
200
+
201
+ def load_part_fields(input)
202
+ @ta.load_fields(input, "ta.")
203
+ @tb.load_fields(input, "tb.")
204
+ @icr.load_fields(input, "icr.")
205
+ @serial.load_fields(input, "sdr.")
206
+ @tod.load_fields(input, "tod.")
207
+ end
208
+ end
209
+
210
+ include KeyedState
211
+ end
212
+ end
213
+ end
@@ -0,0 +1,246 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ module Chips
5
+ class CIA
6
+ # The serial shift register and the CNT line that clocks it.
7
+ #
8
+ # With CRA bit 6 clear the port is an input: SP is sampled on every
9
+ # rising CNT edge, and the byte reaches the data register after eight of
10
+ # them. With the bit set the CIA drives CNT itself, toggling it on every
11
+ # timer A underflow, so a byte takes sixteen underflows to go out. CNT falls
12
+ # as each bit is put on SP, and rises again to clock it in.
13
+ #
14
+ # The register counts itself empty as soon as the eighth bit is on SP,
15
+ # fifteen underflows in: the serial flag follows a few cycles later
16
+ # rather than waiting for the sixteenth underflow, and a byte written
17
+ # from that point on goes out on the next one.
18
+ class Serial
19
+ BITS = 8
20
+
21
+ # A byte written to the data register reaches the shift register one
22
+ # cycle later. The empty countdown starts on the underflow that puts
23
+ # the eighth bit on SP and is decremented on that same cycle, so the
24
+ # flag lands four cycles after it.
25
+ LOAD_DELAY = 1
26
+ EMPTY_DELAY = 5
27
+ BUSY_DELAY = 5
28
+
29
+ # The logic watching the shift register runs a few cycles behind it,
30
+ # so the level saying a bit is still on its way out is scheduled
31
+ # through a delay line: a bit set at position 7 - n takes effect n
32
+ # cycles later. Putting a bit on SP raises the level after one cycle
33
+ # and drops it again after two before raising it for good after
34
+ # three; the eighth bit skips straight to the last of those. CNT
35
+ # rising over a bit lowers the level four cycles later.
36
+ IN_FLIGHT = 0x50
37
+ LAST_IN_FLIGHT = 0x10
38
+ BOUNCE = 0x20
39
+ SETTLED = 0x08
40
+
41
+ attr_reader :data, :sp_out, :cnt, :cnt_in, :idle
42
+ attr_accessor :sp_in
43
+
44
+ def initialize(control)
45
+ @control = control
46
+ @data = 0x0
47
+ @shift = 0x0
48
+ @steps = 0
49
+ @empty_in = nil
50
+ @busy_in = nil
51
+ @busy = false
52
+ @abandoned = false
53
+ @pending = nil
54
+ @underflow_high = false
55
+ @in_flight = false
56
+ @flight_up = 0x0
57
+ @flight_down = 0x0
58
+ @idle = true
59
+ # Nothing drives the user port, so both lines float high.
60
+ @cnt = true
61
+ @cnt_in = true
62
+ @sp_in = true
63
+ @sp_out = true
64
+ end
65
+
66
+ # The bits shifted so far, and the byte waiting to go out after the
67
+ # one shifting.
68
+ attr_reader :steps, :pending
69
+
70
+ def shift_register = @shift
71
+
72
+ # Sets the data register and the shift register, as a VICE snapshot
73
+ # gives them.
74
+ def restore_shift(data, shift)
75
+ @data = data
76
+ @shift = shift
77
+ end
78
+
79
+ def output?
80
+ @control.serial_mode?
81
+ end
82
+
83
+ # Whether nothing is counting down or in the delay lines, so only
84
+ # timer A's underflows reach the register.
85
+ def drained? = idle?
86
+
87
+ # Takes timer A's underflow line as cycle! would have left it, with
88
+ # +underflowed+ its level on the last cycle, for a CIA run without
89
+ # the register in use (CIA#fast_forward).
90
+ def follow_underflow(underflowed)
91
+ @underflow_high = underflowed
92
+ @idle = !underflowed
93
+ end
94
+
95
+ def cnt_in=(level)
96
+ @cnt_in = level
97
+ @cnt = level unless output?
98
+ end
99
+
100
+ def write(value)
101
+ @data = value
102
+ @pending = LOAD_DELAY
103
+ @idle = false
104
+ end
105
+
106
+ # Clocked every cycle with timer A's underflow line. Yields once the
107
+ # shift register reports itself empty, which it does without waiting
108
+ # for the underflow that raises CNT over the eighth bit.
109
+ def cycle!(underflowed)
110
+ return if @idle && !underflowed
111
+
112
+ shift(underflowed)
113
+ @pending -= 1 if @pending&.positive?
114
+ drain_flight
115
+ count_busy
116
+ @idle = !underflowed && idle?
117
+ return if @empty_in.nil?
118
+
119
+ @empty_in -= 1
120
+ return unless @empty_in.zero?
121
+
122
+ @empty_in = nil
123
+ @busy = false
124
+ yield
125
+ end
126
+
127
+ # Clocked by rising CNT edges while receiving. Yields once a whole
128
+ # byte has arrived.
129
+ def rising_edge!
130
+ return if output?
131
+
132
+ @shift = ((@shift << 1) | (@sp_in ? 1 : 0)) & 0xff
133
+ @steps += 1
134
+ return unless @steps == BITS
135
+
136
+ @steps = 0
137
+ @data = @shift
138
+ yield if block_given?
139
+ end
140
+
141
+ # A mode change drops the byte in flight and hands CNT back to
142
+ # whichever side drives it now, and yields: that byte counts as gone.
143
+ # Handing the port over as an input tears a transmission down, so a
144
+ # busy register reports it; the same change latches how far the bit
145
+ # in flight had got, and taking the port back reports that.
146
+ def reset!
147
+ abandoned = output? ? @abandoned : @busy
148
+ @abandoned = output? ? false : @in_flight
149
+ @in_flight = false
150
+ @steps = 0
151
+ @empty_in = nil
152
+ @busy_in = nil
153
+ @busy = false
154
+ @pending = nil
155
+ @cnt = output? || @cnt_in
156
+ yield if abandoned
157
+ end
158
+
159
+ private
160
+
161
+ # Without an underflow, a register with nothing counting down and
162
+ # nothing in the delay lines has nothing to do on a cycle.
163
+ def idle?
164
+ !@pending&.positive? && (@flight_up | @flight_down).zero? &&
165
+ @busy_in.nil? && @empty_in.nil?
166
+ end
167
+
168
+ # The shift register picks up a waiting byte whenever the underflow
169
+ # line is asserted, but every half-step after that needs a fresh edge
170
+ # on it. A zero latch holds the line down for good rather than
171
+ # pulsing it, so such a byte stops after its first bit and never
172
+ # reaches the data register.
173
+ def shift(underflowed)
174
+ edge = underflowed && !@underflow_high
175
+ @underflow_high = underflowed
176
+ clock(edge) if underflowed && output?
177
+ end
178
+
179
+ def clock(edge)
180
+ start if @steps <= 1
181
+ if @steps.zero?
182
+ @flight_down |= SETTLED
183
+ elsif edge || @steps == BITS * 2
184
+ half_step
185
+ end
186
+ end
187
+
188
+ def half_step
189
+ @steps -= 1
190
+ @cnt = @steps.even?
191
+ if @cnt
192
+ @flight_down |= SETTLED
193
+ else
194
+ shift_out
195
+ end
196
+ end
197
+
198
+ # The register reads as busy from a few cycles after the first bit
199
+ # reaches SP until it reports itself empty over the eighth.
200
+ def mark_busy
201
+ case @steps
202
+ when (BITS * 2) - 1 then @busy_in = BUSY_DELAY
203
+ when 1
204
+ @empty_in = EMPTY_DELAY
205
+ @busy_in = nil
206
+ end
207
+ end
208
+
209
+ def count_busy
210
+ return if @busy_in.nil?
211
+
212
+ @busy_in -= 1
213
+ return unless @busy_in.zero?
214
+
215
+ @busy_in = nil
216
+ @busy = true
217
+ end
218
+
219
+ def drain_flight
220
+ @in_flight = false if @flight_down.anybits?(0x80)
221
+ @flight_down = (@flight_down << 1) & 0xff
222
+ @in_flight = true if @flight_up.anybits?(0x80)
223
+ @flight_up = (@flight_up << 1) & 0xff
224
+ end
225
+
226
+ # A byte only moves out of the data register once the one before it
227
+ # has put its last bit on SP.
228
+ def start
229
+ return unless @pending&.zero?
230
+
231
+ @pending = nil
232
+ @shift = @data
233
+ @steps = BITS * 2
234
+ end
235
+
236
+ def shift_out
237
+ @sp_out = @shift.anybits?(0x80)
238
+ @shift = (@shift << 1) & 0xff
239
+ mark_busy
240
+ @flight_up |= @steps == 1 ? LAST_IN_FLIGHT : IN_FLIGHT
241
+ @flight_down |= BOUNCE
242
+ end
243
+ end
244
+ end
245
+ end
246
+ end
@@ -0,0 +1,210 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ module Chips
5
+ class CIA
6
+ class TimeOfDay
7
+ # The TOD pin is fed from the AC supply. A CIA passes its region's
8
+ # clock and mains frequency, and these are PAL's.
9
+ CLOCK_HZ = Region::PAL.clock_hz
10
+ MAINS_HZ = Region::PAL.mains_hz
11
+
12
+ FIELDS = %i[tenths seconds minutes hours].freeze
13
+
14
+ def initialize(clock_hz: CLOCK_HZ, mains_hz: MAINS_HZ)
15
+ # The accumulator advances mains_hz per cycle, so a TOD pin pulse has
16
+ # arrived when it reaches clock_hz. Integer math keeps it exact.
17
+ @cycles_per_pulse = clock_hz
18
+ @mains_hz = mains_hz
19
+ @accumulator = 0
20
+ @pulses = 0
21
+ @clock = { tenths: 0x00, seconds: 0x00, minutes: 0x00, hours: 0x01 }
22
+ @alarm = { tenths: 0x00, seconds: 0x00, minutes: 0x00, hours: 0x00 }
23
+ @latch = nil
24
+ @stopped = true
25
+ @alarm_pending = false
26
+ self.fifty_hz = false
27
+ end
28
+
29
+ # CRA bit 7 divides the TOD pin by 5 instead of 6. The divider has to
30
+ # match the pin frequency to keep time, so PAL software selects 50 Hz.
31
+ def fifty_hz=(enabled)
32
+ @match = enabled ? 4 : 5
33
+ end
34
+
35
+ # Whether the clock is stopped, or gets no pulses on its pin, with no
36
+ # alarm to raise, so its cycles move only the divider's phase.
37
+ def quiet? = (@stopped || @mains_hz.zero?) && !@alarm_pending
38
+
39
+ # Runs +cycles+ quiet cycles at once.
40
+ def fast_forward(cycles)
41
+ @accumulator = (@accumulator + (cycles * @mains_hz)) % @cycles_per_pulse
42
+ end
43
+
44
+ def cycle!
45
+ @accumulator += @mains_hz
46
+ pulse! if @accumulator >= @cycles_per_pulse
47
+ return unless @alarm_pending
48
+
49
+ @alarm_pending = false
50
+ yield if block_given?
51
+ end
52
+
53
+ def tenths
54
+ value = (@latch || @clock)[:tenths]
55
+ @latch = nil
56
+ value
57
+ end
58
+
59
+ def seconds = (@latch || @clock)[:seconds]
60
+ def minutes = (@latch || @clock)[:minutes]
61
+
62
+ def hours
63
+ @latch ||= @clock.dup
64
+ @latch[:hours]
65
+ end
66
+
67
+ attr_reader :pulses
68
+
69
+ # The clock as a read of the tenths would see it: the latch while the
70
+ # clock is latched. In FIELDS order.
71
+ def latched_fields = FIELDS.map { |field| (@latch || @clock)[field] }
72
+
73
+ # The cycles until the clock next steps a tenth, as VICE counts them.
74
+ def cycles_to_tenth
75
+ to_pulse = (@cycles_per_pulse - @accumulator + @mains_hz - 1) / @mains_hz
76
+ to_pulse + ([@match - @pulses, 0].max * @cycles_per_pulse / @mains_hz)
77
+ end
78
+
79
+ # Sets the clock, the alarm and the latch from VICE's fields, in FIELDS
80
+ # order, the latch nil while unlatched.
81
+ def restore_fields(clock, alarm, latch)
82
+ FIELDS.each_with_index do |field, i|
83
+ @clock[field] = clock[i]
84
+ @alarm[field] = alarm[i]
85
+ end
86
+ @latch = latch && FIELDS.each_with_index.to_h { |field, i| [field, latch[i]] }
87
+ end
88
+
89
+ # Sets whether the clock is stopped, the pulses counted towards the
90
+ # next tenth and the cycles until it, placing the divider so the tenth
91
+ # lands then.
92
+ def restore_divider(stopped, pulses, cycles_to_tenth)
93
+ @stopped = stopped
94
+ @pulses = pulses
95
+ to_pulse = cycles_to_tenth - ([@match - pulses, 0].max * @cycles_per_pulse / @mains_hz)
96
+ @accumulator = (@cycles_per_pulse - (to_pulse * @mains_hz)).clamp(0, @cycles_per_pulse - 1)
97
+ end
98
+
99
+ # The clock and alarm as stored, read without latching the clock.
100
+ def registers
101
+ [@clock[:tenths], @clock[:seconds], @clock[:minutes], @clock[:hours],
102
+ @alarm[:tenths], @alarm[:seconds], @alarm[:minutes], @alarm[:hours],
103
+ @stopped ? 1 : 0, @latch ? 1 : 0]
104
+ end
105
+
106
+ def write(field, value, alarm:)
107
+ # The frequency counter is held clear while the clock is stopped, and
108
+ # starts counting again from the write to tenths that restarts it.
109
+ if field == :tenths && !alarm && @stopped
110
+ @pulses = 0
111
+ @stopped = false
112
+ end
113
+ store(field, value & (field == :tenths ? 0x0f : 0x7f), alarm: alarm)
114
+ end
115
+
116
+ # 12-hour BCD value, bit 7 is the AM/PM flag.
117
+ def write_hours(value, alarm:)
118
+ value &= 0x9f
119
+ # Writing 12 to the hour register flips the AM/PM bit, the same way a
120
+ # carry into 12 does. Writing the alarm hours does not.
121
+ value ^= 0x80 if !alarm && (value & 0x1f) == 0x12
122
+ # Halt the clock when writing hours, so that it doesn't
123
+ # advance mid-update.
124
+ @stopped = true unless alarm
125
+ store(:hours, value, alarm: alarm)
126
+ end
127
+
128
+ private
129
+
130
+ def store(field, value, alarm:)
131
+ target = alarm ? @alarm : @clock
132
+ return if target[field] == value
133
+
134
+ target[field] = value
135
+ @alarm_pending = true if alarm?
136
+ end
137
+
138
+ def pulse!
139
+ @accumulator -= @cycles_per_pulse
140
+ return if @stopped
141
+
142
+ # A three-bit ring counter with six states, compared against the
143
+ # divider on each pin pulse rather than on its way past it.
144
+ if @pulses == @match
145
+ @pulses = 0
146
+ advance
147
+ else
148
+ @pulses += 1
149
+ @pulses = 0 if @pulses > 5
150
+ end
151
+ end
152
+
153
+ def advance
154
+ tenths = (@clock[:tenths] + 1) & 0x0f
155
+ @clock[:tenths] = tenths == 10 ? 0 : tenths
156
+ carry_seconds if tenths == 10
157
+ @alarm_pending = true if alarm?
158
+ end
159
+
160
+ def carry_seconds
161
+ return unless advance_pair(:seconds)
162
+ return unless advance_pair(:minutes)
163
+
164
+ advance_hours
165
+ end
166
+
167
+ # Seconds and minutes hold a four-bit low digit that carries at ten into
168
+ # a three-bit high digit that carries out at six. Both are plain binary
169
+ # counters, so out-of-range BCD counts up from where it was written.
170
+ def advance_pair(field)
171
+ low = ((@clock[field] & 0x0f) + 1) & 0x0f
172
+ high = (@clock[field] >> 4) & 0x07
173
+ carry = false
174
+
175
+ if low == 10
176
+ low = 0
177
+ high = (high + 1) & 0x07
178
+ if high == 6
179
+ high = 0
180
+ carry = true
181
+ end
182
+ end
183
+
184
+ @clock[field] = (high << 4) | low
185
+ carry
186
+ end
187
+
188
+ # The hour runs 9 -> 10 and 12 -> 1 by swapping its two digits, and the
189
+ # AM/PM bit flips as it passes through 12.
190
+ def advance_hours
191
+ pm = @clock[:hours] & 0x80
192
+ high = (@clock[:hours] >> 4) & 0x01
193
+ low = @clock[:hours] & 0x0f
194
+
195
+ if (high == 1 && low == 2) || (high.zero? && low == 9)
196
+ low = high
197
+ high ^= 1
198
+ else
199
+ low = (low + 1) & 0x0f
200
+ pm ^= 0x80 if high == 1 && low == 2
201
+ end
202
+
203
+ @clock[:hours] = pm | (high << 4) | low
204
+ end
205
+
206
+ def alarm? = @clock == @alarm
207
+ end
208
+ end
209
+ end
210
+ end