From 12497caaa706e6d970065a82031e8b670a0423c8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20Magalh=C3=A3es?= <joamag@gmail.com>
Date: Mon, 4 Jul 2022 16:06:59 +0100
Subject: [PATCH] feat: initial game pad support

---
 src/cpu.rs |  4 +--
 src/gb.rs  |  8 +++---
 src/lib.rs |  1 +
 src/mmu.rs | 76 +++++++++++++++++++++++++--------------------------
 src/pad.rs | 79 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/ppu.rs |  4 +--
 6 files changed, 127 insertions(+), 45 deletions(-)
 create mode 100644 src/pad.rs

diff --git a/src/cpu.rs b/src/cpu.rs
index 4862a4b7..ceed42e7 100644
--- a/src/cpu.rs
+++ b/src/cpu.rs
@@ -28,7 +28,7 @@ pub struct Cpu {
 }
 
 impl Cpu {
-    pub fn new(mmu: Mmu) -> Cpu {
+    pub fn new(mmu: Mmu) -> Self {
         let mut implemented = 0;
         let mut implemented_ext = 0;
 
@@ -55,7 +55,7 @@ impl Cpu {
             EXTENDED.len()
         );
 
-        Cpu {
+        Self {
             pc: 0x0,
             sp: 0x0,
             a: 0x0,
diff --git a/src/gb.rs b/src/gb.rs
index ca34032c..286fdc4b 100644
--- a/src/gb.rs
+++ b/src/gb.rs
@@ -2,6 +2,7 @@ use crate::{
     cpu::Cpu,
     data::{DMG_BOOT, SGB_BOOT},
     mmu::Mmu,
+    pad::Pad,
     ppu::{Ppu, FRAME_BUFFER_SIZE},
     util::read_file,
 };
@@ -20,11 +21,12 @@ pub struct GameBoy {
 #[cfg_attr(feature = "wasm", wasm_bindgen)]
 impl GameBoy {
     #[cfg_attr(feature = "wasm", wasm_bindgen(constructor))]
-    pub fn new() -> GameBoy {
+    pub fn new() -> Self {
+        let pad = Pad::new();
         let ppu = Ppu::new();
-        let mmu = Mmu::new(ppu);
+        let mmu = Mmu::new(ppu, pad);
         let cpu = Cpu::new(mmu);
-        GameBoy { cpu: cpu }
+        Self { cpu: cpu }
     }
 
     pub fn pc(&self) -> u16 {
diff --git a/src/lib.rs b/src/lib.rs
index 23d5ff7a..cd9b622a 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -3,5 +3,6 @@ pub mod data;
 pub mod gb;
 pub mod inst;
 pub mod mmu;
+pub mod pad;
 pub mod ppu;
 pub mod util;
diff --git a/src/mmu.rs b/src/mmu.rs
index ce4c54d2..2d9ace7f 100644
--- a/src/mmu.rs
+++ b/src/mmu.rs
@@ -1,4 +1,4 @@
-use crate::ppu::Ppu;
+use crate::{pad::Pad, ppu::Ppu};
 
 pub const BIOS_SIZE: usize = 256;
 pub const ROM_SIZE: usize = 32768;
@@ -7,6 +7,7 @@ pub const ERAM_SIZE: usize = 8192;
 
 pub struct Mmu {
     ppu: Ppu,
+    pad: Pad,
     boot_active: bool,
     boot: [u8; BIOS_SIZE],
     rom: [u8; ROM_SIZE],
@@ -15,9 +16,10 @@ pub struct Mmu {
 }
 
 impl Mmu {
-    pub fn new(ppu: Ppu) -> Mmu {
-        Mmu {
+    pub fn new(ppu: Ppu, pad: Pad) -> Self {
+        Self {
             ppu: ppu,
+            pad: pad,
             boot_active: true,
             boot: [0u8; BIOS_SIZE],
             rom: [0u8; ROM_SIZE],
@@ -76,11 +78,14 @@ impl Mmu {
                         self.ppu.hram[(addr & 0x007f) as usize]
                     } else {
                         match addr & 0x00f0 {
+                            0x00 => match addr & 0x00ff {
+                                0x00 => self.pad.read(addr),
+                                _ => {
+                                    println!("Reading from unknown IO control 0x{:04x}", addr);
+                                    0x00
+                                }
+                            },
                             0x40 | 0x50 | 0x60 | 0x70 => self.ppu.read(addr),
-                            0x00 => {
-                                println!("Reading from Game Pad control 0x{:04x}", addr);
-                                0x00
-                            }
                             _ => {
                                 println!("Reading from unknown IO control 0x{:04x}", addr);
                                 0x00
@@ -129,41 +134,36 @@ impl Mmu {
                 self.ram[(addr & 0x1fff) as usize] = value;
             }
             // Working RAM Shadow, I/O, Zero-page RAM
-            0xf000 => {
-                match addr & 0x0f00 {
-                    0x000 | 0x100 | 0x200 | 0x300 | 0x400 | 0x500 | 0x600 | 0x700 | 0x800
-                    | 0x900 | 0xa00 | 0xb00 | 0xc00 | 0xd00 => {
-                        self.ram[(addr & 0x1fff) as usize] = value;
-                    }
-                    0xe00 => {
-                        println!("Writing to PPU OAM at 0x{:04x}", addr);
-                    }
-                    0xf00 => {
-                        if addr >= 0xff80 {
-                            self.ppu.hram[(addr & 0x007f) as usize] = value;
-                        } else {
-                            match addr & 0x00f0 {
-                                0x40 | 0x60 | 0x70 => {
-                                    self.ppu.write(addr, value);
-                                }
-                                0x00 => {
-                                    println!("Writing to Game Pad, timer, etc. control 0x{:04x} := 0x{:02x}", addr, value);
-                                }
-                                0x50 => match addr & 0x00ff {
-                                    0x50 => self.boot_active = false,
-                                    _ => {
-                                        println!("Writing to unknown IO control 0x{:04x}", addr);
-                                    }
-                                },
-                                _ => {
-                                    println!("Writing to unknown IO control 0x{:04x}", addr);
-                                }
+            0xf000 => match addr & 0x0f00 {
+                0x000 | 0x100 | 0x200 | 0x300 | 0x400 | 0x500 | 0x600 | 0x700 | 0x800 | 0x900
+                | 0xa00 | 0xb00 | 0xc00 | 0xd00 => {
+                    self.ram[(addr & 0x1fff) as usize] = value;
+                }
+                0xe00 => {
+                    println!("Writing to PPU OAM at 0x{:04x}", addr);
+                }
+                0xf00 => {
+                    if addr >= 0xff80 {
+                        self.ppu.hram[(addr & 0x007f) as usize] = value;
+                    } else {
+                        match addr & 0x00f0 {
+                            0x00 => match addr & 0x00ff {
+                                0x00 => self.pad.write(addr, value),
+                                _ => println!("Writing to unknown IO control 0x{:04x}", addr),
+                            },
+                            0x40 | 0x60 | 0x70 => {
+                                self.ppu.write(addr, value);
                             }
+                            0x50 => match addr & 0x00ff {
+                                0x50 => self.boot_active = false,
+                                _ => println!("Writing to unknown IO control 0x{:04x}", addr),
+                            },
+                            _ => println!("Writing to unknown IO control 0x{:04x}", addr),
                         }
                     }
-                    addr => panic!("Writing in unknown location 0x{:04x}", addr),
                 }
-            }
+                addr => panic!("Writing in unknown location 0x{:04x}", addr),
+            },
             addr => panic!("Writing in unknown location 0x{:04x}", addr),
         }
     }
diff --git a/src/pad.rs b/src/pad.rs
new file mode 100644
index 00000000..62ad7ad3
--- /dev/null
+++ b/src/pad.rs
@@ -0,0 +1,79 @@
+pub struct Pad {
+    down: bool,
+    up: bool,
+    left: bool,
+    right: bool,
+    start: bool,
+    select: bool,
+    b: bool,
+    a: bool,
+    selection: PadSelection,
+}
+
+#[derive(Clone, Copy, PartialEq)]
+pub enum PadSelection {
+    Action,
+    Direction,
+}
+
+impl Pad {
+    pub fn new() -> Self {
+        Self {
+            down: false,
+            up: false,
+            left: false,
+            right: false,
+            start: false,
+            select: false,
+            b: false,
+            a: false,
+            selection: PadSelection::Action,
+        }
+    }
+
+    pub fn read(&mut self, addr: u16) -> u8 {
+        match addr & 0x00ff {
+            0x0000 => {
+                let mut value;
+                match self.selection {
+                    PadSelection::Action => {
+                        value = if self.a { 0x01 } else { 0x00 }
+                            | if self.b { 0x02 } else { 0x00 }
+                            | if self.select { 0x04 } else { 0x00 }
+                            | if self.start { 0x08 } else { 0x00 }
+                    }
+                    PadSelection::Direction => {
+                        value = if self.right { 0x01 } else { 0x00 }
+                            | if self.left { 0x02 } else { 0x00 }
+                            | if self.up { 0x04 } else { 0x00 }
+                            | if self.down { 0x08 } else { 0x00 }
+                    }
+                }
+                value |= if self.selection == PadSelection::Direction {
+                    0x00
+                } else {
+                    0x10
+                } | if self.selection == PadSelection::Action {
+                    0x00
+                } else {
+                    0x20
+                };
+                value
+            }
+            addr => panic!("Reading from unknown Pad location 0x{:04x}", addr),
+        }
+    }
+
+    pub fn write(&mut self, addr: u16, value: u8) {
+        match addr & 0x00ff {
+            0x0000 => {
+                self.selection = if value & 0x10 == 0x00 {
+                    PadSelection::Direction
+                } else {
+                    PadSelection::Action
+                }
+            }
+            addr => panic!("Reading from unknown Pad location 0x{:04x}", addr),
+        }
+    }
+}
diff --git a/src/ppu.rs b/src/ppu.rs
index c3b7667a..84c7e4b7 100644
--- a/src/ppu.rs
+++ b/src/ppu.rs
@@ -95,8 +95,8 @@ pub enum PpuMode {
 }
 
 impl Ppu {
-    pub fn new() -> Ppu {
-        Ppu {
+    pub fn new() -> Self {
+        Self {
             frame_buffer: Box::new([0u8; DISPLAY_WIDTH * DISPLAY_HEIGHT * RGB_SIZE]),
             vram: [0u8; VRAM_SIZE],
             hram: [0u8; HRAM_SIZE],
-- 
GitLab