badline 0.16.1 → 0.18.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 (256) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +213 -0
  3. data/CLAUDE.md +11 -7
  4. data/CONTRIBUTING.md +7 -1
  5. data/README.md +265 -26
  6. data/doc/media.md +5 -2
  7. data/doc/pinned-behaviour.md +189 -0
  8. data/doc/snapshot-format.md +616 -74
  9. data/doc/snapshots.md +192 -24
  10. data/exe/badline-ruby +5 -0
  11. data/lib/badline/audio/terminal.rb +34 -2
  12. data/lib/badline/c128/keyed_state.rb +2 -0
  13. data/lib/badline/c128.rb +1 -0
  14. data/lib/badline/c64/keyed_state.rb +2 -0
  15. data/lib/badline/c64.rb +1 -0
  16. data/lib/badline/cbm2/acia.rb +10 -0
  17. data/lib/badline/cbm2/bus.rb +263 -0
  18. data/lib/badline/cbm2/cpu.rb +58 -0
  19. data/lib/badline/cbm2/crtc_video.rb +133 -0
  20. data/lib/badline/cbm2/ieee_drive.rb +18 -0
  21. data/lib/badline/cbm2/ieee_layout.rb +69 -0
  22. data/lib/badline/cbm2/ieee_ports.rb +104 -0
  23. data/lib/badline/cbm2/ieee_traps.rb +81 -0
  24. data/lib/badline/cbm2/keyboard_ports.rb +60 -0
  25. data/lib/badline/cbm2/keyed_modules.rb +67 -0
  26. data/lib/badline/cbm2/keyed_state.rb +95 -0
  27. data/lib/badline/cbm2/model.rb +44 -0
  28. data/lib/badline/cbm2/p500/control_ports.rb +80 -0
  29. data/lib/badline/cbm2/p500/ieee_layout.rb +41 -0
  30. data/lib/badline/cbm2/p500/keyed_state.rb +85 -0
  31. data/lib/badline/cbm2/p500/model.rb +39 -0
  32. data/lib/badline/cbm2/p500/pages.rb +57 -0
  33. data/lib/badline/cbm2/p500.rb +264 -0
  34. data/lib/badline/cbm2/sid_page.rb +19 -0
  35. data/lib/badline/cbm2.rb +270 -0
  36. data/lib/badline/{plus4 → chips}/acia/frames.rb +1 -1
  37. data/lib/badline/chips/acia/keyed_state.rb +47 -0
  38. data/lib/badline/chips/acia.rb +307 -0
  39. data/lib/badline/chips/cia/timer.rb +2 -2
  40. data/lib/badline/chips/cpu/instruction_set.rb +1 -1
  41. data/lib/badline/chips/crtc.rb +3 -2
  42. data/lib/badline/chips/m6809/arithmetic.rb +140 -0
  43. data/lib/badline/chips/m6809/bus_steps.rb +223 -0
  44. data/lib/badline/chips/m6809/core.rb +149 -0
  45. data/lib/badline/chips/m6809/indexed.rb +82 -0
  46. data/lib/badline/chips/m6809/interrupts.rb +159 -0
  47. data/lib/badline/chips/m6809/keyed_state.rb +147 -0
  48. data/lib/badline/chips/m6809/opcodes.rb +159 -0
  49. data/lib/badline/chips/m6809/operations.rb +258 -0
  50. data/lib/badline/chips/m6809/plans.rb +143 -0
  51. data/lib/badline/chips/m6809/registers.rb +82 -0
  52. data/lib/badline/chips/m6809/stack.rb +140 -0
  53. data/lib/badline/chips/m6809.rb +13 -0
  54. data/lib/badline/chips/riot/fast_forward.rb +72 -0
  55. data/lib/badline/chips/riot/keyed_parts.rb +69 -0
  56. data/lib/badline/chips/riot/timer.rb +91 -0
  57. data/lib/badline/chips/riot.rb +162 -0
  58. data/lib/badline/chips/rriot/keyed_state.rb +30 -0
  59. data/lib/badline/chips/rriot.rb +134 -0
  60. data/lib/badline/chips/sid/filter.rb +4 -4
  61. data/lib/badline/chips/sid/waveform.rb +6 -6
  62. data/lib/badline/chips/tpi/keyed_state.rb +38 -0
  63. data/lib/badline/chips/tpi.rb +233 -0
  64. data/lib/badline/chips/vic/bank.rb +60 -5
  65. data/lib/badline/chips/vic/sprite.rb +4 -3
  66. data/lib/badline/chips/vic_i/keyed_parts.rb +7 -2
  67. data/lib/badline/chips/vic_i.rb +84 -21
  68. data/lib/badline/data_path.rb +3 -3
  69. data/lib/badline/drives/datasette/keyed_state.rb +9 -1
  70. data/lib/badline/drives/datasette.rb +27 -3
  71. data/lib/badline/drives/ieee_models.rb +54 -0
  72. data/lib/badline/drives/model1001.rb +37 -0
  73. data/lib/badline/drives/model2031.rb +6 -0
  74. data/lib/badline/drives/model4040/disk.rb +62 -0
  75. data/lib/badline/drives/model4040/ip_bus.rb +46 -0
  76. data/lib/badline/drives/model4040/keyed_state.rb +57 -0
  77. data/lib/badline/drives/model4040/mechanism.rb +192 -0
  78. data/lib/badline/drives/model4040/parts.rb +20 -0
  79. data/lib/badline/drives/model4040.rb +179 -0
  80. data/lib/badline/drives/model8050/board.rb +77 -0
  81. data/lib/badline/drives/model8050/disk.rb +163 -0
  82. data/lib/badline/drives/model8050/dos_board.rb +91 -0
  83. data/lib/badline/drives/model8050/fdc_bus.rb +102 -0
  84. data/lib/badline/drives/model8050/idle.rb +181 -0
  85. data/lib/badline/drives/model8050/ieee_port.rb +147 -0
  86. data/lib/badline/drives/model8050/ip_bus.rb +93 -0
  87. data/lib/badline/drives/model8050/keyed_state.rb +163 -0
  88. data/lib/badline/drives/model8050/mechanism.rb +230 -0
  89. data/lib/badline/drives/model8050/parts.rb +37 -0
  90. data/lib/badline/drives/model8050.rb +131 -0
  91. data/lib/badline/drives/model9060/idle.rb +149 -0
  92. data/lib/badline/drives/model9060/keyed_state.rb +123 -0
  93. data/lib/badline/drives/model9060/parts.rb +98 -0
  94. data/lib/badline/drives/model9060/sasi_bus.rb +88 -0
  95. data/lib/badline/drives/model9060/sasi_commands.rb +168 -0
  96. data/lib/badline/drives/model9060/sasi_target.rb +161 -0
  97. data/lib/badline/drives/model9060.rb +195 -0
  98. data/lib/badline/frontend/app.rb +18 -3
  99. data/lib/badline/frontend/boot.rb +4 -3
  100. data/lib/badline/frontend/controls.rb +107 -15
  101. data/lib/badline/frontend/gamepads.rb +3 -3
  102. data/lib/badline/frontend/keys.rb +97 -2
  103. data/lib/badline/frontend/kim1_panel.rb +208 -0
  104. data/lib/badline/frontend/media_slots.rb +8 -1
  105. data/lib/badline/frontend/menu/drive_types.rb +56 -0
  106. data/lib/badline/frontend/menu/menu_dialogs.rb +61 -14
  107. data/lib/badline/frontend/menu/menu_media.rb +47 -14
  108. data/lib/badline/frontend/menu/menu_pages.rb +113 -4
  109. data/lib/badline/frontend/menu/menu_ports.rb +77 -4
  110. data/lib/badline/frontend/menu/pause_menu.rb +6 -2
  111. data/lib/badline/frontend/pet_keyboard.rb +45 -7
  112. data/lib/badline/frontend/screen.rb +1 -1
  113. data/lib/badline/frontend/screens.rb +40 -2
  114. data/lib/badline/frontend/teletype_pane.rb +152 -0
  115. data/lib/badline/frontend/timeline.rb +49 -11
  116. data/lib/badline/frontend.rb +3 -0
  117. data/lib/badline/kernal_trap/dos/disk_commands.rb +80 -0
  118. data/lib/badline/kernal_trap/dos/status.rb +3 -0
  119. data/lib/badline/kernal_trap/dos.rb +18 -5
  120. data/lib/badline/kernal_trap/ieee.rb +4 -2
  121. data/lib/badline/kernal_trap/ieee_layout.rb +95 -43
  122. data/lib/badline/kim1/bus.rb +117 -0
  123. data/lib/badline/kim1/cpu.rb +25 -0
  124. data/lib/badline/kim1/host_terminal.rb +74 -0
  125. data/lib/badline/kim1/key_entry.rb +74 -0
  126. data/lib/badline/kim1/keyed_modules.rb +53 -0
  127. data/lib/badline/kim1/keyed_state.rb +133 -0
  128. data/lib/badline/kim1/panel.rb +176 -0
  129. data/lib/badline/kim1/rriots.rb +28 -0
  130. data/lib/badline/kim1/sound.rb +29 -0
  131. data/lib/badline/kim1/tape.rb +116 -0
  132. data/lib/badline/kim1/teletype.rb +193 -0
  133. data/lib/badline/kim1.rb +190 -0
  134. data/lib/badline/machine/ieee_drives.rb +171 -0
  135. data/lib/badline/machine/keyboard_buffer.rb +20 -3
  136. data/lib/badline/machine/line_sound.rb +122 -0
  137. data/lib/badline/machine/settling.rb +4 -0
  138. data/lib/badline/machine.rb +62 -14
  139. data/lib/badline/media/cbm2_media.rb +29 -0
  140. data/lib/badline/media/extensions.rb +8 -4
  141. data/lib/badline/media/kim1_media.rb +150 -0
  142. data/lib/badline/media/pet_media.rb +3 -1
  143. data/lib/badline/media/program.rb +6 -3
  144. data/lib/badline/media/true_drive.rb +51 -25
  145. data/lib/badline/media.rb +23 -8
  146. data/lib/badline/options/family.rb +108 -6
  147. data/lib/badline/options/help.rb +58 -12
  148. data/lib/badline/options/table.rb +60 -14
  149. data/lib/badline/options/validation.rb +71 -11
  150. data/lib/badline/options.rb +46 -13
  151. data/lib/badline/pet/bus.rb +16 -2
  152. data/lib/badline/pet/cassettes.rb +22 -1
  153. data/lib/badline/pet/ieee_drive.rb +5 -64
  154. data/lib/badline/pet/ieee_ports.rb +7 -0
  155. data/lib/badline/pet/keyboard_ports.rb +9 -2
  156. data/lib/badline/pet/keyed_state.rb +4 -0
  157. data/lib/badline/pet/model.rb +11 -2
  158. data/lib/badline/pet/rom_sets.rb +1 -0
  159. data/lib/badline/pet/sound.rb +5 -105
  160. data/lib/badline/pet/superpet/acia.rb +12 -0
  161. data/lib/badline/pet/superpet/board.rb +221 -0
  162. data/lib/badline/pet/superpet/dongle.rb +81 -0
  163. data/lib/badline/pet/superpet/keyed_modules.rb +63 -0
  164. data/lib/badline/pet/superpet/keyed_parts.rb +76 -0
  165. data/lib/badline/pet/superpet/m6809.rb +32 -0
  166. data/lib/badline/pet/superpet/waterloo_traps.rb +199 -0
  167. data/lib/badline/pet/superpet.rb +216 -0
  168. data/lib/badline/pet.rb +33 -6
  169. data/lib/badline/plus4/acia.rb +4 -298
  170. data/lib/badline/plus4/keyed_parts.rb +0 -44
  171. data/lib/badline/plus4/keyed_state.rb +2 -0
  172. data/lib/badline/plus4.rb +2 -0
  173. data/lib/badline/roms/1001/fdc-251257-02a.rom +0 -0
  174. data/lib/badline/roms/4040/dos-1.2.rom +0 -0
  175. data/lib/badline/roms/4040/dos-2.1-rev.rom +0 -0
  176. data/lib/badline/roms/4040/dos-2.1.rom +0 -0
  177. data/lib/badline/roms/4040/fdc-901466-02.rom +0 -0
  178. data/lib/badline/roms/4040/fdc-901466-04.rom +0 -0
  179. data/lib/badline/roms/8050/dos-2.7.rom +0 -0
  180. data/lib/badline/roms/8050/fdc-901869-01.rom +0 -0
  181. data/lib/badline/roms/8050/fdc-901885-04.rom +0 -0
  182. data/lib/badline/roms/9060/dos-3.0.rom +0 -0
  183. data/lib/badline/roms/9060/sasi-300515-revb.rom +0 -0
  184. data/lib/badline/roms/README +277 -0
  185. data/lib/badline/roms/cbm2/basic128-02b.rom +0 -0
  186. data/lib/badline/roms/cbm2/basic128.rom +0 -0
  187. data/lib/badline/roms/cbm2/basic256.rom +0 -0
  188. data/lib/badline/roms/cbm2/characters-600.rom +0 -0
  189. data/lib/badline/roms/cbm2/characters-700.rom +0 -0
  190. data/lib/badline/roms/cbm2/kernal-03b.rom +0 -0
  191. data/lib/badline/roms/cbm2/kernal-04a.rom +0 -0
  192. data/lib/badline/roms/cbm2/p500-basic.rom +0 -0
  193. data/lib/badline/roms/cbm2/p500-kernal.rom +0 -0
  194. data/lib/badline/roms/kim1/6530-002.rom +0 -0
  195. data/lib/badline/roms/kim1/6530-003.rom +0 -0
  196. data/lib/badline/roms/pet/characters-superpet.rom +0 -0
  197. data/lib/badline/roms/pet/superpet-a000.rom +0 -0
  198. data/lib/badline/roms/pet/superpet-c000.rom +0 -0
  199. data/lib/badline/roms/pet/superpet-e000-50hz.rom +0 -0
  200. data/lib/badline/roms/pet/superpet-e000.rom +0 -0
  201. data/lib/badline/snapshot/cbm2_setup.rb +53 -0
  202. data/lib/badline/snapshot/container.rb +9 -2
  203. data/lib/badline/snapshot/field_doc/markdown.rb +4 -2
  204. data/lib/badline/snapshot/field_doc.rb +59 -3
  205. data/lib/badline/snapshot/image.rb +65 -9
  206. data/lib/badline/snapshot/kim1_setup.rb +28 -0
  207. data/lib/badline/snapshot/pet_setup.rb +27 -11
  208. data/lib/badline/snapshot/storage_setup.rb +4 -1
  209. data/lib/badline/snapshot/vic20_setup.rb +11 -7
  210. data/lib/badline/snapshot/vice/cbm2.rb +93 -0
  211. data/lib/badline/snapshot/vice/cbm2_acia.rb +75 -0
  212. data/lib/badline/snapshot/vice/cbm2_crtc.rb +127 -0
  213. data/lib/badline/snapshot/vice/cbm2_mains.rb +49 -0
  214. data/lib/badline/snapshot/vice/cbm2_mem.rb +75 -0
  215. data/lib/badline/snapshot/vice/cbm2_restore.rb +204 -0
  216. data/lib/badline/snapshot/vice/cbm2_tpi.rb +58 -0
  217. data/lib/badline/snapshot/vice/cias.rb +13 -9
  218. data/lib/badline/snapshot/vice/p500.rb +96 -0
  219. data/lib/badline/snapshot/vice/p500_restore.rb +130 -0
  220. data/lib/badline/snapshot/vice/p500_vicii.rb +166 -0
  221. data/lib/badline/snapshot/vice/pet.rb +20 -10
  222. data/lib/badline/snapshot/vice/pet_cpu.rb +42 -5
  223. data/lib/badline/snapshot/vice/pet_crtc.rb +25 -17
  224. data/lib/badline/snapshot/vice/pet_drive.rb +180 -0
  225. data/lib/badline/snapshot/vice/pet_drive8050.rb +336 -0
  226. data/lib/badline/snapshot/vice/pet_mem.rb +49 -5
  227. data/lib/badline/snapshot/vice/pet_restore.rb +100 -7
  228. data/lib/badline/snapshot/vice/pet_via.rb +6 -5
  229. data/lib/badline/snapshot/vice/sid_registers.rb +2 -1
  230. data/lib/badline/snapshot/vice/vicii_import.rb +10 -6
  231. data/lib/badline/snapshot/vice.rb +3 -0
  232. data/lib/badline/snapshot.rb +15 -3
  233. data/lib/badline/status.rb +30 -30
  234. data/lib/badline/storage/crt_file.rb +7 -1
  235. data/lib/badline/storage/d64_image.rb +7 -0
  236. data/lib/badline/storage/d67_image.rb +55 -0
  237. data/lib/badline/storage/d80_image.rb +17 -1
  238. data/lib/badline/storage/d81_image.rb +14 -0
  239. data/lib/badline/storage/d90_image.rb +180 -0
  240. data/lib/badline/storage/disk_image/bam.rb +18 -3
  241. data/lib/badline/storage/disk_image/formatting.rb +69 -0
  242. data/lib/badline/storage/disk_image/writing.rb +37 -2
  243. data/lib/badline/storage/disk_image.rb +2 -0
  244. data/lib/badline/storage/kim_tape.rb +254 -0
  245. data/lib/badline/storage/papertape.rb +90 -0
  246. data/lib/badline/storage.rb +5 -0
  247. data/lib/badline/support/cbm2_checkpoint.rb +48 -0
  248. data/lib/badline/support/kim1_checkpoint.rb +41 -0
  249. data/lib/badline/support/p500_checkpoint.rb +35 -0
  250. data/lib/badline/support/superpet_checkpoint.rb +35 -0
  251. data/lib/badline/version.rb +1 -1
  252. data/lib/badline/vic20/keyed_modules.rb +6 -3
  253. data/lib/badline/vic20/keyed_state.rb +6 -4
  254. data/lib/badline/vic20.rb +26 -12
  255. data/lib/badline.rb +2 -0
  256. metadata +148 -2
