Skip to content
Open
806 changes: 806 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 @@ -3682,8 +3682,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
80 changes: 79 additions & 1 deletion 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 @@ -241,6 +245,10 @@ pub enum SerialMode {
/// Serial in/out is bridged to host MIDI endpoints. Requires a build
/// with the `midi` feature; without it, resolving this mode is an error.
Midi,
/// Serial in/out is bridged to a host TCP port, like UAE's `TCP:`
/// serial device. With an `AUX:` shell on the Amiga side, a connected
/// client gets a remote AmigaDOS console.
Tcp,
}

impl SerialMode {
Expand All @@ -250,6 +258,7 @@ impl SerialMode {
Self::Off => "off",
Self::Stdout => "stdout",
Self::Midi => "midi",
Self::Tcp => "tcp",
}
}
}
Expand All @@ -263,6 +272,9 @@ pub struct SerialConfig {
pub mode: SerialMode,
pub midi_out: Option<String>,
pub midi_in: Option<String>,
/// TCP listen address for [`SerialMode::Tcp`]; `None` means the
/// default `127.0.0.1:1234` (the port UAE's `TCP:` serial uses).
pub listen: Option<String>,
}

/// A configured hard-drive image: the host path plus an optional volume-name
Expand Down Expand Up @@ -300,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 @@ -824,6 +851,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 @@ -1071,6 +1099,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 @@ -1135,6 +1165,9 @@ pub(crate) struct RawSerial {
/// Host MIDI input endpoint name (substring match); MIDI mode only.
#[serde(skip_serializing_if = "Option::is_none")]
pub(crate) midi_in: Option<String>,
/// TCP listen address; tcp mode only. Defaults to 127.0.0.1:1234.
#[serde(skip_serializing_if = "Option::is_none")]
pub(crate) listen: Option<String>,
}

/// A drive image entry in `[ide]`/`[scsi]`. Accepts either a bare path string
Expand Down Expand Up @@ -1263,6 +1296,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 @@ -1667,6 +1723,7 @@ impl TryFrom<RawConfig> for Config {
},
midi_out: raw.serial.midi_out.clone(),
midi_in: raw.serial.midi_in.clone(),
listen: raw.serial.listen.clone(),
};

let ide = IdeConfig {
Expand Down Expand Up @@ -1702,6 +1759,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 @@ -1825,6 +1901,7 @@ impl TryFrom<RawConfig> for Config {
audio,
ide,
scsi,
a4091,
a2065_net,
floppy,
floppy_connected,
Expand Down Expand Up @@ -1873,8 +1950,9 @@ pub(crate) fn parse_serial_mode(s: &str) -> Result<SerialMode> {
"off" | "none" => Ok(SerialMode::Off),
"stdout" | "terminal" => Ok(SerialMode::Stdout),
"midi" => Ok(SerialMode::Midi),
"tcp" => Ok(SerialMode::Tcp),
_ => Err(anyhow!(
"unknown [serial] mode {:?}: expected \"off\", \"stdout\", or \"midi\"",
"unknown [serial] mode {:?}: expected \"off\", \"stdout\", \"midi\", or \"tcp\"",
s
)),
}
Expand Down
4 changes: 2 additions & 2 deletions src/cpu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2200,7 +2200,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 @@ -2403,7 +2403,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
25 changes: 25 additions & 0 deletions src/emulator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1742,6 +1742,9 @@ fn build_serial_sink(cfg: &Config) -> Result<Box<dyn crate::serial::SerialSink>>
SerialMode::Midi => Err(anyhow!(
"[serial] mode = \"midi\" needs a build with --features midi"
)),
SerialMode::Tcp => Ok(Box::new(crate::serial::TcpSerialSink::listen(
cfg.serial.listen.as_deref().unwrap_or("127.0.0.1:1234"),
)?)),
}
}

Expand Down Expand Up @@ -1776,6 +1779,28 @@ 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 board = crate::a4091::A4091::new(rom)?;
if cfg.a4091.units.iter().any(Option::is_some) {
log::warn!(
"a4091: the 53C710 SCSI core is not implemented yet; \
[a4091] drive images are ignored"
);
Comment on lines +1791 to +1794
}
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
Loading
Loading