emu: factor out operations and operands into own module
This commit is contained in:
213
src/emu/i8088.rs
213
src/emu/i8088.rs
@@ -3,6 +3,8 @@ use std::fmt::{Debug, Formatter};
|
|||||||
|
|
||||||
use super::byteorder::{ByteOrder, LittleEndian};
|
use super::byteorder::{ByteOrder, LittleEndian};
|
||||||
|
|
||||||
|
use emu::operands::{Addr, Reg, RegHi};
|
||||||
|
use emu::operations as ops;
|
||||||
use emu::pc::Bus;
|
use emu::pc::Bus;
|
||||||
|
|
||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
@@ -50,156 +52,6 @@ pub struct Flags {
|
|||||||
// bits 12-15 always 1
|
// bits 12-15 always 1
|
||||||
}
|
}
|
||||||
|
|
||||||
mod ops {
|
|
||||||
use emu::byteorder::{ByteOrder, LittleEndian};
|
|
||||||
use emu::dos;
|
|
||||||
use emu::i8088::{Address, Bus, LValue, Reg, RValue, i8088, segoff_to_addr};
|
|
||||||
|
|
||||||
pub fn show(cpu: &mut i8088) {
|
|
||||||
println!("{:#X?}", cpu);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn peek(addr: &(impl Address + RValue<u8>)) {
|
|
||||||
println!("PEEK: @{:#X} = {:#X} ({})", addr.addr(), addr.read(), addr.read());
|
|
||||||
}
|
|
||||||
|
|
||||||
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) {
|
|
||||||
match num {
|
|
||||||
0x21 => dos::interrupt(cpu, bus),
|
|
||||||
_ => unimplemented!("interrupt: {:02X}\ncpu: {:#X?}", num, cpu)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn mov<T>(dst: &mut impl LValue<T>, src: &impl RValue<T>) {
|
|
||||||
dst.write(src.read());
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn nop() {}
|
|
||||||
|
|
||||||
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 Reg, mem: &mut [u8], val: u16) {
|
|
||||||
// XXX: Not checking for stack faults or anything
|
|
||||||
sp.write(sp.read() - 2);
|
|
||||||
LittleEndian::write_u16(&mut mem[segoff_to_addr(ss.read(), sp.read())..], val);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn ret(ip: &mut Reg, ss: u16, sp: &mut Reg, mem: &[u8]) {
|
|
||||||
ip.write(pop16(ss, sp, mem));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait LValue<T> {
|
|
||||||
fn write(&mut self, val: T);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait RValue<T> {
|
|
||||||
fn read(&self) -> T;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait Address {
|
|
||||||
fn addr(&self) -> usize;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RValue<u8> for u8 {
|
|
||||||
fn read(&self) -> u8 {
|
|
||||||
*self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LValue<u16> for u16 {
|
|
||||||
fn write(&mut self, val: u16) {
|
|
||||||
*self = val;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RValue<u16> for u16 {
|
|
||||||
fn read(&self) -> u16 {
|
|
||||||
*self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Reg<'a> {
|
|
||||||
reg: &'a Cell<u16>
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LValue<u16> for Reg<'_> {
|
|
||||||
fn write(&mut self, val: u16) {
|
|
||||||
self.reg.set(val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RValue<u16> for Reg<'_> {
|
|
||||||
fn read(&self) -> u16 {
|
|
||||||
self.reg.get()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct RegHi<'a> {
|
|
||||||
reg: &'a Cell<u16>
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LValue<u8> for RegHi<'_> {
|
|
||||||
fn write(&mut self, val: u8) {
|
|
||||||
write_hi(&mut self.reg, val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RValue<u8> for RegHi<'_> {
|
|
||||||
fn read(&self) -> u8 {
|
|
||||||
read_hi(self.reg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Addr<'a> {
|
|
||||||
bus: &'a mut Bus,
|
|
||||||
addr: usize
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Address for Addr<'_> {
|
|
||||||
fn addr(&self) -> usize {
|
|
||||||
self.addr
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LValue<u8> for Addr<'_> {
|
|
||||||
fn write(&mut self, val: u8) {
|
|
||||||
self.bus.write(self.addr, val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RValue<u8> for Addr<'_> {
|
|
||||||
fn read(&self) -> u8 {
|
|
||||||
self.bus.read(self.addr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct RegLo<'a> {
|
|
||||||
reg: &'a Cell<u16>
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LValue<u8> for RegLo<'_> {
|
|
||||||
fn write(&mut self, val: u8) {
|
|
||||||
write_lo(&mut self.reg, val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RValue<u8> for RegLo<'_> {
|
|
||||||
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 ( $($done:tt)* ),
|
(@code ( $($done:tt)* ),
|
||||||
@@ -384,37 +236,6 @@ macro_rules! step {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const fn segoff_to_addr(segment: u16, offset: u16) -> usize {
|
|
||||||
let segaddr = (segment as usize) << 4;
|
|
||||||
segaddr + offset as usize
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn read_hi(val: &Cell<u16>) -> u8 {
|
|
||||||
let mut buf = [0; 2];
|
|
||||||
LittleEndian::write_u16(&mut buf, val.get());
|
|
||||||
buf[1] as u8
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn read_lo(val: &Cell<u16>) -> u8 {
|
|
||||||
let mut buf = [0; 2];
|
|
||||||
LittleEndian::write_u16(&mut buf, val.get());
|
|
||||||
buf[0] as u8
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn write_hi(reg: &Cell<u16>, val: u8) {
|
|
||||||
let mut buf = [0; 2];
|
|
||||||
LittleEndian::write_u16(&mut buf, reg.get());
|
|
||||||
buf[1] = val;
|
|
||||||
reg.set(LittleEndian::read_u16(&buf))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn write_lo(reg: &Cell<u16>, val: u8) {
|
|
||||||
let mut buf = [0; 2];
|
|
||||||
LittleEndian::write_u16(&mut buf, reg.get());
|
|
||||||
buf[1] = val;
|
|
||||||
reg.set(LittleEndian::read_u16(&buf));
|
|
||||||
}
|
|
||||||
|
|
||||||
impl i8088 {
|
impl i8088 {
|
||||||
pub fn run(&mut self, bus: &mut Bus) {
|
pub fn run(&mut self, bus: &mut Bus) {
|
||||||
loop {
|
loop {
|
||||||
@@ -481,6 +302,36 @@ impl i8088 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub const fn segoff_to_addr(segment: u16, offset: u16) -> usize {
|
||||||
|
let segaddr = (segment as usize) << 4;
|
||||||
|
segaddr + offset as usize
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_hi(val: &Cell<u16>) -> u8 {
|
||||||
|
let mut buf = [0; 2];
|
||||||
|
LittleEndian::write_u16(&mut buf, val.get());
|
||||||
|
buf[1] as u8
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_lo(val: &Cell<u16>) -> u8 {
|
||||||
|
let mut buf = [0; 2];
|
||||||
|
LittleEndian::write_u16(&mut buf, val.get());
|
||||||
|
buf[0] as u8
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_hi(reg: &Cell<u16>, val: u8) {
|
||||||
|
let mut buf = [0; 2];
|
||||||
|
LittleEndian::write_u16(&mut buf, reg.get());
|
||||||
|
buf[1] = val;
|
||||||
|
reg.set(LittleEndian::read_u16(&buf))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_lo(reg: &Cell<u16>, val: u8) {
|
||||||
|
let mut buf = [0; 2];
|
||||||
|
LittleEndian::write_u16(&mut buf, reg.get());
|
||||||
|
buf[1] = val;
|
||||||
|
reg.set(LittleEndian::read_u16(&buf));
|
||||||
|
}
|
||||||
|
|
||||||
impl From<u16> for Flags {
|
impl From<u16> for Flags {
|
||||||
fn from(flags: u16) -> Self {
|
fn from(flags: u16) -> Self {
|
||||||
|
|||||||
@@ -2,4 +2,6 @@ extern crate byteorder;
|
|||||||
|
|
||||||
pub mod dos;
|
pub mod dos;
|
||||||
pub mod i8088;
|
pub mod i8088;
|
||||||
|
mod operands;
|
||||||
|
mod operations;
|
||||||
pub mod pc;
|
pub mod pc;
|
||||||
|
|||||||
104
src/emu/operands.rs
Normal file
104
src/emu/operands.rs
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
use std::cell::Cell;
|
||||||
|
use emu::i8088::{read_hi, read_lo, write_hi, write_lo};
|
||||||
|
use emu::pc::Bus;
|
||||||
|
|
||||||
|
pub trait LValue<T> {
|
||||||
|
fn write(&mut self, val: T);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait RValue<T> {
|
||||||
|
fn read(&self) -> T;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Address {
|
||||||
|
fn addr(&self) -> usize;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RValue<u8> for u8 {
|
||||||
|
fn read(&self) -> u8 {
|
||||||
|
*self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LValue<u16> for u16 {
|
||||||
|
fn write(&mut self, val: u16) {
|
||||||
|
*self = val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RValue<u16> for u16 {
|
||||||
|
fn read(&self) -> u16 {
|
||||||
|
*self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Addr<'a> {
|
||||||
|
pub bus: &'a mut Bus,
|
||||||
|
pub addr: usize
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Address for Addr<'_> {
|
||||||
|
fn addr(&self) -> usize {
|
||||||
|
self.addr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LValue<u8> for Addr<'_> {
|
||||||
|
fn write(&mut self, val: u8) {
|
||||||
|
self.bus.write(self.addr, val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RValue<u8> for Addr<'_> {
|
||||||
|
fn read(&self) -> u8 {
|
||||||
|
self.bus.read(self.addr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Reg<'a> {
|
||||||
|
pub reg: &'a Cell<u16>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LValue<u16> for Reg<'_> {
|
||||||
|
fn write(&mut self, val: u16) {
|
||||||
|
self.reg.set(val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RValue<u16> for Reg<'_> {
|
||||||
|
fn read(&self) -> u16 {
|
||||||
|
self.reg.get()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct RegHi<'a> {
|
||||||
|
pub reg: &'a Cell<u16>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LValue<u8> for RegHi<'_> {
|
||||||
|
fn write(&mut self, val: u8) {
|
||||||
|
write_hi(&mut self.reg, val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RValue<u8> for RegHi<'_> {
|
||||||
|
fn read(&self) -> u8 {
|
||||||
|
read_hi(self.reg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct RegLo<'a> {
|
||||||
|
pub reg: &'a Cell<u16>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LValue<u8> for RegLo<'_> {
|
||||||
|
fn write(&mut self, val: u8) {
|
||||||
|
write_lo(&mut self.reg, val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RValue<u8> for RegLo<'_> {
|
||||||
|
fn read(&self) -> u8 {
|
||||||
|
read_lo(self.reg)
|
||||||
|
}
|
||||||
|
}
|
||||||
48
src/emu/operations.rs
Normal file
48
src/emu/operations.rs
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
use emu::byteorder::{ByteOrder, LittleEndian};
|
||||||
|
use emu::dos;
|
||||||
|
use emu::i8088::{i8088, segoff_to_addr};
|
||||||
|
use emu::operands::{Address, LValue, Reg, RValue};
|
||||||
|
use emu::pc::Bus;
|
||||||
|
|
||||||
|
pub fn show(cpu: &mut i8088) {
|
||||||
|
println!("{:#X?}", cpu);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn peek(addr: &(impl Address + RValue<u8>)) {
|
||||||
|
println!("PEEK: @{:#X} = {:#X} ({})", addr.addr(), addr.read(), addr.read());
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
match num {
|
||||||
|
0x21 => dos::interrupt(cpu, bus),
|
||||||
|
_ => unimplemented!("interrupt: {:02X}\ncpu: {:#X?}", num, cpu)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mov<T>(dst: &mut impl LValue<T>, src: &impl RValue<T>) {
|
||||||
|
dst.write(src.read());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn nop() {}
|
||||||
|
|
||||||
|
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 Reg, mem: &mut [u8], val: u16) {
|
||||||
|
// XXX: Not checking for stack faults or anything
|
||||||
|
sp.write(sp.read() - 2);
|
||||||
|
LittleEndian::write_u16(&mut mem[segoff_to_addr(ss.read(), sp.read())..], val);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ret(ip: &mut Reg, ss: u16, sp: &mut Reg, mem: &[u8]) {
|
||||||
|
ip.write(pop16(ss, sp, mem));
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user