mirror of
https://github.com/FranLMSP/rmg-001.git
synced 2024-11-14 14:11:33 +00:00
Compare commits
2 Commits
7bfe760a57
...
4b47ae96a1
Author | SHA1 | Date | |
---|---|---|---|
4b47ae96a1 | |||
76a930385d |
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,3 +1,4 @@
|
|||||||
/target
|
/target
|
||||||
*.log
|
*.log
|
||||||
/ignore
|
/ignore
|
||||||
|
.vscode
|
||||||
|
175
src/bus.rs
175
src/bus.rs
@ -1,3 +1,4 @@
|
|||||||
|
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};
|
||||||
@ -29,6 +30,21 @@ 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>,
|
||||||
@ -59,7 +75,8 @@ impl Bus {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
let info = rom.info().clone();
|
let info = rom.info().clone();
|
||||||
let cgb_mode = info.cgb_features() || info.cgb_only();
|
let force_dmg_mode = !env::var("FORCE_DMG").is_err();
|
||||||
|
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,
|
||||||
@ -103,34 +120,53 @@ impl Bus {
|
|||||||
bus
|
bus
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read(&self, address: u16) -> u8 {
|
fn map_address(address: u16) -> MemoryMap {
|
||||||
if BANK_ZERO.contains(&address) || BANK_SWITCHABLE.contains(&address) || EXTERNAL_RAM.contains(&address) {
|
match address {
|
||||||
return self.rom.read(address);
|
0x0000..=0x3FFF => MemoryMap::BankZero,
|
||||||
} else if WORK_RAM_1.contains(&address) || WORK_RAM_2.contains(&address) || address == WRAM_BANK_SELECT_ADDRESS {
|
0x4000..=0x7FFF => MemoryMap::BankSwitchable,
|
||||||
return self.ram.read(address);
|
0x8000..=0x9FFF => MemoryMap::VideoRam,
|
||||||
} else if ECHO_RAM.contains(&address) {
|
0xA000..=0xBFFF => MemoryMap::ExternalRam,
|
||||||
return self.ram.read(WORK_RAM_1.min().unwrap() + ((address - ECHO_RAM.min().unwrap()) & 0x1FFF));
|
0xC000..=0xCFFF => MemoryMap::WorkRam1,
|
||||||
} else if address == INTERRUPT_ENABLE_ADDRESS || address == INTERRUPT_FLAG_ADDRESS {
|
0xD000..=0xDFFF => MemoryMap::WorkRam2,
|
||||||
return self.interrupts.read(address);
|
0xE000..=0xFDFF => MemoryMap::EchoRam,
|
||||||
} else if VIDEO_RAM.contains(&address) {
|
0xFE00..=0xFE9F => MemoryMap::SpriteAttributeTable,
|
||||||
return self.ppu.read_vram_external(address);
|
0xFEA0..=0xFEFF => MemoryMap::NotUsable,
|
||||||
} else if SPRITE_ATTRIBUTE_TABLE.contains(&address) {
|
0xFF00..=0xFF7F => MemoryMap::IoRegisters,
|
||||||
return self.ppu.read_oam(address);
|
0xFF80..=0xFFFE => MemoryMap::HighRam,
|
||||||
} else if PPU::is_io_register(address) {
|
INTERRUPT_ENABLE_ADDRESS => MemoryMap::InterruptEnable,
|
||||||
return self.ppu.get_register(address);
|
}
|
||||||
} else if Sound::is_io_register(address) {
|
}
|
||||||
return self.sound.get_register(address);
|
|
||||||
} else if address == JOYPAD_ADDRESS {
|
pub fn read(&self, address: u16) -> u8 {
|
||||||
return self.joypad.read(self.data[address as usize]);
|
match Bus::map_address(address) {
|
||||||
} else if Timer::is_io_register(address) {
|
MemoryMap::BankZero | MemoryMap::BankSwitchable | MemoryMap::ExternalRam => self.rom.read(address),
|
||||||
return self.timer.get_register(address);
|
MemoryMap::WorkRam1 | MemoryMap::WorkRam2 | MemoryMap::EchoRam => self.ram.read(address),
|
||||||
} else if address == PREPARE_SPEED_SWITCH_ADDRESS && self.cgb_mode {
|
MemoryMap::VideoRam => self.ppu.read_vram_external(address),
|
||||||
let byte = self.data[address as usize];
|
MemoryMap::SpriteAttributeTable => self.ppu.read_oam(address),
|
||||||
let current_speed = (self.double_speed_mode as u8) << 7;
|
MemoryMap::IoRegisters => {
|
||||||
let prepare_speed_switch = self.prepare_double_speed_mode as u8;
|
if self.cgb_mode && address == PREPARE_SPEED_SWITCH_ADDRESS {
|
||||||
return (byte & 0b0111_1110) | current_speed | prepare_speed_switch;
|
let byte = 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 (byte & 0b0111_1110) | current_speed | prepare_speed_switch;
|
||||||
|
} else if address == WRAM_BANK_SELECT_ADDRESS {
|
||||||
|
return self.ram.read(address);
|
||||||
|
} else if address == INTERRUPT_FLAG_ADDRESS {
|
||||||
|
return self.interrupts.read(address);
|
||||||
|
} else if PPU::is_io_register(address) {
|
||||||
|
return self.ppu.get_register(address);
|
||||||
|
} else if Sound::is_io_register(address) {
|
||||||
|
return self.sound.get_register(address);
|
||||||
|
} else if Timer::is_io_register(address) {
|
||||||
|
return self.timer.get_register(address);
|
||||||
|
} else if address == JOYPAD_ADDRESS {
|
||||||
|
return self.joypad.read(self.data[address as usize]);
|
||||||
|
}
|
||||||
|
return self.data[address as usize];
|
||||||
|
},
|
||||||
|
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 {
|
||||||
@ -138,47 +174,48 @@ impl Bus {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn write(&mut self, address: u16, data: u8) {
|
pub fn write(&mut self, address: u16, data: u8) {
|
||||||
if address == 0xFF01 {
|
match Bus::map_address(address) {
|
||||||
// print!("{}", data as char);
|
MemoryMap::BankZero | MemoryMap::BankSwitchable | MemoryMap::ExternalRam => self.rom.write(address, data),
|
||||||
}
|
MemoryMap::WorkRam1 | MemoryMap::WorkRam2 | MemoryMap::EchoRam => self.ram.write(address, data),
|
||||||
|
MemoryMap::VideoRam => self.ppu.write_vram_external(address, data),
|
||||||
if BANK_ZERO.contains(&address) || BANK_SWITCHABLE.contains(&address) || EXTERNAL_RAM.contains(&address) {
|
MemoryMap::SpriteAttributeTable => self.ppu.write_oam(address, data),
|
||||||
self.rom.write(address, data);
|
MemoryMap::IoRegisters => {
|
||||||
} else if address == INTERRUPT_ENABLE_ADDRESS || address == INTERRUPT_FLAG_ADDRESS {
|
if self.cgb_mode && address == PREPARE_SPEED_SWITCH_ADDRESS {
|
||||||
self.interrupts.write(address, data);
|
let current_byte = self.data[address as usize];
|
||||||
} else if WORK_RAM_1.contains(&address) || WORK_RAM_2.contains(&address) || address == WRAM_BANK_SELECT_ADDRESS {
|
self.prepare_double_speed_mode = (data & 1) == 1;
|
||||||
self.ram.write(address, data);
|
// bit 7 is read only on cgb mode
|
||||||
} else if EXTERNAL_RAM.contains(&address) {
|
self.data[address as usize] = (current_byte & 0b1000_0000) | (data & 0b0111_1111);
|
||||||
self.rom.write(address, data);
|
} else if address == WRAM_BANK_SELECT_ADDRESS {
|
||||||
} else if ECHO_RAM.contains(&address) {
|
self.ram.write(address, data);
|
||||||
self.ram.write(WORK_RAM_1.min().unwrap() + ((address - ECHO_RAM.min().unwrap()) & 0x1FFF), data);
|
} else if address == INTERRUPT_FLAG_ADDRESS {
|
||||||
} else if Timer::is_io_register(address) {
|
self.interrupts.write(address, data);
|
||||||
self.timer.set_register(address, data);
|
} else if PPU::is_io_register(address) {
|
||||||
} else if Sound::is_io_register(address) {
|
self.ppu.set_register(address, data);
|
||||||
self.sound.set_register(address, data);
|
match address {
|
||||||
} else if address == JOYPAD_ADDRESS {
|
DMA_ADDRESS => {
|
||||||
let byte = self.data[address as usize];
|
self.ppu.set_register(address, data);
|
||||||
self.data[address as usize] = (data & 0b11110000) | (byte & 0b00001111);
|
self.dma_transfer(data);
|
||||||
} else if VIDEO_RAM.contains(&address) {
|
},
|
||||||
return self.ppu.write_vram_external(address, data);
|
HDMA5_ADDRESS => {
|
||||||
} else if SPRITE_ATTRIBUTE_TABLE.contains(&address) {
|
self.ppu.set_register(address, data);
|
||||||
return self.ppu.write_oam(address, data);
|
self.hdma_transfer(data);
|
||||||
} else if address == DMA_ADDRESS {
|
},
|
||||||
self.ppu.set_register(address, data);
|
_ => {}
|
||||||
self.dma_transfer(data);
|
}
|
||||||
} else if address == HDMA5_ADDRESS {
|
} else if Sound::is_io_register(address) {
|
||||||
self.ppu.set_register(address, data);
|
self.sound.set_register(address, data);
|
||||||
self.hdma_transfer(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 address == JOYPAD_ADDRESS {
|
||||||
} else if address == PREPARE_SPEED_SWITCH_ADDRESS && self.cgb_mode {
|
let byte = self.data[address as usize];
|
||||||
let current_byte = self.data[address as usize];
|
self.data[address as usize] = (data & 0b11110000) | (byte & 0b00001111);
|
||||||
self.prepare_double_speed_mode = (data & 1) == 1;
|
} else {
|
||||||
// bit 7 is read only on cgb mode
|
self.data[address as usize] = data;
|
||||||
self.data[address as usize] = (current_byte & 0b1000_0000) | (data & 0b0111_1111);
|
}
|
||||||
} else {
|
},
|
||||||
self.data[address as usize] = data;
|
MemoryMap::InterruptEnable => self.interrupts.write(address, 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) {
|
||||||
|
@ -920,7 +920,6 @@ 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);
|
||||||
|
120
src/ppu.rs
120
src/ppu.rs
@ -394,85 +394,83 @@ impl PPU {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_register(&self, address: u16) -> u8 {
|
pub fn get_register(&self, address: u16) -> u8 {
|
||||||
if address >= HDMA1_ADDRESS && address <= HDMA5_ADDRESS {
|
match address {
|
||||||
return match address {
|
HDMA1_ADDRESS..=HDMA5_ADDRESS => 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,
|
||||||
}
|
},
|
||||||
} else if address >= 0xFF68 && address <= 0xFF6B {
|
0xFF68..=0xFF6B => self.cram_registers[(address as usize) - 0xFF68],
|
||||||
return self.cram_registers[(address as usize) - 0xFF68];
|
VRAM_BANK_SELECT_ADDRESS => self.get_vram_bank(),
|
||||||
} else if address == VRAM_BANK_SELECT_ADDRESS {
|
LCD_CONTROL_ADDRESS => self.lcd_control,
|
||||||
return self.get_vram_bank();
|
LCD_Y_ADDRESS => self.lcd_y,
|
||||||
} else if address == LCD_CONTROL_ADDRESS {
|
_ => self.io_registers[(address - 0xFF40) as usize],
|
||||||
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) {
|
||||||
if address >= HDMA1_ADDRESS && address <= HDMA5_ADDRESS {
|
match address {
|
||||||
match address {
|
HDMA1_ADDRESS..=HDMA5_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,
|
||||||
_ => (),
|
_ => (),
|
||||||
};
|
},
|
||||||
} else if address >= 0xFF68 && address <= 0xFF6B {
|
0xFF68..=0xFF6B => {
|
||||||
self.cram_registers[(address as usize) - 0xFF68] = data;
|
self.cram_registers[(address as usize) - 0xFF68] = data;
|
||||||
|
match address {
|
||||||
if address == BCPD_BGPD_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];
|
},
|
||||||
let auto_increment = get_bit(byte, BitIndex::I7);
|
VRAM_BANK_SELECT_ADDRESS => self.set_vram_bank(data),
|
||||||
let cram_address = byte & 0b111111;
|
LCD_Y_ADDRESS => {},
|
||||||
self.bg_cram[cram_address as usize] = data;
|
LCD_CONTROL_ADDRESS => {
|
||||||
if auto_increment {
|
self.lcd_control = data;
|
||||||
self.cram_registers[(BCPS_BGPI_ADDRESS as usize) - 0xFF68] = ((byte + 1) & 0b111111) | ((auto_increment as u8) << 7);
|
// 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;
|
||||||
}
|
}
|
||||||
} else if address == OCPD_OBPD_ADDRESS {
|
},
|
||||||
if self.get_lcd_status(LCDStatus::ModeFlag(LCDStatusModeFlag::TransferringToLCD)) {
|
LCD_STATUS_ADDRESS => {
|
||||||
return;
|
let address = address - 0xFF40;
|
||||||
}
|
let byte = self.io_registers[address as usize];
|
||||||
let byte = self.cram_registers[(OCPS_OBPI_ADDRESS as usize) - 0xFF68];
|
self.io_registers[address as usize] = (data & 0b11111000) | (byte & 0b00000111);
|
||||||
let auto_increment = get_bit(byte, BitIndex::I7);
|
},
|
||||||
let cram_address = byte & 0b111111;
|
_ => self.io_registers[address as usize - 0xFF40] = data,
|
||||||
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) {
|
||||||
|
17
src/ram.rs
17
src/ram.rs
@ -1,3 +1,5 @@
|
|||||||
|
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 {
|
||||||
@ -17,11 +19,20 @@ 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]
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -29,6 +40,7 @@ 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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -63,6 +75,7 @@ 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];
|
||||||
}
|
}
|
||||||
@ -72,7 +85,9 @@ 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;
|
||||||
|
@ -2,6 +2,7 @@ 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;
|
||||||
@ -10,10 +11,13 @@ 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 {
|
||||||
@ -24,7 +28,7 @@ pub fn create_pixels(width: u32, height: u32, window: &Window) -> Pixels {
|
|||||||
},
|
},
|
||||||
..wgpu::DeviceDescriptor::default()
|
..wgpu::DeviceDescriptor::default()
|
||||||
})
|
})
|
||||||
.enable_vsync(false)
|
.enable_vsync(!is_fps_unlocked())
|
||||||
.build()
|
.build()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
@ -88,7 +92,9 @@ pub fn start_eventloop() {
|
|||||||
frame_counter.reset_timer();
|
frame_counter.reset_timer();
|
||||||
}
|
}
|
||||||
window.request_redraw();
|
window.request_redraw();
|
||||||
frame_limit.limit();
|
if !is_fps_unlocked() {
|
||||||
|
frame_limit.limit();
|
||||||
|
}
|
||||||
frame_limit.reset_timer();
|
frame_limit.reset_timer();
|
||||||
},
|
},
|
||||||
Event::RedrawRequested(_) => {
|
Event::RedrawRequested(_) => {
|
||||||
|
Loading…
Reference in New Issue
Block a user