mirror of
https://github.com/SourMesen/Mesen2.git
synced 2025-04-02 10:21:44 -04:00
411 lines
11 KiB
C++
411 lines
11 KiB
C++
#include "pch.h"
|
|
#include "GBA/GbaCpu.h"
|
|
#include "GBA/GbaMemoryManager.h"
|
|
#include "GBA/GbaRomPrefetch.h"
|
|
#include "Shared/Emulator.h"
|
|
#include "Shared/EmuSettings.h"
|
|
#include "Utilities/Serializer.h"
|
|
|
|
void GbaCpu::Init(Emulator* emu, GbaMemoryManager* memoryManager, GbaRomPrefetch* prefetch)
|
|
{
|
|
_emu = emu;
|
|
_memoryManager = memoryManager;
|
|
_prefetch = prefetch;
|
|
|
|
_state = {};
|
|
_state.Pipeline.ReloadRequested = true;
|
|
|
|
if(_emu->GetSettings()->GetGbaConfig().SkipBootScreen) {
|
|
_state.R[13] = 0x3007F00;
|
|
_state.R[14] = 0x8000000;
|
|
_state.R[15] = 0x8000000;
|
|
_state.UserRegs[4] = 0x300FCA0;
|
|
_state.UserRegs[5] = 0x3007F00;
|
|
_state.UserRegs[6] = 0x00000C0;
|
|
_state.IrqRegs[0] = 0x3007FA0;
|
|
_state.SupervisorRegs[0] = 0x3007FE0;
|
|
_state.CPSR.Mode = GbaCpuMode::System;
|
|
} else {
|
|
_state.CPSR.Mode = GbaCpuMode::Supervisor;
|
|
_state.CPSR.IrqDisable = true;
|
|
_state.CPSR.FiqDisable = true;
|
|
}
|
|
}
|
|
|
|
GbaCpu::~GbaCpu()
|
|
{
|
|
}
|
|
|
|
void GbaCpu::StaticInit()
|
|
{
|
|
InitArmOpTable();
|
|
InitThumbOpTable();
|
|
}
|
|
|
|
void GbaCpu::SwitchMode(GbaCpuMode mode)
|
|
{
|
|
//High bit of mode is always set according to psr test
|
|
mode = (GbaCpuMode)((int)mode | 0x10);
|
|
|
|
if(_state.CPSR.Mode == mode) {
|
|
return;
|
|
}
|
|
|
|
GbaCpuMode orgMode = _state.CPSR.Mode;
|
|
switch(orgMode) {
|
|
default:
|
|
case GbaCpuMode::System:
|
|
case GbaCpuMode::User:
|
|
memcpy(_state.UserRegs, &_state.R[8], 7 * sizeof(uint32_t));
|
|
break;
|
|
|
|
case GbaCpuMode::Fiq:
|
|
memcpy(_state.FiqRegs, &_state.R[8], 7 * sizeof(uint32_t));
|
|
break;
|
|
|
|
case GbaCpuMode::Irq:
|
|
memcpy(_state.UserRegs, &_state.R[8], 5 * sizeof(uint32_t));
|
|
memcpy(_state.IrqRegs, &_state.R[13], 2 * sizeof(uint32_t));
|
|
break;
|
|
|
|
case GbaCpuMode::Supervisor:
|
|
memcpy(_state.UserRegs, &_state.R[8], 5 * sizeof(uint32_t));
|
|
memcpy(_state.SupervisorRegs, &_state.R[13], 2 * sizeof(uint32_t));
|
|
break;
|
|
|
|
case GbaCpuMode::Abort:
|
|
memcpy(_state.UserRegs, &_state.R[8], 5 * sizeof(uint32_t));
|
|
memcpy(_state.AbortRegs, &_state.R[13], 2 * sizeof(uint32_t));
|
|
break;
|
|
|
|
case GbaCpuMode::Undefined:
|
|
memcpy(_state.UserRegs, &_state.R[8], 5 * sizeof(uint32_t));
|
|
memcpy(_state.UndefinedRegs, &_state.R[13], 2 * sizeof(uint32_t));
|
|
break;
|
|
}
|
|
|
|
_state.CPSR.Mode = mode;
|
|
|
|
if(mode != GbaCpuMode::Fiq) {
|
|
memcpy(&_state.R[8], _state.UserRegs, 7 * sizeof(uint32_t));
|
|
switch(mode) {
|
|
case GbaCpuMode::Irq: memcpy(&_state.R[13], _state.IrqRegs, 2 * sizeof(uint32_t)); break;
|
|
case GbaCpuMode::Supervisor: memcpy(&_state.R[13], _state.SupervisorRegs, 2 * sizeof(uint32_t)); break;
|
|
case GbaCpuMode::Abort: memcpy(&_state.R[13], _state.AbortRegs, 2 * sizeof(uint32_t)); break;
|
|
case GbaCpuMode::Undefined: memcpy(&_state.R[13], _state.UndefinedRegs, 2 * sizeof(uint32_t)); break;
|
|
}
|
|
} else {
|
|
memcpy(&_state.R[8], _state.FiqRegs, 7 * sizeof(uint32_t));
|
|
}
|
|
}
|
|
|
|
void GbaCpu::ReloadPipeline()
|
|
{
|
|
GbaCpuPipeline& pipe = _state.Pipeline;
|
|
pipe.Mode = GbaAccessMode::Prefetch | (_state.CPSR.Thumb ? GbaAccessMode::HalfWord : GbaAccessMode::Word);
|
|
|
|
pipe.ReloadRequested = false;
|
|
pipe.Fetch.Address = _state.R[15] = _state.R[15] & (_state.CPSR.Thumb ? ~0x01 : ~0x03);
|
|
_prefetch->ForceNonSequential(_state.R[15]);
|
|
|
|
pipe.Fetch.OpCode = ReadCode(pipe.Mode, pipe.Fetch.Address);
|
|
pipe.Execute = pipe.Decode;
|
|
pipe.Decode = pipe.Fetch;
|
|
|
|
pipe.Fetch.Address = _state.R[15] = _state.CPSR.Thumb ? (_state.R[15] + 2) : (_state.R[15] + 4);
|
|
pipe.Fetch.OpCode = ReadCode(pipe.Mode, pipe.Fetch.Address);
|
|
}
|
|
|
|
void GbaCpu::CheckForIrqs()
|
|
{
|
|
uint32_t originalPc = _state.Pipeline.Execute.Address;
|
|
bool thumb = _state.CPSR.Thumb;
|
|
ProcessException(GbaCpuMode::Irq, GbaCpuVector::Irq);
|
|
if(thumb) {
|
|
_state.R[14] += 2;
|
|
}
|
|
ProcessPipeline();
|
|
_emu->ProcessInterrupt<CpuType::Gba>(originalPc, _state.Pipeline.Execute.Address, false);
|
|
}
|
|
|
|
void GbaCpu::ProcessException(GbaCpuMode mode, GbaCpuVector vector)
|
|
{
|
|
#ifndef DUMMYCPU
|
|
GbaCpuFlags cpsr = _state.CPSR;
|
|
SwitchMode(mode);
|
|
GetSpsr() = cpsr;
|
|
_state.CPSR.Thumb = false;
|
|
_state.CPSR.IrqDisable = true;
|
|
_state.R[14] = _state.Pipeline.Decode.Address;
|
|
_state.R[15] = (uint32_t)vector;
|
|
_state.Pipeline.ReloadRequested = true;
|
|
#endif
|
|
}
|
|
|
|
uint32_t GbaCpu::ReadCode(GbaAccessModeVal mode, uint32_t addr)
|
|
{
|
|
#ifndef DUMMYCPU
|
|
//Next access should be sequential
|
|
//This is done before the call to Read() because e.g if DMA pauses the CPU and
|
|
//runs, the next access will not be sequential (force-nseq-access test)
|
|
_state.Pipeline.Mode |= GbaAccessMode::Sequential;
|
|
return _memoryManager->Read(mode, addr);
|
|
#else
|
|
uint32_t value = _memoryManager->DebugCpuRead(mode, addr);
|
|
LogMemoryOperation(addr, value, mode, MemoryOperationType::ExecOpCode);
|
|
return value;
|
|
#endif
|
|
}
|
|
|
|
uint32_t GbaCpu::Read(GbaAccessModeVal mode, uint32_t addr)
|
|
{
|
|
#ifndef DUMMYCPU
|
|
_state.Pipeline.Mode &= ~GbaAccessMode::Sequential;
|
|
return _memoryManager->Read(mode, addr);
|
|
#else
|
|
uint32_t value = _memoryManager->DebugCpuRead(mode, addr);
|
|
LogMemoryOperation(addr, value, mode, MemoryOperationType::Read);
|
|
return value;
|
|
#endif
|
|
}
|
|
|
|
void GbaCpu::Write(GbaAccessModeVal mode, uint32_t addr, uint32_t value)
|
|
{
|
|
#ifndef DUMMYCPU
|
|
_state.Pipeline.Mode &= ~GbaAccessMode::Sequential;
|
|
_memoryManager->Write(mode, addr, value);
|
|
#else
|
|
LogMemoryOperation(addr, value, mode, MemoryOperationType::Write);
|
|
#endif
|
|
}
|
|
|
|
void GbaCpu::Idle()
|
|
{
|
|
#ifndef DUMMYCPU
|
|
_state.Pipeline.Mode &= ~GbaAccessMode::Sequential;
|
|
_memoryManager->ProcessIdleCycle();
|
|
#endif
|
|
}
|
|
|
|
void GbaCpu::Idle(uint8_t cycleCount)
|
|
{
|
|
switch(cycleCount) {
|
|
case 4: Idle(); [[fallthrough]];
|
|
case 3: Idle(); [[fallthrough]];
|
|
case 2: Idle(); [[fallthrough]];
|
|
case 1: Idle(); break;
|
|
}
|
|
}
|
|
|
|
uint32_t GbaCpu::R(uint8_t reg)
|
|
{
|
|
return _state.R[reg];
|
|
}
|
|
|
|
GbaCpuFlags& GbaCpu::GetSpsr()
|
|
{
|
|
switch(_state.CPSR.Mode) {
|
|
default:
|
|
case GbaCpuMode::User: return _state.CPSR;
|
|
case GbaCpuMode::Fiq: return _state.FiqSpsr;
|
|
case GbaCpuMode::Irq: return _state.IrqSpsr;
|
|
case GbaCpuMode::Supervisor: return _state.SupervisorSpsr;
|
|
case GbaCpuMode::Abort: return _state.AbortSpsr;
|
|
case GbaCpuMode::Undefined: return _state.UndefinedSpsr;
|
|
case GbaCpuMode::System: return _state.CPSR;
|
|
}
|
|
}
|
|
|
|
uint32_t GbaCpu::Add(uint32_t op1, uint32_t op2, bool carry, bool updateFlags)
|
|
{
|
|
uint32_t result = op1 + op2 + (uint32_t)carry;
|
|
if(updateFlags) {
|
|
uint32_t overflow = ~(op1 ^ op2) & (op1 ^ result) & (1 << 31);
|
|
_state.CPSR.Negative = result & (1 << 31);
|
|
_state.CPSR.Zero = (uint32_t)result == 0;
|
|
_state.CPSR.Overflow = overflow;
|
|
_state.CPSR.Carry = (op1 ^ op2 ^ overflow ^ result) & (1 << 31);
|
|
}
|
|
return (uint32_t)result;
|
|
}
|
|
|
|
uint32_t GbaCpu::Sub(uint32_t op1, uint32_t op2, bool carry, bool updateFlags)
|
|
{
|
|
return Add(op1, ~op2, carry, updateFlags);
|
|
}
|
|
|
|
uint32_t GbaCpu::LogicalOp(uint32_t result, bool carry, bool updateFlags)
|
|
{
|
|
//"If the S bit is set(and Rd is not R15, see below) the V flag in the CPSR will be unaffected, the C
|
|
//flag will be set to the carry out from the barrel shifter (or preserved when the shift
|
|
//operation is LSL #0), the Z flag will be set if and only if the result is all zeros, and the N
|
|
//flag will be set to the logical value of bit 31 of the result."
|
|
if(updateFlags) {
|
|
_state.CPSR.Carry = carry;
|
|
_state.CPSR.Zero = result == 0;
|
|
_state.CPSR.Negative = result & (1 << 31);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
uint32_t GbaCpu::RotateRight(uint32_t value, uint32_t shift)
|
|
{
|
|
return (value >> shift) | (value << (32 - shift));
|
|
}
|
|
|
|
uint32_t GbaCpu::RotateRight(uint32_t value, uint32_t shift, bool& carry)
|
|
{
|
|
carry = (value >> (shift - 1)) & 1;
|
|
return (value >> shift) | (value << (32 - shift));
|
|
}
|
|
|
|
uint32_t GbaCpu::ShiftLsl(uint32_t value, uint8_t shift, bool& carry)
|
|
{
|
|
if(shift) {
|
|
carry = shift < 33 ? (value & (1 << (32 - shift))) : 0;
|
|
value = shift < 32 ? (value << shift) : 0;
|
|
}
|
|
return value;
|
|
}
|
|
|
|
uint32_t GbaCpu::ShiftLsr(uint32_t value, uint8_t shift, bool& carry)
|
|
{
|
|
if(shift) {
|
|
carry = shift < 33 ? (value & (1 << (shift - 1))) : 0;
|
|
value = shift < 32 ? (value >> shift) : 0;
|
|
}
|
|
return value;
|
|
}
|
|
|
|
uint32_t GbaCpu::ShiftAsr(uint32_t value, uint8_t shift, bool& carry)
|
|
{
|
|
if(shift) {
|
|
carry = shift < 33 ? (value & (1 << (shift - 1))) : (value & (1 << 31));
|
|
value = shift < 32 ? ((int32_t)value >> shift) : ((int32_t)value >> 31);
|
|
}
|
|
return value;
|
|
}
|
|
|
|
uint32_t GbaCpu::ShiftRor(uint32_t value, uint8_t shift, bool& carry)
|
|
{
|
|
if(shift) {
|
|
shift &= 0x1F;
|
|
if(shift) {
|
|
value = (value << (32 - shift)) | (value >> shift);
|
|
}
|
|
carry = value & (1 << 31);
|
|
}
|
|
return value;
|
|
}
|
|
|
|
uint32_t GbaCpu::ShiftRrx(uint32_t value, bool& carry)
|
|
{
|
|
bool orgCarry = carry;
|
|
carry = value & 0x01;
|
|
return (value >> 1) | (orgCarry << 31);
|
|
}
|
|
|
|
void GbaCpu::PowerOn()
|
|
{
|
|
ProcessPipeline();
|
|
}
|
|
|
|
void GbaCpu::SetProgramCounter(uint32_t addr, bool thumb)
|
|
{
|
|
//Used by debugger - set new PC and reload pipeline (using debugger reads)
|
|
_state.R[15] = addr;
|
|
_state.CPSR.Thumb = thumb;
|
|
|
|
GbaCpuPipeline& pipe = _state.Pipeline;
|
|
pipe.Mode = GbaAccessMode::Prefetch | (_state.CPSR.Thumb ? GbaAccessMode::HalfWord : GbaAccessMode::Word) | GbaAccessMode::Sequential;
|
|
pipe.ReloadRequested = false;
|
|
pipe.Execute.Address = _state.R[15] = _state.R[15] & (_state.CPSR.Thumb ? ~0x01 : ~0x03);
|
|
pipe.Execute.OpCode = _memoryManager->DebugCpuRead(pipe.Mode, pipe.Execute.Address);
|
|
pipe.Decode.Address = _state.R[15] = _state.CPSR.Thumb ? (_state.R[15] + 2) : (_state.R[15] + 4);
|
|
pipe.Decode.OpCode = _memoryManager->DebugCpuRead(pipe.Mode, pipe.Decode.Address);
|
|
pipe.Fetch.Address = _state.R[15] = _state.CPSR.Thumb ? (_state.R[15] + 2) : (_state.R[15] + 4);
|
|
pipe.Fetch.OpCode = _memoryManager->DebugCpuRead(pipe.Mode, pipe.Fetch.Address);
|
|
}
|
|
|
|
GbaCpuState& GbaCpu::GetState()
|
|
{
|
|
_state.CycleCount = _memoryManager->GetMasterClock();
|
|
return _state;
|
|
}
|
|
|
|
void GbaCpu::Serialize(Serializer& s)
|
|
{
|
|
SV(_state.Pipeline.Fetch.Address);
|
|
SV(_state.Pipeline.Fetch.OpCode);
|
|
SV(_state.Pipeline.Decode.Address);
|
|
SV(_state.Pipeline.Decode.OpCode);
|
|
SV(_state.Pipeline.Execute.Address);
|
|
SV(_state.Pipeline.Execute.OpCode);
|
|
SV(_state.Pipeline.ReloadRequested);
|
|
SV(_state.Pipeline.Mode);
|
|
|
|
SV(_state.CPSR.Mode);
|
|
SV(_state.CPSR.Thumb);
|
|
SV(_state.CPSR.FiqDisable);
|
|
SV(_state.CPSR.IrqDisable);
|
|
SV(_state.CPSR.Overflow);
|
|
SV(_state.CPSR.Carry);
|
|
SV(_state.CPSR.Zero);
|
|
SV(_state.CPSR.Negative);
|
|
|
|
SV(_state.Stopped);
|
|
SVArray(_state.R, 16);
|
|
SVArray(_state.UserRegs, 7);
|
|
SVArray(_state.FiqRegs, 7);
|
|
SVArray(_state.IrqRegs, 2);
|
|
SVArray(_state.SupervisorRegs, 2);
|
|
SVArray(_state.AbortRegs, 2);
|
|
SVArray(_state.UndefinedRegs, 2);
|
|
|
|
SV(_state.FiqSpsr.Mode);
|
|
SV(_state.FiqSpsr.Thumb);
|
|
SV(_state.FiqSpsr.FiqDisable);
|
|
SV(_state.FiqSpsr.IrqDisable);
|
|
SV(_state.FiqSpsr.Overflow);
|
|
SV(_state.FiqSpsr.Carry);
|
|
SV(_state.FiqSpsr.Zero);
|
|
SV(_state.FiqSpsr.Negative);
|
|
|
|
SV(_state.IrqSpsr.Mode);
|
|
SV(_state.IrqSpsr.Thumb);
|
|
SV(_state.IrqSpsr.FiqDisable);
|
|
SV(_state.IrqSpsr.IrqDisable);
|
|
SV(_state.IrqSpsr.Overflow);
|
|
SV(_state.IrqSpsr.Carry);
|
|
SV(_state.IrqSpsr.Zero);
|
|
SV(_state.IrqSpsr.Negative);
|
|
|
|
SV(_state.SupervisorSpsr.Mode);
|
|
SV(_state.SupervisorSpsr.Thumb);
|
|
SV(_state.SupervisorSpsr.FiqDisable);
|
|
SV(_state.SupervisorSpsr.IrqDisable);
|
|
SV(_state.SupervisorSpsr.Overflow);
|
|
SV(_state.SupervisorSpsr.Carry);
|
|
SV(_state.SupervisorSpsr.Zero);
|
|
SV(_state.SupervisorSpsr.Negative);
|
|
|
|
SV(_state.AbortSpsr.Mode);
|
|
SV(_state.AbortSpsr.Thumb);
|
|
SV(_state.AbortSpsr.FiqDisable);
|
|
SV(_state.AbortSpsr.IrqDisable);
|
|
SV(_state.AbortSpsr.Overflow);
|
|
SV(_state.AbortSpsr.Carry);
|
|
SV(_state.AbortSpsr.Zero);
|
|
SV(_state.AbortSpsr.Negative);
|
|
|
|
SV(_state.UndefinedSpsr.Mode);
|
|
SV(_state.UndefinedSpsr.Thumb);
|
|
SV(_state.UndefinedSpsr.FiqDisable);
|
|
SV(_state.UndefinedSpsr.IrqDisable);
|
|
SV(_state.UndefinedSpsr.Overflow);
|
|
SV(_state.UndefinedSpsr.Carry);
|
|
SV(_state.UndefinedSpsr.Zero);
|
|
SV(_state.UndefinedSpsr.Negative);
|
|
|
|
SV(_state.CycleCount);
|
|
}
|