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,206 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "badline/drives/engine"
4
+ require "badline/drives/model1581/track"
5
+ require "badline/drives/model1581/track_bytes"
6
+ require "badline/drives/model1581/disk"
7
+ require "badline/drives/model1581/mechanism"
8
+ require "badline/drives/model1581/wd1772"
9
+ require "badline/drives/model1581/cpu"
10
+ require "badline/drives/model1581/serial_port"
11
+ require "badline/drives/model1581/bus"
12
+ require "badline/drives/model1581/keyed_state"
13
+ require "badline/drives/model1581/slot"
14
+
15
+ module Badline
16
+ module Drives
17
+ # The 1581 3.5" disk drive: a 6502 at 2 MHz with 8 KB of RAM, an 8520
18
+ # for the serial bus and the mechanism's lines, a WD1772 for the MFM
19
+ # disk, and the DOS in 32 KB of ROM (1581 Service Manual). Drives::Engine::Core
20
+ # clocks it against the host.
21
+ #
22
+ # The 8520's port B faces the serial bus as VIA 1's does on the 1541,
23
+ # with the same bits, and ATN reaches its FLAG pin, so ATN going low
24
+ # interrupts the DOS. Its SP and CNT carry fast serial on DATA and SRQ,
25
+ # through buffers PB5 turns round (Drives::Engine::FastSerial), as the 1571's
26
+ # 6526 does. The ATN acknowledge differs: a NAND of ATN IN and
27
+ # ATN ACK pulls DATA, so DATA follows ATN only while ATN is asserted
28
+ # and PB4 is high, where the 1541's XOR pulls it whenever the two
29
+ # differ (Drives::SerialBus::DRIVE_ATN_GATED). Port A drives the motor, the side
30
+ # and the two LEDs, and reads the switches and the mechanism's /RDY and
31
+ # /DISK CHNG (SerialPort). Timer B interrupts every 10 ms for the
32
+ # controller code that runs the WD1772.
33
+ #
34
+ # The 8520 differs from the 6526 in its TOD, a 24-bit binary event
35
+ # counter where the 6526 keeps a BCD clock. The 1581 holds the TOD pin
36
+ # high when it carries a WD1772 (jumper J1 out), so the counter never
37
+ # counts, and the DOS's one use of it, writing $00 to the low byte and
38
+ # reading it back 20 µs later to tell a WD1770 from a WD1772, reads $00
39
+ # on either chip: the drive's CIA gets no TOD pulses (CIA's tod_hz).
40
+ class Model1581
41
+ include Engine::Core
42
+ include Engine::FastSerial
43
+
44
+ CLOCK_HZ = 2_000_000
45
+
46
+ # Where the DOS's idle loop starts over (see Drives::Engine::Idle).
47
+ IDLE_LOOP = 0xb105
48
+
49
+ # The host cycles, at about 1 MHz, by which a 1581 switched on with the
50
+ # machine has run its DOS's reset, the RAM test, the ROM checksum and
51
+ # the controller's half-second wait, and waits on the bus: 2.32 s,
52
+ # with a margin.
53
+ BOOT_CYCLES = 2_700_000
54
+
55
+ # The 8520's port A outputs.
56
+ SIDE = 0x01
57
+ MOTOR = 0x04
58
+ POWER_LED = 0x20
59
+ ACTIVITY_LED = 0x40
60
+
61
+ # Port B's ATN acknowledge, and the fast serial direction.
62
+ ATN_ACK = 0x10
63
+ FAST_SERIAL_OUT = 0x20
64
+
65
+ def cia
66
+ settle!
67
+ @cia
68
+ end
69
+
70
+ def fdc
71
+ settle!
72
+ @fdc
73
+ end
74
+
75
+ def model_name = "1581"
76
+
77
+ # +rom+ covers $8000-$FFFF, and defaults to the DOS image in the ROM
78
+ # path. +device+ is the number the switches on port A's PA3 and PA4
79
+ # set, 8 to 11.
80
+ def initialize(rom: nil, host_clock_hz: Model1541::CLOCK_HZ, device: 8, debug: false)
81
+ @device = device
82
+ @clock_hz = CLOCK_HZ
83
+ @mechanism = Mechanism.new
84
+ @fdc = WD1772.new(@mechanism)
85
+ @serial_port = SerialPort.new(@mechanism, device:)
86
+ @cia = Chips::CIA.new(start: 0x4000, peripheral: @serial_port, tod_hz: 0)
87
+ @bus = Bus.new(rom: rom || ROM.load("dos1581.rom", 0x8000), cia: @cia, fdc: @fdc, drive: self)
88
+ @cpu = CPU.new(@bus, debug:)
89
+ @idle_loop = IDLE_LOOP
90
+ @host_clock_hz = host_clock_hz
91
+ @phase = 0
92
+ @cycles = 0
93
+ @serial_bus = nil
94
+ @atn = false
95
+ @activity = false
96
+ @power = false
97
+ init_fast_serial
98
+ init_idle(debug)
99
+ ports_written
100
+ connect(SerialBus.new)
101
+ end
102
+
103
+ # The serial bus's RESET line reaches the CPU, the 8520 and the
104
+ # WD1772. RAM keeps its contents.
105
+ def reset!
106
+ settle!
107
+ @cia.reset!
108
+ @fdc.reset!
109
+ ports_written
110
+ @cpu.reset!
111
+ end
112
+
113
+ def insert(disk)
114
+ settle!
115
+ @mechanism.insert(disk, @fdc.now)
116
+ @fdc.spin_changed
117
+ end
118
+
119
+ # Puts in a Disk from the .d81 image at +path+, write-protected with
120
+ # `read_only`.
121
+ def insert_image(path, read_only: false)
122
+ insert(Disk.open(path, read_only:))
123
+ end
124
+
125
+ # Whether the activity LED is lit. The power LED stays lit, and
126
+ # flashes on an error.
127
+ def led_on? = @activity
128
+
129
+ def power_led_on? = @power
130
+
131
+ # The 8520's port B as it drives the serial bus, in VIA 1's bits, the
132
+ # ATN acknowledge turned round for the NAND that gates it, with the
133
+ # fast serial pins above it. It holds still while the drive sleeps, so
134
+ # reading it leaves the drive asleep.
135
+ def serial_output
136
+ lines = @cia.port_b_lines
137
+ output = (lines & (SerialBus::DRIVE_DATA_OUT | SerialBus::DRIVE_CLK_OUT)) | SerialBus::DRIVE_ATN_GATED |
138
+ @fast_output
139
+ lines.anybits?(ATN_ACK) ? output : output | SerialBus::DRIVE_ATNA
140
+ end
141
+
142
+ # The 8520's ports drive the motor, the side, the LEDs and the fast
143
+ # serial buffers: the drive follows them after every write to a port
144
+ # or direction register.
145
+ def ports_written
146
+ fast_serial_out = @cia.port_b_lines.anybits?(FAST_SERIAL_OUT)
147
+ turn_fast_serial(fast_serial_out) if fast_serial_out != @fast_serial_out
148
+ lines = @cia.port_a_lines
149
+ @mechanism.side = lines.anybits?(SIDE) ? 1 : 0
150
+ @activity = lines.anybits?(ACTIVITY_LED)
151
+ @power = lines.anybits?(POWER_LED)
152
+ motor = lines.nobits?(MOTOR)
153
+ return if motor == @mechanism.motor_on?
154
+
155
+ @mechanism.motor(motor, @fdc.now)
156
+ @fdc.spin_changed
157
+ end
158
+
159
+ private
160
+
161
+ # One drive cycle: ATN going low pulls FLAG, the WD1772 and the 8520
162
+ # clock ahead of the CPU, and the 8520 alone pulls IRQ.
163
+ def step
164
+ atn = @serial_port.atn_low?
165
+ if atn != @atn
166
+ @atn = atn
167
+ @cia.flag! if atn
168
+ end
169
+ @fdc.cycle!
170
+ @cia.cycle!
171
+ drive_fast_serial if @fast_serial_out
172
+ @cpu.irq = @cia.interrupted?
173
+ @cpu.cycle!
174
+ @cycles += 1
175
+ end
176
+
177
+ def hear_atn
178
+ @atn = @serial_port.atn_low?
179
+ end
180
+
181
+ # A pass of the idle loop reads and writes RAM alone, so it repeats
182
+ # while the 8520 pulls no IRQ, for as long as the 8520's counters and
183
+ # the WD1772's next phase leave room: until timer B's next interrupt
184
+ # for the controller code. Neither chip changes but by the clock, so
185
+ # only the CPU is compared.
186
+ def busy_for_pass? = @cia.interrupted?
187
+
188
+ def quiet_cycles = [@cia.quiet_cycles, @fdc.quiet_cycles].min
189
+
190
+ def idle_state = [*@cpu.idle_state, @bus.data, @atn]
191
+
192
+ def fast_forward_chips(cycles)
193
+ @cia.fast_forward(cycles)
194
+ @fdc.fast_forward(cycles)
195
+ end
196
+
197
+ # Every timer B interrupt reads the 8520, so no orbit (Drives::Engine::Orbit)
198
+ # comes round, and the drive keeps no anchors.
199
+ def anchor = nil
200
+
201
+ def counter_state(_touched) = []
202
+
203
+ def skip_chip_orbits(_cycles, _touched) = nil
204
+ end
205
+ end
206
+ end
@@ -0,0 +1,161 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ module Drives
5
+ # The serial bus between the C64 and its drives: ATN, CLK and DATA, each
6
+ # an open-collector line with a pull-up. A line is low while any device
7
+ # pulls it (wired-AND), and high otherwise. Every read works the levels
8
+ # out from what each side drives now.
9
+ #
10
+ # The C64 drives the lines from CIA 2's port A through 7406 inverters, so
11
+ # an output bit of 1 pulls its line low: PA3 ATN, PA4 CLK and PA5 DATA.
12
+ # The machine pushes those bits into the bus (host_lines=), which holds
13
+ # them. PA6 and PA7 read CLK and DATA directly, 1 for a released line.
14
+ # The bus stands in as CIA 2's peripheral for those two inputs.
15
+ #
16
+ # A drive drives CLK and DATA from VIA 1's PB3 and PB1, the same way. It
17
+ # also pulls DATA while ATN IN differs from its ATN acknowledge on PB4
18
+ # (an XOR gate), which answers ATN in hardware before the DOS gets to it.
19
+ # SerialPort reads the lines back into VIA 1. A drive whose
20
+ # serial_output sets DRIVE_ATN_GATED pulls DATA that way only while
21
+ # ATN is low, as the 1581's NAND does.
22
+ #
23
+ # Fast serial adds SRQ, which the C64 leaves alone. A C128 and a 1571
24
+ # clock bytes over it from a CIA's CNT, with the bits on DATA from its
25
+ # SP, through buffers each side turns towards the bus to send. A pin low
26
+ # pulls its line low. Each side pushes the lines it pulls when they
27
+ # move: the host through host_fast_lines=, which tells the drives
28
+ # (fast_lines_moved), and a drive through drives_fast_moved!, which
29
+ # tells the host (on_fast_change).
30
+ class SerialBus
31
+ # CIA 2 port A
32
+ HOST_ATN_OUT = 0x08
33
+ HOST_CLK_OUT = 0x10
34
+ HOST_DATA_OUT = 0x20
35
+ HOST_CLK_IN = 0x40
36
+ HOST_DATA_IN = 0x80
37
+
38
+ # VIA 1 port B
39
+ DRIVE_DATA_OUT = 0x02
40
+ DRIVE_CLK_OUT = 0x08
41
+ DRIVE_ATNA = 0x10
42
+
43
+ # The lines pulled low, as ATN, CLK, DATA and SRQ bits
44
+ ATN = 0x01
45
+ CLK = 0x02
46
+ DATA = 0x04
47
+ SRQ = 0x08
48
+
49
+ # A drive's serial_output bits above VIA 1's port B: its fast serial
50
+ # pins pulling DATA and SRQ.
51
+ DRIVE_FAST_DATA = 0x100
52
+ DRIVE_FAST_SRQ = 0x200
53
+
54
+ # A drive's serial_output bit whose ATN acknowledge pulls DATA only
55
+ # while ATN is low.
56
+ DRIVE_ATN_GATED = 0x400
57
+
58
+ attr_reader :drives, :host_lines
59
+
60
+ # The lines the host's fast serial pins pull, as DATA and SRQ bits.
61
+ attr_reader :host_fast_lines
62
+
63
+ def initialize
64
+ @drives = []
65
+ @host_lines = 0
66
+ @host_fast_lines = 0
67
+ @drive_fast_lines = 0
68
+ @on_fast_change = nil
69
+ end
70
+
71
+ # The host's fast serial pins moved: +low+ is the lines they pull, as
72
+ # DATA and SRQ bits.
73
+ def host_fast_lines=(low)
74
+ return if low == @host_fast_lines
75
+
76
+ @host_fast_lines = low
77
+ @drives.each(&:fast_lines_moved)
78
+ end
79
+
80
+ # A drive's fast serial pins moved (serial_output's fast bits).
81
+ def drives_fast_moved!
82
+ low = 0
83
+ @drives.each do |drive|
84
+ output = drive.serial_output
85
+ low |= DATA if output.anybits?(DRIVE_FAST_DATA)
86
+ low |= SRQ if output.anybits?(DRIVE_FAST_SRQ)
87
+ end
88
+ return if low == @drive_fast_lines
89
+
90
+ @drive_fast_lines = low
91
+ @on_fast_change&.call
92
+ end
93
+
94
+ # Calls the block whenever the drives move their fast serial pins.
95
+ def on_fast_change(&block)
96
+ @on_fast_change = block
97
+ end
98
+
99
+ # The machine pushes the lines it pulls, as port A bits, whenever they
100
+ # may have moved: its output bits, with input bits floating high as the
101
+ # 7406 inputs see them. A bus with only drives on it keeps 0.
102
+ def host_lines=(lines)
103
+ @host_lines = lines
104
+ @drives.each(&:host_written!)
105
+ end
106
+
107
+ def attach(drive)
108
+ @drives << drive unless @drives.include?(drive)
109
+ end
110
+
111
+ def detach(drive)
112
+ @drives.delete(drive)
113
+ end
114
+
115
+ # One pass over everything on the bus, for a reader that wants more
116
+ # than one line, SRQ included. +host+ is the C64's port A lines as the
117
+ # reader sees them, which a drive takes a cycle late (see
118
+ # Drives::Engine::SerialLines).
119
+ def low_lines(host = @host_lines)
120
+ atn = host.anybits?(HOST_ATN_OUT)
121
+ low = @host_fast_lines | @drive_fast_lines
122
+ low |= ATN if atn
123
+ low |= CLK if host.anybits?(HOST_CLK_OUT)
124
+ low |= DATA if host.anybits?(HOST_DATA_OUT)
125
+ i = 0
126
+ while i < @drives.length
127
+ drive = @drives[i].serial_output
128
+ low |= CLK if drive.anybits?(DRIVE_CLK_OUT)
129
+ low |= DATA if drive.anybits?(DRIVE_DATA_OUT) || acknowledging?(drive, atn)
130
+ i += 1
131
+ end
132
+ low
133
+ end
134
+
135
+ def atn_low?(host = @host_lines) = host.anybits?(HOST_ATN_OUT)
136
+
137
+ # Whether a drive's ATN acknowledge pulls DATA, with its serial_output
138
+ # +drive+, while ATN is +atn+.
139
+ def acknowledging?(drive, atn)
140
+ atn != drive.anybits?(DRIVE_ATNA) && (atn || drive.nobits?(DRIVE_ATN_GATED))
141
+ end
142
+
143
+ def clk_low? = low_lines.anybits?(CLK)
144
+
145
+ def data_low? = low_lines.anybits?(DATA)
146
+
147
+ # CIA 2's port A pins as the bus leaves them: CLK IN and DATA IN low
148
+ # while their lines are.
149
+ def read_a(_port_a, _port_b)
150
+ low = low_lines
151
+ value = 0xff
152
+ value &= ~HOST_CLK_IN if low.anybits?(CLK)
153
+ value &= ~HOST_DATA_IN if low.anybits?(DATA)
154
+ value
155
+ end
156
+
157
+ # Port B goes to the user port, not the serial bus.
158
+ def read_b(_port_a, _port_b) = 0xff
159
+ end
160
+ end
161
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ # The drives and the serial bus badline/core's machines share. The C128's
4
+ # 1571 stays out of the core: badline/c128 requires it.
5
+ require "badline/drives/datasette"
6
+ require "badline/drives/serial_bus"
7
+ require "badline/drives/model1541"
8
+ require "badline/drives/model1581"
@@ -21,7 +21,7 @@ module Badline
21
21
  @timeline = timeline
