Refactor project structure and fix all warnings

This commit is contained in:
Franco Colmenarez 2021-11-15 16:38:52 -05:00
parent e2959abfaf
commit 6104f5b077
5 changed files with 304 additions and 225 deletions

View File

@ -1,21 +1,14 @@
use std::rc::Rc;
use std::cell::RefCell;
use std::ops::RangeInclusive; use std::ops::RangeInclusive;
use crate::utils::{ use crate::utils::{
get_bit,
BitIndex,
join_bytes join_bytes
}; };
use crate::rom::ROM; use crate::rom::ROM;
use crate::ppu::{ use crate::ppu::{
PPU, PPU,
LCD_STATUS_ADDRESS,
LCD_CONTROL_ADDRESS,
LCD_Y_ADDRESS,
DMA_ADDRESS, DMA_ADDRESS,
}; };
use crate::cpu::{Interrupt}; use crate::cpu::{Interrupt};
use crate::timer::{Timer, TIMER_DIVIDER_REGISTER_ADDRESS}; use crate::timer::{Timer};
use crate::joypad::{Joypad, JOYPAD_ADDRESS}; use crate::joypad::{Joypad, JOYPAD_ADDRESS};
pub const BANK_ZERO: RangeInclusive<u16> = 0x0000..=0x3FFF; pub const BANK_ZERO: RangeInclusive<u16> = 0x0000..=0x3FFF;
@ -36,13 +29,13 @@ pub const INTERRUPT_FLAG_ADDRESS: u16 = 0xFF0F;
pub struct Bus { pub struct Bus {
game_rom: ROM, game_rom: ROM,
data: [u8; 0x10000], data: [u8; 0x10000],
ppu: Rc<RefCell<PPU>>, pub ppu: PPU,
joypad: Rc<RefCell<Joypad>>, pub joypad: Joypad,
timer: Rc<RefCell<Timer>>, pub timer: Timer,
} }
impl Bus { impl Bus {
pub fn new(ppu: Rc<RefCell<PPU>>, joypad: Rc<RefCell<Joypad>>, timer: Rc<RefCell<Timer>>) -> Self { pub fn new() -> Self {
let args: Vec<String> = std::env::args().collect(); let args: Vec<String> = std::env::args().collect();
if args.len() < 2 { if args.len() < 2 {
println!("Please, specify a ROM file"); println!("Please, specify a ROM file");
@ -82,9 +75,9 @@ impl Bus {
Self { Self {
data, data,
game_rom, game_rom,
ppu, ppu: PPU::new(),
joypad, joypad: Joypad::new(),
timer, timer: Timer::new(),
} }
} }
@ -94,13 +87,15 @@ impl Bus {
} 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 0b11100000 | self.data[address as usize];
} else if VIDEO_RAM.contains(&address) { } 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) { } 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 { } else if address == JOYPAD_ADDRESS {
return self.joypad.borrow().read(self.data[address as usize]); return self.joypad.read(self.data[address as usize]);
} else if address == TIMER_DIVIDER_REGISTER_ADDRESS { } else if Timer::is_io_register(address) {
return self.timer.borrow().read_divider(); return self.timer.get_register(address);
} }
self.data[address as usize] 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; self.data[(ECHO_RAM.min().unwrap() + (address - WORK_RAM_1.min().unwrap())) as usize] = data;
} }
} else if EXTERNAL_RAM.contains(&address) { } else if EXTERNAL_RAM.contains(&address) {
// self.game_rom.write(address, data); self.game_rom.write(address, data);
} else if ECHO_RAM.contains(&address) { } else if ECHO_RAM.contains(&address) {
self.data[address as usize] = data; 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 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 { } else if Timer::is_io_register(address) {
self.timer.borrow_mut().reset(); self.timer.set_register(address, data);
} 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 address == JOYPAD_ADDRESS { } else if address == JOYPAD_ADDRESS {
let byte = self.data[address as usize]; let byte = self.data[address as usize];
self.data[address as usize] = (data & 0b11110000) | (byte & 0b00001111); self.data[address as usize] = (data & 0b11110000) | (byte & 0b00001111);
} else if VIDEO_RAM.contains(&address) { } 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) { } 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 { } else if address == DMA_ADDRESS {
self.data[address as usize] = data; self.data[address as usize] = data;
let source = (data as u16) * 0x100; let source = (data as u16) * 0x100;
let mut count: u16 = 0; let mut count: u16 = 0;
let oam_addr = SPRITE_ATTRIBUTE_TABLE.min().unwrap(); let oam_addr = SPRITE_ATTRIBUTE_TABLE.min().unwrap();
while count < 160 { 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; count += 1;
} }
} else if PPU::is_io_register(address) {
self.ppu.set_register(address, data);
} else { } else {
self.data[address as usize] = data; self.data[address as usize] = data;
} }

View File

