emu: machinery for ModRM propegation
This commit is contained in:
121
src/emu/i8088.rs
121
src/emu/i8088.rs
@@ -130,80 +130,164 @@ impl<'a> RValue<u8> for RegHi<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct RegLo<'a> {
|
||||||
|
reg: &'a mut u16
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> LValue<u8> for RegLo<'a> {
|
||||||
|
fn write(&mut self, val: u8) {
|
||||||
|
write_lo(&mut self.reg, val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> RValue<u8> for RegLo<'a> {
|
||||||
|
fn read(&self) -> u8 {
|
||||||
|
read_lo(*self.reg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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 ( $($args:tt)* ),
|
(@code ( $($args:tt)* ),
|
||||||
$cpu:expr, $bus:expr, $name:ident, $cycles:literal,
|
$cpu:expr, $bus:expr, $modrm:ident, $name:ident, $cycles:literal,
|
||||||
()) => {
|
()) => {
|
||||||
ops::$name($($args),*);
|
ops::$name($($args),*);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Inductive case: decode next arg to be placed in list
|
// Inductive case: decode next arg to be placed in list
|
||||||
(@code ( $($args:tt)* ),
|
(@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)?)*)) => {
|
($next:ident $(= $nextrhs:ident)? $($rest:ident $(= $restrhs:ident)?)*)) => {
|
||||||
step!(@$next$(= $nextrhs)?
|
step!(@$next$(= $nextrhs)?
|
||||||
( ($($args)*), $cpu, $bus, $name, $cycles, ($($rest $(= $restrhs)?)*) ),
|
( ($($args)*), $cpu, $bus, $modrm, $name, $cycles, ($($rest $(= $restrhs)?)*) ),
|
||||||
$cpu, $bus)
|
$cpu, $bus, $modrm)
|
||||||
};
|
};
|
||||||
|
|
||||||
// accept an argument from a decoder and recur to look for next arg
|
// 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) => {
|
$arg:expr) => {
|
||||||
step!(@code ($($args)* $arg), $cpu, $bus, $name, $cycles, ( $($rest)* ))
|
step!(@code ($($args)* $arg), $cpu, $bus, $modrm, $name, $cycles, ( $($rest)* ))
|
||||||
};
|
};
|
||||||
|
|
||||||
// Argument Decoders
|
// Argument Decoders
|
||||||
|
|
||||||
(@bus $cookie:tt, $cpu:expr, $bus:expr) => { {
|
(@bus $cookie:tt, $cpu:expr, $bus:expr, $modrm:ident) => { {
|
||||||
step!(@arg $cookie, $bus)
|
step!(@arg $cookie, $bus)
|
||||||
} };
|
} };
|
||||||
|
|
||||||
(@cpu $cookie:tt, $cpu:expr, $bus:expr) => { {
|
(@cpu $cookie:tt, $cpu:expr, $bus:expr, $modrm:ident) => { {
|
||||||
step!(@arg $cookie, $cpu)
|
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);
|
let d8 = i8088::next_ip($cpu.cs, &mut $cpu.ip, $bus);
|
||||||
step!(@arg $cookie, &d8)
|
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];
|
let mut buf = [0; 2];
|
||||||
buf[0] = i8088::next_ip($cpu.cs, &mut $cpu.ip, $bus);
|
buf[0] = i8088::next_ip($cpu.cs, &mut $cpu.ip, $bus);
|
||||||
buf[1] = 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))
|
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)
|
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);
|
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 });
|
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);
|
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];
|
let mut buf = [0; 2];
|
||||||
buf[0] = i8088::next_ip($cpu.cs, &mut $cpu.ip, $bus);
|
buf[0] = i8088::next_ip($cpu.cs, &mut $cpu.ip, $bus);
|
||||||
buf[1] = 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))
|
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
|
// 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 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) {
|
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)
|
_ => unimplemented!("opcode: {:02X}\ncpu: {:#X?}", opcode, $cpu)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -245,6 +329,9 @@ impl i8088 {
|
|||||||
pub fn run(&mut self, bus: &mut Bus) {
|
pub fn run(&mut self, bus: &mut Bus) {
|
||||||
loop {
|
loop {
|
||||||
step!((self, bus) => {
|
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,
|
0xB4 => mov[reghi=a, d8] / 4,
|
||||||
0xBA => mov[reg=d, d16] / 4,
|
0xBA => mov[reg=d, d16] / 4,
|
||||||
0xC3 => ret[reg=ip, regval=ss, reg=sp, mem] / 20,
|
0xC3 => ret[reg=ip, regval=ss, reg=sp, mem] / 20,
|
||||||
|
|||||||
Reference in New Issue
Block a user