badline 0.17.0 → 0.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +193 -0
- data/CLAUDE.md +19 -7
- data/CONTRIBUTING.md +11 -1
- data/README.md +178 -26
- data/doc/library.md +5 -1
- data/doc/media.md +3 -2
- data/doc/pinned-behaviour.md +169 -1
- data/doc/snapshot-format.md +302 -35
- data/doc/snapshots.md +97 -26
- data/doc/vice.md +344 -0
- data/exe/badline-ruby +5 -0
- data/lib/badline/audio/renderer.rb +4 -8
- data/lib/badline/audio/terminal.rb +34 -2
- data/lib/badline/c128/keyed_state.rb +2 -0
- data/lib/badline/c128.rb +5 -0
- data/lib/badline/c64/bus/fittings.rb +6 -0
- data/lib/badline/c64/keyed_state.rb +2 -0
- data/lib/badline/c64.rb +7 -0
- data/lib/badline/cbm2/keyed_state.rb +4 -0
- data/lib/badline/cbm2/p500.rb +9 -3
- data/lib/badline/cbm2.rb +10 -3
- data/lib/badline/chips/cia/timer.rb +2 -2
- data/lib/badline/chips/cpu/core.rb +29 -4
- data/lib/badline/chips/cpu/instruction_set.rb +1 -1
- data/lib/badline/chips/cpu/interrupts.rb +9 -0
- data/lib/badline/chips/cpu/keyed_state.rb +15 -9
- data/lib/badline/chips/cpu/microcode.rb +10 -0
- data/lib/badline/chips/m6809/arithmetic.rb +140 -0
- data/lib/badline/chips/m6809/bus_steps.rb +223 -0
- data/lib/badline/chips/m6809/core.rb +149 -0
- data/lib/badline/chips/m6809/indexed.rb +82 -0
- data/lib/badline/chips/m6809/interrupts.rb +159 -0
- data/lib/badline/chips/m6809/keyed_state.rb +147 -0
- data/lib/badline/chips/m6809/opcodes.rb +159 -0
- data/lib/badline/chips/m6809/operations.rb +258 -0
- data/lib/badline/chips/m6809/plans.rb +143 -0
- data/lib/badline/chips/m6809/registers.rb +82 -0
- data/lib/badline/chips/m6809/stack.rb +140 -0
- data/lib/badline/chips/m6809.rb +13 -0
- data/lib/badline/chips/riot/timer.rb +14 -3
- data/lib/badline/chips/rriot.rb +8 -3
- data/lib/badline/chips/sid/decimator.rb +1 -1
- data/lib/badline/chips/sid/filter.rb +4 -4
- data/lib/badline/chips/sid/keyed_parts.rb +51 -18
- data/lib/badline/chips/sid/waveform.rb +6 -6
- data/lib/badline/chips/sid.rb +6 -2
- data/lib/badline/chips/vic_i/keyed_parts.rb +12 -2
- data/lib/badline/chips/vic_i/light_pen.rb +89 -0
- data/lib/badline/chips/vic_i.rb +91 -23
- data/lib/badline/data_path.rb +3 -3
- data/lib/badline/drives/ieee_models.rb +54 -0
- data/lib/badline/drives/model1001.rb +37 -0
- data/lib/badline/drives/model1541/sector_reader.rb +20 -3
- data/lib/badline/drives/model1541.rb +1 -0
- data/lib/badline/drives/model1551.rb +1 -0
- data/lib/badline/drives/model1571.rb +1 -0
- data/lib/badline/drives/model1581.rb +1 -0
- data/lib/badline/drives/model2031.rb +7 -0
- data/lib/badline/drives/model4040/disk.rb +62 -0
- data/lib/badline/drives/model4040/ip_bus.rb +46 -0
- data/lib/badline/drives/model4040/keyed_state.rb +57 -0
- data/lib/badline/drives/model4040/mechanism.rb +195 -0
- data/lib/badline/drives/model4040/parts.rb +20 -0
- data/lib/badline/drives/model4040.rb +195 -0
- data/lib/badline/drives/model8050/board.rb +79 -0
- data/lib/badline/drives/model8050/dos_board.rb +134 -0
- data/lib/badline/drives/model8050/fdc_bus.rb +7 -0
- data/lib/badline/drives/model8050/idle.rb +148 -31
- data/lib/badline/drives/model8050/keyed_state.rb +5 -2
- data/lib/badline/drives/model8050.rb +32 -130
- data/lib/badline/drives/model9060/idle.rb +169 -0
- data/lib/badline/drives/model9060/keyed_state.rb +123 -0
- data/lib/badline/drives/model9060/parts.rb +98 -0
- data/lib/badline/drives/model9060/sasi_bus.rb +88 -0
- data/lib/badline/drives/model9060/sasi_commands.rb +168 -0
- data/lib/badline/drives/model9060/sasi_target.rb +161 -0
- data/lib/badline/drives/model9060.rb +197 -0
- data/lib/badline/frontend/app.rb +8 -0
- data/lib/badline/frontend/boot.rb +3 -2
- data/lib/badline/frontend/controls.rb +62 -6
- data/lib/badline/frontend/keys.rb +36 -0
- data/lib/badline/frontend/kim1_panel.rb +208 -0
- data/lib/badline/frontend/media_slots.rb +8 -1
- data/lib/badline/frontend/menu/drive_types.rb +56 -0
- data/lib/badline/frontend/menu/menu_dialogs.rb +61 -14
- data/lib/badline/frontend/menu/menu_media.rb +42 -12
- data/lib/badline/frontend/menu/menu_pages.rb +97 -2
- data/lib/badline/frontend/menu/menu_ports.rb +38 -1
- data/lib/badline/frontend/menu/pause_menu.rb +5 -1
- data/lib/badline/frontend/pet_keyboard.rb +23 -1
- data/lib/badline/frontend/screen.rb +1 -1
- data/lib/badline/frontend/screens.rb +40 -2
- data/lib/badline/frontend/teletype_pane.rb +152 -0
- data/lib/badline/frontend/timeline.rb +31 -2
- data/lib/badline/frontend.rb +3 -0
- data/lib/badline/input/joystick.rb +15 -2
- data/lib/badline/kim1/bus.rb +117 -0
- data/lib/badline/kim1/cpu.rb +25 -0
- data/lib/badline/kim1/host_terminal.rb +74 -0
- data/lib/badline/kim1/key_entry.rb +74 -0
- data/lib/badline/kim1/keyed_modules.rb +53 -0
- data/lib/badline/kim1/keyed_state.rb +133 -0
- data/lib/badline/kim1/panel.rb +176 -0
- data/lib/badline/kim1/rriots.rb +28 -0
- data/lib/badline/kim1/sound.rb +29 -0
- data/lib/badline/kim1/tape.rb +118 -0
- data/lib/badline/kim1/teletype.rb +193 -0
- data/lib/badline/kim1.rb +194 -0
- data/lib/badline/machine/ieee_drives.rb +94 -117
- data/lib/badline/machine/keyboard_buffer.rb +20 -3
- data/lib/badline/machine/line_sound.rb +122 -0
- data/lib/badline/machine.rb +49 -15
- data/lib/badline/media/extensions.rb +8 -4
- data/lib/badline/media/kim1_media.rb +150 -0
- data/lib/badline/media/pet_media.rb +3 -1
- data/lib/badline/media/true_drive.rb +52 -45
- data/lib/badline/media/vic20_media.rb +4 -2
- data/lib/badline/media.rb +16 -6
- data/lib/badline/memory.rb +2 -4
- data/lib/badline/options/family.rb +98 -8
- data/lib/badline/options/help.rb +44 -12
- data/lib/badline/options/table.rb +44 -12
- data/lib/badline/options/validation.rb +44 -8
- data/lib/badline/options.rb +27 -8
- data/lib/badline/pet/bus.rb +16 -2
- data/lib/badline/pet/keyboard_ports.rb +9 -2
- data/lib/badline/pet/keyed_state.rb +4 -0
- data/lib/badline/pet/model.rb +11 -2
- data/lib/badline/pet/rom_sets.rb +1 -0
- data/lib/badline/pet/sound.rb +5 -105
- data/lib/badline/pet/superpet/acia.rb +12 -0
- data/lib/badline/pet/superpet/board.rb +221 -0
- data/lib/badline/pet/superpet/dongle.rb +92 -0
- data/lib/badline/pet/superpet/keyed_modules.rb +63 -0
- data/lib/badline/pet/superpet/keyed_parts.rb +76 -0
- data/lib/badline/pet/superpet/m6809.rb +32 -0
- data/lib/badline/pet/superpet/waterloo_traps.rb +199 -0
- data/lib/badline/pet/superpet.rb +221 -0
- data/lib/badline/pet.rb +23 -3
- data/lib/badline/plus4/keyed_state.rb +2 -0
- data/lib/badline/plus4.rb +5 -0
- data/lib/badline/roms/1001/fdc-251257-02a.rom +0 -0
- data/lib/badline/roms/4040/dos-1.2.rom +0 -0
- data/lib/badline/roms/4040/dos-2.1-rev.rom +0 -0
- data/lib/badline/roms/4040/dos-2.1.rom +0 -0
- data/lib/badline/roms/4040/fdc-901466-02.rom +0 -0
- data/lib/badline/roms/4040/fdc-901466-04.rom +0 -0
- data/lib/badline/roms/9060/dos-3.0.rom +0 -0
- data/lib/badline/roms/9060/sasi-300515-revb.rom +0 -0
- data/lib/badline/roms/README +176 -0
- data/lib/badline/roms/kim1/6530-002.rom +0 -0
- data/lib/badline/roms/kim1/6530-003.rom +0 -0
- data/lib/badline/roms/pet/characters-superpet.rom +0 -0
- data/lib/badline/roms/pet/superpet-a000.rom +0 -0
- data/lib/badline/roms/pet/superpet-c000.rom +0 -0
- data/lib/badline/roms/pet/superpet-e000-50hz.rom +0 -0
- data/lib/badline/roms/pet/superpet-e000.rom +0 -0
- data/lib/badline/snapshot/container.rb +8 -4
- data/lib/badline/snapshot/field_doc/markdown.rb +1 -1
- data/lib/badline/snapshot/field_doc.rb +35 -10
- data/lib/badline/snapshot/image.rb +19 -7
- data/lib/badline/snapshot/kim1_setup.rb +28 -0
- data/lib/badline/snapshot/pet_setup.rb +27 -11
- data/lib/badline/snapshot/storage_setup.rb +4 -1
- data/lib/badline/snapshot/vic20_setup.rb +11 -7
- data/lib/badline/snapshot/vice/pet.rb +14 -7
- data/lib/badline/snapshot/vice/pet_cpu.rb +39 -5
- data/lib/badline/snapshot/vice/pet_crtc.rb +15 -2
- data/lib/badline/snapshot/vice/pet_dongle.rb +72 -0
- data/lib/badline/snapshot/vice/pet_drive8050.rb +188 -68
- data/lib/badline/snapshot/vice/pet_mem.rb +55 -5
- data/lib/badline/snapshot/vice/pet_restore.rb +40 -11
- data/lib/badline/snapshot/vice/vic20.rb +70 -0
- data/lib/badline/snapshot/vice/vic20_cpu.rb +61 -0
- data/lib/badline/snapshot/vice/vic20_mem.rb +85 -0
- data/lib/badline/snapshot/vice/vic20_restore.rb +124 -0
- data/lib/badline/snapshot/vice/vic20_vici.rb +200 -0
- data/lib/badline/snapshot/vice.rb +1 -0
- data/lib/badline/snapshot.rb +8 -4
- data/lib/badline/status.rb +30 -30
- data/lib/badline/storage/d67_image.rb +55 -0
- data/lib/badline/storage/d90_image.rb +180 -0
- data/lib/badline/storage/kim_tape.rb +254 -0
- data/lib/badline/storage/papertape.rb +90 -0
- data/lib/badline/storage.rb +4 -0
- data/lib/badline/support/kim1_checkpoint.rb +41 -0
- data/lib/badline/support/superpet_checkpoint.rb +35 -0
- data/lib/badline/version.rb +1 -1
- data/lib/badline/vic20/keyed_modules.rb +6 -3
- data/lib/badline/vic20/keyed_state.rb +6 -4
- data/lib/badline/vic20/port_wiring.rb +11 -0
- data/lib/badline/vic20.rb +30 -12
- data/lib/badline.rb +1 -0
- metadata +85 -1
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
class PET
|
|
5
|
+
class SuperPET < PET
|
|
6
|
+
# The traps on the Waterloo 6809 ROM's IEEE-488 routines, which serve
|
|
7
|
+
# device 8 from the virtual drive (KernalTrap::DOS) the 6502's traps
|
|
8
|
+
# serve it from, after the ROM's source as Mike Naberezny hosts it
|
|
9
|
+
# (opsys-all-clean-20101210-src.txt). Other devices fall through to
|
|
10
|
+
# the ROM, which drives the bus.
|
|
11
|
+
#
|
|
12
|
+
# The routines take their argument in D and return with RTS: SetLstnr
|
|
13
|
+
# and SetTalkr a file control block, whose byte 9 is the device, byte
|
|
14
|
+
# 10 the secondary address and byte 0 the status, 3 after a timeout,
|
|
15
|
+
# SetLstnr with the command's offset pushed before the call ($F0 to
|
|
16
|
+
# open, $E0 to close, $60 for data), ByteOut_ the byte to send and
|
|
17
|
+
# the byte-in routine none, returning the byte in D and setting bit 0
|
|
18
|
+
# of the block's byte 12 at EOI. Each keeps the block's address at
|
|
19
|
+
# $02 and the bus at $77-$7A: $20 listening or $40 talking, the
|
|
20
|
+
# offset, the device and the secondary address. A language that hooks
|
|
21
|
+
# a routine through its flag at $0580 gets the ROM's routine, which
|
|
22
|
+
# calls the hook.
|
|
23
|
+
#
|
|
24
|
+
# The menu loads from "disk/1.", drive 1 of a dual drive. The virtual
|
|
25
|
+
# drive has one, so a name on drive 1 opens the file on drive 0.
|
|
26
|
+
class WaterlooTraps
|
|
27
|
+
BYTE_OUT = 0xbe6f
|
|
28
|
+
BYTE_IN = 0xbefb
|
|
29
|
+
SET_LISTENER = 0xc072
|
|
30
|
+
UNLISTEN = 0xc0d0
|
|
31
|
+
SET_TALKER = 0xc0dd
|
|
32
|
+
UNTALK = 0xc13c
|
|
33
|
+
|
|
34
|
+
# The routines' user hook numbers, whose flags sit at $0580 on.
|
|
35
|
+
HOOKS = { BYTE_OUT => 23, BYTE_IN => 24, SET_LISTENER => 27, UNLISTEN => 28, SET_TALKER => 29,
|
|
36
|
+
UNTALK => 30 }.freeze
|
|
37
|
+
|
|
38
|
+
BLOCK = 0x02
|
|
39
|
+
ACTIVE = 0x77
|
|
40
|
+
LISTENING = 0x20
|
|
41
|
+
TALKING = 0x40
|
|
42
|
+
TIMED_OUT = 3
|
|
43
|
+
|
|
44
|
+
OPEN = 0xf0
|
|
45
|
+
CLOSE = 0xe0
|
|
46
|
+
|
|
47
|
+
# The device the traps serve, or nil to serve none.
|
|
48
|
+
attr_accessor :device
|
|
49
|
+
|
|
50
|
+
def initialize(cpu:, bus:, dos:)
|
|
51
|
+
@cpu = cpu
|
|
52
|
+
@bus = bus
|
|
53
|
+
@dos = dos
|
|
54
|
+
@device = KernalTrap::Routine::DEVICE
|
|
55
|
+
@listening = false
|
|
56
|
+
@talking = false
|
|
57
|
+
@channel = 0
|
|
58
|
+
@frame = 0
|
|
59
|
+
@buffer = []
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def install
|
|
63
|
+
HOOKS.each_key { |address| @cpu.install_trap(address) { call(address) } }
|
|
64
|
+
self
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def uninstall
|
|
68
|
+
HOOKS.each_key { |address| @cpu.remove_trap(address) }
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Whether a served device listens and talks, the channel and the
|
|
72
|
+
# command, and the bytes sent so far, for a checkpoint.
|
|
73
|
+
def state = [@listening ? 1 : 0, @talking ? 1 : 0, @channel, @frame, *@buffer]
|
|
74
|
+
|
|
75
|
+
private
|
|
76
|
+
|
|
77
|
+
def call(address)
|
|
78
|
+
return unless @bus.peek(0x0580 + HOOKS.fetch(address)).zero?
|
|
79
|
+
|
|
80
|
+
case address
|
|
81
|
+
when SET_LISTENER then set_listener
|
|
82
|
+
when BYTE_OUT then byte_out
|
|
83
|
+
when UNLISTEN then unlisten
|
|
84
|
+
when SET_TALKER then set_talker
|
|
85
|
+
when BYTE_IN then byte_in
|
|
86
|
+
else untalk
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def served?(block) = !@device.nil? && @bus.peek(block + 9) == @device
|
|
91
|
+
|
|
92
|
+
def set_listener
|
|
93
|
+
block = @cpu.d
|
|
94
|
+
return unless served?(block)
|
|
95
|
+
|
|
96
|
+
offset = @bus.peek(@cpu.s + 3)
|
|
97
|
+
select(block, LISTENING, offset)
|
|
98
|
+
@listening = true
|
|
99
|
+
@frame = offset & 0xf0
|
|
100
|
+
@buffer = []
|
|
101
|
+
@dos.close(@channel) if @frame == CLOSE
|
|
102
|
+
return_from_routine
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def set_talker
|
|
106
|
+
block = @cpu.d
|
|
107
|
+
return unless served?(block)
|
|
108
|
+
|
|
109
|
+
select(block, TALKING, 0x60)
|
|
110
|
+
@talking = true
|
|
111
|
+
return_from_routine
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# Ends what the bus was doing, then addresses the block's device as
|
|
115
|
+
# the routine does.
|
|
116
|
+
def select(block, active, offset)
|
|
117
|
+
finish_listening if @listening
|
|
118
|
+
@talking = false
|
|
119
|
+
@bus.poke(BLOCK, block >> 8)
|
|
120
|
+
@bus.poke(BLOCK + 1, block & 0xff)
|
|
121
|
+
@bus.poke(ACTIVE, active)
|
|
122
|
+
@bus.poke(ACTIVE + 1, offset)
|
|
123
|
+
@bus.poke(ACTIVE + 2, @bus.peek(block + 9))
|
|
124
|
+
secondary = @bus.peek(block + 10)
|
|
125
|
+
@bus.poke(ACTIVE + 3, secondary)
|
|
126
|
+
@channel = secondary & 0x0f
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def byte_out
|
|
130
|
+
return unless @listening
|
|
131
|
+
|
|
132
|
+
block = current_block
|
|
133
|
+
if @bus.peek(block).zero?
|
|
134
|
+
@buffer << (@cpu.d & 0xff)
|
|
135
|
+
@bus.poke(block, TIMED_OUT) unless accepted?
|
|
136
|
+
end
|
|
137
|
+
@cpu.d = 0x00ff
|
|
138
|
+
return_from_routine
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def accepted? = [OPEN, CLOSE].include?(@frame) || @dos.listening?(@channel)
|
|
142
|
+
|
|
143
|
+
def unlisten
|
|
144
|
+
return unless @listening
|
|
145
|
+
|
|
146
|
+
finish_listening
|
|
147
|
+
@bus.poke(ACTIVE, 0)
|
|
148
|
+
return_from_routine
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def finish_listening
|
|
152
|
+
@listening = false
|
|
153
|
+
case @frame
|
|
154
|
+
when OPEN then @dos.open(@channel, on_drive_zero(@buffer.pack("C*")))
|
|
155
|
+
when CLOSE then nil
|
|
156
|
+
else @dos.write(@channel, @buffer)
|
|
157
|
+
end
|
|
158
|
+
@buffer = []
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def on_drive_zero(name) = name.start_with?("1:") ? "0:#{name[2..]}" : name
|
|
162
|
+
|
|
163
|
+
def byte_in
|
|
164
|
+
return unless @talking
|
|
165
|
+
|
|
166
|
+
block = current_block
|
|
167
|
+
byte = 0x0d
|
|
168
|
+
if @bus.peek(block).zero?
|
|
169
|
+
read, eoi = @dos.read(@channel)
|
|
170
|
+
if read.nil?
|
|
171
|
+
@bus.poke(block, TIMED_OUT)
|
|
172
|
+
else
|
|
173
|
+
byte = read
|
|
174
|
+
@bus.poke(block + 12, @bus.peek(block + 12) | 0x01) if eoi
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
@cpu.d = byte
|
|
178
|
+
return_from_routine
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def untalk
|
|
182
|
+
return unless @talking
|
|
183
|
+
|
|
184
|
+
@talking = false
|
|
185
|
+
@bus.poke(ACTIVE, 0)
|
|
186
|
+
return_from_routine
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
def current_block = (@bus.peek(BLOCK) << 8) | @bus.peek(BLOCK + 1)
|
|
190
|
+
|
|
191
|
+
def return_from_routine
|
|
192
|
+
s = @cpu.s
|
|
193
|
+
@cpu.pc = (@bus.peek(s) << 8) | @bus.peek(s + 1)
|
|
194
|
+
@cpu.s = (s + 2) & 0xffff
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
end
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "badline/chips/acia"
|
|
4
|
+
require "badline/chips/m6809"
|
|
5
|
+
require "badline/pet/superpet/dongle"
|
|
6
|
+
require "badline/pet/superpet/acia"
|
|
7
|
+
require "badline/pet/superpet/board"
|
|
8
|
+
require "badline/pet/superpet/m6809"
|
|
9
|
+
require "badline/pet/superpet/waterloo_traps"
|
|
10
|
+
require "badline/pet/superpet/keyed_parts"
|
|
11
|
+
require "badline/pet/superpet/keyed_modules"
|
|
12
|
+
|
|
13
|
+
module Badline
|
|
14
|
+
class PET
|
|
15
|
+
# The SuperPET, the SP9000 or MMF 9000: an 8032 with the University of
|
|
16
|
+
# Waterloo's board in its CPU socket, with 64K of bank RAM, a 6551
|
|
17
|
+
# ACIA, the 6702 and a 6809E (Board). The board's ACIA pulls the PET's
|
|
18
|
+
# IRQ line with the PIAs and the VIA. Its character ROM, 901640-01,
|
|
19
|
+
# holds the 8032's in its first half and an ASCII and an APL set in
|
|
20
|
+
# its second, which the CRTC's MA13 picks.
|
|
21
|
+
#
|
|
22
|
+
# Both CPUs sit on the same bus at the same 1 MHz, and one runs at a
|
|
23
|
+
# time: SW2, the CPU switch, picks it, or with SW2 at PROG bit 0 of the
|
|
24
|
+
# system latch. The one that takes the bus over starts from its RESET
|
|
25
|
+
# (Fachat: "The 6502 does a RESET after being switched on"; on the
|
|
26
|
+
# 324037 schematic one-shots gate the 6809's RES), and the other stops
|
|
27
|
+
# where it is. The PET's IRQ line, the ACIA's with it, reaches both.
|
|
28
|
+
#
|
|
29
|
+
# The Waterloo ROMs hold only the menu, setup and the monitor. The
|
|
30
|
+
# menu loads every language, microBASIC, microAPL, FORTRAN, Pascal,
|
|
31
|
+
# COBOL, the editor and the development package, from disk into RAM,
|
|
32
|
+
# so 6809 mode needs a disk on device 8 to run one.
|
|
33
|
+
#
|
|
34
|
+
# In 6809 mode device 8 answers through traps on the Waterloo ROM's
|
|
35
|
+
# IEEE-488 routines (WaterlooTraps) as well as the KERNAL's, from the
|
|
36
|
+
# same disk, or a true drive on the bus answers. type_text types
|
|
37
|
+
# through the Waterloo ROM's key buffer, in ASCII, once its menu is up.
|
|
38
|
+
class SuperPET < PET
|
|
39
|
+
# The Waterloo ROM's key buffer: its read and write pointers, and the
|
|
40
|
+
# ring of bytes they run round.
|
|
41
|
+
KEYS_READ = 0x012c
|
|
42
|
+
KEYS_WRITE = 0x012e
|
|
43
|
+
KEYS_FIRST = 0x0130
|
|
44
|
+
KEYS_END = 0x0158
|
|
45
|
+
|
|
46
|
+
# The cycle by which the 6809 has put up the Waterloo menu, with a
|
|
47
|
+
# margin.
|
|
48
|
+
WATERLOO_BOOT = 600_000
|
|
49
|
+
|
|
50
|
+
attr_reader :board, :acia, :dongle, :m6809
|
|
51
|
+
|
|
52
|
+
# The traps on the Waterloo ROM's IEEE-488 routines, while a disk is
|
|
53
|
+
# on device 8 (WaterlooTraps).
|
|
54
|
+
attr_reader :waterloo_traps
|
|
55
|
+
|
|
56
|
+
# +cpu+ is SW2's position and +bank_ram+ SW1's, as Board names them.
|
|
57
|
+
# The other arguments are the PET's for an 8032, its true drive's too.
|
|
58
|
+
def initialize(refresh_hz: 50, debug: false, basic: nil, editor: nil, cpu: "6502", bank_ram: "rw",
|
|
59
|
+
true_drive_model: Drives::IEEEModels::DEFAULT)
|
|
60
|
+
raise ArgumentError, "the CPU switch has no position #{cpu}" unless Board::CPU_SWITCH.include?(cpu)
|
|
61
|
+
|
|
62
|
+
super(model: "superpet", refresh_hz:, debug:, basic:, editor:, true_drive_model:)
|
|
63
|
+
@acia_irq = false
|
|
64
|
+
@acia_due = Chips::ACIA::NEVER
|
|
65
|
+
@acia = ACIA.new(self, 1_000_000)
|
|
66
|
+
@dongle = Dongle.new(self)
|
|
67
|
+
@board = Board.new(@bus, dongle: @dongle, acia: @acia, keyboard_ports: @keyboard_ports, cpu_switch: cpu,
|
|
68
|
+
ram_switch: bank_ram, refresh_hz: @refresh_hz)
|
|
69
|
+
@m6809 = M6809.new(@bus)
|
|
70
|
+
@m6809_running = @board.m6809?
|
|
71
|
+
@waterloo_traps = nil
|
|
72
|
+
@board.on_select { cpu_selected }
|
|
73
|
+
@bus.fit_board(@board)
|
|
74
|
+
@init_threshold = WATERLOO_BOOT if @m6809_running
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Whether an autostart finds BASIC: not while the 6809 runs.
|
|
78
|
+
def boots_basic? = !@m6809_running
|
|
79
|
+
|
|
80
|
+
# Device 8 through the traps, for the Waterloo ROM's routines too.
|
|
81
|
+
def mount(storage)
|
|
82
|
+
trap = super
|
|
83
|
+
@waterloo_traps ||= WaterlooTraps.new(cpu: @m6809, bus: @bus, dos: @dos).install
|
|
84
|
+
@waterloo_traps.device = trap_device
|
|
85
|
+
trap
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def unmount
|
|
89
|
+
super
|
|
90
|
+
@waterloo_traps&.uninstall
|
|
91
|
+
@waterloo_traps = nil
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def attach_ieee_drive(drive)
|
|
95
|
+
super
|
|
96
|
+
@waterloo_traps&.device = trap_device
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def detach_ieee_drive
|
|
100
|
+
super
|
|
101
|
+
@waterloo_traps&.device = trap_device
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# Types into the Waterloo ROM's key buffer, in ASCII, while the 6809
|
|
105
|
+
# runs, and into the editor's otherwise.
|
|
106
|
+
def type_text(text)
|
|
107
|
+
return super unless @m6809_running
|
|
108
|
+
|
|
109
|
+
type_on_init(text.bytes)
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# Whether the 6809 has the bus, rather than the 6502.
|
|
113
|
+
def m6809_running? = @m6809_running
|
|
114
|
+
|
|
115
|
+
def superpet? = true
|
|
116
|
+
|
|
117
|
+
# Moves the CPU switch to +position+, one of Board::CPU_SWITCH, and
|
|
118
|
+
# resets the machine, so the CPU the switch picks starts from its
|
|
119
|
+
# RESET.
|
|
120
|
+
def switch_cpu(position)
|
|
121
|
+
@board.cpu_switch = position
|
|
122
|
+
reset!
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# Moves the bank RAM switch to +position+, one of Board::RAM_SWITCH.
|
|
126
|
+
def switch_bank_ram(position)
|
|
127
|
+
@board.ram_switch = position
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
# Whether the picture shows the character ROM's APL set: its second
|
|
131
|
+
# half, which the CRTC's MA13 picks, with VIA CA2 high.
|
|
132
|
+
def apl_characters? = @crtc.registers[12].anybits?(0x20) && @via.ca2_output
|
|
133
|
+
|
|
134
|
+
# Drive 1 while the 6809 runs, where the Waterloo menu's "disk/1."
|
|
135
|
+
# looks, and drive 0 in 6502 mode.
|
|
136
|
+
def disk_drive_number = @m6809_running ? 1 : 0
|
|
137
|
+
|
|
138
|
+
# The 32K below $8000 and the 64K board.
|
|
139
|
+
def model_label = "SUPERPET, 96K RAM, #{@refresh_hz} HZ"
|
|
140
|
+
|
|
141
|
+
# The PET's cycle, with the ACIA run when it falls due and its IRQ
|
|
142
|
+
# on the CPU's.
|
|
143
|
+
def cycle!
|
|
144
|
+
handle_init if @cycles == @init_threshold
|
|
145
|
+
feed_keyboard if @pending_keys
|
|
146
|
+
|
|
147
|
+
@crtc.cycle!
|
|
148
|
+
drive_retrace(!@crtc.vsync?)
|
|
149
|
+
@pia1.cycle!
|
|
150
|
+
@pia2.cycle!
|
|
151
|
+
@via.cycle!
|
|
152
|
+
@sound.cycle! if @sound.recording?
|
|
153
|
+
@cassettes.cycle! if @tapes_running
|
|
154
|
+
@acia.run!(@cycles) if @cycles >= @acia_due
|
|
155
|
+
irq = @pia1.irq? || @pia2.irq? || @via.irq? || @acia_irq
|
|
156
|
+
if @m6809_running
|
|
157
|
+
@m6809.irq = irq
|
|
158
|
+
@m6809.cycle!
|
|
159
|
+
else
|
|
160
|
+
@cpu.irq = irq
|
|
161
|
+
@cpu.cycle!
|
|
162
|
+
end
|
|
163
|
+
clock_ieee_drives
|
|
164
|
+
|
|
165
|
+
@cycles += 1
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# Whether the ACIA pulls the IRQ line.
|
|
169
|
+
def acia_irq? = @acia_irq
|
|
170
|
+
|
|
171
|
+
# The ACIA's IRQ line, and the cycle it next has something to do at.
|
|
172
|
+
def acia_changed(irq, due)
|
|
173
|
+
@acia_irq = irq
|
|
174
|
+
@acia_due = due
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
# The RES line reaches the board's latches, the 6702, the ACIA and
|
|
178
|
+
# both CPUs too.
|
|
179
|
+
def reset!
|
|
180
|
+
super
|
|
181
|
+
@acia.reset!
|
|
182
|
+
@m6809.reset!
|
|
183
|
+
@board.reset!
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
def power_cycle!
|
|
187
|
+
@board.power_on!
|
|
188
|
+
super
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
private
|
|
192
|
+
|
|
193
|
+
# The Waterloo ROM's ring takes keys until its write pointer would
|
|
194
|
+
# meet its read pointer.
|
|
195
|
+
def feed_keyboard
|
|
196
|
+
return super unless @m6809_running
|
|
197
|
+
|
|
198
|
+
read = word_at(KEYS_READ)
|
|
199
|
+
write = word_at(KEYS_WRITE)
|
|
200
|
+
until @pending_keys.empty?
|
|
201
|
+
following = write + 1 == KEYS_END ? KEYS_FIRST : write + 1
|
|
202
|
+
break if following == read
|
|
203
|
+
|
|
204
|
+
@bus.poke(write, @pending_keys.shift)
|
|
205
|
+
write = following
|
|
206
|
+
end
|
|
207
|
+
@bus.poke(KEYS_WRITE, write >> 8)
|
|
208
|
+
@bus.poke(KEYS_WRITE + 1, write & 0xff)
|
|
209
|
+
@pending_keys = nil if @pending_keys.empty?
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
def word_at(addr) = (@bus.peek(addr) << 8) | @bus.peek(addr + 1)
|
|
213
|
+
|
|
214
|
+
# The CPU the board picks takes the bus over from its RESET.
|
|
215
|
+
def cpu_selected
|
|
216
|
+
@m6809_running = @board.m6809?
|
|
217
|
+
@m6809_running ? @m6809.reset! : @cpu.reset!
|
|
218
|
+
end
|
|
219
|
+
end
|
|
220
|
+
end
|
|
221
|
+
end
|
data/lib/badline/pet.rb
CHANGED
|
@@ -75,6 +75,8 @@ module Badline
|
|
|
75
75
|
|
|
76
76
|
def family = :pet
|
|
77
77
|
|
|
78
|
+
def portless_board = nil
|
|
79
|
+
|
|
78
80
|
# The datasette on cassette port #1, where a tape goes in (Cassettes).
|
|
79
81
|
def datasette = @cassettes.datasette1
|
|
80
82
|
|
|
@@ -83,10 +85,13 @@ module Badline
|
|
|
83
85
|
# model runs its 50 Hz editor, or its 60 Hz one for +refresh_hz+ 60.
|
|
84
86
|
# +basic+, +editor+ and +characters+ name ROMs the board takes in place
|
|
85
87
|
# of its own (Model.upgraded, Model.fitted), and +jumpers+ the 8296's
|
|
86
|
-
# board jumpers (ROMSets::JUMPERS).
|
|
88
|
+
# board jumpers (ROMSets::JUMPERS). +true_drive_model+ names the drive
|
|
89
|
+
# plug_true_drive plugs in (Drives::IEEEModels).
|
|
87
90
|
def initialize(model: Model::DEFAULT, ram_kb: nil, refresh_hz: 50, debug: false, basic: nil, editor: nil,
|
|
88
|
-
characters: nil, jumpers: [])
|
|
91
|
+
characters: nil, jumpers: [], true_drive_model: Drives::IEEEModels::DEFAULT)
|
|
89
92
|
@model = Model.fitted(Model.upgraded(Model.named(model), basic, editor), characters)
|
|
93
|
+
raise ArgumentError, "the SuperPET is built as PET::SuperPET" if @model.superpet? && !superpet?
|
|
94
|
+
|
|
90
95
|
ram_kb ||= @model.ram_kb
|
|
91
96
|
raise ArgumentError, "the #{@model.name} has no #{ram_kb}K RAM board" unless Model.ram_fits?(@model, ram_kb)
|
|
92
97
|
|
|
@@ -99,6 +104,7 @@ module Badline
|
|
|
99
104
|
matrix = @model.keyboard == :business ? KeyboardPorts::BUSINESS_MATRIX : KeyboardPorts::GRAPHICS_MATRIX
|
|
100
105
|
@keyboard = Input::Keyboard.new(matrix:)
|
|
101
106
|
keyboard_ports = KeyboardPorts.new(@keyboard)
|
|
107
|
+
@keyboard_ports = keyboard_ports
|
|
102
108
|
registers = Array.new(Chips::CRTC::MASKS.length, 0)
|
|
103
109
|
@pia1 = Chips::PIA.new(peripheral: keyboard_ports)
|
|
104
110
|
@ieee = IEEEPorts.new(Drives::IEEEBus.new)
|
|
@@ -125,16 +131,27 @@ module Badline
|
|
|
125
131
|
ieee.lines_written
|
|
126
132
|
end
|
|
127
133
|
@cpu = CPU.new(@bus, debug:)
|
|
134
|
+
@cpu.power_on!
|
|
128
135
|
@cycles = 0
|
|
129
136
|
@init_handlers = []
|
|
130
137
|
@pending_keys = nil
|
|
138
|
+
@init_keys = nil
|
|
131
139
|
@dos = nil
|
|
132
140
|
@ieee_trap = nil
|
|
133
141
|
@capture_output = nil
|
|
134
|
-
init_ieee_drives
|
|
142
|
+
init_ieee_drives(true_drive_model)
|
|
135
143
|
@init_threshold = boot_cycles(ram_kb)
|
|
136
144
|
end
|
|
137
145
|
|
|
146
|
+
# Whether this is the SuperPET's board (SuperPET).
|
|
147
|
+
def superpet? = false
|
|
148
|
+
|
|
149
|
+
# Whether the picture shows the SuperPET's APL characters.
|
|
150
|
+
def apl_characters? = false
|
|
151
|
+
|
|
152
|
+
# Whether an autostart finds BASIC to type into.
|
|
153
|
+
def boots_basic? = true
|
|
154
|
+
|
|
138
155
|
# The vertical drive line, high but in the retrace.
|
|
139
156
|
def vertical_drive? = @vertical_drive
|
|
140
157
|
|
|
@@ -208,6 +225,7 @@ module Badline
|
|
|
208
225
|
def power_cycle!
|
|
209
226
|
@bus.power_on!
|
|
210
227
|
reset!
|
|
228
|
+
@cpu.power_on!
|
|
211
229
|
end
|
|
212
230
|
|
|
213
231
|
# Puts a program file's bytes where its first two say, through the bus,
|
|
@@ -253,3 +271,5 @@ module Badline
|
|
|
253
271
|
end
|
|
254
272
|
end
|
|
255
273
|
end
|
|
274
|
+
|
|
275
|
+
require "badline/pet/superpet"
|
|
@@ -28,6 +28,7 @@ module Badline
|
|
|
28
28
|
out.int("cycles", @cycles)
|
|
29
29
|
pending = @pending_keys
|
|
30
30
|
out.ints("host.pending_keys", pending) if pending
|
|
31
|
+
save_init_keys(out)
|
|
31
32
|
out.int("host.init_pending", booting? ? @init_handlers.length : 0)
|
|
32
33
|
out.text("host.true_drive_model", @true_drive_model)
|
|
33
34
|
end
|
|
@@ -36,6 +37,7 @@ module Badline
|
|
|
36
37
|
@cycles = input.int("cycles", @cycles)
|
|
37
38
|
pending = input.ints("host.pending_keys", [])
|
|
38
39
|
@pending_keys = pending.empty? ? nil : pending.dup
|
|
40
|
+
load_init_keys(input)
|
|
39
41
|
@init_handlers_lost = [input.int("host.init_pending", 0) - @init_handlers.length, 0].max
|
|
40
42
|
@true_drive_model = input.choice("host.true_drive_model", TCBMDrive::TRUE_DRIVE_MODELS, @true_drive_model)
|
|
41
43
|
end
|
data/lib/badline/plus4.rb
CHANGED
|
@@ -67,6 +67,8 @@ module Badline
|
|
|
67
67
|
|
|
68
68
|
def family = :plus4
|
|
69
69
|
|
|
70
|
+
def portless_board = nil
|
|
71
|
+
|
|
70
72
|
# The joysticks reach the TED's keyboard latch, so there are no C64
|
|
71
73
|
# control ports to plug a pot device into.
|
|
72
74
|
def control_ports = nil
|
|
@@ -139,11 +141,13 @@ module Badline
|
|
|
139
141
|
@tias = tcbm ? [Chips::TIA.new, Chips::TIA.new] : []
|
|
140
142
|
@bus.plug_tcbm(drive8: @tias[0], drive9: @tias[1]) if tcbm
|
|
141
143
|
@cpu = CPU.new(@bus, debug:)
|
|
144
|
+
@cpu.power_on!
|
|
142
145
|
@sound = Chips::TED::Sound.new(self, @model.ted.clock_hz)
|
|
143
146
|
@ted.sound = @sound
|
|
144
147
|
@sound_mix = @sid_card ? SoundMix.new(@sound, @sid_card) : nil
|
|
145
148
|
@init_handlers = []
|
|
146
149
|
@pending_keys = nil
|
|
150
|
+
@init_keys = nil
|
|
147
151
|
@dos = nil
|
|
148
152
|
@serial_trap = nil
|
|
149
153
|
@save_trap = nil
|
|
@@ -201,6 +205,7 @@ module Badline
|
|
|
201
205
|
@ted.power_on!
|
|
202
206
|
@sound.power_on!
|
|
203
207
|
reset!
|
|
208
|
+
@cpu.power_on!
|
|
204
209
|
end
|
|
205
210
|
|
|
206
211
|
# The ACIA's IRQ line, and the cycle it next has something to do at.
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|