Skip to main content

Rsp

Struct Rsp 

Source
pub struct Rsp {
    pub su_regs: [u32; 32],
    pub vu_regs: [[u16; 8]; 32],
    pub vu_acc: [u64; 8],
    pub pc: u16,
    pub halted: bool,
    pub dmem: Box<[u8; 4096]>,
    pub imem: Box<[u8; 4096]>,
    pub sp: SpRegs,
    pub dp: [u32; 8],
    pub vu_ctrl: Control,
    pub div: Divide,
    /* private fields */
}
Expand description

RSP architectural state.

Holds the SU register file, the VU vector register file + accumulator, the program counter into IMEM, the halted flag, and the DMEM/IMEM scratch. The execution engine (SU + VU) runs the microcode instruction stream.

Fields§

§su_regs: [u32; 32]

Scalar unit: 32 × 32-bit general registers.

§vu_regs: [[u16; 8]; 32]

Vector unit: 32 registers × 8 lanes of 16-bit.

§vu_acc: [u64; 8]

48-bit-per-lane VU accumulator (modeled as [u64; 8], low 48 used).

§pc: u16
👎Deprecated since 0.8.0:

never written; read Rsp::pc() instead, which returns SP_STATUS’s PC

Vestigial. The authoritative PC lives in Self::sp (SpRegs::pc()), which is what su_step fetches from; this field is never written. Kept only so the save-state layout is unchanged — removing it is a format break (ADR 0005), which module 70 reserves for an announced major release — and deprecated so any read is a loud warning (the workspace treats warnings as errors). Use Rsp::pc instead.

This is not hypothetical tidiness: while it was pub it was sampled twice in one debugging session and produced two confident, wrong conclusions — “the RSP never starts” and “its PC never advances” — when in fact retail microcode was executing hundreds of distinct instructions. That is the inert-API hazard in docs/engineering-lessons.md §3.2 exactly.

§halted: bool
👎Deprecated since 0.8.0:

never written; read Rsp::halted() instead, which returns SP_STATUS.halt

Vestigial — see Self::pc. The authoritative halt state is sp.halted() (SP_STATUS.halt), which is what gates execution. Use Rsp::halted instead.

§dmem: Box<[u8; 4096]>

4 KiB data memory.

§imem: Box<[u8; 4096]>

4 KiB instruction memory.

§sp: SpRegs

The SP interface registers, shared by the CPU’s memory-mapped window and the RSP’s own COP0 – one set of physical registers, so one field.

§dp: [u32; 8]

Shadow of the eight DP command registers (COP0 c8c15 = DP_START/END/CURRENT/STATUS/…), so an MFC0 reads back what a prior MTC0 in the same run wrote. The authoritative copy lives in rustyn64-rdp; the Bus forwards each write there (StepResult::dp_write) and this shadow is the RSP-local view — which is why reads of DP state the RDP mutated on its own are not yet reflected here (Phase 3).

§vu_ctrl: Control

The VU’s three control registers (VCO, VCC, VCE).

§div: Divide

The reciprocal unit’s staging latches (DIVIN/DIVOUT/DIVDP).

Implementations§

Source§

impl Rsp

Source

pub const fn r(&self, i: usize) -> u32

Read one scalar register, with r0 pinned to zero.

Public so the vector load/store family can compute its base address — those instructions live in crate::vu but address DMEM through a GPR.

Source

pub const fn set_su(&mut self, i: usize, v: u32)

Write one scalar register; writes to r0 are discarded.

Public under this name so the VU’s move instructions can reach it — MFC2 and CFC2 write a GPR, and they live in crate::vu.

Source

pub fn su_step(&mut self) -> StepResult

Execute one scalar instruction, if the core is running.

Returns what the step asked the machine to do; see StepResult.

Source§

impl Rsp

Source

pub const fn vu_byte(&self, reg: usize, byte: usize) -> u8

Read a byte of a vector register, big-endian within the 128 bits.

Byte 0 is the most significant half of lane 0, matching the wiki’s convention that byte indices count “from the higher part of the register (in big-endian order)”.

Source

pub const fn set_vu_byte(&mut self, reg: usize, byte: usize, val: u8)

Write a byte of a vector register.

Source

pub fn mfc2(&mut self, rt: usize, vs: usize, elem: usize) -> u32

MFC2 — copy two bytes of a vector register into a GPR, sign-extended.

The pair is taken at a byte offset, so an odd offset straddles two lanes. At offset 15 the second byte wraps to byte 0 of the same register rather than reading past the end — a rule that is invisible until something actually addresses the last byte.

Source

pub const fn mtc2(&mut self, value: u32, vs: usize, elem: usize)

MTC2 — copy the low 16 bits of a GPR into a vector register at a byte offset.

At offset 15 only one byte is written, taken from rt[15..8]: there is no byte 16 to receive the other half, and unlike MFC2 it does not wrap around to byte 0. The asymmetry between the two is deliberate on hardware and is exactly what a lane-oriented implementation loses.

Source

pub fn cfc2(&mut self, rt: usize, vs: u32) -> u32

