From 0c55ff9b3657944c2ac3f6ed48764bf1fe4ddc4b Mon Sep 17 00:00:00 2001 From: Jared Burce Date: Fri, 12 Mar 2021 08:01:28 -0800 Subject: [PATCH] emu: modrm displacement, LEA instruction --- src/emu/i8088.rs | 116 +++++++++++++++++++++++++++++++++--------- src/emu/operands.rs | 19 ++++--- src/emu/operations.rs | 6 ++- 3 files changed, 109 insertions(+), 32 deletions(-) diff --git a/src/emu/i8088.rs b/src/emu/i8088.rs index f5308b9..c36c578 100644 --- a/src/emu/i8088.rs +++ b/src/emu/i8088.rs @@ -64,7 +64,7 @@ macro_rules! step { // Inductive case: decode next arg to be placed in list (@code ( $($done:tt)* ), $cpu:expr, $bus:expr, $prefix:tt, $modrm:tt, $name:ident, $cycles:literal, - ($next:ident $(= $nextrhs:ident)? $($rest:ident $(= $restrhs:ident)?)*)) => { + ($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 ( ($($done)*), $cpu, $bus, $prefix, $modrm, $name, $cycles, ($($rest $(= $restrhs)?)*) ), @@ -87,8 +87,7 @@ macro_rules! step { (@addr $cookie:tt, $cpu:expr, $bus:expr, ($segment:ident, $prefix_loop:lifetime), $modrm:tt) => { { let a16 = i8088::next_ip16($cpu.cs.get(), &mut $cpu.ip, $bus); - let addr = segoff_to_addr($segment.unwrap(), a16); - step!(@arg $cookie, &mut Addr { bus: $bus, addr: addr } ) + step!(@arg $cookie, &mut Addr { bus: $bus, segment: $segment.unwrap(), offset: a16 } ) } }; (@bus $cookie:tt, $cpu:expr, $bus:expr, $prefix:tt, $modrm:tt) => { @@ -109,33 +108,76 @@ 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) => { + 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) => { { + 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) => { { + 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 }) + } }; + (@mem $cookie:tt, $cpu:expr, $bus:expr, $prefix:tt, $modrm:tt) => { step!(@arg $cookie, &mut $bus.ram) }; - (@modrm16 $cookie:tt, $cpu:expr, $bus:expr, $prefix:tt, - ($modrm_val:ident, { $($val:literal => $($args:ident = $argrhs:tt),* / $mode:ident $cycles:literal),*$(,)? }, $modrm8:tt)) => { { + (@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)* ) ) ),*, + $( $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, $modrm16:tt, { $($val:literal => $($args:ident = $argrhs:tt),* / $mode:ident $cycles:literal),*$(,)? } )) => { { + ($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)* ) ) ),*, + $( $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, $prefix_loop:lifetime), $modrm:tt) => { continue $prefix_loop; }; - (@r16 $cookie:tt, $cpu:expr, $bus:expr, $prefix:tt, ($modrm:ident, $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? - match $modrm >> 3 & 0x7 { + match $modrm_val >> 3 & 0x7 { 0 => step!(@arg $cookie, &mut Reg { reg: &$cpu.a } ), 1 => step!(@arg $cookie, &mut Reg { reg: &$cpu.c } ), 2 => step!(@arg $cookie, &mut Reg { reg: &$cpu.d } ), @@ -148,9 +190,9 @@ macro_rules! step { }; }; - (@r8 $cookie:tt, $cpu:expr, $bus:expr, $prefix:tt, ($modrm:ident, $modrm16:tt, $modrm8:tt)) => { + (@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 >> 3 & 0x7 { + match $modrm_val >> 3 & 0x7 { 0 => step!(@arg $cookie, &mut RegHi { reg: &$cpu.a } ), 1 => step!(@arg $cookie, &mut RegHi { reg: &$cpu.c } ), 2 => step!(@arg $cookie, &mut RegHi { reg: &$cpu.d } ), @@ -192,13 +234,13 @@ macro_rules! step { } }; // Group Decoder - (@group $cpu:expr, $bus:expr, $prefix:tt, ($modrm:ident, $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:ident)?),*] / $cycles:literal),*$(,)? } ) => { { - let subcode = $modrm & 0x38; + $name:ident[$($args:ident $(= $argrhs:tt)?),*] / $cycles:literal),*$(,)? } ) => { { + let subcode = $modrm_val & 0x38; match subcode { - $( $subcode => step!(@code (), $cpu, $bus, $prefix, ($modrm, $modrm16, $modrm8), $name, $cycles, ($($args $(= $argrhs)?)*)) ),*, - _ => unimplemented!("opcode: {:02X} {:02X}({:02X})\ncpu: {:#X?}", $code, $modrm, subcode, $cpu) + $( $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) } } }; @@ -209,31 +251,32 @@ macro_rules! step { // Non-group opcodes $( $($ext:pat)? => - $name:ident[$($args:ident $(= $argrhs:ident)?),*] / $cycles:literal + $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 modrm: 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 { let opcode = i8088::next_ip($cpu.cs.get(), &mut $cpu.ip, $bus); match(opcode) { $( $( $code => { $( let $ext = (); /* No-op just to trigger expansion. */ - modrm = i8088::next_ip($cpu.cs.get(), &mut $cpu.ip, $bus); )? - step!(@code (), $cpu, $bus, (segment, 'prefix_loop), (modrm, $modrm16, $modrm8), $name, $cycles, ($($args $(= $argrhs)?)*)) + 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)?)*)) } )? $( $code => { - modrm = i8088::next_ip($cpu.cs.get(), &mut $cpu.ip, $bus); - step!(@group $cpu, $bus, (segment, 'prefix_loop), (modrm, $modrm16, $modrm8), $code, $subcodes) + 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) } )? ),*, @@ -265,6 +308,7 @@ impl i8088 { 0x08 => mov[modrm16, reg=cs] / "2/13+", 0x10 => mov[modrm16, reg=ss] / "2/13+", 0x18 => mov[modrm16, reg=ds] / "2/13+", }, + 0x8D _ => lea[r16, modrm] / "2+", 0x8E: { 0x00 => mov[reg=es, modrm16] / "2/12+", 0x08 => mov[reg=cs, modrm16] / "2/12+", 0x10 => mov[reg=ss, modrm16] / "2/12+", @@ -294,6 +338,32 @@ impl i8088 { 0xCD => int[cpu, bus, d8] / 71, 0xE8 => call[reg=ip, regval=ss, reg=sp, mem, rel16] / 23, }, + 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, diff --git a/src/emu/operands.rs b/src/emu/operands.rs index 85fe90d..bb3c8c5 100644 --- a/src/emu/operands.rs +++ b/src/emu/operands.rs @@ -1,3 +1,4 @@ +use emu::util::segoff_to_addr; use std::cell::Cell; use emu::util::{read_hi, read_lo, write_hi, write_lo}; @@ -35,40 +36,42 @@ impl RValue for u16 { pub struct Addr<'a> { pub bus: &'a mut Bus, - pub addr: usize + pub segment: u16, + pub offset: u16 } impl Address for Addr<'_> { fn addr(&self) -> usize { - self.addr + segoff_to_addr(self.segment, self.offset) } } impl LValue for Addr<'_> { fn write(&mut self, val: u8) { - self.bus.write(self.addr, val); + self.bus.write(self.addr(), val); } } impl RValue for Addr<'_> { fn read(&self) -> u8 { - self.bus.read(self.addr) + self.bus.read(self.addr()) } } impl LValue for Addr<'_> { fn write(&mut self, val: u16) { let buf = val.to_le_bytes(); - self.bus.write(self.addr, buf[0]); - self.bus.write(self.addr + 1, buf[1]); + self.bus.write(self.addr(), buf[0]); + self.bus.write(self.addr() + 1, buf[1]); } } impl RValue for Addr<'_> { fn read(&self) -> u16 { + let addr = self.addr(); let mut buf = [0u8; 2]; - buf[0] = self.bus.read(self.addr); - buf[1] = self.bus.read(self.addr + 1); + buf[0] = self.bus.read(addr); + buf[1] = self.bus.read(addr + 1); u16::from_le_bytes(buf) } } diff --git a/src/emu/operations.rs b/src/emu/operations.rs index 594469f..d0c9118 100644 --- a/src/emu/operations.rs +++ b/src/emu/operations.rs @@ -1,7 +1,7 @@ use emu::byteorder::{ByteOrder, LittleEndian}; use emu::dos; use emu::i8088::i8088; -use emu::operands::{Address, LValue, Reg, RValue}; +use emu::operands::{Addr, Address, LValue, Reg, RValue}; use emu::pc::Bus; use emu::util::segoff_to_addr; @@ -26,6 +26,10 @@ pub fn int(cpu: &mut i8088, bus: &mut Bus, num: &u8) { } } +pub fn lea(dst: &mut Reg, addr: &Addr) { + dst.write(addr.offset) +} + pub fn mov(dst: &mut impl LValue, src: &impl RValue) { dst.write(src.read()); }