badline 0.16.1 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +213 -0
- data/CLAUDE.md +11 -7
- data/CONTRIBUTING.md +7 -1
- data/README.md +265 -26
- data/doc/media.md +5 -2
- data/doc/pinned-behaviour.md +189 -0
- data/doc/snapshot-format.md +616 -74
- data/doc/snapshots.md +192 -24
- data/exe/badline-ruby +5 -0
- data/lib/badline/audio/terminal.rb +34 -2
- data/lib/badline/c128/keyed_state.rb +2 -0
- data/lib/badline/c128.rb +1 -0
- data/lib/badline/c64/keyed_state.rb +2 -0
- data/lib/badline/c64.rb +1 -0
- data/lib/badline/cbm2/acia.rb +10 -0
- data/lib/badline/cbm2/bus.rb +263 -0
- data/lib/badline/cbm2/cpu.rb +58 -0
- data/lib/badline/cbm2/crtc_video.rb +133 -0
- data/lib/badline/cbm2/ieee_drive.rb +18 -0
- data/lib/badline/cbm2/ieee_layout.rb +69 -0
- data/lib/badline/cbm2/ieee_ports.rb +104 -0
- data/lib/badline/cbm2/ieee_traps.rb +81 -0
- data/lib/badline/cbm2/keyboard_ports.rb +60 -0
- data/lib/badline/cbm2/keyed_modules.rb +67 -0
- data/lib/badline/cbm2/keyed_state.rb +95 -0
- data/lib/badline/cbm2/model.rb +44 -0
- data/lib/badline/cbm2/p500/control_ports.rb +80 -0
- data/lib/badline/cbm2/p500/ieee_layout.rb +41 -0
- data/lib/badline/cbm2/p500/keyed_state.rb +85 -0
- data/lib/badline/cbm2/p500/model.rb +39 -0
- data/lib/badline/cbm2/p500/pages.rb +57 -0
- data/lib/badline/cbm2/p500.rb +264 -0
- data/lib/badline/cbm2/sid_page.rb +19 -0
- data/lib/badline/cbm2.rb +270 -0
- data/lib/badline/{plus4 → chips}/acia/frames.rb +1 -1
- data/lib/badline/chips/acia/keyed_state.rb +47 -0
- data/lib/badline/chips/acia.rb +307 -0
- data/lib/badline/chips/cia/timer.rb +2 -2
- data/lib/badline/chips/cpu/instruction_set.rb +1 -1
- data/lib/badline/chips/crtc.rb +3 -2
- data/lib/badline/chips/m6809/arithmetic.rb +140 -0
- data/lib/badline/chips/m6809/bus_steps.rb +223 -0
- data/lib/badline/chips/m6809/core.rb +149 -0
- data/lib/badline/chips/m6809/indexed.rb +82 -0
- data/lib/badline/chips/m6809/interrupts.rb +159 -0
- data/lib/badline/chips/m6809/keyed_state.rb +147 -0
- data/lib/badline/chips/m6809/opcodes.rb +159 -0
- data/lib/badline/chips/m6809/operations.rb +258 -0
- data/lib/badline/chips/m6809/plans.rb +143 -0
- data/lib/badline/chips/m6809/registers.rb +82 -0
- data/lib/badline/chips/m6809/stack.rb +140 -0
- data/lib/badline/chips/m6809.rb +13 -0
- data/lib/badline/chips/riot/fast_forward.rb +72 -0
- data/lib/badline/chips/riot/keyed_parts.rb +69 -0
- data/lib/badline/chips/riot/timer.rb +91 -0
- data/lib/badline/chips/riot.rb +162 -0
- data/lib/badline/chips/rriot/keyed_state.rb +30 -0
- data/lib/badline/chips/rriot.rb +134 -0
- data/lib/badline/chips/sid/filter.rb +4 -4
- data/lib/badline/chips/sid/waveform.rb +6 -6
- data/lib/badline/chips/tpi/keyed_state.rb +38 -0
- data/lib/badline/chips/tpi.rb +233 -0
- data/lib/badline/chips/vic/bank.rb +60 -5
- data/lib/badline/chips/vic/sprite.rb +4 -3
- data/lib/badline/chips/vic_i/keyed_parts.rb +7 -2
- data/lib/badline/chips/vic_i.rb +84 -21
- data/lib/badline/data_path.rb +3 -3
- data/lib/badline/drives/datasette/keyed_state.rb +9 -1
- data/lib/badline/drives/datasette.rb +27 -3
- data/lib/badline/drives/ieee_models.rb +54 -0
- data/lib/badline/drives/model1001.rb +37 -0
- data/lib/badline/drives/model2031.rb +6 -0
- data/lib/badline/drives/model4040/disk.rb +62 -0
- data/lib/badline/drives/model4040/ip_bus.rb +46 -0
- data/lib/badline/drives/model4040/keyed_state.rb +57 -0
- data/lib/badline/drives/model4040/mechanism.rb +192 -0
- data/lib/badline/drives/model4040/parts.rb +20 -0
- data/lib/badline/drives/model4040.rb +179 -0
- data/lib/badline/drives/model8050/board.rb +77 -0
- data/lib/badline/drives/model8050/disk.rb +163 -0
- data/lib/badline/drives/model8050/dos_board.rb +91 -0
- data/lib/badline/drives/model8050/fdc_bus.rb +102 -0
- data/lib/badline/drives/model8050/idle.rb +181 -0
- data/lib/badline/drives/model8050/ieee_port.rb +147 -0
- data/lib/badline/drives/model8050/ip_bus.rb +93 -0
- data/lib/badline/drives/model8050/keyed_state.rb +163 -0
- data/lib/badline/drives/model8050/mechanism.rb +230 -0
- data/lib/badline/drives/model8050/parts.rb +37 -0
- data/lib/badline/drives/model8050.rb +131 -0
- data/lib/badline/drives/model9060/idle.rb +149 -0
- data/lib/badline/drives/model9060/keyed_state.rb +123 -0
- data/lib/badline/drives/model9060/parts.rb +98 -0
- data/lib/badline/drives/model9060/sasi_bus.rb +88 -0
- data/lib/badline/drives/model9060/sasi_commands.rb +168 -0
- data/lib/badline/drives/model9060/sasi_target.rb +161 -0
- data/lib/badline/drives/model9060.rb +195 -0
- data/lib/badline/frontend/app.rb +18 -3
- data/lib/badline/frontend/boot.rb +4 -3
- data/lib/badline/frontend/controls.rb +107 -15
- data/lib/badline/frontend/gamepads.rb +3 -3
- data/lib/badline/frontend/keys.rb +97 -2
- data/lib/badline/frontend/kim1_panel.rb +208 -0
- data/lib/badline/frontend/media_slots.rb +8 -1
- data/lib/badline/frontend/menu/drive_types.rb +56 -0
- data/lib/badline/frontend/menu/menu_dialogs.rb +61 -14
- data/lib/badline/frontend/menu/menu_media.rb +47 -14
- data/lib/badline/frontend/menu/menu_pages.rb +113 -4
- data/lib/badline/frontend/menu/menu_ports.rb +77 -4
- data/lib/badline/frontend/menu/pause_menu.rb +6 -2
- data/lib/badline/frontend/pet_keyboard.rb +45 -7
- data/lib/badline/frontend/screen.rb +1 -1
- data/lib/badline/frontend/screens.rb +40 -2
- data/lib/badline/frontend/teletype_pane.rb +152 -0
- data/lib/badline/frontend/timeline.rb +49 -11
- data/lib/badline/frontend.rb +3 -0
- data/lib/badline/kernal_trap/dos/disk_commands.rb +80 -0
- data/lib/badline/kernal_trap/dos/status.rb +3 -0
- data/lib/badline/kernal_trap/dos.rb +18 -5
- data/lib/badline/kernal_trap/ieee.rb +4 -2
- data/lib/badline/kernal_trap/ieee_layout.rb +95 -43
- data/lib/badline/kim1/bus.rb +117 -0
- data/lib/badline/kim1/cpu.rb +25 -0
- data/lib/badline/kim1/host_terminal.rb +74 -0
- data/lib/badline/kim1/key_entry.rb +74 -0
- data/lib/badline/kim1/keyed_modules.rb +53 -0
- data/lib/badline/kim1/keyed_state.rb +133 -0
- data/lib/badline/kim1/panel.rb +176 -0
- data/lib/badline/kim1/rriots.rb +28 -0
- data/lib/badline/kim1/sound.rb +29 -0
- data/lib/badline/kim1/tape.rb +116 -0
- data/lib/badline/kim1/teletype.rb +193 -0
- data/lib/badline/kim1.rb +190 -0
- data/lib/badline/machine/ieee_drives.rb +171 -0
- data/lib/badline/machine/keyboard_buffer.rb +20 -3
- data/lib/badline/machine/line_sound.rb +122 -0
- data/lib/badline/machine/settling.rb +4 -0
- data/lib/badline/machine.rb +62 -14
- data/lib/badline/media/cbm2_media.rb +29 -0
- data/lib/badline/media/extensions.rb +8 -4
- data/lib/badline/media/kim1_media.rb +150 -0
- data/lib/badline/media/pet_media.rb +3 -1
- data/lib/badline/media/program.rb +6 -3
- data/lib/badline/media/true_drive.rb +51 -25
- data/lib/badline/media.rb +23 -8
- data/lib/badline/options/family.rb +108 -6
- data/lib/badline/options/help.rb +58 -12
- data/lib/badline/options/table.rb +60 -14
- data/lib/badline/options/validation.rb +71 -11
- data/lib/badline/options.rb +46 -13
- data/lib/badline/pet/bus.rb +16 -2
- data/lib/badline/pet/cassettes.rb +22 -1
- data/lib/badline/pet/ieee_drive.rb +5 -64
- data/lib/badline/pet/ieee_ports.rb +7 -0
- data/lib/badline/pet/keyboard_ports.rb +9 -2
- data/lib/badline/pet/keyed_state.rb +4 -0
- data/lib/badline/pet/model.rb +11 -2
- data/lib/badline/pet/rom_sets.rb +1 -0
- data/lib/badline/pet/sound.rb +5 -105
- data/lib/badline/pet/superpet/acia.rb +12 -0
- data/lib/badline/pet/superpet/board.rb +221 -0
- data/lib/badline/pet/superpet/dongle.rb +81 -0
- data/lib/badline/pet/superpet/keyed_modules.rb +63 -0
- data/lib/badline/pet/superpet/keyed_parts.rb +76 -0
- data/lib/badline/pet/superpet/m6809.rb +32 -0
- data/lib/badline/pet/superpet/waterloo_traps.rb +199 -0
- data/lib/badline/pet/superpet.rb +216 -0
- data/lib/badline/pet.rb +33 -6
- data/lib/badline/plus4/acia.rb +4 -298
- data/lib/badline/plus4/keyed_parts.rb +0 -44
- data/lib/badline/plus4/keyed_state.rb +2 -0
- data/lib/badline/plus4.rb +2 -0
- data/lib/badline/roms/1001/fdc-251257-02a.rom +0 -0
- data/lib/badline/roms/4040/dos-1.2.rom +0 -0
- data/lib/badline/roms/4040/dos-2.1-rev.rom +0 -0
- data/lib/badline/roms/4040/dos-2.1.rom +0 -0
- data/lib/badline/roms/4040/fdc-901466-02.rom +0 -0
- data/lib/badline/roms/4040/fdc-901466-04.rom +0 -0
- data/lib/badline/roms/8050/dos-2.7.rom +0 -0
- data/lib/badline/roms/8050/fdc-901869-01.rom +0 -0
- data/lib/badline/roms/8050/fdc-901885-04.rom +0 -0
- data/lib/badline/roms/9060/dos-3.0.rom +0 -0
- data/lib/badline/roms/9060/sasi-300515-revb.rom +0 -0
- data/lib/badline/roms/README +277 -0
- data/lib/badline/roms/cbm2/basic128-02b.rom +0 -0
- data/lib/badline/roms/cbm2/basic128.rom +0 -0
- data/lib/badline/roms/cbm2/basic256.rom +0 -0
- data/lib/badline/roms/cbm2/characters-600.rom +0 -0
- data/lib/badline/roms/cbm2/characters-700.rom +0 -0
- data/lib/badline/roms/cbm2/kernal-03b.rom +0 -0
- data/lib/badline/roms/cbm2/kernal-04a.rom +0 -0
- data/lib/badline/roms/cbm2/p500-basic.rom +0 -0
- data/lib/badline/roms/cbm2/p500-kernal.rom +0 -0
- data/lib/badline/roms/kim1/6530-002.rom +0 -0
- data/lib/badline/roms/kim1/6530-003.rom +0 -0
- data/lib/badline/roms/pet/characters-superpet.rom +0 -0
- data/lib/badline/roms/pet/superpet-a000.rom +0 -0
- data/lib/badline/roms/pet/superpet-c000.rom +0 -0
- data/lib/badline/roms/pet/superpet-e000-50hz.rom +0 -0
- data/lib/badline/roms/pet/superpet-e000.rom +0 -0
- data/lib/badline/snapshot/cbm2_setup.rb +53 -0
- data/lib/badline/snapshot/container.rb +9 -2
- data/lib/badline/snapshot/field_doc/markdown.rb +4 -2
- data/lib/badline/snapshot/field_doc.rb +59 -3
- data/lib/badline/snapshot/image.rb +65 -9
- data/lib/badline/snapshot/kim1_setup.rb +28 -0
- data/lib/badline/snapshot/pet_setup.rb +27 -11
- data/lib/badline/snapshot/storage_setup.rb +4 -1
- data/lib/badline/snapshot/vic20_setup.rb +11 -7
- data/lib/badline/snapshot/vice/cbm2.rb +93 -0
- data/lib/badline/snapshot/vice/cbm2_acia.rb +75 -0
- data/lib/badline/snapshot/vice/cbm2_crtc.rb +127 -0
- data/lib/badline/snapshot/vice/cbm2_mains.rb +49 -0
- data/lib/badline/snapshot/vice/cbm2_mem.rb +75 -0
- data/lib/badline/snapshot/vice/cbm2_restore.rb +204 -0
- data/lib/badline/snapshot/vice/cbm2_tpi.rb +58 -0
- data/lib/badline/snapshot/vice/cias.rb +13 -9
- data/lib/badline/snapshot/vice/p500.rb +96 -0
- data/lib/badline/snapshot/vice/p500_restore.rb +130 -0
- data/lib/badline/snapshot/vice/p500_vicii.rb +166 -0
- data/lib/badline/snapshot/vice/pet.rb +20 -10
- data/lib/badline/snapshot/vice/pet_cpu.rb +42 -5
- data/lib/badline/snapshot/vice/pet_crtc.rb +25 -17
- data/lib/badline/snapshot/vice/pet_drive.rb +180 -0
- data/lib/badline/snapshot/vice/pet_drive8050.rb +336 -0
- data/lib/badline/snapshot/vice/pet_mem.rb +49 -5
- data/lib/badline/snapshot/vice/pet_restore.rb +100 -7
- data/lib/badline/snapshot/vice/pet_via.rb +6 -5
- data/lib/badline/snapshot/vice/sid_registers.rb +2 -1
- data/lib/badline/snapshot/vice/vicii_import.rb +10 -6
- data/lib/badline/snapshot/vice.rb +3 -0
- data/lib/badline/snapshot.rb +15 -3
- data/lib/badline/status.rb +30 -30
- data/lib/badline/storage/crt_file.rb +7 -1
- data/lib/badline/storage/d64_image.rb +7 -0
- data/lib/badline/storage/d67_image.rb +55 -0
- data/lib/badline/storage/d80_image.rb +17 -1
- data/lib/badline/storage/d81_image.rb +14 -0
- data/lib/badline/storage/d90_image.rb +180 -0
- data/lib/badline/storage/disk_image/bam.rb +18 -3
- data/lib/badline/storage/disk_image/formatting.rb +69 -0
- data/lib/badline/storage/disk_image/writing.rb +37 -2
- data/lib/badline/storage/disk_image.rb +2 -0
- data/lib/badline/storage/kim_tape.rb +254 -0
- data/lib/badline/storage/papertape.rb +90 -0
- data/lib/badline/storage.rb +5 -0
- data/lib/badline/support/cbm2_checkpoint.rb +48 -0
- data/lib/badline/support/kim1_checkpoint.rb +41 -0
- data/lib/badline/support/p500_checkpoint.rb +35 -0
- data/lib/badline/support/superpet_checkpoint.rb +35 -0
- data/lib/badline/version.rb +1 -1
- data/lib/badline/vic20/keyed_modules.rb +6 -3
- data/lib/badline/vic20/keyed_state.rb +6 -4
- data/lib/badline/vic20.rb +26 -12
- data/lib/badline.rb +2 -0
- metadata +148 -2
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
class PET
|
|
5
|
+
class SuperPET < PET
|
|
6
|
+
# The SuperPET as keyed snapshot modules: the 8032's (PET::KeyedModules),
|
|
7
|
+
# then BL-SUPERPET with the board and the 6702 under dongle.*, BL-ACIA
|
|
8
|
+
# and BL-M6809. The traps on the Waterloo ROM's routines go in
|
|
9
|
+
# BL-TRAPDOS under waterloo.*, beside the KERNAL's. Without BL-ACIA or
|
|
10
|
+
# BL-M6809, as from xpet, the unit stays as it is. The CPU switch and
|
|
11
|
+
# the latches say which CPU has the bus, and the CPU that has it
|
|
12
|
+
# settles on its instruction boundary, or in SYNC or CWAI.
|
|
13
|
+
module KeyedModules
|
|
14
|
+
def settled_for_snapshot?
|
|
15
|
+
return super unless @m6809_running
|
|
16
|
+
|
|
17
|
+
@m6809.boundary? || @m6809.waiting?
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def save_modules
|
|
21
|
+
sections = super
|
|
22
|
+
board = @board
|
|
23
|
+
dongle = @dongle
|
|
24
|
+
sections << Snapshot::Keyed.section("BL-SUPERPET") do |out|
|
|
25
|
+
board.save_fields(out)
|
|
26
|
+
dongle.save_fields(out, "dongle.")
|
|
27
|
+
end
|
|
28
|
+
sections << Snapshot::Keyed.section("BL-ACIA") { |out| @acia.save_fields(out) }
|
|
29
|
+
sections << Snapshot::Keyed.section("BL-M6809") { |out| @m6809.save_fields(out) }
|
|
30
|
+
sections
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def load_modules(modules)
|
|
34
|
+
super
|
|
35
|
+
board = modules.required("BL-SUPERPET")
|
|
36
|
+
@board.load_fields(board)
|
|
37
|
+
@dongle.load_fields(board, "dongle.")
|
|
38
|
+
acia = modules.optional("BL-ACIA")
|
|
39
|
+
@acia.load_fields(acia) if acia
|
|
40
|
+
m6809 = modules.optional("BL-M6809")
|
|
41
|
+
@m6809.load_fields(m6809) if m6809
|
|
42
|
+
@m6809_running = @board.m6809?
|
|
43
|
+
@board.restored!
|
|
44
|
+
@acia.restored!
|
|
45
|
+
@m6809.restored!
|
|
46
|
+
self
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def save_trap_dos_fields(out)
|
|
50
|
+
super
|
|
51
|
+
@waterloo_traps&.save_fields(out, "waterloo.")
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def load_trap_dos_fields(input, detached: false)
|
|
55
|
+
super
|
|
56
|
+
@waterloo_traps&.load_fields(input, "waterloo.")
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
include KeyedModules
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
class PET
|
|
5
|
+
class SuperPET < PET
|
|
6
|
+
class Board
|
|
7
|
+
# The latches, the socket bit and the 64K of bank RAM, in a keyed
|
|
8
|
+
# snapshot module (BL-SUPERPET). The switches come with the setup
|
|
9
|
+
# (Snapshot::PETSetup). Which CPU has the bus, and the diagnostic
|
|
10
|
+
# sense line, follow from the latches once loaded, and the pages
|
|
11
|
+
# are laid out again (#restored!).
|
|
12
|
+
module KeyedState
|
|
13
|
+
def save_fields(out)
|
|
14
|
+
out.int("bank_latch", @bank_latch).int("system_latch", @system_latch).int("socket", @socket)
|
|
15
|
+
out.bytes("ram", @cells)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def load_fields(input)
|
|
19
|
+
@bank_latch = input.byte("bank_latch", 0)
|
|
20
|
+
@system_latch = input.byte("system_latch", 0)
|
|
21
|
+
@socket = input.int("socket", 0) & 0x01
|
|
22
|
+
input.bytes_into("ram", @cells)
|
|
23
|
+
@m6809 = m6809_selected?
|
|
24
|
+
@keyboard_ports.diagnostic_low = @system_latch.anybits?(0x08)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def restored! = @bus.map_pages
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
include KeyedState
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
class Dongle
|
|
34
|
+
# The shift registers, the latch and a shift waiting, as the cycles
|
|
35
|
+
# from now to the one it ends, under `prefix`.
|
|
36
|
+
module KeyedState
|
|
37
|
+
def save_fields(out, prefix)
|
|
38
|
+
out.ints("#{prefix}registers", @registers).int("#{prefix}latch", @latch)
|
|
39
|
+
out.optional_int("#{prefix}shift_in", @shift_at == NONE ? nil : @shift_at - @clock.cycles)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def load_fields(input, prefix)
|
|
43
|
+
input.ints_into("#{prefix}registers", @registers)
|
|
44
|
+
@latch = input.byte("#{prefix}latch", 0xff)
|
|
45
|
+
shift_in = input.optional_int("#{prefix}shift_in")
|
|
46
|
+
@shift_at = shift_in.nil? ? NONE : @clock.cycles + shift_in
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
include KeyedState
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
class WaterlooTraps
|
|
54
|
+
# Whether a served device listens or talks, the channel and the
|
|
55
|
+
# command it was addressed with, and the bytes sent it so far,
|
|
56
|
+
# under `prefix`.
|
|
57
|
+
module KeyedState
|
|
58
|
+
def save_fields(out, prefix)
|
|
59
|
+
out.bool("#{prefix}listening", @listening).bool("#{prefix}talking", @talking)
|
|
60
|
+
out.int("#{prefix}channel", @channel).int("#{prefix}frame", @frame).ints("#{prefix}buffer", @buffer)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def load_fields(input, prefix)
|
|
64
|
+
@listening = input.bool("#{prefix}listening", false)
|
|
65
|
+
@talking = input.bool("#{prefix}talking", false)
|
|
66
|
+
@channel = input.int("#{prefix}channel", 0) & 0x0f
|
|
67
|
+
@frame = input.byte("#{prefix}frame", 0)
|
|
68
|
+
@buffer = input.ints("#{prefix}buffer", []).map { |byte| byte & 0xff }
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
include KeyedState
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
class PET
|
|
5
|
+
class SuperPET < PET
|
|
6
|
+
# The board's 6809E, on the PET's bus, as the 6502 is. A trap
|
|
7
|
+
# installed on an address runs as the 6809 is about to fetch an
|
|
8
|
+
# opcode there, and may move the PC on.
|
|
9
|
+
class M6809
|
|
10
|
+
include Chips::M6809::Core
|
|
11
|
+
|
|
12
|
+
def initialize(bus)
|
|
13
|
+
super
|
|
14
|
+
@traps = nil
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def install_trap(addr, &handler)
|
|
18
|
+
(@traps ||= {})[addr] = handler
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def remove_trap(addr)
|
|
22
|
+
@traps&.delete(addr)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def cycle!
|
|
26
|
+
@traps[@pc]&.call if @traps && boundary?
|
|
27
|
+
super
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -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,216 @@
|
|
|
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
|
+
# In 6809 mode device 8 answers through traps on the Waterloo ROM's
|
|
30
|
+
# IEEE-488 routines (WaterlooTraps) as well as the KERNAL's, from the
|
|
31
|
+
# same disk, or a true drive on the bus answers. type_text types
|
|
32
|
+
# through the Waterloo ROM's key buffer, in ASCII, once its menu is up.
|
|
33
|
+
class SuperPET < PET
|
|
34
|
+
# The Waterloo ROM's key buffer: its read and write pointers, and the
|
|
35
|
+
# ring of bytes they run round.
|
|
36
|
+
KEYS_READ = 0x012c
|
|
37
|
+
KEYS_WRITE = 0x012e
|
|
38
|
+
KEYS_FIRST = 0x0130
|
|
39
|
+
KEYS_END = 0x0158
|
|
40
|
+
|
|
41
|
+
# The cycle by which the 6809 has put up the Waterloo menu, with a
|
|
42
|
+
# margin.
|
|
43
|
+
WATERLOO_BOOT = 600_000
|
|
44
|
+
|
|
45
|
+
attr_reader :board, :acia, :dongle, :m6809
|
|
46
|
+
|
|
47
|
+
# The traps on the Waterloo ROM's IEEE-488 routines, while a disk is
|
|
48
|
+
# on device 8 (WaterlooTraps).
|
|
49
|
+
attr_reader :waterloo_traps
|
|
50
|
+
|
|
51
|
+
# +cpu+ is SW2's position and +bank_ram+ SW1's, as Board names them.
|
|
52
|
+
# The other arguments are the PET's for an 8032, its true drive's too.
|
|
53
|
+
def initialize(refresh_hz: 50, debug: false, basic: nil, editor: nil, cpu: "6502", bank_ram: "rw",
|
|
54
|
+
true_drive_model: Drives::IEEEModels::DEFAULT)
|
|
55
|
+
raise ArgumentError, "the CPU switch has no position #{cpu}" unless Board::CPU_SWITCH.include?(cpu)
|
|
56
|
+
|
|
57
|
+
super(model: "superpet", refresh_hz:, debug:, basic:, editor:, true_drive_model:)
|
|
58
|
+
@acia_irq = false
|
|
59
|
+
@acia_due = Chips::ACIA::NEVER
|
|
60
|
+
@acia = ACIA.new(self, 1_000_000)
|
|
61
|
+
@dongle = Dongle.new(self)
|
|
62
|
+
@board = Board.new(@bus, dongle: @dongle, acia: @acia, keyboard_ports: @keyboard_ports, cpu_switch: cpu,
|
|
63
|
+
ram_switch: bank_ram, refresh_hz: @refresh_hz)
|
|
64
|
+
@m6809 = M6809.new(@bus)
|
|
65
|
+
@m6809_running = @board.m6809?
|
|
66
|
+
@waterloo_traps = nil
|
|
67
|
+
@board.on_select { cpu_selected }
|
|
68
|
+
@bus.fit_board(@board)
|
|
69
|
+
@init_threshold = WATERLOO_BOOT if @m6809_running
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Whether an autostart finds BASIC: not while the 6809 runs.
|
|
73
|
+
def boots_basic? = !@m6809_running
|
|
74
|
+
|
|
75
|
+
# Device 8 through the traps, for the Waterloo ROM's routines too.
|
|
76
|
+
def mount(storage)
|
|
77
|
+
trap = super
|
|
78
|
+
@waterloo_traps ||= WaterlooTraps.new(cpu: @m6809, bus: @bus, dos: @dos).install
|
|
79
|
+
@waterloo_traps.device = trap_device
|
|
80
|
+
trap
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def unmount
|
|
84
|
+
super
|
|
85
|
+
@waterloo_traps&.uninstall
|
|
86
|
+
@waterloo_traps = nil
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def attach_ieee_drive(drive)
|
|
90
|
+
super
|
|
91
|
+
@waterloo_traps&.device = trap_device
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def detach_ieee_drive
|
|
95
|
+
super
|
|
96
|
+
@waterloo_traps&.device = trap_device
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# Types into the Waterloo ROM's key buffer, in ASCII, while the 6809
|
|
100
|
+
# runs, and into the editor's otherwise.
|
|
101
|
+
def type_text(text)
|
|
102
|
+
return super unless @m6809_running
|
|
103
|
+
|
|
104
|
+
type_on_init(text.bytes)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# Whether the 6809 has the bus, rather than the 6502.
|
|
108
|
+
def m6809_running? = @m6809_running
|
|
109
|
+
|
|
110
|
+
def superpet? = true
|
|
111
|
+
|
|
112
|
+
# Moves the CPU switch to +position+, one of Board::CPU_SWITCH, and
|
|
113
|
+
# resets the machine, so the CPU the switch picks starts from its
|
|
114
|
+
# RESET.
|
|
115
|
+
def switch_cpu(position)
|
|
116
|
+
@board.cpu_switch = position
|
|
117
|
+
reset!
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# Moves the bank RAM switch to +position+, one of Board::RAM_SWITCH.
|
|
121
|
+
def switch_bank_ram(position)
|
|
122
|
+
@board.ram_switch = position
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# Whether the picture shows the character ROM's APL set: its second
|
|
126
|
+
# half, which the CRTC's MA13 picks, with VIA CA2 high.
|
|
127
|
+
def apl_characters? = @crtc.registers[12].anybits?(0x20) && @via.ca2_output
|
|
128
|
+
|
|
129
|
+
# Drive 1 while the 6809 runs, where the Waterloo menu's "disk/1."
|
|
130
|
+
# looks, and drive 0 in 6502 mode.
|
|
131
|
+
def disk_drive_number = @m6809_running ? 1 : 0
|
|
132
|
+
|
|
133
|
+
# The 32K below $8000 and the 64K board.
|
|
134
|
+
def model_label = "SUPERPET, 96K RAM, #{@refresh_hz} HZ"
|
|
135
|
+
|
|
136
|
+
# The PET's cycle, with the ACIA run when it falls due and its IRQ
|
|
137
|
+
# on the CPU's.
|
|
138
|
+
def cycle!
|
|
139
|
+
handle_init if @cycles == @init_threshold
|
|
140
|
+
feed_keyboard if @pending_keys
|
|
141
|
+
|
|
142
|
+
@crtc.cycle!
|
|
143
|
+
drive_retrace(!@crtc.vsync?)
|
|
144
|
+
@pia1.cycle!
|
|
145
|
+
@pia2.cycle!
|
|
146
|
+
@via.cycle!
|
|
147
|
+
@sound.cycle! if @sound.recording?
|
|
148
|
+
@cassettes.cycle! if @tapes_running
|
|
149
|
+
@acia.run!(@cycles) if @cycles >= @acia_due
|
|
150
|
+
irq = @pia1.irq? || @pia2.irq? || @via.irq? || @acia_irq
|
|
151
|
+
if @m6809_running
|
|
152
|
+
@m6809.irq = irq
|
|
153
|
+
@m6809.cycle!
|
|
154
|
+
else
|
|
155
|
+
@cpu.irq = irq
|
|
156
|
+
@cpu.cycle!
|
|
157
|
+
end
|
|
158
|
+
clock_ieee_drives
|
|
159
|
+
|
|
160
|
+
@cycles += 1
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
# Whether the ACIA pulls the IRQ line.
|
|
164
|
+
def acia_irq? = @acia_irq
|
|
165
|
+
|
|
166
|
+
# The ACIA's IRQ line, and the cycle it next has something to do at.
|
|
167
|
+
def acia_changed(irq, due)
|
|
168
|
+
@acia_irq = irq
|
|
169
|
+
@acia_due = due
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
# The RES line reaches the board's latches, the 6702, the ACIA and
|
|
173
|
+
# both CPUs too.
|
|
174
|
+
def reset!
|
|
175
|
+
super
|
|
176
|
+
@acia.reset!
|
|
177
|
+
@m6809.reset!
|
|
178
|
+
@board.reset!
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def power_cycle!
|
|
182
|
+
@board.power_on!
|
|
183
|
+
super
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
private
|
|
187
|
+
|
|
188
|
+
# The Waterloo ROM's ring takes keys until its write pointer would
|
|
189
|
+
# meet its read pointer.
|
|
190
|
+
def feed_keyboard
|
|
191
|
+
return super unless @m6809_running
|
|
192
|
+
|
|
193
|
+
read = word_at(KEYS_READ)
|
|
194
|
+
write = word_at(KEYS_WRITE)
|
|
195
|
+
until @pending_keys.empty?
|
|
196
|
+
following = write + 1 == KEYS_END ? KEYS_FIRST : write + 1
|
|
197
|
+
break if following == read
|
|
198
|
+
|
|
199
|
+
@bus.poke(write, @pending_keys.shift)
|
|
200
|
+
write = following
|
|
201
|
+
end
|
|
202
|
+
@bus.poke(KEYS_WRITE, write >> 8)
|
|
203
|
+
@bus.poke(KEYS_WRITE + 1, write & 0xff)
|
|
204
|
+
@pending_keys = nil if @pending_keys.empty?
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
def word_at(addr) = (@bus.peek(addr) << 8) | @bus.peek(addr + 1)
|
|
208
|
+
|
|
209
|
+
# The CPU the board picks takes the bus over from its RESET.
|
|
210
|
+
def cpu_selected
|
|
211
|
+
@m6809_running = @board.m6809?
|
|
212
|
+
@m6809_running ? @m6809.reset! : @cpu.reset!
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
end
|
|
216
|
+
end
|
data/lib/badline/pet.rb
CHANGED
|
@@ -83,10 +83,13 @@ module Badline
|
|
|
83
83
|
# model runs its 50 Hz editor, or its 60 Hz one for +refresh_hz+ 60.
|
|
84
84
|
# +basic+, +editor+ and +characters+ name ROMs the board takes in place
|
|
85
85
|
# of its own (Model.upgraded, Model.fitted), and +jumpers+ the 8296's
|
|
86
|
-
# board jumpers (ROMSets::JUMPERS).
|
|
86
|
+
# board jumpers (ROMSets::JUMPERS). +true_drive_model+ names the drive
|
|
87
|
+
# plug_true_drive plugs in (Drives::IEEEModels).
|
|
87
88
|
def initialize(model: Model::DEFAULT, ram_kb: nil, refresh_hz: 50, debug: false, basic: nil, editor: nil,
|
|
88
|
-
characters: nil, jumpers: [])
|
|
89
|
+
characters: nil, jumpers: [], true_drive_model: Drives::IEEEModels::DEFAULT)
|
|
89
90
|
@model = Model.fitted(Model.upgraded(Model.named(model), basic, editor), characters)
|
|
91
|
+
raise ArgumentError, "the SuperPET is built as PET::SuperPET" if @model.superpet? && !superpet?
|
|
92
|
+
|
|
90
93
|
ram_kb ||= @model.ram_kb
|
|
91
94
|
raise ArgumentError, "the #{@model.name} has no #{ram_kb}K RAM board" unless Model.ram_fits?(@model, ram_kb)
|
|
92
95
|
|
|
@@ -99,6 +102,7 @@ module Badline
|
|
|
99
102
|
matrix = @model.keyboard == :business ? KeyboardPorts::BUSINESS_MATRIX : KeyboardPorts::GRAPHICS_MATRIX
|
|
100
103
|
@keyboard = Input::Keyboard.new(matrix:)
|
|
101
104
|
keyboard_ports = KeyboardPorts.new(@keyboard)
|
|
105
|
+
@keyboard_ports = keyboard_ports
|
|
102
106
|
registers = Array.new(Chips::CRTC::MASKS.length, 0)
|
|
103
107
|
@pia1 = Chips::PIA.new(peripheral: keyboard_ports)
|
|
104
108
|
@ieee = IEEEPorts.new(Drives::IEEEBus.new)
|
|
@@ -106,6 +110,7 @@ module Badline
|
|
|
106
110
|
@via = VIA.new(start: 0xe800, peripheral: VIAPorts.new(self))
|
|
107
111
|
@sound = Sound.new(@via)
|
|
108
112
|
@cassettes = Cassettes.new(@pia1, @via)
|
|
113
|
+
watch_tapes
|
|
109
114
|
keyboard_ports.connect(@pia1, @ieee, @cassettes)
|
|
110
115
|
characters = Characters.new(@model.characters)
|
|
111
116
|
@crtc_output = CRTCVideo.new(characters, @via, registers, columns80: @model.video == :crtc80,
|
|
@@ -127,13 +132,23 @@ module Badline
|
|
|
127
132
|
@cycles = 0
|
|
128
133
|
@init_handlers = []
|
|
129
134
|
@pending_keys = nil
|
|
135
|
+
@init_keys = nil
|
|
130
136
|
@dos = nil
|
|
131
137
|
@ieee_trap = nil
|
|
132
138
|
@capture_output = nil
|
|
133
|
-
|
|
139
|
+
init_ieee_drives(true_drive_model)
|
|
134
140
|
@init_threshold = boot_cycles(ram_kb)
|
|
135
141
|
end
|
|
136
142
|
|
|
143
|
+
# Whether this is the SuperPET's board (SuperPET).
|
|
144
|
+
def superpet? = false
|
|
145
|
+
|
|
146
|
+
# Whether the picture shows the SuperPET's APL characters.
|
|
147
|
+
def apl_characters? = false
|
|
148
|
+
|
|
149
|
+
# Whether an autostart finds BASIC to type into.
|
|
150
|
+
def boots_basic? = true
|
|
151
|
+
|
|
137
152
|
# The vertical drive line, high but in the retrace.
|
|
138
153
|
def vertical_drive? = @vertical_drive
|
|
139
154
|
|
|
@@ -177,10 +192,10 @@ module Badline
|
|
|
177
192
|
@pia2.cycle!
|
|
178
193
|
@via.cycle!
|
|
179
194
|
@sound.cycle! if @sound.recording?
|
|
180
|
-
@cassettes.cycle!
|
|
195
|
+
@cassettes.cycle! if @tapes_running
|
|
181
196
|
@cpu.irq = @pia1.irq? || @pia2.irq? || @via.irq?
|
|
182
197
|
@cpu.cycle!
|
|
183
|
-
|
|
198
|
+
clock_ieee_drives
|
|
184
199
|
|
|
185
200
|
@cycles += 1
|
|
186
201
|
end
|
|
@@ -197,7 +212,7 @@ module Badline
|
|
|
197
212
|
@via.reset!
|
|
198
213
|
@crtc.reset!
|
|
199
214
|
@bus.reset!
|
|
200
|
-
|
|
215
|
+
reset_ieee_drives
|
|
201
216
|
@ieee.lines_written
|
|
202
217
|
@cpu.reset!
|
|
203
218
|
end
|
|
@@ -234,6 +249,16 @@ module Badline
|
|
|
234
249
|
# on a 3032 and 1,980,000 on an 8032.
|
|
235
250
|
def boot_cycles(ram_kb) = (@model.basic == "pet/basic4.rom" ? 700_000 : 150_000) + (46_000 * ram_kb)
|
|
236
251
|
|
|
252
|
+
# The decks are clocked only while a tape runs.
|
|
253
|
+
def watch_tapes
|
|
254
|
+
@tapes_running = false
|
|
255
|
+
@cassettes.on_running_change { tapes_changed }
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
def tapes_changed
|
|
259
|
+
@tapes_running = @cassettes.running?
|
|
260
|
+
end
|
|
261
|
+
|
|
237
262
|
def drive_retrace(high)
|
|
238
263
|
return if high == @vertical_drive
|
|
239
264
|
|
|
@@ -242,3 +267,5 @@ module Badline
|
|
|
242
267
|
end
|
|
243
268
|
end
|
|
244
269
|
end
|
|
270
|
+
|
|
271
|
+
require "badline/pet/superpet"
|