badline 0.17.0 → 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 (166) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +153 -0
  3. data/CLAUDE.md +11 -7
  4. data/CONTRIBUTING.md +7 -1
  5. data/README.md +169 -20
  6. data/doc/pinned-behaviour.md +128 -0
  7. data/doc/snapshot-format.md +253 -18
  8. data/doc/snapshots.md +57 -23
  9. data/exe/badline-ruby +5 -0
  10. data/lib/badline/audio/terminal.rb +34 -2
  11. data/lib/badline/c128/keyed_state.rb +2 -0
  12. data/lib/badline/c128.rb +1 -0
  13. data/lib/badline/c64/keyed_state.rb +2 -0
  14. data/lib/badline/c64.rb +1 -0
  15. data/lib/badline/cbm2/keyed_state.rb +4 -0
  16. data/lib/badline/cbm2/p500.rb +5 -3
  17. data/lib/badline/cbm2.rb +6 -3
  18. data/lib/badline/chips/cia/timer.rb +2 -2
  19. data/lib/badline/chips/cpu/instruction_set.rb +1 -1
  20. data/lib/badline/chips/m6809/arithmetic.rb +140 -0
  21. data/lib/badline/chips/m6809/bus_steps.rb +223 -0
  22. data/lib/badline/chips/m6809/core.rb +149 -0
  23. data/lib/badline/chips/m6809/indexed.rb +82 -0
  24. data/lib/badline/chips/m6809/interrupts.rb +159 -0
  25. data/lib/badline/chips/m6809/keyed_state.rb +147 -0
  26. data/lib/badline/chips/m6809/opcodes.rb +159 -0
  27. data/lib/badline/chips/m6809/operations.rb +258 -0
  28. data/lib/badline/chips/m6809/plans.rb +143 -0
  29. data/lib/badline/chips/m6809/registers.rb +82 -0
  30. data/lib/badline/chips/m6809/stack.rb +140 -0
  31. data/lib/badline/chips/m6809.rb +13 -0
  32. data/lib/badline/chips/riot/timer.rb +14 -3
  33. data/lib/badline/chips/rriot.rb +4 -2
  34. data/lib/badline/chips/sid/filter.rb +4 -4
  35. data/lib/badline/chips/sid/waveform.rb +6 -6
  36. data/lib/badline/chips/vic_i/keyed_parts.rb +7 -2
  37. data/lib/badline/chips/vic_i.rb +84 -21
  38. data/lib/badline/data_path.rb +3 -3
  39. data/lib/badline/drives/ieee_models.rb +54 -0
  40. data/lib/badline/drives/model1001.rb +37 -0
  41. data/lib/badline/drives/model2031.rb +6 -0
  42. data/lib/badline/drives/model4040/disk.rb +62 -0
  43. data/lib/badline/drives/model4040/ip_bus.rb +46 -0
  44. data/lib/badline/drives/model4040/keyed_state.rb +57 -0
  45. data/lib/badline/drives/model4040/mechanism.rb +192 -0
  46. data/lib/badline/drives/model4040/parts.rb +20 -0
  47. data/lib/badline/drives/model4040.rb +179 -0
  48. data/lib/badline/drives/model8050/board.rb +77 -0
  49. data/lib/badline/drives/model8050/dos_board.rb +91 -0
  50. data/lib/badline/drives/model8050/idle.rb +11 -7
  51. data/lib/badline/drives/model8050/keyed_state.rb +5 -2
  52. data/lib/badline/drives/model8050.rb +18 -130
  53. data/lib/badline/drives/model9060/idle.rb +149 -0
  54. data/lib/badline/drives/model9060/keyed_state.rb +123 -0
  55. data/lib/badline/drives/model9060/parts.rb +98 -0
  56. data/lib/badline/drives/model9060/sasi_bus.rb +88 -0
  57. data/lib/badline/drives/model9060/sasi_commands.rb +168 -0
  58. data/lib/badline/drives/model9060/sasi_target.rb +161 -0
  59. data/lib/badline/drives/model9060.rb +195 -0
  60. data/lib/badline/frontend/app.rb +8 -0
  61. data/lib/badline/frontend/boot.rb +3 -2
  62. data/lib/badline/frontend/controls.rb +62 -6
  63. data/lib/badline/frontend/keys.rb +36 -0
  64. data/lib/badline/frontend/kim1_panel.rb +208 -0
  65. data/lib/badline/frontend/media_slots.rb +8 -1
  66. data/lib/badline/frontend/menu/drive_types.rb +56 -0
  67. data/lib/badline/frontend/menu/menu_dialogs.rb +61 -14
  68. data/lib/badline/frontend/menu/menu_media.rb +42 -12
  69. data/lib/badline/frontend/menu/menu_pages.rb +97 -2
  70. data/lib/badline/frontend/menu/menu_ports.rb +38 -1
  71. data/lib/badline/frontend/menu/pause_menu.rb +5 -1
  72. data/lib/badline/frontend/pet_keyboard.rb +23 -1
  73. data/lib/badline/frontend/screen.rb +1 -1
  74. data/lib/badline/frontend/screens.rb +40 -2
  75. data/lib/badline/frontend/teletype_pane.rb +152 -0
  76. data/lib/badline/frontend/timeline.rb +31 -2
  77. data/lib/badline/frontend.rb +3 -0
  78. data/lib/badline/kim1/bus.rb +117 -0
  79. data/lib/badline/kim1/cpu.rb +25 -0
  80. data/lib/badline/kim1/host_terminal.rb +74 -0
  81. data/lib/badline/kim1/key_entry.rb +74 -0
  82. data/lib/badline/kim1/keyed_modules.rb +53 -0
  83. data/lib/badline/kim1/keyed_state.rb +133 -0
  84. data/lib/badline/kim1/panel.rb +176 -0
  85. data/lib/badline/kim1/rriots.rb +28 -0
  86. data/lib/badline/kim1/sound.rb +29 -0
  87. data/lib/badline/kim1/tape.rb +116 -0
  88. data/lib/badline/kim1/teletype.rb +193 -0
  89. data/lib/badline/kim1.rb +190 -0
  90. data/lib/badline/machine/ieee_drives.rb +94 -117
  91. data/lib/badline/machine/keyboard_buffer.rb +20 -3
  92. data/lib/badline/machine/line_sound.rb +122 -0
  93. data/lib/badline/machine.rb +46 -15
  94. data/lib/badline/media/extensions.rb +8 -4
  95. data/lib/badline/media/kim1_media.rb +150 -0
  96. data/lib/badline/media/pet_media.rb +3 -1
  97. data/lib/badline/media/true_drive.rb +42 -45
  98. data/lib/badline/media.rb +12 -6
  99. data/lib/badline/options/family.rb +98 -8
  100. data/lib/badline/options/help.rb +44 -12
  101. data/lib/badline/options/table.rb +43 -11
  102. data/lib/badline/options/validation.rb +44 -8
  103. data/lib/badline/options.rb +27 -8
  104. data/lib/badline/pet/bus.rb +16 -2
  105. data/lib/badline/pet/keyboard_ports.rb +9 -2
  106. data/lib/badline/pet/keyed_state.rb +4 -0
  107. data/lib/badline/pet/model.rb +11 -2
  108. data/lib/badline/pet/rom_sets.rb +1 -0
  109. data/lib/badline/pet/sound.rb +5 -105
  110. data/lib/badline/pet/superpet/acia.rb +12 -0
  111. data/lib/badline/pet/superpet/board.rb +221 -0
  112. data/lib/badline/pet/superpet/dongle.rb +81 -0
  113. data/lib/badline/pet/superpet/keyed_modules.rb +63 -0
  114. data/lib/badline/pet/superpet/keyed_parts.rb +76 -0
  115. data/lib/badline/pet/superpet/m6809.rb +32 -0
  116. data/lib/badline/pet/superpet/waterloo_traps.rb +199 -0
  117. data/lib/badline/pet/superpet.rb +216 -0
  118. data/lib/badline/pet.rb +19 -3
  119. data/lib/badline/plus4/keyed_state.rb +2 -0
  120. data/lib/badline/plus4.rb +1 -0
  121. data/lib/badline/roms/1001/fdc-251257-02a.rom +0 -0
  122. data/lib/badline/roms/4040/dos-1.2.rom +0 -0
  123. data/lib/badline/roms/4040/dos-2.1-rev.rom +0 -0
  124. data/lib/badline/roms/4040/dos-2.1.rom +0 -0
  125. data/lib/badline/roms/4040/fdc-901466-02.rom +0 -0
  126. data/lib/badline/roms/4040/fdc-901466-04.rom +0 -0
  127. data/lib/badline/roms/9060/dos-3.0.rom +0 -0
  128. data/lib/badline/roms/9060/sasi-300515-revb.rom +0 -0
  129. data/lib/badline/roms/README +166 -0
  130. data/lib/badline/roms/kim1/6530-002.rom +0 -0
  131. data/lib/badline/roms/kim1/6530-003.rom +0 -0
  132. data/lib/badline/roms/pet/characters-superpet.rom +0 -0
  133. data/lib/badline/roms/pet/superpet-a000.rom +0 -0
  134. data/lib/badline/roms/pet/superpet-c000.rom +0 -0
  135. data/lib/badline/roms/pet/superpet-e000-50hz.rom +0 -0
  136. data/lib/badline/roms/pet/superpet-e000.rom +0 -0
  137. data/lib/badline/snapshot/container.rb +2 -0
  138. data/lib/badline/snapshot/field_doc/markdown.rb +1 -1
  139. data/lib/badline/snapshot/field_doc.rb +27 -6
  140. data/lib/badline/snapshot/image.rb +4 -1
  141. data/lib/badline/snapshot/kim1_setup.rb +28 -0
  142. data/lib/badline/snapshot/pet_setup.rb +27 -11
  143. data/lib/badline/snapshot/storage_setup.rb +4 -1
  144. data/lib/badline/snapshot/vic20_setup.rb +11 -7
  145. data/lib/badline/snapshot/vice/pet.rb +13 -7
  146. data/lib/badline/snapshot/vice/pet_cpu.rb +39 -5
  147. data/lib/badline/snapshot/vice/pet_crtc.rb +15 -2
  148. data/lib/badline/snapshot/vice/pet_drive8050.rb +188 -68
  149. data/lib/badline/snapshot/vice/pet_mem.rb +49 -5
  150. data/lib/badline/snapshot/vice/pet_restore.rb +40 -11
  151. data/lib/badline/snapshot.rb +4 -0
  152. data/lib/badline/status.rb +30 -30
  153. data/lib/badline/storage/d67_image.rb +55 -0
  154. data/lib/badline/storage/d90_image.rb +180 -0
  155. data/lib/badline/storage/disk_image/writing.rb +2 -2
  156. data/lib/badline/storage/kim_tape.rb +254 -0
  157. data/lib/badline/storage/papertape.rb +90 -0
  158. data/lib/badline/storage.rb +4 -0
  159. data/lib/badline/support/kim1_checkpoint.rb +41 -0
  160. data/lib/badline/support/superpet_checkpoint.rb +35 -0
  161. data/lib/badline/version.rb +1 -1
  162. data/lib/badline/vic20/keyed_modules.rb +6 -3
  163. data/lib/badline/vic20/keyed_state.rb +6 -4
  164. data/lib/badline/vic20.rb +26 -12
  165. data/lib/badline.rb +1 -0
  166. metadata +77 -1
