Speed enhancements; Write savefiles

This commit is contained in:
Kevin Hamacher 2016-06-02 13:22:35 +02:00
parent be4f9da380
commit 023b9ff0e4
4 changed files with 154 additions and 85 deletions

View File

@ -1,5 +1,8 @@
use super::interconnect; use super::interconnect;
use std::time::{Duration, Instant};
use std::thread::sleep;
const REG_A: usize = 6; const REG_A: usize = 6;
const REG_N_B: usize = 0; const REG_N_B: usize = 0;
@ -46,7 +49,7 @@ impl CPU {
ip: 0, ip: 0,
sp: 0xFFFE, sp: 0xFFFE,
interconnect: interconnect, interconnect: interconnect,
ime: false, // Is this correct? ime: false,
debug: false, debug: false,
halted: false, halted: false,
} }
@ -861,39 +864,58 @@ impl CPU {
self.halted = false; self.halted = false;
} }
pub fn run_instruction(&mut self) { pub fn run(&mut self) {
// self.debug = !self.interconnect.is_boot_rom(); loop {
/* let mut cycles: i32 = 0;
if self.ip == 0x553 { let start = Instant::now();
self.debug = true; for _ in 0 .. 1000 {
cycles += self.run_instruction() as i32;
} }
*/
// Check for interrupts. let gb_dur = cycles * 238;
// TODO: Make this right let our_dur = start.elapsed().subsec_nanos() as i32;
if self.ime { let delta = gb_dur - our_dur;
if delta > (20 * 238) { // We're at least 20 cycles faster.
sleep(Duration::new(0, delta as u32));
} else if delta < 0 {
print!("-");
}
}
}
fn check_interrupts(&mut self) {
// read pending interrupts // read pending interrupts
let pending = self.interconnect.read_byte(0xFF0F); let pending = self.interconnect.read_byte(0xFF0F);
let enabled = self.interconnect.read_byte(0xFFFF); let enabled = self.interconnect.read_byte(0xFFFF);
let e_pending = pending & enabled; let e_pending = pending & enabled;
if e_pending & interconnect::INTERRUPT_DISPLAY_VBLANK > 0 { if e_pending & interconnect::INTERRUPT_DISPLAY_VBLANK > 0 {
// println!("Handling vblank interrupt");
self.handle_interrupt(0x40, interconnect::INTERRUPT_DISPLAY_VBLANK); self.handle_interrupt(0x40, interconnect::INTERRUPT_DISPLAY_VBLANK);
} else if e_pending & interconnect::INTERRUPT_DISPLAY_STAT > 0 { } else if e_pending & interconnect::INTERRUPT_DISPLAY_STAT > 0 {
// println!("Handling display stat interrupt");
self.handle_interrupt(0x48, interconnect::INTERRUPT_DISPLAY_STAT); self.handle_interrupt(0x48, interconnect::INTERRUPT_DISPLAY_STAT);
} else if e_pending & interconnect::INTERRUPT_TIMER_OVERFLOW > 0{ } else if e_pending & interconnect::INTERRUPT_TIMER_OVERFLOW > 0 {
println!("Handling timer interrupt");
self.handle_interrupt(0x50, interconnect::INTERRUPT_TIMER_OVERFLOW); self.handle_interrupt(0x50, interconnect::INTERRUPT_TIMER_OVERFLOW);
} else if e_pending & interconnect::INTERRUPT_SERIAL > 0 {
self.handle_interrupt(0x58, interconnect::INTERRUPT_SERIAL);
} else if e_pending & interconnect::INTERRUPT_INPUT > 0 {
self.handle_interrupt(0x60, interconnect::INTERRUPT_INPUT);
} else if e_pending > 0 { } else if e_pending > 0 {
panic!("Unknown pending interrupt: {:02X}", e_pending); panic!("Unknown pending interrupt: {:02X}", e_pending);
} }
} }
pub fn run_instruction(&mut self) -> u8 {
// self.debug = !self.interconnect.is_boot_rom();
// Check for interrupts.
if self.ime {
self.check_interrupts();
}
let mut cycles: u8 = 1; let mut cycles: u8 = 1;
let instruction: u8;
if !self.halted { if !self.halted {
// We need to double-check the flags // We need to double-check the flags
let instruction = self.read_byte(self.ip); instruction = self.read_byte(self.ip);
if self.debug { if self.debug {
print!("{:#06x}: [SP: {:#04X}] i={:02X}. ", &self.ip, &self.sp, &instruction); print!("{:#06x}: [SP: {:#04X}] i={:02X}. ", &self.ip, &self.sp, &instruction);
for i in 0 .. 6 { for i in 0 .. 6 {
@ -1647,5 +1669,6 @@ impl CPU {
}; };
} }
self.interconnect.tick(cycles); self.interconnect.tick(cycles);
cycles
} }
} }

