emu: RCL/RCR ops.

I even did some testing this time...
This commit is contained in:
2021-04-06 00:53:58 -07:00
parent 647841469d
commit 4833b44074
5 changed files with 157 additions and 25 deletions

View File

@@ -7,13 +7,13 @@ authors = ["Jared Burce <jaredr@gmail.com>"]
byteorder = "1.4.2" byteorder = "1.4.2"
env_logger = "0.3" env_logger = "0.3"
itertools = ">=0.4" itertools = ">=0.4"
log = "0.3"
memmap = "~0.2"
gl = "0.10" gl = "0.10"
gfx = "0.17" gfx = "0.17"
gfx_device_gl = "0.15" gfx_device_gl = "0.15"
image = "0.19" image = "0.19"
log = "0.3"
lzw = "0.10" lzw = "0.10"
memmap = "~0.2"
nalgebra = "0.16" nalgebra = "0.16"
num-traits = "0.2.14" num-traits = "0.2.14"
openvr = "0.6.0" openvr = "0.6.0"

View File

@@ -39,11 +39,27 @@ impl Flags {
FlagOp::DEC { cf } => cf, FlagOp::DEC { cf } => cf,
FlagOp::INC { cf } => cf, FlagOp::INC { cf } => cf,
FlagOp::POPF => { self.res & 1 << CF_BIT != 0 }, FlagOp::POPF => { self.res & 1 << CF_BIT != 0 },
FlagOp::ROL { dst, src, rot_mask, .. } => { FlagOp::RCL { dst, shamt, bits, old_cf, .. } => {
dst & (self.sign_mask >> ((src.wrapping_sub(1)) & rot_mask as u16)) != 0 // for carry-rotate shamt bas been modded to have range [0,bits].
let full_dst = dst as u32
| (old_cf as u32) << bits; // dst w/ carry as new hi-bit
let hi_bit = 1u32 << bits;
full_dst & (hi_bit >> shamt) != 0
}, },
FlagOp::ROR { dst, src, rot_mask, .. } => { FlagOp::RCR { dst, shamt, old_cf, .. } => {
dst & (1 << ((src.wrapping_sub(1)) & rot_mask as u16)) != 0 // for carry-rotate shamt bas been modded to have range [0,bits].
let full_dst = (dst as u32) << 1
| old_cf as u32; // dst w/ carry as new lo-bit
full_dst & (1 << shamt) != 0
},
// Rotate without carry only preserves previous CF on shamt=0,
// which is an early-return in the op and doesn't touch flags,
// so it doesn't need to be considered here.
FlagOp::ROL { dst, shamt, rot_mask, .. } => {
dst & (self.sign_mask >> ((shamt.wrapping_sub(1)) & rot_mask as u16)) != 0
},
FlagOp::ROR { dst, shamt, rot_mask, .. } => {
dst & (1 << ((shamt.wrapping_sub(1)) & rot_mask as u16)) != 0
}, },
FlagOp::SAR { dst, src, sign_bit } => { FlagOp::SAR { dst, src, sign_bit } => {
match 1u16.checked_shl(src as u32 - 1) { match 1u16.checked_shl(src as u32 - 1) {
@@ -71,7 +87,9 @@ impl Flags {
match self.flag_op { match self.flag_op {
FlagOp::Eager { pf, .. } => pf, FlagOp::Eager { pf, .. } => pf,
FlagOp::POPF => { self.res & 1 << PF_BIT != 0 }, FlagOp::POPF => { self.res & 1 << PF_BIT != 0 },
FlagOp::ROL { pf, .. } FlagOp::RCL { pf, .. }
| FlagOp::RCR { pf, .. }
| FlagOp::ROL { pf, .. }
| FlagOp::ROR { pf, .. } => pf, | FlagOp::ROR { pf, .. } => pf,
_ => { self.res.count_ones() & 1 == 0 }, _ => { self.res.count_ones() & 1 == 0 },
} }
@@ -81,7 +99,9 @@ impl Flags {
match self.flag_op { match self.flag_op {
FlagOp::Eager { af, .. } => af, FlagOp::Eager { af, .. } => af,
FlagOp::POPF => { self.res & 1 << AF_BIT != 0 }, FlagOp::POPF => { self.res & 1 << AF_BIT != 0 },
FlagOp::ROL { af, .. } FlagOp::RCL { af, .. }
| FlagOp::RCR { af, .. }
| FlagOp::ROL { af, .. }
| FlagOp::ROR { af, .. } => af, | FlagOp::ROR { af, .. } => af,
_ => { false /* XXX: unimplemented! */ }, _ => { false /* XXX: unimplemented! */ },
} }
@@ -91,7 +111,9 @@ impl Flags {
match self.flag_op { match self.flag_op {
FlagOp::Eager { zf, .. } => zf, FlagOp::Eager { zf, .. } => zf,
FlagOp::POPF => { self.res & 1 << ZF_BIT != 0 }, FlagOp::POPF => { self.res & 1 << ZF_BIT != 0 },
FlagOp::ROL { zf, .. } FlagOp::RCL { zf, .. }
| FlagOp::RCR { zf, .. }
| FlagOp::ROL { zf, .. }
| FlagOp::ROR { zf, .. } => zf, | FlagOp::ROR { zf, .. } => zf,
_ => { self.res == 0 }, _ => { self.res == 0 },
} }
@@ -101,7 +123,9 @@ impl Flags {
match self.flag_op { match self.flag_op {
FlagOp::Eager { sf, .. } => sf, FlagOp::Eager { sf, .. } => sf,
FlagOp::POPF => { self.res & 1 << SF_BIT != 0 }, FlagOp::POPF => { self.res & 1 << SF_BIT != 0 },
FlagOp::ROL { sf, .. } FlagOp::RCL { sf, .. }
| FlagOp::RCR { sf, .. }
| FlagOp::ROL { sf, .. }
| FlagOp::ROR { sf, .. } => sf, | FlagOp::ROR { sf, .. } => sf,
_ => { self.res & self.sign_mask != 0 }, _ => { self.res & self.sign_mask != 0 },
} }
@@ -110,17 +134,19 @@ impl Flags {
pub fn of(&self) -> bool { pub fn of(&self) -> bool {
match self.flag_op { match self.flag_op {
FlagOp::Eager { of, .. } => of, FlagOp::Eager { of, .. } => of,
FlagOp::POPF => { 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::ROL { dst, .. } FlagOp::POPF => { self.res & 1 << OF_BIT != 0 },
FlagOp::RCL { dst, .. }
| FlagOp::RCR { dst, .. }
| FlagOp::ROL { dst, .. }
| FlagOp::ROR { dst, .. } => { self.sign_mask & (dst ^ self.res) != 0 }, | FlagOp::ROR { dst, .. } => { self.sign_mask & (dst ^ self.res) != 0 },
FlagOp::SAR { dst, .. } FlagOp::SAR { dst, .. }
| FlagOp::SHL { dst, .. } | FlagOp::SHL { dst, .. }
| FlagOp::SHR { dst, .. } => { 0 != self.sign_mask & (dst ^ self.res) }, | FlagOp::SHR { dst, .. } => { 0 != self.sign_mask & (dst ^ self.res) },
FlagOp::SUB { dst, src, .. } => { 0 != self.sign_mask & // In the (maybe) sign bit... FlagOp::SUB { dst, src, .. } => { 0 != self.sign_mask & // In the (maybe) sign bit...
(src ^ dst) & // ...operands have different signs... (src ^ dst) & // ...operands have different signs...
(dst ^ self.res) }, /* ...and result sign-bit changed */ (dst ^ self.res) }, // ...and result sign-bit changed
} }
} }
@@ -137,8 +163,10 @@ pub enum FlagOp {
DEC { cf: bool }, DEC { cf: bool },
INC { cf: bool }, INC { cf: bool },
POPF, // flags encoded in result POPF, // flags encoded in result
ROL { dst: u16, src: u16, rot_mask: u16, cf: bool, pf: bool, af: bool, zf: bool, sf: bool }, RCL { dst: u16, shamt: u16, bits: u16, old_cf: bool, pf: bool, af: bool, zf: bool, sf: bool },
ROR { dst: u16, src: u16, rot_mask: u16, cf: bool, pf: bool, af: bool, zf: bool, sf: bool }, RCR { dst: u16, shamt: u16, old_cf: bool, pf: bool, af: bool, zf: bool, sf: bool },
ROL { dst: u16, shamt: u16, rot_mask: u16, pf: bool, af: bool, zf: bool, sf: bool },
ROR { dst: u16, shamt: u16, rot_mask: u16, pf: bool, af: bool, zf: bool, sf: bool },
SAR { dst: u16, src: u16, sign_bit: bool }, SAR { dst: u16, src: u16, sign_bit: bool },
SHL { dst: u16, src: u16 }, SHL { dst: u16, src: u16 },
SHR { dst: u16, src: u16 }, SHR { dst: u16, src: u16 },

View File

@@ -524,21 +524,29 @@ impl i8088 {
0xCD => int[cpu, bus, d8] / 71, 0xCD => int[cpu, bus, d8] / 71,
0xD0: { 0x00 => rol[form=byte3, flags, modrm8, const=1u8] / "2/23+", // ROL r/m16, 1 0xD0: { 0x00 => rol[form=byte3, flags, modrm8, const=1u8] / "2/23+", // ROL r/m16, 1
0x08 => ror[form=byte3, flags, modrm8, const=1u8] / "2/23+", // ROR r/m16, 1 0x08 => ror[form=byte3, flags, modrm8, const=1u8] / "2/23+", // ROR r/m16, 1
0x10 => rcl[form=byte3, flags, modrm8, const=1u8] / "2/23+", // RCL r/m16, 1
0x18 => rcr[form=byte3, flags, modrm8, const=1u8] / "2/23+", // RCR r/m16, 1
0x20 => shl[form=byte3, flags, modrm8, const=1u8] / "2/23+", // SHL r/m8, 1 0x20 => shl[form=byte3, flags, modrm8, const=1u8] / "2/23+", // SHL r/m8, 1
0x28 => shr[form=byte3, flags, modrm8, const=1u8] / "2/23+", // SHR r/m8, 1 0x28 => shr[form=byte3, flags, modrm8, const=1u8] / "2/23+", // SHR r/m8, 1
0x38 => sar[form=byte3, flags, modrm8, const=1u8] / "2/23+" }, // SAR r/m8, 1 0x38 => sar[form=byte3, flags, modrm8, const=1u8] / "2/23+" }, // SAR r/m8, 1
0xD1: { 0x00 => rol[form=word3, flags, modrm16, const=1u16] / "2/23+", // ROL r/m16, 1 0xD1: { 0x00 => rol[form=word3, flags, modrm16, const=1u16] / "2/23+", // ROL r/m16, 1
0x08 => ror[form=word3, flags, modrm16, const=1u16] / "2/23+", // ROR r/m16, 1 0x08 => ror[form=word3, flags, modrm16, const=1u16] / "2/23+", // ROR r/m16, 1
0x10 => rcl[form=word3, flags, modrm16, const=1u16] / "2/23+", // RCL r/m16, 1
0x18 => rcr[form=word3, flags, modrm16, const=1u16] / "2/23+", // RCR r/m16, 1
0x20 => shl[form=word3, flags, modrm16, const=1u16] / "2/23+", // SHL r/m16, 1 0x20 => shl[form=word3, flags, modrm16, const=1u16] / "2/23+", // SHL r/m16, 1
0x28 => shr[form=word3, flags, modrm16, const=1u16] / "2/23+", // SHR r/m16, 1 0x28 => shr[form=word3, flags, modrm16, const=1u16] / "2/23+", // SHR r/m16, 1
0x38 => sar[form=word3, flags, modrm16, const=1u16] / "2/23+" }, // SAR r/m16, 1 0x38 => sar[form=word3, flags, modrm16, const=1u16] / "2/23+" }, // SAR r/m16, 1
0xD2: { 0x00 => rol[form=byte3, flags, modrm8, reglo=c] / "8/28++4n", // ROL r/m8, CL 0xD2: { 0x00 => rol[form=byte3, flags, modrm8, reglo=c] / "8/28++4n", // ROL r/m8, CL
0x08 => ror[form=byte3, flags, modrm8, reglo=c] / "8/28++4n", // ROR r/m8, CL 0x08 => ror[form=byte3, flags, modrm8, reglo=c] / "8/28++4n", // ROR r/m8, CL
0x10 => rcl[form=byte3, flags, modrm8, reglo=c] / "8/28++4n", // RCL r/m8, CL
0x18 => rcr[form=byte3, flags, modrm8, reglo=c] / "8/28++4n", // RCR r/m8, CL
0x20 => shl[form=byte3, flags, modrm8, reglo=c] / "2/23+", // SHL r/m8, CL 0x20 => shl[form=byte3, flags, modrm8, reglo=c] / "2/23+", // SHL r/m8, CL
0x28 => shr[form=byte3, flags, modrm8, reglo=c] / "2/23+", // SHR r/m8, CL 0x28 => shr[form=byte3, flags, modrm8, reglo=c] / "2/23+", // SHR r/m8, CL
0x38 => sar[form=byte3, flags, modrm8, reglo=c] / "2/23+" }, // SAR r/m8, CL 0x38 => sar[form=byte3, flags, modrm8, reglo=c] / "2/23+" }, // SAR r/m8, CL
0xD3: { 0x00 => rol[form=word3, flags, modrm16, reglo=c] / "8/28++4n", // ROL r/m16, CL 0xD3: { 0x00 => rol[form=word3, flags, modrm16, reglo=c] / "8/28++4n", // ROL r/m16, CL
0x08 => ror[form=word3, flags, modrm16, reglo=c] / "8/28++4n", // ROR r/m16, CL 0x08 => ror[form=word3, flags, modrm16, reglo=c] / "8/28++4n", // ROR r/m16, CL
0x10 => rcl[form=word3, flags, modrm16, reglo=c] / "8/28++4n", // RCL r/m16, CL
0x18 => rcr[form=word3, flags, modrm16, reglo=c] / "8/28++4n", // RCR r/m16, CL
0x20 => shl[form=word3, flags, modrm16, reglo=c] / "2/23+", // SHL r/m16, CL 0x20 => shl[form=word3, flags, modrm16, reglo=c] / "2/23+", // SHL r/m16, CL
0x28 => shr[form=word3, flags, modrm16, reglo=c] / "2/23+", // SHR r/m16, CL 0x28 => shr[form=word3, flags, modrm16, reglo=c] / "2/23+", // SHR r/m16, CL
0x38 => sar[form=word3, flags, modrm16, reglo=c] / "2/23+" }, // SAR r/m16, CL 0x38 => sar[form=word3, flags, modrm16, reglo=c] / "2/23+" }, // SAR r/m16, CL

View File

@@ -8,6 +8,7 @@ use emu::pc::Bus;
pub trait Operand: Copy + 'static + pub trait Operand: Copy + 'static +
std::convert::Into<u16> + std::convert::Into<u16> +
std::fmt::Display +
nt::int::PrimInt + nt::int::PrimInt +
nt::ops::checked::CheckedShl<Output = Self> + nt::ops::checked::CheckedShl<Output = Self> +
nt::ops::checked::CheckedShr<Output = Self> + nt::ops::checked::CheckedShr<Output = Self> +
@@ -19,7 +20,8 @@ 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 ROTATE_MASK: u16 = (std::mem::size_of::<Self>() * 8 - 1) as _; const BITS: u16 = std::mem::size_of::<Self>() as u16 * 8;
const ROTATE_MASK: u16 = Self::BITS - 1;
fn hi_bit(self) -> bool; fn hi_bit(self) -> bool;
fn as_signed(self) -> Self::Signed; fn as_signed(self) -> Self::Signed;

View File

@@ -354,6 +354,87 @@ pub fn push_modrm<'a>(ss: u16, sp: Reg, src: DynLValue<'a>, bus: &mut Bus) {
} }
} }
pub fn rcl<T, U, LVal, RVal>(flags: &mut Flags,
mut dst: LVal,
src: RVal)
where T: Operand + Into<u32>,
U: Operand + AsPrimitive<u32>,
LVal: LValue<T>,
RVal: RValue<U>,
u32: AsPrimitive<T>
{
// Rust shifts overflow if shamt >= bit-size of the
// operand. Unfortunately, because we're looping-in the carry flag,
// our operand size is effectively 1 bit larger and so we might want
// a shift as large as the base operand size. We'll use u32 instead
// so that we always have adequate range on shifting operations.
//
// TODO-ish: Rust doesn't have static asserts, but it'd be nice to
// verify that u32 is larger than our operand. This technique is
// also used in the CF flag calculation.
let dst_before: u32 = dst.read().into();
let src_before = src.read().into(); // may alias dst
let shamt = src_before % (T::BITS + 1); // carry-flag effectively enlarges operand 1 bit
let res;
if shamt == 0 {
res = dst_before;
dst.write(res.as_()); // dst may be volatile
if src_before == 0 { return } // No flags update
} else {
res = dst_before << shamt
| (flags.cf() as u32) << shamt - 1
| dst_before >> T::BITS + 1 - shamt;
dst.write(res.as_());
}
flags.update(FlagOp::RCL { dst: dst_before as u16,
shamt: shamt,
bits: T::BITS,
old_cf: flags.cf(),
pf: flags.pf(),
af: flags.af(),
zf: flags.zf(),
sf: flags.sf() },
res as u16,
T::HI_BIT_MASK.into());
}
pub fn rcr<T, U, LVal, RVal>(flags: &mut Flags,
mut dst: LVal,
src: RVal)
where T: Operand + Into<u32>,
U: Operand + AsPrimitive<u32>,
LVal: LValue<T>,
RVal: RValue<U>,
u32: AsPrimitive<T>
{
// See comments on rcl().
let dst_before:u32 = dst.read().into();
let src_before = src.read().into(); // may alias dst
let shamt = src_before % (T::BITS + 1); // carry-flag effectively enlarges operand 1 bit
let res;
if shamt == 0 {
res = dst_before;
dst.write(res.as_()); // dst may be volatile
if src_before == 0 { return } // No flags update
} else {
res = dst_before >> shamt
| (flags.cf() as u32) << T::BITS >> shamt
| dst_before << T::BITS + 1 - shamt;
dst.write(res.as_());
}
flags.update(FlagOp::RCR { dst: dst_before as u16,
shamt: shamt,
old_cf: flags.cf(),
pf: flags.pf(),
af: flags.af(),
zf: flags.zf(),
sf: flags.sf() },
res as u16,
T::HI_BIT_MASK.into());
}
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)
@@ -364,13 +445,15 @@ where T: Operand,
{ {
let dst_before = dst.read(); let dst_before = dst.read();
let src_before = src.read(); // may alias dst let src_before = src.read(); // may alias dst
if src_before == U::zero() { return } // 286 and beyond probably masks shamt before this check? if src_before == U::zero() { // 286 and beyond probably masks shamt before this check?
dst.write(dst_before); // dst may be volatile
return; // No flags update
}
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.update(FlagOp::ROL { dst: dst_before.into(), flags.update(FlagOp::ROL { dst: dst_before.into(),
src: src_before.into(), shamt: src_before.into(),
rot_mask: T::ROTATE_MASK, rot_mask: T::ROTATE_MASK,
cf: flags.cf(),
pf: flags.pf(), pf: flags.pf(),
af: flags.af(), af: flags.af(),
zf: flags.zf(), zf: flags.zf(),
@@ -389,13 +472,15 @@ where T: Operand,
{ {
let dst_before = dst.read(); let dst_before = dst.read();
let src_before = src.read(); // may alias dst let src_before = src.read(); // may alias dst
if src_before == U::zero() { return } // 286 and beyond probably masks shamt before this check? if src_before == U::zero() { // 286 and beyond probably masks shamt before this check?
dst.write(dst_before); // dst may be volatile
return; // No flags update
}
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.update(FlagOp::ROR { dst: dst_before.into(), flags.update(FlagOp::ROR { dst: dst_before.into(),
src: src_before.into(), shamt: src_before.into(),
rot_mask: T::ROTATE_MASK, rot_mask: T::ROTATE_MASK,
cf: flags.cf(),
pf: flags.pf(), pf: flags.pf(),
af: flags.af(), af: flags.af(),
zf: flags.zf(), zf: flags.zf(),
@@ -412,7 +497,10 @@ where T: Operand,
{ {
let dst_before = dst.read(); let dst_before = dst.read();
let src_before = src.read(); // may alias dst let src_before = src.read(); // may alias dst
if src_before == U::zero() { return } if src_before == U::zero() { // 286 and beyond probably masks shamt before this check?
dst.write(dst_before); // dst may be volatile
return; // No flags update
}
// Before the 286, shifts could be up to 255 bits even though the // Before the 286, shifts could be up to 255 bits even though the
// operands themselves could only be 16 bits. The 286 and beyond // operands themselves could only be 16 bits. The 286 and beyond
@@ -462,7 +550,10 @@ where T: Operand,
{ {
let dst_before = dst.read(); let dst_before = dst.read();
let src_before = src.read(); // may alias dst let src_before = src.read(); // may alias dst
if src_before == U::zero() { return } if src_before == U::zero() { // 286 and beyond probably masks shamt before this check?
dst.write(dst_before); // dst may be volatile
return; // No flags update
}
let res = match dst_before.checked_shl(src_before.as_()) { let res = match dst_before.checked_shl(src_before.as_()) {
Some(shifted) => shifted, Some(shifted) => shifted,
None => T::zero() None => T::zero()
@@ -484,7 +575,10 @@ where T: Operand,
{ {
let dst_before = dst.read(); let dst_before = dst.read();
let src_before = src.read(); // may alias dst let src_before = src.read(); // may alias dst
if src_before == U::zero() { return } if src_before == U::zero() { // 286 and beyond probably masks shamt before this check?
dst.write(dst_before); // dst may be volatile
return; // No flags update
}
let res = match dst_before.checked_shr(src_before.as_()) { let res = match dst_before.checked_shr(src_before.as_()) {
Some(shifted) => shifted, Some(shifted) => shifted,
None => T::zero() None => T::zero()