mirror of
https://github.com/SourMesen/Mesen2.git
synced 2025-04-02 10:21:44 -04:00
155 lines
3.8 KiB
C++
155 lines
3.8 KiB
C++
#include "pch.h"
|
|
#include "GBA/GbaTimer.h"
|
|
#include "GBA/GbaMemoryManager.h"
|
|
#include "GBA/APU/GbaApu.h"
|
|
#include "Utilities/Serializer.h"
|
|
#include "Utilities/BitUtilities.h"
|
|
|
|
void GbaTimer::Init(GbaMemoryManager* memoryManager, GbaApu* apu)
|
|
{
|
|
_memoryManager = memoryManager;
|
|
_apu = apu;
|
|
}
|
|
|
|
void GbaTimer::ClockTimer(int i)
|
|
{
|
|
_state.Timer[i].Timer = _state.Timer[i].ReloadValue;
|
|
if(i < 3) {
|
|
if(_state.Timer[i + 1].Mode && _state.Timer[i + 1].Enabled) {
|
|
if(++_state.Timer[i + 1].Timer == 0) {
|
|
ClockTimer(i + 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
if(_state.Timer[i].IrqEnabled) {
|
|
_memoryManager->SetIrqSource((GbaIrqSource)((int)GbaIrqSource::Timer0 << i));
|
|
}
|
|
|
|
_apu->ClockFifo(i);
|
|
}
|
|
|
|
void GbaTimer::ProcessPendingWrites()
|
|
{
|
|
_hasPendingWrites = false;
|
|
for(int i = 0; i < 4; i++) {
|
|
GbaTimerState& timer = _state.Timer[i];
|
|
if(timer.WritePending) {
|
|
timer.WritePending = false;
|
|
|
|
switch(timer.Control & 0x03) {
|
|
case 0: timer.PrescaleMask = 0; break;
|
|
case 1: timer.PrescaleMask = 0x3F; break;
|
|
case 2: timer.PrescaleMask = 0xFF; break;
|
|
case 3: timer.PrescaleMask = 0x3FF; break;
|
|
}
|
|
timer.Mode = timer.Control & 0x04;
|
|
timer.IrqEnabled = timer.Control & 0x40;
|
|
|
|
bool enabled = (timer.Control & 0x80);
|
|
if(!timer.Enabled && enabled) {
|
|
timer.EnableDelay = 2;
|
|
timer.Enabled = true;
|
|
_hasPendingTimers = true;
|
|
_memoryManager->SetPendingUpdateFlag();
|
|
} else if(timer.Enabled && !enabled) {
|
|
timer.Enabled = false;
|
|
timer.ProcessTimer = false;
|
|
timer.EnableDelay = 0;
|
|
}
|
|
|
|
timer.ReloadValue = timer.NewReloadValue;
|
|
}
|
|
}
|
|
}
|
|
|
|
void GbaTimer::ProcessPendingTimers()
|
|
{
|
|
_hasPendingTimers = false;
|
|
for(int i = 0; i < 4; i++) {
|
|
GbaTimerState& timer = _state.Timer[i];
|
|
|
|
if(timer.EnableDelay) {
|
|
timer.EnableDelay--;
|
|
if(timer.EnableDelay == 1) {
|
|
timer.Timer = timer.ReloadValue;
|
|
if(timer.Mode) {
|
|
timer.EnableDelay = 0;
|
|
timer.ProcessTimer = false;
|
|
}
|
|
} else if(timer.EnableDelay == 0) {
|
|
timer.ProcessTimer = true;
|
|
}
|
|
_hasPendingTimers |= timer.EnableDelay != 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
void GbaTimer::TriggerUpdate(GbaTimerState& timer)
|
|
{
|
|
timer.WritePending = true;
|
|
_hasPendingWrites = true;
|
|
_memoryManager->SetPendingLateUpdateFlag();
|
|
}
|
|
|
|
void GbaTimer::WriteRegister(uint32_t addr, uint8_t value)
|
|
{
|
|
uint8_t timerIndex = (addr & 0x0C) >> 2;
|
|
GbaTimerState& timer = _state.Timer[timerIndex];
|
|
switch(addr) {
|
|
case 0x100: case 0x104: case 0x108: case 0x10C:
|
|
BitUtilities::SetBits<0>(timer.NewReloadValue, value);
|
|
TriggerUpdate(timer);
|
|
break;
|
|
|
|
case 0x101: case 0x105: case 0x109: case 0x10D:
|
|
BitUtilities::SetBits<8>(timer.NewReloadValue, value);
|
|
TriggerUpdate(timer);
|
|
break;
|
|
|
|
case 0x102: case 0x106: case 0x10A: case 0x10E: {
|
|
if(timerIndex == 0) {
|
|
//Mode is always disabled and always returns 0 for timer 0
|
|
value &= ~0x04;
|
|
}
|
|
|
|
timer.Control = value & 0xC7;
|
|
TriggerUpdate(timer);
|
|
break;
|
|
}
|
|
|
|
case 0x103: case 0x107: case 0x10B: case 0x10F:
|
|
break;
|
|
}
|
|
}
|
|
|
|
uint8_t GbaTimer::ReadRegister(uint32_t addr)
|
|
{
|
|
GbaTimerState& timer = _state.Timer[(addr & 0x0C) >> 2];
|
|
switch(addr) {
|
|
case 0x100: case 0x104: case 0x108: case 0x10C: return BitUtilities::GetBits<0>(timer.Timer);
|
|
case 0x101: case 0x105: case 0x109: case 0x10D: return BitUtilities::GetBits<8>(timer.Timer);
|
|
case 0x102: case 0x106: case 0x10A: case 0x10E: return timer.Control;
|
|
case 0x103: case 0x107: case 0x10B: case 0x10F: return 0;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void GbaTimer::Serialize(Serializer& s)
|
|
{
|
|
for(int i = 0; i < 4; i++) {
|
|
SVI(_state.Timer[i].ReloadValue);
|
|
SVI(_state.Timer[i].NewReloadValue);
|
|
SVI(_state.Timer[i].Control);
|
|
SVI(_state.Timer[i].PrescaleMask);
|
|
SVI(_state.Timer[i].Timer);
|
|
SVI(_state.Timer[i].EnableDelay);
|
|
SVI(_state.Timer[i].WritePending);
|
|
SVI(_state.Timer[i].Mode);
|
|
SVI(_state.Timer[i].IrqEnabled);
|
|
SVI(_state.Timer[i].Enabled);
|
|
SVI(_state.Timer[i].ProcessTimer);
|
|
}
|
|
SV(_hasPendingTimers);
|
|
}
|