From 1540cc2e9d503fe69c5bd98a282cae91f6053735 Mon Sep 17 00:00:00 2001 From: Will Glynn Date: Tue, 22 Feb 2022 18:27:48 -0600 Subject: [PATCH] In the beginning was Word --- src/bus.rs | 9 +- src/cpu.rs | 108 +++++++---------- src/display.rs | 55 +++++---- src/memory.rs | 85 +++++++------ src/word.rs | 319 ++++++++++++++++++++++++++++++++++++++++++++----- 5 files changed, 415 insertions(+), 161 deletions(-) diff --git a/src/bus.rs b/src/bus.rs index b288475..7341151 100644 --- a/src/bus.rs +++ b/src/bus.rs @@ -61,6 +61,7 @@ impl Device for Bus { #[cfg(test)] mod tests { use super::*; + use crate::memory::PeekPokeExt; struct TestDevice(i32); impl Device for TestDevice { @@ -111,8 +112,8 @@ mod tests { #[test] fn test_poke_peek() { let mut bus = Bus::new(5, 10, ArrayDevice([0u8; 10]), ArrayDevice([0u8; 10])); - bus.poke_u32(2, 2); // Goes into the 2nd device - bus.poke_u32(6, 6); // Goes into the first device... + bus.poke8(2, 2); // Goes into the 2nd device + bus.poke8(6, 6); // Goes into the first device... assert_eq!(bus.device.0[1], 6); // At index 1 assert_eq!(bus.rest.0[2], 2); // Second device gets the other write @@ -120,7 +121,7 @@ mod tests { assert_eq!(bus.device.0[2], 0); assert_eq!(bus.rest.0[1], 0); - assert_eq!(bus.peek_u32(2), 2); // Reading from the first device - assert_eq!(bus.peek_u32(6), 6); // And the second + assert_eq!(bus.peek8(2), 2); // Reading from the first device + assert_eq!(bus.peek8(6), 6); // And the second } } diff --git a/src/cpu.rs b/src/cpu.rs index 376aed3..12160ba 100644 --- a/src/cpu.rs +++ b/src/cpu.rs @@ -1,5 +1,5 @@ -use crate::memory::Memory; use crate::memory::PeekPoke; +use crate::memory::{Memory, PeekPokeExt}; use crate::opcodes::InvalidOpcode; use crate::opcodes::Opcode; use crate::word::Word; @@ -19,7 +19,7 @@ pub struct CPU { #[derive(Copy, Clone, Eq, PartialEq, Debug)] struct Instruction { opcode: Opcode, - arg: Option, + arg: Option, length: u8, } @@ -54,32 +54,32 @@ impl CPU { self.halted = true; } - fn push_data>(&mut self, word: A) { - self.memory.poke24(self.dp, word.into()); + fn push_data>(&mut self, word: A) { + self.memory.poke24(self.dp, word); self.dp += 3; } - fn push_call>(&mut self, word: A) { + fn push_call>(&mut self, word: A) { self.sp -= 3; - self.memory.poke24(self.sp, word.into()); + self.memory.poke24(self.sp, word); } - fn pop_data(&mut self) -> u32 { + fn pop_data(&mut self) -> Word { self.dp -= 3; self.memory.peek24(self.dp) } - fn pop_call(&mut self) -> u32 { + fn pop_call(&mut self) -> Word { let val = self.memory.peek24(self.sp); self.sp += 3; val } - fn peek_call(&self) -> u32 { + fn peek_call(&self) -> Word { self.memory.peek24(self.sp) } - fn peek_data(&self) -> u32 { + fn peek_data(&self) -> Word { self.memory.peek24(self.dp - 3) } @@ -103,7 +103,7 @@ impl CPU { } Ok(Instruction { opcode, - arg: Some(arg), + arg: Some(Word::from(arg)), length: arg_length + 1, }) } @@ -130,16 +130,16 @@ impl CPU { Opcode::And => self.push_data(y & x), Opcode::Or => self.push_data(y | x), Opcode::Xor => self.push_data(y ^ x), - Opcode::Gt => self.push_data(bool_as_word(y > x)), - Opcode::Lt => self.push_data(bool_as_word(y < x)), - Opcode::Agt => self.push_data(bool_as_word(word_as_signed(y) > word_as_signed(x))), - Opcode::Alt => self.push_data(bool_as_word(word_as_signed(y) < word_as_signed(x))), + Opcode::Gt => self.push_data(y > x), + Opcode::Lt => self.push_data(y < x), + Opcode::Agt => self.push_data(i32::from(y) > i32::from(x)), + Opcode::Alt => self.push_data(i32::from(y) < i32::from(x)), Opcode::Lshift => self.push_data(y << x), Opcode::Rshift => self.push_data(y >> x), Opcode::Arshift => { if y & 0x800000 != 0 { let mut shifted = y; - for _ in 0..x { + for _ in 0..u32::from(x).clamp(0, 24) { shifted = shifted >> 1 | 0x800000; } self.push_data(shifted) @@ -151,23 +151,23 @@ impl CPU { self.push_data(x); self.push_data(y) } - Opcode::Store => self.memory.poke(x.into(), y as u8), - Opcode::Storew => self.memory.poke24(x.into(), y), + Opcode::Store => self.memory.poke8(x, y.to_bytes()[0]), + Opcode::Storew => self.memory.poke24(x, y), Opcode::Setsdp => { self.dp = x.into(); self.sp = y.into() } Opcode::Brz => { if y == 0 { - return self.pc + word_as_signed(x); + return self.pc + i32::from(x); } } Opcode::Brnz => { if y != 0 { - return self.pc + word_as_signed(x); + return self.pc + i32::from(x); } } - _ => {} // This can never happen + _ => unreachable!(), } self.pc + instruction.length as i32 } else { @@ -176,7 +176,7 @@ impl CPU { Opcode::Rand => {} // TODO remove this whole instruction Opcode::Not => { let x = self.pop_data(); - self.push_data(bool_as_word(x == 0)) + self.push_data(x == 0) } Opcode::Pop => { self.pop_data(); @@ -184,7 +184,7 @@ impl CPU { Opcode::Dup => self.push_data(self.peek_data()), Opcode::Pick => { let index = self.pop_data(); - let val = self.memory.peek24(self.dp - (index as i32 + 1) * 3); + let val = self.memory.peek24(self.dp - (i32::from(index) + 1) * 3); self.push_data(val) } Opcode::Rot => { @@ -197,7 +197,7 @@ impl CPU { } Opcode::Jmp => return self.pop_data().into(), Opcode::Jmpr => { - let x = word_as_signed(self.pop_data()); + let x = i32::from(self.pop_data()); return self.pc + x; } Opcode::Call => { @@ -209,11 +209,11 @@ impl CPU { Opcode::Hlt => self.halted = true, Opcode::Load => { let x = self.pop_data(); - self.push_data(self.memory.peek(x.into()) as u32) + self.push_data(self.memory.peek8(x)) } Opcode::Loadw => { let x = self.pop_data(); - self.push_data(self.memory.peek24(x.into())) + self.push_data(self.memory.peek24(x)) } Opcode::Inton => self.int_enabled = true, Opcode::Intoff => self.int_enabled = false, @@ -270,29 +270,13 @@ impl Opcode { } } -fn word_as_signed(word: u32) -> i32 { - if word & 0x800000 != 0 { - -(((word ^ 0xffffff) + 1) as i32) - } else { - word as i32 - } -} - -fn bool_as_word(flag: bool) -> u32 { - if flag { - 1 - } else { - 0 - } -} - #[cfg(test)] mod tests { use super::*; use Opcode::*; impl CPU { - fn get_stack(&self) -> Vec { + fn get_stack(&self) -> Vec { let mut v = Vec::new(); let mut curr = Word::from(256); while curr < self.dp { @@ -302,7 +286,7 @@ mod tests { v } - fn get_call(&self) -> Vec { + fn get_call(&self) -> Vec { let mut v = Vec::new(); let mut curr = Word::from(1024); while curr > self.sp { @@ -520,8 +504,8 @@ mod tests { cpu.push_data(2000u32) }, |cpu| { - assert_eq!(cpu.sp, 1000.into()); - assert_eq!(cpu.dp, 2000.into()) + assert_eq!(cpu.sp, 1000); + assert_eq!(cpu.dp, 2000) }, ); call_stack_opcode_test(vec![123], vec![], Pushr, vec![], vec![123], 1025.into()); @@ -532,7 +516,7 @@ mod tests { #[test] fn test_cpu_new() { let cpu = CPU::new(Memory::default()); - assert_eq!(cpu.pc, 1024.into()); + assert_eq!(cpu.pc, 1024); assert_eq!(cpu.halted, true); } @@ -541,7 +525,7 @@ mod tests { let mut cpu = CPU::new(Memory::default()); cpu.iv = 12345.into(); cpu.reset(); - assert_eq!(cpu.iv, 1024.into()); + assert_eq!(cpu.iv, 1024); } #[test] @@ -549,40 +533,40 @@ mod tests { let mut cpu = CPU::new(Memory::default()); cpu.push_data(37u32); cpu.push_data(45u32); - assert_eq!(cpu.memory.peek24_u32(256), 37); - assert_eq!(cpu.memory.peek24_u32(259), 45); + assert_eq!(cpu.memory.peek24(256), 37); + assert_eq!(cpu.memory.peek24(259), 45); cpu.push_call(12u32); cpu.push_call(34u32); assert_eq!(cpu.memory.peek24(cpu.sp), 34); assert_eq!(cpu.memory.peek24(cpu.sp + 3), 12); - assert_eq!(cpu.sp, (1024 - 6).into()); - assert_eq!(cpu.dp, (256 + 6).into()); + assert_eq!(cpu.sp, 1024 - 6); + assert_eq!(cpu.dp, 256 + 6); assert_eq!(cpu.pop_data(), 45); assert_eq!(cpu.pop_data(), 37); - assert_eq!(cpu.dp, 256.into()); + assert_eq!(cpu.dp, 256); assert_eq!(cpu.pop_call(), 34); assert_eq!(cpu.pop_call(), 12); - assert_eq!(cpu.sp, 1024.into()); + assert_eq!(cpu.sp, 1024); } #[test] fn test_cpu_fetch() { let mut cpu = CPU::new(Memory::default()); - cpu.memory.poke_u32(0x400, 0x01); // nop 1 arg - cpu.memory.poke_u32(0x401, 0x02); // 2 - cpu.memory.poke_u32(0x402, 0x07); // add 3 arg - cpu.memory.poke24_u32(0x403, 0x123456); // 3-byte arg - cpu.memory.poke_u32(0x406, 29 << 2); // hlt - cpu.memory.poke_u32(0x407, 0xfc); // gibberish + cpu.memory.poke8(0x400, 0x01); // nop 1 arg + cpu.memory.poke8(0x401, 0x02); // 2 + cpu.memory.poke8(0x402, 0x07); // add 3 arg + cpu.memory.poke24(0x403, 0x123456); // 3-byte arg + cpu.memory.poke8(0x406, 29 << 2); // hlt + cpu.memory.poke8(0x407, 0xfc); // gibberish assert_eq!( cpu.fetch(), Ok(Instruction { opcode: Opcode::Nop, - arg: Some(2), + arg: Some(Word::from(2)), length: 2 }) ); @@ -592,7 +576,7 @@ mod tests { cpu.fetch(), Ok(Instruction { opcode: Opcode::Add, - arg: Some(0x123456), + arg: Some(Word::from(0x123456)), length: 4 }) ); diff --git a/src/display.rs b/src/display.rs index 72876cf..0fedf81 100644 --- a/src/display.rs +++ b/src/display.rs @@ -1,37 +1,36 @@ -use crate::memory::PeekPoke; +use crate::memory::{PeekPoke, PeekPokeExt}; use crate::Word; -use std::convert::TryFrom; #[derive(Copy, Clone, Debug, Eq, PartialEq)] struct DisplayRegisters { mode: u8, - screen: u32, - palette: u32, - font: u32, - height: u32, - width: u32, - row_offset: u32, - col_offset: u32, + screen: Word, + palette: Word, + font: Word, + height: Word, + width: Word, + row_offset: Word, + col_offset: Word, } impl Default for DisplayRegisters { fn default() -> Self { Self { - mode: 4, - screen: 0x10000, - palette: 0x20000 - 0x100, - font: 0x20000 - 0x100 - 0x2000, - height: 128, - width: 128, - row_offset: 0, - col_offset: 0, + mode: 5, + screen: Word::from(0x10000), + palette: Word::from(0x20000 - 0x100), + font: Word::from(0x20000 - 0x100 - 0x2000), + height: Word::from(128), + width: Word::from(128), + row_offset: Word::from(0), + col_offset: Word::from(0), } } } fn read_display_registers(machine: &P, start: Word) -> DisplayRegisters { DisplayRegisters { - mode: machine.peek(start), + mode: machine.peek8(start), screen: machine.peek24(start + 1), palette: machine.peek24(start + 4), font: machine.peek24(start + 7), @@ -45,13 +44,13 @@ fn read_display_registers(machine: &P, start: Word) -> DisplayRegis fn init_display_registers(machine: &mut P, start: Word) { let dr = DisplayRegisters::default(); machine.poke(start, dr.mode); - machine.poke24(start + 1, dr.screen.into()); - machine.poke24(start + 4, dr.palette.into()); - machine.poke24(start + 7, dr.font.into()); - machine.poke24(start + 10, dr.height.into()); - machine.poke24(start + 13, dr.width.into()); - machine.poke24(start + 16, dr.row_offset.into()); - machine.poke24(start + 19, dr.col_offset.into()); + machine.poke24(start + 1, dr.screen); + machine.poke24(start + 4, dr.palette); + machine.poke24(start + 7, dr.font); + machine.poke24(start + 10, dr.height); + machine.poke24(start + 13, dr.width); + machine.poke24(start + 16, dr.row_offset); + machine.poke24(start + 19, dr.col_offset); } fn init_font(machine: &mut P) { @@ -96,9 +95,9 @@ fn init_palette(machine: &mut P) { } } -fn to_byte_address((x, y): (u32, u32), reg: DisplayRegisters) -> u32 { - let row_start = (y + reg.row_offset % reg.height) * reg.width + reg.screen; - ((x + reg.col_offset) % reg.width) + row_start +fn to_byte_address((x, y): (u32, u32), reg: DisplayRegisters) -> Word { + let row_start = (Word::from(y) + reg.row_offset % reg.height) * reg.width + reg.screen; + ((Word::from(x) + reg.col_offset) % reg.width) + row_start } fn draw_direct_high_gfx(machine: &P, reg: DisplayRegisters, frame: &mut [u8]) { diff --git a/src/memory.rs b/src/memory.rs index 5a7872b..5dbff8e 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -10,13 +10,6 @@ impl Default for Memory { } } -impl From for usize { - fn from(w: Word) -> Self { - let w: u32 = w.into(); - (w & (MEM_SIZE - 1)) as usize - } -} - impl From for Memory { fn from(mut rng: R) -> Self { let mut mem = Memory::default(); @@ -41,32 +34,54 @@ impl std::ops::IndexMut for Memory { } pub trait PeekPoke { + /// Read a byte from a given address. fn peek(&self, addr: Word) -> u8; + + /// Write a byte to a given address. fn poke(&mut self, addr: Word, val: u8); - fn peek24(&self, addr: Word) -> u32 { - (self.peek(addr) as u32) - | ((self.peek(addr + 1) as u32) << 8) - | ((self.peek(addr + 2) as u32) << 16) + /// Read a slice, starting from a given address. + fn peek_slice(&self, addr: Word, buffer: &mut [u8]) { + for (i, byte) in buffer.iter_mut().enumerate() { + *byte = self.peek(addr + i); + } } - fn poke24(&mut self, addr: Word, val: u32) { - self.poke(addr, val as u8); - self.poke(addr + 1, (val >> 8) as u8); - self.poke(addr + 2, (val >> 16) as u8); + /// Write a slice, starting at a given address. + fn poke_slice(&mut self, addr: Word, buffer: &[u8]) { + for (i, byte) in buffer.iter().enumerate() { + self.poke(addr + i, *byte); + } } +} - fn peek_u32(&self, addr: u32) -> u8 { +pub trait PeekPokeExt { + fn peek8>(&self, addr: A) -> u8; + fn poke8, V: Into>(&mut self, addr: A, val: V); + + fn peek24>(&self, addr: A) -> Word; + fn poke24, V: Into>(&mut self, addr: A, val: V); +} + +impl PeekPokeExt for T { + fn peek8>(&self, addr: A) -> u8 { self.peek(addr.into()) } - fn poke_u32(&mut self, addr: u32, val: u8) { - self.poke(addr.into(), val) + + fn poke8, V: Into>(&mut self, addr: A, val: V) { + self.poke(addr.into(), val.into()); } - fn peek24_u32(&mut self, addr: u32) -> u32 { - self.peek24(addr.into()) + + fn peek24>(&self, addr: A) -> Word { + let mut bytes = [0u8; 3]; + self.peek_slice(addr.into(), &mut bytes); + bytes.into() } - fn poke24_u32(&mut self, addr: u32, val: u32) { - self.poke24(addr.into(), val) + + fn poke24, V: Into>(&mut self, addr: A, val: V) { + let addr = addr.into(); + let val = val.into(); + self.poke_slice(addr, &val.to_bytes()); } } @@ -86,26 +101,20 @@ mod tests { #[test] fn test_mem_peek_poke() { let mut mem = Memory::default(); - assert_eq!(mem.peek_u32(35), 0); - mem.poke_u32(35, 45); - assert_eq!(mem.peek_u32(35), 45); - assert_eq!(mem.peek_u32(36), 0); + assert_eq!(mem.peek24(35), 0); + mem.poke24(35, 45); + assert_eq!(mem.peek24(35), 45); + assert_eq!(mem.peek24(36), 0); } #[test] fn test_mem_word_fns() { let mut mem = Memory::default(); - mem.poke24(10.into(), 0x123456); - assert_eq!(mem.peek_u32(10), 0x56); - assert_eq!(mem.peek_u32(11), 0x34); - assert_eq!(mem.peek_u32(12), 0x12); - assert_eq!(mem.peek24(10.into()), 0x123456); - assert_eq!(mem.peek24(11.into()), 0x001234); - } - - #[test] - fn test_addressing_arrays() { - let a: usize = Word::from(0xffffff).into(); - assert_eq!(a, 0x01ffff as usize); + mem.poke24(10, 0x123456); + assert_eq!(mem.peek8(10), 0x56); + assert_eq!(mem.peek8(11), 0x34); + assert_eq!(mem.peek8(12), 0x12); + assert_eq!(mem.peek24(10), 0x123456); + assert_eq!(mem.peek24(11), 0x001234); } } diff --git a/src/word.rs b/src/word.rs index cf70c05..d4cd2c1 100644 --- a/src/word.rs +++ b/src/word.rs @@ -1,58 +1,319 @@ // 128k, the amount of memory in a standard Vulcan machine pub const MEM_SIZE: u32 = 128 * 1024; -#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)] +#[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 for Word { fn from(a: u32) -> Self { Self(a & 0xffffff) } } - impl From for u32 { fn from(word: Word) -> Self { word.0 } } -impl std::ops::Add for Word { - type Output = Word; - fn add(self, rhs: i32) -> Self::Output { - Word::from((self.0 as i32).overflowing_add(rhs).0 as u32) - } +#[test] +fn to_from_u32() { + assert_eq!(u32::from(Word::from(0x123456u32)), 0x123456u32); + assert_eq!(u32::from(Word::from(0x12345678u32)), 0x345678u32); } -impl std::ops::Sub for Word { - type Output = Word; - fn sub(self, rhs: i32) -> Self::Output { - self + -rhs +impl From for i32 { + fn from(word: Word) -> Self { + if word.0 & 0x800000 != 0 { + -(((word.0 ^ 0xffffff) + 1) as i32) + } else { + word.0 as i32 + } } } - -impl std::ops::Sub for Word { - type Output = Word; - fn sub(self, rhs: Word) -> Self::Output { - Word(self.0 - rhs.0) - } -} - -impl std::ops::SubAssign for Word { - fn sub_assign(&mut self, rhs: i32) { - *self = *self - rhs; - } -} - -impl std::ops::AddAssign for Word { - fn add_assign(&mut self, rhs: i32) { - *self = *self + rhs; +impl From 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 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 for Word { + fn from(value: bool) -> Self { + if value { + Word::from(1) + } else { + Word::from(0) + } + } +} + +impl From 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 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 { + <$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.into()); + assert_eq!(a, 0x00223344); } #[test]