@@ -0,0 +1,166 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ module Snapshot
5
+ module Vice
6
+ module P500
7
+ # VIC-II 1.3 as xcbm5x0 writes it, which runs VICE's line-based
8
+ # VIC-II rather than x64sc's, so the module is laid out otherwise
9
+ # than x64sc's under the same name: whether bad lines may happen,
10
+ # whether this line is one, the vertical border, the colour
11
+ # buffer, colour RAM, whether the sequencer is idle, the light
12
+ # pen's trigger and position, the video matrix buffer, the sprites
13
+ # whose DMA starts next line, a word xcbm5x0 keeps at $00100000,
14
+ # the beam's cycle and line, the registers, the sprite collisions
15
+ # pending, the sprites' DMA, the VIC's bank, VC, the matrix's
16
+ # width, VCBASE, then sprite and drawing fields with the bank
17
+ # again, the line its picture draws next and its size, and the
18
+ # picture. xcbm5x0's cycle in the line is badline's column.
19
+ #
20
+ # Written: the registers, colour RAM, the buffers, the beam with
21
+ # its counters and the sprites' DMA as badline's VIC has them, the
22
+ # bank as TPI2's PC6 and PC7 pick it, and the rest as xcbm5x0
23
+ # leaves it. The picture is left blank, as xcbm5x0 paints it again.
24
+ #
25
+ # Read: the registers, colour RAM, the buffers, the beam with VC,
26
+ # VCBASE and whether the sequencer is idle, the light pen and the
27
+ # sprites' DMA, through the x64sc module's reader (VICII::Import).
28
+ # The row counter follows from the line and YSCROLL, and the
29
+ # sprites' counters and the borders' flip-flops start afresh.
30
+ # xcbm5x0's VIC isn't x64sc's, so a program badline runs on from its
31
+ # snapshot reads the raster a cycle or so off now and then.
32
+ module VICII
33
+ NAME = Vice::VICII::NAME
34
+ MAJOR = 1
35
+ MINOR = 3
36
+ ALLOW_BAD_LINES = 0x000
37
+ BAD_LINE = 0x001
38
+ BLANK = 0x002
39
+ COLOR_BUFFER = 0x003
40
+ COLOR_RAM = 0x02b
41
+ IDLE = 0x42b
42
+ LIGHT_PEN = 0x42c
43
+ MATRIX_BUFFER = 0x42f
44
+ RAM_BASE = 0x458
45
+ CYCLE = 0x45c
46
+ LINE = 0x45d
47
+ REGISTERS = 0x45f
48
+ SPRITE_DMA = 0x4a0
49
+ BANK = 0x4a2
50
+ VC = 0x4a4
51
+ VC_ADD = 0x4a6
52
+ VC_BASE = 0x4a7
53
+ VIDEO_INT = 0x4a9
54
+ DRAW_FLAGS = 0x4cd
55
+ FETCH_BANK = 0x4cf
56
+ DRAWN_LINE = 0x4d1
57
+ PICTURE = 0x4d5
58
+ HEADER = 0x4e1
59
+ # The picture's size: PAL's and NTSC's.
60
+ SIZES = { 312 => [504, 312], 263 => [520, 275] }.freeze
61
+
62
+ module_function
63
+
64
+ def reads?(section) = section.major == MAJOR && section.minor == MINOR
65
+
66
+ # The lines xcbm5x0's picture has, which name its region.
67
+ def picture_lines(section) = FieldReader.new(section).skip(PICTURE + 4).dword
68
+
69
+ def export(machine)
70
+ vic = machine.vic
71
+ width, height = SIZES.fetch(vic.region.lines_per_frame)
72
+ bytes = Array.new(HEADER + (width * (height + 4)), 0)
73
+ sequencer(vic, bytes)
74
+ bytes[COLOR_RAM, 0x400] = Array.new(0x400) { |offset| machine.color_ram.nibble(0xd400 + offset) }
75
+ beam(vic, bytes)
76
+ bank = ((machine.tpi2.port_c_output >> 6) & 0x03) << 14
77
+ [BANK, FETCH_BANK].each { |at| word(bytes, at, bank) }
78
+ [RAM_BASE + 2, DRAW_FLAGS].each { |at| bytes[at] = 0x10 }
79
+ bytes[VIDEO_INT] = 1
80
+ [vic.rasterline, width, height, width].each_with_index do |value, n|
81
+ dword(bytes, DRAWN_LINE + (4 * n), value)
82
+ end
83
+ FieldWriter.new.bytes(bytes).section(NAME, MAJOR, MINOR)
84
+ end
85
+
86
+ # The sequencer's state and the buffers it reads.
87
+ def sequencer(vic, bytes)
88
+ state = vic.display_state
89
+ bytes[ALLOW_BAD_LINES] = state.bad_lines_enabled ? 1 : 0
90
+ bytes[BAD_LINE] = state.bad_line_condition? ? 1 : 0
91
+ bytes[BLANK] = vic.sequencer.borders.first ? 1 : 0
92
+ bytes[COLOR_BUFFER, 40] = vic.color_buffer.map { |value| value || 0 }
93
+ bytes[IDLE] = state.display? ? 0 : 1
94
+ bytes[MATRIX_BUFFER, 40] = vic.character_buffer.map { |value| value || 0 }
95
+ end
96
+
97
+ # The beam, the registers and the counters.
98
+ def beam(vic, bytes)
99
+ state = vic.display_state
100
+ bytes[CYCLE] = vic.column
101
+ word(bytes, LINE, vic.rasterline)
102
+ bytes[REGISTERS, 0x40] = vic.register_file
103
+ bytes[SPRITE_DMA] = 8.times.sum { |n| vic.sprites[n].displaying? ? 1 << n : 0 }
104
+ word(bytes, VC, state.vc)
105
+ bytes[VC_ADD] = 40
106
+ word(bytes, VC_BASE, state.vc_base)
107
+ end
108
+
109
+ def word(bytes, at, value) = bytes[at, 2] = [value & 0xffff].pack("v").bytes
110
+
111
+ def dword(bytes, at, value) = bytes[at, 4] = [value].pack("V").bytes
112
+
113
+ def import(section, vic, color_ram)
114
+ Import.new(section, vic.region).apply_to(vic, color_ram, 0xd400)
115
+ end
116
+
117
+ # Reads xcbm5x0's module into the fields x64sc's reader puts into
118
+ # badline's VIC.
119
+ class Import < Vice::VICII::Import
120
+ # x64sc's cycle is badline's column plus one, its cycle 0 the
121
+ # last column of the line before (VICII.vice_cycle).
122
+ def initialize(section, region)
123
+ super(nil)
124
+ bytes = section.data.bytes
125
+ @registers = bytes[REGISTERS, 0x40]
126
+ line = bytes[LINE] | (bytes[LINE + 1] << 8)
127
+ @cycle = (bytes[CYCLE] + 1) % region.cycles_per_line
128
+ @line = @cycle.zero? ? (line + 1) % region.lines_per_frame : line
129
+ @frame_start = false
130
+ @irq_status = @registers[0x19]
131
+ @raster_match = false
132
+ @character_buffer = bytes[MATRIX_BUFFER, 40]
133
+ @color_buffer = bytes[COLOR_BUFFER, 40]
134
+ read_old_counters(bytes)
135
+ @color_ram = bytes[COLOR_RAM, 0x400]
136
+ @sprite_mc = Array.new(8, 0)
137
+ @sprite_mcbase = Array.new(8, 0)
138
+ @sprite_exp_ff = 0
139
+ end
140
+
141
+ private
142
+
143
+ def read_old_counters(bytes)
144
+ @bad_lines_enabled = !bytes[ALLOW_BAD_LINES].zero?
145
+ @collisions = [@registers[0x1e], @registers[0x1f]]
146
+ @display = bytes[IDLE].zero?
147
+ @vc_base = bytes[VC_BASE] | (bytes[VC_BASE + 1] << 8)
148
+ @vc = bytes[VC] | (bytes[VC + 1] << 8)
149
+ @rc = @display ? (@line - @registers[0x11]) & 0x07 : 7
150
+ @vmli = 0
151
+ @lp_low = false
152
+ @lp_triggered = !bytes[LIGHT_PEN].zero?
153
+ @fetch_d011 = @registers[0x11]
154
+ @dma_bits = bytes[SPRITE_DMA]
155
+ @display_bits = @dma_bits
156
+ blank = !bytes[BLANK].zero?
157
+ @vertical_border = blank
158
+ @vertical_armed = false
159
+ @main_border = blank
160
+ end
161
+ end
162
+ end
163
+ end
164
+ end
165
+ end
166
+ end
@@ -1,17 +1,21 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "badline/snapshot/vice/cbm2_acia"
3
4
  require "badline/snapshot/vice/pet_cpu"
