From: Cassie Jones Date: Wed, 22 Jan 2020 06:41:59 +0000 (-0500) Subject: Implement instruction execution X-Git-Url: https://git.witchoflight.com/jade-mouse/commitdiff_plain?ds=sidebyside Implement instruction execution --- diff --git a/toolchain/src/cpu.rs b/toolchain/src/cpu.rs index 232617c..18a9a30 100644 --- a/toolchain/src/cpu.rs +++ b/toolchain/src/cpu.rs @@ -1,11 +1,11 @@ -use crate::inst::{Inst, Reg}; +use crate::inst::{Addr, Cond, Half, Inst, LdSt, Op1, Op2, OpC, Reg, Size}; use std::ops::{Index, IndexMut}; #[cfg(test)] mod test; struct RegFile { - values: [u8; 4], + values: [u16; 4], } pub struct Cpu { @@ -25,7 +25,7 @@ impl Cpu { } } - pub fn reg(&self, reg: Reg) -> u8 { + pub fn reg(&self, reg: Reg) -> u16 { self.reg[reg] } @@ -39,15 +39,125 @@ impl Cpu { pub fn run_inst(&mut self, inst: &Inst) { match *inst { - Inst::Move(dst, src) => self.reg[dst] = self.reg[src], - _ => todo!(), + Inst::Halt | Inst::Nope => (), + Inst::Alu2(rd, op, r2) => { + self.reg[rd] = match op { + Op2::Add => self.reg[rd].wrapping_add(self.reg[r2]), + Op2::Sub => self.reg[rd].wrapping_sub(self.reg[r2]), + Op2::AddPc => self.pc.wrapping_add(self.reg[r2]), + Op2::And => self.reg[rd] & self.reg[r2], + Op2::Or => self.reg[rd] | self.reg[r2], + Op2::Xor => self.reg[rd] ^ self.reg[r2], + } + } + Inst::Jalr(r) => std::mem::swap(&mut self.pc, &mut self.reg[r]), + Inst::Move(rd, r2) => self.reg[rd] = self.reg[r2], + Inst::Alu1(rd, op) => { + self.reg[rd] = match op { + Op1::Inc => self.reg[rd].wrapping_add(1), + Op1::Dec => self.reg[rd].wrapping_sub(1), + Op1::Neg => 0u16.wrapping_sub(self.reg[rd]), // negate :) + Op1::Compl => !self.reg[rd], + } + } + Inst::Mem(LdSt::Ld, r, addr) => { + if let Addr::Extended { + base, + stack: true, + size, + .. + } = addr + { + self.reg[base] = self.reg[base].wrapping_sub(size.bytes()); + } + let (addr0, size) = self.eval_addr(addr); + self.reg[r] = self.load(addr0, size); + } + Inst::Mem(LdSt::St, r, addr) => { + let (addr0, size) = self.eval_addr(addr); + self.store(addr0, size, self.reg[r]); + if let Addr::Extended { + base, + stack: true, + size, + .. + } = addr + { + self.reg[base] = self.reg[base].wrapping_add(size.bytes()); + } + } + Inst::Branch(cond, offset) => { + if self.eval_cond(cond) { + self.pc = self.pc.offset(offset); + } + } + Inst::JumpI(offset) => self.pc = self.pc.offset(offset), + Inst::AddI(rd, i) => self.reg[rd] = self.reg[rd].offset(i), + Inst::AluCompact(rd, op, i) => { + self.reg[rd] = match op { + OpC::Lsl => self.reg[rd] << i.get(), + OpC::Lsr => self.reg[rd] >> i.get(), + OpC::Asr => (self.reg[rd] as i16 >> i.get()) as u16, + OpC::Rol => self.reg[rd].rotate_left(i.get() as u32), + OpC::Clr => self.reg[rd] & !(1 << i.get()), + OpC::Set => self.reg[rd] | (1 << i.get()), + OpC::Tog => self.reg[rd] ^ (1 << i.get()), + OpC::Ext => self.reg[rd] & (1 << i.get()), + } + } + Inst::LdImm(Half::Low, rd, i) => self.reg[rd] = self.reg[rd] & 0xFF00 | i as u16, + Inst::LdImm(Half::High, rd, i) => { + self.reg[rd] = self.reg[rd] & 0x00FF | (i as u16) << 8 + } + } + } + + fn eval_addr(&self, addr: Addr) -> (u16, Size) { + match addr { + Addr::Fixed(i) => (i.get() * 2, Size::Word), + Addr::Reg(r) => (self.reg[r], Size::Word), + Addr::Extended { + base, size, offset, .. + } => (self.reg[base].offset(offset), size), + } + } + + fn load(&self, addr: u16, size: Size) -> u16 { + match size { + Size::Byte => self.memory[addr as usize] as u16, + Size::Word => u16::from_le_bytes([ + self.memory[addr as usize], + self.memory[addr.wrapping_add(1) as usize], + ]), + } + } + + fn store(&mut self, addr: u16, size: Size, value: u16) { + match size { + Size::Byte => self.memory[addr as usize] = value as u8, + Size::Word => { + let [b0, b1] = value.to_le_bytes(); + self.memory[addr as usize] = b0; + self.memory[addr.wrapping_add(1) as usize] = b1; + } + } + } + + fn eval_cond(&self, cond: Cond) -> bool { + match cond { + Cond::Eql(a, b) => self.reg[a] == self.reg[b], + Cond::Neq(a, b) => self.reg[a] != self.reg[b], + Cond::Test(a, b) => self.reg[a] & self.reg[b] != 0, + Cond::TestNot(a, b) => self.reg[a] & self.reg[b] == 0, + Cond::Lt(a, b) => (self.reg[a] as i16) < self.reg[b] as i16, + Cond::Ult(a, b) => self.reg[a] < self.reg[b], } } } impl Index for RegFile { - type Output = u8; - fn index(&self, index: Reg) -> &u8 { + type Output = u16; + fn index(&self, index: Reg) -> &Self::Output { match index { Reg::R0 => &self.values[0], Reg::R1 => &self.values[1], @@ -58,7 +168,7 @@ impl Index for RegFile { } impl IndexMut for RegFile { - fn index_mut(&mut self, index: Reg) -> &mut u8 { + fn index_mut(&mut self, index: Reg) -> &mut Self::Output { match index { Reg::R0 => &mut self.values[0], Reg::R1 => &mut self.values[1], @@ -89,3 +199,13 @@ impl std::fmt::Debug for Cpu { .finish() } } + +trait OffsetExt { + fn offset(&self, x: impl Into) -> Self; +} + +impl OffsetExt for u16 { + fn offset(&self, x: impl Into) -> Self { + self.wrapping_add(x.into() as u16) + } +} diff --git a/toolchain/src/cpu/test.rs b/toolchain/src/cpu/test.rs index 5057354..d707ec6 100644 --- a/toolchain/src/cpu/test.rs +++ b/toolchain/src/cpu/test.rs @@ -1,11 +1,11 @@ use super::*; -use crate::inst::{Inst, Reg}; +use crate::inst::{Inst, Reg, Half}; use proptest::prelude::*; fn cpu() -> impl Strategy { ( 0..1024u16, - [any::(); 4], + [any::(); 4], prop::collection::vec(any::(), 0..1024), ) .prop_map(|(pc, reg, memory)| Cpu { @@ -35,4 +35,22 @@ proptest! { prop_assert_eq!(val, cpu.reg(src)); prop_assert_eq!(val, cpu.reg(dst)); } + + #[test] + fn run_jalr(mut cpu in cpu(), r in reg()) { + let inst = Inst::Jalr(r); + let dest = cpu.reg(r); + let pc0 = cpu.pc; + cpu.run_inst(&inst); + prop_assert_eq!(pc0, cpu.reg(r)); + prop_assert_eq!(dest, cpu.pc); + } + + #[test] + fn run_imm_lohi(mut cpu in cpu(), r in reg(), val: u16) { + let [b0, b1] = val.to_le_bytes(); + cpu.run_inst(&Inst::LdImm(Half::Low, r, b0)); + cpu.run_inst(&Inst::LdImm(Half::High, r, b1)); + prop_assert_eq!(val, cpu.reg(r)); + } } diff --git a/toolchain/src/inst.rs b/toolchain/src/inst.rs index 130afc9..1f6fcdd 100644 --- a/toolchain/src/inst.rs +++ b/toolchain/src/inst.rs @@ -154,3 +154,46 @@ impl PartialEq for Cond { } } } + +impl Size { + pub fn bytes(&self) -> u16 { + match *self { + Size::Byte => 1, + Size::Word => 2, + } + } +} + +impl U4 { + pub fn get(&self) -> usize { + self.0 as usize + } +} + +impl U7 { + pub fn get(&self) -> u16 { + self.0 as u16 + } +} + +impl I5 { + pub fn get(&self) -> i8 { + self.0 + } +} + +impl Into for I5 { + fn into(self) -> i32 { + self.0 as i32 + } +} + +impl Into for I7 { + fn into(self) -> i32 { + self.0 as i32 + } +} + +/* +pub struct I5(#[cfg_attr(test, proptest(strategy = "-16..=15i8"))] i8); +*/ diff --git a/toolchain/src/inst/test.rs b/toolchain/src/inst/test.rs index efe1b5e..95aafdb 100644 --- a/toolchain/src/inst/test.rs +++ b/toolchain/src/inst/test.rs @@ -1,4 +1,4 @@ -use super::{Cond, Decode, Encode, Inst, Reg}; +use super::{Decode, Encode, Inst, Reg}; use proptest::prelude::*; use std::{fmt::Debug, io::Cursor};