badline 0.2.1 → 0.4.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/.claude/hooks/session-start.sh +57 -0
- data/.claude/settings.json +14 -0
- data/CHANGELOG.md +107 -0
- data/CLAUDE.md +51 -16
- data/CONTRIBUTING.md +95 -3
- data/README.md +119 -51
- data/SECURITY.md +9 -0
- data/doc/pinned-behaviour.md +293 -39
- data/exe/badline-ruby +59 -0
- data/lib/badline/address_bus.rb +28 -14
- data/lib/badline/addressable.rb +1 -1
- data/lib/badline/audio/aiff.rb +8 -3
- data/lib/badline/audio/bare_player.rb +20 -3
- data/lib/badline/audio/cli.rb +17 -12
- data/lib/badline/audio/console.rb +12 -58
- data/lib/badline/audio/machine_player.rb +9 -6
- data/lib/badline/audio/playback.rb +3 -0
- data/lib/badline/audio/renderer.rb +3 -3
- data/lib/badline/audio/sdl_sink.rb +20 -52
- data/lib/badline/audio/stream.rb +117 -0
- data/lib/badline/audio/terminal.rb +90 -0
- data/lib/badline/audio.rb +3 -2
- data/lib/badline/cartridge/easy_flash.rb +6 -2
- data/lib/badline/cartridge/epyx_fastload.rb +1 -1
- data/lib/badline/cartridge/geo_ram.rb +63 -0
- data/lib/badline/cartridge/mach5.rb +1 -1
- data/lib/badline/cartridge/magic_desk.rb +5 -1
- data/lib/badline/cartridge/ocean.rb +5 -2
- data/lib/badline/cartridge/rex_utility.rb +1 -1
- data/lib/badline/cartridge/rgcd.rb +1 -1
- data/lib/badline/cartridge/simons_basic.rb +2 -2
- data/lib/badline/cartridge/westermann.rb +2 -2
- data/lib/badline/cartridge.rb +9 -0
- data/lib/badline/checkpoint.rb +98 -0
- data/lib/badline/cia/interrupt_register.rb +34 -10
- data/lib/badline/cia.rb +19 -10
- data/lib/badline/color_memory.rb +2 -2
- data/lib/badline/computer.rb +34 -14
- data/lib/badline/cpu.rb +2 -2
- data/lib/badline/gui/application.rb +70 -26
- data/lib/badline/gui/gamepads.rb +22 -22
- data/lib/badline/gui/joy_map.rb +1 -1
- data/lib/badline/gui/key_map.rb +6 -1
- data/lib/badline/gui/pane.rb +7 -6
- data/lib/badline/gui/screen_pane.rb +9 -13
- data/lib/badline/gui/window.rb +35 -20
- data/lib/badline/gui.rb +1 -1
- data/lib/badline/input/mouse1351.rb +21 -6
- data/lib/badline/input/paddles.rb +11 -6
- data/lib/badline/kernal_trap/channel.rb +102 -7
- data/lib/badline/kernal_trap/drive/block_commands.rb +108 -0
- data/lib/badline/kernal_trap/drive/channels.rb +53 -0
- data/lib/badline/kernal_trap/drive/memory.rb +109 -0
- data/lib/badline/kernal_trap/drive/parameters.rb +28 -0
- data/lib/badline/kernal_trap/drive/status.rb +3 -0
- data/lib/badline/kernal_trap/drive/write_file.rb +41 -0
- data/lib/badline/kernal_trap/drive/writes.rb +92 -0
- data/lib/badline/kernal_trap/drive.rb +109 -131
- data/lib/badline/kernal_trap/file.rb +6 -14
- data/lib/badline/kernal_trap/load.rb +6 -0
- data/lib/badline/kernal_trap/save.rb +13 -7
- data/lib/badline/keyboard.rb +19 -3
- data/lib/badline/media.rb +44 -13
- data/lib/badline/memory.rb +2 -2
- data/lib/badline/options.rb +209 -0
- data/lib/badline/ram_expansion/banking.rb +30 -0
- data/lib/badline/ram_expansion/plus256k.rb +38 -0
- data/lib/badline/ram_expansion/plus60k.rb +31 -0
- data/lib/badline/ram_expansion/unexpanded.rb +22 -0
- data/lib/badline/ram_expansion.rb +21 -0
- data/lib/badline/region.rb +51 -0
- data/lib/badline/rom.rb +14 -5
- data/lib/badline/sdl/events.rb +47 -0
- data/lib/badline/sdl/functions.rb +73 -0
- data/lib/badline/sdl.rb +67 -0
- data/lib/badline/sid/catch_up.rb +110 -0
- data/lib/badline/sid/decimator.rb +6 -1
- data/lib/badline/sid/filter.rb +5 -5
- data/lib/badline/sid/waveform/fast_forward.rb +10 -10
- data/lib/badline/sid/waveform/noise_writeback.rb +32 -14
- data/lib/badline/sid/waveform.rb +8 -8
- data/lib/badline/sid.rb +19 -93
- data/lib/badline/storage/d64_image.rb +10 -0
- data/lib/badline/storage/d71_image.rb +12 -0
- data/lib/badline/storage/d81_image.rb +9 -0
- data/lib/badline/storage/disk_image/bam.rb +111 -0
- data/lib/badline/storage/disk_image/directory.rb +79 -0
- data/lib/badline/storage/disk_image/writing.rb +110 -0
- data/lib/badline/storage/disk_image.rb +49 -4
- data/lib/badline/storage/host_directory.rb +17 -4
- data/lib/badline/storage/sid_file.rb +36 -2
- data/lib/badline/storage.rb +18 -1
- data/lib/badline/time_of_day.rb +11 -2
- data/lib/badline/version.rb +1 -1
- data/lib/badline/via/control_lines.rb +104 -0
- data/lib/badline/via/interrupt_register.rb +58 -0
- data/lib/badline/via/shift_register.rb +135 -0
- data/lib/badline/via/timer1.rb +82 -0
- data/lib/badline/via/timer2.rb +84 -0
- data/lib/badline/via.rb +262 -0
- data/lib/badline/vic/bank.rb +4 -3
- data/lib/badline/vic/color_patches.rb +6 -3
- data/lib/badline/vic/display_state.rb +4 -0
- data/lib/badline/vic/graphics_shifter.rb +49 -8
- data/lib/badline/vic/registers.rb +19 -27
- data/lib/badline/vic/sequencer.rb +18 -18
- data/lib/badline/vic/sequencer_output.rb +4 -4
- data/lib/badline/vic/sprite/internal_bus.rb +4 -0
- data/lib/badline/vic/sprite/shifter.rb +25 -3
- data/lib/badline/vic/sprite.rb +5 -4
- data/lib/badline/vic/sprites.rb +8 -3
- data/lib/badline/vic.rb +98 -46
- data/lib/badline.rb +5 -0
- data/packaging/homebrew/badline.rb +34 -0
- metadata +37 -20
- data/exe/badline +0 -75
- data/exe/badline-sid +0 -34
- data/lib/badline/audio/options.rb +0 -137
data/lib/badline/vic.rb
CHANGED
|
@@ -11,14 +11,22 @@ module Badline
|
|
|
11
11
|
include Addressable
|
|
12
12
|
include IntegerHelper
|
|
13
13
|
|
|
14
|
+
# The 6569 of the breadbin C64, and the HMOS 8565 of the C64C.
|
|
15
|
+
MODELS = %i[mos6569 mos8565].freeze
|
|
16
|
+
|
|
14
17
|
attr_reader :address_bus, :display, :width, :height, :vic_bank, :column,
|
|
15
|
-
:rasterline, :dirty_lines
|
|
18
|
+
:rasterline, :dirty_lines, :model, :region
|
|
16
19
|
|
|
17
20
|
# Returns the byte the CPU's halted read would see, for the c-accesses
|
|
18
21
|
# that run before AEC. Without it they read colour RAM.
|
|
19
22
|
attr_writer :open_bus
|
|
20
23
|
|
|
21
24
|
LIGHTPEN_IRQ = 0x08 # $D019 latch bit
|
|
25
|
+
COLOR_REGISTERS = 0x20..0x2e
|
|
26
|
+
|
|
27
|
+
# What the 8565 shows on the pixel before a color register write takes
|
|
28
|
+
# hold, where the 6569 shows the old color: the grey dot.
|
|
29
|
+
GREY_DOT = 0x0f
|
|
22
30
|
|
|
23
31
|
# A g-access reaches the pixel output this many columns after it runs.
|
|
24
32
|
GRAPHICS_DELAY = 2
|
|
@@ -28,17 +36,19 @@ module Badline
|
|
|
28
36
|
G_IDLE = 1
|
|
29
37
|
G_DISPLAY = 2
|
|
30
38
|
|
|
39
|
+
# The idle g-access in the column a mid-row bad line opens display
|
|
40
|
+
# state reads here on a 6569.
|
|
41
|
+
DMA_DELAY_IDLE_ADDRESS = 0x38ff
|
|
42
|
+
|
|
31
43
|
# The $d011 mode bits a g-access still sees for a cycle after they fall:
|
|
32
44
|
# BMM always, and ECM too when the access it leaves read the character
|
|
33
45
|
# ROM.
|
|
34
46
|
FETCH_HOLD = 0x20
|
|
35
47
|
FETCH_HOLD_ROM = 0x60
|
|
36
48
|
|
|
37
|
-
#
|
|
38
|
-
#
|
|
39
|
-
|
|
40
|
-
[14, 15, 53, 54, 57, 62].include?(column)
|
|
41
|
-
end.freeze
|
|
49
|
+
# The columns carrying a hook, besides the line's last column, which
|
|
50
|
+
# starts the vertical border for the line after it.
|
|
51
|
+
HOOKS = [14, 15, 53, 54, 57].freeze
|
|
42
52
|
|
|
43
53
|
# BA falls three columns ahead of each sprite's pair of s-accesses, and
|
|
44
54
|
# the five-column windows step two columns apart from sprite 0 at column
|
|
@@ -46,19 +56,29 @@ module Badline
|
|
|
46
56
|
# split: the tail columns fall on the line whose cycle 55/56 compare
|
|
47
57
|
# started the fetch, the head columns on the line after it.
|
|
48
58
|
SPRITE_BA_WINDOWS = Array.new(8) { |n| (54 + (2 * n))..(58 + (2 * n)) }.freeze
|
|
49
|
-
SPRITE_BA_TAIL = SPRITE_BA_WINDOWS.map { |w| w.select { |c| c < 63 } }.freeze
|
|
50
|
-
SPRITE_BA_HEAD = SPRITE_BA_WINDOWS.map { |w| w.filter_map { |c| c - 63 if c >= 63 } }.freeze
|
|
51
59
|
|
|
52
|
-
def initialize(address_bus = nil, debug: false)
|
|
60
|
+
def initialize(address_bus = nil, debug: false, model: :mos6569, region: Region::PAL)
|
|
61
|
+
raise ArgumentError, "unknown VIC-II model #{model}" unless MODELS.include?(model)
|
|
62
|
+
|
|
53
63
|
addressable_at(0xd000, length: 2**10)
|
|
64
|
+
@model = model
|
|
65
|
+
@region = region
|
|
66
|
+
@lightpen_extra = model == :mos8565 ? 1 : 2
|
|
67
|
+
@grey_dots = model == :mos8565
|
|
68
|
+
@delayed_fetch = model == :mos8565
|
|
69
|
+
@dma_delay_idle = model == :mos6569
|
|
54
70
|
@address_bus = address_bus || AddressBus.new
|
|
55
71
|
@vic_bank = VIC::Bank.new(@address_bus)
|
|
56
72
|
@debug = debug
|
|
57
73
|
|
|
58
|
-
@
|
|
59
|
-
@
|
|
60
|
-
@
|
|
74
|
+
@columns_per_line = region.cycles_per_line
|
|
75
|
+
@width = @columns_per_line * 8
|
|
76
|
+
@height = region.lines_per_frame
|
|
77
|
+
@last_column = @columns_per_line - 1
|
|
61
78
|
@last_line = @height - 1
|
|
79
|
+
layout_columns
|
|
80
|
+
@blank_columns = Array.new(@columns_per_line) { |column| Region.blanked?(column, region.hblank) }.freeze
|
|
81
|
+
@blank_lines = Array.new(@height) { |line| Region.blanked?(line, region.vblank) }.freeze
|
|
62
82
|
@display = Array.new(@width * @height, 0)
|
|
63
83
|
@lines = Array.new(@height) { Array.new(@width, 0) }
|
|
64
84
|
@dirty_lines = Array.new(@height, true)
|
|
@@ -75,9 +95,9 @@ module Badline
|
|
|
75
95
|
@registers = VIC::Registers.new
|
|
76
96
|
@register_bytes = @registers.bytes
|
|
77
97
|
@display_state = VIC::DisplayState.new(@registers)
|
|
78
|
-
@sequencer = VIC::Sequencer.new(@width, @registers, @vic_bank)
|
|
98
|
+
@sequencer = VIC::Sequencer.new(@width, @registers, @vic_bank, model: @model, region: @region)
|
|
79
99
|
@sequencer.render = @render
|
|
80
|
-
@sprites = VIC::Sprites.new(@registers, @vic_bank, @width)
|
|
100
|
+
@sprites = VIC::Sprites.new(@registers, @vic_bank, @width, model: @model)
|
|
81
101
|
@display.fill(0)
|
|
82
102
|
@lines.each { |line| line.fill(0) }
|
|
83
103
|
@dirty_lines.fill(true)
|
|
@@ -108,11 +128,11 @@ module Badline
|
|
|
108
128
|
# The g-access runs in the first half of the cycle, ahead of the bad
|
|
109
129
|
# line compare, and the c-access in the second half, after it.
|
|
110
130
|
draw!
|
|
111
|
-
@display_state.cycle(@rasterline, @column)
|
|
131
|
+
dma_delay_idle_access if @display_state.cycle(@rasterline, @column)
|
|
112
132
|
|
|
113
133
|
fetch_character_data! if dma_active?
|
|
114
134
|
|
|
115
|
-
column_hooks if
|
|
135
|
+
column_hooks if @hook_columns[@column]
|
|
116
136
|
|
|
117
137
|
@column += 1
|
|
118
138
|
if @column == @columns_per_line
|
|
@@ -140,7 +160,7 @@ module Badline
|
|
|
140
160
|
when 53 then check_sprite_dma
|
|
141
161
|
when 54 then check_dma_and_toggle_expansion
|
|
142
162
|
when 57 then @sprites.check_display(@rasterline)
|
|
143
|
-
when
|
|
163
|
+
when @last_column then @sequencer.start_vertical_border(@rasterline == @last_line ? 0 : @rasterline + 1)
|
|
144
164
|
end
|
|
145
165
|
end
|
|
146
166
|
|
|
@@ -151,7 +171,7 @@ module Badline
|
|
|
151
171
|
end
|
|
152
172
|
|
|
153
173
|
def peek(addr)
|
|
154
|
-
i =
|
|
174
|
+
i = offset_of(addr) % (2**6)
|
|
155
175
|
case i
|
|
156
176
|
when 0x11 then (@registers[0x11] & 0x7f) | ((raster_counter & 0x100) >> 1)
|
|
157
177
|
when 0x12 then raster_counter & 0xff
|
|
@@ -161,8 +181,11 @@ module Badline
|
|
|
161
181
|
end.tap { |value| @sprites.bus_data(@column, value) }
|
|
162
182
|
end
|
|
163
183
|
|
|
184
|
+
# The register file as stored, read without a bus access's side effects.
|
|
185
|
+
def register_file = Array.new(0x40) { |reg| @registers[reg] }
|
|
186
|
+
|
|
164
187
|
def poke(addr, value)
|
|
165
|
-
reg =
|
|
188
|
+
reg = offset_of(addr) % (2**6)
|
|
166
189
|
@sprites.bus_data(@column, value)
|
|
167
190
|
log_register_change(reg, value)
|
|
168
191
|
@registers.write(reg, value)
|
|
@@ -194,9 +217,10 @@ module Badline
|
|
|
194
217
|
end
|
|
195
218
|
|
|
196
219
|
# The first trigger per frame latches the beam position into LPX/LPY and
|
|
197
|
-
# raises the LP IRQ. The latch happens one cycle after the edge, with
|
|
198
|
-
#
|
|
199
|
-
# without latching unless it lands
|
|
220
|
+
# raises the LP IRQ. The latch happens one cycle after the edge, with
|
|
221
|
+
# the model's extra half-pixels, two on the 6569 and one on the 8565; a
|
|
222
|
+
# trigger on the last line is consumed without latching unless it lands
|
|
223
|
+
# on the line's first cycle.
|
|
200
224
|
def trigger_lightpen
|
|
201
225
|
return if @lp_triggered
|
|
202
226
|
|
|
@@ -210,7 +234,7 @@ module Badline
|
|
|
210
234
|
return if line == @last_line && column.positive?
|
|
211
235
|
|
|
212
236
|
vic_x = ((column * 8) - Sprite::X_OFFSET) % @width
|
|
213
|
-
latch_lightpen((vic_x >> 1) +
|
|
237
|
+
latch_lightpen((vic_x >> 1) + @lightpen_extra, line)
|
|
214
238
|
end
|
|
215
239
|
|
|
216
240
|
# The byte the VIC fetched in the phi1 half of the cycle the CPU is
|
|
@@ -221,17 +245,9 @@ module Badline
|
|
|
221
245
|
vic_bank.peek(phi1_address)
|
|
222
246
|
end
|
|
223
247
|
|
|
224
|
-
def hblank?
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
def vblank?
|
|
229
|
-
@rasterline < 16 || @rasterline > 299
|
|
230
|
-
end
|
|
231
|
-
|
|
232
|
-
def blanking?
|
|
233
|
-
@rasterline < 16 || @rasterline > 299 || @column < 10 || @column > 60
|
|
234
|
-
end
|
|
248
|
+
def hblank? = @blank_columns[@column]
|
|
249
|
+
def vblank? = @blank_lines[@rasterline]
|
|
250
|
+
def blanking? = @blank_lines[@rasterline] || @blank_columns[@column]
|
|
235
251
|
|
|
236
252
|
def render? = @render
|
|
237
253
|
|
|
@@ -262,19 +278,30 @@ module Badline
|
|
|
262
278
|
# Mid-line writes to the color and sprite registers are logged against
|
|
263
279
|
# the cycle after the write (the CPU runs after the VIC within a machine
|
|
264
280
|
# cycle, so @column already points there); each register adds its own
|
|
265
|
-
# pixel delay on top.
|
|
281
|
+
# pixel delay on top. On the 8565 a color register write that leaves
|
|
282
|
+
# the value as it was still shows its grey dot.
|
|
266
283
|
def log_register_change(reg, value)
|
|
267
284
|
old = @registers[reg]
|
|
268
|
-
return if old == value
|
|
285
|
+
return if old == value && !grey_dot?(reg)
|
|
269
286
|
|
|
270
287
|
if (0x20..0x24).cover?(reg)
|
|
271
288
|
@sequencer.colors_changed! unless reg == 0x20
|
|
272
289
|
@sequencer.color_patches.log(reg, old, value, @column * 8) if @render && !blanking?
|
|
273
290
|
else
|
|
274
|
-
|
|
291
|
+
log_sprite_change(reg, old, value)
|
|
275
292
|
end
|
|
276
293
|
end
|
|
277
294
|
|
|
295
|
+
def grey_dot?(reg) = @grey_dots && COLOR_REGISTERS.cover?(reg)
|
|
296
|
+
|
|
297
|
+
# A sprite color shows its grey dot as a write of light grey on the
|
|
298
|
+
# pixel before the new color.
|
|
299
|
+
def log_sprite_change(reg, old, value)
|
|
300
|
+
beam_x = @column * 8
|
|
301
|
+
@sprites.log_change(reg, old, GREY_DOT, beam_x - 1) if grey_dot?(reg)
|
|
302
|
+
@sprites.log_change(reg, old, value, beam_x)
|
|
303
|
+
end
|
|
304
|
+
|
|
278
305
|
# Bauer cycles 1-10 and 58-63 are sprite p- and s-accesses, 11-15
|
|
279
306
|
# refresh, 16-55 g-accesses and 56-57 idle. @column has already
|
|
280
307
|
# advanced, so it is the Bauer cycle less one.
|
|
@@ -308,11 +335,12 @@ module Badline
|
|
|
308
335
|
graphics_address(@registers[0x11], @display_state.vmli - 1, (@display_state.vc - 1) & 0x3ff)
|
|
309
336
|
end
|
|
310
337
|
|
|
311
|
-
# A $d011/$d012 write is compared in the next column. A write
|
|
312
|
-
# column
|
|
338
|
+
# A $d011/$d012 write is compared in the next column. A write in the
|
|
339
|
+
# line's second last column is left to its last, which compares the
|
|
340
|
+
# next line.
|
|
313
341
|
def compare_raster_writes(reg)
|
|
314
342
|
return unless (0x11..0x12).cover?(reg)
|
|
315
|
-
return if @column == @
|
|
343
|
+
return if @column == @last_column
|
|
316
344
|
|
|
317
345
|
check_raster_irq!
|
|
318
346
|
@sequencer.compare_vertical_border(@rasterline) if reg == 0x11
|
|
@@ -355,11 +383,21 @@ module Badline
|
|
|
355
383
|
8.times do |n|
|
|
356
384
|
next unless @sprites[n].displaying?
|
|
357
385
|
|
|
358
|
-
|
|
359
|
-
|
|
386
|
+
@sprite_ba_tail[n].each { |c| @sprite_ba[c] = true }
|
|
387
|
+
@sprite_ba_head[n].each { |c| @sprite_ba[c] = true }
|
|
360
388
|
end
|
|
361
389
|
end
|
|
362
390
|
|
|
391
|
+
# Splits the sprite BA windows at the end of the line and marks the hook
|
|
392
|
+
# columns, for a line of the region's length. The marks let an ordinary
|
|
393
|
+
# column cost one array read instead of the dispatch.
|
|
394
|
+
def layout_columns
|
|
395
|
+
last = @last_column
|
|
396
|
+
@sprite_ba_tail = SPRITE_BA_WINDOWS.map { |w| w.select { |c| c <= last } }.freeze
|
|
397
|
+
@sprite_ba_head = SPRITE_BA_WINDOWS.map { |w| w.filter_map { |c| c - last - 1 if c > last } }.freeze
|
|
398
|
+
@hook_columns = Array.new(@columns_per_line) { |c| c == last || HOOKS.include?(c) }.freeze
|
|
399
|
+
end
|
|
400
|
+
|
|
363
401
|
# Runs this column's g-access and draws the one from GRAPHICS_DELAY
|
|
364
402
|
# columns earlier. The access reads its byte now, through the mode,
|
|
365
403
|
# $d018 and bank of this cycle. A column with no g-access, or one made
|
|
@@ -400,15 +438,29 @@ module Badline
|
|
|
400
438
|
@g_color[slot] = @g_kept_color = @color_buffer[vmli] || 1
|
|
401
439
|
end
|
|
402
440
|
|
|
403
|
-
#
|
|
404
|
-
#
|
|
405
|
-
#
|
|
406
|
-
#
|
|
441
|
+
# On the 6569 a bad line that opens display state mid-row (DMA delay)
|
|
442
|
+
# moves the idle g-access of the column that triggers it from $3fff (or
|
|
443
|
+
# $39ff) to DMA_DELAY_IDLE_ADDRESS, unless YSCROLL is 0 (vsp-tester,
|
|
444
|
+
# colorfetchbug/main).
|
|
445
|
+
def dma_delay_idle_access
|
|
446
|
+
slot = @g_tick
|
|
447
|
+
return unless @dma_delay_idle && @g_kind[slot] == G_IDLE
|
|
448
|
+
return if @registers.yscroll.zero?
|
|
449
|
+
|
|
450
|
+
@g_data[slot] = vic_bank.peek(DMA_DELAY_IDLE_ADDRESS)
|
|
451
|
+
end
|
|
452
|
+
|
|
453
|
+
# The 6569's g-access sees a mode bit that falls a cycle late: it
|
|
454
|
+
# addresses with $d011 as this column has it, OR-ed with the held bits
|
|
455
|
+
# the column before had. When BMM changes and the access moves from RAM
|
|
456
|
+
# onto the character ROM, the low address byte still comes from the old
|
|
457
|
+
# mode. The 8565's addresses with $d011 as the column before had it
|
|
407
458
|
# (VICE x64sc `vicii_fetch_graphics`).
|
|
408
459
|
def fetch_graphics(vmli, counter)
|
|
409
460
|
d011 = @register_bytes[0x11]
|
|
410
461
|
last = @fetch_d011
|
|
411
462
|
return vic_bank.peek(graphics_address(d011, vmli, counter)) if d011 == last
|
|
463
|
+
return vic_bank.peek(graphics_address(last, vmli, counter)) if @delayed_fetch
|
|
412
464
|
|
|
413
465
|
from = graphics_address(last, vmli, counter)
|
|
414
466
|
from_rom = vic_bank.character_rom?(from)
|
data/lib/badline.rb
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "badline/version"
|
|
4
|
+
require "badline/options"
|
|
4
5
|
require "badline/integer_helper"
|
|
6
|
+
require "badline/region"
|
|
5
7
|
require "badline/addressable"
|
|
6
8
|
require "badline/memory"
|
|
7
9
|
require "badline/color_memory"
|
|
8
10
|
require "badline/rom"
|
|
11
|
+
require "badline/ram_expansion"
|
|
9
12
|
require "badline/address_bus"
|
|
10
13
|
require "badline/instruction"
|
|
11
14
|
require "badline/instruction_set"
|
|
@@ -18,6 +21,7 @@ require "badline/cycleable"
|
|
|
18
21
|
require "badline/datasette"
|
|
19
22
|
require "badline/time_of_day"
|
|
20
23
|
require "badline/cia"
|
|
24
|
+
require "badline/via"
|
|
21
25
|
require "badline/sid"
|
|
22
26
|
require "badline/debug_register"
|
|
23
27
|
require "badline/interrupts"
|
|
@@ -26,6 +30,7 @@ require "badline/cpu"
|
|
|
26
30
|
require "badline/vic"
|
|
27
31
|
require "badline/keyboard_buffer"
|
|
28
32
|
require "badline/computer"
|
|
33
|
+
require "badline/checkpoint"
|
|
29
34
|
require "badline/storage"
|
|
30
35
|
require "badline/cartridge"
|
|
31
36
|
require "badline/kernal_trap"
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# The native badline, built from the `rake native:pack` tarball attached to
|
|
4
|
+
# each release. The release workflow copies this file into the tap with the
|
|
5
|
+
# release's url and sha256 filled in.
|
|
6
|
+
class Badline < Formula
|
|
7
|
+
desc "Cycle-accurate Commodore 64 emulator"
|
|
8
|
+
homepage "https://github.com/elektronaut/badline"
|
|
9
|
+
url "https://github.com/elektronaut/badline/releases/download/v0.3.0/badline-0.3.0-spinel-15f037af.tar.gz"
|
|
10
|
+
sha256 "0000000000000000000000000000000000000000000000000000000000000000"
|
|
11
|
+
license "MIT"
|
|
12
|
+
|
|
13
|
+
depends_on "sdl2"
|
|
14
|
+
|
|
15
|
+
uses_from_macos "libxcrypt"
|
|
16
|
+
|
|
17
|
+
def install
|
|
18
|
+
system "make"
|
|
19
|
+
libexec.install "badline"
|
|
20
|
+
pkgshare.install "roms"
|
|
21
|
+
(bin / "badline").write_env_script libexec / "badline",
|
|
22
|
+
BADLINE_ROM_PATH: "${BADLINE_ROM_PATH:-#{pkgshare}/roms}"
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
test do
|
|
26
|
+
assert_match "badline #{version}", shell_output("#{bin}/badline --version")
|
|
27
|
+
|
|
28
|
+
ENV["SDL_VIDEODRIVER"] = "dummy"
|
|
29
|
+
ENV["SDL_RENDER_DRIVER"] = "software"
|
|
30
|
+
ENV["SDL_AUDIODRIVER"] = "dummy"
|
|
31
|
+
system bin / "badline", "--unpaced", "--no-sound", "--frames", "10", "--screenshot", testpath / "frame.bmp"
|
|
32
|
+
assert_path_exists testpath / "frame.bmp"
|
|
33
|
+
end
|
|
34
|
+
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: badline
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Inge Jørgensen
|
|
@@ -23,20 +23,6 @@ dependencies:
|
|
|
23
23
|
- - "~>"
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
25
|
version: '1.1'
|
|
26
|
-
- !ruby/object:Gem::Dependency
|
|
27
|
-
name: ruby-sdl2
|
|
28
|
-
requirement: !ruby/object:Gem::Requirement
|
|
29
|
-
requirements:
|
|
30
|
-
- - "~>"
|
|
31
|
-
- !ruby/object:Gem::Version
|
|
32
|
-
version: '0.3'
|
|
33
|
-
type: :runtime
|
|
34
|
-
prerelease: false
|
|
35
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
-
requirements:
|
|
37
|
-
- - "~>"
|
|
38
|
-
- !ruby/object:Gem::Version
|
|
39
|
-
version: '0.3'
|
|
40
26
|
description: Badline is a Commodore 64 emulator written in Ruby, implementing cycle-accurate
|
|
41
27
|
timing and hardware behavior. Supports PRG/P00 programs, D64/D71/D81 disk images,
|
|
42
28
|
T64 archives, TAP tapes, CRT cartridges, SID tunes and host directories mounted
|
|
@@ -44,20 +30,21 @@ description: Badline is a Commodore 64 emulator written in Ruby, implementing cy
|
|
|
44
30
|
email:
|
|
45
31
|
- inge@elektronaut.no
|
|
46
32
|
executables:
|
|
47
|
-
- badline
|
|
48
|
-
- badline-sid
|
|
33
|
+
- badline-ruby
|
|
49
34
|
extensions: []
|
|
50
35
|
extra_rdoc_files: []
|
|
51
36
|
files:
|
|
37
|
+
- ".claude/hooks/session-start.sh"
|
|
38
|
+
- ".claude/settings.json"
|
|
52
39
|
- CHANGELOG.md
|
|
53
40
|
- CLAUDE.md
|
|
54
41
|
- CODE_OF_CONDUCT.md
|
|
55
42
|
- CONTRIBUTING.md
|
|
56
43
|
- MIT-LICENSE
|
|
57
44
|
- README.md
|
|
45
|
+
- SECURITY.md
|
|
58
46
|
- doc/pinned-behaviour.md
|
|
59
|
-
- exe/badline
|
|
60
|
-
- exe/badline-sid
|
|
47
|
+
- exe/badline-ruby
|
|
61
48
|
- lib/badline.rb
|
|
62
49
|
- lib/badline/address_bus.rb
|
|
63
50
|
- lib/badline/addressable.rb
|
|
@@ -68,11 +55,12 @@ files:
|
|
|
68
55
|
- lib/badline/audio/console.rb
|
|
69
56
|
- lib/badline/audio/jukebox.rb
|
|
70
57
|
- lib/badline/audio/machine_player.rb
|
|
71
|
-
- lib/badline/audio/options.rb
|
|
72
58
|
- lib/badline/audio/pcm_writer.rb
|
|
73
59
|
- lib/badline/audio/playback.rb
|
|
74
60
|
- lib/badline/audio/renderer.rb
|
|
75
61
|
- lib/badline/audio/sdl_sink.rb
|
|
62
|
+
- lib/badline/audio/stream.rb
|
|
63
|
+
- lib/badline/audio/terminal.rb
|
|
76
64
|
- lib/badline/audio/wav.rb
|
|
77
65
|
- lib/badline/cartridge.rb
|
|
78
66
|
- lib/badline/cartridge/action_replay.rb
|
|
@@ -88,6 +76,7 @@ files:
|
|
|
88
76
|
- lib/badline/cartridge/fun_play.rb
|
|
89
77
|
- lib/badline/cartridge/g_mod2.rb
|
|
90
78
|
- lib/badline/cartridge/game_system.rb
|
|
79
|
+
- lib/badline/cartridge/geo_ram.rb
|
|
91
80
|
- lib/badline/cartridge/kcs_power.rb
|
|
92
81
|
- lib/badline/cartridge/mach5.rb
|
|
93
82
|
- lib/badline/cartridge/magic_desk.rb
|
|
@@ -101,6 +90,7 @@ files:
|
|
|
101
90
|
- lib/badline/cartridge/super_games.rb
|
|
102
91
|
- lib/badline/cartridge/westermann.rb
|
|
103
92
|
- lib/badline/cartridge/zaxxon.rb
|
|
93
|
+
- lib/badline/checkpoint.rb
|
|
104
94
|
- lib/badline/chrout_trap.rb
|
|
105
95
|
- lib/badline/cia.rb
|
|
106
96
|
- lib/badline/cia/interrupt_register.rb
|
|
@@ -145,7 +135,13 @@ files:
|
|
|
145
135
|
- lib/badline/kernal_trap.rb
|
|
146
136
|
- lib/badline/kernal_trap/channel.rb
|
|
147
137
|
- lib/badline/kernal_trap/drive.rb
|
|
138
|
+
- lib/badline/kernal_trap/drive/block_commands.rb
|
|
139
|
+
- lib/badline/kernal_trap/drive/channels.rb
|
|
140
|
+
- lib/badline/kernal_trap/drive/memory.rb
|
|
141
|
+
- lib/badline/kernal_trap/drive/parameters.rb
|
|
148
142
|
- lib/badline/kernal_trap/drive/status.rb
|
|
143
|
+
- lib/badline/kernal_trap/drive/write_file.rb
|
|
144
|
+
- lib/badline/kernal_trap/drive/writes.rb
|
|
149
145
|
- lib/badline/kernal_trap/file.rb
|
|
150
146
|
- lib/badline/kernal_trap/load.rb
|
|
151
147
|
- lib/badline/kernal_trap/routine.rb
|
|
@@ -155,6 +151,13 @@ files:
|
|
|
155
151
|
- lib/badline/keyboard_buffer.rb
|
|
156
152
|
- lib/badline/media.rb
|
|
157
153
|
- lib/badline/memory.rb
|
|
154
|
+
- lib/badline/options.rb
|
|
155
|
+
- lib/badline/ram_expansion.rb
|
|
156
|
+
- lib/badline/ram_expansion/banking.rb
|
|
157
|
+
- lib/badline/ram_expansion/plus256k.rb
|
|
158
|
+
- lib/badline/ram_expansion/plus60k.rb
|
|
159
|
+
- lib/badline/ram_expansion/unexpanded.rb
|
|
160
|
+
- lib/badline/region.rb
|
|
158
161
|
- lib/badline/rom.rb
|
|
159
162
|
- lib/badline/roms/README
|
|
160
163
|
- lib/badline/roms/basic.rom
|
|
@@ -163,7 +166,11 @@ files:
|
|
|
163
166
|
- lib/badline/roms/eapi/README
|
|
164
167
|
- lib/badline/roms/eapi/eapi-am29f040-14
|
|
165
168
|
- lib/badline/roms/kernal.rom
|
|
169
|
+
- lib/badline/sdl.rb
|
|
170
|
+
- lib/badline/sdl/events.rb
|
|
171
|
+
- lib/badline/sdl/functions.rb
|
|
166
172
|
- lib/badline/sid.rb
|
|
173
|
+
- lib/badline/sid/catch_up.rb
|
|
167
174
|
- lib/badline/sid/decimator.rb
|
|
168
175
|
- lib/badline/sid/envelope.rb
|
|
169
176
|
- lib/badline/sid/filter.rb
|
|
@@ -179,6 +186,9 @@ files:
|
|
|
179
186
|
- lib/badline/storage/d71_image.rb
|
|
180
187
|
- lib/badline/storage/d81_image.rb
|
|
181
188
|
- lib/badline/storage/disk_image.rb
|
|
189
|
+
- lib/badline/storage/disk_image/bam.rb
|
|
190
|
+
- lib/badline/storage/disk_image/directory.rb
|
|
191
|
+
- lib/badline/storage/disk_image/writing.rb
|
|
182
192
|
- lib/badline/storage/host_directory.rb
|
|
183
193
|
- lib/badline/storage/p00.rb
|
|
184
194
|
- lib/badline/storage/sid_file.rb
|
|
@@ -188,6 +198,12 @@ files:
|
|
|
188
198
|
- lib/badline/time_of_day.rb
|
|
189
199
|
- lib/badline/traps.rb
|
|
190
200
|
- lib/badline/version.rb
|
|
201
|
+
- lib/badline/via.rb
|
|
202
|
+
- lib/badline/via/control_lines.rb
|
|
203
|
+
- lib/badline/via/interrupt_register.rb
|
|
204
|
+
- lib/badline/via/shift_register.rb
|
|
205
|
+
- lib/badline/via/timer1.rb
|
|
206
|
+
- lib/badline/via/timer2.rb
|
|
191
207
|
- lib/badline/vic.rb
|
|
192
208
|
- lib/badline/vic/bank.rb
|
|
193
209
|
- lib/badline/vic/border_mask.rb
|
|
@@ -204,6 +220,7 @@ files:
|
|
|
204
220
|
- lib/badline/vic/sprite/internal_bus.rb
|
|
205
221
|
- lib/badline/vic/sprite/shifter.rb
|
|
206
222
|
- lib/badline/vic/sprites.rb
|
|
223
|
+
- packaging/homebrew/badline.rb
|
|
207
224
|
- vendor/.gitkeep
|
|
208
225
|
homepage: https://github.com/elektronaut/badline
|
|
209
226
|
licenses:
|
data/exe/badline
DELETED
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env ruby
|
|
2
|
-
# frozen_string_literal: true
|
|
3
|
-
|
|
4
|
-
lib = File.expand_path("../lib", __dir__)
|
|
5
|
-
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
6
|
-
|
|
7
|
-
require "optparse"
|
|
8
|
-
|
|
9
|
-
options = { autostart: true, jit: true }
|
|
10
|
-
|
|
11
|
-
parser = OptionParser.new do |opts|
|
|
12
|
-
opts.banner = <<~BANNER
|
|
13
|
-
Usage: badline [options] [media]
|
|
14
|
-
|
|
15
|
-
Media can be a .prg/.p00 program, a .d64/.d71/.d81 disk image,
|
|
16
|
-
a .t64 tape archive, a .tap tape, a .crt cartridge, a .sid tune, or
|
|
17
|
-
a directory to mount as device 8.
|
|
18
|
-
|
|
19
|
-
Options:
|
|
20
|
-
BANNER
|
|
21
|
-
|
|
22
|
-
opts.on("--no-autostart", "Boot to READY. instead of running the program") do
|
|
23
|
-
options[:autostart] = false
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
opts.on("--song N", Integer, "Subtune of a .sid to play (default: the tune's own)") do |n|
|
|
27
|
-
options[:song] = n
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
opts.on("--sid MODEL", { "6581" => :mos6581, "8580" => :mos8580 },
|
|
31
|
-
"SID to fit: 6581 or 8580 (default: a .sid tune's own, else 6581)") do |model|
|
|
32
|
-
options[:sid_model] = model
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
opts.on("--disable-jit", "Run without enabling YJIT") do
|
|
36
|
-
options[:jit] = false
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
opts.on("-h", "--help", "Show this help") do
|
|
40
|
-
puts opts
|
|
41
|
-
exit
|
|
42
|
-
end
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
begin
|
|
46
|
-
parser.parse!
|
|
47
|
-
rescue OptionParser::ParseError => e
|
|
48
|
-
warn "badline: #{e.message}"
|
|
49
|
-
warn "Try 'badline --help' for more information."
|
|
50
|
-
exit 1
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
media_path = ARGV[0]
|
|
54
|
-
if media_path && !File.exist?(media_path)
|
|
55
|
-
warn "badline: no such file or directory: #{media_path}"
|
|
56
|
-
exit 1
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
jit_available = defined?(RubyVM::YJIT) && !RubyVM::YJIT.enabled?
|
|
60
|
-
RubyVM::YJIT.enable if options[:jit] && jit_available
|
|
61
|
-
|
|
62
|
-
require "badline"
|
|
63
|
-
require "badline/gui"
|
|
64
|
-
|
|
65
|
-
begin
|
|
66
|
-
application = Badline::GUI::Application.new(media_path:, autostart: options[:autostart],
|
|
67
|
-
song: options[:song], sid_model: options[:sid_model])
|
|
68
|
-
rescue Badline::Storage::SIDFile::FormatError, Badline::Storage::T64::FormatError,
|
|
69
|
-
Badline::Storage::TAP::FormatError, Badline::Storage::CRTFile::FormatError,
|
|
70
|
-
Badline::Cartridge::UnsupportedTypeError => e
|
|
71
|
-
warn "badline: #{media_path}: #{e.message}"
|
|
72
|
-
exit 1
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
application.run
|
data/exe/badline-sid
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env ruby
|
|
2
|
-
# frozen_string_literal: true
|
|
3
|
-
|
|
4
|
-
lib = File.expand_path("../lib", __dir__)
|
|
5
|
-
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
6
|
-
|
|
7
|
-
require "badline/audio/options"
|
|
8
|
-
|
|
9
|
-
begin
|
|
10
|
-
options = Badline::Audio::Options.parse(ARGV)
|
|
11
|
-
rescue Badline::Audio::Options::Error => e
|
|
12
|
-
warn "badline-sid: #{e.message}"
|
|
13
|
-
warn "Try 'badline-sid --help' for more information."
|
|
14
|
-
exit 1
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
if options.help?
|
|
18
|
-
puts options.help
|
|
19
|
-
exit
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
jit_available = defined?(RubyVM::YJIT) && !RubyVM::YJIT.enabled?
|
|
23
|
-
RubyVM::YJIT.enable if options.jit? && jit_available
|
|
24
|
-
|
|
25
|
-
require "badline"
|
|
26
|
-
|
|
27
|
-
begin
|
|
28
|
-
Badline::Audio::CLI.new(options).run
|
|
29
|
-
rescue Badline::Audio::CLI::Error => e
|
|
30
|
-
warn "badline-sid: #{e.message}"
|
|
31
|
-
exit 1
|
|
32
|
-
rescue Interrupt
|
|
33
|
-
exit 130
|
|
34
|
-
end
|