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,258 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ module Chips
5
+ class M6809
6
+ # The instruction methods the plans' *_exec steps call. A read takes
7
+ # its operand from @value; a write and a read-modify-write leave the
8
+ # byte or word to write there.
9
+ module Operations
10
+ private
11
+
12
+ def none; end
13
+ def nop; end
14
+ def sync; end
15
+
16
+ def neg = @value = neg8(@value)
17
+ def xnc = @value = xnc8(@value)
18
+ def com = @value = com8(@value)
19
+ def lsr = @value = lsr8(@value)
20
+ def ror = @value = ror8(@value)
21
+ def asr = @value = asr8(@value)
22
+ def asl = @value = asl8(@value)
23
+ def rol = @value = rol8(@value)
24
+ def dec = @value = dec8(@value)
25
+ def xdec = @value = xdec8(@value)
26
+ def inc = @value = inc8(@value)
27
+ def tst = logic8(@value)
28
+ def clr = @value = clr8
29
+
30
+ def jmp
31
+ @pc = @ea
32
+ end
33
+
34
+ def nega = @a = neg8(@a)
35
+ def xnca = @a = xnc8(@a)
36
+ def coma = @a = com8(@a)
37
+ def lsra = @a = lsr8(@a)
38
+ def rora = @a = ror8(@a)
39
+ def asra = @a = asr8(@a)
40
+ def asla = @a = asl8(@a)
41
+ def rola = @a = rol8(@a)
42
+ def deca = @a = dec8(@a)
43
+ def xdeca = @a = xdec8(@a)
44
+ def inca = @a = inc8(@a)
45
+ def tsta = logic8(@a)
46
+ def xclra = @a = xclr8
47
+ def clra = @a = clr8
48
+
49
+ def negb = @b = neg8(@b)
50
+ def xncb = @b = xnc8(@b)
51
+ def comb = @b = com8(@b)
52
+ def lsrb = @b = lsr8(@b)
53
+ def rorb = @b = ror8(@b)
54
+ def asrb = @b = asr8(@b)
55
+ def aslb = @b = asl8(@b)
56
+ def rolb = @b = rol8(@b)
57
+ def decb = @b = dec8(@b)
58
+ def xdecb = @b = xdec8(@b)
59
+ def incb = @b = inc8(@b)
60
+ def tstb = logic8(@b)
61
+ def xclrb = @b = xclr8
62
+ def clrb = @b = clr8
63
+
64
+ def suba = @a = sub8(@a, @value, 0)
65
+ def cmpa = sub8(@a, @value, 0)
66
+ def sbca = @a = sub8(@a, @value, carry)
67
+ def anda = @a = logic8(@a & @value)
68
+ def bita = logic8(@a & @value)
69
+ def lda = @a = logic8(@value)
70
+ def sta = @value = logic8(@a)
71
+ def eora = @a = logic8(@a ^ @value)
72
+ def adca = @a = add8(@a, @value, carry)
73
+ def ora = @a = logic8(@a | @value)
74
+ def adda = @a = add8(@a, @value, 0)
75
+
76
+ def subb = @b = sub8(@b, @value, 0)
77
+ def cmpb = sub8(@b, @value, 0)
78
+ def sbcb = @b = sub8(@b, @value, carry)
79
+ def andb = @b = logic8(@b & @value)
80
+ def bitb = logic8(@b & @value)
81
+ def ldb = @b = logic8(@value)
82
+ def stb = @value = logic8(@b)
83
+ def eorb = @b = logic8(@b ^ @value)
84
+ def adcb = @b = add8(@b, @value, carry)
85
+ def orb = @b = logic8(@b | @value)
86
+ def addb = @b = add8(@b, @value, 0)
87
+
88
+ def subd = self.d = sub16(d, @value)
89
+ def addd = self.d = add16(d, @value)
90
+ def cmpd = sub16(d, @value)
91
+ def cmpx = sub16(@x, @value)
92
+ def cmpy = sub16(@y, @value)
93
+ def cmpu = sub16(@u, @value)
94
+ def cmps = sub16(@s, @value)
95
+ def xaddd = add16(d, @value)
96
+ def xaddu = add16(@u | 0xff00, @value)
97
+
98
+ def ldd = self.d = logic16(@value)
99
+ def ldx = @x = logic16(@value)
100
+ def ldy = @y = logic16(@value)
101
+ def ldu = @u = logic16(@value)
102
+ def lds = load_s(logic16(@value))
103
+ def std = @value = logic16(d)
104
+ def stx = @value = logic16(@x)
105
+ def sty = @value = logic16(@y)
106
+ def stu = @value = logic16(@u)
107
+ def sts = @value = logic16(@s)
108
+
109
+ # The undocumented stores immediate. Prefixed, they set N and clear
110
+ # Z and V. Unprefixed, their flags hang on the instruction before
111
+ # (Banks' "Flags for unprefixed Store Immediate"), and the 16-bit
112
+ # ones also clear Z unless the register's low byte is 0.
113
+ def xsta = flags(NEGATIVE | ZERO | OVERFLOW, previous_group_flags)
114
+ def xstb = flags(NEGATIVE | ZERO | OVERFLOW, previous_group_flags)
115
+ def xsta2 = flags(NEGATIVE | ZERO | OVERFLOW, NEGATIVE)
116
+ def xstb2 = flags(NEGATIVE | ZERO | OVERFLOW, NEGATIVE)
117
+ def xstx = @value = store_word_low(@x)
118
+ def xstu = @value = store_word_low(@u)
119
+
120
+ def xsty = @value = store_word_low_prefixed(@y)
121
+ def xsts = @value = store_word_low_prefixed(@s)
122
+ def xstx2 = @value = store_word_low_prefixed(@x)
123
+ def xstu2 = @value = store_word_low_prefixed(@u)
124
+
125
+ def store_word_low(word)
126
+ low = word & 0xff
127
+ set = previous_group_flags & (NEGATIVE | ZERO)
128
+ set &= ~ZERO unless low.zero?
129
+ flags(NEGATIVE | ZERO | OVERFLOW, set)
130
+ low
131
+ end
132
+
133
+ def store_word_low_prefixed(word)
134
+ flags(NEGATIVE | ZERO | OVERFLOW, NEGATIVE)
135
+ word & 0xff
136
+ end
137
+
138
+ def previous_group_flags
139
+ case @previous_group
140
+ when GROUP_N0_Z1 then ZERO
141
+ when GROUP_A_00 then nz8(@a)
142
+ when GROUP_A_FF then (@a == 0xff ? ZERO : 0) | (@a.anybits?(0x80) ? 0 : NEGATIVE)
143
+ when GROUP_A_01 then decremented_flags(@a)
144
+ when GROUP_B_01 then decremented_flags(@b)
145
+ when GROUP_WORD_01 then decremented_flags(@alu_word >> 8)
146
+ when GROUP_LEA then decremented_flags((@alu_word >> 8) | (@alu_word & 0xff))
147
+ else NEGATIVE
148
+ end
149
+ end
150
+
151
+ # Z for 1, N for a negative value less one, V for $80.
152
+ def decremented_flags(value)
153
+ (value == 1 ? ZERO : 0) | ((value - 1).nobits?(0x80) ? 0 : NEGATIVE) | (value == 0x80 ? OVERFLOW : 0)
154
+ end
155
+
156
+ # JSR, BSR and LBSR, once the return address is stacked.
157
+ def jsr
158
+ @pc = @ea
159
+ end
160
+
161
+ def leax
162
+ @alu_word = @ea
163
+ @x = @ea
164
+ flags(ZERO, @ea.zero? ? ZERO : 0)
165
+ end
166
+
167
+ def leay
168
+ @alu_word = @ea
169
+ @y = @ea
170
+ flags(ZERO, @ea.zero? ? ZERO : 0)
171
+ end
172
+
173
+ def leas
174
+ @alu_word = @ea
175
+ load_s(@ea)
176
+ end
177
+
178
+ def leau
179
+ @alu_word = @ea
180
+ @u = @ea
181
+ end
182
+
183
+ def orcc = @cc |= @value
184
+ def andcc = @cc &= @value
185
+
186
+ def abx = @x = (@x + @b) & 0xffff
187
+
188
+ def mul
189
+ self.d = @a * @b
190
+ flags(ZERO | CARRY, (d.zero? ? ZERO : 0) | (@b >> 7))
191
+ end
192
+
193
+ def sex
194
+ @a = @b.nobits?(0x80) ? 0 : 0xff
195
+ flags(NEGATIVE | ZERO, nz16(d))
196
+ end
197
+
198
+ # DAA. V is C xor bit 7 of the result, as Banks found, where the
199
+ # datasheet clears it.
200
+ def daa
201
+ correction = 0
202
+ correction |= 0x06 if @cc.anybits?(HALF_CARRY) || (@a & 0x0f) > 0x09
203
+ high = @a & 0xf0
204
+ correction |= 0x60 if @cc.anybits?(CARRY) || high > 0x90 || (high > 0x80 && (@a & 0x0f) > 0x09)
205
+ result = (@a + correction) & 0xff
206
+ carry = correction >= 0x60 ? CARRY : 0
207
+ overflow = (carry == 1) == result.anybits?(0x80) ? 0 : OVERFLOW
208
+ flags(NEGATIVE | ZERO | OVERFLOW | CARRY, nz8(result) | overflow | carry)
209
+ @a = result
210
+ end
211
+
212
+ # The undocumented $18: each flag, ANDed with its bit of the byte
213
+ # after the opcode, moves one bit up, and V also takes Z ANDed with
214
+ # bit 2.
215
+ def x18
216
+ masked = @cc & @value
217
+ @cc = ((masked << 1) & 0xff) | (masked.nobits?(ZERO) ? 0 : OVERFLOW)
218
+ end
219
+
220
+ def exg
221
+ first = @post >> 4
222
+ second = @post & 0x0f
223
+ value = transfer_register(second)
224
+ set_transfer_register(second, sized(transfer_register(first), first, second))
225
+ set_transfer_register(first, sized(value, second, first))
226
+ end
227
+
228
+ def tfr
229
+ source = @post >> 4
230
+ destination = @post & 0x0f
231
+ set_transfer_register(destination, sized(transfer_register(source), source, destination))
232
+ end
233
+
234
+ # A value moved from register code +from+ to +to+: an 8-bit register
235
+ # fills a 16-bit one's high byte with $FF, and a 16-bit one gives an
236
+ # 8-bit one its low byte.
237
+ def sized(value, from, to)
238
+ return value & 0xff if to >= 8
239
+ return value | 0xff00 if from >= 8
240
+
241
+ value
242
+ end
243
+
244
+ def swi = software_interrupt(0xfffa, IRQ_MASK | FIRQ_MASK, ENTIRE)
245
+ def swi2 = software_interrupt(0xfff4, 0, ENTIRE)
246
+ def swi3 = software_interrupt(0xfff2, 0, ENTIRE)
247
+ def xres = software_interrupt(0xfffe, 0, 0)
248
+ def xswi2 = software_interrupt(0xfff4, 0, 0)
249
+ def xfirq = software_interrupt(0xfff6, 0, 0)
250
+
251
+ def software_interrupt(vector, mask, entire)
252
+ @cc |= entire
253
+ interrupt_vector(vector, mask)
254
+ end
255
+ end
256
+ end
257
+ end
258
+ end
@@ -0,0 +1,143 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ module Chips
5
+ class M6809
6
+ # The condition code bits.
7
+ CARRY = 0x01
8
+ OVERFLOW = 0x02
9
+ ZERO = 0x04
10
+ NEGATIVE = 0x08
11
+ IRQ_MASK = 0x10
12
+ HALF_CARRY = 0x20
13
+ FIRQ_MASK = 0x40
14
+ ENTIRE = 0x80
15
+
16
+ # What an opcode runs: the instruction method its *_exec step calls,
17
+ # the steps that work out its address, one a cycle, and the steps
18
+ # that follow them, and the group the undocumented stores immediate
19
+ # take their flags from when it ran before them. An indexed opcode's
20
+ # address steps end in the postbyte, which picks the rest from
21
+ # INDEXED_PLANS.
22
+ Entry = Struct.new(:operation, :plan, :tail, :group)
23
+
24
+ # The groups of David Banks' "Flags for unprefixed Store Immediate".
25
+ GROUP_OTHER = 0
26
+ GROUP_N0_Z1 = 1
27
+ GROUP_A_00 = 2
28
+ GROUP_A_FF = 3
29
+ GROUP_A_01 = 4
30
+ GROUP_B_01 = 5
31
+ GROUP_WORD_01 = 6
32
+ GROUP_LEA = 7
33
+ GROUPS = {
34
+ GROUP_N0_Z1 => %i[bita cmpa],
35
+ GROUP_A_00 => %i[lda ldd],
36
+ GROUP_A_FF => %i[adca adda anda eora ora sbca suba],
37
+ GROUP_A_01 => %i[asla asra coma daa deca inca lsra nega rola rora subd tsta xdeca xnca],
38
+ GROUP_B_01 => %i[sex],
39
+ GROUP_WORD_01 => %i[cmpd cmps cmpu cmpx cmpy xaddd xaddu],
40
+ GROUP_LEA => %i[leas leau leax leay]
41
+ }.flat_map { |group, operations| operations.map { |operation| [operation, group] } }.to_h.freeze
42
+
43
+ # The plan between instructions: the next cycle fetches an opcode.
44
+ FETCH_PLAN = %i[fetch].freeze
45
+
46
+ # The tail an instruction has once it is running its tail, never run.
47
+ # The empty tail, JUMP_TAIL, runs the instruction method as the
48
+ # address steps end.
49
+ NO_TAIL = %i[none].freeze
50
+ JUMP_TAIL = [].freeze
51
+
52
+ DIRECT_PLAN = %i[direct dead_v].freeze
53
+ EXTENDED_PLAN = %i[extended_hi extended_lo dead_v].freeze
54
+ INDEXED_PLAN = %i[postbyte].freeze
55
+
56
+ # The steps that read or write the address once it is known.
57
+ TAILS = {
58
+ read8: %i[read_exec],
59
+ read16: %i[read_hi read_lo_exec],
60
+ read16_dead: %i[read_hi read_lo_exec dead_v],
61
+ write8: %i[write_exec],
62
+ write16: %i[write_hi_exec write_lo],
63
+ rmw: %i[rmw_read dead_v rmw_write],
64
+ test: %i[read_exec dead_v dead_v],
65
+ jump: JUMP_TAIL,
66
+ subroutine: %i[dead_ea dead_v push_pcl push_pch_exec],
67
+ load_address: %i[dead_v_exec]
68
+ }.freeze
69
+
70
+ IMMEDIATE_TAILS = {
71
+ read8: %i[immediate_exec],
72
+ read16: %i[immediate_hi immediate_lo_exec],
73
+ read16_dead: %i[immediate_hi immediate_lo_exec dead_v]
74
+ }.freeze
75
+
76
+ # The 12 bytes SWI and the hardware interrupts other than FIRQ stack,
77
+ # in the order they go onto S.
78
+ PUSH_ALL = %i[push_pcl push_pch push_wl push_wh push_yl push_yh push_xl push_xh push_dp push_b push_a
79
+ push_cc].freeze
80
+ VECTOR_STEPS = %i[dead_v vector_hi vector_lo dead_v].freeze
81
+
82
+ # SWI, SWI2, SWI3 and the undocumented XRES, XSWI2 and XFIRQ, after
83
+ # the opcode. The instruction method picks the vector.
84
+ SOFTWARE_INTERRUPT_TAIL = [:dead_pc, :dead_v_exec, *PUSH_ALL, *VECTOR_STEPS].freeze
85
+
86
+ # IRQ and NMI after the cycle that would have fetched the opcode, and
87
+ # FIRQ, which stacks only PC and CC.
88
+ INTERRUPT_PLAN = [:dead_pc, :dead_v_exec, *PUSH_ALL, *VECTOR_STEPS].freeze
89
+ FAST_INTERRUPT_PLAN = [:dead_pc, :dead_v_exec, :push_pcl, :push_pch, :push_cc, *VECTOR_STEPS].freeze
90
+
91
+ # The interrupt CWAI was waiting for: its vector, no more stacking.
92
+ WAITED_INTERRUPT_PLAN = %i[vector_hi vector_lo dead_v].freeze
93
+
94
+ # RTI after pulling CC, with E set and with E clear.
95
+ RETURN_ENTIRE_PLAN = %i[pull_a pull_b pull_dp pull_xh pull_xl pull_yh pull_yl pull_wh pull_wl pull_pch pull_pcl
96
+ dead_stack].freeze
97
+ RETURN_FAST_PLAN = %i[pull_pch pull_pcl dead_stack].freeze
98
+
99
+ # The cycles after RESET goes high: two dead reads of $FFFE, the
100
+ # vector, and a dead cycle.
101
+ RESET_PLAN = %i[reset_dead reset_dead reset_hi reset_lo dead_v].freeze
102
+
103
+ LONG_BRANCH_TAKEN_PLAN = %i[dead_v_take].freeze
104
+ SYNC_END_PLAN = %i[idle].freeze
105
+
106
+ # PSH and PUL by postbyte: the bytes in the order they go onto or
107
+ # come off the stack, PC first on and last off.
108
+ PUSH_ORDER = [[0x80, %i[push_pcl push_pch]], [0x40, %i[push_wl push_wh]], [0x20, %i[push_yl push_yh]],
109
+ [0x10, %i[push_xl push_xh]], [0x08, %i[push_dp]], [0x04, %i[push_b]], [0x02, %i[push_a]],
110
+ [0x01, %i[push_cc]]].freeze
111
+ PULL_ORDER = [[0x01, %i[pull_cc]], [0x02, %i[pull_a]], [0x04, %i[pull_b]], [0x08, %i[pull_dp]],
112
+ [0x10, %i[pull_xh pull_xl]], [0x20, %i[pull_yh pull_yl]], [0x40, %i[pull_wh pull_wl]],
113
+ [0x80, %i[pull_pch pull_pcl]]].freeze
114
+ PUSH_PLANS = Array.new(256) do |mask|
115
+ [:dead_v, :dead_v, :dead_stack, *PUSH_ORDER.flat_map { |bit, steps| mask.nobits?(bit) ? [] : steps }].freeze
116
+ end.freeze
117
+ PULL_PLANS = Array.new(256) do |mask|
118
+ [:dead_v, :dead_v, *PULL_ORDER.flat_map { |bit, steps| mask.nobits?(bit) ? [] : steps }, :dead_stack].freeze
119
+ end.freeze
120
+
121
+ # The steps after the postbyte, by postbyte, Figure 17 sheet 4.
122
+ INDEXED_PLANS = Array.new(256) do |post|
123
+ next %i[dead_pc dead_v].freeze if post < 0x80
124
+
125
+ plan = case post & 0x0f
126
+ when 0x0, 0x2 then %i[dead_pc dead_v dead_v]
127
+ when 0x1, 0x3 then %i[dead_pc dead_v dead_v dead_v]
128
+ when 0x4 then %i[dead_pc]
129
+ when 0x5, 0x6 then %i[dead_pc dead_v]
130
+ when 0x8 then %i[offset8 dead_v]
131
+ when 0x9 then %i[offset_hi offset_lo_register dead_pc dead_v dead_v]
132
+ when 0xb then %i[dead_pc dead_pc_next dead_pc_next2 dead_v dead_v]
133
+ when 0xc then %i[offset8_pc dead_v]
134
+ when 0xd then %i[offset_hi offset_lo_pc dead_pc dead_v dead_v dead_v]
135
+ when 0xf then %i[offset_hi offset_lo_absolute dead_pc]
136
+ else []
137
+ end
138
+ plan += %i[indirect_hi indirect_lo dead_v] if post.anybits?(0x10)
139
+ plan.freeze
140
+ end.freeze
141
+ end
142
+ end
143
+ end
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ module Chips
5
+ class M6809
6
+ # The programmer's registers, and the codes TFR and EXG and the
7
+ # indexed postbyte name them by.
8
+ module Registers
9
+ attr_accessor :a, :b, :x, :y, :u, :s, :pc, :dp, :cc
10
+
11
+ def d = (@a << 8) | @b
12
+
13
+ def d=(value)
14
+ @a = value >> 8
15
+ @b = value & 0xff
16
+ end
17
+
18
+ # Whether NMI is armed: a load of S since reset.
19
+ def nmi_armed? = @nmi_armed
20
+
21
+ private
22
+
23
+ def load_s(value)
24
+ @s = value
25
+ @nmi_armed = true
26
+ end
27
+
28
+ # The register bits 5 and 6 of an indexed postbyte name.
29
+ def index_register
30
+ case @post & 0x60
31
+ when 0x00 then @x
32
+ when 0x20 then @y
33
+ when 0x40 then @u
34
+ else @s
35
+ end
36
+ end
37
+
38
+ def index_register=(value)
39
+ case @post & 0x60
40
+ when 0x00 then @x = value
41
+ when 0x20 then @y = value
42
+ when 0x40 then @u = value
43
+ else load_s(value)
44
+ end
45
+ end
46
+
47
+ # A TFR or EXG register: D, X, Y, U, S and PC are 0 to 5, A, B, CC
48
+ # and DP 8 to 11. The other codes read as $FF or $FFFF.
49
+ def transfer_register(code)
50
+ case code
51
+ when 0x0 then d
52
+ when 0x1 then @x
53
+ when 0x2 then @y
54
+ when 0x3 then @u
55
+ when 0x4 then @s
56
+ when 0x5 then @pc
57
+ when 0x8 then @a
58
+ when 0x9 then @b
59
+ when 0xa then @cc
60
+ when 0xb then @dp
61
+ else code < 8 ? 0xffff : 0xff
62
+ end
63
+ end
64
+
65
+ def set_transfer_register(code, value)
66
+ case code
67
+ when 0x0 then self.d = value
68
+ when 0x1 then @x = value
69
+ when 0x2 then @y = value
70
+ when 0x3 then @u = value
71
+ when 0x4 then load_s(value)
72
+ when 0x5 then @pc = value
73
+ when 0x8 then @a = value
74
+ when 0x9 then @b = value
75
+ when 0xa then @cc = value
76
+ when 0xb then @dp = value
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,140 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ module Chips
5
+ class M6809
6
+ # The stack steps of PSH, PUL, JSR, BSR, RTS, RTI, the software
7
+ # interrupts and the hardware ones. Each pushes onto or pulls off the
8
+ # stack @stack_u names: U for PSHU and PULU, S otherwise. The "other"
9
+ # stack pointer, w, is the one that isn't.
10
+ module Stack
11
+ private
12
+
13
+ def push(value)
14
+ if @stack_u
15
+ @u = (@u - 1) & 0xffff
16
+ @bus.write(@u, value)
17
+ else
18
+ @s = (@s - 1) & 0xffff
19
+ @bus.write(@s, value)
20
+ end
21
+ end
22
+
23
+ def pull
24
+ if @stack_u
25
+ value = @bus.read(@u)
26
+ @u = (@u + 1) & 0xffff
27
+ else
28
+ value = @bus.read(@s)
29
+ @s = (@s + 1) & 0xffff
30
+ end
31
+ value
32
+ end
33
+
34
+ def dead_stack = @bus.read(@stack_u ? @u : @s)
35
+
36
+ def other_stack = @stack_u ? @s : @u
37
+
38
+ def other_stack=(value)
39
+ @stack_u ? load_s(value) : @u = value
40
+ end
41
+
42
+ def push_s_post = stack_post(false, PUSH_PLANS)
43
+
44
+ def push_u_post = stack_post(true, PUSH_PLANS)
45
+
46
+ def pull_s_post = stack_post(false, PULL_PLANS)
47
+
48
+ def pull_u_post = stack_post(true, PULL_PLANS)
49
+
50
+ def stack_post(stack_u, plans)
51
+ @stack_u = stack_u
52
+ switch_to(plans[read_pc])
53
+ end
54
+
55
+ def push_pcl = push(@pc & 0xff)
56
+ def push_pch = push(@pc >> 8)
57
+ def push_wl = push(other_stack & 0xff)
58
+ def push_wh = push(other_stack >> 8)
59
+ def push_yl = push(@y & 0xff)
60
+ def push_yh = push(@y >> 8)
61
+ def push_xl = push(@x & 0xff)
62
+ def push_xh = push(@x >> 8)
63
+ def push_dp = push(@dp)
64
+ def push_b = push(@b)
65
+ def push_a = push(@a)
66
+ def push_cc = push(@cc)
67
+
68
+ def push_pch_exec
69
+ push_pch
70
+ execute
71
+ end
72
+
73
+ def pull_cc
74
+ @cc = pull
75
+ end
76
+
77
+ def pull_a
78
+ @a = pull
79
+ end
80
+
81
+ def pull_b
82
+ @b = pull
83
+ end
84
+
85
+ def pull_dp
86
+ @dp = pull
87
+ end
88
+
89
+ def pull_xh
90
+ @x = (pull << 8) | (@x & 0xff)
91
+ end
92
+
93
+ def pull_xl
94
+ @x = (@x & 0xff00) | pull
95
+ end
96
+
97
+ def pull_yh
98
+ @y = (pull << 8) | (@y & 0xff)
99
+ end
100
+
101
+ def pull_yl
102
+ @y = (@y & 0xff00) | pull
103
+ end
104
+
105
+ def pull_wh
106
+ self.other_stack = (pull << 8) | (other_stack & 0xff)
107
+ end
108
+
109
+ def pull_wl
110
+ self.other_stack = (other_stack & 0xff00) | pull
111
+ end
112
+
113
+ def pull_pch
114
+ @pc = (pull << 8) | (@pc & 0xff)
115
+ end
116
+
117
+ def pull_pcl
118
+ @pc = (@pc & 0xff00) | pull
119
+ end
120
+
121
+ # RTI after pulling CC: the whole state when E is set, else PC.
122
+ def return_cc
123
+ pull_cc
124
+ switch_to(@cc.nobits?(ENTIRE) ? RETURN_FAST_PLAN : RETURN_ENTIRE_PLAN)
125
+ end
126
+
127
+ # The vector of the interrupt or SWI being taken. The masks it sets
128
+ # take effect as its first byte is read.
129
+ def vector_hi
130
+ @value = @bus.read(@vector) << 8
131
+ @cc |= @interrupt_mask
132
+ end
133
+
134
+ def vector_lo
135
+ @pc = @value | @bus.read(@vector + 1)
136
+ end
137
+ end
138
+ end
139
+ end
140
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "badline/chips/m6809/core"
4
+
5
+ module Badline
6
+ module Chips
7
+ # The Motorola 6809E, which the SuperPET has beside its 6502. It is not
8
+ # part of badline/core, so a machine that has one requires it.
9
+ class M6809
10
+ include Core
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ module Chips
5
+ class RIOT
6
+ class Timer
7
+ # Running the timer a stretch of cycles at once, as cycle! would one
8
+ # by one, for a drive sleeping through its idle loops.
9
+ module FastForward
10
+ # quiet_cycles for a timer whose flag can't pull IRQ.
11
+ QUIET = 1 << 40
12
+
13
+ # How many cycles the timer can run without pulling IRQ: until the
14
+ # count that sets its flag, and forever with its interrupt off.
15
+ def quiet_cycles
16
+ return QUIET unless @irq_enabled
17
+
18
+ cycles_to_flag - 1
19
+ end
20
+
21
+ # Runs +cycles+ cycles at once.
22
+ def fast_forward(cycles)
23
+ return if cycles.zero?
24
+ return count_expired(cycles) if @expired
25
+
26
+ to_flag = cycles_to_flag
27
+ if cycles < to_flag
28
+ count_down(cycles)
29
+ else
30
+ @counter = 0xff
31
+ @prescale = @divider
32
+ @expired = @flag = @fired = true
33
+ count_expired(cycles - to_flag) if cycles > to_flag
34
+ end
35
+ end
36
+
37
+ # What the counter and the flag don't hold, for comparing the
38
+ # timer at two points.
39
+ def state = [@divider, @expired, @irq_enabled]
40
+
41
+ private
42
+
43
+ # The cycles until the count that sets the flag: the count through
44
+ # 0 at the interval, or the next once expired.
45
+ def cycles_to_flag = @expired ? @counter + 1 : @prescale + (@counter * @divider)
46
+
47
+ # Counts at the interval, short of 0.
48
+ def count_down(cycles)
49
+ @fired = false
50
+ if cycles < @prescale
51
+ @prescale -= cycles
52
+ else
53
+ rest = cycles - @prescale
54
+ @counter -= 1 + (rest / @divider)
55
+ @prescale = @divider - (rest % @divider)
56
+ end
57
+ end
58
+
59
+ # Counts every cycle, setting the flag at each pass through 0.
60
+ def count_expired(cycles)
61
+ to_wrap = @counter + 1
62
+ @counter = (@counter - cycles) & 0xff
63
+ @fired = cycles >= to_wrap && ((cycles - to_wrap) % 256).zero?
64
+ @flag = true if cycles >= to_wrap
65
+ end
66
+ end
67
+
68
+ include FastForward
69
+ end
70
+ end
71
+ end
72
+ end