2008-11-03 00:36:40 +00:00
|
|
|
#include <assert.h>
|
2019-08-17 13:51:31 -04:00
|
|
|
#include <cstring>
|
2008-11-03 00:36:40 +00:00
|
|
|
#include "Iop_RootCounters.h"
|
|
|
|
#include "Iop_Intc.h"
|
2023-05-16 09:23:46 -04:00
|
|
|
#include "Ps2Const.h"
|
2014-06-28 23:49:51 -04:00
|
|
|
#include "string_format.h"
|
2025-03-11 12:48:26 -04:00
|
|
|
#include "Log.h"
|
2019-02-06 13:34:51 -05:00
|
|
|
#include "../states/RegisterStateFile.h"
|
2008-11-03 00:36:40 +00:00
|
|
|
|
|
|
|
#define LOG_NAME ("iop_counters")
|
|
|
|
|
2018-04-30 21:01:23 +01:00
|
|
|
#define STATE_REGS_XML ("iop_counters/regs.xml")
|
2014-06-28 23:49:51 -04:00
|
|
|
|
2008-11-03 00:36:40 +00:00
|
|
|
using namespace Iop;
|
|
|
|
|
2020-09-18 13:05:24 -04:00
|
|
|
// clang-format off
|
2012-05-29 01:01:02 +00:00
|
|
|
const uint32 CRootCounters::g_counterInterruptLines[MAX_COUNTERS] =
|
2020-09-18 13:05:24 -04:00
|
|
|
{
|
|
|
|
CIntc::LINE_RTC0,
|
|
|
|
CIntc::LINE_RTC1,
|
|
|
|
CIntc::LINE_RTC2,
|
|
|
|
CIntc::LINE_RTC3,
|
|
|
|
CIntc::LINE_RTC4,
|
|
|
|
CIntc::LINE_RTC5
|
|
|
|
};
|
2012-05-29 01:01:02 +00:00
|
|
|
|
|
|
|
const uint32 CRootCounters::g_counterBaseAddresses[MAX_COUNTERS] =
|
2020-09-18 13:05:24 -04:00
|
|
|
{
|
|
|
|
CNT0_BASE,
|
|
|
|
CNT1_BASE,
|
|
|
|
CNT2_BASE,
|
|
|
|
CNT3_BASE,
|
|
|
|
CNT4_BASE,
|
|
|
|
CNT5_BASE
|
|
|
|
};
|
2012-05-29 01:01:02 +00:00
|
|
|
|
|
|
|
const uint32 CRootCounters::g_counterSources[MAX_COUNTERS] =
|
2020-09-18 13:05:24 -04:00
|
|
|
{
|
|
|
|
COUNTER_SOURCE_SYSCLOCK | COUNTER_SOURCE_PIXEL | COUNTER_SOURCE_HOLD,
|
|
|
|
COUNTER_SOURCE_SYSCLOCK | COUNTER_SOURCE_HLINE | COUNTER_SOURCE_HOLD,
|
|
|
|
COUNTER_SOURCE_SYSCLOCK,
|
2020-09-18 13:08:10 -04:00
|
|
|
COUNTER_SOURCE_SYSCLOCK | COUNTER_SOURCE_HLINE,
|
2020-09-18 13:05:24 -04:00
|
|
|
COUNTER_SOURCE_SYSCLOCK,
|
|
|
|
COUNTER_SOURCE_SYSCLOCK
|
|
|
|
};
|
2012-05-29 01:01:02 +00:00
|
|
|
|
|
|
|
const uint32 CRootCounters::g_counterSizes[MAX_COUNTERS] =
|
2020-09-18 13:05:24 -04:00
|
|
|
{
|
|
|
|
16,
|
|
|
|
16,
|
|
|
|
16,
|
|
|
|
32,
|
|
|
|
32,
|
|
|
|
32
|
|
|
|
};
|
2012-05-29 01:01:02 +00:00
|
|
|
|
2015-07-21 01:22:26 -04:00
|
|
|
const uint32 CRootCounters::g_counterMaxScales[MAX_COUNTERS] =
|
2020-09-18 13:05:24 -04:00
|
|
|
{
|
|
|
|
1,
|
|
|
|
1,
|
|
|
|
8,
|
|
|
|
1,
|
|
|
|
256,
|
|
|
|
256
|
|
|
|
};
|
|
|
|
// clang-format on
|
2015-07-21 01:22:26 -04:00
|
|
|
|
2012-05-29 01:01:02 +00:00
|
|
|
CRootCounters::CRootCounters(unsigned int clockFreq, Iop::CIntc& intc)
|
2023-05-16 09:23:46 -04:00
|
|
|
: m_hsyncClocks(clockFreq / PS2::GS_NTSC_HSYNC_FREQ)
|
|
|
|
, m_pixelClocks(clockFreq / PS2::GPU_DOT_CLOCK_FREQ)
|
2018-04-30 21:01:23 +01:00
|
|
|
, m_intc(intc)
|
2008-11-03 00:36:40 +00:00
|
|
|
{
|
|
|
|
Reset();
|
|
|
|
}
|
|
|
|
|
2012-05-29 01:01:02 +00:00
|
|
|
unsigned int CRootCounters::GetCounterIdByAddress(uint32 address)
|
|
|
|
{
|
|
|
|
return (address >= ADDR_BEGIN2) ? ((address - CNT3_BASE) / 0x10) + 3 : ((address - CNT0_BASE) / 0x10);
|
|
|
|
}
|
|
|
|
|
2008-11-03 00:36:40 +00:00
|
|
|
void CRootCounters::Reset()
|
|
|
|
{
|
|
|
|
memset(&m_counter, 0, sizeof(m_counter));
|
|
|
|
}
|
|
|
|
|
2014-06-28 23:49:51 -04:00
|
|
|
void CRootCounters::LoadState(Framework::CZipArchiveReader& archive)
|
|
|
|
{
|
|
|
|
CRegisterStateFile registerFile(*archive.BeginReadFile(STATE_REGS_XML));
|
|
|
|
for(unsigned int i = 0; i < MAX_COUNTERS; i++)
|
|
|
|
{
|
|
|
|
auto& counter = m_counter[i];
|
|
|
|
auto counterPrefix = string_format("COUNTER_%d_", i);
|
2018-04-30 21:01:23 +01:00
|
|
|
counter.count = registerFile.GetRegister32((counterPrefix + "COUNT").c_str());
|
|
|
|
counter.mode <<= registerFile.GetRegister32((counterPrefix + "MODE").c_str());
|
|
|
|
counter.target = registerFile.GetRegister32((counterPrefix + "TGT").c_str());
|
|
|
|
counter.clockRemain = registerFile.GetRegister32((counterPrefix + "REM").c_str());
|
2014-06-28 23:49:51 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CRootCounters::SaveState(Framework::CZipArchiveWriter& archive)
|
|
|
|
{
|
2023-07-23 17:22:18 -04:00
|
|
|
auto registerFile = std::make_unique<CRegisterStateFile>(STATE_REGS_XML);
|
2014-06-28 23:49:51 -04:00
|
|
|
for(unsigned int i = 0; i < MAX_COUNTERS; i++)
|
|
|
|
{
|
|
|
|
const auto& counter = m_counter[i];
|
|
|
|
auto counterPrefix = string_format("COUNTER_%d_", i);
|
|
|
|
registerFile->SetRegister32((counterPrefix + "COUNT").c_str(), counter.count);
|
|
|
|
registerFile->SetRegister32((counterPrefix + "MODE").c_str(), counter.mode);
|
|
|
|
registerFile->SetRegister32((counterPrefix + "TGT").c_str(), counter.target);
|
|
|
|
registerFile->SetRegister32((counterPrefix + "REM").c_str(), counter.clockRemain);
|
|
|
|
}
|
2023-07-23 17:22:18 -04:00
|
|
|
archive.InsertFile(std::move(registerFile));
|
2014-06-28 23:49:51 -04:00
|
|
|
}
|
|
|
|
|
2008-11-03 00:36:40 +00:00
|
|
|
void CRootCounters::Update(unsigned int ticks)
|
|
|
|
{
|
|
|
|
for(unsigned int i = 0; i < MAX_COUNTERS; i++)
|
|
|
|
{
|
2022-06-23 17:56:44 -04:00
|
|
|
auto& counter = m_counter[i];
|
2008-11-03 00:36:40 +00:00
|
|
|
if(i == 2 && counter.mode.en) continue;
|
|
|
|
//Compute count increment
|
2022-06-23 17:56:44 -04:00
|
|
|
uint32 clockRatio = 1;
|
2012-05-29 01:01:02 +00:00
|
|
|
if(i == 0 && counter.mode.clc)
|
|
|
|
{
|
|
|
|
clockRatio = m_pixelClocks;
|
|
|
|
}
|
2020-09-18 13:08:10 -04:00
|
|
|
if(((i == 1) || (i == 3)) && counter.mode.clc)
|
2012-05-29 01:01:02 +00:00
|
|
|
{
|
|
|
|
clockRatio = m_hsyncClocks;
|
|
|
|
}
|
2015-07-21 01:22:26 -04:00
|
|
|
if(i == 2 && (counter.mode.div != COUNTER_SCALE_1))
|
2012-05-29 01:01:02 +00:00
|
|
|
{
|
2015-07-21 01:22:26 -04:00
|
|
|
assert(counter.mode.div == COUNTER_SCALE_8);
|
2012-05-29 01:01:02 +00:00
|
|
|
clockRatio = 8;
|
|
|
|
}
|
2015-07-21 01:22:26 -04:00
|
|
|
if(
|
2018-04-30 21:01:23 +01:00
|
|
|
((i == 4) || (i == 5)) &&
|
|
|
|
(counter.mode.div != COUNTER_SCALE_1))
|
2015-07-21 01:22:26 -04:00
|
|
|
{
|
|
|
|
switch(counter.mode.div)
|
|
|
|
{
|
|
|
|
case COUNTER_SCALE_8:
|
|
|
|
clockRatio = 8;
|
|
|
|
break;
|
|
|
|
case COUNTER_SCALE_16:
|
|
|
|
clockRatio = 16;
|
|
|
|
break;
|
|
|
|
case COUNTER_SCALE_256:
|
|
|
|
clockRatio = 256;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2022-06-23 17:56:44 -04:00
|
|
|
uint32 totalTicks = counter.clockRemain + ticks;
|
|
|
|
uint64 countAdd = totalTicks / clockRatio;
|
2012-05-29 01:01:02 +00:00
|
|
|
counter.clockRemain = totalTicks % clockRatio;
|
2008-11-03 00:36:40 +00:00
|
|
|
//Update count
|
2022-06-23 17:56:44 -04:00
|
|
|
uint64 counterMax = 0;
|
2012-05-29 01:01:02 +00:00
|
|
|
if(g_counterSizes[i] == 16)
|
|
|
|
{
|
|
|
|
counterMax = counter.mode.tar ? static_cast<uint16>(counter.target) : 0xFFFF;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
counterMax = counter.mode.tar ? counter.target : 0xFFFFFFFF;
|
|
|
|
}
|
2022-06-23 17:56:44 -04:00
|
|
|
uint64 counterTemp = static_cast<uint64>(counter.count) + countAdd;
|
2008-11-03 00:36:40 +00:00
|
|
|
if(counterTemp >= counterMax)
|
|
|
|
{
|
|
|
|
counterTemp -= counterMax;
|
|
|
|
if(counter.mode.iq1 && counter.mode.iq2)
|
|
|
|
{
|
2012-05-29 01:01:02 +00:00
|
|
|
m_intc.AssertLine(g_counterInterruptLines[i]);
|
2008-11-03 00:36:40 +00:00
|
|
|
}
|
|
|
|
}
|
2012-05-29 01:01:02 +00:00
|
|
|
if(g_counterSizes[i] == 16)
|
|
|
|
{
|
|
|
|
counter.count = static_cast<uint16>(counterTemp);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2022-06-23 17:56:44 -04:00
|
|
|
counter.count = static_cast<uint32>(counterTemp);
|
2012-05-29 01:01:02 +00:00
|
|
|
}
|
2008-11-03 00:36:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32 CRootCounters::ReadRegister(uint32 address)
|
|
|
|
{
|
|
|
|
#ifdef _DEBUG
|
|
|
|
DisassembleRead(address);
|
|
|
|
#endif
|
2012-05-29 01:01:02 +00:00
|
|
|
unsigned int counterId = GetCounterIdByAddress(address);
|
2008-11-03 00:36:40 +00:00
|
|
|
unsigned int registerId = address & 0x0F;
|
|
|
|
assert(counterId < MAX_COUNTERS);
|
|
|
|
switch(registerId)
|
|
|
|
{
|
|
|
|
case CNT_COUNT:
|
|
|
|
return m_counter[counterId].count;
|
|
|
|
break;
|
|
|
|
case CNT_MODE:
|
|
|
|
return m_counter[counterId].mode;
|
|
|
|
break;
|
|
|
|
case CNT_TARGET:
|
|
|
|
return m_counter[counterId].target;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32 CRootCounters::WriteRegister(uint32 address, uint32 value)
|
|
|
|
{
|
|
|
|
#ifdef _DEBUG
|
|
|
|
DisassembleWrite(address, value);
|
|
|
|
#endif
|
2012-05-29 01:01:02 +00:00
|
|
|
unsigned int counterId = GetCounterIdByAddress(address);
|
2008-11-03 00:36:40 +00:00
|
|
|
unsigned int registerId = address & 0x0F;
|
|
|
|
assert(counterId < MAX_COUNTERS);
|
|
|
|
COUNTER& counter = m_counter[counterId];
|
|
|
|
switch(registerId)
|
|
|
|
{
|
|
|
|
case CNT_COUNT:
|
2012-05-29 01:01:02 +00:00
|
|
|
counter.count = value;
|
2008-11-03 00:36:40 +00:00
|
|
|
break;
|
|
|
|
case CNT_MODE:
|
|
|
|
counter.mode <<= value;
|
|
|
|
break;
|
|
|
|
case CNT_TARGET:
|
2012-05-29 01:01:02 +00:00
|
|
|
counter.target = value;
|
2008-11-03 00:36:40 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CRootCounters::DisassembleRead(uint32 address)
|
|
|
|
{
|
2012-05-29 01:01:02 +00:00
|
|
|
unsigned int counterId = GetCounterIdByAddress(address);
|
2008-11-03 00:36:40 +00:00
|
|
|
unsigned int registerId = address & 0x0F;
|
|
|
|
switch(registerId)
|
|
|
|
{
|
|
|
|
case CNT_COUNT:
|
2012-05-29 01:01:02 +00:00
|
|
|
CLog::GetInstance().Print(LOG_NAME, "CNT%d: = COUNT\r\n", counterId);
|
2008-11-03 00:36:40 +00:00
|
|
|
break;
|
|
|
|
case CNT_MODE:
|
2012-05-29 01:01:02 +00:00
|
|
|
CLog::GetInstance().Print(LOG_NAME, "CNT%d: = MODE\r\n", counterId);
|
2008-11-03 00:36:40 +00:00
|
|
|
break;
|
|
|
|
case CNT_TARGET:
|
2012-05-29 01:01:02 +00:00
|
|
|
CLog::GetInstance().Print(LOG_NAME, "CNT%d: = TARGET\r\n", counterId);
|
2008-11-03 00:36:40 +00:00
|
|
|
break;
|
|
|
|
default:
|
2017-05-29 06:01:32 +01:00
|
|
|
CLog::GetInstance().Print(LOG_NAME, "Reading an unknown register (0x%08X).\r\n", address);
|
2008-11-03 00:36:40 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CRootCounters::DisassembleWrite(uint32 address, uint32 value)
|
|
|
|
{
|
2012-05-29 01:01:02 +00:00
|
|
|
unsigned int counterId = GetCounterIdByAddress(address);
|
2008-11-03 00:36:40 +00:00
|
|
|
unsigned int registerId = address & 0x0F;
|
|
|
|
switch(registerId)
|
|
|
|
{
|
|
|
|
case CNT_COUNT:
|
2017-05-29 06:01:32 +01:00
|
|
|
CLog::GetInstance().Print(LOG_NAME, "CNT%d: COUNT = 0x%04X\r\n", counterId, value);
|
2008-11-03 00:36:40 +00:00
|
|
|
break;
|
|
|
|
case CNT_MODE:
|
2017-05-29 06:01:32 +01:00
|
|
|
CLog::GetInstance().Print(LOG_NAME, "CNT%d: MODE = 0x%08X\r\n", counterId, value);
|
2008-11-03 00:36:40 +00:00
|
|
|
break;
|
|
|
|
case CNT_TARGET:
|
2017-05-29 06:01:32 +01:00
|
|
|
CLog::GetInstance().Print(LOG_NAME, "CNT%d: TARGET = 0x%04X\r\n", counterId, value);
|
2008-11-03 00:36:40 +00:00
|
|
|
break;
|
|
|
|
default:
|
2017-05-29 06:01:32 +01:00
|
|
|
CLog::GetInstance().Print(LOG_NAME, "Writing to an unknown register (0x%08X, 0x%08X).\r\n", address, value);
|
2008-11-03 00:36:40 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|