thug/Code/Gfx/NGPS/NX/nx_init.cpp

435 lines
13 KiB
C++
Raw Permalink Normal View History

2016-02-13 21:39:12 +00:00
#include <stdio.h>
#include <stdlib.h>
#include <libgraph.h>
#include <libdma.h>
#include <devvu0.h>
#include <devvu1.h>
#include <core/defines.h>
#include <core/debug.h>
#include <libsn.h>
#include <sys/file/pip.h>
#include <sys/config/config.h>
#include "nx_init.h"
#include "dma.h"
#include "vif.h"
#include "vu1.h"
#include "vu1code.h"
#include "vu0code.h"
#include "gif.h"
#include "gs.h"
#include "dmacalls.h"
#include "pcrtc.h"
#include "render.h"
#include "chars.h"
#include "scene.h"
#include "group.h"
#include "interrupts.h"
#include "switches.h"
#include "instance.h"
#include "geomnode.h"
#include "texture.h"
#include "resource.h"
#include "fx.h"
namespace NxPs2
{
extern void WaitForRendering();
// Interrupts
static void StartInterrupts();
static void StopInterrupts();
Mth::Matrix GmyMat[4];
// Interrupt Handler IDs
#if USE_INTERRUPTS
#if GIF_INTERRUPT
static int sGifHandlerId = -1;
#endif
#if GS_INTERRUPT
static int sGsHandlerId = -1;
#endif
#if VIF1_INTERRUPT
static int sVif1HandlerId = -1;
#endif
#endif
//////////////////////////////////////////////////////////////////
// void InitialiseEngine(void)
//
// Sets up the PS2 engine, down to the hardware level
// - Resets the hardware, using sce library calls, setting video mode
// - sets up the PCRTC registers (display buffer conficuration, and read circuit anti-aliasing)
// - Initilizes memory for DMA buffers
// - Builds the standard DMA subroutines (clear VRAM, page flip, etc)
// - adds GS, VIF1 and GIF interrupts
// - creates an empty "scene" and an empty group as the default things to render
void InitialiseEngine(void)
{
// Mick: Make it safe to call more than once
// Needed for a last minute loading screen hack TT#12480
static bool done = false;
if (done)
{
return;
}
done = true;
// Initialize devices
sceDmaReset(1);
sceGsResetPath();
sceDevVu1PutDBit(1); // sceGsResetPath will clear the D-bit, so need to switch it on
//sceDevVu0PutTBit(1);
// setup PCRTC registers - switch off display until vram has been cleared
SetupPCRTC(0, SCE_GS_FIELD);
// Clear resoruces
CSystemResources::sInitResources();
// allocate dma list memory (prebuilt & run-time) and subroutine table
dma::pPrebuiltBuffer = (uint8 *)Mem::Calloc(PREBUILT_DMA_BUFFER_SIZE/16,16);
Dbg_MsgAssert(dma::pPrebuiltBuffer, ("couldn't malloc memory for prebuilt dma buffer"));
if (Config::GotExtraMemory())
{
// Allocate a dummy buffer so that the current heap's usage is the same
// as for when there is not extra memory.
dma::pDummyBuffer = (uint8 *)Mem::Calloc(NON_DEBUG_DMA_BUFFER_SIZE/16,16);
// But for the actual runtime buffer allocate a massive buffer off the debug heap.
Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().DebugHeap());
dma::pRuntimeBuffer = (uint8 *)Mem::Calloc(DEBUG_DMA_BUFFER_SIZE/16,16);
Mem::Manager::sHandle().PopContext();
Dbg_MsgAssert(dma::pRuntimeBuffer, ("couldn't malloc memory for runtime dma buffer"));
// set runtime dma list pointers (they are double buffered, using half the buffer each)
dma::pList[0] = dma::pRuntimeBuffer;
dma::pList[1] = dma::pRuntimeBuffer + DEBUG_DMA_BUFFER_SIZE/2;
}
else
{
dma::pRuntimeBuffer = (uint8 *)Mem::Calloc(NON_DEBUG_DMA_BUFFER_SIZE/16,16);
Dbg_MsgAssert(dma::pRuntimeBuffer, ("couldn't malloc memory for runtime dma buffer"));
// set runtime dma list pointers (they are double buffered, using half the buffer each)
dma::pList[0] = dma::pRuntimeBuffer;
dma::pList[1] = dma::pRuntimeBuffer + NON_DEBUG_DMA_BUFFER_SIZE/2;
}
dma::size = NON_DEBUG_DMA_BUFFER_SIZE/2; // normal sized (half the buffer)
dma::Gosubs = (uint64 *)Mem::Calloc(NUM_SUBROUTINES, sizeof(uint64));
Dbg_MsgAssert(dma::Gosubs, ("couldn't malloc memory for dma gosub table"));
//Subroutines = (uint8 **)Mem::Calloc(256, sizeof(uint8 *));
//Dbg_MsgAssert(Subroutines, ("couldn't malloc memory for dma gosub table"));
// initialise dma pointer for use by BuildDmaSubroutines
dma::pLoc = dma::pPrebuiltBuffer;
// prebuild dma subroutines (into dma::pPrebuiltBuffer)
BuildDmaSubroutines();
// Enable interrupts
StartInterrupts();
// wait for v-sync
sceGsSyncV(0);
// clear vram
FlushCache(WRITEBACK_DCACHE);
*D1_QWC = 0; // must zero QWC because the first action will be to use current MADR & QWC
*D1_TADR = dma::Gosubs[CLEAR_VRAM] >> 32;
*D1_CHCR = 0x145; // start transfer, tte=1, chain mode, from memory
sceGsSyncPath(0, 0);
// setup PCRTC registers - vram is cleared so switch on display
SetupPCRTC(1, SCE_GS_FIELD);
// upload vu0 code
*D0_QWC = ((uint8 *)MPGEnd0-(uint8 *)MPGStart0+15)/16;
*D0_MADR = (uint)MPGStart0;
*D0_CHCR = 0x100; // start transfer, normal mode
sceGsSyncPath(0, 0);
// empty scene list
sScene::pHead = NULL;
// create the shadow group
sGroup::pShadow = (sGroup *)Mem::Malloc(sizeof(sGroup));
memset(sGroup::pShadow,0,sizeof(sGroup));
Dbg_MsgAssert(sGroup::pShadow, ("couldn't allocate shadow group\n"));
sGroup::pShadow->Priority = 1500.001f;
sGroup::pShadow->pMeshes = NULL;
sGroup::pShadow->NumMeshes = 0;
#if STENCIL_SHADOW
sGroup::pShadow->VramStart = 0x3740;
#else
sGroup::pShadow->VramStart = 0x3E00;
#endif
sGroup::pShadow->VramEnd = 0x4000;
sGroup::pShadow->flags = 0;
sGroup::pShadow->profile_color = 0xFFFFFF; // bright white = shadow group
// create the fog group
sGroup::pFog = (sGroup *)Mem::Malloc(sizeof(sGroup));
memset(sGroup::pFog,0,sizeof(sGroup));
Dbg_MsgAssert(sGroup::pFog, ("couldn't allocate fog group\n"));
sGroup::pFog->Priority = 9999.0f;
sGroup::pFog->pMeshes = NULL;
sGroup::pFog->NumMeshes = 0;
sGroup::pFog->VramStart = 0x3000;
sGroup::pFog->VramEnd = 0x4000;
sGroup::pFog->flags = 0;
sGroup::pFog->pScene = NULL;
sGroup::pFog->profile_color = 0x303030; // dark grey = fog group
// create the particle group
sGroup::pParticles = (sGroup *)Mem::Malloc(sizeof(sGroup));
memset(sGroup::pParticles,0,sizeof(sGroup));
Dbg_MsgAssert(sGroup::pParticles, ("couldn't allocate particle group\n"));
sGroup::pParticles->Priority = 10000.0f;
sGroup::pParticles->pMeshes = NULL;
sGroup::pParticles->NumMeshes = 0;
sGroup::pParticles->VramStart = 0x3000;
sGroup::pParticles->VramEnd = 0x4000;
sGroup::pParticles->flags = 0;
sGroup::pParticles->pScene = NULL;
sGroup::pParticles->profile_color = 0x0; // black = particle group
// create epilogue group
sGroup::pEpilogue = (sGroup *)Mem::Malloc(sizeof(sGroup));
memset(sGroup::pEpilogue,0,sizeof(sGroup));
Dbg_MsgAssert(sGroup::pEpilogue, ("couldn't allocate epilogue group\n"));
sGroup::pEpilogue->Priority = 1000000;
sGroup::pEpilogue->pMeshes = NULL;
sGroup::pEpilogue->NumMeshes = 0;
sGroup::pEpilogue->VramStart = 0x0000; // pretend it uses all of vram
sGroup::pEpilogue->VramEnd = 0x4000; // so there's no buffer address worries
sGroup::pEpilogue->profile_color = 0x800000; // blue = epilogue group
// link predefined groups together
sGroup::pShadow->pNext = sGroup::pFog;
sGroup::pFog->pNext = sGroup::pParticles;
sGroup::pParticles->pNext = sGroup::pEpilogue;
sGroup::pEpilogue->pNext = NULL;
sGroup::pShadow->pPrev = NULL;
sGroup::pFog->pPrev = sGroup::pShadow;
sGroup::pParticles->pPrev = sGroup::pFog;
sGroup::pEpilogue->pPrev = sGroup::pParticles;
sGroup::pHead = sGroup::pShadow;
sGroup::pTail = sGroup::pEpilogue;
// initialise vram group buffering scheme
sGroup::VramBufferBase = 0x2BC0;
// initialise font vram base pointer
FontVramStart = FontVramBase = 0x2BC0; // a cheat
// initialise empty font list
pFontList = NULL;
// activate the world!
CGeomNode::sWorld.SetActive(true);
// set up fog palette
// Fx::SetupFogPalette(0x40806060, 10.0f);
}
void AllocateExtraDMA(bool allocate)
{
static void * s_extra = NULL;
static void * old_pList1;
Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().BottomUpHeap());
if (allocate)
{
Dbg_MsgAssert(!s_extra,("Tried to allocate extra DMA when already allocated"));
old_pList1 = dma::pList[1];
s_extra = (uint8 *)Mem::Calloc(NON_DEBUG_DMA_BUFFER_SIZE/16,16); // same size as the entire double buffer
dma::pList[1] = (uint8 *)s_extra;
dma::size = NON_DEBUG_DMA_BUFFER_SIZE; // double sized
}
else
{
if (s_extra)
{
dma::pList[1] = (uint8 *)old_pList1;
Mem::Free(s_extra);
s_extra = NULL;
dma::size = NON_DEBUG_DMA_BUFFER_SIZE/2; // normal sized
}
}
dma::pLoc = 0 ; // reset pLoc as it might not be valid. This will catch this function being called in the wrong place
Mem::Manager::sHandle().PopContext();
}
//////////////////////////////////////////////////////////////////
// void ResetEngine(void)
//
// Sets up the PS2 engine, down to the hardware level
// - Resets the hardware, using sce library calls, setting video mode
// - sets up the PCRTC registers (display buffer conficuration, and read circuit anti-aliasing)
// - clears VRAM, which also sets the TEXA register
// - resets GS, VIF1 and GIF interrupts
void ResetEngine(void)
{
// initialize devices
sceDmaReset(1);
sceGsResetPath();
sceDevVu1PutDBit(1); // sceGsResetPath will clear the D-bit, so need to switch it on
//sceDevVu0PutTBit(1);
// Don't call sceGsResetGraph() anymore. It interferes with SetupPCRTC().
//// re-initialize the GS
//if (Config::NTSC())
//{
// sceGsResetGraph(0, SCE_GS_INTERLACE, SCE_GS_NTSC, SCE_GS_FIELD);
//} else {
// sceGsResetGraph(0, SCE_GS_INTERLACE, SCE_GS_PAL, SCE_GS_FIELD);
//}
// setup PCRTC registers (don't need this call anymore; the last call is all that is necessary)
//SetupPCRTC(0, SCE_GS_FIELD);
// restart interrupts
StartInterrupts();
// wait for v-sync
sceGsSyncV(0);
// clear vram (Garrett: This is the ONLY place that TEXA is ever set! Caused many weird bugs if not called.)
FlushCache(WRITEBACK_DCACHE);
*D1_QWC = 0; // must zero QWC because the first action will be to use current MADR & QWC
*D1_TADR = dma::Gosubs[CLEAR_VRAM] >> 32;
*D1_CHCR = 0x145; // start transfer, tte=1, chain mode, from memory
sceGsSyncPath(0, 0);
// setup PCRTC registers - vram is cleared so switch on display
SetupPCRTC(1, SCE_GS_FIELD);
}
//////////////////////////////////////////////////////////////////
// void SuspendEngine(void)
//
// Suspends the PS2 engine by waiting for any rendering to finish
// and then shuts down the interrupts.
void SuspendEngine(void)
{
// Let the engine finish what it was doing first
WaitForRendering();
// stop interrupts
StopInterrupts();
}
//////////////////////////////////////////////////////////////////
// void StartInterrupts(void)
//
// Starts all the interrupts for the PS2 engine.
static void StartInterrupts(void)
{
#if USE_INTERRUPTS
#if GIF_INTERRUPT
// install interrupt handler for texture upload completion
if ((sGifHandlerId = AddDmacHandler(DMAC_GIF, GifHandler, 0)) == -1)
{
printf("Couldn't register dmac handler\n");
exit(1);
}
*D_STAT = 1 << DMAC_GIF;
EnableDmac(DMAC_GIF);
#endif
#if GS_INTERRUPT
// install interrupt handler for render completion
if ((sGsHandlerId = AddIntcHandler(INTC_GS, GsHandler, 0)) == -1)
{
printf("Couldn't register GS handler\n");
exit(1);
}
*GS_IMR = PackIMR(0,1,1,1); // SIGNAL, FINISH, HSync, VSync
EnableIntc(INTC_GS);
#endif
#if VIF1_INTERRUPT
// install interrupt handler for render completion
if ((sVif1HandlerId = AddIntcHandler(INTC_VIF1, Vif1Handler, 0)) == -1)
{
printf("Couldn't register vif1 handler\n");
exit(1);
}
*VIF1_ERR = 0;
EnableIntc(INTC_VIF1);
#endif
// set intermittent mode transfer
*GIF_MODE = 0x0;
#endif
}
//////////////////////////////////////////////////////////////////
// void StopInterrupts(void)
//
// Stops all the interrupts for the PS2 engine.
static void StopInterrupts(void)
{
#if USE_INTERRUPTS
#if GIF_INTERRUPT
// remove interrupt handler for texture upload completion
DisableDmac(DMAC_GIF);
if (sGifHandlerId >= 0)
{
RemoveDmacHandler(DMAC_GIF, sGifHandlerId);
sGifHandlerId = -1;
}
#endif
#if GS_INTERRUPT
// remove interrupt handler for render completion
DisableIntc(INTC_GS);
if (sGsHandlerId >= 0)
{
RemoveIntcHandler(INTC_GS, sGsHandlerId);
sGsHandlerId = -1;
}
#endif
#if VIF1_INTERRUPT
// remove interrupt handler for render completion
DisableIntc(INTC_VIF1);
if (sVif1HandlerId >= 0)
{
RemoveIntcHandler(INTC_VIF1, sVif1HandlerId);
sVif1HandlerId = -1;
}
#endif
#endif
}
} // namespace NxPs2