From 874fc6adbc125e5d94be4feb75d1b6baa427b8ea Mon Sep 17 00:00:00 2001 From: Jared Burce Date: Sat, 27 Mar 2021 00:53:37 -0700 Subject: [PATCH] emu: Make flags lazy, implement parity flag & JPE/JPO ops --- src/emu/flags.rs | 138 +++++++++++++++++++++++++++++++++--------- src/emu/i8088.rs | 4 +- src/emu/operands.rs | 21 ++++--- src/emu/operations.rs | 91 ++++++++++++---------------- 4 files changed, 163 insertions(+), 91 deletions(-) diff --git a/src/emu/flags.rs b/src/emu/flags.rs index 89830b8..8578a7e 100644 --- a/src/emu/flags.rs +++ b/src/emu/flags.rs @@ -1,34 +1,118 @@ use std::fmt::{Debug, Formatter}; +use emu::operands::OperandWidth; + +const CF_BIT: u8 = 0; // Carry Flag +const PF_BIT: u8 = 2; // Parity Flag +const AF_BIT: u8 = 4; // Adjust Flag +const ZF_BIT: u8 = 6; // Zero Flag +const SF_BIT: u8 = 7; // Sign Flag +const TF_BIT: u8 = 8; // Trap Flag +const IF_BIT: u8 = 9; // Interrupt Enable +const DF_BIT: u8 = 10; // Direction Flag +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) // 1: Reserved - pub pf: bool, // 2: Parity Flag: 1=PE(Even), 0=PO(Odd) + // 2: LAZY Parity Flag: 1=PE(Even), 0=PO(Odd) // 3: Reserved - pub af: bool, // 4: Adjust Flag: 1=AC(Aux Carry), 0=NA(No Aux Carry) + // 4: LAZY Adjust Flag: 1=AC(Aux Carry), 0=NA(No Aux Carry) // 5: Reserved - pub zf: bool, // 6: Zero Flag: 1=ZR(Zero), 0=NZ(Not Zero) - pub sf: bool, // 7: Sign Flag: 1=NG(Negative), 0=PL(Positive) + // 6: LAZY Zero Flag: 1=ZR(Zero), 0=NZ(Not Zero) + // 7: LAZY Sign Flag: 1=NG(Negative), 0=PL(Positive) pub tf: bool, // 8: Trap Flag pub ie: bool, // 9: (Real name "IF") Interrupt Enable: 1=EI(Enable Interrupt), 0=DI(Disable Interrupt) pub df: bool, // 10: Direction Flag: 1=DN(Down), 0=UP(Up) - pub of: bool, // 11: Overflow Flag: 1=OV(Overflow), 0=NV(Not Overflow) + // 11: LAZY Overflow Flag: 1=OV(Overflow), 0=NV(Not Overflow) // bits 12-15 always 1 + + // ALU state for lazy flag evaluation + flag_op: FlagOp, + dst: u16, + src: u16, + res: u16, + sign_bit_mask: u16, +} + +impl Flags { + pub fn pf(&self) -> bool { + match self.flag_op { + FlagOp::Eager => { self.res & 1 << PF_BIT != 0 }, + _ => { self.res.count_ones() & 1 == 0 }, + } + } + + pub fn af(&self) -> bool { + match self.flag_op { + FlagOp::Eager => { self.res & 1 << AF_BIT != 0 }, + _ => { false /* XXX: unimplemented! */ }, + } + } + + pub fn zf(&self) -> bool { + match self.flag_op { + FlagOp::Eager => { self.res & 1 << ZF_BIT != 0 }, + _ => { self.res == 0 }, + } + } + + pub fn sf(&self) -> bool { + match self.flag_op { + FlagOp::Eager => { self.res & 1 << SF_BIT != 0 }, + _ => { self.res & self.sign_bit_mask != 0 }, + } + } + + pub fn of(&self) -> bool { + match self.flag_op { + FlagOp::Eager => { self.res & 1 << OF_BIT != 0 }, + FlagOp::DEC => { self.res == self.sign_bit_mask - 1 }, + FlagOp::INC => { self.res == self.sign_bit_mask }, + FlagOp::SUB => { 0 != self.sign_bit_mask & // In the (maybe) sign bit... + (self.src ^ self.dst) & // ...operands have different signs... + (self.dst ^ self.res) /* ...and result sign-bit changed */ }, + } + } + + pub fn update(&mut self, op: FlagOp, dst: u16, src: u16, res: u16, op_width: OperandWidth) { + self.flag_op = op; + self.dst = dst; + self.src = src; + self.res = res; + self.sign_bit_mask = match op_width { + OperandWidth::Byte => 0x80, + OperandWidth::Word => 0x8000, + } + } +} + +#[derive(Clone, Copy)] +pub enum FlagOp { + Eager, // precomputed into result, for e.g. POPF? (Anything else?) + DEC, + INC, + SUB, +} + +impl Default for FlagOp { + fn default() -> Self { FlagOp::Eager } } impl From for Flags { fn from(flags: u16) -> Self { Self { - cf: flags & 1 != 0, - pf: flags & 1 << 2 != 0, - af: flags & 1 << 4 != 0, - zf: flags & 1 << 6 != 0, - sf: flags & 1 << 7 != 0, - tf: flags & 1 << 8 != 0, - ie: flags & 1 << 9 != 0, - df: flags & 1 << 10 != 0, - of: flags & 1 << 11 != 0, + cf: flags & 1 << CF_BIT != 0, + tf: flags & 1 << TF_BIT != 0, + ie: flags & 1 << IF_BIT != 0, + df: flags & 1 << DF_BIT != 0, + + flag_op: FlagOp::Eager, + dst: 0, + src: 0, + res: flags, + sign_bit_mask: 0, } } } @@ -37,14 +121,14 @@ impl From 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.pf as u16) << 2 - | (flags.af as u16) << 4 - | (flags.zf as u16) << 6 - | (flags.sf as u16) << 7 - | (flags.tf as u16) << 8 - | (flags.ie as u16) << 9 - | (flags.df as u16) << 10 - | (flags.of as u16) << 11 + | (flags.pf() as u16) << PF_BIT + | (flags.af() as u16) << AF_BIT + | (flags.zf() as u16) << ZF_BIT + | (flags.sf() as u16) << SF_BIT + | (flags.tf as u16) << TF_BIT + | (flags.ie as u16) << IF_BIT + | (flags.df as u16) << DF_BIT + | (flags.of() as u16) << OF_BIT } } @@ -53,14 +137,14 @@ impl Debug for Flags { use std::fmt::Write; fmt.write_str("[ ")?; for flag in [ (self.cf, "CF "), - (self.pf, "PF "), - (self.af, "AF "), - (self.zf, "ZF "), - (self.sf, "SF "), + (self.pf(), "PF "), + (self.af(), "AF "), + (self.zf(), "ZF "), + (self.sf(), "SF "), (self.tf, "TF "), (self.ie, "IF "), (self.df, "DF "), - (self.of, "OF ") ].iter() { + (self.of(), "OF ") ].iter() { if flag.0 { fmt.write_str(flag.1)? }; } fmt.write_char(']')?; diff --git a/src/emu/i8088.rs b/src/emu/i8088.rs index 348c654..52014d5 100644 --- a/src/emu/i8088.rs +++ b/src/emu/i8088.rs @@ -456,8 +456,8 @@ impl i8088 { 0x77 => ja[flags, reg=ip, rel8] / "16/4", // JNBE/JA rel8 0x78 => js[flags, reg=ip, rel8] / "16/4", // JS rel8 0x79 => jns[flags, reg=ip, rel8] / "16/4", // JNS rel8 - //0x7A => jpe[flags, reg=ip, rel8] / "16/4", // JP/JPE rel8 XXX: Parity flag not implemented - //0x7B => jpo[flags, reg=ip, rel8] / "16/4", // JNP/JPO rel8 XXX: Parity flag not implemented + 0x7A => jpe[flags, reg=ip, rel8] / "16/4", // JP/JPE rel8 + 0x7B => jpo[flags, reg=ip, rel8] / "16/4", // JNP/JPO rel8 0x7C => jl[flags, reg=ip, rel8] / "16/4", // JL/JNGE rel8 0x7D => jge[flags, reg=ip, rel8] / "16/4", // JNL/JGE rel8 0x7E => jle[flags, reg=ip, rel8] / "16/4", // JLE/JNG rel8 diff --git a/src/emu/operands.rs b/src/emu/operands.rs index 10cb0a3..db36f4c 100644 --- a/src/emu/operands.rs +++ b/src/emu/operands.rs @@ -7,27 +7,28 @@ use emu::util::{read_hi, read_lo, segoff_to_addr, write_hi, write_lo}; use emu::pc::Bus; pub trait Operand: Copy + + std::convert::Into + nt::Num + nt::ops::overflowing::OverflowingAdd + nt::ops::overflowing::OverflowingSub + + nt::ops::wrapping::WrappingAdd + + nt::ops::wrapping::WrappingSub + std::ops::BitAnd + std::ops::BitXor { - const HI_BIT_MASK: Self; - fn hi_bit(&self) -> bool; + const WIDTH: OperandWidth; +} + +pub enum OperandWidth { + Byte, + Word } impl Operand for u8 { - const HI_BIT_MASK: u8 = 0x80; - fn hi_bit(&self) -> bool { - self >> 7 == 1 - } + const WIDTH: OperandWidth = OperandWidth::Byte; } impl Operand for u16 { - const HI_BIT_MASK: u16 = 0x8000; - fn hi_bit(&self) -> bool { - self >> 15 == 1 - } + const WIDTH: OperandWidth = OperandWidth::Word; } pub trait LValue: RValue { diff --git a/src/emu/operations.rs b/src/emu/operations.rs index 470e92b..1a0fd12 100644 --- a/src/emu/operations.rs +++ b/src/emu/operations.rs @@ -2,7 +2,7 @@ use std::convert::Into; use std::fmt::Debug; use emu::dos; -use emu::flags::Flags; +use emu::flags::{FlagOp, Flags}; use emu::i8088::{RepPrefix, i8088}; use emu::operands::{Address, DynLValue, FarPtr, LValue, Operand, Reg, RValue}; use emu::pc::Bus; @@ -50,33 +50,6 @@ macro_rules! string_op { } } -// Based on SUB/CMP, others will vary! -// Decide if this can realistically be generalized -macro_rules! update_flags { - // Base Case - (@recur $flags:expr, $t:ty, $dst:ident, $carry:ident) => {}; - - (@af $flags:expr, $t:ty, $dst:ident, $carry:ident) => { /* XXX: Adjust/AuxCarry esoteric? (mostly for BCD?), might not need */ }; - (@cf $flags:expr, $t:ty, $dst:ident, $carry:ident) => { $flags.cf = $carry }; - (@of $flags:expr, $t:ty, $dst:ident, $carry:ident) => { compile_error!("compute flags.of outside macro"); }; - (@pf $flags:expr, $t:ty, $dst:ident, $carry:ident) => { /* XXX: parity looks esoteric, might not need */ }; - (@sf $flags:expr, $t:ty, $dst:ident, $carry:ident) => { $flags.sf = $dst.hi_bit(); }; - (@zf $flags:expr, $t:ty, $dst:ident, $carry:ident) => { $flags.zf = $dst == <$t>::zero(); }; - - // Recursive step - (@recur $flags:expr, $t:ty, $dst:ident, $carry:ident, $next:ident $(, $rest:ident)* ) => { - update_flags!(@$next $flags, $t, $dst, $carry); - update_flags!(@recur $flags, $t, $dst, $carry $(, $rest)*); - }; - - // Entry Point - ($flags:expr, $t:ty, $eval:expr $(, $flagnames:ident)* ) => { { - let (dst, _carry) = $eval; // _carry used iff CF flag being updated - update_flags!(@recur $flags, $t, dst, _carry $(, $flagnames)*); - dst - } } -} - pub fn assert(loc: impl RValue, val: impl RValue) { assert_eq!(loc.read(), val.read(), "ASSERT instruction failed: {:#2X?} != {:#2X?}", loc.read(), val.read()); @@ -99,10 +72,9 @@ pub fn call(mut ip: Reg, bus: &mut Bus, ss: u16, sp: Reg, rel16: u16) { pub fn cmp(flags: &mut Flags, dst: impl RValue, src: impl RValue) { let (dst, src) = (dst.read(), src.read()); - let after = update_flags!(flags, T, dst.overflowing_sub(&src), af, cf, pf, sf, zf); - flags.of = T::zero() != T::HI_BIT_MASK & // In the (maybe) sign bit... - (src ^ dst) & // ...operands have different signs... - (dst ^ after); // ...and destination sign-bit changed + let (res, carry) = dst.overflowing_sub(&src); + flags.cf = carry; + flags.update(FlagOp::SUB, dst.into(), src.into(), res.into(), T::WIDTH); } pub fn cmpsb(flags: &mut Flags, @@ -113,7 +85,7 @@ pub fn cmpsb(flags: &mut Flags, mut si: Reg, es: Reg, mut di: Reg) { - string_op!((u8, flags, rep, cx, si=si, di=di, zf=flags.zf), { + string_op!((u8, flags, rep, cx, si=si, di=di, zf=flags.zf()), { let src = >::read(&FarPtr { bus: bus, segment: seg, offset: si.read() }); let dst = >::read(&FarPtr { bus: bus, segment: es.read(), offset: di.read() }); @@ -129,7 +101,7 @@ pub fn cmpsw(flags: &mut Flags, mut si: Reg, es: Reg, mut di: Reg) { - string_op!((u16, flags, rep, cx, si=si, di=di, zf=flags.zf), { + string_op!((u16, flags, rep, cx, si=si, di=di, zf=flags.zf()), { let src = >::read(&FarPtr { bus: bus, segment: seg, offset: si.read() }); let dst = >::read(&FarPtr { bus: bus, segment: es.read(), offset: di.read() }); @@ -138,15 +110,17 @@ pub fn cmpsw(flags: &mut Flags, } pub fn dec>(flags: &mut Flags, mut dst: LVal) { - let res = update_flags!(flags, T, dst.read().overflowing_sub(&T::one()), sf, zf, af, pf); - flags.of = res == T::HI_BIT_MASK - T::one(); + let dst_before = dst.read(); + let res = dst_before.wrapping_sub(&T::one()); dst.write(res); + flags.update(FlagOp::DEC, dst_before.into(), 0, res.into(), T::WIDTH); } pub fn inc>(flags: &mut Flags, mut dst: LVal) { - let res = update_flags!(flags, T, dst.read().overflowing_add(&T::one()), sf, zf, af, pf); - flags.of = res == T::HI_BIT_MASK; + let dst_before = dst.read(); + let res = dst_before.wrapping_add(&T::one()); dst.write(res); + flags.update(FlagOp::INC, dst_before.into(), 0, res.into(), T::WIDTH); } pub fn int(cpu: &mut i8088, bus: &mut Bus, num: u8) { @@ -157,7 +131,7 @@ 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)); } } @@ -175,7 +149,7 @@ pub fn jb(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)); } } @@ -187,25 +161,25 @@ pub fn jcxz(mut ip: Reg, cx: Reg, rel8: u16) { } pub fn jg(flags: &Flags, mut ip: Reg, rel8: u16) { - if flags.zf && flags.sf == flags.of { + if flags.zf() && flags.sf() == flags.of() { ip.write(ip.read().wrapping_add(rel8)); } } pub fn jge(flags: &Flags, mut ip: Reg, rel8: u16) { - if flags.sf == flags.of { + if flags.sf() == flags.of() { ip.write(ip.read().wrapping_add(rel8)); } } pub fn jl(flags: &Flags, mut ip: Reg, rel8: u16) { - if flags.sf != flags.of { + if flags.sf() != flags.of() { ip.write(ip.read().wrapping_add(rel8)); } } pub fn jle(flags: &Flags, mut ip: Reg, rel8: u16) { - if flags.zf || flags.sf != flags.of { + if flags.zf() || flags.sf() != flags.of() { ip.write(ip.read().wrapping_add(rel8)); } } @@ -215,37 +189,50 @@ pub fn jmp(mut ip: Reg, rel: u16) { } pub fn jno(flags: &Flags, mut ip: Reg, rel8: u16) { - if !flags.of { + if !flags.of() { ip.write(ip.read().wrapping_add(rel8)); } } pub fn jns(flags: &Flags, mut ip: Reg, rel8: u16) { - if !flags.sf { + if !flags.sf() { ip.write(ip.read().wrapping_add(rel8)); } } pub fn jnz(flags: &Flags, mut ip: Reg, rel8: u16) { - if !flags.zf { + if !flags.zf() { ip.write(ip.read().wrapping_add(rel8)); } } pub fn jo(flags: &Flags, mut ip: Reg, rel8: u16) { - if flags.of { + if flags.of() { ip.write(ip.read().wrapping_add(rel8)); } } +pub fn jpe(flags: &Flags, mut ip: Reg, rel8: u16) { + if flags.pf() { + ip.write(ip.read().wrapping_add(rel8)); + } +} + +pub fn jpo(flags: &Flags, mut ip: Reg, rel8: u16) { + if !flags.pf() { + ip.write(ip.read().wrapping_add(rel8)); + } +} + + pub fn js(flags: &Flags, mut ip: Reg, rel8: u16) { - if flags.sf { + if flags.sf() { ip.write(ip.read().wrapping_add(rel8)); } } pub fn jz(flags: &Flags, mut ip: Reg, rel8: u16) { - if flags.zf { + if flags.zf() { ip.write(ip.read().wrapping_add(rel8)); } } @@ -394,7 +381,7 @@ pub fn scasb(flags: &mut Flags, mut di: Reg, needle: impl RValue) { let needle = needle.read(); - string_op!((u8, flags, rep, cx, di=di, zf=flags.zf), { + string_op!((u8, flags, rep, cx, di=di, zf=flags.zf()), { let elem = FarPtr { bus: bus, segment: es.read(), offset: di.read() }; cmp(flags, elem, needle); }); @@ -408,7 +395,7 @@ pub fn scasw(flags: &mut Flags, mut di: Reg, needle: impl RValue) { let needle = needle.read(); - string_op!((u16, flags, rep, cx, di=di, zf=flags.zf), { + string_op!((u16, flags, rep, cx, di=di, zf=flags.zf()), { let elem = FarPtr { bus: bus, segment: es.read(), offset: di.read() }; cmp(flags, elem, needle); });