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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,10 @@ hashes are given where a change is easiest to understand by reading the commit.
audio registers now brings the rings up to date, so IRIX, which keeps
only about 2 ms of sound queued, no longer lets them run dry between
service passes.
- **The O2's Ethernet-address chip is on the shared 1-wire bus** (`dev::mace_id`,
ported from the QEMU model); the MAC reads correctly at any emulator speed.
- The O2's front LEDs show in the status bar.
- The O2's SCSI and Ethernet lights show in the status bar.
- **The O2 is on `Physical`.** Its bus is the same decode table and memory
window as the Indy's: CRIME's banks are RAM banks a bank-control write
remaps, CRIME, GBE, VICE, MACE and the flash are table entries, and
Expand Down
4 changes: 2 additions & 2 deletions debug.md
Original file line number Diff line number Diff line change
Expand Up @@ -542,7 +542,7 @@ its SCSI controllers answer to `scsi`/`cow` and `scsi1`/`cow1` as described unde
| `audio status` / `audio codec` | MACE audio rings and rates / AD1843 codec registers |
| `ust status` | MACE UST counter |
| `rtc status` / `rtc dump` / `rtc r <reg>` / `rtc w <reg> <val>` | DS1687 RTC |
| `onewire status` / `onewire log` | 1-Wire ID chip (the MAC address) |
| `onewire status` | MACE identity register and the ID chip holding the MAC address |

`o2 <command>` runs the same commands `[o2] control_port` serves, one per line:

Expand All @@ -559,7 +559,7 @@ its SCSI controllers answer to `scsi`/`cow` and `scsi1`/`cow1` as described unde
| `o2 retrace PATH` | Recorded rendering-engine primitives (`IRIS_IP32_RE_TRACE`) |
| `o2 scsi PATH` | The SCSI controllers' recent commands |
| `o2 unmapped` | Bus accesses that reached no device (the first 64 distinct), with the PC that made each (not kept in a `lightning` build) |
| `o2 ps2log PATH` / `o2 crimelog PATH` / `o2 nictrace PATH` | PS/2 traffic / CRIME interrupt-register accesses recorded since `crime irqlog on` / the MAC-address chip's register trace and 1-Wire protocol log |
| `o2 ps2log PATH` / `o2 crimelog PATH` | PS/2 traffic / CRIME interrupt-register accesses recorded since `crime irqlog on` |

## Recipes

Expand Down
14 changes: 13 additions & 1 deletion src/dev/aic7880.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
use crate::locks::{SpinGuard, SpinLock};
use crate::scsi::{scsi_cmd, ScsiBus, ScsiDataLength, ScsiIdentities, ScsiRequest, CDROM_IDENTITY, DISK_IDENTITY};
use std::collections::VecDeque;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
use std::sync::{Arc, Mutex, Weak};

use crate::snapshot::{get_field, hex_u16, hex_u32, hex_u64, hex_u8, load_u8_slice, toml_u16, toml_u32, toml_u64, toml_u8, u8_slice_to_toml};
Expand Down Expand Up @@ -427,6 +427,8 @@ pub struct Aic7880 {
dma: std::sync::OnceLock<std::sync::Arc<dyn crate::pci::PciDma>>,
/// The SCSI id a disk attached without one answers at.
target: u8,
/// The status bar's activity bits, shared with the display thread.
heartbeat: std::sync::OnceLock<Arc<AtomicU64>>,
/// Which of the O2's two controllers this is (0 internal, 1 external):
/// the monitor's `scsi`/`cow` address the first, `scsi1`/`cow1` the second.
index: u8,
Expand Down Expand Up @@ -486,9 +488,15 @@ impl Aic7880 {
[Bar::Io(0x100), Bar::Mem(0x1000), Bar::Unused, Bar::Unused, Bar::Unused, Bar::Unused], PCI_INTA)
},
target: 1,
heartbeat: std::sync::OnceLock::new(),
})
}

/// Light the status bar's SCSI activity bits in `heartbeat`.
pub fn set_heartbeat(&self, heartbeat: Arc<AtomicU64>) {
let _ = self.heartbeat.set(heartbeat);
}