@@ -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
@@ -13,8 +13,12 @@ module Badline
13
13
  # reads N - 1 until cycle T, 0 until cycle N x T, and $FF with the flag
14
14
  # set on cycle N x T: 52 at ÷8 sets the flag on cycle 416. From there
15
15
  # the counter counts every cycle, so it tells how long ago the flag
16
- # set, and each count through 0 sets the flag again. Only a write puts
17
- # the prescaler back.
16
+ # set, and each count through 0 sets the flag again. On the 6532 only
17
+ # a write puts the prescaler back. On the 6530, as the KIM-1 User
18
+ # Manual's appendix H has it, a read of the counter after the flag
19
+ # set does too, and the counter counts on from where it is at the
20
+ # interval written: a 6530 builds its timer with
21
+ # read_restores_divider. No test program has confirmed it.
18
22
  #
19
23
  # Reading or writing the counter clears the flag, except for a read on
20
24
  # the cycle that sets it. Each access takes A3 as the interrupt enable.
@@ -24,7 +28,8 @@ module Badline
24
28
  attr_reader :counter, :flag
25
29
  attr_accessor :irq_enabled
26
30
 
27
- def initialize
31
+ def initialize(read_restores_divider: false)
32
+ @read_restores_divider = read_restores_divider
28
33
  @counter = 0
