mirror of
https://github.com/emu-russia/pureikyubu.git
synced 2025-04-02 10:42:15 -04:00
361 lines
12 KiB
C++
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;
|
|
}
|
|
}
|