4
5
  require "badline/snapshot/vice/pet_mem"
5
6
  require "badline/snapshot/vice/pet_pias"
6
7
  require "badline/snapshot/vice/pet_via"
7
8
  require "badline/snapshot/vice/pet_crtc"
9
+ require "badline/snapshot/vice/pet_drive"
10
+ require "badline/snapshot/vice/pet_drive8050"
8
11
  require "badline/snapshot/vice/pet_restore"
9
12
 
10
13
  module Badline
11
14
  module Snapshot
12
15
  module Vice
13
- # xpet's modules for the PET: the CPU, the RAM, the CRTC, both PIAs
14
- # and the VIA, written from badline's machine and read into it. Each
16
+ # xpet's modules for the PET: the CPU, the RAM, the CRTC, both PIAs,
17
+ # the VIA and a true 2031, written from badline's machine and read
18
+ # into it. Each
15
19
  # module's file says what maps and what is approximated. They go
16
20
  # through the PET's own keyed fields (Keyed), which a save writes and a
17
21
  # load reads, so they read no chip's insides.
@@ -19,8 +23,8 @@ module Badline
19
23
  # The models badline builds by the board PETMEM names (PETMem): the
20
24
  # discrete board is a 2001's where xpet's settings say so, and a
21
25
  # 3032 otherwise.
