Compare commits

..

2 Commits

Author SHA1 Message Date
806a4bf211 Refactor timer 2021-11-01 22:22:43 -05:00
98be8f29dd EI instruction delay and fix palette bug 2021-11-01 17:03:56 -05:00
6 changed files with 89 additions and 67 deletions

View File

@ -54,14 +54,15 @@ pub const INTERRUPT_FLAG_ADDRESS: u16 = 0xFF0F;
pub struct Bus { pub struct Bus {
game_rom: ROM, game_rom: ROM,
data: [u8; 0x10000], data: [u8; 0x10000],
pub reset_timer: bool,
} }
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/dr-mario.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()) {
// let game_rom = match ROM::load_file("roms/cpu_instrs_individual/03-op sp,hl.gb".to_string()) { // let game_rom = match ROM::load_file("roms/cpu_instrs_individual/03-op sp,hl.gb".to_string()) {
// let game_rom = match ROM::load_file("roms/cpu_instrs_individual/04-op r,imm.gb".to_string()) { // let game_rom = match ROM::load_file("roms/cpu_instrs_individual/04-op r,imm.gb".to_string()) {
// let game_rom = match ROM::load_file("roms/cpu_instrs_individual/05-op rp.gb".to_string()) { // let game_rom = match ROM::load_file("roms/cpu_instrs_individual/05-op rp.gb".to_string()) {
@ -76,6 +77,7 @@ impl Bus {
_ => panic!("Could not read ROM"), _ => panic!("Could not read ROM"),
}; };
let mut data = [0x00; 0x10000]; let mut data = [0x00; 0x10000];
data[0xFF00] = 0b11001111;
data[0xFF01] = 0x00; data[0xFF01] = 0x00;
data[0xFF02] = 0x7E; data[0xFF02] = 0x7E;
data[0xFF04] = 0x18; data[0xFF04] = 0x18;
@ -95,10 +97,12 @@ impl Bus {
data[0xFF4A] = 0x00; data[0xFF4A] = 0x00;
data[0xFF4B] = 0x00; data[0xFF4B] = 0x00;
data[0xFFFF] = 0x00;
Self { Self {
data, data,
game_rom, game_rom,
reset_timer: false,
} }
} }
@ -118,6 +122,10 @@ impl Bus {
// print!("{}", data as char); // print!("{}", data as char);
} }
if address == 0xFF06 {
println!("Writing {:02X} to modulo", data);
}
if BANK_ZERO.in_range(address) || BANK_SWITCHABLE.in_range(address) { if BANK_ZERO.in_range(address) || BANK_SWITCHABLE.in_range(address) {
// println!("WRITING TO ROM"); // println!("WRITING TO ROM");
} else if WORK_RAM_1.in_range(address) || WORK_RAM_2.in_range(address) { } else if WORK_RAM_1.in_range(address) || WORK_RAM_2.in_range(address) {
@ -130,13 +138,14 @@ impl Bus {
self.data[address as usize] = data; self.data[address as usize] = data;
self.data[(WORK_RAM_1.begin() + (address - ECHO_RAM.begin())) as usize] = data; // Copy to the working RAM self.data[(WORK_RAM_1.begin() + (address - ECHO_RAM.begin())) as usize] = data; // Copy to the working RAM
} else if address == TIMER_DIVIDER_REGISTER_ADDRESS { } else if address == TIMER_DIVIDER_REGISTER_ADDRESS {
self.data[address as usize] = 0x00; println!("bus timer reset");
self.reset_timer = true;
} 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 == JOYPAD_ADDRESS { } else if address == JOYPAD_ADDRESS {
let byte = self.data[JOYPAD_ADDRESS as usize]; let byte = self.data[JOYPAD_ADDRESS as usize];
self.data[JOYPAD_ADDRESS as usize] = (data & 0b00110000) | 0b11000000 | (byte & 0b00001111); self.data[JOYPAD_ADDRESS as usize] = (data & 0b11110000) | (byte & 0b00001111);
} else { } else {
self.data[address as usize] = data; self.data[address as usize] = data;
} }

View File

@ -843,6 +843,7 @@ pub struct CPU {
exec_calls_count: usize, exec_calls_count: usize,
is_halted: bool, is_halted: bool,
ime: bool, // Interrupt Master Enable ime: bool, // Interrupt Master Enable
ei_delay: bool,
} }
impl CPU { impl CPU {
@ -853,6 +854,7 @@ impl CPU {
last_op_cycles: Cycles(0), last_op_cycles: Cycles(0),
exec_calls_count: 0, exec_calls_count: 0,
is_halted: false, is_halted: false,
ei_delay: false,
ime: true, ime: true,
} }
} }
@ -909,14 +911,16 @@ impl CPU {
} }
pub fn handle_interrupt(&mut self, bus: &mut Bus, interrupt: Interrupt) { pub fn handle_interrupt(&mut self, bus: &mut Bus, interrupt: Interrupt) {
bus.set_interrupt_enable(interrupt, false);
bus.set_interrupt_flag(interrupt, false);
let vector = interrupt.get_vector();
self.exec(Opcode::CALL(OpcodeParameter::U16(vector)), bus);
println!("Interrupt: {:?}", interrupt); println!("Interrupt: {:?}", interrupt);
bus.set_interrupt_flag(interrupt, false);
self.ime = false;
self.exec(Opcode::CALL(OpcodeParameter::U16(interrupt.get_vector())), bus);
} }
pub fn check_interrupts(&mut self, bus: &mut Bus) -> Option<Interrupt> { pub fn check_interrupts(&mut self, bus: &mut Bus) -> Option<Interrupt> {
/* println!("IE {:08b}", bus.read(INTERRUPT_ENABLE_ADDRESS));
println!("IF {:08b}", bus.read(INTERRUPT_FLAG_ADDRESS));
println!("---"); */
if !self.ime && !self.is_halted { if !self.ime && !self.is_halted {
return None; return None;
} }
@ -952,6 +956,12 @@ impl CPU {
} }
self.increment_cycles(cycles); self.increment_cycles(cycles);
self.exec(opcode, bus); self.exec(opcode, bus);
if self.ei_delay && !self.ime {
println!("EI delay");
self.ei_delay = false;
self.run(bus);
self.ime = true;
}
} else if self.is_halted { } else if self.is_halted {
self.increment_cycles(Cycles(1)); self.increment_cycles(Cycles(1));
} }
@ -1781,25 +1791,20 @@ impl CPU {
Opcode::EI => { Opcode::EI => {
println!("EI"); println!("EI");
self.registers.increment(Register::PC, 1); self.registers.increment(Register::PC, 1);
self.ime = true; self.ei_delay = true;
}, },
// Disable interrupts // Disable interrupts
Opcode::DI => { Opcode::DI => {
println!("DI");
self.registers.increment(Register::PC, 1); self.registers.increment(Register::PC, 1);
self.ime = false; self.ime = false;
}, },
// Same as enabling interrupts and then executing RET // Same as enabling interrupts and then executing RET
Opcode::RETI => { Opcode::RETI => {
println!("RETI");
let prev_pc = self.registers.get(Register::PC);
self.exec(Opcode::EI, bus);
self.exec(Opcode::RET(OpcodeParameter::NoParam), bus); self.exec(Opcode::RET(OpcodeParameter::NoParam), bus);
self.registers.set(Register::PC, prev_pc.wrapping_add(1)); self.ime = true;
}, },
// Don't execute instructions until an interrupt is requested // Don't execute instructions until an interrupt is requested
Opcode::HALT => { Opcode::HALT => {
println!("HALT");
self.registers.increment(Register::PC, 1); self.registers.increment(Register::PC, 1);
self.is_halted = true; self.is_halted = true;
}, },
@ -1807,8 +1812,11 @@ impl CPU {
self.registers.increment(Register::PC, 2); self.registers.increment(Register::PC, 2);
}, },
Opcode::NOP => self.registers.increment(Register::PC, 1), Opcode::NOP => self.registers.increment(Register::PC, 1),
Opcode::IllegalInstruction => {panic!("Illegal instruction");}, Opcode::IllegalInstruction => {
_ => {panic!("Illegal instruction");}, println!("Illegal instruction!");
self.registers.increment(Register::PC, 1);
},
_ => unreachable!(),
}; };
} }
} }

View File

@ -112,6 +112,10 @@ impl Emulator {
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);
if self.bus.reset_timer {
self.bus.reset_timer = false;
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());
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());
} }

View File

@ -73,8 +73,7 @@ impl Joypad {
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);
let data = 0b11000000 | let data = (byte & 0b11110000) |
(byte & 0b00110000) |
( (
(!((direction && self.down) || (action && self.start)) as u8) << 3 (!((direction && self.down) || (action && self.start)) as u8) << 3
) | ( ) | (

View File

@ -54,14 +54,14 @@ pub enum LCDControl {
impl LCDControl { impl LCDControl {
fn index(&self) -> BitIndex { fn index(&self) -> BitIndex {
match self { match self {
LCDControl::LCDEnable => BitIndex::I7, LCDControl::LCDEnable => BitIndex::I7,
LCDControl::WindowTileMapAddress => BitIndex::I6, LCDControl::WindowTileMapAddress => BitIndex::I6,
LCDControl::WindowEnable => BitIndex::I5, LCDControl::WindowEnable => BitIndex::I5,
LCDControl::TileAddressMode => BitIndex::I4, LCDControl::TileAddressMode => BitIndex::I4,
LCDControl::BackgroundTileMapAddress => BitIndex::I3, LCDControl::BackgroundTileMapAddress => BitIndex::I3,
LCDControl::ObjectSize => BitIndex::I2, LCDControl::ObjectSize => BitIndex::I2,
LCDControl::ObjectEnable => BitIndex::I1, LCDControl::ObjectEnable => BitIndex::I1,
LCDControl::BackgroundPriority => BitIndex::I0, LCDControl::BackgroundPriority => BitIndex::I0,
} }
} }
@ -163,16 +163,19 @@ impl PPU {
self.reset_cycles(); self.reset_cycles();
PPU::set_lcd_y(bus, PPU::get_lcd_y(bus).wrapping_add(1)); PPU::set_lcd_y(bus, PPU::get_lcd_y(bus).wrapping_add(1));
let lyc_compare = PPU::get_lcd_y(bus) == bus.read(LCD_Y_COMPARE_ADDRESS);
PPU::set_lcd_status(bus, LCDStatus::LYCFlag, lyc_compare);
if PPU::get_lcd_status(bus, LCDStatus::LYCInterrupt) && lyc_compare {
PPU::request_interrupt(bus, Interrupt::LCDSTAT);
}
// Frame completed // Frame completed
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);
}
}
fn check_lyc(bus: &mut Bus) {
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::request_interrupt(bus, Interrupt::LCDSTAT);
} }
} }
@ -335,17 +338,17 @@ impl PPU {
0b01 => (palette_byte >> 2) & 0b11, 0b01 => (palette_byte >> 2) & 0b11,
0b10 => (palette_byte >> 4) & 0b11, 0b10 => (palette_byte >> 4) & 0b11,
0b11 => (palette_byte >> 6) & 0b11, 0b11 => (palette_byte >> 6) & 0b11,
_ => 0b00, _ => unreachable!(),
} }
} }
fn get_pixel(two_bit_pixel: u8) -> Pixel { fn get_pixel(two_bit_pixel: u8) -> Pixel {
match two_bit_pixel { match two_bit_pixel {
0x00 => Pixel::White, 0b00 => Pixel::White,
0x01 => Pixel::Light, 0b01 => Pixel::Light,
0x10 => Pixel::Dark, 0b10 => Pixel::Dark,
0x11 => Pixel::Black, 0b11 => Pixel::Black,
_ => Pixel::Black, _ => unreachable!(),
} }
} }

View File

@ -11,23 +11,23 @@ pub const TIMER_MODULO_ADDRESS: u16 = 0xFF06;
pub const TIMER_CONTROL_ADDRESS: u16 = 0xFF07; pub const TIMER_CONTROL_ADDRESS: u16 = 0xFF07;
pub struct Timer { pub struct Timer {
cycles: Cycles, divider: u16,
prev_result: bool,
} }
impl Timer { impl Timer {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
cycles: Cycles(0), divider: 0,
prev_result: false,
} }
} }
fn increment_cycles(&mut self, cycles: Cycles) { pub fn reset(&mut self, bus: &mut Bus) {
self.cycles.0 += cycles.0; println!("timer reset");
} self.divider = 0;
bus.force_write(TIMER_DIVIDER_REGISTER_ADDRESS, 0);
fn reset_cycles(&mut self) {
self.cycles.0 = 0;
} }
pub fn do_cycles(&mut self, bus: &mut Bus, cycles: Cycles) { pub fn do_cycles(&mut self, bus: &mut Bus, cycles: Cycles) {
@ -39,38 +39,37 @@ impl Timer {
} }
fn cycle(&mut self, bus: &mut Bus) { fn cycle(&mut self, bus: &mut Bus) {
let div = bus.read(TIMER_DIVIDER_REGISTER_ADDRESS); self.divider = self.divider.wrapping_add(1);
bus.write(TIMER_DIVIDER_REGISTER_ADDRESS, div.wrapping_add(1)); bus.force_write(TIMER_DIVIDER_REGISTER_ADDRESS, self.divider.to_be_bytes()[0]);
if Timer::is_timer_enabled(bus) { let result = Timer::is_timer_enabled(bus) && self.get_tima_rate(bus);
let tima = bus.read(TIMER_COUNTER_ADDRESS);
let tima_rate = Timer::get_tima_rate(bus); if self.prev_result && !result {
if self.cycles.0 >= tima_rate { let tima = bus.read(TIMER_COUNTER_ADDRESS).wrapping_add(1);
if tima.checked_add(1) == None { if tima == 0 {
bus.write(TIMER_COUNTER_ADDRESS, bus.read(TIMER_MODULO_ADDRESS)); bus.write(TIMER_COUNTER_ADDRESS, bus.read(TIMER_MODULO_ADDRESS));
bus.set_interrupt_flag(Interrupt::Timer, false); bus.set_interrupt_flag(Interrupt::Timer, true);
} else { println!("Timer interrupt set");
bus.write(TIMER_COUNTER_ADDRESS, tima.wrapping_add(1)); } else {
} bus.write(TIMER_COUNTER_ADDRESS, tima);
self.reset_cycles();
} }
} }
self.increment_cycles(Cycles(1)); self.prev_result = result;
} }
fn is_timer_enabled(bus: &Bus) -> bool { fn is_timer_enabled(bus: &Bus) -> bool {
get_bit(bus.read(TIMER_CONTROL_ADDRESS), BitIndex::I2) get_bit(bus.read(TIMER_CONTROL_ADDRESS), BitIndex::I2)
} }
fn get_tima_rate(bus: &Bus) -> usize { fn get_tima_rate(&self, bus: &Bus) -> bool {
let clock_select = bus.read(TIMER_CONTROL_ADDRESS) & 0b0000_0011; let clock_select = bus.read(TIMER_CONTROL_ADDRESS) & 0b0000_0011;
match clock_select { match clock_select {
0b00 => 16, 0b00 => ((self.divider >> 9) & 1) == 1,
0b01 => 64, 0b01 => ((self.divider >> 3) & 1) == 1,
0b10 => 256, 0b10 => ((self.divider >> 5) & 1) == 1,
0b11 => 1024, 0b11 => ((self.divider >> 7) & 1) == 1,
_ => 1, _ => unreachable!(),
} }
} }
} }