badline 0.12.0 → 0.13.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 (516) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +25 -0
  3. data/CLAUDE.md +59 -31
  4. data/README.md +1 -1
  5. data/doc/library.md +9 -9
  6. data/doc/pinned-behaviour.md +105 -105
  7. data/doc/snapshots.md +1 -4
  8. data/lib/badline/audio/bare_player.rb +4 -4
  9. data/lib/badline/audio/cli.rb +6 -6
  10. data/lib/badline/audio/jukebox.rb +2 -2
  11. data/lib/badline/audio/machine_player.rb +2 -2
  12. data/lib/badline/{media → audio}/queue.rb +1 -1
  13. data/lib/badline/audio/queued_tune.rb +6 -6
  14. data/lib/badline/audio/renderer.rb +5 -5
  15. data/lib/badline/audio/stereo.rb +1 -1
  16. data/lib/badline/c128/{bus_state.rb → bus/saved_state.rb} +6 -6
  17. data/lib/badline/c128/bus.rb +30 -51
  18. data/lib/badline/c128/color_lines.rb +2 -2
  19. data/lib/badline/c128/cpu.rb +1 -1
  20. data/lib/badline/c128/drives.rb +10 -11
  21. data/lib/badline/c128/fast_serial.rb +5 -5
  22. data/lib/badline/c128/mmu_pages.rb +1 -1
  23. data/lib/badline/c128/modes.rb +2 -2
  24. data/lib/badline/c128/saved_state.rb +21 -47
  25. data/lib/badline/c128/z80.rb +2 -2
  26. data/lib/badline/c128.rb +18 -72
  27. data/lib/badline/{computer → c64}/attachments.rb +16 -47
  28. data/lib/badline/c64/banked_ram/banking.rb +32 -0
  29. data/lib/badline/c64/banked_ram/plus256k.rb +56 -0
  30. data/lib/badline/c64/banked_ram/plus60k.rb +45 -0
  31. data/lib/badline/c64/banked_ram/unexpanded.rb +30 -0
  32. data/lib/badline/c64/banked_ram.rb +23 -0
  33. data/lib/badline/c64/bus/extra_sids.rb +32 -0
  34. data/lib/badline/c64/bus/fittings.rb +40 -0
  35. data/lib/badline/c64/bus/pla.rb +149 -0
  36. data/lib/badline/c64/bus/roms.rb +41 -0
  37. data/lib/badline/c64/bus/saved_state.rb +68 -0
  38. data/lib/badline/c64/bus/sid_slots.rb +44 -0
  39. data/lib/badline/c64/bus.rb +218 -0
  40. data/lib/badline/c64/cartridge/action_replay.rb +128 -0
  41. data/lib/badline/c64/cartridge/atomic_power.rb +26 -0
  42. data/lib/badline/c64/cartridge/bank.rb +83 -0
  43. data/lib/badline/c64/cartridge/comal80.rb +52 -0
  44. data/lib/badline/c64/cartridge/dinamic.rb +36 -0
  45. data/lib/badline/c64/cartridge/easy_flash.rb +147 -0
  46. data/lib/badline/c64/cartridge/epyx_fastload.rb +81 -0
  47. data/lib/badline/c64/cartridge/final_cartridge3.rb +91 -0
  48. data/lib/badline/c64/cartridge/flash/saved_state.rb +40 -0
  49. data/lib/badline/c64/cartridge/flash.rb +279 -0
  50. data/lib/badline/c64/cartridge/freezer.rb +35 -0
  51. data/lib/badline/c64/cartridge/fun_play.rb +40 -0
  52. data/lib/badline/c64/cartridge/g_mod2.rb +76 -0
  53. data/lib/badline/c64/cartridge/game_system.rb +42 -0
  54. data/lib/badline/c64/cartridge/geo_ram.rb +81 -0
  55. data/lib/badline/c64/cartridge/kcs_power.rb +77 -0
  56. data/lib/badline/c64/cartridge/mach5.rb +34 -0
  57. data/lib/badline/c64/cartridge/magic_desk.rb +32 -0
  58. data/lib/badline/c64/cartridge/ocean.rb +37 -0
  59. data/lib/badline/c64/cartridge/pagefox.rb +70 -0
  60. data/lib/badline/c64/cartridge/retro_replay/saved_state.rb +47 -0
  61. data/lib/badline/c64/cartridge/retro_replay.rb +286 -0
  62. data/lib/badline/c64/cartridge/rex_utility.rb +31 -0
  63. data/lib/badline/c64/cartridge/rgcd.rb +60 -0
  64. data/lib/badline/c64/cartridge/saved_state.rb +63 -0
  65. data/lib/badline/c64/cartridge/simons_basic.rb +37 -0
  66. data/lib/badline/c64/cartridge/standard.rb +17 -0
  67. data/lib/badline/c64/cartridge/super_games.rb +50 -0
  68. data/lib/badline/c64/cartridge/westermann.rb +32 -0
  69. data/lib/badline/c64/cartridge/zaxxon.rb +68 -0
  70. data/lib/badline/c64/cartridge.rb +258 -0
  71. data/lib/badline/c64/color_memory.rb +27 -0
  72. data/lib/badline/c64/common.rb +34 -0
  73. data/lib/badline/c64/kernal_traps.rb +89 -0
  74. data/lib/badline/c64/reu/dma/saved_state.rb +52 -0
  75. data/lib/badline/c64/reu/dma.rb +275 -0
  76. data/lib/badline/c64/reu/ram.rb +109 -0
  77. data/lib/badline/c64/reu/saved_state.rb +55 -0
  78. data/lib/badline/c64/reu/trigger.rb +34 -0
  79. data/lib/badline/c64/reu.rb +291 -0
  80. data/lib/badline/{computer → c64}/saved_state.rb +13 -18
  81. data/lib/badline/{computer → c64}/true_drives.rb +20 -31
  82. data/lib/badline/{computer.rb → c64.rb} +22 -80
  83. data/lib/badline/chips/cia/fast_forward.rb +62 -0
  84. data/lib/badline/chips/cia/interrupt_register.rb +145 -0
  85. data/lib/badline/chips/cia/saved_state.rb +49 -0
  86. data/lib/badline/chips/cia/serial/saved_state.rb +45 -0
  87. data/lib/badline/chips/cia/serial.rb +250 -0
  88. data/lib/badline/chips/cia/time_of_day.rb +232 -0
  89. data/lib/badline/chips/cia/timer.rb +208 -0
  90. data/lib/badline/chips/cia.rb +281 -0
  91. data/lib/badline/chips/cpu/addressing.rb +133 -0
  92. data/lib/badline/chips/cpu/boundary.rb +56 -0
  93. data/lib/badline/chips/cpu/core.rb +167 -0
  94. data/lib/badline/chips/cpu/instruction.rb +338 -0
  95. data/lib/badline/chips/cpu/instruction_set/arithmetic.rb +118 -0
  96. data/lib/badline/chips/cpu/instruction_set/bitwise.rb +130 -0
  97. data/lib/badline/chips/cpu/instruction_set/branch.rb +83 -0
  98. data/lib/badline/chips/cpu/instruction_set/flag.rb +67 -0
  99. data/lib/badline/chips/cpu/instruction_set/illegal.rb +274 -0
  100. data/lib/badline/chips/cpu/instruction_set/inc_dec.rb +73 -0
  101. data/lib/badline/chips/cpu/instruction_set/stack.rb +56 -0
  102. data/lib/badline/chips/cpu/instruction_set/transfer.rb +141 -0
  103. data/lib/badline/chips/cpu/instruction_set.rb +59 -0
  104. data/lib/badline/chips/cpu/interrupts.rb +108 -0
  105. data/lib/badline/chips/cpu/microcode.rb +179 -0
  106. data/lib/badline/chips/cpu/operations.rb +90 -0
  107. data/lib/badline/chips/cpu/port.rb +116 -0
  108. data/lib/badline/chips/cpu/saved_state.rb +117 -0
  109. data/lib/badline/chips/cpu/stack_operations.rb +84 -0
  110. data/lib/badline/chips/cpu/traps.rb +23 -0
  111. data/lib/badline/chips/cpu.rb +12 -0
  112. data/lib/badline/chips/sid/catch_up.rb +122 -0
  113. data/lib/badline/chips/sid/decimator.rb +66 -0
  114. data/lib/badline/chips/sid/envelope/saved_state.rb +70 -0
  115. data/lib/badline/chips/sid/envelope.rb +241 -0
  116. data/lib/badline/chips/sid/filter/saved_state.rb +42 -0
  117. data/lib/badline/chips/sid/filter.rb +241 -0
  118. data/lib/badline/chips/sid/saved_state.rb +52 -0
  119. data/lib/badline/chips/sid/voice.rb +79 -0
  120. data/lib/badline/chips/sid/waveform/combined.rb +128 -0
  121. data/lib/badline/chips/sid/waveform/fast_forward.rb +107 -0
  122. data/lib/badline/chips/sid/waveform/noise_writeback.rb +124 -0
  123. data/lib/badline/chips/sid/waveform/saved_state.rb +68 -0
  124. data/lib/badline/chips/sid/waveform.rb +307 -0
  125. data/lib/badline/chips/sid.rb +265 -0
  126. data/lib/badline/{c128 → chips}/vdc/memory.rb +1 -1
  127. data/lib/badline/{c128 → chips}/vdc/painter.rb +1 -1
  128. data/lib/badline/{c128 → chips}/vdc/raster.rb +1 -1
  129. data/lib/badline/chips/vdc/saved_state.rb +36 -0
  130. data/lib/badline/{c128 → chips}/vdc/window.rb +1 -1
  131. data/lib/badline/{c128 → chips}/vdc.rb +7 -31
  132. data/lib/badline/chips/via/control_lines.rb +124 -0
  133. data/lib/badline/chips/via/fast_forward.rb +55 -0
  134. data/lib/badline/chips/via/interrupt_register.rb +69 -0
  135. data/lib/badline/chips/via/saved_state.rb +44 -0
  136. data/lib/badline/chips/via/shift_register.rb +182 -0
  137. data/lib/badline/chips/via/timer1.rb +133 -0
  138. data/lib/badline/chips/via/timer2.rb +135 -0
  139. data/lib/badline/chips/via.rb +268 -0
  140. data/lib/badline/chips/vic/bank.rb +135 -0
  141. data/lib/badline/chips/vic/border_mask.rb +88 -0
  142. data/lib/badline/chips/vic/collisions.rb +117 -0
  143. data/lib/badline/chips/vic/color_patches.rb +49 -0
  144. data/lib/badline/chips/vic/display_state.rb +187 -0
  145. data/lib/badline/chips/vic/graphics_mode.rb +166 -0
  146. data/lib/badline/chips/vic/graphics_shifter/saved_state.rb +40 -0
  147. data/lib/badline/chips/vic/graphics_shifter.rb +187 -0
  148. data/lib/badline/chips/vic/register_log.rb +83 -0
  149. data/lib/badline/chips/vic/registers.rb +197 -0
  150. data/lib/badline/chips/vic/saved_state.rb +82 -0
  151. data/lib/badline/chips/vic/sequencer/saved_state.rb +91 -0
  152. data/lib/badline/chips/vic/sequencer.rb +277 -0
  153. data/lib/badline/chips/vic/sequencer_output.rb +118 -0
  154. data/lib/badline/chips/vic/sprite/internal_bus.rb +70 -0
  155. data/lib/badline/chips/vic/sprite/saved_state.rb +66 -0
  156. data/lib/badline/chips/vic/sprite/shifter.rb +148 -0
  157. data/lib/badline/chips/vic/sprite/timing.rb +58 -0
  158. data/lib/badline/chips/vic/sprite.rb +339 -0
  159. data/lib/badline/chips/vic/sprites/saved_state.rb +59 -0
  160. data/lib/badline/chips/vic/sprites.rb +284 -0
  161. data/lib/badline/chips/vic.rb +783 -0
  162. data/lib/badline/chips/vic_i/painter/saved_state.rb +40 -0
  163. data/lib/badline/{vic20/vic → chips/vic_i}/painter.rb +6 -30
  164. data/lib/badline/{vic20/vic → chips/vic_i}/saved_state.rb +2 -2
  165. data/lib/badline/chips/vic_i/sound/output.rb +85 -0
  166. data/lib/badline/chips/vic_i/sound.rb +235 -0
  167. data/lib/badline/{vic20/vic → chips/vic_i}/text_window.rb +2 -2
  168. data/lib/badline/{vic20/vic.rb → chips/vic_i.rb} +5 -5
  169. data/lib/badline/chips/z80/arithmetic.rb +119 -0
  170. data/lib/badline/chips/z80/bit_page.rb +57 -0
  171. data/lib/badline/chips/z80/block_io.rb +60 -0
  172. data/lib/badline/chips/z80/block_transfers.rb +61 -0
  173. data/lib/badline/chips/z80/bus_cycles.rb +113 -0
  174. data/lib/badline/chips/z80/core.rb +115 -0
  175. data/lib/badline/chips/z80/exchanges.rb +52 -0
  176. data/lib/badline/chips/z80/extended_page.rb +91 -0
  177. data/lib/badline/chips/z80/flags.rb +39 -0
  178. data/lib/badline/chips/z80/interrupt_acceptance.rb +48 -0
  179. data/lib/badline/chips/z80/jumps.rb +84 -0
  180. data/lib/badline/chips/z80/loads.rb +100 -0
  181. data/lib/badline/chips/z80/main_page.rb +136 -0
  182. data/lib/badline/chips/z80/registers.rb +122 -0
  183. data/lib/badline/chips/z80/saved_state.rb +71 -0
  184. data/lib/badline/chips/z80/shifts.rb +66 -0
  185. data/lib/badline/chips/z80.rb +13 -0
  186. data/lib/badline/core.rb +27 -29
  187. data/lib/badline/drives/datasette/saved_state.rb +37 -0
  188. data/lib/badline/drives/datasette.rb +140 -0
  189. data/lib/badline/drives/engine/core.rb +149 -0
  190. data/lib/badline/drives/engine/fast_serial.rb +75 -0
  191. data/lib/badline/drives/engine/idle.rb +127 -0
  192. data/lib/badline/drives/engine/idle_cpu.rb +30 -0
  193. data/lib/badline/drives/engine/orbit.rb +81 -0
  194. data/lib/badline/drives/engine/serial_lines.rb +75 -0
  195. data/lib/badline/drives/engine/sleep.rb +114 -0
  196. data/lib/badline/drives/engine/vias.rb +86 -0
  197. data/lib/badline/drives/engine/watch.rb +152 -0
  198. data/lib/badline/drives/engine.rb +11 -0
  199. data/lib/badline/drives/model1541/bus.rb +88 -0
  200. data/lib/badline/drives/model1541/byte_ready.rb +50 -0
  201. data/lib/badline/drives/model1541/cpu.rb +13 -0
  202. data/lib/badline/drives/model1541/disk/saved_state.rb +88 -0
  203. data/lib/badline/drives/model1541/disk.rb +317 -0
  204. data/lib/badline/drives/model1541/disk_via.rb +38 -0
  205. data/lib/badline/drives/model1541/gcr.rb +59 -0
  206. data/lib/badline/drives/model1541/mechanism/saved_state.rb +72 -0
  207. data/lib/badline/drives/model1541/mechanism.rb +315 -0
  208. data/lib/badline/drives/model1541/rotation.rb +123 -0
  209. data/lib/badline/drives/model1541/saved_state.rb +44 -0
  210. data/lib/badline/drives/model1541/sector_reader.rb +78 -0
  211. data/lib/badline/drives/model1541/serial_port.rb +20 -0
  212. data/lib/badline/drives/model1541/track.rb +100 -0
  213. data/lib/badline/drives/model1541.rb +81 -0
  214. data/lib/badline/drives/model1571/bus.rb +113 -0
  215. data/lib/badline/drives/model1571/cpu.rb +13 -0
  216. data/lib/badline/drives/model1571/saved_state.rb +47 -0
  217. data/lib/badline/drives/model1571/serial_port.rb +39 -0
  218. data/lib/badline/drives/model1571/serial_via.rb +27 -0
  219. data/lib/badline/drives/model1571/wd1770.rb +122 -0
  220. data/lib/badline/drives/model1571.rb +142 -0
  221. data/lib/badline/drives/model1581/bus.rb +139 -0
  222. data/lib/badline/drives/model1581/cpu.rb +13 -0
  223. data/lib/badline/drives/model1581/disk/saved_state.rb +89 -0
  224. data/lib/badline/drives/model1581/disk.rb +137 -0
  225. data/lib/badline/drives/model1581/mechanism.rb +119 -0
  226. data/lib/badline/drives/model1581/saved_state.rb +42 -0
  227. data/lib/badline/drives/model1581/serial_port.rb +54 -0
  228. data/lib/badline/drives/model1581/slot.rb +79 -0
  229. data/lib/badline/drives/model1581/track.rb +102 -0
  230. data/lib/badline/drives/model1581/track_bytes.rb +77 -0
  231. data/lib/badline/drives/model1581/wd1772/registers.rb +91 -0
  232. data/lib/badline/drives/model1581/wd1772/saved_state.rb +68 -0
  233. data/lib/badline/drives/model1581/wd1772/sector_writes.rb +75 -0
  234. data/lib/badline/drives/model1581/wd1772/sectors.rb +152 -0
  235. data/lib/badline/drives/model1581/wd1772/stepping.rb +111 -0
  236. data/lib/badline/drives/model1581/wd1772/tracks.rb +127 -0
  237. data/lib/badline/drives/model1581/wd1772/turn.rb +75 -0
  238. data/lib/badline/drives/model1581/wd1772.rb +218 -0
  239. data/lib/badline/drives/model1581.rb +206 -0
  240. data/lib/badline/drives/serial_bus.rb +161 -0
  241. data/lib/badline/frontend/boot.rb +10 -61
  242. data/lib/badline/frontend/machine_window.rb +3 -1
  243. data/lib/badline/frontend/player/filter_view.rb +1 -1
  244. data/lib/badline/frontend/player/player_controller.rb +2 -2
  245. data/lib/badline/frontend/player/player_screen.rb +1 -0
  246. data/lib/badline/frontend/player/theme.rb +7 -7
  247. data/lib/badline/frontend/timeline.rb +3 -3
  248. data/lib/badline/input/control_ports.rb +78 -0
  249. data/lib/badline/input/joystick.rb +27 -0
  250. data/lib/badline/input/keyboard.rb +114 -0
  251. data/lib/badline/input.rb +3 -0
  252. data/lib/badline/kernal_trap/{drive → dos}/block_commands.rb +1 -1
  253. data/lib/badline/kernal_trap/dos/channel.rb +183 -0
  254. data/lib/badline/kernal_trap/{drive → dos}/channels.rb +1 -1
  255. data/lib/badline/kernal_trap/{drive → dos}/memory.rb +1 -1
  256. data/lib/badline/kernal_trap/{drive → dos}/parameters.rb +1 -1
  257. data/lib/badline/kernal_trap/{drive → dos}/status.rb +1 -1
  258. data/lib/badline/kernal_trap/{drive → dos}/write_file.rb +1 -1
  259. data/lib/badline/kernal_trap/{drive → dos}/writes.rb +1 -1
  260. data/lib/badline/kernal_trap/{drive.rb → dos.rb} +11 -10
  261. data/lib/badline/kernal_trap/{file.rb → file_routine.rb} +1 -1
  262. data/lib/badline/kernal_trap/load.rb +6 -6
  263. data/lib/badline/kernal_trap/save.rb +5 -5
  264. data/lib/badline/kernal_trap/serial.rb +7 -7
  265. data/lib/badline/kernal_trap.rb +2 -3
  266. data/lib/badline/machine/clocking.rb +50 -0
  267. data/lib/badline/machine.rb +63 -7
  268. data/lib/badline/media/boot_disk.rb +2 -2
  269. data/lib/badline/media/disk_list.rb +3 -3
  270. data/lib/badline/media/disk_set.rb +1 -1
  271. data/lib/badline/media/extensions.rb +33 -0
  272. data/lib/badline/media/program.rb +28 -0
  273. data/lib/badline/media/true_drive.rb +4 -3
  274. data/lib/badline/media/tune.rb +35 -0
  275. data/lib/badline/media/vic20_cartridge.rb +1 -7
  276. data/lib/badline/media/vic20_media.rb +38 -80
  277. data/lib/badline/media.rb +84 -81
  278. data/lib/badline/model.rb +1 -1
  279. data/lib/badline/options/event.rb +3 -1
  280. data/lib/badline/roms/README +6 -6
  281. data/lib/badline/sdl.rb +1 -0
  282. data/lib/badline/{storage/sid_file.rb → sid_tune/file.rb} +3 -3
  283. data/lib/badline/{storage → sid_tune}/hvsc.rb +7 -7
  284. data/lib/badline/{storage → sid_tune}/song_lengths.rb +2 -2
  285. data/lib/badline/{storage → sid_tune}/stil/credit.rb +1 -1
  286. data/lib/badline/{storage → sid_tune}/stil.rb +3 -3
  287. data/lib/badline/sid_tune.rb +6 -0
  288. data/lib/badline/snapshot/c128_setup.rb +43 -0
  289. data/lib/badline/snapshot/c64_setup.rb +79 -0
  290. data/lib/badline/snapshot/image.rb +5 -25
  291. data/lib/badline/snapshot/machine_state.rb +10 -2
  292. data/lib/badline/snapshot/setup.rb +22 -66
  293. data/lib/badline/snapshot/state.rb +8 -17
  294. data/lib/badline/snapshot/storage_setup.rb +68 -0
  295. data/lib/badline/snapshot/vic20_setup.rb +37 -0
  296. data/lib/badline/snapshot/vice/c64_mem.rb +1 -1
  297. data/lib/badline/snapshot/vice/restore.rb +1 -1
  298. data/lib/badline/snapshot/vice/reu1764.rb +1 -1
  299. data/lib/badline/snapshot/vice/reu_fields.rb +40 -0
  300. data/lib/badline/snapshot/vice.rb +7 -5
  301. data/lib/badline/snapshot.rb +13 -12
  302. data/lib/badline/storage/d64_image.rb +15 -9
  303. data/lib/badline/storage/d71_image.rb +0 -2
  304. data/lib/badline/storage/d81_image.rb +0 -2
  305. data/lib/badline/storage/disk_image/writing.rb +2 -2
  306. data/lib/badline/storage/disk_image.rb +3 -6
  307. data/lib/badline/storage/g64_image.rb +2 -2
  308. data/lib/badline/storage/host_directory.rb +0 -5
  309. data/lib/badline/storage/t64.rb +2 -8
  310. data/lib/badline/storage/unavailable.rb +1 -1
  311. data/lib/badline/storage.rb +0 -37
  312. data/lib/badline/support/c128_checkpoint.rb +39 -0
  313. data/lib/badline/support/checkpoint.rb +108 -0
  314. data/lib/badline/support/vic20_checkpoint.rb +38 -0
  315. data/lib/badline/support.rb +5 -0
  316. data/lib/badline/version.rb +1 -1
  317. data/lib/badline/vic20/attachments.rb +11 -81
  318. data/lib/badline/vic20/bus/saved_state.rb +35 -0
  319. data/lib/badline/vic20/bus.rb +3 -25
  320. data/lib/badline/vic20/cpu.rb +1 -1
  321. data/lib/badline/vic20/port_wiring.rb +4 -4
  322. data/lib/badline/vic20/saved_state.rb +16 -60
  323. data/lib/badline/vic20/user_via_ports.rb +2 -2
  324. data/lib/badline/vic20.rb +20 -51
  325. metadata +250 -221
  326. data/lib/badline/address_bus/extra_sids.rb +0 -30
  327. data/lib/badline/address_bus/fittings.rb +0 -38
  328. data/lib/badline/address_bus/pla.rb +0 -147
  329. data/lib/badline/address_bus/roms.rb +0 -39
  330. data/lib/badline/address_bus/saved_state.rb +0 -72
  331. data/lib/badline/address_bus/sid_slots.rb +0 -42
  332. data/lib/badline/address_bus.rb +0 -235
  333. data/lib/badline/cartridge/action_replay.rb +0 -126
  334. data/lib/badline/cartridge/atomic_power.rb +0 -24
  335. data/lib/badline/cartridge/bank.rb +0 -81
  336. data/lib/badline/cartridge/comal80.rb +0 -50
  337. data/lib/badline/cartridge/dinamic.rb +0 -34
  338. data/lib/badline/cartridge/easy_flash.rb +0 -145
  339. data/lib/badline/cartridge/epyx_fastload.rb +0 -79
  340. data/lib/badline/cartridge/final_cartridge3.rb +0 -89
  341. data/lib/badline/cartridge/flash.rb +0 -306
  342. data/lib/badline/cartridge/freezer.rb +0 -33
  343. data/lib/badline/cartridge/fun_play.rb +0 -38
  344. data/lib/badline/cartridge/g_mod2.rb +0 -74
  345. data/lib/badline/cartridge/game_system.rb +0 -40
  346. data/lib/badline/cartridge/geo_ram.rb +0 -79
  347. data/lib/badline/cartridge/kcs_power.rb +0 -75
  348. data/lib/badline/cartridge/mach5.rb +0 -32
  349. data/lib/badline/cartridge/magic_desk.rb +0 -30
  350. data/lib/badline/cartridge/ocean.rb +0 -35
  351. data/lib/badline/cartridge/pagefox.rb +0 -68
  352. data/lib/badline/cartridge/retro_replay.rb +0 -320
  353. data/lib/badline/cartridge/rex_utility.rb +0 -29
  354. data/lib/badline/cartridge/rgcd.rb +0 -58
  355. data/lib/badline/cartridge/simons_basic.rb +0 -35
  356. data/lib/badline/cartridge/standard.rb +0 -15
  357. data/lib/badline/cartridge/super_games.rb +0 -48
  358. data/lib/badline/cartridge/westermann.rb +0 -30
  359. data/lib/badline/cartridge/zaxxon.rb +0 -66
  360. data/lib/badline/cartridge.rb +0 -303
  361. data/lib/badline/checkpoint.rb +0 -98
  362. data/lib/badline/cia/fast_forward.rb +0 -60
  363. data/lib/badline/cia/interrupt_register.rb +0 -143
  364. data/lib/badline/cia/serial.rb +0 -275
  365. data/lib/badline/cia/state.rb +0 -47
  366. data/lib/badline/cia/timer.rb +0 -206
  367. data/lib/badline/cia.rb +0 -279
  368. data/lib/badline/color_memory.rb +0 -25
  369. data/lib/badline/computer/kernal_traps.rb +0 -29
  370. data/lib/badline/control_ports.rb +0 -76
  371. data/lib/badline/cpu/addressing.rb +0 -131
  372. data/lib/badline/cpu/boundary.rb +0 -54
  373. data/lib/badline/cpu/core.rb +0 -165
  374. data/lib/badline/cpu/microcode.rb +0 -177
  375. data/lib/badline/cpu/operations.rb +0 -88
  376. data/lib/badline/cpu/saved_state.rb +0 -115
  377. data/lib/badline/cpu/stack_operations.rb +0 -82
  378. data/lib/badline/cpu.rb +0 -10
  379. data/lib/badline/cycleable.rb +0 -38
  380. data/lib/badline/datasette.rb +0 -161
  381. data/lib/badline/drive/core.rb +0 -147
  382. data/lib/badline/drive/fast_serial.rb +0 -73
  383. data/lib/badline/drive/idle.rb +0 -125
  384. data/lib/badline/drive/idle_cpu.rb +0 -28
  385. data/lib/badline/drive/orbit.rb +0 -79
  386. data/lib/badline/drive/serial_lines.rb +0 -73
  387. data/lib/badline/drive/sleep.rb +0 -112
  388. data/lib/badline/drive/vias.rb +0 -84
  389. data/lib/badline/drive/watch.rb +0 -150
  390. data/lib/badline/drive.rb +0 -11
  391. data/lib/badline/drive1541/bus.rb +0 -86
  392. data/lib/badline/drive1541/byte_ready.rb +0 -48
  393. data/lib/badline/drive1541/cpu.rb +0 -11
  394. data/lib/badline/drive1541/disk.rb +0 -318
  395. data/lib/badline/drive1541/disk_state.rb +0 -94
  396. data/lib/badline/drive1541/disk_via.rb +0 -36
  397. data/lib/badline/drive1541/gcr.rb +0 -57
  398. data/lib/badline/drive1541/mechanism.rb +0 -313
  399. data/lib/badline/drive1541/mechanism_state.rb +0 -68
  400. data/lib/badline/drive1541/rotation.rb +0 -121
  401. data/lib/badline/drive1541/sector_reader.rb +0 -76
  402. data/lib/badline/drive1541/serial_port.rb +0 -18
  403. data/lib/badline/drive1541/track.rb +0 -98
  404. data/lib/badline/drive1541.rb +0 -111
  405. data/lib/badline/drive1571/bus.rb +0 -111
  406. data/lib/badline/drive1571/cpu.rb +0 -11
  407. data/lib/badline/drive1571/serial_port.rb +0 -37
  408. data/lib/badline/drive1571/serial_via.rb +0 -25
  409. data/lib/badline/drive1571/wd1770.rb +0 -120
  410. data/lib/badline/drive1571.rb +0 -175
  411. data/lib/badline/drive1581/bus.rb +0 -137
  412. data/lib/badline/drive1581/cpu.rb +0 -11
  413. data/lib/badline/drive1581/disk.rb +0 -135
  414. data/lib/badline/drive1581/disk_state.rb +0 -87
  415. data/lib/badline/drive1581/mechanism.rb +0 -117
  416. data/lib/badline/drive1581/serial_port.rb +0 -52
  417. data/lib/badline/drive1581/slot.rb +0 -78
  418. data/lib/badline/drive1581/track.rb +0 -100
  419. data/lib/badline/drive1581/track_bytes.rb +0 -75
  420. data/lib/badline/drive1581/wd1772/registers.rb +0 -89
  421. data/lib/badline/drive1581/wd1772/sector_writes.rb +0 -73
  422. data/lib/badline/drive1581/wd1772/sectors.rb +0 -150
  423. data/lib/badline/drive1581/wd1772/state.rb +0 -66
  424. data/lib/badline/drive1581/wd1772/stepping.rb +0 -109
  425. data/lib/badline/drive1581/wd1772/tracks.rb +0 -125
  426. data/lib/badline/drive1581/wd1772/turn.rb +0 -73
  427. data/lib/badline/drive1581/wd1772.rb +0 -216
  428. data/lib/badline/drive1581.rb +0 -233
  429. data/lib/badline/iec_bus.rb +0 -159
  430. data/lib/badline/instruction.rb +0 -334
  431. data/lib/badline/instruction_set/arithmetic.rb +0 -114
  432. data/lib/badline/instruction_set/bitwise.rb +0 -126
  433. data/lib/badline/instruction_set/branch.rb +0 -79
  434. data/lib/badline/instruction_set/flag.rb +0 -63
  435. data/lib/badline/instruction_set/illegal.rb +0 -270
  436. data/lib/badline/instruction_set/inc_dec.rb +0 -69
  437. data/lib/badline/instruction_set/stack.rb +0 -52
  438. data/lib/badline/instruction_set/transfer.rb +0 -137
  439. data/lib/badline/instruction_set.rb +0 -55
  440. data/lib/badline/interrupts.rb +0 -104
  441. data/lib/badline/joystick.rb +0 -25
  442. data/lib/badline/kernal_trap/channel.rb +0 -181
  443. data/lib/badline/keyboard.rb +0 -112
  444. data/lib/badline/ram_expansion/banking.rb +0 -30
  445. data/lib/badline/ram_expansion/plus256k.rb +0 -54
  446. data/lib/badline/ram_expansion/plus60k.rb +0 -43
  447. data/lib/badline/ram_expansion/unexpanded.rb +0 -28
  448. data/lib/badline/ram_expansion.rb +0 -21
  449. data/lib/badline/reu/dma/saved_state.rb +0 -50
  450. data/lib/badline/reu/dma.rb +0 -273
  451. data/lib/badline/reu/ram.rb +0 -107
  452. data/lib/badline/reu/saved_state.rb +0 -80
  453. data/lib/badline/reu/trigger.rb +0 -32
  454. data/lib/badline/reu.rb +0 -289
  455. data/lib/badline/sid/catch_up.rb +0 -120
  456. data/lib/badline/sid/decimator.rb +0 -64
  457. data/lib/badline/sid/envelope/state.rb +0 -68
  458. data/lib/badline/sid/envelope.rb +0 -239
  459. data/lib/badline/sid/filter.rb +0 -263
  460. data/lib/badline/sid/voice.rb +0 -77
  461. data/lib/badline/sid/waveform/combined.rb +0 -126
  462. data/lib/badline/sid/waveform/fast_forward.rb +0 -105
  463. data/lib/badline/sid/waveform/noise_writeback.rb +0 -122
  464. data/lib/badline/sid/waveform/state.rb +0 -66
  465. data/lib/badline/sid/waveform.rb +0 -305
  466. data/lib/badline/sid.rb +0 -303
  467. data/lib/badline/time_of_day.rb +0 -228
  468. data/lib/badline/traps.rb +0 -19
  469. data/lib/badline/via/control_lines.rb +0 -122
  470. data/lib/badline/via/fast_forward.rb +0 -53
  471. data/lib/badline/via/interrupt_register.rb +0 -67
  472. data/lib/badline/via/shift_register.rb +0 -180
  473. data/lib/badline/via/state.rb +0 -42
  474. data/lib/badline/via/timer1.rb +0 -131
  475. data/lib/badline/via/timer2.rb +0 -133
  476. data/lib/badline/via.rb +0 -266
  477. data/lib/badline/vic/bank.rb +0 -133
  478. data/lib/badline/vic/border_mask.rb +0 -86
  479. data/lib/badline/vic/collisions.rb +0 -115
  480. data/lib/badline/vic/color_patches.rb +0 -47
  481. data/lib/badline/vic/display_state.rb +0 -185
  482. data/lib/badline/vic/graphics_mode.rb +0 -164
  483. data/lib/badline/vic/graphics_shifter.rb +0 -207
  484. data/lib/badline/vic/register_log.rb +0 -81
  485. data/lib/badline/vic/registers.rb +0 -195
  486. data/lib/badline/vic/sequencer.rb +0 -275
  487. data/lib/badline/vic/sequencer_output.rb +0 -116
  488. data/lib/badline/vic/sequencer_state.rb +0 -89
  489. data/lib/badline/vic/sprite/internal_bus.rb +0 -68
  490. data/lib/badline/vic/sprite/saved_state.rb +0 -64
  491. data/lib/badline/vic/sprite/shifter.rb +0 -146
  492. data/lib/badline/vic/sprite/timing.rb +0 -56
  493. data/lib/badline/vic/sprite.rb +0 -337
  494. data/lib/badline/vic/sprites.rb +0 -282
  495. data/lib/badline/vic/sprites_state.rb +0 -57
  496. data/lib/badline/vic.rb +0 -852
  497. data/lib/badline/vic20/sound/output.rb +0 -83
  498. data/lib/badline/vic20/sound.rb +0 -233
  499. data/lib/badline/z80/arithmetic.rb +0 -117
  500. data/lib/badline/z80/bit_page.rb +0 -55
  501. data/lib/badline/z80/block_io.rb +0 -58
  502. data/lib/badline/z80/block_transfers.rb +0 -59
  503. data/lib/badline/z80/bus_cycles.rb +0 -111
  504. data/lib/badline/z80/core.rb +0 -113
  505. data/lib/badline/z80/exchanges.rb +0 -50
  506. data/lib/badline/z80/extended_page.rb +0 -89
  507. data/lib/badline/z80/flags.rb +0 -37
  508. data/lib/badline/z80/interrupt_acceptance.rb +0 -46
  509. data/lib/badline/z80/jumps.rb +0 -82
  510. data/lib/badline/z80/loads.rb +0 -98
  511. data/lib/badline/z80/main_page.rb +0 -134
  512. data/lib/badline/z80/registers.rb +0 -120
  513. data/lib/badline/z80/saved_state.rb +0 -69
  514. data/lib/badline/z80/shifts.rb +0 -64
  515. data/lib/badline/z80.rb +0 -11
  516. /data/lib/badline/snapshot/{fields.rb → vice/fields.rb} +0 -0
