badline 0.15.1 → 0.16.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 +39 -0
- data/CLAUDE.md +8 -7
- data/README.md +102 -6
- data/doc/pinned-behaviour.md +67 -0
- data/doc/snapshot-format.md +213 -18
- data/doc/snapshots.md +14 -0
- data/lib/badline/c64/common.rb +1 -1
- data/lib/badline/chips/crtc/keyed_state.rb +46 -0
- data/lib/badline/chips/crtc.rb +163 -0
- data/lib/badline/chips/pia/control_lines.rb +138 -0
- data/lib/badline/chips/pia/keyed_parts.rb +56 -0
- data/lib/badline/chips/pia.rb +138 -0
- data/lib/badline/chips/vic/register_log.rb +1 -4
- data/lib/badline/core.rb +3 -0
- data/lib/badline/drives/ieee_bus.rb +28 -0
- data/lib/badline/drives/model1551/cable.rb +5 -2
- data/lib/badline/drives/model1551/tia.rb +13 -34
- data/lib/badline/drives/model1551.rb +26 -4
- data/lib/badline/drives.rb +1 -0
- data/lib/badline/frontend/app.rb +1 -1
- data/lib/badline/frontend/controls.rb +23 -3
- data/lib/badline/frontend/gamepads.rb +10 -3
- data/lib/badline/frontend/keys.rb +88 -0
- data/lib/badline/frontend/media_slots.rb +1 -0
- data/lib/badline/frontend/menu/menu_dialogs.rb +2 -1
- data/lib/badline/frontend/menu/menu_media.rb +27 -28
- data/lib/badline/frontend/menu/menu_pages.rb +15 -2
- data/lib/badline/frontend/menu/menu_ports.rb +22 -1
- data/lib/badline/frontend/menu/pause_menu.rb +1 -0
- data/lib/badline/frontend/pet_keyboard.rb +124 -0
- data/lib/badline/frontend/screens.rb +2 -1
- data/lib/badline/frontend/timeline.rb +16 -9
- data/lib/badline/frontend.rb +1 -0
- data/lib/badline/kernal_trap/dos/writes.rb +3 -1
- data/lib/badline/kernal_trap/dos.rb +6 -1
- data/lib/badline/kernal_trap/frames.rb +117 -0
- data/lib/badline/kernal_trap/ieee.rb +148 -0
- data/lib/badline/kernal_trap/ieee_layout.rb +113 -0
- data/lib/badline/kernal_trap/keyed_state.rb +4 -4
- data/lib/badline/kernal_trap/routine.rb +3 -53
- data/lib/badline/kernal_trap/serial.rb +4 -102
- data/lib/badline/kernal_trap/steps.rb +64 -0
- data/lib/badline/kernal_trap.rb +4 -0
- data/lib/badline/machine/keyboard_buffer.rb +8 -0
- data/lib/badline/machine/settling.rb +1 -1
- data/lib/badline/machine.rb +28 -6
- data/lib/badline/media/extensions.rb +5 -2
- data/lib/badline/media/pet_media.rb +40 -0
- data/lib/badline/media/program.rb +14 -2
- data/lib/badline/media/vic20_media.rb +2 -2
- data/lib/badline/media.rb +36 -16
- data/lib/badline/options/family.rb +17 -6
- data/lib/badline/options/help.rb +19 -3
- data/lib/badline/options/table.rb +14 -2
- data/lib/badline/options/validation.rb +68 -5
- data/lib/badline/options.rb +31 -19
- data/lib/badline/pet/bus/expansion.rb +106 -0
- data/lib/badline/pet/bus.rb +204 -0
- data/lib/badline/pet/canvas.rb +97 -0
- data/lib/badline/pet/cassettes.rb +63 -0
- data/lib/badline/pet/characters.rb +26 -0
- data/lib/badline/pet/cpu.rb +10 -0
- data/lib/badline/pet/crtc_video.rb +142 -0
- data/lib/badline/pet/discrete_video.rb +83 -0
- data/lib/badline/pet/ieee_ports.rb +66 -0
- data/lib/badline/pet/ieee_traps.rb +75 -0
- data/lib/badline/pet/keyboard_ports.rb +74 -0
- data/lib/badline/pet/keyed_modules.rb +85 -0
- data/lib/badline/pet/keyed_parts.rb +123 -0
- data/lib/badline/pet/keyed_state.rb +42 -0
- data/lib/badline/pet/model.rb +113 -0
- data/lib/badline/pet/rom_sets.rb +65 -0
- data/lib/badline/pet/sound.rb +128 -0
- data/lib/badline/pet/via.rb +10 -0
- data/lib/badline/pet/via_ports.rb +19 -0
- data/lib/badline/pet.rb +226 -0
- data/lib/badline/plus4/keyed_state.rb +5 -3
- data/lib/badline/plus4/tcbm_drive.rb +14 -0
- data/lib/badline/plus4.rb +1 -1
- data/lib/badline/roms/README +137 -0
- data/lib/badline/roms/pet/basic1.rom +0 -0
- data/lib/badline/roms/pet/basic2.rom +0 -0
- data/lib/badline/roms/pet/basic4.rom +0 -0
- data/lib/badline/roms/pet/characters1.rom +0 -0
- data/lib/badline/roms/pet/characters2.rom +0 -0
- data/lib/badline/roms/pet/editor1.rom +0 -0
- data/lib/badline/roms/pet/editor2-b.rom +0 -0
- data/lib/badline/roms/pet/editor2-n.rom +0 -0
- data/lib/badline/roms/pet/editor4-40-n-50hz.rom +0 -0
- data/lib/badline/roms/pet/editor4-40-n-60hz.rom +0 -0
- data/lib/badline/roms/pet/editor4-80-b-50hz-0283.rom +0 -0
- data/lib/badline/roms/pet/editor4-80-b-50hz.rom +0 -0
- data/lib/badline/roms/pet/editor4-80-b-60hz.rom +0 -0
- data/lib/badline/roms/pet/editor4-b.rom +0 -0
- data/lib/badline/roms/pet/editor4-n.rom +0 -0
- data/lib/badline/roms/pet/kernal1.rom +0 -0
- data/lib/badline/roms/pet/kernal2.rom +0 -0
- data/lib/badline/roms/pet/kernal4.rom +0 -0
- data/lib/badline/snapshot/container.rb +2 -0
- data/lib/badline/snapshot/field_doc/markdown.rb +1 -1
- data/lib/badline/snapshot/field_doc.rb +16 -3
- data/lib/badline/snapshot/image.rb +14 -7
- data/lib/badline/snapshot/pet_setup.rb +71 -0
- data/lib/badline/snapshot/storage_setup.rb +7 -1
- data/lib/badline/snapshot.rb +9 -5
- data/lib/badline/storage/d80_image.rb +48 -0
- data/lib/badline/storage/d82_image.rb +21 -0
- data/lib/badline/storage/tap.rb +16 -3
- data/lib/badline/storage.rb +2 -0
- data/lib/badline/support/pet_checkpoint.rb +38 -0
- data/lib/badline/timing.rb +5 -4
- data/lib/badline/version.rb +1 -1
- data/lib/badline/vic20/attachments.rb +7 -2
- data/lib/badline/vic20/bus/keyed_state.rb +5 -4
- data/lib/badline/vic20/bus.rb +5 -0
- data/lib/badline/vic20.rb +1 -1
- metadata +55 -1
data/lib/badline/pet.rb
ADDED
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "badline/pet/rom_sets"
|
|
4
|
+
require "badline/pet/model"
|
|
5
|
+
require "badline/pet/cpu"
|
|
6
|
+
require "badline/pet/via"
|
|
7
|
+
require "badline/pet/via_ports"
|
|
8
|
+
require "badline/pet/keyboard_ports"
|
|
9
|
+
require "badline/pet/ieee_ports"
|
|
10
|
+
require "badline/pet/ieee_traps"
|
|
11
|
+
require "badline/pet/cassettes"
|
|
12
|
+
require "badline/pet/characters"
|
|
13
|
+
require "badline/pet/canvas"
|
|
14
|
+
require "badline/pet/discrete_video"
|
|
15
|
+
require "badline/pet/crtc_video"
|
|
16
|
+
require "badline/pet/bus"
|
|
17
|
+
require "badline/pet/sound"
|
|
18
|
+
require "badline/snapshot/pet_setup"
|
|
19
|
+
require "badline/pet/keyed_parts"
|
|
20
|
+
require "badline/pet/keyed_state"
|
|
21
|
+
require "badline/pet/keyed_modules"
|
|
22
|
+
|
|
23
|
+
module Badline
|
|
24
|
+
# The Commodore PET and CBM: a 6502 at 1 MHz, two 6520 PIAs at $E810 and
|
|
25
|
+
# $E820, a 6522 VIA at $E840 and, from the 4032 on, a 6545 CRTC at
|
|
26
|
+
# $E880. Every chip's IRQ is wired to the CPU's, and nothing pulls NMI.
|
|
27
|
+
# Nothing halts the CPU either: the video reads the screen RAM between
|
|
28
|
+
# its accesses.
|
|
29
|
+
#
|
|
30
|
+
# The video's vertical drive line, low in the retrace, reaches PIA 1's
|
|
31
|
+
# CB1, whose falling edge is the editor's 50 or 60 Hz interrupt, and the
|
|
32
|
+
# VIA's PB5. The discrete boards count it out themselves
|
|
33
|
+
# (DiscreteVideo), and the CRTC boards take it from the CRTC's vertical
|
|
34
|
+
# sync, inverted.
|
|
35
|
+
#
|
|
36
|
+
# The key matrix hangs off PIA 1 (KeyboardPorts), the graphics one or
|
|
37
|
+
# the business one as the model has, and type_text types through the
|
|
38
|
+
# editor's keyboard buffer.
|
|
39
|
+
#
|
|
40
|
+
# The discrete boards paint their picture (DiscreteVideo) on a Canvas.
|
|
41
|
+
# The CRTC boards paint theirs from the CRTC's outputs (CRTCVideo).
|
|
42
|
+
# The VIA's CB2 is the sound (Sound).
|
|
43
|
+
#
|
|
44
|
+
# Device 8 answers through traps on the KERNAL's IEEE-488 primitives
|
|
45
|
+
# (IEEETraps), on a bus nothing else is plugged into (IEEEPorts), and
|
|
46
|
+
# each cassette port has a datasette (Cassettes). It runs headless.
|
|
47
|
+
class PET
|
|
48
|
+
include Machine::KeyboardBuffer
|
|
49
|
+
include Machine::Clocking
|
|
50
|
+
include Machine::Settling
|
|
51
|
+
include IEEETraps
|
|
52
|
+
|
|
53
|
+
attr_reader :model, :bus, :cpu, :pia1, :pia2, :via, :crtc, :keyboard, :ieee, :cassettes, :cycles, :init_threshold
|
|
54
|
+
|
|
55
|
+
# The editor's frame rate: 50 or 60 on a CRTC board, 60 on the others.
|
|
56
|
+
attr_reader :refresh_hz
|
|
57
|
+
|
|
58
|
+
# The RAM below $8000, in K.
|
|
59
|
+
attr_reader :ram_kb
|
|
60
|
+
|
|
61
|
+
def ram = @bus.ram
|
|
62
|
+
|
|
63
|
+
# Reads and writes memory as the CPU sees it, through the bus.
|
|
64
|
+
def peek(addr) = @bus.peek(addr)
|
|
65
|
+
|
|
66
|
+
def poke(addr, value) = @bus.poke(addr, value)
|
|
67
|
+
|
|
68
|
+
def install_debug_register(&) = @bus.install_debug_register(&)
|
|
69
|
+
|
|
70
|
+
def family = :pet
|
|
71
|
+
|
|
72
|
+
# The datasette on cassette port #1, where a tape goes in (Cassettes).
|
|
73
|
+
def datasette = @cassettes.datasette1
|
|
74
|
+
|
|
75
|
+
# +model+ names one of Model::ALL, with +ram_kb+ of RAM below $8000
|
|
76
|
+
# (Model::RAM_SIZES) in place of the model's own when not nil. A CRTC
|
|
77
|
+
# model runs its 50 Hz editor, or its 60 Hz one for +refresh_hz+ 60.
|
|
78
|
+
# +basic+ and +editor+ name ROMs the board takes in place of its own
|
|
79
|
+
# (Model.upgraded).
|
|
80
|
+
def initialize(model: Model::DEFAULT, ram_kb: nil, refresh_hz: 50, debug: false, basic: nil, editor: nil)
|
|
81
|
+
@model = Model.upgraded(Model.named(model), basic, editor)
|
|
82
|
+
ram_kb ||= @model.ram_kb
|
|
83
|
+
raise ArgumentError, "a PET has no #{ram_kb}K RAM board" unless Model::RAM_SIZES.include?(ram_kb)
|
|
84
|
+
|
|
85
|
+
@ram_kb = ram_kb
|
|
86
|
+
|
|
87
|
+
@refresh_hz = @model.crtc? ? refresh_hz : 60
|
|
88
|
+
@crtc_video = @model.crtc?
|
|
89
|
+
@vertical_drive = true
|
|
90
|
+
matrix = @model.keyboard == :business ? KeyboardPorts::BUSINESS_MATRIX : KeyboardPorts::GRAPHICS_MATRIX
|
|
91
|
+
@keyboard = Input::Keyboard.new(matrix:)
|
|
92
|
+
keyboard_ports = KeyboardPorts.new(@keyboard)
|
|
93
|
+
registers = Array.new(Chips::CRTC::MASKS.length, 0)
|
|
94
|
+
@pia1 = Chips::PIA.new(peripheral: keyboard_ports)
|
|
95
|
+
@ieee = IEEEPorts.new(Drives::IEEEBus.new)
|
|
96
|
+
@pia2 = Chips::PIA.new(peripheral: @ieee)
|
|
97
|
+
@via = VIA.new(start: 0xe800, peripheral: VIAPorts.new(self))
|
|
98
|
+
@sound = Sound.new(@via)
|
|
99
|
+
@cassettes = Cassettes.new(@pia1, @via)
|
|
100
|
+
keyboard_ports.connect(@pia1, @ieee, @cassettes)
|
|
101
|
+
characters = Characters.new(@model.characters)
|
|
102
|
+
@crtc_output = CRTCVideo.new(characters, @via, registers, columns80: @model.video == :crtc80)
|
|
103
|
+
@crtc = Chips::CRTC.new(@crtc_output, registers:)
|
|
104
|
+
@ieee.connect(@pia1, @pia2, @via)
|
|
105
|
+
@bus = Bus.new(model: @model, ram_kb:, editor: @model.editor(@refresh_hz), pia1: @pia1, pia2: @pia2,
|
|
106
|
+
via: @via, crtc: @crtc)
|
|
107
|
+
@crtc_output.cells = @bus.screen.cells
|
|
108
|
+
@discrete = DiscreteVideo.new(@bus.screen.cells, characters, @via)
|
|
109
|
+
@canvas = @crtc_video ? @crtc_output.canvas : @discrete.canvas
|
|
110
|
+
cassettes = @cassettes
|
|
111
|
+
@bus.on_io_write { cassettes.lines_written }
|
|
112
|
+
@cpu = CPU.new(@bus, debug:)
|
|
113
|
+
@cycles = 0
|
|
114
|
+
@init_handlers = []
|
|
115
|
+
@pending_keys = nil
|
|
116
|
+
@dos = nil
|
|
117
|
+
@ieee_trap = nil
|
|
118
|
+
@capture_output = nil
|
|
119
|
+
@init_threshold = boot_cycles(ram_kb)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# The vertical drive line, high but in the retrace.
|
|
123
|
+
def vertical_drive? = @vertical_drive
|
|
124
|
+
|
|
125
|
+
# The model, its RAM and its editor's frame rate as the pause menu
|
|
126
|
+
# names them, such as "8032, 32K RAM, 50 HZ".
|
|
127
|
+
def model_label = "#{@model.name.upcase}, #{@ram_kb}K RAM, #{@refresh_hz} HZ"
|
|
128
|
+
|
|
129
|
+
# The Canvas a front end shows.
|
|
130
|
+
def video = @canvas
|
|
131
|
+
|
|
132
|
+
# The 1 MHz clock and the frame the editor sets up, and all of the
|
|
133
|
+
# Canvas. The CRTC editors' lines and frames are their ROM's
|
|
134
|
+
# (ROMSets.crtc_frame), which a program can change. 80 columns show
|
|
135
|
+
# two dots to a window pixel.
|
|
136
|
+
def timing
|
|
137
|
+
crop = [0, 0, @canvas.width, @canvas.height]
|
|
138
|
+
if @crtc_video
|
|
139
|
+
cycles_per_line, lines_per_frame = ROMSets.crtc_frame(@model.editor(@refresh_hz), @refresh_hz)
|
|
140
|
+
Timing.new(clock_hz: 1_000_000, cycles_per_line:, lines_per_frame:,
|
|
141
|
+
crop:, pixel_width: 1, dots: @model.video == :crtc80 ? 2 : 1)
|
|
142
|
+
else
|
|
143
|
+
Timing.new(clock_hz: 1_000_000, cycles_per_line: DiscreteVideo::CYCLES_PER_LINE,
|
|
144
|
+
lines_per_frame: DiscreteVideo::LINES_PER_FRAME, crop:, pixel_width: 1, dots: 1)
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
# The video counts and moves the vertical drive line, then the PIAs
|
|
149
|
+
# and the VIA clock, ahead of the CPU.
|
|
150
|
+
def cycle!
|
|
151
|
+
handle_init if @cycles == @init_threshold
|
|
152
|
+
feed_keyboard if @pending_keys
|
|
153
|
+
|
|
154
|
+
if @crtc_video
|
|
155
|
+
@crtc.cycle!
|
|
156
|
+
drive_retrace(!@crtc.vsync?)
|
|
157
|
+
else
|
|
158
|
+
@discrete.cycle!
|
|
159
|
+
drive_retrace(@discrete.vertical_drive?)
|
|
160
|
+
end
|
|
161
|
+
@pia1.cycle!
|
|
162
|
+
@pia2.cycle!
|
|
163
|
+
@via.cycle!
|
|
164
|
+
@sound.cycle! if @sound.recording?
|
|
165
|
+
@cassettes.cycle!
|
|
166
|
+
@cpu.irq = @pia1.irq? || @pia2.irq? || @via.irq?
|
|
167
|
+
@cpu.cycle!
|
|
168
|
+
|
|
169
|
+
@cycles += 1
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
# What a front end plays: the VIA's CB2 (Sound), which answers #record
|
|
173
|
+
# and #drain_samples.
|
|
174
|
+
def sound_source = @sound
|
|
175
|
+
|
|
176
|
+
# The RES line reaches the CPU, the PIAs, the VIA, the CRTC and the
|
|
177
|
+
# 8096's control register.
|
|
178
|
+
def reset!
|
|
179
|
+
@pia1.reset!
|
|
180
|
+
@pia2.reset!
|
|
181
|
+
@via.reset!
|
|
182
|
+
@crtc.reset!
|
|
183
|
+
@bus.reset!
|
|
184
|
+
@cpu.reset!
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
# Switches the machine off and on: RAM starts cleared, and the RES
|
|
188
|
+
# line resets everything else.
|
|
189
|
+
def power_cycle!
|
|
190
|
+
@bus.power_on!
|
|
191
|
+
reset!
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
# Puts a program file's bytes where its first two say, through the bus,
|
|
195
|
+
# and returns that address.
|
|
196
|
+
def load_prg(data)
|
|
197
|
+
load_addr = data[0] | (data[1] << 8)
|
|
198
|
+
bytes = data[2..] || []
|
|
199
|
+
bytes = bytes[0, 0x10000 - load_addr] if load_addr + bytes.length > 0x10000
|
|
200
|
+
bytes.each_with_index { |byte, i| @bus.poke(load_addr + i, byte) }
|
|
201
|
+
load_addr
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
# Where the editor keeps its keyboard buffer: in page 2 under BASIC 1,
|
|
205
|
+
# in the zero page and page 2 under BASIC 2 and 4.
|
|
206
|
+
def keyboard_buffer
|
|
207
|
+
@model.basic1? ? Machine::KeyboardBuffer::PET_BASIC1_LAYOUT : Machine::KeyboardBuffer::PET_LAYOUT
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
private
|
|
211
|
+
|
|
212
|
+
# The cycle by which the KERNAL has booted and BASIC printed READY,
|
|
213
|
+
# with a margin. The RAM test takes most of it, about 41,000 to
|
|
214
|
+
# 44,000 cycles a K, and the CRTC editors take another 600,000 or so
|
|
215
|
+
# before it: READY is up by about 440,000 on a 2001 with 8K, 1,450,000
|
|
216
|
+
# on a 3032 and 1,980,000 on an 8032.
|
|
217
|
+
def boot_cycles(ram_kb) = (@model.basic == "pet/basic4.rom" ? 700_000 : 150_000) + (46_000 * ram_kb)
|
|
218
|
+
|
|
219
|
+
def drive_retrace(high)
|
|
220
|
+
return if high == @vertical_drive
|
|
221
|
+
|
|
222
|
+
@vertical_drive = high
|
|
223
|
+
@pia1.cb1 = high
|
|
224
|
+
end
|
|
225
|
+
end
|
|
226
|
+
end
|
|
@@ -4,9 +4,9 @@ module Badline
|
|
|
4
4
|
class Plus4
|
|
5
5
|
# The machine's own fields in a keyed snapshot module (Snapshot::Keyed,
|
|
6
6
|
# BL-MACHINE): the family, its setup by name (Snapshot::Plus4Setup), the
|
|
7
|
-
# cycle count, and the host's typing still to feed
|
|
8
|
-
# still to run
|
|
9
|
-
# written for reports and never read.
|
|
7
|
+
# cycle count, and the host's typing still to feed, on_init handlers
|
|
8
|
+
# still to run and the model plug_true_drive plugs in under host.*. The
|
|
9
|
+
# badline version that wrote it is written for reports and never read.
|
|
10
10
|
module KeyedState
|
|
11
11
|
# How many more on_init handlers the machine a snapshot was taken of had
|
|
12
12
|
# yet to run than this one has. Nil until the machine is restored.
|
|
@@ -29,6 +29,7 @@ module Badline
|
|
|
29
29
|
pending = @pending_keys
|
|
30
30
|
out.ints("host.pending_keys", pending) if pending
|
|
31
31
|
out.int("host.init_pending", booting? ? @init_handlers.length : 0)
|
|
32
|
+
out.text("host.true_drive_model", @true_drive_model)
|
|
32
33
|
end
|
|
33
34
|
|
|
34
35
|
def load_machine_fields(input)
|
|
@@ -36,6 +37,7 @@ module Badline
|
|
|
36
37
|
pending = input.ints("host.pending_keys", [])
|
|
37
38
|
@pending_keys = pending.empty? ? nil : pending.dup
|
|
38
39
|
@init_handlers_lost = [input.int("host.init_pending", 0) - @init_handlers.length, 0].max
|
|
40
|
+
@true_drive_model = input.choice("host.true_drive_model", TCBMDrive::TRUE_DRIVE_MODELS, @true_drive_model)
|
|
39
41
|
end
|
|
40
42
|
end
|
|
41
43
|
|
|
@@ -32,6 +32,7 @@ module Badline
|
|
|
32
32
|
drive.host_clock_hz = timing.clock_hz
|
|
33
33
|
@drive1551 = drive
|
|
34
34
|
@bus.plug_tia(drive.device, drive.host_tia)
|
|
35
|
+
drive.on_renumber { |old| drive1551_renumbered(old) }
|
|
35
36
|
@serial_trap&.device = serial_trap_device
|
|
36
37
|
@init_threshold = [@init_threshold, Badline::Drives::Model1551::BOOT_CYCLES].max if @cycles < @init_threshold
|
|
37
38
|
end
|
|
@@ -43,6 +44,7 @@ module Badline
|
|
|
43
44
|
return unless drive
|
|
44
45
|
|
|
45
46
|
@bus.plug_tia(drive.device, tcbm_tia(drive.device))
|
|
47
|
+
drive.on_renumber
|
|
46
48
|
@drive1551 = nil
|
|
47
49
|
@serial_trap&.device = serial_trap_device
|
|
48
50
|
end
|
|
@@ -93,6 +95,18 @@ module Badline
|
|
|
93
95
|
@drive1551.load_modules(modules, input, "BL-D#{device}")
|
|
94
96
|
end
|
|
95
97
|
|
|
98
|
+
# The 1551's DEV line moved it from device +old+ to the other: its
|
|
99
|
+
# interface's TIA moves with it, and the TCBM interface's own takes
|
|
100
|
+
# its old place.
|
|
101
|
+
def drive1551_renumbered(old)
|
|
102
|
+
drive = @drive1551
|
|
103
|
+
return unless drive
|
|
104
|
+
|
|
105
|
+
@bus.plug_tia(old, tcbm_tia(old))
|
|
106
|
+
@bus.plug_tia(drive.device, drive.host_tia)
|
|
107
|
+
@serial_trap&.device = serial_trap_device
|
|
108
|
+
end
|
|
109
|
+
|
|
96
110
|
# The TCBM interface's own TIA for +device+, or nil without one.
|
|
97
111
|
def tcbm_tia(device)
|
|
98
112
|
tias = @tias
|
data/lib/badline/plus4.rb
CHANGED
|
@@ -89,7 +89,7 @@ module Badline
|
|
|
89
89
|
ntsc = profile == Chips::TED::NTSC
|
|
90
90
|
Timing.new(clock_hz: profile.clock_hz, cycles_per_line: Chips::TED::Beam::CYCLES_PER_LINE,
|
|
91
91
|
lines_per_frame: ntsc ? 262 : 312, crop: ntsc ? [0, 248, 384, 242] : [0, 276, 384, 288],
|
|
92
|
-
pixel_width: 1)
|
|
92
|
+
pixel_width: 1, dots: 1)
|
|
93
93
|
end
|
|
94
94
|
|
|
95
95
|
# Reads and writes memory as the CPU sees it, through the bus.
|
data/lib/badline/roms/README
CHANGED
|
@@ -179,3 +179,140 @@ plus4/function-high.rom
|
|
|
179
179
|
The Plus/4's 3-plus-1 function ROM, high half, 317054-01
|
|
180
180
|
(3-plus-1.317054-01.bin), for $C000-$FFFF in ROM bank 1.
|
|
181
181
|
SHA-1 0ad7ac2db7da692d972e586ca0dfd747d82c7693.
|
|
182
|
+
|
|
183
|
+
Commodore PET/CBM ROMs
|
|
184
|
+
======================
|
|
185
|
+
|
|
186
|
+
Downloaded from
|
|
187
|
+
https://www.zimmers.net/anonftp/pub/cbm/firmware/computers/pet/
|
|
188
|
+
Each BASIC and KERNAL pair is one ROM set, and the editors and the
|
|
189
|
+
character sets are files of their own, so a model's stock set and the
|
|
190
|
+
upgrade ROMs Commodore sold can be picked apart. PET::ROMSets
|
|
191
|
+
(pet/rom_sets.rb) names them for --basic and --editor and says which
|
|
192
|
+
fit each model, and PET::Bus maps the ones fitted.
|
|
193
|
+
|
|
194
|
+
pet/basic1.rom
|
|
195
|
+
--------------
|
|
196
|
+
BASIC 1 for $C000-$DFFF, four 2K chips concatenated:
|
|
197
|
+
901447-09 (rom-2-c000.901447-09.bin, the revision that replaced
|
|
198
|
+
901447-01), 901447-02, 901447-03 and 901447-04 (rom-1-c800, rom-1-d000,
|
|
199
|
+
rom-1-d800).
|
|
200
|
+
SHA-1 3be9cd41b646fefa00d80f1c3433a06146e97c59.
|
|
201
|
+
|
|
202
|
+
pet/editor1.rom
|
|
203
|
+
---------------
|
|
204
|
+
The BASIC 1 screen editor, 901447-05 (rom-1-e000.901447-05.bin), for
|
|
205
|
+
$E000-$E7FF.
|
|
206
|
+
SHA-1 f02f5fb492ba93dbbd390f24c10f7a832dec432a.
|
|
207
|
+
|
|
208
|
+
pet/kernal1.rom
|
|
209
|
+
---------------
|
|
210
|
+
The BASIC 1 KERNAL for $F000-$FFFF, 901447-06 and 901447-07
|
|
211
|
+
(rom-1-f000.901447-06.bin, rom-1-f800.901447-07.bin) concatenated.
|
|
212
|
+
SHA-1 32dc08828e88c74d6433156e64d402b564556308.
|
|
213
|
+
|
|
214
|
+
pet/characters1.rom
|
|
215
|
+
-------------------
|
|
216
|
+
The 2001's character generator, 901447-08 (characters-1.901447-08.bin),
|
|
217
|
+
with upper and lower case swapped against 901447-10.
|
|
218
|
+
SHA-1 3e067cc621e4beafca2b90cb8f6dba975df2855b.
|
|
219
|
+
|
|
220
|
+
pet/basic2.rom
|
|
221
|
+
--------------
|
|
222
|
+
BASIC 2 for $C000-$DFFF, 901465-01 and 901465-02
|
|
223
|
+
(basic-2-c000.901465-01.bin, basic-2-d000.901465-02.bin) concatenated.
|
|
224
|
+
The 2K chips of the 6540 boards (rom-3-*) hold the same bytes.
|
|
225
|
+
SHA-1 9322a9f0354b9049847d98d3a045fbec860a6264.
|
|
226
|
+
|
|
227
|
+
pet/kernal2.rom
|
|
228
|
+
---------------
|
|
229
|
+
The BASIC 2 KERNAL, 901465-03 (kernal-2.901465-03.bin), for $F000-$FFFF.
|
|
230
|
+
SHA-1 38742bdf449f629bcba6276ef24d3daeb7da6e84.
|
|
231
|
+
|
|
232
|
+
pet/editor2-n.rom
|
|
233
|
+
-----------------
|
|
234
|
+
The BASIC 2 editor for the graphics keyboard, 901447-24
|
|
235
|
+
(edit-2-n.901447-24.bin), for $E000-$E7FF.
|
|
236
|
+
SHA-1 5e5502ce32f5a7e387d65efe058916282041e54b.
|
|
237
|
+
|
|
238
|
+
pet/editor2-b.rom
|
|
239
|
+
-----------------
|
|
240
|
+
The BASIC 2 editor for the business keyboard, 901474-01
|
|
241
|
+
(edit-2-b.901474-01.bin), for $E000-$E7FF.
|
|
242
|
+
SHA-1 174ace3a8c0348cd21d39cc864e2adc58b0101a9.
|
|
243
|
+
|
|
244
|
+
pet/characters2.rom
|
|
245
|
+
-------------------
|
|
246
|
+
The character generator of BASIC 2 and 4 machines, 901447-10
|
|
247
|
+
(characters-2.901447-10.bin).
|
|
248
|
+
SHA-1 0157a2d55b7ac4eaeb38475889ebeea52e2593db.
|
|
249
|
+
|
|
250
|
+
pet/basic4.rom
|
|
251
|
+
--------------
|
|
252
|
+
BASIC 4 for $B000-$DFFF, 901465-23, 901465-20 and 901465-21
|
|
253
|
+
(basic-4-b000.901465-23.bin, basic-4-c000.901465-20.bin,
|
|
254
|
+
basic-4-d000.901465-21.bin) concatenated. 901465-23 is the patched
|
|
255
|
+
$B000 chip that replaced 901465-19. The 8296's 324746-01 holds the same
|
|
256
|
+
four chips.
|
|
257
|
+
SHA-1 d70bec479e1e3a0d0908a4897066e9959bbc11c8.
|
|
258
|
+
|
|
259
|
+
pet/kernal4.rom
|
|
260
|
+
---------------
|
|
261
|
+
The BASIC 4 KERNAL, 901465-22 (kernal-4.901465-22.bin), for
|
|
262
|
+
$F000-$FFFF.
|
|
263
|
+
SHA-1 96a0fa56e0c937da92971d9c99d504e44e898806.
|
|
264
|
+
|
|
265
|
+
pet/editor4-n.rom
|
|
266
|
+
-----------------
|
|
267
|
+
The BASIC 4 editor for the graphics keyboard without a CRTC, 901447-29
|
|
268
|
+
(edit-4-n.901447-29.bin), for $E000-$E7FF.
|
|
269
|
+
SHA-1 e88f56e5c54b0e8d8d4e8cb39a4647c803c1f51c.
|
|
270
|
+
|
|
271
|
+
pet/editor4-b.rom
|
|
272
|
+
-----------------
|
|
273
|
+
The BASIC 4 editor for the business keyboard without a CRTC, 901474-02
|
|
274
|
+
(edit-4-b.901474-02.bin), for $E000-$E7FF.
|
|
275
|
+
SHA-1 0ca5c4e8f532f914cb0bf86ea9900f20f0a655ce.
|
|
276
|
+
|
|
277
|
+
pet/editor4-40-n-50hz.rom
|
|
278
|
+
-------------------------
|
|
279
|
+
The 4032's 50 Hz editor, 40 columns on a CRTC, graphics keyboard,
|
|
280
|
+
901498-01 (edit-4-40-n-50Hz.901498-01.bin), for $E000-$E7FF.
|
|
281
|
+
SHA-1 05af284c914d53a52987b5f602466de75765f650.
|
|
282
|
+
|
|
283
|
+
pet/editor4-40-n-60hz.rom
|
|
284
|
+
-------------------------
|
|
285
|
+
The 4032's 60 Hz editor, 901499-01 (edit-4-40-n-60Hz.901499-01.bin),
|
|
286
|
+
for $E000-$E7FF.
|
|
287
|
+
SHA-1 8cbf086c1ce4dfb2a2fe24c47476dfb878493dee.
|
|
288
|
+
|
|
289
|
+
pet/editor4-80-b-50hz.rom
|
|
290
|
+
-------------------------
|
|
291
|
+
The 8032's 50 Hz editor, 80 columns on a CRTC, business keyboard,
|
|
292
|
+
901474-04, the chip dated 3681
|
|
293
|
+
(edit-4-80-b-50Hz.901474-04-3681.bin), for $E000-$E7FF.
|
|
294
|
+
SHA-1 7040b283ba39e9630e3d147f7d076b7abc39bc70.
|
|
295
|
+
|
|
296
|
+
zimmers.net has three dumps of 901474-04 that differ. This one is the
|
|
297
|
+
60 Hz 901474-03 below with the 50 Hz jiffy clock patch at $E431-$E441
|
|
298
|
+
and $E455, the CRTC's vertical registers (R4, R5 and R7) changed in both
|
|
299
|
+
of its register tables at $E72A, and the byte at $E787 changed to keep
|
|
300
|
+
the ROM's byte sum at $8A, the same as 901474-03's. The line length (R0)
|
|
301
|
+
stays the 60 Hz editor's. A second chip, dated 0384, reads the same
|
|
302
|
+
(8296/edit-50hz-324243-02b.txt). The -0283 dump changes R0 and sums to
|
|
303
|
+
$AE, and the _alt dump changes R0 and $E787.
|
|
304
|
+
|
|
305
|
+
pet/editor4-80-b-50hz-0283.rom
|
|
306
|
+
------------------------------
|
|
307
|
+
The 8032's other 50 Hz editor, 901474-04, the chip dated 0283
|
|
308
|
+
(edit-4-80-b-50Hz.901474-04-0283.bin), for $E000-$E7FF, which
|
|
309
|
+
--editor 4-80-b-50hz-0283 fits. It differs from the 3681 dump only in
|
|
310
|
+
the CRTC's register tables at $E72A and $E73C: lines of 64 cycles (R0
|
|
311
|
+
63) and frames of 313 lines, with nine scanlines a character row.
|
|
312
|
+
SHA-1 81975eab31a8f4f51ae2a20d099a567c7b3f2dd1.
|
|
313
|
+
|
|
314
|
+
pet/editor4-80-b-60hz.rom
|
|
315
|
+
-------------------------
|
|
316
|
+
The 8032's 60 Hz editor, 901474-03 (edit-4-80-b-60Hz.901474-03.bin),
|
|
317
|
+
for $E000-$E7FF.
|
|
318
|
+
SHA-1 c605fa343fd77c73cbe1e0e9567e2f014f6e7e30.
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -36,6 +36,8 @@ module Badline
|
|
|
36
36
|
C128 = "C128"
|
|
37
37
|
# xplus4 names its machine PLUS4, for the C16 too.
|
|
38
38
|
PLUS4 = "PLUS4"
|
|
39
|
+
# xpet names its machine PET, for every model.
|
|
40
|
+
PET = "PET"
|
|
39
41
|
GZIP_MAGIC = "\x1f\x8b".b
|
|
40
42
|
|
|
41
43
|
# The VICE version block, when VICE wrote the file: four version
|
|
@@ -7,7 +7,7 @@ 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" }.freeze
|
|
10
|
+
NAMES = { "c64" => "C64", "vic20" => "VIC-20", "c128" => "C128", "plus4" => "Plus/4", "pet" => "PET" }.freeze
|
|
11
11
|
TAG_TYPES = { Keyed::INT => "int", Keyed::BOOL => "bool", Keyed::BYTES => "bytes", Keyed::INTS => "ints",
|
|
12
12
|
Keyed::TEXT => "text" }.freeze
|
|
13
13
|
|
|
@@ -36,8 +36,9 @@ module Badline
|
|
|
36
36
|
|
|
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
|
-
#
|
|
40
|
-
# true drives, the 1541 and the 1551
|
|
39
|
+
# the 264 series' models, its TCBM interface, cartridge, tape and
|
|
40
|
+
# true drives, the 1541 and the 1551, and a CRTC PET, a 2001, an 8096
|
|
41
|
+
# and a PET with a disk and tapes.
|
|
41
42
|
def self.machines
|
|
42
43
|
{
|
|
43
44
|
"c64" => [C64.new, C64.new(ram_expansion: :plus256k), C64.new(reu: 512),
|
|
@@ -48,10 +49,22 @@ module Badline
|
|
|
48
49
|
"plus4" => [Plus4.new, Plus4.new(model: "c16"), Plus4.new(tcbm: true), Plus4.new(sid_card: :mos6581),
|
|
49
50
|
Plus4.new(sid_card: :mos8580, sid_card_address: 0xfe80),
|
|
50
51
|
Plus4.new(ram_expansion: :csory256),
|
|
51
|
-
plus4_with_cartridge, plus4_with_tape, plus4_with_drive, plus4_with_drive("1551")]
|
|
52
|
+
plus4_with_cartridge, plus4_with_tape, plus4_with_drive, plus4_with_drive("1551")],
|
|
53
|
+
"pet" => [PET.new, PET.new(model: "2001"), PET.new(model: "8096"), pet_with_media]
|
|
52
54
|
}
|
|
53
55
|
end
|
|
54
56
|
|
|
57
|
+
# A PET with a disk in device 8 through the traps and a tape in each
|
|
58
|
+
# datasette.
|
|
59
|
+
def self.pet_with_media
|
|
60
|
+
bytes = "C64-TAPE-RAW".bytes + [1, 3, 0, 0, 4, 0, 0, 0, 0x30, 0x30, 0x30, 0x30]
|
|
61
|
+
PET.new.tap do |machine|
|
|
62
|
+
machine.mount(Storage::D64Image.new("", read_only: true, bytes: blank(174_848)))
|
|
63
|
+
machine.cassettes.datasette1.insert(Storage::TAP.new("", bytes:))
|
|
64
|
+
machine.cassettes.datasette2.insert(Storage::TAP.new("", bytes:))
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
55
68
|
def self.blank(length) = Array.new(length, 0)
|
|
56
69
|
|
|
57
70
|
def self.with_cartridge(type)
|
|
@@ -34,7 +34,7 @@ module Badline
|
|
|
34
34
|
# of a machine in memory (.of).
|
|
35
35
|
class Image
|
|
36
36
|
MACHINE = "BL-MACHINE"
|
|
37
|
-
FAMILIES = { "c64" => "C64", "vic20" => "VIC-20", "c128" => "C128", "plus4" => "Plus/4" }.freeze
|
|
37
|
+
FAMILIES = { "c64" => "C64", "vic20" => "VIC-20", "c128" => "C128", "plus4" => "Plus/4", "pet" => "PET" }.freeze
|
|
38
38
|
|
|
39
39
|
attr_reader :container
|
|
40
40
|
|
|
@@ -54,10 +54,11 @@ module Badline
|
|
|
54
54
|
end
|
|
55
55
|
|
|
56
56
|
# The snapshot with VICE's modules ahead of the BL-* ones for a C64,
|
|
57
|
-
# so x64sc opens it too. Raises FormatError for a Plus/4,
|
|
58
|
-
# snapshots badline doesn't write for xplus4.
|
|
57
|
+
# so x64sc opens it too. Raises FormatError for a Plus/4 or a PET,
|
|
58
|
+
# whose snapshots badline doesn't write for xplus4 or xpet.
|
|
59
59
|
def self.exported(computer)
|
|
60
60
|
raise FormatError, "badline doesn't export a Plus/4's snapshot for VICE" if computer.family == :plus4
|
|
61
|
+
raise FormatError, "badline doesn't export a PET's snapshot for VICE" if computer.family == :pet
|
|
61
62
|
|
|
62
63
|
computer.settle_for_snapshot!
|
|
63
64
|
sections = computer.save_modules
|
|
@@ -71,6 +72,7 @@ module Badline
|
|
|
71
72
|
when :vic20 then Container::VIC20
|
|
72
73
|
when :c128 then Container::C128
|
|
73
74
|
when :plus4 then Container::PLUS4
|
|
75
|
+
when :pet then Container::PET
|
|
74
76
|
else Container::MACHINE
|
|
75
77
|
end
|
|
76
78
|
end
|
|
@@ -82,6 +84,7 @@ module Badline
|
|
|
82
84
|
when "vic20" then Vic20Setup.read_fields(machine)
|
|
83
85
|
when "c128" then C128Setup.read_fields(machine)
|
|
84
86
|
when "plus4" then Plus4Setup.read_fields(machine)
|
|
87
|
+
when "pet" then PETSetup.read_fields(machine)
|
|
85
88
|
else C64Setup.read_fields(machine)
|
|
86
89
|
end
|
|
87
90
|
end
|
|
@@ -97,15 +100,16 @@ module Badline
|
|
|
97
100
|
|
|
98
101
|
# A new machine, built as the snapshot's was and restored from it. A
|
|
99
102
|
# badline snapshot restores from its BL-* modules alone, and a VICE
|
|
100
|
-
# one through the modules badline reads. xvic's, x128's
|
|
101
|
-
# modules aren't read. Yields a line for each thing left
|
|
102
|
-
# warns it without a block. `detached` restores it without
|
|
103
|
-
# files (Keyed::Modules).
|
|
103
|
+
# one through the modules badline reads. xvic's, x128's, xplus4's and
|
|
104
|
+
# xpet's own modules aren't read. Yields a line for each thing left
|
|
105
|
+
# out, or warns it without a block. `detached` restores it without
|
|
106
|
+
# the host's files (Keyed::Modules).
|
|
104
107
|
def load(detached: false, &)
|
|
105
108
|
refuse_old_layout
|
|
106
109
|
raise FormatError, "badline reads only its own VIC-20 snapshots, not xvic's" if vic20? && !badline?
|
|
107
110
|
raise FormatError, "badline reads only its own C128 snapshots, not x128's" if c128? && !badline?
|
|
108
111
|
raise FormatError, "badline reads only its own Plus/4 snapshots, not xplus4's" if plus4? && !badline?
|
|
112
|
+
raise FormatError, "badline reads only its own PET snapshots, not xpet's" if pet? && !badline?
|
|
109
113
|
return load_vice(&) unless badline?
|
|
110
114
|
|
|
111
115
|
modules = Keyed::Modules.new(sections, detached:)
|
|
@@ -123,6 +127,9 @@ module Badline
|
|
|
123
127
|
# Whether the machine is of the 264 series, as the container names it.
|
|
124
128
|
def plus4? = @container.machine == Container::PLUS4
|
|
125
129
|
|
|
130
|
+
# Whether the machine is a PET, as the container names it.
|
|
131
|
+
def pet? = @container.machine == Container::PET
|
|
132
|
+
|
|
126
133
|
# Restores `computer` from the snapshot, and returns the Report. The
|
|
127
134
|
# snapshot goes into a new machine first, so one that fails, or is of
|
|
128
135
|
# a machine built another way, leaves `computer` as it was.
|