22
22
  @snapshots = Snapshots.new(computer, options)
23
23
  @screens = Screens.new(computer)
24
- @led = DriveLed.for(computer)
24
+ @led = DriveLed.new(computer)
25
25
  @controls = Controls.new(computer)
26
26
  @window = MachineWindow.new(@screens.screen, vsync: @pacer.vsync?)
27
27
  @pacer.fit(@window.refresh_rate) if @pacer.vsync?
@@ -128,7 +128,7 @@ module Badline
128
128
  @computer = computer
129
129
  @screens.computer = computer
130
130
  fit_window
131
- @led = DriveLed.for(computer)
131
+ @led = DriveLed.new(computer)
132
132
  @controls.computer = computer
133
133
  @gamepads.computer = computer
134
134
  @sound.switch(computer.sound_source, computer.timing.clock_hz)
@@ -220,7 +220,7 @@ module Badline
220
220
  renderer = @window.renderer
221
221
  SDL.SDL_RenderClear(renderer)
222
222
  SDL.SDL_RenderCopy(renderer, @window.texture, nil, nil)
223
- @led&.draw(renderer, @window.width, @window.height)
223
+ @led.draw(renderer, @window.width, @window.height)
224
224
  @timeline.screenshots(@frames + 1).each { |path| Screenshot.write(renderer, path) }
