pub mod asm;
pub mod sim;
use std::fmt::Write as _;
use std::num::TryFromIntError;
use offset_base::OffsetBacking;
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
pub enum Reg {
R0 = 0,
R1 = 1,
R2 = 2,
R3 = 3,
R4 = 4,
R5 = 5,
R6 = 6,
R7 = 7
}
impl Reg {
pub(crate) const REG_SIZE: usize = 8;
pub fn reg_no(self) -> u8 {
self as u8
}
}
impl std::fmt::Display for Reg {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "R{}", self.reg_no())
}
}
impl From<Reg> for usize {
fn from(value: Reg) -> Self {
usize::from(value.reg_no())
}
}
impl TryFrom<u8> for Reg {
type Error = TryFromIntError;
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
0 => Ok(Reg::R0),
1 => Ok(Reg::R1),
2 => Ok(Reg::R2),
3 => Ok(Reg::R3),
4 => Ok(Reg::R4),
5 => Ok(Reg::R5),
6 => Ok(Reg::R6),
7 => Ok(Reg::R7),
_ => u8::try_from(256).map(|_| unreachable!("should've been TryFromIntError")),
}
}
}
pub type CondCode = u8;
pub type IOffset<const N: u32> = Offset<i16, N>;
pub type TrapVect8 = Offset<u16, 8>;
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
pub enum ImmOrReg<const N: u32> {
#[allow(missing_docs)]
Imm(IOffset<N>),
#[allow(missing_docs)]
Reg(Reg)
}
impl<const N: u32> std::fmt::Display for ImmOrReg<N> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ImmOrReg::Imm(imm) => imm.fmt(f),
ImmOrReg::Reg(reg) => reg.fmt(f),
}
}
}
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
pub struct Offset<OFF, const N: u32>(OFF);
impl<OFF: std::fmt::Display, const N: u32> std::fmt::Display for Offset<OFF, N> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_char('#')?;
self.0.fmt(f)
}
}
impl<OFF: std::fmt::Binary, const N: u32> std::fmt::Binary for Offset<OFF, N> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_char('b')?;
self.0.fmt(f)
}
}
impl<OFF: std::fmt::LowerHex, const N: u32> std::fmt::LowerHex for Offset<OFF, N> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_char('x')?;
self.0.fmt(f)
}
}
impl<OFF: std::fmt::UpperHex, const N: u32> std::fmt::UpperHex for Offset<OFF, N> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_char('x')?;
self.0.fmt(f)
}
}
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
pub enum OffsetNewErr {
CannotFitUnsigned(u32),
CannotFitSigned(u32)
}
impl std::fmt::Display for OffsetNewErr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
OffsetNewErr::CannotFitUnsigned(n) => write!(f, "value is too big for unsigned {n}-bit integer"),
OffsetNewErr::CannotFitSigned(n) => write!(f, "value is too big for signed {n}-bit integer"),
}
}
}
impl std::error::Error for OffsetNewErr {}
impl crate::err::Error for OffsetNewErr {
fn help(&self) -> Option<std::borrow::Cow<str>> {
use std::borrow::Cow;
let error = match self {
OffsetNewErr::CannotFitUnsigned(n) => Cow::from(format!("the range for an unsigned {n}-bit integer is [0, {}]", (1 << n) - 1)),
OffsetNewErr::CannotFitSigned(n) => Cow::from(format!("the range for a signed {n}-bit integer is [{}, {}]", (-1) << (n - 1), (1 << (n - 1)) - 1)),
};
Some(error)
}
}
mod offset_base {
use super::OffsetNewErr;
pub trait OffsetBacking: Copy + Eq {
const BITS: u32;
fn truncate(self, bit_size: u32) -> Self;
fn does_not_fit_error(bit_size: u32) -> OffsetNewErr;
}
macro_rules! impl_offset_backing_for_ints {
($($Int:ty: $Err:ident),*) => {
$(
impl OffsetBacking for $Int {
const BITS: u32 = Self::BITS;
fn truncate(self, bit_size: u32) -> Self {
(self << (Self::BITS - bit_size)) >> (Self::BITS - bit_size)
}
fn does_not_fit_error(bit_size: u32) -> OffsetNewErr {
OffsetNewErr::$Err(bit_size)
}
}
)*
}
}
impl_offset_backing_for_ints! {
u16: CannotFitUnsigned,
i16: CannotFitSigned
}
}
impl<OFF: OffsetBacking, const N: u32> Offset<OFF, N> {
pub fn new(n: OFF) -> Result<Self, OffsetNewErr> {
assert!(N <= OFF::BITS, "bit size {N} exceeds size of backing ({})", OFF::BITS);
match n == n.truncate(N) {
true => Ok(Offset(n)),
false => Err(OFF::does_not_fit_error(N)),
}
}
pub fn new_trunc(n: OFF) -> Self {
assert!(N <= OFF::BITS, "bit size {N} exceeds size of backing ({})", OFF::BITS);
Self(n.truncate(N))
}
pub fn get(&self) -> OFF {
self.0
}
}
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
pub enum PCOffset<OFF, const N: u32> {
#[allow(missing_docs)]
Offset(Offset<OFF, N>),
#[allow(missing_docs)]
Label(Label)
}
impl<OFF, const N: u32> std::fmt::Display for PCOffset<OFF, N>
where Offset<OFF, N>: std::fmt::Display
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
PCOffset::Offset(off) => off.fmt(f),
PCOffset::Label(label) => label.fmt(f),
}
}
}
#[derive(Clone, PartialEq, Eq, Hash, Debug, Default)]
pub struct Label {
pub name: String,
start: usize
}
impl Label {
pub fn new(name: String, span: std::ops::Range<usize>) -> Self {
debug_assert_eq!(span.start + name.len(), span.end, "span should have the same length as name");
Label { name, start: span.start }
}
pub fn span(&self) -> std::ops::Range<usize> {
self.start .. (self.start + self.name.len())
}
}
impl std::fmt::Display for Label {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.name.fmt(f)
}
}