badline 0.13.0 → 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 (254) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +49 -1
  3. data/CLAUDE.md +27 -7
  4. data/CONTRIBUTING.md +4 -3
  5. data/README.md +8 -5
  6. data/doc/media.md +2 -1
  7. data/doc/pinned-behaviour.md +29 -0
  8. data/doc/snapshot-format.md +1047 -0
  9. data/doc/snapshots.md +72 -39
  10. data/lib/badline/audio/bare_player.rb +50 -17
  11. data/lib/badline/audio/machine_player.rb +15 -9
  12. data/lib/badline/audio/renderer.rb +32 -21
  13. data/lib/badline/c128/banks.rb +6 -3
  14. data/lib/badline/c128/bus/keyed_state.rb +45 -0
  15. data/lib/badline/c128/bus.rb +17 -3
  16. data/lib/badline/c128/drives.rb +6 -25
  17. data/lib/badline/c128/keyed_modules.rb +117 -0
  18. data/lib/badline/c128/keyed_state.rb +91 -0
  19. data/lib/badline/c128/keys.rb +9 -0
  20. data/lib/badline/c128/mmu.rb +9 -8
  21. data/lib/badline/c128/mmu_pages.rb +20 -0
  22. data/lib/badline/c128/modes.rb +19 -9
  23. data/lib/badline/c128/z80_turns.rb +1 -1
  24. data/lib/badline/c128.rb +8 -8
  25. data/lib/badline/c64/attachments.rb +0 -44
  26. data/lib/badline/c64/banked_ram/plus256k.rb +18 -10
  27. data/lib/badline/c64/banked_ram/plus60k.rb +8 -6
  28. data/lib/badline/c64/banked_ram/unexpanded.rb +2 -2
  29. data/lib/badline/c64/bus/debug_register.rb +32 -0
  30. data/lib/badline/c64/bus/keyed_state.rb +41 -0
  31. data/lib/badline/c64/bus.rb +2 -1
  32. data/lib/badline/c64/cartridge/action_replay.rb +0 -13
  33. data/lib/badline/c64/cartridge/easy_flash.rb +0 -15
  34. data/lib/badline/c64/cartridge/epyx_fastload.rb +0 -8
  35. data/lib/badline/c64/cartridge/final_cartridge3.rb +0 -12
  36. data/lib/badline/c64/cartridge/flash.rb +3 -3
  37. data/lib/badline/c64/cartridge/g_mod2.rb +0 -12
  38. data/lib/badline/c64/cartridge/geo_ram.rb +0 -16
  39. data/lib/badline/c64/cartridge/kcs_power.rb +0 -8
  40. data/lib/badline/c64/cartridge/keyed_state.rb +393 -0
  41. data/lib/badline/c64/cartridge/pagefox.rb +0 -10
  42. data/lib/badline/c64/cartridge/retro_replay.rb +0 -8
  43. data/lib/badline/c64/cartridge/rgcd.rb +0 -10
  44. data/lib/badline/c64/cartridge/super_games.rb +0 -10
  45. data/lib/badline/c64/cartridge/zaxxon.rb +0 -16
  46. data/lib/badline/c64/cartridge.rb +1 -27
  47. data/lib/badline/c64/kernal_traps.rb +28 -25
  48. data/lib/badline/c64/keyed_modules.rb +111 -0
  49. data/lib/badline/c64/keyed_state.rb +68 -0
  50. data/lib/badline/c64/reu/dma.rb +0 -4
  51. data/lib/badline/c64/reu/keyed_state.rb +140 -0
  52. data/lib/badline/c64/reu/ram.rb +0 -15
  53. data/lib/badline/c64/reu.rb +1 -3
  54. data/lib/badline/c64/true_drives.rb +56 -5
  55. data/lib/badline/c64.rb +7 -3
  56. data/lib/badline/chips/cia/interrupt_register.rb +0 -17
  57. data/lib/badline/chips/cia/keyed_parts.rb +213 -0
  58. data/lib/badline/chips/cia/serial.rb +0 -4
  59. data/lib/badline/chips/cia/time_of_day.rb +0 -22
  60. data/lib/badline/chips/cia/timer.rb +0 -20
  61. data/lib/badline/chips/cia.rb +2 -2
  62. data/lib/badline/chips/cpu/core.rb +18 -2
  63. data/lib/badline/chips/cpu/keyed_state.rb +177 -0
  64. data/lib/badline/chips/cpu/microcode.rb +16 -0
  65. data/lib/badline/chips/cpu/port.rb +20 -3
  66. data/lib/badline/chips/cpu.rb +5 -0
  67. data/lib/badline/chips/sid/decimator.rb +10 -8
  68. data/lib/badline/chips/sid/envelope.rb +1 -6
  69. data/lib/badline/chips/sid/filter.rb +0 -14
  70. data/lib/badline/chips/sid/keyed_parts.rb +286 -0
  71. data/lib/badline/chips/sid/voice.rb +0 -10
  72. data/lib/badline/chips/sid/waveform.rb +0 -2
  73. data/lib/badline/chips/sid.rb +1 -2
  74. data/lib/badline/chips/ted/beam.rb +119 -0
  75. data/lib/badline/chips/ted/timers.rb +66 -0
  76. data/lib/badline/chips/ted.rb +143 -0
  77. data/lib/badline/chips/tia.rb +49 -0
  78. data/lib/badline/chips/vdc/keyed_parts.rb +105 -0
  79. data/lib/badline/chips/vdc/painter.rb +0 -12
  80. data/lib/badline/chips/vdc/raster.rb +0 -16
  81. data/lib/badline/chips/vdc/window.rb +0 -9
  82. data/lib/badline/chips/vdc.rb +1 -2
  83. data/lib/badline/chips/via/control_lines.rb +0 -15
  84. data/lib/badline/chips/via/interrupt_register.rb +0 -9
  85. data/lib/badline/chips/via/keyed_parts.rb +163 -0
  86. data/lib/badline/chips/via/shift_register.rb +0 -14
  87. data/lib/badline/chips/via/timer1.rb +0 -13
  88. data/lib/badline/chips/via/timer2.rb +0 -18
  89. data/lib/badline/chips/via.rb +1 -2
  90. data/lib/badline/chips/vic/border_mask.rb +0 -10
  91. data/lib/badline/chips/vic/collisions.rb +0 -11
  92. data/lib/badline/chips/vic/color_patches.rb +0 -11
  93. data/lib/badline/chips/vic/display_state.rb +0 -19
  94. data/lib/badline/chips/vic/graphics_shifter.rb +0 -4
  95. data/lib/badline/chips/vic/keyed_parts.rb +228 -0
  96. data/lib/badline/chips/vic/keyed_sprites.rb +225 -0
  97. data/lib/badline/chips/vic/keyed_state.rb +102 -0
  98. data/lib/badline/chips/vic/register_log.rb +0 -18
  99. data/lib/badline/chips/vic/registers.rb +0 -9
  100. data/lib/badline/chips/vic/sequencer.rb +0 -2
  101. data/lib/badline/chips/vic/sprite/internal_bus.rb +0 -11
  102. data/lib/badline/chips/vic/sprite.rb +0 -2
  103. data/lib/badline/chips/vic/sprites.rb +0 -3
  104. data/lib/badline/chips/vic.rb +6 -2
  105. data/lib/badline/chips/vic_i/keyed_parts.rb +142 -0
  106. data/lib/badline/chips/vic_i/painter.rb +0 -4
  107. data/lib/badline/chips/vic_i/sound.rb +38 -46
  108. data/lib/badline/chips/vic_i.rb +2 -2
  109. data/lib/badline/chips/z80/core.rb +2 -2
  110. data/lib/badline/chips/z80/keyed_state.rb +73 -0
  111. data/lib/badline/chips.rb +10 -0
  112. data/lib/badline/core.rb +4 -21
  113. data/lib/badline/drives/datasette/keyed_state.rb +38 -0
  114. data/lib/badline/drives/datasette.rb +40 -7
  115. data/lib/badline/drives/engine/core.rb +3 -0
  116. data/lib/badline/drives/engine/serial_lines.rb +4 -7
  117. data/lib/badline/drives/model1541/bus.rb +0 -13
  118. data/lib/badline/drives/model1541/disk.rb +9 -8
  119. data/lib/badline/drives/model1541/keyed_state.rb +251 -0
  120. data/lib/badline/drives/model1541/mechanism.rb +0 -2
  121. data/lib/badline/drives/model1541.rb +1 -2
  122. data/lib/badline/drives/model1571/bus.rb +0 -13
  123. data/lib/badline/drives/model1571/keyed_state.rb +108 -0
  124. data/lib/badline/drives/model1571/wd1770.rb +0 -14
  125. data/lib/badline/drives/model1571.rb +1 -2
  126. data/lib/badline/drives/model1581/bus.rb +0 -13
  127. data/lib/badline/drives/model1581/disk.rb +0 -4
  128. data/lib/badline/drives/model1581/keyed_state.rb +280 -0
  129. data/lib/badline/drives/model1581/mechanism.rb +0 -21
  130. data/lib/badline/drives/model1581/slot.rb +0 -18
  131. data/lib/badline/drives/model1581/wd1772.rb +0 -2
  132. data/lib/badline/drives/model1581.rb +2 -2
  133. data/lib/badline/drives.rb +8 -0
  134. data/lib/badline/frontend/app.rb +3 -3
  135. data/lib/badline/frontend/boot.rb +1 -1
  136. data/lib/badline/frontend/controls.rb +19 -3
  137. data/lib/badline/frontend/drive_led.rb +44 -21
  138. data/lib/badline/frontend/menu/menu_dialogs.rb +2 -2
  139. data/lib/badline/frontend/snapshots.rb +12 -1
  140. data/lib/badline/frontend/timeline.rb +28 -10
  141. data/lib/badline/input/matrix_typist.rb +98 -0
  142. data/lib/badline/input.rb +1 -0
  143. data/lib/badline/kernal_trap/chrout.rb +45 -0
  144. data/lib/badline/kernal_trap/dos/channel.rb +0 -27
  145. data/lib/badline/kernal_trap/dos/channels.rb +0 -25
  146. data/lib/badline/kernal_trap/dos/memory.rb +0 -9
  147. data/lib/badline/kernal_trap/dos/status.rb +0 -8
  148. data/lib/badline/kernal_trap/dos/write_file.rb +0 -18
  149. data/lib/badline/kernal_trap/dos.rb +0 -19
  150. data/lib/badline/kernal_trap/file_routine.rb +4 -4
  151. data/lib/badline/kernal_trap/keyed_state.rb +150 -0
  152. data/lib/badline/kernal_trap/layout.rb +75 -11
  153. data/lib/badline/kernal_trap/load.rb +14 -16
  154. data/lib/badline/kernal_trap/routine.rb +12 -0
  155. data/lib/badline/kernal_trap/save.rb +8 -18
  156. data/lib/badline/kernal_trap/serial.rb +1 -17
  157. data/lib/badline/kernal_trap.rb +2 -0
  158. data/lib/badline/machine/keyboard_buffer.rb +49 -0
  159. data/lib/badline/machine/settling.rb +29 -0
  160. data/lib/badline/machine.rb +5 -2
  161. data/lib/badline/media/boot_disk.rb +14 -2
  162. data/lib/badline/media/extensions.rb +10 -0
  163. data/lib/badline/memory.rb +3 -6
  164. data/lib/badline/options/family.rb +5 -2
  165. data/lib/badline/options/help.rb +42 -16
  166. data/lib/badline/options/table.rb +2 -0
  167. data/lib/badline/options/validation.rb +2 -0
  168. data/lib/badline/options.rb +6 -1
  169. data/lib/badline/plus4/acia.rb +45 -0
  170. data/lib/badline/plus4/bus.rb +312 -0
  171. data/lib/badline/plus4/cpu.rb +10 -0
  172. data/lib/badline/plus4/cpu_port.rb +93 -0
  173. data/lib/badline/plus4/keyboard_port.rb +51 -0
  174. data/lib/badline/plus4/model.rb +38 -0
  175. data/lib/badline/plus4/user_port.rb +31 -0
  176. data/lib/badline/plus4.rb +164 -0
  177. data/lib/badline/roms/README +38 -0
  178. data/lib/badline/roms/plus4/basic.rom +0 -0
  179. data/lib/badline/roms/plus4/function-high.rom +0 -0
  180. data/lib/badline/roms/plus4/function-low.rom +0 -0
  181. data/lib/badline/roms/plus4/kernal-ntsc.rom +0 -0
  182. data/lib/badline/roms/plus4/kernal-pal.rom +0 -0
  183. data/lib/badline/snapshot/c128_setup.rb +9 -20
  184. data/lib/badline/snapshot/c64_setup.rb +28 -24
  185. data/lib/badline/snapshot/container.rb +4 -0
  186. data/lib/badline/snapshot/field_doc/markdown.rb +112 -0
  187. data/lib/badline/snapshot/field_doc.rb +109 -0
  188. data/lib/badline/snapshot/image.rb +134 -28
  189. data/lib/badline/snapshot/keyed/cursor.rb +40 -0
  190. data/lib/badline/snapshot/keyed/modules.rb +66 -0
  191. data/lib/badline/snapshot/keyed/reader.rb +188 -0
  192. data/lib/badline/snapshot/keyed/recorder.rb +88 -0
  193. data/lib/badline/snapshot/keyed/report.rb +26 -0
  194. data/lib/badline/snapshot/keyed/writer.rb +55 -0
  195. data/lib/badline/snapshot/keyed.rb +64 -0
  196. data/lib/badline/snapshot/machine_state.rb +17 -64
  197. data/lib/badline/snapshot/storage_setup.rb +29 -34
  198. data/lib/badline/snapshot/vic20_setup.rb +6 -15
  199. data/lib/badline/snapshot/vice/reu1764.rb +2 -2
  200. data/lib/badline/snapshot/vice.rb +7 -31
  201. data/lib/badline/snapshot.rb +43 -20
  202. data/lib/badline/storage/crt_file.rb +3 -1
  203. data/lib/badline/storage/g64_image.rb +8 -3
  204. data/lib/badline/storage/tap.rb +43 -15
  205. data/lib/badline/version.rb +1 -1
  206. data/lib/badline/vic20/attachments.rb +3 -0
  207. data/lib/badline/vic20/bus/keyed_state.rb +52 -0
  208. data/lib/badline/vic20/bus.rb +1 -2
  209. data/lib/badline/vic20/keyed_modules.rb +73 -0
  210. data/lib/badline/vic20/keyed_state.rb +45 -0
  211. data/lib/badline/vic20.rb +5 -5
  212. data/lib/badline.rb +1 -0
  213. metadata +61 -42
  214. data/lib/badline/c128/bus/saved_state.rb +0 -53
  215. data/lib/badline/c128/saved_state.rb +0 -117
  216. data/lib/badline/c64/bus/saved_state.rb +0 -68
  217. data/lib/badline/c64/cartridge/flash/saved_state.rb +0 -40
  218. data/lib/badline/c64/cartridge/retro_replay/saved_state.rb +0 -47
  219. data/lib/badline/c64/cartridge/saved_state.rb +0 -63
  220. data/lib/badline/c64/reu/dma/saved_state.rb +0 -52
  221. data/lib/badline/c64/reu/saved_state.rb +0 -55
  222. data/lib/badline/c64/saved_state.rb +0 -112
  223. data/lib/badline/chips/cia/saved_state.rb +0 -49
  224. data/lib/badline/chips/cia/serial/saved_state.rb +0 -45
  225. data/lib/badline/chips/cpu/saved_state.rb +0 -117
  226. data/lib/badline/chips/sid/envelope/saved_state.rb +0 -70
  227. data/lib/badline/chips/sid/filter/saved_state.rb +0 -42
  228. data/lib/badline/chips/sid/saved_state.rb +0 -52
  229. data/lib/badline/chips/sid/waveform/saved_state.rb +0 -68
  230. data/lib/badline/chips/vdc/saved_state.rb +0 -36
  231. data/lib/badline/chips/via/saved_state.rb +0 -44
  232. data/lib/badline/chips/vic/graphics_shifter/saved_state.rb +0 -40
  233. data/lib/badline/chips/vic/saved_state.rb +0 -82
  234. data/lib/badline/chips/vic/sequencer/saved_state.rb +0 -91
  235. data/lib/badline/chips/vic/sprite/saved_state.rb +0 -66
  236. data/lib/badline/chips/vic/sprites/saved_state.rb +0 -59
  237. data/lib/badline/chips/vic_i/painter/saved_state.rb +0 -40
  238. data/lib/badline/chips/vic_i/saved_state.rb +0 -55
  239. data/lib/badline/chips/z80/saved_state.rb +0 -71
  240. data/lib/badline/chrout_trap.rb +0 -43
  241. data/lib/badline/debug_register.rb +0 -28
  242. data/lib/badline/drives/datasette/saved_state.rb +0 -37
  243. data/lib/badline/drives/model1541/disk/saved_state.rb +0 -88
  244. data/lib/badline/drives/model1541/mechanism/saved_state.rb +0 -72
  245. data/lib/badline/drives/model1541/saved_state.rb +0 -44
  246. data/lib/badline/drives/model1571/saved_state.rb +0 -47
  247. data/lib/badline/drives/model1581/disk/saved_state.rb +0 -89
  248. data/lib/badline/drives/model1581/saved_state.rb +0 -42
  249. data/lib/badline/drives/model1581/wd1772/saved_state.rb +0 -68
  250. data/lib/badline/keyboard_buffer.rb +0 -33
  251. data/lib/badline/snapshot/setup.rb +0 -31
  252. data/lib/badline/snapshot/state.rb +0 -179
  253. data/lib/badline/vic20/bus/saved_state.rb +0 -35
  254. data/lib/badline/vic20/saved_state.rb +0 -110
