badline 0.13.0 → 0.15.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 +72 -1
- data/CLAUDE.md +42 -12
- data/CONTRIBUTING.md +4 -3
- data/README.md +76 -13
- data/doc/media.md +2 -1
- data/doc/pinned-behaviour.md +174 -0
- data/doc/snapshot-format.md +1197 -0
- data/doc/snapshots.md +90 -40
- data/lib/badline/audio/bare_player.rb +46 -17
- data/lib/badline/audio/machine_player.rb +6 -10
- data/lib/badline/audio/renderer.rb +32 -21
- data/lib/badline/c128/banks.rb +7 -6
- data/lib/badline/c128/bus/keyed_state.rb +45 -0
- data/lib/badline/c128/bus.rb +36 -4
- data/lib/badline/c128/drives.rb +6 -25
- data/lib/badline/c128/keyed_modules.rb +117 -0
- data/lib/badline/c128/keyed_state.rb +91 -0
- data/lib/badline/c128/keys.rb +9 -0
- data/lib/badline/c128/mmu.rb +9 -8
- data/lib/badline/c128/mmu_pages.rb +27 -0
- data/lib/badline/c128/modes.rb +19 -9
- data/lib/badline/c128/z80_turns.rb +1 -1
- data/lib/badline/c128.rb +8 -8
- data/lib/badline/c64/attachments.rb +0 -44
- data/lib/badline/c64/banked_ram/plus256k.rb +18 -10
- data/lib/badline/c64/banked_ram/plus60k.rb +8 -6
- data/lib/badline/c64/banked_ram/unexpanded.rb +2 -2
- data/lib/badline/c64/bus/debug_register.rb +32 -0
- data/lib/badline/c64/bus/extra_sids.rb +80 -0
- data/lib/badline/c64/bus/keyed_state.rb +41 -0
- data/lib/badline/c64/bus.rb +3 -1
- data/lib/badline/c64/cartridge/action_replay.rb +6 -15
- data/lib/badline/c64/cartridge/comal80.rb +1 -0
- data/lib/badline/c64/cartridge/dinamic.rb +3 -1
- data/lib/badline/c64/cartridge/easy_flash.rb +0 -15
- data/lib/badline/c64/cartridge/epyx_fastload.rb +0 -8
- data/lib/badline/c64/cartridge/final_cartridge3.rb +1 -12
- data/lib/badline/c64/cartridge/flash.rb +3 -3
- data/lib/badline/c64/cartridge/fun_play.rb +2 -1
- data/lib/badline/c64/cartridge/g_mod2.rb +0 -12
- data/lib/badline/c64/cartridge/game_system.rb +2 -1
- data/lib/badline/c64/cartridge/geo_ram.rb +0 -16
- data/lib/badline/c64/cartridge/kcs_power.rb +0 -8
- data/lib/badline/c64/cartridge/keyed_state.rb +514 -0
- data/lib/badline/c64/cartridge/magic_desk.rb +2 -1
- data/lib/badline/c64/cartridge/ocean.rb +1 -0
- data/lib/badline/c64/cartridge/pagefox.rb +1 -10
- data/lib/badline/c64/cartridge/retro_replay.rb +0 -8
- data/lib/badline/c64/cartridge/rgcd.rb +2 -11
- data/lib/badline/c64/cartridge/super_games.rb +3 -12
- data/lib/badline/c64/cartridge/zaxxon.rb +1 -16
- data/lib/badline/c64/cartridge.rb +4 -30
- data/lib/badline/c64/kernal_traps.rb +28 -25
- data/lib/badline/c64/keyed_modules.rb +115 -0
- data/lib/badline/c64/keyed_state.rb +68 -0
- data/lib/badline/c64/reu/dma.rb +0 -4
- data/lib/badline/c64/reu/keyed_state.rb +140 -0
- data/lib/badline/c64/reu/ram.rb +0 -15
- data/lib/badline/c64/reu.rb +1 -3
- data/lib/badline/c64/true_drives.rb +56 -5
- data/lib/badline/c64.rb +7 -3
- data/lib/badline/chips/cia/interrupt_register.rb +0 -17
- data/lib/badline/chips/cia/keyed_parts.rb +213 -0
- data/lib/badline/chips/cia/serial.rb +0 -4
- data/lib/badline/chips/cia/time_of_day.rb +0 -22
- data/lib/badline/chips/cia/timer.rb +0 -20
- data/lib/badline/chips/cia.rb +2 -2
- data/lib/badline/chips/cpu/core.rb +18 -2
- data/lib/badline/chips/cpu/keyed_state.rb +177 -0
- data/lib/badline/chips/cpu/microcode.rb +16 -0
- data/lib/badline/chips/cpu/port.rb +20 -3
- data/lib/badline/chips/cpu.rb +5 -0
- data/lib/badline/chips/sid/decimator.rb +10 -8
- data/lib/badline/chips/sid/envelope.rb +1 -6
- data/lib/badline/chips/sid/filter.rb +0 -14
- data/lib/badline/chips/sid/keyed_parts.rb +286 -0
- data/lib/badline/chips/sid/voice.rb +0 -10
- data/lib/badline/chips/sid/waveform.rb +0 -2
- data/lib/badline/chips/sid.rb +1 -2
- data/lib/badline/chips/ted/beam.rb +118 -0
- data/lib/badline/chips/ted/fetches.rb +199 -0
- data/lib/badline/chips/ted/keyed_state.rb +156 -0
- data/lib/badline/chips/ted/modes.rb +134 -0
- data/lib/badline/chips/ted/palette.rb +43 -0
- data/lib/badline/chips/ted/picture.rb +182 -0
- data/lib/badline/chips/ted/sound/output.rb +86 -0
- data/lib/badline/chips/ted/sound.rb +284 -0
- data/lib/badline/chips/ted/timers.rb +66 -0
- data/lib/badline/chips/ted.rb +190 -0
- data/lib/badline/chips/tia.rb +61 -0
- data/lib/badline/chips/vdc/keyed_parts.rb +105 -0
- data/lib/badline/chips/vdc/painter.rb +0 -12
- data/lib/badline/chips/vdc/raster.rb +0 -16
- data/lib/badline/chips/vdc/window.rb +0 -9
- data/lib/badline/chips/vdc.rb +1 -2
- data/lib/badline/chips/via/control_lines.rb +0 -15
- data/lib/badline/chips/via/interrupt_register.rb +0 -9
- data/lib/badline/chips/via/keyed_parts.rb +163 -0
- data/lib/badline/chips/via/shift_register.rb +0 -14
- data/lib/badline/chips/via/timer1.rb +0 -13
- data/lib/badline/chips/via/timer2.rb +0 -18
- data/lib/badline/chips/via.rb +1 -2
- data/lib/badline/chips/vic/border_mask.rb +0 -10
- data/lib/badline/chips/vic/collisions.rb +0 -11
- data/lib/badline/chips/vic/color_patches.rb +0 -11
- data/lib/badline/chips/vic/display_state.rb +0 -19
- data/lib/badline/chips/vic/graphics_shifter.rb +0 -4
- data/lib/badline/chips/vic/keyed_parts.rb +228 -0
- data/lib/badline/chips/vic/keyed_sprites.rb +225 -0
- data/lib/badline/chips/vic/keyed_state.rb +102 -0
- data/lib/badline/chips/vic/register_log.rb +0 -18
- data/lib/badline/chips/vic/registers.rb +0 -9
- data/lib/badline/chips/vic/sequencer.rb +0 -2
- data/lib/badline/chips/vic/sprite/internal_bus.rb +0 -11
- data/lib/badline/chips/vic/sprite.rb +0 -2
- data/lib/badline/chips/vic/sprites.rb +0 -3
- data/lib/badline/chips/vic.rb +6 -2
- data/lib/badline/chips/vic_i/keyed_parts.rb +142 -0
- data/lib/badline/chips/vic_i/painter.rb +0 -4
- data/lib/badline/chips/vic_i/sound.rb +38 -46
- data/lib/badline/chips/vic_i.rb +2 -2
- data/lib/badline/chips/z80/core.rb +2 -2
- data/lib/badline/chips/z80/keyed_state.rb +73 -0
- data/lib/badline/chips.rb +10 -0
- data/lib/badline/core.rb +4 -21
- data/lib/badline/drives/datasette/keyed_state.rb +43 -0
- data/lib/badline/drives/datasette.rb +62 -12
- data/lib/badline/drives/engine/core.rb +3 -0
- data/lib/badline/drives/engine/serial_lines.rb +4 -7
- data/lib/badline/drives/model1541/bus.rb +0 -13
- data/lib/badline/drives/model1541/disk.rb +9 -8
- data/lib/badline/drives/model1541/keyed_state.rb +251 -0
- data/lib/badline/drives/model1541/mechanism.rb +0 -2
- data/lib/badline/drives/model1541.rb +1 -2
- data/lib/badline/drives/model1571/bus.rb +0 -13
- data/lib/badline/drives/model1571/keyed_state.rb +108 -0
- data/lib/badline/drives/model1571/wd1770.rb +0 -14
- data/lib/badline/drives/model1571.rb +1 -2
- data/lib/badline/drives/model1581/bus.rb +0 -13
- data/lib/badline/drives/model1581/disk.rb +0 -4
- data/lib/badline/drives/model1581/keyed_state.rb +280 -0
- data/lib/badline/drives/model1581/mechanism.rb +0 -21
- data/lib/badline/drives/model1581/slot.rb +0 -18
- data/lib/badline/drives/model1581/wd1772.rb +0 -2
- data/lib/badline/drives/model1581.rb +2 -2
- data/lib/badline/drives.rb +8 -0
- data/lib/badline/frontend/app.rb +3 -3
- data/lib/badline/frontend/boot.rb +4 -2
- data/lib/badline/frontend/controls.rb +25 -5
- data/lib/badline/frontend/drive_led.rb +44 -21
- data/lib/badline/frontend/keys.rb +14 -2
- data/lib/badline/frontend/media_slots.rb +11 -1
- data/lib/badline/frontend/menu/menu_dialogs.rb +4 -3
- data/lib/badline/frontend/menu/menu_media.rb +13 -3
- data/lib/badline/frontend/menu/menu_pages.rb +8 -8
- data/lib/badline/frontend/menu/menu_ports.rb +13 -1
- data/lib/badline/frontend/screen.rb +33 -12
- data/lib/badline/frontend/screens.rb +2 -1
- data/lib/badline/frontend/snapshots.rb +12 -1
- data/lib/badline/frontend/sound.rb +4 -4
- data/lib/badline/frontend/timeline.rb +43 -12
- data/lib/badline/input/matrix_typist.rb +98 -0
- data/lib/badline/input.rb +1 -0
- data/lib/badline/kernal_trap/chrout.rb +45 -0
- data/lib/badline/kernal_trap/dos/channel.rb +0 -27
- data/lib/badline/kernal_trap/dos/channels.rb +0 -25
- data/lib/badline/kernal_trap/dos/memory.rb +0 -9
- data/lib/badline/kernal_trap/dos/status.rb +0 -8
- data/lib/badline/kernal_trap/dos/write_file.rb +0 -18
- data/lib/badline/kernal_trap/dos.rb +0 -19
- data/lib/badline/kernal_trap/file_routine.rb +4 -4
- data/lib/badline/kernal_trap/keyed_state.rb +150 -0
- data/lib/badline/kernal_trap/layout.rb +75 -11
- data/lib/badline/kernal_trap/load.rb +14 -16
- data/lib/badline/kernal_trap/routine.rb +12 -0
- data/lib/badline/kernal_trap/save.rb +8 -18
- data/lib/badline/kernal_trap/serial.rb +1 -17
- data/lib/badline/kernal_trap.rb +2 -0
- data/lib/badline/machine/keyboard_buffer.rb +49 -0
- data/lib/badline/machine/settling.rb +29 -0
- data/lib/badline/machine.rb +29 -6
- data/lib/badline/media/boot_disk.rb +14 -2
- data/lib/badline/media/extensions.rb +10 -0
- data/lib/badline/media/plus4_media.rb +68 -0
- data/lib/badline/media/program.rb +20 -6
- data/lib/badline/media.rb +33 -12
- data/lib/badline/memory.rb +3 -6
- data/lib/badline/options/family.rb +6 -2
- data/lib/badline/options/help.rb +66 -16
- data/lib/badline/options/table.rb +17 -2
- data/lib/badline/options/validation.rb +26 -2
- data/lib/badline/options.rb +42 -9
- data/lib/badline/plus4/acia.rb +45 -0
- data/lib/badline/plus4/bus.rb +375 -0
- data/lib/badline/plus4/cassette_sense.rb +21 -0
- data/lib/badline/plus4/cpu.rb +10 -0
- data/lib/badline/plus4/cpu_port.rb +93 -0
- data/lib/badline/plus4/keyboard_port.rb +76 -0
- data/lib/badline/plus4/keyed_modules.rb +99 -0
- data/lib/badline/plus4/keyed_parts.rb +143 -0
- data/lib/badline/plus4/keyed_state.rb +41 -0
- data/lib/badline/plus4/model.rb +38 -0
- data/lib/badline/plus4/user_port.rb +31 -0
- data/lib/badline/plus4.rb +240 -0
- data/lib/badline/roms/README +38 -0
- data/lib/badline/roms/plus4/basic.rom +0 -0
- data/lib/badline/roms/plus4/function-high.rom +0 -0
- data/lib/badline/roms/plus4/function-low.rom +0 -0
- data/lib/badline/roms/plus4/kernal-ntsc.rom +0 -0
- data/lib/badline/roms/plus4/kernal-pal.rom +0 -0
- data/lib/badline/snapshot/c128_setup.rb +9 -20
- data/lib/badline/snapshot/c64_setup.rb +28 -24
- data/lib/badline/snapshot/container.rb +6 -0
- data/lib/badline/snapshot/field_doc/markdown.rb +113 -0
- data/lib/badline/snapshot/field_doc.rb +148 -0
- data/lib/badline/snapshot/image.rb +143 -28
- data/lib/badline/snapshot/keyed/cursor.rb +40 -0
- data/lib/badline/snapshot/keyed/modules.rb +66 -0
- data/lib/badline/snapshot/keyed/reader.rb +188 -0
- data/lib/badline/snapshot/keyed/recorder.rb +88 -0
- data/lib/badline/snapshot/keyed/report.rb +26 -0
- data/lib/badline/snapshot/keyed/writer.rb +55 -0
- data/lib/badline/snapshot/keyed.rb +64 -0
- data/lib/badline/snapshot/machine_state.rb +17 -64
- data/lib/badline/snapshot/plus4_setup.rb +39 -0
- data/lib/badline/snapshot/storage_setup.rb +29 -34
- data/lib/badline/snapshot/vic20_setup.rb +6 -15
- data/lib/badline/snapshot/vice/reu1764.rb +2 -2
- data/lib/badline/snapshot/vice.rb +7 -31
- data/lib/badline/snapshot.rb +48 -20
- data/lib/badline/storage/crt_file.rb +3 -1
- data/lib/badline/storage/g64_image.rb +9 -4
- data/lib/badline/storage/tap.rb +57 -20
- data/lib/badline/support/checkpoint.rb +3 -2
- data/lib/badline/support/plus4_checkpoint.rb +42 -0
- data/lib/badline/version.rb +1 -1
- data/lib/badline/vic20/attachments.rb +3 -0
- data/lib/badline/vic20/bus/keyed_state.rb +52 -0
- data/lib/badline/vic20/bus.rb +1 -2
- data/lib/badline/vic20/keyed_modules.rb +73 -0
- data/lib/badline/vic20/keyed_state.rb +45 -0
- data/lib/badline/vic20.rb +5 -5
- data/lib/badline.rb +1 -0
- metadata +75 -42
- data/lib/badline/c128/bus/saved_state.rb +0 -53
- data/lib/badline/c128/saved_state.rb +0 -117
- data/lib/badline/c64/bus/saved_state.rb +0 -68
- data/lib/badline/c64/cartridge/flash/saved_state.rb +0 -40
- data/lib/badline/c64/cartridge/retro_replay/saved_state.rb +0 -47
- data/lib/badline/c64/cartridge/saved_state.rb +0 -63
- data/lib/badline/c64/reu/dma/saved_state.rb +0 -52
- data/lib/badline/c64/reu/saved_state.rb +0 -55
- data/lib/badline/c64/saved_state.rb +0 -112
- data/lib/badline/chips/cia/saved_state.rb +0 -49
- data/lib/badline/chips/cia/serial/saved_state.rb +0 -45
- data/lib/badline/chips/cpu/saved_state.rb +0 -117
- data/lib/badline/chips/sid/envelope/saved_state.rb +0 -70
- data/lib/badline/chips/sid/filter/saved_state.rb +0 -42
- data/lib/badline/chips/sid/saved_state.rb +0 -52
- data/lib/badline/chips/sid/waveform/saved_state.rb +0 -68
- data/lib/badline/chips/vdc/saved_state.rb +0 -36
- data/lib/badline/chips/via/saved_state.rb +0 -44
- data/lib/badline/chips/vic/graphics_shifter/saved_state.rb +0 -40
- data/lib/badline/chips/vic/saved_state.rb +0 -82
- data/lib/badline/chips/vic/sequencer/saved_state.rb +0 -91
- data/lib/badline/chips/vic/sprite/saved_state.rb +0 -66
- data/lib/badline/chips/vic/sprites/saved_state.rb +0 -59
- data/lib/badline/chips/vic_i/painter/saved_state.rb +0 -40
- data/lib/badline/chips/vic_i/saved_state.rb +0 -55
- data/lib/badline/chips/z80/saved_state.rb +0 -71
- data/lib/badline/chrout_trap.rb +0 -43
- data/lib/badline/debug_register.rb +0 -28
- data/lib/badline/drives/datasette/saved_state.rb +0 -37
- data/lib/badline/drives/model1541/disk/saved_state.rb +0 -88
- data/lib/badline/drives/model1541/mechanism/saved_state.rb +0 -72
- data/lib/badline/drives/model1541/saved_state.rb +0 -44
- data/lib/badline/drives/model1571/saved_state.rb +0 -47
- data/lib/badline/drives/model1581/disk/saved_state.rb +0 -89
- data/lib/badline/drives/model1581/saved_state.rb +0 -42
- data/lib/badline/drives/model1581/wd1772/saved_state.rb +0 -68
- data/lib/badline/keyboard_buffer.rb +0 -33
- data/lib/badline/snapshot/setup.rb +0 -31
- data/lib/badline/snapshot/state.rb +0 -179
- data/lib/badline/vic20/bus/saved_state.rb +0 -35
- data/lib/badline/vic20/saved_state.rb +0 -110
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
class Plus4
|
|
5
|
+
# The keyboard and the joysticks: the 6529 at $FD30-$FD3F, whose eight
|
|
6
|
+
# outputs drive the key matrix's lines, and the TED's K0-K7 inputs,
|
|
7
|
+
# which $FF08 latches (Chips::TED#keyboard_port=). The KERNAL's scan
|
|
8
|
+
# ($DB11) writes each line's mask to $FD30 and $FF08 and reads the keys
|
|
9
|
+
# back from $FF08, and its key table at $E026 puts each key at line
|
|
10
|
+
# times 8 plus input bit.
|
|
11
|
+
#
|
|
12
|
+
# The joysticks share K0-K7. The byte written to $FF08 selects them,
|
|
13
|
+
# bit 2 low joystick 1 and bit 1 low joystick 2, as BASIC's JOY does
|
|
14
|
+
# ($FA and $FD). A selected joystick pulls up, down, left and right
|
|
15
|
+
# onto K0-K3 and its fire button onto K6 (joystick 1) or K7
|
|
16
|
+
# (joystick 2), and those lines settle against the matrix with the
|
|
17
|
+
# keys'.
|
|
18
|
+
#
|
|
19
|
+
# The 6529 is a quasi-bidirectional latch: a read returns what was
|
|
20
|
+
# written to it.
|
|
21
|
+
class KeyboardPort
|
|
22
|
+
# One row of keys per 6529 output, in K0-K7 order, after the
|
|
23
|
+
# KERNAL's key table. HELP is the KERNAL's F7, and the two SHIFT keys
|
|
24
|
+
# share one place.
|
|
25
|
+
MATRIX = [
|
|
26
|
+
%i[delete return £ help f1 f2 f3 @],
|
|
27
|
+
%i[3 w a 4 z s e lshift],
|
|
28
|
+
%i[5 r d 6 c f t x],
|
|
29
|
+
%i[7 y g 8 b h u v],
|
|
30
|
+
%i[9 i j 0 m k o n],
|
|
31
|
+
%i[crsr_down p l crsr_up . : - ,],
|
|
32
|
+
%i[crsr_left * ; crsr_right esc = + /],
|
|
33
|
+
%i[1 clr_home control 2 space cbm q run_stop]
|
|
34
|
+
].freeze
|
|
35
|
+
|
|
36
|
+
attr_reader :keyboard, :output
|
|
37
|
+
|
|
38
|
+
def initialize(keyboard, joystick1, joystick2)
|
|
39
|
+
@keyboard = keyboard
|
|
40
|
+
@joystick1 = joystick1
|
|
41
|
+
@joystick2 = joystick2
|
|
42
|
+
@output = 0xff
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def reset!
|
|
46
|
+
@output = 0xff
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def peek(_addr) = @output
|
|
50
|
+
|
|
51
|
+
def poke(_addr, value)
|
|
52
|
+
@output = value
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# The K0-K7 inputs the TED latches on a write of +value+ to $FF08:
|
|
56
|
+
# high but where a held key joins a line the 6529 pulls low, or a
|
|
57
|
+
# joystick +value+ selects holds a switch closed.
|
|
58
|
+
def latch(value) = @keyboard.scan(@output, joystick_lines(value)).last
|
|
59
|
+
|
|
60
|
+
private
|
|
61
|
+
|
|
62
|
+
def joystick_lines(value)
|
|
63
|
+
lines = 0xff
|
|
64
|
+
lines &= joystick_inputs(@joystick1.port_bits, 0x40) if value.nobits?(0x04)
|
|
65
|
+
lines &= joystick_inputs(@joystick2.port_bits, 0x80) if value.nobits?(0x02)
|
|
66
|
+
lines
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# A joystick's switches, Input::Joystick's bits 0-4, on K0-K7: the
|
|
70
|
+
# directions on K0-K3 and the fire button on +fire+.
|
|
71
|
+
def joystick_inputs(bits, fire)
|
|
72
|
+
bits.anybits?(0x10) ? bits | 0xf0 : (bits | 0xf0) & ~fire & 0xff
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
class Plus4
|
|
5
|
+
# The whole machine as keyed snapshot modules (Snapshot::Keyed): BL-MACHINE
|
|
6
|
+
# with the setup, the cartridges' ROMs by slot, the CPU with its port,
|
|
7
|
+
# the TED, its sound, the keyboard's 6529, the user port's 6529 and the
|
|
8
|
+
# ACIA where the model has them, the TCBM interface's TIAs where one is
|
|
9
|
+
# plugged in, the RAM with the ROM bank latch and the ROM/RAM switch
|
|
10
|
+
# under bus.*, the datasette, device 8 through the traps and the true
|
|
11
|
+
# drives.
|
|
12
|
+
#
|
|
13
|
+
# A load maps the banks before the chips load, and works out what each
|
|
14
|
+
# unit derives from its fields once every unit is loaded.
|
|
15
|
+
module KeyedModules
|
|
16
|
+
def save_modules
|
|
17
|
+
bus = @bus
|
|
18
|
+
sections = [Snapshot::Keyed.section("BL-MACHINE") { |out| save_machine_fields(out) }]
|
|
19
|
+
sections << Snapshot::Keyed.section("BL-CART") { |out| bus.save_slot_fields(out) } if bus.cartridge?
|
|
20
|
+
save_chip_modules(sections)
|
|
21
|
+
sections << Snapshot::Keyed.section("BL-RAM") do |out|
|
|
22
|
+
bus.ram.save_fields(out, "ram")
|
|
23
|
+
bus.save_fields(out, "bus.")
|
|
24
|
+
end
|
|
25
|
+
sections << Snapshot::Keyed.section("BL-TAPE") { |out| @datasette.save_fields(out) }
|
|
26
|
+
sections << Snapshot::Keyed.section("BL-TRAPDOS") { |out| save_trap_dos_fields(out) } if mounted?
|
|
27
|
+
save_drive_modules(sections)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Raises Snapshot::FormatError for the modules of a machine built
|
|
31
|
+
# another way.
|
|
32
|
+
def load_modules(modules)
|
|
33
|
+
machine = modules.machine
|
|
34
|
+
check_snapshot_setup(Snapshot::Plus4Setup.read_fields(machine))
|
|
35
|
+
load_machine_fields(machine)
|
|
36
|
+
bus = @bus
|
|
37
|
+
memory = modules.required("BL-RAM")
|
|
38
|
+
bus.ram.load_fields(memory, "ram")
|
|
39
|
+
bus.load_fields(memory, "bus.")
|
|
40
|
+
bus.load_slot_fields(modules.optional("BL-CART") || Snapshot::Keyed::Reader.new("BL-CART"))
|
|
41
|
+
bus.restored!
|
|
42
|
+
load_chip_modules(modules)
|
|
43
|
+
@datasette.load_fields(modules.required("BL-TAPE"))
|
|
44
|
+
trap_dos = modules.optional("BL-TRAPDOS")
|
|
45
|
+
trap_dos ? load_trap_dos_fields(trap_dos, detached: modules.detached?) : unmount
|
|
46
|
+
load_drive_modules(modules)
|
|
47
|
+
modules_restored!
|
|
48
|
+
self
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
private
|
|
52
|
+
|
|
53
|
+
def save_chip_modules(sections)
|
|
54
|
+
sections << Snapshot::Keyed.section("BL-CPU") do |out|
|
|
55
|
+
@cpu.save_fields(out)
|
|
56
|
+
@port.save_fields(out, "port.")
|
|
57
|
+
end
|
|
58
|
+
sections << Snapshot::Keyed.section("BL-TED") { |out| @ted.save_fields(out) }
|
|
59
|
+
sections << Snapshot::Keyed.section("BL-TEDSOUND") { |out| @sound.save_fields(out) }
|
|
60
|
+
sections << Snapshot::Keyed.section("BL-KEYPORT") { |out| @keyboard_port.save_fields(out) }
|
|
61
|
+
user_port = @user_port
|
|
62
|
+
sections << Snapshot::Keyed.section("BL-USERPORT") { |out| user_port.save_fields(out) } if user_port
|
|
63
|
+
acia = @acia
|
|
64
|
+
sections << Snapshot::Keyed.section("BL-ACIA") { |out| acia.save_fields(out) } if acia
|
|
65
|
+
@tias.each_with_index do |tia, index|
|
|
66
|
+
sections << Snapshot::Keyed.section("BL-TIA#{index + 1}") { |out| tia.save_fields(out) }
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def load_chip_modules(modules)
|
|
71
|
+
cpu = modules.required("BL-CPU")
|
|
72
|
+
@port.load_fields(cpu, "port.")
|
|
73
|
+
@ted.load_fields(modules.required("BL-TED"))
|
|
74
|
+
@sound.load_fields(modules.required("BL-TEDSOUND"))
|
|
75
|
+
@keyboard_port.load_fields(modules.required("BL-KEYPORT"))
|
|
76
|
+
@user_port&.load_fields(modules.required("BL-USERPORT"))
|
|
77
|
+
@acia&.load_fields(modules.required("BL-ACIA"))
|
|
78
|
+
@tias.each_with_index { |tia, index| tia.load_fields(modules.required("BL-TIA#{index + 1}")) }
|
|
79
|
+
@cpu.load_fields(cpu)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def modules_restored!
|
|
83
|
+
@cpu.restored!
|
|
84
|
+
@ted.restored!
|
|
85
|
+
@sound.restored!(@ted)
|
|
86
|
+
@port.restored!
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def check_snapshot_setup(setup)
|
|
90
|
+
ours = Snapshot::Plus4Setup.of(self)
|
|
91
|
+
return if setup == ours
|
|
92
|
+
|
|
93
|
+
raise Snapshot::FormatError, "the snapshot is of a #{setup}, not a #{ours}"
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
include KeyedModules
|
|
98
|
+
end
|
|
99
|
+
end
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
class Plus4
|
|
5
|
+
class CPUPort
|
|
6
|
+
# The port's registers in a keyed snapshot module (Snapshot::Keyed),
|
|
7
|
+
# under `prefix`. The serial bus's lines and the datasette's write
|
|
8
|
+
# line are driven again from them once everything is loaded
|
|
9
|
+
# (#restored!), without recording an edge on the tape.
|
|
10
|
+
module KeyedState
|
|
11
|
+
def save_fields(out, prefix)
|
|
12
|
+
out.int("#{prefix}ddr", @ddr).int("#{prefix}data", @data)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def load_fields(input, prefix)
|
|
16
|
+
@ddr = input.byte("#{prefix}ddr", @ddr)
|
|
17
|
+
@data = input.byte("#{prefix}data", @data)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def restored!
|
|
21
|
+
@serial_bus.host_lines = serial_lines
|
|
22
|
+
@datasette.write_level = levels.anybits?(CLK_OUT)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
include KeyedState
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
class KeyboardPort
|
|
30
|
+
# The 6529's latch in a keyed snapshot module (BL-KEYPORT).
|
|
31
|
+
module KeyedState
|
|
32
|
+
def save_fields(out) = out.int("output", @output)
|
|
33
|
+
|
|
34
|
+
def load_fields(input)
|
|
35
|
+
@output = input.byte("output", @output)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
include KeyedState
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
class UserPort
|
|
43
|
+
# The 6529's latch in a keyed snapshot module (BL-USERPORT).
|
|
44
|
+
module KeyedState
|
|
45
|
+
def save_fields(out) = out.int("output", @output)
|
|
46
|
+
|
|
47
|
+
def load_fields(input)
|
|
48
|
+
@output = input.byte("output", @output)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
include KeyedState
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
class ACIA
|
|
56
|
+
# The command and control registers in a keyed snapshot module
|
|
57
|
+
# (BL-ACIA).
|
|
58
|
+
module KeyedState
|
|
59
|
+
def save_fields(out) = out.int("command", @command).int("control", @control)
|
|
60
|
+
|
|
61
|
+
def load_fields(input)
|
|
62
|
+
@command = input.byte("command", @command)
|
|
63
|
+
@control = input.byte("control", @control)
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
include KeyedState
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
class Bus
|
|
71
|
+
# The ROM bank latch, whether ROM is selected and the last byte on the
|
|
72
|
+
# bus, under `prefix`, and the cartridges' ROMs by slot (#save_slot_fields),
|
|
73
|
+
# in keyed snapshot modules (Snapshot::Keyed). The TED learns which
|
|
74
|
+
# memory is selected, and the pages are mapped again, once they are
|
|
75
|
+
# loaded (#restored!). The BASIC, KERNAL and function ROMs come with
|
|
76
|
+
# the model.
|
|
77
|
+
module KeyedState
|
|
78
|
+
# The cartridge slots, as xplus4 names them: cartridge 1 or 2, the
|
|
79
|
+
# low ($8000) or the high ($C000) half.
|
|
80
|
+
SLOTS = %w[c1lo c2lo c1hi c2hi].freeze
|
|
81
|
+
|
|
82
|
+
def save_fields(out, prefix)
|
|
83
|
+
out.int("#{prefix}low_bank", @low_bank).int("#{prefix}high_bank", @high_bank)
|
|
84
|
+
out.bool("#{prefix}rom_selected", @rom_selected).int("#{prefix}data", @data)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def load_fields(input, prefix)
|
|
88
|
+
@low_bank = input.masked("#{prefix}low_bank", @low_bank, 0x03)
|
|
89
|
+
@high_bank = input.masked("#{prefix}high_bank", @high_bank, 0x03)
|
|
90
|
+
@rom_selected = input.bool("#{prefix}rom_selected", @rom_selected)
|
|
91
|
+
@data = input.byte("#{prefix}data", @data)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Whether a cartridge's ROM is in any slot.
|
|
95
|
+
def cartridge?
|
|
96
|
+
index = 0
|
|
97
|
+
while index < SLOTS.length
|
|
98
|
+
return true unless slot_rom(index).nil?
|
|
99
|
+
|
|
100
|
+
index += 1
|
|
101
|
+
end
|
|
102
|
+
false
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# Each slot's ROM, 16K, under its name. An empty slot writes
|
|
106
|
+
# nothing.
|
|
107
|
+
def save_slot_fields(out)
|
|
108
|
+
SLOTS.each_with_index do |name, index|
|
|
109
|
+
rom = slot_rom(index)
|
|
110
|
+
rom&.save_fields(out, name)
|
|
111
|
+
end
|
|
112
|
+
out
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# A slot the module holds nothing for is left empty.
|
|
116
|
+
def load_slot_fields(input)
|
|
117
|
+
SLOTS.each_with_index do |name, index|
|
|
118
|
+
bytes = input.bytes(name, "".b).bytes
|
|
119
|
+
if index < 2
|
|
120
|
+
@low_roms[2 + index] = slot(bytes, 0x8000)
|
|
121
|
+
else
|
|
122
|
+
@high_roms[index] = slot(bytes, 0xc000)
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def restored!
|
|
128
|
+
@ted.rom_selected = @rom_selected
|
|
129
|
+
map_reads
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
private
|
|
133
|
+
|
|
134
|
+
def slot_rom(index) = index < 2 ? @low_roms[2 + index] : @high_roms[index]
|
|
135
|
+
|
|
136
|
+
# A slot's ROM from its bytes at `start`, or none.
|
|
137
|
+
def slot(bytes, start) = bytes.empty? ? nil : ROM.new(bytes, length: bytes.length, start:)
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
include KeyedState
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
class Plus4
|
|
5
|
+
# The machine's own fields in a keyed snapshot module (Snapshot::Keyed,
|
|
6
|
+
# BL-MACHINE): the family, its setup by name (Snapshot::Plus4Setup), the
|
|
7
|
+
# cycle count, and the host's typing still to feed and on_init handlers
|
|
8
|
+
# still to run under host.*. The badline version that wrote it is
|
|
9
|
+
# written for reports and never read.
|
|
10
|
+
module KeyedState
|
|
11
|
+
# How many more on_init handlers the machine a snapshot was taken of had
|
|
12
|
+
# yet to run than this one has. Nil until the machine is restored.
|
|
13
|
+
attr_reader :init_handlers_lost
|
|
14
|
+
|
|
15
|
+
# Whether a snapshot saves now (Machine::Settling): the CPU between
|
|
16
|
+
# instructions or jammed.
|
|
17
|
+
def settled_for_snapshot? = @cpu.boundary? || @cpu.jammed?
|
|
18
|
+
|
|
19
|
+
# The sound's voices, run up to this cycle.
|
|
20
|
+
def settle_chips_for_snapshot = @sound.catch_up!
|
|
21
|
+
|
|
22
|
+
def save_machine_fields(out)
|
|
23
|
+
out.text("family", "plus4").text("written_by", VERSION)
|
|
24
|
+
Snapshot::Plus4Setup.of(self).write_fields(out)
|
|
25
|
+
out.int("cycles", @cycles)
|
|
26
|
+
pending = @pending_keys
|
|
27
|
+
out.ints("host.pending_keys", pending) if pending
|
|
28
|
+
out.int("host.init_pending", booting? ? @init_handlers.length : 0)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def load_machine_fields(input)
|
|
32
|
+
@cycles = input.int("cycles", @cycles)
|
|
33
|
+
pending = input.ints("host.pending_keys", [])
|
|
34
|
+
@pending_keys = pending.empty? ? nil : pending.dup
|
|
35
|
+
@init_handlers_lost = [input.int("host.init_pending", 0) - @init_handlers.length, 0].max
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
include KeyedState
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
class Plus4
|
|
5
|
+
# The 264 series machines badline builds by name, after xplus4's
|
|
6
|
+
# -model names, with the C116 as another name for the C16: one board
|
|
7
|
+
# design with its RAM, its video standard and KERNAL, and whether the
|
|
8
|
+
# Plus/4's own parts are fitted (the 3-plus-1 function ROMs, the 6551
|
|
9
|
+
# ACIA and the user port's 6529).
|
|
10
|
+
module Model
|
|
11
|
+
Profile = Data.define(:name, :ram_kb, :ted, :kernal, :plus4_parts)
|
|
12
|
+
|
|
13
|
+
PLUS4 = Profile.new(name: "plus4", ram_kb: 64, ted: Chips::TED::PAL, kernal: "plus4/kernal-pal.rom",
|
|
14
|
+
plus4_parts: true)
|
|
15
|
+
PLUS4_NTSC = Profile.new(name: "plus4ntsc", ram_kb: 64, ted: Chips::TED::NTSC,
|
|
16
|
+
kernal: "plus4/kernal-ntsc.rom", plus4_parts: true)
|
|
17
|
+
C16 = Profile.new(name: "c16", ram_kb: 16, ted: Chips::TED::PAL, kernal: "plus4/kernal-pal.rom",
|
|
18
|
+
plus4_parts: false)
|
|
19
|
+
C16_NTSC = Profile.new(name: "c16ntsc", ram_kb: 16, ted: Chips::TED::NTSC, kernal: "plus4/kernal-ntsc.rom",
|
|
20
|
+
plus4_parts: false)
|
|
21
|
+
|
|
22
|
+
ALL = [PLUS4, PLUS4_NTSC, C16, C16_NTSC].freeze
|
|
23
|
+
|
|
24
|
+
# Other names a model goes by.
|
|
25
|
+
ALIASES = { "c116" => "c16" }.freeze
|
|
26
|
+
|
|
27
|
+
# The RAM sizes a machine can have, in K.
|
|
28
|
+
RAM_SIZES = [16, 32, 64].freeze
|
|
29
|
+
|
|
30
|
+
def self.named(name)
|
|
31
|
+
model = ALL.find { |candidate| candidate.name == ALIASES.fetch(name, name) }
|
|
32
|
+
raise ArgumentError, "no Plus/4 model named #{name}" if model.nil?
|
|
33
|
+
|
|
34
|
+
model
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
class Plus4
|
|
5
|
+
# The Plus/4's user port 6529 at $FD10-$FD1F, a quasi-bidirectional
|
|
6
|
+
# latch: a line reads low where the latch or the outside pulls it. With
|
|
7
|
+
# nothing plugged into the user port, the outside pulls only bit 2, the
|
|
8
|
+
# cassette sense switch, low while a key is down on the deck. The
|
|
9
|
+
# KERNAL's IRQ reads it there ($CFBF).
|
|
10
|
+
class UserPort
|
|
11
|
+
TAPE_SENSE = 0x04
|
|
12
|
+
|
|
13
|
+
attr_reader :output
|
|
14
|
+
|
|
15
|
+
def initialize(datasette)
|
|
16
|
+
@datasette = datasette
|
|
17
|
+
@output = 0xff
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def reset!
|
|
21
|
+
@output = 0xff
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def peek(_addr) = @datasette.sense_low? ? @output & ~TAPE_SENSE : @output
|
|
25
|
+
|
|
26
|
+
def poke(_addr, value)
|
|
27
|
+
@output = value
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "badline/chips/ted"
|
|
4
|
+
require "badline/chips/tia"
|
|
5
|
+
require "badline/plus4/model"
|
|
6
|
+
require "badline/plus4/cpu"
|
|
7
|
+
require "badline/plus4/cpu_port"
|
|
8
|
+
require "badline/plus4/keyboard_port"
|
|
9
|
+
require "badline/plus4/user_port"
|
|
10
|
+
require "badline/plus4/cassette_sense"
|
|
11
|
+
require "badline/plus4/acia"
|
|
12
|
+
require "badline/plus4/bus"
|
|
13
|
+
require "badline/snapshot/plus4_setup"
|
|
14
|
+
require "badline/plus4/keyed_parts"
|
|
15
|
+
require "badline/plus4/keyed_state"
|
|
16
|
+
require "badline/plus4/keyed_modules"
|
|
17
|
+
|
|
18
|
+
module Badline
|
|
19
|
+
# The Commodore 264 series: the Plus/4, the C16 and the C116. A 7501 on
|
|
20
|
+
# the TED's clock, which runs it once or twice a TED cycle (Chips::TED's
|
|
21
|
+
# Beam), the TED and, on the Plus/4, a 6551 ACIA and the user port's
|
|
22
|
+
# 6529. Only the TED pulls IRQ, and nothing pulls NMI, which the 7501
|
|
23
|
+
# doesn't have.
|
|
24
|
+
#
|
|
25
|
+
# The TED paints the picture a front end shows (#video, #timing). Device 8
|
|
26
|
+
# serves disks through the KERNAL traps (C64::KernalTraps), or a true
|
|
27
|
+
# 1541 or 1581 on the serial bus does (C64::TrueDrives), cartridges go
|
|
28
|
+
# in the two cartridges' slots, and tapes play on the datasette, whose read
|
|
29
|
+
# line's level the KERNAL times with the TED's timers.
|
|
30
|
+
class Plus4
|
|
31
|
+
include IntegerHelper
|
|
32
|
+
include Machine::KeyboardBuffer
|
|
33
|
+
include Machine::Clocking
|
|
34
|
+
include Machine::Settling
|
|
35
|
+
include C64::TrueDrives
|
|
36
|
+
include C64::KernalTraps
|
|
37
|
+
|
|
38
|
+
# The cycle by which the KERNAL has booted and BASIC printed READY,
|
|
39
|
+
# with a margin, in TED cycles. READY is up by about 116,000 on a PAL
|
|
40
|
+
# Plus/4, 126,000 on an NTSC one and sooner on a C16.
|
|
41
|
+
INIT_THRESHOLD = 200_000
|
|
42
|
+
|
|
43
|
+
# The cycle by which a 1541 switched on with the machine has run its
|
|
44
|
+
# DOS's reset, the RAM test and the ROM checksum, and waits on the
|
|
45
|
+
# bus, with a margin, in TED cycles.
|
|
46
|
+
DRIVE_BOOT_CYCLES = 1_200_000
|
|
47
|
+
|
|
48
|
+
attr_reader :model, :bus, :cpu, :ted, :sound, :port, :keyboard, :keyboard_port, :joystick1, :joystick2, :acia,
|
|
49
|
+
:user_port, :datasette, :tias, :iec_bus, :cycles, :ram_kb, :drive1541, :init_threshold
|
|
50
|
+
|
|
51
|
+
def family = :plus4
|
|
52
|
+
|
|
53
|
+
# The joysticks reach the TED's keyboard latch, so there are no C64
|
|
54
|
+
# control ports to plug a pot device into.
|
|
55
|
+
def control_ports = nil
|
|
56
|
+
|
|
57
|
+
def ram = @bus.ram
|
|
58
|
+
|
|
59
|
+
# What a front end plays: the TED's sound, which answers #record and
|
|
60
|
+
# #drain_samples.
|
|
61
|
+
def sound_source = @sound
|
|
62
|
+
|
|
63
|
+
# The chip whose #display a front end shows.
|
|
64
|
+
def video = @ted
|
|
65
|
+
|
|
66
|
+
# The TED's clock and raster, and the part of its picture xplus4 shows:
|
|
67
|
+
# all 384 pixels of a line, and 288 lines from line 276 on PAL or 242
|
|
68
|
+
# from line 248 on NTSC, running on past the frame's last line to its
|
|
69
|
+
# first, as the top border is the end of the frame. The TED draws 8
|
|
70
|
+
# pixels a cycle, so each shows one window pixel wide, as on the C64.
|
|
71
|
+
def timing
|
|
72
|
+
profile = @ted.profile
|
|
73
|
+
ntsc = profile == Chips::TED::NTSC
|
|
74
|
+
Timing.new(clock_hz: profile.clock_hz, cycles_per_line: Chips::TED::Beam::CYCLES_PER_LINE,
|
|
75
|
+
lines_per_frame: ntsc ? 262 : 312, crop: ntsc ? [0, 248, 384, 242] : [0, 276, 384, 288],
|
|
76
|
+
pixel_width: 1)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Reads and writes memory as the CPU sees it, through the bus.
|
|
80
|
+
def peek(addr) = @bus.peek(addr)
|
|
81
|
+
|
|
82
|
+
def poke(addr, value) = @bus.poke(addr, value)
|
|
83
|
+
|
|
84
|
+
# +model+ names one of Model::ALL, or c116 for the C16, and +ram+ the
|
|
85
|
+
# RAM in K, one of Model::RAM_SIZES, when not the model's own. +tcbm+
|
|
86
|
+
# plugs in a 1551's TCBM interface, the TIAs at $FEC0-$FEFF, with no
|
|
87
|
+
# drive answering on the other side.
|
|
88
|
+
def initialize(model: "plus4", ram: nil, tcbm: false, debug: false)
|
|
89
|
+
@model = Model.named(model)
|
|
90
|
+
@ram_kb = ram || @model.ram_kb
|
|
91
|
+
raise ArgumentError, "no Plus/4 RAM size of #{@ram_kb}K" unless Model::RAM_SIZES.include?(@ram_kb)
|
|
92
|
+
|
|
93
|
+
@ted = Chips::TED.new(@model.ted)
|
|
94
|
+
@keyboard = Input::Keyboard.new(matrix: KeyboardPort::MATRIX)
|
|
95
|
+
@joystick1 = Input::Joystick.new
|
|
96
|
+
@joystick2 = Input::Joystick.new
|
|
97
|
+
@keyboard_port = KeyboardPort.new(@keyboard, @joystick1, @joystick2)
|
|
98
|
+
@ted.keyboard_port = @keyboard_port
|
|
99
|
+
@iec_bus = Drives::SerialBus.new
|
|
100
|
+
@datasette = Drives::Datasette.new(level: true)
|
|
101
|
+
@port = CPUPort.new(serial_bus: @iec_bus, datasette: @datasette)
|
|
102
|
+
plus4_parts = @model.plus4_parts
|
|
103
|
+
@acia = plus4_parts ? ACIA.new : nil
|
|
104
|
+
@user_port = plus4_parts ? UserPort.new(@datasette) : nil
|
|
105
|
+
io = Bus::IOPage.new(keyboard_port: @keyboard_port, acia: @acia, user_port: @user_port,
|
|
106
|
+
cassette_sense: plus4_parts ? nil : CassetteSense.new(@datasette))
|
|
107
|
+
@bus = Bus.new(ted: @ted, port: @port, io:, model: @model, ram_kb: @ram_kb)
|
|
108
|
+
@ted.connect(@bus)
|
|
109
|
+
@tias = tcbm ? [Chips::TIA.new, Chips::TIA.new] : []
|
|
110
|
+
@bus.plug_tcbm(drive8: @tias[0], drive9: @tias[1]) if tcbm
|
|
111
|
+
@cpu = CPU.new(@bus, debug:)
|
|
112
|
+
@cycles = 0
|
|
113
|
+
@sound = Chips::TED::Sound.new(self, @model.ted.clock_hz)
|
|
114
|
+
@ted.sound = @sound
|
|
115
|
+
@init_handlers = []
|
|
116
|
+
@pending_keys = nil
|
|
117
|
+
@dos = nil
|
|
118
|
+
@serial_trap = nil
|
|
119
|
+
@save_trap = nil
|
|
120
|
+
@capture_output = nil
|
|
121
|
+
@drive1541 = nil
|
|
122
|
+
@drive1581 = nil
|
|
123
|
+
@init_threshold = INIT_THRESHOLD
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
# One TED cycle: the TED moves on and decides the CPU's clock, then the
|
|
127
|
+
# CPU runs its cycles in it, none while the TED holds the bus.
|
|
128
|
+
def cycle!
|
|
129
|
+
handle_init if @cycles == @init_threshold
|
|
130
|
+
feed_keyboard if @pending_keys
|
|
131
|
+
|
|
132
|
+
ted = @ted
|
|
133
|
+
ted.cycle!
|
|
134
|
+
@datasette.cycle!
|
|
135
|
+
@cpu.irq = ted.irq?
|
|
136
|
+
slots = ted.cpu_slots
|
|
137
|
+
if slots.zero?
|
|
138
|
+
@cpu.stall!
|
|
139
|
+
else
|
|
140
|
+
clock_cpu(ted)
|
|
141
|
+
clock_cpu(ted) if slots == 2
|
|
142
|
+
end
|
|
143
|
+
@drive1541&.host_cycle!
|
|
144
|
+
@drive1581&.host_cycle!
|
|
145
|
+
|
|
146
|
+
@cycles += 1
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# The RESET line: the CPU, the port, the 6529s, the ACIA, the bank
|
|
150
|
+
# latch and ROM selection, and through the serial bus's RESET line, the
|
|
151
|
+
# drives. The TED has no reset pin.
|
|
152
|
+
def reset!
|
|
153
|
+
@port.reset!
|
|
154
|
+
@keyboard_port.reset!
|
|
155
|
+
@user_port&.reset!
|
|
156
|
+
@dos&.reset!
|
|
157
|
+
@drive1541&.reset!
|
|
158
|
+
@drive1581&.reset!
|
|
159
|
+
@acia&.reset!
|
|
160
|
+
@tias.each(&:reset!)
|
|
161
|
+
@bus.reset!
|
|
162
|
+
@cpu.reset!
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
# Switches the machine off and on: RAM, the TED and its sound start
|
|
166
|
+
# from their power-on state, and the RES line resets everything else.
|
|
167
|
+
def power_cycle!
|
|
168
|
+
@bus.power_on!
|
|
169
|
+
@ted.power_on!
|
|
170
|
+
@sound.power_on!
|
|
171
|
+
reset!
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
# Calls the block with the exit code a VICE testprog writes to $FDCF.
|
|
175
|
+
def install_debug_register(&) = @bus.install_debug_register(&)
|
|
176
|
+
|
|
177
|
+
# Puts a cartridge's ROM chips in their slots (Bus#attach_cartridge)
|
|
178
|
+
# and switches the machine off and on, as a cartridge goes in with the
|
|
179
|
+
# power off. The KERNAL starts it at reset when it signs itself with CBM
|
|
180
|
+
# at $8007.
|
|
181
|
+
def attach_cartridge(chips)
|
|
182
|
+
@bus.attach_cartridge(chips)
|
|
183
|
+
power_cycle!
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
# Takes both cartridges out (Bus#detach_cartridges) and switches the
|
|
187
|
+
# machine off and on.
|
|
188
|
+
def detach_cartridges
|
|
189
|
+
@bus.detach_cartridges
|
|
190
|
+
power_cycle!
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
# The cartridge slots with a ROM in them (Bus#cartridge_slots).
|
|
194
|
+
def cartridge_slots = @bus.cartridge_slots
|
|
195
|
+
|
|
196
|
+
# The model and its RAM as the pause menu names them, such as
|
|
197
|
+
# "PLUS/4 PAL, 64K RAM".
|
|
198
|
+
def model_label
|
|
199
|
+
name = @model.plus4_parts ? "PLUS/4" : "C16"
|
|
200
|
+
standard = @model.ted == Chips::TED::NTSC ? "NTSC" : "PAL"
|
|
201
|
+
"#{name} #{standard}, #{@ram_kb}K RAM"
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
# Stores a PRG's bytes from its load address on, into RAM, and returns
|
|
205
|
+
# that address.
|
|
206
|
+
def load_prg(data)
|
|
207
|
+
load_addr = uint16(data[0], data[1])
|
|
208
|
+
bytes = data[2..] || []
|
|
209
|
+
bytes = bytes[0, 0x10000 - load_addr] if load_addr + bytes.length > 0x10000
|
|
210
|
+
bytes.each_with_index { |byte, i| @bus.ram_view.poke(load_addr + i, byte) }
|
|
211
|
+
load_addr
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
private
|
|
215
|
+
|
|
216
|
+
# The 264 KERNAL's keyboard buffer.
|
|
217
|
+
def keyboard_buffer = Machine::KeyboardBuffer::PLUS4_LAYOUT
|
|
218
|
+
|
|
219
|
+
# The 264 KERNAL, which the traps stand in for.
|
|
220
|
+
def trap_layout = KernalTrap::PLUS4_LAYOUT
|
|
221
|
+
|
|
222
|
+
def trap_bus = @bus
|
|
223
|
+
|
|
224
|
+
# A drive that powers on with the machine takes longer to boot than
|
|
225
|
+
# the KERNAL, and doesn't answer the bus until it has, so the
|
|
226
|
+
# machine's on_init handlers wait for it too.
|
|
227
|
+
def drive1541_attached
|
|
228
|
+
@init_threshold = [@init_threshold, DRIVE_BOOT_CYCLES].max if @cycles < @init_threshold
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
def drive1581_attached
|
|
232
|
+
@init_threshold = [@init_threshold, Drives::Model1581::BOOT_CYCLES].max if @cycles < @init_threshold
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
# BA stops the CPU at a read, and a write goes ahead.
|
|
236
|
+
def clock_cpu(ted)
|
|
237
|
+
ted.ba_low? && !@cpu.pending_write? ? @cpu.stall! : @cpu.cycle!
|
|
238
|
+
end
|
|
239
|
+
end
|
|
240
|
+
end
|