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,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ module Drives
5
+ class Datasette
6
+ # The datasette's fields in a keyed snapshot module (Snapshot::Keyed,
7
+ # BL-TAPE): the keys, the motor, the countdown to the next pulse, and
8
+ # a tape in it under tape.*: its path, its bytes and how far it has
9
+ # played. A tape that isn't in leaves tape.* out. The sense and flag
10
+ # handlers don't fire on load.
11
+ module KeyedState
12
+ def save_fields(out)
13
+ out.bool("playing", @playing).bool("motor", @motor).int("countdown", @countdown)
14
+ tape = @tape
15
+ return out unless tape
16
+
17
+ out.text("tape.path", tape.path.to_s).bytes("tape.bytes", tape.bytes).int("tape.position", tape.position)
18
+ end
19
+
20
+ # The tape goes back in from the bytes the module holds, without its
21
+ # host file, unless the tape in is the same one.
22
+ def load_fields(input)
23
+ @playing = input.bool("playing", @playing)
24
+ @motor = input.bool("motor", @motor)
25
+ @countdown = input.int("countdown", @countdown)
26
+ bytes = input.bytes("tape.bytes", "".b).bytes
27
+ return @tape = nil if bytes.empty?
28
+
29
+ path = input.text("tape.path", "")
30
+ @tape = Storage::TAP.new(path, bytes:) unless @tape&.path.to_s == path && @tape&.bytes == bytes
31
+ @tape&.position = input.int("tape.position", 0)
32
+ end
33
+ end
34
+
35
+ include KeyedState
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,173 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "badline/drives/datasette/keyed_state"
4
+
5
+ module Badline
6
+ module Drives
7
+ # The 1530 datasette. Plays a pulse stream into CIA 1's FLAG pin while the
8
+ # motor runs with a key pressed, and reports the keys through the cassette
9
+ # sense line on the $01 port. With RECORD down too, it records the
10
+ # machine's write line onto the tape instead. Built with +level: true+,
11
+ # it also times the read line's level, for a machine that reads that
12
+ # (#read_high?) rather than the falling edges.
13
+ class Datasette
14
+ # What inserting a tape raises on a machine without a cassette port.
15
+ class Missing < ArgumentError; end
16
+ attr_reader :tape
17
+ attr_writer :motor
18
+
19
+ def initialize(tape = nil, level: false)
20
+ @tape = tape
21
+ @level = level
22
+ @connected = true
23
+ @playing = false
24
+ @recording = false
25
+ @write_high = true
26
+ @since = 0
27
+ @motor = false
28
+ @countdown = 0
29
+ @rise_at = 0
30
+ @flag_handler = nil
31
+ @sense_handler = nil
32
+ end
33
+
34
+ # Falling edges on the tape read line.
35
+ def on_flag(&handler)
36
+ @flag_handler = handler
37
+ end
38
+
39
+ # The cassette sense line, which the $01 port reads back.
40
+ def on_sense_change(&handler)
41
+ @sense_handler = handler
42
+ end
43
+
44
+ # Whether the machine has a datasette at its cassette port. The SX-64
45
+ # has none: its keys stay up and it takes no tape.
46
+ def connected? = @connected
47
+
48
+ def disconnect!
49
+ eject
50
+ @connected = false
51
+ end
52
+
53
+ def insert(tape)
54
+ raise Missing, "this machine has no datasette" unless @connected
55
+
56
+ @tape = tape
57
+ rewind
58
+ end
59
+
60
+ def eject
61
+ stop!
62
+ @tape = nil
63
+ end
64
+
65
+ def rewind
66
+ @tape&.rewind
67
+ @countdown = 0
68
+ @rise_at = 0
69
+ end
70
+
71
+ def play! = press(true)
72
+ def stop! = press(false)
73
+
74
+ # Presses RECORD and PLAY together.
75
+ def record!
76
+ return if !@connected || @tape.nil?
77
+
78
+ press(true)
79
+ @recording = true
80
+ @since = 0
81
+ end
82
+
83
+ def recording? = @recording
84
+
85
+ # The machine's tape write line. While the deck records with its motor
86
+ # running, each rising edge ends a pulse on the tape, as long as the
87
+ # time since the edge before, and plays back as a falling edge on the
88
+ # read line.
89
+ def write_line=(high)
90
+ rose = high && !@write_high
91
+ @write_high = high
92
+ return unless rose && @recording && running?
93
+
94
+ @tape.record_pulse(@since)
95
+ @since = 0
96
+ end
97
+
98
+ # Sets the write line's level without recording an edge, as a
99
+ # snapshot loads it.
100
+ def write_level=(high)
101
+ @write_high = high
102
+ end
103
+
104
+ def playing? = @playing
105
+
106
+ # Sense reads low while a key is down on the deck.
107
+ def sense_low? = @playing
108
+
109
+ def motor? = @motor
110
+
111
+ def running? = @playing && @motor && !@tape.nil?
112
+
113
+ # The tape read line's level, on a datasette built with +level: true+.
114
+ # Each pulse holds it low for its first half and high for its second,
115
+ # up to the falling edge that ends it. A version 2 tape times each
116
+ # half, and a version 0 or 1 tape's pulse plays as two equal halves.
117
+ # It reads high while the tape isn't playing.
118
+ def read_high?
119
+ return true unless running? && !@recording
120
+
121
+ @countdown.positive? && @countdown <= @rise_at
122
+ end
123
+
124
+ def cycle!
125
+ return unless running?
126
+ return @since += 1 if @recording
127
+
128
+ next_pulse if @countdown.zero?
129
+ return if @countdown.zero?
130
+
131
+ @countdown -= 1
132
+ @flag_handler&.call if @countdown.zero?
133
+ end
134
+
135
+ def inspect
136
+ "#<#{self.class.name} tape=#{@tape ? @tape.class.name : 'none'} " \
137
+ "playing=#{@playing} motor=#{@motor}>"
138
+ end
139
+
140
+ private
141
+
142
+ def next_pulse
143
+ return @countdown = @tape.next_pulse.to_i unless @level
144
+ return next_half_waves if @tape.half_waves?
145
+
146
+ @countdown = @tape.next_pulse.to_i
147
+ @rise_at = @countdown / 2
148
+ end
149
+
150
+ def next_half_waves
151
+ first = @tape.next_half_wave.to_i
152
+ @rise_at = @tape.next_half_wave.to_i
153
+ @countdown = first + @rise_at
154
+ end
155
+
156
+ def press(down)
157
+ return if down == @playing || !@connected
158
+
159
+ finish_recording unless down
160
+ @playing = down
161
+ @sense_handler&.call
162
+ end
163
+
164
+ # Releasing the keys ends a recording, and writes the tape out.
165
+ def finish_recording
166
+ return unless @recording
167
+
168
+ @recording = false
169
+ @tape.save
170
+ end
171
+ end
172
+ end
173
+ end
@@ -0,0 +1,152 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ module Drives
5
+ module Engine
6
+ # What every Commodore disk drive here shares: a 6502 running the DOS
7
+ # ROM, with chips on its bus facing the serial bus and the disk
8
+ # mechanism, on a clock of its own.
9
+ #
10
+ # The drive runs on its own crystal, so it clocks against the host's
11
+ # clock through a fractional accumulator: each host cycle adds the
12
+ # drive's clock rate worth of phase, and every whole host period in it
13
+ # runs a drive cycle. Against the PAL C64's 985,248 Hz a drive at
14
+ # 1 MHz runs one drive cycle per host cycle and a second one about
15
+ # every 67. The host sets its clock on attaching the drive, and until
16
+ # then a 1 MHz drive runs one cycle per host cycle.
17
+ #
18
+ # The Mechanism reads a Disk put in with insert.
19
+ #
20
+ # While the DOS idles, host_cycle! skips the drive's cycles and catches
21
+ # up on them later, exactly (see Idle). The readers of the drive's parts
22
+ # catch up first, so they find the drive as running every cycle would
23
+ # have left it. Once the DOS's timer interrupts come round, the drive
24
+ # sleeps through them too (see Orbit).
25
+ #
26
+ # A model includes Core and builds its parts, sets @clock_hz and
27
+ # @idle_loop, and runs one drive cycle in step. It hears ATN on
28
+ # connecting (hear_atn), drives the serial bus (serial_output), and
29
+ # answers Idle's and Orbit's questions about its chips, as VIAs does
30
+ # for the 1541 and the 1571.
31
+ module Core
32
+ include Sleep
33
+ include Idle
34
+ include Orbit
35
+
36
+ attr_reader :device, :serial_bus
37
+
38
+ def cpu
39
+ settle!
40
+ @cpu
41
+ end
42
+
43
+ def bus
44
+ settle!
45
+ @bus
46
+ end
47
+
48
+ def mechanism
49
+ settle!
50
+ @mechanism
51
+ end
52
+
53
+ def cycles
54
+ settle!
55
+ @cycles
56
+ end
57
+
58
+ def ram = bus.ram
59
+
60
+ def host_clock_hz=(clock_hz)
61
+ settle!
62
+ @host_clock_hz = clock_hz
63
+ end
64
+
65
+ # Plugs the drive into a serial bus, leaving the one it was on. A drive
66
+ # starts out on a bus of its own, with nothing else on it.
67
+ def connect(serial_bus)
68
+ settle!
69
+ @serial_bus&.detach(self)
70
+ @serial_bus = serial_bus
71
+ @serial_port.bus = serial_bus
72
+ serial_bus.attach(self)
73
+ hear_atn
74
+ end
75
+
76
+ # Puts a Disk in the drive (Disk.from_d64 makes one from an image).
77
+ # Nil takes the disk out.
78
+ def insert(disk)
79
+ settle!
80
+ @mechanism.insert(disk)
81
+ end
82
+
83
+ def disk = mechanism.disk
84
+
85
+ # Whether the LED is lit. Reading it leaves the drive asleep: the LED
86
+ # is the same at the end of every pass the drive sleeps through.
87
+ def led_on? = @mechanism.led_on?
88
+
89
+ # The power LED, which stays lit.
90
+ def power_led_on? = true
91
+
92
+ # Stores what the head wrote since the motor last stopped in the
93
+ # disk's image, as the motor stopping does.
94
+ def flush
95
+ settle!
96
+ @mechanism.flush
97
+ end
98
+
99
+ # Runs the drive cycles that fall in one host cycle: none, one or two.
100
+ # The host has run this cycle already, and the drive sees what it did
101
+ # from the next one on (see SerialPort). Asleep, the drive owes the
102
+ # cycles instead, until ATN moves or its counters are due (see Idle).
103
+ # It looks at ATN only after the machine pushed its lines into the bus
104
+ # (see host_written!).
105
+ def host_cycle!
106
+ if @asleep
107
+ return if (@slept += 1) != @wake_at && @host_still
108
+
109
+ return doze
110
+ end
111
+
112
+ phase = @phase + @clock_hz
113
+ while phase >= @host_clock_hz
114
+ @asleep ? @owed += 1 : run_cycle
115
+ phase -= @host_clock_hz
116
+ end
117
+ @phase = phase
118
+ settle! if @serial_port.atn_moved?
119
+ @serial_port.latch_host
120
+ plan_wake if @asleep
121
+ end
122
+
123
+ # The machine pushed its lines into the bus, so ATN may have moved.
124
+ def host_written!
125
+ @host_still = false
126
+ end
127
+
128
+ # The host moved its fast serial pins, which a 1541 has none to hear.
129
+ def fast_lines_moved = nil
130
+
131
+ # Runs one drive cycle, catching up first on any the drive owes (see
132
+ # step).
133
+ def cycle!
134
+ settle!
135
+ step
136
+ end
137
+
138
+ def inspect
139
+ "#<#{self.class.name} cycles=#{cycles} cpu=(#{cpu.inspect})>"
140
+ end
141
+
142
+ private
143
+
144
+ # A drive cycle from host_cycle!, which may find the idle loop.
145
+ def run_cycle
146
+ step
147
+ idle_loop_reached if @cpu.program_counter == @idle_loop && @idle_skip
148
+ end
149
+ end
150
+ end
151
+ end
152
+ end
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ module Drives
5
+ module Engine
6
+ # Fast serial on a drive with a CIA, the 1571's 6526 or the 1581's
7
+ # 8520: through buffers a port line turns round, the CIA's CNT drives
8
+ # SRQ and its SP drives DATA, or it hears them. The drive pushes its
9
+ # pins into the bus as they move, and hears the host's as the host
10
+ # pushes them, pin by pin, so each byte goes through both shift
11
+ # registers bit by bit. The CIA takes no part in a pass of the idle
12
+ # loop, and any access to it, or the host moving its fast serial pins,
13
+ # wakes the drive.
14
+ #
15
+ # A model includes it beside Core, and calls turn_fast_serial when its
16
+ # direction line moves and drive_fast_serial each cycle the buffers
17
+ # face out. Its serial_output carries @fast_output above its port.
18
+ module FastSerial
19
+ # Whether the buffers face out, the drive's CIA driving DATA and SRQ.
20
+ attr_reader :fast_serial_out
21
+
22
+ # The host moved its fast serial pins: the CIA hears SRQ on CNT and
23
+ # DATA on SP while the buffers face in.
24
+ def fast_lines_moved
25
+ settle!
26
+ hear_fast_serial unless @fast_serial_out
27
+ end
28
+
29
+ private
30
+
31
+ def init_fast_serial
32
+ @fast_serial_out = false
33
+ @fast_output = 0
34
+ end
35
+
36
+ # The buffers turned round: outwards the CIA's pins drive the lines,
37
+ # and inwards they let go and hear them.
38
+ def turn_fast_serial(out)
39
+ @fast_serial_out = out
40
+ return unless @serial_bus
41
+
42
+ push_fast_output(out ? fast_pins : 0)
43
+ hear_fast_serial unless out
44
+ end
45
+
46
+ def drive_fast_serial
47
+ low = fast_pins
48
+ push_fast_output(low) if low != @fast_output
49
+ end
50
+
51
+ # The lines the CIA's CNT and SP pull while its serial port drives
52
+ # them.
53
+ def fast_pins
54
+ serial = @cia.serial
55
+ return 0 unless serial.output?
56
+
57
+ low = serial.cnt ? 0 : SerialBus::DRIVE_FAST_SRQ
58
+ serial.sp_out ? low : low | SerialBus::DRIVE_FAST_DATA
59
+ end
60
+
61
+ def push_fast_output(low)
62
+ @fast_output = low
63
+ @serial_bus&.drives_fast_moved!
64
+ end
65
+
66
+ def hear_fast_serial
67
+ low = @serial_bus.low_lines
68
+ serial = @cia.serial
69
+ serial.cnt_in = low.nobits?(SerialBus::SRQ)
70
+ serial.sp_in = low.nobits?(SerialBus::DATA)
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,127 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ module Drives
5
+ module Engine
6
+ # Skipping the drive's cycles while the DOS idles, exactly.
7
+ #
8
+ # With nothing to do, the DOS goes round its idle loop from the
9
+ # model's @idle_loop ($EBFF in the 1541, 446 cycles a pass), until VIA
10
+ # 2's timer 1 interrupts it every 14,850 cycles for the job loop, or
11
+ # ATN interrupts it through VIA 1's CA1. A pass writes RAM and VIA 2's
12
+ # port B, but puts back what it found, so the drive comes round to the
13
+ # loop's start as it left it. Only the timers' counters and the cycle
14
+ # counts move.
15
+ #
16
+ # Each time the CPU comes to the loop's start, the drive records the
17
+ # pass that follows. At the next it checks that the pass
18
+ #
19
+ # - left the CPU, both VIAs but for their counters, the bus's last
20
+ # byte, the SO pin, the mechanism (Mechanism#idle_state) and what
21
+ # the ports read from it, and every byte of RAM it read or wrote as
22
+ # it found them (Bus#watch!)
23
+ # - read nothing that changes from pass to pass: no counter, no shift
24
+ # register, and not VIA 1's port B, which reads the serial bus
25
+ # - wrote nothing that reaches outside the drive: not VIA 1, whose port
26
+ # B drives the serial bus, and not the motor on
27
+ # - ran while no counter could set a flag (VIA#quiet_cycles), with the
28
+ # motor off, no interrupt pulled and no trap on the CPU.
29
+ #
30
+ # Such a pass runs the same way again from where it ended, for as
31
+ # long as ATN holds still and no counter sets a flag, so the drive
32
+ # sleeps: host_cycle! only counts the host cycles that go by (see
33
+ # Sleep). It wakes
34
+ # when ATN moves, when a counter is due to set a flag, or when something
35
+ # outside reads or changes the drive (the readers in Core call
36
+ # settle!). Waking counts off the whole passes it owes in bulk,
37
+ # moving the counters on, and runs the rest cycle by cycle.
38
+ #
39
+ # The loop reads neither CLK nor DATA, so the drive sleeps through them
40
+ # moving and takes the C64's lines as they stand once it wakes. Its
41
+ # own lines hold still while it sleeps, so the C64 reads them without
42
+ # waking it (serial_output).
43
+ #
44
+ # A pass starts at the loop's start, where the CPU always comes from
45
+ # the loop's last instruction, a JMP, so what that leaves in the CPU's
46
+ # working registers is the same each time round.
47
+ #
48
+ # The timer's interrupts come round too, and once they do, the drive
49
+ # sleeps on through them (see Orbit).
50
+ module Idle
51
+ # Whether the drive may sleep through its idle loop. On by default,
52
+ # but for a drive whose CPU logs each instruction.
53
+ attr_reader :idle_skip
54
+
55
+ def idle_skip=(on)
56
+ settle!
57
+ @idle_skip = on
58
+ end
59
+
60
+ private
61
+
62
+ def init_idle(debug)
63
+ @idle_skip = !debug
64
+ @recording = false
65
+ @pass_cycles = 0
66
+ @pass_instructions = 0
67
+ init_sleep
68
+ init_orbits
69
+ end
70
+
71
+ # The CPU is at the loop's start. At an instruction boundary, the
72
+ # drive sleeps if the pass it recorded can repeat, and otherwise
73
+ # records the next.
74
+ def idle_loop_reached
75
+ return unless @cpu.boundary?
76
+
77
+ if @recording
78
+ stop_recording
79
+ return fall_asleep if repeatable_pass? && room_for_pass?
80
+ end
81
+ start_recording
82
+ end
83
+
84
+ def start_recording
85
+ return if busy_for_pass? || @cpu.trapped?
86
+
87
+ @recording = true
88
+ @record_state = idle_state
89
+ @record_cycles = @cycles
90
+ @record_instructions = @cpu.instructions
91
+ @record_quiet = quiet_cycles
92
+ @bus.watch!
93
+ end
94
+
95
+ def stop_recording
96
+ @recording = false
97
+ @bus.unwatch!
98
+ end
99
+
100
+ # Whether the pass just recorded can repeat.
101
+ def repeatable_pass?
102
+ !@bus.volatile && @cycles - @record_cycles <= @record_quiet && idle_state == @record_state &&
103
+ ram_as_found?
104
+ end
105
+
106
+ # Whether the counters leave room for a whole pass.
107
+ def room_for_pass? = quiet_cycles >= @cycles - @record_cycles
108
+
109
+ # Sleeps through the pass just recorded, over and over.
110
+ def fall_asleep
111
+ @pass_cycles = @cycles - @record_cycles
112
+ @pass_instructions = @cpu.instructions - @record_instructions
113
+ @asleep = true
114
+ @host_still = false
115
+ @owed = 0
116
+ @budget = quiet_cycles
117
+ anchor
118
+ end
119
+
120
+ def ram_as_found?
121
+ ram = @bus.ram
122
+ @bus.touched.all? { |addr, value| ram.peek(addr) == value }
123
+ end
124
+ end
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ module Drives
5
+ module Engine
6
+ # What Idle needs of a drive's 6502, which includes it beside
7
+ # CPU::Core.
8
+ module IdleCPU
9
+ # Everything the CPU holds but its cycle and instruction counts: the
10
+ # registers, the interrupt lines and pipeline, and what the last
11
+ # instruction left in its working registers. Drives::Engine::Idle compares
12
+ # it at two instruction boundaries.
13
+ def idle_state
14
+ [@program_counter, @stack_pointer, @a, @x, @y, @status.value,
15
+ @irq, @nmi, @irq_sample, @irq_pending, @nmi_sample, @nmi_pending, @skip_poll,
16
+ @boundary_irq, @boundary_nmi, @so_high, @interrupt, @brk, @pending_write, @stalled_at, @traps,
17
+ @operation, @optional_dummy, @boundary_crossed, @branch_taken,
18
+ @address, @pointer, @dummy_address, @value, @rmw_result]
19
+ end
20
+
21
+ # Counts +cycles+ cycles and +instructions+ instructions that ran
22
+ # without changing anything idle_state holds.
23
+ def fast_forward(cycles, instructions)
24
+ @cycles += cycles
25
+ @instructions += instructions
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ module Drives
5
+ module Engine
6
+ # Sleeping through the DOS's timer interrupts, exactly, once they come
7
+ # round.
8
+ #
9
+ # VIA 2's timer 1 interrupts the idle loop for the job loop, which
10
+ # finds no job, restarts the timer and returns into the pass it cut
11
+ # into, 14,998 cycles and more apart. The interrupts land at a
12
+ # different point in the pass each time, until, six of them on, the
13
+ # whole drive is back where it was: an orbit of 90,044 cycles.
14
+ #
15
+ # Each place the drive falls asleep (see Idle) is an anchor, and the
16
+ # drive keeps everything it holds at each one: RAM, the counters of
17
+ # the timers that are armed or that it read or wrote since, and the
18
+ # rest as Idle compares a pass. It keeps them only while it keeps to
19
+ # itself, with Bus#guard! checking that it doesn't read the serial
20
+ # bus, write VIA 1, turn the motor on or change the LED. ATN moving,
21
+ # or a reader settling the drive, forgets them.
22
+ #
23
+ # Coming to an anchor it kept before closes an orbit, which runs the
24
+ # same way again from here for as long as ATN holds still, so the
25
+ # drive sleeps on without waking for its counters. Waking counts off
26
+ # the whole orbits it owes in bulk, moving on only the counters it
27
+ # never touched, and runs the rest as Idle does.
28
+ module Orbit
29
+ # How many anchors the drive keeps while it looks for an orbit.
30
+ ANCHORS = 32
31
+
32
+ private
33
+
34
+ def init_orbits
35
+ @anchors = {}
36
+ @orbit_cycles = nil
37
+ @orbit_instructions = 0
38
+ @bus.guard!
39
+ end
40
+
41
+ # The drive falls asleep at an anchor. One it came to before closes
42
+ # an orbit: everything from there to here runs the same way again.
43
+ def anchor
44
+ forget_orbits if @bus.tainted || @anchors.size >= ANCHORS
45
+ key = anchor_state
46
+ if (seen = @anchors[key])
47
+ @orbit_cycles = @cycles - seen[0]
48
+ @orbit_instructions = @cpu.instructions - seen[1]
49
+ else
50
+ @anchors[key] = [@cycles, @cpu.instructions]
51
+ end
52
+ end
53
+
54
+ def forget_orbits
55
+ @anchors.clear
56
+ @orbit_cycles = nil
57
+ @bus.guard!
58
+ end
59
+
60
+ # Runs whole orbits, which bring the drive back to the anchor it
61
+ # sleeps at but for the counters it never touched. Returns the
62
+ # cycles.
63
+ def skip_orbits(orbits)
64
+ cycles = orbits * @orbit_cycles
65
+ touched = @bus.touched_timers
66
+ @cpu.fast_forward(cycles, orbits * @orbit_instructions)
67
+ skip_chip_orbits(cycles, touched)
68
+ @cycles += cycles
69
+ cycles
70
+ end
71
+
72
+ # Everything the drive holds at an anchor, but for the counters of
73
+ # the unarmed timers it hasn't touched since it was last tainted.
74
+ def anchor_state
75
+ touched = @bus.touched_timers
76
+ [*idle_state, *counter_state(touched), @bus.ram.snapshot]
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end