Newer
Older
pub pc: u16,
pub sp: u16,
pub a: u8,
pub b: u8,
pub c: u8,
pub d: u8,
pub e: u8,
pub h: u8,
pub l: u8,
zero: bool,
sub: bool,
half_carry: bool,
carry: bool,
/// Reference to the MMU (Memory Management Unit) to be used
/// for memory bus access operations.
/// Temporary counter used to control the number of cycles
/// taken by the current or last CPU operation.
pub cycles: u8,
/// The pointer to the parent configuration of the running
/// Game Boy emulator, that can be used to control the behaviour
/// of Game Boy emulation.
gbc: Rc<RefCell<GameBoyConfig>>,
pub fn new(mmu: Mmu, gbc: Rc<RefCell<GameBoyConfig>>) -> Self {
a: 0x0,
b: 0x0,
c: 0x0,
d: 0x0,
e: 0x0,
h: 0x0,
l: 0x0,
zero: false,
sub: false,
half_carry: false,
carry: false,
pub fn reset(&mut self) {
self.pc = 0x0;
self.sp = 0x0;
self.a = 0x0;
self.b = 0x0;
self.c = 0x0;
self.d = 0x0;
self.e = 0x0;
self.h = 0x0;
self.l = 0x0;
self.ime = false;
self.zero = false;
self.sub = false;
self.half_carry = false;
self.carry = false;
self.halted = false;
/// Sets the CPU registers and some of the memory space to the
/// state expected after the Game Boy boot ROM executes, using
/// these values its possible to skip the boot loading process.
pub fn boot(&mut self) {
self.pc = 0x0100;
self.sp = 0xfffe;
self.a = 0x01;
self.b = 0xff;
self.c = 0x13;
self.d = 0x00;
self.e = 0xc1;
self.h = 0x84;
self.l = 0x03;
self.zero = false;
self.sub = false;
self.half_carry = false;
self.carry = false;
// updates part of the MMU state, disabling the
// boot memory overlap and setting the LCD control
// register to enabled (required by some ROMs)
self.mmu.set_boot_active(false);
self.mmu.write(0xff40, 0x91);
}
// gathers the PC (program counter) reference that
// is going to be used in the fetching phase
panic!("Invalid PC area at 0x{:04x}", pc);
}
// @TODO this is so bad, need to improve this by an order
// of magnitude, to be able to have better performance
// in case the CPU execution halted and there's an interrupt
// to be handled, releases the CPU from the halted state
// this verification is only done in case the IME (interrupt
// master enable) is disabled, otherwise the CPU halt disabled
// is going to be handled ahead
&& (((self.mmu.ie & 0x01 == 0x01) && self.mmu.ppu().int_vblank())
|| ((self.mmu.ie & 0x02 == 0x02) && self.mmu.ppu().int_stat())
|| ((self.mmu.ie & 0x04 == 0x04) && self.mmu.timer().int_tima())
|| ((self.mmu.ie & 0x08 == 0x08) && self.mmu.serial().int_serial())
|| ((self.mmu.ie & 0x10 == 0x10) && self.mmu.pad().int_pad()))
{
self.halted = false;
// checks the IME (interrupt master enable) is enabled and then checks
// if there's any interrupt to be handled, in case there's one, tries
// to check which one should be handled and then handles it
// this code assumes that the're no more that one interrupt triggered
// per clock cycle, this is a limitation of the current implementation
if self.ime && self.mmu.ie != 0x00 {
if (self.mmu.ie & 0x01 == 0x01) && self.mmu.ppu().int_vblank() {
debugln!("Going to run V-Blank interrupt handler (0x40)");
self.disable_int();
self.push_word(pc);
self.pc = 0x40;
// acknowledges that the V-Blank interrupt has been
// properly handled
// in case the CPU is currently halted waiting
// for an interrupt, releases it
if self.halted {
self.halted = false;
}
} else if (self.mmu.ie & 0x02 == 0x02) && self.mmu.ppu().int_stat() {
debugln!("Going to run LCD STAT interrupt handler (0x48)");
self.disable_int();
self.push_word(pc);
self.pc = 0x48;
// acknowledges that the STAT interrupt has been
// properly handled
self.mmu.ppu().ack_stat();
// in case the CPU is currently halted waiting
// for an interrupt, releases it
if self.halted {
self.halted = false;
}
} else if (self.mmu.ie & 0x04 == 0x04) && self.mmu.timer().int_tima() {
debugln!("Going to run Timer interrupt handler (0x50)");
self.disable_int();
self.push_word(pc);
self.pc = 0x50;
// acknowledges that the timer interrupt has been
// properly handled
self.mmu.timer().ack_tima();
// in case the CPU is currently halted waiting
// for an interrupt, releases it
if self.halted {
self.halted = false;
}
return 24;
} else if (self.mmu.ie & 0x08 == 0x08) && self.mmu.serial().int_serial() {
debugln!("Going to run Serial interrupt handler (0x58)");
self.disable_int();
self.push_word(pc);
self.pc = 0x58;
// acknowledges that the serial interrupt has been
// properly handled
self.mmu.serial().ack_serial();
// in case the CPU is currently halted waiting
// for an interrupt, releases it
if self.halted {
} else if (self.mmu.ie & 0x10 == 0x10) && self.mmu.pad().int_pad() {
debugln!("Going to run JoyPad interrupt handler (0x60)");
self.disable_int();
self.push_word(pc);
self.pc = 0x60;
// acknowledges that the pad interrupt has been
// properly handled
self.mmu.pad().ack_pad();
// in case the CPU is currently halted waiting
// for an interrupt, releases it
if self.halted {
self.halted = false;
}
// in case the CPU is currently in the halted state
// returns the control flow immediately with the associated
// number of cycles estimated for the halted execution
if self.halted {
return 4;
}
// fetches the current instruction and increments
// the PC (program counter) accordingly
let mut opcode = self.mmu.read(self.pc);
self.pc = self.pc.wrapping_add(1);
opcode = self.mmu.read(self.pc);
self.pc = self.pc.wrapping_add(1);
#[allow(unused_variables)]
#[cfg(feature = "cpulog")]
if *inst_str == "! UNIMP !" || *inst_str == "HALT" {
if *inst_str == "HALT" {
debugln!("HALT with IE=0x{:02x} IME={}", self.mmu.ie, self.ime);
"{}\t(0x{:02x})\t${:04x} {}",
let title_str = format!("[0x{:04x}] {}", self.pc - 1, inst_str);
let inst_time_str = format!("({} cycles)", inst_time);
let registers_str = format!("[PC=0x{:04x} SP=0x{:04x}] [A=0x{:02x} B=0x{:02x} C=0x{:02x} D=0x{:02x} E=0x{:02x} H=0x{:02x} L=0x{:02x}]",
self.pc, self.sp, self.a, self.b, self.c, self.d, self.e, self.h, self.l);
"{0: <24} {1: <11} {2: <10}",
title_str, inst_time_str, registers_str
#[cfg(feature = "pedantic")]
if self.mmu.boot_active() && self.pc - 1 > 0x08ff {
panic!("Invalid boot address: {:04x}", self.pc - 1);
}
// calls the current instruction and increments the number of
// cycles executed by the instruction time of the instruction
// that has just been executed
self.cycles = 0;
inst_fn(self);
self.cycles = self.cycles.wrapping_add(*inst_time);
// returns the number of cycles that the operation
// that has been executed has taken
#[inline(always)]
pub fn mmu(&mut self) -> &mut Mmu {
&mut self.mmu
}
#[inline(always)]
pub fn mmu_i(&self) -> &Mmu {
&self.mmu
}
#[inline(always)]
pub fn ppu(&mut self) -> &mut Ppu {
self.mmu().ppu()
}
#[inline(always)]
pub fn ppu_i(&self) -> &Ppu {
self.mmu_i().ppu_i()
}
#[inline(always)]
pub fn apu(&mut self) -> &mut Apu {
self.mmu().apu()
}
#[inline(always)]
pub fn apu_i(&self) -> &Apu {
self.mmu_i().apu_i()
}
#[inline(always)]
pub fn dma(&mut self) -> &mut Dma {
self.mmu().dma()
}
#[inline(always)]
pub fn dma_i(&self) -> &Dma {
self.mmu_i().dma_i()
}
#[inline(always)]
pub fn pad(&mut self) -> &mut Pad {
self.mmu().pad()
}
#[inline(always)]
pub fn pad_i(&self) -> &Pad {
self.mmu_i().pad_i()
}
#[inline(always)]
pub fn timer(&mut self) -> &mut Timer {
self.mmu().timer()
}
#[inline(always)]
pub fn timer_i(&self) -> &Timer {
self.mmu_i().timer_i()
}
#[inline(always)]
pub fn serial(&mut self) -> &mut Serial {
self.mmu().serial()
}
#[inline(always)]
pub fn serial_i(&self) -> &Serial {
self.mmu_i().serial_i()
}
#[inline(always)]
pub fn halted(&self) -> bool {
self.halted
}
#[inline(always)]
pub fn pc(&self) -> u16 {
self.pc
}
#[inline(always)]
pub fn set_pc(&mut self, value: u16) {
self.pc = value;
}
#[inline(always)]
pub fn sp(&self) -> u16 {
self.sp
}
#[inline(always)]
pub fn set_sp(&mut self, value: u16) {
self.sp = value;
}
let mut f = 0x0u8;
if self.zero {
f |= 0x80;
}
if self.sub {
f |= 0x40;
}
if self.half_carry {
f |= 0x20;
}
if self.carry {
f |= 0x10;
}
f
}
#[inline(always)]
pub fn set_f(&mut self, value: u8) {
self.zero = value & 0x80 == 0x80;
self.sub = value & 0x40 == 0x40;
self.half_carry = value & 0x20 == 0x20;
self.carry = value & 0x10 == 0x10;
}
#[inline(always)]
pub fn set_af(&mut self, value: u16) {
self.a = (value >> 8) as u8;
self.set_f(value as u8);
}
pub fn set_bc(&mut self, value: u16) {
self.b = (value >> 8) as u8;
self.c = value as u8;
}
#[inline(always)]
(self.d as u16) << 8 | self.e as u16
}
#[inline(always)]
pub fn set_de(&mut self, value: u16) {
self.d = (value >> 8) as u8;
self.e = value as u8;
(self.h as u16) << 8 | self.l as u16
}
#[inline(always)]
pub fn set_hl(&mut self, value: u16) {
self.h = (value >> 8) as u8;
self.l = value as u8;
#[inline(always)]
pub fn ime(&self) -> bool {
self.ime
}
#[inline(always)]
pub fn set_ime(&mut self, value: bool) {
self.ime = value;
}
pub fn read_u8(&mut self) -> u8 {
byte
}
#[inline(always)]
pub fn read_u16(&mut self) -> u16 {
let byte1 = self.read_u8();
let byte2 = self.read_u8();
pub fn push_byte(&mut self, byte: u8) {
self.mmu.write(self.sp, byte);
}
#[inline(always)]
pub fn push_word(&mut self, word: u16) {
self.push_byte((word >> 8) as u8);
self.push_byte(word as u8);
}
pub fn pop_byte(&mut self) -> u8 {
pub fn pop_word(&mut self) -> u16 {
self.pop_byte() as u16 | ((self.pop_byte() as u16) << 8)
self.zero
}
#[inline(always)]
pub fn set_zero(&mut self, value: bool) {
pub fn set_sub(&mut self, value: bool) {
self.half_carry
}
#[inline(always)]
pub fn set_half_carry(&mut self, value: bool) {
self.carry
}
#[inline(always)]
pub fn set_carry(&mut self, value: bool) {
#[inline(always)]
pub fn halt(&mut self) {
self.halted = true;
}
let mmu = self.mmu();
if mmu.switching {
#[inline(always)]
pub fn enable_int(&mut self) {
}
#[inline(always)]
pub fn disable_int(&mut self) {
pub fn set_gbc(&mut self, value: Rc<RefCell<GameBoyConfig>>) {
self.gbc = value;
}
impl Default for Cpu {
fn default() -> Self {
let gbc: Rc<RefCell<GameBoyConfig>> = Rc::new(RefCell::new(GameBoyConfig::default()));
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
#[test]
fn test_cpu_clock() {
let mut cpu = Cpu::default();
cpu.boot();
cpu.mmu.allocate_default();
// test NOP instruction
cpu.pc = 0xc000;
cpu.mmu.write(0xc000, 0x00);
let cycles = cpu.clock();
assert_eq!(cycles, 4);
assert_eq!(cpu.pc, 0xc001);
// test LD A, d8 instruction
cpu.pc = 0xc000;
cpu.mmu.write(0xc000, 0x3e);
cpu.mmu.write(0xc001, 0x42);
let cycles = cpu.clock();
assert_eq!(cycles, 8);
assert_eq!(cpu.pc, 0xc002);
assert_eq!(cpu.a, 0x42);
// test LD (HL+), A instruction
cpu.pc = 0xc000;
cpu.mmu.write(0xc000, 0x22);
cpu.set_hl(0xc000);
let cycles = cpu.clock();
assert_eq!(cycles, 8);
assert_eq!(cpu.pc, 0xc001);
assert_eq!(cpu.hl(), 0xc001);
assert_eq!(cpu.mmu.read(cpu.hl()), 0x42);
// test INC A instruction
cpu.pc = 0xc000;
cpu.mmu.write(0xc000, 0x3c);
cpu.a = 0x42;
let cycles = cpu.clock();
assert_eq!(cycles, 4);
assert_eq!(cpu.pc, 0xc001);
assert_eq!(cpu.a, 0x43);
// test DEC A instruction
cpu.pc = 0xc000;
cpu.mmu.write(0xc000, 0x3d);
cpu.a = 0x42;
let cycles = cpu.clock();
assert_eq!(cycles, 4);
assert_eq!(cpu.pc, 0xc001);
assert_eq!(cpu.a, 0x41);
// test LD A, (HL) instruction
cpu.pc = 0xc000;
cpu.mmu.write(0xc000, 0x7e);
cpu.set_hl(0xc001);
cpu.mmu.write(0xc001, 0x42);
let cycles = cpu.clock();
assert_eq!(cycles, 8);
assert_eq!(cpu.pc, 0xc001);
assert_eq!(cpu.a, 0x42);
assert_eq!(cpu.hl(), 0xc001);
// test LD (HL), d8 instruction
cpu.pc = 0xc000;
cpu.mmu.write(0xc000, 0x36);
cpu.set_hl(0xc000);
cpu.mmu.write(0xc001, 0x42);
let cycles = cpu.clock();
assert_eq!(cycles, 12);
assert_eq!(cpu.pc, 0xc002);
assert_eq!(cpu.hl(), 0xc000);
assert_eq!(cpu.mmu.read(cpu.hl()), 0x42);
// test JR n instruction
cpu.pc = 0xc000;
cpu.mmu.write(0xc000, 0x18);
cpu.mmu.write(0xc001, 0x03);
let cycles = cpu.clock();
assert_eq!(cycles, 12);
assert_eq!(cpu.pc, 0xc005);
// test ADD A, d8 instruction
cpu.pc = 0xc000;
cpu.mmu.write(0xc000, 0xc6);
cpu.mmu.write(0xc001, 0x01);
cpu.a = 0x42;
let cycles = cpu.clock();
assert_eq!(cycles, 8);
assert_eq!(cpu.pc, 0xc002);
assert_eq!(cpu.a, 0x43);
// test SUB A, d8 instruction
cpu.pc = 0xc000;
cpu.mmu.write(0xc000, 0xd6);
cpu.mmu.write(0xc001, 0x01);
cpu.a = 0x42;
let cycles = cpu.clock();
assert_eq!(cycles, 8);
assert_eq!(cpu.pc, 0xc002);
assert_eq!(cpu.a, 0x41);
// test AND A, d8 instruction
cpu.pc = 0xc000;
cpu.mmu.write(0xc000, 0xe6);
cpu.mmu.write(0xc001, 0x0f);
cpu.a = 0x0a;
let cycles = cpu.clock();
assert_eq!(cycles, 8);
assert_eq!(cpu.pc, 0xc002);
assert_eq!(cpu.a, 0x0a & 0x0f);
// test OR A, d8 instruction
cpu.pc = 0xc000;
cpu.mmu.write(0xc000, 0xf6);
cpu.mmu.write(0xc001, 0x0f);
cpu.a = 0x0a;
let cycles = cpu.clock();
assert_eq!(cycles, 8);
assert_eq!(cpu.pc, 0xc002);
assert_eq!(cpu.a, 0x0a | 0x0f);
// test XOR A, d8 instruction
cpu.pc = 0xc000;
cpu.mmu.write(0xc000, 0xee);
cpu.mmu.write(0xc001, 0x0f);
cpu.a = 0x0a;
let cycles = cpu.clock();
assert_eq!(cycles, 8);
assert_eq!(cpu.pc, 0xc002);
assert_eq!(cpu.a, 0x0a ^ 0x0f);
}
}