emu: Make FlagOp enum algebraic to carry op-specific data
Avoids writing operand data for lazy flags that won't be read
This commit is contained in:
@@ -1,7 +1,5 @@
|
|||||||
use std::fmt::{Debug, Formatter};
|
use std::fmt::{Debug, Formatter};
|
||||||
|
|
||||||
use emu::operands::OperandWidth;
|
|
||||||
|
|
||||||
const CF_BIT: u8 = 0; // Carry Flag
|
const CF_BIT: u8 = 0; // Carry Flag
|
||||||
const PF_BIT: u8 = 2; // Parity Flag
|
const PF_BIT: u8 = 2; // Parity Flag
|
||||||
const AF_BIT: u8 = 4; // Adjust Flag
|
const AF_BIT: u8 = 4; // Adjust Flag
|
||||||
@@ -30,10 +28,8 @@ pub struct Flags {
|
|||||||
|
|
||||||
// ALU state for lazy flag evaluation
|
// ALU state for lazy flag evaluation
|
||||||
flag_op: FlagOp,
|
flag_op: FlagOp,
|
||||||
dst: u16,
|
|
||||||
src: u16,
|
|
||||||
res: u16,
|
res: u16,
|
||||||
sign_bit_mask: u16,
|
sign_mask: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Flags {
|
impl Flags {
|
||||||
@@ -61,31 +57,26 @@ 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 },
|
||||||
_ => { self.res & self.sign_bit_mask != 0 },
|
_ => { self.res & self.sign_mask != 0 },
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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_bit_mask - 1 },
|
FlagOp::DEC => { self.res == self.sign_mask - 1 },
|
||||||
FlagOp::INC => { self.res == self.sign_bit_mask },
|
FlagOp::INC => { self.res == self.sign_mask },
|
||||||
FlagOp::SHIFT => { 0 != self.sign_bit_mask & (self.dst ^ self.res) },
|
FlagOp::SHIFT { dst } => { 0 != self.sign_mask & (dst ^ self.res) },
|
||||||
FlagOp::SUB => { 0 != self.sign_bit_mask & // In the (maybe) sign bit...
|
FlagOp::SUB { dst, src } => { 0 != self.sign_mask & // In the (maybe) sign bit...
|
||||||
(self.src ^ self.dst) & // ...operands have different signs...
|
(src ^ dst) & // ...operands have different signs...
|
||||||
(self.dst ^ self.res) /* ...and result sign-bit changed */ },
|
(dst ^ self.res) }, /* ...and result sign-bit changed */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update(&mut self, op: FlagOp, dst: u16, src: u16, res: u16, op_width: OperandWidth) {
|
pub fn update(&mut self, op: FlagOp, res: u16, sign_mask: u16) {
|
||||||
self.flag_op = op;
|
self.flag_op = op;
|
||||||
self.dst = dst;
|
|
||||||
self.src = src;
|
|
||||||
self.res = res;
|
self.res = res;
|
||||||
self.sign_bit_mask = match op_width {
|
self.sign_mask = sign_mask;
|
||||||
OperandWidth::Byte => 0x80,
|
|
||||||
OperandWidth::Word => 0x8000,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// The rotate ops update OF don't touch PF/AF/ZF/SF so we make
|
// The rotate ops update OF don't touch PF/AF/ZF/SF so we make
|
||||||
@@ -105,8 +96,8 @@ pub enum FlagOp {
|
|||||||
Eager, // precomputed into result, for POPF or anything that changes only some flags
|
Eager, // precomputed into result, for POPF or anything that changes only some flags
|
||||||
DEC,
|
DEC,
|
||||||
INC,
|
INC,
|
||||||
SHIFT,
|
SHIFT { dst: u16 },
|
||||||
SUB,
|
SUB { dst: u16, src: u16 },
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for FlagOp {
|
impl Default for FlagOp {
|
||||||
@@ -122,10 +113,8 @@ impl From<u16> for Flags {
|
|||||||
df: flags & 1 << DF_BIT != 0,
|
df: flags & 1 << DF_BIT != 0,
|
||||||
|
|
||||||
flag_op: FlagOp::Eager,
|
flag_op: FlagOp::Eager,
|
||||||
dst: 0,
|
|
||||||
src: 0,
|
|
||||||
res: flags,
|
res: flags,
|
||||||
sign_bit_mask: 0,
|
sign_mask: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ pub trait Operand: Copy + 'static +
|
|||||||
std::ops::Shr<Output = Self>
|
std::ops::Shr<Output = Self>
|
||||||
{
|
{
|
||||||
type Signed: nt::ops::checked::CheckedShr<Output = Self::Signed>;
|
type Signed: nt::ops::checked::CheckedShr<Output = Self::Signed>;
|
||||||
const WIDTH: OperandWidth;
|
|
||||||
const HI_BIT_MASK: Self;
|
const HI_BIT_MASK: Self;
|
||||||
const BITS: Self;
|
const BITS: Self;
|
||||||
|
|
||||||
@@ -27,14 +26,8 @@ pub trait Operand: Copy + 'static +
|
|||||||
fn from_signed(src: Self::Signed) -> Self;
|
fn from_signed(src: Self::Signed) -> Self;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum OperandWidth {
|
|
||||||
Byte,
|
|
||||||
Word
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Operand for u8 {
|
impl Operand for u8 {
|
||||||
type Signed = i8;
|
type Signed = i8;
|
||||||
const WIDTH: OperandWidth = OperandWidth::Byte;
|
|
||||||
const HI_BIT_MASK: u8 = 0x80;
|
const HI_BIT_MASK: u8 = 0x80;
|
||||||
const BITS: u8 = 8;
|
const BITS: u8 = 8;
|
||||||
|
|
||||||
@@ -53,7 +46,6 @@ impl Operand for u8 {
|
|||||||
|
|
||||||
impl Operand for u16 {
|
impl Operand for u16 {
|
||||||
type Signed = i16;
|
type Signed = i16;
|
||||||
const WIDTH: OperandWidth = OperandWidth::Word;
|
|
||||||
const HI_BIT_MASK: u16 = 0x8000;
|
const HI_BIT_MASK: u16 = 0x8000;
|
||||||
const BITS: u16 = 16;
|
const BITS: u16 = 16;
|
||||||
|
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ pub fn cmp<T: Operand>(flags: &mut Flags, dst: impl RValue<T>, src: impl RValue<
|
|||||||
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.cf = carry;
|
||||||
flags.update(FlagOp::SUB, dst.into(), src.into(), res.into(), T::WIDTH);
|
flags.update(FlagOp::SUB { dst: dst.into(), src: src.into() }, res.into(), T::HI_BIT_MASK.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn cmps<T>(flags: &mut Flags,
|
pub fn cmps<T>(flags: &mut Flags,
|
||||||
@@ -102,14 +102,14 @@ 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, dst_before.into(), 0, res.into(), T::WIDTH);
|
flags.update(FlagOp::DEC, 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, dst_before.into(), 0, res.into(), T::WIDTH);
|
flags.update(FlagOp::INC, 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) {
|
||||||
@@ -389,7 +389,7 @@ where T: Operand,
|
|||||||
Some(carrymask) => dst_before & carrymask != T::zero(),
|
Some(carrymask) => dst_before & carrymask != T::zero(),
|
||||||
None => sign_bit
|
None => sign_bit
|
||||||
};
|
};
|
||||||
flags.update(FlagOp::SHIFT, dst_before.into(), src_before.into(), res.into(), T::WIDTH);
|
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,
|
||||||
@@ -430,7 +430,7 @@ where T: Operand,
|
|||||||
Some(carrymask) => dst_before & carrymask != T::zero(),
|
Some(carrymask) => dst_before & carrymask != T::zero(),
|
||||||
None => false
|
None => false
|
||||||
};
|
};
|
||||||
flags.update(FlagOp::SHIFT, dst_before.into(), src_before.into(), res.into(), T::WIDTH);
|
flags.update(FlagOp::SHIFT { dst: dst_before.into() }, res.into(), T::HI_BIT_MASK.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
// See comments on sar()
|
// See comments on sar()
|
||||||
@@ -453,7 +453,7 @@ where T: Operand,
|
|||||||
Some(carrymask) => dst_before & carrymask != T::zero(),
|
Some(carrymask) => dst_before & carrymask != T::zero(),
|
||||||
None => false
|
None => false
|
||||||
};
|
};
|
||||||
flags.update(FlagOp::SHIFT, dst_before.into(), src_before.into(), res.into(), T::WIDTH);
|
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,
|
||||||
|
|||||||
Reference in New Issue
Block a user