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
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Badline
4
4
  module KernalTrap
5
- class Drive
5
+ class DOS
6
6
  # The drive's open data channels by secondary address, and the five
7
7
  # buffers they hold. The DOS hands out the highest free buffer.
8
8
  # Reading the BAM when it initializes the disk takes buffer 4, so
@@ -22,31 +22,6 @@ module Badline
22
22
 
23
23
  def clear = @channels.clear
24
24
 
25
- # Each open channel with its secondary address, in the order they
26
- # opened.
27
- def save_state(out)
28
- out.int(@channels.length)
29
- @channels.each do |secondary, channel|
30
- out.int(secondary)
31
- channel.save_state(out)
32
- end
33
- end
34
-
35
- # Buffer channels take their block in the drive's RAM again.
36
- def load_state(input, memory)
37
- @channels.clear
38
- input.int.times do
39
- secondary = input.int
40
- @channels[secondary] = Channels.channel_from_state(input, memory.ram)
41
- end
42
- end
43
-
44
- # A channel save_state wrote: a file open for writing, or a Channel
45
- # whose buffer, if it has one, is in `ram`.
46
- def self.channel_from_state(input, ram)
47
- input.boolean? ? WriteFile.from_state(input) : Channel.from_state(input, ram)
48
- end
49
-
50
25
  # Opens a buffer channel on the buffer a "#n" name asks for, or on
51
26
  # the highest free one. Returns nil when that buffer isn't free,
52
27
  # which leaves a channel asking again for its own buffer open.
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Badline
4
4
  module KernalTrap
5
- class Drive
5
+ class DOS
6
6
  # The drive's 2K of RAM, as M-W and M-R reach it, with the job queue
7
7
  # that runs a read job written into it: a job code for each of five
8
8
  # buffers at $00, their track and sector pairs from $06, and the
@@ -62,15 +62,6 @@ module Badline
62
62
  load_buffer(Channels::BAM_BUFFER, block) if block
63
63
  end
64
64
 
65
- def save_state(out)
66
- out.blob(@ram).boolean(@bam)
67
- end
68
-
69
- def load_state(input)
70
- input.blob_into(@ram)
71
- @bam = input.boolean?
72
- end
73
-
74
65
  # Whether the BAM has been read since the last reset.
75
66
  def bam? = @bam
76
67
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Badline
4
4
  module KernalTrap
5
- class Drive
5
+ class DOS
6
6
  # The numbers a DOS command takes. The DOS skips spaces, commas and
7
7
  # cursor-rights before a parameter, reads it from the characters $30
8
8
  # to $3F that follow, the low nibble of each, and skips whatever
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Badline
4
4
  module KernalTrap
5
- class Drive
5
+ class DOS
6
6
  # The command channel's side of the drive: the last DOS error with the
7
7
  # track and sector it happened at, or the bytes an M-R read. Once its
8
8
  # last byte has been read, it holds OK again.
@@ -37,14 +37,6 @@ module Badline
37
37
  nil
38
38
  end
39
39
 
40
- def save_state(out)
41
- @channel.save_state(out)
42
- end
43
-
44
- def load_state(input)
45
- @channel = Channels.channel_from_state(input, [])
46
- end
47
-
48
40
  def replace(bytes)
49
41
  @channel.replace(bytes)
50
42
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Badline
4
4
  module KernalTrap
5
- class Drive
5
+ class DOS
6
6
  # A channel open for writing a file. It gathers the bytes sent to it,
7
7
  # and the drive writes the file to the disk when the channel closes.
8
8
  # An append starts from the file's end, a replace writes over the
@@ -14,14 +14,6 @@ module Badline
14
14
  TYPES = %i[del seq prg usr rel].freeze
15
15
  MODES = %i[write replace append].freeze
16
16
 
