badline 0.17.0 → 0.19.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 (195) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +193 -0
  3. data/CLAUDE.md +19 -7
  4. data/CONTRIBUTING.md +11 -1
  5. data/README.md +178 -26
  6. data/doc/library.md +5 -1
  7. data/doc/media.md +3 -2
  8. data/doc/pinned-behaviour.md +169 -1
  9. data/doc/snapshot-format.md +302 -35
  10. data/doc/snapshots.md +97 -26
  11. data/doc/vice.md +344 -0
  12. data/exe/badline-ruby +5 -0
  13. data/lib/badline/audio/renderer.rb +4 -8
  14. data/lib/badline/audio/terminal.rb +34 -2
  15. data/lib/badline/c128/keyed_state.rb +2 -0
  16. data/lib/badline/c128.rb +5 -0
  17. data/lib/badline/c64/bus/fittings.rb +6 -0
  18. data/lib/badline/c64/keyed_state.rb +2 -0
  19. data/lib/badline/c64.rb +7 -0
  20. data/lib/badline/cbm2/keyed_state.rb +4 -0
  21. data/lib/badline/cbm2/p500.rb +9 -3
  22. data/lib/badline/cbm2.rb +10 -3
  23. data/lib/badline/chips/cia/timer.rb +2 -2
  24. data/lib/badline/chips/cpu/core.rb +29 -4
  25. data/lib/badline/chips/cpu/instruction_set.rb +1 -1
  26. data/lib/badline/chips/cpu/interrupts.rb +9 -0
  27. data/lib/badline/chips/cpu/keyed_state.rb +15 -9
  28. data/lib/badline/chips/cpu/microcode.rb +10 -0
  29. data/lib/badline/chips/m6809/arithmetic.rb +140 -0
  30. data/lib/badline/chips/m6809/bus_steps.rb +223 -0
  31. data/lib/badline/chips/m6809/core.rb +149 -0
  32. data/lib/badline/chips/m6809/indexed.rb +82 -0
  33. data/lib/badline/chips/m6809/interrupts.rb +159 -0
  34. data/lib/badline/chips/m6809/keyed_state.rb +147 -0
  35. data/lib/badline/chips/m6809/opcodes.rb +159 -0
  36. data/lib/badline/chips/m6809/operations.rb +258 -0
  37. data/lib/badline/chips/m6809/plans.rb +143 -0
  38. data/lib/badline/chips/m6809/registers.rb +82 -0
  39. data/lib/badline/chips/m6809/stack.rb +140 -0
  40. data/lib/badline/chips/m6809.rb +13 -0
  41. data/lib/badline/chips/riot/timer.rb +14 -3
  42. data/lib/badline/chips/rriot.rb +8 -3
  43. data/lib/badline/chips/sid/decimator.rb +1 -1
  44. data/lib/badline/chips/sid/filter.rb +4 -4
  45. data/lib/badline/chips/sid/keyed_parts.rb +51 -18
  46. data/lib/badline/chips/sid/waveform.rb +6 -6
  47. data/lib/badline/chips/sid.rb +6 -2
  48. data/lib/badline/chips/vic_i/keyed_parts.rb +12 -2
  49. data/lib/badline/chips/vic_i/light_pen.rb +89 -0
  50. data/lib/badline/chips/vic_i.rb +91 -23
  51. data/lib/badline/data_path.rb +3 -3
  52. data/lib/badline/drives/ieee_models.rb +54 -0
  53. data/lib/badline/drives/model1001.rb +37 -0
  54. data/lib/badline/drives/model1541/sector_reader.rb +20 -3
  55. data/lib/badline/drives/model1541.rb +1 -0
  56. data/lib/badline/drives/model1551.rb +1 -0
  57. data/lib/badline/drives/model1571.rb +1 -0
  58. data/lib/badline/drives/model1581.rb +1 -0
  59. data/lib/badline/drives/model2031.rb +7 -0
  60. data/lib/badline/drives/model4040/disk.rb +62 -0
  61. data/lib/badline/drives/model4040/ip_bus.rb +46 -0
  62. data/lib/badline/drives/model4040/keyed_state.rb +57 -0
  63. data/lib/badline/drives/model4040/mechanism.rb +195 -0
  64. data/lib/badline/drives/model4040/parts.rb +20 -0
  65. data/lib/badline/drives/model4040.rb +195 -0
  66. data/lib/badline/drives/model8050/board.rb +79 -0
  67. data/lib/badline/drives/model8050/dos_board.rb +134 -0
  68. data/lib/badline/drives/model8050/fdc_bus.rb +7 -0
  69. data/lib/badline/drives/model8050/idle.rb +148 -31
  70. data/lib/badline/drives/model8050/keyed_state.rb +5 -2
  71. data/lib/badline/drives/model8050.rb +32 -130
  72. data/lib/badline/drives/model9060/idle.rb +169 -0
  73. data/lib/badline/drives/model9060/keyed_state.rb +123 -0
  74. data/lib/badline/drives/model9060/parts.rb +98 -0
  75. data/lib/badline/drives/model9060/sasi_bus.rb +88 -0
  76. data/lib/badline/drives/model9060/sasi_commands.rb +168 -0
  77. data/lib/badline/drives/model9060/sasi_target.rb +161 -0
  78. data/lib/badline/drives/model9060.rb +197 -0
  79. data/lib/badline/frontend/app.rb +8 -0
  80. data/lib/badline/frontend/boot.rb +3 -2
  81. data/lib/badline/frontend/controls.rb +62 -6
  82. data/lib/badline/frontend/keys.rb +36 -0
  83. data/lib/badline/frontend/kim1_panel.rb +208 -0
  84. data/lib/badline/frontend/media_slots.rb +8 -1
  85. data/lib/badline/frontend/menu/drive_types.rb +56 -0
  86. data/lib/badline/frontend/menu/menu_dialogs.rb +61 -14
  87. data/lib/badline/frontend/menu/menu_media.rb +42 -12
  88. data/lib/badline/frontend/menu/menu_pages.rb +97 -2
  89. data/lib/badline/frontend/menu/menu_ports.rb +38 -1
  90. data/lib/badline/frontend/menu/pause_menu.rb +5 -1
  91. data/lib/badline/frontend/pet_keyboard.rb +23 -1
  92. data/lib/badline/frontend/screen.rb +1 -1
  93. data/lib/badline/frontend/screens.rb +40 -2
  94. data/lib/badline/frontend/teletype_pane.rb +152 -0
  95. data/lib/badline/frontend/timeline.rb +31 -2
  96. data/lib/badline/frontend.rb +3 -0
  97. data/lib/badline/input/joystick.rb +15 -2
  98. data/lib/badline/kim1/bus.rb +117 -0
  99. data/lib/badline/kim1/cpu.rb +25 -0
  100. data/lib/badline/kim1/host_terminal.rb +74 -0
  101. data/lib/badline/kim1/key_entry.rb +74 -0
  102. data/lib/badline/kim1/keyed_modules.rb +53 -0
  103. data/lib/badline/kim1/keyed_state.rb +133 -0
  104. data/lib/badline/kim1/panel.rb +176 -0
  105. data/lib/badline/kim1/rriots.rb +28 -0
  106. data/lib/badline/kim1/sound.rb +29 -0
  107. data/lib/badline/kim1/tape.rb +118 -0
  108. data/lib/badline/kim1/teletype.rb +193 -0
  109. data/lib/badline/kim1.rb +194 -0
  110. data/lib/badline/machine/ieee_drives.rb +94 -117
  111. data/lib/badline/machine/keyboard_buffer.rb +20 -3
  112. data/lib/badline/machine/line_sound.rb +122 -0
  113. data/lib/badline/machine.rb +49 -15
  114. data/lib/badline/media/extensions.rb +8 -4
  115. data/lib/badline/media/kim1_media.rb +150 -0
  116. data/lib/badline/media/pet_media.rb +3 -1
  117. data/lib/badline/media/true_drive.rb +52 -45
  118. data/lib/badline/media/vic20_media.rb +4 -2
  119. data/lib/badline/media.rb +16 -6
  120. data/lib/badline/memory.rb +2 -4
  121. data/lib/badline/options/family.rb +98 -8
  122. data/lib/badline/options/help.rb +44 -12
  123. data/lib/badline/options/table.rb +44 -12
  124. data/lib/badline/options/validation.rb +44 -8
  125. data/lib/badline/options.rb +27 -8
  126. data/lib/badline/pet/bus.rb +16 -2
  127. data/lib/badline/pet/keyboard_ports.rb +9 -2
  128. data/lib/badline/pet/keyed_state.rb +4 -0
  129. data/lib/badline/pet/model.rb +11 -2
  130. data/lib/badline/pet/rom_sets.rb +1 -0
  131. data/lib/badline/pet/sound.rb +5 -105
  132. data/lib/badline/pet/superpet/acia.rb +12 -0
  133. data/lib/badline/pet/superpet/board.rb +221 -0
  134. data/lib/badline/pet/superpet/dongle.rb +92 -0
  135. data/lib/badline/pet/superpet/keyed_modules.rb +63 -0
  136. data/lib/badline/pet/superpet/keyed_parts.rb +76 -0
  137. data/lib/badline/pet/superpet/m6809.rb +32 -0
  138. data/lib/badline/pet/superpet/waterloo_traps.rb +199 -0
  139. data/lib/badline/pet/superpet.rb +221 -0
  140. data/lib/badline/pet.rb +23 -3
  141. data/lib/badline/plus4/keyed_state.rb +2 -0
  142. data/lib/badline/plus4.rb +5 -0
  143. data/lib/badline/roms/1001/fdc-251257-02a.rom +0 -0
  144. data/lib/badline/roms/4040/dos-1.2.rom +0 -0
  145. data/lib/badline/roms/4040/dos-2.1-rev.rom +0 -0
  146. data/lib/badline/roms/4040/dos-2.1.rom +0 -0
  147. data/lib/badline/roms/4040/fdc-901466-02.rom +0 -0
  148. data/lib/badline/roms/4040/fdc-901466-04.rom +0 -0
  149. data/lib/badline/roms/9060/dos-3.0.rom +0 -0
  150. data/lib/badline/roms/9060/sasi-300515-revb.rom +0 -0
  151. data/lib/badline/roms/README +176 -0
  152. data/lib/badline/roms/kim1/6530-002.rom +0 -0
  153. data/lib/badline/roms/kim1/6530-003.rom +0 -0
  154. data/lib/badline/roms/pet/characters-superpet.rom +0 -0
  155. data/lib/badline/roms/pet/superpet-a000.rom +0 -0
  156. data/lib/badline/roms/pet/superpet-c000.rom +0 -0
  157. data/lib/badline/roms/pet/superpet-e000-50hz.rom +0 -0
  158. data/lib/badline/roms/pet/superpet-e000.rom +0 -0
  159. data/lib/badline/snapshot/container.rb +8 -4
  160. data/lib/badline/snapshot/field_doc/markdown.rb +1 -1
  161. data/lib/badline/snapshot/field_doc.rb +35 -10
  162. data/lib/badline/snapshot/image.rb +19 -7
  163. data/lib/badline/snapshot/kim1_setup.rb +28 -0
  164. data/lib/badline/snapshot/pet_setup.rb +27 -11
  165. data/lib/badline/snapshot/storage_setup.rb +4 -1
  166. data/lib/badline/snapshot/vic20_setup.rb +11 -7
  167. data/lib/badline/snapshot/vice/pet.rb +14 -7
  168. data/lib/badline/snapshot/vice/pet_cpu.rb +39 -5
  169. data/lib/badline/snapshot/vice/pet_crtc.rb +15 -2
  170. data/lib/badline/snapshot/vice/pet_dongle.rb +72 -0
  171. data/lib/badline/snapshot/vice/pet_drive8050.rb +188 -68
  172. data/lib/badline/snapshot/vice/pet_mem.rb +55 -5
  173. data/lib/badline/snapshot/vice/pet_restore.rb +40 -11
  174. data/lib/badline/snapshot/vice/vic20.rb +70 -0
  175. data/lib/badline/snapshot/vice/vic20_cpu.rb +61 -0
  176. data/lib/badline/snapshot/vice/vic20_mem.rb +85 -0
  177. data/lib/badline/snapshot/vice/vic20_restore.rb +124 -0
  178. data/lib/badline/snapshot/vice/vic20_vici.rb +200 -0
  179. data/lib/badline/snapshot/vice.rb +1 -0
  180. data/lib/badline/snapshot.rb +8 -4
  181. data/lib/badline/status.rb +30 -30
  182. data/lib/badline/storage/d67_image.rb +55 -0
  183. data/lib/badline/storage/d90_image.rb +180 -0
  184. data/lib/badline/storage/kim_tape.rb +254 -0
  185. data/lib/badline/storage/papertape.rb +90 -0
  186. data/lib/badline/storage.rb +4 -0
  187. data/lib/badline/support/kim1_checkpoint.rb +41 -0
  188. data/lib/badline/support/superpet_checkpoint.rb +35 -0
  189. data/lib/badline/version.rb +1 -1
  190. data/lib/badline/vic20/keyed_modules.rb +6 -3
  191. data/lib/badline/vic20/keyed_state.rb +6 -4
  192. data/lib/badline/vic20/port_wiring.rb +11 -0
  193. data/lib/badline/vic20.rb +30 -12
  194. data/lib/badline.rb +1 -0
  195. metadata +85 -1
