badline 0.17.0 → 0.19.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 +193 -0
- data/CLAUDE.md +19 -7
- data/CONTRIBUTING.md +11 -1
- data/README.md +178 -26
- data/doc/library.md +5 -1
- data/doc/media.md +3 -2
- data/doc/pinned-behaviour.md +169 -1
- data/doc/snapshot-format.md +302 -35
- data/doc/snapshots.md +97 -26
- data/doc/vice.md +344 -0
- data/exe/badline-ruby +5 -0
- data/lib/badline/audio/renderer.rb +4 -8
- data/lib/badline/audio/terminal.rb +34 -2
- data/lib/badline/c128/keyed_state.rb +2 -0
- data/lib/badline/c128.rb +5 -0
- data/lib/badline/c64/bus/fittings.rb +6 -0
- data/lib/badline/c64/keyed_state.rb +2 -0
- data/lib/badline/c64.rb +7 -0
- data/lib/badline/cbm2/keyed_state.rb +4 -0
- data/lib/badline/cbm2/p500.rb +9 -3
- data/lib/badline/cbm2.rb +10 -3
- data/lib/badline/chips/cia/timer.rb +2 -2
- data/lib/badline/chips/cpu/core.rb +29 -4
- data/lib/badline/chips/cpu/instruction_set.rb +1 -1
- data/lib/badline/chips/cpu/interrupts.rb +9 -0
- data/lib/badline/chips/cpu/keyed_state.rb +15 -9
- data/lib/badline/chips/cpu/microcode.rb +10 -0
- data/lib/badline/chips/m6809/arithmetic.rb +140 -0
- data/lib/badline/chips/m6809/bus_steps.rb +223 -0
- data/lib/badline/chips/m6809/core.rb +149 -0
- data/lib/badline/chips/m6809/indexed.rb +82 -0
- data/lib/badline/chips/m6809/interrupts.rb +159 -0
- data/lib/badline/chips/m6809/keyed_state.rb +147 -0
- data/lib/badline/chips/m6809/opcodes.rb +159 -0
- data/lib/badline/chips/m6809/operations.rb +258 -0
- data/lib/badline/chips/m6809/plans.rb +143 -0
- data/lib/badline/chips/m6809/registers.rb +82 -0
- data/lib/badline/chips/m6809/stack.rb +140 -0
- data/lib/badline/chips/m6809.rb +13 -0
- data/lib/badline/chips/riot/timer.rb +14 -3
- data/lib/badline/chips/rriot.rb +8 -3
- data/lib/badline/chips/sid/decimator.rb +1 -1
- data/lib/badline/chips/sid/filter.rb +4 -4
- data/lib/badline/chips/sid/keyed_parts.rb +51 -18
- data/lib/badline/chips/sid/waveform.rb +6 -6
- data/lib/badline/chips/sid.rb +6 -2
- data/lib/badline/chips/vic_i/keyed_parts.rb +12 -2
- data/lib/badline/chips/vic_i/light_pen.rb +89 -0
- data/lib/badline/chips/vic_i.rb +91 -23
- data/lib/badline/data_path.rb +3 -3
- data/lib/badline/drives/ieee_models.rb +54 -0
- data/lib/badline/drives/model1001.rb +37 -0
- data/lib/badline/drives/model1541/sector_reader.rb +20 -3
- data/lib/badline/drives/model1541.rb +1 -0
- data/lib/badline/drives/model1551.rb +1 -0
- data/lib/badline/drives/model1571.rb +1 -0
- data/lib/badline/drives/model1581.rb +1 -0
- data/lib/badline/drives/model2031.rb +7 -0
- data/lib/badline/drives/model4040/disk.rb +62 -0
- data/lib/badline/drives/model4040/ip_bus.rb +46 -0
- data/lib/badline/drives/model4040/keyed_state.rb +57 -0
- data/lib/badline/drives/model4040/mechanism.rb +195 -0
- data/lib/badline/drives/model4040/parts.rb +20 -0
- data/lib/badline/drives/model4040.rb +195 -0
- data/lib/badline/drives/model8050/board.rb +79 -0
- data/lib/badline/drives/model8050/dos_board.rb +134 -0
- data/lib/badline/drives/model8050/fdc_bus.rb +7 -0
- data/lib/badline/drives/model8050/idle.rb +148 -31
- data/lib/badline/drives/model8050/keyed_state.rb +5 -2
- data/lib/badline/drives/model8050.rb +32 -130
- data/lib/badline/drives/model9060/idle.rb +169 -0
- data/lib/badline/drives/model9060/keyed_state.rb +123 -0
- data/lib/badline/drives/model9060/parts.rb +98 -0
- data/lib/badline/drives/model9060/sasi_bus.rb +88 -0
- data/lib/badline/drives/model9060/sasi_commands.rb +168 -0
- data/lib/badline/drives/model9060/sasi_target.rb +161 -0
- data/lib/badline/drives/model9060.rb +197 -0
- data/lib/badline/frontend/app.rb +8 -0
- data/lib/badline/frontend/boot.rb +3 -2
- data/lib/badline/frontend/controls.rb +62 -6
- data/lib/badline/frontend/keys.rb +36 -0
- data/lib/badline/frontend/kim1_panel.rb +208 -0
- data/lib/badline/frontend/media_slots.rb +8 -1
- data/lib/badline/frontend/menu/drive_types.rb +56 -0
- data/lib/badline/frontend/menu/menu_dialogs.rb +61 -14
- data/lib/badline/frontend/menu/menu_media.rb +42 -12
- data/lib/badline/frontend/menu/menu_pages.rb +97 -2
- data/lib/badline/frontend/menu/menu_ports.rb +38 -1
- data/lib/badline/frontend/menu/pause_menu.rb +5 -1
- data/lib/badline/frontend/pet_keyboard.rb +23 -1
- data/lib/badline/frontend/screen.rb +1 -1
- data/lib/badline/frontend/screens.rb +40 -2
- data/lib/badline/frontend/teletype_pane.rb +152 -0
- data/lib/badline/frontend/timeline.rb +31 -2
- data/lib/badline/frontend.rb +3 -0
- data/lib/badline/input/joystick.rb +15 -2
- data/lib/badline/kim1/bus.rb +117 -0
- data/lib/badline/kim1/cpu.rb +25 -0
- data/lib/badline/kim1/host_terminal.rb +74 -0
- data/lib/badline/kim1/key_entry.rb +74 -0
- data/lib/badline/kim1/keyed_modules.rb +53 -0
- data/lib/badline/kim1/keyed_state.rb +133 -0
- data/lib/badline/kim1/panel.rb +176 -0
- data/lib/badline/kim1/rriots.rb +28 -0
- data/lib/badline/kim1/sound.rb +29 -0
- data/lib/badline/kim1/tape.rb +118 -0
- data/lib/badline/kim1/teletype.rb +193 -0
- data/lib/badline/kim1.rb +194 -0
- data/lib/badline/machine/ieee_drives.rb +94 -117
- data/lib/badline/machine/keyboard_buffer.rb +20 -3
- data/lib/badline/machine/line_sound.rb +122 -0
- data/lib/badline/machine.rb +49 -15
- data/lib/badline/media/extensions.rb +8 -4
- data/lib/badline/media/kim1_media.rb +150 -0
- data/lib/badline/media/pet_media.rb +3 -1
- data/lib/badline/media/true_drive.rb +52 -45
- data/lib/badline/media/vic20_media.rb +4 -2
- data/lib/badline/media.rb +16 -6
- data/lib/badline/memory.rb +2 -4
- data/lib/badline/options/family.rb +98 -8
- data/lib/badline/options/help.rb +44 -12
- data/lib/badline/options/table.rb +44 -12
- data/lib/badline/options/validation.rb +44 -8
- data/lib/badline/options.rb +27 -8
- data/lib/badline/pet/bus.rb +16 -2
- data/lib/badline/pet/keyboard_ports.rb +9 -2
- data/lib/badline/pet/keyed_state.rb +4 -0
- data/lib/badline/pet/model.rb +11 -2
- data/lib/badline/pet/rom_sets.rb +1 -0
- data/lib/badline/pet/sound.rb +5 -105
- data/lib/badline/pet/superpet/acia.rb +12 -0
- data/lib/badline/pet/superpet/board.rb +221 -0
- data/lib/badline/pet/superpet/dongle.rb +92 -0
- data/lib/badline/pet/superpet/keyed_modules.rb +63 -0
- data/lib/badline/pet/superpet/keyed_parts.rb +76 -0
- data/lib/badline/pet/superpet/m6809.rb +32 -0
- data/lib/badline/pet/superpet/waterloo_traps.rb +199 -0
- data/lib/badline/pet/superpet.rb +221 -0
- data/lib/badline/pet.rb +23 -3
- data/lib/badline/plus4/keyed_state.rb +2 -0
- data/lib/badline/plus4.rb +5 -0
- data/lib/badline/roms/1001/fdc-251257-02a.rom +0 -0
- data/lib/badline/roms/4040/dos-1.2.rom +0 -0
- data/lib/badline/roms/4040/dos-2.1-rev.rom +0 -0
- data/lib/badline/roms/4040/dos-2.1.rom +0 -0
- data/lib/badline/roms/4040/fdc-901466-02.rom +0 -0
- data/lib/badline/roms/4040/fdc-901466-04.rom +0 -0
- data/lib/badline/roms/9060/dos-3.0.rom +0 -0
- data/lib/badline/roms/9060/sasi-300515-revb.rom +0 -0
- data/lib/badline/roms/README +176 -0
- data/lib/badline/roms/kim1/6530-002.rom +0 -0
- data/lib/badline/roms/kim1/6530-003.rom +0 -0
- data/lib/badline/roms/pet/characters-superpet.rom +0 -0
- data/lib/badline/roms/pet/superpet-a000.rom +0 -0
- data/lib/badline/roms/pet/superpet-c000.rom +0 -0
- data/lib/badline/roms/pet/superpet-e000-50hz.rom +0 -0
- data/lib/badline/roms/pet/superpet-e000.rom +0 -0
- data/lib/badline/snapshot/container.rb +8 -4
- data/lib/badline/snapshot/field_doc/markdown.rb +1 -1
- data/lib/badline/snapshot/field_doc.rb +35 -10
- data/lib/badline/snapshot/image.rb +19 -7
- data/lib/badline/snapshot/kim1_setup.rb +28 -0
- data/lib/badline/snapshot/pet_setup.rb +27 -11
- data/lib/badline/snapshot/storage_setup.rb +4 -1
- data/lib/badline/snapshot/vic20_setup.rb +11 -7
- data/lib/badline/snapshot/vice/pet.rb +14 -7
- data/lib/badline/snapshot/vice/pet_cpu.rb +39 -5
- data/lib/badline/snapshot/vice/pet_crtc.rb +15 -2
- data/lib/badline/snapshot/vice/pet_dongle.rb +72 -0
- data/lib/badline/snapshot/vice/pet_drive8050.rb +188 -68
- data/lib/badline/snapshot/vice/pet_mem.rb +55 -5
- data/lib/badline/snapshot/vice/pet_restore.rb +40 -11
- data/lib/badline/snapshot/vice/vic20.rb +70 -0
- data/lib/badline/snapshot/vice/vic20_cpu.rb +61 -0
- data/lib/badline/snapshot/vice/vic20_mem.rb +85 -0
- data/lib/badline/snapshot/vice/vic20_restore.rb +124 -0
- data/lib/badline/snapshot/vice/vic20_vici.rb +200 -0
- data/lib/badline/snapshot/vice.rb +1 -0
- data/lib/badline/snapshot.rb +8 -4
- data/lib/badline/status.rb +30 -30
- data/lib/badline/storage/d67_image.rb +55 -0
- data/lib/badline/storage/d90_image.rb +180 -0
- data/lib/badline/storage/kim_tape.rb +254 -0
- data/lib/badline/storage/papertape.rb +90 -0
- data/lib/badline/storage.rb +4 -0
- data/lib/badline/support/kim1_checkpoint.rb +41 -0
- data/lib/badline/support/superpet_checkpoint.rb +35 -0
- data/lib/badline/version.rb +1 -1
- data/lib/badline/vic20/keyed_modules.rb +6 -3
- data/lib/badline/vic20/keyed_state.rb +6 -4
- data/lib/badline/vic20/port_wiring.rb +11 -0
- data/lib/badline/vic20.rb +30 -12
- data/lib/badline.rb +1 -0
- metadata +85 -1
|
@@ -86,16 +86,20 @@ module Badline
|
|
|
86
86
|
LINE_LAYOUT = 1
|
|
87
87
|
|
|
88
88
|
# The VIC-I's fields in a keyed snapshot module (Snapshot::Keyed): the
|
|
89
|
-
# registers, the beam, the
|
|
90
|
-
#
|
|
89
|
+
# registers, the beam, the frame's length in lines and whether the
|
|
90
|
+
# next interlaced field is the long one, the text window's counters
|
|
91
|
+
# and the fetch in flight, the light pen's latch and line under pen.*,
|
|
92
|
+
# the painter's lines under line.*, the picture, and the sound
|
|
91
93
|
# under sound.* What the registers decode to, where the line's fetches
|
|
92
94
|
# end and the next event are worked out on load.
|
|
93
95
|
module KeyedState
|
|
94
96
|
def save_fields(out)
|
|
95
97
|
out.bytes("registers", @registers).int("raster", @rasterline).int("column", @column)
|
|
98
|
+
out.int("frame_lines", @frame_lines).bool("long_field", @long_field)
|
|
96
99
|
out.bool("window_open", @window_open).bool("window_done", @window_done).int("row_start", @row_start)
|
|
97
100
|
out.int("matrix", @matrix).int("char_line", @char_line).int("text_rows", @text_rows)
|
|
98
101
|
out.int("fetch_from", @fetch_from).int("fetched", @fetched).int("code", @code)
|
|
102
|
+
out.int("pen.x", @pen_x).int("pen.y", @pen_y).bool("pen.low", @pen_low)
|
|
99
103
|
out.int("line.layout", LINE_LAYOUT)
|
|
100
104
|
@painter.save_fields(out)
|
|
101
105
|
@sound&.save_fields(out, "sound.")
|
|
@@ -106,6 +110,8 @@ module Badline
|
|
|
106
110
|
input.bytes_into("registers", @registers)
|
|
107
111
|
@rasterline = input.int("raster", @rasterline)
|
|
108
112
|
@column = input.int("column", @column)
|
|
113
|
+
@frame_lines = input.int("frame_lines", @lines_per_frame)
|
|
114
|
+
@long_field = input.bool("long_field", true)
|
|
109
115
|
@window_open = input.bool("window_open", @window_open)
|
|
110
116
|
@window_done = input.bool("window_done", @window_done)
|
|
111
117
|
@row_start = input.int("row_start", @row_start)
|
|
@@ -115,6 +121,9 @@ module Badline
|
|
|
115
121
|
@fetch_from = input.int("fetch_from", @fetch_from)
|
|
116
122
|
@fetched = input.int("fetched", @fetched)
|
|
117
123
|
@code = input.byte("code", @code)
|
|
124
|
+
@pen_x = input.byte("pen.x", @pen_x)
|
|
125
|
+
@pen_y = input.byte("pen.y", @pen_y)
|
|
126
|
+
@pen_low = input.bool("pen.low", @pen_low)
|
|
118
127
|
line = input.int("line.layout", 0) == LINE_LAYOUT
|
|
119
128
|
input.adjust("line in progress from another renderer, restarted") unless line || input.missing?
|
|
120
129
|
@painter.load_fields(input, line)
|
|
@@ -122,6 +131,7 @@ module Badline
|
|
|
122
131
|
end
|
|
123
132
|
|
|
124
133
|
def restored!
|
|
134
|
+
@frame_wrap = @frame_lines == @interlaced_lines ? @count_column : @wrap_column
|
|
125
135
|
@origin_x = @registers[0] & 0x7f
|
|
126
136
|
@columns = @registers[2] & 0x7f
|
|
127
137
|
@origin_y = @registers[1]
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
module Chips
|
|
5
|
+
class VICI
|
|
6
|
+
# The light pen input, the line the VIC-20 shares with the joystick's
|
|
7
|
+
# fire button, and the position a falling edge on it latches into
|
|
8
|
+
# $9006 and $9007.
|
|
9
|
+
#
|
|
10
|
+
# $9006 reads 2h + 1, where h is the edge's column plus two, wrapping
|
|
11
|
+
# at the line's end, and $9007 bits 8-1 of the raster count in the
|
|
12
|
+
# cycle after the edge. Around the cycle where the count turns to 0
|
|
13
|
+
# the latch reads otherwise:
|
|
14
|
+
#
|
|
15
|
+
# - An edge from 8 cycles before that cycle to 1 after it latches as
|
|
16
|
+
# an edge in the cycle after it would, with $9007 at 0, but for an
|
|
17
|
+
# edge 2 cycles before it, which latches an even $9006, one less.
|
|
18
|
+
# - Before that, an edge latches nothing while the count read its
|
|
19
|
+
# last value of the frame in the cycle before the edge: line 311 on
|
|
20
|
+
# the 6561, and 261, past the last line, on the 6560.
|
|
21
|
+
module LightPen
|
|
22
|
+
# Where the window around the count's turn to 0 starts and ends,
|
|
23
|
+
# in cycles from that turn, and the edge in it that latches an even
|
|
24
|
+
# $9006.
|
|
25
|
+
WRAP_WINDOW = (-8..1)
|
|
26
|
+
WRAP_EVEN = -2
|
|
27
|
+
|
|
28
|
+
# The pen's line, low while the pen sees the beam or fire is held.
|
|
29
|
+
def lightpen_level(high)
|
|
30
|
+
low = !high
|
|
31
|
+
return if low == @pen_low
|
|
32
|
+
|
|
33
|
+
@pen_low = low
|
|
34
|
+
pen_fell if low
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
def power_on_pen!
|
|
40
|
+
@pen_x = @pen_y = 0
|
|
41
|
+
@pen_low = false
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def pen_fell
|
|
45
|
+
line = @rasterline
|
|
46
|
+
column = @column
|
|
47
|
+
from_wrap = cycles_from_wrap(line, column)
|
|
48
|
+
if WRAP_WINDOW.cover?(from_wrap)
|
|
49
|
+
latch_pen(@frame_wrap + 1, 0, from_wrap == WRAP_EVEN)
|
|
50
|
+
elsif !pen_blocked?(line, column)
|
|
51
|
+
next_column = column + 1
|
|
52
|
+
if next_column == @cycles_per_line
|
|
53
|
+
next_column = 0
|
|
54
|
+
line = line == @frame_lines - 1 ? 0 : line + 1
|
|
55
|
+
end
|
|
56
|
+
latch_pen(column, count_at(line, next_column) >> 1, false)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# How many cycles the beam is past the cycle where the count turns
|
|
61
|
+
# to 0, negative before it. On the 6561 the count turns as the
|
|
62
|
+
# frame starts, so the frame's first cycles are past it.
|
|
63
|
+
def cycles_from_wrap(line, column)
|
|
64
|
+
position = (line * @cycles_per_line) + column
|
|
65
|
+
return position if position < 2 && @frame_wrap == @cycles_per_line
|
|
66
|
+
|
|
67
|
+
position - ((@frame_lines - 1) * @cycles_per_line) - @frame_wrap
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Whether the count read its last value of the frame in the cycle
|
|
71
|
+
# before this one.
|
|
72
|
+
def pen_blocked?(line, column)
|
|
73
|
+
column -= 1
|
|
74
|
+
if column.negative?
|
|
75
|
+
column = @cycles_per_line - 1
|
|
76
|
+
line = line.zero? ? @frame_lines - 1 : line - 1
|
|
77
|
+
end
|
|
78
|
+
last = @frame_wrap > @count_column ? @frame_lines : @frame_lines - 1
|
|
79
|
+
count_at(line, column) == last
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def latch_pen(column, count, even)
|
|
83
|
+
@pen_x = (2 * ((column + 2) % @cycles_per_line)) + (even ? 0 : 1)
|
|
84
|
+
@pen_y = count
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
data/lib/badline/chips/vic_i.rb
CHANGED
|
@@ -2,19 +2,22 @@
|
|
|
2
2
|
|
|
3
3
|
require "badline/chips/vic_i/painter"
|
|
4
4
|
require "badline/chips/vic_i/text_window"
|
|
5
|
+
require "badline/chips/vic_i/light_pen"
|
|
5
6
|
require "badline/chips/vic_i/sound"
|
|
6
7
|
require "badline/chips/vic_i/keyed_parts"
|
|
7
8
|
|
|
8
9
|
module Badline
|
|
9
10
|
module Chips
|
|
10
|
-
# The VIC-I, the 6561 on PAL machines
|
|
11
|
+
# The VIC-I, the 6561 on PAL machines or the 6560 on NTSC ones: its
|
|
12
|
+
# sixteen registers, the
|
|
11
13
|
# raster, the text window's fetches over the V-bus and the picture. The
|
|
12
14
|
# writes to the sound's registers, $900A-$900E, go on to the Sound
|
|
13
15
|
# attached as #sound.
|
|
14
16
|
#
|
|
15
17
|
# The registers repeat through $9000-$90FF. $9003 bit 7 and $9004 read
|
|
16
|
-
# the raster
|
|
17
|
-
#
|
|
18
|
+
# the raster count, its bit 0 and bits 8-1, which on the 6561 is the
|
|
19
|
+
# raster line (see Profile for the 6560's). $9006 and $9007 read the
|
|
20
|
+
# light pen's latch (LightPen), and $9008 and $9009 the two
|
|
18
21
|
# pots, which read $FF with nothing plugged in. The other registers
|
|
19
22
|
# read back what was written to them.
|
|
20
23
|
#
|
|
@@ -35,23 +38,49 @@ module Badline
|
|
|
35
38
|
# of a text row where the row started, and the next row starts where
|
|
36
39
|
# the last line of the row left it. It starts the frame at zero.
|
|
37
40
|
#
|
|
38
|
-
# The picture is 4 pixels a cycle, so 284 a line
|
|
39
|
-
# pixels come out in the two cycles after its
|
|
40
|
-
# display's lines start 6 cycles after the raster
|
|
41
|
-
# xvic's view of the frame does, so the character
|
|
42
|
-
# into the line lands 48 pixels in. Lines 0-27 are
|
|
41
|
+
# The picture is 4 pixels a cycle, so 284 a line on the 6561 and 260 on
|
|
42
|
+
# the 6560. A character's 8 pixels come out in the two cycles after its
|
|
43
|
+
# pattern fetch. The display's lines start 6 cycles after the raster
|
|
44
|
+
# line moves, as xvic's view of the frame does, so the character
|
|
45
|
+
# fetched 16 cycles into the line lands 48 pixels in. Lines 0-27 are
|
|
46
|
+
# blanked.
|
|
43
47
|
class VICI
|
|
44
48
|
include TextWindow
|
|
49
|
+
include LightPen
|
|
45
50
|
|
|
46
51
|
# What a VIC-I sets for the machine: the clock, the raster's cycles
|
|
47
|
-
# per line and lines per frame,
|
|
48
|
-
#
|
|
49
|
-
# the
|
|
50
|
-
|
|
52
|
+
# per line and lines per frame, how many lines at the top of the frame
|
|
53
|
+
# it blanks and how many it shows from there, and where in the line
|
|
54
|
+
# the raster count $9003 and $9004 read moves on, and how many lines
|
|
55
|
+
# the long field of an interlaced frame has, or 0 without interlace.
|
|
56
|
+
#
|
|
57
|
+
# The 6561's count is the raster line. The 6560's runs ahead of it: it
|
|
58
|
+
# reads the next line from +count_column+ on, and on the last line it
|
|
59
|
+
# reads one past the last line from there, then 0 from
|
|
60
|
+
# +wrap_column+ on, so the count's first and last values each last
|
|
61
|
+
# about half a line. The 6561's +count_column+ is past the end of the
|
|
62
|
+
# line, so it never moves early.
|
|
63
|
+
#
|
|
64
|
+
# With $9000 bit 7 set at the start of a frame, the 6560 interlaces:
|
|
65
|
+
# its frames alternate between the long field, +interlaced_lines+
|
|
66
|
+
# long, and the short one, a line shorter, the long one first. On the
|
|
67
|
+
# long field's last line the count wraps to 0 where it would move on,
|
|
68
|
+
# so its first and last values each last a whole line.
|
|
69
|
+
Profile = Data.define(:clock_hz, :cycles_per_line, :lines_per_frame, :blanked_lines, :shown_lines,
|
|
70
|
+
:count_column, :wrap_column, :interlaced_lines)
|
|
51
71
|
|
|
52
72
|
# The 6561: 71 cycles by 312 lines, lines 0-27 blanked (Marko
|
|
53
73
|
# Mäkelä's VIC-I.txt).
|
|
54
|
-
PAL = Profile.new(clock_hz: 1_108_405, cycles_per_line: 71, lines_per_frame: 312, blanked_lines: 28
|
|
74
|
+
PAL = Profile.new(clock_hz: 1_108_405, cycles_per_line: 71, lines_per_frame: 312, blanked_lines: 28,
|
|
75
|
+
shown_lines: 284, count_column: 71, wrap_column: 71, interlaced_lines: 0)
|
|
76
|
+
|
|
77
|
+
# The 6560: 65 cycles by 261 lines on a 14.31818 MHz crystal divided
|
|
78
|
+
# by 14. The count's columns and the lines shown are xvic's, measured
|
|
79
|
+
# as a black box: the count moves on 28 cycles into the line, and
|
|
80
|
+
# wraps to 0 on the last line 61 cycles in. Interlaced, its fields are
|
|
81
|
+
# 263 and 262 lines.
|
|
82
|
+
NTSC = Profile.new(clock_hz: 1_022_727, cycles_per_line: 65, lines_per_frame: 261, blanked_lines: 28,
|
|
83
|
+
shown_lines: 233, count_column: 28, wrap_column: 61, interlaced_lines: 263)
|
|
55
84
|
|
|
56
85
|
PIXELS_PER_CYCLE = 4
|
|
57
86
|
|
|
@@ -71,6 +100,10 @@ module Badline
|
|
|
71
100
|
0xb27240, 0xf2b27f, 0xc2655d, 0xbdffff, 0xf299ff, 0x8de57f, 0x806eda, 0xffffa5
|
|
72
101
|
].freeze
|
|
73
102
|
|
|
103
|
+
# The display's lines: the frame's, or the long interlaced field's
|
|
104
|
+
# where that is longer.
|
|
105
|
+
attr_reader :height
|
|
106
|
+
|
|
74
107
|
attr_reader :rasterline, :column, :profile
|
|
75
108
|
attr_accessor :sound
|
|
76
109
|
|
|
@@ -78,7 +111,11 @@ module Badline
|
|
|
78
111
|
@profile = profile
|
|
79
112
|
@cycles_per_line = profile.cycles_per_line
|
|
80
113
|
@lines_per_frame = profile.lines_per_frame
|
|
81
|
-
@
|
|
114
|
+
@count_column = profile.count_column
|
|
115
|
+
@wrap_column = profile.wrap_column
|
|
116
|
+
@interlaced_lines = profile.interlaced_lines
|
|
117
|
+
@height = [@lines_per_frame, @interlaced_lines].max
|
|
118
|
+
@painter = Painter.new(@height, @cycles_per_line * PIXELS_PER_CYCLE, profile.blanked_lines)
|
|
82
119
|
@patterns = @painter.patterns
|
|
83
120
|
@colors = @painter.colors
|
|
84
121
|
@counts = @painter.counts
|
|
@@ -106,11 +143,15 @@ module Badline
|
|
|
106
143
|
@origin_x = @origin_y = @columns = @rows = 0
|
|
107
144
|
@char_shift = 3
|
|
108
145
|
@screen_base = @char_base = 0
|
|
146
|
+
@long_field = true
|
|
147
|
+
@frame_lines = @lines_per_frame
|
|
148
|
+
@frame_wrap = @wrap_column
|
|
109
149
|
new_frame
|
|
110
150
|
@fetch_from = @fetch_to = 0
|
|
111
151
|
@fetched = @slot = 0
|
|
112
152
|
@code = 0
|
|
113
|
-
@painter.power_on!(@
|
|
153
|
+
@painter.power_on!(@frame_lines - 1)
|
|
154
|
+
power_on_pen!
|
|
114
155
|
schedule
|
|
115
156
|
end
|
|
116
157
|
|
|
@@ -131,14 +172,12 @@ module Badline
|
|
|
131
172
|
# The sixteen registers as written.
|
|
132
173
|
def register_file = @registers.dup
|
|
133
174
|
|
|
134
|
-
# The display, a palette index a pixel,
|
|
135
|
-
# of the frame.
|
|
175
|
+
# The display, a palette index a pixel, 4 to a cycle of the line, and
|
|
176
|
+
# every line of the frame.
|
|
136
177
|
def display = @painter.display
|
|
137
178
|
|
|
138
179
|
def width = @painter.width
|
|
139
180
|
|
|
140
|
-
def height = @lines_per_frame
|
|
141
|
-
|
|
142
181
|
# Whether each line of the display has changed since the front end
|
|
143
182
|
# last cleared them.
|
|
144
183
|
def dirty_lines = @painter.dirty_lines
|
|
@@ -157,9 +196,10 @@ module Badline
|
|
|
157
196
|
def peek(addr)
|
|
158
197
|
register = addr & 0x0f
|
|
159
198
|
case register
|
|
160
|
-
when 0x03 then ((
|
|
161
|
-
when 0x04 then
|
|
162
|
-
when 0x06
|
|
199
|
+
when 0x03 then ((raster_count & 0x01) << 7) | (@registers[3] & 0x7f)
|
|
200
|
+
when 0x04 then raster_count >> 1
|
|
201
|
+
when 0x06 then @pen_x
|
|
202
|
+
when 0x07 then @pen_y
|
|
163
203
|
when 0x08, 0x09 then 0xff
|
|
164
204
|
else @registers[register]
|
|
165
205
|
end
|
|
@@ -181,8 +221,19 @@ module Badline
|
|
|
181
221
|
end
|
|
182
222
|
end
|
|
183
223
|
|
|
224
|
+
# The raster count $9003 and $9004 read (see Profile).
|
|
225
|
+
def raster_count = count_at(@rasterline, @column)
|
|
226
|
+
|
|
184
227
|
private
|
|
185
228
|
|
|
229
|
+
# What the count reads at +column+ of +line+ in this frame.
|
|
230
|
+
def count_at(line, column)
|
|
231
|
+
return line if column < @count_column
|
|
232
|
+
return 0 if line == @frame_lines - 1 && column >= @frame_wrap
|
|
233
|
+
|
|
234
|
+
line + 1
|
|
235
|
+
end
|
|
236
|
+
|
|
186
237
|
def event(column)
|
|
187
238
|
if column == @cycles_per_line
|
|
188
239
|
column = @column = 0
|
|
@@ -205,9 +256,10 @@ module Badline
|
|
|
205
256
|
|
|
206
257
|
def start_line
|
|
207
258
|
line = @rasterline + 1
|
|
208
|
-
if line == @
|
|
259
|
+
if line == @frame_lines
|
|
209
260
|
line = 0
|
|
210
261
|
new_frame
|
|
262
|
+
next_field
|
|
211
263
|
end
|
|
212
264
|
@rasterline = line
|
|
213
265
|
next_text_line if @window_open
|
|
@@ -215,6 +267,22 @@ module Badline
|
|
|
215
267
|
place_fetches
|
|
216
268
|
end
|
|
217
269
|
|
|
270
|
+
# How many lines the frame starting has, and where on its last line
|
|
271
|
+
# the count wraps: the field's, if $9000 bit 7 asks the 6560 to
|
|
272
|
+
# interlace, else the profile's.
|
|
273
|
+
def next_field
|
|
274
|
+
if @interlaced_lines.positive? && @registers[0] >= 0x80
|
|
275
|
+
long = @long_field
|
|
276
|
+
@frame_lines = long ? @interlaced_lines : @interlaced_lines - 1
|
|
277
|
+
@frame_wrap = long ? @count_column : @wrap_column
|
|
278
|
+
@long_field = !long
|
|
279
|
+
else
|
|
280
|
+
@frame_lines = @lines_per_frame
|
|
281
|
+
@frame_wrap = @wrap_column
|
|
282
|
+
@long_field = true
|
|
283
|
+
end
|
|
284
|
+
end
|
|
285
|
+
|
|
218
286
|
def place_fetches
|
|
219
287
|
line = @rasterline
|
|
220
288
|
@slot = line * Painter::SLOTS
|
data/lib/badline/data_path.rb
CHANGED
|
@@ -24,10 +24,10 @@ module Badline
|
|
|
24
24
|
File.join(xdg, "badline")
|
|
25
25
|
end
|
|
26
26
|
|
|
27
|
-
# The quicksaves, autosaves or
|
|
28
|
-
# along with data_path if it doesn't exist yet.
|
|
27
|
+
# The quicksaves, autosaves, saves or tapes subfolder of data_path,
|
|
28
|
+
# created along with data_path if it doesn't exist yet.
|
|
29
29
|
def data_folder(name)
|
|
30
|
-
raise ArgumentError, "unknown data folder: #{name}" unless %w[quicksaves autosaves saves].include?(name)
|
|
30
|
+
raise ArgumentError, "unknown data folder: #{name}" unless %w[quicksaves autosaves saves tapes].include?(name)
|
|
31
31
|
|
|
32
32
|
path = File.join(data_path, name)
|
|
33
33
|
FileUtils.mkdir_p(path)
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
module Drives
|
|
5
|
+
# The true drives the PET's and the CBM-II's IEEE-488 bus takes, by the
|
|
6
|
+
# names --drive-type gives them, xpet's drive types, and the disk images
|
|
7
|
+
# each reads. Options reads it to check --drive-type, and
|
|
8
|
+
# Media::TrueDrive to pick the drive an image goes in, so it loads
|
|
9
|
+
# nothing else. Machine::IEEEDrives builds each.
|
|
10
|
+
module IEEEModels
|
|
11
|
+
# The images each model reads, by extension.
|
|
12
|
+
MEDIA = {
|
|
13
|
+
"2031" => %w[.d64 .g64], "8050" => %w[.d80], "8250" => %w[.d82], "1001" => %w[.d82], "4040" => %w[.d64 .d67],
|
|
14
|
+
"3040" => %w[.d64 .d67], "2040" => %w[.d67], "9060" => %w[.d90], "9090" => %w[.d90]
|
|
15
|
+
}.freeze
|
|
16
|
+
|
|
17
|
+
# The model --true-drive plugs in without --drive-type.
|
|
18
|
+
DEFAULT = "2031"
|
|
19
|
+
|
|
20
|
+
# The model an image goes in when neither the drive on device 8 nor
|
|
21
|
+
# the machine's own model reads it. A .d90 goes by its size
|
|
22
|
+
# (hard_disk_model).
|
|
23
|
+
FOR_IMAGES = { ".d64" => "2031", ".g64" => "2031", ".d80" => "8050", ".d82" => "8250", ".d67" => "2040" }.freeze
|
|
24
|
+
|
|
25
|
+
NAMES = MEDIA.keys.freeze
|
|
26
|
+
|
|
27
|
+
module_function
|
|
28
|
+
|
|
29
|
+
# Whether the image at path is a hard disk's, which only a true drive
|
|
30
|
+
# reads.
|
|
31
|
+
def hard_disk?(path) = File.extname(path).casecmp?(".d90")
|
|
32
|
+
|
|
33
|
+
# The hard disk a .d90 image belongs to, by its size: a D9090 for a
|
|
34
|
+
# D9090's, and otherwise a D9060.
|
|
35
|
+
def hard_disk_model(path) = Storage::D90Image.heads_for(File.size?(path).to_i) == 6 ? "9090" : "9060"
|
|
36
|
+
|
|
37
|
+
# Whether the model reads the image at path.
|
|
38
|
+
def reads?(model, path) = MEDIA.fetch(model, []).include?(File.extname(path).downcase)
|
|
39
|
+
|
|
40
|
+
# The model the image at path goes in: the drive's on device 8,
|
|
41
|
+
# `present`, if it reads it, or else the machine's own, `chosen`, or
|
|
42
|
+
# else the image's own, or nil for an image none reads. A .d90 goes
|
|
43
|
+
# in the machine's own model if that is a hard disk, and otherwise in
|
|
44
|
+
# the one its size names.
|
|
45
|
+
def taking(path, present, chosen)
|
|
46
|
+
return reads?(chosen, path) ? chosen : hard_disk_model(path) if hard_disk?(path)
|
|
47
|
+
return present if present && reads?(present, path)
|
|
48
|
+
return chosen if reads?(chosen, path)
|
|
49
|
+
|
|
50
|
+
FOR_IMAGES[File.extname(path).downcase]
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "badline/drives/model8050"
|
|
4
|
+
|
|
5
|
+
module Badline
|
|
6
|
+
module Drives
|
|
7
|
+
# The SFD-1001: the 8250LP's board redrawn as one PCB with one
|
|
8
|
+
# double-sided drive, a Matsushita JU-570-2 (schematic 251406). Both
|
|
9
|
+
# processors see an 8250. The interface processor runs DOS 2.7, which
|
|
10
|
+
# doesn't know there is one drive, and the controller runs 251257-02A
|
|
11
|
+
# from the EPROM on its 6530 adaptor (251252-01): the 8250LP's program
|
|
12
|
+
# with an interrupt every 3 ms for the JU-570's step rate, a longer head
|
|
13
|
+
# settle, and every job for drive 1 ended with an error. Drive 1's
|
|
14
|
+
# stepper, motor and LED aren't wired, so the unit takes no disk there,
|
|
15
|
+
# and its LED never lights.
|
|
16
|
+
class Model1001 < Model8250
|
|
17
|
+
# The LED bit of drive 1, which the board leaves unwired.
|
|
18
|
+
DRIVE1_LED = 0x01
|
|
19
|
+
|
|
20
|
+
def model_name = "1001"
|
|
21
|
+
|
|
22
|
+
# +fdc_rom+ defaults to 251257-02A, the rest as Model8050's.
|
|
23
|
+
def initialize(dos_rom: nil, fdc_rom: nil, host_clock_hz: CLOCK_HZ, device: 8)
|
|
24
|
+
super(dos_rom:, fdc_rom: fdc_rom || ROM.read("1001/fdc-251257-02a.rom"), host_clock_hz:, device:)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def leds = super & ~DRIVE1_LED
|
|
28
|
+
|
|
29
|
+
# Puts a Disk in drive 0, the only one. Drive 1 raises ArgumentError.
|
|
30
|
+
def insert(disk, drive: 0)
|
|
31
|
+
raise ArgumentError, "the 1001 has one drive, not drive #{drive}" unless drive.zero?
|
|
32
|
+
|
|
33
|
+
super
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -13,12 +13,22 @@ module Badline
|
|
|
13
13
|
# that sector's data block, whatever it holds.
|
|
14
14
|
module SectorReader
|
|
15
15
|
HEADER_BITS = 10 * 8
|
|
16
|
-
DATA_BITS = 325 * 8
|
|
17
16
|
SYNC = /1{10,}/
|
|
18
17
|
|
|
18
|
+
# A data block as far as its checksum: its ID, 256 bytes and the
|
|
19
|
+
# checksum. The DOS writes two more after them, and switches back to
|
|
20
|
+
# read mode a few bits into the last, so the gap that was there
|
|
21
|
+
# before finishes its last code.
|
|
22
|
+
CHECKED_BYTES = 258
|
|
23
|
+
CHECKED_BITS = CHECKED_BYTES * 10
|
|
24
|
+
|
|
25
|
+
# The codes of two $0 nibbles, which finish the last group of
|
|
26
|
+
# CHECKED_BITS.
|
|
27
|
+
PADDING = "01010" * 4
|
|
28
|
+
|
|
19
29
|
# A sector as read: its decoded header, [ID, checksum, sector, track,
|
|
20
30
|
# the second ID byte, the first, ...], and its decoded data block,
|
|
21
|
-
# [ID, 256 data bytes, checksum
|
|
31
|
+
# [ID, 256 data bytes, checksum], nil when there isn't one that
|
|
22
32
|
# decodes.
|
|
23
33
|
Sector = Struct.new(:header, :data)
|
|
24
34
|
|
|
@@ -62,10 +72,17 @@ module Badline
|
|
|
62
72
|
next unless header && header[0] == Disk::HEADER_ID
|
|
63
73
|
|
|
64
74
|
data_sync = SYNC.match(bits, pos + HEADER_BITS)
|
|
65
|
-
yield header, data_sync &&
|
|
75
|
+
yield header, data_sync && decode_data(bits, data_sync.end(0))
|
|
66
76
|
end
|
|
67
77
|
end
|
|
68
78
|
|
|
79
|
+
def decode_data(bits, pos)
|
|
80
|
+
slice = bits[pos, CHECKED_BITS]
|
|
81
|
+
return unless slice.length == CHECKED_BITS
|
|
82
|
+
|
|
83
|
+
GCR.decode([slice + PADDING].pack("B*").bytes)&.first(CHECKED_BYTES)
|
|
84
|
+
end
|
|
85
|
+
|
|
69
86
|
def decode(bits, pos, count)
|
|
70
87
|
slice = bits[pos, count]
|
|
71
88
|
return unless slice.length == count
|
|
@@ -39,6 +39,7 @@ module Badline
|
|
|
39
39
|
@via2 = DiskVIA.new(start: 0x1c00, mechanism: @mechanism)
|
|
40
40
|
@bus = Bus.new(rom: rom || ROM.load("dos1541.rom", 0xc000), via1: @via1, via2: @via2)
|
|
41
41
|
@cpu = CPU.new(@bus, debug:)
|
|
42
|
+
@cpu.power_on!
|
|
42
43
|
@clock_hz = CLOCK_HZ
|
|
43
44
|
@idle_loop = IDLE_LOOP
|
|
44
45
|
@host_clock_hz = host_clock_hz
|
|
@@ -97,6 +97,7 @@ module Badline
|
|
|
97
97
|
@host_tia = Chips::TIA.new
|
|
98
98
|
@bus = Bus.new(rom: rom || ROM.load("dos1551.rom", 0xc000), port: @port, tia: @tia)
|
|
99
99
|
@cpu = CPU.new(@bus, debug:)
|
|
100
|
+
@cpu.power_on!
|
|
100
101
|
@idle_loop = IDLE_LOOP
|
|
101
102
|
@host_clock_hz = host_clock_hz
|
|
102
103
|
@phase = 0
|
|
@@ -68,6 +68,7 @@ module Badline
|
|
|
68
68
|
@fdc = WD1770.new
|
|
69
69
|
@bus = Bus.new(rom: rom || ROM.load("dos1571.rom", 0x8000), via1: @via1, via2: @via2, cia: @cia, fdc: @fdc)
|
|
70
70
|
@cpu = CPU.new(@bus, debug:)
|
|
71
|
+
@cpu.power_on!
|
|
71
72
|
@fdc.cpu = @cpu
|
|
72
73
|
@idle_loop = IDLE_LOOP
|
|
73
74
|
@host_clock_hz = host_clock_hz
|
|
@@ -87,6 +87,7 @@ module Badline
|
|
|
87
87
|
@cia = Chips::CIA.new(start: 0x4000, peripheral: @serial_port, tod_hz: 0)
|
|
88
88
|
@bus = Bus.new(rom: rom || ROM.load("dos1581.rom", 0x8000), cia: @cia, fdc: @fdc, drive: self)
|
|
89
89
|
@cpu = CPU.new(@bus, debug:)
|
|
90
|
+
@cpu.power_on!
|
|
90
91
|
@idle_loop = IDLE_LOOP
|
|
91
92
|
@host_clock_hz = host_clock_hz
|
|
92
93
|
@phase = 0
|
|
@@ -34,6 +34,12 @@ module Badline
|
|
|
34
34
|
|
|
35
35
|
def model_name = "2031"
|
|
36
36
|
|
|
37
|
+
def boot_cycles = BOOT_CYCLES
|
|
38
|
+
|
|
39
|
+
# Whether the drive is where VICE saves it: its CPU between
|
|
40
|
+
# instructions.
|
|
41
|
+
def settled_for_vice? = cpu.boundary?
|
|
42
|
+
|
|
37
43
|
# +rom+ covers $C000-$FFFF, and defaults to the DOS image in the ROM
|
|
38
44
|
# path. +device+ is the number the jumpers set, 8 to 11.
|
|
39
45
|
def initialize(rom: nil, host_clock_hz: CLOCK_HZ, device: 8, debug: false)
|
|
@@ -46,6 +52,7 @@ module Badline
|
|
|
46
52
|
@via2 = Model1541::DiskVIA.new(start: 0x1c00, mechanism: @mechanism)
|
|
47
53
|
@bus = Bus.new(rom: rom || ROM.load("dos2031.rom", 0xc000), via1: @via1, via2: @via2)
|
|
48
54
|
@cpu = CPU.new(@bus, debug:)
|
|
55
|
+
@cpu.power_on!
|
|
49
56
|
@clock_hz = CLOCK_HZ
|
|
50
57
|
@idle_loop = IDLE_LOOP
|
|
51
58
|
@host_clock_hz = host_clock_hz
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
module Drives
|
|
5
|
+
class Model4040
|
|
6
|
+
# A .d64 image as the 4040's controller finds it on the disk, each
|
|
7
|
+
# track a ring of byte cells as on the 8050 (Model8050::Disk), laid
|
|
8
|
+
# out as DOS 2.1's format writes it, sector after sector:
|
|
9
|
+
#
|
|
10
|
+
# 4 sync, $08, checksum, sector, track, ID 2, ID 1,
|
|
11
|
+
# 9 x $00, 4 sync, $07, 256 data bytes, checksum, then $00
|
|
12
|
+
#
|
|
13
|
+
# nine cells being the header gap the interface processor gives the
|
|
14
|
+
# controller ($049D, from DOS 2.1's SECTRK table), past which a write
|
|
15
|
+
# puts its syncs. A .d67, DOS 1's disk, is laid out as DOS 1 writes a
|
|
16
|
+
# sector: 2 sync, the header, one $08, 2 sync, $07, the data and its
|
|
17
|
+
# checksum. DOS 1's format writes headers only, so this is the track as
|
|
18
|
+
# if every sector had been written. The disk's ID is the BAM's, at
|
|
19
|
+
# 18/0 $A2. A turn passes 80,000 / (16 - zone) cells, the 16 MHz bit
|
|
20
|
+
# clock's.
|
|
21
|
+
class Disk < Model8050::Disk
|
|
22
|
+
# How many tracks a disk holds: 35.
|
|
23
|
+
def side_tracks = 35
|
|
24
|
+
|
|
25
|
+
def sides = 1
|
|
26
|
+
|
|
27
|
+
# The cells a track of the zone holds in one turn at 300 rpm: the
|
|
28
|
+
# 200,000 µs of a turn over 2.5 µs times 16 less the zone a byte.
|
|
29
|
+
def self.cells_in(zone) = 80_000 / (16 - zone)
|
|
30
|
+
|
|
31
|
+
# The zone a track is recorded in, 3 outermost to 0 innermost.
|
|
32
|
+
def self.zone_of(track) = Storage::D64Image.zone(track)
|
|
33
|
+
|
|
34
|
+
private
|
|
35
|
+
|
|
36
|
+
def build(number)
|
|
37
|
+
id1, id2 = disk_id
|
|
38
|
+
cells = []
|
|
39
|
+
sectors = @image.sectors_in(number)
|
|
40
|
+
dos1 = @image.is_a?(Storage::D67Image)
|
|
41
|
+
sectors.times do |sector|
|
|
42
|
+
data = @image.read_block(number, sector)
|
|
43
|
+
header = [0x08, number ^ sector ^ id1 ^ id2, sector, number, id2, id1]
|
|
44
|
+
if dos1
|
|
45
|
+
cells.push(SYNC, SYNC, *header, 0x08, SYNC, SYNC, 0x07)
|
|
46
|
+
else
|
|
47
|
+
cells.push(SYNC, SYNC, SYNC, SYNC, *header, *Array.new(9, 0x00), SYNC, SYNC, SYNC, SYNC, 0x07)
|
|
48
|
+
end
|
|
49
|
+
cells.concat(data)
|
|
50
|
+
cells << checksum(data)
|
|
51
|
+
end
|
|
52
|
+
pad(cells, sectors, self.class.cells_in(self.class.zone_of(number)))
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def disk_id
|
|
56
|
+
bam = @image.read_block(18, 0)
|
|
57
|
+
[bam[0xa2], bam[0xa3]]
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
module Drives
|
|
5
|
+
class Model4040
|
|
6
|
+
# The interface processor's bus (schematic 320806, sheet 1). UB3
|
|
7
|
+
# decodes A12-A14 only, so the map repeats every 32 KB:
|
|
8
|
+
#
|
|
9
|
+
# $0000-$0FFF - the two 6532s, as on the 8050 (Model8050::IPBus)
|
|
10
|
+
# $1000-$4FFF - the common RAM, 1 KB in each block, mirrored through it
|
|
11
|
+
# $5000-$7FFF - the ROM sockets UJ1, UL1 and UH1, seen again at
|
|
12
|
+
# $D000-$FFFF, where the processor finds its vectors
|
|
13
|
+
#
|
|
14
|
+
# A socket left empty, as UJ1 is under DOS 1, reads as open bus: the
|
|
15
|
+
# last byte on the data bus.
|
|
16
|
+
class IPBus < Model8050::IPBus
|
|
17
|
+
def peek(addr)
|
|
18
|
+
addr &= 0x7fff
|
|
19
|
+
return @data = rom_peek(addr | 0x8000) if addr >= 0x5000
|
|
20
|
+
|
|
21
|
+
@data = if addr < 0x1000
|
|
22
|
+
peek_riots(addr)
|
|
23
|
+
else
|
|
24
|
+
@common.peek((((addr >> 12) - 1) << 10) | (addr & 0x03ff))
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def poke(addr, value)
|
|
29
|
+
@data = value
|
|
30
|
+
addr &= 0x7fff
|
|
31
|
+
return if addr >= 0x5000
|
|
32
|
+
|
|
33
|
+
if addr < 0x1000
|
|
34
|
+
poke_riots(addr, value)
|
|
35
|
+
else
|
|
36
|
+
poke_common((((addr >> 12) - 1) << 10) | (addr & 0x03ff), value)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
private
|
|
41
|
+
|
|
42
|
+
def rom_peek(addr) = addr >= @rom.start ? @rom.peek(addr) : @data
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|