@@ -0,0 +1,111 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ class C64
5
+ # The whole machine as keyed snapshot modules (Snapshot::Keyed), in the
6
+ # order a load applies them: BL-MACHINE with the setup, the memories,
7
+ # the cartridge, the CPU with its port, the chips, an REU, the
8
+ # datasette, device 8 through the traps and the true drives. A load
9
+ # reads the CPU's port before the chips, so the banks are mapped as the
10
+ # chips load, and the CPU's own fields after them. What each unit works
11
+ # out again from its fields comes last, once every unit is loaded.
12
+ module KeyedModules
13
+ def save_modules
14
+ bus = address_bus
15
+ sections = [Snapshot::Keyed.section("BL-MACHINE") { |out| save_machine_fields(out) },
16
+ Snapshot::Keyed.section("BL-RAM") { |out| bus.ram.save_fields(out, "ram") }]
17
+ expansion = bus.ram_expansion
18
+ sections << Snapshot::Keyed.section("BL-RAMEXP") { |out| expansion.save_fields(out) } if expansion.type
19
+ sections << Snapshot::Keyed.section("BL-COLORRAM") { |out| bus.color_ram.save_fields(out, "ram") }
20
+ cartridge = bus.cartridge
21
+ sections << Snapshot::Keyed.section("BL-CART") { |out| cartridge.save_fields(out) } if cartridge
22
+ save_chip_modules(sections, bus)
23
+ reu = @reu
24
+ sections << Snapshot::Keyed.section("BL-REU") { |out| reu.save_fields(out) } if reu
25
+ sections << Snapshot::Keyed.section("BL-TAPE") { |out| bus.datasette.save_fields(out) }
26
+ sections << Snapshot::Keyed.section("BL-TRAPDOS") { |out| save_trap_dos_fields(out) } if mounted?
27
+ save_drive_modules(sections)
28
+ end
29
+
30
+ # Raises Snapshot::FormatError for the modules of a machine built
31
+ # another way.
32
+ def load_modules(modules)
33
+ machine = modules.machine
34
+ check_snapshot_setup(Snapshot::C64Setup.read_fields(machine))
35
+ load_machine_fields(machine)
36
+ bus = address_bus
37
+ bus.ram.load_fields(modules.required("BL-RAM"), "ram")
38
+ bus.ram_expansion.load_fields(modules.required("BL-RAMEXP")) if bus.ram_expansion.type
39
+ bus.color_ram.load_fields(modules.required("BL-COLORRAM"), "ram")
40
+ load_cartridge_module(modules.optional("BL-CART"))
41
+ cpu = modules.required("BL-CPU")
42
+ bus.load_port_fields(cpu)
43
+ bus.restored!
44
+ load_chip_modules(modules, cpu)
45
+ bus.datasette.load_fields(modules.required("BL-TAPE"))
46
+ load_trap_dos_module(modules)
47
+ load_drive_modules(modules)
48
+ modules_restored!
49
+ self
50
+ end
51
+
52
+ private
53
+
54
+ def save_chip_modules(sections, bus)
55
+ sections << Snapshot::Keyed.section("BL-CPU") do |out|
56
+ @cpu.save_fields(out)
57
+ bus.save_port_fields(out)
58
+ end
59
+ sections << Snapshot::Keyed.section("BL-VIC") { |out| @vic.save_fields(out) }
60
+ sections << Snapshot::Keyed.section("BL-CIA1") { |out| @cia1.save_fields(out) }
61
+ sections << Snapshot::Keyed.section("BL-CIA2") { |out| @cia2.save_fields(out) }
62
+ sections << Snapshot::Keyed.section("BL-SID") { |out| @sid.save_fields(out) }
63
+ end
64
+
65
+ def load_chip_modules(modules, cpu)
66
+ @vic.load_fields(modules.required("BL-VIC"))
67
+ @cia1.load_fields(modules.required("BL-CIA1"))
68
+ @cia2.load_fields(modules.required("BL-CIA2"))
69
+ @sid.load_fields(modules.required("BL-SID"))
70
+ @reu&.load_fields(modules.required("BL-REU"))
71
+ @cpu.load_fields(cpu)
72
+ end
73
+
74
+ # The cartridge comes back built afresh from the CRT image the module
75
+ # holds, before its state goes in. No module means no cartridge.
76
+ def load_cartridge_module(input)
77
+ if input
78
+ cartridge = Cartridge.from_fields(input)
79
+ connect_cartridge(cartridge)
80
+ cartridge.load_fields(input)
81
+ elsif address_bus.cartridge
82
+ address_bus.detach_cartridge
83
+ end
84
+ end
85
+
86
+ def load_trap_dos_module(modules)
87
+ input = modules.optional("BL-TRAPDOS")
88
+ input ? load_trap_dos_fields(input, detached: modules.detached?) : unmount
89
+ end
90
+
91
+ def modules_restored!
92
+ @cpu.restored!
93
+ @vic.restored!
94
+ @cia1.restored!
95
+ @cia2.restored!
96
+ @sid.restored!
97
+ @reu&.restored!
98
+ machine_restored!
99
+ end
100
+
101
+ def check_snapshot_setup(setup)
102
+ ours = Snapshot::C64Setup.of(address_bus)
103
+ return if setup == ours
104
+
105
+ raise Snapshot::FormatError, "the snapshot is of a machine with #{setup}, not #{ours}"
106
+ end
107
+ end
108
+
109
+ include KeyedModules
110
+ end
111
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ class C64
5
+ # The machine's own fields in a keyed snapshot module (Snapshot::Keyed,
6
+ # BL-MACHINE): the family, its setup by name, the cycle count, the NMI
7
+ # glue, the freezer's write count and the REU's hold on the bus, and the
8
+ # host's typing still to feed and on_init handlers still to run under
9
+ # host.*. The badline version that wrote it is written for reports and
10
+ # never read.
11
+ module KeyedState
12
+ # How many more on_init handlers the machine a snapshot was taken of had
13
+ # yet to run than this one has: they are the host's blocks, which a
14
+ # snapshot can't hold. Nil until the machine is restored.
15
+ attr_reader :init_handlers_lost
16
+
17
+ # Sets the machine's cycle count, for a snapshot from VICE, which
18
+ # counts from its own clock.
19
+ def resume_at(cycles)
20
+ @cycles = cycles
21
+ end
22
+
23
+ # Whether a snapshot saves now (Machine::Settling): the CPU between
24
+ # instructions or jammed, and no REU transfer under way.
25
+ def settled_for_snapshot? = (@cpu.boundary? || @cpu.jammed?) && !reu&.dma?
26
+
27
+ # The SID's deferred cycles and writes, run up to this cycle, and the
28
+ # VIC's sprite collisions latched up to the beam.
29
+ def settle_chips_for_snapshot
30
+ sid.catch_up!
31
+ @vic.fold_collisions!
32
+ end
33
+
34
+ def save_machine_fields(out)
35
+ out.text("family", "c64").text("written_by", VERSION)
36
+ Snapshot::C64Setup.of(address_bus).write_fields(out)
37
+ out.int("cycles", @cycles).bool("nmi_asserted", @nmi_asserted).bool("cartridge_nmi", @cartridge_nmi)
38
+ out.bool("restore_pulse", @restore_pulse).bool("freezing", @freezing).int("freeze_writes", @freeze_writes)
39
+ out.bool("dma", @dma)
40
+ pending = @pending_keys
41
+ out.ints("host.pending_keys", pending) if pending
42
+ out.int("host.init_pending", booting? ? @init_handlers.length : 0)
43
+ end
44
+
45
+ def load_machine_fields(input)
46
+ @cycles = input.int("cycles", @cycles)
47
+ @nmi_asserted = input.bool("nmi_asserted", @nmi_asserted)
48
+ @cartridge_nmi = input.bool("cartridge_nmi", @cartridge_nmi)
49
+ @restore_pulse = input.bool("restore_pulse", @restore_pulse)
50
+ @freezing = input.bool("freezing", @freezing)
51
+ @freeze_writes = input.int("freeze_writes", @freeze_writes)
52
+ @dma = input.bool("dma", @dma)
53
+ pending = input.ints("host.pending_keys", [])
54
+ @pending_keys = pending.empty? ? nil : pending.dup
55
+ @init_handlers_lost = [input.int("host.init_pending", 0) - @init_handlers.length, 0].max
56
+ end
57
+
58
+ # The REU's IRQ line and the serial bus's lines from CIA 2, once the
59
+ # chips are loaded.
60
+ def machine_restored!
61
+ @reu_irq = @reu.nil? ? false : @reu.irq?
62
+ push_serial_lines
63
+ end
64
+ end
65
+
66
+ include KeyedState
67
+ end
68
+ end
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "badline/c64/reu/dma/saved_state"
4
-
5
3
  module Badline
