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,91 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ class C128
5
+ # The machine's own fields in a keyed snapshot module (Snapshot::Keyed,
6
+ # BL-MACHINE): the family, its model, SID and mode by name, the cycle
7
+ # count, $D030's clock bits on their way, the NMI glue, the two
8
+ # schedulers' offsets (z80_ahead, the Z80's T-states past when it was
9
+ # due, and chips_ahead), an 8502 reset waiting for the bus, and under
10
+ # host.* the host's typing (the codes still to type, whether it is
11
+ # typing, and where Input::MatrixTypist is in a code on the key
12
+ # matrix), C= held from power-on and the on_init handlers still to run.
13
+ # They load after the Z80's own fields, which z80_ahead counts from.
14
+ module KeyedState
15
+ # How many more on_init handlers the machine a snapshot was taken of had
16
+ # yet to run than this one has. Nil until the machine is restored.
17
+ attr_reader :init_handlers_lost
18
+
19
+ # Whether a snapshot saves now (Machine::Settling): the 8502 between
20
+ # instructions or jammed. While the Z80 runs, the 8502 waits at the
21
+ # boundary it handed the bus over at, and the Z80 runs whole
22
+ # instructions.
23
+ def settled_for_snapshot? = @cpu.boundary? || @cpu.jammed?
24
+
25
+ # The SID's deferred cycles and writes, run up to this cycle, and the
26
+ # VIC's sprite collisions latched up to the beam.
27
+ def settle_chips_for_snapshot
28
+ sid.catch_up!
29
+ @vic.fold_collisions!
30
+ end
31
+
32
+ def save_machine_fields(out)
33
+ out.text("family", "c128").text("written_by", VERSION)
34
+ snapshot_setup.write_fields(out)
35
+ out.int("cycles", @cycles).int("clock_bits", @clock_bits).bool("nmi_asserted", @nmi_asserted)
36
+ out.bool("cartridge_nmi", @cartridge_nmi).bool("restore_pulse", @restore_pulse)
37
+ out.int("z80_ahead", @z80.cycles - @z80_due).int("chips_ahead", @chips_ahead)
38
+ out.bool("cpu_reset_pending", @cpu_reset_pending)
39
+ pending = @pending_keys
40
+ out.ints("host.pending_keys", pending) if pending
41
+ out.bool("host.typing", !pending.nil?)
42
+ out.int("host.init_pending", @cycles <= init_threshold ? @init_handlers.length : 0)
43
+ out.bool("host.holding_commodore", @holding_commodore)
44
+ @typist.save_fields(out, "host.typist.")
45
+ end
46
+
47
+ def load_machine_fields(input)
48
+ @cycles = input.int("cycles", @cycles)
49
+ @clock_bits = input.int("clock_bits", @clock_bits)
50
+ @nmi_asserted = input.bool("nmi_asserted", @nmi_asserted)
51
+ @cartridge_nmi = input.bool("cartridge_nmi", @cartridge_nmi)
52
+ @restore_pulse = input.bool("restore_pulse", @restore_pulse)
53
+ @z80_due = @z80.cycles - input.int("z80_ahead", @z80.cycles - @z80_due)
54
+ @chips_ahead = input.int("chips_ahead", @chips_ahead)
55
+ @cpu_reset_pending = input.bool("cpu_reset_pending", @cpu_reset_pending)
56
+ pending = input.ints("host.pending_keys", [])
57
+ @pending_keys = input.bool("host.typing", !pending.empty?) ? pending.dup : nil
58
+ @init_handlers_lost = [input.int("host.init_pending", 0) - @init_handlers.length, 0].max
59
+ load_holding_fields(input)
60
+ @typist.load_fields(input, "host.typist.")
61
+ end
62
+
63
+ # Which CPU has the bus and whose turn it is, from the MMU and the
64
+ # schedulers' offsets, and the serial bus's lines.
65
+ def machine_restored!
66
+ @z80_running = @bus.z80?
67
+ @z80_turn = @z80_running || @chips_ahead.positive?
68
+ push_serial_lines
69
+ push_fast_serial
70
+ end
71
+
72
+ private
73
+
74
+ # C= held from power-on until the machine reaches C64 mode
75
+ # (Modes#hold_commodore_key).
76
+ def load_holding_fields(input)
77
+ holding = input.bool("host.holding_commodore", @holding_commodore)
78
+ keyboard.release(:cbm) if @holding_commodore && !holding
79
+ keyboard.press(:cbm) if holding && !@holding_commodore
80
+ @holding_commodore = holding
81
+ end
82
+
83
+ # How this machine was built.
84
+ def snapshot_setup
85
+ Snapshot::C128Setup.new(model: @model.name, sid_model: @sid.model, mode: @c64_built ? :c64 : :c128)
86
+ end
87
+ end
88
+
89
+ include KeyedState
90
+ end
91
+ end
@@ -21,6 +21,11 @@ module Badline
21
21
  @bus.caps_lock = false
