From a183a5ad38e2d68f06af368898b4f579351e6cdd Mon Sep 17 00:00:00 2001 From: Jared Burce Date: Sun, 28 Mar 2021 02:08:23 -0700 Subject: [PATCH] emu: SHL/SHR/SAR opcodes (BARELY TESTED!), const Op Directive Carry Flag as yet untested and likely to contain bugs --- src/emu/flags.rs | 2 ++ src/emu/i8088.rs | 36 +++++++++++++++---- src/emu/operands.rs | 42 +++++++++++++++++++++-- src/emu/operations.rs | 80 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 152 insertions(+), 8 deletions(-) diff --git a/src/emu/flags.rs b/src/emu/flags.rs index 8578a7e..508b559 100644 --- a/src/emu/flags.rs +++ b/src/emu/flags.rs @@ -70,6 +70,7 @@ impl Flags { 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::SHIFT => { 0 != self.sign_bit_mask & (self.dst ^ self.res) }, 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 */ }, @@ -93,6 +94,7 @@ pub enum FlagOp { Eager, // precomputed into result, for e.g. POPF? (Anything else?) DEC, INC, + SHIFT, SUB, } diff --git a/src/emu/i8088.rs b/src/emu/i8088.rs index 52014d5..91fd1d5 100644 --- a/src/emu/i8088.rs +++ b/src/emu/i8088.rs @@ -85,7 +85,7 @@ macro_rules! step { step!(@code form=$form, $done, $cpu, $bus, $prefix, $modrm, $name, $cycles, $rest) }; - // Argument Decoders + // === OP DIRECTIVES === (@addr $cookie:tt, $cpu:expr, $bus:expr, ($segment:ident, $repeat:ident, $prefix_loop:lifetime), @@ -137,6 +137,10 @@ macro_rules! step { ) }; + (@const=$val:literal $cookie:tt, $cpu:expr, $bus:expr, $prefix:tt, $modrm:tt) => { + step!(@arg $cookie, $val) + }; + (@cpu $cookie:tt, $cpu:expr, $bus:expr, $prefix:tt, $modrm:tt) => { step!(@arg $cookie, $cpu) }; @@ -186,6 +190,14 @@ macro_rules! step { step!(@arg $cookie, &mut $cpu.flags) }; + (@form=ternary_byte $cookie:tt, $cpu:expr, $bus:expr, $prefix:tt, $modrm:tt) => { + step!(@form $cookie, form=(u8, _, _, _)); + }; + + (@form=ternary_word $cookie:tt, $cpu:expr, $bus:expr, $prefix:tt, $modrm:tt) => { + step!(@form $cookie, form=(u16, _, _, _)); + }; + (@form=unary_byte $cookie:tt, $cpu:expr, $bus:expr, $prefix:tt, $modrm:tt) => { step!(@form $cookie, form=(u8, _)); }; @@ -512,17 +524,29 @@ impl i8088 { 0xBF => mov[reg=di, d16] / 4, 0xC3 => pop[bus, regval=ss, reg=sp, reg=ip] / 20, // RET 0xCD => int[cpu, bus, d8] / 71, + 0xD0: { 0x20 => shl[form=ternary_byte, flags, modrm8, const=1u8] / "2/23+", // SHL r/m8, 1 + 0x28 => shr[form=ternary_byte, flags, modrm8, const=1u8] / "2/23+", // SHR r/m8, 1 + 0x38 => sar[form=ternary_byte, flags, modrm8, const=1u8] / "2/23+" }, // SAR r/m8, 1 + 0xD1: { 0x20 => shl[form=ternary_word, flags, modrm16, const=1u16] / "2/23+", // SHL r/m16, 1 + 0x28 => shr[form=ternary_word, flags, modrm16, const=1u16] / "2/23+", // SHR r/m16, 1 + 0x38 => sar[form=ternary_word, flags, modrm16, const=1u16] / "2/23+" }, // SAR r/m16, 1 + 0xD2: { 0x20 => shl[form=ternary_byte, flags, modrm8, reglo=c] / "2/23+", // SHL r/m8, CL + 0x28 => shr[form=ternary_byte, flags, modrm8, reglo=c] / "2/23+", // SHR r/m8, CL + 0x38 => sar[form=ternary_byte, flags, modrm8, reglo=c] / "2/23+" }, // SAR r/m8, CL + 0xD3: { 0x20 => shl[form=ternary_word, flags, modrm16, reglo=c] / "2/23+", // SHL r/m16, CL + 0x28 => shr[form=ternary_word, flags, modrm16, reglo=c] / "2/23+", // SHR r/m16, CL + 0x38 => sar[form=ternary_word, flags, modrm16, reglo=c] / "2/23+" }, // SAR r/m16, CL 0xE3 => jcxz[reg=ip, reg=c, rel8] / "18/6", // JCXZ rel8 0xE9 => jmp[reg=ip, rel16] / 15, // JMP rel16 0xEB => jmp[reg=ip, rel8] / 15, // JMP rel8 0xE8 => call[reg=ip, bus, regval=ss, reg=sp, rel16] / 23, 0xF2 => nop[rep=NotEqual, prefix] / 0, // REPNE/REPNZ 0xF3 => nop[rep=Equal, prefix] / 0, // REP/REPE/REPZ - 0xFE: { 00 => inc[form=unary_byte, flags, modrm8] / "3/23+", // INC r/m8 - 08 => dec[form=unary_byte, flags, modrm8] / "3/23+", }, // DEC r/m8 - 0xFF: { 00 => inc[form=unary_word, flags, modrm16] / "3/23+", // INC r/m16 - 08 => dec[form=unary_word, flags, modrm16] / "3/23+", // DEC r/m16 - 30 => push_modrm[unsafe_bus_maybe, regval=ss, reg=sp, modrm16] / "15/24+" }, + 0xFE: { 0x00 => inc[form=unary_byte, flags, modrm8] / "3/23+", // INC r/m8 + 0x08 => dec[form=unary_byte, flags, modrm8] / "3/23+", }, // DEC r/m8 + 0xFF: { 0x00 => inc[form=unary_word, flags, modrm16] / "3/23+", // INC r/m16 + 0x08 => dec[form=unary_word, flags, modrm16] / "3/23+", // DEC r/m16 + 0x30 => push_modrm[unsafe_bus_maybe, regval=ss, reg=sp, modrm16] / "15/24+" }, }, modrm: { 0x00 => seg=ds, displace0=(b+si) / M 7, diff --git a/src/emu/operands.rs b/src/emu/operands.rs index db36f4c..e793e02 100644 --- a/src/emu/operands.rs +++ b/src/emu/operands.rs @@ -6,16 +6,26 @@ use emu::num_traits as nt; use emu::util::{read_hi, read_lo, segoff_to_addr, write_hi, write_lo}; use emu::pc::Bus; -pub trait Operand: Copy + +pub trait Operand: Copy + 'static + std::convert::Into + nt::Num + + nt::Bounded + + nt::ops::checked::CheckedShl + + nt::ops::checked::CheckedShr + nt::ops::overflowing::OverflowingAdd + nt::ops::overflowing::OverflowingSub + nt::ops::wrapping::WrappingAdd + nt::ops::wrapping::WrappingSub + std::ops::BitAnd + - std::ops::BitXor { + std::ops::BitXor +{ + type Signed: nt::ops::checked::CheckedShr; const WIDTH: OperandWidth; + const HI_BIT_MASK: Self; + + fn hi_bit(self) -> bool; + fn as_signed(self) -> Self::Signed; + fn from_signed(src: Self::Signed) -> Self; } pub enum OperandWidth { @@ -24,11 +34,39 @@ pub enum OperandWidth { } impl Operand for u8 { + type Signed = i8; const WIDTH: OperandWidth = OperandWidth::Byte; + const HI_BIT_MASK: u8 = 0x80; + + fn hi_bit(self) -> bool { + self >> 7 == 1 + } + + fn as_signed(self) -> Self::Signed { + self as Self::Signed + } + + fn from_signed(src: Self::Signed) -> Self { + src as Self + } } impl Operand for u16 { + type Signed = i16; const WIDTH: OperandWidth = OperandWidth::Word; + const HI_BIT_MASK: u16 = 0x8000; + + fn hi_bit(self) -> bool { + self >> 15 == 1 + } + + fn as_signed(self) -> Self::Signed { + self as Self::Signed + } + + fn from_signed(src: Self::Signed) -> Self { + src as Self + } } pub trait LValue: RValue { diff --git a/src/emu/operations.rs b/src/emu/operations.rs index 1a0fd12..fa47f48 100644 --- a/src/emu/operations.rs +++ b/src/emu/operations.rs @@ -1,6 +1,9 @@ use std::convert::Into; use std::fmt::Debug; +use emu::num_traits::AsPrimitive; +use emu::num_traits::CheckedShr; + use emu::dos; use emu::flags::{FlagOp, Flags}; use emu::i8088::{RepPrefix, i8088}; @@ -373,6 +376,37 @@ pub fn push_modrm<'a>(bus: Option<&mut Bus>, ss: u16, sp: Reg, src: impl Into(flags: &mut Flags, mut dst: LVal, src: RVal) +where T: Operand, + U: Operand + AsPrimitive, + LVal: LValue, + RVal: RValue +{ + let dst_before = dst.read(); + let src_before = src.read(); // may alias dst + if src_before == U::zero() { return } + + // Before the 286, shifts could be up to 255 bits even though the + // operands themselves could only be 16 bits. The 286 and beyond + // masked the shamt down to 5 bits, but does so regardless of target + // operand size so the result is kinda weird. Since nobody seemed to + // care about this change back in the day, it's tempting for us not + // to care either, but we already need to do work to calculate CF + // (the last bit shifted off), and figuring out what that should be + // with a 5-bit shamt mask doesn't actually sound easier. + let sign_bit = dst_before.hi_bit(); + let res = match dst_before.as_signed().checked_shr(src_before.as_()) { + Some(shifted) => T::from_signed(shifted), + None => if sign_bit { T::max_value() } else { T::zero() } // Out-of-Range yields all sign bits + }; + dst.write(res); + flags.cf = match T::one().checked_shl(src_before.as_() - 1) { + Some(carrymask) => dst_before & carrymask != T::zero(), + None => sign_bit + }; + flags.update(FlagOp::SHIFT, dst_before.into(), src_before.into(), res.into(), T::WIDTH); +} + pub fn scasb(flags: &mut Flags, bus: &mut Bus, rep: RepPrefix, @@ -401,6 +435,52 @@ pub fn scasw(flags: &mut Flags, }); } +// See comments on sar() +pub fn shl(flags: &mut Flags, + mut dst: LVal, + src: RVal) +where T: Operand, + U: Operand + AsPrimitive, + LVal: LValue, + RVal: RValue +{ + let dst_before = dst.read(); + let src_before = src.read(); // may alias dst + let res = match dst_before.checked_shl(src_before.as_()) { + Some(shifted) => shifted, + None => T::zero() + }; + dst.write(res); + flags.cf = match T::HI_BIT_MASK.checked_shr(src_before.as_() - 1) { + Some(carrymask) => dst_before & carrymask != T::zero(), + None => false + }; + flags.update(FlagOp::SHIFT, dst_before.into(), src_before.into(), res.into(), T::WIDTH); +} + +// See comments on sar() +pub fn shr(flags: &mut Flags, + mut dst: LVal, + src: RVal) +where T: Operand, + U: Operand + AsPrimitive, + LVal: LValue, + RVal: RValue +{ + let dst_before = dst.read(); + let src_before = src.read(); // may alias dst + let res = match dst_before.checked_shr(src_before.as_()) { + Some(shifted) => shifted, + None => T::zero() + }; + dst.write(res); + flags.cf = match T::one().checked_shl(src_before.as_() - 1) { + Some(carrymask) => dst_before & carrymask != T::zero(), + None => false + }; + flags.update(FlagOp::SHIFT, dst_before.into(), src_before.into(), res.into(), T::WIDTH); +} + pub fn stosb(flags: &Flags, bus: &mut Bus, rep: RepPrefix,