17
- # Reads what save_state wrote after the flag that marks a file
18
- # open for writing.
19
- def self.from_state(input)
20
- name = input.string
21
- type = TYPES.fetch(input.int).to_sym
22
- new(name, type, mode: MODES.fetch(input.int).to_sym).tap { |file| file.load_state(input) }
23
- end
24
-
25
17
  def initialize(name, type, mode: :write)
26
18
  @name = name
27
19
  @type = type
@@ -29,16 +21,6 @@ module Badline
29
21
  @bytes = []
30
22
  end
31
23
 
32
- def save_state(out)
33
- out.boolean(true).string(@name).int(TYPES.index(@type)).int(MODES.index(@mode))
34
- out.blob(@bytes).optional_int(@buffer)
35
- end
36
-
37
- def load_state(input)
38
- @bytes = input.blob
39
- @buffer = input.optional_int
40
- end
41
-
42
24
  def write(bytes) = @bytes.concat(bytes)
43
25
 
44
26
  def writable? = true
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Badline
4
4
  module KernalTrap
5
- class Drive
5
+ class DOS
6
6
  # Opens for writing, the S command and the reports a write makes. A
7
7
  # file open for writing takes the type its name asks for, or SEQ on a
8
8
  # data channel. On a write-protected disk, or storage that doesn't
@@ -1,12 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "badline/kernal_trap/drive/block_commands"
4
- require "badline/kernal_trap/drive/channels"
5
- require "badline/kernal_trap/drive/memory"
6
- require "badline/kernal_trap/drive/parameters"
7
- require "badline/kernal_trap/drive/status"
8
- require "badline/kernal_trap/drive/writes"
9
- require "badline/kernal_trap/drive/write_file"
3
+ require "badline/kernal_trap/dos/block_commands"
4
+ require "badline/kernal_trap/dos/channel"
5
+ require "badline/kernal_trap/dos/channels"
6
+ require "badline/kernal_trap/dos/memory"
7
+ require "badline/kernal_trap/dos/parameters"
8
+ require "badline/kernal_trap/dos/status"
9
+ require "badline/kernal_trap/dos/writes"
10
+ require "badline/kernal_trap/dos/write_file"
10
11
 
11
12
  module Badline
12
13
  module KernalTrap
@@ -16,7 +17,7 @@ module Badline
16
17
  # LOAD. A disk image whose host file takes writes takes files, blocks
17
18
  # and BAM changes too. Other disks are write-protected, though storage
18
19
  # that can write files still takes a SAVE handed over whole.
19
- class Drive
20
+ class DOS
20
21
  include BlockCommands
21
22
  include Writes
22
23
 
@@ -146,25 +147,6 @@ module Badline
146
147
  # vector, as U: does.
147
148
  def reset! = reset(cold: true)
148
149
 
149
- # The mounted storage's setup, for Storage.reopen, then the drive's
150
- # RAM, its status and its open channels.
151
- def save_state(out)
152
- @storage.save_setup(out)
153
- out.marker("TRAP DRIVE")
154
- @memory.save_state(out)
155
- @status.save_state(out)
156
- @channels.save_state(out)
157
- end
158
-
159
- # Takes the state after the storage's setup, into a drive mounted on
160
- # the reopened storage.
161
- def load_state(input)
162
- input.marker("TRAP DRIVE")
163
- @memory.load_state(input)
164
- @status.load_state(input)
165
- @channels.load_state(input, @memory)
166
- end
167
-
168
150
  # Returns the next byte and whether it is the channel's last, or nil
169
151
  # when there is nothing left to send.
170
152
  def read(secondary)
@@ -6,17 +6,17 @@ module Badline
6
6
  private
7
7
 
8
8
  def active?
9
- kernal_mapped? && @bus.peek(0xba) == DEVICE
9
+ kernal_mapped? && @bus.peek(zero_page.device) == DEVICE
10
10
  end
11
11
 
12
12
  def filename
13
13
  Storage.strip_drive_prefix(full_filename)
14
14
  end
15
15
 
