badline 0.11.1 → 0.12.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 +18 -0
- data/CLAUDE.md +8 -7
- data/CONTRIBUTING.md +7 -6
- data/README.md +12 -5
- data/doc/media.md +28 -1
- data/doc/pinned-behaviour.md +31 -1
- data/exe/badline-ruby +1 -1
- data/lib/badline/c128/bus.rb +4 -17
- data/lib/badline/c128/drives.rb +35 -22
- data/lib/badline/c128/fast_serial.rb +1 -0
- data/lib/badline/c128/io_pages.rb +35 -0
- data/lib/badline/c128/mmu.rb +9 -7
- data/lib/badline/c128/modes.rb +17 -4
- data/lib/badline/c128/saved_state.rb +22 -0
- data/lib/badline/c128/z80.rb +49 -0
- data/lib/badline/c128/z80_pages.rb +118 -0
- data/lib/badline/c128/z80_turns.rb +106 -0
- data/lib/badline/c128.rb +16 -19
- data/lib/badline/cartridge/action_replay.rb +2 -2
- data/lib/badline/cartridge/comal80.rb +1 -7
- data/lib/badline/cartridge/dinamic.rb +0 -8
- data/lib/badline/cartridge/final_cartridge3.rb +6 -6
- data/lib/badline/cartridge/fun_play.rb +0 -8
- data/lib/badline/cartridge/game_system.rb +0 -8
- data/lib/badline/cartridge/kcs_power.rb +10 -7
- data/lib/badline/cartridge/mach5.rb +2 -4
- data/lib/badline/cartridge/magic_desk.rb +0 -8
- data/lib/badline/cartridge/ocean.rb +0 -8
- data/lib/badline/cartridge/pagefox.rb +2 -2
- data/lib/badline/cartridge/retro_replay.rb +7 -6
- data/lib/badline/cartridge/rex_utility.rb +2 -6
- data/lib/badline/cartridge/rgcd.rb +2 -2
- data/lib/badline/cartridge/simons_basic.rb +0 -5
- data/lib/badline/cartridge/super_games.rb +4 -2
- data/lib/badline/cartridge/westermann.rb +2 -6
- data/lib/badline/cartridge.rb +37 -9
- data/lib/badline/cia/fast_forward.rb +21 -3
- data/lib/badline/cia.rb +8 -5
- data/lib/badline/computer/attachments.rb +4 -56
- data/lib/badline/computer/true_drives.rb +80 -0
- data/lib/badline/computer.rb +4 -0
- data/lib/badline/core.rb +2 -0
- data/lib/badline/drive/core.rb +8 -33
- data/lib/badline/drive/fast_serial.rb +73 -0
- data/lib/badline/drive/idle.rb +1 -11
- data/lib/badline/drive/orbit.rb +2 -5
- data/lib/badline/drive/sleep.rb +0 -2
- data/lib/badline/drive/vias.rb +84 -0
- data/lib/badline/drive.rb +2 -0
- data/lib/badline/drive1541.rb +1 -0
- data/lib/badline/drive1571/wd1770.rb +94 -8
- data/lib/badline/drive1571.rb +8 -58
- data/lib/badline/drive1581/bus.rb +137 -0
- data/lib/badline/drive1581/cpu.rb +11 -0
- data/lib/badline/drive1581/disk.rb +135 -0
- data/lib/badline/drive1581/disk_state.rb +87 -0
- data/lib/badline/drive1581/mechanism.rb +117 -0
- data/lib/badline/drive1581/serial_port.rb +52 -0
- data/lib/badline/drive1581/slot.rb +78 -0
- data/lib/badline/drive1581/track.rb +100 -0
- data/lib/badline/drive1581/track_bytes.rb +75 -0
- data/lib/badline/drive1581/wd1772/registers.rb +89 -0
- data/lib/badline/drive1581/wd1772/sector_writes.rb +73 -0
- data/lib/badline/drive1581/wd1772/sectors.rb +150 -0
- data/lib/badline/drive1581/wd1772/state.rb +66 -0
- data/lib/badline/drive1581/wd1772/stepping.rb +109 -0
- data/lib/badline/drive1581/wd1772/tracks.rb +125 -0
- data/lib/badline/drive1581/wd1772/turn.rb +73 -0
- data/lib/badline/drive1581/wd1772.rb +216 -0
- data/lib/badline/drive1581.rb +233 -0
- data/lib/badline/frontend/app.rb +1 -3
- data/lib/badline/frontend/audio_device.rb +67 -0
- data/lib/badline/frontend/audio_sink.rb +16 -50
- data/lib/badline/frontend/boot.rb +18 -10
- data/lib/badline/frontend/buttons.rb +7 -7
- data/lib/badline/frontend/keys.rb +33 -8
- data/lib/badline/frontend/media_slots.rb +42 -0
- data/lib/badline/frontend/{confirmation.rb → menu/confirmation.rb} +4 -4
- data/lib/badline/frontend/{file_browser.rb → menu/file_browser.rb} +18 -34
- data/lib/badline/frontend/{menu_dialogs.rb → menu/menu_dialogs.rb} +3 -7
- data/lib/badline/frontend/{menu_media.rb → menu/menu_media.rb} +26 -51
- data/lib/badline/frontend/{menu_pages.rb → menu/menu_pages.rb} +5 -8
- data/lib/badline/frontend/{name_field.rb → menu/name_field.rb} +11 -24
- data/lib/badline/frontend/{pause_menu.rb → menu/pause_menu.rb} +13 -28
- data/lib/badline/frontend/menu/theme.rb +18 -0
- data/lib/badline/frontend/{filter_view.rb → player/filter_view.rb} +4 -4
- data/lib/badline/frontend/{info_view.rb → player/info_view.rb} +3 -3
- data/lib/badline/frontend/{player_window.rb → player/player_controller.rb} +17 -25
- data/lib/badline/frontend/{player_footer.rb → player/player_footer.rb} +8 -8
- data/lib/badline/frontend/{player_header.rb → player/player_header.rb} +5 -5
- data/lib/badline/frontend/{player_screen.rb → player/player_screen.rb} +4 -5
- data/lib/badline/frontend/{scope.rb → player/scope.rb} +1 -1
- data/lib/badline/frontend/{sid_view.rb → player/sid_view.rb} +16 -20
- data/lib/badline/frontend/player/theme.rb +18 -0
- data/lib/badline/frontend/{visualizer_view.rb → player/visualizer_view.rb} +8 -8
- data/lib/badline/frontend/sound.rb +19 -38
- data/lib/badline/frontend/timeline.rb +3 -14
- data/lib/badline/frontend.rb +24 -20
- data/lib/badline/iec_bus.rb +14 -2
- data/lib/badline/machine.rb +5 -4
- data/lib/badline/media/boot_disk.rb +50 -0
- data/lib/badline/media/not_disk.rb +8 -0
- data/lib/badline/media/true_drive.rb +35 -15
- data/lib/badline/media.rb +9 -3
- data/lib/badline/options/event.rb +2 -2
- data/lib/badline/options/family.rb +3 -9
- data/lib/badline/options/help.rb +2 -1
- data/lib/badline/roms/README +9 -0
- data/lib/badline/roms/dos1581.rom +0 -0
- data/lib/badline/sdl.rb +37 -20
- data/lib/badline/snapshot/state.rb +5 -4
- data/lib/badline/storage/p00.rb +8 -0
- data/lib/badline/time_of_day.rb +3 -3
- data/lib/badline/version.rb +1 -1
- data/lib/badline/vic20/attachments.rb +21 -7
- data/lib/badline/vic20/saved_state.rb +2 -0
- data/lib/badline/vic20.rb +3 -0
- data/lib/badline/z80/core.rb +2 -0
- data/lib/badline/z80/saved_state.rb +69 -0
- metadata +54 -21
- /data/lib/badline/frontend/{menu_ports.rb → menu/menu_ports.rb} +0 -0
- /data/lib/badline/frontend/{player_commands.rb → player/player_commands.rb} +0 -0
- /data/lib/badline/frontend/{player_state.rb → player/player_state.rb} +0 -0
- /data/lib/badline/frontend/{sid_history.rb → player/sid_history.rb} +0 -0
|
@@ -6,7 +6,27 @@ module Badline
|
|
|
6
6
|
# keys, the C128's own keys and, in joystick mode, the joystick
|
|
7
7
|
# directions.
|
|
8
8
|
module Keys
|
|
9
|
+
RETURN = 40
|
|
10
|
+
ESCAPE = 41
|
|
11
|
+
BACKSPACE = 42
|
|
9
12
|
TAB = 43
|
|
13
|
+
SPACE = 44
|
|
14
|
+
F8 = 65
|
|
15
|
+
F9 = 66
|
|
16
|
+
F10 = 67
|
|
17
|
+
F11 = 68
|
|
18
|
+
F12 = 69
|
|
19
|
+
HOME = 74
|
|
20
|
+
PAGE_UP = 75
|
|
21
|
+
END_KEY = 77
|
|
22
|
+
PAGE_DOWN = 78
|
|
23
|
+
RIGHT = 79
|
|
24
|
+
LEFT = 80
|
|
25
|
+
DOWN = 81
|
|
26
|
+
UP = 82
|
|
27
|
+
|
|
28
|
+
# The arrow keys, which move through the menus.
|
|
29
|
+
DIRECTIONS = { RIGHT => :right, LEFT => :left, DOWN => :down, UP => :up }.freeze
|
|
10
30
|
|
|
11
31
|
LETTERS = %i[a b c d e f g h i j k l m n o p q r s t u v w x y z].freeze
|
|
12
32
|
DIGITS = %i[1 2 3 4 5 6 7 8 9 0].freeze
|
|
@@ -36,14 +56,6 @@ module Badline
|
|
|
36
56
|
63 => :forty_eighty, 72 => :forty_eighty, 78 => :run_stop
|
|
37
57
|
}.freeze
|
|
38
58
|
|
|
39
|
-
ESCAPE = 41
|
|
40
|
-
|
|
41
|
-
F8 = 65
|
|
42
|
-
F9 = 66
|
|
43
|
-
F10 = 67
|
|
44
|
-
F11 = 68
|
|
45
|
-
F12 = 69
|
|
46
|
-
|
|
47
59
|
ARROWS = { 44 => :fire, 228 => :fire, 79 => :right, 80 => :left, 81 => :down, 82 => :up }.freeze
|
|
48
60
|
WASD = { 225 => :fire, 7 => :right, 4 => :left, 22 => :down, 26 => :up }.freeze
|
|
49
61
|
|
|
@@ -65,6 +77,19 @@ module Badline
|
|
|
65
77
|
C128_OTHERS[scancode] || c64_key(scancode)
|
|
66
78
|
end
|
|
67
79
|
|
|
80
|
+
# The character the key types on a US layout: a letter, a capital
|
|
81
|
+
# with `shift`, or a digit, and "" for any other key.
|
|
82
|
+
def self.character(scancode, shift)
|
|
83
|
+
if scancode.between?(4, 29)
|
|
84
|
+
letter = (97 + scancode - 4).chr
|
|
85
|
+
shift ? letter.upcase : letter
|
|
86
|
+
elsif scancode.between?(30, 39)
|
|
87
|
+
"1234567890"[scancode - 30]
|
|
88
|
+
else
|
|
89
|
+
""
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
68
93
|
def self.arrows(scancode) = ARROWS[scancode] || :none
|
|
69
94
|
|
|
70
95
|
def self.wasd(scancode) = WASD[scancode] || :none
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
module Frontend
|
|
5
|
+
# The machine's media slots as the pause menu and the --at events
|
|
6
|
+
# both take media out of them: device 8, a true drive there or else
|
|
7
|
+
# the KERNAL traps, and the expansion port.
|
|
8
|
+
module MediaSlots
|
|
9
|
+
class << self
|
|
10
|
+
# Whether device 8 has a disk in.
|
|
11
|
+
def disk?(computer)
|
|
12
|
+
drive = Media::TrueDrive.drive(computer)
|
|
13
|
+
drive.nil? ? computer.mounted? : !drive.disk.nil?
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# The expanded path of the disk in device 8, however it went in, or
|
|
17
|
+
# an empty one.
|
|
18
|
+
def disk_path(computer)
|
|
19
|
+
drive = Media::TrueDrive.drive(computer)
|
|
20
|
+
unless drive.nil?
|
|
21
|
+
disk = drive.disk
|
|
22
|
+
return disk.nil? ? "" : disk.path.to_s
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
path = computer.mounted_path
|
|
26
|
+
path.empty? ? path : File.expand_path(path)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def eject_disk(computer)
|
|
30
|
+
drive = Media::TrueDrive.drive(computer)
|
|
31
|
+
drive.nil? ? computer.unmount : drive.insert(nil)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Takes the cartridge out with the power off.
|
|
35
|
+
def remove_cartridge(computer)
|
|
36
|
+
computer.address_bus.detach_cartridge
|
|
37
|
+
computer.power_cycle!
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -43,11 +43,11 @@ module Badline
|
|
|
43
43
|
|
|
44
44
|
def draw(left, top)
|
|
45
45
|
painter = @painter
|
|
46
|
-
painter.text(left, top, TITLES.fetch(@kind),
|
|
47
|
-
painter.text(left, top + 20, Painter.fit(File.basename(@path), 40),
|
|
46
|
+
painter.text(left, top, TITLES.fetch(@kind), MenuTheme::BRIGHT)
|
|
47
|
+
painter.text(left, top + 20, Painter.fit(File.basename(@path), 40), MenuTheme::TEXT) unless @path.empty?
|
|
48
48
|
warning = WARNINGS.fetch(@kind)
|
|
49
|
-
painter.text(left, top + 44, warning[0],
|
|
50
|
-
painter.text(left, top + 56, warning[1],
|
|
49
|
+
painter.text(left, top + 44, warning[0], MenuTheme::TEXT)
|
|
50
|
+
painter.text(left, top + 56, warning[1], MenuTheme::TEXT) unless warning[1].empty?
|
|
51
51
|
@buttons.row([left, top + 80, MenuPages::ROW_WIDTH], "CANCEL", :cancel)
|
|
52
52
|
@buttons.row([left, top + 80 + Buttons::HEIGHT + 2, MenuPages::ROW_WIDTH], ANSWERS.fetch(@kind), :confirm)
|
|
53
53
|
end
|
|
@@ -12,19 +12,6 @@ module Badline
|
|
|
12
12
|
ROWS = 12
|
|
13
13
|
COLUMNS = 40
|
|
14
14
|
|
|
15
|
-
RETURN = 40
|
|
16
|
-
ESCAPE = 41
|
|
17
|
-
BACKSPACE = 42
|
|
18
|
-
HOME = 74
|
|
19
|
-
PAGE_UP = 75
|
|
20
|
-
END_KEY = 77
|
|
21
|
-
PAGE_DOWN = 78
|
|
22
|
-
RIGHT = 79
|
|
23
|
-
LEFT = 80
|
|
24
|
-
DOWN = 81
|
|
25
|
-
UP = 82
|
|
26
|
-
DIGITS = "1234567890"
|
|
27
|
-
|
|
28
15
|
attr_reader :directory
|
|
29
16
|
|
|
30
17
|
def initialize(painter, buttons)
|
|
@@ -60,9 +47,9 @@ module Badline
|
|
|
60
47
|
# Handles a key, and returns the path of a file picked, :cancel when
|
|
61
48
|
# Esc leaves the browser, or nil.
|
|
62
49
|
def key(scancode)
|
|
63
|
-
return :cancel if scancode == ESCAPE
|
|
64
|
-
return pick if [RETURN, RIGHT].include?(scancode)
|
|
65
|
-
return up if [LEFT, BACKSPACE].include?(scancode)
|
|
50
|
+
return :cancel if scancode == Keys::ESCAPE
|
|
51
|
+
return pick if [Keys::RETURN, Keys::RIGHT].include?(scancode)
|
|
52
|
+
return up if [Keys::LEFT, Keys::BACKSPACE].include?(scancode)
|
|
66
53
|
|
|
67
54
|
select(@selected + step(scancode)) unless step(scancode).zero?
|
|
68
55
|
jump(scancode)
|
|
@@ -83,14 +70,14 @@ module Badline
|
|
|
83
70
|
end
|
|
84
71
|
|
|
85
72
|
def draw(left, top, width, title)
|
|
86
|
-
@painter.text(left, top, title,
|
|
87
|
-
@painter.text(left, top + ROW + 2, shown_directory,
|
|
73
|
+
@painter.text(left, top, title, MenuTheme::BRIGHT)
|
|
74
|
+
@painter.text(left, top + ROW + 2, shown_directory, MenuTheme::DIM)
|
|
88
75
|
@list_top = top + (ROW * 2) + 8
|
|
89
76
|
@buttons.area([left, @list_top, width, ROW * ROWS], :browse)
|
|
90
77
|
draw_rows(left, width)
|
|
91
78
|
draw_bar(left + width - 2) if @names.size > ROWS
|
|
92
79
|
@painter.text(left, @list_top + (ROW * ROWS) + 6, "RETURN: OPEN LEFT: BACK ESC: CANCEL",
|
|
93
|
-
|
|
80
|
+
MenuTheme::DIM)
|
|
94
81
|
end
|
|
95
82
|
|
|
96
83
|
private
|
|
@@ -109,10 +96,10 @@ module Badline
|
|
|
109
96
|
name = label(index)
|
|
110
97
|
top = @list_top + (row * ROW)
|
|
111
98
|
if index == @selected
|
|
112
|
-
@painter.box(left - 2, top - 2, width - 4, ROW,
|
|
113
|
-
@painter.text(left, top, name,
|
|
99
|
+
@painter.box(left - 2, top - 2, width - 4, ROW, MenuTheme::TEXT)
|
|
100
|
+
@painter.text(left, top, name, MenuTheme::PANEL)
|
|
114
101
|
else
|
|
115
|
-
@painter.text(left, top, name, index < @folders ?
|
|
102
|
+
@painter.text(left, top, name, index < @folders ? MenuTheme::BRIGHT : MenuTheme::TEXT)
|
|
116
103
|
end
|
|
117
104
|
end
|
|
118
105
|
end
|
|
@@ -121,8 +108,8 @@ module Badline
|
|
|
121
108
|
height = ROW * ROWS
|
|
122
109
|
size = [(height * ROWS) / @names.size, 4].max
|
|
123
110
|
offset = ((height - size) * @top) / [@names.size - ROWS, 1].max
|
|
124
|
-
@painter.box(left, @list_top - 2, 2, height,
|
|
125
|
-
@painter.box(left, @list_top - 2 + offset, 2, size,
|
|
111
|
+
@painter.box(left, @list_top - 2, 2, height, MenuTheme::EDGE)
|
|
112
|
+
@painter.box(left, @list_top - 2 + offset, 2, size, MenuTheme::TEXT)
|
|
126
113
|
end
|
|
127
114
|
|
|
128
115
|
def label(index)
|
|
@@ -133,12 +120,12 @@ module Badline
|
|
|
133
120
|
|
|
134
121
|
def step(scancode)
|
|
135
122
|
case scancode
|
|
136
|
-
when UP then -1
|
|
137
|
-
when DOWN then 1
|
|
138
|
-
when PAGE_UP then -ROWS
|
|
139
|
-
when PAGE_DOWN then ROWS
|
|
140
|
-
when HOME then -@names.size
|
|
141
|
-
when END_KEY then @names.size
|
|
123
|
+
when Keys::UP then -1
|
|
124
|
+
when Keys::DOWN then 1
|
|
125
|
+
when Keys::PAGE_UP then -ROWS
|
|
126
|
+
when Keys::PAGE_DOWN then ROWS
|
|
127
|
+
when Keys::HOME then -@names.size
|
|
128
|
+
when Keys::END_KEY then @names.size
|
|
142
129
|
else 0
|
|
143
130
|
end
|
|
144
131
|
end
|
|
@@ -154,10 +141,7 @@ module Badline
|
|
|
154
141
|
# Selects the next name after the one selected that starts with the
|
|
155
142
|
# key's letter or digit.
|
|
156
143
|
def jump(scancode)
|
|
157
|
-
char =
|
|
158
|
-
elsif scancode.between?(30, 39) then DIGITS[scancode - 30]
|
|
159
|
-
else ""
|
|
160
|
-
end
|
|
144
|
+
char = Keys.character(scancode, false)
|
|
161
145
|
return if char.empty? || @names.empty?
|
|
162
146
|
|
|
163
147
|
@names.size.times do |offset|
|
|
@@ -14,10 +14,6 @@ module Badline
|
|
|
14
14
|
TITLES = { disk: "INSERT A DISK", tape: "INSERT A TAPE", cartridge: "INSERT A CARTRIDGE",
|
|
15
15
|
program: "QUICK OPEN", snapshot: "LOAD A SAVE" }.freeze
|
|
16
16
|
|
|
17
|
-
ESCAPE = 41
|
|
18
|
-
RETURN = 40
|
|
19
|
-
SPACE = 44
|
|
20
|
-
|
|
21
17
|
# The machine Quick open started or a save loaded.
|
|
22
18
|
attr_reader :computer
|
|
23
19
|
|
|
@@ -78,10 +74,10 @@ module Badline
|
|
|
78
74
|
if @name_field.open? && !@confirmation.open?
|
|
79
75
|
return named(@name_field.key(scancode, SDL.event_mod(SDL.event).anybits?(SDL::KMOD_SHIFT)))
|
|
80
76
|
end
|
|
81
|
-
return answer(:cancel) if scancode == ESCAPE
|
|
82
|
-
return answer(@buttons.focus) if [RETURN, SPACE].include?(scancode)
|
|
77
|
+
return answer(:cancel) if scancode == Keys::ESCAPE
|
|
78
|
+
return answer(@buttons.focus) if [Keys::RETURN, Keys::SPACE].include?(scancode)
|
|
83
79
|
|
|
84
|
-
direction =
|
|
80
|
+
direction = Keys::DIRECTIONS[scancode]
|
|
85
81
|
@buttons.shift(direction == :up ? -1 : 1, 1) if %i[up down].include?(direction)
|
|
86
82
|
nil
|
|
87
83
|
end
|
|
@@ -25,40 +25,22 @@ module Badline
|
|
|
25
25
|
|
|
26
26
|
def writable? = @writable
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
# an empty one.
|
|
30
|
-
def disk_path
|
|
31
|
-
drive = @computer.true_drive
|
|
32
|
-
unless drive.nil?
|
|
33
|
-
disk = drive.disk
|
|
34
|
-
return disk.nil? ? "" : disk.path.to_s
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
path = @computer.mounted_path
|
|
38
|
-
path.empty? ? path : File.expand_path(path)
|
|
39
|
-
end
|
|
28
|
+
def disk_path = MediaSlots.disk_path(@computer)
|
|
40
29
|
|
|
41
|
-
def inserted?
|
|
42
|
-
drive = @computer.true_drive
|
|
43
|
-
drive.nil? ? @computer.mounted? : !drive.disk.nil?
|
|
44
|
-
end
|
|
30
|
+
def inserted? = MediaSlots.disk?(@computer)
|
|
45
31
|
|
|
46
32
|
# Puts a disk in device 8 or a tape in the datasette, `kind` :disk or
|
|
47
33
|
# :tape, and says whether it went in. A list puts its first disk in.
|
|
48
34
|
def insert(kind, path)
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
35
|
+
succeeds? do
|
|
36
|
+
if kind == :disk
|
|
37
|
+
disk = Media::DiskList.disk(path)
|
|
38
|
+
Media.insert_disk(@computer, disk, read_only: !@writable)
|
|
39
|
+
list(path)
|
|
40
|
+
else
|
|
41
|
+
@computer.datasette.insert(Storage::TAP.new(path))
|
|
42
|
+
end
|
|
56
43
|
end
|
|
57
|
-
true
|
|
58
|
-
rescue ArgumentError, SystemCallError, Storage::TAP::FormatError, Storage::T64::FormatError,
|
|
59
|
-
Storage::G64Image::FormatError => e
|
|
60
|
-
@problem = e.message
|
|
61
|
-
false
|
|
62
44
|
end
|
|
63
45
|
|
|
64
46
|
# Does what the Drive page's :eject_disk, :previous_disk, :next_disk,
|
|
@@ -72,10 +54,7 @@ module Badline
|
|
|
72
54
|
end
|
|
73
55
|
end
|
|
74
56
|
|
|
75
|
-
def eject_disk
|
|
76
|
-
drive = @computer.true_drive
|
|
77
|
-
drive.nil? ? @computer.unmount : drive.insert(nil)
|
|
78
|
-
end
|
|
57
|
+
def eject_disk = MediaSlots.eject_disk(@computer)
|
|
79
58
|
|
|
80
59
|
# Sets whether disks are writable, for the disk in the drive too,
|
|
81
60
|
# which goes in again as the notch now says.
|
|
@@ -111,12 +90,7 @@ module Badline
|
|
|
111
90
|
# Swaps the cartridge for the one at `path`, which power cycles the
|
|
112
91
|
# machine, and says whether it went in.
|
|
113
92
|
def attach_cartridge(path)
|
|
114
|
-
@
|
|
115
|
-
Media.attach(@computer, path)
|
|
116
|
-
true
|
|
117
|
-
rescue ArgumentError, SystemCallError, Storage::CRTFile::FormatError, Cartridge::UnsupportedTypeError => e
|
|
118
|
-
@problem = e.message
|
|
119
|
-
false
|
|
93
|
+
succeeds? { Media.attach(@computer, path) }
|
|
120
94
|
end
|
|
121
95
|
|
|
122
96
|
# What a save of the machine is named after: the cartridge, the disk,
|
|
@@ -147,27 +121,28 @@ module Badline
|
|
|
147
121
|
end
|
|
148
122
|
|
|
149
123
|
# Takes the cartridge out with the power off.
|
|
150
|
-
def remove_cartridge
|
|
151
|
-
@computer.address_bus.detach_cartridge
|
|
152
|
-
@computer.power_cycle!
|
|
153
|
-
end
|
|
124
|
+
def remove_cartridge = MediaSlots.remove_cartridge(@computer)
|
|
154
125
|
|
|
155
126
|
# A new machine started on `path`, as the command line would, or nil
|
|
156
127
|
# when it won't start.
|
|
157
128
|
def start(options, path)
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
Storage::G64Image::FormatError, Cartridge::UnsupportedTypeError => e
|
|
165
|
-
@problem = e.message
|
|
166
|
-
nil
|
|
129
|
+
computer = nil
|
|
130
|
+
started = succeeds? do
|
|
131
|
+
computer = Frontend.start(options, path, @writable)
|
|
132
|
+
list(path)
|
|
133
|
+
end
|
|
134
|
+
started ? computer : nil
|
|
167
135
|
end
|
|
168
136
|
|
|
169
137
|
private
|
|
170
138
|
|
|
139
|
+
# Runs the block, keeps the media error it raised as the problem, and
|
|
140
|
+
# says whether there was none.
|
|
141
|
+
def succeeds?(&)
|
|
142
|
+
@problem = Frontend.media_problem(&)
|
|
143
|
+
@problem.empty?
|
|
144
|
+
end
|
|
145
|
+
|
|
171
146
|
# Keeps `path` as the list the set comes from, if it's a list.
|
|
172
147
|
def list(path)
|
|
173
148
|
return unless Media::DiskList.list?(path)
|
|
@@ -9,13 +9,10 @@ module Badline
|
|
|
9
9
|
class MenuPages
|
|
10
10
|
include MenuPorts
|
|
11
11
|
|
|
12
|
-
TEXT = PauseMenu::TEXT
|
|
13
|
-
DIM = PauseMenu::DIM
|
|
14
12
|
LINE = 12
|
|
15
13
|
COLUMNS = 25
|
|
16
14
|
ROW_WIDTH = 200
|
|
17
15
|
|
|
18
|
-
WARNING = 0xff7a6b
|
|
19
16
|
RECENT = %i[recent0 recent1 recent2 recent3 recent4 recent5 recent6 recent7].freeze
|
|
20
17
|
|
|
21
18
|
SID_MODELS = %i[mos6581 mos8580].freeze
|
|
@@ -51,7 +48,7 @@ module Badline
|
|
|
51
48
|
|
|
52
49
|
def draw(page, left, top)
|
|
53
50
|
problem = @media.problem
|
|
54
|
-
@painter.text(left, top + 186, problem[0, COLUMNS], WARNING) unless problem.empty?
|
|
51
|
+
@painter.text(left, top + 186, problem[0, COLUMNS], MenuTheme::WARNING) unless problem.empty?
|
|
55
52
|
@left = left
|
|
56
53
|
@top = top
|
|
57
54
|
case page
|
|
@@ -95,7 +92,7 @@ module Badline
|
|
|
95
92
|
# Says in the dim colour that the VIC-20 hasn't got what the page
|
|
96
93
|
# shows.
|
|
97
94
|
def note
|
|
98
|
-
@painter.text(@left, @top, "NOT ON THE VIC-20 YET", DIM)
|
|
95
|
+
@painter.text(@left, @top, "NOT ON THE VIC-20 YET", MenuTheme::DIM)
|
|
99
96
|
end
|
|
100
97
|
|
|
101
98
|
def draw_drive
|
|
@@ -182,11 +179,11 @@ module Badline
|
|
|
182
179
|
# A label, and its value under it, or NONE in the dim colour when the
|
|
183
180
|
# value is empty.
|
|
184
181
|
def info(label, value)
|
|
185
|
-
@painter.text(@left, @top, label, DIM)
|
|
182
|
+
@painter.text(@left, @top, label, MenuTheme::DIM)
|
|
186
183
|
if value.empty?
|
|
187
|
-
@painter.text(@left, @top + LINE, "NONE", DIM)
|
|
184
|
+
@painter.text(@left, @top + LINE, "NONE", MenuTheme::DIM)
|
|
188
185
|
else
|
|
189
|
-
@painter.text(@left, @top + LINE, Painter.fit(value, COLUMNS), TEXT)
|
|
186
|
+
@painter.text(@left, @top + LINE, Painter.fit(value, COLUMNS), MenuTheme::TEXT)
|
|
190
187
|
end
|
|
191
188
|
@top += (LINE * 2) + 6
|
|
192
189
|
end
|
|
@@ -8,14 +8,8 @@ module Badline
|
|
|
8
8
|
# name and Esc leaves it.
|
|
9
9
|
class NameField
|
|
10
10
|
COLUMNS = 30
|
|
11
|
-
LETTERS = "abcdefghijklmnopqrstuvwxyz"
|
|
12
|
-
DIGITS = "1234567890"
|
|
13
11
|
OTHERS = { 44 => [" ", " "], 45 => ["-", "_"], 55 => [".", "."] }.freeze
|
|
14
12
|
|
|
15
|
-
RETURN = 40
|
|
16
|
-
ESCAPE = 41
|
|
17
|
-
BACKSPACE = 42
|
|
18
|
-
|
|
19
13
|
attr_reader :text
|
|
20
14
|
|
|
21
15
|
def initialize(painter)
|
|
@@ -47,11 +41,11 @@ module Badline
|
|
|
47
41
|
# Handles a key, and returns :done for Return, :cancel for Esc, or
|
|
48
42
|
# nil.
|
|
49
43
|
def key(scancode, shift)
|
|
50
|
-
return :done if scancode == RETURN
|
|
51
|
-
return :cancel if scancode == ESCAPE
|
|
44
|
+
return :done if scancode == Keys::RETURN
|
|
45
|
+
return :cancel if scancode == Keys::ESCAPE
|
|
52
46
|
|
|
53
47
|
@problem = ""
|
|
54
|
-
if scancode == BACKSPACE
|
|
48
|
+
if scancode == Keys::BACKSPACE
|
|
55
49
|
@text = @text[0, @text.length - 1].to_s
|
|
56
50
|
else
|
|
57
51
|
char = character(scancode, shift)
|
|
@@ -62,26 +56,19 @@ module Badline
|
|
|
62
56
|
|
|
63
57
|
def draw(left, top, width)
|
|
64
58
|
painter = @painter
|
|
65
|
-
painter.text(left, top, @title,
|
|
66
|
-
painter.box(left, top + 18, width, Buttons::HEIGHT + 4,
|
|
67
|
-
painter.text(left + 4, top + 22, "#{@text}_",
|
|
68
|
-
painter.text(left, top + 42, @problem,
|
|
69
|
-
painter.text(left, top + 60, "RETURN: SAVE ESC: CANCEL",
|
|
59
|
+
painter.text(left, top, @title, MenuTheme::BRIGHT)
|
|
60
|
+
painter.box(left, top + 18, width, Buttons::HEIGHT + 4, MenuTheme::FILL)
|
|
61
|
+
painter.text(left + 4, top + 22, "#{@text}_", MenuTheme::TEXT)
|
|
62
|
+
painter.text(left, top + 42, @problem, MenuTheme::WARNING) unless @problem.empty?
|
|
63
|
+
painter.text(left, top + 60, "RETURN: SAVE ESC: CANCEL", MenuTheme::DIM)
|
|
70
64
|
end
|
|
71
65
|
|
|
72
66
|
private
|
|
73
67
|
|
|
74
68
|
def character(scancode, shift)
|
|
75
|
-
if
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
elsif scancode.between?(30, 39)
|
|
79
|
-
DIGITS[scancode - 30]
|
|
80
|
-
elsif OTHERS.key?(scancode)
|
|
81
|
-
OTHERS[scancode][shift ? 1 : 0]
|
|
82
|
-
else
|
|
83
|
-
""
|
|
84
|
-
end
|
|
69
|
+
return OTHERS[scancode][shift ? 1 : 0] if OTHERS.key?(scancode)
|
|
70
|
+
|
|
71
|
+
Keys.character(scancode, shift)
|
|
85
72
|
end
|
|
86
73
|
end
|
|
87
74
|
end
|
|
@@ -17,30 +17,18 @@ module Badline
|
|
|
17
17
|
SECTIONS = ["SNAPSHOTS", "DRIVE 8", "DATASETTE", "EXPANSION PORT", "PORTS", "SOUND", "POWER"].freeze
|
|
18
18
|
PAGES = %i[snapshots drive datasette expansion ports sound power].freeze
|
|
19
19
|
|
|
20
|
-
PANEL = 0x1d2230
|
|
21
|
-
EDGE = 0x3a4256
|
|
22
|
-
TEXT = 0xc9d1e0
|
|
23
|
-
BRIGHT = 0xffc66d
|
|
24
|
-
DIM = 0x6b7489
|
|
25
|
-
FILL = 0x343c50
|
|
26
|
-
|
|
27
20
|
MARGIN = 16
|
|
28
21
|
WIDTH = 352
|
|
29
22
|
HEIGHT = 240
|
|
30
23
|
BODY = 138
|
|
31
24
|
|
|
32
|
-
ESCAPE = 41
|
|
33
|
-
RETURN = 40
|
|
34
|
-
SPACE = 44
|
|
35
25
|
SNAPSHOT_ACTIONS = %i[quicksave_now save_as load_save].freeze
|
|
36
|
-
ARROWS = { 79 => :right, 80 => :left, 81 => :down, 82 => :up }.freeze
|
|
37
|
-
MOUSEWHEEL = 0x403
|
|
38
26
|
|
|
39
27
|
# Takes the media, the REU and whether disks are writable from Options.
|
|
40
28
|
def initialize(painter, options, snapshots)
|
|
41
29
|
media_path = options.media_path.to_s
|
|
42
30
|
@painter = painter
|
|
43
|
-
@buttons = Buttons.new(painter, [TEXT, BRIGHT, PANEL, FILL])
|
|
31
|
+
@buttons = Buttons.new(painter, [MenuTheme::TEXT, MenuTheme::BRIGHT, MenuTheme::PANEL, MenuTheme::FILL])
|
|
44
32
|
@pages = MenuPages.new(painter, @buttons, media_path, options, snapshots)
|
|
45
33
|
@snapshots = snapshots
|
|
46
34
|
@dialogs = MenuDialogs.new(painter, @buttons, @pages.media, options, snapshots)
|
|
@@ -76,10 +64,7 @@ module Badline
|
|
|
76
64
|
# goes straight in, and anything else asks first.
|
|
77
65
|
def drop(computer, controls, sound)
|
|
78
66
|
show(computer, controls, sound)
|
|
79
|
-
|
|
80
|
-
asks = @dialogs.drop(LibC.strstr(file, ""))
|
|
81
|
-
LibC.free(file)
|
|
82
|
-
asks
|
|
67
|
+
@dialogs.drop(SDL.dropped_file)
|
|
83
68
|
end
|
|
84
69
|
|
|
85
70
|
# Handles a key pressed while the menu is open: the arrows move
|
|
@@ -91,10 +76,10 @@ module Badline
|
|
|
91
76
|
def key(scancode)
|
|
92
77
|
return :resume if scancode == Keys::F9
|
|
93
78
|
return @dialogs.key(scancode) if @dialogs.open?
|
|
94
|
-
return :resume if scancode == ESCAPE
|
|
95
|
-
return choose(@buttons.focus) if [RETURN, SPACE].include?(scancode)
|
|
79
|
+
return :resume if scancode == Keys::ESCAPE
|
|
80
|
+
return choose(@buttons.focus) if [Keys::RETURN, Keys::SPACE].include?(scancode)
|
|
96
81
|
|
|
97
|
-
direction =
|
|
82
|
+
direction = Keys::DIRECTIONS[scancode]
|
|
98
83
|
move(direction) unless direction.nil?
|
|
99
84
|
nil
|
|
100
85
|
end
|
|
@@ -134,18 +119,18 @@ module Badline
|
|
|
134
119
|
def draw
|
|
135
120
|
painter = @painter
|
|
136
121
|
@buttons.forget
|
|
137
|
-
painter.box(@left, @top, WIDTH, HEIGHT, PANEL)
|
|
138
|
-
painter.box(@left, @top + 24, WIDTH, 1, EDGE)
|
|
139
|
-
painter.text(@left + 8, @top + 9, "PAUSED", BRIGHT)
|
|
122
|
+
painter.box(@left, @top, WIDTH, HEIGHT, MenuTheme::PANEL)
|
|
123
|
+
painter.box(@left, @top + 24, WIDTH, 1, MenuTheme::EDGE)
|
|
124
|
+
painter.text(@left + 8, @top + 9, "PAUSED", MenuTheme::BRIGHT)
|
|
140
125
|
resume = "RESUME"
|
|
141
126
|
@buttons.plain(@left + WIDTH - 8 - Painter.width(resume) - (Buttons::PAD * 2), @top + 7, resume, :resume)
|
|
142
127
|
return @dialogs.draw(@left + 8, @top + 34, WIDTH - 16) if @dialogs.open?
|
|
143
128
|
|
|
144
|
-
painter.box(@left + BODY - 8, @top + 32, 1, HEIGHT - 56, EDGE)
|
|
129
|
+
painter.box(@left + BODY - 8, @top + 32, 1, HEIGHT - 56, MenuTheme::EDGE)
|
|
145
130
|
draw_sections
|
|
146
131
|
@buttons.plain(@left + 6, @top + HEIGHT - 28, "QUICK OPEN...", :quick_open)
|
|
147
|
-
painter.text(@left + 8, @top + HEIGHT - 12, "F9/ESC: RESUME", DIM)
|
|
148
|
-
painter.text(@left + BODY, @top + HEIGHT - 12, "F6: 40/80 KEY F8: SCREEN", DIM) if @pages.c128?
|
|
132
|
+
painter.text(@left + 8, @top + HEIGHT - 12, "F9/ESC: RESUME", MenuTheme::DIM)
|
|
133
|
+
painter.text(@left + BODY, @top + HEIGHT - 12, "F6: 40/80 KEY F8: SCREEN", MenuTheme::DIM) if @pages.c128?
|
|
149
134
|
@pages.draw(PAGES[@section], @left + BODY, @top + 34)
|
|
150
135
|
end
|
|
151
136
|
|
|
@@ -241,13 +226,13 @@ module Badline
|
|
|
241
226
|
when SDL::MOUSEMOTION then @buttons.point(SDL.event_x(event), SDL.event_y(event)) && nil
|
|
242
227
|
when SDL::MOUSEBUTTONDOWN
|
|
243
228
|
SDL.event_button(event) == 1 ? click(SDL.event_x(event), SDL.event_y(event)) : nil
|
|
244
|
-
when MOUSEWHEEL then @dialogs.scroll(-3 * SDL.event_x(event)) && nil
|
|
229
|
+
when SDL::MOUSEWHEEL then @dialogs.scroll(-3 * SDL.event_x(event)) && nil
|
|
245
230
|
end
|
|
246
231
|
end
|
|
247
232
|
|
|
248
233
|
# A key held down repeats only the arrows.
|
|
249
234
|
def key_down(scancode, repeat)
|
|
250
|
-
repeat.zero? ||
|
|
235
|
+
repeat.zero? || Keys::DIRECTIONS.key?(scancode) ? key(scancode) : nil
|
|
251
236
|
end
|
|
252
237
|
|
|
253
238
|
def draw_sections
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
module Frontend
|
|
5
|
+
# The pause menu's colours, which its pages and dialogs share. They lie
|
|
6
|
+
# outside the C64's palette, so the menu reads as the emulator's and
|
|
7
|
+
# not the machine's.
|
|
8
|
+
module MenuTheme
|
|
9
|
+
PANEL = 0x1d2230
|
|
10
|
+
EDGE = 0x3a4256
|
|
11
|
+
TEXT = 0xc9d1e0
|
|
12
|
+
BRIGHT = 0xffc66d
|
|
13
|
+
DIM = 0x6b7489
|
|
14
|
+
FILL = 0x343c50
|
|
15
|
+
WARNING = 0xff7a6b
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -6,10 +6,10 @@ module Badline
|
|
|
6
6
|
# resonance and volume, and its response across the audible range on
|
|
7
7
|
# the chip playing, whose cutoff curve the two models don't share.
|
|
8
8
|
class FilterView
|
|
9
|
-
TEXT =
|
|
10
|
-
BRIGHT =
|
|
11
|
-
DIM =
|
|
12
|
-
BOX =
|
|
9
|
+
TEXT = PlayerTheme::TEXT
|
|
10
|
+
BRIGHT = PlayerTheme::BRIGHT
|
|
11
|
+
DIM = PlayerTheme::DIM
|
|
12
|
+
BOX = PlayerTheme::BOX
|
|
13
13
|
|
|
14
14
|
MODES = ["LP", "BP", "HP", "3 OFF"].freeze
|
|
15
15
|
|
|
@@ -52,7 +52,7 @@ module Badline
|
|
|
52
52
|
def draw
|
|
53
53
|
index = 0
|
|
54
54
|
while index < ROWS && @offset + index < @lines.size
|
|
55
|
-
@painter.text(LEFT, @top + (index * LINE), @lines[@offset + index],
|
|
55
|
+
@painter.text(LEFT, @top + (index * LINE), @lines[@offset + index], PlayerTheme::TEXT)
|
|
56
56
|
index += 1
|
|
57
57
|
end
|
|
58
58
|
draw_bar if @lines.size > ROWS
|
|
@@ -110,10 +110,10 @@ module Badline
|
|
|
110
110
|
|
|
111
111
|
def draw_bar
|
|
112
112
|
@buttons.area([BAR_LEFT, @top, BAR, HEIGHT], :scroll)
|
|
113
|
-
@painter.box(BAR_LEFT, @top, BAR, HEIGHT,
|
|
113
|
+
@painter.box(BAR_LEFT, @top, BAR, HEIGHT, PlayerTheme::BOX)
|
|
114
114
|
thumb = [HEIGHT * ROWS / @lines.size, 8].max
|
|
115
115
|
place = (HEIGHT - thumb) * @offset / [last_offset, 1].max
|
|
116
|
-
@painter.box(BAR_LEFT, @top + place, BAR, thumb,
|
|
116
|
+
@painter.box(BAR_LEFT, @top + place, BAR, thumb, PlayerTheme::TEXT)
|
|
117
117
|
end
|
|
118
118
|
end
|
|
119
119
|
end
|