View File

@ -45,12 +45,20 @@ const SPRITE_PALETTE_NO: u8 = 1 << 4; // NonCGB only
// const SPRITE_TILE_VRAM_BANK: u8 = 1 << 3; // CGB only // const SPRITE_TILE_VRAM_BANK: u8 = 1 << 3; // CGB only
// Display color // Display color
/*
const MONOCHROME_PALETTE: &'static [[f64; 3]; 4] = &[ const MONOCHROME_PALETTE: &'static [[f64; 3]; 4] = &[
[0.605, 0.734, 0.059], [0.605, 0.734, 0.059],
[0.543, 0.672, 0.059], [0.543, 0.672, 0.059],
[0.188, 0.383, 0.188], [0.188, 0.383, 0.188],
[0.059, 0.219, 0.059], [0.059, 0.219, 0.059],
]; ];
*/
const MONOCHROME_PALETTE: &'static [[u8; 3]; 4] = &[
[255, 255, 255],
[200, 200, 200],
[125, 125, 12],
[50, 50, 50],
];
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
enum PixelOrigin { enum PixelOrigin {
@ -150,6 +158,9 @@ pub struct Display {
stat_interrupt: bool, stat_interrupt: bool,
pixels: [Pixel; GB_PIXELS_X * GB_PIXELS_Y], pixels: [Pixel; GB_PIXELS_X * GB_PIXELS_Y],
frameskip: u8,
frame_no: u8,
} }
impl Display { impl Display {
@ -185,6 +196,8 @@ impl Display {
vblank_interrupt: false, vblank_interrupt: false,
stat_interrupt: false, stat_interrupt: false,
pixels: pixels, pixels: pixels,
frameskip: 0,
frame_no: 0,
} }
} }
@ -328,7 +341,9 @@ impl Display {
if self.current_ticks > TICKS_END_READMODE { if self.current_ticks > TICKS_END_READMODE {
self.current_ticks = 0; self.current_ticks = 0;
self.current_mode = DisplayMode::HBlank; self.current_mode = DisplayMode::HBlank;
if self.frameskip < self.frame_no {
self.renderscan(); self.renderscan();
}
if self.status & STAT_MODE_HBLANK_INT > 0 { if self.status & STAT_MODE_HBLANK_INT > 0 {
self.stat_interrupt = true; self.stat_interrupt = true;
} }
@ -365,10 +380,15 @@ impl Display {
} }
if self.curline > 153 { if self.curline > 153 {
self.current_mode = DisplayMode::ReadOAMMemory; // Mode 2, scanline. self.current_mode = DisplayMode::ReadOAMMemory; // Mode 2, scanline.
if self.frameskip < self.frame_no {
self.render_screen(); self.render_screen();
self.renderer.present(); self.renderer.present();
self.renderer.set_draw_color(sdl2::pixels::Color::RGB(255, 255, 255)); self.renderer.set_draw_color(sdl2::pixels::Color::RGB(255, 255, 255));
self.renderer.clear(); self.renderer.clear();
self.frame_no = 0;
} else {
self.frame_no += 1;
}
self.curline = 0; self.curline = 0;
if self.status & STAT_MODE_OAM_INT > 0 { if self.status & STAT_MODE_OAM_INT > 0 {
self.stat_interrupt = true; self.stat_interrupt = true;
@ -423,10 +443,11 @@ impl Display {
let tile_offset_y: usize = render_y as usize - y_o as usize; let tile_offset_y: usize = render_y as usize - y_o as usize;
let tile_base_addr: usize = sprite.tile as usize * 16; // Should this be twice as wide in wide mode? let tile_base_addr: usize = sprite.tile as usize * 16; // Should this be twice as wide in wide mode?
let tile_line_1 = self.vram[tile_base_addr + tile_offset_y * 2 + 1]; let tile_addr = tile_base_addr + tile_offset_y * 2;
let tile_line_2 = self.vram[tile_base_addr + tile_offset_y * 2 + 1]; let tile_line_1 = self.vram[tile_addr + 0];
let tile_line_3 = self.vram[tile_base_addr + tile_offset_y * 2 + 2]; let tile_line_2 = self.vram[tile_addr + 1];
let tile_line_4 = self.vram[tile_base_addr + tile_offset_y * 2 + 3]; let tile_line_3 = self.vram[tile_addr + 2];
let tile_line_4 = self.vram[tile_addr + 3];
// We need to draw this. // We need to draw this.
let wide_mode = self.control & CTRL_BG_SPRITE_SIZE > 0; let wide_mode = self.control & CTRL_BG_SPRITE_SIZE > 0;
@ -488,7 +509,7 @@ impl Display {
let entry = MONOCHROME_PALETTE[lookup[c as usize] as usize]; let entry = MONOCHROME_PALETTE[lookup[c as usize] as usize];
// Draw stuff. We're currently only in monochrome mode // Draw stuff. We're currently only in monochrome mode
self.set_pixel(x.wrapping_add(x_o), render_y, sdl2::pixels::Color::RGB((entry[0]*255.0) as u8, (entry[1]*255.0) as u8, (entry[2]*255.0) as u8), PixelOrigin::Sprite); self.set_pixel(x.wrapping_add(x_o), render_y, sdl2::pixels::Color::RGB(entry[0], entry[1], entry[2]), PixelOrigin::Sprite);
} }
} }
} }
@ -499,11 +520,11 @@ impl Display {
fn get_bg_window_tile_addr(&self, tile_id: u8) -> usize { fn get_bg_window_tile_addr(&self, tile_id: u8) -> usize {
if (self.control & CTRL_BG_WINDOW_TILE_DATA_SELECT) == CTRL_BG_WINDOW_TILE_DATA_SELECT { if (self.control & CTRL_BG_WINDOW_TILE_DATA_SELECT) == CTRL_BG_WINDOW_TILE_DATA_SELECT {
let base_addr = 0x0000; let base_addr = 0x0000;
base_addr + (tile_id as usize) * 16 base_addr + ((tile_id as usize) << 4)
} else { } else {
let base_addr = 0x0800; let base_addr = 0x0800;
let tile_id = (128u8 as i8).wrapping_add(tile_id as i8) as u8; let tile_id = (128u8 as i8).wrapping_add(tile_id as i8) as u8;
base_addr + (tile_id as usize) * 16 base_addr + ((tile_id as usize) << 4)
} }
} }
@ -570,19 +591,20 @@ impl Display {
let tile_index_y: u8 = render_abs_y >> 3; let tile_index_y: u8 = render_abs_y >> 3;
let tile_offset_y: u8 = render_abs_y & 7; let tile_offset_y: u8 = render_abs_y & 7;
let vram_offset: usize = background_map + (tile_index_y as usize) * 32 + tile_index_x as usize; let vram_offset: usize = background_map + ((tile_index_y as usize) * 32) + tile_index_x as usize;
// Obtain tile ID in this area // Obtain tile ID in this area
let tile_id = self.vram[vram_offset]; let tile_id = self.vram[vram_offset];
// Obtain tile information // Obtain tile information
let tile_base_addr: usize = self.get_bg_window_tile_addr(tile_id); let tile_base_addr: usize = self.get_bg_window_tile_addr(tile_id);
let tile_line_1 = self.vram[tile_base_addr + (tile_offset_y as usize) * 2]; let addr = tile_base_addr + (tile_offset_y as usize) * 2;
let tile_line_2 = self.vram[tile_base_addr + (tile_offset_y as usize) * 2 + 1]; let tile_line_1 = self.vram[addr];
let tile_line_2 = self.vram[addr + 1];
// Get the correct bit // Get the correct bit
let b1: bool = (tile_line_1 & 1 << (7 - tile_offset_x)) > 0; let b1: bool = (tile_line_1 & 1 << (7 - tile_offset_x)) != 0;
let b2: bool = (tile_line_2 & 1 << (7 - tile_offset_x)) > 0; let b2: bool = (tile_line_2 & 1 << (7 - tile_offset_x)) != 0;
// Lookup the color // Lookup the color
let c = (b1 as u8) * 2 + b2 as u8; let c = (b1 as u8) * 2 + b2 as u8;
@ -598,11 +620,7 @@ impl Display {
0 => PixelOrigin::Empty, // Hack so that objects will be in front of it. 0 => PixelOrigin::Empty, // Hack so that objects will be in front of it.
_ => PixelOrigin::Background, _ => PixelOrigin::Background,
}; };
self.set_pixel(render_x, render_y, sdl2::pixels::Color::RGB((entry[0]*255.0) as u8, (entry[1]*255.0) as u8, (entry[2]*255.0) as u8), origin); self.set_pixel(render_x, render_y, sdl2::pixels::Color::RGB(entry[0], entry[1], entry[2]), origin);
}
if (self.control & CTRL_BG_SPRITE_ENABLE) == CTRL_BG_SPRITE_ENABLE {
self.render_sprites(&queue);
} }
} }
@ -628,8 +646,9 @@ impl Display {
// Obtain tile information // Obtain tile information
let tile_base_addr: usize = self.get_bg_window_tile_addr(tile_id); let tile_base_addr: usize = self.get_bg_window_tile_addr(tile_id);
let tile_line_1 = self.vram[tile_base_addr + (tile_offset_y as usize) * 2]; let tile_addr = tile_base_addr + (tile_offset_y as usize) * 2;
let tile_line_2 = self.vram[tile_base_addr + (tile_offset_y as usize) * 2 + 1]; let tile_line_1 = self.vram[tile_addr];
let tile_line_2 = self.vram[tile_addr + 1];
// Get the correct bit // Get the correct bit
let b1: bool = (tile_line_1 & 1 << (7 - tile_offset_x)) > 0; let b1: bool = (tile_line_1 & 1 << (7 - tile_offset_x)) > 0;
@ -649,9 +668,12 @@ impl Display {
0 => PixelOrigin::Empty, // Hack so that objects will be in front of it. 0 => PixelOrigin::Empty, // Hack so that objects will be in front of it.
_ => PixelOrigin::Window, _ => PixelOrigin::Window,
}; };
self.set_pixel(render_x, render_y, sdl2::pixels::Color::RGB((entry[0]*255.0) as u8, (entry[1]*255.0) as u8, (entry[2]*255.0) as u8), origin); self.set_pixel(render_x, render_y, sdl2::pixels::Color::RGB(entry[0], entry[1], entry[2]), origin);
} }
} }
} }
if (self.control & CTRL_BG_SPRITE_ENABLE) == CTRL_BG_SPRITE_ENABLE {
self.render_sprites(&queue);
}
} }
} }

