altair_emu/src/cpu.rs
2023-08-10 13:58:34 -05:00

603 lines
21 KiB
Rust

mod opcode;
mod opcode_table;
use std::{
fmt::Display,
ops::{Index, IndexMut},
};
use bitflags::bitflags;
use opcode_table::OPCODE_TABLE;
use self::opcode::{Opcode, Register, RegisterPair};
bitflags! {
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Status: u8 {
const INTA = 0x1;
const WO = 0x2;
const STACK = 0x4;
const HLTA = 0x8;
const OUT = 0x10;
const M1 = 0x20;
const INP = 0x40;
const MEMR = 0x80;
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum MemCycle {
Fetch(u16),
Read(u16),
Write(u16, u8),
StackRead(u16),
StackWrite(u16, u8),
In(u16),
Out(u16, u8),
#[allow(unused)]
Inta(u16),
Hlta(u16),
#[allow(unused)]
IntaHlt(u16),
}
impl MemCycle {
pub fn get_status(self) -> Status {
match self {
Self::Fetch(_) => Status::WO | Status::M1 | Status::MEMR,
Self::Read(_) => Status::WO | Status::MEMR,
Self::Write(_, _) => Status::empty(),
Self::StackRead(_) => Status::WO | Status::MEMR | Status::STACK,
Self::StackWrite(_, _) => Status::STACK,
Self::In(_) => Status::WO | Status::INP,
Self::Out(_, _) => Status::OUT,
Self::Inta(_) => Status::INTA | Status::WO | Status::M1,
Self::Hlta(_) => Status::HLTA | Status::WO | Status::M1,
Self::IntaHlt(_) => Status::INTA | Status::HLTA | Status::WO | Status::M1,
}
}
pub fn address(self) -> u16 {
match self {
Self::Fetch(a) => a,
Self::Read(a) => a,
Self::Write(a, _) => a,
Self::StackRead(a) => a,
Self::StackWrite(a, _) => a,
Self::In(a) => a,
Self::Out(a, _) => a,
Self::Inta(a) => a,
Self::Hlta(a) => a,
Self::IntaHlt(a) => a,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum MCycle {
M1,
M2,
M3,
M4,
M5,
}
impl MCycle {
fn next(self) -> Self {
match self {
Self::M1 => Self::M2,
Self::M2 => Self::M3,
Self::M3 => Self::M4,
Self::M4 => Self::M5,
Self::M5 => panic!(),
}
}
}
impl Display for MCycle {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::M1 => f.write_str("M1"),
Self::M2 => f.write_str("M2"),
Self::M3 => f.write_str("M3"),
Self::M4 => f.write_str("M4"),
Self::M5 => f.write_str("M5"),
}
}
}
#[derive(Clone, Debug)]
struct RegisterFile {
b: u8,
c: u8,
d: u8,
e: u8,
h: u8,
l: u8,
a: u8,
}
impl RegisterFile {
fn new() -> Self {
Self {
b: rand::random(),
c: rand::random(),
d: rand::random(),
e: rand::random(),
h: rand::random(),
l: rand::random(),
a: rand::random(),
}
}
}
impl Index<Register> for RegisterFile {
type Output = u8;
fn index(&self, index: Register) -> &Self::Output {
match index {
Register::B => &self.b,
Register::C => &self.c,
Register::D => &self.d,
Register::E => &self.e,
Register::H => &self.h,
Register::L => &self.l,
Register::A => &self.a,
}
}
}
impl IndexMut<Register> for RegisterFile {
fn index_mut(&mut self, index: Register) -> &mut Self::Output {
match index {
Register::B => &mut self.b,
Register::C => &mut self.c,
Register::D => &mut self.d,
Register::E => &mut self.e,
Register::H => &mut self.h,
Register::L => &mut self.l,
Register::A => &mut self.a,
}
}
}
impl Default for RegisterFile {
fn default() -> Self {
Self::new()
}
}
#[derive(Clone, Debug)]
pub struct I8080 {
pc: u16,
regs: RegisterFile,
sp: u16,
#[allow(unused)]
sign: bool,
#[allow(unused)]
zero: bool,
#[allow(unused)]
parity: bool,
#[allow(unused)]
carry: bool,
#[allow(unused)]
aux_carry: bool,
cycle: MCycle,
opcode: Opcode,
w: u8,
z: u8,
tmp: u8,
halted: bool,
}
impl Display for I8080 {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_fmt(format_args!(
"PC: {:#x} A: {:#x}, B: {:#x}, C: {:#x}, D: {:#x}, E: {:#x}, H: {:#x}, L: {:#x}, SP: {:#x}",
self.pc,
self.regs.a,
self.regs.b,
self.regs.c,
self.regs.d,
self.regs.e,
self.regs.h,
self.regs.l,
self.sp
))
}
}
impl I8080 {
pub fn new() -> Self {
Self {
pc: rand::random(),
regs: RegisterFile::new(),
sp: rand::random(),
sign: rand::random(),
zero: rand::random(),
parity: rand::random(),
carry: rand::random(),
aux_carry: rand::random(),
cycle: MCycle::M1,
opcode: Opcode::Nop,
w: rand::random(),
z: rand::random(),
tmp: rand::random(),
halted: rand::random(),
}
}
pub fn reset(&mut self) {
self.pc = 0;
self.halted = false;
}
pub fn get_mem_cycle(&self) -> MemCycle {
if self.halted {
return MemCycle::Hlta(self.pc);
};
match self.cycle {
MCycle::M1 => MemCycle::Fetch(self.pc),
MCycle::M2 => match self.opcode {
Opcode::MovMR(src) => {
MemCycle::Write(self.get_pair(RegisterPair::HL), self.regs[src])
}
Opcode::MovRM(_) => MemCycle::Read(self.get_pair(RegisterPair::HL)),
Opcode::Mvi(_) => MemCycle::Read(self.pc),
Opcode::Lxi(_) => MemCycle::Read(self.pc),
Opcode::Lda => MemCycle::Read(self.pc),
Opcode::Sta => MemCycle::Read(self.pc),
Opcode::Lhld => MemCycle::Read(self.pc),
Opcode::Shld => MemCycle::Read(self.pc),
Opcode::Ldax(rp) => MemCycle::Read(self.get_pair(rp)),
Opcode::Stax(rp) => MemCycle::Read(self.get_pair(rp)),
Opcode::Add(_) => MemCycle::Read(self.get_pair(RegisterPair::HL)),
Opcode::Adi => MemCycle::Read(self.pc),
Opcode::Adc(_) => MemCycle::Read(self.get_pair(RegisterPair::HL)),
Opcode::Aci => MemCycle::Read(self.pc),
Opcode::Sub(_) => MemCycle::Read(self.get_pair(RegisterPair::HL)),
Opcode::Sui => MemCycle::Read(self.pc),
Opcode::Sbb(_) => MemCycle::Read(self.get_pair(RegisterPair::HL)),
Opcode::Sbi => MemCycle::Read(self.pc),
Opcode::Inr(_) => MemCycle::Read(self.get_pair(RegisterPair::HL)),
Opcode::Dcr(_) => MemCycle::Read(self.get_pair(RegisterPair::HL)),
Opcode::Ana(_) => MemCycle::Read(self.get_pair(RegisterPair::HL)),
Opcode::Ani => MemCycle::Read(self.pc),
Opcode::Xra(_) => MemCycle::Read(self.get_pair(RegisterPair::HL)),
Opcode::Xri => MemCycle::Read(self.pc),
Opcode::Ora(_) => MemCycle::Read(self.get_pair(RegisterPair::HL)),
Opcode::Ori => MemCycle::Read(self.pc),
Opcode::Cmp(_) => MemCycle::Read(self.get_pair(RegisterPair::HL)),
Opcode::Cpi => MemCycle::Read(self.pc),
Opcode::Jmp => MemCycle::Read(self.pc),
Opcode::Jcc(_) => MemCycle::Read(self.pc),
Opcode::Call => MemCycle::Read(self.pc),
Opcode::Ccc(_) => MemCycle::Read(self.pc),
Opcode::Ret => MemCycle::Read(self.sp),
Opcode::Rcc(_) => MemCycle::StackRead(self.sp),
Opcode::Rst(_) => MemCycle::StackWrite(self.sp, (self.pc >> 8) as u8),
Opcode::Push(_) => MemCycle::StackRead(self.sp),
Opcode::Pop(_) => MemCycle::StackRead(self.sp),
Opcode::Xthl => MemCycle::StackRead(self.sp),
Opcode::In => MemCycle::Read(self.pc),
Opcode::Out => MemCycle::Read(self.pc),
_ => unreachable!(),
},
MCycle::M3 => match self.opcode {
Opcode::Mvi(_) => MemCycle::Write(self.get_pair(RegisterPair::HL), self.tmp),
Opcode::Lxi(_) => MemCycle::Read(self.pc),
Opcode::Lda => MemCycle::Read(self.pc),
Opcode::Sta => MemCycle::Read(self.pc),
Opcode::Lhld => MemCycle::Read(self.pc),
Opcode::Shld => MemCycle::Read(self.pc),
Opcode::Inr(_) => MemCycle::Write(self.get_pair(RegisterPair::HL), self.tmp),
Opcode::Dcr(_) => MemCycle::Write(self.get_pair(RegisterPair::HL), self.tmp),
Opcode::Jmp => MemCycle::Read(self.pc),
Opcode::Jcc(_) => MemCycle::Read(self.pc),
Opcode::Call => MemCycle::Read(self.pc),
Opcode::Ccc(_) => MemCycle::Read(self.pc),
Opcode::Ret => MemCycle::StackRead(self.sp),
Opcode::Rcc(_) => MemCycle::StackRead(self.sp),
Opcode::Rst(_) => MemCycle::StackWrite(self.sp, 0),
Opcode::Xthl => MemCycle::StackRead(self.sp),
Opcode::In => MemCycle::In(self.get_wz()),
Opcode::Out => MemCycle::Out(self.get_wz(), self.regs.a),
_ => unreachable!(),
},
MCycle::M4 => match self.opcode {
Opcode::Lda => MemCycle::Read(self.get_wz()),
Opcode::Sta => MemCycle::Write(self.get_wz(), self.regs.a),
Opcode::Lhld => MemCycle::Read(self.get_wz()),
Opcode::Shld => MemCycle::Write(self.get_wz(), self.regs.l),
Opcode::Call => MemCycle::StackWrite(self.sp, (self.pc >> 8) as u8),
Opcode::Ccc(_) => MemCycle::StackWrite(self.sp, (self.pc >> 8) as u8),
Opcode::Xthl => MemCycle::StackWrite(self.sp, self.regs.h),
_ => unreachable!(),
},
MCycle::M5 => match self.opcode {
Opcode::Lhld => MemCycle::Read(self.get_wz()),
Opcode::Shld => MemCycle::Write(self.get_wz(), self.regs.h),
Opcode::Call => MemCycle::StackWrite(self.sp, self.pc as u8),
Opcode::Ccc(_) => MemCycle::StackWrite(self.sp, self.pc as u8),
Opcode::Xthl => MemCycle::StackWrite(self.sp, self.regs.l),
_ => unreachable!(),
},
}
}
pub fn finish_m_cycle(&mut self, data: u8) {
if self.halted {
return;
}
println!(
"Finish {} ({:?}), got data {}",
self.cycle,
self.get_mem_cycle(),
data
);
match self.cycle {
MCycle::M1 => {
self.pc += 1;
self.opcode = OPCODE_TABLE[data as usize];
dbg!(self.opcode);
match self.opcode {
Opcode::MovMR(_) => (),
Opcode::MovRM(_) => (),
Opcode::Mov(dst, src) => {
self.regs[dst] = self.regs[src];
}
Opcode::Sphl => {
self.sp = ((self.regs.h as u16) << 8) | (self.regs.l as u16);
}
Opcode::MviM => (),
Opcode::Mvi(_) => (),
Opcode::Lxi(_) => (),
Opcode::Lda => (),
Opcode::Sta => (),
Opcode::Lhld => (),
Opcode::Shld => (),
Opcode::Ldax(_) => (),
Opcode::Stax(_) => (),
Opcode::Xchg => {
(self.regs.d, self.regs.e, self.regs.h, self.regs.l) =
(self.regs.h, self.regs.l, self.regs.d, self.regs.e);
}
Opcode::AddM => (),
Opcode::Add(src) => {
let a = self.regs.a;
let b = self.regs[src];
let res = (a as u16) + (b as u16);
self.sign = ((res as u8) & 0x80) > 0;
self.zero = (res as u8) == 0;
self.aux_carry = (a & 0x0f) + (b & 0x0f) > 0x0f;
self.parity = (res as u8).count_ones() % 2 == 0;
self.carry = res > 0xff;
self.regs.a = res as u8;
}
Opcode::Adi => (),
Opcode::AdcM => (),
Opcode::Adc(_) => todo!(),
Opcode::Aci => (),
Opcode::SubM => (),
Opcode::Sub(_) => todo!(),
Opcode::Sui => (),
Opcode::SbbM => (),
Opcode::Sbb(_) => todo!(),
Opcode::Sbi => (),
Opcode::InrM => (),
Opcode::Inr(_) => todo!(),
Opcode::DcrM => (),
Opcode::Dcr(_) => todo!(),
Opcode::Inx(dst) => self.set_pair(dst, self.get_pair(dst) + 1),
Opcode::Dcx(dst) => self.set_pair(dst, self.get_pair(dst) + 2),
Opcode::Dad(src) => {
let a = self.get_pair(RegisterPair::HL) as u32;
let b = self.get_pair(src) as u32;
let res = a + b;
self.carry = res > 0xffff;
self.set_pair(RegisterPair::HL, res as u16);
}
Opcode::Daa => todo!(),
Opcode::AnaM => (),
Opcode::Ana(_) => todo!(),
Opcode::Ani => (),
Opcode::XraM => (),
Opcode::Xra(_) => todo!(),
Opcode::Xri => (),
Opcode::OraM => (),
Opcode::Ora(_) => todo!(),
Opcode::Ori => (),
Opcode::CmpM => (),
Opcode::Cmp(_) => todo!(),
Opcode::Cpi => (),
Opcode::Rlc => todo!(),
Opcode::Rrc => todo!(),
Opcode::Ral => todo!(),
Opcode::Rar => todo!(),
Opcode::Cma => todo!(),
Opcode::Cmc => todo!(),
Opcode::Stc => todo!(),
Opcode::Jmp => (),
Opcode::Jcc(_) => todo!(),
Opcode::Call => todo!(),
Opcode::Ccc(_) => todo!(),
Opcode::Ret => (),
Opcode::Rcc(_) => todo!(),
Opcode::Rst(_) => todo!(),
Opcode::Pchl => todo!(),
Opcode::Push(_) => todo!(),
Opcode::Pop(_) => (),
Opcode::Xthl => (),
Opcode::In => (),
Opcode::Out => (),
Opcode::Ei => todo!(),
Opcode::Di => todo!(),
Opcode::Hlt => self.halted = true,
Opcode::Nop => (),
}
}
MCycle::M2 => match self.opcode {
Opcode::MovMR(_) => (),
Opcode::MovRM(dst) => self.regs[dst] = data,
Opcode::MviM => {
self.pc += 1;
self.tmp = data;
}
Opcode::Mvi(dst) => {
self.regs[dst] = data;
}
Opcode::Lxi(_) => todo!(),
Opcode::Lda => {
self.z = data;
self.pc += 1;
}
Opcode::Sta => {
self.z = data;
self.pc += 1;
}
Opcode::Lhld => todo!(),
Opcode::Shld => todo!(),
Opcode::Ldax(_) => todo!(),
Opcode::Stax(_) => todo!(),
Opcode::AddM => {
let a = self.regs.a;
let b = data;
let res = (a as u16) + (b as u16);
self.sign = ((res as u8) & 0x80) > 0;
self.zero = (res as u8) == 0;
self.aux_carry = (a & 0x0f) + (b & 0x0f) > 0x0f;
self.parity = (res as u8).count_ones() % 2 == 0;
self.carry = res > 0xff;
self.regs.a = res as u8;
}
Opcode::Adi => todo!(),
Opcode::AdcM => todo!(),
Opcode::Aci => todo!(),
Opcode::SubM => todo!(),
Opcode::Sui => todo!(),
Opcode::SbbM => todo!(),
Opcode::Sbi => todo!(),
Opcode::InrM => todo!(),
Opcode::DcrM => todo!(),
Opcode::AnaM => todo!(),
Opcode::Ani => todo!(),
Opcode::XraM => todo!(),
Opcode::Xri => todo!(),
Opcode::OraM => todo!(),
Opcode::Ori => todo!(),
Opcode::CmpM => todo!(),
Opcode::Cpi => todo!(),
Opcode::Jmp => {
self.z = data;
self.pc += 1;
}
Opcode::Jcc(_) => todo!(),
Opcode::Call => todo!(),
Opcode::Ccc(_) => todo!(),
Opcode::Ret => todo!(),
Opcode::Rcc(_) => todo!(),
Opcode::Rst(_) => todo!(),
Opcode::Push(_) => todo!(),
Opcode::Pop(_) => todo!(),
Opcode::Xthl => {
self.sp += 1;
self.z = data;
}
Opcode::In => todo!(),
Opcode::Out => todo!(),
Opcode::Hlt => todo!(),
_ => unreachable!(),
},
MCycle::M3 => match self.opcode {
Opcode::MviM => (),
Opcode::Lxi(_) => todo!(),
Opcode::Lda => {
self.w = data;
self.pc += 1;
}
Opcode::Sta => {
self.w = data;
self.pc += 1;
}
Opcode::Lhld => todo!(),
Opcode::Shld => {
self.set_wz(self.get_wz() + 1);
}
Opcode::Inr(_) => todo!(),
Opcode::Dcr(_) => todo!(),
Opcode::Jmp => {
self.w = data;
self.pc = self.get_wz();
}
Opcode::Jcc(_) => todo!(),
Opcode::Call => todo!(),
Opcode::Ccc(_) => todo!(),
Opcode::Ret => todo!(),
Opcode::Rcc(_) => todo!(),
Opcode::Rst(_) => todo!(),
_ => unreachable!(),
},
MCycle::M4 => match self.opcode {
Opcode::Lda => {
self.regs.a = data;
}
Opcode::Sta => (),
Opcode::Lhld => todo!(),
Opcode::Shld => todo!(),
Opcode::Call => todo!(),
Opcode::Ccc(_) => todo!(),
Opcode::Xthl => (),
_ => unreachable!(),
},
MCycle::M5 => match self.opcode {
Opcode::Call => todo!(),
Opcode::Ccc(_) => todo!(),
Opcode::Xthl => (),
_ => unreachable!(),
},
}
if self.cycle == self.opcode.max_m_cycle() {
println!("Instruction done");
self.cycle = MCycle::M1;
} else {
self.cycle = self.cycle.next();
}
}
fn get_pair(&self, pair: RegisterPair) -> u16 {
match pair {
RegisterPair::BC => ((self.regs.b as u16) << 8) | (self.regs.c as u16),
RegisterPair::DE => ((self.regs.d as u16) << 8) | (self.regs.e as u16),
RegisterPair::HL => ((self.regs.h as u16) << 8) | (self.regs.l as u16),
RegisterPair::SP => self.sp,
}
}
fn get_wz(&self) -> u16 {
((self.w as u16) << 8) | (self.z as u16)
}
fn set_pair(&mut self, pair: RegisterPair, val: u16) {
match pair {
RegisterPair::BC => {
self.regs.b = (val >> 8) as u8;
self.regs.c = val as u8;
}
RegisterPair::DE => {
self.regs.d = (val >> 8) as u8;
self.regs.e = val as u8;
}
RegisterPair::HL => {
self.regs.h = (val >> 8) as u8;
self.regs.l = val as u8;
}
RegisterPair::SP => self.sp = val,
}
}
fn set_wz(&mut self, val: u16) {
self.w = (val >> 8) as u8;
self.z = val as u8;
}
}