mirror of
https://github.com/FranLMSP/rmg-001.git
synced 2024-11-23 02:01:32 +00:00
Refactor interrupts
This commit is contained in:
parent
aa88a6498e
commit
10848d28eb
47
Cargo.lock
generated
47
Cargo.lock
generated
@ -1268,12 +1268,6 @@ version = "0.2.4"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bb20dcc30536a1508e75d47dd0e399bb2fe7354dcf35cda9127f2bf1ed92e30e"
|
checksum = "bb20dcc30536a1508e75d47dd0e399bb2fe7354dcf35cda9127f2bf1ed92e30e"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "ppv-lite86"
|
|
||||||
version = "0.2.15"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ed0cfbc8191465bed66e1718596ee0b0b35d5ee1f41c5df2189d0fe8bde535ba"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro-crate"
|
name = "proc-macro-crate"
|
||||||
version = "0.1.5"
|
version = "0.1.5"
|
||||||
@ -1317,46 +1311,6 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rand"
|
|
||||||
version = "0.8.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8"
|
|
||||||
dependencies = [
|
|
||||||
"libc",
|
|
||||||
"rand_chacha",
|
|
||||||
"rand_core",
|
|
||||||
"rand_hc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rand_chacha"
|
|
||||||
version = "0.3.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
|
||||||
dependencies = [
|
|
||||||
"ppv-lite86",
|
|
||||||
"rand_core",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rand_core"
|
|
||||||
version = "0.6.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
|
|
||||||
dependencies = [
|
|
||||||
"getrandom",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rand_hc"
|
|
||||||
version = "0.3.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7"
|
|
||||||
dependencies = [
|
|
||||||
"rand_core",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "range-alloc"
|
name = "range-alloc"
|
||||||
version = "0.1.2"
|
version = "0.1.2"
|
||||||
@ -1422,7 +1376,6 @@ dependencies = [
|
|||||||
"env_logger",
|
"env_logger",
|
||||||
"log",
|
"log",
|
||||||
"pixels",
|
"pixels",
|
||||||
"rand",
|
|
||||||
"winit",
|
"winit",
|
||||||
"winit_input_helper",
|
"winit_input_helper",
|
||||||
]
|
]
|
||||||
|
24
src/bus.rs
24
src/bus.rs
@ -7,10 +7,14 @@ use crate::ppu::{
|
|||||||
PPU,
|
PPU,
|
||||||
DMA_ADDRESS,
|
DMA_ADDRESS,
|
||||||
};
|
};
|
||||||
use crate::cpu::{Interrupt};
|
|
||||||
use crate::timer::{Timer};
|
use crate::timer::{Timer};
|
||||||
use crate::joypad::{Joypad, JOYPAD_ADDRESS};
|
use crate::joypad::{Joypad, JOYPAD_ADDRESS};
|
||||||
use crate::sound::{Sound};
|
use crate::sound::{Sound};
|
||||||
|
use crate::interrupts::{
|
||||||
|
Interrupts,
|
||||||
|
INTERRUPT_ENABLE_ADDRESS,
|
||||||
|
INTERRUPT_FLAG_ADDRESS,
|
||||||
|
};
|
||||||
|
|
||||||
pub const BANK_ZERO: RangeInclusive<u16> = 0x0000..=0x3FFF;
|
pub const BANK_ZERO: RangeInclusive<u16> = 0x0000..=0x3FFF;
|
||||||
pub const BANK_SWITCHABLE: RangeInclusive<u16> = 0x4000..=0x7FFF;
|
pub const BANK_SWITCHABLE: RangeInclusive<u16> = 0x4000..=0x7FFF;
|
||||||
@ -23,9 +27,6 @@ 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 INTERRUPT_ENABLE_REGISTER: RangeInclusive<u16> = 0xFFFF..=0xFFFF;
|
|
||||||
pub const INTERRUPT_ENABLE_ADDRESS: u16 = 0xFFFF;
|
|
||||||
pub const INTERRUPT_FLAG_ADDRESS: u16 = 0xFF0F;
|
|
||||||
|
|
||||||
pub struct Bus {
|
pub struct Bus {
|
||||||
data: [u8; 0x10000],
|
data: [u8; 0x10000],
|
||||||
@ -34,6 +35,7 @@ pub struct Bus {
|
|||||||
pub joypad: Joypad,
|
pub joypad: Joypad,
|
||||||
pub timer: Timer,
|
pub timer: Timer,
|
||||||
pub sound: Sound,
|
pub sound: Sound,
|
||||||
|
pub interrupts: Interrupts,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Bus {
|
impl Bus {
|
||||||
@ -58,6 +60,7 @@ impl Bus {
|
|||||||
joypad: Joypad::new(),
|
joypad: Joypad::new(),
|
||||||
timer: Timer::new(),
|
timer: Timer::new(),
|
||||||
sound: Sound::new(),
|
sound: Sound::new(),
|
||||||
|
interrupts: Interrupts::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Hardware registers after the bootrom
|
// Hardware registers after the bootrom
|
||||||
@ -90,7 +93,7 @@ impl Bus {
|
|||||||
if BANK_ZERO.contains(&address) || BANK_SWITCHABLE.contains(&address) || EXTERNAL_RAM.contains(&address) {
|
if BANK_ZERO.contains(&address) || BANK_SWITCHABLE.contains(&address) || EXTERNAL_RAM.contains(&address) {
|
||||||
return self.rom.read(address);
|
return self.rom.read(address);
|
||||||
} else if address == INTERRUPT_ENABLE_ADDRESS || address == INTERRUPT_FLAG_ADDRESS {
|
} else if address == INTERRUPT_ENABLE_ADDRESS || address == INTERRUPT_FLAG_ADDRESS {
|
||||||
return 0b11100000 | self.data[address as usize];
|
return self.interrupts.read(address);
|
||||||
} else if VIDEO_RAM.contains(&address) {
|
} else if VIDEO_RAM.contains(&address) {
|
||||||
return self.ppu.read_vram(address);
|
return self.ppu.read_vram(address);
|
||||||
} else if SPRITE_ATTRIBUTE_TABLE.contains(&address) {
|
} else if SPRITE_ATTRIBUTE_TABLE.contains(&address) {
|
||||||
@ -118,6 +121,8 @@ impl Bus {
|
|||||||
|
|
||||||
if BANK_ZERO.contains(&address) || BANK_SWITCHABLE.contains(&address) || EXTERNAL_RAM.contains(&address) {
|
if BANK_ZERO.contains(&address) || BANK_SWITCHABLE.contains(&address) || EXTERNAL_RAM.contains(&address) {
|
||||||
self.rom.write(address, data);
|
self.rom.write(address, data);
|
||||||
|
} else if address == INTERRUPT_ENABLE_ADDRESS || address == INTERRUPT_FLAG_ADDRESS {
|
||||||
|
self.interrupts.write(address, data);
|
||||||
} else if WORK_RAM_1.contains(&address) || WORK_RAM_2.contains(&address) {
|
} else if WORK_RAM_1.contains(&address) || WORK_RAM_2.contains(&address) {
|
||||||
self.data[address as usize] = data;
|
self.data[address as usize] = data;
|
||||||
// Copy to the ECHO RAM
|
// Copy to the ECHO RAM
|
||||||
@ -156,18 +161,9 @@ impl Bus {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn force_write(&mut self, address: u16, data: u8) {
|
|
||||||
self.data[address as usize] = data;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn write_16bit(&mut self, address: u16, data: u16) {
|
pub fn write_16bit(&mut self, address: u16, data: u16) {
|
||||||
let bytes = data.to_le_bytes();
|
let bytes = data.to_le_bytes();
|
||||||
self.write(address, bytes[0]);
|
self.write(address, bytes[0]);
|
||||||
self.write(address.wrapping_add(1), bytes[1]);
|
self.write(address.wrapping_add(1), bytes[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_interrupt_flag(&mut self, interrupt: Interrupt, val: bool) {
|
|
||||||
let byte = self.read(INTERRUPT_FLAG_ADDRESS);
|
|
||||||
self.write(INTERRUPT_FLAG_ADDRESS, interrupt.set(byte, val));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
48
src/cpu.rs
48
src/cpu.rs
@ -8,7 +8,12 @@ use crate::utils::{
|
|||||||
sub_half_carry,
|
sub_half_carry,
|
||||||
add_half_carry_16bit,
|
add_half_carry_16bit,
|
||||||
};
|
};
|
||||||
use crate::bus::{Bus, INTERRUPT_ENABLE_ADDRESS, INTERRUPT_FLAG_ADDRESS};
|
use crate::bus::{Bus};
|
||||||
|
use crate::interrupts::{
|
||||||
|
Interrupt,
|
||||||
|
INTERRUPT_ENABLE_ADDRESS,
|
||||||
|
INTERRUPT_FLAG_ADDRESS,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub enum Register {
|
pub enum Register {
|
||||||
@ -53,45 +58,6 @@ pub enum FlagRegister {
|
|||||||
Carry, // Set if a carry was ocurrend from the last math operation or if register A is the smaller value when executing the CP instruction
|
Carry, // Set if a carry was ocurrend from the last math operation or if register A is the smaller value when executing the CP instruction
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
|
||||||
pub enum Interrupt {
|
|
||||||
VBlank,
|
|
||||||
LCDSTAT,
|
|
||||||
Timer,
|
|
||||||
Serial,
|
|
||||||
Joypad,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Interrupt {
|
|
||||||
fn get_bit_index(&self) -> BitIndex {
|
|
||||||
match self {
|
|
||||||
Interrupt::VBlank => BitIndex::I0,
|
|
||||||
Interrupt::LCDSTAT => BitIndex::I1,
|
|
||||||
Interrupt::Timer => BitIndex::I2,
|
|
||||||
Interrupt::Serial => BitIndex::I3,
|
|
||||||
Interrupt::Joypad => BitIndex::I4,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get(&self, byte: u8) -> bool {
|
|
||||||
get_bit(byte, self.get_bit_index())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set(&self, byte: u8, val: bool) -> u8 {
|
|
||||||
set_bit(byte, val, self.get_bit_index())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_vector(&self) -> u16 {
|
|
||||||
match self {
|
|
||||||
Interrupt::VBlank => 0x40,
|
|
||||||
Interrupt::LCDSTAT => 0x48,
|
|
||||||
Interrupt::Timer => 0x50,
|
|
||||||
Interrupt::Serial => 0x58,
|
|
||||||
Interrupt::Joypad => 0x60,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Registers {
|
pub struct Registers {
|
||||||
a: u8,
|
a: u8,
|
||||||
f: u8,
|
f: u8,
|
||||||
@ -914,7 +880,7 @@ impl CPU {
|
|||||||
|
|
||||||
pub fn handle_interrupt(&mut self, bus: &mut Bus, interrupt: Interrupt) {
|
pub fn handle_interrupt(&mut self, bus: &mut Bus, interrupt: Interrupt) {
|
||||||
// println!("Interrupt: {:?}", interrupt);
|
// println!("Interrupt: {:?}", interrupt);
|
||||||
bus.set_interrupt_flag(interrupt, false);
|
bus.interrupts.set(interrupt, false);
|
||||||
self.ime = false;
|
self.ime = false;
|
||||||
self.registers.decrement(Register::PC, 3);
|
self.registers.decrement(Register::PC, 3);
|
||||||
self.exec(Opcode::CALL(OpcodeParameter::U16(interrupt.get_vector())), bus);
|
self.exec(Opcode::CALL(OpcodeParameter::U16(interrupt.get_vector())), bus);
|
||||||
|
@ -2,7 +2,8 @@
|
|||||||
use winit_input_helper::WinitInputHelper;
|
use winit_input_helper::WinitInputHelper;
|
||||||
use winit::event::{VirtualKeyCode};
|
use winit::event::{VirtualKeyCode};
|
||||||
|
|
||||||
use crate::cpu::{CPU, Cycles, Interrupt};
|
use crate::cpu::{CPU, Cycles};
|
||||||
|
use crate::interrupts::Interrupt;
|
||||||
use crate::bus::Bus;
|
use crate::bus::Bus;
|
||||||
use crate::joypad::{Button};
|
use crate::joypad::{Button};
|
||||||
#[cfg(not(test))]
|
#[cfg(not(test))]
|
||||||
@ -99,7 +100,7 @@ impl Emulator {
|
|||||||
self.bus.joypad.release(Button::Select);
|
self.bus.joypad.release(Button::Select);
|
||||||
}
|
}
|
||||||
if change {
|
if change {
|
||||||
self.bus.set_interrupt_flag(Interrupt::Joypad, true);
|
self.bus.interrupts.request(Interrupt::Joypad);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -108,20 +109,8 @@ impl Emulator {
|
|||||||
while self.cpu.get_cycles().to_t().0 <= cpu_cycles.0 {
|
while self.cpu.get_cycles().to_t().0 <= cpu_cycles.0 {
|
||||||
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(cycles, frame_buffer);
|
self.bus.ppu.do_cycles(&mut self.bus.interrupts, cycles, frame_buffer);
|
||||||
if self.bus.ppu.get_interrupt(Interrupt::VBlank) {
|
self.bus.timer.do_cycles(&mut self.bus.interrupts, cycles);
|
||||||
self.bus.set_interrupt_flag(Interrupt::VBlank, true);
|
|
||||||
self.bus.ppu.set_interrupt(Interrupt::VBlank, false);
|
|
||||||
}
|
|
||||||
if self.bus.ppu.get_interrupt(Interrupt::LCDSTAT) {
|
|
||||||
self.bus.set_interrupt_flag(Interrupt::LCDSTAT, true);
|
|
||||||
self.bus.ppu.set_interrupt(Interrupt::LCDSTAT, false);
|
|
||||||
}
|
|
||||||
self.bus.timer.do_cycles(cycles);
|
|
||||||
if self.bus.timer.get_interrupt() {
|
|
||||||
self.bus.set_interrupt_flag(Interrupt::Timer, true);
|
|
||||||
self.bus.timer.set_interrupt(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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()));
|
||||||
@ -135,20 +124,8 @@ impl Emulator {
|
|||||||
while !exit {
|
while !exit {
|
||||||
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(cycles, &mut frame);
|
self.bus.ppu.do_cycles(&mut self.bus.interrupts, cycles, &mut frame);
|
||||||
if self.bus.ppu.get_interrupt(Interrupt::VBlank) {
|
self.bus.timer.do_cycles(&mut self.bus.interrupts, cycles);
|
||||||
self.bus.set_interrupt_flag(Interrupt::VBlank, true);
|
|
||||||
self.bus.ppu.set_interrupt(Interrupt::VBlank, false);
|
|
||||||
}
|
|
||||||
if self.bus.ppu.get_interrupt(Interrupt::LCDSTAT) {
|
|
||||||
self.bus.set_interrupt_flag(Interrupt::LCDSTAT, true);
|
|
||||||
self.bus.ppu.set_interrupt(Interrupt::LCDSTAT, false);
|
|
||||||
}
|
|
||||||
self.bus.timer.do_cycles(cycles);
|
|
||||||
if self.bus.timer.get_interrupt() {
|
|
||||||
self.bus.set_interrupt_flag(Interrupt::Timer, true);
|
|
||||||
self.bus.timer.set_interrupt(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
// exit = self.cpu.get_exec_calls_count() >= 1258895; // log 1
|
// exit = self.cpu.get_exec_calls_count() >= 1258895; // log 1
|
||||||
exit = self.cpu.get_exec_calls_count() >= 161502; // log 2
|
exit = self.cpu.get_exec_calls_count() >= 161502; // log 2
|
||||||
|
90
src/interrupts.rs
Normal file
90
src/interrupts.rs
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
use crate::utils::{
|
||||||
|
BitIndex,
|
||||||
|
get_bit,
|
||||||
|
set_bit,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const INTERRUPT_ENABLE_ADDRESS: u16 = 0xFFFF;
|
||||||
|
pub const INTERRUPT_FLAG_ADDRESS: u16 = 0xFF0F;
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub enum Interrupt {
|
||||||
|
VBlank,
|
||||||
|
LCDSTAT,
|
||||||
|
Timer,
|
||||||
|
Serial,
|
||||||
|
Joypad,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Interrupt {
|
||||||
|
fn get_bit_index(&self) -> BitIndex {
|
||||||
|
match self {
|
||||||
|
Interrupt::VBlank => BitIndex::I0,
|
||||||
|
Interrupt::LCDSTAT => BitIndex::I1,
|
||||||
|
Interrupt::Timer => BitIndex::I2,
|
||||||
|
Interrupt::Serial => BitIndex::I3,
|
||||||
|
Interrupt::Joypad => BitIndex::I4,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(&self, byte: u8) -> bool {
|
||||||
|
get_bit(byte, self.get_bit_index())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set(&self, byte: u8, val: bool) -> u8 {
|
||||||
|
set_bit(byte, val, self.get_bit_index())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_vector(&self) -> u16 {
|
||||||
|
match self {
|
||||||
|
Interrupt::VBlank => 0x40,
|
||||||
|
Interrupt::LCDSTAT => 0x48,
|
||||||
|
Interrupt::Timer => 0x50,
|
||||||
|
Interrupt::Serial => 0x58,
|
||||||
|
Interrupt::Joypad => 0x60,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Interrupts {
|
||||||
|
interrupt_enable: u8,
|
||||||
|
interrupt_flag: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Interrupts {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
interrupt_enable: 0,
|
||||||
|
interrupt_flag: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read(&self, address: u16) -> u8 {
|
||||||
|
let byte = match address {
|
||||||
|
INTERRUPT_ENABLE_ADDRESS => self.interrupt_enable,
|
||||||
|
INTERRUPT_FLAG_ADDRESS => self.interrupt_flag,
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
0b11100000 | byte
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write(&mut self, address: u16, data: u8) {
|
||||||
|
match address {
|
||||||
|
INTERRUPT_ENABLE_ADDRESS => self.interrupt_enable = data,
|
||||||
|
INTERRUPT_FLAG_ADDRESS => self.interrupt_flag = data,
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set(&mut self, interrupt: Interrupt, val: bool) {
|
||||||
|
self.interrupt_flag = interrupt.set(self.interrupt_flag, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(&self, interrupt: Interrupt) -> bool {
|
||||||
|
interrupt.get(self.interrupt_flag)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn request(&mut self, interrupt: Interrupt) {
|
||||||
|
self.set(interrupt, true)
|
||||||
|
}
|
||||||
|
}
|
@ -5,6 +5,7 @@ pub mod timer;
|
|||||||
pub mod sound;
|
pub mod sound;
|
||||||
pub mod rom;
|
pub mod rom;
|
||||||
pub mod bus;
|
pub mod bus;
|
||||||
|
pub mod interrupts;
|
||||||
pub mod joypad;
|
pub mod joypad;
|
||||||
pub mod emulator;
|
pub mod emulator;
|
||||||
pub mod render;
|
pub mod render;
|
||||||
|
39
src/ppu.rs
39
src/ppu.rs
@ -4,7 +4,8 @@ use crate::utils::{
|
|||||||
set_bit,
|
set_bit,
|
||||||
};
|
};
|
||||||
use crate::bus::{SPRITE_ATTRIBUTE_TABLE};
|
use crate::bus::{SPRITE_ATTRIBUTE_TABLE};
|
||||||
use crate::cpu::{Cycles, Interrupt};
|
use crate::cpu::{Cycles};
|
||||||
|
use crate::interrupts::{Interrupts, Interrupt};
|
||||||
|
|
||||||
pub const LCD_WIDTH: u32 = 160;
|
pub const LCD_WIDTH: u32 = 160;
|
||||||
pub const LCD_HEIGHT: u32 = 144;
|
pub const LCD_HEIGHT: u32 = 144;
|
||||||
@ -210,8 +211,6 @@ impl Sprite {
|
|||||||
|
|
||||||
pub struct PPU {
|
pub struct PPU {
|
||||||
state: bool,
|
state: bool,
|
||||||
vblank_request: bool,
|
|
||||||
lcdstat_request: bool,
|
|
||||||
background_priority: bool,
|
background_priority: bool,
|
||||||
window_enable: bool,
|
window_enable: bool,
|
||||||
lcd_enable: bool,
|
lcd_enable: bool,
|
||||||
@ -239,8 +238,6 @@ impl PPU {
|
|||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
state: false,
|
state: false,
|
||||||
vblank_request: false,
|
|
||||||
lcdstat_request: false,
|
|
||||||
background_priority: false,
|
background_priority: false,
|
||||||
window_enable: false,
|
window_enable: false,
|
||||||
window_drawn: false,
|
window_drawn: false,
|
||||||
@ -265,22 +262,6 @@ impl PPU {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_interrupt(&mut self, interrupt: Interrupt, val: bool) {
|
|
||||||
match interrupt {
|
|
||||||
Interrupt::VBlank => self.vblank_request = val,
|
|
||||||
Interrupt::LCDSTAT => self.lcdstat_request = val,
|
|
||||||
_ => unreachable!(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_interrupt(&self, interrupt: Interrupt) -> bool {
|
|
||||||
match interrupt {
|
|
||||||
Interrupt::VBlank => self.vblank_request,
|
|
||||||
Interrupt::LCDSTAT => self.lcdstat_request,
|
|
||||||
_ => unreachable!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_io_register(address: u16) -> bool {
|
pub fn is_io_register(address: u16) -> bool {
|
||||||
address >= 0xFF40 && address <= 0xFF4B
|
address >= 0xFF40 && address <= 0xFF4B
|
||||||
}
|
}
|
||||||
@ -345,7 +326,7 @@ impl PPU {
|
|||||||
self.cycles.0 += cycles.0;
|
self.cycles.0 += cycles.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn do_cycles(&mut self, cycles: Cycles, frame_buffer: &mut [u8]) {
|
pub fn do_cycles(&mut self, interrupts: &mut Interrupts, cycles: Cycles, frame_buffer: &mut [u8]) {
|
||||||
if !self.lcd_enable {
|
if !self.lcd_enable {
|
||||||
self.increment_cycles(cycles);
|
self.increment_cycles(cycles);
|
||||||
return;
|
return;
|
||||||
@ -355,7 +336,7 @@ impl PPU {
|
|||||||
if self.cycles.0 <= 80 && !self.get_lcd_status(LCDStatus::ModeFlag(LCDStatusModeFlag::SearchingOAM)) {
|
if self.cycles.0 <= 80 && !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();
|
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 && self.cycles.0 <= 80 + 172 {
|
||||||
// Mode 3 drawing pixel line. This could also last 289 cycles
|
// Mode 3 drawing pixel line. This could also last 289 cycles
|
||||||
@ -367,13 +348,13 @@ impl PPU {
|
|||||||
} 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 + 172 && self.cycles.0 <= 80 + 172 + 204 && !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();
|
self.stat_interrupt(interrupts);
|
||||||
}
|
}
|
||||||
} else if self.lcd_y >= 144 && !self.get_lcd_status(LCDStatus::ModeFlag(LCDStatusModeFlag::VBlank)) {
|
} else if self.lcd_y >= 144 && !self.get_lcd_status(LCDStatus::ModeFlag(LCDStatusModeFlag::VBlank)) {
|
||||||
// Mode 1 Vertical blank
|
// Mode 1 Vertical blank
|
||||||
self.set_lcd_status(LCDStatus::ModeFlag(LCDStatusModeFlag::VBlank), true);
|
self.set_lcd_status(LCDStatus::ModeFlag(LCDStatusModeFlag::VBlank), true);
|
||||||
self.set_interrupt(Interrupt::VBlank, true);
|
interrupts.request(Interrupt::VBlank);
|
||||||
self.stat_interrupt();
|
self.stat_interrupt(interrupts);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.increment_cycles(cycles);
|
self.increment_cycles(cycles);
|
||||||
@ -393,11 +374,11 @@ impl PPU {
|
|||||||
self.window_y_counter = 0;
|
self.window_y_counter = 0;
|
||||||
self.lcd_y = 0;
|
self.lcd_y = 0;
|
||||||
}
|
}
|
||||||
self.stat_interrupt();
|
self.stat_interrupt(interrupts);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stat_interrupt(&mut self) {
|
fn stat_interrupt(&mut self, interrupts: &mut Interrupts) {
|
||||||
let prev_state = self.state;
|
let prev_state = self.state;
|
||||||
let lyc_compare = self.lcd_y == self.get_register(LCD_Y_COMPARE_ADDRESS);
|
let lyc_compare = self.lcd_y == self.get_register(LCD_Y_COMPARE_ADDRESS);
|
||||||
self.set_lcd_status(LCDStatus::LYCFlag, lyc_compare);
|
self.set_lcd_status(LCDStatus::LYCFlag, lyc_compare);
|
||||||
@ -417,7 +398,7 @@ impl PPU {
|
|||||||
self.get_lcd_status(LCDStatus::ModeFlag(LCDStatusModeFlag::VBlank))
|
self.get_lcd_status(LCDStatus::ModeFlag(LCDStatusModeFlag::VBlank))
|
||||||
);
|
);
|
||||||
if self.state && !prev_state {
|
if self.state && !prev_state {
|
||||||
self.set_interrupt(Interrupt::LCDSTAT, self.state);
|
interrupts.request(Interrupt::LCDSTAT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
42
src/timer.rs
42
src/timer.rs
@ -1,4 +1,5 @@
|
|||||||
use crate::cpu::{Cycles};
|
use crate::cpu::{Cycles};
|
||||||
|
use crate::interrupts::{Interrupt, Interrupts};
|
||||||
use crate::utils::{
|
use crate::utils::{
|
||||||
BitIndex,
|
BitIndex,
|
||||||
get_bit,
|
get_bit,
|
||||||
@ -13,7 +14,6 @@ pub struct Timer {
|
|||||||
divider: u16,
|
divider: u16,
|
||||||
prev_result: bool,
|
prev_result: bool,
|
||||||
is_enabled: bool,
|
is_enabled: bool,
|
||||||
interrupt: bool,
|
|
||||||
control: u8,
|
control: u8,
|
||||||
io_registers: [u8; 4],
|
io_registers: [u8; 4],
|
||||||
}
|
}
|
||||||
@ -25,7 +25,6 @@ impl Timer {
|
|||||||
divider: 0,
|
divider: 0,
|
||||||
control: 0,
|
control: 0,
|
||||||
prev_result: false,
|
prev_result: false,
|
||||||
interrupt: false,
|
|
||||||
is_enabled: false,
|
is_enabled: false,
|
||||||
io_registers: [0; 4],
|
io_registers: [0; 4],
|
||||||
}
|
}
|
||||||
@ -62,29 +61,21 @@ impl Timer {
|
|||||||
self.io_registers[(address - 0xFF04) as usize] = data;
|
self.io_registers[(address - 0xFF04) as usize] = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_interrupt(&self) -> bool {
|
|
||||||
self.interrupt
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_interrupt(&mut self, val: bool) {
|
|
||||||
self.interrupt = val
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn read_divider(&self) -> u8 {
|
pub fn read_divider(&self) -> u8 {
|
||||||
self.divider.to_be_bytes()[0]
|
self.divider.to_be_bytes()[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn do_cycles(&mut self, 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;
|
||||||
while count < cycles.0 {
|
while count < cycles.0 {
|
||||||
self.cycle();
|
self.cycle(interrupts);
|
||||||
count += 1;
|
count += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cycle(&mut self) {
|
fn cycle(&mut self, interrupts: &mut Interrupts) {
|
||||||
self.divider = self.divider.wrapping_add(1);
|
self.divider = self.divider.wrapping_add(1);
|
||||||
|
|
||||||
let result = self.is_enabled && self.get_tima_rate();
|
let result = self.is_enabled && self.get_tima_rate();
|
||||||
@ -93,7 +84,7 @@ impl Timer {
|
|||||||
let tima = self.get_register(TIMER_COUNTER_ADDRESS).wrapping_add(1);
|
let tima = self.get_register(TIMER_COUNTER_ADDRESS).wrapping_add(1);
|
||||||
if tima == 0 {
|
if tima == 0 {
|
||||||
self.set_register(TIMER_COUNTER_ADDRESS, self.get_register(TIMER_MODULO_ADDRESS));
|
self.set_register(TIMER_COUNTER_ADDRESS, self.get_register(TIMER_MODULO_ADDRESS));
|
||||||
self.interrupt = true;
|
interrupts.request(Interrupt::Timer);
|
||||||
} else {
|
} else {
|
||||||
self.set_register(TIMER_COUNTER_ADDRESS, tima);
|
self.set_register(TIMER_COUNTER_ADDRESS, tima);
|
||||||
}
|
}
|
||||||
@ -125,50 +116,53 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_tima_increment() {
|
fn test_tima_increment() {
|
||||||
let mut timer = Timer::new();
|
let mut timer = Timer::new();
|
||||||
|
let mut interrupts = Interrupts::new();
|
||||||
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(Cycles(1));
|
timer.do_cycles(&mut interrupts, Cycles(1));
|
||||||
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!(timer.get_interrupt(), false);
|
assert_eq!(interrupts.get(Interrupt::Timer), false);
|
||||||
|
|
||||||
timer.do_cycles(Cycles(7));
|
timer.do_cycles(&mut interrupts, Cycles(7));
|
||||||
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!(timer.get_interrupt(), false);
|
assert_eq!(interrupts.get(Interrupt::Timer), false);
|
||||||
timer.do_cycles(Cycles(1));
|
timer.do_cycles(&mut interrupts, Cycles(1));
|
||||||
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);
|
||||||
assert_eq!(timer.get_interrupt(), false);
|
assert_eq!(interrupts.get(Interrupt::Timer), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_tima_overflow() {
|
fn test_tima_overflow() {
|
||||||
let mut timer = Timer::new();
|
let mut timer = Timer::new();
|
||||||
|
let mut interrupts = Interrupts::new();
|
||||||
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(Cycles(9));
|
timer.do_cycles(&mut interrupts, Cycles(9));
|
||||||
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!(timer.get_interrupt(), true);
|
assert_eq!(interrupts.get(Interrupt::Timer), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_timer_enable() {
|
fn test_timer_enable() {
|
||||||
let mut timer = Timer::new();
|
let mut timer = Timer::new();
|
||||||
|
let mut interrupts = Interrupts::new();
|
||||||
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(Cycles(1));
|
timer.do_cycles(&mut interrupts, Cycles(1));
|
||||||
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(Cycles(1));
|
timer.do_cycles(&mut interrupts, Cycles(1));
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user