29
34
  @divider = 1
30
35
  @prescale = 1
@@ -50,6 +55,7 @@ module Badline
50
55
  def read(irq_enabled)
51
56
  @irq_enabled = irq_enabled
52
57
  @flag = false unless @fired
58
+ restore_divider if @read_restores_divider && @expired
53
59
  @counter
54
60
  end
55
61
 
@@ -66,6 +72,11 @@ module Badline
66
72
 
67
73
  private
68
74
 
75
+ def restore_divider
76
+ @expired = false
77
+ @prescale = @divider
78
+ end
79
+
69
80
  def count
70
81
  @counter = (@counter - 1) & 0xff
71
82
  return unless @counter == 0xff
@@ -9,7 +9,9 @@ module Badline
9
9
  # of RAM, two 8-bit ports, A and B, each with a direction register, a 1
10
10
  # making the pin an output, and the 6532's interval timer (RIOT::Timer).
11
11
  # It has no edge detector. The 8050 and 8250's floppy controller has
12
- # one, its ROM the controller's program.
12
+ # one, its ROM the controller's program, and the KIM-1 two. Its timer
13
+ # reads its counter as the 6532's does, but a read after the flag sets
14
+ # puts the prescaler back (RIOT::Timer).
13
15
  #
14
16
  # Which addresses select the ROM, the RAM and the registers is part of
15
17
  # the mask, so the board decodes them and calls read_rom (A9-A0),
@@ -36,7 +38,7 @@ module Badline
36
38
  @peripheral = peripheral
37
39
  @irq_on_pb7 = irq_on_pb7
38
40
  @ram = Array.new(64, 0)
39
- @timer = RIOT::Timer.new
41
+ @timer = RIOT::Timer.new(read_restores_divider: true)
40
42
  reset!
41
43
  end
42
44