2021-11-15 14:15:31 +00:00
|
|
|
use std::rc::Rc;
|
|
|
|
use std::cell::RefCell;
|
2021-11-15 14:46:59 +00:00
|
|
|
use std::ops::RangeInclusive;
|
2021-10-29 03:13:23 +00:00
|
|
|
use crate::utils::{
|
|
|
|
get_bit,
|
|
|
|
BitIndex,
|
|
|
|
join_bytes
|
|
|
|
};
|
2021-10-14 00:38:37 +00:00
|
|
|
use crate::rom::ROM;
|
2021-11-01 18:04:09 +00:00
|
|
|
use crate::ppu::{
|
|
|
|
LCD_STATUS_ADDRESS,
|
|
|
|
LCD_CONTROL_ADDRESS,
|
2021-11-05 05:19:47 +00:00
|
|
|
LCD_Y_ADDRESS,
|
|
|
|
DMA_ADDRESS,
|
2021-11-01 18:04:09 +00:00
|
|
|
};
|
2021-10-29 20:40:47 +00:00
|
|
|
use crate::cpu::{Interrupt};
|
2021-11-15 14:27:03 +00:00
|
|
|
use crate::timer::{Timer, TIMER_DIVIDER_REGISTER_ADDRESS};
|
2021-11-01 02:02:09 +00:00
|
|
|
use crate::joypad::{Joypad, JOYPAD_ADDRESS};
|
2021-10-14 00:38:37 +00:00
|
|
|
|
2021-11-15 14:46:59 +00:00
|
|
|
pub const BANK_ZERO: RangeInclusive<u16> = 0x0000..=0x3FFF;
|
|
|
|
pub const BANK_SWITCHABLE: RangeInclusive<u16> = 0x4000..=0x7FFF;
|
|
|
|
pub const VIDEO_RAM: RangeInclusive<u16> = 0x8000..=0x9FFF;
|
|
|
|
pub const EXTERNAL_RAM: RangeInclusive<u16> = 0xA000..=0xBFFF;
|
|
|
|
pub const WORK_RAM_1: RangeInclusive<u16> = 0xC000..=0xCFFF;
|
|
|
|
pub const WORK_RAM_2: RangeInclusive<u16> = 0xD000..=0xDFFF;
|
|
|
|
pub const ECHO_RAM: RangeInclusive<u16> = 0xE000..=0xFDFF;
|
|
|
|
pub const SPRITE_ATTRIBUTE_TABLE: RangeInclusive<u16> = 0xFE00..=0xFE9F;
|
|
|
|
pub const NOT_USABLE: RangeInclusive<u16> = 0xFEA0..=0xFEFF;
|
|
|
|
pub const IO_REGISTERS: RangeInclusive<u16> = 0xFF00..=0xFF7F;
|
|
|
|
pub const HIGH_RAM: RangeInclusive<u16> = 0xFF80..=0xFFFE;
|
|
|
|
pub const INTERRUPT_ENABLE_REGISTER: RangeInclusive<u16> = 0xFFFF..=0xFFFF;
|
2021-10-29 20:40:47 +00:00
|
|
|
pub const INTERRUPT_ENABLE_ADDRESS: u16 = 0xFFFF;
|
2021-10-29 03:13:23 +00:00
|
|
|
pub const INTERRUPT_FLAG_ADDRESS: u16 = 0xFF0F;
|
2021-10-22 20:32:12 +00:00
|
|
|
|
2021-10-14 00:38:37 +00:00
|
|
|
pub struct Bus {
|
|
|
|
game_rom: ROM,
|
2021-10-14 18:25:20 +00:00
|
|
|
data: [u8; 0x10000],
|
2021-11-15 14:15:31 +00:00
|
|
|
joypad: Rc<RefCell<Joypad>>,
|
2021-11-15 14:27:03 +00:00
|
|
|
timer: Rc<RefCell<Timer>>,
|
2021-10-14 00:38:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Bus {
|
2021-11-15 14:27:03 +00:00
|
|
|
pub fn new(joypad: Rc<RefCell<Joypad>>, timer: Rc<RefCell<Timer>>) -> Self {
|
2021-11-12 16:18:09 +00:00
|
|
|
let args: Vec<String> = std::env::args().collect();
|
|
|
|
if args.len() < 2 {
|
2021-11-12 19:04:10 +00:00
|
|
|
println!("Please, specify a ROM file");
|
|
|
|
std::process::exit(1);
|
2021-11-12 16:18:09 +00:00
|
|
|
}
|
|
|
|
let game_rom = match ROM::load_file(&args[1]) {
|
2021-10-14 00:38:37 +00:00
|
|
|
Ok(rom) => rom,
|
2021-11-12 19:04:10 +00:00
|
|
|
Err(err) => {
|
|
|
|
println!("Could not read ROM: {}", err);
|
|
|
|
std::process::exit(1);
|
|
|
|
},
|
2021-10-14 00:38:37 +00:00
|
|
|
};
|
2021-11-01 02:02:09 +00:00
|
|
|
let mut data = [0x00; 0x10000];
|
2021-11-03 13:36:30 +00:00
|
|
|
// Hardware registers after the bootrom
|
2021-11-03 16:59:48 +00:00
|
|
|
data[0xFF00] = 0xCF;
|
2021-11-01 18:04:09 +00:00
|
|
|
data[0xFF01] = 0x00;
|
|
|
|
data[0xFF02] = 0x7E;
|
|
|
|
data[0xFF04] = 0x18;
|
|
|
|
data[0xFF05] = 0x00;
|
|
|
|
data[0xFF06] = 0x00;
|
|
|
|
data[0xFF07] = 0xF8;
|
|
|
|
data[0xFF0F] = 0xE1;
|
|
|
|
|
|
|
|
data[0xFF40] = 0x91;
|
|
|
|
data[0xFF41] = 0x81;
|
|
|
|
data[0xFF42] = 0x00;
|
|
|
|
data[0xFF43] = 0x00;
|
|
|
|
data[0xFF44] = 0x91;
|
|
|
|
data[0xFF45] = 0x00;
|
|
|
|
data[0xFF46] = 0xFF;
|
|
|
|
data[0xFF47] = 0xFC;
|
|
|
|
|
|
|
|
data[0xFF4A] = 0x00;
|
|
|
|
data[0xFF4B] = 0x00;
|
2021-11-01 22:03:56 +00:00
|
|
|
data[0xFFFF] = 0x00;
|
2021-11-01 18:04:09 +00:00
|
|
|
|
2021-10-14 00:38:37 +00:00
|
|
|
Self {
|
2021-11-01 02:02:09 +00:00
|
|
|
data,
|
2021-10-14 00:38:37 +00:00
|
|
|
game_rom,
|
2021-11-15 14:15:31 +00:00
|
|
|
joypad,
|
2021-11-15 14:27:03 +00:00
|
|
|
timer,
|
2021-10-14 00:38:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn read(&self, address: u16) -> u8 {
|
2021-11-15 14:46:59 +00:00
|
|
|
if BANK_ZERO.contains(&address) || BANK_SWITCHABLE.contains(&address) || EXTERNAL_RAM.contains(&address) {
|
2021-10-22 20:32:12 +00:00
|
|
|
return self.game_rom.read(address);
|
2021-11-04 18:13:22 +00:00
|
|
|
} else if address == INTERRUPT_ENABLE_ADDRESS || address == INTERRUPT_FLAG_ADDRESS {
|
|
|
|
return 0b11100000 | self.data[address as usize];
|
2021-11-05 05:19:47 +00:00
|
|
|
} else if address == JOYPAD_ADDRESS {
|
2021-11-15 14:15:31 +00:00
|
|
|
return self.joypad.borrow().read(self.data[address as usize]);
|
2021-11-15 14:27:03 +00:00
|
|
|
} else if address == TIMER_DIVIDER_REGISTER_ADDRESS {
|
|
|
|
return self.timer.borrow().read_divider();
|
2021-10-14 00:38:37 +00:00
|
|
|
}
|
2021-10-22 20:32:12 +00:00
|
|
|
self.data[address as usize]
|
2021-10-14 00:38:37 +00:00
|
|
|
}
|
|
|
|
|
2021-10-16 00:18:00 +00:00
|
|
|
pub fn read_16bit(&self, address: u16) -> u16 {
|
2021-10-22 14:56:54 +00:00
|
|
|
join_bytes(self.read(address.wrapping_add(1)), self.read(address))
|
2021-10-16 00:18:00 +00:00
|
|
|
}
|
|
|
|
|
2021-10-14 00:38:37 +00:00
|
|
|
pub fn write(&mut self, address: u16, data: u8) {
|
2021-10-20 17:29:55 +00:00
|
|
|
if address == 0xFF01 {
|
2021-10-29 22:03:02 +00:00
|
|
|
// print!("{}", data as char);
|
2021-10-20 17:29:55 +00:00
|
|
|
}
|
2021-10-22 20:32:12 +00:00
|
|
|
|
2021-11-15 14:46:59 +00:00
|
|
|
if BANK_ZERO.contains(&address) || BANK_SWITCHABLE.contains(&address) || EXTERNAL_RAM.contains(&address) {
|
2021-11-05 19:20:10 +00:00
|
|
|
self.game_rom.write(address, data);
|
2021-11-15 14:46:59 +00:00
|
|
|
} else if WORK_RAM_1.contains(&address) || WORK_RAM_2.contains(&address) {
|
2021-10-22 20:32:12 +00:00
|
|
|
self.data[address as usize] = data;
|
|
|
|
// Copy to the ECHO RAM
|
|
|
|
if address <= 0xDDFF {
|
2021-11-15 14:46:59 +00:00
|
|
|
self.data[(ECHO_RAM.min().unwrap() + (address - WORK_RAM_1.min().unwrap())) as usize] = data;
|
2021-10-22 20:32:12 +00:00
|
|
|
}
|
2021-11-15 14:46:59 +00:00
|
|
|
} else if EXTERNAL_RAM.contains(&address) {
|
2021-11-05 15:08:17 +00:00
|
|
|
// self.game_rom.write(address, data);
|
2021-11-15 14:46:59 +00:00
|
|
|
} else if ECHO_RAM.contains(&address) {
|
2021-10-22 20:32:12 +00:00
|
|
|
self.data[address as usize] = data;
|
2021-11-15 14:46:59 +00:00
|
|
|
self.data[(WORK_RAM_1.min().unwrap() + (address - ECHO_RAM.min().unwrap())) as usize] = data; // Copy to the working RAM
|
2021-10-30 14:13:31 +00:00
|
|
|
} else if address == TIMER_DIVIDER_REGISTER_ADDRESS {
|
2021-11-15 14:27:03 +00:00
|
|
|
self.timer.borrow_mut().reset();
|
2021-11-09 15:39:26 +00:00
|
|
|
} else if address == LCD_CONTROL_ADDRESS {
|
|
|
|
self.data[address as usize] = data;
|
2021-11-13 00:52:46 +00:00
|
|
|
// Check if LCD is being turned on or off
|
|
|
|
if (get_bit(data, BitIndex::I7) && !get_bit(self.data[address as usize], BitIndex::I7)) ||
|
|
|
|
!get_bit(data, BitIndex::I7) {
|
2021-11-09 00:40:30 +00:00
|
|
|
self.data[LCD_Y_ADDRESS as usize] = 0x00;
|
2021-11-13 00:52:46 +00:00
|
|
|
// Set Hblank
|
|
|
|
let byte = self.data[LCD_STATUS_ADDRESS as usize];
|
|
|
|
self.data[LCD_STATUS_ADDRESS as usize] = byte & 0b11111100;
|
2021-11-09 00:40:30 +00:00
|
|
|
}
|
2021-11-03 16:59:48 +00:00
|
|
|
} else if address == LCD_Y_ADDRESS {
|
2021-11-04 03:24:31 +00:00
|
|
|
// println!("Write to LCD_Y not allowed");
|
|
|
|
} else if address == LCD_STATUS_ADDRESS {
|
|
|
|
let byte = self.data[address as usize];
|
|
|
|
self.data[address as usize] = (data & 0b11111000) | (byte & 0b00000111);
|
|
|
|
} else if address == JOYPAD_ADDRESS {
|
|
|
|
let byte = self.data[address as usize];
|
|
|
|
self.data[address as usize] = (data & 0b11110000) | (byte & 0b00001111);
|
2021-11-05 05:19:47 +00:00
|
|
|
} else if address == DMA_ADDRESS {
|
|
|
|
// the idea is: when something gets written to $FF46, multiply it by 0x100, then copy 160 bytes starting from that memory location into OAM
|
|
|
|
self.data[address as usize] = data;
|
|
|
|
let source = (data as usize) * 0x100;
|
|
|
|
let mut count = 0;
|
2021-11-15 14:46:59 +00:00
|
|
|
let oam_addr = SPRITE_ATTRIBUTE_TABLE.min().unwrap() as usize;
|
2021-11-05 05:19:47 +00:00
|
|
|
while count < 160 {
|
|
|
|
self.data[oam_addr + count] = self.data[source + count];
|
|
|
|
count += 1;
|
|
|
|
}
|
2021-10-29 03:13:23 +00:00
|
|
|
} else {
|
|
|
|
self.data[address as usize] = data;
|
2021-10-22 20:32:12 +00:00
|
|
|
}
|
2021-10-13 17:56:00 +00:00
|
|
|
}
|
2021-10-16 00:18:00 +00:00
|
|
|
|
2021-11-01 02:02:09 +00:00
|
|
|
pub fn force_write(&mut self, address: u16, data: u8) {
|
|
|
|
self.data[address as usize] = data;
|
|
|
|
}
|
|
|
|
|
2021-10-16 00:18:00 +00:00
|
|
|
pub fn write_16bit(&mut self, address: u16, data: u16) {
|
2021-10-19 14:53:50 +00:00
|
|
|
let bytes = data.to_le_bytes();
|
|
|
|
self.write(address, bytes[0]);
|
2021-10-22 14:56:54 +00:00
|
|
|
self.write(address.wrapping_add(1), bytes[1]);
|
2021-10-16 00:18:00 +00:00
|
|
|
}
|
2021-10-29 03:13:23 +00:00
|
|
|
|
2021-10-31 12:25:47 +00:00
|
|
|
pub fn set_interrupt_flag(&mut self, interrupt: Interrupt, val: bool) {
|
2021-10-29 03:13:23 +00:00
|
|
|
let byte = self.read(INTERRUPT_FLAG_ADDRESS);
|
2021-10-29 23:27:21 +00:00
|
|
|
self.write(INTERRUPT_FLAG_ADDRESS, interrupt.set(byte, val));
|
2021-10-29 03:13:23 +00:00
|
|
|
}
|
2021-10-13 17:56:00 +00:00
|
|
|
}
|