mirror of
https://github.com/Zenithsiz/dcb.git
synced 2026-02-04 00:21:57 +00:00
Added initial support for executing instructions.
This commit is contained in:
parent
233db9b200
commit
3c02d93dd9
@ -5,6 +5,7 @@ pub mod basic;
|
||||
pub mod decode;
|
||||
pub mod directive;
|
||||
pub mod error;
|
||||
pub mod exec;
|
||||
pub mod fmt;
|
||||
pub mod parse;
|
||||
pub mod pseudo;
|
||||
|
||||
@ -18,6 +18,7 @@ pub mod sys;
|
||||
|
||||
// Imports
|
||||
use super::{
|
||||
exec::{ExecError, ExecState, Executable},
|
||||
parse::{LineArg, Parsable},
|
||||
DisplayCtx, InstDisplay, InstFmtArg, InstSize, ParseCtx, ParseError, Register,
|
||||
};
|
||||
@ -194,6 +195,25 @@ impl ModifiesReg for Inst {
|
||||
}
|
||||
}
|
||||
|
||||
impl Executable for Inst {
|
||||
#[rustfmt::skip]
|
||||
fn exec(&self, state: &mut ExecState) -> Result<(), ExecError> {
|
||||
match self {
|
||||
Inst::Alu (inst) => inst.exec(state),
|
||||
Inst::Cond (inst) => inst.exec(state),
|
||||
Inst::Jmp (inst) => inst.exec(state),
|
||||
Inst::Load (inst) => inst.exec(state),
|
||||
Inst::Lui (inst) => inst.exec(state),
|
||||
Inst::Mult (inst) => inst.exec(state),
|
||||
Inst::Shift(inst) => inst.exec(state),
|
||||
Inst::Store(inst) => inst.exec(state),
|
||||
//Inst::Sys (inst) => inst.exec(state),
|
||||
//Inst::Co (inst) => inst.exec(state),
|
||||
_ => todo!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Any basic decodable instruction is 4 bytes
|
||||
impl<T: Decode> InstSize for T {
|
||||
fn size(&self) -> usize {
|
||||
|
||||
@ -8,6 +8,7 @@ pub mod reg;
|
||||
use super::ModifiesReg;
|
||||
use crate::inst::{
|
||||
basic::{Decode, Encode},
|
||||
exec::{ExecError, ExecState, Executable},
|
||||
parse::LineArg,
|
||||
DisplayCtx, InstDisplay, InstFmtArg, Parsable, ParseCtx, ParseError,
|
||||
};
|
||||
@ -78,3 +79,12 @@ impl ModifiesReg for Inst {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Executable for Inst {
|
||||
fn exec(&self, state: &mut ExecState) -> Result<(), ExecError> {
|
||||
match self {
|
||||
Inst::Imm(inst) => inst.exec(state),
|
||||
Inst::Reg(inst) => inst.exec(state),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,10 +3,11 @@
|
||||
// Imports
|
||||
use crate::inst::{
|
||||
basic::{Decode, Encode, ModifiesReg},
|
||||
exec::{ExecError, ExecState, Executable},
|
||||
parse::LineArg,
|
||||
DisplayCtx, InstDisplay, InstFmtArg, Parsable, ParseCtx, ParseError, Register,
|
||||
};
|
||||
use int_conv::{Signed, Truncated, ZeroExtended};
|
||||
use int_conv::{SignExtended, Signed, Truncated, ZeroExtended};
|
||||
use std::{array, convert::TryInto};
|
||||
|
||||
/// Instruction kind
|
||||
@ -184,3 +185,29 @@ impl ModifiesReg for Inst {
|
||||
self.dst == reg
|
||||
}
|
||||
}
|
||||
|
||||
impl Executable for Inst {
|
||||
fn exec(&self, state: &mut ExecState) -> Result<(), ExecError> {
|
||||
state[self.dst] = match self.kind {
|
||||
Kind::Add(rhs) => state[self.lhs]
|
||||
.as_signed()
|
||||
.checked_add(rhs.sign_extended::<i32>())
|
||||
.ok_or(ExecError::Overflow)?
|
||||
.as_unsigned(),
|
||||
Kind::AddUnsigned(rhs) => state[self.lhs]
|
||||
.as_signed()
|
||||
.wrapping_add(rhs.sign_extended::<i32>())
|
||||
.as_unsigned(),
|
||||
Kind::SetLessThan(rhs) => state[self.lhs].as_signed().lt(&rhs.sign_extended::<i32>()).into(),
|
||||
// TODO: Verify it's sign extended
|
||||
Kind::SetLessThanUnsigned(rhs) => state[self.lhs]
|
||||
.lt(&rhs.as_signed().sign_extended::<i32>().as_unsigned())
|
||||
.into(),
|
||||
Kind::And(rhs) => state[self.lhs] & rhs.zero_extended::<u32>(),
|
||||
Kind::Or(rhs) => state[self.lhs] | rhs.zero_extended::<u32>(),
|
||||
Kind::Xor(rhs) => state[self.lhs] ^ rhs.zero_extended::<u32>(),
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,9 +3,11 @@
|
||||
// Imports
|
||||
use crate::inst::{
|
||||
basic::{Decode, Encode, ModifiesReg},
|
||||
exec::{ExecError, ExecState, Executable},
|
||||
parse::LineArg,
|
||||
DisplayCtx, InstDisplay, InstFmtArg, Parsable, ParseCtx, ParseError, Register,
|
||||
};
|
||||
use int_conv::Signed;
|
||||
use std::array;
|
||||
|
||||
/// Alu register instruction kind
|
||||
@ -198,3 +200,33 @@ impl ModifiesReg for Inst {
|
||||
self.dst == reg
|
||||
}
|
||||
}
|
||||
|
||||
impl Executable for Inst {
|
||||
fn exec(&self, state: &mut ExecState) -> Result<(), ExecError> {
|
||||
let lhs = state[self.lhs];
|
||||
let rhs = state[self.rhs];
|
||||
|
||||
state[self.dst] = match self.kind {
|
||||
Kind::Add => lhs
|
||||
.as_signed()
|
||||
.checked_add(rhs.as_signed())
|
||||
.ok_or(ExecError::Overflow)?
|
||||
.as_unsigned(),
|
||||
Kind::AddUnsigned => lhs.as_signed().wrapping_add(rhs.as_signed()).as_unsigned(),
|
||||
Kind::Sub => lhs
|
||||
.as_signed()
|
||||
.checked_sub(rhs.as_signed())
|
||||
.ok_or(ExecError::Overflow)?
|
||||
.as_unsigned(),
|
||||
Kind::SubUnsigned => lhs.as_signed().wrapping_sub(rhs.as_signed()).as_unsigned(),
|
||||
Kind::And => lhs & rhs,
|
||||
Kind::Or => lhs | rhs,
|
||||
Kind::Xor => lhs ^ rhs,
|
||||
Kind::Nor => !(lhs | rhs),
|
||||
Kind::SetLessThan => (lhs.as_signed() < rhs.as_signed()).into(),
|
||||
Kind::SetLessThanUnsigned => (lhs < rhs).into(),
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,6 +5,7 @@ use super::ModifiesReg;
|
||||
use crate::{
|
||||
inst::{
|
||||
basic::{Decode, Encode},
|
||||
exec::{ExecError, ExecState, Executable},
|
||||
parse::LineArg,
|
||||
DisplayCtx, InstDisplay, InstFmtArg, Parsable, ParseCtx, ParseError, Register,
|
||||
},
|
||||
@ -264,3 +265,30 @@ impl ModifiesReg for Inst {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl Executable for Inst {
|
||||
fn exec(&self, state: &mut ExecState) -> Result<(), ExecError> {
|
||||
let lhs = state[self.arg].as_signed();
|
||||
let do_jump = match self.kind {
|
||||
Kind::Equal(rhs) => lhs == state[rhs].as_signed(),
|
||||
Kind::NotEqual(rhs) => lhs != state[rhs].as_signed(),
|
||||
Kind::LessOrEqualZero => lhs <= 0i32,
|
||||
Kind::GreaterThanZero => lhs > 0i32,
|
||||
Kind::LessThanZero | Kind::LessThanZeroLink => lhs < 0i32,
|
||||
Kind::GreaterOrEqualZero | Kind::GreaterOrEqualZeroLink => lhs >= 0i32,
|
||||
};
|
||||
|
||||
match do_jump {
|
||||
true => {
|
||||
// If we should link, set `$ra`
|
||||
if matches!(self.kind, Kind::LessThanZeroLink | Kind::GreaterOrEqualZeroLink) {
|
||||
state[Register::Ra] = (state.pc() + 8u32).0;
|
||||
}
|
||||
|
||||
// Then set the jump
|
||||
state.set_jump(self.target(state.pc()))
|
||||
},
|
||||
false => Ok(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,6 +8,7 @@ pub mod reg;
|
||||
use super::ModifiesReg;
|
||||
use crate::inst::{
|
||||
basic::{Decode, Encode},
|
||||
exec::{ExecError, ExecState, Executable},
|
||||
parse::LineArg,
|
||||
DisplayCtx, InstDisplay, InstFmtArg, Parsable, ParseCtx, ParseError, Register,
|
||||
};
|
||||
@ -78,3 +79,12 @@ impl ModifiesReg for Inst {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Executable for Inst {
|
||||
fn exec(&self, state: &mut ExecState) -> Result<(), ExecError> {
|
||||
match self {
|
||||
Inst::Imm(inst) => inst.exec(state),
|
||||
Inst::Reg(inst) => inst.exec(state),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
use crate::{
|
||||
inst::{
|
||||
basic::{Decode, Encode, ModifiesReg},
|
||||
exec::{ExecError, ExecState, Executable},
|
||||
parse::LineArg,
|
||||
DisplayCtx, InstDisplay, InstFmtArg, Parsable, ParseCtx, ParseError, Register,
|
||||
},
|
||||
@ -134,3 +135,15 @@ impl ModifiesReg for Inst {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl Executable for Inst {
|
||||
fn exec(&self, state: &mut ExecState) -> Result<(), ExecError> {
|
||||
// If we should link, set `$ra`
|
||||
if matches!(self.kind, Kind::JumpLink) {
|
||||
state[Register::Ra] = (state.pc() + 8u32).0;
|
||||
}
|
||||
|
||||
// Then set the jump
|
||||
state.set_jump(self.target(state.pc()))
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,10 +1,14 @@
|
||||
//! Jump register instructions
|
||||
|
||||
// Imports
|
||||
use crate::inst::{
|
||||
basic::{Decode, Encode, ModifiesReg},
|
||||
parse::LineArg,
|
||||
DisplayCtx, InstDisplay, InstFmtArg, Parsable, ParseCtx, ParseError, Register,
|
||||
use crate::{
|
||||
inst::{
|
||||
basic::{Decode, Encode, ModifiesReg},
|
||||
exec::{ExecError, ExecState, Executable},
|
||||
parse::LineArg,
|
||||
DisplayCtx, InstDisplay, InstFmtArg, Parsable, ParseCtx, ParseError, Register,
|
||||
},
|
||||
Pos,
|
||||
};
|
||||
use std::array;
|
||||
|
||||
@ -122,3 +126,15 @@ impl ModifiesReg for Inst {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl Executable for Inst {
|
||||
fn exec(&self, state: &mut ExecState) -> Result<(), ExecError> {
|
||||
// If we should link, set `$ra`
|
||||
if let Kind::JumpLink(link) = self.kind {
|
||||
state[link] = (state.pc() + 8u32).0;
|
||||
}
|
||||
|
||||
// Then set the jump
|
||||
state.set_jump(Pos(state[self.target]))
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,12 +2,16 @@
|
||||
|
||||
// Imports
|
||||
use super::ModifiesReg;
|
||||
use crate::inst::{
|
||||
basic::{Decode, Encode},
|
||||
parse::LineArg,
|
||||
DisplayCtx, InstDisplay, InstFmtArg, Parsable, ParseCtx, ParseError, Register,
|
||||
use crate::{
|
||||
inst::{
|
||||
basic::{Decode, Encode},
|
||||
exec::{ExecError, ExecState, Executable},
|
||||
parse::LineArg,
|
||||
DisplayCtx, InstDisplay, InstFmtArg, Parsable, ParseCtx, ParseError, Register,
|
||||
},
|
||||
Pos,
|
||||
};
|
||||
use int_conv::{Signed, Truncated, ZeroExtended};
|
||||
use int_conv::{SignExtended, Signed, Truncated, ZeroExtended};
|
||||
use std::array;
|
||||
|
||||
/// Instruction kind
|
||||
@ -174,3 +178,27 @@ impl ModifiesReg for Inst {
|
||||
self.value == reg
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl Executable for Inst {
|
||||
fn exec(&self, state: &mut ExecState) -> Result<(), ExecError> {
|
||||
state[self.value] = match self.kind {
|
||||
Kind::Byte => state
|
||||
.read_byte(Pos(state[self.addr]))?
|
||||
.as_signed()
|
||||
.sign_extended::<i32>()
|
||||
.as_unsigned(),
|
||||
Kind::HalfWord => state
|
||||
.read_half_word(Pos(state[self.addr]))?
|
||||
.as_signed()
|
||||
.sign_extended::<i32>()
|
||||
.as_unsigned(),
|
||||
Kind::Word => state.read_word(Pos(state[self.addr]))?,
|
||||
Kind::ByteUnsigned => state.read_byte(Pos(state[self.addr]))?.zero_extended(),
|
||||
Kind::HalfWordUnsigned => state.read_half_word(Pos(state[self.addr]))?.zero_extended(),
|
||||
Kind::WordLeft | Kind::WordRight => todo!(),
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,10 +4,11 @@
|
||||
use super::ModifiesReg;
|
||||
use crate::inst::{
|
||||
basic::{Decode, Encode},
|
||||
exec::{ExecError, ExecState, Executable},
|
||||
parse::LineArg,
|
||||
DisplayCtx, InstDisplay, InstFmtArg, Parsable, ParseCtx, ParseError, Register,
|
||||
};
|
||||
use int_conv::{Truncated, ZeroExtended};
|
||||
use int_conv::{Join, Truncated, ZeroExtended};
|
||||
use std::array;
|
||||
|
||||
/// Load instructions
|
||||
@ -82,3 +83,10 @@ impl ModifiesReg for Inst {
|
||||
self.dst == reg
|
||||
}
|
||||
}
|
||||
|
||||
impl Executable for Inst {
|
||||
fn exec(&self, state: &mut ExecState) -> Result<(), ExecError> {
|
||||
state[self.dst] = u32::join(0, self.value);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,9 +4,11 @@
|
||||
use super::ModifiesReg;
|
||||
use crate::inst::{
|
||||
basic::{Decode, Encode},
|
||||
exec::{ExecError, ExecState, Executable},
|
||||
parse::LineArg,
|
||||
DisplayCtx, InstDisplay, InstFmtArg, Parsable, ParseCtx, ParseError, Register,
|
||||
};
|
||||
use int_conv::{SignExtended, Signed, Split, ZeroExtended};
|
||||
use std::array;
|
||||
|
||||
/// Operation kind
|
||||
@ -243,3 +245,48 @@ impl ModifiesReg for Inst {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Executable for Inst {
|
||||
fn exec(&self, state: &mut ExecState) -> Result<(), ExecError> {
|
||||
match *self {
|
||||
Inst::Mult { kind, mode, lhs, rhs } => {
|
||||
let (lo, hi) = match (kind, mode) {
|
||||
(MultKind::Mult, MultMode::Signed) => {
|
||||
let lhs: i64 = state[lhs].as_signed().sign_extended();
|
||||
let rhs: i64 = state[rhs].as_signed().sign_extended();
|
||||
|
||||
lhs.wrapping_mul(rhs).as_unsigned().lo_hi()
|
||||
},
|
||||
(MultKind::Mult, MultMode::Unsigned) => {
|
||||
let lhs: u64 = state[lhs].zero_extended();
|
||||
let rhs: u64 = state[rhs].zero_extended();
|
||||
|
||||
lhs.wrapping_mul(rhs).as_unsigned().lo_hi()
|
||||
},
|
||||
(MultKind::Div, MultMode::Signed) => match (state[lhs].as_signed(), state[rhs].as_signed()) {
|
||||
(lhs @ 0i32..=i32::MAX, 0i32) => ((-1i32).as_unsigned(), lhs.as_unsigned()),
|
||||
(lhs @ i32::MIN..0i32, 0i32) => (1u32, lhs.as_unsigned()),
|
||||
(lhs, rhs) => (lhs.wrapping_div(rhs).as_unsigned(), lhs.wrapping_rem(rhs).as_unsigned()),
|
||||
},
|
||||
(MultKind::Div, MultMode::Unsigned) => match (state[lhs], state[rhs]) {
|
||||
(lhs, 0) => ((-1i32).as_unsigned(), lhs),
|
||||
(lhs, rhs) => (lhs / rhs, lhs % rhs),
|
||||
},
|
||||
};
|
||||
|
||||
state[MultReg::Lo] = lo;
|
||||
state[MultReg::Hi] = hi;
|
||||
|
||||
Ok(())
|
||||
},
|
||||
Inst::MoveFrom { dst, src } => {
|
||||
state[dst] = state[src];
|
||||
Ok(())
|
||||
},
|
||||
Inst::MoveTo { src, dst } => {
|
||||
state[dst] = state[src];
|
||||
Ok(())
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,6 +7,7 @@ pub mod reg;
|
||||
// Imports
|
||||
use crate::inst::{
|
||||
basic::{Decode, Encode, ModifiesReg, TryEncode},
|
||||
exec::{ExecError, ExecState, Executable},
|
||||
parse::LineArg,
|
||||
DisplayCtx, InstDisplay, InstFmtArg, Parsable, ParseCtx, ParseError, Register,
|
||||
};
|
||||
@ -87,3 +88,12 @@ impl ModifiesReg for Inst {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Executable for Inst {
|
||||
fn exec(&self, state: &mut ExecState) -> Result<(), ExecError> {
|
||||
match self {
|
||||
Inst::Imm(inst) => inst.exec(state),
|
||||
Inst::Reg(inst) => inst.exec(state),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,10 +3,11 @@
|
||||
// Imports
|
||||
use crate::inst::{
|
||||
basic::{Decode, ModifiesReg, TryEncode},
|
||||
exec::{ExecError, ExecState, Executable},
|
||||
parse::LineArg,
|
||||
DisplayCtx, InstDisplay, InstFmtArg, Parsable, ParseCtx, ParseError, Register,
|
||||
};
|
||||
use int_conv::{Truncated, ZeroExtended};
|
||||
use int_conv::{Signed, Truncated, ZeroExtended};
|
||||
use std::array;
|
||||
|
||||
/// Shift immediate instruction kind
|
||||
@ -157,3 +158,18 @@ impl ModifiesReg for Inst {
|
||||
self.dst == reg
|
||||
}
|
||||
}
|
||||
|
||||
impl Executable for Inst {
|
||||
fn exec(&self, state: &mut ExecState) -> Result<(), ExecError> {
|
||||
state[self.dst] = match self.kind {
|
||||
Kind::LeftLogical => state[self.lhs].wrapping_shl(self.rhs.zero_extended()),
|
||||
Kind::RightLogical => state[self.lhs].wrapping_shr(self.rhs.zero_extended()),
|
||||
Kind::RightArithmetic => state[self.lhs]
|
||||
.as_signed()
|
||||
.wrapping_shr(self.rhs.zero_extended())
|
||||
.as_unsigned(),
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,9 +3,11 @@
|
||||
// Imports
|
||||
use crate::inst::{
|
||||
basic::{Decode, Encode, ModifiesReg},
|
||||
exec::{ExecError, ExecState, Executable},
|
||||
parse::LineArg,
|
||||
DisplayCtx, InstDisplay, InstFmtArg, Parsable, ParseCtx, ParseError, Register,
|
||||
};
|
||||
use int_conv::Signed;
|
||||
use std::array;
|
||||
|
||||
/// Shift register instruction kind
|
||||
@ -142,3 +144,15 @@ impl ModifiesReg for Inst {
|
||||
self.dst == reg
|
||||
}
|
||||
}
|
||||
|
||||
impl Executable for Inst {
|
||||
fn exec(&self, state: &mut ExecState) -> Result<(), ExecError> {
|
||||
state[self.dst] = match self.kind {
|
||||
Kind::LeftLogical => state[self.lhs].wrapping_shl(state[self.rhs]),
|
||||
Kind::RightLogical => state[self.lhs].wrapping_shr(state[self.rhs]),
|
||||
Kind::RightArithmetic => state[self.lhs].as_signed().wrapping_shr(state[self.rhs]).as_unsigned(),
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,10 +2,14 @@
|
||||
|
||||
// Imports
|
||||
use super::ModifiesReg;
|
||||
use crate::inst::{
|
||||
basic::{Decode, Encode},
|
||||
parse::LineArg,
|
||||
DisplayCtx, InstDisplay, InstFmtArg, Parsable, ParseCtx, ParseError, Register,
|
||||
use crate::{
|
||||
inst::{
|
||||
basic::{Decode, Encode},
|
||||
exec::{ExecError, ExecState, Executable},
|
||||
parse::LineArg,
|
||||
DisplayCtx, InstDisplay, InstFmtArg, Parsable, ParseCtx, ParseError, Register,
|
||||
},
|
||||
Pos,
|
||||
};
|
||||
use int_conv::{Signed, Truncated, ZeroExtended};
|
||||
use std::array;
|
||||
@ -162,3 +166,14 @@ impl ModifiesReg for Inst {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl Executable for Inst {
|
||||
fn exec(&self, state: &mut ExecState) -> Result<(), ExecError> {
|
||||
match self.kind {
|
||||
Kind::Byte => state.write_byte(Pos(state[self.addr]), state[self.value].truncated()),
|
||||
Kind::HalfWord => state.write_half_word(Pos(state[self.addr]), state[self.value].truncated()),
|
||||
Kind::Word => state.write_word(Pos(state[self.addr]), state[self.value]),
|
||||
Kind::WordLeft | Kind::WordRight => todo!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
283
dcb-exe/src/inst/exec.rs
Normal file
283
dcb-exe/src/inst/exec.rs
Normal file
@ -0,0 +1,283 @@
|
||||
//! Execution
|
||||
|
||||
// Imports
|
||||
use crate::{
|
||||
inst::{
|
||||
basic::{self, mult::MultReg, Decode},
|
||||
Register,
|
||||
},
|
||||
Pos,
|
||||
};
|
||||
use byteorder::{ByteOrder, LittleEndian};
|
||||
use std::{
|
||||
convert::TryInto,
|
||||
ops::{Index, IndexMut},
|
||||
};
|
||||
|
||||
/// Execution state
|
||||
pub struct ExecState {
|
||||
/// Program counter
|
||||
pc: Pos,
|
||||
|
||||
/// Registers
|
||||
regs: [u32; 32],
|
||||
|
||||
/// Lo / Hi
|
||||
lo_hi_reg: [u32; 2],
|
||||
|
||||
/// Memory
|
||||
memory: Box<[u8]>,
|
||||
|
||||
/// Jump target
|
||||
jump_target: JumpTargetState,
|
||||
}
|
||||
|
||||
impl ExecState {
|
||||
/// Creates a new execution state
|
||||
#[must_use]
|
||||
pub fn new(memory: Box<[u8]>, pc: Pos) -> Self {
|
||||
Self {
|
||||
pc,
|
||||
regs: [0; 32],
|
||||
lo_hi_reg: [0; 2],
|
||||
memory,
|
||||
jump_target: JumpTargetState::None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Executes the next instruction
|
||||
pub fn exec(&mut self) -> Result<(), ExecError> {
|
||||
// Read the next instruction
|
||||
let inst = self.read_word(self.pc)?;
|
||||
|
||||
// Parse the instruction
|
||||
let inst = basic::Inst::decode(inst).ok_or(ExecError::DecodeInst)?;
|
||||
|
||||
// Then execute it
|
||||
inst.exec(self)?;
|
||||
|
||||
// Then update our pc depending on whether we have a jump
|
||||
self.pc = match self.jump_target {
|
||||
JumpTargetState::None => self.pc + 4u32,
|
||||
JumpTargetState::JumpNext(pos) => {
|
||||
self.jump_target = JumpTargetState::JumpNow(pos);
|
||||
self.pc + 4u32
|
||||
},
|
||||
JumpTargetState::JumpNow(pos) => {
|
||||
self.jump_target = JumpTargetState::None;
|
||||
pos
|
||||
},
|
||||
};
|
||||
|
||||
// And increment out program counter
|
||||
self.pc += 4u32;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns the current program counter
|
||||
#[must_use]
|
||||
pub const fn pc(&self) -> Pos {
|
||||
self.pc
|
||||
}
|
||||
|
||||
/// Sets a jump to happen
|
||||
pub fn set_jump(&mut self, pos: Pos) -> Result<(), ExecError> {
|
||||
match self.jump_target {
|
||||
JumpTargetState::None => {
|
||||
self.jump_target = JumpTargetState::JumpNext(pos);
|
||||
Ok(())
|
||||
},
|
||||
_ => Err(ExecError::JumpWhileJumping),
|
||||
}
|
||||
}
|
||||
|
||||
/// Reads a word from a memory position
|
||||
pub fn read_word(&self, pos: Pos) -> Result<u32, ExecError> {
|
||||
// If the position isn't aligned, return Err
|
||||
if !pos.is_word_aligned() {
|
||||
return Err(ExecError::MemoryUnalignedAccess { pos });
|
||||
}
|
||||
|
||||
// Ignore the top 3 bits
|
||||
let idx = pos.0 & 0x7FFF_FFFF;
|
||||
let idx = idx.try_into().expect("Memory position didn't fit into `usize`");
|
||||
|
||||
// Then read from memory
|
||||
let mem = self
|
||||
.memory
|
||||
.get(idx..(idx + 4))
|
||||
.ok_or(ExecError::MemoryOutOfBounds { pos })?;
|
||||
Ok(LittleEndian::read_u32(mem))
|
||||
}
|
||||
|
||||
/// Reads a half-word from a memory position
|
||||
pub fn read_half_word(&self, pos: Pos) -> Result<u16, ExecError> {
|
||||
// If the position isn't aligned, return Err
|
||||
if !pos.is_half_word_aligned() {
|
||||
return Err(ExecError::MemoryUnalignedAccess { pos });
|
||||
}
|
||||
|
||||
// Ignore the top 3 bits
|
||||
let idx = pos.0 & 0x7FFF_FFFF;
|
||||
let idx = idx.try_into().expect("Memory position didn't fit into `usize`");
|
||||
|
||||
// Then read from memory
|
||||
let mem = self
|
||||
.memory
|
||||
.get(idx..(idx + 2))
|
||||
.ok_or(ExecError::MemoryOutOfBounds { pos })?;
|
||||
Ok(LittleEndian::read_u16(mem))
|
||||
}
|
||||
|
||||
/// Reads a byte from a memory position
|
||||
pub fn read_byte(&self, pos: Pos) -> Result<u8, ExecError> {
|
||||
// Ignore the top 3 bits
|
||||
let idx = pos.0 & 0x7FFF_FFFF;
|
||||
let idx: usize = idx.try_into().expect("Memory position didn't fit into `usize`");
|
||||
|
||||
// Then read from memory
|
||||
self.memory
|
||||
.get(idx)
|
||||
.copied()
|
||||
.ok_or(ExecError::MemoryOutOfBounds { pos })
|
||||
}
|
||||
|
||||
/// Stores a word to a memory position
|
||||
pub fn write_word(&mut self, pos: Pos, value: u32) -> Result<(), ExecError> {
|
||||
// If the position isn't aligned, return Err
|
||||
if !pos.is_word_aligned() {
|
||||
return Err(ExecError::MemoryUnalignedAccess { pos });
|
||||
}
|
||||
|
||||
// Ignore the top 3 bits
|
||||
let idx = pos.0 & 0x7FFF_FFFF;
|
||||
let idx = idx.try_into().expect("Memory position didn't fit into `usize`");
|
||||
|
||||
// Then write to memory
|
||||
let mem = self
|
||||
.memory
|
||||
.get_mut(idx..(idx + 4))
|
||||
.ok_or(ExecError::MemoryOutOfBounds { pos })?;
|
||||
LittleEndian::write_u32(mem, value);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Writes a half-word to a memory position
|
||||
pub fn write_half_word(&mut self, pos: Pos, value: u16) -> Result<(), ExecError> {
|
||||
// If the position isn't aligned, return Err
|
||||
if !pos.is_half_word_aligned() {
|
||||
return Err(ExecError::MemoryUnalignedAccess { pos });
|
||||
}
|
||||
|
||||
// Ignore the top 3 bits
|
||||
let idx = pos.0 & 0x7FFF_FFFF;
|
||||
let idx = idx.try_into().expect("Memory position didn't fit into `usize`");
|
||||
|
||||
// Then write to memory
|
||||
let mem = self
|
||||
.memory
|
||||
.get_mut(idx..(idx + 2))
|
||||
.ok_or(ExecError::MemoryOutOfBounds { pos })?;
|
||||
LittleEndian::write_u16(mem, value);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Writes a byte to a memory position
|
||||
pub fn write_byte(&mut self, pos: Pos, value: u8) -> Result<(), ExecError> {
|
||||
// Ignore the top 3 bits
|
||||
let idx = pos.0 & 0x7FFF_FFFF;
|
||||
let idx: usize = idx.try_into().expect("Memory position didn't fit into `usize`");
|
||||
|
||||
// Then write to memory
|
||||
let mem = self.memory.get_mut(idx).ok_or(ExecError::MemoryOutOfBounds { pos })?;
|
||||
*mem = value;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Index<Register> for ExecState {
|
||||
type Output = u32;
|
||||
|
||||
fn index(&self, reg: Register) -> &Self::Output {
|
||||
let idx: usize = reg.idx().try_into().expect("Register index didn't fit into `usize`");
|
||||
&self.regs[idx]
|
||||
}
|
||||
}
|
||||
|
||||
impl IndexMut<Register> for ExecState {
|
||||
fn index_mut(&mut self, reg: Register) -> &mut Self::Output {
|
||||
let idx: usize = reg.idx().try_into().expect("Register index didn't fit into `usize`");
|
||||
&mut self.regs[idx]
|
||||
}
|
||||
}
|
||||
|
||||
impl Index<MultReg> for ExecState {
|
||||
type Output = u32;
|
||||
|
||||
fn index(&self, reg: MultReg) -> &Self::Output {
|
||||
match reg {
|
||||
MultReg::Lo => &self.lo_hi_reg[0],
|
||||
MultReg::Hi => &self.lo_hi_reg[1],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IndexMut<MultReg> for ExecState {
|
||||
fn index_mut(&mut self, reg: MultReg) -> &mut Self::Output {
|
||||
match reg {
|
||||
MultReg::Lo => &mut self.lo_hi_reg[0],
|
||||
MultReg::Hi => &mut self.lo_hi_reg[1],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An executable instruction
|
||||
pub trait Executable {
|
||||
/// Executes this instruction in `state`
|
||||
fn exec(&self, state: &mut ExecState) -> Result<(), ExecError>;
|
||||
}
|
||||
|
||||
/// Executing error
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum ExecError {
|
||||
/// Memory address was out of bounds
|
||||
#[error("Memory access for {pos} is out of bounds")]
|
||||
MemoryOutOfBounds {
|
||||
/// Position the instruction tried to access
|
||||
pos: Pos,
|
||||
},
|
||||
|
||||
/// Memory address was unaligned
|
||||
#[error("Memory access for {pos} was unaligned")]
|
||||
MemoryUnalignedAccess {
|
||||
/// Position which is unaligned
|
||||
pos: Pos,
|
||||
},
|
||||
|
||||
/// Unable to decode instruction
|
||||
#[error("Unable to decode instruction")]
|
||||
DecodeInst,
|
||||
|
||||
/// Overflow
|
||||
#[error("Overflow")]
|
||||
Overflow,
|
||||
|
||||
/// Attempted to jump while jumping
|
||||
#[error("Cannot jump while jumping")]
|
||||
JumpWhileJumping,
|
||||
}
|
||||
|
||||
/// Jump target state
|
||||
#[derive(PartialEq, Clone, Copy, Debug)]
|
||||
pub enum JumpTargetState {
|
||||
/// No jump
|
||||
None,
|
||||
|
||||
/// Jump next
|
||||
JumpNext(Pos),
|
||||
|
||||
/// Jump now
|
||||
JumpNow(Pos),
|
||||
}
|
||||
@ -19,7 +19,8 @@
|
||||
min_type_alias_impl_trait,
|
||||
external_doc,
|
||||
assert_matches,
|
||||
extend_one
|
||||
extend_one,
|
||||
exclusive_range_pattern
|
||||
)]
|
||||
// Lints
|
||||
#![warn(clippy::restriction, clippy::pedantic, clippy::nursery)]
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user