From 965db0e0cb7fbfbd9a1d8c5391c76cb270e3c14b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20Magalh=C3=A3es?= <joamag@gmail.com>
Date: Sat, 6 May 2023 09:54:49 +0100
Subject: [PATCH] chore: initial support for the DMA controller This is only a
 structure change and no real DMA implementation has been done.

---
 src/cpu.rs | 11 +++++++++++
 src/dma.rs | 17 +++++++++++++++++
 src/gb.rs  | 38 +++++++++++++++++++++++++++++++++++++-
 src/lib.rs |  1 +
 src/mmu.rs | 16 ++++++++++++++++
 5 files changed, 82 insertions(+), 1 deletion(-)
 create mode 100644 src/dma.rs

diff --git a/src/cpu.rs b/src/cpu.rs
index ba60309d..39bb3c01 100644
--- a/src/cpu.rs
+++ b/src/cpu.rs
@@ -3,6 +3,7 @@ use std::{cell::RefCell, rc::Rc};
 use crate::{
     apu::Apu,
     debugln,
+    dma::Dma,
     gb::GameBoyConfig,
     inst::{EXTENDED, INSTRUCTIONS},
     mmu::Mmu,
@@ -334,6 +335,16 @@ impl Cpu {
         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()
diff --git a/src/dma.rs b/src/dma.rs
new file mode 100644
index 00000000..9f6d014c
--- /dev/null
+++ b/src/dma.rs
@@ -0,0 +1,17 @@
+pub struct Dma {}
+
+impl Dma {
+    pub fn new() -> Self {
+        Self {}
+    }
+
+    pub fn reset(&mut self) {}
+
+    pub fn clock(&mut self, cycles: u8) {}
+}
+
+impl Default for Dma {
+    fn default() -> Self {
+        Self::new()
+    }
+}
diff --git a/src/gb.rs b/src/gb.rs
index 9be41c40..9de814b6 100644
--- a/src/gb.rs
+++ b/src/gb.rs
@@ -10,6 +10,7 @@ use crate::{
     cpu::Cpu,
     data::{BootRom, CGB_BOOT, DMG_BOOT, DMG_BOOTIX, MGB_BOOTIX, SGB_BOOT},
     devices::{printer::PrinterDevice, stdout::StdoutDevice},
+    dma::Dma,
     gen::{COMPILATION_DATE, COMPILATION_TIME, COMPILER, COMPILER_VERSION, VERSION},
     mmu::Mmu,
     pad::{Pad, PadKey},
@@ -133,6 +134,9 @@ pub struct GameBoyConfig {
     /// If the APU is enabled, it will be clocked.
     apu_enabled: bool,
 
+    /// if the DMA is enabled, it will be clocked.
+    dma_enabled: bool,
+
     /// If the timer is enabled, it will be clocked.
     timer_enabled: bool,
 
@@ -186,6 +190,14 @@ impl GameBoyConfig {
         self.apu_enabled = value;
     }
 
+    pub fn dma_enabled(&self) -> bool {
+        self.dma_enabled
+    }
+
+    pub fn set_dma_enabled(&mut self, value: bool) {
+        self.dma_enabled = value;
+    }
+
     pub fn timer_enabled(&self) -> bool {
         self.timer_enabled
     }
@@ -217,6 +229,7 @@ impl Default for GameBoyConfig {
             mode: GameBoyMode::Dmg,
             ppu_enabled: true,
             apu_enabled: true,
+            dma_enabled: true,
             timer_enabled: true,
             serial_enabled: true,
             clock_freq: GameBoy::CPU_FREQ,
@@ -246,6 +259,11 @@ pub struct GameBoy {
     /// kept for performance reasons.
     apu_enabled: bool,
 
+    /// If the DMA is enabled, it will be clocked.
+    /// This is a clone of the configuration value
+    /// kept for performance reasons.
+    dma_enabled: bool,
+
     /// If the timer is enabled, it will be clocked.
     /// This is a clone of the configuration value
     /// kept for performance reasons.
@@ -314,6 +332,7 @@ impl GameBoy {
             mode,
             ppu_enabled: true,
             apu_enabled: true,
+            dma_enabled: true,
             timer_enabled: true,
             serial_enabled: true,
             clock_freq: GameBoy::CPU_FREQ,
@@ -321,16 +340,18 @@ impl GameBoy {
 
         let ppu = Ppu::new(mode, gbc.clone());
         let apu = Apu::default();
+        let dma = Dma::default();
         let pad = Pad::default();
         let timer = Timer::default();
         let serial = Serial::default();
-        let mmu = Mmu::new(ppu, apu, pad, timer, serial, mode, gbc.clone());
+        let mmu = Mmu::new(ppu, apu, dma, pad, timer, serial, mode, gbc.clone());
         let cpu = Cpu::new(mmu, gbc.clone());
 
         Self {
             mode,
             ppu_enabled: true,
             apu_enabled: true,
+            dma_enabled: true,
             timer_enabled: true,
             serial_enabled: true,
             clock_freq: GameBoy::CPU_FREQ,
@@ -356,6 +377,9 @@ impl GameBoy {
         if self.apu_enabled {
             self.apu_clock(cycles);
         }
+        if self.dma_enabled {
+            self.dma_clock(cycles);
+        }
         if self.timer_enabled {
             self.timer_clock(cycles);
         }
@@ -385,6 +409,10 @@ impl GameBoy {
         self.apu().clock(cycles)
     }
 
+    pub fn dma_clock(&mut self, cycles: u8) {
+        self.dma().clock(cycles)
+    }
+
     pub fn timer_clock(&mut self, cycles: u8) {
         self.timer().clock(cycles)
     }
@@ -735,6 +763,14 @@ impl GameBoy {
         self.cpu.apu_i()
     }
 
+    pub fn dma(&mut self) -> &mut Dma {
+        self.cpu.dma()
+    }
+
+    pub fn dma_i(&self) -> &Dma {
+        self.cpu.dma_i()
+    }
+
     pub fn pad(&mut self) -> &mut Pad {
         self.cpu.pad()
     }
diff --git a/src/lib.rs b/src/lib.rs
index 80f4f5b8..d25f5ac6 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -4,6 +4,7 @@ pub mod apu;
 pub mod cpu;
 pub mod data;
 pub mod devices;
+pub mod dma;
 pub mod gb;
 pub mod gen;
 pub mod inst;
diff --git a/src/mmu.rs b/src/mmu.rs
index c6a0eea1..6b7ebe86 100644
--- a/src/mmu.rs
+++ b/src/mmu.rs
@@ -3,6 +3,7 @@ use std::{cell::RefCell, rc::Rc};
 use crate::{
     apu::Apu,
     debugln,
+    dma::Dma,
     gb::{GameBoyConfig, GameBoyMode, GameBoySpeed},
     pad::Pad,
     ppu::Ppu,
@@ -44,6 +45,11 @@ pub struct Mmu {
     /// some of the access operations.
     apu: Apu,
 
+    /// Reference to the DMA (Direct Memory Access) controller that is going
+    /// to be used for quick and CPU offloaded memory transfers.
+    /// There are multiple execution modes for the DMA.
+    dma: Dma,
+
     /// Reference to the Gamepad structure that is going to control
     /// the I/O access to this device.
     pad: Pad,
@@ -95,6 +101,7 @@ impl Mmu {
     pub fn new(
         ppu: Ppu,
         apu: Apu,
+        dma: Dma,
         pad: Pad,
         timer: Timer,
         serial: Serial,
@@ -104,6 +111,7 @@ impl Mmu {
         Self {
             ppu,
             apu,
+            dma,
             pad,
             timer,
             serial,
@@ -161,6 +169,14 @@ impl Mmu {
         &self.apu
     }
 
+    pub fn dma(&mut self) -> &mut Dma {
+        &mut self.dma
+    }
+
+    pub fn dma_i(&self) -> &Dma {
+        &self.dma
+    }
+
     pub fn pad(&mut self) -> &mut Pad {
         &mut self.pad
     }
-- 
GitLab