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.
Files changed (125) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +18 -0
  3. data/CLAUDE.md +8 -7
  4. data/CONTRIBUTING.md +7 -6
  5. data/README.md +12 -5
  6. data/doc/media.md +28 -1
  7. data/doc/pinned-behaviour.md +31 -1
  8. data/exe/badline-ruby +1 -1
  9. data/lib/badline/c128/bus.rb +4 -17
  10. data/lib/badline/c128/drives.rb +35 -22
  11. data/lib/badline/c128/fast_serial.rb +1 -0
  12. data/lib/badline/c128/io_pages.rb +35 -0
  13. data/lib/badline/c128/mmu.rb +9 -7
  14. data/lib/badline/c128/modes.rb +17 -4
  15. data/lib/badline/c128/saved_state.rb +22 -0
  16. data/lib/badline/c128/z80.rb +49 -0
  17. data/lib/badline/c128/z80_pages.rb +118 -0
  18. data/lib/badline/c128/z80_turns.rb +106 -0
  19. data/lib/badline/c128.rb +16 -19
  20. data/lib/badline/cartridge/action_replay.rb +2 -2
  21. data/lib/badline/cartridge/comal80.rb +1 -7
  22. data/lib/badline/cartridge/dinamic.rb +0 -8
  23. data/lib/badline/cartridge/final_cartridge3.rb +6 -6
  24. data/lib/badline/cartridge/fun_play.rb +0 -8
  25. data/lib/badline/cartridge/game_system.rb +0 -8
  26. data/lib/badline/cartridge/kcs_power.rb +10 -7
  27. data/lib/badline/cartridge/mach5.rb +2 -4
  28. data/lib/badline/cartridge/magic_desk.rb +0 -8
  29. data/lib/badline/cartridge/ocean.rb +0 -8
  30. data/lib/badline/cartridge/pagefox.rb +2 -2
  31. data/lib/badline/cartridge/retro_replay.rb +7 -6
  32. data/lib/badline/cartridge/rex_utility.rb +2 -6
  33. data/lib/badline/cartridge/rgcd.rb +2 -2
  34. data/lib/badline/cartridge/simons_basic.rb +0 -5
  35. data/lib/badline/cartridge/super_games.rb +4 -2
  36. data/lib/badline/cartridge/westermann.rb +2 -6
  37. data/lib/badline/cartridge.rb +37 -9
  38. data/lib/badline/cia/fast_forward.rb +21 -3
  39. data/lib/badline/cia.rb +8 -5
  40. data/lib/badline/computer/attachments.rb +4 -56
  41. data/lib/badline/computer/true_drives.rb +80 -0
  42. data/lib/badline/computer.rb +4 -0
  43. data/lib/badline/core.rb +2 -0
  44. data/lib/badline/drive/core.rb +8 -33
  45. data/lib/badline/drive/fast_serial.rb +73 -0
  46. data/lib/badline/drive/idle.rb +1 -11
  47. data/lib/badline/drive/orbit.rb +2 -5
  48. data/lib/badline/drive/sleep.rb +0 -2
  49. data/lib/badline/drive/vias.rb +84 -0
  50. data/lib/badline/drive.rb +2 -0
  51. data/lib/badline/drive1541.rb +1 -0
  52. data/lib/badline/drive1571/wd1770.rb +94 -8
  53. data/lib/badline/drive1571.rb +8 -58
  54. data/lib/badline/drive1581/bus.rb +137 -0
  55. data/lib/badline/drive1581/cpu.rb +11 -0
  56. data/lib/badline/drive1581/disk.rb +135 -0
  57. data/lib/badline/drive1581/disk_state.rb +87 -0
  58. data/lib/badline/drive1581/mechanism.rb +117 -0
  59. data/lib/badline/drive1581/serial_port.rb +52 -0
  60. data/lib/badline/drive1581/slot.rb +78 -0
  61. data/lib/badline/drive1581/track.rb +100 -0
  62. data/lib/badline/drive1581/track_bytes.rb +75 -0
  63. data/lib/badline/drive1581/wd1772/registers.rb +89 -0
  64. data/lib/badline/drive1581/wd1772/sector_writes.rb +73 -0
  65. data/lib/badline/drive1581/wd1772/sectors.rb +150 -0
  66. data/lib/badline/drive1581/wd1772/state.rb +66 -0
  67. data/lib/badline/drive1581/wd1772/stepping.rb +109 -0
  68. data/lib/badline/drive1581/wd1772/tracks.rb +125 -0
  69. data/lib/badline/drive1581/wd1772/turn.rb +73 -0
  70. data/lib/badline/drive1581/wd1772.rb +216 -0
  71. data/lib/badline/drive1581.rb +233 -0
  72. data/lib/badline/frontend/app.rb +1 -3
  73. data/lib/badline/frontend/audio_device.rb +67 -0
  74. data/lib/badline/frontend/audio_sink.rb +16 -50
  75. data/lib/badline/frontend/boot.rb +18 -10
  76. data/lib/badline/frontend/buttons.rb +7 -7
  77. data/lib/badline/frontend/keys.rb +33 -8
  78. data/lib/badline/frontend/media_slots.rb +42 -0
  79. data/lib/badline/frontend/{confirmation.rb → menu/confirmation.rb} +4 -4
  80. data/lib/badline/frontend/{file_browser.rb → menu/file_browser.rb} +18 -34
  81. data/lib/badline/frontend/{menu_dialogs.rb → menu/menu_dialogs.rb} +3 -7
  82. data/lib/badline/frontend/{menu_media.rb → menu/menu_media.rb} +26 -51
  83. data/lib/badline/frontend/{menu_pages.rb → menu/menu_pages.rb} +5 -8
  84. data/lib/badline/frontend/{name_field.rb → menu/name_field.rb} +11 -24
  85. data/lib/badline/frontend/{pause_menu.rb → menu/pause_menu.rb} +13 -28
  86. data/lib/badline/frontend/menu/theme.rb +18 -0
  87. data/lib/badline/frontend/{filter_view.rb → player/filter_view.rb} +4 -4
  88. data/lib/badline/frontend/{info_view.rb → player/info_view.rb} +3 -3
  89. data/lib/badline/frontend/{player_window.rb → player/player_controller.rb} +17 -25
  90. data/lib/badline/frontend/{player_footer.rb → player/player_footer.rb} +8 -8
  91. data/lib/badline/frontend/{player_header.rb → player/player_header.rb} +5 -5
  92. data/lib/badline/frontend/{player_screen.rb → player/player_screen.rb} +4 -5
  93. data/lib/badline/frontend/{scope.rb → player/scope.rb} +1 -1
  94. data/lib/badline/frontend/{sid_view.rb → player/sid_view.rb} +16 -20
  95. data/lib/badline/frontend/player/theme.rb +18 -0
  96. data/lib/badline/frontend/{visualizer_view.rb → player/visualizer_view.rb} +8 -8
  97. data/lib/badline/frontend/sound.rb +19 -38
  98. data/lib/badline/frontend/timeline.rb +3 -14
  99. data/lib/badline/frontend.rb +24 -20
  100. data/lib/badline/iec_bus.rb +14 -2
  101. data/lib/badline/machine.rb +5 -4
  102. data/lib/badline/media/boot_disk.rb +50 -0
  103. data/lib/badline/media/not_disk.rb +8 -0
  104. data/lib/badline/media/true_drive.rb +35 -15
  105. data/lib/badline/media.rb +9 -3
  106. data/lib/badline/options/event.rb +2 -2
  107. data/lib/badline/options/family.rb +3 -9
  108. data/lib/badline/options/help.rb +2 -1
  109. data/lib/badline/roms/README +9 -0
  110. data/lib/badline/roms/dos1581.rom +0 -0
  111. data/lib/badline/sdl.rb +37 -20
  112. data/lib/badline/snapshot/state.rb +5 -4
  113. data/lib/badline/storage/p00.rb +8 -0
  114. data/lib/badline/time_of_day.rb +3 -3
  115. data/lib/badline/version.rb +1 -1
  116. data/lib/badline/vic20/attachments.rb +21 -7
  117. data/lib/badline/vic20/saved_state.rb +2 -0
  118. data/lib/badline/vic20.rb +3 -0
  119. data/lib/badline/z80/core.rb +2 -0
  120. data/lib/badline/z80/saved_state.rb +69 -0
  121. metadata +54 -21
  122. /data/lib/badline/frontend/{menu_ports.rb → menu/menu_ports.rb} +0 -0
  123. /data/lib/badline/frontend/{player_commands.rb → player/player_commands.rb} +0 -0
  124. /data/lib/badline/frontend/{player_state.rb → player/player_state.rb} +0 -0
  125. /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 runs BASIC 7.0 and the C128 KERNAL from reset.
