use crate::interpreter::{error::Error, EMBIVE_INTERRUPT_CODE};
const MSTATUS_ADDR: u16 = 0x300;
const MISA_ADDR: u16 = 0x301;
const MIE_ADDR: u16 = 0x304;
const MTVEC_ADDR: u16 = 0x305;
const MSTATUSH_ADDR: u16 = 0x310;
const MCOUNTINHIBIT_ADDR: u16 = 0x320;
const MSCRATCH_ADDR: u16 = 0x340;
const MEPC_ADDR: u16 = 0x341;
const MCAUSE_ADDR: u16 = 0x342;
const MTVAL_ADDR: u16 = 0x343;
const MIP_ADDR: u16 = 0x344;
const MHPMEVENT31H_ADDR: u16 = 0x33F;
const MCYCLE_ADDR: u16 = 0xB00;
const MHPMCOUNTER31H_ADDR: u16 = 0xB9F;
const MVENDORID_ADDR: u16 = 0xF11;
const MCONFIGPTR_ADDR: u16 = 0xF15;
const MXLEN: u32 = 32;
const MXL_32: u32 = 0b01;
const MISA_A: u32 = 1 << 0;
const MISA_I: u32 = 1 << 8;
const MISA_M: u32 = 1 << 12;
const MTVEC_MODE: u32 = 0b11;
const MEPC_BIT0: u32 = 0b1;
const MSTATUS_MIE: u8 = 0b1 << 3;
const MSTATUS_MPIE: u8 = 0b1 << 7;
const MSTATUS_MASK: u8 = MSTATUS_MIE | MSTATUS_MPIE;
const MCAUSE_MEI_CODE: u32 = EMBIVE_INTERRUPT_CODE;
const MCAUSE_INTERRUPT: u32 = 0b1 << 31;
const MI_E_P_MASK: u32 = 0b1 << EMBIVE_INTERRUPT_CODE;
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum CSOperation {
Write(u32),
Set(u32),
Clear(u32),
}
const fn get_misa() -> u32 {
(MXL_32 << (MXLEN - 2)) | MISA_I | MISA_M | MISA_A
}
#[derive(Debug, Default, PartialEq, Copy, Clone)]
pub struct CSRegisters {
mtvec: u32,
mscratch: u32,
mepc: u32,
mcause: u32,
mtval: i32,
mie_embive: bool,
mip_embive: bool,
mstatus: u8,
}
impl CSRegisters {
#[inline]
pub fn operation(&mut self, op: Option<CSOperation>, addr: u16) -> Result<u32, Error> {
match addr {
MSTATUS_ADDR => {
let ret = self.mstatus as u32;
self.mstatus = (execute_operation(op, ret) as u8) & MSTATUS_MASK;
Ok(ret)
}
MISA_ADDR => Ok(get_misa()), MIE_ADDR => {
let ret = (self.mie_embive as u32) << EMBIVE_INTERRUPT_CODE;
self.mie_embive = (execute_operation(op, ret) & MI_E_P_MASK) != 0;
Ok(ret)
}
MTVEC_ADDR => {
let ret = self.mtvec;
self.mtvec = execute_operation(op, ret) & !MTVEC_MODE;
Ok(ret)
}
MSTATUSH_ADDR => Ok(0), MCOUNTINHIBIT_ADDR..=MHPMEVENT31H_ADDR => Ok(0), MSCRATCH_ADDR => {
let ret = self.mscratch;
self.mscratch = execute_operation(op, ret);
Ok(ret)
}
MEPC_ADDR => {
let ret = self.mepc;
self.mepc = execute_operation(op, ret) & !MEPC_BIT0;
Ok(ret)
}
MCAUSE_ADDR => {
let ret = self.mcause;
self.mcause = execute_operation(op, ret);
Ok(ret)
}
MTVAL_ADDR => {
let ret = self.mtval as u32;
self.mtval = execute_operation(op, ret) as i32;
Ok(ret)
}
MIP_ADDR => {
let ret = (self.mip_embive as u32) << EMBIVE_INTERRUPT_CODE;
self.mip_embive = (execute_operation(op, ret) & MI_E_P_MASK) != 0;
Ok(ret)
}
MCYCLE_ADDR..=MHPMCOUNTER31H_ADDR => Ok(0), MVENDORID_ADDR..=MCONFIGPTR_ADDR => Ok(0), _ => Err(Error::InvalidCSRegister(addr)),
}
}
#[inline(always)]
pub(crate) fn set_interrupt(&mut self) {
self.mip_embive = true;
}
#[inline(always)]
pub(crate) fn interrupt_enabled(&self) -> bool {
self.mie_embive && (self.mstatus & MSTATUS_MIE) != 0
}
pub(crate) fn trap_entry(&mut self, pc: &mut u32, value: i32) {
if (self.mstatus & MSTATUS_MIE) != 0 {
self.mstatus |= MSTATUS_MPIE;
} else {
self.mstatus &= !MSTATUS_MPIE;
}
self.mstatus &= !MSTATUS_MIE;
self.mcause = MCAUSE_INTERRUPT | MCAUSE_MEI_CODE;
self.mepc = *pc;
self.mtval = value;
*pc = self.mtvec & !MTVEC_MODE;
}
pub(crate) fn trap_return(&mut self) -> u32 {
if (self.mstatus & MSTATUS_MPIE) != 0 {
self.mstatus |= MSTATUS_MIE;
} else {
self.mstatus &= !MSTATUS_MIE;
}
self.mepc
}
}
#[inline]
fn execute_operation(op: Option<CSOperation>, value: u32) -> u32 {
match op {
Some(CSOperation::Write(val)) => val,
Some(CSOperation::Set(val)) => value | val,
Some(CSOperation::Clear(val)) => value & !val,
None => value,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_mstatus() {
let mut cs = CSRegisters::default();
assert_eq!(
cs.operation(Some(CSOperation::Write(0x1898)), MSTATUS_ADDR),
Ok(0)
);
assert_eq!(
cs.operation(None, MSTATUS_ADDR),
Ok(0x1898 & MSTATUS_MASK as u32)
);
}
#[test]
fn test_misa() {
let mut cs = CSRegisters::default();
assert_eq!(
cs.operation(Some(CSOperation::Write(0x1898)), MISA_ADDR),
Ok(get_misa())
);
assert_eq!(cs.operation(None, MISA_ADDR), Ok(get_misa()));
}
#[test]
fn test_mie() {
let mut cs = CSRegisters::default();
assert_eq!(
cs.operation(Some(CSOperation::Write(0x1810)), MIE_ADDR),
Ok(0)
);
assert_eq!(cs.operation(None, MIE_ADDR), Ok(0x1810 & MI_E_P_MASK));
}
#[test]
fn test_mtvec() {
let mut cs = CSRegisters::default();
assert_eq!(
cs.operation(Some(CSOperation::Write(0x12FF)), MTVEC_ADDR),
Ok(0)
);
assert_eq!(cs.operation(None, MTVEC_ADDR), Ok(0x12FF & !MTVEC_MODE));
}
#[test]
fn test_mscratch() {
let mut cs = CSRegisters::default();
assert_eq!(
cs.operation(Some(CSOperation::Write(0xFFFF)), MSCRATCH_ADDR),
Ok(0)
);
assert_eq!(cs.operation(None, MSCRATCH_ADDR), Ok(0xFFFF));
}
#[test]
fn test_mepc() {
let mut cs = CSRegisters::default();
assert_eq!(
cs.operation(Some(CSOperation::Write(0x1231)), MEPC_ADDR),
Ok(0)
);
assert_eq!(cs.operation(None, MEPC_ADDR), Ok(0x1231 & !MEPC_BIT0));
}
#[test]
fn test_mcause() {
let mut cs = CSRegisters::default();
assert_eq!(
cs.operation(Some(CSOperation::Write(0xFFFF)), MCAUSE_ADDR),
Ok(0)
);
assert_eq!(cs.operation(None, MCAUSE_ADDR), Ok(0xFFFF));
}
#[test]
fn test_mip() {
let mut cs = CSRegisters::default();
assert_eq!(cs.operation(None, MIP_ADDR), Ok(0));
cs.set_interrupt();
assert_eq!(cs.operation(None, MIP_ADDR), Ok(MI_E_P_MASK));
}
}