16
- # Filename pointer at $BB/$BC, length at $B7
16
+ # The file name FNADR points to, FNLEN bytes long.
17
17
  def full_filename
18
- pointer = uint16(@bus.peek(0xbb), @bus.peek(0xbc))
19
- bytes = Array.new(@bus.peek(0xb7)) do |i|
18
+ pointer = peek_word(zero_page.filename_address)
19
+ bytes = Array.new(@bus.peek(zero_page.filename_length)) do |i|
20
20
  @layout.filename_byte(@bus, (pointer + i) & 0xffff)
21
21
  end
22
22
  Storage.ascii(bytes)
@@ -0,0 +1,150 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ module KernalTrap
5
+ class DOS
6
+ class Channel
7
+ # A channel's fields under `prefix`: whether it is a buffer in the
8
+ # drive's RAM, where it starts and ends, the pointer, the byte it
9
+ # leads with and its buffer's number, the error it reports once its
10
+ # bytes run out, and a file's bytes.
11
+ def save_fields(out, prefix)
12
+ out.text("#{prefix}kind", "channel").bool("#{prefix}writable", @writable).int("#{prefix}base", @base)
13
+ out.int("#{prefix}length", @length).int("#{prefix}pointer", @pointer).optional_int("#{prefix}lead", @lead)
14
+ out.optional_int("#{prefix}buffer", @buffer)
15
+ error = @error
16
+ out.ints("#{prefix}error", error) if error
17
+ out.bytes("#{prefix}bytes", @bytes) unless @writable
18
+ end
19
+
20
+ # A buffer channel's bytes are the drive's `ram`.
21
+ def load_fields(input, prefix, ram)
22
+ @writable = input.bool("#{prefix}writable", false)
23
+ @base = input.int("#{prefix}base", 0)
24
+ @length = input.int("#{prefix}length", 0)
25
+ @pointer = input.int("#{prefix}pointer", 0)
26
+ @lead = input.optional_int("#{prefix}lead")
27
+ @buffer = input.optional_int("#{prefix}buffer")
28
+ error = input.ints("#{prefix}error", [])
29
+ @error = error.empty? ? nil : error.dup
30
+ @bytes = @writable ? ram : input.bytes("#{prefix}bytes", "".b).bytes
31
+ self
32
+ end
33
+ end
34
+
35
+ class WriteFile
36
+ # A file open for writing, under `prefix`: its name, type and mode,
37
+ # the bytes written so far and the byte held back.
38
+ def save_fields(out, prefix)
39
+ out.text("#{prefix}kind", "write_file").bytes("#{prefix}name", @name.b).text("#{prefix}type", @type.to_s)
40
+ out.text("#{prefix}mode", @mode.to_s).bytes("#{prefix}bytes", @bytes).optional_int("#{prefix}held", @buffer)
41
+ end
42
+
43
+ def self.from_fields(input, prefix)
44
+ type = input.choice("#{prefix}type", TYPES.map(&:to_s), "prg")
45
+ mode = input.choice("#{prefix}mode", MODES.map(&:to_s), "write")
46
+ new(input.bytes("#{prefix}name", "".b), TYPES.find { |each| each.to_s == type } || :prg,
47
+ mode: MODES.find { |each| each.to_s == mode } || :write).tap do |file|
48
+ file.load_fields(input, prefix)
49
+ end
50
+ end
51
+
52
+ def load_fields(input, prefix)
53
+ @bytes = input.bytes("#{prefix}bytes", "".b).bytes
54
+ @buffer = input.optional_int("#{prefix}held")
55
+ end
56
+ end
57
+
58
+ class Channels
59
+ # The channels open, by secondary address, each under channel<SA>.
60
+ def save_fields(out)
61
+ out.ints("channels", @channels.keys)
62
+ @channels.each { |secondary, channel| channel.save_fields(out, "channel#{secondary}.") }
63
+ end
64
+
65
+ def load_fields(input, ram)
66
+ @channels.clear
67
+ input.ints("channels", []).each do |secondary|
68
+ @channels[secondary] = Channels.channel_from_fields(input, "channel#{secondary}.", ram)
69
+ end
70
+ end
71
+
72
+ def self.channel_from_fields(input, prefix, ram)
73
+ return WriteFile.from_fields(input, prefix) if input.text("#{prefix}kind", "channel") == "write_file"
74
+
75
+ Channel.new.load_fields(input, prefix, ram)
76
+ end
77
+ end
78
+
79
+ class Status
80
+ # The error channel, under status.
81
+ def save_fields(out) = @channel.save_fields(out, "status.")
82
+
83
+ def load_fields(input)
84
+ input.choice("status.kind", ["channel"], "channel")
85
+ @channel = Channel.new.load_fields(input, "status.", [])
86
+ end
87
+ end
88
+
89
+ class Memory
90
+ # The drive's RAM, and whether the BAM has been read.
91
+ def save_fields(out)
92
+ out.bytes("ram", @ram).bool("bam", @bam)
93
+ end
94
+
95
+ def load_fields(input)
96
+ input.bytes_into("ram", @ram)
97
+ @bam = input.bool("bam", @bam)
98
+ end
99
+ end
100
+
101
+ # The storage device 8 serves.
102
+ attr_reader :storage
103
+
104
+ # The DOS's fields in a keyed snapshot module (BL-TRAPDOS), after the
105
+ # storage's under storage.*: its RAM, the error channel and the
106
+ # channels open.
107
+ def save_fields(out)
108
+ @memory.save_fields(out)
109
+ @status.save_fields(out)
110
+ @channels.save_fields(out)
111
+ end
112
+
113
+ def load_fields(input)
114
+ @memory.load_fields(input)
115
+ @status.load_fields(input)
116
+ @channels.load_fields(input, @memory.ram)
117
+ end
118
+ end
119
+
120
+ class Serial
121
+ # The serial trap's fields under `prefix`: whether device 8 listens
122
+ # or talks, on which channel, the frame it is in and the bytes it has
123
+ # gathered.
124
+ def save_fields(out, prefix)
125
+ out.bool("#{prefix}listening", @listening).bool("#{prefix}talking", @talking)
126
+ out.optional_int("#{prefix}listen_channel", @listen_channel)
127
+ out.optional_int("#{prefix}talk_channel", @talk_channel).int("#{prefix}frame", @frame)
128
+ out.bytes("#{prefix}buffer", @buffer)
129
+ end
130
+
131
+ def load_fields(input, prefix)
132
+ @listening = input.bool("#{prefix}listening", @listening)
133
+ @talking = input.bool("#{prefix}talking", @talking)
134
+ @listen_channel = input.optional_int("#{prefix}listen_channel")
135
+ @talk_channel = input.optional_int("#{prefix}talk_channel")
136
+ @frame = input.int("#{prefix}frame", @frame)
137
+ @buffer = input.bytes("#{prefix}buffer", "".b).bytes
138
+ end
139
+ end
140
+
141
+ class Save
142
+ # Whether a SAVE is under way, under `prefix`.
143
+ def save_fields(out, prefix) = out.bool("#{prefix}saving", @saving)
144
+
145
+ def load_fields(input, prefix)
146
+ @saving = input.bool("#{prefix}saving", @saving)
147
+ end
148
+ end
149
+ end
150
+ end
@@ -20,7 +20,11 @@ module Badline
20
20
  # +banked+ says whether the 6510 port can bank the KERNAL out, and so