22
- MODELS = { PETMem::CRTC40 => "4032", PETMem::CRTC80 => "8032", PETMem::CBM8096 => "8096",
23
- PETMem::CBM8296 => "8296" }.freeze
26
+ MODELS = { PETMem::CRTC40 => "4032", PETMem::CRTC80 => "8032", PETMem::SUPERPET => "superpet",
27
+ PETMem::CBM8096 => "8096", PETMem::CBM8296 => "8296" }.freeze
24
28
  # The modules xpet resets the machine without, for the parts badline
25
29
  # doesn't model in VICE's terms, each as xpet leaves them with
26
30
  # nothing attached: no file system drive open, both cassette ports
@@ -45,8 +49,10 @@ module Badline
45
49
  [MainCPU.export(machine), CPU6809.export(machine), PETMem.export(machine),
46
50
  CRTC.export(machine, reader(readers, "BL-CRTC"), reader(readers, "BL-VIDEO")),
47
51
  PIAs.export("PIA1", machine.pia1, reader(readers, "BL-PIA1")),
48
- PIAs.export("PIA2", machine.pia2, reader(readers, "BL-PIA2")), VIA.export(reader(readers, "BL-VIA"))] +
49
- PERIPHERALS.map { |name, major, minor, bytes| FieldWriter.new.bytes(bytes).section(name, major, minor) }
52
+ PIAs.export("PIA2", machine.pia2, reader(readers, "BL-PIA2")), VIA.export(reader(readers, "BL-VIA")),
53
+ *Drive2031.export(machine, readers), *Drive8050.export(machine, readers)] +
54
+ PERIPHERALS.map { |name, major, minor, bytes| FieldWriter.new.bytes(bytes).section(name, major, minor) } +
55
+ (machine.superpet? ? [CBM2::ACIA.export(reader(readers, "BL-ACIA"), machine.cycles)] : [])
50
56
  end