CFC2 — copy a VU control register into a GPR, sign-extended from 16 bits. The element field is ignored.

Source

pub const fn ctc2(&mut self, value: u32, vs: u32)

CTC2 — copy the low 16 bits of a GPR into a VU control register.

Source§

impl Rsp

The 48-bit-per-lane accumulator, and the computational instructions.

§The accumulator is one 48-bit register per lane, not three 16-bit ones

VSAR slices it into ACC_HI (bits 47..32), ACC_MD (31..16) and ACC_LO (15..0), which invites modeling it as three separate halfwords. It is not: the multiply instructions write and accumulate across the full 48 bits, and the extraction that produces vd reads a 32-bit window spanning two of those slices. Splitting the storage makes carries between them disappear.

Source

pub const fn vt_lane(&self, vt: usize, element: u32, lane: usize) -> u16

Broadcast-modified read of a vt lane (N64brew RSP CPU Core §Broadcast modifier).

element 0 and 1 both mean “no broadcast” — the table lists them separately and gives them identical lane sets, so this is not a simplification.

Source

pub fn vu_compute( &mut self, op: u32, element: u32, vs: usize, vt: usize, vd: usize, ) -> bool

The computational COP2 instructions.

Returns false for an opcode this does not implement yet, so the caller can leave the instruction inert rather than writing a wrong result.

Source§

impl Rsp

The vector load/store family (Sprint 3, brought forward).

Encoding: LWC2/SWC2 | base (25..21) | vt (20..16) | opcode (15..11) | element (10..7) | offset (6..0, signed 7-bit).

The offset is scaled by the access size, and element is a byte index into the vector register naming the first byte the operation touches — so a non-zero element means fewer bytes move, not a shifted window.

Source

pub fn vector_mem( &mut self, store: bool, op: u32, base: usize, vt: usize, element: usize, offset: u32, ) -> bool

Execute a vector load or store. Returns false for an opcode not implemented yet, leaving the instruction inert.

Source§

impl Rsp

The single-lane reciprocal group: VRCP, VRSQ, their L/H partners, VMOV and VNOP.

These do not operate lane-wise. They read one lane of vt (selected by the element field), write one lane of vd (selected by the de field, which occupies the vs position), and set the whole accumulator low slice to the broadcast vt.

Source

pub fn vu_single_lane( &mut self, op: u32, element: u32, vt: usize, de: usize, vd: usize, ) -> bool

The single-lane group’s dispatch. Returns false for an opcode outside it, so the caller can fall through.

Source§

impl Rsp

Source

pub fn new() -> Self

Construct at power-on (halted, zeroed scratch).

Source

pub const fn pc(&self) -> u32

The RSP’s program counter into IMEM — the value execution actually uses.

Delegates to SP_STATUS’s register file rather than the struct field of the same name, which is vestigial.

Returns u32, not the u16 the vestigial field used, because that is SpRegs::pc()’s type and widening here would be a lossless re-narrowing for every caller. The value is a 12-bit IMEM offset either way — su_step masks it with 0xFFC — so the wider type costs nothing and avoids a cast at every call site.

Source

pub const fn halted(&self) -> bool

Is the RSP halted? This is SP_STATUS.halt, the flag that actually gates Rsp::tick — not the vestigial halted field.

Source

pub fn mem_read(&self, off: u32) -> u8

Read a byte of DMEM/IMEM as the CPU sees it.

Bit 12 of the folded offset selects IMEM over DMEM, and each bank wraps within its own 4 KiB — a transfer or access never spills from one into the other (N64brew RSP Interface §DMEM and IMEM).

Source

pub const fn mem_write(&mut self, off: u32, val: u8)

Write a byte of DMEM/IMEM as the CPU sees it.

Source

pub fn tick(&mut self) -> StepResult

Advance the RSP by one instruction when running.

Returns what the step asked of the rest of the machine — see su::StepResult. It reports rather than acting because the RSP owns neither RDRAM nor the MI, and a chip reaching back into its owner is the dependency cycle docs/architecture.md exists to prevent.

This is also why it no longer borrows a bus: the caller needed to move the whole chip out of the Bus to satisfy the borrow checker, and moving it out meant Default-constructing a replacement — two 4 KiB allocations on every RCP step, behind a comment that claimed there were none.

Trait Implementations§

Source§

impl Clone for Rsp

Source§

fn clone(&self) -> Rsp

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Rsp

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for Rsp

Source§

fn default() -> Self

Returns the “default value” for a type. Read more
Source§

impl<'de> Deserialize<'de> for Rsp

Source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
Source§

impl Serialize for Rsp

Source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where __S: Serializer,

Serialize this value into the given Serde serializer. Read more

Auto Trait Implementations§

§

impl Freeze for Rsp

§

impl RefUnwindSafe for Rsp

§

impl Send for Rsp

§

impl Sync for Rsp

§

impl Unpin for Rsp

§

impl UnsafeUnpin for Rsp

§

impl UnwindSafe for Rsp

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> DeserializeOwned for T
where T: for<'de> Deserialize<'de>,