21
21
  # whether RAM lies under every ROM and the I/O. +mmu+ says the C128's
22
22
  # MMU maps the KERNAL instead, and that the file name and the file's
23
- # bytes lie in the banks the KERNAL keeps at FNBANK and BA. Each
23
+ # bytes lie in the banks the KERNAL keeps at FNBANK and BA.
24
+ # +rom_select+ says the 264 series' TED selects ROM or RAM and its
25
+ # latch the ROM banks, so the KERNAL is in while ROM is selected with
26
+ # the KERNAL's bank, and the ROM reads the file name and the file's
27
+ # bytes from RAM, under any ROM, through its bank fetch at $07D9. Each
24
28
  # serial byte starts a one-shot timer by writing the high byte of its
25
29
  # count to +serial_timer+, and the control value TIMER_ONE_SHOT_START
26
30
  # to +serial_timer_start+ where the timer has a control register to
@@ -29,16 +33,19 @@ module Badline
29
33
  # +serial_timer+. The ROM lets go of each serial line by reading a
30
34
  # port, masking the line's bit off and writing it back: ATN at
31
35
  # +atn_port+ with +atn_mask+, then the clock and the data line.
36
+ # +zero_page+ is a ZeroPage, where the KERNAL keeps the file's
37
+ # parameters and the status the traps read and set.
32
38
  Layout = Data.define(:load, :save, :searching_message, :loading_message, :load_byte_loop, :load_done,
33
39
  :file_not_found_exit, :missing_file_name_exit, :saving_message, :clock_release,
34
40
  :data_release, :save_done, :talk, :listen, :second, :tksa, :ciout, :untalk,
35
41
  :unlisten, :acptr, :banked, :serial_timer, :serial_timer_start, :atn_port, :atn_mask,
36
- :clock_port, :clock_mask, :data_port, :data_mask, :mmu) do
42
+ :clock_port, :clock_mask, :data_port, :data_mask, :mmu, :rom_select, :zero_page) do
37
43
  # Whether the CPU reads the KERNAL ROM at the trapped addresses: on a
