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,
|
||||
|
||||
@@ -16,6 +16,22 @@ pub trait Address {
|
||||
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 {
|
||||
fn read(&self) -> u8 {
|
||||
*self
|
||||
|
||||
@@ -1,12 +1,83 @@
|
||||
use std::fmt::Debug;
|
||||
|
||||
use emu::byteorder::{ByteOrder, LittleEndian};
|
||||
|
||||
use emu::dos;
|
||||
use emu::i8088::i8088;
|
||||
use emu::operands::{Addr, Address, LValue, Reg, RValue};
|
||||
use emu::i8088::{Flags, RepPrefix, i8088};
|
||||
use emu::operands::{Addr, Address, HiBit, LValue, Reg, RValue};
|
||||
use emu::pc::Bus;
|
||||
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>) {
|
||||
assert_eq!(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);
|
||||
}
|
||||
|
||||
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) {
|
||||
match num {
|
||||
0x21 => dos::interrupt(cpu, bus),
|
||||
@@ -38,10 +157,64 @@ pub fn lea(dst: &mut Reg, addr: &Addr) {
|
||||
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>) {
|
||||
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 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]) {
|
||||
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