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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +193 -0
- data/CLAUDE.md +19 -7
- data/CONTRIBUTING.md +11 -1
- data/README.md +178 -26
- data/doc/library.md +5 -1
- data/doc/media.md +3 -2
- data/doc/pinned-behaviour.md +169 -1
- data/doc/snapshot-format.md +302 -35
- data/doc/snapshots.md +97 -26
- data/doc/vice.md +344 -0
- data/exe/badline-ruby +5 -0
- data/lib/badline/audio/renderer.rb +4 -8
- data/lib/badline/audio/terminal.rb +34 -2
- data/lib/badline/c128/keyed_state.rb +2 -0
- data/lib/badline/c128.rb +5 -0
- data/lib/badline/c64/bus/fittings.rb +6 -0
- data/lib/badline/c64/keyed_state.rb +2 -0
- data/lib/badline/c64.rb +7 -0
- data/lib/badline/cbm2/keyed_state.rb +4 -0
- data/lib/badline/cbm2/p500.rb +9 -3
- data/lib/badline/cbm2.rb +10 -3
- data/lib/badline/chips/cia/timer.rb +2 -2
- data/lib/badline/chips/cpu/core.rb +29 -4
- data/lib/badline/chips/cpu/instruction_set.rb +1 -1
- data/lib/badline/chips/cpu/interrupts.rb +9 -0
- data/lib/badline/chips/cpu/keyed_state.rb +15 -9
- data/lib/badline/chips/cpu/microcode.rb +10 -0
- data/lib/badline/chips/m6809/arithmetic.rb +140 -0
- data/lib/badline/chips/m6809/bus_steps.rb +223 -0
- data/lib/badline/chips/m6809/core.rb +149 -0
- data/lib/badline/chips/m6809/indexed.rb +82 -0
- data/lib/badline/chips/m6809/interrupts.rb +159 -0
- data/lib/badline/chips/m6809/keyed_state.rb +147 -0
- data/lib/badline/chips/m6809/opcodes.rb +159 -0
- data/lib/badline/chips/m6809/operations.rb +258 -0
- data/lib/badline/chips/m6809/plans.rb +143 -0
- data/lib/badline/chips/m6809/registers.rb +82 -0
- data/lib/badline/chips/m6809/stack.rb +140 -0
- data/lib/badline/chips/m6809.rb +13 -0
- data/lib/badline/chips/riot/timer.rb +14 -3
- data/lib/badline/chips/rriot.rb +8 -3
- data/lib/badline/chips/sid/decimator.rb +1 -1
- data/lib/badline/chips/sid/filter.rb +4 -4
- data/lib/badline/chips/sid/keyed_parts.rb +51 -18
- data/lib/badline/chips/sid/waveform.rb +6 -6
- data/lib/badline/chips/sid.rb +6 -2
- data/lib/badline/chips/vic_i/keyed_parts.rb +12 -2
- data/lib/badline/chips/vic_i/light_pen.rb +89 -0
- data/lib/badline/chips/vic_i.rb +91 -23
- data/lib/badline/data_path.rb +3 -3
- data/lib/badline/drives/ieee_models.rb +54 -0
- data/lib/badline/drives/model1001.rb +37 -0
- data/lib/badline/drives/model1541/sector_reader.rb +20 -3
- data/lib/badline/drives/model1541.rb +1 -0
- data/lib/badline/drives/model1551.rb +1 -0
- data/lib/badline/drives/model1571.rb +1 -0
- data/lib/badline/drives/model1581.rb +1 -0
- data/lib/badline/drives/model2031.rb +7 -0
- data/lib/badline/drives/model4040/disk.rb +62 -0
- data/lib/badline/drives/model4040/ip_bus.rb +46 -0
- data/lib/badline/drives/model4040/keyed_state.rb +57 -0
- data/lib/badline/drives/model4040/mechanism.rb +195 -0
- data/lib/badline/drives/model4040/parts.rb +20 -0
- data/lib/badline/drives/model4040.rb +195 -0
- data/lib/badline/drives/model8050/board.rb +79 -0
- data/lib/badline/drives/model8050/dos_board.rb +134 -0
- data/lib/badline/drives/model8050/fdc_bus.rb +7 -0
- data/lib/badline/drives/model8050/idle.rb +148 -31
- data/lib/badline/drives/model8050/keyed_state.rb +5 -2
- data/lib/badline/drives/model8050.rb +32 -130
- data/lib/badline/drives/model9060/idle.rb +169 -0
- data/lib/badline/drives/model9060/keyed_state.rb +123 -0
- data/lib/badline/drives/model9060/parts.rb +98 -0
- data/lib/badline/drives/model9060/sasi_bus.rb +88 -0
- data/lib/badline/drives/model9060/sasi_commands.rb +168 -0
- data/lib/badline/drives/model9060/sasi_target.rb +161 -0
- data/lib/badline/drives/model9060.rb +197 -0
- data/lib/badline/frontend/app.rb +8 -0
- data/lib/badline/frontend/boot.rb +3 -2
- data/lib/badline/frontend/controls.rb +62 -6
- data/lib/badline/frontend/keys.rb +36 -0
- data/lib/badline/frontend/kim1_panel.rb +208 -0
- data/lib/badline/frontend/media_slots.rb +8 -1
- data/lib/badline/frontend/menu/drive_types.rb +56 -0
- data/lib/badline/frontend/menu/menu_dialogs.rb +61 -14
- data/lib/badline/frontend/menu/menu_media.rb +42 -12
- data/lib/badline/frontend/menu/menu_pages.rb +97 -2
- data/lib/badline/frontend/menu/menu_ports.rb +38 -1
- data/lib/badline/frontend/menu/pause_menu.rb +5 -1
- data/lib/badline/frontend/pet_keyboard.rb +23 -1
- data/lib/badline/frontend/screen.rb +1 -1
- data/lib/badline/frontend/screens.rb +40 -2
- data/lib/badline/frontend/teletype_pane.rb +152 -0
- data/lib/badline/frontend/timeline.rb +31 -2
- data/lib/badline/frontend.rb +3 -0
- data/lib/badline/input/joystick.rb +15 -2
- data/lib/badline/kim1/bus.rb +117 -0
- data/lib/badline/kim1/cpu.rb +25 -0
- data/lib/badline/kim1/host_terminal.rb +74 -0
- data/lib/badline/kim1/key_entry.rb +74 -0
- data/lib/badline/kim1/keyed_modules.rb +53 -0
- data/lib/badline/kim1/keyed_state.rb +133 -0
- data/lib/badline/kim1/panel.rb +176 -0
- data/lib/badline/kim1/rriots.rb +28 -0
- data/lib/badline/kim1/sound.rb +29 -0
- data/lib/badline/kim1/tape.rb +118 -0
- data/lib/badline/kim1/teletype.rb +193 -0
- data/lib/badline/kim1.rb +194 -0
- data/lib/badline/machine/ieee_drives.rb +94 -117
- data/lib/badline/machine/keyboard_buffer.rb +20 -3
- data/lib/badline/machine/line_sound.rb +122 -0
- data/lib/badline/machine.rb +49 -15
- data/lib/badline/media/extensions.rb +8 -4
- data/lib/badline/media/kim1_media.rb +150 -0
- data/lib/badline/media/pet_media.rb +3 -1
- data/lib/badline/media/true_drive.rb +52 -45
- data/lib/badline/media/vic20_media.rb +4 -2
- data/lib/badline/media.rb +16 -6
- data/lib/badline/memory.rb +2 -4
- data/lib/badline/options/family.rb +98 -8
- data/lib/badline/options/help.rb +44 -12
- data/lib/badline/options/table.rb +44 -12
- data/lib/badline/options/validation.rb +44 -8
- data/lib/badline/options.rb +27 -8
- data/lib/badline/pet/bus.rb +16 -2
- data/lib/badline/pet/keyboard_ports.rb +9 -2
- data/lib/badline/pet/keyed_state.rb +4 -0
- data/lib/badline/pet/model.rb +11 -2
- data/lib/badline/pet/rom_sets.rb +1 -0
- data/lib/badline/pet/sound.rb +5 -105
- data/lib/badline/pet/superpet/acia.rb +12 -0
- data/lib/badline/pet/superpet/board.rb +221 -0
- data/lib/badline/pet/superpet/dongle.rb +92 -0
- data/lib/badline/pet/superpet/keyed_modules.rb +63 -0
- data/lib/badline/pet/superpet/keyed_parts.rb +76 -0
- data/lib/badline/pet/superpet/m6809.rb +32 -0
- data/lib/badline/pet/superpet/waterloo_traps.rb +199 -0
- data/lib/badline/pet/superpet.rb +221 -0
- data/lib/badline/pet.rb +23 -3
- data/lib/badline/plus4/keyed_state.rb +2 -0
- data/lib/badline/plus4.rb +5 -0
- data/lib/badline/roms/1001/fdc-251257-02a.rom +0 -0
- data/lib/badline/roms/4040/dos-1.2.rom +0 -0
- data/lib/badline/roms/4040/dos-2.1-rev.rom +0 -0
- data/lib/badline/roms/4040/dos-2.1.rom +0 -0
- data/lib/badline/roms/4040/fdc-901466-02.rom +0 -0
- data/lib/badline/roms/4040/fdc-901466-04.rom +0 -0
- data/lib/badline/roms/9060/dos-3.0.rom +0 -0
- data/lib/badline/roms/9060/sasi-300515-revb.rom +0 -0
- data/lib/badline/roms/README +176 -0
- data/lib/badline/roms/kim1/6530-002.rom +0 -0
- data/lib/badline/roms/kim1/6530-003.rom +0 -0
- data/lib/badline/roms/pet/characters-superpet.rom +0 -0
- data/lib/badline/roms/pet/superpet-a000.rom +0 -0
- data/lib/badline/roms/pet/superpet-c000.rom +0 -0
- data/lib/badline/roms/pet/superpet-e000-50hz.rom +0 -0
- data/lib/badline/roms/pet/superpet-e000.rom +0 -0
- data/lib/badline/snapshot/container.rb +8 -4
- data/lib/badline/snapshot/field_doc/markdown.rb +1 -1
- data/lib/badline/snapshot/field_doc.rb +35 -10
- data/lib/badline/snapshot/image.rb +19 -7
- data/lib/badline/snapshot/kim1_setup.rb +28 -0
- data/lib/badline/snapshot/pet_setup.rb +27 -11
- data/lib/badline/snapshot/storage_setup.rb +4 -1
- data/lib/badline/snapshot/vic20_setup.rb +11 -7
- data/lib/badline/snapshot/vice/pet.rb +14 -7
- data/lib/badline/snapshot/vice/pet_cpu.rb +39 -5
- data/lib/badline/snapshot/vice/pet_crtc.rb +15 -2
- data/lib/badline/snapshot/vice/pet_dongle.rb +72 -0
- data/lib/badline/snapshot/vice/pet_drive8050.rb +188 -68
- data/lib/badline/snapshot/vice/pet_mem.rb +55 -5
- data/lib/badline/snapshot/vice/pet_restore.rb +40 -11
- data/lib/badline/snapshot/vice/vic20.rb +70 -0
- data/lib/badline/snapshot/vice/vic20_cpu.rb +61 -0
- data/lib/badline/snapshot/vice/vic20_mem.rb +85 -0
- data/lib/badline/snapshot/vice/vic20_restore.rb +124 -0
- data/lib/badline/snapshot/vice/vic20_vici.rb +200 -0
- data/lib/badline/snapshot/vice.rb +1 -0
- data/lib/badline/snapshot.rb +8 -4
- data/lib/badline/status.rb +30 -30
- data/lib/badline/storage/d67_image.rb +55 -0
- data/lib/badline/storage/d90_image.rb +180 -0
- data/lib/badline/storage/kim_tape.rb +254 -0
- data/lib/badline/storage/papertape.rb +90 -0
- data/lib/badline/storage.rb +4 -0
- data/lib/badline/support/kim1_checkpoint.rb +41 -0
- data/lib/badline/support/superpet_checkpoint.rb +35 -0
- data/lib/badline/version.rb +1 -1
- data/lib/badline/vic20/keyed_modules.rb +6 -3
- data/lib/badline/vic20/keyed_state.rb +6 -4
- data/lib/badline/vic20/port_wiring.rb +11 -0
- data/lib/badline/vic20.rb +30 -12
- data/lib/badline.rb +1 -0
- 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
|