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
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
module Media
|
|
5
|
+
# A C128 boot disk. At power-on the C128 KERNAL reads track 1, sector 0
|
|
6
|
+
# of the disk in device 8 and runs it when it starts with "CBM", as boot
|
|
7
|
+
# games have it. The 1581's DOS first loads and runs a USR file named
|
|
8
|
+
# COPYRIGHT CBM 86 when the disk has one, which is how CP/M's 1581 disks
|
|
9
|
+
# hand the KERNAL their boot sector, so a .d81 with that file boots too.
|
|
10
|
+
#
|
|
11
|
+
# On a C128 in C128 mode a boot disk goes in a true drive, a 1581 for a
|
|
12
|
+
# .d81 and the C128D's 1571 otherwise, and nothing is typed, so the
|
|
13
|
+
# KERNAL boots it. Other disks, and other machines, keep the traps and
|
|
14
|
+
# the autostart.
|
|
15
|
+
module BootDisk
|
|
16
|
+
SIGNATURE = "CBM".bytes.freeze
|
|
17
|
+
AUTO_BOOT = "copyright cbm 86"
|
|
18
|
+
TYPES = %w[.d64 .d71 .d81].freeze
|
|
19
|
+
|
|
20
|
+
class << self
|
|
21
|
+
def takes?(computer, path)
|
|
22
|
+
computer.family == :c128 && computer.mode == :c128 && boot?(path)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Puts a disk that boots on the computer in a true drive as device
|
|
26
|
+
# 8, write-protected with `disk: { read_only: true }`, for the KERNAL
|
|
27
|
+
# to boot at power-on, and says so. Nil for any other disk.
|
|
28
|
+
def attach(computer, path, disk: {}, **)
|
|
29
|
+
return unless takes?(computer, path)
|
|
30
|
+
|
|
31
|
+
read_only = disk.fetch(:read_only, false)
|
|
32
|
+
"#{TrueDrive.insert(computer, path, read_only:)}, to boot at power-on"
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Whether the disk image at path boots: its boot sector, or on a
|
|
36
|
+
# .d81 the 1581's auto-boot file.
|
|
37
|
+
def boot?(path)
|
|
38
|
+
type = File.extname(path).downcase
|
|
39
|
+
return false unless TYPES.include?(type) && File.file?(path)
|
|
40
|
+
|
|
41
|
+
image = MOUNT_TYPES.fetch(type).new(path, read_only: true)
|
|
42
|
+
block = image.read_block(1, 0)
|
|
43
|
+
return true if block && block[0, 3] == SIGNATURE
|
|
44
|
+
|
|
45
|
+
type == ".d81" && !image.read_file(AUTO_BOOT, type: :usr).nil?
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -3,13 +3,15 @@
|
|
|
3
3
|
module Badline
|
|
4
4
|
module Media
|
|
5
5
|
# Device 8 as a true drive instead of the KERNAL traps: a 1541, or on
|
|
6
|
-
# the C128 its 1571. Once a true drive is plugged in as
|
|
7
|
-
# media go into it rather than into the traps, so a LOAD
|
|
8
|
-
# and every byte crosses the serial bus. A .g64 holds the
|
|
9
|
-
# GCR, which only a true drive reads, so it plugs one in
|
|
10
|
-
# none yet. The 1541 reads only .d64 and .g64 images,
|
|
11
|
-
# .d71 and .g71 as well,
|
|
12
|
-
#
|
|
6
|
+
# the C128 its 1571, or a 1581. Once a true drive is plugged in as
|
|
7
|
+
# device 8, disk media go into it rather than into the traps, so a LOAD
|
|
8
|
+
# runs the DOS and every byte crosses the serial bus. A .g64 holds the
|
|
9
|
+
# disk's raw GCR, which only a true drive reads, so it plugs one in
|
|
10
|
+
# when there's none yet. The 1541 reads only .d64 and .g64 images, the
|
|
11
|
+
# 1571 .d71 and .g71 as well, and the 1581 only .d81, so a .d81 swaps
|
|
12
|
+
# the drive on device 8 for a 1581 (Drive1581::Slot), and another disk
|
|
13
|
+
# image swaps a 1581 back for the machine's own drive. The traps' other
|
|
14
|
+
# disk media, a .t64 or a host directory, raise Error.
|
|
13
15
|
module TrueDrive
|
|
14
16
|
class Error < ArgumentError; end
|
|
15
17
|
|
|
@@ -35,22 +37,40 @@ module Badline
|
|
|
35
37
|
!drive(computer).nil? && (File.directory?(path) || MOUNT_TYPES.key?(File.extname(path).downcase))
|
|
36
38
|
end
|
|
37
39
|
|
|
38
|
-
# Puts the image in the drive on device 8, plugging one in
|
|
39
|
-
# there'
|
|
40
|
-
# so LOAD and SAVE reach the drive too.
|
|
41
|
-
# write-protected.
|
|
40
|
+
# Puts the image in the drive on device 8, plugging one in that
|
|
41
|
+
# reads it when the drive there doesn't, and takes out what was
|
|
42
|
+
# mounted through the traps, so LOAD and SAVE reach the drive too.
|
|
43
|
+
# `read_only` puts it in write-protected.
|
|
42
44
|
def insert(computer, path, read_only: false)
|
|
43
|
-
|
|
45
|
+
return insert_d81(computer, path, read_only) if File.extname(path).casecmp?(".d81")
|
|
46
|
+
|
|
47
|
+
drive = own_drive(computer)
|
|
48
|
+
name = drive&.model_name || (computer.family == :c128 ? "1571" : "1541")
|
|
44
49
|
types = name == "1571" ? %w[.d64 .g64 .d71 .g71] : %w[.d64 .g64]
|
|
45
50
|
unless types.include?(File.extname(path).downcase)
|
|
46
|
-
raise Error, "#{path} is not a #{types
|
|
51
|
+
raise Error, "#{path} is not a #{types.join(', ')} or .d81 image, which the #{name} and the 1581 read"
|
|
47
52
|
end
|
|
48
53
|
|
|
49
|
-
|
|
54
|
+
(drive || computer.plug_true_drive).insert_image(path, read_only:)
|
|
50
55
|
computer.unmount
|
|
51
|
-
plug(computer).insert(disk)
|
|
52
56
|
"Inserted #{path} in the #{name} as device 8"
|
|
53
57
|
end
|
|
58
|
+
|
|
59
|
+
private
|
|
60
|
+
|
|
61
|
+
# The drive on device 8 unless it's a 1581.
|
|
62
|
+
def own_drive(computer)
|
|
63
|
+
drive = drive(computer)
|
|
64
|
+
drive unless drive&.model_name == "1581"
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def insert_d81(computer, path, read_only)
|
|
68
|
+
drive = drive(computer)
|
|
69
|
+
drive = computer.plug_drive1581 unless drive&.model_name == "1581"
|
|
70
|
+
drive.insert_image(path, read_only:)
|
|
71
|
+
computer.unmount
|
|
72
|
+
"Inserted #{path} in the 1581 as device 8"
|
|
73
|
+
end
|
|
54
74
|
end
|
|
55
75
|
end
|
|
56
76
|
end
|
data/lib/badline/media.rb
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "badline/media/true_drive"
|
|
4
|
+
require "badline/media/boot_disk"
|
|
5
|
+
require "badline/media/not_disk"
|
|
4
6
|
require "badline/media/queue"
|
|
5
7
|
require "badline/media/disk_set"
|
|
6
8
|
require "badline/media/disk_list"
|
|
@@ -32,6 +34,9 @@ module Badline
|
|
|
32
34
|
# there, a .d64 goes into it instead of the KERNAL traps too. The
|
|
33
35
|
# autostart then loads through it.
|
|
34
36
|
#
|
|
37
|
+
# On a C128 in C128 mode, a boot disk goes in a true drive for the
|
|
38
|
+
# KERNAL to boot at power-on, with nothing typed (BootDisk).
|
|
39
|
+
#
|
|
35
40
|
# An .m3u or .vfl list of disks attaches the first disk it lists
|
|
36
41
|
# (DiskList). The VIC-20 takes its own media (Vic20Media), and the
|
|
37
42
|
# C128 in C64 mode a C64's.
|
|
@@ -39,7 +44,7 @@ module Badline
|
|
|
39
44
|
path = DiskList.disk(path)
|
|
40
45
|
return Vic20Media.attach(computer, path, autostart:, **) if computer.family == :vic20
|
|
41
46
|
|
|
42
|
-
attach_c64(computer, path, autostart:, subtune:, **)
|
|
47
|
+
BootDisk.attach(computer, path, **) || attach_c64(computer, path, autostart:, subtune:, **)
|
|
43
48
|
end
|
|
44
49
|
|
|
45
50
|
# Swaps the disk in device 8 for a disk image or a host directory,
|
|
@@ -50,11 +55,12 @@ module Badline
|
|
|
50
55
|
# A .g64 goes in a true 1541, which is plugged in as device 8 when
|
|
51
56
|
# there's none yet (TrueDrive). Once a true 1541 is device 8, a .d64
|
|
52
57
|
# goes in its drive as well, and other disks, which a 1541 can't
|
|
53
|
-
# read, raise TrueDrive::Error, an ArgumentError.
|
|
58
|
+
# read, raise TrueDrive::Error, an ArgumentError. Anything else
|
|
59
|
+
# raises NotDisk.
|
|
54
60
|
def insert_disk(computer, path, read_only: false)
|
|
55
61
|
return TrueDrive.insert(computer, path, read_only:) if TrueDrive.takes?(computer, path)
|
|
56
62
|
|
|
57
|
-
raise
|
|
63
|
+
raise NotDisk, "#{path} is not a disk image or a directory" unless disk?(path)
|
|
58
64
|
|
|
59
65
|
computer.mount(open_storage(path, { read_only: }))
|
|
60
66
|
"Inserted #{path} in device 8"
|
|
@@ -23,7 +23,7 @@ module Badline
|
|
|
23
23
|
|
|
24
24
|
# What insert takes: disk images, tapes and cartridges, or an .m3u
|
|
25
25
|
# or .vfl list of disks or a directory to mount as device 8.
|
|
26
|
-
INSERTABLE = %w[.d64 .d71 .d81 .g64 .t64 .tap .crt].freeze
|
|
26
|
+
INSERTABLE = %w[.d64 .d71 .d81 .g64 .t64 .tap .crt .m3u .vfl].freeze
|
|
27
27
|
|
|
28
28
|
attr_reader :frame, :action, :argument
|
|
29
29
|
|
|
@@ -95,7 +95,7 @@ module Badline
|
|
|
95
95
|
def self.insertable?(path)
|
|
96
96
|
return true if File.directory?(path)
|
|
97
97
|
|
|
98
|
-
File.exist?(path) &&
|
|
98
|
+
File.exist?(path) && INSERTABLE.include?(File.extname(path).downcase)
|
|
99
99
|
end
|
|
100
100
|
end
|
|
101
101
|
end
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "badline/storage/p00"
|
|
4
|
+
|
|
3
5
|
module Badline
|
|
4
6
|
class Options
|
|
5
7
|
# The machine family: the subcommand that names it, or else the media,
|
|
@@ -37,15 +39,7 @@ module Badline
|
|
|
37
39
|
return if @family_named || @sid_command || @media_path.nil?
|
|
38
40
|
return unless %w[.prg .p00].include?(File.extname(@media_path).downcase) && File.file?(@media_path)
|
|
39
41
|
|
|
40
|
-
@family = :c128 if load_address(@media_path) == C128_BASIC_START
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
# The load address of a .prg, or of the program a .p00 wraps after its
|
|
44
|
-
# 26-byte header.
|
|
45
|
-
def load_address(path)
|
|
46
|
-
offset = File.extname(path).casecmp?(".p00") ? 26 : 0
|
|
47
|
-
bytes = File.binread(path).bytes
|
|
48
|
-
bytes.size >= offset + 2 ? bytes[offset] | (bytes[offset + 1] << 8) : -1
|
|
42
|
+
@family = :c128 if Storage::P00.load_address(@media_path) == C128_BASIC_START
|
|
49
43
|
end
|
|
50
44
|
end
|
|
51
45
|
end
|
data/lib/badline/options/help.rb
CHANGED
|
@@ -61,7 +61,8 @@ module Badline
|
|
|
61
61
|
pause menu loads. Device 8 answers through traps on the KERNAL's
|
|
62
62
|
disk routines, unless --true-drive puts an emulated 1541 there,
|
|
63
63
|
which runs its own DOS and reads .d64 and .g64 images only, or on
|
|
64
|
-
the C128 the C128D's 1571, which reads .d71 and .g71 too. A .
|
|
64
|
+
the C128 the C128D's 1571, which reads .d71 and .g71 too. A .d81
|
|
65
|
+
then goes in an emulated 1581 in its place. A .sid
|
|
65
66
|
tune opens in the SID player, as `sid` plays it, unless an option
|
|
66
67
|
of the emulator's window asks for the machine.
|
|
67
68
|
|
data/lib/badline/roms/README
CHANGED
|
@@ -70,6 +70,15 @@ Downloaded from
|
|
|
70
70
|
https://www.zimmers.net/anonftp/pub/cbm/firmware/drives/new/1571/1571-rom.310654-05.bin
|
|
71
71
|
SHA-1 f1be619c106641a685f6609e4d43d6fc9eac1e70.
|
|
72
72
|
|
|
73
|
+
dos1581.rom
|
|
74
|
+
-----------
|
|
75
|
+
The 1581 disk drive's DOS, 318045-02, 32 KB for $8000-$FFFF.
|
|
76
|
+
Drive1581 loads it.
|
|
77
|
+
|
|
78
|
+
Downloaded from
|
|
79
|
+
https://www.zimmers.net/anonftp/pub/cbm/firmware/drives/new/1581/1581-rom.318045-02.bin
|
|
80
|
+
SHA-1 01228eae6f066bd9b7b2b6a7fa3f667e41dad393.
|
|
81
|
+
|
|
73
82
|
Commodore VIC-20 ROMs
|
|
74
83
|
=====================
|
|
75
84
|
|
|
Binary file
|
data/lib/badline/sdl.rb
CHANGED
|
@@ -59,23 +59,6 @@ module Badline
|
|
|
59
59
|
ffi_func :SDL_PushEvent, [:ptr], :int
|
|
60
60
|
ffi_func :SDL_GetKeyName, [:int], :str
|
|
61
61
|
|
|
62
|
-
ffi_buffer :event, 56
|
|
63
|
-
ffi_read_u32 :event_type, 0
|
|
64
|
-
ffi_read_u8 :event_repeat, 13
|
|
65
|
-
ffi_read_i32 :event_scancode, 16
|
|
66
|
-
ffi_read_i32 :event_sym, 20
|
|
67
|
-
ffi_read_u16 :event_mod, 24
|
|
68
|
-
# SDL_MouseMotionEvent's xrel and yrel, and SDL_MouseButtonEvent's
|
|
69
|
-
# button.
|
|
70
|
-
ffi_read_i32 :event_xrel, 28
|
|
71
|
-
ffi_read_i32 :event_yrel, 32
|
|
72
|
-
ffi_read_u8 :event_button, 16
|
|
73
|
-
# Where the pointer is, in a motion or button event.
|
|
74
|
-
ffi_read_i32 :event_x, 20
|
|
75
|
-
ffi_read_i32 :event_y, 24
|
|
76
|
-
# SDL_DropEvent's file, which the receiver frees.
|
|
77
|
-
ffi_read_ptr :event_file, 8
|
|
78
|
-
|
|
79
62
|
ffi_buffer :rect, 16
|
|
80
63
|
ffi_write_i32 :rect_x, 0
|
|
81
64
|
ffi_write_i32 :rect_y, 4
|
|
@@ -115,19 +98,53 @@ module Badline
|
|
|
115
98
|
ffi_const :PIXELFORMAT_ARGB8888, 0x16362004
|
|
116
99
|
ffi_const :BLENDMODE_BLEND, 0x01
|
|
117
100
|
ffi_const :TEXTUREACCESS_STREAMING, 1
|
|
101
|
+
ffi_const :AUDIO_S16LSB, 0x8010
|
|
102
|
+
ffi_const :ALLOW_FREQUENCY_CHANGE, 0x01
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# The events: the SDL_Event that SDL_PollEvent fills, the fields the
|
|
106
|
+
# front end reads from it, the event types, the key modifier and
|
|
107
|
+
# keycodes, and the dropped file's path.
|
|
108
|
+
module SDL
|
|
109
|
+
ffi_buffer :event, 56
|
|
110
|
+
ffi_read_u32 :event_type, 0
|
|
111
|
+
ffi_read_u8 :event_repeat, 13
|
|
112
|
+
ffi_read_i32 :event_scancode, 16
|
|
113
|
+
ffi_read_i32 :event_sym, 20
|
|
114
|
+
ffi_read_u16 :event_mod, 24
|
|
115
|
+
# SDL_MouseMotionEvent's xrel and yrel, and SDL_MouseButtonEvent's
|
|
116
|
+
# button.
|
|
117
|
+
ffi_read_i32 :event_xrel, 28
|
|
118
|
+
ffi_read_i32 :event_yrel, 32
|
|
119
|
+
ffi_read_u8 :event_button, 16
|
|
120
|
+
# Where the pointer is, in a motion or button event.
|
|
121
|
+
ffi_read_i32 :event_x, 20
|
|
122
|
+
ffi_read_i32 :event_y, 24
|
|
123
|
+
# SDL_DropEvent's file, which the receiver frees.
|
|
124
|
+
ffi_read_ptr :event_file, 8
|
|
125
|
+
|
|
118
126
|
ffi_const :QUIT, 0x100
|
|
119
127
|
ffi_const :KEYDOWN, 0x300
|
|
120
128
|
ffi_const :KEYUP, 0x301
|
|
121
129
|
ffi_const :MOUSEMOTION, 0x400
|
|
122
130
|
ffi_const :MOUSEBUTTONDOWN, 0x401
|
|
123
131
|
ffi_const :MOUSEBUTTONUP, 0x402
|
|
124
|
-
ffi_const :
|
|
132
|
+
ffi_const :MOUSEWHEEL, 0x403
|
|
125
133
|
ffi_const :CONTROLLERDEVICEADDED, 0x653
|
|
126
134
|
ffi_const :CONTROLLERDEVICEREMOVED, 0x654
|
|
127
|
-
ffi_const :
|
|
128
|
-
ffi_const :
|
|
135
|
+
ffi_const :DROPFILE, 0x1000
|
|
136
|
+
ffi_const :KMOD_SHIFT, 0x0003
|
|
129
137
|
ffi_const :KEY_TAB, 0x09
|
|
130
138
|
ffi_const :KEY_F10, 0x4000_0043
|
|
139
|
+
|
|
140
|
+
# The path of the file the DROPFILE event in SDL.event carries, as a
|
|
141
|
+
# String, freeing SDL's copy.
|
|
142
|
+
def self.dropped_file
|
|
143
|
+
file = SDL.event_file(SDL.event)
|
|
144
|
+
path = LibC.strstr(file, "")
|
|
145
|
+
LibC.free(file)
|
|
146
|
+
path
|
|
147
|
+
end
|
|
131
148
|
end
|
|
132
149
|
|
|
133
150
|
# The few C library calls the native front end makes.
|
|
@@ -15,10 +15,11 @@ module Badline
|
|
|
15
15
|
# save_state writes something else, so a State in another layout
|
|
16
16
|
# fails before anything is read into a machine. Layout 3, without the
|
|
17
17
|
# KERNAL and datasette in the setup, layout 4, without the VIA shift
|
|
18
|
-
# register's clock delay,
|
|
19
|
-
#
|
|
20
|
-
|
|
21
|
-
|
|
18
|
+
# register's clock delay, layout 5, without the board in the setup,
|
|
19
|
+
# layout 6, without the C128's Z80, and layout 7, without a 1581 on the
|
|
20
|
+
# serial bus, still read.
|
|
21
|
+
SCHEMA = 8
|
|
22
|
+
READABLE = [3, 4, 5, 6, 7, SCHEMA].freeze
|
|
22
23
|
|
|
23
24
|
attr_reader :values, :strings
|
|
24
25
|
|
data/lib/badline/storage/p00.rb
CHANGED
|
@@ -18,6 +18,14 @@ module Badline
|
|
|
18
18
|
def data(bytes)
|
|
19
19
|
bytes[HEADER_SIZE..]
|
|
20
20
|
end
|
|
21
|
+
|
|
22
|
+
# The load address of the program in a .prg file, or wrapped in a
|
|
23
|
+
# .p00 one, or -1 when the file is too short to have one.
|
|
24
|
+
def load_address(path)
|
|
25
|
+
bytes = File.binread(path).bytes
|
|
26
|
+
offset = wraps?(bytes) ? HEADER_SIZE : 0
|
|
27
|
+
bytes.size >= offset + 2 ? bytes[offset] | (bytes[offset + 1] << 8) : -1
|
|
28
|
+
end
|
|
21
29
|
end
|
|
22
30
|
end
|
|
23
31
|
end
|
data/lib/badline/time_of_day.rb
CHANGED
|
@@ -52,9 +52,9 @@ module Badline
|
|
|
52
52
|
@latch = input.boolean? ? FIELDS.to_h { |field| [field, input.int] } : nil
|
|
53
53
|
end
|
|
54
54
|
|
|
55
|
-
# Whether the clock is stopped
|
|
56
|
-
# move only the divider's phase.
|
|
57
|
-
def quiet? = @stopped && !@alarm_pending
|
|
55
|
+
# Whether the clock is stopped, or gets no pulses on its pin, with no
|
|
56
|
+
# alarm to raise, so its cycles move only the divider's phase.
|
|
57
|
+
def quiet? = (@stopped || @mains_hz.zero?) && !@alarm_pending
|
|
58
58
|
|
|
59
59
|
# Runs +cycles+ quiet cycles at once.
|
|
60
60
|
def fast_forward(cycles)
|
data/lib/badline/version.rb
CHANGED
|
@@ -4,8 +4,10 @@ module Badline
|
|
|
4
4
|
class Vic20
|
|
5
5
|
# What plugs into the VIC-20 besides its chips: cartridge ROM in the
|
|
6
6
|
# expansion blocks, the disk device 8 serves through the KERNAL traps,
|
|
7
|
-
# and a true 1541 on the serial bus.
|
|
7
|
+
# and a true 1541 or 1581 on the serial bus.
|
|
8
8
|
module Attachments
|
|
9
|
+
include Drive1581::Slot
|
|
10
|
+
|
|
9
11
|
# The cycle by which a 1541 switched on with the machine has run its
|
|
10
12
|
# DOS's reset, the RAM test and the ROM checksum, and waits on the
|
|
11
13
|
# bus, with a margin: about 1.1 million of the VIC-20's cycles.
|
|
@@ -48,7 +50,7 @@ module Badline
|
|
|
48
50
|
|
|
49
51
|
cpu.remove_trap(KernalTrap::VIC20_LAYOUT.load)
|
|
50
52
|
cpu.remove_trap(KernalTrap::VIC20_LAYOUT.save)
|
|
51
|
-
@serial_trap.
|
|
53
|
+
@serial_trap.uninstall
|
|
52
54
|
@serial_trap = nil
|
|
53
55
|
@save_trap = nil
|
|
54
56
|
@drive = nil
|
|
@@ -75,11 +77,16 @@ module Badline
|
|
|
75
77
|
@serial_trap&.device = serial_trap_device
|
|
76
78
|
end
|
|
77
79
|
|
|
78
|
-
# The true drive on the serial bus: the
|
|
79
|
-
|
|
80
|
+
# The true drive on the serial bus: the Drive1581 ahead of the
|
|
81
|
+
# Drive1541, or nil.
|
|
82
|
+
def true_drive = @drive1581 || @drive1541
|
|
80
83
|
|
|
81
|
-
# Plugs a Drive1541 in as device 8,
|
|
82
|
-
|
|
84
|
+
# Plugs a Drive1541 in as device 8, in place of a 1581 there, and
|
|
85
|
+
# returns it.
|
|
86
|
+
def plug_true_drive
|
|
87
|
+
detach_drive1581 if drive1581_on_device8?
|
|
88
|
+
Drive1541.new.tap { |drive| attach_drive1541(drive) }
|
|
89
|
+
end
|
|
83
90
|
|
|
84
91
|
# Unplugs the Drive1541, leaving the serial bus with nothing on it.
|
|
85
92
|
def detach_drive1541
|
|
@@ -102,7 +109,14 @@ module Badline
|
|
|
102
109
|
# The device number the serial traps answer: device 8, unless a true
|
|
103
110
|
# drive is on the bus as device 8.
|
|
104
111
|
def serial_trap_device
|
|
105
|
-
|
|
112
|
+
device = KernalTrap::Routine::DEVICE
|
|
113
|
+
@drive1541&.device == device || drive1581_on_device8? ? nil : device
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# A 1581 plugged in, as a 1541 does, holds the machine's on_init
|
|
117
|
+
# handlers back until its DOS has booted.
|
|
118
|
+
def drive1581_attached
|
|
119
|
+
@init_threshold = [@init_threshold, Drive1581::BOOT_CYCLES].max if @cycles < @init_threshold
|
|
106
120
|
end
|
|
107
121
|
end
|
|
108
122
|
end
|
|
@@ -78,6 +78,7 @@ module Badline
|
|
|
78
78
|
@datasette.save_state(out)
|
|
79
79
|
save_trap_drive(out)
|
|
80
80
|
save_drive1541(out)
|
|
81
|
+
save_drive1581(out)
|
|
81
82
|
end
|
|
82
83
|
|
|
83
84
|
def load_state(input)
|
|
@@ -99,6 +100,7 @@ module Badline
|
|
|
99
100
|
@datasette.load_state(input)
|
|
100
101
|
load_trap_drive(input)
|
|
101
102
|
load_drive1541(input)
|
|
103
|
+
load_drive1581(input)
|
|
102
104
|
end
|
|
103
105
|
|
|
104
106
|
private
|
data/lib/badline/vic20.rb
CHANGED
|
@@ -75,6 +75,7 @@ module Badline
|
|
|
75
75
|
@save_trap = nil
|
|
76
76
|
@capture_output = nil
|
|
77
77
|
@drive1541 = nil
|
|
78
|
+
@drive1581 = nil
|
|
78
79
|
@init_threshold = boot_cycles
|
|
79
80
|
wire_ports
|
|
80
81
|
end
|
|
@@ -94,6 +95,7 @@ module Badline
|
|
|
94
95
|
drive_nmi
|
|
95
96
|
@cpu.cycle!
|
|
96
97
|
@drive1541&.host_cycle!
|
|
98
|
+
@drive1581&.host_cycle!
|
|
97
99
|
|
|
98
100
|
@cycles += 1
|
|
99
101
|
end
|
|
@@ -176,6 +178,7 @@ module Badline
|
|
|
176
178
|
via_written
|
|
177
179
|
@drive&.reset!
|
|
178
180
|
@drive1541&.reset!
|
|
181
|
+
@drive1581&.reset!
|
|
179
182
|
@nmi_asserted = false
|
|
180
183
|
@cpu.reset!
|
|
181
184
|
end
|
data/lib/badline/z80/core.rb
CHANGED
|
@@ -14,6 +14,7 @@ require "badline/z80/extended_page"
|
|
|
14
14
|
require "badline/z80/block_transfers"
|
|
15
15
|
require "badline/z80/block_io"
|
|
16
16
|
require "badline/z80/interrupt_acceptance"
|
|
17
|
+
require "badline/z80/saved_state"
|
|
17
18
|
|
|
18
19
|
module Badline
|
|
19
20
|
class Z80
|
|
@@ -42,6 +43,7 @@ module Badline
|
|
|
42
43
|
include BlockTransfers
|
|
43
44
|
include BlockIO
|
|
44
45
|
include InterruptAcceptance
|
|
46
|
+
include SavedState
|
|
45
47
|
|
|
46
48
|
attr_reader :bus, :cycles, :nmi
|
|
47
49
|
attr_accessor :int
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
class Z80
|
|
5
|
+
# The registers, the T-state count and the interrupt latches for a
|
|
6
|
+
# snapshot, taken between instructions.
|
|
7
|
+
module SavedState
|
|
8
|
+
def save_state(out)
|
|
9
|
+
out.marker("Z80")
|
|
10
|
+
out.int(@a).int(@f).int(@b).int(@c).int(@d).int(@e).int(@h).int(@l)
|
|
11
|
+
out.int(@af_alt).int(@bc_alt).int(@de_alt).int(@hl_alt)
|
|
12
|
+
out.int(@i).int(@r).int(@pc).int(@sp).int(@ix).int(@iy).int(@wz).int(@q).int(@last_q).int(@im)
|
|
13
|
+
out.int(@cycles)
|
|
14
|
+
out.boolean(@iff1).boolean(@iff2).boolean(@halted).boolean(@after_ei).boolean(@after_ld_a_ir)
|
|
15
|
+
out.boolean(@int).boolean(@nmi).boolean(@nmi_pending)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def load_state(input)
|
|
19
|
+
input.marker("Z80")
|
|
20
|
+
load_main_registers(input)
|
|
21
|
+
@af_alt = input.int
|
|
22
|
+
@bc_alt = input.int
|
|
23
|
+
@de_alt = input.int
|
|
24
|
+
@hl_alt = input.int
|
|
25
|
+
load_special_registers(input)
|
|
26
|
+
@cycles = input.int
|
|
27
|
+
load_latches(input)
|
|
28
|
+
@prefix = 0
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
private
|
|
32
|
+
|
|
33
|
+
def load_main_registers(input)
|
|
34
|
+
@a = input.int
|
|
35
|
+
@f = input.int
|
|
36
|
+
@b = input.int
|
|
37
|
+
@c = input.int
|
|
38
|
+
@d = input.int
|
|
39
|
+
@e = input.int
|
|
40
|
+
@h = input.int
|
|
41
|
+
@l = input.int
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def load_special_registers(input)
|
|
45
|
+
@i = input.int
|
|
46
|
+
@r = input.int
|
|
47
|
+
@pc = input.int
|
|
48
|
+
@sp = input.int
|
|
49
|
+
@ix = input.int
|
|
50
|
+
@iy = input.int
|
|
51
|
+
@wz = input.int
|
|
52
|
+
@q = input.int
|
|
53
|
+
@last_q = input.int
|
|
54
|
+
@im = input.int
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def load_latches(input)
|
|
58
|
+
@iff1 = input.boolean?
|
|
59
|
+
@iff2 = input.boolean?
|
|
60
|
+
@halted = input.boolean?
|
|
61
|
+
@after_ei = input.boolean?
|
|
62
|
+
@after_ld_a_ir = input.boolean?
|
|
63
|
+
@int = input.boolean?
|
|
64
|
+
@nmi = input.boolean?
|
|
65
|
+
@nmi_pending = input.boolean?
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|