diff --git a/src/bus.rs b/src/bus.rs index c0f2c29..482f9ff 100644 --- a/src/bus.rs +++ b/src/bus.rs @@ -1,21 +1,14 @@ -use std::rc::Rc; -use std::cell::RefCell; use std::ops::RangeInclusive; use crate::utils::{ - get_bit, - BitIndex, join_bytes }; use crate::rom::ROM; use crate::ppu::{ PPU, - LCD_STATUS_ADDRESS, - LCD_CONTROL_ADDRESS, - LCD_Y_ADDRESS, DMA_ADDRESS, }; use crate::cpu::{Interrupt}; -use crate::timer::{Timer, TIMER_DIVIDER_REGISTER_ADDRESS}; +use crate::timer::{Timer}; use crate::joypad::{Joypad, JOYPAD_ADDRESS}; pub const BANK_ZERO: RangeInclusive = 0x0000..=0x3FFF; @@ -36,13 +29,13 @@ pub const INTERRUPT_FLAG_ADDRESS: u16 = 0xFF0F; pub struct Bus { game_rom: ROM, data: [u8; 0x10000], - ppu: Rc>, - joypad: Rc>, - timer: Rc>, + pub ppu: PPU, + pub joypad: Joypad, + pub timer: Timer, } impl Bus { - pub fn new(ppu: Rc>, joypad: Rc>, timer: Rc>) -> Self { + pub fn new() -> Self { let args: Vec = std::env::args().collect(); if args.len() < 2 { println!("Please, specify a ROM file"); @@ -82,9 +75,9 @@ impl Bus { Self { data, game_rom, - ppu, - joypad, - timer, + ppu: PPU::new(), + joypad: Joypad::new(), + timer: Timer::new(), } } @@ -94,13 +87,15 @@ impl Bus { } else if address == INTERRUPT_ENABLE_ADDRESS || address == INTERRUPT_FLAG_ADDRESS { return 0b11100000 | self.data[address as usize]; } else if VIDEO_RAM.contains(&address) { - return self.ppu.borrow().read_vram(address); + return self.ppu.read_vram(address); } else if SPRITE_ATTRIBUTE_TABLE.contains(&address) { - return self.ppu.borrow().read_oam(address); + return self.ppu.read_oam(address); + } else if PPU::is_io_register(address) { + return self.ppu.get_register(address); } else if address == JOYPAD_ADDRESS { - return self.joypad.borrow().read(self.data[address as usize]); - } else if address == TIMER_DIVIDER_REGISTER_ADDRESS { - return self.timer.borrow().read_divider(); + return self.joypad.read(self.data[address as usize]); + } else if Timer::is_io_register(address) { + return self.timer.get_register(address); } self.data[address as usize] } @@ -123,43 +118,30 @@ impl Bus { self.data[(ECHO_RAM.min().unwrap() + (address - WORK_RAM_1.min().unwrap())) as usize] = data; } } else if EXTERNAL_RAM.contains(&address) { - // self.game_rom.write(address, data); + self.game_rom.write(address, data); } else if ECHO_RAM.contains(&address) { self.data[address as usize] = data; self.data[(WORK_RAM_1.min().unwrap() + (address - ECHO_RAM.min().unwrap())) as usize] = data; // Copy to the working RAM - } else if address == TIMER_DIVIDER_REGISTER_ADDRESS { - self.timer.borrow_mut().reset(); - } else if address == LCD_CONTROL_ADDRESS { - self.data[address as usize] = data; - // Check if LCD is being turned on or off - if (get_bit(data, BitIndex::I7) && !get_bit(self.data[address as usize], BitIndex::I7)) || - !get_bit(data, BitIndex::I7) { - self.data[LCD_Y_ADDRESS as usize] = 0x00; - // Set Hblank - let byte = self.data[LCD_STATUS_ADDRESS as usize]; - self.data[LCD_STATUS_ADDRESS as usize] = byte & 0b11111100; - } - } else if address == LCD_Y_ADDRESS { - // println!("Write to LCD_Y not allowed"); - } else if address == LCD_STATUS_ADDRESS { - let byte = self.data[address as usize]; - self.data[address as usize] = (data & 0b11111000) | (byte & 0b00000111); + } else if Timer::is_io_register(address) { + self.timer.set_register(address, data); } else if address == JOYPAD_ADDRESS { let byte = self.data[address as usize]; self.data[address as usize] = (data & 0b11110000) | (byte & 0b00001111); } else if VIDEO_RAM.contains(&address) { - return self.ppu.borrow_mut().write_vram(address, data); + return self.ppu.write_vram(address, data); } else if SPRITE_ATTRIBUTE_TABLE.contains(&address) { - return self.ppu.borrow_mut().write_oam(address, data); + return self.ppu.write_oam(address, data); } else if address == DMA_ADDRESS { self.data[address as usize] = data; let source = (data as u16) * 0x100; let mut count: u16 = 0; let oam_addr = SPRITE_ATTRIBUTE_TABLE.min().unwrap(); while count < 160 { - self.ppu.borrow_mut().write_oam(oam_addr + count, self.data[(source + count) as usize]); + self.ppu.write_oam(oam_addr + count, self.data[(source + count) as usize]); count += 1; } + } else if PPU::is_io_register(address) { + self.ppu.set_register(address, data); } else { self.data[address as usize] = data; } diff --git a/src/cpu.rs b/src/cpu.rs index 876fc7a..1b38b07 100644 --- a/src/cpu.rs +++ b/src/cpu.rs @@ -1385,11 +1385,10 @@ impl CPU { let prev_value = self.registers.get(register); self.registers.increment(register, 1); if affect_flags { - let mut byte_compare = 0; - match register.is_8bit() { - true => byte_compare = prev_value.to_be_bytes()[1], - false => byte_compare = prev_value.to_be_bytes()[0], - } + let byte_compare = match register.is_8bit() { + true => prev_value.to_be_bytes()[1], + false => prev_value.to_be_bytes()[0], + }; let result = self.registers.get(register); self.registers.set_flag(FlagRegister::Substract, false); self.registers.set_flag(FlagRegister::HalfCarry, add_half_carry(byte_compare, 1)); @@ -1413,11 +1412,10 @@ impl CPU { let prev_value = self.registers.get(register); self.registers.decrement(register, 1); if affect_flags { - let mut byte_compare = 0; - match register.is_8bit() { - true => byte_compare = prev_value.to_be_bytes()[1], - false => byte_compare = prev_value.to_be_bytes()[0], - } + let byte_compare = match register.is_8bit() { + true => prev_value.to_be_bytes()[1], + false => prev_value.to_be_bytes()[0], + }; let result = self.registers.get(register); self.registers.set_flag(FlagRegister::Substract, true); self.registers.set_flag(FlagRegister::HalfCarry, sub_half_carry(byte_compare, 1)); @@ -1594,46 +1592,51 @@ impl CPU { self.registers.increment(Register::PC, 2); match *opcode { Opcode::RLC(register) => { - let mut result = 0; - let mut val = 0; - if register.is_8bit() { - val = self.registers.get_8bit(register); - result = val.rotate_left(1); - self.registers.set(register, result as u16); - } else { - let addr = self.registers.get(register); - val = bus.read(addr); - result = val.rotate_left(1); - bus.write(addr, result); - } + let (val, result) = match register.is_8bit() { + true => { + let val = self.registers.get_8bit(register); + let result = val.rotate_left(1); + self.registers.set(register, result as u16); + (val, result) + }, + false => { + let addr = self.registers.get(register); + let val = bus.read(addr); + let result = val.rotate_left(1); + bus.write(addr, result); + (val, result) + } + }; self.registers.set_flag(FlagRegister::Zero, result == 0); self.registers.set_flag(FlagRegister::Substract, false); self.registers.set_flag(FlagRegister::HalfCarry, false); self.registers.set_flag(FlagRegister::Carry, get_bit(val, BitIndex::I7)); }, Opcode::RRC(register) => { - let mut result = 0; - let mut val = 0; - if register.is_8bit() { - val = self.registers.get_8bit(register); - result = val.rotate_right(1); - self.registers.set(register, result as u16); - } else { - let addr = self.registers.get(register); - val = bus.read(addr); - result = val.rotate_right(1); - bus.write(addr, result); - } + let (val, result) = match register.is_8bit() { + true => { + let val = self.registers.get_8bit(register); + let result = val.rotate_right(1); + self.registers.set(register, result as u16); + (val, result) + }, + false => { + let addr = self.registers.get(register); + let val = bus.read(addr); + let result = val.rotate_right(1); + bus.write(addr, result); + (val, result) + }, + }; self.registers.set_flag(FlagRegister::Zero, result == 0); self.registers.set_flag(FlagRegister::Substract, false); self.registers.set_flag(FlagRegister::HalfCarry, false); self.registers.set_flag(FlagRegister::Carry, get_bit(val, BitIndex::I0)); }, Opcode::RL(register) => { - let mut val = 0; - match register.is_8bit() { - true => val = self.registers.get_8bit(register), - false => val = bus.read(self.registers.get(register)), + let val = match register.is_8bit() { + true => self.registers.get_8bit(register), + false => bus.read(self.registers.get(register)), }; let old_carry = self.registers.get_flag(FlagRegister::Carry); let new_carry = get_bit(val, BitIndex::I7); @@ -1648,10 +1651,9 @@ impl CPU { self.registers.set_flag(FlagRegister::HalfCarry, false); }, Opcode::RR(register) => { - let mut val = 0; - match register.is_8bit() { - true => val = self.registers.get_8bit(register), - false => val = bus.read(self.registers.get(register)), + let val = match register.is_8bit() { + true => self.registers.get_8bit(register), + false => bus.read(self.registers.get(register)), }; let old_carry = self.registers.get_flag(FlagRegister::Carry); let new_carry = get_bit(val, BitIndex::I0); @@ -1666,10 +1668,9 @@ impl CPU { self.registers.set_flag(FlagRegister::HalfCarry, false); }, Opcode::SLA(register) => { - let mut val = 0; - match register.is_8bit() { - true => val = self.registers.get_8bit(register) as i8, - false => val = bus.read(self.registers.get(register)) as i8, + let val = match register.is_8bit() { + true => self.registers.get_8bit(register) as i8, + false => bus.read(self.registers.get(register)) as i8, }; let res = val << 1; match register.is_8bit() { @@ -1682,10 +1683,9 @@ impl CPU { self.registers.set_flag(FlagRegister::HalfCarry, false); }, Opcode::SRA(register) => { - let mut val = 0; - match register.is_8bit() { - true => val = self.registers.get_8bit(register) as i8, - false => val = bus.read(self.registers.get(register)) as i8, + let val = match register.is_8bit() { + true => self.registers.get_8bit(register) as i8, + false => bus.read(self.registers.get(register)) as i8, }; let res = val >> 1; match register.is_8bit() { @@ -1698,10 +1698,9 @@ impl CPU { self.registers.set_flag(FlagRegister::HalfCarry, false); }, Opcode::SRL(register) => { - let mut val = 0; - match register.is_8bit() { - true => val = self.registers.get_8bit(register), - false => val = bus.read(self.registers.get(register)), + let val = match register.is_8bit() { + true => self.registers.get_8bit(register), + false => bus.read(self.registers.get(register)), }; let carry = get_bit(val, BitIndex::I0); let val = val >> 1; @@ -1715,10 +1714,9 @@ impl CPU { self.registers.set_flag(FlagRegister::HalfCarry, false); }, Opcode::SWAP(register) => { - let mut val = 0; - match register.is_8bit() { - true => val = self.registers.get_8bit(register), - false => val = bus.read(self.registers.get(register)), + let val = match register.is_8bit() { + true => self.registers.get_8bit(register), + false => bus.read(self.registers.get(register)), }; let val = (val << 4) | (val >> 4); match register.is_8bit() { @@ -1731,10 +1729,9 @@ impl CPU { self.registers.set_flag(FlagRegister::HalfCarry, false); }, Opcode::BIT(index, register) => { - let mut val = 0; - match register.is_8bit() { - true => val = self.registers.get_8bit(register), - false => val = bus.read(self.registers.get(register)), + let val = match register.is_8bit() { + true => self.registers.get_8bit(register), + false => bus.read(self.registers.get(register)), }; let res = get_bit(val, index); self.registers.set_flag(FlagRegister::Zero, !res); @@ -1742,10 +1739,9 @@ impl CPU { self.registers.set_flag(FlagRegister::HalfCarry, true); }, Opcode::RES(index, register) => { - let mut val = 0; - match register.is_8bit() { - true => val = self.registers.get_8bit(register), - false => val = bus.read(self.registers.get(register)), + let val = match register.is_8bit() { + true => self.registers.get_8bit(register), + false => bus.read(self.registers.get(register)), }; let val = set_bit(val, false, index); match register.is_8bit() { @@ -1754,10 +1750,9 @@ impl CPU { }; }, Opcode::SET(index, register) => { - let mut val = 0; - match register.is_8bit() { - true => val = self.registers.get_8bit(register), - false => val = bus.read(self.registers.get(register)), + let val = match register.is_8bit() { + true => self.registers.get_8bit(register), + false => bus.read(self.registers.get(register)), }; let val = set_bit(val, true, index); match register.is_8bit() { diff --git a/src/emulator.rs b/src/emulator.rs index 1bbc572..65831f0 100644 --- a/src/emulator.rs +++ b/src/emulator.rs @@ -1,104 +1,90 @@ // use std::{thread, time}; -use std::rc::Rc; -use std::cell::RefCell; use winit_input_helper::WinitInputHelper; use winit::event::{VirtualKeyCode}; use crate::cpu::{CPU, Cycles, Interrupt}; -use crate::ppu::PPU; use crate::bus::Bus; -use crate::timer::Timer; -use crate::joypad::{Joypad, Button}; +use crate::joypad::{Button}; pub struct Emulator { bus: Bus, cpu: CPU, - ppu: Rc>, - timer: Rc>, - joypad: Rc>, } impl Emulator { pub fn new() -> Self { - let joypad = Rc::new(RefCell::new(Joypad::new())); - let timer = Rc::new(RefCell::new(Timer::new())); - let ppu = Rc::new(RefCell::new(PPU::new())); Self { - bus: Bus::new(Rc::clone(&ppu), Rc::clone(&joypad), Rc::clone(&timer)), + bus: Bus::new(), cpu: CPU::new(), - ppu, - timer, - joypad, } } pub fn handle_input(&mut self, input: &WinitInputHelper) { let mut change = false; - let mut joypad = self.joypad.borrow_mut(); if input.key_pressed(VirtualKeyCode::K) { change = true; - joypad.press(Button::A); + self.bus.joypad.press(Button::A); } if input.key_pressed(VirtualKeyCode::J) { change = true; - joypad.press(Button::B); + self.bus.joypad.press(Button::B); } if input.key_pressed(VirtualKeyCode::W) { change = true; - joypad.press(Button::Up); + self.bus.joypad.press(Button::Up); } if input.key_pressed(VirtualKeyCode::S) { change = true; - joypad.press(Button::Down); + self.bus.joypad.press(Button::Down); } if input.key_pressed(VirtualKeyCode::A) { change = true; - joypad.press(Button::Left); + self.bus.joypad.press(Button::Left); } if input.key_pressed(VirtualKeyCode::D) { change = true; - joypad.press(Button::Right); + self.bus.joypad.press(Button::Right); } if input.key_pressed(VirtualKeyCode::N) { change = true; - joypad.press(Button::Start); + self.bus.joypad.press(Button::Start); } if input.key_pressed(VirtualKeyCode::B) { change = true; - joypad.press(Button::Select); + self.bus.joypad.press(Button::Select); } if input.key_released(VirtualKeyCode::K) { change = true; - joypad.release(Button::A); + self.bus.joypad.release(Button::A); } if input.key_released(VirtualKeyCode::J) { change = true; - joypad.release(Button::B); + self.bus.joypad.release(Button::B); } if input.key_released(VirtualKeyCode::W) { change = true; - joypad.release(Button::Up); + self.bus.joypad.release(Button::Up); } if input.key_released(VirtualKeyCode::S) { change = true; - joypad.release(Button::Down); + self.bus.joypad.release(Button::Down); } if input.key_released(VirtualKeyCode::A) { change = true; - joypad.release(Button::Left); + self.bus.joypad.release(Button::Left); } if input.key_released(VirtualKeyCode::D) { change = true; - joypad.release(Button::Right); + self.bus.joypad.release(Button::Right); } if input.key_released(VirtualKeyCode::N) { change = true; - joypad.release(Button::Start); + self.bus.joypad.release(Button::Start); } if input.key_released(VirtualKeyCode::B) { change = true; - joypad.release(Button::Select); + self.bus.joypad.release(Button::Select); } if change { self.bus.set_interrupt_flag(Interrupt::Joypad, true); @@ -110,8 +96,20 @@ impl Emulator { while self.cpu.get_cycles().to_t().0 <= cpu_cycles.0 { self.cpu.run(&mut self.bus); let cycles = self.cpu.get_last_op_cycles().to_t(); - self.ppu.borrow_mut().do_cycles(&mut self.bus, cycles, frame_buffer); - self.timer.borrow_mut().do_cycles(&mut self.bus, cycles); + self.bus.ppu.do_cycles(cycles, frame_buffer); + if self.bus.ppu.get_interrupt(Interrupt::VBlank) { + 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 // thread::sleep(time::Duration::from_nanos((self.cpu.get_last_op_cycles().0 * 238).try_into().unwrap())); @@ -124,8 +122,21 @@ impl Emulator { let mut frame: [u8; 144 * 160 * 4] = [0; 144 * 160 * 4]; while !exit { self.cpu.run(&mut self.bus); - self.ppu.borrow_mut().do_cycles(&mut self.bus, self.cpu.get_last_op_cycles().to_t(), &mut frame); - self.timer.borrow_mut().do_cycles(&mut self.bus, 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); + if self.bus.ppu.get_interrupt(Interrupt::VBlank) { + 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() >= 161502; // log 2 diff --git a/src/ppu.rs b/src/ppu.rs index 5ef2919..cd2dda7 100644 --- a/src/ppu.rs +++ b/src/ppu.rs @@ -3,7 +3,7 @@ use crate::utils::{ get_bit, set_bit, }; -use crate::bus::{Bus, VIDEO_RAM, SPRITE_ATTRIBUTE_TABLE}; +use crate::bus::{SPRITE_ATTRIBUTE_TABLE}; use crate::cpu::{Cycles, Interrupt}; pub const LCD_WIDTH: u32 = 160; @@ -25,6 +25,7 @@ pub const OBJECT_PALETTE_0_ADDRESS: u16 = 0xFF48; pub const OBJECT_PALETTE_1_ADDRESS: u16 = 0xFF49; pub const WINDOW_Y_ADDRESS: u16 = 0xFF4A; pub const WINDOW_X_ADDRESS: u16 = 0xFF4B; + pub const TILE_MAP_ADDRESS: u16 = 0x9800; #[derive(Debug, Copy, Clone)] @@ -184,7 +185,7 @@ impl Sprite { let tile_line = y.rem_euclid(height) * 2; let addr = 0x8000 + (tile_number as u16 * 16) + tile_line as u16; - let vram_start = VIDEO_RAM.min().unwrap(); + let vram_start = 0x8000; let tile_byte_1 = vram[(addr - vram_start) as usize]; let tile_byte_2 = vram[(addr - vram_start + 1) as usize]; let bit_pixels_array = PPU::get_byte_pixels(tile_byte_1, tile_byte_2); @@ -204,6 +205,8 @@ impl Sprite { pub struct PPU { state: bool, + vblank_request: bool, + lcdstat_request: bool, cycles: Cycles, sprite_buffer: Vec, window_y_counter: u8, @@ -217,6 +220,7 @@ pub struct PPU { scroll_y: u8, window_x: u8, window_y: u8, + io_registers: [u8; 12], vram: [u8; 0x2000], oam: [u8; 0xA0], } @@ -225,6 +229,8 @@ impl PPU { pub fn new() -> Self { Self { state: false, + vblank_request: false, + lcdstat_request: false, cycles: Cycles(0), sprite_buffer: Vec::new(), window_y_counter: 0, @@ -238,25 +244,79 @@ impl PPU { scroll_y: 0, window_x: 0, window_y: 0, + io_registers: [0; 12], vram: [0; 0x2000], oam: [0; 0xA0], } } + 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 { + address >= 0xFF40 && address <= 0xFF4B + } + pub fn read_vram(&self, address: u16) -> u8 { - self.vram[(address - VIDEO_RAM.min().unwrap()) as usize] + self.vram[(address - 0x8000) as usize] } pub fn write_vram(&mut self, address: u16, data: u8) { - self.vram[(address - VIDEO_RAM.min().unwrap()) as usize] = data; + self.vram[(address - 0x8000) as usize] = data; } pub fn read_oam(&self, address: u16) -> u8 { - self.oam[(address - SPRITE_ATTRIBUTE_TABLE.min().unwrap()) as usize] + self.oam[(address - 0xFE00) as usize] } pub fn write_oam(&mut self, address: u16, data: u8) { - self.oam[(address - SPRITE_ATTRIBUTE_TABLE.min().unwrap()) as usize] = data; + self.oam[(address - 0xFE00) as usize] = data; + } + + pub fn get_register(&self, address: u16) -> u8 { + self.io_registers[(address - 0xFF40) as usize] + } + + pub fn set_register(&mut self, address: u16, data: u8) { + if address == LCD_Y_ADDRESS { + return; + } else if address == LCD_CONTROL_ADDRESS { + let address = address - 0xFF40; + self.io_registers[address as usize] = data; + // Check if LCD is being turned on or off + if (get_bit(data, BitIndex::I7) && !get_bit(self.io_registers[address as usize], BitIndex::I7)) || + !get_bit(data, BitIndex::I7) + { + self.io_registers[LCD_Y_ADDRESS as usize - 0xFF40] = 0x00; + // Set Hblank + let byte = self.io_registers[LCD_STATUS_ADDRESS as usize - 0xFF40]; + self.io_registers[LCD_STATUS_ADDRESS as usize - 0xFF40] = byte & 0b11111100; + } + return; + } else if address == LCD_STATUS_ADDRESS { + let address = address - 0xFF40; + let byte = self.io_registers[address as usize]; + self.io_registers[address as usize] = (data & 0b11111000) | (byte & 0b00000111); + } else { + self.io_registers[address as usize - 0xFF40] = data; + } + } + + pub fn force_set_register(&mut self, address: u16, data: u8) { + self.io_registers[address as usize - 0xFF40] = data; } pub fn reset_cycles(&mut self) { @@ -267,38 +327,38 @@ impl PPU { self.cycles.0 += cycles.0; } - pub fn do_cycles(&mut self, bus: &mut Bus, cycles: Cycles, frame_buffer: &mut [u8]) { + pub fn do_cycles(&mut self, cycles: Cycles, frame_buffer: &mut [u8]) { self.lcd_control_cache = None; - if !self.get_lcd_control(bus, LCDControl::LCDEnable) { + if !self.get_lcd_control(LCDControl::LCDEnable) { self.increment_cycles(cycles); return; } - self.lcd_y = bus.read(LCD_Y_ADDRESS); + self.lcd_y = self.get_register(LCD_Y_ADDRESS); if self.lcd_y < 144 { - if self.cycles.0 <= 80 && !PPU::get_lcd_status(bus, LCDStatus::ModeFlag(LCDStatusModeFlag::SearchingOAM)) { + if self.cycles.0 <= 80 && !self.get_lcd_status(LCDStatus::ModeFlag(LCDStatusModeFlag::SearchingOAM)) { // Mode 2 OAM scan - PPU::set_lcd_status(bus, LCDStatus::ModeFlag(LCDStatusModeFlag::SearchingOAM), true); - self.stat_interrupt(bus); - self.oam_search(bus); - } else if self.cycles.0 > 80 && self.cycles.0 <= 80 + 172 && !PPU::get_lcd_status(bus, LCDStatus::ModeFlag(LCDStatusModeFlag::TransferringToLCD)) { + self.set_lcd_status(LCDStatus::ModeFlag(LCDStatusModeFlag::SearchingOAM), true); + self.stat_interrupt(); + self.oam_search(); + } else if self.cycles.0 > 80 && self.cycles.0 <= 80 + 172 && !self.get_lcd_status(LCDStatus::ModeFlag(LCDStatusModeFlag::TransferringToLCD)) { // Mode 3 drawing pixel line. This could also last 289 cycles - self.scroll_x = bus.read(SCROLL_X_ADDRESS); - self.scroll_y = bus.read(SCROLL_Y_ADDRESS); - self.window_x = bus.read(WINDOW_X_ADDRESS); - self.window_y = bus.read(WINDOW_Y_ADDRESS); - PPU::set_lcd_status(bus, LCDStatus::ModeFlag(LCDStatusModeFlag::TransferringToLCD), true); - self.draw_line(bus, frame_buffer); - } else if self.cycles.0 > 80 + 172 && self.cycles.0 <= 80 + 172 + 204 && !PPU::get_lcd_status(bus, LCDStatus::ModeFlag(LCDStatusModeFlag::HBlank)) { + self.scroll_x = self.get_register(SCROLL_X_ADDRESS); + self.scroll_y = self.get_register(SCROLL_Y_ADDRESS); + self.window_x = self.get_register(WINDOW_X_ADDRESS); + self.window_y = self.get_register(WINDOW_Y_ADDRESS); + self.set_lcd_status(LCDStatus::ModeFlag(LCDStatusModeFlag::TransferringToLCD), true); + self.draw_line(frame_buffer); + } 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 - PPU::set_lcd_status(bus, LCDStatus::ModeFlag(LCDStatusModeFlag::HBlank), true); - self.stat_interrupt(bus); + self.set_lcd_status(LCDStatus::ModeFlag(LCDStatusModeFlag::HBlank), true); + self.stat_interrupt(); } - } else if self.lcd_y >= 144 && !PPU::get_lcd_status(bus, LCDStatus::ModeFlag(LCDStatusModeFlag::VBlank)) { + } else if self.lcd_y >= 144 && !self.get_lcd_status(LCDStatus::ModeFlag(LCDStatusModeFlag::VBlank)) { // Mode 1 Vertical blank - PPU::set_lcd_status(bus, LCDStatus::ModeFlag(LCDStatusModeFlag::VBlank), true); - bus.set_interrupt_flag(Interrupt::VBlank, true); - self.stat_interrupt(bus); + self.set_lcd_status(LCDStatus::ModeFlag(LCDStatusModeFlag::VBlank), true); + self.set_interrupt(Interrupt::VBlank, true); + self.stat_interrupt(); } self.increment_cycles(cycles); @@ -314,43 +374,43 @@ impl PPU { self.lcd_y = 0; self.window_y_counter = 0; } - bus.force_write(LCD_Y_ADDRESS, self.lcd_y); - self.stat_interrupt(bus); + self.force_set_register(LCD_Y_ADDRESS, self.lcd_y); + self.stat_interrupt(); } } - fn stat_interrupt(&mut self, bus: &mut Bus) { + fn stat_interrupt(&mut self) { let prev_state = self.state; - let lyc_compare = self.lcd_y == bus.read(LCD_Y_COMPARE_ADDRESS); - PPU::set_lcd_status(bus, LCDStatus::LYCFlag, lyc_compare); + let lyc_compare = self.lcd_y == self.get_register(LCD_Y_COMPARE_ADDRESS); + self.set_lcd_status(LCDStatus::LYCFlag, lyc_compare); self.state = ( lyc_compare && - PPU::get_lcd_status(bus, LCDStatus::LYCInterrupt) + self.get_lcd_status(LCDStatus::LYCInterrupt) ) || ( - PPU::get_lcd_status(bus, LCDStatus::Mode2OAMInterrupt) && - PPU::get_lcd_status(bus, LCDStatus::ModeFlag(LCDStatusModeFlag::SearchingOAM)) + self.get_lcd_status(LCDStatus::Mode2OAMInterrupt) && + self.get_lcd_status(LCDStatus::ModeFlag(LCDStatusModeFlag::SearchingOAM)) ) || ( - PPU::get_lcd_status(bus, LCDStatus::Mode0HBlankInterrupt) && - PPU::get_lcd_status(bus, LCDStatus::ModeFlag(LCDStatusModeFlag::HBlank)) + self.get_lcd_status(LCDStatus::Mode0HBlankInterrupt) && + self.get_lcd_status(LCDStatus::ModeFlag(LCDStatusModeFlag::HBlank)) ) || ( - PPU::get_lcd_status(bus, LCDStatus::Mode1VBlankInterrupt) && - PPU::get_lcd_status(bus, LCDStatus::ModeFlag(LCDStatusModeFlag::VBlank)) + self.get_lcd_status(LCDStatus::Mode1VBlankInterrupt) && + self.get_lcd_status(LCDStatus::ModeFlag(LCDStatusModeFlag::VBlank)) ); if self.state && !prev_state { - bus.set_interrupt_flag(Interrupt::LCDSTAT, self.state); + self.set_interrupt(Interrupt::LCDSTAT, self.state); } } - fn oam_search(&mut self, bus: &Bus) { - if !self.get_lcd_control(bus, LCDControl::ObjectEnable) { + fn oam_search(&mut self) { + if !self.get_lcd_control(LCDControl::ObjectEnable) { return; } self.sprite_buffer = Vec::new(); - let palette_0 = bus.read(OBJECT_PALETTE_0_ADDRESS); - let palette_1 = bus.read(OBJECT_PALETTE_1_ADDRESS); - let long_sprites = self.get_lcd_control(bus, LCDControl::ObjectSize); + let palette_0 = self.get_register(OBJECT_PALETTE_0_ADDRESS); + let palette_1 = self.get_register(OBJECT_PALETTE_1_ADDRESS); + let long_sprites = self.get_lcd_control(LCDControl::ObjectSize); let mut addr = SPRITE_ATTRIBUTE_TABLE.min().unwrap(); while addr <= SPRITE_ATTRIBUTE_TABLE.max().unwrap() { // The gameboy only supports 10 sprites per line, @@ -416,11 +476,11 @@ impl PPU { return None; } - pub fn get_lcd_control(&mut self, bus: &Bus, control: LCDControl) -> bool { + pub fn get_lcd_control(&mut self, control: LCDControl) -> bool { let byte = match self.lcd_control_cache { Some(byte) => byte, None => { - let byte = bus.read(LCD_CONTROL_ADDRESS); + let byte = self.get_register(LCD_CONTROL_ADDRESS); self.lcd_control_cache = Some(byte); byte }, @@ -428,8 +488,8 @@ impl PPU { control.get(byte) } - pub fn get_lcd_status(bus: &Bus, status: LCDStatus) -> bool { - let byte = bus.read(LCD_STATUS_ADDRESS); + pub fn get_lcd_status(&self, status: LCDStatus) -> bool { + let byte = self.get_register(LCD_STATUS_ADDRESS); match status { LCDStatus::LYCInterrupt => get_bit(byte, BitIndex::I6), LCDStatus::Mode2OAMInterrupt => get_bit(byte, BitIndex::I5), @@ -445,8 +505,8 @@ impl PPU { } } - fn set_lcd_status(bus: &mut Bus, status: LCDStatus, val: bool) { - let mut byte = bus.read(LCD_STATUS_ADDRESS); + fn set_lcd_status(&mut self, status: LCDStatus, val: bool) { + let mut byte = self.get_register(LCD_STATUS_ADDRESS); byte = match status { LCDStatus::LYCInterrupt => set_bit(byte, val, BitIndex::I6), LCDStatus::Mode2OAMInterrupt => set_bit(byte, val, BitIndex::I5), @@ -460,7 +520,7 @@ impl PPU { LCDStatusModeFlag::TransferringToLCD => (byte & 0b11111100) | 3, }, }; - bus.force_write(LCD_STATUS_ADDRESS, byte); + self.force_set_register(LCD_STATUS_ADDRESS, byte); } fn get_tile_bytes(&self, x: u8, y: u8, tilemap_area: u16, default_method: bool) -> (u8, u8) { @@ -481,8 +541,8 @@ impl PPU { (self.read_vram(addr), self.read_vram(addr + 1)) } - fn get_window_pixel(&mut self, lcd_x: u8, bus: &Bus) -> Option { - if !self.get_lcd_control(bus, LCDControl::WindowEnable) { + fn get_window_pixel(&mut self, lcd_x: u8) -> Option { + if !self.get_lcd_control(LCDControl::WindowEnable) { return None; } @@ -512,8 +572,8 @@ impl PPU { bit_pixels_array[bit_pixel_index] }, None => { - let default_mode = self.get_lcd_control(bus, LCDControl::TileAddressMode); - let tilemap_area = match self.get_lcd_control(bus, LCDControl::WindowTileMapAddress) { + let default_mode = self.get_lcd_control(LCDControl::TileAddressMode); + let tilemap_area = match self.get_lcd_control(LCDControl::WindowTileMapAddress) { true => 0x9C00, false => 0x9800, }; @@ -530,8 +590,8 @@ impl PPU { Some(PPU::get_pixel(PPU::get_palette(bit_pixel, self.bg_palette))) } - fn get_background_pixel(&mut self, lcd_x: u8, bus: &Bus) -> Option { - if !self.get_lcd_control(bus, LCDControl::BackgroundPriority) { + fn get_background_pixel(&mut self, lcd_x: u8) -> Option { + if !self.get_lcd_control(LCDControl::BackgroundPriority) { return None; } let lcd_y = self.lcd_y; @@ -548,8 +608,8 @@ impl PPU { bit_pixels_array[bit_pixel_index] }, None => { - let default_mode = self.get_lcd_control(bus, LCDControl::TileAddressMode); - let tilemap_area = match self.get_lcd_control(bus, LCDControl::BackgroundTileMapAddress) { + let default_mode = self.get_lcd_control(LCDControl::TileAddressMode); + let tilemap_area = match self.get_lcd_control(LCDControl::BackgroundTileMapAddress) { true => 0x9C00, false => 0x9800, }; @@ -565,7 +625,7 @@ impl PPU { Some(PPU::get_pixel(PPU::get_palette(bit_pixel, self.bg_palette))) } - fn draw_line(&mut self, bus: &Bus, frame_buffer: &mut [u8]) { + fn draw_line(&mut self, frame_buffer: &mut [u8]) { let lcd_y = self.lcd_y; if lcd_y as u32 >= LCD_HEIGHT { return; @@ -573,25 +633,25 @@ impl PPU { self.current_background_pixels = None; self.current_window_pixels = None; - self.bg_palette = bus.read(BACKGROUND_PALETTE_ADDRESS); + self.bg_palette = self.get_register(BACKGROUND_PALETTE_ADDRESS); let mut lcd_x: u8 = 0; let mut window_drawn = false; while (lcd_x as u32) < LCD_WIDTH { let idx = (lcd_x as usize + (lcd_y as usize * LCD_WIDTH as usize)) * 4; - if let Some(window_pixel) = self.get_window_pixel(lcd_x, bus) { + if let Some(window_pixel) = self.get_window_pixel(lcd_x) { window_drawn = true; let rgba = PPU::get_rgba(window_pixel, WINDOW_COLORS); frame_buffer[idx] = rgba[0]; frame_buffer[idx + 1] = rgba[1]; frame_buffer[idx + 2] = rgba[2]; - } else if let Some(background_pixel) = self.get_background_pixel(lcd_x, bus) { + } else if let Some(background_pixel) = self.get_background_pixel(lcd_x) { let rgba = PPU::get_rgba(background_pixel, BACKGROUND_COLORS); frame_buffer[idx] = rgba[0]; frame_buffer[idx + 1] = rgba[1]; frame_buffer[idx + 2] = rgba[2]; } - if self.get_lcd_control(bus, LCDControl::ObjectEnable) { + if self.get_lcd_control(LCDControl::ObjectEnable) { if let Some((sprite_pixel, palette_zero)) = self.find_sprite_pixel(lcd_x) { let rgba = PPU::get_rgba(sprite_pixel, match palette_zero { true => SPRITE_0_COLORS, diff --git a/src/timer.rs b/src/timer.rs index 0e8fb58..0b89ec0 100644 --- a/src/timer.rs +++ b/src/timer.rs @@ -1,5 +1,4 @@ -use crate::cpu::{Interrupt, Cycles}; -use crate::bus::Bus; +use crate::cpu::{Cycles}; use crate::utils::{ BitIndex, get_bit, @@ -14,7 +13,9 @@ pub struct Timer { divider: u16, prev_result: bool, is_enabled: bool, + interrupt: bool, control: u8, + io_registers: [u8; 4], } impl Timer { @@ -24,10 +25,40 @@ impl Timer { divider: 0, control: 0, prev_result: false, + interrupt: false, is_enabled: false, + io_registers: [0; 4], } } + pub fn is_io_register(address: u16) -> bool { + address >= 0xFF04 && address <= 0xFF07 + } + + pub fn get_register(&self, address: u16) -> u8 { + if address == TIMER_DIVIDER_REGISTER_ADDRESS { + return self.read_divider(); + } + self.io_registers[(address - 0xFF04) as usize] + } + + pub fn set_register(&mut self, address: u16, data: u8) { + if address == TIMER_DIVIDER_REGISTER_ADDRESS { + self.divider = 0; + self.io_registers[(TIMER_DIVIDER_REGISTER_ADDRESS - 0xFF04) as usize] = 0; + } else { + 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 { self.divider.to_be_bytes()[0] } @@ -37,36 +68,36 @@ impl Timer { self.divider = 0; } - pub fn do_cycles(&mut self, bus: &mut Bus, cycles: Cycles) { - self.is_enabled = Timer::is_timer_enabled(bus); - self.control = bus.read(TIMER_CONTROL_ADDRESS); + pub fn do_cycles(&mut self, cycles: Cycles) { + self.is_enabled = self.is_timer_enabled(); + self.control = self.get_register(TIMER_CONTROL_ADDRESS); let mut count = 0; while count < cycles.0 { - self.cycle(bus); + self.cycle(); count += 1; } } - fn cycle(&mut self, bus: &mut Bus) { + fn cycle(&mut self) { self.divider = self.divider.wrapping_add(1); let result = self.is_enabled && self.get_tima_rate(); if self.prev_result && !result { - let tima = bus.read(TIMER_COUNTER_ADDRESS).wrapping_add(1); + let tima = self.get_register(TIMER_COUNTER_ADDRESS).wrapping_add(1); if tima == 0 { - bus.write(TIMER_COUNTER_ADDRESS, bus.read(TIMER_MODULO_ADDRESS)); - bus.set_interrupt_flag(Interrupt::Timer, true); + self.set_register(TIMER_COUNTER_ADDRESS, self.get_register(TIMER_MODULO_ADDRESS)); + self.interrupt = true; } else { - bus.write(TIMER_COUNTER_ADDRESS, tima); + self.set_register(TIMER_COUNTER_ADDRESS, tima); } } self.prev_result = result; } - fn is_timer_enabled(bus: &Bus) -> bool { - get_bit(bus.read(TIMER_CONTROL_ADDRESS), BitIndex::I2) + fn is_timer_enabled(&self) -> bool { + get_bit(self.get_register(TIMER_CONTROL_ADDRESS), BitIndex::I2) } fn get_tima_rate(&self) -> bool {