WIP cgb double speed switch

This commit is contained in:
Franco Colmenarez 2022-10-11 20:34:01 -05:00
parent ba4051bc14
commit a69c5be762
7 changed files with 590 additions and 539 deletions

View File

@ -27,6 +27,7 @@ pub const SPRITE_ATTRIBUTE_TABLE: RangeInclusive<u16> = 0xFE00..=0xFE9F;
pub const NOT_USABLE: RangeInclusive<u16> = 0xFEA0..=0xFEFF; pub const NOT_USABLE: RangeInclusive<u16> = 0xFEA0..=0xFEFF;
pub const IO_REGISTERS: RangeInclusive<u16> = 0xFF00..=0xFF7F; pub const IO_REGISTERS: RangeInclusive<u16> = 0xFF00..=0xFF7F;
pub const HIGH_RAM: RangeInclusive<u16> = 0xFF80..=0xFFFE; pub const HIGH_RAM: RangeInclusive<u16> = 0xFF80..=0xFFFE;
pub const PREPARE_SPEED_SWITCH_ADDRESS: u16 = 0xFF4D;
pub struct Bus { pub struct Bus {
data: [u8; 0x10000], data: [u8; 0x10000],
@ -38,6 +39,8 @@ pub struct Bus {
pub sound: Sound, pub sound: Sound,
pub interrupts: Interrupts, pub interrupts: Interrupts,
pub cgb_mode: bool, pub cgb_mode: bool,
pub double_speed_mode: bool,
pub prepare_double_speed_mode: bool,
} }
impl Bus { impl Bus {
@ -70,6 +73,8 @@ impl Bus {
sound: Sound::new(), sound: Sound::new(),
interrupts: Interrupts::new(), interrupts: Interrupts::new(),
cgb_mode, cgb_mode,
double_speed_mode: false,
prepare_double_speed_mode: false,
}; };
// Hardware registers after the bootrom // Hardware registers after the bootrom
@ -119,6 +124,11 @@ impl Bus {
return self.joypad.read(self.data[address as usize]); return self.joypad.read(self.data[address as usize]);
} else if Timer::is_io_register(address) { } else if Timer::is_io_register(address) {
return self.timer.get_register(address); return self.timer.get_register(address);
} else if address == PREPARE_SPEED_SWITCH_ADDRESS && self.cgb_mode {
let byte = self.data[address as usize];
let current_speed = (self.double_speed_mode as u8) << 7;
let prepare_speed_switch = self.prepare_double_speed_mode as u8;
return (byte & 0b0111_1110) | current_speed | prepare_speed_switch;
} }
self.data[address as usize] self.data[address as usize]
} }
@ -161,6 +171,11 @@ impl Bus {
self.hdma_transfer(data); self.hdma_transfer(data);
} else if PPU::is_io_register(address) { } else if PPU::is_io_register(address) {
self.ppu.set_register(address, data); self.ppu.set_register(address, data);
} else if address == PREPARE_SPEED_SWITCH_ADDRESS && self.cgb_mode {
let current_byte = self.data[address as usize];
self.prepare_double_speed_mode = (data & 1) == 1;
// bit 7 is read only on cgb mode
self.data[address as usize] = (current_byte & 0b1000_0000) | (data & 0b0111_1111);
} else { } else {
self.data[address as usize] = data; self.data[address as usize] = data;
} }
@ -172,6 +187,18 @@ impl Bus {
self.write(address.wrapping_add(1), bytes[1]); self.write(address.wrapping_add(1), bytes[1]);
} }
pub fn prepare_double_speed_mode(&self) -> bool {
self.cgb_mode && self.prepare_double_speed_mode
}
pub fn double_speed_mode(&self) -> bool {
self.cgb_mode && self.double_speed_mode
}
pub fn set_double_speed_mode(&mut self, val: bool) {
self.double_speed_mode = val;
}
fn dma_transfer(&mut self, data: u8) { fn dma_transfer(&mut self, data: u8) {
let source = (data as u16) * 0x100; let source = (data as u16) * 0x100;
let mut count: u16 = 0; let mut count: u16 = 0;

1057
src/cpu.rs

File diff suppressed because it is too large Load Diff

View File

@ -115,8 +115,11 @@ impl Emulator {
self.cpu.run(&mut self.bus); self.cpu.run(&mut self.bus);
let cycles = self.cpu.get_last_op_cycles().to_t(); let cycles = self.cpu.get_last_op_cycles().to_t();
self.bus.ppu.do_cycles(&mut self.bus.interrupts, cycles, frame_buffer); self.bus.ppu.do_cycles(&mut self.bus.interrupts, cycles, frame_buffer);
self.bus.timer.do_cycles(&mut self.bus.interrupts, cycles);
self.bus.sound.do_cycles(cycles); self.bus.sound.do_cycles(cycles);
self.bus.timer.do_cycles(&mut self.bus.interrupts, cycles);
if self.bus.double_speed_mode() {
self.bus.timer.do_cycles(&mut self.bus.interrupts, Cycles(cycles.0 * 3.0));
}
// 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()));

View File

@ -316,7 +316,7 @@ impl PPU {
window_enable: false, window_enable: false,
window_drawn: false, window_drawn: false,
lcd_enable: false, lcd_enable: false,
cycles: Cycles(0), cycles: Cycles(0.0),
sprite_buffer: Vec::new(), sprite_buffer: Vec::new(),
window_y_counter: 0, window_y_counter: 0,
last_bg_index: 0, last_bg_index: 0,
@ -482,7 +482,7 @@ impl PPU {
} }
pub fn reset_cycles(&mut self) { pub fn reset_cycles(&mut self) {
self.cycles.0 = 0; self.cycles.0 = 0.0;
} }
pub fn increment_cycles(&mut self, cycles: Cycles) { pub fn increment_cycles(&mut self, cycles: Cycles) {
@ -496,19 +496,19 @@ impl PPU {
} }
if self.lcd_y < 144 { if self.lcd_y < 144 {
if self.cycles.0 <= 80 && !self.get_lcd_status(LCDStatus::ModeFlag(LCDStatusModeFlag::SearchingOAM)) { if self.cycles.0 <= 80.0 && !self.get_lcd_status(LCDStatus::ModeFlag(LCDStatusModeFlag::SearchingOAM)) {
// Mode 2 OAM scan // Mode 2 OAM scan
self.set_lcd_status(LCDStatus::ModeFlag(LCDStatusModeFlag::SearchingOAM), true); self.set_lcd_status(LCDStatus::ModeFlag(LCDStatusModeFlag::SearchingOAM), true);
self.stat_interrupt(interrupts); self.stat_interrupt(interrupts);
self.oam_search(); self.oam_search();
} else if self.cycles.0 > 80 && self.cycles.0 <= 80 + 172 { } else if self.cycles.0 > 80.0 && self.cycles.0 <= 80.0 + 172.0 {
// Mode 3 drawing pixel line. This could also last 289 cycles // Mode 3 drawing pixel line. This could also last 289 cycles
if !self.get_lcd_status(LCDStatus::ModeFlag(LCDStatusModeFlag::TransferringToLCD)) { if !self.get_lcd_status(LCDStatus::ModeFlag(LCDStatusModeFlag::TransferringToLCD)) {
self.window_drawn = false; self.window_drawn = false;
self.set_lcd_status(LCDStatus::ModeFlag(LCDStatusModeFlag::TransferringToLCD), true); self.set_lcd_status(LCDStatus::ModeFlag(LCDStatusModeFlag::TransferringToLCD), true);
} }
self.draw_line(cycles, frame_buffer); self.draw_line(cycles, frame_buffer);
} else if self.cycles.0 > 80 + 172 && self.cycles.0 <= 80 + 172 + 204 && !self.get_lcd_status(LCDStatus::ModeFlag(LCDStatusModeFlag::HBlank)) { } else if self.cycles.0 > 80.0 + 172.0 && self.cycles.0 <= 80.0 + 172.0 + 204.0 && !self.get_lcd_status(LCDStatus::ModeFlag(LCDStatusModeFlag::HBlank)) {
// 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
self.set_lcd_status(LCDStatus::ModeFlag(LCDStatusModeFlag::HBlank), true); self.set_lcd_status(LCDStatus::ModeFlag(LCDStatusModeFlag::HBlank), true);
self.stat_interrupt(interrupts); self.stat_interrupt(interrupts);
@ -523,7 +523,7 @@ impl PPU {
self.increment_cycles(cycles); self.increment_cycles(cycles);
// Horizontal scan completed // Horizontal scan completed
if self.cycles.0 > 456 { if self.cycles.0 > 456.0 {
self.reset_cycles(); self.reset_cycles();
self.lcd_y = self.lcd_y.wrapping_add(1); self.lcd_y = self.lcd_y.wrapping_add(1);
@ -855,7 +855,7 @@ impl PPU {
self.current_background_pixels = None; self.current_background_pixels = None;
self.current_window_pixels = None; self.current_window_pixels = None;
self.bg_palette = self.get_register(BACKGROUND_PALETTE_ADDRESS); self.bg_palette = self.get_register(BACKGROUND_PALETTE_ADDRESS);
let mut count = 0; let mut count = 0.0;
while count < cycles.0 && (self.lcd_x as u32) < LCD_WIDTH { while count < cycles.0 && (self.lcd_x as u32) < LCD_WIDTH {
let idx = (self.lcd_x as usize + (self.lcd_y as usize * LCD_WIDTH as usize)) * 4; let idx = (self.lcd_x as usize + (self.lcd_y as usize * LCD_WIDTH as usize)) * 4;
@ -896,7 +896,7 @@ impl PPU {
} }
self.lcd_x += 1; self.lcd_x += 1;
count += 1; count += 1.0;
} }
} }

View File

@ -81,7 +81,7 @@ pub fn start_eventloop() {
*control_flow = ControlFlow::Exit *control_flow = ControlFlow::Exit
}, },
Event::MainEventsCleared => { Event::MainEventsCleared => {
emulator.run(Cycles(70224), pixels.get_frame()); emulator.run(Cycles(70224.0), pixels.get_frame());
frame_counter.increment(); frame_counter.increment();
if frame_counter.elapsed_ms() >= 1000 { if frame_counter.elapsed_ms() >= 1000 {
window.set_title(&format!("rmg-001 (FPS: {})", frame_counter.count())); window.set_title(&format!("rmg-001 (FPS: {})", frame_counter.count()));

View File

@ -175,10 +175,10 @@ impl Sound {
} }
pub fn do_cycles(&mut self, cycles: Cycles) { pub fn do_cycles(&mut self, cycles: Cycles) {
let mut count = 0; let mut count = 0.0;
while count < cycles.0 { while count < cycles.0 {
self.cycle(); self.cycle();
count += 1; count += 1.0;
} }
} }

View File

@ -68,10 +68,10 @@ impl Timer {
pub fn do_cycles(&mut self, interrupts: &mut Interrupts, cycles: Cycles) { pub fn do_cycles(&mut self, interrupts: &mut Interrupts, cycles: Cycles) {
self.is_enabled = self.is_timer_enabled(); self.is_enabled = self.is_timer_enabled();
self.control = self.get_register(TIMER_CONTROL_ADDRESS); self.control = self.get_register(TIMER_CONTROL_ADDRESS);
let mut count = 0; let mut count = 0.0;
while count < cycles.0 { while count < cycles.0 {
self.cycle(interrupts); self.cycle(interrupts);
count += 1; count += 1.0;
} }
} }
@ -120,18 +120,18 @@ mod tests {
timer.set_register(TIMER_CONTROL_ADDRESS, 0b101); timer.set_register(TIMER_CONTROL_ADDRESS, 0b101);
timer.set_register(TIMER_COUNTER_ADDRESS, 0); timer.set_register(TIMER_COUNTER_ADDRESS, 0);
timer.set_div(0b10111); timer.set_div(0b10111);
timer.do_cycles(&mut interrupts, Cycles(1)); timer.do_cycles(&mut interrupts, Cycles(1.0));
assert_eq!(timer.div(), 0b11000); assert_eq!(timer.div(), 0b11000);
assert_eq!(timer.prev_result(), true); assert_eq!(timer.prev_result(), true);
assert_eq!(timer.get_register(TIMER_COUNTER_ADDRESS), 0); assert_eq!(timer.get_register(TIMER_COUNTER_ADDRESS), 0);
assert_eq!(interrupts.get(Interrupt::Timer), false); assert_eq!(interrupts.get(Interrupt::Timer), false);
timer.do_cycles(&mut interrupts, Cycles(7)); timer.do_cycles(&mut interrupts, Cycles(7.0));
assert_eq!(timer.div(), 0b11111); assert_eq!(timer.div(), 0b11111);
assert_eq!(timer.prev_result(), true); assert_eq!(timer.prev_result(), true);
assert_eq!(timer.get_register(TIMER_COUNTER_ADDRESS), 0); assert_eq!(timer.get_register(TIMER_COUNTER_ADDRESS), 0);
assert_eq!(interrupts.get(Interrupt::Timer), false); assert_eq!(interrupts.get(Interrupt::Timer), false);
timer.do_cycles(&mut interrupts, Cycles(1)); timer.do_cycles(&mut interrupts, Cycles(1.0));
assert_eq!(timer.div(), 0b100000); assert_eq!(timer.div(), 0b100000);
assert_eq!(timer.get_register(TIMER_COUNTER_ADDRESS), 1); assert_eq!(timer.get_register(TIMER_COUNTER_ADDRESS), 1);
assert_eq!(timer.prev_result(), false); assert_eq!(timer.prev_result(), false);
@ -145,7 +145,7 @@ mod tests {
timer.set_register(TIMER_CONTROL_ADDRESS, 0b101); timer.set_register(TIMER_CONTROL_ADDRESS, 0b101);
timer.set_register(TIMER_COUNTER_ADDRESS, 0xFF); timer.set_register(TIMER_COUNTER_ADDRESS, 0xFF);
timer.set_div(0b10111); timer.set_div(0b10111);
timer.do_cycles(&mut interrupts, Cycles(9)); timer.do_cycles(&mut interrupts, Cycles(9.0));
assert_eq!(timer.div(), 0b100000); assert_eq!(timer.div(), 0b100000);
assert_eq!(timer.get_register(TIMER_COUNTER_ADDRESS), 0x00); assert_eq!(timer.get_register(TIMER_COUNTER_ADDRESS), 0x00);
assert_eq!(interrupts.get(Interrupt::Timer), true); assert_eq!(interrupts.get(Interrupt::Timer), true);
@ -158,11 +158,11 @@ mod tests {
timer.set_register(TIMER_CONTROL_ADDRESS, 0b101); timer.set_register(TIMER_CONTROL_ADDRESS, 0b101);
timer.set_register(TIMER_COUNTER_ADDRESS, 0); timer.set_register(TIMER_COUNTER_ADDRESS, 0);
timer.set_div(0b11000); timer.set_div(0b11000);
timer.do_cycles(&mut interrupts, Cycles(1)); timer.do_cycles(&mut interrupts, Cycles(1.0));
assert_eq!(timer.div(), 0b11001); assert_eq!(timer.div(), 0b11001);
assert_eq!(timer.get_register(TIMER_COUNTER_ADDRESS), 0); assert_eq!(timer.get_register(TIMER_COUNTER_ADDRESS), 0);
timer.set_register(TIMER_CONTROL_ADDRESS, 0b001); timer.set_register(TIMER_CONTROL_ADDRESS, 0b001);
timer.do_cycles(&mut interrupts, Cycles(1)); timer.do_cycles(&mut interrupts, Cycles(1.0));
assert_eq!(timer.div(), 0b11010); assert_eq!(timer.div(), 0b11010);
assert_eq!(timer.get_register(TIMER_COUNTER_ADDRESS), 1); assert_eq!(timer.get_register(TIMER_COUNTER_ADDRESS), 1);
} }