From 4833b4407421e54c8c0cd1d52dca797f382831e6 Mon Sep 17 00:00:00 2001 From: Jared Burce Date: Tue, 6 Apr 2021 00:53:58 -0700 Subject: [PATCH] emu: RCL/RCR ops. I even did some testing this time... --- Cargo.toml | 4 +- src/emu/flags.rs | 54 +++++++++++++++----- src/emu/i8088.rs | 8 +++ src/emu/operands.rs | 4 +- src/emu/operations.rs | 112 ++++++++++++++++++++++++++++++++++++++---- 5 files changed, 157 insertions(+), 25 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 27b1493..15f2e2d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,13 +7,13 @@ authors = ["Jared Burce "] 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" diff --git a/src/emu/flags.rs b/src/emu/flags.rs index 5a61cf0..599ddf6 100644 --- a/src/emu/flags.rs +++ b/src/emu/flags.rs @@ -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 }, diff --git a/src/emu/i8088.rs b/src/emu/i8088.rs index 09e2c4f..32ef010 100644 --- a/src/emu/i8088.rs +++ b/src/emu/i8088.rs @@ -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 diff --git a/src/emu/operands.rs b/src/emu/operands.rs index e12227c..62df980 100644 --- a/src/emu/operands.rs +++ b/src/emu/operands.rs @@ -8,6 +8,7 @@ use emu::pc::Bus; pub trait Operand: Copy + 'static + std::convert::Into + + std::fmt::Display + nt::int::PrimInt + nt::ops::checked::CheckedShl + nt::ops::checked::CheckedShr + @@ -19,7 +20,8 @@ pub trait Operand: Copy + 'static + { type Signed: nt::ops::checked::CheckedShr; const HI_BIT_MASK: Self; - const ROTATE_MASK: u16 = (std::mem::size_of::() * 8 - 1) as _; + const BITS: u16 = std::mem::size_of::() as u16 * 8; + const ROTATE_MASK: u16 = Self::BITS - 1; fn hi_bit(self) -> bool; fn as_signed(self) -> Self::Signed; diff --git a/src/emu/operations.rs b/src/emu/operations.rs index 894f2e4..01eda0a 100644 --- a/src/emu/operations.rs +++ b/src/emu/operations.rs @@ -354,6 +354,87 @@ pub fn push_modrm<'a>(ss: u16, sp: Reg, src: DynLValue<'a>, bus: &mut Bus) { } } +pub fn rcl(flags: &mut Flags, + mut dst: LVal, + src: RVal) +where T: Operand + Into, + U: Operand + AsPrimitive, + LVal: LValue, + RVal: RValue, + u32: AsPrimitive +{ + // 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(flags: &mut Flags, + mut dst: LVal, + src: RVal) +where T: Operand + Into, + U: Operand + AsPrimitive, + LVal: LValue, + RVal: RValue, + u32: AsPrimitive +{ + // 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(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()