2021-07-15 04:45:01 +00:00
|
|
|
use crate::apu::Apu;
|
2021-06-07 01:47:11 +00:00
|
|
|
use crate::cartridge::Cartridge;
|
|
|
|
use crate::high_ram::HighRam;
|
|
|
|
use crate::interrupt::{Interrupt, InterruptFlag};
|
|
|
|
use crate::joypad::Joypad;
|
|
|
|
use crate::ppu::{Ppu, PpuMode};
|
|
|
|
use crate::serial::Serial;
|
|
|
|
use crate::timer::Timer;
|
|
|
|
use crate::work_ram::{VariableWorkRam, WorkRam};
|
2021-01-03 07:38:31 +00:00
|
|
|
|
2021-09-18 02:29:29 +00:00
|
|
|
pub(crate) const BOOT_SIZE: usize = 0x100;
|
2021-03-21 01:22:31 +00:00
|
|
|
|
2021-07-28 04:24:10 +00:00
|
|
|
#[derive(Debug)]
|
2020-12-23 09:43:49 +00:00
|
|
|
pub struct Bus {
|
2021-09-18 02:29:29 +00:00
|
|
|
boot: Option<[u8; BOOT_SIZE]>, // Boot ROM is 256b long
|
|
|
|
cart: Option<Cartridge>,
|
2021-07-19 01:55:26 +00:00
|
|
|
pub(crate) ppu: Ppu,
|
2021-04-27 09:06:08 +00:00
|
|
|
work_ram: WorkRam,
|
|
|
|
var_ram: VariableWorkRam,
|
2021-06-28 01:28:29 +00:00
|
|
|
pub(crate) timer: Timer,
|
2021-04-04 06:52:53 +00:00
|
|
|
int: Interrupt,
|
2021-07-19 01:55:26 +00:00
|
|
|
pub(crate) apu: Apu,
|
2021-04-27 09:06:08 +00:00
|
|
|
high_ram: HighRam,
|
2021-01-19 07:36:44 +00:00
|
|
|
serial: Serial,
|
2021-06-28 01:28:29 +00:00
|
|
|
pub(crate) joypad: Joypad,
|
2020-12-23 09:43:49 +00:00
|
|
|
}
|
2020-12-23 09:25:16 +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,
|
2021-09-18 02:29:29 +00:00
|
|
|
cart: None,
|
2020-12-24 06:27:06 +00:00
|
|
|
ppu: Default::default(),
|
2021-04-27 09:06:08 +00:00
|
|
|
work_ram: Default::default(),
|
|
|
|
var_ram: Default::default(),
|
2021-01-03 07:21:19 +00:00
|
|
|
timer: Default::default(),
|
2021-04-04 06:52:53 +00:00
|
|
|
int: Default::default(),
|
2021-07-15 04:45:01 +00:00
|
|
|
apu: Default::default(),
|
2021-04-27 09:06:08 +00:00
|
|
|
high_ram: Default::default(),
|
2021-01-19 07:36:44 +00:00
|
|
|
serial: Default::default(),
|
2021-03-21 02:11:45 +00:00
|
|
|
joypad: Default::default(),
|
2020-12-24 01:39:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Bus {
|
2021-09-18 02:29:29 +00:00
|
|
|
pub(crate) fn with_boot(rom: [u8; 256]) -> Self {
|
|
|
|
Self {
|
|
|
|
boot: Some(rom),
|
2020-12-24 03:24:27 +00:00
|
|
|
..Default::default()
|
2021-09-18 02:29:29 +00:00
|
|
|
}
|
2020-12-23 09:43:49 +00:00
|
|
|
}
|
2020-12-24 01:39:37 +00:00
|
|
|
|
2021-09-18 02:29:29 +00:00
|
|
|
pub(crate) fn load_cart(&mut self, rom: Vec<u8>) {
|
|
|
|
self.cart = Some(Cartridge::new(rom));
|
2020-12-24 01:39:37 +00:00
|
|
|
}
|
2021-01-19 04:54:38 +00:00
|
|
|
|
2021-09-18 02:29:29 +00:00
|
|
|
pub(crate) fn cart_title(&self) -> Option<&str> {
|
|
|
|
self.cart.as_ref()?.title()
|
2021-04-14 06:21:45 +00:00
|
|
|
}
|
|
|
|
|
2021-08-03 02:55:07 +00:00
|
|
|
#[allow(dead_code)]
|
2021-08-01 01:29:13 +00:00
|
|
|
pub(crate) fn boot_mapped(&self) -> bool {
|
|
|
|
self.boot.is_some()
|
|
|
|
}
|
|
|
|
|
2021-09-20 04:31:03 +00:00
|
|
|
#[inline]
|
2021-06-10 00:41:10 +00:00
|
|
|
pub(crate) fn clock(&mut self) {
|
2021-08-14 05:10:51 +00:00
|
|
|
self.tick(4);
|
2021-01-19 04:54:38 +00:00
|
|
|
}
|
2021-06-07 02:30:08 +00:00
|
|
|
|
2021-09-20 04:31:03 +00:00
|
|
|
#[inline]
|
2021-08-14 05:10:51 +00:00
|
|
|
fn tick(&mut self, limit: u8) {
|
|
|
|
for _ in 0..limit {
|
|
|
|
self.timer.tick();
|
|
|
|
self.ppu.tick();
|
|
|
|
self.apu.tick(self.timer.divider);
|
|
|
|
self.dma_tick()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn dma_tick(&mut self) {
|
|
|
|
if let Some((src_addr, dest_addr)) = self.ppu.dma.tick() {
|
2021-06-10 00:41:10 +00:00
|
|
|
let byte = self.oam_read_byte(src_addr);
|
|
|
|
self.oam_write_byte(dest_addr, byte);
|
2021-06-07 01:47:11 +00:00
|
|
|
}
|
|
|
|
}
|
2021-09-20 04:31:03 +00:00
|
|
|
|
|
|
|
#[inline]
|
|
|
|
pub(crate) fn joyp_mut(&mut self) -> &mut Joypad {
|
|
|
|
&mut self.joypad
|
|
|
|
}
|
2020-12-23 09:43:49 +00:00
|
|
|
}
|
2020-08-29 23:38:27 +00:00
|
|
|
|
2021-06-07 04:57:54 +00:00
|
|
|
impl Bus {
|
|
|
|
pub fn oam_read_byte(&self, addr: u16) -> u8 {
|
2020-12-23 09:25:16 +00:00
|
|
|
match addr {
|
2021-06-07 04:57:54 +00:00
|
|
|
0x0000..=0x7FFF => {
|
|
|
|
// 16KB ROM bank 00 (ends at 0x3FFF)
|
|
|
|
// and 16KB ROM Bank 01 -> NN (switchable via MB)
|
2021-03-24 02:01:33 +00:00
|
|
|
if addr < 0x100 {
|
2021-03-16 03:08:47 +00:00
|
|
|
if let Some(boot) = self.boot {
|
|
|
|
return boot[addr as usize];
|
2020-12-24 01:39:37 +00:00
|
|
|
}
|
|
|
|
}
|
2021-03-16 03:08:47 +00:00
|
|
|
|
2021-09-18 02:29:29 +00:00
|
|
|
match self.cart.as_ref() {
|
2021-03-16 03:08:47 +00:00
|
|
|
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
|
|
|
}
|
2020-12-24 01:39:37 +00:00
|
|
|
}
|
2021-06-07 04:57:54 +00:00
|
|
|
0x8000..=0x9FFF => self.ppu.read_byte(addr), // 8KB Video RAM
|
2021-09-18 02:29:29 +00:00
|
|
|
0xA000..=0xBFFF => match self.cart.as_ref() {
|
2021-06-07 04:57:54 +00:00
|
|
|
// 8KB External RAM
|
2020-12-24 01:39:37 +00:00
|
|
|
Some(cart) => cart.read_byte(addr),
|
2021-03-21 08:03:03 +00:00
|
|
|
None => panic!("Tried to read from a non-existent cartridge"),
|
2020-12-24 01:39:37 +00:00
|
|
|
},
|
2021-06-07 04:57:54 +00:00
|
|
|
0xC000..=0xCFFF => self.work_ram.read_byte(addr), // 4KB Work RAM Bank 0
|
|
|
|
0xD000..=0xDFFF => self.var_ram.read_byte(addr), // 4KB Work RAM Bank 1 -> N
|
|
|
|
0xE000..=0xFDFF => {
|
|
|
|
// Mirror of 0xC000 to 0xDDFF (ECHO RAM)
|
2021-06-07 05:22:11 +00:00
|
|
|
let masked_addr = addr & 0x1FFF;
|
|
|
|
let equiv_addr = 0xC000 + masked_addr;
|
|
|
|
|
|
|
|
match masked_addr {
|
2021-06-07 04:57:54 +00:00
|
|
|
// 0xE000 ..= 0xEFFF
|
|
|
|
0x0000..=0x0FFF => {
|
|
|
|
// 4KB Work RAM Bank 0
|
2021-06-07 05:22:11 +00:00
|
|
|
self.work_ram.read_byte(equiv_addr)
|
2021-06-07 04:57:54 +00:00
|
|
|
}
|
|
|
|
// 0xF000 ..= 0xFDFF
|
|
|
|
0x1000..=0x1DFF => {
|
|
|
|
// 4KB Work RAM Bank 1 -> N
|
2021-06-07 05:22:11 +00:00
|
|
|
self.var_ram.read_byte(equiv_addr)
|
2021-06-07 04:57:54 +00:00
|
|
|
}
|
|
|
|
_ => unreachable!("{:#06X} was incorrectly handled by ECHO RAM", addr),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => panic!("OAM Transfer abnormally tried reading from {:#06X}", addr),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn oam_write_byte(&mut self, addr: u16, byte: u8) {
|
|
|
|
self.ppu.oam.write_byte(addr, byte);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl BusIo for Bus {
|
|
|
|
fn read_byte(&self, addr: u16) -> u8 {
|
|
|
|
match addr {
|
|
|
|
0x0000..=0x7FFF => {
|
|
|
|
// 16KB ROM bank 00 (ends at 0x3FFF)
|
|
|
|
// and 16KB ROM Bank 01 -> NN (switchable via MB)
|
|
|
|
if addr < 0x100 {
|
|
|
|
if let Some(boot) = self.boot {
|
|
|
|
return boot[addr as usize];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-18 02:29:29 +00:00
|
|
|
match self.cart.as_ref() {
|
2021-06-07 04:57:54 +00:00
|
|
|
Some(cart) => cart.read_byte(addr),
|
|
|
|
None => panic!("Tried to read from a non-existent cartridge"),
|
|
|
|
}
|
|
|
|
}
|
2020-12-24 01:39:37 +00:00
|
|
|
0x8000..=0x9FFF => {
|
|
|
|
// 8KB Video RAM
|
2021-06-10 01:48:31 +00:00
|
|
|
match self.ppu.stat.mode() {
|
2021-06-10 03:02:39 +00:00
|
|
|
PpuMode::Drawing => 0xFF,
|
2021-06-10 01:48:31 +00:00
|
|
|
_ => self.ppu.read_byte(addr),
|
|
|
|
}
|
2020-12-24 01:39:37 +00:00
|
|
|
}
|
2021-09-18 02:29:29 +00:00
|
|
|
0xA000..=0xBFFF => match self.cart.as_ref() {
|
2020-12-24 01:39:37 +00:00
|
|
|
// 8KB External RAM
|
2021-01-20 07:39:24 +00:00
|
|
|
Some(cart) => cart.read_byte(addr),
|
2021-03-23 03:33:56 +00:00
|
|
|
None => panic!("Tried to read from a non-existent cartridge"),
|
2021-01-20 07:39:24 +00:00
|
|
|
},
|
2021-06-07 04:57:54 +00:00
|
|
|
0xC000..=0xCFFF => self.work_ram.read_byte(addr), // 4KB Work RAM Bank 0
|
|
|
|
0xD000..=0xDFFF => self.var_ram.read_byte(addr), // 4KB Work RAM Bank 1 -> N
|
2020-12-24 01:39:37 +00:00
|
|
|
0xE000..=0xFDFF => {
|
2021-05-19 07:52:32 +00:00
|
|
|
// Mirror of 0xC000 to 0xDDFF (ECHO RAM)
|
2021-06-07 05:22:11 +00:00
|
|
|
let masked_addr = addr & 0x1FFF;
|
|
|
|
let equiv_addr = 0xC000 + masked_addr;
|
|
|
|
|
|
|
|
match masked_addr {
|
2021-05-19 07:52:32 +00:00
|
|
|
// 0xE000 ..= 0xEFFF
|
|
|
|
0x0000..=0x0FFF => {
|
2021-04-08 22:58:20 +00:00
|
|
|
// 4KB Work RAM Bank 0
|
2021-06-07 05:22:11 +00:00
|
|
|
self.work_ram.read_byte(equiv_addr)
|
2021-04-08 22:58:20 +00:00
|
|
|
}
|
2021-05-19 07:52:32 +00:00
|
|
|
// 0xF000 ..= 0xFDFF
|
|
|
|
0x1000..=0x1DFF => {
|
2021-04-08 22:58:20 +00:00
|
|
|
// 4KB Work RAM Bank 1 -> N
|
2021-06-07 05:22:11 +00:00
|
|
|
self.var_ram.read_byte(equiv_addr)
|
2021-04-08 22:58:20 +00:00
|
|
|
}
|
|
|
|
_ => unreachable!("{:#06X} was incorrectly handled by ECHO RAM", addr),
|
|
|
|
}
|
2020-12-24 01:39:37 +00:00
|
|
|
}
|
|
|
|
0xFE00..=0xFE9F => {
|
2021-03-16 00:19:40 +00:00
|
|
|
// Sprite Attribute Table
|
2021-06-10 01:48:31 +00:00
|
|
|
use PpuMode::{HBlank, VBlank};
|
2021-06-07 02:17:48 +00:00
|
|
|
|
2021-06-10 01:48:31 +00:00
|
|
|
match self.ppu.stat.mode() {
|
|
|
|
HBlank | VBlank => self.ppu.oam.read_byte(addr),
|
|
|
|
_ => 0xFF,
|
|
|
|
}
|
2020-12-24 01:39:37 +00:00
|
|
|
}
|
2021-04-09 00:22:55 +00:00
|
|
|
0xFEA0..=0xFEFF => {
|
2021-06-07 02:17:48 +00:00
|
|
|
// Prohibited Memory
|
|
|
|
use PpuMode::{HBlank, VBlank};
|
|
|
|
|
|
|
|
match self.ppu.stat.mode() {
|
|
|
|
HBlank | VBlank => 0x00,
|
|
|
|
_ => 0xFF, // TODO: OAM Sprite bug now occurs on the DMG
|
|
|
|
}
|
2021-04-09 00:22:55 +00:00
|
|
|
}
|
2020-12-24 01:39:37 +00:00
|
|
|
0xFF00..=0xFF7F => {
|
|
|
|
// IO Registers
|
2021-05-19 07:52:32 +00:00
|
|
|
|
|
|
|
// Every address here starts with 0xFF so we can just check the
|
|
|
|
// low byte to figure out which register it is
|
|
|
|
match addr & 0x00FF {
|
2021-06-09 18:43:46 +00:00
|
|
|
0x00 => self.joypad.p1,
|
2021-05-19 07:52:32 +00:00
|
|
|
0x01 => self.serial.next,
|
2021-06-09 18:43:46 +00:00
|
|
|
0x02 => self.serial.ctrl.into(),
|
2021-05-19 07:52:32 +00:00
|
|
|
0x04 => (self.timer.divider >> 8) as u8,
|
2021-08-14 05:10:51 +00:00
|
|
|
0x05 => self.timer.tima(),
|
2021-05-19 07:52:32 +00:00
|
|
|
0x06 => self.timer.modulo,
|
2021-06-09 18:43:46 +00:00
|
|
|
0x07 => self.timer.ctrl.into(),
|
2021-05-19 07:52:32 +00:00
|
|
|
0x0F => self.interrupt_flag().into(),
|
2021-07-25 23:19:27 +00:00
|
|
|
0x10..=0x3F => self.apu.read_byte(addr),
|
2021-06-09 18:43:46 +00:00
|
|
|
0x40 => self.ppu.ctrl.into(),
|
2021-05-19 07:52:32 +00:00
|
|
|
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,
|
2021-06-09 18:43:46 +00:00
|
|
|
0x46 => self.ppu.dma.start.into(),
|
2021-05-19 07:52:32 +00:00
|
|
|
0x47 => self.ppu.monochrome.bg_palette.into(),
|
|
|
|
0x48 => self.ppu.monochrome.obj_palette_0.into(),
|
|
|
|
0x49 => self.ppu.monochrome.obj_palette_1.into(),
|
|
|
|
0x4A => self.ppu.pos.window_y,
|
|
|
|
0x4B => self.ppu.pos.window_x,
|
2021-07-19 02:45:09 +00:00
|
|
|
_ => {
|
|
|
|
eprintln!("Read 0xFF from unused IO register {:#06X}.", addr);
|
|
|
|
0xFF
|
|
|
|
}
|
2021-01-03 07:21:19 +00:00
|
|
|
}
|
2020-12-24 01:39:37 +00:00
|
|
|
}
|
|
|
|
0xFF80..=0xFFFE => {
|
|
|
|
// High RAM
|
2021-04-27 09:06:08 +00:00
|
|
|
self.high_ram.read_byte(addr)
|
2020-12-24 01:39:37 +00:00
|
|
|
}
|
|
|
|
0xFFFF => {
|
2021-03-16 00:19:40 +00:00
|
|
|
// Interrupts Enable Register
|
2021-04-04 06:52:53 +00:00
|
|
|
self.int.enable.into()
|
2020-12-23 09:25:16 +00:00
|
|
|
}
|
|
|
|
}
|
2020-08-29 23:38:27 +00:00
|
|
|
}
|
|
|
|
|
2021-06-07 01:47:11 +00:00
|
|
|
fn write_byte(&mut self, addr: u16, byte: u8) {
|
2020-12-24 03:24:27 +00:00
|
|
|
match addr {
|
2021-06-07 04:57:54 +00:00
|
|
|
0x0000..=0x7FFF => {
|
|
|
|
// 16KB ROM bank 00 (ends at 0x3FFF)
|
|
|
|
// and 16KB ROM Bank 01 -> NN (switchable via MB)
|
2021-09-18 02:29:29 +00:00
|
|
|
match self.cart.as_mut() {
|
2021-01-20 07:39:24 +00:00
|
|
|
Some(cart) => cart.write_byte(addr, byte),
|
2021-03-23 03:33:56 +00:00
|
|
|
None => panic!("Tried to write into non-existent cartridge"),
|
2021-01-20 07:39:24 +00:00
|
|
|
}
|
2020-12-24 03:24:27 +00:00
|
|
|
}
|
|
|
|
0x8000..=0x9FFF => {
|
|
|
|
// 8KB Video RAM
|
2021-06-10 03:02:39 +00:00
|
|
|
match self.ppu.stat.mode() {
|
|
|
|
PpuMode::Drawing => {}
|
|
|
|
_ => self.ppu.write_byte(addr, byte),
|
|
|
|
}
|
2020-12-24 03:24:27 +00:00
|
|
|
}
|
|
|
|
0xA000..=0xBFFF => {
|
|
|
|
// 8KB External RAM
|
2021-09-18 02:29:29 +00:00
|
|
|
match self.cart.as_mut() {
|
2021-01-20 07:39:24 +00:00
|
|
|
Some(cart) => cart.write_byte(addr, byte),
|
2021-03-23 03:33:56 +00:00
|
|
|
None => panic!("Tried to write into non-existent cartridge"),
|
2021-01-20 07:39:24 +00:00
|
|
|
}
|
2020-12-24 03:24:27 +00:00
|
|
|
}
|
2021-06-07 04:57:54 +00:00
|
|
|
0xC000..=0xCFFF => self.work_ram.write_byte(addr, byte), // 4KB Work RAM Bank 0
|
|
|
|
0xD000..=0xDFFF => self.var_ram.write_byte(addr, byte), // 4KB Work RAM Bank 1 -> N
|
2020-12-24 03:24:27 +00:00
|
|
|
0xE000..=0xFDFF => {
|
2021-05-19 07:52:32 +00:00
|
|
|
// Mirror of 0xC000 to 0xDDFF (ECHO RAM)
|
2021-06-07 05:22:11 +00:00
|
|
|
let masked_addr = addr & 0x1FFF;
|
|
|
|
let equiv_addr = 0xC000 + masked_addr;
|
|
|
|
|
|
|
|
match masked_addr {
|
2021-05-19 07:52:32 +00:00
|
|
|
// 0xE000 ..= 0xEFFF
|
|
|
|
0x0000..=0x0FFF => {
|
2021-04-08 22:58:20 +00:00
|
|
|
// 4KB Work RAM Bank 0
|
2021-06-07 05:22:11 +00:00
|
|
|
self.work_ram.write_byte(equiv_addr, byte);
|
2021-04-08 22:58:20 +00:00
|
|
|
}
|
2021-05-19 07:52:32 +00:00
|
|
|
// 0xF000 ..= 0xFDFF
|
|
|
|
0x1000..=0x1DFF => {
|
2021-04-08 22:58:20 +00:00
|
|
|
// 4KB Work RAM Bank 1 -> N
|
2021-06-07 05:22:11 +00:00
|
|
|
self.var_ram.write_byte(equiv_addr, byte);
|
2021-04-08 22:58:20 +00:00
|
|
|
}
|
|
|
|
_ => unreachable!("{:#06X} was incorrectly handled by ECHO RAM", addr),
|
|
|
|
}
|
2020-12-24 03:24:27 +00:00
|
|
|
}
|
|
|
|
0xFE00..=0xFE9F => {
|
2021-03-16 00:19:40 +00:00
|
|
|
// Sprite Attribute Table
|
2021-06-10 01:48:31 +00:00
|
|
|
use PpuMode::{HBlank, VBlank};
|
2021-06-07 05:01:40 +00:00
|
|
|
|
2021-06-10 01:48:31 +00:00
|
|
|
match self.ppu.stat.mode() {
|
|
|
|
HBlank | VBlank => self.ppu.oam.write_byte(addr, byte),
|
|
|
|
_ => {}
|
|
|
|
}
|
2020-12-24 03:24:27 +00:00
|
|
|
}
|
2021-08-16 04:31:27 +00:00
|
|
|
0xFEA0..=0xFEFF => {} // FIXME: As far as I know, writes to here do nothing.
|
2020-12-24 03:24:27 +00:00
|
|
|
0xFF00..=0xFF7F => {
|
|
|
|
// IO Registers
|
2021-05-19 07:52:32 +00:00
|
|
|
|
|
|
|
// Every address here starts with 0xFF so we can just check the
|
|
|
|
// low byte to figure out which register it is
|
|
|
|
match addr & 0x00FF {
|
2021-06-07 22:23:48 +00:00
|
|
|
0x00 => self.joypad.update(byte),
|
2021-05-19 07:52:32 +00:00
|
|
|
0x01 => self.serial.next = byte,
|
2021-06-09 18:43:46 +00:00
|
|
|
0x02 => self.serial.ctrl = byte.into(),
|
2021-05-19 07:52:32 +00:00
|
|
|
0x04 => self.timer.divider = 0x0000,
|
2021-08-14 05:10:51 +00:00
|
|
|
0x05 => self.timer.set_tima(byte),
|
2021-05-19 07:52:32 +00:00
|
|
|
0x06 => self.timer.modulo = byte,
|
2021-06-09 18:43:46 +00:00
|
|
|
0x07 => self.timer.ctrl = byte.into(),
|
2021-05-19 07:52:32 +00:00
|
|
|
0x0F => self.set_interrupt_flag(byte),
|
2021-07-25 23:19:27 +00:00
|
|
|
0x10..=0x3F => self.apu.write_byte(addr, byte),
|
2021-06-09 18:43:46 +00:00
|
|
|
0x40 => self.ppu.ctrl = byte.into(),
|
2021-05-19 07:52:32 +00:00
|
|
|
0x41 => self.ppu.stat.update(byte),
|
|
|
|
0x42 => self.ppu.pos.scroll_y = byte,
|
|
|
|
0x43 => self.ppu.pos.scroll_x = byte,
|
|
|
|
0x44 => self.ppu.pos.line_y = byte,
|
|
|
|
0x45 => {
|
2021-03-21 05:01:21 +00:00
|
|
|
// Update LYC
|
|
|
|
self.ppu.pos.ly_compare = byte;
|
|
|
|
|
|
|
|
// Update Coincidence Flag
|
2021-05-05 13:29:39 +00:00
|
|
|
let are_equal = self.ppu.pos.line_y == byte;
|
|
|
|
self.ppu.stat.set_coincidence(are_equal);
|
|
|
|
|
|
|
|
// If enabled, request a LCD STAT interrupt
|
|
|
|
if self.ppu.stat.coincidence_int() && are_equal {
|
|
|
|
self.ppu.int.set_lcd_stat(true);
|
2021-03-21 05:01:21 +00:00
|
|
|
}
|
|
|
|
}
|
2021-06-09 18:43:46 +00:00
|
|
|
0x46 => self.ppu.dma.start.update(byte, &mut self.ppu.dma.state),
|
2021-05-19 07:52:32 +00:00
|
|
|
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(),
|
|
|
|
0x4A => self.ppu.pos.window_y = byte,
|
|
|
|
0x4B => self.ppu.pos.window_x = byte,
|
2021-07-02 04:09:02 +00:00
|
|
|
0x4D => {} // CGB Specific Register
|
2021-05-19 07:52:32 +00:00
|
|
|
0x50 => {
|
2021-01-19 06:30:10 +00:00
|
|
|
// Disable Boot ROM
|
|
|
|
if byte != 0 {
|
|
|
|
self.boot = None;
|
|
|
|
}
|
|
|
|
}
|
2021-07-19 02:45:09 +00:00
|
|
|
_ => eprintln!("Wrote {:#04X} to unused IO register {:#06X}.", byte, addr),
|
2021-01-03 07:21:19 +00:00
|
|
|
};
|
2020-12-24 03:24:27 +00:00
|
|
|
}
|
|
|
|
0xFF80..=0xFFFE => {
|
|
|
|
// High RAM
|
2021-04-27 09:06:08 +00:00
|
|
|
self.high_ram.write_byte(addr, byte);
|
2020-12-24 03:24:27 +00:00
|
|
|
}
|
|
|
|
0xFFFF => {
|
2021-03-16 00:19:40 +00:00
|
|
|
// Interrupts Enable Register
|
2021-04-04 06:52:53 +00:00
|
|
|
self.int.enable = byte.into();
|
2020-12-24 03:24:27 +00:00
|
|
|
}
|
|
|
|
}
|
2020-08-29 23:38:27 +00:00
|
|
|
}
|
2021-06-07 01:47:11 +00:00
|
|
|
}
|
2020-08-29 23:38:27 +00:00
|
|
|
|
2021-03-21 00:53:56 +00:00
|
|
|
impl Bus {
|
|
|
|
fn interrupt_flag(&self) -> InterruptFlag {
|
2021-03-21 02:21:39 +00:00
|
|
|
// Read the current interrupt information from the PPU
|
2021-04-04 06:52:53 +00:00
|
|
|
let vblank = self.ppu.int.vblank();
|
|
|
|
let lcd_stat = self.ppu.int.lcd_stat();
|
2021-03-21 00:53:56 +00:00
|
|
|
|
2021-03-21 08:03:03 +00:00
|
|
|
// Read the current interrupt information from the Joypad
|
2021-03-21 02:21:39 +00:00
|
|
|
let joypad = self.joypad.interrupt();
|
2021-03-21 00:53:56 +00:00
|
|
|
|
2021-03-21 08:03:03 +00:00
|
|
|
// Read the current interrupt information from the Timer
|
|
|
|
let timer = self.timer.interrupt();
|
|
|
|
|
2021-03-21 00:53:56 +00:00
|
|
|
// Copy the Interrupt Flag register 0xFF0F
|
2021-04-04 06:52:53 +00:00
|
|
|
let mut flag = self.int.flag;
|
2021-03-21 00:53:56 +00:00
|
|
|
|
2021-03-21 02:21:39 +00:00
|
|
|
// 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);
|
2021-03-21 00:53:56 +00:00
|
|
|
flag
|
|
|
|
}
|
|
|
|
|
|
|
|
fn set_interrupt_flag(&mut self, byte: u8) {
|
|
|
|
// Update the Interrupt register 0xFF0F
|
2021-04-04 06:52:53 +00:00
|
|
|
self.int.flag = byte.into();
|
2021-03-21 00:53:56 +00:00
|
|
|
|
2021-04-04 06:52:53 +00:00
|
|
|
let vblank = self.int.flag.vblank();
|
|
|
|
let lcd_stat = self.int.flag.lcd_stat();
|
|
|
|
let joypad = self.int.flag.joypad();
|
|
|
|
let timer = self.int.flag.timer();
|
2021-03-21 00:53:56 +00:00
|
|
|
|
2021-03-21 02:21:39 +00:00
|
|
|
// Update the PPU's instance of the following interrupts
|
2021-04-04 06:52:53 +00:00
|
|
|
self.ppu.int.set_vblank(vblank);
|
|
|
|
self.ppu.int.set_lcd_stat(lcd_stat);
|
2021-03-21 02:21:39 +00:00
|
|
|
|
|
|
|
// 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);
|
2021-03-21 00:53:56 +00:00
|
|
|
}
|
|
|
|
}
|
2021-06-04 18:47:06 +00:00
|
|
|
|
2021-06-07 01:47:11 +00:00
|
|
|
pub(crate) trait BusIo {
|
|
|
|
fn read_byte(&self, addr: u16) -> u8;
|
|
|
|
fn write_byte(&mut self, addr: u16, byte: u8);
|
2021-06-04 18:47:06 +00:00
|
|
|
}
|