use std::cell::Cell; use std::fmt::Debug; use super::byteorder::{ByteOrder, LittleEndian}; use emu::operands::{FarPtr, Reg, RegHi, RegLo}; use emu::operations as ops; use emu::flags::Flags; use emu::pc::Bus; use emu::util::segoff_to_addr; #[allow(non_camel_case_types)] #[derive(Clone, Debug, Default)] pub struct i8088 { // Data Registers pub a: Cell, pub b: Cell, pub c: Cell, pub d: Cell, // Index Registers pub si: Cell, // Source Index pub di: Cell, // Dest Index pub bp: Cell, // Base Pointer pub sp: Cell, // Stack Pointer // Segment Registers pub cs: Cell, // Code Segment pub ds: Cell, // Data Segment pub es: Cell, // Extra Segment pub ss: Cell, // Stack Segment // Pointer Register pub ip: Cell, // Status Register pub flags: Flags, } #[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 $(form=($($tyargs:tt),*),)? ( $($done:tt)* ), $cpu:expr, $bus:expr, $prefix:tt, $modrm:tt, $name:ident, $cycles:literal, ()) => { ops::$name$(::<$($tyargs),*>)?($($done),*); }; // Inductive case: decode next arg to be placed in list (@code $(form=$form:tt,)? ( $($done:tt)* ), $cpu:expr, $bus:expr, $prefix:tt, $modrm:tt, $name:ident, $cycles:literal, ($next:ident $(= $nextrhs:tt)? $($rest:ident $(= $restrhs:tt)?)*)) => { step!(@$next$(= $nextrhs)? // "cookie" tt to be passed thru to @arg so we have all the args for the recursive step ( $(form=$form,)? ($($done)*), $cpu, $bus, $prefix, $modrm, $name, $cycles, ($($rest $(= $restrhs)?)*) ), $cpu, $bus, $prefix, $modrm) }; // accept an argument from a decoder and recur to look for next arg (@arg ( $(form=$form:tt,)? ($($done:tt)*), $cpu:expr, $bus:expr, $prefix:tt, $modrm:tt, $name:ident, $cycles:literal, $rest:tt ) $(, $arg:expr)?) => { step!(@code $(form=$form,)? ($($done)* $($arg)?), $cpu, $bus, $prefix, $modrm, $name, $cycles, $rest) }; // decoder deferring to sub-decoders, push them onto the decoder stack to process next (@push ( $(form=$form:tt,)? ($($done:tt)*), $cpu:expr, $bus:expr, $prefix:tt, $modrm:tt, $name:ident, $cycles:literal, ( $($rest:tt)* ) ), ( $($args:tt)* )) => { step!(@code $(form=$form,)? ($($done)*), $cpu, $bus, $prefix, $modrm, $name, $cycles, ( $($args)* $($rest)* )) }; // Set the type arguments for operations that cannot infer width (@form ( $done:tt, $cpu:expr, $bus:expr, $prefix:tt, $modrm:tt, $name:ident, $cycles:literal, $rest:tt ), form=$form:tt) => { step!(@code form=$form, $done, $cpu, $bus, $prefix, $modrm, $name, $cycles, $rest) }; // Adds tokens to the last argument. Used by @convert to append ".into()" to the last arg (@append-to-last ($($done:tt)*) ( $(form=$form:tt,)? ($last:tt), $cpu:expr, $bus:expr, $prefix:tt, $modrm:tt, $name:ident, $cycles:literal, $rest:tt ) { $($conv:tt)+ } ) => { step!(@code $(form=$form,)? ($($done)* ($last$($conv)+)), $cpu, $bus, $prefix, $modrm, $name, $cycles, $rest ) }; // Recursive case for @append-to-last, walks through already // processed args to find the last one for appending (@append-to-last ($($done:tt)*) ( $(form=$form:tt,)? ($next:tt $($restdone:tt)+), $cpu:expr, $bus:expr, $prefix:tt, $modrm:tt, $name:ident, $cycles:literal, $rest:tt ) { $($conv:tt)+ } ) => { step!(@append-to-last ($($done)* $next) ( $(form=$form,)? ($($restdone)+), $cpu, $bus, $prefix, $modrm, $name, $cycles, $rest ) { $($conv)+ } ) }; // === OP DIRECTIVES === (@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, FarPtr { bus: $bus, segment: $segment.unwrap(), offset: a16 } ) } }; (@bus $cookie:tt, $cpu:expr, $bus:expr, $prefix:tt, $modrm:tt) => { step!(@arg $cookie, $bus) }; (@const=$val:literal $cookie:tt, $cpu:expr, $bus:expr, $prefix:tt, $modrm:tt) => { step!(@arg $cookie, $val) }; // Adds ".into()" onto the last argument (@convert $cookie:tt, $cpu:expr, $bus:expr, $prefix:tt, $modrm:tt) => { step!(@append-to-last () $cookie { .into() } ) }; (@cpu $cookie:tt, $cpu:expr, $bus:expr, $prefix:tt, $modrm:tt) => { step!(@arg $cookie, $cpu) }; (@d8 $cookie:tt, $cpu:expr, $bus:expr, $prefix:tt, $modrm:tt) => { { let d8 = i8088::next_ip($cpu.cs.get(), &mut $cpu.ip, $bus); step!(@arg $cookie, d8) } }; (@d8_as_d16 $cookie:tt, $cpu:expr, $bus:expr, $prefix:tt, $modrm:tt) => { { let d8 = i8088::next_ip($cpu.cs.get(), &mut $cpu.ip, $bus); step!(@arg $cookie, d8 as u16) } }; (@d16 $cookie:tt, $cpu:expr, $bus:expr, $prefix:tt, $modrm:tt) => { { let d16 = i8088::next_ip16($cpu.cs.get(), &mut $cpu.ip, $bus); step!(@arg $cookie, d16) } }; (@displace0=($reg1:ident $(+ $reg2:ident)? ) $cookie:tt, $cpu:expr, $bus:expr, ($segment:ident, $repeat:ident, $prefix_loop:lifetime), $modrm:tt) => { step!(@arg $cookie, FarPtr { 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, $repeat:ident, $prefix_loop:lifetime), $modrm:tt) => { { let rel8 = i8088::next_ip($cpu.cs.get(), &mut $cpu.ip, $bus) as i8 as u16; step!(@arg $cookie, FarPtr { bus: $bus, segment: $segment.unwrap(), offset: ($cpu.$reg1.get() $(+ $cpu.$reg2.get())?).wrapping_add(rel8) }) } }; (@displace16=($reg1:ident $(+ $reg2:ident)? ) $cookie:tt, $cpu:expr, $bus:expr, ($segment:ident, $repeat:ident, $prefix_loop:lifetime), $modrm:tt) => { { let rel16 = i8088::next_ip16($cpu.cs.get(), &mut $cpu.ip, $bus); step!(@arg $cookie, FarPtr { bus: $bus, segment: $segment.unwrap(), offset: ($cpu.$reg1.get() $(+ $cpu.$reg2.get())?).wrapping_add(rel16) }) } }; (@flags $cookie:tt, $cpu:expr, $bus:expr, $prefix:tt, $modrm:tt) => { step!(@arg $cookie, &mut $cpu.flags) }; (@form=byte0 $cookie:tt, $cpu:expr, $bus:expr, $prefix:tt, $modrm:tt) => { step!(@form $cookie, form=(u8)); }; (@form=byte1 $cookie:tt, $cpu:expr, $bus:expr, $prefix:tt, $modrm:tt) => { step!(@form $cookie, form=(u8, _)); }; (@form=byte3 $cookie:tt, $cpu:expr, $bus:expr, $prefix:tt, $modrm:tt) => { step!(@form $cookie, form=(u8, _, _, _)); }; (@form=word0 $cookie:tt, $cpu:expr, $bus:expr, $prefix:tt, $modrm:tt) => { step!(@form $cookie, form=(u16)); }; (@form=word1 $cookie:tt, $cpu:expr, $bus:expr, $prefix:tt, $modrm:tt) => { step!(@form $cookie, form=(u16, _)); }; (@form=word3 $cookie:tt, $cpu:expr, $bus:expr, $prefix:tt, $modrm:tt) => { step!(@form $cookie, form=(u16, _, _, _)); }; (@mem $cookie:tt, $cpu:expr, $bus:expr, $prefix:tt, $modrm:tt) => { step!(@arg $cookie, &mut $bus.ram) }; (@modrm $cookie:tt, $cpu:expr, $bus:expr, $prefix:tt, ($modrm_val:ident, { $($val:literal => $($args:ident $(= $argrhs:tt)?),* / $mode:ident $cycles:literal),*$(,)? }, $modrm16:tt, $modrm8:tt )) => { { let modrm_val = $modrm_val & !0x38; match modrm_val { $( $val => step!(@push $cookie, ($($args $(= $argrhs)?)* ) ) ),*, _ => unimplemented!("modrm: {:02X}({:02X})\ncpu: {:#X?}", $modrm_val, modrm_val, $cpu) } } }; (@modrm16 $cookie:tt, $cpu:expr, $bus:expr, $prefix:tt, ($modrm_val:ident, { $($val:literal => $($args:ident $(= $argrhs:tt)?),* / $mode:ident $cycles:literal),*$(,)? }, { $($val16:literal => $($args16:ident $(= $argrhs16:tt)?),* / $mode16:ident $cycles16:literal),*$(,)? }, $modrm8:tt )) => { { let modrm_val = $modrm_val & !0x38; match modrm_val { $( $val => step!(@push $cookie, ($($args $(= $argrhs)?)* ) ) ),*, $( $val16 => step!(@push $cookie, ($($args16 $(= $argrhs16)?)* ) ) ),*, _ => unimplemented!("modrm: {:02X}({:02X})\ncpu: {:#X?}", $modrm_val, modrm_val, $cpu) } } }; (@modrm8 $cookie:tt, $cpu:expr, $bus:expr, $prefix:tt, ($modrm_val:ident, { $($val:literal => $($args:ident $(= $argrhs:tt)?),* / $mode:ident $cycles:literal),*$(,)? }, $modrm16:tt, { $($val8:literal => $($args8:ident $(= $argrhs8:tt)?),* / $mode8:ident $cycles8:literal),*$(,)? } )) => { { let modrm_val = $modrm_val & !0x38; match modrm_val { $( $val => step!(@push $cookie, ($($args $(= $argrhs)?)* ) ) ),*, $( $val8 => step!(@push $cookie, ($($args8 $(= $argrhs8)?)* ) ) ),*, _ => unimplemented!("modrm: {:02X}({:02X})\ncpu: {:#X?}", $modrm_val, modrm_val, $cpu) } } }; (@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? match $modrm_val >> 3 & 0x7 { 0 => step!(@arg $cookie, Reg { reg: &$cpu.a } ), 1 => step!(@arg $cookie, Reg { reg: &$cpu.c } ), 2 => step!(@arg $cookie, Reg { reg: &$cpu.d } ), 3 => step!(@arg $cookie, Reg { reg: &$cpu.b } ), 4 => step!(@arg $cookie, Reg { reg: &$cpu.sp } ), 5 => step!(@arg $cookie, Reg { reg: &$cpu.bp } ), 6 => step!(@arg $cookie, Reg { reg: &$cpu.si } ), 7 => step!(@arg $cookie, Reg { reg: &$cpu.di } ), _ => unreachable!() }; }; (@r8 $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? match $modrm_val >> 3 & 0x7 { 0 => step!(@arg $cookie, RegLo { reg: &$cpu.a } ), 1 => step!(@arg $cookie, RegLo { reg: &$cpu.c } ), 2 => step!(@arg $cookie, RegLo { reg: &$cpu.d } ), 3 => step!(@arg $cookie, RegLo { reg: &$cpu.b } ), 4 => step!(@arg $cookie, RegHi { reg: &$cpu.a } ), 5 => step!(@arg $cookie, RegHi { reg: &$cpu.c } ), 6 => step!(@arg $cookie, RegHi { reg: &$cpu.d } ), 7 => step!(@arg $cookie, RegHi { reg: &$cpu.b } ), _ => unreachable!() }; }; (@reg=$reg:ident $cookie:tt, $cpu:expr, $bus:expr, $prefix:tt, $modrm:tt) => { step!(@arg $cookie, Reg { reg: &$cpu.$reg }); }; (@reghi=$reg:ident $cookie:tt, $cpu:expr, $bus:expr, $prefix:tt, $modrm:tt) => { step!(@arg $cookie, RegHi { reg: &$cpu.$reg }); }; (@reglo=$reg:ident $cookie:tt, $cpu:expr, $bus:expr, $prefix:tt, $modrm:tt) => { step!(@arg $cookie, RegLo { reg: &$cpu.$reg }); }; (@regval=$reg:ident $cookie:tt, $cpu:expr, $bus:expr, $prefix:tt, $modrm:tt) => { step!(@arg $cookie, $cpu.$reg.get()); }; (@rel8 $cookie:tt, $cpu:expr, $bus:expr, $prefix:tt, $modrm:tt) => { { let rel8 = i8088::next_ip($cpu.cs.get(), &mut $cpu.ip, $bus) as i8; step!(@arg $cookie, rel8 as u16) } }; (@rel16 $cookie:tt, $cpu:expr, $bus:expr, $prefix:tt, $modrm:tt) => { { let mut buf = [0; 2]; buf[0] = i8088::next_ip($cpu.cs.get(), &mut $cpu.ip, $bus); buf[1] = i8088::next_ip($cpu.cs.get(), &mut $cpu.ip, $bus); step!(@arg $cookie, LittleEndian::read_i16(&buf) as u16) } }; (@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, { $( $subcode:literal => $name:ident[$($args:ident $(= $argrhs:tt)?),*] / $cycles:literal),*$(,)? } ) => { { let subcode = $modrm_val & 0x38; match subcode { $( $subcode => step!(@code (), $cpu, $bus, $prefix, ($modrm_val, $modrm, $modrm16, $modrm8), $name, $cycles, ($($args $(= $argrhs)?)*)) ),*, _ => unimplemented!("opcode: {:02X} {:02X}({:02X})\ncpu: {:#X?}", $code, $modrm_val, subcode, $cpu) } } }; // Entry Point (($cpu:expr, $bus:expr) => opcodes: { $( $code:literal // Non-group opcodes $( $($ext:pat)? => $name:ident[$($args:ident $(= $argrhs:tt)?),*] / $cycles:literal )? // Group opcodes $( : $subcodes:tt )? ),* $(,)? }, modrm: $modrm:tt, modrm16: $modrm16:tt, modrm8: $modrm8:tt ) => { { 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); match(opcode) { $( $( $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, 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, repeat, 'prefix_loop), (modrm_val, $modrm, $modrm16, $modrm8), $code, $subcodes) } )? ),*, _ => unimplemented!("opcode: {:02X}\ncpu: {:#X?}", opcode, $cpu) } // Only prefixes loop and they do so with an explicit continue statement break; } } } } impl i8088 { pub fn run(&mut self, bus: &mut Bus) { loop { step!((self, bus) => opcodes: { 0x06 => push[bus, regval=ss, reg=sp, regval=es] / 14, 0x07 => pop[bus, regval=ss, reg=sp, reg=es] / 12, 0x0E => push[bus, regval=ss, reg=sp, regval=cs] / 14, 0x16 => push[bus, regval=ss, reg=sp, regval=ss] / 14, 0x17 => pop[bus, regval=ss, reg=sp, reg=ss] / 12, 0x1E => push[bus, regval=ss, reg=sp, regval=ds] / 14, 0x1F => pop[bus, regval=ss, reg=sp, reg=ds] / 12, 0x26 => nop[seg=es, prefix] / 2, 0x2E => nop[seg=cs, prefix] / 2, 0x36 => nop[seg=ss, prefix] / 2, 0x38 _ => cmp[flags, modrm8, r8] / "3/24+", 0x39 _ => cmp[flags, modrm16, r16] / "3/24+", 0x3A _ => cmp[flags, r8, modrm8] / "3/24+", 0x3B _ => cmp[flags, r16, modrm16] / "3/24+", 0x3C => cmp[flags, reglo=a, d8] / "3/24+", 0x3D => cmp[flags, reg=a, d16] / "3/24+", 0x3E => nop[seg=ds, prefix] / 2, 0x40 => inc[flags, reg=a] / 3, 0x41 => inc[flags, reg=c] / 3, 0x42 => inc[flags, reg=d] / 3, 0x43 => inc[flags, reg=b] / 3, 0x44 => inc[flags, reg=sp] / 3, 0x45 => inc[flags, reg=bp] / 3, 0x46 => inc[flags, reg=si] / 3, 0x47 => inc[flags, reg=di] / 3, 0x48 => dec[flags, reg=a] / 3, 0x49 => dec[flags, reg=c] / 3, 0x4A => dec[flags, reg=d] / 3, 0x4B => dec[flags, reg=b] / 3, 0x4C => dec[flags, reg=sp] / 3, 0x4D => dec[flags, reg=bp] / 3, 0x4E => dec[flags, reg=si] / 3, 0x4F => dec[flags, reg=di] / 3, 0x50 => push[bus, regval=ss, reg=sp, regval=a] / 15, 0x51 => push[bus, regval=ss, reg=sp, regval=c] / 15, 0x52 => push[bus, regval=ss, reg=sp, regval=d] / 15, 0x53 => push[bus, regval=ss, reg=sp, regval=b] / 15, 0x54 => push[bus, regval=ss, reg=sp, regval=sp] / 15, 0x55 => push[bus, regval=ss, reg=sp, regval=bp] / 15, 0x56 => push[bus, regval=ss, reg=sp, regval=si] / 15, 0x57 => push[bus, regval=ss, reg=sp, regval=di] / 15, 0x58 => pop[bus, regval=ss, reg=sp, reg=a] / 12, 0x59 => pop[bus, regval=ss, reg=sp, reg=c] / 12, 0x5A => pop[bus, regval=ss, reg=sp, reg=d] / 12, 0x5B => pop[bus, regval=ss, reg=sp, reg=b] / 12, 0x5C => pop[bus, regval=ss, reg=sp, reg=sp] / 12, 0x5D => pop[bus, regval=ss, reg=sp, reg=bp] / 12, 0x5E => pop[bus, regval=ss, reg=sp, reg=si] / 12, 0x5F => pop[bus, regval=ss, reg=sp, reg=di] / 12, 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 0x70 => jo[flags, reg=ip, rel8] / "16/4", // JO rel8 0x71 => jno[flags, reg=ip, rel8] / "16/4", // JNO rel8 0x72 => jb[flags, reg=ip, rel8] / "16/4", // JB/JNAE/JC rel8 0x73 => jae[flags, reg=ip, rel8] / "16/4", // JNB/JAE/JNC rel8 0x74 => jz[flags, reg=ip, rel8] / "16/4", // JE/JZ rel8 0x75 => jnz[flags, reg=ip, rel8] / "16/4", // JNE/JNZ rel8 0x76 => jbe[flags, reg=ip, rel8] / "16/4", // JBE/JNA rel8 0x77 => ja[flags, reg=ip, rel8] / "16/4", // JNBE/JA rel8 0x78 => js[flags, reg=ip, rel8] / "16/4", // JS rel8 0x79 => jns[flags, reg=ip, rel8] / "16/4", // JNS rel8 0x7A => jpe[flags, reg=ip, rel8] / "16/4", // JP/JPE rel8 0x7B => jpo[flags, reg=ip, rel8] / "16/4", // JNP/JPO rel8 0x7C => jl[flags, reg=ip, rel8] / "16/4", // JL/JNGE rel8 0x7D => jge[flags, reg=ip, rel8] / "16/4", // JNL/JGE rel8 0x7E => jle[flags, reg=ip, rel8] / "16/4", // JLE/JNG rel8 0x7F => jg[flags, reg=ip, rel8] / "16/4", // JNLE/JG rel8 0x80: { 0x38 => cmp[flags, modrm8, d8] / "4/23+", }, 0x81: { 0x38 => cmp[flags, modrm16, d16] / "4/23+", }, 0x83: { 0x38 => cmp[flags, modrm16, d8_as_d16] / "4/23+", }, 0x88 _ => mov[modrm8, r8] / "2/13+", // MOV r/m8, r8 0x89 _ => mov[modrm16, r16] / "2/13+", // MOV r/m16, r16 0x8A _ => mov[r8, modrm8] / "2/12+", // MOV r8, r/m8 0x8B _ => mov[r16, modrm16] / "2/12+", // MOV r16, r/m16 0x8C: { 0x00 => mov[modrm16, reg=es] / "2/13+", // MOV r/m16, es 0x08 => mov[modrm16, reg=cs] / "2/13+", // MOV r/m16, cs 0x10 => mov[modrm16, reg=ss] / "2/13+", // MOV r/m16, ss 0x18 => mov[modrm16, reg=ds] / "2/13+", }, // MOV r/m16, ds 0x8D _ => lea[r16, modrm] / "2+", 0x8E: { 0x00 => mov[reg=es, modrm16] / "2/12+", // MOV es, r/m16 0x08 => mov[reg=cs, modrm16] / "2/12+", // MOV cs, r/m16 0x10 => mov[reg=ss, modrm16] / "2/12+", // MOV ss, r/m16 0x18 => mov[reg=ds, modrm16] / "2/12+", }, // MOV ds, r/m16 0x8F: { 0x00 => pop_modrm[regval=ss, reg=sp, modrm16, convert, bus] / "12/25+" }, // POP r/m16 0x90 => nop[] / 3, 0xA0 => mov[seg=ds, reglo=a, addr] / 14, // MOV al, [addr] 0xA1 => mov[seg=ds, reg=a, addr] / 14, // MOV ax, [addr] 0xA2 => mov[seg=ds, addr, reglo=a] / 14, // MOV [addr], al 0xA3 => mov[seg=ds, addr, reg=a] / 14, // MOV [addr], ax 0xA4 => movs[form=byte0, flags, bus, rep, reg=c, seg=ds, seg, reg=si, reg=es, reg=di] / "18/9+17n", 0xA5 => movs[form=word0, flags, bus, rep, reg=c, seg=ds, seg, reg=si, reg=es, reg=di] / "26/9+25n", 0xA6 => cmps[form=byte0, flags, bus, rep, reg=c, seg=ds, seg, reg=si, reg=es, reg=di] / "22/9+22n", 0xA7 => cmps[form=word0, flags, bus, rep, reg=c, seg=ds, seg, reg=si, reg=es, reg=di] / "30/9+30n", 0xAA => stos[flags, bus, rep, reg=c, reg=es, reg=di, reglo=a] / "11/9+10n", 0xAB => stos[flags, bus, rep, reg=c, reg=es, reg=di, reg=a] / "15/9+14n", 0xAC => lods[flags, bus, rep, reg=c, seg=ds, seg, reg=si, reglo=a] / "12/9+13n", 0xAD => lods[flags, bus, rep, reg=c, seg=ds, seg, reg=si, reg=a] / "16/9+17n", 0xAE => scas[flags, bus, rep, reg=c, reg=es, reg=di, reglo=a] / "15/9+15n", 0xAF => scas[flags, bus, rep, reg=c, reg=es, reg=di, reg=a] / "19/9+19n", 0xB0 => mov[reglo=a, d8] / 4, // MOV al, d8 0xB1 => mov[reglo=c, d8] / 4, // MOV cl, d8 0xB2 => mov[reglo=d, d8] / 4, // MOV dl, d8 0xB3 => mov[reglo=b, d8] / 4, // MOV bl, d8 0xB4 => mov[reghi=a, d8] / 4, // MOV ah, d8 0xB5 => mov[reghi=c, d8] / 4, // MOV ch, d8 0xB6 => mov[reghi=d, d8] / 4, // MOV dh, d8 0xB7 => mov[reghi=b, d8] / 4, // MOV bh, d8 0xB8 => mov[reg=a, d16] / 4, // MOV ax, d16 0xB9 => mov[reg=c, d16] / 4, // MOV cx, d16 0xBA => mov[reg=d, d16] / 4, // MOV dx, d16 0xBB => mov[reg=b, d16] / 4, // MOV bx, d16 0xBC => mov[reg=sp, d16] / 4, // MOV sp, d16 0xBD => mov[reg=bp, d16] / 4, // MOV bp, d16 0xBE => mov[reg=si, d16] / 4, // MOV si, d16 0xBF => mov[reg=di, d16] / 4, // MOV di, d16 0xC3 => pop[bus, regval=ss, reg=sp, reg=ip] / 20, // RET 0xC6: { 0x00 => mov[modrm8, d8] / "4/14+" }, // MOV r/m8, d8 0xC7: { 0x00 => mov[modrm16, d16] / "4/14+" }, // MOV r/m16, d16 0xCD => int[cpu, bus, d8] / 71, 0xD0: { 0x00 => rol[form=byte3, flags, modrm8, const=1u8] / "2/23+", // ROL r/m16, 1 0x08 => ror[form=byte3, flags, modrm8, const=1u8] / "2/23+", // ROR r/m16, 1 0x10 => rcl[form=byte3, flags, modrm8, const=1u8] / "2/23+", // RCL r/m16, 1 0x18 => rcr[form=byte3, flags, modrm8, const=1u8] / "2/23+", // RCR r/m16, 1 0x20 => shl[form=byte3, flags, modrm8, const=1u8] / "2/23+", // SHL r/m8, 1 0x28 => shr[form=byte3, flags, modrm8, const=1u8] / "2/23+", // SHR r/m8, 1 0x38 => sar[form=byte3, flags, modrm8, const=1u8] / "2/23+" }, // SAR r/m8, 1 0xD1: { 0x00 => rol[form=word3, flags, modrm16, const=1u16] / "2/23+", // ROL r/m16, 1 0x08 => ror[form=word3, flags, modrm16, const=1u16] / "2/23+", // ROR r/m16, 1 0x10 => rcl[form=word3, flags, modrm16, const=1u16] / "2/23+", // RCL r/m16, 1 0x18 => rcr[form=word3, flags, modrm16, const=1u16] / "2/23+", // RCR r/m16, 1 0x20 => shl[form=word3, flags, modrm16, const=1u16] / "2/23+", // SHL r/m16, 1 0x28 => shr[form=word3, flags, modrm16, const=1u16] / "2/23+", // SHR r/m16, 1 0x38 => sar[form=word3, flags, modrm16, const=1u16] / "2/23+" }, // SAR r/m16, 1 0xD2: { 0x00 => rol[form=byte3, flags, modrm8, reglo=c] / "8/28++4n", // ROL r/m8, CL 0x08 => ror[form=byte3, flags, modrm8, reglo=c] / "8/28++4n", // ROR r/m8, CL 0x10 => rcl[form=byte3, flags, modrm8, reglo=c] / "8/28++4n", // RCL r/m8, CL 0x18 => rcr[form=byte3, flags, modrm8, reglo=c] / "8/28++4n", // RCR r/m8, CL 0x20 => shl[form=byte3, flags, modrm8, reglo=c] / "2/23+", // SHL r/m8, CL 0x28 => shr[form=byte3, flags, modrm8, reglo=c] / "2/23+", // SHR r/m8, CL 0x38 => sar[form=byte3, flags, modrm8, reglo=c] / "2/23+" }, // SAR r/m8, CL 0xD3: { 0x00 => rol[form=word3, flags, modrm16, reglo=c] / "8/28++4n", // ROL r/m16, CL 0x08 => ror[form=word3, flags, modrm16, reglo=c] / "8/28++4n", // ROR r/m16, CL 0x10 => rcl[form=word3, flags, modrm16, reglo=c] / "8/28++4n", // RCL r/m16, CL 0x18 => rcr[form=word3, flags, modrm16, reglo=c] / "8/28++4n", // RCR r/m16, CL 0x20 => shl[form=word3, flags, modrm16, reglo=c] / "2/23+", // SHL r/m16, CL 0x28 => shr[form=word3, flags, modrm16, reglo=c] / "2/23+", // SHR r/m16, CL 0x38 => sar[form=word3, 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 0xF5 => cmc[flags] / 2, // CMC (Complement Carry Flag) 0xF8 => clc[flags] / 2, // CLC (Clear Carry Flag) 0xF9 => stc[flags] / 2, // STC (Set Carry Flag) 0xFC => cld[flags] / 2, // CLD (Clear Direction Flag) 0xFD => std[flags] / 2, // STD (Set Direction Flag) 0xFE: { 0x00 => inc[form=byte1, flags, modrm8] / "3/23+", // INC r/m8 0x08 => dec[form=byte1, flags, modrm8] / "3/23+", }, // DEC r/m8 0xFF: { 0x00 => inc[form=word1, flags, modrm16] / "3/23+", // INC r/m16 0x08 => dec[form=word1, flags, modrm16] / "3/23+", // DEC r/m16 0x30 => push_modrm[regval=ss, reg=sp, modrm16, convert, bus] / "15/24+" }, }, modrm: { 0x00 => seg=ds, displace0=(b+si) / M 7, 0x01 => seg=ds, displace0=(b+di) / M 8, 0x02 => seg=ss, displace0=(bp+si) / M 8, 0x03 => seg=ss, displace0=(bp+di) / M 7, 0x04 => seg=ds, displace0=(si) / M 5, 0x05 => seg=ds, displace0=(di) / M 5, 0x06 => seg=ds, addr / M 6, 0x07 => seg=ds, displace0=(b) / M 5, 0x40 => seg=ds, displace8=(b+si) / M 11, 0x41 => seg=ds, displace8=(b+di) / M 12, 0x42 => seg=ss, displace8=(bp+si) / M 12, 0x43 => seg=ss, displace8=(bp+di) / M 11, 0x44 => seg=ds, displace8=(si) / M 9, 0x45 => seg=ds, displace8=(di) / M 9, 0x46 => seg=ss, displace8=(bp) / M 9, 0x47 => seg=ds, displace8=(b) / M 9, 0x80 => seg=ds, displace16=(b+si) / M 11, 0x81 => seg=ds, displace16=(b+di) / M 12, 0x82 => seg=ss, displace16=(bp+si) / M 12, 0x83 => seg=ss, displace16=(bp+di) / M 11, 0x84 => seg=ds, displace16=(si) / M 9, 0x85 => seg=ds, displace16=(di) / M 9, 0x86 => seg=ss, displace16=(bp) / M 9, 0x87 => seg=ds, displace16=(b) / M 9, }, modrm16: { 0xC0 => reg=a / R 0, 0xC1 => reg=c / R 0, 0xC2 => reg=d / R 0, 0xC3 => reg=b / R 0, 0xC4 => reg=sp / R 0, 0xC5 => reg=bp / R 0, 0xC6 => reg=si / R 0, 0xC7 => reg=di / R 0, }, modrm8: { 0xC0 => reglo=a / R 0, 0xC1 => reglo=c / R 0, 0xC2 => reglo=d / R 0, 0xC3 => reglo=b / R 0, 0xC4 => reghi=a / R 0, 0xC5 => reghi=c / R 0, 0xC6 => reghi=d / R 0, 0xC7 => reghi=b / R 0, }) } } fn next_ip<'a>(cs: u16, ip: &mut Cell, bus: &Bus) -> u8 { let eip = segoff_to_addr(cs, ip.get()); ip.set(ip.get() + 1); // We'll assume cpu is always executing in RAM. Also assume the // IP doesn't reach the end of the segment (My guess is that it // should loop within the segment if it did) bus.ram[eip - Bus::RAM_LOCATION] } fn next_ip16<'a>(cs: u16, ip: &mut Cell, bus: &Bus) -> u16 { let eip = segoff_to_addr(cs, ip.get()); ip.set(ip.get() + 2); // We'll assume cpu is always executing in RAM. Also assume the // IP doesn't reach the end of the segment (My guess is that it // should loop within the segment if it did) let buf = &bus.ram[eip - Bus::RAM_LOCATION .. eip + 2 - Bus::RAM_LOCATION]; LittleEndian::read_u16(buf) } }