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,313 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "badline/drives/model1541/rotation"
4
+ require "badline/drives/model1541/byte_ready"
5
+
6
+ module Badline
7
+ module Drives
8
+ class Model1541
9
+ # The disk mechanism behind VIA 2: the spindle motor, the head on its
10
+ # stepper, the LED, the read and write electronics and the
11
+ # write-protect sensor.
12
+ #
13
+ # Port B drives it and reads it back:
14
+ #
15
+ # PB0-1 stepper phase (out) PB4 write protect (in, 1 = writable)
16
+ # PB2 motor on (out) PB5-6 bit rate, speed zone 0-3 (out)
17
+ # PB3 LED on (out) PB7 SYNC (in, 0 while in a SYNC mark)
18
+ #
19
+ # Port A reads the read shift register's low eight bits, which CA1
20
+ # latches on BYTE READY. In write mode it drives the write shift
21
+ # register, and the read shift register takes the bits written.
22
+ #
23
+ # The disk turns at 300 rpm under the head, and a read clock at the
24
+ # bit rate PB5-6 select clocks the bits off it (see Rotation).
25
+ #
26
+ # Ten 1 bits in a row are a SYNC mark while CB2 selects reading: PB7
27
+ # reads it, and it holds the bit counter at zero, so the first 0 bit
28
+ # after it starts a byte. Every eighth bit after that is BYTE READY,
29
+ # which pulls VIA 2's CA1 low until the next bit and reaches the CPU's
30
+ # SO pin (see byte_ready!).
31
+ #
32
+ # CB2 low selects write mode, where SYNC detection and the flux are
33
+ # off and the clock runs on at the selected rate. At each BYTE READY
34
+ # the write shift register loads what the VIA drives on port A, and
35
+ # the next eight clocks put that byte's bits on the track, most
36
+ # significant first, in place of what it held. While reading, the load
37
+ # takes the byte just read, so a switch to writing mid-byte writes the
38
+ # rest of it. A write lays its bits one to a cell from the cell under
39
+ # the head, so on a track written at the selected rate each bit fills
40
+ # the cell it passes over. A write to a half track without data gives
41
+ # it a blank track first, a turn long at the selected rate, and a
42
+ # write to a track written at another rate lays the whole track out
43
+ # again as a turn at the selected rate first (Track#relaid).
44
+ #
45
+ # PB4 reads the write-protect sensor: high with no disk in, or with
46
+ # the notch of a writable one open. With the disk write-protected the
47
+ # write gate stays shut: the bits and BYTE READY go on, and nothing
48
+ # reaches the disk. The DOS reads PB4 first and refuses to write, so
49
+ # only a program that writes without asking finds that out.
50
+ #
51
+ # The motor stopping flushes what was written to the disk's image
52
+ # (see Disk#flush), which is when the DOS is done with a job, and so
53
+ # does taking the disk out.
54
+ #
55
+ # The stepper moves the head a half track each time the phase steps
56
+ # by one, inwards for +1 and outwards for -1. Each half track has a
57
+ # phase of its own, from power-on the low two bits of its number, so
58
+ # the head sits on a whole track when phase 0 or 2 holds it. The
59
+ # stepper's coils are powered only while PB2 runs the motor, as VICE
60
+ # reads the 1541's schematic, so a phase set with the motor off moves
61
+ # nothing until the motor comes on and it pulls the head. Tracks run
62
+ # from half track 2 (track 1), where the head stops against the end of
63
+ # its rail, to Disk::MAX_HALF_TRACK. A step out against the stop slips:
64
+ # the head stays on track 1 and the phase that tried to pull it out
65
+ # becomes track 1's, so the DOS's bump leaves the head on track 1 in
66
+ # line with the phase it ends on, and its first step in reaches the
67
+ # half track after it.
68
+ #
69
+ # The 1571's stepper is lined up so that phase 0 holds track 1, where
70
+ # its track 0 sensor looks for the head (Mechanism.new's +slip+).
71
+ class Mechanism
72
+ include Rotation
73
+ include ByteReady
74
+
75
+ MOTOR = 0x04
76
+ LED = 0x08
77
+ WRITE_PROTECT = 0x10
78
+ SYNC = 0x80
79
+
80
+ MIN_HALF_TRACK = 2
81
+ # Where the head sits at power-on: track 18, the directory.
82
+ START_HALF_TRACK = 36
83
+
84
+ SYNC_BITS = 10
85
+
86
+ attr_reader :disk, :half_track, :zone
87
+
88
+ # +slip+ is the stepper's phase offset at power-on: 0, where each half
89
+ # track's phase is the low two bits of its number, or 2 for track 1
90
+ # on phase 0.
91
+ def initialize(slip: 0)
92
+ @via = nil
93
+ @so_pending = false
94
+ @disk = nil
95
+ @half_track = START_HALF_TRACK
96
+ @slip = slip
97
+ @motor = false
98
+ @led = false
99
+ @zone = 0
100
+ @clock = Track.cell(0)
101
+ @time = 0
102
+ @clock_at = @clock / 2
103
+ @clocks = 0
104
+ @shift = 0
105
+ @ones = 0
106
+ @bits = 0
107
+ @sync = false
108
+ @byte_ready = false
109
+ @byte_latched = false
110
+ @writing = false
111
+ @write_shift = 0
112
+ @write_gate = false
113
+ @protected = false
114
+ @side_base = 0
115
+ @cycle_ticks = CYCLE
116
+ load_track
117
+ end
118
+
119
+ # The drive's clock rate: the disk turns on by fewer ticks each
120
+ # cycle at 2 MHz.
121
+ def clock_hz=(clock_hz)
122
+ @cycle_ticks = CYCLE * CLOCK_HZ / clock_hz
123
+ end
124
+
125
+ # The head that reads, 0 or 1, on a drive with two (Disk::SIDE).
126
+ def side = @side_base.zero? ? 0 : 1
127
+
128
+ def side=(side)
129
+ side_base = side * Disk::SIDE
130
+ return if side_base == @side_base
131
+
132
+ @side_base = side_base
133
+ load_track
134
+ end
135
+
136
+ def motor_on? = @motor
137
+
138
+ def led_on? = @led
139
+
140
+ def sync? = @sync
141
+
142
+ def writing? = @writing
143
+
144
+ # What port B and CB2 can change with the motor off: the motor, the
145
+ # LED, the zone and the clock's rate, the stepper, write mode and
146
+ # where a write starts, and the track and cell under the head. The
147
+ # rest moves only while the motor turns, or as a disk goes in (see
148
+ # Drives::Engine::Idle).
149
+ def idle_state
150
+ [@motor, @led, @zone, @clock, @half_track, @slip, @side_base, @disk, @track, @index, @mask, @cell_end,
151
+ @time, @writing, @write_index, @sync, @byte_latched]
152
+ end
153
+
154
+ # Puts a Disk in, or takes it out with nil, flushing the disk that
155
+ # was in. The head goes on from the same point in the turn.
156
+ def insert(disk)
157
+ flush
158
+ @disk = disk
159
+ @protected = disk&.write_protected? || false
160
+ @write_gate = !disk.nil? && !@protected
161
+ load_track
162
+ end
163
+
164
+ # Stores what the head wrote in the disk's image.
165
+ def flush
166
+ @disk.flush if @disk&.written?
167
+ rescue Storage::WriteError => e
168
+ warn "1541: couldn't write the disk back: #{e.message}"
169
+ end
170
+
171
+ # VIA 2 drives CB2 to +high+: low selects write mode, which stops
172
+ # SYNC detection at once.
173
+ def cb2_written(high)
174
+ @write_index = nil if high || !@writing
175
+ @writing = !high
176
+ @sync = !@writing && @ones >= SYNC_BITS
177
+ end
178
+
179
+ # VIA 2 drives port B's lines to +lines+.
180
+ def port_b_written(lines)
181
+ motor = @motor
182
+ @motor = lines.anybits?(MOTOR)
183
+ flush if motor && !@motor
184
+ @led = lines.anybits?(LED)
185
+ zone = (lines >> 5) & 0x03
186
+ if zone != @zone
187
+ @zone = zone
188
+ @clock = Track.cell(zone)
189
+ @track_written = false
190
+ end
191
+ step(lines & 0x03) if @motor
192
+ end
193
+
194
+ # Port A is the read shift register.
195
+ def read_a(_lines) = @shift & 0xff
196
+
197
+ # SYNC low in a SYNC mark, and write protect low for a protected
198
+ # disk.
199
+ def read_b(_lines)
200
+ lines = @sync ? 0xff & ~SYNC : 0xff
201
+ @protected ? lines & ~WRITE_PROTECT : lines
202
+ end
203
+
204
+ private
205
+
206
+ def read_bit(one)
207
+ if @byte_ready
208
+ @byte_ready = false
209
+ byte_ready_ended!
210
+ end
211
+ @shift = ((@shift << 1) & 0x3fe) | (one ? 1 : 0)
212
+ if one
213
+ @ones += 1
214
+ @sync = true if @ones >= SYNC_BITS
215
+ else
216
+ @ones = 0
217
+ @sync = false
218
+ end
219
+ count_bit
220
+ end
221
+
222
+ # The write shift register's top bit goes onto the track through the
223
+ # write gate, and into the read shift register, as the head's own
224
+ # signal.
225
+ def write_bit(at)
226
+ if @byte_ready
227
+ @byte_ready = false
228
+ byte_ready_ended!
229
+ end
230
+ one = @write_shift.anybits?(0x80)
231
+ @write_shift = (@write_shift << 1) & 0xff
232
+ store_bit(one, at) if @write_gate
233
+ @shift = ((@shift << 1) & 0x3fe) | (one ? 1 : 0)
234
+ @ones = one ? @ones + 1 : 0
235
+ count_bit
236
+ end
237
+
238
+ # Each bit written goes in the cell after the last one's, from the
239
+ # cell under the head when the write started.
240
+ def store_bit(one, at)
241
+ track_written(at) unless @track_written
242
+ @write_index ||= (@index * 8) + 8 - @mask.bit_length
243
+ index = @write_index >> 3
244
+ mask = 0x80 >> (@write_index & 7)
245
+ if one
246
+ @bytes[index] |= mask
247
+ else
248
+ @bytes[index] &= ~mask
249
+ end
250
+ @write_index += 1
251
+ @write_index = 0 if @write_index == @length * 8
252
+ end
253
+
254
+ # The first write to the track since the head came to it or the rate
255
+ # changed: a half track without data gets a blank track, and a track
256
+ # written at another rate is laid out again at this one.
257
+ def track_written(at)
258
+ surface = self.surface
259
+ track = @disk.writable_track(surface, @zone)
260
+ @disk.write(surface, track.relaid(@zone)) unless track.written_at?(@zone)
261
+ load_track(at)
262
+ @disk.written(surface)
263
+ @track_written = true
264
+ end
265
+
266
+ # Every eighth bit outside a SYNC mark is BYTE READY, which loads the
267
+ # write shift register: from what the VIA drives on port A in write
268
+ # mode, and with the byte just read otherwise.
269
+ def count_bit
270
+ if @sync
271
+ @bits = 0
272
+ return
273
+ end
274
+ @bits += 1
275
+ return unless @bits == 8
276
+
277
+ @bits = 0
278
+ @write_shift = @writing ? @via.port_a_output : @shift & 0xff
279
+ @byte_ready = true
280
+ byte_ready!
281
+ end
282
+
283
+ # The stepper's rotor turns with the head, a phase to each half
284
+ # track, so the energized phase pulls the head to the neighbouring
285
+ # half track it belongs to. A phase two away pulls both ways, and the
286
+ # head stays. Against the stop at track 1 the head can't follow, and
287
+ # the phases slip round to where the head is.
288
+ def step(phase)
289
+ move = (phase - @half_track - @slip) & 0x03
290
+ if move == 1
291
+ seek(@half_track + 1)
292
+ elsif move == 3 && @half_track == MIN_HALF_TRACK
293
+ @slip = (phase - MIN_HALF_TRACK) & 0x03
294
+ elsif move == 3
295
+ seek(@half_track - 1)
296
+ end
297
+ end
298
+
299
+ # The half track under the head that reads, numbered on the disk
300
+ # (see Disk::SIDE).
301
+ def surface = @half_track + @side_base
302
+
303
+ def seek(half_track)
304
+ half_track = half_track.clamp(MIN_HALF_TRACK, Disk::MAX_HALF_TRACK)
305
+ return if half_track == @half_track
306
+
307
+ @half_track = half_track
308
+ load_track
309
+ end
310
+ end
311
+ end
312
+ end
313
+ end
@@ -0,0 +1,123 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ module Drives
5
+ class Model1541
6
+ # The disk turning under the Mechanism's head, and its read clock.
7
+ #
8
+ # The disk turns at 300 rpm, whatever the bit rate: once in 200 ms, or
9
+ # 200,000 drive cycles. Each track's bits pass under the head at the
10
+ # rate they were written at (see Track), and a 1 bit is a flux
11
+ # transition at the start of its cell.
12
+ #
13
+ # The read circuit clocks bits out at the rate PB5-6 select, from a
14
+ # counter the 16 MHz crystal steps every 16 - zone ticks. A flux
15
+ # transition clears the counter. The counter clocks a bit into the
16
+ # read shift register as it reaches 2 and every four steps after
17
+ # that, and the bit is a 1 only for the first clock after the counter
18
+ # passed 0. A bit is 4 * (16 - zone) ticks, so a byte takes 32, 30, 28
19
+ # or 26 drive cycles in zones 0 to 3, and each transition brings the
20
+ # clock back into step with the flux. A track read at the rate it was
21
+ # written reads its bits. At another rate the clock and the cells
22
+ # drift apart between transitions: a faster clock slips an extra 0
23
+ # bit into some runs of 0 bits, a slower one drops one from some, and
24
+ # the bytes come out garbled. Without flux the counter runs round on
25
+ # its own and reads a 1 bit in every four.
26
+ module Rotation
27
+ CYCLE = 16 * Track::TICK
28
+
29
+ # What the head reads with no disk in, or off the tracks a disk has:
30
+ # no flux.
31
+ BLANK = Track.new(Array.new(Disk::TRACK_LENGTHS[0], 0).freeze, 0)
32
+
33
+ # One drive cycle: nothing with the motor off, and otherwise the
34
+ # disk turns on by a cycle's ticks, 16 at 1 MHz and 8 at 2 MHz,
35
+ # through whatever cells and clocks fall in them.
36
+ def cycle!
37
+ return unless @motor
38
+
39
+ @time += @cycle_ticks
40
+ pass_time if @time >= @next
41
+ end
42
+
43
+ private
44
+
45
+ # The cell edges and clocks up to now, in the order they come. A
46
+ # clock that falls on a cell edge comes after it.
47
+ def pass_time
48
+ while @cell_end <= @time || @clock_at <= @time
49
+ if @cell_end <= @clock_at
50
+ next_cell
51
+ else
52
+ clock
53
+ end
54
+ end
55
+ @next = [@cell_end, @clock_at].min
56
+ end
57
+
58
+ # The head comes to the next cell, and a 1 bit's flux clears the
59
+ # read clock while reading.
60
+ def next_cell
61
+ start = @cell_end
62
+ @mask >>= 1
63
+ start = next_byte(start) if @mask.zero?
64
+ @cell_end = @index == @last && @mask == 1 ? Track::TURN : start + @width
65
+ return if @writing || @bytes[@index].nobits?(@mask)
66
+
67
+ @clock_at = start + (@clock / 2)
68
+ @clocks = 0
69
+ end
70
+
71
+ # The head comes to the next byte, and at the end of the track back
72
+ # round to the first, where the turn starts over.
73
+ def next_byte(start)
74
+ @mask = 0x80
75
+ @index += 1
76
+ if @index == @length
77
+ @index = 0
78
+ @time -= Track::TURN
79
+ @clock_at -= Track::TURN
80
+ start = 0
81
+ end
82
+ @width = @widths[@index] if @widths
83
+ start
84
+ end
85
+
86
+ # The read clock ticks a bit into the shift register: a 1 the first
87
+ # time after a flux transition or the counter's wrap.
88
+ def clock
89
+ at = @clock_at
90
+ one = @clocks.zero?
91
+ @clocks = (@clocks + 1) & 3
92
+ @clock_at = at + @clock
93
+ @writing ? write_bit(at) : read_bit(one)
94
+ end
95
+
96
+ # The track under the head and its layout, leaving the head's place
97
+ # on it as it stands.
98
+ def head_on_track
99
+ @track = @disk&.track(surface) || BLANK
100
+ @bytes = @track.bytes
101
+ @widths = @track.widths
102
+ @length = @track.length
103
+ @last = @length - 1
104
+ end
105
+
106
+ # The track under the head, from the cell that's under it +time+
107
+ # into the turn.
108
+ def load_track(time = @time)
109
+ @track = @disk&.track(surface) || BLANK
110
+ @bytes = @track.bytes
111
+ @widths = @track.widths
112
+ @length = @track.length
113
+ @last = @length - 1
114
+ @index, @mask, @cell_end = @track.cell_at(time)
115
+ @width = @widths ? @widths[@index] : @track.width
116
+ @write_index = nil
117
+ @track_written = false
118
+ @next = [@cell_end, @clock_at].min
119
+ end
120
+ end
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ module Drives
5
+ class Model1541
6
+ # Reads a track's blocks back out of its GCR, as the DOS finds them:
7
+ # each SYNC mark is ten or more 1 bits, and the block after it starts
8
+ # at the first 0 bit. Writes land wherever the head was, so a block
9
+ # can start at any bit of the stored bytes, and the reader works on
10
+ # the bits.
11
+ #
12
+ # A header block starts with $08, and the next block after a header is
13
+ # that sector's data block, whatever it holds.
14
+ module SectorReader
15
+ HEADER_BITS = 10 * 8
16
+ DATA_BITS = 325 * 8
17
+ SYNC = /1{10,}/
18
+
19
+ # A sector as read: its decoded header, [ID, checksum, sector, track,
20
+ # the second ID byte, the first, ...], and its decoded data block,
21
+ # [ID, 256 data bytes, checksum, ...], nil when there isn't one that
22
+ # decodes.
23
+ Sector = Struct.new(:header, :data)
24
+
25
+ module_function
26
+
27
+ # The sectors found on the track's bytes, by sector number. Headers
28
+ # for another track are left out.
29
+ def read(bytes, track)
30
+ ring = ring(bytes)
31
+ return {} unless ring
32
+
33
+ sectors = {}
34
+ each_block(ring) do |header, data|
35
+ next unless header[3] == track
36
+
37
+ sectors[header[2]] ||= Sector.new(header, data)
38
+ end
39
+ sectors
40
+ end
41
+
42
+ # The track's bits, starting at a 0 bit so no SYNC mark straddles the
43
+ # start, twice over so a block can run on past the end. Nil for a
44
+ # track without a 0 bit.
45
+ def ring(bytes)
46
+ bits = bytes.pack("C*").unpack1("B*")
47
+ zero = bits.index("0")
48
+ return unless zero
49
+
50
+ bits = bits[zero..] + bits[0, zero]
51
+ [bits * 2, bits.length]
52
+ end
53
+
54
+ # Yields each header whose SYNC mark starts in the first turn, with
55
+ # the data block after it.
56
+ def each_block(ring)
57
+ bits, length = ring
58
+ pos = 0
59
+ while (sync = SYNC.match(bits, pos)) && sync.begin(0) < length
60
+ pos = sync.end(0)
61
+ header = decode(bits, pos, HEADER_BITS)
62
+ next unless header && header[0] == Disk::HEADER_ID
63
+
64
+ data_sync = SYNC.match(bits, pos + HEADER_BITS)
65
+ yield header, data_sync && decode(bits, data_sync.end(0), DATA_BITS)
66
+ end
67
+ end
68
+
69
+ def decode(bits, pos, count)
70
+ slice = bits[pos, count]
71
+ return unless slice.length == count
72
+
73
+ GCR.decode([slice].pack("B*").bytes)
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ module Drives
5
+ class Model1541
6
+ # What VIA 1's ports read from outside the chip. Port B carries the
7
+ # serial bus inputs and the device number jumpers (Drives::Engine::SerialLines);
8
+ # port A goes to the unused parallel header and floats high.
9
+ class SerialPort
10
+ include Engine::SerialLines
11
+
12
+ def initialize(device: 8, bus: SerialBus.new)
13
+ init_serial_lines(device, bus)
14
+ end
15
+
16
+ def read_a(_lines) = 0xff
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,100 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ module Drives
5
+ class Model1541
6
+ # One ring of flux on the disk, as the GCR bytes the head reads from it,
7
+ # most significant bit first, and the speed zone it was written in
8
+ # (0-3). The bytes wrap around: the last one's bits run into the
9
+ # first's. A track is as long as its bytes, which is what a .g64 image
10
+ # stores for each half track.
11
+ #
12
+ # The disk turns at 300 rpm, so every track passes under the head in
13
+ # the same 200 ms, and its bits pass at the rate they were written at.
14
+ # Each bit is a cell of time, starting at the track's index angle with
15
+ # the first byte's top bit, and a 1 bit is a flux transition at the
16
+ # start of its cell. The cells are spread evenly over the turn, so a
17
+ # track as long as a turn holds at its zone's rate
18
+ # (Disk::TRACK_LENGTHS) passes at that rate, within the part of a byte
19
+ # the length rounds off, and a longer or shorter one faster or slower.
20
+ # A .g64 speed map gives each byte the zone it was written in, and the
21
+ # cells of each byte are as wide as their zone's, scaled together to
22
+ # fill the turn.
23
+ #
24
+ # Times are in TICKs, 1/65536 of a tick of the drive's 16 MHz crystal,
25
+ # so the cells add up to a turn within a tick.
26
+ class Track
27
+ TICK = 1 << 16
28
+ TURN = 200_000 * 16 * TICK
29
+
30
+ # A bit cell at the zone's rate, in TICKs.
31
+ def self.cell(zone) = 4 * (16 - zone) * TICK
32
+
33
+ attr_reader :bytes, :zone, :speeds, :width, :widths
34
+
35
+ # +speeds+ holds each byte's zone, where the track's rate changes
36
+ # along it.
37
+ def initialize(bytes, zone, speeds = nil)
38
+ @bytes = bytes
39
+ @zone = zone
40
+ @speeds = speeds
41
+ @widths = speeds && scaled_widths(speeds)
42
+ @width = speeds ? @widths[0] : TURN / (bytes.length * 8)
43
+ end
44
+
45
+ def length = @bytes.length
46
+
47
+ # Whether the whole track was written at the +zone+'s rate.
48
+ def written_at?(zone) = @widths.nil? && @zone == zone
49
+
50
+ # The bit cell under the head +time+ TICKs into the turn, as
51
+ # [byte, mask, the time the cell ends].
52
+ def cell_at(time)
53
+ start = 0
54
+ index = 0
55
+ if @widths
56
+ while index < length - 1 && time >= start + (@widths[index] * 8)
57
+ start += @widths[index] * 8
58
+ index += 1
59
+ end
60
+ else
61
+ index = [time / (@width * 8), length - 1].min
62
+ start = index * @width * 8
63
+ end
64
+ width = @widths ? @widths[index] : @width
65
+ bit = [(time - start) / width, 7].min
66
+ cell_end = index == length - 1 && bit == 7 ? TURN : start + ((bit + 1) * width)
67
+ [index, 0x80 >> bit, cell_end]
68
+ end
69
+
70
+ # The track laid out again as one turn at the +zone+'s rate, each
71
+ # flux transition in the cell under it.
72
+ def relaid(zone)
73
+ bits = Disk::TRACK_LENGTHS[zone] * 8
74
+ cell = TURN / bits
75
+ relaid = Array.new(bits / 8, 0)
76
+ time = 0
77
+ @bytes.each_with_index do |byte, index|
78
+ width = @widths ? @widths[index] : @width
79
+ 8.times do |bit|
80
+ if byte.anybits?(0x80 >> bit)
81
+ cell_index = [time / cell, bits - 1].min
82
+ relaid[cell_index >> 3] |= 0x80 >> (cell_index & 7)
83
+ end
84
+ time += width
85
+ end
86
+ end
87
+ Track.new(relaid, zone)
88
+ end
89
+
90
+ private
91
+
92
+ def scaled_widths(speeds)
93
+ units = 0
94
+ speeds.each { |zone| units += 16 - zone }
95
+ speeds.map { |zone| TURN * (16 - zone) / (units * 8) }
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end