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,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ module Media
5
+ # A program loading into a C64 or a C128 once it has booted
6
+ # (Media.attach). The VIC-20 has its own (Vic20Basic).
7
+ module Program
8
+ class << self
9
+ # Loads the program where it says, and RUNs one that loads at the
10
+ # start of BASIC.
11
+ def load(computer, data, autostart:)
12
+ load_addr = computer.load_prg(data)
13
+ # Run only makes sense for programs at BASIC start, or a byte ahead
14
+ # of it, where BASIC keeps the zero before its first line. BASIC 7.0
15
+ # starts at $1C01 and keeps the end of its text at $1210.
16
+ start, text_end = computer.family == :c128 && computer.mode == :c128 ? [0x1c01, 0x1210] : [BASIC_START, 0x2d]
17
+ return unless autostart && (load_addr == start || load_addr == start - 1)
18
+
19
+ # The end of the program, where BASIC's variables start, and where
20
+ # the KERNAL's LOAD leaves its end address.
21
+ end_addr = load_addr + data.length - 2
22
+ [text_end, 0xae].each { |pointer| computer.ram.write(pointer, [end_addr & 0xff, end_addr >> 8]) }
23
+ computer.type_text("run\r")
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -9,7 +9,7 @@ module Badline
9
9
  # disk's raw GCR, which only a true drive reads, so it plugs one in
10
10
  # when there's none yet. The 1541 reads only .d64 and .g64 images, the
11
11
  # 1571 .d71 and .g71 as well, and the 1581 only .d81, so a .d81 swaps
12
- # the drive on device 8 for a 1581 (Drive1581::Slot), and another disk
12
+ # the drive on device 8 for a 1581 (Drives::Model1581::Slot), and another disk
13
13
  # image swaps a 1581 back for the machine's own drive. The traps' other
14
14
  # disk media, a .t64 or a host directory, raise Error.
15
15
  module TrueDrive
@@ -32,9 +32,10 @@ module Badline
32
32
  # other disk media, which the traps would mount, when a true drive
33
33
  # is device 8.
34
34
  def takes?(computer, path)
35
- return true if %w[.g64 .g71].include?(File.extname(path).downcase)
35
+ kind = Extensions.kind(path)
36
+ return true if kind == :gcr
36
37
 
37
- !drive(computer).nil? && (File.directory?(path) || MOUNT_TYPES.key?(File.extname(path).downcase))
38
+ !drive(computer).nil? && (File.directory?(path) || %i[disk archive].include?(kind))
38
39
  end
39
40
 
40
41
  # Puts the image in the drive on device 8, plugging one in that
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ module Media
5
+ # A .sid tune on a machine with a SID (Media.attach): its data and the
6
+ # player the tune boots with go in RAM once the KERNAL has booted, and
7
+ # the autostart types what starts it.
8
+ module Tune
9
+ class << self
10
+ # Plays `subtune`, or the tune's own start subtune, and says what
11
+ # plays. A tune for more than one SID raises FormatError.
12
+ def attach(computer, path, autostart:, subtune:)
13
+ tune = SIDTune::File.new(path)
14
+ if tune.sids > 1
15
+ raise SIDTune::File::FormatError, "Written for #{tune.sids} SIDs, which only the SID player plays"
16
+ end
17
+
18
+ subtune = (subtune || tune.start_subtune).clamp(1, tune.subtunes)
19
+ computer.on_init { start(computer, tune, autostart:, subtune:) }
20
+ title = tune.name.empty? ? path : tune.name
21
+ title += " (subtune #{subtune})" if tune.subtunes > 1
22
+ autostart ? "Playing #{title}" : "Loaded #{title}"
23
+ end
24
+
25
+ private
26
+
27
+ def start(computer, tune, autostart:, subtune:)
28
+ computer.ram.write(tune.load_address, tune.data)
29
+ tune.boot_memory(subtune:).each { |address, bytes| computer.ram.write(address, bytes) }
30
+ computer.type_text(tune.boot_command) if autostart
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -26,12 +26,6 @@ module Badline
26
26
  parts.map { |part| rom_chip(part) }
27
27
  end