22
22
  end
23
23
 
24
+ # Pushes CAPS LOCK, down if it was up and up if it was down.
25
+ def push_caps_lock
26
+ @bus.caps_lock = !@bus.caps_lock
27
+ end
28
+
24
29
  # The 40/80 DISPLAY key locks down and up. The KERNAL reads it at reset
25
30
  # and starts the screen editor on the VDC's 80 columns while it is down.
26
31
  def press_display_key
@@ -30,6 +35,10 @@ module Badline
30
35
  def release_display_key
31
36
  @bus.mmu.display_key = false
32
37
  end
38
+
39
+ def push_display_key
40
+ @bus.mmu.display_key = !@bus.mmu.display_key
41
+ end
33
42
  end
34
43
  end
35
44
  end
@@ -151,16 +151,17 @@ module Badline
151
151
  end
152
152
  end
153
153
 
154
- # The registers and the P0H and P1H writes waiting for P0L and P1L.
155
- # The bus maps the restored registers once its own state is back.
156
- def save_state(out)
157
- out.ints(@registers).int(@p0h_latch).int(@p1h_latch)
154
+ # The registers and the page pointers' high bytes waiting for their
155
+ # low bytes, in a keyed snapshot module (Snapshot::Keyed). The bus
156
+ # maps its banks from them afterwards.
157
+ def save_fields(out)
158
+ out.bytes("registers", @registers).int("p0h_latch", @p0h_latch).int("p1h_latch", @p1h_latch)
158
159
  end
159
160
 
160
- def load_state(input)
161
- input.ints_into(@registers)
162
- @p0h_latch = input.int
163
- @p1h_latch = input.int
161
+ def load_fields(input)
162
+ input.bytes_into("registers", @registers)
163
+ @p0h_latch = input.masked("p0h_latch", @p0h_latch, 0x0f)
164
+ @p1h_latch = input.masked("p1h_latch", @p1h_latch, 0x0f)
164
165
  end
165
166
 
166
167
  # CR and the LCRs at $FF00-$FF04: an LCR reads its PCR.
@@ -46,6 +46,26 @@ module Badline
46
46
  end
47
47
  end
48
48
 
49
+ # The system ROM's byte at +addr+, $8000 or above, as CR value 0 maps it.
50
+ def system_rom_peek(addr)
51
+ if addr < 0xc000
52
+ @basic_high_rom.peek(addr)
53
+ elsif addr < 0xd000
54
+ @editor_rom.peek(addr)
55
+ elsif addr < 0xe000
56
+ @c128_character_rom.peek(addr)
57
+ else
58
+ @c128_kernal_rom.peek(addr)
59
+ end
60
+ end
61
+
62
+ # The chip's byte at +addr+, in $D000-$DFFF, as C128 mode's I/O maps it.
63
+ def bank_io_peek(addr)
64
+ lay_out_io(@bank_io_reads, @bank_io_writes)
65
+ @bank_io_reads[0xd5] = @mmu
66
+ @bank_io_reads[addr >> 8].peek(addr)
67
+ end
68
+
49
69
  # The bank CR picks, with common RAM in bank 0, then the pages page 0
50
70
  # and page 1 move to.
51
71
  def map_ram_pages
@@ -46,15 +46,25 @@ module Badline
46
46
 
47
47
  # The keyboard buffer of the KERNAL the machine runs: the C64's, or in
48
48
  # C128 mode BASIC 7.0's.
