badline 0.5.1 → 0.6.1
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 +52 -0
- data/CLAUDE.md +10 -9
- data/README.md +82 -10
- data/doc/pinned-behaviour.md +131 -30
- data/exe/badline-ruby +20 -23
- data/lib/badline/address_bus/saved_state.rb +72 -0
- data/lib/badline/address_bus.rb +11 -1
- data/lib/badline/audio/cli.rb +18 -5
- data/lib/badline/audio/console.rb +2 -1
- data/lib/badline/audio.rb +0 -3
- 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 +13 -33
- data/lib/badline/cpu/boundary.rb +54 -0
- data/lib/badline/cpu/core.rb +165 -0
- data/lib/badline/cpu/microcode.rb +8 -0
- data/lib/badline/cpu/saved_state.rb +115 -0
- data/lib/badline/cpu.rb +3 -212
- data/lib/badline/datasette.rb +27 -0
- data/lib/badline/drive1541/bus.rb +13 -0
- data/lib/badline/drive1541/cpu.rb +29 -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 +35 -0
- data/lib/badline/ffi.rb +157 -0
- data/lib/badline/frontend/app.rb +225 -0
- data/lib/badline/frontend/audio_sink.rb +72 -0
- data/lib/badline/frontend/boot.rb +52 -0
- data/lib/badline/frontend/controls.rb +163 -0
- data/lib/badline/frontend/drive_led.rb +51 -0
- data/lib/badline/frontend/frame_rate.rb +83 -0
- data/lib/badline/frontend/gamepads.rb +76 -0
- data/lib/badline/frontend/keys.rb +48 -0
- data/lib/badline/frontend/pacer.rb +100 -0
- data/lib/badline/frontend/pad_port.rb +61 -0
- data/lib/badline/frontend/screen.rb +65 -0
- data/lib/badline/frontend/screenshot.rb +22 -0
- data/lib/badline/frontend/snapshots.rb +60 -0
- data/lib/badline/frontend/sound.rb +153 -0
- data/lib/badline/frontend/timeline.rb +171 -0
- data/lib/badline/frontend.rb +18 -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/event.rb +94 -0
- data/lib/badline/options/help.rb +78 -0
- data/lib/badline/options/option.rb +29 -0
- data/lib/badline/options/table.rb +62 -0
- data/lib/badline/options.rb +181 -144
- 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/saved_state.rb +50 -0
- data/lib/badline/reu/dma.rb +56 -11
- data/lib/badline/reu/ram.rb +15 -0
- data/lib/badline/reu/saved_state.rb +53 -0
- data/lib/badline/reu.rb +23 -10
- data/lib/badline/sdl.rb +123 -51
- 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/noise_writeback.rb +33 -11
- data/lib/badline/sid/waveform/state.rb +66 -0
- data/lib/badline/sid/waveform.rb +4 -3
- 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/bank.rb +21 -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 +109 -16
- data/lib/badline.rb +1 -0
- metadata +59 -18
- data/.claude/hooks/session-start.sh +0 -57
- data/.claude/settings.json +0 -14
- data/lib/badline/audio/sdl_sink.rb +0 -70
- data/lib/badline/audio/stream.rb +0 -117
- data/lib/badline/gui/application.rb +0 -216
- data/lib/badline/gui/drive_led_pane.rb +0 -29
- data/lib/badline/gui/gamepads.rb +0 -97
- data/lib/badline/gui/joy_map.rb +0 -27
- data/lib/badline/gui/key_map.rb +0 -39
- data/lib/badline/gui/palette.rb +0 -35
- data/lib/badline/gui/pane.rb +0 -36
- data/lib/badline/gui/screen_pane.rb +0 -46
- data/lib/badline/gui/window.rb +0 -61
- data/lib/badline/gui.rb +0 -13
- data/lib/badline/sdl/events.rb +0 -47
- data/lib/badline/sdl/functions.rb +0 -74
- data/vendor/.gitkeep +0 -0
|
@@ -0,0 +1,53 @@
|
|
|
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).boolean(@ba_was_low)
|
|
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
|
+
@ba_was_low = input.boolean?
|
|
31
|
+
@dma.load_state(input)
|
|
32
|
+
@ram.load_state(input)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
private
|
|
36
|
+
|
|
37
|
+
def load_registers(input)
|
|
38
|
+
@status = input.int
|
|
39
|
+
@command = input.int
|
|
40
|
+
@c64 = input.int
|
|
41
|
+
@c64_start = input.int
|
|
42
|
+
@expansion = input.int
|
|
43
|
+
@expansion_start = input.int
|
|
44
|
+
@bank = input.int
|
|
45
|
+
@bank_start = input.int
|
|
46
|
+
@length = input.int
|
|
47
|
+
@length_start = input.int
|
|
48
|
+
@interrupt_mask = input.int
|
|
49
|
+
@address_control = input.int
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
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
|
|
@@ -102,9 +106,8 @@ module Badline
|
|
|
102
106
|
# From the moment the REC asks for the bus until it hands it back.
|
|
103
107
|
def dma? = @requested || @running
|
|
104
108
|
|
|
105
|
-
# Whether the REC
|
|
106
|
-
#
|
|
107
|
-
# REC lets go.
|
|
109
|
+
# Whether the REC ran a transfer on the last cycle clocked, which it
|
|
110
|
+
# goes on doing until the cycle it lets go of the bus.
|
|
108
111
|
def holds_bus? = @running
|
|
109
112
|
|
|
110
113
|
def reset!
|
|
@@ -116,7 +119,7 @@ module Badline
|
|
|
116
119
|
@length = @length_start = 0xffff
|
|
117
120
|
@interrupt_mask = 0x1f
|
|
118
121
|
@address_control = 0x3f
|
|
119
|
-
@armed = @requested = @running = false
|
|
122
|
+
@armed = @requested = @running = @ba_was_low = false
|
|
120
123
|
interrupt(false)
|
|
121
124
|
end
|
|
122
125
|
|
|
@@ -154,16 +157,20 @@ module Badline
|
|
|
154
157
|
request_bus
|
|
155
158
|
end
|
|
156
159
|
|
|
157
|
-
# Clocks the REC for a cycle, given the VIC's BA line
|
|
158
|
-
#
|
|
159
|
-
# CPU
|
|
160
|
-
|
|
160
|
+
# Clocks the REC for a cycle, given the VIC's BA line. A requested
|
|
161
|
+
# transfer starts on the first cycle with BA high as the REC sees it,
|
|
162
|
+
# even one the CPU writes on. Pinned by REU/rmw-trigger. A fetch
|
|
163
|
+
# requested on the cycle before BA falls writes its first byte on the
|
|
164
|
+
# first BA-low cycle as well, and again when it starts. Pinned by
|
|
165
|
+
# REU/badoublewrite.
|
|
166
|
+
def dma_cycle!(ba_low)
|
|
167
|
+
late = ba_low && @vic.reu_ba_late?
|
|
161
168
|
if @requested
|
|
162
|
-
return if
|
|
169
|
+
return hold_off if ba_low && !late
|
|
163
170
|
|
|
164
171
|
start_transfer
|
|
165
172
|
end
|
|
166
|
-
@dma.cycle!(ba_low)
|
|
173
|
+
@dma.cycle!(ba_low, late, ba_low && @vic.reu_ba_handed_on?)
|
|
167
174
|
end_transfer unless @dma.holds_bus?
|
|
168
175
|
end
|
|
169
176
|
|
|
@@ -235,9 +242,15 @@ module Badline
|
|
|
235
242
|
|
|
236
243
|
def request_bus
|
|
237
244
|
@requested = true
|
|
245
|
+
@ba_was_low = @vic.ba_low?
|
|
238
246
|
@on_dma&.call
|
|
239
247
|
end
|
|
240
248
|
|
|
249
|
+
def hold_off
|
|
250
|
+
@dma.write_ahead(@command & TRANSFER_TYPE, @c64, @expansion | (@bank << 16)) unless @ba_was_low
|
|
251
|
+
@ba_was_low = true
|
|
252
|
+
end
|
|
253
|
+
|
|
241
254
|
def start_transfer
|
|
242
255
|
@requested = false
|
|
243
256
|
@running = true
|
data/lib/badline/sdl.rb
CHANGED
|
@@ -1,67 +1,139 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "fiddle"
|
|
4
|
-
|
|
5
3
|
module Badline
|
|
6
|
-
#
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
# SDL::RenderClear.call(renderer) calls SDL_RenderClear. They're built
|
|
12
|
-
# with need_gvl, which skips releasing Ruby's lock around the call: none
|
|
13
|
-
# of these block for long, and releasing it costs more than most of them.
|
|
4
|
+
# libSDL2 through Spinel's FFI DSL, the one binding both builds use. The
|
|
5
|
+
# native build compiles the declarations, and finds the library's
|
|
6
|
+
# directory (see NativeBuild.sdl2_flags). badline-ruby loads badline/ffi
|
|
7
|
+
# first, which implements them over Fiddle, so only the library itself
|
|
8
|
+
# has to be present.
|
|
14
9
|
module SDL
|
|
15
|
-
|
|
10
|
+
ffi_lib "SDL2"
|
|
16
11
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
]
|
|
12
|
+
ffi_func :SDL_Init, [:uint32], :int
|
|
13
|
+
ffi_func :SDL_Quit, [], :void
|
|
14
|
+
ffi_func :SDL_GetError, [], :str
|
|
15
|
+
ffi_func :SDL_SetHint, %i[str str], :int
|
|
16
|
+
ffi_func :SDL_CreateWindow, %i[str int int int int uint32], :ptr
|
|
17
|
+
ffi_func :SDL_SetWindowTitle, %i[ptr str], :void
|
|
18
|
+
ffi_func :SDL_DestroyWindow, [:ptr], :void
|
|
19
|
+
ffi_func :SDL_CreateRenderer, %i[ptr int uint32], :ptr
|
|
20
|
+
ffi_func :SDL_GetWindowDisplayMode, %i[ptr ptr], :int
|
|
21
|
+
ffi_func :SDL_DestroyRenderer, [:ptr], :void
|
|
22
|
+
ffi_func :SDL_RenderSetLogicalSize, %i[ptr int int], :int
|
|
23
|
+
ffi_func :SDL_CreateTexture, %i[ptr uint32 int int int], :ptr
|
|
24
|
+
ffi_func :SDL_DestroyTexture, [:ptr], :void
|
|
25
|
+
ffi_func :SDL_UpdateTexture, %i[ptr ptr int_array int], :int
|
|
26
|
+
ffi_func :SDL_RenderClear, [:ptr], :int
|
|
27
|
+
ffi_func :SDL_SetRenderDrawColor, %i[ptr int int int int], :int
|
|
28
|
+
ffi_func :SDL_RenderFillRect, %i[ptr ptr], :int
|
|
29
|
+
ffi_func :SDL_RenderCopy, %i[ptr ptr ptr ptr], :int
|
|
30
|
+
ffi_func :SDL_RenderPresent, [:ptr], :void
|
|
31
|
+
ffi_func :SDL_PollEvent, [:ptr], :int
|
|
32
|
+
ffi_func :SDL_SetRelativeMouseMode, [:int], :int
|
|
33
|
+
ffi_func :SDL_Delay, [:uint32], :void
|
|
34
|
+
ffi_func :SDL_GetRendererOutputSize, %i[ptr ptr ptr], :int
|
|
35
|
+
ffi_func :SDL_RenderReadPixels, %i[ptr ptr uint32 ptr int], :int
|
|
36
|
+
ffi_func :SDL_CreateRGBSurfaceWithFormatFrom, %i[ptr int int int int uint32], :ptr
|
|
37
|
+
ffi_func :SDL_FreeSurface, [:ptr], :void
|
|
38
|
+
ffi_func :SDL_RWFromFile, %i[str str], :ptr
|
|
39
|
+
ffi_func :SDL_SaveBMP_RW, %i[ptr ptr int], :int
|
|
40
|
+
ffi_func :SDL_InitSubSystem, [:uint32], :int
|
|
41
|
+
ffi_func :SDL_QuitSubSystem, [:uint32], :void
|
|
42
|
+
ffi_func :SDL_OpenAudioDevice, %i[ptr int ptr ptr int], :uint32
|
|
43
|
+
ffi_func :SDL_CloseAudioDevice, [:uint32], :void
|
|
44
|
+
ffi_func :SDL_PauseAudioDevice, %i[uint32 int], :void
|
|
45
|
+
ffi_func :SDL_QueueAudio, %i[uint32 buffer_in uint32], :int
|
|
46
|
+
ffi_func :SDL_GetQueuedAudioSize, [:uint32], :uint32
|
|
47
|
+
ffi_func :SDL_NumJoysticks, [], :int
|
|
48
|
+
ffi_func :SDL_IsGameController, [:int], :int
|
|
49
|
+
ffi_func :SDL_GameControllerOpen, [:int], :ptr
|
|
50
|
+
ffi_func :SDL_GameControllerClose, [:ptr], :void
|
|
51
|
+
ffi_func :SDL_GameControllerName, [:ptr], :str
|
|
52
|
+
ffi_func :SDL_GameControllerGetButton, %i[ptr int], :uint8
|
|
53
|
+
ffi_func :SDL_GameControllerGetAxis, %i[ptr int], :int16
|
|
54
|
+
ffi_func :SDL_ClearQueuedAudio, [:uint32], :void
|
|
55
|
+
ffi_func :SDL_GetCurrentDisplayMode, %i[int ptr], :int
|
|
56
|
+
ffi_func :SDL_CreateRGBSurfaceFrom, %i[ptr int int int int uint32 uint32 uint32 uint32], :ptr
|
|
57
|
+
ffi_func :SDL_CreateTextureFromSurface, %i[ptr ptr], :ptr
|
|
58
|
+
ffi_func :SDL_PushEvent, [:ptr], :int
|
|
59
|
+
ffi_func :SDL_GetKeyName, [:int], :str
|
|
24
60
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
61
|
+
ffi_buffer :event, 56
|
|
62
|
+
ffi_read_u32 :event_type, 0
|
|
63
|
+
ffi_read_u8 :event_repeat, 13
|
|
64
|
+
ffi_read_i32 :event_scancode, 16
|
|
65
|
+
ffi_read_i32 :event_sym, 20
|
|
66
|
+
ffi_read_u16 :event_mod, 24
|
|
67
|
+
# SDL_MouseMotionEvent's xrel and yrel, and SDL_MouseButtonEvent's
|
|
68
|
+
# button.
|
|
69
|
+
ffi_read_i32 :event_xrel, 28
|
|
70
|
+
ffi_read_i32 :event_yrel, 32
|
|
71
|
+
ffi_read_u8 :event_button, 16
|
|
33
72
|
|
|
34
|
-
|
|
73
|
+
ffi_buffer :rect, 16
|
|
74
|
+
ffi_write_i32 :rect_x, 0
|
|
75
|
+
ffi_write_i32 :rect_y, 4
|
|
76
|
+
ffi_write_i32 :rect_w, 8
|
|
77
|
+
ffi_write_i32 :rect_h, 12
|
|
78
|
+
# Where the drive LED goes.
|
|
79
|
+
ffi_buffer :led_rect, 16
|
|
35
80
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
INT16 = Fiddle::TYPE_INT16_T
|
|
40
|
-
UINT32 = Fiddle::TYPE_UINT32_T
|
|
41
|
-
POINTER = Fiddle::TYPE_VOIDP
|
|
42
|
-
STRING = Fiddle::TYPE_CONST_STRING
|
|
81
|
+
# SDL_DisplayMode: Uint32 format; int w, h, refresh_rate; then a pointer.
|
|
82
|
+
ffi_buffer :display_mode, 24
|
|
83
|
+
ffi_read_i32 :mode_refresh, 12
|
|
43
84
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
85
|
+
ffi_buffer :output_w, 4
|
|
86
|
+
ffi_buffer :output_h, 4
|
|
87
|
+
ffi_read_i32 :read_i32, 0
|
|
47
88
|
|
|
48
|
-
|
|
89
|
+
# SDL_AudioSpec: int freq; Uint16 format; Uint8 channels, silence;
|
|
90
|
+
# Uint16 samples, padding; Uint32 size; then two pointers.
|
|
91
|
+
ffi_buffer :wanted, 32
|
|
92
|
+
ffi_buffer :obtained, 32
|
|
93
|
+
ffi_write_i32 :spec_freq, 0
|
|
94
|
+
ffi_write_u16 :spec_format, 4
|
|
95
|
+
ffi_write_u8 :spec_channels, 6
|
|
96
|
+
ffi_write_u16 :spec_samples, 8
|
|
49
97
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
98
|
+
ffi_const :INIT_AUDIO, 0x10
|
|
99
|
+
ffi_const :INIT_VIDEO, 0x20
|
|
100
|
+
ffi_const :INIT_GAMECONTROLLER, 0x2000
|
|
101
|
+
ffi_const :INIT_EVENTS, 0x4000
|
|
102
|
+
ffi_const :WINDOWPOS_CENTERED, 0x2fff0000
|
|
103
|
+
ffi_const :WINDOW_RESIZABLE, 0x20
|
|
104
|
+
ffi_const :RENDERER_ACCELERATED, 0x02
|
|
105
|
+
ffi_const :RENDERER_PRESENTVSYNC, 0x04
|
|
106
|
+
ffi_const :PIXELFORMAT_RGB888, 0x16161804
|
|
107
|
+
ffi_const :TEXTUREACCESS_STREAMING, 1
|
|
108
|
+
ffi_const :QUIT, 0x100
|
|
109
|
+
ffi_const :KEYDOWN, 0x300
|
|
110
|
+
ffi_const :KEYUP, 0x301
|
|
111
|
+
ffi_const :MOUSEMOTION, 0x400
|
|
112
|
+
ffi_const :MOUSEBUTTONDOWN, 0x401
|
|
113
|
+
ffi_const :MOUSEBUTTONUP, 0x402
|
|
114
|
+
ffi_const :KMOD_SHIFT, 0x0003
|
|
115
|
+
ffi_const :CONTROLLERDEVICEADDED, 0x653
|
|
116
|
+
ffi_const :CONTROLLERDEVICEREMOVED, 0x654
|
|
117
|
+
ffi_const :CONTROLLERDEVICEREMAPPED, 0x655
|
|
118
|
+
ffi_const :AUDIO_S16LSB, 0x8010
|
|
119
|
+
ffi_const :ALLOW_FREQUENCY_CHANGE, 0x01
|
|
120
|
+
ffi_const :KEY_TAB, 0x09
|
|
121
|
+
ffi_const :KEY_F10, 0x4000_0043
|
|
122
|
+
ffi_const :KEY_F11, 0x4000_0044
|
|
123
|
+
ffi_const :KEY_F12, 0x4000_0045
|
|
124
|
+
end
|
|
53
125
|
|
|
54
|
-
|
|
55
|
-
|
|
126
|
+
# The few C library calls the native front end makes.
|
|
127
|
+
module LibC
|
|
128
|
+
ffi_func :malloc, [:size_t], :ptr
|
|
129
|
+
ffi_func :free, [:ptr], :void
|
|
130
|
+
ffi_func :poll, %i[ptr size_t int], :int
|
|
56
131
|
|
|
57
|
-
|
|
58
|
-
def self.check_pointer(pointer)
|
|
59
|
-
raise Error, error if pointer.null?
|
|
132
|
+
ffi_const :POLLIN, 0x01
|
|
60
133
|
|
|
61
|
-
|
|
62
|
-
|
|
134
|
+
# struct pollfd: int fd; short events, revents.
|
|
135
|
+
ffi_buffer :pollfd, 8
|
|
136
|
+
ffi_write_i32 :pollfd_fd, 0
|
|
137
|
+
ffi_write_i16 :pollfd_events, 4
|
|
63
138
|
end
|
|
64
139
|
end
|
|
65
|
-
|
|
66
|
-
require "badline/sdl/functions"
|
|
67
|
-
require "badline/sdl/events"
|
|
@@ -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
|
|
@@ -20,11 +20,12 @@ module Badline
|
|
|
20
20
|
# so they lose a different number of bits. The 8580's pair is the one
|
|
21
21
|
# that reproduces SID/wf12nsr's pulse+noise row (it reads $f8, and the
|
|
22
22
|
# writeback leaves $fc behind), and SID/wb_testsuite's C->9 and C->E
|
|
23
|
-
# rows need that same $fc written at release.
|
|
24
|
-
#
|
|
25
|
-
#
|
|
26
|
-
# leave the register alone.
|
|
27
|
-
|
|
23
|
+
# rows need that same $fc written at release. The 6581 reads $00 of a
|
|
24
|
+
# full register (SID/wf12nsr's pulse+noise row) and pulls nothing
|
|
25
|
+
# while the register holds: wb_testsuite's 8->C and C->C rows run
|
|
26
|
+
# pulse+noise and leave the register alone. Its lines only land
|
|
27
|
+
# part way through a shift (#write_back_shift).
|
|
28
|
+
PULSE_NOISE_READ = { mos6581: 0x000, mos8580: 0xf80 }.freeze
|
|
28
29
|
PULSE_NOISE_WRITE = { mos6581: 0xff0, mos8580: 0xfc0 }.freeze
|
|
29
30
|
|
|
30
31
|
# What a 6581 writes back when the test bit falls with only pulse+noise
|
|
@@ -40,24 +41,45 @@ module Badline
|
|
|
40
41
|
# (SID/wf12nsr's noise+triangle and noise+sawtooth rows).
|
|
41
42
|
def write_back(selected, value)
|
|
42
43
|
return value & ((value << 1) | (value >> 1)) unless selected == 0xc
|
|
43
|
-
return
|
|
44
|
+
return 0x000 if @pulse.zero?
|
|
44
45
|
|
|
45
46
|
noise & @pulse_noise_write
|
|
46
47
|
end
|
|
47
48
|
|
|
49
|
+
# What the lines write while the register is part way through a
|
|
50
|
+
# shift, where the 6581's pulse+noise writes its output as read.
|
|
51
|
+
def write_back_mid_shift(selected, value)
|
|
52
|
+
return value if pulse_noise_mid_shift?(selected)
|
|
53
|
+
|
|
54
|
+
write_back(selected, value)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def pulse_noise_mid_shift?(selected) = @topbit_feedback && selected == 0xc
|
|
58
|
+
|
|
48
59
|
def write_shift_register(value)
|
|
49
60
|
NOISE_TAPS.each { |bit, line| @shift_register &= ~bit if value.nobits?(line) }
|
|
50
61
|
end
|
|
51
62
|
|
|
52
|
-
#
|
|
53
|
-
#
|
|
54
|
-
#
|
|
55
|
-
#
|
|
63
|
+
# The second phase of a shift writes back by the test bit release
|
|
64
|
+
# rule with the waveform left alone. The 6581's pulse+noise, which
|
|
65
|
+
# that rule leaves out, writes its lines too (SID/wf12nsr's
|
|
66
|
+
# pulse+noise row).
|
|
67
|
+
def write_back_shift
|
|
68
|
+
return unless pulse_noise_mid_shift?(@selected) || release_writes_back?(@selected, @selected)
|
|
69
|
+
|
|
70
|
+
write_shift_register(write_back_mid_shift(@selected, @output))
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Setting the test bit starts the bleed, and stalls the register part
|
|
74
|
+
# way through a shift, where a noise combination writes its output
|
|
75
|
+
# back first, from the accumulator it had. SID/noiselfsrinit's
|
|
76
|
+
# $f8/$80 pairs set the bit onto all four from noise alone, and
|
|
77
|
+
# SID/wf12nsr's pulse+noise row sets it onto pulse+noise.
|
|
56
78
|
def raise_test
|
|
57
79
|
@shift_register_reset = @shift_register_reset_delay
|
|
58
80
|
return unless @selected.anybits?(0x8) && combined?(@selected) && @shift_pipeline != 1
|
|
59
81
|
|
|
60
|
-
write_shift_register(
|
|
82
|
+
write_shift_register(write_back_mid_shift(@selected, shape(@selected)))
|
|
61
83
|
end
|
|
62
84
|
|
|
63
85
|
# Releasing the test bit finishes the shift it interrupted: the old
|
|
@@ -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
|
data/lib/badline/sid/waveform.rb
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
require "badline/sid/waveform/combined"
|
|
4
4
|
require "badline/sid/waveform/fast_forward"
|
|
5
5
|
require "badline/sid/waveform/noise_writeback"
|
|
6
|
+
require "badline/sid/waveform/state"
|
|
6
7
|
|
|
7
8
|
module Badline
|
|
8
9
|
class SID
|
|
@@ -27,6 +28,7 @@ module Badline
|
|
|
27
28
|
class Waveform
|
|
28
29
|
include FastForward
|
|
29
30
|
include NoiseWriteback
|
|
31
|
+
include State
|
|
30
32
|
|
|
31
33
|
# All bits high at power up, odd ones stored inverted (SID/oscinit).
|
|
32
34
|
POWER_ON_ACCUMULATOR = 0x555555
|
|
@@ -254,8 +256,7 @@ module Badline
|
|
|
254
256
|
# A rise of bit 19 starts a shift that takes two more cycles
|
|
255
257
|
# (SID/noisewriteback's noise_writeback_test2, after libresidfp's shift
|
|
256
258
|
# pipeline). The first phase latches the output, and the second writes
|
|
257
|
-
# it over the taps
|
|
258
|
-
# alone, before the register shifts.
|
|
259
|
+
# it over the taps before the register shifts.
|
|
259
260
|
def advance
|
|
260
261
|
previous = @accumulator
|
|
261
262
|
@accumulator = (previous + @frequency) & 0xffffff
|
|
@@ -263,7 +264,7 @@ module Badline
|
|
|
263
264
|
if @accumulator & ~previous & 0x080000 != 0
|
|
264
265
|
@shift_pipeline = 2
|
|
265
266
|
elsif @shift_pipeline != 0 && (@shift_pipeline -= 1).zero?
|
|
266
|
-
|
|
267
|
+
write_back_shift
|
|
267
268
|
shift_noise
|
|
268
269
|
end
|
|
269
270
|
end
|