mirror of
https://github.com/FranLMSP/rmg-001.git
synced 2024-11-23 18:21:31 +00:00
Compare commits
4 Commits
22c09895d7
...
52ad064ab5
Author | SHA1 | Date | |
---|---|---|---|
52ad064ab5 | |||
2eb5d55bcc | |||
186d6fa23b | |||
841cc96a67 |
@ -17,11 +17,11 @@ Any help or suggestion is welcome!
|
|||||||
- [x] MBC1
|
- [x] MBC1
|
||||||
- [x] MBC2
|
- [x] MBC2
|
||||||
- [ ] MBC3 (partially implemented)
|
- [ ] MBC3 (partially implemented)
|
||||||
- [X] MBC5
|
- [x] MBC5
|
||||||
- [ ] MBC6
|
- [ ] MBC6
|
||||||
- [ ] MBC7
|
- [ ] MBC7
|
||||||
- [ ] HuC1
|
- [ ] HuC1
|
||||||
- [ ] Save files
|
- [x] Save files
|
||||||
- [ ] Web Assembly support (because this is a Rust project and it has to support Web Assembly)
|
- [ ] Web Assembly support (because this is a Rust project and it has to support Web Assembly)
|
||||||
- [ ] Gameboy boot ROM (Not important for now)
|
- [ ] Gameboy boot ROM (Not important for now)
|
||||||
- [ ] Gameboy Color compatibility
|
- [ ] Gameboy Color compatibility
|
||||||
|
18
src/bus.rs
18
src/bus.rs
@ -27,8 +27,8 @@ pub const INTERRUPT_ENABLE_ADDRESS: u16 = 0xFFFF;
|
|||||||
pub const INTERRUPT_FLAG_ADDRESS: u16 = 0xFF0F;
|
pub const INTERRUPT_FLAG_ADDRESS: u16 = 0xFF0F;
|
||||||
|
|
||||||
pub struct Bus {
|
pub struct Bus {
|
||||||
game_rom: Box<dyn ROM>,
|
|
||||||
data: [u8; 0x10000],
|
data: [u8; 0x10000],
|
||||||
|
pub rom: Box<dyn ROM>,
|
||||||
pub ppu: PPU,
|
pub ppu: PPU,
|
||||||
pub joypad: Joypad,
|
pub joypad: Joypad,
|
||||||
pub timer: Timer,
|
pub timer: Timer,
|
||||||
@ -39,19 +39,19 @@ impl Bus {
|
|||||||
let args: Vec<String> = std::env::args().collect();
|
let args: Vec<String> = std::env::args().collect();
|
||||||
#[cfg(not(test))]
|
#[cfg(not(test))]
|
||||||
if args.len() < 2 {
|
if args.len() < 2 {
|
||||||
println!("Please, specify a ROM file");
|
eprintln!("Please, specify a ROM file");
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
let game_rom = match load_rom(&args.get(1).unwrap_or(&"".to_string())) {
|
let rom = match load_rom(&args.get(1).unwrap_or(&"".to_string())) {
|
||||||
Ok(rom) => rom,
|
Ok(rom) => rom,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
println!("Could not read ROM: {}", err);
|
eprintln!("Could not read ROM: {}", err);
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
let mut bus = Self {
|
let mut bus = Self {
|
||||||
data: [0x00; 0x10000],
|
data: [0x00; 0x10000],
|
||||||
game_rom,
|
rom,
|
||||||
ppu: PPU::new(),
|
ppu: PPU::new(),
|
||||||
joypad: Joypad::new(),
|
joypad: Joypad::new(),
|
||||||
timer: Timer::new(),
|
timer: Timer::new(),
|
||||||
@ -85,7 +85,7 @@ impl Bus {
|
|||||||
|
|
||||||
pub fn read(&self, address: u16) -> u8 {
|
pub fn read(&self, address: u16) -> u8 {
|
||||||
if BANK_ZERO.contains(&address) || BANK_SWITCHABLE.contains(&address) || EXTERNAL_RAM.contains(&address) {
|
if BANK_ZERO.contains(&address) || BANK_SWITCHABLE.contains(&address) || EXTERNAL_RAM.contains(&address) {
|
||||||
return self.game_rom.read(address);
|
return self.rom.read(address);
|
||||||
} else if address == INTERRUPT_ENABLE_ADDRESS || address == INTERRUPT_FLAG_ADDRESS {
|
} else if address == INTERRUPT_ENABLE_ADDRESS || address == INTERRUPT_FLAG_ADDRESS {
|
||||||
return 0b11100000 | self.data[address as usize];
|
return 0b11100000 | self.data[address as usize];
|
||||||
} else if VIDEO_RAM.contains(&address) {
|
} else if VIDEO_RAM.contains(&address) {
|
||||||
@ -112,7 +112,7 @@ impl Bus {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if BANK_ZERO.contains(&address) || BANK_SWITCHABLE.contains(&address) || EXTERNAL_RAM.contains(&address) {
|
if BANK_ZERO.contains(&address) || BANK_SWITCHABLE.contains(&address) || EXTERNAL_RAM.contains(&address) {
|
||||||
self.game_rom.write(address, data);
|
self.rom.write(address, data);
|
||||||
} else if WORK_RAM_1.contains(&address) || WORK_RAM_2.contains(&address) {
|
} else if WORK_RAM_1.contains(&address) || WORK_RAM_2.contains(&address) {
|
||||||
self.data[address as usize] = data;
|
self.data[address as usize] = data;
|
||||||
// Copy to the ECHO RAM
|
// Copy to the ECHO RAM
|
||||||
@ -120,7 +120,7 @@ impl Bus {
|
|||||||
self.data[(ECHO_RAM.min().unwrap() + (address - WORK_RAM_1.min().unwrap())) as usize] = data;
|
self.data[(ECHO_RAM.min().unwrap() + (address - WORK_RAM_1.min().unwrap())) as usize] = data;
|
||||||
}
|
}
|
||||||
} else if EXTERNAL_RAM.contains(&address) {
|
} else if EXTERNAL_RAM.contains(&address) {
|
||||||
self.game_rom.write(address, data);
|
self.rom.write(address, data);
|
||||||
} else if ECHO_RAM.contains(&address) {
|
} else if ECHO_RAM.contains(&address) {
|
||||||
self.data[address as usize] = data;
|
self.data[address as usize] = data;
|
||||||
self.data[(WORK_RAM_1.min().unwrap() + (address - ECHO_RAM.min().unwrap())) as usize] = data; // Copy to the working RAM
|
self.data[(WORK_RAM_1.min().unwrap() + (address - ECHO_RAM.min().unwrap())) as usize] = data; // Copy to the working RAM
|
||||||
@ -139,7 +139,7 @@ impl Bus {
|
|||||||
let mut count: u16 = 0;
|
let mut count: u16 = 0;
|
||||||
let oam_addr = SPRITE_ATTRIBUTE_TABLE.min().unwrap();
|
let oam_addr = SPRITE_ATTRIBUTE_TABLE.min().unwrap();
|
||||||
while count < 160 {
|
while count < 160 {
|
||||||
self.ppu.write_oam(oam_addr + count, self.data[(source + count) as usize]);
|
self.ppu.write_oam(oam_addr + count, self.read(source + count));
|
||||||
count += 1;
|
count += 1;
|
||||||
}
|
}
|
||||||
} else if PPU::is_io_register(address) {
|
} else if PPU::is_io_register(address) {
|
||||||
|
@ -5,6 +5,8 @@ use winit::event::{VirtualKeyCode};
|
|||||||
use crate::cpu::{CPU, Cycles, Interrupt};
|
use crate::cpu::{CPU, Cycles, Interrupt};
|
||||||
use crate::bus::Bus;
|
use crate::bus::Bus;
|
||||||
use crate::joypad::{Button};
|
use crate::joypad::{Button};
|
||||||
|
#[cfg(not(test))]
|
||||||
|
use crate::rom::{save_file};
|
||||||
|
|
||||||
pub struct Emulator {
|
pub struct Emulator {
|
||||||
bus: Bus,
|
bus: Bus,
|
||||||
@ -19,6 +21,16 @@ impl Emulator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn close(&self) {
|
||||||
|
println!("closing emulator");
|
||||||
|
|
||||||
|
#[cfg(not(test))]
|
||||||
|
match save_file(self.bus.rom.ram(), self.bus.rom.info()) {
|
||||||
|
Err(err) => eprintln!("Could not save file: {}", err),
|
||||||
|
_ => {},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
pub fn handle_input(&mut self, input: &WinitInputHelper) {
|
pub fn handle_input(&mut self, input: &WinitInputHelper) {
|
||||||
let mut change = false;
|
let mut change = false;
|
||||||
if input.key_pressed(VirtualKeyCode::K) {
|
if input.key_pressed(VirtualKeyCode::K) {
|
||||||
|
@ -2,6 +2,7 @@ pub mod utils;
|
|||||||
pub mod cpu;
|
pub mod cpu;
|
||||||
pub mod ppu;
|
pub mod ppu;
|
||||||
pub mod timer;
|
pub mod timer;
|
||||||
|
pub mod sound;
|
||||||
pub mod rom;
|
pub mod rom;
|
||||||
pub mod bus;
|
pub mod bus;
|
||||||
pub mod joypad;
|
pub mod joypad;
|
||||||
|
26
src/ppu.rs
26
src/ppu.rs
@ -156,8 +156,8 @@ impl Sprite {
|
|||||||
true => 16,
|
true => 16,
|
||||||
false => 8,
|
false => 8,
|
||||||
};
|
};
|
||||||
let x = lcd_x.saturating_sub(self.x.saturating_sub(8));
|
let x = (lcd_x + 8) - self.x;
|
||||||
let y = lcd_y.saturating_sub(self.y .saturating_sub(16));
|
let y = (lcd_y + 16) - self.y;
|
||||||
let x = match self.x_flip {
|
let x = match self.x_flip {
|
||||||
true => 7 - x,
|
true => 7 - x,
|
||||||
false => x,
|
false => x,
|
||||||
@ -430,16 +430,28 @@ impl PPU {
|
|||||||
let y = self.read_oam(addr);
|
let y = self.read_oam(addr);
|
||||||
let x = self.read_oam(addr + 1);
|
let x = self.read_oam(addr + 1);
|
||||||
|
|
||||||
if x == 0 {
|
|
||||||
addr += 4;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let sprite_height: u8 = match long_sprites {
|
let sprite_height: u8 = match long_sprites {
|
||||||
true => 16,
|
true => 16,
|
||||||
false => 8,
|
false => 8,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if x == 0 {
|
||||||
|
addr += 4;
|
||||||
|
continue;
|
||||||
|
} else if x >= 160 + 8 {
|
||||||
|
addr += 4;
|
||||||
|
continue;
|
||||||
|
} else if y == 0 {
|
||||||
|
addr += 4;
|
||||||
|
continue;
|
||||||
|
} else if y >= 144 + 16 {
|
||||||
|
addr += 4;
|
||||||
|
continue;
|
||||||
|
} else if !long_sprites && y <= 8 {
|
||||||
|
addr += 4;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
let lcd_y = self.lcd_y.saturating_add(16);
|
let lcd_y = self.lcd_y.saturating_add(16);
|
||||||
|
|
||||||
if lcd_y < y || lcd_y > (y + sprite_height - 1) {
|
if lcd_y < y || lcd_y > (y + sprite_height - 1) {
|
||||||
|
@ -52,12 +52,13 @@ pub fn start_eventloop() {
|
|||||||
let mut pixels = create_pixels(WIDTH, HEIGHT, &window);
|
let mut pixels = create_pixels(WIDTH, HEIGHT, &window);
|
||||||
|
|
||||||
event_loop.run(move |event, _, control_flow| {
|
event_loop.run(move |event, _, control_flow| {
|
||||||
*control_flow = ControlFlow::Wait;
|
// *control_flow = ControlFlow::Wait;
|
||||||
|
|
||||||
// Handle input events
|
// Handle input events
|
||||||
if input.update(&event) {
|
if input.update(&event) {
|
||||||
// Close events
|
// Close events
|
||||||
if input.key_pressed(VirtualKeyCode::Escape) || input.quit() {
|
if input.key_pressed(VirtualKeyCode::Escape) || input.quit() {
|
||||||
|
emulator.close();
|
||||||
*control_flow = ControlFlow::Exit;
|
*control_flow = ControlFlow::Exit;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -76,6 +77,7 @@ pub fn start_eventloop() {
|
|||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
println!("The close button was pressed; stopping");
|
println!("The close button was pressed; stopping");
|
||||||
|
emulator.close();
|
||||||
*control_flow = ControlFlow::Exit
|
*control_flow = ControlFlow::Exit
|
||||||
},
|
},
|
||||||
Event::MainEventsCleared => {
|
Event::MainEventsCleared => {
|
||||||
|
144
src/rom.rs
144
src/rom.rs
@ -2,6 +2,8 @@
|
|||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
#[cfg(not(test))]
|
#[cfg(not(test))]
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
|
#[cfg(not(test))]
|
||||||
|
use std::io::Write;
|
||||||
|
|
||||||
use crate::bus::{
|
use crate::bus::{
|
||||||
BANK_ZERO,
|
BANK_ZERO,
|
||||||
@ -36,6 +38,7 @@ fn header_checksum(data: &Vec<u8>) -> bool {
|
|||||||
pub fn load_rom(_filename: &str) -> std::io::Result<Box<dyn ROM>> {
|
pub fn load_rom(_filename: &str) -> std::io::Result<Box<dyn ROM>> {
|
||||||
Ok(Box::new(NoMBC::new(Vec::new(), ROMInfo {
|
Ok(Box::new(NoMBC::new(Vec::new(), ROMInfo {
|
||||||
mbc: MBC::NoMBC,
|
mbc: MBC::NoMBC,
|
||||||
|
filename: "".to_string(),
|
||||||
publisher: "".to_string(),
|
publisher: "".to_string(),
|
||||||
title: "".to_string(),
|
title: "".to_string(),
|
||||||
cgb_only: false,
|
cgb_only: false,
|
||||||
@ -58,16 +61,59 @@ pub fn load_rom(filename: &str) -> std::io::Result<Box<dyn ROM>> {
|
|||||||
return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "Header checksum failed. Is this a Gameboy ROM?"));
|
return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "Header checksum failed. Is this a Gameboy ROM?"));
|
||||||
}
|
}
|
||||||
|
|
||||||
let info = ROMInfo::from_bytes(&data);
|
let mut info = ROMInfo::from_bytes(&data);
|
||||||
|
info.set_filename(filename.to_string());
|
||||||
|
let info_copy = info.clone();
|
||||||
|
|
||||||
Ok(match info.mbc {
|
let mut rom: Box<dyn ROM> = match info.mbc {
|
||||||
MBC::NoMBC => Box::new(NoMBC::new(data, info)),
|
MBC::NoMBC => Box::new(NoMBC::new(data, info)),
|
||||||
MBC::MBC1 => Box::new(MBC1::new(data, info)),
|
MBC::MBC1 => Box::new(MBC1::new(data, info)),
|
||||||
MBC::MBC2 => Box::new(MBC2::new(data, info)),
|
MBC::MBC2 => Box::new(MBC2::new(data, info)),
|
||||||
MBC::MBC3 => Box::new(MBC3::new(data, info)),
|
MBC::MBC3 => Box::new(MBC3::new(data, info)),
|
||||||
MBC::MBC5 => Box::new(MBC5::new(data, info)),
|
MBC::MBC5 => Box::new(MBC5::new(data, info)),
|
||||||
_ => unimplemented!(),
|
_ => unimplemented!(),
|
||||||
})
|
};
|
||||||
|
|
||||||
|
#[cfg(not(test))]
|
||||||
|
match load_save(rom.ram_mut(), &info_copy) {
|
||||||
|
Err(err) => eprintln!("Could not load save file: {}", err),
|
||||||
|
_ => {},
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(rom)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(test))]
|
||||||
|
pub fn save_file(ram: &Vec<u8>, info: &ROMInfo) -> std::io::Result<()> {
|
||||||
|
if !info.has_ram || !info.has_battery {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
let mut file = File::create(format!("{}.sav", info.filename))?;
|
||||||
|
file.write_all(ram)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(test))]
|
||||||
|
pub fn load_save(ram: &mut Vec<u8>, info: &ROMInfo) -> std::io::Result<()> {
|
||||||
|
if !info.has_ram || !info.has_battery {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut file = File::open(format!("{}.sav", info.filename))?;
|
||||||
|
let mut data = vec![];
|
||||||
|
file.read_to_end(&mut data)?;
|
||||||
|
|
||||||
|
let mut index = 0;
|
||||||
|
let size = match ram.len() < data.len() {
|
||||||
|
true => ram.len(),
|
||||||
|
false => data.len(),
|
||||||
|
};
|
||||||
|
while index < size {
|
||||||
|
ram[index] = data[index];
|
||||||
|
index += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
@ -86,21 +132,22 @@ enum MBC {
|
|||||||
BandaiTIMA5,
|
BandaiTIMA5,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
enum Region {
|
enum Region {
|
||||||
Japanese,
|
Japanese,
|
||||||
NonJapanese,
|
NonJapanese,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Copy, Clone, PartialEq, Debug)]
|
||||||
enum BankingMode {
|
enum BankingMode {
|
||||||
Simple,
|
Simple,
|
||||||
Advanced,
|
Advanced,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct ROMInfo {
|
pub struct ROMInfo {
|
||||||
mbc: MBC,
|
mbc: MBC,
|
||||||
|
filename: String,
|
||||||
publisher: String,
|
publisher: String,
|
||||||
title: String,
|
title: String,
|
||||||
cgb_only: bool,
|
cgb_only: bool,
|
||||||
@ -114,6 +161,10 @@ pub struct ROMInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ROMInfo {
|
impl ROMInfo {
|
||||||
|
pub fn set_filename(&mut self, filename: String) {
|
||||||
|
self.filename = filename;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn from_bytes(bytes: &[u8]) -> Self {
|
pub fn from_bytes(bytes: &[u8]) -> Self {
|
||||||
let rom_type = bytes[CARTRIDGE_TYPE_ADDRESS as usize];
|
let rom_type = bytes[CARTRIDGE_TYPE_ADDRESS as usize];
|
||||||
Self {
|
Self {
|
||||||
@ -147,6 +198,7 @@ impl ROMInfo {
|
|||||||
0xFF => MBC::HuC1,
|
0xFF => MBC::HuC1,
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
},
|
},
|
||||||
|
filename: "".to_string(),
|
||||||
region: match bytes[DESTINATION_CODE_ADDRESS as usize] {
|
region: match bytes[DESTINATION_CODE_ADDRESS as usize] {
|
||||||
0x00 => Region::Japanese,
|
0x00 => Region::Japanese,
|
||||||
_ => Region::NonJapanese,
|
_ => Region::NonJapanese,
|
||||||
@ -207,11 +259,15 @@ impl ROMInfo {
|
|||||||
pub trait ROM {
|
pub trait ROM {
|
||||||
fn read(&self, address: u16) -> u8;
|
fn read(&self, address: u16) -> u8;
|
||||||
fn write(&mut self, address: u16, data: u8);
|
fn write(&mut self, address: u16, data: u8);
|
||||||
|
fn ram_mut(&mut self) -> &mut Vec<u8>;
|
||||||
|
fn ram(&self) -> &Vec<u8>;
|
||||||
|
fn info(&self) -> &ROMInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct NoMBC {
|
pub struct NoMBC {
|
||||||
data: Vec<u8>,
|
data: Vec<u8>,
|
||||||
info: ROMInfo,
|
info: ROMInfo,
|
||||||
|
ram: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NoMBC {
|
impl NoMBC {
|
||||||
@ -219,6 +275,7 @@ impl NoMBC {
|
|||||||
let rom = Self {
|
let rom = Self {
|
||||||
data,
|
data,
|
||||||
info,
|
info,
|
||||||
|
ram: Vec::new(),
|
||||||
};
|
};
|
||||||
println!("MBC {:?}", rom.info.mbc);
|
println!("MBC {:?}", rom.info.mbc);
|
||||||
println!("Region {:?}", rom.info.region);
|
println!("Region {:?}", rom.info.region);
|
||||||
@ -236,6 +293,18 @@ impl ROM for NoMBC {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn write(&mut self, _address: u16, _data: u8) {}
|
fn write(&mut self, _address: u16, _data: u8) {}
|
||||||
|
|
||||||
|
fn ram_mut(&mut self) -> &mut Vec<u8> {
|
||||||
|
&mut self.ram
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ram(&self) -> &Vec<u8> {
|
||||||
|
&self.ram
|
||||||
|
}
|
||||||
|
|
||||||
|
fn info(&self) -> &ROMInfo {
|
||||||
|
&self.info
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct MBC1 {
|
pub struct MBC1 {
|
||||||
@ -254,10 +323,11 @@ impl MBC1 {
|
|||||||
println!("MBC {:?}", info.mbc);
|
println!("MBC {:?}", info.mbc);
|
||||||
println!("Region {:?}", info.region);
|
println!("Region {:?}", info.region);
|
||||||
println!("Has RAM {}", info.has_ram);
|
println!("Has RAM {}", info.has_ram);
|
||||||
|
println!("Has battery {}", info.has_battery);
|
||||||
println!("ROM banks {}", info.rom_banks);
|
println!("ROM banks {}", info.rom_banks);
|
||||||
println!("RAM banks {}", info.ram_banks);
|
println!("RAM banks {}", info.ram_banks);
|
||||||
let ram = vec![0; info.ram_size() as usize];
|
let ram = vec![0; info.ram_size() as usize];
|
||||||
let is_large_rom = info.rom_size() >= 1048576;
|
let is_large_rom = info.rom_banks > 32;
|
||||||
Self {
|
Self {
|
||||||
data,
|
data,
|
||||||
info,
|
info,
|
||||||
@ -278,12 +348,13 @@ impl MBC1 {
|
|||||||
if self.rom_bank > self.info.rom_banks.saturating_sub(1) {
|
if self.rom_bank > self.info.rom_banks.saturating_sub(1) {
|
||||||
self.rom_bank = self.info.rom_banks.saturating_sub(1);
|
self.rom_bank = self.info.rom_banks.saturating_sub(1);
|
||||||
}
|
}
|
||||||
//println!("switched to ROM bank {}", self.rom_bank);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn switch_ram_bank(&mut self, bank: u8) {
|
fn switch_ram_bank(&mut self, bank: u8) {
|
||||||
self.ram_bank = bank & 0b11;
|
self.ram_bank = bank & 0b11;
|
||||||
// println!("switched to RAM bank {}", self.ram_bank);
|
if self.ram_bank > self.info.ram_banks.saturating_sub(1) {
|
||||||
|
self.ram_bank = self.info.ram_banks.saturating_sub(1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_bank_zero_address(&self, address: u16) -> usize {
|
fn get_bank_zero_address(&self, address: u16) -> usize {
|
||||||
@ -292,9 +363,7 @@ impl MBC1 {
|
|||||||
}
|
}
|
||||||
match self.banking_mode {
|
match self.banking_mode {
|
||||||
BankingMode::Simple => address as usize,
|
BankingMode::Simple => address as usize,
|
||||||
BankingMode::Advanced => {
|
BankingMode::Advanced => ((self.ram_bank as usize) << 5) * ((address & 0x3FFF) as usize),
|
||||||
((self.ram_bank as usize) << 5) * ((address & 0x3FFF) as usize)
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -368,6 +437,18 @@ impl ROM for MBC1 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn ram_mut(&mut self) -> &mut Vec<u8> {
|
||||||
|
&mut self.ram
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ram(&self) -> &Vec<u8> {
|
||||||
|
&self.ram
|
||||||
|
}
|
||||||
|
|
||||||
|
fn info(&self) -> &ROMInfo {
|
||||||
|
&self.info
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct MBC2 {
|
pub struct MBC2 {
|
||||||
@ -383,6 +464,7 @@ impl MBC2 {
|
|||||||
println!("MBC {:?}", info.mbc);
|
println!("MBC {:?}", info.mbc);
|
||||||
println!("Region {:?}", info.region);
|
println!("Region {:?}", info.region);
|
||||||
println!("Has RAM {}", info.has_ram);
|
println!("Has RAM {}", info.has_ram);
|
||||||
|
println!("Has battery {}", info.has_battery);
|
||||||
println!("ROM banks {}", info.rom_banks);
|
println!("ROM banks {}", info.rom_banks);
|
||||||
println!("RAM banks {}", info.ram_banks);
|
println!("RAM banks {}", info.ram_banks);
|
||||||
let ram = vec![0; 0x200];
|
let ram = vec![0; 0x200];
|
||||||
@ -444,6 +526,18 @@ impl ROM for MBC2 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn ram_mut(&mut self) -> &mut Vec<u8> {
|
||||||
|
&mut self.ram
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ram(&self) -> &Vec<u8> {
|
||||||
|
&self.ram
|
||||||
|
}
|
||||||
|
|
||||||
|
fn info(&self) -> &ROMInfo {
|
||||||
|
&self.info
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct MBC3 {
|
pub struct MBC3 {
|
||||||
@ -460,6 +554,7 @@ impl MBC3 {
|
|||||||
println!("MBC {:?}", info.mbc);
|
println!("MBC {:?}", info.mbc);
|
||||||
println!("Region {:?}", info.region);
|
println!("Region {:?}", info.region);
|
||||||
println!("Has RAM {}", info.has_ram);
|
println!("Has RAM {}", info.has_ram);
|
||||||
|
println!("Has battery {}", info.has_battery);
|
||||||
println!("ROM banks {}", info.rom_banks);
|
println!("ROM banks {}", info.rom_banks);
|
||||||
println!("RAM banks {}", info.ram_banks);
|
println!("RAM banks {}", info.ram_banks);
|
||||||
let ram = vec![0; info.ram_size() as usize];
|
let ram = vec![0; info.ram_size() as usize];
|
||||||
@ -520,6 +615,18 @@ impl ROM for MBC3 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn ram_mut(&mut self) -> &mut Vec<u8> {
|
||||||
|
&mut self.ram
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ram(&self) -> &Vec<u8> {
|
||||||
|
&self.ram
|
||||||
|
}
|
||||||
|
|
||||||
|
fn info(&self) -> &ROMInfo {
|
||||||
|
&self.info
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct MBC5 {
|
pub struct MBC5 {
|
||||||
@ -536,6 +643,7 @@ impl MBC5 {
|
|||||||
println!("MBC {:?}", info.mbc);
|
println!("MBC {:?}", info.mbc);
|
||||||
println!("Region {:?}", info.region);
|
println!("Region {:?}", info.region);
|
||||||
println!("Has RAM {}", info.has_ram);
|
println!("Has RAM {}", info.has_ram);
|
||||||
|
println!("Has battery {}", info.has_battery);
|
||||||
println!("ROM banks {}", info.rom_banks);
|
println!("ROM banks {}", info.rom_banks);
|
||||||
println!("RAM banks {}", info.ram_banks);
|
println!("RAM banks {}", info.ram_banks);
|
||||||
let ram = vec![0; info.ram_size() as usize];
|
let ram = vec![0; info.ram_size() as usize];
|
||||||
@ -592,4 +700,16 @@ impl ROM for MBC5 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn ram_mut(&mut self) -> &mut Vec<u8> {
|
||||||
|
&mut self.ram
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ram(&self) -> &Vec<u8> {
|
||||||
|
&self.ram
|
||||||
|
}
|
||||||
|
|
||||||
|
fn info(&self) -> &ROMInfo {
|
||||||
|
&self.info
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
47
src/sound.rs
Normal file
47
src/sound.rs
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
use std::ops::RangeInclusive;
|
||||||
|
|
||||||
|
pub const NR10_ADDRESS: u16 = 0xFF10;
|
||||||
|
pub const NR11_ADDRESS: u16 = 0xFF11;
|
||||||
|
pub const NR12_ADDRESS: u16 = 0xFF12;
|
||||||
|
pub const NR13_ADDRESS: u16 = 0xFF13;
|
||||||
|
pub const NR14_ADDRESS: u16 = 0xFF14;
|
||||||
|
|
||||||
|
pub const NR21_ADDRESS: u16 = 0xFF16;
|
||||||
|
pub const NR22_ADDRESS: u16 = 0xFF17;
|
||||||
|
pub const NR23_ADDRESS: u16 = 0xFF18;
|
||||||
|
pub const NR24_ADDRESS: u16 = 0xFF19;
|
||||||
|
|
||||||
|
pub const NR30_ADDRESS: u16 = 0xFF1A;
|
||||||
|
pub const NR31_ADDRESS: u16 = 0xFF1B;
|
||||||
|
pub const NR32_ADDRESS: u16 = 0xFF1C;
|
||||||
|
pub const NR33_ADDRESS: u16 = 0xFF1D;
|
||||||
|
pub const NR34_ADDRESS: u16 = 0xFF1E;
|
||||||
|
|
||||||
|
pub const NR41_ADDRESS: u16 = 0xFF20;
|
||||||
|
pub const NR42_ADDRESS: u16 = 0xFF21;
|
||||||
|
pub const NR43_ADDRESS: u16 = 0xFF22;
|
||||||
|
pub const NR44_ADDRESS: u16 = 0xFF23;
|
||||||
|
|
||||||
|
pub const NR50_ADDRESS: u16 = 0xFF24;
|
||||||
|
pub const NR51_ADDRESS: u16 = 0xFF25;
|
||||||
|
pub const NR52_ADDRESS: u16 = 0xFF26;
|
||||||
|
|
||||||
|
pub const WAVE_PATTERN_RAM: RangeInclusive<u16> = 0xFF30..=0xFF3F;
|
||||||
|
|
||||||
|
pub struct Sound {
|
||||||
|
io_registers: [u8; 48],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sound {
|
||||||
|
pub fn is_io_register(address: u16) -> bool {
|
||||||
|
address >= 0xFF10 && address <= 0xFF3F
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_register(&self, address: u16) -> u8 {
|
||||||
|
self.io_registers[(address - 0xFF10) as usize]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_register(&mut self, address: u16, data: u8) {
|
||||||
|
self.io_registers[(address - 0xFF10) as usize] = data;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user