mirror of
https://github.com/FranLMSP/rmg-001.git
synced 2024-11-23 10:12:11 +00:00
WIP cgb double speed switch
This commit is contained in:
parent
ba4051bc14
commit
a69c5be762
27
src/bus.rs
27
src/bus.rs
@ -27,6 +27,7 @@ pub const SPRITE_ATTRIBUTE_TABLE: RangeInclusive<u16> = 0xFE00..=0xFE9F;
|
||||
pub const NOT_USABLE: RangeInclusive<u16> = 0xFEA0..=0xFEFF;
|
||||
pub const IO_REGISTERS: RangeInclusive<u16> = 0xFF00..=0xFF7F;
|
||||
pub const HIGH_RAM: RangeInclusive<u16> = 0xFF80..=0xFFFE;
|
||||
pub const PREPARE_SPEED_SWITCH_ADDRESS: u16 = 0xFF4D;
|
||||
|
||||
pub struct Bus {
|
||||
data: [u8; 0x10000],
|
||||
@ -38,6 +39,8 @@ pub struct Bus {
|
||||
pub sound: Sound,
|
||||
pub interrupts: Interrupts,
|
||||
pub cgb_mode: bool,
|
||||
pub double_speed_mode: bool,
|
||||
pub prepare_double_speed_mode: bool,
|
||||
}
|
||||
|
||||
impl Bus {
|
||||
@ -70,6 +73,8 @@ impl Bus {
|
||||
sound: Sound::new(),
|
||||
interrupts: Interrupts::new(),
|
||||
cgb_mode,
|
||||
double_speed_mode: false,
|
||||
prepare_double_speed_mode: false,
|
||||
};
|
||||
|
||||
// Hardware registers after the bootrom
|
||||
@ -119,6 +124,11 @@ impl Bus {
|
||||
return self.joypad.read(self.data[address as usize]);
|
||||
} else if Timer::is_io_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]
|
||||
}
|
||||
@ -161,6 +171,11 @@ impl Bus {
|
||||
self.hdma_transfer(data);
|
||||
} else if PPU::is_io_register(address) {
|
||||
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 {
|
||||
self.data[address as usize] = data;
|
||||
}
|
||||
@ -172,6 +187,18 @@ impl Bus {
|
||||
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) {
|
||||
let source = (data as u16) * 0x100;
|
||||
let mut count: u16 = 0;
|
||||
|
1059
src/cpu.rs
1059
src/cpu.rs
File diff suppressed because it is too large
Load Diff
@ -115,8 +115,11 @@ impl Emulator {
|
||||
self.cpu.run(&mut self.bus);
|
||||
let cycles = self.cpu.get_last_op_cycles().to_t();
|
||||
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.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
|
||||
// thread::sleep(time::Duration::from_nanos((self.cpu.get_last_op_cycles().0 * 238).try_into().unwrap()));
|
||||
|
16
src/ppu.rs
16
src/ppu.rs
@ -316,7 +316,7 @@ impl PPU {
|
||||
window_enable: false,
|
||||
window_drawn: false,
|
||||
lcd_enable: false,
|
||||
cycles: Cycles(0),
|
||||
cycles: Cycles(0.0),
|
||||
sprite_buffer: Vec::new(),
|
||||
window_y_counter: 0,
|
||||
last_bg_index: 0,
|
||||
@ -482,7 +482,7 @@ impl PPU {
|
||||
}
|
||||
|
||||
pub fn reset_cycles(&mut self) {
|
||||
self.cycles.0 = 0;
|
||||
self.cycles.0 = 0.0;
|
||||
}
|
||||
|
||||
pub fn increment_cycles(&mut self, cycles: Cycles) {
|
||||
@ -496,19 +496,19 @@ impl PPU {
|
||||
}
|
||||
|
||||
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
|
||||
self.set_lcd_status(LCDStatus::ModeFlag(LCDStatusModeFlag::SearchingOAM), true);
|
||||
self.stat_interrupt(interrupts);
|
||||
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
|
||||
if !self.get_lcd_status(LCDStatus::ModeFlag(LCDStatusModeFlag::TransferringToLCD)) {
|
||||
self.window_drawn = false;
|
||||
self.set_lcd_status(LCDStatus::ModeFlag(LCDStatusModeFlag::TransferringToLCD), true);
|
||||
}
|
||||
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
|
||||
self.set_lcd_status(LCDStatus::ModeFlag(LCDStatusModeFlag::HBlank), true);
|
||||
self.stat_interrupt(interrupts);
|
||||
@ -523,7 +523,7 @@ impl PPU {
|
||||
self.increment_cycles(cycles);
|
||||
|
||||
// Horizontal scan completed
|
||||
if self.cycles.0 > 456 {
|
||||
if self.cycles.0 > 456.0 {
|
||||
self.reset_cycles();
|
||||
|
||||
self.lcd_y = self.lcd_y.wrapping_add(1);
|
||||
@ -855,7 +855,7 @@ impl PPU {
|
||||
self.current_background_pixels = None;
|
||||
self.current_window_pixels = None;
|
||||
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 {
|
||||
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;
|
||||
count += 1;
|
||||
count += 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -81,7 +81,7 @@ pub fn start_eventloop() {
|
||||
*control_flow = ControlFlow::Exit
|
||||
},
|
||||
Event::MainEventsCleared => {
|
||||
emulator.run(Cycles(70224), pixels.get_frame());
|
||||
emulator.run(Cycles(70224.0), pixels.get_frame());
|
||||
frame_counter.increment();
|
||||
if frame_counter.elapsed_ms() >= 1000 {
|
||||
window.set_title(&format!("rmg-001 (FPS: {})", frame_counter.count()));
|
||||
|
@ -175,10 +175,10 @@ impl Sound {
|
||||
}
|
||||
|
||||
pub fn do_cycles(&mut self, cycles: Cycles) {
|
||||
let mut count = 0;
|
||||
let mut count = 0.0;
|
||||
while count < cycles.0 {
|
||||
self.cycle();
|
||||
count += 1;
|
||||
count += 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
|
16
src/timer.rs
16
src/timer.rs
@ -68,10 +68,10 @@ impl Timer {
|
||||
pub fn do_cycles(&mut self, interrupts: &mut Interrupts, cycles: Cycles) {
|
||||
self.is_enabled = self.is_timer_enabled();
|
||||
self.control = self.get_register(TIMER_CONTROL_ADDRESS);
|
||||
let mut count = 0;
|
||||
let mut count = 0.0;
|
||||
while count < cycles.0 {
|
||||
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_COUNTER_ADDRESS, 0);
|
||||
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.prev_result(), true);
|
||||
assert_eq!(timer.get_register(TIMER_COUNTER_ADDRESS), 0);
|
||||
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.prev_result(), true);
|
||||
assert_eq!(timer.get_register(TIMER_COUNTER_ADDRESS), 0);
|
||||
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.get_register(TIMER_COUNTER_ADDRESS), 1);
|
||||
assert_eq!(timer.prev_result(), false);
|
||||
@ -145,7 +145,7 @@ mod tests {
|
||||
timer.set_register(TIMER_CONTROL_ADDRESS, 0b101);
|
||||
timer.set_register(TIMER_COUNTER_ADDRESS, 0xFF);
|
||||
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.get_register(TIMER_COUNTER_ADDRESS), 0x00);
|
||||
assert_eq!(interrupts.get(Interrupt::Timer), true);
|
||||
@ -158,11 +158,11 @@ mod tests {
|
||||
timer.set_register(TIMER_CONTROL_ADDRESS, 0b101);
|
||||
timer.set_register(TIMER_COUNTER_ADDRESS, 0);
|
||||
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.get_register(TIMER_COUNTER_ADDRESS), 0);
|
||||
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.get_register(TIMER_COUNTER_ADDRESS), 1);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user