225
225
  SDL.SDL_RenderPresent(renderer)
226
226
  end
@@ -10,7 +10,7 @@ module Badline
10
10
  # attach, an --at key names nothing or an event fails, which it warns
11
11
  # about.
12
12
  def self.run(options)
13
- error = Timeline.error(options.timeline)
13
+ error = Timeline.error(options.timeline, options.family)
14
14
  unless error.empty?
15
15
  warn "#{options.program}: #{error}"
16
16
  return 1
@@ -33,9 +33,9 @@ module Badline
33
33
  def self.media_problem
34
34
  yield
35
35
  ""
36
- rescue Machine::Unsupported, Media::TrueDrive::Error, Media::DiskList::Error, Media::NotDisk, Datasette::Missing,
37
- SystemCallError, Storage::SIDFile::FormatError, Storage::T64::FormatError, Storage::TAP::FormatError,
38
- Storage::CRTFile::FormatError, Storage::G64Image::FormatError, Cartridge::UnsupportedTypeError,
36
+ rescue Machine::Unsupported, Media::TrueDrive::Error, Media::DiskList::Error, Media::NotDisk, Drives::Datasette::Missing,
37
+ SystemCallError, SIDTune::File::FormatError, Storage::T64::FormatError, Storage::TAP::FormatError,
38
+ Storage::CRTFile::FormatError, Storage::G64Image::FormatError, C64::Cartridge::UnsupportedTypeError,
39
39
  Snapshot::FormatError => e