38
- # banked machine, the 6510 port's banking bits select it, and on the
39
- # C128 the MMU.
44
+ # banked machine, the 6510 port's banking bits select it, on the
45
+ # C128 the MMU, and on the 264 series the ROM select and bank latch.
40
46
  def kernal_mapped?(bus)
41
47
  return bus.system_rom_mapped? if mmu
48
+ return bus.kernal_selected? if rom_select
42
49
 
43
50
  !banked || bus.io_port.kernal?
44
51
  end
@@ -55,15 +62,21 @@ module Badline
55
62
  end
56
63
 
57
64
  # The file name's byte at +address+, in the bank at FNBANK on the
58
- # C128.
65
+ # C128 and in RAM on the 264 series.
59
66
  def filename_byte(bus, address)
60
- mmu ? bus.peek_bank(bus.peek(FILENAME_BANK), address) : bus.peek(address)
67
+ return bus.peek_bank(bus.peek(FILENAME_BANK), address) if mmu
68
+ return bus.ram_view.peek(address) if rom_select
69
+
70
+ bus.peek(address)
61
71
  end
62
72
 
63
73
  # The byte at +address+ a SAVE writes or a VERIFY compares, in the bank
64
- # at BA on the C128.
74
+ # at BA on the C128 and in RAM on the 264 series.
65
75
  def file_byte(bus, address)
66
- mmu ? bus.peek_bank(bus.peek(BANK), address) : bus.peek(address)
76
+ return bus.peek_bank(bus.peek(BANK), address) if mmu
77
+ return bus.ram_view.peek(address) if rom_select
78
+
79
+ bus.peek(address)
67
80
  end
68
81
 
69
82
  # Starts the timer the ROM times a serial byte with, its count's high
@@ -95,6 +108,32 @@ module Badline
95
108
  end
96
109
  end
97
110
 
