badline 0.16.1 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +213 -0
- data/CLAUDE.md +11 -7
- data/CONTRIBUTING.md +7 -1
- data/README.md +265 -26
- data/doc/media.md +5 -2
- data/doc/pinned-behaviour.md +189 -0
- data/doc/snapshot-format.md +616 -74
- data/doc/snapshots.md +192 -24
- data/exe/badline-ruby +5 -0
- data/lib/badline/audio/terminal.rb +34 -2
- data/lib/badline/c128/keyed_state.rb +2 -0
- data/lib/badline/c128.rb +1 -0
- data/lib/badline/c64/keyed_state.rb +2 -0
- data/lib/badline/c64.rb +1 -0
- data/lib/badline/cbm2/acia.rb +10 -0
- data/lib/badline/cbm2/bus.rb +263 -0
- data/lib/badline/cbm2/cpu.rb +58 -0
- data/lib/badline/cbm2/crtc_video.rb +133 -0
- data/lib/badline/cbm2/ieee_drive.rb +18 -0
- data/lib/badline/cbm2/ieee_layout.rb +69 -0
- data/lib/badline/cbm2/ieee_ports.rb +104 -0
- data/lib/badline/cbm2/ieee_traps.rb +81 -0
- data/lib/badline/cbm2/keyboard_ports.rb +60 -0
- data/lib/badline/cbm2/keyed_modules.rb +67 -0
- data/lib/badline/cbm2/keyed_state.rb +95 -0
- data/lib/badline/cbm2/model.rb +44 -0
- data/lib/badline/cbm2/p500/control_ports.rb +80 -0
- data/lib/badline/cbm2/p500/ieee_layout.rb +41 -0
- data/lib/badline/cbm2/p500/keyed_state.rb +85 -0
- data/lib/badline/cbm2/p500/model.rb +39 -0
- data/lib/badline/cbm2/p500/pages.rb +57 -0
- data/lib/badline/cbm2/p500.rb +264 -0
- data/lib/badline/cbm2/sid_page.rb +19 -0
- data/lib/badline/cbm2.rb +270 -0
- data/lib/badline/{plus4 → chips}/acia/frames.rb +1 -1
- data/lib/badline/chips/acia/keyed_state.rb +47 -0
- data/lib/badline/chips/acia.rb +307 -0
- data/lib/badline/chips/cia/timer.rb +2 -2
- data/lib/badline/chips/cpu/instruction_set.rb +1 -1
- data/lib/badline/chips/crtc.rb +3 -2
- 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/fast_forward.rb +72 -0
- data/lib/badline/chips/riot/keyed_parts.rb +69 -0
- data/lib/badline/chips/riot/timer.rb +91 -0
- data/lib/badline/chips/riot.rb +162 -0
- data/lib/badline/chips/rriot/keyed_state.rb +30 -0
- data/lib/badline/chips/rriot.rb +134 -0
- data/lib/badline/chips/sid/filter.rb +4 -4
- data/lib/badline/chips/sid/waveform.rb +6 -6
- data/lib/badline/chips/tpi/keyed_state.rb +38 -0
- data/lib/badline/chips/tpi.rb +233 -0
- data/lib/badline/chips/vic/bank.rb +60 -5
- data/lib/badline/chips/vic/sprite.rb +4 -3
- data/lib/badline/chips/vic_i/keyed_parts.rb +7 -2
- data/lib/badline/chips/vic_i.rb +84 -21
- data/lib/badline/data_path.rb +3 -3
- data/lib/badline/drives/datasette/keyed_state.rb +9 -1
- data/lib/badline/drives/datasette.rb +27 -3
- data/lib/badline/drives/ieee_models.rb +54 -0
- data/lib/badline/drives/model1001.rb +37 -0
- data/lib/badline/drives/model2031.rb +6 -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 +192 -0
- data/lib/badline/drives/model4040/parts.rb +20 -0
- data/lib/badline/drives/model4040.rb +179 -0
- data/lib/badline/drives/model8050/board.rb +77 -0
- data/lib/badline/drives/model8050/disk.rb +163 -0
- data/lib/badline/drives/model8050/dos_board.rb +91 -0
- data/lib/badline/drives/model8050/fdc_bus.rb +102 -0
- data/lib/badline/drives/model8050/idle.rb +181 -0
- data/lib/badline/drives/model8050/ieee_port.rb +147 -0
- data/lib/badline/drives/model8050/ip_bus.rb +93 -0
- data/lib/badline/drives/model8050/keyed_state.rb +163 -0
- data/lib/badline/drives/model8050/mechanism.rb +230 -0
- data/lib/badline/drives/model8050/parts.rb +37 -0
- data/lib/badline/drives/model8050.rb +131 -0
- data/lib/badline/drives/model9060/idle.rb +149 -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 +195 -0
- data/lib/badline/frontend/app.rb +18 -3
- data/lib/badline/frontend/boot.rb +4 -3
- data/lib/badline/frontend/controls.rb +107 -15
- data/lib/badline/frontend/gamepads.rb +3 -3
- data/lib/badline/frontend/keys.rb +97 -2
- 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 +47 -14
- data/lib/badline/frontend/menu/menu_pages.rb +113 -4
- data/lib/badline/frontend/menu/menu_ports.rb +77 -4
- data/lib/badline/frontend/menu/pause_menu.rb +6 -2
- data/lib/badline/frontend/pet_keyboard.rb +45 -7
- 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 +49 -11
- data/lib/badline/frontend.rb +3 -0
- data/lib/badline/kernal_trap/dos/disk_commands.rb +80 -0
- data/lib/badline/kernal_trap/dos/status.rb +3 -0
- data/lib/badline/kernal_trap/dos.rb +18 -5
- data/lib/badline/kernal_trap/ieee.rb +4 -2
- data/lib/badline/kernal_trap/ieee_layout.rb +95 -43
- 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 +116 -0
- data/lib/badline/kim1/teletype.rb +193 -0
- data/lib/badline/kim1.rb +190 -0
- data/lib/badline/machine/ieee_drives.rb +171 -0
- data/lib/badline/machine/keyboard_buffer.rb +20 -3
- data/lib/badline/machine/line_sound.rb +122 -0
- data/lib/badline/machine/settling.rb +4 -0
- data/lib/badline/machine.rb +62 -14
- data/lib/badline/media/cbm2_media.rb +29 -0
- 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/program.rb +6 -3
- data/lib/badline/media/true_drive.rb +51 -25
- data/lib/badline/media.rb +23 -8
- data/lib/badline/options/family.rb +108 -6
- data/lib/badline/options/help.rb +58 -12
- data/lib/badline/options/table.rb +60 -14
- data/lib/badline/options/validation.rb +71 -11
- data/lib/badline/options.rb +46 -13
- data/lib/badline/pet/bus.rb +16 -2
- data/lib/badline/pet/cassettes.rb +22 -1
- data/lib/badline/pet/ieee_drive.rb +5 -64
- data/lib/badline/pet/ieee_ports.rb +7 -0
- 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 +81 -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 +216 -0
- data/lib/badline/pet.rb +33 -6
- data/lib/badline/plus4/acia.rb +4 -298
- data/lib/badline/plus4/keyed_parts.rb +0 -44
- data/lib/badline/plus4/keyed_state.rb +2 -0
- data/lib/badline/plus4.rb +2 -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/8050/dos-2.7.rom +0 -0
- data/lib/badline/roms/8050/fdc-901869-01.rom +0 -0
- data/lib/badline/roms/8050/fdc-901885-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 +277 -0
- data/lib/badline/roms/cbm2/basic128-02b.rom +0 -0
- data/lib/badline/roms/cbm2/basic128.rom +0 -0
- data/lib/badline/roms/cbm2/basic256.rom +0 -0
- data/lib/badline/roms/cbm2/characters-600.rom +0 -0
- data/lib/badline/roms/cbm2/characters-700.rom +0 -0
- data/lib/badline/roms/cbm2/kernal-03b.rom +0 -0
- data/lib/badline/roms/cbm2/kernal-04a.rom +0 -0
- data/lib/badline/roms/cbm2/p500-basic.rom +0 -0
- data/lib/badline/roms/cbm2/p500-kernal.rom +0 -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/cbm2_setup.rb +53 -0
- data/lib/badline/snapshot/container.rb +9 -2
- data/lib/badline/snapshot/field_doc/markdown.rb +4 -2
- data/lib/badline/snapshot/field_doc.rb +59 -3
- data/lib/badline/snapshot/image.rb +65 -9
- 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/cbm2.rb +93 -0
- data/lib/badline/snapshot/vice/cbm2_acia.rb +75 -0
- data/lib/badline/snapshot/vice/cbm2_crtc.rb +127 -0
- data/lib/badline/snapshot/vice/cbm2_mains.rb +49 -0
- data/lib/badline/snapshot/vice/cbm2_mem.rb +75 -0
- data/lib/badline/snapshot/vice/cbm2_restore.rb +204 -0
- data/lib/badline/snapshot/vice/cbm2_tpi.rb +58 -0
- data/lib/badline/snapshot/vice/cias.rb +13 -9
- data/lib/badline/snapshot/vice/p500.rb +96 -0
- data/lib/badline/snapshot/vice/p500_restore.rb +130 -0
- data/lib/badline/snapshot/vice/p500_vicii.rb +166 -0
- data/lib/badline/snapshot/vice/pet.rb +20 -10
- data/lib/badline/snapshot/vice/pet_cpu.rb +42 -5
- data/lib/badline/snapshot/vice/pet_crtc.rb +25 -17
- data/lib/badline/snapshot/vice/pet_drive.rb +180 -0
- data/lib/badline/snapshot/vice/pet_drive8050.rb +336 -0
- data/lib/badline/snapshot/vice/pet_mem.rb +49 -5
- data/lib/badline/snapshot/vice/pet_restore.rb +100 -7
- data/lib/badline/snapshot/vice/pet_via.rb +6 -5
- data/lib/badline/snapshot/vice/sid_registers.rb +2 -1
- data/lib/badline/snapshot/vice/vicii_import.rb +10 -6
- data/lib/badline/snapshot/vice.rb +3 -0
- data/lib/badline/snapshot.rb +15 -3
- data/lib/badline/status.rb +30 -30
- data/lib/badline/storage/crt_file.rb +7 -1
- data/lib/badline/storage/d64_image.rb +7 -0
- data/lib/badline/storage/d67_image.rb +55 -0
- data/lib/badline/storage/d80_image.rb +17 -1
- data/lib/badline/storage/d81_image.rb +14 -0
- data/lib/badline/storage/d90_image.rb +180 -0
- data/lib/badline/storage/disk_image/bam.rb +18 -3
- data/lib/badline/storage/disk_image/formatting.rb +69 -0
- data/lib/badline/storage/disk_image/writing.rb +37 -2
- data/lib/badline/storage/disk_image.rb +2 -0
- data/lib/badline/storage/kim_tape.rb +254 -0
- data/lib/badline/storage/papertape.rb +90 -0
- data/lib/badline/storage.rb +5 -0
- data/lib/badline/support/cbm2_checkpoint.rb +48 -0
- data/lib/badline/support/kim1_checkpoint.rb +41 -0
- data/lib/badline/support/p500_checkpoint.rb +35 -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.rb +26 -12
- data/lib/badline.rb +2 -0
- metadata +148 -2
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
class CBM2
|
|
5
|
+
# The B series models badline builds by name, after xcbm2's -model
|
|
6
|
+
# names: the segments the RAM board fills, the BASIC that fits it, the
|
|
7
|
+
# character ROM, and whether the board's PC7 jumper tells the editor
|
|
8
|
+
# it drives the 700's monitor.
|
|
9
|
+
module Model
|
|
10
|
+
Profile = Data.define(:name, :ram_segments, :basic, :characters, :high_profile) do
|
|
11
|
+
def ram_kb = ram_segments.length * 64
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
M610 = Profile.new(name: "610", ram_segments: [1, 2].freeze, basic: "cbm2/basic128.rom",
|
|
15
|
+
characters: "cbm2/characters-600.rom", high_profile: false)
|
|
16
|
+
M620 = Profile.new(name: "620", ram_segments: [1, 2, 3, 4].freeze, basic: "cbm2/basic256.rom",
|
|
17
|
+
characters: "cbm2/characters-600.rom", high_profile: false)
|
|
18
|
+
M710 = Profile.new(name: "710", ram_segments: [1, 2].freeze, basic: "cbm2/basic128.rom",
|
|
19
|
+
characters: "cbm2/characters-700.rom", high_profile: true)
|
|
20
|
+
M720 = Profile.new(name: "720", ram_segments: [1, 2, 3, 4].freeze, basic: "cbm2/basic256.rom",
|
|
21
|
+
characters: "cbm2/characters-700.rom", high_profile: true)
|
|
22
|
+
|
|
23
|
+
ALL = [M610, M620, M710, M720].freeze
|
|
24
|
+
DEFAULT = "610"
|
|
25
|
+
|
|
26
|
+
# The KERNALs a B series board takes: 901244-04a, VICE's, and
|
|
27
|
+
# 901244-03b, the 1983 release.
|
|
28
|
+
KERNALS = { "04a" => "cbm2/kernal-04a.rom", "03b" => "cbm2/kernal-03b.rom" }.freeze
|
|
29
|
+
|
|
30
|
+
def self.named(name)
|
|
31
|
+
model = ALL.find { |candidate| candidate.name == name }
|
|
32
|
+
raise ArgumentError, "no CBM-II model named #{name}" if model.nil?
|
|
33
|
+
|
|
34
|
+
model
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# The BASIC +model+ runs with +kernal+: BASIC 128 of the 1983
|
|
38
|
+
# release, 02b, with 03b.
|
|
39
|
+
def self.basic(model, kernal)
|
|
40
|
+
kernal == "03b" && model.basic == "cbm2/basic128.rom" ? "cbm2/basic128-02b.rom" : model.basic
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
class CBM2
|
|
5
|
+
class P500
|
|
6
|
+
# The P500's two control ports on the CIA at $DC00, as its schematic's
|
|
7
|
+
# game port interface wires them (sheets 9 and 10): port 1's
|
|
8
|
+
# directions on PB0-PB3 and its fire button on PA6, which is the VIC's
|
|
9
|
+
# light pen input too, and port 2's on PB4-PB7 and PA7. Every switch
|
|
10
|
+
# pulls its line low. Port A carries the IEEE-488 bus's data lines as
|
|
11
|
+
# well (IEEEPorts::Data), which pull it low too.
|
|
12
|
+
#
|
|
13
|
+
# A device on a port, a pair of paddles or a mouse, adds its buttons
|
|
14
|
+
# to the port's lines, and its pots reach the SID through the analog
|
|
15
|
+
# switches PA0 (port 1) and PA1 (port 2) close, as PA6 and PA7 do on
|
|
16
|
+
# the C64 (Input::ControlPorts).
|
|
17
|
+
class ControlPorts
|
|
18
|
+
DIRECTIONS = 0x0f
|
|
19
|
+
FIRE = 0x10
|
|
20
|
+
PORT1_POTS = 0x01
|
|
21
|
+
PORT2_POTS = 0x02
|
|
22
|
+
|
|
23
|
+
attr_reader :joystick1, :joystick2
|
|
24
|
+
attr_accessor :device1, :device2
|
|
25
|
+
|
|
26
|
+
def initialize(joystick1, joystick2, ieee)
|
|
27
|
+
@joystick1 = joystick1
|
|
28
|
+
@joystick2 = joystick2
|
|
29
|
+
@ieee = ieee
|
|
30
|
+
@device1 = nil
|
|
31
|
+
@device2 = nil
|
|
32
|
+
@cia = nil
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# The CIA whose PA0 and PA1 close the pots' switches.
|
|
36
|
+
def connect(cia)
|
|
37
|
+
@cia = cia
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def read_a(port_a, port_b)
|
|
41
|
+
fire1 = port1.anybits?(FIRE) ? 0x40 : 0
|
|
42
|
+
fire2 = port2.anybits?(FIRE) ? 0x80 : 0
|
|
43
|
+
(0x3f | fire1 | fire2) & @ieee.read_a(port_a, port_b)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def read_b(_port_a, _port_b)
|
|
47
|
+
(port1 & DIRECTIONS) | ((port2 & DIRECTIONS) << 4)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# PB4 is port 2's up line here, not a light pen input.
|
|
51
|
+
def port_b4_high? = true
|
|
52
|
+
|
|
53
|
+
# Whether port 1's fire line is high, the light pen input's.
|
|
54
|
+
def fire1_high? = port1.anybits?(FIRE)
|
|
55
|
+
|
|
56
|
+
def pot_x = selected_devices.map(&:pot_x).min || 0xff
|
|
57
|
+
|
|
58
|
+
def pot_y = selected_devices.map(&:pot_y).min || 0xff
|
|
59
|
+
|
|
60
|
+
private
|
|
61
|
+
|
|
62
|
+
def port1 = port_bits(@joystick1, @device1)
|
|
63
|
+
|
|
64
|
+
def port2 = port_bits(@joystick2, @device2)
|
|
65
|
+
|
|
66
|
+
def port_bits(joystick, device)
|
|
67
|
+
device ? joystick.port_bits & device.port_bits : joystick.port_bits
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def selected_devices
|
|
71
|
+
lines = @cia&.port_a_lines || 0x00
|
|
72
|
+
devices = []
|
|
73
|
+
devices << @device1 if @device1 && lines.anybits?(PORT1_POTS)
|
|
74
|
+
devices << @device2 if @device2 && lines.anybits?(PORT2_POTS)
|
|
75
|
+
devices
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
class CBM2
|
|
5
|
+
class P500
|
|
6
|
+
# Where the P500's KERNAL, 901234-02, keeps the eight IEEE-488
|
|
7
|
+
# primitives: each seven bytes above the B series' (CBM2::IEEELayout),
|
|
8
|
+
# with the same file variables in segment 15's zero page. It drives
|
|
9
|
+
# the bus from the same ports, but leaves TPI1's PA2 an input, so its
|
|
10
|
+
# writes to TPI1's port A and its direction register differ in that
|
|
11
|
+
# bit: TALK and LISTEN set DDRA to $3B and PA to $FE, TKSA and ACPTR
|
|
12
|
+
# keep $3D of PA and set DDRA to $C3, ACPTR keeps $3D of PA and sets
|
|
13
|
+
# PA0, and UNTALK and UNLISTEN leave PA $FD before letting ATN go.
|
|
14
|
+
module IEEELayout
|
|
15
|
+
LineStep = KernalTrap::IEEELineStep
|
|
16
|
+
|
|
17
|
+
def self.write(address, value) = CBM2::IEEELayout.write(address, value)
|
|
18
|
+
|
|
19
|
+
def self.update(address, keep, set) = CBM2::IEEELayout.update(address, keep, set)
|
|
20
|
+
|
|
21
|
+
ATTENTION = [write(0xde03, 0x3b), write(0xdc00, 0xff), write(0xdc02, 0xff), write(0xde00, 0xfe),
|
|
22
|
+
update(0xde00, 0xf7, 0)].freeze
|
|
23
|
+
LISTEN_TO_TALKER = [update(0xde00, 0x3d, 0), write(0xde03, 0xc3), write(0xdc02, 0x00)].freeze
|
|
24
|
+
|
|
25
|
+
LINES = KernalTrap::IEEELines.new(
|
|
26
|
+
timer: CBM2::IEEELayout::TIMER, attention: ATTENTION, byte_sent: CBM2::IEEELayout::BYTE_SENT,
|
|
27
|
+
release_attention: CBM2::IEEELayout::RELEASE_ATTENTION, listen_to_talker: LISTEN_TO_TALKER,
|
|
28
|
+
byte_received: [update(0xde00, 0x3d, 0x01)].freeze,
|
|
29
|
+
release: (ATTENTION + CBM2::IEEELayout::BYTE_SENT + [write(0xde00, 0xfd)] + LISTEN_TO_TALKER.drop(1) +
|
|
30
|
+
CBM2::IEEELayout::RELEASE_ATTENTION).freeze
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
LAYOUT = KernalTrap::IEEELayout.new(
|
|
34
|
+
talk: 0xf237, listen: 0xf23b, second: 0xf27b, tksa: 0xf287, ciout: 0xf29e, untalk: 0xf2b2,
|
|
35
|
+
unlisten: 0xf2b6, acptr: 0xf311, overwrites_device: false, device_in_a: true,
|
|
36
|
+
zero_page: CBM2::IEEELayout::ZERO_PAGE, lines: LINES
|
|
37
|
+
)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
class CBM2
|
|
5
|
+
class P500
|
|
6
|
+
# The machine's own fields as the B series keeps them (CBM2::KeyedState),
|
|
7
|
+
# and the whole machine as keyed snapshot modules (Snapshot::Keyed), in
|
|
8
|
+
# the order a load applies them: BL-MACHINE with the setup, the RAM
|
|
9
|
+
# with the segment registers, the colour RAM, the VIC, the SID, the
|
|
10
|
+
# CIA, the ACIA, both TPIs, the CPU, device 8 through the traps and a
|
|
11
|
+
# true 2031 on the IEEE-488 bus.
|
|
12
|
+
#
|
|
13
|
+
# A load lays the pages out before the chips load, and works out what
|
|
14
|
+
# each unit derives from its fields, the VIC's view of memory among
|
|
15
|
+
# them, once every unit is loaded.
|
|
16
|
+
module KeyedModules
|
|
17
|
+
# The SID's deferred cycles and writes, run up to this cycle, and the
|
|
18
|
+
# VIC's sprite collisions latched up to the beam.
|
|
19
|
+
def settle_chips_for_snapshot
|
|
20
|
+
@sid.catch_up!
|
|
21
|
+
@vic.fold_collisions!
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def save_modules
|
|
25
|
+
bus = @bus
|
|
26
|
+
sections = [Snapshot::Keyed.section("BL-MACHINE") { |out| save_machine_fields(out) }]
|
|
27
|
+
sections << Snapshot::Keyed.section("BL-CPU") { |out| @cpu.save_fields(out) }
|
|
28
|
+
sections << Snapshot::Keyed.section("BL-RAM") { |out| bus.save_fields(out) }
|
|
29
|
+
sections << Snapshot::Keyed.section("BL-COLORRAM") { |out| @color_ram.save_fields(out, "ram") }
|
|
30
|
+
sections << Snapshot::Keyed.section("BL-VIC") { |out| @vic.save_fields(out) }
|
|
31
|
+
sections << Snapshot::Keyed.section("BL-SID") { |out| @sid.save_fields(out) }
|
|
32
|
+
sections << Snapshot::Keyed.section("BL-CIA") { |out| @cia.save_fields(out) }
|
|
33
|
+
sections << Snapshot::Keyed.section("BL-ACIA") { |out| @acia.save_fields(out) }
|
|
34
|
+
sections << Snapshot::Keyed.section("BL-TPI1") { |out| @tpi1.save_fields(out) }
|
|
35
|
+
sections << Snapshot::Keyed.section("BL-TPI2") { |out| @tpi2.save_fields(out) }
|
|
36
|
+
sections << Snapshot::Keyed.section("BL-TRAPDOS") { |out| save_trap_dos_fields(out) } if mounted?
|
|
37
|
+
save_drive_modules(sections)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Raises Snapshot::FormatError for the modules of a machine built
|
|
41
|
+
# another way.
|
|
42
|
+
def load_modules(modules)
|
|
43
|
+
check_snapshot_setup(Snapshot::CBM2Setup.read_fields(modules.machine))
|
|
44
|
+
load_machine_fields(modules.machine)
|
|
45
|
+
@bus.load_fields(modules.required("BL-RAM"))
|
|
46
|
+
@bus.restored!
|
|
47
|
+
@color_ram.load_fields(modules.required("BL-COLORRAM"), "ram")
|
|
48
|
+
@vic.load_fields(modules.required("BL-VIC"))
|
|
49
|
+
@sid.load_fields(modules.required("BL-SID"))
|
|
50
|
+
@cia.load_fields(modules.required("BL-CIA"))
|
|
51
|
+
@acia.load_fields(modules.required("BL-ACIA"))
|
|
52
|
+
@tpi1.load_fields(modules.required("BL-TPI1"))
|
|
53
|
+
@tpi2.load_fields(modules.required("BL-TPI2"))
|
|
54
|
+
@cpu.load_fields(modules.required("BL-CPU"))
|
|
55
|
+
trap_dos = modules.optional("BL-TRAPDOS")
|
|
56
|
+
trap_dos ? load_trap_dos_fields(trap_dos, detached: modules.detached?) : unmount
|
|
57
|
+
load_drive_modules(modules)
|
|
58
|
+
restored!
|
|
59
|
+
self
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
private
|
|
63
|
+
|
|
64
|
+
def restored!
|
|
65
|
+
@cpu.restored!
|
|
66
|
+
@vic.restored!
|
|
67
|
+
@sid.restored!
|
|
68
|
+
@cia.restored!
|
|
69
|
+
@acia.restored!
|
|
70
|
+
update_video_bus
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def check_snapshot_setup(setup)
|
|
74
|
+
ours = Snapshot::CBM2Setup.of(self)
|
|
75
|
+
return if setup == ours
|
|
76
|
+
|
|
77
|
+
raise Snapshot::FormatError, "the snapshot is of a #{setup}, not a #{ours}"
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
include CBM2::KeyedState
|
|
82
|
+
include KeyedModules
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
class CBM2
|
|
5
|
+
class P500
|
|
6
|
+
# The P500s badline builds by name: the 510 of xcbm5x0's -model, PAL
|
|
7
|
+
# with the 6569 and NTSC with the 6567R8, each with its own region's
|
|
8
|
+
# mains unless told otherwise. Both have 128K of RAM, segments 0 and 1,
|
|
9
|
+
# and the one KERNAL and BASIC.
|
|
10
|
+
module Model
|
|
11
|
+
Profile = Data.define(:name, :region) do
|
|
12
|
+
def ram_kb = RAM_SEGMENTS.length * 64
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
M510 = Profile.new(name: "510", region: Region::PAL)
|
|
16
|
+
M510_NTSC = Profile.new(name: "510ntsc", region: Region::NTSC)
|
|
17
|
+
|
|
18
|
+
ALL = [M510, M510_NTSC].freeze
|
|
19
|
+
DEFAULT = "510"
|
|
20
|
+
|
|
21
|
+
RAM_SEGMENTS = [0, 1].freeze
|
|
22
|
+
|
|
23
|
+
# 901234-02, the KERNAL xcbm5x0 runs, and its BASIC 128, 901236-02
|
|
24
|
+
# and 901235-02.
|
|
25
|
+
KERNALS = { "02" => "cbm2/p500-kernal.rom" }.freeze
|
|
26
|
+
BASIC = "cbm2/p500-basic.rom"
|
|
27
|
+
|
|
28
|
+
def self.named(name)
|
|
29
|
+
model = ALL.find { |candidate| candidate.name == name }
|
|
30
|
+
raise ArgumentError, "no P500 model named #{name}" if model.nil?
|
|
31
|
+
|
|
32
|
+
model
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def self.include?(name) = ALL.any? { |model| model.name == name }
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
class CBM2
|
|
5
|
+
class P500
|
|
6
|
+
# Page $D8 of segment 15: the VIC-II's 64 registers, repeated over the
|
|
7
|
+
# page.
|
|
8
|
+
class VICPage
|
|
9
|
+
def initialize(vic)
|
|
10
|
+
@vic = vic
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def peek(addr) = @vic.peek(0xd000 | (addr & 0x3f))
|
|
14
|
+
|
|
15
|
+
def poke(addr, value)
|
|
16
|
+
@vic.poke(0xd000 | (addr & 0x3f), value)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# The colour RAM's four bits at $D400-$D7FF of segment 15, which the
|
|
21
|
+
# CPU reads with 0 in the high four, as xcbm5x0 does.
|
|
22
|
+
class ColorPages
|
|
23
|
+
def initialize(color_ram)
|
|
24
|
+
@color_ram = color_ram
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def peek(addr) = @color_ram.nibble(addr)
|
|
28
|
+
|
|
29
|
+
def poke(addr, value)
|
|
30
|
+
@color_ram.poke(addr, value)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# A TPI's page, $DE or $DF of segment 15. TPI1's CA and CB are STATVID
|
|
35
|
+
# and VICDOTSEL, and TPI2's PC6 and PC7 the VIC's bank lines, which
|
|
36
|
+
# an access to either chip can move, so the machine takes them again
|
|
37
|
+
# after each one (P500#update_video_bus).
|
|
38
|
+
class TPIPage
|
|
39
|
+
def initialize(tpi, machine)
|
|
40
|
+
@tpi = tpi
|
|
41
|
+
@machine = machine
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def peek(addr)
|
|
45
|
+
value = @tpi.peek(addr)
|
|
46
|
+
@machine.update_video_bus
|
|
47
|
+
value
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def poke(addr, value)
|
|
51
|
+
@tpi.poke(addr, value)
|
|
52
|
+
@machine.update_video_bus
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "badline/cbm2/p500/model"
|
|
4
|
+
require "badline/cbm2/p500/pages"
|
|
5
|
+
require "badline/cbm2/p500/control_ports"
|
|
6
|
+
require "badline/cbm2/p500/ieee_layout"
|
|
7
|
+
require "badline/cbm2/p500/keyed_state"
|
|
8
|
+
|
|
9
|
+
module Badline
|
|
10
|
+
class CBM2
|
|
11
|
+
# The P500, the CBM 510: the B series' 6509, SID, CIA, ACIA and TPIs on
|
|
12
|
+
# the same segment bus (Bus, CPU), at the VIC-II's clock of about
|
|
13
|
+
# 1 MHz, with a VIC-II at $D800 in place of the 6845. RAM fills segments
|
|
14
|
+
# 0 and 1.
|
|
15
|
+
#
|
|
16
|
+
# Segment 15 holds what the B series' does but for $C000-$D7FF: the
|
|
17
|
+
# character ROM at $C000, which the CPU reads too, the 1K static video
|
|
18
|
+
# matrix at $D000 and the colour RAM's nibbles at $D400. What the VIC
|
|
19
|
+
# reads follows the P500's video bus (Chips::VIC::Bank#map_video_bus):
|
|
20
|
+
# TPI1's CA is STATVID and its CB VICDOTSEL, and TPI2's PC6 and PC7
|
|
21
|
+
# drive VA14 and VA15, which pick the 16K of segment 0 the VIC sees.
|
|
22
|
+
# BA halts the CPU as on the C64, and the VIC's IRQ reaches the CPU
|
|
23
|
+
# along with TPI1's.
|
|
24
|
+
#
|
|
25
|
+
# TPI1 is the interrupt controller as on the B series, with the mains
|
|
26
|
+
# on I0, the CIA on I2 and the ACIA on I4. TPI2 scans the B series'
|
|
27
|
+
# key matrix (KeyboardPorts), and the CIA reads the two control ports
|
|
28
|
+
# (ControlPorts), whose paddles reach the SID's pots and whose port 1
|
|
29
|
+
# fire line is the VIC's light pen input. Device 8 answers through traps on the KERNAL's
|
|
30
|
+
# IEEE-488 primitives as on the B series (IEEETraps, with the P500's
|
|
31
|
+
# IEEELayout), or a true 2031, 8050 or 8250 on the bus answers, on the
|
|
32
|
+
# same ports as the B series' (IEEEPorts, IEEEDrive).
|
|
33
|
+
class P500
|
|
34
|
+
include Machine::KeyboardBuffer
|
|
35
|
+
include Machine::Clocking
|
|
36
|
+
include Machine::Settling
|
|
37
|
+
include IEEETraps
|
|
38
|
+
include IEEEDrive
|
|
39
|
+
|
|
40
|
+
# The segment BASIC keeps its program text in.
|
|
41
|
+
TEXT_SEGMENT = 0
|
|
42
|
+
|
|
43
|
+
# The ACIA at $DD00, whose IRQ reaches TPI1's I4.
|
|
44
|
+
class ACIA < Chips::ACIA
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
attr_reader :model, :bus, :cpu, :vic, :sid, :cia, :acia, :tpi1, :tpi2, :keyboard, :joystick1, :joystick2,
|
|
48
|
+
:control_ports, :color_ram, :ieee, :mains_hz, :kernal, :cycles, :init_threshold
|
|
49
|
+
|
|
50
|
+
def ram = @bus.ram
|
|
51
|
+
|
|
52
|
+
# The VIC refreshes the DRAM in its own half of the cycle, so the
|
|
53
|
+
# refresh takes none of the CPU's.
|
|
54
|
+
def refresh? = false
|
|
55
|
+
|
|
56
|
+
def family = :cbm2
|
|
57
|
+
|
|
58
|
+
def peek(addr) = @bus.peek(addr)
|
|
59
|
+
|
|
60
|
+
def poke(addr, value) = @bus.poke(addr, value)
|
|
61
|
+
|
|
62
|
+
def install_debug_register(&) = @bus.install_debug_register(&)
|
|
63
|
+
|
|
64
|
+
def keyboard_buffer = CBM2::KEYBOARD_LAYOUT
|
|
65
|
+
|
|
66
|
+
def sound_source = @sid
|
|
67
|
+
|
|
68
|
+
# The VIC, whose display a front end shows.
|
|
69
|
+
def video = @vic
|
|
70
|
+
|
|
71
|
+
def region = @model.region
|
|
72
|
+
|
|
73
|
+
# +model+ names one of Model::ALL, and +mains_hz+ the mains, 50 or
|
|
74
|
+
# 60, the model's region's unless given. +true_drive_model+ names the
|
|
75
|
+
# drive plug_true_drive plugs in (Drives::IEEEModels).
|
|
76
|
+
def initialize(model: Model::DEFAULT, mains_hz: nil, debug: false, true_drive_model: Drives::IEEEModels::DEFAULT)
|
|
77
|
+
@model = Model.named(model)
|
|
78
|
+
@mains_hz = mains_hz || @model.region.mains_hz
|
|
79
|
+
raise ArgumentError, "the mains is 50 or 60 Hz, not #{@mains_hz}" unless [50, 60].include?(@mains_hz)
|
|
80
|
+
|
|
81
|
+
@kernal = Model::KERNALS.keys.first
|
|
82
|
+
clock_hz = @model.region.clock_hz
|
|
83
|
+
@bus = Bus.new(basic: Model::BASIC, kernal: Model::KERNALS.fetch(@kernal), ram_segments: Model::RAM_SEGMENTS,
|
|
84
|
+
screen_length: 0x400)
|
|
85
|
+
@vic = Chips::VIC.new(model: :mos6569, region: @model.region)
|
|
86
|
+
@color_ram = C64::ColorMemory.new(@vic, start: 0xd400)
|
|
87
|
+
map_video
|
|
88
|
+
@ieee = IEEEPorts.new(Drives::IEEEBus.new)
|
|
89
|
+
@tpi1 = Chips::TPI.new(peripheral: @ieee)
|
|
90
|
+
@joystick1 = Input::Joystick.new
|
|
91
|
+
@joystick2 = Input::Joystick.new
|
|
92
|
+
@control_ports = ControlPorts.new(@joystick1, @joystick2, @ieee.data)
|
|
93
|
+
@sid = Chips::SID.new(model: :mos6581, pots: @control_ports, at: 0xda00)
|
|
94
|
+
@cia = Chips::CIA.new(start: 0xdc00, peripheral: @control_ports, region: @model.region, tod_hz: @mains_hz)
|
|
95
|
+
@control_ports.connect(@cia)
|
|
96
|
+
@ieee.connect(@cia, @tpi1)
|
|
97
|
+
@lightpen_high = true
|
|
98
|
+
@acia = ACIA.new(self, clock_hz)
|
|
99
|
+
@keyboard = Input::Keyboard.new(matrix: KeyboardPorts::MATRIX)
|
|
100
|
+
keyboard_ports = KeyboardPorts.new(@keyboard, 0xc0)
|
|
101
|
+
@tpi2 = Chips::TPI.new(peripheral: keyboard_ports)
|
|
102
|
+
keyboard_ports.connect(@tpi2)
|
|
103
|
+
map_io
|
|
104
|
+
@cpu = CPU.new(@bus, debug:)
|
|
105
|
+
@cycles = 0
|
|
106
|
+
@mains_half_period = clock_hz / (2 * @mains_hz)
|
|
107
|
+
@mains_countdown = @mains_half_period
|
|
108
|
+
@mains_high = true
|
|
109
|
+
# The B series' refresh counter, which CBM2::KeyedState saves: 0
|
|
110
|
+
# here, where the VIC refreshes the DRAM.
|
|
111
|
+
@refresh = 0
|
|
112
|
+
@acia_irq = false
|
|
113
|
+
@acia_due = Chips::ACIA::NEVER
|
|
114
|
+
@init_handlers = []
|
|
115
|
+
@pending_keys = nil
|
|
116
|
+
@init_keys = nil
|
|
117
|
+
@dos = nil
|
|
118
|
+
@ieee_trap = nil
|
|
119
|
+
@capture_output = nil
|
|
120
|
+
init_ieee_drives(true_drive_model)
|
|
121
|
+
@init_threshold = INIT_THRESHOLD
|
|
122
|
+
update_video_bus
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# The model, its RAM and its mains as the pause menu names them, such
|
|
126
|
+
# as "510, 128K RAM, 50 HZ".
|
|
127
|
+
def model_label = "#{@model.name}, #{@model.ram_kb}K RAM, #{@mains_hz} HZ"
|
|
128
|
+
|
|
129
|
+
def timing
|
|
130
|
+
region = @model.region
|
|
131
|
+
Timing.new(clock_hz: region.clock_hz, cycles_per_line: region.cycles_per_line,
|
|
132
|
+
lines_per_frame: region.lines_per_frame, crop: region.crop, pixel_width: 1, dots: 1)
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
# The VIC and the mains ahead of the chips, then TPI1's inputs from
|
|
136
|
+
# the CIA and the ACIA, and the CPU last, halted while BA is low on a
|
|
137
|
+
# read.
|
|
138
|
+
def cycle!
|
|
139
|
+
handle_init if @cycles == @init_threshold
|
|
140
|
+
feed_keyboard if @pending_keys
|
|
141
|
+
|
|
142
|
+
vic = @vic
|
|
143
|
+
vic.cycle!
|
|
144
|
+
tick_mains
|
|
145
|
+
@cia.cycle!
|
|
146
|
+
sample_lightpen
|
|
147
|
+
@sid.cycle!
|
|
148
|
+
@acia.run!(@cycles) if @cycles >= @acia_due
|
|
149
|
+
tpi1 = @tpi1
|
|
150
|
+
tpi1.interrupt_line(2, !@cia.interrupted?)
|
|
151
|
+
tpi1.interrupt_line(4, !@acia_irq)
|
|
152
|
+
cpu = @cpu
|
|
153
|
+
cpu.irq = tpi1.irq? || vic.interrupted?
|
|
154
|
+
cpu.pending_write? || !vic.ba_low? ? cpu.cycle! : cpu.stall!
|
|
155
|
+
clock_ieee_drives
|
|
156
|
+
|
|
157
|
+
@cycles += 1
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
# The ACIA's IRQ line, and the cycle it next has something to do at.
|
|
161
|
+
def acia_changed(irq, due)
|
|
162
|
+
@acia_irq = irq
|
|
163
|
+
@acia_due = due
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
# STATVID and VICDOTSEL from TPI1's CA and CB, PC6 and PC7 in mode 0,
|
|
167
|
+
# and VA14 and VA15 from TPI2's PC6 and PC7, to the VIC's bank.
|
|
168
|
+
def update_video_bus
|
|
169
|
+
lines = @tpi1.port_c_output
|
|
170
|
+
@vic.vic_bank.video_bus_lines(lines.anybits?(0x40), lines.anybits?(0x80), @tpi2.port_c_output >> 6)
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
# The RES line reaches the CPU and its segment registers, the SID, the
|
|
174
|
+
# CIA, the ACIA and both TPIs. The VIC has no reset pin.
|
|
175
|
+
def reset!
|
|
176
|
+
@sid.reset!
|
|
177
|
+
@cia.reset!
|
|
178
|
+
@acia.reset!
|
|
179
|
+
@tpi1.reset!
|
|
180
|
+
@tpi2.reset!
|
|
181
|
+
update_video_bus
|
|
182
|
+
@bus.reset!
|
|
183
|
+
@cpu.reset!
|
|
184
|
+
reset_ieee_drives
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
# Puts a program file's bytes where its first two say in BASIC's text
|
|
188
|
+
# segment, segment 0, and returns that address.
|
|
189
|
+
def load_prg(data)
|
|
190
|
+
load_addr = data[0] | (data[1] << 8)
|
|
191
|
+
bytes = data[2..] || []
|
|
192
|
+
bytes = bytes[0, 0x10000 - load_addr] if load_addr + bytes.length > 0x10000
|
|
193
|
+
bytes.each_with_index { |byte, i| @bus.poke_bank(TEXT_SEGMENT, load_addr + i, byte) }
|
|
194
|
+
load_addr
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
# The IEEELayout of 901234-02.
|
|
198
|
+
def trap_layout = IEEELayout::LAYOUT
|
|
199
|
+
|
|
200
|
+
# Puts a cartridge's ROM chips in (Bus#attach_cartridge) and switches
|
|
201
|
+
# the machine off and on, for the KERNAL to find it as it starts.
|
|
202
|
+
def attach_cartridge(chips)
|
|
203
|
+
@bus.attach_cartridge(chips)
|
|
204
|
+
power_cycle!
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
# Switches the machine off and on: RAM starts cleared, the VIC starts
|
|
208
|
+
# over, and the RES line resets everything else.
|
|
209
|
+
def power_cycle!
|
|
210
|
+
@bus.power_on!
|
|
211
|
+
@color_ram.clear!
|
|
212
|
+
@vic.power_on!
|
|
213
|
+
reset!
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
# The cycle by which the KERNAL has tested segments 0 and 1 and BASIC
|
|
217
|
+
# printed READY, with a margin.
|
|
218
|
+
INIT_THRESHOLD = 8_600_000
|
|
219
|
+
|
|
220
|
+
private
|
|
221
|
+
|
|
222
|
+
# The character ROM at $C000 and the colour RAM at $D400 of segment
|
|
223
|
+
# 15, and what the VIC reads.
|
|
224
|
+
def map_video
|
|
225
|
+
character_rom = ROM.load("character.rom", 0xc000)
|
|
226
|
+
@bus.map_video(character_rom, 0xc0, 0x10)
|
|
227
|
+
@bus.map_video(ColorPages.new(@color_ram), 0xd4, 0x04, writable: true)
|
|
228
|
+
bank = @vic.vic_bank
|
|
229
|
+
bank.map_character_rom(character_rom, base: 0xc000)
|
|
230
|
+
bank.map_video_bus(@bus.segment_ram[0], @bus.screen, @color_ram, 0xd400)
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
def map_io
|
|
234
|
+
@bus.map_io(0xd8, VICPage.new(@vic))
|
|
235
|
+
@bus.map_io(0xda, SIDPage.new(@sid))
|
|
236
|
+
@bus.map_io(0xdc, @cia)
|
|
237
|
+
@bus.map_io(0xdd, @acia)
|
|
238
|
+
@bus.map_io(0xde, TPIPage.new(@tpi1, self))
|
|
239
|
+
@bus.map_io(0xdf, TPIPage.new(@tpi2, self))
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
# The VIC's light pen input, CIA PA6 as the CIA drives it and control
|
|
243
|
+
# port 1's fire line pulls it, whose falling edge latches the beam.
|
|
244
|
+
def sample_lightpen
|
|
245
|
+
high = @cia.port_a_lines.anybits?(0x40) && @control_ports.fire1_high?
|
|
246
|
+
return if high == @lightpen_high
|
|
247
|
+
|
|
248
|
+
@lightpen_high = high
|
|
249
|
+
@vic.lightpen_level(high)
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
# The mains, low for half its period and high for the other half, on
|
|
253
|
+
# TPI1's I0, whose falling edge latches the tick.
|
|
254
|
+
def tick_mains
|
|
255
|
+
@mains_countdown -= 1
|
|
256
|
+
return unless @mains_countdown.zero?
|
|
257
|
+
|
|
258
|
+
@mains_countdown = @mains_half_period
|
|
259
|
+
@mains_high = !@mains_high
|
|
260
|
+
@tpi1.interrupt_line(0, @mains_high)
|
|
261
|
+
end
|
|
262
|
+
end
|
|
263
|
+
end
|
|
264
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
class CBM2
|
|
5
|
+
# Page $DA of segment 15: the SID's 32 registers, repeated over the
|
|
6
|
+
# page.
|
|
7
|
+
class SIDPage
|
|
8
|
+
def initialize(sid)
|
|
9
|
+
@sid = sid
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def peek(addr) = @sid.peek(0xda00 | (addr & 0x1f))
|
|
13
|
+
|
|
14
|
+
def poke(addr, value)
|
|
15
|
+
@sid.poke(0xda00 | (addr & 0x1f), value)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|