use super::instr::Reg;
pub const ADDR_MASK: u32 = 0xF_FFFF;
pub const OUT_MASK: u16 = 0x0FFF;
pub const RSTK_DEPTH: usize = 8;
pub const HST_XM: u8 = 0b0001;
pub const HST_SB: u8 = 0b0010;
pub const HST_SR: u8 = 0b0100;
pub const HST_MP: u8 = 0b1000;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum Mode {
#[default]
Hex,
Dec,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct ReturnStack {
levels: [u32; RSTK_DEPTH],
}
impl ReturnStack {
pub fn push(&mut self, addr: u32) {
self.levels.copy_within(0..RSTK_DEPTH - 1, 1);
self.levels[0] = addr & ADDR_MASK;
}
pub fn pop(&mut self) -> u32 {
let top = self.levels[0];
self.levels.copy_within(1..RSTK_DEPTH, 0);
self.levels[RSTK_DEPTH - 1] = 0;
top
}
pub fn levels(&self) -> &[u32; RSTK_DEPTH] {
&self.levels
}
pub fn from_levels(levels: [u32; RSTK_DEPTH]) -> Self {
Self {
levels: levels.map(|l| l & ADDR_MASK),
}
}
pub fn clear(&mut self) {
self.levels = [0; RSTK_DEPTH];
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Registers {
pub a: u64,
pub b: u64,
pub c: u64,
pub d: u64,
pub r: [u64; 5],
pub d0: u32,
pub d1: u32,
pub pc: u32,
pub p: u8,
pub st: u16,
pub hst: u8,
pub carry: bool,
pub mode: Mode,
pub out: u16,
pub inp: u16,
pub rstk: ReturnStack,
pub interrupts_enabled: bool,
pub in_interrupt: bool,
pub interrupt_pending: bool,
}
impl Default for Registers {
fn default() -> Self {
let mut r = Self {
a: 0,
b: 0,
c: 0,
d: 0,
r: [0; 5],
d0: 0,
d1: 0,
pc: 0,
p: 0,
st: 0,
hst: 0,
carry: false,
mode: Mode::Hex,
out: 0,
inp: 0,
rstk: ReturnStack::default(),
interrupts_enabled: true,
in_interrupt: false,
interrupt_pending: false,
};
r.reset();
r
}
}
impl Registers {
pub fn reset(&mut self) {
self.a = 0;
self.b = 0;
self.c = 0;
self.d = 0;
self.r = [0; 5];
self.d0 = 0;
self.d1 = 0;
self.pc = 0;
self.p = 0;
self.st = 0;
self.hst = 0;
self.carry = false;
self.mode = Mode::Hex;
self.out = 0;
self.inp = 0;
self.rstk.clear();
self.interrupts_enabled = true;
self.in_interrupt = false;
self.interrupt_pending = false;
}
pub fn get(&self, reg: Reg) -> u64 {
match reg {
Reg::A => self.a,
Reg::B => self.b,
Reg::C => self.c,
Reg::D => self.d,
}
}
pub fn set(&mut self, reg: Reg, value: u64) {
match reg {
Reg::A => self.a = value,
Reg::B => self.b = value,
Reg::C => self.c = value,
Reg::D => self.d = value,
}
}
pub fn push(&mut self, addr: u32) {
self.rstk.push(addr);
}
pub fn pop(&mut self) -> u32 {
self.rstk.pop()
}
pub fn set_d0(&mut self, v: u32) {
self.d0 = v & ADDR_MASK;
}
pub fn set_d1(&mut self, v: u32) {
self.d1 = v & ADDR_MASK;
}
pub fn set_pc(&mut self, v: u32) {
self.pc = v & ADDR_MASK;
}
pub fn set_p(&mut self, v: u8) {
self.p = v & 0xF;
}
pub fn set_out(&mut self, v: u16) {
self.out = v & OUT_MASK;
}
}
pub fn get_nibble(reg: u64, idx: u8) -> u8 {
((reg >> (4 * u32::from(idx & 0xF))) & 0xF) as u8
}
pub fn set_nibble(reg: u64, idx: u8, val: u8) -> u64 {
let shift = 4 * u32::from(idx & 0xF);
(reg & !(0xF << shift)) | (u64::from(val & 0xF) << shift)
}
pub fn field_mask(lo: u8, hi: u8) -> u64 {
let (lo, hi) = (lo & 0xF, hi & 0xF);
if lo > hi {
return 0;
}
let width_bits = 4 * u32::from(hi - lo + 1);
let low = if width_bits >= 64 {
u64::MAX
} else {
(1u64 << width_bits) - 1
};
low << (4 * u32::from(lo))
}
pub fn field_width(lo: u8, hi: u8) -> u32 {
let (lo, hi) = (lo & 0xF, hi & 0xF);
if lo > hi { 0 } else { u32::from(hi - lo + 1) }
}
pub fn field_get(reg: u64, lo: u8, hi: u8) -> u64 {
(reg & field_mask(lo, hi)) >> (4 * u32::from(lo & 0xF))
}
pub fn field_set(reg: u64, lo: u8, hi: u8, value: u64) -> u64 {
let mask = field_mask(lo, hi);
let shifted = value.checked_shl(4 * u32::from(lo & 0xF)).unwrap_or(0);
(reg & !mask) | (shifted & mask)
}
#[cfg(test)]
#[allow(clippy::unusual_byte_groupings)]
mod tests {
use super::*;
#[test]
fn nibble_helpers() {
let r = 0xFEDC_BA98_7654_3210u64;
for i in 0..16u8 {
assert_eq!(get_nibble(r, i), i);
}
assert_eq!(set_nibble(r, 0, 0xA), 0xFEDC_BA98_7654_321A);
assert_eq!(set_nibble(r, 15, 0x0), 0x0EDC_BA98_7654_3210);
assert_eq!(set_nibble(0, 3, 0x1F), 0x0000_0000_0000_F000);
}
#[test]
fn field_helpers() {
let r = 0xFEDC_BA98_7654_3210u64;
assert_eq!(field_mask(0, 15), u64::MAX);
assert_eq!(field_mask(0, 4), 0xF_FFFF);
assert_eq!(field_mask(2, 2), 0xF00);
assert_eq!(field_mask(3, 2), 0);
assert_eq!(field_width(0, 15), 16);
assert_eq!(field_get(r, 0, 4), 0x43210); assert_eq!(field_get(r, 3, 14), 0xEDC_BA98_7654_3); assert_eq!(field_get(r, 15, 15), 0xF); assert_eq!(field_get(r, 0, 15), r); assert_eq!(field_set(r, 0, 1, 0xABC), 0xFEDC_BA98_7654_32BC); assert_eq!(field_set(r, 0, 15, 5), 5);
assert_eq!(field_set(r, 15, 15, 0), 0x0EDC_BA98_7654_3210);
}
#[test]
fn work_reg_get_set() {
let mut regs = Registers::default();
regs.set(Reg::A, 1);
regs.set(Reg::B, 2);
regs.set(Reg::C, 3);
regs.set(Reg::D, 4);
assert_eq!(
[Reg::A, Reg::B, Reg::C, Reg::D].map(|r| regs.get(r)),
[1, 2, 3, 4]
);
}
#[test]
fn rstk_circular() {
let mut regs = Registers::default();
for v in 1..=9 {
regs.push(v);
}
let popped: Vec<u32> = (0..9).map(|_| regs.pop()).collect();
assert_eq!(popped, vec![9, 8, 7, 6, 5, 4, 3, 2, 0]);
assert!(regs.rstk.levels().iter().all(|&l| l == 0));
regs.push(0x1_2345_6);
assert_eq!(regs.pop(), 0x2_3456);
}
#[test]
fn reset_state() {
let mut regs = Registers {
pc: 0x1234,
p: 7,
carry: true,
mode: Mode::Dec,
st: 0xFFFF,
hst: 0xF,
interrupts_enabled: false,
..Registers::default()
};
regs.push(5);
regs.reset();
assert_eq!(regs, Registers::default());
assert_eq!(regs.pc, 0);
assert_eq!(regs.mode, Mode::Hex);
assert!(regs.interrupts_enabled);
assert!(!regs.carry);
assert_eq!(regs.rstk.levels(), &[0; RSTK_DEPTH]);
}
#[test]
fn masked_setters() {
let mut regs = Registers::default();
regs.set_d0(0xFFF_FFFF);
regs.set_d1(0x10_0001);
regs.set_pc(0x12_3456);
regs.set_p(0x1F);
regs.set_out(0xFFFF);
assert_eq!(
(regs.d0, regs.d1, regs.pc, regs.p, regs.out),
(0xF_FFFF, 0x1, 0x2_3456, 0xF, 0xFFF)
);
}
}