emu: UNTESTED string ops, CMP instruction
Likely to be a bugfest. There's a flags macro for now but unclear how useful it will turn out to be in practice. Flags in particular need testing. Also disappointing that I couldn't make instructions generic across u8/u16, especially for ordinary, non-string CMP. Need to look into this more to see if it can be done with num-traits or some other trickery.
This commit is contained in:
114
src/emu/i8088.rs
114
src/emu/i8088.rs
@@ -38,21 +38,28 @@ pub struct i8088 {
|
||||
|
||||
#[derive(Clone, Copy, Default)]
|
||||
pub struct Flags {
|
||||
cf: bool, // 0: Carry Flag: 1=CY(Carry), 0=NC(No Carry)
|
||||
pub cf: bool, // 0: Carry Flag: 1=CY(Carry), 0=NC(No Carry)
|
||||
// 1: Reserved
|
||||
pf: bool, // 2: Parity Flag: 1=PE(Even), 0=PO(Odd)
|
||||
pub pf: bool, // 2: Parity Flag: 1=PE(Even), 0=PO(Odd)
|
||||
// 3: Reserved
|
||||
af: bool, // 4: Adjust Flag: 1=AC(Aux Carry), 0=NA(No Aux Carry)
|
||||
pub af: bool, // 4: Adjust Flag: 1=AC(Aux Carry), 0=NA(No Aux Carry)
|
||||
// 5: Reserved
|
||||
zf: bool, // 6: Zero Flag: 1=ZR(Zero), 0=NZ(Not Zero)
|
||||
sf: bool, // 7: Sign Flag: 1=NG(Negative), 0=PL(Positive)
|
||||
tf: bool, // 8: Trap Flag
|
||||
ie: bool, // 9: (Real name "IF") Interrupt Enable: 1=EI(Enable Interrupt), 0=DI(Disable Interrupt)
|
||||
df: bool, // 10: Direction Flag: 1=DN(Down), 0=UP(Up)
|
||||
of: bool, // 11: Overflow Flag: 1=OV(Overflow), 0=NV(Not Overflow)
|
||||
pub zf: bool, // 6: Zero Flag: 1=ZR(Zero), 0=NZ(Not Zero)
|
||||
pub sf: bool, // 7: Sign Flag: 1=NG(Negative), 0=PL(Positive)
|
||||
pub tf: bool, // 8: Trap Flag
|
||||
pub ie: bool, // 9: (Real name "IF") Interrupt Enable: 1=EI(Enable Interrupt), 0=DI(Disable Interrupt)
|
||||
pub df: bool, // 10: Direction Flag: 1=DN(Down), 0=UP(Up)
|
||||
pub of: bool, // 11: Overflow Flag: 1=OV(Overflow), 0=NV(Not Overflow)
|
||||
// bits 12-15 always 1
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub enum RepPrefix {
|
||||
None, // No Prefix
|
||||
Equal, // REP/REPE/REPZ
|
||||
NotEqual // REPNE/REPNZ
|
||||
}
|
||||
|
||||
macro_rules! step {
|
||||
// Base case: all args processed and ready to call op
|
||||
(@code ( $($done:tt)* ),
|
||||
@@ -85,7 +92,9 @@ macro_rules! step {
|
||||
};
|
||||
// Argument Decoders
|
||||
|
||||
(@addr $cookie:tt, $cpu:expr, $bus:expr, ($segment:ident, $prefix_loop:lifetime), $modrm:tt) => { {
|
||||
(@addr $cookie:tt, $cpu:expr, $bus:expr,
|
||||
($segment:ident, $repeat:ident, $prefix_loop:lifetime),
|
||||
$modrm:tt) => { {
|
||||
let a16 = i8088::next_ip16($cpu.cs.get(), &mut $cpu.ip, $bus);
|
||||
step!(@arg $cookie, &mut Addr { bus: $bus, segment: $segment.unwrap(), offset: a16 } )
|
||||
} };
|
||||
@@ -108,26 +117,36 @@ macro_rules! step {
|
||||
step!(@arg $cookie, &d16)
|
||||
} };
|
||||
|
||||
(@displace0=($reg1:ident $(+ $reg2:ident)? ) $cookie:tt, $cpu:expr, $bus:expr, ($segment:ident, $prefix_loop:lifetime), $modrm:tt) => {
|
||||
(@displace0=($reg1:ident $(+ $reg2:ident)? ) $cookie:tt, $cpu:expr, $bus:expr,
|
||||
($segment:ident, $repeat:ident, $prefix_loop:lifetime),
|
||||
$modrm:tt) => {
|
||||
step!(@arg $cookie, &mut Addr { bus: $bus,
|
||||
segment: $segment.unwrap(),
|
||||
offset: $cpu.$reg1.get() $(+ $cpu.$reg2.get())? })
|
||||
};
|
||||
|
||||
(@displace8=($reg1:ident $(+ $reg2:ident)? ) $cookie:tt, $cpu:expr, $bus:expr, ($segment:ident, $prefix_loop:lifetime), $modrm:tt) => { {
|
||||
(@displace8=($reg1:ident $(+ $reg2:ident)? ) $cookie:tt, $cpu:expr, $bus:expr,
|
||||
($segment:ident, $repeat:ident, $prefix_loop:lifetime),
|
||||
$modrm:tt) => { {
|
||||
let d8 = i8088::next_ip($cpu.cs.get(), &mut $cpu.ip, $bus);
|
||||
step!(@arg $cookie, &mut Addr { bus: $bus,
|
||||
segment: $segment.unwrap(),
|
||||
offset: $cpu.$reg1.get() $(+ $cpu.$reg2.get())? + d8 as u16 })
|
||||
} };
|
||||
|
||||
(@displace16=($reg1:ident $(+ $reg2:ident)? ) $cookie:tt, $cpu:expr, $bus:expr, ($segment:ident, $prefix_loop:lifetime), $modrm:tt) => { {
|
||||
(@displace16=($reg1:ident $(+ $reg2:ident)? ) $cookie:tt, $cpu:expr, $bus:expr,
|
||||
($segment:ident, $repeat:ident, $prefix_loop:lifetime),
|
||||
$modrm:tt) => { {
|
||||
let d16 = i8088::next_ip16($cpu.cs.get(), &mut $cpu.ip, $bus);
|
||||
step!(@arg $cookie, &mut Addr { bus: $bus,
|
||||
segment: $segment.unwrap(),
|
||||
offset: $cpu.$reg1.get() $(+ $cpu.$reg2.get())? + d16 })
|
||||
} };
|
||||
|
||||
(@flags $cookie:tt, $cpu:expr, $bus:expr, $prefix:tt, $modrm:tt) => {
|
||||
step!(@arg $cookie, &mut $cpu.flags)
|
||||
};
|
||||
|
||||
(@mem $cookie:tt, $cpu:expr, $bus:expr, $prefix:tt, $modrm:tt) => {
|
||||
step!(@arg $cookie, &mut $bus.ram)
|
||||
};
|
||||
@@ -173,7 +192,11 @@ macro_rules! step {
|
||||
}
|
||||
} };
|
||||
|
||||
(@prefix $cookie:tt, $cpu:expr, $bus:expr, ($segment:ident, $prefix_loop:lifetime), $modrm:tt) => { continue $prefix_loop; };
|
||||
(@prefix $cookie:tt, $cpu:expr, $bus:expr,
|
||||
($segment:ident, $repeat:ident, $prefix_loop:lifetime),
|
||||
$modrm:tt) => {
|
||||
continue $prefix_loop;
|
||||
};
|
||||
|
||||
(@r16 $cookie:tt, $cpu:expr, $bus:expr, $prefix:tt, ($modrm_val:ident, $modrm:tt, $modrm16:tt, $modrm8:tt)) => {
|
||||
// TODO: Should these also be passed into the macro like the modrm specs?
|
||||
@@ -228,13 +251,42 @@ macro_rules! step {
|
||||
step!(@arg $cookie, LittleEndian::read_i16(&buf))
|
||||
} };
|
||||
|
||||
(@seg=$seg:ident $cookie:tt, $cpu:expr, $bus:expr, ($segment:ident, $prefix_loop:lifetime), $modrm:tt) => { {
|
||||
(@rep $cookie:tt, $cpu:expr, $bus:expr,
|
||||
($segment:ident, $repeat:ident, $prefix_loop:lifetime),
|
||||
$modrm:tt) => { {
|
||||
step!(@arg $cookie, $repeat)
|
||||
} };
|
||||
|
||||
(@rep=$mode:ident $cookie:tt, $cpu:expr, $bus:expr,
|
||||
($segment:ident, $repeat:ident, $prefix_loop:lifetime),
|
||||
$modrm:tt) => { {
|
||||
$repeat = RepPrefix::$mode;
|
||||
step!(@arg $cookie)
|
||||
} };
|
||||
|
||||
(@rep $cookie:tt, $cpu:expr, $bus:expr,
|
||||
($segment:ident, $repeat:ident, $prefix_loop:lifetime),
|
||||
$modrm:tt) => { {
|
||||
step!(@arg $cookie, $repeat)
|
||||
} };
|
||||
|
||||
(@seg=$seg:ident $cookie:tt, $cpu:expr, $bus:expr,
|
||||
($segment:ident, $repeat:ident, $prefix_loop:lifetime),
|
||||
$modrm:tt) => { {
|
||||
$segment = $segment.or(Some($cpu.$seg.get()));
|
||||
step!(@arg $cookie)
|
||||
} };
|
||||
|
||||
(@seg $cookie:tt, $cpu:expr, $bus:expr,
|
||||
($segment:ident, $repeat:ident, $prefix_loop:lifetime),
|
||||
$modrm:tt) => { {
|
||||
step!(@arg $cookie, $segment.unwrap())
|
||||
} };
|
||||
|
||||
// Group Decoder
|
||||
(@group $cpu:expr, $bus:expr, $prefix:tt, ($modrm_val:ident, $modrm:tt, $modrm16:tt, $modrm8:tt), $code:literal,
|
||||
(@group $cpu:expr, $bus:expr, $prefix:tt,
|
||||
($modrm_val:ident, $modrm:tt, $modrm16:tt, $modrm8:tt),
|
||||
$code:literal,
|
||||
{ $( $subcode:literal =>
|
||||
$name:ident[$($args:ident $(= $argrhs:tt)?),*] / $cycles:literal),*$(,)? } ) => { {
|
||||
let subcode = $modrm_val & 0x38;
|
||||
@@ -264,6 +316,7 @@ macro_rules! step {
|
||||
) => {
|
||||
{
|
||||
let mut segment = None;
|
||||
let mut repeat = RepPrefix::None;
|
||||
let modrm_val: u8; // Type ascription unnecessary but gives better err messages when missing $ext
|
||||
'prefix_loop: loop {
|
||||
let opcode = i8088::next_ip($cpu.cs.get(), &mut $cpu.ip, $bus);
|
||||
@@ -271,12 +324,12 @@ macro_rules! step {
|
||||
$( $( $code => {
|
||||
$( let $ext = (); /* No-op just to trigger expansion. */
|
||||
modrm_val = i8088::next_ip($cpu.cs.get(), &mut $cpu.ip, $bus); )?
|
||||
step!(@code (), $cpu, $bus, (segment, 'prefix_loop), (modrm_val, $modrm, $modrm16, $modrm8), $name, $cycles, ($($args $(= $argrhs)?)*))
|
||||
step!(@code (), $cpu, $bus, (segment, repeat, 'prefix_loop), (modrm_val, $modrm, $modrm16, $modrm8), $name, $cycles, ($($args $(= $argrhs)?)*))
|
||||
}
|
||||
)?
|
||||
$( $code => {
|
||||
modrm_val = i8088::next_ip($cpu.cs.get(), &mut $cpu.ip, $bus);
|
||||
step!(@group $cpu, $bus, (segment, 'prefix_loop), (modrm_val, $modrm, $modrm16, $modrm8), $code, $subcodes)
|
||||
step!(@group $cpu, $bus, (segment, repeat, 'prefix_loop), (modrm_val, $modrm, $modrm16, $modrm8), $code, $subcodes)
|
||||
}
|
||||
)?
|
||||
),*,
|
||||
@@ -295,13 +348,22 @@ impl i8088 {
|
||||
step!((self, bus) =>
|
||||
opcodes: {
|
||||
0x26 => nop[seg=es, prefix] / 2,
|
||||
0x2e => nop[seg=cs, prefix] / 2,
|
||||
0x2E => nop[seg=cs, prefix] / 2,
|
||||
0x36 => nop[seg=ss, prefix] / 2,
|
||||
0x3e => nop[seg=ds, prefix] / 2,
|
||||
0x38 _ => cmpb[flags, modrm8, r8] / "3/24+",
|
||||
0x39 _ => cmpw[flags, modrm16, r16] / "3/24+",
|
||||
0x3A _ => cmpb[flags, r8, modrm8] / "3/24+",
|
||||
0x3B _ => cmpw[flags, r16, modrm16] / "3/24+",
|
||||
0x3C => cmpb[flags, reglo=a, d8] / "3/24+",
|
||||
0x3D => cmpw[flags, reg=a, d16] / "3/24+",
|
||||
0x3E => nop[seg=ds, prefix] / 2,
|
||||
0x60 => show[cpu] / 0, // Fake opcode for debugging
|
||||
0x61 => peek[seg=ds, addr] / 0, // Fake opcode for debugging
|
||||
0x62 _ => assert[modrm8, d8] / 0, // Fake opcode for debugging
|
||||
0x63 _ => assert[modrm16, d16] / 0, // Fake opcode for debugging
|
||||
0x80: { 0x38 => cmpb[flags, modrm8, d8] / "4/23+", },
|
||||
0x81: { 0x38 => cmpw[flags, modrm16, d16] / "4/23+", },
|
||||
//0x83: { 0x38 => cmp?[flags, modrm16, d8] / "4/23+", },
|
||||
0x88 _ => mov[modrm8, r8] / "2/13+",
|
||||
0x89 _ => mov[modrm16, r16] / "2/13+",
|
||||
0x8A _ => mov[r8, modrm8] / "2/12+",
|
||||
@@ -320,6 +382,16 @@ impl i8088 {
|
||||
0xA1 => mov[reg=a, addr] / 14,
|
||||
0xA2 => mov[addr, reglo=a] / 14,
|
||||
0xA3 => mov[addr, reg=a] / 14,
|
||||
0xA4 => movsb[flags, bus, rep, reg=c, seg=ds, seg, reg=si, reg=es, reg=di] / "18/9+17n",
|
||||
0xA5 => movsw[flags, bus, rep, reg=c, seg=ds, seg, reg=si, reg=es, reg=di] / "26/9+25n",
|
||||
0xA6 => cmpsb[flags, bus, rep, reg=c, seg=ds, seg, reg=si, reg=es, reg=di] / "22/9+22n",
|
||||
0xA7 => cmpsw[flags, bus, rep, reg=c, seg=ds, seg, reg=si, reg=es, reg=di] / "30/9+30n",
|
||||
0xAA => stosb[flags, bus, rep, reg=c, reg=es, reg=di, reglo=a] / "11/9+10n",
|
||||
0xAB => stosw[flags, bus, rep, reg=c, reg=es, reg=di, reg=a] / "15/9+14n",
|
||||
0xAC => lodsb[flags, bus, rep, reg=c, seg=ds, seg, reg=si, reglo=a] / "12/9+13n",
|
||||
0xAD => lodsw[flags, bus, rep, reg=c, seg=ds, seg, reg=si, reg=a] / "16/9+17n",
|
||||
0xAE => scasb[flags, bus, rep, reg=c, reg=es, reg=di, reglo=a] / "15/9+15n",
|
||||
0xAF => scasw[flags, bus, rep, reg=c, reg=es, reg=di, reg=a] / "19/9+19n",
|
||||
0xB0 => mov[reglo=a, d8] / 4,
|
||||
0xB1 => mov[reglo=c, d8] / 4,
|
||||
0xB2 => mov[reglo=d, d8] / 4,
|
||||
@@ -339,6 +411,8 @@ impl i8088 {
|
||||
0xC3 => ret[reg=ip, regval=ss, reg=sp, mem] / 20,
|
||||
0xCD => int[cpu, bus, d8] / 71,
|
||||
0xE8 => call[reg=ip, regval=ss, reg=sp, mem, rel16] / 23,
|
||||
0xF2 => nop[rep=NotEqual, prefix] / 0, // REPNE/REPNZ
|
||||
0xF3 => nop[rep=Equal, prefix] / 0, // REP/REPE/REPZ
|
||||
},
|
||||
modrm: {
|
||||
0x00 => seg=ds, displace0=(b+si) / M 7,
|
||||
|
||||
Reference in New Issue
Block a user