@@ -0,0 +1,223 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ module Chips
5
+ class M6809
6
+ PREFIX_PLAN = %i[fetch_paged].freeze
7
+
8
+ # The steps that fetch opcodes and operands, work out direct and
9
+ # extended addresses and branch targets, and read and write the
10
+ # operand, one bus cycle each.
11
+ module BusSteps
12
+ private
13
+
14
+ def read_pc
15
+ value = @bus.read(@pc)
16
+ @pc = (@pc + 1) & 0xffff
17
+ value
18
+ end
19
+
20
+ def fetch
21
+ return halted_cycle if @halt_seen
22
+ return take_interrupt if interrupt_due?
23
+
24
+ opcode = read_pc
25
+ @opcode = opcode
26
+ case opcode
27
+ when 0x10
28
+ @page = PAGE2
29
+ switch_to(PREFIX_PLAN)
30
+ when 0x11
31
+ @page = PAGE3
32
+ switch_to(PREFIX_PLAN)
33
+ else start(PAGE1[opcode])
34
+ end
35
+ end
36
+
37
+ # The opcode after a prefix. A further prefix only costs a cycle.
38
+ def fetch_paged
39
+ opcode = read_pc
40
+ return @index -= 1 if [0x10, 0x11].include?(opcode)
41
+
42
+ @opcode = opcode
43
+ start(@page[opcode])
44
+ end
45
+
46
+ def dead_pc = @bus.read(@pc)
47
+
48
+ def dead_pc_next = @bus.read((@pc + 1) & 0xffff)
49
+
50
+ def dead_pc_next2 = @bus.read((@pc + 2) & 0xffff)
51
+
52
+ def dead_v = @bus.read(0xffff)
53
+
54
+ def dead_ea = @bus.read(@ea)
55
+
56
+ # A cycle without a bus access.
57
+ def idle; end
58
+
59
+ def dead_pc_exec
60
+ dead_pc
61
+ execute
62
+ end
63
+
64
+ # A dead read of the byte after the opcode, which the undocumented
65
+ # $18 uses.
66
+ def next_byte_exec
67
+ @value = @bus.read(@pc)
68
+ execute
69
+ end
70
+
71
+ def dead_v_exec
72
+ dead_v
73
+ execute
74
+ end
75
+
76
+ def direct
77
+ @ea = (@dp << 8) | read_pc
78
+ end
79
+
80
+ def extended_hi
81
+ @ea = read_pc << 8
82
+ end
83
+
84
+ def extended_lo
85
+ @ea |= read_pc
86
+ end
87
+
88
+ def immediate_exec
89
+ @value = read_pc
90
+ execute
91
+ end
92
+
93
+ def immediate_hi
94
+ @value = read_pc << 8
95
+ end
96
+
97
+ def immediate_lo_exec
98
+ @value |= read_pc
99
+ execute
100
+ end
101
+
102
+ def immediate_discard = read_pc
103
+
104
+ # The undocumented 16-bit stores immediate write the register's low
105
+ # byte over the operand's.
106
+ def store_immediate
107
+ execute
108
+ @bus.write(@pc, @value)
109
+ @pc = (@pc + 1) & 0xffff
110
+ end
111
+
112
+ def read_post
113
+ @post = read_pc
114
+ end
115
+
116
+ def read_exec
117
+ @value = @bus.read(@ea)
118
+ execute
119
+ end
120
+
121
+ def read_hi
122
+ @value = @bus.read(@ea) << 8
123
+ end
124
+
125
+ def read_lo_exec
126
+ @value |= @bus.read((@ea + 1) & 0xffff)
127
+ execute
128
+ end
129
+
130
+ def write_exec
131
+ execute
132
+ @bus.write(@ea, @value)
133
+ end
134
+
135
+ def write_hi_exec
136
+ execute
137
+ @bus.write(@ea, @value >> 8)
138
+ end
139
+
140
+ def write_lo
141
+ @bus.write((@ea + 1) & 0xffff, @value & 0xff)
142
+ end
143
+
144
+ def rmw_read
145
+ @value = @bus.read(@ea)
146
+ execute
147
+ end
148
+
149
+ def rmw_write
150
+ @bus.write(@ea, @value)
151
+ end
152
+
153
+ # A short branch's offset, and the target it gives.
154
+ def offset8_branch
155
+ offset = read_pc
156
+ @ea = (@pc + offset - ((offset & 0x80) * 2)) & 0xffff
157
+ end
158
+
159
+ def offset_hi
160
+ @value = read_pc << 8
161
+ end
162
+
163
+ # The low byte of a 16-bit offset from PC, and its target.
164
+ def offset_lo_pc
165
+ @value |= read_pc
166
+ @ea = (@pc + @value) & 0xffff
167
+ end
168
+
169
+ def dead_v_branch
170
+ dead_v
171
+ @pc = @ea if condition?(@opcode)
172
+ end
173
+
174
+ # A long branch taken takes one more cycle.
175
+ def dead_v_long_branch
176
+ dead_v
177
+ switch_to(LONG_BRANCH_TAKEN_PLAN) if condition?(@opcode)
178
+ end
179
+
180
+ def dead_v_take
181
+ dead_v
182
+ @pc = @ea
183
+ end
184
+
185
+ # The undocumented $14, $15 and $CD read on from PC, a byte a cycle,
186
+ # until reset.
187
+ def free_run
188
+ read_pc
189
+ @index -= 1
190
+ end
191
+
192
+ def condition?(opcode)
193
+ cc = @cc
194
+ case opcode & 0x0f
195
+ when 0x0 then true
196
+ when 0x1 then false
197
+ when 0x2 then cc.nobits?(CARRY | ZERO)
198
+ when 0x3 then cc.anybits?(CARRY | ZERO)
199
+ when 0x4 then cc.nobits?(CARRY)
200
+ when 0x5 then cc.anybits?(CARRY)
201
+ when 0x6 then cc.nobits?(ZERO)
202
+ when 0x7 then cc.anybits?(ZERO)
203
+ when 0x8 then cc.nobits?(OVERFLOW)
204
+ when 0x9 then cc.anybits?(OVERFLOW)
205
+ when 0xa then cc.nobits?(NEGATIVE)
206
+ when 0xb then cc.anybits?(NEGATIVE)
207
+ else signed_condition?(opcode, cc)
208
+ end
209
+ end
210
+
211
+ def signed_condition?(opcode, cc)
212
+ less = cc.anybits?(NEGATIVE) != cc.anybits?(OVERFLOW)
213
+ case opcode & 0x0f
214
+ when 0xc then !less
215
+ when 0xd then less
216
+ when 0xe then !less && cc.nobits?(ZERO)
217
+ else less || cc.anybits?(ZERO)
218
+ end
219
+ end
220
+ end
221
+ end
222
+ end
223
+ end
@@ -0,0 +1,149 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "badline/chips/m6809/opcodes"
4
+ require "badline/chips/m6809/registers"
5
+ require "badline/chips/m6809/bus_steps"
6
+ require "badline/chips/m6809/indexed"
7
+ require "badline/chips/m6809/stack"
8
+ require "badline/chips/m6809/interrupts"
9
+ require "badline/chips/m6809/arithmetic"
10
+ require "badline/chips/m6809/operations"
11
+ require "badline/chips/m6809/keyed_state"
12
+
13
+ module Badline
14
+ module Chips
15
+ class M6809
16
+ # The Motorola 6809E, one bus cycle per #cycle!, on whatever bus +bus+
17
+ # is. The bus answers read(address) and write(address, value). Dead
18
+ # cycles are reads like any other, of the addresses Figure 17 of the
19
+ # MC6809E datasheet gives them, and the cycles the CPU leaves the bus
20
+ # alone (SYNC's, and HALT's) call nothing.
21
+ #
22
+ # Each instruction is a plan of steps, one a cycle, as the 6502 core
23
+ # runs them: the opcode fetch picks an Entry, whose address steps
24
+ # run first and whose tail follows.
25
+ module Core
26
+ include Registers
27
+ include BusSteps
28
+ include Indexed
29
+ include Stack
30
+ include Interrupts
31
+ include Arithmetic
32
+ include Operations
33
+ include KeyedState
34
+
35
+ attr_reader :bus, :cycles
36
+
37
+ def initialize(bus)
38
+ @bus = bus
39
+ @cycles = 0
40
+ @a = @b = @dp = 0
41
+ @x = @y = @u = @s = @pc = 0
42
+ @cc = IRQ_MASK | FIRQ_MASK
43
+ @ea = @value = @post = @opcode = 0
44
+ @group = @previous_group = GROUP_OTHER
45
+ @alu_word = 0
46
+ @vector = 0xfffe
47
+ @interrupt_mask = 0
48
+ @stack_u = false
49
+ @operation = :none
50
+ @page = PAGE1
51
+ @tail = NO_TAIL
52
+ clear_interrupts
53
+ reset!
54
+ end
55
+
56
+ # RESET going high: the CPU masks FIRQ and IRQ, clears DP, disarms
57
+ # NMI until S is loaded, and reads its vector from $FFFE.
58
+ def reset!
59
+ @cc |= IRQ_MASK | FIRQ_MASK
60
+ @dp = 0
61
+ @nmi_armed = false
62
+ clear_interrupts
63
+ @tail = NO_TAIL
64
+ @plan = RESET_PLAN
65
+ @index = 0
66
+ end
67
+
68
+ # Drops the instruction in flight so the next cycle fetches an
69
+ # opcode at PC, and sets the cycle count.
70
+ def resume(cycles)
71
+ @cycles = cycles
72
+ clear_interrupts
73
+ boundary
74
+ end
75
+
76
+ def cycle!
77
+ index = @index
78
+ @index = index + 1
79
+ send(@plan[index])
80
+ @cycles += 1
81
+ next_plan if @index >= @plan.length
82
+ sample_lines
83
+ nil
84
+ end
85
+
86
+ # Runs to the end of the instruction or interrupt sequence.
87
+ def step!
88
+ cycle!
89
+ cycle! until boundary? || waiting?
90
+ nil
91
+ end
92
+
93
+ # Whether the next cycle fetches an opcode, or takes an interrupt.
94
+ def boundary? = @index.zero? && @plan.equal?(FETCH_PLAN)
95
+
96
+ def inspect
97
+ format("Cycles: %<cycles>d, PC: %<pc>04x, S: %<s>04x, U: %<u>04x, X: %<x>04x, Y: %<y>04x, " \
98
+ "A: %<a>02x, B: %<b>02x, DP: %<dp>02x, CC: %<cc>02x",
99
+ cycles: @cycles, pc: @pc, s: @s, u: @u, x: @x, y: @y, a: @a, b: @b, dp: @dp, cc: @cc)
100
+ end
101
+
102
+ private
103
+
104
+ def boundary
105
+ @plan = FETCH_PLAN
106
+ @index = 0
107
+ @tail = NO_TAIL
108
+ end
109
+
110
+ # Starts an opcode's plan after its fetch.
111
+ def start(entry)
112
+ @operation = entry.operation
113
+ @previous_group = @group
114
+ @group = entry.group
115
+ @stack_u = false
116
+ plan = entry.plan
117
+ if plan.empty?
118
+ @tail = NO_TAIL
119
+ switch_to(entry.tail)
120
+ else
121
+ @tail = entry.tail
122
+ @plan = plan
123
+ @index = 0
124
+ end
125
+ end
126
+
127
+ # Replaces the rest of the plan.
128
+ def switch_to(plan)
129
+ @plan = plan
130
+ @index = 0
131
+ end
132
+
133
+ def next_plan
134
+ tail = @tail
135
+ return boundary if tail.equal?(NO_TAIL)
136
+
137
+ @tail = NO_TAIL
138
+ return switch_to(tail) unless tail.empty?
139
+
140
+ execute
141
+ boundary
142
+ end
143
+
144
+ # Runs the instruction method of the opcode in flight.
145
+ def execute = send(@operation)
146
+ end
147
+ end
148
+ end
149
+ end
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ module Chips
5
+ class M6809
6
+ # Indexed addressing: the postbyte, and the steps INDEXED_PLANS run
7
+ # after it.
8
+ module Indexed
9
+ private
10
+
11
+ # Reads the postbyte, works out every address that needs no more
12
+ # bytes, and moves on to the postbyte's plan. The undefined
13
+ # postbytes $x7, $xA and $xE address $0000 and take no cycles.
14
+ def postbyte
15
+ post = read_pc
16
+ @post = post
17
+ if post < 0x80
18
+ @ea = (index_register + (post & 0x0f) - (post & 0x10)) & 0xffff
19
+ else
20
+ register_address(post & 0x0f)
21
+ end
22
+ plan = INDEXED_PLANS[post]
23
+ return next_plan if plan.empty?
24
+
25
+ switch_to(plan)
26
+ end
27
+
28
+ def register_address(mode)
29
+ case mode
30
+ when 0x0 then post_increment(1)
31
+ when 0x1 then post_increment(2)
32
+ when 0x2 then pre_decrement(1)
33
+ when 0x3 then pre_decrement(2)
34
+ when 0x4 then @ea = index_register
35
+ when 0x5 then @ea = (index_register + signed8(@b)) & 0xffff
36
+ when 0x6 then @ea = (index_register + signed8(@a)) & 0xffff
37
+ when 0xb then @ea = (index_register + d) & 0xffff
38
+ when 0x7, 0xa, 0xe then @ea = 0
39
+ end
40
+ end
41
+
42
+ def post_increment(amount)
43
+ @ea = index_register
44
+ self.index_register = (@ea + amount) & 0xffff
45
+ end
46
+
47
+ def pre_decrement(amount)
48
+ @ea = (index_register - amount) & 0xffff
49
+ self.index_register = @ea
50
+ end
51
+
52
+ def signed8(value) = value - ((value & 0x80) << 1)
53
+
54
+ def offset8
55
+ @ea = (index_register + signed8(read_pc)) & 0xffff
56
+ end
57
+
58
+ def offset8_pc
59
+ offset = signed8(read_pc)
60
+ @ea = (@pc + offset) & 0xffff
61
+ end
62
+
63
+ def offset_lo_register
64
+ @value |= read_pc
65
+ @ea = (index_register + @value) & 0xffff
66
+ end
67
+
68
+ def offset_lo_absolute
69
+ @ea = @value | read_pc
70
+ end
71
+
72
+ def indirect_hi
73
+ @value = @bus.read(@ea) << 8
74
+ end
75
+
76
+ def indirect_lo
77
+ @ea = @value | @bus.read((@ea + 1) & 0xffff)
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,159 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ module Chips
5
+ class M6809
6
+ # The IRQ, FIRQ, NMI and HALT lines, SYNC and CWAI.
7
+ #
8
+ # The CPU samples each line once a cycle, and an instruction boundary
9
+ # sees the sample of two cycles before: a line asserted on the
10
+ # instruction's next-to-last cycle is taken after it, as in Figures 8
11
+ # and 9 of the MC6809E datasheet. NMI is an edge, latched until taken,
12
+ # and ignored until a program loads S after reset.
13
+ module Interrupts
14
+ attr_reader :irq, :firq, :nmi, :halt
15
+
16
+ # Asserts (true) or releases the IRQ line.
17
+ def irq=(asserted)
18
+ @irq = asserted
19
+ end
20
+
21
+ def firq=(asserted)
22
+ @firq = asserted
23
+ end
24
+
25
+ def nmi=(asserted)
26
+ @nmi_edge = true if asserted && !@nmi && @nmi_armed
27
+ @nmi = asserted
28
+ end
29
+
30
+ # The HALT line: the CPU stops at the next instruction boundary and
31
+ # leaves the bus alone until it is released.
32
+ def halt=(asserted)
33
+ @halt = asserted
34
+ end
35
+
36
+ def halted? = boundary? && @halt_seen
37
+
38
+ # Whether the CPU is waiting in SYNC or CWAI, or running free.
39
+ def waiting?
40
+ step = @plan[@index]
41
+ step == :sync_wait || step == :cwai_wait || step == :free_run || halted?
42
+ end
43
+
44
+ private
45
+
46
+ def clear_interrupts
47
+ @irq = @firq = @nmi = @halt = false
48
+ @irq_latched = @irq_seen = false
49
+ @firq_latched = @firq_seen = false
50
+ @nmi_edge = @nmi_latched = @nmi_seen = false
51
+ @halt_latched = @halt_seen = false
52
+ end
53
+
54
+ def sample_lines
55
+ @irq_seen = @irq_latched
56
+ @irq_latched = @irq
57
+ @firq_seen = @firq_latched
58
+ @firq_latched = @firq
59
+ @nmi_seen = @nmi_latched
60
+ @nmi_latched = @nmi_edge
61
+ @halt_seen = @halt_latched
62
+ @halt_latched = @halt
63
+ end
64
+
65
+ def interrupt_due?
66
+ @nmi_seen || (@firq_seen && @cc.nobits?(FIRQ_MASK)) || (@irq_seen && @cc.nobits?(IRQ_MASK))
67
+ end
68
+
69
+ # The cycle that would have fetched an opcode reads it and drops it,
70
+ # and the interrupt's sequence follows.
71
+ def take_interrupt
72
+ dead_pc
73
+ @stack_u = false
74
+ if @nmi_seen
75
+ @operation = :enter_nmi
76
+ switch_to(INTERRUPT_PLAN)
77
+ elsif @firq_seen && @cc.nobits?(FIRQ_MASK)
78
+ @operation = :enter_firq
79
+ switch_to(FAST_INTERRUPT_PLAN)
80
+ else
81
+ @operation = :enter_irq
82
+ switch_to(INTERRUPT_PLAN)
83
+ end
84
+ end
85
+
86
+ def enter_nmi
87
+ clear_nmi
88
+ @cc |= ENTIRE
89
+ interrupt_vector(0xfffc, IRQ_MASK | FIRQ_MASK)
90
+ end
91
+
92
+ def enter_firq
93
+ @cc &= ~ENTIRE & 0xff
94
+ interrupt_vector(0xfff6, IRQ_MASK | FIRQ_MASK)
95
+ end
96
+
97
+ def enter_irq
98
+ @cc |= ENTIRE
99
+ interrupt_vector(0xfff8, IRQ_MASK)
100
+ end
101
+
102
+ def interrupt_vector(vector, mask)
103
+ @vector = vector
104
+ @interrupt_mask = mask
105
+ end
106
+
107
+ def clear_nmi
108
+ @nmi_edge = @nmi_latched = @nmi_seen = false
109
+ end
110
+
111
+ def reset_dead = @bus.read(0xfffe)
112
+
113
+ def reset_hi
114
+ @value = @bus.read(0xfffe) << 8
115
+ end
116
+
117
+ def reset_lo
118
+ @pc = @value | @bus.read(0xffff)
119
+ end
120
+
121
+ def halted_cycle
122
+ @index = 0
123
+ end
124
+
125
+ # SYNC leaves the bus alone until any interrupt line is asserted,
126
+ # masked or not, then for one more cycle.
127
+ def sync_wait
128
+ if @nmi_latched || @firq_latched || @irq_latched
129
+ switch_to(SYNC_END_PLAN)
130
+ else
131
+ @index -= 1
132
+ end
133
+ end
134
+
135
+ # CWAI ANDs CC with its operand and sets E before it stacks.
136
+ def cwai_mask
137
+ @cc = (read_pc & @cc) | ENTIRE
138
+ end
139
+
140
+ # CWAI, all stacked, reads $FFFF until an interrupt it may take
141
+ # comes, and then fetches that interrupt's vector.
142
+ def cwai_wait
143
+ dead_v
144
+ if @nmi_latched
145
+ clear_nmi
146
+ interrupt_vector(0xfffc, IRQ_MASK | FIRQ_MASK)
147
+ elsif @firq_latched && @cc.nobits?(FIRQ_MASK)
148
+ interrupt_vector(0xfff6, IRQ_MASK | FIRQ_MASK)
149
+ elsif @irq_latched && @cc.nobits?(IRQ_MASK)
150
+ interrupt_vector(0xfff8, IRQ_MASK)
151
+ else
152
+ return @index -= 1
153
+ end
154
+ switch_to(WAITED_INTERRUPT_PLAN)
155
+ end
156
+ end
157
+ end
158
+ end
159
+ end