fix: correctly sign-extend instead of plain byte to word cast

This commit is contained in:
2025-06-10 14:55:52 +09:00
parent 5529fc0b89
commit 3756ada3e0

View File

@@ -27,6 +27,41 @@ pub enum ImmediateOperand {
} }
impl 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. // Check if value is zero.
pub fn zero(&self) -> bool { pub fn zero(&self) -> bool {
match self { match self {
@@ -47,7 +82,7 @@ impl ImmediateOperand {
} }
/// Check if least significant bit is set. /// Check if least significant bit is set.
pub fn lsb(&self) -> bool { pub fn _lsb(&self) -> bool {
match self { match self {
Self::Byte(byte) => return byte & 1 == 1, Self::Byte(byte) => return byte & 1 == 1,
Self::Word(word) => return word & 1 == 1, Self::Word(word) => return word & 1 == 1,
@@ -57,8 +92,8 @@ impl ImmediateOperand {
/// Check if most significant bit is set. /// Check if most significant bit is set.
pub fn msb(&self) -> bool { pub fn msb(&self) -> bool {
match self { match self {
Self::Byte(byte) => return byte & (1 << 3) == 1, Self::Byte(byte) => return (byte >> 7) == 1,
Self::Word(word) => return word & (1 << 7) == 1, Self::Word(word) => return (word >> 15) == 1,
} }
} }
} }
@@ -74,8 +109,11 @@ impl Add for ImmediateOperand {
}, },
ImmediateOperand::Word(lhsw) => match other { ImmediateOperand::Word(lhsw) => match other {
ImmediateOperand::Word(rhsw) => ImmediateOperand::Word(lhsw.wrapping_add(rhsw)), ImmediateOperand::Word(rhsw) => ImmediateOperand::Word(lhsw.wrapping_add(rhsw)),
ImmediateOperand::Byte(rhsb) => { ImmediateOperand::Byte(_) => {
ImmediateOperand::Word(lhsw.wrapping_add(rhsb as Word)) 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(lhsw) => match other {
ImmediateOperand::Word(rhsw) => ImmediateOperand::Word(lhsw.wrapping_sub(rhsw)), ImmediateOperand::Word(rhsw) => ImmediateOperand::Word(lhsw.wrapping_sub(rhsw)),
ImmediateOperand::Byte(rhsb) => { ImmediateOperand::Byte(_) => {
ImmediateOperand::Word(lhsw.wrapping_sub(rhsb as Word)) 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(lhsw) => match other {
ImmediateOperand::Word(rhsw) => ImmediateOperand::Word(lhsw.wrapping_mul(rhsw)), ImmediateOperand::Word(rhsw) => ImmediateOperand::Word(lhsw.wrapping_mul(rhsw)),
ImmediateOperand::Byte(rhsb) => { ImmediateOperand::Byte(_) => {
ImmediateOperand::Word(lhsw.wrapping_mul(rhsb as Word)) 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(lhsw) => match other {
ImmediateOperand::Word(rhsw) => ImmediateOperand::Word(lhsw.wrapping_div(rhsw)), ImmediateOperand::Word(rhsw) => ImmediateOperand::Word(lhsw.wrapping_div(rhsw)),
ImmediateOperand::Byte(rhsb) => { ImmediateOperand::Byte(_) => {
ImmediateOperand::Word(lhsw.wrapping_div(rhsb as Word)) 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) 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);
}
}