diff --git a/src/bus.rs b/src/bus.rs index 91c9731..99c5409 100644 --- a/src/bus.rs +++ b/src/bus.rs @@ -225,13 +225,13 @@ impl BusIo for Bus { // Every address here starts with 0xFF so we can just check the // low byte to figure out which register it is match addr & 0x00FF { - 0x00 => self.joypad.status, + 0x00 => self.joypad.p1, 0x01 => self.serial.next, - 0x02 => self.serial.control.into(), + 0x02 => self.serial.ctrl.into(), 0x04 => (self.timer.divider >> 8) as u8, 0x05 => self.timer.counter, 0x06 => self.timer.modulo, - 0x07 => self.timer.control.into(), + 0x07 => self.timer.ctrl.into(), 0x0F => self.interrupt_flag().into(), 0x11 => self.sound.ch1.sound_duty.into(), 0x12 => self.sound.ch1.vol_envelope.into(), @@ -239,13 +239,13 @@ impl BusIo for Bus { 0x24 => self.sound.control.channel.into(), 0x25 => self.sound.control.output.into(), 0x26 => self.sound.control.status.into(), - 0x40 => self.ppu.control.into(), + 0x40 => self.ppu.ctrl.into(), 0x41 => self.ppu.stat.into(), 0x42 => self.ppu.pos.scroll_y, 0x43 => self.ppu.pos.scroll_x, 0x44 => self.ppu.pos.line_y, 0x45 => self.ppu.pos.ly_compare as u8, - 0x46 => self.ppu.dma.ctrl.repr, + 0x46 => self.ppu.dma.start.into(), 0x47 => self.ppu.monochrome.bg_palette.into(), 0x48 => self.ppu.monochrome.obj_palette_0.into(), 0x49 => self.ppu.monochrome.obj_palette_1.into(), @@ -336,11 +336,11 @@ impl BusIo for Bus { match addr & 0x00FF { 0x00 => self.joypad.update(byte), 0x01 => self.serial.next = byte, - 0x02 => self.serial.control = byte.into(), + 0x02 => self.serial.ctrl = byte.into(), 0x04 => self.timer.divider = 0x0000, 0x05 => self.timer.counter = byte, 0x06 => self.timer.modulo = byte, - 0x07 => self.timer.control = byte.into(), + 0x07 => self.timer.ctrl = byte.into(), 0x0F => self.set_interrupt_flag(byte), 0x11 => self.sound.ch1.sound_duty = byte.into(), 0x12 => self.sound.ch1.vol_envelope = byte.into(), @@ -349,7 +349,7 @@ impl BusIo for Bus { 0x24 => self.sound.control.channel = byte.into(), 0x25 => self.sound.control.output = byte.into(), 0x26 => self.sound.control.status = byte.into(), // FIXME: Should we control which bytes are written to here? - 0x40 => self.ppu.control = byte.into(), + 0x40 => self.ppu.ctrl = byte.into(), 0x41 => self.ppu.stat.update(byte), 0x42 => self.ppu.pos.scroll_y = byte, 0x43 => self.ppu.pos.scroll_x = byte, @@ -367,7 +367,7 @@ impl BusIo for Bus { self.ppu.int.set_lcd_stat(true); } } - 0x46 => self.ppu.dma.ctrl.update(byte, &mut self.ppu.dma.state), + 0x46 => self.ppu.dma.start.update(byte, &mut self.ppu.dma.state), 0x47 => self.ppu.monochrome.bg_palette = byte.into(), 0x48 => self.ppu.monochrome.obj_palette_0 = byte.into(), 0x49 => self.ppu.monochrome.obj_palette_1 = byte.into(), diff --git a/src/gui.rs b/src/gui.rs index 930d157..55911bc 100644 --- a/src/gui.rs +++ b/src/gui.rs @@ -178,7 +178,7 @@ impl Egui { ui.horizontal(|ui| { ui.label("TAC"); - ui.monospace(format!("{:?}", timer.control)); + ui.monospace(format!("{:?}", timer.ctrl)); }); }); diff --git a/src/joypad.rs b/src/joypad.rs index f1db0ca..3fd6bc7 100644 --- a/src/joypad.rs +++ b/src/joypad.rs @@ -2,7 +2,8 @@ use gilrs::{Button, Event as GamepadEvent, EventType as GamepadEventType}; #[derive(Debug, Clone, Copy)] pub struct Joypad { - pub(crate) status: u8, + /// 0xFF00 | P1/JOYP - Player 1 Joypad + pub(crate) p1: u8, ext: JoypadState, interrupt: bool, } @@ -10,7 +11,7 @@ pub struct Joypad { impl Default for Joypad { fn default() -> Self { Self { - status: 0xFF, + p1: 0xFF, ext: Default::default(), interrupt: Default::default(), } @@ -42,7 +43,7 @@ impl Joypad { } }; - self.status = updated + self.p1 = updated } } diff --git a/src/ppu.rs b/src/ppu.rs index 8520d41..534ec25 100644 --- a/src/ppu.rs +++ b/src/ppu.rs @@ -2,7 +2,7 @@ use crate::bus::BusIo; use crate::Cycle; use crate::GB_HEIGHT; use crate::GB_WIDTH; -use dma::DmaProcess; +use dma::DirectMemoryAccess; use std::collections::VecDeque; use std::convert::TryInto; pub(crate) use types::PpuMode; @@ -36,13 +36,15 @@ const BLACK: [u8; 4] = 0x202020FFu32.to_be_bytes(); #[derive(Debug, Clone)] pub struct Ppu { pub(crate) int: Interrupt, - pub(crate) control: LCDControl, + /// 0xFF40 | LCDC - LCD Control + pub(crate) ctrl: LCDControl, + /// 0xFF41 | STAT - LCD Status + pub(crate) stat: LCDStatus, pub(crate) monochrome: Monochrome, pub(crate) pos: ScreenPosition, vram: Box<[u8; VRAM_SIZE]>, - pub(crate) stat: LCDStatus, pub(crate) oam: ObjectAttributeTable, - pub(crate) dma: DmaProcess, + pub(crate) dma: DirectMemoryAccess, scan_state: OamScanState, fetch: PixelFetcher, fifo: FifoRenderer, @@ -105,7 +107,7 @@ impl Ppu { self.fifo.obj.clear(); self.stat.set_mode(PpuMode::HBlank); - } else if self.control.lcd_enabled() { + } else if self.ctrl.lcd_enabled() { // Only Draw when the LCD Is Enabled self.draw(self.cycle.into()); } else { @@ -196,7 +198,7 @@ impl Ppu { .set_coincidence(self.pos.line_y == self.pos.window_y); } - let sprite_height = self.control.obj_size().as_u8(); + let sprite_height = self.ctrl.obj_size().as_u8(); let index = self.scan_state.count(); let attr = self.oam.attribute(index as usize); @@ -244,7 +246,7 @@ impl Ppu { } ToLowByteSleep => self.fetch.obj.next(TileLowByte), TileLowByte => { - let obj_size = self.control.obj_size(); + let obj_size = self.ctrl.obj_size(); let addr = PixelFetcher::get_obj_addr(&attr, &self.pos, obj_size); @@ -255,7 +257,7 @@ impl Ppu { } ToHighByteSleep => self.fetch.obj.next(TileHighByte), TileHighByte => { - let obj_size = self.control.obj_size(); + let obj_size = self.ctrl.obj_size(); let addr = PixelFetcher::get_obj_addr(&attr, &self.pos, obj_size); @@ -311,7 +313,7 @@ impl Ppu { } } - if self.control.window_enabled() + if self.ctrl.window_enabled() && !self.window_stat.should_draw() && self.window_stat.coincidence() && self.x_pos >= self.pos.window_x - 7 @@ -331,7 +333,7 @@ impl Ppu { .back .should_render_window(self.window_stat.should_draw()); - let addr = self.fetch.bg_tile_num_addr(&self.control, &self.pos, x_pos); + let addr = self.fetch.bg_tile_num_addr(&self.ctrl, &self.pos, x_pos); let id = self.read_byte(addr); self.fetch.back.tile.with_id(id); @@ -341,7 +343,7 @@ impl Ppu { } ToLowByteSleep => self.fetch.back.next(TileLowByte), TileLowByte => { - let addr = self.fetch.bg_byte_addr(&self.control, &self.pos); + let addr = self.fetch.bg_byte_addr(&self.ctrl, &self.pos); let low = self.read_byte(addr); self.fetch.back.tile.with_low_byte(low); @@ -350,7 +352,7 @@ impl Ppu { } ToHighByteSleep => self.fetch.back.next(TileHighByte), TileHighByte => { - let addr = self.fetch.bg_byte_addr(&self.control, &self.pos); + let addr = self.fetch.bg_byte_addr(&self.ctrl, &self.pos); let high = self.read_byte(addr + 1); self.fetch.back.tile.with_high_byte(high); @@ -376,8 +378,8 @@ impl Ppu { use RenderPriority::*; // Handle Background Pixel and Sprite FIFO - let bg_enabled = self.control.bg_win_enabled(); - let obj_enabled = self.control.obj_enabled(); + let bg_enabled = self.ctrl.bg_win_enabled(); + let obj_enabled = self.ctrl.obj_enabled(); let i0_colour = self.monochrome.bg_palette.i0_colour(); // FIXME: Is this the correct behaviour @@ -443,7 +445,7 @@ impl Default for Ppu { cycle: Cycle::new(0), frame_buf: Box::new([0; GB_WIDTH * GB_HEIGHT * 4]), int: Default::default(), - control: Default::default(), + ctrl: Default::default(), monochrome: Default::default(), pos: Default::default(), stat: Default::default(), @@ -485,18 +487,27 @@ impl Interrupt { #[derive(Debug, Clone, Copy, Default)] pub(crate) struct ScreenPosition { + /// 0xFF42 | SCY - Scroll Y pub(crate) scroll_y: u8, + /// 0xFF43 | SCX - Scroll X pub(crate) scroll_x: u8, + /// 0xFF44 | LY - LCD Y Coordinate pub(crate) line_y: u8, + /// 0xFF45 | LYC - LY Compare pub(crate) ly_compare: u8, + /// 0xFF4A | WY - Window Y Position pub(crate) window_y: u8, + /// 0xFF4B | WX - Window X Position pub(crate) window_x: u8, } #[derive(Debug, Clone, Copy, Default)] pub(crate) struct Monochrome { + /// 0xFF47 | BGP - Background Palette Data pub(crate) bg_palette: BackgroundPalette, + /// 0xFF48 | OBP0 - Object Palette 0 Data pub(crate) obj_palette_0: ObjectPalette, + /// 0xFF49 | OBP1 - Object Palette 1 Data pub(crate) obj_palette_1: ObjectPalette, } diff --git a/src/ppu/dma.rs b/src/ppu/dma.rs index 355bdb8..bd04133 100644 --- a/src/ppu/dma.rs +++ b/src/ppu/dma.rs @@ -1,13 +1,14 @@ use crate::instruction::Cycle; #[derive(Debug, Default, Clone)] -pub(crate) struct DmaProcess { +pub(crate) struct DirectMemoryAccess { pub(crate) state: DmaState, cycle: Cycle, - pub(crate) ctrl: DmaControl, + /// 0xFF46 | DMA - Transfer and Start Address + pub(crate) start: DmaAddress, } -impl DmaProcess { +impl DirectMemoryAccess { pub(crate) fn clock(&mut self) -> Option<(u16, u16)> { match self.state { DmaState::Pending => { @@ -26,8 +27,8 @@ impl DmaProcess { self.cycle += 1; let src_addr = self - .ctrl - .src_addr + .start + .addr .as_mut() .expect("DMA Transfer Attempted without a known source address"); @@ -57,7 +58,7 @@ impl DmaProcess { fn reset(&mut self) { self.cycle = Cycle::new(0); self.state = DmaState::Disabled; - self.ctrl.src_addr = None; + self.start.addr = None; } } @@ -74,27 +75,28 @@ impl Default for DmaState { } } -#[derive(Debug, Clone)] -pub(crate) struct DmaControl { - pub(crate) repr: u8, - src_addr: Option, +#[derive(Debug, Default, Clone, Copy)] +pub(crate) struct DmaAddress { + /// The current *source* address of the DMA Transfer + /// + /// NB: Will be None if no DMA Transfer is in progress + addr: Option, } -impl Default for DmaControl { - fn default() -> Self { - Self { - repr: 0, - src_addr: None, - } - } -} - -impl DmaControl { +impl DmaAddress { pub(crate) fn update(&mut self, byte: u8, state: &mut DmaState) { let start = (byte as u16) << 8; - self.repr = byte; - self.src_addr = Some(start); + self.addr = Some(start); *state = DmaState::Pending; } } + +impl From for u8 { + fn from(ctrl: DmaAddress) -> Self { + match ctrl.addr { + Some(addr) => (addr >> 8) as u8, + None => 0xFF, // TODO: What garbage value should be here? + } + } +} diff --git a/src/serial.rs b/src/serial.rs index 2907469..af51093 100644 --- a/src/serial.rs +++ b/src/serial.rs @@ -2,8 +2,10 @@ use bitfield::bitfield; #[derive(Debug, Clone, Copy, Default)] pub(crate) struct Serial { + /// 0xFF01 | SB - Serial Transfer Data pub(crate) next: u8, - pub(crate) control: SerialControl, + /// 0xFF02 | SC - Serial Transfer Control + pub(crate) ctrl: SerialControl, } bitfield! { diff --git a/src/sound.rs b/src/sound.rs index 0cd81af..6924ea2 100644 --- a/src/sound.rs +++ b/src/sound.rs @@ -14,8 +14,11 @@ impl Sound { #[derive(Debug, Clone, Copy, Default)] pub(crate) struct SoundControl { + /// 0xFF24 | NR50 - Channel Control pub(crate) channel: ChannelControl, + /// 0xFF25 | NR51 - Selection of Sound output terminal pub(crate) output: SoundOutput, + /// 0xFF26 | NR52 - Sound On/Off pub(crate) status: SoundStatus, } @@ -138,10 +141,14 @@ impl From for u8 { #[derive(Debug, Clone, Copy, Default)] pub(crate) struct Channel1 { + /// 0xFF11 | NR11 - Channel 1 Sound length / Wave pattern duty pub(crate) sound_duty: SoundDuty, + /// 0xFF12 | NR12 - Channel 1 Volume Envelope pub(crate) vol_envelope: VolumeEnvelope, - pub(crate) freq_hi: FrequencyHigh, + /// 0xFF13 | NR13 - Channel 1 Frequency Low (Lower 8 bits only) pub(crate) freq_lo: FrequencyLow, + /// 0xFF14 | NR14 - Channel 1 Frequency High + pub(crate) freq_hi: FrequencyHigh, } bitfield! { diff --git a/src/timer.rs b/src/timer.rs index 6e889d7..ae8a942 100644 --- a/src/timer.rs +++ b/src/timer.rs @@ -3,9 +3,13 @@ use bitfield::bitfield; #[derive(Debug, Clone, Copy)] pub(crate) struct Timer { - pub(crate) control: TimerControl, + /// 0xFF07 | TAC - Timer Control + pub(crate) ctrl: TimerControl, + /// 0xFF05 | TIMA - Timer Counter pub(crate) counter: u8, + /// 0xFF06 | TMA - Timer Modulo pub(crate) modulo: u8, + /// 0xFF04 | DIV - Divider Register pub(crate) divider: u16, prev_and_result: Option, interrupt: bool, @@ -19,7 +23,7 @@ impl Timer { self.divider = self.divider.wrapping_add(1); // Get Bit Position - let bit = match self.control.speed() { + let bit = match self.ctrl.speed() { Hz4096 => 9, Hz262144 => 3, Hz65536 => 5, @@ -27,7 +31,7 @@ impl Timer { }; let bit = (self.divider >> bit) as u8 & 0x01; - let timer_enable = self.control.enabled() as u8; + let timer_enable = self.ctrl.enabled() as u8; let and_result = bit & timer_enable; if let Some(previous) = self.prev_and_result { @@ -64,7 +68,7 @@ impl Timer { impl Default for Timer { fn default() -> Self { Self { - control: Default::default(), + ctrl: Default::default(), counter: 0, modulo: 0, divider: 0,