emu: RCL/RCR ops.
I even did some testing this time...
This commit is contained in:
@@ -7,13 +7,13 @@ authors = ["Jared Burce <jaredr@gmail.com>"]
|
||||
byteorder = "1.4.2"
|
||||
env_logger = "0.3"
|
||||
itertools = ">=0.4"
|
||||
log = "0.3"
|
||||
memmap = "~0.2"
|
||||
gl = "0.10"
|
||||
gfx = "0.17"
|
||||
gfx_device_gl = "0.15"
|
||||
image = "0.19"
|
||||
log = "0.3"
|
||||
lzw = "0.10"
|
||||
memmap = "~0.2"
|
||||
nalgebra = "0.16"
|
||||
num-traits = "0.2.14"
|
||||
openvr = "0.6.0"
|
||||
|
||||
@@ -39,11 +39,27 @@ impl Flags {
|
||||
FlagOp::DEC { cf } => cf,
|
||||
FlagOp::INC { cf } => cf,
|
||||
FlagOp::POPF => { self.res & 1 << CF_BIT != 0 },
|
||||
FlagOp::ROL { dst, src, rot_mask, .. } => {
|
||||
dst & (self.sign_mask >> ((src.wrapping_sub(1)) & rot_mask as u16)) != 0
|
||||
FlagOp::RCL { dst, shamt, bits, old_cf, .. } => {
|
||||
// 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, .. } => {
|
||||
dst & (1 << ((src.wrapping_sub(1)) & rot_mask as u16)) != 0
|
||||
FlagOp::RCR { dst, shamt, old_cf, .. } => {
|
||||
// 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 } => {
|
||||
match 1u16.checked_shl(src as u32 - 1) {
|
||||
@@ -71,7 +87,9 @@ impl Flags {
|
||||
match self.flag_op {
|
||||
FlagOp::Eager { pf, .. } => pf,
|
||||
FlagOp::POPF => { self.res & 1 << PF_BIT != 0 },
|
||||
FlagOp::ROL { pf, .. }
|
||||
FlagOp::RCL { pf, .. }
|
||||
| FlagOp::RCR { pf, .. }
|
||||
| FlagOp::ROL { pf, .. }
|
||||
| FlagOp::ROR { pf, .. } => pf,
|
||||
_ => { self.res.count_ones() & 1 == 0 },
|
||||
}
|
||||
@@ -81,7 +99,9 @@ impl Flags {
|
||||
match self.flag_op {
|
||||
FlagOp::Eager { af, .. } => af,
|
||||
FlagOp::POPF => { self.res & 1 << AF_BIT != 0 },
|
||||
FlagOp::ROL { af, .. }
|
||||
FlagOp::RCL { af, .. }
|
||||
| FlagOp::RCR { af, .. }
|
||||
| FlagOp::ROL { af, .. }
|
||||
| FlagOp::ROR { af, .. } => af,
|
||||
_ => { false /* XXX: unimplemented! */ },
|
||||
}
|
||||
@@ -91,7 +111,9 @@ impl Flags {
|
||||
match self.flag_op {
|
||||
FlagOp::Eager { zf, .. } => zf,
|
||||
FlagOp::POPF => { self.res & 1 << ZF_BIT != 0 },
|
||||
FlagOp::ROL { zf, .. }
|
||||
FlagOp::RCL { zf, .. }
|
||||
| FlagOp::RCR { zf, .. }
|
||||
| FlagOp::ROL { zf, .. }
|
||||
| FlagOp::ROR { zf, .. } => zf,
|
||||
_ => { self.res == 0 },
|
||||
}
|
||||
@@ -101,7 +123,9 @@ impl Flags {
|
||||
match self.flag_op {
|
||||
FlagOp::Eager { sf, .. } => sf,
|
||||
FlagOp::POPF => { self.res & 1 << SF_BIT != 0 },
|
||||
FlagOp::ROL { sf, .. }
|
||||
FlagOp::RCL { sf, .. }
|
||||
| FlagOp::RCR { sf, .. }
|
||||
| FlagOp::ROL { sf, .. }
|
||||
| FlagOp::ROR { sf, .. } => sf,
|
||||
_ => { self.res & self.sign_mask != 0 },
|
||||
}
|
||||
@@ -110,17 +134,19 @@ impl Flags {
|
||||
pub fn of(&self) -> bool {
|
||||
match self.flag_op {
|
||||
FlagOp::Eager { of, .. } => of,
|
||||
FlagOp::POPF => { self.res & 1 << OF_BIT != 0 },
|
||||
FlagOp::DEC { .. } => { self.res == self.sign_mask - 1 },
|
||||
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::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 */
|
||||
(dst ^ self.res) }, // ...and result sign-bit changed
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,8 +163,10 @@ pub enum FlagOp {
|
||||
DEC { cf: bool },
|
||||
INC { cf: bool },
|
||||
POPF, // flags encoded in result
|
||||
ROL { dst: u16, src: u16, rot_mask: u16, 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 },
|
||||
RCL { dst: u16, shamt: u16, bits: u16, old_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 },
|
||||
SHL { dst: u16, src: u16 },
|
||||
SHR { dst: u16, src: u16 },
|
||||
|
||||
@@ -524,21 +524,29 @@ impl i8088 {
|
||||
0xCD => int[cpu, bus, d8] / 71,
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
|
||||
@@ -8,6 +8,7 @@ use emu::pc::Bus;
|
||||
|
||||
pub trait Operand: Copy + 'static +
|
||||
std::convert::Into<u16> +
|
||||
std::fmt::Display +
|
||||
nt::int::PrimInt +
|
||||
nt::ops::checked::CheckedShl<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>;
|
||||
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 as_signed(self) -> Self::Signed;
|
||||
|
||||
@@ -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,
|
||||
mut dst: LVal,
|
||||
src: RVal)
|
||||
@@ -364,13 +445,15 @@ where T: Operand,
|
||||
{
|
||||
let dst_before = dst.read();
|
||||
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_());
|
||||
dst.write(res);
|
||||
flags.update(FlagOp::ROL { dst: dst_before.into(),
|
||||
src: src_before.into(),
|
||||
shamt: src_before.into(),
|
||||
rot_mask: T::ROTATE_MASK,
|
||||
cf: flags.cf(),
|
||||
pf: flags.pf(),
|
||||
af: flags.af(),
|
||||
zf: flags.zf(),
|
||||
@@ -389,13 +472,15 @@ where T: Operand,
|
||||
{
|
||||
let dst_before = dst.read();
|
||||
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_());
|
||||
dst.write(res);
|
||||
flags.update(FlagOp::ROR { dst: dst_before.into(),
|
||||
src: src_before.into(),
|
||||
shamt: src_before.into(),
|
||||
rot_mask: T::ROTATE_MASK,
|
||||
cf: flags.cf(),
|
||||
pf: flags.pf(),
|
||||
af: flags.af(),
|
||||
zf: flags.zf(),
|
||||
@@ -412,7 +497,10 @@ where T: Operand,
|
||||
{
|
||||
let dst_before = dst.read();
|
||||
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
|
||||
// operands themselves could only be 16 bits. The 286 and beyond
|
||||
@@ -462,7 +550,10 @@ where T: Operand,
|
||||
{
|
||||
let dst_before = dst.read();
|
||||
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_()) {
|
||||
Some(shifted) => shifted,
|
||||
None => T::zero()
|
||||
@@ -484,7 +575,10 @@ where T: Operand,
|
||||
{
|
||||
let dst_before = dst.read();
|
||||
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_()) {
|
||||
Some(shifted) => shifted,
|
||||
None => T::zero()
|
||||
|
||||
Reference in New Issue
Block a user