/// `n` bytes of host memory from bus address `addr`, through the bridge.
/// Its two views reach the same bytes, and the driver uses both in the
/// same breath: the SCB's own address is given as `0x410752fc` while the
Expand Down Expand Up @@ -839,6 +847,10 @@ impl Aic7880 {
/// the controller's own sense, if it holds one.
fn request(&self, target: u8, cdb: &[u8], data_in: Option<&[u8]>) -> (Vec<u8>, u8) {
const CHECK_CONDITION: u8 = 0x02;
// The bar has lights for SCSI ids 0-5.
if let (Some(hb), 0..=5) = (self.heartbeat.get(), target) {
hb.fetch_or(1u64 << (crate::dev::ng1::rex3::Rex3::HB_SCSI_BASE as u64 + target as u64), Ordering::Relaxed);
}
if cdb[0] == scsi_cmd::REQUEST_SENSE {
if let Some((key, asc, ascq)) = self.sense.lock().unwrap().take() {
let mut d = vec![0u8; 18];
Expand Down
77 changes: 27 additions & 50 deletions src/dev/mace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
//! and hands the access to the block it names, as HPC3 does for its
//! children on the Indigo2. The blocks themselves are in their own files
//! (`mace_pci`, `mec`, `mace_isa`, `mace_ps2`, `mace_ust`, `mace_audio`,
//! `mace_sdma`, `ns16550`, `ds1687`, `onewire`); what has no model yet is a
//! `mace_sdma`, `ns16550`, `ds1687`, `mace_id`); what has no model yet is a
//! storing register file (`regs`).

use std::sync::{Arc, Mutex};
Expand All @@ -19,9 +19,9 @@ use crate::dev::mace_sdma::*;
use crate::dev::mace_ust::MaceUst;
use crate::dev::mec::Mec;
use crate::dev::ns16550::Com16550;
use crate::dev::onewire::{NicTrace, OneWireId};
use crate::dev::mace_id::{MaceId, DEFAULT_MAC};
use crate::ip32::{
nic_bit, Stub, IP32_MEC_CRIME_INT, MACE_BASE, MACE_COM0, MACE_COM1, MACE_COM_SIZE, MACE_ISA_FLASH_NIC_REG,
Stub, IP32_MEC_CRIME_INT, MACE_BASE, MACE_COM0, MACE_COM1, MACE_COM_SIZE, MACE_ISA_FLASH_NIC_REG,
MACE_MEC, MACE_MEC_SIZE, MACE_PS2, MACE_PS2_SIZE, MACE_RTC, MACE_RTC_SIZE, MACE_SIZE, MACE_UST_MSC,
MACE_UST_MSC_SIZE, MACEPCI_BASE, MACEPCI_SIZE,
};
Expand All @@ -38,10 +38,9 @@ pub struct Mace {
/// The PCI host bridge: its registers here, its windows outside MACE.
pub pci: MacePci,
pub ust: Arc<MaceUst>,
pub nic_trace: NicTrace,
pub rtc: MaceRtc,
pub ps2: MacePs2,
pub onewire: OneWireId,
pub id: MaceId,
pub com0: Com16550,
pub com1: Com16550,
/// The serial ports' DMA rings, which IRIX's console runs on.
Expand All @@ -63,11 +62,10 @@ impl Mace {
regs: Stub::new("mace"),
pci: MacePci::new(crime.clone(), ram.clone()),
ust,
nic_trace: NicTrace::new(),
rtc: MaceRtc::new(),
ps2: MacePs2::new(),
// A locally-administered address; nothing depends on the value yet.
onewire: OneWireId::from_mac([0x08, 0x00, 0x69, 0x12, 0x34, 0x56]),
id: MaceId::new(DEFAULT_MAC),
com0: Com16550::named("com0"),
com1: Com16550::named("com1"),
sdma: MaceSerialDma::new(),
Expand Down Expand Up @@ -113,7 +111,7 @@ impl Mace {
/// commands through on the Indigo2.
pub fn devices(&self) -> Vec<&dyn crate::traits::Device> {
vec![
&self.pci, &*self.ust, &self.rtc, &self.ps2, &self.onewire, &self.com0, &self.com1, &self.sdma,
&self.pci, &*self.ust, &self.rtc, &self.ps2, &self.id, &self.com0, &self.com1, &self.sdma,
&self.audio, &*self.mec, &*self.isa,
]
}
Expand Down Expand Up @@ -234,33 +232,18 @@ impl Mace {
(addr & !3) == MACE_ISA_FLASH_NIC_REG
}

/// Feed a write of the NIC register through to the 1-Wire device.
fn nic_write(&self, val: u8) {
self.nic_trace.record(self.ust.now(), self.ust.guest_us(), true, val);
// DEASSERT alone decides it: clear means the master is pulling the
// line down, set means it has let go. The firmware writes DATA=1 in
// both cases, so DATA is an input here, not a level to drive — which
// is exactly what the bus trace shows (only 0x08 and 0x0c are ever
// written).
let low = val & nic_bit::DEASSERT == 0;
/// The identity/flash/LED register: its own device holds the value.
fn id_write(&self, val: u8) {
self.ust.onewire_busy();
self.onewire.master_drive(self.ust.guest_us(), low);
}

/// The NIC register, with the 1-Wire line as it stands now. A read does
/// not hold the guest clock (only the master driving the line does):
/// IRIX reads this register about a hundred times a second for its
/// other bits, and each read holding the clock for 5 ms ran UST at half
/// speed.
fn nic_read(&self) -> u8 {
let mut v = self.regs.cell(MACE_ISA_FLASH_NIC_REG) as u8;
if self.onewire.line_level(self.ust.guest_us()) {
v |= nic_bit::DATA;
} else {
v &= !nic_bit::DATA;
}
self.nic_trace.record(self.ust.now(), self.ust.guest_us(), false, v);
v
self.id.write(self.ust.guest_us(), val);
}

/// A read does not hold the guest clock (only the master driving the
/// line does): IRIX reads this register about a hundred times a second
/// for its other bits, and each read holding the clock for 5 ms ran UST
/// at half speed.
fn id_read(&self) -> u8 {
self.id.read(self.ust.guest_us())
}
}

Expand Down Expand Up @@ -296,7 +279,7 @@ impl Mace {
("pci", &self.pci),
("rtc", &self.rtc),
("ps2", &self.ps2),
("onewire", &self.onewire),
("id", &self.id),
("com0", &self.com0),
("com1", &self.com1),
("sdma", &self.sdma),
Expand All @@ -319,7 +302,7 @@ impl BusDevice for Mace {
return BusRead8::ok(self.pci.read32(addr & !3).data as u8);
}
if Self::is_nic_reg(a) {
return BusRead8::ok(self.nic_read());
return BusRead8::ok(self.id_read());
}
if let Some(c) = self.com_for(a) {
return c.read8(addr);
Expand Down Expand Up @@ -397,7 +380,7 @@ impl BusDevice for Mace {
return self.pci.read32(addr);
}
if Self::is_nic_reg(a) {
return BusRead32::ok(self.nic_read() as u32);
return BusRead32::ok(self.id_read() as u32);
}
if let Some(c) = self.com_for(a) {
return c.read32(addr);
Expand Down Expand Up @@ -442,7 +425,7 @@ impl BusDevice for Mace {
return self.pci.read64(addr);
}
if Self::is_nic_reg(a) {
return BusRead64::ok(self.nic_read() as u64);
return BusRead64::ok(self.id_read() as u64);
}
if let Some(c) = self.com_for(a) {
return c.read64(addr);
Expand Down Expand Up @@ -479,10 +462,8 @@ impl BusDevice for Mace {
return self.pci.write32(addr & !3, val as u32);
}
if Self::is_nic_reg(a) {
self.nic_write(val);
let r = self.regs.write8(addr, val);
self.regs_wrote(a);
return r;
self.id_write(val);
return BUS_OK;
}
if let Some(c) = self.com_for(a) {
return c.write8(addr, val);
Expand Down Expand Up @@ -574,10 +555,8 @@ impl BusDevice for Mace {
return self.pci.write32(addr, val);
}
if Self::is_nic_reg(a) {
self.nic_write(val as u8);
let r = self.regs.write32(addr, val);
self.regs_wrote(a);
return r;
self.id_write(val as u8);
return BUS_OK;
}
if let Some(c) = self.com_for(a) {
return c.write32(addr, val);
Expand Down Expand Up @@ -627,10 +606,8 @@ impl BusDevice for Mace {
return self.pci.write64(addr, val);
}
if Self::is_nic_reg(a) {
self.nic_write(val as u8);
let r = self.regs.write64(addr, val);
self.regs_wrote(a);
return r;
self.id_write(val as u8);
return BUS_OK;
}
if let Some(c) = self.com_for(a) {
return c.write64(addr, val);
Expand Down
Loading
Loading