From 6395f11d5d401b0de45b9ef0185f743aa8e2c2ca Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20Magalh=C3=A3es?= <joamag@gmail.com>
Date: Mon, 27 Feb 2023 00:05:39 +0000
Subject: [PATCH] feat: initial PPU read and write code

---
 src/apu.rs | 109 ++++++++++++++++++++++++++++++++++++++++++++++++++---
 src/gb.rs  |   3 +-
 src/mmu.rs |   4 +-
 3 files changed, 108 insertions(+), 8 deletions(-)

diff --git a/src/apu.rs b/src/apu.rs
index dc25415c..f7932444 100644
--- a/src/apu.rs
+++ b/src/apu.rs
@@ -1,24 +1,121 @@
+use crate::warnln;
+
 pub struct Apu {
+    ch1_sweep_slope: u8,
+    ch1_sweep_increase: bool,
+    ch1_sweep_pace: u8,
+    ch1_length_timer: u8,
+    ch1_wave_duty: u8,
+    ch1_pace: u8,
+    ch1_direction: u8,
+    ch1_volume: u8,
+    ch1_wave_length: u16,
+    ch1_sound_length: bool,
+    ch1_enabled: bool,
+
+    ch2_length_timer: u8,
+    ch2_wave_duty: u8,
+    ch2_pace: u8,
+    ch2_direction: u8,
+    ch2_volume: u8,
+    ch2_wave_length: u16,
+    ch2_sound_length: bool,
+    ch2_enabled: bool,
 }
 
 impl Apu {
     pub fn new() -> Self {
-        Self {}
+        Self {
+            ch1_sweep_slope: 0x0,
+            ch1_sweep_increase: false,
+            ch1_sweep_pace: 0x0,
+            ch1_length_timer: 0x0,
+            ch1_wave_duty: 0x0,
+            ch1_pace: 0x0,
+            ch1_direction: 0x0,
+            ch1_volume: 0x0,
+            ch1_wave_length: 0x0,
+            ch1_sound_length: false,
+            ch1_enabled: false,
+
+            ch2_length_timer: 0x0,
+            ch2_wave_duty: 0x0,
+            ch2_pace: 0x0,
+            ch2_direction: 0x0,
+            ch2_volume: 0x0,
+            ch2_wave_length: 0x0,
+            ch2_sound_length: false,
+            ch2_enabled: false,
+        }
     }
 
     pub fn read(&mut self, addr: u16) -> u8 {
         match addr {
             0xff26 => 1 as u8, // todo implement this
-            _ => 0x00
+            _ => {
+                warnln!("Reading from unknown APU location 0x{:04x}", addr);
+                0xff
+            }
         }
     }
 
     pub fn write(&mut self, addr: u16, value: u8) {
         match addr {
-            0xff26 => {
-                // @todo implement the logic here
-            },
-            _ => {}
+            // 0xFF10 — NR10: Channel 1 length timer & duty cycle
+            0xff10 => {
+                self.ch1_sweep_slope = value & 0x03;
+                self.ch1_sweep_increase = value & 0x04 == 0x04;
+                self.ch1_sweep_pace = (value & 0x70) >> 4;
+            }
+            // 0xFF11 — NR11: Channel 1 length timer & duty cycle
+            0xff11 => {
+                self.ch1_length_timer = value & 0x3f;
+                self.ch1_wave_duty = (value & 0xc0) >> 6;
+            }
+            // 0xFF12 — NR12: Channel 1 volume & envelope
+            0xff12 => {
+                self.ch1_pace = value & 0x07;
+                self.ch1_direction = (value & 0x08) >> 3;
+                self.ch1_volume = (value & 0xf0) >> 4;
+            }
+            // 0xFF13 — NR13: Channel 1 wavelength low
+            0xff13 => {
+                self.ch1_wave_length = (self.ch1_wave_length & 0xff00) | value as u16;
+            }
+            // 0xFF14 — NR14: Channel 1 wavelength high & control
+            0xff14 => {
+                self.ch1_wave_length =
+                    (self.ch1_wave_length & 0x00ff) | (((value & 0x07) as u16) << 8);
+                self.ch1_sound_length |= value & 0x40 == 0x40;
+                self.ch1_enabled |= value & 0x80 == 0x80;
+                println!("CH1 Enabled {}", self.ch1_enabled);
+            }
+
+            // 0xFF16 — NR21: Channel 2 length timer & duty cycle
+            0xff16 => {
+                self.ch2_length_timer = value & 0x3f;
+                self.ch2_wave_duty = (value & 0xc0) >> 6;
+            }
+            // 0xFF17 — NR22: Channel 2 volume & envelope
+            0xff17 => {
+                self.ch2_pace = value & 0x07;
+                self.ch2_direction = (value & 0x08) >> 3;
+                self.ch2_volume = (value & 0xf0) >> 4;
+            }
+            // 0xFF18 — NR23: Channel 2 wavelength low
+            0xff18 => {
+                self.ch2_wave_length = (self.ch2_wave_length & 0xff00) | value as u16;
+            }
+            // 0xFF19 — NR24: Channel 1 wavelength high & control
+            0xff19 => {
+                self.ch2_wave_length =
+                    (self.ch2_wave_length & 0x00ff) | (((value & 0x07) as u16) << 8);
+                self.ch2_sound_length |= value & 0x40 == 0x40;
+                self.ch2_enabled |= value & 0x80 == 0x80;
+                println!("CH2 Enabled {}", self.ch2_enabled);
+            }
+
+            _ => warnln!("Writing in unknown APU location 0x{:04x}", addr),
         }
     }
 }
