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
|
@@ -12,16 +12,28 @@ module Badline
|
|
|
12
12
|
# With either Alt down, a key that types a character presses the PET
|
|
13
13
|
# key its unshifted character is on with the PET's SHIFT, so the
|
|
14
14
|
# graphics on the graphics keyboard's symbol keys can be typed.
|
|
15
|
+
#
|
|
16
|
+
# While the SuperPET shows its APL characters the keys go by position
|
|
17
|
+
# instead (#positional=): a key that types a character presses the PET
|
|
18
|
+
# key its unshifted character is on, and the PET's SHIFT follows the
|
|
19
|
+
# host's, so a key types the APL symbol on the SuperPET's keycap in the
|
|
20
|
+
# same place, shifted or not.
|
|
21
|
+
#
|
|
22
|
+
# The CBM-II's keyboard goes the same way (Keys.cbm2_key), and there
|
|
23
|
+
# Alt with a key presses one of the keys the host has none for
|
|
24
|
+
# (Keys.cbm2_alt_key).
|
|
15
25
|
class PETKeyboard
|
|
16
26
|
LSHIFT = 225
|
|
17
27
|
RSHIFT = 229
|
|
18
28
|
LALT = 226
|
|
19
29
|
RALT = 230
|
|
20
30
|
|
|
21
|
-
# +layout+ is the PET's keyboard, :graphics or :business
|
|
31
|
+
# +layout+ is the PET's keyboard, :graphics or :business, or :cbm2
|
|
32
|
+
# for the CBM-II's.
|
|
22
33
|
def initialize(keyboard, layout)
|
|
23
34
|
@keyboard = keyboard
|
|
24
35
|
@layout = layout
|
|
36
|
+
@shift = layout == :cbm2 ? :shift : :lshift
|
|
25
37
|
@held = {}
|
|
26
38
|
@left_shift = false
|
|
27
39
|
@right_shift = false
|
|
@@ -29,8 +41,12 @@ module Badline
|
|
|
29
41
|
@right_alt = false
|
|
30
42
|
@forced = -1
|
|
31
43
|
@forced_shift = false
|
|
44
|
+
@positional = false
|
|
32
45
|
end
|
|
33
46
|
|
|
47
|
+
# Whether the keys go by position, for the SuperPET's APL characters.
|
|
48
|
+
attr_writer :positional
|
|
49
|
+
|
|
34
50
|
def key(scancode, down)
|
|
35
51
|
if [LSHIFT, RSHIFT].include?(scancode)
|
|
36
52
|
shift_key(scancode, down)
|
|
@@ -43,6 +59,11 @@ module Badline
|
|
|
43
59
|
end
|
|
44
60
|
end
|
|
45
61
|
|
|
62
|
+
# Whether a key the scancode pressed is still down.
|
|
63
|
+
def holds?(scancode) = @held.key?(scancode)
|
|
64
|
+
|
|
65
|
+
def alt? = @left_alt || @right_alt
|
|
66
|
+
|
|
46
67
|
# Lets go of every key, SHIFT among them.
|
|
47
68
|
def release_all
|
|
48
69
|
@held.each_value { |key| @keyboard.release(key) }
|
|
@@ -59,7 +80,10 @@ module Badline
|
|
|
59
80
|
|
|
60
81
|
def key_down(scancode)
|
|
61
82
|
key_up(scancode) if @held.key?(scancode)
|
|
62
|
-
key, shift = alt?
|
|
83
|
+
key, shift = if alt? then shifted_key(scancode)
|
|
84
|
+
elsif @positional then positional_key(scancode)
|
|
85
|
+
else layout_key(scancode, shifted?)
|
|
86
|
+
end
|
|
63
87
|
return if key == :none
|
|
64
88
|
|
|
65
89
|
unless shift == :host
|
|
@@ -99,10 +123,17 @@ module Badline
|
|
|
99
123
|
end
|
|
100
124
|
end
|
|
101
125
|
|
|
126
|
+
def layout_key(scancode, shift)
|
|
127
|
+
@layout == :cbm2 ? Keys.cbm2_key(scancode, shift) : Keys.pet_key(scancode, shift, @layout)
|
|
128
|
+
end
|
|
129
|
+
|
|
102
130
|
# The PET key for the scancode with Alt down: for a letter or a key
|
|
103
131
|
# that types a character, the key its unshifted character is on, with
|
|
104
|
-
# SHIFT, and for any other key the one it presses without Alt.
|
|
132
|
+
# SHIFT, and for any other key the one it presses without Alt. The
|
|
133
|
+
# CBM-II's is the one Keys.cbm2_alt_key names.
|
|
105
134
|
def shifted_key(scancode)
|
|
135
|
+
return Keys.cbm2_alt_key(scancode) if @layout == :cbm2
|
|
136
|
+
|
|
106
137
|
key, shift = Keys.pet_key(scancode, false, @layout)
|
|
107
138
|
return [key, shift] if key == :none
|
|
108
139
|
return [key, shift] unless scancode.between?(4, 29) || Keys::US_CHARACTERS.key?(scancode)
|
|
@@ -110,14 +141,21 @@ module Badline
|
|
|
110
141
|
[key, :on]
|
|
111
142
|
end
|
|
112
143
|
|
|
113
|
-
|
|
144
|
+
# The PET key for the scancode by position: the one its unshifted
|
|
145
|
+
# character is on, with the host's SHIFT.
|
|
146
|
+
def positional_key(scancode)
|
|
147
|
+
key, shift = Keys.pet_key(scancode, false, @layout)
|
|
148
|
+
return [key, shift] if key == :none || !Keys::US_CHARACTERS.key?(scancode)
|
|
114
149
|
|
|
115
|
-
|
|
150
|
+
[key, :host]
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def shifted? = @left_shift || @right_shift
|
|
116
154
|
|
|
117
155
|
def sync_shift
|
|
118
156
|
shift = @forced == -1 ? shifted? : @forced_shift
|
|
119
|
-
@keyboard.release(
|
|
120
|
-
@keyboard.press(
|
|
157
|
+
@keyboard.release(@shift)
|
|
158
|
+
@keyboard.press(@shift) if shift
|
|
121
159
|
end
|
|
122
160
|
end
|
|
123
161
|
end
|
|
@@ -15,7 +15,7 @@ module Badline
|
|
|
15
15
|
# least MIN_WIDTH window pixels wide, and at least as high as PAL's C64
|
|
16
16
|
# crop, 272 lines, so a C64 shows in the same window whatever its
|
|
17
17
|
# region. A crop with fewer lines or pixels sits in the middle of the
|
|
18
|
-
# texture, between black bands. The VIC-20's 284 by 284 crop and the
|
|
18
|
+
# texture, between black bands. The PAL VIC-20's 284 by 284 crop and the
|
|
19
19
|
# TED's 384 by 288 fill their own.
|
|
20
20
|
#
|
|
21
21
|
# With `frame_lines`, the crop's lines run on past the frame's last
|
|
@@ -10,6 +10,10 @@ module Badline
|
|
|
10
10
|
# On the C128, #follow switches to the screen the machine prints to
|
|
11
11
|
# (C128#active_screen) each time it moves there, and #show_vdc's choice
|
|
12
12
|
# holds until the next move.
|
|
13
|
+
#
|
|
14
|
+
# The KIM-1 has no video chip, and shows its panel (KIM1Panel), or with
|
|
15
|
+
# its TTY/KB jumper fitted its teletype's page (TeletypePane), which
|
|
16
|
+
# #follow brings up to the machine each frame.
|
|
13
17
|
class Screens
|
|
14
18
|
# The frames a move holds before #follow takes it. The C128 KERNAL's
|
|
15
19
|
# reset clears the 80 column screen with the editor on it for over a
|
|
@@ -18,8 +22,17 @@ module Badline
|
|
|
18
22
|
|
|
19
23
|
attr_reader :screen
|
|
20
24
|
|
|
25
|
+
# The KIM-1's panel, the picture a KIM-1 shows.
|
|
26
|
+
attr_reader :kim1_panel
|
|
27
|
+
|
|
28
|
+
# The KIM-1's teletype's page, which it shows with the jumper fitted.
|
|
29
|
+
attr_reader :teletype_pane
|
|
30
|
+
|
|
21
31
|
def initialize(computer)
|
|
22
32
|
@computer = computer
|
|
33
|
+
@kim1_panel = KIM1Panel.new
|
|
34
|
+
@teletype_pane = TeletypePane.new
|
|
35
|
+
@built_tty = false
|
|
23
36
|
@vdc_shown = false
|
|
24
37
|
@vdc_shape = []
|
|
25
38
|
forget_screen
|
|
@@ -31,6 +44,8 @@ module Badline
|
|
|
31
44
|
# Shows another machine's video chip, once #build builds its screen.
|
|
32
45
|
def computer=(computer)
|
|
33
46
|
@computer = computer
|
|
47
|
+
@kim1_panel = KIM1Panel.new
|
|
48
|
+
@teletype_pane = TeletypePane.new
|
|
34
49
|
@vdc_shown = false
|
|
35
50
|
forget_screen
|
|
36
51
|
end
|
|
@@ -38,6 +53,7 @@ module Badline
|
|
|
38
53
|
# Shows the screen a C128 has moved its output to once the move has
|
|
39
54
|
# held for FOLLOW_FRAMES calls, one a frame. #build then builds it.
|
|
40
55
|
def follow
|
|
56
|
+
return follow_kim1 if @computer.family == :kim1
|
|
41
57
|
return unless @computer.family == :c128
|
|
42
58
|
|
|
43
59
|
vdc = @computer.active_screen == :vdc
|
|
@@ -60,11 +76,17 @@ module Badline
|
|
|
60
76
|
|
|
61
77
|
# Whether the screen was built for the other chip, or the VDC's
|
|
62
78
|
# display has changed size or crop since the screen was built for it.
|
|
63
|
-
def stale?
|
|
79
|
+
def stale?
|
|
80
|
+
return @built_tty != @computer.tty? if @computer.family == :kim1
|
|
81
|
+
|
|
82
|
+
@built_vdc != @vdc_shown || (@vdc_shown && vdc_shape(@computer.vdc) != @vdc_shape)
|
|
83
|
+
end
|
|
64
84
|
|
|
65
85
|
def build
|
|
66
86
|
@built_vdc = @vdc_shown
|
|
67
|
-
if @
|
|
87
|
+
if @computer.family == :kim1
|
|
88
|
+
build_kim1
|
|
89
|
+
elsif @vdc_shown
|
|
68
90
|
vdc = @computer.vdc
|
|
69
91
|
@vdc_shape = vdc_shape(vdc)
|
|
70
92
|
@screen = Screen.new(vdc, vdc.crop, dots: 2)
|
|
@@ -78,6 +100,22 @@ module Badline
|
|
|
78
100
|
|
|
79
101
|
private
|
|
80
102
|
|
|
103
|
+
# The panel and the teletype's page both follow the machine, the
|
|
104
|
+
# LEDs' brightness and the page's lines going on behind the other.
|
|
105
|
+
def follow_kim1
|
|
106
|
+
@kim1_panel.follow(@computer)
|
|
107
|
+
@teletype_pane.follow(@computer)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def build_kim1
|
|
111
|
+
@built_tty = @computer.tty?
|
|
112
|
+
@screen = if @built_tty
|
|
113
|
+
Screen.new(@teletype_pane, @teletype_pane.crop)
|
|
114
|
+
else
|
|
115
|
+
Screen.new(@kim1_panel, @kim1_panel.crop)
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
81
119
|
# The machine's output counts as on the VIC-IIe's screen, the one
|
|
82
120
|
# shown.
|
|
83
121
|
def forget_screen
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
module Frontend
|
|
5
|
+
# The KIM-1's teletype as the window shows it with the TTY/KB jumper
|
|
6
|
+
# fitted: a page of COLUMNS by ROWS characters in the character ROM's
|
|
7
|
+
# font, printed as the teletype prints (KIM1::Teletype#output), with a
|
|
8
|
+
# line of the host keys that type what the window's keys don't. RETURN
|
|
9
|
+
# goes back to the line's start, LINE FEED down a line, the page
|
|
10
|
+
# scrolling up from the last, and a line full at COLUMNS goes on to the
|
|
11
|
+
# next. It answers what a Screen reads of a video chip, as KIM1Panel
|
|
12
|
+
# does.
|
|
13
|
+
class TeletypePane
|
|
14
|
+
COLUMNS = 72
|
|
15
|
+
ROWS = 24
|
|
16
|
+
LINE = 10
|
|
17
|
+
MARGIN = 8
|
|
18
|
+
TOP = 22
|
|
19
|
+
|
|
20
|
+
WIDTH = (COLUMNS * 8) + (2 * MARGIN)
|
|
21
|
+
HEIGHT = 272
|
|
22
|
+
|
|
23
|
+
PAPER = 0
|
|
24
|
+
INK = 1
|
|
25
|
+
HINT = 2
|
|
26
|
+
CURSOR = 3
|
|
27
|
+
PALETTE = [0xf2ecd8, 0x202020, 0x6c6858, 0x9c9480].freeze
|
|
28
|
+
|
|
29
|
+
HINTS = "BACKSPACE: RUBOUT DOWN: LINE FEED F9: MENU"
|
|
30
|
+
|
|
31
|
+
attr_reader :display, :dirty_lines
|
|
32
|
+
|
|
33
|
+
def initialize
|
|
34
|
+
@display = Array.new(WIDTH * HEIGHT, PAPER)
|
|
35
|
+
@dirty_lines = Array.new(HEIGHT, true)
|
|
36
|
+
@font = ROM.read("character.rom")
|
|
37
|
+
@page = Array.new(ROWS) { Array.new(COLUMNS, 0x20) }
|
|
38
|
+
@column = 0
|
|
39
|
+
@row = 0
|
|
40
|
+
@printed = 0
|
|
41
|
+
@baud = 0
|
|
42
|
+
draw_cursor(CURSOR)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def width = WIDTH
|
|
46
|
+
|
|
47
|
+
def height = HEIGHT
|
|
48
|
+
|
|
49
|
+
def palette = PALETTE
|
|
50
|
+
|
|
51
|
+
def crop = [0, 0, WIDTH, HEIGHT]
|
|
52
|
+
|
|
53
|
+
def clear_dirty_lines!
|
|
54
|
+
@dirty_lines.map! { false }
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# The characters on the page, a line each, without trailing spaces.
|
|
58
|
+
def lines = @page.map { |row| row.map(&:chr).join.rstrip }
|
|
59
|
+
|
|
60
|
+
# Prints what the KIM-1 `computer`'s teletype has printed since the
|
|
61
|
+
# last call.
|
|
62
|
+
def follow(computer)
|
|
63
|
+
teletype = computer.teletype
|
|
64
|
+
title(teletype.baud) if teletype.baud != @baud
|
|
65
|
+
output = teletype.output
|
|
66
|
+
return if output.size == @printed
|
|
67
|
+
|
|
68
|
+
draw_cursor(PAPER)
|
|
69
|
+
output[@printed, output.size - @printed].bytes.each { |byte| print_byte(byte) }
|
|
70
|
+
@printed = output.size
|
|
71
|
+
draw_cursor(CURSOR)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
private
|
|
75
|
+
|
|
76
|
+
def title(baud)
|
|
77
|
+
@baud = baud
|
|
78
|
+
box(0, 0, WIDTH, TOP, PAPER)
|
|
79
|
+
text(MARGIN, 6, "KIM-1 TELETYPE, #{baud} BAUD", HINT)
|
|
80
|
+
text(WIDTH - MARGIN - (HINTS.size * 8), 6, HINTS, HINT)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def print_byte(byte)
|
|
84
|
+
case byte
|
|
85
|
+
when 0x0d then @column = 0
|
|
86
|
+
when 0x0a then line_feed
|
|
87
|
+
when 0x20..0x7e then print_character(byte)
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def print_character(byte)
|
|
92
|
+
if @column == COLUMNS
|
|
93
|
+
@column = 0
|
|
94
|
+
line_feed
|
|
95
|
+
end
|
|
96
|
+
@page[@row][@column] = byte
|
|
97
|
+
glyph(MARGIN + (@column * 8), TOP + (@row * LINE), byte, INK)
|
|
98
|
+
@column += 1
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def line_feed
|
|
102
|
+
if @row < ROWS - 1
|
|
103
|
+
@row += 1
|
|
104
|
+
return
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
@page.shift
|
|
108
|
+
@page << Array.new(COLUMNS, 0x20)
|
|
109
|
+
redraw_page
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def redraw_page
|
|
113
|
+
box(0, TOP, WIDTH, ROWS * LINE, PAPER)
|
|
114
|
+
@page.each_with_index do |characters, row|
|
|
115
|
+
characters.each_with_index do |byte, column|
|
|
116
|
+
glyph(MARGIN + (column * 8), TOP + (row * LINE), byte, INK) unless byte == 0x20
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def draw_cursor(color)
|
|
122
|
+
column = [@column, COLUMNS - 1].min
|
|
123
|
+
box(MARGIN + (column * 8), TOP + (@row * LINE) + 8, 8, 1, color)
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def box(left, top, width, height, color)
|
|
127
|
+
height.times do |row|
|
|
128
|
+
line = top + row
|
|
129
|
+
@display.fill(color, (line * WIDTH) + left, width)
|
|
130
|
+
@dirty_lines[line] = true
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def text(left, top, string, color)
|
|
135
|
+
string.bytes.each_with_index { |byte, index| glyph(left + (index * 8), top, byte, color) }
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
# Draws the glyph for the ASCII `byte` over a cleared cell.
|
|
139
|
+
def glyph(left, top, byte, color)
|
|
140
|
+
code = Painter::SCREEN_CODES[byte & 0x7f]
|
|
141
|
+
8.times do |row|
|
|
142
|
+
bits = @font[0x800 + (code * 8) + row]
|
|
143
|
+
line = top + row
|
|
144
|
+
8.times do |dot|
|
|
145
|
+
@display[(line * WIDTH) + left + dot] = bits.anybits?(0x80 >> dot) ? color : PAPER
|
|
146
|
+
end
|
|
147
|
+
@dirty_lines[line] = true
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
end
|
|
@@ -25,7 +25,8 @@ module Badline
|
|
|
25
25
|
LOCKS = %w[caps_lock forty_eighty].freeze
|
|
26
26
|
|
|
27
27
|
# What's wrong with the events for the family's machine, the first
|
|
28
|
-
# key or menu page that names nothing, or an empty string.
|
|
28
|
+
# key or menu page that names nothing, or an empty string. The
|
|
29
|
+
# CBM-II's P500 is family :p500 here, for its joysticks.
|
|
29
30
|
def self.error(events, family)
|
|
30
31
|
event = events.find { |candidate| candidate.action == "key" && !key?(candidate.argument, family) }
|
|
31
32
|
return "no such key: #{event.argument}" unless event.nil?
|
|
@@ -38,23 +39,28 @@ module Badline
|
|
|
38
39
|
# sound. An empty one is its first.
|
|
39
40
|
def self.page?(name) = name.empty? || PauseMenu::PAGES.any? { |page| page.to_s == name }
|
|
40
41
|
|
|
41
|
-
# Whether the family's machine has a key of the name
|
|
42
|
-
# no RESTORE,
|
|
42
|
+
# Whether the family's machine has a key of the name, the CBM-II's
|
|
43
|
+
# P500's as family :p500. The Plus/4 has no RESTORE, the P500 none
|
|
44
|
+
# either, and the PET and the CBM-II's B series neither that nor
|
|
45
|
+
# joysticks.
|
|
43
46
|
def self.key?(name, family)
|
|
44
|
-
|
|
45
|
-
|
|
47
|
+
return !kim1_key(name).empty? if family == :kim1
|
|
48
|
+
|
|
49
|
+
pet = %i[pet cbm2].include?(family)
|
|
50
|
+
(name == "restore" && !pet && !%i[plus4 p500].include?(family)) || (family == :c128 && LOCKS.include?(name)) ||
|
|
46
51
|
!keyboard_key(name, family).nil? || (!pet && !joystick_direction(name).nil?)
|
|
47
52
|
end
|
|
48
53
|
|
|
49
54
|
# The family's Keyboard key for a name such as space, return or a,
|
|
50
55
|
# on the C128 esc or keypad1, on the Plus/4 help or crsr_up, or on
|
|
51
|
-
# the PET rvs or kp1, of either of its keyboards
|
|
52
|
-
#
|
|
56
|
+
# the PET rvs or kp1, of either of its keyboards, or on the CBM-II
|
|
57
|
+
# escape, norm_graph or kp00. The shifted cursor keys, such as
|
|
58
|
+
# cursor_up, are the C64's and the C128's.
|
|
53
59
|
def self.keyboard_key(name, family)
|
|
54
60
|
return nil if name == "none"
|
|
55
61
|
|
|
56
62
|
key = keyboard_matrix(family).flatten.find { |candidate| candidate.to_s == name }
|
|
57
|
-
return key unless key.nil? && !%i[plus4 pet].include?(family)
|
|
63
|
+
return key unless key.nil? && !%i[plus4 pet cbm2 p500].include?(family)
|
|
58
64
|
|
|
59
65
|
Input::Keyboard::COMBINATIONS.keys.find { |candidate| candidate.to_s == name }
|
|
60
66
|
end
|
|
@@ -64,10 +70,18 @@ module Badline
|
|
|
64
70
|
when :c128 then C128::KEYBOARD_MATRIX
|
|
65
71
|
when :plus4 then Plus4::KeyboardPort::MATRIX
|
|
66
72
|
when :pet then PET::KeyboardPorts::GRAPHICS_MATRIX + PET::KeyboardPorts::BUSINESS_MATRIX
|
|
73
|
+
when :cbm2, :p500 then CBM2::KeyboardPorts::MATRIX
|
|
67
74
|
else Input::Keyboard::C64_MATRIX
|
|
68
75
|
end
|
|
69
76
|
end
|
|
70
77
|
|
|
78
|
+
# The KIM-1's key for a name such as go, a or plus, as
|
|
79
|
+
# KIM1::Panel::KEYS names it, with st and rs, or an empty string.
|
|
80
|
+
def self.kim1_key(name)
|
|
81
|
+
key = name == "plus" ? "+" : name.upcase
|
|
82
|
+
KIM1::Panel::KEYS.include?(key) || %w[ST RS].include?(key) ? key : ""
|
|
83
|
+
end
|
|
84
|
+
|
|
71
85
|
# The direction of joy1-NAME or joy2-NAME, such as up or fire.
|
|
72
86
|
def self.joystick_direction(name)
|
|
73
87
|
return nil unless name.start_with?("joy1-") || name.start_with?("joy2-")
|
|
@@ -145,7 +159,7 @@ module Badline
|
|
|
145
159
|
def perform(computer, event)
|
|
146
160
|
case event.action
|
|
147
161
|
when "key" then press(computer, event)
|
|
148
|
-
when "type" then computer
|
|
162
|
+
when "type" then type(computer, event.argument)
|
|
149
163
|
when "insert" then insert(computer, event.argument)
|
|
150
164
|
when "eject" then eject(computer, event.argument)
|
|
151
165
|
when "reset" then computer.reset!
|
|
@@ -153,8 +167,15 @@ module Badline
|
|
|
153
167
|
end
|
|
154
168
|
end
|
|
155
169
|
|
|
170
|
+
# Types the text, which on the KIM-1 goes to its teletype, or with
|
|
171
|
+
# none presses the keys it names (KIM1#type_text).
|
|
172
|
+
def type(computer, text)
|
|
173
|
+
computer.type_text(text.gsub("\\n", "\r").gsub("\\r", "\r"))
|
|
174
|
+
end
|
|
175
|
+
|
|
156
176
|
def press(computer, event)
|
|
157
177
|
name = event.argument
|
|
178
|
+
return press_kim1(computer, event) if computer.family == :kim1
|
|
158
179
|
return lock(computer, name) if LOCKS.include?(name)
|
|
159
180
|
|
|
160
181
|
if name == "restore"
|
|
@@ -171,6 +192,17 @@ module Badline
|
|
|
171
192
|
hold(event)
|
|
172
193
|
end
|
|
173
194
|
|
|
195
|
+
# ST and RS act as they go down, and the KIM-1's other keys are held.
|
|
196
|
+
def press_kim1(computer, event)
|
|
197
|
+
case (key = Timeline.kim1_key(event.argument))
|
|
198
|
+
when "ST" then computer.press_stop
|
|
199
|
+
when "RS" then computer.press_reset
|
|
200
|
+
else
|
|
201
|
+
computer.press_key(key)
|
|
202
|
+
hold(event)
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
|
|
174
206
|
# CAPS LOCK and 40/80 DISPLAY lock: a push puts one down if it was up,
|
|
175
207
|
# and up if it was down.
|
|
176
208
|
def lock(computer, name)
|
|
@@ -194,6 +226,8 @@ module Badline
|
|
|
194
226
|
def release(computer, event)
|
|
195
227
|
return computer.release_cartridge_button if event.action == "freeze" && computer.family == :c64
|
|
196
228
|
|
|
229
|
+
return computer.release_key if computer.family == :kim1
|
|
230
|
+
|
|
197
231
|
name = event.argument
|
|
198
232
|
return computer.release_restore if name == "restore"
|
|
199
233
|
|
|
@@ -228,9 +262,13 @@ module Badline
|
|
|
228
262
|
end
|
|
229
263
|
|
|
230
264
|
# Whether there's one in to eject. A VIC-20 has no cartridge to take
|
|
231
|
-
# out yet,
|
|
265
|
+
# out yet, a PET no expansion port, and a CBM-II no datasette, and
|
|
266
|
+
# its cartridge stays in.
|
|
232
267
|
def ejectable?(computer, what)
|
|
233
|
-
|
|
268
|
+
family = computer.family
|
|
269
|
+
return false if family == :kim1 || (family == :cbm2 && what != "disk")
|
|
270
|
+
|
|
271
|
+
(what != "cartridge" || !%i[vic20 pet].include?(family)) && inserted?(computer, what)
|
|
234
272
|
end
|
|
235
273
|
|
|
236
274
|
def inserted?(computer, what)
|
data/lib/badline/frontend.rb
CHANGED
|
@@ -4,6 +4,8 @@ require "badline/sdl"
|
|
|
4
4
|
require "badline/frontend/boot"
|
|
5
5
|
require "badline/frontend/frame_rate"
|
|
6
6
|
require "badline/frontend/screen"
|
|
7
|
+
require "badline/frontend/kim1_panel"
|
|
8
|
+
require "badline/frontend/teletype_pane"
|
|
7
9
|
require "badline/frontend/screens"
|
|
8
10
|
require "badline/frontend/drive_led"
|
|
9
11
|
require "badline/frontend/keys"
|
|
@@ -42,6 +44,7 @@ require "badline/frontend/menu/pause_menu"
|
|
|
42
44
|
require "badline/frontend/menu/file_browser"
|
|
43
45
|
require "badline/frontend/menu/confirmation"
|
|
44
46
|
require "badline/frontend/menu/name_field"
|
|
47
|
+
require "badline/frontend/menu/drive_types"
|
|
45
48
|
require "badline/frontend/menu/menu_dialogs"
|
|
46
49
|
require "badline/frontend/menu/menu_media"
|
|
47
50
|
require "badline/frontend/menu/menu_ports"
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
module KernalTrap
|
|
5
|
+
class DOS
|
|
6
|
+
# N, R and C: the commands that format a disk, rename a file and copy
|
|
7
|
+
# files. Each takes its names after a colon. A name can't hold a
|
|
8
|
+
# wildcard, and R and C take their new name before an "=". The DOS
|
|
9
|
+
# here has one drive, so a file on drive 1 is never found, and C
|
|
10
|
+
# without a colon, which copies a whole disk between the drives of a
|
|
11
|
+
# dual drive, and D, which duplicates one, are invalid commands, as on
|
|
12
|
+
# a single drive.
|
|
13
|
+
module DiskCommands
|
|
14
|
+
WILDCARD = /[*?]/
|
|
15
|
+
OTHER_DRIVE = /\A[1-9]\d*:/
|
|
16
|
+
DRIVE_ZERO = /\A0+:/
|
|
17
|
+
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
# N with an ID after a comma formats the disk, and without one only
|
|
21
|
+
# clears it. The ID is the two bytes after the comma, which takes the
|
|
22
|
+
# carriage return PRINT# ends the command with when the ID is one
|
|
23
|
+
# character. A comma with nothing after it gives no ID.
|
|
24
|
+
def new_disk(text)
|
|
25
|
+
colon = text.index(":")
|
|
26
|
+
return report(NO_FILE_GIVEN) unless colon
|
|
27
|
+
|
|
28
|
+
command = text.chomp("\r")
|
|
29
|
+
name, id = command[(colon + 1)..].split(",", 2)
|
|
30
|
+
return report(SYNTAX_ERROR) if name.to_s.match?(/[*?=]/)
|
|
31
|
+
return report(WRITE_PROTECT_ON) unless @storage.respond_to?(:format)
|
|
32
|
+
|
|
33
|
+
id = id.to_s.empty? ? nil : text.bytes[(command.index(",") + 1), 2]
|
|
34
|
+
writing { @storage.format(name.to_s, id && (id + [Storage::DiskImage::NAME_PADDING]).first(2)) }
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# R takes the new name, then the file's.
|
|
38
|
+
def rename_file(text)
|
|
39
|
+
new_name, old_names = file_names(text, NO_FILE_GIVEN)
|
|
40
|
+
return unless new_name
|
|
41
|
+
return report(SYNTAX_ERROR) unless old_names.one?
|
|
42
|
+
return report(WRITE_PROTECT_ON) unless @storage.respond_to?(:rename)
|
|
43
|
+
return report(FILE_NOT_FOUND) if old_names.first.match?(OTHER_DRIVE)
|
|
44
|
+
|
|
45
|
+
writing { @storage.rename(Storage.strip_drive_prefix(old_names.first), new_name) }
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# C takes the new name, then the files it joins, separated by
|
|
49
|
+
# commas. A first file named with drive 0 that is the new file
|
|
50
|
+
# itself has the others added to its end.
|
|
51
|
+
def copy_file(text)
|
|
52
|
+
new_name, old_names = file_names(text, INVALID_COMMAND)
|
|
53
|
+
return unless new_name
|
|
54
|
+
return report(WRITE_PROTECT_ON) unless @storage.respond_to?(:copy)
|
|
55
|
+
return report(FILE_NOT_FOUND) if old_names.any? { |name| name.match?(OTHER_DRIVE) }
|
|
56
|
+
|
|
57
|
+
names = old_names.map { |name| Storage.strip_drive_prefix(name) }
|
|
58
|
+
in_place = old_names.first.match?(DRIVE_ZERO) && names.first == new_name
|
|
59
|
+
writing { in_place ? @storage.concat(new_name, names.drop(1)) : @storage.copy(new_name, names) }
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# The new name and the old ones of "new=old,...", or nil once it
|
|
63
|
+
# has reported what's wrong.
|
|
64
|
+
def file_names(text, missing_colon)
|
|
65
|
+
colon = text.index(":")
|
|
66
|
+
return report(missing_colon) unless colon
|
|
67
|
+
|
|
68
|
+
new_name, rest = text[(colon + 1)..].split("=", 2)
|
|
69
|
+
new_name = new_name.to_s
|
|
70
|
+
return report(SYNTAX_ERROR) if rest.nil? || new_name.include?(",")
|
|
71
|
+
|
|
72
|
+
old_names = rest.empty? ? [""] : rest.split(",", -1)
|
|
73
|
+
return report(SYNTAX_ERROR) if [new_name, *old_names].any? { |name| name.match?(WILDCARD) }
|
|
74
|
+
|
|
75
|
+
[new_name, old_names]
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
@@ -15,8 +15,11 @@ module Badline
|
|
|
15
15
|
28 => "WRITE ERROR",
|
|
16
16
|
29 => "DISK ID MISMATCH",
|
|
17
17
|
30 => "SYNTAX ERROR",
|
|
18
|
+
31 => "SYNTAX ERROR",
|
|
19
|
+
34 => "SYNTAX ERROR",
|
|
18
20
|
62 => "FILE NOT FOUND",
|
|
19
21
|
63 => "FILE EXISTS",
|
|
22
|
+
64 => "FILE TYPE MISMATCH",
|
|
20
23
|
65 => "NO BLOCK",
|
|
21
24
|
66 => "ILLEGAL TRACK OR SECTOR",
|
|
22
25
|
70 => "NO CHANNEL",
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
require "badline/kernal_trap/dos/block_commands"
|
|
4
4
|
require "badline/kernal_trap/dos/channel"
|
|
5
5
|
require "badline/kernal_trap/dos/channels"
|
|
6
|
+
require "badline/kernal_trap/dos/disk_commands"
|
|
6
7
|
require "badline/kernal_trap/dos/memory"
|
|
7
8
|
require "badline/kernal_trap/dos/parameters"
|
|
8
9
|
require "badline/kernal_trap/dos/status"
|
|
@@ -15,10 +16,12 @@ module Badline
|
|
|
15
16
|
# command channel and its status message. `U1` reads a raw block into a
|
|
16
17
|
# buffer channel, which is how block-access loaders bypass the KERNAL's
|
|
17
18
|
# LOAD. A disk image whose host file takes writes takes files, blocks
|
|
18
|
-
# and BAM changes too
|
|
19
|
-
#
|
|
19
|
+
# and BAM changes too, and N, R and C format it, rename and copy files.
|
|
20
|
+
# Other disks are write-protected, though storage that can write files
|
|
21
|
+
# still takes a SAVE handed over whole.
|
|
20
22
|
class DOS
|
|
21
23
|
include BlockCommands
|
|
24
|
+
include DiskCommands
|
|
22
25
|
include Writes
|
|
23
26
|
|
|
24
27
|
COMMAND_CHANNEL = 15
|
|
@@ -28,6 +31,8 @@ module Badline
|
|
|
28
31
|
FILES_SCRATCHED = 1
|
|
29
32
|
WRITE_PROTECT_ON = 26
|
|
30
33
|
SYNTAX_ERROR = 30
|
|
34
|
+
INVALID_COMMAND = 31
|
|
35
|
+
NO_FILE_GIVEN = 34
|
|
31
36
|
FILE_NOT_FOUND = 62
|
|
32
37
|
FILE_EXISTS = 63
|
|
33
38
|
NO_BLOCK = 65
|
|
@@ -60,11 +65,15 @@ module Badline
|
|
|
60
65
|
/\AV/i => :initialized,
|
|
61
66
|
/\AB-A\s*:?\s*(.*)/i => :block_allocate,
|
|
62
67
|
/\AB-F\s*:?\s*(.*)/i => :block_free,
|
|
63
|
-
/\AS[^:]*:(.*)/im => :scratch
|
|
68
|
+
/\AS[^:]*:(.*)/im => :scratch,
|
|
69
|
+
/\AN(.*)/im => :new_disk,
|
|
70
|
+
/\AR(.*)/im => :rename_file,
|
|
71
|
+
/\AC(.*)/im => :copy_file,
|
|
72
|
+
/\AD/i => :invalid
|
|
64
73
|
}.freeze
|
|
65
74
|
|
|
66
75
|
# Commands that take their argument as text rather than numbers.
|
|
67
|
-
TEXT_COMMANDS = %i[scratch].freeze
|
|
76
|
+
TEXT_COMMANDS = %i[scratch new_disk rename_file copy_file].freeze
|
|
68
77
|
|
|
69
78
|
# A drive powers on reporting its DOS version, as a reset does. It
|
|
70
79
|
# can take the RAM of a drive it replaces, and initializes its disk.
|
|
@@ -197,7 +206,7 @@ module Badline
|
|
|
197
206
|
return report(SYNTAX_ERROR) unless action
|
|
198
207
|
|
|
199
208
|
action = USER_TABLE[(command.getbyte(1) - 1) & 0x0f] if action == :user
|
|
200
|
-
argument = pattern.match(command)[1]
|
|
209
|
+
argument = pattern.match(action == :new_disk ? text.lstrip : command)[1]
|
|
201
210
|
run_command(action, TEXT_COMMANDS.include?(action) ? argument : Parameters.parse(argument))
|
|
202
211
|
end
|
|
203
212
|
|
|
@@ -218,6 +227,10 @@ module Badline
|
|
|
218
227
|
when :block_allocate then block_allocate(arguments)
|
|
219
228
|
when :block_free then block_free(arguments)
|
|
220
229
|
when :scratch then scratch(arguments)
|
|
230
|
+
when :new_disk then new_disk(arguments)
|
|
231
|
+
when :rename_file then rename_file(arguments)
|
|
232
|
+
when :copy_file then copy_file(arguments)
|
|
233
|
+
when :invalid then report(INVALID_COMMAND)
|
|
221
234
|
when :cold_reset then reset(cold: true)
|
|
222
235
|
when :warm_reset then reset(cold: false)
|
|
223
236
|
when :memory_write then memory_write(arguments)
|