28
28
 
29
- # A program's bytes, unwrapped from a .p00.
30
- def program_bytes(path)
31
- bytes = File.binread(path).bytes
32
- Storage::P00.wraps?(bytes) ? Storage::P00.data(bytes) : bytes
33
- end
34
-
35
29
  private
36
30
 
37
31
  def load_address(data)
@@ -42,7 +36,7 @@ module Badline
42
36
  name = part_names(path, own, address).find { |candidate| File.file?(candidate) }
43
37
  return unless name
44
38
 
45
- part = program_bytes(name)
39
+ part = Media.program_bytes(name)
46
40
  part if load_address(part) == address
47
41
  end
48
42
 
@@ -2,45 +2,25 @@
2
2
 
3
3
  module Badline
4
4
  module Media
5
- # Media on a VIC-20. A program loads once the KERNAL has booted: a
6
- # BASIC program at the start of BASIC, wherever the RAM puts it, and
7
- # RUNs. A program that loads into BLK5 at $A000 is a cartridge's ROM
8
- # instead, as are the parts of a set that includes one
9
- # (Vic20Cartridge), and goes in with a power cycle, as a .crt does.
10
- # Disks mount on device 8 through the KERNAL traps, or go in a true
11
- # 1541 (TrueDrive), and tapes play on the datasette.
5
+ # What a VIC-20 does with media of its own (Media.attach). A program
6
+ # loads once the KERNAL has booted: a BASIC program at the start of
7
+ # BASIC, wherever the RAM puts it, and RUNs. A program that loads into
8
+ # BLK5 at $A000 is a cartridge's ROM instead, as are the parts of a set
9
+ # that includes one (Vic20Cartridge), and goes in with a power cycle,
10
+ # as a .crt does. Disks mount on device 8 through the KERNAL traps, or
11
+ # go in a true 1541 (TrueDrive), and tapes play on the datasette.
12
12
  module Vic20Media
13
13
  # The expansions that fill BLK1, BLK2 and BLK3 one block after
14
14
  # another, each with the last address it fills.
15
15
  BLOCK_EXPANSIONS = [[0x3fff, :"8k"], [0x5fff, :"16k"], [0x7fff, :"24k"]].freeze
16
16
 
17
17
  # What a VIC-20 doesn't take.
18
- UNSUPPORTED = %w[.sid].freeze
18
+ UNSUPPORTED = Extensions.of(%i[tune])
19
19
 
20
20
  # The platform byte of a VIC-20 tape (Storage::TAP#platform).
21
21
  TAPE_PLATFORM = 1
22
22
 
23
23
  class << self
24
- def attach(machine, path, autostart:, **options)
25
- extension = File.extname(path).downcase
26
- raise ArgumentError, "#{path} doesn't go in a VIC-20" if UNSUPPORTED.include?(extension)
27
-
28
- if TrueDrive.takes?(machine, path)
29
- attach_true_drive(machine, path, options.fetch(:disk, {}), autostart:)
30
- elsif File.directory?(path)
31
- machine.mount(Storage::HostDirectory.new(path))
32
- "Mounted #{path} as device 8"
33
- elsif extension == ".crt"
34
- attach_cartridge(machine, path, Storage::CRTFile.new(path, machine: :vic20).chips)
35
- elsif extension == ".tap"
36
- attach_tape(machine, path, autostart:)
37
- elsif MOUNT_TYPES.key?(extension)
38
- attach_storage(machine, path, options.fetch(:disk, {}), autostart:)
39
- else
40
- attach_prg(machine, path, autostart:)
41
- end
42
- end
43
-
44
24
  # The RAM expansion (Vic20::Bus::RAM_CONFIGURATIONS) a VIC-20 needs
45
25
  # for the program at `path`, or for the first program on the disk
46
26
  # or in the directory there, by where it loads. A BASIC program
@@ -60,72 +40,50 @@ module Badline
60
40
  end
61
41
  end
62
42
 
63
- private
64
-
65
- def expansion_reaching(last)
66
- BLOCK_EXPANSIONS.each { |reach, ram| return ram if last <= reach }
67
- BLOCK_EXPANSIONS.last.last
68
- end
69
-
43
+ # The program at `path`, or the first on the disk or in the
44
+ # directory there, or nil.
70
45
  def first_program(path)