49
- def feed_keyboard
50
- c64 = mode == :c64
51
- count = c64 ? KeyboardBuffer::COUNT : C128_KEYBOARD_COUNT
52
- return unless ram.peek(count).zero?
53
-
54
- chunk = @pending_keys.shift(KeyboardBuffer::CAPACITY)
55
- ram.write(c64 ? KeyboardBuffer::ADDRESS : C128_KEYBOARD_BUFFER, chunk)
56
- ram.poke(count, chunk.length)
57
- @pending_keys = nil if @pending_keys.empty?
49
+ def keyboard_buffer
50
+ mode == :c64 ? Machine::KeyboardBuffer::C64_LAYOUT : Machine::KeyboardBuffer::C128_LAYOUT
51
+ end
52
+
53
+ # Types the text type_text left: into the keyboard buffer, or while
54
+ # the Z80 runs, as under CP/M, where no KERNAL reads the buffer, on
55
+ # the key matrix, all of it once begun there.
56
+ def type_pending
57
+ if @z80_running || @typist.typing?
58
+ type_on_matrix
59
+ else
60
+ feed_keyboard
61
+ end
62
+ end
63
+
64
+ def type_on_matrix
65
+ pending = @pending_keys
66
+ @typist.cycle!(pending)
67
+ @pending_keys = nil if pending.empty? && !@typist.typing?
58
68
  end
59
69
  end
60
70
  end
@@ -58,7 +58,7 @@ module Badline
58
58
  # interrupt lines going to the Z80.
59
59
  def clock_chips
60
60
  handle_init if @cycles == @init_threshold
61
- feed_keyboard if @pending_keys
61
+ type_pending if @pending_keys
62
62
 
63
63
  clock_bits = @clock_bits
64
64
  @clock_bits = @vic.clock_bits
data/lib/badline/c128.rb CHANGED
@@ -8,12 +8,14 @@ require "badline/c128/io_pages"
8
8
  require "badline/c128/z80_pages"
9
9
  require "badline/c128/color_lines"
10
10
  require "badline/c128/banks"
11
- require "badline/c128/bus/saved_state"
11
+ require "badline/c128/bus/keyed_state"
12
12
  require "badline/c128/bus"
13
13
  require "badline/c128/cpu"
14
14
  require "badline/c128/z80"
15
15
  require "badline/c128/z80_turns"
16
- require "badline/c128/saved_state"
16
+ require "badline/snapshot/c128_setup"
17
+ require "badline/c128/keyed_state"
18
+ require "badline/c128/keyed_modules"
17
19
  require "badline/c128/keys"
18
20
  require "badline/c128/modes"
19
21
  require "badline/c128/drives"
@@ -43,8 +45,9 @@ module Badline
43
45
  # mirrors, and $D500-$D7FF holds the hidden MMU, the VDC and open bus.
44
46
  class C128
45
47
  include IntegerHelper
46
- include KeyboardBuffer
48
+ include Machine::KeyboardBuffer
47
49
  include Machine::Clocking
50
+ include Machine::Settling
48
51
  include C64::Common
49
52
  include C64::Attachments
50
53
  include C64::KernalTraps
@@ -67,10 +70,6 @@ module Badline
67
70
  # BASIC 7.0's, which is READY by then.
68
71
  C128_INIT_THRESHOLD = 2_000_000
69
72
 
70
- # The C128 KERNAL's keyboard buffer and its count.
71
- C128_KEYBOARD_BUFFER = 0x034a
72
- C128_KEYBOARD_COUNT = 0xd0
73
-
74
73
  attr_reader :cpu, :cycles, :drive1541, :model, :init_threshold
75
74
 
76
75
  def family = :c128
@@ -140,6 +139,7 @@ module Badline
140
139
  @restore_pulse = false
141
140
  @init_handlers = []
142
141
  @pending_keys = nil
142
+ @typist = Input::MatrixTypist.new(@bus.keyboard)
143
143
  @capture_output = nil
144
144
  init_drives
145
145
  enter_c64_mode if @c64_built
@@ -154,7 +154,7 @@ module Badline
154
154
  return z80_cycle! if @z80_turn
155
155
 
156
156
  handle_init if @cycles == @init_threshold
157
- feed_keyboard if @pending_keys
157
+ type_pending if @pending_keys
158
158
 
