badline 0.16.1 → 0.17.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 +60 -0
- data/CLAUDE.md +1 -1
- data/README.md +102 -12
- data/doc/media.md +5 -2
- data/doc/pinned-behaviour.md +61 -0
- data/doc/snapshot-format.md +376 -69
- data/doc/snapshots.md +148 -14
- 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 +91 -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 +262 -0
- data/lib/badline/cbm2/sid_page.rb +19 -0
- data/lib/badline/cbm2.rb +267 -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/crtc.rb +3 -2
- 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 +80 -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 +132 -0
- 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/drives/datasette/keyed_state.rb +9 -1
- data/lib/badline/drives/datasette.rb +27 -3
- data/lib/badline/drives/model8050/disk.rb +163 -0
- data/lib/badline/drives/model8050/fdc_bus.rb +102 -0
- data/lib/badline/drives/model8050/idle.rb +177 -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 +160 -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 +243 -0
- data/lib/badline/frontend/app.rb +10 -3
- data/lib/badline/frontend/boot.rb +1 -1
- data/lib/badline/frontend/controls.rb +51 -15
- data/lib/badline/frontend/gamepads.rb +3 -3
- data/lib/badline/frontend/keys.rb +61 -2
- data/lib/badline/frontend/media_slots.rb +1 -1
- data/lib/badline/frontend/menu/menu_media.rb +14 -11
- data/lib/badline/frontend/menu/menu_pages.rb +18 -4
- data/lib/badline/frontend/menu/menu_ports.rb +40 -4
- data/lib/badline/frontend/menu/pause_menu.rb +1 -1
- data/lib/badline/frontend/pet_keyboard.rb +23 -7
- data/lib/badline/frontend/timeline.rb +19 -10
- 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/machine/ieee_drives.rb +194 -0
- data/lib/badline/machine/settling.rb +4 -0
- data/lib/badline/machine.rb +23 -6
- data/lib/badline/media/cbm2_media.rb +29 -0
- data/lib/badline/media/program.rb +6 -3
- data/lib/badline/media/true_drive.rb +38 -9
- data/lib/badline/media.rb +15 -6
- data/lib/badline/options/family.rb +15 -3
- data/lib/badline/options/help.rb +16 -2
- data/lib/badline/options/table.rb +18 -4
- data/lib/badline/options/validation.rb +27 -3
- data/lib/badline/options.rb +21 -7
- 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.rb +15 -4
- data/lib/badline/plus4/acia.rb +4 -298
- data/lib/badline/plus4/keyed_parts.rb +0 -44
- data/lib/badline/plus4.rb +1 -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/README +111 -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/snapshot/cbm2_setup.rb +53 -0
- data/lib/badline/snapshot/container.rb +7 -2
- data/lib/badline/snapshot/field_doc/markdown.rb +4 -2
- data/lib/badline/snapshot/field_doc.rb +38 -3
- data/lib/badline/snapshot/image.rb +62 -9
- 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 +7 -3
- data/lib/badline/snapshot/vice/pet_cpu.rb +5 -2
- data/lib/badline/snapshot/vice/pet_crtc.rb +10 -15
- data/lib/badline/snapshot/vice/pet_drive.rb +180 -0
- data/lib/badline/snapshot/vice/pet_drive8050.rb +216 -0
- data/lib/badline/snapshot/vice/pet_restore.rb +70 -6
- 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 +11 -3
- data/lib/badline/storage/crt_file.rb +7 -1
- data/lib/badline/storage/d64_image.rb +7 -0
- data/lib/badline/storage/d80_image.rb +17 -1
- data/lib/badline/storage/d81_image.rb +14 -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 +35 -0
- data/lib/badline/storage/disk_image.rb +2 -0
- data/lib/badline/storage.rb +1 -0
- data/lib/badline/support/cbm2_checkpoint.rb +48 -0
- data/lib/badline/support/p500_checkpoint.rb +35 -0
- data/lib/badline/version.rb +1 -1
- data/lib/badline.rb +1 -0
- metadata +72 -2
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
class CBM2
|
|
5
|
+
# Where the B series KERNALs, 901244-04a and -03b, keep the eight
|
|
6
|
+
# IEEE-488 primitives, their file variables in segment 15, and the
|
|
7
|
+
# steps they take on the bus's lines (KernalTrap::IEEELayout). TALK and
|
|
8
|
+
# LISTEN take the device in A.
|
|
9
|
+
#
|
|
10
|
+
# The KERNAL drives the bus from TPI1's port A at $DE00 (PA3 ATN, PA4
|
|
11
|
+
# DAV, PA5 EOI, PA6 NDAC, PA7 NRFD, PA0-PA2 the transceivers'
|
|
12
|
+
# directions) and the CIA's port A at $DC00, the data lines inverted.
|
|
13
|
+
# TALK and LISTEN set both ports' directions for sending and pull ATN
|
|
14
|
+
# low, TKSA and ACPTR turn NRFD, NDAC and the data lines round for
|
|
15
|
+
# listening, and each handshake times out on CIA timer B, which the
|
|
16
|
+
# ROM starts with $FF to $DC07 and $11 to $DC0F and acknowledges by
|
|
17
|
+
# reading $DC0D. ACPTR leaves port A as TKSA does, without touching
|
|
18
|
+
# the directions. 901244-03b differs from -04a in setting PA0 where -04a
|
|
19
|
+
# clears it, at the end of UNTALK, UNLISTEN and ACPTR.
|
|
20
|
+
module IEEELayout
|
|
21
|
+
LineStep = KernalTrap::IEEELineStep
|
|
22
|
+
|
|
23
|
+
# A write of +value+ to +address+.
|
|
24
|
+
def self.write(address, value) = LineStep.new(address:, keep: 0, set: value)
|
|
25
|
+
|
|
26
|
+
# +address+'s bits in +keep+, ORed with +set+.
|
|
27
|
+
def self.update(address, keep, set) = LineStep.new(address:, keep:, set:)
|
|
28
|
+
|
|
29
|
+
TIMER = [write(0xdc07, 0xff), write(0xdc0f, 0x11),
|
|
30
|
+
LineStep.new(address: 0xdc0d, keep: KernalTrap::IEEE_LINE_READ, set: 0)].freeze
|
|
31
|
+
ATTENTION = [write(0xde03, 0x3f), write(0xdc00, 0xff), write(0xdc02, 0xff), write(0xde00, 0xfa),
|
|
32
|
+
update(0xde00, 0xf7, 0)].freeze
|
|
33
|
+
BYTE_SENT = [update(0xde00, 0xff, 0x12), write(0xdc00, 0xff)].freeze
|
|
34
|
+
RELEASE_ATTENTION = [update(0xde00, 0xff, 0x08)].freeze
|
|
35
|
+
LISTEN_TO_TALKER = [update(0xde00, 0x39, 0), write(0xde03, 0xc7), write(0xdc02, 0x00)].freeze
|
|
36
|
+
|
|
37
|
+
# The end of UNTALK and UNLISTEN, which leaves PA $F8, or $F9 on
|
|
38
|
+
# 901244-03b, before letting ATN go, and the end of ACPTR, which
|
|
39
|
+
# leaves PA0 clear, or set on 901244-03b.
|
|
40
|
+
def self.lines(release)
|
|
41
|
+
KernalTrap::IEEELines.new(
|
|
42
|
+
timer: TIMER, attention: ATTENTION, byte_sent: BYTE_SENT, release_attention: RELEASE_ATTENTION,
|
|
43
|
+
listen_to_talker: LISTEN_TO_TALKER, byte_received: [update(0xde00, 0x39, release & 0x01)].freeze,
|
|
44
|
+
release: (ATTENTION + BYTE_SENT + [write(0xde00, release)] + LISTEN_TO_TALKER.drop(1) +
|
|
45
|
+
RELEASE_ATTENTION).freeze
|
|
46
|
+
)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# The file variables in segment 15's zero page: the file name's
|
|
50
|
+
# pointer at $90, SAVE's start at $99 and its end at $96, ST at $9C,
|
|
51
|
+
# FNLEN at $9D, FA at $9F, SA at $A0 and VERIFY at $035F.
|
|
52
|
+
ZERO_PAGE = KernalTrap::ZeroPage.new(
|
|
53
|
+
status: 0x9c, verify: 0x035f, start_address: 0x99, end_address: 0x96, filename_length: 0x9d,
|
|
54
|
+
secondary_address: 0xa0, device: 0x9f, filename_address: 0x90, save_start: 0x99, load_address: 0x99
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
def self.layout(release)
|
|
58
|
+
KernalTrap::IEEELayout.new(
|
|
59
|
+
talk: 0xf230, listen: 0xf234, second: 0xf274, tksa: 0xf280, ciout: 0xf297, untalk: 0xf2ab,
|
|
60
|
+
unlisten: 0xf2af, acptr: 0xf30a, overwrites_device: false, device_in_a: true, zero_page: ZERO_PAGE,
|
|
61
|
+
lines: lines(release)
|
|
62
|
+
)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# By the KERNAL's name in Model::KERNALS.
|
|
66
|
+
LAYOUTS = { "04a" => layout(0xf8), "03b" => layout(0xf9) }.freeze
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
class CBM2
|
|
5
|
+
# The CBM-II's side of the IEEE-488 bus (Drives::IEEEBus): the data
|
|
6
|
+
# lines DIO1-8 on the CIA's port A at $DC00, and the handshake on
|
|
7
|
+
# TPI1's port A at $DE00:
|
|
8
|
+
#
|
|
9
|
+
# PA3 ATN PA4 DAV PA5 EOI PA6 NDAC PA7 NRFD
|
|
10
|
+
#
|
|
11
|
+
# PA0-PA2 set the transceivers' directions, which follow the ports'
|
|
12
|
+
# direction registers as the KERNAL sets them. Each line goes through
|
|
13
|
+
# its transceiver as it is: an output bit at 0 pulls its line low, and
|
|
14
|
+
# an input bit reads its line, 0 while it is low. It is TPI1's
|
|
15
|
+
# peripheral, and Data the CIA's.
|
|
16
|
+
#
|
|
17
|
+
# The machine pushes the lines it pulls into the bus for the drives on
|
|
18
|
+
# it once a cycle while a drive is on the bus (#cycle_lines), and only
|
|
19
|
+
# when they moved.
|
|
20
|
+
class IEEEPorts
|
|
21
|
+
ATN = 0x08
|
|
22
|
+
DAV = 0x10
|
|
23
|
+
EOI = 0x20
|
|
24
|
+
NDAC = 0x40
|
|
25
|
+
NRFD = 0x80
|
|
26
|
+
|
|
27
|
+
# The CIA's port A: the data lines.
|
|
28
|
+
class Data
|
|
29
|
+
def initialize(ports)
|
|
30
|
+
@ports = ports
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def read_a(_port_a, _port_b) = ~@ports.low_lines & 0xff
|
|
34
|
+
|
|
35
|
+
def read_b(_port_a, _port_b) = 0xff
|
|
36
|
+
|
|
37
|
+
def port_b4_high? = true
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
attr_reader :bus, :data
|
|
41
|
+
|
|
42
|
+
def initialize(bus)
|
|
43
|
+
@bus = bus
|
|
44
|
+
@data = Data.new(self)
|
|
45
|
+
@cia = nil
|
|
46
|
+
@tpi = nil
|
|
47
|
+
@pushed = 0
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def connect(cia, tpi)
|
|
51
|
+
@cia = cia
|
|
52
|
+
@tpi = tpi
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# TPI1's port A: the handshake lines and ATN.
|
|
56
|
+
def read_a(_lines)
|
|
57
|
+
low = low_lines
|
|
58
|
+
value = 0xff
|
|
59
|
+
value &= ~ATN if low.anybits?(Drives::IEEEBus::ATN)
|
|
60
|
+
value &= ~DAV if low.anybits?(Drives::IEEEBus::DAV)
|
|
61
|
+
value &= ~EOI if low.anybits?(Drives::IEEEBus::EOI)
|
|
62
|
+
value &= ~NDAC if low.anybits?(Drives::IEEEBus::NDAC)
|
|
63
|
+
value &= ~NRFD if low.anybits?(Drives::IEEEBus::NRFD)
|
|
64
|
+
value & 0xff
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def read_b(_lines) = 0xff
|
|
68
|
+
|
|
69
|
+
def read_c(_lines) = 0xff
|
|
70
|
+
|
|
71
|
+
# The lines low on the bus.
|
|
72
|
+
def low_lines = @bus.low_lines(controller_lines)
|
|
73
|
+
|
|
74
|
+
# The drives on the bus see the lines the machine pulls now, if they
|
|
75
|
+
# moved.
|
|
76
|
+
def cycle_lines
|
|
77
|
+
lines = controller_lines
|
|
78
|
+
push(lines) unless lines == @pushed
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# The drives on the bus see the lines the machine pulls now.
|
|
82
|
+
def push_lines = push(controller_lines)
|
|
83
|
+
|
|
84
|
+
private
|
|
85
|
+
|
|
86
|
+
def push(lines)
|
|
87
|
+
@pushed = lines
|
|
88
|
+
@bus.host_lines = lines
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# The lines the machine pulls low.
|
|
92
|
+
def controller_lines
|
|
93
|
+
low = ~@cia.port_a_lines & Drives::IEEEBus::DIO
|
|
94
|
+
port_a = @tpi.port_a_output
|
|
95
|
+
low |= Drives::IEEEBus::ATN if port_a.nobits?(ATN)
|
|
96
|
+
low |= Drives::IEEEBus::DAV if port_a.nobits?(DAV)
|
|
97
|
+
low |= Drives::IEEEBus::EOI if port_a.nobits?(EOI)
|
|
98
|
+
low |= Drives::IEEEBus::NDAC if port_a.nobits?(NDAC)
|
|
99
|
+
low |= Drives::IEEEBus::NRFD if port_a.nobits?(NRFD)
|
|
100
|
+
low
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
class CBM2
|
|
5
|
+
# The disk device 8 serves through the traps on the KERNAL's IEEE-488
|
|
6
|
+
# primitives (KernalTrap::IEEE, IEEELayout), and CHROUT capture. There
|
|
7
|
+
# is no LOAD or SAVE trap: the ROM's own LOAD and SAVE, which move the
|
|
8
|
+
# bytes into the segment they name, and BASIC's disk commands run
|
|
9
|
+
# through the primitives. Other devices find only a true drive on the
|
|
10
|
+
# bus (IEEEDrive), and the KERNAL reports the rest not present.
|
|
11
|
+
module IEEETraps
|
|
12
|
+
# Puts the storage in device 8. Mounting again swaps the disk while
|
|
13
|
+
# the machine runs, and the drive keeps its RAM and its status. The
|
|
14
|
+
# traps leave device 8 to a true drive with that number.
|
|
15
|
+
def mount(storage)
|
|
16
|
+
return @dos.insert(storage) if @dos
|
|
17
|
+
|
|
18
|
+
@dos = dos = KernalTrap::DOS.new(storage, channel_saves: true)
|
|
19
|
+
@ieee_trap = KernalTrap::IEEE.new(cpu:, bus:, layout: trap_layout, dos:).install
|
|
20
|
+
@ieee_trap.device = trap_device
|
|
21
|
+
@ieee_trap
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Takes device 8's storage out, and the traps with it.
|
|
25
|
+
def unmount
|
|
26
|
+
trap = @ieee_trap
|
|
27
|
+
return unless trap
|
|
28
|
+
|
|
29
|
+
trap.uninstall
|
|
30
|
+
@ieee_trap = nil
|
|
31
|
+
@dos = nil
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Whether device 8 serves a disk or directory through the traps.
|
|
35
|
+
def mounted? = !@dos.nil?
|
|
36
|
+
|
|
37
|
+
# The path of the disk or directory device 8 serves, or an empty one.
|
|
38
|
+
def mounted_path = @dos.nil? ? "" : @dos.path
|
|
39
|
+
|
|
40
|
+
# Device 8 through the traps in a keyed snapshot module (BL-TRAPDOS):
|
|
41
|
+
# the storage under storage.*, the DOS, and the IEEE-488 trap under
|
|
42
|
+
# ieee.*. Nothing mounted writes nothing.
|
|
43
|
+
def save_trap_dos_fields(out)
|
|
44
|
+
dos = @dos
|
|
45
|
+
return unless dos
|
|
46
|
+
|
|
47
|
+
Snapshot::StorageSetup.write_fields(dos.storage, out)
|
|
48
|
+
dos.save_fields(out)
|
|
49
|
+
@ieee_trap&.save_fields(out, "ieee.")
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# `detached` mounts a disk image write-protected and no directory.
|
|
53
|
+
def load_trap_dos_fields(input, detached: false)
|
|
54
|
+
unmount
|
|
55
|
+
return if input.missing?
|
|
56
|
+
|
|
57
|
+
mount(Snapshot::StorageSetup.read_fields(input, detached:))
|
|
58
|
+
@dos&.load_fields(input)
|
|
59
|
+
@ieee_trap&.load_fields(input, "ieee.")
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Records what the KERNAL prints through CHROUT (KernalTrap::Chrout).
|
|
63
|
+
def capture_output
|
|
64
|
+
@capture_output ||= KernalTrap::Chrout.new(cpu:, bus:, layout: trap_layout).tap do |trap|
|
|
65
|
+
cpu.install_trap(KernalTrap::Chrout::ADDRESS) { trap.call }
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# The IEEELayout of the KERNAL the machine runs.
|
|
70
|
+
def trap_layout = IEEELayout::LAYOUTS.fetch(@kernal)
|
|
71
|
+
|
|
72
|
+
private
|
|
73
|
+
|
|
74
|
+
# Device 8, unless a true drive is device 8.
|
|
75
|
+
def trap_device
|
|
76
|
+
device = KernalTrap::Routine::DEVICE
|
|
77
|
+
true_drive&.device == device ? nil : device
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
class CBM2
|
|
5
|
+
# What TPI2's ports read from outside the chip: the key matrix and the
|
|
6
|
+
# board's jumpers. The KERNAL drives the matrix's sixteen column lines
|
|
7
|
+
# low one at a time from PB0-PB7 and PA0-PA7 and reads its six row
|
|
8
|
+
# lines on PC0-PC5, a 0 for each key down. PC6 and PC7 read the
|
|
9
|
+
# jumpers: PC7 high on a 700, whose editor then loads its own CRTC
|
|
10
|
+
# table, and PC6 high on a 600 at 60 Hz.
|
|
11
|
+
#
|
|
12
|
+
# MATRIX lists the keys on each column line, PB0 first, in row order,
|
|
13
|
+
# :none where no key sits, as the KERNAL's key table at $EA29 places
|
|
14
|
+
# them, named as the B series' keyboard matrix sheet labels them.
|
|
15
|
+
class KeyboardPorts
|
|
16
|
+
MATRIX = [
|
|
17
|
+
%i[f1 escape tab none shift control],
|
|
18
|
+
%i[f2 1 q a z none],
|
|
19
|
+
%i[f3 2 w s x c],
|
|
20
|
+
%i[f4 3 e d f v],
|
|
21
|
+
%i[f5 4 r t g b],
|
|
22
|
+
%i[f6 5 6 y h n],
|
|
23
|
+
%i[f7 7 u j m space],
|
|
24
|
+
%i[f8 8 i k , .],
|
|
25
|
+
%i[f9 9 o l ; /],
|
|
26
|
+
[:f10, :"0", :-, :p, :"[", :"'"],
|
|
27
|
+
[:cursor_down, :"=", :left_arrow, :"]", :return, :pi],
|
|
28
|
+
%i[cursor_up cursor_left cursor_right delete cbm none],
|
|
29
|
+
%i[clr_home kp_question kp7 kp4 kp1 kp0],
|
|
30
|
+
%i[off_rvs ce kp8 kp5 kp2 kp_period],
|
|
31
|
+
%i[norm_graph kp_multiply kp9 kp6 kp3 kp00],
|
|
32
|
+
%i[run_stop kp_divide kp_minus kp_plus kp_enter none]
|
|
33
|
+
].freeze
|
|
34
|
+
|
|
35
|
+
attr_reader :keyboard
|
|
36
|
+
|
|
37
|
+
# +jumpers+ is PC6 and PC7 as the board's jumpers drive them.
|
|
38
|
+
def initialize(keyboard, jumpers)
|
|
39
|
+
@keyboard = keyboard
|
|
40
|
+
@jumpers = jumpers
|
|
41
|
+
@tpi = nil
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# The TPI whose ports drive the column lines.
|
|
45
|
+
def connect(tpi)
|
|
46
|
+
@tpi = tpi
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def read_a(lines) = lines
|
|
50
|
+
|
|
51
|
+
def read_b(lines) = lines
|
|
52
|
+
|
|
53
|
+
def read_c(lines)
|
|
54
|
+
columns = (@tpi.port_a_output << 8) | @tpi.port_b_output
|
|
55
|
+
rows = @keyboard.scan(columns, lines | 0x3f).last
|
|
56
|
+
(rows & 0x3f) | (@jumpers & lines & 0xc0)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
class CBM2
|
|
5
|
+
# The whole machine as keyed snapshot modules (Snapshot::Keyed), in the
|
|
6
|
+
# order a load applies them: BL-MACHINE with the setup, the RAM with
|
|
7
|
+
# the segment registers, the CRTC, the picture, the SID, the CIA, the ACIA, both
|
|
8
|
+
# TPIs, the CPU, device 8 through the traps and a true 2031 on the
|
|
9
|
+
# IEEE-488 bus.
|
|
10
|
+
#
|
|
11
|
+
# A load lays the pages out before the chips load, and works out what
|
|
12
|
+
# each unit derives from its fields once every unit is loaded.
|
|
13
|
+
module KeyedModules
|
|
14
|
+
def save_modules
|
|
15
|
+
bus = @bus
|
|
16
|
+
sections = [Snapshot::Keyed.section("BL-MACHINE") { |out| save_machine_fields(out) }]
|
|
17
|
+
sections << Snapshot::Keyed.section("BL-CPU") { |out| @cpu.save_fields(out) }
|
|
18
|
+
sections << Snapshot::Keyed.section("BL-RAM") { |out| bus.save_fields(out) }
|
|
19
|
+
sections << Snapshot::Keyed.section("BL-CRTC") { |out| @crtc.save_fields(out) }
|
|
20
|
+
sections << Snapshot::Keyed.section("BL-VIDEO") { |out| @video.save_fields(out) }
|
|
21
|
+
sections << Snapshot::Keyed.section("BL-SID") { |out| @sid.save_fields(out) }
|
|
22
|
+
sections << Snapshot::Keyed.section("BL-CIA") { |out| @cia.save_fields(out) }
|
|
23
|
+
sections << Snapshot::Keyed.section("BL-ACIA") { |out| @acia.save_fields(out) }
|
|
24
|
+
sections << Snapshot::Keyed.section("BL-TPI1") { |out| @tpi1.save_fields(out) }
|
|
25
|
+
sections << Snapshot::Keyed.section("BL-TPI2") { |out| @tpi2.save_fields(out) }
|
|
26
|
+
sections << Snapshot::Keyed.section("BL-TRAPDOS") { |out| save_trap_dos_fields(out) } if mounted?
|
|
27
|
+
save_drive_modules(sections)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Raises Snapshot::FormatError for the modules of a machine built
|
|
31
|
+
# another way.
|
|
32
|
+
def load_modules(modules)
|
|
33
|
+
check_snapshot_setup(Snapshot::CBM2Setup.read_fields(modules.machine))
|
|
34
|
+
load_machine_fields(modules.machine)
|
|
35
|
+
@bus.load_fields(modules.required("BL-RAM"))
|
|
36
|
+
@bus.restored!
|
|
37
|
+
@crtc.load_fields(modules.required("BL-CRTC"))
|
|
38
|
+
@video.load_fields(modules.required("BL-VIDEO"))
|
|
39
|
+
@sid.load_fields(modules.required("BL-SID"))
|
|
40
|
+
@cia.load_fields(modules.required("BL-CIA"))
|
|
41
|
+
@acia.load_fields(modules.required("BL-ACIA"))
|
|
42
|
+
@tpi1.load_fields(modules.required("BL-TPI1"))
|
|
43
|
+
@tpi2.load_fields(modules.required("BL-TPI2"))
|
|
44
|
+
@cpu.load_fields(modules.required("BL-CPU"))
|
|
45
|
+
trap_dos = modules.optional("BL-TRAPDOS")
|
|
46
|
+
trap_dos ? load_trap_dos_fields(trap_dos, detached: modules.detached?) : unmount
|
|
47
|
+
load_drive_modules(modules)
|
|
48
|
+
@cpu.restored!
|
|
49
|
+
@sid.restored!
|
|
50
|
+
@cia.restored!
|
|
51
|
+
@acia.restored!
|
|
52
|
+
self
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
private
|
|
56
|
+
|
|
57
|
+
def check_snapshot_setup(setup)
|
|
58
|
+
ours = Snapshot::CBM2Setup.of(self)
|
|
59
|
+
return if setup == ours
|
|
60
|
+
|
|
61
|
+
raise Snapshot::FormatError, "the snapshot is of a #{setup}, not a #{ours}"
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
include KeyedModules
|
|
66
|
+
end
|
|
67
|
+
end
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
class CBM2
|
|
5
|
+
class Bus
|
|
6
|
+
# Segment 15's RAM, the screen RAM, the RAM board's segments, both
|
|
7
|
+
# segment registers, the last byte on the bus and a cartridge's ROMs
|
|
8
|
+
# by load address, in a keyed snapshot module (BL-RAM). The pages are
|
|
9
|
+
# laid out again once loaded (#restored!). The BASIC and KERNAL ROMs
|
|
10
|
+
# come with the machine.
|
|
11
|
+
module KeyedState
|
|
12
|
+
def save_fields(out)
|
|
13
|
+
@ram.save_fields(out, "system")
|
|
14
|
+
@screen.save_fields(out, "screen")
|
|
15
|
+
@segment_ram.each_with_index { |memory, segment| memory&.save_fields(out, "segment#{segment}") }
|
|
16
|
+
out.int("execution_segment", @execution_segment).int("indirect_segment", @indirect_segment)
|
|
17
|
+
out.int("data", @data)
|
|
18
|
+
out.ints("cartridge.addresses", @cartridge.map(&:start))
|
|
19
|
+
@cartridge.each { |rom| out.bytes("cartridge.#{rom.start}", rom.snapshot) }
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def load_fields(input)
|
|
23
|
+
@ram.load_fields(input, "system")
|
|
24
|
+
@screen.load_fields(input, "screen")
|
|
25
|
+
@segment_ram.each_with_index { |memory, segment| memory&.load_fields(input, "segment#{segment}") }
|
|
26
|
+
@execution_segment = input.masked("execution_segment", @execution_segment, 0x0f)
|
|
27
|
+
@indirect_segment = input.masked("indirect_segment", @indirect_segment, 0x0f)
|
|
28
|
+
@data = input.byte("data", @data)
|
|
29
|
+
@cartridge = input.ints("cartridge.addresses", []).filter_map { |address| cartridge_rom(input, address) }
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def restored! = map_pages
|
|
33
|
+
|
|
34
|
+
private
|
|
35
|
+
|
|
36
|
+
# The cartridge ROM saved for +address+, in $1000-$7FFF of segment
|
|
37
|
+
# 15, or nil.
|
|
38
|
+
def cartridge_rom(input, address)
|
|
39
|
+
data = input.bytes("cartridge.#{address}", "".b).bytes
|
|
40
|
+
return nil if data.empty? || address < 0x1000 || address + data.length > 0x8000
|
|
41
|
+
|
|
42
|
+
ROM.new(data, length: data.length, start: address)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
include KeyedState
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# The machine's own fields in a keyed snapshot module (Snapshot::Keyed,
|
|
50
|
+
# BL-MACHINE): the family, its setup by name (Snapshot::CBM2Setup), the
|
|
51
|
+
# cycle count, where the mains is in its period, the refresh counter,
|
|
52
|
+
# and the host's typing
|
|
53
|
+
# still to feed and on_init handlers still to run under host.*. The
|
|
54
|
+
# badline version that wrote it is written for reports and never read.
|
|
55
|
+
module KeyedState
|
|
56
|
+
# How many more on_init handlers the machine a snapshot was taken of had
|
|
57
|
+
# yet to run than this one has. Nil until the machine is restored.
|
|
58
|
+
attr_reader :init_handlers_lost
|
|
59
|
+
|
|
60
|
+
# Whether a snapshot saves now (Machine::Settling): the CPU between
|
|
61
|
+
# instructions or jammed.
|
|
62
|
+
def settled_for_snapshot? = @cpu.boundary? || @cpu.jammed?
|
|
63
|
+
|
|
64
|
+
# The SID's deferred cycles and writes, run up to this cycle.
|
|
65
|
+
def settle_chips_for_snapshot = @sid.catch_up!
|
|
66
|
+
|
|
67
|
+
def save_machine_fields(out)
|
|
68
|
+
out.text("family", "cbm2").text("written_by", VERSION)
|
|
69
|
+
Snapshot::CBM2Setup.of(self).write_fields(out)
|
|
70
|
+
out.int("cycles", @cycles).int("mains_countdown", @mains_countdown).bool("mains_high", @mains_high)
|
|
71
|
+
out.int("refresh", @refresh)
|
|
72
|
+
pending = @pending_keys
|
|
73
|
+
out.ints("host.pending_keys", pending) if pending
|
|
74
|
+
out.int("host.init_pending", @cycles <= @init_threshold ? @init_handlers.length : 0)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def load_machine_fields(input)
|
|
78
|
+
@cycles = input.int("cycles", @cycles)
|
|
79
|
+
countdown = input.int("mains_countdown", @mains_countdown)
|
|
80
|
+
@mains_countdown = countdown.between?(1, @mains_half_period) ? countdown : @mains_half_period
|
|
81
|
+
@mains_high = input.bool("mains_high", @mains_high)
|
|
82
|
+
@refresh = input.int("refresh", 0).clamp(0, REFRESH_COUNT)
|
|
83
|
+
pending = input.ints("host.pending_keys", [])
|
|
84
|
+
@pending_keys = pending.empty? ? nil : pending.dup
|
|
85
|
+
@init_handlers_lost = [input.int("host.init_pending", 0) - @init_handlers.length, 0].max
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
include KeyedState
|
|
90
|
+
end
|
|
91
|
+
end
|
|
@@ -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
|