71
- extension = File.extname(path).downcase
72
- if File.directory?(path) || MOUNT_TYPES.key?(extension)
73
- open_storage(path).read_file("*")
74
- elsif File.file?(path) && !%w[.sid .crt .tap .g64].include?(extension)
75
- Vic20Cartridge.program_bytes(path)
46
+ kind = Extensions.kind(path)
47
+ if File.directory?(path) || %i[disk archive].include?(kind)
48
+ Media.open_storage(path).read_file("*")
49
+ elsif File.file?(path) && !%i[tune cartridge tape gcr].include?(kind)
50
+ Media.program_bytes(path)
76
51
  end
77
52
  end
78
53
 
79
- def open_storage(path, disk = {})
80
- return Storage::HostDirectory.new(path) if File.directory?(path)
81
-
82
- storage = MOUNT_TYPES.fetch(File.extname(path).downcase)
83
- storage < Storage::DiskImage ? storage.new(path, **disk) : storage.new(path)
84
- end
85
-
86
- def attach_cartridge(machine, path, chips)
87
- machine.attach_cartridge(chips)
88
- "Attached cartridge #{path}"
89
- end
90
-
91
- # A tape made for another machine plays all the same, and says so.
92
- def attach_tape(machine, path, autostart:)
93
- tape = Storage::TAP.new(path)
94
- machine.datasette.insert(tape)
95
- machine.datasette.play!
96
- machine.type_text(TAPE_AUTOSTART) if autostart
97
- message = "Inserted #{path} in the datasette"
98
- tape.platform == TAPE_PLATFORM ? message : "#{message}, a tape for another machine than the VIC-20"
99
- end
100
-
101
- def attach_true_drive(machine, path, disk, autostart:)
102
- message = TrueDrive.insert(machine, path, read_only: disk.fetch(:read_only, false))
103
- machine.type_text(disk_autostart(first_program(path))) if autostart
104
- message
105
- end
106
-
107
- def attach_storage(machine, path, disk, autostart:)
108
- storage = open_storage(path, disk)
109
- machine.mount(storage)
110
- machine.type_text(disk_autostart(storage.read_file("*"))) if autostart
111
- "Mounted #{path} as device 8"
112
- end
113
-
114
- # A BASIC program loads relocated to the start of BASIC, and anything
115
- # else where it says.
54
+ # What loads and runs `data`, the first program on the disk in
55
+ # device 8: a BASIC program loads relocated to the start of BASIC,
56
+ # and anything else where it says.
116
57
  def disk_autostart(data)
117
58
  basic = data && data.length > 2 && Vic20Basic::BASIC_STARTS.key?(data[0] | (data[1] << 8))
118
59
  basic ? %(lO"*",8\rrun\r) : AUTOSTART
119
60
  end
120
61
 
121
- def attach_prg(machine, path, autostart:)
122
- bytes = Vic20Cartridge.program_bytes(path)
62
+ # Loads the program, `bytes` from the file at `path`, as Vic20Basic
63
+ # has it once the KERNAL has booted, or attaches the cartridge it's
64
+ # a part of (Vic20Cartridge), and says which.
65
+ def attach_program(machine, path, bytes, autostart:)
123
66
  chips = Vic20Cartridge.chips(path, bytes)
124
- return attach_cartridge(machine, path, chips) if chips
67
+ if chips
68
+ machine.attach_cartridge(chips)
69
+ return "Attached cartridge #{path}"
70
+ end
125
71
 
126
72
  machine.on_init { Vic20Basic.load_program(machine, bytes, autostart:) }
127
73
  "Loading #{path}"
128
74
  end
75
+
76
+ # A tape made for another machine plays all the same, and says so.
77
+ def tape_message(tape, message)
78
+ tape.platform == TAPE_PLATFORM ? message : "#{message}, a tape for another machine than the VIC-20"
79
+ end
80
+
81
+ private
82
+
83
+ def expansion_reaching(last)
84
+ BLOCK_EXPANSIONS.each { |reach, ram| return ram if last <= reach }
85
+ BLOCK_EXPANSIONS.last.last
86
+ end
129
87
  end
