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)]
|
#[derive(Clone, Copy, Default)]
|
||||||
pub struct Flags {
|
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
|
// 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
|
// 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
|
// 5: Reserved
|
||||||
zf: bool, // 6: Zero Flag: 1=ZR(Zero), 0=NZ(Not Zero)
|
pub zf: bool, // 6: Zero Flag: 1=ZR(Zero), 0=NZ(Not Zero)
|
||||||
sf: bool, // 7: Sign Flag: 1=NG(Negative), 0=PL(Positive)
|
pub sf: bool, // 7: Sign Flag: 1=NG(Negative), 0=PL(Positive)
|
||||||
tf: bool, // 8: Trap Flag
|
pub tf: bool, // 8: Trap Flag
|
||||||
ie: bool, // 9: (Real name "IF") Interrupt Enable: 1=EI(Enable Interrupt), 0=DI(Disable Interrupt)
|
pub 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)
|
pub df: bool, // 10: Direction Flag: 1=DN(Down), 0=UP(Up)
|
||||||
of: bool, // 11: Overflow Flag: 1=OV(Overflow), 0=NV(Not Overflow)
|
pub of: bool, // 11: Overflow Flag: 1=OV(Overflow), 0=NV(Not Overflow)
|
||||||
// bits 12-15 always 1
|
// 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 {
|
macro_rules! step {
|
||||||
// Base case: all args processed and ready to call op
|
// Base case: all args processed and ready to call op
|
||||||
(@code ( $($done:tt)* ),
|
(@code ( $($done:tt)* ),
|
||||||
@@ -85,7 +92,9 @@ macro_rules! step {
|
|||||||
};
|
};
|
||||||
// Argument Decoders
|
// 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);
|
let a16 = i8088::next_ip16($cpu.cs.get(), &mut $cpu.ip, $bus);
|
||||||
step!(@arg $cookie, &mut Addr { bus: $bus, segment: $segment.unwrap(), offset: a16 } )
|
step!(@arg $cookie, &mut Addr { bus: $bus, segment: $segment.unwrap(), offset: a16 } )
|
||||||
} };
|
} };
|
||||||
@@ -108,26 +117,36 @@ macro_rules! step {
|
|||||||
step!(@arg $cookie, &d16)
|
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,
|
step!(@arg $cookie, &mut Addr { bus: $bus,
|
||||||
segment: $segment.unwrap(),
|
segment: $segment.unwrap(),
|
||||||
offset: $cpu.$reg1.get() $(+ $cpu.$reg2.get())? })
|
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);
|
let d8 = i8088::next_ip($cpu.cs.get(), &mut $cpu.ip, $bus);
|
||||||
step!(@arg $cookie, &mut Addr { bus: $bus,
|
step!(@arg $cookie, &mut Addr { bus: $bus,
|
||||||
segment: $segment.unwrap(),
|
segment: $segment.unwrap(),
|
||||||
offset: $cpu.$reg1.get() $(+ $cpu.$reg2.get())? + d8 as u16 })
|
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);
|
let d16 = i8088::next_ip16($cpu.cs.get(), &mut $cpu.ip, $bus);
|
||||||
step!(@arg $cookie, &mut Addr { bus: $bus,
|
step!(@arg $cookie, &mut Addr { bus: $bus,
|
||||||
segment: $segment.unwrap(),
|
segment: $segment.unwrap(),
|
||||||
offset: $cpu.$reg1.get() $(+ $cpu.$reg2.get())? + d16 })
|
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) => {
|
(@mem $cookie:tt, $cpu:expr, $bus:expr, $prefix:tt, $modrm:tt) => {
|
||||||
step!(@arg $cookie, &mut $bus.ram)
|
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)) => {
|
(@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?
|
// 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))
|
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()));
|
$segment = $segment.or(Some($cpu.$seg.get()));
|
||||||
step!(@arg $cookie)
|
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 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 =>
|
{ $( $subcode:literal =>
|
||||||
$name:ident[$($args:ident $(= $argrhs:tt)?),*] / $cycles:literal),*$(,)? } ) => { {
|
$name:ident[$($args:ident $(= $argrhs:tt)?),*] / $cycles:literal),*$(,)? } ) => { {
|
||||||
let subcode = $modrm_val & 0x38;
|
let subcode = $modrm_val & 0x38;
|
||||||
@@ -264,6 +316,7 @@ macro_rules! step {
|
|||||||
) => {
|
) => {
|
||||||
{
|
{
|
||||||
let mut segment = None;
|
let mut segment = None;
|
||||||
|
let mut repeat = RepPrefix::None;
|
||||||
let modrm_val: u8; // Type ascription unnecessary but gives better err messages when missing $ext
|
let modrm_val: u8; // Type ascription unnecessary but gives better err messages when missing $ext
|
||||||
'prefix_loop: loop {
|
'prefix_loop: loop {
|
||||||
let opcode = i8088::next_ip($cpu.cs.get(), &mut $cpu.ip, $bus);
|
let opcode = i8088::next_ip($cpu.cs.get(), &mut $cpu.ip, $bus);
|
||||||
@@ -271,12 +324,12 @@ macro_rules! step {
|
|||||||
$( $( $code => {
|
$( $( $code => {
|
||||||
$( let $ext = (); /* No-op just to trigger expansion. */
|
$( let $ext = (); /* No-op just to trigger expansion. */
|
||||||
modrm_val = i8088::next_ip($cpu.cs.get(), &mut $cpu.ip, $bus); )?
|
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 => {
|
$( $code => {
|
||||||
modrm_val = i8088::next_ip($cpu.cs.get(), &mut $cpu.ip, $bus);
|
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) =>
|
step!((self, bus) =>
|
||||||
opcodes: {
|
opcodes: {
|
||||||
0x26 => nop[seg=es, prefix] / 2,
|
0x26 => nop[seg=es, prefix] / 2,
|
||||||
0x2e => nop[seg=cs, prefix] / 2,
|
0x2E => nop[seg=cs, prefix] / 2,
|
||||||
0x36 => nop[seg=ss, 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
|
0x60 => show[cpu] / 0, // Fake opcode for debugging
|
||||||
0x61 => peek[seg=ds, addr] / 0, // Fake opcode for debugging
|
0x61 => peek[seg=ds, addr] / 0, // Fake opcode for debugging
|
||||||
0x62 _ => assert[modrm8, d8] / 0, // Fake opcode for debugging
|
0x62 _ => assert[modrm8, d8] / 0, // Fake opcode for debugging
|
||||||
0x63 _ => assert[modrm16, d16] / 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+",
|
0x88 _ => mov[modrm8, r8] / "2/13+",
|
||||||
0x89 _ => mov[modrm16, r16] / "2/13+",
|
0x89 _ => mov[modrm16, r16] / "2/13+",
|
||||||
0x8A _ => mov[r8, modrm8] / "2/12+",
|
0x8A _ => mov[r8, modrm8] / "2/12+",
|
||||||
@@ -320,6 +382,16 @@ impl i8088 {
|
|||||||
0xA1 => mov[reg=a, addr] / 14,
|
0xA1 => mov[reg=a, addr] / 14,
|
||||||
0xA2 => mov[addr, reglo=a] / 14,
|
0xA2 => mov[addr, reglo=a] / 14,
|
||||||
0xA3 => mov[addr, reg=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,
|
0xB0 => mov[reglo=a, d8] / 4,
|
||||||
0xB1 => mov[reglo=c, d8] / 4,
|
0xB1 => mov[reglo=c, d8] / 4,
|
||||||
0xB2 => mov[reglo=d, d8] / 4,
|
0xB2 => mov[reglo=d, d8] / 4,
|
||||||
@@ -339,6 +411,8 @@ impl i8088 {
|
|||||||
0xC3 => ret[reg=ip, regval=ss, reg=sp, mem] / 20,
|
0xC3 => ret[reg=ip, regval=ss, reg=sp, mem] / 20,
|
||||||
0xCD => int[cpu, bus, d8] / 71,
|
0xCD => int[cpu, bus, d8] / 71,
|
||||||
0xE8 => call[reg=ip, regval=ss, reg=sp, mem, rel16] / 23,
|
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: {
|
modrm: {
|
||||||
0x00 => seg=ds, displace0=(b+si) / M 7,
|
0x00 => seg=ds, displace0=(b+si) / M 7,
|
||||||
|
|||||||
@@ -16,6 +16,22 @@ pub trait Address {
|
|||||||
fn addr(&self) -> usize;
|
fn addr(&self) -> usize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait HiBit {
|
||||||
|
fn hi_bit(&self) -> bool;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HiBit for u8 {
|
||||||
|
fn hi_bit(&self) -> bool {
|
||||||
|
self >> 7 == 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HiBit for u16 {
|
||||||
|
fn hi_bit(&self) -> bool {
|
||||||
|
self >> 15 == 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl RValue<u8> for u8 {
|
impl RValue<u8> for u8 {
|
||||||
fn read(&self) -> u8 {
|
fn read(&self) -> u8 {
|
||||||
*self
|
*self
|
||||||
|
|||||||
@@ -1,12 +1,83 @@
|
|||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
|
||||||
use emu::byteorder::{ByteOrder, LittleEndian};
|
use emu::byteorder::{ByteOrder, LittleEndian};
|
||||||
|
|
||||||
use emu::dos;
|
use emu::dos;
|
||||||
use emu::i8088::i8088;
|
use emu::i8088::{Flags, RepPrefix, i8088};
|
||||||
use emu::operands::{Addr, Address, LValue, Reg, RValue};
|
use emu::operands::{Addr, Address, HiBit, LValue, Reg, RValue};
|
||||||
use emu::pc::Bus;
|
use emu::pc::Bus;
|
||||||
use emu::util::segoff_to_addr;
|
use emu::util::segoff_to_addr;
|
||||||
|
|
||||||
|
macro_rules! string_op {
|
||||||
|
( ($type:ty,
|
||||||
|
$flags:ident,
|
||||||
|
$rep:ident,
|
||||||
|
$cx:ident
|
||||||
|
$(, si=$si:expr)?
|
||||||
|
$(, di=$di:expr)?
|
||||||
|
$(, zf=$zf:expr)? ),
|
||||||
|
$code:tt
|
||||||
|
) => {
|
||||||
|
let stride = std::mem::size_of::<$type>() as u16;
|
||||||
|
if $rep == RepPrefix::None {
|
||||||
|
$code;
|
||||||
|
if $flags.df {
|
||||||
|
$($si.write($si.read() - stride);)?
|
||||||
|
$($di.write($di.read() - stride);)?
|
||||||
|
} else {
|
||||||
|
$($si.write($si.read() + stride);)?
|
||||||
|
$($di.write($di.read() + stride);)?
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
while $cx.read() != 0 {
|
||||||
|
$cx.write($cx.read() - 1);
|
||||||
|
$code;
|
||||||
|
if $flags.df {
|
||||||
|
$($si.write($si.read() - stride);)?
|
||||||
|
$($di.write($di.read() - stride);)?
|
||||||
|
} else {
|
||||||
|
$($si.write($si.read() + stride);)?
|
||||||
|
$($di.write($di.read() + stride);)?
|
||||||
|
}
|
||||||
|
$( match ($rep, $zf) {
|
||||||
|
(RepPrefix::Equal, true) => (),
|
||||||
|
(RepPrefix::Equal, false) => break,
|
||||||
|
(RepPrefix::NotEqual, true) => break,
|
||||||
|
(RepPrefix::NotEqual, false) => (),
|
||||||
|
_ => (),
|
||||||
|
} )?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Based on SUB/CMP, others will vary!
|
||||||
|
// Decide if this can realistically be generalized
|
||||||
|
macro_rules! update_flags {
|
||||||
|
// Base Case
|
||||||
|
(@recur $flags:expr, $dst:ident, $carry:ident) => {};
|
||||||
|
|
||||||
|
(@af $flags:expr, $dst:ident, $carry:ident) => { /* XXX: Adjust/AuxCarry esoteric? (mostly for BCD?), might not need */ };
|
||||||
|
(@cf $flags:expr, $dst:ident, $carry:ident) => { $flags.cf = $carry };
|
||||||
|
(@of $flags:expr, $dst:ident, $carry:ident) => { compile_error!("compute flags.of outside macro"); };
|
||||||
|
(@pf $flags:expr, $dst:ident, $carry:ident) => { /* XXX: parity looks esoteric, might not need */ };
|
||||||
|
(@sf $flags:expr, $dst:ident, $carry:ident) => { $flags.sf = $dst.hi_bit(); };
|
||||||
|
(@zf $flags:expr, $dst:ident, $carry:ident) => { $flags.zf = $dst == 0; };
|
||||||
|
|
||||||
|
// Recursive step
|
||||||
|
(@recur $flags:expr, $dst:ident, $carry:ident, $next:ident $(, $rest:ident)* ) => {
|
||||||
|
update_flags!(@$next $flags, $dst, $carry);
|
||||||
|
update_flags!(@recur $flags, $dst, $carry $(, $rest)*);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Entry Point
|
||||||
|
($flags:expr, $eval:expr $(, $flagnames:ident)* ) => { {
|
||||||
|
let (dst, carry) = $eval;
|
||||||
|
update_flags!(@recur $flags, dst, carry $(, $flagnames)*);
|
||||||
|
dst
|
||||||
|
} }
|
||||||
|
}
|
||||||
|
|
||||||
pub fn assert<T: Debug + Eq>(loc: &impl RValue<T>, val: &impl RValue<T>) {
|
pub fn assert<T: Debug + Eq>(loc: &impl RValue<T>, val: &impl RValue<T>) {
|
||||||
assert_eq!(loc.read(), val.read(),
|
assert_eq!(loc.read(), val.read(),
|
||||||
"ASSERT instruction failed: {:#2X?} != {:#2X?}", loc.read(), val.read());
|
"ASSERT instruction failed: {:#2X?} != {:#2X?}", loc.read(), val.read());
|
||||||
@@ -27,6 +98,54 @@ pub fn call(ip: &mut Reg, ss: u16, sp: &mut Reg, mem: &mut [u8], addr: i16) {
|
|||||||
ip.write(target);
|
ip.write(target);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn cmpb(flags: &mut Flags, dst: &impl RValue<u8>, src: &impl RValue<u8>) {
|
||||||
|
let (dst, src) = (dst.read(), src.read());
|
||||||
|
let after = update_flags!(flags, dst.overflowing_sub(src), af, cf, pf, sf, zf);
|
||||||
|
flags.of = 0 != 0x80 & // In the sign bit...
|
||||||
|
(src ^ dst) & // ...operands have different signs...
|
||||||
|
(dst ^ after); // ...and destination sign-bit changed
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cmpw(flags: &mut Flags, dst: &impl RValue<u16>, src: &impl RValue<u16>) {
|
||||||
|
let (dst, src) = (dst.read(), src.read());
|
||||||
|
let after = update_flags!(flags, dst.overflowing_sub(src), af, cf, pf, sf, zf);
|
||||||
|
flags.of = 0 != 0x8000 & // In the sign bit...
|
||||||
|
(src ^ dst) & // ...operands have different signs...
|
||||||
|
(dst ^ after); // ...and destination sign-bit changed
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cmpsb(flags: &mut Flags,
|
||||||
|
bus: &mut Bus,
|
||||||
|
rep: RepPrefix,
|
||||||
|
cx: &mut Reg,
|
||||||
|
seg: u16,
|
||||||
|
si: &mut Reg,
|
||||||
|
es: &mut Reg,
|
||||||
|
di: &mut Reg) {
|
||||||
|
string_op!((u8, flags, rep, cx, di=di, zf=flags.zf), {
|
||||||
|
let src = <Addr as RValue<u8>>::read(&Addr { bus: bus, segment: seg, offset: si.read() });
|
||||||
|
let dst = <Addr as RValue<u8>>::read(&Addr { bus: bus, segment: es.read(), offset: di.read() });
|
||||||
|
|
||||||
|
cmpb(flags, &dst, &src);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cmpsw(flags: &mut Flags,
|
||||||
|
bus: &mut Bus,
|
||||||
|
rep: RepPrefix,
|
||||||
|
cx: &mut Reg,
|
||||||
|
seg: u16,
|
||||||
|
si: &mut Reg,
|
||||||
|
es: &mut Reg,
|
||||||
|
di: &mut Reg) {
|
||||||
|
string_op!((u16, flags, rep, cx, di=di, zf=flags.zf), {
|
||||||
|
let src = <Addr as RValue<u16>>::read(&Addr { bus: bus, segment: seg, offset: si.read() });
|
||||||
|
let dst = <Addr as RValue<u16>>::read(&Addr { bus: bus, segment: es.read(), offset: di.read() });
|
||||||
|
|
||||||
|
cmpw(flags, &dst, &src);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
pub fn int(cpu: &mut i8088, bus: &mut Bus, num: &u8) {
|
pub fn int(cpu: &mut i8088, bus: &mut Bus, num: &u8) {
|
||||||
match num {
|
match num {
|
||||||
0x21 => dos::interrupt(cpu, bus),
|
0x21 => dos::interrupt(cpu, bus),
|
||||||
@@ -38,10 +157,64 @@ pub fn lea(dst: &mut Reg, addr: &Addr) {
|
|||||||
dst.write(addr.offset)
|
dst.write(addr.offset)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn lodsb(flags: &Flags,
|
||||||
|
bus: &mut Bus,
|
||||||
|
rep: RepPrefix,
|
||||||
|
cx: &mut Reg,
|
||||||
|
seg: u16,
|
||||||
|
si: &mut Reg,
|
||||||
|
al: &mut impl LValue<u8>) {
|
||||||
|
string_op!((u8, flags, rep, cx, si=si), {
|
||||||
|
al.write(Addr { bus: bus, segment: seg, offset: si.read() }.read());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lodsw(flags: &Flags,
|
||||||
|
bus: &mut Bus,
|
||||||
|
rep: RepPrefix,
|
||||||
|
cx: &mut Reg,
|
||||||
|
seg: u16,
|
||||||
|
si: &mut Reg,
|
||||||
|
ax: &mut impl LValue<u16>) {
|
||||||
|
string_op!((u16, flags, rep, cx, si=si), {
|
||||||
|
ax.write(Addr { bus: bus, segment: seg, offset: si.read() }.read());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
pub fn mov<T>(dst: &mut impl LValue<T>, src: &impl RValue<T>) {
|
pub fn mov<T>(dst: &mut impl LValue<T>, src: &impl RValue<T>) {
|
||||||
dst.write(src.read());
|
dst.write(src.read());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn movsb(flags: &Flags,
|
||||||
|
bus: &mut Bus,
|
||||||
|
rep: RepPrefix,
|
||||||
|
cx: &mut Reg,
|
||||||
|
seg: u16,
|
||||||
|
si: &mut Reg,
|
||||||
|
es: &Reg,
|
||||||
|
di: &mut Reg) {
|
||||||
|
string_op!((u8, flags, rep, cx, si=si, di=di), {
|
||||||
|
let src = <Addr as RValue<u8>>::read(&Addr { bus: bus, segment: seg, offset: si.read() });
|
||||||
|
let mut dst = Addr { bus: bus, segment: es.read(), offset: di.read() };
|
||||||
|
<Addr as LValue<u8>>::write(&mut dst, src);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn movsw(flags: &Flags,
|
||||||
|
bus: &mut Bus,
|
||||||
|
rep: RepPrefix,
|
||||||
|
cx: &mut Reg,
|
||||||
|
seg: u16,
|
||||||
|
si: &mut Reg,
|
||||||
|
es: &Reg,
|
||||||
|
di: &mut Reg) {
|
||||||
|
string_op!((u16, flags, rep, cx, si=si, di=di), {
|
||||||
|
let src = <Addr as RValue<u16>>::read(&Addr { bus: bus, segment: seg, offset: si.read() });
|
||||||
|
let mut dst = Addr { bus: bus, segment: es.read(), offset: di.read() };
|
||||||
|
<Addr as LValue<u16>>::write(&mut dst, src);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
pub fn nop() {}
|
pub fn nop() {}
|
||||||
|
|
||||||
pub fn pop16(ss: u16, sp: &mut Reg, mem: &[u8]) -> u16 {
|
pub fn pop16(ss: u16, sp: &mut Reg, mem: &[u8]) -> u16 {
|
||||||
@@ -59,3 +232,55 @@ pub fn push16(ss: u16, sp: &mut Reg, mem: &mut [u8], val: u16) {
|
|||||||
pub fn ret(ip: &mut Reg, ss: u16, sp: &mut Reg, mem: &[u8]) {
|
pub fn ret(ip: &mut Reg, ss: u16, sp: &mut Reg, mem: &[u8]) {
|
||||||
ip.write(pop16(ss, sp, mem));
|
ip.write(pop16(ss, sp, mem));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn scasb(flags: &mut Flags,
|
||||||
|
bus: &mut Bus,
|
||||||
|
rep: RepPrefix,
|
||||||
|
cx: &mut Reg,
|
||||||
|
es: &Reg,
|
||||||
|
di: &mut Reg,
|
||||||
|
needle: &impl RValue<u8>) {
|
||||||
|
string_op!((u8, flags, rep, cx, di=di, zf=flags.zf), {
|
||||||
|
let elem = Addr { bus: bus, segment: es.read(), offset: di.read() };
|
||||||
|
cmpb(flags, &elem, needle);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn scasw(flags: &mut Flags,
|
||||||
|
bus: &mut Bus,
|
||||||
|
rep: RepPrefix,
|
||||||
|
cx: &mut Reg,
|
||||||
|
es: &Reg,
|
||||||
|
di: &mut Reg,
|
||||||
|
needle: &impl RValue<u16>) {
|
||||||
|
string_op!((u16, flags, rep, cx, di=di, zf=flags.zf), {
|
||||||
|
let elem = Addr { bus: bus, segment: es.read(), offset: di.read() };
|
||||||
|
cmpw(flags, &elem, needle);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn stosb(flags: &Flags,
|
||||||
|
bus: &mut Bus,
|
||||||
|
rep: RepPrefix,
|
||||||
|
cx: &mut Reg,
|
||||||
|
es: &Reg,
|
||||||
|
di: &mut Reg,
|
||||||
|
val: &impl RValue<u8>) {
|
||||||
|
string_op!((u8, flags, rep, cx, di=di), {
|
||||||
|
let mut dst = Addr { bus: bus, segment: es.read(), offset: di.read() };
|
||||||
|
dst.write(val.read());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn stosw(flags: &Flags,
|
||||||
|
bus: &mut Bus,
|
||||||
|
rep: RepPrefix,
|
||||||
|
cx: &mut Reg,
|
||||||
|
es: &Reg,
|
||||||
|
di: &mut Reg,
|
||||||
|
val: &impl RValue<u16>) {
|
||||||
|
string_op!((u16, flags, rep, cx, di=di), {
|
||||||
|
let mut dst = Addr { bus: bus, segment: es.read(), offset: di.read() };
|
||||||
|
dst.write(val.read());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user