From 9d55eb6c344c57e6c21cc8934b53b4cdb138b315 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20Magalh=C3=A3es?= <joamag@gmail.com>
Date: Mon, 24 Apr 2023 17:41:23 +0100
Subject: [PATCH] feat: initial support for config

---
 src/cpu.rs  |  10 ++-
 src/gb.rs   | 174 +++++++++++++++++++++++++++++++++++++++++-----------
 src/inst.rs |  25 ++++++++
 src/mmu.rs  |  21 ++++++-
 4 files changed, 190 insertions(+), 40 deletions(-)

diff --git a/src/cpu.rs b/src/cpu.rs
index ba115c2e..2466a71f 100644
--- a/src/cpu.rs
+++ b/src/cpu.rs
@@ -1,8 +1,10 @@
 use core::panic;
+use std::{cell::RefCell, rc::Rc};
 
 use crate::{
     apu::Apu,
     debugln,
+    gb::GameBoyConfig,
     inst::{EXTENDED, INSTRUCTIONS},
     mmu::Mmu,
     pad::Pad,
@@ -38,10 +40,15 @@ pub struct Cpu {
     /// 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>>,
 }
 
 impl Cpu {
-    pub fn new(mmu: Mmu) -> Self {
+    pub fn new(mmu: Mmu, gbc: Rc<RefCell<GameBoyConfig>>) -> Self {
         Self {
             pc: 0x0,
             sp: 0x0,
@@ -60,6 +67,7 @@ impl Cpu {
             halted: false,
             mmu,
             cycles: 0,
+            gbc: gbc,
         }
     }
 
diff --git a/src/gb.rs b/src/gb.rs
index 4c43caa9..6e36863e 100644
--- a/src/gb.rs
+++ b/src/gb.rs
@@ -13,7 +13,7 @@ use crate::{
     util::read_file,
 };
 
-use std::collections::VecDeque;
+use std::{cell::RefCell, collections::VecDeque, rc::Rc};
 
 #[cfg(feature = "wasm")]
 use wasm_bindgen::prelude::*;
@@ -32,28 +32,22 @@ use std::{
 
 /// Enumeration that describes the multiple running
 // modes of the Game Boy emulator.
+#[cfg_attr(feature = "wasm", wasm_bindgen)]
+#[derive(Clone, Copy, PartialEq, Eq)]
 pub enum GBMode {
     Dmg = 1,
     Cgb = 2,
     Sgb = 3,
 }
 
-/// Top level structure that abstracts the usage of the
-/// Game Boy system under the Boytacean emulator.
-/// Should serve as the main entry-point API.
 #[cfg_attr(feature = "wasm", wasm_bindgen)]
-pub struct GameBoy {
+#[derive(Clone, Copy, PartialEq, Eq)]
+pub struct GameBoyConfig {
     /// The current running mode of the emulator, this
     /// may affect many aspects of the emulation, like
     /// CPU frequency, PPU frequency, Boot rome size, etc.
     mode: GBMode,
 
-    /// Reference to the Game Boy CPU component to be
-    /// used as the main element of the system, when
-    /// clocked, the amount of ticks from it will be
-    /// used as reference or the rest of the components.
-    cpu: Cpu,
-
     /// If the PPU is enabled, it will be clocked.
     ppu_enabled: bool,
 
@@ -75,6 +69,98 @@ pub struct GameBoy {
     clock_freq: u32,
 }
 
+#[cfg_attr(feature = "wasm", wasm_bindgen)]
+impl GameBoyConfig {
+    #[inline(always)]
+    pub fn is_dmg(&self) -> bool {
+        self.mode == GBMode::Dmg
+    }
+
+    #[inline(always)]
+    pub fn is_cgb(&self) -> bool {
+        self.mode == GBMode::Cgb
+    }
+
+    #[inline(always)]
+    pub fn is_sgb(&self) -> bool {
+        self.mode == GBMode::Sgb
+    }
+
+    #[inline(always)]
+    pub fn mode(&self) -> GBMode {
+        self.mode
+    }
+
+    #[inline(always)]
+    pub fn set_mode(&mut self, value: GBMode) {
+        self.mode = value;
+    }
+
+    #[inline(always)]
+    pub fn ppu_enabled(&self) -> bool {
+        self.ppu_enabled
+    }
+
+    #[inline(always)]
+    pub fn set_ppu_enabled(&mut self, value: bool) {
+        self.ppu_enabled = value;
+    }
+
+    #[inline(always)]
+    pub fn apu_enabled(&self) -> bool {
+        self.apu_enabled
+    }
+
+    #[inline(always)]
+    pub fn set_apu_enabled(&mut self, value: bool) {
+        self.apu_enabled = value;
+    }
+
+    #[inline(always)]
+    pub fn timer_enabled(&self) -> bool {
+        self.timer_enabled
+    }
+
+    #[inline(always)]
+    pub fn set_timer_enabled(&mut self, value: bool) {
+        self.timer_enabled = value;
+    }
+
+    #[inline(always)]
+    pub fn serial_enabled(&self) -> bool {
+        self.serial_enabled
+    }
+
+    #[inline(always)]
+    pub fn set_serial_enabled(&mut self, value: bool) {
+        self.serial_enabled = value;
+    }
+
+    #[inline(always)]
+    pub fn clock_freq(&self) -> u32 {
+        self.clock_freq
+    }
+
+    #[inline(always)]
+    pub fn set_clock_freq(&mut self, value: u32) {
+        self.clock_freq = value;
+    }
+}
+
+/// Top level structure that abstracts the usage of the
+/// Game Boy system under the Boytacean emulator.
+/// Should serve as the main entry-point API.
+#[cfg_attr(feature = "wasm", wasm_bindgen)]
+pub struct GameBoy {
+    /// Reference to the Game Boy CPU component to be
+    /// used as the main element of the system, when
+    /// clocked, the amount of ticks from it will be
+    /// used as reference or the rest of the components.
+    cpu: Cpu,
+
+    gbc: Rc<RefCell<GameBoyConfig>>,
+}
+
 #[cfg_attr(feature = "wasm", wasm_bindgen)]
 pub struct Registers {
     pub pc: u16,
@@ -104,22 +190,24 @@ pub trait AudioProvider {
 impl GameBoy {
     #[cfg_attr(feature = "wasm", wasm_bindgen(constructor))]
     pub fn new(mode: GBMode) -> Self {
-        let ppu = Ppu::default();
-        let apu = Apu::default();
-        let pad = Pad::default();
-        let timer = Timer::default();
-        let serial = Serial::default();
-        let mmu = Mmu::new(ppu, apu, pad, timer, serial);
-        let cpu = Cpu::new(mmu);
-        Self {
+        let gbc = Rc::new(RefCell::new(GameBoyConfig {
             mode,
-            cpu,
             ppu_enabled: true,
             apu_enabled: true,
             timer_enabled: true,
             serial_enabled: true,
             clock_freq: GameBoy::CPU_FREQ,
-        }
+        }));
+
+        let ppu = Ppu::default();
+        let apu = Apu::default();
+        let pad = Pad::default();
+        let timer = Timer::default();
+        let serial = Serial::default();
+        let mmu = Mmu::new(ppu, apu, pad, timer, serial, gbc.clone());
+        let cpu = Cpu::new(mmu, gbc.clone());
+
+        Self { cpu, gbc }
     }
 
     pub fn reset(&mut self) {
@@ -133,16 +221,16 @@ impl GameBoy {
 
     pub fn clock(&mut self) -> u8 {
         let cycles = self.cpu_clock();
-        if self.ppu_enabled {
+        if self.ppu_enabled() {
             self.ppu_clock(cycles);
         }
-        if self.apu_enabled {
+        if self.apu_enabled() {
             self.apu_clock(cycles);
         }
-        if self.timer_enabled {
+        if self.timer_enabled() {
             self.timer_clock(cycles);
         }
-        if self.serial_enabled {
+        if self.serial_enabled() {
             self.serial_clock(cycles);
         }
         cycles
@@ -193,7 +281,7 @@ impl GameBoy {
     }
 
     pub fn load(&mut self, boot: bool) {
-        match self.mode {
+        match self.mode() {
             GBMode::Dmg => self.load_dmg(boot),
             GBMode::Cgb => self.load_cgb(boot),
             GBMode::Sgb => todo!(),
@@ -357,44 +445,58 @@ impl GameBoy {
         String::from(COMPILATION_TIME)
     }
 
+    #[inline(always)]
+    pub fn mode(&self) -> GBMode {
+        (*self.gbc).borrow().mode()
+    }
+
+    pub fn set_mode(&mut self, value: GBMode) {
+        (*self.gbc).borrow_mut().set_mode(value);
+    }
+
+    #[inline(always)]
     pub fn ppu_enabled(&self) -> bool {
-        self.ppu_enabled
+        (*self.gbc).borrow().ppu_enabled()
     }
 
     pub fn set_ppu_enabled(&mut self, value: bool) {
-        self.ppu_enabled = value;
+        (*self.gbc).borrow_mut().set_ppu_enabled(value);
     }
 
+    #[inline(always)]
     pub fn apu_enabled(&self) -> bool {
-        self.apu_enabled
+        (*self.gbc).borrow().apu_enabled
     }
 
     pub fn set_apu_enabled(&mut self, value: bool) {
-        self.apu_enabled = value;
+        (*self.gbc).borrow_mut().set_apu_enabled(value);
     }
 
+    #[inline(always)]
     pub fn timer_enabled(&self) -> bool {
-        self.timer_enabled
+        (*self.gbc).borrow().timer_enabled()
     }
 
     pub fn set_timer_enabled(&mut self, value: bool) {
-        self.timer_enabled = value;
+        (*self.gbc).borrow_mut().set_timer_enabled(value);
     }
 
+    #[inline(always)]
     pub fn serial_enabled(&self) -> bool {
-        self.serial_enabled
+        (*self.gbc).borrow().serial_enabled()
     }
 
     pub fn set_serial_enabled(&mut self, value: bool) {
-        self.serial_enabled = value;
+        (*self.gbc).borrow_mut().set_serial_enabled(value);
     }
 
+    #[inline(always)]
     pub fn clock_freq(&self) -> u32 {
-        self.clock_freq
+        (*self.gbc).borrow().clock_freq()
     }
 
     pub fn set_clock_freq(&mut self, value: u32) {
-        self.clock_freq = value;
+        (*self.gbc).borrow_mut().set_clock_freq(value);
         self.apu().set_clock_freq(value);
     }
 
diff --git a/src/inst.rs b/src/inst.rs
index 8b059a59..9c58eff3 100644
--- a/src/inst.rs
+++ b/src/inst.rs
@@ -3119,11 +3119,13 @@ fn set_7_a(cpu: &mut Cpu) {
 }
 
 /// Helper function to set one bit in a u8.
+#[inline(always)]
 fn set(value: u8, bit: u8) -> u8 {
     value | (1u8 << (bit as usize))
 }
 
 /// Helper function to clear one bit in a u8.
+#[inline(always)]
 fn res(value: u8, bit: u8) -> u8 {
     value & !(1u8 << (bit as usize))
 }
@@ -3131,6 +3133,7 @@ fn res(value: u8, bit: u8) -> u8 {
 /// Helper function that rotates (shifts) left the given
 /// byte (probably from a register) and updates the
 /// proper flag registers.
+#[inline(always)]
 fn rl(cpu: &mut Cpu, value: u8) -> u8 {
     let carry = cpu.carry();
 
@@ -3145,6 +3148,7 @@ fn rl(cpu: &mut Cpu, value: u8) -> u8 {
     result
 }
 
+#[inline(always)]
 fn rlc(cpu: &mut Cpu, value: u8) -> u8 {
     cpu.set_carry((value & 0x80) == 0x80);
 
@@ -3160,6 +3164,7 @@ fn rlc(cpu: &mut Cpu, value: u8) -> u8 {
 /// Helper function that rotates (shifts) right the given
 /// byte (probably from a register) and updates the
 /// proper flag registers.
+#[inline(always)]
 fn rr(cpu: &mut Cpu, value: u8) -> u8 {
     let carry = cpu.carry();
 
@@ -3174,6 +3179,7 @@ fn rr(cpu: &mut Cpu, value: u8) -> u8 {
     result
 }
 
+#[inline(always)]
 fn rrc(cpu: &mut Cpu, value: u8) -> u8 {
     cpu.set_carry((value & 0x1) == 0x1);
 
@@ -3188,52 +3194,61 @@ fn rrc(cpu: &mut Cpu, value: u8) -> u8 {
 
 /// Helper function to test one bit in a u8.
 /// Returns true if bit is 0.
+#[inline(always)]
 fn bit_zero(val: u8, bit: u8) -> bool {
     (val & (1u8 << (bit as usize))) == 0
 }
 
+#[inline(always)]
 fn bit_a(cpu: &mut Cpu, bit: u8) {
     cpu.set_sub(false);
     cpu.set_zero(bit_zero(cpu.a, bit));
     cpu.set_half_carry(true);
 }
 
+#[inline(always)]
 fn bit_b(cpu: &mut Cpu, bit: u8) {
     cpu.set_sub(false);
     cpu.set_zero(bit_zero(cpu.b, bit));
     cpu.set_half_carry(true);
 }
 
+#[inline(always)]
 fn bit_c(cpu: &mut Cpu, bit: u8) {
     cpu.set_sub(false);
     cpu.set_zero(bit_zero(cpu.c, bit));
     cpu.set_half_carry(true);
 }
 
+#[inline(always)]
 fn bit_d(cpu: &mut Cpu, bit: u8) {
     cpu.set_sub(false);
     cpu.set_zero(bit_zero(cpu.d, bit));
     cpu.set_half_carry(true);
 }
 
+#[inline(always)]
 fn bit_e(cpu: &mut Cpu, bit: u8) {
     cpu.set_sub(false);
     cpu.set_zero(bit_zero(cpu.e, bit));
     cpu.set_half_carry(true);
 }
 
+#[inline(always)]
 fn bit_h(cpu: &mut Cpu, bit: u8) {
     cpu.set_sub(false);
     cpu.set_zero(bit_zero(cpu.h, bit));
     cpu.set_half_carry(true);
 }
 
+#[inline(always)]
 fn bit_l(cpu: &mut Cpu, bit: u8) {
     cpu.set_sub(false);
     cpu.set_zero(bit_zero(cpu.l, bit));
     cpu.set_half_carry(true);
 }
 
+#[inline(always)]
 fn bit_mhl(cpu: &mut Cpu, bit: u8) {
     let byte = cpu.mmu.read(cpu.hl());
     cpu.set_sub(false);
@@ -3241,6 +3256,7 @@ fn bit_mhl(cpu: &mut Cpu, bit: u8) {
     cpu.set_half_carry(true);
 }
 
+#[inline(always)]
 fn add_set_flags(cpu: &mut Cpu, first: u8, second: u8) -> u8 {
     let first = first as u32;
     let second = second as u32;
@@ -3256,6 +3272,7 @@ fn add_set_flags(cpu: &mut Cpu, first: u8, second: u8) -> u8 {
     result_b
 }
 
+#[inline(always)]
 fn add_carry_set_flags(cpu: &mut Cpu, first: u8, second: u8) -> u8 {
     let first = first as u32;
     let second = second as u32;
@@ -3272,6 +3289,7 @@ fn add_carry_set_flags(cpu: &mut Cpu, first: u8, second: u8) -> u8 {
     result_b
 }
 
+#[inline(always)]
 fn sub_set_flags(cpu: &mut Cpu, first: u8, second: u8) -> u8 {
     let first = first as u32;
     let second = second as u32;
@@ -3287,6 +3305,7 @@ fn sub_set_flags(cpu: &mut Cpu, first: u8, second: u8) -> u8 {
     result_b
 }
 
+#[inline(always)]
 fn sub_carry_set_flags(cpu: &mut Cpu, first: u8, second: u8) -> u8 {
     let first = first as u32;
     let second = second as u32;
@@ -3303,6 +3322,7 @@ fn sub_carry_set_flags(cpu: &mut Cpu, first: u8, second: u8) -> u8 {
     result_b
 }
 
+#[inline(always)]
 fn add_u16_u16(cpu: &mut Cpu, first: u16, second: u16) -> u16 {
     let first = first as u32;
     let second = second as u32;
@@ -3315,6 +3335,7 @@ fn add_u16_u16(cpu: &mut Cpu, first: u16, second: u16) -> u16 {
     result as u16
 }
 
+#[inline(always)]
 fn swap(cpu: &mut Cpu, value: u8) -> u8 {
     cpu.set_sub(false);
     cpu.set_zero(value == 0);
@@ -3326,6 +3347,7 @@ fn swap(cpu: &mut Cpu, value: u8) -> u8 {
 
 /// Helper function to shift an `u8` to the left and update CPU
 /// flags.
+#[inline(always)]
 fn sla(cpu: &mut Cpu, value: u8) -> u8 {
     let result = value << 1;
 
@@ -3337,6 +3359,7 @@ fn sla(cpu: &mut Cpu, value: u8) -> u8 {
     result
 }
 
+#[inline(always)]
 fn sra(cpu: &mut Cpu, value: u8) -> u8 {
     let result = (value >> 1) | (value & 0x80);
 
@@ -3348,6 +3371,7 @@ fn sra(cpu: &mut Cpu, value: u8) -> u8 {
     result
 }
 
+#[inline(always)]
 fn srl(cpu: &mut Cpu, value: u8) -> u8 {
     let result = value >> 1;
 
@@ -3362,6 +3386,7 @@ fn srl(cpu: &mut Cpu, value: u8) -> u8 {
 /// Helper function for RST instructions, pushes the
 /// current PC to the stack and jumps to the provided
 /// address.
+#[inline(always)]
 fn rst(cpu: &mut Cpu, addr: u16) {
     cpu.push_word(cpu.pc);
     cpu.pc = addr;
diff --git a/src/mmu.rs b/src/mmu.rs
index c659df2a..ff011776 100644
--- a/src/mmu.rs
+++ b/src/mmu.rs
@@ -1,4 +1,9 @@
-use crate::{apu::Apu, debugln, pad::Pad, ppu::Ppu, rom::Cartridge, serial::Serial, timer::Timer};
+use std::{cell::RefCell, rc::Rc};
+
+use crate::{
+    apu::Apu, debugln, gb::GameBoyConfig, pad::Pad, ppu::Ppu, rom::Cartridge, serial::Serial,
+    timer::Timer,
+};
 
 pub const BOOT_SIZE_DMG: usize = 256;
 pub const BOOT_SIZE_CGB: usize = 2304;
@@ -55,14 +60,23 @@ pub struct Mmu {
     ram: Vec<u8>,
 
     /// The RAM bank to be used in the read and write operation of
-    /// the 0xD000-0xDFFF memory range. CGB Only
+    /// the 0xD000-0xDFFF memory range (CGB Only).
     ram_bank: u8,
 
     ram_offset: u16,
+
+    gbc: Rc<RefCell<GameBoyConfig>>,
 }
 
 impl Mmu {
-    pub fn new(ppu: Ppu, apu: Apu, pad: Pad, timer: Timer, serial: Serial) -> Self {
+    pub fn new(
+        ppu: Ppu,
+        apu: Apu,
+        pad: Pad,
+        timer: Timer,
+        serial: Serial,
+        gbc: Rc<RefCell<GameBoyConfig>>,
+    ) -> Self {
         Self {
             ppu,
             apu,
@@ -76,6 +90,7 @@ impl Mmu {
             ram_bank: 0x1,
             ram_offset: 0x1000,
             ie: 0x0,
+            gbc: gbc,
         }
     }
 
-- 
GitLab