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
data/lib/badline/plus4/acia.rb
CHANGED
|
@@ -1,305 +1,11 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "badline/plus4/acia/frames"
|
|
4
|
-
|
|
5
3
|
module Badline
|
|
6
4
|
class Plus4
|
|
7
|
-
# The Plus/4's 6551
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
|
|
11
|
-
# $FD02 - the command register
|
|
12
|
-
# $FD03 - the control register
|
|
13
|
-
#
|
|
14
|
-
# Status: bit 0 a parity error, 1 a framing error, 2 an overrun, 3 the
|
|
15
|
-
# receiver full, 4 the transmitter empty, 5 DCD and 6 DSR as their pins
|
|
16
|
-
# read (1 high, not asserted), 7 an interrupt. Reading it clears bit 7.
|
|
17
|
-
#
|
|
18
|
-
# Command: bit 0 DTR, which also enables the receiver and every
|
|
19
|
-
# interrupt, bit 1 set disables the receiver's interrupt, bits 2-3 the
|
|
20
|
-
# transmitter (00 off with RTS high, 01 on with its interrupt, 10 on
|
|
21
|
-
# without, 11 sending a break), bit 4 echoes what is received, bits 5-7
|
|
22
|
-
# the parity.
|
|
23
|
-
#
|
|
24
|
-
# Control: bits 0-3 the baud rate from the 1.8432 MHz crystal (0, the
|
|
25
|
-
# 16x external clock, runs straight from the crystal, 115,200 baud),
|
|
26
|
-
# bit 4 the receiver's clock (0 the RxC pin, which nothing drives on the
|
|
27
|
-
# Plus/4, so the receiver hears nothing), bits 5-6 the word length (8
|
|
28
|
-
# down to 5 bits) and bit 7 the stop bits (Frames).
|
|
29
|
-
#
|
|
30
|
-
# The transmitter runs in whole characters while it is on: at the end
|
|
31
|
-
# of each it hands the character it shifted out to the line, moves a
|
|
32
|
-
# waiting byte from the data register into the shift register and sets
|
|
33
|
-
# the transmitter empty, and with its interrupt on interrupts while the
|
|
34
|
-
# register is empty, every character until a byte comes. The first
|
|
35
|
-
# character ends a character's time after the transmitter comes on.
|
|
36
|
-
# The receiver takes a character's time for each byte the line sends,
|
|
37
|
-
# and a byte that arrives while the last is unread is lost to an
|
|
38
|
-
# overrun. The receiver full, and DCD or DSR changing, interrupt unless
|
|
39
|
-
# bit 1 disables it.
|
|
40
|
-
#
|
|
41
|
-
# The user port carries no CTS, so the transmitter is always clear to
|
|
42
|
-
# send; the KERNAL takes the user port's 6529 P1 as its handshake.
|
|
43
|
-
#
|
|
44
|
-
# What is on the other end of the user port (#plug): nothing, where
|
|
45
|
-
# DCD and DSR read high and what is sent goes nowhere; a terminal,
|
|
46
|
-
# which asserts DCD and DSR, sends what the host feeds it (#feed) while
|
|
47
|
-
# DTR is asserted and keeps what it receives for the host to drain
|
|
48
|
-
# (#drain); or a loopback plug, which returns TxD to RxD and DTR to DSR
|
|
49
|
-
# and DCD.
|
|
50
|
-
#
|
|
51
|
-
# The machine runs the ACIA at the cycle it next has something to do
|
|
52
|
-
# (#due) through #run!, and learns its IRQ line and that cycle from
|
|
53
|
-
# `clock`'s #acia_changed, whose #cycles is the time.
|
|
54
|
-
class ACIA
|
|
55
|
-
include Frames
|
|
56
|
-
|
|
57
|
-
PARITY_ERROR = 0x01
|
|
58
|
-
FRAMING_ERROR = 0x02
|
|
59
|
-
OVERRUN = 0x04
|
|
60
|
-
RECEIVER_FULL = 0x08
|
|
61
|
-
TRANSMITTER_EMPTY = 0x10
|
|
62
|
-
DCD = 0x20
|
|
63
|
-
DSR = 0x40
|
|
64
|
-
IRQ = 0x80
|
|
65
|
-
|
|
66
|
-
NOTHING = 0
|
|
67
|
-
TERMINAL = 1
|
|
68
|
-
LOOPBACK = 2
|
|
69
|
-
|
|
70
|
-
# No shift in progress.
|
|
71
|
-
IDLE = -1
|
|
72
|
-
|
|
73
|
-
# Far enough ahead that it never comes.
|
|
74
|
-
NEVER = 1 << 60
|
|
75
|
-
|
|
76
|
-
attr_reader :command, :control, :device, :due
|
|
77
|
-
|
|
78
|
-
def initialize(clock, clock_hz)
|
|
79
|
-
@clock = clock
|
|
80
|
-
@clock_hz = clock_hz
|
|
81
|
-
@device = NOTHING
|
|
82
|
-
@incoming = []
|
|
83
|
-
@received = []
|
|
84
|
-
reset!
|
|
85
|
-
end
|
|
86
|
-
|
|
87
|
-
# The RES line: every register cleared, the transmitter empty and
|
|
88
|
-
# both shift registers stopped.
|
|
89
|
-
def reset!
|
|
90
|
-
@command = 0
|
|
91
|
-
@control = 0
|
|
92
|
-
@errors = 0
|
|
93
|
-
@receiver_full = false
|
|
94
|
-
@transmitter_empty = true
|
|
95
|
-
@irq = false
|
|
96
|
-
@receive_data = 0
|
|
97
|
-
@transmit_data = 0
|
|
98
|
-
@shifting = IDLE
|
|
99
|
-
@transmit_at = NEVER
|
|
100
|
-
@transmit_remainder = 0
|
|
101
|
-
@receive_at = NEVER
|
|
102
|
-
@receive_remainder = 0
|
|
103
|
-
@clock_lost = false
|
|
104
|
-
@lines = modem_lines
|
|
105
|
-
changed
|
|
106
|
-
end
|
|
107
|
-
|
|
108
|
-
def irq? = @irq
|
|
109
|
-
|
|
110
|
-
# DTR and RTS as asserted, low on the pins.
|
|
111
|
-
def dtr? = @command.anybits?(0x01)
|
|
112
|
-
|
|
113
|
-
def rts? = @command.anybits?(0x0c)
|
|
114
|
-
|
|
115
|
-
# Whether the transmitter sends a break, holding TxD low.
|
|
116
|
-
def break? = @command & 0x0c == 0x0c
|
|
117
|
-
|
|
118
|
-
# Plugs NOTHING, a TERMINAL or a LOOPBACK plug into the user port.
|
|
119
|
-
def plug(device)
|
|
120
|
-
@device = device
|
|
121
|
-
@incoming.clear
|
|
122
|
-
lines_changed
|
|
123
|
-
start_receiving
|
|
124
|
-
changed
|
|
125
|
-
end
|
|
126
|
-
|
|
127
|
-
# Bytes the terminal sends, one a character's time while DTR is
|
|
128
|
-
# asserted.
|
|
129
|
-
def feed(bytes)
|
|
130
|
-
return unless @device == TERMINAL
|
|
131
|
-
|
|
132
|
-
@incoming.concat(bytes)
|
|
133
|
-
start_receiving
|
|
134
|
-
changed
|
|
135
|
-
end
|
|
136
|
-
|
|
137
|
-
# What the terminal has received since the last drain.
|
|
138
|
-
def drain
|
|
139
|
-
received = @received
|
|
140
|
-
@received = []
|
|
141
|
-
received
|
|
142
|
-
end
|
|
143
|
-
|
|
144
|
-
# Runs what falls due up to cycle `now`: the transmitter's character
|
|
145
|
-
# ends, then the receiver's.
|
|
146
|
-
def run!(now)
|
|
147
|
-
end_character while @transmit_at <= now
|
|
148
|
-
receive_character while @receive_at <= now
|
|
149
|
-
changed
|
|
150
|
-
end
|
|
151
|
-
|
|
152
|
-
def peek(addr)
|
|
153
|
-
case addr & 0x03
|
|
154
|
-
when 0
|
|
155
|
-
@receiver_full = false
|
|
156
|
-
@receive_data
|
|
157
|
-
when 1 then read_status
|
|
158
|
-
when 2 then @command
|
|
159
|
-
else @control
|
|
160
|
-
end
|
|
161
|
-
end
|
|
162
|
-
|
|
163
|
-
def poke(addr, value)
|
|
164
|
-
case addr & 0x03
|
|
165
|
-
when 0 then write_data(value)
|
|
166
|
-
when 1 then write_command(@command & 0xe0, clear_overrun: true)
|
|
167
|
-
when 2 then write_command(value)
|
|
168
|
-
else @control = value
|
|
169
|
-
end
|
|
170
|
-
changed
|
|
171
|
-
end
|
|
172
|
-
|
|
173
|
-
# The status as a read finds it, without clearing the interrupt.
|
|
174
|
-
def status
|
|
175
|
-
status = @errors | (@lines & (DCD | DSR))
|
|
176
|
-
status |= RECEIVER_FULL if @receiver_full
|
|
177
|
-
status |= TRANSMITTER_EMPTY if @transmitter_empty
|
|
178
|
-
@irq ? status | IRQ : status
|
|
179
|
-
end
|
|
180
|
-
|
|
181
|
-
private
|
|
182
|
-
|
|
183
|
-
def read_status
|
|
184
|
-
value = status
|
|
185
|
-
@irq = false
|
|
186
|
-
changed
|
|
187
|
-
value
|
|
188
|
-
end
|
|
189
|
-
|
|
190
|
-
def write_data(value)
|
|
191
|
-
@transmit_data = value
|
|
192
|
-
@transmitter_empty = false
|
|
193
|
-
end
|
|
194
|
-
|
|
195
|
-
def write_command(value, clear_overrun: false)
|
|
196
|
-
was_on = transmitter_on?
|
|
197
|
-
@command = value
|
|
198
|
-
@errors &= ~OVERRUN if clear_overrun
|
|
199
|
-
start_transmitting if transmitter_on? && !was_on
|
|
200
|
-
stop_transmitting unless transmitter_on? || @shifting != IDLE
|
|
201
|
-
lines_changed
|
|
202
|
-
start_receiving
|
|
203
|
-
end
|
|
204
|
-
|
|
205
|
-
def transmitter_on?
|
|
206
|
-
control = @command & 0x0c
|
|
207
|
-
control != 0 && control != 0x0c
|
|
208
|
-
end
|
|
209
|
-
|
|
210
|
-
def interrupts? = @command.anybits?(0x01)
|
|
211
|
-
|
|
212
|
-
def receiver_interrupts? = @command & 0x03 == 0x01
|
|
213
|
-
|
|
214
|
-
def transmitter_interrupts? = @command & 0x0d == 0x05
|
|
215
|
-
|
|
216
|
-
def receiving? = dtr? && @control.anybits?(0x10)
|
|
217
|
-
|
|
218
|
-
def start_transmitting
|
|
219
|
-
@transmit_remainder = 0
|
|
220
|
-
@transmit_at = after(@clock.cycles, true)
|
|
221
|
-
end
|
|
222
|
-
|
|
223
|
-
def stop_transmitting
|
|
224
|
-
@transmit_at = NEVER
|
|
225
|
-
end
|
|
226
|
-
|
|
227
|
-
# A character's end: the character shifted out goes to the line, and
|
|
228
|
-
# the transmitter takes the next byte or interrupts while it has none.
|
|
229
|
-
def end_character
|
|
230
|
-
sent = @shifting
|
|
231
|
-
@shifting = IDLE
|
|
232
|
-
deliver(sent) unless sent == IDLE
|
|
233
|
-
on = transmitter_on?
|
|
234
|
-
if on && !@transmitter_empty
|
|
235
|
-
@shifting = @transmit_data & word_mask
|
|
236
|
-
@transmitter_empty = true
|
|
237
|
-
end
|
|
238
|
-
@irq = true if on && @transmitter_empty && transmitter_interrupts?
|
|
239
|
-
if on || @shifting != IDLE
|
|
240
|
-
@transmit_at = after(@transmit_at, true)
|
|
241
|
-
else
|
|
242
|
-
stop_transmitting
|
|
243
|
-
end
|
|
244
|
-
end
|
|
245
|
-
|
|
246
|
-
def deliver(byte)
|
|
247
|
-
case @device
|
|
248
|
-
when TERMINAL then @received << byte
|
|
249
|
-
when LOOPBACK then take(byte) if receiving?
|
|
250
|
-
end
|
|
251
|
-
end
|
|
252
|
-
|
|
253
|
-
def start_receiving
|
|
254
|
-
return unless @receive_at == NEVER && !@incoming.empty? && receiving?
|
|
255
|
-
|
|
256
|
-
@receive_remainder = 0
|
|
257
|
-
@receive_at = after(@clock.cycles, false)
|
|
258
|
-
end
|
|
259
|
-
|
|
260
|
-
def receive_character
|
|
261
|
-
byte = @incoming[0]
|
|
262
|
-
@incoming.delete_at(0)
|
|
263
|
-
take(byte)
|
|
264
|
-
@receive_at = !@incoming.empty? && receiving? ? after(@receive_at, false) : NEVER
|
|
265
|
-
end
|
|
266
|
-
|
|
267
|
-
# A whole character in from RxD: into the data register, or lost to
|
|
268
|
-
# an overrun while the last is unread. In echo mode it goes back out
|
|
269
|
-
# to the terminal.
|
|
270
|
-
def take(byte)
|
|
271
|
-
if @receiver_full
|
|
272
|
-
@errors |= OVERRUN
|
|
273
|
-
else
|
|
274
|
-
@receive_data = byte & word_mask
|
|
275
|
-
@receiver_full = true
|
|
276
|
-
@errors = 0
|
|
277
|
-
end
|
|
278
|
-
@received << byte if @device == TERMINAL && @command & 0x1c == 0x10
|
|
279
|
-
@irq = true if receiver_interrupts?
|
|
280
|
-
end
|
|
281
|
-
|
|
282
|
-
# DCD and DSR as the device drives them, interrupting on a change.
|
|
283
|
-
def lines_changed
|
|
284
|
-
lines = modem_lines
|
|
285
|
-
return if lines == @lines
|
|
286
|
-
|
|
287
|
-
@lines = lines
|
|
288
|
-
@irq = true if receiver_interrupts?
|
|
289
|
-
end
|
|
290
|
-
|
|
291
|
-
def modem_lines
|
|
292
|
-
case @device
|
|
293
|
-
when TERMINAL then 0
|
|
294
|
-
when LOOPBACK then dtr? ? 0 : DCD | DSR
|
|
295
|
-
else DCD | DSR
|
|
296
|
-
end
|
|
297
|
-
end
|
|
298
|
-
|
|
299
|
-
def changed
|
|
300
|
-
@due = [@transmit_at, @receive_at].min
|
|
301
|
-
@clock.acia_changed(@irq && interrupts?, @due)
|
|
302
|
-
end
|
|
5
|
+
# The Plus/4's 6551 at $FD00-$FD0F, on the user port. Nothing drives
|
|
6
|
+
# its RxC pin, and the user port carries no CTS; the KERNAL takes the
|
|
7
|
+
# user port's 6529 P1 as its handshake.
|
|
8
|
+
class ACIA < Chips::ACIA
|
|
303
9
|
end
|
|
304
10
|
end
|
|
305
11
|
end
|
|
@@ -52,50 +52,6 @@ module Badline
|
|
|
52
52
|
include KeyedState
|
|
53
53
|
end
|
|
54
54
|
|
|
55
|
-
class ACIA
|
|
56
|
-
# The registers, the status, the shift register and when the
|
|
57
|
-
# transmitter's character ends, in a keyed snapshot module (BL-ACIA).
|
|
58
|
-
# What the device on the user port has still to send stays with the
|
|
59
|
-
# host, and the receiver starts on it again once loaded (#restored!).
|
|
60
|
-
# A snapshot from before the transmitter's timing was saved starts
|
|
61
|
-
# its character clock on load.
|
|
62
|
-
module KeyedState
|
|
63
|
-
def save_fields(out)
|
|
64
|
-
out.int("command", @command).int("control", @control).int("errors", @errors)
|
|
65
|
-
out.bool("receiver_full", @receiver_full).bool("transmitter_empty", @transmitter_empty).bool("irq", @irq)
|
|
66
|
-
out.int("receive_data", @receive_data).int("transmit_data", @transmit_data).int("shifting", @shifting)
|
|
67
|
-
out.int("transmit_at", @transmit_at).int("transmit_remainder", @transmit_remainder)
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
def load_fields(input)
|
|
71
|
-
@command = input.byte("command", @command)
|
|
72
|
-
@control = input.byte("control", @control)
|
|
73
|
-
@errors = input.masked("errors", 0, PARITY_ERROR | FRAMING_ERROR | OVERRUN)
|
|
74
|
-
@receiver_full = input.bool("receiver_full", false)
|
|
75
|
-
@transmitter_empty = input.bool("transmitter_empty", true)
|
|
76
|
-
@irq = input.bool("irq", false)
|
|
77
|
-
@receive_data = input.byte("receive_data", 0)
|
|
78
|
-
@transmit_data = input.byte("transmit_data", 0)
|
|
79
|
-
@shifting = input.int("shifting", IDLE)
|
|
80
|
-
@transmit_remainder = input.int("transmit_remainder", 0)
|
|
81
|
-
transmit_at = input.optional_int("transmit_at")
|
|
82
|
-
@transmit_at = transmit_at || NEVER
|
|
83
|
-
@clock_lost = transmit_at.nil?
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
def restored!
|
|
87
|
-
@receive_at = NEVER
|
|
88
|
-
@lines = modem_lines
|
|
89
|
-
start_transmitting if @clock_lost && transmitter_on?
|
|
90
|
-
@clock_lost = false
|
|
91
|
-
start_receiving
|
|
92
|
-
changed
|
|
93
|
-
end
|
|
94
|
-
end
|
|
95
|
-
|
|
96
|
-
include KeyedState
|
|
97
|
-
end
|
|
98
|
-
|
|
99
55
|
class Bus
|
|
100
56
|
# The ROM bank latch, whether ROM is selected and the last byte on the
|
|
101
57
|
# bus, under `prefix`, and the cartridges' ROMs by slot (#save_slot_fields),
|
data/lib/badline/plus4.rb
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
data/lib/badline/roms/README
CHANGED
|
@@ -100,6 +100,34 @@ https://www.zimmers.net/anonftp/pub/cbm/firmware/drives/old/2031/
|
|
|
100
100
|
and concatenated.
|
|
101
101
|
SHA-1 c53e180a96649ceb3f421739e8dc66faba7cba44.
|
|
102
102
|
|
|
103
|
+
8050/fdc-901869-01.rom
|
|
104
|
+
----------------------
|
|
105
|
+
The 6530 of the 8050 and 8250's floppy controller with the DOS 2.7
|
|
106
|
+
mask for MPI mechanisms, 901869-01 (6530-48): the 1 KB ROM the
|
|
107
|
+
controller runs from $FC00-$FFFF. Chips::RRIOT holds it.
|
|
108
|
+
SHA-1 3a9a80f72ce76e5f5c72513f8ef7553212912ae3.
|
|
109
|
+
|
|
110
|
+
8050/fdc-901885-04.rom
|
|
111
|
+
----------------------
|
|
112
|
+
The same 6530 with the DOS 2.7 mask for Micropolis mechanisms,
|
|
113
|
+
901885-04 (6530-47). It differs from 901869-01 in 104 bytes.
|
|
114
|
+
SHA-1 0dc9a3b60f1b866c63eebd882403532fc59fe57f.
|
|
115
|
+
|
|
116
|
+
8050/dos-2.7.rom
|
|
117
|
+
----------------
|
|
118
|
+
The 8050 and 8250's DOS 2.7, 16 KB for the interface processor's
|
|
119
|
+
$C000-$FFFF: 901887-01 ($C000-$DFFF, SHA-1
|
|
120
|
+
b10603195f240118fe5fb6c6dfe5c5097463d890) followed by 901888-01
|
|
121
|
+
($E000-$FFFF, SHA-1 2e6c2d7ca934e5c550ad14bd5e9e7749686b7af4). xcbm2
|
|
122
|
+
and xpet with -drive8type 8050 or 8250 run the same 16 KB.
|
|
123
|
+
Drives::Model8050 loads it.
|
|
124
|
+
SHA-1 d50b093828888146596f17ae0df9ef3dfde71b05.
|
|
125
|
+
|
|
126
|
+
Both downloaded from
|
|
127
|
+
https://www.zimmers.net/anonftp/pub/cbm/firmware/drives/old/8050/
|
|
128
|
+
and named by the 8050's parts list
|
|
129
|
+
(https://www.zimmers.net/anonftp/pub/cbm/schematics/drives/old/8050/parts.txt).
|
|
130
|
+
|
|
103
131
|
Commodore VIC-20 ROMs
|
|
104
132
|
=====================
|
|
105
133
|
|
|
@@ -348,3 +376,86 @@ second half is 901447-10 byte for byte, and its first a DIN set, with
|
|
|
348
376
|
the German letters and the section sign in place of @ [ \ ] and in
|
|
349
377
|
some of the graphics. The CRTC's MA13, bit 5 of R12, picks the half.
|
|
350
378
|
SHA-1 9616f7f18757cccefb702a945f954b644d5b17d1.
|
|
379
|
+
|
|
380
|
+
Commodore CBM-II ROMs
|
|
381
|
+
=====================
|
|
382
|
+
|
|
383
|
+
Downloaded from
|
|
384
|
+
https://www.zimmers.net/anonftp/pub/cbm/firmware/computers/b/
|
|
385
|
+
for the B series (610, 620, 710 and 720) and the P500 (510). Each BASIC
|
|
386
|
+
is its two 8K chips concatenated, $8000-$9FFF followed by $A000-$BFFF,
|
|
387
|
+
as the PET's are. Nothing maps them yet.
|
|
388
|
+
|
|
389
|
+
cbm2/kernal-04a.rom
|
|
390
|
+
-------------------
|
|
391
|
+
The B series KERNAL, 901244-04a (kernal.901244-04a.bin), for
|
|
392
|
+
$E000-$FFFF in segment 15. The default, the one VICE runs on every B
|
|
393
|
+
model.
|
|
394
|
+
SHA-1 abb26418b9e1614a8f52bdeee0822d4a96071439.
|
|
395
|
+
|
|
396
|
+
cbm2/kernal-03b.rom
|
|
397
|
+
-------------------
|
|
398
|
+
The B series KERNAL of the 1983 release, 901244-03b
|
|
399
|
+
(kernal.901244-03b.bin), for $E000-$FFFF in segment 15. Its entry
|
|
400
|
+
points and page 3 vectors are 901244-04a's.
|
|
401
|
+
SHA-1 a624899c236bc4458570144d25aaf0b3be08b2cd.
|
|
402
|
+
|
|
403
|
+
cbm2/basic128.rom
|
|
404
|
+
-----------------
|
|
405
|
+
BASIC 128 for the 610 and 710, 901243-04a and 901242-04a
|
|
406
|
+
(b128-8000.901243-04a.bin, SHA-1
|
|
407
|
+
08d333208060ee2ce84d4532028d94f71c016b96, and b128-a000.901242-04a.bin,
|
|
408
|
+
SHA-1 7c6de17d46a3343dc597d9b9519cf63037b31908) concatenated, for
|
|
409
|
+
$8000-$BFFF in segment 15. The default, paired with kernal-04a.rom.
|
|
410
|
+
SHA-1 9409f92e167984096fe4d5913714bff6214c3b2d.
|
|
411
|
+
|
|
412
|
+
cbm2/basic128-02b.rom
|
|
413
|
+
---------------------
|
|
414
|
+
BASIC 128 of the 1983 release, 901243-02b and 901242-02b
|
|
415
|
+
(b128-8000.901243-02b.bin, SHA-1
|
|
416
|
+
625f7337ea972a8bce2bdf2daababc0ed0b3b69b, and b128-a000.901242-02b.bin,
|
|
417
|
+
SHA-1 56e8d2f86bf73ba36b3d3cb84dd75806b66c530a) concatenated, for
|
|
418
|
+
$8000-$BFFF in segment 15. Paired with kernal-03b.rom.
|
|
419
|
+
SHA-1 0da8311666521a1d5d7c80561550f9037f652989.
|
|
420
|
+
|
|
421
|
+
cbm2/basic256.rom
|
|
422
|
+
-----------------
|
|
423
|
+
BASIC 256 for the 620 and 720, 901241-03 and 901240-03
|
|
424
|
+
(b256-8000.901241-03.bin, SHA-1
|
|
425
|
+
2d46be2cd89594b718cdd0a86d51b6f628343f42, and b256-a000.901240-03.bin,
|
|
426
|
+
SHA-1 0d7f77746290afba8d0abeb87c9caab9a3ad89ce) concatenated, for
|
|
427
|
+
$8000-$BFFF in segment 15. Both KERNALs run it.
|
|
428
|
+
SHA-1 53314b4d565cc405a67a0936b935e6e08955580d.
|
|
429
|
+
|
|
430
|
+
cbm2/characters-600.rom
|
|
431
|
+
-----------------------
|
|
432
|
+
The 600's character generator, 901237-01 (characters.901237-01.bin),
|
|
433
|
+
4K: two sets of 128 glyphs, 16 bytes each, an 8x8 matrix in the first
|
|
434
|
+
eight rows.
|
|
435
|
+
SHA-1 e63bf18da48e5a53c99ef127c1ae721333d1d102.
|
|
436
|
+
|
|
437
|
+
cbm2/characters-700.rom
|
|
438
|
+
-----------------------
|
|
439
|
+
The 700's character generator, 901232-01 (characters.901232-01.bin),
|
|
440
|
+
4K: two sets of 128 glyphs, 16 bytes each, an 8x16 matrix. zimmers.net
|
|
441
|
+
also has 901232-02, from a 710, which differs in 139 bytes. xcbm2 runs
|
|
442
|
+
-01 on its 710, and a 710's boot screen painted with it matches xcbm2's
|
|
443
|
+
dot for dot.
|
|
444
|
+
SHA-1 e7f3cbc8e282f79a00c3e95d75c8d725ee3c6287.
|
|
445
|
+
|
|
446
|
+
cbm2/p500-kernal.rom
|
|
447
|
+
--------------------
|
|
448
|
+
The P500's KERNAL, 901234-02 (kernal.901234-02.bin), for $E000-$FFFF in
|
|
449
|
+
segment 15. The P500's character generator is the C64's,
|
|
450
|
+
character.rom above.
|
|
451
|
+
SHA-1 097197d4d08e0b82e0466a5f1fbd49a24f3d2523.
|
|
452
|
+
|
|
453
|
+
cbm2/p500-basic.rom
|
|
454
|
+
-------------------
|
|
455
|
+
The P500's BASIC 128, 901236-02 and 901235-02
|
|
456
|
+
(p500-basic-lo.901236-02.bin, SHA-1
|
|
457
|
+
f50240407bade901144f7e9f489fa9c607834eca, and
|
|
458
|
+
p500-basic-hi.901235-02.bin, SHA-1
|
|
459
|
+
1b9a55f12f8cf025754d8029cc5324b474c35841) concatenated, for
|
|
460
|
+
$8000-$BFFF in segment 15.
|
|
461
|
+
SHA-1 e69531d45f5993f8e53c6bd9ab2a7bc10fe2d02f.
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
module Snapshot
|
|
5
|
+
# How a CBM-II was built: its model's name, one of CBM2::Model::ALL or
|
|
6
|
+
# CBM2::P500::Model::ALL, its KERNAL, one of the model's KERNALS, its
|
|
7
|
+
# mains, 50 or 60 Hz, and whether the DRAM refresh takes its cycles,
|
|
8
|
+
# the arguments CBM2.new or CBM2::P500.new takes.
|
|
9
|
+
CBM2Setup = Data.define(:model, :kernal, :mains_hz, :refresh)
|
|
10
|
+
|
|
11
|
+
class CBM2Setup
|
|
12
|
+
MODELS = (CBM2::Model::ALL + CBM2::P500::Model::ALL).map(&:name).freeze
|
|
13
|
+
|
|
14
|
+
KERNALS = (CBM2::Model::KERNALS.keys + CBM2::P500::Model::KERNALS.keys).freeze
|
|
15
|
+
|
|
16
|
+
MAINS = [50, 60].freeze
|
|
17
|
+
|
|
18
|
+
# The setup of `machine`.
|
|
19
|
+
def self.of(machine)
|
|
20
|
+
new(model: machine.model.name, kernal: machine.kernal, mains_hz: machine.mains_hz, refresh: machine.refresh?)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# The model by name, the KERNAL, the mains and the refresh, in a keyed
|
|
24
|
+
# snapshot module.
|
|
25
|
+
def write_fields(out)
|
|
26
|
+
out.text("model", model).text("kernal", kernal).int("mains_hz", mains_hz).bool("dram_refresh", refresh)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def self.read_fields(input)
|
|
30
|
+
model = input.choice("model", MODELS, CBM2::Model::DEFAULT)
|
|
31
|
+
kernal = input.choice("kernal", KERNALS, "04a")
|
|
32
|
+
mains_hz = input.int("mains_hz", 50)
|
|
33
|
+
unless MAINS.include?(mains_hz)
|
|
34
|
+
raise FormatError, "the snapshot is of a CBM-II at #{mains_hz} Hz, which badline doesn't build"
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
new(model:, kernal:, mains_hz:, refresh: input.bool("dram_refresh", true))
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# A machine built this way, at power-on.
|
|
41
|
+
def build
|
|
42
|
+
return CBM2::P500.new(model:, mains_hz:) if CBM2::P500::Model.include?(model)
|
|
43
|
+
|
|
44
|
+
CBM2.new(model:, kernal:, mains_hz:, refresh:)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def to_s
|
|
48
|
+
machine = "#{model} with KERNAL #{kernal} at #{mains_hz} Hz"
|
|
49
|
+
refresh || CBM2::P500::Model.include?(model) ? machine : "#{machine} without the DRAM refresh"
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -38,9 +38,14 @@ module Badline
|
|
|
38
38
|
PLUS4 = "PLUS4"
|
|
39
39
|
# xpet names its machine PET, for every model.
|
|
40
40
|
PET = "PET"
|
|
41
|
+
# xcbm2 names its machine CBM-II, for every B series model.
|
|
42
|
+
CBM2 = "CBM-II"
|
|
43
|
+
# xcbm5x0 names its machine CBM-II-5x0, the P500.
|
|
44
|
+
CBM2_5X0 = "CBM-II-5x0"
|
|
41
45
|
# The file format version each machine's VICE emulator reads, where
|
|
42
|
-
# it isn't FORMAT_MAJOR.0: xpet
|
|
43
|
-
|
|
46
|
+
# it isn't FORMAT_MAJOR.0: xpet and xcbm2 refuse any other than 1.0, and
|
|
47
|
+
# xcbm5x0 writes 0.0.
|
|
48
|
+
FORMAT_MAJORS = { PET => 1, CBM2 => 1, CBM2_5X0 => 0 }.freeze
|
|
44
49
|
GZIP_MAGIC = "\x1f\x8b".b
|
|
45
50
|
|
|
46
51
|
# The VICE version block, when VICE wrote the file: four version
|
|
@@ -7,7 +7,8 @@ module Badline
|
|
|
7
7
|
# its default, with keys that differ only in a unit's number, such as
|
|
8
8
|
# voice1 to voice3, on one row.
|
|
9
9
|
class Markdown
|
|
10
|
-
NAMES = { "c64" => "C64", "vic20" => "VIC-20", "c128" => "C128", "plus4" => "Plus/4", "pet" => "PET"
|
|
10
|
+
NAMES = { "c64" => "C64", "vic20" => "VIC-20", "c128" => "C128", "plus4" => "Plus/4", "pet" => "PET",
|
|
11
|
+
"cbm2" => "CBM-II" }.freeze
|
|
11
12
|
TAG_TYPES = { Keyed::INT => "int", Keyed::BOOL => "bool", Keyed::BYTES => "bytes", Keyed::INTS => "ints",
|
|
12
13
|
Keyed::TEXT => "text" }.freeze
|
|
13
14
|
|
|
@@ -31,7 +32,8 @@ module Badline
|
|
|
31
32
|
`int & 0x..` are ints cut to the latch's width. A text that lists
|
|
32
33
|
its values takes one of them. An optional int is absent when the
|
|
33
34
|
latch holds nothing. A true drive's modules are named from its
|
|
34
|
-
device number, `BL-D8` to `BL-D11`,
|
|
35
|
+
device number, `BL-D8` to `BL-D11`, or to `BL-D15` for an 8050 or
|
|
36
|
+
an 8250. The SIDs past the first
|
|
35
37
|
are `BL-SID2` onwards, in the order they were fitted. Keys with
|
|
36
38
|
`<N>` stand for each numbered unit. A default of "written only" is
|
|
37
39
|
a key the load doesn't read, or reads only when the snapshot has
|
|
@@ -37,8 +37,11 @@ module Badline
|
|
|
37
37
|
# The machines the doc is made from: each family at power-on, the C64
|
|
38
38
|
# with each kind of expansion, cartridge and media, and a second SID,
|
|
39
39
|
# the 264 series' models, its TCBM interface, cartridge, tape and
|
|
40
|
-
# true drives, the 1541 and the 1551,
|
|
41
|
-
# an 8296 with its user port jumpered
|
|
40
|
+
# true drives, the 1541 and the 1551, a CRTC PET, a 2001, an 8096,
|
|
41
|
+
# an 8296 with its user port jumpered, a PET with a disk and tapes and
|
|
42
|
+
# one with a true 2031, one with an 8050 and one with an 8250, and a
|
|
43
|
+
# 610, a 720, a 610 with a cartridge and one with a disk, and a PAL
|
|
44
|
+
# and an NTSC 510.
|
|
42
45
|
def self.machines
|
|
43
46
|
{
|
|
44
47
|
"c64" => [C64.new, C64.new(ram_expansion: :plus256k), C64.new(reu: 512),
|
|
@@ -51,7 +54,10 @@ module Badline
|
|
|
51
54
|
Plus4.new(ram_expansion: :csory256),
|
|
52
55
|
plus4_with_cartridge, plus4_with_tape, plus4_with_drive, plus4_with_drive("1551")],
|
|
53
56
|
"pet" => [PET.new, PET.new(model: "2001"), PET.new(model: "8096"),
|
|
54
|
-
PET.new(model: "8296", characters: "din", jumpers: %w[userport]), pet_with_media
|
|
57
|
+
PET.new(model: "8296", characters: "din", jumpers: %w[userport]), pet_with_media,
|
|
58
|
+
pet_with_drive, pet_with_unit("8050"), pet_with_unit("8250")],
|
|
59
|
+
"cbm2" => [CBM2.new, CBM2.new(model: "720", kernal: "03b", mains_hz: 60), cbm2_with_cartridge,
|
|
60
|
+
cbm2_with_disk, CBM2::P500.new, CBM2::P500.new(model: "510ntsc")]
|
|
55
61
|
}
|
|
56
62
|
end
|
|
57
63
|
|
|
@@ -66,8 +72,37 @@ module Badline
|
|
|
66
72
|
end
|
|
67
73
|
end
|
|
68
74
|
|
|
75
|
+
# A PET with a 2031 holding a blank disk.
|
|
76
|
+
def self.pet_with_drive
|
|
77
|
+
PET.new.tap do |machine|
|
|
78
|
+
image = Storage::D64Image.new("", bytes: blank(174_848))
|
|
79
|
+
machine.plug_true_drive.insert(Drives::Model1541::Disk.decode(image))
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# A PET with an 8050 or an 8250 holding a blank disk in drive 0.
|
|
84
|
+
def self.pet_with_unit(model)
|
|
85
|
+
image = if model == "8250"
|
|
86
|
+
Storage::D82Image.new("", bytes: blank(1_066_496))
|
|
87
|
+
else
|
|
88
|
+
Storage::D80Image.new("", bytes: blank(533_248))
|
|
89
|
+
end
|
|
90
|
+
PET.new.tap { |machine| machine.plug_drive8050(model).insert(Drives::Model8050::Disk.new(image)) }
|
|
91
|
+
end
|
|
92
|
+
|
|
69
93
|
def self.blank(length) = Array.new(length, 0)
|
|
70
94
|
|
|
95
|
+
# A 610 with a disk in device 8 through the traps.
|
|
96
|
+
def self.cbm2_with_disk
|
|
97
|
+
CBM2.new.tap { |machine| machine.mount(Storage::D64Image.new("", read_only: true, bytes: blank(174_848))) }
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# A 610 with an 8K cartridge ROM at $2000.
|
|
101
|
+
def self.cbm2_with_cartridge
|
|
102
|
+
chip = Storage::CRTFile::Chip.new(chip_type: 0, bank: 0, address: 0x2000, data: blank(0x2000))
|
|
103
|
+
CBM2.new.tap { |machine| machine.attach_cartridge([chip]) }
|
|
104
|
+
end
|
|
105
|
+
|
|
71
106
|
def self.with_cartridge(type)
|
|
72
107
|
chips = [0x8000, 0xa000].map do |address|
|
|
73
108
|
Storage::CRTFile::Chip.new(chip_type: 2, bank: 0, address:, data: blank(0x2000))
|