From 47bdc58c1a30c22cf322b42e68fcf71687dd54ec Mon Sep 17 00:00:00 2001 From: kozarovv <15552250+kozarovv@users.noreply.github.com> Date: Tue, 27 Jul 2021 00:02:07 +0200 Subject: [PATCH] PGIF: Code refactoring/cleanup. Improve few games like RE2, THPS2, Castlevania SOTN, FF8. --- pcsx2/HwRead.cpp | 3 +- pcsx2/ps2/pgif.cpp | 1448 +++++++++++++++++--------------------------- pcsx2/ps2/pgif.h | 268 +++++++- 3 files changed, 830 insertions(+), 889 deletions(-) diff --git a/pcsx2/HwRead.cpp b/pcsx2/HwRead.cpp index d8dcfddffb..d6288e37df 100644 --- a/pcsx2/HwRead.cpp +++ b/pcsx2/HwRead.cpp @@ -86,7 +86,8 @@ mem32_t __fastcall _hwRead32(u32 mem) if (mem == INTC_STAT) { - if (intcstathack) IntCHackCheck(); + // Disable INTC hack when in PS1 mode as it seems to break games. + if (intcstathack && !(psxHu32(HW_ICFG) & (1 << 3))) IntCHackCheck(); return psHu32(INTC_STAT); } diff --git a/pcsx2/ps2/pgif.cpp b/pcsx2/ps2/pgif.cpp index 68d5063c04..b49dce7d4c 100644 --- a/pcsx2/ps2/pgif.cpp +++ b/pcsx2/ps2/pgif.cpp @@ -1,5 +1,5 @@ /* PCSX2 - PS2 Emulator for PCs -* Copyright (C) 2016-2016 PCSX2 Dev Team +* Copyright (C) 2016-2021 PCSX2 Dev Team * Copyright (C) 2016 Wisi * * PCSX2 is free software: you can redistribute it and/or modify it under the terms @@ -19,59 +19,6 @@ #include "ps2/HwInternal.h" #include "ps2/pgif.h" -#include "ConsoleLogger.h" - -#include - -#define psxHu(mem) (*(u32 *)&iopHw[(mem)&0xffff]) - - -//using namespace Internal; - -int doAnyIopLs = 0; - - - -//Debug printf: -#define REG_LOG 0 - - - -//Constants here control code that is either not certainly correct or may affect compatibility: - -//Default=1. Enables changing the data FIFO/DMA direction bit in the Status register by the PGIF Ctrl register. -#define STAT_DIR_BY_IF_CTRL 1 -//Specifies which bits (29 and 30) from the "Set DMA direction" command to copy to the PGPU Status reg. -//Default(works better)=0x40000000, but makes more sense to be =0x20000000 or =0x60000000. -//Status reg. bits 30:29: DMA Direction (0=Off, 1=FIFO, 2=CPUtoGP0, 3=GPUREADtoCPU) ;GPUSTAT.29-30 -#define STAT_DIR_BY_CMD_BITS 0x40000000 -//#define STAT_DIR_BY_CMD_BITS 0x60000000 - -//Enables interrupt on the GP0(1Fh) - Interrupt Request (IRQ1) command. Default=0. -#define PGPU_IRQ1_ENABLE 0 -//Default=1. -#define PREVENT_IRQ_ON_NORM_DMA_TO_GPU 1 - -//When intercepted, GP1() commands are acted-upon and the their effects are emulated by this code, besides PS1DRV. -//This seems to be what the PGIF does, but disabling it (=0) doesn't break too much stuff either. -//Default=0. Do intercept when IOP code writes (sends) to FIFO the CMD. -#define CMD_INTERCEPT_AT_WR 0 -//Default=1. Do intercept when PS1DRV reads from FIFO the CMD. -#define CMD_INTERCEPT_AT_RD 1 - -//PGIF_DAT_RB_LEAVE_FREE - How many elements of the FIFO buffer to leave free in DMA. -//Can be 0 and no faults are observed. -//As the buffer has 32 elements, and normal DMA reads are usually done in 4 qwords (16 words), -//this must be less than 16, otherwise the PS1DRV will never read from the FIFO. -//At one point (in Linked-List DMA), PS1DRV will expect at least a certain number of elements, that is sent as argument to the func. -#define PGIF_DAT_RB_LEAVE_FREE 1 - -//Default=0. If =1, additional modification to the Status register (0x1F801814) is not done by the PGIF emulator. -//It is unknown if the PGIF emulates it, or all that's necessary is done by PS1DRV, which should have(?) complete write control over it. -#define SKIP_UPD_PGPU_STAT 0 - - - //NOTES (TODO): /* - 8 and 16 bit access to the PGPU regs is not emulated... is it ever used? Emulating it would be tricky. @@ -86,175 +33,93 @@ All the PS1 GPU info comes from psx-spx: http://problemkaputt.de/psx-spx.htm */ -//Registers 0x00 - 0x40 are similar, in that they all serve as immediate-response registers to commands GP0(E1) - GP0(E5). -//PGPU_STAT 0x1000F300 The GP1 - Status register, whgich PS1DRV writes (emulates) for the IOP to read. -#define PGPU_STAT 0x1000F300 -//PGIF_1-PGIF_4 - "immediate response registers" - hold the return values for commands that require immediate response. -//They correspond to GP0() E2-E5 commands. -#define PGIF_1 0x1000F310 -#define PGIF_2 0x1000F320 -#define PGIF_3 0x1000F330 -#define PGIF_4 0x1000F340 -//PGIF_CTRL 0x1000F380 Main register for PGIF status info & control. -#define PGIF_CTRL 0x1000F380 -//PGPU_CMD_FIFO FIFO buffer for GPU GP1 (CMD reg) CMDs IOP->EE only (unknown if reverse direction is possible). -#define PGPU_CMD_FIFO 0x1000F3C0 -//PGPU_DAT_FIFO FIFO buffer for GPU GP0 (DATA reg) IOP->EE, but also EE->IOP. Direction is controlled by reg. 0x80/bit4 (most likely). -//Official name is "GFIFO", according to PS1DRV. -#define PGPU_DAT_FIFO 0x1000F3E0 -//Registers 0x50 - 0x70, 0x90 - 0xB0, 0xD0, 0xF0 are not known to be used for anything. -/*the following three are from the homebrew PS2SDK, but their names are somewhat misleading: -#define PGIF_GPU_STAT (0x1000F300) -#define PGIF_CFIFO_STAT (0x1000F380) -#define PGIF_CFIFO_DATA (0x1000F3C0)*/ +//Debug printf: +// Set to 1 to log PGIF HW IO +#define LOG_REG 0 -//Bit-fields definitions for the PGPU Status register: -#define PGPU_STAT_IRQ1 0x01000000 - -//Specifies the bits in the PGIF Control (0xf380) register that are not writable from EE side, by the PS1DRV. -#define PGIF_CTRL_RO_MASK 0x00000000 - - -#define PGPU_DMA_MADR 0x1F8010A0 -#define PGPU_DMA_BCR 0x1F8010A4 -#define PGPU_DMA_CHCR 0x1F8010A8 -//is there a tadr? -#define PGPU_DMA_TADR 0x1F8010AC - -#define DMA_START_BUSY 0x01000000 -#define DMA_TRIGGER 0x10000000 -//write to peripheral -#define DMA_DIR_FROM_RAM 0x1 -#define DMA_LINKED_LIST 0x00000400 -#define DMA_LL_END_CODE 0x00FFFFFF - - - -//Local variables for emulation: - -static u32 PGpuStatReg = 0; //Read-only to IOP -//Hold the values of the immediate-response registers. -u32 pgif1reg = 0; -u32 pgif2reg = 0; -u32 pgif3reg = 0; -u32 pgif4reg = 0; - -static u32 PGifCtrlReg = 0; //PGifIfStat -static u32 PGifCtrlRegOld = 0; -static u32 PGpuDataReg = 0; //Read-only to IOP - -u32 lastGpuCmd = 0; -u32 lastGpuData = 0; - -//int clrBufCnt = 0; - - -#if 0 -u32 pgpuDmaMadr = 0; -u32 pgpuDmaBcr = 0; -u32 pgpuDmaChcr = 0; -u32 pgpuDmaTadr = 0; +#if LOG_REG + #define REG_LOG pgifConLog #else -#define pgpuDmaMadr HW_DMA2_MADR -#define pgpuDmaBcr HW_DMA2_BCR -#define pgpuDmaChcr HW_DMA2_CHCR -//u32 pgpuDmaChcr = 0; -#define pgpuDmaTadr HW_DMA2_TADR + #define REG_LOG(...) do {} while(0) #endif -//Internal flags: -int PgpuDmaLlAct = 0; -int PgpuDmaNrActToGpu = 0; -int PgpuDmaNrActToIop = 0; - -u32 pgpuDmaTrAddr = 0; -u32 wCnt = 0; // -u32 wordNr = 0; //total number of words -u32 nextAddr = 0; -u32 dntPrt = 0; - -u32 nrWordsN = 0; //total number of words in Normal DMA -u32 crWordN = 0; //current word number in Normal DMA -u32 addrNdma = 0; - +// Set to 1 to log PGPU DMA +#define LOG_PGPU_DMA 1 +#if LOG_PGPU_DMA + #define PGPU_DMA_LOG pgifConLog +#else + #define PGPU_DMA_LOG(...) do {} while(0) +#endif +u32 old_gp0_value = 0; void fillFifoOnDrain(void); - void drainPgpuDmaLl(void); void drainPgpuDmaNrToGpu(void); void drainPgpuDmaNrToIop(void); -//u32 getUpdPgifCtrlReg(void); - - - -//Generic FIFO-related: -struct ringBuf_t +void ringBufPut(struct ringBuf_t* rb, u32* data) { - u32 *buf; - int size; - int count; - int head; - int tail; -}; - -void ringBufPut(struct ringBuf_t *rb, u32 *data) -{ - if (rb->count < rb->size) { //there is available space - *(rb->buf + rb->head) = *data; - if ((++(rb->head)) >= rb->size) - rb->head = 0; //wrap back when the end is reached - rb->count++; - } else { - // This should never happen. If it does, the code is bad somewhere. - pgifConLog("############################# PGIF FIFO overflow! sz= %X", rb->size); - } + if (rb->count < rb->size) + { + //there is available space + *(rb->buf + rb->head) = *data; + if ((++(rb->head)) >= rb->size) + rb->head = 0; //wrap back when the end is reached + rb->count++; + } + else + { + // This should never happen. If it does, the code is bad somewhere. + Console.Error("PGIF FIFO overflow! sz= %X", rb->size); + } } -void ringBufGet(struct ringBuf_t *rb, u32 *data) +void ringBufGet(struct ringBuf_t* rb, u32* data) { - if (rb->count > 0) { //there is available data - *data = *(rb->buf + rb->tail); - if ((++(rb->tail)) >= rb->size) - rb->tail = 0; //wrap back when the end is reached - rb->count--; - } else { - // This should never happen. If it does, the code is bad somewhere. - pgifConLog("############################# PGIF FIFO overflow! sz= %X", rb->size); - } + if (rb->count > 0) + { + //there is available data + *data = *(rb->buf + rb->tail); + if ((++(rb->tail)) >= rb->size) + rb->tail = 0; //wrap back when the end is reached + rb->count--; + } + else + { + // This should never happen. If it does, the code is bad somewhere. + Console.Error("PGIF FIFO underflow! sz= %X", rb->size); + } } -void ringBufClear(struct ringBuf_t *rb) +void ringBufferClear(struct ringBuf_t* rb) { - rb->head = 0; - rb->tail = 0; - rb->count = 0; - // wisi comment: + rb->head = 0; + rb->tail = 0; + rb->count = 0; + // wisi comment: // Yes, the memset should be commented-out. The reason is that it shouldn't really be necessary (but I am not sure). // It is better not to be enabled, because clearing the new huge PGIF FIFO, can waste significant time. //memset(rb->buf, 0, rb->size * sizeof(u32)); - return; + return; } //Ring buffers definition and initialization: //Command (GP1) FIFO, size= 0x8 words: -#define PGIF_CMD_RB_SZ 0x8 -struct ringBuf_t pgifCmdRbC; //Ring buffer control variables. -u32 pgiffCmdRbD[PGIF_CMD_RB_SZ] = {0}; //Ring buffer data. +#define PGIF_CMD_RB_SIZE 0x8 +struct ringBuf_t rb_gp1; //Ring buffer control variables. +u32 pgif_gp1_buffer[PGIF_CMD_RB_SIZE] = {0}; //Ring buffer data. //Data (GP0) FIFO - the so-called (in PS1DRV) "GFIFO", (real) size= 0x20 words: -//#define PGIF_DAT_RB_SZ 0x20 -//FIXED: Values 0xD54 and higher cause PS logo to disappair. //Using small (the real) FIFO size, disturbs MDEC video (and other stuff), //because the MDEC does DMA instantly, while this emulation drains the FIFO, //only when the PS1DRV gets data from it, which depends on IOP-EE sync, among other things. //The reson it works in the real hardware, is that the MDEC DMA would run in the pauses of GPU DMA, //thus the GPU DMA would never get data, MDEC hasn't yet written to RAM yet (too early). -#define PGIF_DAT_RB_SZ 0x20000 -struct ringBuf_t pgifDatRbC; //Ring buffer control variables. -u32 pgiffDatRbD[PGIF_DAT_RB_SZ] = {0}; //Ring buffer data. +#define PGIF_DAT_RB_SIZE 0x20000 +struct ringBuf_t rb_gp0; //Ring buffer control variables. +u32 pgif_gp0_buffer[PGIF_DAT_RB_SIZE] = {0}; //Ring buffer data. @@ -262,807 +127,634 @@ u32 pgiffDatRbD[PGIF_DAT_RB_SZ] = {0}; //Ring buffer data. //given that the PGIF is in the EE ASIC, on the other side of the SBUS. void pgifInit() { - pgifCmdRbC.buf = pgiffCmdRbD; - pgifCmdRbC.size = PGIF_CMD_RB_SZ; - ringBufClear(&pgifCmdRbC); - pgifDatRbC.buf = pgiffDatRbD; - pgifDatRbC.size = PGIF_DAT_RB_SZ; - ringBufClear(&pgifDatRbC); + rb_gp1.buf = pgif_gp1_buffer; + rb_gp1.size = PGIF_CMD_RB_SIZE; + ringBufferClear(&rb_gp1); - PGpuStatReg = 0; - pgif1reg = 0; - pgif2reg = 0; - pgif3reg = 0; - pgif4reg = 0; - PGifCtrlReg = 0; - PGpuDataReg = 0; + rb_gp0.buf = pgif_gp0_buffer; + rb_gp0.size = PGIF_DAT_RB_SIZE; + ringBufferClear(&rb_gp0); + + pgpu.stat.write(0); + pgif.ctrl.write(0); + old_gp0_value = 0; - pgpuDmaMadr = 0; - pgpuDmaBcr = 0; - pgpuDmaChcr = 0; - pgpuDmaTadr = 0; + dmaRegs.madr.address = 0; + dmaRegs.bcr.write(0); + dmaRegs.chcr.write(0); + //pgpuDmaTadr = 0; - PgpuDmaLlAct = 0; - PgpuDmaNrActToGpu = 0; - PgpuDmaNrActToIop = 0; + dma.state.ll_active = 0; + dma.state.to_gpu_active = 0; + dma.state.to_iop_active = 0; - pgpuDmaTrAddr = 0; - wCnt = 0; // - wordNr = 0; //total number of words - nextAddr = 0; + dma.ll_dma.data_read_address = 0; + dma.ll_dma.current_word = 0; + dma.ll_dma.total_words = 0; + dma.ll_dma.next_address = 0; - nrWordsN = 0; //total number of words in Normal DMA - crWordN = 0; //current word number in Normal DMA - addrNdma = 0; - - return; + dma.normal.total_words = 0; + dma.normal.current_word = 0; + dma.normal.address = 0; } -//Basically resetting the PS1 GPU. -void pgifReset() -{ - //pgifInit(); - //return; - - ringBufClear(&pgifDatRbC); - ringBufClear(&pgifCmdRbC); - - PGpuStatReg = 0; - pgif1reg = 0; - pgif2reg = 0; - pgif3reg = 0; - pgif4reg = 0; - PGifCtrlReg = 0; - PGpuDataReg = 0; - - //pgpuDmaMadr = 0; - //pgpuDmaBcr = 0; - //pgpuDmaChcr = 0; - //pgpuDmaTadr = 0; - - //PgpuDmaLlAct = 0; - //PgpuDmaNrActToGpu = 0; - //PgpuDmaNrActToIop = 0; - - //pgpuDmaTrAddr = 0; - //wCnt = 0; // - //wordNr = 0; //total number of words - //nextAddr = 0; - - //nrWordsN = 0; //total number of words in Normal DMA - //crWordN = 0; //current word number in Normal DMA - //addrNdma = 0; - - return; -} - -//------ - //Interrupt-related (IOP, EE and DMA): void triggerPgifInt(int subCause) -{ //For the PGIF on EE side. - pgifConLog("TRIGGERRING PGIF IRQ! %08X %08X ", psHu32(INTC_STAT), psHu32(INTC_MASK)); - //Left-overs from tests: - //iopCycleEE = -1; //0000; - //iopBreak = 0; - //--cpuTestINTCInts(); - //--cpuSetEvent(); - hwIntcIrq(15); - cpuSetEvent(); - return; +{ + // Probably we should try to mess with delta here. + hwIntcIrq(15); + cpuSetEvent(); } void getIrqCmd(u32 data) -{ //For the IOP GPU. This is triggered by the GP0(1Fh) - Interrupt Request (IRQ1) command. -//This may break stuff, because it doesn't detect whether this is really a GP0() command or data... -//WARNIG: Consider disabling this. -#if PGPU_IRQ1_ENABLE == 1 - if ((data & 0xFF000000) == 0x1F000000) { - iopIntcIrq(1); - PGpuStatReg |= PGPU_STAT_IRQ1; - } -#endif +{ + //For the IOP - GPU. This is triggered by the GP0(1Fh) - Interrupt Request (IRQ1) command. + //This may break stuff, because it doesn't detect whether this is really a GP0() command or data... + //Since PS1 HW also didn't recognize that is data or not, we should left it enabled. + { + if ((data & 0xFF000000) == 0x1F000000) + { + pgpu.stat.bits.IRQ1 = 1; + iopIntcIrq(1); + } + } } -void ackGpuIrq() -{ //Acknowledge for the IOP GPU interrupt. - //This is a "null"-function in PS1DRV... maybe because hardware takes care of it... but does any software use it? - PGpuStatReg &= ~PGPU_STAT_IRQ1; +void ackGpuIrq1() +{ + //Acknowledge for the IOP GPU interrupt. + pgpu.stat.bits.IRQ1 = 0; } void pgpuDmaIntr(int trigDma) -{ //For the IOP GPU DMA channel. -//trigDma: 1=normal,ToGPU; 2=normal,FromGPU, 3=LinkedList +{ + //For the IOP GPU DMA channel. + //trigDma: 1=normal,ToGPU; 2=normal,FromGPU, 3=LinkedList -// psxmode: 25.09.2016 at this point, the emulator works even when removing this interrupt call. how? why? + // psxmode: 25.09.2016 at this point, the emulator works even when removing this interrupt call. how? why? #if PREVENT_IRQ_ON_NORM_DMA_TO_GPU == 1 - if (trigDma != 1) //Interrupt on ToGPU DMA breaks some games. TODO: Why? + if (trigDma != 1) //Interrupt on ToGPU DMA breaks some games. TODO: Why? #endif - psxDmaInterrupt(2); + psxDmaInterrupt(2); } //Pass-through & intercepting functions: u32 immRespHndl(u32 cmd, u32 data) -{ //Handles the GP1(10h) command, that requires immediate responce. - //The data argument is the old data of the register (shouldn't be critical what it contains). - //Descriptions here are taken directly from psx-spx. - switch ((cmd & 0xFF)) { - case 0x00: - case 0x01: - break; //Returns Nothing (old value in GPUREAD remains unchanged) - case 0x02: - data = pgif1reg & 0x000FFFFF; - break; //Read Texture Window setting ;GP0(E2h) ;20bit/MSBs=Nothing - case 0x03: - data = pgif2reg & 0x000FFFFF; - break; //Read Draw area top left ;GP0(E3h) ;20bit/MSBs=Nothing - case 0x04: - data = pgif3reg & 0x000FFFFF; - break; //Read Draw area bottom right ;GP0(E4h) ;20bit/MSBs=Nothing - case 0x05: - data = pgif4reg & 0x003FFFFF; - break; //Read Draw offset ;GP0(E5h) ;22bit - } - return data; +{ + //Handles the GP1(10h) command, that requires immediate response. + //The data argument is the old data of the register (shouldn't be critical what it contains). + switch ((cmd & 0x7)) + { + case 0: + case 1: + case 6: + case 7: + break; //Returns Nothing (old value in GPUREAD remains unchanged) + case 2: + data = pgif.imm_response.reg.e2 & 0x000FFFFF; + break; //Read Texture Window setting ;GP0(E2h) ;20bit/MSBs=Nothing + case 3: + data = pgif.imm_response.reg.e3 & 0x0007FFFF; + break; //Read Draw area top left ;GP0(E3h) ;19bit/MSBs=Nothing + case 4: + data = pgif.imm_response.reg.e4 & 0x0007FFFF; + break; //Read Draw area bottom right ;GP0(E4h) ;19bit/MSBs=Nothing + case 5: + data = pgif.imm_response.reg.e5 & 0x003FFFFF; + break; //Read Draw offset ;GP0(E5h) ;22bit + } + return data; } -void ckhCmdSetPgif(u32 cmd) -{ //Check GP1() command and configure PGIF accordingly. - //Maybe all below are hanled by PS1DRV and this functions is only wasting time and memory and cusing errors...but that doesn't seem to be the case. - u32 cmdNr = ((cmd >> 24) & 0xFF) & 0x3F; //Higher commands are known to mirror lower commands. - u32 data = 0x00000000; - switch (cmdNr) { - case 0x00: - ringBufClear(&pgifDatRbC); - // ringBufClear(&pgifCmdRbC); //Makes more sense to leave it disabled - if the PS1 program has written a reset command, it would be the last in the FIFO anyway. - ackGpuIrq(); - // pgpu1reg = 0; pgpu2reg = 0; pgpu3reg = 0; pgpu4reg = 0; - //More stuff to clear here.... - pgifReset(); - ringBufPut(&pgifDatRbC, &data); //write a reset command for the PS1DRV to get. - break; - case 0x01: //Reset Command Buffer (only?)... This would actually mean the Data (GP0()) register buffer. - //ringBufClear(&pgifCmdRbC); //as above. - ringBufClear(&pgifDatRbC); - break; - case 0x02: //Acknowledge GPU IRQ - ackGpuIrq(); - break; - case 0x04: //DMA Direction / Data Request - //The PS1DRV doesn't seem to take care of this... - //Removing the following two, "damages" the PS logo, so they are probably necessary. - //The PS1 program will not start DMA GPU->IOP (only) if this is missing. - PGpuStatReg &= ~STAT_DIR_BY_CMD_BITS; //TODO: Which bits should be controlled here? - PGpuStatReg |= (cmd << 29) & STAT_DIR_BY_CMD_BITS; //0x60000000; //0x20000000 bit 30 is handled by PS1DRV - //PGpuStatReg &= ~0x60000000; //TODO: Which bits should be controlled here? - //PGpuStatReg |= (cmd <<29) & 0x40000000; //0x60000000; //0x20000000 bit 30 is handled by PS1DRV - //0-1 DMA Direction (0=Off, 1=FIFO, 2=CPUtoGP0, 3=GPUREADtoCPU) ;GPUSTAT.29-30 - //2-23 Not used (zero) - drainPgpuDmaLl(); //See comment in this function. - break; - } +void handleGp1Command(u32 cmd) +{ + //Check GP1() command and configure PGIF accordingly. + //Commands 0x00 - 0x01 are partially handled in ps1drv, we should just clear fifo. + u32 cmdNr = ((cmd >> 24) & 0xFF) & 0x3F; + u32 param = 0; + switch (cmdNr) + { + case 0: + // Pgpu reset, mostly handled by ps1drv (00201D64 in ps1drv). Comment for case 1 apply here too. + ringBufferClear(&rb_gp0); + break; + case 1: //Reset GP0 fifo, seems to check that something is empty, so maybe we should do the same before clear? + ringBufferClear(&rb_gp0); + break; + case 2: //Acknowledge GPU IRQ + ackGpuIrq1(); + break; + case 4: //DMA Direction / Data Request. The PS1DRV doesn't care of this... Should we do something on pgif ctrl? + pgpu.stat.bits.DDIR = cmd & 0x3; + //Since DREQ bit is dependent on DDIR bits, we should set it as soon as command is processed. + switch (pgpu.stat.bits.DDIR) //29-30 bit (0=Off, 1=FIFO, 2=CPUtoGP0, 3=GPUREADtoCPU) ;GP1(04h).0-1 + { + case 0x00: // When GP1(04h)=0 ---> Always zero (0) + pgpu.stat.bits.DREQ = 0; + break; + case 0x01: // When GP1(04h)=1 ---> FIFO State (0=Full, 1=Not Full) + if (rb_gp0.count < (rb_gp0.size - PGIF_DAT_RB_LEAVE_FREE)) + { + pgpu.stat.bits.DREQ = 1; + } + else + { + pgpu.stat.bits.DREQ = 0; + } + break; + case 0x02: // When GP1(04h)=2 ---> Same as GPUSTAT.28 + pgpu.stat.bits.DREQ = pgpu.stat.bits.RDMA; + drainPgpuDmaLl(); //See comment in this function. + break; + case 0x03: //When GP1(04h)=3 ---> Same as GPUSTAT.27 + pgpu.stat.bits.DREQ = pgpu.stat.bits.RSEND; + break; + } + break; + } } u32 getUpdPgpuStatReg() { -#if SKIP_UPD_PGPU_STAT == 1 - return PGpuStatReg; -#endif - //The code below is not necessary! ... but not always...why? - //Does PS1DRV does set bit 27 - reverse direction (VRAM->CPU) in some conditions... but not always...TODO: Find why. - //The PS1 program pools this bit to determine if there is data in the FIFO, it can get. Then starts DMA to get it. - //The PS1 program will not send the DMA direction command (GP1(04h)) and will not start DMA until this bit (27) becomes set. - //Maybe move this to the PGifCtrlReg write handler...? - PGpuStatReg &= ~0x08000000; //VRAM->CPU data ready - PGpuStatReg &= ~0x02000000; //common "ready" flag - //PGpuStatReg |= 0x10000000; //GPU is ready to receive DMA data. PS1DRV seems to set and clear this bit, - //but it is cleared in the next code anyway, because it seems safer. - //PGpuStatReg |= 0x04000000; //GPU is ready to receive GP0() cmds. - //Setting this bit is best left to the PS1DRV (PS logo polygons start missing otherwise). -#if GPU_STAT_DIR_BY_IF_CTRL == 0 - if ((PGifCtrlReg & 0x10) || (PGpuStatReg & 0x20000000)) { //Reverse direction bit GPU->CPU. -#else - PGpuStatReg &= ~0x20000000; - if (PGifCtrlReg & 0x10) { - PGpuStatReg |= 0x20000000; -#endif - //Or maybe bit 29 of the GPU STATUS reg, should copy the state of bit 4 of the PGIF_CTRL reg? - PGpuStatReg &= ~0x04000000; //Same with receiving GP0() commands. - if (pgifDatRbC.count > 0) //Data FIFO has data. - PGpuStatReg |= 0x08000000; //Could skip the second check, but that could cause buffer underflow on direct read. - //Should the above be set only when there are at least 16 words of data? - } + //PS1DRV does set bit RSEND on (probably - took from command print) GP0(C0h), should we do something? + //The PS1 program pools this bit to determine if there is data in the FIFO, it can get. Then starts DMA to get it. - switch ((PGpuStatReg >> 29) & 0x3) { - case 0x00: - break; // When GP1(04h)=0 ---> Always zero (0) - case 0x01: // When GP1(04h)=1 ---> FIFO State (0=Full, 1=Not Full) - if (pgifDatRbC.count < (pgifDatRbC.size - 0)) - PGpuStatReg |= 0x02000000; //Not (yet) full, so set the bit. - break; - case 0x02: // When GP1(04h)=2 ---> Same as GPUSTAT.28 - if (PGpuStatReg & 0x10000000) //Ready to receive DMA Block (0=No, 1=Ready) ;GP0(...) ;via GP0 - PGpuStatReg |= 0x02000000; - break; - case 0x03: //When GP1(04h)=3 ---> Same as GPUSTAT.27 - if (PGpuStatReg & 0x08000000) //Ready to send VRAM to CPU (0=No, 1=Ready) ;GP0(C0h) ;via GPUREAD - PGpuStatReg |= 0x02000000; - break; - } - return PGpuStatReg; + //The PS1 program will not send the DMA direction command (GP1(04h)) and will not start DMA until this bit (27) becomes set. + pgpu.stat.bits.RSEND = pgif.ctrl.bits.data_from_gpu_ready; + return pgpu.stat.get(); } -//This returns "correct" element-in-FIFO count, even if extremely large buffer is used. -int getDatRbC_Count() +u8 getGP0RbC_Count() { - return std::min(pgifDatRbC.count, 0x1F); -} - -void setPgifCtrlReg(u32 data) -{ - PGifCtrlReg = (data & (~PGIF_CTRL_RO_MASK)) | (PGifCtrlReg & PGIF_CTRL_RO_MASK); + //Returns "correct" element-in-FIFO count, even if extremely large buffer is used. + return std::min(rb_gp0.count, 0x1F); } u32 getUpdPgifCtrlReg() { - PGifCtrlReg &= 0xFFFFE0FF; - if (pgifCmdRbC.count == 0) //fake that DATA FIFO is empty, so that PS1DRV processes any commands on the GP1() register first. - PGifCtrlReg |= ((getDatRbC_Count() & 0x1F) << 8) & 0x1F00; - PGifCtrlReg &= 0xFFF8FFFF; - PGifCtrlReg |= ((pgifCmdRbC.count & 0x07) << 16) & 0x70000; - PGifCtrlReg &= 0x7FFFFFFF; //bit 31= (DMA?) transfer active. Has to be clear, for PS1DRV to continue. - - //The above is most likely the 9-th bit of the FIFO counter. It is 0x20 in size most likly... though it could be 0x2F. - //It doesn't look as it is (ever) used as an overflow flag. It is usually added to the main part of the counter as a 9-th bit - //and then comaprisons to some number of elements are made. - //Always fill FIFO (for drain by PS1DRV) at least 0x10 words - it will refuse to drain it. - if (pgifCmdRbC.count == 0) //fake that DATA FIFO is empty, so that PS1DRV processes any commands on the GP1() register first. - PGifCtrlReg |= ((getDatRbC_Count() & 0x20) << 26) & 0x80000000; - //if ((PgpuDmaLlAct == 1) || (PgpuDmaNrActToGpu == 1)) - // PGifCtrlReg |= 0x80000000; - return PGifCtrlReg; + //Update fifo counts before returning register value + pgif.ctrl.bits.GP0_fifo_count = getGP0RbC_Count(); + pgif.ctrl.bits.GP1_fifo_count = rb_gp1.count; + return pgif.ctrl.get(); } -void cmdRingBufGet(u32 *data) -{ //Used by PGIF/PS1DRV. - ringBufGet(&pgifCmdRbC, data); -#if CMD_INTERCEPT_AT_RD == 1 - ckhCmdSetPgif(*data); //Let's assume that the PGIF hardware processes GPU commands just when the EE reads them from its FIFO. -#endif -} - -void datRingBufGet(u32 *data) +void rb_gp1_Get(u32* data) { - if (pgifDatRbC.count >= (PGIF_DAT_RB_SZ -1)) - { - pgifConLog( "PGIF DAT BUF FULL %08X ", pgifDatRbC.count); - } - if (pgifDatRbC.count > 0) { - ringBufGet(&pgifDatRbC, data); - getIrqCmd(*data); //Checks if an IRQ CMD passes through here a triggers IRQ when it does. - } else { //Return last value in register: -#if 0 - if ((((lastGpuCmd >>24) & 0xFF) & 0x30) == 0x10) { //Handle the immediate-response command. - //Cmds 0x10 - 0x1F are all 0x10, and 0x40-0xFF mirror 0x00-0x3F. - PGpuDataReg = immRespHndl(lastGpuCmd, PGpuDataReg); //Would it be better if this is done when data is read at datRingBufGet()? - } -#endif - *data = PGpuDataReg; - } + ringBufGet(&rb_gp1, data); + handleGp1Command(*data); //Setup GP1 right after reading command from FIFO. } - +void rb_gp0_Get(u32* data) +{ + if (rb_gp0.count > 0) + { + ringBufGet(&rb_gp0, data); + getIrqCmd(*data); //Checks if an IRQ CMD passes through here and triggers IRQ when it does. + } + else + { + *data = old_gp0_value; + } +} //PS1 GPU registers I/O handlers: void psxGPUw(int addr, u32 data) { -#if REG_LOG == 1 - pgifConLog("PGPU write 0x%08X = 0x%08X", addr, data); -#endif - - if (addr == HW_PS1_GPU_DATA) { - //PGpuDataReg = data; //just until it is certain that nothing uses the old value in the latch. - lastGpuData = data; - ringBufPut(&pgifDatRbC, &data); - } else if (addr == HW_PS1_GPU_STATUS) { //Command register GP1 (write-only). - lastGpuCmd = data; - if ((((data >> 24) & 0xFF) & 0x30) == 0x10) { //Handle the immediate-response command. - //Cmds 0x10 - 0x1F are all 0x10, and 0x40-0xFF mirror 0x00-0x3F. - PGpuDataReg = immRespHndl(lastGpuCmd, PGpuDataReg); //Would it be better if this is done when data is read at datRingBufGet()? - } else { //Should the "immediate-response" command not be sent to the GPU (PS1DRV)..? - triggerPgifInt(0); - ringBufPut(&pgifCmdRbC, &data); - } -#if CMD_INTERCEPT_AT_WR == 1 - ckhCmdSetPgif(data); -#endif - } + REG_LOG("PGPU write 0x%08X = 0x%08X", addr, data); + if (addr == HW_PS1_GPU_DATA) + { + ringBufPut(&rb_gp0, &data); + } + else if (addr == HW_PS1_GPU_STATUS) + { + // Check for Cmd 0x10-0x1F + u8 imm_check = (data >> 28); + imm_check &= 0x3; + if (imm_check == 1) + { + //Handle immediate-response command. Commands are NOT sent to the Fifo apparently (PS1DRV). + old_gp0_value = immRespHndl(data, old_gp0_value); + } + else + { + triggerPgifInt(0); + ringBufPut(&rb_gp1, &data); + } + } } u32 psxGPUr(int addr) { - u32 data = 0; - if (addr == HW_PS1_GPU_DATA) { - //data = PGpuDataReg; - //The following is commented-out, because it seems more logical to return GP1() CMD responces only when Data FIFO is empty. - //if ((lastGpuCmd & 0xFF000000) == 0x10000000) { - // data = immRespHndl(lastGpuCmd, PGpuDataReg); - // lastGpuCmd = 0; - //} else { - datRingBufGet(&data); - //} - } else if (addr == HW_PS1_GPU_STATUS) { - data = getUpdPgpuStatReg(); - } -#if REG_LOG == 1 - if (addr != HW_PS1_GPU_STATUS) //prints way too many times (pools) - pgifConLog("PGPU read 0x%08X = 0x%08X EEpc= %08X ", addr, data, cpuRegs.pc); -#endif - return data; + u32 data = 0; + if (addr == HW_PS1_GPU_DATA) + { + rb_gp0_Get(&data); + } + else if (addr == HW_PS1_GPU_STATUS) + { + data = getUpdPgpuStatReg(); + } + if (addr != HW_PS1_GPU_STATUS) + REG_LOG("PGPU read 0x%08X = 0x%08X", addr, data); + + return data; } -//PGIF registers I/O handlers: +// PGIF registers I/O handlers: void PGIFw(int addr, u32 data) { -#if REG_LOG == 1 - if (addr != PGIF_CTRL) - if ((addr != PGIF_CTRL) || ((addr == PGIF_CTRL) && (getUpdPgifCtrlReg() != data))) - if (addr != PGPU_STAT) - pgifConLog("PGIF write 0x%08X = 0x%08X 0x%08X EEpc= %08X IOPpc= %08X ", addr, data, getUpdPgifCtrlReg(), cpuRegs.pc, psxRegs.pc); -#endif + //if (((addr != PGIF_CTRL) || (addr != PGPU_STAT) || ((addr == PGIF_CTRL) && (getUpdPgifCtrlReg() != data))) && (addr != PGPU_STAT)) + REG_LOG("PGIF write 0x%08X = 0x%08X 0x%08X EEpc= %08X IOPpc= %08X ", addr, data, getUpdPgifCtrlReg(), cpuRegs.pc, psxRegs.pc); - if (addr == PGPU_CMD_FIFO) { - pgifConLog("PGIF CMD FIFO write by EE (SHOULDN'T HAPPEN) 0x%08X = 0x%08X", addr, data); - } else if (addr == PGPU_DAT_FIFO) { //reverse write - mind the direction set by reg f380 - //PGpuDataReg = data; - ringBufPut(&pgifDatRbC, &data); - // pgifConLog( "\n\r PGIF REVERSE !!! DATA write 0x%08X = 0x%08X IF_CTRL= %08X PGPU_STAT= %08X CmdCnt 0x%X \n\r", addr, data, getUpdPgifCtrlReg(), getUpdPgpuStatReg(), pgifCmdRbC.count); - drainPgpuDmaNrToIop(); - } else if (addr == PGPU_STAT) { - PGpuStatReg = data; //Should all bits be writable? - } else if (addr == PGIF_CTRL) { //PGIF config - setPgifCtrlReg(data); - PGifCtrlRegOld = PGifCtrlReg; //data - fillFifoOnDrain(); //Now this checks the 0x8 bit of the PGIF_CTRL reg, so it here too, - //so that it gets updated immediately once it is set. - } else if (addr == PGIF_1) { - pgif1reg = data; //GP0(E2h) - } else if (addr == PGIF_2) { - pgif2reg = data; - } else if (addr == PGIF_3) { - pgif3reg = data; - } else if (addr == PGIF_4) { - pgif4reg = data; - } + switch (addr) + { + case PGPU_STAT: + pgpu.stat.write(data); //Should all bits be writable? + break; + case PGIF_CTRL: + pgif.ctrl.write(data); + fillFifoOnDrain(); //Now this checks the 0x8 bit of the PGIF_CTRL reg, so it here too, + break; //so that it gets updated immediately once it is set. + case IMM_E2: + pgif.imm_response.reg.e2 = data; + break; + case IMM_E3: + pgif.imm_response.reg.e3 = data; + break; + case IMM_E4: + pgif.imm_response.reg.e4 = data; + break; + case IMM_E5: + pgif.imm_response.reg.e5 = data; + break; + case PGPU_CMD_FIFO: + Console.Error("PGIF CMD FIFO write by EE (SHOULDN'T HAPPEN) 0x%08X = 0x%08X", addr, data); + break; + case PGPU_DAT_FIFO: + ringBufPut(&rb_gp0, &data); + // Console.WriteLn( "\n\r PGIF REVERSE !!! DATA write 0x%08X = 0x%08X IF_CTRL= %08X PGPU_STAT= %08X CmdCnt 0x%X \n\r", addr, data, getUpdPgifCtrlReg(), getUpdPgpuStatReg(), rb_gp1.count); + drainPgpuDmaNrToIop(); + break; + default: + DevCon.Error("PGIF write to unknown location 0xx% , data: %x", addr, data); + break; + } } +// Read PGIF Hardware Registers. u32 PGIFr(int addr) { - u32 data = 0; - if (addr == PGPU_CMD_FIFO) { - cmdRingBufGet(&data); - } else if (addr == PGPU_DAT_FIFO) { - fillFifoOnDrain(); - datRingBufGet(&data); - } else if (addr == PGPU_STAT) { - data = PGpuStatReg; - } else if (addr == PGIF_CTRL) { //PGIF config - data = getUpdPgifCtrlReg(); - } else if (addr == PGIF_1) { - data = pgif1reg; //GP0(E2h) - } else if (addr == PGIF_2) { - data = pgif2reg; - } else if (addr == PGIF_3) { - data = pgif3reg; - } else if (addr == PGIF_4) { - data = pgif4reg; - } + u32 data = 0; + switch (addr) + { + case PGPU_STAT: + data = pgpu.stat.get(); + break; + case PGIF_CTRL: + data = getUpdPgifCtrlReg(); + break; + case IMM_E2: + data = pgif.imm_response.reg.e2; + break; + case IMM_E3: + data = pgif.imm_response.reg.e3; + break; + case IMM_E4: + data = pgif.imm_response.reg.e4; + break; + case IMM_E5: + data = pgif.imm_response.reg.e5; + break; + case PGPU_CMD_FIFO: + rb_gp1_Get(&data); + break; + case PGPU_DAT_FIFO: + fillFifoOnDrain(); + rb_gp0_Get(&data); + break; + default: + DevCon.Error("PGIF read from unknown location 0xx%", addr); + break; + } + //if (addr != PGPU_DAT_FIFO) + //if ((addr != PGIF_CTRL) || (getUpdPgifCtrlReg() != data)) + // if (addr != PGPU_STAT) + // REG_LOG("PGIF read %08X = %08X GPU_ST %08X IF_STAT %08X IOPpc %08X EEpc= %08X ", addr, data, getUpdPgpuStatReg(), getUpdPgifCtrlReg(), psxRegs.pc, cpuRegs.pc); -#if REG_LOG == 1 - if (addr != PGPU_DAT_FIFO) - if ((addr != PGIF_CTRL) || ((addr == PGIF_CTRL) && (getUpdPgifCtrlReg() != data))) - if (addr != PGPU_STAT) - pgifConLog("PGIF read %08X = %08X GPU_ST %08X IF_STAT %08X IOPpc %08X EEpc= %08X ", addr, data, getUpdPgpuStatReg(), getUpdPgifCtrlReg(), psxRegs.pc, cpuRegs.pc); -#endif - return data; + return data; } -void PGIFrQword(u32 addr, void *dat) +void PGIFrQword(u32 addr, void* dat) { - u32 *data = (u32 *)dat; + u32* data = (u32*)dat; - if (addr == PGPU_CMD_FIFO) { //shouldn't happen - pgifConLog("PGIF QW CMD read =ERR!"); - } else if (addr == PGPU_DAT_FIFO) { - fillFifoOnDrain(); - datRingBufGet(data + 0); - datRingBufGet(data + 1); - datRingBufGet(data + 2); - datRingBufGet(data + 3); + if (addr == PGPU_CMD_FIFO) + { + //shouldn't happen + Console.Error("PGIF QW CMD read =ERR!"); + } + else if (addr == PGPU_DAT_FIFO) + { + fillFifoOnDrain(); + rb_gp0_Get(data + 0); + rb_gp0_Get(data + 1); + rb_gp0_Get(data + 2); + rb_gp0_Get(data + 3); - fillFifoOnDrain(); - } else { - pgifConLog("PGIF QWord Read from add %08X ERR - shouldnt happen!", addr); - } -#if REG_LOG == 1 - if (addr != PGPU_DAT_FIFO) - pgifConLog("PGIF QW read 0x%08X = %08X %08X %08X %08X ", addr, *(u32 *)(data + 0), *(u32 *)(data + 1), *(u32 *)(data + 2), *(u32 *)(data + 3)); -#endif + fillFifoOnDrain(); + } + else + { + Console.WriteLn("PGIF QWord Read from address %08X ERR - shouldnt happen!", addr); + Console.WriteLn("Data = %08X %08X %08X %08X ", *(u32*)(data + 0), *(u32*)(data + 1), *(u32*)(data + 2), *(u32*)(data + 3)); + } } -void PGIFwQword(u32 addr, void *dat) +void PGIFwQword(u32 addr, void* dat) { - u32 *data = (u32 *)dat; - DevCon.Warning("WARNING PGIF WRITE BY PS1DRV ! - NOT KNOWN TO EVER BE DONE!"); - pgifConLog("PGIF QW write 0x%08X = 0x%08X %08X %08X %08X ", addr, *(u32 *)(data + 0), *(u32 *)(data + 1), *(u32 *)(data + 2), *(u32 *)(data + 3)); - if (addr == PGPU_CMD_FIFO) { //shouldn't happen - pgifConLog("PGIF QW CMD write =ERR!"); - } else if (addr == PGPU_DAT_FIFO) { - ringBufPut(&pgifDatRbC, (u32 *)(data + 0)); - ringBufPut(&pgifDatRbC, (u32 *)(data + 1)); - ringBufPut(&pgifDatRbC, (u32 *)(data + 2)); - ringBufPut(&pgifDatRbC, (u32 *)(data + 3)); - drainPgpuDmaNrToIop(); - } + u32* data = (u32*)dat; + DevCon.Warning("WARNING PGIF WRITE BY PS1DRV ! - NOT KNOWN TO EVER BE DONE!"); + Console.WriteLn("PGIF QW write 0x%08X = 0x%08X %08X %08X %08X ", addr, *(u32*)(data + 0), *(u32*)(data + 1), *(u32*)(data + 2), *(u32*)(data + 3)); + + if (addr == PGPU_CMD_FIFO) + { + Console.Error("PGIF QW CMD write!"); + } + else if (addr == PGPU_DAT_FIFO) + { + ringBufPut(&rb_gp0, (u32*)(data + 0)); + ringBufPut(&rb_gp0, (u32*)(data + 1)); + ringBufPut(&rb_gp0, (u32*)(data + 2)); + ringBufPut(&rb_gp0, (u32*)(data + 3)); + drainPgpuDmaNrToIop(); + } } - - //DMA-emulating functions: //This function is used as a global FIFO-DMA-fill function and both Linked-list normal DMA call it, -//regardless which DMA mode runs. void fillFifoOnDrain() { - if ((getUpdPgifCtrlReg() & 0x8) == 0) - return; //Skip filing FIFO with elements, if PS1DRV hasn't set this bit. - //Maybe it could be cleared once FIFO has data? - //This bit could be an enabler for the DREQ (EE->IOP for SIF2=PGIF) line. The FIFO is filled only when this is set - //and the IOP acknowledges it. - drainPgpuDmaLl(); - drainPgpuDmaNrToGpu(); - //This is done here in a loop, rather than recursively in each function, because a very large buffer causes stack oveflow. - while ((pgifDatRbC.count < ((pgifDatRbC.size) - PGIF_DAT_RB_LEAVE_FREE)) && ((PgpuDmaNrActToGpu != 0) || (PgpuDmaLlAct != 0))) { - drainPgpuDmaLl(); - drainPgpuDmaNrToGpu(); - } - //Flags' names: PgpuDmaLlAct is for linked-list, PgpuDmaNrActToGpu is for normal IOP->GPU - if ((PgpuDmaLlAct == 1) || (PgpuDmaNrActToGpu == 1)) - if (PgpuDmaNrActToIop != 1) - PGifCtrlReg &= ~0x8; - //Clear bit as DMA will be run - normally it should be cleared only once the current request finishes, but the IOP won't notice anything anyway. - //WARNING: Check if GPU->IOP DMA uses this flag, and if it does (it would make sense for it to use it as well), - //then only clear it here if the mode is not GPU->IOP. + //Skip filing FIFO with elements, if PS1DRV hasn't set this bit. + //Maybe it could be cleared once FIFO has data? + if (!pgif.ctrl.bits.fifo_GP0_ready_for_data) + return; + + + //This is done here in a loop, rather than recursively in each function, because a very large buffer causes stack oveflow. + while ((rb_gp0.count < ((rb_gp0.size) - PGIF_DAT_RB_LEAVE_FREE)) && ((dma.state.to_gpu_active) || (dma.state.ll_active))) + { + drainPgpuDmaLl(); + drainPgpuDmaNrToGpu(); + } + //Clear bit as DMA will be run - normally it should be cleared only once the current request finishes, but the IOP won't notice anything anyway. + //WARNING: Current implementation assume that GPU->IOP DMA uses this flag, so we only clear it here if the mode is not GPU->IOP. + if (((dma.state.ll_active) || (dma.state.to_gpu_active)) && (!dma.state.to_iop_active)) + { + pgif.ctrl.bits.fifo_GP0_ready_for_data = 0; + } } void drainPgpuDmaLl() { - u32 data = 0; - if (PgpuDmaLlAct == 0) - return; //process only on transfer active + u32 data = 0; + if (!dma.state.ll_active) + return; - //Some games (Breath of Fire 3 US) set-up linked-list DMA, but don't immediatelly have the list correctly set-up, - //so the result is that this function loops indefinitely, because of some links pointing back to themselves, forming a loop. - //The solution is to only start DMA once the GP1(04h) - DMA Direction / Data Request command has been set to the value 0x2 (CPU->GPU DMA) - //This func will be caled by this command as well. - //if ((PGpuStatReg & 0x60000000) != 0x40000000) return; + //Some games (Breath of Fire 3 US) set-up linked-list DMA, but don't immediatelly have the list correctly set-up, + //so the result is that this function loops indefinitely, because of some links pointing back to themselves, forming a loop. + //The solution is to only start DMA once the GP1(04h) - DMA Direction / Data Request command has been set to the value 0x2 (CPU->GPU DMA) - if (pgifDatRbC.count >= ((pgifDatRbC.size) - PGIF_DAT_RB_LEAVE_FREE)) - return; //buffer full - needs to be drained first. + //Buffer full - needs to be drained first. + if (rb_gp0.count >= ((rb_gp0.size) - PGIF_DAT_RB_LEAVE_FREE)) + return; - if ((nextAddr == 0x000C8000) || (nextAddr == 0x0) || (nextAddr == 0x3)) - dntPrt++; - // if (dntPrt <5) - //pgifConLog( "LLDMAFILL nxtAdr %08X trAddr %08X wordNr %08X wCnt %08X IF_CTRL= %08X ", nextAddr, pgpuDmaTrAddr, wordNr, wCnt, getUpdPgifCtrlReg(), cpuRegs.pc, psxRegs.pc); + if (dmaRegs.chcr.bits.MAS) + DevCon.Error("Unimplemented backward memory step on PGPU DMA Linked List"); - if (wCnt >= wordNr) { //Reached end of sequence, or the beginning of a new one - //if ((nextAddr == DMA_LL_END_CODE) || (nextAddr == 0x00000000)) { //complete - //why does BoF3 US get to a link pointing to address 0?? - if (nextAddr == DMA_LL_END_CODE) { - dntPrt = 0; - PgpuDmaLlAct = 0; - pgpuDmaMadr = 0x00FFFFFF; - //pgpuDmaMadr = nextAddr; //The END_CODE should go in the MADR reg. - //But instead copy the whole header code, each time one is encuntered - makes a bit more sense. - pgpuDmaChcr &= ~DMA_TRIGGER; //DMA is no longer active (in transfer) - pgpuDmaChcr &= ~DMA_START_BUSY; //Transfer completed => clear busy flag - pgpuDmaIntr(3); - pgifConLog("PGPU DMA LINKED LIST FINISHED "); - } else { - data = iopMemRead32(nextAddr); - // pgifConLog( "PGPU LL DMA HDR= %08X ", data); - //data = psxHu32(nextAddr); //The (next) current header word. - //pgifConLog( "PGPU LL DMA data= %08X ", data); - pgpuDmaMadr = data; //Copy the header word in MADR. - //It is unknown whether the whole word should go here, but because this is a "Memory ADdress Register", leave only the address. - pgpuDmaMadr &= 0x00FFFFFF; // correct or not? - pgpuDmaTrAddr = nextAddr + 4; //start of data section of packet - wCnt = 0; - wordNr = (data >> 24) & 0xFF; // Current length of packet and future address of header word. - nextAddr = data & 0x00FFFFFF; - } - } else { - //if (wCnt < wordNr) { - data = iopMemRead32(pgpuDmaTrAddr); //0; //psxHu32(pgpuDmaTrAddr); //Get the word of the packet from IOP RAM. -#if LOG_PGPU_DMA == 1 -// pgifConLog( "PGPU LL DMA data= %08X addr %08X ", data, pgpuDmaTrAddr); -#endif - ringBufPut(&pgifDatRbC, &data); - pgpuDmaTrAddr += 4; - wCnt++; - } + if (dma.ll_dma.current_word >= dma.ll_dma.total_words) + { + if (dma.ll_dma.next_address == DMA_LL_END_CODE) + { + //Reached end of linked list + dma.state.ll_active = 0; + dmaRegs.madr.address = 0x00FFFFFF; + dmaRegs.chcr.bits.BUSY = 0; //Transfer completed => clear busy flag + pgpuDmaIntr(3); + PGPU_DMA_LOG("PGPU DMA Linked List Finished"); + } + else + { + //Or the beginning of a new one + data = iopMemRead32(dma.ll_dma.next_address); + PGPU_DMA_LOG( "Next PGPU LL DMA header= %08X ", data); + dmaRegs.madr.address = data & 0x00FFFFFF; //Copy the address in MADR. + dma.ll_dma.data_read_address = dma.ll_dma.next_address + 4; //start of data section of packet + dma.ll_dma.current_word = 0; + dma.ll_dma.total_words = (data >> 24) & 0xFF; // Current length of packet and future address of header word. + dma.ll_dma.next_address = dmaRegs.madr.address; + } + } + else + { + //We are in the middle of linked list transfer + data = iopMemRead32(dma.ll_dma.data_read_address); + PGPU_DMA_LOG( "PGPU LL DMA data= %08X addr %08X ", data, dma.ll_dma.data_read_address); + ringBufPut(&rb_gp0, &data); + dma.ll_dma.data_read_address += 4; + dma.ll_dma.current_word++; + } } void drainPgpuDmaNrToGpu() { - u32 data = 0; + u32 data = 0; + if (!dma.state.to_gpu_active) + return; - if (PgpuDmaNrActToGpu == 0) - return; //process only on transfer active - if (pgifDatRbC.count >= ((pgifDatRbC.size) - PGIF_DAT_RB_LEAVE_FREE)) - return; //buffer full - needs to be drained first. + //Buffer full - needs to be drained first. + if (rb_gp0.count >= ((rb_gp0.size) - PGIF_DAT_RB_LEAVE_FREE)) + return; - if (crWordN < nrWordsN) { - data = iopMemRead32(addrNdma); //Get the word of the packet from IOP RAM. -// pgifConLog( "PGPU NRM DMA data= %08X addr %08X ", data, pgpuDmaTrAddr); - ringBufPut(&pgifDatRbC, &data); - addrNdma += 4; - crWordN++; - //The following two are common to all DMA channels, and in future, some global hanler may be used... or not. - //pgpuDmaMadr += 4; //It is unclear if this should be done exactly so... - pgpuDmaMadr = 4 + pgpuDmaMadr; - if ((crWordN % (pgpuDmaBcr & 0xFFFF)) == 0) //Because this is actually the "next word number", it can be compared to the size of the block. //End of block reached. - if (pgpuDmaBcr >= 0x10000) - pgpuDmaBcr = pgpuDmaBcr - 0x10000; //pgpuDmaBcr -= 0x10000; - //This is very inoptimized... - } - if (crWordN >= nrWordsN) { //Reached end of sequence = complete - PgpuDmaNrActToGpu = 0; - pgpuDmaChcr &= ~DMA_TRIGGER; //DMA is no longer active (in transfer) - pgpuDmaChcr &= ~DMA_START_BUSY; //Transfer completed => clear busy flag - pgpuDmaIntr(1); - pgifConLog("PGPU DMA Norm IOP->GPU FINISHED "); - } + if (dma.normal.current_word < dma.normal.total_words) + { + data = iopMemRead32(dma.normal.address); + PGPU_DMA_LOG( "To GPU Normal DMA data= %08X addr %08X ", data, dma.ll_dma.data_read_address); + + ringBufPut(&rb_gp0, &data); + if (dmaRegs.chcr.bits.MAS) + { + DevCon.Error("Unimplemented backward memory step on TO GPU DMA"); + } + dmaRegs.madr.address += 4; + dma.normal.address += 4; + dma.normal.current_word++; + + // decrease block amount only if full block size were drained. + if ((dma.normal.current_word % dmaRegs.bcr.bit.block_size) == 0) + dmaRegs.bcr.bit.block_amount -= 1; + } + if (dma.normal.current_word >= dma.normal.total_words) + { + //Reached end of sequence = complete + dma.state.to_gpu_active = 0; + dmaRegs.chcr.bits.BUSY = 0; + pgpuDmaIntr(1); + PGPU_DMA_LOG("To GPU DMA Normal FINISHED"); + } } void drainPgpuDmaNrToIop() { - u32 data; - if (PgpuDmaNrActToIop == 0) - return; - if (pgifDatRbC.count <= 0) - return; - /* if (crWordN >= nrWordsN) { - PgpuDmaNrActToIop = 0; - pgpuDmaChcr &= ~DMA_TRIGGER; //DMA is no longer active (in transfer) - pgpuDmaChcr &= ~DMA_START_BUSY; //Transfer completed => clear busy flag - psxDmaInterrupt(2); - } else { */ - if (crWordN < nrWordsN) { //This is not the best way, but... is there another? - ringBufGet(&pgifDatRbC, &data); - iopMemWrite32(addrNdma, data); - pgpuDmaMadr = 4 + pgpuDmaMadr; //addrNdma += 4; - crWordN++; - //The following two are common to all DMA channels, and in future, some global hanler may be used... or not. - pgpuDmaMadr += 4; //It is unclear if this should be done exactly so... - if ((crWordN % (pgpuDmaBcr & 0xFFFF)) == 0) //Because this is actually the "next word number", it can be compared to the size of the block. //End of block reached. - if (pgpuDmaBcr >= 0x10000) - pgpuDmaBcr = pgpuDmaBcr - 0x10000; // pgpuDmaBcr -= 0x10000; - //This is very unoptimized... better use separate block counter. - } - if (crWordN >= nrWordsN) { - PgpuDmaNrActToIop = 0; - pgpuDmaChcr &= ~DMA_TRIGGER; //DMA is no longer active (in transfer) - pgpuDmaChcr &= ~DMA_START_BUSY; //Transfer completed => clear busy flag - pgpuDmaIntr(2); - pgifConLog("PGPU DMA GPU->IOP FINISHED "); - } + u32 data; + if (!dma.state.to_iop_active || rb_gp0.count <= 0) + return; - if (pgifDatRbC.count > 0) - drainPgpuDmaNrToIop(); + if (dma.normal.current_word < dma.normal.total_words) + { + //This is not the best way, but... is there another? + ringBufGet(&rb_gp0, &data); + iopMemWrite32(dma.normal.address, data); + if (dmaRegs.chcr.bits.MAS) + { + DevCon.Error("Unimplemented backward memory step on FROM GPU DMA"); + } + dmaRegs.madr.address += 4; + dma.normal.address += 4; + dma.normal.current_word++; + //dmaRegs.madr.address += 4; //It is unclear if this should be done exactly so... // kozarovv: WHY EVEN DO THAT AGAIN? + if ((dma.normal.current_word % dmaRegs.bcr.bit.block_size) == 0) + { + // decrease block amount only if full block size were drained. + dmaRegs.bcr.bit.block_amount -= 1; + } + PGPU_DMA_LOG("GPU->IOP ba: %x , cw: %x , tw: %x" ,dmaRegs.bcr.bit.block_amount, dma.normal.current_word, dma.normal.total_words); + } + if (dma.normal.current_word >= dma.normal.total_words) + { + dma.state.to_iop_active = 0; + dmaRegs.chcr.bits.BUSY = 0; + pgpuDmaIntr(2); + } + + if (rb_gp0.count > 0) + drainPgpuDmaNrToIop(); } void processPgpuDma() -{ //For normal mode & linked list. - if ((pgpuDmaChcr & DMA_START_BUSY) == 0) - return; //not really neessary in this case. - pgpuDmaChcr |= DMA_TRIGGER; //DMA is active (in transfer) - pgifConLog("PGPU DMA EXEC CHCR %08X BCR %08X MADR %08X ", pgpuDmaChcr, pgpuDmaBcr, pgpuDmaMadr); - if (pgpuDmaChcr & DMA_LINKED_LIST) { - pgifConLog("PGPU DMA LINKED LIST WARNING!!! dir= %d ", (pgpuDmaChcr & 1)); //for (i=0;i<0x0FFFFFFF;i++) {} - if (pgpuDmaChcr & DMA_DIR_FROM_RAM) { //Linked-list only supported on direction to GPU. - PgpuDmaLlAct = 1; - //for the very start, the address of the first word should come for the address, stored at MADR - nextAddr = (pgpuDmaMadr & 0x1FFFFFFF); //The address in IOP RAM where to load the first header word from - //u32 data = iopMemRead32(nextAddr); - //pgifConLog( "PGPU LL DMA data= %08X ", data); +{ + if (!dmaRegs.chcr.bits.TSM) + { + Console.Error("SyncMode 0 on GPU DMA!"); + } + if (dmaRegs.chcr.bits.TSM == 3) + { + Console.Warning("SyncMode 3! Assuming SyncMode 1"); + dmaRegs.chcr.bits.TSM = 1; + } + PGPU_DMA_LOG("Starting GPU DMA! CHCR %08X BCR %08X MADR %08X ", dmaRegs.chcr.get(), dmaRegs.bcr.get(), dmaRegs.madr.address); - wCnt = 0; //current word - wordNr = 0; //total number of words - pgifConLog("LL DMA FILL "); - fillFifoOnDrain(); //drainPgpuDmaLl(); //fill a single word in fifo now, because otherwise PS1DRV won't know that there is aything in it and it wouldn't know that a transfer is pending. - return; - } else { - pgifConLog("ERR: Linked list GPU DMA TO IOP!"); - return; - } - } - pgifConLog("WARNING! NORMAL DMA TO EE "); //for (i=0;i<0x1FFFFFFF;i++) {} - crWordN = 0; - addrNdma = pgpuDmaMadr & 0x1FFFFFFF; - nrWordsN = ((pgpuDmaBcr & 0xFFFF) * ((pgpuDmaBcr >> 16) & 0xFFFF)); + //Linked List Mode + if (dmaRegs.chcr.bits.TSM == 2) + { + //To GPU + if (dmaRegs.chcr.bits.DIR) + { + dma.state.ll_active = 1; + dma.ll_dma.next_address = (dmaRegs.madr.address & 0x00FFFFFF); //The address in IOP RAM where to load the first header word from + dma.ll_dma.current_word = 0; + dma.ll_dma.total_words = 0; + PGPU_DMA_LOG("LL DMA FILL"); - if (pgpuDmaChcr & DMA_DIR_FROM_RAM) { //to peripheral - PgpuDmaNrActToGpu = 1; - fillFifoOnDrain(); //drainPgpuDmaNrToGpu(); - } else { - pgifConLog(" NORMAL DMA TO IOP "); - PgpuDmaNrActToIop = 1; - drainPgpuDmaNrToIop(); - } + //fill a single word in fifo now, because otherwise PS1DRV won't know that a transfer is pending. + fillFifoOnDrain(); + return; + } + else + { + Console.Error("Error: Linked list from GPU DMA!"); + return; + } + } + dma.normal.current_word = 0; + dma.normal.address = dmaRegs.madr.address & 0x1FFFFFFF; // Sould we allow whole range? Maybe for psx SPR? + dma.normal.total_words = (dmaRegs.bcr.bit.block_size * dmaRegs.bcr.get_block_amount()); + + if (dmaRegs.chcr.bits.DIR) // to gpu + { + PGPU_DMA_LOG("NORMAL DMA TO GPU"); + dma.state.to_gpu_active = 1; + fillFifoOnDrain(); + } + else + { + PGPU_DMA_LOG("NORMAL DMA FROM GPU"); + dma.state.to_iop_active = 1; + drainPgpuDmaNrToIop(); + } } - -//DMA registers I/O: - u32 psxDma2GpuR(u32 addr) { - u32 data = 0; - addr &= 0x1FFFFFFF; - if (addr == PGPU_DMA_MADR) { - data = pgpuDmaMadr; - } else if (addr == PGPU_DMA_BCR) { - data = pgpuDmaBcr; - } else if (addr == PGPU_DMA_CHCR) { - if ((PgpuDmaLlAct != 1) && (PgpuDmaNrActToGpu != 1) && (PgpuDmaNrActToIop != 1)) - pgpuDmaChcr &= ~0x01000000; - data = pgpuDmaChcr; - //When using the memory for registers, rather than the local variables (see the definition of pgpuDmaChcr), - //bit 0x01000000 gets set again, very soon after transfer ended(by the game) and then when the game pools it to detect dma completion, - //it times-out. the above condition works around that... but that is an indicator that st. is wrong. -how could the game reset it so soon? - //maybe it just writes to it twice...? or maybe it comes as a high 16-bit write... - todo: find the writer. - } else if (addr == PGPU_DMA_TADR) { - data = pgpuDmaTadr; - pgifConLog("PGPU DMA read TADR !!! "); - } - if (addr != PGPU_DMA_CHCR) - pgifConLog("PGPU DMA read 0x%08X = 0x%08X", addr, data); - return data; + u32 data = 0; + addr &= 0x1FFFFFFF; + switch (addr) + { + case PGPU_DMA_MADR: + data = dmaRegs.madr.address; + break; + case PGPU_DMA_BCR: + data = dmaRegs.bcr.get(); + break; + case PGPU_DMA_CHCR: + data = dmaRegs.chcr.get(); + break; + case PGPU_DMA_TADR: + data = pgpuDmaTadr; + Console.Error("PGPU DMA read TADR!"); + break; + default: + Console.Error("Unknown PGPU DMA read 0x%08X", addr); + break; + } + if (addr != PGPU_DMA_CHCR) + PGPU_DMA_LOG("PGPU DMA read 0x%08X = 0x%08X", addr, data); + return data; } void psxDma2GpuW(u32 addr, u32 data) { - pgifConLog("PGPU DMA write 0x%08X = 0x%08X", addr, data); - addr &= 0x1FFFFFFF; - if (addr == PGPU_DMA_MADR) { - pgpuDmaMadr = (data & 0x00FFFFFF); - } else if (addr == PGPU_DMA_BCR) { - pgpuDmaBcr = data; - } else if (addr == PGPU_DMA_CHCR) { - pgpuDmaChcr = data; - if (pgpuDmaChcr & DMA_START_BUSY) { - processPgpuDma(); - } - } else if (addr == PGPU_DMA_TADR) { - pgpuDmaTadr = data; - pgifConLog("PGPU DMA read TADR !!! "); - } -} - - - -//######################################################################################################### -//Other debug & test code, not directly relatedto the PGIF: - -//------------------------- -//debug print from IOP: - -//BIOS POST -void ps12PostOut(u32 mem, u8 value) -{ - pgifConLog("XXXXXXXXXXXXXXXXXXXXXXX PS12 POST = %02X ", value); - //iopMemWrite32(0x0000B9B0, 1); //a bit of high-level emulation to enable DUART TTY in PS1 kernel...which doesn't work -} - - -void psDuartW(u32 mem, u8 value) -{ - pgifConLog("XXXXXXXXXXXXXXXXXXXXXXX DUART reg %08X = %02X >%c<", mem, value, value); - if ((mem & 0x1FFFFFFF) == 0x1F802023) { - pgifConLog("XXXXXXXXXXXXXXXXXXXXXXX DUART A = %02X >%c<", value, value); - } else if ((mem & 0x1FFFFFFF) == 0x1F80202B) { - pgifConLog("XXXXXXXXXXXXXXXXXXXXXXX DUART B = %02X >%c<", value, value); - } -} - -u8 psExp2R8(u32 mem) -{ - if ((mem & 0x1FFFFFFF) == 0x1F802000) { - return 0x18; - } - return 0xFF; -} - -//int flgint = 0; - -//0x80051080 -u32 getIntTmrKReg(u32 mem, u32 data) -{ - u32 outd = data; - if (PGifCtrlReg != 0) { - pgifConLog("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"); - //outd = 0; - outd = 0x7FFFFFFF; - } - return outd; -} - -void HPCoS_print(u32 mem, u32 data) -{ - int i; - if ((mem & 0x1FFFFFFF) == 0x1103A0) { - //pgifConLog("HPCoS BufPnt = %08X ", data); - char str[201]; - //if (data == 0) { - for (i = 0; i < 200; i++) { - str[i] = iopMemRead8(0x117328 + i); - if (str[i] == 0) - break; - } - str[i] = 0; - // pgifConLog(" FFFFFFFFFFFFFFFFFFFFFFFFFFFF >%s< ", str); - //} - } //else if ((mem & 0x1FFFFFFF) == 0x117328) { - // pgifConLog("HPCoS BufAdr = %08X >%c<", data, data); - - //} -} - -int startPrntPc = 0; -void anyIopLS(u32 addr, u32 data, int Wr) -{ - if (doAnyIopLs == 0) - return; -//#define nrRuns 0xFFFFFF -#define nrRuns 0xFFFFFFF - //if ((startPrntPc > nrRuns) && (startPrntPc < (nrRuns + 2000))) - // pgifConLog( "MEM %d 0x%08X = 0x%08X IOPpc= %08X ", Wr, addr, data, psxRegs.pc); - //else - if (startPrntPc <= (nrRuns + 2000)) - startPrntPc++; -} - - -void dma6_OTClear() -{ - if ((psxHu(0x1f8010e8) & 0x01000000) == 0) - return; - u32 madr = psxHu(0x1f8010e0); - u32 blkSz = psxHu(0x1f8010e4) & 0xFFFF; - //u32 blkCnt = (psxHu(0x1f8010e4) >> 16) & 0xFFFF; - u32 addrPnt = madr; - u32 pntCnt = 0; - Console.WriteLn("DMA6 OT clr madr %08X bcr %08X chcr %08X", psxHu(0x1f8010e0), psxHu(0x1f8010e4), psxHu(0x1f8010e8)); - - while (pntCnt < blkSz) { //Very unoptimized... - iopMemWrite32(addrPnt, ((addrPnt - 4) & 0x00FFFFFF)); - addrPnt -= 4; - pntCnt++; - } - iopMemWrite32((addrPnt + 4), 0x00FFFFFF); - - //psxHu(0x1f8010e0) - //psxHu(0x1f8010e4) - - psxHu(0x1f8010e8) &= ~0x01000000; - psxDmaInterrupt(6); -} - -//NOTE: use iopPhysMem(madr) for getting the physical (local) address of IOP RAM stuff. ... does this account for the complete 16MB range? + PGPU_DMA_LOG("PGPU DMA write 0x%08X = 0x%08X", addr, data); + addr &= 0x1FFFFFFF; + switch (addr) + { + case PGPU_DMA_MADR: + dmaRegs.madr.address = (data & 0x00FFFFFF); + break; + case PGPU_DMA_BCR: + dmaRegs.bcr.write(data); + break; + case PGPU_DMA_CHCR: + dmaRegs.chcr.write(data); + if (dmaRegs.chcr.bits.BUSY) + { + processPgpuDma(); + } + break; + case PGPU_DMA_TADR: + pgpuDmaTadr = data; + Console.Error("PGPU DMA write TADR! "); + break; + default: + Console.Error("Unknown PGPU DMA write 0x%08X = 0x%08X", addr, data); + break; + } +} \ No newline at end of file diff --git a/pcsx2/ps2/pgif.h b/pcsx2/ps2/pgif.h index f4b8983af8..f04e220578 100644 --- a/pcsx2/ps2/pgif.h +++ b/pcsx2/ps2/pgif.h @@ -1,5 +1,5 @@ /* PCSX2 - PS2 Emulator for PCs -* Copyright (C) 2016-2016 PCSX2 Dev Team +* Copyright (C) 2016-2021 PCSX2 Dev Team * Copyright (C) 2016 Wisi * * PCSX2 is free software: you can redistribute it and/or modify it under the terms @@ -14,6 +14,251 @@ * If not, see . */ +#pragma once + +//HW Registers +union tPGIF_CTRL +{ + + struct pgifCtrl_t + { + //Please keep in mind, that not all of values are 100% confirmed. + u32 UNK1 : 2; // 0-1 + u32 fifo_GP1_ready_for_data : 1; // 2 + u32 fifo_GP0_ready_for_data : 1; // 3 + u32 data_from_gpu_ready : 1; // 4 sets in ps1drv same time as DMA RSEND + u32 UNK2 : 1; // 5 + u32 UNK3 : 2; // 6-7 + u32 GP0_fifo_count : 5; // 8-12 + u32 UNK4 : 3; // 13-15 + u32 GP1_fifo_count : 3; // 16 - 18 + u32 UNK5 : 1; // 19 + u32 GP0_fifo_empty : 1; // 20 + u32 UNK6 : 1; // 21 + u32 UNK7 : 1; // 22 + u32 UNK8 : 8; // 23-30 + u32 BUSY : 1; // Busy + }bits; + + u32 _u32; + tPGIF_CTRL( u32 val ) { _u32 = val; } + void write(u32 value) { _u32 = value; } + u32 get() { return _u32; } +}; + +union tPGIF_IMM +{ + struct imm_t + { + + u32 e2; + u32 dummy1[3]; + u32 e3; + u32 dummy2[3]; + u32 e4; + u32 dummy3[3]; + u32 e5; + u32 dummy4[3]; + + }reg; + void reset() { reg.e2 = reg.e3 = reg.e4 = reg.e5 = 0; } +}; + +struct PGIFregisters +{ + tPGIF_IMM imm_response; + u128 dummy1[2]; + tPGIF_CTRL ctrl; +}; +static PGIFregisters& pgif = (PGIFregisters&)eeHw[0xf310]; + +union tPGPU_REGS +{ + struct Bits_t + { + u32 TPXB : 4; // 0-3 Texture page X Base (N*64) + u32 TPYB : 1; // 4 Texture page Y Base (N*256) (ie. 0 or 256) + u32 ST : 2; // 5-6 Semi Transparency (0=B/2+F/2, 1=B+F, 2=B-F, 3=B+F/4) + u32 TPC : 2; // 7-8 Texture page colors (0=4bit, 1=8bit, 2=15bit, 3=Reserved) + u32 DITH : 1; // 9 Dither 24bit to 15bit (0=Off/strip LSBs, 1=Dither Enabled) + u32 DRAW : 1; // 10 Drawing to display area + u32 DMSK : 1; // 11 Set Mask-bit when drawing pixels (0=No, 1=Yes/Mask) + u32 DPIX : 1; // 12 Draw Pixels (0=Always, 1=Not to Masked areas) + u32 ILAC : 1; // 13 Interlace Field (or, always 1 when GP1(08h).5=0) + u32 RFLG : 1; // 14 "Reverseflag" (0=Normal, 1=Distorted) + u32 TDIS : 1; // 15 Texture Disable (0=Normal, 1=Disable Textures) + u32 HR2 : 1; // 16 Horizontal Resolution 2 (0=256/320/512/640, 1=368) + u32 HR1 : 2; // 17-18 Horizontal Resolution 1 (0=256, 1=320, 2=512, 3=640) + u32 VRES : 1; // 19 Vertical Resolution (0=240, 1=480, when Bit22=1) + u32 VMOD : 1; // 20 Video Mode (0=NTSC/60Hz, 1=PAL/50Hz) + u32 COLD : 1; // 21 Display Area Color Depth (0=15bit, 1=24bit) + u32 VILAC : 1; // 22 Vertical Interlace (0=Off, 1=On) + u32 DE : 1; // 23 Display Enable (0=Enabled, 1=Disabled) + u32 IRQ1 : 1; // 24 Interrupt Request (IRQ1) (0=Off, 1=IRQ) ;GP0(1Fh)/GP1(02h) + u32 DREQ : 1; // 25 DMA / Data Request, meaning depends on GP1(04h) DMA Direction: + // When GP1(04h)=0 ---> Always zero (0) + // When GP1(04h)=1 ---> FIFO State (0=Full, 1=Not Full) + // When GP1(04h)=2 ---> Same as GPUSTAT.28 + // When GP1(04h)=3 ---> Same as GPUSTAT.27 + u32 RCMD : 1; // 26 Ready to receive Cmd Word (0=No, 1=Ready) ;GP0(...) ;via GP0 + u32 RSEND : 1; // 27 Ready to send VRAM to CPU (0=No, 1=Ready) ;GP0(C0h) ;via GPUREAD + u32 RDMA : 1; // 28 Ready to receive DMA Block (0=No, 1=Ready) ;GP0(...) ;via GP0 + u32 DDIR : 2; // 29-30 DMA Direction (0=Off, 1=?, 2=CPUtoGP0, 3=GPUREADtoCPU) + u32 DEO : 1; // 31 Drawing even/odd lines in interlace mode (0=Even or Vblank, 1=Odd) + }bits; + + u32 _u32; + tPGPU_REGS( u32 val ) { _u32 = val; } + void write(u32 value) { _u32 = value; } + u32 get() { return _u32; } +}; + +struct PGPUregisters +{ + tPGPU_REGS stat; +}; +static PGPUregisters& pgpu = (PGPUregisters&)eeHw[0xf300]; + +static struct Regs_t +{ + struct pgifRegs_t + { + //PGifIfStat + u32 ctrl; + } pgif; +} hwRegs; + +//Internal dma flags: +static struct dma_t +{ + struct dmaState_t + { + bool ll_active; + bool to_gpu_active; + bool to_iop_active; + } state; + + struct ll_dma_t + { + u32 data_read_address; + u32 total_words; //total number of words + u32 current_word; //current word number + u32 next_address; + } ll_dma; + + struct normalDma_t + { + u32 total_words; //total number of words in Normal DMA + u32 current_word; //current word number in Normal DMA + u32 address; + } normal; +} dma; + +union tCHCR_DMA +{ + struct chcrDma_t + { + u32 DIR : 1; //0 Transfer Direction (0=To Main RAM, 1=From Main RAM) + u32 MAS : 1; //1 Memory Address Step (0=Forward;+4, 1=Backward;-4) + u32 resv0 : 6; + u32 CHE : 1; //8 Chopping Enable (0=Normal, 1=Chopping; run CPU during DMA gaps) + u32 TSM : 2; //9-10 SyncMode, Transfer Synchronisation/Mode (0-3): + //0 Start immediately and transfer all at once (used for CDROM, OTC) + //1 Sync blocks to DMA requests (used for MDEC, SPU, and GPU-data) + //2 Linked-List mode (used for GPU-command-lists) + //3 Reserved (not used) + u32 resv1 : 5; + u32 CDWS : 3; // 16-18 Chopping DMA Window Size (1 SHL N words) + u32 resv2 : 1; + u32 CCWS : 3; // 20-22 Chopping CPU Window Size (1 SHL N clks) + u32 resv3 : 1; + u32 BUSY : 1; // 24 Start/Busy (0=Stopped/Completed, 1=Start/Enable/Busy) + u32 resv4 : 3; + u32 TRIG : 1; // 28 Start/Trigger (0=Normal, 1=Manual Start; use for SyncMode=0) + u32 UKN1 : 1; // 29 Unknown (R/W) Pause? (0=No, 1=Pause?) (For SyncMode=0 only?) + u32 UNK2 : 1; // 30 Unknown (R/W) + u32 resv5 : 1; + }bits; + u32 _u32; + tCHCR_DMA( u32 val ) { _u32 = val; } + void write(u32 value) { _u32 = value; } + u32 get() { return _u32; } +}; + +union tBCR_DMA +{ + struct bcrDma_t + { + u32 block_size : 16; + u32 block_amount : 16; + }bit; + + u32 _u32; + tBCR_DMA( u32 val ) { _u32 = val; } + u32 get_block_amount() { return bit.block_amount ? bit.block_amount : 0x10000; } + u32 get_block_size() { return bit.block_size; } + void write(u32 value) { _u32 = value; } + u32 get() { return _u32; } +}; + +union tMADR_DMA +{ + u32 address; + + tMADR_DMA( u32 val ) { address = val; } + void write(u32 value) { address = value; } + u32 get() { return address; } +}; + +struct DMAregisters +{ + tMADR_DMA madr; + tBCR_DMA bcr; + tCHCR_DMA chcr; +}; +static DMAregisters& dmaRegs = (DMAregisters&)iopHw[0x10a0]; + +//Generic FIFO-related: +struct ringBuf_t +{ + u32* buf; + int size; + int count; + int head; + int tail; +}; + +//Defines for address labels: + +//PGPU_STAT 0x1000F300 The GP1 - Status register, which PS1DRV writes (emulates) for the IOP to read. +#define PGPU_STAT 0x1000F300 + +//IMM_E2-IMM_E5 - "immediate response registers" - hold the return values for commands that require immediate response. +//They correspond to GP0() E2-E5 commands. +#define IMM_E2 0x1000F310 +#define IMM_E3 0x1000F320 +#define IMM_E4 0x1000F330 +#define IMM_E5 0x1000F340 + +//PGIF_CTRL 0x1000F380 Main register for PGIF status info & control. +#define PGIF_CTRL 0x1000F380 + +//PGPU_CMD_FIFO FIFO buffer for GPU GP1 (CMD reg) CMDs IOP->EE only (unknown if reverse direction is possible). +#define PGPU_CMD_FIFO 0x1000F3C0 +//PGPU_DAT_FIFO FIFO buffer for GPU GP0 (DATA reg) IOP->EE, but also EE->IOP. Direction is controlled by reg. 0x80/bit4 (most likely). +//Official name is "GFIFO", according to PS1DRV. +#define PGPU_DAT_FIFO 0x1000F3E0 + +//write to peripheral +#define DMA_LL_END_CODE 0x00FFFFFF + +#define PGPU_DMA_MADR 0x1F8010A0 +#define PGPU_DMA_BCR 0x1F8010A4 +#define PGPU_DMA_CHCR 0x1F8010A8 +#define PGPU_DMA_TADR 0x1F8010AC + +#define pgpuDmaTadr HW_DMA2_TADR + void pgifInit(void); void pgifReset(void); @@ -29,13 +274,16 @@ extern void PGIFrQword(u32 addr, void *); extern u32 psxDma2GpuR(u32 addr); extern void psxDma2GpuW(u32 addr, u32 data); +/*************************************************************************************************** +*** Constants here control code that is either not certainly correct or may affect compatibility *** +***************************************************************************************************/ -extern void ps12PostOut(u32 mem, u8 value); -extern void psDuartW(u32 mem, u8 value); -extern u8 psExp2R8(u32 mem); -extern void kernelTTYFileDescrWrite(u32 mem, u32 data); -extern u32 getIntTmrKReg(u32 mem, u32 data); -extern void testInt(void); -extern void HPCoS_print(u32 mem, u32 data); -extern void anyIopLS(u32 addr, u32 data, int Wr); -extern void dma6_OTClear(void); \ No newline at end of file +//Default=1. Is unknown why we need this, but we need this.. +#define PREVENT_IRQ_ON_NORM_DMA_TO_GPU 1 + +//PGIF_DAT_RB_LEAVE_FREE - How many elements of the FIFO buffer to leave free in DMA. +//Can be 0 and no faults are observed. +//As the buffer has 32 elements, and normal DMA reads are usually done in 4 qwords (16 words), +//this must be less than 16, otherwise the PS1DRV will never read from the FIFO. +//At one point (in Linked-List DMA), PS1DRV will expect at least a certain number of elements, that is sent as argument to the func. +#define PGIF_DAT_RB_LEAVE_FREE 1