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 std::time::{Duration, Instant};
use std::thread::sleep;
const REG_A: usize = 6;
const REG_N_B: usize = 0;
@ -46,7 +49,7 @@ impl CPU {
ip: 0,
sp: 0xFFFE,
interconnect: interconnect,
ime: false, // Is this correct?
ime: false,
debug: false,
halted: false,
}
@ -861,39 +864,58 @@ impl CPU {
self.halted = false;
}
pub fn run_instruction(&mut self) {
// self.debug = !self.interconnect.is_boot_rom();
/*
if self.ip == 0x553 {
self.debug = true;
}
*/
// Check for interrupts.
// TODO: Make this right
if self.ime {
// read pending interrupts
let pending = self.interconnect.read_byte(0xFF0F);
let enabled = self.interconnect.read_byte(0xFFFF);
let e_pending = pending & enabled;
if e_pending & interconnect::INTERRUPT_DISPLAY_VBLANK > 0 {
// println!("Handling vblank interrupt");
self.handle_interrupt(0x40, interconnect::INTERRUPT_DISPLAY_VBLANK);
} else if e_pending & interconnect::INTERRUPT_DISPLAY_STAT > 0 {
// println!("Handling display stat interrupt");
self.handle_interrupt(0x48, interconnect::INTERRUPT_DISPLAY_STAT);
} else if e_pending & interconnect::INTERRUPT_TIMER_OVERFLOW > 0{
println!("Handling timer interrupt");
self.handle_interrupt(0x50, interconnect::INTERRUPT_TIMER_OVERFLOW);
} else if e_pending > 0 {
panic!("Unknown pending interrupt: {:02X}", e_pending);
pub fn run(&mut self) {
loop {
let mut cycles: i32 = 0;
let start = Instant::now();
for _ in 0 .. 1000 {
cycles += self.run_instruction() as i32;
}
let gb_dur = cycles * 238;
let our_dur = start.elapsed().subsec_nanos() as i32;
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
let pending = self.interconnect.read_byte(0xFF0F);
let enabled = self.interconnect.read_byte(0xFFFF);
let e_pending = pending & enabled;
if e_pending & interconnect::INTERRUPT_DISPLAY_VBLANK > 0 {
self.handle_interrupt(0x40, interconnect::INTERRUPT_DISPLAY_VBLANK);
} else if e_pending & interconnect::INTERRUPT_DISPLAY_STAT > 0 {
self.handle_interrupt(0x48, interconnect::INTERRUPT_DISPLAY_STAT);
} else if e_pending & interconnect::INTERRUPT_TIMER_OVERFLOW > 0 {
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 {
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 instruction: u8;
if !self.halted {
// We need to double-check the flags
let instruction = self.read_byte(self.ip);
instruction = self.read_byte(self.ip);
if self.debug {
print!("{:#06x}: [SP: {:#04X}] i={:02X}. ", &self.ip, &self.sp, &instruction);
for i in 0 .. 6 {
@ -1647,5 +1669,6 @@ impl CPU {
};
}
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
// Display color
/*
const MONOCHROME_PALETTE: &'static [[f64; 3]; 4] = &[
[0.605, 0.734, 0.059],
[0.543, 0.672, 0.059],
[0.188, 0.383, 0.188],
[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)]
enum PixelOrigin {
@ -150,6 +158,9 @@ pub struct Display {
stat_interrupt: bool,
pixels: [Pixel; GB_PIXELS_X * GB_PIXELS_Y],
frameskip: u8,
frame_no: u8,
}
impl Display {
@ -185,6 +196,8 @@ impl Display {
vblank_interrupt: false,
stat_interrupt: false,
pixels: pixels,
frameskip: 0,
frame_no: 0,
}
}
@ -328,7 +341,9 @@ impl Display {
if self.current_ticks > TICKS_END_READMODE {
self.current_ticks = 0;
self.current_mode = DisplayMode::HBlank;
self.renderscan();
if self.frameskip < self.frame_no {
self.renderscan();
}
if self.status & STAT_MODE_HBLANK_INT > 0 {
self.stat_interrupt = true;
}
@ -365,10 +380,15 @@ impl Display {
}
if self.curline > 153 {
self.current_mode = DisplayMode::ReadOAMMemory; // Mode 2, scanline.
self.render_screen();
self.renderer.present();
self.renderer.set_draw_color(sdl2::pixels::Color::RGB(255, 255, 255));
self.renderer.clear();
if self.frameskip < self.frame_no {
self.render_screen();
self.renderer.present();
self.renderer.set_draw_color(sdl2::pixels::Color::RGB(255, 255, 255));
self.renderer.clear();
self.frame_no = 0;
} else {
self.frame_no += 1;
}
self.curline = 0;
if self.status & STAT_MODE_OAM_INT > 0 {
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_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_line_2 = self.vram[tile_base_addr + tile_offset_y * 2 + 1];
let tile_line_3 = self.vram[tile_base_addr + tile_offset_y * 2 + 2];
let tile_line_4 = self.vram[tile_base_addr + tile_offset_y * 2 + 3];
let tile_addr = tile_base_addr + tile_offset_y * 2;
let tile_line_1 = self.vram[tile_addr + 0];
let tile_line_2 = self.vram[tile_addr + 1];
let tile_line_3 = self.vram[tile_addr + 2];
let tile_line_4 = self.vram[tile_addr + 3];
// We need to draw this.
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];
// 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 {
if (self.control & CTRL_BG_WINDOW_TILE_DATA_SELECT) == CTRL_BG_WINDOW_TILE_DATA_SELECT {
let base_addr = 0x0000;
base_addr + (tile_id as usize) * 16
base_addr + ((tile_id as usize) << 4)
} else {
let base_addr = 0x0800;
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_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
let tile_id = self.vram[vram_offset];
// Obtain tile information
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_line_2 = self.vram[tile_base_addr + (tile_offset_y as usize) * 2 + 1];
let addr = tile_base_addr + (tile_offset_y as usize) * 2;
let tile_line_1 = self.vram[addr];
let tile_line_2 = self.vram[addr + 1];
// Get the correct bit
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 b1: bool = (tile_line_1 & 1 << (7 - tile_offset_x)) != 0;
let b2: bool = (tile_line_2 & 1 << (7 - tile_offset_x)) != 0;
// Lookup the color
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.
_ => 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);
}
if (self.control & CTRL_BG_SPRITE_ENABLE) == CTRL_BG_SPRITE_ENABLE {
self.render_sprites(&queue);
self.set_pixel(render_x, render_y, sdl2::pixels::Color::RGB(entry[0], entry[1], entry[2]), origin);
}
}
@ -628,8 +646,9 @@ impl Display {
// Obtain tile information
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_line_2 = self.vram[tile_base_addr + (tile_offset_y as usize) * 2 + 1];
let tile_addr = tile_base_addr + (tile_offset_y as usize) * 2;
let tile_line_1 = self.vram[tile_addr];
let tile_line_2 = self.vram[tile_addr + 1];
// Get the correct bit
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.
_ => 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 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_DISPLAY_STAT: u8 = 1 << 1;
pub const INTERRUPT_DISPLAY_VBLANK: u8 = 1 << 0;
@ -92,11 +94,13 @@ impl Interconnect {
joy_regular_keys: 0x0F,
joy_special_keys: 0x0F,
joy_switch: 0x30,
cycles: 0,
}
}
fn press_key(&mut self, key: Key) {
println!("Press key {:?}", &key);
// println!("Press key {:?}", &key);
match key {
Key::UP => self.joy_regular_keys &= !KEY_UP,
Key::DOWN => self.joy_regular_keys &= !KEY_DOWN,
@ -107,10 +111,11 @@ impl Interconnect {
Key::A => self.joy_special_keys &= !KEY_A,
Key::B => self.joy_special_keys &= !KEY_B,
}
self.interrupt_request_flags |= INTERRUPT_INPUT;
}
fn release_key(&mut self, key: Key) {
println!("Release key {:?}", &key);
// println!("Release key {:?}", &key);
match key {
Key::UP => self.joy_regular_keys |= KEY_UP,
Key::DOWN => self.joy_regular_keys |= KEY_DOWN,
@ -121,6 +126,7 @@ impl Interconnect {
Key::A => self.joy_special_keys |= KEY_A,
Key::B => self.joy_special_keys |= KEY_B,
}
self.interrupt_request_flags |= INTERRUPT_INPUT;
}
// Somehow we need different timers for this.
@ -140,36 +146,46 @@ impl Interconnect {
self.interrupt_request_flags |= INTERRUPT_TIMER_OVERFLOW;
}
// Make sure the window is responsive:
loop {
if let Some(event) = self.display.event_pump.poll_event(){
match event {
Event::Quit {..} | Event::KeyDown { keycode: Some(Keycode::Escape), .. } => {
panic!("TODO: Proper shutdown");
},
Event::KeyDown { keycode: Some(Keycode::Left), .. } => self.press_key(Key::LEFT),
Event::KeyDown { keycode: Some(Keycode::Down), .. } => self.press_key(Key::DOWN),
Event::KeyDown { keycode: Some(Keycode::Up), .. } => self.press_key(Key::UP),
Event::KeyDown { keycode: Some(Keycode::Right), .. } => self.press_key(Key::RIGHT),
Event::KeyDown { keycode: Some(Keycode::A), .. } => self.press_key(Key::START),
Event::KeyDown { keycode: Some(Keycode::S), .. } => self.press_key(Key::SELECT),
Event::KeyDown { keycode: Some(Keycode::Z), .. } => self.press_key(Key::A),
Event::KeyDown { keycode: Some(Keycode::X), .. } => self.press_key(Key::B),
if self.serial.serial_interrupt() {
self.interrupt_request_flags |= INTERRUPT_SERIAL;
}
Event::KeyUp { keycode: Some(Keycode::Left), .. } => self.release_key(Key::LEFT),
Event::KeyUp { keycode: Some(Keycode::Down), .. } => self.release_key(Key::DOWN),
Event::KeyUp { keycode: Some(Keycode::Up), .. } => self.release_key(Key::UP),
Event::KeyUp { keycode: Some(Keycode::Right), .. } => self.release_key(Key::RIGHT),
Event::KeyUp { keycode: Some(Keycode::A), .. } => self.release_key(Key::START),
Event::KeyUp { keycode: Some(Keycode::S), .. } => self.release_key(Key::SELECT),
Event::KeyUp { keycode: Some(Keycode::Z), .. } => self.release_key(Key::A),
Event::KeyUp { keycode: Some(Keycode::X), .. } => self.release_key(Key::B),
_ => {}
}
} else {
break;
}
}
// Make sure the window is responsive:
if self.cycles > 500 {
loop {
if let Some(event) = self.display.event_pump.poll_event(){
match event {
Event::Quit {..} | Event::KeyDown { keycode: Some(Keycode::Escape), .. } => {
self.cartridge.save();
panic!("TODO: Proper shutdown");
},
Event::KeyDown { keycode: Some(Keycode::Left), .. } => self.press_key(Key::LEFT),
Event::KeyDown { keycode: Some(Keycode::Down), .. } => self.press_key(Key::DOWN),
Event::KeyDown { keycode: Some(Keycode::Up), .. } => self.press_key(Key::UP),
Event::KeyDown { keycode: Some(Keycode::Right), .. } => self.press_key(Key::RIGHT),
Event::KeyDown { keycode: Some(Keycode::A), .. } => self.press_key(Key::START),
Event::KeyDown { keycode: Some(Keycode::S), .. } => self.press_key(Key::SELECT),
Event::KeyDown { keycode: Some(Keycode::Z), .. } => self.press_key(Key::A),
Event::KeyDown { keycode: Some(Keycode::X), .. } => self.press_key(Key::B),
Event::KeyUp { keycode: Some(Keycode::Left), .. } => self.release_key(Key::LEFT),
Event::KeyUp { keycode: Some(Keycode::Down), .. } => self.release_key(Key::DOWN),
Event::KeyUp { keycode: Some(Keycode::Up), .. } => self.release_key(Key::UP),
Event::KeyUp { keycode: Some(Keycode::Right), .. } => self.release_key(Key::RIGHT),
Event::KeyUp { keycode: Some(Keycode::A), .. } => self.release_key(Key::START),
Event::KeyUp { keycode: Some(Keycode::S), .. } => self.release_key(Key::SELECT),
Event::KeyUp { keycode: Some(Keycode::Z), .. } => self.release_key(Key::A),
Event::KeyUp { keycode: Some(Keycode::X), .. } => self.release_key(Key::B),
_ => {}
}
} else {
break;
}
}
self.cycles = 0;
} else {
self.cycles += cycles as u16;
}
}
#[inline]

View File

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