save files
This commit is contained in:
parent
f40268666e
commit
e3330a4df7
29
Cargo.lock
generated
29
Cargo.lock
generated
|
@ -252,6 +252,16 @@ dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ctrlc"
|
||||||
|
version = "3.2.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bbcf33c2a618cbe41ee43ae6e9f2e48368cd9f9db2896f10167d8d762679f639"
|
||||||
|
dependencies = [
|
||||||
|
"nix 0.26.2",
|
||||||
|
"windows-sys 0.45.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cty"
|
name = "cty"
|
||||||
version = "0.2.2"
|
version = "0.2.2"
|
||||||
|
@ -417,6 +427,7 @@ dependencies = [
|
||||||
"async-ringbuf",
|
"async-ringbuf",
|
||||||
"clap",
|
"clap",
|
||||||
"cpal",
|
"cpal",
|
||||||
|
"ctrlc",
|
||||||
"futures",
|
"futures",
|
||||||
"gilrs",
|
"gilrs",
|
||||||
"itertools",
|
"itertools",
|
||||||
|
@ -795,6 +806,18 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nix"
|
||||||
|
version = "0.26.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bfdda3d196821d6af13126e40375cdf7da646a96114af134d5f417a9a1dc8e1a"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"static_assertions",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nom"
|
name = "nom"
|
||||||
version = "7.1.3"
|
version = "7.1.3"
|
||||||
|
@ -1237,6 +1260,12 @@ dependencies = [
|
||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "static_assertions"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "strsim"
|
name = "strsim"
|
||||||
version = "0.10.0"
|
version = "0.10.0"
|
||||||
|
|
|
@ -18,3 +18,4 @@ async-ringbuf = "0.1.2"
|
||||||
futures = "0.3.26"
|
futures = "0.3.26"
|
||||||
once_cell = "1.17.1"
|
once_cell = "1.17.1"
|
||||||
itertools = "0.10.5"
|
itertools = "0.10.5"
|
||||||
|
ctrlc = "3.2.5"
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
pub use crate::processor::memory::mmio::joypad::JoypadState;
|
pub use crate::processor::memory::mmio::joypad::JoypadState;
|
||||||
|
|
||||||
|
pub enum EmulatorMessage {
|
||||||
|
Stop,
|
||||||
|
}
|
||||||
|
|
||||||
pub trait Renderer {
|
pub trait Renderer {
|
||||||
fn prepare(&mut self, width: usize, height: usize);
|
fn prepare(&mut self, width: usize, height: usize);
|
||||||
|
|
||||||
|
|
23
src/lib.rs
23
src/lib.rs
|
@ -10,12 +10,16 @@ use crate::{
|
||||||
processor::memory::Memory,
|
processor::memory::Memory,
|
||||||
util::{pause, print_cycles},
|
util::{pause, print_cycles},
|
||||||
};
|
};
|
||||||
use connect::Renderer;
|
use connect::{EmulatorMessage, Renderer};
|
||||||
use once_cell::sync::OnceCell;
|
use once_cell::sync::OnceCell;
|
||||||
use processor::{memory::Rom, Cpu};
|
use processor::{memory::Rom, Cpu};
|
||||||
use std::{
|
use std::{
|
||||||
fs,
|
fs::{self},
|
||||||
io::{stdout, Write},
|
io::{stdout, Write},
|
||||||
|
path::PathBuf,
|
||||||
|
process::exit,
|
||||||
|
str::FromStr,
|
||||||
|
sync::mpsc::Receiver,
|
||||||
};
|
};
|
||||||
use util::pause_then_step;
|
use util::pause_then_step;
|
||||||
|
|
||||||
|
@ -42,14 +46,19 @@ pub const WIDTH: usize = 160;
|
||||||
pub const HEIGHT: usize = 144;
|
pub const HEIGHT: usize = 144;
|
||||||
|
|
||||||
pub fn init(
|
pub fn init(
|
||||||
|
receiver: Receiver<EmulatorMessage>,
|
||||||
options: Options,
|
options: Options,
|
||||||
mut window: Box<dyn Renderer>,
|
mut window: Box<dyn Renderer>,
|
||||||
tile_window: Option<Box<dyn Renderer>>,
|
tile_window: Option<Box<dyn Renderer>>,
|
||||||
) {
|
) {
|
||||||
VERBOSE.set(options.verbose).unwrap();
|
VERBOSE.set(options.verbose).unwrap();
|
||||||
|
|
||||||
|
let maybe_save = PathBuf::from_str(&options.rom_path)
|
||||||
|
.unwrap()
|
||||||
|
.with_extension("sav");
|
||||||
|
|
||||||
let rom: Rom = match fs::read(options.rom_path) {
|
let rom: Rom = match fs::read(options.rom_path) {
|
||||||
Ok(data) => Rom::load(data),
|
Ok(data) => Rom::load(data, maybe_save),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
println!("Error reading ROM: {e}");
|
println!("Error reading ROM: {e}");
|
||||||
return;
|
return;
|
||||||
|
@ -94,6 +103,14 @@ pub fn init(
|
||||||
pause();
|
pause();
|
||||||
},
|
},
|
||||||
None => loop {
|
None => loop {
|
||||||
|
while let Ok(msg) = receiver.try_recv() {
|
||||||
|
match msg {
|
||||||
|
EmulatorMessage::Stop => {
|
||||||
|
cpu.memory.flush_rom();
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
cycle_num += 1;
|
cycle_num += 1;
|
||||||
if options.cycle_count {
|
if options.cycle_count {
|
||||||
print_cycles(&cycle_num);
|
print_cycles(&cycle_num);
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
#![feature(let_chains)]
|
#![feature(let_chains)]
|
||||||
|
|
||||||
|
use std::sync::mpsc::channel;
|
||||||
|
|
||||||
use clap::{ArgGroup, Parser};
|
use clap::{ArgGroup, Parser};
|
||||||
use gb_emu::{
|
use gb_emu::{
|
||||||
connect::{JoypadState, Renderer},
|
connect::{EmulatorMessage, JoypadState, Renderer},
|
||||||
util::scale_buffer,
|
util::scale_buffer,
|
||||||
};
|
};
|
||||||
use gilrs::{
|
use gilrs::{
|
||||||
|
@ -73,7 +75,12 @@ fn main() {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let (sender, receiver) = channel::<EmulatorMessage>();
|
||||||
|
|
||||||
|
ctrlc::set_handler(move || sender.send(EmulatorMessage::Stop).unwrap()).unwrap();
|
||||||
|
|
||||||
gb_emu::init(
|
gb_emu::init(
|
||||||
|
receiver,
|
||||||
options,
|
options,
|
||||||
Box::new(WindowRenderer::new(factor, Some(Gilrs::new().unwrap()))),
|
Box::new(WindowRenderer::new(factor, Some(Gilrs::new().unwrap()))),
|
||||||
tile_window,
|
tile_window,
|
||||||
|
|
|
@ -220,6 +220,10 @@ impl Memory {
|
||||||
self.set(i, if rand::random() { 0xFF } else { 0x00 });
|
self.set(i, if rand::random() { 0xFF } else { 0x00 });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn flush_rom(&mut self) {
|
||||||
|
self.rom.flush();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Cpu {
|
impl Cpu {
|
||||||
|
|
|
@ -1,17 +1,89 @@
|
||||||
use crate::processor::memory::Address;
|
use crate::processor::memory::Address;
|
||||||
use std::str::from_utf8_unchecked;
|
use std::{
|
||||||
|
fs::{File, OpenOptions},
|
||||||
|
io::{Read, Write},
|
||||||
|
path::PathBuf,
|
||||||
|
str::from_utf8_unchecked,
|
||||||
|
};
|
||||||
|
|
||||||
use self::mbcs::{Mbc, Mbc1, Mbc3, Mbc5, None};
|
use self::mbcs::{Mbc, Mbc1, Mbc3, Mbc5, None};
|
||||||
|
|
||||||
mod mbcs;
|
mod mbcs;
|
||||||
|
|
||||||
|
struct MaybeBufferedSram {
|
||||||
|
buf: Vec<u8>,
|
||||||
|
inner: Option<File>,
|
||||||
|
unbuffered_writes: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
const NUM_WRITES_TO_FLUSH: usize = 256;
|
||||||
|
|
||||||
|
impl MaybeBufferedSram {
|
||||||
|
fn new(path: Option<PathBuf>, length: usize) -> Self {
|
||||||
|
let mut buf = vec![0; length];
|
||||||
|
let inner = if let Some(path) = path {
|
||||||
|
if path.exists() {
|
||||||
|
let mut writer = OpenOptions::new()
|
||||||
|
.write(true)
|
||||||
|
.read(true)
|
||||||
|
.open(path)
|
||||||
|
.unwrap();
|
||||||
|
writer.read_exact(&mut buf).unwrap();
|
||||||
|
Some(writer)
|
||||||
|
} else {
|
||||||
|
let writer = OpenOptions::new()
|
||||||
|
.write(true)
|
||||||
|
.create_new(true)
|
||||||
|
.open(path)
|
||||||
|
.unwrap();
|
||||||
|
Some(writer)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
Self {
|
||||||
|
buf,
|
||||||
|
inner,
|
||||||
|
unbuffered_writes: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn len(&self) -> usize {
|
||||||
|
self.buf.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get(&self, addr: usize) -> u8 {
|
||||||
|
self.buf[addr]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set(&mut self, addr: usize, data: u8) {
|
||||||
|
self.unbuffered_writes += 1;
|
||||||
|
self.buf[addr] = data;
|
||||||
|
if self.unbuffered_writes >= NUM_WRITES_TO_FLUSH {
|
||||||
|
self.flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush(&mut self) {
|
||||||
|
if let Some(ref mut writer) = self.inner {
|
||||||
|
writer.write_all(&self.buf).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for MaybeBufferedSram {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
self.flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Rom {
|
pub struct Rom {
|
||||||
title: String,
|
title: String,
|
||||||
mbc: Box<dyn Mbc>,
|
mbc: Box<dyn Mbc>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Rom {
|
impl Rom {
|
||||||
pub fn load(data: Vec<u8>) -> Self {
|
pub fn load(data: Vec<u8>, save_path: PathBuf) -> Self {
|
||||||
let mut title_length = 0x143;
|
let mut title_length = 0x143;
|
||||||
for (i, val) in data.iter().enumerate().take(0x143).skip(0x134) {
|
for (i, val) in data.iter().enumerate().take(0x143).skip(0x134) {
|
||||||
title_length = i;
|
title_length = i;
|
||||||
|
@ -28,36 +100,18 @@ impl Rom {
|
||||||
0x00 => Box::new(None::init(data)),
|
0x00 => Box::new(None::init(data)),
|
||||||
0x01 => Box::new(Mbc1::init(data, rom_size, 0, None)),
|
0x01 => Box::new(Mbc1::init(data, rom_size, 0, None)),
|
||||||
0x02 => Box::new(Mbc1::init(data, rom_size, ram_size, None)),
|
0x02 => Box::new(Mbc1::init(data, rom_size, ram_size, None)),
|
||||||
0x03 => {
|
0x03 => Box::new(Mbc1::init(data, rom_size, ram_size, Some(save_path))),
|
||||||
println!("MBC1 w/battery - battery not implemented!");
|
0x0F => Box::new(Mbc3::init(data, rom_size, 0, true, Some(save_path))),
|
||||||
Box::new(Mbc1::init(data, rom_size, ram_size, None))
|
0x10 => Box::new(Mbc3::init(data, rom_size, ram_size, true, Some(save_path))),
|
||||||
}
|
0x11 => Box::new(Mbc3::init(data, rom_size, 0, false, None)),
|
||||||
0x0F => {
|
0x12 => Box::new(Mbc3::init(data, rom_size, ram_size, false, None)),
|
||||||
println!("MBC3 w/timer + battery - battery not implemented!");
|
0x13 => Box::new(Mbc3::init(data, rom_size, ram_size, false, Some(save_path))),
|
||||||
Box::new(Mbc3::init(data, rom_size, 0, true))
|
0x19 => Box::new(Mbc5::init(data, rom_size, 0, false, None)),
|
||||||
}
|
0x1A => Box::new(Mbc5::init(data, rom_size, ram_size, false, None)),
|
||||||
0x10 => {
|
0x1B => Box::new(Mbc5::init(data, rom_size, ram_size, false, Some(save_path))),
|
||||||
println!("MBC3 w/timer + battery - battery not implemented!");
|
0x1C => Box::new(Mbc5::init(data, rom_size, 0, true, None)),
|
||||||
Box::new(Mbc3::init(data, rom_size, ram_size, true))
|
0x1D => Box::new(Mbc5::init(data, rom_size, ram_size, true, None)),
|
||||||
}
|
0x1E => Box::new(Mbc5::init(data, rom_size, ram_size, true, Some(save_path))),
|
||||||
0x11 => Box::new(Mbc3::init(data, rom_size, 0, false)),
|
|
||||||
0x12 => Box::new(Mbc3::init(data, rom_size, ram_size, false)),
|
|
||||||
0x13 => {
|
|
||||||
println!("MBC3 w/battery - battery not implemented!");
|
|
||||||
Box::new(Mbc3::init(data, rom_size, 0, false))
|
|
||||||
}
|
|
||||||
0x19 => Box::new(Mbc5::init(data, rom_size, 0, false)),
|
|
||||||
0x1A => Box::new(Mbc5::init(data, rom_size, ram_size, false)),
|
|
||||||
0x1B => {
|
|
||||||
println!("MBC5 w/battery - battery not implemented!");
|
|
||||||
Box::new(Mbc5::init(data, rom_size, ram_size, false))
|
|
||||||
}
|
|
||||||
0x1C => Box::new(Mbc5::init(data, rom_size, 0, true)),
|
|
||||||
0x1D => Box::new(Mbc5::init(data, rom_size, ram_size, true)),
|
|
||||||
0x1E => {
|
|
||||||
println!("MBC5 w/rumble + battery - battery not implemented!");
|
|
||||||
Box::new(Mbc5::init(data, rom_size, ram_size, true))
|
|
||||||
}
|
|
||||||
_ => panic!("unimplemented mbc: {:#X}", data[0x147]),
|
_ => panic!("unimplemented mbc: {:#X}", data[0x147]),
|
||||||
};
|
};
|
||||||
Self { title, mbc }
|
Self { title, mbc }
|
||||||
|
@ -94,4 +148,8 @@ impl Rom {
|
||||||
pub fn mbc_type(&self) -> String {
|
pub fn mbc_type(&self) -> String {
|
||||||
self.mbc.mbc_type()
|
self.mbc.mbc_type()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(super) fn flush(&mut self) {
|
||||||
|
self.mbc.flush();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,7 @@ pub(super) trait Mbc {
|
||||||
fn can_rumble(&self) -> bool {
|
fn can_rumble(&self) -> bool {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
fn flush(&mut self) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rom_banks(rom_size: u8) -> usize {
|
fn rom_banks(rom_size: u8) -> usize {
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use super::{ram_size_kb, rom_banks, Mbc, KB, RAM_BANK_SIZE, ROM_BANK_SIZE};
|
use super::{ram_size_kb, rom_banks, Mbc, KB, RAM_BANK_SIZE, ROM_BANK_SIZE};
|
||||||
use crate::processor::memory::Address;
|
use crate::processor::memory::{rom::MaybeBufferedSram, Address};
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
enum BankingMode {
|
enum BankingMode {
|
||||||
|
@ -12,17 +14,17 @@ pub struct Mbc1 {
|
||||||
rom_len: usize,
|
rom_len: usize,
|
||||||
rom_bank: u8,
|
rom_bank: u8,
|
||||||
ram_enabled: bool,
|
ram_enabled: bool,
|
||||||
ram: Option<Vec<u8>>,
|
ram: Option<MaybeBufferedSram>,
|
||||||
ram_bank: u8,
|
ram_bank: u8,
|
||||||
upper_banks: u8,
|
upper_banks: u8,
|
||||||
bank_mode: BankingMode,
|
bank_mode: BankingMode,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Mbc1 {
|
impl Mbc1 {
|
||||||
pub fn init(data: Vec<u8>, rom_size: u8, ram_size: u8, _save_file: Option<Vec<u8>>) -> Self {
|
pub fn init(data: Vec<u8>, rom_size: u8, ram_size: u8, save_file: Option<PathBuf>) -> Self {
|
||||||
let rom_len = rom_banks(rom_size) * ROM_BANK_SIZE;
|
let rom_len = rom_banks(rom_size) * ROM_BANK_SIZE;
|
||||||
// in kb
|
// in kb
|
||||||
let ram = ram_size_kb(ram_size).map(|s| vec![0; s * KB]);
|
let ram = ram_size_kb(ram_size).map(|s| MaybeBufferedSram::new(save_file, s * KB));
|
||||||
Self {
|
Self {
|
||||||
data,
|
data,
|
||||||
rom_len,
|
rom_len,
|
||||||
|
@ -43,8 +45,8 @@ impl Mbc for Mbc1 {
|
||||||
|
|
||||||
fn get_ram(&self, address: Address) -> u8 {
|
fn get_ram(&self, address: Address) -> u8 {
|
||||||
if self.ram_enabled && let Some(ram) = &self.ram {
|
if self.ram_enabled && let Some(ram) = &self.ram {
|
||||||
let addr = self.get_ram_addr(address)%ram.len();
|
let addr = self.get_ram_addr(address) % ram.len();
|
||||||
return ram[addr];
|
return ram.get(addr);
|
||||||
}
|
}
|
||||||
0xFF
|
0xFF
|
||||||
}
|
}
|
||||||
|
@ -53,7 +55,7 @@ impl Mbc for Mbc1 {
|
||||||
let mut addr = self.get_ram_addr(address);
|
let mut addr = self.get_ram_addr(address);
|
||||||
if self.ram_enabled && let Some(ram) = &mut self.ram {
|
if self.ram_enabled && let Some(ram) = &mut self.ram {
|
||||||
addr %= ram.len();
|
addr %= ram.len();
|
||||||
ram[addr] = data;
|
ram.set(addr, data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -94,6 +96,12 @@ impl Mbc for Mbc1 {
|
||||||
format!("{}KB MBC1", self.rom_len / KB)
|
format!("{}KB MBC1", self.rom_len / KB)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn flush(&mut self) {
|
||||||
|
if let Some(ref mut ram) = self.ram {
|
||||||
|
ram.flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Mbc1 {
|
impl Mbc1 {
|
||||||
|
|
|
@ -1,6 +1,12 @@
|
||||||
use super::{ram_size_kb, rom_banks, Mbc, KB, RAM_BANK_SIZE, ROM_BANK_SIZE};
|
use super::{ram_size_kb, rom_banks, Mbc, KB, RAM_BANK_SIZE, ROM_BANK_SIZE};
|
||||||
use crate::{processor::memory::Address, util::set_or_clear_bit};
|
use crate::{
|
||||||
use std::time::{Duration, Instant};
|
processor::memory::{rom::MaybeBufferedSram, Address},
|
||||||
|
util::set_or_clear_bit,
|
||||||
|
};
|
||||||
|
use std::{
|
||||||
|
path::PathBuf,
|
||||||
|
time::{Duration, Instant},
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum RtcRegister {
|
enum RtcRegister {
|
||||||
|
@ -106,7 +112,7 @@ pub struct Mbc3 {
|
||||||
data: Vec<u8>,
|
data: Vec<u8>,
|
||||||
rom_bank: u8,
|
rom_bank: u8,
|
||||||
rom_size: usize,
|
rom_size: usize,
|
||||||
ram: Option<Vec<u8>>,
|
ram: Option<MaybeBufferedSram>,
|
||||||
ram_bank: RamBank,
|
ram_bank: RamBank,
|
||||||
ram_size: usize,
|
ram_size: usize,
|
||||||
ram_enabled: bool,
|
ram_enabled: bool,
|
||||||
|
@ -114,8 +120,14 @@ pub struct Mbc3 {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Mbc3 {
|
impl Mbc3 {
|
||||||
pub fn init(data: Vec<u8>, rom_size: u8, ram_size: u8, rtc: bool) -> Self {
|
pub fn init(
|
||||||
let ram = ram_size_kb(ram_size).map(|s| vec![0; s * KB]);
|
data: Vec<u8>,
|
||||||
|
rom_size: u8,
|
||||||
|
ram_size: u8,
|
||||||
|
rtc: bool,
|
||||||
|
save_file: Option<PathBuf>,
|
||||||
|
) -> Self {
|
||||||
|
let ram = ram_size_kb(ram_size).map(|s| MaybeBufferedSram::new(save_file, s * KB));
|
||||||
Self {
|
Self {
|
||||||
data,
|
data,
|
||||||
rom_bank: 1,
|
rom_bank: 1,
|
||||||
|
@ -154,7 +166,7 @@ impl Mbc for Mbc3 {
|
||||||
match &self.ram_bank {
|
match &self.ram_bank {
|
||||||
RamBank::Ram(ram_bank) => {
|
RamBank::Ram(ram_bank) => {
|
||||||
if let Some(ram) = &self.ram {
|
if let Some(ram) = &self.ram {
|
||||||
return ram[self.get_ram_addr(address, *ram_bank as usize)];
|
return ram.get(self.get_ram_addr(address, *ram_bank as usize));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
RamBank::Rtc(rtc_register) => {
|
RamBank::Rtc(rtc_register) => {
|
||||||
|
@ -216,7 +228,7 @@ impl Mbc for Mbc3 {
|
||||||
RamBank::Ram(ram_bank) => {
|
RamBank::Ram(ram_bank) => {
|
||||||
let real_addr = self.get_ram_addr(address, *ram_bank as usize);
|
let real_addr = self.get_ram_addr(address, *ram_bank as usize);
|
||||||
if let Some(ram) = &mut self.ram {
|
if let Some(ram) = &mut self.ram {
|
||||||
ram[real_addr] = data;
|
ram.set(real_addr, data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
RamBank::Rtc(rtc_register) => {
|
RamBank::Rtc(rtc_register) => {
|
||||||
|
@ -239,4 +251,10 @@ impl Mbc for Mbc3 {
|
||||||
format!("{}KB MBC3", self.rom_size / KB)
|
format!("{}KB MBC3", self.rom_size / KB)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn flush(&mut self) {
|
||||||
|
if let Some(ref mut ram) = self.ram {
|
||||||
|
ram.flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,9 @@
|
||||||
use crate::{processor::memory::Address, util::get_bit};
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
processor::memory::{rom::MaybeBufferedSram, Address},
|
||||||
|
util::get_bit,
|
||||||
|
};
|
||||||
|
|
||||||
use super::{ram_size_kb, rom_banks, Mbc, KB, RAM_BANK_SIZE, ROM_BANK_SIZE};
|
use super::{ram_size_kb, rom_banks, Mbc, KB, RAM_BANK_SIZE, ROM_BANK_SIZE};
|
||||||
|
|
||||||
|
@ -6,7 +11,7 @@ pub struct Mbc5 {
|
||||||
data: Vec<u8>,
|
data: Vec<u8>,
|
||||||
rom_bank: u16,
|
rom_bank: u16,
|
||||||
rom_size: usize,
|
rom_size: usize,
|
||||||
ram: Option<Vec<u8>>,
|
ram: Option<MaybeBufferedSram>,
|
||||||
ram_bank: u8,
|
ram_bank: u8,
|
||||||
ram_size: usize,
|
ram_size: usize,
|
||||||
ram_enabled: bool,
|
ram_enabled: bool,
|
||||||
|
@ -15,8 +20,14 @@ pub struct Mbc5 {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Mbc5 {
|
impl Mbc5 {
|
||||||
pub fn init(data: Vec<u8>, rom_size: u8, ram_size: u8, rumble: bool) -> Self {
|
pub fn init(
|
||||||
let ram = ram_size_kb(ram_size).map(|s| vec![0; s * KB]);
|
data: Vec<u8>,
|
||||||
|
rom_size: u8,
|
||||||
|
ram_size: u8,
|
||||||
|
rumble: bool,
|
||||||
|
save_file: Option<PathBuf>,
|
||||||
|
) -> Self {
|
||||||
|
let ram = ram_size_kb(ram_size).map(|s| MaybeBufferedSram::new(save_file, s * KB));
|
||||||
Self {
|
Self {
|
||||||
data,
|
data,
|
||||||
rom_bank: 1,
|
rom_bank: 1,
|
||||||
|
@ -53,7 +64,7 @@ impl Mbc for Mbc5 {
|
||||||
|
|
||||||
fn get_ram(&self, address: Address) -> u8 {
|
fn get_ram(&self, address: Address) -> u8 {
|
||||||
if self.ram_enabled && let Some(ram) = &self.ram {
|
if self.ram_enabled && let Some(ram) = &self.ram {
|
||||||
ram[self.get_ram_addr(address)]
|
ram.get(self.get_ram_addr(address))
|
||||||
} else {
|
} else {
|
||||||
0xFF
|
0xFF
|
||||||
}
|
}
|
||||||
|
@ -86,7 +97,7 @@ impl Mbc for Mbc5 {
|
||||||
fn set_ram(&mut self, address: Address, data: u8) {
|
fn set_ram(&mut self, address: Address, data: u8) {
|
||||||
let real_addr = self.get_ram_addr(address);
|
let real_addr = self.get_ram_addr(address);
|
||||||
if self.ram_enabled && let Some(ram) = &mut self.ram {
|
if self.ram_enabled && let Some(ram) = &mut self.ram {
|
||||||
ram[real_addr] = data;
|
ram.set(real_addr, data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,4 +121,10 @@ impl Mbc for Mbc5 {
|
||||||
fn can_rumble(&self) -> bool {
|
fn can_rumble(&self) -> bool {
|
||||||
self.rumble
|
self.rumble
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn flush(&mut self) {
|
||||||
|
if let Some(ref mut ram) = self.ram {
|
||||||
|
ram.flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue