badline 0.13.0 → 0.15.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 (286) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +72 -1
  3. data/CLAUDE.md +42 -12
  4. data/CONTRIBUTING.md +4 -3
  5. data/README.md +76 -13
  6. data/doc/media.md +2 -1
  7. data/doc/pinned-behaviour.md +174 -0
  8. data/doc/snapshot-format.md +1197 -0
  9. data/doc/snapshots.md +90 -40
  10. data/lib/badline/audio/bare_player.rb +46 -17
  11. data/lib/badline/audio/machine_player.rb +6 -10
  12. data/lib/badline/audio/renderer.rb +32 -21
  13. data/lib/badline/c128/banks.rb +7 -6
  14. data/lib/badline/c128/bus/keyed_state.rb +45 -0
  15. data/lib/badline/c128/bus.rb +36 -4
  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 +27 -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/extra_sids.rb +80 -0
  31. data/lib/badline/c64/bus/keyed_state.rb +41 -0
  32. data/lib/badline/c64/bus.rb +3 -1
  33. data/lib/badline/c64/cartridge/action_replay.rb +6 -15
  34. data/lib/badline/c64/cartridge/comal80.rb +1 -0
  35. data/lib/badline/c64/cartridge/dinamic.rb +3 -1
  36. data/lib/badline/c64/cartridge/easy_flash.rb +0 -15
  37. data/lib/badline/c64/cartridge/epyx_fastload.rb +0 -8
  38. data/lib/badline/c64/cartridge/final_cartridge3.rb +1 -12
  39. data/lib/badline/c64/cartridge/flash.rb +3 -3
  40. data/lib/badline/c64/cartridge/fun_play.rb +2 -1
  41. data/lib/badline/c64/cartridge/g_mod2.rb +0 -12
  42. data/lib/badline/c64/cartridge/game_system.rb +2 -1
  43. data/lib/badline/c64/cartridge/geo_ram.rb +0 -16
  44. data/lib/badline/c64/cartridge/kcs_power.rb +0 -8
  45. data/lib/badline/c64/cartridge/keyed_state.rb +514 -0
  46. data/lib/badline/c64/cartridge/magic_desk.rb +2 -1
  47. data/lib/badline/c64/cartridge/ocean.rb +1 -0
  48. data/lib/badline/c64/cartridge/pagefox.rb +1 -10
  49. data/lib/badline/c64/cartridge/retro_replay.rb +0 -8
  50. data/lib/badline/c64/cartridge/rgcd.rb +2 -11
  51. data/lib/badline/c64/cartridge/super_games.rb +3 -12
  52. data/lib/badline/c64/cartridge/zaxxon.rb +1 -16
  53. data/lib/badline/c64/cartridge.rb +4 -30
  54. data/lib/badline/c64/kernal_traps.rb +28 -25
  55. data/lib/badline/c64/keyed_modules.rb +115 -0
  56. data/lib/badline/c64/keyed_state.rb +68 -0
  57. data/lib/badline/c64/reu/dma.rb +0 -4
  58. data/lib/badline/c64/reu/keyed_state.rb +140 -0
  59. data/lib/badline/c64/reu/ram.rb +0 -15
  60. data/lib/badline/c64/reu.rb +1 -3
  61. data/lib/badline/c64/true_drives.rb +56 -5
  62. data/lib/badline/c64.rb +7 -3
  63. data/lib/badline/chips/cia/interrupt_register.rb +0 -17
  64. data/lib/badline/chips/cia/keyed_parts.rb +213 -0
  65. data/lib/badline/chips/cia/serial.rb +0 -4
  66. data/lib/badline/chips/cia/time_of_day.rb +0 -22
  67. data/lib/badline/chips/cia/timer.rb +0 -20
  68. data/lib/badline/chips/cia.rb +2 -2
  69. data/lib/badline/chips/cpu/core.rb +18 -2
  70. data/lib/badline/chips/cpu/keyed_state.rb +177 -0
  71. data/lib/badline/chips/cpu/microcode.rb +16 -0
  72. data/lib/badline/chips/cpu/port.rb +20 -3
  73. data/lib/badline/chips/cpu.rb +5 -0
  74. data/lib/badline/chips/sid/decimator.rb +10 -8
  75. data/lib/badline/chips/sid/envelope.rb +1 -6
  76. data/lib/badline/chips/sid/filter.rb +0 -14
  77. data/lib/badline/chips/sid/keyed_parts.rb +286 -0
  78. data/lib/badline/chips/sid/voice.rb +0 -10
  79. data/lib/badline/chips/sid/waveform.rb +0 -2
  80. data/lib/badline/chips/sid.rb +1 -2
  81. data/lib/badline/chips/ted/beam.rb +118 -0
  82. data/lib/badline/chips/ted/fetches.rb +199 -0
  83. data/lib/badline/chips/ted/keyed_state.rb +156 -0
  84. data/lib/badline/chips/ted/modes.rb +134 -0
  85. data/lib/badline/chips/ted/palette.rb +43 -0
  86. data/lib/badline/chips/ted/picture.rb +182 -0
  87. data/lib/badline/chips/ted/sound/output.rb +86 -0
  88. data/lib/badline/chips/ted/sound.rb +284 -0
  89. data/lib/badline/chips/ted/timers.rb +66 -0
  90. data/lib/badline/chips/ted.rb +190 -0
  91. data/lib/badline/chips/tia.rb +61 -0
  92. data/lib/badline/chips/vdc/keyed_parts.rb +105 -0
  93. data/lib/badline/chips/vdc/painter.rb +0 -12
  94. data/lib/badline/chips/vdc/raster.rb +0 -16
  95. data/lib/badline/chips/vdc/window.rb +0 -9
  96. data/lib/badline/chips/vdc.rb +1 -2
  97. data/lib/badline/chips/via/control_lines.rb +0 -15
  98. data/lib/badline/chips/via/interrupt_register.rb +0 -9
  99. data/lib/badline/chips/via/keyed_parts.rb +163 -0
  100. data/lib/badline/chips/via/shift_register.rb +0 -14
  101. data/lib/badline/chips/via/timer1.rb +0 -13
  102. data/lib/badline/chips/via/timer2.rb +0 -18
  103. data/lib/badline/chips/via.rb +1 -2
  104. data/lib/badline/chips/vic/border_mask.rb +0 -10
  105. data/lib/badline/chips/vic/collisions.rb +0 -11
  106. data/lib/badline/chips/vic/color_patches.rb +0 -11
  107. data/lib/badline/chips/vic/display_state.rb +0 -19
  108. data/lib/badline/chips/vic/graphics_shifter.rb +0 -4
  109. data/lib/badline/chips/vic/keyed_parts.rb +228 -0
  110. data/lib/badline/chips/vic/keyed_sprites.rb +225 -0
  111. data/lib/badline/chips/vic/keyed_state.rb +102 -0
  112. data/lib/badline/chips/vic/register_log.rb +0 -18
  113. data/lib/badline/chips/vic/registers.rb +0 -9
  114. data/lib/badline/chips/vic/sequencer.rb +0 -2
  115. data/lib/badline/chips/vic/sprite/internal_bus.rb +0 -11
  116. data/lib/badline/chips/vic/sprite.rb +0 -2
  117. data/lib/badline/chips/vic/sprites.rb +0 -3
  118. data/lib/badline/chips/vic.rb +6 -2
  119. data/lib/badline/chips/vic_i/keyed_parts.rb +142 -0
  120. data/lib/badline/chips/vic_i/painter.rb +0 -4
  121. data/lib/badline/chips/vic_i/sound.rb +38 -46
  122. data/lib/badline/chips/vic_i.rb +2 -2
  123. data/lib/badline/chips/z80/core.rb +2 -2
  124. data/lib/badline/chips/z80/keyed_state.rb +73 -0
  125. data/lib/badline/chips.rb +10 -0
  126. data/lib/badline/core.rb +4 -21
  127. data/lib/badline/drives/datasette/keyed_state.rb +43 -0
  128. data/lib/badline/drives/datasette.rb +62 -12
  129. data/lib/badline/drives/engine/core.rb +3 -0
  130. data/lib/badline/drives/engine/serial_lines.rb +4 -7
  131. data/lib/badline/drives/model1541/bus.rb +0 -13
  132. data/lib/badline/drives/model1541/disk.rb +9 -8
  133. data/lib/badline/drives/model1541/keyed_state.rb +251 -0
  134. data/lib/badline/drives/model1541/mechanism.rb +0 -2
  135. data/lib/badline/drives/model1541.rb +1 -2
  136. data/lib/badline/drives/model1571/bus.rb +0 -13
  137. data/lib/badline/drives/model1571/keyed_state.rb +108 -0
  138. data/lib/badline/drives/model1571/wd1770.rb +0 -14
  139. data/lib/badline/drives/model1571.rb +1 -2
  140. data/lib/badline/drives/model1581/bus.rb +0 -13
  141. data/lib/badline/drives/model1581/disk.rb +0 -4
  142. data/lib/badline/drives/model1581/keyed_state.rb +280 -0
  143. data/lib/badline/drives/model1581/mechanism.rb +0 -21
  144. data/lib/badline/drives/model1581/slot.rb +0 -18
  145. data/lib/badline/drives/model1581/wd1772.rb +0 -2
  146. data/lib/badline/drives/model1581.rb +2 -2
  147. data/lib/badline/drives.rb +8 -0
  148. data/lib/badline/frontend/app.rb +3 -3
  149. data/lib/badline/frontend/boot.rb +4 -2
  150. data/lib/badline/frontend/controls.rb +25 -5
  151. data/lib/badline/frontend/drive_led.rb +44 -21
  152. data/lib/badline/frontend/keys.rb +14 -2
  153. data/lib/badline/frontend/media_slots.rb +11 -1
  154. data/lib/badline/frontend/menu/menu_dialogs.rb +4 -3
  155. data/lib/badline/frontend/menu/menu_media.rb +13 -3
  156. data/lib/badline/frontend/menu/menu_pages.rb +8 -8
  157. data/lib/badline/frontend/menu/menu_ports.rb +13 -1
  158. data/lib/badline/frontend/screen.rb +33 -12
  159. data/lib/badline/frontend/screens.rb +2 -1
  160. data/lib/badline/frontend/snapshots.rb +12 -1
  161. data/lib/badline/frontend/sound.rb +4 -4
  162. data/lib/badline/frontend/timeline.rb +43 -12
  163. data/lib/badline/input/matrix_typist.rb +98 -0
  164. data/lib/badline/input.rb +1 -0
  165. data/lib/badline/kernal_trap/chrout.rb +45 -0
  166. data/lib/badline/kernal_trap/dos/channel.rb +0 -27
  167. data/lib/badline/kernal_trap/dos/channels.rb +0 -25
  168. data/lib/badline/kernal_trap/dos/memory.rb +0 -9
  169. data/lib/badline/kernal_trap/dos/status.rb +0 -8
  170. data/lib/badline/kernal_trap/dos/write_file.rb +0 -18
  171. data/lib/badline/kernal_trap/dos.rb +0 -19
  172. data/lib/badline/kernal_trap/file_routine.rb +4 -4
  173. data/lib/badline/kernal_trap/keyed_state.rb +150 -0
  174. data/lib/badline/kernal_trap/layout.rb +75 -11
  175. data/lib/badline/kernal_trap/load.rb +14 -16
  176. data/lib/badline/kernal_trap/routine.rb +12 -0
  177. data/lib/badline/kernal_trap/save.rb +8 -18
  178. data/lib/badline/kernal_trap/serial.rb +1 -17
  179. data/lib/badline/kernal_trap.rb +2 -0
  180. data/lib/badline/machine/keyboard_buffer.rb +49 -0
  181. data/lib/badline/machine/settling.rb +29 -0
  182. data/lib/badline/machine.rb +29 -6
  183. data/lib/badline/media/boot_disk.rb +14 -2
  184. data/lib/badline/media/extensions.rb +10 -0
  185. data/lib/badline/media/plus4_media.rb +68 -0
  186. data/lib/badline/media/program.rb +20 -6
  187. data/lib/badline/media.rb +33 -12
  188. data/lib/badline/memory.rb +3 -6
  189. data/lib/badline/options/family.rb +6 -2
  190. data/lib/badline/options/help.rb +66 -16
  191. data/lib/badline/options/table.rb +17 -2
  192. data/lib/badline/options/validation.rb +26 -2
  193. data/lib/badline/options.rb +42 -9
  194. data/lib/badline/plus4/acia.rb +45 -0
  195. data/lib/badline/plus4/bus.rb +375 -0
  196. data/lib/badline/plus4/cassette_sense.rb +21 -0
  197. data/lib/badline/plus4/cpu.rb +10 -0
  198. data/lib/badline/plus4/cpu_port.rb +93 -0
  199. data/lib/badline/plus4/keyboard_port.rb +76 -0
  200. data/lib/badline/plus4/keyed_modules.rb +99 -0
  201. data/lib/badline/plus4/keyed_parts.rb +143 -0
  202. data/lib/badline/plus4/keyed_state.rb +41 -0
  203. data/lib/badline/plus4/model.rb +38 -0
  204. data/lib/badline/plus4/user_port.rb +31 -0
  205. data/lib/badline/plus4.rb +240 -0
  206. data/lib/badline/roms/README +38 -0
  207. data/lib/badline/roms/plus4/basic.rom +0 -0
  208. data/lib/badline/roms/plus4/function-high.rom +0 -0
  209. data/lib/badline/roms/plus4/function-low.rom +0 -0
  210. data/lib/badline/roms/plus4/kernal-ntsc.rom +0 -0
  211. data/lib/badline/roms/plus4/kernal-pal.rom +0 -0
  212. data/lib/badline/snapshot/c128_setup.rb +9 -20
  213. data/lib/badline/snapshot/c64_setup.rb +28 -24
  214. data/lib/badline/snapshot/container.rb +6 -0
  215. data/lib/badline/snapshot/field_doc/markdown.rb +113 -0
  216. data/lib/badline/snapshot/field_doc.rb +148 -0
  217. data/lib/badline/snapshot/image.rb +143 -28
  218. data/lib/badline/snapshot/keyed/cursor.rb +40 -0
  219. data/lib/badline/snapshot/keyed/modules.rb +66 -0
  220. data/lib/badline/snapshot/keyed/reader.rb +188 -0
  221. data/lib/badline/snapshot/keyed/recorder.rb +88 -0
  222. data/lib/badline/snapshot/keyed/report.rb +26 -0
  223. data/lib/badline/snapshot/keyed/writer.rb +55 -0
  224. data/lib/badline/snapshot/keyed.rb +64 -0
  225. data/lib/badline/snapshot/machine_state.rb +17 -64
  226. data/lib/badline/snapshot/plus4_setup.rb +39 -0
  227. data/lib/badline/snapshot/storage_setup.rb +29 -34
  228. data/lib/badline/snapshot/vic20_setup.rb +6 -15
  229. data/lib/badline/snapshot/vice/reu1764.rb +2 -2
  230. data/lib/badline/snapshot/vice.rb +7 -31
  231. data/lib/badline/snapshot.rb +48 -20
  232. data/lib/badline/storage/crt_file.rb +3 -1
  233. data/lib/badline/storage/g64_image.rb +9 -4
  234. data/lib/badline/storage/tap.rb +57 -20
  235. data/lib/badline/support/checkpoint.rb +3 -2
  236. data/lib/badline/support/plus4_checkpoint.rb +42 -0
  237. data/lib/badline/version.rb +1 -1
  238. data/lib/badline/vic20/attachments.rb +3 -0
  239. data/lib/badline/vic20/bus/keyed_state.rb +52 -0
  240. data/lib/badline/vic20/bus.rb +1 -2
  241. data/lib/badline/vic20/keyed_modules.rb +73 -0
  242. data/lib/badline/vic20/keyed_state.rb +45 -0
  243. data/lib/badline/vic20.rb +5 -5
  244. data/lib/badline.rb +1 -0
  245. metadata +75 -42
  246. data/lib/badline/c128/bus/saved_state.rb +0 -53
  247. data/lib/badline/c128/saved_state.rb +0 -117
  248. data/lib/badline/c64/bus/saved_state.rb +0 -68
  249. data/lib/badline/c64/cartridge/flash/saved_state.rb +0 -40
  250. data/lib/badline/c64/cartridge/retro_replay/saved_state.rb +0 -47
  251. data/lib/badline/c64/cartridge/saved_state.rb +0 -63
  252. data/lib/badline/c64/reu/dma/saved_state.rb +0 -52
  253. data/lib/badline/c64/reu/saved_state.rb +0 -55
  254. data/lib/badline/c64/saved_state.rb +0 -112
  255. data/lib/badline/chips/cia/saved_state.rb +0 -49
  256. data/lib/badline/chips/cia/serial/saved_state.rb +0 -45
  257. data/lib/badline/chips/cpu/saved_state.rb +0 -117
  258. data/lib/badline/chips/sid/envelope/saved_state.rb +0 -70
  259. data/lib/badline/chips/sid/filter/saved_state.rb +0 -42
  260. data/lib/badline/chips/sid/saved_state.rb +0 -52
  261. data/lib/badline/chips/sid/waveform/saved_state.rb +0 -68
  262. data/lib/badline/chips/vdc/saved_state.rb +0 -36
  263. data/lib/badline/chips/via/saved_state.rb +0 -44
  264. data/lib/badline/chips/vic/graphics_shifter/saved_state.rb +0 -40
  265. data/lib/badline/chips/vic/saved_state.rb +0 -82
  266. data/lib/badline/chips/vic/sequencer/saved_state.rb +0 -91
  267. data/lib/badline/chips/vic/sprite/saved_state.rb +0 -66
  268. data/lib/badline/chips/vic/sprites/saved_state.rb +0 -59
  269. data/lib/badline/chips/vic_i/painter/saved_state.rb +0 -40
  270. data/lib/badline/chips/vic_i/saved_state.rb +0 -55
  271. data/lib/badline/chips/z80/saved_state.rb +0 -71
  272. data/lib/badline/chrout_trap.rb +0 -43
  273. data/lib/badline/debug_register.rb +0 -28
  274. data/lib/badline/drives/datasette/saved_state.rb +0 -37
  275. data/lib/badline/drives/model1541/disk/saved_state.rb +0 -88
  276. data/lib/badline/drives/model1541/mechanism/saved_state.rb +0 -72
  277. data/lib/badline/drives/model1541/saved_state.rb +0 -44
  278. data/lib/badline/drives/model1571/saved_state.rb +0 -47
  279. data/lib/badline/drives/model1581/disk/saved_state.rb +0 -89
  280. data/lib/badline/drives/model1581/saved_state.rb +0 -42
  281. data/lib/badline/drives/model1581/wd1772/saved_state.rb +0 -68
  282. data/lib/badline/keyboard_buffer.rb +0 -33
  283. data/lib/badline/snapshot/setup.rb +0 -31
  284. data/lib/badline/snapshot/state.rb +0 -179
  285. data/lib/badline/vic20/bus/saved_state.rb +0 -35
  286. data/lib/badline/vic20/saved_state.rb +0 -110
data/doc/snapshots.md CHANGED
@@ -28,41 +28,74 @@ and `$XDG_DATA_HOME/badline` or `~/.local/share/badline` elsewhere.
28
28
 
29
29
  Both executables do this, and both open a `.vsf` given as the media,
30
30
  in a machine built as the saved one was, whatever `--model` says.
31
- `--save-snapshot FILE` saves one to `FILE` after the last frame. A
32
- snapshot that fails to open leaves the machine running as it was.
31
+ `--save-snapshot FILE` saves one to `FILE` after the last frame, and
32
+ `--vice` adds VICE's modules to it (see [VICE](#vice)). A snapshot that
33
+ fails to open leaves the machine running as it was.
34
+
35
+ ## Which snapshots load
36
+
37
+ Snapshots from badline 0.14.0 on load in that version and every later
38
+ one. Each part of the machine is saved as named fields, so a later
39
+ badline skips a field it no longer needs, and a field an earlier one
40
+ didn't save starts as it does when the machine is switched on. What a
41
+ restore leaves out or changes is printed, such as a module missing from
42
+ the file. [snapshot-format.md](snapshot-format.md) lists every field.
43
+
44
+ Snapshots from badline 0.13 and earlier are refused, with a message
45
+ naming the version that saved them, which still opens them. No snapshot
46
+ loaded across releases before 0.14.0, and none converts.
33
47
 
34
48
  ## From Ruby
35
49
 
36
50
  `computer.save_snapshot(path)` saves, `computer.restore_snapshot(path)`
37
51
  takes a machine back to a snapshot, and `Badline::Snapshot.load(path)`
38
52
  builds a new machine as the saved one was built and restores it.
39
- `computer.snapshot` and `computer.restore(state)` do the same in memory,
40
- without a file. A restore that fails leaves the machine as it was.
53
+ `computer.snapshot` returns a `Badline::Snapshot::Image` in memory,
54
+ without a file, and `computer.restore(image)` takes the machine back to
55
+ it; `image.load` builds a new machine from it. A restore that fails
56
+ leaves the machine as it was. `computer.export_snapshot(path)` saves with
57
+ VICE's modules too.
58
+
59
+ Saving first runs the machine on to the next instruction boundary, and
60
+ to the end of an REU transfer under way, so it advances the machine by a
61
+ few cycles: usually under a hundred, and up to about 262,000 for an REU
62
+ swapping 64K. `computer.cycles` afterwards says where it was saved. A
63
+ save straight after another runs no cycles and writes the same file. In
64
+ the window this happens between frames, and the cycles fold into the
65
+ next frame. `computer.snapshot` in memory saves the same way.
41
66
 
42
67
  ## What a snapshot holds
43
68
 
44
- A snapshot holds the machine as it was on the cycle it was saved: the
45
- chips down to the instruction step and the pixel pipeline, the RAM and
69
+ A snapshot holds the machine as it was at the instruction boundary it
70
+ was saved at: the chips down to the pixel pipeline, the RAM and
46
71
  any +60K or +256K expansion, the cartridge with its RAM and flash, a
47
72
  GEO-RAM, a true 1541 with its RAM, its VIAs and the disk under the head,
48
73
  a disk or directory mounted through the traps with its open channels,
49
74
  and the tape with its place on it. A restored machine runs on exactly
50
75
  as the saved one would have.
51
76
 
52
- A tape, a `.t64` archive and a disk image mounted through the traps come
53
- back with their contents from the snapshot, so they need no file to
54
- restore. A true drive's disk opens again from its image's path, with its
55
- tracks as the drive last saw them. When that file is gone, the disk
56
- comes back from the tracks alone, write-protected. A directory mounted
57
- as device 8 opens again from its path. What the host holds stays the
77
+ A tape, a `.t64` archive, a disk image mounted through the traps and a
78
+ true drive's disk come back with their contents from the snapshot, so
79
+ they need no file to restore. A true drive's disk holds its image as last
80
+ written to its file, with the tracks the drive has written since, and
81
+ writes back to that file while it is there. When the file is gone, the
82
+ disk comes back write-protected. A directory mounted as device 8 opens
83
+ again from its path. What the host holds stays the
58
84
  host's: the keyboard, the joysticks, the mouse and paddles, sound, and
59
85
  blocks given to `on_init` that hadn't run yet, which a restore reports.
60
86
 
61
87
  Every model saves this way, PAL, NTSC, old NTSC and Drean alike, and so
62
- does a machine with an REU, with its RAM, its registers and a transfer
63
- part way through. A snapshot only restores in the badline version that
64
- wrote it, into a machine with the same chip models, region, RAM
65
- expansion, REU, KERNAL, datasette and board.
88
+ does a machine with an REU, with its RAM and its registers. A snapshot
89
+ restores into a machine with the same chip models, region, RAM
90
+ expansion, REU, KERNAL, datasette and board. The second and third SIDs
91
+ the SID player fits for a stereo tune are saved too, each with its
92
+ address and model, and a restore fits them.
93
+
94
+ Text typed with `type_text` that the machine hasn't taken yet, such as
95
+ autostart's `RUN`, carries over, and so do the keys a C128 is typing on
96
+ its key matrix. The VIC-II's line in progress carries over to a badline
97
+ that draws lines the same way. A later badline that draws them another
98
+ way starts the rest of that line afresh, and says so.
66
99
 
67
100
  ## The VIC-20
68
101
 
@@ -70,10 +103,10 @@ expansion, REU, KERNAL, datasette and board.
70
103
  slots and pause menu page. A VIC-20 snapshot holds its RAM and colour
71
104
  RAM, a cartridge's ROM, the VIC with its picture and sound, both VIAs,
72
105
  the CPU, the tape with its place on it, the disk or directory device 8
73
- serves through the traps and a true 1541, and restores into a VIC-20
74
- with the same RAM expansion. Its file names its machine `VIC20`, as
75
- xvic's do, and holds only the `BADLINE` module, so xvic doesn't open
76
- it, and badline doesn't open xvic's. Restoring a
106
+ serves through the traps and a true 1541 or 1581, and restores into a
107
+ VIC-20 with the same RAM expansion. Its file names its machine `VIC20`,
108
+ as xvic's do, and holds only badline's modules, so xvic doesn't open it,
109
+ and badline doesn't open xvic's. Restoring a
77
110
  VIC-20 snapshot while a C64 runs, or a C64 one while a VIC-20 runs,
78
111
  swaps the machine in the window.
79
112
 
@@ -82,30 +115,47 @@ swaps the machine in the window.
82
115
  `badline c128` saves and restores the same way too. A C128 snapshot
83
116
  holds its 128K of RAM, both colour RAM banks, the CPU port, the MMU's
84
117
  registers and the page pointer writes it holds, the VIC-IIe, both CIAs,
85
- the SID, the VDC with its RAM and registers, the CPU, a cartridge, the
86
- tape, the disk device 8 serves through the traps and a true 1541, and
87
- restores into a C128 of the same model with the same SID, built for the
88
- same mode. The VDC's picture is left out and painted again while the
89
- window shows it, and the window shows the VIC-IIe after a restore. Its
90
- file names its machine `C128`, as x128's do, and holds only the
91
- `BADLINE` module, so x128 doesn't open it, and badline doesn't open
92
- x128's. Restoring a C128 snapshot while another machine runs swaps the
118
+ the SID, the VDC with its RAM and registers, the 8502 and the Z80, a
119
+ cartridge, the tape, the disk device 8 serves through the traps and a
120
+ true 1541, 1571 or 1581, and restores into a C128 of the same model with
121
+ the same SID, built for the same mode. The VDC's picture is left out and
122
+ painted again while the window shows it, and the window shows the
123
+ VIC-IIe after a restore. Its file names its machine `C128`, as x128's
124
+ do, and holds only badline's modules, so x128 doesn't open it, and
125
+ badline doesn't open x128's. Restoring a C128 snapshot while another machine runs swaps the
93
126
  machine in the window, as for the VIC-20.
94
127
 
128
+ ## The Plus/4
129
+
130
+ The Plus/4, the C16 and the C116 save and restore the same way. A
131
+ snapshot of one holds its RAM with the ROM bank latch and whether ROM or
132
+ RAM is selected, the cartridges' ROMs in each slot, the CPU with its
133
+ port, the TED with its timers, its counters and its picture, the TED's
134
+ sound, the keyboard's 6529, the Plus/4's user port 6529 and ACIA, a TCBM
135
+ interface's TIAs, the tape with its place on it, the disk or directory
136
+ device 8 serves through the traps and a true 1541 or 1581. It restores
137
+ into a machine of the same model with the same RAM, and the BASIC,
138
+ KERNAL and function ROMs come with the model. Its file names its machine
139
+ `PLUS4`, as xplus4's do, and holds only badline's modules, so xplus4
140
+ doesn't open it, and badline doesn't open xplus4's. `--vice` and
141
+ `computer.export_snapshot(path)` refuse a Plus/4.
142
+
95
143
  ## VICE
96
144
 
97
- Snapshots use VICE's `.vsf` format. badline writes VICE's modules for
98
- the CPU, RAM and CPU port, both CIAs, the SID and the VIC-II, and an
99
- REU's `REU1764`, plus the ones x64sc needs to open the file, with
100
- nothing else attached to the cartridge port, nothing on the tape or user
101
- ports and no true drive. The VIC-II module names the chip as VICE does:
102
- the 6569 or 8565 on PAL, the 6567R8 or 8562 on NTSC, the 6567R56A on old
103
- NTSC and the 6572 on PAL-N. A `BADLINE` module, which VICE skips, holds
104
- the whole machine, and badline restores its own snapshots from it.
105
-
106
- x64sc 3.10 opens badline's snapshots, taken at the end of the
107
- instruction the CPU was in, and of an REU transfer under way, as long
108
- as its VIC-II model matches: `-model c64`, `c64c`, `ntsc`, `newntsc`,
145
+ Snapshots use VICE's `.vsf` format, and badline's own modules are named
146
+ `BL-` and something, which VICE skips. A C64 snapshot saved with
147
+ `--vice`, or `computer.export_snapshot(path)`, also holds VICE's
148
+ modules for the CPU, RAM and CPU port, both CIAs, the SID and the
149
+ VIC-II, and an REU's `REU1764`, plus the ones x64sc needs to open the
150
+ file, with nothing else attached to the cartridge port, nothing on the
151
+ tape or user ports and no true drive. The VIC-II module names the chip
152
+ as VICE does: the 6569 or 8565 on PAL, the 6567R8 or 8562 on NTSC, the
153
+ 6567R56A on old NTSC and the 6572 on PAL-N. badline restores such a
154
+ snapshot from its own modules, and leaves VICE's alone.
155
+
156
+ x64sc 3.10 opens those snapshots, which are saved between
157
+ instructions with no REU transfer under way, as long as its VIC-II
158
+ model matches: `-model c64`, `c64c`, `ntsc`, `newntsc`,
109
159
  `oldntsc` or `drean`, as `--model` names the machine. It takes the REU
110
160
  from the snapshot, keeps its own drives and leaves out the cartridge,
111
161
  the other expansions and the tape. A program x64sc runs on from badline's
@@ -74,26 +74,38 @@ module Badline
74
74
  settle
75
75
  end
76
76
 
77
- # The rig between frames: the bus and its chips, the extra SIDs, the
78
- # CPU, and the call waiting for the idle loop.
79
- def save_state(out)
80
- out.marker("BARE PLAYER")
81
- @bus.save_state(out)
82
- @extras.each { |sid| sid.save_state(out) }
83
- @cpu.save_state(out)
84
- out.optional_int(@pending).int(@argument).boolean(@idle)
77
+ # The rig between frames as keyed snapshot modules: the call waiting
78
+ # for the idle loop in BL-PLAYER, the memories, the CPU and its port,
79
+ # the chips and the extra SIDs, as BL-SID2 and BL-SID3.
80
+ def save_modules
81
+ bus = @bus
82
+ cpu = @cpu
83
+ sections = [Snapshot::Keyed.section("BL-PLAYER") do |out|
84
+ out.optional_int("pending", @pending).int("argument", @argument).bool("idle", @idle)
85
+ end]
86
+ sections << Snapshot::Keyed.section("BL-RAM") { |out| bus.ram.save_fields(out, "ram") }
87
+ sections << Snapshot::Keyed.section("BL-COLORRAM") { |out| bus.color_ram.save_fields(out, "ram") }
88
+ sections << Snapshot::Keyed.section("BL-CPU") do |out|
89
+ cpu.save_fields(out)
90
+ bus.save_port_fields(out)
91
+ end
92
+ sections << Snapshot::Keyed.section("BL-VIC") { |out| bus.vic.save_fields(out) }
93
+ sections << Snapshot::Keyed.section("BL-CIA1") { |out| bus.cia1.save_fields(out) }
94
+ sections << Snapshot::Keyed.section("BL-CIA2") { |out| bus.cia2.save_fields(out) }
95
+ sections << Snapshot::Keyed.section("BL-SID") { |out| bus.sid.save_fields(out) }
96
+ bus.save_extra_sid_modules(sections)
85
97
  end
86
98
 
87
- # Takes a rig that hasn't started to where #save_state found one, in
99
+ # Takes a rig that hasn't started to where #save_modules found one, in
88
100
  # place of #start.
89
- def load_state(input)
90
- input.marker("BARE PLAYER")
91
- @bus.load_state(input)
92
- @extras.each { |sid| sid.load_state(input) }
93
- @cpu.load_state(input)
94
- @pending = input.optional_int
95
- @argument = input.int
96
- @idle = input.boolean?
101
+ def load_modules(modules)
102
+ load_bus_modules(modules)
103
+ @cpu.load_fields(modules.required("BL-CPU"))
104
+ @cpu.restored!
105
+ player = modules.required("BL-PLAYER")
106
+ @pending = player.optional_int("pending")
107
+ @argument = player.int("argument", 0)
108
+ @idle = player.bool("idle", false)
97
109
  @stereo.synthesize!
98
110
  install_dispatch
99
111
  end
@@ -128,6 +140,23 @@ module Badline
128
140
  @bus.cia1.timer_a_latch + 1
129
141
  end
130
142
 
143
+ def load_bus_modules(modules)
144
+ bus = @bus
145
+ bus.ram.load_fields(modules.required("BL-RAM"), "ram")
146
+ bus.color_ram.load_fields(modules.required("BL-COLORRAM"), "ram")
147
+ bus.load_port_fields(modules.required("BL-CPU"))
148
+ bus.restored!
149
+ bus.vic.load_fields(modules.required("BL-VIC"))
150
+ bus.cia1.load_fields(modules.required("BL-CIA1"))
151
+ bus.cia2.load_fields(modules.required("BL-CIA2"))
152
+ bus.sid.load_fields(modules.required("BL-SID"))
153
+ bus.load_extra_sid_modules(modules)
154
+ @stereo.sids.each(&:restored!)
155
+ bus.vic.restored!
156
+ bus.cia1.restored!
157
+ bus.cia2.restored!
158
+ end
159
+
131
160
  def stub_address = @stub_address ||= @tune.driver_address
132
161
 
133
162
  def idle_address = stub_address + 3
@@ -39,18 +39,14 @@ module Badline
39
39
 
40
40
  def clock_hz = @computer.region.clock_hz
41
41
 
42
- # The whole machine, as C64#snapshot takes it, and the extra SIDs,
43
- # once it plays.
44
- def save_state(out)
45
- @computer.save_state(out)
46
- @extras.each { |sid| sid.save_state(out) }
47
- end
42
+ # The whole machine as its keyed snapshot modules, as it stands between
43
+ # frames, with the extra SIDs as BL-SID2 and BL-SID3, once it plays.
44
+ def save_modules = @computer.save_modules
48
45
 
49
- # Takes a machine that hasn't started to where #save_state found one,
46
+ # Takes a machine that hasn't started to where #save_modules found one,
50
47
  # in place of #start.
51
- def load_state(input)
52
- @computer.load_state(input)
53
- @extras.each { |sid| sid.load_state(input) }
48
+ def load_modules(modules)
49
+ @computer.load_modules(modules)
54
50
  @stereo.synthesize!
55
51
  @started = true
56
52
  end
@@ -136,17 +136,17 @@ module Badline
136
136
  # or before `cycles` if there is one, and returns the cycles into the
137
137
  # subtune it starts at.
138
138
  def start(cycles)
139
- state = @checkpoints&.latest(cycles)
140
- return start_afresh if state.nil?
139
+ sections = @checkpoints&.latest(cycles)
140
+ return start_afresh if sections.nil?
141
141
 
142
- input = Snapshot::StateReader.new(state)
143
- played = input.int
144
- restore_player(input)
142
+ modules = Snapshot::Keyed::Modules.new(sections)
143
+ audio = modules.required("BL-AUDIO")
144
+ restore_player(modules, audio)
145
145
  record
146
- player.stereo.sids.each { |sid| sid.load_recording(input) }
147
- @level = input.int
148
- @still = input.int
149
- played
146
+ player.stereo.sids.each_with_index { |sid, index| sid.load_recording_fields(audio, "sid#{index + 1}.rec.") }
147
+ @level = audio.int("level", 0)
148
+ @still = audio.int("still", 0)
149
+ audio.int("played", 0)
150
150
  end
151
151
 
152
152
  def start_afresh
@@ -157,13 +157,17 @@ module Badline
157
157
 
158
158
  def record = player.stereo.record(rate: @rate, filter_chunk: @filter_chunk, clock_hz: player.clock_hz)
159
159
 
160
- # Restores the player as it was, then puts back the SID models chosen
161
- # for this renderer.
162
- def restore_player(input)
160
+ # Restores the player as it was, on the SID models it was saved on,
161
+ # then puts back the SID models chosen for this renderer.
162
+ def restore_player(modules, audio)
163
163
  sids = player.stereo.sids
164
164
  chosen = sids.map(&:model)
165
- sids.each { |sid| refit(sid, Snapshot::C64Setup::SID_MODELS.fetch(input.int)) }
166
- player.load_state(input)
165
+ names = Snapshot::C64Setup::SID_MODELS.map(&:to_s)
166
+ sids.each_with_index do |sid, index|
167
+ name = audio.choice("sid#{index + 1}.model", names, sid.model.to_s)
168
+ refit(sid, Snapshot::C64Setup::SID_MODELS.find { |model| model.to_s == name } || sid.model)
169
+ end
170
+ player.load_modules(modules)
167
171
  sids.each_with_index { |sid, index| refit(sid, chosen[index]) }
168
172
  end
169
173
 
@@ -171,18 +175,25 @@ module Badline
171
175
  sid.model = model unless sid.model == model
172
176
  end
173
177
 
178
+ # The player's modules, after a BL-AUDIO module with the cycles into
179
+ # the subtune, each SID's model and how far its recording has got,
180
+ # and the silence count. The SIDs have caught up as the frame drained
181
+ # them, so they leave no samples behind.
174
182
  def save_checkpoint(played, clock_hz)
175
183
  checkpoints = @checkpoints
176
184
  return if checkpoints.nil? || !checkpoints.due?(played, clock_hz)
177
185
 
178
186
  sids = player.stereo.sids
179
- out = Snapshot::StateWriter.new
180
- out.int(played)
181
- sids.each { |sid| out.int(Snapshot::C64Setup::SID_MODELS.index(sid.model) || 0) }
182
- player.save_state(out)
183
- sids.each { |sid| sid.save_recording(out) }
184
- out.int(@level).int(@still)
185
- checkpoints.keep(played, out.state)
187
+ sections = player.save_modules
188
+ audio = Snapshot::Keyed.section("BL-AUDIO") do |out|
189
+ out.int("played", played)
190
+ sids.each_with_index do |sid, index|
191
+ out.text("sid#{index + 1}.model", sid.model.to_s)
192
+ sid.save_recording_fields(out, "sid#{index + 1}.rec.")
193
+ end
194
+ out.int("level", @level).int("still", @still)
195
+ end
196
+ checkpoints.keep(played, [audio, *sections])
186
197
  end
187
198
 
188
199
  # Counts the samples since the output last strayed more than QUIET
@@ -5,8 +5,8 @@ module Badline
5
5
  # The RAM the C128 KERNAL's bank numbers 0-15 reach, as its INDFET and
6
6
  # INDSTA reach it for LOAD, SAVE and the file name: the RAM bank of the
7
7
  # CR value the KERNAL's table at $F7F0 holds for the bank, with common
8
- # RAM in bank 0. The ROMs and I/O some of those CR values map are left
9
- # out.
8
+ # RAM in bank 0. The Bus lays the ROMs and I/O some of those CR values
9
+ # map over it.
10
10
  class Banks
11
11
  CONFIGURATIONS = 0xf7f0
12
12
 
@@ -18,16 +18,17 @@ module Badline
18
18
 
19
19
  def peek(bank, addr) = @ram.peek(physical(bank, addr))
20
20
 
21
- def write(bank, addr, bytes)
22
- bytes.each_with_index { |byte, i| @ram.poke(physical(bank, (addr + i) & 0xffff), byte) }
23
- end
21
+ # The CR value the KERNAL's table holds for the bank.
22
+ def configuration(bank) = @kernal_rom.peek(CONFIGURATIONS + (bank & 0x0f))
23
+
24
+ def poke(bank, addr, value) = @ram.poke(physical(bank, addr), value)
24
25
 
25
26
  private
26
27
 
27
28
  def physical(bank, addr)
28
29
  page = addr >> 8
29
30
  common = page < @mmu.common_low_pages || page >= @mmu.common_high_start
30
- ram_bank = common ? 0 : (@kernal_rom.peek(CONFIGURATIONS + (bank & 0x0f)) >> 6) & 0x01
31
+ ram_bank = common ? 0 : (configuration(bank) >> 6) & 0x01
31
32
  (ram_bank << 16) | addr
32
33
  end
33
34
  end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ class C128
