use crate::cartridge::Cartridge;
use crate::gameboy::interrupt::InterruptHandler;
use crate::gameboy::timer::Timer;
use crate::gameboy::lcd::LCD;
use crate::gameboy::joypad::Joypad;
pub struct MMU {
cart: Cartridge,
system_ram: [u8; 0x2000], high_ram: [u8; 0x7F],
serial: u8,
pub interrupt: InterruptHandler,
cycles: u128,
prev_cycles: u128,
timer: Timer,
pub lcd: LCD,
pub joypad: Joypad,
}
impl MMU {
pub fn new(cartridge: Cartridge) -> MMU {
MMU {
cart: cartridge,
system_ram: [0x0; 0x2000],
high_ram: [0x0; 0x7F],
serial: 0x00,
interrupt: InterruptHandler::new(),
cycles: 0,
prev_cycles: 0,
timer: Timer::new(),
lcd: LCD::new(),
joypad: Joypad::new(),
}
}
pub fn get_cycle_diff(&mut self) -> u8 {
let cycle_diff = self.cycles - self.prev_cycles;
self.prev_cycles = self.cycles;
cycle_diff as u8
}
fn read_addr_map(&self, addr: u16) -> u8 {
match addr {
0x0000 ..= 0x3FFF => self.cart.read(addr), 0x4000 ..= 0x7FFF => self.cart.read(addr), 0x8000 ..= 0x97FF => self.lcd.vram_tile_data[(addr - 0x8000) as usize],
0x9800 ..= 0x9BFF => self.lcd.vram_bg_maps[(addr - 0x9800) as usize], 0x9C00 ..= 0x9FFF => self.lcd.vram_bg_maps[(addr - 0x9800) as usize], 0xA000 ..= 0xBFFF => self.cart.read(addr), 0xC000 ..= 0xDFFF => self.system_ram[(addr - 0xC000) as usize],
0xE000 ..= 0xFDFF => self.system_ram[(addr - 0xE000) as usize], 0xFE00 ..= 0xFE9F => self.lcd.read_oam(addr - 0xFE00), 0xFEA0 ..= 0xFEFF => 0xFF, 0xFF00 => self.joypad.as_u8(), 0xFF01 => 0xFF, 0xFF02 => 0xFF, 0xFF03 => 0xFF, 0xFF04 ..= 0xFF07 => self.timer.read_register(addr),
0xFF08 ..= 0xFF0E => 0xFF, 0xFF0F => self.interrupt.get_flag(),
0xFF10 ..= 0xFF26 => 0xFF, 0xFF27 ..= 0xFF2F => 0xFF, 0xFF30 ..= 0xFF3F => 0xFF, 0xFF40 ..= 0xFF4B => self.lcd.read_register(addr), 0xFF4C ..= 0xFF4F => 0xFF, 0xFF50 => 0xFF, 0xFF51 ..= 0xFF7F => 0xFF, 0xFF80 ..= 0xFFFE => self.high_ram[(addr & 0x7F) as usize],
0xFFFF => self.interrupt.get_enable(),
}
}
fn write_addr_map(&mut self, addr: u16, value: u8) {
match addr {
0x0000 ..= 0x7FFF => self.cart.write(addr, value), 0x8000 ..= 0x97FF => self.lcd.vram_tile_data[(addr - 0x8000) as usize] = value,
0x9800 ..= 0x9BFF => self.lcd.vram_bg_maps[(addr - 0x9800) as usize] = value, 0x9C00 ..= 0x9FFF => self.lcd.vram_bg_maps[(addr - 0x9800) as usize] = value, 0xA000 ..= 0xBFFF => self.cart.write(addr, value), 0xC000 ..= 0xDFFF => self.system_ram[(addr - 0xC000) as usize] = value,
0xE000 ..= 0xFDFF => self.system_ram[(addr - 0xE000) as usize] = value, 0xFE00 ..= 0xFE9F => self.lcd.write_oam(addr - 0xFE00, value), 0xFEA0 ..= 0xFEFF => (), 0xFF00 => self.joypad.write_select_bits(value), 0xFF01 => self.serial = value, 0xFF02 => { print!("{}", self.serial as char); }, 0xFF03 => (), 0xFF04 ..= 0xFF07 => self.timer.write_register(addr, value),
0xFF08 ..= 0xFF0E => (), 0xFF0F => self.interrupt.set_flag(value),
0xFF10 ..= 0xFF26 => (), 0xFF27 ..= 0xFF2F => (), 0xFF30 ..= 0xFF3F => (), 0xFF40 ..= 0xFF45 => self.lcd.write_register(addr, value), 0xFF46 => self.dma_transfer(value), 0xFF47 ..= 0xFF4B => self.lcd.write_register(addr, value), 0xFF4C ..= 0xFF4F => (), 0xFF50 => (), 0xFF51 ..= 0xFF7F => (), 0xFF80 ..= 0xFFFE => self.high_ram[(addr & 0x007F) as usize] = value,
0xFFFF => self.interrupt.set_enable(value),
}
}
pub fn read_u8(&mut self, addr: u16) -> u8 {
self.step();
self.read_addr_map(addr)
}
pub fn write_u8(&mut self, addr: u16, value: u8) {
self.step();
self.write_addr_map(addr, value);
}
pub fn dma_transfer(&mut self, value: u8) {
let addr = (value as u16) << 8;
for offset in 0x00..0xA0 {
let data = self.read_u8(addr + offset);
self.write_u8(0xFE00 + offset, data);
}
}
fn add_machine_cycles(&mut self, machine_cycles: u8) {
self.cycles += (machine_cycles as u128) * 4;
}
fn step(&mut self) {
self.add_machine_cycles(1);
self.timer.step(&mut self.interrupt);
self.lcd.step(&mut self.interrupt);
}
pub fn spin(&mut self) {
self.step();
}
}