fix: correctly sign-extend instead of plain byte to word cast
This commit is contained in:
119
src/operands.rs
119
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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user