pureikyubu/SRC/Hardware/CP.cpp
2020-07-23 17:31:15 +03:00

361 lines
12 KiB
C++

// CP - command processor, PE - pixel engine.
#include "pch.h"
using namespace Debug;
FifoControl fifo;
// ---------------------------------------------------------------------------
// fifo
// TODO: Make a GP update when copying the frame buffer by Pixel Engine.
static void DONE_INT()
{
fifo.done_num++; vi.frames++;
if(fifo.done_num == 1)
{
vi.xfb = 0; // disable VI output
}
if (fifo.log)
{
Report(Channel::PE, "PE_DONE (frame:%u)", fifo.done_num);
}
if(fifo.pe.sr & PE_SR_DONEMSK)
{
fifo.pe.sr |= PE_SR_DONE;
PIAssertInt(PI_INTERRUPT_PE_FINISH);
}
}
static void TOKEN_INT()
{
vi.frames++;
if (fifo.log)
{
Report(Channel::PE, "PE_TOKEN (%04X)", fifo.pe.token);
}
vi.xfb = 0; // disable VI output
if(fifo.pe.sr & PE_SR_TOKENMSK)
{
fifo.pe.sr |= PE_SR_TOKEN;
PIAssertInt(PI_INTERRUPT_PE_TOKEN);
}
}
static void CP_BREAK()
{
if (fifo.cp.cr & CP_CR_BPINTEN && (fifo.cp.sr & CP_SR_BPINT) == 0)
{
fifo.cp.sr |= CP_SR_BPINT;
PIAssertInt(PI_INTERRUPT_CP);
Report(Channel::CP, "BREAK");
}
}
static void CP_OVF()
{
if (fifo.cp.cr & CP_CR_OVFEN && (fifo.cp.sr & CP_SR_OVF) == 0)
{
fifo.cp.sr |= CP_SR_OVF;
PIAssertInt(PI_INTERRUPT_CP);
Report(Channel::CP, "OVF");
}
}
static void CP_UVF()
{
if (fifo.cp.cr & CP_CR_UVFEN && (fifo.cp.sr & CP_SR_UVF) == 0)
{
fifo.cp.sr |= CP_SR_UVF;
PIAssertInt(PI_INTERRUPT_CP);
Report(Channel::CP, "UVF");
}
}
static void CPDrawDoneCallback()
{
DONE_INT();
}
static void CPDrawTokenCallback(uint16_t tokenValue)
{
fifo.pe.token = tokenValue;
TOKEN_INT();
}
static void CPThread(void* Param)
{
while (true)
{
int64_t ticks = Gekko::Gekko->GetTicks();
if (ticks < fifo.updateTbrValue)
{
continue;
}
fifo.updateTbrValue = ticks + fifo.tickPerFifo;
// Calculate count
if (fifo.cp.wrptr >= fifo.cp.rdptr)
{
fifo.cp.cnt = fifo.cp.wrptr - fifo.cp.rdptr;
}
else
{
fifo.cp.cnt = (fifo.cp.top - fifo.cp.rdptr) + (fifo.cp.wrptr - fifo.cp.base);
}
// Watermarks logic. Active only in linked-mode.
if (fifo.cp.cnt > fifo.cp.himark && (fifo.cp.cr & CP_CR_WPINC))
{
CP_OVF();
}
if (fifo.cp.cnt < fifo.cp.lomark && (fifo.cp.cr & CP_CR_WPINC))
{
CP_UVF();
}
// Breakpoint
if ((fifo.cp.rdptr & ~0x1f) == (fifo.cp.bpptr & ~0x1f) && (fifo.cp.cr & CP_CR_BPEN))
{
CP_BREAK();
}
// Advance read pointer.
if (fifo.cp.cnt != 0 && fifo.cp.cr & CP_CR_RDEN && (fifo.cp.sr & (CP_SR_OVF | CP_SR_UVF | CP_SR_BPINT)) == 0)
{
fifo.cp.sr &= ~CP_SR_RD_IDLE;
fifo.cp.sr &= ~CP_SR_CMD_IDLE;
GXWriteFifo(&mi.ram[fifo.cp.rdptr & RAMMASK]);
fifo.cp.sr |= CP_SR_CMD_IDLE;
fifo.cp.rdptr += 32;
if (fifo.cp.rdptr == fifo.cp.top)
{
fifo.cp.rdptr = fifo.cp.base;
}
}
else
{
fifo.cp.sr |= (CP_SR_RD_IDLE | CP_SR_CMD_IDLE);
}
}
}
// ---------------------------------------------------------------------------
// registers
//
// pixel engine status register (0x100a)
//
static void write_pe_sr(uint32_t addr, uint32_t data)
{
// clear interrupts
if(fifo.pe.sr & PE_SR_DONE)
{
fifo.pe.sr &= ~PE_SR_DONE;
PIClearInt(PI_INTERRUPT_PE_FINISH);
}
if(fifo.pe.sr & PE_SR_TOKEN)
{
fifo.pe.sr &= ~PE_SR_TOKEN;
PIClearInt(PI_INTERRUPT_PE_TOKEN);
}
// set mask bits
if(data & PE_SR_DONEMSK) fifo.pe.sr |= PE_SR_DONEMSK;
else fifo.pe.sr &= ~PE_SR_DONEMSK;
if(data & PE_SR_TOKENMSK) fifo.pe.sr |= PE_SR_TOKENMSK;
else fifo.pe.sr &= ~PE_SR_TOKENMSK;
}
static void read_pe_sr(uint32_t addr, uint32_t *reg) { *reg = fifo.pe.sr; }
static void read_pe_token(uint32_t addr, uint32_t *reg) { *reg = fifo.pe.token; }
//
// command processor
//
// control and status registers
static void read_cp_sr(uint32_t addr, uint32_t *reg)
{
*reg = fifo.cp.sr;
}
static void write_cp_cr(uint32_t addr, uint32_t data)
{
fifo.cp.cr = (uint16_t)data;
// clear breakpoint
if((data & CP_CR_BPINTEN) == 0)
{
fifo.cp.sr &= ~CP_SR_BPINT;
}
if ((fifo.cp.sr & CP_SR_BPINT) == 0 && (fifo.cp.sr & CP_SR_OVF) == 0 && (fifo.cp.sr & CP_SR_UVF) == 0)
{
PIClearInt(PI_INTERRUPT_CP);
}
}
static void read_cp_cr(uint32_t addr, uint32_t *reg) { *reg = fifo.cp.cr; }
static void write_cp_clr(uint32_t addr, uint32_t data)
{
// clear watermark conditions
if(data & CP_CLR_OVFCLR)
{
fifo.cp.sr &= ~CP_SR_OVF;
}
if(data & CP_CLR_UVFCLR)
{
fifo.cp.sr &= ~CP_SR_UVF;
}
if ((fifo.cp.sr & CP_SR_BPINT) == 0 && (fifo.cp.sr & CP_SR_OVF) == 0 && (fifo.cp.sr & CP_SR_UVF) == 0)
{
PIClearInt(PI_INTERRUPT_CP);
}
}
// pointers
// show CP fifo configuration
void DumpCPFIFO()
{
// fifo modes
char*md = (fifo.cp.cr & CP_CR_WPINC) ? ((char *)"immediate ") : ((char *)"multi-");
char bp = (fifo.cp.cr & CP_CR_BPEN) ? ('B') : ('b'); // breakpoint
char lw = (fifo.cp.cr & CP_CR_UVFEN)? ('U') : ('u'); // low-wmark
char hw = (fifo.cp.cr & CP_CR_OVFEN)? ('O') : ('o'); // high-wmark
Report(Channel::Norm, "CP %sfifo configuration:%c%c%c", md, bp, lw, hw);
Report(Channel::Norm, "control :0x%08X", fifo.cp.cr);
Report(Channel::Norm, " status :0x%08X", fifo.cp.sr);
Report(Channel::Norm, " base :0x%08X", fifo.cp.base);
Report(Channel::Norm, " top :0x%08X", fifo.cp.top);
Report(Channel::Norm, " low :0x%08X", fifo.cp.lomark);
Report(Channel::Norm, " high :0x%08X", fifo.cp.himark);
Report(Channel::Norm, " cnt :0x%08X", fifo.cp.cnt);
Report(Channel::Norm, " wrptr:0x%08X", fifo.cp.wrptr);
Report(Channel::Norm, " rdptr:0x%08X", fifo.cp.rdptr);
Report(Channel::Norm, " break:0x%08X", fifo.cp.bpptr);
}
static void read_cp_baseh(uint32_t addr, uint32_t *reg) { *reg = fifo.cp.baseh; }
static void write_cp_baseh(uint32_t addr, uint32_t data) { fifo.cp.baseh = data; }
static void read_cp_basel(uint32_t addr, uint32_t *reg) { *reg = fifo.cp.basel & 0xffe0; }
static void write_cp_basel(uint32_t addr, uint32_t data) { fifo.cp.basel = data & 0xffe0; }
static void read_cp_toph(uint32_t addr, uint32_t *reg) { *reg = fifo.cp.toph; }
static void write_cp_toph(uint32_t addr, uint32_t data) { fifo.cp.toph = data; }
static void read_cp_topl(uint32_t addr, uint32_t *reg) { *reg = fifo.cp.topl & 0xffe0; }
static void write_cp_topl(uint32_t addr, uint32_t data) { fifo.cp.topl = data & 0xffe0; }
static void read_cp_hmarkh(uint32_t addr, uint32_t *reg) { *reg = fifo.cp.himarkh; }
static void write_cp_hmarkh(uint32_t addr, uint32_t data) { fifo.cp.himarkh = data; }
static void read_cp_hmarkl(uint32_t addr, uint32_t *reg) { *reg = fifo.cp.himarkl & 0xffe0; }
static void write_cp_hmarkl(uint32_t addr, uint32_t data) { fifo.cp.himarkl = data & 0xffe0; }
static void read_cp_lmarkh(uint32_t addr, uint32_t *reg) { *reg = fifo.cp.lomarkh; }
static void write_cp_lmarkh(uint32_t addr, uint32_t data) { fifo.cp.lomarkh = data; }
static void read_cp_lmarkl(uint32_t addr, uint32_t *reg) { *reg = fifo.cp.lomarkl & 0xffe0; }
static void write_cp_lmarkl(uint32_t addr, uint32_t data) { fifo.cp.lomarkl = data & 0xffe0; }
static void read_cp_cnth(uint32_t addr, uint32_t *reg) { *reg = fifo.cp.cnth; }
static void write_cp_cnth(uint32_t addr, uint32_t data) { fifo.cp.cnth = data; }
static void read_cp_cntl(uint32_t addr, uint32_t *reg) { *reg = fifo.cp.cntl & 0xffe0; }
static void write_cp_cntl(uint32_t addr, uint32_t data) { fifo.cp.cntl = data & 0xffe0; }
static void read_cp_wrptrh(uint32_t addr, uint32_t *reg) { *reg = fifo.cp.wrptrh; }
static void write_cp_wrptrh(uint32_t addr, uint32_t data) { fifo.cp.wrptrh = data; }
static void read_cp_wrptrl(uint32_t addr, uint32_t *reg) { *reg = fifo.cp.wrptrl & 0xffe0; }
static void write_cp_wrptrl(uint32_t addr, uint32_t data) { fifo.cp.wrptrl = data & 0xffe0; }
static void read_cp_rdptrh(uint32_t addr, uint32_t *reg) { *reg = fifo.cp.rdptrh; }
static void write_cp_rdptrh(uint32_t addr, uint32_t data) { fifo.cp.rdptrh = data; }
static void read_cp_rdptrl(uint32_t addr, uint32_t *reg) { *reg = fifo.cp.rdptrl & 0xffe0; }
static void write_cp_rdptrl(uint32_t addr, uint32_t data) { fifo.cp.rdptrl = data & 0xffe0; }
static void read_cp_bpptrh(uint32_t addr, uint32_t *reg) { *reg = fifo.cp.bpptrh; }
static void write_cp_bpptrh(uint32_t addr, uint32_t data) { fifo.cp.bpptrh = data; }
static void read_cp_bpptrl(uint32_t addr, uint32_t *reg) { *reg = fifo.cp.bpptrl & 0xffe0; }
static void write_cp_bpptrl(uint32_t addr, uint32_t data) { fifo.cp.bpptrl = data & 0xffe0; }
//
// stubs
//
static void no_write(uint32_t addr, uint32_t data) {}
static void no_read(uint32_t addr, uint32_t *reg) { *reg = 0; }
// ---------------------------------------------------------------------------
// init
void CPOpen(HWConfig * config)
{
Report(Channel::CP, "Command processor (for GX)\n");
// clear registers
memset(&fifo, 0, sizeof(FifoControl));
fifo.log = false;
// command processor
MISetTrap(16, CP_SR , read_cp_sr, NULL);
MISetTrap(16, CP_CR , read_cp_cr, write_cp_cr);
MISetTrap(16, CP_CLR , NULL, write_cp_clr);
MISetTrap(16, CP_TEX , no_read, no_write);
MISetTrap(16, CP_BASE , read_cp_basel, write_cp_basel);
MISetTrap(16, CP_BASE + 2 , read_cp_baseh, write_cp_baseh);
MISetTrap(16, CP_TOP , read_cp_topl, write_cp_topl);
MISetTrap(16, CP_TOP + 2 , read_cp_toph, write_cp_toph);
MISetTrap(16, CP_HIWMARK , read_cp_hmarkl, write_cp_hmarkl);
MISetTrap(16, CP_HIWMARK + 2, read_cp_hmarkh, write_cp_hmarkh);
MISetTrap(16, CP_LOWMARK , read_cp_lmarkl, write_cp_lmarkl);
MISetTrap(16, CP_LOWMARK + 2, read_cp_lmarkh, write_cp_lmarkh);
MISetTrap(16, CP_CNT , read_cp_cntl, write_cp_cntl);
MISetTrap(16, CP_CNT + 2 , read_cp_cnth, write_cp_cnth);
MISetTrap(16, CP_WRPTR , read_cp_wrptrl, write_cp_wrptrl);
MISetTrap(16, CP_WRPTR + 2 , read_cp_wrptrh, write_cp_wrptrh);
MISetTrap(16, CP_RDPTR , read_cp_rdptrl, write_cp_rdptrl);
MISetTrap(16, CP_RDPTR + 2 , read_cp_rdptrh, write_cp_rdptrh);
MISetTrap(16, CP_BPPTR , read_cp_bpptrl, write_cp_bpptrl);
MISetTrap(16, CP_BPPTR + 2 , read_cp_bpptrh, write_cp_bpptrh);
// I-Ninja. TODO: Research
MISetTrap(16, 0x0c00'0040, no_read, nullptr);
MISetTrap(16, 0x0c00'0042, no_read, nullptr);
MISetTrap(16, 0x0c00'0044, no_read, nullptr);
MISetTrap(16, 0x0c00'0046, no_read, nullptr);
MISetTrap(16, 0x0c00'0048, no_read, nullptr);
MISetTrap(16, 0x0c00'004a, no_read, nullptr);
MISetTrap(16, 0x0c00'004c, no_read, nullptr);
MISetTrap(16, 0x0c00'004e, no_read, nullptr);
// pixel engine
MISetTrap(16, PE_ZCR , no_read, no_write);
MISetTrap(16, PE_ACR , no_read, no_write);
MISetTrap(16, PE_ALPHA_DST , no_read, no_write);
MISetTrap(16, PE_ALPHA_MODE, no_read, no_write);
MISetTrap(16, PE_ALPHA_READ, no_read, no_write);
MISetTrap(16, PE_SR , read_pe_sr, write_pe_sr);
MISetTrap(16, PE_TOKEN , read_pe_token, NULL);
GXSetDrawCallbacks(CPDrawDoneCallback, CPDrawTokenCallback);
fifo.tickPerFifo = 100;
fifo.updateTbrValue = Gekko::Gekko->GetTicks() + fifo.tickPerFifo;
fifo.thread = new Thread(CPThread, false, nullptr, "CPThread");
}
void CPClose()
{
if (fifo.thread)
{
delete fifo.thread;
fifo.thread = nullptr;
}
}