use std::fmt::{Debug, Formatter}; use super::byteorder::{ByteOrder, LittleEndian}; use emu::pc::Bus; #[allow(non_camel_case_types)] #[derive(Clone, Copy, Debug, Default)] pub struct i8088 { // Data Registers pub a: Reg16, pub b: Reg16, pub c: Reg16, pub d: Reg16, // Index Registers pub si: u16, // Source Index pub di: u16, // Dest Index pub bp: u16, // Base Pointer pub sp: u16, // Stack Pointer // Segment Registers pub cs: u16, // Code Segment pub ds: u16, // Data Segment pub es: u16, // Extra Segment pub ss: u16, // Stack Segment // Pointer Register pub ip: u16, // Status Register pub flags: Flags, } #[derive(Clone, Copy, Default)] pub struct Flags { 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) // 3: Reserved 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) // bits 12-15 always 1 } mod ops { pub fn call(cpu: &mut super::i8088, mem: &mut [u8], addr: i16) { let target = cpu.ip.wrapping_add(addr as u16); trace!("CALL 0x{:04X}", target); cpu.push16(mem, cpu.ip); cpu.ip = target; } } macro_rules! step { (@code $cpu:ident, $bus:ident, ($name:ident, $cycles:literal, $arg:tt)) => { step!(@$arg $name $cpu, $bus) }; (@rel16 $name:ident $cpu:ident, $bus:ident) => { { let mut buf = [0; 2]; buf[0] = $cpu.next_ip($bus); buf[1] = $cpu.next_ip($bus); ops::$name($cpu, &mut $bus.ram, LittleEndian::read_i16(&buf)); } }; // Entry Point (($cpu:ident, $bus:ident) => { $( $code:literal => $impl:tt ),* } ) => { { let opcode = $cpu.next_ip($bus); match(opcode) { $( $code => step!(@code $cpu, $bus, $impl) )*, _ => unimplemented!("opcode: {:02X}", opcode) } } } } pub const fn segoff_to_addr(segment: u16, offset: u16) -> usize { let segaddr = (segment as usize) << 4; segaddr + offset as usize } impl i8088 { pub fn run(&mut self, bus: &mut Bus) { loop { step!((self, bus) => { 0xE8 => (call, 23, rel16) }) } } fn push16(&mut self, mem: &mut [u8], val: u16) { // XXX: Not checking for stack faults or anything self.sp -= 2; LittleEndian::write_u16(&mut mem[segoff_to_addr(self.ss, self.sp)..], val); } fn next_ip<'a>(&mut self, bus: &Bus) -> u8 { let eip = segoff_to_addr(self.cs, self.ip); self.ip += 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] } } impl From for Flags { fn from(flags: u16) -> Self { Self { cf: flags & 1 != 0, pf: flags & 1 << 2 != 0, af: flags & 1 << 4 != 0, zf: flags & 1 << 6 != 0, sf: flags & 1 << 7 != 0, tf: flags & 1 << 8 != 0, ie: flags & 1 << 9 != 0, df: flags & 1 << 10 != 0, of: flags & 1 << 11 != 0, } } } impl From for u16 { fn from(flags: Flags) -> Self { 0b1111_0000_0010_1010 // Not sure what all reserved bits should be, but it shouldn't matter | (flags.cf as u16) | (flags.pf as u16) << 2 | (flags.af as u16) << 4 | (flags.zf as u16) << 6 | (flags.sf as u16) << 7 | (flags.tf as u16) << 8 | (flags.ie as u16) << 9 | (flags.df as u16) << 10 | (flags.of as u16) << 11 } } impl Debug for Flags { fn fmt(&self, fmt: &mut Formatter) -> Result<(), std::fmt::Error> { use std::fmt::Write; fmt.write_str("[ ")?; for flag in [ (self.cf, "CF "), (self.pf, "PF "), (self.af, "AF "), (self.zf, "ZF "), (self.sf, "SF "), (self.tf, "TF "), (self.ie, "IF "), (self.df, "DF "), (self.of, "OF ") ].iter() { if flag.0 { fmt.write_str(flag.1)? }; } fmt.write_char(']')?; Ok(()) } } #[repr(C)] #[derive(Clone, Copy)] pub union Reg16 { x: u16, lh: [u8; 2], } impl Debug for Reg16 { fn fmt(&self, fmt: &mut Formatter) -> Result<(), std::fmt::Error> { unsafe { self.x.fmt(fmt) } } } impl Default for Reg16 { fn default() -> Self { Self { x: 0u16 } } }