emu: SHL/SHR/SAR opcodes (BARELY TESTED!), const Op Directive
Carry Flag as yet untested and likely to contain bugs
This commit is contained in:
@@ -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,
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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<u16> +
|
||||
nt::Num +
|
||||
nt::Bounded +
|
||||
nt::ops::checked::CheckedShl<Output = Self> +
|
||||
nt::ops::checked::CheckedShr<Output = Self> +
|
||||
nt::ops::overflowing::OverflowingAdd +
|
||||
nt::ops::overflowing::OverflowingSub +
|
||||
nt::ops::wrapping::WrappingAdd +
|
||||
nt::ops::wrapping::WrappingSub +
|
||||
std::ops::BitAnd<Output = Self> +
|
||||
std::ops::BitXor<Output = Self> {
|
||||
std::ops::BitXor<Output = Self>
|
||||
{
|
||||
type Signed: nt::ops::checked::CheckedShr<Output = Self::Signed>;
|
||||
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<T>: RValue<T> {
|
||||
|
||||
@@ -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<Dy
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sar<T, U, LVal, RVal>(flags: &mut Flags, mut dst: LVal, src: RVal)
|
||||
where T: Operand,
|
||||
U: Operand + AsPrimitive<u32>,
|
||||
LVal: LValue<T>,
|
||||
RVal: RValue<U>
|
||||
{
|
||||
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<T, U, LVal, RVal>(flags: &mut Flags,
|
||||
mut dst: LVal,
|
||||
src: RVal)
|
||||
where T: Operand,
|
||||
U: Operand + AsPrimitive<u32>,
|
||||
LVal: LValue<T>,
|
||||
RVal: RValue<U>
|
||||
{
|
||||
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<T, U, LVal, RVal>(flags: &mut Flags,
|
||||
mut dst: LVal,
|
||||
src: RVal)
|
||||
where T: Operand,
|
||||
U: Operand + AsPrimitive<u32>,
|
||||
LVal: LValue<T>,
|
||||
RVal: RValue<U>
|
||||
{
|
||||
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,
|
||||
|
||||
Reference in New Issue
Block a user