5
+ class Bus
6
+ # The last access on the bus, which FAST mode's VIC and open bus read,
7
+ # in a keyed snapshot module (Snapshot::Keyed) under `prefix`, and
8
+ # what is worked out once the memories, the MMU and the chips are
9
+ # loaded: the banks each side sees and the keyboard's extra rows.
10
+ module KeyedState
11
+ def save_fields(out, prefix)
12
+ out.int("#{prefix}address", @address).int("#{prefix}data", @data)
13
+ end
14
+
15
+ def load_fields(input, prefix)
16
+ @address = input.word("#{prefix}address", @address)
17
+ @data = input.byte("#{prefix}data", @data)
18
+ end
19
+
20
+ # Both banks of colour RAM as bank0 and bank1.
21
+ def save_color_fields(out)
22
+ @color_lines.color_ram(0).save_fields(out, "bank0")
23
+ @color_lines.color_ram(1).save_fields(out, "bank1")
24
+ end
25
+
26
+ def load_color_fields(input)
27
+ @color_lines.color_ram(0).load_fields(input, "bank0")
28
+ @color_lines.color_ram(1).load_fields(input, "bank1")
29
+ end
30
+
31
+ # The 8502's port, as the C64's (C64::Bus::KeyedState).
32
+ def save_port_fields(out) = @cpu_port.save_fields(out, "port.")
33
+
34
+ def load_port_fields(input) = @cpu_port.load_fields(input, "port.")
35
+
36
+ def restored!
37
+ update_overlays!
38
+ @control_ports.extra_rows = 0xf8 | @vic.extra_keyboard_lines
39
+ end
40
+ end
41
+
42
+ include KeyedState
43
+ end
44
+ end
45
+ end
@@ -39,7 +39,6 @@ module Badline
39
39
  include MMUPages
40
40
  include IOPages
41
41
  include Z80Pages
42
- include SavedState
43
42
 
44
43
  # The 8502's port has seven pins. P0-P5 are the 6510's, and P6 senses
45
44
  # the CAPS LOCK key, high while it is up. Bit 7 has no pin, and holds
@@ -161,6 +160,8 @@ module Badline
161
160
 
162
161
  @read_pages = Array.new(256)
163
162
  @write_pages = Array.new(256)
163
+ @bank_io_reads = Array.new(256)
164
+ @bank_io_writes = Array.new(256)
164
165
  @address = 0
165
166
  @data = 0
166
167
  @io_mapped = false
@@ -211,10 +212,41 @@ module Badline
211
212
  # ROM at $C000-$FFFF.
212
213
  def system_rom_mapped? = !@mmu.c64_mode? && @mmu.cr.nobits?(0x30)
213
214
 
214
- # The RAM the C128 KERNAL's bank number +bank+ reaches (Banks).
215
- def peek_bank(bank, addr) = @banks.peek(bank, addr)
215
+ # The byte the C128 KERNAL's bank number +bank+ reads at +addr+, as
216
+ # its INDFET does: the ROMs and I/O the bank's CR value maps, over the
217
+ # RAM it reaches (Banks).
218
+ def peek_bank(bank, addr)
219
+ cr = @banks.configuration(bank)
220
+ return @mmu.peek_configuration(addr) if addr.between?(0xff00, 0xff04)
221
+ return bank_io_peek(addr) if cr.nobits?(0x01) && (addr & 0xf000) == 0xd000
222
+ return @basic_low_rom.peek(addr) if cr.nobits?(0x02) && addr.between?(0x4000, 0x7fff)
216
223
 
217
- def write_bank(bank, addr, bytes) = @banks.write(bank, addr, bytes)
224
+ select = addr < 0xc000 ? (cr >> 2) & 0x03 : (cr >> 4) & 0x03
225
+ return @banks.peek(bank, addr) if addr < 0x8000 || select == 3
226
+ return @open_bus.peek(addr) unless select.zero?
227
+
228
+ system_rom_peek(addr)
229
+ end
230
+
231
+ # Stores +bytes+ from +addr+ on in the C128 KERNAL's bank number
232
+ # +bank+, as its INDSTA does, with CR set to the bank's CR value for
233
+ # each byte and put back after it: into the chips where that value
234
+ # maps I/O, the MMU's at $D500 among them, into nothing at $FF00-$FF04,
235
+ # where the configuration registers take the byte, and otherwise into
236
+ # the RAM the bank reaches (Banks).
237
+ def write_bank(bank, addr, bytes)
238
+ cr = @mmu.cr
239
+ io = @banks.configuration(bank).nobits?(0x01)
240
+ bytes.each_with_index do |byte, i|
241
+ address = (addr + i) & 0xffff
242
+ if io && (address & 0xf000) == 0xd000
243
+ bank_io_poke(address, byte)
244
+ elsif !address.between?(0xff00, 0xff04)
245
+ @banks.poke(bank, address, byte)
246
+ end
247
+ end
248
+ @mmu.poke_configuration(0xff00, cr) unless @mmu.cr == cr
249
+ end
218
250
 
219
251
  def install_debug_register(&)
220
252
  @debug_page = DebugPage.new(@open_bus, &)
@@ -72,35 +72,16 @@ module Badline
72
72
  detach_drive1571 if @drive1571&.device == device
73
73
  end
74
74
 
75
- # A true 1541 (C64::Attachments#save_serial_bus), then a 1571 and
76
- # a 1581.
77
- def save_serial_bus(out)
78
- out.boolean(!@drive1541.nil?)
79
- if @drive1541
80
- out.int(@drive1541.device)
81
- @drive1541.save_state(out)
82
- end
83
- out.boolean(!@drive1571.nil?)
84
- if @drive1571
85
- out.int(@drive1571.device)
86
- @drive1571.save_state(out)
87
- end
88
- save_drive1581(out)
75
+ def save_drive1571_modules(sections)
76
+ drive1571 = @drive1571
77
+ drive1571&.save_modules(sections, "BL-D#{drive1571.device}")
78
+ sections
89
79
  end
90
80
 
91
- def load_serial_bus(input)
92
- load_drive1541(input)
93
- load_drive1571(input)
94
- load_drive1581(input)
95
- end
96
-
97
- def load_drive1571(input)
98
- return detach_drive1571 unless input.boolean?
99
-
100
- device = input.int
81
+ def load_drive1571_modules(modules, input, device)
101
82
  detach_drive1571 if @drive1571 && @drive1571.device != device
102
83
  attach_drive1571(Badline::Drives::Model1571.new(device:)) unless @drive1571
103
- @drive1571.load_state(input)
84
+ @drive1571.load_modules(modules, input, "BL-D#{device}")
104
85
  end
105
86
  end
106
87
  end