From 1da01a318da6480a8c74c368db2bb45161f9900e Mon Sep 17 00:00:00 2001 From: Rekai Musuka Date: Tue, 19 Jan 2021 00:30:10 -0600 Subject: [PATCH] feat: emulator now sucessfully runs boot rom --- src/bus.rs | 9 +++++++ src/main.rs | 11 --------- src/ppu.rs | 55 ++++++++++++++++++++++++++++++++---------- src/sound.rs | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 119 insertions(+), 23 deletions(-) diff --git a/src/bus.rs b/src/bus.rs index 3b10a1e..46659f2 100644 --- a/src/bus.rs +++ b/src/bus.rs @@ -112,6 +112,7 @@ impl Bus { 0xFF0F => self.interrupt.flag.into(), 0xFF11 => self.sound.ch1.sound_duty.into(), 0xFF12 => self.sound.ch1.vol_envelope.into(), + 0xFF14 => self.sound.ch1.freq.get_hi(), 0xFF24 => self.sound.control.channel.into(), 0xFF25 => self.sound.control.select.into(), 0xFF26 => self.sound.control.status.into(), @@ -180,6 +181,8 @@ impl Bus { 0xFF0F => self.interrupt.flag = byte.into(), 0xFF11 => self.sound.ch1.sound_duty = byte.into(), 0xFF12 => self.sound.ch1.vol_envelope = byte.into(), + 0xFF13 => self.sound.ch1.freq.set_lo(byte), + 0xFF14 => self.sound.ch1.freq.set_hi(byte), 0xFF24 => self.sound.control.channel = byte.into(), 0xFF25 => self.sound.control.select = byte.into(), 0xFF26 => self.sound.control.status = byte.into(), // FIXME: Should we control which bytes are written to here? @@ -189,6 +192,12 @@ impl Bus { 0xFF43 => self.ppu.pos.scroll_x = byte, 0xFF44 => self.ppu.pos.line_y = byte, 0xFF47 => self.ppu.monochrome.bg_palette = byte.into(), + 0xFF50 => { + // Disable Boot ROM + if byte != 0 { + self.boot = None; + } + } _ => unimplemented!("Unable to write to {:#06X} in I/O Registers", addr), }; } diff --git a/src/main.rs b/src/main.rs index 1f9e976..1f2d22a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -54,17 +54,6 @@ fn main() -> Result<()> { // window.request_redraw(); } }); - - // loop { - // let pc = game_boy.register_pair(gb::cpu::RegisterPair::PC); - // let opcode = game_boy.fetch(); - // let instruction = game_boy.decode(opcode); - - // let _cycles = game_boy.execute(instruction); - - // let ppu = game_boy.get_ppu(); - // ppu.step(); - // } } pub fn create_window(event_loop: &EventLoop<()>) -> Result { diff --git a/src/ppu.rs b/src/ppu.rs index a84c6f7..e7e25fe 100644 --- a/src/ppu.rs +++ b/src/ppu.rs @@ -1,5 +1,7 @@ use crate::instruction::Cycles; +const GB_WIDTH: usize = 160; +const GB_HEIGHT: usize = 144; #[derive(Debug, Clone)] pub struct PPU { pub lcd_control: LCDControl, @@ -7,32 +9,60 @@ pub struct PPU { pub pos: ScreenPosition, pub vram: Box<[u8]>, pub oam: Box<[u8]>, + frame_buf: [u8; GB_WIDTH * GB_HEIGHT * 4], pub stat: LCDStatus, cycles: Cycles, - mode: PPUMode, + mode: Mode, } impl PPU { pub fn step(&mut self, cycles: Cycles) { self.cycles += cycles; + // let smth: u32 = self.cycles.into(); + // println!("Mode: {:?} | Cycles: {}", self.mode, smth); + match self.mode { - PPUMode::OAMScan => { + Mode::OAMScan => { if self.cycles >= 80.into() { - self.cycles = cycles % 80; + self.cycles %= 80; + self.mode = Mode::Draw; + } + } + Mode::Draw => { + if self.cycles >= 172.into() { + // 172 -> 129 Cycles + // self.cycles %= 172; + self.mode = Mode::HBlank; + } + } + Mode::HBlank => { + // The 80 comes from the 80 cycles we made disappear in OAMScan above. + if self.cycles >= (456 - 80).into() { + self.cycles %= 456 - 80; + self.pos.line_y += 1; + + if self.pos.line_y >= 144 { + self.mode = Mode::VBlank; + } + } + } + Mode::VBlank => { + if self.cycles >= 456.into() { + self.cycles %= 456; + self.pos.line_y += 1; + + if self.pos.line_y == 154 { + self.mode = Mode::OAMScan; + self.pos.line_y = 0; + } } } - PPUMode::Draw => {} - PPUMode::HBlank => {} - PPUMode::VBlank => {} } } pub fn draw(&self, frame: &mut [u8]) { - for (_i, pixel) in frame.chunks_exact_mut(4).enumerate() { - let rgba: [u8; 4] = [0xFF, 0xFF, 0xFF, 0xFF]; - pixel.copy_from_slice(&rgba); - } + frame.copy_from_slice(&self.frame_buf); } } @@ -46,13 +76,14 @@ impl Default for PPU { vram: vec![0; 8192].into_boxed_slice(), oam: vec![0; 160].into_boxed_slice(), cycles: 0.into(), - mode: PPUMode::OAMScan, + frame_buf: [0; GB_WIDTH * GB_HEIGHT * 4], + mode: Mode::OAMScan, } } } #[derive(Debug, Clone, Copy)] -enum PPUMode { +enum Mode { OAMScan, Draw, HBlank, diff --git a/src/sound.rs b/src/sound.rs index 25bfd61..a56edda 100644 --- a/src/sound.rs +++ b/src/sound.rs @@ -19,6 +19,72 @@ pub struct SoundControl { pub status: SoundStatus, } +#[derive(Debug, Clone, Copy, Default)] +pub struct Frequency { + initial: bool, + freq_type: FrequencyType, + lo: u8, // Bottom 8 bits + hi: u8, // Top 3 bits (11 bits total) +} + +#[derive(Debug, Clone, Copy)] +enum FrequencyType { + Counter, + Consequtive, +} + +impl From for FrequencyType { + fn from(byte: u8) -> Self { + match byte { + 0b00 => Self::Counter, + 0b01 => Self::Consequtive, + _ => unreachable!("{} is not a valid number for FreuquencyType"), + } + } +} + +impl From for u8 { + fn from(freq_type: FrequencyType) -> Self { + match freq_type { + FrequencyType::Counter => 0b00, + FrequencyType::Consequtive => 0b01, + } + } +} + +impl Default for FrequencyType { + fn default() -> Self { + Self::Counter + } +} + +impl Frequency { + fn get_11bit_freq(&self) -> u16 { + (self.hi as u16) << 8 | self.lo as u16 + } + + pub fn set_lo(&mut self, byte: u8) { + self.lo = byte; + } + + pub fn get_lo(&self) -> u8 { + self.lo + } + + pub fn set_hi(&mut self, byte: u8) { + *self = Self { + initial: byte >> 7 == 0x01, + freq_type: ((byte >> 6) & 0x01).into(), + lo: self.lo, + hi: byte & 0x07, + }; + } + + pub fn get_hi(&self) -> u8 { + (self.initial as u8) << 7 | (self.freq_type as u8) << 6 | self.hi + } +} + #[derive(Debug, Clone, Copy, Default)] pub struct SoundStatus { pub all_enabled: bool, // You can actually write to this one. @@ -54,6 +120,7 @@ impl From for u8 { pub struct Channel1 { pub sound_duty: SoundDuty, pub vol_envelope: VolumeEnvelope, + pub freq: Frequency, } #[derive(Debug, Clone, Copy, Default)]