6
4
  class C64
7
5
  class REU
@@ -11,8 +9,6 @@ module Badline
11
9
  # After a write it goes on through the first BA-low cycle and stops at
12
10
  # the second. See doc/pinned-behaviour.md, REU DMA.
13
11
  class DMA
14
- include SavedState
15
-
16
12
  # Transfer types, as the command register's bottom two bits give
17
13
  # them.
18
14
  STASH = 0
@@ -0,0 +1,140 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ class C64
5
+ class REU
6
+ class DMA
7
+ # The kinds of transfer, by name.
8
+ TYPE_NAMES = %w[stash fetch swap verify].freeze
9
+
10
+ # A transfer's fields in a keyed snapshot module, under `prefix`.
11
+ # Before the first transfer, the fields it sets on starting are
12
+ # absent.
13
+ module KeyedState
14
+ def save_fields(out, prefix)
15
+ out.int("#{prefix}c64", @c64).int("#{prefix}expansion", @expansion).int("#{prefix}length", @length)
16
+ out.int("#{prefix}events", @events)
17
+ type = @type
18
+ out.text("#{prefix}type", TYPE_NAMES.fetch(type)) unless type.nil?
19
+ out.optional_int("#{prefix}c64_step", @c64_step).optional_int("#{prefix}expansion_step", @expansion_step)
20
+ out.optional_int("#{prefix}last", @last).optional_int("#{prefix}ba_low_after_writes", @ba_low_after_writes)
21
+ out.optional_int("#{prefix}swap_byte", @swap_byte).optional_int("#{prefix}held_read", @held_read)
22
+ save_flag_fields(out, prefix)
23
+ end
24
+
25
+ def load_fields(input, prefix)
26
+ @c64 = input.word("#{prefix}c64", @c64)
27
+ @expansion = input.int("#{prefix}expansion", @expansion)
28
+ @length = input.int("#{prefix}length", @length)
29
+ @events = input.int("#{prefix}events", @events)
30
+ type = input.choice("#{prefix}type", TYPE_NAMES + ["none"], "none")
31
+ @type = type == "none" ? nil : TYPE_NAMES.index(type)
32
+ @c64_step = input.optional_int("#{prefix}c64_step")
33
+ @expansion_step = input.optional_int("#{prefix}expansion_step")
34
+ @last = input.optional_int("#{prefix}last")
35
+ @ba_low_after_writes = input.optional_int("#{prefix}ba_low_after_writes")
36
+ @swap_byte = input.optional_int("#{prefix}swap_byte")
37
+ @held_read = input.optional_int("#{prefix}held_read")
38
+ load_flag_fields(input, prefix)
39
+ end
40
+
41
+ private
42
+
43
+ def save_flag_fields(out, prefix)
44
+ out.bool("#{prefix}holding", @holding).bool("#{prefix}waiting", @waiting)
45
+ out.bool("#{prefix}stopped_after_write", @stopped_after_write)
46
+ out.bool("#{prefix}swap_write_due", @swap_write_due).bool("#{prefix}swap_read_again", @swap_read_again)
47
+ out.bool("#{prefix}moved_all", @moved_all).bool("#{prefix}extra_cycle", @extra_cycle)
48
+ end
49
+
50
+ def load_flag_fields(input, prefix)
51
+ @holding = input.bool("#{prefix}holding", @holding)
52
+ @waiting = input.bool("#{prefix}waiting", @waiting)
53
+ @stopped_after_write = input.bool("#{prefix}stopped_after_write", @stopped_after_write)
54
+ @swap_write_due = input.bool("#{prefix}swap_write_due", @swap_write_due)
55
+ @swap_read_again = input.bool("#{prefix}swap_read_again", @swap_read_again)
56
+ @moved_all = input.bool("#{prefix}moved_all", @moved_all)
57
+ @extra_cycle = input.bool("#{prefix}extra_cycle", @extra_cycle)
58
+ end
59
+ end
60
+
61
+ include KeyedState
62
+ end
63
+
64
+ class RAM
65
+ # The data latch and each bank the REC has touched, as bank<N>; a
66
+ # bank left out holds its power-on contents.
67
+ module KeyedState
68
+ def save_fields(out)
69
+ out.int("latch", @latch)
70
+ @banks.each_with_index { |bank, number| out.bytes("bank#{number}", bank) unless bank.empty? }
71
+ end
72
+
73
+ def load_fields(input)
74
+ @latch = input.byte("latch", @latch)
75
+ @banks.each_index do |number|
76
+ @banks[number] = input.bytes("bank#{number}", "".b).bytes
77
+ end
78
+ end
79
+ end
80
+
81
+ include KeyedState
82
+ end
83
+
84
+ # The REU's fields in a keyed snapshot module (Snapshot::Keyed): the
85
+ # REC's registers and the values its counters start from, an armed or
86
+ # running transfer, the transfer under dma.*, and the RAM. The IRQ
87
+ # line comes from the status register on load.
88
+ module KeyedState
89
+ # The REU's size in K, which the machine's setup names.
90
+ def size_kb = @size_kb
91
+
92
+ def save_fields(out)
93
+ out.int("status", @status).int("command", @command).int("c64", @c64).int("c64_start", @c64_start)
94
+ out.int("expansion", @expansion).int("expansion_start", @expansion_start).int("bank", @bank)
95
+ out.int("bank_start", @bank_start).int("length", @length).int("length_start", @length_start)
96
+ out.int("interrupt_mask", @interrupt_mask).int("address_control", @address_control)
97
+ out.bool("armed", @armed).bool("requested", @requested).bool("running", @running)
98
+ out.bool("ba_was_low", @ba_was_low)
99
+ @dma.save_fields(out, "dma.")
100
+ @ram.save_fields(out)
101
+ out
102
+ end
103
+
104
+ def load_fields(input)
105
+ load_register_fields(input)
106
+ @armed = input.bool("armed", @armed)
107
+ @requested = input.bool("requested", @requested)
108
+ @running = input.bool("running", @running)
109
+ @ba_was_low = input.bool("ba_was_low", @ba_was_low)
110
+ @dma.load_fields(input, "dma.")
111
+ @ram.load_fields(input)
112
+ end
113
+
114
+ # Without calling back into the machine, which reads the line again.
115
+ def restored!
116
+ @irq = @status.anybits?(INTERRUPT_PENDING)
117
+ end
118
+
119
+ private
120
+
121
+ def load_register_fields(input)
122
+ @status = input.byte("status", @status)
123
+ @command = input.byte("command", @command)
124
+ @c64 = input.word("c64", @c64)
125
+ @c64_start = input.word("c64_start", @c64_start)
126
+ @expansion = input.word("expansion", @expansion)
127
+ @expansion_start = input.word("expansion_start", @expansion_start)
128
+ @bank = input.byte("bank", @bank)
129
+ @bank_start = input.byte("bank_start", @bank_start)
130
+ @length = input.word("length", @length)
131
+ @length_start = input.word("length_start", @length_start)
132
+ @interrupt_mask = input.byte("interrupt_mask", @interrupt_mask)
133
+ @address_control = input.byte("address_control", @address_control)
134
+ end
135
+ end
136
+
137
+ include KeyedState
138
+ end
139
+ end
140
+ end
@@ -38,21 +38,6 @@ module Badline
38
38
  bank(addr >> 16)[addr & 0xffff] = value if addr < @size
