emu: wrap cpu regs in Cell. r16 addr mode.
r16 + modrm16 allowed cpu regs to alias. Wrap them in Cell to allow this. We also create a Reg wrapper when passing regs so that we can pass as &mut while simultaneously having other references to the same reg.
This commit is contained in:
@@ -2,16 +2,16 @@ use emu::pc::Bus;
|
||||
use emu::i8088::{i8088, read_hi, read_lo, segoff_to_addr};
|
||||
|
||||
pub fn interrupt(cpu: &mut i8088, bus: &mut Bus) {
|
||||
let svc = read_hi(cpu.a);
|
||||
let svc = read_hi(&cpu.a);
|
||||
match svc {
|
||||
0x09 => print_string(cpu, bus),
|
||||
0x4C => exit(read_lo(cpu.a)),
|
||||
0x4C => exit(read_lo(&cpu.a)),
|
||||
_ => unimplemented!("dos service: AH={:02X}h\ncpu: {:#X?}", svc, cpu)
|
||||
}
|
||||
}
|
||||
|
||||
fn print_string(cpu: &i8088, bus: &Bus) {
|
||||
let addr = segoff_to_addr(cpu.ds, cpu.d);
|
||||
let addr = segoff_to_addr(cpu.ds.get(), cpu.d.get());
|
||||
for byte in bus.ram[addr..].iter().take_while(|byte| **byte as char != '$') {
|
||||
print!("{}", *byte as char);
|
||||
}
|
||||
|
||||
172
src/emu/i8088.rs
172
src/emu/i8088.rs
@@ -1,3 +1,4 @@
|
||||
use std::cell::Cell;
|
||||
use std::fmt::{Debug, Formatter};
|
||||
|
||||
use super::byteorder::{ByteOrder, LittleEndian};
|
||||
@@ -5,28 +6,28 @@ use super::byteorder::{ByteOrder, LittleEndian};
|
||||
use emu::pc::Bus;
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct i8088 {
|
||||
// Data Registers
|
||||
pub a: u16,
|
||||
pub b: u16,
|
||||
pub c: u16,
|
||||
pub d: u16,
|
||||
pub a: Cell<u16>,
|
||||
pub b: Cell<u16>,
|
||||
pub c: Cell<u16>,
|
||||
pub d: Cell<u16>,
|
||||
|
||||
// Index Registers
|
||||
pub si: u16, // Source Index
|
||||
pub di: u16, // Dest Index
|
||||
pub bp: u16, // Base Pointer
|
||||
pub sp: u16, // Stack Pointer
|
||||
pub si: Cell<u16>, // Source Index
|
||||
pub di: Cell<u16>, // Dest Index
|
||||
pub bp: Cell<u16>, // Base Pointer
|
||||
pub sp: Cell<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
|
||||
pub cs: Cell<u16>, // Code Segment
|
||||
pub ds: Cell<u16>, // Data Segment
|
||||
pub es: Cell<u16>, // Extra Segment
|
||||
pub ss: Cell<u16>, // Stack Segment
|
||||
|
||||
// Pointer Register
|
||||
pub ip: u16,
|
||||
pub ip: Cell<u16>,
|
||||
|
||||
// Status Register
|
||||
pub flags: Flags,
|
||||
@@ -52,22 +53,22 @@ pub struct Flags {
|
||||
mod ops {
|
||||
use emu::byteorder::{ByteOrder, LittleEndian};
|
||||
use emu::dos;
|
||||
use emu::i8088::{Bus, LValue, RValue, i8088, segoff_to_addr};
|
||||
use emu::i8088::{Bus, LValue, Reg, RValue, i8088, segoff_to_addr};
|
||||
|
||||
pub fn show(cpu: &mut i8088) {
|
||||
println!("{:#X?}", cpu);
|
||||
}
|
||||
|
||||
pub fn peek(cpu: &mut i8088, bus: &mut Bus, addr: &u16) {
|
||||
let addr = segoff_to_addr(cpu.ds, *addr);
|
||||
let addr = segoff_to_addr(cpu.ds.get(), *addr);
|
||||
let val = bus.ram[addr];
|
||||
println!("PEEK: @{:#X} = {:#X} ({})", addr, val, val);
|
||||
}
|
||||
|
||||
pub fn call(ip: &mut u16, ss: u16, sp: &mut u16, mem: &mut [u8], addr: i16) {
|
||||
let target = ip.wrapping_add(addr as u16);
|
||||
push16(ss, sp, mem, *ip);
|
||||
*ip = target;
|
||||
pub fn call(ip: &mut Reg, ss: u16, sp: &mut Reg, mem: &mut [u8], addr: i16) {
|
||||
let target = ip.read().wrapping_add(addr as u16);
|
||||
push16(ss, sp, mem, ip.read());
|
||||
ip.write(target);
|
||||
}
|
||||
|
||||
pub fn int(cpu: &mut i8088, bus: &mut Bus, num: &u8) {
|
||||
@@ -81,20 +82,20 @@ mod ops {
|
||||
dst.write(src.read());
|
||||
}
|
||||
|
||||
pub fn pop16(ss: u16, sp: &mut u16, mem: &[u8]) -> u16 {
|
||||
let val = LittleEndian::read_u16(&mem[segoff_to_addr(ss, *sp)..]);
|
||||
*sp += 2;
|
||||
pub fn pop16(ss: u16, sp: &mut Reg, mem: &[u8]) -> u16 {
|
||||
let val = LittleEndian::read_u16(&mem[segoff_to_addr(ss.read(), sp.read())..]);
|
||||
sp.write(sp.read() + 2);
|
||||
val
|
||||
}
|
||||
|
||||
pub fn push16(ss: u16, sp: &mut u16, mem: &mut [u8], val: u16) {
|
||||
pub fn push16(ss: u16, sp: &mut Reg, mem: &mut [u8], val: u16) {
|
||||
// XXX: Not checking for stack faults or anything
|
||||
*sp -= 2;
|
||||
LittleEndian::write_u16(&mut mem[segoff_to_addr(ss, *sp)..], val);
|
||||
sp.write(sp.read() - 2);
|
||||
LittleEndian::write_u16(&mut mem[segoff_to_addr(ss.read(), sp.read())..], val);
|
||||
}
|
||||
|
||||
pub fn ret(ip: &mut u16, ss: u16, sp: &mut u16, mem: &[u8]) {
|
||||
*ip = pop16(ss, sp, mem);
|
||||
pub fn ret(ip: &mut Reg, ss: u16, sp: &mut Reg, mem: &[u8]) {
|
||||
ip.write(pop16(ss, sp, mem));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,8 +125,24 @@ impl RValue<u16> for u16 {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Reg<'a> {
|
||||
reg: &'a Cell<u16>
|
||||
}
|
||||
|
||||
impl<'a> LValue<u16> for Reg<'a> {
|
||||
fn write(&mut self, val: u16) {
|
||||
self.reg.set(val);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RValue<u16> for Reg<'a> {
|
||||
fn read(&self) -> u16 {
|
||||
self.reg.get()
|
||||
}
|
||||
}
|
||||
|
||||
struct RegHi<'a> {
|
||||
reg: &'a mut u16
|
||||
reg: &'a Cell<u16>
|
||||
}
|
||||
|
||||
impl<'a> LValue<u8> for RegHi<'a> {
|
||||
@@ -136,12 +153,12 @@ impl<'a> LValue<u8> for RegHi<'a> {
|
||||
|
||||
impl<'a> RValue<u8> for RegHi<'a> {
|
||||
fn read(&self) -> u8 {
|
||||
read_hi(*self.reg)
|
||||
read_hi(self.reg)
|
||||
}
|
||||
}
|
||||
|
||||
struct RegLo<'a> {
|
||||
reg: &'a mut u16
|
||||
reg: &'a Cell<u16>
|
||||
}
|
||||
|
||||
impl<'a> LValue<u8> for RegLo<'a> {
|
||||
@@ -152,7 +169,7 @@ impl<'a> LValue<u8> for RegLo<'a> {
|
||||
|
||||
impl<'a> RValue<u8> for RegLo<'a> {
|
||||
fn read(&self) -> u8 {
|
||||
read_lo(*self.reg)
|
||||
read_lo(self.reg)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -196,14 +213,14 @@ macro_rules! step {
|
||||
} };
|
||||
|
||||
(@d8 $cookie:tt, $cpu:expr, $bus:expr, $modrm:tt) => { {
|
||||
let d8 = i8088::next_ip($cpu.cs, &mut $cpu.ip, $bus);
|
||||
let d8 = i8088::next_ip($cpu.cs.get(), &mut $cpu.ip, $bus);
|
||||
step!(@arg $cookie, &d8)
|
||||
} };
|
||||
|
||||
(@d16 $cookie:tt, $cpu:expr, $bus:expr, $modrm:tt) => { {
|
||||
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);
|
||||
buf[0] = i8088::next_ip($cpu.cs.get(), &mut $cpu.ip, $bus);
|
||||
buf[1] = i8088::next_ip($cpu.cs.get(), &mut $cpu.ip, $bus);
|
||||
step!(@arg $cookie, &LittleEndian::read_u16(&buf))
|
||||
} };
|
||||
|
||||
@@ -220,57 +237,56 @@ macro_rules! step {
|
||||
}
|
||||
} };
|
||||
|
||||
(@r16 $cookie:tt, $cpu:expr, $bus:expr, ($modrm:ident, $modrm16:tt, $modrm8:tt)) => { {
|
||||
(@r16 $cookie:tt, $cpu:expr, $bus:expr, ($modrm:ident, $modrm16:tt, $modrm8:tt)) => {
|
||||
// TODO: Should these also be passed into the macro like the modrm specs?
|
||||
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,
|
||||
match $modrm >> 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 } ),
|
||||
3 => step!(@arg $cookie, &mut Reg { reg: &$cpu.b } ),
|
||||
4 => step!(@arg $cookie, &mut Reg { reg: &$cpu.sp } ),
|
||||
5 => step!(@arg $cookie, &mut Reg { reg: &$cpu.bp } ),
|
||||
6 => step!(@arg $cookie, &mut Reg { reg: &$cpu.si } ),
|
||||
7 => step!(@arg $cookie, &mut Reg { reg: &$cpu.di } ),
|
||||
_ => unreachable!()
|
||||
};
|
||||
step!(@arg $cookie, reg)
|
||||
} };
|
||||
};
|
||||
|
||||
(@r8 $cookie:tt, $cpu:expr, $bus:expr, ($modrm:ident, $modrm16:tt, $modrm8:tt)) => {
|
||||
// TODO: Should these also be passed into the macro like the modrm specs?
|
||||
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 } ),
|
||||
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 } ),
|
||||
3 => step!(@arg $cookie, &mut RegHi { reg: &$cpu.b } ),
|
||||
4 => step!(@arg $cookie, &mut RegLo { reg: &$cpu.a } ),
|
||||
5 => step!(@arg $cookie, &mut RegLo { reg: &$cpu.c } ),
|
||||
6 => step!(@arg $cookie, &mut RegLo { reg: &$cpu.d } ),
|
||||
7 => step!(@arg $cookie, &mut RegLo { reg: &$cpu.b } ),
|
||||
_ => unreachable!()
|
||||
};
|
||||
};
|
||||
|
||||
(@reg=$reg:ident $cookie:tt, $cpu:expr, $bus:expr, $modrm:tt) => {
|
||||
step!(@arg $cookie, &mut $cpu.$reg);
|
||||
step!(@arg $cookie, &mut Reg { reg: &$cpu.$reg });
|
||||
};
|
||||
|
||||
(@reghi=$reg:ident $cookie:tt, $cpu:expr, $bus:expr, $modrm:tt) => {
|
||||
step!(@arg $cookie, &mut RegHi { reg: &mut $cpu.$reg });
|
||||
step!(@arg $cookie, &mut RegHi { reg: &$cpu.$reg });
|
||||
};
|
||||
|
||||
(@reglo=$reg:ident $cookie:tt, $cpu:expr, $bus:expr, $modrm:tt) => {
|
||||
step!(@arg $cookie, &mut RegLo { reg: &mut $cpu.$reg });
|
||||
step!(@arg $cookie, &mut RegLo { reg: &$cpu.$reg });
|
||||
};
|
||||
|
||||
(@regval=$reg:ident $cookie:tt, $cpu:expr, $bus:expr, $modrm:tt) => {
|
||||
step!(@arg $cookie, $cpu.$reg);
|
||||
step!(@arg $cookie, $cpu.$reg.get());
|
||||
};
|
||||
|
||||
(@rel16 $cookie:tt, $cpu:expr, $bus:expr, $modrm:tt) => { {
|
||||
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);
|
||||
buf[0] = i8088::next_ip($cpu.cs.get(), &mut $cpu.ip, $bus);
|
||||
buf[1] = i8088::next_ip($cpu.cs.get(), &mut $cpu.ip, $bus);
|
||||
step!(@arg $cookie, LittleEndian::read_i16(&buf))
|
||||
} };
|
||||
|
||||
@@ -303,17 +319,17 @@ macro_rules! step {
|
||||
modrm8: $modrm8:tt
|
||||
) => {
|
||||
{
|
||||
let opcode = i8088::next_ip($cpu.cs, &mut $cpu.ip, $bus);
|
||||
let opcode = i8088::next_ip($cpu.cs.get(), &mut $cpu.ip, $bus);
|
||||
let modrm: u8; // Type ascription unnecessary but gives better err messages when missing $ext
|
||||
match(opcode) {
|
||||
$( $( $code => {
|
||||
$( let $ext = (); /* No-op just to trigger expansion. */
|
||||
modrm = i8088::next_ip($cpu.cs, &mut $cpu.ip, $bus); )?
|
||||
modrm = i8088::next_ip($cpu.cs.get(), &mut $cpu.ip, $bus); )?
|
||||
step!(@code (), $cpu, $bus, (modrm, $modrm16, $modrm8), $name, $cycles, ($($args $(= $argrhs)?)*))
|
||||
}
|
||||
)?
|
||||
$( $code => {
|
||||
modrm = i8088::next_ip($cpu.cs, &mut $cpu.ip, $bus);
|
||||
modrm = i8088::next_ip($cpu.cs.get(), &mut $cpu.ip, $bus);
|
||||
step!(@group $cpu, $bus, (modrm, $modrm16, $modrm8), $code, $subcodes)
|
||||
}
|
||||
)?
|
||||
@@ -329,30 +345,30 @@ pub const fn segoff_to_addr(segment: u16, offset: u16) -> usize {
|
||||
segaddr + offset as usize
|
||||
}
|
||||
|
||||
pub fn read_hi(val: u16) -> u8 {
|
||||
pub fn read_hi(val: &Cell<u16>) -> u8 {
|
||||
let mut buf = [0; 2];
|
||||
LittleEndian::write_u16(&mut buf, val);
|
||||
LittleEndian::write_u16(&mut buf, val.get());
|
||||
buf[1] as u8
|
||||
}
|
||||
|
||||
pub fn read_lo(val: u16) -> u8 {
|
||||
pub fn read_lo(val: &Cell<u16>) -> u8 {
|
||||
let mut buf = [0; 2];
|
||||
LittleEndian::write_u16(&mut buf, val);
|
||||
LittleEndian::write_u16(&mut buf, val.get());
|
||||
buf[0] as u8
|
||||
}
|
||||
|
||||
pub fn write_hi(reg: &mut u16, val: u8) {
|
||||
pub fn write_hi(reg: &Cell<u16>, val: u8) {
|
||||
let mut buf = [0; 2];
|
||||
LittleEndian::write_u16(&mut buf, *reg);
|
||||
LittleEndian::write_u16(&mut buf, reg.get());
|
||||
buf[1] = val;
|
||||
*reg = LittleEndian::read_u16(&buf);
|
||||
reg.set(LittleEndian::read_u16(&buf))
|
||||
}
|
||||
|
||||
pub fn write_lo(reg: &mut u16, val: u8) {
|
||||
pub fn write_lo(reg: &Cell<u16>, val: u8) {
|
||||
let mut buf = [0; 2];
|
||||
LittleEndian::write_u16(&mut buf, *reg);
|
||||
LittleEndian::write_u16(&mut buf, reg.get());
|
||||
buf[1] = val;
|
||||
*reg = LittleEndian::read_u16(&buf);
|
||||
reg.set(LittleEndian::read_u16(&buf));
|
||||
}
|
||||
|
||||
impl i8088 {
|
||||
@@ -362,7 +378,7 @@ impl i8088 {
|
||||
opcodes: {
|
||||
0x60 => show[cpu] / 0, // Fake opcode for debugging
|
||||
0x61 => peek[cpu, bus, d16] / 0, // Fake opcode for debugging
|
||||
//0x8B _ => mov[r16, modrm16] / "2/12+",
|
||||
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,
|
||||
@@ -394,9 +410,9 @@ impl i8088 {
|
||||
}
|
||||
}
|
||||
|
||||
fn next_ip<'a>(cs: u16, ip: &mut u16, bus: &Bus) -> u8 {
|
||||
let eip = segoff_to_addr(cs, *ip);
|
||||
*ip += 1;
|
||||
fn next_ip<'a>(cs: u16, ip: &mut Cell<u16>, bus: &Bus) -> u8 {
|
||||
let eip = segoff_to_addr(cs, ip.get());
|
||||
ip.set(ip.get() + 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
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use std::cell::Cell;
|
||||
use emu::i8088::{i8088, segoff_to_addr};
|
||||
|
||||
use std::fmt::{Debug, Formatter};
|
||||
@@ -32,12 +33,12 @@ impl PC {
|
||||
"No memory for loading COM file");
|
||||
|
||||
let mut pc = PC {
|
||||
cpu: { i8088 { cs: LOAD_SEGMENT, // COMs always uses Tiny memory model
|
||||
ds: LOAD_SEGMENT, // COMs always uses Tiny memory model
|
||||
es: LOAD_SEGMENT, // COMs always uses Tiny memory model
|
||||
ss: LOAD_SEGMENT, // COMs always uses Tiny memory model
|
||||
sp: 0xFFFE,
|
||||
ip: LOAD_OFFSET,
|
||||
cpu: { i8088 { cs: Cell::new(LOAD_SEGMENT), // COMs always uses Tiny memory model
|
||||
ds: Cell::new(LOAD_SEGMENT), // COMs always uses Tiny memory model
|
||||
es: Cell::new(LOAD_SEGMENT), // COMs always uses Tiny memory model
|
||||
ss: Cell::new(LOAD_SEGMENT), // COMs always uses Tiny memory model
|
||||
sp: Cell::new(0xFFFE),
|
||||
ip: Cell::new(LOAD_OFFSET),
|
||||
..i8088::default() } },
|
||||
bus: Bus { ram: [0; RAM_SIZE] }
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user