diff --git a/src/gb.rs b/src/gb.rs
index e7a1952f..61e8afbc 100644
--- a/src/gb.rs
+++ b/src/gb.rs
@@ -1,4 +1,5 @@
 use crate::{
+    apu::Apu,
     cpu::Cpu,
     data::{BootRom, CGB_BOOT, DMG_BOOT, DMG_BOOTIX, MGB_BOOTIX, SGB_BOOT},
     gen::{COMPILATION_DATE, COMPILATION_TIME, COMPILER, COMPILER_VERSION},
@@ -7,7 +8,7 @@ use crate::{
     ppu::{Ppu, PpuMode, Tile, FRAME_BUFFER_SIZE},
     rom::Cartridge,
     timer::Timer,
-    util::read_file, apu::Apu,
+    util::read_file,
 };
 
 #[cfg(feature = "wasm")]
diff --git a/src/mmu.rs b/src/mmu.rs
index ed1324b0..f7916e8a 100644
--- a/src/mmu.rs
+++ b/src/mmu.rs
@@ -1,4 +1,4 @@
-use crate::{debugln, pad::Pad, ppu::Ppu, rom::Cartridge, timer::Timer, apu::Apu};
+use crate::{apu::Apu, debugln, pad::Pad, ppu::Ppu, rom::Cartridge, timer::Timer};
 
 pub const BOOT_SIZE: usize = 2304;
 pub const RAM_SIZE: usize = 8192;
@@ -162,6 +162,7 @@ impl Mmu {
                                 0x00
                             }
                         },
+                        0x10..=26 | 0x30..=0x37 => self.apu.read(addr),
                         0x40 | 0x50 | 0x60 | 0x70 => self.ppu.read(addr),
                         _ => {
                             debugln!("Reading from unknown IO control 0x{:04x}", addr);
@@ -232,6 +233,7 @@ impl Mmu {
                                 0x04..=0x07 => self.timer.write(addr, value),
                                 _ => debugln!("Writing to unknown IO control 0x{:04x}", addr),
                             },
+                            0x10..=26 | 0x30..=0x37 => self.apu.write(addr, value),
                             0x40 | 0x60 | 0x70 => {
                                 match addr & 0x00ff {
                                     // 0xFF46 — DMA: OAM DMA source address & start
-- 
GitLab