Compare commits

..

No commits in common. "4b47ae96a1a0f286a95bf2a1dea4f5b1a3711358" and "7bfe760a57fca33e5084da04edbf0dcf6fe76d41" have entirely different histories.

6 changed files with 134 additions and 190 deletions

1
.gitignore vendored
View File

@ -1,4 +1,3 @@
/target /target
*.log *.log
/ignore /ignore
.vscode

View File

@ -1,4 +1,3 @@
use std::env;
use std::ops::RangeInclusive; use std::ops::RangeInclusive;
use crate::utils::join_bytes; use crate::utils::join_bytes;
use crate::rom::{ROM, load_rom}; use crate::rom::{ROM, load_rom};
@ -30,21 +29,6 @@ pub const IO_REGISTERS: RangeInclusive<u16> = 0xFF00..=0xFF7F;
pub const HIGH_RAM: RangeInclusive<u16> = 0xFF80..=0xFFFE; pub const HIGH_RAM: RangeInclusive<u16> = 0xFF80..=0xFFFE;
pub const PREPARE_SPEED_SWITCH_ADDRESS: u16 = 0xFF4D; pub const PREPARE_SPEED_SWITCH_ADDRESS: u16 = 0xFF4D;
enum MemoryMap {
BankZero,
BankSwitchable,
VideoRam,
ExternalRam,
WorkRam1,
WorkRam2,
EchoRam,
SpriteAttributeTable,
NotUsable,
IoRegisters,
HighRam,
InterruptEnable,
}
pub struct Bus { pub struct Bus {
data: [u8; 0x10000], data: [u8; 0x10000],
pub rom: Box<dyn ROM>, pub rom: Box<dyn ROM>,
@ -75,8 +59,7 @@ impl Bus {
}, },
}; };
let info = rom.info().clone(); let info = rom.info().clone();
let force_dmg_mode = !env::var("FORCE_DMG").is_err(); let cgb_mode = info.cgb_features() || info.cgb_only();
let cgb_mode = (info.cgb_features() || info.cgb_only()) && !force_dmg_mode;
let mut bus = Self { let mut bus = Self {
data: [0x00; 0x10000], data: [0x00; 0x10000],
rom, rom,
@ -120,53 +103,34 @@ impl Bus {
bus bus
} }
fn map_address(address: u16) -> MemoryMap {
match address {
0x0000..=0x3FFF => MemoryMap::BankZero,
0x4000..=0x7FFF => MemoryMap::BankSwitchable,
0x8000..=0x9FFF => MemoryMap::VideoRam,
0xA000..=0xBFFF => MemoryMap::ExternalRam,
0xC000..=0xCFFF => MemoryMap::WorkRam1,
0xD000..=0xDFFF => MemoryMap::WorkRam2,
0xE000..=0xFDFF => MemoryMap::EchoRam,
0xFE00..=0xFE9F => MemoryMap::SpriteAttributeTable,
0xFEA0..=0xFEFF => MemoryMap::NotUsable,
0xFF00..=0xFF7F => MemoryMap::IoRegisters,
0xFF80..=0xFFFE => MemoryMap::HighRam,
INTERRUPT_ENABLE_ADDRESS => MemoryMap::InterruptEnable,
}
}
pub fn read(&self, address: u16) -> u8 { pub fn read(&self, address: u16) -> u8 {
match Bus::map_address(address) { if BANK_ZERO.contains(&address) || BANK_SWITCHABLE.contains(&address) || EXTERNAL_RAM.contains(&address) {
MemoryMap::BankZero | MemoryMap::BankSwitchable | MemoryMap::ExternalRam => self.rom.read(address), return self.rom.read(address);
MemoryMap::WorkRam1 | MemoryMap::WorkRam2 | MemoryMap::EchoRam => self.ram.read(address), } else if WORK_RAM_1.contains(&address) || WORK_RAM_2.contains(&address) || address == WRAM_BANK_SELECT_ADDRESS {
MemoryMap::VideoRam => self.ppu.read_vram_external(address), return self.ram.read(address);
MemoryMap::SpriteAttributeTable => self.ppu.read_oam(address), } else if ECHO_RAM.contains(&address) {
MemoryMap::IoRegisters => { return self.ram.read(WORK_RAM_1.min().unwrap() + ((address - ECHO_RAM.min().unwrap()) & 0x1FFF));
if self.cgb_mode && address == PREPARE_SPEED_SWITCH_ADDRESS { } else if address == INTERRUPT_ENABLE_ADDRESS || address == INTERRUPT_FLAG_ADDRESS {
let byte = self.data[address as usize]; return self.interrupts.read(address);
let current_speed = (self.double_speed_mode as u8) << 7; } else if VIDEO_RAM.contains(&address) {
let prepare_speed_switch = self.prepare_double_speed_mode as u8; return self.ppu.read_vram_external(address);
return (byte & 0b0111_1110) | current_speed | prepare_speed_switch; } else if SPRITE_ATTRIBUTE_TABLE.contains(&address) {
} else if address == WRAM_BANK_SELECT_ADDRESS { return self.ppu.read_oam(address);
return self.ram.read(address); } else if PPU::is_io_register(address) {
} else if address == INTERRUPT_FLAG_ADDRESS { return self.ppu.get_register(address);
return self.interrupts.read(address); } else if Sound::is_io_register(address) {
} else if PPU::is_io_register(address) { return self.sound.get_register(address);
return self.ppu.get_register(address); } else if address == JOYPAD_ADDRESS {
} else if Sound::is_io_register(address) { return self.joypad.read(self.data[address as usize]);
return self.sound.get_register(address); } else if Timer::is_io_register(address) {
} else if Timer::is_io_register(address) { return self.timer.get_register(address);
return self.timer.get_register(address); } else if address == PREPARE_SPEED_SWITCH_ADDRESS && self.cgb_mode {
} else if address == JOYPAD_ADDRESS { let byte = self.data[address as usize];
return self.joypad.read(self.data[address as usize]); let current_speed = (self.double_speed_mode as u8) << 7;
} let prepare_speed_switch = self.prepare_double_speed_mode as u8;
return self.data[address as usize]; return (byte & 0b0111_1110) | current_speed | prepare_speed_switch;
},
MemoryMap::InterruptEnable => self.interrupts.read(address),
_ => self.data[address as usize],
} }
self.data[address as usize]
} }
pub fn read_16bit(&self, address: u16) -> u16 { pub fn read_16bit(&self, address: u16) -> u16 {
@ -174,48 +138,47 @@ impl Bus {
} }
pub fn write(&mut self, address: u16, data: u8) { pub fn write(&mut self, address: u16, data: u8) {
match Bus::map_address(address) { if address == 0xFF01 {
MemoryMap::BankZero | MemoryMap::BankSwitchable | MemoryMap::ExternalRam => self.rom.write(address, data), // print!("{}", data as char);
MemoryMap::WorkRam1 | MemoryMap::WorkRam2 | MemoryMap::EchoRam => self.ram.write(address, data), }
MemoryMap::VideoRam => self.ppu.write_vram_external(address, data),
MemoryMap::SpriteAttributeTable => self.ppu.write_oam(address, data), if BANK_ZERO.contains(&address) || BANK_SWITCHABLE.contains(&address) || EXTERNAL_RAM.contains(&address) {
MemoryMap::IoRegisters => { self.rom.write(address, data);
if self.cgb_mode && address == PREPARE_SPEED_SWITCH_ADDRESS { } else if address == INTERRUPT_ENABLE_ADDRESS || address == INTERRUPT_FLAG_ADDRESS {
let current_byte = self.data[address as usize]; self.interrupts.write(address, data);
self.prepare_double_speed_mode = (data & 1) == 1; } else if WORK_RAM_1.contains(&address) || WORK_RAM_2.contains(&address) || address == WRAM_BANK_SELECT_ADDRESS {
// bit 7 is read only on cgb mode self.ram.write(address, data);
self.data[address as usize] = (current_byte & 0b1000_0000) | (data & 0b0111_1111); } else if EXTERNAL_RAM.contains(&address) {
} else if address == WRAM_BANK_SELECT_ADDRESS { self.rom.write(address, data);
self.ram.write(address, data); } else if ECHO_RAM.contains(&address) {
} else if address == INTERRUPT_FLAG_ADDRESS { self.ram.write(WORK_RAM_1.min().unwrap() + ((address - ECHO_RAM.min().unwrap()) & 0x1FFF), data);
self.interrupts.write(address, data); } else if Timer::is_io_register(address) {
} else if PPU::is_io_register(address) { self.timer.set_register(address, data);
self.ppu.set_register(address, data); } else if Sound::is_io_register(address) {
match address { self.sound.set_register(address, data);
DMA_ADDRESS => { } else if address == JOYPAD_ADDRESS {
self.ppu.set_register(address, data); let byte = self.data[address as usize];
self.dma_transfer(data); self.data[address as usize] = (data & 0b11110000) | (byte & 0b00001111);
}, } else if VIDEO_RAM.contains(&address) {
HDMA5_ADDRESS => { return self.ppu.write_vram_external(address, data);
self.ppu.set_register(address, data); } else if SPRITE_ATTRIBUTE_TABLE.contains(&address) {
self.hdma_transfer(data); return self.ppu.write_oam(address, data);
}, } else if address == DMA_ADDRESS {
_ => {} self.ppu.set_register(address, data);
} self.dma_transfer(data);
} else if Sound::is_io_register(address) { } else if address == HDMA5_ADDRESS {
self.sound.set_register(address, data); self.ppu.set_register(address, data);
} else if Timer::is_io_register(address) { self.hdma_transfer(data);
self.timer.set_register(address, data); } else if PPU::is_io_register(address) {
} else if address == JOYPAD_ADDRESS { self.ppu.set_register(address, data);
let byte = self.data[address as usize]; } else if address == PREPARE_SPEED_SWITCH_ADDRESS && self.cgb_mode {
self.data[address as usize] = (data & 0b11110000) | (byte & 0b00001111); let current_byte = self.data[address as usize];
} else { self.prepare_double_speed_mode = (data & 1) == 1;
self.data[address as usize] = data; // bit 7 is read only on cgb mode
} self.data[address as usize] = (current_byte & 0b1000_0000) | (data & 0b0111_1111);
}, } else {
MemoryMap::InterruptEnable => self.interrupts.write(address, data), self.data[address as usize] = data;
_ => self.data[address as usize] = data, }
};
} }
pub fn write_16bit(&mut self, address: u16, data: u16) { pub fn write_16bit(&mut self, address: u16, data: u16) {

View File

@ -920,6 +920,7 @@ impl CPU {
} }
pub fn handle_interrupt(&mut self, bus: &mut Bus, interrupt: Interrupt) { pub fn handle_interrupt(&mut self, bus: &mut Bus, interrupt: Interrupt) {
// println!("Interrupt: {:?}", interrupt);
bus.interrupts.set(interrupt, false); bus.interrupts.set(interrupt, false);
self.ime = false; self.ime = false;
self.registers.decrement(Register::PC, 3); self.registers.decrement(Register::PC, 3);

View File

@ -394,83 +394,85 @@ impl PPU {
} }
pub fn get_register(&self, address: u16) -> u8 { pub fn get_register(&self, address: u16) -> u8 {
match address { if address >= HDMA1_ADDRESS && address <= HDMA5_ADDRESS {
HDMA1_ADDRESS..=HDMA5_ADDRESS => match address { return match address {
HDMA1_ADDRESS => self.hdma_source.to_be_bytes()[0], HDMA1_ADDRESS => self.hdma_source.to_be_bytes()[0],
HDMA2_ADDRESS => self.hdma_source.to_be_bytes()[1], HDMA2_ADDRESS => self.hdma_source.to_be_bytes()[1],
HDMA3_ADDRESS => self.hdma_destination.to_be_bytes()[0], HDMA3_ADDRESS => self.hdma_destination.to_be_bytes()[0],
HDMA4_ADDRESS => self.hdma_destination.to_be_bytes()[1], HDMA4_ADDRESS => self.hdma_destination.to_be_bytes()[1],
HDMA5_ADDRESS => self.hdma_start, HDMA5_ADDRESS => self.hdma_start,
_ => 0x00, _ => 0x00,
}, }
0xFF68..=0xFF6B => self.cram_registers[(address as usize) - 0xFF68], } else if address >= 0xFF68 && address <= 0xFF6B {
VRAM_BANK_SELECT_ADDRESS => self.get_vram_bank(), return self.cram_registers[(address as usize) - 0xFF68];
LCD_CONTROL_ADDRESS => self.lcd_control, } else if address == VRAM_BANK_SELECT_ADDRESS {
LCD_Y_ADDRESS => self.lcd_y, return self.get_vram_bank();
_ => self.io_registers[(address - 0xFF40) as usize], } else if address == LCD_CONTROL_ADDRESS {
return self.lcd_control;
} else if address == LCD_Y_ADDRESS {
return self.lcd_y;
} }
self.io_registers[(address - 0xFF40) as usize]
} }
pub fn set_register(&mut self, address: u16, data: u8) { pub fn set_register(&mut self, address: u16, data: u8) {
match address { if address >= HDMA1_ADDRESS && address <= HDMA5_ADDRESS {
HDMA1_ADDRESS..=HDMA5_ADDRESS => match address { match address {
HDMA1_ADDRESS => self.hdma_source = (self.hdma_source & 0xFF) | ((data as u16) << 8), HDMA1_ADDRESS => self.hdma_source = (self.hdma_source & 0xFF) | ((data as u16) << 8),
HDMA2_ADDRESS => self.hdma_source = (self.hdma_source & 0xFF00) | (data as u16), HDMA2_ADDRESS => self.hdma_source = (self.hdma_source & 0xFF00) | (data as u16),
HDMA3_ADDRESS => self.hdma_destination = (self.hdma_destination & 0xFF) | ((data as u16) << 8), HDMA3_ADDRESS => self.hdma_destination = (self.hdma_destination & 0xFF) | ((data as u16) << 8),
HDMA4_ADDRESS => self.hdma_destination = (self.hdma_destination & 0xFF00) | (data as u16), HDMA4_ADDRESS => self.hdma_destination = (self.hdma_destination & 0xFF00) | (data as u16),
HDMA5_ADDRESS => self.hdma_start = data, HDMA5_ADDRESS => self.hdma_start = data,
_ => (), _ => (),
}, };
0xFF68..=0xFF6B => { } else if address >= 0xFF68 && address <= 0xFF6B {
self.cram_registers[(address as usize) - 0xFF68] = data; self.cram_registers[(address as usize) - 0xFF68] = data;
match address {
BCPD_BGPD_ADDRESS => { if address == BCPD_BGPD_ADDRESS {
if self.get_lcd_status(LCDStatus::ModeFlag(LCDStatusModeFlag::TransferringToLCD)) { if self.get_lcd_status(LCDStatus::ModeFlag(LCDStatusModeFlag::TransferringToLCD)) {
return; return;
}
let byte = self.cram_registers[(BCPS_BGPI_ADDRESS as usize) - 0xFF68];
let auto_increment = get_bit(byte, BitIndex::I7);
let cram_address = byte & 0b111111;
self.bg_cram[cram_address as usize] = data;
if auto_increment {
self.cram_registers[(BCPS_BGPI_ADDRESS as usize) - 0xFF68] = ((byte + 1) & 0b111111) | ((auto_increment as u8) << 7);
}
},
OCPD_OBPD_ADDRESS => {
if self.get_lcd_status(LCDStatus::ModeFlag(LCDStatusModeFlag::TransferringToLCD)) {
return;
}
let byte = self.cram_registers[(OCPS_OBPI_ADDRESS as usize) - 0xFF68];
let auto_increment = get_bit(byte, BitIndex::I7);
let cram_address = byte & 0b111111;
self.obj_cram[cram_address as usize] = data;
if auto_increment {
self.cram_registers[(OCPS_OBPI_ADDRESS as usize) - 0xFF68] = ((byte + 1) & 0b111111) | ((auto_increment as u8) << 7);
}
}
_ => {},
} }
}, let byte = self.cram_registers[(BCPS_BGPI_ADDRESS as usize) - 0xFF68];
VRAM_BANK_SELECT_ADDRESS => self.set_vram_bank(data), let auto_increment = get_bit(byte, BitIndex::I7);
LCD_Y_ADDRESS => {}, let cram_address = byte & 0b111111;
LCD_CONTROL_ADDRESS => { self.bg_cram[cram_address as usize] = data;
self.lcd_control = data; if auto_increment {
// Check if LCD is being turned on or off self.cram_registers[(BCPS_BGPI_ADDRESS as usize) - 0xFF68] = ((byte + 1) & 0b111111) | ((auto_increment as u8) << 7);
self.lcd_enable = get_bit(data, BitIndex::I7);
if !get_bit(data, BitIndex::I7) || (get_bit(data, BitIndex::I7) && !get_bit(self.lcd_control, BitIndex::I7)) {
self.lcd_y = 0x00;
// Set Hblank
let byte = self.io_registers[LCD_STATUS_ADDRESS as usize - 0xFF40];
self.io_registers[LCD_STATUS_ADDRESS as usize - 0xFF40] = byte & 0b11111100;
} }
}, } else if address == OCPD_OBPD_ADDRESS {
LCD_STATUS_ADDRESS => { if self.get_lcd_status(LCDStatus::ModeFlag(LCDStatusModeFlag::TransferringToLCD)) {
let address = address - 0xFF40; return;
let byte = self.io_registers[address as usize]; }
self.io_registers[address as usize] = (data & 0b11111000) | (byte & 0b00000111); let byte = self.cram_registers[(OCPS_OBPI_ADDRESS as usize) - 0xFF68];
}, let auto_increment = get_bit(byte, BitIndex::I7);
_ => self.io_registers[address as usize - 0xFF40] = data, let cram_address = byte & 0b111111;
}; self.obj_cram[cram_address as usize] = data;
if auto_increment {
self.cram_registers[(OCPS_OBPI_ADDRESS as usize) - 0xFF68] = ((byte + 1) & 0b111111) | ((auto_increment as u8) << 7);
}
}
} else if address == VRAM_BANK_SELECT_ADDRESS {
return self.set_vram_bank(data);
} else if address == LCD_Y_ADDRESS {
return;
} else if address == LCD_CONTROL_ADDRESS {
self.lcd_control = data;
// Check if LCD is being turned on or off
self.lcd_enable = get_bit(data, BitIndex::I7);
if !get_bit(data, BitIndex::I7) || (get_bit(data, BitIndex::I7) && !get_bit(self.lcd_control, BitIndex::I7)) {
self.lcd_y = 0x00;
// Set Hblank
let byte = self.io_registers[LCD_STATUS_ADDRESS as usize - 0xFF40];
self.io_registers[LCD_STATUS_ADDRESS as usize - 0xFF40] = byte & 0b11111100;
}
return;
} else if address == LCD_STATUS_ADDRESS {
let address = address - 0xFF40;
let byte = self.io_registers[address as usize];
self.io_registers[address as usize] = (data & 0b11111000) | (byte & 0b00000111);
} else {
self.io_registers[address as usize - 0xFF40] = data;
}
} }
pub fn force_set_register(&mut self, address: u16, data: u8) { pub fn force_set_register(&mut self, address: u16, data: u8) {

View File

@ -1,5 +1,3 @@
use crate::bus::{ECHO_RAM, WORK_RAM_1};
pub const WRAM_BANK_SELECT_ADDRESS: u16 = 0xFF70; pub const WRAM_BANK_SELECT_ADDRESS: u16 = 0xFF70;
pub trait RAM { pub trait RAM {
@ -19,20 +17,11 @@ impl DMGRAM {
} }
} }
fn parse_echo_ram_address(address: u16) -> u16 {
let mut address = address;
if ECHO_RAM.contains(&address) {
address = WORK_RAM_1.min().unwrap() + ((address - ECHO_RAM.min().unwrap()) & 0x1FFF);
}
address
}
impl RAM for DMGRAM { impl RAM for DMGRAM {
fn read(&self, address: u16) -> u8 { fn read(&self, address: u16) -> u8 {
if address == WRAM_BANK_SELECT_ADDRESS { if address == WRAM_BANK_SELECT_ADDRESS {
return 0xFF; return 0xFF;
} }
let address = parse_echo_ram_address(address);
self.data[(address - 0xC000) as usize] self.data[(address - 0xC000) as usize]
} }
@ -40,7 +29,6 @@ impl RAM for DMGRAM {
if address == WRAM_BANK_SELECT_ADDRESS { if address == WRAM_BANK_SELECT_ADDRESS {
return; return;
} }
let address = parse_echo_ram_address(address);
self.data[(address - 0xC000) as usize] = value; self.data[(address - 0xC000) as usize] = value;
} }
} }
@ -75,7 +63,6 @@ impl RAM for CGBRAM {
if address == WRAM_BANK_SELECT_ADDRESS { if address == WRAM_BANK_SELECT_ADDRESS {
return self.bank; return self.bank;
} }
let address = parse_echo_ram_address(address);
if address <= 0xCFFF { if address <= 0xCFFF {
return self.data[(address - 0xC000) as usize]; return self.data[(address - 0xC000) as usize];
} }
@ -85,9 +72,7 @@ impl RAM for CGBRAM {
fn write(&mut self, address: u16, value: u8) { fn write(&mut self, address: u16, value: u8) {
if address == WRAM_BANK_SELECT_ADDRESS { if address == WRAM_BANK_SELECT_ADDRESS {
return self.switch_bank(value); return self.switch_bank(value);
} } else if address <= 0xCFFF {
let address = parse_echo_ram_address(address);
if address <= 0xCFFF {
return self.data[(address - 0xC000) as usize] = value; return self.data[(address - 0xC000) as usize] = value;
} }
self.data[((address - 0xD000) as usize) + (4096 * (self.bank as usize))] = value; self.data[((address - 0xD000) as usize) + (4096 * (self.bank as usize))] = value;

View File

@ -2,7 +2,6 @@ use crate::emulator::Emulator;
use crate::frames::Frames; use crate::frames::Frames;
use crate::ppu::{WIDTH, HEIGHT}; use crate::ppu::{WIDTH, HEIGHT};
use std::env;
use log::error; use log::error;
use pixels::{wgpu, Pixels, PixelsBuilder, SurfaceTexture}; use pixels::{wgpu, Pixels, PixelsBuilder, SurfaceTexture};
use winit::dpi::LogicalSize; use winit::dpi::LogicalSize;
@ -11,13 +10,10 @@ use winit::event_loop::{ControlFlow, EventLoop};
use winit::window::{Window, WindowBuilder}; use winit::window::{Window, WindowBuilder};
use winit_input_helper::WinitInputHelper; use winit_input_helper::WinitInputHelper;
fn is_fps_unlocked() -> bool {
!env::var("UNLOCK_FPS").is_err()
}
pub fn create_pixels(width: u32, height: u32, window: &Window) -> Pixels { pub fn create_pixels(width: u32, height: u32, window: &Window) -> Pixels {
let window_size = window.inner_size(); let window_size = window.inner_size();
let surface_texture = SurfaceTexture::new(window_size.width, window_size.height, window); let surface_texture = SurfaceTexture::new(window_size.width, window_size.height, window);
// Pixels::new(width, height, surface_texture).unwrap()
PixelsBuilder::new(width, height, surface_texture) PixelsBuilder::new(width, height, surface_texture)
.device_descriptor(wgpu::DeviceDescriptor { .device_descriptor(wgpu::DeviceDescriptor {
limits: wgpu::Limits { limits: wgpu::Limits {
@ -28,7 +24,7 @@ pub fn create_pixels(width: u32, height: u32, window: &Window) -> Pixels {
}, },
..wgpu::DeviceDescriptor::default() ..wgpu::DeviceDescriptor::default()
}) })
.enable_vsync(!is_fps_unlocked()) .enable_vsync(false)
.build() .build()
.unwrap() .unwrap()
} }
@ -92,9 +88,7 @@ pub fn start_eventloop() {
frame_counter.reset_timer(); frame_counter.reset_timer();
} }
window.request_redraw(); window.request_redraw();
if !is_fps_unlocked() { frame_limit.limit();
frame_limit.limit();
}
frame_limit.reset_timer(); frame_limit.reset_timer();
}, },
Event::RedrawRequested(_) => { Event::RedrawRequested(_) => {