From 6f3fd9b2199cba775328b76c2455e88c10f78b67 Mon Sep 17 00:00:00 2001 From: Alex Janka Date: Fri, 24 Feb 2023 10:41:09 +1100 Subject: [PATCH] mmio interrupts --- src/processor/memory.rs | 161 ++++++++++++++--------------- src/processor/memory/interrupts.rs | 77 ++++++++++++++ src/processor/mod.rs | 2 - 3 files changed, 152 insertions(+), 88 deletions(-) create mode 100644 src/processor/memory/interrupts.rs diff --git a/src/processor/memory.rs b/src/processor/memory.rs index 98bad65..12a992d 100644 --- a/src/processor/memory.rs +++ b/src/processor/memory.rs @@ -1,13 +1,14 @@ -use self::mmio::{Apu, Gpu, Joypad, Serial, Timer}; pub use self::rom::Rom; -use crate::{ - processor::SplitRegister, - util::{clear_bit, set_bit, set_or_clear_bit}, - verbose_println, Cpu, +use self::{ + interrupts::Interrupt, + mmio::{Apu, Gpu, Joypad, Serial, Timer}, }; +use crate::{processor::SplitRegister, verbose_println, Cpu}; use gilrs::Gilrs; use minifb::{Key, Window}; +mod interrupts; +pub use interrupts::Interrupts; pub mod mmio; pub(crate) mod rom; @@ -20,11 +21,10 @@ pub struct Memory { ram: [u8; 8192], switchable_ram: [u8; 8192], cpu_ram: [u8; 128], - interrupts: u8, + interrupts: Interrupts, pub(super) ime: bool, pub(super) ime_scheduled: u8, - io: [u8; 76], - pub(super) user_mode: bool, + dma_addr: u8, joypad: Joypad, gpu: Gpu, apu: Apu, @@ -51,11 +51,10 @@ impl Memory { ram: [0x0; 8192], switchable_ram: [0x0; 8192], cpu_ram: [0x0; 128], - interrupts: 0x0, + interrupts: Interrupts::default(), ime: false, ime_scheduled: 0x0, - io: [0xFF; 76], - user_mode: false, + dma_addr: 0xFF, joypad: Joypad::default(), gpu: Gpu::new(window, enable_tile_window), apu: Apu::init_default(), @@ -87,7 +86,7 @@ impl Memory { 0xFF00..0xFF4C => self.get_io(address), 0xFF4C..0xFF80 => 0xFF, 0xFF80..0xFFFF => self.cpu_ram[(address - 0xFF80) as usize], - 0xFFFF => self.interrupts, + 0xFFFF => self.interrupts.get_enable_register(), } } @@ -110,7 +109,7 @@ impl Memory { 0xFFFF => { verbose_println!("interrupts set to {:#b}", data); verbose_println!(" / {:#X}", data); - self.interrupts = data; + self.interrupts.set_enable_register(data); } } } @@ -125,6 +124,7 @@ impl Memory { 0xFF05 => self.timers.get_tima(), 0xFF06 => self.timers.get_tma(), 0xFF07 => self.timers.get_timer_control(), + 0xFF0F => self.interrupts.get_flag_register(), 0xFF10..0xFF40 => self.apu.get_register(address), 0xFF40 => self.gpu.get_lcdc(), 0xFF41 => self.gpu.get_lcd_status(), @@ -132,69 +132,62 @@ impl Memory { 0xFF43 => self.gpu.get_scx(), 0xFF44 => self.gpu.get_ly(), 0xFF45 => self.gpu.get_lyc(), + 0xFF46 => self.dma_addr, 0xFF47 => self.gpu.get_bg_palette(), 0xFF48 => self.gpu.get_obj_palette_0(), 0xFF49 => self.gpu.get_obj_palette_1(), 0xFF4A => self.gpu.get_wy(), 0xFF4B => self.gpu.get_wx(), - _ => self.io[(address - 0xFF00) as usize], + 0xFF03 | 0xFF08..0xFF0F => 0xFF, + 0x0..0xFF00 | 0xFF4C..=0xFFFF => panic!("passed wrong address to get_io"), } } fn set_io(&mut self, address: Address, data: u8) { // range: 0xFF00 - 0xFF4B inclusive - let addr_l = (address - 0xFF00) as usize; - if !self.user_mode { - self.io[addr_l] = data; - } else { - match address { - 0xFF00 => { - // joypad - self.joypad.mmio_write(data); - } - 0xFF01 => self.serial.update_queued(data), - 0xFF02 => self.serial.update_control(data), - 0xFF04 => self.timers.update_div(), - 0xFF05 => self.timers.update_tima(data), - 0xFF06 => self.timers.update_tma(data), - 0xFF07 => self.timers.update_timer_control(data), - 0xFF0F => self.masked_io(addr_l, data, 0b11111), - 0xFF10..0xFF40 => self.apu.mmio_write(address, data), - 0xFF40 => self.gpu.update_lcdc(data), - 0xFF41 => self.gpu.update_lcd_status(data), - 0xFF42 => self.gpu.update_scy(data), - 0xFF43 => self.gpu.update_scx(data), - 0xFF45 => self.gpu.update_lyc(data), - 0xFF46 => { - if data > 0xDF { - panic!("dma transfer out of bounds: {data:#X}"); - } - self.io[addr_l] = data; - let mut addr: u16 = 0x0; - addr.set_high(data); - for l in 0x0..0xA0 { - addr.set_low(l); - self.gpu.oam.data[l as usize] = self.get(addr); - } - } - 0xFF47 => self.gpu.update_bg_palette(data), - 0xFF48 => self.gpu.update_obj_palette_0(data), - 0xFF49 => self.gpu.update_obj_palette_1(data), - 0xFF4A => self.gpu.update_wy(data), - 0xFF4B => self.gpu.update_wx(data), - 0xFF03 | 0xFF08..0xFF0F | 0xFF44 => { - // read-only addresses - println!("BANNED write: {data:#X} to {address:#X}"); - } - 0x0..0xFF00 | 0xFF4C..=u16::MAX => panic!("passed wrong address to set_io"), + match address { + 0xFF00 => { + // joypad + self.joypad.mmio_write(data); } + 0xFF01 => self.serial.update_queued(data), + 0xFF02 => self.serial.update_control(data), + 0xFF04 => self.timers.update_div(), + 0xFF05 => self.timers.update_tima(data), + 0xFF06 => self.timers.update_tma(data), + 0xFF07 => self.timers.update_timer_control(data), + 0xFF0F => self.interrupts.set_flag_register(data), + 0xFF10..0xFF40 => self.apu.mmio_write(address, data), + 0xFF40 => self.gpu.update_lcdc(data), + 0xFF41 => self.gpu.update_lcd_status(data), + 0xFF42 => self.gpu.update_scy(data), + 0xFF43 => self.gpu.update_scx(data), + 0xFF45 => self.gpu.update_lyc(data), + 0xFF46 => { + if data > 0xDF { + panic!("dma transfer out of bounds: {data:#X}"); + } + self.dma_addr = data; + let mut addr: u16 = 0x0; + addr.set_high(data); + for l in 0x0..0xA0 { + addr.set_low(l); + self.gpu.oam.data[l as usize] = self.get(addr); + } + } + 0xFF47 => self.gpu.update_bg_palette(data), + 0xFF48 => self.gpu.update_obj_palette_0(data), + 0xFF49 => self.gpu.update_obj_palette_1(data), + 0xFF4A => self.gpu.update_wy(data), + 0xFF4B => self.gpu.update_wx(data), + 0xFF03 | 0xFF08..0xFF0F | 0xFF44 => { + // read-only addresses + println!("BANNED write: {data:#X} to {address:#X}"); + } + 0x0..0xFF00 | 0xFF4C..=u16::MAX => panic!("passed wrong address to set_io"), } } - fn masked_io(&mut self, addr_l: usize, data: u8, mask: u8) { - self.io[addr_l] = masked_update(self.io[addr_l], data, mask); - } - pub fn update_pressed_keys(&mut self, keys: Vec, gamepads: &mut Gilrs) -> bool { self.joypad.update_pressed_keys(keys, gamepads) } @@ -231,42 +224,38 @@ impl Cpu { let steps = (machine_cycles as usize) * 4; let timer_return = self.memory.timers.tick(steps); + for _ in 0..timer_return.num_apu_ticks { self.memory.apu.div_apu_tick(); } - self.memory.set( - 0xFF0F, - set_or_clear_bit(self.memory.get(0xFF0F), 2, timer_return.timer_interrupt), - ); + self.memory + .interrupts + .set_interrupt(Interrupt::Timer, timer_return.timer_interrupt); self.memory.apu.tick(steps); - if self.memory.serial.tick(steps) { - self.memory.set(0xFF0F, set_bit(self.memory.get(0xFF0F), 3)); - } + + let serial_interrupt = self.memory.serial.tick(steps); + self.memory + .interrupts + .set_interrupt(Interrupt::Serial, serial_interrupt); let gpu_interrupts = self.memory.gpu.tick(steps); + self.memory + .interrupts + .set_interrupt(Interrupt::Vblank, gpu_interrupts.vblank); + self.memory + .interrupts + .set_interrupt(Interrupt::LcdStat, gpu_interrupts.lcd_stat); + if gpu_interrupts.vblank { - self.memory.set(0xFF0F, set_bit(self.memory.get(0xFF0F), 0)); - - if self + let joypad_interrupt = self .memory - .update_pressed_keys(self.memory.gpu.window.get_keys(), &mut self.gamepad_handler) - { - self.memory.set(0xFF0F, set_bit(self.memory.get(0xFF0F), 4)); - } - } - - if gpu_interrupts.lcd_stat { - self.memory.set(0xFF0F, set_bit(self.memory.get(0xFF0F), 1)); - } else { + .update_pressed_keys(self.memory.gpu.window.get_keys(), &mut self.gamepad_handler); self.memory - .set(0xFF0F, clear_bit(self.memory.get(0xFF0F), 1)); + .interrupts + .set_interrupt(Interrupt::Joypad, joypad_interrupt); } } } - -pub fn masked_update(current: u8, data: u8, mask: u8) -> u8 { - (current & (!mask)) | (data & mask) -} diff --git a/src/processor/memory/interrupts.rs b/src/processor/memory/interrupts.rs new file mode 100644 index 0000000..f69dc07 --- /dev/null +++ b/src/processor/memory/interrupts.rs @@ -0,0 +1,77 @@ +use crate::util::get_bit; + +#[derive(Default)] +struct InterruptRegister { + vblank: bool, + lcd_stat: bool, + timer: bool, + serial: bool, + joypad: bool, +} + +impl InterruptRegister { + fn as_register(&self) -> u8 { + 0b11100000 + | bool_to_shifted(self.vblank, 0) + | bool_to_shifted(self.lcd_stat, 1) + | bool_to_shifted(self.timer, 2) + | bool_to_shifted(self.serial, 3) + | bool_to_shifted(self.joypad, 4) + } + + fn set_from_bits(&mut self, data: u8) { + self.vblank = get_bit(data, 0); + self.lcd_stat = get_bit(data, 1); + self.timer = get_bit(data, 2); + self.serial = get_bit(data, 3); + self.joypad = get_bit(data, 4); + } +} + +fn bool_to_shifted(input: bool, shift: u8) -> u8 { + (if input { 1 } else { 0 }) << shift +} + +pub enum Interrupt { + Vblank, + LcdStat, + Timer, + Serial, + Joypad, +} + +#[derive(Default)] +pub struct Interrupts { + // 0xFFFF + enable_register: InterruptRegister, + // 0xFF0F + flag_register: InterruptRegister, +} + +impl Interrupts { + pub(super) fn get_enable_register(&self) -> u8 { + self.enable_register.as_register() + } + + pub(super) fn set_enable_register(&mut self, data: u8) { + self.enable_register.set_from_bits(data); + } + + pub(super) fn get_flag_register(&self) -> u8 { + self.flag_register.as_register() + } + + pub(super) fn set_flag_register(&mut self, data: u8) { + self.flag_register.set_from_bits(data); + } + + pub(super) fn set_interrupt(&mut self, interrupt: Interrupt, status: bool) { + match interrupt { + Interrupt::Vblank => self.flag_register.vblank = status, + Interrupt::LcdStat => self.flag_register.lcd_stat = status, + Interrupt::Timer => self.flag_register.timer = status, + Interrupt::Serial => self.flag_register.serial = status, + Interrupt::Joypad => self.flag_register.joypad = status, + } + } +} diff --git a/src/processor/mod.rs b/src/processor/mod.rs index 3846161..55cda48 100644 --- a/src/processor/mod.rs +++ b/src/processor/mod.rs @@ -76,9 +76,7 @@ impl Cpu { opcode, self.last_instruction_addr ); - self.memory.user_mode = true; let cycles = self.run_opcode(opcode); - self.memory.user_mode = false; self.increment_timers(cycles); }