51
57
 
52
58
  def reader(readers, name) = readers[name] || Keyed::Reader.missing(name)
@@ -57,7 +63,9 @@ module Badline
57
63
  # xpet doesn't save the keyboard, the ROMs or an 8296's jumpers, so
58
64
  # a discrete board is a 3032 with the graphics keyboard unless it is
59
65
  # a 2001, each model has its own ROMs, and an 8296 none of its
60
- # jumpers, as xpet's own has.
66
+ # jumpers, as xpet's own has. Nor does it save the SuperPET's
67
+ # switches: its CPU switch is at the CPU that has the bus, and its
68
+ # RAM switch at R/W, which is the only position xpet has.
61
69
  def setup(container)
62
70
  memory = memory(container)
63
71
  ram_kb = PETMem.ram_kb(memory)
@@ -71,8 +79,11 @@ module Badline
71
79
  crtc = nil unless crtc.nil? || CRTC.reads?(crtc)
72
80
  refresh_hz = refresh_hz(crtc, profile)
73
81
  profile = Badline::PET::Model.upgraded(profile, nil, editor(crtc, profile, refresh_hz))
82
+ superpet = profile.superpet?
83
+ cpu_switch = PETMem.m6809?(memory) ? "6809" : "6502"
74
84
  PETSetup.new(model: name, ram_kb:, refresh_hz:, basic: profile.basic, editor: profile.editor(refresh_hz),
75
- kernal: profile.kernal, characters: profile.characters, jumpers: "")
85
+ kernal: profile.kernal, characters: profile.characters, jumpers: "",
86
+ cpu_switch: superpet ? cpu_switch : "", ram_switch: superpet ? "rw" : "")
76
87
  end
77
88
 
78
89
  def memory(container)
@@ -90,8 +101,7 @@ module Badline
90
101
  name = MODELS[board]
91
102
  return name unless name.nil?
92
103
 
93
- what = board == PETMem::SUPERPET ? "a SuperPET" : "board #{board}"
94
- raise FormatError, "#{memory}: #{what}, which badline doesn't build"
104
+ raise FormatError, "#{memory}: board #{board}, which badline doesn't build"
95
105
  end
96
106
 
97
107
  # The editor's frame rate, from the cycles the CRTC's registers set
@@ -26,13 +26,20 @@ module Badline
26
26
  def reads?(section) = section.major == MAJOR && section.minor == MINOR
27
27
 
28
28
  def export(machine)
29
+ sources = [machine.pia1.irq?, machine.pia2.irq?, machine.via.irq?]
30
+ sources << machine.acia_irq? if machine.superpet?
31
+ write(machine, sources.count(true))
32
+ end
33
+
34
+ # The module for `machine`, whose CPU's IRQ line `irqs` sources
35
+ # pull.
36
+ def write(machine, irqs)
29
37
  cpu = machine.cpu
30
38
  fields = FieldWriter.new.qword(machine.cycles)
31
39
  [cpu.a, cpu.x, cpu.y, cpu.stack_pointer].each { |register| fields.byte(register) }
32
40
  fields.word(cpu.program_counter).byte(cpu.p)
33
41
  fields.dword(0).dword(0).dword(0)
34
42
  5.times { fields.qword(0) }
35
- irqs = [machine.pia1.irq?, machine.pia2.irq?, machine.via.irq?].count(true)
36
43
  fields.dword(irqs).dword(0).dword(irqs.positive? ? IK_IRQ : 0)
