pub trait Bus {
// Required methods
fn read_u8(&mut self, addr: u32) -> u8;
fn write_u8(&mut self, addr: u32, val: u8);
// Provided methods
fn read_u32(&mut self, addr: u32) -> u32 { ... }
fn write_u32(&mut self, addr: u32, val: u32) { ... }
fn write_sized(&mut self, addr: u32, width: u64, value: u64) { ... }
fn emux_enabled(&self) -> bool { ... }
fn emux_log(&mut self, bytes: &[u8]) { ... }
fn emux_exit(&mut self) { ... }
fn poll_irq(&mut self) -> bool { ... }
}Expand description
The system-memory bus the VR4300 borrows during Cpu::tick.
Implemented by rustyn64-core::Bus. Kept as a trait so the CPU can be
fuzzed and benchmarked against a tiny in-crate bus without pulling in the
rest of the machine.
Required Methods§
Provided Methods§
Sourcefn read_u32(&mut self, addr: u32) -> u32
fn read_u32(&mut self, addr: u32) -> u32
Read an aligned big-endian 32-bit word. Default composes four byte
reads; rustyn64-core overrides with a fast RDRAM path.
Sourcefn write_sized(&mut self, addr: u32, width: u64, value: u64)
fn write_sized(&mut self, addr: u32, width: u64, value: u64)
Write value as a width-byte store, carrying the access width and
the untruncated source register to the bus.
The CPU cannot narrow the value itself, because whether narrowing is
correct is a property of the target, not of the instruction. RDRAM
honors the byte enables and stores exactly width bytes; every device
on the RCP’s internal bus ignores the access size entirely and latches
the whole 32-bit word the VR4300 placed on SysAD — including the bits
of the source register that a narrow store was never meant to send
(N64brew Memory map §Physical Memory Map accesses).
So the width and the full register both have to survive the call, and
the implementor decides. The default here is the RDRAM-style narrowing,
which is right for a plain memory and for the test buses; rustyn64-core
overrides it to model the RCP’s size-blindness.
Sourcefn emux_enabled(&self) -> bool
fn emux_enabled(&self) -> bool
Does this host offer the EMUX emulator extensions?
Default: no, and that default is the load-bearing part. Real hardware
has no EMUX — COP0 CO funct 0x20-0x3F retires inertly (ledger C-8), so
xdetect leaves its destination register untouched and the guest
concludes no extensions exist. Advertising them changes which console
path n64-systemtest takes – it changes the instruction stream.
ares takes exactly this position: every EMUX handler in its emux.cpp
opens with if(!system.homebrewMode) return;, off by default. A default
build of RustyN64 must behave like hardware; a harness that wants the
faster console opts in deliberately.
Sourcefn emux_log(&mut self, bytes: &[u8])
fn emux_log(&mut self, bytes: &[u8])
EMUX xlog: the guest has asked the emulator to print bytes.
Default: discard. rustyn64-core collects it. This is n64-systemtest’s
out-of-band console channel — it reaches the host without any PI, SI or
ISViewer emulation, which is why it is worth implementing well before the
cartridge subsystem exists.
Sourcefn emux_exit(&mut self)
fn emux_exit(&mut self)
EMUX xioctl(EXIT): the guest has asked the emulator to terminate.
Default: ignore. A harness that honors it gets a definite end-of-run signal instead of a tick budget, which is the difference between “the suite finished” and “we stopped watching”.
Sourcefn poll_irq(&mut self) -> bool
fn poll_irq(&mut self) -> bool
Sample the pending-interrupt level: the MI lines masked by MI_MASK.
Default: no interrupt pending. rustyn64-core overrides it.
Sampling happens once per PClock in the DC stage (UM Figure 4-12 and
§4.7.6, which lists the interrupt exception among the DC-stage priorities)
and is accepted only if the previous PCycle was a run cycle (§4.7.1). The
run-cycle gate lives in the pipeline, not here — this hook only reports the
level. Exactly one recognition predicate exists in the tree.