diff --git a/src/operands.rs b/src/operands.rs index 8756ae0..078a392 100644 --- a/src/operands.rs +++ b/src/operands.rs @@ -27,6 +27,41 @@ pub enum ImmediateOperand { } impl ImmediateOperand { + // Sign-extend [`Self::Byte`] into [`Self::Word`]. + // Returns [`Self::Word`], if already a word. + pub fn sign_extend(self) -> Self { + match self { + Self::Byte(_) => { + let sign = self.msb(); + let byte = self.flip_sign(); + let word = byte.as_word(); + if sign { + return word.flip_sign(); + } else { + return word; + } + } + Self::Word(_) => self, + } + } + + // Interprets [`Self::Byte`] as [`Self::Word`]. + // Returns word, if already a [`Self::Word`]. + pub fn as_word(self) -> Self { + match self { + Self::Byte(b) => Self::Word(b as Word), + Self::Word(_) => self, + } + } + + // Flip most significant bit. + pub fn flip_sign(self) -> Self { + match self { + Self::Byte(b) => Self::Byte(b ^ (1 << 7)), + Self::Word(w) => Self::Word(w ^ (1 << 15)), + } + } + // Check if value is zero. pub fn zero(&self) -> bool { match self { @@ -47,7 +82,7 @@ impl ImmediateOperand { } /// Check if least significant bit is set. - pub fn lsb(&self) -> bool { + pub fn _lsb(&self) -> bool { match self { Self::Byte(byte) => return byte & 1 == 1, Self::Word(word) => return word & 1 == 1, @@ -57,8 +92,8 @@ impl ImmediateOperand { /// Check if most significant bit is set. pub fn msb(&self) -> bool { match self { - Self::Byte(byte) => return byte & (1 << 3) == 1, - Self::Word(word) => return word & (1 << 7) == 1, + Self::Byte(byte) => return (byte >> 7) == 1, + Self::Word(word) => return (word >> 15) == 1, } } } @@ -74,8 +109,11 @@ impl Add for ImmediateOperand { }, ImmediateOperand::Word(lhsw) => match other { ImmediateOperand::Word(rhsw) => ImmediateOperand::Word(lhsw.wrapping_add(rhsw)), - ImmediateOperand::Byte(rhsb) => { - ImmediateOperand::Word(lhsw.wrapping_add(rhsb as Word)) + ImmediateOperand::Byte(_) => { + ImmediateOperand::Word(lhsw.wrapping_add(match other.sign_extend() { + ImmediateOperand::Word(w) => w, + _ => panic!("Sign-extended Word is a Byte"), + })) } }, } @@ -118,8 +156,11 @@ impl Sub for ImmediateOperand { }, ImmediateOperand::Word(lhsw) => match other { ImmediateOperand::Word(rhsw) => ImmediateOperand::Word(lhsw.wrapping_sub(rhsw)), - ImmediateOperand::Byte(rhsb) => { - ImmediateOperand::Word(lhsw.wrapping_sub(rhsb as Word)) + ImmediateOperand::Byte(_) => { + ImmediateOperand::Word(lhsw.wrapping_sub(match other.sign_extend() { + ImmediateOperand::Word(w) => w, + _ => panic!("Sign-extended Word is a Byte"), + })) } }, } @@ -137,8 +178,11 @@ impl Mul for ImmediateOperand { }, ImmediateOperand::Word(lhsw) => match other { ImmediateOperand::Word(rhsw) => ImmediateOperand::Word(lhsw.wrapping_mul(rhsw)), - ImmediateOperand::Byte(rhsb) => { - ImmediateOperand::Word(lhsw.wrapping_mul(rhsb as Word)) + ImmediateOperand::Byte(_) => { + ImmediateOperand::Word(lhsw.wrapping_mul(match other.sign_extend() { + ImmediateOperand::Word(w) => w, + _ => panic!("Sign-extended Word is a Byte"), + })) } }, } @@ -156,8 +200,11 @@ impl Div for ImmediateOperand { }, ImmediateOperand::Word(lhsw) => match other { ImmediateOperand::Word(rhsw) => ImmediateOperand::Word(lhsw.wrapping_div(rhsw)), - ImmediateOperand::Byte(rhsb) => { - ImmediateOperand::Word(lhsw.wrapping_div(rhsb as Word)) + ImmediateOperand::Byte(_) => { + ImmediateOperand::Word(lhsw.wrapping_div(match other.sign_extend() { + ImmediateOperand::Word(w) => w, + _ => panic!("Sign-extended Word is a Byte"), + })) } }, } @@ -339,3 +386,53 @@ impl std::fmt::Display for Pointer32 { write!(f, "{:#04x}:{:#04x}", self.segment, self.offset) } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_msb() { + let pos = ImmediateOperand::Byte(1 << 4); + let neg = ImmediateOperand::Byte(1 << 7); + assert_eq!(pos.msb(), false); + assert_eq!(neg.msb(), true); + } + + #[test] + fn test_as_word() { + let b: u8 = 5; + let byte = ImmediateOperand::Byte(b); + let word = ImmediateOperand::Word(b as Word); + assert_eq!(byte.as_word(), word); + assert_eq!(word, word); + } + + #[test] + fn test_flip_sign_neg_to_pos() { + let b = 0 << 2; + let byte = ImmediateOperand::Byte((1 << 7) | b); + let word = ImmediateOperand::Word((1 << 15) | b as Word); + assert_eq!(byte.flip_sign(), ImmediateOperand::Byte(b)); + assert_eq!(word.flip_sign(), ImmediateOperand::Word(b as Word)); + } + + #[test] + fn test_flip_sign_pos_to_neg() { + let b = 1 << 2; + let byte = ImmediateOperand::Byte(b); + let word = ImmediateOperand::Word(b as Word); + assert_eq!(byte.flip_sign(), ImmediateOperand::Byte((1 << 7) | b)); + assert_eq!( + word.flip_sign(), + ImmediateOperand::Word((1 << 15) | b as Word) + ); + } + + #[test] + fn test_sign_extend() { + let byte = ImmediateOperand::Byte(1 << 7); + let word = ImmediateOperand::Word(1 << 15); + assert_eq!(byte.sign_extend(), word); + } +}