342 lines
8.6 KiB
Rust
342 lines
8.6 KiB
Rust
use std::fmt::{Display, Formatter};
|
|
|
|
// 128k, the amount of memory in a standard Vulcan machine
|
|
pub const MEM_SIZE: u32 = 128 * 1024;
|
|
|
|
#[derive(Debug, Copy, Clone, Eq)]
|
|
pub struct Word(u32);
|
|
|
|
impl Word {
|
|
/// Create a `Word` from three bytes.
|
|
///
|
|
/// # Example
|
|
///
|
|
/// ```
|
|
/// use vcore::word::Word;
|
|
///
|
|
/// assert_eq!(Word::from_bytes([0x01, 0x02, 0x03]), 0x030201);
|
|
/// ```
|
|
pub fn from_bytes(bytes: [u8; 3]) -> Self {
|
|
let [a, b, c] = bytes;
|
|
Self(u32::from_le_bytes([a, b, c, 0]))
|
|
}
|
|
|
|
/// Convert a `Word` into three bytes.
|
|
///
|
|
/// # Example
|
|
///
|
|
/// ```
|
|
/// use vcore::word::Word;
|
|
///
|
|
/// assert_eq!(Word::from(0x010203).to_bytes(), [0x03, 0x02, 0x01]);
|
|
/// ```
|
|
pub fn to_bytes(self) -> [u8; 3] {
|
|
let [a, b, c, _] = self.0.to_le_bytes();
|
|
[a, b, c]
|
|
}
|
|
}
|
|
|
|
impl From<u32> for Word {
|
|
fn from(a: u32) -> Self {
|
|
Self(a & 0xffffff)
|
|
}
|
|
}
|
|
impl From<Word> for u32 {
|
|
fn from(word: Word) -> Self {
|
|
word.0
|
|
}
|
|
}
|
|
|
|
impl Display for Word {
|
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
|
write!(f, "{}", i32::from(*self))
|
|
}
|
|
}
|
|
#[test]
|
|
fn to_from_u32() {
|
|
assert_eq!(u32::from(Word::from(0x123456u32)), 0x123456u32);
|
|
assert_eq!(u32::from(Word::from(0x12345678u32)), 0x345678u32);
|
|
}
|
|
|
|
impl From<Word> for i32 {
|
|
fn from(word: Word) -> Self {
|
|
if word.0 & 0x800000 != 0 {
|
|
-(((word.0 ^ 0xffffff) + 1) as i32)
|
|
} else {
|
|
word.0 as i32
|
|
}
|
|
}
|
|
}
|
|
impl From<i32> for Word {
|
|
fn from(value: i32) -> Self {
|
|
(value as u32).into()
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn to_from_i32() {
|
|
assert_eq!(i32::from(Word::from(0x123456i32)), 0x123456i32);
|
|
assert_eq!(i32::from(Word::from(-555i32)), -555i32);
|
|
}
|
|
|
|
// Perform various conversions using the conversions above
|
|
macro_rules! convert_via {
|
|
($big:ty => $little:ty) => {
|
|
impl From<$big> for Word {
|
|
fn from(value: $big) -> Self {
|
|
(value as $little).into()
|
|
}
|
|
}
|
|
impl From<Word> for $big {
|
|
fn from(value: Word) -> Self {
|
|
<$little>::from(value) as $big
|
|
}
|
|
}
|
|
};
|
|
}
|
|
convert_via!(u8 => u32);
|
|
convert_via!(i8 => i32);
|
|
|
|
#[test]
|
|
fn to_from_u8() {
|
|
assert_eq!(u8::from(Word::from(0x7fu8)), 0x7fu8);
|
|
assert_eq!(u8::from(Word::from_bytes([1, 2, 3])), 1u8);
|
|
}
|
|
|
|
#[test]
|
|
fn to_from_i8() {
|
|
assert_eq!(i8::from(Word::from(-1i8)), -1i8);
|
|
assert_eq!(i8::from(Word::from_bytes([1, 2, 3])), 1i8);
|
|
assert_eq!(i8::from(Word::from_bytes([0xff, 0, 0])), -1i8);
|
|
}
|
|
|
|
convert_via!(u16 => u32);
|
|
convert_via!(i16 => i32);
|
|
|
|
convert_via!(u64 => u32);
|
|
convert_via!(i64 => i32);
|
|
|
|
convert_via!(usize => u32);
|
|
convert_via!(isize => i32);
|
|
|
|
impl From<bool> for Word {
|
|
fn from(value: bool) -> Self {
|
|
if value {
|
|
Word::from(1)
|
|
} else {
|
|
Word::from(0)
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<Word> for bool {
|
|
fn from(word: Word) -> Self {
|
|
word.0 != 0
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn to_from_bool() {
|
|
assert!(!bool::from(Word::from(0)));
|
|
assert!(bool::from(Word::from(1)));
|
|
assert!(bool::from(Word::from(0x123456)));
|
|
|
|
assert_eq!(Word::from(false), Word::from(0));
|
|
assert_eq!(Word::from(true), Word::from(1));
|
|
}
|
|
|
|
impl From<[u8; 3]> for Word {
|
|
fn from(value: [u8; 3]) -> Self {
|
|
Word::from_bytes(value)
|
|
}
|
|
}
|
|
impl From<Word> for [u8; 3] {
|
|
fn from(value: Word) -> Self {
|
|
value.to_bytes()
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn to_from_u8_3() {
|
|
assert_eq!(Word::from([0, 0, 0]), Word::from(0));
|
|
assert_eq!(Word::from([1, 0, 0]), Word::from(1));
|
|
assert_eq!(Word::from([0xff, 0xff, 0xff]), Word::from(0xffffff));
|
|
|
|
assert_eq!(<[u8; 3]>::from(Word::from(0)), [0, 0, 0]);
|
|
assert_eq!(<[u8; 3]>::from(Word::from(1)), [1, 0, 0]);
|
|
assert_eq!(<[u8; 3]>::from(Word::from(0xffffff)), [0xff, 0xff, 0xff]);
|
|
}
|
|
|
|
// Implement negation via i32
|
|
impl std::ops::Neg for Word {
|
|
type Output = Word;
|
|
|
|
fn neg(self) -> Self::Output {
|
|
Self::from(-i32::from(self))
|
|
}
|
|
}
|
|
|
|
macro_rules! ops {
|
|
// Implement operations for $target by converting both Word and $target to $target
|
|
($target:ty) => {
|
|
ops!($target, $target);
|
|
};
|
|
|
|
// Implement operations for $target by converting both Word and $target to $intermediate
|
|
($target:ty, $intermediate:ty) => {
|
|
impl std::ops::Add<$target> for Word {
|
|
type Output = Word;
|
|
fn add(self, rhs: $target) -> Self::Output {
|
|
<$intermediate>::from(self)
|
|
.overflowing_add(<$intermediate>::from(rhs))
|
|
.0
|
|
.into()
|
|
}
|
|
}
|
|
|
|
impl std::ops::Sub<$target> for Word {
|
|
type Output = Word;
|
|
fn sub(self, rhs: $target) -> Self::Output {
|
|
<$intermediate>::from(self)
|
|
.overflowing_sub(<$intermediate>::from(rhs))
|
|
.0
|
|
.into()
|
|
}
|
|
}
|
|
|
|
impl std::ops::Mul<$target> for Word {
|
|
type Output = Word;
|
|
fn mul(self, rhs: $target) -> Self::Output {
|
|
<$intermediate>::from(self)
|
|
.overflowing_mul(<$intermediate>::from(rhs))
|
|
.0
|
|
.into()
|
|
}
|
|
}
|
|
|
|
impl std::ops::Div<$target> for Word {
|
|
type Output = Word;
|
|
fn div(self, rhs: $target) -> Self::Output {
|
|
<$intermediate>::from(self)
|
|
.overflowing_div(<$intermediate>::from(rhs))
|
|
.0
|
|
.into()
|
|
}
|
|
}
|
|
|
|
impl std::ops::Rem<$target> for Word {
|
|
type Output = Word;
|
|
fn rem(self, rhs: $target) -> Self::Output {
|
|
<$intermediate>::from(self)
|
|
.rem(<$intermediate>::from(rhs))
|
|
.into()
|
|
}
|
|
}
|
|
|
|
impl std::ops::BitAnd<$target> for Word {
|
|
type Output = Word;
|
|
fn bitand(self, rhs: $target) -> Self::Output {
|
|
<$intermediate>::from(self)
|
|
.bitand(<$intermediate>::from(rhs))
|
|
.into()
|
|
}
|
|
}
|
|
|
|
impl std::ops::BitOr<$target> for Word {
|
|
type Output = Word;
|
|
fn bitor(self, rhs: $target) -> Self::Output {
|
|
<$intermediate>::from(self)
|
|
.bitor(<$intermediate>::from(rhs))
|
|
.into()
|
|
}
|
|
}
|
|
|
|
impl std::ops::BitXor<$target> for Word {
|
|
type Output = Word;
|
|
fn bitxor(self, rhs: $target) -> Self::Output {
|
|
<$intermediate>::from(self)
|
|
.bitxor(<$intermediate>::from(rhs))
|
|
.into()
|
|
}
|
|
}
|
|
|
|
impl std::ops::Shl<$target> for Word {
|
|
type Output = Word;
|
|
fn shl(self, rhs: $target) -> Self::Output {
|
|
<$intermediate>::from(self)
|
|
.shl(<$intermediate>::from(rhs))
|
|
.into()
|
|
}
|
|
}
|
|
|
|
impl std::ops::Shr<$target> for Word {
|
|
type Output = Word;
|
|
fn shr(self, rhs: $target) -> Self::Output {
|
|
<$intermediate>::from(self)
|
|
.shr(<$intermediate>::from(rhs))
|
|
.into()
|
|
}
|
|
}
|
|
|
|
impl std::ops::SubAssign<$target> for Word {
|
|
fn sub_assign(&mut self, rhs: $target) {
|
|
*self = *self - rhs;
|
|
}
|
|
}
|
|
|
|
impl std::ops::AddAssign<$target> for Word {
|
|
fn add_assign(&mut self, rhs: $target) {
|
|
*self = *self + rhs;
|
|
}
|
|
}
|
|
|
|
impl std::cmp::PartialOrd<$target> for Word {
|
|
fn partial_cmp(&self, rhs: &$target) -> Option<std::cmp::Ordering> {
|
|
<$intermediate>::from(*self).partial_cmp(&<$intermediate>::from(*rhs))
|
|
}
|
|
}
|
|
impl std::cmp::PartialEq<$target> for Word {
|
|
fn eq(&self, rhs: &$target) -> bool {
|
|
<$intermediate>::from(*self).eq(&<$intermediate>::from(*rhs))
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
ops!(Word, u32);
|
|
|
|
ops!(u8);
|
|
ops!(u16);
|
|
ops!(u32);
|
|
ops!(u64);
|
|
ops!(usize);
|
|
|
|
ops!(i8);
|
|
ops!(i16);
|
|
ops!(i32);
|
|
ops!(i64);
|
|
ops!(isize);
|
|
|
|
#[test]
|
|
fn test_address_truncation() {
|
|
let a: Word = 0x11223344.into();
|
|
assert_eq!(a, 0x00223344);
|
|
}
|
|
|
|
#[test]
|
|
fn test_address_overflows() {
|
|
let a = Word::from(0xfffffa);
|
|
assert_eq!(a + 10, Word(4));
|
|
|
|
let b = Word::from(3);
|
|
assert_eq!(b - 10, Word(0xfffff9));
|
|
|
|
let mut c = Word::from(5);
|
|
c += 3;
|
|
assert_eq!(c, Word(8));
|
|
|
|
let mut d = Word::from(5);
|
|
d -= 3;
|
|
assert_eq!(d, Word(2));
|
|
}
|