39
39
  end
40
40
 
41
- # The latch, and each 64K bank the REC has touched. A bank it hasn't
42
- # keeps its power-on contents until it does.
43
- def save_state(out)
44
- out.int(@latch)
45
- @banks.each do |bank|
46
- out.boolean(!bank.empty?)
47
- out.blob(bank) unless bank.empty?
48
- end
49
- end
50
-
51
- def load_state(input)
52
- @latch = input.int
53
- @banks.each_index { |number| @banks[number] = input.boolean? ? input.blob : [] }
54
- end
55
-
56
41
  # Every byte, as one string, with a bank the REC hasn't touched at
57
42
  # its power-on contents.
58
43
  def contents
@@ -3,7 +3,7 @@
3
3
  require "badline/c64/reu/ram"
4
4
  require "badline/c64/reu/dma"
5
5
  require "badline/c64/reu/trigger"
6
- require "badline/c64/reu/saved_state"
6
+ require "badline/c64/reu/keyed_state"
7
7
 
8
8
  module Badline
9
9
  class C64
@@ -20,8 +20,6 @@ module Badline
20
20
  # waits for the CPU's next read cycle with BA high, and C64 then
21
21
  # clocks it through #dma_cycle! until it hands the bus back.
22
22
  class REU
23
- include SavedState
24
-
25
23
  SIZES_KB = [128, 256, 512, 1024, 2048, 4096, 8192, 16_384].freeze