130
88
  end
131
89
  end
data/lib/badline/media.rb CHANGED
@@ -1,14 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "badline/media/extensions"
3
4
  require "badline/media/true_drive"
4
5
  require "badline/media/boot_disk"
5
6
  require "badline/media/not_disk"
6
- require "badline/media/queue"
7
+ require "badline/audio/queue"
7
8
  require "badline/media/disk_set"
8
9
  require "badline/media/disk_list"
9
10
  require "badline/media/vic20_basic"
10
11
  require "badline/media/vic20_cartridge"
11
12
  require "badline/media/vic20_media"
13
+ require "badline/media/tune"
14
+ require "badline/media/program"
12
15
 
13
16
  module Badline
14
17
  module Media
@@ -16,6 +19,7 @@ module Badline
16
19
  TAPE_AUTOSTART = %(lO\rrun\r)
17
20
  BASIC_START = 0x0801
18
21
 
22
+ # The storage that opens each of Extensions::MOUNTABLE.
19
23
  MOUNT_TYPES = {
20
24
  ".d64" => Storage::D64Image,
21
25
  ".d71" => Storage::D71Image,
@@ -38,13 +42,17 @@ module Badline
38
42
  # KERNAL to boot at power-on, with nothing typed (BootDisk).
39
43
  #
40
44
  # An .m3u or .vfl list of disks attaches the first disk it lists
41
- # (DiskList). The VIC-20 takes its own media (Vic20Media), and the
42
- # C128 in C64 mode a C64's.
45
+ # (DiskList). Every machine takes each kind of medium the same way,
46
+ # except that the VIC-20 (Vic20Media) has cartridges, programs and a
47
+ # disk autostart of its own, and takes no .sid tunes. The C128 in C64
48
+ # mode takes a C64's.
43
49
  def attach(computer, path, autostart: true, subtune: nil, **)
44
50
  path = DiskList.disk(path)
45
- return Vic20Media.attach(computer, path, autostart:, **) if computer.family == :vic20
51
+ if computer.family == :vic20 && Vic20Media::UNSUPPORTED.include?(File.extname(path).downcase)
52
+ raise ArgumentError, "#{path} doesn't go in a VIC-20"
53
+ end
46
54
 
47
- BootDisk.attach(computer, path, **) || attach_c64(computer, path, autostart:, subtune:, **)
55
+ BootDisk.attach(computer, path, **) || attach_kind(computer, path, autostart:, subtune:, **)
48
56
  end
49
57
 
50
58
  # Swaps the disk in device 8 for a disk image or a host directory,
@@ -73,109 +81,104 @@ module Badline
73
81
  # The SID a machine for `path` should be built with. A .sid tune names
74
82
  # its own; everything else gets `otherwise`.
75
83
  def sid_model(path, otherwise: :mos6581)
76
- return otherwise unless path && File.extname(path).downcase == ".sid"
84
+ return otherwise unless path && Extensions.kind(path) == :tune
77
85
 
78
- Storage::SIDFile.new(path).sid_model
86
+ SIDTune::File.new(path).sid_model
79
87
  end
80
88
 
81
- private
89
+ # What the medium at `path` is to `computer`, which attach goes by:
90
+ # :true_drive for a disk that goes in a true drive (TrueDrive),
91
+ # :directory, :storage for a disk image or a .t64 that the traps
92
+ # mount, and otherwise its kind by its extension (Extensions).
93
+ def kind(computer, path)
94
+ return :true_drive if TrueDrive.takes?(computer, path)
95
+ return :directory if File.directory?(path)
82
96
 
83
- def attach_c64(computer, path, autostart:, subtune:, **options)
84
- if TrueDrive.takes?(computer, path)
85
- attach_true_drive(computer, path, options.fetch(:disk, {}), autostart:)
86
- elsif File.directory?(path)
87
- computer.mount(Storage::HostDirectory.new(path))
88
- "Mounted #{path} as device 8"
89
- elsif File.extname(path).downcase == ".crt"
90
- attach_cartridge(computer, path, options.fetch(:cartridge, {}))
91
- elsif File.extname(path).downcase == ".sid"
92
- attach_sid(computer, path, autostart:, subtune:)
93
- elsif File.extname(path).downcase == ".tap"
94
- attach_tape(computer, path, autostart:)
95
- elsif MOUNT_TYPES.key?(File.extname(path).downcase)
96
- attach_storage(computer, path, options.fetch(:disk, {}), autostart:)
97
- else
98
- attach_prg(computer, path, autostart:)
99
- end
97
+ kind = Extensions.kind(path)
98
+ %i[disk archive].include?(kind) ? :storage : kind
100
99
  end
101
100
 
102
- def disk?(path)
103
- storage = MOUNT_TYPES[File.extname(path).downcase]
104
- File.directory?(path) || (!storage.nil? && storage < Storage::DiskImage)
105
- end
106
-
107
- # Disk images take the `disk` options. A .t64 is read-only whatever
108
- # it's given.
109
- def open_storage(path, disk)
101
+ # What device 8 serves through the traps for a host directory, a disk
102
+ # image or a .t64. Disk images take the `disk` options. A .t64 is
103
+ # read-only whatever it's given.
104
+ def open_storage(path, disk = {})
110
105
  return Storage::HostDirectory.new(path) if File.directory?(path)
111
106
 
112
- storage = MOUNT_TYPES[File.extname(path).downcase]
107
+ storage = MOUNT_TYPES.fetch(File.extname(path).downcase)
113
108
  storage < Storage::DiskImage ? storage.new(path, **disk) : storage.new(path)
114
109
  end
115
110
 
116
- def attach_cartridge(computer, path, options)
117
- computer.attach_cartridge(Cartridge.from_file(path, **options))
118
- "Attached cartridge #{path}"
111
+ # A program file's bytes, unwrapped from a .p00.
112
+ def program_bytes(path)
113
+ bytes = File.binread(path).bytes
114
+ Storage::P00.wraps?(bytes) ? Storage::P00.data(bytes) : bytes
119
115
  end
120
116
 
121
- def attach_sid(computer, path, autostart:, subtune:)
122
- tune = Storage::SIDFile.new(path)
123
- if tune.sids > 1
124
- raise Storage::SIDFile::FormatError, "Written for #{tune.sids} SIDs, which only the SID player plays"
125
- end
126
-
127
- subtune = (subtune || tune.start_subtune).clamp(1, tune.subtunes)
128
- computer.on_init { start_tune(computer, tune, autostart:, subtune:) }
129
- title = tune.name.empty? ? path : tune.name
130
- title += " (subtune #{subtune})" if tune.subtunes > 1
131
- autostart ? "Playing #{title}" : "Loaded #{title}"
132
- end
117
+ private
133
118
 
134
- def start_tune(computer, tune, autostart:, subtune:)
135
- computer.ram.write(tune.load_address, tune.data)
136
- tune.boot_memory(subtune:).each { |address, bytes| computer.ram.write(address, bytes) }
137
- computer.type_text(tune.boot_command) if autostart
119
+ def attach_kind(computer, path, autostart:, subtune:, **options)
120
+ disk = options.fetch(:disk, {})
121
+ case kind(computer, path)
122
+ when :true_drive then attach_true_drive(computer, path, disk, autostart:)
123
+ when :directory then mount(computer, path, disk, autostart: false)
124
+ when :storage then mount(computer, path, disk, autostart:)
125
+ when :cartridge then attach_cartridge(computer, path, options.fetch(:cartridge, {}))
126
+ when :tune then Tune.attach(computer, path, autostart:, subtune:)
127
+ when :tape then attach_tape(computer, path, autostart:)
128
+ else attach_prg(computer, path, autostart:)
129
+ end
138
130
  end
139
131
 
140
- def attach_tape(computer, path, autostart:)
141
- computer.datasette.insert(Storage::TAP.new(path))
142
- computer.datasette.play!
143
- computer.type_text(TAPE_AUTOSTART) if autostart
144
- "Inserted #{path} in the datasette"
145
- end
132
+ def disk?(path) = File.directory?(path) || Extensions.kind(path) == :disk
146
133
 
147
134
  def attach_true_drive(computer, path, disk, autostart:)
148
135
  message = TrueDrive.insert(computer, path, read_only: disk.fetch(:read_only, false))
149
- computer.type_text(AUTOSTART) if autostart
136
+ computer.type_text(disk_autostart(computer) { Vic20Media.first_program(path) }) if autostart
150
137
  message
151
138
  end
152
139
 
153
- def attach_storage(computer, path, disk, autostart:)
154
- computer.mount(open_storage(path, disk))
155
- computer.type_text(AUTOSTART) if autostart
140
+ def mount(computer, path, disk, autostart:)
141
+ storage = open_storage(path, disk)
142
+ computer.mount(storage)
143
+ computer.type_text(disk_autostart(computer) { storage.read_file("*") }) if autostart
156
144
  "Mounted #{path} as device 8"
157
145
  end
158
146
 
159
- def attach_prg(computer, path, autostart:)
160
- bytes = File.binread(path).bytes
161
- bytes = Storage::P00.data(bytes) if Storage::P00.wraps?(bytes)
162
- computer.on_init { start_prg(computer, bytes, autostart:) }
163
- "Loading #{path}"
147
+ # What loads and runs the first program on the disk in device 8. A
148
+ # VIC-20 goes by that program, which the block gives
149
+ # (Vic20Media.disk_autostart).
150
+ def disk_autostart(computer)
151
+ computer.family == :vic20 ? Vic20Media.disk_autostart(yield) : AUTOSTART
152
+ end
153
+
154
+ # A VIC-20 cartridge is its ROM chips.
155
+ def attach_cartridge(computer, path, options)
156
+ if computer.family == :vic20
157
+ computer.attach_cartridge(Storage::CRTFile.new(path, machine: :vic20).chips)
158
+ else
159
+ computer.attach_cartridge(C64::Cartridge.from_file(path, **options))
160
+ end
161
+ "Attached cartridge #{path}"
164
162
  end
165
163
 
166
- def start_prg(computer, data, autostart:)
167
- load_addr = computer.load_prg(data)
168
- # Run only makes sense for programs at BASIC start, or a byte ahead
169
- # of it, where BASIC keeps the zero before its first line. BASIC 7.0
170
- # starts at $1C01 and keeps the end of its text at $1210.
171
- start, text_end = computer.family == :c128 && computer.mode == :c128 ? [0x1c01, 0x1210] : [BASIC_START, 0x2d]
172
- return unless autostart && (load_addr == start || load_addr == start - 1)
173
-
174
- # The end of the program, where BASIC's variables start, and where
175
- # the KERNAL's LOAD leaves its end address.
176
- end_addr = load_addr + data.length - 2
177
- [text_end, 0xae].each { |pointer| computer.ram.write(pointer, [end_addr & 0xff, end_addr >> 8]) }
178
- computer.type_text("run\r")
164
+ # A VIC-20 says when the tape was made for another machine
165
+ # (Vic20Media.tape_message).
166
+ def attach_tape(computer, path, autostart:)
167
+ tape = Storage::TAP.new(path)
168
+ computer.datasette.insert(tape)
169
+ computer.datasette.play!
170
+ computer.type_text(TAPE_AUTOSTART) if autostart
171
+ message = "Inserted #{path} in the datasette"
172
+ computer.family == :vic20 ? Vic20Media.tape_message(tape, message) : message
173
+ end
174
+
175
+ # A VIC-20 has programs of its own (Vic20Media.attach_program).
176
+ def attach_prg(computer, path, autostart:)
177
+ bytes = program_bytes(path)
178
+ return Vic20Media.attach_program(computer, path, bytes, autostart:) if computer.family == :vic20
179
+
180
+ computer.on_init { Program.load(computer, bytes, autostart:) }
181
+ "Loading #{path}"
179
182
  end
180
183
  end
181
184
  end
data/lib/badline/model.rb CHANGED
@@ -7,7 +7,7 @@ module Badline
7
7
  # each was sold with, its video standard, its KERNAL and whether it has
8
8
  # a datasette. The names are x64sc's.
9
9
  module Model
10
- # Each field but the name is a keyword Computer.new takes.
10
+ # Each field but the name is a keyword C64.new takes.
11
11
  Profile = Data.define(:name, :vic_model, :cia_model, :sid_model, :region, :kernal, :datasette, :board)
12
12
 
13
13
  # The breadbin PAL C64: the 6569 VIC-II, 6526 CIAs and the 6581 SID.
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "badline/media/extensions"
4
+
3
5
  module Badline
4
6
  class Options
5
7
  # One event of --at or --script: what to do, with its argument, once
@@ -23,7 +25,7 @@ module Badline
23
25
 
24
26
  # What insert takes: disk images, tapes and cartridges, or an .m3u
25
27
  # or .vfl list of disks or a directory to mount as device 8.
26
- INSERTABLE = %w[.d64 .d71 .d81 .g64 .t64 .tap .crt .m3u .vfl].freeze
28
+ INSERTABLE = Media::Extensions.of(%i[disk gcr archive tape cartridge disk_list])
27
29
 
28
30
  attr_reader :frame, :action, :argument
29
31
 
@@ -20,7 +20,7 @@ The character generator ROM.
20
20
  kernal-sx64.rom
21
21
  ---------------
22
22
  The SX-64's KERNAL, 251104-04. It boots with the SX-64's banner and
23
- colours, and loads from device 8 rather than tape. Computer.new(kernal:
23
+ colours, and loads from device 8 rather than tape. C64.new(kernal:
24
24
  :sx64) fits it, as --model sx64 does.
25
25
 
26
26
  Downloaded from
@@ -32,7 +32,7 @@ kernal-pet64.rom
32
32
  The KERNAL of the PET 64, 4064 and Educator 64, 901246-01. It boots
33
33
  with the "**** COMMODORE 4064 BASIC V2.0 ****" banner and a white
34
34
  cursor, and its interrupt routine keeps the border, background and
35
- sprite colours black unless bit 7 of $0286 is set. Computer.new(kernal:
35
+ sprite colours black unless bit 7 of $0286 is set. C64.new(kernal:
36
36
  :pet64) fits it, as --model pet64 does.
37
37
 
38
38
  Downloaded from
@@ -45,7 +45,7 @@ The C64GS's BASIC and KERNAL, 390852-01, 16 KB: BASIC for $A000-$BFFF
45
45
  followed by the KERNAL for $E000-$FFFF. Its BASIC is 901226-01's but
46
46
  for 30 unused bytes. Its KERNAL is 901227-03's with most changes in
47
47
  $F72C-$FC92, the cassette routines' place, and many jumps redirected to
48
- I/O error 5. Computer.new(kernal: :gs) fits both, as --model c64gs does.
48
+ I/O error 5. C64.new(kernal: :gs) fits both, as --model c64gs does.
49
49
 
50
50
  Downloaded from
51
51
  https://www.zimmers.net/anonftp/pub/cbm/firmware/computers/c64/64gs.390852-01.bin
@@ -54,7 +54,7 @@ MD5 049e657afafbdd802e8c6a4f88f3d730.
54
54
  dos1541.rom
55
55
  -----------
56
56
  The 1541 disk drive's DOS 2.6, 16 KB for $C000-$FFFF: 325302-01
57
- ($C000-$DFFF) followed by 901229-05 ($E000-$FFFF). Drive1541 loads it.
57
+ ($C000-$DFFF) followed by 901229-05 ($E000-$FFFF). Drives::Model1541 loads it.
58
58
 
59
59
  Both halves downloaded from
60
60
  https://www.zimmers.net/anonftp/pub/cbm/firmware/drives/new/1541/
@@ -64,7 +64,7 @@ SHA-1 ab16f56989b27d89babe5f89c5a8cb3da71a82f0.
64
64
  dos1571.rom
65
65
  -----------
66
66
  The 1571 disk drive's DOS 3.0, 310654-05, 32 KB for $8000-$FFFF.
67
- Drive1571 loads it.
67
+ Drives::Model1571 loads it.
68
68
 
69
69
  Downloaded from
70
70
  https://www.zimmers.net/anonftp/pub/cbm/firmware/drives/new/1571/1571-rom.310654-05.bin
@@ -73,7 +73,7 @@ SHA-1 f1be619c106641a685f6609e4d43d6fc9eac1e70.
73
73
  dos1581.rom
74
74
  -----------
75
75
  The 1581 disk drive's DOS, 318045-02, 32 KB for $8000-$FFFF.
76
- Drive1581 loads it.
76
+ Drives::Model1581 loads it.
77
77
 
78
78
  Downloaded from
79
79
  https://www.zimmers.net/anonftp/pub/cbm/firmware/drives/new/1581/1581-rom.318045-02.bin
data/lib/badline/sdl.rb CHANGED
@@ -58,6 +58,7 @@ module Badline
58
58
  ffi_func :SDL_ClearQueuedAudio, [:uint32], :void
59
59
  ffi_func :SDL_PushEvent, [:ptr], :int
60
60
  ffi_func :SDL_GetKeyName, [:int], :str
61
+ ffi_func :SDL_StopTextInput, [], :void
61
62
 
62
63
  ffi_buffer :rect, 16
63
64
  ffi_write_i32 :rect_x, 0
@@ -3,10 +3,10 @@
3
3
  require "digest"
4
4
 
5
5
  module Badline
6
- module Storage
6
+ module SIDTune
7
7
  # PSID/RSID tunes: a memory image plus the addresses of the routines
8
8
  # that initialise it and produce one frame of music.
9
- class SIDFile
9
+ class File
10
10
  include IntegerHelper
11
11
 
12
12
  class FormatError < StandardError; end
@@ -147,7 +147,7 @@ module Badline
147
147
  attr_reader :format
148
148
 
149
149
  def initialize(path)
150
- @bytes = File.binread(path).bytes
150
+ @bytes = ::File.binread(path).bytes
151
151
  @format = @bytes[0, 4].pack("C*")
152
152
  raise FormatError, "Missing PSID or RSID signature" unless %w[PSID RSID].include?(@format)
153
153
  raise FormatError, "Truncated header" if truncated?
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Badline
4
- module Storage
4
+ module SIDTune
5
5
  # The layout of an HVSC collection: tunes in directories under its root,
6
6
  # and databases about them in `DOCUMENTS`. A tune's HVSC path is its path
7
7
  # from the root, as `/MUSICIANS/H/Hubbard_Rob/Commando.sid`.
@@ -10,15 +10,15 @@ module Badline
10
10
  # The named file in the DOCUMENTS directory of a collection above the
11
11
  # tune, or else of the one `$HVSC_BASE` points at.
12
12
  def document(tune_path, name)
13
- candidates(tune_path).map { |dir| File.join(dir, "DOCUMENTS", name) }
14
- .find { |path| File.file?(path) }
13
+ candidates(tune_path).map { |dir| ::File.join(dir, "DOCUMENTS", name) }
14
+ .find { |path| ::File.file?(path) }
15
15
  end
16
16
 
17
17
  # The tune's HVSC path in the collection that holds the document, or
18
18
  # nil when the tune lies outside it.
19
19
  def path(tune_path, document)
20
- root = File.dirname(File.expand_path(document), 2)
21
- tune = File.expand_path(tune_path)
20
+ root = ::File.dirname(::File.expand_path(document), 2)
21
+ tune = ::File.expand_path(tune_path)
22
22
  tune.start_with?("#{root}/") ? tune[root.length..] : nil
23
23
  end
24
24
 
@@ -26,10 +26,10 @@ module Badline
26
26
 
27
27
  def candidates(tune_path)
28
28
  dirs = []
29
- dir = File.dirname(File.expand_path(tune_path))
29
+ dir = ::File.dirname(::File.expand_path(tune_path))
30
30
  until dirs.last == dir
31
31
  dirs << dir
32
- dir = File.dirname(dir)
32
+ dir = ::File.dirname(dir)
33
33
  end
34
34
  base = ENV.fetch("HVSC_BASE", nil)
35
35
  dirs << base if base