badline 0.10.1 → 0.11.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 +15 -0
- data/CLAUDE.md +5 -3
- data/README.md +59 -4
- data/doc/pinned-behaviour.md +75 -0
- data/doc/snapshots.md +15 -0
- data/lib/badline/address_bus/pla.rb +3 -16
- data/lib/badline/address_bus.rb +14 -0
- data/lib/badline/c128/banks.rb +35 -0
- data/lib/badline/c128/bus.rb +347 -0
- data/lib/badline/c128/bus_state.rb +53 -0
- data/lib/badline/c128/color_lines.rb +39 -0
- data/lib/badline/c128/cpu.rb +10 -0
- data/lib/badline/c128/keys.rb +35 -0
- data/lib/badline/c128/mmu.rb +182 -0
- data/lib/badline/c128/mmu_pages.rb +105 -0
- data/lib/badline/c128/model.rb +39 -0
- data/lib/badline/c128/modes.rb +48 -0
- data/lib/badline/c128/saved_state.rb +120 -0
- data/lib/badline/c128/vdc/memory.rb +54 -0
- data/lib/badline/c128/vdc/painter.rb +143 -0
- data/lib/badline/c128/vdc/raster.rb +97 -0
- data/lib/badline/c128/vdc/window.rb +215 -0
- data/lib/badline/c128/vdc.rb +268 -0
- data/lib/badline/c128.rb +320 -0
- data/lib/badline/chrout_trap.rb +3 -0
- data/lib/badline/computer/attachments.rb +4 -13
- data/lib/badline/computer/kernal_traps.rb +29 -0
- data/lib/badline/computer.rb +5 -0
- data/lib/badline/frontend/app.rb +34 -21
- data/lib/badline/frontend/boot.rb +31 -1
- data/lib/badline/frontend/controls.rb +11 -0
- data/lib/badline/frontend/keys.rb +28 -1
- data/lib/badline/frontend/machine_window.rb +16 -13
- data/lib/badline/frontend/menu_pages.rb +6 -1
- data/lib/badline/frontend/menu_ports.rb +1 -1
- data/lib/badline/frontend/pause_menu.rb +1 -0
- data/lib/badline/frontend/screen.rb +33 -8
- data/lib/badline/frontend/screens.rb +90 -0
- data/lib/badline/frontend/timeline.rb +15 -4
- data/lib/badline/frontend.rb +1 -0
- data/lib/badline/kernal_trap/file.rb +1 -1
- data/lib/badline/kernal_trap/layout.rb +58 -8
- data/lib/badline/kernal_trap/load.rb +1 -1
- data/lib/badline/kernal_trap/save.rb +1 -1
- data/lib/badline/kernal_trap/serial.rb +5 -0
- data/lib/badline/machine.rb +15 -6
- data/lib/badline/media.rb +8 -6
- data/lib/badline/options/event.rb +5 -1
- data/lib/badline/options/family.rb +52 -0
- data/lib/badline/options/help.rb +5 -1
- data/lib/badline/options/table.rb +7 -1
- data/lib/badline/options/validation.rb +14 -1
- data/lib/badline/options.rb +20 -19
- data/lib/badline/roms/README +27 -0
- data/lib/badline/roms/c128/basic.rom +0 -0
- data/lib/badline/roms/c128/character.rom +0 -0
- data/lib/badline/roms/c128/kernal.rom +0 -0
- data/lib/badline/snapshot/container.rb +2 -0
- data/lib/badline/snapshot/image.rb +14 -0
- data/lib/badline/snapshot.rb +9 -2
- data/lib/badline/version.rb +1 -1
- data/lib/badline/vic/display_state.rb +12 -0
- data/lib/badline/vic/registers.rb +3 -0
- data/lib/badline/vic.rb +108 -7
- data/lib/badline.rb +1 -0
- metadata +24 -1
data/lib/badline/options/help.rb
CHANGED
|
@@ -39,6 +39,7 @@ module Badline
|
|
|
39
39
|
<<~BANNER
|
|
40
40
|
Usage: #{@program} [c64] [options] [media]
|
|
41
41
|
#{@program} vic20 [options] [media]
|
|
42
|
+
#{@program} c128 [options] [media]
|
|
42
43
|
#{@program} --headless [options] tune.sid
|
|
43
44
|
#{@program} [options] tune.sid --audio-out FILE
|
|
44
45
|
#{@program} sid [options] FILE|DIR...
|
|
@@ -46,6 +47,7 @@ module Badline
|
|
|
46
47
|
Commands:
|
|
47
48
|
c64 Run a C64, the one --model names (the default)
|
|
48
49
|
vic20 Run a PAL VIC-20, with the RAM --ram names
|
|
50
|
+
c128 Run a C128 in C128 mode, the one --model names
|
|
49
51
|
sid Play .sid tunes and directories of them
|
|
50
52
|
|
|
51
53
|
Media can be a .prg/.p00 program, a .d64/.d71/.d81 disk image, a
|
|
@@ -89,7 +91,9 @@ module Badline
|
|
|
89
91
|
the frame as a .bmp, with the frame's number in place of %d. menu
|
|
90
92
|
opens the pause menu, and menu=PAGE opens it on snapshots, drive,
|
|
91
93
|
datasette, expansion, ports, sound or power. Its frames still count,
|
|
92
|
-
and resume closes it.
|
|
94
|
+
and resume closes it. display=vdc or display=vic shows the C128's
|
|
95
|
+
80 or 40 column screen, as F8 switches them. reset, freeze and quit
|
|
96
|
+
take no argument.
|
|
93
97
|
BANNER
|
|
94
98
|
end
|
|
95
99
|
|
|
@@ -11,6 +11,9 @@ module Badline
|
|
|
11
11
|
# emulated.
|
|
12
12
|
VIC20_MODELS = %w[pal ntsc].freeze
|
|
13
13
|
|
|
14
|
+
# The C128's models, as C128::Model names them.
|
|
15
|
+
C128_MODELS = %w[c128 c128ntsc c128dcr c128dcrntsc].freeze
|
|
16
|
+
|
|
14
17
|
# The VIC-20 RAM expansions --ram takes, in the order the help lists
|
|
15
18
|
# them.
|
|
16
19
|
RAM_NAMES = %w[unexpanded 3k 8k 16k 24k 32k all].freeze
|
|
@@ -36,9 +39,12 @@ module Badline
|
|
|
36
39
|
section: "Window options", needs: :window),
|
|
37
40
|
Option.new("--reu SIZE", "Plug in an REU of SIZE K: 128, 256, 512 (a 1750) or up to 16384",
|
|
38
41
|
section: "Window options", needs: :window),
|
|
39
|
-
Option.new("--model NAME", "C64 to run: #{MODELS.join(', ')} (default: #{MODELS.first}); VIC-20: pal"
|
|
42
|
+
Option.new("--model NAME", "C64 to run: #{MODELS.join(', ')} (default: #{MODELS.first}); VIC-20: pal; " \
|
|
43
|
+
"C128: #{C128_MODELS.join(', ')}",
|
|
40
44
|
section: "Window options", needs: :window),
|
|
41
45
|
Option.new("--ntsc", "Run an NTSC C64, as --model ntsc does", section: "Window options", needs: :window),
|
|
46
|
+
Option.new("--c64", "Start the C128 in C64 mode, holding C= at power-on",
|
|
47
|
+
section: "Window options", needs: :window),
|
|
42
48
|
Option.new("--ram SIZE", "VIC-20 RAM expansion: #{RAM_NAMES.join(', ')} (default: unexpanded)",
|
|
43
49
|
section: "Window options", needs: :window),
|
|
44
50
|
Option.new("--no-vsync", "Pace frames by the timer or the sound instead of the display",
|
|
@@ -64,6 +64,19 @@ module Badline
|
|
|
64
64
|
elsif !@ram.nil?
|
|
65
65
|
raise Error, "--ram needs vic20"
|
|
66
66
|
end
|
|
67
|
+
if @family == :c128
|
|
68
|
+
validate_c128
|
|
69
|
+
elsif @c64_mode
|
|
70
|
+
raise Error, "--c64 needs c128"
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# The C128 runs in the window, without an REU, and its NTSC models
|
|
75
|
+
# have names of their own.
|
|
76
|
+
def validate_c128
|
|
77
|
+
raise Error, "--ntsc needs the C64; the NTSC C128 is --model c128ntsc" if @models.include?("ntsc")
|
|
78
|
+
raise Error, "--reu needs the C64" unless @reu.nil?
|
|
79
|
+
raise Error, "the C128 needs the window" if headless?
|
|
67
80
|
end
|
|
68
81
|
|
|
69
82
|
# The VIC-20 is a PAL one, in the window, without the C64's SID and
|
|
@@ -87,7 +100,7 @@ module Badline
|
|
|
87
100
|
# --model and --ntsc may be given more than once, but only for one
|
|
88
101
|
# model.
|
|
89
102
|
def validate_models
|
|
90
|
-
names =
|
|
103
|
+
names = family_models
|
|
91
104
|
unknown = @models.find { |name| !names.include?(name) }
|
|
92
105
|
raise Error, "invalid argument: --model #{unknown}" unless unknown.nil?
|
|
93
106
|
raise Error, "conflicting models: #{@models.uniq.join(' and ')}" unless @models.uniq.size <= 1
|
data/lib/badline/options.rb
CHANGED
|
@@ -5,16 +5,17 @@ require "badline/options/event"
|
|
|
5
5
|
require "badline/options/table"
|
|
6
6
|
require "badline/options/help"
|
|
7
7
|
require "badline/options/validation"
|
|
8
|
+
require "badline/options/family"
|
|
8
9
|
|
|
9
10
|
module Badline
|
|
10
11
|
# The command line of both builds, badline-ruby and the native badline.
|
|
11
12
|
# Either opens the window for any media, or with --headless or
|
|
12
13
|
# --audio-out plays or renders a .sid tune without one. `sid`, given
|
|
13
14
|
# first, plays .sid tunes and directories of them in the terminal.
|
|
14
|
-
# `c64` or `
|
|
15
|
-
#
|
|
16
|
-
# native build adds --no-sound and
|
|
17
|
-
# --disable-jit.
|
|
15
|
+
# `c64`, `vic20` or `c128`, given first, names the machine family, and
|
|
16
|
+
# without one the family is the C64, or the C128 for a program that loads
|
|
17
|
+
# where C128 BASIC starts. The native build adds --no-sound and
|
|
18
|
+
# --version, and badline-ruby adds --disable-jit.
|
|
18
19
|
#
|
|
19
20
|
# TABLE lists the options, and both the parser and the help read it. It
|
|
20
21
|
# parses by hand, inside the subset of Ruby Spinel compiles. Values come
|
|
@@ -23,6 +24,7 @@ module Badline
|
|
|
23
24
|
# arguments before paying for the emulator.
|
|
24
25
|
class Options
|
|
25
26
|
include Validation
|
|
27
|
+
include Family
|
|
26
28
|
|
|
27
29
|
class Error < StandardError; end
|
|
28
30
|
|
|
@@ -55,7 +57,8 @@ module Badline
|
|
|
55
57
|
attr_reader :program, :media_path, :tune_paths, :subtune, :sid_model, :reu, :frames, :screenshot, :save_snapshot,
|
|
56
58
|
:audio_out, :seconds, :songlengths, :filter_chunk, :timeline
|
|
57
59
|
|
|
58
|
-
# The machine family to build (Badline::Machine), :c64
|
|
60
|
+
# The machine family to build (Badline::Machine), :c64, :vic20 or
|
|
61
|
+
# :c128.
|
|
59
62
|
attr_reader :family
|
|
60
63
|
|
|
61
64
|
# The VIC-20's RAM expansion --ram names, a key of
|
|
@@ -72,6 +75,8 @@ module Badline
|
|
|
72
75
|
@program = native ? "badline" : "badline-ruby"
|
|
73
76
|
@media_path = nil
|
|
74
77
|
@family = :c64
|
|
78
|
+
@family_named = false
|
|
79
|
+
@c64_mode = false
|
|
75
80
|
@sid_command = false
|
|
76
81
|
@tune_paths = []
|
|
77
82
|
@subtune = nil
|
|
@@ -110,6 +115,7 @@ module Badline
|
|
|
110
115
|
args = argv.dup
|
|
111
116
|
subcommand(args)
|
|
112
117
|
argument(args.shift, args) until args.empty?
|
|
118
|
+
pick_family
|
|
113
119
|
play_lone_tune
|
|
114
120
|
validate unless help? || version?
|
|
115
121
|
self
|
|
@@ -167,9 +173,14 @@ module Badline
|
|
|
167
173
|
|
|
168
174
|
def jit? = @jit
|
|
169
175
|
|
|
170
|
-
# The name of the model to build: a C64 Badline::Model names,
|
|
171
|
-
#
|
|
172
|
-
|
|
176
|
+
# The name of the model to build: a C64 Badline::Model names, a VIC-20
|
|
177
|
+
# of VIC20_MODELS or a C128 of C128_MODELS. --ntsc is short for --model
|
|
178
|
+
# ntsc.
|
|
179
|
+
def model = @models.first || family_models.first
|
|
180
|
+
|
|
181
|
+
# Whether --c64 asks the C128 to start in C64 mode, holding C= at
|
|
182
|
+
# power-on.
|
|
183
|
+
def c64_mode? = @c64_mode
|
|
173
184
|
|
|
174
185
|
def help? = @help
|
|
175
186
|
|
|
@@ -183,17 +194,6 @@ module Badline
|
|
|
183
194
|
[:both, @native ? :native : :ruby].include?(option.build)
|
|
184
195
|
end
|
|
185
196
|
|
|
186
|
-
# Takes `sid`, `c64` or `vic20` when given first.
|
|
187
|
-
def subcommand(args)
|
|
188
|
-
case args.first
|
|
189
|
-
when "sid" then @sid_command = true
|
|
190
|
-
when "c64" then @family = :c64
|
|
191
|
-
when "vic20" then @family = :vic20
|
|
192
|
-
else return
|
|
193
|
-
end
|
|
194
|
-
args.shift
|
|
195
|
-
end
|
|
196
|
-
|
|
197
197
|
# A .sid on its own plays in the SID player, as `sid` would, unless an
|
|
198
198
|
# option of the emulator's window asks for the machine.
|
|
199
199
|
def play_lone_tune
|
|
@@ -273,6 +273,7 @@ module Badline
|
|
|
273
273
|
when "--verbose" then @verbose = true
|
|
274
274
|
when "--true-drive" then @true_drive = true
|
|
275
275
|
when "--ntsc" then @models << "ntsc"
|
|
276
|
+
when "--c64" then @c64_mode = true
|
|
276
277
|
when "--unpaced" then @paced = false
|
|
277
278
|
when "--disable-jit" then @jit = false
|
|
278
279
|
when "--help" then @help = true
|
data/lib/badline/roms/README
CHANGED
|
@@ -87,3 +87,30 @@ vic20/character.rom
|
|
|
87
87
|
-------------------
|
|
88
88
|
The character generator ROM, 901460-03, for $8000-$8FFF.
|
|
89
89
|
SHA-1 4fd85ab6647ee2ac7ba40f729323f2472d35b9b4.
|
|
90
|
+
|
|
91
|
+
Commodore 128 ROMs
|
|
92
|
+
==================
|
|
93
|
+
|
|
94
|
+
Downloaded from
|
|
95
|
+
https://www.zimmers.net/anonftp/pub/cbm/firmware/computers/c128/
|
|
96
|
+
C128::Bus maps them in C128 mode. C64 mode runs basic.rom, kernal.rom
|
|
97
|
+
and character.rom above.
|
|
98
|
+
|
|
99
|
+
c128/basic.rom
|
|
100
|
+
--------------
|
|
101
|
+
BASIC 7.0, 318022-02 (basic.318022-02.bin), 32 KB: the low half for
|
|
102
|
+
$4000-$7FFF followed by the high half for $8000-$BFFF.
|
|
103
|
+
SHA-1 953dcdf5784a6b39ef84dd6fd968c7a03d8d6816.
|
|
104
|
+
|
|
105
|
+
c128/kernal.rom
|
|
106
|
+
---------------
|
|
107
|
+
318020-05 (kernal.318020-05.bin), 16 KB: the screen editor for
|
|
108
|
+
$C000-$CFFF, the Z80 BIOS for $D000-$DFFF and the KERNAL for
|
|
109
|
+
$E000-$FFFF.
|
|
110
|
+
SHA-1 ceb6e1a1bf7e08eb9cbc651afa29e26adccf38ab.
|
|
111
|
+
|
|
112
|
+
c128/character.rom
|
|
113
|
+
------------------
|
|
114
|
+
The character generator ROM, 390059-01 (characters.390059-01.bin),
|
|
115
|
+
8 KB: the C64's set, the same as character.rom, followed by the C128's.
|
|
116
|
+
SHA-1 29ed066d513f2d5c09ff26d9166ba23c2afb2b3f.
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -29,6 +29,7 @@ module Badline
|
|
|
29
29
|
# warns it without a block.
|
|
30
30
|
def load(&)
|
|
31
31
|
return load_vic20(&) if vic20?
|
|
32
|
+
return load_c128(&) if c128?
|
|
32
33
|
|
|
33
34
|
computer = badline? ? opening { Computer.restored(state) } : Vice.setup(@container).build
|
|
34
35
|
tell(badline? ? state_report(computer) : Vice::Restore.apply(@container, computer), &)
|
|
@@ -38,6 +39,9 @@ module Badline
|
|
|
38
39
|
# Whether the machine is a VIC-20, as the container names it.
|
|
39
40
|
def vic20? = @container.machine == Container::VIC20
|
|
40
41
|
|
|
42
|
+
# Whether the machine is a C128, as the container names it.
|
|
43
|
+
def c128? = @container.machine == Container::C128
|
|
44
|
+
|
|
41
45
|
# Restores `computer` from the snapshot, and returns the Report. A
|
|
42
46
|
# snapshot that fails leaves the machine as it was.
|
|
43
47
|
def restore(computer, &)
|
|
@@ -60,6 +64,16 @@ module Badline
|
|
|
60
64
|
machine
|
|
61
65
|
end
|
|
62
66
|
|
|
67
|
+
# A C128 restores from its BADLINE module alone. x128's own modules
|
|
68
|
+
# aren't read.
|
|
69
|
+
def load_c128(&)
|
|
70
|
+
raise FormatError, "badline reads only its own C128 snapshots, not x128's" unless badline?
|
|
71
|
+
|
|
72
|
+
machine = C128.restored(state)
|
|
73
|
+
tell(state_report(machine), &)
|
|
74
|
+
machine
|
|
75
|
+
end
|
|
76
|
+
|
|
63
77
|
def tell(report)
|
|
64
78
|
report.ignored.each { |line| block_given? ? yield(line) : warn("badline: #{line}") }
|
|
65
79
|
end
|
data/lib/badline/snapshot.rb
CHANGED
|
@@ -27,11 +27,12 @@ module Badline
|
|
|
27
27
|
path
|
|
28
28
|
end
|
|
29
29
|
|
|
30
|
-
# A VIC-20's snapshot holds the BADLINE module alone, in a
|
|
31
|
-
# named as xvic names its machine.
|
|
30
|
+
# A VIC-20's or a C128's snapshot holds the BADLINE module alone, in a
|
|
31
|
+
# container named as xvic or x128 names its machine.
|
|
32
32
|
def dump(computer)
|
|
33
33
|
state = computer.snapshot
|
|
34
34
|
return Container.new([MachineState.section(state)], machine: Container::VIC20).to_s if computer.family == :vic20
|
|
35
|
+
return Container.new([MachineState.section(state)], machine: Container::C128).to_s if computer.family == :c128
|
|
35
36
|
|
|
36
37
|
Container.new(Vice.export(state) + [MachineState.section(state)]).to_s
|
|
37
38
|
end
|
|
@@ -59,4 +60,10 @@ module Badline
|
|
|
59
60
|
|
|
60
61
|
def restore_snapshot(path, &) = Snapshot.restore(self, path, &)
|
|
61
62
|
end
|
|
63
|
+
|
|
64
|
+
class C128
|
|
65
|
+
def save_snapshot(path) = Snapshot.save(self, path)
|
|
66
|
+
|
|
67
|
+
def restore_snapshot(path, &) = Snapshot.restore(self, path, &)
|
|
68
|
+
end
|
|
62
69
|
end
|
data/lib/badline/version.rb
CHANGED
|
@@ -68,6 +68,12 @@ module Badline
|
|
|
68
68
|
# True once AEC has followed BA down and the VIC owns the bus.
|
|
69
69
|
def bus_taken?(column) = @ba ? column >= @ba + BA_DELAY : false
|
|
70
70
|
|
|
71
|
+
# The CPU kept the bus through the cycle before +column+, so on a bad
|
|
72
|
+
# line AEC falls BA_DELAY cycles after it at the earliest.
|
|
73
|
+
def keep_bus(column)
|
|
74
|
+
@ba &&= column
|
|
75
|
+
end
|
|
76
|
+
|
|
71
77
|
def fetching?(column)
|
|
72
78
|
@matched && column >= FETCH_FIRST && column <= DMA_LAST
|
|
73
79
|
end
|
|
@@ -98,6 +104,12 @@ module Badline
|
|
|
98
104
|
|
|
99
105
|
def new_line(line)
|
|
100
106
|
@ba = nil
|
|
107
|
+
raster_step(line)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# The raster counter moved on to +line+, at the line's start or, under
|
|
111
|
+
# the VIC-IIe's TEST bit, within it.
|
|
112
|
+
def raster_step(line)
|
|
101
113
|
@line_bits = line & 0b111
|
|
102
114
|
@den_line = line == FIRST_LINE
|
|
103
115
|
@in_window = line.between?(FIRST_LINE, LAST_LINE)
|
|
@@ -150,6 +150,9 @@ module Badline
|
|
|
150
150
|
def fast? = @iie && @bytes[0x30].anybits?(0x01)
|
|
151
151
|
def test? = @iie && @bytes[0x30].anybits?(0x02)
|
|
152
152
|
|
|
153
|
+
# FAST and TEST as bits 0 and 1.
|
|
154
|
+
def clock_bits = @iie ? @bytes[0x30] & 0x03 : 0
|
|
155
|
+
|
|
153
156
|
def raster_target = uint16(@bytes[0x12], (@bytes[0x11] & 0x80) >> 7)
|
|
154
157
|
|
|
155
158
|
def latch_irq!(bit)
|
data/lib/badline/vic.rb
CHANGED
|
@@ -42,6 +42,11 @@ module Badline
|
|
|
42
42
|
# GREEN_PALETTE on a monochrome monitor.
|
|
43
43
|
attr_accessor :palette
|
|
44
44
|
|
|
45
|
+
# The line of #display the beam draws, which is the raster line unless
|
|
46
|
+
# the VIC-IIe's TEST bit has moved the raster counter on since the
|
|
47
|
+
# last vertical sync (#test_step!).
|
|
48
|
+
attr_reader :output_line
|
|
49
|
+
|
|
45
50
|
# The parts a VICE snapshot reads and sets.
|
|
46
51
|
attr_reader :registers, :display_state, :sequencer, :sprites, :character_buffer, :color_buffer,
|
|
47
52
|
:fetch_d011
|
|
@@ -86,6 +91,11 @@ module Badline
|
|
|
86
91
|
HOOK_DISPLAY = 16
|
|
87
92
|
HOOK_LAST = 32
|
|
88
93
|
|
|
94
|
+
# The vertical sync starts this many lines into the vertical blank,
|
|
95
|
+
# and lasts VSYNC_LINES.
|
|
96
|
+
VSYNC_DELAY = 3
|
|
97
|
+
VSYNC_LINES = 3
|
|
98
|
+
|
|
89
99
|
# On the 6569 the DMA compares fall in columns 53 and 54 and the
|
|
90
100
|
# display compare in column 57. A region whose sprite fetches run later
|
|
91
101
|
# moves them with the fetches (Region::Profile#sprite_cycle).
|
|
@@ -125,6 +135,7 @@ module Badline
|
|
|
125
135
|
@height = region.lines_per_frame
|
|
126
136
|
@last_column = @columns_per_line - 1
|
|
127
137
|
@last_line = @height - 1
|
|
138
|
+
@vsync_line = region.vblank[0] + VSYNC_DELAY
|
|
128
139
|
layout_columns
|
|
129
140
|
@blank_columns = Array.new(@columns_per_line) { |column| Region.blanked?(column, region.hblank) }.freeze
|
|
130
141
|
@blank_lines = Array.new(@height) { |line| Region.blanked?(line, region.vblank) }.freeze
|
|
@@ -153,6 +164,7 @@ module Badline
|
|
|
153
164
|
|
|
154
165
|
@column = 0
|
|
155
166
|
@rasterline = 0
|
|
167
|
+
@output_line = 0
|
|
156
168
|
|
|
157
169
|
@character_buffer = Array.new(40, 0)
|
|
158
170
|
@color_buffer = Array.new(40, 0)
|
|
@@ -169,11 +181,13 @@ module Badline
|
|
|
169
181
|
@lp_triggered = false
|
|
170
182
|
@lp_low = false
|
|
171
183
|
@raster_match = false
|
|
184
|
+
@test_wrap = false
|
|
172
185
|
end
|
|
173
186
|
|
|
174
187
|
# Everything the VIC holds between cycles: the beam, the fetch and
|
|
175
188
|
# g-access pipeline, the light pen and raster latches, the finished
|
|
176
|
-
# lines, the
|
|
189
|
+
# lines, on the VIC-IIe the display line and the TEST bit's wrap, the
|
|
190
|
+
# registers and the display, sequencer and sprite logic.
|
|
177
191
|
# Whether it renders is the host's, and every line comes back dirty so
|
|
178
192
|
# a front end repaints.
|
|
179
193
|
def save_state(out)
|
|
@@ -184,6 +198,7 @@ module Badline
|
|
|
184
198
|
out.int(@vic_bank.lines)
|
|
185
199
|
out.blob(@character_buffer).blob(@color_buffer).booleans(@sprite_ba)
|
|
186
200
|
@lines.each { |line| out.blob(line) }
|
|
201
|
+
out.int(@output_line).boolean(@test_wrap) if @iie
|
|
187
202
|
@registers.save_state(out)
|
|
188
203
|
@display_state.save_state(out)
|
|
189
204
|
@sequencer.save_state(out)
|
|
@@ -195,6 +210,7 @@ module Badline
|
|
|
195
210
|
load_beam(input)
|
|
196
211
|
load_buffers(input)
|
|
197
212
|
load_lines(input)
|
|
213
|
+
load_test_state(input) if @iie
|
|
198
214
|
@registers.load_state(input)
|
|
199
215
|
@display_state.load_state(input)
|
|
200
216
|
@sequencer.load_state(input)
|
|
@@ -218,6 +234,7 @@ module Badline
|
|
|
218
234
|
if @column == @columns_per_line
|
|
219
235
|
finish_line!
|
|
220
236
|
@column = 0
|
|
237
|
+
step_output_line
|
|
221
238
|
@rasterline = @rasterline == @last_line ? 0 : @rasterline + 1
|
|
222
239
|
check_raster_irq!(@rasterline.zero? ? @last_line : @rasterline)
|
|
223
240
|
end
|
|
@@ -255,6 +272,57 @@ module Badline
|
|
|
255
272
|
def fast? = @registers.fast?
|
|
256
273
|
def test? = @registers.test?
|
|
257
274
|
|
|
275
|
+
# $D030's FAST and TEST bits as bits 0 and 1, which the C128 reads once
|
|
276
|
+
# a cycle.
|
|
277
|
+
def clock_bits = @registers.clock_bits
|
|
278
|
+
|
|
279
|
+
# Whether the cycle just run was one of the five refresh cycles, Bauer's
|
|
280
|
+
# 11-15, whose phi1 half the VIC keeps in the VIC-IIe's FAST mode.
|
|
281
|
+
# @column has already advanced, so it is the Bauer cycle less one.
|
|
282
|
+
def refresh_cycle? = @column.between?(10, 14)
|
|
283
|
+
|
|
284
|
+
# In the VIC-IIe's FAST mode the 8502 drives the bus in both halves of
|
|
285
|
+
# the cycle, and the VIC's accesses in the cycle just run latched what
|
|
286
|
+
# it put there. The g-access, or the idle access, took the byte of
|
|
287
|
+
# phi1. A c-access took the byte of phi2 as one made before AEC does
|
|
288
|
+
# (#fetch_character_data!): $ff for the video matrix, unless the CPU
|
|
289
|
+
# was reading or writing the VIC's own registers, and the byte's low
|
|
290
|
+
# nibble for colour. AEC can't follow BA down while the CPU runs at
|
|
291
|
+
# 2 MHz, so on a bad line it falls three cycles after FAST mode ends.
|
|
292
|
+
def take_cpu_bus(phi1, phi2, register_access)
|
|
293
|
+
slot = @g_tick
|
|
294
|
+
@g_data[slot] = phi1 unless @g_kind[slot] == G_BLANK
|
|
295
|
+
display_state = @display_state
|
|
296
|
+
display_state.keep_bus(@column)
|
|
297
|
+
return unless display_state.fetching?(@column.zero? ? @last_column : @column - 1)
|
|
298
|
+
|
|
299
|
+
vmli = display_state.vmli
|
|
300
|
+
@character_buffer[vmli] = register_access ? phi2 : 0xff
|
|
301
|
+
@color_buffer[vmli] = phi2 & 0x0f
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
# The VIC-IIe's TEST bit clocks the raster counter in every cycle, and
|
|
305
|
+
# the C128 calls this ahead of each cycle the bit is set in. The line's
|
|
306
|
+
# last cycle, whose own step it falls in with, adds nothing more. The
|
|
307
|
+
# counter takes two steps from the frame's last line to line 0, as line
|
|
308
|
+
# 0 reads the last line for its first cycle.
|
|
309
|
+
def test_step!
|
|
310
|
+
return if @column == @last_column
|
|
311
|
+
|
|
312
|
+
if @rasterline == @last_line
|
|
313
|
+
@test_wrap = !@test_wrap
|
|
314
|
+
return if @test_wrap
|
|
315
|
+
|
|
316
|
+
@rasterline = 0
|
|
317
|
+
@display_state.new_frame
|
|
318
|
+
else
|
|
319
|
+
@test_wrap = false
|
|
320
|
+
@rasterline += 1
|
|
321
|
+
end
|
|
322
|
+
@display_state.raster_step(@rasterline)
|
|
323
|
+
check_raster_irq!
|
|
324
|
+
end
|
|
325
|
+
|
|
258
326
|
def peek(addr)
|
|
259
327
|
i = offset_of(addr) % (2**6)
|
|
260
328
|
case i
|
|
@@ -363,7 +431,7 @@ module Badline
|
|
|
363
431
|
|
|
364
432
|
# Puts the beam at the start of `line`.
|
|
365
433
|
def restore_line(line)
|
|
366
|
-
@rasterline = line
|
|
434
|
+
@rasterline = @output_line = line
|
|
367
435
|
@column = 0
|
|
368
436
|
end
|
|
369
437
|
|
|
@@ -400,7 +468,7 @@ module Badline
|
|
|
400
468
|
|
|
401
469
|
def load_beam(input)
|
|
402
470
|
@column = input.int
|
|
403
|
-
@rasterline = input.int
|
|
471
|
+
@rasterline = @output_line = input.int
|
|
404
472
|
@cycles = input.int
|
|
405
473
|
@pending_write = input.boolean?
|
|
406
474
|
@g_tick = input.int
|
|
@@ -421,6 +489,13 @@ module Badline
|
|
|
421
489
|
input.booleans_into(@sprite_ba)
|
|
422
490
|
end
|
|
423
491
|
|
|
492
|
+
# The VIC-IIe's display line and TEST wrap, which only its TEST bit
|
|
493
|
+
# moves off the raster line.
|
|
494
|
+
def load_test_state(input)
|
|
495
|
+
@output_line = input.int
|
|
496
|
+
@test_wrap = input.boolean?
|
|
497
|
+
end
|
|
498
|
+
|
|
424
499
|
# The display is the finished lines one after another.
|
|
425
500
|
def load_lines(input)
|
|
426
501
|
@lines.each_with_index do |line, number|
|
|
@@ -663,7 +738,7 @@ module Badline
|
|
|
663
738
|
# or not there is a line to draw — then composite the active sprites
|
|
664
739
|
# over the finished background and copy the line into the frame display.
|
|
665
740
|
def finish_line!
|
|
666
|
-
return
|
|
741
|
+
return finish_blank_line if vblank? || !@render
|
|
667
742
|
|
|
668
743
|
composite = @sprites.active?
|
|
669
744
|
@sequencer.apply_color_patches
|
|
@@ -672,12 +747,38 @@ module Badline
|
|
|
672
747
|
@sequencer.apply_border if composite
|
|
673
748
|
|
|
674
749
|
colors = @sequencer.colors
|
|
675
|
-
|
|
750
|
+
output_line = @output_line
|
|
751
|
+
line = @lines[output_line]
|
|
676
752
|
return if line == colors
|
|
677
753
|
|
|
678
754
|
line[0, @width] = colors
|
|
679
|
-
@display[
|
|
680
|
-
@dirty_lines[
|
|
755
|
+
@display[output_line * @width, @width] = colors
|
|
756
|
+
@dirty_lines[output_line] = true
|
|
757
|
+
end
|
|
758
|
+
|
|
759
|
+
# A blanked line paints nothing, and is never shown unless the TEST
|
|
760
|
+
# bit has moved the beam off the raster line, where it shows black.
|
|
761
|
+
def finish_blank_line
|
|
762
|
+
@sprites.finish_line(nil, @sequencer.fg)
|
|
763
|
+
return if @output_line == @rasterline || !@render
|
|
764
|
+
|
|
765
|
+
line = @output_line
|
|
766
|
+
@lines[line].fill(0)
|
|
767
|
+
@display.fill(0, line * @width, @width)
|
|
768
|
+
@dirty_lines[line] = true
|
|
769
|
+
end
|
|
770
|
+
|
|
771
|
+
# The display line the beam draws next follows the one before it, and
|
|
772
|
+
# a line that ends in the vertical sync puts it back on the raster
|
|
773
|
+
# line. Only the VIC-IIe's TEST bit moves the raster counter on between
|
|
774
|
+
# the two, so the lines it skips shift the picture up until the next
|
|
775
|
+
# sync.
|
|
776
|
+
def step_output_line
|
|
777
|
+
@output_line = if @rasterline.between?(@vsync_line, @vsync_line + VSYNC_LINES - 1)
|
|
778
|
+
@rasterline + 1
|
|
779
|
+
else
|
|
780
|
+
@output_line == @last_line ? 0 : @output_line + 1
|
|
781
|
+
end
|
|
681
782
|
end
|
|
682
783
|
|
|
683
784
|
def video_matrix(index)
|
data/lib/badline.rb
CHANGED
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.11.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Inge Jørgensen
|
|
@@ -71,6 +71,23 @@ files:
|
|
|
71
71
|
- lib/badline/audio/stereo.rb
|
|
72
72
|
- lib/badline/audio/terminal.rb
|
|
73
73
|
- lib/badline/audio/wav.rb
|
|
74
|
+
- lib/badline/c128.rb
|
|
75
|
+
- lib/badline/c128/banks.rb
|
|
76
|
+
- lib/badline/c128/bus.rb
|
|
77
|
+
- lib/badline/c128/bus_state.rb
|
|
78
|
+
- lib/badline/c128/color_lines.rb
|
|
79
|
+
- lib/badline/c128/cpu.rb
|
|
80
|
+
- lib/badline/c128/keys.rb
|
|
81
|
+
- lib/badline/c128/mmu.rb
|
|
82
|
+
- lib/badline/c128/mmu_pages.rb
|
|
83
|
+
- lib/badline/c128/model.rb
|
|
84
|
+
- lib/badline/c128/modes.rb
|
|
85
|
+
- lib/badline/c128/saved_state.rb
|
|
86
|
+
- lib/badline/c128/vdc.rb
|
|
87
|
+
- lib/badline/c128/vdc/memory.rb
|
|
88
|
+
- lib/badline/c128/vdc/painter.rb
|
|
89
|
+
- lib/badline/c128/vdc/raster.rb
|
|
90
|
+
- lib/badline/c128/vdc/window.rb
|
|
74
91
|
- lib/badline/cartridge.rb
|
|
75
92
|
- lib/badline/cartridge/action_replay.rb
|
|
76
93
|
- lib/badline/cartridge/atomic_power.rb
|
|
@@ -109,6 +126,7 @@ files:
|
|
|
109
126
|
- lib/badline/color_memory.rb
|
|
110
127
|
- lib/badline/computer.rb
|
|
111
128
|
- lib/badline/computer/attachments.rb
|
|
129
|
+
- lib/badline/computer/kernal_traps.rb
|
|
112
130
|
- lib/badline/computer/saved_state.rb
|
|
113
131
|
- lib/badline/control_ports.rb
|
|
114
132
|
- lib/badline/core.rb
|
|
@@ -174,6 +192,7 @@ files:
|
|
|
174
192
|
- lib/badline/frontend/player_window.rb
|
|
175
193
|
- lib/badline/frontend/scope.rb
|
|
176
194
|
- lib/badline/frontend/screen.rb
|
|
195
|
+
- lib/badline/frontend/screens.rb
|
|
177
196
|
- lib/badline/frontend/screenshot.rb
|
|
178
197
|
- lib/badline/frontend/sid_history.rb
|
|
179
198
|
- lib/badline/frontend/sid_view.rb
|
|
@@ -229,6 +248,7 @@ files:
|
|
|
229
248
|
- lib/badline/model.rb
|
|
230
249
|
- lib/badline/options.rb
|
|
231
250
|
- lib/badline/options/event.rb
|
|
251
|
+
- lib/badline/options/family.rb
|
|
232
252
|
- lib/badline/options/help.rb
|
|
233
253
|
- lib/badline/options/option.rb
|
|
234
254
|
- lib/badline/options/table.rb
|
|
@@ -248,6 +268,9 @@ files:
|
|
|
248
268
|
- lib/badline/rom.rb
|
|
249
269
|
- lib/badline/roms/README
|
|
250
270
|
- lib/badline/roms/basic.rom
|
|
271
|
+
- lib/badline/roms/c128/basic.rom
|
|
272
|
+
- lib/badline/roms/c128/character.rom
|
|
273
|
+
- lib/badline/roms/c128/kernal.rom
|
|
251
274
|
- lib/badline/roms/c64gs.rom
|
|
252
275
|
- lib/badline/roms/character.rom
|
|
253
276
|
- lib/badline/roms/dos1541.rom
|