View File

@ -2,6 +2,8 @@
const WRAM_SIZE: usize = 0x8000; const WRAM_SIZE: usize = 0x8000;
const HIRAM_SIZE: usize = (0xFFFE - 0xFF80) + 1; const HIRAM_SIZE: usize = (0xFFFE - 0xFF80) + 1;
pub const INTERRUPT_INPUT: u8 = 1 << 4;
pub const INTERRUPT_SERIAL: u8 = 1 << 3;
pub const INTERRUPT_TIMER_OVERFLOW: u8 = 1 << 2; pub const INTERRUPT_TIMER_OVERFLOW: u8 = 1 << 2;
pub const INTERRUPT_DISPLAY_STAT: u8 = 1 << 1; pub const INTERRUPT_DISPLAY_STAT: u8 = 1 << 1;
pub const INTERRUPT_DISPLAY_VBLANK: u8 = 1 << 0; pub const INTERRUPT_DISPLAY_VBLANK: u8 = 1 << 0;
@ -92,11 +94,13 @@ impl Interconnect {
joy_regular_keys: 0x0F, joy_regular_keys: 0x0F,
joy_special_keys: 0x0F, joy_special_keys: 0x0F,
joy_switch: 0x30, joy_switch: 0x30,
cycles: 0,
} }
} }
fn press_key(&mut self, key: Key) { fn press_key(&mut self, key: Key) {
println!("Press key {:?}", &key); // println!("Press key {:?}", &key);
match key { match key {
Key::UP => self.joy_regular_keys &= !KEY_UP, Key::UP => self.joy_regular_keys &= !KEY_UP,
Key::DOWN => self.joy_regular_keys &= !KEY_DOWN, Key::DOWN => self.joy_regular_keys &= !KEY_DOWN,
@ -107,10 +111,11 @@ impl Interconnect {
Key::A => self.joy_special_keys &= !KEY_A, Key::A => self.joy_special_keys &= !KEY_A,
Key::B => self.joy_special_keys &= !KEY_B, Key::B => self.joy_special_keys &= !KEY_B,
} }
self.interrupt_request_flags |= INTERRUPT_INPUT;
} }
fn release_key(&mut self, key: Key) { fn release_key(&mut self, key: Key) {
println!("Release key {:?}", &key); // println!("Release key {:?}", &key);
match key { match key {
Key::UP => self.joy_regular_keys |= KEY_UP, Key::UP => self.joy_regular_keys |= KEY_UP,
Key::DOWN => self.joy_regular_keys |= KEY_DOWN, Key::DOWN => self.joy_regular_keys |= KEY_DOWN,
@ -121,6 +126,7 @@ impl Interconnect {
Key::A => self.joy_special_keys |= KEY_A, Key::A => self.joy_special_keys |= KEY_A,
Key::B => self.joy_special_keys |= KEY_B, Key::B => self.joy_special_keys |= KEY_B,
} }
self.interrupt_request_flags |= INTERRUPT_INPUT;
} }
// Somehow we need different timers for this. // Somehow we need different timers for this.
@ -140,11 +146,17 @@ impl Interconnect {
self.interrupt_request_flags |= INTERRUPT_TIMER_OVERFLOW; self.interrupt_request_flags |= INTERRUPT_TIMER_OVERFLOW;
} }
if self.serial.serial_interrupt() {
self.interrupt_request_flags |= INTERRUPT_SERIAL;
}
// Make sure the window is responsive: // Make sure the window is responsive:
if self.cycles > 500 {
loop { loop {
if let Some(event) = self.display.event_pump.poll_event(){ if let Some(event) = self.display.event_pump.poll_event(){
match event { match event {
Event::Quit {..} | Event::KeyDown { keycode: Some(Keycode::Escape), .. } => { Event::Quit {..} | Event::KeyDown { keycode: Some(Keycode::Escape), .. } => {
self.cartridge.save();
panic!("TODO: Proper shutdown"); panic!("TODO: Proper shutdown");
}, },
Event::KeyDown { keycode: Some(Keycode::Left), .. } => self.press_key(Key::LEFT), Event::KeyDown { keycode: Some(Keycode::Left), .. } => self.press_key(Key::LEFT),
@ -170,6 +182,10 @@ impl Interconnect {
break; break;
} }
} }
self.cycles = 0;
} else {
self.cycles += cycles as u16;
}
} }
#[inline] #[inline]

View File

@ -11,7 +11,10 @@ impl Serial {
pub fn write_byte(&mut self, addr: u16, val: u8) { pub fn write_byte(&mut self, addr: u16, val: u8) {
match addr { match addr {
0xFF01 => self.data = val, 0xFF01 => {
println!("Serial: Write {:02X}", val);
self.data = val;
}
0xFF02 => self.control = val, 0xFF02 => self.control = val,
_ => panic!("Serial: Write {:02X} to {:04X} unsupported", val, addr), _ => panic!("Serial: Write {:02X} to {:04X} unsupported", val, addr),
} }
@ -24,4 +27,9 @@ impl Serial {
_ => panic!("Serial: Read from {:04X} unsupported", addr), _ => panic!("Serial: Read from {:04X} unsupported", addr),
} }
} }
pub fn serial_interrupt(&self) -> bool {
// TODO
false
}
} }