diff --git a/src/emu/i8088.rs b/src/emu/i8088.rs index 41e6c3f..d2c84d6 100644 --- a/src/emu/i8088.rs +++ b/src/emu/i8088.rs @@ -130,80 +130,164 @@ impl<'a> RValue for RegHi<'a> { } } +struct RegLo<'a> { + reg: &'a mut u16 +} + +impl<'a> LValue for RegLo<'a> { + fn write(&mut self, val: u8) { + write_lo(&mut self.reg, val); + } +} + +impl<'a> RValue for RegLo<'a> { + fn read(&self) -> u8 { + read_lo(*self.reg) + } +} + macro_rules! step { // Base case: all args processed and ready to call op (@code ( $($args:tt)* ), - $cpu:expr, $bus:expr, $name:ident, $cycles:literal, + $cpu:expr, $bus:expr, $modrm:ident, $name:ident, $cycles:literal, ()) => { ops::$name($($args),*); }; // Inductive case: decode next arg to be placed in list (@code ( $($args:tt)* ), - $cpu:expr, $bus:expr, $name:ident, $cycles:literal, + $cpu:expr, $bus:expr, $modrm:ident, $name:ident, $cycles:literal, ($next:ident $(= $nextrhs:ident)? $($rest:ident $(= $restrhs:ident)?)*)) => { step!(@$next$(= $nextrhs)? - ( ($($args)*), $cpu, $bus, $name, $cycles, ($($rest $(= $restrhs)?)*) ), - $cpu, $bus) + ( ($($args)*), $cpu, $bus, $modrm, $name, $cycles, ($($rest $(= $restrhs)?)*) ), + $cpu, $bus, $modrm) }; // accept an argument from a decoder and recur to look for next arg - (@arg ( ($($args:tt)*), $cpu:expr, $bus:expr, $name:ident, $cycles:literal, ( $($rest:tt)* ) ), + (@arg ( ($($args:tt)*), $cpu:expr, $bus:expr, $modrm:ident, $name:ident, $cycles:literal, ( $($rest:tt)* ) ), $arg:expr) => { - step!(@code ($($args)* $arg), $cpu, $bus, $name, $cycles, ( $($rest)* )) + step!(@code ($($args)* $arg), $cpu, $bus, $modrm, $name, $cycles, ( $($rest)* )) }; // Argument Decoders - (@bus $cookie:tt, $cpu:expr, $bus:expr) => { { + (@bus $cookie:tt, $cpu:expr, $bus:expr, $modrm:ident) => { { step!(@arg $cookie, $bus) } }; - (@cpu $cookie:tt, $cpu:expr, $bus:expr) => { { + (@cpu $cookie:tt, $cpu:expr, $bus:expr, $modrm:ident) => { { step!(@arg $cookie, $cpu) } }; - (@d8 $cookie:tt, $cpu:expr, $bus:expr) => { { + (@d8 $cookie:tt, $cpu:expr, $bus:expr, $modrm:ident) => { { let d8 = i8088::next_ip($cpu.cs, &mut $cpu.ip, $bus); step!(@arg $cookie, &d8) } }; - (@d16 $cookie:tt, $cpu:expr, $bus:expr) => { { + (@d16 $cookie:tt, $cpu:expr, $bus:expr, $modrm:ident) => { { let mut buf = [0; 2]; buf[0] = i8088::next_ip($cpu.cs, &mut $cpu.ip, $bus); buf[1] = i8088::next_ip($cpu.cs, &mut $cpu.ip, $bus); step!(@arg $cookie, &LittleEndian::read_u16(&buf)) } }; - (@mem $cookie:tt, $cpu:expr, $bus:expr) => { + (@mem $cookie:tt, $cpu:expr, $bus:expr, $modrm:ident) => { step!(@arg $cookie, &mut $bus.ram) }; - (@reg=$reg:ident $cookie:tt, $cpu:expr, $bus:expr) => { + (@modrm16 $cookie:tt, $cpu:expr, $bus:expr, $modrm:ident) => { { + unimplemented!("step!(@arg $cookie, &mut ...)") + } }; + + (@r16 $cookie:tt, $cpu:expr, $bus:expr, $modrm:ident) => { { + let reg = match $modrm >> 3 & 0x7 { + 0 => &mut $cpu.a, + 1 => &mut $cpu.c, + 2 => &mut $cpu.d, + 3 => &mut $cpu.b, + 4 => &mut $cpu.sp, + 5 => &mut $cpu.bp, + 6 => &mut $cpu.si, + 7 => &mut $cpu.di, + _ => unreachable!() + }; + step!(@arg $cookie, reg) + } }; + + (@r8 $cookie:tt, $cpu:expr, $bus:expr, $modrm:ident) => { + match $modrm >> 3 & 0x7 { + 0 => step!(@arg $cookie, &mut RegHi { reg: &mut $cpu.a } ), + 1 => step!(@arg $cookie, &mut RegHi { reg: &mut $cpu.c } ), + 2 => step!(@arg $cookie, &mut RegHi { reg: &mut $cpu.d } ) , + 3 => step!(@arg $cookie, &mut RegHi { reg: &mut $cpu.b } ), + 4 => step!(@arg $cookie, &mut RegLo { reg: &mut $cpu.a } ), + 5 => step!(@arg $cookie, &mut RegLo { reg: &mut $cpu.c } ), + 6 => step!(@arg $cookie, &mut RegLo { reg: &mut $cpu.d } ), + 7 => step!(@arg $cookie, &mut RegLo { reg: &mut $cpu.b } ), + _ => unreachable!() + }; + }; + + (@reg=$reg:ident $cookie:tt, $cpu:expr, $bus:expr, $modrm:ident) => { step!(@arg $cookie, &mut $cpu.$reg); }; - (@reghi=$reg:ident $cookie:tt, $cpu:expr, $bus:expr) => { + (@reghi=$reg:ident $cookie:tt, $cpu:expr, $bus:expr, $modrm:ident) => { step!(@arg $cookie, &mut RegHi { reg: &mut $cpu.$reg }); }; - (@regval=$reg:ident $cookie:tt, $cpu:expr, $bus:expr) => { + (@regval=$reg:ident $cookie:tt, $cpu:expr, $bus:expr, $modrm:ident) => { step!(@arg $cookie, $cpu.$reg); }; - (@rel16 $cookie:tt, $cpu:expr, $bus:expr) => { { + (@rel16 $cookie:tt, $cpu:expr, $bus:expr, $modrm:ident) => { { let mut buf = [0; 2]; buf[0] = i8088::next_ip($cpu.cs, &mut $cpu.ip, $bus); buf[1] = i8088::next_ip($cpu.cs, &mut $cpu.ip, $bus); step!(@arg $cookie, LittleEndian::read_i16(&buf)) } }; + // Group Decoder + (@group $cpu:expr, $bus:expr, $modrm:ident, $code:literal, + { $( $subcode:literal => + $name:ident[$($args:ident $(= $argrhs:ident)?),*] / $cycles:literal),*$(,)? } ) => { { + let subcode = $modrm & 0x38; + match subcode { + $( $subcode => step!(@code (), $cpu, $bus, $modrm, $name, $cycles, ($($args $(= $argrhs)?)*)) ),*, + _ => unimplemented!("opcode: {:02X} {:02X}({:02X})\ncpu: {:#X?}", $code, $modrm, subcode, $cpu) + } } + }; + // Entry Point - (($cpu:expr, $bus:expr) => { $( $code:literal => $name:ident[$($args:ident $(= $argrhs:ident)?),*] / $cycles:literal ),*$(,)? } ) => { + (($cpu:expr, $bus:expr) => + { $( + $code:literal + + // Non-group opcodes + $( $($ext:pat)? => + $name:ident[$($args:ident $(= $argrhs:ident)?),*] / $cycles:literal + )? + + // Group opcodes + $( : $subcodes:tt )? + ),* + $(,)? } ) => { { let opcode = i8088::next_ip($cpu.cs, &mut $cpu.ip, $bus); + let modrm: u8; // Type ascription unnecessary but gives better err messages when missing $ext match(opcode) { - $( $code => step!(@code (), $cpu, $bus, $name, $cycles, ($($args $(= $argrhs)?)*)) ),*, + $( $( $code => { + $( let $ext = (); /* No-op just to trigger expansion. */ + modrm = i8088::next_ip($cpu.cs, &mut $cpu.ip, $bus); )? + step!(@code (), $cpu, $bus, modrm, $name, $cycles, ($($args $(= $argrhs)?)*)) + } + )? + $( $code => { + modrm = i8088::next_ip($cpu.cs, &mut $cpu.ip, $bus); + step!(@group $cpu, $bus, modrm, $code, $subcodes) + } + )? + ),*, _ => unimplemented!("opcode: {:02X}\ncpu: {:#X?}", opcode, $cpu) } } @@ -245,6 +329,9 @@ impl i8088 { pub fn run(&mut self, bus: &mut Bus) { loop { step!((self, bus) => { + 0x8B _ => mov[r16, modrm16] / "2/12+", + 0x8C: { 0x08 => mov[modrm16, reg=cs] / "2/13+", }, + 0x8E: { 0x18 => mov[reg=ds, modrm16] / "2/12+", }, 0xB4 => mov[reghi=a, d8] / 4, 0xBA => mov[reg=d, d16] / 4, 0xC3 => ret[reg=ip, regval=ss, reg=sp, mem] / 20,