111
+ # The zero-page variables of a KERNAL's file routines, by address:
112
+ # +status+ (ST), +verify+ (VERCK, 0 for LOAD and 1 for VERIFY),
113
+ # +start_address+ (SAL), +end_address+ (EAL), +filename_length+
114
+ # (FNLEN), +secondary_address+ (SA), +device+ (FA), +filename_address+
115
+ # (FNADR), +save_start+ (STAL) and +load_address+ (MEMUSS, where LOAD
116
+ # puts a file loaded with secondary address 0). Each two-byte one names
117
+ # its low byte, and the high byte follows it.
118
+ ZeroPage = Data.define(:status, :verify, :start_address, :end_address, :filename_length, :secondary_address,
119
+ :device, :filename_address, :save_start, :load_address)
120
+
121
+ # The C64's file variables. The VIC-20 and C128 KERNALs keep theirs at
122
+ # the same addresses.
123
+ C64_ZERO_PAGE = ZeroPage.new(
124
+ status: 0x90, verify: 0x93, start_address: 0xac, end_address: 0xae, filename_length: 0xb7,
125
+ secondary_address: 0xb9, device: 0xba, filename_address: 0xbb, save_start: 0xc1, load_address: 0xc3
126
+ )
127
+
128
+ # The 264 series KERNAL's file variables, as its LOAD ($F04A) and SAVE
129
+ # ($F1A4) use them: SAVE walks SAL at $9B from STAL at $B2 up to EAL at
130
+ # $9D, and LOAD ends with EAL at $9D, or starts it from MEMUSS at $B4
131
+ # for secondary address 0.
132
+ PLUS4_ZERO_PAGE = ZeroPage.new(
133
+ status: 0x90, verify: 0x93, start_address: 0x9b, end_address: 0x9d, filename_length: 0xab,
134
+ secondary_address: 0xad, device: 0xae, filename_address: 0xaf, save_start: 0xb2, load_address: 0xb4
135
+ )
136
+
98
137
  # A CIA's control value that starts its timer: force load, one-shot,
99
138
  # start.
100
139
  TIMER_ONE_SHOT_START = 0x19
@@ -117,7 +156,7 @@ module Badline
117
156
  ciout: 0xeddd, untalk: 0xedef, unlisten: 0xedfe, acptr: 0xee13,
118
157
  banked: true, serial_timer: 0xdc07, serial_timer_start: 0xdc0f,
119
158
  atn_port: 0xdd00, atn_mask: 0xf7, clock_port: 0xdd00, clock_mask: 0xef, data_port: 0xdd00, data_mask: 0xdf,
120
- mmu: false
159
+ mmu: false, rom_select: false, zero_page: C64_ZERO_PAGE
121
160
  )
122
161
 
123
162
  # The VIC-20 KERNAL, 901486-07 (PAL). The NTSC one, 901486-06, differs
@@ -135,7 +174,7 @@ module Badline
135
174
  ciout: 0xeee4, untalk: 0xeef6, unlisten: 0xef04, acptr: 0xef19,
136
175
  banked: false, serial_timer: 0x9129, serial_timer_start: nil,
137
176
  atn_port: 0x911f, atn_mask: 0x7f, clock_port: 0x912c, clock_mask: 0xfd, data_port: 0x912c, data_mask: 0xdf,
138
- mmu: false
177
+ mmu: false, rom_select: false, zero_page: C64_ZERO_PAGE
139
178
  )
140
179
 
141
180
  # The C128 KERNAL, 318020-05, in C128 mode: the jump table's targets
@@ -156,7 +195,32 @@ module Badline
156
195
  ciout: 0xe503, untalk: 0xe515, unlisten: 0xe526, acptr: 0xe43e,
157
196
  banked: true, serial_timer: nil, serial_timer_start: nil,
158
197
  atn_port: 0xdd00, atn_mask: 0xf7, clock_port: 0xdd00, clock_mask: 0xef, data_port: 0xdd00, data_mask: 0xdf,
