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
data/lib/badline/kim1.rb
ADDED
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "badline/kim1/cpu"
|
|
4
|
+
require "badline/kim1/rriots"
|
|
5
|
+
require "badline/kim1/tape"
|
|
6
|
+
require "badline/kim1/panel"
|
|
7
|
+
require "badline/kim1/key_entry"
|
|
8
|
+
require "badline/kim1/bus"
|
|
9
|
+
require "badline/kim1/sound"
|
|
10
|
+
require "badline/kim1/teletype"
|
|
11
|
+
require "badline/kim1/host_terminal"
|
|
12
|
+
|
|
13
|
+
module Badline
|
|
14
|
+
# The MOS KIM-1: a 6502 at 1 MHz, 1K of RAM and two 6530 RRIOTs, U2
|
|
15
|
+
# with the monitor in its ROM and U3 with the tape routines (KIM1::Bus).
|
|
16
|
+
# U2's ports drive the keypad and the six LED digits (Panel), and its PB7
|
|
17
|
+
# the audio output (Sound) and the cassette (Tape). Nothing pulls IRQ
|
|
18
|
+
# unless U3's PB7 is wired to it, as the User Manual's appendix H has the
|
|
19
|
+
# user do for U3's timer.
|
|
20
|
+
#
|
|
21
|
+
# RS resets the CPU and both 6530s, and ST pulls NMI, which the monitor
|
|
22
|
+
# leaves to the vector at $17FA: $1C00 there saves the registers and
|
|
23
|
+
# shows the PC. With the SST switch on, every instruction outside the
|
|
24
|
+
# monitor ends in an NMI (CPU#single_step). With the TTY/KB jumper
|
|
25
|
+
# fitted, the monitor talks to a teletype (Teletype) in place of the
|
|
26
|
+
# keypad and display. It runs headless.
|
|
27
|
+
class KIM1
|
|
28
|
+
include Machine::Clocking
|
|
29
|
+
include Machine::Settling
|
|
30
|
+
include KeyEntry
|
|
31
|
+
|
|
32
|
+
# The cycle by which the monitor is scanning the display, with a
|
|
33
|
+
# margin.
|
|
34
|
+
BOOT_CYCLES = 10_000
|
|
35
|
+
|
|
36
|
+
attr_reader :bus, :cpu, :u2, :u3, :panel, :teletype, :cycles, :init_threshold
|
|
37
|
+
|
|
38
|
+
# Whether the SST switch is on.
|
|
39
|
+
attr_accessor :sst
|
|
40
|
+
|
|
41
|
+
# Whether U3's PB7, its timer's interrupt, is wired to IRQ.
|
|
42
|
+
attr_accessor :irq_wired
|
|
43
|
+
|
|
44
|
+
# The 1 MHz clock. With no video, a frame is nominal: 64 by 260
|
|
45
|
+
# cycles, some 60 a second.
|
|
46
|
+
TIMING = Timing.new(clock_hz: 1_000_000, cycles_per_line: 64, lines_per_frame: 260, crop: [0, 0, 0, 0],
|
|
47
|
+
pixel_width: 1, dots: 1)
|
|
48
|
+
|
|
49
|
+
def family = :kim1
|
|
50
|
+
|
|
51
|
+
def ram = @bus.ram
|
|
52
|
+
|
|
53
|
+
# Reads and writes memory as the CPU sees it, through the bus.
|
|
54
|
+
def peek(addr) = @bus.peek(addr)
|
|
55
|
+
|
|
56
|
+
def poke(addr, value) = @bus.poke(addr, value)
|
|
57
|
+
|
|
58
|
+
# With +tty+ the TTY/KB jumper is fitted, and the monitor talks to a
|
|
59
|
+
# teletype at +baud+ (Teletype::BAUDS) in place of the keypad and
|
|
60
|
+
# display. +ram+ names the RAM, one of Bus::RAM_CONFIGURATIONS, or
|
|
61
|
+
# nil for the stock 1K.
|
|
62
|
+
def initialize(tty: false, baud: 9600, ram: nil)
|
|
63
|
+
@cycles = 0
|
|
64
|
+
@panel = Panel.new(self)
|
|
65
|
+
@panel.tty = tty
|
|
66
|
+
@u2 = MonitorRRIOT.new(rom: ROM.read("kim1/6530-002.rom"), peripheral: @panel)
|
|
67
|
+
@u3 = UserRRIOT.new(rom: ROM.read("kim1/6530-003.rom"))
|
|
68
|
+
@panel.connect(@u2)
|
|
69
|
+
@bus = Bus.new(@u2, @u3, ram: ram || :"1k")
|
|
70
|
+
@cpu = CPU.new(@bus)
|
|
71
|
+
@sst = false
|
|
72
|
+
@irq_wired = false
|
|
73
|
+
@sound = Sound.new(@u2, @u3)
|
|
74
|
+
@teletype = Teletype.new(@panel, baud:)
|
|
75
|
+
@teletype.reset!(0) if tty
|
|
76
|
+
@init_handlers = []
|
|
77
|
+
@init_threshold = BOOT_CYCLES
|
|
78
|
+
@pending_keys = nil
|
|
79
|
+
@key_down = false
|
|
80
|
+
@next_key_at = 0
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Whether the TTY/KB jumper is fitted.
|
|
84
|
+
def tty? = @panel.tty
|
|
85
|
+
|
|
86
|
+
# Fits or takes out the TTY/KB jumper.
|
|
87
|
+
def tty=(fitted)
|
|
88
|
+
@panel.tty = fitted
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def timing = TIMING
|
|
92
|
+
|
|
93
|
+
# Types `text` on the teletype with the jumper fitted, and otherwise
|
|
94
|
+
# presses the keys it names (KeyEntry#type_keys).
|
|
95
|
+
def type_text(text)
|
|
96
|
+
return type_keys(text) unless @panel.tty
|
|
97
|
+
|
|
98
|
+
@teletype.type(text)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# What the teletype has printed: #output holds it all.
|
|
102
|
+
def capture_output = @teletype
|
|
103
|
+
|
|
104
|
+
# What a front end plays: U2's PB7 or the speaker (Sound), which
|
|
105
|
+
# answers #record and #drain_samples.
|
|
106
|
+
def sound_source = @sound
|
|
107
|
+
|
|
108
|
+
# Whether the speaker on U3's PA0 is wired, and plays in place of PB7.
|
|
109
|
+
def speaker? = @sound.speaker
|
|
110
|
+
|
|
111
|
+
def speaker=(wired)
|
|
112
|
+
@sound.speaker = wired
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# The machine as the pause menu names it.
|
|
116
|
+
def model_label = "KIM-1, #{ram_kb}K RAM"
|
|
117
|
+
|
|
118
|
+
# The RAM in K (Bus#ram_kb).
|
|
119
|
+
def ram_kb = @bus.ram_kb
|
|
120
|
+
|
|
121
|
+
# No drive plugs into the KIM-1, and nothing is mounted.
|
|
122
|
+
def true_drive = nil
|
|
123
|
+
|
|
124
|
+
def mounted? = false
|
|
125
|
+
|
|
126
|
+
def mounted_path = ""
|
|
127
|
+
|
|
128
|
+
# The 6530s count ahead of the CPU. With SST on, the board pulls NMI
|
|
129
|
+
# ahead of an opcode fetch outside the monitor.
|
|
130
|
+
def cycle!
|
|
131
|
+
handle_init if @cycles == @init_threshold
|
|
132
|
+
feed_keys unless @pending_keys.nil?
|
|
133
|
+
|
|
134
|
+
@u2.cycle!
|
|
135
|
+
@u3.cycle!
|
|
136
|
+
@sound.cycle! if @sound.recording?
|
|
137
|
+
@teletype.cycle!(@cycles, @u2.port_b_output.anybits?(0x01) && @teletype.line_high?) if @panel.tty
|
|
138
|
+
@cpu.irq = @irq_wired && @u3.irq?
|
|
139
|
+
@cpu.single_step if @sst
|
|
140
|
+
@cpu.cycle!
|
|
141
|
+
|
|
142
|
+
@cycles += 1
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
# RS: the RST line resets the CPU and both 6530s. RAM keeps what it
|
|
146
|
+
# holds. A teletype then sends RUBOUT for the monitor to time.
|
|
147
|
+
def reset!
|
|
148
|
+
@u2.reset!
|
|
149
|
+
@u3.reset!
|
|
150
|
+
@cpu.reset!
|
|
151
|
+
@teletype.reset!(@cycles) if @panel.tty
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
alias press_reset reset!
|
|
155
|
+
|
|
156
|
+
# Switches the machine off and on: RAM starts cleared, and RS resets
|
|
157
|
+
# everything else.
|
|
158
|
+
def power_cycle!
|
|
159
|
+
@bus.power_on!
|
|
160
|
+
reset!
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
# ST: an edge on NMI.
|
|
164
|
+
def press_stop
|
|
165
|
+
@cpu.nmi = true
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# Holds down the key Panel::KEYS names `name`.
|
|
169
|
+
def press_key(name)
|
|
170
|
+
@panel.press(name)
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def release_key
|
|
174
|
+
@panel.release
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
# The segments each LED digit shows (Panel#display).
|
|
178
|
+
def display = @panel.display
|
|
179
|
+
|
|
180
|
+
# The display as text, such as "0200 A9" (Panel#display_text).
|
|
181
|
+
def display_text = @panel.display_text
|
|
182
|
+
|
|
183
|
+
# The cassette deck on U2's PB7.
|
|
184
|
+
def tape = @panel.tape
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
require "badline/snapshot/kim1_setup"
|
|
189
|
+
require "badline/kim1/keyed_state"
|
|
190
|
+
require "badline/kim1/keyed_modules"
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "badline/drives/ieee_models"
|
|
4
|
+
require "badline/drives/model2031"
|
|
5
|
+
require "badline/drives/model8050"
|
|
6
|
+
require "badline/drives/model9060"
|
|
7
|
+
require "badline/drives/model1001"
|
|
8
|
+
require "badline/drives/model4040"
|
|
9
|
+
|
|
10
|
+
module Badline
|
|
11
|
+
module Machine
|
|
12
|
+
# A machine's true drive on its IEEE-488 bus, one at a time, of a model
|
|
13
|
+
# Drives::IEEEModels names: a Drives::Model2031, Model8050, Model8250,
|
|
14
|
+
# Model1001, Model4040, Model3040, Model2040, Model9060 or Model9090. It
|
|
15
|
+
# is clocked after the CPU each cycle at the machine's clock
|
|
16
|
+
# (ieee_host_clock_hz) and reset with it, as the machine's RES line
|
|
17
|
+
# reaches IFC. The traps on the KERNAL's primitives leave device 8 to
|
|
18
|
+
# the drive while it is device 8 (trap_device). The PET (PET::IEEEDrive) and the CBM-II
|
|
19
|
+
# (CBM2::IEEEDrive) include it.
|
|
20
|
+
#
|
|
21
|
+
# The machine's @ieee is its side of the bus: #bus, the
|
|
22
|
+
# Drives::IEEEBus, #push_lines, which shows the drives the lines the
|
|
23
|
+
# machine pulls now, and #cycle_lines, which does so once a cycle if
|
|
24
|
+
# the machine doesn't push them as its chips are written.
|
|
25
|
+
module IEEEDrives
|
|
26
|
+
# The true drive on the bus, or nil.
|
|
27
|
+
attr_reader :true_drive
|
|
28
|
+
|
|
29
|
+
# The model plug_true_drive plugs in, one Drives::IEEEModels names.
|
|
30
|
+
attr_reader :true_drive_model
|
|
31
|
+
|
|
32
|
+
# Plugs in the drive, in place of the drive there.
|
|
33
|
+
def attach_ieee_drive(drive)
|
|
34
|
+
detach_ieee_drive
|
|
35
|
+
drive.host_clock_hz = ieee_host_clock_hz
|
|
36
|
+
drive.connect(@ieee.bus)
|
|
37
|
+
@ieee.push_lines
|
|
38
|
+
@true_drive = drive
|
|
39
|
+
@ieee_trap&.device = trap_device
|
|
40
|
+
@init_threshold = [@init_threshold, drive.boot_cycles].max if @cycles < @init_threshold
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Unplugs the true drive, leaving it on a bus of its own.
|
|
44
|
+
def detach_ieee_drive
|
|
45
|
+
drive = @true_drive
|
|
46
|
+
return unless drive
|
|
47
|
+
|
|
48
|
+
drive.connect(Badline::Drives::IEEEBus.new)
|
|
49
|
+
@true_drive = nil
|
|
50
|
+
@ieee_trap&.device = trap_device
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Plugs in the model as +device+, in place of the drive there, and
|
|
54
|
+
# returns it.
|
|
55
|
+
def plug_ieee_drive(model, device: 8)
|
|
56
|
+
build_ieee_drive(model, device).tap { |drive| attach_ieee_drive(drive) }
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# The drive of a dual unit a disk image given on its own goes in
|
|
60
|
+
# (Media::TrueDrive): drive 0.
|
|
61
|
+
def disk_drive_number = 0
|
|
62
|
+
|
|
63
|
+
# Plugs in the model true_drive_model names as device 8, in place of
|
|
64
|
+
# the drive there, and returns it.
|
|
65
|
+
def plug_true_drive = plug_ieee_drive(@true_drive_model)
|
|
66
|
+
|
|
67
|
+
# Settles for a snapshot, then runs on until the true drive is where
|
|
68
|
+
# VICE saves it (the drive's settled_for_vice?) between instructions
|
|
69
|
+
# with the machine's, or for so many cycles, then on to the machine's
|
|
70
|
+
# next instruction. It stands in for Machine::Settling's, which the
|
|
71
|
+
# machines include first.
|
|
72
|
+
def settle_for_vice!
|
|
73
|
+
ran = settle_for_snapshot!
|
|
74
|
+
drive = @true_drive
|
|
75
|
+
return ran if drive.nil?
|
|
76
|
+
|
|
77
|
+
until ran >= Machine::Settling::LIMIT || (drive.settled_for_vice? && settled_for_snapshot?)
|
|
78
|
+
cycle!
|
|
79
|
+
ran += 1
|
|
80
|
+
end
|
|
81
|
+
ran + settle_for_snapshot!
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
private
|
|
85
|
+
|
|
86
|
+
def init_ieee_drives(true_drive_model)
|
|
87
|
+
unless Badline::Drives::IEEEModels::NAMES.include?(true_drive_model)
|
|
88
|
+
raise ArgumentError, "no true drive model #{true_drive_model} for the IEEE-488 bus"
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
@true_drive_model = true_drive_model
|
|
92
|
+
@true_drive = nil
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# A new drive of the model as +device+.
|
|
96
|
+
def build_ieee_drive(model, device)
|
|
97
|
+
case model
|
|
98
|
+
when "8050" then Badline::Drives::Model8050.new(device:)
|
|
99
|
+
when "8250" then Badline::Drives::Model8250.new(device:)
|
|
100
|
+
when "1001" then Badline::Drives::Model1001.new(device:)
|
|
101
|
+
when "4040" then Badline::Drives::Model4040.new(device:)
|
|
102
|
+
when "3040" then Badline::Drives::Model3040.new(device:)
|
|
103
|
+
when "2040" then Badline::Drives::Model2040.new(device:)
|
|
104
|
+
when "9060" then Badline::Drives::Model9060.new(device:)
|
|
105
|
+
when "9090" then Badline::Drives::Model9090.new(device:)
|
|
106
|
+
else Badline::Drives::Model2031.new(device:)
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# The true drive's cycles for one of the machine's, after the lines
|
|
111
|
+
# it pulls.
|
|
112
|
+
def clock_ieee_drives
|
|
113
|
+
drive = @true_drive
|
|
114
|
+
return unless drive
|
|
115
|
+
|
|
116
|
+
@ieee.cycle_lines
|
|
117
|
+
drive.host_cycle!
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# The RES line reaches the true drive as IFC.
|
|
121
|
+
def reset_ieee_drives
|
|
122
|
+
@true_drive&.reset!
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def save_ieee_drive_fields(out)
|
|
126
|
+
out.text("host.ieee_drive_model", @true_drive_model)
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def load_ieee_drive_fields(input)
|
|
130
|
+
@true_drive_model = input.choice("host.ieee_drive_model", Badline::Drives::IEEEModels::NAMES,
|
|
131
|
+
@true_drive_model)
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# The true drive as keyed snapshot modules named from its device
|
|
135
|
+
# number: BL-D8, BL-D8-CPU and so on for device 8.
|
|
136
|
+
def save_drive_modules(sections)
|
|
137
|
+
drive = @true_drive
|
|
138
|
+
drive&.save_modules(sections, "BL-D#{drive.device}")
|
|
139
|
+
sections
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
# The true drive a snapshot's modules hold, of the model they name,
|
|
143
|
+
# built afresh unless the same model is on the bus as the same
|
|
144
|
+
# device, or none when they hold no drive. The bus then carries the
|
|
145
|
+
# machine's lines as its chips left them.
|
|
146
|
+
def load_drive_modules(modules)
|
|
147
|
+
device = 8
|
|
148
|
+
input = modules.optional("BL-D8")
|
|
149
|
+
while input.nil? && device < 15
|
|
150
|
+
device += 1
|
|
151
|
+
input = modules.optional("BL-D#{device}")
|
|
152
|
+
end
|
|
153
|
+
if input
|
|
154
|
+
load_ieee_drive_modules(modules, input, device)
|
|
155
|
+
else
|
|
156
|
+
detach_ieee_drive
|
|
157
|
+
end
|
|
158
|
+
@ieee.push_lines
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def load_ieee_drive_modules(modules, input, device)
|
|
162
|
+
model = input.text("model", Badline::Drives::IEEEModels::DEFAULT)
|
|
163
|
+
model = Badline::Drives::IEEEModels::DEFAULT unless Badline::Drives::IEEEModels::NAMES.include?(model)
|
|
164
|
+
drive = @true_drive
|
|
165
|
+
detach_ieee_drive if drive && (drive.device != device || drive.model_name != model)
|
|
166
|
+
drive = @true_drive || plug_ieee_drive(model, device:)
|
|
167
|
+
drive.load_modules(modules, input, "BL-D#{device}")
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
end
|
|
@@ -28,13 +28,30 @@ module Badline
|
|
|
28
28
|
# The PET's under BASIC 1, outside the zero page.
|
|
29
29
|
PET_BASIC1_LAYOUT = Layout.new(address: 0x020f, count: 0x020d, capacity: 10)
|
|
30
30
|
|
|
31
|
-
def type_text(text)
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
def type_text(text) = type_on_init(text.bytes.map { |b| ascii_to_petscii(b) })
|
|
32
|
+
|
|
33
|
+
# The keys type_text left for the KERNAL to boot first, under
|
|
34
|
+
# host.init_keys, so that a machine restored before then types them.
|
|
35
|
+
def save_init_keys(out)
|
|
36
|
+
keys = @init_keys
|
|
37
|
+
out.ints("host.init_keys", keys) if keys
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def load_init_keys(input)
|
|
41
|
+
keys = input.ints("host.init_keys", [])
|
|
42
|
+
type_on_init(keys.dup) unless keys.empty?
|
|
34
43
|
end
|
|
35
44
|
|
|
36
45
|
private
|
|
37
46
|
|
|
47
|
+
def type_on_init(codes)
|
|
48
|
+
@init_keys = codes if booting?
|
|
49
|
+
on_init do
|
|
50
|
+
@init_keys = nil
|
|
51
|
+
@pending_keys = codes
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
38
55
|
def feed_keyboard
|
|
39
56
|
buffer = keyboard_buffer
|
|
40
57
|
return unless ram.peek(buffer.count).zero?
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
module Machine
|
|
5
|
+
# The sound of one logic line a machine drives into its speaker or
|
|
6
|
+
# audio output, such as the PET's CB2 or the KIM-1's PB7. The class
|
|
7
|
+
# that includes it answers #line_high?, the line's level this cycle.
|
|
8
|
+
#
|
|
9
|
+
# The machine calls #cycle! once a cycle while #recording?, from #record
|
|
10
|
+
# on, so a sound nobody records costs only that check. Each sample is
|
|
11
|
+
# the share of its cycles the line spent high, through the output
|
|
12
|
+
# stage: a one-pole low-pass at 5 kHz and a one-pole high-pass at
|
|
13
|
+
# 100 Hz, so a line left high or low settles to silence.
|
|
14
|
+
module LineSound
|
|
15
|
+
LOW_PASS_HZ = 5_000
|
|
16
|
+
HIGH_PASS_HZ = 100
|
|
17
|
+
|
|
18
|
+
# A swing from the line low to the line high, as a signed 16-bit
|
|
19
|
+
# sample.
|
|
20
|
+
SCALE = 24_000.0
|
|
21
|
+
|
|
22
|
+
attr_reader :recording
|
|
23
|
+
alias recording? recording
|
|
24
|
+
|
|
25
|
+
# Starts collecting the output, averaged down to `rate` samples a
|
|
26
|
+
# second at `clock_hz` cycles a second, for #drain_samples. The
|
|
27
|
+
# output stage takes the line's level now as settled.
|
|
28
|
+
def record(rate:, clock_hz: 1_000_000)
|
|
29
|
+
@rate = rate
|
|
30
|
+
@clock_hz = clock_hz
|
|
31
|
+
@remainder = 0
|
|
32
|
+
@window = next_window
|
|
33
|
+
@left = @window
|
|
34
|
+
@sum = 0
|
|
35
|
+
@high = line_high?
|
|
36
|
+
@run = 0
|
|
37
|
+
@low = @drift = @high ? 1.0 : 0.0
|
|
38
|
+
@low_pass = 1.0 - Math.exp(-2.0 * Math::PI * LOW_PASS_HZ / rate)
|
|
39
|
+
@high_pass = 1.0 - Math.exp(-2.0 * Math::PI * HIGH_PASS_HZ / rate)
|
|
40
|
+
@samples = []
|
|
41
|
+
@recording = true
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Takes the line's level for this cycle, while recording.
|
|
45
|
+
def cycle!
|
|
46
|
+
high = line_high?
|
|
47
|
+
if high == @high
|
|
48
|
+
@run += 1
|
|
49
|
+
else
|
|
50
|
+
hold(@high, @run)
|
|
51
|
+
@high = high
|
|
52
|
+
@run = 1
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# The samples closed since the last drain.
|
|
57
|
+
def drain_samples
|
|
58
|
+
return [] unless @recording
|
|
59
|
+
|
|
60
|
+
hold(@high, @run)
|
|
61
|
+
@run = 0
|
|
62
|
+
samples = @samples
|
|
63
|
+
@samples = []
|
|
64
|
+
samples
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
private
|
|
68
|
+
|
|
69
|
+
def init_line_sound
|
|
70
|
+
@recording = false
|
|
71
|
+
@high = true
|
|
72
|
+
@run = 0
|
|
73
|
+
@rate = 0
|
|
74
|
+
@clock_hz = 0
|
|
75
|
+
@remainder = 0
|
|
76
|
+
@window = 0
|
|
77
|
+
@left = 0
|
|
78
|
+
@sum = 0
|
|
79
|
+
@low = 0.0
|
|
80
|
+
@drift = 0.0
|
|
81
|
+
@low_pass = 0.0
|
|
82
|
+
@high_pass = 0.0
|
|
83
|
+
@samples = []
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Adds `cycles` cycles with the line high or low, closing every
|
|
87
|
+
# sample they finish.
|
|
88
|
+
def hold(high, cycles)
|
|
89
|
+
left = @left
|
|
90
|
+
while cycles >= left
|
|
91
|
+
@sum += left if high
|
|
92
|
+
cycles -= left
|
|
93
|
+
close
|
|
94
|
+
left = @left
|
|
95
|
+
end
|
|
96
|
+
@sum += cycles if high
|
|
97
|
+
@left = left - cycles
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def close
|
|
101
|
+
@low += @low_pass * (@sum.fdiv(@window) - @low)
|
|
102
|
+
@drift += @high_pass * (@low - @drift)
|
|
103
|
+
sample = ((@low - @drift) * SCALE).round
|
|
104
|
+
sample = 32_767 if sample > 32_767
|
|
105
|
+
sample = -32_768 if sample < -32_768
|
|
106
|
+
@samples << sample
|
|
107
|
+
@sum = 0
|
|
108
|
+
@window = next_window
|
|
109
|
+
@left = @window
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# Each window is a whole number of cycles, and they add up to the
|
|
113
|
+
# clock's cycles over a second.
|
|
114
|
+
def next_window
|
|
115
|
+
@remainder += @clock_hz
|
|
116
|
+
cycles = @remainder / @rate
|
|
117
|
+
@remainder -= cycles * @rate
|
|
118
|
+
cycles
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
end
|
|
@@ -24,6 +24,10 @@ module Badline
|
|
|
24
24
|
settle_chips_for_snapshot
|
|
25
25
|
ran
|
|
26
26
|
end
|
|
27
|
+
|
|
28
|
+
# Settles as #settle_for_snapshot! does, for a snapshot VICE opens,
|
|
29
|
+
# which a machine with a drive VICE runs settles further.
|
|
30
|
+
def settle_for_vice! = settle_for_snapshot!
|
|
27
31
|
end
|
|
28
32
|
end
|
|
29
33
|
end
|
data/lib/badline/machine.rb
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require "badline/machine/clocking"
|
|
4
4
|
require "badline/machine/keyboard_buffer"
|
|
5
|
+
require "badline/machine/line_sound"
|
|
5
6
|
require "badline/machine/settling"
|
|
6
7
|
|
|
7
8
|
module Badline
|
|
@@ -58,6 +59,11 @@ module Badline
|
|
|
58
59
|
# and +capture_output+, and besides +model_label+, +cartridge_slots+ and
|
|
59
60
|
# +detach_cartridges+ for its two cartridges.
|
|
60
61
|
#
|
|
62
|
+
# The KIM-1 answers the identity, clocking, lifecycle and sound parts,
|
|
63
|
+
# with no video chip: a front end draws its panel (KIM1::Panel), and
|
|
64
|
+
# #true_drive is nil. Its keys are the panel's, through +press_key+,
|
|
65
|
+
# +release_key+, +type_keys+, +press_stop+ and +press_reset+.
|
|
66
|
+
#
|
|
61
67
|
# The PET answers the identity, clocking, lifecycle, video, sound and
|
|
62
68
|
# keyboard parts, with no joysticks, RESTORE or control ports, and of
|
|
63
69
|
# the rest +ram+, +load_prg+, +type_text+, +mount+, +unmount+,
|
|
@@ -69,20 +75,28 @@ module Badline
|
|
|
69
75
|
# Media the machine named doesn't take.
|
|
70
76
|
class Unsupported < ArgumentError; end
|
|
71
77
|
|
|
72
|
-
# A new machine of `family`, :c64, :vic20, :c128, :plus4 or :
|
|
73
|
-
# model of it `model` names. A C64's model is one of Model::ALL, with
|
|
74
|
-
# `sid_model` when not nil and an REU of `reu` K when not nil.
|
|
75
|
-
# VIC-20's is "pal", with the RAM expansion `ram` names
|
|
78
|
+
# A new machine of `family`, :c64, :vic20, :c128, :plus4, :pet, :cbm2 or :kim1, the
|
|
79
|
+
# model of it `model` names. A C64's model is one of Model::ALL, with
|
|
80
|
+
# its SID `sid_model` when not nil and an REU of `reu` K when not nil.
|
|
81
|
+
# A VIC-20's is "pal" or "ntsc", with the RAM expansion `ram` names
|
|
76
82
|
# (Vic20::Bus::RAM_CONFIGURATIONS), unexpanded when nil. A C128's is
|
|
77
83
|
# one of C128::Model::ALL, with its SID `sid_model` when not nil, built
|
|
78
84
|
# for C64 mode. A Plus/4's is one of Plus4::Model::ALL or c116, with
|
|
79
85
|
# the model's own RAM. A PET's is one of PET::Model::ALL, with its own
|
|
80
|
-
# RAM and ROMs, its CRTC's editor the 50 Hz one
|
|
86
|
+
# RAM and ROMs, its CRTC's editor the 50 Hz one, the superpet a
|
|
87
|
+
# PET::SuperPET with its CPU switch at 6502. A CBM-II's is one of
|
|
88
|
+
# CBM2::Model::ALL, with VICE's KERNAL, 901244-04a, at 50 Hz, or a 510,
|
|
89
|
+
# one of CBM2::P500::Model::ALL, with its region's mains. A KIM-1 is
|
|
90
|
+
# the one model, with the RAM `ram` names
|
|
91
|
+
# (KIM1::Bus::RAM_CONFIGURATIONS), 1K when nil.
|
|
81
92
|
def self.build(family, model:, sid_model: nil, reu: nil, ram: nil)
|
|
93
|
+
return KIM1.new(ram:) if family == :kim1
|
|
82
94
|
return vic20(model, ram) if family == :vic20
|
|
83
95
|
return c128(model, sid_model, :c64) if family == :c128
|
|
84
96
|
return Plus4.new(model:) if family == :plus4
|
|
85
|
-
return PET.new(model:) if family == :pet
|
|
97
|
+
return model == "superpet" ? PET::SuperPET.new : PET.new(model:) if family == :pet
|
|
98
|
+
return CBM2::P500.new(model:) if family == :cbm2 && model.start_with?("510")
|
|
99
|
+
return CBM2.new(model:) if family == :cbm2
|
|
86
100
|
raise ArgumentError, "no machine family named #{family}" unless family == :c64
|
|
87
101
|
|
|
88
102
|
profile = Model.named(model)
|
|
@@ -98,12 +112,15 @@ module Badline
|
|
|
98
112
|
# only C64 mode runs: a .sid tune or a program that loads at the C64's
|
|
99
113
|
# BASIC start. A VIC-20 without RAM named gets the expansion the media
|
|
100
114
|
# needs (Media::Vic20Media.ram_for) and yields a line saying so. Raises
|
|
101
|
-
# Unsupported for media a VIC-20, a Plus/4 or a
|
|
115
|
+
# Unsupported for media a VIC-20, a Plus/4, a PET or a CBM-II doesn't
|
|
116
|
+
# take.
|
|
102
117
|
def self.for_options(options, media, &)
|
|
103
118
|
family = options.family
|
|
104
119
|
return vic20_for(media, options.model, options.ram, &) if family == :vic20
|
|
105
120
|
return plus4_for(media, options) if family == :plus4
|
|
106
121
|
return pet_for(media, options) if family == :pet
|
|
122
|
+
return cbm2_for(media, options) if family == :cbm2
|
|
123
|
+
return kim1_for(media, options) if family == :kim1
|
|
107
124
|
|
|
108
125
|
sid_model = options.sid_model || Media.sid_model(media, otherwise: nil)
|
|
109
126
|
return build(:c64, model: options.model, sid_model:, reu: options.reu) unless family == :c128
|
|
@@ -113,11 +130,7 @@ module Badline
|
|
|
113
130
|
machine
|
|
114
131
|
end
|
|
115
132
|
|
|
116
|
-
def self.vic20(model, ram)
|
|
117
|
-
raise ArgumentError, "no VIC-20 model named #{model}" unless model == "pal"
|
|
118
|
-
|
|
119
|
-
Vic20.new(ram: ram || :unexpanded)
|
|
120
|
-
end
|
|
133
|
+
def self.vic20(model, ram) = Vic20.new(ram: ram || :unexpanded, model:)
|
|
121
134
|
private_class_method :vic20
|
|
122
135
|
|
|
123
136
|
def self.c128(model, sid_model, mode) = C128.new(model:, sid_model:, mode:)
|
|
@@ -154,16 +167,51 @@ module Badline
|
|
|
154
167
|
# A PET with the RAM --ram names (PET::Model::RAM_SIZES), or with the
|
|
155
168
|
# model's own without it, the editor at the frame rate --60hz or
|
|
156
169
|
# --editor asks for, the ROMs --basic, --editor and --characters fit it
|
|
157
|
-
# with,
|
|
170
|
+
# with, the 8296 board's jumpers --jumpers names, and the true drive
|
|
171
|
+
# --drive-type names. The SuperPET takes the frame rate, the 8032's
|
|
172
|
+
# BASIC and editors, the true drive, the CPU --cpu names and the bank
|
|
173
|
+
# RAM switch --bank-ram sets, and with the 6809 disks alone.
|
|
158
174
|
def self.pet_for(media, options)
|
|
159
175
|
unsupported = !media.nil? && Media::PETMedia::UNSUPPORTED.include?(File.extname(media).downcase)
|
|
160
176
|
raise Unsupported, "doesn't go in a PET" if unsupported
|
|
161
177
|
|
|
178
|
+
if options.model == "superpet"
|
|
179
|
+
disk = media.nil? || File.directory?(media) || %i[disk ieee_disk].include?(Media::Extensions.kind(media))
|
|
180
|
+
raise Unsupported, "doesn't go in the SuperPET's 6809 side" unless disk || options.cpu == "6502"
|
|
181
|
+
|
|
182
|
+
return PET::SuperPET.new(refresh_hz: options.refresh_hz, basic: options.basic, editor: options.editor,
|
|
183
|
+
cpu: options.cpu, bank_ram: options.bank_ram, true_drive_model: options.drive_type)
|
|
184
|
+
end
|
|
185
|
+
|
|
162
186
|
PET.new(model: options.model, ram_kb: options.ram_kb, refresh_hz: options.refresh_hz, basic: options.basic,
|
|
163
|
-
editor: options.editor, characters: options.characters, jumpers: options.jumpers
|
|
187
|
+
editor: options.editor, characters: options.characters, jumpers: options.jumpers,
|
|
188
|
+
true_drive_model: options.drive_type)
|
|
164
189
|
end
|
|
165
190
|
private_class_method :pet_for
|
|
166
191
|
|
|
192
|
+
# A CBM-II with the KERNAL --kernal names, on the mains --60hz asks
|
|
193
|
+
# for: a B series' 50 Hz without it, and a P500's its region's, and
|
|
194
|
+
# the true drive --drive-type names.
|
|
195
|
+
def self.cbm2_for(media, options)
|
|
196
|
+
unsupported = !media.nil? && Media::CBM2Media::UNSUPPORTED.include?(File.extname(media).downcase)
|
|
197
|
+
raise Unsupported, "doesn't go in a CBM-II" if unsupported
|
|
198
|
+
|
|
199
|
+
drive = options.drive_type
|
|
200
|
+
return CBM2::P500.new(model: options.model, mains_hz: options.mains_hz, true_drive_model: drive) if options.p500?
|
|
201
|
+
|
|
202
|
+
CBM2.new(model: options.model, kernal: options.kernal, mains_hz: options.mains_hz || 50, true_drive_model: drive)
|
|
203
|
+
end
|
|
204
|
+
private_class_method :cbm2_for
|
|
205
|
+
|
|
206
|
+
# A KIM-1 with the RAM --ram names, on a teletype at --baud with
|
|
207
|
+
# --tty, for media it takes (Media::KIM1Media).
|
|
208
|
+
def self.kim1_for(media, options)
|
|
209
|
+
raise Unsupported, "doesn't go in a KIM-1" unless media.nil? || Media::KIM1Media.takes?(media)
|
|
210
|
+
|
|
211
|
+
KIM1.new(tty: options.tty?, baud: options.baud, ram: options.ram)
|
|
212
|
+
end
|
|
213
|
+
private_class_method :kim1_for
|
|
214
|
+
|
|
167
215
|
def self.c64_media?(media)
|
|
168
216
|
return false if media.nil? || !File.file?(media)
|
|
169
217
|
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
module Media
|
|
5
|
+
# What a CBM-II does with media of its own (Media.attach). Disks of
|
|
6
|
+
# every kind, .d80 and .d82 among them, directories and .t64 archives
|
|
7
|
+
# mount on device 8 through the traps on the KERNAL's IEEE-488
|
|
8
|
+
# primitives (CBM2::IEEETraps), programs load into BASIC's text segment
|
|
9
|
+
# (Program), and a CBM2 .crt's ROMs go in at their load addresses in
|
|
10
|
+
# segment 15. There is no datasette, the SID plays no .sid tunes, and
|
|
11
|
+
# the true drives are serial ones.
|
|
12
|
+
module CBM2Media
|
|
13
|
+
# What a CBM-II doesn't take.
|
|
14
|
+
UNSUPPORTED = Extensions.of(%i[tune tape gcr])
|
|
15
|
+
|
|
16
|
+
# What loads and runs the first program on the disk in device 8.
|
|
17
|
+
AUTOSTART = %(load"*",8\rrun\r)
|
|
18
|
+
|
|
19
|
+
# Where BASIC's text starts in segment 1, and the pointers to its end
|
|
20
|
+
# and to the KERNAL's end address (EAL) in segment 15.
|
|
21
|
+
BASIC_START = 0x0003
|
|
22
|
+
TEXT_END = 0x2f
|
|
23
|
+
EAL = 0x96
|
|
24
|
+
|
|
25
|
+
# The ROM chips of a CBM2 .crt.
|
|
26
|
+
def self.cartridge_chips(path) = Storage::CRTFile.new(path, machine: :cbm2).chips
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|