@@ -0,0 +1,315 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "badline/drives/model1541/rotation"
4
+ require "badline/drives/model1541/mechanism/saved_state"
5
+ require "badline/drives/model1541/byte_ready"
6
+
7
+ module Badline
8
+ module Drives
9
+ class Model1541
10
+ # The disk mechanism behind VIA 2: the spindle motor, the head on its
11
+ # stepper, the LED, the read and write electronics and the
12
+ # write-protect sensor.
13
+ #
14
+ # Port B drives it and reads it back:
15
+ #
16
+ # PB0-1 stepper phase (out) PB4 write protect (in, 1 = writable)
17
+ # PB2 motor on (out) PB5-6 bit rate, speed zone 0-3 (out)
18
+ # PB3 LED on (out) PB7 SYNC (in, 0 while in a SYNC mark)
19
+ #
20
+ # Port A reads the read shift register's low eight bits, which CA1
21
+ # latches on BYTE READY. In write mode it drives the write shift
22
+ # register, and the read shift register takes the bits written.
23
+ #
24
+ # The disk turns at 300 rpm under the head, and a read clock at the
25
+ # bit rate PB5-6 select clocks the bits off it (see Rotation).
26
+ #
27
+ # Ten 1 bits in a row are a SYNC mark while CB2 selects reading: PB7
28
+ # reads it, and it holds the bit counter at zero, so the first 0 bit
29
+ # after it starts a byte. Every eighth bit after that is BYTE READY,
30
+ # which pulls VIA 2's CA1 low until the next bit and reaches the CPU's
31
+ # SO pin (see byte_ready!).
32
+ #
33
+ # CB2 low selects write mode, where SYNC detection and the flux are
34
+ # off and the clock runs on at the selected rate. At each BYTE READY
35
+ # the write shift register loads what the VIA drives on port A, and
36
+ # the next eight clocks put that byte's bits on the track, most
37
+ # significant first, in place of what it held. While reading, the load
38
+ # takes the byte just read, so a switch to writing mid-byte writes the
39
+ # rest of it. A write lays its bits one to a cell from the cell under
40
+ # the head, so on a track written at the selected rate each bit fills
41
+ # the cell it passes over. A write to a half track without data gives
42
+ # it a blank track first, a turn long at the selected rate, and a
43
+ # write to a track written at another rate lays the whole track out
44
+ # again as a turn at the selected rate first (Track#relaid).
45
+ #
46
+ # PB4 reads the write-protect sensor: high with no disk in, or with
47
+ # the notch of a writable one open. With the disk write-protected the
48
+ # write gate stays shut: the bits and BYTE READY go on, and nothing
49
+ # reaches the disk. The DOS reads PB4 first and refuses to write, so
50
+ # only a program that writes without asking finds that out.
51
+ #
52
+ # The motor stopping flushes what was written to the disk's image
53
+ # (see Disk#flush), which is when the DOS is done with a job, and so
54
+ # does taking the disk out.
55
+ #
56
+ # The stepper moves the head a half track each time the phase steps
57
+ # by one, inwards for +1 and outwards for -1. Each half track has a
58
+ # phase of its own, from power-on the low two bits of its number, so
59
+ # the head sits on a whole track when phase 0 or 2 holds it. The
60
+ # stepper's coils are powered only while PB2 runs the motor, as VICE
61
+ # reads the 1541's schematic, so a phase set with the motor off moves
62
+ # nothing until the motor comes on and it pulls the head. Tracks run
63
+ # from half track 2 (track 1), where the head stops against the end of
64
+ # its rail, to Disk::MAX_HALF_TRACK. A step out against the stop slips:
65
+ # the head stays on track 1 and the phase that tried to pull it out
66
+ # becomes track 1's, so the DOS's bump leaves the head on track 1 in
67
+ # line with the phase it ends on, and its first step in reaches the
68
+ # half track after it.
69
+ #
70
+ # The 1571's stepper is lined up so that phase 0 holds track 1, where
71
+ # its track 0 sensor looks for the head (Mechanism.new's +slip+).
72
+ class Mechanism
73
+ include Rotation
74
+ include SavedState
75
+ include ByteReady
76
+
77
+ MOTOR = 0x04
78
+ LED = 0x08
79
+ WRITE_PROTECT = 0x10
80
+ SYNC = 0x80
81
+
82
+ MIN_HALF_TRACK = 2
83
+ # Where the head sits at power-on: track 18, the directory.
84
+ START_HALF_TRACK = 36
85
+
86
+ SYNC_BITS = 10
87
+
88
+ attr_reader :disk, :half_track, :zone
89
+
90
+ # +slip+ is the stepper's phase offset at power-on: 0, where each half
91
+ # track's phase is the low two bits of its number, or 2 for track 1
92
+ # on phase 0.
93
+ def initialize(slip: 0)
94
+ @via = nil
95
+ @so_pending = false
96
+ @disk = nil
97
+ @half_track = START_HALF_TRACK
98
+ @slip = slip
99
+ @motor = false
100
+ @led = false
101
+ @zone = 0
102
+ @clock = Track.cell(0)
103
+ @time = 0
104
+ @clock_at = @clock / 2
105
+ @clocks = 0
106
+ @shift = 0
107
+ @ones = 0
108
+ @bits = 0
109
+ @sync = false
110
+ @byte_ready = false
111
+ @byte_latched = false
112
+ @writing = false
113
+ @write_shift = 0
114
+ @write_gate = false
115
+ @protected = false
116
+ @side_base = 0
117
+ @cycle_ticks = CYCLE
118
+ load_track
119
+ end
120
+
121
+ # The drive's clock rate: the disk turns on by fewer ticks each
122
+ # cycle at 2 MHz.
123
+ def clock_hz=(clock_hz)
124
+ @cycle_ticks = CYCLE * CLOCK_HZ / clock_hz
125
+ end
126
+
127
+ # The head that reads, 0 or 1, on a drive with two (Disk::SIDE).
128
+ def side = @side_base.zero? ? 0 : 1
129
+
130
+ def side=(side)
131
+ side_base = side * Disk::SIDE
132
+ return if side_base == @side_base
133
+
134
+ @side_base = side_base
135
+ load_track
136
+ end
137
+
138
+ def motor_on? = @motor
139
+
140
+ def led_on? = @led
141
+
142
+ def sync? = @sync
143
+
144
+ def writing? = @writing
145
+
146
+ # What port B and CB2 can change with the motor off: the motor, the
147
+ # LED, the zone and the clock's rate, the stepper, write mode and
148
+ # where a write starts, and the track and cell under the head. The
149
+ # rest moves only while the motor turns, or as a disk goes in (see
150
+ # Drives::Engine::Idle).
151
+ def idle_state
152
+ [@motor, @led, @zone, @clock, @half_track, @slip, @side_base, @disk, @track, @index, @mask, @cell_end,
153
+ @time, @writing, @write_index, @sync, @byte_latched]
154
+ end
155
+
156
+ # Puts a Disk in, or takes it out with nil, flushing the disk that
157
+ # was in. The head goes on from the same point in the turn.
158
+ def insert(disk)
159
+ flush
160
+ @disk = disk
161
+ @protected = disk&.write_protected? || false
162
+ @write_gate = !disk.nil? && !@protected
163
+ load_track
164
+ end
165
+
166
+ # Stores what the head wrote in the disk's image.
167
+ def flush
168
+ @disk.flush if @disk&.written?
169
+ rescue Storage::WriteError => e
170
+ warn "1541: couldn't write the disk back: #{e.message}"
171
+ end
172
+
173
+ # VIA 2 drives CB2 to +high+: low selects write mode, which stops
174
+ # SYNC detection at once.
175
+ def cb2_written(high)
176
+ @write_index = nil if high || !@writing
177
+ @writing = !high
178
+ @sync = !@writing && @ones >= SYNC_BITS
179
+ end
180
+
181
+ # VIA 2 drives port B's lines to +lines+.
182
+ def port_b_written(lines)
183
+ motor = @motor
184
+ @motor = lines.anybits?(MOTOR)
185
+ flush if motor && !@motor
186
+ @led = lines.anybits?(LED)
187
+ zone = (lines >> 5) & 0x03
188
+ if zone != @zone
189
+ @zone = zone
190
+ @clock = Track.cell(zone)
191
+ @track_written = false
192
+ end
193
+ step(lines & 0x03) if @motor
194
+ end
195
+
196
+ # Port A is the read shift register.
197
+ def read_a(_lines) = @shift & 0xff
198
+
199
+ # SYNC low in a SYNC mark, and write protect low for a protected
200
+ # disk.
201
+ def read_b(_lines)
202
+ lines = @sync ? 0xff & ~SYNC : 0xff
203
+ @protected ? lines & ~WRITE_PROTECT : lines
204
+ end
205
+
206
+ private
207
+
208
+ def read_bit(one)
209
+ if @byte_ready
210
+ @byte_ready = false
211
+ byte_ready_ended!
212
+ end
213
+ @shift = ((@shift << 1) & 0x3fe) | (one ? 1 : 0)
214
+ if one
215
+ @ones += 1
216
+ @sync = true if @ones >= SYNC_BITS
217
+ else
218
+ @ones = 0
219
+ @sync = false
220
+ end
221
+ count_bit
222
+ end
223
+
224
+ # The write shift register's top bit goes onto the track through the
225
+ # write gate, and into the read shift register, as the head's own
226
+ # signal.
227
+ def write_bit(at)
228
+ if @byte_ready
229
+ @byte_ready = false
230
+ byte_ready_ended!
231
+ end
232
+ one = @write_shift.anybits?(0x80)
233
+ @write_shift = (@write_shift << 1) & 0xff
234
+ store_bit(one, at) if @write_gate
235
+ @shift = ((@shift << 1) & 0x3fe) | (one ? 1 : 0)
236
+ @ones = one ? @ones + 1 : 0
237
+ count_bit
238
+ end
239
+
240
+ # Each bit written goes in the cell after the last one's, from the
241
+ # cell under the head when the write started.
242
+ def store_bit(one, at)
243
+ track_written(at) unless @track_written
244
+ @write_index ||= (@index * 8) + 8 - @mask.bit_length
245
+ index = @write_index >> 3
246
+ mask = 0x80 >> (@write_index & 7)
247
+ if one
248
+ @bytes[index] |= mask
249
+ else
250
+ @bytes[index] &= ~mask
251
+ end
252
+ @write_index += 1
253
+ @write_index = 0 if @write_index == @length * 8
254
+ end
255
+
256
+ # The first write to the track since the head came to it or the rate
257
+ # changed: a half track without data gets a blank track, and a track
258
+ # written at another rate is laid out again at this one.
259
+ def track_written(at)
260
+ surface = self.surface
261
+ track = @disk.writable_track(surface, @zone)
262
+ @disk.write(surface, track.relaid(@zone)) unless track.written_at?(@zone)
263
+ load_track(at)
264
+ @disk.written(surface)
265
+ @track_written = true
266
+ end
267
+
268
+ # Every eighth bit outside a SYNC mark is BYTE READY, which loads the
269
+ # write shift register: from what the VIA drives on port A in write
270
+ # mode, and with the byte just read otherwise.
271
+ def count_bit
272
+ if @sync
273
+ @bits = 0
274
+ return
275
+ end
276
+ @bits += 1
277
+ return unless @bits == 8
278
+
279
+ @bits = 0
280
+ @write_shift = @writing ? @via.port_a_output : @shift & 0xff
281
+ @byte_ready = true
282
+ byte_ready!
283
+ end
284
+
285
+ # The stepper's rotor turns with the head, a phase to each half
286
+ # track, so the energized phase pulls the head to the neighbouring
287
+ # half track it belongs to. A phase two away pulls both ways, and the
288
+ # head stays. Against the stop at track 1 the head can't follow, and
289
+ # the phases slip round to where the head is.
290
+ def step(phase)
291
+ move = (phase - @half_track - @slip) & 0x03
292
+ if move == 1
293
+ seek(@half_track + 1)
294
+ elsif move == 3 && @half_track == MIN_HALF_TRACK
295
+ @slip = (phase - MIN_HALF_TRACK) & 0x03
296
+ elsif move == 3
297
+ seek(@half_track - 1)
298
+ end
299
+ end
300
+
301
+ # The half track under the head that reads, numbered on the disk
302
+ # (see Disk::SIDE).
303
+ def surface = @half_track + @side_base
304
+
305
+ def seek(half_track)
306
+ half_track = half_track.clamp(MIN_HALF_TRACK, Disk::MAX_HALF_TRACK)
307
+ return if half_track == @half_track
308
+
309
+ @half_track = half_track
310
+ load_track
311
+ end
312
+ end
313
+ end
314
+ end
315
+ 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,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ module Drives
5
+ class Model1541
6
+ # The drive's state for a snapshot.
7
+ module SavedState
8
+ # The drive's whole state: its clock phase, the CPU, RAM, both VIAs,
9
+ # the serial port's latch of the C64's lines and the mechanism with its
10
+ # disk. The drive settles first, so it is awake and not recording a
11
+ # pass, and a restored drive starts the same way, its orbits forgotten
12
+ # from the state it takes: Idle's and Orbit's bookkeeping
13
+ # isn't stored. The device number, the host's clock rate and whether
14
+ # the drive may sleep are the machine's wiring.
15
+ def save_state(out)
16
+ settle!
17
+ out.marker("DRIVE1541")
18
+ out.int(@phase).int(@cycles).boolean(@mechanism.so_pending)
19
+ @serial_port.save_state(out)
20
+ @via1.save_state(out)
21
+ @via2.save_state(out)
22
+ @bus.save_state(out)
23
+ @cpu.save_state(out)
24
+ @mechanism.save_state(out)
25
+ end
26
+
27
+ def load_state(input)
28
+ settle!
29
+ input.marker("DRIVE1541")
30
+ @phase = input.int
31
+ @cycles = input.int
32
+ @mechanism.so_pending = input.boolean?
33
+ @serial_port.load_state(input)
34
+ @via1.load_state(input)
35
+ @via2.load_state(input)
36
+ @bus.load_state(input)
37
+ @cpu.load_state(input)
38
+ @mechanism.load_state(input)
39
+ forget_orbits
40
+ end
41
+ end
42
+ end
43
+ end
44
+ 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