mirror of
https://github.com/FranLMSP/rmg-001.git
synced 2024-09-20 18:20:50 +00:00
Compare commits
No commits in common. "d90a25e6f671e4eaab3832eb843dc95545378725" and "747a528c2721004f1f87352f1fbb94cb61db2c42" have entirely different histories.
d90a25e6f6
...
747a528c27
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -1171,7 +1171,6 @@ dependencies = [
|
|||||||
"log",
|
"log",
|
||||||
"pixels",
|
"pixels",
|
||||||
"rand",
|
"rand",
|
||||||
"wgpu",
|
|
||||||
"winit",
|
"winit",
|
||||||
"winit_input_helper",
|
"winit_input_helper",
|
||||||
]
|
]
|
||||||
|
@ -14,6 +14,5 @@ rand = "0.8"
|
|||||||
env_logger = "0.9"
|
env_logger = "0.9"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
pixels = "0.7"
|
pixels = "0.7"
|
||||||
wgpu = "0.11"
|
|
||||||
winit = "0.25"
|
winit = "0.25"
|
||||||
winit_input_helper = "0.10"
|
winit_input_helper = "0.10"
|
||||||
|
37
src/bus.rs
37
src/bus.rs
@ -57,17 +57,26 @@ pub struct Bus {
|
|||||||
|
|
||||||
impl Bus {
|
impl Bus {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let args: Vec<String> = std::env::args().collect();
|
let game_rom = match ROM::load_file("/home/fran/Development/Personal/Rust/rmg-001/ignore/mario-land.gb".to_string()) {
|
||||||
if args.len() < 2 {
|
// let game_rom = match ROM::load_file("ignore/dmg-acid2.gb".to_string()) {
|
||||||
println!("Please, specify a ROM file");
|
// let game_rom = match ROM::load_file("ignore/mario-land.gb".to_string()) {
|
||||||
std::process::exit(1);
|
// let game_rom = match ROM::load_file("ignore/tetris.gb".to_string()) {
|
||||||
}
|
// let game_rom = match ROM::load_file("ignore/mooneye/emulator-only/mbc1/bits_bank1.gb".to_string()) {
|
||||||
let game_rom = match ROM::load_file(&args[1]) {
|
// let game_rom = match ROM::load_file("roms/cpu_instrs.gb".to_string()) {
|
||||||
|
// let game_rom = match ROM::load_file("roms/cpu_instrs_individual/01-special.gb".to_string()) {
|
||||||
|
// let game_rom = match ROM::load_file("roms/cpu_instrs_individual/02-interrupts.gb".to_string()) {
|
||||||
|
// let game_rom = match ROM::load_file("roms/cpu_instrs_individual/03-op sp,hl.gb".to_string()) {
|
||||||
|
// let game_rom = match ROM::load_file("roms/cpu_instrs_individual/04-op r,imm.gb".to_string()) {
|
||||||
|
// let game_rom = match ROM::load_file("roms/cpu_instrs_individual/05-op rp.gb".to_string()) {
|
||||||
|
// let game_rom = match ROM::load_file("roms/cpu_instrs_individual/06-ld r,r.gb".to_string()) {
|
||||||
|
// let game_rom = match ROM::load_file("roms/cpu_instrs_individual/07-jr,jp,call,ret,rst.gb".to_string()) {
|
||||||
|
// let game_rom = match ROM::load_file("roms/cpu_instrs_individual/08-misc instrs.gb".to_string()) {
|
||||||
|
// let game_rom = match ROM::load_file("roms/cpu_instrs_individual/09-op r,r.gb".to_string()) {
|
||||||
|
// let game_rom = match ROM::load_file("roms/cpu_instrs_individual/10-bit ops.gb".to_string()) {
|
||||||
|
// let game_rom = match ROM::load_file("roms/cpu_instrs_individual/11-op a,(hl).gb".to_string()) {
|
||||||
Ok(rom) => rom,
|
Ok(rom) => rom,
|
||||||
Err(err) => {
|
// _ => ROM::from_bytes(&[0; 0xFFFF])
|
||||||
println!("Could not read ROM: {}", err);
|
_ => panic!("Could not read ROM"),
|
||||||
std::process::exit(1);
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
let mut data = [0x00; 0x10000];
|
let mut data = [0x00; 0x10000];
|
||||||
// Hardware registers after the bootrom
|
// Hardware registers after the bootrom
|
||||||
@ -138,13 +147,9 @@ impl Bus {
|
|||||||
self.reset_timer = true;
|
self.reset_timer = true;
|
||||||
} else if address == LCD_CONTROL_ADDRESS {
|
} else if address == LCD_CONTROL_ADDRESS {
|
||||||
self.data[address as usize] = data;
|
self.data[address as usize] = data;
|
||||||
// Check if LCD is being turned on or off
|
// Check if LCD is being turned on
|
||||||
if (get_bit(data, BitIndex::I7) && !get_bit(self.data[address as usize], BitIndex::I7)) ||
|
if get_bit(data, BitIndex::I7) && !get_bit(self.data[address as usize], BitIndex::I7) {
|
||||||
!get_bit(data, BitIndex::I7) {
|
|
||||||
self.data[LCD_Y_ADDRESS as usize] = 0x00;
|
self.data[LCD_Y_ADDRESS as usize] = 0x00;
|
||||||
// Set Hblank
|
|
||||||
let byte = self.data[LCD_STATUS_ADDRESS as usize];
|
|
||||||
self.data[LCD_STATUS_ADDRESS as usize] = byte & 0b11111100;
|
|
||||||
}
|
}
|
||||||
} else if address == LCD_Y_ADDRESS {
|
} else if address == LCD_Y_ADDRESS {
|
||||||
// println!("Write to LCD_Y not allowed");
|
// println!("Write to LCD_Y not allowed");
|
||||||
|
@ -857,7 +857,7 @@ impl CPU {
|
|||||||
is_halted: false,
|
is_halted: false,
|
||||||
ei_delay: false,
|
ei_delay: false,
|
||||||
ime: true,
|
ime: true,
|
||||||
enable_logs: !env::var("CPU_LOG").is_err() || !env::var("CPU_LOGS").is_err(),
|
enable_logs: !env::var("CPU_LOG").is_err(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,37 +0,0 @@
|
|||||||
use std::time::Instant;
|
|
||||||
|
|
||||||
pub struct Frames {
|
|
||||||
count: usize,
|
|
||||||
timer: Instant,
|
|
||||||
time_start: u128,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Frames {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
count: 0,
|
|
||||||
timer: Instant::now(),
|
|
||||||
time_start: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn reset_count(&mut self) {
|
|
||||||
self.count = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn reset_timer(&mut self) {
|
|
||||||
self.time_start = self.timer.elapsed().as_millis();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn increment(&mut self) {
|
|
||||||
self.count = self.count.saturating_add(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn elapsed_ms(&self) -> u128 {
|
|
||||||
self.timer.elapsed().as_millis().saturating_sub(self.time_start)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn count(&self) -> usize {
|
|
||||||
self.count
|
|
||||||
}
|
|
||||||
}
|
|
@ -7,4 +7,3 @@ pub mod bus;
|
|||||||
pub mod joypad;
|
pub mod joypad;
|
||||||
pub mod emulator;
|
pub mod emulator;
|
||||||
pub mod render;
|
pub mod render;
|
||||||
pub mod frames;
|
|
||||||
|
@ -23,8 +23,8 @@ pub const DMA_ADDRESS: u16 = 0xFF46;
|
|||||||
pub const BACKGROUND_PALETTE_ADDRESS: u16 = 0xFF47;
|
pub const BACKGROUND_PALETTE_ADDRESS: u16 = 0xFF47;
|
||||||
pub const OBJECT_PALETTE_0_ADDRESS: u16 = 0xFF48;
|
pub const OBJECT_PALETTE_0_ADDRESS: u16 = 0xFF48;
|
||||||
pub const OBJECT_PALETTE_1_ADDRESS: u16 = 0xFF49;
|
pub const OBJECT_PALETTE_1_ADDRESS: u16 = 0xFF49;
|
||||||
pub const WINDOW_Y_ADDRESS: u16 = 0xFF4A;
|
|
||||||
pub const WINDOW_X_ADDRESS: u16 = 0xFF4B;
|
pub const WINDOW_X_ADDRESS: u16 = 0xFF4B;
|
||||||
|
pub const WINDOW_Y_ADDRESS: u16 = 0xFF4A;
|
||||||
pub const TILE_MAP_ADDRESS: u16 = 0x9800;
|
pub const TILE_MAP_ADDRESS: u16 = 0x9800;
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
@ -1,12 +1,11 @@
|
|||||||
use crate::emulator::Emulator;
|
use crate::emulator::Emulator;
|
||||||
use crate::frames::Frames;
|
|
||||||
use crate::cpu::{Cycles};
|
use crate::cpu::{Cycles};
|
||||||
use crate::ppu::{WIDTH, HEIGHT};
|
use crate::ppu::{WIDTH, HEIGHT};
|
||||||
|
|
||||||
use std::{thread, time};
|
use std::{thread, time};
|
||||||
|
|
||||||
use log::error;
|
use log::error;
|
||||||
use pixels::{Pixels, PixelsBuilder, SurfaceTexture};
|
use pixels::{Pixels, SurfaceTexture};
|
||||||
use winit::dpi::LogicalSize;
|
use winit::dpi::LogicalSize;
|
||||||
use winit::event::{Event, VirtualKeyCode, WindowEvent};
|
use winit::event::{Event, VirtualKeyCode, WindowEvent};
|
||||||
use winit::event_loop::{ControlFlow, EventLoop};
|
use winit::event_loop::{ControlFlow, EventLoop};
|
||||||
@ -16,20 +15,7 @@ use winit_input_helper::WinitInputHelper;
|
|||||||
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()
|
Pixels::new(width, height, surface_texture).unwrap()
|
||||||
PixelsBuilder::new(width, height, surface_texture)
|
|
||||||
.device_descriptor(wgpu::DeviceDescriptor {
|
|
||||||
limits: wgpu::Limits {
|
|
||||||
max_storage_textures_per_shader_stage: 4,
|
|
||||||
max_texture_dimension_2d: 4096,
|
|
||||||
max_texture_dimension_1d: 4096,
|
|
||||||
..wgpu::Limits::default()
|
|
||||||
},
|
|
||||||
..wgpu::DeviceDescriptor::default()
|
|
||||||
})
|
|
||||||
.enable_vsync(false)
|
|
||||||
.build()
|
|
||||||
.unwrap()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_window<T>(width: u32, height: u32, title: String, event_loop: &EventLoop<T>) -> Window {
|
pub fn create_window<T>(width: u32, height: u32, title: String, event_loop: &EventLoop<T>) -> Window {
|
||||||
@ -43,9 +29,6 @@ pub fn create_window<T>(width: u32, height: u32, title: String, event_loop: &Eve
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn start_eventloop() {
|
pub fn start_eventloop() {
|
||||||
let mut emulator = Emulator::new();
|
|
||||||
let mut frames = Frames::new();
|
|
||||||
|
|
||||||
env_logger::init();
|
env_logger::init();
|
||||||
let event_loop = EventLoop::new();
|
let event_loop = EventLoop::new();
|
||||||
let mut input = WinitInputHelper::new();
|
let mut input = WinitInputHelper::new();
|
||||||
@ -53,6 +36,8 @@ pub fn start_eventloop() {
|
|||||||
let window = create_window(WIDTH, HEIGHT, "rmg-001".to_string(), &event_loop);
|
let window = create_window(WIDTH, HEIGHT, "rmg-001".to_string(), &event_loop);
|
||||||
let mut pixels = create_pixels(WIDTH, HEIGHT, &window);
|
let mut pixels = create_pixels(WIDTH, HEIGHT, &window);
|
||||||
|
|
||||||
|
let mut emulator = Emulator::new();
|
||||||
|
|
||||||
event_loop.run(move |event, _, control_flow| {
|
event_loop.run(move |event, _, control_flow| {
|
||||||
*control_flow = ControlFlow::Wait;
|
*control_flow = ControlFlow::Wait;
|
||||||
|
|
||||||
@ -82,12 +67,6 @@ pub fn start_eventloop() {
|
|||||||
},
|
},
|
||||||
Event::MainEventsCleared => {
|
Event::MainEventsCleared => {
|
||||||
emulator.run(Cycles(70224), pixels.get_frame());
|
emulator.run(Cycles(70224), pixels.get_frame());
|
||||||
frames.increment();
|
|
||||||
if frames.elapsed_ms() >= 1000 {
|
|
||||||
window.set_title(&format!("rmg-001 (FPS: {})", frames.count()));
|
|
||||||
frames.reset_count();
|
|
||||||
frames.reset_timer();
|
|
||||||
}
|
|
||||||
|
|
||||||
// thread::sleep(time::Duration::from_millis(1));
|
// thread::sleep(time::Duration::from_millis(1));
|
||||||
window.request_redraw();
|
window.request_redraw();
|
||||||
|
41
src/rom.rs
41
src/rom.rs
@ -14,7 +14,6 @@ pub const RAM_SIZE_ADDRESS: u16 = 0x0149;
|
|||||||
pub const ROM_SIZE_ADDRESS: u16 = 0x0148;
|
pub const ROM_SIZE_ADDRESS: u16 = 0x0148;
|
||||||
pub const DESTINATION_CODE_ADDRESS: u16 = 0x014A;
|
pub const DESTINATION_CODE_ADDRESS: u16 = 0x014A;
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
enum Region {
|
enum Region {
|
||||||
Japanese,
|
Japanese,
|
||||||
NonJapanese,
|
NonJapanese,
|
||||||
@ -38,13 +37,11 @@ enum MBC {
|
|||||||
BandaiTIMA5,
|
BandaiTIMA5,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
enum BankingMode {
|
enum BankingMode {
|
||||||
Simple,
|
Simple,
|
||||||
Advanced,
|
Advanced,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct ROMInfo {
|
pub struct ROMInfo {
|
||||||
mbc: MBC,
|
mbc: MBC,
|
||||||
publisher: String,
|
publisher: String,
|
||||||
@ -142,7 +139,7 @@ impl ROMInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn ram_size(&self) -> usize {
|
pub fn ram_size(&self) -> usize {
|
||||||
0x2000 * self.ram_banks as usize
|
0x4000 * self.ram_banks as usize
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -157,17 +154,14 @@ pub struct ROM {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ROM {
|
impl ROM {
|
||||||
pub fn load_file(filename: &str) -> std::io::Result<Self> {
|
pub fn load_file(filename: String) -> std::io::Result<Self> {
|
||||||
let mut file = File::open(filename)?;
|
let mut file = File::open(filename)?;
|
||||||
let mut data = vec![];
|
let mut data = vec![];
|
||||||
file.read_to_end(&mut data)?;
|
file.read_to_end(&mut data)?;
|
||||||
|
|
||||||
let info = ROMInfo::from_bytes(&data);
|
let info = ROMInfo::from_bytes(&data);
|
||||||
println!("MBC {:?}", info.mbc);
|
println!("has ram {}", info.has_ram);
|
||||||
println!("Has RAM {}", info.has_ram);
|
println!("mbc {:?}", info.mbc);
|
||||||
println!("ROM banks {}", info.rom_banks);
|
|
||||||
println!("RAM banks {}", info.ram_banks);
|
|
||||||
println!("Region {:?}", info.region);
|
|
||||||
let ram = Vec::with_capacity(info.ram_size() as usize);
|
let ram = Vec::with_capacity(info.ram_size() as usize);
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
@ -183,12 +177,6 @@ impl ROM {
|
|||||||
|
|
||||||
pub fn read(&self, address: u16) -> u8 {
|
pub fn read(&self, address: u16) -> u8 {
|
||||||
match self.info.mbc {
|
match self.info.mbc {
|
||||||
MBC::NoMBC => {
|
|
||||||
return match self.data.get(address as usize) {
|
|
||||||
Some(data) => *data,
|
|
||||||
None => 0xFF,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
MBC::MBC1 => {
|
MBC::MBC1 => {
|
||||||
if BANK_ZERO.in_range(address) {
|
if BANK_ZERO.in_range(address) {
|
||||||
return self.data[address as usize];
|
return self.data[address as usize];
|
||||||
@ -200,26 +188,19 @@ impl ROM {
|
|||||||
if !self.info.has_ram {
|
if !self.info.has_ram {
|
||||||
return 0xFF;
|
return 0xFF;
|
||||||
}
|
}
|
||||||
return match self.ram.get((address - EXTERNAL_RAM.begin() + (0x2000 * self.ram_bank as u16)) as usize) {
|
return self.ram[(address - EXTERNAL_RAM.begin() + (EXTERNAL_RAM.begin() * self.ram_bank as u16)) as usize];
|
||||||
Some(data) => *data,
|
|
||||||
None => 0xFF,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
unreachable!("ROM read: Address {} not valid", address);
|
|
||||||
},
|
},
|
||||||
_ => unimplemented!(),
|
_ => {},
|
||||||
}
|
}
|
||||||
self.data[address as usize]
|
self.data[address as usize]
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write(&mut self, address: u16, data: u8) {
|
pub fn write(&mut self, address: u16, data: u8) {
|
||||||
match self.info.mbc {
|
match self.info.mbc {
|
||||||
MBC::NoMBC => {},
|
|
||||||
MBC::MBC1 => {
|
MBC::MBC1 => {
|
||||||
|
|
||||||
if address >= 0x0000 && address <= 0x1FFF { // RAM enable register
|
if address >= 0x0000 && address <= 0x1FFF { // RAM enable register
|
||||||
if !self.info.has_ram {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
self.ram_enable = match data & 0x0F {
|
self.ram_enable = match data & 0x0F {
|
||||||
0x0A => true,
|
0x0A => true,
|
||||||
_ => false,
|
_ => false,
|
||||||
@ -237,14 +218,12 @@ impl ROM {
|
|||||||
if !self.ram_enable || !self.info.has_ram {
|
if !self.ram_enable || !self.info.has_ram {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let address = address as usize - EXTERNAL_RAM.begin() as usize + (EXTERNAL_RAM.begin() as usize * self.ram_bank as usize);
|
let address = (address - EXTERNAL_RAM.begin() + (EXTERNAL_RAM.begin() * self.ram_bank as u16)) as usize;
|
||||||
if let Some(elem) = self.ram.get_mut(address) {
|
self.ram[address] = data;
|
||||||
*elem = data;
|
|
||||||
}
|
|
||||||
self.switch_rom_bank(self.rom_bank + (data as u16 >> 5));
|
self.switch_rom_bank(self.rom_bank + (data as u16 >> 5));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
_ => unimplemented!(),
|
_ => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user