Expand description
Low-level hardware abstraction crate (aka a Peripheral Access Crate) for the Nintendo 64 console.
Unlike typical PACs, this API is not generated via svd2rust, as it doesn’t support the architecture and the N64 contains features not found on microcontrollers.
Additionally, while a singleton pattern is available to ensure safe access to hardware, helper functions are available that will bypass this pattern (static write/modify functions require unsafe).
§Singleton Pattern
Hardware
is a top-level type that holds access to all available hardware abstractions. Using
Hardware::take()
a single instance of this abstraction can be taken, and if called a second
time, None
will be returned instead. This ensures safe, race-free, access to low level hardware.
If the developer wishes to bypass this pattern, such as in cases where interrupts are not used, or the developer has taken precautions against data races, they have several methods available.
§Direct hardware access
Both CPU registers and memory mapped registers have two methods of access outside the Hardware
singleton type. Each method will usually optimize down to identical instructions.
§Static functions
Each module contains various read/modify/write functions for accessing hardware.
§Examples
Reads the VI_CTRL register, sets the pixel color depth to 32-bits, and writes it back to memory:
use n64_pac::vi;
use n64_pac::vi::ColorDepth;
let mut value = vi::ctrl();
value.set_depth(ColorDepth::BPP32);
unsafe {
vi::set_ctrl(value);
}
Just like the above example, but using the modify function:
use n64_pac::vi;
use n64_pac::vi::ColorDepth;
unsafe {
vi::modify_ctrl(|value| value.with_depth(ColorDepth::BPP32));
}
§Wrapper types
Memory mapped registers are accessed using mutable references that point to their location in memory. These references are wrapped into a struct for ease of use and so that blocks of registers can be automatically mapped using only a single pointer address.
CPU registers don’t use memory locations, but zero-sized structs exist anyways so that they can
be accessed via the top-level Hardware
abstraction.
It’s recommended to use the Static functions instead, as they implicitly use these wrappers, and will be optimized into the same instructions.
§Examples
Creates a wrapped pointer to the Video Interface’s block of registers, reads the VI_CTRL register, sets the pixel color depth to 32-bits, and writes it back to memory:
use n64_pac::vi::{ColorDepth, VideoInterface};
let vi = unsafe { VideoInterface::new() };
let mut value = vi.ctrl.read();
value.set_depth(ColorDepth::BPP32);
vi.ctrl.write(value);
Just like the above example, but using the modify method:
use n64_pac::vi::{ColorDepth, VideoInterface};
let vi = unsafe { VideoInterface::new() };
vi.ctrl.modify(|value| value.with_depth(ColorDepth::BPP32));
Modules§
- ai
- RCP - Audio Interface
- cp0
- CPU - Coprocessor 0
- cp1
- FPU - Coprocessor 1
- mi
- RCP - MIPS Interface
- pi
- RCP - Peripheral Interface
- si
- RCP - Serial Interface
- vi
- RCP - Video Interface