Skip to main content

Bus

Trait Bus 

Source
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§

Source

fn read_u8(&mut self, addr: u32) -> u8

Read a byte at a 32-bit physical address (post-TLB).

Source

fn write_u8(&mut self, addr: u32, val: u8)

Write a byte at a 32-bit physical address (post-TLB).

Provided Methods§

Source

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.

Source

fn write_u32(&mut self, addr: u32, val: u32)

Write an aligned big-endian 32-bit word.

Source

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.

Source

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.

Source

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.

Source

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”.

Source

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.

Implementors§