Rearranged into a cargo workspace
This commit is contained in:
@@ -0,0 +1,334 @@
|
||||
// 128k, the amount of memory in a standard Vulcan machine
|
||||
pub const MEM_SIZE: u32 = 128 * 1024;
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, Ord)]
|
||||
pub struct Word(u32);
|
||||
|
||||
impl Word {
|
||||
/// Create a `Word` from three bytes.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use vulcan_emu::Word;
|
||||
///
|
||||
/// assert_eq!(Word::from_bytes([0x01, 0x02, 0x03]) == 0x010203);
|
||||
/// ```
|
||||
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 vulcan_emu::Word;
|
||||
///
|
||||
/// assert_eq!(Word::from(0x010203).to_bytes(), [0x01, 0x02, 0x03]);
|
||||
/// ```
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
#[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_eq!(bool::from(Word::from(0)), false);
|
||||
assert_eq!(bool::from(Word::from(1)), true);
|
||||
assert_eq!(bool::from(Word::from(0x123456)), true);
|
||||
|
||||
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));
|
||||
}
|
||||
Reference in New Issue
Block a user