40
40
  e.message
41
41
  end
@@ -29,6 +29,7 @@ module Badline
29
29
  @arrows_port = 2
30
30
  @mouse = Input::Mouse1351.new
31
31
  @paddles = Input::Paddles.new
32
+ @held = {}
32
33
  end
33
34
 
34
35
  # Routes the input to another machine, the pot device going into its
@@ -73,10 +74,8 @@ module Badline
73
74
  def key(scancode, down)
74
75
  if joystick_mode? && Keys.joystick?(scancode)
75
76
  joystick_key(scancode, down)
76
- elsif @computer.family == :c128
77
- c128_key(Keys.c128_key(scancode, @computer.mode), down)
78
77
  else
79
- c64_key(Keys.c64_key(scancode), down)
78
+ machine_key(scancode, down)
80
79
  end
81
80
  end
82
81
 
@@ -104,6 +103,23 @@ module Badline
104
103
 
105
104
  private
106
105
 
106
+ # A host key lets go of the key it pressed, though the C128 has
107
+ # switched between C64 and C128 mode since and maps it elsewhere.
108
+ def machine_key(scancode, down)
109
+ @held[scancode] = host_key(scancode) if down || !@held.key?(scancode)
110
+ key = @held[scancode]
111
+ @held.delete(scancode) unless down
112
+ if @computer.family == :c128
113
+ c128_key(key, down)
114
+ else
115
+ c64_key(key, down)
116
+ end
117
+ end
118
+
119
+ def host_key(scancode)
120
+ @computer.family == :c128 ? Keys.c128_key(scancode, @computer.mode) : Keys.c64_key(scancode)
121
+ end
122
+
107
123
  # RESTORE isn't in the key matrix, so it goes to the machine instead.
