badline 0.16.1 → 0.18.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 +213 -0
- data/CLAUDE.md +11 -7
- data/CONTRIBUTING.md +7 -1
- data/README.md +265 -26
- data/doc/media.md +5 -2
- data/doc/pinned-behaviour.md +189 -0
- data/doc/snapshot-format.md +616 -74
- data/doc/snapshots.md +192 -24
- data/exe/badline-ruby +5 -0
- data/lib/badline/audio/terminal.rb +34 -2
- data/lib/badline/c128/keyed_state.rb +2 -0
- data/lib/badline/c128.rb +1 -0
- data/lib/badline/c64/keyed_state.rb +2 -0
- data/lib/badline/c64.rb +1 -0
- data/lib/badline/cbm2/acia.rb +10 -0
- data/lib/badline/cbm2/bus.rb +263 -0
- data/lib/badline/cbm2/cpu.rb +58 -0
- data/lib/badline/cbm2/crtc_video.rb +133 -0
- data/lib/badline/cbm2/ieee_drive.rb +18 -0
- data/lib/badline/cbm2/ieee_layout.rb +69 -0
- data/lib/badline/cbm2/ieee_ports.rb +104 -0
- data/lib/badline/cbm2/ieee_traps.rb +81 -0
- data/lib/badline/cbm2/keyboard_ports.rb +60 -0
- data/lib/badline/cbm2/keyed_modules.rb +67 -0
- data/lib/badline/cbm2/keyed_state.rb +95 -0
- data/lib/badline/cbm2/model.rb +44 -0
- data/lib/badline/cbm2/p500/control_ports.rb +80 -0
- data/lib/badline/cbm2/p500/ieee_layout.rb +41 -0
- data/lib/badline/cbm2/p500/keyed_state.rb +85 -0
- data/lib/badline/cbm2/p500/model.rb +39 -0
- data/lib/badline/cbm2/p500/pages.rb +57 -0
- data/lib/badline/cbm2/p500.rb +264 -0
- data/lib/badline/cbm2/sid_page.rb +19 -0
- data/lib/badline/cbm2.rb +270 -0
- data/lib/badline/{plus4 → chips}/acia/frames.rb +1 -1
- data/lib/badline/chips/acia/keyed_state.rb +47 -0
- data/lib/badline/chips/acia.rb +307 -0
- data/lib/badline/chips/cia/timer.rb +2 -2
- data/lib/badline/chips/cpu/instruction_set.rb +1 -1
- data/lib/badline/chips/crtc.rb +3 -2
- 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/fast_forward.rb +72 -0
- data/lib/badline/chips/riot/keyed_parts.rb +69 -0
- data/lib/badline/chips/riot/timer.rb +91 -0
- data/lib/badline/chips/riot.rb +162 -0
- data/lib/badline/chips/rriot/keyed_state.rb +30 -0
- data/lib/badline/chips/rriot.rb +134 -0
- data/lib/badline/chips/sid/filter.rb +4 -4
- data/lib/badline/chips/sid/waveform.rb +6 -6
- data/lib/badline/chips/tpi/keyed_state.rb +38 -0
- data/lib/badline/chips/tpi.rb +233 -0
- data/lib/badline/chips/vic/bank.rb +60 -5
- data/lib/badline/chips/vic/sprite.rb +4 -3
- data/lib/badline/chips/vic_i/keyed_parts.rb +7 -2
- data/lib/badline/chips/vic_i.rb +84 -21
- data/lib/badline/data_path.rb +3 -3
- data/lib/badline/drives/datasette/keyed_state.rb +9 -1
- data/lib/badline/drives/datasette.rb +27 -3
- data/lib/badline/drives/ieee_models.rb +54 -0
- data/lib/badline/drives/model1001.rb +37 -0
- data/lib/badline/drives/model2031.rb +6 -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 +192 -0
- data/lib/badline/drives/model4040/parts.rb +20 -0
- data/lib/badline/drives/model4040.rb +179 -0
- data/lib/badline/drives/model8050/board.rb +77 -0
- data/lib/badline/drives/model8050/disk.rb +163 -0
- data/lib/badline/drives/model8050/dos_board.rb +91 -0
- data/lib/badline/drives/model8050/fdc_bus.rb +102 -0
- data/lib/badline/drives/model8050/idle.rb +181 -0
- data/lib/badline/drives/model8050/ieee_port.rb +147 -0
- data/lib/badline/drives/model8050/ip_bus.rb +93 -0
- data/lib/badline/drives/model8050/keyed_state.rb +163 -0
- data/lib/badline/drives/model8050/mechanism.rb +230 -0
- data/lib/badline/drives/model8050/parts.rb +37 -0
- data/lib/badline/drives/model8050.rb +131 -0
- data/lib/badline/drives/model9060/idle.rb +149 -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 +195 -0
- data/lib/badline/frontend/app.rb +18 -3
- data/lib/badline/frontend/boot.rb +4 -3
- data/lib/badline/frontend/controls.rb +107 -15
- data/lib/badline/frontend/gamepads.rb +3 -3
- data/lib/badline/frontend/keys.rb +97 -2
- 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 +47 -14
- data/lib/badline/frontend/menu/menu_pages.rb +113 -4
- data/lib/badline/frontend/menu/menu_ports.rb +77 -4
- data/lib/badline/frontend/menu/pause_menu.rb +6 -2
- data/lib/badline/frontend/pet_keyboard.rb +45 -7
- 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 +49 -11
- data/lib/badline/frontend.rb +3 -0
- data/lib/badline/kernal_trap/dos/disk_commands.rb +80 -0
- data/lib/badline/kernal_trap/dos/status.rb +3 -0
- data/lib/badline/kernal_trap/dos.rb +18 -5
- data/lib/badline/kernal_trap/ieee.rb +4 -2
- data/lib/badline/kernal_trap/ieee_layout.rb +95 -43
- 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 +116 -0
- data/lib/badline/kim1/teletype.rb +193 -0
- data/lib/badline/kim1.rb +190 -0
- data/lib/badline/machine/ieee_drives.rb +171 -0
- data/lib/badline/machine/keyboard_buffer.rb +20 -3
- data/lib/badline/machine/line_sound.rb +122 -0
- data/lib/badline/machine/settling.rb +4 -0
- data/lib/badline/machine.rb +62 -14
- data/lib/badline/media/cbm2_media.rb +29 -0
- 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/program.rb +6 -3
- data/lib/badline/media/true_drive.rb +51 -25
- data/lib/badline/media.rb +23 -8
- data/lib/badline/options/family.rb +108 -6
- data/lib/badline/options/help.rb +58 -12
- data/lib/badline/options/table.rb +60 -14
- data/lib/badline/options/validation.rb +71 -11
- data/lib/badline/options.rb +46 -13
- data/lib/badline/pet/bus.rb +16 -2
- data/lib/badline/pet/cassettes.rb +22 -1
- data/lib/badline/pet/ieee_drive.rb +5 -64
- data/lib/badline/pet/ieee_ports.rb +7 -0
- 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 +81 -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 +216 -0
- data/lib/badline/pet.rb +33 -6
- data/lib/badline/plus4/acia.rb +4 -298
- data/lib/badline/plus4/keyed_parts.rb +0 -44
- data/lib/badline/plus4/keyed_state.rb +2 -0
- data/lib/badline/plus4.rb +2 -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/8050/dos-2.7.rom +0 -0
- data/lib/badline/roms/8050/fdc-901869-01.rom +0 -0
- data/lib/badline/roms/8050/fdc-901885-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 +277 -0
- data/lib/badline/roms/cbm2/basic128-02b.rom +0 -0
- data/lib/badline/roms/cbm2/basic128.rom +0 -0
- data/lib/badline/roms/cbm2/basic256.rom +0 -0
- data/lib/badline/roms/cbm2/characters-600.rom +0 -0
- data/lib/badline/roms/cbm2/characters-700.rom +0 -0
- data/lib/badline/roms/cbm2/kernal-03b.rom +0 -0
- data/lib/badline/roms/cbm2/kernal-04a.rom +0 -0
- data/lib/badline/roms/cbm2/p500-basic.rom +0 -0
- data/lib/badline/roms/cbm2/p500-kernal.rom +0 -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/cbm2_setup.rb +53 -0
- data/lib/badline/snapshot/container.rb +9 -2
- data/lib/badline/snapshot/field_doc/markdown.rb +4 -2
- data/lib/badline/snapshot/field_doc.rb +59 -3
- data/lib/badline/snapshot/image.rb +65 -9
- 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/cbm2.rb +93 -0
- data/lib/badline/snapshot/vice/cbm2_acia.rb +75 -0
- data/lib/badline/snapshot/vice/cbm2_crtc.rb +127 -0
- data/lib/badline/snapshot/vice/cbm2_mains.rb +49 -0
- data/lib/badline/snapshot/vice/cbm2_mem.rb +75 -0
- data/lib/badline/snapshot/vice/cbm2_restore.rb +204 -0
- data/lib/badline/snapshot/vice/cbm2_tpi.rb +58 -0
- data/lib/badline/snapshot/vice/cias.rb +13 -9
- data/lib/badline/snapshot/vice/p500.rb +96 -0
- data/lib/badline/snapshot/vice/p500_restore.rb +130 -0
- data/lib/badline/snapshot/vice/p500_vicii.rb +166 -0
- data/lib/badline/snapshot/vice/pet.rb +20 -10
- data/lib/badline/snapshot/vice/pet_cpu.rb +42 -5
- data/lib/badline/snapshot/vice/pet_crtc.rb +25 -17
- data/lib/badline/snapshot/vice/pet_drive.rb +180 -0
- data/lib/badline/snapshot/vice/pet_drive8050.rb +336 -0
- data/lib/badline/snapshot/vice/pet_mem.rb +49 -5
- data/lib/badline/snapshot/vice/pet_restore.rb +100 -7
- data/lib/badline/snapshot/vice/pet_via.rb +6 -5
- data/lib/badline/snapshot/vice/sid_registers.rb +2 -1
- data/lib/badline/snapshot/vice/vicii_import.rb +10 -6
- data/lib/badline/snapshot/vice.rb +3 -0
- data/lib/badline/snapshot.rb +15 -3
- data/lib/badline/status.rb +30 -30
- data/lib/badline/storage/crt_file.rb +7 -1
- data/lib/badline/storage/d64_image.rb +7 -0
- data/lib/badline/storage/d67_image.rb +55 -0
- data/lib/badline/storage/d80_image.rb +17 -1
- data/lib/badline/storage/d81_image.rb +14 -0
- data/lib/badline/storage/d90_image.rb +180 -0
- data/lib/badline/storage/disk_image/bam.rb +18 -3
- data/lib/badline/storage/disk_image/formatting.rb +69 -0
- data/lib/badline/storage/disk_image/writing.rb +37 -2
- data/lib/badline/storage/disk_image.rb +2 -0
- data/lib/badline/storage/kim_tape.rb +254 -0
- data/lib/badline/storage/papertape.rb +90 -0
- data/lib/badline/storage.rb +5 -0
- data/lib/badline/support/cbm2_checkpoint.rb +48 -0
- data/lib/badline/support/kim1_checkpoint.rb +41 -0
- data/lib/badline/support/p500_checkpoint.rb +35 -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.rb +26 -12
- data/lib/badline.rb +2 -0
- metadata +148 -2
|
@@ -54,7 +54,9 @@ module Badline
|
|
|
54
54
|
address_device if @listening
|
|
55
55
|
end
|
|
56
56
|
|
|
57
|
-
|
|
57
|
+
# The device TALK or LISTEN addresses: the one in A or in FA, as the
|
|
58
|
+
# layout says.
|
|
59
|
+
def addressed? = (@layout.device_in_a ? @cpu.a : @bus.peek(zero_page.device)) == @device
|
|
58
60
|
|
|
59
61
|
# TALK and LISTEN end with the command sent under ATN, which stays
|
|
60
62
|
# low, and A holding $FF.
|
|
@@ -116,7 +118,7 @@ module Badline
|
|
|
116
118
|
def acptr
|
|
117
119
|
return unless @talking
|
|
118
120
|
|
|
119
|
-
@layout.
|
|
121
|
+
@layout.byte_received(@bus)
|
|
120
122
|
byte, status = received
|
|
121
123
|
time_serial_byte(Routine::ACPTR_TIMEOUT)
|
|
122
124
|
@cpu.a = byte
|
|
@@ -2,70 +2,122 @@
|
|
|
2
2
|
|
|
3
3
|
module Badline
|
|
4
4
|
module KernalTrap
|
|
5
|
-
#
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
5
|
+
# The +keep+ of an IEEELineStep that only reads its register.
|
|
6
|
+
IEEE_LINE_READ = -1
|
|
7
|
+
|
|
8
|
+
# One step a KERNAL takes on the IEEE-488 bus's lines: a write of +set+
|
|
9
|
+
# to the register at +address+, or with a +keep+ mask the register read
|
|
10
|
+
# first, its +keep+ bits kept and +set+ ORed in, or with keep
|
|
11
|
+
# IEEE_LINE_READ only a read. Returns the value written, or read.
|
|
12
|
+
IEEELineStep = Data.define(:address, :keep, :set) do
|
|
13
|
+
def act_on(bus)
|
|
14
|
+
return bus.peek(address) if keep == IEEE_LINE_READ
|
|
15
|
+
|
|
16
|
+
value = keep.zero? ? set : (bus.peek(address) & keep) | set
|
|
17
|
+
bus.poke(address, value)
|
|
18
|
+
value
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# The steps a KERNAL takes on the bus's lines at each moment the traps
|
|
23
|
+
# stand in for, each run in order (#run_steps), the last one's value
|
|
24
|
+
# returned: +timer+ starts the timer that times out each byte's
|
|
25
|
+
# handshake, +attention+ readies the bus and pulls ATN low for TALK
|
|
26
|
+
# and LISTEN, +byte_sent+ ends a byte sent, +release_attention+ ends
|
|
27
|
+
# SECOND, TKSA, UNTALK and UNLISTEN by letting ATN go, +listen_to_talker+
|
|
28
|
+
# ends TKSA with the computer listening, +byte_received+ ends ACPTR,
|
|
29
|
+
# and +release+ ends UNTALK and UNLISTEN.
|
|
30
|
+
IEEELines = Data.define(:timer, :attention, :byte_sent, :release_attention, :listen_to_talker, :byte_received,
|
|
31
|
+
:release) do
|
|
32
|
+
def run_steps(steps, bus)
|
|
33
|
+
value = 0
|
|
34
|
+
steps.each { |step| value = step.act_on(bus) }
|
|
35
|
+
value
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Where a KERNAL keeps the eight IEEE-488 bus primitives, which are
|
|
40
|
+
# internal routines, not jump table entries, the ZeroPage of its file
|
|
41
|
+
# variables, and the steps it takes on the bus's lines (IEEELines).
|
|
42
|
+
# Each ROM set has its own.
|
|
16
43
|
#
|
|
17
|
-
#
|
|
18
|
-
#
|
|
19
|
-
#
|
|
20
|
-
#
|
|
21
|
-
#
|
|
22
|
-
#
|
|
44
|
+
# On a PET, TALK and LISTEN take no device number in A: they send $40
|
|
45
|
+
# or $20 ORed with FA. UNTALK and UNLISTEN send $5F and $3F through the
|
|
46
|
+
# same code, and SECOND, TKSA, UNTALK and UNLISTEN end by letting go of
|
|
47
|
+
# ATN themselves. LOAD and SAVE run in the ROM, through the primitives.
|
|
48
|
+
# +overwrites_device+ says UNTALK and UNLISTEN store their command in
|
|
49
|
+
# FA before they send it. +device_in_a+ says TALK and LISTEN take the
|
|
50
|
+
# device in A, as the CBM-II's do, where a PET's ignore A and read FA.
|
|
23
51
|
IEEELayout = Data.define(:talk, :listen, :second, :tksa, :ciout, :untalk, :unlisten, :acptr, :overwrites_device,
|
|
24
|
-
:zero_page) do
|
|
25
|
-
# The KERNAL is mapped unless
|
|
52
|
+
:device_in_a, :zero_page, :lines) do
|
|
53
|
+
# The KERNAL is mapped unless something lies over it.
|
|
26
54
|
def kernal_mapped?(bus) = bus.kernal_mapped?
|
|
27
55
|
|
|
28
|
-
# Starts
|
|
56
|
+
# Starts the timer that times out the handshake of each byte.
|
|
29
57
|
def time_serial_byte(bus, _timer_high)
|
|
30
|
-
|
|
58
|
+
lines.run_steps(lines.timer, bus)
|
|
31
59
|
end
|
|
32
60
|
|
|
33
|
-
# TALK and LISTEN:
|
|
61
|
+
# TALK and LISTEN: the handshake lines let go, then ATN pulled low.
|
|
34
62
|
def attention(bus)
|
|
35
|
-
|
|
36
|
-
bus.poke(0xe821, 0x3c)
|
|
37
|
-
bus.poke(0xe840, bus.peek(0xe840) & 0xfb)
|
|
63
|
+
lines.run_steps(lines.attention, bus)
|
|
38
64
|
end
|
|
39
65
|
|
|
40
66
|
# The end of a byte sent: DAV and the data lines let go.
|
|
41
67
|
def byte_sent(bus)
|
|
42
|
-
|
|
43
|
-
bus.poke(0xe822, 0xff)
|
|
68
|
+
lines.run_steps(lines.byte_sent, bus)
|
|
44
69
|
end
|
|
45
70
|
|
|
46
71
|
# The end of SECOND, TKSA, UNTALK and UNLISTEN: ATN let go. Returns
|
|
47
72
|
# the value written, which the ROM leaves in A.
|
|
48
73
|
def release_attention(bus)
|
|
49
|
-
|
|
50
|
-
bus.poke(0xe840, value)
|
|
51
|
-
value
|
|
74
|
+
lines.run_steps(lines.release_attention, bus)
|
|
52
75
|
end
|
|
53
76
|
|
|
54
|
-
# TKSA
|
|
77
|
+
# TKSA ends listening: NRFD and NDAC pulled low.
|
|
55
78
|
def listen_to_talker(bus)
|
|
56
|
-
|
|
57
|
-
|
|
79
|
+
lines.run_steps(lines.listen_to_talker, bus)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# ACPTR ends listening as well.
|
|
83
|
+
def byte_received(bus)
|
|
84
|
+
lines.run_steps(lines.byte_received, bus)
|
|
58
85
|
end
|
|
59
86
|
|
|
60
87
|
# The end of UNTALK and UNLISTEN, which address the bus as TALK and
|
|
61
|
-
# LISTEN do, send their command and let ATN go.
|
|
88
|
+
# LISTEN do, send their command and let ATN go. Returns the value
|
|
89
|
+
# the ROM leaves in A.
|
|
62
90
|
def release_serial_lines(bus)
|
|
63
|
-
|
|
64
|
-
byte_sent(bus)
|
|
65
|
-
release_attention(bus)
|
|
91
|
+
lines.run_steps(lines.release, bus)
|
|
66
92
|
end
|
|
67
93
|
end
|
|
68
94
|
|
|
95
|
+
# A PET drives the bus's control lines from three chips: ATN from VIA
|
|
96
|
+
# PB2 and NRFD from PB1 at $E840, NDAC from PIA 2's CA2 and DAV from its
|
|
97
|
+
# CB2, through the control registers at $E821 and $E823, which the ROM
|
|
98
|
+
# writes $34 to pull a line low and $3C to let it go, and the data lines
|
|
99
|
+
# from PIA 2's port B at $E822. Each handshake times out on VIA timer 1,
|
|
100
|
+
# which the ROM starts with a write of $FF to $E845. The steps leave the
|
|
101
|
+
# lines as each primitive leaves them.
|
|
102
|
+
PET_ATTENTION = [
|
|
103
|
+
IEEELineStep.new(address: 0xe840, keep: 0xff, set: 0x02),
|
|
104
|
+
IEEELineStep.new(address: 0xe821, keep: 0, set: 0x3c),
|
|
105
|
+
IEEELineStep.new(address: 0xe840, keep: 0xfb, set: 0)
|
|
106
|
+
].freeze
|
|
107
|
+
PET_BYTE_SENT = [
|
|
108
|
+
IEEELineStep.new(address: 0xe823, keep: 0, set: 0x3c),
|
|
109
|
+
IEEELineStep.new(address: 0xe822, keep: 0, set: 0xff)
|
|
110
|
+
].freeze
|
|
111
|
+
PET_RELEASE_ATTENTION = [IEEELineStep.new(address: 0xe840, keep: 0xff, set: 0x04)].freeze
|
|
112
|
+
PET_LISTEN = [IEEELineStep.new(address: 0xe840, keep: 0xfd, set: 0),
|
|
113
|
+
IEEELineStep.new(address: 0xe821, keep: 0, set: 0x34)].freeze
|
|
114
|
+
PET_LINES = IEEELines.new(
|
|
115
|
+
timer: [IEEELineStep.new(address: 0xe845, keep: 0, set: 0xff)].freeze,
|
|
116
|
+
attention: PET_ATTENTION, byte_sent: PET_BYTE_SENT, release_attention: PET_RELEASE_ATTENTION,
|
|
117
|
+
listen_to_talker: PET_LISTEN, byte_received: PET_LISTEN,
|
|
118
|
+
release: (PET_ATTENTION + PET_BYTE_SENT + PET_RELEASE_ATTENTION).freeze
|
|
119
|
+
)
|
|
120
|
+
|
|
69
121
|
# The file variables of the BASIC 2 and BASIC 4 KERNALs, which keep
|
|
70
122
|
# them at the same addresses. SAVE copies STAL at $FB to SAL at $C7 and
|
|
71
123
|
# walks SAL up to EAL at $C9. LOAD keeps no MEMUSS: it stores each byte
|
|
@@ -90,24 +142,24 @@ module Badline
|
|
|
90
142
|
# LISTEN without a secondary address.
|
|
91
143
|
PET_BASIC1_LAYOUT = IEEELayout.new(
|
|
92
144
|
talk: 0xf0b6, listen: 0xf0ba, second: 0xf12c, tksa: 0xf15b,
|
|
93
|
-
ciout: 0xf167, untalk: 0xf17a, unlisten: 0xf17e, acptr: 0xf187, overwrites_device: true,
|
|
94
|
-
zero_page: PET_BASIC1_ZERO_PAGE
|
|
145
|
+
ciout: 0xf167, untalk: 0xf17a, unlisten: 0xf17e, acptr: 0xf187, overwrites_device: true, device_in_a: false,
|
|
146
|
+
zero_page: PET_BASIC1_ZERO_PAGE, lines: PET_LINES
|
|
95
147
|
)
|
|
96
148
|
|
|
97
149
|
# BASIC 2, 901465-03. Its LOAD sends the secondary address after TALK
|
|
98
150
|
# with SECOND, not TKSA.
|
|
99
151
|
PET_BASIC2_LAYOUT = IEEELayout.new(
|
|
100
152
|
talk: 0xf0b6, listen: 0xf0ba, second: 0xf128, tksa: 0xf164,
|
|
101
|
-
ciout: 0xf16f, untalk: 0xf17f, unlisten: 0xf183, acptr: 0xf18c, overwrites_device: false,
|
|
102
|
-
zero_page: PET_ZERO_PAGE
|
|
153
|
+
ciout: 0xf16f, untalk: 0xf17f, unlisten: 0xf183, acptr: 0xf18c, overwrites_device: false, device_in_a: false,
|
|
154
|
+
zero_page: PET_ZERO_PAGE, lines: PET_LINES
|
|
103
155
|
)
|
|
104
156
|
|
|
105
157
|
# BASIC 4, 901465-22. LISTEN at $F0D5 is the operand of the BIT
|
|
106
158
|
# instruction that skips it on the way from TALK.
|
|
107
159
|
PET_BASIC4_LAYOUT = IEEELayout.new(
|
|
108
160
|
talk: 0xf0d2, listen: 0xf0d5, second: 0xf143, tksa: 0xf193,
|
|
109
|
-
ciout: 0xf19e, untalk: 0xf1ae, unlisten: 0xf1b9, acptr: 0xf1c0, overwrites_device: false,
|
|
110
|
-
zero_page: PET_ZERO_PAGE
|
|
161
|
+
ciout: 0xf19e, untalk: 0xf1ae, unlisten: 0xf1b9, acptr: 0xf1c0, overwrites_device: false, device_in_a: false,
|
|
162
|
+
zero_page: PET_ZERO_PAGE, lines: PET_LINES
|
|
111
163
|
)
|
|
112
164
|
end
|
|
113
165
|
end
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
class KIM1
|
|
5
|
+
# The CPU's bus. The board decodes A10-A12 into eight 1K blocks and
|
|
6
|
+
# leaves A13-A15 alone, so the 8K repeats through all 64K, and the
|
|
7
|
+
# vectors at $FFFA-$FFFF are the monitor's at $1FFA-$1FFF (User Manual,
|
|
8
|
+
# figure 3.12 and section 6.2):
|
|
9
|
+
#
|
|
10
|
+
# $0000-$03FF - the 1K of RAM
|
|
11
|
+
# $0400-$16FF - nothing
|
|
12
|
+
# $1700-$173F - U3's ports and timer, its 16 registers repeated
|
|
13
|
+
# $1740-$177F - U2's
|
|
14
|
+
# $1780-$17BF - U3's 64 bytes of RAM
|
|
15
|
+
# $17C0-$17FF - U2's
|
|
16
|
+
# $1800-$1BFF - U3's ROM, the tape routines
|
|
17
|
+
# $1C00-$1FFF - U2's ROM, the monitor
|
|
18
|
+
#
|
|
19
|
+
# A read from nothing finds the last byte on the bus. Writes to ROM
|
|
20
|
+
# and to nothing go nowhere.
|
|
21
|
+
#
|
|
22
|
+
# The board takes more RAM, as RAM_CONFIGURATIONS names it: the stock
|
|
23
|
+
# 1K, 5K with a board in K1-K4 filling $0400-$13FF, or those 5K and
|
|
24
|
+
# RAM at $2000-$DFFF, with an expansion that decodes A13-A15 and
|
|
25
|
+
# repeats the first 8K only at $E000-$FFFF, so the vectors are still
|
|
26
|
+
# the monitor's (User Manual, sections 6.1 and 6.2).
|
|
27
|
+
class Bus
|
|
28
|
+
include Addressable
|
|
29
|
+
|
|
30
|
+
RAM_CONFIGURATIONS = %i[1k 5k expanded].freeze
|
|
31
|
+
|
|
32
|
+
# The RAM, and the one of RAM_CONFIGURATIONS it comes as.
|
|
33
|
+
attr_reader :ram, :ram_configuration
|
|
34
|
+
|
|
35
|
+
# The last byte the CPU read or wrote.
|
|
36
|
+
attr_reader :data
|
|
37
|
+
|
|
38
|
+
# +monitor+ is U2, the 6530-002, and +user+ U3, the 6530-003, with
|
|
39
|
+
# the RAM `ram` names.
|
|
40
|
+
def initialize(monitor, user, ram: :"1k")
|
|
41
|
+
addressable_at(0, length: 2**16)
|
|
42
|
+
raise ArgumentError, "no KIM-1 RAM configuration named #{ram}" unless RAM_CONFIGURATIONS.include?(ram)
|
|
43
|
+
|
|
44
|
+
@u2 = monitor
|
|
45
|
+
@u3 = user
|
|
46
|
+
@ram_configuration = ram
|
|
47
|
+
# The end of RAM in the first 8K, and where the first 8K stops
|
|
48
|
+
# repeating, past the RAM above it.
|
|
49
|
+
@low_end = ram == :"1k" ? 0x0400 : 0x1400
|
|
50
|
+
@mirror_end = ram == :expanded ? 0xe000 : 0x2000
|
|
51
|
+
@ram = Memory.new([], length: @mirror_end == 0x2000 ? @low_end : @mirror_end, start: 0)
|
|
52
|
+
@data = 0
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# The RAM in K, without the 6530s' 64 bytes each.
|
|
56
|
+
def ram_kb = (@low_end + @mirror_end - 0x2000) >> 10
|
|
57
|
+
|
|
58
|
+
# Whether RAM answers at `addr`, the board's or a 6530's.
|
|
59
|
+
def ram?(addr)
|
|
60
|
+
offset = addr < 0x2000 || addr >= @mirror_end ? addr & 0x1fff : addr
|
|
61
|
+
offset < @low_end || offset >= 0x2000 || offset.between?(0x1780, 0x17ff)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def peek(addr)
|
|
65
|
+
offset = addr < 0x2000 || addr >= @mirror_end ? addr & 0x1fff : addr
|
|
66
|
+
@data = if offset < @low_end || offset >= 0x2000
|
|
67
|
+
@ram.peek(offset)
|
|
68
|
+
elsif offset >= 0x1c00
|
|
69
|
+
@u2.read_rom(offset)
|
|
70
|
+
elsif offset >= 0x1800
|
|
71
|
+
@u3.read_rom(offset)
|
|
72
|
+
elsif (offset >> 8) == 0x17
|
|
73
|
+
read_chips(offset)
|
|
74
|
+
else
|
|
75
|
+
@data
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def poke(addr, value)
|
|
80
|
+
@data = value
|
|
81
|
+
offset = addr < 0x2000 || addr >= @mirror_end ? addr & 0x1fff : addr
|
|
82
|
+
if offset < @low_end || offset >= 0x2000
|
|
83
|
+
@ram.poke(offset, value)
|
|
84
|
+
elsif (offset >> 8) == 0x17
|
|
85
|
+
write_chips(offset, value)
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def power_on!
|
|
90
|
+
@ram.clear!
|
|
91
|
+
@u2.ram.map! { 0 }
|
|
92
|
+
@u3.ram.map! { 0 }
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
private
|
|
96
|
+
|
|
97
|
+
# A6 picks U2 over U3, and A7 the RAM over the registers.
|
|
98
|
+
def read_chips(offset)
|
|
99
|
+
ram = offset.anybits?(0x80)
|
|
100
|
+
if offset.anybits?(0x40)
|
|
101
|
+
ram ? @u2.read_ram(offset) : @u2.peek(offset & 0x0f)
|
|
102
|
+
else
|
|
103
|
+
ram ? @u3.read_ram(offset) : @u3.peek(offset & 0x0f)
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def write_chips(offset, value)
|
|
108
|
+
ram = offset.anybits?(0x80)
|
|
109
|
+
if offset.anybits?(0x40)
|
|
110
|
+
ram ? @u2.write_ram(offset, value) : @u2.poke(offset & 0x0f, value)
|
|
111
|
+
else
|
|
112
|
+
ram ? @u3.write_ram(offset, value) : @u3.poke(offset & 0x0f, value)
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
class KIM1
|
|
5
|
+
# The KIM-1's 6502, U1, on the KIM1::Bus.
|
|
6
|
+
#
|
|
7
|
+
# With the SST switch on, the board's U26 gate pulls NMI low through
|
|
8
|
+
# each opcode fetch outside K7, $1C00-$1FFF and its mirrors, where the
|
|
9
|
+
# monitor runs. The CPU takes the NMI at the end of the instruction
|
|
10
|
+
# that fetch starts, a 2-cycle one too, so the monitor steps a program
|
|
11
|
+
# one instruction at a time (User Manual, figure 5.6).
|
|
12
|
+
class CPU
|
|
13
|
+
include Badline::Chips::CPU::Core
|
|
14
|
+
|
|
15
|
+
# The K7 block's address lines, A10-A12, which the board decodes.
|
|
16
|
+
K7 = 0x1c00
|
|
17
|
+
|
|
18
|
+
# Pulls NMI if the next cycle fetches an opcode outside K7. The
|
|
19
|
+
# board calls it ahead of each cycle while SST is on.
|
|
20
|
+
def single_step
|
|
21
|
+
@nmi = true if boundary? && (@program_counter & K7) != K7
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
class KIM1
|
|
5
|
+
# The KIM-1's teletype on the host's terminal, for `badline kim1
|
|
6
|
+
# --headless`: what the terminal types goes down the line, and what
|
|
7
|
+
# the teletype prints comes out on it, without the NULs and RUBOUTs a
|
|
8
|
+
# terminal doesn't show. The machine runs at its own 1 MHz against the
|
|
9
|
+
# host's clock, catching up at most CATCH_UP seconds at a time, until
|
|
10
|
+
# Ctrl-C.
|
|
11
|
+
#
|
|
12
|
+
# `console` answers #typing, #read_text and #print_text, as
|
|
13
|
+
# Audio::Terminal does.
|
|
14
|
+
class HostTerminal
|
|
15
|
+
SLICE = 0.01
|
|
16
|
+
CATCH_UP = 0.1
|
|
17
|
+
|
|
18
|
+
def self.run(machine, console) = new(machine, console).run
|
|
19
|
+
|
|
20
|
+
# Builds the KIM-1 the command line's `options` name, loads the
|
|
21
|
+
# program they name (Media::KIM1Media), and runs it on `console`
|
|
22
|
+
# until Ctrl-C. Returns the exit status, 1 when the program won't
|
|
23
|
+
# load, which it warns about.
|
|
24
|
+
def self.start(options, console)
|
|
25
|
+
path = options.media_path
|
|
26
|
+
machine = Machine.for_options(options, path)
|
|
27
|
+
unless path.nil?
|
|
28
|
+
loaded = Media.attach(machine, path, autostart: true, kim1: Media::KIM1Media.loading(options))
|
|
29
|
+
console.print_text("#{loaded}\r\n")
|
|
30
|
+
end
|
|
31
|
+
run(machine, console)
|
|
32
|
+
rescue Machine::Unsupported, Storage::Papertape::FormatError, SystemCallError => e
|
|
33
|
+
warn "#{options.program}: #{path}: #{e.message}"
|
|
34
|
+
1
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def initialize(machine, console)
|
|
38
|
+
@machine = machine
|
|
39
|
+
@console = console
|
|
40
|
+
@printed = 0
|
|
41
|
+
@started = 0.0
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Runs until Ctrl-C, and returns the exit status.
|
|
45
|
+
def run
|
|
46
|
+
@started = now
|
|
47
|
+
@console.print_text("KIM-1 teletype at #{@machine.teletype.baud} baud. Ctrl-C: Quit\r\n")
|
|
48
|
+
@console.typing { loop { step } }
|
|
49
|
+
rescue Interrupt
|
|
50
|
+
0
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
private
|
|
54
|
+
|
|
55
|
+
def step
|
|
56
|
+
text = @console.read_text(SLICE)
|
|
57
|
+
@machine.teletype.type(text) unless text.empty?
|
|
58
|
+
behind = ((now - @started) * 1_000_000).to_i - @machine.cycles
|
|
59
|
+
@machine.run_cycles([behind, (CATCH_UP * 1_000_000).to_i].min) if behind.positive?
|
|
60
|
+
print_output
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def print_output
|
|
64
|
+
output = @machine.teletype.output
|
|
65
|
+
return if output.size == @printed
|
|
66
|
+
|
|
67
|
+
@console.print_text(output[@printed, output.size - @printed].gsub("\x00", "").gsub("\x7f", ""))
|
|
68
|
+
@printed = output.size
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def now = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
class KIM1
|
|
5
|
+
# Pressing the panel's keys in turn, each long enough for the monitor
|
|
6
|
+
# to take it. The monitor takes a key it reads down on two scans in a
|
|
7
|
+
# row, after a scan with no key down, and a scan takes some 4,300
|
|
8
|
+
# cycles, so each key is held for HOLD_CYCLES and let go for
|
|
9
|
+
# RELEASE_CYCLES.
|
|
10
|
+
module KeyEntry
|
|
11
|
+
HOLD_CYCLES = 12_000
|
|
12
|
+
RELEASE_CYCLES = 8_000
|
|
13
|
+
|
|
14
|
+
# The panel's keys that type_keys takes by name. Any other word is a
|
|
15
|
+
# run of hex keys.
|
|
16
|
+
NAMED_KEYS = %w[AD DA GO PC + ST RS].freeze
|
|
17
|
+
|
|
18
|
+
# Presses the keys `text` names once the monitor is running: AD, DA,
|
|
19
|
+
# GO, PC, + and the hex digits, ST, which pulls NMI, and RS, which
|
|
20
|
+
# resets the machine. Words go by whitespace, and a word that isn't a
|
|
21
|
+
# key's name is a hex key for each of its characters, so
|
|
22
|
+
# "AD 0200 DA A9 +" presses nine keys, and the byte $AD goes in as
|
|
23
|
+
# "A D".
|
|
24
|
+
def type_keys(text)
|
|
25
|
+
keys = text.upcase.split.flat_map { |word| NAMED_KEYS.include?(word) ? [word] : word.chars }
|
|
26
|
+
unknown = keys.find { |key| !NAMED_KEYS.include?(key) && !Panel::KEYS.include?(key) }
|
|
27
|
+
raise ArgumentError, "no KIM-1 key named #{unknown}" unless unknown.nil?
|
|
28
|
+
|
|
29
|
+
on_init { queue_keys(keys) }
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Whether keys type_keys was given are still to be pressed.
|
|
33
|
+
def typing? = !@pending_keys.nil?
|
|
34
|
+
|
|
35
|
+
private
|
|
36
|
+
|
|
37
|
+
def queue_keys(keys)
|
|
38
|
+
@pending_keys = keys
|
|
39
|
+
@key_down = false
|
|
40
|
+
@next_key_at = @cycles
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def feed_keys
|
|
44
|
+
return if @cycles < @next_key_at
|
|
45
|
+
|
|
46
|
+
if @key_down
|
|
47
|
+
release_key
|
|
48
|
+
@key_down = false
|
|
49
|
+
@next_key_at = @cycles + RELEASE_CYCLES
|
|
50
|
+
else
|
|
51
|
+
press_next_key
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def press_next_key
|
|
56
|
+
keys = @pending_keys
|
|
57
|
+
name = keys.shift
|
|
58
|
+
if name.nil?
|
|
59
|
+
@pending_keys = nil
|
|
60
|
+
return
|
|
61
|
+
end
|
|
62
|
+
if name == "ST"
|
|
63
|
+
press_stop
|
|
64
|
+
elsif name == "RS"
|
|
65
|
+
press_reset
|
|
66
|
+
else
|
|
67
|
+
press_key(name)
|
|
68
|
+
end
|
|
69
|
+
@key_down = true
|
|
70
|
+
@next_key_at = @cycles + HOLD_CYCLES
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
class KIM1
|
|
5
|
+
# The whole machine as keyed snapshot modules (Snapshot::Keyed), in the
|
|
6
|
+
# order a load applies them: BL-MACHINE with the setup, the RAM, U2 and
|
|
7
|
+
# U3, each with its 64 bytes, ports and timer, the panel, the cassette
|
|
8
|
+
# deck, the teletype, the sound's wiring and the CPU.
|
|
9
|
+
module KeyedModules
|
|
10
|
+
def save_modules
|
|
11
|
+
sound = @sound
|
|
12
|
+
[Snapshot::Keyed.section("BL-MACHINE") { |out| save_machine_fields(out) },
|
|
13
|
+
Snapshot::Keyed.section("BL-CPU") { |out| @cpu.save_fields(out) },
|
|
14
|
+
Snapshot::Keyed.section("BL-RAM") { |out| @bus.save_fields(out) },
|
|
15
|
+
Snapshot::Keyed.section("BL-U2") { |out| @u2.save_fields(out) },
|
|
16
|
+
Snapshot::Keyed.section("BL-U3") { |out| @u3.save_fields(out) },
|
|
17
|
+
Snapshot::Keyed.section("BL-PANEL") { |out| @panel.save_fields(out) },
|
|
18
|
+
Snapshot::Keyed.section("BL-DECK") { |out| @panel.tape.save_fields(out) },
|
|
19
|
+
Snapshot::Keyed.section("BL-TTY") { |out| @teletype.save_fields(out) },
|
|
20
|
+
Snapshot::Keyed.section("BL-SOUND") { |out| out.bool("speaker", sound.speaker) }]
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Raises Snapshot::FormatError for the modules of a machine built
|
|
24
|
+
# another way.
|
|
25
|
+
def load_modules(modules)
|
|
26
|
+
machine = modules.machine
|
|
27
|
+
check_snapshot_setup(Snapshot::KIM1Setup.read_fields(machine))
|
|
28
|
+
load_machine_fields(machine)
|
|
29
|
+
@bus.load_fields(modules.required("BL-RAM"))
|
|
30
|
+
@u2.load_fields(modules.required("BL-U2"))
|
|
31
|
+
@u3.load_fields(modules.required("BL-U3"))
|
|
32
|
+
@panel.load_fields(modules.required("BL-PANEL"))
|
|
33
|
+
@panel.tape.load_fields(modules.required("BL-DECK"))
|
|
34
|
+
@teletype.load_fields(modules.required("BL-TTY"))
|
|
35
|
+
@sound.speaker = modules.required("BL-SOUND").bool("speaker", false)
|
|
36
|
+
@cpu.load_fields(modules.required("BL-CPU"))
|
|
37
|
+
@cpu.restored!
|
|
38
|
+
self
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
private
|
|
42
|
+
|
|
43
|
+
def check_snapshot_setup(setup)
|
|
44
|
+
ours = Snapshot::KIM1Setup.of(self)
|
|
45
|
+
return if setup == ours
|
|
46
|
+
|
|
47
|
+
raise Snapshot::FormatError, "the snapshot is of a #{setup}, not a #{ours}"
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
include KeyedModules
|
|
52
|
+
end
|
|
53
|
+
end
|