@ -1385,11 +1385,10 @@ impl CPU {
let prev_value = self.registers.get(register); let prev_value = self.registers.get(register);
self.registers.increment(register, 1); self.registers.increment(register, 1);
if affect_flags { if affect_flags {
let mut byte_compare = 0; let byte_compare = match register.is_8bit() {
match register.is_8bit() { true => prev_value.to_be_bytes()[1],
true => byte_compare = prev_value.to_be_bytes()[1], false => prev_value.to_be_bytes()[0],
false => byte_compare = prev_value.to_be_bytes()[0], };
}
let result = self.registers.get(register); let result = self.registers.get(register);
self.registers.set_flag(FlagRegister::Substract, false); self.registers.set_flag(FlagRegister::Substract, false);
self.registers.set_flag(FlagRegister::HalfCarry, add_half_carry(byte_compare, 1)); 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); let prev_value = self.registers.get(register);
self.registers.decrement(register, 1); self.registers.decrement(register, 1);
if affect_flags { if affect_flags {
let mut byte_compare = 0; let byte_compare = match register.is_8bit() {
match register.is_8bit() { true => prev_value.to_be_bytes()[1],
true => byte_compare = prev_value.to_be_bytes()[1], false => prev_value.to_be_bytes()[0],
false => byte_compare = prev_value.to_be_bytes()[0], };
}
let result = self.registers.get(register); let result = self.registers.get(register);
self.registers.set_flag(FlagRegister::Substract, true); self.registers.set_flag(FlagRegister::Substract, true);
self.registers.set_flag(FlagRegister::HalfCarry, sub_half_carry(byte_compare, 1)); self.registers.set_flag(FlagRegister::HalfCarry, sub_half_carry(byte_compare, 1));
@ -1594,46 +1592,51 @@ impl CPU {
self.registers.increment(Register::PC, 2); self.registers.increment(Register::PC, 2);
match *opcode { match *opcode {
Opcode::RLC(register) => { Opcode::RLC(register) => {
let mut result = 0; let (val, result) = match register.is_8bit() {
let mut val = 0; true => {
if register.is_8bit() { let val = self.registers.get_8bit(register);
val = self.registers.get_8bit(register); let result = val.rotate_left(1);
result = val.rotate_left(1); self.registers.set(register, result as u16);
self.registers.set(register, result as u16); (val, result)
} else { },
let addr = self.registers.get(register); false => {
val = bus.read(addr); let addr = self.registers.get(register);
result = val.rotate_left(1); let val = bus.read(addr);
bus.write(addr, result); 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::Zero, result == 0);
self.registers.set_flag(FlagRegister::Substract, false); self.registers.set_flag(FlagRegister::Substract, false);
self.registers.set_flag(FlagRegister::HalfCarry, false); self.registers.set_flag(FlagRegister::HalfCarry, false);
self.registers.set_flag(FlagRegister::Carry, get_bit(val, BitIndex::I7)); self.registers.set_flag(FlagRegister::Carry, get_bit(val, BitIndex::I7));
}, },
Opcode::RRC(register) => { Opcode::RRC(register) => {
let mut result = 0; let (val, result) = match register.is_8bit() {
let mut val = 0; true => {
if register.is_8bit() { let val = self.registers.get_8bit(register);
val = self.registers.get_8bit(register); let result = val.rotate_right(1);
result = val.rotate_right(1); self.registers.set(register, result as u16);
self.registers.set(register, result as u16); (val, result)
} else { },
let addr = self.registers.get(register); false => {
val = bus.read(addr); let addr = self.registers.get(register);
result = val.rotate_right(1); let val = bus.read(addr);
bus.write(addr, result); 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::Zero, result == 0);
self.registers.set_flag(FlagRegister::Substract, false); self.registers.set_flag(FlagRegister::Substract, false);
self.registers.set_flag(FlagRegister::HalfCarry, false); self.registers.set_flag(FlagRegister::HalfCarry, false);
self.registers.set_flag(FlagRegister::Carry, get_bit(val, BitIndex::I0)); self.registers.set_flag(FlagRegister::Carry, get_bit(val, BitIndex::I0));
}, },
Opcode::RL(register) => { Opcode::RL(register) => {
let mut val = 0; let val = match register.is_8bit() {
match register.is_8bit() { true => self.registers.get_8bit(register),
true => val = self.registers.get_8bit(register), false => bus.read(self.registers.get(register)),
false => val = bus.read(self.registers.get(register)),
}; };
let old_carry = self.registers.get_flag(FlagRegister::Carry); let old_carry = self.registers.get_flag(FlagRegister::Carry);
let new_carry = get_bit(val, BitIndex::I7); let new_carry = get_bit(val, BitIndex::I7);
@ -1648,10 +1651,9 @@ impl CPU {
self.registers.set_flag(FlagRegister::HalfCarry, false); self.registers.set_flag(FlagRegister::HalfCarry, false);
}, },
Opcode::RR(register) => { Opcode::RR(register) => {
let mut val = 0; let val = match register.is_8bit() {
match register.is_8bit() { true => self.registers.get_8bit(register),
true => val = self.registers.get_8bit(register), false => bus.read(self.registers.get(register)),
false => val = bus.read(self.registers.get(register)),
}; };
let old_carry = self.registers.get_flag(FlagRegister::Carry); let old_carry = self.registers.get_flag(FlagRegister::Carry);
let new_carry = get_bit(val, BitIndex::I0); let new_carry = get_bit(val, BitIndex::I0);
@ -1666,10 +1668,9 @@ impl CPU {
self.registers.set_flag(FlagRegister::HalfCarry, false); self.registers.set_flag(FlagRegister::HalfCarry, false);
}, },
Opcode::SLA(register) => { Opcode::SLA(register) => {
let mut val = 0; let val = match register.is_8bit() {
match register.is_8bit() { true => self.registers.get_8bit(register) as i8,
true => val = self.registers.get_8bit(register) as i8, false => bus.read(self.registers.get(register)) as i8,
false => val = bus.read(self.registers.get(register)) as i8,
}; };
let res = val << 1; let res = val << 1;
match register.is_8bit() { match register.is_8bit() {
@ -1682,10 +1683,9 @@ impl CPU {
self.registers.set_flag(FlagRegister::HalfCarry, false); self.registers.set_flag(FlagRegister::HalfCarry, false);
}, },
Opcode::SRA(register) => { Opcode::SRA(register) => {
let mut val = 0; let val = match register.is_8bit() {
match register.is_8bit() { true => self.registers.get_8bit(register) as i8,
true => val = self.registers.get_8bit(register) as i8, false => bus.read(self.registers.get(register)) as i8,
false => val = bus.read(self.registers.get(register)) as i8,
}; };
let res = val >> 1; let res = val >> 1;
match register.is_8bit() { match register.is_8bit() {
@ -1698,10 +1698,9 @@ impl CPU {
self.registers.set_flag(FlagRegister::HalfCarry, false); self.registers.set_flag(FlagRegister::HalfCarry, false);
}, },
Opcode::SRL(register) => { Opcode::SRL(register) => {
let mut val = 0; let val = match register.is_8bit() {
match register.is_8bit() { true => self.registers.get_8bit(register),
true => val = self.registers.get_8bit(register), false => bus.read(self.registers.get(register)),
false => val = bus.read(self.registers.get(register)),
}; };
let carry = get_bit(val, BitIndex::I0); let carry = get_bit(val, BitIndex::I0);
let val = val >> 1; let val = val >> 1;
@ -1715,10 +1714,9 @@ impl CPU {
self.registers.set_flag(FlagRegister::HalfCarry, false); self.registers.set_flag(FlagRegister::HalfCarry, false);
}, },
Opcode::SWAP(register) => { Opcode::SWAP(register) => {
let mut val = 0; let val = match register.is_8bit() {
match register.is_8bit() { true => self.registers.get_8bit(register),
true => val = self.registers.get_8bit(register), false => bus.read(self.registers.get(register)),
false => val = bus.read(self.registers.get(register)),
}; };
let val = (val << 4) | (val >> 4); let val = (val << 4) | (val >> 4);
match register.is_8bit() { match register.is_8bit() {
@ -1731,10 +1729,9 @@ impl CPU {
self.registers.set_flag(FlagRegister::HalfCarry, false); self.registers.set_flag(FlagRegister::HalfCarry, false);
}, },
Opcode::BIT(index, register) => { Opcode::BIT(index, register) => {
let mut val = 0; let val = match register.is_8bit() {
match register.is_8bit() { true => self.registers.get_8bit(register),
true => val = self.registers.get_8bit(register), false => bus.read(self.registers.get(register)),
false => val = bus.read(self.registers.get(register)),
}; };
let res = get_bit(val, index); let res = get_bit(val, index);
self.registers.set_flag(FlagRegister::Zero, !res); self.registers.set_flag(FlagRegister::Zero, !res);
@ -1742,10 +1739,9 @@ impl CPU {
self.registers.set_flag(FlagRegister::HalfCarry, true); self.registers.set_flag(FlagRegister::HalfCarry, true);
}, },
Opcode::RES(index, register) => { Opcode::RES(index, register) => {
let mut val = 0; let val = match register.is_8bit() {
match register.is_8bit() { true => self.registers.get_8bit(register),
true => val = self.registers.get_8bit(register), false => bus.read(self.registers.get(register)),
false => val = bus.read(self.registers.get(register)),
}; };
let val = set_bit(val, false, index); let val = set_bit(val, false, index);
match register.is_8bit() { match register.is_8bit() {
@ -1754,10 +1750,9 @@ impl CPU {
}; };
}, },
Opcode::SET(index, register) => { Opcode::SET(index, register) => {
let mut val = 0; let val = match register.is_8bit() {
match register.is_8bit() { true => self.registers.get_8bit(register),
true => val = self.registers.get_8bit(register), false => bus.read(self.registers.get(register)),
false => val = bus.read(self.registers.get(register)),
}; };
let val = set_bit(val, true, index); let val = set_bit(val, true, index);
match register.is_8bit() { match register.is_8bit() {

View File

@ -1,104 +1,90 @@
// use std::{thread, time}; // use std::{thread, time};
use std::rc::Rc;
use std::cell::RefCell;
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, Interrupt};
use crate::ppu::PPU;
use crate::bus::Bus; use crate::bus::Bus;
use crate::timer::Timer; use crate::joypad::{Button};
use crate::joypad::{Joypad, Button};
pub struct Emulator { pub struct Emulator {
bus: Bus, bus: Bus,
cpu: CPU, cpu: CPU,
ppu: Rc<RefCell<PPU>>,
timer: Rc<RefCell<Timer>>,
joypad: Rc<RefCell<Joypad>>,
} }
impl Emulator { impl Emulator {
pub fn new() -> Self { 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 { Self {
bus: Bus::new(Rc::clone(&ppu), Rc::clone(&joypad), Rc::clone(&timer)), bus: Bus::new(),
cpu: CPU::new(), cpu: CPU::new(),
ppu,
timer,
joypad,
} }
} }
pub fn handle_input(&mut self, input: &WinitInputHelper) { pub fn handle_input(&mut self, input: &WinitInputHelper) {
let mut change = false; let mut change = false;
let mut joypad = self.joypad.borrow_mut();
if input.key_pressed(VirtualKeyCode::K) { if input.key_pressed(VirtualKeyCode::K) {
change = true; change = true;
joypad.press(Button::A); self.bus.joypad.press(Button::A);
} }
if input.key_pressed(VirtualKeyCode::J) { if input.key_pressed(VirtualKeyCode::J) {
change = true; change = true;
joypad.press(Button::B); self.bus.joypad.press(Button::B);
} }
if input.key_pressed(VirtualKeyCode::W) { if input.key_pressed(VirtualKeyCode::W) {
change = true; change = true;
joypad.press(Button::Up); self.bus.joypad.press(Button::Up);
} }
if input.key_pressed(VirtualKeyCode::S) { if input.key_pressed(VirtualKeyCode::S) {
change = true; change = true;
joypad.press(Button::Down); self.bus.joypad.press(Button::Down);
} }
if input.key_pressed(VirtualKeyCode::A) { if input.key_pressed(VirtualKeyCode::A) {
change = true; change = true;
joypad.press(Button::Left); self.bus.joypad.press(Button::Left);
} }
if input.key_pressed(VirtualKeyCode::D) { if input.key_pressed(VirtualKeyCode::D) {
change = true; change = true;
joypad.press(Button::Right); self.bus.joypad.press(Button::Right);
} }
if input.key_pressed(VirtualKeyCode::N) { if input.key_pressed(VirtualKeyCode::N) {
change = true; change = true;
joypad.press(Button::Start); self.bus.joypad.press(Button::Start);
} }
if input.key_pressed(VirtualKeyCode::B) { if input.key_pressed(VirtualKeyCode::B) {
change = true; change = true;
joypad.press(Button::Select); self.bus.joypad.press(Button::Select);
} }
if input.key_released(VirtualKeyCode::K) { if input.key_released(VirtualKeyCode::K) {
change = true; change = true;
joypad.release(Button::A); self.bus.joypad.release(Button::A);
} }
if input.key_released(VirtualKeyCode::J) { if input.key_released(VirtualKeyCode::J) {
change = true; change = true;
joypad.release(Button::B); self.bus.joypad.release(Button::B);
} }
if input.key_released(VirtualKeyCode::W) { if input.key_released(VirtualKeyCode::W) {
change = true; change = true;
joypad.release(Button::Up); self.bus.joypad.release(Button::Up);
} }
if input.key_released(VirtualKeyCode::S) { if input.key_released(VirtualKeyCode::S) {
change = true; change = true;
joypad.release(Button::Down); self.bus.joypad.release(Button::Down);
} }
if input.key_released(VirtualKeyCode::A) { if input.key_released(VirtualKeyCode::A) {
change = true; change = true;
joypad.release(Button::Left); self.bus.joypad.release(Button::Left);
} }
if input.key_released(VirtualKeyCode::D) { if input.key_released(VirtualKeyCode::D) {
change = true; change = true;
joypad.release(Button::Right); self.bus.joypad.release(Button::Right);
} }
if input.key_released(VirtualKeyCode::N) { if input.key_released(VirtualKeyCode::N) {
change = true; change = true;
joypad.release(Button::Start); self.bus.joypad.release(Button::Start);
} }
if input.key_released(VirtualKeyCode::B) { if input.key_released(VirtualKeyCode::B) {
change = true; change = true;
joypad.release(Button::Select); self.bus.joypad.release(Button::Select);
} }
if change { if change {
self.bus.set_interrupt_flag(Interrupt::Joypad, true); 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 { 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.ppu.borrow_mut().do_cycles(&mut self.bus, cycles, frame_buffer); self.bus.ppu.do_cycles(cycles, frame_buffer);
self.timer.borrow_mut().do_cycles(&mut self.bus, cycles); 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 // 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()));
@ -124,8 +122,21 @@ impl Emulator {
let mut frame: [u8; 144 * 160 * 4] = [0; 144 * 160 * 4]; let mut frame: [u8; 144 * 160 * 4] = [0; 144 * 160 * 4];
while !exit { while !exit {
self.cpu.run(&mut self.bus); 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); let cycles = self.cpu.get_last_op_cycles().to_t();
self.timer.borrow_mut().do_cycles(&mut self.bus, 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() >= 1258895; // log 1
exit = self.cpu.get_exec_calls_count() >= 161502; // log 2 exit = self.cpu.get_exec_calls_count() >= 161502; // log 2

View File

@ -3,7 +3,7 @@ use crate::utils::{
get_bit, get_bit,
set_bit, set_bit,
}; };
use crate::bus::{Bus, VIDEO_RAM, SPRITE_ATTRIBUTE_TABLE}; use crate::bus::{SPRITE_ATTRIBUTE_TABLE};
use crate::cpu::{Cycles, Interrupt}; use crate::cpu::{Cycles, Interrupt};
pub const LCD_WIDTH: u32 = 160; 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 OBJECT_PALETTE_1_ADDRESS: u16 = 0xFF49;
pub const WINDOW_Y_ADDRESS: u16 = 0xFF4A; pub const WINDOW_Y_ADDRESS: u16 = 0xFF4A;
pub const WINDOW_X_ADDRESS: u16 = 0xFF4B; pub const WINDOW_X_ADDRESS: u16 = 0xFF4B;
pub const TILE_MAP_ADDRESS: u16 = 0x9800; pub const TILE_MAP_ADDRESS: u16 = 0x9800;
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
@ -184,7 +185,7 @@ impl Sprite {
let tile_line = y.rem_euclid(height) * 2; let tile_line = y.rem_euclid(height) * 2;
let addr = 0x8000 + (tile_number as u16 * 16) + tile_line as u16; 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_1 = vram[(addr - vram_start) as usize];
let tile_byte_2 = vram[(addr - vram_start + 1) 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); let bit_pixels_array = PPU::get_byte_pixels(tile_byte_1, tile_byte_2);
@ -204,6 +205,8 @@ impl Sprite {
pub struct PPU { pub struct PPU {
state: bool, state: bool,
vblank_request: bool,
lcdstat_request: bool,
cycles: Cycles, cycles: Cycles,
sprite_buffer: Vec<Sprite>, sprite_buffer: Vec<Sprite>,
window_y_counter: u8, window_y_counter: u8,
@ -217,6 +220,7 @@ pub struct PPU {
scroll_y: u8, scroll_y: u8,
window_x: u8, window_x: u8,
window_y: u8, window_y: u8,
io_registers: [u8; 12],
vram: [u8; 0x2000], vram: [u8; 0x2000],
oam: [u8; 0xA0], oam: [u8; 0xA0],
} }
@ -225,6 +229,8 @@ impl PPU {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
state: false, state: false,
vblank_request: false,
lcdstat_request: false,
cycles: Cycles(0), cycles: Cycles(0),
sprite_buffer: Vec::new(), sprite_buffer: Vec::new(),
window_y_counter: 0, window_y_counter: 0,
@ -238,25 +244,79 @@ impl PPU {
scroll_y: 0, scroll_y: 0,
window_x: 0, window_x: 0,
window_y: 0, window_y: 0,
io_registers: [0; 12],
vram: [0; 0x2000], vram: [0; 0x2000],
oam: [0; 0xA0], 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 { 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) { 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 { 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) { 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) { pub fn reset_cycles(&mut self) {
@ -267,38 +327,38 @@ impl PPU {
self.cycles.0 += cycles.0; 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; self.lcd_control_cache = None;
if !self.get_lcd_control(bus, LCDControl::LCDEnable) { if !self.get_lcd_control(LCDControl::LCDEnable) {
self.increment_cycles(cycles); self.increment_cycles(cycles);
return; 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.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 // Mode 2 OAM scan
PPU::set_lcd_status(bus, LCDStatus::ModeFlag(LCDStatusModeFlag::SearchingOAM), true); self.set_lcd_status(LCDStatus::ModeFlag(LCDStatusModeFlag::SearchingOAM), true);
self.stat_interrupt(bus); self.stat_interrupt();
self.oam_search(bus); self.oam_search();
} else if self.cycles.0 > 80 && self.cycles.0 <= 80 + 172 && !PPU::get_lcd_status(bus, LCDStatus::ModeFlag(LCDStatusModeFlag::TransferringToLCD)) { } 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 // Mode 3 drawing pixel line. This could also last 289 cycles
self.scroll_x = bus.read(SCROLL_X_ADDRESS); self.scroll_x = self.get_register(SCROLL_X_ADDRESS);
self.scroll_y = bus.read(SCROLL_Y_ADDRESS); self.scroll_y = self.get_register(SCROLL_Y_ADDRESS);
self.window_x = bus.read(WINDOW_X_ADDRESS); self.window_x = self.get_register(WINDOW_X_ADDRESS);
self.window_y = bus.read(WINDOW_Y_ADDRESS); self.window_y = self.get_register(WINDOW_Y_ADDRESS);
PPU::set_lcd_status(bus, LCDStatus::ModeFlag(LCDStatusModeFlag::TransferringToLCD), true); self.set_lcd_status(LCDStatus::ModeFlag(LCDStatusModeFlag::TransferringToLCD), true);
self.draw_line(bus, frame_buffer); self.draw_line(frame_buffer);
} else if self.cycles.0 > 80 + 172 && self.cycles.0 <= 80 + 172 + 204 && !PPU::get_lcd_status(bus, 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
PPU::set_lcd_status(bus, LCDStatus::ModeFlag(LCDStatusModeFlag::HBlank), true); self.set_lcd_status(LCDStatus::ModeFlag(LCDStatusModeFlag::HBlank), true);
self.stat_interrupt(bus); 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 // Mode 1 Vertical blank
PPU::set_lcd_status(bus, LCDStatus::ModeFlag(LCDStatusModeFlag::VBlank), true); self.set_lcd_status(LCDStatus::ModeFlag(LCDStatusModeFlag::VBlank), true);
bus.set_interrupt_flag(Interrupt::VBlank, true); self.set_interrupt(Interrupt::VBlank, true);
self.stat_interrupt(bus); self.stat_interrupt();
} }
self.increment_cycles(cycles); self.increment_cycles(cycles);
@ -314,43 +374,43 @@ impl PPU {
self.lcd_y = 0; self.lcd_y = 0;
self.window_y_counter = 0; self.window_y_counter = 0;
} }
bus.force_write(LCD_Y_ADDRESS, self.lcd_y); self.force_set_register(LCD_Y_ADDRESS, self.lcd_y);
self.stat_interrupt(bus); self.stat_interrupt();
} }
} }
fn stat_interrupt(&mut self, bus: &mut Bus) { fn stat_interrupt(&mut self) {
let prev_state = self.state; let prev_state = self.state;
let lyc_compare = self.lcd_y == bus.read(LCD_Y_COMPARE_ADDRESS); let lyc_compare = self.lcd_y == self.get_register(LCD_Y_COMPARE_ADDRESS);
PPU::set_lcd_status(bus, LCDStatus::LYCFlag, lyc_compare); self.set_lcd_status(LCDStatus::LYCFlag, lyc_compare);
self.state = self.state =
( (
lyc_compare && lyc_compare &&
PPU::get_lcd_status(bus, LCDStatus::LYCInterrupt) self.get_lcd_status(LCDStatus::LYCInterrupt)
) || ) ||
( (
PPU::get_lcd_status(bus, LCDStatus::Mode2OAMInterrupt) && self.get_lcd_status(LCDStatus::Mode2OAMInterrupt) &&
PPU::get_lcd_status(bus, LCDStatus::ModeFlag(LCDStatusModeFlag::SearchingOAM)) self.get_lcd_status(LCDStatus::ModeFlag(LCDStatusModeFlag::SearchingOAM))
) || ( ) || (
PPU::get_lcd_status(bus, LCDStatus::Mode0HBlankInterrupt) && self.get_lcd_status(LCDStatus::Mode0HBlankInterrupt) &&
PPU::get_lcd_status(bus, LCDStatus::ModeFlag(LCDStatusModeFlag::HBlank)) self.get_lcd_status(LCDStatus::ModeFlag(LCDStatusModeFlag::HBlank))
) || ( ) || (
PPU::get_lcd_status(bus, LCDStatus::Mode1VBlankInterrupt) && self.get_lcd_status(LCDStatus::Mode1VBlankInterrupt) &&
PPU::get_lcd_status(bus, LCDStatus::ModeFlag(LCDStatusModeFlag::VBlank)) self.get_lcd_status(LCDStatus::ModeFlag(LCDStatusModeFlag::VBlank))
); );
if self.state && !prev_state { 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) { fn oam_search(&mut self) {
if !self.get_lcd_control(bus, LCDControl::ObjectEnable) { if !self.get_lcd_control(LCDControl::ObjectEnable) {
return; return;
} }
self.sprite_buffer = Vec::new(); self.sprite_buffer = Vec::new();
let palette_0 = bus.read(OBJECT_PALETTE_0_ADDRESS); let palette_0 = self.get_register(OBJECT_PALETTE_0_ADDRESS);
let palette_1 = bus.read(OBJECT_PALETTE_1_ADDRESS); let palette_1 = self.get_register(OBJECT_PALETTE_1_ADDRESS);
let long_sprites = self.get_lcd_control(bus, LCDControl::ObjectSize); let long_sprites = self.get_lcd_control(LCDControl::ObjectSize);
let mut addr = SPRITE_ATTRIBUTE_TABLE.min().unwrap(); let mut addr = SPRITE_ATTRIBUTE_TABLE.min().unwrap();
while addr <= SPRITE_ATTRIBUTE_TABLE.max().unwrap() { while addr <= SPRITE_ATTRIBUTE_TABLE.max().unwrap() {
// The gameboy only supports 10 sprites per line, // The gameboy only supports 10 sprites per line,
@ -416,11 +476,11 @@ impl PPU {
return None; 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 { let byte = match self.lcd_control_cache {
Some(byte) => byte, Some(byte) => byte,
None => { None => {
let byte = bus.read(LCD_CONTROL_ADDRESS); let byte = self.get_register(LCD_CONTROL_ADDRESS);
self.lcd_control_cache = Some(byte); self.lcd_control_cache = Some(byte);
byte byte
}, },
@ -428,8 +488,8 @@ impl PPU {
control.get(byte) control.get(byte)
} }
pub fn get_lcd_status(bus: &Bus, status: LCDStatus) -> bool { pub fn get_lcd_status(&self, status: LCDStatus) -> bool {
let byte = bus.read(LCD_STATUS_ADDRESS); let byte = self.get_register(LCD_STATUS_ADDRESS);
match status { match status {
LCDStatus::LYCInterrupt => get_bit(byte, BitIndex::I6), LCDStatus::LYCInterrupt => get_bit(byte, BitIndex::I6),
LCDStatus::Mode2OAMInterrupt => get_bit(byte, BitIndex::I5), LCDStatus::Mode2OAMInterrupt => get_bit(byte, BitIndex::I5),
@ -445,8 +505,8 @@ impl PPU {
} }
} }
fn set_lcd_status(bus: &mut Bus, status: LCDStatus, val: bool) { fn set_lcd_status(&mut self, status: LCDStatus, val: bool) {
let mut byte = bus.read(LCD_STATUS_ADDRESS); let mut byte = self.get_register(LCD_STATUS_ADDRESS);
byte = match status { byte = match status {
LCDStatus::LYCInterrupt => set_bit(byte, val, BitIndex::I6), LCDStatus::LYCInterrupt => set_bit(byte, val, BitIndex::I6),
LCDStatus::Mode2OAMInterrupt => set_bit(byte, val, BitIndex::I5), LCDStatus::Mode2OAMInterrupt => set_bit(byte, val, BitIndex::I5),
@ -460,7 +520,7 @@ impl PPU {
LCDStatusModeFlag::TransferringToLCD => (byte & 0b11111100) | 3, 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) { 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)) (self.read_vram(addr), self.read_vram(addr + 1))
} }
fn get_window_pixel(&mut self, lcd_x: u8, bus: &Bus) -> Option<Pixel> { fn get_window_pixel(&mut self, lcd_x: u8) -> Option<Pixel> {
if !self.get_lcd_control(bus, LCDControl::WindowEnable) { if !self.get_lcd_control(LCDControl::WindowEnable) {
return None; return None;
} }
@ -512,8 +572,8 @@ impl PPU {
bit_pixels_array[bit_pixel_index] bit_pixels_array[bit_pixel_index]
}, },
None => { None => {
let default_mode = self.get_lcd_control(bus, LCDControl::TileAddressMode); let default_mode = self.get_lcd_control(LCDControl::TileAddressMode);
let tilemap_area = match self.get_lcd_control(bus, LCDControl::WindowTileMapAddress) { let tilemap_area = match self.get_lcd_control(LCDControl::WindowTileMapAddress) {
true => 0x9C00, true => 0x9C00,
false => 0x9800, false => 0x9800,
}; };
@ -530,8 +590,8 @@ impl PPU {
Some(PPU::get_pixel(PPU::get_palette(bit_pixel, self.bg_palette))) Some(PPU::get_pixel(PPU::get_palette(bit_pixel, self.bg_palette)))
} }
fn get_background_pixel(&mut self, lcd_x: u8, bus: &Bus) -> Option<Pixel> { fn get_background_pixel(&mut self, lcd_x: u8) -> Option<Pixel> {
if !self.get_lcd_control(bus, LCDControl::BackgroundPriority) { if !self.get_lcd_control(LCDControl::BackgroundPriority) {
return None; return None;
} }
let lcd_y = self.lcd_y; let lcd_y = self.lcd_y;
@ -548,8 +608,8 @@ impl PPU {
bit_pixels_array[bit_pixel_index] bit_pixels_array[bit_pixel_index]
}, },
None => { None => {
let default_mode = self.get_lcd_control(bus, LCDControl::TileAddressMode); let default_mode = self.get_lcd_control(LCDControl::TileAddressMode);
let tilemap_area = match self.get_lcd_control(bus, LCDControl::BackgroundTileMapAddress) { let tilemap_area = match self.get_lcd_control(LCDControl::BackgroundTileMapAddress) {
true => 0x9C00, true => 0x9C00,
false => 0x9800, false => 0x9800,
}; };
@ -565,7 +625,7 @@ impl PPU {
Some(PPU::get_pixel(PPU::get_palette(bit_pixel, self.bg_palette))) 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; let lcd_y = self.lcd_y;
if lcd_y as u32 >= LCD_HEIGHT { if lcd_y as u32 >= LCD_HEIGHT {
return; return;
@ -573,25 +633,25 @@ impl PPU {
self.current_background_pixels = None; self.current_background_pixels = None;
self.current_window_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 lcd_x: u8 = 0;
let mut window_drawn = false; let mut window_drawn = false;
while (lcd_x as u32) < LCD_WIDTH { while (lcd_x as u32) < LCD_WIDTH {
let idx = (lcd_x as usize + (lcd_y as usize * LCD_WIDTH as usize)) * 4; 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; window_drawn = true;
let rgba = PPU::get_rgba(window_pixel, WINDOW_COLORS); let rgba = PPU::get_rgba(window_pixel, WINDOW_COLORS);
frame_buffer[idx] = rgba[0]; frame_buffer[idx] = rgba[0];
frame_buffer[idx + 1] = rgba[1]; frame_buffer[idx + 1] = rgba[1];
frame_buffer[idx + 2] = rgba[2]; 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); let rgba = PPU::get_rgba(background_pixel, BACKGROUND_COLORS);
frame_buffer[idx] = rgba[0]; frame_buffer[idx] = rgba[0];
frame_buffer[idx + 1] = rgba[1]; frame_buffer[idx + 1] = rgba[1];
frame_buffer[idx + 2] = rgba[2]; 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) { if let Some((sprite_pixel, palette_zero)) = self.find_sprite_pixel(lcd_x) {
let rgba = PPU::get_rgba(sprite_pixel, match palette_zero { let rgba = PPU::get_rgba(sprite_pixel, match palette_zero {
true => SPRITE_0_COLORS, true => SPRITE_0_COLORS,

View File

@ -1,5 +1,4 @@
use crate::cpu::{Interrupt, Cycles}; use crate::cpu::{Cycles};
use crate::bus::Bus;
use crate::utils::{ use crate::utils::{
BitIndex, BitIndex,
get_bit, get_bit,
@ -14,7 +13,9 @@ 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],
} }
impl Timer { impl Timer {
@ -24,10 +25,40 @@ 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],
} }
} }
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 { pub fn read_divider(&self) -> u8 {
self.divider.to_be_bytes()[0] self.divider.to_be_bytes()[0]
} }
@ -37,36 +68,36 @@ impl Timer {
self.divider = 0; self.divider = 0;
} }
pub fn do_cycles(&mut self, bus: &mut Bus, cycles: Cycles) { pub fn do_cycles(&mut self, cycles: Cycles) {
self.is_enabled = Timer::is_timer_enabled(bus); self.is_enabled = self.is_timer_enabled();
self.control = bus.read(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(bus); self.cycle();
count += 1; count += 1;
} }
} }
fn cycle(&mut self, bus: &mut Bus) { fn cycle(&mut self) {
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();
if self.prev_result && !result { 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 { if tima == 0 {
bus.write(TIMER_COUNTER_ADDRESS, bus.read(TIMER_MODULO_ADDRESS)); self.set_register(TIMER_COUNTER_ADDRESS, self.get_register(TIMER_MODULO_ADDRESS));
bus.set_interrupt_flag(Interrupt::Timer, true); self.interrupt = true;
} else { } else {
bus.write(TIMER_COUNTER_ADDRESS, tima); self.set_register(TIMER_COUNTER_ADDRESS, tima);
} }
} }
self.prev_result = result; self.prev_result = result;
} }
fn is_timer_enabled(bus: &Bus) -> bool { fn is_timer_enabled(&self) -> bool {
get_bit(bus.read(TIMER_CONTROL_ADDRESS), BitIndex::I2) get_bit(self.get_register(TIMER_CONTROL_ADDRESS), BitIndex::I2)
} }
fn get_tima_rate(&self) -> bool { fn get_tima_rate(&self) -> bool {