25
- # The C128's Z80 runs first and hands the bus to the 8502, but here the
26
- # 8502 starts at its reset vector, with the MMU as the Z80 leaves it.
27
- # The KERNAL goes to C64 mode as on the C128, with C= held at reset, a
28
- # C64 cartridge or GO64.
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
- @cpu.reset!
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
- save_windows(out, windows)
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
- load_windows(input, windows)
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 save_mapper(out)
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
- self.mode = :rom16k
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
- self.mode = :ultimax
60
- changed!
58
+ select_mode(:ultimax)
61
59
  end
62
60
 
63
61
  private
64
62
 
65
63
  def save_mapper(out)
66
- save_windows(out, [*@roml_banks, *@romh_banks, EMPTY_BANK])
64
+ super
67
65
  out.boolean(@register_visible).boolean(@phi1_ultimax)
68
66
  end
69
67
 
70
68
  def load_mapper(input)
71
- load_windows(input, [*@roml_banks, *@romh_banks, EMPTY_BANK])
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
- switch(addr.anybits?(0x02) ? :off : :rom8k)
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
- switch(addr.anybits?(0x02) ? :ultimax : :rom16k)
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
- switch(:rom16k)
48
+ select_mode(:rom16k)
49
49
  end
50
50
 
51
51
  def freeze!