108
124
  def c64_key(key, down)
109
125
  if key == :restore
@@ -2,48 +2,71 @@
2
2
 
3
3
  module Badline
4
4
  module Frontend
5
- # The true drive's red LED, drawn over the bottom right corner of the
6
- # border: bright while the DOS lights it, as it does while a file is
7
- # open or after an error, and dim otherwise.
5
+ # The true drive's LEDs, drawn over the bottom right corner of the
6
+ # border: the red activity LED, bright while the DOS lights it, as it
7
+ # does while a file is open or after an error, and dim otherwise, and
8
+ # left of it the green power LED, which a 1581 flashes after an error.
9
+ # They follow whichever true drive the machine has, one plugged in
10
+ # after start among them, and are drawn only while it has one.
8
11
  class DriveLed
9
12
  WIDTH = 12
10
13
  HEIGHT = 4
11
14
 
15
+ # The power LED's distance left of the activity LED.
16
+ POWER_OFFSET = WIDTH + 4
17
+
12
18
  LIT = [0xff, 0x20, 0x20].freeze
13
19
  DARK = [0x40, 0x00, 0x00].freeze
20
+ POWER_LIT = [0x20, 0xff, 0x20].freeze
21
+ POWER_DARK = [0x00, 0x40, 0x00].freeze
14
22
 
