chore: document IO registers
This commit is contained in:
parent
1449ed3765
commit
a904503b90
18
src/bus.rs
18
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(),
|
||||
|
|
|
@ -178,7 +178,7 @@ impl Egui {
|
|||
|
||||
ui.horizontal(|ui| {
|
||||
ui.label("TAC");
|
||||
ui.monospace(format!("{:?}", timer.control));
|
||||
ui.monospace(format!("{:?}", timer.ctrl));
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
41
src/ppu.rs
41
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,
|
||||
}
|
||||
|
||||
|
|
|
@ -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<u16>,
|
||||
#[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<u16>,
|
||||
}
|
||||
|
||||
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<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?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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! {
|
||||
|
|
|
@ -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<SoundStatus> 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! {
|
||||
|
|
12
src/timer.rs
12
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<u8>,
|
||||
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,
|
||||
|
|
Loading…
Reference in New Issue