use core::arch::asm;
use core::marker::PhantomData;
use num_enum::{FromPrimitive, IntoPrimitive};
use proc_bitfield::bitfield;
macro_rules! cp0fn_ro {
($reg:ident, $width:ident, $index:literal, $datatype:ident) => {
paste::paste! {
#[doc = concat!("Reads from CP0 register ", stringify!($index), ".")]
#[inline(always)]
pub fn $reg() -> $datatype {
[<read_ $width>]::<$index>().into()
}
}
};
}
macro_rules! cp0fn_wo {
($reg:ident, $width:ident, $index:literal, $datatype:ident) => {
paste::paste! {
#[doc = concat!("Writes to CP0 register ", stringify!($index), ".")]
#[inline(always)]
pub unsafe fn [<set_ $reg>](data: $datatype) {
[<write_ $width>]::<$index>(data.into());
}
}
};
}
macro_rules! cp0fn_rw {
($reg:ident, $width:ident, $index:literal, $datatype:ident) => {
cp0fn_ro!($reg, $width, $index, $datatype);
cp0fn_wo!($reg, $width, $index, $datatype);
paste::paste! {
#[doc = concat!("Reads from CP0 register ", stringify!($index), ", modifies the data, then writes it back into the register.")]
#[inline(always)]
pub unsafe fn [<modify_ $reg>]<F: FnOnce($datatype) -> $datatype>(func: F) {
[<set_ $reg>](func($reg()));
}
}
}
}
macro_rules! cp0method_ro {
($reg:ident, $datatype:ident) => {
pub fn $reg(&self) -> $datatype {
$reg()
}
}
}
macro_rules! cp0method_wo {
($reg:ident, $datatype:ident) => {
paste::paste! {
pub fn [<set_ $reg>](&self, data: $datatype) {
unsafe { [<set_ $reg>](data); }
}
}
}
}
macro_rules! cp0method_rw {
($reg:ident, $datatype:ident) => {
cp0method_ro!($reg, $datatype);
cp0method_wo!($reg, $datatype);
paste::paste! {
pub fn [<modify_ $reg>]<F: FnOnce($datatype) -> $datatype>(&self, func: F) {
unsafe { [<set_ $reg>](func($reg())); }
}
}
}
}
macro_rules! derive_tofrom_primitive {
($kind:ident, $prim:ident) => {
impl From<$prim> for $kind {
fn from(value: $prim) -> Self {
Self(value)
}
}
impl From<$kind> for $prim {
fn from(value: $kind) -> Self {
value.0
}
}
}
}
pub struct Cp0 {
_marker: PhantomData<*const ()>
}
impl Cp0 {
pub unsafe fn new() -> Self { Self {
_marker: PhantomData
}}
cp0method_rw!(index, IndexReg);
cp0method_rw!(random, RandomReg);
cp0method_rw!(entrylo0, EntryLoReg);
cp0method_rw!(entrylo1, EntryLoReg);
cp0method_rw!(context, ContextReg);
cp0method_rw!(pagemask, PageMaskReg);
cp0method_rw!(wired, WiredReg);
cp0method_ro!(badvaddr, BadVAddrReg);
cp0method_rw!(count, u32);
cp0method_rw!(entryhi, EntryHiReg);
cp0method_rw!(compare, u32);
cp0method_rw!(status, StatusReg);
cp0method_rw!(cause, CauseReg);
cp0method_rw!(exception_pc, ExceptionPcReg);
cp0method_ro!(processor_revision_id, ProcessorRevisionIdReg);
cp0method_rw!(config, ConfigReg);
cp0method_rw!(load_linked_address, u32);
cp0method_rw!(watchlo, WatchLoReg);
cp0method_rw!(watchhi, WatchHiReg);
cp0method_rw!(xcontext, XContextReg);
cp0method_rw!(parity_error, ParityErrorReg);
cp0method_rw!(taglo, TagLoReg);
cp0method_rw!(error_exception_pc, ErrorExceptionPcReg);
}
cp0fn_rw!(index, u32, 0, IndexReg);
cp0fn_rw!(random, u32, 1, RandomReg);
cp0fn_rw!(entrylo0, u32, 2, EntryLoReg);
cp0fn_rw!(entrylo1, u32, 3, EntryLoReg);
cp0fn_rw!(context, u64, 4, ContextReg);
cp0fn_rw!(pagemask, u32, 5, PageMaskReg);
cp0fn_rw!(wired, u32, 6, WiredReg);
cp0fn_ro!(badvaddr, u64, 8, BadVAddrReg);
cp0fn_rw!(count, u32, 9, u32);
cp0fn_rw!(entryhi, u64, 10, EntryHiReg);
cp0fn_rw!(compare, u32, 11, u32);
cp0fn_rw!(status, u32, 12, StatusReg);
cp0fn_rw!(cause, u32, 13, CauseReg);
cp0fn_rw!(exception_pc, u64, 14, ExceptionPcReg);
cp0fn_ro!(processor_revision_id, u32, 15, ProcessorRevisionIdReg);
cp0fn_rw!(config, u32, 16, ConfigReg);
cp0fn_rw!(load_linked_address, u32, 17, u32);
cp0fn_rw!(watchlo, u32, 18, WatchLoReg);
cp0fn_rw!(watchhi, u32, 19, WatchHiReg);
cp0fn_rw!(xcontext, u64, 20, XContextReg);
cp0fn_rw!(parity_error, u32, 26, ParityErrorReg);
cp0fn_rw!(taglo, u32, 28, TagLoReg);
cp0fn_rw!(error_exception_pc, u64, 30, ErrorExceptionPcReg);
bitfield! {
#[derive(Copy, Clone, PartialEq, Eq)]
pub struct IndexReg(pub u32): Debug {
pub index: u8 @ 0..=5,
pub probe: bool @ 31,
}
}
derive_tofrom_primitive!(IndexReg, u32);
bitfield! {
#[derive(Copy, Clone, PartialEq, Eq)]
pub struct RandomReg(pub u32): Debug {
pub random: u8 [ro] @ 0..=5,
}
}
derive_tofrom_primitive!(RandomReg, u32);
#[derive(IntoPrimitive, FromPrimitive, Copy, Clone, Debug, Eq, PartialEq)]
#[repr(u8)]
pub enum CacheAlgorithm {
Uncached = 0b010,
#[default]
Cached = 0b011,
}
bitfield! {
#[derive(Copy, Clone, PartialEq, Eq)]
pub struct EntryLoReg(pub u32): Debug {
pub global: bool @ 0,
pub valid: bool @ 1,
pub dirty: bool @ 2,
pub cache_algorithm: u8 [CacheAlgorithm] @ 3..=5,
pub page_frame_number: u32 @ 6..=29,
}
}
derive_tofrom_primitive!(EntryLoReg, u32);
bitfield! {
#[derive(Copy, Clone, PartialEq, Eq)]
pub struct ContextReg(pub u64): Debug {
pub bad_vpn2: u32 @ 4..=22,
pub pte_base_u32: u32 @ 23..=31,
pub pte_base_u64: u64 @ 23..=63,
}
}
derive_tofrom_primitive!(ContextReg, u64);
#[derive(IntoPrimitive, FromPrimitive, Copy, Clone, Debug, Eq, PartialEq)]
#[repr(u16)]
pub enum PageSize {
KB4 = 0x000,
KB16 = 0x003,
KB64 = 0x00F,
KB256 = 0x03F,
MB1 = 0x0FF,
MB4 = 0x3FF,
MB16 = 0xFFF,
#[default]
Undefined,
}
bitfield! {
#[derive(Copy, Clone, PartialEq, Eq)]
pub struct PageMaskReg(pub u32): Debug {
pub mask: u16 [PageSize] @ 13..=24,
}
}
derive_tofrom_primitive!(PageMaskReg, u32);
bitfield! {
#[derive(Copy, Clone, PartialEq, Eq)]
pub struct WiredReg(pub u32): Debug {
pub wired: u8 @ 0..=5,
}
}
derive_tofrom_primitive!(WiredReg, u32);
bitfield! {
#[derive(Copy, Clone, PartialEq, Eq)]
pub struct BadVAddrReg(pub u64): Debug {
pub badvaddr_u32: u32 [ro] @ 0..=31,
pub badvaddr_u64: u64 [ro] @ 0..=63,
}
}
derive_tofrom_primitive!(BadVAddrReg, u64);
#[derive(IntoPrimitive, FromPrimitive, Copy, Clone, Debug, Eq, PartialEq)]
#[repr(u8)]
pub enum VAddrRegion {
User = 0,
Supervisor = 1,
#[default]
Unknown = 2,
Kernel = 3,
}
bitfield! {
#[derive(Copy, Clone, PartialEq, Eq)]
pub struct EntryHiReg(pub u64): Debug {
pub asid: u8 @ 0..=7,
pub vpn2_u32: u32 @ 13..=31,
pub vpn2_u64: u32 @ 13..=39,
pub fill: u32 @ 40..=61,
pub region: u8 [VAddrRegion] @ 62..=63,
}
}
derive_tofrom_primitive!(EntryHiReg, u64);
bitfield! {
#[derive(Copy, Clone, PartialEq, Eq)]
pub struct StatusReg(pub u32): Debug {
pub ie: bool @ 0,
pub exl: bool @ 1,
pub erl: bool @ 2,
pub ksu: u8 @ 3..=4,
pub ux: bool @ 5,
pub sx: bool @ 6,
pub kx: bool @ 7,
pub im: u8 @ 8..=15,
pub im_ip0: bool @ 8,
pub im_ip1: bool @ 9,
pub im_int0: bool @ 10,
pub im_int1: bool @ 11,
pub im_int2: bool @ 12,
pub im_int3: bool @ 13,
pub im_int4: bool @ 14,
pub im_timer: bool @ 15,
pub ds: u16 @ 16..=24,
pub ds_de: bool @ 16,
pub ds_ce: bool @ 17,
pub ds_ch: bool @ 18,
pub ds_sr: bool @ 20,
pub ds_ts: bool @ 21,
pub ds_bev: bool @ 22,
pub ds_its: bool @ 24,
pub re: bool @ 25,
pub fr: bool @ 26,
pub rp: bool @ 27,
pub cu: u8 @ 28..=31,
}
}
derive_tofrom_primitive!(StatusReg, u32);
#[derive(IntoPrimitive, FromPrimitive, Copy, Clone, Debug, Eq, PartialEq)]
#[repr(u8)]
pub enum ExceptionCode {
Interrupt = 0,
TlbModification = 1,
TlbMissOnLoad = 2,
TlbMissOnStore = 3,
AddressErrorOnLoad = 4,
AddressErrorOnStore = 5,
InstructionBusError = 6,
DataBusError = 7,
Syscall = 8,
Breakpoint = 9,
ReservedInstruction = 10,
CoprocessorUnusable = 11,
ArithmeticOverflow = 12,
Trap = 13,
FloatingPoint = 15,
Watch = 23,
#[default]
Reserved,
}
bitfield! {
#[derive(Copy, Clone, PartialEq, Eq)]
pub struct CauseReg(pub u32): Debug {
pub exception_code: u8 [ExceptionCode, ro] @ 2..=6,
pub ip0: bool @ 7,
pub ip1: bool @ 8,
pub ip2: bool [ro] @ 9,
pub ip3: bool [ro] @ 10,
pub ip4: bool [ro] @ 11,
pub ip5: bool [ro] @ 12,
pub ip6: bool [ro] @ 13,
pub ip7: bool [ro] @ 14,
pub ce: u8 [ro] @ 28..=29,
pub branch_delay: bool [ro] @ 31,
}
}
derive_tofrom_primitive!(CauseReg, u32);
bitfield! {
#[derive(Copy, Clone, PartialEq, Eq)]
pub struct ExceptionPcReg(pub u64): Debug {
pub epc_u32: u32 @ 0..=31,
pub epc_u64: u64 @ 0..=63,
}
}
derive_tofrom_primitive!(ExceptionPcReg, u64);
bitfield! {
#[derive(Copy, Clone, PartialEq, Eq)]
pub struct ProcessorRevisionIdReg(pub u32): Debug {
pub revision: u8 [ro] @ 0..=7,
pub processor_id: u8 [ro] @ 8..=15,
}
}
derive_tofrom_primitive!(ProcessorRevisionIdReg, u32);
bitfield! {
#[derive(Copy, Clone, PartialEq, Eq)]
pub struct ConfigReg(pub u32): Debug {
pub k0: u8 [CacheAlgorithm] @ 0..=2,
pub cu: bool @ 3,
pub be: bool @ 15,
pub ep: u8 @ 24..=27,
pub ec: u8 [ro] @ 28..=30,
}
}
derive_tofrom_primitive!(ConfigReg, u32);
bitfield! {
#[derive(Copy, Clone, PartialEq, Eq)]
pub struct WatchLoReg(pub u32): Debug {
pub w: bool @ 0,
pub r: bool @ 1,
pub paddr0: u32 @ 3..=31,
}
}
derive_tofrom_primitive!(WatchLoReg, u32);
bitfield! {
#[derive(Copy, Clone, PartialEq, Eq)]
pub struct WatchHiReg(pub u32): Debug {
pub paddr1: u32 @ 0..=3,
}
}
derive_tofrom_primitive!(WatchHiReg, u32);
bitfield! {
#[derive(Copy, Clone, PartialEq, Eq)]
pub struct XContextReg(pub u64): Debug {
pub badvpn2: u32 @ 4..=30,
pub region: u8 [VAddrRegion] @ 31..=32,
pub ptebase: u32 @ 33..=63,
}
}
derive_tofrom_primitive!(XContextReg, u64);
bitfield! {
#[derive(Copy, Clone, PartialEq, Eq)]
pub struct ParityErrorReg(pub u32): Debug {
pub diagnostic: u8 @ 0..=7,
}
}
derive_tofrom_primitive!(ParityErrorReg, u32);
bitfield! {
#[derive(Copy, Clone, PartialEq, Eq)]
pub struct TagLoReg(pub u32): Debug {
pub pstate: u8 @ 6..=7,
pub ptaglo: u32 @ 8..=27,
}
}
derive_tofrom_primitive!(TagLoReg, u32);
bitfield! {
#[derive(Copy, Clone, PartialEq, Eq)]
pub struct ErrorExceptionPcReg(pub u64): Debug {
pub epc_u32: u32 @ 0..=31,
pub epc_u64: u64 @ 0..=63,
}
}
derive_tofrom_primitive!(ErrorExceptionPcReg, u64);
#[inline(always)]
pub fn read_u32<const INDEX: u32>() -> u32 {
let value: u32;
unsafe {
asm!("
.set noat
mfc0 {gpr}, ${cp_reg}
",
gpr = out(reg) value,
cp_reg = const INDEX
);
}
value
}
#[inline(always)]
pub fn read_u64<const INDEX: u32>() -> u64 {
let value_lo: u32;
let value_hi: u32;
unsafe {
asm!("
.set noat
dmfc0 {tmp}, ${cp_reg}
add {lo}, $0, {tmp}
dsrl32 {hi}, {tmp}, 0
",
tmp = out(reg) _,
lo = out(reg) value_lo,
hi = out(reg) value_hi,
cp_reg = const INDEX
);
}
((value_hi as u64) << 32) | (value_lo as u64)
}
#[inline(always)]
pub unsafe fn write_u32<const INDEX: u32>(value: u32) {
asm!("
.set noat
mtc0 {gpr}, ${cp_reg}
nop
",
gpr = in(reg) value,
cp_reg = const INDEX
);
}
#[inline(always)]
pub unsafe fn write_u64<const INDEX: u32>(value: u64) {
asm!("
.set noat
dsll32 {tmp}, {hi}, 0
dsll32 {tmp2}, {lo}, 0
dsrl32 {tmp2}, {tmp2}, 0
or {tmp}, {tmp}, {tmp2}
dmtc0 {tmp}, ${cp_reg}
nop
",
tmp = out(reg) _,
tmp2 = out(reg) _,
lo = in(reg) (value as u32),
hi = in(reg) ((value >> 32) as u32),
cp_reg = const INDEX
);
}