37
44
  fields.section(NAME, MAJOR, MINOR)
38
45
  end
@@ -49,16 +56,46 @@ module Badline
49
56
  end
50
57
 
51
58
  # The SuperPET's 6809, CPU6809 1.1, which xpet saves on every model
52
- # and won't load a snapshot without: the clock and the registers as
53
- # an xpet with no SuperPET board leaves them. badline doesn't read
54
- # it.
59
+ # and won't load a snapshot without: the clock, X, Y, U, S and PC,
60
+ # DP, CC, A and B, then five bytes. xpet 3.10's layout was inferred
61
+ # from its snapshots of a 6809 running a loop that loads each
62
+ # register with a value of its own; the five bytes are 0 in every
63
+ # snapshot xpet wrote.
64
+ #
65
+ # Written: the clock and the registers, the SuperPET's 6809's
66
+ # between instructions while it has the bus, and otherwise the
67
+ # registers of a 6809 that hasn't run, as xpet leaves them. Read:
68
+ # the registers, with the 6809 on an instruction boundary, as
69
+ # xpet's snapshot is taken on one.
55
70
  module CPU6809
56
71
  NAME = "CPU6809"
72
+ MAJOR = 1
73
+ MINOR = 1
74
+ # The CC an xpet without a SuperPET board saves.
75
+ IDLE_CC = 0x04
57
76
 
58
77
  module_function
59
78
 
79
+ def reads?(section) = section.major == MAJOR && section.minor == MINOR
80
+
60
81
  def export(machine)
61
- FieldWriter.new.qword(machine.cycles).zeros(11).byte(0x04).zeros(7).section(NAME, 1, 1)
82
+ fields = FieldWriter.new.qword(machine.cycles)
83
+ unless machine.superpet? && machine.m6809_running?
84
+ return fields.zeros(11).byte(IDLE_CC).zeros(7).section(NAME, MAJOR, MINOR)
85
+ end
86
+
87
+ cpu = machine.m6809
88
+ [cpu.x, cpu.y, cpu.u, cpu.s, cpu.pc].each { |register| fields.word(register) }
89
+ fields.byte(cpu.dp).byte(cpu.cc).byte(cpu.a).byte(cpu.b).zeros(5).section(NAME, MAJOR, MINOR)
90
+ end
91
+
92
+ # The registers by their BL-M6809 keys, in a keyed module's writer,
93
+ # with the 6809's cycle count the machine's `cycles`.
94
+ def import(section, out, cycles)
95
+ fields = FieldReader.new(section).skip(8)
96
+ %w[x y u s pc].each { |name| out.int(name, fields.word) }
97
+ %w[dp cc a b].each { |name| out.int(name, fields.byte) }
98
+ out.int("cycles", cycles).bool("nmi.armed", true)
62
99
  end
63
100
  end
64
101
  end
@@ -6,7 +6,7 @@ module Badline
6
6
  module PET
7
7
  # CRTC 1.2, which xpet saves on every model, the discrete boards'
8
8
  # video too, as a 6545 with the registers fixed: the board's screen
9
- # settings, the registers, the one selected, the beam's character,
9
+ # settings, the character set VIA CA2 picks, the registers, the one selected, the beam's character,
10
10
  # row and line in the row, whether the frame is in its vertical
11
11
  # adjust, the row's start in screen RAM, the lines of vertical sync
12
12
  # left, whether the rows show, the picture's size, the lines in the
@@ -36,6 +36,13 @@ module Badline
36
36
  SETTINGS = [0xff, 0x03, 0x00, 0x20, 0x00, 0x20, 0x00, 0x10, 0xff, 0x1f].freeze
37
37
  # The 8296's, whose screen RAM spans 4K.
38
38
  SETTINGS_8296 = [0xff, 0x0f, 0x00, 0x20, 0x00, 0x20, 0xff, 0x0f, 0xff, 0x1f].freeze
39
+ # The SuperPET's, whose character ROM spans 4K, inferred from
40
+ # xpet 3.10's SuperPET snapshots.
41
+ SETTINGS_SUPERPET = [0xff, 0x03, 0x00, 0x20, 0x00, 0x20, 0x00, 0x10, 0xff, 0x3f].freeze
42
+ # The word after the settings while VIA CA2 is high, picking the
43
+ # character ROM's second set, as xpet 3.10 writes it and paints
44
+ # the picture from on every PET board.
45
+ SECOND_SET = 0x1000
39
46
  # The 2001's and 3000 series' video as xpet's CRTC runs it: 64
40
47
  # cycles a line, 32 rows of 8 lines and 4 more, the first 25 rows
41
48
  # shown.
@@ -82,9 +89,10 @@ module Badline
82
89
  beam = if discrete
83
90
  discrete_beam(video_input)
84
91
  else
85
- crtc_beam(run_on(machine, crtc_input, -CRTC_LEAD % frame_cycles(registers)), registers)
92
+ crtc_beam(run_on(fresh_crtc(machine), crtc_input, -CRTC_LEAD % frame_cycles(registers)),
93
+ registers)
86
94
  end
87
- fields = FieldWriter.new.bytes(model.cbm8296? ? SETTINGS_8296 : SETTINGS).word(attributes(model))
95
+ fields = FieldWriter.new.bytes(settings(model)).word(machine.via.ca2_output ? SECOND_SET : 0)
88
96
  fields.byte(0).byte(columns(model))
89
97
  fields.byte(model.basic1? ? 1 : 0).bytes(registers).byte(discrete ? 0 : crtc_input.int("selected", 0))
90
98
  beam_fields(fields, beam, registers, model)
@@ -92,6 +100,12 @@ module Badline
92
100
  fields.section(NAME, MAJOR, MINOR)
93
101
  end
94
102
 
103
+ def settings(model)
104
+ return SETTINGS_8296 if model.cbm8296?
105
+
106
+ model.superpet? ? SETTINGS_SUPERPET : SETTINGS
107
+ end
108
+
95
109
  def crtc_registers(input)
96
110
  registers = input.bytes("registers", "".b).bytes
97
111
  Array.new(REGISTERS) { |index| registers[index] || 0 }
@@ -119,10 +133,12 @@ module Badline
119
133
 
120
134
  def frame_cycles(registers) = (registers[0] + 1) * frame_lines(registers)
121
135
 
122
- # The BL-CRTC fields of a CRTC loaded with those in `input` and run
123
- # `cycles` cycles on its own, in a machine built as `machine` was.
124
- def run_on(machine, input, cycles)
125
- crtc = PETSetup.of(machine).build.crtc
136
+ # The CRTC of a machine built as `machine` was, at power-on.
137
+ def fresh_crtc(machine) = PETSetup.of(machine).build.crtc
138
+
139
+ # The BL-CRTC fields of `crtc`, a CRTC of its own, loaded with those
140
+ # in `input` and run `cycles` cycles.
141
+ def run_on(crtc, input, cycles)
126
142
  crtc.load_fields(input)
