badline 0.5.1 → 0.6.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 +12 -0
- data/README.md +58 -0
- data/exe/badline-ruby +2 -1
- data/lib/badline/address_bus/saved_state.rb +72 -0
- data/lib/badline/address_bus.rb +1 -0
- data/lib/badline/audio/stream.rb +6 -0
- data/lib/badline/cartridge/action_replay.rb +15 -0
- data/lib/badline/cartridge/comal80.rb +8 -0
- data/lib/badline/cartridge/dinamic.rb +8 -0
- data/lib/badline/cartridge/easy_flash.rb +20 -1
- data/lib/badline/cartridge/epyx_fastload.rb +8 -0
- data/lib/badline/cartridge/final_cartridge3.rb +12 -0
- data/lib/badline/cartridge/flash.rb +41 -0
- data/lib/badline/cartridge/fun_play.rb +8 -0
- data/lib/badline/cartridge/g_mod2.rb +12 -0
- data/lib/badline/cartridge/game_system.rb +8 -0
- data/lib/badline/cartridge/geo_ram.rb +16 -0
- data/lib/badline/cartridge/magic_desk.rb +8 -0
- data/lib/badline/cartridge/ocean.rb +8 -0
- data/lib/badline/cartridge/pagefox.rb +12 -0
- data/lib/badline/cartridge/retro_replay.rb +44 -1
- data/lib/badline/cartridge/rgcd.rb +10 -0
- data/lib/badline/cartridge/super_games.rb +10 -0
- data/lib/badline/cartridge/zaxxon.rb +16 -0
- data/lib/badline/cartridge.rb +72 -0
- data/lib/badline/cia/interrupt_register.rb +17 -0
- data/lib/badline/cia/serial.rb +44 -0
- data/lib/badline/cia/state.rb +47 -0
- data/lib/badline/cia/timer.rb +33 -0
- data/lib/badline/cia.rb +8 -0
- data/lib/badline/computer/attachments.rb +132 -0
- data/lib/badline/computer/saved_state.rb +116 -0
- data/lib/badline/computer.rb +7 -30
- data/lib/badline/cpu/microcode.rb +8 -0
- data/lib/badline/cpu/saved_state.rb +115 -0
- data/lib/badline/cpu.rb +2 -0
- data/lib/badline/datasette.rb +27 -0
- data/lib/badline/drive1541/bus.rb +13 -0
- data/lib/badline/drive1541/disk.rb +7 -2
- data/lib/badline/drive1541/disk_state.rb +80 -0
- data/lib/badline/drive1541/mechanism.rb +2 -0
- data/lib/badline/drive1541/mechanism_state.rb +67 -0
- data/lib/badline/drive1541/rotation.rb +10 -0
- data/lib/badline/drive1541/serial_port.rb +10 -0
- data/lib/badline/drive1541.rb +34 -0
- data/lib/badline/gui/application.rb +41 -14
- data/lib/badline/gui/gamepads.rb +7 -0
- data/lib/badline/gui/snapshots.rb +67 -0
- data/lib/badline/gui.rb +1 -0
- data/lib/badline/kernal_trap/channel.rb +27 -0
- data/lib/badline/kernal_trap/drive/channels.rb +25 -0
- data/lib/badline/kernal_trap/drive/memory.rb +9 -0
- data/lib/badline/kernal_trap/drive/status.rb +8 -0
- data/lib/badline/kernal_trap/drive/write_file.rb +21 -0
- data/lib/badline/kernal_trap/drive.rb +19 -0
- data/lib/badline/kernal_trap/save.rb +8 -0
- data/lib/badline/kernal_trap/serial.rb +16 -0
- data/lib/badline/memory.rb +8 -0
- data/lib/badline/options.rb +3 -2
- data/lib/badline/ram_expansion/plus256k.rb +16 -0
- data/lib/badline/ram_expansion/plus60k.rb +12 -0
- data/lib/badline/ram_expansion/unexpanded.rb +6 -0
- data/lib/badline/reu/dma.rb +36 -0
- data/lib/badline/reu/ram.rb +15 -0
- data/lib/badline/reu/saved_state.rb +52 -0
- data/lib/badline/reu.rb +4 -0
- data/lib/badline/sdl/events.rb +2 -0
- data/lib/badline/sid/envelope/state.rb +68 -0
- data/lib/badline/sid/envelope.rb +7 -0
- data/lib/badline/sid/filter.rb +38 -0
- data/lib/badline/sid/voice.rb +10 -0
- data/lib/badline/sid/waveform/state.rb +66 -0
- data/lib/badline/sid/waveform.rb +2 -0
- data/lib/badline/sid.rb +34 -0
- data/lib/badline/snapshot/container.rb +117 -0
- data/lib/badline/snapshot/fields.rb +54 -0
- data/lib/badline/snapshot/image.rb +70 -0
- data/lib/badline/snapshot/machine_state.rb +77 -0
- data/lib/badline/snapshot/setup.rb +58 -0
- data/lib/badline/snapshot/state.rb +178 -0
- data/lib/badline/snapshot/vice/c64_mem.rb +53 -0
- data/lib/badline/snapshot/vice/cias.rb +89 -0
- data/lib/badline/snapshot/vice/cias_import.rb +70 -0
- data/lib/badline/snapshot/vice/main_cpu.rb +76 -0
- data/lib/badline/snapshot/vice/peripherals.rb +47 -0
- data/lib/badline/snapshot/vice/restore.rb +125 -0
- data/lib/badline/snapshot/vice/sid_extended.rb +96 -0
- data/lib/badline/snapshot/vice/sid_registers.rb +46 -0
- data/lib/badline/snapshot/vice/vicii.rb +119 -0
- data/lib/badline/snapshot/vice/vicii_import.rb +131 -0
- data/lib/badline/snapshot/vice.rb +74 -0
- data/lib/badline/snapshot.rb +52 -0
- data/lib/badline/storage/crt_file.rb +9 -3
- data/lib/badline/storage/d64_image.rb +2 -0
- data/lib/badline/storage/d71_image.rb +2 -0
- data/lib/badline/storage/d81_image.rb +2 -0
- data/lib/badline/storage/disk_image.rb +11 -2
- data/lib/badline/storage/host_directory.rb +5 -0
- data/lib/badline/storage/t64.rb +6 -0
- data/lib/badline/storage/tap.rb +10 -1
- data/lib/badline/storage/unavailable.rb +20 -0
- data/lib/badline/storage.rb +34 -0
- data/lib/badline/time_of_day.rb +56 -0
- data/lib/badline/version.rb +1 -1
- data/lib/badline/via/control_lines.rb +15 -0
- data/lib/badline/via/interrupt_register.rb +9 -0
- data/lib/badline/via/shift_register.rb +13 -0
- data/lib/badline/via/state.rb +42 -0
- data/lib/badline/via/timer1.rb +13 -0
- data/lib/badline/via/timer2.rb +18 -0
- data/lib/badline/via.rb +2 -0
- data/lib/badline/vic/border_mask.rb +10 -0
- data/lib/badline/vic/collisions.rb +11 -0
- data/lib/badline/vic/color_patches.rb +11 -0
- data/lib/badline/vic/display_state.rb +29 -1
- data/lib/badline/vic/graphics_shifter.rb +26 -0
- data/lib/badline/vic/register_log.rb +18 -0
- data/lib/badline/vic/registers.rb +13 -2
- data/lib/badline/vic/sequencer.rb +12 -0
- data/lib/badline/vic/sequencer_state.rb +89 -0
- data/lib/badline/vic/sprite/internal_bus.rb +11 -0
- data/lib/badline/vic/sprite/saved_state.rb +64 -0
- data/lib/badline/vic/sprite.rb +2 -0
- data/lib/badline/vic/sprites.rb +3 -0
- data/lib/badline/vic/sprites_state.rb +57 -0
- data/lib/badline/vic.rb +94 -10
- data/lib/badline.rb +1 -0
- metadata +35 -1
|
@@ -80,6 +80,33 @@ module Badline
|
|
|
80
80
|
data ? new(data) : new([], error: [Drive::ILLEGAL_TRACK_OR_SECTOR, track, sector])
|
|
81
81
|
end
|
|
82
82
|
|
|
83
|
+
# Reads what save_state wrote after the flag that marks a file open
|
|
84
|
+
# for writing (Drive::Channels.channel_from_state). A buffer channel
|
|
85
|
+
# takes its block in `ram`, the drive's RAM, again.
|
|
86
|
+
def self.from_state(input, ram)
|
|
87
|
+
new.tap { |channel| channel.load_state(input, ram) }
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# A buffer channel's bytes are the drive's RAM, which the drive
|
|
91
|
+
# writes itself.
|
|
92
|
+
def save_state(out)
|
|
93
|
+
out.boolean(false).boolean(@writable).int(@base).int(@length).int(@pointer)
|
|
94
|
+
out.optional_int(@lead).optional_int(@buffer).boolean(!@error.nil?)
|
|
95
|
+
out.ints(@error) if @error
|
|
96
|
+
out.blob(@bytes) unless @writable
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def load_state(input, ram)
|
|
100
|
+
@writable = input.boolean?
|
|
101
|
+
@base = input.int
|
|
102
|
+
@length = input.int
|
|
103
|
+
@pointer = input.int
|
|
104
|
+
@lead = input.optional_int
|
|
105
|
+
@buffer = input.optional_int
|
|
106
|
+
@error = input.boolean? ? input.ints : nil
|
|
107
|
+
@bytes = @writable ? ram : input.blob
|
|
108
|
+
end
|
|
109
|
+
|
|
83
110
|
def replace(bytes, length = bytes.length)
|
|
84
111
|
@bytes = bytes
|
|
85
112
|
rewind(length)
|
|
@@ -22,6 +22,31 @@ module Badline
|
|
|
22
22
|
|
|
23
23
|
def clear = @channels.clear
|
|
24
24
|
|
|
25
|
+
# Each open channel with its secondary address, in the order they
|
|
26
|
+
# opened.
|
|
27
|
+
def save_state(out)
|
|
28
|
+
out.int(@channels.length)
|
|
29
|
+
@channels.each do |secondary, channel|
|
|
30
|
+
out.int(secondary)
|
|
31
|
+
channel.save_state(out)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Buffer channels take their block in the drive's RAM again.
|
|
36
|
+
def load_state(input, memory)
|
|
37
|
+
@channels.clear
|
|
38
|
+
input.int.times do
|
|
39
|
+
secondary = input.int
|
|
40
|
+
@channels[secondary] = Channels.channel_from_state(input, memory.ram)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# A channel save_state wrote: a file open for writing, or a Channel
|
|
45
|
+
# whose buffer, if it has one, is in `ram`.
|
|
46
|
+
def self.channel_from_state(input, ram)
|
|
47
|
+
input.boolean? ? WriteFile.from_state(input) : Channel.from_state(input, ram)
|
|
48
|
+
end
|
|
49
|
+
|
|
25
50
|
# Opens a buffer channel on the buffer a "#n" name asks for, or on
|
|
26
51
|
# the highest free one. Returns nil when that buffer isn't free,
|
|
27
52
|
# which leaves a channel asking again for its own buffer open.
|
|
@@ -62,6 +62,15 @@ module Badline
|
|
|
62
62
|
load_buffer(Channels::BAM_BUFFER, block) if block
|
|
63
63
|
end
|
|
64
64
|
|
|
65
|
+
def save_state(out)
|
|
66
|
+
out.blob(@ram).boolean(@bam)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def load_state(input)
|
|
70
|
+
input.blob_into(@ram)
|
|
71
|
+
@bam = input.boolean?
|
|
72
|
+
end
|
|
73
|
+
|
|
65
74
|
# Whether the BAM has been read since the last reset.
|
|
66
75
|
def bam? = @bam
|
|
67
76
|
|
|
@@ -11,6 +11,17 @@ module Badline
|
|
|
11
11
|
attr_accessor :buffer
|
|
12
12
|
attr_reader :name, :type, :mode
|
|
13
13
|
|
|
14
|
+
TYPES = %i[del seq prg usr rel].freeze
|
|
15
|
+
MODES = %i[write replace append].freeze
|
|
16
|
+
|
|
17
|
+
# Reads what save_state wrote after the flag that marks a file
|
|
18
|
+
# open for writing.
|
|
19
|
+
def self.from_state(input)
|
|
20
|
+
name = input.string
|
|
21
|
+
type = TYPES.fetch(input.int).to_sym
|
|
22
|
+
new(name, type, mode: MODES.fetch(input.int).to_sym).tap { |file| file.load_state(input) }
|
|
23
|
+
end
|
|
24
|
+
|
|
14
25
|
def initialize(name, type, mode: :write)
|
|
15
26
|
@name = name
|
|
16
27
|
@type = type
|
|
@@ -18,6 +29,16 @@ module Badline
|
|
|
18
29
|
@bytes = []
|
|
19
30
|
end
|
|
20
31
|
|
|
32
|
+
def save_state(out)
|
|
33
|
+
out.boolean(true).string(@name).int(TYPES.index(@type)).int(MODES.index(@mode))
|
|
34
|
+
out.blob(@bytes).optional_int(@buffer)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def load_state(input)
|
|
38
|
+
@bytes = input.blob
|
|
39
|
+
@buffer = input.optional_int
|
|
40
|
+
end
|
|
41
|
+
|
|
21
42
|
def write(bytes) = @bytes.concat(bytes)
|
|
22
43
|
|
|
23
44
|
def writable? = true
|
|
@@ -143,6 +143,25 @@ module Badline
|
|
|
143
143
|
# vector, as U: does.
|
|
144
144
|
def reset! = reset(cold: true)
|
|
145
145
|
|
|
146
|
+
# The mounted storage's setup, for Storage.reopen, then the drive's
|
|
147
|
+
# RAM, its status and its open channels.
|
|
148
|
+
def save_state(out)
|
|
149
|
+
@storage.save_setup(out)
|
|
150
|
+
out.marker("TRAP DRIVE")
|
|
151
|
+
@memory.save_state(out)
|
|
152
|
+
@status.save_state(out)
|
|
153
|
+
@channels.save_state(out)
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
# Takes the state after the storage's setup, into a drive mounted on
|
|
157
|
+
# the reopened storage.
|
|
158
|
+
def load_state(input)
|
|
159
|
+
input.marker("TRAP DRIVE")
|
|
160
|
+
@memory.load_state(input)
|
|
161
|
+
@status.load_state(input)
|
|
162
|
+
@channels.load_state(input, @memory)
|
|
163
|
+
end
|
|
164
|
+
|
|
146
165
|
# Returns the next byte and whether it is the channel's last, or nil
|
|
147
166
|
# when there is nothing left to send.
|
|
148
167
|
def read(secondary)
|
|
@@ -46,6 +46,22 @@ module Badline
|
|
|
46
46
|
@buffer = []
|
|
47
47
|
end
|
|
48
48
|
|
|
49
|
+
# The frame under way, with the channels and the bytes it gathered.
|
|
50
|
+
# The device it answers is the machine's to set.
|
|
51
|
+
def save_state(out)
|
|
52
|
+
out.boolean(@listening).boolean(@talking).optional_int(@listen_channel).optional_int(@talk_channel)
|
|
53
|
+
out.int(@frame).blob(@buffer)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def load_state(input)
|
|
57
|
+
@listening = input.boolean?
|
|
58
|
+
@talking = input.boolean?
|
|
59
|
+
@listen_channel = input.optional_int
|
|
60
|
+
@talk_channel = input.optional_int
|
|
61
|
+
@frame = input.int
|
|
62
|
+
@buffer = input.blob
|
|
63
|
+
end
|
|
64
|
+
|
|
49
65
|
def install
|
|
50
66
|
ROUTINES.each do |address, routine|
|
|
51
67
|
@cpu.install_trap(address) { call_routine(routine) if kernal? }
|
data/lib/badline/memory.rb
CHANGED
|
@@ -29,6 +29,14 @@ module Badline
|
|
|
29
29
|
Array(bytes).each_with_index { |b, i| poke(addr + i, b) }
|
|
30
30
|
end
|
|
31
31
|
|
|
32
|
+
def save_state(out)
|
|
33
|
+
out.blob(@storage)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def load_state(input)
|
|
37
|
+
input.blob_into(@storage)
|
|
38
|
+
end
|
|
39
|
+
|
|
32
40
|
# Refills the whole memory as if newly built with `initial`
|
|
33
41
|
def clear!(initial = [])
|
|
34
42
|
@storage.replace(zero_fill(initial))
|
data/lib/badline/options.rb
CHANGED
|
@@ -23,8 +23,9 @@ module Badline
|
|
|
23
23
|
|
|
24
24
|
Media can be a .prg/.p00 program, a .d64/.d71/.d81 disk image, a
|
|
25
25
|
.g64 disk image for the true 1541, a .t64 tape archive, a .tap
|
|
26
|
-
tape, a .crt cartridge, a .sid tune, or a directory
|
|
27
|
-
device 8. It opens in the emulator window
|
|
26
|
+
tape, a .crt cartridge, a .sid tune, a .vsf snapshot, or a directory
|
|
27
|
+
to mount as device 8. It opens in the emulator window, where F11
|
|
28
|
+
saves a snapshot and F12 restores it. Device 8 answers through
|
|
28
29
|
traps on the KERNAL's disk routines, unless --true-drive puts an
|
|
29
30
|
emulated 1541 there, which runs its own DOS and reads .d64 and .g64
|
|
30
31
|
images only.
|
|
@@ -30,6 +30,22 @@ module Badline
|
|
|
30
30
|
@locked = false
|
|
31
31
|
end
|
|
32
32
|
|
|
33
|
+
def type = :plus256k
|
|
34
|
+
|
|
35
|
+
def save_state(out)
|
|
36
|
+
[@low_ram, @video_ram, @high_ram].each { |bank| out.int(@banks.index(bank)) }
|
|
37
|
+
out.boolean(@locked)
|
|
38
|
+
@banks.drop(1).each { |bank| bank.save_state(out) }
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def load_state(input)
|
|
42
|
+
@low_ram = @banks.fetch(input.int)
|
|
43
|
+
@video_ram = @banks.fetch(input.int)
|
|
44
|
+
@high_ram = @banks.fetch(input.int)
|
|
45
|
+
@locked = input.boolean?
|
|
46
|
+
@banks.drop(1).each { |bank| bank.load_state(input) }
|
|
47
|
+
end
|
|
48
|
+
|
|
33
49
|
def power_on!
|
|
34
50
|
@banks.drop(1).each(&:clear!)
|
|
35
51
|
end
|
|
@@ -23,6 +23,18 @@ module Badline
|
|
|
23
23
|
@high_ram = @banks[0]
|
|
24
24
|
end
|
|
25
25
|
|
|
26
|
+
def type = :plus60k
|
|
27
|
+
|
|
28
|
+
def save_state(out)
|
|
29
|
+
out.int(@banks.index(@high_ram))
|
|
30
|
+
@banks[1].save_state(out)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def load_state(input)
|
|
34
|
+
@high_ram = @banks.fetch(input.int)
|
|
35
|
+
@banks[1].load_state(input)
|
|
36
|
+
end
|
|
37
|
+
|
|
26
38
|
def power_on!
|
|
27
39
|
@banks[1].clear!
|
|
28
40
|
end
|
data/lib/badline/reu/dma.rb
CHANGED
|
@@ -69,8 +69,44 @@ module Badline
|
|
|
69
69
|
@moved_all ? wind_down : move(ba_low)
|
|
70
70
|
end
|
|
71
71
|
|
|
72
|
+
# Everything a transfer holds between cycles. Before the first
|
|
73
|
+
# transfer, the fields it sets on starting are empty.
|
|
74
|
+
def save_state(out)
|
|
75
|
+
out.int(@c64).int(@expansion).int(@length).int(@events)
|
|
76
|
+
[@type, @c64_step, @expansion_step, @last, @ba_low_after_writes, @swap_byte, @held_read].each do |value|
|
|
77
|
+
out.optional_int(value)
|
|
78
|
+
end
|
|
79
|
+
[@holding, @waiting, @stopped_after_write, @swap_write_due, @moved_all, @extra_cycle].each do |flag|
|
|
80
|
+
out.boolean(flag)
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def load_state(input)
|
|
85
|
+
@c64 = input.int
|
|
86
|
+
@expansion = input.int
|
|
87
|
+
@length = input.int
|
|
88
|
+
@events = input.int
|
|
89
|
+
load_steps(input)
|
|
90
|
+
@holding = input.boolean?
|
|
91
|
+
@waiting = input.boolean?
|
|
92
|
+
@stopped_after_write = input.boolean?
|
|
93
|
+
@swap_write_due = input.boolean?
|
|
94
|
+
@moved_all = input.boolean?
|
|
95
|
+
@extra_cycle = input.boolean?
|
|
96
|
+
end
|
|
97
|
+
|
|
72
98
|
private
|
|
73
99
|
|
|
100
|
+
def load_steps(input)
|
|
101
|
+
@type = input.optional_int
|
|
102
|
+
@c64_step = input.optional_int
|
|
103
|
+
@expansion_step = input.optional_int
|
|
104
|
+
@last = input.optional_int
|
|
105
|
+
@ba_low_after_writes = input.optional_int
|
|
106
|
+
@swap_byte = input.optional_int
|
|
107
|
+
@held_read = input.optional_int
|
|
108
|
+
end
|
|
109
|
+
|
|
74
110
|
# The second BA-low cycle in a row after writes stops the REC, and so
|
|
75
111
|
# does any BA-low cycle after a read. It then waits for BA to rise.
|
|
76
112
|
def note_ba(ba_low)
|
data/lib/badline/reu/ram.rb
CHANGED
|
@@ -37,6 +37,21 @@ module Badline
|
|
|
37
37
|
bank(addr >> 16)[addr & 0xffff] = value if addr < @size
|
|
38
38
|
end
|
|
39
39
|
|
|
40
|
+
# The latch, and each 64K bank the REC has touched. A bank it hasn't
|
|
41
|
+
# keeps its power-on contents until it does.
|
|
42
|
+
def save_state(out)
|
|
43
|
+
out.int(@latch)
|
|
44
|
+
@banks.each do |bank|
|
|
45
|
+
out.boolean(!bank.empty?)
|
|
46
|
+
out.blob(bank) unless bank.empty?
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def load_state(input)
|
|
51
|
+
@latch = input.int
|
|
52
|
+
@banks.each_index { |number| @banks[number] = input.boolean? ? input.blob : [] }
|
|
53
|
+
end
|
|
54
|
+
|
|
40
55
|
private
|
|
41
56
|
|
|
42
57
|
# A bank takes its power-on contents the first time it is touched,
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
class REU
|
|
5
|
+
# The REU's state for a snapshot: the REC's registers and the values
|
|
6
|
+
# its counters start from, an armed or running transfer, the IRQ line,
|
|
7
|
+
# the transfer's own state and the RAM. The size is the machine's
|
|
8
|
+
# setup (Snapshot::Setup), so a restore finds an REU of the same size.
|
|
9
|
+
module SavedState
|
|
10
|
+
def size_kb = @size_kb
|
|
11
|
+
|
|
12
|
+
def save_state(out)
|
|
13
|
+
out.marker("REU")
|
|
14
|
+
[@status, @command, @c64, @c64_start, @expansion, @expansion_start, @bank, @bank_start, @length,
|
|
15
|
+
@length_start, @interrupt_mask, @address_control].each { |value| out.int(value) }
|
|
16
|
+
out.boolean(@armed).boolean(@requested).boolean(@running).boolean(@irq)
|
|
17
|
+
@dma.save_state(out)
|
|
18
|
+
@ram.save_state(out)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# The IRQ line comes back as it was without calling back into the
|
|
22
|
+
# machine, which reads it again afterwards.
|
|
23
|
+
def load_state(input)
|
|
24
|
+
input.marker("REU")
|
|
25
|
+
load_registers(input)
|
|
26
|
+
@armed = input.boolean?
|
|
27
|
+
@requested = input.boolean?
|
|
28
|
+
@running = input.boolean?
|
|
29
|
+
@irq = input.boolean?
|
|
30
|
+
@dma.load_state(input)
|
|
31
|
+
@ram.load_state(input)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
private
|
|
35
|
+
|
|
36
|
+
def load_registers(input)
|
|
37
|
+
@status = input.int
|
|
38
|
+
@command = input.int
|
|
39
|
+
@c64 = input.int
|
|
40
|
+
@c64_start = input.int
|
|
41
|
+
@expansion = input.int
|
|
42
|
+
@expansion_start = input.int
|
|
43
|
+
@bank = input.int
|
|
44
|
+
@bank_start = input.int
|
|
45
|
+
@length = input.int
|
|
46
|
+
@length_start = input.int
|
|
47
|
+
@interrupt_mask = input.int
|
|
48
|
+
@address_control = input.int
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
data/lib/badline/reu.rb
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
require "badline/reu/ram"
|
|
4
4
|
require "badline/reu/dma"
|
|
5
5
|
require "badline/reu/trigger"
|
|
6
|
+
require "badline/reu/saved_state"
|
|
6
7
|
|
|
7
8
|
module Badline
|
|
8
9
|
# A RAM Expansion Unit: the Commodore 1700 (128K), 1764 (256K) and 1750
|
|
@@ -18,6 +19,8 @@ module Badline
|
|
|
18
19
|
# waits for the CPU's next read cycle with BA high, and Computer then
|
|
19
20
|
# clocks it through #dma_cycle! until it hands the bus back.
|
|
20
21
|
class REU
|
|
22
|
+
include SavedState
|
|
23
|
+
|
|
21
24
|
SIZES_KB = [128, 256, 512, 1024, 2048, 4096, 8192, 16_384].freeze
|
|
22
25
|
|
|
23
26
|
# The registers, by offset.
|
|
@@ -74,6 +77,7 @@ module Badline
|
|
|
74
77
|
|
|
75
78
|
# The 1700's REC counts 17 address bits, the others' 19.
|
|
76
79
|
span = size_kb == 128 ? 0x20000 : 0x80000
|
|
80
|
+
@size_kb = size_kb
|
|
77
81
|
size = size_kb * 1024
|
|
78
82
|
@address_mask = [size, span].max - 1
|
|
79
83
|
@bank_bits = size > span ? 0xff : 0x07
|
data/lib/badline/sdl/events.rb
CHANGED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
class SID
|
|
5
|
+
class Envelope
|
|
6
|
+
# A snapshot of an envelope: its rates, the gate, the state and the
|
|
7
|
+
# counters and pipelines between them, and the same fields as reSID
|
|
8
|
+
# keeps them, for VICE's SIDEXTENDED module.
|
|
9
|
+
module State
|
|
10
|
+
# The fields reSID keeps for an envelope between cycles: the rate and
|
|
11
|
+
# exponential counters, the level, the state as an index into STATES,
|
|
12
|
+
# whether it holds at zero, the two periods and the pipeline.
|
|
13
|
+
def resid_fields
|
|
14
|
+
[@rate_counter, @exponential_counter, @counter, STATES.index(@state).to_i, @hold_zero ? 1 : 0,
|
|
15
|
+
@rate_period, @exponential_period, @envelope_pipeline]
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Takes the fields resid_fields gives from `fields`, from `at` on.
|
|
19
|
+
def restore_resid_fields(fields, at)
|
|
20
|
+
@rate_counter = fields[at]
|
|
21
|
+
@exponential_counter = fields[at + 1]
|
|
22
|
+
@counter = @env3 = fields[at + 2]
|
|
23
|
+
state = fields[at + 3]
|
|
24
|
+
@state = @next_state = STATES.fetch(state.clamp(0, STATES.length - 1)).to_sym
|
|
25
|
+
@hold_zero = fields[at + 4] != 0
|
|
26
|
+
@rate_period = fields[at + 5]
|
|
27
|
+
@exponential_period = fields[at + 6]
|
|
28
|
+
@envelope_pipeline = fields[at + 7]
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def save_state(out)
|
|
32
|
+
[@attack, @decay, @sustain, @release].each { |rate| out.int(rate) }
|
|
33
|
+
out.boolean(@gate).int(STATES.index(@state)).int(STATES.index(@next_state))
|
|
34
|
+
out.int(@counter).int(@env3).int(@rate_counter).int(@rate_period).boolean(@reset_rate_counter)
|
|
35
|
+
out.int(@exponential_counter).int(@exponential_period)
|
|
36
|
+
out.int(@state_pipeline).int(@envelope_pipeline).int(@exponential_pipeline).boolean(@hold_zero)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def load_state(input)
|
|
40
|
+
@attack = input.int
|
|
41
|
+
@decay = input.int
|
|
42
|
+
@sustain = input.int
|
|
43
|
+
@release = input.int
|
|
44
|
+
@gate = input.boolean?
|
|
45
|
+
@state = STATES.fetch(input.int).to_sym
|
|
46
|
+
@next_state = STATES.fetch(input.int).to_sym
|
|
47
|
+
load_counters(input)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
private
|
|
51
|
+
|
|
52
|
+
def load_counters(input)
|
|
53
|
+
@counter = input.int
|
|
54
|
+
@env3 = input.int
|
|
55
|
+
@rate_counter = input.int
|
|
56
|
+
@rate_period = input.int
|
|
57
|
+
@reset_rate_counter = input.boolean?
|
|
58
|
+
@exponential_counter = input.int
|
|
59
|
+
@exponential_period = input.int
|
|
60
|
+
@state_pipeline = input.int
|
|
61
|
+
@envelope_pipeline = input.int
|
|
62
|
+
@exponential_pipeline = input.int
|
|
63
|
+
@hold_zero = input.boolean?
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
data/lib/badline/sid/envelope.rb
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "badline/sid/envelope/state"
|
|
4
|
+
|
|
3
5
|
module Badline
|
|
4
6
|
class SID
|
|
5
7
|
# ADSR envelope generator.
|
|
@@ -25,6 +27,11 @@ module Badline
|
|
|
25
27
|
EXPONENTIAL_PERIODS = { 0xff => 1, 0x5d => 2, 0x36 => 4, 0x1a => 8,
|
|
26
28
|
0x0e => 16, 0x06 => 30, 0x00 => 1 }.freeze
|
|
27
29
|
|
|
30
|
+
# The states a snapshot names by index.
|
|
31
|
+
STATES = %i[attack decay_sustain release].freeze
|
|
32
|
+
|
|
33
|
+
include State
|
|
34
|
+
|
|
28
35
|
attr_reader :state, :counter, :env3
|
|
29
36
|
|
|
30
37
|
def initialize
|
data/lib/badline/sid/filter.rb
CHANGED
|
@@ -90,6 +90,16 @@ module Badline
|
|
|
90
90
|
@output = 0
|
|
91
91
|
end
|
|
92
92
|
|
|
93
|
+
def save_state(out)
|
|
94
|
+
out.int(@lowpass).int(@highpass).int(@output)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def load_state(input)
|
|
98
|
+
@lowpass = input.int
|
|
99
|
+
@highpass = input.int
|
|
100
|
+
@output = input.int
|
|
101
|
+
end
|
|
102
|
+
|
|
93
103
|
# Takes the input in 1/2^Filter::FRACTION units, and reads the
|
|
94
104
|
# output out in whole ones.
|
|
95
105
|
def cycle!(input, cycles = 1)
|
|
@@ -124,6 +134,25 @@ module Badline
|
|
|
124
134
|
@input = @unfiltered = 0
|
|
125
135
|
end
|
|
126
136
|
|
|
137
|
+
# The registers as the filter decoded them and its integrators. The
|
|
138
|
+
# W0 table is the model's.
|
|
139
|
+
def save_state(out)
|
|
140
|
+
out.int(@cutoff).int(@routing).int(@mode).int(@volume).boolean(@voice3_off).int(@w0).int(@resonance)
|
|
141
|
+
[@highpass, @bandpass, @lowpass, @unfiltered, @input].each { |value| out.int(value) }
|
|
142
|
+
@external.save_state(out)
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def load_state(input)
|
|
146
|
+
@cutoff = input.int
|
|
147
|
+
@routing = input.int
|
|
148
|
+
@mode = input.int
|
|
149
|
+
@volume = input.int
|
|
150
|
+
@voice3_off = input.boolean?
|
|
151
|
+
@w0 = input.int
|
|
152
|
+
@resonance = input.int
|
|
153
|
+
load_integrators(input)
|
|
154
|
+
end
|
|
155
|
+
|
|
127
156
|
def write(reg, value)
|
|
128
157
|
case reg
|
|
129
158
|
when 0x15 then self.cutoff = (@cutoff & 0x7f8) | (value & 0x07)
|
|
@@ -163,6 +192,15 @@ module Badline
|
|
|
163
192
|
|
|
164
193
|
private
|
|
165
194
|
|
|
195
|
+
def load_integrators(input)
|
|
196
|
+
@highpass = input.int
|
|
197
|
+
@bandpass = input.int
|
|
198
|
+
@lowpass = input.int
|
|
199
|
+
@unfiltered = input.int
|
|
200
|
+
@input = input.int
|
|
201
|
+
@external.load_state(input)
|
|
202
|
+
end
|
|
203
|
+
|
|
166
204
|
def write_resonance(value)
|
|
167
205
|
@routing = value & 0x0f
|
|
168
206
|
@resonance = RESONANCE[(value >> 4) & 0x0f]
|
data/lib/badline/sid/voice.rb
CHANGED
|
@@ -38,6 +38,16 @@ module Badline
|
|
|
38
38
|
@envelope = Envelope.new
|
|
39
39
|
end
|
|
40
40
|
|
|
41
|
+
def save_state(out)
|
|
42
|
+
@waveform.save_state(out)
|
|
43
|
+
@envelope.save_state(out)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def load_state(input)
|
|
47
|
+
@waveform.load_state(input)
|
|
48
|
+
@envelope.load_state(input)
|
|
49
|
+
end
|
|
50
|
+
|
|
41
51
|
def control=(value)
|
|
42
52
|
@waveform.control = value
|
|
43
53
|
@envelope.control = value
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
class SID
|
|
5
|
+
class Waveform
|
|
6
|
+
# A snapshot of an oscillator: its phase, the noise register and the
|
|
7
|
+
# output latch, and the same fields as reSID keeps them, for VICE's
|
|
8
|
+
# SIDEXTENDED module. The model's tables and the voices it syncs with
|
|
9
|
+
# are the SID's wiring.
|
|
10
|
+
module State
|
|
11
|
+
# The fields reSID keeps for an oscillator between cycles: the phase,
|
|
12
|
+
# the noise register, its shift pipeline and reset countdown, the
|
|
13
|
+
# floating output's countdown and the pulse level.
|
|
14
|
+
def resid_fields
|
|
15
|
+
[@accumulator, @shift_register, @shift_pipeline, @shift_register_reset, @floating_ttl, @pulse]
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Takes the fields resid_fields gives from `fields`, from `at` on.
|
|
19
|
+
def restore_resid_fields(fields, at)
|
|
20
|
+
@accumulator = fields[at]
|
|
21
|
+
@shift_register = fields[at + 1]
|
|
22
|
+
@shift_pipeline = fields[at + 2]
|
|
23
|
+
@shift_register_reset = fields[at + 3]
|
|
24
|
+
@floating_ttl = fields[at + 4]
|
|
25
|
+
@pulse = fields[at + 5]
|
|
26
|
+
@stale = true
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def save_state(out)
|
|
30
|
+
out.int(@accumulator).int(@delayed_triangle).int(@delayed_sawtooth)
|
|
31
|
+
out.int(@shift_register).int(@shift_register_reset).int(@shift_pipeline)
|
|
32
|
+
out.int(@frequency).int(@pulse_width).int(@selected)
|
|
33
|
+
[@test, @ring, @sync, @msb_rising].each { |flag| out.boolean(flag) }
|
|
34
|
+
out.int(@floating).int(@floating_ttl).int(@pulse).int(@output).boolean(@stale)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def load_state(input)
|
|
38
|
+
@accumulator = input.int
|
|
39
|
+
@delayed_triangle = input.int
|
|
40
|
+
@delayed_sawtooth = input.int
|
|
41
|
+
@shift_register = input.int
|
|
42
|
+
@shift_register_reset = input.int
|
|
43
|
+
@shift_pipeline = input.int
|
|
44
|
+
@frequency = input.int
|
|
45
|
+
@pulse_width = input.int
|
|
46
|
+
@selected = input.int
|
|
47
|
+
load_latches(input)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
private
|
|
51
|
+
|
|
52
|
+
def load_latches(input)
|
|
53
|
+
@test = input.boolean?
|
|
54
|
+
@ring = input.boolean?
|
|
55
|
+
@sync = input.boolean?
|
|
56
|
+
@msb_rising = input.boolean?
|
|
57
|
+
@floating = input.int
|
|
58
|
+
@floating_ttl = input.int
|
|
59
|
+
@pulse = input.int
|
|
60
|
+
@output = input.int
|
|
61
|
+
@stale = input.boolean?
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|