2020-12-24 06:27:06 +00:00
|
|
|
use super::cartridge::Cartridge;
|
2021-01-18 03:13:59 +00:00
|
|
|
use super::high_ram::HighRAM;
|
2021-01-19 07:36:44 +00:00
|
|
|
use super::instruction::Cycles;
|
2021-01-03 07:38:31 +00:00
|
|
|
use super::interrupt::Interrupt;
|
2020-12-24 06:27:06 +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;
|
2021-01-03 07:21:19 +00:00
|
|
|
use super::timer::Timer;
|
2021-01-03 06:28:07 +00:00
|
|
|
use super::work_ram::{VariableWorkRAM, WorkRAM};
|
2021-01-28 04:07:31 +00:00
|
|
|
use std::{convert::TryInto, fs::File, io::Read};
|
2021-01-03 07:38:31 +00:00
|
|
|
|
2020-12-24 01:39:37 +00:00
|
|
|
#[derive(Debug, Clone)]
|
2020-12-23 09:43:49 +00:00
|
|
|
pub struct Bus {
|
2020-12-24 03:24:27 +00:00
|
|
|
boot: Option<[u8; 256]>, // Boot ROM is 256b long
|
2020-12-24 01:39:37 +00:00
|
|
|
cartridge: Option<Cartridge>,
|
2021-01-19 02:47:09 +00:00
|
|
|
pub ppu: PPU,
|
2021-01-03 06:28:07 +00:00
|
|
|
wram: WorkRAM,
|
|
|
|
vwram: VariableWorkRAM,
|
2021-01-03 07:21:19 +00:00
|
|
|
timer: Timer,
|
2021-01-03 07:38:31 +00:00
|
|
|
interrupt: Interrupt,
|
2021-01-03 08:05:46 +00:00
|
|
|
sound: Sound,
|
2021-01-18 03:13:59 +00:00
|
|
|
hram: HighRAM,
|
2021-01-19 07:36:44 +00:00
|
|
|
serial: Serial,
|
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,
|
2020-12-24 01:39:37 +00:00
|
|
|
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(),
|
2021-01-03 07:21:19 +00:00
|
|
|
timer: Default::default(),
|
2021-01-03 07:38:31 +00:00
|
|
|
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(),
|
2020-12-24 01:39:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Bus {
|
2021-01-28 04:07:31 +00:00
|
|
|
pub fn with_boot(path: &str) -> Self {
|
|
|
|
let mut file = File::open(path).unwrap();
|
|
|
|
let mut buf = Vec::with_capacity(256);
|
|
|
|
file.read_to_end(&mut buf).unwrap();
|
|
|
|
|
|
|
|
let boot_rom: [u8; 256] = buf.try_into().unwrap();
|
|
|
|
|
2020-12-24 01:39:37 +00:00
|
|
|
Self {
|
2021-01-28 04:07:31 +00:00
|
|
|
boot: Some(boot_rom),
|
2020-12-24 03:24:27 +00:00
|
|
|
..Default::default()
|
2020-12-23 09:43:49 +00:00
|
|
|
}
|
|
|
|
}
|
2020-12-24 01:39:37 +00:00
|
|
|
|
|
|
|
pub fn load_cartridge(&mut self, path: &str) {
|
|
|
|
self.cartridge = Some(Cartridge::new(path).unwrap());
|
|
|
|
}
|
2021-01-19 04:54:38 +00:00
|
|
|
|
|
|
|
pub fn step(&mut self, cycles: Cycles) {
|
|
|
|
let _ = self.timer.step(cycles);
|
|
|
|
let _ = self.sound.step(cycles);
|
|
|
|
let _ = self.ppu.step(cycles);
|
|
|
|
}
|
2020-12-23 09:43:49 +00:00
|
|
|
}
|
2020-08-29 23:38:27 +00:00
|
|
|
|
|
|
|
impl Bus {
|
2020-09-04 05:41:19 +00:00
|
|
|
pub fn read_byte(&self, addr: u16) -> u8 {
|
2020-12-23 09:25:16 +00:00
|
|
|
match addr {
|
2020-12-24 01:39:37 +00:00
|
|
|
0x0000..=0x3FFF => {
|
|
|
|
// 16KB ROM bank 00
|
|
|
|
if addr <= 0x00FF && self.boot.is_some() {
|
|
|
|
let boot = self.boot.unwrap();
|
|
|
|
boot[addr as usize]
|
|
|
|
} else {
|
|
|
|
match &self.cartridge {
|
|
|
|
Some(cart) => cart.read_byte(addr),
|
|
|
|
None => panic!("Tried to read from a non-existant cartridge"),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
0x4000..=0x7FFF => match &self.cartridge {
|
|
|
|
// 16KB ROM Bank 01 -> NN (switchable via MB)
|
|
|
|
Some(cart) => cart.read_byte(addr),
|
|
|
|
None => panic!("Tried to read from a non-existant cartridge"),
|
|
|
|
},
|
|
|
|
0x8000..=0x9FFF => {
|
|
|
|
// 8KB Video RAM
|
2020-12-24 06:27:06 +00:00
|
|
|
self.ppu.vram[(addr - 0x8000) as usize]
|
2020-12-24 01:39:37 +00:00
|
|
|
}
|
2021-01-20 07:39:24 +00:00
|
|
|
0xA000..=0xBFFF => match &self.cartridge {
|
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),
|
|
|
|
None => panic!("Tried to read from the external RAM of a non-existant cartridge"),
|
|
|
|
},
|
2020-12-24 01:39:37 +00:00
|
|
|
0xC000..=0xCFFF => {
|
|
|
|
// 4KB Work RAM Bank 0
|
2021-01-03 06:28:07 +00:00
|
|
|
self.wram.read_byte((addr - 0xC000) as usize)
|
2020-12-24 01:39:37 +00:00
|
|
|
}
|
|
|
|
0xD000..=0xDFFF => {
|
|
|
|
// 4KB Work RAM Bank 1 -> N
|
2021-01-03 06:28:07 +00:00
|
|
|
self.vwram.read_byte((addr - 0xD000) as usize)
|
2020-12-24 01:39:37 +00:00
|
|
|
}
|
|
|
|
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
|
2020-12-24 01:39:37 +00:00
|
|
|
unimplemented!("Unable to read {:#06X} in the Sprite Attribute Table", addr);
|
|
|
|
}
|
|
|
|
0xFEA0..=0xFEFF => unimplemented!("{:#06X} is not allowed to be used", addr),
|
|
|
|
0xFF00..=0xFF7F => {
|
|
|
|
// IO Registers
|
2021-01-03 07:21:19 +00:00
|
|
|
match addr {
|
2021-01-19 07:40:07 +00:00
|
|
|
0xFF01 => self.serial.next,
|
|
|
|
0xFF02 => self.serial.control.into(),
|
2021-01-03 07:21:19 +00:00
|
|
|
0xFF07 => self.timer.control.into(),
|
2021-01-03 07:38:31 +00:00
|
|
|
0xFF0F => self.interrupt.flag.into(),
|
2021-01-17 23:33:12 +00:00
|
|
|
0xFF11 => self.sound.ch1.sound_duty.into(),
|
|
|
|
0xFF12 => self.sound.ch1.vol_envelope.into(),
|
2021-01-19 06:30:10 +00:00
|
|
|
0xFF14 => self.sound.ch1.freq.get_hi(),
|
2021-01-18 00:58:57 +00:00
|
|
|
0xFF24 => self.sound.control.channel.into(),
|
|
|
|
0xFF25 => self.sound.control.select.into(),
|
|
|
|
0xFF26 => self.sound.control.status.into(),
|
2021-01-18 01:25:53 +00:00
|
|
|
0xFF40 => self.ppu.lcd_control.into(),
|
2021-01-18 08:22:45 +00:00
|
|
|
0xFF41 => self.ppu.stat.into(),
|
|
|
|
0xFF42 => self.ppu.pos.scroll_y,
|
|
|
|
0xFF43 => self.ppu.pos.scroll_x,
|
|
|
|
0xFF44 => self.ppu.pos.line_y,
|
2021-01-18 01:25:53 +00:00
|
|
|
0xFF47 => self.ppu.monochrome.bg_palette.into(),
|
2021-01-03 07:21:19 +00:00
|
|
|
_ => unimplemented!("Unable to read {:#06X} in I/O Registers", addr),
|
|
|
|
}
|
2020-12-24 01:39:37 +00:00
|
|
|
}
|
|
|
|
0xFF80..=0xFFFE => {
|
|
|
|
// High RAM
|
2021-01-18 03:13:59 +00:00
|
|
|
self.hram.read_byte((addr - 0xFF80) as usize)
|
2020-12-24 01:39:37 +00:00
|
|
|
}
|
|
|
|
0xFFFF => {
|
2021-03-16 00:19:40 +00:00
|
|
|
// Interrupts Enable Register
|
2021-01-03 07:38:31 +00:00
|
|
|
self.interrupt.enable.into()
|
2020-12-23 09:25:16 +00:00
|
|
|
}
|
|
|
|
}
|
2020-08-29 23:38:27 +00:00
|
|
|
}
|
|
|
|
|
2020-12-23 09:43:49 +00:00
|
|
|
pub fn write_byte(&mut self, addr: u16, byte: u8) {
|
2020-12-24 03:24:27 +00:00
|
|
|
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"),
|
|
|
|
}
|
2020-12-24 03:24:27 +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"),
|
|
|
|
}
|
2020-12-24 03:24:27 +00:00
|
|
|
}
|
|
|
|
0x8000..=0x9FFF => {
|
|
|
|
// 8KB Video RAM
|
2020-12-24 06:27:06 +00:00
|
|
|
self.ppu.vram[(addr - 0x8000) as usize] = byte;
|
2020-12-24 03:24:27 +00:00
|
|
|
}
|
|
|
|
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"),
|
|
|
|
}
|
2020-12-24 03:24:27 +00:00
|
|
|
}
|
|
|
|
0xC000..=0xCFFF => {
|
|
|
|
// 4KB Work RAM Bank 0
|
2021-01-03 06:28:07 +00:00
|
|
|
self.wram.write_byte((addr - 0xC000) as usize, byte);
|
2020-12-24 03:24:27 +00:00
|
|
|
}
|
|
|
|
0xD000..=0xDFFF => {
|
|
|
|
// 4KB Work RAM Bank 1 -> N
|
2021-01-03 06:28:07 +00:00
|
|
|
self.vwram.write_byte((addr - 0xD000) as usize, byte);
|
2020-12-24 03:24:27 +00:00
|
|
|
}
|
|
|
|
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
|
2020-12-24 03:24:27 +00:00
|
|
|
unimplemented!(
|
|
|
|
"Unable to write to {:#06X} in the Sprite Attribute Table",
|
|
|
|
addr
|
|
|
|
);
|
|
|
|
}
|
|
|
|
0xFEA0..=0xFEFF => unimplemented!("{:#06X} is not allowed to be used", addr),
|
|
|
|
0xFF00..=0xFF7F => {
|
|
|
|
// IO Registers
|
2021-01-03 07:21:19 +00:00
|
|
|
match addr {
|
2021-01-19 07:40:07 +00:00
|
|
|
0xFF01 => self.serial.next = byte,
|
|
|
|
0xFF02 => self.serial.control = byte.into(),
|
2021-01-03 07:21:19 +00:00
|
|
|
0xFF07 => self.timer.control = byte.into(),
|
2021-01-03 07:38:31 +00:00
|
|
|
0xFF0F => self.interrupt.flag = byte.into(),
|
2021-01-17 23:33:12 +00:00
|
|
|
0xFF11 => self.sound.ch1.sound_duty = byte.into(),
|
|
|
|
0xFF12 => self.sound.ch1.vol_envelope = byte.into(),
|
2021-01-19 06:30:10 +00:00
|
|
|
0xFF13 => self.sound.ch1.freq.set_lo(byte),
|
|
|
|
0xFF14 => self.sound.ch1.freq.set_hi(byte),
|
2021-01-18 00:58:57 +00:00
|
|
|
0xFF24 => self.sound.control.channel = byte.into(),
|
|
|
|
0xFF25 => self.sound.control.select = byte.into(),
|
|
|
|
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(),
|
2021-01-18 08:22:45 +00:00
|
|
|
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-01-18 01:25:53 +00:00
|
|
|
0xFF47 => self.ppu.monochrome.bg_palette = byte.into(),
|
2021-01-19 06:30:10 +00:00
|
|
|
0xFF50 => {
|
|
|
|
// Disable Boot ROM
|
|
|
|
if byte != 0 {
|
|
|
|
self.boot = None;
|
|
|
|
}
|
|
|
|
}
|
2021-01-03 07:21:19 +00:00
|
|
|
_ => unimplemented!("Unable to write to {:#06X} in I/O Registers", addr),
|
|
|
|
};
|
2020-12-24 03:24:27 +00:00
|
|
|
}
|
|
|
|
0xFF80..=0xFFFE => {
|
|
|
|
// High RAM
|
2021-01-18 03:13:59 +00:00
|
|
|
self.hram.write_byte((addr - 0xFF80) as usize, byte);
|
2020-12-24 03:24:27 +00:00
|
|
|
}
|
|
|
|
0xFFFF => {
|
2021-03-16 00:19:40 +00:00
|
|
|
// Interrupts Enable Register
|
2021-01-03 07:38:31 +00:00
|
|
|
self.interrupt.enable = byte.into();
|
2020-12-24 03:24:27 +00:00
|
|
|
}
|
|
|
|
}
|
2020-08-29 23:38:27 +00:00
|
|
|
}
|
|
|
|
|
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-08-29 23:38:27 +00:00
|
|
|
}
|
|
|
|
|
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-08-29 23:38:27 +00:00
|
|
|
}
|
2020-09-04 05:41:19 +00:00
|
|
|
}
|