badline 0.15.1 → 0.16.1
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 +55 -0
- data/CLAUDE.md +8 -7
- data/README.md +128 -9
- data/doc/media.md +9 -0
- data/doc/pinned-behaviour.md +67 -0
- data/doc/snapshot-format.md +215 -18
- data/doc/snapshots.md +55 -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 +74 -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/model2031/ieee_port.rb +133 -0
- data/lib/badline/drives/model2031/keyed_state.rb +66 -0
- data/lib/badline/drives/model2031/parts.rb +25 -0
- data/lib/badline/drives/model2031.rb +92 -0
- 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 +24 -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 +151 -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 +29 -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/true_drive.rb +19 -6
- data/lib/badline/media/vic20_media.rb +2 -2
- data/lib/badline/media.rb +41 -17
- data/lib/badline/options/family.rb +17 -6
- data/lib/badline/options/help.rb +23 -3
- data/lib/badline/options/table.rb +22 -4
- data/lib/badline/options/validation.rb +84 -5
- data/lib/badline/options.rb +47 -16
- data/lib/badline/pet/bus/expansion.rb +200 -0
- data/lib/badline/pet/bus.rb +222 -0
- data/lib/badline/pet/canvas.rb +97 -0
- data/lib/badline/pet/cassettes.rb +63 -0
- data/lib/badline/pet/characters.rb +32 -0
- data/lib/badline/pet/cpu.rb +10 -0
- data/lib/badline/pet/crtc_video.rb +155 -0
- data/lib/badline/pet/discrete_video.rb +83 -0
- data/lib/badline/pet/ieee_drive.rb +76 -0
- data/lib/badline/pet/ieee_ports.rb +77 -0
- data/lib/badline/pet/ieee_traps.rb +80 -0
- data/lib/badline/pet/keyboard_ports.rb +74 -0
- data/lib/badline/pet/keyed_modules.rb +88 -0
- data/lib/badline/pet/keyed_parts.rb +127 -0
- data/lib/badline/pet/keyed_state.rb +42 -0
- data/lib/badline/pet/model.rb +163 -0
- data/lib/badline/pet/rom_sets.rb +89 -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 +244 -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 +169 -0
- data/lib/badline/roms/dos2031.rom +0 -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/characters-din.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-8296.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 +6 -1
- data/lib/badline/snapshot/field_doc/markdown.rb +1 -1
- data/lib/badline/snapshot/field_doc.rb +17 -3
- data/lib/badline/snapshot/image.rb +28 -10
- data/lib/badline/snapshot/pet_setup.rb +88 -0
- data/lib/badline/snapshot/storage_setup.rb +7 -1
- data/lib/badline/snapshot/vice/pet.rb +117 -0
- data/lib/badline/snapshot/vice/pet_cpu.rb +67 -0
- data/lib/badline/snapshot/vice/pet_crtc.rb +246 -0
- data/lib/badline/snapshot/vice/pet_mem.rb +93 -0
- data/lib/badline/snapshot/vice/pet_pias.rb +56 -0
- data/lib/badline/snapshot/vice/pet_restore.rb +150 -0
- data/lib/badline/snapshot/vice/pet_via.rb +164 -0
- data/lib/badline/snapshot/vice.rb +1 -0
- 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 +70 -1
|
@@ -7,6 +7,8 @@ module Badline
|
|
|
7
7
|
# It loads on its own, for Options.
|
|
8
8
|
#
|
|
9
9
|
# - :disk, a disk image that the traps mount and a true drive reads
|
|
10
|
+
# - :ieee_disk, an 8050's or 8250's disk image, for a PET's IEEE-488
|
|
11
|
+
# drive
|
|
10
12
|
# - :gcr, a disk's raw GCR, which only a true drive reads
|
|
11
13
|
# - :archive, a .t64, which the traps mount read-only
|
|
12
14
|
# - :tape, :cartridge and :tune
|
|
@@ -14,7 +16,8 @@ module Badline
|
|
|
14
16
|
module Extensions
|
|
15
17
|
KINDS = {
|
|
16
18
|
".d64" => :disk, ".d71" => :disk, ".d81" => :disk, ".g64" => :gcr, ".g71" => :gcr, ".t64" => :archive,
|
|
17
|
-
".
|
|
19
|
+
".d80" => :ieee_disk, ".d82" => :ieee_disk, ".tap" => :tape, ".crt" => :cartridge, ".sid" => :tune,
|
|
20
|
+
".m3u" => :disk_list, ".vfl" => :disk_list
|
|
18
21
|
}.freeze
|
|
19
22
|
|
|
20
23
|
# The kind of the medium at `path`, by its extension.
|
|
@@ -27,7 +30,7 @@ module Badline
|
|
|
27
30
|
DISKS = of(%i[disk gcr])
|
|
28
31
|
|
|
29
32
|
# What goes in device 8 through the traps.
|
|
30
|
-
MOUNTABLE = of(%i[disk archive])
|
|
33
|
+
MOUNTABLE = of(%i[disk archive ieee_disk])
|
|
31
34
|
|
|
32
35
|
# What goes in device 8, a true drive's or the traps', and the lists
|
|
33
36
|
# of disks to put there.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
module Media
|
|
5
|
+
# What a PET does with media of its own (Media.attach). Disks of every
|
|
6
|
+
# kind, .d80 and .d82 among them, directories and .t64 archives mount
|
|
7
|
+
# on device 8 through the traps on the KERNAL's IEEE-488 primitives
|
|
8
|
+
# (PET::IEEETraps), programs load at the start of BASIC, $0401
|
|
9
|
+
# (Program), and tapes play on the datasette at cassette port #1. There
|
|
10
|
+
# is no cartridge port and no SID, and the true drives are serial ones.
|
|
11
|
+
module PETMedia
|
|
12
|
+
# What a PET doesn't take.
|
|
13
|
+
UNSUPPORTED = Extensions.of(%i[tune cartridge])
|
|
14
|
+
|
|
15
|
+
# What loads and runs the first program on the disk in device 8.
|
|
16
|
+
AUTOSTART = %(load"*",8\rrun\r)
|
|
17
|
+
|
|
18
|
+
# What loads and runs the first program on the tape in cassette #1.
|
|
19
|
+
TAPE_AUTOSTART = "load\rrun\r"
|
|
20
|
+
|
|
21
|
+
class << self
|
|
22
|
+
# A tape made for another machine plays all the same, and says so.
|
|
23
|
+
def tape_message(tape, message)
|
|
24
|
+
tape.platform == Storage::TAP::PET_PLATFORM ? message : "#{message}, a tape for another machine than the PET"
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# BASIC 2 and 4 load the first program off the disk and run it.
|
|
28
|
+
# BASIC 1's LOAD finds no device on the IEEE-488 bus, so the first
|
|
29
|
+
# program, which the block gives, goes in as a program file does,
|
|
30
|
+
# once the KERNAL has booted.
|
|
31
|
+
def disk_autostart(machine)
|
|
32
|
+
return machine.type_text(AUTOSTART) unless machine.model.basic1?
|
|
33
|
+
|
|
34
|
+
data = yield
|
|
35
|
+
machine.on_init { Program.load(machine, data, autostart: true) } if data
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -2,12 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
module Badline
|
|
4
4
|
module Media
|
|
5
|
-
# A program loading into a C64, a C128
|
|
6
|
-
# has booted (Media.attach). The VIC-20 has its own
|
|
5
|
+
# A program loading into a C64, a C128, a 264 series machine or a PET
|
|
6
|
+
# once it has booted (Media.attach). The VIC-20 has its own
|
|
7
|
+
# (Vic20Basic).
|
|
7
8
|
module Program
|
|
8
9
|
# Where BASIC 3.5 starts on the 264 series.
|
|
9
10
|
PLUS4_BASIC_START = 0x1001
|
|
10
11
|
|
|
12
|
+
# Where BASIC starts on every PET.
|
|
13
|
+
PET_BASIC_START = 0x0401
|
|
14
|
+
|
|
11
15
|
class << self
|
|
12
16
|
# Loads the program where it says, and RUNs one that loads at the
|
|
13
17
|
# start of BASIC.
|
|
@@ -30,12 +34,20 @@ module Badline
|
|
|
30
34
|
# Where BASIC starts, the pointer to the end of its text and the
|
|
31
35
|
# KERNAL's end address (EAL). BASIC 7.0 starts at $1C01 and keeps
|
|
32
36
|
# the end of its text at $1210, and the 264 KERNAL keeps EAL at $9D.
|
|
37
|
+
# PET BASIC 2 and 4 keep the end of their text at $2A and EAL at
|
|
38
|
+
# $C9, and BASIC 1 at $7C and $E5.
|
|
33
39
|
def basic(computer)
|
|
34
40
|
return [PLUS4_BASIC_START, 0x2d, KernalTrap::PLUS4_ZERO_PAGE.end_address] if computer.family == :plus4
|
|
41
|
+
return pet_basic(computer) if computer.family == :pet
|
|
35
42
|
return [0x1c01, 0x1210, 0xae] if computer.family == :c128 && computer.mode == :c128
|
|
36
43
|
|
|
37
44
|
[BASIC_START, 0x2d, 0xae]
|
|
38
45
|
end
|
|
46
|
+
|
|
47
|
+
def pet_basic(computer)
|
|
48
|
+
model = computer.model
|
|
49
|
+
[PET_BASIC_START, model.basic1? ? 0x7c : 0x2a, model.trap_layout.zero_page.end_address]
|
|
50
|
+
end
|
|
39
51
|
end
|
|
40
52
|
end
|
|
41
53
|
end
|
|
@@ -4,15 +4,16 @@ module Badline
|
|
|
4
4
|
module Media
|
|
5
5
|
# Device 8 as a true drive instead of the KERNAL traps: a 1541, or on
|
|
6
6
|
# the C128 its 1571, or on the Plus/4 a 1551 if it asks for one, or a
|
|
7
|
-
# 1581. Once a true drive is plugged in as device 8, disk media go into
|
|
7
|
+
# 1581, or on the PET a 2031 on the IEEE-488 bus. Once a true drive is plugged in as device 8, disk media go into
|
|
8
8
|
# it rather than into the traps, so a LOAD runs the DOS and every byte
|
|
9
9
|
# crosses the serial bus or TCBM. A .g64 holds the
|
|
10
10
|
# disk's raw GCR, which only a true drive reads, so it plugs one in
|
|
11
11
|
# when there's none yet. The 1541 reads only .d64 and .g64 images, the
|
|
12
12
|
# 1571 .d71 and .g71 as well, and the 1581 only .d81, so a .d81 swaps
|
|
13
13
|
# the drive on device 8 for a 1581 (Drives::Model1581::Slot), and another disk
|
|
14
|
-
# image swaps a 1581 back for the machine's own drive. The
|
|
15
|
-
#
|
|
14
|
+
# image swaps a 1581 back for the machine's own drive. The PET has no
|
|
15
|
+
# 1581, and its 2031 reads .d64 and .g64 images only. The traps' other
|
|
16
|
+
# disk media, a .t64, a host directory, or a .d80 or .d82, raise Error.
|
|
16
17
|
module TrueDrive
|
|
17
18
|
class Error < ArgumentError; end
|
|
18
19
|
|
|
@@ -36,7 +37,7 @@ module Badline
|
|
|
36
37
|
kind = Extensions.kind(path)
|
|
37
38
|
return true if kind == :gcr
|
|
38
39
|
|
|
39
|
-
!drive(computer).nil? && (File.directory?(path) || %i[disk archive].include?(kind))
|
|
40
|
+
!drive(computer).nil? && (File.directory?(path) || %i[disk archive ieee_disk].include?(kind))
|
|
40
41
|
end
|
|
41
42
|
|
|
42
43
|
# Puts the image in the drive on device 8, plugging one in that
|
|
@@ -44,12 +45,15 @@ module Badline
|
|
|
44
45
|
# mounted through the traps, so LOAD and SAVE reach the drive too.
|
|
45
46
|
# `read_only` puts it in write-protected.
|
|
46
47
|
def insert(computer, path, read_only: false)
|
|
47
|
-
|
|
48
|
+
pet = computer.family == :pet
|
|
49
|
+
return insert_d81(computer, path, read_only) if !pet && File.extname(path).casecmp?(".d81")
|
|
48
50
|
|
|
49
51
|
drive = own_drive(computer)
|
|
50
|
-
name = drive&.model_name || (computer
|
|
52
|
+
name = drive&.model_name || default_model(computer)
|
|
51
53
|
types = name == "1571" ? %w[.d64 .g64 .d71 .g71] : %w[.d64 .g64]
|
|
52
54
|
unless types.include?(File.extname(path).downcase)
|
|
55
|
+
raise Error, "#{path} is not a #{types.join(' or ')} image, which the #{name} reads" if pet
|
|
56
|
+
|
|
53
57
|
raise Error, "#{path} is not a #{types.join(', ')} or .d81 image, which the #{name} and the 1581 read"
|
|
54
58
|
end
|
|
55
59
|
|
|
@@ -61,6 +65,15 @@ module Badline
|
|
|
61
65
|
|
|
62
66
|
private
|
|
63
67
|
|
|
68
|
+
# The model plug_true_drive plugs in.
|
|
69
|
+
def default_model(computer)
|
|
70
|
+
case computer.family
|
|
71
|
+
when :c128 then "1571"
|
|
72
|
+
when :pet then "2031"
|
|
73
|
+
else "1541"
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
64
77
|
# The drive on device 8 unless it's a 1581.
|
|
65
78
|
def own_drive(computer)
|
|
66
79
|
drive = drive(computer)
|
|
@@ -61,11 +61,11 @@ module Badline
|
|
|
61
61
|
|
|
62
62
|
# Loads the program, `bytes` from the file at `path`, as Vic20Basic
|
|
63
63
|
# has it once the KERNAL has booted, or attaches the cartridge it's
|
|
64
|
-
# a part of (Vic20Cartridge), and says which.
|
|
64
|
+
# a part of (Vic20Cartridge), named after the file, and says which.
|
|
65
65
|
def attach_program(machine, path, bytes, autostart:)
|
|
66
66
|
chips = Vic20Cartridge.chips(path, bytes)
|
|
67
67
|
if chips
|
|
68
|
-
machine.attach_cartridge(chips)
|
|
68
|
+
machine.attach_cartridge(chips, File.basename(path, File.extname(path)))
|
|
69
69
|
return "Attached cartridge #{path}"
|
|
70
70
|
end
|
|
71
71
|
|
data/lib/badline/media.rb
CHANGED
|
@@ -11,6 +11,7 @@ require "badline/media/vic20_basic"
|
|
|
11
11
|
require "badline/media/vic20_cartridge"
|
|
12
12
|
require "badline/media/vic20_media"
|
|
13
13
|
require "badline/media/plus4_media"
|
|
14
|
+
require "badline/media/pet_media"
|
|
14
15
|
require "badline/media/tune"
|
|
15
16
|
require "badline/media/program"
|
|
16
17
|
|
|
@@ -25,7 +26,9 @@ module Badline
|
|
|
25
26
|
".d64" => Storage::D64Image,
|
|
26
27
|
".d71" => Storage::D71Image,
|
|
27
28
|
".d81" => Storage::D81Image,
|
|
28
|
-
".t64" => Storage::T64
|
|
29
|
+
".t64" => Storage::T64,
|
|
30
|
+
".d80" => Storage::D80Image,
|
|
31
|
+
".d82" => Storage::D82Image
|
|
29
32
|
}.freeze
|
|
30
33
|
|
|
31
34
|
class << self
|
|
@@ -48,8 +51,10 @@ module Badline
|
|
|
48
51
|
# disk autostart of its own, and takes no .sid tunes, and the 264
|
|
49
52
|
# series loads programs at the start of its BASIC (Program), takes
|
|
50
53
|
# .crt cartridges and raw .bin ROM images in its two cartridges'
|
|
51
|
-
# slots (Plus4Media), and no .sid tunes either. The
|
|
52
|
-
#
|
|
54
|
+
# slots (Plus4Media), and no .sid tunes either. The PET mounts .d80
|
|
55
|
+
# and .d82 disks too, which no other machine takes, and has no
|
|
56
|
+
# cartridges, tunes or true drives (PETMedia). The C128 in C64 mode
|
|
57
|
+
# takes a C64's.
|
|
53
58
|
def attach(computer, path, autostart: true, subtune: nil, **)
|
|
54
59
|
path = DiskList.disk(path)
|
|
55
60
|
refuse_unsupported(computer, path)
|
|
@@ -99,7 +104,7 @@ module Badline
|
|
|
99
104
|
return :cartridge if computer.family == :plus4 && Plus4Media.rom_image?(path)
|
|
100
105
|
|
|
101
106
|
kind = Extensions.kind(path)
|
|
102
|
-
%i[disk archive].include?(kind) ? :storage : kind
|
|
107
|
+
%i[disk archive ieee_disk].include?(kind) ? :storage : kind
|
|
103
108
|
end
|
|
104
109
|
|
|
105
110
|
# What device 8 serves through the traps for a host directory, a disk
|
|
@@ -120,14 +125,21 @@ module Badline
|
|
|
120
125
|
|
|
121
126
|
private
|
|
122
127
|
|
|
123
|
-
# The VIC-20 and the 264 series have no SID to play a .sid tune on
|
|
128
|
+
# The VIC-20 and the 264 series have no SID to play a .sid tune on,
|
|
129
|
+
# and only the PET has the IEEE-488 drive a .d80 or .d82 goes in.
|
|
124
130
|
def refuse_unsupported(computer, path)
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
when :vic20 then raise ArgumentError, "#{path} doesn't go in a VIC-20"
|
|
129
|
-
when :plus4 then raise ArgumentError, "#{path} doesn't go in a Plus/4"
|
|
131
|
+
family = computer.family
|
|
132
|
+
if Extensions.kind(path) == :ieee_disk && family != :pet
|
|
133
|
+
raise ArgumentError, "#{path} needs a PET's IEEE-488 drive"
|
|
130
134
|
end
|
|
135
|
+
|
|
136
|
+
refused, name = case family
|
|
137
|
+
when :vic20 then [Vic20Media::UNSUPPORTED, "a VIC-20"]
|
|
138
|
+
when :plus4 then [Vic20Media::UNSUPPORTED, "a Plus/4"]
|
|
139
|
+
when :pet then [PETMedia::UNSUPPORTED, "a PET"]
|
|
140
|
+
else [[], ""]
|
|
141
|
+
end
|
|
142
|
+
raise ArgumentError, "#{path} doesn't go in #{name}" if refused.include?(File.extname(path).downcase)
|
|
131
143
|
end
|
|
132
144
|
|
|
133
145
|
def attach_kind(computer, path, autostart:, subtune:, **options)
|
|
@@ -143,18 +155,26 @@ module Badline
|
|
|
143
155
|
end
|
|
144
156
|
end
|
|
145
157
|
|
|
146
|
-
def disk?(path) = File.directory?(path) || Extensions.kind(path)
|
|
158
|
+
def disk?(path) = File.directory?(path) || %i[disk ieee_disk].include?(Extensions.kind(path))
|
|
147
159
|
|
|
148
160
|
def attach_true_drive(computer, path, disk, autostart:)
|
|
149
161
|
message = TrueDrive.insert(computer, path, read_only: disk.fetch(:read_only, false))
|
|
150
|
-
|
|
162
|
+
if autostart && computer.family == :pet
|
|
163
|
+
PETMedia.disk_autostart(computer) { Vic20Media.first_program(path) }
|
|
164
|
+
elsif autostart
|
|
165
|
+
computer.type_text(disk_autostart(computer) { Vic20Media.first_program(path) })
|
|
166
|
+
end
|
|
151
167
|
message
|
|
152
168
|
end
|
|
153
169
|
|
|
154
170
|
def mount(computer, path, disk, autostart:)
|
|
155
171
|
storage = open_storage(path, disk)
|
|
156
172
|
computer.mount(storage)
|
|
157
|
-
|
|
173
|
+
if autostart && computer.family == :pet
|
|
174
|
+
PETMedia.disk_autostart(computer) { storage.read_file("*") }
|
|
175
|
+
elsif autostart
|
|
176
|
+
computer.type_text(disk_autostart(computer) { storage.read_file("*") })
|
|
177
|
+
end
|
|
158
178
|
"Mounted #{path} as device 8"
|
|
159
179
|
end
|
|
160
180
|
|
|
@@ -170,7 +190,9 @@ module Badline
|
|
|
170
190
|
# (Plus4Media.cartridge_chips).
|
|
171
191
|
def attach_cartridge(computer, path, options)
|
|
172
192
|
case computer.family
|
|
173
|
-
when :vic20
|
|
193
|
+
when :vic20
|
|
194
|
+
crt = Storage::CRTFile.new(path, machine: :vic20)
|
|
195
|
+
computer.attach_cartridge(crt.chips, crt.name.empty? ? File.basename(path, File.extname(path)) : crt.name)
|
|
174
196
|
when :plus4 then computer.attach_cartridge(Plus4Media.cartridge_chips(path, slot: options.fetch(:slot, nil)))
|
|
175
197
|
else
|
|
176
198
|
computer.attach_cartridge(C64::Cartridge.from_file(path, **options))
|
|
@@ -178,17 +200,19 @@ module Badline
|
|
|
178
200
|
"Attached cartridge #{path}"
|
|
179
201
|
end
|
|
180
202
|
|
|
181
|
-
# A VIC-20 or a
|
|
182
|
-
# (Vic20Media.tape_message, Plus4Media.tape_message
|
|
203
|
+
# A VIC-20, a 264 or a PET says when the tape was made for another
|
|
204
|
+
# machine (Vic20Media.tape_message, Plus4Media.tape_message,
|
|
205
|
+
# PETMedia.tape_message).
|
|
183
206
|
def attach_tape(computer, path, autostart:)
|
|
184
207
|
tape = Storage::TAP.new(path)
|
|
185
208
|
computer.datasette.insert(tape)
|
|
186
209
|
computer.datasette.play!
|
|
187
|
-
computer.type_text(TAPE_AUTOSTART) if autostart
|
|
210
|
+
computer.type_text(computer.family == :pet ? PETMedia::TAPE_AUTOSTART : TAPE_AUTOSTART) if autostart
|
|
188
211
|
message = "Inserted #{path} in the datasette"
|
|
189
212
|
case computer.family
|
|
190
213
|
when :vic20 then Vic20Media.tape_message(tape, message)
|
|
191
214
|
when :plus4 then Plus4Media.tape_message(tape, message)
|
|
215
|
+
when :pet then PETMedia.tape_message(tape, message)
|
|
192
216
|
else message
|
|
193
217
|
end
|
|
194
218
|
end
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "badline/storage/p00"
|
|
4
|
+
require "badline/storage/tap"
|
|
4
5
|
|
|
5
6
|
module Badline
|
|
6
7
|
class Options
|
|
@@ -12,7 +13,7 @@ module Badline
|
|
|
12
13
|
|
|
13
14
|
private
|
|
14
15
|
|
|
15
|
-
# Takes `sid`, `c64`, `vic20`, `c128` or `
|
|
16
|
+
# Takes `sid`, `c64`, `vic20`, `c128`, `plus4` or `pet` when given first.
|
|
16
17
|
def subcommand(args)
|
|
17
18
|
case args.first
|
|
18
19
|
when "sid" then @sid_command = true
|
|
@@ -20,6 +21,7 @@ module Badline
|
|
|
20
21
|
when "vic20" then @family = :vic20
|
|
21
22
|
when "c128" then @family = :c128
|
|
22
23
|
when "plus4" then @family = :plus4
|
|
24
|
+
when "pet" then @family = :pet
|
|
23
25
|
else return
|
|
24
26
|
end
|
|
25
27
|
@family_named = !@sid_command
|
|
@@ -31,19 +33,28 @@ module Badline
|
|
|
31
33
|
when :vic20 then VIC20_MODELS
|
|
32
34
|
when :c128 then C128_MODELS
|
|
33
35
|
when :plus4 then PLUS4_MODELS
|
|
36
|
+
when :pet then PET_MODELS
|
|
34
37
|
else MODELS
|
|
35
38
|
end
|
|
36
39
|
end
|
|
37
40
|
|
|
38
41
|
# Without a family named, a program that loads at C128_BASIC_START is
|
|
39
|
-
# the C128's.
|
|
42
|
+
# the C128's, and an 8050's or 8250's disk or a PET tape the PET's. A
|
|
43
|
+
# program that loads where PET BASIC starts could be a VIC-20's too.
|
|
40
44
|
def pick_family
|
|
41
|
-
return if @family_named || @sid_command || @media_path.nil?
|
|
45
|
+
return if @family_named || @sid_command || @media_path.nil? || !File.file?(@media_path)
|
|
42
46
|
|
|
43
|
-
|
|
44
|
-
|
|
47
|
+
if pet_media?
|
|
48
|
+
@family = :pet
|
|
49
|
+
elsif Media::Extensions::PROGRAMS.include?(File.extname(@media_path).downcase)
|
|
50
|
+
@family = :c128 if Storage::P00.load_address(@media_path) == C128_BASIC_START
|
|
51
|
+
end
|
|
52
|
+
end
|
|
45
53
|
|
|
46
|
-
|
|
54
|
+
# Whether the media is an 8050's or 8250's disk or a PET tape.
|
|
55
|
+
def pet_media?
|
|
56
|
+
kind = Media::Extensions.kind(@media_path)
|
|
57
|
+
kind == :ieee_disk || (kind == :tape && Storage::TAP.platform_of(@media_path) == Storage::TAP::PET_PLATFORM)
|
|
47
58
|
end
|
|
48
59
|
end
|
|
49
60
|
end
|
data/lib/badline/options/help.rb
CHANGED
|
@@ -49,7 +49,8 @@ module Badline
|
|
|
49
49
|
# wrapped as the banner is.
|
|
50
50
|
def media_kinds
|
|
51
51
|
text = "Media can be #{listed(Media::Extensions::PROGRAMS)} program, #{listed(%i[disk])} disk image, " \
|
|
52
|
-
"#{listed(%i[
|
|
52
|
+
"#{listed(%i[ieee_disk])} disk image for a PET, #{listed(%i[gcr])} disk image for a true drive, " \
|
|
53
|
+
"#{listed(%i[disk_list])} list of disk images, " \
|
|
53
54
|
"#{listed(%i[archive])} tape archive, #{listed(%i[tape])} tape, #{listed(%i[cartridge])} " \
|
|
54
55
|
"cartridge, #{listed(%i[tune])} tune, #{listed(%w[.vsf])} snapshot, or a directory to mount as device 8."
|
|
55
56
|
wrap(text)
|
|
@@ -84,6 +85,7 @@ module Badline
|
|
|
84
85
|
#{@program} vic20 [options] [media]
|
|
85
86
|
#{@program} c128 [options] [media]
|
|
86
87
|
#{@program} plus4 [options] [media]
|
|
88
|
+
#{@program} pet [options] [media]
|
|
87
89
|
#{@program} --headless [options] tune.sid
|
|
88
90
|
#{@program} [options] tune.sid --audio-out FILE
|
|
89
91
|
#{@program} sid [options] FILE|DIR...
|
|
@@ -93,6 +95,7 @@ module Badline
|
|
|
93
95
|
vic20 Run a PAL VIC-20, with the RAM --ram names
|
|
94
96
|
c128 Run a C128 in C128 mode, the one --model names
|
|
95
97
|
plus4 Run a Plus/4 or a C16, the one --model names
|
|
98
|
+
pet Run a PET or a CBM, the one --model names
|
|
96
99
|
sid Play .sid tunes and directories of them
|
|
97
100
|
|
|
98
101
|
#{media_kinds}
|
|
@@ -116,6 +119,22 @@ module Badline
|
|
|
116
119
|
--drive-type 1541 a 1541 on the serial bus instead. There Escape is
|
|
117
120
|
ESC, Page Down RUN/STOP and F4 HELP.
|
|
118
121
|
|
|
122
|
+
The PET and the CBM take programs, disks, .d80 and .d82 among them,
|
|
123
|
+
and tapes, and a .d80, a .d82 or a PET tape starts one without
|
|
124
|
+
`pet`. Their keys type what the host's keys type, Alt with a key
|
|
125
|
+
presses the PET's SHIFT and the key its unshifted character is on,
|
|
126
|
+
and the pause menu's KEYBOARD page says where the rest are. --basic
|
|
127
|
+
and --editor fit the upgrades Commodore sold, as in --model 2001
|
|
128
|
+
--basic 2. BASIC 1 can't LOAD from a disk drive, though a disk still
|
|
129
|
+
starts its first program, and BASIC 2 can. Their --true-drive makes
|
|
130
|
+
device 8 an emulated 2031 on the IEEE-488 bus, which reads .d64 and
|
|
131
|
+
.g64 images. The 8032 and the 8096 have two 50 Hz editors:
|
|
132
|
+
4-80-b-50hz, the default, and 4-80-b-50hz-0283, another chip's,
|
|
133
|
+
with 64-cycle lines and 313 lines a frame. The 8296 runs its own,
|
|
134
|
+
4-80-b-50hz-8296, and takes the 8032's too. --characters din fits
|
|
135
|
+
it with the DIN character ROM, and --jumpers ram9,rama puts RAM at
|
|
136
|
+
$9000 and $A000, as xpet's -petram9 and -petramA do.
|
|
137
|
+
|
|
119
138
|
--headless plays a .sid tune on the host's audio device without the
|
|
120
139
|
window, and --audio-out renders it to 16-bit PCM instead. The
|
|
121
140
|
container follows the file's extension, .wav or .aiff. Played on a
|
|
@@ -136,8 +155,9 @@ module Badline
|
|
|
136
155
|
--at and --script run events once a number of frames has run, as
|
|
137
156
|
in --at 3500:key=space or --at 250,500:screenshot=shot%05d.bmp.
|
|
138
157
|
key=NAME presses a key (space, return, a, f1, run_stop, restore, and
|
|
139
|
-
on the C128 esc, help, keypad1 and the rest,
|
|
140
|
-
esc, help and f1 to f3) or a
|
|
158
|
+
on the C128 esc, help, keypad1 and the rest, on the Plus/4
|
|
159
|
+
esc, help and f1 to f3, and on the PET rvs, escape and kp1) or a
|
|
160
|
+
joystick's
|
|
141
161
|
direction or fire (joy1-up, joy2-fire) for #{Event::HOLD} frames, and
|
|
142
162
|
pushes the C128's caps_lock or forty_eighty, which lock. type=TEXT
|
|
143
163
|
types it, with \\n for RETURN. insert=FILE swaps in a disk, a list's
|
|
@@ -18,6 +18,10 @@ module Badline
|
|
|
18
18
|
# another name for the C16.
|
|
19
19
|
PLUS4_MODELS = %w[plus4 plus4ntsc c16 c16ntsc c116].freeze
|
|
20
20
|
|
|
21
|
+
# The PETs and CBMs, as PET::Model names them, the 8032 first, the
|
|
22
|
+
# default.
|
|
23
|
+
PET_MODELS = %w[8032 8096 8296 4032 3032 3032b 2001].freeze
|
|
24
|
+
|
|
21
25
|
# The true drives --drive-type names for the Plus/4.
|
|
22
26
|
PLUS4_DRIVE_TYPES = %w[1541 1551].freeze
|
|
23
27
|
|
|
@@ -43,20 +47,34 @@ module Badline
|
|
|
43
47
|
section: "Window options", needs: :window),
|
|
44
48
|
Option.new("--no-sound", "Don't play the SID", section: "Window options", needs: :window, build: :native),
|
|
45
49
|
Option.new("--true-drive", "Put a true 1541 on device 8 instead of the KERNAL traps, a 1571 on the C128, " \
|
|
46
|
-
"a 1551 on the Plus/4",
|
|
50
|
+
"a 1551 on the Plus/4, a 2031 on the PET",
|
|
47
51
|
section: "Window options", needs: :window),
|
|
48
52
|
Option.new("--drive-type MODEL", "The Plus/4's true drive: 1541 or 1551 (default: 1551); implies --true-drive",
|
|
49
53
|
section: "Window options", needs: :window),
|
|
50
54
|
Option.new("--reu SIZE", "Plug in an REU of SIZE K: 128, 256, 512 (a 1750) or up to 16384",
|
|
51
55
|
section: "Window options", needs: :window),
|
|
52
56
|
Option.new("--model NAME", "C64 to run: #{MODELS.join(', ')} (default: #{MODELS.first}); VIC-20: pal; " \
|
|
53
|
-
"C128: #{C128_MODELS.join(', ')}; Plus/4: #{PLUS4_MODELS.join(', ')}"
|
|
57
|
+
"C128: #{C128_MODELS.join(', ')}; Plus/4: #{PLUS4_MODELS.join(', ')}; " \
|
|
58
|
+
"PET: #{PET_MODELS.join(', ')}",
|
|
54
59
|
section: "Window options", needs: :window),
|
|
55
60
|
Option.new("--ntsc", "Run an NTSC C64, as --model ntsc does", section: "Window options", needs: :window),
|
|
56
61
|
Option.new("--c64", "Start the C128 in C64 mode, holding C= at power-on",
|
|
57
62
|
section: "Window options", needs: :window),
|
|
58
63
|
Option.new("--ram SIZE", "VIC-20 RAM expansion: #{RAM_NAMES.join(', ')} (default: unexpanded); " \
|
|
59
|
-
"Plus/4 RAM: 16k, 32k or 64k (default: the model's own)",
|
|
64
|
+
"Plus/4 RAM: 16k, 32k or 64k; PET RAM: 4k, 8k, 16k or 32k (default: the model's own)",
|
|
65
|
+
section: "Window options", needs: :window),
|
|
66
|
+
Option.new("--60hz", "Run a CRTC PET's 60 Hz editor instead of its 50 Hz one",
|
|
67
|
+
section: "Window options", needs: :window),
|
|
68
|
+
Option.new("--basic VERSION", "Fit a PET with BASIC 1, 2 or 4, as its board takes (default: its own)",
|
|
69
|
+
section: "Window options", needs: :window),
|
|
70
|
+
Option.new("--editor NAME", "Fit a PET with an editor ROM its board and BASIC take: " \
|
|
71
|
+
"#{PET::ROMSets::EDITORS.keys.join(', ')}",
|
|
72
|
+
section: "Window options", needs: :window),
|
|
73
|
+
Option.new("--characters NAME", "Fit an 8296 with a character ROM: ascii or din (default: ascii)",
|
|
74
|
+
section: "Window options", needs: :window),
|
|
75
|
+
Option.new("--jumpers LIST", "Fit an 8296's board with jumpers, comma-separated: ram9 and rama for RAM at " \
|
|
76
|
+
"$9000 and $A000, userport for the user port's PA0-PA2 to switch in " \
|
|
77
|
+
"the RAM under the ROMs",
|
|
60
78
|
section: "Window options", needs: :window),
|
|
61
79
|
Option.new("--c1lo FILE", "Put a .bin ROM image in the Plus/4's cartridge 1, low slot ($8000)",
|
|
62
80
|
section: "Window options", needs: :window),
|
|
@@ -100,7 +118,7 @@ module Badline
|
|
|
100
118
|
section: "Testing options", needs: :window),
|
|
101
119
|
Option.new("--save-snapshot FILE", "Save the machine as a .vsf snapshot after the last frame",
|
|
102
120
|
section: "Testing options", needs: :window),
|
|
103
|
-
Option.new("--vice", "Add VICE's modules to --save-snapshot's C64 snapshot, for x64sc",
|
|
121
|
+
Option.new("--vice", "Add VICE's modules to --save-snapshot's C64 or PET snapshot, for x64sc or xpet",
|
|
104
122
|
section: "Testing options", needs: :window),
|
|
105
123
|
Option.new("--at FRAME:EVENT", "Run EVENT once FRAME frames have run (see above)",
|
|
106
124
|
section: "Testing options", needs: :window),
|
|
@@ -39,6 +39,12 @@ module Badline
|
|
|
39
39
|
PLUS4_RAM[value]
|
|
40
40
|
end
|
|
41
41
|
|
|
42
|
+
def pet_ram(value)
|
|
43
|
+
raise Error, "invalid argument: --ram #{value}" unless PET_RAM.key?(value)
|
|
44
|
+
|
|
45
|
+
PET_RAM[value]
|
|
46
|
+
end
|
|
47
|
+
|
|
42
48
|
def plus4_ram_expansion(value)
|
|
43
49
|
raise Error, "invalid argument: --ram-expansion #{value}" unless PLUS4_RAM_EXPANSIONS.key?(value)
|
|
44
50
|
|
|
@@ -92,12 +98,10 @@ module Badline
|
|
|
92
98
|
end
|
|
93
99
|
|
|
94
100
|
def validate_family
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
elsif !@ram.nil?
|
|
98
|
-
raise Error, "--ram needs vic20 or plus4"
|
|
99
|
-
end
|
|
101
|
+
ram_option
|
|
102
|
+
validate_vic20 if @family == :vic20
|
|
100
103
|
@family == :plus4 ? validate_plus4 : refuse_plus4_options
|
|
104
|
+
@family == :pet ? validate_pet : refuse_pet_options
|
|
101
105
|
if @family == :c128
|
|
102
106
|
validate_c128
|
|
103
107
|
elsif @c64_mode
|
|
@@ -105,6 +109,19 @@ module Badline
|
|
|
105
109
|
end
|
|
106
110
|
end
|
|
107
111
|
|
|
112
|
+
# --ram names the VIC-20's RAM expansion, or the Plus/4's or the PET's
|
|
113
|
+
# RAM.
|
|
114
|
+
def ram_option
|
|
115
|
+
return if @ram_name.nil?
|
|
116
|
+
|
|
117
|
+
case @family
|
|
118
|
+
when :vic20 then @ram = ram_configuration(@ram_name)
|
|
119
|
+
when :plus4 then @ram_kb = plus4_ram(@ram_name)
|
|
120
|
+
when :pet then @ram_kb = pet_ram(@ram_name)
|
|
121
|
+
else raise Error, "--ram needs vic20, plus4 or pet"
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
108
125
|
# The C128 runs in the window, without an REU, and its NTSC models
|
|
109
126
|
# have names of their own.
|
|
110
127
|
def validate_c128
|
|
@@ -153,6 +170,68 @@ module Badline
|
|
|
153
170
|
raise Error, "--sid-card-address needs --sid-card" unless @sid_card_address.nil? || @sid_card
|
|
154
171
|
end
|
|
155
172
|
|
|
173
|
+
# Only the PET has a frame rate to pick and BASIC and editor ROMs to
|
|
174
|
+
# fit.
|
|
175
|
+
def refuse_pet_options
|
|
176
|
+
raise Error, "--60hz needs pet" unless @refresh_hz.nil?
|
|
177
|
+
raise Error, "--basic needs pet" unless @basic.nil?
|
|
178
|
+
raise Error, "--editor needs pet" unless @editor.nil?
|
|
179
|
+
raise Error, "--characters needs pet" unless @characters.nil?
|
|
180
|
+
raise Error, "--jumpers needs pet" unless @jumpers.empty?
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
# The PET runs in the window, without the C64's SID and REU, at the
|
|
184
|
+
# frame rate its editor sets.
|
|
185
|
+
def validate_pet
|
|
186
|
+
raise Error, "--ntsc needs the C64; a CRTC PET's 60 Hz editor is --60hz" if @models.include?("ntsc")
|
|
187
|
+
raise Error, "--sid needs the C64" unless @sid_model.nil?
|
|
188
|
+
raise Error, "--reu needs the C64" unless @reu.nil?
|
|
189
|
+
raise Error, "the PET needs the window" if headless?
|
|
190
|
+
|
|
191
|
+
return unless PET_MODELS.include?(model)
|
|
192
|
+
|
|
193
|
+
validate_pet_roms
|
|
194
|
+
validate_pet_board
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
# --characters names a character ROM the board takes, --jumpers the
|
|
198
|
+
# 8296 board's jumpers, and the 8296 has all of its RAM soldered on.
|
|
199
|
+
def validate_pet_board
|
|
200
|
+
unless @characters.nil? || PET::ROMSets.characters(model).include?(@characters)
|
|
201
|
+
raise Error, "the #{model} takes no character ROM #{@characters}"
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
unknown = @jumpers.find { |jumper| !PET::ROMSets.jumpers(model).include?(jumper) }
|
|
205
|
+
raise Error, "the #{model} has no jumper #{unknown}" unless unknown.nil?
|
|
206
|
+
raise Error, "the 8296 has 32k of RAM" if model == "8296" && ![nil, 32].include?(@ram_kb)
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
# --basic and --editor name ROMs the model's board takes
|
|
210
|
+
# (PET::ROMSets), and an editor named sets the frame rate.
|
|
211
|
+
def validate_pet_roms
|
|
212
|
+
raise Error, "invalid argument: --basic #{@basic}" unless @basic.nil? || PET::ROMSets::BASICS.key?(@basic)
|
|
213
|
+
raise Error, "invalid argument: --editor #{@editor}" unless @editor.nil? || PET::ROMSets::EDITORS.key?(@editor)
|
|
214
|
+
|
|
215
|
+
basic = @basic || PET::ROMSets.stock_basic(model)
|
|
216
|
+
fitting = PET::ROMSets.editors(model, basic)
|
|
217
|
+
raise Error, "the #{model} takes no BASIC #{basic}" if fitting.empty?
|
|
218
|
+
return if @editor.nil?
|
|
219
|
+
raise Error, "the #{model} with BASIC #{basic} takes no editor #{@editor}" unless fitting.include?(@editor)
|
|
220
|
+
|
|
221
|
+
editor_rate(fitting)
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
# The frame rate of the editor named, on a CRTC board, which has a 50
|
|
225
|
+
# Hz and a 60 Hz one.
|
|
226
|
+
def editor_rate(fitting)
|
|
227
|
+
return if fitting.size == 1
|
|
228
|
+
|
|
229
|
+
editor_hz = @editor == fitting.last ? 60 : 50
|
|
230
|
+
raise Error, "--60hz takes a 60 Hz editor, not #{@editor}" if @refresh_hz == 60 && editor_hz == 50
|
|
231
|
+
|
|
232
|
+
@refresh_hz = editor_hz
|
|
233
|
+
end
|
|
234
|
+
|
|
156
235
|
# `sid` takes directories as well as tunes.
|
|
157
236
|
def validate_tune(path)
|
|
158
237
|
return if !File.exist?(path) || File.extname(path).casecmp?(".sid")
|