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)]
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
// 2: LAZY Parity Flag: 1=PE(Even), 0=PO(Odd)
// 3: Reserved
@@ -33,9 +33,40 @@ pub struct 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 {
match self.flag_op {
FlagOp::Eager => { self.res & 1 << PF_BIT != 0 },
FlagOp::ROL { pf, .. }
| FlagOp::ROR { pf, .. } => { pf },
_ => { self.res.count_ones() & 1 == 0 },
}
}
@@ -43,6 +74,8 @@ impl Flags {
pub fn af(&self) -> bool {
match self.flag_op {
FlagOp::Eager => { self.res & 1 << AF_BIT != 0 },
FlagOp::ROL { af, .. }
| FlagOp::ROR { af, .. } => { af },
_ => { false /* XXX: unimplemented! */ },
}
}
@@ -50,6 +83,8 @@ impl Flags {
pub fn zf(&self) -> bool {
match self.flag_op {
FlagOp::Eager => { self.res & 1 << ZF_BIT != 0 },
FlagOp::ROL { zf, .. }
| FlagOp::ROR { zf, .. } => { zf },
_ => { self.res == 0 },
}
}
@@ -57,6 +92,8 @@ impl Flags {
pub fn sf(&self) -> bool {
match self.flag_op {
FlagOp::Eager => { self.res & 1 << SF_BIT != 0 },
FlagOp::ROL { sf, .. }
| FlagOp::ROR { sf, .. } => { sf },
_ => { self.res & self.sign_mask != 0 },
}
}
@@ -64,12 +101,16 @@ impl Flags {
pub fn of(&self) -> bool {
match self.flag_op {
FlagOp::Eager => { self.res & 1 << OF_BIT != 0 },
FlagOp::DEC => { self.res == self.sign_mask - 1 },
FlagOp::INC => { self.res == self.sign_mask },
FlagOp::SHIFT { 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 */
FlagOp::DEC { .. } => { self.res == self.sign_mask - 1 },
FlagOp::INC { .. } => { self.res == self.sign_mask },
FlagOp::ROL { dst, .. }
| FlagOp::ROR { dst, .. } => { self.sign_mask & (dst ^ self.res) != 0 },
FlagOp::SAR { dst, .. }
| 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.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)]
pub enum FlagOp {
Eager, // precomputed into result, for POPF or anything that changes only some flags
DEC,
INC,
SHIFT { dst: u16 },
SUB { dst: u16, src: u16 },
Eager, // precomputed into result, for e.g. POPF
DEC { cf: bool },
INC { cf: bool },
ROL { dst: u16, src: u16, bits: u8, cf: bool, pf: bool, af: bool, zf: bool, sf: bool },
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 {
@@ -107,7 +141,6 @@ impl Default for FlagOp {
impl From<u16> for Flags {
fn from(flags: u16) -> Self {
Self {
cf: flags & 1 << CF_BIT != 0,
tf: flags & 1 << TF_BIT != 0,
ie: flags & 1 << IF_BIT != 0,
df: flags & 1 << DF_BIT != 0,
@@ -122,7 +155,7 @@ impl From<u16> for Flags {
impl From<Flags> for u16 {
fn from(flags: Flags) -> Self {
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.af() as u16) << AF_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> {
use std::fmt::Write;
fmt.write_str("[ ")?;
for flag in [ (self.cf, "CF "),
for flag in [ (self.cf(), "CF "),
(self.pf(), "PF "),
(self.af(), "AF "),
(self.zf(), "ZF "),

View File

@@ -19,7 +19,7 @@ pub trait Operand: Copy + 'static +
{
type Signed: nt::ops::checked::CheckedShr<Output = Self::Signed>;
const HI_BIT_MASK: Self;
const BITS: Self;
const BITS: u8;
fn hi_bit(self) -> bool;
fn as_signed(self) -> Self::Signed;
@@ -47,7 +47,7 @@ impl Operand for u8 {
impl Operand for u16 {
type Signed = i16;
const HI_BIT_MASK: u16 = 0x8000;
const BITS: u16 = 16;
const BITS: u8 = 16;
fn hi_bit(self) -> bool {
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>) {
let (dst, src) = (dst.read(), src.read());
let (res, carry) = dst.overflowing_sub(&src);
flags.cf = carry;
flags.update(FlagOp::SUB { dst: dst.into(), src: src.into() }, res.into(), T::HI_BIT_MASK.into());
flags.update(FlagOp::SUB { dst: dst.into(), src: src.into(), cf: carry },
res.into(),
T::HI_BIT_MASK.into());
}
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 res = dst_before.wrapping_sub(&T::one());
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) {
let dst_before = dst.read();
let res = dst_before.wrapping_add(&T::one());
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) {
@@ -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) {
if !flags.cf && !flags.zf() {
if !flags.cf() && !flags.zf() {
ip.write(ip.read().wrapping_add(rel8));
}
}
pub fn jae(flags: &Flags, mut ip: Reg, rel8: u16) {
if !flags.cf {
if !flags.cf() {
ip.write(ip.read().wrapping_add(rel8));
}
}
pub fn jb(flags: &Flags, mut ip: Reg, rel8: u16) {
if flags.cf {
if flags.cf() {
ip.write(ip.read().wrapping_add(rel8));
}
}
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));
}
}
@@ -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,
mut dst: LVal,
src: RVal)
where T: Operand + From<U>,
where T: Operand,
U: Operand + AsPrimitive<u32>,
LVal: LValue<T>,
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?
let res = dst_before.rotate_left(src_before.as_());
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_of(T::zero() != T::HI_BIT_MASK & (dst_before ^ res));
flags.update(FlagOp::ROL { dst: dst_before.into(),
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,
mut dst: LVal,
src: RVal)
where T: Operand + From<U>,
U: Operand + Into<T> + AsPrimitive<u32>,
where T: Operand,
U: Operand + AsPrimitive<u32>,
LVal: LValue<T>,
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?
let res = dst_before.rotate_right(src_before.as_());
dst.write(res);
flags.cf = T::zero() != dst_before & (T::one() << (<T as From<U>>::from(src_before - U::one()) % T::BITS));
flags.update_of(T::zero() != T::HI_BIT_MASK & (dst_before ^ res));
flags.update(FlagOp::ROR { dst: dst_before.into(),
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)
@@ -385,11 +404,9 @@ where T: Operand,
None => if sign_bit { T::max_value() } else { T::zero() } // Out-of-Range yields all sign bits
};
dst.write(res);
flags.cf = match T::one().checked_shl(src_before.as_() - 1) {
Some(carrymask) => dst_before & carrymask != T::zero(),
None => sign_bit
};
flags.update(FlagOp::SHIFT { dst: dst_before.into() }, res.into(), T::HI_BIT_MASK.into());
flags.update(FlagOp::SAR { dst: dst_before.into(), src: src_before.into(), sign_bit },
res.into(),
T::HI_BIT_MASK.into());
}
pub fn scas<T, RVal>(flags: &mut Flags,
@@ -426,11 +443,9 @@ where T: Operand,
None => T::zero()
};
dst.write(res);
flags.cf = match T::HI_BIT_MASK.checked_shr(src_before.as_() - 1) {
Some(carrymask) => dst_before & carrymask != T::zero(),
None => false
};
flags.update(FlagOp::SHIFT { dst: dst_before.into() }, res.into(), T::HI_BIT_MASK.into());
flags.update(FlagOp::SHL { dst: dst_before.into(), src: src_before.into() },
res.into(),
T::HI_BIT_MASK.into());
}
// See comments on sar()
@@ -449,11 +464,9 @@ where T: Operand,
None => T::zero()
};
dst.write(res);
flags.cf = match T::one().checked_shl(src_before.as_() - 1) {
Some(carrymask) => dst_before & carrymask != T::zero(),
None => false
};
flags.update(FlagOp::SHIFT { dst: dst_before.into() }, res.into(), T::HI_BIT_MASK.into());
flags.update(FlagOp::SHR { dst: dst_before.into(), src: src_before.into() },
res.into(),
T::HI_BIT_MASK.into());
}
pub fn stos<T, RVal>(flags: &Flags,