Files
vrtue/src/emu/i8088.rs

184 lines
4.6 KiB
Rust

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<u16> 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<Flags> 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 } }
}