emu: flags: make CF & OF always lazy. Remove Flags::update_of()

Rotate ops now backup flags manually into their enum data instead of
converting everything to eager mode
This commit is contained in:
2021-03-30 01:53:27 -07:00
parent 984ff891c0
commit a789b7e87d
3 changed files with 104 additions and 58 deletions

View File

@@ -12,7 +12,7 @@ const OF_BIT: u8 = 11; // Overflow Flag
#[derive(Clone, Copy, Default)] #[derive(Clone, Copy, Default)]
pub struct Flags { pub struct Flags {
pub cf: bool, // 0: Carry Flag: 1=CY(Carry), 0=NC(No Carry) // 0: Carry Flag: 1=CY(Carry), 0=NC(No Carry)
// 1: Reserved // 1: Reserved
// 2: LAZY Parity Flag: 1=PE(Even), 0=PO(Odd) // 2: LAZY Parity Flag: 1=PE(Even), 0=PO(Odd)
// 3: Reserved // 3: Reserved
@@ -33,9 +33,40 @@ pub struct Flags {
} }
impl Flags { impl Flags {
pub fn cf(&self) -> bool {
match self.flag_op {
FlagOp::Eager => { self.res & 1 << CF_BIT != 0 },
FlagOp::DEC { cf } => { cf },
FlagOp::INC { cf } => { cf },
FlagOp::ROL { dst, src, bits, .. } => { dst & (self.sign_mask >> ((src - 1) % bits as u16)) != 0 },
FlagOp::ROR { dst, src, bits, .. } => { dst & (1 << ((src - 1) % bits as u16)) != 0 },
FlagOp::SAR { dst, src, sign_bit } => {
match 1u16.checked_shl(src as u32 - 1) {
Some(carrymask) => dst & carrymask != 0,
None => sign_bit
}
},
FlagOp::SHL { dst, src } => {
match self.sign_mask.checked_shr(src as u32 - 1) {
Some(carrymask) => dst & carrymask != 0,
None => false
}
},
FlagOp::SHR { dst, src } => {
match 1u16.checked_shl(src as u32 - 1) {
Some(carrymask) => dst & carrymask != 0,
None => false
}
},
FlagOp::SUB { cf, .. } => { cf },
}
}
pub fn pf(&self) -> bool { pub fn pf(&self) -> bool {
match self.flag_op { match self.flag_op {
FlagOp::Eager => { self.res & 1 << PF_BIT != 0 }, FlagOp::Eager => { self.res & 1 << PF_BIT != 0 },
FlagOp::ROL { pf, .. }
| FlagOp::ROR { pf, .. } => { pf },
_ => { self.res.count_ones() & 1 == 0 }, _ => { self.res.count_ones() & 1 == 0 },
} }
} }
@@ -43,6 +74,8 @@ impl Flags {
pub fn af(&self) -> bool { pub fn af(&self) -> bool {
match self.flag_op { match self.flag_op {
FlagOp::Eager => { self.res & 1 << AF_BIT != 0 }, FlagOp::Eager => { self.res & 1 << AF_BIT != 0 },
FlagOp::ROL { af, .. }
| FlagOp::ROR { af, .. } => { af },
_ => { false /* XXX: unimplemented! */ }, _ => { false /* XXX: unimplemented! */ },
} }
} }
@@ -50,6 +83,8 @@ impl Flags {
pub fn zf(&self) -> bool { pub fn zf(&self) -> bool {
match self.flag_op { match self.flag_op {
FlagOp::Eager => { self.res & 1 << ZF_BIT != 0 }, FlagOp::Eager => { self.res & 1 << ZF_BIT != 0 },
FlagOp::ROL { zf, .. }
| FlagOp::ROR { zf, .. } => { zf },
_ => { self.res == 0 }, _ => { self.res == 0 },
} }
} }
@@ -57,6 +92,8 @@ impl Flags {
pub fn sf(&self) -> bool { pub fn sf(&self) -> bool {
match self.flag_op { match self.flag_op {
FlagOp::Eager => { self.res & 1 << SF_BIT != 0 }, FlagOp::Eager => { self.res & 1 << SF_BIT != 0 },
FlagOp::ROL { sf, .. }
| FlagOp::ROR { sf, .. } => { sf },
_ => { self.res & self.sign_mask != 0 }, _ => { self.res & self.sign_mask != 0 },
} }
} }
@@ -64,12 +101,16 @@ impl Flags {
pub fn of(&self) -> bool { pub fn of(&self) -> bool {
match self.flag_op { match self.flag_op {
FlagOp::Eager => { self.res & 1 << OF_BIT != 0 }, FlagOp::Eager => { self.res & 1 << OF_BIT != 0 },
FlagOp::DEC => { self.res == self.sign_mask - 1 }, FlagOp::DEC { .. } => { self.res == self.sign_mask - 1 },
FlagOp::INC => { self.res == self.sign_mask }, FlagOp::INC { .. } => { self.res == self.sign_mask },
FlagOp::SHIFT { dst } => { 0 != self.sign_mask & (dst ^ self.res) }, FlagOp::ROL { dst, .. }
FlagOp::SUB { dst, src } => { 0 != self.sign_mask & // In the (maybe) sign bit... | FlagOp::ROR { dst, .. } => { self.sign_mask & (dst ^ self.res) != 0 },
(src ^ dst) & // ...operands have different signs... FlagOp::SAR { dst, .. }
(dst ^ self.res) }, /* ...and result sign-bit changed */ | FlagOp::SHL { dst, .. }
| FlagOp::SHR { dst, .. } => { 0 != self.sign_mask & (dst ^ self.res) },
FlagOp::SUB { dst, src, .. } => { 0 != self.sign_mask & // In the (maybe) sign bit...
(src ^ dst) & // ...operands have different signs...
(dst ^ self.res) }, /* ...and result sign-bit changed */
} }
} }
@@ -78,26 +119,19 @@ impl Flags {
self.res = res; self.res = res;
self.sign_mask = sign_mask; self.sign_mask = sign_mask;
} }
// The rotate ops update OF don't touch PF/AF/ZF/SF so we make
// everything eager so they don't get clobbered
pub fn update_of(&mut self, of: bool) {
let mut flags = (*self).into();
flags &= !(1 << OF_BIT); // Mask out old OF Flag
if of { flags |= 1 << OF_BIT; } // Mask in new OF Flag
self.flag_op = FlagOp::Eager;
self.res = flags;
}
} }
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub enum FlagOp { pub enum FlagOp {
Eager, // precomputed into result, for POPF or anything that changes only some flags Eager, // precomputed into result, for e.g. POPF
DEC, DEC { cf: bool },
INC, INC { cf: bool },
SHIFT { dst: u16 }, ROL { dst: u16, src: u16, bits: u8, cf: bool, pf: bool, af: bool, zf: bool, sf: bool },
SUB { dst: u16, src: u16 }, ROR { dst: u16, src: u16, bits: u8, cf: bool, pf: bool, af: bool, zf: bool, sf: bool },
SAR { dst: u16, src: u16, sign_bit: bool },
SHL { dst: u16, src: u16 },
SHR { dst: u16, src: u16 },
SUB { dst: u16, src: u16, cf: bool },
} }
impl Default for FlagOp { impl Default for FlagOp {
@@ -107,7 +141,6 @@ impl Default for FlagOp {
impl From<u16> for Flags { impl From<u16> for Flags {
fn from(flags: u16) -> Self { fn from(flags: u16) -> Self {
Self { Self {
cf: flags & 1 << CF_BIT != 0,
tf: flags & 1 << TF_BIT != 0, tf: flags & 1 << TF_BIT != 0,
ie: flags & 1 << IF_BIT != 0, ie: flags & 1 << IF_BIT != 0,
df: flags & 1 << DF_BIT != 0, df: flags & 1 << DF_BIT != 0,
@@ -122,7 +155,7 @@ impl From<u16> for Flags {
impl From<Flags> for u16 { impl From<Flags> for u16 {
fn from(flags: Flags) -> Self { fn from(flags: Flags) -> Self {
0b1111_0000_0010_1010 // Not sure what all reserved bits should be, but it shouldn't matter 0b1111_0000_0010_1010 // Not sure what all reserved bits should be, but it shouldn't matter
| (flags.cf as u16) | (flags.cf() as u16)
| (flags.pf() as u16) << PF_BIT | (flags.pf() as u16) << PF_BIT
| (flags.af() as u16) << AF_BIT | (flags.af() as u16) << AF_BIT
| (flags.zf() as u16) << ZF_BIT | (flags.zf() as u16) << ZF_BIT
@@ -138,7 +171,7 @@ impl Debug for Flags {
fn fmt(&self, fmt: &mut Formatter) -> Result<(), std::fmt::Error> { fn fmt(&self, fmt: &mut Formatter) -> Result<(), std::fmt::Error> {
use std::fmt::Write; use std::fmt::Write;
fmt.write_str("[ ")?; fmt.write_str("[ ")?;
for flag in [ (self.cf, "CF "), for flag in [ (self.cf(), "CF "),
(self.pf(), "PF "), (self.pf(), "PF "),
(self.af(), "AF "), (self.af(), "AF "),
(self.zf(), "ZF "), (self.zf(), "ZF "),

View File

@@ -19,7 +19,7 @@ pub trait Operand: Copy + 'static +
{ {
type Signed: nt::ops::checked::CheckedShr<Output = Self::Signed>; type Signed: nt::ops::checked::CheckedShr<Output = Self::Signed>;
const HI_BIT_MASK: Self; const HI_BIT_MASK: Self;
const BITS: Self; const BITS: u8;
fn hi_bit(self) -> bool; fn hi_bit(self) -> bool;
fn as_signed(self) -> Self::Signed; fn as_signed(self) -> Self::Signed;
@@ -47,7 +47,7 @@ impl Operand for u8 {
impl Operand for u16 { impl Operand for u16 {
type Signed = i16; type Signed = i16;
const HI_BIT_MASK: u16 = 0x8000; const HI_BIT_MASK: u16 = 0x8000;
const BITS: u16 = 16; const BITS: u8 = 16;
fn hi_bit(self) -> bool { fn hi_bit(self) -> bool {
self >> 15 == 1 self >> 15 == 1

View File

@@ -75,8 +75,9 @@ pub fn call(mut ip: Reg, bus: &mut Bus, ss: u16, sp: Reg, rel16: u16) {
pub fn cmp<T: Operand>(flags: &mut Flags, dst: impl RValue<T>, src: impl RValue<T>) { pub fn cmp<T: Operand>(flags: &mut Flags, dst: impl RValue<T>, src: impl RValue<T>) {
let (dst, src) = (dst.read(), src.read()); let (dst, src) = (dst.read(), src.read());
let (res, carry) = dst.overflowing_sub(&src); let (res, carry) = dst.overflowing_sub(&src);
flags.cf = carry; flags.update(FlagOp::SUB { dst: dst.into(), src: src.into(), cf: carry },
flags.update(FlagOp::SUB { dst: dst.into(), src: src.into() }, res.into(), T::HI_BIT_MASK.into()); res.into(),
T::HI_BIT_MASK.into());
} }
pub fn cmps<T>(flags: &mut Flags, pub fn cmps<T>(flags: &mut Flags,
@@ -102,14 +103,16 @@ pub fn dec<T: Operand, LVal: LValue<T>>(flags: &mut Flags, mut dst: LVal) {
let dst_before = dst.read(); let dst_before = dst.read();
let res = dst_before.wrapping_sub(&T::one()); let res = dst_before.wrapping_sub(&T::one());
dst.write(res); dst.write(res);
flags.update(FlagOp::DEC, res.into(), T::HI_BIT_MASK.into()); let cf = flags.cf(); // we'll store current CF so we don't clobber it
flags.update(FlagOp::DEC { cf }, res.into(), T::HI_BIT_MASK.into());
} }
pub fn inc<T: Operand, LVal: LValue<T>>(flags: &mut Flags, mut dst: LVal) { pub fn inc<T: Operand, LVal: LValue<T>>(flags: &mut Flags, mut dst: LVal) {
let dst_before = dst.read(); let dst_before = dst.read();
let res = dst_before.wrapping_add(&T::one()); let res = dst_before.wrapping_add(&T::one());
dst.write(res); dst.write(res);
flags.update(FlagOp::INC, res.into(), T::HI_BIT_MASK.into()); let cf = flags.cf(); // we'll store current CF so we don't clobber it
flags.update(FlagOp::INC { cf }, res.into(), T::HI_BIT_MASK.into());
} }
pub fn int(cpu: &mut i8088, bus: &mut Bus, num: u8) { pub fn int(cpu: &mut i8088, bus: &mut Bus, num: u8) {
@@ -120,25 +123,25 @@ pub fn int(cpu: &mut i8088, bus: &mut Bus, num: u8) {
} }
pub fn ja(flags: &Flags, mut ip: Reg, rel8: u16) { pub fn ja(flags: &Flags, mut ip: Reg, rel8: u16) {
if !flags.cf && !flags.zf() { if !flags.cf() && !flags.zf() {
ip.write(ip.read().wrapping_add(rel8)); ip.write(ip.read().wrapping_add(rel8));
} }
} }
pub fn jae(flags: &Flags, mut ip: Reg, rel8: u16) { pub fn jae(flags: &Flags, mut ip: Reg, rel8: u16) {
if !flags.cf { if !flags.cf() {
ip.write(ip.read().wrapping_add(rel8)); ip.write(ip.read().wrapping_add(rel8));
} }
} }
pub fn jb(flags: &Flags, mut ip: Reg, rel8: u16) { pub fn jb(flags: &Flags, mut ip: Reg, rel8: u16) {
if flags.cf { if flags.cf() {
ip.write(ip.read().wrapping_add(rel8)); ip.write(ip.read().wrapping_add(rel8));
} }
} }
pub fn jbe(flags: &Flags, mut ip: Reg, rel8: u16) { pub fn jbe(flags: &Flags, mut ip: Reg, rel8: u16) {
if flags.cf || flags.zf() { if flags.cf() || flags.zf() {
ip.write(ip.read().wrapping_add(rel8)); ip.write(ip.read().wrapping_add(rel8));
} }
} }
@@ -330,7 +333,7 @@ pub fn push_modrm<'a>(ss: u16, sp: Reg, src: DynLValue<'a>, bus: &mut Bus) {
pub fn rol<T, U, LVal, RVal>(flags: &mut Flags, pub fn rol<T, U, LVal, RVal>(flags: &mut Flags,
mut dst: LVal, mut dst: LVal,
src: RVal) src: RVal)
where T: Operand + From<U>, where T: Operand,
U: Operand + AsPrimitive<u32>, U: Operand + AsPrimitive<u32>,
LVal: LValue<T>, LVal: LValue<T>,
RVal: RValue<U> RVal: RValue<U>
@@ -340,15 +343,23 @@ where T: Operand + From<U>,
if src_before == U::zero() { return } // 286 and beyond probably masks shamt before this check? if src_before == U::zero() { return } // 286 and beyond probably masks shamt before this check?
let res = dst_before.rotate_left(src_before.as_()); let res = dst_before.rotate_left(src_before.as_());
dst.write(res); dst.write(res);
flags.cf = T::zero() != dst_before & (T::HI_BIT_MASK >> (<T as From<U>>::from(src_before - U::one()) % T::BITS)); flags.update(FlagOp::ROL { dst: dst_before.into(),
flags.update_of(T::zero() != T::HI_BIT_MASK & (dst_before ^ res)); src: src_before.into(),
bits: T::BITS,
cf: flags.cf(),
pf: flags.pf(),
af: flags.af(),
zf: flags.zf(),
sf: flags.sf() },
res.into(),
T::HI_BIT_MASK.into());
} }
pub fn ror<T, U, LVal, RVal>(flags: &mut Flags, pub fn ror<T, U, LVal, RVal>(flags: &mut Flags,
mut dst: LVal, mut dst: LVal,
src: RVal) src: RVal)
where T: Operand + From<U>, where T: Operand,
U: Operand + Into<T> + AsPrimitive<u32>, U: Operand + AsPrimitive<u32>,
LVal: LValue<T>, LVal: LValue<T>,
RVal: RValue<U> RVal: RValue<U>
{ {
@@ -357,8 +368,16 @@ where T: Operand + From<U>,
if src_before == U::zero() { return } // 286 and beyond probably masks shamt before this check? if src_before == U::zero() { return } // 286 and beyond probably masks shamt before this check?
let res = dst_before.rotate_right(src_before.as_()); let res = dst_before.rotate_right(src_before.as_());
dst.write(res); dst.write(res);
flags.cf = T::zero() != dst_before & (T::one() << (<T as From<U>>::from(src_before - U::one()) % T::BITS)); flags.update(FlagOp::ROR { dst: dst_before.into(),
flags.update_of(T::zero() != T::HI_BIT_MASK & (dst_before ^ res)); src: src_before.into(),
bits: T::BITS,
cf: flags.cf(),
pf: flags.pf(),
af: flags.af(),
zf: flags.zf(),
sf: flags.sf() },
res.into(),
T::HI_BIT_MASK.into());
} }
pub fn sar<T, U, LVal, RVal>(flags: &mut Flags, mut dst: LVal, src: RVal) pub fn sar<T, U, LVal, RVal>(flags: &mut Flags, mut dst: LVal, src: RVal)
@@ -385,11 +404,9 @@ where T: Operand,
None => if sign_bit { T::max_value() } else { T::zero() } // Out-of-Range yields all sign bits None => if sign_bit { T::max_value() } else { T::zero() } // Out-of-Range yields all sign bits
}; };
dst.write(res); dst.write(res);
flags.cf = match T::one().checked_shl(src_before.as_() - 1) { flags.update(FlagOp::SAR { dst: dst_before.into(), src: src_before.into(), sign_bit },
Some(carrymask) => dst_before & carrymask != T::zero(), res.into(),
None => sign_bit T::HI_BIT_MASK.into());
};
flags.update(FlagOp::SHIFT { dst: dst_before.into() }, res.into(), T::HI_BIT_MASK.into());
} }
pub fn scas<T, RVal>(flags: &mut Flags, pub fn scas<T, RVal>(flags: &mut Flags,
@@ -426,11 +443,9 @@ where T: Operand,
None => T::zero() None => T::zero()
}; };
dst.write(res); dst.write(res);
flags.cf = match T::HI_BIT_MASK.checked_shr(src_before.as_() - 1) { flags.update(FlagOp::SHL { dst: dst_before.into(), src: src_before.into() },
Some(carrymask) => dst_before & carrymask != T::zero(), res.into(),
None => false T::HI_BIT_MASK.into());
};
flags.update(FlagOp::SHIFT { dst: dst_before.into() }, res.into(), T::HI_BIT_MASK.into());
} }
// See comments on sar() // See comments on sar()
@@ -449,11 +464,9 @@ where T: Operand,
None => T::zero() None => T::zero()
}; };
dst.write(res); dst.write(res);
flags.cf = match T::one().checked_shl(src_before.as_() - 1) { flags.update(FlagOp::SHR { dst: dst_before.into(), src: src_before.into() },
Some(carrymask) => dst_before & carrymask != T::zero(), res.into(),
None => false T::HI_BIT_MASK.into());
};
flags.update(FlagOp::SHIFT { dst: dst_before.into() }, res.into(), T::HI_BIT_MASK.into());
} }
pub fn stos<T, RVal>(flags: &Flags, pub fn stos<T, RVal>(flags: &Flags,