159
159
  clock_bits = @clock_bits
160
160
  @clock_bits = @vic.clock_bits
@@ -37,50 +37,6 @@ module Badline
37
37
  def push_serial_lines
38
38
  @iec_bus.host_lines = @cia2.port_a_lines
39
39
  end
40
-
41
- def save_cartridge(out)
42
- cartridge = address_bus.cartridge
43
- out.boolean(!cartridge.nil?)
44
- cartridge&.save_setup(out)
45
- end
46
-
47
- # A cartridge comes back built afresh from the setup it was built
48
- # with, before the address bus puts its state into it.
49
- def load_cartridge(input)
50
- if input.boolean?
51
- connect_cartridge(Cartridge.from_setup(input))
52
- elsif address_bus.cartridge
53
- address_bus.detach_cartridge
54
- end
55
- end
56
-
57
- # A true 1541 on the serial bus, then a 1581.
58
- def save_serial_bus(out)
59
- out.boolean(!@drive1541.nil?)
60
- if @drive1541
61
- out.int(@drive1541.device)
62
- @drive1541.save_state(out)
63
- end
64
- save_drive1581(out)
65
- end
66
-
67
- def load_serial_bus(input)
68
- load_drive1541(input)
69
- load_drive1581(input)
70
- end
71
-
72
- # The REU, which the setup says the machine has, and whether it has
73
- # asked for the bus. Its IRQ line reaches the CPU as it was.
74
- def save_reu(out)
75
- @reu&.save_state(out)
76
- out.boolean(@dma)
77
- end
78
-
79
- def load_reu(input)
80
- @reu&.load_state(input)
81
- @reu_irq = @reu ? @reu.irq? : false
82
- @dma = input.boolean?
83
- end
84
40
  end
85
41
  end
86
42
  end
@@ -33,18 +33,26 @@ module Badline
33
33
 
34
34
  def type = :plus256k
35
35
 
36
- def save_state(out)
37
- [@low_ram, @video_ram, @high_ram].each { |bank| out.int(@banks.index(bank)) }
38
- out.boolean(@locked)
39
- @banks.drop(1).each { |bank| bank.save_state(out) }
36
+ # $D100 as the banks it picks give it, whether it is locked, and
37
+ # banks 1 to 3, in a keyed snapshot module.
38
+ def save_fields(out)
39
+ low = @banks.index(@low_ram).to_i
40
+ video = @banks.index(@video_ram).to_i
41
+ out.int("d100", low | (video << 2) | (@banks.index(@high_ram).to_i << 6)).bool("locked", @locked)
42
+ @banks[1].save_fields(out, "bank1")
43
+ @banks[2].save_fields(out, "bank2")
44
+ @banks[3].save_fields(out, "bank3")
40
45
  end
41
46
 
42
- def load_state(input)
43
- @low_ram = @banks.fetch(input.int)
44
- @video_ram = @banks.fetch(input.int)
45
- @high_ram = @banks.fetch(input.int)
46
- @locked = input.boolean?
47
- @banks.drop(1).each { |bank| bank.load_state(input) }
47
+ def load_fields(input)
48
+ value = input.byte("d100", 0)
49
+ @low_ram = @banks[value & 0b11]
50
+ @video_ram = @banks[(value >> 2) & 0b11]
51
+ @high_ram = @banks[value >> 6]
52
+ @locked = input.bool("locked", @locked)
53
+ @banks[1].load_fields(input, "bank1")
54
+ @banks[2].load_fields(input, "bank2")
55
+ @banks[3].load_fields(input, "bank3")
48
56
  end
49
57
 
50
58
  def power_on!
@@ -26,14 +26,16 @@ module Badline
26
26
 
27
27
  def type = :plus60k
28
28
 
29
- def save_state(out)
30
- out.int(@banks.index(@high_ram))
31
- @banks[1].save_state(out)
29
+ # $D100 as bit 7 picks the bank above $1000, and bank 1, in a
30
+ # keyed snapshot module.
31
+ def save_fields(out)
32
+ out.int("d100", @banks.index(@high_ram).to_i << 7)
33
+ @banks[1].save_fields(out, "bank1")
32
34
  end
33
35
 
