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
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ class Plus4
5
+ # The keyboard and the joysticks: the 6529 at $FD30-$FD3F, whose eight
6
+ # outputs drive the key matrix's lines, and the TED's K0-K7 inputs,
7
+ # which $FF08 latches (Chips::TED#keyboard_port=). The KERNAL's scan
8
+ # ($DB11) writes each line's mask to $FD30 and $FF08 and reads the keys
9
+ # back from $FF08, and its key table at $E026 puts each key at line
10
+ # times 8 plus input bit.
11
+ #
12
+ # The joysticks share K0-K7. The byte written to $FF08 selects them,
13
+ # bit 2 low joystick 1 and bit 1 low joystick 2, as BASIC's JOY does
14
+ # ($FA and $FD). A selected joystick pulls up, down, left and right
15
+ # onto K0-K3 and its fire button onto K6 (joystick 1) or K7
16
+ # (joystick 2), and those lines settle against the matrix with the
17
+ # keys'.
18
+ #
19
+ # The 6529 is a quasi-bidirectional latch: a read returns what was
20
+ # written to it.
21
+ class KeyboardPort
22
+ # One row of keys per 6529 output, in K0-K7 order, after the
23
+ # KERNAL's key table. HELP is the KERNAL's F7, and the two SHIFT keys
24
+ # share one place.
25
+ MATRIX = [
26
+ %i[delete return £ help f1 f2 f3 @],
27
+ %i[3 w a 4 z s e lshift],
28
+ %i[5 r d 6 c f t x],
29
+ %i[7 y g 8 b h u v],
30
+ %i[9 i j 0 m k o n],
31
+ %i[crsr_down p l crsr_up . : - ,],
32
+ %i[crsr_left * ; crsr_right esc = + /],
33
+ %i[1 clr_home control 2 space cbm q run_stop]
34
+ ].freeze
35
+
36
+ attr_reader :keyboard, :output
37
+
38
+ def initialize(keyboard, joystick1, joystick2)
39
+ @keyboard = keyboard
40
+ @joystick1 = joystick1
41
+ @joystick2 = joystick2
42
+ @output = 0xff
43
+ end
44
+
45
+ def reset!
46
+ @output = 0xff
47
+ end
48
+
49
+ def peek(_addr) = @output
50
+
51
+ def poke(_addr, value)
52
+ @output = value
53
+ end
54
+
55
+ # The K0-K7 inputs the TED latches on a write of +value+ to $FF08:
56
+ # high but where a held key joins a line the 6529 pulls low, or a
57
+ # joystick +value+ selects holds a switch closed.
58
+ def latch(value) = @keyboard.scan(@output, joystick_lines(value)).last
59
+
60
+ private
61
+
62
+ def joystick_lines(value)
63
+ lines = 0xff
64
+ lines &= joystick_inputs(@joystick1.port_bits, 0x40) if value.nobits?(0x04)
65
+ lines &= joystick_inputs(@joystick2.port_bits, 0x80) if value.nobits?(0x02)
66
+ lines
67
+ end
68
+
69
+ # A joystick's switches, Input::Joystick's bits 0-4, on K0-K7: the
70
+ # directions on K0-K3 and the fire button on +fire+.
71
+ def joystick_inputs(bits, fire)
72
+ bits.anybits?(0x10) ? bits | 0xf0 : (bits | 0xf0) & ~fire & 0xff
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,99 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ class Plus4
5
+ # The whole machine as keyed snapshot modules (Snapshot::Keyed): BL-MACHINE
6
+ # with the setup, the cartridges' ROMs by slot, the CPU with its port,
7
+ # the TED, its sound, the keyboard's 6529, the user port's 6529 and the
8
+ # ACIA where the model has them, the TCBM interface's TIAs where one is
9
+ # plugged in, the RAM with the ROM bank latch and the ROM/RAM switch
10
+ # under bus.*, the datasette, device 8 through the traps and the true
11
+ # drives.
12
+ #
13
+ # A load maps the banks before the chips load, and works out what each
14
+ # unit derives from its fields once every unit is loaded.
15
+ module KeyedModules
16
+ def save_modules
17
+ bus = @bus
18
+ sections = [Snapshot::Keyed.section("BL-MACHINE") { |out| save_machine_fields(out) }]
19
+ sections << Snapshot::Keyed.section("BL-CART") { |out| bus.save_slot_fields(out) } if bus.cartridge?
20
+ save_chip_modules(sections)
21
+ sections << Snapshot::Keyed.section("BL-RAM") do |out|
22
+ bus.ram.save_fields(out, "ram")
23
+ bus.save_fields(out, "bus.")
24
+ end
25
+ sections << Snapshot::Keyed.section("BL-TAPE") { |out| @datasette.save_fields(out) }
26
+ sections << Snapshot::Keyed.section("BL-TRAPDOS") { |out| save_trap_dos_fields(out) } if mounted?
27
+ save_drive_modules(sections)
28
+ end
29
+
30
+ # Raises Snapshot::FormatError for the modules of a machine built
31
+ # another way.
32
+ def load_modules(modules)
33
+ machine = modules.machine
34
+ check_snapshot_setup(Snapshot::Plus4Setup.read_fields(machine))
35
+ load_machine_fields(machine)
36
+ bus = @bus
37
+ memory = modules.required("BL-RAM")
38
+ bus.ram.load_fields(memory, "ram")
39
+ bus.load_fields(memory, "bus.")
40
+ bus.load_slot_fields(modules.optional("BL-CART") || Snapshot::Keyed::Reader.new("BL-CART"))
41
+ bus.restored!
42
+ load_chip_modules(modules)
43
+ @datasette.load_fields(modules.required("BL-TAPE"))
44
+ trap_dos = modules.optional("BL-TRAPDOS")
45
+ trap_dos ? load_trap_dos_fields(trap_dos, detached: modules.detached?) : unmount
46
+ load_drive_modules(modules)
47
+ modules_restored!
48
+ self
49
+ end
50
+
51
+ private
52
+
53
+ def save_chip_modules(sections)
54
+ sections << Snapshot::Keyed.section("BL-CPU") do |out|
55
+ @cpu.save_fields(out)
56
+ @port.save_fields(out, "port.")
57
+ end
58
+ sections << Snapshot::Keyed.section("BL-TED") { |out| @ted.save_fields(out) }
59
+ sections << Snapshot::Keyed.section("BL-TEDSOUND") { |out| @sound.save_fields(out) }
60
+ sections << Snapshot::Keyed.section("BL-KEYPORT") { |out| @keyboard_port.save_fields(out) }
61
+ user_port = @user_port
62
+ sections << Snapshot::Keyed.section("BL-USERPORT") { |out| user_port.save_fields(out) } if user_port
63
+ acia = @acia
64
+ sections << Snapshot::Keyed.section("BL-ACIA") { |out| acia.save_fields(out) } if acia
65
+ @tias.each_with_index do |tia, index|
66
+ sections << Snapshot::Keyed.section("BL-TIA#{index + 1}") { |out| tia.save_fields(out) }
67
+ end
68
+ end
69
+
70
+ def load_chip_modules(modules)
71
+ cpu = modules.required("BL-CPU")
72
+ @port.load_fields(cpu, "port.")
73
+ @ted.load_fields(modules.required("BL-TED"))
74
+ @sound.load_fields(modules.required("BL-TEDSOUND"))
75
+ @keyboard_port.load_fields(modules.required("BL-KEYPORT"))
76
+ @user_port&.load_fields(modules.required("BL-USERPORT"))
77
+ @acia&.load_fields(modules.required("BL-ACIA"))
78
+ @tias.each_with_index { |tia, index| tia.load_fields(modules.required("BL-TIA#{index + 1}")) }
79
+ @cpu.load_fields(cpu)
80
+ end
81
+
82
+ def modules_restored!
83
+ @cpu.restored!
84
+ @ted.restored!
85
+ @sound.restored!(@ted)
86
+ @port.restored!
87
+ end
88
+
89
+ def check_snapshot_setup(setup)
90
+ ours = Snapshot::Plus4Setup.of(self)
91
+ return if setup == ours
92
+
93
+ raise Snapshot::FormatError, "the snapshot is of a #{setup}, not a #{ours}"
94
+ end
95
+ end
96
+
97
+ include KeyedModules
98
+ end
99
+ end
@@ -0,0 +1,143 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ class Plus4
5
+ class CPUPort
6
+ # The port's registers in a keyed snapshot module (Snapshot::Keyed),
7
+ # under `prefix`. The serial bus's lines and the datasette's write
8
+ # line are driven again from them once everything is loaded
9
+ # (#restored!), without recording an edge on the tape.
10
+ module KeyedState
11
+ def save_fields(out, prefix)
12
+ out.int("#{prefix}ddr", @ddr).int("#{prefix}data", @data)
13
+ end
14
+
15
+ def load_fields(input, prefix)
16
+ @ddr = input.byte("#{prefix}ddr", @ddr)
17
+ @data = input.byte("#{prefix}data", @data)
18
+ end
19
+
20
+ def restored!
21
+ @serial_bus.host_lines = serial_lines
22
+ @datasette.write_level = levels.anybits?(CLK_OUT)
23
+ end
24
+ end
25
+
26
+ include KeyedState
27
+ end
28
+
29
+ class KeyboardPort
30
+ # The 6529's latch in a keyed snapshot module (BL-KEYPORT).
31
+ module KeyedState
32
+ def save_fields(out) = out.int("output", @output)
33
+
34
+ def load_fields(input)
35
+ @output = input.byte("output", @output)
36
+ end
37
+ end
38
+
39
+ include KeyedState
40
+ end
41
+
42
+ class UserPort
43
+ # The 6529's latch in a keyed snapshot module (BL-USERPORT).
44
+ module KeyedState
45
+ def save_fields(out) = out.int("output", @output)
46
+
47
+ def load_fields(input)
48
+ @output = input.byte("output", @output)
49
+ end
50
+ end
51
+
52
+ include KeyedState
53
+ end
54
+
55
+ class ACIA
56
+ # The command and control registers in a keyed snapshot module
57
+ # (BL-ACIA).
58
+ module KeyedState
59
+ def save_fields(out) = out.int("command", @command).int("control", @control)
60
+
61
+ def load_fields(input)
62
+ @command = input.byte("command", @command)
63
+ @control = input.byte("control", @control)
64
+ end
65
+ end
66
+
67
+ include KeyedState
68
+ end
69
+
70
+ class Bus
71
+ # The ROM bank latch, whether ROM is selected and the last byte on the
72
+ # bus, under `prefix`, and the cartridges' ROMs by slot (#save_slot_fields),
73
+ # in keyed snapshot modules (Snapshot::Keyed). The TED learns which
74
+ # memory is selected, and the pages are mapped again, once they are
75
+ # loaded (#restored!). The BASIC, KERNAL and function ROMs come with
76
+ # the model.
77
+ module KeyedState
78
+ # The cartridge slots, as xplus4 names them: cartridge 1 or 2, the
79
+ # low ($8000) or the high ($C000) half.
80
+ SLOTS = %w[c1lo c2lo c1hi c2hi].freeze
81
+
82
+ def save_fields(out, prefix)
83
+ out.int("#{prefix}low_bank", @low_bank).int("#{prefix}high_bank", @high_bank)
84
+ out.bool("#{prefix}rom_selected", @rom_selected).int("#{prefix}data", @data)
85
+ end
86
+
87
+ def load_fields(input, prefix)
88
+ @low_bank = input.masked("#{prefix}low_bank", @low_bank, 0x03)
89
+ @high_bank = input.masked("#{prefix}high_bank", @high_bank, 0x03)
90
+ @rom_selected = input.bool("#{prefix}rom_selected", @rom_selected)
91
+ @data = input.byte("#{prefix}data", @data)
92
+ end
93
+
94
+ # Whether a cartridge's ROM is in any slot.
95
+ def cartridge?
96
+ index = 0
97
+ while index < SLOTS.length
98
+ return true unless slot_rom(index).nil?
99
+
100
+ index += 1
101
+ end
102
+ false
103
+ end
104
+
105
+ # Each slot's ROM, 16K, under its name. An empty slot writes
106
+ # nothing.
107
+ def save_slot_fields(out)
108
+ SLOTS.each_with_index do |name, index|
109
+ rom = slot_rom(index)
110
+ rom&.save_fields(out, name)
111
+ end
112
+ out
113
+ end
114
+
115
+ # A slot the module holds nothing for is left empty.
116
+ def load_slot_fields(input)
117
+ SLOTS.each_with_index do |name, index|
118
+ bytes = input.bytes(name, "".b).bytes
119
+ if index < 2
120
+ @low_roms[2 + index] = slot(bytes, 0x8000)
121
+ else
122
+ @high_roms[index] = slot(bytes, 0xc000)
123
+ end
124
+ end
125
+ end
126
+
127
+ def restored!
128
+ @ted.rom_selected = @rom_selected
129
+ map_reads
130
+ end
131
+
132
+ private
133
+
134
+ def slot_rom(index) = index < 2 ? @low_roms[2 + index] : @high_roms[index]
135
+
136
+ # A slot's ROM from its bytes at `start`, or none.
137
+ def slot(bytes, start) = bytes.empty? ? nil : ROM.new(bytes, length: bytes.length, start:)
138
+ end
139
+
140
+ include KeyedState
141
+ end
142
+ end
143
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ class Plus4
5
+ # The machine's own fields in a keyed snapshot module (Snapshot::Keyed,
6
+ # BL-MACHINE): the family, its setup by name (Snapshot::Plus4Setup), the
7
+ # cycle count, and the host's typing still to feed and on_init handlers
8
+ # still to run under host.*. The badline version that wrote it is
9
+ # written for reports and never read.
10
+ module KeyedState
11
+ # How many more on_init handlers the machine a snapshot was taken of had
12
+ # yet to run than this one has. Nil until the machine is restored.
13
+ attr_reader :init_handlers_lost
14
+
15
+ # Whether a snapshot saves now (Machine::Settling): the CPU between
16
+ # instructions or jammed.
17
+ def settled_for_snapshot? = @cpu.boundary? || @cpu.jammed?
18
+
19
+ # The sound's voices, run up to this cycle.
20
+ def settle_chips_for_snapshot = @sound.catch_up!
21
+
22
+ def save_machine_fields(out)
23
+ out.text("family", "plus4").text("written_by", VERSION)
24
+ Snapshot::Plus4Setup.of(self).write_fields(out)
25
+ out.int("cycles", @cycles)
26
+ pending = @pending_keys
27
+ out.ints("host.pending_keys", pending) if pending
28
+ out.int("host.init_pending", booting? ? @init_handlers.length : 0)
29
+ end
30
+
31
+ def load_machine_fields(input)
32
+ @cycles = input.int("cycles", @cycles)
33
+ pending = input.ints("host.pending_keys", [])
34
+ @pending_keys = pending.empty? ? nil : pending.dup
35
+ @init_handlers_lost = [input.int("host.init_pending", 0) - @init_handlers.length, 0].max
36
+ end
37
+ end
38
+
39
+ include KeyedState
40
+ end
41
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ class Plus4
5
+ # The 264 series machines badline builds by name, after xplus4's
6
+ # -model names, with the C116 as another name for the C16: one board
7
+ # design with its RAM, its video standard and KERNAL, and whether the
8
+ # Plus/4's own parts are fitted (the 3-plus-1 function ROMs, the 6551
9
+ # ACIA and the user port's 6529).
10
+ module Model
11
+ Profile = Data.define(:name, :ram_kb, :ted, :kernal, :plus4_parts)
12
+
13
+ PLUS4 = Profile.new(name: "plus4", ram_kb: 64, ted: Chips::TED::PAL, kernal: "plus4/kernal-pal.rom",
14
+ plus4_parts: true)
15
+ PLUS4_NTSC = Profile.new(name: "plus4ntsc", ram_kb: 64, ted: Chips::TED::NTSC,
16
+ kernal: "plus4/kernal-ntsc.rom", plus4_parts: true)
17
+ C16 = Profile.new(name: "c16", ram_kb: 16, ted: Chips::TED::PAL, kernal: "plus4/kernal-pal.rom",
18
+ plus4_parts: false)
19
+ C16_NTSC = Profile.new(name: "c16ntsc", ram_kb: 16, ted: Chips::TED::NTSC, kernal: "plus4/kernal-ntsc.rom",
20
+ plus4_parts: false)
21
+
22
+ ALL = [PLUS4, PLUS4_NTSC, C16, C16_NTSC].freeze
23
+
24
+ # Other names a model goes by.
25
+ ALIASES = { "c116" => "c16" }.freeze
26
+
27
+ # The RAM sizes a machine can have, in K.
28
+ RAM_SIZES = [16, 32, 64].freeze
29
+
30
+ def self.named(name)
31
+ model = ALL.find { |candidate| candidate.name == ALIASES.fetch(name, name) }
32
+ raise ArgumentError, "no Plus/4 model named #{name}" if model.nil?
33
+
34
+ model
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ class Plus4
5
+ # The Plus/4's user port 6529 at $FD10-$FD1F, a quasi-bidirectional
6
+ # latch: a line reads low where the latch or the outside pulls it. With
7
+ # nothing plugged into the user port, the outside pulls only bit 2, the
8
+ # cassette sense switch, low while a key is down on the deck. The
9
+ # KERNAL's IRQ reads it there ($CFBF).
10
+ class UserPort
11
+ TAPE_SENSE = 0x04
12
+
13
+ attr_reader :output
14
+
15
+ def initialize(datasette)
16
+ @datasette = datasette
17
+ @output = 0xff
18
+ end
19
+
20
+ def reset!
21
+ @output = 0xff
22
+ end
23
+
24
+ def peek(_addr) = @datasette.sense_low? ? @output & ~TAPE_SENSE : @output
25
+
26
+ def poke(_addr, value)
27
+ @output = value
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,240 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "badline/chips/ted"
4
+ require "badline/chips/tia"
5
+ require "badline/plus4/model"
6
+ require "badline/plus4/cpu"
7
+ require "badline/plus4/cpu_port"
8
+ require "badline/plus4/keyboard_port"
9
+ require "badline/plus4/user_port"
10
+ require "badline/plus4/cassette_sense"
11
+ require "badline/plus4/acia"
12
+ require "badline/plus4/bus"
13
+ require "badline/snapshot/plus4_setup"
14
+ require "badline/plus4/keyed_parts"
15
+ require "badline/plus4/keyed_state"
16
+ require "badline/plus4/keyed_modules"
17
+
18
+ module Badline
19
+ # The Commodore 264 series: the Plus/4, the C16 and the C116. A 7501 on
20
+ # the TED's clock, which runs it once or twice a TED cycle (Chips::TED's
21
+ # Beam), the TED and, on the Plus/4, a 6551 ACIA and the user port's
22
+ # 6529. Only the TED pulls IRQ, and nothing pulls NMI, which the 7501
23
+ # doesn't have.
24
+ #
25
+ # The TED paints the picture a front end shows (#video, #timing). Device 8
26
+ # serves disks through the KERNAL traps (C64::KernalTraps), or a true
27
+ # 1541 or 1581 on the serial bus does (C64::TrueDrives), cartridges go
28
+ # in the two cartridges' slots, and tapes play on the datasette, whose read
29
+ # line's level the KERNAL times with the TED's timers.
30
+ class Plus4
31
+ include IntegerHelper
32
+ include Machine::KeyboardBuffer
33
+ include Machine::Clocking
34
+ include Machine::Settling
35
+ include C64::TrueDrives
36
+ include C64::KernalTraps
37
+
38
+ # The cycle by which the KERNAL has booted and BASIC printed READY,
39
+ # with a margin, in TED cycles. READY is up by about 116,000 on a PAL
40
+ # Plus/4, 126,000 on an NTSC one and sooner on a C16.
41
+ INIT_THRESHOLD = 200_000
42
+
43
+ # The cycle by which a 1541 switched on with the machine has run its
44
+ # DOS's reset, the RAM test and the ROM checksum, and waits on the
45
+ # bus, with a margin, in TED cycles.
46
+ DRIVE_BOOT_CYCLES = 1_200_000
47
+
48
+ attr_reader :model, :bus, :cpu, :ted, :sound, :port, :keyboard, :keyboard_port, :joystick1, :joystick2, :acia,
49
+ :user_port, :datasette, :tias, :iec_bus, :cycles, :ram_kb, :drive1541, :init_threshold
50
+
51
+ def family = :plus4
52
+
53
+ # The joysticks reach the TED's keyboard latch, so there are no C64
54
+ # control ports to plug a pot device into.
55
+ def control_ports = nil
56
+
57
+ def ram = @bus.ram
58
+
59
+ # What a front end plays: the TED's sound, which answers #record and
60
+ # #drain_samples.
61
+ def sound_source = @sound
62
+
63
+ # The chip whose #display a front end shows.
64
+ def video = @ted
65
+
66
+ # The TED's clock and raster, and the part of its picture xplus4 shows:
67
+ # all 384 pixels of a line, and 288 lines from line 276 on PAL or 242
68
+ # from line 248 on NTSC, running on past the frame's last line to its
69
+ # first, as the top border is the end of the frame. The TED draws 8
70
+ # pixels a cycle, so each shows one window pixel wide, as on the C64.
71
+ def timing
72
+ profile = @ted.profile
73
+ ntsc = profile == Chips::TED::NTSC
74
+ Timing.new(clock_hz: profile.clock_hz, cycles_per_line: Chips::TED::Beam::CYCLES_PER_LINE,
75
+ lines_per_frame: ntsc ? 262 : 312, crop: ntsc ? [0, 248, 384, 242] : [0, 276, 384, 288],
76
+ pixel_width: 1)
77
+ end
78
+
79
+ # Reads and writes memory as the CPU sees it, through the bus.
80
+ def peek(addr) = @bus.peek(addr)
81
+
82
+ def poke(addr, value) = @bus.poke(addr, value)
83
+
84
+ # +model+ names one of Model::ALL, or c116 for the C16, and +ram+ the
85
+ # RAM in K, one of Model::RAM_SIZES, when not the model's own. +tcbm+
86
+ # plugs in a 1551's TCBM interface, the TIAs at $FEC0-$FEFF, with no
87
+ # drive answering on the other side.
88
+ def initialize(model: "plus4", ram: nil, tcbm: false, debug: false)
89
+ @model = Model.named(model)
90
+ @ram_kb = ram || @model.ram_kb
91
+ raise ArgumentError, "no Plus/4 RAM size of #{@ram_kb}K" unless Model::RAM_SIZES.include?(@ram_kb)
92
+
93
+ @ted = Chips::TED.new(@model.ted)
94
+ @keyboard = Input::Keyboard.new(matrix: KeyboardPort::MATRIX)
95
+ @joystick1 = Input::Joystick.new
96
+ @joystick2 = Input::Joystick.new
97
+ @keyboard_port = KeyboardPort.new(@keyboard, @joystick1, @joystick2)
98
+ @ted.keyboard_port = @keyboard_port
99
+ @iec_bus = Drives::SerialBus.new
100
+ @datasette = Drives::Datasette.new(level: true)
101
+ @port = CPUPort.new(serial_bus: @iec_bus, datasette: @datasette)
102
+ plus4_parts = @model.plus4_parts
103
+ @acia = plus4_parts ? ACIA.new : nil
104
+ @user_port = plus4_parts ? UserPort.new(@datasette) : nil
105
+ io = Bus::IOPage.new(keyboard_port: @keyboard_port, acia: @acia, user_port: @user_port,
106
+ cassette_sense: plus4_parts ? nil : CassetteSense.new(@datasette))
107
+ @bus = Bus.new(ted: @ted, port: @port, io:, model: @model, ram_kb: @ram_kb)
108
+ @ted.connect(@bus)
109
+ @tias = tcbm ? [Chips::TIA.new, Chips::TIA.new] : []
110
+ @bus.plug_tcbm(drive8: @tias[0], drive9: @tias[1]) if tcbm
111
+ @cpu = CPU.new(@bus, debug:)
112
+ @cycles = 0
113
+ @sound = Chips::TED::Sound.new(self, @model.ted.clock_hz)
114
+ @ted.sound = @sound
115
+ @init_handlers = []
116
+ @pending_keys = nil
117
+ @dos = nil
118
+ @serial_trap = nil
119
+ @save_trap = nil
120
+ @capture_output = nil
121
+ @drive1541 = nil
122
+ @drive1581 = nil
123
+ @init_threshold = INIT_THRESHOLD
124
+ end
125
+
126
+ # One TED cycle: the TED moves on and decides the CPU's clock, then the
127
+ # CPU runs its cycles in it, none while the TED holds the bus.
128
+ def cycle!
129
+ handle_init if @cycles == @init_threshold
130
+ feed_keyboard if @pending_keys
131
+
132
+ ted = @ted
133
+ ted.cycle!
134
+ @datasette.cycle!
135
+ @cpu.irq = ted.irq?
136
+ slots = ted.cpu_slots
137
+ if slots.zero?
138
+ @cpu.stall!
139
+ else
140
+ clock_cpu(ted)
141
+ clock_cpu(ted) if slots == 2
142
+ end
143
+ @drive1541&.host_cycle!
144
+ @drive1581&.host_cycle!
145
+
146
+ @cycles += 1
147
+ end
148
+
149
+ # The RESET line: the CPU, the port, the 6529s, the ACIA, the bank
150
+ # latch and ROM selection, and through the serial bus's RESET line, the
151
+ # drives. The TED has no reset pin.
152
+ def reset!
153
+ @port.reset!
154
+ @keyboard_port.reset!
155
+ @user_port&.reset!
156
+ @dos&.reset!
157
+ @drive1541&.reset!
158
+ @drive1581&.reset!
159
+ @acia&.reset!
160
+ @tias.each(&:reset!)
161
+ @bus.reset!
162
+ @cpu.reset!
163
+ end
164
+
165
+ # Switches the machine off and on: RAM, the TED and its sound start
166
+ # from their power-on state, and the RES line resets everything else.
167
+ def power_cycle!
168
+ @bus.power_on!
169
+ @ted.power_on!
170
+ @sound.power_on!
171
+ reset!
172
+ end
173
+
174
+ # Calls the block with the exit code a VICE testprog writes to $FDCF.
175
+ def install_debug_register(&) = @bus.install_debug_register(&)
176
+
177
+ # Puts a cartridge's ROM chips in their slots (Bus#attach_cartridge)
178
+ # and switches the machine off and on, as a cartridge goes in with the
179
+ # power off. The KERNAL starts it at reset when it signs itself with CBM
180
+ # at $8007.
181
+ def attach_cartridge(chips)
182
+ @bus.attach_cartridge(chips)
183
+ power_cycle!
184
+ end
185
+
186
+ # Takes both cartridges out (Bus#detach_cartridges) and switches the
187
+ # machine off and on.
188
+ def detach_cartridges
189
+ @bus.detach_cartridges
190
+ power_cycle!
191
+ end
192
+
193
+ # The cartridge slots with a ROM in them (Bus#cartridge_slots).
194
+ def cartridge_slots = @bus.cartridge_slots
195
+
196
+ # The model and its RAM as the pause menu names them, such as
197
+ # "PLUS/4 PAL, 64K RAM".
198
+ def model_label
199
+ name = @model.plus4_parts ? "PLUS/4" : "C16"
200
+ standard = @model.ted == Chips::TED::NTSC ? "NTSC" : "PAL"
201
+ "#{name} #{standard}, #{@ram_kb}K RAM"
202
+ end
203
+
204
+ # Stores a PRG's bytes from its load address on, into RAM, and returns
205
+ # that address.
206
+ def load_prg(data)
207
+ load_addr = uint16(data[0], data[1])
208
+ bytes = data[2..] || []
209
+ bytes = bytes[0, 0x10000 - load_addr] if load_addr + bytes.length > 0x10000
210
+ bytes.each_with_index { |byte, i| @bus.ram_view.poke(load_addr + i, byte) }
211
+ load_addr
212
+ end
213
+
214
+ private
215
+
216
+ # The 264 KERNAL's keyboard buffer.
217
+ def keyboard_buffer = Machine::KeyboardBuffer::PLUS4_LAYOUT
218
+
219
+ # The 264 KERNAL, which the traps stand in for.
220
+ def trap_layout = KernalTrap::PLUS4_LAYOUT
221
+
222
+ def trap_bus = @bus
223
+
224
+ # A drive that powers on with the machine takes longer to boot than
225
+ # the KERNAL, and doesn't answer the bus until it has, so the
226
+ # machine's on_init handlers wait for it too.
227
+ def drive1541_attached
228
+ @init_threshold = [@init_threshold, DRIVE_BOOT_CYCLES].max if @cycles < @init_threshold
229
+ end
230
+
231
+ def drive1581_attached
232
+ @init_threshold = [@init_threshold, Drives::Model1581::BOOT_CYCLES].max if @cycles < @init_threshold
233
+ end
234
+
235
+ # BA stops the CPU at a read, and a write goes ahead.
236
+ def clock_cpu(ted)
237
+ ted.ba_low? && !@cpu.pending_write? ? @cpu.stall! : @cpu.cycle!
238
+ end
239
+ end
240
+ end