15
- # The LED of the machine's true drive, or nil without one.
16
- def self.for(computer)
17
- drive = computer.true_drive
18
- drive ? new(drive) : nil
19
- end
20
-
21
- # Sets SDL.led_rect to where the LED goes on a screen `width` by
22
- # `height`.
23
- def self.place(width, height)
23
+ # Sets SDL.led_rect to where the activity LED goes on a screen `width`
24
+ # by `height`, or with `offset` that many pixels left of it.
25
+ def self.place(width, height, offset = 0)
24
26
  rect = SDL.led_rect
25
- SDL.rect_x(rect, width - WIDTH - 8)
27
+ SDL.rect_x(rect, width - WIDTH - 8 - offset)
26
28
  SDL.rect_y(rect, height - HEIGHT - 6)
27
29
  SDL.rect_w(rect, WIDTH)
28
30
  SDL.rect_h(rect, HEIGHT)
29
31
  end
30
32
 
31
- def initialize(drive)
32
- @drive = drive
33
+ def initialize(computer)
34
+ @computer = computer
33
35
  end
34
36
 
35
- def lit? = @drive.led_on?
37
+ # Whether the machine has a true drive, and so LEDs to draw.
38
+ def shown? = !@computer.true_drive.nil?
39
+
40
+ def lit?
41
+ drive = @computer.true_drive
42
+ drive.nil? ? false : drive.led_on?
43
+ end
44
+
45
+ def power_lit?
46
+ drive = @computer.true_drive
47
+ drive.nil? ? false : drive.power_led_on?
48
+ end
36
49
 