34
- def load_state(input)
35
- @high_ram = @banks.fetch(input.int)
36
- @banks[1].load_state(input)
36
+ def load_fields(input)
37
+ @high_ram = @banks[(input.byte("d100", @banks.index(@high_ram).to_i << 7) >> 7) & 1]
38
+ @banks[1].load_fields(input, "bank1")
37
39
  end
38
40
 
39
41
  def power_on!
@@ -17,9 +17,9 @@ module Badline
17
17
 
18
18
  def map_io(_read_pages, _write_pages); end
19
19
 
20
- def save_state(_out); end
20
+ def save_fields(_out); end
21
21
 
22
- def load_state(_input); end
22
+ def load_fields(_input); end
23
23
 
24
24
  def reset!; end
25
25
 
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ class C64
5
+ class Bus
6
+ # The VICE debug cartridge register, mapped at $D7FF on the C64. It is a
7
+ # VICE facility rather than real hardware, enabled there with -debugcart:
8
+ # writing a byte ends the run with that value as the exit code. The VICE
9
+ # test suites write $00 for success and $ff for failure.
10
+ #
11
+ # Wraps the SID's top mirror page, so every other access falls through.
12
+ class DebugRegister
13
+ ADDRESS = 0xd7ff
14
+
15
+ def initialize(sid, &handler)
16
+ @sid = sid
17
+ @handler = handler
18
+ end
19
+
20
+ def peek(addr)
21
+ @sid.peek(addr)
22
+ end
23
+
24
+ def poke(addr, value)
25
+ return @handler.call(value) if addr == ADDRESS
26
+
27
+ @sid.poke(addr, value)
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ class C64
5
+ class Bus
6
+ # The 6510's port under port.* in a keyed snapshot module
7
+ # (Snapshot::Keyed, BL-CPU), and the bus once its memories, the port
8
+ # and the cartridge are loaded: the banks the CPU and the VIC see are
9
+ # worked out again.
10
+ module KeyedState
11
+ # The RAM expansion fitted, or an Unexpanded stand-in.
12
+ attr_reader :ram_expansion
13
+
14
+ # Takes the cartridge out of the expansion port, as a restore does for
15
+ # a snapshot without one.
16
+ def detach_cartridge
17
+ @cartridge = nil
18
+ update_overlays!
19
+ end
20
+
21
+ # The 6510 port's direction and output registers, and the charge left
22
+ # on its floating bits.
23
+ def port_state = [@cpu_port.ddr, @cpu_port.data, @cpu_port.floating]
24
+
25
+ # Sets the port as port_state reads it, as a snapshot restores it,
26
+ # leaving the RAM under $00 and $01 alone.
27
+ def restore_port(ddr, out, floating)
28
+ @cpu_port.restore(ddr, out, floating)
29
+ end
30
+
31
+ def save_port_fields(out) = @cpu_port.save_fields(out, "port.")
32
+
33
+ def load_port_fields(input) = @cpu_port.load_fields(input, "port.")
34
+
35
+ def restored! = update_overlays!
36
+ end
37
+
38
+ include KeyedState
39
+ end
40
+ end
41
+ end
@@ -1,11 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "badline/c64/bus/saved_state"
3
+ require "badline/c64/bus/keyed_state"
4
4
  require "badline/c64/bus/sid_slots"
5
5
  require "badline/c64/bus/extra_sids"
6
6
  require "badline/c64/bus/pla"
7
7
  require "badline/c64/bus/roms"
8
8
  require "badline/c64/bus/fittings"
9
+ require "badline/c64/bus/debug_register"
9
10
 
10
11
  module Badline
11
12
  # Memory layout:
@@ -65,19 +65,6 @@ module Badline
65
65
 
66
66
  private
67
67
 
68
- def save_mapper(out)
69
- super
70
- out.int(window_index(@rom, windows)).boolean(@active).boolean(@io2_ram).blob(@ram_data)
71
- end
72
-
73
- def load_mapper(input)
74
- super
75
- @rom = window_at(input.int, windows)
76
- @active = input.boolean?
77
- @io2_ram = input.boolean?
78
- input.blob_into(@ram_data)
79
- end
80
-
81
68
  def windows = [@through, @isolated, @contended, *@rom_banks, EMPTY_BANK]
82
69
 
83
70
  def io2_peek(addr)
@@ -88,21 +88,6 @@ module Badline
88
88
  @romh = @high_flash.window(@bank * BANK_SIZE)
89
89
  end
90
90
 
91
- def save_mapper(out)
92
- out.int(@bank).int(@control).blob(@io_ram)
93
- @low_flash.save_state(out)
94
- @high_flash.save_state(out)
95
- end
96
-
97
- def load_mapper(input)
98
- @bank = input.int
99
- @control = input.int
100
- input.blob_into(@io_ram)
101
- @low_flash.load_state(input)
102
- @high_flash.load_state(input)
103
- map_bank
104
- end
105
-
106
91
  def apply_control
107
92
  game = @control.nobits?(0x04) || @control.anybits?(0x01)
108
93
  exrom = @control.anybits?(0x02)
@@ -57,14 +57,6 @@ module Badline
57
57
 
58
58
  private
59
59
 
60
- def save_mapper(out)
61
- out.int(@discharged_at)
62
- end
63
-
64
- def load_mapper(input)
65
- @discharged_at = input.int
66
- end
67
-
68
60
  def discharge!
69
61
  @discharged_at = @clock ? @clock.call : 0
70
62
  end
@@ -61,18 +61,6 @@ module Badline
61
61
 
62
62
  private
63
63
 
64
- def save_mapper(out)
65
- super
66
- out.boolean(@register_visible).boolean(@phi1_ultimax)
67
- end
68
-
69
- def load_mapper(input)
70
- super
71
- @io_bank = @roml
72
- @register_visible = input.boolean?
73
- @phi1_ultimax = input.boolean?
74
- end
75
-
76
64
  def windows = [*@roml_banks, *@romh_banks, EMPTY_BANK]
77
65
 
78
66
  def select(number)
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "badline/c64/cartridge/flash/saved_state"
4
-
5
3
  module Badline
6
4
  class C64
7
5
  class Cartridge
@@ -69,7 +67,9 @@ module Badline
69
67
  alias [] peek
70
68
  end
71
69
 
72
- include SavedState
70
+ # The command states, which a snapshot names.
71
+ STATES = %i[read program_setup autoselect programming erase_window erasing suspended chip_erase
72
+ program_error].freeze
73
73
 
74
74
  attr_reader :data, :state
75
75
  attr_writer :clock
@@ -48,18 +48,6 @@ module Badline
48
48
  changed!
49
49
  end
50
50
 
51
- def save_mapper(out)
52
- out.int(@bank).boolean(@flash_writes)
53
- @flash.save_state(out)
54
- end
55
-
56
- def load_mapper(input)
57
- @bank = input.int
58
- @flash_writes = input.boolean?
59
- @flash.load_state(input)
60
- @roml = @flash.window(@bank * BANK_SIZE)
61
- end
62
-
63
51
  def install_chips(chips)
64
52
  data = Array.new(BANKS * BANK_SIZE, 0xff)
65
53
  chips.each do |chip|
@@ -53,24 +53,8 @@ module Badline
53
53
  @block = @page = @window = 0
54
54
  end
55
55
 
56
- # A GEO-RAM is built from its size alone.
57
- def save_setup(out)
58
- out.int(GEO_RAM_SETUP).int(@size)
59
- end
60
-
61
56
  private
62
57
 
63
- def save_mapper(out)
64
- out.blob(@ram_data).int(@block).int(@page).int(@window)
65
- end
66
-
67
- def load_mapper(input)
68
- input.blob_into(@ram_data)
69
- @block = input.int
70
- @page = input.int
71
- @window = input.int
72
- end
73
-
74
58
  def install_chips(_chips)
75
59
  @ram_data = Array.new(@size * 1024, 0)
76
60
  reset
@@ -56,14 +56,6 @@ module Badline
56
56
 
57
57
  private
58
58
 
59
- def save_mapper(out)
60
- out.blob(@io_ram)
61
- end
62
-
63
- def load_mapper(input)
64
- input.blob_into(@io_ram)
65
- end
66
-
67
59
  def install_chips(chips)
68
60
  roml, romh = banks_from(chips)
69
61
  @roml = roml.first || EMPTY_BANK