gb-emu/src/processor/mod.rs

310 lines
8.2 KiB
Rust

use std::{
mem::transmute,
ops::{BitAnd, BitOr, BitXor},
};
use crate::{verbose_println, Inner, Memory, Register, State};
mod opcodes;
#[allow(dead_code)]
#[derive(PartialEq)]
enum FLAGS {
Zero = 7,
NSubtract = 6,
HalfCarry = 5,
Carry = 4,
}
enum Direction {
Left,
Right,
}
pub struct CPU {
pub memory: Memory,
pub state: State,
pub last_instruction: u8,
pub last_instruction_addr: u16,
}
impl CPU {
pub fn exec_next(&mut self) {
unsafe { self.last_instruction_addr = self.state.pc.as_u16 };
let opcode = self.next_opcode();
self.last_instruction = opcode;
verbose_println!(
"exec {:#4X} from pc: {:#X}",
opcode,
self.last_instruction_addr
);
self.run_opcode(opcode);
}
fn next_opcode(&mut self) -> u8 {
unsafe {
let opcode = self.memory.get(self.state.pc.as_u16);
self.state.pc.as_u16 = self.state.pc.as_u16.wrapping_add(0x1);
return opcode;
};
}
fn rlc(&mut self, byte: u8) -> u8 {
self.rotate_c(byte, Direction::Left)
}
fn rrc(&mut self, byte: u8) -> u8 {
self.rotate_c(byte, Direction::Right)
}
fn rl(&mut self, byte: u8) -> u8 {
self.rotate(byte, Direction::Left)
}
fn rr(&mut self, byte: u8) -> u8 {
self.rotate(byte, Direction::Right)
}
fn rotate_c(&mut self, byte: u8, direction: Direction) -> u8 {
let (mut rotated, carry) = rotate(byte, &direction);
if carry {
rotated += get_rotation_carry(&direction);
self.set_flag(FLAGS::Carry);
}
return rotated;
}
fn rotate(&mut self, byte: u8, direction: Direction) -> u8 {
let old_carry = self.get_flag(FLAGS::Carry);
let (mut rotated, carry) = rotate(byte, &direction);
if old_carry > 0 {
rotated += get_rotation_carry(&direction);
}
if carry {
self.set_flag(FLAGS::Carry);
}
return rotated;
}
fn sla(&mut self, byte: u8) -> u8 {
self.shift(byte, Direction::Left)
}
fn sra(&mut self, byte: u8) -> u8 {
let b = get_bit(byte, 7);
let val = self.shift(byte, Direction::Right);
if b {
val + 0b10000000
} else {
val
}
}
fn srl(&mut self, byte: u8) -> u8 {
self.shift(byte, Direction::Right)
}
fn shift(&mut self, byte: u8, direction: Direction) -> u8 {
let (rotated, carry) = rotate(byte, &direction);
if carry {
self.set_flag(FLAGS::Carry);
}
return rotated;
}
fn bit(&mut self, byte: u8, bit: u8) {
self.set_or_clear_flag(FLAGS::Zero, !get_bit(byte, bit));
}
fn rst(&mut self, address: u16) {
self.push(self.state.pc);
self.state.pc.as_u8s.left = 0x0;
self.state.pc.as_u8s.right = self.memory.get(address);
}
fn push(&mut self, register: Register) {
unsafe {
let address = self.state.sp.as_u16;
self.memory
.set(address.wrapping_sub(1), register.as_u8s.right);
self.memory
.set(address.wrapping_sub(2), register.as_u8s.left);
self.state.sp.as_u16 = address.wrapping_sub(2);
}
}
fn pop_word(&mut self) -> Register {
unsafe {
let address = self.state.sp.as_u16;
self.state.sp.as_u16 = self.state.sp.as_u16.wrapping_add(0x2);
Register {
as_u8s: Inner {
left: self.memory.get(address),
right: self.memory.get(address.wrapping_add(1)),
},
}
}
}
fn store_word(&mut self, address: u16, word: Register) {
unsafe {
self.memory.set(address, word.as_u8s.left);
self.memory.set(address + 1, word.as_u8s.right);
};
}
fn ld_immediate_word(&mut self) -> Register {
Register {
as_u8s: Inner {
left: self.next_opcode(),
right: self.next_opcode(),
},
}
}
fn ld_immediate_byte(&mut self) -> u8 {
self.next_opcode()
}
fn get_flag(&mut self, flag: FLAGS) -> u8 {
unsafe {
if get_bit(self.state.af.as_u8s.right, flag as u8) {
0x1
} else {
0x0
}
}
}
fn set_flag(&mut self, flag: FLAGS) {
if flag == FLAGS::Zero {
verbose_println!("setting z flag");
}
unsafe {
verbose_println!(
"setting flag: currently {0:#b} / {0:#X}",
self.state.af.as_u8s.right
);
self.state.af.as_u8s.right = self.state.af.as_u8s.right.bitor(1 << flag as u8);
verbose_println!(
" now {0:#b} / {0:#X}",
self.state.af.as_u8s.right
);
};
}
fn clear_flag(&mut self, flag: FLAGS) {
unsafe {
self.state.af.as_u8s.right = self.state.af.as_u8s.right.bitand(!(1 << flag as u8));
};
}
fn toggle_flag(&mut self, flag: FLAGS) {
unsafe {
self.state.af.as_u8s.right = self.state.af.as_u8s.right.bitxor(1 << flag as u8);
};
}
fn set_or_clear_flag(&mut self, flag: FLAGS, state: bool) {
if state {
self.set_flag(flag)
} else {
self.clear_flag(flag)
}
}
fn add_u8s(&mut self, first: u8, second: u8) -> u8 {
let (result, carry) = first.overflowing_add(second);
self.clear_flag(FLAGS::NSubtract);
self.set_or_clear_flag(FLAGS::Carry, carry);
self.set_or_clear_flag(FLAGS::Zero, result == 0x0);
self.set_or_clear_flag(
FLAGS::HalfCarry,
(((first & 0xF).wrapping_add(second & 0xF)) & 0x10) == 0x10,
);
return result;
}
fn add_u16s(&mut self, first: u16, second: u16) -> u16 {
let (result, carry) = first.overflowing_add(second);
self.clear_flag(FLAGS::NSubtract);
self.set_or_clear_flag(FLAGS::Carry, carry);
self.set_or_clear_flag(FLAGS::Zero, result == 0x0);
self.set_or_clear_flag(
FLAGS::HalfCarry,
(((first & 0xFFF).wrapping_add(second & 0xFFF)) & 0x1000) == 0x1000,
);
return result;
}
fn sub_u8s(&mut self, first: u8, second: u8) -> u8 {
let (result, carry) = first.overflowing_sub(second);
self.set_flag(FLAGS::NSubtract);
self.set_or_clear_flag(FLAGS::Carry, carry);
self.set_or_clear_flag(FLAGS::Zero, result == 0x0);
self.set_or_clear_flag(
FLAGS::HalfCarry,
(((first & 0xF).wrapping_sub(second & 0xF)) & 0x10) == 0x10,
);
return result;
}
fn sub_u16s(&mut self, first: u16, second: u16) -> u16 {
let (result, carry) = first.overflowing_sub(second);
self.set_flag(FLAGS::NSubtract);
self.set_or_clear_flag(FLAGS::Carry, carry);
self.set_or_clear_flag(FLAGS::Zero, result == 0x0);
self.set_or_clear_flag(
FLAGS::HalfCarry,
(((first & 0xFFF).wrapping_sub(second & 0xFFF)) & 0x1000) == 0x1000,
);
return result;
}
}
fn as_signed(unsigned: u8) -> i8 {
unsafe {
return transmute(unsigned);
}
}
fn get_bit(byte: u8, flag: u8) -> bool {
let mask = 1 << flag;
let got = byte & mask;
return got > 0x0;
}
fn rotate(byte: u8, direction: &Direction) -> (u8, bool) {
match direction {
Direction::Left => {
let carry = get_bit(byte, 7);
let r = byte << 1;
return (r, carry);
}
Direction::Right => {
let carry = get_bit(byte, 0);
let r = byte >> 1;
return (r, carry);
}
}
}
fn get_rotation_carry(direction: &Direction) -> u8 {
match direction {
Direction::Left => 0b1,
Direction::Right => 0b10000000,
}
}
fn swap_nibbles(byte: u8) -> u8 {
(byte & 0x0F) << 4 | (byte & 0xF0) >> 4
}
fn res(byte: u8, bit: u8) -> u8 {
byte & !(1 << bit)
}
fn set(byte: u8, bit: u8) -> u8 {
byte | (1 << bit)
}