Compare commits

...

2 Commits

5 changed files with 80 additions and 62 deletions

View File

@ -59,7 +59,7 @@ pub struct Bus {
impl Bus { impl Bus {
pub fn new() -> Self { pub fn new() -> Self {
let game_rom = match ROM::load_file("ignore/tetris.gb".to_string()) { let game_rom = match ROM::load_file("ignore/m3_scy_change.gb".to_string()) {
// let game_rom = match ROM::load_file("roms/cpu_instrs.gb".to_string()) { // 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/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/02-interrupts.gb".to_string()) {
@ -78,7 +78,7 @@ impl Bus {
}; };
let mut data = [0x00; 0x10000]; let mut data = [0x00; 0x10000];
// Hardware registers after the bootrom // Hardware registers after the bootrom
data[0xFF00] = 0b11001111; data[0xFF00] = 0xCF;
data[0xFF01] = 0x00; data[0xFF01] = 0x00;
data[0xFF02] = 0x7E; data[0xFF02] = 0x7E;
data[0xFF04] = 0x18; data[0xFF04] = 0x18;
@ -139,9 +139,14 @@ impl Bus {
} else if address == LCD_CONTROL_ADDRESS && get_bit(data, BitIndex::I7) { } else if address == LCD_CONTROL_ADDRESS && get_bit(data, BitIndex::I7) {
self.data[address as usize] = data; self.data[address as usize] = data;
self.data[LCD_Y_ADDRESS as usize] = 0x00; self.data[LCD_Y_ADDRESS as usize] = 0x00;
} else if address == LCD_Y_ADDRESS {
// println!("Write to LCD_Y not allowed");
} else if address == LCD_STATUS_ADDRESS {
let byte = self.data[address as usize];
self.data[address as usize] = (data & 0b11111000) | (byte & 0b00000111);
} else if address == JOYPAD_ADDRESS { } else if address == JOYPAD_ADDRESS {
let byte = self.data[JOYPAD_ADDRESS as usize]; let byte = self.data[address as usize];
self.data[JOYPAD_ADDRESS as usize] = (data & 0b11110000) | (byte & 0b00001111); self.data[address as usize] = (data & 0b11110000) | (byte & 0b00001111);
} else { } else {
self.data[address as usize] = data; self.data[address as usize] = data;
} }

View File

@ -96,19 +96,11 @@ impl Emulator {
self.joypad.release(Button::Select); self.joypad.release(Button::Select);
} }
if change { if change {
self.joypad.update(&mut self.bus);
self.bus.set_interrupt_flag(Interrupt::Joypad, true); self.bus.set_interrupt_flag(Interrupt::Joypad, true);
} }
} }
pub fn draw(&mut self, frame: &mut [u8]) { pub fn run(&mut self, cpu_cycles: Cycles, frame_buffer: &mut [u8]) {
let ppu_frame = self.ppu.get_rgba_frame();
for (i, pixel) in frame.chunks_exact_mut(4).enumerate() {
pixel.copy_from_slice(&ppu_frame[i]);
}
}
pub fn run(&mut self, cpu_cycles: Cycles) {
self.cpu.reset_cycles(); self.cpu.reset_cycles();
while self.cpu.get_cycles().0 <= cpu_cycles.0 { while self.cpu.get_cycles().0 <= cpu_cycles.0 {
self.cpu.run(&mut self.bus); self.cpu.run(&mut self.bus);
@ -116,7 +108,7 @@ impl Emulator {
self.bus.reset_timer = false; self.bus.reset_timer = false;
self.timer.reset(&mut self.bus); self.timer.reset(&mut self.bus);
} }
self.ppu.do_cycles(&mut self.bus, self.cpu.get_last_op_cycles()); self.ppu.do_cycles(&mut self.bus, self.cpu.get_last_op_cycles(), frame_buffer);
self.timer.do_cycles(&mut self.bus, self.cpu.get_last_op_cycles()); self.timer.do_cycles(&mut self.bus, self.cpu.get_last_op_cycles());
// 1 CPU cycle = 238.42ns // 1 CPU cycle = 238.42ns
@ -127,13 +119,14 @@ impl Emulator {
pub fn cpu_loop(&mut self) { pub fn cpu_loop(&mut self) {
let mut exit = false; let mut exit = false;
let mut frame: [u8; 144 * 160] = [0; 144 * 160];
while !exit { while !exit {
self.cpu.run(&mut self.bus); self.cpu.run(&mut self.bus);
if self.bus.reset_timer { if self.bus.reset_timer {
self.bus.reset_timer = false; self.bus.reset_timer = false;
self.timer.reset(&mut self.bus); self.timer.reset(&mut self.bus);
} }
self.ppu.do_cycles(&mut self.bus, self.cpu.get_last_op_cycles()); self.ppu.do_cycles(&mut self.bus, self.cpu.get_last_op_cycles(), &mut frame);
self.timer.do_cycles(&mut self.bus, self.cpu.get_last_op_cycles()); self.timer.do_cycles(&mut self.bus, self.cpu.get_last_op_cycles());
// exit = self.cpu.get_exec_calls_count() >= 1258895; // log 1 // exit = self.cpu.get_exec_calls_count() >= 1258895; // log 1

View File

@ -68,7 +68,7 @@ impl Joypad {
}; };
} }
pub fn update(&self, bus: &mut Bus) { pub fn read(&self, bus: &mut Bus) -> u8 {
let byte = bus.read(JOYPAD_ADDRESS); let byte = bus.read(JOYPAD_ADDRESS);
let direction = !get_bit(byte, BitIndex::I4); let direction = !get_bit(byte, BitIndex::I4);
let action = !get_bit(byte, BitIndex::I5); let action = !get_bit(byte, BitIndex::I5);
@ -83,7 +83,6 @@ impl Joypad {
) | ( ) | (
(!((direction && self.right) || (action && self.a)) as u8) (!((direction && self.right) || (action && self.a)) as u8)
); );
println!("New joypad write: {:08b}", data); return data;
bus.force_write(JOYPAD_ADDRESS, data);
} }
} }

View File

@ -91,6 +91,7 @@ pub enum LCDStatus {
} }
pub struct PPU { pub struct PPU {
prev_state: bool,
cycles: Cycles, cycles: Cycles,
rgba_frame: [[u8; 4]; FRAME_BUFFER_LENGTH as usize], rgba_frame: [[u8; 4]; FRAME_BUFFER_LENGTH as usize],
} }
@ -103,6 +104,7 @@ enum TileNumber {
impl PPU { impl PPU {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
prev_state: false,
cycles: Cycles(0), cycles: Cycles(0),
rgba_frame: [[0xFF, 0xFF, 0xFF, 0]; FRAME_BUFFER_LENGTH as usize], rgba_frame: [[0xFF, 0xFF, 0xFF, 0]; FRAME_BUFFER_LENGTH as usize],
} }
@ -116,15 +118,15 @@ impl PPU {
self.cycles.0 += cycles.0; self.cycles.0 += cycles.0;
} }
pub fn do_cycles(&mut self, bus: &mut Bus, cycles: Cycles) { pub fn do_cycles(&mut self, bus: &mut Bus, cycles: Cycles, frame_buffer: &mut [u8]) {
let mut count = 0; let mut count = 0;
while count < cycles.0 { while count < cycles.0 {
self.cycle(bus); self.cycle(bus, frame_buffer);
count += 1; count += 1;
} }
} }
pub fn cycle(&mut self, bus: &mut Bus) { pub fn cycle(&mut self, bus: &mut Bus, frame_buffer: &mut [u8]) {
if !PPU::get_lcd_control(bus, LCDControl::LCDEnable) { if !PPU::get_lcd_control(bus, LCDControl::LCDEnable) {
self.increment_cycles(Cycles(1)); self.increment_cycles(Cycles(1));
return; return;
@ -134,26 +136,21 @@ impl PPU {
if self.cycles.0 == 0 { if self.cycles.0 == 0 {
// Mode 2 OAM scan // Mode 2 OAM scan
PPU::set_lcd_status(bus, LCDStatus::ModeFlag(LCDStatusModeFlag::SearchingOAM), true); PPU::set_lcd_status(bus, LCDStatus::ModeFlag(LCDStatusModeFlag::SearchingOAM), true);
if PPU::get_lcd_status(bus, LCDStatus::Mode2OAMInterrupt) { self.stat_interrupt(bus);
PPU::request_interrupt(bus, Interrupt::LCDSTAT);
}
} else if self.cycles.0 == 80 + 1 { } else if self.cycles.0 == 80 + 1 {
// Mode 3 drawing pixel line. This could also last 289 cycles // Mode 3 drawing pixel line. This could also last 289 cycles
self.draw_line(bus); self.draw_line(bus, frame_buffer);
PPU::set_lcd_status(bus, LCDStatus::ModeFlag(LCDStatusModeFlag::TransferringToLCD), true); PPU::set_lcd_status(bus, LCDStatus::ModeFlag(LCDStatusModeFlag::TransferringToLCD), true);
} else if self.cycles.0 == 80 + 172 + 1 { } else if self.cycles.0 == 80 + 172 + 1 {
// Mode 0 Horizontal blank. This could last 87 or 204 cycles depending on the mode 3 // Mode 0 Horizontal blank. This could last 87 or 204 cycles depending on the mode 3
PPU::set_lcd_status(bus, LCDStatus::ModeFlag(LCDStatusModeFlag::HBlank), true); PPU::set_lcd_status(bus, LCDStatus::ModeFlag(LCDStatusModeFlag::HBlank), true);
if PPU::get_lcd_status(bus, LCDStatus::Mode0HBlankInterrupt) { self.stat_interrupt(bus);
PPU::request_interrupt(bus, Interrupt::LCDSTAT);
}
} }
} else if PPU::get_lcd_y(bus) == 144 && self.cycles.0 == 0 { } else if PPU::get_lcd_y(bus) == 144 && self.cycles.0 == 0 {
// Mode 1 Vertical blank // Mode 1 Vertical blank
bus.set_interrupt_flag(Interrupt::VBlank, true);
PPU::set_lcd_status(bus, LCDStatus::ModeFlag(LCDStatusModeFlag::VBlank), true); PPU::set_lcd_status(bus, LCDStatus::ModeFlag(LCDStatusModeFlag::VBlank), true);
if PPU::get_lcd_status(bus, LCDStatus::Mode1VBlankInterrupt) { self.stat_interrupt(bus);
PPU::request_interrupt(bus, Interrupt::VBlank);
}
} }
self.increment_cycles(Cycles(1)); self.increment_cycles(Cycles(1));
@ -167,21 +164,28 @@ impl PPU {
if PPU::get_lcd_y(bus) > 153 { if PPU::get_lcd_y(bus) > 153 {
PPU::set_lcd_y(bus, 0); PPU::set_lcd_y(bus, 0);
} }
PPU::check_lyc(bus); self.check_lyc(bus);
} }
} }
fn check_lyc(bus: &mut Bus) { fn stat_interrupt(&mut self, bus: &mut Bus) {
let state = self.prev_state;
self.prev_state = (
(PPU::get_lcd_status(bus, LCDStatus::Mode2OAMInterrupt) && PPU::get_lcd_status(bus, LCDStatus::ModeFlag(LCDStatusModeFlag::SearchingOAM))) ||
(PPU::get_lcd_status(bus, LCDStatus::Mode0HBlankInterrupt) && PPU::get_lcd_status(bus, LCDStatus::ModeFlag(LCDStatusModeFlag::HBlank))) ||
(PPU::get_lcd_status(bus, LCDStatus::Mode1VBlankInterrupt) && PPU::get_lcd_status(bus, LCDStatus::ModeFlag(LCDStatusModeFlag::VBlank)))
);
if self.prev_state && !state {
bus.set_interrupt_flag(Interrupt::LCDSTAT, true);
}
}
fn check_lyc(&mut self, bus: &mut Bus) {
let lyc_compare = PPU::get_lcd_y(bus) == bus.read(LCD_Y_COMPARE_ADDRESS); let lyc_compare = PPU::get_lcd_y(bus) == bus.read(LCD_Y_COMPARE_ADDRESS);
if PPU::get_lcd_status(bus, LCDStatus::LYCInterrupt) && lyc_compare {
PPU::set_lcd_status(bus, LCDStatus::LYCFlag, lyc_compare); PPU::set_lcd_status(bus, LCDStatus::LYCFlag, lyc_compare);
PPU::request_interrupt(bus, Interrupt::LCDSTAT); if lyc_compare && PPU::get_lcd_status(bus, LCDStatus::LYCInterrupt) {
} bus.set_interrupt_flag(Interrupt::LCDSTAT, true);
} self.prev_state = true;
fn request_interrupt(bus: &mut Bus, interrupt: Interrupt) {
if PPU::get_lcd_control(bus, LCDControl::LCDEnable) {
bus.set_interrupt_flag(interrupt, true);
} }
} }
@ -190,7 +194,7 @@ impl PPU {
} }
fn set_lcd_y(bus: &mut Bus, val: u8) { fn set_lcd_y(bus: &mut Bus, val: u8) {
bus.write(LCD_Y_ADDRESS, val); bus.force_write(LCD_Y_ADDRESS, val);
} }
fn get_scroll_x(bus: &Bus) -> u8 { fn get_scroll_x(bus: &Bus) -> u8 {
@ -216,7 +220,7 @@ impl PPU {
fn set_lcd_control(bus: &mut Bus, control: LCDControl, val: bool) { fn set_lcd_control(bus: &mut Bus, control: LCDControl, val: bool) {
let mut byte = bus.read(LCD_CONTROL_ADDRESS); let mut byte = bus.read(LCD_CONTROL_ADDRESS);
bus.write(LCD_CONTROL_ADDRESS, control.set(byte, val)); bus.force_write(LCD_CONTROL_ADDRESS, control.set(byte, val));
} }
pub fn get_lcd_status(bus: &Bus, status: LCDStatus) -> bool { pub fn get_lcd_status(bus: &Bus, status: LCDStatus) -> bool {
@ -251,7 +255,7 @@ impl PPU {
LCDStatusModeFlag::TransferringToLCD => (byte & 0b11111100) | 3, LCDStatusModeFlag::TransferringToLCD => (byte & 0b11111100) | 3,
}, },
}; };
bus.write(LCD_STATUS_ADDRESS, byte); bus.force_write(LCD_STATUS_ADDRESS, byte);
} }
fn get_tile_bytes(x: u8, y: u8, tile_number_type: TileNumber, default_method: bool, bus: &Bus) -> (u8, u8) { fn get_tile_bytes(x: u8, y: u8, tile_number_type: TileNumber, default_method: bool, bus: &Bus) -> (u8, u8) {
@ -277,15 +281,15 @@ impl PPU {
fn get_window_pixel(lcd_x: u8, bus: &Bus) -> Option<Pixel> { fn get_window_pixel(lcd_x: u8, bus: &Bus) -> Option<Pixel> {
let lcd_y = PPU::get_lcd_y(bus); let lcd_y = PPU::get_lcd_y(bus);
let window_x = (PPU::get_window_x(bus) as i8 - 7) as u8; let window_x = PPU::get_window_x(bus);
let window_y = PPU::get_window_y(bus); let window_y = PPU::get_window_y(bus);
if !PPU::get_lcd_control(bus, LCDControl::WindowEnable) || window_x != lcd_x || window_y != lcd_y { if !PPU::get_lcd_control(bus, LCDControl::WindowEnable) || lcd_x < (window_x.saturating_sub(7)) || window_y != lcd_y {
return None; return None;
} }
let x = lcd_x - window_x; let x = lcd_x.wrapping_sub(window_x.saturating_sub(7));
let y = lcd_y - window_x; let y = lcd_y.wrapping_sub(window_y);
let default_mode = PPU::get_lcd_control(bus, LCDControl::TileAddressMode); let default_mode = PPU::get_lcd_control(bus, LCDControl::TileAddressMode);
let tilemap_area = match PPU::get_lcd_control(bus, LCDControl::WindowTileMapAddress) { let tilemap_area = match PPU::get_lcd_control(bus, LCDControl::WindowTileMapAddress) {
@ -300,7 +304,7 @@ impl PPU {
Some(pixels[(x as usize).rem_euclid(8)]) Some(pixels[(x as usize).rem_euclid(8)])
} }
fn draw_line(&mut self, bus: &Bus) { fn draw_line(&mut self, bus: &Bus, frame_buffer: &mut [u8]) {
let palette = bus.read(BACKGROUND_PALETTE_ADDRESS); let palette = bus.read(BACKGROUND_PALETTE_ADDRESS);
let lcd_y = PPU::get_lcd_y(bus); let lcd_y = PPU::get_lcd_y(bus);
if lcd_y as u32 >= LCD_HEIGHT { if lcd_y as u32 >= LCD_HEIGHT {
@ -320,15 +324,36 @@ impl PPU {
let bg_pixels = PPU::get_byte_pixels(tile_byte_1, tile_byte_2, palette); let bg_pixels = PPU::get_byte_pixels(tile_byte_1, tile_byte_2, palette);
for pixel in bg_pixels { let idx = (lcd_x as usize + (lcd_y as usize * LCD_WIDTH as usize)) * 4;
let idx = lcd_x as usize + (lcd_y as usize * LCD_WIDTH as usize); let pixel = bg_pixels[x.rem_euclid(8) as usize];
self.rgba_frame[idx] = PPU::get_rgba(pixel); let rgba = PPU::get_rgba(pixel);
frame_buffer[idx] = rgba[0];
frame_buffer[idx + 1] = rgba[1];
frame_buffer[idx + 2] = rgba[2];
if let Some(window_pixel) = PPU::get_window_pixel(lcd_x, bus) { if let Some(window_pixel) = PPU::get_window_pixel(lcd_x, bus) {
self.rgba_frame[idx] = PPU::get_rgba(window_pixel); let rgba = PPU::get_rgba(pixel);
frame_buffer[idx] = rgba[0];
frame_buffer[idx + 1] = rgba[1];
frame_buffer[idx + 2] = rgba[2];
} }
lcd_x += 1; lcd_x += 1;
/* for pixel in bg_pixels {
let idx = (lcd_x as usize + (lcd_y as usize * LCD_WIDTH as usize)) * 4;
let rgba = PPU::get_rgba(pixel);
frame_buffer[idx] = rgba[0];
frame_buffer[idx + 1] = rgba[1];
frame_buffer[idx + 2] = rgba[2];
if let Some(window_pixel) = PPU::get_window_pixel(lcd_x, bus) {
let rgba = PPU::get_rgba(pixel);
frame_buffer[idx] = rgba[0];
frame_buffer[idx + 1] = rgba[1];
frame_buffer[idx + 2] = rgba[2];
} }
lcd_x += 1;
} */
} }
} }
@ -373,8 +398,4 @@ impl PPU {
PPU::get_pixel(PPU::get_palette((byte1 & 0b01) | ((byte2 << 1) & 0b10), palette)), PPU::get_pixel(PPU::get_palette((byte1 & 0b01) | ((byte2 << 1) & 0b10), palette)),
] ]
} }
pub fn get_rgba_frame(&self) -> &[[u8; 4]; FRAME_BUFFER_LENGTH as usize] {
&self.rgba_frame
}
} }

View File

@ -64,10 +64,10 @@ pub fn start_eventloop() {
*control_flow = ControlFlow::Exit *control_flow = ControlFlow::Exit
}, },
Event::MainEventsCleared => { Event::MainEventsCleared => {
emulator.run(Cycles(70224)); emulator.run(Cycles(70224), pixels.get_frame());
emulator.draw(pixels.get_frame()); // emulator.draw(pixels.get_frame());
// thread::sleep(time::Duration::from_millis(14)); // thread::sleep(time::Duration::from_millis(1000));
window.request_redraw(); window.request_redraw();
}, },
Event::RedrawRequested(_) => { Event::RedrawRequested(_) => {