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
data/lib/badline/frontend/app.rb
CHANGED
|
@@ -105,22 +105,37 @@ module Badline
|
|
|
105
105
|
@controls.mouse_motion(SDL.event_xrel(SDL.event), SDL.event_yrel(SDL.event))
|
|
106
106
|
when SDL::MOUSEBUTTONDOWN, SDL::MOUSEBUTTONUP
|
|
107
107
|
@controls.mouse_button(SDL.event_button(SDL.event), type == SDL::MOUSEBUTTONDOWN)
|
|
108
|
+
click_panel(type == SDL::MOUSEBUTTONDOWN) if @computer.family == :kim1 && !@computer.tty?
|
|
108
109
|
when SDL::CONTROLLERDEVICEADDED, SDL::CONTROLLERDEVICEREMOVED
|
|
109
110
|
@gamepads.rescan
|
|
110
111
|
when SDL::DROPFILE then resume(false) unless @menu.drop(@computer, @controls, @sound)
|
|
111
112
|
end
|
|
112
113
|
end
|
|
113
114
|
|
|
115
|
+
# The window keeps F8, F9, F10, Tab and the snapshot keys for itself,
|
|
116
|
+
# but for the keys the machine claims (Controls#claims?).
|
|
114
117
|
def handle_key(scancode, down)
|
|
115
|
-
|
|
118
|
+
claimed = @controls.claims?(scancode)
|
|
119
|
+
if !claimed && toggle?(scancode)
|
|
116
120
|
handle_toggle(scancode) if down
|
|
117
|
-
elsif down && @snapshots.key(scancode)
|
|
121
|
+
elsif !claimed && down && @snapshots.key(scancode)
|
|
118
122
|
swap(@snapshots.computer)
|
|
119
|
-
elsif !Snapshots::KEYS.include?(scancode)
|
|
123
|
+
elsif claimed || !Snapshots::KEYS.include?(scancode)
|
|
120
124
|
@controls.key(scancode, down)
|
|
121
125
|
end
|
|
122
126
|
end
|
|
123
127
|
|
|
128
|
+
# A click on the KIM-1's panel presses the key under the pointer, and
|
|
129
|
+
# its release lets go.
|
|
130
|
+
def click_panel(down)
|
|
131
|
+
event = SDL.event
|
|
132
|
+
@controls.panel_click(@screens.kim1_panel.key_at(SDL.event_x(event), SDL.event_y(event)), down)
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def toggle?(scancode)
|
|
136
|
+
[Keys::F8, Keys::F9, Keys::F10].include?(scancode) || (scancode == Keys::TAB && @controls.tab_toggles?)
|
|
137
|
+
end
|
|
138
|
+
|
|
124
139
|
# Runs the restored machine in place of the one before: the screen,
|
|
125
140
|
# the drive LED, the controls, the gamepads and the sound go over to
|
|
126
141
|
# it.
|
|
@@ -10,7 +10,7 @@ module Badline
|
|
|
10
10
|
# attach, an --at key names nothing or an event fails, which it warns
|
|
11
11
|
# about.
|
|
12
12
|
def self.run(options)
|
|
13
|
-
error = Timeline.error(options.timeline, options.family)
|
|
13
|
+
error = Timeline.error(options.timeline, options.p500? ? :p500 : options.family)
|
|
14
14
|
unless error.empty?
|
|
15
15
|
warn "#{options.program}: #{error}"
|
|
16
16
|
return 1
|
|
@@ -39,8 +39,9 @@ module Badline
|
|
|
39
39
|
""
|
|
40
40
|
rescue Machine::Unsupported, Media::TrueDrive::Error, Media::DiskList::Error, Media::NotDisk, Drives::Datasette::Missing,
|
|
41
41
|
SystemCallError, SIDTune::File::FormatError, Storage::T64::FormatError, Storage::TAP::FormatError,
|
|
42
|
+
Storage::KIMTape::FormatError,
|
|
42
43
|
Storage::CRTFile::FormatError, Storage::G64Image::FormatError, C64::Cartridge::UnsupportedTypeError,
|
|
43
|
-
Snapshot::FormatError => e
|
|
44
|
+
Storage::Papertape::FormatError, Snapshot::FormatError => e
|
|
44
45
|
e.message
|
|
45
46
|
end
|
|
46
47
|
|
|
@@ -83,7 +84,7 @@ module Badline
|
|
|
83
84
|
options.cartridges.each { |slot, path| puts Media.attach(computer, path, cartridge: { slot: }) }
|
|
84
85
|
unless media.nil?
|
|
85
86
|
puts Media.attach(computer, media, autostart: options.autostart?, subtune: options.subtune,
|
|
86
|
-
disk: { read_only: !writable })
|
|
87
|
+
disk: { read_only: !writable }, kim1: Media::KIM1Media.loading(options))
|
|
87
88
|
end
|
|
88
89
|
computer
|
|
89
90
|
end
|
|
@@ -8,10 +8,13 @@ module Badline
|
|
|
8
8
|
# switches, the arrow cluster and space drive one joystick and WASD
|
|
9
9
|
# and left shift the other. The arrows start on joystick 2, and
|
|
10
10
|
# #swap_ports swaps them. The VIC-20's one joystick takes both. The
|
|
11
|
-
# PET
|
|
12
|
-
# (PETKeyboard)
|
|
13
|
-
# -
|
|
14
|
-
#
|
|
11
|
+
# PET's and the CBM-II's keys go by the character they type
|
|
12
|
+
# (PETKeyboard), and the PET and the CBM-II's B series have no
|
|
13
|
+
# joystick. The KIM-1's host keys press its panel's (Keys::KIM1_KEYS),
|
|
14
|
+
# and so does a click on one the window draws.
|
|
15
|
+
# - A pot device the pause menu plugs into a control port of a C64, a
|
|
16
|
+
# C128 or a P500: a 1351 mouse or a pair of paddles, which the host
|
|
17
|
+
# mouse drives. Motion
|
|
15
18
|
# turns the paddles or moves the mouse, and the host's left and right
|
|
16
19
|
# buttons go to whichever lines the device puts them on.
|
|
17
20
|
class Controls
|
|
@@ -24,6 +27,21 @@ module Badline
|
|
|
24
27
|
# The pot device plugged in, one of POTS.
|
|
25
28
|
attr_reader :pot, :arrows_port
|
|
26
29
|
|
|
30
|
+
# Whether the machine has joysticks: all but the PET and the CBM-II's
|
|
31
|
+
# B series.
|
|
32
|
+
def self.joysticks?(computer)
|
|
33
|
+
family = computer.family
|
|
34
|
+
family != :pet && family != :kim1 && (family != :cbm2 || p500?(computer))
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Whether the machine is the CBM-II's P500, which has the B series'
|
|
38
|
+
# keyboard and two joysticks.
|
|
39
|
+
def self.p500?(computer) = computer.is_a?(CBM2::P500)
|
|
40
|
+
|
|
41
|
+
# Whether the machine's keys go by the character they type: the PET's
|
|
42
|
+
# and the CBM-II's.
|
|
43
|
+
def self.symbolic?(computer) = %i[pet cbm2].include?(computer.family)
|
|
44
|
+
|
|
27
45
|
def initialize(computer)
|
|
28
46
|
@computer = computer
|
|
29
47
|
@joystick = false
|
|
@@ -32,6 +50,8 @@ module Badline
|
|
|
32
50
|
@mouse = Input::Mouse1351.new
|
|
33
51
|
@paddles = Input::Paddles.new
|
|
34
52
|
@held = {}
|
|
53
|
+
@clicked = ""
|
|
54
|
+
@shifts = 0
|
|
35
55
|
@pet_keyboard = pet_keyboard(computer)
|
|
36
56
|
end
|
|
37
57
|
|
|
@@ -58,9 +78,19 @@ module Badline
|
|
|
58
78
|
self.joystick_mode = !@joystick if tab_toggles?
|
|
59
79
|
end
|
|
60
80
|
|
|
61
|
-
# Whether Tab switches the keys to the joysticks, or on
|
|
62
|
-
#
|
|
63
|
-
def tab_toggles? = @computer
|
|
81
|
+
# Whether Tab switches the keys to the joysticks, or on a machine
|
|
82
|
+
# without them is a key of its own.
|
|
83
|
+
def tab_toggles? = Controls.joysticks?(@computer)
|
|
84
|
+
|
|
85
|
+
# Whether the machine takes a key the window would otherwise keep for
|
|
86
|
+
# itself: on the CBM-II, F8, RUN/STOP on F12, Alt with F9 or F10, and
|
|
87
|
+
# the release of any key it pressed.
|
|
88
|
+
def claims?(scancode)
|
|
89
|
+
return false unless @computer.family == :cbm2
|
|
90
|
+
return true if @pet_keyboard.holds?(scancode) || [Keys::F8, Keys::F12].include?(scancode)
|
|
91
|
+
|
|
92
|
+
@pet_keyboard.alt? && [Keys::F9, Keys::F10].include?(scancode)
|
|
93
|
+
end
|
|
64
94
|
|
|
65
95
|
# Plugs in one of POTS, in place of the one before.
|
|
66
96
|
def plug(pot)
|
|
@@ -100,6 +130,18 @@ module Badline
|
|
|
100
130
|
paddles? ? @paddles.move(delta_x, delta_y) : @mouse.move(delta_x, delta_y)
|
|
101
131
|
end
|
|
102
132
|
|
|
133
|
+
# Presses the KIM-1 key KIM1Panel names `name` for a click on it, or
|
|
134
|
+
# lets go of the key the last click pressed.
|
|
135
|
+
def panel_click(name, down)
|
|
136
|
+
if down
|
|
137
|
+
@clicked = name
|
|
138
|
+
kim1_key(name, true) unless name.empty?
|
|
139
|
+
else
|
|
140
|
+
kim1_key(@clicked, false) unless @clicked.empty?
|
|
141
|
+
@clicked = ""
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
103
145
|
def mouse_button(button, down)
|
|
104
146
|
name = MOUSE_BUTTONS[button]
|
|
105
147
|
return unless name && pot_device?
|
|
@@ -116,7 +158,11 @@ module Badline
|
|
|
116
158
|
# A host key lets go of the key it pressed, though the C128 has
|
|
117
159
|
# switched between C64 and C128 mode since and maps it elsewhere.
|
|
118
160
|
def machine_key(scancode, down)
|
|
119
|
-
|
|
161
|
+
if Controls.symbolic?(@computer)
|
|
162
|
+
@pet_keyboard.positional = @computer.family == :pet && @computer.apl_characters? if down
|
|
163
|
+
return @pet_keyboard.key(scancode, down)
|
|
164
|
+
end
|
|
165
|
+
return kim1_input(scancode, down) if @computer.family == :kim1
|
|
120
166
|
|
|
121
167
|
@held[scancode] = host_key(scancode) if down || !@held.key?(scancode)
|
|
122
168
|
key = @held[scancode]
|
|
@@ -154,12 +200,45 @@ module Badline
|
|
|
154
200
|
end
|
|
155
201
|
end
|
|
156
202
|
|
|
203
|
+
# The keys type on the teletype with the KIM-1's jumper fitted, and
|
|
204
|
+
# press the panel's keys without it.
|
|
205
|
+
def kim1_input(scancode, down)
|
|
206
|
+
return kim1_key(Keys.kim1_key(scancode), down) unless @computer.tty?
|
|
207
|
+
return shift(down) if [225, 229].include?(scancode)
|
|
208
|
+
|
|
209
|
+
text = Keys.kim1_teletype(scancode, @shifts.positive?)
|
|
210
|
+
@computer.teletype.type(text) if down && !text.empty?
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
def shift(down)
|
|
214
|
+
@shifts += down ? 1 : -1
|
|
215
|
+
@shifts = 0 if @shifts.negative?
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
# ST pulls NMI and RS resets as they go down, and T flips the SST
|
|
219
|
+
# switch. The other keys stay down until let go.
|
|
220
|
+
def kim1_key(name, down)
|
|
221
|
+
case name
|
|
222
|
+
when "" then nil
|
|
223
|
+
when "ST" then @computer.press_stop if down
|
|
224
|
+
when "RS" then @computer.press_reset if down
|
|
225
|
+
when "SST" then @computer.sst = !@computer.sst if down
|
|
226
|
+
else
|
|
227
|
+
if down
|
|
228
|
+
@computer.press_key(name)
|
|
229
|
+
elsif @computer.panel.held == name
|
|
230
|
+
@computer.release_key
|
|
231
|
+
end
|
|
232
|
+
end
|
|
233
|
+
end
|
|
234
|
+
|
|
157
235
|
def paddles? = POTS.index(@pot) >= 3
|
|
158
236
|
|
|
159
237
|
# A fresh pot device on its port, and none on the other. A machine
|
|
160
238
|
# without the ports takes none.
|
|
161
239
|
def attach_pot_device
|
|
162
|
-
|
|
240
|
+
portless = Controls.symbolic?(@computer) || @computer.family == :kim1
|
|
241
|
+
ports = Controls.p500?(@computer) || !portless ? @computer.control_ports : nil
|
|
163
242
|
if ports.nil?
|
|
164
243
|
@pot = :none
|
|
165
244
|
return
|
|
@@ -211,17 +290,30 @@ module Badline
|
|
|
211
290
|
down ? joystick.press(direction) : joystick.release(direction)
|
|
212
291
|
end
|
|
213
292
|
|
|
214
|
-
# The PET's keyboard for a PET, and for any other machine
|
|
215
|
-
# uses
|
|
293
|
+
# The PET's keyboard for a PET or a CBM-II, and for any other machine
|
|
294
|
+
# one it never uses, on a keyboard of its own for the KIM-1, which has
|
|
295
|
+
# none.
|
|
216
296
|
def pet_keyboard(computer)
|
|
217
|
-
|
|
297
|
+
layout = case computer.family
|
|
298
|
+
when :pet then computer.model.keyboard
|
|
299
|
+
when :cbm2 then :cbm2
|
|
300
|
+
else :graphics
|
|
301
|
+
end
|
|
302
|
+
keyboard = computer.family == :kim1 ? Input::Keyboard.new : computer.keyboard
|
|
303
|
+
PETKeyboard.new(keyboard, layout)
|
|
218
304
|
end
|
|
219
305
|
|
|
220
306
|
def release_all
|
|
221
|
-
return @
|
|
307
|
+
return @computer.release_key if @computer.family == :kim1
|
|
308
|
+
|
|
309
|
+
if Controls.symbolic?(@computer)
|
|
310
|
+
@pet_keyboard.release_all
|
|
311
|
+
else
|
|
312
|
+
Keys::ARROWS.each_key { |scancode| @computer.keyboard.release(host_key(scancode)) }
|
|
313
|
+
Keys::WASD.each_key { |scancode| @computer.keyboard.release(host_key(scancode)) }
|
|
314
|
+
end
|
|
315
|
+
return unless Controls.joysticks?(@computer)
|
|
222
316
|
|
|
223
|
-
Keys::ARROWS.each_key { |scancode| @computer.keyboard.release(host_key(scancode)) }
|
|
224
|
-
Keys::WASD.each_key { |scancode| @computer.keyboard.release(host_key(scancode)) }
|
|
225
317
|
Keys::ARROWS.each_value do |direction|
|
|
226
318
|
@computer.joystick1.release(direction)
|
|
227
319
|
@computer.joystick2.release(direction)
|
|
@@ -7,8 +7,8 @@ module Badline
|
|
|
7
7
|
# second one joystick 1. PadPort holds the mapping.
|
|
8
8
|
#
|
|
9
9
|
# The controllers are polled every frame, and rescanned whenever SDL
|
|
10
|
-
# reports one added or removed. The PET
|
|
11
|
-
# controllers steer two that nothing reads.
|
|
10
|
+
# reports one added or removed. The PET and the CBM-II's B series have
|
|
11
|
+
# no joysticks, and the controllers steer two that nothing reads.
|
|
12
12
|
class Gamepads
|
|
13
13
|
PORTS = [2, 1].freeze
|
|
14
14
|
|
|
@@ -53,7 +53,7 @@ module Badline
|
|
|
53
53
|
private
|
|
54
54
|
|
|
55
55
|
def joystick(computer, port)
|
|
56
|
-
return Input::Joystick.new
|
|
56
|
+
return Input::Joystick.new unless Controls.joysticks?(computer)
|
|
57
57
|
|
|
58
58
|
port == 1 ? computer.joystick1 : computer.joystick2
|
|
59
59
|
end
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
module Badline
|
|
4
4
|
module Frontend
|
|
5
5
|
# SDL scancodes follow key positions on a US layout. They map onto the C64
|
|
6
|
-
# keys, the C128's and the
|
|
7
|
-
# joystick directions.
|
|
6
|
+
# keys, the C128's, the 264's, the PET's and the CBM-II's own keys and,
|
|
7
|
+
# in joystick mode, the joystick directions.
|
|
8
8
|
module Keys
|
|
9
9
|
RETURN = 40
|
|
10
10
|
ESCAPE = 41
|
|
@@ -118,6 +118,54 @@ module Badline
|
|
|
118
118
|
# The cursor keys, CRSR ⇔ and CRSR ⇕, each with SHIFT for left or up.
|
|
119
119
|
PET_CURSORS = { RIGHT => :cursor_h, LEFT => :cursor_h, DOWN => :cursor_v, UP => :cursor_v }.freeze
|
|
120
120
|
|
|
121
|
+
# The CBM-II's keys for the characters a host key types: those it
|
|
122
|
+
# types without SHIFT, then those it types with it, as the KERNAL's
|
|
123
|
+
# key tables place them. ← is on _ and `, and SHIFT turns it into \.
|
|
124
|
+
CBM2_CHARACTERS = {
|
|
125
|
+
"1" => :"1", "2" => :"2", "3" => :"3", "4" => :"4", "5" => :"5", "6" => :"6", "7" => :"7", "8" => :"8",
|
|
126
|
+
"9" => :"9", "0" => :"0", "-" => :-, "=" => :"=", "[" => :"[", "]" => :"]", ";" => :";", "'" => :"'",
|
|
127
|
+
"," => :",", "." => :".", "/" => :/, "_" => :left_arrow, "`" => :left_arrow
|
|
128
|
+
}.freeze
|
|
129
|
+
|
|
130
|
+
CBM2_SHIFTED = {
|
|
131
|
+
"!" => :"1", "@" => :"2", "#" => :"3", "$" => :"4", "%" => :"5", "^" => :"6", "&" => :"7", "*" => :"8",
|
|
132
|
+
"(" => :"9", ")" => :"0", "+" => :"=", ":" => :";", "\"" => :"'", "<" => :",", ">" => :".", "?" => :/,
|
|
133
|
+
"\\" => :left_arrow
|
|
134
|
+
}.freeze
|
|
135
|
+
|
|
136
|
+
# The CBM-II's keys that don't type a character, which keep the
|
|
137
|
+
# host's SHIFT: RETURN, INST/DEL on Backspace, CLR/HOME on Home, ESC,
|
|
138
|
+
# TAB, RUN/STOP on F12, CTRL on Control, the four cursor keys on the
|
|
139
|
+
# arrows, F1-F8 on F1-F8, and the keypad on the host's.
|
|
140
|
+
CBM2_KEYS = {
|
|
141
|
+
40 => :return, 42 => :delete, 44 => :space, 74 => :clr_home, 41 => :escape, 43 => :tab, 69 => :run_stop,
|
|
142
|
+
224 => :control, 228 => :control, 79 => :cursor_right, 80 => :cursor_left, 81 => :cursor_down,
|
|
143
|
+
82 => :cursor_up, 58 => :f1, 59 => :f2, 60 => :f3, 61 => :f4, 62 => :f5, 63 => :f6, 64 => :f7, 65 => :f8,
|
|
144
|
+
84 => :kp_divide, 85 => :kp_multiply, 86 => :kp_minus, 87 => :kp_plus, 88 => :kp_enter, 89 => :kp1,
|
|
145
|
+
90 => :kp2, 91 => :kp3, 92 => :kp4, 93 => :kp5, 94 => :kp6, 95 => :kp7, 96 => :kp8, 97 => :kp9,
|
|
146
|
+
98 => :kp0, 99 => :kp_period
|
|
147
|
+
}.freeze
|
|
148
|
+
|
|
149
|
+
# The CBM-II's keys that Alt and a host key press: C= on C, CE on E,
|
|
150
|
+
# NORM/GRAPH on G, OFF/RVS on R, π on P, 00 on 0, and F9 and F10 on
|
|
151
|
+
# F9 and F10, which open the pause menu and mute without Alt.
|
|
152
|
+
CBM2_ALT_KEYS = {
|
|
153
|
+
6 => :cbm, 8 => :ce, 10 => :norm_graph, 21 => :off_rvs, 19 => :pi, 39 => :kp00, 66 => :f9, 67 => :f10
|
|
154
|
+
}.freeze
|
|
155
|
+
|
|
156
|
+
# The KIM-1's keys, by the names KIM1::Panel::KEYS gives them, with
|
|
157
|
+
# ST, RS and the SST switch: the hex keys on the digits, the keypad's
|
|
158
|
+
# and A-F, AD on comma, DA on period, + on space, = and the keypad's
|
|
159
|
+
# +, GO on G, Return and Enter, PC on P, ST on S and Esc, RS on R and
|
|
160
|
+
# SST on T.
|
|
161
|
+
KIM1_KEYS = {
|
|
162
|
+
4 => "A", 5 => "B", 6 => "C", 7 => "D", 8 => "E", 9 => "F", 30 => "1", 31 => "2", 32 => "3", 33 => "4",
|
|
163
|
+
34 => "5", 35 => "6", 36 => "7", 37 => "8", 38 => "9", 39 => "0", 89 => "1", 90 => "2", 91 => "3",
|
|
164
|
+
92 => "4", 93 => "5", 94 => "6", 95 => "7", 96 => "8", 97 => "9", 98 => "0", 54 => "AD", 55 => "DA",
|
|
165
|
+
44 => "+", 46 => "+", 87 => "+", 10 => "GO", 40 => "GO", 88 => "GO", 19 => "PC", 22 => "ST", 41 => "ST",
|
|
166
|
+
21 => "RS", 23 => "SST"
|
|
167
|
+
}.freeze
|
|
168
|
+
|
|
121
169
|
ARROWS = { 44 => :fire, 228 => :fire, 79 => :right, 80 => :left, 81 => :down, 82 => :up }.freeze
|
|
122
170
|
WASD = { 225 => :fire, 7 => :right, 4 => :left, 22 => :down, 26 => :up }.freeze
|
|
123
171
|
|
|
@@ -177,6 +225,30 @@ module Badline
|
|
|
177
225
|
shifted.nil? ? %i[none host] : [shifted, :on]
|
|
178
226
|
end
|
|
179
227
|
|
|
228
|
+
# The CBM-II key for the scancode, with the host's SHIFT down if
|
|
229
|
+
# +shift+, and how the CBM-II's SHIFT goes with it, as pet_key gives
|
|
230
|
+
# them.
|
|
231
|
+
def self.cbm2_key(scancode, shift)
|
|
232
|
+
return [LETTERS[scancode - 4], :host] if scancode.between?(4, 29)
|
|
233
|
+
|
|
234
|
+
key = CBM2_KEYS[scancode]
|
|
235
|
+
return [key, :host] unless key.nil?
|
|
236
|
+
|
|
237
|
+
characters = US_CHARACTERS[scancode]
|
|
238
|
+
return %i[none host] if characters.nil?
|
|
239
|
+
|
|
240
|
+
character = characters[shift ? 1 : 0]
|
|
241
|
+
unshifted = CBM2_CHARACTERS[character]
|
|
242
|
+
return [unshifted, :off] unless unshifted.nil?
|
|
243
|
+
|
|
244
|
+
shifted = CBM2_SHIFTED[character]
|
|
245
|
+
shifted.nil? ? %i[none host] : [shifted, :on]
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
# The CBM-II key Alt and the scancode press, which keeps the host's
|
|
249
|
+
# SHIFT, or :none.
|
|
250
|
+
def self.cbm2_alt_key(scancode) = [CBM2_ALT_KEYS[scancode] || :none, :host]
|
|
251
|
+
|
|
180
252
|
# The character the key types on a US layout: a letter, a capital
|
|
181
253
|
# with `shift`, or a digit, and "" for any other key.
|
|
182
254
|
def self.character(scancode, shift)
|
|
@@ -190,6 +262,29 @@ module Badline
|
|
|
190
262
|
end
|
|
191
263
|
end
|
|
192
264
|
|
|
265
|
+
# What the KIM-1's teletype sends for the keys that type no
|
|
266
|
+
# character: RETURN, RUBOUT on Backspace and Delete, and LINE FEED on
|
|
267
|
+
# Down.
|
|
268
|
+
KIM1_TELETYPE_KEYS = { 40 => "\r", 88 => "\r", 42 => "\x7f", 76 => "\x7f", 81 => "\n" }.freeze
|
|
269
|
+
|
|
270
|
+
# What the KIM-1's teletype sends for the scancode, with the host's
|
|
271
|
+
# SHIFT down if `shift`: a key of KIM1_TELETYPE_KEYS, or the
|
|
272
|
+
# character the key types on a US layout, or an empty string.
|
|
273
|
+
def self.kim1_teletype(scancode, shift)
|
|
274
|
+
sent = KIM1_TELETYPE_KEYS[scancode]
|
|
275
|
+
return sent unless sent.nil?
|
|
276
|
+
return " " if scancode == SPACE
|
|
277
|
+
return character(scancode, shift) if scancode.between?(4, 29)
|
|
278
|
+
|
|
279
|
+
characters = US_CHARACTERS[scancode]
|
|
280
|
+
return "" if characters.nil?
|
|
281
|
+
|
|
282
|
+
characters[shift ? 1 : 0]
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
# The KIM-1 key for the scancode, or an empty string.
|
|
286
|
+
def self.kim1_key(scancode) = KIM1_KEYS[scancode] || ""
|
|
287
|
+
|
|
193
288
|
def self.arrows(scancode) = ARROWS[scancode] || :none
|
|
194
289
|
|
|
195
290
|
def self.wasd(scancode) = WASD[scancode] || :none
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
module Frontend
|
|
5
|
+
# The KIM-1's panel as the window shows it, in place of a video chip's
|
|
6
|
+
# picture: the six LED digits, the keypad laid out as the board's
|
|
7
|
+
# (User Manual, figure 3.6), the SST switch, and the host keys that
|
|
8
|
+
# press each. It answers what a Screen reads of a video chip: a palette
|
|
9
|
+
# index a pixel, the lines changed, and the palette.
|
|
10
|
+
#
|
|
11
|
+
# Each frame #follow takes the cycles each segment was lit
|
|
12
|
+
# (KIM1::Panel#lit_cycles). A segment lit FULL_DUTY of the time, as
|
|
13
|
+
# the monitor lights each digit, shows at full brightness, and one lit
|
|
14
|
+
# less dimmer, through a square root, as the eye sees it. The level
|
|
15
|
+
# follows over SMOOTHING_CYCLES, the eye's persistence, so the frames'
|
|
16
|
+
# beat against the program's scan doesn't flicker.
|
|
17
|
+
class KIM1Panel
|
|
18
|
+
WIDTH = 384
|
|
19
|
+
HEIGHT = 272
|
|
20
|
+
|
|
21
|
+
BOARD = 0
|
|
22
|
+
LED = 1
|
|
23
|
+
LED_LEVELS = 16
|
|
24
|
+
WINDOW = 17
|
|
25
|
+
KEY = 18
|
|
26
|
+
KEY_HELD = 19
|
|
27
|
+
KEY_TEXT = 20
|
|
28
|
+
HINT = 21
|
|
29
|
+
TITLE = 22
|
|
30
|
+
SWITCH_ON = 23
|
|
31
|
+
|
|
32
|
+
# The board's green, the LED segments from unlit to full, the black
|
|
33
|
+
# behind them, the keys, and the text.
|
|
34
|
+
PALETTE = ([0x123c22] + Array.new(LED_LEVELS) do |level|
|
|
35
|
+
red = 0x2c + (((0xff - 0x2c) * level) / (LED_LEVELS - 1))
|
|
36
|
+
rest = 0x08 + ((0x28 * level) / (LED_LEVELS - 1))
|
|
37
|
+
(red << 16) | (rest << 8) | rest
|
|
38
|
+
end + [0x000000, 0xe8e0c8, 0x8c8470, 0x101010, 0xa8c8b0, 0xffffff, 0xffd040] +
|
|
39
|
+
Array.new(8, 0)).freeze
|
|
40
|
+
|
|
41
|
+
FULL_DUTY = 0.15
|
|
42
|
+
SMOOTHING_CYCLES = 40_000.0
|
|
43
|
+
|
|
44
|
+
# Where the digits go: the left edge of each, and their top.
|
|
45
|
+
DIGIT_LEFT = [56, 100, 144, 188, 252, 296].freeze
|
|
46
|
+
DIGIT_TOP = 30
|
|
47
|
+
THICKNESS = 4
|
|
48
|
+
LENGTH = 20
|
|
49
|
+
|
|
50
|
+
# Each segment's place in a digit, a to g: left, top, width, height.
|
|
51
|
+
SEGMENTS = [
|
|
52
|
+
[THICKNESS, 0, LENGTH, THICKNESS],
|
|
53
|
+
[THICKNESS + LENGTH, THICKNESS, THICKNESS, LENGTH],
|
|
54
|
+
[THICKNESS + LENGTH, (2 * THICKNESS) + LENGTH, THICKNESS, LENGTH],
|
|
55
|
+
[THICKNESS, (2 * THICKNESS) + (2 * LENGTH), LENGTH, THICKNESS],
|
|
56
|
+
[0, (2 * THICKNESS) + LENGTH, THICKNESS, LENGTH],
|
|
57
|
+
[0, THICKNESS, THICKNESS, LENGTH],
|
|
58
|
+
[THICKNESS, THICKNESS + LENGTH, LENGTH, THICKNESS]
|
|
59
|
+
].freeze
|
|
60
|
+
|
|
61
|
+
# The keypad, top row first, as on the board, with the SST switch at
|
|
62
|
+
# the end of the first row.
|
|
63
|
+
KEYPAD = [%w[GO ST RS SST], %w[AD DA PC +], %w[C D E F], %w[8 9 A B], %w[4 5 6 7], %w[0 1 2 3]].freeze
|
|
64
|
+
KEYPAD_LEFT = 24
|
|
65
|
+
KEYPAD_TOP = 100
|
|
66
|
+
KEY_WIDTH = 40
|
|
67
|
+
KEY_HEIGHT = 22
|
|
68
|
+
KEY_GAP = 6
|
|
69
|
+
|
|
70
|
+
# The host keys that press the panel's.
|
|
71
|
+
HINTS = ["0-9, A-F: HEX", "COMMA: AD", "PERIOD: DA", "SPACE, +: +", "G, RETURN: GO", "P: PC",
|
|
72
|
+
"S, ESC: ST", "R: RS", "T: SST", "CLICK: KEY", "F9: MENU"].freeze
|
|
73
|
+
HINT_LEFT = 216
|
|
74
|
+
HINT_TOP = 104
|
|
75
|
+
HINT_LINE = 13
|
|
76
|
+
|
|
77
|
+
attr_reader :display, :dirty_lines
|
|
78
|
+
|
|
79
|
+
def initialize
|
|
80
|
+
@display = Array.new(WIDTH * HEIGHT, BOARD)
|
|
81
|
+
@dirty_lines = Array.new(HEIGHT, true)
|
|
82
|
+
@font = ROM.read("character.rom")
|
|
83
|
+
@brightness = Array.new(KIM1::Panel::DIGITS * 7, 0.0)
|
|
84
|
+
@levels = Array.new(KIM1::Panel::DIGITS * 7, -1)
|
|
85
|
+
@held = ""
|
|
86
|
+
@sst = false
|
|
87
|
+
@cycles = 0
|
|
88
|
+
draw_board
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def width = WIDTH
|
|
92
|
+
|
|
93
|
+
def height = HEIGHT
|
|
94
|
+
|
|
95
|
+
def palette = PALETTE
|
|
96
|
+
|
|
97
|
+
# The whole picture: the window has nothing to crop.
|
|
98
|
+
def crop = [0, 0, WIDTH, HEIGHT]
|
|
99
|
+
|
|
100
|
+
def clear_dirty_lines!
|
|
101
|
+
@dirty_lines.map! { false }
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# Brings the picture up to the KIM-1 `computer`: the digits from the
|
|
105
|
+
# segments' lit cycles since the last frame, and the key held and
|
|
106
|
+
# the SST switch.
|
|
107
|
+
def follow(computer)
|
|
108
|
+
cycles = computer.cycles
|
|
109
|
+
elapsed = cycles - @cycles
|
|
110
|
+
@cycles = cycles
|
|
111
|
+
lit = computer.panel.lit_cycles
|
|
112
|
+
light(lit, elapsed) if elapsed.positive?
|
|
113
|
+
held = computer.panel.held.to_s
|
|
114
|
+
draw_keys(held, computer.sst) if held != @held || computer.sst != @sst
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# The name of the panel's key at the window pixel `left`, `top`, as
|
|
118
|
+
# KIM1::Panel::KEYS names it, ST, RS or SST, or an empty string.
|
|
119
|
+
def key_at(left, top)
|
|
120
|
+
across = left - KEYPAD_LEFT
|
|
121
|
+
down = top - KEYPAD_TOP
|
|
122
|
+
column = across / (KEY_WIDTH + KEY_GAP)
|
|
123
|
+
row = down / (KEY_HEIGHT + KEY_GAP)
|
|
124
|
+
return "" if across.negative? || down.negative? || column > 3 || row >= KEYPAD.size
|
|
125
|
+
return "" if across % (KEY_WIDTH + KEY_GAP) >= KEY_WIDTH || down % (KEY_HEIGHT + KEY_GAP) >= KEY_HEIGHT
|
|
126
|
+
|
|
127
|
+
KEYPAD[row][column]
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
# The brightness, 0 to 1, of each segment, seven to a digit.
|
|
131
|
+
def brightness = @brightness.dup
|
|
132
|
+
|
|
133
|
+
private
|
|
134
|
+
|
|
135
|
+
def light(lit, elapsed)
|
|
136
|
+
smoothing = 1.0 - Math.exp(-elapsed / SMOOTHING_CYCLES)
|
|
137
|
+
lit.each_with_index do |cycles, index|
|
|
138
|
+
duty = cycles.fdiv(elapsed) / FULL_DUTY
|
|
139
|
+
target = Math.sqrt([duty, 1.0].min)
|
|
140
|
+
level = @brightness[index] + (smoothing * (target - @brightness[index]))
|
|
141
|
+
@brightness[index] = level
|
|
142
|
+
shade = (level * (LED_LEVELS - 1)).round
|
|
143
|
+
next if shade == @levels[index]
|
|
144
|
+
|
|
145
|
+
@levels[index] = shade
|
|
146
|
+
draw_segment(index / 7, index % 7, LED + shade)
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def draw_board
|
|
151
|
+
text(16, 8, "MOS KIM-1", TITLE)
|
|
152
|
+
box(40, 22, 304, 64, WINDOW)
|
|
153
|
+
DIGIT_LEFT.each_index { |digit| 7.times { |segment| draw_segment(digit, segment, LED) } }
|
|
154
|
+
@levels.map! { 0 }
|
|
155
|
+
HINTS.each_with_index { |hint, line| text(HINT_LEFT, HINT_TOP + (line * HINT_LINE), hint, HINT) }
|
|
156
|
+
draw_keys("", false)
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def draw_segment(digit, segment, color)
|
|
160
|
+
place = SEGMENTS[segment]
|
|
161
|
+
box(DIGIT_LEFT[digit] + place[0], DIGIT_TOP + place[1], place[2], place[3], color)
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def draw_keys(held, sst)
|
|
165
|
+
@held = held
|
|
166
|
+
@sst = sst
|
|
167
|
+
KEYPAD.each_with_index do |names, row|
|
|
168
|
+
names.each_with_index do |name, column|
|
|
169
|
+
on = name == "SST" ? sst : name == held
|
|
170
|
+
draw_key(KEYPAD_LEFT + (column * (KEY_WIDTH + KEY_GAP)), KEYPAD_TOP + (row * (KEY_HEIGHT + KEY_GAP)),
|
|
171
|
+
name, on)
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
def draw_key(left, top, name, on)
|
|
177
|
+
face = on ? KEY_HELD : KEY
|
|
178
|
+
face = on ? SWITCH_ON : KEY if name == "SST"
|
|
179
|
+
box(left, top, KEY_WIDTH, KEY_HEIGHT, face)
|
|
180
|
+
text(left + ((KEY_WIDTH - (name.size * 8)) / 2), top + ((KEY_HEIGHT - 8) / 2), name, KEY_TEXT)
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def box(left, top, width, height, color)
|
|
184
|
+
height.times do |row|
|
|
185
|
+
line = top + row
|
|
186
|
+
@display.fill(color, (line * WIDTH) + left, width)
|
|
187
|
+
@dirty_lines[line] = true
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
# Draws `string` from `left`, `top` in the character ROM's second set.
|
|
192
|
+
def text(left, top, string, color)
|
|
193
|
+
string.bytes.each_with_index do |byte, index|
|
|
194
|
+
glyph(left + (index * 8), top, Painter::SCREEN_CODES[byte & 0x7f], color)
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
def glyph(left, top, code, color)
|
|
199
|
+
8.times do |row|
|
|
200
|
+
bits = @font[0x800 + (code * 8) + row]
|
|
201
|
+
line = top + row
|
|
202
|
+
8.times { |dot| @display[(line * WIDTH) + left + dot] = color if bits.anybits?(0x80 >> dot) }
|
|
203
|
+
@dirty_lines[line] = true
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
end
|
|
@@ -26,6 +26,13 @@ module Badline
|
|
|
26
26
|
path.empty? ? path : File.expand_path(path)
|
|
27
27
|
end
|
|
28
28
|
|
|
29
|
+
# The model of the true drive on device 8, or an empty one for the
|
|
30
|
+
# traps.
|
|
31
|
+
def drive_model(computer)
|
|
32
|
+
drive = Media::TrueDrive.drive(computer)
|
|
33
|
+
drive.nil? ? "" : drive.model_name
|
|
34
|
+
end
|
|
35
|
+
|
|
29
36
|
def eject_disk(computer)
|
|
30
37
|
drive = Media::TrueDrive.drive(computer)
|
|
31
38
|
drive.nil? ? computer.unmount : drive.insert(nil)
|
|
@@ -35,7 +42,7 @@ module Badline
|
|
|
35
42
|
# either cartridge's slots.
|
|
36
43
|
def cartridge?(computer)
|
|
37
44
|
return !computer.cartridge_slots.empty? if computer.family == :plus4
|
|
38
|
-
return false if computer.family
|
|
45
|
+
return false if %i[pet cbm2 kim1].include?(computer.family)
|
|
39
46
|
|
|
40
47
|
!computer.address_bus.cartridge.nil?
|
|
41
48
|
end
|