52
52
  self.nmi = false
53
- switch(:ultimax)
53
+ select_mode(:ultimax)
54
54
  end
55
55
 
56
56
  private
57
57
 
58
- def switch(mode)
59
- self.mode = mode
60
- changed!
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
- self.mode = addr < 0xdf00 ? :rom8k : :off
18
- changed!
17
+ select_mode(addr < 0xdf00 ? :rom8k : :off)
19
18
  end
20
19
 
21
20
  def reset
22
- self.mode = :rom8k
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
- save_windows(out, windows)
33
+ super
34
34
  @ram_banks.each { |ram| out.blob(ram.data) }
35
35
  end
36
36
 
37
37
  def load_mapper(input)
38
- load_windows(input, windows)
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
- self.mode = :rom8k
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
- save_windows(out, [*@banks, EMPTY_BANK])
32
+ super
33
33
  out.boolean(@disabled)
34
34
  end
35
35
 
36
36
  def load_mapper(input)
37
- load_windows(input, [*@banks, EMPTY_BANK])
37
+ super
38
38
  @disabled = input.boolean?
39
39
  end
40
40
 
@@ -24,11 +24,6 @@ module Badline
24
24
 
25
25
  private
26
26
 
27
- def select_mode(mode)
28
- self.mode = mode
29
- changed!
30
- end
31
-
32
27
  def install_chips(chips)
33
28
  roml, romh = banks_from(chips)
34
29
  @roml = bank(roml, 0)
@@ -21,15 +21,17 @@ module Badline
21
21
  private
22
22
 
23
23
  def save_mapper(out)
24
- save_windows(out, [*@roml_banks, *@romh_banks, EMPTY_BANK])
24
+ super
25
25
  out.boolean(@locked)
26
26
  end
27
27
 
28
28
  def load_mapper(input)
29
- load_windows(input, [*@roml_banks, *@romh_banks, EMPTY_BANK])
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
- self.mode = :rom16k
21
- changed!
17
+ select_mode(:rom16k)
22
18
  end
23
19
 
24
20
  private