mirror of
https://github.com/FranLMSP/rmg-001.git
synced 2024-11-23 18:21:31 +00:00
Fix interrupts, implement halt mode and fix IME
This commit is contained in:
parent
61db367f31
commit
5723c3b3b6
22
src/bus.rs
22
src/bus.rs
@ -5,7 +5,7 @@ use crate::utils::{
|
|||||||
join_bytes
|
join_bytes
|
||||||
};
|
};
|
||||||
use crate::rom::ROM;
|
use crate::rom::ROM;
|
||||||
use crate::ppu::{PPU, LCDStatus, LCDStatusModeFlag};
|
use crate::ppu::{PPU, LCDStatus, LCDStatusModeFlag, LCD_CONTROL_ADDRESS, LCD_Y_ADDRESS};
|
||||||
use crate::cpu::{Interrupt};
|
use crate::cpu::{Interrupt};
|
||||||
use crate::timer::{TIMER_DIVIDER_REGISTER_ADDRESS};
|
use crate::timer::{TIMER_DIVIDER_REGISTER_ADDRESS};
|
||||||
|
|
||||||
@ -50,7 +50,7 @@ pub struct Bus {
|
|||||||
|
|
||||||
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/mario-land.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()) {
|
||||||
@ -74,14 +74,11 @@ impl Bus {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn read(&self, address: u16) -> u8 {
|
pub fn read(&self, address: u16) -> u8 {
|
||||||
if BANK_ZERO.in_range(address) || BANK_SWITCHABLE.in_range(address) {
|
if address == 0xFF00 {
|
||||||
return self.game_rom.read(address);
|
|
||||||
} else if VIDEO_RAM.in_range(address) {
|
|
||||||
if PPU::get_lcd_status(self, LCDStatus::ModeFlag(LCDStatusModeFlag::TransferringToLCD)) {
|
|
||||||
return 0xFF;
|
return 0xFF;
|
||||||
}
|
}
|
||||||
} else if IO_REGISTERS.in_range(address) {
|
if BANK_ZERO.in_range(address) || BANK_SWITCHABLE.in_range(address) {
|
||||||
return self.data[address as usize];
|
return self.game_rom.read(address);
|
||||||
}
|
}
|
||||||
self.data[address as usize]
|
self.data[address as usize]
|
||||||
}
|
}
|
||||||
@ -106,10 +103,11 @@ impl Bus {
|
|||||||
} else if ECHO_RAM.in_range(address) {
|
} else if ECHO_RAM.in_range(address) {
|
||||||
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 VIDEO_RAM.in_range(address) {
|
|
||||||
self.data[address as usize] = data;
|
|
||||||
} else if address == TIMER_DIVIDER_REGISTER_ADDRESS {
|
} else if address == TIMER_DIVIDER_REGISTER_ADDRESS {
|
||||||
self.data[address as usize] = 0x00;
|
self.data[address as usize] = 0x00;
|
||||||
|
} else if address == LCD_CONTROL_ADDRESS && get_bit(data, BitIndex::I7) {
|
||||||
|
self.data[address as usize] = data;
|
||||||
|
self.data[LCD_Y_ADDRESS as usize] = 0x00;
|
||||||
} else {
|
} else {
|
||||||
self.data[address as usize] = data;
|
self.data[address as usize] = data;
|
||||||
}
|
}
|
||||||
@ -121,12 +119,12 @@ impl Bus {
|
|||||||
self.write(address.wrapping_add(1), bytes[1]);
|
self.write(address.wrapping_add(1), bytes[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_interrupt_master(&mut self, interrupt: Interrupt, val: bool) {
|
pub fn set_interrupt_enable(&mut self, interrupt: Interrupt, val: bool) {
|
||||||
let byte = self.read(INTERRUPT_ENABLE_ADDRESS);
|
let byte = self.read(INTERRUPT_ENABLE_ADDRESS);
|
||||||
self.write(INTERRUPT_ENABLE_ADDRESS, interrupt.set(byte, val));
|
self.write(INTERRUPT_ENABLE_ADDRESS, interrupt.set(byte, val));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_interrupt(&mut self, interrupt: Interrupt, val: bool) {
|
pub fn set_interrupt_flag(&mut self, interrupt: Interrupt, val: bool) {
|
||||||
let byte = self.read(INTERRUPT_FLAG_ADDRESS);
|
let byte = self.read(INTERRUPT_FLAG_ADDRESS);
|
||||||
self.write(INTERRUPT_FLAG_ADDRESS, interrupt.set(byte, val));
|
self.write(INTERRUPT_FLAG_ADDRESS, interrupt.set(byte, val));
|
||||||
}
|
}
|
||||||
|
35
src/cpu.rs
35
src/cpu.rs
@ -841,6 +841,8 @@ pub struct CPU {
|
|||||||
cycles: Cycles,
|
cycles: Cycles,
|
||||||
last_op_cycles: Cycles,
|
last_op_cycles: Cycles,
|
||||||
exec_calls_count: usize,
|
exec_calls_count: usize,
|
||||||
|
is_halted: bool,
|
||||||
|
ime: bool, // Interrupt Master Enable
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CPU {
|
impl CPU {
|
||||||
@ -850,6 +852,8 @@ impl CPU {
|
|||||||
cycles: Cycles(0),
|
cycles: Cycles(0),
|
||||||
last_op_cycles: Cycles(0),
|
last_op_cycles: Cycles(0),
|
||||||
exec_calls_count: 0,
|
exec_calls_count: 0,
|
||||||
|
is_halted: false,
|
||||||
|
ime: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -905,8 +909,8 @@ 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_master(interrupt, false);
|
bus.set_interrupt_enable(interrupt, false);
|
||||||
bus.set_interrupt(interrupt, false);
|
bus.set_interrupt_flag(interrupt, false);
|
||||||
let vector = interrupt.get_vector();
|
let vector = interrupt.get_vector();
|
||||||
self.exec(Opcode::CALL(OpcodeParameter::U16(vector)), bus);
|
self.exec(Opcode::CALL(OpcodeParameter::U16(vector)), bus);
|
||||||
self.increment_cycles(Cycles(5));
|
self.increment_cycles(Cycles(5));
|
||||||
@ -914,6 +918,12 @@ impl CPU {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn check_interrupts(&mut self, bus: &mut Bus) -> Option<Interrupt> {
|
pub fn check_interrupts(&mut self, bus: &mut Bus) -> Option<Interrupt> {
|
||||||
|
if bus.read(INTERRUPT_ENABLE_ADDRESS) & bus.read(INTERRUPT_FLAG_ADDRESS) != 0 {
|
||||||
|
self.is_halted = false;
|
||||||
|
}
|
||||||
|
if !self.ime {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
if bus.get_interrupt(Interrupt::VBlank) {
|
if bus.get_interrupt(Interrupt::VBlank) {
|
||||||
return Some(Interrupt::VBlank);
|
return Some(Interrupt::VBlank);
|
||||||
} else if bus.get_interrupt(Interrupt::LCDSTAT) {
|
} else if bus.get_interrupt(Interrupt::LCDSTAT) {
|
||||||
@ -932,7 +942,7 @@ impl CPU {
|
|||||||
let cycles_start = self.get_cycles();
|
let cycles_start = self.get_cycles();
|
||||||
if let Some(interrupt) = self.check_interrupts(bus) {
|
if let Some(interrupt) = self.check_interrupts(bus) {
|
||||||
self.handle_interrupt(bus, interrupt);
|
self.handle_interrupt(bus, interrupt);
|
||||||
} else {
|
} else if !self.is_halted {
|
||||||
let program_counter = self.registers.get(Register::PC);
|
let program_counter = self.registers.get(Register::PC);
|
||||||
let parameter_bytes = OpcodeParameterBytes::from_address(program_counter, bus);
|
let parameter_bytes = OpcodeParameterBytes::from_address(program_counter, bus);
|
||||||
let (opcode, cycles) = parameter_bytes.parse_opcode();
|
let (opcode, cycles) = parameter_bytes.parse_opcode();
|
||||||
@ -941,6 +951,8 @@ impl CPU {
|
|||||||
}
|
}
|
||||||
self.increment_cycles(cycles);
|
self.increment_cycles(cycles);
|
||||||
self.exec(opcode, bus);
|
self.exec(opcode, bus);
|
||||||
|
} else if self.is_halted {
|
||||||
|
self.increment_cycles(Cycles(1));
|
||||||
}
|
}
|
||||||
let cycles_end = self.get_cycles();
|
let cycles_end = self.get_cycles();
|
||||||
self.set_last_op_cycles(cycles_start, cycles_end);
|
self.set_last_op_cycles(cycles_start, cycles_end);
|
||||||
@ -1767,21 +1779,24 @@ impl CPU {
|
|||||||
// Enable interrupts
|
// Enable interrupts
|
||||||
Opcode::EI => {
|
Opcode::EI => {
|
||||||
self.registers.increment(Register::PC, 1);
|
self.registers.increment(Register::PC, 1);
|
||||||
bus.write(INTERRUPT_ENABLE_ADDRESS, 0xFF); // Disable all interrupts
|
self.ime = true;
|
||||||
|
// bus.write(INTERRUPT_MASTER_ENABLE_ADDRESS, 0xFF); // Enable all interrupts
|
||||||
},
|
},
|
||||||
// Disable interrupts
|
// Disable interrupts
|
||||||
Opcode::DI => {
|
Opcode::DI => {
|
||||||
self.registers.increment(Register::PC, 1);
|
self.registers.increment(Register::PC, 1);
|
||||||
bus.write(INTERRUPT_ENABLE_ADDRESS, 0x00); // Disable all interrupts
|
self.ime = false;
|
||||||
|
// bus.write(INTERRUPT_MASTER_ENABLE_ADDRESS, 0x00); // Disable all interrupts
|
||||||
},
|
},
|
||||||
// Same as enabling interrupts and then executing RET
|
// Same as enabling interrupts and then executing RET
|
||||||
Opcode::RETI => {
|
Opcode::RETI => {
|
||||||
self.exec(Opcode::EI, bus);
|
self.exec(Opcode::EI, bus);
|
||||||
self.exec(Opcode::RET(OpcodeParameter::NoParam), bus);
|
self.exec(Opcode::RET(OpcodeParameter::NoParam), bus);
|
||||||
},
|
},
|
||||||
// WIP
|
// Don't execute instructions until an interrupt is requested
|
||||||
Opcode::HALT => {
|
Opcode::HALT => {
|
||||||
self.registers.increment(Register::PC, 1);
|
self.registers.increment(Register::PC, 1);
|
||||||
|
self.is_halted = true;
|
||||||
},
|
},
|
||||||
Opcode::STOP => {
|
Opcode::STOP => {
|
||||||
self.registers.increment(Register::PC, 2);
|
self.registers.increment(Register::PC, 2);
|
||||||
@ -1919,16 +1934,16 @@ mod tests {
|
|||||||
let mut bus = Bus::new();
|
let mut bus = Bus::new();
|
||||||
let addr = 0xFF00;
|
let addr = 0xFF00;
|
||||||
cpu.registers.set(Register::A, 0xF1);
|
cpu.registers.set(Register::A, 0xF1);
|
||||||
cpu.exec(Opcode::LD(OpcodeParameter::FF00plusU8_Register(4, Register::A)), &mut bus);
|
cpu.exec(Opcode::LD(OpcodeParameter::FF00plusU8_Register(0x42, Register::A)), &mut bus);
|
||||||
assert_eq!(bus.read(addr + 4), 0xF1);
|
assert_eq!(bus.read(addr + 0x42), 0xF1);
|
||||||
assert_eq!(cpu.registers.get(Register::PC), 0x102);
|
assert_eq!(cpu.registers.get(Register::PC), 0x102);
|
||||||
|
|
||||||
let mut cpu = CPU::new();
|
let mut cpu = CPU::new();
|
||||||
let mut bus = Bus::new();
|
let mut bus = Bus::new();
|
||||||
let addr = 0xFF00;
|
let addr = 0xFF00;
|
||||||
cpu.registers.set(Register::A, 0x00);
|
cpu.registers.set(Register::A, 0x00);
|
||||||
bus.write(addr + 4, 0xF1);
|
bus.write(addr + 0x42, 0xF1);
|
||||||
cpu.exec(Opcode::LD(OpcodeParameter::Register_FF00plusU8(Register::A, 4)), &mut bus);
|
cpu.exec(Opcode::LD(OpcodeParameter::Register_FF00plusU8(Register::A, 0x42)), &mut bus);
|
||||||
assert_eq!(cpu.registers.get(Register::A), 0xF1);
|
assert_eq!(cpu.registers.get(Register::A), 0xF1);
|
||||||
assert_eq!(cpu.registers.get(Register::PC), 0x102);
|
assert_eq!(cpu.registers.get(Register::PC), 0x102);
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@ pub struct Emulator {
|
|||||||
cpu: CPU,
|
cpu: CPU,
|
||||||
ppu: PPU,
|
ppu: PPU,
|
||||||
bus: Bus,
|
bus: Bus,
|
||||||
|
timer: Timer,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Emulator {
|
impl Emulator {
|
||||||
@ -17,6 +18,7 @@ impl Emulator {
|
|||||||
cpu: CPU::new(),
|
cpu: CPU::new(),
|
||||||
ppu: PPU::new(),
|
ppu: PPU::new(),
|
||||||
bus: Bus::new(),
|
bus: Bus::new(),
|
||||||
|
timer: Timer::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -32,7 +34,7 @@ impl Emulator {
|
|||||||
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);
|
||||||
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());
|
||||||
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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
64
src/ppu.rs
64
src/ppu.rs
@ -19,7 +19,7 @@ enum Pixel {
|
|||||||
struct ColorPalette(u8, u8, u8, u8);
|
struct ColorPalette(u8, u8, u8, u8);
|
||||||
|
|
||||||
pub enum LCDControl {
|
pub enum LCDControl {
|
||||||
DisplayEnable,
|
LCDEnable,
|
||||||
WindowTileMapAddress,
|
WindowTileMapAddress,
|
||||||
WindowEnable,
|
WindowEnable,
|
||||||
BackgroundWindowTileAddress,
|
BackgroundWindowTileAddress,
|
||||||
@ -51,20 +51,20 @@ pub const WIDTH: u32 = LCD_WIDTH;
|
|||||||
pub const HEIGHT: u32 = LCD_HEIGHT;
|
pub const HEIGHT: u32 = LCD_HEIGHT;
|
||||||
pub const FRAME_BUFFER_LENGTH: u32 = WIDTH * HEIGHT;
|
pub const FRAME_BUFFER_LENGTH: u32 = WIDTH * HEIGHT;
|
||||||
|
|
||||||
const LCD_CONTROL_ADDRESS: u16 = 0xFF40;
|
pub const LCD_CONTROL_ADDRESS: u16 = 0xFF40;
|
||||||
const LCD_STATUS_ADDRESS: u16 = 0xFF41;
|
pub const LCD_STATUS_ADDRESS: u16 = 0xFF41;
|
||||||
|
|
||||||
const SCROLL_Y_ADDRESS: u16 = 0xFF42;
|
pub const SCROLL_Y_ADDRESS: u16 = 0xFF42;
|
||||||
const SCROLL_X_ADDRESS: u16 = 0xFF43;
|
pub const SCROLL_X_ADDRESS: u16 = 0xFF43;
|
||||||
const LCD_Y_ADDRESS: u16 = 0xFF44;
|
pub const LCD_Y_ADDRESS: u16 = 0xFF44;
|
||||||
const LCD_Y_COMPARE_ADDRESS: u16 = 0xFF45;
|
pub const LCD_Y_COMPARE_ADDRESS: u16 = 0xFF45;
|
||||||
const DMA_ADDRESS: u16 = 0xFF46;
|
pub const DMA_ADDRESS: u16 = 0xFF46;
|
||||||
const BACKGROUND_PALETTE_ADDRESS: u16 = 0xFF47;
|
pub const BACKGROUND_PALETTE_ADDRESS: u16 = 0xFF47;
|
||||||
const OBJECT_PALETTE_0_ADDRESS: u16 = 0xFF48;
|
pub const OBJECT_PALETTE_0_ADDRESS: u16 = 0xFF48;
|
||||||
const OBJECT_PALETTE_1_ADDRESS: u16 = 0xFF49;
|
pub const OBJECT_PALETTE_1_ADDRESS: u16 = 0xFF49;
|
||||||
const WINDOW_X_ADDRESS: u16 = 0xFF4A;
|
pub const WINDOW_X_ADDRESS: u16 = 0xFF4A;
|
||||||
const WINDOW_Y_ADDRESS: u16 = 0xFF4B;
|
pub const WINDOW_Y_ADDRESS: u16 = 0xFF4B;
|
||||||
const TILE_MAP_ADDRESS: u16 = 0x9800;
|
pub const TILE_MAP_ADDRESS: u16 = 0x9800;
|
||||||
|
|
||||||
pub struct PPU {
|
pub struct PPU {
|
||||||
cycles: Cycles,
|
cycles: Cycles,
|
||||||
@ -96,30 +96,32 @@ impl PPU {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn cycle(&mut self, bus: &mut Bus) {
|
pub fn cycle(&mut self, bus: &mut Bus) {
|
||||||
// Mode 1 Vertical blank
|
self.increment_cycles(Cycles(1));
|
||||||
if PPU::get_lcd_y(bus) >= 144 {
|
if !PPU::get_lcd_control(bus, LCDControl::LCDEnable) {
|
||||||
if PPU::get_lcd_y(bus) == 144 {
|
return;
|
||||||
bus.set_interrupt(Interrupt::VBlank, true);
|
|
||||||
PPU::set_lcd_status(bus, LCDStatus::ModeFlag(LCDStatusModeFlag::VBlank), true);
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
|
if PPU::get_lcd_y(bus) < 144 {
|
||||||
if self.cycles.0 == 0 {
|
if self.cycles.0 == 0 {
|
||||||
// Mode 2 OAM scan
|
// Mode 2 OAM scan
|
||||||
|
PPU::request_interrupt(bus, Interrupt::LCDSTAT);
|
||||||
PPU::set_lcd_status(bus, LCDStatus::ModeFlag(LCDStatusModeFlag::SearchingOAM), true);
|
PPU::set_lcd_status(bus, LCDStatus::ModeFlag(LCDStatusModeFlag::SearchingOAM), true);
|
||||||
} else if self.cycles.0 == 80 + 1 {
|
} else if self.cycles.0 == 80 + 1 {
|
||||||
// Mode 3 drawing pixel line. This could also last 289 cycles
|
// Mode 3 drawing pixel line. This could also last 289 cycles
|
||||||
bus.set_interrupt(Interrupt::LCDSTAT, true);
|
// PPU::request_interrupt(bus, Interrupt::LCDSTAT);
|
||||||
self.draw_line(bus);
|
self.draw_line(bus);
|
||||||
PPU::set_lcd_status(bus, LCDStatus::ModeFlag(LCDStatusModeFlag::TransferringToLCD), true);
|
PPU::set_lcd_status(bus, LCDStatus::ModeFlag(LCDStatusModeFlag::TransferringToLCD), true);
|
||||||
} else if self.cycles.0 == 80 + 172 + 1 {
|
} else if self.cycles.0 == 80 + 172 + 1 {
|
||||||
// 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
|
||||||
bus.set_interrupt(Interrupt::LCDSTAT, true);
|
PPU::request_interrupt(bus, Interrupt::LCDSTAT);
|
||||||
PPU::set_lcd_status(bus, LCDStatus::ModeFlag(LCDStatusModeFlag::HBlank), true);
|
PPU::set_lcd_status(bus, LCDStatus::ModeFlag(LCDStatusModeFlag::HBlank), true);
|
||||||
}
|
}
|
||||||
|
} else if PPU::get_lcd_y(bus) == 144 && self.cycles.0 == 0 {
|
||||||
|
// Mode 1 Vertical blank
|
||||||
|
PPU::request_interrupt(bus, Interrupt::VBlank);
|
||||||
|
PPU::set_lcd_status(bus, LCDStatus::ModeFlag(LCDStatusModeFlag::VBlank), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.increment_cycles(Cycles(1));
|
|
||||||
|
|
||||||
// Horizontal scan completed
|
// Horizontal scan completed
|
||||||
if self.cycles.0 > 456 {
|
if self.cycles.0 > 456 {
|
||||||
self.reset_cycles();
|
self.reset_cycles();
|
||||||
@ -129,7 +131,7 @@ impl PPU {
|
|||||||
let lyc_compare = PPU::get_lcd_y(bus) == bus.read(LCD_Y_COMPARE_ADDRESS);
|
let lyc_compare = PPU::get_lcd_y(bus) == bus.read(LCD_Y_COMPARE_ADDRESS);
|
||||||
if lyc_compare {
|
if lyc_compare {
|
||||||
PPU::set_lcd_status(bus, LCDStatus::LYCInterrupt, lyc_compare);
|
PPU::set_lcd_status(bus, LCDStatus::LYCInterrupt, lyc_compare);
|
||||||
bus.set_interrupt(Interrupt::LCDSTAT, true);
|
PPU::request_interrupt(bus, Interrupt::LCDSTAT);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Frame completed
|
// Frame completed
|
||||||
@ -139,6 +141,14 @@ impl PPU {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn request_interrupt(bus: &mut Bus, interrupt: Interrupt) {
|
||||||
|
if PPU::get_lcd_control(bus, LCDControl::LCDEnable) {
|
||||||
|
bus.set_interrupt_flag(interrupt, true);
|
||||||
|
} else {
|
||||||
|
println!("lcd off");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn get_lcd_y(bus: &Bus) -> u8 {
|
fn get_lcd_y(bus: &Bus) -> u8 {
|
||||||
bus.read(LCD_Y_ADDRESS)
|
bus.read(LCD_Y_ADDRESS)
|
||||||
}
|
}
|
||||||
@ -166,7 +176,7 @@ impl PPU {
|
|||||||
pub fn get_lcd_control(bus: &Bus, control: LCDControl) -> bool {
|
pub fn get_lcd_control(bus: &Bus, control: LCDControl) -> bool {
|
||||||
let byte = bus.read(LCD_CONTROL_ADDRESS);
|
let byte = bus.read(LCD_CONTROL_ADDRESS);
|
||||||
match control {
|
match control {
|
||||||
LCDControl::DisplayEnable => get_bit(byte, BitIndex::I7),
|
LCDControl::LCDEnable => get_bit(byte, BitIndex::I7),
|
||||||
LCDControl::WindowTileMapAddress => get_bit(byte, BitIndex::I6),
|
LCDControl::WindowTileMapAddress => get_bit(byte, BitIndex::I6),
|
||||||
LCDControl::WindowEnable => get_bit(byte, BitIndex::I5),
|
LCDControl::WindowEnable => get_bit(byte, BitIndex::I5),
|
||||||
LCDControl::BackgroundWindowTileAddress => get_bit(byte, BitIndex::I4),
|
LCDControl::BackgroundWindowTileAddress => get_bit(byte, BitIndex::I4),
|
||||||
@ -180,7 +190,7 @@ impl PPU {
|
|||||||
fn set_lcd_control(bus: &mut Bus, control: LCDControl, val: bool) {
|
fn set_lcd_control(bus: &mut Bus, control: LCDControl, val: bool) {
|
||||||
let mut byte = bus.read(LCD_CONTROL_ADDRESS);
|
let mut byte = bus.read(LCD_CONTROL_ADDRESS);
|
||||||
byte = match control {
|
byte = match control {
|
||||||
LCDControl::DisplayEnable => set_bit(byte, val, BitIndex::I7),
|
LCDControl::LCDEnable => set_bit(byte, val, BitIndex::I7),
|
||||||
LCDControl::WindowTileMapAddress => set_bit(byte, val, BitIndex::I6),
|
LCDControl::WindowTileMapAddress => set_bit(byte, val, BitIndex::I6),
|
||||||
LCDControl::WindowEnable => set_bit(byte, val, BitIndex::I5),
|
LCDControl::WindowEnable => set_bit(byte, val, BitIndex::I5),
|
||||||
LCDControl::BackgroundWindowTileAddress => set_bit(byte, val, BitIndex::I4),
|
LCDControl::BackgroundWindowTileAddress => set_bit(byte, val, BitIndex::I4),
|
||||||
|
47
src/timer.rs
47
src/timer.rs
@ -10,45 +10,66 @@ pub const TIMER_COUNTER_ADDRESS: u16 = 0xFF05;
|
|||||||
pub const TIMER_MODULO_ADDRESS: u16 = 0xFF06;
|
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,
|
||||||
|
}
|
||||||
|
|
||||||
impl Timer {
|
impl Timer {
|
||||||
|
|
||||||
pub fn do_cycles(bus: &mut Bus, cycles: Cycles) {
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
cycles: Cycles(0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn increment_cycles(&mut self, cycles: Cycles) {
|
||||||
|
self.cycles.0 += cycles.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reset_cycles(&mut self) {
|
||||||
|
self.cycles.0 = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn do_cycles(&mut self, bus: &mut Bus, cycles: Cycles) {
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
while count < cycles.to_t() {
|
while count < cycles.to_t() {
|
||||||
Timer::cycle(bus);
|
self.cycle(bus);
|
||||||
count += 1;
|
count += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cycle(bus: &mut Bus) {
|
fn cycle(&mut self, bus: &mut Bus) {
|
||||||
let div = bus.read(TIMER_DIVIDER_REGISTER_ADDRESS);
|
let div = bus.read(TIMER_DIVIDER_REGISTER_ADDRESS);
|
||||||
bus.write(TIMER_DIVIDER_REGISTER_ADDRESS, div.wrapping_add(1));
|
bus.write(TIMER_DIVIDER_REGISTER_ADDRESS, div.wrapping_add(1));
|
||||||
|
|
||||||
if Timer::is_timer_enabled(bus) {
|
if Timer::is_timer_enabled(bus) {
|
||||||
let tima = bus.read(TIMER_COUNTER_ADDRESS);
|
let tima = bus.read(TIMER_COUNTER_ADDRESS);
|
||||||
let tima_increment = Timer::get_tima_increment(bus);
|
let tima_rate = Timer::get_tima_rate(bus);
|
||||||
if tima.checked_add(tima_increment) == None {
|
if self.cycles.0 >= tima_rate {
|
||||||
|
if tima.checked_add(1) == None {
|
||||||
bus.write(TIMER_COUNTER_ADDRESS, bus.read(TIMER_MODULO_ADDRESS));
|
bus.write(TIMER_COUNTER_ADDRESS, bus.read(TIMER_MODULO_ADDRESS));
|
||||||
bus.set_interrupt(Interrupt::Timer, true);
|
bus.set_interrupt_flag(Interrupt::Timer, true);
|
||||||
} else {
|
} else {
|
||||||
bus.write(TIMER_COUNTER_ADDRESS, tima.wrapping_add(tima_increment));
|
bus.write(TIMER_COUNTER_ADDRESS, tima.wrapping_add(1));
|
||||||
|
}
|
||||||
|
self.reset_cycles();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.increment_cycles(Cycles(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
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_increment(bus: &Bus) -> u8 {
|
fn get_tima_rate(bus: &Bus) -> usize {
|
||||||
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 => (4096 as u16 / 1026 as u16 / 4 as u16).to_be_bytes()[1],
|
0b00 => 16,
|
||||||
0b01 => (4096 as u16 / 16 as u16 / 4 as u16).to_be_bytes()[1],
|
0b01 => 64,
|
||||||
0b10 => (4096 as u16 / 64 as u16 / 4 as u16).to_be_bytes()[1],
|
0b10 => 256,
|
||||||
0b11 => (4096 as u16 / 256 as u16 / 4 as u16).to_be_bytes()[1],
|
0b11 => 1024,
|
||||||
_ => 1,
|
_ => 1,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user