159
- mmu: true
198
+ mmu: true, rom_select: false, zero_page: C64_ZERO_PAGE
199
+ )
200
+
201
+ # The 264 series KERNAL, 318004-05 (PAL), the same at every address
202
+ # here in the NTSC 318005-05: ILOAD's and ISAVE's default targets and
203
+ # the jump table's targets for the serial primitives, each of which
204
+ # tries a 1551's TCBM interface for devices 8 and 9 before the serial
205
+ # bus. LOAD prints SEARCHING FOR at $F160 and LOADING or VERIFYING at
206
+ # $F189, which ends CLC, retries a byte from $F09F and returns at $F0EB
207
+ # (LDX $9D, LDY $9E, RTS). FILE NOT FOUND leaves through $F27C and
208
+ # MISSING FILE NAME through $F288. SAVE prints SAVING at $F228 and
209
+ # returns at $F226 (CLC, RTS). The port at $01 drives the lines, ATN
210
+ # from bit 2, the clock from bit 1 ($E2B8 lets it go) and the data line
211
+ # from bit 0 ($E2C6). Its serial bytes are timed with TED timer 2, but
212
+ # its timeouts differ from the C64's, so the traps leave the timer
213
+ # alone.
214
+ PLUS4_LAYOUT = Layout.new(
215
+ load: 0xf04a, save: 0xf1a4,
216
+ searching_message: 0xf160, loading_message: 0xf189, load_byte_loop: 0xf09f, load_done: 0xf0eb,
217
+ file_not_found_exit: 0xf27c, missing_file_name_exit: 0xf288,
218
+ saving_message: 0xf228, clock_release: 0xe2b8, data_release: 0xe2c6, save_done: 0xf226,
219
+ talk: 0xedfa, listen: 0xee2c, second: 0xee4d, tksa: 0xee1a,
220
+ ciout: 0xecdf, untalk: 0xef3b, unlisten: 0xef23, acptr: 0xec8b,
221
+ banked: false, serial_timer: nil, serial_timer_start: nil,
222
+ atn_port: 0x01, atn_mask: 0xfb, clock_port: 0x01, clock_mask: 0xfd, data_port: 0x01, data_mask: 0xfe,
223
+ mmu: false, rom_select: true, zero_page: PLUS4_ZERO_PAGE
160
224
  )
161
225
  end
162
226
  end
@@ -23,22 +23,22 @@ module Badline
23
23
  # address even when the first one relocated.
24
24
  LOAD_SECONDARY = 0x60
25
25
 
26
- def initialize(cpu:, bus:, layout:, drive:)
26
+ def initialize(cpu:, bus:, layout:, dos:)
27
27
  super(cpu:, bus:, layout:)
28
- @drive = drive
28
+ @dos = dos
29
29
  end
30
30
 
31
31
  def call
32
32
  return unless active?
33
33
 
34
- @bus.poke(0x93, @cpu.a)
35
- @bus.poke(0x90, 0x00)
34
+ @bus.poke(zero_page.verify, @cpu.a)
35
+ @bus.poke(zero_page.status, 0x00)
36
36
  name = filename
37
37
  return @cpu.program_counter = @layout.missing_file_name_exit if Storage.parse_name(name).first.empty?
38
38
 
39
39
  data, complete = receive(name)
40
40
  if low_memory?(data)
41
- @drive.close(0)
41
+ @dos.close(0)
42
42
  else
43
43
  finish(data, complete)
44
44
  end
@@ -50,10 +50,10 @@ module Badline
50
50
  # flagged EOI. A read error in the file's chain stops the bytes early,
51
51
  # with no EOI.
52
52
  def receive(name)
53
- @drive.open(0, name)
53
+ @dos.open(0, name)
54
54
  data = []
55
55
  loop do
56
- byte, eoi = @drive.read(0)
56
+ byte, eoi = @dos.read(0)
57
57
  return [data, false] unless byte
58
58
 
59
59
  data << byte
@@ -66,17 +66,17 @@ module Badline
66
66
  # until RUN/STOP breaks the load, as on a real drive.
67
67
  def finish(data, complete)
68
68
  if data.empty?
69
- @bus.poke(0x90, EOI | READ_TIMEOUT)
69
+ @bus.poke(zero_page.status, EOI | READ_TIMEOUT)
70
70
  leave_bus(ACPTR_TIMEOUT)
