chore: document IO registers

This commit is contained in:
Rekai Nyangadzayi Musuka 2021-06-09 13:43:46 -05:00
parent 1449ed3765
commit a904503b90
8 changed files with 83 additions and 56 deletions

View File

@ -225,13 +225,13 @@ impl BusIo for Bus {
// Every address here starts with 0xFF so we can just check the // Every address here starts with 0xFF so we can just check the
// low byte to figure out which register it is // low byte to figure out which register it is
match addr & 0x00FF { match addr & 0x00FF {
0x00 => self.joypad.status, 0x00 => self.joypad.p1,
0x01 => self.serial.next, 0x01 => self.serial.next,
0x02 => self.serial.control.into(), 0x02 => self.serial.ctrl.into(),
0x04 => (self.timer.divider >> 8) as u8, 0x04 => (self.timer.divider >> 8) as u8,
0x05 => self.timer.counter, 0x05 => self.timer.counter,
0x06 => self.timer.modulo, 0x06 => self.timer.modulo,
0x07 => self.timer.control.into(), 0x07 => self.timer.ctrl.into(),
0x0F => self.interrupt_flag().into(), 0x0F => self.interrupt_flag().into(),
0x11 => self.sound.ch1.sound_duty.into(), 0x11 => self.sound.ch1.sound_duty.into(),
0x12 => self.sound.ch1.vol_envelope.into(), 0x12 => self.sound.ch1.vol_envelope.into(),
@ -239,13 +239,13 @@ impl BusIo for Bus {
0x24 => self.sound.control.channel.into(), 0x24 => self.sound.control.channel.into(),
0x25 => self.sound.control.output.into(), 0x25 => self.sound.control.output.into(),
0x26 => self.sound.control.status.into(), 0x26 => self.sound.control.status.into(),
0x40 => self.ppu.control.into(), 0x40 => self.ppu.ctrl.into(),
0x41 => self.ppu.stat.into(), 0x41 => self.ppu.stat.into(),
0x42 => self.ppu.pos.scroll_y, 0x42 => self.ppu.pos.scroll_y,
0x43 => self.ppu.pos.scroll_x, 0x43 => self.ppu.pos.scroll_x,
0x44 => self.ppu.pos.line_y, 0x44 => self.ppu.pos.line_y,
0x45 => self.ppu.pos.ly_compare as u8, 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(), 0x47 => self.ppu.monochrome.bg_palette.into(),
0x48 => self.ppu.monochrome.obj_palette_0.into(), 0x48 => self.ppu.monochrome.obj_palette_0.into(),
0x49 => self.ppu.monochrome.obj_palette_1.into(), 0x49 => self.ppu.monochrome.obj_palette_1.into(),
@ -336,11 +336,11 @@ impl BusIo for Bus {
match addr & 0x00FF { match addr & 0x00FF {
0x00 => self.joypad.update(byte), 0x00 => self.joypad.update(byte),
0x01 => self.serial.next = byte, 0x01 => self.serial.next = byte,
0x02 => self.serial.control = byte.into(), 0x02 => self.serial.ctrl = byte.into(),
0x04 => self.timer.divider = 0x0000, 0x04 => self.timer.divider = 0x0000,
0x05 => self.timer.counter = byte, 0x05 => self.timer.counter = byte,
0x06 => self.timer.modulo = byte, 0x06 => self.timer.modulo = byte,
0x07 => self.timer.control = byte.into(), 0x07 => self.timer.ctrl = byte.into(),
0x0F => self.set_interrupt_flag(byte), 0x0F => self.set_interrupt_flag(byte),
0x11 => self.sound.ch1.sound_duty = byte.into(), 0x11 => self.sound.ch1.sound_duty = byte.into(),
0x12 => self.sound.ch1.vol_envelope = 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(), 0x24 => self.sound.control.channel = byte.into(),
0x25 => self.sound.control.output = 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? 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), 0x41 => self.ppu.stat.update(byte),
0x42 => self.ppu.pos.scroll_y = byte, 0x42 => self.ppu.pos.scroll_y = byte,
0x43 => self.ppu.pos.scroll_x = byte, 0x43 => self.ppu.pos.scroll_x = byte,
@ -367,7 +367,7 @@ impl BusIo for Bus {
self.ppu.int.set_lcd_stat(true); 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(), 0x47 => self.ppu.monochrome.bg_palette = byte.into(),
0x48 => self.ppu.monochrome.obj_palette_0 = byte.into(), 0x48 => self.ppu.monochrome.obj_palette_0 = byte.into(),
0x49 => self.ppu.monochrome.obj_palette_1 = byte.into(), 0x49 => self.ppu.monochrome.obj_palette_1 = byte.into(),

View File

@ -178,7 +178,7 @@ impl Egui {
ui.horizontal(|ui| { ui.horizontal(|ui| {
ui.label("TAC"); ui.label("TAC");
ui.monospace(format!("{:?}", timer.control)); ui.monospace(format!("{:?}", timer.ctrl));
}); });
}); });

View File

@ -2,7 +2,8 @@ use gilrs::{Button, Event as GamepadEvent, EventType as GamepadEventType};
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub struct Joypad { pub struct Joypad {
pub(crate) status: u8, /// 0xFF00 | P1/JOYP - Player 1 Joypad
pub(crate) p1: u8,
ext: JoypadState, ext: JoypadState,
interrupt: bool, interrupt: bool,
} }
@ -10,7 +11,7 @@ pub struct Joypad {
impl Default for Joypad { impl Default for Joypad {
fn default() -> Self { fn default() -> Self {
Self { Self {
status: 0xFF, p1: 0xFF,
ext: Default::default(), ext: Default::default(),
interrupt: Default::default(), interrupt: Default::default(),
} }
@ -42,7 +43,7 @@ impl Joypad {
} }
}; };
self.status = updated self.p1 = updated
} }
} }

View File

@ -2,7 +2,7 @@ use crate::bus::BusIo;
use crate::Cycle; use crate::Cycle;
use crate::GB_HEIGHT; use crate::GB_HEIGHT;
use crate::GB_WIDTH; use crate::GB_WIDTH;
use dma::DmaProcess; use dma::DirectMemoryAccess;
use std::collections::VecDeque; use std::collections::VecDeque;
use std::convert::TryInto; use std::convert::TryInto;
pub(crate) use types::PpuMode; pub(crate) use types::PpuMode;
@ -36,13 +36,15 @@ const BLACK: [u8; 4] = 0x202020FFu32.to_be_bytes();
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Ppu { pub struct Ppu {
pub(crate) int: Interrupt, 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) monochrome: Monochrome,
pub(crate) pos: ScreenPosition, pub(crate) pos: ScreenPosition,
vram: Box<[u8; VRAM_SIZE]>, vram: Box<[u8; VRAM_SIZE]>,
pub(crate) stat: LCDStatus,
pub(crate) oam: ObjectAttributeTable, pub(crate) oam: ObjectAttributeTable,
pub(crate) dma: DmaProcess, pub(crate) dma: DirectMemoryAccess,
scan_state: OamScanState, scan_state: OamScanState,
fetch: PixelFetcher, fetch: PixelFetcher,
fifo: FifoRenderer, fifo: FifoRenderer,
@ -105,7 +107,7 @@ impl Ppu {
self.fifo.obj.clear(); self.fifo.obj.clear();
self.stat.set_mode(PpuMode::HBlank); 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 // Only Draw when the LCD Is Enabled
self.draw(self.cycle.into()); self.draw(self.cycle.into());
} else { } else {
@ -196,7 +198,7 @@ impl Ppu {
.set_coincidence(self.pos.line_y == self.pos.window_y); .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 index = self.scan_state.count();
let attr = self.oam.attribute(index as usize); let attr = self.oam.attribute(index as usize);
@ -244,7 +246,7 @@ impl Ppu {
} }
ToLowByteSleep => self.fetch.obj.next(TileLowByte), ToLowByteSleep => self.fetch.obj.next(TileLowByte),
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); let addr = PixelFetcher::get_obj_addr(&attr, &self.pos, obj_size);
@ -255,7 +257,7 @@ impl Ppu {
} }
ToHighByteSleep => self.fetch.obj.next(TileHighByte), ToHighByteSleep => self.fetch.obj.next(TileHighByte),
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); 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.should_draw()
&& self.window_stat.coincidence() && self.window_stat.coincidence()
&& self.x_pos >= self.pos.window_x - 7 && self.x_pos >= self.pos.window_x - 7
@ -331,7 +333,7 @@ impl Ppu {
.back .back
.should_render_window(self.window_stat.should_draw()); .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); let id = self.read_byte(addr);
self.fetch.back.tile.with_id(id); self.fetch.back.tile.with_id(id);
@ -341,7 +343,7 @@ impl Ppu {
} }
ToLowByteSleep => self.fetch.back.next(TileLowByte), ToLowByteSleep => self.fetch.back.next(TileLowByte),
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); let low = self.read_byte(addr);
self.fetch.back.tile.with_low_byte(low); self.fetch.back.tile.with_low_byte(low);
@ -350,7 +352,7 @@ impl Ppu {
} }
ToHighByteSleep => self.fetch.back.next(TileHighByte), ToHighByteSleep => self.fetch.back.next(TileHighByte),
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); let high = self.read_byte(addr + 1);
self.fetch.back.tile.with_high_byte(high); self.fetch.back.tile.with_high_byte(high);
@ -376,8 +378,8 @@ impl Ppu {
use RenderPriority::*; use RenderPriority::*;
// Handle Background Pixel and Sprite FIFO // Handle Background Pixel and Sprite FIFO
let bg_enabled = self.control.bg_win_enabled(); let bg_enabled = self.ctrl.bg_win_enabled();
let obj_enabled = self.control.obj_enabled(); let obj_enabled = self.ctrl.obj_enabled();
let i0_colour = self.monochrome.bg_palette.i0_colour(); let i0_colour = self.monochrome.bg_palette.i0_colour();
// FIXME: Is this the correct behaviour // FIXME: Is this the correct behaviour
@ -443,7 +445,7 @@ impl Default for Ppu {
cycle: Cycle::new(0), cycle: Cycle::new(0),
frame_buf: Box::new([0; GB_WIDTH * GB_HEIGHT * 4]), frame_buf: Box::new([0; GB_WIDTH * GB_HEIGHT * 4]),
int: Default::default(), int: Default::default(),
control: Default::default(), ctrl: Default::default(),
monochrome: Default::default(), monochrome: Default::default(),
pos: Default::default(), pos: Default::default(),
stat: Default::default(), stat: Default::default(),
@ -485,18 +487,27 @@ impl Interrupt {
#[derive(Debug, Clone, Copy, Default)] #[derive(Debug, Clone, Copy, Default)]
pub(crate) struct ScreenPosition { pub(crate) struct ScreenPosition {
/// 0xFF42 | SCY - Scroll Y
pub(crate) scroll_y: u8, pub(crate) scroll_y: u8,
/// 0xFF43 | SCX - Scroll X
pub(crate) scroll_x: u8, pub(crate) scroll_x: u8,
/// 0xFF44 | LY - LCD Y Coordinate
pub(crate) line_y: u8, pub(crate) line_y: u8,
/// 0xFF45 | LYC - LY Compare
pub(crate) ly_compare: u8, pub(crate) ly_compare: u8,
/// 0xFF4A | WY - Window Y Position
pub(crate) window_y: u8, pub(crate) window_y: u8,
/// 0xFF4B | WX - Window X Position
pub(crate) window_x: u8, pub(crate) window_x: u8,
} }
#[derive(Debug, Clone, Copy, Default)] #[derive(Debug, Clone, Copy, Default)]
pub(crate) struct Monochrome { pub(crate) struct Monochrome {
/// 0xFF47 | BGP - Background Palette Data
pub(crate) bg_palette: BackgroundPalette, pub(crate) bg_palette: BackgroundPalette,
/// 0xFF48 | OBP0 - Object Palette 0 Data
pub(crate) obj_palette_0: ObjectPalette, pub(crate) obj_palette_0: ObjectPalette,
/// 0xFF49 | OBP1 - Object Palette 1 Data
pub(crate) obj_palette_1: ObjectPalette, pub(crate) obj_palette_1: ObjectPalette,
} }

View File

@ -1,13 +1,14 @@
use crate::instruction::Cycle; use crate::instruction::Cycle;
#[derive(Debug, Default, Clone)] #[derive(Debug, Default, Clone)]
pub(crate) struct DmaProcess { pub(crate) struct DirectMemoryAccess {
pub(crate) state: DmaState, pub(crate) state: DmaState,
cycle: Cycle, 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)> { pub(crate) fn clock(&mut self) -> Option<(u16, u16)> {
match self.state { match self.state {
DmaState::Pending => { DmaState::Pending => {
@ -26,8 +27,8 @@ impl DmaProcess {
self.cycle += 1; self.cycle += 1;
let src_addr = self let src_addr = self
.ctrl .start
.src_addr .addr
.as_mut() .as_mut()
.expect("DMA Transfer Attempted without a known source address"); .expect("DMA Transfer Attempted without a known source address");
@ -57,7 +58,7 @@ impl DmaProcess {
fn reset(&mut self) { fn reset(&mut self) {
self.cycle = Cycle::new(0); self.cycle = Cycle::new(0);
self.state = DmaState::Disabled; self.state = DmaState::Disabled;
self.ctrl.src_addr = None; self.start.addr = None;
} }
} }
@ -74,27 +75,28 @@ impl Default for DmaState {
} }
} }
#[derive(Debug, Clone)] #[derive(Debug, Default, Clone, Copy)]
pub(crate) struct DmaControl { pub(crate) struct DmaAddress {
pub(crate) repr: u8, /// The current *source* address of the DMA Transfer
src_addr: Option<u16>, ///
/// NB: Will be None if no DMA Transfer is in progress
addr: Option<u16>,
} }
impl Default for DmaControl { impl DmaAddress {
fn default() -> Self {
Self {
repr: 0,
src_addr: None,
}
}
}
impl DmaControl {
pub(crate) fn update(&mut self, byte: u8, state: &mut DmaState) { pub(crate) fn update(&mut self, byte: u8, state: &mut DmaState) {
let start = (byte as u16) << 8; let start = (byte as u16) << 8;
self.repr = byte; self.addr = Some(start);
self.src_addr = Some(start);
*state = DmaState::Pending; *state = DmaState::Pending;
} }
} }
impl From<DmaAddress> 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?
}
}
}

View File

@ -2,8 +2,10 @@ use bitfield::bitfield;
#[derive(Debug, Clone, Copy, Default)] #[derive(Debug, Clone, Copy, Default)]
pub(crate) struct Serial { pub(crate) struct Serial {
/// 0xFF01 | SB - Serial Transfer Data
pub(crate) next: u8, pub(crate) next: u8,
pub(crate) control: SerialControl, /// 0xFF02 | SC - Serial Transfer Control
pub(crate) ctrl: SerialControl,
} }
bitfield! { bitfield! {

View File

@ -14,8 +14,11 @@ impl Sound {
#[derive(Debug, Clone, Copy, Default)] #[derive(Debug, Clone, Copy, Default)]
pub(crate) struct SoundControl { pub(crate) struct SoundControl {
/// 0xFF24 | NR50 - Channel Control
pub(crate) channel: ChannelControl, pub(crate) channel: ChannelControl,
/// 0xFF25 | NR51 - Selection of Sound output terminal
pub(crate) output: SoundOutput, pub(crate) output: SoundOutput,
/// 0xFF26 | NR52 - Sound On/Off
pub(crate) status: SoundStatus, pub(crate) status: SoundStatus,
} }
@ -138,10 +141,14 @@ impl From<SoundStatus> for u8 {
#[derive(Debug, Clone, Copy, Default)] #[derive(Debug, Clone, Copy, Default)]
pub(crate) struct Channel1 { pub(crate) struct Channel1 {
/// 0xFF11 | NR11 - Channel 1 Sound length / Wave pattern duty
pub(crate) sound_duty: SoundDuty, pub(crate) sound_duty: SoundDuty,
/// 0xFF12 | NR12 - Channel 1 Volume Envelope
pub(crate) vol_envelope: VolumeEnvelope, 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, pub(crate) freq_lo: FrequencyLow,
/// 0xFF14 | NR14 - Channel 1 Frequency High
pub(crate) freq_hi: FrequencyHigh,
} }
bitfield! { bitfield! {

View File

@ -3,9 +3,13 @@ use bitfield::bitfield;
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub(crate) struct Timer { pub(crate) struct Timer {
pub(crate) control: TimerControl, /// 0xFF07 | TAC - Timer Control
pub(crate) ctrl: TimerControl,
/// 0xFF05 | TIMA - Timer Counter
pub(crate) counter: u8, pub(crate) counter: u8,
/// 0xFF06 | TMA - Timer Modulo
pub(crate) modulo: u8, pub(crate) modulo: u8,
/// 0xFF04 | DIV - Divider Register
pub(crate) divider: u16, pub(crate) divider: u16,
prev_and_result: Option<u8>, prev_and_result: Option<u8>,
interrupt: bool, interrupt: bool,
@ -19,7 +23,7 @@ impl Timer {
self.divider = self.divider.wrapping_add(1); self.divider = self.divider.wrapping_add(1);
// Get Bit Position // Get Bit Position
let bit = match self.control.speed() { let bit = match self.ctrl.speed() {
Hz4096 => 9, Hz4096 => 9,
Hz262144 => 3, Hz262144 => 3,
Hz65536 => 5, Hz65536 => 5,
@ -27,7 +31,7 @@ impl Timer {
}; };
let bit = (self.divider >> bit) as u8 & 0x01; 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; let and_result = bit & timer_enable;
if let Some(previous) = self.prev_and_result { if let Some(previous) = self.prev_and_result {
@ -64,7 +68,7 @@ impl Timer {
impl Default for Timer { impl Default for Timer {
fn default() -> Self { fn default() -> Self {
Self { Self {
control: Default::default(), ctrl: Default::default(),
counter: 0, counter: 0,
modulo: 0, modulo: 0,
divider: 0, divider: 0,