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
|
@@ -39,6 +39,7 @@ module Badline
|
|
|
39
39
|
pending = @pending_keys
|
|
40
40
|
out.ints("host.pending_keys", pending) if pending
|
|
41
41
|
out.bool("host.typing", !pending.nil?)
|
|
42
|
+
save_init_keys(out)
|
|
42
43
|
out.int("host.init_pending", @cycles <= init_threshold ? @init_handlers.length : 0)
|
|
43
44
|
out.bool("host.holding_commodore", @holding_commodore)
|
|
44
45
|
@typist.save_fields(out, "host.typist.")
|
|
@@ -55,6 +56,7 @@ module Badline
|
|
|
55
56
|
@cpu_reset_pending = input.bool("cpu_reset_pending", @cpu_reset_pending)
|
|
56
57
|
pending = input.ints("host.pending_keys", [])
|
|
57
58
|
@pending_keys = input.bool("host.typing", !pending.empty?) ? pending.dup : nil
|
|
59
|
+
load_init_keys(input)
|
|
58
60
|
@init_handlers_lost = [input.int("host.init_pending", 0) - @init_handlers.length, 0].max
|
|
59
61
|
load_holding_fields(input)
|
|
60
62
|
@typist.load_fields(input, "host.typist.")
|
data/lib/badline/c128.rb
CHANGED
|
@@ -74,6 +74,8 @@ module Badline
|
|
|
74
74
|
|
|
75
75
|
def family = :c128
|
|
76
76
|
|
|
77
|
+
def portless_board = nil
|
|
78
|
+
|
|
77
79
|
def address_bus = @bus
|
|
78
80
|
|
|
79
81
|
def region = @bus.region
|
|
@@ -124,6 +126,7 @@ module Badline
|
|
|
124
126
|
@holding_commodore = false
|
|
125
127
|
@bus = Bus.new(@model, sid_model: sid_model || @model.sid_model, mode:)
|
|
126
128
|
@cpu = CPU.new(@bus, debug:)
|
|
129
|
+
@cpu.power_on! if @c64_built
|
|
127
130
|
build_z80
|
|
128
131
|
@vic = @bus.vic
|
|
129
132
|
@vic.open_bus = -> { @bus.ram.peek(@cpu.program_counter) }
|
|
@@ -139,6 +142,7 @@ module Badline
|
|
|
139
142
|
@restore_pulse = false
|
|
140
143
|
@init_handlers = []
|
|
141
144
|
@pending_keys = nil
|
|
145
|
+
@init_keys = nil
|
|
142
146
|
@typist = Input::MatrixTypist.new(@bus.keyboard)
|
|
143
147
|
@capture_output = nil
|
|
144
148
|
init_drives
|
|
@@ -203,6 +207,7 @@ module Badline
|
|
|
203
207
|
@vdc.power_on!
|
|
204
208
|
@bus.power_on!
|
|
205
209
|
reset!
|
|
210
|
+
@cpu.power_on! if @c64_built
|
|
206
211
|
enter_c64_mode if @c64_built
|
|
207
212
|
end
|
|
208
213
|
|
|
@@ -15,8 +15,14 @@ module Badline
|
|
|
15
15
|
# alone at $DC00-$DFFF and the cartridge port in Ultimax mode.
|
|
16
16
|
BOARDS = %i[c64 pet64 gs max].freeze
|
|
17
17
|
|
|
18
|
+
# The boards with no serial port for a disk drive: the C64GS's case
|
|
19
|
+
# covers its board's, and the MAX Machine has none.
|
|
20
|
+
NO_SERIAL_PORT = { gs: "the C64GS", max: "the MAX Machine" }.freeze
|
|
21
|
+
|
|
18
22
|
attr_reader :board
|
|
19
23
|
|
|
24
|
+
def portless_board = NO_SERIAL_PORT[@board]
|
|
25
|
+
|
|
20
26
|
# Fits the KERNAL ROM ROMs::KERNALS names, leaves the cassette port
|
|
21
27
|
# empty unless `datasette`, and puts the chips in the board BOARDS
|
|
22
28
|
# names.
|
|
@@ -39,6 +39,7 @@ module Badline
|
|
|
39
39
|
out.bool("dma", @dma)
|
|
40
40
|
pending = @pending_keys
|
|
41
41
|
out.ints("host.pending_keys", pending) if pending
|
|
42
|
+
save_init_keys(out)
|
|
42
43
|
out.int("host.init_pending", booting? ? @init_handlers.length : 0)
|
|
43
44
|
end
|
|
44
45
|
|
|
@@ -52,6 +53,7 @@ module Badline
|
|
|
52
53
|
@dma = input.bool("dma", @dma)
|
|
53
54
|
pending = input.ints("host.pending_keys", [])
|
|
54
55
|
@pending_keys = pending.empty? ? nil : pending.dup
|
|
56
|
+
load_init_keys(input)
|
|
55
57
|
@init_handlers_lost = [input.int("host.init_pending", 0) - @init_handlers.length, 0].max
|
|
56
58
|
end
|
|
57
59
|
|
data/lib/badline/c64.rb
CHANGED
|
@@ -48,6 +48,10 @@ module Badline
|
|
|
48
48
|
|
|
49
49
|
def datasette = address_bus.datasette
|
|
50
50
|
|
|
51
|
+
# The board's name when it has no serial port for a disk drive, or nil.
|
|
52
|
+
# Every family answers it, nil on all but these C64 boards.
|
|
53
|
+
def portless_board = address_bus.portless_board
|
|
54
|
+
|
|
51
55
|
def install_debug_register(&) = address_bus.install_debug_register(&)
|
|
52
56
|
|
|
53
57
|
# The machine options (sid_model:, cia_model:, vic_model:, region: and
|
|
@@ -63,6 +67,7 @@ module Badline
|
|
|
63
67
|
@address_bus = Bus.new(**machine.except(*Bus::Fittings::NAMES))
|
|
64
68
|
@address_bus.fit(**machine.slice(*Bus::Fittings::NAMES))
|
|
65
69
|
@cpu = Chips::CPU.new(@address_bus, debug:)
|
|
70
|
+
@cpu.power_on!
|
|
66
71
|
@vic = @address_bus.vic
|
|
67
72
|
@vic.open_bus = -> { @address_bus.ram.peek(@cpu.program_counter) }
|
|
68
73
|
@cia1 = @address_bus.cia1
|
|
@@ -80,6 +85,7 @@ module Badline
|
|
|
80
85
|
@freeze_writes = 0
|
|
81
86
|
@init_handlers = []
|
|
82
87
|
@pending_keys = nil
|
|
88
|
+
@init_keys = nil
|
|
83
89
|
@dos = nil
|
|
84
90
|
@serial_trap = nil
|
|
85
91
|
@save_trap = nil
|
|
@@ -140,6 +146,7 @@ module Badline
|
|
|
140
146
|
vic.power_on!
|
|
141
147
|
address_bus.power_on!
|
|
142
148
|
reset!
|
|
149
|
+
cpu.power_on!
|
|
143
150
|
end
|
|
144
151
|
|
|
145
152
|
# The RES line reaches the CPU and its port, both CIAs, the SID, the
|
|
@@ -71,7 +71,9 @@ module Badline
|
|
|
71
71
|
out.int("refresh", @refresh)
|
|
72
72
|
pending = @pending_keys
|
|
73
73
|
out.ints("host.pending_keys", pending) if pending
|
|
74
|
+
save_init_keys(out)
|
|
74
75
|
out.int("host.init_pending", @cycles <= @init_threshold ? @init_handlers.length : 0)
|
|
76
|
+
save_ieee_drive_fields(out)
|
|
75
77
|
end
|
|
76
78
|
|
|
77
79
|
def load_machine_fields(input)
|
|
@@ -82,7 +84,9 @@ module Badline
|
|
|
82
84
|
@refresh = input.int("refresh", 0).clamp(0, REFRESH_COUNT)
|
|
83
85
|
pending = input.ints("host.pending_keys", [])
|
|
84
86
|
@pending_keys = pending.empty? ? nil : pending.dup
|
|
87
|
+
load_init_keys(input)
|
|
85
88
|
@init_handlers_lost = [input.int("host.init_pending", 0) - @init_handlers.length, 0].max
|
|
89
|
+
load_ieee_drive_fields(input)
|
|
86
90
|
end
|
|
87
91
|
end
|
|
88
92
|
|
data/lib/badline/cbm2/p500.rb
CHANGED
|
@@ -55,6 +55,8 @@ module Badline
|
|
|
55
55
|
|
|
56
56
|
def family = :cbm2
|
|
57
57
|
|
|
58
|
+
def portless_board = nil
|
|
59
|
+
|
|
58
60
|
def peek(addr) = @bus.peek(addr)
|
|
59
61
|
|
|
60
62
|
def poke(addr, value) = @bus.poke(addr, value)
|
|
@@ -71,8 +73,9 @@ module Badline
|
|
|
71
73
|
def region = @model.region
|
|
72
74
|
|
|
73
75
|
# +model+ names one of Model::ALL, and +mains_hz+ the mains, 50 or
|
|
74
|
-
# 60, the model's region's unless given.
|
|
75
|
-
|
|
76
|
+
# 60, the model's region's unless given. +true_drive_model+ names the
|
|
77
|
+
# drive plug_true_drive plugs in (Drives::IEEEModels).
|
|
78
|
+
def initialize(model: Model::DEFAULT, mains_hz: nil, debug: false, true_drive_model: Drives::IEEEModels::DEFAULT)
|
|
76
79
|
@model = Model.named(model)
|
|
77
80
|
@mains_hz = mains_hz || @model.region.mains_hz
|
|
78
81
|
raise ArgumentError, "the mains is 50 or 60 Hz, not #{@mains_hz}" unless [50, 60].include?(@mains_hz)
|
|
@@ -101,6 +104,7 @@ module Badline
|
|
|
101
104
|
keyboard_ports.connect(@tpi2)
|
|
102
105
|
map_io
|
|
103
106
|
@cpu = CPU.new(@bus, debug:)
|
|
107
|
+
@cpu.power_on!
|
|
104
108
|
@cycles = 0
|
|
105
109
|
@mains_half_period = clock_hz / (2 * @mains_hz)
|
|
106
110
|
@mains_countdown = @mains_half_period
|
|
@@ -112,10 +116,11 @@ module Badline
|
|
|
112
116
|
@acia_due = Chips::ACIA::NEVER
|
|
113
117
|
@init_handlers = []
|
|
114
118
|
@pending_keys = nil
|
|
119
|
+
@init_keys = nil
|
|
115
120
|
@dos = nil
|
|
116
121
|
@ieee_trap = nil
|
|
117
122
|
@capture_output = nil
|
|
118
|
-
init_ieee_drives
|
|
123
|
+
init_ieee_drives(true_drive_model)
|
|
119
124
|
@init_threshold = INIT_THRESHOLD
|
|
120
125
|
update_video_bus
|
|
121
126
|
end
|
|
@@ -209,6 +214,7 @@ module Badline
|
|
|
209
214
|
@color_ram.clear!
|
|
210
215
|
@vic.power_on!
|
|
211
216
|
reset!
|
|
217
|
+
@cpu.power_on!
|
|
212
218
|
end
|
|
213
219
|
|
|
214
220
|
# The cycle by which the KERNAL has tested segments 0 and 1 and BASIC
|
data/lib/badline/cbm2.rb
CHANGED
|
@@ -74,6 +74,8 @@ module Badline
|
|
|
74
74
|
|
|
75
75
|
def family = :cbm2
|
|
76
76
|
|
|
77
|
+
def portless_board = nil
|
|
78
|
+
|
|
77
79
|
# Reads and writes memory as the CPU sees it, in its execution segment.
|
|
78
80
|
def peek(addr) = @bus.peek(addr)
|
|
79
81
|
|
|
@@ -94,8 +96,10 @@ module Badline
|
|
|
94
96
|
# +model+ names one of Model::ALL, +kernal+ one of Model::KERNALS, and
|
|
95
97
|
# +mains_hz+ the mains, 50 or 60, which also sets a 600's jumper for
|
|
96
98
|
# the editor's 60 Hz CRTC table. +refresh+ false leaves out the DRAM
|
|
97
|
-
# refresh's stolen cycles.
|
|
98
|
-
|
|
99
|
+
# refresh's stolen cycles. +true_drive_model+ names the drive
|
|
100
|
+
# plug_true_drive plugs in (Drives::IEEEModels).
|
|
101
|
+
def initialize(model: Model::DEFAULT, kernal: "04a", mains_hz: 50, refresh: true, debug: false,
|
|
102
|
+
true_drive_model: Drives::IEEEModels::DEFAULT)
|
|
99
103
|
@model = Model.named(model)
|
|
100
104
|
raise ArgumentError, "no CBM-II KERNAL named #{kernal}" unless Model::KERNALS.key?(kernal)
|
|
101
105
|
raise ArgumentError, "the mains is 50 or 60 Hz, not #{mains_hz}" unless [50, 60].include?(mains_hz)
|
|
@@ -121,6 +125,7 @@ module Badline
|
|
|
121
125
|
keyboard_ports.connect(@tpi2)
|
|
122
126
|
map_io
|
|
123
127
|
@cpu = CPU.new(@bus, debug:)
|
|
128
|
+
@cpu.power_on!
|
|
124
129
|
@cycles = 0
|
|
125
130
|
@mains_half_period = CLOCK_HZ / (2 * mains_hz)
|
|
126
131
|
@mains_countdown = @mains_half_period
|
|
@@ -131,10 +136,11 @@ module Badline
|
|
|
131
136
|
@acia_due = Chips::ACIA::NEVER
|
|
132
137
|
@init_handlers = []
|
|
133
138
|
@pending_keys = nil
|
|
139
|
+
@init_keys = nil
|
|
134
140
|
@dos = nil
|
|
135
141
|
@ieee_trap = nil
|
|
136
142
|
@capture_output = nil
|
|
137
|
-
init_ieee_drives
|
|
143
|
+
init_ieee_drives(true_drive_model)
|
|
138
144
|
@init_threshold = boot_cycles
|
|
139
145
|
end
|
|
140
146
|
|
|
@@ -221,6 +227,7 @@ module Badline
|
|
|
221
227
|
def power_cycle!
|
|
222
228
|
@bus.power_on!
|
|
223
229
|
reset!
|
|
230
|
+
@cpu.power_on!
|
|
224
231
|
end
|
|
225
232
|
|
|
226
233
|
private
|
|
@@ -52,7 +52,7 @@ module Badline
|
|
|
52
52
|
end
|
|
53
53
|
|
|
54
54
|
def cycle!(feed)
|
|
55
|
-
feed &&= @control.value
|
|
55
|
+
feed &&= @control.value.anybits?(0x01)
|
|
56
56
|
if @settled
|
|
57
57
|
return @counter -= 1 if feed && @counter > 1
|
|
58
58
|
elsif @empty
|
|
@@ -102,7 +102,7 @@ module Badline
|
|
|
102
102
|
private
|
|
103
103
|
|
|
104
104
|
def started?
|
|
105
|
-
@control.value
|
|
105
|
+
@control.value.anybits?(0x01)
|
|
106
106
|
end
|
|
107
107
|
|
|
108
108
|
# Where fast_forward lands +cycles+ after an underflow, with
|
|
@@ -25,8 +25,8 @@ module Badline
|
|
|
25
25
|
include Boundary
|
|
26
26
|
include KeyedState
|
|
27
27
|
|
|
28
|
-
attr_reader :memory, :instructions, :boundary_crossed, :cycles, :pending_write
|
|
29
|
-
attr_accessor :
|
|
28
|
+
attr_reader :memory, :instructions, :boundary_crossed, :cycles, :pending_write, :program_counter
|
|
29
|
+
attr_accessor :stack_pointer, :status, :a, :x, :y, :nmi, :irq
|
|
30
30
|
|
|
31
31
|
alias pending_write? pending_write
|
|
32
32
|
|
|
@@ -56,15 +56,32 @@ module Badline
|
|
|
56
56
|
end_sequence
|
|
57
57
|
end
|
|
58
58
|
|
|
59
|
+
# Clears the registers and runs the reset sequence from its first
|
|
60
|
+
# stack read, the two reads at the program counter having run while
|
|
61
|
+
# the reset circuit held RES. The first opcode fetch comes 5 cycles
|
|
62
|
+
# after power-on, where x64sc's does.
|
|
63
|
+
def power_on!
|
|
64
|
+
@program_counter = @stack_pointer = 0
|
|
65
|
+
@a = @x = @y = 0
|
|
66
|
+
reset!
|
|
67
|
+
@index = 2
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Starts the reset sequence, RESET_PLAN, which reads the program
|
|
71
|
+
# counter from $FFFC and fetches the first opcode 7 cycles later. A,
|
|
72
|
+
# X and Y keep their values, and S ends 3 lower.
|
|
59
73
|
def reset!
|
|
60
74
|
status.interrupt = true
|
|
61
|
-
reset_registers
|
|
62
75
|
@nmi = false
|
|
63
76
|
@irq_sample = @irq_pending = false
|
|
64
77
|
@nmi_sample = @nmi_pending = false
|
|
65
78
|
@skip_poll = false
|
|
66
79
|
@pending_write = false
|
|
67
|
-
|
|
80
|
+
@interrupt = 0xfffc
|
|
81
|
+
@brk = false
|
|
82
|
+
@plan = RESET_PLAN
|
|
83
|
+
@writes = RESET_WRITES
|
|
84
|
+
@index = 0
|
|
68
85
|
end
|
|
69
86
|
|
|
70
87
|
# Drops the instruction or interrupt sequence in flight and the
|
|
@@ -83,6 +100,14 @@ module Badline
|
|
|
83
100
|
@pending_write = false
|
|
84
101
|
end
|
|
85
102
|
|
|
103
|
+
# Sets the program counter. A CPU in its reset sequence leaves it,
|
|
104
|
+
# so the next cycle fetches an opcode at +address+ and not at the
|
|
105
|
+
# reset vector's.
|
|
106
|
+
def program_counter=(address)
|
|
107
|
+
@program_counter = address
|
|
108
|
+
end_sequence if @plan.equal?(RESET_PLAN)
|
|
109
|
+
end
|
|
110
|
+
|
|
86
111
|
def p
|
|
87
112
|
status.value
|
|
88
113
|
end
|
|
@@ -82,6 +82,15 @@ module Badline
|
|
|
82
82
|
@nmi = false if @interrupt == 0xfffa
|
|
83
83
|
end
|
|
84
84
|
|
|
85
|
+
def op_reset_dummy
|
|
86
|
+
@memory.peek(@program_counter)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def op_reset_stack
|
|
90
|
+
@memory.peek(stack_address)
|
|
91
|
+
@stack_pointer = (@stack_pointer - 1) & 0xff
|
|
92
|
+
end
|
|
93
|
+
|
|
85
94
|
def op_int_vector_low
|
|
86
95
|
@address = @memory.peek(@interrupt)
|
|
87
96
|
end
|
|
@@ -4,14 +4,17 @@ module Badline
|
|
|
4
4
|
module Chips
|
|
5
5
|
class CPU
|
|
6
6
|
# Where a CPU caught inside an instruction is, as a snapshot names it:
|
|
7
|
-
# an opcode's instruction, an IRQ or NMI sequence, or
|
|
8
|
-
|
|
7
|
+
# an opcode's instruction, an IRQ or NMI sequence, jammed, or the
|
|
8
|
+
# reset sequence.
|
|
9
|
+
POSITIONS = %w[boundary op interrupt jam reset].freeze
|
|
9
10
|
|
|
10
11
|
# The CPU's fields in a keyed snapshot module (Snapshot::Keyed): the
|
|
11
12
|
# registers, the interrupt lines' pipeline, the counts and, inside an
|
|
12
13
|
# instruction, where it is in hardware terms (pos.*), rather than as
|
|
13
14
|
# the microcode lays it out.
|
|
14
15
|
module KeyedState
|
|
16
|
+
SEQUENCE_NAMES = { INTERRUPT => "interrupt", RESET => "reset" }.freeze
|
|
17
|
+
|
|
15
18
|
def save_fields(out)
|
|
16
19
|
out.int("pc", @program_counter).int("sp", @stack_pointer).int("a", @a).int("x", @x).int("y", @y)
|
|
17
20
|
out.int("p", @status.value).bool("nmi", @nmi).bool("so_high", @so_high)
|
|
@@ -75,8 +78,8 @@ module Badline
|
|
|
75
78
|
return
|
|
76
79
|
end
|
|
77
80
|
|
|
78
|
-
out.text("pos.seq", source
|
|
79
|
-
out.int("pos.op", source) unless source
|
|
81
|
+
out.text("pos.seq", SEQUENCE_NAMES.fetch(source, "op"))
|
|
82
|
+
out.int("pos.op", source) unless SEQUENCE_NAMES.key?(source)
|
|
80
83
|
out.int("pos.t", position_t(source)).bool("pos.crossed", @boundary_crossed)
|
|
81
84
|
out.bool("pos.branch_taken", @branch_taken == true).bool("pos.brk", @brk)
|
|
82
85
|
out.optional_int("pos.vector", @interrupt)
|
|
@@ -88,7 +91,7 @@ module Badline
|
|
|
88
91
|
# The cycles of the instruction or sequence run so far, counting the
|
|
89
92
|
# opcode fetch as 1 and leaving out stalled cycles.
|
|
90
93
|
def position_t(source)
|
|
91
|
-
return @index if source
|
|
94
|
+
return @index if SEQUENCE_NAMES.key?(source)
|
|
92
95
|
|
|
93
96
|
MICROCODE.fetch(source).t_at(@index, @boundary_crossed)
|
|
94
97
|
end
|
|
@@ -96,7 +99,8 @@ module Badline
|
|
|
96
99
|
def load_position(input)
|
|
97
100
|
case input.choice("pos.seq", POSITIONS, "boundary")
|
|
98
101
|
when "op" then load_instruction(input)
|
|
99
|
-
when "interrupt" then
|
|
102
|
+
when "interrupt" then load_sequence(input, INTERRUPT_PLAN, INTERRUPT_WRITES, 1)
|
|
103
|
+
when "reset" then load_sequence(input, RESET_PLAN, RESET_WRITES, 0)
|
|
100
104
|
when "jam" then load_jam
|
|
101
105
|
else load_boundary
|
|
102
106
|
end
|
|
@@ -112,10 +116,11 @@ module Badline
|
|
|
112
116
|
load_latches(input)
|
|
113
117
|
end
|
|
114
118
|
|
|
115
|
-
|
|
116
|
-
|
|
119
|
+
# An interrupt or reset sequence, whose plan starts at step +first+.
|
|
120
|
+
def load_sequence(input, plan, writes, first)
|
|
121
|
+
use_plan(plan, writes)
|
|
117
122
|
@boundary_crossed = input.bool("pos.crossed", false)
|
|
118
|
-
@index = input.int("pos.t",
|
|
123
|
+
@index = input.int("pos.t", first).clamp(first, plan.length - 1)
|
|
119
124
|
load_latches(input)
|
|
120
125
|
end
|
|
121
126
|
|
|
@@ -162,6 +167,7 @@ module Badline
|
|
|
162
167
|
return FETCH if @plan.equal?(FETCH_PLAN)
|
|
163
168
|
return INTERRUPT if @plan.equal?(INTERRUPT_PLAN)
|
|
164
169
|
return JAMMED if @plan.equal?(JAMMED_PLAN)
|
|
170
|
+
return RESET if @plan.equal?(RESET_PLAN)
|
|
165
171
|
|
|
166
172
|
MICROCODE.index { |micro| micro.plan.equal?(@plan) && micro.operation == @operation } ||
|
|
167
173
|
MICROCODE.index { |micro| micro.plan.equal?(@plan) }
|
|
@@ -95,6 +95,14 @@ module Badline
|
|
|
95
95
|
op_int_vector_low op_int_vector_high
|
|
96
96
|
].freeze
|
|
97
97
|
|
|
98
|
+
# RES runs BRK's sequence with its three stack writes turned into
|
|
99
|
+
# reads, which still count S down, and reads the vector at $FFFC. Its
|
|
100
|
+
# first two cycles read the program counter and leave it where it was.
|
|
101
|
+
RESET_PLAN = %i[
|
|
102
|
+
op_reset_dummy op_reset_dummy op_reset_stack op_reset_stack op_reset_stack
|
|
103
|
+
op_int_vector_low op_int_vector_high
|
|
104
|
+
].freeze
|
|
105
|
+
|
|
98
106
|
SPECIAL_PLANS = {
|
|
99
107
|
brk: BRK_PLAN, jam: JAM_PLAN, jsr: JSR_PLAN, rts: RTS_PLAN,
|
|
100
108
|
rti: RTI_PLAN, pha: PUSH_PLAN, php: PUSH_PLAN,
|
|
@@ -181,6 +189,7 @@ module Badline
|
|
|
181
189
|
FETCH_WRITES = Microcode.write_mask(FETCH_PLAN)
|
|
182
190
|
INTERRUPT_WRITES = Microcode.write_mask(INTERRUPT_PLAN)
|
|
183
191
|
JAMMED_WRITES = Microcode.write_mask(JAMMED_PLAN)
|
|
192
|
+
RESET_WRITES = Microcode.write_mask(RESET_PLAN)
|
|
184
193
|
MICROCODE = Microcode.table
|
|
185
194
|
|
|
186
195
|
# The instruction methods, in a fixed order a snapshot names them by.
|
|
@@ -190,6 +199,7 @@ module Badline
|
|
|
190
199
|
FETCH = -1
|
|
191
200
|
INTERRUPT = -2
|
|
192
201
|
JAMMED = -3
|
|
202
|
+
RESET = -4
|
|
193
203
|
end
|
|
194
204
|
end
|
|
195
205
|
end
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
module Chips
|
|
5
|
+
class M6809
|
|
6
|
+
# The ALU: each method returns its result and sets the flags it
|
|
7
|
+
# changes.
|
|
8
|
+
module Arithmetic
|
|
9
|
+
private
|
|
10
|
+
|
|
11
|
+
def flags(clear, set)
|
|
12
|
+
@cc = (@cc & ~clear & 0xff) | set
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def nz8(result) = (result.nobits?(0x80) ? 0 : NEGATIVE) | (result.zero? ? ZERO : 0)
|
|
16
|
+
|
|
17
|
+
def nz16(result) = (result.nobits?(0x8000) ? 0 : NEGATIVE) | (result.zero? ? ZERO : 0)
|
|
18
|
+
|
|
19
|
+
# Loads, stores, AND, OR, EOR and TST: N and Z, V cleared.
|
|
20
|
+
def logic8(result)
|
|
21
|
+
flags(NEGATIVE | ZERO | OVERFLOW, nz8(result))
|
|
22
|
+
result
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def logic16(result)
|
|
26
|
+
flags(NEGATIVE | ZERO | OVERFLOW, nz16(result))
|
|
27
|
+
result
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def add8(left, right, carry)
|
|
31
|
+
sum = left + right + carry
|
|
32
|
+
result = sum & 0xff
|
|
33
|
+
overflow = ((left ^ result) & (right ^ result)).nobits?(0x80) ? 0 : OVERFLOW
|
|
34
|
+
half = (left ^ right ^ sum).nobits?(0x10) ? 0 : HALF_CARRY
|
|
35
|
+
flags(HALF_CARRY | NEGATIVE | ZERO | OVERFLOW | CARRY,
|
|
36
|
+
half | nz8(result) | overflow | (sum > 0xff ? CARRY : 0))
|
|
37
|
+
result
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def sub8(left, right, borrow)
|
|
41
|
+
difference = left - right - borrow
|
|
42
|
+
result = difference & 0xff
|
|
43
|
+
overflow = ((left ^ right) & (left ^ result)).nobits?(0x80) ? 0 : OVERFLOW
|
|
44
|
+
flags(NEGATIVE | ZERO | OVERFLOW | CARRY, nz8(result) | overflow | (difference.negative? ? CARRY : 0))
|
|
45
|
+
result
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def add16(left, right)
|
|
49
|
+
sum = left + right
|
|
50
|
+
result = sum & 0xffff
|
|
51
|
+
@alu_word = result
|
|
52
|
+
overflow = ((left ^ result) & (right ^ result)).nobits?(0x8000) ? 0 : OVERFLOW
|
|
53
|
+
flags(NEGATIVE | ZERO | OVERFLOW | CARRY, nz16(result) | overflow | (sum > 0xffff ? CARRY : 0))
|
|
54
|
+
result
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def sub16(left, right)
|
|
58
|
+
difference = left - right
|
|
59
|
+
result = difference & 0xffff
|
|
60
|
+
@alu_word = result
|
|
61
|
+
overflow = ((left ^ right) & (left ^ result)).nobits?(0x8000) ? 0 : OVERFLOW
|
|
62
|
+
flags(NEGATIVE | ZERO | OVERFLOW | CARRY, nz16(result) | overflow | (difference.negative? ? CARRY : 0))
|
|
63
|
+
result
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def carry = @cc & CARRY
|
|
67
|
+
|
|
68
|
+
def neg8(value) = sub8(0, value, 0)
|
|
69
|
+
|
|
70
|
+
def com8(value)
|
|
71
|
+
result = ~value & 0xff
|
|
72
|
+
flags(NEGATIVE | ZERO | OVERFLOW | CARRY, nz8(result) | CARRY)
|
|
73
|
+
result
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# The undocumented XNC: NEG with C clear, COM with C set.
|
|
77
|
+
def xnc8(value) = carry.zero? ? neg8(value) : com8(value)
|
|
78
|
+
|
|
79
|
+
def lsr8(value)
|
|
80
|
+
result = value >> 1
|
|
81
|
+
flags(NEGATIVE | ZERO | CARRY, nz8(result) | (value & 1))
|
|
82
|
+
result
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def ror8(value)
|
|
86
|
+
result = (value >> 1) | (carry << 7)
|
|
87
|
+
flags(NEGATIVE | ZERO | CARRY, nz8(result) | (value & 1))
|
|
88
|
+
result
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def asr8(value)
|
|
92
|
+
result = (value >> 1) | (value & 0x80)
|
|
93
|
+
flags(NEGATIVE | ZERO | CARRY, nz8(result) | (value & 1))
|
|
94
|
+
result
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def asl8(value) = rotate_left8(value, 0)
|
|
98
|
+
|
|
99
|
+
def rol8(value) = rotate_left8(value, carry)
|
|
100
|
+
|
|
101
|
+
def rotate_left8(value, low)
|
|
102
|
+
result = ((value << 1) | low) & 0xff
|
|
103
|
+
overflow = (value ^ (value << 1)).nobits?(0x80) ? 0 : OVERFLOW
|
|
104
|
+
flags(NEGATIVE | ZERO | OVERFLOW | CARRY, nz8(result) | overflow | (value >> 7))
|
|
105
|
+
result
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def dec8(value)
|
|
109
|
+
result = (value - 1) & 0xff
|
|
110
|
+
flags(NEGATIVE | ZERO | OVERFLOW, nz8(result) | (value == 0x80 ? OVERFLOW : 0))
|
|
111
|
+
result
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# The undocumented XDEC: DEC, with C set unless the operand was 0.
|
|
115
|
+
def xdec8(value)
|
|
116
|
+
result = dec8(value)
|
|
117
|
+
flags(CARRY, value.zero? ? 0 : CARRY)
|
|
118
|
+
result
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def inc8(value)
|
|
122
|
+
result = (value + 1) & 0xff
|
|
123
|
+
flags(NEGATIVE | ZERO | OVERFLOW, nz8(result) | (value == 0x7f ? OVERFLOW : 0))
|
|
124
|
+
result
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def clr8
|
|
128
|
+
flags(NEGATIVE | ZERO | OVERFLOW | CARRY, ZERO)
|
|
129
|
+
0
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
# The undocumented XCLRA and XCLRB: CLR, but C stays.
|
|
133
|
+
def xclr8
|
|
134
|
+
flags(NEGATIVE | ZERO | OVERFLOW, ZERO)
|
|
135
|
+
0
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
end
|