37
50
  def color = lit? ? LIT : DARK
38
51
 
39
- # Fills the LED's rectangle on a screen `width` by `height` in its
40
- # colour, then puts back the black that RenderClear clears with.
52
+ def power_color = power_lit? ? POWER_LIT : POWER_DARK
53
+
54
+ # Fills the LEDs' rectangles on a screen `width` by `height` in their
55
+ # colours, then puts back the black that RenderClear clears with.
41
56
  def draw(renderer, width, height)
42
- DriveLed.place(width, height)
43
- rgb = color
57
+ return unless shown?
58
+
59
+ fill(renderer, width, height, 0, color)
60
+ fill(renderer, width, height, POWER_OFFSET, power_color)
61
+ SDL.SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255)
62
+ end
63
+
64
+ private
65
+
66
+ def fill(renderer, width, height, offset, rgb)
67
+ DriveLed.place(width, height, offset)
44
68
  SDL.SDL_SetRenderDrawColor(renderer, rgb[0], rgb[1], rgb[2], 255)
45
69
  SDL.SDL_RenderFillRect(renderer, SDL.led_rect)
46
- SDL.SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255)
47
70
  end
48
71
  end
49
72
  end
@@ -17,7 +17,8 @@ module Badline
17
17
  attr_reader :renderer, :texture
18
18
 
19
19
  # Opens the window for `screen`, presenting in step with the display
20
- # when `vsync`.
20
+ # when `vsync`. Text input stays off, so a dead key such as ´ on a Mac
21
+ # arrives as a key press, as every other key does.
21
22
  def initialize(screen, vsync:)
22
23
  abort "SDL_Init: #{SDL.SDL_GetError}" unless SDL.SDL_Init(SDL::INIT_VIDEO | SDL::INIT_EVENTS).zero?
23
24
 
@@ -28,6 +29,7 @@ module Badline
28
29
  TITLE, SDL::WINDOWPOS_CENTERED, SDL::WINDOWPOS_CENTERED, @width * SCALE, @height * SCALE,
29
30
  SDL::WINDOW_RESIZABLE
30
31
  )
32
+ SDL.SDL_StopTextInput
31
33
  flags = SDL::RENDERER_ACCELERATED
32
34
  flags |= SDL::RENDERER_PRESENTVSYNC if vsync
33
35
  @renderer = SDL.SDL_CreateRenderer(@window, -1, flags)
@@ -8,8 +8,8 @@ module Badline
8
8
  # name. A disk or tape picked goes straight in, and a save loads.
9
9
  class MenuDialogs
10
10
  EXTENSIONS = {
11
- disk: %w[.d64 .d71 .d81 .g64 .t64 .m3u .vfl], tape: %w[.tap], cartridge: %w[.crt],
12
- program: %w[.d64 .d71 .d81 .g64 .t64 .m3u .vfl .tap .crt .prg .p00 .sid], snapshot: %w[.vsf]
11
+ disk: Media::Extensions::DEVICE8, tape: Media::Extensions.of(%i[tape]),
12
+ cartridge: Media::Extensions.of(%i[cartridge]), program: Media::Extensions::ALL, snapshot: %w[.vsf]
13
13
  }.freeze