26
24
 
27
25
  # The registers, by offset.
@@ -8,6 +8,9 @@ module Badline
8
8
  module TrueDrives
9
9
  include Drives::Model1581::Slot
10
10
 
11
+ # The true drive models a snapshot names.
12
+ DRIVE_MODELS = %w[1541 1571 1581].freeze
13
+
11
14
  # Plugs in a Drives::Model1541, which then runs alongside the machine on its
12
15
  # own clock and talks to it over the serial bus. The serial traps stop
13
16
  # answering the drive's device number, so the KERNAL's TALK, LISTEN and
@@ -33,6 +36,35 @@ module Badline
33
36
  Drives::Model1541.new.tap { |drive| attach_drive1541(drive) }
34
37
  end
35
38
 
39
+ # The true drives as keyed snapshot modules, each named from its device
40
+ # number: BL-D8, BL-D8-CPU and so on for device 8.
41
+ def save_drive_modules(sections)
42
+ drive1541 = @drive1541
43
+ drive1541&.save_modules(sections, "BL-D#{drive1541.device}")
44
+ drive1581 = @drive1581
45
+ drive1581&.save_modules(sections, "BL-D#{drive1581.device}")
46
+ save_drive1571_modules(sections)
47
+ sections
48
+ end
49
+
50
+ # The true drives a snapshot's modules hold, built afresh unless the
51
+ # same model is on the bus as the same device. A drive the snapshot
52
+ # doesn't have comes off the bus.
53
+ def load_drive_modules(modules)
54
+ models = []
55
+ 8.upto(11) do |device|
56
+ input = modules.optional("BL-D#{device}")
57
+ next unless input
58
+
59
+ model = input.choice("model", DRIVE_MODELS, "1541")
60
+ load_drive_model(modules, input, device, model)
61
+ models << model
62
+ end
63
+ detach_drive1541 unless models.include?("1541")
64
+ detach_drive1581 unless models.include?("1581")
65
+ detach_drive1571 unless models.include?("1571")
66
+ end
67
+
36
68
  # Unplugs the Drives::Model1541, leaving the serial bus with nothing on it.