127
143
  cycles.times { crtc.cycle! }
128
144
  Keyed::Reader.parse(Keyed.section("BL-CRTC") { |out| crtc.save_fields(out) })
@@ -135,16 +151,13 @@ module Badline
135
151
  %w[adjusting row_display column_display].each { |key| out.bool(key, input.bool(key, false)) }
136
152
  end
137
153
 
138
- # Both move a row's start on at R1 of its last line, and badline's
139
- # moves it again where the vertical adjust runs past R9 lines,
140
- # which xpet's doesn't.
154
+ # Both move a row's start on at R1 of its last line.
141
155
  def crtc_beam(input, registers)
142
156
  column = input.int("column", 0)
143
157
  row = input.int("row", 0)
144
158
  line = input.int("line", 0)
145
159
  adjusting = input.bool("adjusting", false)
146
160
  start = input.int("row_start", 0)
147
- start = (start - registers[1]) & 0x3fff if adjusting && moved?(line, column, registers)
148
161
  rows = registers[4] + 1
149
162
  lines = registers[9] + 1
150
163
  Beam.new(column:, row: adjusting ? rows : row, raster: adjusting ? 0 : line,
@@ -212,12 +225,7 @@ module Badline
212
225
  adjusting = beam.row >= rows
213
226
  line = adjusting ? beam.frame_line - (rows * (registers[9] + 1)) : beam.raster
214
227
  start = start_address(beam.start, registers, columns, attributes)
215
- if adjusting
216
- start += registers[1] if line > registers[9]
217
- elsif moved?(line, column_of(section), registers)
218
- start -= registers[1]
219
- end
220
- start &= 0x3fff
228
+ start = (start - registers[1]) & 0x3fff if !adjusting && moved?(line, column_of(section), registers)
221
229
  out.bytes("registers", registers.first(Chips::CRTC::MASKS.length)).int("selected", selected(section) & 0x1f)
222
230
  out.int("column", 0).int("line", [line, 0].max).int("row", adjusting ? rows - 1 : beam.row)
223
231
  out.int("since_hsync", registers[0] + 1 - registers[2]).int("vsync_lines", beam.vsync)
@@ -0,0 +1,180 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ module Snapshot
5
+ module Vice
6
+ module PET
7
+ # A true 2031 on device 8, as xpet 3.10, xcbm2 and xcbm5x0 save it with true drive
8
+ # emulation: DRIVE8 to DRIVE11 2.0, one a unit, DRIVECPU0 1.3, the
9
+ # drive's 6502 and its RAM, and its VIAs as VIA 2.2 modules,
10
+ # 2031VIA1D0 at $1800 on the IEEE-488 bus and VIA2D0 at $1C00 on
11
+ # the mechanism.
12
+ #
13
+ # DRIVECPU0 holds the clock, the registers, the last opcode, the
14
+ # main CPU's clock, more clocks of the drive's interrupt bookkeeping,
15
+ # then the 2K of RAM from $5C and twelve bytes after it. DRIVE8
16
+ # holds the head's half track at $10, with track 18 at 36 as badline
17
+ # numbers them, the speed zone its VIA set at $24, $47 and $4B, the
18
+ # drive's type as a word at $28, and from $2C the disk's place in
19
+ # its turn and the clock it was last moved on at. The rest of it,
20
+ # the read electronics among it, badline doesn't read. xpet's head
21
+ # rests on a half track two phases on from where badline's stepper
22
+ # puts it (SLIP).
23
+ #
24
+ # Written: the registers and the RAM, the VIAs as the PET's VIA is
25
+ # written with the stepper's phase moved to xpet's, and DRIVE8 as
26
+ # xpet saves an idle 2031 but for the head's half track, the zone,
27
+ # and the disk at the start of its turn as of the snapshot's clock.
28
+ # The interrupt clocks are written as long past. The snapshot is
29
+ # settled with the drive's CPU between instructions too
30
+ # (PET#settle_for_vice!). One taken while the drive is mid-transfer
31
+ # on the bus may stall there in xpet, as xpet's own snapshots taken
32
+ # while it searches for a file do.
33
+ #
34
+ # Read: the registers, the RAM, the VIAs as the PET's VIA is read,
35
+ # and the head's half track, with the motor, the LED and the zone
36
+ # from VIA 2's port B and the stepper's phase as xpet's (SLIP). The
37
+ # disk turns on from where badline's left it, and the disk in the
38
+ # drive stays.
39
+ module Drive2031
40
+ UNITS = %w[DRIVE8 DRIVE9 DRIVE10 DRIVE11].freeze
41
+ DRIVE_MAJOR = 2
42
+ DRIVE_MINOR = 0
43
+ CPU = "DRIVECPU0"
44
+ CPU_MAJOR = 1
45
+ CPU_MINOR = 3
46
+ VIA1 = "2031VIA1D0"
47
+ VIA2 = "VIA2D0"
48
+ NAMES = [*UNITS, CPU, VIA1, VIA2].freeze
49
+ # Where DRIVECPU0's RAM starts, and its length.
50
+ RAM_AT = 0x5c
51
+ RAM_SIZE = 0x800
52
+ TAIL = 12
53
+ HALF_TRACK = 0x10
54
+ ZONES = [0x24, 0x47, 0x4b].freeze
55
+ SIZE = 145
56
+ # The disk's place in its turn, and the clock it was last moved on at.
57
+ TURN = 0x2c
58
+ # DRIVE8 as xpet saves an idle 2031, and a unit with no drive, by
59
+ # the bytes that aren't 0.
60
+ IDLE = { 0x00 => 0x01, 0x01 => 0x01, 0x02 => 0x01, 0x0f => 0x01, 0x10 => 0x25, 0x1a => 0x01, 0x1b => 0xdf,
61
+ 0x1f => 0x54, 0x20 => 0x55, 0x24 => 0x03, 0x28 => 0xef, 0x29 => 0x07, 0x2c => 0x60, 0x2d => 0x41,
62
+ 0x2e => 0x0f, 0x30 => 0x17, 0x31 => 0x51, 0x32 => 0x0f, 0x40 => 0x54, 0x41 => 0x01, 0x42 => 0x54,
63
+ 0x47 => 0x03, 0x4b => 0x03, 0x6f => 0xcd, 0x70 => 0xab, 0x71 => 0x34, 0x72 => 0x12 }.freeze
64
+ NONE = { 0x00 => 0x01, 0x01 => 0x01, 0x02 => 0x01, 0x0e => 0x01, 0x0f => 0x01, 0x10 => 0x24, 0x1a => 0x01,
65
+ 0x20 => 0x55, 0x6f => 0xcd, 0x70 => 0xab, 0x71 => 0x34, 0x72 => 0x12, 0x8f => 0x01 }.freeze
66
+ SLIP = 2
67
+ MOTOR = 0x04
68
+ LED = 0x08
69
+
70
+ module_function
71
+
72
+ # Whether the snapshot has a true 2031.
73
+ def present?(container) = !container[CPU].nil? && !container[VIA1].nil?
74
+
75
+ def reads?(section)
76
+ case section.name
77
+ when CPU then section.major == CPU_MAJOR && section.minor == CPU_MINOR
78
+ when VIA1, VIA2 then VIA.reads?(section)
79
+ else section.major == DRIVE_MAJOR && section.minor == DRIVE_MINOR
80
+ end
81
+ end
82
+
83
+ # xpet's modules for the 2031 whose BL-D8 modules `readers` holds,
84
+ # and none without one or for an 8050 or 8250, which they leave
85
+ # out.
86
+ def export(machine, readers)
87
+ drive = readers["BL-D8"]
88
+ return [] if drive.nil? || drive.text("model", "2031") != "2031"
89
+
90
+ via2 = readers.fetch("BL-D8-VIA2")
91
+ mechanism = readers.fetch("BL-D8-MECH")
92
+ zone = ((via2.int("orb", 0) | ~via2.int("ddrb", 0)) >> 5) & 0x03
93
+ idle = filled(IDLE)
94
+ idle[HALF_TRACK] = mechanism.int("half_track", 36)
95
+ ZONES.each { |offset| idle[offset] = zone }
96
+ idle[TURN, 8] = [0, machine.cycles & 0xffff_ffff].pack("VV").bytes
97
+ [FieldWriter.new.bytes(idle).section(UNITS.first, DRIVE_MAJOR, DRIVE_MINOR),
98
+ *UNITS.drop(1).map { |name| unit(NONE, name) },
99
+ cpu(machine, readers.fetch("BL-D8-CPU"), readers.fetch("BL-D8-RAM")),
100
+ stepper(VIA.export(via2, VIA2), mechanism.int("slip", 0)),
101
+ VIA.export(readers.fetch("BL-D8-VIA1"), VIA1)]
102
+ end
103
+
104
+ # VIA 2's port B with the stepper's phase as xpet's head takes it
105
+ # where badline's head is: SLIP on from badline's.
106
+ def stepper(section, slip)
107
+ bytes = section.data.bytes
108
+ bytes[2] = (bytes[2] & 0xfc) | ((bytes[2] - slip + SLIP) & 0x03)
109
+ section.with(data: bytes.pack("C*"))
110
+ end
111
+
112
+ def unit(bytes, name) = FieldWriter.new.bytes(filled(bytes)).section(name, DRIVE_MAJOR, DRIVE_MINOR)
113
+
114
+ def filled(bytes) = Array.new(SIZE) { |offset| bytes.fetch(offset, 0) }
115
+
116
+ def cpu(machine, input, ram)
117
+ bytes = ram.bytes("ram", "".b).bytes
118
+ cpu_section(machine, input, Array.new(RAM_SIZE) { |address| bytes[address] || 0 })
119
+ end
120
+
121
+ # The drive's clock, at its 1 MHz, for the machine's at its own.
122
+ def drive_clock(machine) = machine.cycles * Drives::Model2031::CLOCK_HZ / machine.timing.clock_hz
123
+
124
+ # DRIVECPU0 for a drive CPU whose BL- fields `input` holds, with
125
+ # `ram` the drive's RAM as xpet lays it out: the drive's clock,
126
+ # the registers, the machine's clock, the drive's clock again, and
127
+ # the RAM.
128
+ def cpu_section(machine, input, ram)
129
+ clock = drive_clock(machine)
130
+ fields = FieldWriter.new.qword(clock)
131
+ %w[a x y sp].each { |key| fields.byte(input.int(key, 0)) }
132
+ fields.word(input.int("pc", 0)).byte(input.int("p", 0x20)).dword(0)
133
+ fields.qword(machine.cycles).zeros(16).qword(clock).zeros(1).qword(0).zeros(8).qword(0)
134
+ fields.zeros(16).bytes(ram).zeros(TAIL)
135
+ fields.section(CPU, CPU_MAJOR, CPU_MINOR)
136
+ end
137
+
138
+ # The 2031's keyed modules, named for device 8, from the
139
+ # snapshot's, with `disk` the drive's disk module badline keeps,
140
+ # or nil.
141
+ def import(container, disk)
142
+ via2 = container[VIA2]
143
+ sections = [Keyed.section("BL-D8") { |out| drive(container, out) },
144
+ Keyed.section("BL-D8-RAM") { |out| out.bytes("ram", ram(container[CPU])) },
145
+ Keyed.section("BL-D8-VIA1") { |out| VIA.import(container[VIA1], out) },
146
+ Keyed.section("BL-D8-VIA2") { |out| VIA.import(via2, out) },
147
+ Keyed.section("BL-D8-CPU") { |out| registers(container[CPU], out) },
148
+ Keyed.section("BL-D8-MECH") { |out| mechanism(container, via2, out) }]
149
+ disk ? sections + [disk] : sections
150
+ end
151
+
152
+ def drive(container, out)
153
+ out.text("model", "2031").int("phase", 0).int("cycles", FieldReader.new(container[CPU]).qword)
154
+ out.bool("so_pending", false)
155
+ end
156
+
157
+ def ram(section) = FieldReader.new(section).skip(RAM_AT).string(RAM_SIZE)
158
+
159
+ def registers(section, out)
160
+ fields = FieldReader.new(section)
161
+ clock = fields.qword
162
+ out.int("a", fields.byte).int("x", fields.byte).int("y", fields.byte).int("sp", fields.byte)
163
+ out.int("pc", fields.word).int("p", fields.byte).int("cycles", clock)
164
+ end
165
+
166
+ # The head where DRIVE8 has it, and the stepper's phase as VIA 2
167
+ # drives it, which the half track's low two bits stand for.
168
+ def mechanism(container, via2, out)
169
+ half_track = FieldReader.new(container[UNITS.first]).skip(HALF_TRACK).byte
170
+ bytes = FieldReader.new(via2).bytes(4)
171
+ port_b = bytes[2] | (~bytes[3] & 0xff)
172
+ out.int("half_track", half_track).int("slip", SLIP)
173
+ out.int("zone", (port_b >> 5) & 0x03)
174
+ out.bool("motor", port_b.anybits?(MOTOR)).bool("led", port_b.anybits?(LED))
175
+ end
176
+ end
177
+ end
178
+ end
179
+ end
180
+ end