1737 lines
60 KiB
Rust
1737 lines
60 KiB
Rust
use crate::interconnect;
|
|
|
|
use std::convert::TryFrom;
|
|
use std::thread::sleep;
|
|
use std::time::{Duration, Instant};
|
|
|
|
const REG_A: usize = 6;
|
|
|
|
const REG_N_B: usize = 0;
|
|
const REG_N_C: usize = 1;
|
|
const REG_N_D: usize = 2;
|
|
const REG_N_E: usize = 3;
|
|
const REG_N_H: usize = 4;
|
|
const REG_N_L: usize = 5;
|
|
const REG_N_HL: usize = 6;
|
|
const REG_N_A: usize = 7;
|
|
const REG_N_F: usize = 8;
|
|
const REG_NAMES: [&str; 9] = ["B", "C", "D", "E", "H", "L", "(HL)", "A", "F"];
|
|
|
|
const FLAG_Z: u8 = 1 << 7;
|
|
const FLAG_N: u8 = 1 << 6;
|
|
const FLAG_H: u8 = 1 << 5;
|
|
const FLAG_C: u8 = 1 << 4;
|
|
|
|
use interconnect::TickResult;
|
|
pub enum CpuTickResult {
|
|
Continue { cycles_executed: usize },
|
|
Shutdown { cycles_executed: usize },
|
|
}
|
|
|
|
pub struct CPU {
|
|
// Registers: B, C, D, E, H, L, A
|
|
regs: [u8; 7],
|
|
flags: u8,
|
|
ip: u16,
|
|
sp: u16,
|
|
|
|
interconnect: interconnect::Interconnect,
|
|
|
|
ime: bool,
|
|
debug: bool,
|
|
|
|
halted: bool,
|
|
|
|
trigger_once: bool,
|
|
}
|
|
|
|
enum Args {
|
|
Single(u8),
|
|
Double(u8, u8),
|
|
}
|
|
|
|
impl TryFrom<Args> for u8 {
|
|
type Error = ();
|
|
|
|
fn try_from(val: Args) -> Result<u8, Self::Error> {
|
|
match val {
|
|
Args::Single(x) => Ok(x),
|
|
_ => Err(()),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl TryFrom<Args> for u16 {
|
|
type Error = ();
|
|
|
|
fn try_from(val: Args) -> Result<u16, Self::Error> {
|
|
match val {
|
|
Args::Double(a, b) => Ok((a as u16) | ((b as u16) << 8)),
|
|
_ => Err(()),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl CPU {
|
|
pub fn new(interconnect: interconnect::Interconnect) -> CPU {
|
|
CPU {
|
|
flags: 0,
|
|
regs: [0, 0, 0, 0, 0, 0, 0],
|
|
ip: 0,
|
|
sp: 0xFFFE,
|
|
interconnect,
|
|
ime: false,
|
|
debug: false,
|
|
halted: false,
|
|
|
|
trigger_once: false,
|
|
}
|
|
}
|
|
|
|
fn read_byte(&self, addr: u16) -> u8 {
|
|
self.interconnect.read_byte(addr)
|
|
}
|
|
|
|
fn load_args(&mut self, num_args: u8) -> Args {
|
|
match num_args {
|
|
1 => {
|
|
let val = self.read_byte(self.ip);
|
|
self.ip += 1;
|
|
Args::Single(val)
|
|
}
|
|
2 => {
|
|
let b1 = self.read_byte(self.ip);
|
|
self.ip += 1;
|
|
let b2 = self.read_byte(self.ip);
|
|
self.ip += 1;
|
|
Args::Double(b1, b2)
|
|
}
|
|
_ => panic!("load_args only supports two bytes"),
|
|
}
|
|
}
|
|
|
|
fn set_8bit_reg(&mut self, reg_id: usize, value: u8) {
|
|
// Make sure that we skip the (HL) part.
|
|
if reg_id == REG_N_A {
|
|
self.regs[REG_A] = value;
|
|
} else if reg_id == REG_N_F {
|
|
self.flags = value & 0xF0;
|
|
} else if reg_id == REG_N_HL {
|
|
let addr: u16 = self.get_pair_value(REG_N_H, REG_N_L);
|
|
self.interconnect.write_byte(addr, value);
|
|
} else {
|
|
self.regs[reg_id] = value;
|
|
}
|
|
}
|
|
|
|
fn get_8bit_reg(&self, reg_id: usize) -> u8 {
|
|
// Make sure that we skip the (HL) part.
|
|
if reg_id == REG_N_A {
|
|
self.regs[REG_A]
|
|
} else if reg_id == REG_N_F {
|
|
self.flags
|
|
} else if reg_id == REG_N_HL {
|
|
let addr: u16 = self.get_pair_value(REG_N_H, REG_N_L);
|
|
self.interconnect.read_byte(addr)
|
|
} else {
|
|
self.regs[reg_id]
|
|
}
|
|
}
|
|
|
|
fn adc_r(&mut self, val: u8) {
|
|
let old: u8 = self.regs[REG_A];
|
|
let mut new: u8 = old;
|
|
let c: u8;
|
|
if self.flags & FLAG_C == FLAG_C {
|
|
c = 1;
|
|
} else {
|
|
c = 0;
|
|
}
|
|
new = new.wrapping_add(val);
|
|
new = new.wrapping_add(c);
|
|
self.regs[REG_A] = new;
|
|
|
|
self.clear_flag(FLAG_N);
|
|
self.set_clear_flag(FLAG_Z, new == 0);
|
|
self.set_clear_flag(FLAG_C, (new < old) || (c == 1 && new == old));
|
|
self.set_clear_flag(FLAG_H, ((old & 0x0F) + (val & 0x0F) + c) > 0x0F);
|
|
}
|
|
|
|
fn add_r(&mut self, val: u8) {
|
|
let old: u8 = self.regs[REG_A];
|
|
let new: u8 = old.wrapping_add(val);
|
|
let carry: u16 = (old as u16) ^ (val as u16) ^ (new as u16);
|
|
self.regs[REG_A] = new;
|
|
|
|
self.clear_flag(FLAG_N);
|
|
self.set_clear_flag(FLAG_Z, new == 0);
|
|
self.set_clear_flag(FLAG_C, val > 0 && new < old);
|
|
self.set_clear_flag(FLAG_H, carry & 0x10 == 0x10);
|
|
}
|
|
|
|
fn sbc_r(&mut self, val: u8) {
|
|
let old: u8 = self.regs[REG_A];
|
|
let mut new: u8 = old as u8;
|
|
let c: u8;
|
|
if self.flags & FLAG_C == FLAG_C {
|
|
c = 1;
|
|
} else {
|
|
c = 0;
|
|
}
|
|
new = new.wrapping_sub(val);
|
|
new = new.wrapping_sub(c);
|
|
|
|
self.regs[REG_A] = new;
|
|
|
|
self.set_flag(FLAG_N);
|
|
self.set_clear_flag(FLAG_Z, new == 0);
|
|
self.set_clear_flag(FLAG_C, new > old || (new == old && c == 1));
|
|
self.set_clear_flag(
|
|
FLAG_H,
|
|
((old & 0x0F).wrapping_sub(val & 0x0F).wrapping_sub(c)) > 0x0F,
|
|
);
|
|
}
|
|
|
|
fn sub_r(&mut self, val: u8) {
|
|
let old: u8 = self.regs[REG_A];
|
|
let new: u8 = old.wrapping_sub(val);
|
|
let carry: u16 = (old as u16) ^ (val as u16) ^ (new as u16);
|
|
self.regs[REG_A] = new;
|
|
|
|
self.set_flag(FLAG_N);
|
|
self.set_clear_flag(FLAG_Z, new == 0);
|
|
self.set_clear_flag(FLAG_C, new > old);
|
|
self.set_clear_flag(FLAG_H, carry & 0x10 == 0x10);
|
|
}
|
|
|
|
fn cp_r(&mut self, val: u8) {
|
|
let old: u8 = self.regs[REG_A];
|
|
let new: u8 = old.wrapping_sub(val);
|
|
let carry: u16 = (old as u16) ^ (val as u16) ^ (new as u16);
|
|
|
|
self.set_flag(FLAG_N);
|
|
self.set_clear_flag(FLAG_Z, new == 0);
|
|
self.set_clear_flag(FLAG_C, new > old);
|
|
self.set_clear_flag(FLAG_H, carry & 0x10 == 0x10);
|
|
}
|
|
|
|
fn run_prefix_instruction(&mut self) {
|
|
let instruction = self.read_byte(self.ip);
|
|
self.ip += 1;
|
|
|
|
match instruction {
|
|
0x00..=0x07 => {
|
|
let reg_id = (instruction - 0x00) as usize;
|
|
let val = self.get_8bit_reg(reg_id);
|
|
if self.debug {
|
|
println!("RLC {}", REG_NAMES[reg_id]);
|
|
}
|
|
|
|
let nval: u8;
|
|
if val & 0x80 == 0x80 {
|
|
nval = val << 1 | 1;
|
|
} else {
|
|
nval = val << 1;
|
|
}
|
|
self.set_8bit_reg(reg_id, nval);
|
|
|
|
self.set_clear_flag(FLAG_C, val & 0x80 == 0x80);
|
|
self.set_clear_flag(FLAG_Z, nval == 0);
|
|
self.clear_flag(FLAG_N);
|
|
self.clear_flag(FLAG_H);
|
|
}
|
|
0x08..=0x0F => {
|
|
let reg_id = (instruction - 0x08) as usize;
|
|
let val = self.get_8bit_reg(reg_id);
|
|
if self.debug {
|
|
println!("RRC {}", REG_NAMES[reg_id]);
|
|
}
|
|
|
|
self.set_clear_flag(FLAG_C, val & 1 == 1);
|
|
if val & 1 > 0 {
|
|
self.set_8bit_reg(reg_id, val >> 1 | 0x80);
|
|
} else {
|
|
self.set_8bit_reg(reg_id, val >> 1);
|
|
}
|
|
|
|
self.set_clear_flag(FLAG_Z, val == 0);
|
|
self.clear_flag(FLAG_N);
|
|
self.clear_flag(FLAG_H);
|
|
}
|
|
0x10..=0x17 => {
|
|
let reg_id = (instruction - 0x10) as usize;
|
|
let val = self.get_8bit_reg(reg_id);
|
|
if self.debug {
|
|
println!("RL {}", REG_NAMES[reg_id]);
|
|
}
|
|
|
|
let carry = self.flags & FLAG_C > 0;
|
|
let nval: u8;
|
|
if !carry {
|
|
nval = val << 1;
|
|
} else {
|
|
nval = val << 1 | 1;
|
|
}
|
|
self.set_8bit_reg(reg_id, nval);
|
|
self.set_clear_flag(FLAG_C, val & 0x80 == 0x80);
|
|
self.set_clear_flag(FLAG_Z, nval == 0);
|
|
self.clear_flag(FLAG_N);
|
|
self.clear_flag(FLAG_H);
|
|
}
|
|
0x18..=0x1F => {
|
|
// RR
|
|
let reg_id = (instruction - 0x18) as usize;
|
|
let val = self.get_8bit_reg(reg_id);
|
|
if self.debug {
|
|
println!("RR {}", REG_NAMES[reg_id]);
|
|
}
|
|
|
|
let carry = self.flags & FLAG_C > 0;
|
|
let v: u8;
|
|
if !carry {
|
|
v = val >> 1;
|
|
} else {
|
|
v = (val >> 1) | 0x80;
|
|
}
|
|
self.set_8bit_reg(reg_id, v);
|
|
self.set_clear_flag(FLAG_C, val & 1 == 1);
|
|
self.set_clear_flag(FLAG_Z, v == 0);
|
|
self.clear_flag(FLAG_N);
|
|
self.clear_flag(FLAG_H);
|
|
}
|
|
0x20..=0x27 => {
|
|
let reg_id = (instruction - 0x20) as usize;
|
|
if self.debug {
|
|
println!("SLA {}", REG_NAMES[reg_id]);
|
|
}
|
|
let v: u8 = self.get_8bit_reg(reg_id);
|
|
self.flags = 0;
|
|
self.set_clear_flag(FLAG_C, v & 0x80 == 0x80);
|
|
self.set_clear_flag(FLAG_Z, v & 0x7F == 0);
|
|
self.set_8bit_reg(reg_id, v << 1);
|
|
}
|
|
0x28..=0x2F => {
|
|
let reg_id = (instruction - 0x28) as usize;
|
|
if self.debug {
|
|
println!("SRA {}", REG_NAMES[reg_id]);
|
|
}
|
|
let v: u8 = self.get_8bit_reg(reg_id);
|
|
let nv = (v >> 1) | (v & 0x80);
|
|
self.set_8bit_reg(reg_id, nv);
|
|
self.flags = 0;
|
|
self.set_clear_flag(FLAG_Z, nv == 0);
|
|
self.set_clear_flag(FLAG_C, v & 1 == 1);
|
|
}
|
|
0x30..=0x37 => {
|
|
let reg_id = (instruction - 0x30) as usize;
|
|
if self.debug {
|
|
println!("SWAP {}", REG_NAMES[reg_id]);
|
|
}
|
|
let v: u8 = self.get_8bit_reg(reg_id);
|
|
self.set_8bit_reg(reg_id, (v << 4) | (v >> 4));
|
|
self.flags = 0;
|
|
self.set_clear_flag(FLAG_Z, v == 0);
|
|
}
|
|
0x38..=0x3F => {
|
|
let reg_id = (instruction - 0x38) as usize;
|
|
if self.debug {
|
|
println!("SRL {}", REG_NAMES[reg_id]);
|
|
}
|
|
let v: u8 = self.get_8bit_reg(reg_id);
|
|
self.set_8bit_reg(reg_id, v >> 1);
|
|
self.set_clear_flag(FLAG_C, v & 1 == 1);
|
|
self.clear_flag(FLAG_N);
|
|
self.clear_flag(FLAG_H);
|
|
self.set_clear_flag(FLAG_Z, (v & 0xFE) == 0);
|
|
}
|
|
|
|
// Bits
|
|
0x40..=0x47 => {
|
|
// Test 0th bit
|
|
let reg_id = (instruction - 0x40) as usize;
|
|
let reg_content = self.get_8bit_reg(reg_id);
|
|
if self.debug {
|
|
println!("BIT 0, {}", REG_NAMES[reg_id]);
|
|
}
|
|
self.set_clear_flag(FLAG_Z, reg_content & (1 << 0) == 0);
|
|
self.clear_flag(FLAG_N);
|
|
self.set_flag(FLAG_H);
|
|
}
|
|
0x48..=0x4F => {
|
|
// Test 1th bit
|
|
let reg_id = (instruction - 0x48) as usize;
|
|
let reg_content = self.get_8bit_reg(reg_id);
|
|
if self.debug {
|
|
println!("BIT 1, {}", REG_NAMES[reg_id]);
|
|
}
|
|
self.set_clear_flag(FLAG_Z, reg_content & (1 << 1) == 0);
|
|
self.clear_flag(FLAG_N);
|
|
self.set_flag(FLAG_H);
|
|
}
|
|
0x50..=0x57 => {
|
|
// Test 2th bit
|
|
let reg_id = (instruction - 0x50) as usize;
|
|
let reg_content = self.get_8bit_reg(reg_id);
|
|
if self.debug {
|
|
println!("BIT 2, {}", REG_NAMES[reg_id]);
|
|
}
|
|
self.set_clear_flag(FLAG_Z, reg_content & (1 << 2) == 0);
|
|
self.clear_flag(FLAG_N);
|
|
self.set_flag(FLAG_H);
|
|
}
|
|
0x58..=0x5F => {
|
|
// Test 3th bit
|
|
let reg_id = (instruction - 0x58) as usize;
|
|
let reg_content = self.get_8bit_reg(reg_id);
|
|
if self.debug {
|
|
println!("BIT 3, {}", REG_NAMES[reg_id]);
|
|
}
|
|
self.set_clear_flag(FLAG_Z, reg_content & (1 << 3) == 0);
|
|
self.clear_flag(FLAG_N);
|
|
self.set_flag(FLAG_H);
|
|
}
|
|
0x60..=0x67 => {
|
|
// Test 4th bit
|
|
let reg_id = (instruction - 0x60) as usize;
|
|
let reg_content = self.get_8bit_reg(reg_id);
|
|
if self.debug {
|
|
println!("BIT 4, {}", REG_NAMES[reg_id]);
|
|
}
|
|
self.set_clear_flag(FLAG_Z, reg_content & (1 << 4) == 0);
|
|
self.clear_flag(FLAG_N);
|
|
self.set_flag(FLAG_H);
|
|
}
|
|
0x68..=0x6F => {
|
|
// Test 5th bit
|
|
let reg_id = (instruction - 0x68) as usize;
|
|
let reg_content = self.get_8bit_reg(reg_id);
|
|
if self.debug {
|
|
println!("BIT 5, {}", REG_NAMES[reg_id]);
|
|
}
|
|
self.set_clear_flag(FLAG_Z, reg_content & (1 << 5) == 0);
|
|
self.clear_flag(FLAG_N);
|
|
self.set_flag(FLAG_H);
|
|
}
|
|
0x70..=0x77 => {
|
|
// Test 6th bit
|
|
let reg_id = (instruction - 0x70) as usize;
|
|
let reg_content = self.get_8bit_reg(reg_id);
|
|
if self.debug {
|
|
println!("BIT 6, {}", REG_NAMES[reg_id]);
|
|
}
|
|
self.set_clear_flag(FLAG_Z, reg_content & (1 << 6) == 0);
|
|
self.clear_flag(FLAG_N);
|
|
self.set_flag(FLAG_H);
|
|
}
|
|
0x78..=0x7F => {
|
|
// Test 7th bit
|
|
let reg_id = (instruction - 0x78) as usize;
|
|
let reg_content = self.get_8bit_reg(reg_id);
|
|
if self.debug {
|
|
println!("BIT 7, {}", REG_NAMES[reg_id]);
|
|
}
|
|
self.set_clear_flag(FLAG_Z, reg_content & (1 << 7) == 0);
|
|
self.clear_flag(FLAG_N);
|
|
self.set_flag(FLAG_H);
|
|
}
|
|
|
|
// Reset bits
|
|
0x80..=0x87 => {
|
|
// Reset 0th bit
|
|
let reg_id = (instruction - 0x80) as usize;
|
|
let reg_content = self.get_8bit_reg(reg_id);
|
|
if self.debug {
|
|
println!("RES 0, {}", REG_NAMES[reg_id]);
|
|
}
|
|
self.set_8bit_reg(reg_id, reg_content & !(1 << 0));
|
|
}
|
|
0x88..=0x8F => {
|
|
// Reset 1th bit
|
|
let reg_id = (instruction - 0x88) as usize;
|
|
let reg_content = self.get_8bit_reg(reg_id);
|
|
if self.debug {
|
|
println!("RES 1, {}", REG_NAMES[reg_id]);
|
|
}
|
|
self.set_8bit_reg(reg_id, reg_content & !(1 << 1));
|
|
}
|
|
0x90..=0x97 => {
|
|
// Reset 2nd bit
|
|
let reg_id = (instruction - 0x90) as usize;
|
|
let reg_content = self.get_8bit_reg(reg_id);
|
|
if self.debug {
|
|
println!("RES 2, {}", REG_NAMES[reg_id]);
|
|
}
|
|
self.set_8bit_reg(reg_id, reg_content & !(1 << 2));
|
|
}
|
|
0x98..=0x9F => {
|
|
// Reset 3th bit
|
|
let reg_id = (instruction - 0x98) as usize;
|
|
let reg_content = self.get_8bit_reg(reg_id);
|
|
if self.debug {
|
|
println!("RES 3, {}", REG_NAMES[reg_id]);
|
|
}
|
|
self.set_8bit_reg(reg_id, reg_content & !(1 << 3));
|
|
}
|
|
0xA0..=0xA7 => {
|
|
// Reset 4th bit
|
|
let reg_id = (instruction - 0xA0) as usize;
|
|
let reg_content = self.get_8bit_reg(reg_id);
|
|
if self.debug {
|
|
println!("RES 4, {}", REG_NAMES[reg_id]);
|
|
}
|
|
self.set_8bit_reg(reg_id, reg_content & !(1 << 4));
|
|
}
|
|
0xA8..=0xAF => {
|
|
// Reset 5th bit
|
|
let reg_id = (instruction - 0xA8) as usize;
|
|
let reg_content = self.get_8bit_reg(reg_id);
|
|
if self.debug {
|
|
println!("RES 5, {}", REG_NAMES[reg_id]);
|
|
}
|
|
self.set_8bit_reg(reg_id, reg_content & !(1 << 5));
|
|
}
|
|
0xB0..=0xB7 => {
|
|
// Reset 6th bit
|
|
let reg_id = (instruction - 0xB0) as usize;
|
|
let reg_content = self.get_8bit_reg(reg_id);
|
|
if self.debug {
|
|
println!("RES 6, {}", REG_NAMES[reg_id]);
|
|
}
|
|
self.set_8bit_reg(reg_id, reg_content & !(1 << 6));
|
|
}
|
|
0xB8..=0xBF => {
|
|
// Reset 7th bit
|
|
let reg_id = (instruction - 0xB8) as usize;
|
|
let reg_content = self.get_8bit_reg(reg_id);
|
|
if self.debug {
|
|
println!("RES 7, {}", REG_NAMES[reg_id]);
|
|
}
|
|
self.set_8bit_reg(reg_id, reg_content & !(1 << 7));
|
|
}
|
|
|
|
// Set bits
|
|
0xC0..=0xC7 => {
|
|
// Set 0th bit
|
|
let reg_id = (instruction - 0xC0) as usize;
|
|
let reg_content = self.get_8bit_reg(reg_id);
|
|
if self.debug {
|
|
println!("SET 0, {}", REG_NAMES[reg_id]);
|
|
}
|
|
self.set_8bit_reg(reg_id, reg_content | (1 << 0));
|
|
}
|
|
0xC8..=0xCF => {
|
|
// Set 1th bit
|
|
let reg_id = (instruction - 0xC8) as usize;
|
|
let reg_content = self.get_8bit_reg(reg_id);
|
|
if self.debug {
|
|
println!("SET 1, {}", REG_NAMES[reg_id]);
|
|
}
|
|
self.set_8bit_reg(reg_id, reg_content | (1 << 1));
|
|
}
|
|
0xD0..=0xD7 => {
|
|
// Set 2nd bit
|
|
let reg_id = (instruction - 0xD0) as usize;
|
|
let reg_content = self.get_8bit_reg(reg_id);
|
|
if self.debug {
|
|
println!("SET 2, {}", REG_NAMES[reg_id]);
|
|
}
|
|
self.set_8bit_reg(reg_id, reg_content | (1 << 2));
|
|
}
|
|
0xD8..=0xDF => {
|
|
// Set 3th bit
|
|
let reg_id = (instruction - 0xD8) as usize;
|
|
let reg_content = self.get_8bit_reg(reg_id);
|
|
if self.debug {
|
|
println!("SET 3, {}", REG_NAMES[reg_id]);
|
|
}
|
|
self.set_8bit_reg(reg_id, reg_content | (1 << 3));
|
|
}
|
|
0xE0..=0xE7 => {
|
|
// Set 4th bit
|
|
let reg_id = (instruction - 0xE0) as usize;
|
|
let reg_content = self.get_8bit_reg(reg_id);
|
|
if self.debug {
|
|
println!("SET 4, {}", REG_NAMES[reg_id]);
|
|
}
|
|
self.set_8bit_reg(reg_id, reg_content | (1 << 4));
|
|
}
|
|
0xE8..=0xEF => {
|
|
// Set 5th bit
|
|
let reg_id = (instruction - 0xE8) as usize;
|
|
let reg_content = self.get_8bit_reg(reg_id);
|
|
if self.debug {
|
|
println!("SET 5, {}", REG_NAMES[reg_id]);
|
|
}
|
|
self.set_8bit_reg(reg_id, reg_content | (1 << 5));
|
|
}
|
|
0xF0..=0xF7 => {
|
|
// Set 6th bit
|
|
let reg_id = (instruction - 0xF0) as usize;
|
|
let reg_content = self.get_8bit_reg(reg_id);
|
|
if self.debug {
|
|
println!("SET 6, {}", REG_NAMES[reg_id]);
|
|
}
|
|
self.set_8bit_reg(reg_id, reg_content | (1 << 6));
|
|
}
|
|
0xF8..=0xFF => {
|
|
// Set 7th bit
|
|
let reg_id = (instruction - 0xF8) as usize;
|
|
let reg_content = self.get_8bit_reg(reg_id);
|
|
if self.debug {
|
|
println!("SET 7, {}", REG_NAMES[reg_id]);
|
|
}
|
|
self.set_8bit_reg(reg_id, reg_content | (1 << 7));
|
|
}
|
|
}
|
|
}
|
|
|
|
fn get_pair_value(&self, a: usize, b: usize) -> u16 {
|
|
(self.get_8bit_reg(a) as u16) << 8 | self.get_8bit_reg(b) as u16
|
|
}
|
|
|
|
fn set_pair_value(&mut self, a: usize, b: usize, value: u16) {
|
|
self.set_8bit_reg(a, (value >> 8) as u8);
|
|
self.set_8bit_reg(b, value as u8);
|
|
}
|
|
|
|
fn call(&mut self, dst: u16) {
|
|
let ip = self.ip;
|
|
self.push(ip);
|
|
//self.dump_stack();
|
|
self.ip = dst;
|
|
}
|
|
|
|
fn call_condition(&mut self, cond_str: &'static str, cond: bool) -> u8 {
|
|
let dst = u16::try_from(self.load_args(2)).unwrap();
|
|
if self.debug {
|
|
println!("CALL {} {:04X}", cond_str, dst);
|
|
}
|
|
if cond {
|
|
self.call(dst);
|
|
24
|
|
} else {
|
|
12
|
|
}
|
|
}
|
|
|
|
fn ret_condition(&mut self, cond_str: &'static str, cond: bool) -> u8 {
|
|
if self.debug {
|
|
println!("RET {}", cond_str);
|
|
}
|
|
if cond {
|
|
self.ret();
|
|
20
|
|
} else {
|
|
8
|
|
}
|
|
}
|
|
|
|
fn jmp_r(&mut self, addr: u8) {
|
|
let off: i8 = addr as i8;
|
|
if off < 0 {
|
|
self.ip -= (-off) as u16;
|
|
} else {
|
|
self.ip += off as u16;
|
|
}
|
|
}
|
|
|
|
fn jmp_r_condition(&mut self, cond_str: &'static str, cond: bool) -> u8 {
|
|
let t = u8::try_from(self.load_args(1)).unwrap();
|
|
if self.debug {
|
|
println!("JR {} {:02X}", cond_str, t);
|
|
}
|
|
if cond {
|
|
self.jmp_r(t);
|
|
12
|
|
} else {
|
|
8
|
|
}
|
|
}
|
|
|
|
fn jmp_p(&mut self, addr: u16) {
|
|
self.ip = addr;
|
|
}
|
|
|
|
fn jmp_p_condition(&mut self, cond_str: &'static str, cond: bool) -> u8 {
|
|
let t = u16::try_from(self.load_args(2)).unwrap();
|
|
if self.debug {
|
|
println!("JP {} {:04X}", cond_str, t);
|
|
}
|
|
if cond {
|
|
self.jmp_p(t);
|
|
12
|
|
} else {
|
|
8
|
|
}
|
|
}
|
|
|
|
fn rst(&mut self, val: u8) -> u8 {
|
|
// Make sure this is correct.
|
|
if self.debug {
|
|
println!("RST {:02X}", val);
|
|
}
|
|
self.call(val as u16);
|
|
16
|
|
}
|
|
|
|
fn pop_rr(&mut self, r1: usize, r2: usize) -> u8 {
|
|
if self.debug {
|
|
println!("POP {}{}", REG_NAMES[r1], REG_NAMES[r2]);
|
|
}
|
|
let val: u16 = self.pop();
|
|
self.set_pair_value(r1, r2, val);
|
|
12
|
|
}
|
|
|
|
fn push_rr(&mut self, r1: usize, r2: usize) -> u8 {
|
|
if self.debug {
|
|
println!("PUSH {}{}", REG_NAMES[r1], REG_NAMES[r2]);
|
|
}
|
|
let val: u16 = self.get_pair_value(r1, r2);
|
|
self.push(val);
|
|
16
|
|
}
|
|
|
|
fn dec_rr(&mut self, r1: usize, r2: usize) -> u8 {
|
|
if self.debug {
|
|
println!("DEC {}{}", REG_NAMES[r1], REG_NAMES[r2]);
|
|
}
|
|
let v = self.get_pair_value(r1, r2);
|
|
let v = v.wrapping_sub(1);
|
|
self.set_pair_value(r1, r2, v);
|
|
8
|
|
}
|
|
|
|
fn inc_rr(&mut self, r1: usize, r2: usize) -> u8 {
|
|
if self.debug {
|
|
println!("INC {}{}", REG_NAMES[r1], REG_NAMES[r2]);
|
|
}
|
|
let v = self.get_pair_value(r1, r2);
|
|
let v = v.wrapping_add(1);
|
|
self.set_pair_value(r1, r2, v);
|
|
8
|
|
}
|
|
|
|
fn push(&mut self, val: u16) {
|
|
self.interconnect.write_word(self.sp - 2, val);
|
|
self.sp -= 2;
|
|
}
|
|
|
|
fn pop(&mut self) -> u16 {
|
|
let v: u16 = self.interconnect.read_word(self.sp);
|
|
self.sp += 2;
|
|
v
|
|
}
|
|
|
|
fn ld_r_r(&mut self, reg_dst: usize, reg_src: usize) -> u8 {
|
|
if self.debug {
|
|
println!("LD {}, {}", REG_NAMES[reg_dst], REG_NAMES[reg_src])
|
|
}
|
|
let sv = self.get_8bit_reg(reg_src);
|
|
self.set_8bit_reg(reg_dst, sv);
|
|
if reg_dst == REG_N_HL || reg_src == REG_N_HL {
|
|
8
|
|
} else {
|
|
4
|
|
}
|
|
}
|
|
|
|
fn ld_r_v(&mut self, r: usize) -> u8 {
|
|
let val = u8::try_from(self.load_args(1)).unwrap();
|
|
if self.debug {
|
|
println!("LD {}, {:02X}", REG_NAMES[r], val);
|
|
}
|
|
self.set_8bit_reg(r, val);
|
|
if r == REG_N_HL {
|
|
12
|
|
} else {
|
|
8
|
|
}
|
|
}
|
|
|
|
fn ld_rr_vv(&mut self, r1: usize, r2: usize) -> u8 {
|
|
let val = u16::try_from(self.load_args(2)).unwrap();
|
|
if self.debug {
|
|
println!("LD {}{}, {:04X}", REG_NAMES[r1], REG_NAMES[r2], val);
|
|
}
|
|
self.set_pair_value(r1, r2, val);
|
|
12
|
|
}
|
|
|
|
fn reg_inc(&mut self, reg_id: usize) -> u8 {
|
|
if self.debug {
|
|
println!("INC {}", REG_NAMES[reg_id]);
|
|
}
|
|
let old = self.get_8bit_reg(reg_id);
|
|
let val = old.wrapping_add(1);
|
|
self.set_8bit_reg(reg_id, val);
|
|
self.set_clear_flag(FLAG_Z, val == 0);
|
|
self.set_clear_flag(FLAG_H, (old & 0xF) + 1 > 0xF);
|
|
self.clear_flag(FLAG_N);
|
|
4
|
|
}
|
|
|
|
fn add_rr_rr(&mut self, r1: usize, r2: usize, r3: usize, r4: usize) -> u8 {
|
|
if self.debug {
|
|
println!(
|
|
"ADD {}{}, {}{}",
|
|
REG_NAMES[r1], REG_NAMES[r2], REG_NAMES[r3], REG_NAMES[r4]
|
|
);
|
|
}
|
|
|
|
let val1 = self.get_pair_value(r1, r2);
|
|
let val2 = self.get_pair_value(r3, r4);
|
|
let res = val1.wrapping_add(val2);
|
|
self.set_pair_value(r1, r2, res);
|
|
|
|
// We need to update the flags
|
|
self.clear_flag(FLAG_N);
|
|
|
|
// Some magic formula
|
|
self.set_clear_flag(FLAG_C, val1 as usize + val2 as usize & 0x10000 == 0x10000);
|
|
self.set_clear_flag(FLAG_H, (val1 ^ val2 ^ res) & 0x1000 == 0x1000);
|
|
8
|
|
}
|
|
|
|
fn reg_dec(&mut self, reg_id: usize) -> u8 {
|
|
if self.debug {
|
|
println!("DEC {}", REG_NAMES[reg_id]);
|
|
}
|
|
let old = self.get_8bit_reg(reg_id);
|
|
let val = old.wrapping_sub(1);
|
|
self.set_8bit_reg(reg_id, val);
|
|
|
|
self.set_clear_flag(FLAG_Z, val == 0);
|
|
// self.set_clear_flag(FLAG_C, val == 255);
|
|
self.set_clear_flag(FLAG_H, old & 0x0F == 0);
|
|
self.set_flag(FLAG_N);
|
|
4
|
|
}
|
|
|
|
fn ld_dref_rr_a(&mut self, r1: usize, r2: usize) -> u8 {
|
|
if self.debug {
|
|
println!("LD ({}{}), A", REG_NAMES[r1], REG_NAMES[r2]);
|
|
}
|
|
let dst: u16 = self.get_pair_value(r1, r2);
|
|
let val: u8 = self.get_8bit_reg(REG_N_A);
|
|
self.interconnect.write_byte(dst, val);
|
|
8
|
|
}
|
|
|
|
fn set_flag(&mut self, flag: u8) {
|
|
self.flags |= flag;
|
|
}
|
|
|
|
fn clear_flag(&mut self, flag: u8) {
|
|
self.flags &= !flag;
|
|
}
|
|
|
|
fn set_clear_flag(&mut self, flag: u8, dep: bool) {
|
|
if dep {
|
|
self.set_flag(flag);
|
|
} else {
|
|
self.clear_flag(flag);
|
|
}
|
|
}
|
|
|
|
fn int_(&mut self, val: u8) {
|
|
if self.debug {
|
|
println!("INT {:02X}", val);
|
|
}
|
|
// TODO: Clear interrupt register
|
|
self.ime = false;
|
|
self.call(val as u16);
|
|
}
|
|
|
|
fn ret(&mut self) {
|
|
let new_ip: u16 = self.pop();
|
|
//self.dump_stack();
|
|
self.ip = new_ip;
|
|
}
|
|
|
|
fn reti(&mut self) -> u8 {
|
|
self.ret();
|
|
self.ime = true;
|
|
16
|
|
}
|
|
|
|
fn handle_interrupt(&mut self, offset: u8, flag: u8) {
|
|
// Remove interrupt requested flag
|
|
let new_flag = self.interconnect.read_byte(0xFF0F) & !flag;
|
|
self.interconnect.write_byte(0xFF0F, new_flag);
|
|
|
|
// Run interrupt handler
|
|
self.int_(offset);
|
|
self.halted = false;
|
|
}
|
|
|
|
#[allow(unused_variables)]
|
|
pub fn run(&mut self) {
|
|
let start = Instant::now();
|
|
let mut cycles_executed = 0u128;
|
|
loop {
|
|
// Calculate the amount of cycles we should've executed by now.
|
|
// The gameboy has a freq of 4.194304MHz
|
|
// This means it takes it 238.418569ns to execute a single
|
|
// cycle.
|
|
let expected_cycles = start.elapsed().as_nanos() / (238 / 2);
|
|
while cycles_executed < expected_cycles {
|
|
if let CpuTickResult::Continue {
|
|
cycles_executed: x, ..
|
|
} = self.run_instruction()
|
|
{
|
|
cycles_executed += x as u128;
|
|
} else {
|
|
return;
|
|
}
|
|
}
|
|
sleep(Duration::new(0, 10 * 238));
|
|
}
|
|
}
|
|
|
|
fn check_interrupts(&mut self, execute: bool) -> bool {
|
|
// read pending interrupts
|
|
let pending = self.interconnect.read_byte(0xFF0F);
|
|
let enabled = self.interconnect.read_byte(0xFFFF);
|
|
let e_pending = pending & enabled;
|
|
|
|
// Handling interrupts. Do only execute the one with the highest
|
|
// priority
|
|
if e_pending & interconnect::INTERRUPT_DISPLAY_VBLANK > 0 {
|
|
if execute {
|
|
self.handle_interrupt(0x40, interconnect::INTERRUPT_DISPLAY_VBLANK);
|
|
}
|
|
return true;
|
|
} else if e_pending & interconnect::INTERRUPT_DISPLAY_STAT > 0 {
|
|
if execute {
|
|
self.handle_interrupt(0x48, interconnect::INTERRUPT_DISPLAY_STAT);
|
|
}
|
|
return true;
|
|
} else if e_pending & interconnect::INTERRUPT_TIMER_OVERFLOW > 0 {
|
|
if execute {
|
|
self.handle_interrupt(0x50, interconnect::INTERRUPT_TIMER_OVERFLOW);
|
|
}
|
|
return true;
|
|
} else if e_pending & interconnect::INTERRUPT_SERIAL > 0 {
|
|
if execute {
|
|
self.handle_interrupt(0x58, interconnect::INTERRUPT_SERIAL);
|
|
}
|
|
return true;
|
|
} else if e_pending & interconnect::INTERRUPT_INPUT > 0 {
|
|
if execute {
|
|
self.handle_interrupt(0x60, interconnect::INTERRUPT_INPUT);
|
|
}
|
|
return true;
|
|
} else if e_pending > 0 {
|
|
panic!("Unknown pending interrupt: {:02X}", e_pending);
|
|
}
|
|
false
|
|
}
|
|
|
|
pub fn run_instruction(&mut self) -> CpuTickResult {
|
|
// self.debug = !self.interconnect.is_boot_rom();
|
|
if !self.trigger_once && !self.interconnect.is_boot_rom() {
|
|
self.trigger_once = true;
|
|
self.regs[REG_A] = 0x11;
|
|
println!("Patching reg");
|
|
}
|
|
|
|
/*
|
|
if self.ip >= 100 && self.ip < 120 {
|
|
self.debug = true;
|
|
} else {
|
|
self.debug = false;
|
|
}
|
|
*/
|
|
|
|
// self.debug = true;
|
|
// Check for interrupts.
|
|
if self.ime {
|
|
self.check_interrupts(true);
|
|
} else if self.halted && self.check_interrupts(false) {
|
|
self.halted = false;
|
|
}
|
|
|
|
let mut cycles: u8 = 255;
|
|
let instruction: u8;
|
|
if !self.halted {
|
|
// We need to double-check the flags
|
|
instruction = self.read_byte(self.ip);
|
|
if self.debug {
|
|
print!(
|
|
"{:#06x}: [SP: {:#04X}] i={:02X}. ",
|
|
&self.ip, &self.sp, &instruction
|
|
);
|
|
for (idx, reg) in REG_NAMES.iter().enumerate() {
|
|
print!("{}: {:02X} ", reg, self.get_8bit_reg(idx));
|
|
}
|
|
print!("A: {:02X} ", self.regs[REG_A]);
|
|
print!("I: {:02X} ", self.interconnect.read_byte(0xFFFF));
|
|
|
|
// Flags
|
|
print!("Z={} ", self.flags & FLAG_Z != 0);
|
|
print!("N={} ", self.flags & FLAG_N != 0);
|
|
print!("H={} ", self.flags & FLAG_H != 0);
|
|
print!("C={} ", self.flags & FLAG_C != 0);
|
|
}
|
|
|
|
self.ip = self.ip.wrapping_add(1);
|
|
|
|
cycles = match instruction {
|
|
0x00 => {
|
|
if self.debug {
|
|
println!("NOP");
|
|
}
|
|
4
|
|
}
|
|
0x01 => self.ld_rr_vv(REG_N_B, REG_N_C),
|
|
0x02 => self.ld_dref_rr_a(REG_N_B, REG_N_C),
|
|
0x03 => self.inc_rr(REG_N_B, REG_N_C),
|
|
0x04 => self.reg_inc(REG_N_B),
|
|
0x05 => self.reg_dec(REG_N_B),
|
|
0x06 => self.ld_r_v(REG_N_B),
|
|
0x07 => {
|
|
if self.debug {
|
|
println!("RLCA");
|
|
}
|
|
let val = self.regs[REG_A];
|
|
let carry = val & 0x80 == 0x80;
|
|
self.set_clear_flag(FLAG_C, carry);
|
|
if !carry {
|
|
self.regs[REG_A] <<= 1;
|
|
} else {
|
|
self.regs[REG_A] <<= 1;
|
|
self.regs[REG_A] |= 1;
|
|
}
|
|
self.clear_flag(FLAG_Z);
|
|
self.clear_flag(FLAG_N);
|
|
self.clear_flag(FLAG_H);
|
|
4
|
|
}
|
|
0x08 => {
|
|
let a = u16::try_from(self.load_args(2)).unwrap();
|
|
if self.debug {
|
|
println!("LD ({:04X}), sp", a);
|
|
}
|
|
self.interconnect.write_word(a, self.sp);
|
|
20
|
|
}
|
|
0x09 => self.add_rr_rr(REG_N_H, REG_N_L, REG_N_B, REG_N_C),
|
|
0x0A => {
|
|
if self.debug {
|
|
println!("LD A, (BC)");
|
|
}
|
|
self.regs[REG_A] = self
|
|
.interconnect
|
|
.read_byte(self.get_pair_value(REG_N_B, REG_N_C));
|
|
8
|
|
}
|
|
0x0B => self.dec_rr(REG_N_B, REG_N_C),
|
|
0x0C => self.reg_inc(REG_N_C),
|
|
0x0D => self.reg_dec(REG_N_C),
|
|
0x0E => self.ld_r_v(REG_N_C),
|
|
0x0F => {
|
|
if self.debug {
|
|
println!("RRCA");
|
|
}
|
|
let val = self.regs[REG_A];
|
|
self.set_clear_flag(FLAG_C, val & 1 == 1);
|
|
if val & 1 == 0 {
|
|
self.regs[REG_A] >>= 1;
|
|
} else {
|
|
self.regs[REG_A] >>= 1;
|
|
self.regs[REG_A] |= 0x80;
|
|
}
|
|
self.clear_flag(FLAG_Z);
|
|
self.clear_flag(FLAG_N);
|
|
self.clear_flag(FLAG_H);
|
|
4
|
|
}
|
|
|
|
0x10 => {
|
|
println!(
|
|
"STOP 0 {:02X} not implemented.",
|
|
u8::try_from(self.load_args(1)).unwrap()
|
|
);
|
|
4
|
|
}
|
|
0x11 => self.ld_rr_vv(REG_N_D, REG_N_E),
|
|
0x12 => self.ld_dref_rr_a(REG_N_D, REG_N_E),
|
|
0x13 => self.inc_rr(REG_N_D, REG_N_E),
|
|
0x14 => self.reg_inc(REG_N_D),
|
|
0x15 => self.reg_dec(REG_N_D),
|
|
0x16 => self.ld_r_v(REG_N_D),
|
|
0x17 => {
|
|
if self.debug {
|
|
println!("RLA");
|
|
}
|
|
let carry = self.flags & FLAG_C > 0;
|
|
let val = self.regs[REG_A];
|
|
self.set_clear_flag(FLAG_C, val & 0x80 == 0x80);
|
|
if !carry {
|
|
self.regs[REG_A] <<= 1;
|
|
} else {
|
|
self.regs[REG_A] <<= 1;
|
|
self.regs[REG_A] |= 1;
|
|
}
|
|
self.clear_flag(FLAG_Z);
|
|
self.clear_flag(FLAG_N);
|
|
self.clear_flag(FLAG_H);
|
|
4
|
|
}
|
|
0x18 => {
|
|
let dst = u8::try_from(self.load_args(1)).unwrap();
|
|
self.jmp_r(dst);
|
|
if self.debug {
|
|
println!("JMPR {:02X}", dst);
|
|
}
|
|
12
|
|
}
|
|
0x19 => self.add_rr_rr(REG_N_H, REG_N_L, REG_N_D, REG_N_E),
|
|
0x1A => {
|
|
if self.debug {
|
|
println!("LD A, (DE)");
|
|
}
|
|
self.regs[REG_A] = self
|
|
.interconnect
|
|
.read_byte(self.get_pair_value(REG_N_D, REG_N_E));
|
|
8
|
|
}
|
|
0x1B => self.dec_rr(REG_N_D, REG_N_E),
|
|
0x1C => self.reg_inc(REG_N_E),
|
|
0x1D => self.reg_dec(REG_N_E),
|
|
0x1E => self.ld_r_v(REG_N_E),
|
|
0x1F => {
|
|
if self.debug {
|
|
println!("RRA");
|
|
}
|
|
let carry = self.flags & FLAG_C > 0;
|
|
let val = self.regs[REG_A];
|
|
self.set_clear_flag(FLAG_C, val & 1 == 1);
|
|
if !carry {
|
|
self.regs[REG_A] >>= 1;
|
|
} else {
|
|
self.regs[REG_A] >>= 1;
|
|
self.regs[REG_A] |= 0x80;
|
|
}
|
|
self.clear_flag(FLAG_Z);
|
|
self.clear_flag(FLAG_N);
|
|
self.clear_flag(FLAG_H);
|
|
4
|
|
}
|
|
|
|
0x20 => {
|
|
let c = self.flags & FLAG_Z == 0;
|
|
self.jmp_r_condition("NZ", c)
|
|
}
|
|
0x21 => self.ld_rr_vv(REG_N_H, REG_N_L),
|
|
0x22 => {
|
|
if self.debug {
|
|
println!("LD (HL+), A");
|
|
}
|
|
let addr: u16 = self.get_pair_value(REG_N_H, REG_N_L);
|
|
self.interconnect.write_byte(addr, self.regs[REG_A]);
|
|
self.set_pair_value(REG_N_H, REG_N_L, addr.wrapping_add(1));
|
|
8
|
|
}
|
|
0x23 => self.inc_rr(REG_N_H, REG_N_L),
|
|
0x24 => self.reg_inc(REG_N_H),
|
|
0x25 => self.reg_dec(REG_N_H),
|
|
0x26 => self.ld_r_v(REG_N_H),
|
|
0x27 => {
|
|
// Logic copied from some other emulator
|
|
let a = self.regs[REG_A];
|
|
|
|
if self.debug {
|
|
println!("DAA");
|
|
}
|
|
|
|
let mut cor: u8 = 0;
|
|
if (self.flags & FLAG_H) == FLAG_H {
|
|
cor |= 0x06;
|
|
}
|
|
if (self.flags & FLAG_C) == FLAG_C {
|
|
cor |= 0x60;
|
|
}
|
|
|
|
if (self.flags & FLAG_N) == FLAG_N {
|
|
self.regs[REG_A] = a.wrapping_sub(cor);
|
|
} else {
|
|
if (a & 0x0F) > 0x09 {
|
|
cor |= 0x06;
|
|
}
|
|
if a > 0x99 {
|
|
cor |= 0x60;
|
|
}
|
|
self.regs[REG_A] = a.wrapping_add(cor);
|
|
}
|
|
|
|
self.clear_flag(FLAG_H);
|
|
if cor & 0x60 == 0x60 {
|
|
self.set_flag(FLAG_C);
|
|
}
|
|
|
|
let a = self.regs[REG_A];
|
|
self.set_clear_flag(FLAG_Z, a == 0);
|
|
|
|
4
|
|
}
|
|
0x28 => {
|
|
let c = self.flags & FLAG_Z == FLAG_Z;
|
|
self.jmp_r_condition("Z", c)
|
|
}
|
|
0x29 => self.add_rr_rr(REG_N_H, REG_N_L, REG_N_H, REG_N_L),
|
|
0x2A => {
|
|
if self.debug {
|
|
println!("LD A, (HL+)");
|
|
}
|
|
let addr: u16 = self.get_pair_value(REG_N_H, REG_N_L);
|
|
self.regs[REG_A] = self.interconnect.read_byte(addr);
|
|
self.set_pair_value(REG_N_H, REG_N_L, addr.wrapping_add(1));
|
|
8
|
|
}
|
|
0x2B => self.dec_rr(REG_N_H, REG_N_L),
|
|
0x2C => self.reg_inc(REG_N_L),
|
|
0x2D => self.reg_dec(REG_N_L),
|
|
0x2E => self.ld_r_v(REG_N_L),
|
|
0x2F => {
|
|
if self.debug {
|
|
println!("CPL");
|
|
}
|
|
self.regs[REG_A] = !self.regs[REG_A];
|
|
self.set_flag(FLAG_N);
|
|
self.set_flag(FLAG_H);
|
|
4
|
|
}
|
|
|
|
0x30 => {
|
|
let c = self.flags & FLAG_C == 0;
|
|
self.jmp_r_condition("NC", c)
|
|
}
|
|
0x31 => {
|
|
let args = self.load_args(2);
|
|
self.sp = u16::try_from(args).unwrap();
|
|
if self.debug {
|
|
println!("LD SP, {:04x}", self.sp);
|
|
}
|
|
12
|
|
}
|
|
0x32 => {
|
|
if self.debug {
|
|
println!("LD (HL-), A");
|
|
}
|
|
let addr = self.get_pair_value(REG_N_H, REG_N_L);
|
|
self.interconnect.write_byte(addr, self.regs[REG_A]);
|
|
self.set_pair_value(REG_N_H, REG_N_L, addr.wrapping_sub(1));
|
|
8
|
|
}
|
|
0x33 => {
|
|
if self.debug {
|
|
println!("INC SP");
|
|
}
|
|
let sp = self.sp.wrapping_add(1);
|
|
self.sp = sp;
|
|
8
|
|
}
|
|
0x34 => self.reg_inc(REG_N_HL),
|
|
0x35 => self.reg_dec(REG_N_HL),
|
|
0x36 => self.ld_r_v(REG_N_HL),
|
|
0x37 => {
|
|
if self.debug {
|
|
println!("SCF");
|
|
}
|
|
self.set_flag(FLAG_C);
|
|
self.clear_flag(FLAG_N);
|
|
self.clear_flag(FLAG_H);
|
|
4
|
|
}
|
|
0x38 => {
|
|
let c = self.flags & FLAG_C == FLAG_C;
|
|
self.jmp_r_condition("C", c)
|
|
}
|
|
0x39 => {
|
|
if self.debug {
|
|
println!("ADD HL, SP");
|
|
}
|
|
let sp = self.sp;
|
|
let old = self.get_pair_value(REG_N_H, REG_N_L);
|
|
let v = old.wrapping_add(sp);
|
|
self.set_pair_value(REG_N_H, REG_N_L, v);
|
|
|
|
self.clear_flag(FLAG_N);
|
|
self.set_clear_flag(FLAG_C, old > v && sp > 0);
|
|
self.set_clear_flag(FLAG_H, ((old & 0xFFF) + (sp & 0xFFF)) > 0xFFF);
|
|
8
|
|
}
|
|
0x3A => {
|
|
if self.debug {
|
|
println!("LD A, (HL-)");
|
|
}
|
|
let addr = self.get_pair_value(REG_N_H, REG_N_L);
|
|
self.regs[REG_A] = self.interconnect.read_byte(addr);
|
|
self.set_pair_value(REG_N_H, REG_N_L, addr.wrapping_sub(1));
|
|
8
|
|
}
|
|
0x3B => {
|
|
if self.debug {
|
|
println!("DEC SP");
|
|
}
|
|
let sp = self.sp.wrapping_sub(1);
|
|
self.sp = sp;
|
|
8
|
|
}
|
|
0x3C => self.reg_inc(REG_N_A),
|
|
0x3D => self.reg_dec(REG_N_A),
|
|
0x3E => self.ld_r_v(REG_N_A),
|
|
0x3F => {
|
|
if self.debug {
|
|
println!("CCF");
|
|
}
|
|
self.flags ^= FLAG_C;
|
|
self.clear_flag(FLAG_N);
|
|
self.clear_flag(FLAG_H);
|
|
4
|
|
}
|
|
|
|
// LDs
|
|
0x40..=0x47 => self.ld_r_r(REG_N_B, (instruction - 0x40) as usize),
|
|
0x48..=0x4F => self.ld_r_r(REG_N_C, (instruction - 0x48) as usize),
|
|
0x50..=0x57 => self.ld_r_r(REG_N_D, (instruction - 0x50) as usize),
|
|
0x58..=0x5F => self.ld_r_r(REG_N_E, (instruction - 0x58) as usize),
|
|
0x60..=0x67 => self.ld_r_r(REG_N_H, (instruction - 0x60) as usize),
|
|
0x68..=0x6F => self.ld_r_r(REG_N_L, (instruction - 0x68) as usize),
|
|
0x70..=0x75 | 0x77 => self.ld_r_r(REG_N_HL, (instruction - 0x70) as usize),
|
|
0x78..=0x7F => self.ld_r_r(REG_N_A, (instruction - 0x78) as usize),
|
|
|
|
// HALT
|
|
0x76 => {
|
|
if self.debug {
|
|
println!("HALT");
|
|
}
|
|
self.halted = true;
|
|
4
|
|
}
|
|
|
|
// ADD
|
|
0x80..=0x87 => {
|
|
let reg_id = (instruction - 0x80) as usize;
|
|
if self.debug {
|
|
println!("ADD {}", REG_NAMES[reg_id]);
|
|
}
|
|
let v = self.get_8bit_reg(reg_id);
|
|
self.add_r(v);
|
|
4
|
|
}
|
|
|
|
// ADC
|
|
0x88..=0x8F => {
|
|
let reg_id = (instruction - 0x88) as usize;
|
|
if self.debug {
|
|
println!("ADC {}", REG_NAMES[reg_id]);
|
|
}
|
|
|
|
let v = self.get_8bit_reg(reg_id);
|
|
self.adc_r(v);
|
|
4
|
|
}
|
|
|
|
// SUBs
|
|
0x90..=0x97 => {
|
|
let reg_id = (instruction - 0x90) as usize;
|
|
if self.debug {
|
|
println!("SUB {}", REG_NAMES[reg_id]);
|
|
}
|
|
let r = self.get_8bit_reg(reg_id);
|
|
self.sub_r(r);
|
|
4
|
|
}
|
|
|
|
// SBC
|
|
0x98..=0x9F => {
|
|
let reg_id = (instruction - 0x98) as usize;
|
|
if self.debug {
|
|
println!("SBC {}", REG_NAMES[reg_id]);
|
|
}
|
|
let r = self.get_8bit_reg(reg_id);
|
|
self.sbc_r(r);
|
|
4
|
|
}
|
|
|
|
// AND
|
|
0xA0..=0xA7 => {
|
|
let reg_id = (instruction - 0xA0) as usize;
|
|
if self.debug {
|
|
println!("AND {}", REG_NAMES[reg_id]);
|
|
}
|
|
self.regs[REG_A] &= self.get_8bit_reg(reg_id);
|
|
let v = self.regs[REG_A];
|
|
|
|
self.set_clear_flag(FLAG_Z, v == 0);
|
|
self.clear_flag(FLAG_N);
|
|
self.set_flag(FLAG_H);
|
|
self.clear_flag(FLAG_C);
|
|
4
|
|
}
|
|
|
|
// XOR
|
|
0xA8..=0xAF => {
|
|
let reg_id = (instruction - 0xA8) as usize;
|
|
if self.debug {
|
|
println!("XOR {}", REG_NAMES[reg_id]);
|
|
}
|
|
self.regs[REG_A] ^= self.get_8bit_reg(reg_id);
|
|
let v = self.regs[REG_A];
|
|
self.set_clear_flag(FLAG_Z, v == 0);
|
|
|
|
self.clear_flag(FLAG_C);
|
|
self.clear_flag(FLAG_N);
|
|
self.clear_flag(FLAG_H);
|
|
4
|
|
}
|
|
|
|
// OR
|
|
0xB0..=0xB7 => {
|
|
let reg_id = (instruction - 0xB0) as usize;
|
|
if self.debug {
|
|
println!("OR {}", REG_NAMES[reg_id]);
|
|
}
|
|
self.regs[REG_A] |= self.get_8bit_reg(reg_id);
|
|
let v = self.regs[REG_A];
|
|
|
|
self.set_clear_flag(FLAG_Z, v == 0);
|
|
self.clear_flag(FLAG_C);
|
|
self.clear_flag(FLAG_N);
|
|
self.clear_flag(FLAG_H);
|
|
4
|
|
}
|
|
|
|
// CP
|
|
0xB8..=0xBF => {
|
|
let reg_id = (instruction - 0xB8) as usize;
|
|
if self.debug {
|
|
println!("CP {}", REG_NAMES[reg_id]);
|
|
}
|
|
|
|
let v = self.get_8bit_reg(reg_id);
|
|
self.cp_r(v);
|
|
4
|
|
}
|
|
|
|
0xC0 => {
|
|
let c = self.flags & FLAG_Z == 0;
|
|
self.ret_condition("NZ", c)
|
|
}
|
|
0xC1 => self.pop_rr(REG_N_B, REG_N_C),
|
|
0xC2 => {
|
|
let c = self.flags & FLAG_Z == 0;
|
|
self.jmp_p_condition("NZ", c)
|
|
}
|
|
0xC3 => {
|
|
let dst = u16::try_from(self.load_args(2)).unwrap();
|
|
if self.debug {
|
|
println!("JMP {:04X}", dst);
|
|
}
|
|
self.jmp_p(dst);
|
|
16
|
|
}
|
|
0xC4 => {
|
|
let c = self.flags & FLAG_Z == 0;
|
|
self.call_condition("NZ", c)
|
|
}
|
|
0xC5 => self.push_rr(REG_N_B, REG_N_C),
|
|
0xC6 => {
|
|
let val = u8::try_from(self.load_args(1)).unwrap();
|
|
if self.debug {
|
|
println!("ADD A, {:02X}", val);
|
|
}
|
|
|
|
self.add_r(val);
|
|
|
|
8
|
|
}
|
|
0xC7 => self.rst(0x00),
|
|
0xC8 => {
|
|
let c = self.flags & FLAG_Z == FLAG_Z;
|
|
self.ret_condition("Z", c)
|
|
}
|
|
0xC9 => {
|
|
if self.debug {
|
|
println!("RET");
|
|
}
|
|
self.ret();
|
|
16
|
|
}
|
|
0xCA => {
|
|
let c = self.flags & FLAG_Z == FLAG_Z;
|
|
self.jmp_p_condition("Z", c)
|
|
}
|
|
0xCB => {
|
|
self.run_prefix_instruction();
|
|
12 // TODO: Verify that this is the case for all prefix instructions.
|
|
}
|
|
0xCC => {
|
|
let c = self.flags & FLAG_Z == FLAG_Z;
|
|
self.call_condition("Z", c)
|
|
}
|
|
0xCD => self.call_condition("", true),
|
|
0xCE => {
|
|
let arg = u8::try_from(self.load_args(1)).unwrap();
|
|
if self.debug {
|
|
println!("ADC A, {:02X}", arg);
|
|
}
|
|
self.adc_r(arg);
|
|
8
|
|
}
|
|
0xCF => self.rst(0x08),
|
|
|
|
0xD0 => {
|
|
let c = self.flags & FLAG_C == 0;
|
|
self.ret_condition("NC", c)
|
|
}
|
|
0xD1 => self.pop_rr(REG_N_D, REG_N_E),
|
|
0xD2 => {
|
|
let c = self.flags & FLAG_C == 0;
|
|
self.jmp_p_condition("NC", c)
|
|
}
|
|
0xD3 => panic!("NON-EXISTING OPCODE"),
|
|
0xD4 => {
|
|
let c = self.flags & FLAG_C == 0;
|
|
self.call_condition("NC", c)
|
|
}
|
|
0xD5 => self.push_rr(REG_N_D, REG_N_E),
|
|
0xD6 => {
|
|
let val = u8::try_from(self.load_args(1)).unwrap();
|
|
if self.debug {
|
|
println!("SUB {:02X}", val);
|
|
}
|
|
|
|
self.sub_r(val);
|
|
8
|
|
}
|
|
0xD7 => self.rst(0x10),
|
|
0xD8 => {
|
|
let c = self.flags & FLAG_C == FLAG_C;
|
|
self.ret_condition("C", c)
|
|
}
|
|
0xD9 => {
|
|
if self.debug {
|
|
println!("RETI");
|
|
}
|
|
self.reti()
|
|
}
|
|
0xDA => {
|
|
let c = self.flags & FLAG_C == FLAG_C;
|
|
self.jmp_p_condition("C", c)
|
|
}
|
|
0xDB => panic!("NON-EXISTING OPCODE"),
|
|
0xDC => {
|
|
let c = self.flags & FLAG_C == FLAG_C;
|
|
self.call_condition("C", c)
|
|
}
|
|
0xDD => panic!("NON-EXISTING OPCODE"),
|
|
0xDE => {
|
|
let arg = u8::try_from(self.load_args(1)).unwrap();
|
|
if self.debug {
|
|
println!("SBC {:02X}", arg);
|
|
}
|
|
self.sbc_r(arg);
|
|
8
|
|
}
|
|
0xDF => self.rst(0x18),
|
|
|
|
0xE0 => {
|
|
let arg = u8::try_from(self.load_args(1)).unwrap();
|
|
if self.debug {
|
|
println!("LDH {:02X}, A", arg);
|
|
}
|
|
self.interconnect
|
|
.write_byte(0xFF00 + arg as u16, self.regs[REG_A]);
|
|
12
|
|
}
|
|
0xE1 => self.pop_rr(REG_N_H, REG_N_L),
|
|
0xE2 => {
|
|
if self.debug {
|
|
println!("LD (C), A");
|
|
}
|
|
let addr: u16 = 0xFF00 + self.get_8bit_reg(REG_N_C) as u16;
|
|
self.interconnect.write_byte(addr, self.regs[REG_A]);
|
|
8
|
|
}
|
|
0xE3 | 0xE4 => panic!("NON-EXISTING OPCODE"),
|
|
0xE5 => self.push_rr(REG_N_H, REG_N_L),
|
|
0xE6 => {
|
|
let val = u8::try_from(self.load_args(1)).unwrap();
|
|
if self.debug {
|
|
println!("AND {:02X}", val);
|
|
}
|
|
let v = self.regs[REG_A] & val;
|
|
self.regs[REG_A] = v;
|
|
self.set_clear_flag(FLAG_Z, v == 0);
|
|
self.clear_flag(FLAG_N);
|
|
self.set_flag(FLAG_H);
|
|
self.clear_flag(FLAG_C);
|
|
|
|
8
|
|
}
|
|
0xE7 => self.rst(0x20),
|
|
0xE8 => {
|
|
let arg = u8::try_from(self.load_args(1)).unwrap() as i8;
|
|
if self.debug {
|
|
println!("ADD SP, {:02X}", arg);
|
|
}
|
|
let t: u16;
|
|
if arg > 0 {
|
|
t = self.sp.wrapping_add(arg as u16);
|
|
} else {
|
|
t = self.sp.wrapping_sub((-arg) as u16);
|
|
}
|
|
let sp = self.sp;
|
|
self.clear_flag(FLAG_N);
|
|
self.clear_flag(FLAG_Z);
|
|
self.set_clear_flag(FLAG_C, (sp ^ arg as u16 ^ t) & 0x100 == 0x100);
|
|
self.set_clear_flag(FLAG_H, (sp ^ arg as u16 ^ t) & 0x10 == 0x10);
|
|
self.sp = t;
|
|
16
|
|
}
|
|
0xE9 => {
|
|
if self.debug {
|
|
println!("JP (HL)");
|
|
}
|
|
self.ip = self.get_pair_value(REG_N_H, REG_N_L);
|
|
4
|
|
}
|
|
0xEA => {
|
|
let addr = u16::try_from(self.load_args(2)).unwrap();
|
|
if self.debug {
|
|
println!("LD ({:04X}), A", addr);
|
|
}
|
|
self.interconnect.write_byte(addr, self.regs[REG_A]);
|
|
16
|
|
}
|
|
0xEB..=0xED => panic!("NON-EXISTING OPCODE"),
|
|
0xEE => {
|
|
let arg = u8::try_from(self.load_args(1)).unwrap();
|
|
if self.debug {
|
|
println!("XOR {:02X}", arg);
|
|
}
|
|
let v = self.regs[REG_A] ^ arg;
|
|
self.regs[REG_A] = v;
|
|
self.set_clear_flag(FLAG_Z, v == 0);
|
|
self.clear_flag(FLAG_H);
|
|
self.clear_flag(FLAG_C);
|
|
self.clear_flag(FLAG_N);
|
|
8
|
|
}
|
|
0xEF => self.rst(0x28),
|
|
|
|
0xF0 => {
|
|
let arg = u8::try_from(self.load_args(1)).unwrap();
|
|
if self.debug {
|
|
println!("LDH A, {:02X}", arg);
|
|
}
|
|
self.regs[REG_A] = self.interconnect.read_byte(0xFF00 + arg as u16);
|
|
12
|
|
}
|
|
0xF1 => self.pop_rr(REG_N_A, REG_N_F),
|
|
0xF2 => {
|
|
if self.debug {
|
|
println!("LD A, (C)");
|
|
}
|
|
let addr = 0xFF00 + self.get_8bit_reg(REG_N_C) as u16;
|
|
self.regs[REG_A] = self.interconnect.read_byte(addr);
|
|
8
|
|
}
|
|
0xF3 => {
|
|
if self.debug {
|
|
println!("DI");
|
|
}
|
|
self.ime = false;
|
|
4
|
|
}
|
|
0xF4 => panic!("NON-EXISTING OPCODE"),
|
|
0xF5 => self.push_rr(REG_N_A, REG_N_F),
|
|
0xF6 => {
|
|
let val = u8::try_from(self.load_args(1)).unwrap();
|
|
if self.debug {
|
|
println!("OR {:02X}", val);
|
|
}
|
|
let v = self.regs[REG_A] | val;
|
|
self.regs[REG_A] = v;
|
|
self.set_clear_flag(FLAG_Z, v == 0);
|
|
self.clear_flag(FLAG_C);
|
|
self.clear_flag(FLAG_N);
|
|
self.clear_flag(FLAG_H);
|
|
8
|
|
}
|
|
0xF7 => self.rst(0x30),
|
|
0xF8 => {
|
|
let arg = u8::try_from(self.load_args(1)).unwrap() as i8;
|
|
if self.debug {
|
|
println!("LD HL, SP+{:02X}", arg);
|
|
}
|
|
|
|
self.clear_flag(FLAG_N);
|
|
self.clear_flag(FLAG_Z);
|
|
let sp = self.sp;
|
|
|
|
if arg < 0 {
|
|
let v: u16 = self.sp.wrapping_sub((-arg) as u16);
|
|
self.set_pair_value(REG_N_H, REG_N_L, v);
|
|
self.set_clear_flag(FLAG_C, (sp ^ arg as u16 ^ v) & 0x100 == 0x100);
|
|
self.set_clear_flag(FLAG_H, (sp ^ arg as u16 ^ v) & 0x10 == 0x10);
|
|
} else {
|
|
let v: u16 = self.sp.wrapping_add(arg as u16);
|
|
self.set_pair_value(REG_N_H, REG_N_L, v);
|
|
self.set_clear_flag(FLAG_C, (sp ^ arg as u16 ^ v) & 0x100 == 0x100);
|
|
self.set_clear_flag(FLAG_H, (sp ^ arg as u16 ^ v) & 0x10 == 0x10);
|
|
}
|
|
12
|
|
}
|
|
0xF9 => {
|
|
if self.debug {
|
|
println!("LD SP, HL");
|
|
}
|
|
self.sp = self.get_pair_value(REG_N_H, REG_N_L);
|
|
8
|
|
}
|
|
0xFA => {
|
|
let addr = u16::try_from(self.load_args(2)).unwrap();
|
|
if self.debug {
|
|
println!("LD A, ({:04X})", addr);
|
|
}
|
|
self.regs[REG_A] = self.interconnect.read_byte(addr);
|
|
16
|
|
}
|
|
0xFB => {
|
|
if self.debug {
|
|
println!("EI");
|
|
}
|
|
self.ime = true; // interrupt master enable
|
|
4
|
|
}
|
|
0xFC | 0xFD => panic!("NON-EXISTING OPCODE"),
|
|
|
|
0xFE => {
|
|
let arg = u8::try_from(self.load_args(1)).unwrap();
|
|
if self.debug {
|
|
println!("CP {:02X}", arg);
|
|
}
|
|
|
|
self.cp_r(arg);
|
|
8
|
|
}
|
|
0xFF => self.rst(0x38),
|
|
};
|
|
}
|
|
|
|
if self.interconnect.tick(cycles) == TickResult::Shutdown {
|
|
return CpuTickResult::Shutdown {
|
|
cycles_executed: cycles as usize,
|
|
};
|
|
}
|
|
CpuTickResult::Continue {
|
|
cycles_executed: cycles as usize,
|
|
}
|
|
}
|
|
}
|