badline 0.11.1 → 0.12.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 +26 -0
- data/CLAUDE.md +8 -7
- data/CONTRIBUTING.md +7 -6
- data/README.md +12 -5
- data/doc/media.md +28 -1
- data/doc/pinned-behaviour.md +31 -1
- data/exe/badline-ruby +1 -1
- data/lib/badline/address_bus/saved_state.rb +4 -10
- data/lib/badline/address_bus.rb +8 -27
- data/lib/badline/audio/bare_player.rb +1 -1
- data/lib/badline/c128/bus.rb +17 -52
- data/lib/badline/c128/bus_state.rb +5 -5
- data/lib/badline/c128/drives.rb +35 -22
- data/lib/badline/c128/fast_serial.rb +1 -0
- data/lib/badline/c128/io_pages.rb +35 -0
- data/lib/badline/c128/mmu.rb +9 -7
- data/lib/badline/c128/modes.rb +17 -4
- data/lib/badline/c128/saved_state.rb +22 -0
- data/lib/badline/c128/z80.rb +49 -0
- data/lib/badline/c128/z80_pages.rb +118 -0
- data/lib/badline/c128/z80_turns.rb +106 -0
- data/lib/badline/c128.rb +21 -83
- data/lib/badline/cartridge/action_replay.rb +2 -2
- data/lib/badline/cartridge/comal80.rb +1 -7
- data/lib/badline/cartridge/dinamic.rb +0 -8
- data/lib/badline/cartridge/final_cartridge3.rb +6 -6
- data/lib/badline/cartridge/fun_play.rb +0 -8
- data/lib/badline/cartridge/game_system.rb +0 -8
- data/lib/badline/cartridge/kcs_power.rb +10 -7
- data/lib/badline/cartridge/mach5.rb +2 -4
- data/lib/badline/cartridge/magic_desk.rb +0 -8
- data/lib/badline/cartridge/ocean.rb +0 -8
- data/lib/badline/cartridge/pagefox.rb +2 -2
- data/lib/badline/cartridge/retro_replay.rb +7 -6
- data/lib/badline/cartridge/rex_utility.rb +2 -6
- data/lib/badline/cartridge/rgcd.rb +2 -2
- data/lib/badline/cartridge/simons_basic.rb +0 -5
- data/lib/badline/cartridge/super_games.rb +4 -2
- data/lib/badline/cartridge/westermann.rb +2 -6
- data/lib/badline/cartridge.rb +37 -9
- data/lib/badline/cia/fast_forward.rb +21 -3
- data/lib/badline/cia.rb +8 -5
- data/lib/badline/computer/attachments.rb +14 -95
- data/lib/badline/computer/common.rb +34 -0
- data/lib/badline/computer/kernal_traps.rb +65 -5
- data/lib/badline/computer/true_drives.rb +69 -0
- data/lib/badline/computer.rb +9 -68
- data/lib/badline/core.rb +4 -2
- data/lib/badline/cpu_port.rb +112 -0
- data/lib/badline/drive/core.rb +8 -33
- data/lib/badline/drive/fast_serial.rb +73 -0
- data/lib/badline/drive/idle.rb +1 -11
- data/lib/badline/drive/orbit.rb +2 -5
- data/lib/badline/drive/sleep.rb +0 -2
- data/lib/badline/drive/vias.rb +84 -0
- data/lib/badline/drive.rb +2 -0
- data/lib/badline/drive1541/disk.rb +14 -17
- data/lib/badline/drive1541/disk_state.rb +7 -15
- data/lib/badline/drive1541.rb +1 -0
- data/lib/badline/drive1571/wd1770.rb +94 -8
- data/lib/badline/drive1571.rb +8 -58
- data/lib/badline/drive1581/bus.rb +137 -0
- data/lib/badline/drive1581/cpu.rb +11 -0
- data/lib/badline/drive1581/disk.rb +135 -0
- data/lib/badline/drive1581/disk_state.rb +87 -0
- data/lib/badline/drive1581/mechanism.rb +117 -0
- data/lib/badline/drive1581/serial_port.rb +52 -0
- data/lib/badline/drive1581/slot.rb +78 -0
- data/lib/badline/drive1581/track.rb +100 -0
- data/lib/badline/drive1581/track_bytes.rb +75 -0
- data/lib/badline/drive1581/wd1772/registers.rb +89 -0
- data/lib/badline/drive1581/wd1772/sector_writes.rb +73 -0
- data/lib/badline/drive1581/wd1772/sectors.rb +150 -0
- data/lib/badline/drive1581/wd1772/state.rb +66 -0
- data/lib/badline/drive1581/wd1772/stepping.rb +109 -0
- data/lib/badline/drive1581/wd1772/tracks.rb +125 -0
- data/lib/badline/drive1581/wd1772/turn.rb +73 -0
- data/lib/badline/drive1581/wd1772.rb +216 -0
- data/lib/badline/drive1581.rb +233 -0
- data/lib/badline/frontend/app.rb +1 -3
- data/lib/badline/frontend/audio_device.rb +67 -0
- data/lib/badline/frontend/audio_sink.rb +16 -50
- data/lib/badline/frontend/boot.rb +24 -67
- data/lib/badline/frontend/buttons.rb +7 -7
- data/lib/badline/frontend/keys.rb +33 -8
- data/lib/badline/frontend/media_slots.rb +42 -0
- data/lib/badline/frontend/{confirmation.rb → menu/confirmation.rb} +4 -4
- data/lib/badline/frontend/{file_browser.rb → menu/file_browser.rb} +18 -34
- data/lib/badline/frontend/{menu_dialogs.rb → menu/menu_dialogs.rb} +3 -7
- data/lib/badline/frontend/{menu_media.rb → menu/menu_media.rb} +26 -51
- data/lib/badline/frontend/{menu_pages.rb → menu/menu_pages.rb} +5 -8
- data/lib/badline/frontend/{name_field.rb → menu/name_field.rb} +11 -24
- data/lib/badline/frontend/{pause_menu.rb → menu/pause_menu.rb} +13 -28
- data/lib/badline/frontend/menu/theme.rb +18 -0
- data/lib/badline/frontend/{filter_view.rb → player/filter_view.rb} +4 -4
- data/lib/badline/frontend/{info_view.rb → player/info_view.rb} +3 -3
- data/lib/badline/frontend/{player_window.rb → player/player_controller.rb} +17 -25
- data/lib/badline/frontend/{player_footer.rb → player/player_footer.rb} +8 -8
- data/lib/badline/frontend/{player_header.rb → player/player_header.rb} +5 -5
- data/lib/badline/frontend/{player_screen.rb → player/player_screen.rb} +4 -5
- data/lib/badline/frontend/{scope.rb → player/scope.rb} +1 -1
- data/lib/badline/frontend/{sid_view.rb → player/sid_view.rb} +16 -20
- data/lib/badline/frontend/player/theme.rb +18 -0
- data/lib/badline/frontend/{visualizer_view.rb → player/visualizer_view.rb} +8 -8
- data/lib/badline/frontend/sound.rb +19 -38
- data/lib/badline/frontend/timeline.rb +3 -14
- data/lib/badline/frontend.rb +24 -20
- data/lib/badline/iec_bus.rb +14 -2
- data/lib/badline/kernal_trap/{file.rb → file_routine.rb} +1 -1
- data/lib/badline/kernal_trap/load.rb +1 -1
- data/lib/badline/kernal_trap/save.rb +1 -1
- data/lib/badline/kernal_trap.rb +1 -1
- data/lib/badline/machine/clocking.rb +50 -0
- data/lib/badline/machine.rb +63 -6
- data/lib/badline/media/boot_disk.rb +50 -0
- data/lib/badline/media/disk_list.rb +3 -3
- data/lib/badline/media/disk_set.rb +1 -1
- data/lib/badline/media/extensions.rb +33 -0
- data/lib/badline/media/not_disk.rb +8 -0
- data/lib/badline/media/program.rb +28 -0
- data/lib/badline/media/true_drive.rb +38 -17
- data/lib/badline/media/tune.rb +35 -0
- data/lib/badline/media/vic20_cartridge.rb +1 -7
- data/lib/badline/media/vic20_media.rb +38 -80
- data/lib/badline/media.rb +90 -81
- data/lib/badline/options/event.rb +4 -2
- data/lib/badline/options/family.rb +3 -9
- data/lib/badline/options/help.rb +2 -1
- data/lib/badline/roms/README +9 -0
- data/lib/badline/roms/dos1581.rom +0 -0
- data/lib/badline/sdl.rb +37 -20
- data/lib/badline/sid.rb +1 -1
- data/lib/badline/snapshot/machine_state.rb +9 -1
- data/lib/badline/snapshot/state.rb +6 -4
- data/lib/badline/snapshot.rb +11 -9
- data/lib/badline/storage/d64_image.rb +15 -7
- data/lib/badline/storage/disk_image/writing.rb +2 -2
- data/lib/badline/storage/disk_image.rb +1 -1
- data/lib/badline/storage/host_directory.rb +1 -1
- data/lib/badline/storage/p00.rb +8 -0
- data/lib/badline/storage/t64.rb +1 -1
- data/lib/badline/time_of_day.rb +3 -3
- data/lib/badline/version.rb +1 -1
- data/lib/badline/vic/bank.rb +1 -1
- data/lib/badline/vic/border_mask.rb +1 -1
- data/lib/badline/vic/collisions.rb +1 -1
- data/lib/badline/vic/color_patches.rb +1 -1
- data/lib/badline/vic/display_state.rb +1 -1
- data/lib/badline/vic/graphics_mode.rb +1 -1
- data/lib/badline/vic/graphics_shifter.rb +1 -1
- data/lib/badline/vic/register_log.rb +1 -1
- data/lib/badline/vic/registers.rb +1 -1
- data/lib/badline/vic/sequencer.rb +1 -1
- data/lib/badline/vic/sequencer_output.rb +1 -1
- data/lib/badline/vic/sequencer_state.rb +1 -1
- data/lib/badline/vic/sprite/internal_bus.rb +1 -1
- data/lib/badline/vic/sprite/saved_state.rb +1 -1
- data/lib/badline/vic/sprite/shifter.rb +1 -1
- data/lib/badline/vic/sprite/timing.rb +1 -1
- data/lib/badline/vic/sprite.rb +1 -1
- data/lib/badline/vic/sprites.rb +1 -1
- data/lib/badline/vic/sprites_state.rb +1 -1
- data/lib/badline/vic.rb +6 -5
- data/lib/badline/vic20/attachments.rb +15 -71
- data/lib/badline/vic20/saved_state.rb +2 -30
- data/lib/badline/vic20.rb +4 -37
- data/lib/badline/z80/core.rb +2 -0
- data/lib/badline/z80/saved_state.rb +69 -0
- metadata +61 -23
- data/lib/badline/cycleable.rb +0 -38
- /data/lib/badline/frontend/{menu_ports.rb → menu/menu_ports.rb} +0 -0
- /data/lib/badline/frontend/{player_commands.rb → player/player_commands.rb} +0 -0
- /data/lib/badline/frontend/{player_state.rb → player/player_state.rb} +0 -0
- /data/lib/badline/frontend/{sid_history.rb → player/sid_history.rb} +0 -0
data/lib/badline/c128.rb
CHANGED
|
@@ -4,11 +4,15 @@ require "badline/c128/model"
|
|
|
4
4
|
require "badline/c128/mmu"
|
|
5
5
|
require "badline/c128/vdc"
|
|
6
6
|
require "badline/c128/mmu_pages"
|
|
7
|
+
require "badline/c128/io_pages"
|
|
8
|
+
require "badline/c128/z80_pages"
|
|
7
9
|
require "badline/c128/color_lines"
|
|
8
10
|
require "badline/c128/banks"
|
|
9
11
|
require "badline/c128/bus_state"
|
|
10
12
|
require "badline/c128/bus"
|
|
11
13
|
require "badline/c128/cpu"
|
|
14
|
+
require "badline/c128/z80"
|
|
15
|
+
require "badline/c128/z80_turns"
|
|
12
16
|
require "badline/c128/saved_state"
|
|
13
17
|
require "badline/c128/keys"
|
|
14
18
|
require "badline/c128/modes"
|
|
@@ -21,11 +25,12 @@ module Badline
|
|
|
21
25
|
# the VDC and its 80 column display, and 128K of RAM, which the 8722 MMU
|
|
22
26
|
# maps (C128::Bus).
|
|
23
27
|
#
|
|
24
|
-
# Built for C128 mode, it
|
|
25
|
-
#
|
|
26
|
-
#
|
|
27
|
-
#
|
|
28
|
-
#
|
|
28
|
+
# Built for C128 mode, it powers on as the C128 does: the Z80 runs the
|
|
29
|
+
# boot code in its BIOS and hands the bus to the 8502, which then runs
|
|
30
|
+
# its reset vector into BASIC 7.0 and the C128 KERNAL. The KERNAL goes
|
|
31
|
+
# to C64 mode as on the C128, with C= held at reset, a C64 cartridge or
|
|
32
|
+
# GO64. MCR bit 0 hands the bus back and forth from then on, and the
|
|
33
|
+
# Z80 runs 2 T-states in each cycle the VIC leaves it the bus.
|
|
29
34
|
#
|
|
30
35
|
# Built for C64 mode, the default, it powers on in C64 mode, the state
|
|
31
36
|
# the C128 KERNAL reaches when C= is held at power-on, and a reset comes
|
|
@@ -39,12 +44,15 @@ module Badline
|
|
|
39
44
|
class C128
|
|
40
45
|
include IntegerHelper
|
|
41
46
|
include KeyboardBuffer
|
|
47
|
+
include Machine::Clocking
|
|
48
|
+
include Computer::Common
|
|
42
49
|
include Computer::Attachments
|
|
43
50
|
include Computer::KernalTraps
|
|
44
51
|
include Keys
|
|
45
52
|
include Modes
|
|
46
53
|
include Drives
|
|
47
54
|
include FastSerial
|
|
55
|
+
include Z80Turns
|
|
48
56
|
|
|
49
57
|
# The C64's 8x8 matrix and the three rows K0-K2 select, in port B
|
|
50
58
|
# column order.
|
|
@@ -65,10 +73,6 @@ module Badline
|
|
|
65
73
|
|
|
66
74
|
attr_reader :cpu, :cycles, :drive1541, :model, :init_threshold
|
|
67
75
|
|
|
68
|
-
# The path of the disk or directory device 8 serves through the traps
|
|
69
|
-
# (Computer::Attachments), or an empty one.
|
|
70
|
-
def mounted_path = @drive.nil? ? "" : @drive.path
|
|
71
|
-
|
|
72
76
|
def family = :c128
|
|
73
77
|
|
|
74
78
|
def address_bus = @bus
|
|
@@ -116,6 +120,7 @@ module Badline
|
|
|
116
120
|
@holding_commodore = false
|
|
117
121
|
@bus = Bus.new(@model, sid_model: sid_model || @model.sid_model, mode:)
|
|
118
122
|
@cpu = CPU.new(@bus, debug:)
|
|
123
|
+
build_z80
|
|
119
124
|
@vic = @bus.vic
|
|
120
125
|
@vic.open_bus = -> { @bus.ram.peek(@cpu.program_counter) }
|
|
121
126
|
@cia1 = @bus.cia1
|
|
@@ -141,6 +146,8 @@ module Badline
|
|
|
141
146
|
# mode the CPU runs in both halves of the cycle (#clock_fast), and the
|
|
142
147
|
# TEST bit steps the raster counter in every cycle.
|
|
143
148
|
def cycle!
|
|
149
|
+
return z80_cycle! if @z80_turn
|
|
150
|
+
|
|
144
151
|
handle_init if @cycles == @init_threshold
|
|
145
152
|
feed_keyboard if @pending_keys
|
|
146
153
|
|
|
@@ -164,36 +171,6 @@ module Badline
|
|
|
164
171
|
@cycles += 1
|
|
165
172
|
end
|
|
166
173
|
|
|
167
|
-
# Runs `count` cycles, one #cycle! after another.
|
|
168
|
-
def run_cycles(count)
|
|
169
|
-
i = 0
|
|
170
|
-
while i < count
|
|
171
|
-
cycle!
|
|
172
|
-
i += 1
|
|
173
|
-
end
|
|
174
|
-
end
|
|
175
|
-
|
|
176
|
-
# Runs cycles until the block returns true or the cycle count passes
|
|
177
|
-
# `limit`, checking before each cycle.
|
|
178
|
-
def run_until(limit)
|
|
179
|
-
cycle! until yield || @cycles > limit
|
|
180
|
-
end
|
|
181
|
-
|
|
182
|
-
def on_init(&block)
|
|
183
|
-
if @cycles < init_threshold
|
|
184
|
-
@init_handlers << block
|
|
185
|
-
else
|
|
186
|
-
block.call
|
|
187
|
-
end
|
|
188
|
-
end
|
|
189
|
-
|
|
190
|
-
# The clock, the raster and the crop of the VIC-IIe's region.
|
|
191
|
-
def timing
|
|
192
|
-
region = @bus.region
|
|
193
|
-
Timing.new(clock_hz: region.clock_hz, cycles_per_line: region.cycles_per_line,
|
|
194
|
-
lines_per_frame: region.lines_per_frame, crop: region.crop, pixel_width: 1)
|
|
195
|
-
end
|
|
196
|
-
|
|
197
174
|
# The chip whose #display a front end shows: the VIC-IIe, or with
|
|
198
175
|
# :vdc the VDC, whose display paints only while its #render is on.
|
|
199
176
|
def video(chip = :vic) = chip == :vdc ? @vdc : @vic
|
|
@@ -208,12 +185,6 @@ module Badline
|
|
|
208
185
|
# The chip a front end records the machine's sound from.
|
|
209
186
|
def sound_source = @sid
|
|
210
187
|
|
|
211
|
-
def load_prg(data)
|
|
212
|
-
uint16(data[0], data[1]).tap do |load_addr|
|
|
213
|
-
ram.write(load_addr, data[2..])
|
|
214
|
-
end
|
|
215
|
-
end
|
|
216
|
-
|
|
217
188
|
# A cartridge goes in with the power off.
|
|
218
189
|
def attach_cartridge(cartridge)
|
|
219
190
|
connect_cartridge(cartridge)
|
|
@@ -241,23 +212,16 @@ module Badline
|
|
|
241
212
|
@drive&.reset!
|
|
242
213
|
@drive1541&.reset!
|
|
243
214
|
@drive1571&.reset!
|
|
215
|
+
@drive1581&.reset!
|
|
244
216
|
@nmi_asserted = false
|
|
245
|
-
|
|
246
|
-
end
|
|
247
|
-
|
|
248
|
-
# CHROUT is at $FFD2 in both KERNALs, and the trap follows the mode.
|
|
249
|
-
def capture_output
|
|
250
|
-
@capture_output ||= ChroutTrap.new(cpu:, bus: @bus, layout: trap_layout).tap do |trap|
|
|
251
|
-
cpu.install_trap(ChroutTrap::ADDRESS) { trap.call }
|
|
252
|
-
end
|
|
253
|
-
end
|
|
254
|
-
|
|
255
|
-
def inspect
|
|
256
|
-
"#<#{self.class.name} cycles=#{@cycles} cpu=(#{@cpu.inspect})>"
|
|
217
|
+
reset_z80
|
|
257
218
|
end
|
|
258
219
|
|
|
259
220
|
private
|
|
260
221
|
|
|
222
|
+
# The bus the KERNAL traps read and write through.
|
|
223
|
+
def trap_bus = @bus
|
|
224
|
+
|
|
261
225
|
# The C128 KERNAL leaves $D02F at $FF, every extra keyboard row
|
|
262
226
|
# deselected, before it jumps to the C64's reset.
|
|
263
227
|
def enter_c64_mode
|
|
@@ -265,15 +229,6 @@ module Badline
|
|
|
265
229
|
@bus.control_ports.extra_rows = 0xff
|
|
266
230
|
end
|
|
267
231
|
|
|
268
|
-
# The NMI line is wired-OR between CIA 2, the cartridge and RESTORE,
|
|
269
|
-
# and the CPU takes an interrupt on its falling edge.
|
|
270
|
-
def drive_nmi
|
|
271
|
-
nmi = @cia2.interrupted? || @cartridge_nmi || @restore_pulse
|
|
272
|
-
@restore_pulse = false
|
|
273
|
-
@cpu.nmi = true if nmi && !@nmi_asserted
|
|
274
|
-
@nmi_asserted = nmi
|
|
275
|
-
end
|
|
276
|
-
|
|
277
232
|
# BA halts the CPU on a read cycle.
|
|
278
233
|
def clock_cpu
|
|
279
234
|
@cpu.pending_write? || !@vic.ba_low? ? @cpu.cycle! : @cpu.stall!
|
|
@@ -301,22 +256,5 @@ module Badline
|
|
|
301
256
|
@cpu.cycle!
|
|
302
257
|
@vic.take_cpu_bus(phi1, @bus.data, @bus.vic_access?)
|
|
303
258
|
end
|
|
304
|
-
|
|
305
|
-
def handle_init
|
|
306
|
-
@init_handlers.each(&:call)
|
|
307
|
-
end
|
|
308
|
-
|
|
309
|
-
# The keyboard buffer of the KERNAL the machine runs: the C64's, or in
|
|
310
|
-
# C128 mode BASIC 7.0's.
|
|
311
|
-
def feed_keyboard
|
|
312
|
-
c64 = mode == :c64
|
|
313
|
-
count = c64 ? KeyboardBuffer::COUNT : C128_KEYBOARD_COUNT
|
|
314
|
-
return unless ram.peek(count).zero?
|
|
315
|
-
|
|
316
|
-
chunk = @pending_keys.shift(KeyboardBuffer::CAPACITY)
|
|
317
|
-
ram.write(c64 ? KeyboardBuffer::ADDRESS : C128_KEYBOARD_BUFFER, chunk)
|
|
318
|
-
ram.poke(count, chunk.length)
|
|
319
|
-
@pending_keys = nil if @pending_keys.empty?
|
|
320
|
-
end
|
|
321
259
|
end
|
|
322
260
|
end
|
|
@@ -65,12 +65,12 @@ module Badline
|
|
|
65
65
|
private
|
|
66
66
|
|
|
67
67
|
def save_mapper(out)
|
|
68
|
-
|
|
68
|
+
super
|
|
69
69
|
out.int(window_index(@rom, windows)).boolean(@active).boolean(@io2_ram).blob(@ram_data)
|
|
70
70
|
end
|
|
71
71
|
|
|
72
72
|
def load_mapper(input)
|
|
73
|
-
|
|
73
|
+
super
|
|
74
74
|
@rom = window_at(input.int, windows)
|
|
75
75
|
@active = input.boolean?
|
|
76
76
|
@io2_ram = input.boolean?
|
|
@@ -33,13 +33,7 @@ module Badline
|
|
|
33
33
|
|
|
34
34
|
private
|
|
35
35
|
|
|
36
|
-
def
|
|
37
|
-
save_windows(out, [*@roml_banks, *@romh_banks, EMPTY_BANK])
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
def load_mapper(input)
|
|
41
|
-
load_windows(input, [*@roml_banks, *@romh_banks, EMPTY_BANK])
|
|
42
|
-
end
|
|
36
|
+
def windows = [*@roml_banks, *@romh_banks, EMPTY_BANK]
|
|
43
37
|
|
|
44
38
|
def select(number, mode)
|
|
45
39
|
@roml = bank(@roml_banks, number)
|
|
@@ -24,14 +24,6 @@ module Badline
|
|
|
24
24
|
|
|
25
25
|
private
|
|
26
26
|
|
|
27
|
-
def save_mapper(out)
|
|
28
|
-
save_windows(out, [*@banks, EMPTY_BANK])
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
def load_mapper(input)
|
|
32
|
-
load_windows(input, [*@banks, EMPTY_BANK])
|
|
33
|
-
end
|
|
34
|
-
|
|
35
27
|
def install_chips(chips)
|
|
36
28
|
@banks = banks_from(chips).first
|
|
37
29
|
self.mode = :rom8k
|
|
@@ -49,31 +49,31 @@ module Badline
|
|
|
49
49
|
@phi1_ultimax = false
|
|
50
50
|
self.nmi = false
|
|
51
51
|
select(0)
|
|
52
|
-
|
|
53
|
-
changed!
|
|
52
|
+
select_mode(:rom16k)
|
|
54
53
|
end
|
|
55
54
|
|
|
56
55
|
def freeze!
|
|
57
56
|
@register_visible = true
|
|
58
57
|
@phi1_ultimax = false
|
|
59
|
-
|
|
60
|
-
changed!
|
|
58
|
+
select_mode(:ultimax)
|
|
61
59
|
end
|
|
62
60
|
|
|
63
61
|
private
|
|
64
62
|
|
|
65
63
|
def save_mapper(out)
|
|
66
|
-
|
|
64
|
+
super
|
|
67
65
|
out.boolean(@register_visible).boolean(@phi1_ultimax)
|
|
68
66
|
end
|
|
69
67
|
|
|
70
68
|
def load_mapper(input)
|
|
71
|
-
|
|
69
|
+
super
|
|
72
70
|
@io_bank = @roml
|
|
73
71
|
@register_visible = input.boolean?
|
|
74
72
|
@phi1_ultimax = input.boolean?
|
|
75
73
|
end
|
|
76
74
|
|
|
75
|
+
def windows = [*@roml_banks, *@romh_banks, EMPTY_BANK]
|
|
76
|
+
|
|
77
77
|
def select(number)
|
|
78
78
|
@io_bank = @roml = bank(@roml_banks, number)
|
|
79
79
|
@romh = bank(@romh_banks, number)
|
|
@@ -28,14 +28,6 @@ module Badline
|
|
|
28
28
|
|
|
29
29
|
private
|
|
30
30
|
|
|
31
|
-
def save_mapper(out)
|
|
32
|
-
save_windows(out, [*@banks, EMPTY_BANK])
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
def load_mapper(input)
|
|
36
|
-
load_windows(input, [*@banks, EMPTY_BANK])
|
|
37
|
-
end
|
|
38
|
-
|
|
39
31
|
def install_chips(chips)
|
|
40
32
|
@banks = []
|
|
41
33
|
chips.each { |chip| @banks[self.class.bank_number(chip.bank)] = rom_bank(chip.data) }
|
|
@@ -25,14 +25,6 @@ module Badline
|
|
|
25
25
|
|
|
26
26
|
private
|
|
27
27
|
|
|
28
|
-
def save_mapper(out)
|
|
29
|
-
save_windows(out, [*@banks, EMPTY_BANK])
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
def load_mapper(input)
|
|
33
|
-
load_windows(input, [*@banks, EMPTY_BANK])
|
|
34
|
-
end
|
|
35
|
-
|
|
36
28
|
def select(addr)
|
|
37
29
|
@roml = bank(@banks, addr & 0x3f)
|
|
38
30
|
changed!
|
|
@@ -26,7 +26,7 @@ module Badline
|
|
|
26
26
|
|
|
27
27
|
def peek(addr)
|
|
28
28
|
if addr < 0xdf00
|
|
29
|
-
|
|
29
|
+
select_mode(addr.anybits?(0x02) ? :off : :rom8k)
|
|
30
30
|
@roml.peek(IO1_ROM_PAGE | (addr & 0xff))
|
|
31
31
|
elsif addr.anybits?(0x80)
|
|
32
32
|
(@exrom << 7) | (@game << 6) | (open_bus(addr) & 0x3f)
|
|
@@ -37,7 +37,7 @@ module Badline
|
|
|
37
37
|
|
|
38
38
|
def poke(addr, value)
|
|
39
39
|
if addr < 0xdf00
|
|
40
|
-
|
|
40
|
+
select_mode(addr.anybits?(0x02) ? :ultimax : :rom16k)
|
|
41
41
|
elsif addr.nobits?(0x80)
|
|
42
42
|
@io_ram[addr & 0x7f] = value
|
|
43
43
|
end
|
|
@@ -45,19 +45,22 @@ module Badline
|
|
|
45
45
|
|
|
46
46
|
def reset
|
|
47
47
|
self.nmi = false
|
|
48
|
-
|
|
48
|
+
select_mode(:rom16k)
|
|
49
49
|
end
|
|
50
50
|
|
|
51
51
|
def freeze!
|
|
52
52
|
self.nmi = false
|
|
53
|
-
|
|
53
|
+
select_mode(:ultimax)
|
|
54
54
|
end
|
|
55
55
|
|
|
56
56
|
private
|
|
57
57
|
|
|
58
|
-
def
|
|
59
|
-
|
|
60
|
-
|
|
58
|
+
def save_mapper(out)
|
|
59
|
+
out.blob(@io_ram)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def load_mapper(input)
|
|
63
|
+
input.blob_into(@io_ram)
|
|
61
64
|
end
|
|
62
65
|
|
|
63
66
|
def install_chips(chips)
|
|
@@ -14,13 +14,11 @@ module Badline
|
|
|
14
14
|
end
|
|
15
15
|
|
|
16
16
|
def poke(addr, _value)
|
|
17
|
-
|
|
18
|
-
changed!
|
|
17
|
+
select_mode(addr < 0xdf00 ? :rom8k : :off)
|
|
19
18
|
end
|
|
20
19
|
|
|
21
20
|
def reset
|
|
22
|
-
|
|
23
|
-
changed!
|
|
21
|
+
select_mode(:rom8k)
|
|
24
22
|
end
|
|
25
23
|
|
|
26
24
|
private
|
|
@@ -20,14 +20,6 @@ module Badline
|
|
|
20
20
|
|
|
21
21
|
private
|
|
22
22
|
|
|
23
|
-
def save_mapper(out)
|
|
24
|
-
save_windows(out, [*@banks, EMPTY_BANK])
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
def load_mapper(input)
|
|
28
|
-
load_windows(input, [*@banks, EMPTY_BANK])
|
|
29
|
-
end
|
|
30
|
-
|
|
31
23
|
def install_chips(chips)
|
|
32
24
|
@banks = banks_from(chips).first
|
|
33
25
|
@bank_mask = (bank_mask(@banks) | 0x03) & 0x7f
|
|
@@ -19,14 +19,6 @@ module Badline
|
|
|
19
19
|
|
|
20
20
|
private
|
|
21
21
|
|
|
22
|
-
def save_mapper(out)
|
|
23
|
-
save_windows(out, [*@banks, EMPTY_BANK])
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
def load_mapper(input)
|
|
27
|
-
load_windows(input, [*@banks, EMPTY_BANK])
|
|
28
|
-
end
|
|
29
|
-
|
|
30
22
|
# In 16K mode the selected bank shows through ROMH as well.
|
|
31
23
|
def select_bank(number)
|
|
32
24
|
@roml = bank(@banks, number)
|
|
@@ -30,12 +30,12 @@ module Badline
|
|
|
30
30
|
private
|
|
31
31
|
|
|
32
32
|
def save_mapper(out)
|
|
33
|
-
|
|
33
|
+
super
|
|
34
34
|
@ram_banks.each { |ram| out.blob(ram.data) }
|
|
35
35
|
end
|
|
36
36
|
|
|
37
37
|
def load_mapper(input)
|
|
38
|
-
|
|
38
|
+
super
|
|
39
39
|
@ram_banks.each { |ram| input.blob_into(ram.data) }
|
|
40
40
|
end
|
|
41
41
|
|
|
@@ -90,6 +90,11 @@ module Badline
|
|
|
90
90
|
|
|
91
91
|
attr_reader :flash
|
|
92
92
|
|
|
93
|
+
def self.from_jumpers(crt, input)
|
|
94
|
+
input.boolean?
|
|
95
|
+
new(crt, flash_jumper: input.boolean?, bank_jumper: input.boolean?)
|
|
96
|
+
end
|
|
97
|
+
|
|
93
98
|
def initialize(crt, flash_jumper: false, bank_jumper: false)
|
|
94
99
|
@nordic = crt.subtype == 1
|
|
95
100
|
@flash_jumper = flash_jumper
|
|
@@ -131,9 +136,7 @@ module Badline
|
|
|
131
136
|
end
|
|
132
137
|
end
|
|
133
138
|
|
|
134
|
-
def phi1_ultimax?
|
|
135
|
-
@phi1_ultimax
|
|
136
|
-
end
|
|
139
|
+
def phi1_ultimax? = @phi1_ultimax
|
|
137
140
|
|
|
138
141
|
def ultimax_a000
|
|
139
142
|
@isolated[ram_view_bank] if @nordic && @ram_at_a000 && @frozen
|
|
@@ -157,9 +160,7 @@ module Badline
|
|
|
157
160
|
|
|
158
161
|
private
|
|
159
162
|
|
|
160
|
-
def freeze_allowed?
|
|
161
|
-
!@no_freeze && !@flash_jumper
|
|
162
|
-
end
|
|
163
|
+
def freeze_allowed? = !@no_freeze && !@flash_jumper
|
|
163
164
|
|
|
164
165
|
def status
|
|
165
166
|
((@bank & 0x03) << 3) | ((@bank & 0x04) << 5) | ((@bank & 0x08) << 2) | (@flash_jumper ? 0x01 : 0) |
|
|
@@ -10,16 +10,12 @@ module Badline
|
|
|
10
10
|
end
|
|
11
11
|
|
|
12
12
|
def peek(addr)
|
|
13
|
-
if addr >= 0xdf00
|
|
14
|
-
self.mode = (addr & 0xff) < 0xc0 ? :off : :rom8k
|
|
15
|
-
changed!
|
|
16
|
-
end
|
|
13
|
+
select_mode((addr & 0xff) < 0xc0 ? :off : :rom8k) if addr >= 0xdf00
|
|
17
14
|
open_bus(addr)
|
|
18
15
|
end
|
|
19
16
|
|
|
20
17
|
def reset
|
|
21
|
-
|
|
22
|
-
changed!
|
|
18
|
+
select_mode(:rom8k)
|
|
23
19
|
end
|
|
24
20
|
|
|
25
21
|
private
|
|
@@ -29,12 +29,12 @@ module Badline
|
|
|
29
29
|
private
|
|
30
30
|
|
|
31
31
|
def save_mapper(out)
|
|
32
|
-
|
|
32
|
+
super
|
|
33
33
|
out.boolean(@disabled)
|
|
34
34
|
end
|
|
35
35
|
|
|
36
36
|
def load_mapper(input)
|
|
37
|
-
|
|
37
|
+
super
|
|
38
38
|
@disabled = input.boolean?
|
|
39
39
|
end
|
|
40
40
|
|
|
@@ -21,15 +21,17 @@ module Badline
|
|
|
21
21
|
private
|
|
22
22
|
|
|
23
23
|
def save_mapper(out)
|
|
24
|
-
|
|
24
|
+
super
|
|
25
25
|
out.boolean(@locked)
|
|
26
26
|
end
|
|
27
27
|
|
|
28
28
|
def load_mapper(input)
|
|
29
|
-
|
|
29
|
+
super
|
|
30
30
|
@locked = input.boolean?
|
|
31
31
|
end
|
|
32
32
|
|
|
33
|
+
def windows = [*@roml_banks, *@romh_banks, EMPTY_BANK]
|
|
34
|
+
|
|
33
35
|
def select(value)
|
|
34
36
|
@roml = bank(@roml_banks, value & 0x03)
|
|
35
37
|
@romh = bank(@romh_banks, value & 0x03)
|
|
@@ -9,16 +9,12 @@ module Badline
|
|
|
9
9
|
end
|
|
10
10
|
|
|
11
11
|
def peek(addr)
|
|
12
|
-
if addr >= 0xdf00
|
|
13
|
-
self.mode = :rom8k
|
|
14
|
-
changed!
|
|
15
|
-
end
|
|
12
|
+
select_mode(:rom8k) if addr >= 0xdf00
|
|
16
13
|
open_bus(addr)
|
|
17
14
|
end
|
|
18
15
|
|
|
19
16
|
def reset
|
|
20
|
-
|
|
21
|
-
changed!
|
|
17
|
+
select_mode(:rom16k)
|
|
22
18
|
end
|
|
23
19
|
|
|
24
20
|
private
|
data/lib/badline/cartridge.rb
CHANGED
|
@@ -63,11 +63,7 @@ module Badline
|
|
|
63
63
|
end
|
|
64
64
|
|
|
65
65
|
def from_crt(crt, **)
|
|
66
|
-
|
|
67
|
-
raise UnsupportedTypeError,
|
|
68
|
-
"Unsupported cartridge hardware type #{crt.hardware_type}"
|
|
69
|
-
end
|
|
70
|
-
mapper.new(crt, **)
|
|
66
|
+
mapper_for(crt).new(crt, **)
|
|
71
67
|
end
|
|
72
68
|
|
|
73
69
|
# A cartridge built afresh from what save_setup wrote, before
|
|
@@ -76,9 +72,21 @@ module Badline
|
|
|
76
72
|
return GeoRAM.new(size: input.int) if input.int == GEO_RAM_SETUP
|
|
77
73
|
|
|
78
74
|
crt = Storage::CRTFile.new(bytes: input.string)
|
|
79
|
-
|
|
75
|
+
mapper_for(crt).from_jumpers(crt, input)
|
|
76
|
+
end
|
|
80
77
|
|
|
81
|
-
|
|
78
|
+
# A cartridge from its image and what save_jumpers wrote: whether
|
|
79
|
+
# there are jumpers, then their settings on a mapper that has them.
|
|
80
|
+
def from_jumpers(crt, input)
|
|
81
|
+
input.boolean?
|
|
82
|
+
new(crt)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def mapper_for(crt)
|
|
86
|
+
HARDWARE_TYPES.fetch(crt.hardware_type) do
|
|
87
|
+
raise UnsupportedTypeError,
|
|
88
|
+
"Unsupported cartridge hardware type #{crt.hardware_type}"
|
|
89
|
+
end
|
|
82
90
|
end
|
|
83
91
|
end
|
|
84
92
|
|
|
@@ -95,6 +103,7 @@ module Badline
|
|
|
95
103
|
@exrom = crt.exrom
|
|
96
104
|
@game = crt.game
|
|
97
105
|
@roml = @romh = nil
|
|
106
|
+
@banks = nil
|
|
98
107
|
@on_change = nil
|
|
99
108
|
@on_nmi_change = nil
|
|
100
109
|
@nmi = false
|
|
@@ -193,9 +202,22 @@ module Badline
|
|
|
193
202
|
out.boolean(false)
|
|
194
203
|
end
|
|
195
204
|
|
|
196
|
-
|
|
205
|
+
# The mapper's own state: where its windows point, for a mapper that
|
|
206
|
+
# moves them, and whatever a mapper adds after calling super.
|
|
207
|
+
def save_mapper(out)
|
|
208
|
+
save_windows(out, windows) if windows
|
|
209
|
+
end
|
|
197
210
|
|
|
198
|
-
def load_mapper(
|
|
211
|
+
def load_mapper(input)
|
|
212
|
+
load_windows(input, windows) if windows
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
# The banks and views ROML and ROMH can point at, which a snapshot
|
|
216
|
+
# names them by: the mapper's banks, or nothing for a mapper whose
|
|
217
|
+
# windows stay where install_chips put them.
|
|
218
|
+
def windows
|
|
219
|
+
[*@banks, EMPTY_BANK] if @banks
|
|
220
|
+
end
|
|
199
221
|
|
|
200
222
|
# Where the ROML and ROMH windows point, as their places in `windows`,
|
|
201
223
|
# the banks and views the mapper picks them from, or -1 for none.
|
|
@@ -232,6 +254,12 @@ module Badline
|
|
|
232
254
|
@exrom, @game = MODES.fetch(mode)
|
|
233
255
|
end
|
|
234
256
|
|
|
257
|
+
# Sets the lines and tells the machine to map the cartridge again.
|
|
258
|
+
def select_mode(mode)
|
|
259
|
+
self.mode = mode
|
|
260
|
+
changed!
|
|
261
|
+
end
|
|
262
|
+
|
|
235
263
|
def open_bus(addr)
|
|
236
264
|
@open_bus ? @open_bus.peek(addr) : 0xff
|
|
237
265
|
end
|
|
@@ -4,6 +4,9 @@ module Badline
|
|
|
4
4
|
class CIA
|
|
5
5
|
# Running a CIA a stretch of cycles at once, for Drive::Idle.
|
|
6
6
|
module FastForward
|
|
7
|
+
# quiet_cycles for a CIA whose counters will never set a flag.
|
|
8
|
+
QUIET = 1 << 40
|
|
9
|
+
|
|
7
10
|
# Whether fast_forward can run the CIA: timer B stopped with nothing
|
|
8
11
|
# in its pipeline, no interrupt or acknowledge on its way, CNT where it
|
|
9
12
|
# was, the TOD clock stopped, and timer A either stopped, or free
|
|
@@ -16,11 +19,24 @@ module Badline
|
|
|
16
19
|
@ta.idle? ? @serial.idle : timer_a_free_running?
|
|
17
20
|
end
|
|
18
21
|
|
|
19
|
-
#
|
|
20
|
-
#
|
|
21
|
-
#
|
|
22
|
+
# How many cycles the CIA can run with nothing but its counters
|
|
23
|
+
# moving: as quiet? allows, but with timer B free running too, up to
|
|
24
|
+
# the cycle before it underflows. The 1581's DOS leaves timer B
|
|
25
|
+
# interrupting every 10 ms.
|
|
26
|
+
def quiet_cycles
|
|
27
|
+
return 0 unless @icr.quiet && @tod.quiet? && cnt_steady? && timer_a_quiet?
|
|
28
|
+
return QUIET if @tb.idle?
|
|
29
|
+
return 0 unless @tb.free_running? && @control_b.value.nobits?(0x60)
|
|
30
|
+
|
|
31
|
+
@tb.counter - 1
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Runs +cycles+ cycles at once while quiet? or for quiet_cycles, as
|
|
35
|
+
# cycle! would: timer A's underflows flag in the ICR, and reach the
|
|
36
|
+
# serial port's underflow line.
|
|
22
37
|
def fast_forward(cycles)
|
|
23
38
|
@tod.fast_forward(cycles)
|
|
39
|
+
@tb.fast_forward(cycles) unless @tb.idle?
|
|
24
40
|
return if @ta.idle?
|
|
25
41
|
|
|
26
42
|
@icr.flag(:timer_a) if @ta.fast_forward(cycles).positive?
|
|
@@ -29,6 +45,8 @@ module Badline
|
|
|
29
45
|
|
|
30
46
|
private
|
|
31
47
|
|
|
48
|
+
def timer_a_quiet? = @ta.idle? ? @serial.idle : timer_a_free_running?
|
|
49
|
+
|
|
32
50
|
# Timer A free running with its interrupt masked, and the serial port
|
|
33
51
|
# an idle input it only clocks.
|
|
34
52
|
def timer_a_free_running?
|