71
71
  continue_with(@layout.searching_message, @layout.file_not_found_exit)
72
72
  else
73
73
  deliver(data)
74
- @bus.poke(0x90, @bus.peek(0x90) & ~EOI) unless complete
74
+ @bus.poke(zero_page.status, @bus.peek(zero_page.status) & ~EOI) unless complete
75
75
  leave_bus(ISOUR_TIMEOUT)
76
76
  continue_with(@layout.searching_message, @layout.loading_message,
77
77
  complete ? @layout.load_done : @layout.load_byte_loop)
78
78
  end
79
- @bus.poke(0xb9, LOAD_SECONDARY)
79
+ @bus.poke(zero_page.secondary_address, LOAD_SECONDARY)
80
80
  end
81
81
 
82
82
  # A load ends with the UNLISTEN that closes the file, a byte sent,
@@ -91,9 +91,9 @@ module Badline
91
91
  load? && data.length > 2 && load_address(data) < 0x0334
92
92
  end
93
93
 
94
- # A=0 is LOAD, A=1 is VERIFY, kept at $93 (VERCK)
94
+ # A=0 is LOAD, A=1 is VERIFY, kept at VERCK
95
95
  def load?
96
- @bus.peek(0x93).zero?
96
+ @bus.peek(zero_page.verify).zero?
97
97
  end
98
98
 
99
99
  def deliver(data)
@@ -102,25 +102,23 @@ module Badline
102
102
  payload = payload[0, 0x10000 - addr] if addr + payload.length > 0x10000
103
103
  if load?
104
104
  @layout.store_file(@bus, addr, payload)
105
- @bus.poke(0x90, EOI)
105
+ @bus.poke(zero_page.status, EOI)
106
106
  else
107
107
  verify(addr, payload)
108
108
  end
109
- end_addr = (addr + payload.length) & 0xffff
110
- @bus.poke(0xae, low_byte(end_addr))
111
- @bus.poke(0xaf, high_byte(end_addr))
109
+ poke_word(zero_page.end_address, (addr + payload.length) & 0xffff)
112
110
  end
113
111
 
114
112
  def verify(addr, payload)
115
113
  match = payload.each_with_index.all? { |byte, i| @layout.file_byte(@bus, addr + i) == byte }
116
- @bus.poke(0x90, match ? EOI : EOI | VERIFY_MISMATCH)
114
+ @bus.poke(zero_page.status, match ? EOI : EOI | VERIFY_MISMATCH)
117
115
  end
118
116
 
119
- # Secondary address $B9 zero relocates to the caller-supplied address
120
- # stashed at $C3/$C4 (MEMUSS)
117
+ # Secondary address zero relocates to the caller-supplied address
118
+ # stashed at MEMUSS
121
119
  def load_address(data)
122
- if @bus.peek(0xb9).zero?
123
- uint16(@bus.peek(0xc3), @bus.peek(0xc4))
120
+ if @bus.peek(zero_page.secondary_address).zero?
121
+ peek_word(zero_page.load_address)
124
122
  else
125
123
  uint16(data[0], data[1].to_i)
126
124
  end
@@ -26,6 +26,18 @@ module Badline
26
26
  @layout.kernal_mapped?(@bus)
27
27
  end
28
28
 
29
+ def zero_page = @layout.zero_page
30
+
31
+ # The two-byte value whose low byte is at +address+.
32
+ def peek_word(address)
33
+ uint16(@bus.peek(address), @bus.peek(address + 1))
34
+ end
35
+
36
+ def poke_word(address, value)
37
+ @bus.poke(address, low_byte(value))
38
+ @bus.poke(address + 1, high_byte(value))
39
+ end
40
+
29
41
  # The ROM times each byte on the serial bus with a one-shot timer
30
42
  # (Layout#time_serial_byte): ISOUR_TIMEOUT for a byte sent,
31
43
  # ACPTR_TIMEOUT for one received.