gb/src/bus.rs

331 lines
12 KiB
Rust
Raw Normal View History

2020-12-24 06:27:06 +00:00
use super::cartridge::Cartridge;
2021-03-16 06:05:13 +00:00
use super::high_ram::HighRam;
2021-03-27 17:10:18 +00:00
use super::instruction::Cycle;
use super::interrupt::{Interrupt, InterruptFlag};
2021-03-21 02:11:45 +00:00
use super::joypad::Joypad;
2021-03-16 06:05:13 +00:00
use super::ppu::Ppu;
2021-01-19 07:36:44 +00:00
use super::serial::Serial;
2021-01-03 08:05:46 +00:00
use super::sound::Sound;
use super::timer::Timer;
2021-03-16 06:05:13 +00:00
use super::work_ram::{VariableWorkRam, WorkRam};
use std::{fs::File, io::Read};
const BOOT_ROM_SIZE: usize = 256;
#[derive(Debug, Clone)]
2020-12-23 09:43:49 +00:00
pub struct Bus {
boot: Option<[u8; BOOT_ROM_SIZE]>, // Boot ROM is 256b long
cartridge: Option<Cartridge>,
2021-03-16 06:05:13 +00:00
pub ppu: Ppu,
wram: WorkRam,
vwram: VariableWorkRam,
timer: Timer,
interrupt: Interrupt,
2021-01-03 08:05:46 +00:00
sound: Sound,
2021-03-16 06:05:13 +00:00
hram: HighRam,
2021-01-19 07:36:44 +00:00
serial: Serial,
2021-03-21 02:11:45 +00:00
joypad: Joypad,
2020-12-23 09:43:49 +00:00
}
2020-12-23 09:43:49 +00:00
impl Default for Bus {
fn default() -> Self {
Self {
2021-01-19 07:36:44 +00:00
boot: None,
cartridge: None,
2020-12-24 06:27:06 +00:00
ppu: Default::default(),
2021-01-03 06:28:07 +00:00
wram: Default::default(),
vwram: Default::default(),
timer: Default::default(),
interrupt: Default::default(),
2021-01-03 08:05:46 +00:00
sound: Default::default(),
2021-01-18 03:13:59 +00:00
hram: Default::default(),
2021-01-19 07:36:44 +00:00
serial: Default::default(),
2021-03-21 02:11:45 +00:00
joypad: Default::default(),
}
}
}
impl Bus {
pub fn with_boot(path: &str) -> anyhow::Result<Self> {
let mut file = File::open(path)?;
let mut boot_rom = [0u8; 256];
file.read_exact(&mut boot_rom)?;
Ok(Self {
boot: Some(boot_rom),
..Default::default()
})
2020-12-23 09:43:49 +00:00
}
pub fn load_cartridge(&mut self, path: &str) -> std::io::Result<()> {
self.cartridge = Some(Cartridge::new(path)?);
Ok(())
}
2021-01-19 04:54:38 +00:00
2021-03-27 17:10:18 +00:00
pub fn step(&mut self, cycles: Cycle) {
2021-03-19 02:06:01 +00:00
self.timer.step(cycles);
self.sound.step(cycles);
self.ppu.step(cycles);
2021-01-19 04:54:38 +00:00
}
2020-12-23 09:43:49 +00:00
}
impl Bus {
2020-09-04 05:41:19 +00:00
pub fn read_byte(&self, addr: u16) -> u8 {
match addr {
0x0000..=0x3FFF => {
// 16KB ROM bank 00
if addr < 0x100 {
2021-03-16 03:08:47 +00:00
if let Some(boot) = self.boot {
return boot[addr as usize];
}
}
2021-03-16 03:08:47 +00:00
match self.cartridge.as_ref() {
Some(cart) => cart.read_byte(addr),
2021-03-21 08:03:03 +00:00
None => panic!("Tried to read from a non-existent cartridge"),
2021-03-16 03:08:47 +00:00
}
}
2021-03-16 03:08:47 +00:00
0x4000..=0x7FFF => match self.cartridge.as_ref() {
// 16KB ROM Bank 01 -> NN (switchable via MB)
Some(cart) => cart.read_byte(addr),
2021-03-21 08:03:03 +00:00
None => panic!("Tried to read from a non-existent cartridge"),
},
0x8000..=0x9FFF => {
// 8KB Video RAM
self.ppu.read_byte(addr)
}
2021-03-16 03:08:47 +00:00
0xA000..=0xBFFF => match self.cartridge.as_ref() {
// 8KB External RAM
2021-01-20 07:39:24 +00:00
Some(cart) => cart.read_byte(addr),
None => panic!("Tried to read from a non-existent cartridge"),
2021-01-20 07:39:24 +00:00
},
0xC000..=0xCFFF => {
// 4KB Work RAM Bank 0
self.wram.read_byte(addr)
}
0xD000..=0xDFFF => {
// 4KB Work RAM Bank 1 -> N
self.vwram.read_byte(addr)
}
0xE000..=0xFDFF => {
// Mirror of 0xC000 to 0xDDFF
unimplemented!("Unable to read {:#06X} in Restricted Mirror", addr);
}
0xFE00..=0xFE9F => {
2021-03-16 00:19:40 +00:00
// Sprite Attribute Table
unimplemented!("Unable to read {:#06X} in the Sprite Attribute Table", addr);
}
0xFEA0..=0xFEFF => unreachable!("{:#06X} is not allowed to be read from", addr),
0xFF00..=0xFF7F => {
// IO Registers
match addr {
2021-03-21 02:11:45 +00:00
0xFF00 => self.joypad.status.into(),
0xFF01 => self.serial.next,
0xFF02 => self.serial.control.into(),
0xFF04 => (self.timer.divider >> 8) as u8,
2021-03-21 07:01:19 +00:00
0xFF05 => self.timer.counter,
2021-03-21 08:03:03 +00:00
0xFF06 => self.timer.modulo,
0xFF07 => self.timer.control.into(),
0xFF0F => self.interrupt_flag().into(),
0xFF11 => self.sound.ch1.sound_duty.into(),
0xFF12 => self.sound.ch1.vol_envelope.into(),
0xFF14 => self.sound.ch1.freq_hi.into(),
2021-01-18 00:58:57 +00:00
0xFF24 => self.sound.control.channel.into(),
0xFF25 => self.sound.control.output.into(),
2021-01-18 00:58:57 +00:00
0xFF26 => self.sound.control.status.into(),
2021-01-18 01:25:53 +00:00
0xFF40 => self.ppu.lcd_control.into(),
0xFF41 => self.ppu.stat.into(),
0xFF42 => self.ppu.pos.scroll_y,
0xFF43 => self.ppu.pos.scroll_x,
0xFF44 => self.ppu.pos.line_y,
0xFF45 => self.ppu.pos.ly_compare as u8,
2021-01-18 01:25:53 +00:00
0xFF47 => self.ppu.monochrome.bg_palette.into(),
2021-03-16 07:36:09 +00:00
0xFF48 => self.ppu.monochrome.obj_palette_0.into(),
0xFF49 => self.ppu.monochrome.obj_palette_1.into(),
0xFF4A => self.ppu.pos.window_y,
0xFF4B => self.ppu.pos.window_x,
0xFF4D => {
eprintln!("Reading from {:#06X} is available in CGB Mode only", addr);
0x00
}
_ => unimplemented!("Unable to read {:#06X} in I/O Registers", addr),
}
}
0xFF80..=0xFFFE => {
// High RAM
self.hram.read_byte(addr)
}
0xFFFF => {
2021-03-16 00:19:40 +00:00
// Interrupts Enable Register
self.interrupt.enable.into()
}
}
}
2020-12-23 09:43:49 +00:00
pub fn write_byte(&mut self, addr: u16, byte: u8) {
match addr {
0x0000..=0x3FFF => {
// 16KB ROM bank 00
2021-01-20 07:39:24 +00:00
match self.cartridge.as_mut() {
Some(cart) => cart.write_byte(addr, byte),
None => panic!("Tried to write into non-existent cartridge"),
2021-01-20 07:39:24 +00:00
}
}
0x4000..=0x7FFF => {
// 16KB ROM Bank 01 -> NN (switchable via MB)
2021-01-20 07:39:24 +00:00
match self.cartridge.as_mut() {
Some(cart) => cart.write_byte(addr, byte),
None => panic!("Tried to write into non-existent cartridge"),
2021-01-20 07:39:24 +00:00
}
}
0x8000..=0x9FFF => {
// 8KB Video RAM
self.ppu.write_byte(addr, byte);
}
0xA000..=0xBFFF => {
// 8KB External RAM
2021-01-20 07:39:24 +00:00
match self.cartridge.as_mut() {
Some(cart) => cart.write_byte(addr, byte),
None => panic!("Tried to write into non-existent cartridge"),
2021-01-20 07:39:24 +00:00
}
}
0xC000..=0xCFFF => {
// 4KB Work RAM Bank 0
self.wram.write_byte(addr, byte);
}
0xD000..=0xDFFF => {
// 4KB Work RAM Bank 1 -> N
self.vwram.write_byte(addr, byte);
}
0xE000..=0xFDFF => {
// Mirror of 0xC000 to 0xDDFF
unimplemented!("Unable to write to {:#06X} in Restricted Mirror", addr);
}
0xFE00..=0xFE9F => {
2021-03-16 00:19:40 +00:00
// Sprite Attribute Table
unimplemented!("Unable to write to {:#06X} in Sprite Attribute Table", addr);
}
0xFEA0..=0xFEFF => unreachable!("{:#06X} is not allowed to be written to", addr),
0xFF00..=0xFF7F => {
// IO Registers
match addr {
2021-03-21 02:11:45 +00:00
0xFF00 => self.joypad.status = byte.into(),
0xFF01 => self.serial.next = byte,
0xFF02 => self.serial.control = byte.into(),
2021-03-21 08:03:03 +00:00
0xFF04 => self.timer.divider = 0x00,
2021-03-21 07:01:19 +00:00
0xFF05 => self.timer.counter = byte.into(),
2021-03-21 08:03:03 +00:00
0xFF06 => self.timer.modulo = byte.into(),
0xFF07 => self.timer.control = byte.into(),
0xFF0F => self.set_interrupt_flag(byte),
0xFF11 => self.sound.ch1.sound_duty = byte.into(),
0xFF12 => self.sound.ch1.vol_envelope = byte.into(),
0xFF13 => self.sound.ch1.freq_lo = byte.into(),
0xFF14 => self.sound.ch1.freq_hi = byte.into(),
2021-01-18 00:58:57 +00:00
0xFF24 => self.sound.control.channel = byte.into(),
0xFF25 => self.sound.control.output = byte.into(),
2021-01-18 00:58:57 +00:00
0xFF26 => self.sound.control.status = byte.into(), // FIXME: Should we control which bytes are written to here?
2021-01-18 01:25:53 +00:00
0xFF40 => self.ppu.lcd_control = byte.into(),
0xFF41 => self.ppu.stat = byte.into(),
0xFF42 => self.ppu.pos.scroll_y = byte,
0xFF43 => self.ppu.pos.scroll_x = byte,
0xFF44 => self.ppu.pos.line_y = byte,
2021-03-21 05:01:21 +00:00
0xFF45 => {
// Update LYC
self.ppu.pos.ly_compare = byte;
// Update Coincidence Flag
if self.ppu.stat.coincidence_intr() {
let are_equal = self.ppu.pos.line_y == byte;
self.ppu.stat.set_coincidence(are_equal);
}
}
2021-01-18 01:25:53 +00:00
0xFF47 => self.ppu.monochrome.bg_palette = byte.into(),
2021-03-16 07:36:09 +00:00
0xFF48 => self.ppu.monochrome.obj_palette_0 = byte.into(),
0xFF49 => self.ppu.monochrome.obj_palette_1 = byte.into(),
0xFF4A => self.ppu.pos.window_y = byte,
0xFF4B => self.ppu.pos.window_x = byte,
0xFF4D => {
eprintln!(
"Writing {:#04X} to {:#06X} is available in CGB Mode only",
byte, addr
);
}
0xFF50 => {
// Disable Boot ROM
if byte != 0 {
self.boot = None;
}
}
_ => unimplemented!("Unable to write to {:#06X} in I/O Registers", addr),
};
}
0xFF80..=0xFFFE => {
// High RAM
self.hram.write_byte(addr, byte);
}
0xFFFF => {
2021-03-16 00:19:40 +00:00
// Interrupts Enable Register
self.interrupt.enable = byte.into();
}
}
}
2020-09-04 05:41:19 +00:00
pub fn read_word(&self, addr: u16) -> u16 {
2021-01-03 04:49:25 +00:00
(self.read_byte(addr + 1) as u16) << 8 | self.read_byte(addr) as u16
}
2020-12-23 09:43:49 +00:00
pub fn write_word(&mut self, addr: u16, word: u16) {
2021-01-03 04:49:25 +00:00
self.write_byte(addr + 1, (word >> 8) as u8);
self.write_byte(addr, (word & 0x00FF) as u8);
}
2020-09-04 05:41:19 +00:00
}
impl Bus {
fn interrupt_flag(&self) -> InterruptFlag {
// Read the current interrupt information from the PPU
let vblank = self.ppu.interrupt.vblank();
let lcd_stat = self.ppu.interrupt.lcd_stat();
2021-03-21 08:03:03 +00:00
// Read the current interrupt information from the Joypad
let joypad = self.joypad.interrupt();
2021-03-21 08:03:03 +00:00
// Read the current interrupt information from the Timer
let timer = self.timer.interrupt();
// Copy the Interrupt Flag register 0xFF0F
let mut flag = self.interrupt.flag;
// Update the flag to have the most accurate information
flag.set_vblank(vblank);
flag.set_lcd_stat(lcd_stat);
flag.set_joypad(joypad);
2021-03-21 08:03:03 +00:00
flag.set_timer(timer);
flag
}
fn set_interrupt_flag(&mut self, byte: u8) {
// Update the Interrupt register 0xFF0F
self.interrupt.flag = byte.into();
let vblank = self.interrupt.flag.vblank();
let lcd_stat = self.interrupt.flag.lcd_stat();
let joypad = self.interrupt.flag.joypad();
2021-03-21 08:03:03 +00:00
let timer = self.interrupt.flag.timer();
// Update the PPU's instance of the following interrupts
self.ppu.interrupt.set_vblank(vblank);
self.ppu.interrupt.set_lcd_stat(lcd_stat);
// Update the Joypad's instance of the following interrupts
self.joypad.set_interrupt(joypad);
2021-03-21 08:03:03 +00:00
// Update the Timer's instance of the following interrupts
self.timer.set_interrupt(timer);
}
pub fn boot_enabled(&self) -> bool {
self.boot.is_some()
}
}