37
69
  def detach_drive1541
38
70
  return unless @drive1541
@@ -55,15 +87,34 @@ module Badline
55
87
  # for its DOS to boot before typing.
56
88
  def drive1541_attached = nil
57
89
 
58
- # A true 1541 on the serial bus, with its device number.
59
- def load_drive1541(input)
60
- return detach_drive1541 unless input.boolean?
90
+ def load_drive_model(modules, input, device, model)
91
+ case model
92
+ when "1581" then load_drive1581_modules(modules, input, device)
93
+ when "1571" then load_drive1571_modules(modules, input, device)
94
+ else load_drive1541_modules(modules, input, device)
95
+ end
96
+ end
61
97
 
62
- device = input.int
98
+ def load_drive1541_modules(modules, input, device)
63
99
  detach_drive1541 if @drive1541 && @drive1541.device != device
64
100
  attach_drive1541(Drives::Model1541.new(device:)) unless @drive1541
65
- @drive1541.load_state(input)
101
+ @drive1541.load_modules(modules, input, "BL-D#{device}")
66
102
  end
103
+
104
+ def load_drive1581_modules(modules, input, device)
105
+ detach_drive1581 if @drive1581 && @drive1581.device != device
106
+ attach_drive1581(Drives::Model1581.new(device:)) unless @drive1581
107
+ @drive1581.load_modules(modules, input, "BL-D#{device}")
108
+ end
109
+
110
+ # The C128D's 1571, which only the C128 has (C128::Drives).
111
+ def save_drive1571_modules(sections) = sections
112
+
113
+ def load_drive1571_modules(_modules, input, _device)
114
+ input.adjust("a 1571 doesn't plug into this machine, left out")
115
+ end
116
+
117
+ def detach_drive1571 = nil
67
118
  end
