badline 0.11.1 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +18 -0
- data/CLAUDE.md +8 -7
- data/CONTRIBUTING.md +7 -6
- data/README.md +12 -5
- data/doc/media.md +28 -1
- data/doc/pinned-behaviour.md +31 -1
- data/exe/badline-ruby +1 -1
- data/lib/badline/c128/bus.rb +4 -17
- data/lib/badline/c128/drives.rb +35 -22
- data/lib/badline/c128/fast_serial.rb +1 -0
- data/lib/badline/c128/io_pages.rb +35 -0
- data/lib/badline/c128/mmu.rb +9 -7
- data/lib/badline/c128/modes.rb +17 -4
- data/lib/badline/c128/saved_state.rb +22 -0
- data/lib/badline/c128/z80.rb +49 -0
- data/lib/badline/c128/z80_pages.rb +118 -0
- data/lib/badline/c128/z80_turns.rb +106 -0
- data/lib/badline/c128.rb +16 -19
- data/lib/badline/cartridge/action_replay.rb +2 -2
- data/lib/badline/cartridge/comal80.rb +1 -7
- data/lib/badline/cartridge/dinamic.rb +0 -8
- data/lib/badline/cartridge/final_cartridge3.rb +6 -6
- data/lib/badline/cartridge/fun_play.rb +0 -8
- data/lib/badline/cartridge/game_system.rb +0 -8
- data/lib/badline/cartridge/kcs_power.rb +10 -7
- data/lib/badline/cartridge/mach5.rb +2 -4
- data/lib/badline/cartridge/magic_desk.rb +0 -8
- data/lib/badline/cartridge/ocean.rb +0 -8
- data/lib/badline/cartridge/pagefox.rb +2 -2
- data/lib/badline/cartridge/retro_replay.rb +7 -6
- data/lib/badline/cartridge/rex_utility.rb +2 -6
- data/lib/badline/cartridge/rgcd.rb +2 -2
- data/lib/badline/cartridge/simons_basic.rb +0 -5
- data/lib/badline/cartridge/super_games.rb +4 -2
- data/lib/badline/cartridge/westermann.rb +2 -6
- data/lib/badline/cartridge.rb +37 -9
- data/lib/badline/cia/fast_forward.rb +21 -3
- data/lib/badline/cia.rb +8 -5
- data/lib/badline/computer/attachments.rb +4 -56
- data/lib/badline/computer/true_drives.rb +80 -0
- data/lib/badline/computer.rb +4 -0
- data/lib/badline/core.rb +2 -0
- data/lib/badline/drive/core.rb +8 -33
- data/lib/badline/drive/fast_serial.rb +73 -0
- data/lib/badline/drive/idle.rb +1 -11
- data/lib/badline/drive/orbit.rb +2 -5
- data/lib/badline/drive/sleep.rb +0 -2
- data/lib/badline/drive/vias.rb +84 -0
- data/lib/badline/drive.rb +2 -0
- data/lib/badline/drive1541.rb +1 -0
- data/lib/badline/drive1571/wd1770.rb +94 -8
- data/lib/badline/drive1571.rb +8 -58
- data/lib/badline/drive1581/bus.rb +137 -0
- data/lib/badline/drive1581/cpu.rb +11 -0
- data/lib/badline/drive1581/disk.rb +135 -0
- data/lib/badline/drive1581/disk_state.rb +87 -0
- data/lib/badline/drive1581/mechanism.rb +117 -0
- data/lib/badline/drive1581/serial_port.rb +52 -0
- data/lib/badline/drive1581/slot.rb +78 -0
- data/lib/badline/drive1581/track.rb +100 -0
- data/lib/badline/drive1581/track_bytes.rb +75 -0
- data/lib/badline/drive1581/wd1772/registers.rb +89 -0
- data/lib/badline/drive1581/wd1772/sector_writes.rb +73 -0
- data/lib/badline/drive1581/wd1772/sectors.rb +150 -0
- data/lib/badline/drive1581/wd1772/state.rb +66 -0
- data/lib/badline/drive1581/wd1772/stepping.rb +109 -0
- data/lib/badline/drive1581/wd1772/tracks.rb +125 -0
- data/lib/badline/drive1581/wd1772/turn.rb +73 -0
- data/lib/badline/drive1581/wd1772.rb +216 -0
- data/lib/badline/drive1581.rb +233 -0
- data/lib/badline/frontend/app.rb +1 -3
- data/lib/badline/frontend/audio_device.rb +67 -0
- data/lib/badline/frontend/audio_sink.rb +16 -50
- data/lib/badline/frontend/boot.rb +18 -10
- data/lib/badline/frontend/buttons.rb +7 -7
- data/lib/badline/frontend/keys.rb +33 -8
- data/lib/badline/frontend/media_slots.rb +42 -0
- data/lib/badline/frontend/{confirmation.rb → menu/confirmation.rb} +4 -4
- data/lib/badline/frontend/{file_browser.rb → menu/file_browser.rb} +18 -34
- data/lib/badline/frontend/{menu_dialogs.rb → menu/menu_dialogs.rb} +3 -7
- data/lib/badline/frontend/{menu_media.rb → menu/menu_media.rb} +26 -51
- data/lib/badline/frontend/{menu_pages.rb → menu/menu_pages.rb} +5 -8
- data/lib/badline/frontend/{name_field.rb → menu/name_field.rb} +11 -24
- data/lib/badline/frontend/{pause_menu.rb → menu/pause_menu.rb} +13 -28
- data/lib/badline/frontend/menu/theme.rb +18 -0
- data/lib/badline/frontend/{filter_view.rb → player/filter_view.rb} +4 -4
- data/lib/badline/frontend/{info_view.rb → player/info_view.rb} +3 -3
- data/lib/badline/frontend/{player_window.rb → player/player_controller.rb} +17 -25
- data/lib/badline/frontend/{player_footer.rb → player/player_footer.rb} +8 -8
- data/lib/badline/frontend/{player_header.rb → player/player_header.rb} +5 -5
- data/lib/badline/frontend/{player_screen.rb → player/player_screen.rb} +4 -5
- data/lib/badline/frontend/{scope.rb → player/scope.rb} +1 -1
- data/lib/badline/frontend/{sid_view.rb → player/sid_view.rb} +16 -20
- data/lib/badline/frontend/player/theme.rb +18 -0
- data/lib/badline/frontend/{visualizer_view.rb → player/visualizer_view.rb} +8 -8
- data/lib/badline/frontend/sound.rb +19 -38
- data/lib/badline/frontend/timeline.rb +3 -14
- data/lib/badline/frontend.rb +24 -20
- data/lib/badline/iec_bus.rb +14 -2
- data/lib/badline/machine.rb +5 -4
- data/lib/badline/media/boot_disk.rb +50 -0
- data/lib/badline/media/not_disk.rb +8 -0
- data/lib/badline/media/true_drive.rb +35 -15
- data/lib/badline/media.rb +9 -3
- data/lib/badline/options/event.rb +2 -2
- data/lib/badline/options/family.rb +3 -9
- data/lib/badline/options/help.rb +2 -1
- data/lib/badline/roms/README +9 -0
- data/lib/badline/roms/dos1581.rom +0 -0
- data/lib/badline/sdl.rb +37 -20
- data/lib/badline/snapshot/state.rb +5 -4
- data/lib/badline/storage/p00.rb +8 -0
- data/lib/badline/time_of_day.rb +3 -3
- data/lib/badline/version.rb +1 -1
- data/lib/badline/vic20/attachments.rb +21 -7
- data/lib/badline/vic20/saved_state.rb +2 -0
- data/lib/badline/vic20.rb +3 -0
- data/lib/badline/z80/core.rb +2 -0
- data/lib/badline/z80/saved_state.rb +69 -0
- metadata +54 -21
- /data/lib/badline/frontend/{menu_ports.rb → menu/menu_ports.rb} +0 -0
- /data/lib/badline/frontend/{player_commands.rb → player/player_commands.rb} +0 -0
- /data/lib/badline/frontend/{player_state.rb → player/player_state.rb} +0 -0
- /data/lib/badline/frontend/{sid_history.rb → player/sid_history.rb} +0 -0
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
class C128
|
|
5
|
+
class Bus
|
|
6
|
+
# What the Z80 sees, in memory and through IN and OUT, laid over the
|
|
7
|
+
# 8502's map whenever the MMU, the port or a cartridge changes it
|
|
8
|
+
# while the Z80 has the bus.
|
|
9
|
+
#
|
|
10
|
+
# In C128 mode, with CR's RAM bank 0, the MMU translates $0000-$0FFF
|
|
11
|
+
# to $D000-$DFFF of bank 0: a read gets the Z80 BIOS, which the
|
|
12
|
+
# KERNAL ROM holds there, and a write or an IN or OUT the RAM at
|
|
13
|
+
# $Dxxx. With bank 1 those pages are RAM, common RAM's or bank 1's.
|
|
14
|
+
# $D000-$DFFF is RAM. In C64 mode the PLA decodes the Z80's memory
|
|
15
|
+
# accesses as it does the 8502's.
|
|
16
|
+
#
|
|
17
|
+
# In both modes the chips at $D000-$DFFF answer IN and OUT whether
|
|
18
|
+
# I/O is mapped in or not. The MMU's registers at $D500 take an OUT
|
|
19
|
+
# either way in C128 mode, but answer an IN only with CR's I/O bit
|
|
20
|
+
# on, memory answering it otherwise. While I/O is mapped in, the
|
|
21
|
+
# colour RAM the 8502 sees also answers at $1000-$13FF. An IN or OUT
|
|
22
|
+
# anywhere else reaches what memory has there.
|
|
23
|
+
module Z80Pages
|
|
24
|
+
# A colour RAM bank seen at $1000-$13FF.
|
|
25
|
+
class ColorWindow
|
|
26
|
+
def initialize(color_ram)
|
|
27
|
+
@color_ram = color_ram
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def peek(addr) = @color_ram.peek(0xd800 | (addr & 0x3ff))
|
|
31
|
+
def poke(addr, value) = @color_ram.poke(0xd800 | (addr & 0x3ff), value)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# The Z80's memory and I/O pages, each 256 entries.
|
|
35
|
+
attr_reader :z80_read_pages, :z80_write_pages, :z80_input_pages, :z80_output_pages
|
|
36
|
+
|
|
37
|
+
# Calls the block whenever MCR bit 0 hands the bus to the other CPU.
|
|
38
|
+
def on_processor_change(&block)
|
|
39
|
+
@on_processor_change = block
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Whether the Z80 has the bus, MCR bit 0 low.
|
|
43
|
+
def z80? = @z80
|
|
44
|
+
|
|
45
|
+
private
|
|
46
|
+
|
|
47
|
+
# Lays out the Z80's pages again while it has the bus, and tells the
|
|
48
|
+
# machine if MCR bit 0 handed the bus over.
|
|
49
|
+
def update_z80!
|
|
50
|
+
z80 = @mmu.z80?
|
|
51
|
+
map_z80_pages if z80
|
|
52
|
+
return if z80 == @z80
|
|
53
|
+
|
|
54
|
+
@z80 = z80
|
|
55
|
+
@on_processor_change&.call
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def build_z80_pages
|
|
59
|
+
@z80_bios = ROM.new(ROM.read("c128/kernal.rom")[0x1000, 0x1000], length: 0x1000, start: 0)
|
|
60
|
+
@z80_read_pages = Array.new(256)
|
|
61
|
+
@z80_write_pages = Array.new(256)
|
|
62
|
+
@z80_input_pages = Array.new(256)
|
|
63
|
+
@z80_output_pages = Array.new(256)
|
|
64
|
+
@z80_color_ram = [ColorWindow.new(@color_lines.color_ram(0)), ColorWindow.new(@color_lines.color_ram(1))]
|
|
65
|
+
@z80_translated = RAMWindow.new(@ram, 0xd000, 0xfff)
|
|
66
|
+
@z80 = @mmu.z80?
|
|
67
|
+
@on_processor_change = nil
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def map_z80_pages
|
|
71
|
+
c128_mode = !@mmu.c64_mode?
|
|
72
|
+
bios = c128_mode && @mmu.cr < 0x40
|
|
73
|
+
@z80_read_pages.replace(@read_pages)
|
|
74
|
+
@z80_write_pages.replace(@write_pages)
|
|
75
|
+
map_z80_ram_pages if c128_mode
|
|
76
|
+
map_z80_bios if bios
|
|
77
|
+
map_z80_color_ram(c128_mode) if @io_mapped
|
|
78
|
+
@z80_input_pages.replace(@z80_read_pages)
|
|
79
|
+
@z80_output_pages.replace(@z80_write_pages)
|
|
80
|
+
@z80_input_pages.fill(@z80_translated, 0, 16) if bios
|
|
81
|
+
@io_mapped ? map_z80_io_from_cpu : map_z80_io
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def map_z80_ram_pages
|
|
85
|
+
(0xd0..0xdf).each do |page|
|
|
86
|
+
ram = bank_of(page, @mmu.cpu_bank).zero? ? @ram : @bank1
|
|
87
|
+
@z80_read_pages[page] = @z80_write_pages[page] = ram
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def map_z80_bios
|
|
92
|
+
@z80_read_pages.fill(@z80_bios, 0, 16)
|
|
93
|
+
@z80_write_pages.fill(@z80_translated, 0, 16)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def map_z80_color_ram(c128_mode)
|
|
97
|
+
window = @z80_color_ram[c128_mode ? @io_port.value & 0x01 : 1]
|
|
98
|
+
@z80_read_pages.fill(window, 0x10, 4)
|
|
99
|
+
@z80_write_pages.fill(window, 0x10, 4)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def map_z80_io_from_cpu
|
|
103
|
+
(0xd0..0xdf).each do |page|
|
|
104
|
+
@z80_input_pages[page] = @read_pages[page]
|
|
105
|
+
@z80_output_pages[page] = @write_pages[page]
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# The chips at $D000-$DFFF while I/O is mapped out, leaving an IN
|
|
110
|
+
# at $D500 to memory.
|
|
111
|
+
def map_z80_io
|
|
112
|
+
lay_out_io(@z80_input_pages, @z80_output_pages)
|
|
113
|
+
@z80_output_pages[0xd5] = @mmu unless @mmu.c64_mode?
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
class C128
|
|
5
|
+
# The Z80's turns on the bus. MCR bit 0 hands the bus between the 8502
|
|
6
|
+
# and the Z80, and while the Z80 has it each cycle clocks the chips,
|
|
7
|
+
# then gives the Z80 2 T-states unless BA holds it. The Z80 runs whole
|
|
8
|
+
# instructions while it is behind, so an IN or OUT first brings the
|
|
9
|
+
# chips up to the cycle it falls in (#z80_catch_up), and the cycles
|
|
10
|
+
# that follow clock nothing until the machine catches up with them.
|
|
11
|
+
module Z80Turns
|
|
12
|
+
# The T-states from the start of an I/O machine cycle to the one its
|
|
13
|
+
# data moves in. 1 and 2 pass c128modez80-23, whose IN reads open
|
|
14
|
+
# bus, and 0, 3 and 4 fail it.
|
|
15
|
+
IO_DELAY = 2
|
|
16
|
+
|
|
17
|
+
attr_reader :z80
|
|
18
|
+
|
|
19
|
+
# Brings the chips up to the cycle an IN or OUT of the Z80 falls in.
|
|
20
|
+
def z80_catch_up
|
|
21
|
+
while @z80.cycles + IO_DELAY >= @z80_due
|
|
22
|
+
clock_chips
|
|
23
|
+
@chips_ahead += 1
|
|
24
|
+
@z80_due += 2
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
def build_z80
|
|
31
|
+
z80_bus = Z80Bus.new(@bus)
|
|
32
|
+
@z80 = Z80.new(z80_bus)
|
|
33
|
+
z80_bus.machine = self
|
|
34
|
+
@z80_running = @bus.z80?
|
|
35
|
+
@z80_turn = @z80_running
|
|
36
|
+
@z80_due = 0
|
|
37
|
+
@chips_ahead = 0
|
|
38
|
+
@cpu_reset_pending = @z80_running
|
|
39
|
+
@bus.on_processor_change { processor_changed }
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# A cycle while the Z80 has the bus, or after it handed the bus to the
|
|
43
|
+
# 8502 while the chips were ahead.
|
|
44
|
+
def z80_cycle!
|
|
45
|
+
if @chips_ahead.positive?
|
|
46
|
+
@chips_ahead -= 1
|
|
47
|
+
@z80_turn = @z80_running || @chips_ahead.positive?
|
|
48
|
+
return
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
clock_chips
|
|
52
|
+
@z80_due += 2 unless @vic.ba_low?
|
|
53
|
+
z80 = @z80
|
|
54
|
+
z80.step! while @z80_running && z80.cycles < @z80_due
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# The chips' share of a cycle while the Z80 has the bus, with the
|
|
58
|
+
# interrupt lines going to the Z80.
|
|
59
|
+
def clock_chips
|
|
60
|
+
handle_init if @cycles == @init_threshold
|
|
61
|
+
feed_keyboard if @pending_keys
|
|
62
|
+
|
|
63
|
+
clock_bits = @clock_bits
|
|
64
|
+
@clock_bits = @vic.clock_bits
|
|
65
|
+
@vic.test_step! if clock_bits >= 0x02
|
|
66
|
+
|
|
67
|
+
@vic.cycle!
|
|
68
|
+
@cia1.cycle!
|
|
69
|
+
@cia2.cycle!
|
|
70
|
+
@sid.cycle!
|
|
71
|
+
@datasette.cycle!
|
|
72
|
+
@vdc.cycle!
|
|
73
|
+
|
|
74
|
+
@z80.int = @cia1.interrupted? || @vic.interrupted?
|
|
75
|
+
nmi = @cia2.interrupted? || @cartridge_nmi || @restore_pulse
|
|
76
|
+
@restore_pulse = false
|
|
77
|
+
@nmi_asserted = nmi
|
|
78
|
+
@z80.nmi = nmi
|
|
79
|
+
clock_serial_bus
|
|
80
|
+
|
|
81
|
+
@cycles += 1
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# MCR bit 0 handed the bus to the other CPU. The 8502 runs its reset
|
|
85
|
+
# vector the first time it gets the bus after a reset.
|
|
86
|
+
def processor_changed
|
|
87
|
+
@z80_running = @bus.z80?
|
|
88
|
+
@z80_turn = @z80_running || @chips_ahead.positive?
|
|
89
|
+
@z80_due = @z80.cycles
|
|
90
|
+
return if @z80_running || !@cpu_reset_pending
|
|
91
|
+
|
|
92
|
+
@cpu_reset_pending = false
|
|
93
|
+
@cpu.reset!
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# The RES line reaches the Z80, and the 8502 waits for the bus to run
|
|
97
|
+
# its reset vector, unless the machine resets into C64 mode.
|
|
98
|
+
def reset_z80
|
|
99
|
+
@chips_ahead = 0
|
|
100
|
+
@z80_turn = @z80_running
|
|
101
|
+
@z80.reset!
|
|
102
|
+
@c64_built ? @cpu.reset! : @cpu_reset_pending = true
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
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
|
|
@@ -45,6 +50,7 @@ module Badline
|
|
|
45
50
|
include Modes
|
|
46
51
|
include Drives
|
|
47
52
|
include FastSerial
|
|
53
|
+
include Z80Turns
|
|
48
54
|
|
|
49
55
|
# The C64's 8x8 matrix and the three rows K0-K2 select, in port B
|
|
50
56
|
# column order.
|
|
@@ -116,6 +122,7 @@ module Badline
|
|
|
116
122
|
@holding_commodore = false
|
|
117
123
|
@bus = Bus.new(@model, sid_model: sid_model || @model.sid_model, mode:)
|
|
118
124
|
@cpu = CPU.new(@bus, debug:)
|
|
125
|
+
build_z80
|
|
119
126
|
@vic = @bus.vic
|
|
120
127
|
@vic.open_bus = -> { @bus.ram.peek(@cpu.program_counter) }
|
|
121
128
|
@cia1 = @bus.cia1
|
|
@@ -141,6 +148,8 @@ module Badline
|
|
|
141
148
|
# mode the CPU runs in both halves of the cycle (#clock_fast), and the
|
|
142
149
|
# TEST bit steps the raster counter in every cycle.
|
|
143
150
|
def cycle!
|
|
151
|
+
return z80_cycle! if @z80_turn
|
|
152
|
+
|
|
144
153
|
handle_init if @cycles == @init_threshold
|
|
145
154
|
feed_keyboard if @pending_keys
|
|
146
155
|
|
|
@@ -241,8 +250,9 @@ module Badline
|
|
|
241
250
|
@drive&.reset!
|
|
242
251
|
@drive1541&.reset!
|
|
243
252
|
@drive1571&.reset!
|
|
253
|
+
@drive1581&.reset!
|
|
244
254
|
@nmi_asserted = false
|
|
245
|
-
|
|
255
|
+
reset_z80
|
|
246
256
|
end
|
|
247
257
|
|
|
248
258
|
# CHROUT is at $FFD2 in both KERNALs, and the trap follows the mode.
|
|
@@ -305,18 +315,5 @@ module Badline
|
|
|
305
315
|
def handle_init
|
|
306
316
|
@init_handlers.each(&:call)
|
|
307
317
|
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
318
|
end
|
|
322
319
|
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
|