badline 0.13.0 → 0.15.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 +72 -1
- data/CLAUDE.md +42 -12
- data/CONTRIBUTING.md +4 -3
- data/README.md +76 -13
- data/doc/media.md +2 -1
- data/doc/pinned-behaviour.md +174 -0
- data/doc/snapshot-format.md +1197 -0
- data/doc/snapshots.md +90 -40
- data/lib/badline/audio/bare_player.rb +46 -17
- data/lib/badline/audio/machine_player.rb +6 -10
- data/lib/badline/audio/renderer.rb +32 -21
- data/lib/badline/c128/banks.rb +7 -6
- data/lib/badline/c128/bus/keyed_state.rb +45 -0
- data/lib/badline/c128/bus.rb +36 -4
- data/lib/badline/c128/drives.rb +6 -25
- data/lib/badline/c128/keyed_modules.rb +117 -0
- data/lib/badline/c128/keyed_state.rb +91 -0
- data/lib/badline/c128/keys.rb +9 -0
- data/lib/badline/c128/mmu.rb +9 -8
- data/lib/badline/c128/mmu_pages.rb +27 -0
- data/lib/badline/c128/modes.rb +19 -9
- data/lib/badline/c128/z80_turns.rb +1 -1
- data/lib/badline/c128.rb +8 -8
- data/lib/badline/c64/attachments.rb +0 -44
- data/lib/badline/c64/banked_ram/plus256k.rb +18 -10
- data/lib/badline/c64/banked_ram/plus60k.rb +8 -6
- data/lib/badline/c64/banked_ram/unexpanded.rb +2 -2
- data/lib/badline/c64/bus/debug_register.rb +32 -0
- data/lib/badline/c64/bus/extra_sids.rb +80 -0
- data/lib/badline/c64/bus/keyed_state.rb +41 -0
- data/lib/badline/c64/bus.rb +3 -1
- data/lib/badline/c64/cartridge/action_replay.rb +6 -15
- data/lib/badline/c64/cartridge/comal80.rb +1 -0
- data/lib/badline/c64/cartridge/dinamic.rb +3 -1
- data/lib/badline/c64/cartridge/easy_flash.rb +0 -15
- data/lib/badline/c64/cartridge/epyx_fastload.rb +0 -8
- data/lib/badline/c64/cartridge/final_cartridge3.rb +1 -12
- data/lib/badline/c64/cartridge/flash.rb +3 -3
- data/lib/badline/c64/cartridge/fun_play.rb +2 -1
- data/lib/badline/c64/cartridge/g_mod2.rb +0 -12
- data/lib/badline/c64/cartridge/game_system.rb +2 -1
- data/lib/badline/c64/cartridge/geo_ram.rb +0 -16
- data/lib/badline/c64/cartridge/kcs_power.rb +0 -8
- data/lib/badline/c64/cartridge/keyed_state.rb +514 -0
- data/lib/badline/c64/cartridge/magic_desk.rb +2 -1
- data/lib/badline/c64/cartridge/ocean.rb +1 -0
- data/lib/badline/c64/cartridge/pagefox.rb +1 -10
- data/lib/badline/c64/cartridge/retro_replay.rb +0 -8
- data/lib/badline/c64/cartridge/rgcd.rb +2 -11
- data/lib/badline/c64/cartridge/super_games.rb +3 -12
- data/lib/badline/c64/cartridge/zaxxon.rb +1 -16
- data/lib/badline/c64/cartridge.rb +4 -30
- data/lib/badline/c64/kernal_traps.rb +28 -25
- data/lib/badline/c64/keyed_modules.rb +115 -0
- data/lib/badline/c64/keyed_state.rb +68 -0
- data/lib/badline/c64/reu/dma.rb +0 -4
- data/lib/badline/c64/reu/keyed_state.rb +140 -0
- data/lib/badline/c64/reu/ram.rb +0 -15
- data/lib/badline/c64/reu.rb +1 -3
- data/lib/badline/c64/true_drives.rb +56 -5
- data/lib/badline/c64.rb +7 -3
- data/lib/badline/chips/cia/interrupt_register.rb +0 -17
- data/lib/badline/chips/cia/keyed_parts.rb +213 -0
- data/lib/badline/chips/cia/serial.rb +0 -4
- data/lib/badline/chips/cia/time_of_day.rb +0 -22
- data/lib/badline/chips/cia/timer.rb +0 -20
- data/lib/badline/chips/cia.rb +2 -2
- data/lib/badline/chips/cpu/core.rb +18 -2
- data/lib/badline/chips/cpu/keyed_state.rb +177 -0
- data/lib/badline/chips/cpu/microcode.rb +16 -0
- data/lib/badline/chips/cpu/port.rb +20 -3
- data/lib/badline/chips/cpu.rb +5 -0
- data/lib/badline/chips/sid/decimator.rb +10 -8
- data/lib/badline/chips/sid/envelope.rb +1 -6
- data/lib/badline/chips/sid/filter.rb +0 -14
- data/lib/badline/chips/sid/keyed_parts.rb +286 -0
- data/lib/badline/chips/sid/voice.rb +0 -10
- data/lib/badline/chips/sid/waveform.rb +0 -2
- data/lib/badline/chips/sid.rb +1 -2
- data/lib/badline/chips/ted/beam.rb +118 -0
- data/lib/badline/chips/ted/fetches.rb +199 -0
- data/lib/badline/chips/ted/keyed_state.rb +156 -0
- data/lib/badline/chips/ted/modes.rb +134 -0
- data/lib/badline/chips/ted/palette.rb +43 -0
- data/lib/badline/chips/ted/picture.rb +182 -0
- data/lib/badline/chips/ted/sound/output.rb +86 -0
- data/lib/badline/chips/ted/sound.rb +284 -0
- data/lib/badline/chips/ted/timers.rb +66 -0
- data/lib/badline/chips/ted.rb +190 -0
- data/lib/badline/chips/tia.rb +61 -0
- data/lib/badline/chips/vdc/keyed_parts.rb +105 -0
- data/lib/badline/chips/vdc/painter.rb +0 -12
- data/lib/badline/chips/vdc/raster.rb +0 -16
- data/lib/badline/chips/vdc/window.rb +0 -9
- data/lib/badline/chips/vdc.rb +1 -2
- data/lib/badline/chips/via/control_lines.rb +0 -15
- data/lib/badline/chips/via/interrupt_register.rb +0 -9
- data/lib/badline/chips/via/keyed_parts.rb +163 -0
- data/lib/badline/chips/via/shift_register.rb +0 -14
- data/lib/badline/chips/via/timer1.rb +0 -13
- data/lib/badline/chips/via/timer2.rb +0 -18
- data/lib/badline/chips/via.rb +1 -2
- data/lib/badline/chips/vic/border_mask.rb +0 -10
- data/lib/badline/chips/vic/collisions.rb +0 -11
- data/lib/badline/chips/vic/color_patches.rb +0 -11
- data/lib/badline/chips/vic/display_state.rb +0 -19
- data/lib/badline/chips/vic/graphics_shifter.rb +0 -4
- data/lib/badline/chips/vic/keyed_parts.rb +228 -0
- data/lib/badline/chips/vic/keyed_sprites.rb +225 -0
- data/lib/badline/chips/vic/keyed_state.rb +102 -0
- data/lib/badline/chips/vic/register_log.rb +0 -18
- data/lib/badline/chips/vic/registers.rb +0 -9
- data/lib/badline/chips/vic/sequencer.rb +0 -2
- data/lib/badline/chips/vic/sprite/internal_bus.rb +0 -11
- data/lib/badline/chips/vic/sprite.rb +0 -2
- data/lib/badline/chips/vic/sprites.rb +0 -3
- data/lib/badline/chips/vic.rb +6 -2
- data/lib/badline/chips/vic_i/keyed_parts.rb +142 -0
- data/lib/badline/chips/vic_i/painter.rb +0 -4
- data/lib/badline/chips/vic_i/sound.rb +38 -46
- data/lib/badline/chips/vic_i.rb +2 -2
- data/lib/badline/chips/z80/core.rb +2 -2
- data/lib/badline/chips/z80/keyed_state.rb +73 -0
- data/lib/badline/chips.rb +10 -0
- data/lib/badline/core.rb +4 -21
- data/lib/badline/drives/datasette/keyed_state.rb +43 -0
- data/lib/badline/drives/datasette.rb +62 -12
- data/lib/badline/drives/engine/core.rb +3 -0
- data/lib/badline/drives/engine/serial_lines.rb +4 -7
- data/lib/badline/drives/model1541/bus.rb +0 -13
- data/lib/badline/drives/model1541/disk.rb +9 -8
- data/lib/badline/drives/model1541/keyed_state.rb +251 -0
- data/lib/badline/drives/model1541/mechanism.rb +0 -2
- data/lib/badline/drives/model1541.rb +1 -2
- data/lib/badline/drives/model1571/bus.rb +0 -13
- data/lib/badline/drives/model1571/keyed_state.rb +108 -0
- data/lib/badline/drives/model1571/wd1770.rb +0 -14
- data/lib/badline/drives/model1571.rb +1 -2
- data/lib/badline/drives/model1581/bus.rb +0 -13
- data/lib/badline/drives/model1581/disk.rb +0 -4
- data/lib/badline/drives/model1581/keyed_state.rb +280 -0
- data/lib/badline/drives/model1581/mechanism.rb +0 -21
- data/lib/badline/drives/model1581/slot.rb +0 -18
- data/lib/badline/drives/model1581/wd1772.rb +0 -2
- data/lib/badline/drives/model1581.rb +2 -2
- data/lib/badline/drives.rb +8 -0
- data/lib/badline/frontend/app.rb +3 -3
- data/lib/badline/frontend/boot.rb +4 -2
- data/lib/badline/frontend/controls.rb +25 -5
- data/lib/badline/frontend/drive_led.rb +44 -21
- data/lib/badline/frontend/keys.rb +14 -2
- data/lib/badline/frontend/media_slots.rb +11 -1
- data/lib/badline/frontend/menu/menu_dialogs.rb +4 -3
- data/lib/badline/frontend/menu/menu_media.rb +13 -3
- data/lib/badline/frontend/menu/menu_pages.rb +8 -8
- data/lib/badline/frontend/menu/menu_ports.rb +13 -1
- data/lib/badline/frontend/screen.rb +33 -12
- data/lib/badline/frontend/screens.rb +2 -1
- data/lib/badline/frontend/snapshots.rb +12 -1
- data/lib/badline/frontend/sound.rb +4 -4
- data/lib/badline/frontend/timeline.rb +43 -12
- data/lib/badline/input/matrix_typist.rb +98 -0
- data/lib/badline/input.rb +1 -0
- data/lib/badline/kernal_trap/chrout.rb +45 -0
- data/lib/badline/kernal_trap/dos/channel.rb +0 -27
- data/lib/badline/kernal_trap/dos/channels.rb +0 -25
- data/lib/badline/kernal_trap/dos/memory.rb +0 -9
- data/lib/badline/kernal_trap/dos/status.rb +0 -8
- data/lib/badline/kernal_trap/dos/write_file.rb +0 -18
- data/lib/badline/kernal_trap/dos.rb +0 -19
- data/lib/badline/kernal_trap/file_routine.rb +4 -4
- data/lib/badline/kernal_trap/keyed_state.rb +150 -0
- data/lib/badline/kernal_trap/layout.rb +75 -11
- data/lib/badline/kernal_trap/load.rb +14 -16
- data/lib/badline/kernal_trap/routine.rb +12 -0
- data/lib/badline/kernal_trap/save.rb +8 -18
- data/lib/badline/kernal_trap/serial.rb +1 -17
- data/lib/badline/kernal_trap.rb +2 -0
- data/lib/badline/machine/keyboard_buffer.rb +49 -0
- data/lib/badline/machine/settling.rb +29 -0
- data/lib/badline/machine.rb +29 -6
- data/lib/badline/media/boot_disk.rb +14 -2
- data/lib/badline/media/extensions.rb +10 -0
- data/lib/badline/media/plus4_media.rb +68 -0
- data/lib/badline/media/program.rb +20 -6
- data/lib/badline/media.rb +33 -12
- data/lib/badline/memory.rb +3 -6
- data/lib/badline/options/family.rb +6 -2
- data/lib/badline/options/help.rb +66 -16
- data/lib/badline/options/table.rb +17 -2
- data/lib/badline/options/validation.rb +26 -2
- data/lib/badline/options.rb +42 -9
- data/lib/badline/plus4/acia.rb +45 -0
- data/lib/badline/plus4/bus.rb +375 -0
- data/lib/badline/plus4/cassette_sense.rb +21 -0
- data/lib/badline/plus4/cpu.rb +10 -0
- data/lib/badline/plus4/cpu_port.rb +93 -0
- data/lib/badline/plus4/keyboard_port.rb +76 -0
- data/lib/badline/plus4/keyed_modules.rb +99 -0
- data/lib/badline/plus4/keyed_parts.rb +143 -0
- data/lib/badline/plus4/keyed_state.rb +41 -0
- data/lib/badline/plus4/model.rb +38 -0
- data/lib/badline/plus4/user_port.rb +31 -0
- data/lib/badline/plus4.rb +240 -0
- data/lib/badline/roms/README +38 -0
- data/lib/badline/roms/plus4/basic.rom +0 -0
- data/lib/badline/roms/plus4/function-high.rom +0 -0
- data/lib/badline/roms/plus4/function-low.rom +0 -0
- data/lib/badline/roms/plus4/kernal-ntsc.rom +0 -0
- data/lib/badline/roms/plus4/kernal-pal.rom +0 -0
- data/lib/badline/snapshot/c128_setup.rb +9 -20
- data/lib/badline/snapshot/c64_setup.rb +28 -24
- data/lib/badline/snapshot/container.rb +6 -0
- data/lib/badline/snapshot/field_doc/markdown.rb +113 -0
- data/lib/badline/snapshot/field_doc.rb +148 -0
- data/lib/badline/snapshot/image.rb +143 -28
- data/lib/badline/snapshot/keyed/cursor.rb +40 -0
- data/lib/badline/snapshot/keyed/modules.rb +66 -0
- data/lib/badline/snapshot/keyed/reader.rb +188 -0
- data/lib/badline/snapshot/keyed/recorder.rb +88 -0
- data/lib/badline/snapshot/keyed/report.rb +26 -0
- data/lib/badline/snapshot/keyed/writer.rb +55 -0
- data/lib/badline/snapshot/keyed.rb +64 -0
- data/lib/badline/snapshot/machine_state.rb +17 -64
- data/lib/badline/snapshot/plus4_setup.rb +39 -0
- data/lib/badline/snapshot/storage_setup.rb +29 -34
- data/lib/badline/snapshot/vic20_setup.rb +6 -15
- data/lib/badline/snapshot/vice/reu1764.rb +2 -2
- data/lib/badline/snapshot/vice.rb +7 -31
- data/lib/badline/snapshot.rb +48 -20
- data/lib/badline/storage/crt_file.rb +3 -1
- data/lib/badline/storage/g64_image.rb +9 -4
- data/lib/badline/storage/tap.rb +57 -20
- data/lib/badline/support/checkpoint.rb +3 -2
- data/lib/badline/support/plus4_checkpoint.rb +42 -0
- data/lib/badline/version.rb +1 -1
- data/lib/badline/vic20/attachments.rb +3 -0
- data/lib/badline/vic20/bus/keyed_state.rb +52 -0
- data/lib/badline/vic20/bus.rb +1 -2
- data/lib/badline/vic20/keyed_modules.rb +73 -0
- data/lib/badline/vic20/keyed_state.rb +45 -0
- data/lib/badline/vic20.rb +5 -5
- data/lib/badline.rb +1 -0
- metadata +75 -42
- data/lib/badline/c128/bus/saved_state.rb +0 -53
- data/lib/badline/c128/saved_state.rb +0 -117
- data/lib/badline/c64/bus/saved_state.rb +0 -68
- data/lib/badline/c64/cartridge/flash/saved_state.rb +0 -40
- data/lib/badline/c64/cartridge/retro_replay/saved_state.rb +0 -47
- data/lib/badline/c64/cartridge/saved_state.rb +0 -63
- data/lib/badline/c64/reu/dma/saved_state.rb +0 -52
- data/lib/badline/c64/reu/saved_state.rb +0 -55
- data/lib/badline/c64/saved_state.rb +0 -112
- data/lib/badline/chips/cia/saved_state.rb +0 -49
- data/lib/badline/chips/cia/serial/saved_state.rb +0 -45
- data/lib/badline/chips/cpu/saved_state.rb +0 -117
- data/lib/badline/chips/sid/envelope/saved_state.rb +0 -70
- data/lib/badline/chips/sid/filter/saved_state.rb +0 -42
- data/lib/badline/chips/sid/saved_state.rb +0 -52
- data/lib/badline/chips/sid/waveform/saved_state.rb +0 -68
- data/lib/badline/chips/vdc/saved_state.rb +0 -36
- data/lib/badline/chips/via/saved_state.rb +0 -44
- data/lib/badline/chips/vic/graphics_shifter/saved_state.rb +0 -40
- data/lib/badline/chips/vic/saved_state.rb +0 -82
- data/lib/badline/chips/vic/sequencer/saved_state.rb +0 -91
- data/lib/badline/chips/vic/sprite/saved_state.rb +0 -66
- data/lib/badline/chips/vic/sprites/saved_state.rb +0 -59
- data/lib/badline/chips/vic_i/painter/saved_state.rb +0 -40
- data/lib/badline/chips/vic_i/saved_state.rb +0 -55
- data/lib/badline/chips/z80/saved_state.rb +0 -71
- data/lib/badline/chrout_trap.rb +0 -43
- data/lib/badline/debug_register.rb +0 -28
- data/lib/badline/drives/datasette/saved_state.rb +0 -37
- data/lib/badline/drives/model1541/disk/saved_state.rb +0 -88
- data/lib/badline/drives/model1541/mechanism/saved_state.rb +0 -72
- data/lib/badline/drives/model1541/saved_state.rb +0 -44
- data/lib/badline/drives/model1571/saved_state.rb +0 -47
- data/lib/badline/drives/model1581/disk/saved_state.rb +0 -89
- data/lib/badline/drives/model1581/saved_state.rb +0 -42
- data/lib/badline/drives/model1581/wd1772/saved_state.rb +0 -68
- data/lib/badline/keyboard_buffer.rb +0 -33
- data/lib/badline/snapshot/setup.rb +0 -31
- data/lib/badline/snapshot/state.rb +0 -179
- data/lib/badline/vic20/bus/saved_state.rb +0 -35
- data/lib/badline/vic20/saved_state.rb +0 -110
data/lib/badline/machine.rb
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "badline/machine/clocking"
|
|
4
|
+
require "badline/machine/keyboard_buffer"
|
|
5
|
+
require "badline/machine/settling"
|
|
4
6
|
|
|
5
7
|
module Badline
|
|
6
8
|
# = Machine
|
|
7
9
|
#
|
|
8
10
|
# The machines badline builds by family, as the command line's
|
|
9
11
|
# subcommand names them: the C64, Badline::C64, the VIC-20, Vic20, and the
|
|
10
|
-
# C128 in C64 mode, C128.
|
|
12
|
+
# C128 in C64 mode, C128, and the 264 series, Plus4.
|
|
11
13
|
#
|
|
12
14
|
# A machine answers what the front end, Media, the runners and the
|
|
13
15
|
# snapshots call on it:
|
|
@@ -47,21 +49,31 @@ module Badline
|
|
|
47
49
|
# +release_display_key+, and +attach_drive1571+ and +drive1571+ for the
|
|
48
50
|
# C128D's 1571, its true drive.
|
|
49
51
|
#
|
|
52
|
+
# The Plus/4 answers the identity, clocking, lifecycle, video, sound and
|
|
53
|
+
# input parts, with nil for +control_ports+ and no RESTORE, and of the
|
|
54
|
+
# rest +ram+, +load_prg+, +type_text+, +mount+, +unmount+, +mounted?+,
|
|
55
|
+
# +datasette+, +attach_cartridge+, +attach_drive1541+, +drive1541+,
|
|
56
|
+
# +true_drive+, +plug_true_drive+, the 1581's, +install_debug_register+
|
|
57
|
+
# and +capture_output+, and besides +model_label+, +cartridge_slots+ and
|
|
58
|
+
# +detach_cartridges+ for its two cartridges.
|
|
59
|
+
#
|
|
50
60
|
# Each machine includes Machine::Clocking for the clocking part.
|
|
51
61
|
module Machine
|
|
52
62
|
# Media the machine named doesn't take.
|
|
53
63
|
class Unsupported < ArgumentError; end
|
|
54
64
|
|
|
55
|
-
# A new machine of `family`, :c64, :vic20 or :
|
|
56
|
-
# `model` names. A C64's model is one of Model::ALL, with its SID
|
|
65
|
+
# A new machine of `family`, :c64, :vic20, :c128 or :plus4, the model
|
|
66
|
+
# of it `model` names. A C64's model is one of Model::ALL, with its SID
|
|
57
67
|
# `sid_model` when not nil and an REU of `reu` K when not nil. A
|
|
58
68
|
# VIC-20's is "pal", with the RAM expansion `ram` names
|
|
59
69
|
# (Vic20::Bus::RAM_CONFIGURATIONS), unexpanded when nil. A C128's is
|
|
60
70
|
# one of C128::Model::ALL, with its SID `sid_model` when not nil, built
|
|
61
|
-
# for C64 mode.
|
|
71
|
+
# for C64 mode. A Plus/4's is one of Plus4::Model::ALL or c116, with
|
|
72
|
+
# the model's own RAM.
|
|
62
73
|
def self.build(family, model:, sid_model: nil, reu: nil, ram: nil)
|
|
63
74
|
return vic20(model, ram) if family == :vic20
|
|
64
75
|
return c128(model, sid_model, :c64) if family == :c128
|
|
76
|
+
return Plus4.new(model:) if family == :plus4
|
|
65
77
|
raise ArgumentError, "no machine family named #{family}" unless family == :c64
|
|
66
78
|
|
|
67
79
|
profile = Model.named(model)
|
|
@@ -77,10 +89,11 @@ module Badline
|
|
|
77
89
|
# only C64 mode runs: a .sid tune or a program that loads at the C64's
|
|
78
90
|
# BASIC start. A VIC-20 without RAM named gets the expansion the media
|
|
79
91
|
# needs (Media::Vic20Media.ram_for) and yields a line saying so. Raises
|
|
80
|
-
# Unsupported for media a VIC-20 doesn't take.
|
|
92
|
+
# Unsupported for media a VIC-20 or a Plus/4 doesn't take.
|
|
81
93
|
def self.for_options(options, media, &)
|
|
82
94
|
family = options.family
|
|
83
95
|
return vic20_for(media, options.model, options.ram, &) if family == :vic20
|
|
96
|
+
return plus4_for(media, options.model, options.ram_kb) if family == :plus4
|
|
84
97
|
|
|
85
98
|
sid_model = options.sid_model || Media.sid_model(media, otherwise: nil)
|
|
86
99
|
return build(:c64, model: options.model, sid_model:, reu: options.reu) unless family == :c128
|
|
@@ -114,12 +127,22 @@ module Badline
|
|
|
114
127
|
end
|
|
115
128
|
private_class_method :vic20_for
|
|
116
129
|
|
|
130
|
+
# A Plus/4 with `ram_kb` K of RAM (Plus4::Model::RAM_SIZES), or with
|
|
131
|
+
# the model's own when nil.
|
|
132
|
+
def self.plus4_for(media, model, ram_kb)
|
|
133
|
+
unsupported = !media.nil? && Media::Vic20Media::UNSUPPORTED.include?(File.extname(media).downcase)
|
|
134
|
+
raise Unsupported, "doesn't go in a Plus/4" if unsupported
|
|
135
|
+
|
|
136
|
+
Plus4.new(model:, ram: ram_kb)
|
|
137
|
+
end
|
|
138
|
+
private_class_method :plus4_for
|
|
139
|
+
|
|
117
140
|
def self.c64_media?(media)
|
|
118
141
|
return false if media.nil? || !File.file?(media)
|
|
119
142
|
|
|
120
143
|
extension = File.extname(media).downcase
|
|
121
144
|
return true if extension == ".sid"
|
|
122
|
-
return false unless
|
|
145
|
+
return false unless Media::Extensions::PROGRAMS.include?(extension)
|
|
123
146
|
|
|
124
147
|
[Media::BASIC_START, Media::BASIC_START - 1].include?(Storage::P00.load_address(media))
|
|
125
148
|
end
|
|
@@ -4,7 +4,8 @@ module Badline
|
|
|
4
4
|
module Media
|
|
5
5
|
# A C128 boot disk. At power-on the C128 KERNAL reads track 1, sector 0
|
|
6
6
|
# of the disk in device 8 and runs it when it starts with "CBM", as boot
|
|
7
|
-
# games have it
|
|
7
|
+
# games have it, from a sector image or from the raw GCR of a .g64 or
|
|
8
|
+
# .g71. The 1581's DOS first loads and runs a USR file named
|
|
8
9
|
# COPYRIGHT CBM 86 when the disk has one, which is how CP/M's 1581 disks
|
|
9
10
|
# hand the KERNAL their boot sector, so a .d81 with that file boots too.
|
|
10
11
|
#
|
|
@@ -15,7 +16,7 @@ module Badline
|
|
|
15
16
|
module BootDisk
|
|
16
17
|
SIGNATURE = "CBM".bytes.freeze
|
|
17
18
|
AUTO_BOOT = "copyright cbm 86"
|
|
18
|
-
TYPES = Extensions.of(%i[disk])
|
|
19
|
+
TYPES = Extensions.of(%i[disk gcr])
|
|
19
20
|
|
|
20
21
|
class << self
|
|
21
22
|
def takes?(computer, path)
|
|
@@ -37,6 +38,7 @@ module Badline
|
|
|
37
38
|
def boot?(path)
|
|
38
39
|
type = File.extname(path).downcase
|
|
39
40
|
return false unless TYPES.include?(type) && File.file?(path)
|
|
41
|
+
return gcr_signature(path) == SIGNATURE if Extensions.kind(path) == :gcr
|
|
40
42
|
|
|
41
43
|
image = Media.open_storage(path, { read_only: true })
|
|
42
44
|
block = image.read_block(1, 0)
|
|
@@ -44,6 +46,16 @@ module Badline
|
|
|
44
46
|
|
|
45
47
|
type == ".d81" && !image.read_file(AUTO_BOOT, type: :usr).nil?
|
|
46
48
|
end
|
|
49
|
+
|
|
50
|
+
private
|
|
51
|
+
|
|
52
|
+
# The first three bytes of track 1, sector 0 as the drive reads them
|
|
53
|
+
# off the GCR of a .g64 or .g71, or nil when the sector isn't there.
|
|
54
|
+
def gcr_signature(path)
|
|
55
|
+
track = Drives::Model1541::Disk.open(path, read_only: true).track(2)
|
|
56
|
+
data = track && Drives::Model1541::SectorReader.read(track.bytes, 1)[0]&.data
|
|
57
|
+
data && data[1, 3]
|
|
58
|
+
end
|
|
47
59
|
end
|
|
48
60
|
end
|
|
49
61
|
end
|
|
@@ -28,6 +28,16 @@ module Badline
|
|
|
28
28
|
|
|
29
29
|
# What goes in device 8 through the traps.
|
|
30
30
|
MOUNTABLE = of(%i[disk archive])
|
|
31
|
+
|
|
32
|
+
# What goes in device 8, a true drive's or the traps', and the lists
|
|
33
|
+
# of disks to put there.
|
|
34
|
+
DEVICE8 = of(%i[disk gcr archive disk_list])
|
|
35
|
+
|
|
36
|
+
# Programs, which load on their own.
|
|
37
|
+
PROGRAMS = %w[.prg .p00].freeze
|
|
38
|
+
|
|
39
|
+
# Every medium Media.attach takes by its extension.
|
|
40
|
+
ALL = (KINDS.keys + PROGRAMS).freeze
|
|
31
41
|
end
|
|
32
42
|
end
|
|
33
43
|
end
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
module Media
|
|
5
|
+
# What the 264 series does with media of its own (Media.attach). A
|
|
6
|
+
# cartridge is a PLUS4 CARTRIDGE .crt, or a raw ROM image (.bin) that
|
|
7
|
+
# goes in the slot it's given, or by its size in cartridge 1's.
|
|
8
|
+
module Plus4Media
|
|
9
|
+
# The platform byte of a C16 or Plus/4 tape (Storage::TAP#platform).
|
|
10
|
+
TAPE_PLATFORM = 2
|
|
11
|
+
|
|
12
|
+
# The extension of a raw cartridge ROM image.
|
|
13
|
+
ROM_IMAGE = ".bin"
|
|
14
|
+
|
|
15
|
+
# The cartridge slots by xplus4's names, each as the bank and address
|
|
16
|
+
# a .crt's CHIP packet gives it (Plus4::Bus#attach_cartridge).
|
|
17
|
+
SLOTS = { "c1lo" => [0, 0x8000], "c1hi" => [0, 0xc000], "c2lo" => [1, 0x8000], "c2hi" => [1, 0xc000] }.freeze
|
|
18
|
+
|
|
19
|
+
SLOT_SIZE = 0x4000
|
|
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 == TAPE_PLATFORM ? message : "#{message}, a tape for another machine than the Plus/4"
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Whether the file at `path` is a raw cartridge ROM image.
|
|
28
|
+
def rom_image?(path) = File.extname(path).downcase == ROM_IMAGE
|
|
29
|
+
|
|
30
|
+
# The ROM chips, as Storage::CRTFile::Chips, of the cartridge at
|
|
31
|
+
# `path`: a .crt's, or a raw image's (#rom_chips).
|
|
32
|
+
def cartridge_chips(path, slot: nil)
|
|
33
|
+
return rom_chips(File.binread(path).bytes, slot:) if rom_image?(path)
|
|
34
|
+
|
|
35
|
+
Storage::CRTFile.new(path, machine: :plus4).chips
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# A raw ROM image's chips: one in `slot`, one of SLOTS, when it's
|
|
39
|
+
# named. Otherwise up to 16K goes in cartridge 1's low slot, and
|
|
40
|
+
# up to 32K fills its high slot too.
|
|
41
|
+
def rom_chips(bytes, slot: nil)
|
|
42
|
+
return [slot_chip(bytes, slot)] if slot
|
|
43
|
+
|
|
44
|
+
size = bytes.length
|
|
45
|
+
unless size.between?(1, 2 * SLOT_SIZE)
|
|
46
|
+
raise Machine::Unsupported, "a #{size}-byte image is no Plus/4 cartridge"
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
chips = [chip(0, 0x8000, bytes[0, SLOT_SIZE])]
|
|
50
|
+
chips << chip(0, 0xc000, bytes[SLOT_SIZE..]) if size > SLOT_SIZE
|
|
51
|
+
chips
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
private
|
|
55
|
+
|
|
56
|
+
def slot_chip(bytes, slot)
|
|
57
|
+
bank, address = SLOTS.fetch(slot) { raise ArgumentError, "no Plus/4 cartridge slot named #{slot}" }
|
|
58
|
+
size = bytes.length
|
|
59
|
+
raise Machine::Unsupported, "a #{size}-byte image doesn't fit a 16K slot" unless size.between?(1, SLOT_SIZE)
|
|
60
|
+
|
|
61
|
+
chip(bank, address, bytes)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def chip(bank, address, data) = Storage::CRTFile::Chip.new(chip_type: 0, bank:, address:, data:)
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
@@ -2,26 +2,40 @@
|
|
|
2
2
|
|
|
3
3
|
module Badline
|
|
4
4
|
module Media
|
|
5
|
-
# A program loading into a C64 or a
|
|
6
|
-
# (Media.attach). The VIC-20 has its own (Vic20Basic).
|
|
5
|
+
# A program loading into a C64, a C128 or a 264 series machine once it
|
|
6
|
+
# has booted (Media.attach). The VIC-20 has its own (Vic20Basic).
|
|
7
7
|
module Program
|
|
8
|
+
# Where BASIC 3.5 starts on the 264 series.
|
|
9
|
+
PLUS4_BASIC_START = 0x1001
|
|
10
|
+
|
|
8
11
|
class << self
|
|
9
12
|
# Loads the program where it says, and RUNs one that loads at the
|
|
10
13
|
# start of BASIC.
|
|
11
14
|
def load(computer, data, autostart:)
|
|
12
15
|
load_addr = computer.load_prg(data)
|
|
16
|
+
start, text_end, load_end = basic(computer)
|
|
13
17
|
# Run only makes sense for programs at BASIC start, or a byte ahead
|
|
14
|
-
# of it, where BASIC keeps the zero before its first line.
|
|
15
|
-
# starts at $1C01 and keeps the end of its text at $1210.
|
|
16
|
-
start, text_end = computer.family == :c128 && computer.mode == :c128 ? [0x1c01, 0x1210] : [BASIC_START, 0x2d]
|
|
18
|
+
# of it, where BASIC keeps the zero before its first line.
|
|
17
19
|
return unless autostart && (load_addr == start || load_addr == start - 1)
|
|
18
20
|
|
|
19
21
|
# The end of the program, where BASIC's variables start, and where
|
|
20
22
|
# the KERNAL's LOAD leaves its end address.
|
|
21
23
|
end_addr = load_addr + data.length - 2
|
|
22
|
-
[text_end,
|
|
24
|
+
[text_end, load_end].each { |pointer| computer.ram.write(pointer, [end_addr & 0xff, end_addr >> 8]) }
|
|
23
25
|
computer.type_text("run\r")
|
|
24
26
|
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
# Where BASIC starts, the pointer to the end of its text and the
|
|
31
|
+
# KERNAL's end address (EAL). BASIC 7.0 starts at $1C01 and keeps
|
|
32
|
+
# the end of its text at $1210, and the 264 KERNAL keeps EAL at $9D.
|
|
33
|
+
def basic(computer)
|
|
34
|
+
return [PLUS4_BASIC_START, 0x2d, KernalTrap::PLUS4_ZERO_PAGE.end_address] if computer.family == :plus4
|
|
35
|
+
return [0x1c01, 0x1210, 0xae] if computer.family == :c128 && computer.mode == :c128
|
|
36
|
+
|
|
37
|
+
[BASIC_START, 0x2d, 0xae]
|
|
38
|
+
end
|
|
25
39
|
end
|
|
26
40
|
end
|
|
27
41
|
end
|
data/lib/badline/media.rb
CHANGED
|
@@ -10,6 +10,7 @@ require "badline/media/disk_list"
|
|
|
10
10
|
require "badline/media/vic20_basic"
|
|
11
11
|
require "badline/media/vic20_cartridge"
|
|
12
12
|
require "badline/media/vic20_media"
|
|
13
|
+
require "badline/media/plus4_media"
|
|
13
14
|
require "badline/media/tune"
|
|
14
15
|
require "badline/media/program"
|
|
15
16
|
|
|
@@ -44,13 +45,14 @@ module Badline
|
|
|
44
45
|
# An .m3u or .vfl list of disks attaches the first disk it lists
|
|
45
46
|
# (DiskList). Every machine takes each kind of medium the same way,
|
|
46
47
|
# except that the VIC-20 (Vic20Media) has cartridges, programs and a
|
|
47
|
-
# disk autostart of its own, and takes no .sid tunes
|
|
48
|
-
#
|
|
48
|
+
# disk autostart of its own, and takes no .sid tunes, and the 264
|
|
49
|
+
# series loads programs at the start of its BASIC (Program), takes
|
|
50
|
+
# .crt cartridges and raw .bin ROM images in its two cartridges'
|
|
51
|
+
# slots (Plus4Media), and no .sid tunes either. The
|
|
52
|
+
# C128 in C64 mode takes a C64's.
|
|
49
53
|
def attach(computer, path, autostart: true, subtune: nil, **)
|
|
50
54
|
path = DiskList.disk(path)
|
|
51
|
-
|
|
52
|
-
raise ArgumentError, "#{path} doesn't go in a VIC-20"
|
|
53
|
-
end
|
|
55
|
+
refuse_unsupported(computer, path)
|
|
54
56
|
|
|
55
57
|
BootDisk.attach(computer, path, **) || attach_kind(computer, path, autostart:, subtune:, **)
|
|
56
58
|
end
|
|
@@ -89,10 +91,12 @@ module Badline
|
|
|
89
91
|
# What the medium at `path` is to `computer`, which attach goes by:
|
|
90
92
|
# :true_drive for a disk that goes in a true drive (TrueDrive),
|
|
91
93
|
# :directory, :storage for a disk image or a .t64 that the traps
|
|
92
|
-
# mount,
|
|
94
|
+
# mount, :cartridge for a raw ROM image on the 264 series
|
|
95
|
+
# (Plus4Media), and otherwise its kind by its extension (Extensions).
|
|
93
96
|
def kind(computer, path)
|
|
94
97
|
return :true_drive if TrueDrive.takes?(computer, path)
|
|
95
98
|
return :directory if File.directory?(path)
|
|
99
|
+
return :cartridge if computer.family == :plus4 && Plus4Media.rom_image?(path)
|
|
96
100
|
|
|
97
101
|
kind = Extensions.kind(path)
|
|
98
102
|
%i[disk archive].include?(kind) ? :storage : kind
|
|
@@ -116,6 +120,16 @@ module Badline
|
|
|
116
120
|
|
|
117
121
|
private
|
|
118
122
|
|
|
123
|
+
# The VIC-20 and the 264 series have no SID to play a .sid tune on.
|
|
124
|
+
def refuse_unsupported(computer, path)
|
|
125
|
+
return unless Vic20Media::UNSUPPORTED.include?(File.extname(path).downcase)
|
|
126
|
+
|
|
127
|
+
case computer.family
|
|
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"
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
119
133
|
def attach_kind(computer, path, autostart:, subtune:, **options)
|
|
120
134
|
disk = options.fetch(:disk, {})
|
|
121
135
|
case kind(computer, path)
|
|
@@ -151,25 +165,32 @@ module Badline
|
|
|
151
165
|
computer.family == :vic20 ? Vic20Media.disk_autostart(yield) : AUTOSTART
|
|
152
166
|
end
|
|
153
167
|
|
|
154
|
-
# A VIC-20 cartridge is its ROM chips.
|
|
168
|
+
# A VIC-20 or 264 series cartridge is its ROM chips. On the 264, the
|
|
169
|
+
# options' `slot` names the slot a raw ROM image goes in
|
|
170
|
+
# (Plus4Media.cartridge_chips).
|
|
155
171
|
def attach_cartridge(computer, path, options)
|
|
156
|
-
|
|
157
|
-
|
|
172
|
+
case computer.family
|
|
173
|
+
when :vic20 then computer.attach_cartridge(Storage::CRTFile.new(path, machine: :vic20).chips)
|
|
174
|
+
when :plus4 then computer.attach_cartridge(Plus4Media.cartridge_chips(path, slot: options.fetch(:slot, nil)))
|
|
158
175
|
else
|
|
159
176
|
computer.attach_cartridge(C64::Cartridge.from_file(path, **options))
|
|
160
177
|
end
|
|
161
178
|
"Attached cartridge #{path}"
|
|
162
179
|
end
|
|
163
180
|
|
|
164
|
-
# A VIC-20 says when the tape was made for another machine
|
|
165
|
-
# (Vic20Media.tape_message).
|
|
181
|
+
# A VIC-20 or a 264 says when the tape was made for another machine
|
|
182
|
+
# (Vic20Media.tape_message, Plus4Media.tape_message).
|
|
166
183
|
def attach_tape(computer, path, autostart:)
|
|
167
184
|
tape = Storage::TAP.new(path)
|
|
168
185
|
computer.datasette.insert(tape)
|
|
169
186
|
computer.datasette.play!
|
|
170
187
|
computer.type_text(TAPE_AUTOSTART) if autostart
|
|
171
188
|
message = "Inserted #{path} in the datasette"
|
|
172
|
-
computer.family
|
|
189
|
+
case computer.family
|
|
190
|
+
when :vic20 then Vic20Media.tape_message(tape, message)
|
|
191
|
+
when :plus4 then Plus4Media.tape_message(tape, message)
|
|
192
|
+
else message
|
|
193
|
+
end
|
|
173
194
|
end
|
|
174
195
|
|
|
175
196
|
# A VIC-20 has programs of its own (Vic20Media.attach_program).
|
data/lib/badline/memory.rb
CHANGED
|
@@ -29,13 +29,10 @@ module Badline
|
|
|
29
29
|
Array(bytes).each_with_index { |b, i| poke(addr + i, b) }
|
|
30
30
|
end
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
end
|
|
32
|
+
# Every byte as one record of a keyed snapshot module, under `key`.
|
|
33
|
+
def save_fields(out, key) = out.bytes(key, @storage)
|
|
35
34
|
|
|
36
|
-
def
|
|
37
|
-
input.blob_into(@storage)
|
|
38
|
-
end
|
|
35
|
+
def load_fields(input, key) = input.bytes_into(key, @storage)
|
|
39
36
|
|
|
40
37
|
# Refills the whole memory as if newly built with `initial`
|
|
41
38
|
def clear!(initial = [])
|
|
@@ -12,13 +12,14 @@ module Badline
|
|
|
12
12
|
|
|
13
13
|
private
|
|
14
14
|
|
|
15
|
-
# Takes `sid`, `c64`, `vic20` or `
|
|
15
|
+
# Takes `sid`, `c64`, `vic20`, `c128` or `plus4` when given first.
|
|
16
16
|
def subcommand(args)
|
|
17
17
|
case args.first
|
|
18
18
|
when "sid" then @sid_command = true
|
|
19
19
|
when "c64" then @family = :c64
|
|
20
20
|
when "vic20" then @family = :vic20
|
|
21
21
|
when "c128" then @family = :c128
|
|
22
|
+
when "plus4" then @family = :plus4
|
|
22
23
|
else return
|
|
23
24
|
end
|
|
24
25
|
@family_named = !@sid_command
|
|
@@ -29,6 +30,7 @@ module Badline
|
|
|
29
30
|
case @family
|
|
30
31
|
when :vic20 then VIC20_MODELS
|
|
31
32
|
when :c128 then C128_MODELS
|
|
33
|
+
when :plus4 then PLUS4_MODELS
|
|
32
34
|
else MODELS
|
|
33
35
|
end
|
|
34
36
|
end
|
|
@@ -37,7 +39,9 @@ module Badline
|
|
|
37
39
|
# the C128's.
|
|
38
40
|
def pick_family
|
|
39
41
|
return if @family_named || @sid_command || @media_path.nil?
|
|
40
|
-
|
|
42
|
+
|
|
43
|
+
program = Media::Extensions::PROGRAMS.include?(File.extname(@media_path).downcase)
|
|
44
|
+
return unless program && File.file?(@media_path)
|
|
41
45
|
|
|
42
46
|
@family = :c128 if Storage::P00.load_address(@media_path) == C128_BASIC_START
|
|
43
47
|
end
|
data/lib/badline/options/help.rb
CHANGED
|
@@ -16,6 +16,16 @@ module Badline
|
|
|
16
16
|
filter cycle by cycle, which is exact but takes about twice as long.
|
|
17
17
|
TEXT
|
|
18
18
|
|
|
19
|
+
# The article for +name+: "an" where it starts with a vowel sound. A
|
|
20
|
+
# name of letters with a vowel among them is read as a word, and any
|
|
21
|
+
# other letter by letter, so .sid and .tap take "a", and .m3u and .lnx
|
|
22
|
+
# "an".
|
|
23
|
+
def self.article(name)
|
|
24
|
+
word = name.delete_prefix(".").downcase
|
|
25
|
+
vowels = word.match?(/\A[a-z]*[aeiou][a-z]*\z/) ? "aeiou" : "aefhilmnorsx8"
|
|
26
|
+
vowels.include?(word[0]) ? "an" : "a"
|
|
27
|
+
end
|
|
28
|
+
|
|
19
29
|
# What --help prints: the usage, then the options this build takes,
|
|
20
30
|
# section by section, laid out as OptionParser did.
|
|
21
31
|
def help
|
|
@@ -35,11 +45,45 @@ module Badline
|
|
|
35
45
|
|
|
36
46
|
def describe(option) = @native && option.name == "--sound" ? "#{option.text} (default)" : option.text
|
|
37
47
|
|
|
48
|
+
# The media the window opens, by their extensions (Media::Extensions),
|
|
49
|
+
# wrapped as the banner is.
|
|
50
|
+
def media_kinds
|
|
51
|
+
text = "Media can be #{listed(Media::Extensions::PROGRAMS)} program, #{listed(%i[disk])} disk image, " \
|
|
52
|
+
"#{listed(%i[gcr])} disk image for a true drive, #{listed(%i[disk_list])} list of disk images, " \
|
|
53
|
+
"#{listed(%i[archive])} tape archive, #{listed(%i[tape])} tape, #{listed(%i[cartridge])} " \
|
|
54
|
+
"cartridge, #{listed(%i[tune])} tune, #{listed(%w[.vsf])} snapshot, or a directory to mount as device 8."
|
|
55
|
+
wrap(text)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# The extensions of the kinds, or the extensions themselves, after the
|
|
59
|
+
# article the first takes.
|
|
60
|
+
def listed(kinds)
|
|
61
|
+
extensions = kinds.first.is_a?(Symbol) ? Media::Extensions.of(kinds) : kinds
|
|
62
|
+
"#{Options.article(extensions.first)} #{extensions.join('/')}"
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# The words of `text` in lines of at most 70 characters.
|
|
66
|
+
def wrap(text)
|
|
67
|
+
lines = [""]
|
|
68
|
+
text.split.each do |word|
|
|
69
|
+
line = lines.last
|
|
70
|
+
if line.empty?
|
|
71
|
+
lines[-1] = word
|
|
72
|
+
elsif line.size + word.size < 70
|
|
73
|
+
lines[-1] = "#{line} #{word}"
|
|
74
|
+
else
|
|
75
|
+
lines << word
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
lines.join("\n")
|
|
79
|
+
end
|
|
80
|
+
|
|
38
81
|
def banner
|
|
39
82
|
<<~BANNER
|
|
40
83
|
Usage: #{@program} [c64] [options] [media]
|
|
41
84
|
#{@program} vic20 [options] [media]
|
|
42
85
|
#{@program} c128 [options] [media]
|
|
86
|
+
#{@program} plus4 [options] [media]
|
|
43
87
|
#{@program} --headless [options] tune.sid
|
|
44
88
|
#{@program} [options] tune.sid --audio-out FILE
|
|
45
89
|
#{@program} sid [options] FILE|DIR...
|
|
@@ -48,13 +92,11 @@ module Badline
|
|
|
48
92
|
c64 Run a C64, the one --model names (the default)
|
|
49
93
|
vic20 Run a PAL VIC-20, with the RAM --ram names
|
|
50
94
|
c128 Run a C128 in C128 mode, the one --model names
|
|
95
|
+
plus4 Run a Plus/4 or a C16, the one --model names
|
|
51
96
|
sid Play .sid tunes and directories of them
|
|
52
97
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
images, a .t64 tape archive, a .tap tape, a .crt cartridge, a .sid
|
|
56
|
-
tune, a .vsf snapshot, or a directory to mount as device 8. It
|
|
57
|
-
opens in the emulator window. There F11 quicksaves the machine to
|
|
98
|
+
#{media_kinds}
|
|
99
|
+
It opens in the emulator window. There F11 quicksaves the machine to
|
|
58
100
|
the next of five slots in badline's data folder, replacing the
|
|
59
101
|
oldest, and F12 restores the newest quicksave or named save. The
|
|
60
102
|
window also autosaves every two minutes of play, which only the
|
|
@@ -66,6 +108,12 @@ module Badline
|
|
|
66
108
|
tune opens in the SID player, as `sid` plays it, unless an option
|
|
67
109
|
of the emulator's window asks for the machine.
|
|
68
110
|
|
|
111
|
+
The Plus/4 and the C16 take programs, disks, tapes, and cartridges as
|
|
112
|
+
.crt files or raw .bin ROM images. A .bin goes in cartridge 1, its
|
|
113
|
+
low slot for up to 16K and both slots for 32K, and --c1lo, --c1hi,
|
|
114
|
+
--c2lo and --c2hi put one in the slot they name. There Escape is ESC,
|
|
115
|
+
Page Down RUN/STOP and F4 HELP.
|
|
116
|
+
|
|
69
117
|
--headless plays a .sid tune on the host's audio device without the
|
|
70
118
|
window, and --audio-out renders it to 16-bit PCM instead. The
|
|
71
119
|
container follows the file's extension, .wav or .aiff. Played on a
|
|
@@ -85,17 +133,19 @@ module Badline
|
|
|
85
133
|
|
|
86
134
|
--at and --script run events once a number of frames has run, as
|
|
87
135
|
in --at 3500:key=space or --at 250,500:screenshot=shot%05d.bmp.
|
|
88
|
-
key=NAME presses a
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
136
|
+
key=NAME presses a key (space, return, a, f1, run_stop, restore, and
|
|
137
|
+
on the C128 esc, help, keypad1 and the rest, and on the Plus/4
|
|
138
|
+
esc, help and f1 to f3) or a joystick's
|
|
139
|
+
direction or fire (joy1-up, joy2-fire) for #{Event::HOLD} frames, and
|
|
140
|
+
pushes the C128's caps_lock or forty_eighty, which lock. type=TEXT
|
|
141
|
+
types it, with \\n for RETURN. insert=FILE swaps in a disk, a list's
|
|
142
|
+
first disk, a tape or a cartridge, and eject=disk, tape or
|
|
143
|
+
cartridge takes one out. screenshot=FILE saves the frame as a .bmp,
|
|
144
|
+
with the frame's number in place of %d. menu opens the pause menu,
|
|
145
|
+
and menu=PAGE opens it on snapshots, drive, datasette, expansion,
|
|
146
|
+
ports, sound or power. Its frames still count, and resume closes
|
|
147
|
+
it. display=vdc or display=vic shows the C128's 80 or 40 column
|
|
148
|
+
screen, as F8 switches them. reset, freeze and quit take no argument.
|
|
99
149
|
BANNER
|
|
100
150
|
end
|
|
101
151
|
|
|
@@ -14,6 +14,10 @@ module Badline
|
|
|
14
14
|
# The C128's models, as C128::Model names them.
|
|
15
15
|
C128_MODELS = %w[c128 c128ntsc c128dcr c128dcrntsc].freeze
|
|
16
16
|
|
|
17
|
+
# The 264 series' models, as Plus4::Model names them, with the C116 as
|
|
18
|
+
# another name for the C16.
|
|
19
|
+
PLUS4_MODELS = %w[plus4 plus4ntsc c16 c16ntsc c116].freeze
|
|
20
|
+
|
|
17
21
|
# The VIC-20 RAM expansions --ram takes, in the order the help lists
|
|
18
22
|
# them.
|
|
19
23
|
RAM_NAMES = %w[unexpanded 3k 8k 16k 24k 32k all].freeze
|
|
@@ -40,12 +44,21 @@ module Badline
|
|
|
40
44
|
Option.new("--reu SIZE", "Plug in an REU of SIZE K: 128, 256, 512 (a 1750) or up to 16384",
|
|
41
45
|
section: "Window options", needs: :window),
|
|
42
46
|
Option.new("--model NAME", "C64 to run: #{MODELS.join(', ')} (default: #{MODELS.first}); VIC-20: pal; " \
|
|
43
|
-
"C128: #{C128_MODELS.join(', ')}",
|
|
47
|
+
"C128: #{C128_MODELS.join(', ')}; Plus/4: #{PLUS4_MODELS.join(', ')}",
|
|
44
48
|
section: "Window options", needs: :window),
|
|
45
49
|
Option.new("--ntsc", "Run an NTSC C64, as --model ntsc does", section: "Window options", needs: :window),
|
|
46
50
|
Option.new("--c64", "Start the C128 in C64 mode, holding C= at power-on",
|
|
47
51
|
section: "Window options", needs: :window),
|
|
48
|
-
Option.new("--ram SIZE", "VIC-20 RAM expansion: #{RAM_NAMES.join(', ')} (default: unexpanded)"
|
|
52
|
+
Option.new("--ram SIZE", "VIC-20 RAM expansion: #{RAM_NAMES.join(', ')} (default: unexpanded); " \
|
|
53
|
+
"Plus/4 RAM: 16k, 32k or 64k (default: the model's own)",
|
|
54
|
+
section: "Window options", needs: :window),
|
|
55
|
+
Option.new("--c1lo FILE", "Put a .bin ROM image in the Plus/4's cartridge 1, low slot ($8000)",
|
|
56
|
+
section: "Window options", needs: :window),
|
|
57
|
+
Option.new("--c1hi FILE", "Put a .bin ROM image in cartridge 1's high slot ($C000)",
|
|
58
|
+
section: "Window options", needs: :window),
|
|
59
|
+
Option.new("--c2lo FILE", "Put a .bin ROM image in cartridge 2's low slot ($8000)",
|
|
60
|
+
section: "Window options", needs: :window),
|
|
61
|
+
Option.new("--c2hi FILE", "Put a .bin ROM image in cartridge 2's high slot ($C000)",
|
|
49
62
|
section: "Window options", needs: :window),
|
|
50
63
|
Option.new("--no-vsync", "Pace frames by the timer or the sound instead of the display",
|
|
51
64
|
section: "Window options", needs: :window),
|
|
@@ -75,6 +88,8 @@ module Badline
|
|
|
75
88
|
section: "Testing options", needs: :window),
|
|
76
89
|
Option.new("--save-snapshot FILE", "Save the machine as a .vsf snapshot after the last frame",
|
|
77
90
|
section: "Testing options", needs: :window),
|
|
91
|
+
Option.new("--vice", "Add VICE's modules to --save-snapshot's C64 snapshot, for x64sc",
|
|
92
|
+
section: "Testing options", needs: :window),
|
|
78
93
|
Option.new("--at FRAME:EVENT", "Run EVENT once FRAME frames have run (see above)",
|
|
79
94
|
section: "Testing options", needs: :window),
|
|
80
95
|
Option.new("--script FILE", "Run the events in FILE, one FRAME:EVENT a line",
|
|
@@ -21,6 +21,12 @@ module Badline
|
|
|
21
21
|
RAM_CONFIGURATIONS[value]
|
|
22
22
|
end
|
|
23
23
|
|
|
24
|
+
def plus4_ram(value)
|
|
25
|
+
raise Error, "invalid argument: --ram #{value}" unless PLUS4_RAM.key?(value)
|
|
26
|
+
|
|
27
|
+
PLUS4_RAM[value]
|
|
28
|
+
end
|
|
29
|
+
|
|
24
30
|
def reu_size(value)
|
|
25
31
|
raise Error, "invalid argument: --reu #{value}" unless REU_SIZES.include?(value)
|
|
26
32
|
|
|
@@ -62,7 +68,12 @@ module Badline
|
|
|
62
68
|
if @family == :vic20
|
|
63
69
|
validate_vic20
|
|
64
70
|
elsif !@ram.nil?
|
|
65
|
-
raise Error, "--ram needs vic20"
|
|
71
|
+
raise Error, "--ram needs vic20 or plus4"
|
|
72
|
+
end
|
|
73
|
+
if @family == :plus4
|
|
74
|
+
validate_plus4
|
|
75
|
+
elsif !@cartridges.empty?
|
|
76
|
+
raise Error, "--#{@cartridges.keys.first} needs plus4"
|
|
66
77
|
end
|
|
67
78
|
if @family == :c128
|
|
68
79
|
validate_c128
|
|
@@ -89,6 +100,19 @@ module Badline
|
|
|
89
100
|
raise Error, "the VIC-20 needs the window" if headless?
|
|
90
101
|
end
|
|
91
102
|
|
|
103
|
+
# The Plus/4 runs in the window, without the C64's SID and REU, its
|
|
104
|
+
# NTSC models have names of their own, and its cartridge slots take
|
|
105
|
+
# raw ROM images.
|
|
106
|
+
def validate_plus4
|
|
107
|
+
raise Error, "--ntsc needs the C64; the NTSC Plus/4 is --model plus4ntsc" if @models.include?("ntsc")
|
|
108
|
+
raise Error, "--sid needs the C64" unless @sid_model.nil?
|
|
109
|
+
raise Error, "--reu needs the C64" unless @reu.nil?
|
|
110
|
+
raise Error, "the Plus/4 needs the window" if headless?
|
|
111
|
+
|
|
112
|
+
image = @cartridges.values.find { |path| !File.extname(path).casecmp?(".bin") }
|
|
113
|
+
raise Error, "not a .bin ROM image: #{image}" unless image.nil?
|
|
114
|
+
end
|
|
115
|
+
|
|
92
116
|
# `sid` takes directories as well as tunes.
|
|
93
117
|
def validate_tune(path)
|
|
94
118
|
return if !File.exist?(path) || File.extname(path).casecmp?(".sid")
|
|
@@ -118,7 +142,7 @@ module Badline
|
|
|
118
142
|
end
|
|
119
143
|
|
|
120
144
|
def validate_paths
|
|
121
|
-
missing = tune_paths.find { |path| !File.exist?(path) }
|
|
145
|
+
missing = (tune_paths + @cartridges.values).find { |path| !File.exist?(path) }
|
|
122
146
|
raise Error, "no such file or directory: #{missing}" unless missing.nil?
|
|
123
147
|
raise Error, "no such file or directory: #{@songlengths}" unless @songlengths.nil? || File.exist?(@songlengths)
|
|
124
148
|
end
|