14
14
  TITLES = { disk: "INSERT A DISK", tape: "INSERT A TAPE", cartridge: "INSERT A CARTRIDGE",
15
15
  program: "QUICK OPEN", snapshot: "LOAD A SAVE" }.freeze
@@ -31,7 +31,7 @@ module Badline
31
31
  cutoff = (history.register(frame, 0x15) & 0x07) | (history.register(frame, 0x16) << 3)
32
32
  resonance = history.register(frame, 0x17) >> 4
33
33
  mode_volume = history.register(frame, 0x18)
34
- hertz = SID::Filter::W0.fetch(model)[cutoff] / (2 * Math::PI * SID::Filter::SCALE)
34
+ hertz = Chips::SID::Filter::W0.fetch(model)[cutoff] / (2 * Math::PI * Chips::SID::Filter::SCALE)
35
35
  paint = @painter
36
36
  paint.text(LEFT, top, "FILTER", BRIGHT)
37
37
  x = LEFT + 64
@@ -106,7 +106,7 @@ module Badline
106
106
  # Keeps the subtune's STIL credits for the header and the whole entry
107
107
  # for the INFO view.
108
108
  def stil(fields, subtune, subtune_fields)
109
- @credits = Storage::STIL::Credit.list(subtune_fields.empty? ? fields : subtune_fields)
109
+ @credits = SIDTune::STIL::Credit.list(subtune_fields.empty? ? fields : subtune_fields)
110
110
  @stil = fields
111
111
  @stil_subtune = subtune
112
112
  @subtune_stil = subtune_fields
@@ -211,7 +211,7 @@ module Badline
211
211
 
212
212
  def draw
213
213
  @next_draw = now + FRAME
214
- @screen.draw(@state, @player, @history, @tune_file, Storage::STIL::Credit.at(@credits, @state.played))
214
+ @screen.draw(@state, @player, @history, @tune_file, SIDTune::STIL::Credit.at(@credits, @state.played))
215
215
  end
216
216
 
217
217
  def now = Process.clock_gettime(Process::CLOCK_MONOTONIC)
@@ -31,6 +31,7 @@ module Badline
31
31
  SDL.SDL_SetHint("SDL_RENDER_SCALE_QUALITY", "0")
32
32
  @window = SDL.SDL_CreateWindow(TITLE, SDL::WINDOWPOS_CENTERED, SDL::WINDOWPOS_CENTERED,
33
33
  WIDTH * SCALE, height(view) * SCALE, SDL::WINDOW_RESIZABLE)
34
+ SDL.SDL_StopTextInput
34
35
  @renderer = SDL.SDL_CreateRenderer(@window, -1, SDL::RENDERER_ACCELERATED)
35
36
  SDL.SDL_RenderSetLogicalSize(@renderer, WIDTH, height(view))
36
37
  build
@@ -6,13 +6,13 @@ module Badline
6
6
  # C64's palette: one for each voice, and light blue on blue as on the
7
7
  # machine's screen.
8
8
  module PlayerTheme
9
- TEXT = VIC::PALETTE[14]
10
- BRIGHT = VIC::PALETTE[1]
11
- DIM = VIC::PALETTE[11]
12
- BOX = VIC::PALETTE[0]
13
- BACKGROUND = VIC::PALETTE[6]
14
- WARNING = VIC::PALETTE[10]
15
- VOICE_COLORS = [VIC::PALETTE[7], VIC::PALETTE[13], VIC::PALETTE[3]].freeze
9
+ TEXT = Chips::VIC::PALETTE[14]
10
+ BRIGHT = Chips::VIC::PALETTE[1]
11
+ DIM = Chips::VIC::PALETTE[11]
12
+ BOX = Chips::VIC::PALETTE[0]
13
+ BACKGROUND = Chips::VIC::PALETTE[6]
14
+ WARNING = Chips::VIC::PALETTE[10]
15
+ VOICE_COLORS = [Chips::VIC::PALETTE[7], Chips::VIC::PALETTE[13], Chips::VIC::PALETTE[3]].freeze
16
16
  end
17
17
  end
18
18
  end