mirror of
https://github.com/FranLMSP/rmg-001.git
synced 2024-11-23 10:12:11 +00:00
Refactor ppu and vram (currently crashing)
This commit is contained in:
parent
de021c2eff
commit
7fa6857ac3
23
src/bus.rs
23
src/bus.rs
@ -8,6 +8,7 @@ use crate::utils::{
|
|||||||
};
|
};
|
||||||
use crate::rom::ROM;
|
use crate::rom::ROM;
|
||||||
use crate::ppu::{
|
use crate::ppu::{
|
||||||
|
PPU,
|
||||||
LCD_STATUS_ADDRESS,
|
LCD_STATUS_ADDRESS,
|
||||||
LCD_CONTROL_ADDRESS,
|
LCD_CONTROL_ADDRESS,
|
||||||
LCD_Y_ADDRESS,
|
LCD_Y_ADDRESS,
|
||||||
@ -35,12 +36,13 @@ pub const INTERRUPT_FLAG_ADDRESS: u16 = 0xFF0F;
|
|||||||
pub struct Bus {
|
pub struct Bus {
|
||||||
game_rom: ROM,
|
game_rom: ROM,
|
||||||
data: [u8; 0x10000],
|
data: [u8; 0x10000],
|
||||||
|
ppu: Rc<RefCell<PPU>>,
|
||||||
joypad: Rc<RefCell<Joypad>>,
|
joypad: Rc<RefCell<Joypad>>,
|
||||||
timer: Rc<RefCell<Timer>>,
|
timer: Rc<RefCell<Timer>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Bus {
|
impl Bus {
|
||||||
pub fn new(joypad: Rc<RefCell<Joypad>>, timer: Rc<RefCell<Timer>>) -> Self {
|
pub fn new(ppu: Rc<RefCell<PPU>>, joypad: Rc<RefCell<Joypad>>, timer: Rc<RefCell<Timer>>) -> Self {
|
||||||
let args: Vec<String> = std::env::args().collect();
|
let args: Vec<String> = std::env::args().collect();
|
||||||
if args.len() < 2 {
|
if args.len() < 2 {
|
||||||
println!("Please, specify a ROM file");
|
println!("Please, specify a ROM file");
|
||||||
@ -80,6 +82,7 @@ impl Bus {
|
|||||||
Self {
|
Self {
|
||||||
data,
|
data,
|
||||||
game_rom,
|
game_rom,
|
||||||
|
ppu,
|
||||||
joypad,
|
joypad,
|
||||||
timer,
|
timer,
|
||||||
}
|
}
|
||||||
@ -90,6 +93,10 @@ impl Bus {
|
|||||||
return self.game_rom.read(address);
|
return self.game_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) {
|
||||||
|
return self.ppu.borrow().read_vram(address);
|
||||||
|
} else if SPRITE_ATTRIBUTE_TABLE.contains(&address) {
|
||||||
|
return self.ppu.borrow().read_oam(address);
|
||||||
} else if address == JOYPAD_ADDRESS {
|
} else if address == JOYPAD_ADDRESS {
|
||||||
return self.joypad.borrow().read(self.data[address as usize]);
|
return self.joypad.borrow().read(self.data[address as usize]);
|
||||||
} else if address == TIMER_DIVIDER_REGISTER_ADDRESS {
|
} else if address == TIMER_DIVIDER_REGISTER_ADDRESS {
|
||||||
@ -140,14 +147,18 @@ impl Bus {
|
|||||||
} else if address == JOYPAD_ADDRESS {
|
} else if address == JOYPAD_ADDRESS {
|
||||||
let byte = self.data[address as usize];
|
let byte = self.data[address as usize];
|
||||||
self.data[address as usize] = (data & 0b11110000) | (byte & 0b00001111);
|
self.data[address as usize] = (data & 0b11110000) | (byte & 0b00001111);
|
||||||
|
} else if VIDEO_RAM.contains(&address) {
|
||||||
|
return self.ppu.borrow_mut().write_vram(address, data);
|
||||||
|
} else if SPRITE_ATTRIBUTE_TABLE.contains(&address) {
|
||||||
|
return self.ppu.borrow_mut().write_oam(address, data);
|
||||||
} else if address == DMA_ADDRESS {
|
} else if address == DMA_ADDRESS {
|
||||||
// the idea is: when something gets written to $FF46, multiply it by 0x100, then copy 160 bytes starting from that memory location into OAM
|
|
||||||
self.data[address as usize] = data;
|
self.data[address as usize] = data;
|
||||||
let source = (data as usize) * 0x100;
|
let source = (data as u16) * 0x100;
|
||||||
let mut count = 0;
|
let mut count: u16 = 0;
|
||||||
let oam_addr = SPRITE_ATTRIBUTE_TABLE.min().unwrap() as usize;
|
let oam_addr = SPRITE_ATTRIBUTE_TABLE.min().unwrap();
|
||||||
|
let mut ppu = self.ppu.borrow_mut();
|
||||||
while count < 160 {
|
while count < 160 {
|
||||||
self.data[oam_addr + count] = self.data[source + count];
|
ppu.write_oam(oam_addr + count, self.data[(source + count) as usize]);
|
||||||
count += 1;
|
count += 1;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -11,9 +11,9 @@ use crate::timer::Timer;
|
|||||||
use crate::joypad::{Joypad, Button};
|
use crate::joypad::{Joypad, Button};
|
||||||
|
|
||||||
pub struct Emulator {
|
pub struct Emulator {
|
||||||
cpu: CPU,
|
|
||||||
ppu: PPU,
|
|
||||||
bus: Bus,
|
bus: Bus,
|
||||||
|
cpu: CPU,
|
||||||
|
ppu: Rc<RefCell<PPU>>,
|
||||||
timer: Rc<RefCell<Timer>>,
|
timer: Rc<RefCell<Timer>>,
|
||||||
joypad: Rc<RefCell<Joypad>>,
|
joypad: Rc<RefCell<Joypad>>,
|
||||||
}
|
}
|
||||||
@ -21,11 +21,12 @@ pub struct Emulator {
|
|||||||
impl Emulator {
|
impl Emulator {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let joypad = Rc::new(RefCell::new(Joypad::new()));
|
let joypad = Rc::new(RefCell::new(Joypad::new()));
|
||||||
let timer = Rc::new(RefCell::new(Timer::new()));
|
let timer = Rc::new(RefCell::new(Timer::new()));
|
||||||
|
let ppu = Rc::new(RefCell::new(PPU::new()));
|
||||||
Self {
|
Self {
|
||||||
|
bus: Bus::new(Rc::clone(&ppu), Rc::clone(&joypad), Rc::clone(&timer)),
|
||||||
cpu: CPU::new(),
|
cpu: CPU::new(),
|
||||||
ppu: PPU::new(),
|
ppu,
|
||||||
bus: Bus::new(Rc::clone(&joypad), Rc::clone(&timer)),
|
|
||||||
timer,
|
timer,
|
||||||
joypad,
|
joypad,
|
||||||
}
|
}
|
||||||
@ -106,10 +107,13 @@ impl Emulator {
|
|||||||
|
|
||||||
pub fn run(&mut self, cpu_cycles: Cycles, frame_buffer: &mut [u8]) {
|
pub fn run(&mut self, cpu_cycles: Cycles, frame_buffer: &mut [u8]) {
|
||||||
self.cpu.reset_cycles();
|
self.cpu.reset_cycles();
|
||||||
|
let mut ppu = self.ppu.borrow_mut();
|
||||||
|
let mut timer = self.timer.borrow_mut();
|
||||||
while self.cpu.get_cycles().to_t().0 <= cpu_cycles.0 {
|
while self.cpu.get_cycles().to_t().0 <= cpu_cycles.0 {
|
||||||
self.cpu.run(&mut self.bus);
|
self.cpu.run(&mut self.bus);
|
||||||
self.ppu.do_cycles(&mut self.bus, self.cpu.get_last_op_cycles().to_t(), frame_buffer);
|
let cycles = self.cpu.get_last_op_cycles().to_t();
|
||||||
self.timer.borrow_mut().do_cycles(&mut self.bus, self.cpu.get_last_op_cycles().to_t());
|
ppu.do_cycles(&mut self.bus, cycles, frame_buffer);
|
||||||
|
timer.do_cycles(&mut self.bus, cycles);
|
||||||
|
|
||||||
// 1 CPU cycle = 238.42ns
|
// 1 CPU cycle = 238.42ns
|
||||||
// thread::sleep(time::Duration::from_nanos((self.cpu.get_last_op_cycles().0 * 238).try_into().unwrap()));
|
// thread::sleep(time::Duration::from_nanos((self.cpu.get_last_op_cycles().0 * 238).try_into().unwrap()));
|
||||||
@ -122,7 +126,7 @@ impl Emulator {
|
|||||||
let mut frame: [u8; 144 * 160 * 4] = [0; 144 * 160 * 4];
|
let mut frame: [u8; 144 * 160 * 4] = [0; 144 * 160 * 4];
|
||||||
while !exit {
|
while !exit {
|
||||||
self.cpu.run(&mut self.bus);
|
self.cpu.run(&mut self.bus);
|
||||||
self.ppu.do_cycles(&mut self.bus, self.cpu.get_last_op_cycles().to_t(), &mut frame);
|
self.ppu.borrow_mut().do_cycles(&mut self.bus, self.cpu.get_last_op_cycles().to_t(), &mut frame);
|
||||||
self.timer.borrow_mut().do_cycles(&mut self.bus, self.cpu.get_last_op_cycles().to_t());
|
self.timer.borrow_mut().do_cycles(&mut self.bus, self.cpu.get_last_op_cycles().to_t());
|
||||||
|
|
||||||
// exit = self.cpu.get_exec_calls_count() >= 1258895; // log 1
|
// exit = self.cpu.get_exec_calls_count() >= 1258895; // log 1
|
||||||
|
62
src/ppu.rs
62
src/ppu.rs
@ -3,7 +3,7 @@ use crate::utils::{
|
|||||||
get_bit,
|
get_bit,
|
||||||
set_bit,
|
set_bit,
|
||||||
};
|
};
|
||||||
use crate::bus::{Bus, SPRITE_ATTRIBUTE_TABLE};
|
use crate::bus::{Bus, VIDEO_RAM, SPRITE_ATTRIBUTE_TABLE};
|
||||||
use crate::cpu::{Cycles, Interrupt};
|
use crate::cpu::{Cycles, Interrupt};
|
||||||
|
|
||||||
pub const LCD_WIDTH: u32 = 160;
|
pub const LCD_WIDTH: u32 = 160;
|
||||||
@ -142,7 +142,7 @@ impl Sprite {
|
|||||||
self.x
|
self.x
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_pixel(&mut self, lcd_x: u8, lcd_y: u8, bus: &Bus, last_bg_index: u8) -> Option<(Pixel, bool)> {
|
pub fn get_pixel(&mut self, lcd_x: u8, lcd_y: u8, vram: &[u8], last_bg_index: u8) -> Option<(Pixel, bool)> {
|
||||||
if lcd_x < self.x.saturating_sub(8) || lcd_x >= self.x {
|
if lcd_x < self.x.saturating_sub(8) || lcd_x >= self.x {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
@ -184,8 +184,8 @@ impl Sprite {
|
|||||||
let tile_line = y.rem_euclid(height) * 2;
|
let tile_line = y.rem_euclid(height) * 2;
|
||||||
let addr = 0x8000 + (tile_number as u16 * 16) + tile_line as u16;
|
let addr = 0x8000 + (tile_number as u16 * 16) + tile_line as u16;
|
||||||
|
|
||||||
let tile_byte_1 = bus.read(addr);
|
let tile_byte_1 = vram[addr as usize - 0x2000];
|
||||||
let tile_byte_2 = bus.read(addr + 1);
|
let tile_byte_2 = vram[addr as usize - 0x2000 + 1];
|
||||||
let bit_pixels_array = PPU::get_byte_pixels(tile_byte_1, tile_byte_2);
|
let bit_pixels_array = PPU::get_byte_pixels(tile_byte_1, tile_byte_2);
|
||||||
self.bit_pixels = Some(bit_pixels_array);
|
self.bit_pixels = Some(bit_pixels_array);
|
||||||
|
|
||||||
@ -216,6 +216,8 @@ pub struct PPU {
|
|||||||
scroll_y: u8,
|
scroll_y: u8,
|
||||||
window_x: u8,
|
window_x: u8,
|
||||||
window_y: u8,
|
window_y: u8,
|
||||||
|
vram: [u8; 0x2000],
|
||||||
|
oam: [u8; 0xA0],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PPU {
|
impl PPU {
|
||||||
@ -235,9 +237,27 @@ impl PPU {
|
|||||||
scroll_y: 0,
|
scroll_y: 0,
|
||||||
window_x: 0,
|
window_x: 0,
|
||||||
window_y: 0,
|
window_y: 0,
|
||||||
|
vram: [0; 0x2000],
|
||||||
|
oam: [0; 0xA0],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn read_vram(&self, address: u16) -> u8 {
|
||||||
|
self.vram[(address - VIDEO_RAM.min().unwrap()) as usize]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_vram(&mut self, address: u16, data: u8) {
|
||||||
|
self.vram[(address - VIDEO_RAM.min().unwrap()) as usize] = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_oam(&self, address: u16) -> u8 {
|
||||||
|
self.oam[(address - SPRITE_ATTRIBUTE_TABLE.min().unwrap()) as usize]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_oam(&mut self, address: u16, data: u8) {
|
||||||
|
self.oam[(address - SPRITE_ATTRIBUTE_TABLE.min().unwrap()) as usize] = data;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn reset_cycles(&mut self) {
|
pub fn reset_cycles(&mut self) {
|
||||||
self.cycles.0 = 0;
|
self.cycles.0 = 0;
|
||||||
}
|
}
|
||||||
@ -294,7 +314,6 @@ impl PPU {
|
|||||||
self.window_y_counter = 0;
|
self.window_y_counter = 0;
|
||||||
}
|
}
|
||||||
bus.force_write(LCD_Y_ADDRESS, self.lcd_y);
|
bus.force_write(LCD_Y_ADDRESS, self.lcd_y);
|
||||||
// self.check_lyc(bus);
|
|
||||||
self.stat_interrupt(bus);
|
self.stat_interrupt(bus);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -323,15 +342,6 @@ impl PPU {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_lyc(&mut self, bus: &mut Bus) {
|
|
||||||
let lyc_compare = self.lcd_y == bus.read(LCD_Y_COMPARE_ADDRESS);
|
|
||||||
PPU::set_lcd_status(bus, LCDStatus::LYCFlag, lyc_compare);
|
|
||||||
if !self.state && lyc_compare && PPU::get_lcd_status(bus, LCDStatus::LYCInterrupt) {
|
|
||||||
bus.set_interrupt_flag(Interrupt::LCDSTAT, true);
|
|
||||||
self.state = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn oam_search(&mut self, bus: &Bus) {
|
fn oam_search(&mut self, bus: &Bus) {
|
||||||
if !self.get_lcd_control(bus, LCDControl::ObjectEnable) {
|
if !self.get_lcd_control(bus, LCDControl::ObjectEnable) {
|
||||||
return;
|
return;
|
||||||
@ -348,8 +358,8 @@ impl PPU {
|
|||||||
// todo!("Make a setting for the 10 sprites per scanline");
|
// todo!("Make a setting for the 10 sprites per scanline");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
let y = bus.read(addr);
|
let y = self.read_oam(addr);
|
||||||
let x = bus.read(addr + 1);
|
let x = self.read_oam(addr + 1);
|
||||||
|
|
||||||
if x == 0 {
|
if x == 0 {
|
||||||
addr += 4;
|
addr += 4;
|
||||||
@ -369,8 +379,8 @@ impl PPU {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
let tile_number = bus.read(addr + 2);
|
let tile_number = self.read_oam(addr + 2);
|
||||||
let attributes = bus.read(addr + 3);
|
let attributes = self.read_oam(addr + 3);
|
||||||
|
|
||||||
let palette_zero = !get_bit(attributes, BitIndex::I4);
|
let palette_zero = !get_bit(attributes, BitIndex::I4);
|
||||||
self.sprite_buffer.push(Sprite {
|
self.sprite_buffer.push(Sprite {
|
||||||
@ -394,10 +404,10 @@ impl PPU {
|
|||||||
self.sprite_buffer.sort_by(|a, b| a.x().cmp(&b.x()));
|
self.sprite_buffer.sort_by(|a, b| a.x().cmp(&b.x()));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_sprite_pixel(&mut self, lcd_x: u8, bus: &Bus) -> Option<(Pixel, bool)> {
|
fn find_sprite_pixel(&mut self, lcd_x: u8) -> Option<(Pixel, bool)> {
|
||||||
let lcd_y = self.lcd_y;
|
let lcd_y = self.lcd_y;
|
||||||
for sprite in &mut self.sprite_buffer {
|
for sprite in &mut self.sprite_buffer {
|
||||||
if let Some(pixel) = sprite.get_pixel(lcd_x, lcd_y, bus, self.last_bg_index) {
|
if let Some(pixel) = sprite.get_pixel(lcd_x, lcd_y, &self.vram, self.last_bg_index) {
|
||||||
return Some(pixel);
|
return Some(pixel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -452,12 +462,12 @@ impl PPU {
|
|||||||
bus.force_write(LCD_STATUS_ADDRESS, byte);
|
bus.force_write(LCD_STATUS_ADDRESS, byte);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_tile_bytes(x: u8, y: u8, tilemap_area: u16, default_method: bool, bus: &Bus) -> (u8, u8) {
|
fn get_tile_bytes(&self, x: u8, y: u8, tilemap_area: u16, default_method: bool) -> (u8, u8) {
|
||||||
let index_x = x as u16 / 8;
|
let index_x = x as u16 / 8;
|
||||||
let index_y = (y as u16 / 8) * 32;
|
let index_y = (y as u16 / 8) * 32;
|
||||||
let index = index_x + index_y;
|
let index = index_x + index_y;
|
||||||
let tile_line = (y).rem_euclid(8) * 2;
|
let tile_line = (y).rem_euclid(8) * 2;
|
||||||
let tile_number = bus.read(tilemap_area + index as u16) as u16;
|
let tile_number = self.read_vram(tilemap_area + index as u16) as u16;
|
||||||
let addr = if default_method {
|
let addr = if default_method {
|
||||||
0x8000 + tile_line as u16 + (tile_number * 16)
|
0x8000 + tile_line as u16 + (tile_number * 16)
|
||||||
} else {
|
} else {
|
||||||
@ -467,7 +477,7 @@ impl PPU {
|
|||||||
(base + tile_line + (tile_number * 16)) as u16
|
(base + tile_line + (tile_number * 16)) as u16
|
||||||
};
|
};
|
||||||
|
|
||||||
(bus.read(addr), bus.read(addr + 1))
|
(self.read_vram(addr), self.read_vram(addr + 1))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_window_pixel(&mut self, lcd_x: u8, bus: &Bus) -> Option<Pixel> {
|
fn get_window_pixel(&mut self, lcd_x: u8, bus: &Bus) -> Option<Pixel> {
|
||||||
@ -506,7 +516,7 @@ impl PPU {
|
|||||||
true => 0x9C00,
|
true => 0x9C00,
|
||||||
false => 0x9800,
|
false => 0x9800,
|
||||||
};
|
};
|
||||||
let (tile_byte_1, tile_byte_2) = PPU::get_tile_bytes(x, y, tilemap_area, default_mode, bus);
|
let (tile_byte_1, tile_byte_2) = self.get_tile_bytes(x, y, tilemap_area, default_mode);
|
||||||
let bit_pixels_array = PPU::get_byte_pixels(tile_byte_1, tile_byte_2);
|
let bit_pixels_array = PPU::get_byte_pixels(tile_byte_1, tile_byte_2);
|
||||||
self.current_window_pixels = Some(bit_pixels_array);
|
self.current_window_pixels = Some(bit_pixels_array);
|
||||||
|
|
||||||
@ -542,7 +552,7 @@ impl PPU {
|
|||||||
true => 0x9C00,
|
true => 0x9C00,
|
||||||
false => 0x9800,
|
false => 0x9800,
|
||||||
};
|
};
|
||||||
let (tile_byte_1, tile_byte_2) = PPU::get_tile_bytes(x, y, tilemap_area, default_mode, bus);
|
let (tile_byte_1, tile_byte_2) = self.get_tile_bytes(x, y, tilemap_area, default_mode);
|
||||||
let bit_pixels_array = PPU::get_byte_pixels(tile_byte_1, tile_byte_2);
|
let bit_pixels_array = PPU::get_byte_pixels(tile_byte_1, tile_byte_2);
|
||||||
self.current_background_pixels = Some(bit_pixels_array);
|
self.current_background_pixels = Some(bit_pixels_array);
|
||||||
|
|
||||||
@ -581,7 +591,7 @@ impl PPU {
|
|||||||
frame_buffer[idx + 2] = rgba[2];
|
frame_buffer[idx + 2] = rgba[2];
|
||||||
}
|
}
|
||||||
if self.get_lcd_control(bus, LCDControl::ObjectEnable) {
|
if self.get_lcd_control(bus, LCDControl::ObjectEnable) {
|
||||||
if let Some((sprite_pixel, palette_zero)) = self.find_sprite_pixel(lcd_x, bus) {
|
if let Some((sprite_pixel, palette_zero)) = self.find_sprite_pixel(lcd_x) {
|
||||||
let rgba = PPU::get_rgba(sprite_pixel, match palette_zero {
|
let rgba = PPU::get_rgba(sprite_pixel, match palette_zero {
|
||||||
true => SPRITE_0_COLORS,
|
true => SPRITE_0_COLORS,
|
||||||
false => SPRITE_1_COLORS,
|
false => SPRITE_1_COLORS,
|
||||||
|
Loading…
Reference in New Issue
Block a user