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
@@ -1,89 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Badline
4
- module Drives
5
- class Model1581
6
- class Disk
7
- # Saving and restoring a Disk for a snapshot: the image by its path,
8
- # and every track the head wrote since the disk went in, with those
9
- # written since the last flush. The rest come from the image, read
10
- # again from its host file where there still is one.
11
- module SavedState
12
- # The image's path, expanded, and whether it was opened read-only,
13
- # for a disk from Disk.open. Nil for a disk made another way.
14
- attr_reader :path, :read_only
15
-
16
- # The disk a state from save_state describes: +current+ when it was
17
- # opened from the same image the same way, and otherwise a disk for
18
- # the image at the path the state names. Where no file is at that
19
- # path, the disk has no image and is write-protected, and a
20
- # detached reader always gets one without an image.
21
- def self.load(input, current)
22
- path = input.optional_string
23
- read_only = input.boolean?
24
- disk = current if current && !path.nil? && current.path == path && current.read_only == read_only
25
- disk ||= fresh(path, read_only, input.detached?)
26
- disk.load_tracks(input)
27
- disk
28
- end
29
-
30
- def self.fresh(path, read_only, detached)
31
- return Disk.new if path.nil? || detached
32
- return Disk.new.opened(path, true) unless File.file?(path)
33
-
34
- Disk.open(path, read_only:)
35
- end
36
-
37
- # Notes the image's path and how it was opened.
38
- def opened(path, read_only)
39
- @path = File.expand_path(path)
40
- @read_only = read_only
41
- self
42
- end
43
-
44
- def save_state(out)
45
- out.optional_string(@path).boolean(@read_only)
46
- changed = @changed.keys.sort
47
- out.ints(changed)
48
- changed.each { |index| save_track(@tracks[index], out) }
49
- out.ints(@written.keys)
50
- end
51
-
52
- def load_tracks(input)
53
- @changed.clear
54
- input.ints.each do |index|
55
- @tracks[index] = load_track(input)
56
- @changed[index] = true
57
- end
58
- @written.clear
59
- input.ints.each { |index| @written[index] = true }
60
- end
61
-
62
- private
63
-
64
- def save_track(track, out)
65
- out.int(track.sectors.length)
66
- track.sectors.each do |sector|
67
- save_field(sector.id, out)
68
- out.boolean(!sector.data.nil?)
69
- save_field(sector.data, out) if sector.data
70
- end
71
- end
72
-
73
- def save_field(field, out)
74
- out.int(field.mark).ints(field.bytes).boolean(field.good).boolean(field.deleted)
75
- end
76
-
77
- def load_track(input)
78
- Track.new(Array.new(input.int) do
79
- id = load_field(input)
80
- Track::Sector.new(id, input.boolean? ? load_field(input) : nil)
81
- end)
82
- end
83
-
84
- def load_field(input) = Track::Field.new(input.int, input.ints, input.boolean?, input.boolean?)
85
- end
86
- end
87
- end
88
- end
89
- end
@@ -1,42 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Badline
4
- module Drives
5
- class Model1581
6
- # The drive's state for a snapshot.
7
- module SavedState
8
- # The drive's whole state, as the 1541's (Drives::Model1541#save_state), with
9
- # the 8520 and the WD1772 in place of the VIAs.
10
- def save_state(out)
11
- settle!
12
- out.marker("DRIVE1581")
13
- out.int(@phase).int(@cycles).boolean(@atn)
14
- @serial_port.save_state(out)
15
- @cia.save_state(out)
16
- @fdc.save_state(out)
17
- @bus.save_state(out)
18
- @cpu.save_state(out)
19
- @mechanism.save_state(out)
20
- end
21
-
22
- def load_state(input)
23
- settle!
24
- input.marker("DRIVE1581")
25
- @phase = input.int
26
- @cycles = input.int
27
- @atn = input.boolean?
28
- @serial_port.load_state(input)
29
- @cia.load_state(input)
30
- @fdc.load_state(input)
31
- @bus.load_state(input)
32
- @cpu.load_state(input)
33
- @mechanism.load_state(input)
34
- @fast_serial_out = @cia.port_b_lines.anybits?(FAST_SERIAL_OUT)
35
- ports_written
36
- push_fast_output(@fast_serial_out ? fast_pins : 0)
37
- forget_orbits
38
- end
39
- end
40
- end
41
- end
42
- end
@@ -1,68 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Badline
4
- module Drives
5
- class Model1581
6
- class WD1772
7
- # Saving and restoring the WD1772 for a snapshot: its registers and
8
- # lines, and the command running, down to the byte it is at.
9
- module SavedState
10
- def save_state(out)
11
- [@now, @due, @phase, @command, @track, @sector, @data, @status, @steps, @count, @after_spin_up, @mode,
12
- @deadline, @id_at, @found, @index, @crc, @write_id, @size].each { |value| out.int(value) }
13
- [@type1, @drq, @intrq, @motor_on, @inward, @crc_good, @gate_open, @late,
14
- @settling].each { |flag| out.boolean(flag) }
15
- out.ints(@bytes).ints(@syncs.map { |sync| sync ? 1 : 0 })
16
- end
17
-
18
- def load_state(input)
19
- load_registers(input)
20
- load_command(input)
21
- load_flags(input)
22
- @bytes = input.ints
23
- @syncs = input.ints.map { |sync| sync == 1 }
24
- end
25
-
26
- private
27
-
28
- def load_registers(input)
29
- @now = input.int
30
- @due = input.int
31
- @phase = input.int
32
- @command = input.int
33
- @track = input.int
34
- @sector = input.int
35
- @data = input.int
36
- @status = input.int
37
- end
38
-
39
- def load_command(input)
40
- @steps = input.int
41
- @count = input.int
42
- @after_spin_up = input.int
43
- @mode = input.int
44
- @deadline = input.int
45
- @id_at = input.int
46
- @found = input.int
47
- @index = input.int
48
- @crc = input.int
49
- @write_id = input.int
50
- @size = input.int
51
- end
52
-
53
- def load_flags(input)
54
- @type1 = input.boolean?
55
- @drq = input.boolean?
56
- @intrq = input.boolean?
57
- @motor_on = input.boolean?
58
- @inward = input.boolean?
59
- @crc_good = input.boolean?
60
- @gate_open = input.boolean?
61
- @late = input.boolean?
62
- @settling = input.boolean?
63
- end
64
- end
65
- end
66
- end
67
- end
68
- end
@@ -1,33 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Badline
4
- module KeyboardBuffer
5
- ADDRESS = 0x0277
6
- COUNT = 0xc6
7
- CAPACITY = 10
8
-
9
- def type_text(text)
10
- codes = text.bytes.map { |b| ascii_to_petscii(b) }
11
- on_init { @pending_keys = codes }
12
- end
13
-
14
- private
15
-
16
- def feed_keyboard
17
- return unless ram.peek(COUNT).zero?
18
-
19
- chunk = @pending_keys.shift(CAPACITY)
20
- ram.write(ADDRESS, chunk)
21
- ram.poke(COUNT, chunk.length)
22
- @pending_keys = nil if @pending_keys.empty?
23
- end
24
-
25
- def ascii_to_petscii(byte)
26
- case byte
27
- when 0x61..0x7a then byte - 0x20
28
- when 0x41..0x5a then byte + 0x80
29
- else byte
30
- end
31
- end
32
- end
33
- end
@@ -1,31 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "badline/snapshot/c64_setup"
4
- require "badline/snapshot/vic20_setup"
5
- require "badline/snapshot/c128_setup"
6
-
7
- module Badline
8
- module Snapshot
9
- # How the machine a State was taken of was built, so a machine to
10
- # restore it into can be built first. A State starts with its family's
11
- # marker, the stamp (StateWriter#stamp) and the family's setup:
12
- # C64Setup, Vic20Setup or C128Setup. Each reads its setup from a State
13
- # (.from) or from the fields after the stamp (.read), writes it
14
- # (#write), builds a machine that way at power-on (#build) and names it
15
- # (#to_s).
16
- module Setup
17
- # The setup of the family whose marker the State starts with.
18
- def self.read(state)
19
- family = StateReader.new(state).string
20
- return Vic20Setup.from(state) if family == Vic20Setup::MARKER
21
- return C128Setup.from(state) if family == C128Setup::MARKER
22
-
23
- C64Setup.from(state)
24
- end
25
-
26
- # A new machine, built as the one a State was taken of, at that
27
- # state.
28
- def self.restored(state) = read(state).build.apply_state(state)
29
- end
30
- end
31
- end
@@ -1,179 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Badline
4
- module Snapshot
5
- # A file that isn't a snapshot badline reads, is cut short, or holds a
6
- # state that doesn't line up with the machine reading it.
7
- class FormatError < StandardError; end
8
-
9
- # A machine's state, held in memory: the Integers each chip writes in
10
- # turn, and the byte strings for memories, names and paths. Booleans
11
- # are written as 0 and 1. C64#snapshot takes one and
12
- # C64#restore puts it back.
13
- class State
14
- # The layout the chips write their fields in. It goes up whenever a
15
- # save_state writes something else, so a State in another layout
16
- # fails before anything is read into a machine. A State only reads in
17
- # the layout and the badline version that wrote it.
18
- SCHEMA = 10
19
-
20
- attr_reader :values, :strings
21
-
22
- def initialize(values, strings)
23
- @values = values
24
- @strings = strings
25
- end
26
-
27
- def ==(other)
28
- other.is_a?(State) && values == other.values && strings == other.strings
29
- end
30
- end
31
-
32
- # Writes a State. Each chip's save_state writes its fields through one
33
- # of these, and its load_state reads them back from a StateReader in
34
- # the same order.
35
- class StateWriter
36
- attr_reader :state
37
-
38
- def initialize
39
- @values = []
40
- @strings = []
41
- @state = State.new(@values, @strings)
42
- end
43
-
44
- def int(value)
45
- raise TypeError, "not an Integer: #{value.inspect}" unless value.is_a?(Integer)
46
-
47
- @values << value.to_i
48
- self
49
- end
50
-
51
- def boolean(value)
52
- @values << (value ? 1 : 0)
53
- self
54
- end
55
-
56
- def optional_int(value)
57
- return boolean(false) if value.nil?
58
-
59
- boolean(true).int(value)
60
- end
61
-
62
- def ints(values)
63
- int(values.length)
64
- values.each { |value| int(value) }
65
- self
66
- end
67
-
68
- def booleans(values)
69
- int(values.length)
70
- values.each { |value| boolean(value) }
71
- self
72
- end
73
-
74
- # Bytes, 0 to 255 each, kept as one string.
75
- def blob(values)
76
- @strings << values.pack("C*")
77
- self
78
- end
79
-
80
- def string(value)
81
- @strings << value.to_s.b
82
- self
83
- end
84
-
85
- def optional_string(value)
86
- return boolean(false) if value.nil?
87
-
88
- boolean(true).string(value)
89
- end
90
-
91
- # A name a reader checks for, so a state that has gone out of step
92
- # fails there instead of loading the wrong fields.
93
- def marker(name) = string(name)
94
-
95
- # The State's layout and the badline version writing it, which a
96
- # reader checks first (StateReader#check_stamp).
97
- def stamp = int(State::SCHEMA).string(VERSION)
98
- end
99
-
100
- # Reads a State back in the order a StateWriter wrote it.
101
- #
102
- # A detached reader restores a machine that opens no host file and
103
- # writes none, for a copy the machine runs on its own: a disk image
104
- # comes from the bytes the state holds, write-protected, a directory
105
- # device 8 serves is left out, and a true drive's disk keeps the
106
- # state's tracks without its image file.
107
- class StateReader
108
- def initialize(state, detached: false)
109
- @values = state.values
110
- @strings = state.strings
111
- @value = 0
112
- @string = 0
113
- @detached = detached
114
- end
115
-
116
- def detached? = @detached
117
-
118
- def int
119
- raise FormatError, "the state ends early, at value #{@value}" if @value >= @values.length
120
-
121
- value = @values[@value]
122
- @value += 1
123
- value
124
- end
125
-
126
- def boolean?
127
- value = int
128
- raise FormatError, "expected a boolean in the state, found #{value}" unless value.between?(0, 1)
129
-
130
- value == 1
131
- end
132
-
133
- def optional_int = boolean? ? int : nil
134
-
135
- def ints = Array.new(int) { int }
136
-
137
- def booleans = Array.new(int) { boolean? }
138
-
139
- # Reads into an existing array, in place, so whatever else holds it
140
- # sees the values.
141
- def ints_into(array) = array.replace(ints)
142
-
143
- def booleans_into(array) = array.replace(booleans)
144
-
145
- def blob = string.bytes
146
-
147
- def blob_into(array) = array.replace(blob)
148
-
149
- def string
150
- raise FormatError, "the state ends early, at string #{@string}" if @string >= @strings.length
151
-
152
- value = @strings[@string]
153
- @string += 1
154
- value
155
- end
156
-
157
- def optional_string = boolean? ? string : nil
158
-
159
- def marker(name)
160
- found = string
161
- raise FormatError, "expected #{name} in the state, found #{found[0, 20].inspect}" unless found == name
162
- end
163
-
164
- # Fails unless the State was written in this layout by this badline
165
- # version.
166
- def check_stamp
167
- schema = int
168
- version = string
169
- return if schema == State::SCHEMA && version == VERSION
170
-
171
- raise FormatError, "the state was written by badline #{version.inspect} in layout #{schema}, " \
172
- "and this is badline #{VERSION}, layout #{State::SCHEMA}"
173
- end
174
-
175
- # Whether every value and string has been read.
176
- def finished? = @value == @values.length && @string == @strings.length
177
- end
178
- end
179
- end
@@ -1,35 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Badline
4
- class Vic20
5
- class Bus
6
- # The bus's state for a snapshot.
7
- module SavedState
8
- # The RAM, the colour RAM, the byte each side's data lines hold and the
9
- # cartridge's ROM chips, for a snapshot. The expansion blocks are how
10
- # the machine was built.
11
- def save_state(out)
12
- out.marker("VIC20 BUS")
13
- @ram.save_state(out)
14
- out.blob(@color_ram.cells).int(@data).int(@video_data).int(@roms.length)
15
- @roms.each do |rom|
16
- out.int(rom.start)
17
- rom.save_state(out)
18
- end
19
- end
20
-
21
- def load_state(input)
22
- input.marker("VIC20 BUS")
23
- @ram.load_state(input)
24
- input.blob_into(@color_ram.cells)
25
- @data = input.int
26
- @video_data = input.int
27
- chips = Array.new(input.int) { [input.int, input.blob] }
28
- @roms = []
29
- chips.each { |chip| @roms << ROM.new(chip[1], length: chip[1].length, start: chip[0]) }
30
- map_pages
31
- end
32
- end
33
- end
34
- end
35
- end
@@ -1,110 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "badline/snapshot/vic20_setup"
4
-
5
- module Badline
6
- class Vic20
7
- # How the machine a State from #snapshot was taken of was built.
8
- def self.setup(state) = Snapshot::Vic20Setup.from(state)
9
-
10
- # A new machine, built as the one a State from #snapshot was taken of,
11
- # at that state.
12
- def self.restored(state) = setup(state).build.apply_state(state)
13
-
14
- # The whole machine's state for a snapshot, as C64's: the
15
- # machine's own latches, the bus with its RAM and cartridge ROM, the
16
- # VIC with its sound, both VIAs, the CPU, the datasette and its tape,
17
- # the disk device 8 serves through the traps, and a true 1541. The
18
- # serial lines go back into the bus from the restored VIAs. What the
19
- # host holds stays out: the keyboard, the joystick, RESTORE, the traps
20
- # and callbacks, whether the display renders, whether the sound
21
- # records, and whether the datasette records.
22
- module SavedState
23
- # How many more on_init handlers the machine a State was taken of had
24
- # yet to run than this one has. Nil until the machine is restored.
25
- attr_reader :init_handlers_lost
26
-
27
- def snapshot
28
- out = Snapshot::StateWriter.new
29
- save_state(out)
30
- out.state
31
- end
32
-
33
- # Takes the machine back to a State from #snapshot, trying it on a new
34
- # machine first, so one that fails leaves this machine as it was.
35
- # Raises Snapshot::FormatError for a state of another machine or RAM
36
- # expansion.
37
- def restore(state)
38
- check_setup(Vic20.setup(state))
39
- Vic20.restored(state)
40
- apply_state(state)
41
- end
42
-
43
- # Puts a State into the machine without trying it on a new one first.
44
- def apply_state(state)
45
- input = Snapshot::StateReader.new(state)
46
- load_state(input)
47
- raise Snapshot::FormatError, "the state goes on past the machine" unless input.finished?
48
-
49
- self
50
- end
51
-
52
- def save_state(out)
53
- out.marker(Snapshot::Vic20Setup::MARKER).stamp
54
- Snapshot::Vic20Setup.new(ram: @ram_configuration).write(out)
55
- out.int(@cycles).boolean(@nmi_asserted).boolean(!@pending_keys.nil?)
56
- out.ints(@pending_keys) if @pending_keys
57
- out.int(@cycles <= @init_threshold ? @init_handlers.length : 0)
58
- @bus.save_state(out)
59
- @vic.save_state(out)
60
- @sound.save_state(out)
61
- @via1.save_state(out)
62
- @via2.save_state(out)
63
- @cpu.save_state(out)
64
- @datasette.save_state(out)
65
- save_trap_dos(out)
66
- save_drive1541(out)
67
- save_drive1581(out)
68
- end
69
-
70
- def load_state(input)
71
- input.marker(Snapshot::Vic20Setup::MARKER)
72
- input.check_stamp
73
- check_setup(Snapshot::Vic20Setup.read(input))
74
- @cycles = input.int
75
- @nmi_asserted = input.boolean?
76
- @pending_keys = nil
77
- @pending_keys = input.ints if input.boolean?
78
- @init_handlers_lost = [input.int - @init_handlers.length, 0].max
79
- @bus.load_state(input)
80
- @vic.load_state(input)
81
- @sound.load_state(input)
82
- @via1.load_state(input)
83
- @via2.load_state(input)
84
- @cpu.load_state(input)
85
- via_written
86
- @datasette.load_state(input)
87
- load_trap_dos(input)
88
- load_drive1541(input)
89
- load_drive1581(input)
90
- end
91
-
92
- private
93
-
94
- # A true 1541 on the serial bus, with its device number.
95
- def save_drive1541(out)
96
- out.boolean(!@drive1541.nil?)
97
- return unless @drive1541
98
-
99
- out.int(@drive1541.device)
100
- @drive1541.save_state(out)
101
- end
102
-
103
- def check_setup(setup)
104
- return if setup.ram == @ram_configuration
105
-
106
- raise Snapshot::FormatError, "the state is of a VIC-20 with #{setup.ram} RAM, not #{@ram_configuration}"
107
- end
108
- end
109
- end
110
- end