68
119
  end
69
120
  end
data/lib/badline/c64.rb CHANGED
@@ -4,17 +4,18 @@ require "badline/c64/common"
4
4
  require "badline/c64/true_drives"
5
5
  require "badline/c64/attachments"
6
6
  require "badline/c64/kernal_traps"
7
- require "badline/c64/saved_state"
7
+ require "badline/c64/keyed_state"
8
+ require "badline/c64/keyed_modules"
8
9
 
9
10
  module Badline
10
11
  class C64
11
12
  include IntegerHelper
12
- include KeyboardBuffer
13
+ include Machine::KeyboardBuffer
13
14
  include Machine::Clocking
15
+ include Machine::Settling
14
16
  include Common
15
17
  include Attachments
16
18
  include KernalTraps
17
- include SavedState
18
19
 
19
20
  attr_reader :address_bus, :cpu, :cycles, :drive1541
20
21
 
@@ -188,6 +189,9 @@ module Badline
188
189
  # The KERNAL the traps stand in for (KernalTraps#mount).
189
190
  def trap_layout = KernalTrap::C64_LAYOUT
190
191
 
192
+ # The KERNAL's keyboard buffer, which #type_text feeds.
193
+ def keyboard_buffer = Machine::KeyboardBuffer::C64_LAYOUT
194
+
191
195
  def trap_bus = address_bus
192
196
 
193
197
  def plug_reu(size_kb)
@@ -35,23 +35,6 @@ module Badline
35
35
 
36
36
  def interrupted? = status.value >= 0x80
37
37
 
38
- def save_state(out)
39
- out.int(@mask.value).int(@status.value).int(@pending)
40
- [@read, @read_last_cycle, @read_two_cycles_ago].each { |read| out.optional_int(read) }
41
- out.boolean(@timer_b_bug).boolean(@quiet)
42
- end
43
-
44
- def load_state(input)
45
- @mask.value = input.int
46
- @status.value = input.int
47
- @pending = input.int
48
- @read = input.optional_int
49
- @read_last_cycle = input.optional_int
50
- @read_two_cycles_ago = input.optional_int
51
- @timer_b_bug = input.boolean?
52
- @quiet = input.boolean?
53
- end
54
-
55
38
  # Raises IR on this cycle.
56
39
  def assert_now
57
40
  status.interrupt = true