Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,306 changes: 1,306 additions & 0 deletions src/a4091.rs

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions src/bus.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3700,8 +3700,8 @@ impl Bus {
paula,
..
} = self;
for dev in devices.iter_mut() {
let mut host = crate::zorro_device::DeviceHost::new(&mut *mem);
for (slot, dev) in devices.iter_mut().enumerate() {
let mut host = crate::zorro_device::DeviceHost::for_slot(&mut *mem, slot);
crate::zorro_device::ZorroDevice::tick(dev, cck, &mut host);
if crate::zorro_device::ZorroDevice::int2_line(dev) {
paula.intreq |= INT_PORTS;
Expand Down
65 changes: 65 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,10 @@ pub struct Config {
/// drive images on SCSI IDs 0-6. Works on any machine model (the board
/// autoconfigs on the Zorro chain and carries its own scsi.device).
pub scsi: ScsiConfig,
/// A4091 SCSI-2 controller (`[a4091]`): boot ROM image plus up to seven
/// drive images on SCSI IDs 0-6. A Zorro III board; the 53C710 core is
/// not implemented yet, so drives are parsed but not driven.
pub a4091: A4091Config,
/// A2065 Ethernet board (`[a2065]`): when set, an A2065 NIC autoconfigs on
/// the Zorro chain using the named host network backend. Networking is
/// non-deterministic, so a fitted A2065 breaks byte-identical replay.
Expand Down Expand Up @@ -308,6 +312,21 @@ impl ScsiConfig {
}
}

#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct A4091Config {
/// A4091 boot ROM image (a raw 32K or 64K byte-wide EPROM dump).
pub rom: Option<PathBuf>,
/// Drive images by SCSI ID (0-6; ID 7 is the controller).
pub units: [Option<DriveImage>; 7],
}

impl A4091Config {
/// Whether an `[a4091]` section asked for the board at all.
pub fn enabled(&self) -> bool {
self.rom.is_some() || self.units.iter().any(Option::is_some)
}
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Emulation {
/// Whether the machine starts running (powered on) at launch. When
Expand Down Expand Up @@ -880,6 +899,7 @@ impl Default for Config {
audio: AudioConfig::default(),
ide: IdeConfig::default(),
scsi: ScsiConfig::default(),
a4091: A4091Config::default(),
a2065_net: None,
floppy: FloppyConfig::default(),
floppy_connected: [true, false, false, false],
Expand Down Expand Up @@ -1145,6 +1165,8 @@ pub struct RawConfig {
#[serde(default, skip_serializing_if = "is_default")]
pub(crate) scsi: RawScsi,
#[serde(default, skip_serializing_if = "is_default")]
pub(crate) a4091: RawA4091,
#[serde(default, skip_serializing_if = "is_default")]
pub(crate) a2065: RawA2065,
#[serde(default, skip_serializing_if = "is_default")]
pub(crate) floppy: RawFloppy,
Expand Down Expand Up @@ -1347,6 +1369,29 @@ pub(crate) struct RawScsi {
pub(crate) unit6: Option<RawDrive>,
}

/// `[a4091]` SCSI-2 controller: boot ROM image plus drive images by SCSI ID.
#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub(crate) struct RawA4091 {
/// A4091 boot ROM image (raw 32K or 64K byte-wide EPROM dump).
#[serde(skip_serializing_if = "Option::is_none")]
pub(crate) rom: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub(crate) unit0: Option<RawDrive>,
#[serde(skip_serializing_if = "Option::is_none")]
pub(crate) unit1: Option<RawDrive>,
#[serde(skip_serializing_if = "Option::is_none")]
pub(crate) unit2: Option<RawDrive>,
#[serde(skip_serializing_if = "Option::is_none")]
pub(crate) unit3: Option<RawDrive>,
#[serde(skip_serializing_if = "Option::is_none")]
pub(crate) unit4: Option<RawDrive>,
#[serde(skip_serializing_if = "Option::is_none")]
pub(crate) unit5: Option<RawDrive>,
#[serde(skip_serializing_if = "Option::is_none")]
pub(crate) unit6: Option<RawDrive>,
}

/// `[a2065]` Ethernet board. Fitting the board enables host networking, which
/// is non-deterministic.
#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
Expand Down Expand Up @@ -1822,6 +1867,25 @@ impl TryFrom<RawConfig> for Config {
errors.push(anyhow!("[scsi] rom_odd needs rom (the even EPROM half)"));
}

let a4091 = A4091Config {
rom: raw.a4091.rom.map(PathBuf::from),
units: [
raw.a4091.unit0.map(drive_image).transpose()?,
raw.a4091.unit1.map(drive_image).transpose()?,
raw.a4091.unit2.map(drive_image).transpose()?,
raw.a4091.unit3.map(drive_image).transpose()?,
raw.a4091.unit4.map(drive_image).transpose()?,
raw.a4091.unit5.map(drive_image).transpose()?,
raw.a4091.unit6.map(drive_image).transpose()?,
],
};
if a4091.enabled() && a4091.rom.is_none() {
errors.push(anyhow!(
"[a4091] drives need the boot ROM: set [a4091] rom = \"...\" \
(a raw A4091 EPROM image, e.g. the open-source a4091.rom)"
));
}

let a2065_net = match &raw.a2065.net {
None => None,
Some(s) => Some(crate::net::parse_net_config(s).ok_or_else(|| {
Expand Down Expand Up @@ -1945,6 +2009,7 @@ impl TryFrom<RawConfig> for Config {
audio,
ide,
scsi,
a4091,
a2065_net,
floppy,
floppy_connected,
Expand Down
4 changes: 2 additions & 2 deletions src/cpu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2356,7 +2356,7 @@ impl CpuBus {
let (value, activity) = {
let mem = &mut self.bus.mem;
let dev = &mut self.bus.devices[slot];
let mut host = crate::zorro_device::DeviceHost::new(mem);
let mut host = crate::zorro_device::DeviceHost::for_slot(mem, slot);
let v = crate::zorro_device::ZorroDevice::read(dev, off, size, &mut host);
(v, crate::zorro_device::ZorroDevice::take_activity(dev))
};
Expand Down Expand Up @@ -2559,7 +2559,7 @@ impl CpuBus {
let activity = {
let mem = &mut self.bus.mem;
let dev = &mut self.bus.devices[slot];
let mut host = crate::zorro_device::DeviceHost::new(mem);
let mut host = crate::zorro_device::DeviceHost::for_slot(mem, slot);
crate::zorro_device::ZorroDevice::write(dev, off, size, value, &mut host);
crate::zorro_device::ZorroDevice::take_activity(dev)
};
Expand Down
24 changes: 24 additions & 0 deletions src/emulator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1791,6 +1791,30 @@ pub fn build_machine(
);
devices.push(crate::zorro_device::BoardDevice::A2091(board));
}
if cfg.a4091.enabled() {
let rom_path = cfg
.a4091
.rom
.as_ref()
.expect("config validated [a4091] rom");
let rom = crate::a4091::A4091::load_rom(rom_path)?;
let mut board = crate::a4091::A4091::new(rom)?;
for (unit, drive) in cfg.a4091.units.iter().enumerate() {
let Some(drive) = drive else { continue };
board.attach_drive(
unit,
crate::scsi::ScsiDisk::open(&drive.path, unit, drive.volume_name.as_deref())?,
);
info!("a4091: unit {unit} {}", drive.path.display());
}
let slot = devices.len();
zorro.add_board(crate::zorro::BoardSpec::a4091(slot))?;
info!(
"a4091: SCSI controller on the Zorro chain (slot {slot}), ROM {}",
rom_path.display()
);
devices.push(crate::zorro_device::BoardDevice::A4091(board));
}
// WASM plugin boards: assign each a device slot, put its autoconfig
// identity on the chain, and instantiate the module.
for wb in &cfg.wasm_boards {
Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

pub mod a2065;
pub mod a2091;
pub mod a4091;
pub mod akiko;
pub mod amigaos;
pub mod audio;
Expand Down
20 changes: 20 additions & 0 deletions src/zorro.rs
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,26 @@ impl BoardSpec {
}
}

/// The A4091 SCSI-2 controller board: Commodore (manufacturer 514),
/// product 84, a 16M Zorro III window with the DiagArea vector at $0200
/// in the nibble-wide boot ROM. The same identity is baked into the
/// first bytes of the physical EPROM; here the chain supplies it, as it
/// does for every board. `slot` is the index of the matching `A4091`
/// device in `Bus::devices`.
pub fn a4091(slot: usize) -> Self {
Self {
name: "A4091 SCSI".into(),
version: ZorroVersion::III,
manufacturer: 514,
product: 84,
serial: 0,
size_bytes: 0x0100_0000,
backing: BoardBacking::Device(slot),
memlist: false,
diag_vec: Some(0x0200),
}
}

fn validate(&self) -> Result<()> {
match self.version {
ZorroVersion::II => {
Expand Down
38 changes: 38 additions & 0 deletions src/zorro_device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,13 +110,38 @@ pub struct DeviceHost<'a> {
mem: &'a mut Memory,
/// Paula's CD-audio ring, available only on a host built for the CDTV tick.
cd_audio: Option<&'a mut CdAudioRing>,
/// The device slot this host was built for, so a bus-mastering board
/// can recognize DMA addresses inside its own configured window (the
/// A4091 self-test DMAs its own registers) without re-entering itself.
self_slot: Option<usize>,
}

impl<'a> DeviceHost<'a> {
pub fn new(mem: &'a mut Memory) -> Self {
Self {
mem,
cd_audio: None,
self_slot: None,
}
}

/// A host view that knows which device slot it serves. See `self_slot`.
pub fn for_slot(mem: &'a mut Memory, slot: usize) -> Self {
Self {
mem,
cd_audio: None,
self_slot: Some(slot),
}
}

/// The window offset when `addr` falls inside the calling device's own
/// configured board window, `None` otherwise (or when the host was not
/// built with a slot).
pub fn own_window_offset(&self, addr: u32) -> Option<u32> {
let slot = self.self_slot?;
match self.mem.zorro.device_region_at(addr, 1) {
Some((crate::zorro::BoardBacking::Device(s), off)) if s == slot => Some(off),
_ => None,
}
}

Expand All @@ -126,6 +151,7 @@ impl<'a> DeviceHost<'a> {
Self {
mem,
cd_audio: Some(cd_audio),
self_slot: None,
}
}

Expand Down Expand Up @@ -262,6 +288,7 @@ pub trait ZorroDevice {
#[derive(serde::Serialize, serde::Deserialize)]
pub enum BoardDevice {
A2091(crate::a2091::A2091),
A4091(crate::a4091::A4091),
A2065(crate::a2065::A2065),
Wasm(crate::wasmboard::WasmBoard),
}
Expand All @@ -270,6 +297,7 @@ impl ZorroDevice for BoardDevice {
fn read(&mut self, off: u32, size: usize, host: &mut DeviceHost) -> u32 {
match self {
BoardDevice::A2091(d) => ZorroDevice::read(d, off, size, host),
BoardDevice::A4091(d) => ZorroDevice::read(d, off, size, host),
BoardDevice::A2065(d) => ZorroDevice::read(d, off, size, host),
BoardDevice::Wasm(d) => ZorroDevice::read(d, off, size, host),
}
Expand All @@ -278,6 +306,7 @@ impl ZorroDevice for BoardDevice {
fn write(&mut self, off: u32, size: usize, value: u32, host: &mut DeviceHost) {
match self {
BoardDevice::A2091(d) => ZorroDevice::write(d, off, size, value, host),
BoardDevice::A4091(d) => ZorroDevice::write(d, off, size, value, host),
BoardDevice::A2065(d) => ZorroDevice::write(d, off, size, value, host),
BoardDevice::Wasm(d) => ZorroDevice::write(d, off, size, value, host),
}
Expand All @@ -286,6 +315,7 @@ impl ZorroDevice for BoardDevice {
fn peek_word(&self, off: u32) -> Option<u16> {
match self {
BoardDevice::A2091(d) => ZorroDevice::peek_word(d, off),
BoardDevice::A4091(d) => ZorroDevice::peek_word(d, off),
BoardDevice::A2065(d) => ZorroDevice::peek_word(d, off),
BoardDevice::Wasm(d) => ZorroDevice::peek_word(d, off),
}
Expand All @@ -294,6 +324,7 @@ impl ZorroDevice for BoardDevice {
fn tick(&mut self, cck: u32, host: &mut DeviceHost) {
match self {
BoardDevice::A2091(d) => ZorroDevice::tick(d, cck, host),
BoardDevice::A4091(d) => ZorroDevice::tick(d, cck, host),
BoardDevice::A2065(d) => ZorroDevice::tick(d, cck, host),
BoardDevice::Wasm(d) => ZorroDevice::tick(d, cck, host),
}
Expand All @@ -302,6 +333,7 @@ impl ZorroDevice for BoardDevice {
fn int2_line(&self) -> bool {
match self {
BoardDevice::A2091(d) => ZorroDevice::int2_line(d),
BoardDevice::A4091(d) => ZorroDevice::int2_line(d),
BoardDevice::A2065(d) => ZorroDevice::int2_line(d),
BoardDevice::Wasm(d) => ZorroDevice::int2_line(d),
}
Expand All @@ -310,6 +342,7 @@ impl ZorroDevice for BoardDevice {
fn int6_line(&self) -> bool {
match self {
BoardDevice::A2091(d) => ZorroDevice::int6_line(d),
BoardDevice::A4091(d) => ZorroDevice::int6_line(d),
BoardDevice::A2065(d) => ZorroDevice::int6_line(d),
BoardDevice::Wasm(d) => ZorroDevice::int6_line(d),
}
Expand All @@ -318,6 +351,7 @@ impl ZorroDevice for BoardDevice {
fn is_idle(&self) -> bool {
match self {
BoardDevice::A2091(d) => ZorroDevice::is_idle(d),
BoardDevice::A4091(d) => ZorroDevice::is_idle(d),
BoardDevice::A2065(d) => ZorroDevice::is_idle(d),
BoardDevice::Wasm(d) => ZorroDevice::is_idle(d),
}
Expand All @@ -326,6 +360,7 @@ impl ZorroDevice for BoardDevice {
fn next_event_cck(&self) -> Option<u32> {
match self {
BoardDevice::A2091(d) => ZorroDevice::next_event_cck(d),
BoardDevice::A4091(d) => ZorroDevice::next_event_cck(d),
BoardDevice::A2065(d) => ZorroDevice::next_event_cck(d),
BoardDevice::Wasm(d) => ZorroDevice::next_event_cck(d),
}
Expand All @@ -334,6 +369,7 @@ impl ZorroDevice for BoardDevice {
fn take_activity(&mut self) -> bool {
match self {
BoardDevice::A2091(d) => ZorroDevice::take_activity(d),
BoardDevice::A4091(d) => ZorroDevice::take_activity(d),
BoardDevice::A2065(d) => ZorroDevice::take_activity(d),
BoardDevice::Wasm(d) => ZorroDevice::take_activity(d),
}
Expand All @@ -342,6 +378,7 @@ impl ZorroDevice for BoardDevice {
fn reset(&mut self) {
match self {
BoardDevice::A2091(d) => ZorroDevice::reset(d),
BoardDevice::A4091(d) => ZorroDevice::reset(d),
BoardDevice::A2065(d) => ZorroDevice::reset(d),
BoardDevice::Wasm(d) => ZorroDevice::reset(d),
}
Expand All @@ -350,6 +387,7 @@ impl ZorroDevice for BoardDevice {
fn kind(&self) -> &'static str {
match self {
BoardDevice::A2091(d) => ZorroDevice::kind(d),
BoardDevice::A4091(d) => ZorroDevice::kind(d),
BoardDevice::A2065(d) => ZorroDevice::kind(d),
BoardDevice::Wasm(d) => ZorroDevice::kind(d),
}
Expand Down
Loading