Play-/Source/ee/IPU.cpp

2081 lines
46 KiB
C++
Raw Normal View History

2023-05-02 08:56:25 -04:00
#include "IPU.h"
2019-08-17 13:51:31 -04:00
#include <cassert>
#include <cstring>
#include <stdio.h>
#include <algorithm>
#include <exception>
#include <functional>
2023-05-02 08:56:25 -04:00
#include "maybe_unused.h"
#include "IPU_MacroblockAddressIncrementTable.h"
#include "IPU_MacroblockTypeITable.h"
#include "IPU_MacroblockTypePTable.h"
#include "IPU_MacroblockTypeBTable.h"
#include "IPU_MotionCodeTable.h"
#include "IPU_DmVectorTable.h"
#include "mpeg2/DcSizeLuminanceTable.h"
#include "mpeg2/DcSizeChrominanceTable.h"
#include "mpeg2/DctCoefficientTable0.h"
#include "mpeg2/DctCoefficientTable1.h"
#include "mpeg2/CodedBlockPatternTable.h"
#include "mpeg2/QuantiserScaleTable.h"
#include "mpeg2/InverseScanTable.h"
#include "idct/TrivialC.h"
#include "idct/IEEE1180.h"
2025-03-11 12:48:26 -04:00
#include "Log.h"
#include "DMAC.h"
2017-01-01 19:22:43 -05:00
#include "INTC.h"
#include "Ps2Const.h"
2023-12-08 13:10:20 -05:00
#include "../states/RegisterStateFile.h"
#include "../states/RegisterStateUtils.h"
#include "../states/MemoryStateFile.h"
2018-05-25 12:38:51 -04:00
#define LOG_NAME ("ee_ipu")
//#define _DECODE_LOGGING
#define DECODE_LOG_NAME ("ipu_decode")
2023-12-08 13:10:20 -05:00
#define STATE_REGS_XML ("ipu/regs.xml")
#define STATE_INFIFO_REGS_XML ("ipu/infifo.xml")
#define STATE_OUTFIFO ("ipu/outfifo")
#define STATE_INTRAIQ ("ipu/intraiq")
#define STATE_NONINTRAIQ ("ipu/nonintraiq")
#define STATE_VQCLUT ("ipu/vqclut")
#define STATE_REGS_CTRL ("CTRL")
#define STATE_REGS_CMD0 ("CMD0")
#define STATE_REGS_CMD1 ("CMD1")
#define STATE_REGS_TH0 ("TH0")
#define STATE_REGS_TH1 ("TH1")
#define STATE_REGS_CURRENTCMDID ("currentCmdId")
#define STATE_REGS_LASTCMDID ("lastCmdId")
#define STATE_REGS_ISBUSY ("isBusy")
#define STATE_REGS_DCPRED0 ("dcPredictor0")
#define STATE_REGS_DCPRED1 ("dcPredictor1")
#define STATE_REGS_DCPRED2 ("dcPredictor2")
#define STATE_INFIFO_REGS_SIZE ("size")
#define STATE_INFIFO_REGS_BITPOSITION ("bitPosition")
#define STATE_INFIFO_REGS_BUFFER_FORMAT ("Buffer%d")
using namespace IPU;
using namespace MPEG2;
// clang-format off
static const uint8 g_defaultIntraIQ[64] =
{
8, 16, 19, 22, 26, 27, 29, 34,
16, 16, 22, 24, 27, 29, 34, 37,
19, 22, 26, 27, 29, 34, 34, 38,
22, 22, 26, 27, 29, 34, 37, 40,
22, 26, 27, 29, 32, 35, 40, 48,
26, 27, 29, 32, 35, 40, 48, 58,
26, 27, 29, 34, 38, 46, 56, 69,
27, 29, 35, 38, 46, 56, 69, 83,
};
static const uint8 g_defaultNonIntraIQ[64] =
{
16, 17, 18, 19, 20, 21, 22, 23,
17, 18, 19, 20, 21, 22, 23, 24,
18, 19, 20, 21, 22, 23, 24, 25,
19, 20, 21, 22, 23, 24, 26, 27,
20, 21, 22, 23, 25, 26, 27, 28,
21, 22, 23, 24, 26, 27, 28, 30,
22, 23, 24, 26, 27, 28, 30, 31,
23, 24, 25, 27, 28, 30, 31, 33
};
// clang-format on
static CVLCTable::DECODE_STATUS FilterSymbolError(CVLCTable::DECODE_STATUS result)
{
switch(result)
{
case CVLCTable::DECODE_STATUS_SYMBOLNOTFOUND:
throw CVLCTable::CVLCTableException();
break;
default:
return result;
break;
}
}
2017-01-01 19:22:43 -05:00
CIPU::CIPU(CINTC& intc)
2018-04-30 21:01:23 +01:00
: m_intc(intc)
{
m_commands[IPU_CMD_BCLR] = &m_BCLRCommand;
m_commands[IPU_CMD_IDEC] = &m_IDECCommand;
m_commands[IPU_CMD_BDEC] = &m_BDECCommand;
m_commands[IPU_CMD_VDEC] = &m_VDECCommand;
m_commands[IPU_CMD_FDEC] = &m_FDECCommand;
m_commands[IPU_CMD_SETIQ] = &m_SETIQCommand;
m_commands[IPU_CMD_SETVQ] = &m_SETVQCommand;
m_commands[IPU_CMD_CSC] = &m_CSCCommand;
m_commands[IPU_CMD_PACK] = nullptr;
m_commands[IPU_CMD_SETTH] = &m_SETTHCommand;
}
void CIPU::Reset()
{
2018-04-30 21:01:23 +01:00
m_IPU_CTRL = 0;
m_IPU_CMD[0] = 0;
m_IPU_CMD[1] = 0;
m_nTH0 = 0;
m_nTH1 = 0;
m_currentCmdId = IPU_INVALID_CMDID;
m_lastCmdId = IPU_INVALID_CMDID;
m_nDcPredictor[0] = 0;
m_nDcPredictor[1] = 0;
m_nDcPredictor[2] = 0;
static_assert(sizeof(m_nIntraIQ) == sizeof(g_defaultIntraIQ));
memcpy(m_nIntraIQ, g_defaultIntraIQ, sizeof(g_defaultIntraIQ));
static_assert(sizeof(m_nNonIntraIQ) == sizeof(g_defaultNonIntraIQ));
memcpy(m_nNonIntraIQ, g_defaultNonIntraIQ, sizeof(g_defaultNonIntraIQ));
2019-11-21 07:33:19 -05:00
2018-04-30 21:01:23 +01:00
m_isBusy = false;
m_IN_FIFO.Reset();
m_OUT_FIFO.Reset();
}
uint32 CIPU::GetRegister(uint32 nAddress)
{
2025-02-12 14:12:37 -05:00
#if LOGGING_ENABLED
DisassembleGet(nAddress);
#endif
switch(nAddress)
{
case IPU_CMD + 0x0:
//Seems reading from CMD is always defined (Quake 3 Arena relies on this)
if((m_lastCmdId != IPU_CMD_VDEC) && (m_lastCmdId != IPU_CMD_FDEC))
{
unsigned int availableSize = std::min<unsigned int>(32, m_IN_FIFO.GetAvailableBits());
//If no bits are available, return zero immediately, shift below won't have any effect
if(availableSize == 0) return 0;
uint32 result = m_IN_FIFO.PeekBits_MSBF(availableSize);
result <<= (32 - availableSize);
return result;
}
else
{
return m_IPU_CMD[0];
}
break;
case IPU_CMD + 0x4:
return GetBusyBit(m_isBusy);
break;
case IPU_CMD + 0x8:
case IPU_CMD + 0xC:
break;
case IPU_CTRL + 0x0:
2018-04-30 21:01:23 +01:00
{
auto fifoState = GetFifoState();
return m_IPU_CTRL | GetBusyBit(m_isBusy) | fifoState.ifc;
}
break;
case IPU_CTRL + 0x4:
case IPU_CTRL + 0x8:
case IPU_CTRL + 0xC:
break;
case IPU_BP + 0x0:
2018-04-30 21:01:23 +01:00
{
auto fifoState = GetFifoState();
return (fifoState.fp << 16) | (fifoState.ifc << 8) | fifoState.bp;
}
break;
case IPU_BP + 0x4:
case IPU_BP + 0x8:
case IPU_BP + 0xC:
break;
case IPU_TOP + 0x0:
if(!m_isBusy)
{
unsigned int availableSize = std::min<unsigned int>(32, m_IN_FIFO.GetAvailableBits());
//If no bits are available, return zero immediately, shift below won't have any effect
if(availableSize == 0) return 0;
uint32 result = m_IN_FIFO.PeekBits_MSBF(availableSize);
result <<= (32 - availableSize);
return result;
}
else
{
return 0;
}
break;
case IPU_TOP + 0x4:
2018-04-30 21:01:23 +01:00
{
//Not quite sure about this... are we really busy if there's no data in the FIFO?
//This was needed to fix Timesplitters
unsigned int availableSize = std::min<unsigned int>(32, m_IN_FIFO.GetAvailableBits());
return GetBusyBit(m_isBusy) | GetBusyBit(availableSize != 32);
}
break;
case IPU_TOP + 0x8:
case IPU_TOP + 0xC:
break;
default:
2020-07-13 16:56:44 +02:00
CLog::GetInstance().Warn(LOG_NAME, "Reading an unhandled register (0x%08X).\r\n", nAddress);
break;
}
return 0;
}
void CIPU::SetRegister(uint32 nAddress, uint32 nValue)
{
2025-02-12 14:12:37 -05:00
#if LOGGING_ENABLED
DisassembleSet(nAddress, nValue);
#endif
switch(nAddress)
{
case IPU_CMD + 0x0:
//Set BUSY states
{
assert(m_isBusy == false);
if(m_currentCmdId != IPU_INVALID_CMDID)
{
assert(m_IPU_CTRL & IPU_CTRL_ECD);
}
m_IPU_CTRL &= ~IPU_CTRL_ECD;
m_IPU_CTRL &= ~IPU_CTRL_SCD;
unsigned int nCmd = (nValue >> 28);
m_currentCmdId = m_lastCmdId = nCmd;
InitializeCommand(nValue);
m_isBusy = true;
}
2025-02-12 14:12:37 -05:00
#if LOGGING_ENABLED
DisassembleCommand(nValue);
#endif
break;
case IPU_CMD + 0x4:
case IPU_CMD + 0x8:
case IPU_CMD + 0xC:
break;
case IPU_CTRL + 0x0:
if(nValue & IPU_CTRL_RST)
{
m_isBusy = false;
m_currentCmdId = IPU_INVALID_CMDID;
m_lastCmdId = IPU_INVALID_CMDID;
m_nTH0 = 0;
m_nTH1 = 0;
m_IN_FIFO.Reset();
m_OUT_FIFO.Reset();
}
nValue &= 0x3FFF0000;
m_IPU_CTRL &= ~0x3FFF0000;
m_IPU_CTRL |= nValue;
break;
case IPU_CTRL + 0x4:
case IPU_CTRL + 0x8:
case IPU_CTRL + 0xC:
break;
case IPU_IN_FIFO + 0x0:
case IPU_IN_FIFO + 0x4:
case IPU_IN_FIFO + 0x8:
case IPU_IN_FIFO + 0xC:
m_IN_FIFO.Write(&nValue, 4);
break;
default:
2020-07-13 16:56:44 +02:00
CLog::GetInstance().Warn(LOG_NAME, "Writing 0x%08X to an unhandled register (0x%08X).\r\n", nValue, nAddress);
break;
}
}
2023-12-08 13:10:20 -05:00
void CIPU::SaveState(Framework::CZipArchiveWriter& archive)
{
{
auto registerFile = std::make_unique<CRegisterStateFile>(STATE_REGS_XML);
registerFile->SetRegister32(STATE_REGS_CTRL, m_IPU_CTRL);
registerFile->SetRegister32(STATE_REGS_CMD0, m_IPU_CMD[0]);
registerFile->SetRegister32(STATE_REGS_CMD1, m_IPU_CMD[1]);
registerFile->SetRegister32(STATE_REGS_TH0, m_nTH0);
registerFile->SetRegister32(STATE_REGS_TH1, m_nTH1);
registerFile->SetRegister32(STATE_REGS_CURRENTCMDID, m_currentCmdId);
registerFile->SetRegister32(STATE_REGS_LASTCMDID, m_lastCmdId);
registerFile->SetRegister32(STATE_REGS_ISBUSY, m_isBusy ? 1 : 0);
registerFile->SetRegister32(STATE_REGS_DCPRED0, m_nDcPredictor[0]);
registerFile->SetRegister32(STATE_REGS_DCPRED1, m_nDcPredictor[1]);
registerFile->SetRegister32(STATE_REGS_DCPRED2, m_nDcPredictor[2]);
archive.InsertFile(std::move(registerFile));
}
m_IN_FIFO.SaveState(STATE_INFIFO_REGS_XML, archive);
archive.InsertFile(std::make_unique<CMemoryStateFile>(STATE_INTRAIQ, m_nIntraIQ, sizeof(m_nIntraIQ)));
archive.InsertFile(std::make_unique<CMemoryStateFile>(STATE_NONINTRAIQ, m_nNonIntraIQ, sizeof(m_nNonIntraIQ)));
archive.InsertFile(std::make_unique<CMemoryStateFile>(STATE_VQCLUT, m_nVQCLUT, sizeof(m_nVQCLUT)));
assert(m_currentCmdId == IPU_INVALID_CMDID);
}
void CIPU::LoadState(Framework::CZipArchiveReader& archive)
{
{
auto registerFile = CRegisterStateFile(*archive.BeginReadFile(STATE_REGS_XML));
m_IPU_CTRL = registerFile.GetRegister32(STATE_REGS_CTRL);
m_IPU_CMD[0] = registerFile.GetRegister32(STATE_REGS_CMD0);
m_IPU_CMD[1] = registerFile.GetRegister32(STATE_REGS_CMD1);
m_nTH0 = registerFile.GetRegister32(STATE_REGS_TH0);
m_nTH1 = registerFile.GetRegister32(STATE_REGS_TH1);
m_currentCmdId = registerFile.GetRegister32(STATE_REGS_CURRENTCMDID);
m_lastCmdId = registerFile.GetRegister32(STATE_REGS_LASTCMDID);
m_isBusy = registerFile.GetRegister32(STATE_REGS_ISBUSY) != 0;
m_nDcPredictor[0] = registerFile.GetRegister32(STATE_REGS_DCPRED0);
m_nDcPredictor[1] = registerFile.GetRegister32(STATE_REGS_DCPRED1);
m_nDcPredictor[2] = registerFile.GetRegister32(STATE_REGS_DCPRED2);
}
m_IN_FIFO.LoadState(STATE_INFIFO_REGS_XML, archive);
archive.BeginReadFile(STATE_INTRAIQ)->Read(m_nIntraIQ, sizeof(m_nIntraIQ));
archive.BeginReadFile(STATE_NONINTRAIQ)->Read(m_nNonIntraIQ, sizeof(m_nNonIntraIQ));
archive.BeginReadFile(STATE_VQCLUT)->Read(m_nVQCLUT, sizeof(m_nVQCLUT));
assert(m_currentCmdId == IPU_INVALID_CMDID);
}
void CIPU::CountTicks(uint32 ticks)
{
if(m_currentCmdId != IPU_INVALID_CMDID)
{
m_commands[m_currentCmdId]->CountTicks(ticks);
}
}
bool CIPU::IsCommandDelayed() const
{
if(m_currentCmdId != IPU_INVALID_CMDID)
{
return m_commands[m_currentCmdId]->IsDelayed();
}
return false;
}
void CIPU::ExecuteCommand()
{
assert(WillExecuteCommand());
try
{
assert(m_currentCmdId != IPU_INVALID_CMDID);
bool result = m_commands[m_currentCmdId]->Execute();
if(!result)
{
return;
}
m_currentCmdId = IPU_INVALID_CMDID;
//Clear BUSY states
m_isBusy = false;
2017-01-01 19:22:43 -05:00
m_intc.AssertLine(CINTC::INTC_LINE_IPU);
}
catch(const Framework::CBitStream::CBitStreamException&)
{
}
catch(const CStartCodeException&)
{
m_currentCmdId = IPU_INVALID_CMDID;
m_isBusy = false;
m_IPU_CTRL |= IPU_CTRL_SCD;
CLog::GetInstance().Print(LOG_NAME, "Start code encountered.\r\n");
}
catch(const CVLCTable::CVLCTableException&)
{
m_currentCmdId = IPU_INVALID_CMDID;
m_isBusy = false;
m_IPU_CTRL |= IPU_CTRL_ECD;
2022-03-16 14:42:22 -04:00
CLog::GetInstance().Warn(LOG_NAME, "VLC error encountered.\r\n");
}
}
bool CIPU::WillExecuteCommand() const
{
return m_isBusy && ((m_IPU_CTRL & IPU_CTRL_ECD) == 0);
}
bool CIPU::HasPendingOUTFIFOData() const
{
return m_OUT_FIFO.GetSize() != 0;
}
2019-10-28 08:05:46 -04:00
void CIPU::FlushOUTFIFOData()
{
m_OUT_FIFO.Flush();
}
void CIPU::InitializeCommand(uint32 value)
{
unsigned int cmd = (value >> 28);
switch(cmd)
{
case IPU_CMD_BCLR:
2018-04-30 21:01:23 +01:00
m_BCLRCommand.Initialize(&m_IN_FIFO, value);
break;
case IPU_CMD_IDEC:
2018-04-30 21:01:23 +01:00
m_IDECCommand.Initialize(&m_BDECCommand, &m_CSCCommand, &m_IN_FIFO, &m_OUT_FIFO, value, GetDecoderContext(), m_nTH0, m_nTH1);
break;
case IPU_CMD_BDEC:
2018-04-30 21:01:23 +01:00
m_BDECCommand.Initialize(&m_IN_FIFO, &m_OUT_FIFO, value, true, GetDecoderContext());
break;
case IPU_CMD_VDEC:
2018-04-30 21:01:23 +01:00
m_VDECCommand.Initialize(&m_IN_FIFO, value, GetPictureType(), &m_IPU_CMD[0]);
break;
case IPU_CMD_FDEC:
2018-04-30 21:01:23 +01:00
m_FDECCommand.Initialize(&m_IN_FIFO, value, &m_IPU_CMD[0]);
break;
case IPU_CMD_SETIQ:
2018-04-30 21:01:23 +01:00
{
uint8* matrix = (value & 0x08000000) ? m_nNonIntraIQ : m_nIntraIQ;
m_SETIQCommand.Initialize(&m_IN_FIFO, matrix);
}
break;
case IPU_CMD_SETVQ:
2018-04-30 21:01:23 +01:00
m_SETVQCommand.Initialize(&m_IN_FIFO, m_nVQCLUT);
break;
case IPU_CMD_CSC:
2018-04-30 21:01:23 +01:00
m_CSCCommand.Initialize(&m_IN_FIFO, &m_OUT_FIFO, value, m_nTH0, m_nTH1);
break;
case IPU_CMD_SETTH:
2018-04-30 21:01:23 +01:00
m_SETTHCommand.Initialize(value, &m_nTH0, &m_nTH1);
break;
default:
assert(0);
CLog::GetInstance().Warn(LOG_NAME, "Unhandled command execution requested (%d).\r\n", value >> 28);
break;
}
}
void CIPU::SetDMA3ReceiveHandler(const Dma3ReceiveHandler& receiveHandler)
{
m_OUT_FIFO.SetReceiveHandler(receiveHandler);
}
uint32 CIPU::ReceiveDMA4(uint32 address, uint32 nQWC, bool nTagIncluded, uint8* ram, uint8* spr)
{
assert(nTagIncluded == false);
uint32 availableFifoSize = CINFIFO::BUFFERSIZE - m_IN_FIFO.GetSize();
uint32 size = std::min<uint32>(nQWC * 0x10, availableFifoSize);
assert((size & 0xF) == 0);
uint8* memory = nullptr;
if(address & 0x80000000)
{
memory = spr;
address &= PS2::EE_SPR_SIZE - 1;
assert((address + size) <= PS2::EE_SPR_SIZE);
}
else
{
memory = ram;
}
if(size != 0)
{
m_IN_FIFO.Write(memory + address, size);
}
return size / 0x10;
}
CIPU::DECODER_CONTEXT CIPU::GetDecoderContext()
{
DECODER_CONTEXT context;
2018-04-30 21:01:23 +01:00
context.isMpeg1CoeffVLCTable = GetIsMPEG1CoeffVLCTable();
context.isMpeg2 = GetIsMPEG2();
context.isZigZag = GetIsZigZagScan();
context.isLinearQScale = GetIsLinearQScale();
context.dcPrecision = GetDcPrecision();
context.intraIq = m_nIntraIQ;
context.nonIntraIq = m_nNonIntraIQ;
context.dcPredictor = m_nDcPredictor;
return context;
}
uint32 CIPU::GetPictureType()
{
return (m_IPU_CTRL >> 24) & 0x7;
}
uint32 CIPU::GetDcPrecision()
{
return (m_IPU_CTRL >> 16) & 0x3;
}
bool CIPU::GetIsMPEG2()
{
return (m_IPU_CTRL & 0x00800000) == 0;
}
bool CIPU::GetIsLinearQScale()
{
return (m_IPU_CTRL & 0x00400000) == 0;
}
bool CIPU::GetIsMPEG1CoeffVLCTable()
{
return (m_IPU_CTRL & 0x00200000) == 0;
}
bool CIPU::GetIsZigZagScan()
{
return (m_IPU_CTRL & 0x00100000) == 0;
}
void CIPU::DequantiseBlock(int16* pBlock, uint8 nMBI, uint8 nQSC, bool isLinearQScale, uint32 dcPrecision, uint8* intraIq, uint8* nonIntraIq)
{
int16 nQuantScale;
if(isLinearQScale)
{
nQuantScale = (int16)CQuantiserScaleTable::m_nTable0[nQSC];
}
else
{
nQuantScale = (int16)CQuantiserScaleTable::m_nTable1[nQSC];
}
if(nMBI == 1)
{
int16 nIntraDcMult = 0;
switch(dcPrecision)
{
case 0:
nIntraDcMult = 8;
break;
case 1:
nIntraDcMult = 4;
break;
case 2:
nIntraDcMult = 2;
break;
}
pBlock[0] = nIntraDcMult * pBlock[0];
2018-04-30 21:01:23 +01:00
for(unsigned int i = 1; i < 64; i++)
{
int16 nSign = 0;
if(pBlock[i] == 0)
{
nSign = 0;
}
else
{
nSign = (pBlock[i] > 0) ? 0x0001 : 0xFFFF;
}
pBlock[i] = (pBlock[i] * static_cast<int16>(intraIq[i]) * nQuantScale * 2) / 32;
if(nSign != 0)
{
if((pBlock[i] & 1) == 0)
{
pBlock[i] = (pBlock[i] - nSign) | 1;
}
}
}
}
else
{
for(unsigned int i = 0; i < 64; i++)
{
int16 nSign = 0;
if(pBlock[i] == 0)
{
nSign = 0;
}
else
{
nSign = (pBlock[i] > 0) ? 0x0001 : 0xFFFF;
}
pBlock[i] = (((pBlock[i] * 2) + nSign) * static_cast<int16>(nonIntraIq[i]) * nQuantScale) / 32;
if(nSign != 0)
{
if((pBlock[i] & 1) == 0)
{
pBlock[i] = (pBlock[i] - nSign) | 1;
}
}
}
}
//Saturate
int16 nSum = 0;
for(unsigned int i = 0; i < 64; i++)
{
if(pBlock[i] > 2047)
{
pBlock[i] = 2047;
}
if(pBlock[i] < -2048)
{
pBlock[i] = -2048;
}
nSum += pBlock[i];
}
if(nSum & 1)
{
}
}
void CIPU::InverseScan(int16* pBlock, bool isZigZag)
{
int16 nTemp[0x40];
memcpy(nTemp, pBlock, sizeof(int16) * 0x40);
unsigned int* pTable = isZigZag ? CInverseScanTable::m_nTable0 : CInverseScanTable::m_nTable1;
for(unsigned int i = 0; i < 64; i++)
{
pBlock[i] = nTemp[pTable[i]];
}
}
uint32 CIPU::GetBusyBit(bool condition) const
{
return condition ? 0x80000000 : 0x00000000;
}
CIPU::FIFO_STATE CIPU::GetFifoState() const
{
uint32 bp = m_IN_FIFO.GetBitIndex();
uint32 ifc = m_IN_FIFO.GetSize() / 0x10;
uint32 fp = 0;
if((bp != 0) && (ifc != 0))
{
fp++;
ifc--;
}
FIFO_STATE state;
state.bp = bp;
state.ifc = ifc;
state.fp = fp;
return state;
}
void CIPU::DisassembleGet(uint32 nAddress)
{
switch(nAddress)
{
case IPU_CMD:
CLog::GetInstance().Print(LOG_NAME, "IPU_CMD\r\n");
break;
case IPU_CTRL:
CLog::GetInstance().Print(LOG_NAME, "IPU_CTRL\r\n");
break;
case IPU_BP:
CLog::GetInstance().Print(LOG_NAME, "IPU_BP\r\n");
break;
case IPU_TOP:
CLog::GetInstance().Print(LOG_NAME, "IPU_TOP\r\n");
break;
}
}
void CIPU::DisassembleSet(uint32 nAddress, uint32 nValue)
{
switch(nAddress)
{
case IPU_CMD + 0x0:
CLog::GetInstance().Print(LOG_NAME, "IPU_CMD = 0x%08X\r\n", nValue);
break;
case IPU_CMD + 0x4:
case IPU_CMD + 0x8:
case IPU_CMD + 0xC:
break;
case IPU_CTRL + 0x0:
CLog::GetInstance().Print(LOG_NAME, "IPU_CTRL = 0x%08X\r\n", nValue);
break;
case IPU_CTRL + 0x4:
case IPU_CTRL + 0x8:
case IPU_CTRL + 0xC:
break;
case IPU_IN_FIFO + 0x0:
case IPU_IN_FIFO + 0x4:
case IPU_IN_FIFO + 0x8:
case IPU_IN_FIFO + 0xC:
CLog::GetInstance().Print(LOG_NAME, "IPU_IN_FIFO = 0x%08X\r\n", nValue);
break;
}
}
void CIPU::DisassembleCommand(uint32 nValue)
{
switch(nValue >> 28)
{
case 0:
CLog::GetInstance().Print(LOG_NAME, "BCLR(bp = %i);\r\n", nValue & 0x7F);
break;
case 2:
2018-04-30 21:01:23 +01:00
CLog::GetInstance().Print(LOG_NAME, "BDEC(mbi = %i, dcr = %i, dt = %i, qsc = %i, fb = %i);\r\n",
(nValue >> 27) & 1,
(nValue >> 26) & 1,
(nValue >> 25) & 1,
(nValue >> 16) & 0x1F,
nValue & 0x3F);
break;
case 3:
2018-04-30 21:01:23 +01:00
{
uint32 tbl = (nValue >> 26) & 0x3;
const char* tblName = NULL;
switch(tbl)
{
2018-04-30 21:01:23 +01:00
case 0:
//Macroblock Address Increment
tblName = "MBI";
break;
case 1:
//Macroblock Type
switch(GetPictureType())
{
case 1:
2018-04-30 21:01:23 +01:00
//I Picture
tblName = "MB Type (I)";
break;
case 2:
2018-04-30 21:01:23 +01:00
//P Picture
tblName = "MB Type (P)";
break;
case 3:
2018-04-30 21:01:23 +01:00
//B Picture
tblName = "MB Type (B)";
break;
default:
assert(0);
return;
break;
}
2018-04-30 21:01:23 +01:00
break;
case 2:
tblName = "Motion Type";
break;
case 3:
tblName = "DM Vector";
break;
}
2018-04-30 21:01:23 +01:00
CLog::GetInstance().Print(LOG_NAME, "VDEC(tbl = %i (%s), bp = %i);\r\n", tbl, tblName, nValue & 0x3F);
}
break;
case 4:
CLog::GetInstance().Print(LOG_NAME, "FDEC(bp = %i);\r\n", nValue & 0x3F);
break;
case 5:
CLog::GetInstance().Print(LOG_NAME, "SETIQ(iqm = %i, bp = %i);\r\n", (nValue & 0x08000000) != 0 ? 1 : 0, nValue & 0x7F);
break;
case 6:
CLog::GetInstance().Print(LOG_NAME, "SETVQ();\r\n");
break;
case 7:
2018-04-30 21:01:23 +01:00
CLog::GetInstance().Print(LOG_NAME, "CSC(ofm = %i, dte = %i, mbc = %i);\r\n",
(nValue >> 27) & 1,
(nValue >> 26) & 1,
(nValue >> 0) & 0x7FF);
break;
case 9:
CLog::GetInstance().Print(LOG_NAME, "SETTH(th0 = 0x%04X, th1 = 0x%04X);\r\n", nValue & 0x1FF, (nValue >> 16) & 0x1FF);
break;
}
}
/////////////////////////////////////////////
//OUT FIFO class implementation
/////////////////////////////////////////////
CIPU::COUTFIFO::~COUTFIFO()
{
free(m_buffer);
}
void CIPU::COUTFIFO::SetReceiveHandler(const Dma3ReceiveHandler& handler)
{
m_receiveHandler = handler;
}
uint32 CIPU::COUTFIFO::GetSize() const
{
return m_size;
}
void CIPU::COUTFIFO::Write(const void* data, unsigned int size)
{
RequestGrow(size);
memcpy(m_buffer + m_size, data, size);
m_size += size;
}
void CIPU::COUTFIFO::Flush()
{
//Write to memory through DMA channel 3
assert((m_size & 0x0F) == 0);
uint32 copied = m_receiveHandler(m_buffer, m_size / 0x10);
copied *= 0x10;
if(copied == 0) return;
memmove(m_buffer, m_buffer + copied, m_size - copied);
m_size -= copied;
}
void CIPU::COUTFIFO::Reset()
{
m_size = 0;
}
void CIPU::COUTFIFO::RequestGrow(unsigned int size)
{
while(m_alloc <= (size + m_size))
{
m_alloc += GROWSIZE;
m_buffer = reinterpret_cast<uint8*>(realloc(m_buffer, m_alloc));
}
}
/////////////////////////////////////////////
//IN FIFO class implementation
/////////////////////////////////////////////
CIPU::CINFIFO::CINFIFO()
2018-04-30 21:01:23 +01:00
: m_size(0)
, m_bitPosition(0)
{
}
void CIPU::CINFIFO::Write(void* data, unsigned int size)
{
2018-04-30 21:01:23 +01:00
if((size + m_size) > BUFFERSIZE)
{
assert(0);
return;
}
memcpy(m_buffer + m_size, data, size);
m_size += size;
m_lookupBitsDirty = true;
}
bool CIPU::CINFIFO::TryPeekBits_LSBF(uint8 nBits, uint32& result)
{
//Shouldn't be used
return false;
}
bool CIPU::CINFIFO::TryPeekBits_MSBF(uint8 size, uint32& result)
{
assert(size != 0);
assert(size <= 32);
int bitsAvailable = (m_size * 8) - m_bitPosition;
int bitsNeeded = size;
assert(bitsAvailable >= 0);
if(bitsAvailable < bitsNeeded)
{
return false;
}
if(m_lookupBitsDirty)
{
SyncLookupBits();
m_lookupBitsDirty = false;
}
uint8 shift = 64 - (m_bitPosition % 32) - size;
uint64 mask = ~0ULL >> (64 - size);
result = static_cast<uint32>((m_lookupBits >> shift) & mask);
return true;
}
void CIPU::CINFIFO::Advance(uint8 bits)
{
if(bits == 0) return;
if((m_bitPosition + bits) > (m_size * 8))
{
throw CBitStreamException();
}
uint32 wordsBefore = m_bitPosition / 32;
uint32 wordsAfter = (m_bitPosition + bits) / 32;
m_lookupBitsDirty |= (wordsBefore != wordsAfter);
m_bitPosition += bits;
while(m_bitPosition >= 128)
{
if(m_size == 0)
{
assert(0);
}
if((m_size == 0) && (m_bitPosition != 0))
{
//Humm, this seems to happen when the DMA4 has done the transfer
//but we need more data...
assert(0);
}
//Discard the read bytes
memmove(m_buffer, m_buffer + 16, m_size - 16);
m_size -= 16;
m_bitPosition -= 128;
m_lookupBitsDirty = true;
}
}
uint8 CIPU::CINFIFO::GetBitIndex() const
{
return m_bitPosition;
}
void CIPU::CINFIFO::SetBitPosition(unsigned int position)
{
m_bitPosition = position;
}
2017-01-01 22:06:15 -05:00
unsigned int CIPU::CINFIFO::GetSize() const
{
return m_size;
}
2017-01-01 22:06:15 -05:00
unsigned int CIPU::CINFIFO::GetAvailableBits() const
{
return std::max<int32>((m_size * 8) - m_bitPosition, 0);
}
void CIPU::CINFIFO::Reset()
{
m_bitPosition = 0;
m_size = 0;
m_lookupBits = 0;
m_lookupBitsDirty = false;
}
2023-12-08 13:10:20 -05:00
void CIPU::CINFIFO::SaveState(const char* regsFileName, Framework::CZipArchiveWriter& archive)
{
auto registerFile = std::make_unique<CRegisterStateFile>(regsFileName);
registerFile->SetRegister32(STATE_INFIFO_REGS_SIZE, m_size);
registerFile->SetRegister32(STATE_INFIFO_REGS_BITPOSITION, m_bitPosition);
RegisterStateUtils::WriteArray(*registerFile.get(), m_buffer, STATE_INFIFO_REGS_BUFFER_FORMAT);
archive.InsertFile(std::move(registerFile));
}
void CIPU::CINFIFO::LoadState(const char* regsFileName, Framework::CZipArchiveReader& archive)
{
auto registerFile = CRegisterStateFile(*archive.BeginReadFile(regsFileName));
m_size = registerFile.GetRegister32(STATE_INFIFO_REGS_SIZE);
m_bitPosition = registerFile.GetRegister32(STATE_INFIFO_REGS_BITPOSITION);
RegisterStateUtils::ReadArray(registerFile, m_buffer, STATE_INFIFO_REGS_BUFFER_FORMAT);
m_lookupBitsDirty = true;
2023-12-08 13:10:20 -05:00
}
void CIPU::CINFIFO::SyncLookupBits()
{
unsigned int lookupPosition = (m_bitPosition & ~0x1F) / 8;
uint8 lookupBytes[8];
for(unsigned int i = 0; i < 8; i++)
{
lookupBytes[7 - i] = m_buffer[lookupPosition + i];
}
m_lookupBits = *reinterpret_cast<uint64*>(lookupBytes);
}
/////////////////////////////////////////////
//BCLR command implementation
/////////////////////////////////////////////
CIPU::CBCLRCommand::CBCLRCommand()
2018-04-30 21:01:23 +01:00
: m_IN_FIFO(NULL)
, m_commandCode(0)
{
}
void CIPU::CBCLRCommand::Initialize(CINFIFO* fifo, uint32 commandCode)
{
m_IN_FIFO = fifo;
m_commandCode = commandCode;
}
bool CIPU::CBCLRCommand::Execute()
{
m_IN_FIFO->Reset();
m_IN_FIFO->SetBitPosition(m_commandCode & 0x7F);
return true;
}
/////////////////////////////////////////////
//IDEC command implementation
/////////////////////////////////////////////
CIPU::CIDECCommand::CIDECCommand()
{
m_temp_OUT_FIFO.SetReceiveHandler(
2018-04-30 21:01:23 +01:00
[&](const void* data, uint32 size) {
m_blockStream.Write(data, size * 0x10);
return size;
});
}
2018-04-30 21:01:23 +01:00
void CIPU::CIDECCommand::Initialize(CBDECCommand* BDECCommand, CCSCCommand* CSCCommand, CINFIFO* inFifo, COUTFIFO* outFifo,
uint32 commandCode, const DECODER_CONTEXT& context, uint16 TH0, uint16 TH1)
{
m_command <<= commandCode;
assert(m_command.cmdId == IPU_CMD_IDEC);
2018-04-30 21:01:23 +01:00
m_IN_FIFO = inFifo;
m_OUT_FIFO = outFifo;
m_BDECCommand = BDECCommand;
m_CSCCommand = CSCCommand;
2018-04-30 21:01:23 +01:00
m_state = STATE_DELAY;
2018-10-10 13:08:59 -04:00
m_dt = 0;
2018-04-30 21:01:23 +01:00
m_mbType = 0;
m_qsc = m_command.qsc;
m_context = context;
m_TH0 = TH0;
m_TH1 = TH1;
m_mbCount = 0;
m_delayTicks = 1000;
}
bool CIPU::CIDECCommand::Execute()
{
while(1)
{
switch(m_state)
{
case STATE_DELAY:
2018-04-30 21:01:23 +01:00
{
//We need to induce a delay here because some games (Gust games) will issue a command
//and restart an already active DMA3 transfer
if(m_delayTicks > 0)
{
2018-04-30 21:01:23 +01:00
return false;
}
2018-04-30 21:01:23 +01:00
m_state = STATE_ADVANCE;
}
break;
case STATE_ADVANCE:
2018-04-30 21:01:23 +01:00
{
m_IN_FIFO->Advance(m_command.fb);
m_state = STATE_READMBTYPE;
}
break;
case STATE_READMBTYPE:
2018-04-30 21:01:23 +01:00
{
if(FilterSymbolError(CMacroblockTypeITable::GetInstance()->TryGetSymbol(m_IN_FIFO, m_mbType)) != CVLCTable::DECODE_STATUS_SUCCESS)
{
2018-04-30 21:01:23 +01:00
return false;
}
2018-04-30 21:01:23 +01:00
assert(m_mbType & 0x1); //Must always be intra block
m_state = STATE_READDCTTYPE;
}
break;
case STATE_READDCTTYPE:
2018-10-10 13:08:59 -04:00
if(m_command.dtd == 1)
{
if(!m_IN_FIFO->TryGetBits_MSBF(1, m_dt))
{
return false;
}
}
assert(m_dt == 0);
m_state = STATE_READQSC;
break;
case STATE_READQSC:
2018-04-30 21:01:23 +01:00
if(m_mbType & 0x10) //QSC flag
{
if(!m_IN_FIFO->TryGetBits_MSBF(5, m_qsc))
{
return false;
}
}
m_state = STATE_INITREADBLOCK;
break;
case STATE_INITREADBLOCK:
2018-04-30 21:01:23 +01:00
{
auto bdecCommand = make_convertible<CMD_BDEC>(0);
bdecCommand.cmdId = IPU_CMD_BDEC;
bdecCommand.fb = 0;
bdecCommand.mbi = 1;
2018-10-10 13:08:59 -04:00
bdecCommand.dt = m_dt;
2018-04-30 21:01:23 +01:00
bdecCommand.dcr = (m_mbCount == 0) ? 1 : 0;
bdecCommand.qsc = m_qsc;
m_BDECCommand->Initialize(m_IN_FIFO, &m_temp_OUT_FIFO, bdecCommand, false, m_context);
m_state = STATE_READBLOCK;
m_blockStream.ResetBuffer();
}
break;
case STATE_READBLOCK:
2018-04-30 21:01:23 +01:00
{
if(!m_BDECCommand->Execute())
{
2018-04-30 21:01:23 +01:00
return false;
}
2018-04-30 21:01:23 +01:00
//BDEC will yield 384 elements in RAW16 format
assert(m_blockStream.GetSize() == (CCSCCommand::BLOCK_SIZE * sizeof(int16)));
ConvertRawBlock();
m_state = STATE_CSCINIT;
m_mbCount++;
}
break;
case STATE_CSCINIT:
2018-04-30 21:01:23 +01:00
{
auto cscCommand = make_convertible<CMD_CSC>(0);
cscCommand.cmdId = IPU_CMD_CSC;
cscCommand.mbc = 1;
cscCommand.dte = m_command.dte;
cscCommand.ofm = m_command.ofm;
m_CSCCommand->Initialize(&m_temp_IN_FIFO, m_OUT_FIFO, cscCommand, m_TH0, m_TH1);
m_state = STATE_CSC;
//CSC requires 384 elements in RAW8 format to proceed
assert(m_blockStream.GetSize() == (CCSCCommand::BLOCK_SIZE * sizeof(uint8)));
m_blockStream.Seek(0, Framework::STREAM_SEEK_SET);
}
break;
case STATE_CSC:
while(1)
{
uint8 readBuffer[CINFIFO::BUFFERSIZE];
auto readSize = CINFIFO::BUFFERSIZE - m_temp_IN_FIFO.GetSize();
if(readSize != 0)
{
readSize = m_blockStream.Read(readBuffer, readSize);
m_temp_IN_FIFO.Write(readBuffer, readSize);
}
if(m_CSCCommand->Execute())
{
2017-01-03 18:54:04 -05:00
//All data should have been consumed by CSC, so nothing should remain
2023-05-02 08:56:25 -04:00
FRAMEWORK_MAYBE_UNUSED uint32 remainLength = m_temp_IN_FIFO.GetAvailableBits() + (m_blockStream.GetRemainingLength() * 8);
2017-01-03 18:54:04 -05:00
assert(remainLength == 0);
m_state = STATE_CHECKSTARTCODE;
break;
}
if(m_OUT_FIFO->GetSize() != 0)
{
//We assume that DMA3 didn't proceed and that we need to wait
//for CPU to accept the data
return false;
}
}
break;
case STATE_CHECKSTARTCODE:
2018-04-30 21:01:23 +01:00
{
uint32 nextBits = 0;
if(!m_IN_FIFO->TryPeekBits_MSBF(8, nextBits))
{
2018-04-30 21:01:23 +01:00
return false;
}
if(nextBits != 0)
2018-04-30 21:01:23 +01:00
{
m_state = STATE_READMBINCREMENT;
break;
2018-04-30 21:01:23 +01:00
}
m_IN_FIFO->SeekToByteAlign();
m_state = STATE_VALIDATESTARTCODE;
}
break;
case STATE_VALIDATESTARTCODE:
{
uint32 startCode = 0;
if(!m_IN_FIFO->TryPeekBits_MSBF(24, startCode))
2018-04-30 21:01:23 +01:00
{
//Not enough bits to get the full code, but we detected 8 zero bits
//in the previous state, we can assume we found a start code and bail
//Helps games like SMT: Nocturne which finishes a data packet with 8 zero bits
throw CStartCodeException();
}
if(startCode == 0)
{
//Only got zeroes, keep looking
m_IN_FIFO->Advance(8);
}
else if(startCode == 1)
{
//Found our start code
m_state = STATE_DONE;
}
else
{
//Not 0 or 1, something went wrong
throw CVLCTable::CVLCTableException();
2018-04-30 21:01:23 +01:00
}
}
break;
case STATE_READMBINCREMENT:
2018-04-30 21:01:23 +01:00
{
uint32 mbIncrement = 0;
if(CMacroblockAddressIncrementTable::GetInstance()->TryGetSymbol(m_IN_FIFO, mbIncrement) != CVLCTable::DECODE_STATUS_SUCCESS)
{
2018-04-30 21:01:23 +01:00
return false;
}
2018-04-30 21:01:23 +01:00
assert((mbIncrement & 0xFFFF) == 1);
m_state = STATE_READMBTYPE;
}
break;
case STATE_DONE:
return true;
break;
default:
assert(false);
break;
}
}
return false;
}
void CIPU::CIDECCommand::CountTicks(uint32 ticks)
{
m_delayTicks -= ticks;
}
bool CIPU::CIDECCommand::IsDelayed() const
{
return (m_state == STATE_DELAY);
}
void CIPU::CIDECCommand::ConvertRawBlock()
{
//Convert block from RAW16 to RAW8
int16 blockData[CCSCCommand::BLOCK_SIZE];
m_blockStream.Seek(0, Framework::STREAM_SEEK_SET);
m_blockStream.Read(blockData, CCSCCommand::BLOCK_SIZE * sizeof(int16));
m_blockStream.ResetBuffer();
for(uint32 i = 0; i < CCSCCommand::BLOCK_SIZE; i++)
{
int16 blockValue = blockData[i];
blockValue = std::max<int16>(blockValue, 0);
blockValue = std::min<int16>(blockValue, 255);
m_blockStream.Write8(static_cast<uint8>(blockValue));
}
}
/////////////////////////////////////////////
//BDEC command implementation
/////////////////////////////////////////////
CIPU::CBDECCommand::CBDECCommand()
{
2018-04-30 21:01:23 +01:00
m_blocks[0].block = m_yBlock[0];
m_blocks[0].channel = 0;
m_blocks[1].block = m_yBlock[1];
m_blocks[1].channel = 0;
m_blocks[2].block = m_yBlock[2];
m_blocks[2].channel = 0;
m_blocks[3].block = m_yBlock[3];
m_blocks[3].channel = 0;
m_blocks[4].block = m_cbBlock;
m_blocks[4].channel = 1;
m_blocks[5].block = m_crBlock;
m_blocks[5].channel = 2;
}
void CIPU::CBDECCommand::Initialize(CINFIFO* inFifo, COUTFIFO* outFifo, uint32 commandCode, bool checkStartCode, const DECODER_CONTEXT& context)
{
m_command <<= commandCode;
assert(m_command.cmdId == IPU_CMD_BDEC);
m_checkStartCode = checkStartCode;
m_context = context;
2018-04-30 21:01:23 +01:00
m_IN_FIFO = inFifo;
m_OUT_FIFO = outFifo;
m_state = STATE_ADVANCE;
m_codedBlockPattern = 0;
m_currentBlockIndex = 0;
}
bool CIPU::CBDECCommand::Execute()
{
while(1)
{
switch(m_state)
{
case STATE_ADVANCE:
2018-04-30 21:01:23 +01:00
{
m_IN_FIFO->Advance(m_command.fb);
m_state = STATE_READCBP;
}
break;
case STATE_READCBP:
{
if(!m_command.mbi)
{
2018-04-30 21:01:23 +01:00
//Not an Intra Macroblock, so we need to fetch the pattern code
m_codedBlockPattern = static_cast<uint8>(CCodedBlockPatternTable::GetInstance()->GetSymbol(m_IN_FIFO));
}
2018-04-30 21:01:23 +01:00
else
{
2018-04-30 21:01:23 +01:00
m_codedBlockPattern = 0x3F;
}
2018-04-30 21:01:23 +01:00
m_state = STATE_RESETDC;
}
break;
case STATE_RESETDC:
2018-04-30 21:01:23 +01:00
{
#ifdef _DECODE_LOGGING
2018-04-30 21:01:23 +01:00
static int currentMbIndex = 0;
CLog::GetInstance().Print(DECODE_LOG_NAME, "Macroblock(%d, CBP: 0x%02X)\r\n",
currentMbIndex++, m_codedBlockPattern);
#endif
2018-04-30 21:01:23 +01:00
if(m_command.dcr)
{
int16 resetValue = 0;
2018-04-30 21:01:23 +01:00
//Reset the DC prediction values
switch(m_context.dcPrecision)
{
case 0:
resetValue = 128;
break;
case 1:
resetValue = 256;
break;
case 2:
resetValue = 512;
break;
default:
resetValue = 0;
assert(0);
break;
}
2018-04-30 21:01:23 +01:00
m_context.dcPredictor[0] = resetValue;
m_context.dcPredictor[1] = resetValue;
m_context.dcPredictor[2] = resetValue;
}
2018-04-30 21:01:23 +01:00
m_state = STATE_DECODEBLOCK_BEGIN;
}
break;
case STATE_DECODEBLOCK_BEGIN:
2018-04-30 21:01:23 +01:00
{
const BLOCKENTRY& blockInfo(m_blocks[m_currentBlockIndex]);
2018-04-30 21:01:23 +01:00
memset(blockInfo.block, 0, sizeof(int16) * 64);
2018-04-30 21:01:23 +01:00
if((m_codedBlockPattern & (1 << (5 - m_currentBlockIndex))))
{
m_readDctCoeffsCommand.Initialize(m_IN_FIFO,
blockInfo.block, blockInfo.channel,
m_context.dcPredictor, (m_command.mbi != 0), m_context.isMpeg1CoeffVLCTable, m_context.isMpeg2);
2018-04-30 21:01:23 +01:00
m_state = STATE_DECODEBLOCK_READCOEFFS;
}
2018-04-30 21:01:23 +01:00
else
{
m_state = STATE_DECODEBLOCK_GOTONEXT;
}
}
break;
case STATE_DECODEBLOCK_READCOEFFS:
2018-04-30 21:01:23 +01:00
{
if(!m_readDctCoeffsCommand.Execute())
{
2018-04-30 21:01:23 +01:00
return false;
}
2018-04-30 21:01:23 +01:00
BLOCKENTRY& blockInfo(m_blocks[m_currentBlockIndex]);
int16 blockTemp[0x40];
2018-04-30 21:01:23 +01:00
DequantiseBlock(blockInfo.block, (m_command.mbi != 0), m_command.qsc,
m_context.isLinearQScale, m_context.dcPrecision, m_context.intraIq, m_context.nonIntraIq);
InverseScan(blockInfo.block, m_context.isZigZag);
2018-04-30 21:01:23 +01:00
memcpy(blockTemp, blockInfo.block, sizeof(int16) * 0x40);
2018-04-30 21:01:23 +01:00
IDCT::CIEEE1180::GetInstance()->Transform(blockTemp, blockInfo.block);
2018-04-30 21:01:23 +01:00
m_state = STATE_DECODEBLOCK_GOTONEXT;
}
break;
case STATE_DECODEBLOCK_GOTONEXT:
2018-04-30 21:01:23 +01:00
{
m_currentBlockIndex++;
if(m_currentBlockIndex == 6)
{
2018-04-30 21:01:23 +01:00
m_state = STATE_DONE;
}
2018-04-30 21:01:23 +01:00
else
{
m_state = STATE_DECODEBLOCK_BEGIN;
}
}
break;
case STATE_DONE:
2018-04-30 21:01:23 +01:00
{
//Write blocks into out FIFO
for(unsigned int i = 0; i < 8; i++)
{
2018-04-30 21:01:23 +01:00
m_OUT_FIFO->Write(m_blocks[0].block + (i * 8), sizeof(int16) * 0x8);
m_OUT_FIFO->Write(m_blocks[1].block + (i * 8), sizeof(int16) * 0x8);
}
2018-04-30 21:01:23 +01:00
for(unsigned int i = 0; i < 8; i++)
{
m_OUT_FIFO->Write(m_blocks[2].block + (i * 8), sizeof(int16) * 0x8);
m_OUT_FIFO->Write(m_blocks[3].block + (i * 8), sizeof(int16) * 0x8);
}
2018-04-30 21:01:23 +01:00
m_OUT_FIFO->Write(m_blocks[4].block, sizeof(int16) * 0x40);
m_OUT_FIFO->Write(m_blocks[5].block, sizeof(int16) * 0x40);
2018-04-30 21:01:23 +01:00
m_OUT_FIFO->Flush();
2018-04-30 21:01:23 +01:00
//Check if there's more than 7 zero bits after this and set "start code detected"
if(m_checkStartCode)
{
uint32 nextBits = 0;
if(m_IN_FIFO->TryPeekBits_MSBF(8, nextBits))
{
2018-04-30 21:01:23 +01:00
if(nextBits == 0)
{
2018-04-30 21:01:23 +01:00
throw CStartCodeException();
}
}
}
2018-04-30 21:01:23 +01:00
}
return true;
break;
}
}
}
/////////////////////////////////////////////
//BDEC ReadDct subcommand implementation
/////////////////////////////////////////////
void CIPU::CBDECCommand_ReadDct::Initialize(CINFIFO* fifo, int16* block, unsigned int channelId, int16* dcPredictor, bool mbi, bool isMpeg1CoeffVLCTable, bool isMpeg2)
{
2018-04-30 21:01:23 +01:00
m_state = STATE_INIT;
m_IN_FIFO = fifo;
m_block = block;
m_dcPredictor = dcPredictor;
m_channelId = channelId;
m_mbi = mbi;
m_isMpeg1CoeffVLCTable = isMpeg1CoeffVLCTable;
m_isMpeg2 = isMpeg2;
m_coeffTable = NULL;
m_blockIndex = 0;
m_dcDiff = 0;
if(m_mbi && !m_isMpeg1CoeffVLCTable)
{
m_coeffTable = &CDctCoefficientTable1::GetInstance();
}
else
{
m_coeffTable = &CDctCoefficientTable0::GetInstance();
}
}
bool CIPU::CBDECCommand_ReadDct::Execute()
{
while(1)
{
switch(m_state)
{
case STATE_INIT:
2018-04-30 21:01:23 +01:00
{
#ifdef _DECODE_LOGGING
2018-04-30 21:01:23 +01:00
static int currentBlockIndex = 0;
CLog::GetInstance().Print(DECODE_LOG_NAME, "Block(%d) = ", currentBlockIndex++);
#endif
2018-04-30 21:01:23 +01:00
if(m_mbi)
{
m_readDcDiffCommand.Initialize(m_IN_FIFO, m_channelId, &m_dcDiff);
m_state = STATE_READDCDIFF;
}
2018-04-30 21:01:23 +01:00
else
{
m_state = STATE_CHECKEOB;
}
}
break;
case STATE_READDCDIFF:
2018-04-30 21:01:23 +01:00
{
if(!m_readDcDiffCommand.Execute())
{
2018-04-30 21:01:23 +01:00
return false;
}
m_block[0] = static_cast<int16>(m_dcPredictor[m_channelId] + m_dcDiff);
m_dcPredictor[m_channelId] = m_block[0];
#ifdef _DECODE_LOGGING
2018-04-30 21:01:23 +01:00
CLog::GetInstance().Print(DECODE_LOG_NAME, "[%d]: %d ", 0, m_block[0]);
#endif
2018-04-30 21:01:23 +01:00
m_blockIndex = 1;
m_state = STATE_CHECKEOB;
}
2018-04-30 21:01:23 +01:00
break;
case STATE_CHECKEOB:
2018-04-30 21:01:23 +01:00
{
bool isEob = false;
if(m_coeffTable->TryIsEndOfBlock(m_IN_FIFO, isEob) != CVLCTable::DECODE_STATUS_SUCCESS)
{
2018-04-30 21:01:23 +01:00
return false;
}
2018-04-30 21:01:23 +01:00
if((m_blockIndex != 0) && isEob)
{
m_state = STATE_SKIPEOB;
}
else
{
m_state = STATE_READCOEFF;
}
}
break;
case STATE_READCOEFF:
2018-04-30 21:01:23 +01:00
{
MPEG2::RUNLEVELPAIR runLevelPair;
if(m_blockIndex == 0)
{
2018-04-30 21:01:23 +01:00
if(FilterSymbolError(m_coeffTable->TryGetRunLevelPairDc(m_IN_FIFO, &runLevelPair, m_isMpeg2)) != CVLCTable::DECODE_STATUS_SUCCESS)
{
2018-04-30 21:01:23 +01:00
return false;
}
2018-04-30 21:01:23 +01:00
}
else
{
if(FilterSymbolError(m_coeffTable->TryGetRunLevelPair(m_IN_FIFO, &runLevelPair, m_isMpeg2)) != CVLCTable::DECODE_STATUS_SUCCESS)
{
2018-04-30 21:01:23 +01:00
return false;
}
2018-04-30 21:01:23 +01:00
}
m_blockIndex += runLevelPair.run;
if(m_blockIndex < 0x40)
{
m_block[m_blockIndex] = static_cast<int16>(runLevelPair.level);
#ifdef _DECODE_LOGGING
2018-04-30 21:01:23 +01:00
CLog::GetInstance().Print(DECODE_LOG_NAME, "[%d]: %d ", m_blockIndex, runLevelPair.level);
#endif
}
2018-04-30 21:01:23 +01:00
else
{
throw CVLCTable::CVLCTableException();
}
m_blockIndex++;
m_state = STATE_CHECKEOB;
}
break;
case STATE_SKIPEOB:
if(m_coeffTable->TrySkipEndOfBlock(m_IN_FIFO) != CVLCTable::DECODE_STATUS_SUCCESS)
{
return false;
}
#ifdef _DECODE_LOGGING
CLog::GetInstance().Print(DECODE_LOG_NAME, "\r\n");
#endif
return true;
break;
}
}
}
/////////////////////////////////////////////
//BDEC ReadDcDiff subcommand implementation
/////////////////////////////////////////////
void CIPU::CBDECCommand_ReadDcDiff::Initialize(CINFIFO* fifo, unsigned int channelId, int16* result)
{
m_IN_FIFO = fifo;
m_channelId = channelId;
m_state = STATE_READSIZE;
m_dcSize = 0;
m_result = result;
}
bool CIPU::CBDECCommand_ReadDcDiff::Execute()
{
while(1)
{
switch(m_state)
{
case STATE_READSIZE:
2018-04-30 21:01:23 +01:00
{
uint32 dcSize = 0;
switch(m_channelId)
{
2018-04-30 21:01:23 +01:00
case 0:
if(CDcSizeLuminanceTable::GetInstance()->TryGetSymbol(m_IN_FIFO, dcSize) != CVLCTable::DECODE_STATUS_SUCCESS)
{
2018-04-30 21:01:23 +01:00
return false;
}
2018-04-30 21:01:23 +01:00
break;
case 1:
case 2:
if(CDcSizeChrominanceTable::GetInstance()->TryGetSymbol(m_IN_FIFO, dcSize) != CVLCTable::DECODE_STATUS_SUCCESS)
{
return false;
}
break;
}
2018-04-30 21:01:23 +01:00
m_dcSize = dcSize;
m_state = STATE_READDIFF;
}
break;
case STATE_READDIFF:
2018-04-30 21:01:23 +01:00
{
int16 result = 0;
if(m_dcSize == 0)
{
result = 0;
}
else
{
2018-04-30 21:01:23 +01:00
uint32 diffValue = 0;
if(!m_IN_FIFO->TryGetBits_MSBF(m_dcSize, diffValue))
{
2018-04-30 21:01:23 +01:00
return false;
}
2018-04-30 21:01:23 +01:00
int16 halfRange = (1 << (m_dcSize - 1));
result = static_cast<int16>(diffValue);
2018-04-30 21:01:23 +01:00
if(result < halfRange)
{
result = (result + 1) - (2 * halfRange);
}
}
2018-04-30 21:01:23 +01:00
(*m_result) = result;
m_state = STATE_DONE;
}
break;
case STATE_DONE:
return true;
break;
}
}
}
/////////////////////////////////////////////
//VDEC command implementation
/////////////////////////////////////////////
void CIPU::CVDECCommand::Initialize(CINFIFO* fifo, uint32 commandCode, uint32 pictureType, uint32* result)
{
2018-04-30 21:01:23 +01:00
m_IN_FIFO = fifo;
m_commandCode = commandCode;
m_state = STATE_ADVANCE;
m_result = result;
uint32 tbl = (commandCode >> 26) & 0x03;
switch(tbl)
{
case 0:
//Macroblock Address Increment
m_table = CMacroblockAddressIncrementTable::GetInstance();
break;
case 1:
//Macroblock Type
switch(pictureType)
{
case 1:
//I Picture
m_table = CMacroblockTypeITable::GetInstance();
break;
case 2:
//P Picture
m_table = CMacroblockTypePTable::GetInstance();
break;
case 3:
//B Picture
m_table = CMacroblockTypeBTable::GetInstance();
break;
default:
assert(0);
return;
break;
}
break;
case 2:
m_table = CMotionCodeTable::GetInstance();
break;
case 3:
m_table = CDmVectorTable::GetInstance();
break;
default:
assert(0);
return;
break;
}
}
bool CIPU::CVDECCommand::Execute()
{
while(1)
{
switch(m_state)
{
case STATE_ADVANCE:
2018-04-30 21:01:23 +01:00
{
m_IN_FIFO->Advance(static_cast<uint8>(m_commandCode & 0x3F));
m_state = STATE_DECODE;
}
break;
case STATE_DECODE:
2018-04-30 21:01:23 +01:00
{
(*m_result) = m_table->GetSymbol(m_IN_FIFO);
m_state = STATE_DONE;
}
break;
case STATE_DONE:
#ifdef _DECODE_LOGGING
const char* tableName = "unknown";
if(m_table == CMacroblockAddressIncrementTable::GetInstance())
{
tableName = "mb increment";
}
else if(
2018-04-30 21:01:23 +01:00
(m_table == CMacroblockTypeITable::GetInstance()) ||
(m_table == CMacroblockTypePTable::GetInstance()) ||
(m_table == CMacroblockTypeBTable::GetInstance()))
{
tableName = "mb type";
}
else if(m_table == CMotionCodeTable::GetInstance())
{
tableName = "motion code";
}
else if(m_table == CDmVectorTable::GetInstance())
{
tableName = "dm vector";
}
static unsigned int currentVdec = 0;
2018-04-30 21:01:23 +01:00
CLog::GetInstance().Print(DECODE_LOG_NAME, "Symbol(%d, '%s') = %d\r\n",
currentVdec++, tableName, static_cast<int16>((*m_result) & 0xFFFF));
#endif
return true;
break;
}
}
}
/////////////////////////////////////////////
//FDEC command implementation
/////////////////////////////////////////////
void CIPU::CFDECCommand::Initialize(CINFIFO* fifo, uint32 commandCode, uint32* result)
{
2018-04-30 21:01:23 +01:00
m_IN_FIFO = fifo;
m_commandCode = commandCode;
m_state = STATE_ADVANCE;
m_result = result;
}
bool CIPU::CFDECCommand::Execute()
{
while(1)
{
switch(m_state)
{
case STATE_ADVANCE:
2018-04-30 21:01:23 +01:00
{
m_IN_FIFO->Advance(static_cast<uint8>(m_commandCode & 0x3F));
m_state = STATE_DECODE;
}
break;
case STATE_DECODE:
2018-04-30 21:01:23 +01:00
{
if(!m_IN_FIFO->TryPeekBits_MSBF(32, *m_result))
{
2018-04-30 21:01:23 +01:00
return false;
}
2018-04-30 21:01:23 +01:00
m_state = STATE_DONE;
}
break;
case STATE_DONE:
return true;
break;
}
}
}
/////////////////////////////////////////////
//SETIQ command implementation
/////////////////////////////////////////////
CIPU::CSETIQCommand::CSETIQCommand()
2018-04-30 21:01:23 +01:00
: m_IN_FIFO(NULL)
, m_matrix(NULL)
, m_currentIndex(0)
{
}
void CIPU::CSETIQCommand::Initialize(CINFIFO* fifo, uint8* matrix)
{
2018-04-30 21:01:23 +01:00
m_IN_FIFO = fifo;
m_matrix = matrix;
m_currentIndex = 0;
}
bool CIPU::CSETIQCommand::Execute()
{
while(m_currentIndex != 0x40)
{
m_matrix[m_currentIndex] = static_cast<uint8>(m_IN_FIFO->GetBits_MSBF(8));
m_currentIndex++;
}
return true;
}
/////////////////////////////////////////////
//SETVQ command implementation
/////////////////////////////////////////////
CIPU::CSETVQCommand::CSETVQCommand()
2018-04-30 21:01:23 +01:00
: m_IN_FIFO(NULL)
, m_clut(NULL)
, m_currentIndex(0)
{
}
void CIPU::CSETVQCommand::Initialize(CINFIFO* fifo, uint16* clut)
{
2018-04-30 21:01:23 +01:00
m_IN_FIFO = fifo;
m_clut = clut;
m_currentIndex = 0;
}
bool CIPU::CSETVQCommand::Execute()
{
while(m_currentIndex != 0x10)
{
m_clut[m_currentIndex] = static_cast<uint16>(m_IN_FIFO->GetBits_MSBF(16));
m_currentIndex++;
}
return true;
}
/////////////////////////////////////////////
//CSC command implementation
/////////////////////////////////////////////
CIPU::CCSCCommand::CCSCCommand()
{
GenerateCbCrMap();
}
void CIPU::CCSCCommand::Initialize(CINFIFO* input, COUTFIFO* output, uint32 commandCode, uint16 TH0, uint16 TH1)
{
m_command <<= commandCode;
assert(m_command.cmdId == IPU_CMD_CSC);
m_state = STATE_READBLOCKSTART;
m_IN_FIFO = input;
m_OUT_FIFO = output;
m_TH0 = TH0;
m_TH1 = TH1;
m_currentIndex = 0;
m_mbCount = m_command.mbc;
}
bool CIPU::CCSCCommand::Execute()
{
while(1)
{
switch(m_state)
{
case STATE_READBLOCKSTART:
2018-04-30 21:01:23 +01:00
{
if(m_mbCount == 0)
{
2018-04-30 21:01:23 +01:00
m_state = STATE_DONE;
}
2018-04-30 21:01:23 +01:00
else
{
m_state = STATE_READBLOCK;
m_currentIndex = 0;
}
}
break;
case STATE_READBLOCK:
2018-04-30 21:01:23 +01:00
{
if(m_currentIndex == BLOCK_SIZE)
{
2018-04-30 21:01:23 +01:00
m_state = STATE_CONVERTBLOCK;
}
else
{
uint32 blockValue = 0;
if(!m_IN_FIFO->TryGetBits_MSBF(8, blockValue))
{
2018-04-30 21:01:23 +01:00
return false;
}
2018-04-30 21:01:23 +01:00
m_block[m_currentIndex] = static_cast<uint8>(blockValue);
m_currentIndex++;
}
2018-04-30 21:01:23 +01:00
}
break;
case STATE_CONVERTBLOCK:
2018-04-30 21:01:23 +01:00
{
uint32 nPixel[0x100];
uint8* pY = m_block;
uint8* nBlockCb = m_block + 0x100;
uint8* nBlockCr = m_block + 0x140;
2018-04-30 21:01:23 +01:00
uint32* pPixel = nPixel;
unsigned int* pCbCrMap = m_nCbCrMap;
uint16 alphaTh0 = (m_TH0 & 0x1FF);
uint16 alphaTh1 = (m_TH1 & 0x1FF);
2018-04-30 21:01:23 +01:00
for(unsigned int i = 0; i < 16; i++)
{
for(unsigned int j = 0; j < 16; j++)
{
2018-04-30 21:01:23 +01:00
float nY = pY[j];
float nCb = nBlockCb[pCbCrMap[j]];
float nCr = nBlockCr[pCbCrMap[j]];
float nR = nY + 1.402f * (nCr - 128);
float nG = nY - 0.34414f * (nCb - 128) - 0.71414f * (nCr - 128);
float nB = nY + 1.772f * (nCb - 128);
2021-04-14 16:34:53 -04:00
nR = std::clamp(nR, 0.f, 255.f);
nG = std::clamp(nG, 0.f, 255.f);
nB = std::clamp(nB, 0.f, 255.f);
2018-04-30 21:01:23 +01:00
uint8 a = 0;
uint8 r = static_cast<uint8>(nR);
uint8 g = static_cast<uint8>(nG);
uint8 b = static_cast<uint8>(nB);
if(r < alphaTh0 && g < alphaTh0 && b < alphaTh0)
2018-04-30 21:01:23 +01:00
{
a = 0;
}
else if(r < alphaTh1 && g < alphaTh1 && b < alphaTh1)
2018-04-30 21:01:23 +01:00
{
a = 0x40;
}
else
{
a = 0x80;
}
pPixel[j] = (a << 24) | (b << 16) | (g << 8) | (r << 0);
2018-04-30 21:01:23 +01:00
}
2018-04-30 21:01:23 +01:00
pY += 0x10;
pCbCrMap += 0x10;
pPixel += 0x10;
}
2018-04-30 21:01:23 +01:00
if(m_command.ofm == 1)
{
//RGBA16 output
uint16 cvtPixels[0x100];
for(uint32 i = 0; i < 0x100; i++)
{
uint32 pixel = nPixel[i];
uint16 result = 0;
result |= ((pixel & 0x000000F8) >> (0 + 3)) << 0;
result |= ((pixel & 0x0000F800) >> (8 + 3)) << 5;
result |= ((pixel & 0x00F80000) >> (16 + 3)) << 10;
result |= ((pixel & 0x80000000) >> 31) << 15;
cvtPixels[i] = result;
}
m_OUT_FIFO->Write(cvtPixels, sizeof(uint16) * 0x100);
}
else
{
//RGBA32 output
m_OUT_FIFO->Write(nPixel, sizeof(uint32) * 0x100);
}
2018-04-30 21:01:23 +01:00
m_mbCount--;
m_state = STATE_FLUSHBLOCK;
}
break;
case STATE_FLUSHBLOCK:
2018-04-30 21:01:23 +01:00
{
m_OUT_FIFO->Flush();
if(m_OUT_FIFO->GetSize() != 0)
{
2018-04-30 21:01:23 +01:00
return false;
}
2018-04-30 21:01:23 +01:00
m_state = STATE_READBLOCKSTART;
}
break;
case STATE_DONE:
return true;
break;
}
}
}
void CIPU::CCSCCommand::GenerateCbCrMap()
{
unsigned int* pCbCrMap = m_nCbCrMap;
for(unsigned int i = 0; i < 0x40; i += 0x8)
{
for(unsigned int j = 0; j < 0x10; j += 2)
{
pCbCrMap[j + 0x00] = (j / 2) + i;
pCbCrMap[j + 0x01] = (j / 2) + i;
pCbCrMap[j + 0x10] = (j / 2) + i;
pCbCrMap[j + 0x11] = (j / 2) + i;
}
pCbCrMap += 0x20;
}
}
/////////////////////////////////////////////
//SETTH command implementation
/////////////////////////////////////////////
CIPU::CSETTHCommand::CSETTHCommand()
2018-04-30 21:01:23 +01:00
: m_commandCode(0)
, m_TH0(NULL)
, m_TH1(NULL)
{
}
void CIPU::CSETTHCommand::Initialize(uint32 commandCode, uint16* TH0, uint16* TH1)
{
m_commandCode = commandCode;
m_TH0 = TH0;
m_TH1 = TH1;
}
bool CIPU::CSETTHCommand::Execute()
{
2018-04-30 21:01:23 +01:00
(*m_TH0) = static_cast<uint16>((m_commandCode >> 0) & 0x1FF);
(*m_TH1) = static_cast<uint16>((m_commandCode >> 16) & 0x1FF);
return true;
}