summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTakaRikka <38417346+TakaRikka@users.noreply.github.com>2025-12-13 08:21:32 -0800
committerGitHub <noreply@github.com>2025-12-13 18:21:32 +0200
commit4e6dffff5a325365dce234d3b7daba60dc3245c0 (patch)
tree0d3a4657fb992eb4ea83990373a11583884e61e9 /src
parent25dfef320b8c7bd1e9952bd54e5808aa665e841d (diff)
most of shieldD revo sdk matching (#2951)
* shieldD revo wpad done * shieldD revo hio2 done * shieldD revo aralt, ppcarch, gf done * shieldD revo exi done * shieldD revo SI done * shieldD revo vi done * shieldD revo mtx done * shieldD revo GX mostly done * shieldD revo ai/dsp done * shieldD revo sc mostly done * shieldD revo esp/euart/wenc mostly done * build fixes
Diffstat (limited to 'src')
-rw-r--r--src/d/d_home_button.cpp4
-rw-r--r--src/m_Do/m_Do_graphic.cpp2
-rw-r--r--src/revolution/ai/ai.c273
-rw-r--r--src/revolution/aralt/__ar.h18
-rw-r--r--src/revolution/aralt/aralt.c175
-rw-r--r--src/revolution/base/PPCArch.c306
-rw-r--r--src/revolution/base/PPCPm.c34
-rw-r--r--src/revolution/dsp/__dsp.h27
-rw-r--r--src/revolution/dsp/dsp.c194
-rw-r--r--src/revolution/dsp/dsp_debug.c9
-rw-r--r--src/revolution/dsp/dsp_task.c363
-rw-r--r--src/revolution/esp/esp.c320
-rw-r--r--src/revolution/euart/euart.c148
-rw-r--r--src/revolution/exi/EXIBios.c876
-rw-r--r--src/revolution/exi/EXICommon.c113
-rw-r--r--src/revolution/exi/EXIUart.c190
-rw-r--r--src/revolution/gd/GDBase.c45
-rw-r--r--src/revolution/gd/GDFile.c64
-rw-r--r--src/revolution/gd/GDGeometry.c418
-rw-r--r--src/revolution/gd/GDIndirect.c270
-rw-r--r--src/revolution/gd/GDLight.c206
-rw-r--r--src/revolution/gd/GDPixel.c118
-rw-r--r--src/revolution/gd/GDTev.c159
-rw-r--r--src/revolution/gd/GDTexture.c104
-rw-r--r--src/revolution/gd/GDTransform.c127
-rw-r--r--src/revolution/gf/GFGeometry.cpp10
-rw-r--r--src/revolution/gf/GFLight.cpp6
-rw-r--r--src/revolution/gf/GFPixel.cpp74
-rw-r--r--src/revolution/gf/GFTev.cpp15
-rw-r--r--src/revolution/gx/GXAttr.c637
-rw-r--r--src/revolution/gx/GXBump.c308
-rw-r--r--src/revolution/gx/GXDisplayList.c99
-rw-r--r--src/revolution/gx/GXDraw.c551
-rw-r--r--src/revolution/gx/GXFifo.c614
-rw-r--r--src/revolution/gx/GXFrameBuf.c677
-rw-r--r--src/revolution/gx/GXGeometry.c267
-rw-r--r--src/revolution/gx/GXInit.c567
-rw-r--r--src/revolution/gx/GXLight.c571
-rw-r--r--src/revolution/gx/GXMisc.c482
-rw-r--r--src/revolution/gx/GXPerf.c431
-rw-r--r--src/revolution/gx/GXPixel.c332
-rw-r--r--src/revolution/gx/GXSave.c528
-rw-r--r--src/revolution/gx/GXStubs.c5
-rw-r--r--src/revolution/gx/GXTev.c465
-rw-r--r--src/revolution/gx/GXTexture.c1315
-rw-r--r--src/revolution/gx/GXTransform.c607
-rw-r--r--src/revolution/gx/GXVerifRAS.c628
-rw-r--r--src/revolution/gx/GXVerifXF.c946
-rw-r--r--src/revolution/gx/GXVerify.c372
-rw-r--r--src/revolution/gx/GXVert.c86
-rw-r--r--src/revolution/gx/__gx.h573
-rw-r--r--src/revolution/hio2/hio2.c273
-rw-r--r--src/revolution/mtx/mtx.c1197
-rw-r--r--src/revolution/mtx/mtx44.c888
-rw-r--r--src/revolution/mtx/mtx44vec.c247
-rw-r--r--src/revolution/mtx/mtxstack.c108
-rw-r--r--src/revolution/mtx/mtxvec.c204
-rw-r--r--src/revolution/mtx/psmtx.c336
-rw-r--r--src/revolution/mtx/quat.c486
-rw-r--r--src/revolution/mtx/vec.c344
-rw-r--r--src/revolution/sc/scapi.c123
-rw-r--r--src/revolution/sc/scapi_prdinfo.c136
-rw-r--r--src/revolution/sc/scsystem.c996
-rw-r--r--src/revolution/si/SIBios.c821
-rw-r--r--src/revolution/si/SISamplingRate.c125
-rw-r--r--src/revolution/vi/__vi.h40
-rw-r--r--src/revolution/vi/i2c.c230
-rw-r--r--src/revolution/vi/vi.c1703
-rw-r--r--src/revolution/vi/vi3in1.c501
-rw-r--r--src/revolution/wenc/wenc.c117
-rw-r--r--src/revolution/wpad/WPAD.c434
-rw-r--r--src/revolution/wpad/WUD.c63
-rw-r--r--src/revolution/wpad/__wpad.h538
-rw-r--r--src/revolution/wpad/__wud.h79
74 files changed, 25715 insertions, 3 deletions
diff --git a/src/d/d_home_button.cpp b/src/d/d_home_button.cpp
index 194f5a3f86..bd11f303eb 100644
--- a/src/d/d_home_button.cpp
+++ b/src/d/d_home_button.cpp
@@ -147,8 +147,8 @@ void dHomeButton_c::initCursorPos() {
void dHomeButton_c::calcCursorPos() {
for (u32 i = 0; i < 4; i++) {
- WPADDeviceType sp8;
- WPADResult result = WPADProbe(i, &sp8);
+ u32 sp8;
+ s32 result = WPADProbe(i, &sp8);
if (result == -2 || result == -3 || result == 0) {
m_controllerData.wiiCon[i].kpad = &mReCPd::m_pad[i].field_0x4[0];
diff --git a/src/m_Do/m_Do_graphic.cpp b/src/m_Do/m_Do_graphic.cpp
index 7c6074323a..38f0b3dfb1 100644
--- a/src/m_Do/m_Do_graphic.cpp
+++ b/src/m_Do/m_Do_graphic.cpp
@@ -338,7 +338,7 @@ static bool data_80450BE8;
void mDoGph_gInf_c::beginRender() {
#if PLATFORM_WII || PLATFORM_SHIELD
VISetTrapFilter(fapGmHIO_getTrapFilter() ? 1 : 0);
- VISetGamma(fapGmHIO_getGamma());
+ VISetGamma((VIGamma)fapGmHIO_getGamma());
#endif
if (data_80450BE8) {
diff --git a/src/revolution/ai/ai.c b/src/revolution/ai/ai.c
new file mode 100644
index 0000000000..0587b05a66
--- /dev/null
+++ b/src/revolution/ai/ai.c
@@ -0,0 +1,273 @@
+#include <revolution/ai.h>
+#include <revolution/gx.h>
+#include <revolution/os.h>
+#include <revolution/hw_regs.h>
+
+#include "__gx.h"
+
+#ifdef SDK_AUG2010
+#define BUILD_DATE "Aug 23 2010"
+#if DEBUG
+#define BUILD_TIME "17:24:31"
+#else
+#define BUILD_TIME "17:33:06"
+#endif
+#elif SDK_SEP2006
+#define BUILD_DATE "Sep 21 2006"
+#define BUILD_TIME "14:32:13"
+#endif
+
+#ifdef SDK_AUG2010
+#if DEBUG
+const char* __AIVersion = "<< RVL_SDK - AI \tdebug build: "BUILD_DATE" "BUILD_TIME" (0x4302_145) >>";
+#else
+const char* __AIVersion = "<< RVL_SDK - AI \trelease build: "BUILD_DATE" "BUILD_TIME" (0x4302_145) >>";
+#endif
+#elif SDK_SEP2006
+const char* __AIVersion = "<< RVL_SDK - AI \trelease build: "BUILD_DATE" "BUILD_TIME" (0x4200_60422) >>";
+#endif
+
+static AIDCallback __AID_Callback;
+static u8* __CallbackStack;
+static u8* __OldStack;
+static BOOL __AI_init_flag;
+static BOOL __AID_Active;
+static OSTime bound_32KHz;
+static OSTime bound_48KHz;
+static OSTime min_wait;
+static OSTime max_wait;
+static OSTime buffer;
+
+typedef struct {
+ OSTime t_start;
+ OSTime t1;
+ OSTime t2;
+ OSTime t3;
+ OSTime t4;
+ OSTime t_end;
+} STRUCT_TIMELOG;
+STRUCT_TIMELOG __AIprofile;
+
+OSTime __ai_src_time_start;
+OSTime __ai_src_time_end;
+
+// prototypes
+STRUCT_TIMELOG* __ai_src_get_time(void);
+
+static void __AIDHandler(__OSInterrupt interrupt, OSContext* context);
+static void __AICallbackStackSwitch(void* cb);
+static void __AI_SRC_INIT(void);
+
+AIDCallback AIRegisterDMACallback(AIDCallback callback) {
+ AIDCallback old_callback;
+ BOOL old;
+
+ old_callback = __AID_Callback;
+ old = OSDisableInterrupts();
+ __AID_Callback = callback;
+ OSRestoreInterrupts(old);
+ return old_callback;
+}
+
+void AIInitDMA(u32 start_addr, u32 length) {
+ BOOL old;
+
+ old = OSDisableInterrupts();
+ __DSPRegs[24] = (__DSPRegs[24] & ~0x1FFF) | (start_addr >> 16);
+ __DSPRegs[25] = (__DSPRegs[25] & ~0xFFE0) | (start_addr & 0xFFFF);
+ ASSERTMSGLINE(342, (length & 0x1F) == 0, "AIStartDMA: length must be multiple of 32 bytes");
+ __DSPRegs[27] = (__DSPRegs[27] & ~0x7FFF) | (u16)((length >> 5) & 0xFFFF);
+ OSRestoreInterrupts(old);
+}
+
+BOOL AIGetDMAEnableFlag(void) {
+ return (__DSPRegs[27] & (1 << 15)) >> 15;
+}
+
+void AIStartDMA(void) {
+ __DSPRegs[27] = __DSPRegs[27] | 0x8000;
+}
+
+void AIStopDMA(void) {
+ __DSPRegs[27] = __DSPRegs[27] & ~0x8000;
+}
+
+u32 AIGetDMABytesLeft(void) {
+ return (__DSPRegs[29] & 0x7FFF) << 5;
+}
+
+u32 AIGetDMAStartAddr(void) {
+ return (((__DSPRegs[24] & 0x1FFF) << 16) | (__DSPRegs[25] & 0xFFE0));
+}
+
+u32 AIGetDMALength(void) {
+ return (__DSPRegs[27] & 0x7FFF) << 5;
+}
+
+BOOL AICheckInit(void) {
+ return __AI_init_flag;
+}
+
+void AISetDSPSampleRate(u32 rate) {
+ BOOL old;
+
+ if (rate != AIGetDSPSampleRate()) {
+ __AIRegs[0] = (__AIRegs[0] & ~0x40);
+ if (rate == AI_SAMPLERATE_32KHZ) {
+ old = OSDisableInterrupts();
+ __AI_SRC_INIT();
+ __AIRegs[0] |= 0x40;
+ OSRestoreInterrupts(old);
+ }
+ }
+}
+
+u32 AIGetDSPSampleRate(void) {
+ return GET_REG_FIELD(__AIRegs[0], 1, 6) ^ 1;
+}
+
+void AIInit(u8* stack) {
+ u32 reg;
+
+ if (__AI_init_flag != TRUE) {
+ OSRegisterVersion(__AIVersion);
+
+ bound_32KHz = OSNanosecondsToTicks(31524);
+ bound_48KHz = OSNanosecondsToTicks(42024);
+ min_wait = OSNanosecondsToTicks(42000);
+ max_wait = OSNanosecondsToTicks(63000);
+ buffer = OSNanosecondsToTicks(3000);
+
+ reg = __AIRegs[0];
+ reg &= ~(0x1 | 0x4 | 0x10);
+ __AIRegs[0] = reg;
+ __AIRegs[1] = 0;
+ __AIRegs[3] = 0;
+ __AIRegs[0] = (__AIRegs[0] & ~0x20) | (0x1 << 5);
+
+ AISetDSPSampleRate(AI_SAMPLERATE_32KHZ);
+#if DEBUG
+ OSReport("AIInit(): DSP is 32KHz\n");
+#endif
+
+ __AID_Callback = NULL;
+ __CallbackStack = stack;
+
+ ASSERTMSGLINE(663, !stack || ((u32)stack & 7) == 0, "AIInit: stack must be 8-byte aligned");
+
+ __OSSetInterruptHandler(5, __AIDHandler);
+ __OSUnmaskInterrupts(0x04000000);
+ __AI_init_flag = TRUE;
+ }
+}
+
+void AIReset(void) {
+ __AI_init_flag = FALSE;
+}
+
+static void __AIDHandler(__OSInterrupt interrupt, OSContext* context) {
+ OSContext exceptionContext;
+ u16 tmp;
+
+ tmp = __DSPRegs[5];
+ tmp = (tmp & ~0xA0) | 8;
+ __DSPRegs[5] = tmp;
+ OSClearContext(&exceptionContext);
+ OSSetCurrentContext(&exceptionContext);
+ if (__AID_Callback && !__AID_Active) {
+ __AID_Active = TRUE;
+ if (__CallbackStack) {
+ __AICallbackStackSwitch(__AID_Callback);
+ } else {
+ __AID_Callback();
+ }
+ __AID_Active = FALSE;
+ }
+ OSClearContext(&exceptionContext);
+ OSSetCurrentContext(context);
+}
+
+static asm void __AICallbackStackSwitch(__REGISTER void* cb) {
+ fralloc
+ lis r5, __OldStack@ha
+ addi r5, r5, __OldStack@l
+ stw r1, 0x0(r5)
+ lis r5, __CallbackStack@ha
+ addi r5, r5, __CallbackStack@l
+ lwz r1, 0x0(r5)
+ subi r1, r1, 0x8
+ mtlr cb
+ blrl
+ lis r5, __OldStack@ha
+ addi r5, r5, __OldStack@l
+ lwz r1, 0x0(r5)
+ frfree
+ blr
+}
+
+void __AI_SRC_INIT(void) {
+ OSTime rising_32khz = 0;
+ OSTime rising_48khz = 0;
+ OSTime diff = 0;
+ OSTime t1 = 0;
+ OSTime temp;
+ u32 temp0;
+ u32 temp1;
+ u32 done = 0;
+ u32 volume = 0;
+ u32 Init_Cnt = 0;
+ u32 walking = 0;
+
+ walking = 0;
+ Init_Cnt = 0;
+ temp = 0;
+
+#if DEBUG
+ __AIprofile.t_start = OSGetTime();
+#endif
+
+ while (!done) {
+ OLD_SET_REG_FIELD(0, __AIRegs[0], 1, 5, 1);
+ OLD_SET_REG_FIELD(0, __AIRegs[0], 1, 1, 0);
+ OLD_SET_REG_FIELD(0, __AIRegs[0], 1, 0, AI_STREAM_START);
+ temp0 = __AIRegs[2] & 0x7FFFFFFF;
+ while (temp0 == (__AIRegs[2] & 0x7FFFFFFF)) {
+ }
+ rising_32khz = OSGetTime();
+ OLD_SET_REG_FIELD(0, __AIRegs[0], 1, 1, 1);
+ OLD_SET_REG_FIELD(0, __AIRegs[0], 1, 0, AI_STREAM_START);
+ temp1 = __AIRegs[2] & 0x7FFFFFFF;
+ while (temp1 == (__AIRegs[2] & 0x7FFFFFFF)) {
+ }
+ rising_48khz = OSGetTime();
+ diff = rising_48khz - rising_32khz;
+ OLD_SET_REG_FIELD(0, __AIRegs[0], 1, 1, 0);
+ OLD_SET_REG_FIELD(0, __AIRegs[0], 1, 0, AI_STREAM_STOP);
+ if (diff < bound_32KHz - buffer) {
+ temp = min_wait;
+ done = 1;
+ Init_Cnt++;
+ } else if (diff >= bound_32KHz + buffer && diff < bound_48KHz - buffer) {
+ temp = max_wait;
+ done = 1;
+ Init_Cnt++;
+ } else {
+ done = 0;
+ walking = 1;
+ Init_Cnt++;
+ }
+ }
+ while (rising_48khz + temp > OSGetTime()) {
+ }
+#if DEBUG
+ __AIprofile.t_end = OSGetTime();
+#endif
+}
+
+STRUCT_TIMELOG* __ai_src_get_time(void) {
+#if DEBUG
+ return &__AIprofile;
+#else
+ return NULL;
+#endif
+}
diff --git a/src/revolution/aralt/__ar.h b/src/revolution/aralt/__ar.h
new file mode 100644
index 0000000000..850f02aa20
--- /dev/null
+++ b/src/revolution/aralt/__ar.h
@@ -0,0 +1,18 @@
+#ifndef _REVOLUTION_AR_INTERNAL_H_
+#define _REVOLUTION_AR_INTERNAL_H_
+
+#include <revolution/aralt.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void __ARQPopTaskQueueHi(void);
+void __ARQServiceQueueLo(void);
+void __ARQInterruptServiceRoutine(u32);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _REVOLUTION_AR_INTERNAL_H_
diff --git a/src/revolution/aralt/aralt.c b/src/revolution/aralt/aralt.c
new file mode 100644
index 0000000000..8dfd3e2139
--- /dev/null
+++ b/src/revolution/aralt/aralt.c
@@ -0,0 +1,175 @@
+#include <revolution.h>
+#include <revolution/aralt.h>
+#include "fake_tgmath.h"
+
+#include <string.h>
+
+#include "__ar.h"
+
+static void (*__AR_Callback)();
+static u32 __AR_Size;
+static u32 __AR_InternalSize;
+static u32 __ARH_BaseAdr;
+static ARQRequest* __ARQRequestQueueHi;
+static ARQRequest* __ARQRequestQueueLo;
+static ARQRequest* __ARQRequestPendingHi;
+static ARQRequest* __ARQRequestPendingLo;
+static ARQCallback __ARQCallbackHi;
+static ARQCallback __ARQCallbackLo;
+static u32 __ARQChunkSize;
+static BOOL __ARQ_init_flag;
+static BOOL __AR_init_flag;
+
+static u32 __ARALT_AramStartAdr = 0x90000000;
+static u32 __ARH_MemoryTop = 0x90000000;
+
+ARQCallback ARRegisterDMACallback(ARQCallback callback) {
+ ARQCallback old_callback;
+ BOOL old;
+
+ old_callback = __AR_Callback;
+ old = OSDisableInterrupts();
+ __AR_Callback = callback;
+ OSRestoreInterrupts(old);
+ return old_callback;
+}
+
+void ARStartDMA(u32 type, u32 mainmem_addr, u32 aram_addr, u32 length) {
+ OSDisableScheduler();
+
+ if (type == 0) {
+ aram_addr += __ARH_BaseAdr;
+ DCInvalidateRange((u32*)mainmem_addr, length);
+ memcpy((u32*)aram_addr, (u32*)mainmem_addr, length);
+ DCFlushRange((u32*)aram_addr, length);
+ } else if (type == 1) {
+ mainmem_addr += __ARH_BaseAdr;
+ DCFlushRange((u32*)mainmem_addr, length);
+ memcpy((u32*)aram_addr, (u32*)mainmem_addr, length);
+ DCFlushRange((u32*)aram_addr, length);
+ }
+
+ OSEnableScheduler();
+
+ if (__AR_Callback != NULL) {
+ (*__AR_Callback)();
+ }
+}
+
+u32 ARAlloc(u32 length) {
+ u32 tmp;
+ BOOL old;
+
+ old = OSDisableInterrupts();
+ tmp = __ARH_MemoryTop;
+ OSRestoreInterrupts(old);
+
+ __ARH_MemoryTop += length;
+ return tmp - __ARH_BaseAdr;
+}
+
+u32 ARInit(u32* stack_index_addr, u32 num_entries) {
+ BOOL old;
+
+ if (__AR_init_flag) {
+ return ARGetBaseAddress();
+ }
+
+ old = OSDisableInterrupts();
+ __AR_Callback = NULL;
+ __AR_init_flag = TRUE;
+ OSRestoreInterrupts(old);
+
+ {
+ u32 start;
+ u32 end;
+ start = __ARALT_AramStartAdr;
+ end = start + 0x1100000;
+
+ __ARH_MemoryTop = ARGetBaseAddress() + start;
+ __ARH_BaseAdr = start;
+ OSReport("ARInit : Dummy ARAM enabled (RVL), area %p -> %p (size 0x%x)\n", start, end, end - start);
+ __AR_Size = end - start;
+ __AR_InternalSize = __AR_Size;
+ }
+
+ return ARGetBaseAddress();
+}
+
+u32 ARGetBaseAddress(void) {
+ return 0x4000;
+}
+
+u32 ARGetSize(void) {
+ return __AR_Size;
+}
+
+void __ARQPopTaskQueueHi(void) {
+ if (__ARQRequestQueueHi) {
+ if (__ARQRequestQueueHi->type == 0) {
+ ARStartDMA(__ARQRequestQueueHi->type, __ARQRequestQueueHi->source, __ARQRequestQueueHi->dest, __ARQRequestQueueHi->length);
+ } else {
+ ARStartDMA(__ARQRequestQueueHi->type, __ARQRequestQueueHi->dest, __ARQRequestQueueHi->source, __ARQRequestQueueHi->length);
+ }
+ __ARQCallbackHi = __ARQRequestQueueHi->callback;
+ __ARQRequestPendingHi = __ARQRequestQueueHi;
+ __ARQRequestQueueHi = __ARQRequestQueueHi->next;
+ }
+}
+
+void __ARQServiceQueueLo(void) {
+ if (__ARQRequestPendingLo == NULL && __ARQRequestQueueLo) {
+ __ARQRequestPendingLo = __ARQRequestQueueLo;
+ __ARQRequestQueueLo = __ARQRequestQueueLo->next;
+ }
+
+ if (__ARQRequestPendingLo) {
+ if (__ARQRequestPendingLo->length <= __ARQChunkSize) {
+ if (__ARQRequestPendingLo->type == 0) {
+ ARStartDMA(__ARQRequestPendingLo->type, __ARQRequestPendingLo->source, __ARQRequestPendingLo->dest, __ARQRequestPendingLo->length);
+ } else {
+ ARStartDMA(__ARQRequestPendingLo->type, __ARQRequestPendingLo->dest, __ARQRequestPendingLo->source, __ARQRequestPendingLo->length);
+ }
+ __ARQCallbackLo = __ARQRequestPendingLo->callback;
+ } else if (__ARQRequestPendingLo->type == 0) {
+ ARStartDMA(__ARQRequestPendingLo->type, __ARQRequestPendingLo->source, __ARQRequestPendingLo->dest, __ARQChunkSize);
+ } else {
+ ARStartDMA(__ARQRequestPendingLo->type, __ARQRequestPendingLo->dest, __ARQRequestPendingLo->source, __ARQChunkSize);
+ }
+
+ __ARQRequestPendingLo->length -= __ARQChunkSize;
+ __ARQRequestPendingLo->source += __ARQChunkSize;
+ __ARQRequestPendingLo->dest += __ARQChunkSize;
+ }
+}
+
+void __ARQInterruptServiceRoutine(u32) {
+ if (__ARQCallbackHi) {
+ __ARQCallbackHi((u32)__ARQRequestPendingHi);
+ __ARQRequestPendingHi = NULL;
+ __ARQCallbackHi = NULL;
+ } else if (__ARQCallbackLo) {
+ __ARQCallbackLo((u32)__ARQRequestPendingLo);
+ __ARQRequestPendingLo = NULL;
+ __ARQCallbackLo = NULL;
+ }
+
+ __ARQPopTaskQueueHi();
+
+ if (__ARQRequestPendingHi == NULL) {
+ __ARQServiceQueueLo();
+ }
+}
+
+void ARQInit(void) {
+ if (__ARQ_init_flag != TRUE) {
+ __ARQRequestQueueHi = __ARQRequestQueueLo = NULL;
+ __ARQChunkSize = 0x1000;
+ ARRegisterDMACallback(__ARQInterruptServiceRoutine);
+ __ARQRequestPendingHi = NULL;
+ __ARQRequestPendingLo = NULL;
+ __ARQCallbackHi = NULL;
+ __ARQCallbackLo = NULL;
+ __ARQ_init_flag = TRUE;
+ }
+}
diff --git a/src/revolution/base/PPCArch.c b/src/revolution/base/PPCArch.c
new file mode 100644
index 0000000000..a8119de74c
--- /dev/null
+++ b/src/revolution/base/PPCArch.c
@@ -0,0 +1,306 @@
+#include <revolution.h>
+#include <revolution/base/PPCArch.h>
+
+asm u32 PPCMfmsr() {
+ nofralloc
+ mfmsr r3
+ blr
+}
+
+asm void PPCMtmsr(__REGISTER u32 newMSR) {
+ nofralloc
+ mtmsr newMSR
+ blr
+}
+
+asm u32 PPCOrMsr(__REGISTER u32 value) {
+ nofralloc
+ mfmsr r4
+ or value, r4, value
+ blr
+}
+
+asm u32 PPCAndMsr(__REGISTER u32 value) {
+ nofralloc
+ mfmsr r4
+ and value, r4, value
+ blr
+}
+
+asm u32 PPCAndCMsr(__REGISTER u32 value) {
+ nofralloc
+ mfmsr r4
+ andc value, r4, value
+ blr
+}
+
+asm u32 PPCMfhid0() {
+ nofralloc
+ mfspr r3, HID0
+ blr
+}
+
+asm void PPCMthid0(__REGISTER u32 newHID0) {
+ nofralloc
+ mtspr HID0, newHID0
+ blr
+}
+
+asm u32 PPCMfhid1() {
+ nofralloc
+ mfspr r3, HID1
+ blr
+}
+
+asm u32 PPCMfl2cr() {
+ nofralloc
+ mfspr r3, L2CR
+ blr
+}
+
+asm void PPCMtl2cr(__REGISTER u32 newL2cr) {
+ nofralloc
+ mtspr L2CR, newL2cr
+ blr
+}
+
+asm void PPCMtdec(__REGISTER u32 newDec) {
+ nofralloc
+ mtdec newDec
+ blr
+}
+
+asm u32 PPCMfdec() {
+ nofralloc
+ mfdec r3
+ blr
+}
+
+asm void PPCSync() {
+ nofralloc
+ sc
+ blr
+}
+
+asm void PPCEieio() {
+ nofralloc
+ mfmsr r5
+ rlwinm r6, r5, 0, 17, 15
+ mtmsr r6
+ mfspr r3, HID0
+ ori r4, r3, 0x8
+ mtspr HID0, r4
+ isync
+ eieio
+ isync
+ mtspr HID0, r3
+ mtmsr r5
+ isync
+ blr
+}
+
+asm void PPCHalt() {
+ nofralloc
+ sync
+loop:
+ nop
+ li r3, 0
+ nop
+ b loop
+}
+
+asm u32 PPCMfmmcr0() {
+ nofralloc
+ mfspr r3, MMCR0
+ blr
+}
+
+asm void PPCMtmmcr0(__REGISTER u32 newMmcr0) {
+ nofralloc
+ mtspr MMCR0, newMmcr0
+ blr
+}
+
+asm u32 PPCMfmmcr1() {
+ nofralloc
+ mfspr r3, MMCR1
+ blr
+}
+
+asm void PPCMtmmcr1(__REGISTER u32 newMmcr1) {
+ nofralloc
+ mtspr MMCR1, newMmcr1
+ blr
+}
+
+asm u32 PPCMfpmc1() {
+ nofralloc
+ mfspr r3, PMC1
+ blr
+}
+
+asm void PPCMtpmc1(__REGISTER u32 newPmc1) {
+ nofralloc
+ mtspr PMC1, newPmc1
+ blr
+}
+
+asm u32 PPCMfpmc2() {
+ nofralloc
+ mfspr r3, PMC2
+ blr
+}
+
+asm void PPCMtpmc2(__REGISTER u32 newPmc2) {
+ nofralloc
+ mtspr PMC2, newPmc2
+ blr
+}
+
+asm u32 PPCMfpmc3() {
+ nofralloc
+ mfspr r3, PMC3
+ blr
+}
+
+asm void PPCMtpmc3(__REGISTER u32 newPmc3) {
+ nofralloc
+ mtspr PMC3, newPmc3
+ blr
+}
+
+asm u32 PPCMfpmc4() {
+ nofralloc
+ mfspr r3, PMC4
+ blr
+}
+
+asm void PPCMtpmc4(__REGISTER u32 newPmc4) {
+ nofralloc
+ mtspr PMC4, newPmc4
+ blr
+}
+
+asm u32 PPCMfsia() {
+ nofralloc
+ mfspr r3, SIA
+ blr
+}
+
+asm void PPCMtsia(__REGISTER u32 newSia) {
+ nofralloc
+ mtspr SIA, newSia
+ blr
+}
+
+u32 PPCMffpscr() {
+ union FpscrUnion m;
+
+ asm {
+ mffs fp31
+ stfd fp31, m.f;
+ }
+
+ return m.u.fpscr;
+}
+
+void PPCMtfpscr(__REGISTER u32 newFPSCR) {
+ union FpscrUnion m;
+
+ asm {
+ li r4, 0
+ stw r4, m.u.fpscr_pad;
+ stw newFPSCR, m.u.fpscr
+ lfd fp31, m.f
+ mtfsf 0xff, fp31
+ }
+}
+
+asm u32 PPCMfhid2() {
+ nofralloc
+ mfspr r3, HID2
+ blr
+}
+
+asm void PPCMthid2(__REGISTER u32 newhid2) {
+ nofralloc
+ mtspr HID2, newhid2
+ blr
+}
+
+asm u32 PPCMfwpar() {
+ nofralloc
+ sync
+ mfspr r3, WPAR
+ blr
+}
+
+asm void PPCMtwpar(__REGISTER u32 newwpar) {
+ nofralloc
+ mtspr WPAR, newwpar
+ blr
+}
+
+asm u32 PPCMfdmaU() {
+ nofralloc
+ mfspr r3, DMA_U
+ blr
+}
+
+asm u32 PPCMfdmaL() {
+ nofralloc
+ mfspr r3, DMA_L
+ blr
+}
+
+asm void PPCMtdmaU(__REGISTER u32 newdmau) {
+ nofralloc
+ mtspr DMA_U, newdmau
+ blr
+}
+
+asm void PPCMtdmaL(__REGISTER u32 newdmal) {
+ nofralloc
+ mtspr DMA_L, newdmal
+ blr
+}
+
+asm u32 PPCMfpvr() {
+ nofralloc
+ mfspr r3, PVR
+ blr
+}
+
+void PPCEnableSpeculation(void) {
+ PPCMthid0(PPCMfhid0() & ~HID0_SPD);
+}
+
+void PPCDisableSpeculation(void) {
+ PPCMthid0(PPCMfhid0() | HID0_SPD);
+}
+
+asm void PPCSetFpIEEEMode() {
+ nofralloc
+ mtfsb0 29
+ blr
+}
+
+asm void PPCSetFpNonIEEEMode() {
+ nofralloc
+ mtfsb1 29
+ blr
+}
+
+void PPCMthid4(__REGISTER u32 newhid4) {
+ if (newhid4 & 0x80000000) {
+ asm {
+ mtspr 1011, newhid4
+ }
+ } else {
+ OSReport("H4A should not be cleared because of Broadway errata.\n");
+ asm {
+ oris newhid4, newhid4, 0x8000
+ mtspr 1011, newhid4
+ }
+ }
+}
diff --git a/src/revolution/base/PPCPm.c b/src/revolution/base/PPCPm.c
new file mode 100644
index 0000000000..1f27bc663f
--- /dev/null
+++ b/src/revolution/base/PPCPm.c
@@ -0,0 +1,34 @@
+#include <revolution.h>
+#include <revolution/base/PPCArch.h>
+
+void PMBegin(void) {
+ PPCMtmmcr0(0);
+ PPCMtmmcr1(0);
+ PPCMtpmc1(0);
+ PPCMtpmc2(0);
+ PPCMtpmc3(0);
+ PPCMtpmc4(0);
+ PPCMtmmcr0(0x4F);
+ PPCMtmmcr1(0x78800000);
+}
+
+void PMEnd(void) {
+ PPCMtmmcr0(0);
+ PPCMtmmcr1(0);
+}
+
+void PMCycles(void) {
+ PPCMfpmc1();
+}
+
+void PML1FetchMisses(void) {
+ PPCMfpmc2();
+}
+
+void PML1MissCycles(void) {
+ PPCMfpmc3();
+}
+
+void PMInstructions(void) {
+ PPCMfpmc4();
+}
diff --git a/src/revolution/dsp/__dsp.h b/src/revolution/dsp/__dsp.h
new file mode 100644
index 0000000000..08e654ff35
--- /dev/null
+++ b/src/revolution/dsp/__dsp.h
@@ -0,0 +1,27 @@
+#ifndef _REVOLUTION_DSP_INTERNAL_H_
+#define _REVOLUTION_DSP_INTERNAL_H_
+
+#include <revolution/dsp.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern DSPTaskInfo* __DSP_first_task;
+extern DSPTaskInfo* __DSP_last_task;
+extern DSPTaskInfo* __DSP_curr_task;
+extern DSPTaskInfo* __DSP_tmp_task;
+
+DECL_WEAK void __DSPHandler(__OSInterrupt, OSContext*);
+void __DSP_exec_task(DSPTaskInfo*, DSPTaskInfo*);
+void __DSP_boot_task(DSPTaskInfo*);
+void __DSP_insert_task(DSPTaskInfo*);
+void __DSP_add_task(DSPTaskInfo* task);
+void __DSP_remove_task(DSPTaskInfo* task);
+void __DSP_debug_printf(const char* fmt, ...);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/revolution/dsp/dsp.c b/src/revolution/dsp/dsp.c
new file mode 100644
index 0000000000..a023089b59
--- /dev/null
+++ b/src/revolution/dsp/dsp.c
@@ -0,0 +1,194 @@
+#include <stddef.h>
+#include <revolution.h>
+#include <revolution/hw_regs.h>
+
+#include "__dsp.h"
+
+#ifdef SDK_AUG2010
+#define BUILD_DATE "Aug 23 2010"
+#if DEBUG
+#define BUILD_TIME "17:24:48"
+#else
+#define BUILD_TIME "17:33:06"
+#endif
+#elif SDK_SEP2006
+#define BUILD_DATE "Sep 21 2006"
+#define BUILD_TIME "14:32:13"
+#endif
+
+#ifdef SDK_AUG2010
+#if DEBUG
+const char* __DSPVersion = "<< RVL_SDK - DSP \tdebug build: "BUILD_DATE" "BUILD_TIME" (0x4302_145) >>";
+#else
+const char* __DSPVersion = "<< RVL_SDK - DSP \trelease build: "BUILD_DATE" "BUILD_TIME" (0x4302_145) >>";
+#endif
+#elif SDK_SEP2006
+const char* __DSPVersion = "<< RVL_SDK - DSP \trelease build: "BUILD_DATE" "BUILD_TIME" (0x4200_60422) >>";
+#endif
+
+extern DSPTaskInfo* __DSP_rude_task;
+extern int __DSP_rude_task_pending;
+
+static BOOL __DSP_init_flag;
+
+
+u32 DSPCheckMailToDSP(void) {
+ return (__DSPRegs[0] & (1 << 15)) >> 15;
+}
+
+u32 DSPCheckMailFromDSP(void) {
+ return (__DSPRegs[2] & (1 << 15)) >> 15;
+}
+
+u32 DSPReadCPUToDSPMbox(void) {
+ return (__DSPRegs[0] << 16) | __DSPRegs[1];
+}
+
+u32 DSPReadMailFromDSP(void) {
+ return (__DSPRegs[2] << 16) | __DSPRegs[3];
+}
+
+void DSPSendMailToDSP(u32 mail) {
+ __DSPRegs[0] = mail >> 16;
+ __DSPRegs[1] = mail & 0xFFFF;
+}
+
+void DSPAssertInt(void) {
+ BOOL old;
+ u16 tmp;
+
+ old = OSDisableInterrupts();
+ tmp = __DSPRegs[5];
+ tmp = (tmp & ~0xA8) | 2;
+ __DSPRegs[5] = tmp;
+ OSRestoreInterrupts(old);
+}
+
+void DSPInit(void) {
+ BOOL old;
+ u16 tmp;
+
+ __DSP_debug_printf("DSPInit(): Build Date: %s %s\n", BUILD_DATE, BUILD_TIME);
+
+ if (__DSP_init_flag == 1)
+ return;
+
+ OSRegisterVersion(__DSPVersion);
+
+ old = OSDisableInterrupts();
+ __OSSetInterruptHandler(7, __DSPHandler);
+ __OSUnmaskInterrupts(OS_INTERRUPTMASK_DSP_DSP);
+
+ tmp = __DSPRegs[5];
+ tmp = (tmp & ~0xA8) | 0x800;
+ __DSPRegs[5] = tmp;
+
+ tmp = __DSPRegs[5];
+ __DSPRegs[5] = tmp = tmp & ~0xAC;
+
+ __DSP_first_task = __DSP_last_task = __DSP_curr_task = __DSP_tmp_task = NULL;
+ __DSP_init_flag = 1;
+
+ OSRestoreInterrupts(old);
+}
+
+BOOL DSPCheckInit(void) {
+ return __DSP_init_flag;
+}
+
+void DSPReset(void) {
+ BOOL old;
+ u16 tmp;
+
+ old = OSDisableInterrupts();
+ tmp = __DSPRegs[5];
+ tmp = (tmp & ~0xA8) | 0x800 | 1;
+ __DSPRegs[5] = tmp;
+ __DSP_init_flag = 0;
+ OSRestoreInterrupts(old);
+}
+
+void DSPHalt(void) {
+ BOOL old;
+ u16 tmp;
+
+ old = OSDisableInterrupts();
+ tmp = __DSPRegs[5];
+ tmp = (tmp & ~0xA8) | 4;
+ __DSPRegs[5] = tmp;
+ OSRestoreInterrupts(old);
+}
+
+void DSPUnhalt(void) {
+ BOOL old;
+ u16 tmp;
+
+ old = OSDisableInterrupts();
+ tmp = __DSPRegs[5];
+ tmp = (tmp & ~0xAC);
+ __DSPRegs[5] = tmp;
+ OSRestoreInterrupts(old);
+}
+
+u32 DSPGetDMAStatus(void) {
+ return (__DSPRegs[5] & (1 << 9));
+}
+
+DSPTaskInfo* DSPAddTask(DSPTaskInfo* task) {
+ BOOL old;
+
+ ASSERTMSGLINE(556, __DSP_init_flag == 1, "DSPAddTask(): DSP driver not initialized!\n");
+
+ old = OSDisableInterrupts();
+
+ __DSP_insert_task(task);
+ task->state = 0;
+ task->flags = 1;
+
+ OSRestoreInterrupts(old);
+ if (task == __DSP_first_task)
+ __DSP_boot_task(task);
+ return task;
+}
+
+DSPTaskInfo* DSPCancelTask(DSPTaskInfo* task) {
+ BOOL old;
+
+ ASSERTMSGLINE(592, __DSP_init_flag == 1, "DSPCancelTask(): DSP driver not initialized!\n");
+
+ old = OSDisableInterrupts();
+
+ task->flags |= 2;
+
+ OSRestoreInterrupts(old);
+ return task;
+}
+
+DSPTaskInfo* DSPAssertTask(DSPTaskInfo* task) {
+ s32 old;
+
+ ASSERTMSGLINE(623, __DSP_init_flag == 1, "DSPAssertTask(): DSP driver not initialized!\n");
+ ASSERTMSGLINE(624, task->flags & 1, "DSPAssertTask(): Specified task not in active task list!\n");
+
+ old = OSDisableInterrupts();
+
+ if (__DSP_curr_task == task) {
+ __DSP_rude_task = task;
+ __DSP_rude_task_pending = 1;
+ OSRestoreInterrupts(old);
+ return task;
+ }
+
+ if (task->priority < __DSP_curr_task->priority) {
+ __DSP_rude_task = task;
+ __DSP_rude_task_pending = 1;
+ if (__DSP_curr_task->state == 1) {
+ DSPAssertInt();
+ }
+ OSRestoreInterrupts(old);
+ return task;
+ }
+
+ OSRestoreInterrupts(old);
+ return NULL;
+}
diff --git a/src/revolution/dsp/dsp_debug.c b/src/revolution/dsp/dsp_debug.c
new file mode 100644
index 0000000000..4048cbe4e1
--- /dev/null
+++ b/src/revolution/dsp/dsp_debug.c
@@ -0,0 +1,9 @@
+#include <revolution/dsp.h>
+
+#include "__dsp.h"
+
+void __DSP_debug_printf(const char* fmt, ...) {}
+
+DSPTaskInfo* __DSPGetCurrentTask(void) {
+ return __DSP_curr_task;
+}
diff --git a/src/revolution/dsp/dsp_task.c b/src/revolution/dsp/dsp_task.c
new file mode 100644
index 0000000000..7ea99ba8e6
--- /dev/null
+++ b/src/revolution/dsp/dsp_task.c
@@ -0,0 +1,363 @@
+#include <stddef.h>
+
+#include <revolution.h>
+#include <revolution/hw_regs.h>
+
+#include "__dsp.h"
+
+static u32 t0, t1, t2; // unused
+
+DSPTaskInfo* __DSP_curr_task;
+DSPTaskInfo* __DSP_first_task;
+DSPTaskInfo* __DSP_last_task;
+DSPTaskInfo* __DSP_tmp_task;
+DSPTaskInfo* __DSP_rude_task;
+int __DSP_rude_task_pending;
+
+void __DSPHandler(__OSInterrupt intr, OSContext* context) {
+ u8 unused[4];
+ OSContext exceptionContext;
+ u16 tmp;
+ u32 mail;
+
+ tmp = __DSPRegs[5];
+ tmp = (tmp & ~0x28) | 0x80;
+ __DSPRegs[5] = tmp;
+
+ OSClearContext(&exceptionContext);
+ OSSetCurrentContext(&exceptionContext);
+ ASSERTMSGLINE(143, __DSP_curr_task != NULL, "__DSPHandler(): No current task! Someone set us up the bomb!\n");
+
+ while (DSPCheckMailFromDSP() == 0)
+ ;
+
+ mail = DSPReadMailFromDSP();
+ if ((__DSP_curr_task->flags & (1<<(31-0x1E))) && (mail + 0x232F0000) == 2)
+ mail = 0xDCD10003;
+
+ switch (mail) {
+ case 0xDCD10000:
+ __DSP_curr_task->state = 1;
+ if (__DSP_curr_task->init_cb != NULL)
+ __DSP_curr_task->init_cb(__DSP_curr_task);
+ break;
+ case 0xDCD10001:
+ __DSP_curr_task->state = 1;
+ if (__DSP_curr_task->res_cb != NULL)
+ __DSP_curr_task->res_cb(__DSP_curr_task);
+ break;
+ case 0xDCD10002:
+ if (__DSP_rude_task_pending) {
+ if (__DSP_curr_task == __DSP_rude_task) {
+ DSPSendMailToDSP(0xCDD10003);
+ while (DSPCheckMailToDSP() != 0)
+ ;
+ __DSP_rude_task = NULL;
+ __DSP_rude_task_pending = 0;
+
+ if (__DSP_curr_task->res_cb != NULL)
+ __DSP_curr_task->res_cb(__DSP_curr_task);
+ } else {
+ DSPSendMailToDSP(0xCDD10001);
+ while (DSPCheckMailToDSP() != 0)
+ ;
+ __DSP_exec_task(__DSP_curr_task, __DSP_rude_task);
+ __DSP_curr_task->state = 2;
+ __DSP_curr_task = __DSP_rude_task;
+ __DSP_rude_task = NULL;
+ __DSP_rude_task_pending = 0;
+ }
+ } else {
+ if (__DSP_curr_task->next == NULL) {
+ if (__DSP_curr_task == __DSP_first_task) {
+ DSPSendMailToDSP(0xCDD10003);
+ while (DSPCheckMailToDSP() != 0)
+ ;
+ if (__DSP_curr_task->res_cb != NULL)
+ __DSP_curr_task->res_cb(__DSP_curr_task);
+ } else {
+ DSPSendMailToDSP(0xCDD10001);
+ while (DSPCheckMailToDSP() != 0)
+ ;
+ __DSP_exec_task(__DSP_curr_task, __DSP_first_task);
+ __DSP_curr_task->state = 2;
+ __DSP_curr_task = __DSP_first_task;
+ }
+ } else {
+ DSPSendMailToDSP(0xCDD10001);
+ while (DSPCheckMailToDSP() != 0)
+ ;
+ __DSP_exec_task(__DSP_curr_task, __DSP_curr_task->next);
+ __DSP_curr_task->state = 2;
+ __DSP_curr_task = __DSP_curr_task->next;
+ }
+ }
+ break;
+ case 0xDCD10003:
+ if (__DSP_rude_task_pending) {
+ if (__DSP_curr_task->done_cb != NULL)
+ __DSP_curr_task->done_cb(__DSP_curr_task);
+
+ DSPSendMailToDSP(0xCDD10001);
+ while (DSPCheckMailToDSP() != 0)
+ ;
+ __DSP_exec_task(NULL, __DSP_rude_task);
+ __DSP_remove_task(__DSP_curr_task);
+ __DSP_curr_task = __DSP_rude_task;
+ __DSP_rude_task = NULL;
+ __DSP_rude_task_pending = 0;
+ } else {
+ if (__DSP_curr_task->next == NULL) {
+ if (__DSP_curr_task == __DSP_first_task) {
+ if (__DSP_curr_task->done_cb != NULL)
+ __DSP_curr_task->done_cb(__DSP_curr_task);
+
+ DSPSendMailToDSP(0xCDD10002);
+ while (DSPCheckMailToDSP() != 0)
+ ;
+ __DSP_curr_task->state = 3;
+ __DSP_remove_task(__DSP_curr_task);
+ } else {
+ if (__DSP_curr_task->done_cb != NULL)
+ __DSP_curr_task->done_cb(__DSP_curr_task);
+
+ DSPSendMailToDSP(0xCDD10001);
+ while (DSPCheckMailToDSP() != 0)
+ ;
+ __DSP_curr_task->state = 3;
+ __DSP_exec_task(NULL, __DSP_first_task);
+ __DSP_curr_task = __DSP_first_task;
+ __DSP_remove_task(__DSP_last_task);
+ }
+ } else {
+ if (__DSP_curr_task->done_cb != NULL)
+ __DSP_curr_task->done_cb(__DSP_curr_task);
+
+ DSPSendMailToDSP(0xCDD10001);
+ while (DSPCheckMailToDSP() != 0)
+ ;
+ __DSP_curr_task->state = 3;
+ __DSP_exec_task(NULL, __DSP_curr_task->next);
+ __DSP_curr_task = __DSP_curr_task->next;
+ __DSP_remove_task(__DSP_curr_task->prev);
+ }
+ }
+ break;
+ case 0xDCD10004:
+ if (__DSP_curr_task->req_cb != NULL)
+ __DSP_curr_task->req_cb(__DSP_curr_task);
+ break;
+ default:
+ ASSERTMSGLINEV(519, 0, "__DSPHandler(): Unknown msg from DSP 0x%08X - task sync failed!\n", mail);
+ }
+
+ OSClearContext(&exceptionContext);
+ OSSetCurrentContext(context);
+}
+
+void __DSP_exec_task(DSPTaskInfo* curr, DSPTaskInfo* next) {
+ ASSERTMSGLINE(555, next != NULL, "__DSP_exec_task(): NULL task. It is to weep.\n");
+
+ if (curr != NULL) {
+ DSPSendMailToDSP((u32)curr->dram_mmem_addr);
+ while (DSPCheckMailToDSP() != 0)
+ ;
+ DSPSendMailToDSP(curr->dram_length);
+ while (DSPCheckMailToDSP() != 0)
+ ;
+ DSPSendMailToDSP(curr->dram_addr);
+ while (DSPCheckMailToDSP() != 0)
+ ;
+ } else {
+ DSPSendMailToDSP(0);
+ while (DSPCheckMailToDSP() != 0)
+ ;
+ DSPSendMailToDSP(0);
+ while (DSPCheckMailToDSP() != 0)
+ ;
+ DSPSendMailToDSP(0);
+ while (DSPCheckMailToDSP() != 0)
+ ;
+ }
+
+ DSPSendMailToDSP((u32)next->iram_mmem_addr);
+ while (DSPCheckMailToDSP() != 0)
+ ;
+
+ DSPSendMailToDSP(next->iram_length);
+ while (DSPCheckMailToDSP() != 0)
+ ;
+
+ DSPSendMailToDSP(next->iram_addr);
+ while (DSPCheckMailToDSP() != 0)
+ ;
+
+ if (next->state == 0) {
+ DSPSendMailToDSP(next->dsp_init_vector);
+ while (DSPCheckMailToDSP() != 0)
+ ;
+ DSPSendMailToDSP(0);
+ while (DSPCheckMailToDSP() != 0)
+ ;
+ DSPSendMailToDSP(0);
+ while (DSPCheckMailToDSP() != 0)
+ ;
+ DSPSendMailToDSP(0);
+ while (DSPCheckMailToDSP() != 0)
+ ;
+ } else {
+ DSPSendMailToDSP(next->dsp_resume_vector);
+ while (DSPCheckMailToDSP() != 0)
+ ;
+
+ DSPSendMailToDSP((u32)next->dram_mmem_addr);
+ while (DSPCheckMailToDSP() != 0)
+ ;
+
+ DSPSendMailToDSP(next->dram_length);
+ while (DSPCheckMailToDSP() != 0)
+ ;
+
+ DSPSendMailToDSP(next->dram_addr);
+ while (DSPCheckMailToDSP() != 0)
+ ;
+ }
+}
+
+void __DSP_boot_task(DSPTaskInfo* task) {
+ volatile u32 mail;
+
+ ASSERTMSGLINE(637, task != NULL, "__DSP_boot_task(): NULL task!\n");
+ while (DSPCheckMailFromDSP() == 0)
+ ;
+
+ mail = DSPReadMailFromDSP();
+ ASSERTMSGLINEV(643, mail == 0x8071FEED, "__DSP_boot_task(): Failed to sync DSP on boot! (0x%08X)\n", mail);
+
+ DSPSendMailToDSP(0x80F3A001);
+ while (DSPCheckMailToDSP() != 0)
+ ;
+
+ DSPSendMailToDSP((u32)task->iram_mmem_addr);
+ while (DSPCheckMailToDSP() != 0)
+ ;
+
+ DSPSendMailToDSP(0x80F3C002);
+ while (DSPCheckMailToDSP() != 0)
+ ;
+
+ DSPSendMailToDSP(task->iram_addr & 0xFFFF);
+ while (DSPCheckMailToDSP() != 0)
+ ;
+
+ DSPSendMailToDSP(0x80F3A002);
+ while (DSPCheckMailToDSP() != 0)
+ ;
+
+ DSPSendMailToDSP(task->iram_length);
+ while (DSPCheckMailToDSP() != 0)
+ ;
+
+ DSPSendMailToDSP(0x80F3B002);
+ while (DSPCheckMailToDSP() != 0)
+ ;
+
+ DSPSendMailToDSP(0);
+ while (DSPCheckMailToDSP() != 0)
+ ;
+
+ DSPSendMailToDSP(0x80F3D001);
+ while (DSPCheckMailToDSP() != 0)
+ ;
+
+ DSPSendMailToDSP(task->dsp_init_vector & 0xFFFF);
+ while (DSPCheckMailToDSP() != 0)
+ ;
+
+ __DSP_debug_printf("DSP is booting task: 0x%08X\n", (u32)task);
+ __DSP_debug_printf("__DSP_boot_task() : IRAM MMEM ADDR: 0x%08X\n", (u32)task->iram_mmem_addr);
+ __DSP_debug_printf("__DSP_boot_task() : IRAM DSP ADDR : 0x%08X\n", task->iram_addr);
+ __DSP_debug_printf("__DSP_boot_task() : IRAM LENGTH : 0x%08X\n", task->iram_length);
+ __DSP_debug_printf("__DSP_boot_task() : DRAM MMEM ADDR: 0x%08X\n", task->dram_length);
+ __DSP_debug_printf("__DSP_boot_task() : Start Vector : 0x%08X\n", task->dsp_init_vector);
+}
+
+void __DSP_insert_task(DSPTaskInfo* task) {
+ DSPTaskInfo* temp;
+
+ if (__DSP_first_task == NULL) {
+ __DSP_curr_task = task;
+ __DSP_first_task = __DSP_last_task = task;
+ task->next = task->prev = NULL;
+ return;
+ }
+
+ temp = __DSP_first_task;
+ while (temp != NULL) {
+ if (task->priority < temp->priority) {
+ task->prev = temp->prev;
+ temp->prev = task;
+ task->next = temp;
+ if (task->prev == NULL)
+ __DSP_first_task = task;
+ else
+ task->prev->next = task;
+ break;
+ }
+ temp = temp->next;
+ }
+
+ if (temp == NULL) {
+ __DSP_last_task->next = task;
+ task->next = NULL;
+ task->prev = __DSP_last_task;
+ __DSP_last_task = task;
+ }
+}
+
+void __DSP_add_task(DSPTaskInfo* task) {
+ ASSERTMSGLINE(771, task != NULL, "__DSP_add_task(): Why are you adding a NULL task?\n");
+
+ if (__DSP_last_task == NULL) {
+ __DSP_curr_task = task;
+ __DSP_last_task = task;
+ __DSP_first_task = task;
+ task->next = task->prev = NULL;
+ } else {
+ __DSP_last_task->next = task;
+ task->next = NULL;
+ task->prev = __DSP_last_task;
+ __DSP_last_task = task;
+ }
+
+ task->state = 0;
+ __DSP_debug_printf("__DSP_add_task() : Added task : 0x%08X\n", (u32)task);
+}
+
+void __DSP_remove_task(DSPTaskInfo* task) {
+ ASSERTMSGLINE(816, task != NULL, "__DSP_remove_task(): NULL task! Why? WHY?!?!\n");
+ task->flags = 0;
+ task->state = 3;
+
+ if (__DSP_first_task == task) {
+ if (task->next != NULL) {
+ __DSP_first_task = task->next;
+ task->next->prev = NULL;
+ return;
+ }
+
+ __DSP_first_task = __DSP_last_task = __DSP_curr_task = NULL;
+ return;
+ }
+
+ if (__DSP_last_task == task) {
+ __DSP_last_task = task->prev;
+ task->prev->next = NULL;
+ __DSP_curr_task = __DSP_first_task;
+ return;
+ }
+
+ __DSP_curr_task = task->next;
+ task->prev->next = task->next;
+ task->next->prev = task->prev;
+}
diff --git a/src/revolution/esp/esp.c b/src/revolution/esp/esp.c
new file mode 100644
index 0000000000..68899e2598
--- /dev/null
+++ b/src/revolution/esp/esp.c
@@ -0,0 +1,320 @@
+#include <revolution/esp.h>
+#include <revolution/ipc/ipcclt.h>
+#include <revolution/private/iosrestypes.h>
+
+static s32 __esFd = -1;
+
+s32 ESP_InitLib(void) {
+ s32 ret = 0;
+
+ if (__esFd < 0) {
+ __esFd = IOS_Open("/dev/es", 0);
+
+ if (__esFd < 0) {
+ ret = __esFd;
+ }
+ }
+
+ return ret;
+}
+
+s32 ESP_CloseLib(void) {
+ s32 ret = 0;
+
+ if (__esFd >= 0) {
+ ret = IOS_Close(__esFd);
+ if (ret == 0) {
+ __esFd = -1;
+ }
+ }
+
+ return ret;
+}
+
+s32 ESP_LaunchTitle(u64 titleID, ESTicketView* pTicketView) {
+ s32 ret = 0;
+ u8 buf[256] ATTRIBUTE_ALIGN(32);
+ IOSIoVector* vec = (IOSIoVector*)(buf + 208);
+ u64* id = (u64*)buf;
+
+ if (__esFd < 0) {
+ ret = -0x3F9;
+ goto out;
+ }
+
+ // check if ptr is aligned to 0x20
+ if ((u32)pTicketView & 31) {
+ ret = -0x3F9;
+ goto out;
+ }
+
+ *id = titleID;
+ vec[0].base = (u8*)id;
+ vec[0].length = 8;
+ vec[1].base = (u8*)pTicketView;
+ vec[1].length = 0xD8;
+
+ ret = IOS_IoctlvReboot(__esFd, 8, 2, 0, vec);
+ __esFd = -1;
+
+out:
+ return ret;
+}
+
+s32 ESP_GetTicketViews(ESTitleId titleId, ESTicketView* ticketViewList, u32* ticketViewCnt) {
+ s32 rv = 0;
+ u8 __esBuf[256] ATTRIBUTE_ALIGN(32);
+ IOSIoVector* v = (IOSIoVector*)(__esBuf + 256 - 6 * sizeof(IOSIoVector));
+ ESTitleId* p1 = (ESTitleId*)__esBuf;
+ u32* p2 = (u32*)(__esBuf + 32);
+
+ if (__esFd < 0 || ticketViewCnt == NULL) {
+ rv = -1017;
+ goto out;
+ }
+
+ if (((u32)ticketViewList & 31)) {
+ rv = -1017;
+ goto out;
+ }
+
+ *p1 = titleId;
+ if (ticketViewList == NULL) {
+ v[0].base = (u8*)p1;
+ v[0].length = sizeof(ESTitleId);
+ v[1].base = (u8*)p2;
+ v[1].length = 4;
+ rv = IOS_Ioctlv(__esFd, 18, 1, 1, v);
+
+ if (rv == 0) {
+ *ticketViewCnt = *p2;
+ }
+ } else {
+ if (*ticketViewCnt == 0) {
+ rv = -1017;
+ goto out;
+ }
+
+ *p2 = *ticketViewCnt;
+ v[0].base = (u8*)p1;
+ v[0].length = sizeof(ESTitleId);
+ v[1].base = (u8*)p2;
+ v[1].length = 4;
+ v[2].base = (u8*)ticketViewList;
+ v[2].length = sizeof(ESTicketView) * (*ticketViewCnt);
+ rv = IOS_Ioctlv(__esFd, 19, 2, 1, v);
+ }
+
+out:
+ return rv;
+}
+
+s32 ESP_DiGetTicketView(const void* ticket, ESTicketView* ticketView) {
+ s32 rv = 0;
+ u8 __esBuf[256] ATTRIBUTE_ALIGN(32);
+ IOSIoVector* v = (IOSIoVector*)(__esBuf + 256 - 6 * sizeof(IOSIoVector));
+
+ if (__esFd < 0 || ticketView == NULL) {
+ rv = -1017;
+ goto out;
+ }
+
+ if (((u32)ticket & 31) || ((u32)ticketView & 31)) {
+ rv = -1017;
+ goto out;
+ }
+
+ v[0].base = (u8*)ticket;
+
+ if (ticket == NULL) {
+ v[0].length = 0;
+ } else {
+ v[0].length = sizeof(ESTicket);
+ }
+
+ v[1].base = (u8*)ticketView;
+ v[1].length = sizeof(ESTicketView);
+ rv = IOS_Ioctlv(__esFd, 27, 1, 1, v);
+
+out:
+ return rv;
+}
+
+s32 ESP_DiGetTmd(ESTitleMeta* tmd, u32* tmdSize) {
+ s32 rv = 0;
+ u8 __esBuf[256] ATTRIBUTE_ALIGN(32);
+ IOSIoVector* v = (IOSIoVector*)(__esBuf + 256 - 6 * sizeof(IOSIoVector));
+ u32* p1 = (u32*)__esBuf;
+
+ if (__esFd < 0 || tmdSize == NULL) {
+ rv = -1017;
+ goto out;
+ }
+
+ if (((u32)tmd & 31)) {
+ rv = -1017;
+ goto out;
+ }
+
+ if (tmd == NULL) {
+ v[0].base = (u8*)p1;
+ v[0].length = 4;
+ rv = IOS_Ioctlv(__esFd, 57, 0, 1, v);
+
+ if (rv == 0) {
+ *tmdSize = *p1;
+ }
+ } else {
+ if (*tmdSize == 0) {
+ rv = -1017;
+ goto out;
+ }
+
+ *p1 = *tmdSize;
+ v[0].base = (u8*)p1;
+ v[0].length = 4;
+ v[1].base = (u8*)tmd;
+ v[1].length = *tmdSize;
+ rv = IOS_Ioctlv(__esFd, 58, 1, 1, v);
+ }
+
+out:
+ return rv;
+}
+
+s32 ESP_GetTmdView(ESTitleId titleId, ESTmdView* tmdView, u32* size) {
+ s32 rv = 0;
+ u8 __esBuf[256] ATTRIBUTE_ALIGN(32);
+ IOSIoVector* v = (IOSIoVector*)(__esBuf + 256 - 6 * sizeof(IOSIoVector));
+ ESTitleId* p1 = (ESTitleId*)__esBuf;
+ u32* p2 = (u32*)(__esBuf + 32);
+
+ if (__esFd < 0 || size == NULL) {
+ rv = -1017;
+ goto out;
+ }
+
+ if (((u32)tmdView & 31)) {
+ rv = -1017;
+ goto out;
+ }
+
+ *p1 = titleId;
+
+ if (tmdView == NULL) {
+ v[0].base = (u8*)p1;
+ v[0].length = sizeof(ESTitleId);
+ v[1].base = (u8*)p2;
+ v[1].length = 4;
+ rv = IOS_Ioctlv(__esFd, 20, 1, 1, v);
+
+ if (rv == 0) {
+ *size = *p2;
+ }
+ } else {
+ if (*size == 0) {
+ rv = -1017;
+ goto out;
+ }
+
+ *p2 = *size;
+ v[0].base = (u8*)p1;
+ v[0].length = sizeof(ESTitleId);
+ v[1].base = (u8*)p2;
+ v[1].length = 4;
+ v[2].base = (u8*)tmdView;
+ v[2].length = *size;
+ rv = IOS_Ioctlv(__esFd, 21, 2, 1, v);
+ }
+
+out:
+ return rv;
+}
+
+s32 ESP_GetDataDir(ESTitleId titleId, char* dataDir) {
+ s32 rv = 0;
+ u8 __esBuf[256] ATTRIBUTE_ALIGN(32);
+ IOSIoVector* v = (IOSIoVector*)(__esBuf + 256 - 6 * sizeof(IOSIoVector));
+ ESTitleId* p1 = (ESTitleId*)__esBuf;
+
+ if (__esFd < 0 || dataDir == NULL) {
+ rv = -1017;
+ goto out;
+ }
+
+ if (((u32)dataDir & 31)) {
+ rv = -1017;
+ goto out;
+ }
+
+ *p1 = titleId;
+ v[0].base = (u8*)p1;
+ v[0].length = sizeof(ESTitleId);
+ v[1].base = (u8*)dataDir;
+ v[1].length = 0x1E;
+ rv = IOS_Ioctlv(__esFd, 29, 1, 1, v);
+
+out:
+ return rv;
+}
+
+s32 ESP_GetTitleId(ESTitleId* titleId) {
+ s32 rv = 0;
+ u8 __esBuf[256] ATTRIBUTE_ALIGN(32);
+ IOSIoVector* v = (IOSIoVector*)(__esBuf + 256 - 6 * sizeof(IOSIoVector));
+
+ if (__esFd < 0 || titleId == NULL) {
+ rv = -1017;
+ goto out;
+ }
+
+ v[0].base = __esBuf;
+ v[0].length = sizeof(ESTitleId);
+
+ rv = IOS_Ioctlv(__esFd, 32, 0, 1, v);
+
+ if (rv == 0) {
+ *titleId = *(ESTitleId*)__esBuf;
+ }
+
+out:
+ return rv;
+}
+
+s32 ESP_GetConsumption(ESTicketId ticketId, ESLpEntry* entries, u32* nEntries) {
+ s32 rv = 0;
+ u8 __esBuf[256] ATTRIBUTE_ALIGN(32);
+ IOSIoVector* v = (IOSIoVector*)(__esBuf + 256 - 6 * sizeof(IOSIoVector));
+ ESTicketId* p1 = (ESTicketId*)__esBuf;
+ u32* p2 = (u32*)(__esBuf + 32);
+
+ if (__esFd < 0) {
+ rv = -1017;
+ goto out;
+ }
+
+ if (((u32)entries & 31)) {
+ rv = -1017;
+ goto out;
+ }
+
+ *p1 = ticketId;
+ v[0].base = (u8*)p1;
+ v[0].length = sizeof(ESTicketId);
+
+ if (entries == NULL) {
+ v[1].base = NULL;
+ v[1].length = 0;
+ } else {
+ v[1].base = (u8*)entries;
+ v[1].length = sizeof(ESLpEntry) * (*nEntries);
+ *p2 = *nEntries;
+ }
+
+ v[2].base = (u8*)p2;
+ v[2].length = 4;
+ rv = IOS_Ioctlv(__esFd, 22, 1, 2, v);
+ *nEntries = *p2;
+out:
+ return rv;
+}
diff --git a/src/revolution/euart/euart.c b/src/revolution/euart/euart.c
new file mode 100644
index 0000000000..df69df2df8
--- /dev/null
+++ b/src/revolution/euart/euart.c
@@ -0,0 +1,148 @@
+#include <revolution/euart.h>
+#include <revolution/exi.h>
+#include <revolution/os.h>
+
+static BOOL __EUARTInitialized = FALSE;
+static EUARTError __EUARTLastErrorCode = EUART_ERROR_NONE;
+static BOOL __EUARTSendStop = FALSE;
+static u32 Enabled = 0;
+
+BOOL EUARTInit(void) {
+ BOOL enabled;
+ u8 val;
+
+ if (__EUARTInitialized) {
+ return TRUE;
+ }
+
+ if (!(OSGetConsoleType() & 0x10000000)) {
+ __EUARTLastErrorCode = EUART_ERROR_CANNOT_USE;
+ return FALSE;
+ }
+
+ enabled = OSDisableInterrupts();
+
+ val = -14;
+ if (!EXIWriteReg(0, 1, 0xB0000000, &val, 1)) {
+ __EUARTLastErrorCode = EUART_ERROR_INTERNAL;
+ OSRestoreInterrupts(enabled);
+ return FALSE;
+ }
+
+ val = -13;
+ if (!EXIWriteReg(0, 1, 0xB0000000, &val, 1)) {
+ __EUARTLastErrorCode = EUART_ERROR_INTERNAL;
+ OSRestoreInterrupts(enabled);
+ return FALSE;
+ }
+
+ OSRestoreInterrupts(enabled);
+
+ __EUARTInitialized = TRUE;
+ __EUARTLastErrorCode = EUART_ERROR_NONE;
+ __EUARTSendStop = FALSE;
+ return TRUE;
+}
+
+UARTError InitializeUART(UARTBaudRate rate) {
+ if (!(OSGetConsoleType() & 0x10000000)) {
+ Enabled = 0;
+ return kUARTConfigurationError;
+ } else {
+ Enabled = 0xA5FF005A;
+ return kUARTNoError;
+ }
+}
+
+static int QueueLength(void) {
+ u32 cmd, txCnt;
+
+ if (!EXISelect(0, 1, __EXIFreq)) {
+ return -1;
+ }
+
+ cmd = 0x30000100;
+ EXIImm(0, &cmd, 4, 1, NULL);
+ EXISync(0);
+ EXIImm(0, &txCnt, sizeof(txCnt), 0, NULL);
+ EXISync(0);
+ EXIDeselect(0);
+ return 32 - (u8)((txCnt >> 24) & 0x3F);
+}
+
+UARTError WriteUARTN(const void* buf, unsigned long len) {
+ u32 cmd;
+ int qLen;
+ UARTError error;
+ char* sendData = (char*)buf;
+ u32 uart_txd;
+
+ if (Enabled != 0xA5FF005A) {
+ return kUARTConfigurationError;
+ }
+
+ if (!__EUARTInitialized) {
+ if (!EUARTInit()) {
+ return kUARTConfigurationError;
+ }
+ }
+
+ if (!__EUARTInitialized) {
+ __EUARTLastErrorCode = EUART_ERROR_UNINITIALIZED;
+ return kUARTConfigurationError;
+ }
+
+ if (!EXILock(0, 1, 0)) {
+ return kUARTNoError;
+ }
+
+ {
+ char* p;
+
+ for (p = sendData; (p - buf) < len; p++) {
+ if (*p == '\n') {
+ *p = '\r';
+ }
+ }
+ }
+
+ error = kUARTNoError;
+ cmd = 0xB0000100;
+
+ while (len != 0) {
+ qLen = QueueLength();
+
+ if (qLen < 0) {
+ error = kUARTBufferOverflow;
+ break;
+ }
+
+ if (qLen != 32) {
+ continue;
+ }
+
+ if (!EXISelect(0, 1, __EXIFreq)) {
+ error = kUARTBufferOverflow;
+ break;
+ }
+
+ EXIImm(0, &cmd, 4, 1, NULL);
+ EXISync(0);
+
+ while ((qLen > 0) && (len > 0)) {
+ uart_txd = (u32)((*sendData & 0xFF) << 24);
+
+ EXIImm(0, &uart_txd, 4, 1, NULL);
+ EXISync(0);
+
+ sendData++;
+ qLen--;
+ len--;
+ }
+
+ EXIDeselect(0);
+ }
+
+ EXIUnlock(0);
+ return error;
+}
diff --git a/src/revolution/exi/EXIBios.c b/src/revolution/exi/EXIBios.c
new file mode 100644
index 0000000000..19290be936
--- /dev/null
+++ b/src/revolution/exi/EXIBios.c
@@ -0,0 +1,876 @@
+#include <revolution.h>
+
+#define REG_MAX 5
+#define REG(chan, idx) (__EXIRegs[((chan) * REG_MAX) + (idx)])
+
+#define STATE_IDLE 0
+#define STATE_DMA 1
+#define STATE_IMM 2
+#define STATE_BUSY 3
+#define STATE_SELECTED 4
+#define STATE_ATTACHED 8
+#define STATE_LOCKED 16
+
+#define MAX_CHAN 3
+
+#define MAX_IMM 4
+#define MAX_TYPE 3
+#define MAX_DEV 3
+#define MAX_FREQ 6
+
+#define EXI_0LENGTH_EXILENGTH_MASK 0xFFFFFFE0
+
+#ifdef SDK_AUG2010
+#define BUILD_DATE "Aug 23 2010"
+#if DEBUG
+#define BUILD_TIME "17:24:52"
+#else
+#define BUILD_TIME "17:33:06"
+#endif
+#elif SDK_SEP2006
+#define BUILD_DATE "Sep 21 2006"
+#define BUILD_TIME "14:32:13"
+#endif
+
+#ifdef SDK_AUG2010
+#if DEBUG
+const char* __EXIVersion = "<< RVL_SDK - EXI \tdebug build: "BUILD_DATE" "BUILD_TIME" (0x4302_145) >>";
+#else
+const char* __EXIVersion = "<< RVL_SDK - EXI \trelease build: "BUILD_DATE" "BUILD_TIME" (0x4302_145) >>";
+#endif
+#elif SDK_SEP2006
+const char* __EXIVersion = "<< RVL_SDK - EXI \trelease build: "BUILD_DATE" "BUILD_TIME" (0x4200_60422) >>";
+#endif
+
+static EXIControl Ecb[3];
+static u32 IDSerialPort1;
+
+// external functions
+extern void __OSEnableBarnacle(s32 chan, u32 dev);
+
+// prototypes
+u32 EXIClearInterrupts(s32 chan, int exi, int tc, int ext);
+static int __EXIProbe(s32 chan);
+
+static void SetExiInterruptMask(s32 chan, EXIControl* exi) {
+ EXIControl* exi2;
+ exi2 = &Ecb[2];
+
+ switch (chan) {
+ case 0:
+ if ((exi->exiCallback == 0 && exi2->exiCallback == 0) || (exi->state & STATE_LOCKED)) {
+ __OSMaskInterrupts(OS_INTERRUPTMASK_EXI_0_EXI | OS_INTERRUPTMASK_EXI_2_EXI);
+ } else {
+ __OSUnmaskInterrupts(OS_INTERRUPTMASK_EXI_0_EXI | OS_INTERRUPTMASK_EXI_2_EXI);
+ }
+ break;
+ case 1:
+ if (exi->exiCallback == 0 || (exi->state & STATE_LOCKED)) {
+ __OSMaskInterrupts(OS_INTERRUPTMASK_EXI_1_EXI);
+ } else {
+ __OSUnmaskInterrupts(OS_INTERRUPTMASK_EXI_1_EXI);
+ }
+ break;
+ case 2:
+ if (__OSGetInterruptHandler(__OS_INTERRUPT_PI_DEBUG) == 0 || (exi->state & STATE_LOCKED)) {
+ __OSMaskInterrupts(OS_INTERRUPTMASK_PI_DEBUG);
+ } else {
+ __OSUnmaskInterrupts(OS_INTERRUPTMASK_PI_DEBUG);
+ }
+ break;
+ }
+}
+
+static void CompleteTransfer(s32 chan) {
+ EXIControl* exi;
+ u8* buf;
+ u32 data;
+ int i;
+ int len;
+
+ exi = &Ecb[chan];
+ ASSERTLINE(395, 0 <= chan && chan < MAX_CHAN);
+
+ if (exi->state & STATE_BUSY) {
+ if (exi->state & STATE_IMM) {
+ if (exi->immLen != 0) {
+ len = exi->immLen;
+ buf = exi->immBuf;
+ data = __EXIRegs[(chan * 5) + 4];
+ for(i = 0; i < len; i++) {
+ *buf++ = data >> ((3 - i) * 8);
+ }
+ }
+ }
+ exi->state &= ~STATE_BUSY;
+ }
+}
+
+int EXIImm(s32 chan, void* buf, s32 len, u32 type, EXICallback callback) {
+ EXIControl* exi;
+ BOOL enabled;
+
+ exi = &Ecb[chan];
+ ASSERTLINE(434, exi->state & STATE_SELECTED);
+ ASSERTLINE(435, 0 <= chan && chan < MAX_CHAN);
+ ASSERTLINE(436, 0 < len && len <= MAX_IMM);
+ ASSERTLINE(437, type < MAX_TYPE);
+ enabled = OSDisableInterrupts();
+
+ if ((exi->state & STATE_BUSY) || !(exi->state & STATE_SELECTED)) {
+ OSRestoreInterrupts(enabled);
+ return 0;
+ }
+
+ exi->tcCallback = callback;
+ if (exi->tcCallback) {
+ EXIClearInterrupts(chan, 0, 1, 0);
+ __OSUnmaskInterrupts(0x200000U >> (chan * 3));
+ }
+
+ exi->state |= STATE_IMM;
+ if (type != 0) {
+ u32 data;
+ int i;
+
+ data = 0;
+ for(i = 0; i < len; i++) {
+ data |= ((u8*)buf)[i] << ((3 - i) * 8);
+ }
+ __EXIRegs[(chan * 5) + 4] = data;
+ }
+
+ exi->immBuf = buf;
+ exi->immLen = (type != 1) ? len : 0;
+ __EXIRegs[(chan * 5) + 3] = (type << 2) | 1 | ((len - 1) << 4);
+ OSRestoreInterrupts(enabled);
+ return 1;
+}
+
+int EXIImmEx(s32 chan, void* buf, s32 len, u32 mode) {
+ s32 xLen;
+
+ while (len) {
+ xLen = (len < 4) ? len : 4;
+ if (EXIImm(chan, buf, xLen, mode, 0) == 0) {
+ return 0;
+ }
+ if (EXISync(chan) == 0) {
+ return 0;
+ }
+ ((u8*)buf) += xLen;
+ len -= xLen;
+ }
+
+ return 1;
+}
+
+int EXIDma(s32 chan, void* buf, s32 len, u32 type, EXICallback callback) {
+ EXIControl* exi;
+ BOOL enabled;
+
+ exi = &Ecb[chan];
+
+ ASSERTLINE(539, exi->state & STATE_SELECTED);
+ ASSERTLINE(540, OFFSET(buf, 32) == 0);
+ ASSERTLINE(541, 0 < len && OFFSET(len, 32) == 0);
+ ASSERTLINE(543, ((u32) len & ~EXI_0LENGTH_EXILENGTH_MASK) == 0);
+ ASSERTLINE(545, type == EXI_READ || type == EXI_WRITE);
+
+ enabled = OSDisableInterrupts();
+ if ((exi->state & STATE_BUSY) || !(exi->state & STATE_SELECTED)) {
+ OSRestoreInterrupts(enabled);
+ return 0;
+ }
+
+ exi->tcCallback = callback;
+ if ((u32)exi->tcCallback) {
+ EXIClearInterrupts(chan, 0, 1, 0);
+ __OSUnmaskInterrupts(0x200000U >> (chan * 3));
+ }
+
+ exi->state |= STATE_DMA;
+ __EXIRegs[(chan * 5) + 1] = (u32)buf & EXI_0LENGTH_EXILENGTH_MASK;
+ __EXIRegs[(chan * 5) + 2] = len;
+ __EXIRegs[(chan * 5) + 3] = (type * 4) | 3;
+
+ OSRestoreInterrupts(enabled);
+ return 1;
+}
+
+int EXISync(s32 chan) {
+ EXIControl* exi;
+ int rc;
+ BOOL enabled;
+
+ exi = &Ecb[chan];
+ rc = 0;
+ ASSERTLINE(595, 0 <= chan && chan < MAX_CHAN);
+
+ while ((exi->state & STATE_SELECTED)) {
+ if (!(__EXIRegs[(chan * 5) + 3] & 1)) {
+ enabled = OSDisableInterrupts();
+ if (exi->state & STATE_SELECTED) {
+ CompleteTransfer(chan);
+ if (__OSGetDIConfig() != 0xFF || (OSGetConsoleType() & 0xf0000000) == 0x20000000 || exi->immLen != 4 || (__EXIRegs[chan * 5] & 0x70) || (__EXIRegs[(chan * 5) + 4] != 0x01010000 && __EXIRegs[(chan * 5) + 4] != 0x05070000 && __EXIRegs[(chan * 5) + 4] != 0x04220001) || __OSDeviceCode == 0x8200) {
+ rc = 1;
+ }
+ }
+ OSRestoreInterrupts(enabled);
+ break;
+ }
+ }
+
+ ASSERTLINE(623, !(exi->state & STATE_BUSY));
+ return rc;
+}
+
+u32 EXIClearInterrupts(s32 chan, int exi, int tc, int ext) {
+ u32 cpr;
+ u32 prev;
+
+ ASSERTLINE(644, 0 <= chan && chan < MAX_CHAN);
+
+ cpr = prev = __EXIRegs[(chan * 5)];
+ prev &= 0x7F5;
+
+ if (exi != 0) {
+ prev |= 2;
+ }
+
+ if (tc != 0) {
+ prev |= 8;
+ }
+
+ if (ext != 0) {
+ prev |= 0x800;
+ }
+
+ __EXIRegs[(chan * 5)] = prev;
+ return cpr;
+}
+
+EXICallback EXISetExiCallback(s32 chan, EXICallback exiCallback) {
+ EXIControl* exi;
+ EXICallback prev;
+ BOOL enabled;
+
+ exi = &Ecb[chan];
+ ASSERTLINE(678, 0 <= chan && chan < MAX_CHAN);
+ enabled = OSDisableInterrupts();
+
+ prev = exi->exiCallback;
+ exi->exiCallback = exiCallback;
+ if (chan != 2) {
+ SetExiInterruptMask(chan, exi);
+ } else {
+ SetExiInterruptMask(0, &Ecb[0]);
+ }
+
+ OSRestoreInterrupts(enabled);
+ return prev;
+}
+
+void EXIProbeReset() {
+ __gUnknown800030C0[0] = __gUnknown800030C0[1] = 0;
+ Ecb[0].idTime = Ecb[1].idTime = 0;
+ __EXIProbe(0);
+ __EXIProbe(1);
+}
+
+static int __EXIProbe(s32 chan) {
+ EXIControl* exi;
+ BOOL enabled;
+ int rc;
+ u32 cpr;
+ s32 t;
+
+ exi = &Ecb[chan];
+ ASSERTLINE(733, 0 <= chan && chan < MAX_CHAN);
+ if (chan == 2) {
+ return 1;
+ }
+
+ rc = 1;
+ enabled = OSDisableInterrupts();
+ cpr = __EXIRegs[(chan * 5)];
+
+ if (!(exi->state & STATE_ATTACHED)) {
+ if (cpr & 0x800) {
+ EXIClearInterrupts(chan, 0, 0, 1);
+ __gUnknown800030C0[chan] = exi->idTime = 0;
+ }
+
+ if (cpr & 0x1000) {
+ t = ((s32)(OSTicksToMilliseconds(OSGetTime()) / 100) + 1);
+
+ if (__gUnknown800030C0[chan] == 0) {
+ __gUnknown800030C0[chan] = t;
+ }
+
+ if (t - (s32)__gUnknown800030C0[chan] < 3) {
+ rc = 0;
+ }
+ } else {
+ __gUnknown800030C0[chan] = exi->idTime = 0;
+ rc = 0;
+ }
+ } else if(!(cpr & 0x1000) || (cpr & 0x800)) {
+ __gUnknown800030C0[chan] = exi->idTime = 0;
+ rc = 0;
+ }
+
+ OSRestoreInterrupts(enabled);
+ return rc;
+}
+
+int EXIProbe(s32 chan) {
+ EXIControl* exi = &Ecb[chan];
+ int rc;
+ u32 id;
+
+ rc = __EXIProbe(chan);
+ if (rc && !exi->idTime) {
+ rc = EXIGetID(chan, 0, &id) ? 1 : 0;
+ }
+
+ return rc;
+}
+
+s32 EXIProbeEx(s32 chan) {
+ if (EXIProbe(chan)) {
+ return 1;
+ }
+
+ if (__gUnknown800030C0[chan]) {
+ return 0;
+ }
+
+ return -1;
+}
+
+static int __EXIAttach(s32 chan, EXICallback extCallback) {
+ EXIControl* exi;
+ BOOL enabled;
+
+ exi = &Ecb[chan];
+ ASSERTLINE(838, 0 <= chan && chan < 2);
+ enabled = OSDisableInterrupts();
+
+ if ((exi->state & STATE_ATTACHED) || !__EXIProbe(chan)) {
+ OSRestoreInterrupts(enabled);
+ return 0;
+ }
+
+ EXIClearInterrupts(chan, 1, 0, 0);
+ exi->extCallback = extCallback;
+ __OSUnmaskInterrupts(0x100000U >> (chan * 3));
+ exi->state |= STATE_ATTACHED;
+
+ OSRestoreInterrupts(enabled);
+ return 1;
+}
+
+int EXIAttach(s32 chan, EXICallback extCallback) {
+ EXIControl* exi;
+ BOOL enabled;
+ int rc;
+
+ exi = &Ecb[chan];
+ ASSERTLINE(864, 0 <= chan && chan < 2);
+
+ EXIProbe(chan);
+ enabled = OSDisableInterrupts();
+ if (exi->idTime == 0) {
+ OSRestoreInterrupts(enabled);
+ return 0;
+ }
+
+ rc = __EXIAttach(chan, extCallback);
+ OSRestoreInterrupts(enabled);
+ return rc;
+}
+
+int EXIDetach(s32 chan) {
+ EXIControl* exi;
+ BOOL enabled;
+
+ exi = &Ecb[chan];
+ ASSERTLINE(898, 0 <= chan && chan < 2);
+ enabled = OSDisableInterrupts();
+
+ if (!(exi->state & STATE_ATTACHED)) {
+ OSRestoreInterrupts(enabled);
+ return 1;
+ }
+
+ if ((exi->state & STATE_LOCKED) && (exi->dev == 0)) {
+ OSRestoreInterrupts(enabled);
+ return 0;
+ }
+
+ exi->state &= ~STATE_ATTACHED;
+ __OSMaskInterrupts(0x500000U >> (chan * 3));
+
+ OSRestoreInterrupts(enabled);
+ return 1;
+}
+
+int EXISelectSD(s32 chan, u32 dev, u32 freq) {
+ EXIControl* exi;
+ u32 cpr;
+ BOOL enabled;
+
+ exi = &Ecb[chan];
+
+ ASSERTLINE(908, 0 <= chan && chan < MAX_CHAN);
+ ASSERTLINE(909, chan == 0 && dev < MAX_DEV || dev == 0);
+ ASSERTLINE(910, freq < MAX_FREQ);
+ ASSERTLINE(911, !(exi->state & STATE_SELECTED));
+
+ enabled = OSDisableInterrupts();
+ if ((exi->state & STATE_SELECTED) || ((chan != 2) && (((dev == 0) && !(exi->state & STATE_ATTACHED) && (EXIProbe(chan) == 0)) || !(exi->state & STATE_LOCKED) || (exi->dev != dev)))) {
+ OSRestoreInterrupts(enabled);
+ return 0;
+ }
+
+ exi->state |= STATE_SELECTED;
+ cpr = __EXIRegs[(chan * 5)];
+ cpr &= 0x405;
+ cpr |= freq * 0x10;
+ __EXIRegs[(chan * 5)] = cpr;
+
+ if (exi->state & STATE_ATTACHED) {
+ switch (chan) {
+ case 0:
+ __OSMaskInterrupts(0x100000U);
+ break;
+ case 1:
+ __OSMaskInterrupts(0x20000U);
+ break;
+ }
+ }
+
+ OSRestoreInterrupts(enabled);
+ return 1;
+}
+
+int EXISelect(s32 chan, u32 dev, u32 freq) {
+ EXIControl* exi;
+ u32 cpr;
+ BOOL enabled;
+
+ exi = &Ecb[chan];
+
+ ASSERTLINE(996, 0 <= chan && chan < MAX_CHAN);
+ ASSERTLINE(997, chan == 0 && dev < MAX_DEV || dev == 0);
+ ASSERTLINE(998, freq < MAX_FREQ);
+ ASSERTLINE(999, !(exi->state & STATE_SELECTED));
+
+ enabled = OSDisableInterrupts();
+ if ((exi->state & STATE_SELECTED) || ((chan != 2) && (((dev == 0) && !(exi->state & STATE_ATTACHED) && (__EXIProbe(chan) == 0)) || !(exi->state & STATE_LOCKED) || (exi->dev != dev)))) {
+ OSRestoreInterrupts(enabled);
+ return 0;
+ }
+
+ exi->state |= STATE_SELECTED;
+ cpr = __EXIRegs[(chan * 5)];
+ cpr &= 0x405;
+ cpr |= (((1 << dev) << 7) | (freq * 0x10));
+ __EXIRegs[(chan * 5)] = cpr;
+
+ if (exi->state & STATE_ATTACHED) {
+ switch (chan) {
+ case 0:
+ __OSMaskInterrupts(0x100000U);
+ break;
+ case 1:
+ __OSMaskInterrupts(0x20000U);
+ break;
+ }
+ }
+
+ OSRestoreInterrupts(enabled);
+ return 1;
+}
+
+int EXIDeselect(s32 chan) {
+ EXIControl* exi;
+ u32 cpr;
+ BOOL enabled;
+
+ exi = &Ecb[chan];
+ ASSERTLINE(1077, 0 <= chan && chan < MAX_CHAN);
+ enabled = OSDisableInterrupts();
+
+ if (!(exi->state & STATE_SELECTED)) {
+ OSRestoreInterrupts(enabled);
+ return 0;
+ }
+
+ exi->state &= ~STATE_SELECTED;
+ cpr = __EXIRegs[(chan * 5)];
+ __EXIRegs[(chan * 5)] = cpr & 0x405;
+
+ if (exi->state & STATE_ATTACHED) {
+ switch (chan) {
+ case 0:
+ __OSUnmaskInterrupts(0x100000U);
+ break;
+ case 1:
+ __OSUnmaskInterrupts(0x20000U);
+ break;
+ }
+ }
+
+ OSRestoreInterrupts(enabled);
+
+ if ((chan != 2) && (cpr & 0x80)) {
+ return __EXIProbe(chan) ? TRUE : FALSE;
+ }
+
+ return 1;
+}
+
+static void EXIIntrruptHandler(__OSInterrupt interrupt, OSContext* context) {
+ s32 chan;
+ EXIControl* exi;
+ EXICallback callback;
+
+ chan = (interrupt - 9) / 3;
+
+ ASSERTLINE(1150, 0 <= chan && chan < MAX_CHAN);
+ exi = &Ecb[chan];
+ EXIClearInterrupts(chan, 1, 0, 0);
+
+ callback = exi->exiCallback;
+ if (callback) {
+ OSContext exceptionContext;
+
+ OSClearContext(&exceptionContext);
+ OSSetCurrentContext(&exceptionContext);
+ callback(chan, context);
+ OSClearContext(&exceptionContext);
+ OSSetCurrentContext(context);
+ }
+}
+
+static void TCIntrruptHandler(__OSInterrupt interrupt, OSContext* context) {
+ s32 chan;
+ EXIControl* exi;
+ EXICallback callback;
+
+ chan = (interrupt - 10) / 3;
+
+ ASSERTLINE(1186, 0 <= chan && chan < MAX_CHAN);
+ exi = &Ecb[chan];
+ __OSMaskInterrupts(0x80000000U >> interrupt);
+ EXIClearInterrupts(chan, 0, 1, 0);
+
+ callback = exi->tcCallback;
+ if (callback) {
+ OSContext exceptionContext;
+
+ exi->tcCallback = NULL;
+ CompleteTransfer(chan);
+
+ OSClearContext(&exceptionContext);
+ OSSetCurrentContext(&exceptionContext);
+ callback(chan, context);
+ OSClearContext(&exceptionContext);
+ OSSetCurrentContext(context);
+ }
+}
+
+static void EXTIntrruptHandler(__OSInterrupt interrupt, OSContext* context) {
+ s32 chan;
+ EXIControl* exi;
+ EXICallback callback;
+
+ chan = (interrupt - 11) / 3;
+
+ ASSERTLINE(1226, 0 <= chan && chan < 2);
+ __OSMaskInterrupts(0x500000U >> (chan * 3));
+ exi = &Ecb[chan];
+ callback = exi->extCallback;
+ exi->state &= ~STATE_ATTACHED;
+
+ if (callback) {
+ OSContext exceptionContext;
+
+ OSClearContext(&exceptionContext);
+ OSSetCurrentContext(&exceptionContext);
+ exi->extCallback = NULL;
+ callback(chan, context);
+ OSClearContext(&exceptionContext);
+ OSSetCurrentContext(context);
+ }
+}
+
+void EXIInit() {
+ u32 id;
+
+ while (((REG(0, 3) & 1) == 1) || ((REG(1, 3) & 1) == 1) || ((REG(2, 3) & 1) == 1)) {}
+
+ __OSMaskInterrupts(0x7F8000U);
+ __EXIRegs[0] = 0;
+ __EXIRegs[5] = 0;
+ __EXIRegs[10] = 0;
+ __EXIRegs[0] = 0x2000;
+ __OSSetInterruptHandler(9, EXIIntrruptHandler);
+ __OSSetInterruptHandler(10, TCIntrruptHandler);
+ __OSSetInterruptHandler(11, EXTIntrruptHandler);
+ __OSSetInterruptHandler(12, EXIIntrruptHandler);
+ __OSSetInterruptHandler(13, TCIntrruptHandler);
+ __OSSetInterruptHandler(14, EXTIntrruptHandler);
+ __OSSetInterruptHandler(15, EXIIntrruptHandler);
+ __OSSetInterruptHandler(16, TCIntrruptHandler);
+
+ EXIGetID(0, 2, &IDSerialPort1);
+
+ if (__OSInIPL) {
+ EXIProbeReset();
+ } else if (EXIGetID(0, 0, &id) && id == 0x7010000) {
+ __OSEnableBarnacle(1, 0);
+ } else if (EXIGetID(1, 0, &id) && id == 0x7010000) {
+ __OSEnableBarnacle(0, 2);
+ }
+
+ OSRegisterVersion(__EXIVersion);
+}
+
+int EXILock(s32 chan, u32 dev, EXICallback unlockedCallback) {
+ EXIControl* exi;
+ BOOL enabled;
+ int i;
+
+ exi = &Ecb[chan];
+ ASSERTLINE(1338, 0 <= chan && chan < MAX_CHAN);
+ ASSERTLINE(1339, chan == 0 && dev < MAX_DEV || dev == 0);
+ enabled = OSDisableInterrupts();
+
+ if (exi->state & STATE_LOCKED) {
+ if (unlockedCallback) {
+ ASSERTLINE(1345, chan == 0 && exi->items < (MAX_DEV - 1) || exi->items == 0);
+ for(i = 0; i < exi->items; i++) {
+ if (exi->queue[i].dev == dev) {
+ OSRestoreInterrupts(enabled);
+ return 0;
+ }
+ }
+ exi->queue[exi->items].callback = unlockedCallback;
+ exi->queue[exi->items].dev = dev;
+ exi->items++;
+ }
+ OSRestoreInterrupts(enabled);
+ return 0;
+ }
+
+ ASSERTLINE(1361, exi->items == 0);
+ exi->state |= STATE_LOCKED;
+ exi->dev = dev;
+ SetExiInterruptMask(chan, exi);
+ OSRestoreInterrupts(enabled);
+ return 1;
+}
+
+int EXIUnlock(s32 chan) {
+ EXIControl* exi;
+ BOOL enabled;
+ EXICallback unlockedCallback;
+
+ exi = &Ecb[chan];
+ ASSERTLINE(1385, 0 <= chan && chan < MAX_CHAN);
+ enabled = OSDisableInterrupts();
+
+ if (!(exi->state & STATE_LOCKED)) {
+ OSRestoreInterrupts(enabled);
+ return 0;
+ }
+
+ exi->state &= ~STATE_LOCKED;
+ SetExiInterruptMask(chan, exi);
+ if (exi->items > 0) {
+ unlockedCallback = exi->queue[0].callback;
+ if (--exi->items > 0) {
+ memmove(&exi->queue[0], &exi->queue[1], exi->items * 8);
+ }
+ unlockedCallback(chan, 0);
+ }
+
+ OSRestoreInterrupts(enabled);
+ return 1;
+}
+
+u32 EXIGetState(s32 chan) {
+ EXIControl* exi;
+
+ exi = &Ecb[chan];
+ ASSERTLINE(1343, 0 <= chan && chan < MAX_CHAN);
+ return exi->state;
+}
+
+static void UnlockedHandler(s32 chan, OSContext* context) {
+ u32 id;
+ EXIGetID(chan, 0, &id);
+}
+
+s32 EXIGetID(s32 chan, u32 dev, u32* id) {
+ EXIControl* exi = &Ecb[chan];
+ int err;
+ u32 cmd;
+ s32 startTime;
+ BOOL enabled;
+
+ ASSERTLINE(1459, 0 <= chan && chan < MAX_CHAN);
+ if (chan == 0 && dev == 2 && IDSerialPort1 != 0) {
+ *id = IDSerialPort1;
+ return 1;
+ }
+
+ if ((chan < 2) && (dev == 0)) {
+ if ((__EXIProbe(chan) == 0)) {
+ return 0;
+ }
+
+ if (exi->idTime == __gUnknown800030C0[chan]) {
+ *id = exi->id;
+ return exi->idTime;
+ }
+
+ if (!__EXIAttach(chan, NULL)) {
+ return 0;
+ }
+
+ startTime = __gUnknown800030C0[chan];
+ }
+
+ enabled = OSDisableInterrupts();
+
+ err = !EXILock(chan, dev, (chan < 2 && dev == 0) ? &UnlockedHandler : NULL);
+ if (err == 0) {
+ err = !EXISelect(chan, dev, 0);
+ if (err == 0) {
+ cmd = 0;
+ err |= !EXIImm(chan, &cmd, 2, 1, 0);
+ err |= !EXISync(chan);
+ err |= !EXIImm(chan, id, 4, 0, 0);
+ err |= !EXISync(chan);
+ err |= !EXIDeselect(chan);
+ }
+
+ EXIUnlock(chan);
+ }
+
+ OSRestoreInterrupts(enabled);
+
+ if ((chan < 2) && (dev == 0)) {
+ EXIDetach(chan);
+ enabled = OSDisableInterrupts();
+ err |= __gUnknown800030C0[chan] != startTime;
+
+ if (!err) {
+ exi->id = *id;
+ exi->idTime = startTime;
+ }
+
+ OSRestoreInterrupts(enabled);
+ return err ? 0 : exi->idTime;
+ }
+
+ return err ? 0 : 1;
+}
+
+s32 EXIGetType(s32 chan, u32 dev, u32* type) {
+ u32 _type;
+ s32 probe;
+
+ probe = EXIGetID(chan, dev, &_type);
+
+ if (!probe) {
+ return probe;
+ }
+
+ switch (_type & ~0xFF) {
+ case 0x04020300:
+ case EXI_ETHER:
+ case 0x04020100:
+ case EXI_MIC:
+ *type = (_type & ~0xFF);
+ return probe;
+ default:
+ switch (_type & ~0xFFFF) {
+ case 0x00000000:
+ if (!(_type & 0x3803)) {
+ switch (_type & 0xFC) {
+ case EXI_MEMORY_CARD_59:
+ case EXI_MEMORY_CARD_123:
+ case EXI_MEMORY_CARD_251:
+ case EXI_MEMORY_CARD_507:
+ case EXI_MEMORY_CARD_1019:
+ case EXI_MEMORY_CARD_2043:
+ *type = _type & 0xFC;
+ return probe;
+ }
+ }
+ break;
+ case EXI_IS_VIEWER:
+ *type = EXI_IS_VIEWER;
+ return probe;
+ }
+
+ *type = _type;
+ return probe;
+ }
+}
+
+char* EXIGetTypeString(u32 type) {
+ switch (type) {
+ case EXI_MEMORY_CARD_59:
+ return "Memory Card 59";
+ case EXI_MEMORY_CARD_123:
+ return "Memory Card 123";
+ case EXI_MEMORY_CARD_251:
+ return "Memory Card 251";
+ case EXI_MEMORY_CARD_507:
+ return "Memory Card 507";
+ case EXI_MEMORY_CARD_1019:
+ return "Memory Card 1019";
+ case EXI_MEMORY_CARD_2043:
+ return "Memory Card 2043";
+ case EXI_USB_ADAPTER:
+ return "USB Adapter";
+ case EXI_NPDP_GDEV:
+ return "GDEV";
+ case EXI_MODEM:
+ return "Modem";
+ case EXI_MARLIN:
+ return "Marlin";
+ case EXI_AD16:
+ return "AD16";
+ case EXI_RS232C:
+ return "RS232C";
+ case 0x80000020:
+ case 0x80000080:
+ case 0x80000040:
+ case 0x80000008:
+ case 0x80000010:
+ case 0x80000004:
+ return "Net Card";
+ case EXI_ETHER_VIEWER:
+ return "Artist Ether";
+ case 0x4020100:
+ case 0x4020300:
+ case EXI_ETHER:
+ case 0x4220000:
+ return "Broadband Adapter";
+ case EXI_MIC:
+ return "Mic";
+ case EXI_STREAM_HANGER:
+ return "Stream Hanger";
+ case EXI_IS_VIEWER:
+ return "IS-DOL-VIEWER";
+ default:
+ return "Unknown";
+ }
+}
diff --git a/src/revolution/exi/EXICommon.c b/src/revolution/exi/EXICommon.c
new file mode 100644
index 0000000000..e53525fbba
--- /dev/null
+++ b/src/revolution/exi/EXICommon.c
@@ -0,0 +1,113 @@
+#include <revolution/types.h>
+#include <revolution/os.h>
+#include <revolution/exi.h>
+
+const u32 __EXIFreq = 4;
+
+s32 EXIGetConsoleType(void) {
+ return 1;
+}
+
+void EXIWait(void) {
+ while (__OSDeviceCode == 0) {}
+}
+
+static u32 __EXISwap32(u32 val) {
+ return val >> 24 & 0x000000FF | val >> 8 & 0x0000FF00 | val << 8 & 0x00FF0000 | val << 24 & 0xFF000000;
+}
+
+BOOL EXIWriteReg(s32 chan, u32 dev, u32 exiCmd, void* reg, s32 size) {
+ BOOL ret = FALSE;
+ u32 reg32;
+
+ switch (size) {
+ case 1:
+ reg32 = (u32)((*((u8*)reg) & 0xFF) << 24);
+ break;
+ case 2:
+ reg32 = (u32)(((*((u16*)reg) & 0xFF) << 24) | ((*((u16*)reg) & 0xFF00) << 8));
+ break;
+ default:
+ reg32 = __EXISwap32(*((u32*)reg));
+ break;
+ }
+
+ ret |= !EXILock(chan, dev, 0);
+
+ if (ret) {
+ return FALSE;
+ }
+
+ ret |= !EXISelect(chan, dev, 4);
+
+ if (ret) {
+ EXIUnlock(chan);
+ return FALSE;
+ }
+
+ ret |= !EXIImm(chan, &exiCmd, 4, 1, NULL);
+ ret |= !EXISync(chan);
+ ret |= !EXIImm(chan, &reg32, 4, 1, NULL);
+ ret |= !EXISync(chan);
+ ret |= !EXIDeselect(chan);
+ ret |= !EXIUnlock(chan);
+ return !ret;
+}
+
+BOOL EXIReadRam(s32 chan, u32 dev, u32 exiCmd, void* buffer, s32 size, EXICallback callback) {
+ BOOL ret = FALSE;
+
+ ret |= !EXILock(chan, dev, NULL);
+
+ if (ret) {
+ return FALSE;
+ }
+
+ ret |= !EXISelect(chan, dev, 4);
+
+ if (ret) {
+ EXIUnlock(chan);
+ return FALSE;
+ }
+
+ ret |= !EXIImm(chan, &exiCmd, 4, 1, NULL);
+ ret |= !EXISync(chan);
+ ret |= !EXIDma(chan, buffer, size, EXI_READ, callback);
+
+ if (callback == NULL) {
+ ret |= !EXISync(chan);
+ ret |= !EXIDeselect(chan);
+ ret |= !EXIUnlock(chan);
+ }
+
+ return !ret;
+}
+
+BOOL EXIWriteRam(s32 chan, u32 dev, u32 exiCmd, void* buffer, s32 size, EXICallback callback) {
+ BOOL ret = FALSE;
+
+ ret |= !EXILock(chan, dev, NULL);
+
+ if (ret) {
+ return FALSE;
+ }
+
+ ret |= !EXISelect(chan, dev, 4);
+
+ if (ret) {
+ EXIUnlock(chan);
+ return FALSE;
+ }
+
+ ret |= !EXIImm(chan, &exiCmd, 4, 1, NULL);
+ ret |= !EXISync(chan);
+ ret |= !EXIDma(chan, buffer, size, EXI_WRITE, callback);
+
+ if (callback == NULL) {
+ ret |= !EXISync(chan);
+ ret |= !EXIDeselect(chan);
+ ret |= !EXIUnlock(chan);
+ }
+
+ return !ret;
+}
diff --git a/src/revolution/exi/EXIUart.c b/src/revolution/exi/EXIUart.c
new file mode 100644
index 0000000000..0a14c130b0
--- /dev/null
+++ b/src/revolution/exi/EXIUart.c
@@ -0,0 +1,190 @@
+#include <revolution/exi.h>
+#include <revolution/os.h>
+#include "__os.h"
+
+static s32 Chan;
+static u32 Dev;
+static u32 BarnacleEnabled;
+static u32 Enabled;
+
+// prototypes
+int InitializeUART(void);
+int ReadUARTN(void);
+int WriteUARTN(void *buf, u32 len);
+void __OSEnableBarnacle(s32 chan, u32 dev);
+
+static BOOL ProbeBarnacle(s32 chan, u32 dev, u32* revision) {
+ int err;
+ u32 cmd;
+
+ if (chan != 2 && dev == 0 && !EXIAttach(chan, NULL)) {
+ return FALSE;
+ }
+
+ err = !EXILock(chan, dev, NULL);
+ if (!err) {
+ err = !EXISelect(chan, dev, EXI_FREQ_1M);
+ if (!err) {
+ cmd = 0x20011300;
+ err = FALSE;
+ err |= !EXIImm(chan, &cmd, sizeof(cmd), EXI_WRITE, NULL);
+ err |= !EXISync(chan);
+ err |= !EXIImm(chan, revision, sizeof(revision), EXI_READ, NULL);
+ err |= !EXISync(chan);
+ err |= !EXIDeselect(chan);
+ }
+
+ EXIUnlock(chan);
+ }
+
+ if (chan != 2 && dev == 0) {
+ EXIDetach(chan);
+ }
+
+ if (err) {
+ return FALSE;
+ }
+
+ return (*revision != 0xFFFFFFFF) ? TRUE : FALSE;
+}
+
+void __OSEnableBarnacle(s32 chan, u32 dev) {
+ u32 id;
+
+ if (!EXIGetID(chan, dev, &id)) {
+ return;
+ }
+
+ switch (id) {
+ case EXI_MEMORY_CARD_59:
+ case EXI_MEMORY_CARD_123:
+ case EXI_MEMORY_CARD_251:
+ case EXI_MEMORY_CARD_507:
+ case EXI_USB_ADAPTER:
+ case EXI_NPDP_GDEV:
+ case EXI_MODEM:
+ case 0x03010000:
+ case 0x04020100:
+ case EXI_ETHER:
+ case 0x04020300:
+ case 0x04220000:
+ case EXI_RS232C:
+ case EXI_MIC:
+ case EXI_AD16:
+ case EXI_STREAM_HANGER:
+ case 0x80000004:
+ case 0x80000008:
+ case 0x80000010:
+ case 0x80000020:
+ case 0xFFFFFFFF:
+ break;
+ default:
+ if (ProbeBarnacle(chan, dev, &id)) {
+ Chan = chan;
+ Dev = dev;
+ Enabled = BarnacleEnabled = 0xA5FF005A;
+ break;
+ }
+ }
+}
+
+int InitializeUART(void) {
+ if (BarnacleEnabled == 0xA5FF005A) {
+ return 0;
+ }
+
+ if ((OSGetConsoleType() & 0x10000000) == 0) {
+ Enabled = 0;
+ return 2;
+ }
+
+ Chan = 0;
+ Dev = 1;
+ Enabled = 0xA5FF005A;
+ return 0;
+}
+
+int ReadUARTN(void) {
+ return 4;
+}
+
+static int QueueLength(void) {
+ u32 cmd;
+
+ if (EXISelect(Chan, Dev, 3) == 0) {
+ return -1;
+ }
+
+ cmd = 0x20010000;
+ EXIImm(Chan, &cmd, sizeof(cmd), EXI_WRITE, 0);
+ EXISync(Chan);
+ EXIImm(Chan, &cmd, 1, EXI_READ, 0);
+ EXISync(Chan);
+ EXIDeselect(Chan);
+ return 0x10 - (cmd >> 0x18);
+}
+
+int WriteUARTN(void *buf, u32 len) {
+ u32 cmd;
+ s32 xLen;
+ int qLen;
+ char* ptr;
+ int locked;
+ int error;
+
+ if ((Enabled - 0xA5FF0000) != 0x5A) {
+ return 2;
+ }
+
+ locked = EXILock(Chan, Dev, 0);
+ if (locked == 0) {
+ return 0;
+ } else {
+ ptr = (char*)buf;
+ }
+
+ while ((u32)ptr - (u32)buf < len) {
+ if (*(s8*)ptr == 0xA) {
+ *ptr = 0xD;
+ }
+ ptr++;
+ }
+ error = 0;
+ cmd = 0xA0010000;
+
+ while (len != 0) {
+ qLen = QueueLength();
+ if (qLen < 0) {
+ error = 3;
+ break;
+ }
+
+ if ((qLen >= 0xC) || (qLen >= len)) {
+ if (EXISelect(Chan, Dev, EXI_FREQ_8M) == 0) {
+ error = 3;
+ break;
+ }
+
+ EXIImm(Chan, &cmd, sizeof(cmd), EXI_WRITE, 0);
+ EXISync(Chan);
+
+ while((qLen != 0) && (len != 0)) {
+ if ((qLen < 4) && (qLen < len)) {
+ break;
+ }
+
+ xLen = len < 4 ? (long)len : 4;
+
+ EXIImm(Chan, buf, xLen, EXI_WRITE, 0);
+ (char*)buf += xLen;
+ len -= xLen;
+ qLen -= xLen;
+ EXISync(Chan);
+ }
+ EXIDeselect(Chan);
+ }
+ }
+
+ EXIUnlock(Chan);
+ return error;
+}
diff --git a/src/revolution/gd/GDBase.c b/src/revolution/gd/GDBase.c
new file mode 100644
index 0000000000..2195dc602d
--- /dev/null
+++ b/src/revolution/gd/GDBase.c
@@ -0,0 +1,45 @@
+#include <revolution/gd.h>
+#include <revolution/os.h>
+
+GDLObj* __GDCurrentDL = NULL;
+static GDOverflowCb overflowcb = NULL;
+
+void GDInitGDLObj(GDLObj* dl, void* start, u32 length) {
+ ASSERTMSGLINE(49, !((u32)start & 0x1F), "start must be aligned to 32 bytes");
+ ASSERTMSGLINE(50, !((u32)length & 0x1F), "length must be aligned to 32 bytes");
+ dl->start = start;
+ dl->ptr = start;
+ dl->top = (u8*)start + length;
+ dl->length = length;
+}
+
+void GDFlushCurrToMem(void) {
+ DCFlushRange(__GDCurrentDL->start, __GDCurrentDL->length);
+}
+
+void GDPadCurr32(void) {
+ u32 n;
+
+ n = (u32)__GDCurrentDL->ptr & 0x1F;
+ if (n != 0) {
+ for (; n < 32; n = n + 1) {
+ __GDWrite(0);
+ }
+ }
+}
+
+void GDOverflowed(void) {
+ if (overflowcb) {
+ (*overflowcb)();
+ } else {
+ ASSERTMSGLINE(86, 0, "GDWrite: display list overflowed");
+ }
+}
+
+void GDSetOverflowCallback(GDOverflowCb callback) {
+ overflowcb = callback;
+}
+
+GDOverflowCb GDGetOverflowCallback(void) {
+ return overflowcb;
+}
diff --git a/src/revolution/gd/GDFile.c b/src/revolution/gd/GDFile.c
new file mode 100644
index 0000000000..0d890a8516
--- /dev/null
+++ b/src/revolution/gd/GDFile.c
@@ -0,0 +1,64 @@
+#include <revolution/gd.h>
+#include <revolution/os.h>
+
+#define ROUND(n, a) (((u32)(n) + (a)-1) & ~((a)-1))
+
+s32 GDReadDLFile(const char* fName, u32* numDLs, u32* numPLs, GDGList** DLDescArray, GDGList** PLDescArray) {
+ DVDFileInfo finfo;
+ u32 length;
+ u32 i;
+ u8* buf;
+ GDFileHeader* hdr;
+
+ *numDLs = *numPLs = 0;
+ *DLDescArray = *PLDescArray = NULL;
+
+ if (!DVDOpen(fName, &finfo)) {
+ OSReport("Can't open file %s\n", fName);
+ return -3;
+ }
+
+ length = finfo.length;
+
+ if (NULL == (buf = OSAlloc(ROUND(length, 0x20)))) {
+ OSReport("Alloc failed\n");
+ DVDClose(&finfo);
+ return -5;
+ }
+
+ if (DVDReadPrio(&finfo, buf, ROUND(length, 0x20), 0, 2) != ROUND(length, 0x20)) {
+ OSReport("Error occurred when reading %s\n", fName);
+ DVDClose(&finfo);
+ OSFree(buf);
+ return -2;
+ }
+
+ DVDClose(&finfo);
+
+ hdr = (GDFileHeader*)buf;
+
+ if (hdr->versionNumber != GD_FILE_VERSION_NUMBER) {
+ OSReport("Bad version number for GDL file %s\n", fName);
+ OSFree(buf);
+ return -4;
+ }
+
+ *numDLs = hdr->numDLs;
+ *numPLs = hdr->numPLs;
+
+ // Loaded pointers are relative to disc start, remap
+ // them to be relative to RAM start
+ *DLDescArray = (GDGList*)((u32)hdr->DLDescArray + (u32)hdr);
+ *PLDescArray = (GDGList*)((u32)hdr->PLDescArray + (u32)hdr);
+
+ // And internal pointers too
+ for (i = 0; i < *numDLs; ++i) {
+ (*DLDescArray)[i].ptr = (void*)((u32)hdr + (u32)(*DLDescArray)[i].ptr);
+ }
+
+ for (i = 0; i < *numPLs; ++i) {
+ (*PLDescArray)[i].ptr = (void*)((u32)hdr + (u32)(*PLDescArray)[i].ptr);
+ }
+
+ return 0;
+}
diff --git a/src/revolution/gd/GDGeometry.c b/src/revolution/gd/GDGeometry.c
new file mode 100644
index 0000000000..79b098114e
--- /dev/null
+++ b/src/revolution/gd/GDGeometry.c
@@ -0,0 +1,418 @@
+#include <revolution/gd.h>
+#include <revolution/os.h>
+
+void GDSetVtxDescv(const GXVtxDescList* attrPtr) {
+ u32 nnorms = 0;
+ u32 ncols = 0;
+ u32 ntexs = 0;
+ u32 pnMtxIdx = GX_NONE;
+ u32 txMtxIdxMask = 0;
+ u32 posn = GX_DIRECT;
+ u32 norm = GX_NONE;
+ u32 col0 = GX_NONE;
+ u32 col1 = GX_NONE;
+ u32 tex0 = GX_NONE;
+ u32 tex1 = GX_NONE;
+ u32 tex2 = GX_NONE;
+ u32 tex3 = GX_NONE;
+ u32 tex4 = GX_NONE;
+ u32 tex5 = GX_NONE;
+ u32 tex6 = GX_NONE;
+ u32 tex7 = GX_NONE;
+
+ for (; attrPtr->attr != GX_VA_NULL; ++attrPtr) {
+ ASSERTMSGLINE(103, attrPtr->attr >= GX_VA_PNMTXIDX && attrPtr->attr <= GX_VA_MAX_ATTR, "GDSetVtxDescv: invalid attribute");
+ ASSERTMSGLINE(107, attrPtr->type >= GX_NONE && attrPtr->type <= GX_INDEX16, "GDSetVtxDescv: invalid type");
+
+ ASSERTMSGLINE(113, attrPtr->attr >= GX_VA_PNMTXIDX && attrPtr->attr <= GX_VA_TEX7MTXIDX ? (attrPtr->type == 0 || attrPtr->type == 1) : TRUE, "GDSetVtxDescv: invalid type for given attribute");
+
+ switch (attrPtr->attr) {
+ case GX_VA_PNMTXIDX:
+ pnMtxIdx = attrPtr->type;
+ break;
+ case GX_VA_TEX0MTXIDX:
+ txMtxIdxMask = (txMtxIdxMask & ~0x1) | (attrPtr->type << 0);
+ break;
+ case GX_VA_TEX1MTXIDX:
+ txMtxIdxMask = (txMtxIdxMask & ~0x2) | (attrPtr->type << 1);
+ break;
+ case GX_VA_TEX2MTXIDX:
+ txMtxIdxMask = (txMtxIdxMask & ~0x4) | (attrPtr->type << 2);
+ break;
+ case GX_VA_TEX3MTXIDX:
+ txMtxIdxMask = (txMtxIdxMask & ~0x8) | (attrPtr->type << 3);
+ break;
+ case GX_VA_TEX4MTXIDX:
+ txMtxIdxMask = (txMtxIdxMask & ~0x10) | (attrPtr->type << 4);
+ break;
+ case GX_VA_TEX5MTXIDX:
+ txMtxIdxMask = (txMtxIdxMask & ~0x20) | (attrPtr->type << 5);
+ break;
+ case GX_VA_TEX6MTXIDX:
+ txMtxIdxMask = (txMtxIdxMask & ~0x40) | (attrPtr->type << 6);
+ break;
+ case GX_VA_TEX7MTXIDX:
+ txMtxIdxMask = (txMtxIdxMask & ~0x80) | (attrPtr->type << 7);
+ break;
+ case GX_VA_POS:
+ posn = attrPtr->type;
+ break;
+ case GX_VA_NRM:
+ if (attrPtr->type != GX_NONE) {
+ norm = attrPtr->type;
+ nnorms = 1;
+ }
+ break;
+ case GX_VA_NBT:
+ if (attrPtr->type != GX_NONE) {
+ norm = attrPtr->type;
+ nnorms = 2;
+ }
+ break;
+ case GX_VA_CLR0:
+ col0 = attrPtr->type;
+ ncols += (col0 != GX_NONE);
+ break;
+ case GX_VA_CLR1:
+ col1 = attrPtr->type;
+ ncols += (col1 != GX_NONE);
+ break;
+ case GX_VA_TEX0:
+ tex0 = attrPtr->type;
+ ntexs += (tex0 != GX_NONE);
+ break;
+ case GX_VA_TEX1:
+ tex1 = attrPtr->type;
+ ntexs += (tex1 != GX_NONE);
+ break;
+ case GX_VA_TEX2:
+ tex2 = attrPtr->type;
+ ntexs += (tex2 != GX_NONE);
+ break;
+ case GX_VA_TEX3:
+ tex3 = attrPtr->type;
+ ntexs += (tex3 != GX_NONE);
+ break;
+ case GX_VA_TEX4:
+ tex4 = attrPtr->type;
+ ntexs += (tex4 != GX_NONE);
+ break;
+ case GX_VA_TEX5:
+ tex5 = attrPtr->type;
+ ntexs += (tex5 != GX_NONE);
+ break;
+ case GX_VA_TEX6:
+ tex6 = attrPtr->type;
+ ntexs += (tex6 != GX_NONE);
+ break;
+ case GX_VA_TEX7:
+ tex7 = attrPtr->type;
+ ntexs += (tex7 != GX_NONE);
+ break;
+ }
+ }
+
+ GDWriteCPCmd(CP_REG_VCD_LO_ID, CP_REG_VCD_LO(pnMtxIdx, txMtxIdxMask, posn, norm, col0, col1));
+ GDWriteCPCmd(CP_REG_VCD_HI_ID, CP_REG_VCD_HI(tex0, tex1, tex2, tex3, tex4, tex5, tex6, tex7));
+ GDWriteXFCmd(XF_REG_INVERTEXSPEC_ID, XF_REG_INVTXSPEC(ncols, nnorms, ntexs));
+}
+
+void GDSetVtxAttrFmtv(GXVtxFmt vtxfmt, const GXVtxAttrFmtList* list) {
+ u32 posCnt = GX_POS_XYZ;
+ u32 posType = GX_F32;
+ u32 posFrac = 0;
+
+ u32 nrmCnt = GX_NRM_XYZ;
+ u32 nrmType = GX_F32;
+ u32 nrmIdx3 = 0;
+
+ u32 c0Cnt = GX_CLR_RGBA;
+ u32 c0Type = GX_RGBA8;
+
+ u32 c1Cnt = GX_CLR_RGBA;
+ u32 c1Type = GX_RGBA8;
+
+ u32 tx0Cnt = GX_TEX_ST;
+ u32 tx0Type = GX_F32;
+ u32 tx0Frac = 0;
+
+ u32 tx1Cnt = GX_TEX_ST;
+ u32 tx1Type = GX_F32;
+ u32 tx1Frac = 0;
+
+ u32 tx2Cnt = GX_TEX_ST;
+ u32 tx2Type = GX_F32;
+ u32 tx2Frac = 0;
+
+ u32 tx3Cnt = GX_TEX_ST;
+ u32 tx3Type = GX_F32;
+ u32 tx3Frac = 0;
+
+ u32 tx4Cnt = GX_TEX_ST;
+ u32 tx4Type = GX_F32;
+ u32 tx4Frac = 0;
+
+ u32 tx5Cnt = GX_TEX_ST;
+ u32 tx5Type = GX_F32;
+ u32 tx5Frac = 0;
+
+ u32 tx6Cnt = GX_TEX_ST;
+ u32 tx6Type = GX_F32;
+ u32 tx6Frac = 0;
+
+ u32 tx7Cnt = GX_TEX_ST;
+ u32 tx7Type = GX_F32;
+ u32 tx7Frac = 0;
+
+ ASSERTMSGLINE(233, vtxfmt < GX_MAX_VTXFMT, "GDSetVtxAttrFmtv: invalid vtx fmt");
+
+ for (; list->attr != GX_VA_NULL; ++list) {
+ ASSERTMSGLINE(239, (list->attr >= GX_VA_POS && list->attr <= GX_VA_TEX7) || list->attr == GX_VA_NBT, "GDSetVtxAttrFmtv: invalid attribute");
+ ASSERTMSGLINE(240, list->frac < 32, "GDSetVtxAttrFmtv: invalid frac value");
+
+ switch (list->attr) {
+ case GX_VA_POS:
+ posCnt = list->cnt;
+ posType = list->type;
+ posFrac = list->frac;
+ break;
+ case GX_VA_NRM:
+ case GX_VA_NBT:
+ nrmType = list->type;
+ if (list->cnt == GX_NRM_NBT3) {
+ nrmCnt = GX_NRM_NBT;
+ nrmIdx3 = 1;
+ } else {
+ nrmCnt = list->cnt;
+ nrmIdx3 = 0;
+ }
+ break;
+ case GX_VA_CLR0:
+ c0Cnt = list->cnt;
+ c0Type = list->type;
+ break;
+ case GX_VA_CLR1:
+ c1Cnt = list->cnt;
+ c1Type = list->type;
+ break;
+ case GX_VA_TEX0:
+ tx0Cnt = list->cnt;
+ tx0Type = list->type;
+ tx0Frac = list->frac;
+ break;
+ case GX_VA_TEX1:
+ tx1Cnt = list->cnt;
+ tx1Type = list->type;
+ tx1Frac = list->frac;
+ break;
+ case GX_VA_TEX2:
+ tx2Cnt = list->cnt;
+ tx2Type = list->type;
+ tx2Frac = list->frac;
+ break;
+ case GX_VA_TEX3:
+ tx3Cnt = list->cnt;
+ tx3Type = list->type;
+ tx3Frac = list->frac;
+ break;
+ case GX_VA_TEX4:
+ tx4Cnt = list->cnt;
+ tx4Type = list->type;
+ tx4Frac = list->frac;
+ break;
+ case GX_VA_TEX5:
+ tx5Cnt = list->cnt;
+ tx5Type = list->type;
+ tx5Frac = list->frac;
+ break;
+ case GX_VA_TEX6:
+ tx6Cnt = list->cnt;
+ tx6Type = list->type;
+ tx6Frac = list->frac;
+ break;
+ case GX_VA_TEX7:
+ tx7Cnt = list->cnt;
+ tx7Type = list->type;
+ tx7Frac = list->frac;
+ }
+ }
+
+ GDWriteCPCmd(vtxfmt + CP_REG_VAT_GRP0_ID, CP_REG_VAT_GRP0(posCnt, posType, posFrac, nrmCnt, nrmType, c0Cnt, c0Type, c1Cnt, c1Type, tx0Cnt, tx0Type, tx0Frac, 1, nrmIdx3));
+ GDWriteCPCmd(vtxfmt + CP_REG_VAT_GRP1_ID, CP_REG_VAT_GRP1(tx1Cnt, tx1Type, tx1Frac, tx2Cnt, tx2Type, tx2Frac, tx3Cnt, tx3Type, tx3Frac, tx4Cnt, tx4Type, 1));
+ GDWriteCPCmd(vtxfmt + CP_REG_VAT_GRP2_ID, CP_REG_VAT_GRP2(tx4Frac, tx5Cnt, tx5Type, tx5Frac, tx6Cnt, tx6Type, tx6Frac, tx7Cnt, tx7Type, tx7Frac));
+}
+
+void GDSetArray(GXAttr attr, void* base_ptr, u8 stride) {
+ s32 cpAttr = attr == GX_VA_NBT ? GX_VA_TEX0MTXIDX : attr - GX_VA_POS;
+ GDWriteCPCmd(cpAttr + CP_REG_ARRAYBASE_ID, OSCachedToPhysical(base_ptr));
+ GDWriteCPCmd(cpAttr + CP_REG_ARRAYSTRIDE_ID, stride);
+}
+
+void GDSetArrayRaw(GXAttr attr, u32 base_ptr_raw, u8 stride) {
+ s32 cpAttr = attr == GX_VA_NBT ? GX_VA_TEX0MTXIDX : attr - GX_VA_POS;
+ GDWriteCPCmd(cpAttr + CP_REG_ARRAYBASE_ID, base_ptr_raw);
+ GDWriteCPCmd(cpAttr + CP_REG_ARRAYSTRIDE_ID, stride);
+}
+
+void GDPatchArrayPtr(void* base_ptr) {
+ GDWrite_u32(OSCachedToPhysical(base_ptr));
+}
+
+void GDSetTexCoordGen(GXTexCoordID dst_coord, GXTexGenType func, GXTexGenSrc src_param, u8 normalize, u32 postmtx) {
+ u32 form;
+ u32 tgType;
+ u32 proj;
+ u32 row;
+ u32 embossRow;
+ u32 embossLit;
+
+ form = 0;
+ proj = 0;
+ row = 5;
+ embossRow = 5;
+ embossLit = 0;
+
+ ASSERTMSGLINE(445, dst_coord < GX_MAX_TEXCOORD, "GDSetTexCoordGen: invalid texcoord ID");
+
+ switch(src_param) {
+ case GX_TG_POS:
+ row = 0;
+ form = 1;
+ break;
+ case GX_TG_NRM:
+ row = 1;
+ form = 1;
+ break;
+ case GX_TG_BINRM:
+ row = 3;
+ form = 1;
+ break;
+ case GX_TG_TANGENT:
+ row = 4;
+ form = 1;
+ break;
+ case GX_TG_COLOR0:
+ row = 2;
+ break;
+ case GX_TG_COLOR1:
+ row = 2;
+ break;
+ case GX_TG_TEX0:
+ row = 5;
+ break;
+ case GX_TG_TEX1:
+ row = 6;
+ break;
+ case GX_TG_TEX2:
+ row = 7;
+ break;
+ case GX_TG_TEX3:
+ row = 8;
+ break;
+ case GX_TG_TEX4:
+ row = 9;
+ break;
+ case GX_TG_TEX5:
+ row = 10;
+ break;
+ case GX_TG_TEX6:
+ row = 11;
+ break;
+ case GX_TG_TEX7:
+ row = 12;
+ break;
+ case GX_TG_TEXCOORD0:
+ embossRow = 0;
+ break;
+ case GX_TG_TEXCOORD1:
+ embossRow = 1;
+ break;
+ case GX_TG_TEXCOORD2:
+ embossRow = 2;
+ break;
+ case GX_TG_TEXCOORD3:
+ embossRow = 3;
+ break;
+ case GX_TG_TEXCOORD4:
+ embossRow = 4;
+ break;
+ case GX_TG_TEXCOORD5:
+ embossRow = 5;
+ break;
+ case GX_TG_TEXCOORD6:
+ embossRow = 6;
+ break;
+ default:
+ ASSERTMSGLINE(470, 0, "GDSetTexCoordGen: invalid texgen source");
+ break;
+ }
+
+ switch (func) {
+ case GX_TG_MTX2x4:
+ tgType = 0;
+ break;
+ case GX_TG_MTX3x4:
+ tgType = 0;
+ proj = 1;
+ break;
+ case GX_TG_BUMP0:
+ case GX_TG_BUMP1:
+ case GX_TG_BUMP2:
+ case GX_TG_BUMP3:
+ case GX_TG_BUMP4:
+ case GX_TG_BUMP5:
+ case GX_TG_BUMP6:
+ case GX_TG_BUMP7:
+ ASSERTMSGLINE(494, src_param >= GX_TG_TEXCOORD0 && src_param <= GX_TG_TEXCOORD6, "GDSetTexCoordGen: invalid emboss source");
+ tgType = 1;
+ embossLit = func - GX_TG_BUMP0;
+ break;
+ case GX_TG_SRTG:
+ if (src_param == GX_TG_COLOR0) {
+ tgType = 2;
+ } else {
+ tgType = 3;
+ }
+ break;
+ default:
+ ASSERTMSGLINE(508, 0, "GDSetTexCoordGen: invalid texgen function");
+ break;
+ }
+
+ GDWriteXFCmd(dst_coord + XF_REG_TEX0_ID, XF_REG_TEX(proj, form, tgType, row, embossRow, embossLit));
+ GDWriteXFCmd(dst_coord + XF_REG_DUALTEX0_ID, XF_REG_DUALTEX(postmtx - 0x40, normalize));
+}
+
+void GDSetCullMode(GXCullMode mode) {
+ static u8 cm2hw[4] = { 0, 2, 1, 3 };
+
+ GDWriteBPCmd(0xFE00C000);
+ GDWriteBPCmd(cm2hw[mode] << 14);
+}
+
+void GDSetGenMode(u8 nTexGens, u8 nChans, u8 nTevs) {
+ GDWriteBPCmd(0xFE003C3F);
+ GDWriteBPCmd(BP_GEN_MODE(nTexGens, nChans, nTevs - 1, 0, 0));
+
+ GDWriteXFCmd(XF_REG_NUMCOLORS_ID, nChans);
+ GDWriteXFCmd(XF_REG_NUMTEX_ID, nTexGens);
+}
+
+void GDSetGenMode2(u8 nTexGens, u8 nChans, u8 nTevs, u8 nInds, GXCullMode cm) {
+ static u8 cm2hw[4] = { 0, 2, 1, 3 };
+
+ GDWriteBPCmd(0xFE07FC3F);
+ GDWriteBPCmd(BP_GEN_MODE(nTexGens, nChans, nTevs - 1, cm2hw[cm], nInds));
+
+ GDWriteXFCmd(XF_REG_NUMCOLORS_ID, nChans);
+ GDWriteXFCmd(XF_REG_NUMTEX_ID, nTexGens);
+}
+
+void GDSetLPSize(u8 lineWidth, u8 pointSize, GXTexOffset lineOffset, GXTexOffset pointOffset, u8 lineHalfAspect) {
+ GDWriteBPCmd(BP_LP_SIZE(lineWidth, pointSize, lineOffset, pointOffset, lineHalfAspect, 0x22));
+}
+
+void GDSetCoPlanar(u8 enable) {
+ GDWriteBPCmd(0xFE080000);
+ GDWriteBPCmd(enable << 19);
+}
diff --git a/src/revolution/gd/GDIndirect.c b/src/revolution/gd/GDIndirect.c
new file mode 100644
index 0000000000..b2d221fd71
--- /dev/null
+++ b/src/revolution/gd/GDIndirect.c
@@ -0,0 +1,270 @@
+#include <revolution/gd.h>
+#include <revolution/os.h>
+
+void GDSetTevIndirect(GXTevStageID tev_stage, GXIndTexStageID ind_stage,
+ GXIndTexFormat format, GXIndTexBiasSel bias_sel,
+ GXIndTexMtxID matrix_sel, GXIndTexWrap wrap_s,
+ GXIndTexWrap wrap_t, u8 add_prev, u8 utc_lod,
+ GXIndTexAlphaSel alpha_sel) {
+ GDWriteBPCmd(BP_TEV_INDIRECT(
+ ind_stage & 3,
+ format & 3,
+ bias_sel & 7,
+ alpha_sel & 3,
+ matrix_sel & 0xF,
+ wrap_s & 7,
+ wrap_t & 7,
+ utc_lod & 1,
+ add_prev & 1,
+ 0x10 + (tev_stage & 0xF)
+ ));
+}
+
+void GDSetIndTexMtx(GXIndTexMtxID mtx_id, const f32 offset[2][3], s8 scale_exp) {
+ u32 id_offset;
+
+ switch (mtx_id) {
+ case GX_ITM_0:
+ id_offset = 0;
+ break;
+ case GX_ITM_1:
+ id_offset = 3;
+ break;
+ case GX_ITM_2:
+ id_offset = 6;
+ break;
+ default:
+ ASSERTMSGLINE(111, 0, "GDSetIndTexMtx: Invalid matrix id");
+ break;
+ }
+
+ scale_exp += 17;
+
+ GDWriteBPCmd(BP_IND_MTX(
+ (s32)(offset[0][0] * 0x400) & 0x7FF,
+ (s32)(offset[1][0] * 0x400) & 0x7FF,
+ scale_exp & 3,
+ 6 + id_offset
+ ));
+
+ GDWriteBPCmd(BP_IND_MTX(
+ (s32)(offset[0][1] * 0x400) & 0x7FF,
+ (s32)(offset[1][1] * 0x400) & 0x7FF,
+ (scale_exp >> 2) & 3,
+ 7 + id_offset
+ ));
+
+ GDWriteBPCmd(BP_IND_MTX(
+ (s32)(offset[0][2] * 0x400) & 0x7FF,
+ (s32)(offset[1][2] * 0x400) & 0x7FF,
+ (scale_exp >> 4) & 3,
+ 8 + id_offset
+ ));
+}
+
+void GDSetIndTexCoordScale(GXIndTexStageID indStageEven, GXIndTexScale scaleS0,
+ GXIndTexScale scaleT0, GXIndTexScale scaleS1,
+ GXIndTexScale scaleT1) {
+ GDWriteBPCmd(BP_IND_TEXCOORD_SCALE(
+ scaleS0 & 0xF,
+ scaleT0 & 0xF,
+ scaleS1 & 0xF,
+ scaleT1 & 0xF,
+ 0x25 + ((indStageEven >> 1) & 1)
+ ));
+}
+
+void GDSetIndTexOrder(GXTexCoordID texCoord0, GXTexMapID texMap0,
+ GXTexCoordID texCoord1, GXTexMapID texMap1,
+ GXTexCoordID texCoord2, GXTexMapID texMap2,
+ GXTexCoordID texCoord3, GXTexMapID texMap3) {
+ GDWriteBPCmd(BP_IND_TEX_ORDER(
+ texMap0 & 7,
+ texCoord0 & 7,
+ texMap1 & 7,
+ texCoord1 & 7,
+ texMap2 & 7,
+ texCoord2 & 7,
+ texMap3 & 7,
+ texCoord3 & 7,
+ 0x27
+ ));
+}
+
+void GDSetTevDirect(GXTevStageID tev_stage) {
+ GDSetTevIndirect(tev_stage, GX_INDTEXSTAGE0, GX_ITF_8, GX_ITB_NONE, GX_ITM_OFF, GX_ITW_OFF, GX_ITW_OFF, 0, 0, GX_ITBA_OFF);
+}
+
+void GDSetTevIndWarp(GXTevStageID tev_stage, GXIndTexStageID ind_stage, u8 signed_offset, u8 replace_mode, GXIndTexMtxID matrix_sel) {
+ GXIndTexWrap wrap;
+ wrap = replace_mode ? GX_ITW_0 : GX_ITW_OFF;
+
+ GDSetTevIndirect(tev_stage,
+ ind_stage,
+ GX_ITF_8,
+ signed_offset ? GX_ITB_STU : GX_ITB_NONE,
+ matrix_sel,
+ wrap,
+ wrap,
+ 0,
+ 0,
+ GX_ITBA_OFF);
+}
+
+void GDSetTevIndTile(GXTevStageID tev_stage, GXIndTexStageID ind_stage,
+ u16 tilesize_s, u16 tilesize_t, u16 tilespacing_s,
+ u16 tilespacing_t, GXIndTexFormat format,
+ GXIndTexMtxID matrix_sel, GXIndTexBiasSel bias_sel,
+ GXIndTexAlphaSel alpha_sel) {
+ GXIndTexWrap wrap_s;
+ GXIndTexWrap wrap_t;
+ f32 mtx[2][3];
+
+ switch (tilesize_s) {
+ case 256:
+ wrap_s = GX_ITW_256;
+ break;
+ case 128:
+ wrap_s = GX_ITW_128;
+ break;
+ case 64:
+ wrap_s = GX_ITW_64;
+ break;
+ case 32:
+ wrap_s = GX_ITW_32;
+ break;
+ case 16:
+ wrap_s = GX_ITW_16;
+ break;
+ default:
+ ASSERTMSGLINE(268, 0, "GDSetTevIndTile: Invalid tilesize for S coordinate");
+ wrap_s = GX_ITW_OFF;
+ break;
+ }
+
+ switch (tilesize_t) {
+ case 256:
+ wrap_t = GX_ITW_256;
+ break;
+ case 128:
+ wrap_t = GX_ITW_128;
+ break;
+ case 64:
+ wrap_t = GX_ITW_64;
+ break;
+ case 32:
+ wrap_t = GX_ITW_32;
+ break;
+ case 16:
+ wrap_t = GX_ITW_16;
+ break;
+ default:
+ ASSERTMSGLINE(280, 0, "GDSetTevIndTile: Invalid tilesize for T coordinate");
+ wrap_t = GX_ITW_OFF;
+ break;
+ }
+
+ mtx[0][0] = (f32)tilespacing_s / 0x400;
+ mtx[0][1] = mtx[0][2] = 0.0f;
+
+ mtx[1][1] = (f32)tilespacing_t / 0x400;
+ mtx[1][0] = mtx[1][2] = 0.0f;
+ GDSetIndTexMtx(matrix_sel, mtx, 10);
+
+ GDSetTevIndirect(tev_stage,
+ ind_stage,
+ format,
+ bias_sel,
+ matrix_sel,
+ wrap_s,
+ wrap_t,
+ 0,
+ 1,
+ alpha_sel);
+}
+
+void GDSetTevIndBumpST(GXTevStageID tev_stage, GXIndTexStageID ind_stage, GXIndTexMtxID matrix_sel) {
+ GXIndTexMtxID sm;
+ GXIndTexMtxID tm;
+
+ switch(matrix_sel) {
+ case GX_ITM_0:
+ sm = GX_ITM_S0;
+ tm = GX_ITM_T0;
+ break;
+ case GX_ITM_1:
+ sm = GX_ITM_S1;
+ tm = GX_ITM_T1;
+ break;
+ case GX_ITM_2:
+ sm = GX_ITM_S2;
+ tm = GX_ITM_T2;
+ break;
+ default:
+ ASSERTMSGLINE(332, 0, "GDSetTevIndBumpST: Invalid matrix selection");
+ break;
+ }
+
+ GDSetTevIndirect(tev_stage,
+ ind_stage,
+ GX_ITF_8,
+ GX_ITB_ST,
+ sm,
+ GX_ITW_0,
+ GX_ITW_0,
+ 0,
+ 0,
+ GX_ITBA_OFF);
+
+ GDSetTevIndirect(tev_stage + 1,
+ ind_stage,
+ GX_ITF_8,
+ GX_ITB_ST,
+ tm,
+ GX_ITW_0,
+ GX_ITW_0,
+ 1,
+ 0,
+ GX_ITBA_OFF);
+
+ GDSetTevIndirect(tev_stage + 2,
+ ind_stage,
+ GX_ITF_8,
+ GX_ITB_NONE,
+ GX_ITM_OFF,
+ GX_ITW_OFF,
+ GX_ITW_OFF,
+ 1,
+ 0,
+ GX_ITBA_OFF);
+}
+
+void GDSetTevIndBumpXYZ(GXTevStageID tev_stage, GXIndTexStageID ind_stage, GXIndTexMtxID matrix_sel) {
+ GDSetTevIndirect(tev_stage,
+ ind_stage,
+ GX_ITF_8,
+ GX_ITB_STU,
+ matrix_sel,
+ GX_ITW_OFF,
+ GX_ITW_OFF,
+ 0,
+ 0,
+ GX_ITBA_OFF);
+}
+
+void GDSetTevIndRepeat(GXTevStageID tev_stage) {
+ GDSetTevIndirect(tev_stage,
+ GX_INDTEXSTAGE0,
+ GX_ITF_8,
+ GX_ITB_NONE,
+ GX_ITM_OFF,
+ GX_ITW_0,
+ GX_ITW_0,
+ 1,
+ 0,
+ GX_ITBA_OFF);
+}
+
+void __GDSetIndTexMask(u32 mask) {
+ GDWriteBPCmd(BP_IND_MASK(mask & 0xFF, 0xF));
+}
diff --git a/src/revolution/gd/GDLight.c b/src/revolution/gd/GDLight.c
new file mode 100644
index 0000000000..15e6a64233
--- /dev/null
+++ b/src/revolution/gd/GDLight.c
@@ -0,0 +1,206 @@
+#include <revolution/gd.h>
+#include <math.h>
+
+void GDSetLightAttn(GXLightID light, f32 a0, f32 a1, f32 a2, f32 k0, f32 k1, f32 k2) {
+ GDWriteXFCmdHdr(__GDLightID2Offset(light) + XF_LIGHT_ATTN_ID, 6);
+ GDWrite_f32(a0);
+ GDWrite_f32(a1);
+ GDWrite_f32(a2);
+ GDWrite_f32(k0);
+ GDWrite_f32(k1);
+ GDWrite_f32(k2);
+}
+
+void GDSetLightSpot(GXLightID light, f32 cutoff, GXSpotFn spot_func) {
+ f32 a0;
+ f32 a1;
+ f32 a2;
+ f32 r;
+ f32 d;
+ f32 cr;
+
+ if (cutoff <= 0.0f || cutoff > 90.0f) {
+ spot_func = 0;
+ }
+
+ r = M_PI * cutoff / 180.0f;
+ cr = cosf(r);
+
+ switch(spot_func) {
+ case GX_SP_FLAT:
+ a0 = -1000.0f * cr;
+ a1 = 1000.0f;
+ a2 = 0.0f;
+ break;
+ case GX_SP_COS:
+ a0 = -cr / (1.0f - cr);
+ a1 = 1.0f / (1.0f - cr);
+ a2 = 0.0f;
+ break;
+ case GX_SP_COS2:
+ a0 = 0.0f;
+ a1 = -cr / (1.0f - cr);
+ a2 = 1.0f / (1.0f - cr);
+ break;
+ case GX_SP_SHARP:
+ d = (1.0f - cr) * (1.0f - cr);
+ a0 = (cr * (cr - 2.0f)) / d;
+ a1 = 2.0f / d;
+ a2 = -1.0f / d;
+ break;
+ case GX_SP_RING1:
+ d = (1.0f - cr) * (1.0f - cr);
+ a0 = (-4.0f * cr) / d;
+ a1 = (4.0f * (1.0f + cr)) / d;
+ a2 = -4.0f / d;
+ break;
+ case GX_SP_RING2:
+ d = (1.0f - cr) * (1.0f - cr);
+ a0 = 1.0f - (2.0f * cr * cr) / d;
+ a1 = (4.0f * cr) / d;
+ a2 = -2.0f / d;
+ break;
+ case GX_SP_OFF:
+ default:
+ a0 = 1.0f;
+ a1 = 0.0f;
+ a2 = 0.0f;
+ break;
+ }
+
+ GDWriteXFCmdHdr(__GDLightID2Offset(light) + XF_LIGHT_ATTN_ID, 3);
+ GDWrite_f32(a0);
+ GDWrite_f32(a1);
+ GDWrite_f32(a2);
+}
+
+void GDSetLightDistAttn(GXLightID light, f32 ref_dist, f32 ref_br, GXDistAttnFn dist_func) {
+ f32 k0;
+ f32 k1;
+ f32 k2;
+
+ if (ref_dist < 0.0f || ref_br <= 0.0f || ref_br >= 1.0f) {
+ dist_func = 0;
+ }
+
+ switch (dist_func) {
+ case GX_DA_GENTLE:
+ k0 = 1.0f;
+ k1 = (1.0f - ref_br) / (ref_br * ref_dist);
+ k2 = 0.0f;
+ break;
+ case GX_DA_MEDIUM:
+ k0 = 1.0f;
+ k1 = (0.5f * (1.0f - ref_br)) / (ref_br * ref_dist);
+ k2 = (0.5f * (1.0f - ref_br)) / (ref_dist * (ref_br * ref_dist));
+ break;
+ case GX_DA_STEEP:
+ k0 = 1.0f;
+ k1 = 0.0f;
+ k2 = (1.0f - ref_br) / (ref_dist * (ref_br * ref_dist));
+ break;
+ case GX_DA_OFF:
+ default:
+ k0 = 1.0f;
+ k1 = 0.0f;
+ k2 = 0.0f;
+ break;
+ }
+
+ GDWriteXFCmdHdr(__GDLightID2Offset(light) + XF_LIGHT_DISTATTN_ID, 3);
+ GDWrite_f32(k0);
+ GDWrite_f32(k1);
+ GDWrite_f32(k2);
+}
+
+void GDSetLightColor(GXLightID light, GXColor color) {
+ GDWriteXFCmd(__GDLightID2Offset(light) + XF_LIGHT_COLOR_ID, color.r << 24 | color.g << 16 | color.b << 8 | color.a);
+}
+
+void GDSetLightPos(GXLightID light, f32 x, f32 y, f32 z) {
+ GDWriteXFCmdHdr(__GDLightID2Offset(light) + XF_LIGHT_POS_ID, 3);
+ GDWrite_f32(x);
+ GDWrite_f32(y);
+ GDWrite_f32(z);
+}
+
+void GDSetLightDir(GXLightID light, f32 nx, f32 ny, f32 nz) {
+ GDWriteXFCmdHdr(__GDLightID2Offset(light) + XF_LIGHT_DIR_ID, 3);
+ GDWrite_f32(nx);
+ GDWrite_f32(ny);
+ GDWrite_f32(nz);
+}
+
+void GDSetSpecularDirHA(GXLightID light, f32 nx, f32 ny, f32 nz, f32 hx, f32 hy, f32 hz) {
+ f32 px;
+ f32 py;
+ f32 pz;
+ px = 1000000000000000000.0f * -nx;
+ py = 1000000000000000000.0f * -ny;
+ pz = 1000000000000000000.0f * -nz;
+
+ GDWriteXFCmdHdr(__GDLightID2Offset(light) + XF_LIGHT_SPEC_DIR_ID, 6);
+ GDWrite_f32(px);
+ GDWrite_f32(py);
+ GDWrite_f32(pz);
+ GDWrite_f32(hx);
+ GDWrite_f32(hy);
+ GDWrite_f32(hz);
+}
+
+void GDSetSpecularDir(GXLightID light, f32 nx, f32 ny, f32 nz) {
+ f32 px;
+ f32 py;
+ f32 pz;
+ f32 hx;
+ f32 hy;
+ f32 hz;
+ f32 mag;
+
+ hx = -nx;
+ hy = -ny;
+ hz = 1.0f + -nz;
+
+ mag = hx * hx + hy * hy + hz * hz;
+ if (mag != 0.0f) {
+ mag = 1.0f / sqrtf(mag);
+ }
+
+ hx = hx * mag;
+ hy = hy * mag;
+ hz = hz * mag;
+ px = 1000000000000000000.0f * -nx;
+ py = 1000000000000000000.0f * -ny;
+ pz = 1000000000000000000.0f * -nz;
+
+ GDWriteXFCmdHdr(__GDLightID2Offset(light) + XF_LIGHT_SPEC_DIR_ID, 6);
+ GDWrite_f32(px);
+ GDWrite_f32(py);
+ GDWrite_f32(pz);
+ GDWrite_f32(hx);
+ GDWrite_f32(hy);
+ GDWrite_f32(hz);
+}
+
+void GDLoadLightObjIndx(u32 lt_obj_indx, GXLightID light) {
+ GDWriteXFIndxDCmd(__GDLightID2Offset(light) + XF_LIGHT_ID, 0x10, lt_obj_indx);
+}
+
+void GDSetChanAmbColor(GXChannelID chan, GXColor color) {
+ GDWriteXFCmd((chan & 1) + XF_REG_AMBIENT0_ID, color.r << 24 | color.g << 16 | color.b << 8 | color.a);
+}
+
+void GDSetChanMatColor(GXChannelID chan, GXColor color) {
+ GDWriteXFCmd((chan & 1) + XF_REG_MATERIAL0_ID, color.r << 24 | color.g << 16 | color.b << 8 | color.a);
+}
+
+void GDSetChanCtrl(GXChannelID chan, u8 enable, GXColorSrc amb_src, GXColorSrc mat_src, u32 light_mask, GXDiffuseFn diff_fn, GXAttnFn attn_fn) {
+ u32 reg;
+
+ reg = XF_REG_CHAN_CTRL(mat_src, enable, light_mask & 0xF, amb_src, attn_fn == GX_AF_SPEC ? GX_DF_NONE : diff_fn, attn_fn != GX_AF_NONE, attn_fn != GX_AF_SPEC, (light_mask >> 4) & 0xF);
+ GDWriteXFCmd((chan & 3) + XF_REG_COLOR0CNTRL_ID, reg);
+
+ if (chan == 4 || chan == 5) {
+ GDWriteXFCmd(chan + XF_REG_MATERIAL0_ID, reg);
+ }
+}
diff --git a/src/revolution/gd/GDPixel.c b/src/revolution/gd/GDPixel.c
new file mode 100644
index 0000000000..a14ddceee3
--- /dev/null
+++ b/src/revolution/gd/GDPixel.c
@@ -0,0 +1,118 @@
+#include <revolution/gd.h>
+#include <revolution/os.h>
+
+void GDSetFog(GXFogType type, f32 startz, f32 endz, f32 nearz, f32 farz, GXColor color) {
+ f32 A;
+ f32 B;
+ f32 B_mant;
+ f32 C;
+ f32 A_f;
+ u32 b_expn;
+ u32 b_m;
+ u32 a_hex;
+ u32 c_hex;
+ u32 fsel;
+ u32 proj;
+
+ ASSERTMSGLINE(62, farz >= 0.0f, "GDSetFog: The farz should be positive value");
+ ASSERTMSGLINE(63, farz >= nearz, "GDSetFog: The farz should be larger than nearz");
+
+ fsel = type & 7;
+ proj = (type >> 3) & 1;
+
+ if (proj != 0) {
+ if (farz == nearz || endz == startz) {
+ A_f = 0.0f;
+ C = 0.0f;
+ } else {
+ A = 1.0f / (endz - startz);
+ A_f = (farz - nearz) * A;
+ C = (startz - nearz) * A;
+ }
+
+ b_expn = 0;
+ b_m = 0;
+ } else {
+ if (farz == nearz || endz == startz) {
+ A = 0.0f;
+ B = 0.5f;
+ C = 0.0f;
+ } else {
+ A = (farz * nearz) / ((farz - nearz) * (endz - startz));
+ B = farz / (farz - nearz);
+ C = startz / (endz - startz);
+ }
+
+ B_mant = B;
+ b_expn = 1;
+
+ while (B_mant > 1.0) {
+ B_mant *= 0.5f;
+ b_expn++;
+ }
+
+ while (B_mant > 0.0f && B_mant < 0.5) {
+ B_mant *= 2.0f;
+ b_expn--;
+ }
+
+ A_f = A / (1 << b_expn);
+ b_m = (u32) (8388638.0f * B_mant);
+ }
+
+ a_hex = *(u32*)&A_f;
+ c_hex = *(u32*)&C;
+
+ GDWriteBPCmd(BP_FOG_UNK0(a_hex >> 12, 0xEE));
+ GDWriteBPCmd(BP_FOG_UNK1(b_m, 0xEF));
+ GDWriteBPCmd(BP_FOG_UNK2(b_expn, 0xF0));
+ GDWriteBPCmd(BP_FOG_UNK3(c_hex >> 12, proj, fsel, 0xF1));
+ GDWriteBPCmd(BP_FOG_COLOR(color.r, color.g, color.b, 0xF2));
+}
+
+void GDSetBlendMode(GXBlendMode type, GXBlendFactor src_factor, GXBlendFactor dst_factor, GXLogicOp logic_op) {
+ GDWriteBPCmd(0xFE00FFE3);
+ GDWriteBPCmd(BP_BLEND_MODE(
+ type == GX_BM_BLEND || type == GX_BM_SUBTRACT,
+ type == GX_BM_LOGIC,
+ 0,
+ 0,
+ 0,
+ dst_factor,
+ src_factor,
+ type == GX_BM_SUBTRACT,
+ logic_op,
+ 0x41
+ ));
+}
+
+void GDSetBlendModeEtc(GXBlendMode type, GXBlendFactor src_factor,
+ GXBlendFactor dst_factor, GXLogicOp logic_op,
+ u8 color_update_enable, u8 alpha_update_enable,
+ u8 dither_enable) {
+ GDWriteBPCmd(BP_BLEND_MODE(
+ type == GX_BM_BLEND || type == GX_BM_SUBTRACT,
+ type == GX_BM_LOGIC,
+ dither_enable,
+ color_update_enable,
+ alpha_update_enable,
+ dst_factor,
+ src_factor,
+ type == GX_BM_SUBTRACT,
+ logic_op,
+ 0x41
+ ));
+}
+
+void GDSetZMode(u8 compare_enable, GXCompare func, u8 update_enable) {
+ GDWriteBPCmd(BP_Z_MODE(compare_enable, func, update_enable, 0x40));
+}
+
+void GDSetDstAlpha(u8 enable, u8 alpha) {
+ GDWriteBPCmd(BP_DST_ALPHA(alpha, enable, 0x42));
+}
+
+void GDSetDrawSync(u16 token) {
+ GDWriteBPCmd(BP_TOKEN(token, 0x48));
+ GDWriteBPCmd(BP_TOKEN(token, 0x47));
+}
diff --git a/src/revolution/gd/GDTev.c b/src/revolution/gd/GDTev.c
new file mode 100644
index 0000000000..4aba7e8686
--- /dev/null
+++ b/src/revolution/gd/GDTev.c
@@ -0,0 +1,159 @@
+#include <revolution/gd.h>
+#include <revolution/os.h>
+
+void GDSetTevOp(GXTevStageID stage, GXTevMode mode) {
+ GXTevColorArg carg = GX_CC_RASC;
+ GXTevAlphaArg aarg = GX_CA_RASA;
+
+ if (stage != GX_TEVSTAGE0) {
+ carg = GX_CC_CPREV;
+ aarg = GX_CA_APREV;
+ }
+
+ switch (mode) {
+ case GX_MODULATE:
+ GDSetTevColorCalc(stage, GX_CC_ZERO, GX_CC_TEXC, carg, GX_CC_ZERO, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, 1, GX_TEVPREV);
+ GDSetTevAlphaCalcAndSwap(stage, GX_CA_ZERO, GX_CA_TEXA, aarg, GX_CA_ZERO, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, 1, GX_TEVPREV, GX_TEV_SWAP0, GX_TEV_SWAP0);
+ break;
+ case GX_DECAL:
+ GDSetTevColorCalc(stage, carg, GX_CC_TEXC, GX_CC_TEXA, GX_CC_ZERO, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, 1, GX_TEVPREV);
+ GDSetTevAlphaCalcAndSwap(stage, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, aarg, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, 1, GX_TEVPREV, GX_TEV_SWAP0, GX_TEV_SWAP0);
+ break;
+ case GX_BLEND:
+ GDSetTevColorCalc(stage, carg, GX_CC_ONE, GX_CC_TEXC, GX_CC_ZERO, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, 1, GX_TEVPREV);
+ GDSetTevAlphaCalcAndSwap(stage, GX_CA_ZERO, GX_CA_TEXA, aarg, GX_CA_ZERO, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, 1, GX_TEVPREV, GX_TEV_SWAP0, GX_TEV_SWAP0);
+ break;
+ case GX_REPLACE:
+ GDSetTevColorCalc(stage, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO, GX_CC_TEXC, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, 1, GX_TEVPREV);
+ GDSetTevAlphaCalcAndSwap(stage, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_TEXA, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, 1, GX_TEVPREV, GX_TEV_SWAP0, GX_TEV_SWAP0);
+ break;
+ case GX_PASSCLR:
+ GDSetTevColorCalc(stage, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO, carg, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, 1, GX_TEVPREV);
+ GDSetTevAlphaCalcAndSwap(stage, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, aarg, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, 1, GX_TEVPREV, GX_TEV_SWAP0, GX_TEV_SWAP0);
+ break;
+ default:
+ ASSERTMSGLINE(110, 0, "GDSetTevOp: Invalid Tev Mode");
+ break;
+ }
+}
+
+void GDSetTevColorCalc(GXTevStageID stage, GXTevColorArg a, GXTevColorArg b,
+ GXTevColorArg c, GXTevColorArg d, GXTevOp op,
+ GXTevBias bias, GXTevScale scale, u8 clamp,
+ GXTevRegID out_reg) {
+ if (op <= GX_TEV_SUB) {
+ GDWriteBPCmd(BP_TEV_COLOR(d, c, b, a, bias, op & 1, clamp, scale, out_reg, stage * 2 + 0xC0));
+ } else {
+ GDWriteBPCmd(BP_TEV_COLOR(d, c, b, a, 3, op & 1, clamp, (op >> 1) & 3, out_reg, stage * 2 + 0xC0));
+ }
+}
+
+void GDSetTevAlphaCalcAndSwap(GXTevStageID stage, GXTevAlphaArg a,
+ GXTevAlphaArg b, GXTevAlphaArg c, GXTevAlphaArg d,
+ GXTevOp op, GXTevBias bias, GXTevScale scale,
+ u8 clamp, GXTevRegID out_reg,
+ GXTevSwapSel ras_sel, GXTevSwapSel tex_sel) {
+ if (op <= GX_TEV_SUB) {
+ GDWriteBPCmd(BP_TEV_ALPHA(ras_sel, tex_sel, d, c, b, a, bias, op & 1, clamp, scale, out_reg, stage * 2 + 0xC1));
+ } else {
+ GDWriteBPCmd(BP_TEV_ALPHA(ras_sel, tex_sel, d, c, b, a, 3, op & 1, clamp, (op >> 1) & 3, out_reg, stage * 2 + 0xC1));
+ }
+}
+
+void GDSetTevColor(GXTevRegID reg, GXColor color) {
+ u32 regRA;
+ u32 regBG;
+
+ regRA = BP_TEV_COLOR_REG_RA(color.r, color.a, 0, 0xE0 + reg * 2);
+ regBG = BP_TEV_COLOR_REG_BG(color.b, color.g, 0, 0xE1 + reg * 2);
+
+ GDWriteBPCmd(regRA);
+ GDWriteBPCmd(regBG);
+ GDWriteBPCmd(regBG);
+ GDWriteBPCmd(regBG);
+}
+
+void GDSetTevColorS10(GXTevRegID reg, GXColorS10 color) {
+ u32 regRA;
+ u32 regBG;
+
+ regRA = BP_TEV_COLOR_REG_RA(color.r & 0x7FF, color.a & 0x7FF, 0, 0xE0 + reg * 2);
+ regBG = BP_TEV_COLOR_REG_BG(color.b & 0x7FF, color.g & 0x7FF, 0, 0xE1 + reg * 2);
+
+ GDWriteBPCmd(regRA);
+ GDWriteBPCmd(regBG);
+ GDWriteBPCmd(regBG);
+ GDWriteBPCmd(regBG);
+}
+
+void GDSetTevKColor(GXTevKColorID reg, GXColor color) {
+ u32 regRA;
+ u32 regBG;
+
+ regRA = BP_TEV_COLOR_REG_RA(color.r, color.a, 1, 0xE0 + reg * 2);
+ regBG = BP_TEV_COLOR_REG_BG(color.b, color.g, 1, 0xE1 + reg * 2);
+
+ GDWriteBPCmd(regRA);
+ GDWriteBPCmd(regBG);
+}
+
+void GDSetTevKonstantSel(GXTevStageID evenStage, GXTevKColorSel kcsel0,
+ GXTevKAlphaSel kasel0, GXTevKColorSel kcsel1,
+ GXTevKAlphaSel kasel1) {
+ GDWriteBPCmd(0xFEFFFFF0);
+ GDWriteBPCmd(BP_TEV_KSEL(0, 0, kcsel0, kasel0, kcsel1, kasel1, evenStage / 2 + 0xF6));
+}
+
+void GDSetTevSwapModeTable(GXTevSwapSel table, GXTevColorChan red,
+ GXTevColorChan green, GXTevColorChan blue,
+ GXTevColorChan alpha) {
+ GDWriteBPCmd(0xFE00000F);
+ GDWriteBPCmd(BP_TEV_KSEL(red, green, 0, 0, 0, 0, table * 2 + 0xF6));
+
+ GDWriteBPCmd(0xFE00000F);
+ GDWriteBPCmd(BP_TEV_KSEL(blue, alpha, 0, 0, 0, 0, table * 2 + 0xF7));
+}
+
+void GDSetAlphaCompare(GXCompare comp0, u8 ref0, GXAlphaOp op, GXCompare comp1, u8 ref1) {
+ GDWriteBPCmd(BP_ALPHA_COMPARE(ref0, ref1, comp0, comp1, op, 0xF3));
+}
+
+void GDSetZTexture(GXZTexOp op, GXTexFmt fmt, u32 bias) {
+ u32 zfmt;
+
+ switch (fmt) {
+ case GX_TF_Z8:
+ zfmt = 0;
+ break;
+ case GX_TF_Z16:
+ zfmt = 1;
+ break;
+ case GX_TF_Z24X8:
+ zfmt = 2;
+ break;
+ default:
+ ASSERTMSGLINE(399, 0, "GDSetZTexture: Invalid format");
+ zfmt = 2;
+ break;
+ }
+
+ GDWriteBPCmd(BP_ZTEX_PARAMS_0(bias, 0xF4));
+ GDWriteBPCmd(BP_ZTEX_PARAMS_1(zfmt, op, 0xF5));
+}
+
+void GDSetTevOrder(GXTevStageID evenStage, GXTexCoordID coord0, GXTexMapID map0,
+ GXChannelID color0, GXTexCoordID coord1, GXTexMapID map1,
+ GXChannelID color1) {
+ static u8 c2r[] = { 0, 1, 0, 1, 0, 1, 7, 5, 6, 0, 0, 0, 0, 0, 0, 7 };
+ GDWriteBPCmd(BP_TEV_ORDER(
+ map0 & 7,
+ coord0 & 7,
+ map0 != GX_TEXMAP_NULL && !(map0 & GX_TEX_DISABLE),
+ c2r[color0 & 0xF],
+ map1 & 7,
+ coord1 & 7,
+ map1 != GX_TEXMAP_NULL && !(map1 & GX_TEX_DISABLE),
+ c2r[color1 & 0xF],
+ evenStage / 2 + 0x28
+ ));
+}
diff --git a/src/revolution/gd/GDTexture.c b/src/revolution/gd/GDTexture.c
new file mode 100644
index 0000000000..748132e4d0
--- /dev/null
+++ b/src/revolution/gd/GDTexture.c
@@ -0,0 +1,104 @@
+#include <revolution/gd.h>
+#include <revolution/os.h>
+
+u8 GD2HWFiltConv[] = {0, 4, 1, 5, 2, 6};
+
+u8 GDTexMode0Ids[8] = {128, 129, 130, 131, 160, 161, 162, 163};
+u8 GDTexMode1Ids[8] = {132, 133, 134, 135, 164, 165, 166, 167};
+u8 GDTexImage0Ids[8] = {136, 137, 138, 139, 168, 169, 170, 171};
+u8 GDTexImage1Ids[8] = {140, 141, 142, 143, 172, 173, 174, 175};
+u8 GDTexImage2Ids[8] = {144, 145, 146, 147, 176, 177, 178, 179};
+u8 GDTexImage3Ids[8] = {148, 149, 150, 151, 180, 181, 182, 183};
+u8 GDTexTlutIds[8] = {152, 153, 154, 155, 184, 185, 186, 187};
+
+void GDSetTexLookupMode(GXTexMapID id, GXTexWrapMode wrap_s,
+ GXTexWrapMode wrap_t, GXTexFilter min_filt,
+ GXTexFilter mag_filt, f32 min_lod, f32 max_lod,
+ f32 lod_bias, u8 bias_clamp, u8 do_edge_lod,
+ GXAnisotropy max_aniso) {
+ GDWriteBPCmd(BP_TEX_MODE0(wrap_s, wrap_t, mag_filt == TRUE, GD2HWFiltConv[min_filt], !do_edge_lod, (u8)(32.0f * lod_bias), max_aniso, bias_clamp, GDTexMode0Ids[id]));
+ GDWriteBPCmd(BP_TEX_MODE1((u8)(16.0f * min_lod), (u8)(16.0f * max_lod), GDTexMode1Ids[id]));
+}
+
+void GDSetTexImgAttr(GXTexMapID id, u16 width, u16 height, GXTexFmt format) {
+ GDWriteBPCmd(BP_IMAGE_ATTR(width - 1, height - 1, format, GDTexImage0Ids[id]));
+}
+
+void GDSetTexImgPtr(GXTexMapID id, void* image_ptr) {
+ GDWriteBPCmd(BP_IMAGE_PTR(OSCachedToPhysical(image_ptr) >> 5, GDTexImage3Ids[id]));
+}
+
+void GDSetTexImgPtrRaw(GXTexMapID id, u32 image_ptr_raw) {
+ GDWriteBPCmd(BP_IMAGE_PTR(image_ptr_raw, GDTexImage3Ids[id]));
+}
+
+void GDPatchTexImgPtr(void* image_ptr) {
+ GDWrite_u24(OSCachedToPhysical(image_ptr) >> 5);
+}
+
+void GDSetTexCached(GXTexMapID id, u32 tmem_even, GXTexCacheSize size_even,
+ u32 tmem_odd, GXTexCacheSize size_odd) {
+ GDWriteBPCmd(BP_TEX_CACHE_EVEN(tmem_even >> 5, size_even + 3, size_even + 3, 0, GDTexImage1Ids[id]));
+
+ if (size_odd != 3 && tmem_odd < 0x100000) {
+ GDWriteBPCmd(BP_TEX_CACHE_ODD(tmem_odd >> 5, size_odd + 3, size_odd + 3, GDTexImage2Ids[id]));
+ }
+}
+
+
+void GDSetTexPreLoaded(GXTexMapID id, u32 tmem_even, u32 tmem_odd) {
+ GDWriteBPCmd(BP_TEX_CACHE_EVEN(tmem_even >> 5, 0, 0, 1, GDTexImage1Ids[id]));
+
+ if (tmem_odd < 0x100000) {
+ GDWriteBPCmd(BP_TEX_CACHE_ODD(tmem_odd >> 5, 0, 0, GDTexImage2Ids[id]));
+ }
+}
+
+void GDSetTexTlut(GXTexMapID id, u32 tmem_addr, GXTlutFmt format) {
+ GDWriteBPCmd(BP_TEX_TLUT((tmem_addr - 0x80000) >> 9, format, GDTexTlutIds[id]));
+}
+
+void GDSetTexCoordScale(GXTexCoordID coord, u16 s_scale, u16 t_scale) {
+ GDWriteBPCmd(0xFE00FFFF);
+ GDWriteBPCmd(BP_TEXCOORD_S_SCALE(s_scale - 1, 0, 0, 0, 0, coord * 2 + 0x30));
+
+ GDWriteBPCmd(0xFE00FFFF);
+ GDWriteBPCmd(BP_TEXCOORD_T_SCALE(t_scale - 1, 0, 0, coord * 2 + 0x31));
+}
+
+void GDSetTexCoordScale2(GXTexCoordID coord, u16 s_scale, u8 s_bias,
+ u8 s_wrap, u16 t_scale, u8 t_bias, u8 t_wrap) {
+ GDWriteBPCmd(0xFE03FFFF);
+ GDWriteBPCmd(BP_TEXCOORD_S_SCALE(s_scale - 1, s_bias, s_wrap, 0, 0, coord * 2 + 0x30));
+ GDWriteBPCmd(BP_TEXCOORD_T_SCALE(t_scale - 1, t_bias, t_wrap, coord * 2 + 0x31));
+}
+
+void GDSetTexCoordScaleAndTOEs(GXTexCoordID coord, u16 s_scale, u8 s_bias,
+ u8 s_wrap, u16 t_scale, u8 t_bias, u8 t_wrap,
+ u8 line_offset, u8 point_offset) {
+ GDWriteBPCmd(BP_TEXCOORD_S_SCALE(s_scale - 1, s_bias, s_wrap, line_offset, point_offset, coord * 2 + 0x30));
+ GDWriteBPCmd(BP_TEXCOORD_T_SCALE(t_scale - 1, t_bias, t_wrap, coord * 2 + 0x31));
+}
+
+void GDLoadTlut(void* tlut_ptr, u32 tmem_addr, GXTlutSize size) {
+ ASSERTMSGLINE(488, !(tmem_addr & 0x1ff), "GDLoadTlut: invalid TMEM pointer");
+ ASSERTMSGLINE(489, size <= 0x400, "GDLoadTlut: invalid TLUT size");
+
+ GDWriteBPCmd(0xFEFFFF00);
+ GDWriteBPCmd(0xF000000);
+ GDWriteBPCmd(BP_LOAD_TLUT0(OSCachedToPhysical(tlut_ptr) >> 5, 0x64));
+ GDWriteBPCmd(BP_LOAD_TLUT1((tmem_addr - 0x80000) >> 9, size, 0x65));
+ GDWriteBPCmd(0xFEFFFF00);
+ GDWriteBPCmd(0xF000000);
+}
+
+void GDLoadTlutRaw(u32 tlut_ptr_raw, u32 tmem_addr, GXTlutSize size) {
+ ASSERTMSGLINE(527, size <= 0x400, "GDLoadTlut: invalid TLUT size");
+
+ GDWriteBPCmd(0xFEFFFF00);
+ GDWriteBPCmd(0xF000000);
+ GDWriteBPCmd(BP_LOAD_TLUT0(tlut_ptr_raw, 0x64));
+ GDWriteBPCmd(BP_LOAD_TLUT1((tmem_addr - 0x80000) >> 9, size, 0x65));
+ GDWriteBPCmd(0xFEFFFF00);
+ GDWriteBPCmd(0xF000000);
+}
diff --git a/src/revolution/gd/GDTransform.c b/src/revolution/gd/GDTransform.c
new file mode 100644
index 0000000000..c1c17ef0a1
--- /dev/null
+++ b/src/revolution/gd/GDTransform.c
@@ -0,0 +1,127 @@
+#include <revolution/gd.h>
+#include <revolution/os.h>
+
+void GDLoadPosMtxImm(const Mtx mtx, u32 id) {
+ GDWriteXFCmdHdr(4 * id, 12);
+ GDWrite_f32(mtx[0][0]);
+ GDWrite_f32(mtx[0][1]);
+ GDWrite_f32(mtx[0][2]);
+ GDWrite_f32(mtx[0][3]);
+ GDWrite_f32(mtx[1][0]);
+ GDWrite_f32(mtx[1][1]);
+ GDWrite_f32(mtx[1][2]);
+ GDWrite_f32(mtx[1][3]);
+ GDWrite_f32(mtx[2][0]);
+ GDWrite_f32(mtx[2][1]);
+ GDWrite_f32(mtx[2][2]);
+ GDWrite_f32(mtx[2][3]);
+}
+
+void GDLoadPosMtxIndx(u16 mtx_indx, u32 id) {
+ GDWriteXFIndxACmd(4 * id, 12, mtx_indx);
+}
+
+void GDLoadNrmMtxImm(const Mtx mtx, u32 id) {
+ GDWriteXFCmdHdr(id * 3 + 0x400, 9);
+ GDWrite_f32(mtx[0][0]);
+ GDWrite_f32(mtx[0][1]);
+ GDWrite_f32(mtx[0][2]);
+ GDWrite_f32(mtx[1][0]);
+ GDWrite_f32(mtx[1][1]);
+ GDWrite_f32(mtx[1][2]);
+ GDWrite_f32(mtx[2][0]);
+ GDWrite_f32(mtx[2][1]);
+ GDWrite_f32(mtx[2][2]);
+}
+
+void GDLoadNrmMtxImm3x3(const f32 mtx[3][3], u32 id) {
+ GDWriteXFCmdHdr(id * 3 + 0x400, 9);
+ GDWrite_f32(mtx[0][0]);
+ GDWrite_f32(mtx[0][1]);
+ GDWrite_f32(mtx[0][2]);
+ GDWrite_f32(mtx[1][0]);
+ GDWrite_f32(mtx[1][1]);
+ GDWrite_f32(mtx[1][2]);
+ GDWrite_f32(mtx[2][0]);
+ GDWrite_f32(mtx[2][1]);
+ GDWrite_f32(mtx[2][2]);
+}
+
+void GDLoadNrmMtxIndx3x3(u16 mtx_indx, u32 id) {
+ GDWriteXFIndxBCmd(id * 3 + 0x400, 9, mtx_indx);
+}
+
+void GDLoadTexMtxImm(const Mtx mtx, u32 id, GXTexMtxType type) {
+ u16 addr;
+ u8 count;
+
+ if (id >= 0x40) {
+ ASSERTMSGLINE(178, type == GX_MTX3x4, "GDLoadTexMtxImm: invalid matrix type");
+ addr = ((id - 0x40) << 2) + 0x500;
+ count = 12;
+ } else {
+ addr = 4 * id;
+ count = type == GX_MTX2x4 ? 8 : 12;
+ }
+
+ GDWriteXFCmdHdr(addr,count);
+ GDWrite_f32(mtx[0][0]);
+ GDWrite_f32(mtx[0][1]);
+ GDWrite_f32(mtx[0][2]);
+ GDWrite_f32(mtx[0][3]);
+ GDWrite_f32(mtx[1][0]);
+ GDWrite_f32(mtx[1][1]);
+ GDWrite_f32(mtx[1][2]);
+ GDWrite_f32(mtx[1][3]);
+
+ if (type == GX_MTX3x4) {
+ GDWrite_f32(mtx[2][0]);
+ GDWrite_f32(mtx[2][1]);
+ GDWrite_f32(mtx[2][2]);
+ GDWrite_f32(mtx[2][3]);
+ }
+}
+
+void GDLoadTexMtxIndx(u16 mtx_indx, u32 id, GXTexMtxType type) {
+ u16 addr;
+ u8 count;
+
+ if (id >= 0x40) {
+ ASSERTMSGLINE(227, type == GX_MTX3x4, "GDLoadTexMtxIndx: invalid matrix type");
+ addr = ((id - 0x40) << 2) + 0x500;
+ count = 12;
+ } else {
+ addr = 4 * id;
+ count = type == GX_MTX2x4 ? 8 : 12;
+ }
+
+ GDWriteXFIndxCCmd(addr, count, mtx_indx);
+}
+
+void GDSetCurrentMtx(u32 pn, u32 t0, u32 t1, u32 t2, u32 t3, u32 t4, u32 t5, u32 t6, u32 t7) {
+ u32 regA;
+ u32 regB;
+
+ regA = CP_MTX_REG_A(pn, t0, t1, t2, t3);
+ regB = CP_MTX_REG_B(t4, t5, t6, t7);
+
+ GDWriteCPCmd(CP_MTX_REG_A_ID, regA);
+ GDWriteCPCmd(CP_MTX_REG_B_ID, regB);
+ GDWriteXFCmdHdr(XF_REG_MATRIXINDEX0_ID, 2);
+ GDWrite_u32(regA);
+ GDWrite_u32(regB);
+}
+
+void GDSetProjection(const Mtx44 mtx, GXProjectionType type) {
+ u32 c;
+ c = type == GX_ORTHOGRAPHIC ? 3 : 2;
+
+ GDWriteXFCmdHdr(XF_REG_PROJECTIONA_ID, 7);
+ GDWrite_f32(mtx[0][0]);
+ GDWrite_f32(mtx[0][c]);
+ GDWrite_f32(mtx[1][1]);
+ GDWrite_f32(mtx[1][c]);
+ GDWrite_f32(mtx[2][2]);
+ GDWrite_f32(mtx[2][3]);
+ GDWrite_u32(type);
+}
diff --git a/src/revolution/gf/GFGeometry.cpp b/src/revolution/gf/GFGeometry.cpp
new file mode 100644
index 0000000000..699ab207c8
--- /dev/null
+++ b/src/revolution/gf/GFGeometry.cpp
@@ -0,0 +1,10 @@
+#include <revolution/gf.h>
+#include <revolution/gd.h>
+
+void GFSetGenMode2(u8 nTexGens, u8 nChans, u8 nTevs, u8 nInds, GXCullMode cm) {
+ static u8 cm2hw[4] = {0, 2, 1, 3};
+ GFWriteBPCmd(0xFE07FC3F);
+ GFWriteBPCmd(GF_GEN_MODE(nTexGens, nChans, nTevs - 1, cm2hw[cm], nInds));
+ GFWriteXFCmd(XF_REG_NUMCOLORS_ID, nChans);
+ GFWriteXFCmd(XF_REG_NUMTEX_ID, nTexGens);
+}
diff --git a/src/revolution/gf/GFLight.cpp b/src/revolution/gf/GFLight.cpp
new file mode 100644
index 0000000000..8234f0eda9
--- /dev/null
+++ b/src/revolution/gf/GFLight.cpp
@@ -0,0 +1,6 @@
+#include <revolution/gf.h>
+#include <revolution/gd.h>
+
+void GFSetChanAmbColor(GXChannelID chan, GXColor color) {
+ GFWriteXFCmd((chan & 1) + XF_REG_AMBIENT0_ID, color.r << 0x18 | color.g << 0x10 | color.b << 8 | color.a);
+}
diff --git a/src/revolution/gf/GFPixel.cpp b/src/revolution/gf/GFPixel.cpp
new file mode 100644
index 0000000000..b3dbf0723d
--- /dev/null
+++ b/src/revolution/gf/GFPixel.cpp
@@ -0,0 +1,74 @@
+#include <revolution/gf.h>
+#include <revolution/gd.h>
+
+void GFSetFog(GXFogType type, f32 startz, f32 endz, f32 nearz, f32 farz, GXColor color) {
+ f32 A;
+ f32 B;
+ f32 B_mant;
+ f32 C;
+ f32 A_f;
+ u32 b_expn;
+ u32 b_m;
+ u32 a_hex;
+ u32 c_hex;
+
+ ASSERTMSGLINE(62, farz >= 0.0f, "GFSetFog: The farz should be positive value");
+ ASSERTMSGLINE(63, farz >= nearz, "GFSetFog: The farz should be larger than nearz");
+
+ if (farz == nearz || endz == startz) {
+ A = 0.0f;
+ B = 0.5f;
+ C = 0.0f;
+ } else {
+ A = (farz * nearz) / ((farz - nearz) * (endz - startz));
+ B = farz / (farz - nearz);
+ C = startz / (endz - startz);
+ }
+
+ B_mant = B;
+ b_expn = 1;
+
+ while (B_mant > 1.0) {
+ B_mant *= 0.5f;
+ b_expn++;
+ }
+
+ while (B_mant > 0.0f && B_mant < 0.5) {
+ B_mant *= 2.0f;
+ b_expn--;
+ }
+
+ A_f = A / (1 << b_expn);
+ b_m = (u32) (8388638.0f * B_mant);
+
+ a_hex = *(u32*)&A_f;
+ c_hex = *(u32*)&C;
+
+ GFWriteBPCmd(BP_FOG_UNK0(a_hex >> 12, 0xEE));
+ GFWriteBPCmd(BP_FOG_UNK1(b_m, 0xEF));
+ GFWriteBPCmd(BP_FOG_UNK2(b_expn, 0xF0));
+ GFWriteBPCmd(BP_FOG_UNK3(c_hex >> 12, 0, type, 0xF1));
+ GFWriteBPCmd(BP_FOG_COLOR(color.r, color.g, color.b, 0xF2));
+}
+
+void GFSetBlendModeEtc(GXBlendMode type, GXBlendFactor src_factor,
+ GXBlendFactor dst_factor, GXLogicOp logic_op,
+ u8 color_update_enable, u8 alpha_update_enable,
+ u8 dither_enable) {
+ GFWriteBPCmd(BP_BLEND_MODE(
+ type == GX_BM_BLEND || type == GX_BM_SUBTRACT,
+ type == GX_BM_LOGIC,
+ dither_enable,
+ color_update_enable,
+ alpha_update_enable,
+ dst_factor,
+ src_factor,
+ type == GX_BM_SUBTRACT,
+ logic_op,
+ 0x41
+ ));
+}
+
+void GFSetZMode(u8 compare_enable, GXCompare func, u8 update_enable) {
+ GFWriteBPCmd(BP_Z_MODE(compare_enable, func, update_enable, 0x40));
+}
diff --git a/src/revolution/gf/GFTev.cpp b/src/revolution/gf/GFTev.cpp
new file mode 100644
index 0000000000..be98b5f0b6
--- /dev/null
+++ b/src/revolution/gf/GFTev.cpp
@@ -0,0 +1,15 @@
+#include <revolution/gf.h>
+#include <revolution/gd.h>
+
+void GFSetTevColorS10(GXTevRegID reg, GXColorS10 color) {
+ u32 regRA;
+ u32 regBG;
+
+ regRA = BP_TEV_COLOR_REG_RA(color.r & 0x7FF, color.a & 0x7FF, 0, 0xE0 + reg * 2);
+ regBG = BP_TEV_COLOR_REG_BG(color.b & 0x7FF, color.g & 0x7FF, 0, 0xE1 + reg * 2);
+
+ GFWriteBPCmd(regRA);
+ GFWriteBPCmd(regBG);
+ GFWriteBPCmd(regBG);
+ GFWriteBPCmd(regBG);
+}
diff --git a/src/revolution/gx/GXAttr.c b/src/revolution/gx/GXAttr.c
new file mode 100644
index 0000000000..b60f1063b9
--- /dev/null
+++ b/src/revolution/gx/GXAttr.c
@@ -0,0 +1,637 @@
+#include <revolution/gx.h>
+#include <revolution/os.h>
+
+#include "__gx.h"
+
+#define CHECK_ATTRPTR(line, attrPtr) ASSERTMSGLINE(line, (attrPtr) != NULL, "GXSetVtxDescv: attrPtr is NULL")
+#define CHECK_ATTRNAME(line, attr) ASSERTMSGLINE(line, (attr) >= GX_VA_PNMTXIDX, "GXSetVtxDesc: Invalid vertex attribute name")
+#define CHECK_ATTRNAME2(line, attr) ASSERTMSGLINE(line, (attr) <= GX_VA_TEX7 || (attr) == GX_VA_NBT, "GXSetVtxDesc: Invalid vertex attribute name")
+#define CHECK_ATTRNAME3(line, attr) ASSERTMSGLINE(line, (attr) >= GX_VA_PNMTXIDX && (attr) < GX_VA_MAX_ATTR, "GXSetVtxDesc: Invalid vertex attribute name")
+#define CHECK_ATTRNAME4(line, attr) ASSERTMSGLINE(line, ((attr) >= GX_VA_POS && (attr) <= GX_VA_TEX7) || (attr) == GX_VA_NBT, "GXSetVtxAttrFmt: Invalid vertex attribute name")
+#define CHECK_ATTRNAME5(line, attr) ASSERTMSGLINE(line, (attr) >= GX_VA_POS && (attr) <= GX_LIGHT_ARRAY, "GXSetArray: Invalid vertex attribute name")
+#define CHECK_ATTRTYPE(line, type) ASSERTMSGLINE(line, (type) >= GX_NONE && (type) <= GX_INDEX16, "GXSetVtxDesc: Invalid vertex attribute type")
+#define CHECK_VTXFMT(line, vtxfmt) ASSERTMSGLINE(line, (vtxfmt) < GX_MAX_VTXFMT, "GXSetVtxAttrFmt: Format Index is out of range")
+#define CHECK_FRAC(line, frac) ASSERTMSGLINE(line, (frac) < 32, "GXSetVtxAttrFmt: Frac value is >= 32")
+#define CHECK_LISTPTR(line, list) ASSERTMSGLINE(line, (list) != NULL, "GXSetVtxAttrFmt: list pointer is NULL")
+#define CHECK_MTXIDX(line, attr, type) ASSERTMSGLINE(line, (attr) > GX_VA_TEX7MTXIDX || (type) <= GX_VA_TEX0MTXIDX, "GXSetVtxDesc: GX_VA_*MTXIDX accepts GX_NONE or GX_DIRECT only")
+
+static void __GXXfVtxSpecs(void) {
+ u32 nCols;
+ u32 nNrm;
+ u32 nTex;
+ u32 reg;
+
+ nNrm = __GXData->hasBiNrms ? 2 : __GXData->hasNrms ? 1 : 0;
+
+#if DEBUG
+ nCols = CP_VCD_REG_LO_GET_COL0(__GXData->vcdLo) ? 1 : 0;
+ nCols += CP_VCD_REG_LO_GET_COL1(__GXData->vcdLo) ? 1 : 0;
+#else
+ nCols = (u32)__cntlzw((__GXData->vcdLo & CP_VCD_REG_LO_COLALL_MASK)>>CP_VCD_REG_LO_COLALL_SHIFT);
+ nCols = (33 - nCols) >> 1;
+#endif
+
+#if DEBUG
+ nTex = 0;
+ nTex += CP_VCD_REG_HI_GET_TEX0(__GXData->vcdHi) ? 1 : 0;
+ nTex += CP_VCD_REG_HI_GET_TEX1(__GXData->vcdHi) ? 1 : 0;
+ nTex += CP_VCD_REG_HI_GET_TEX2(__GXData->vcdHi) ? 1 : 0;
+ nTex += CP_VCD_REG_HI_GET_TEX3(__GXData->vcdHi) ? 1 : 0;
+ nTex += CP_VCD_REG_HI_GET_TEX4(__GXData->vcdHi) ? 1 : 0;
+ nTex += CP_VCD_REG_HI_GET_TEX5(__GXData->vcdHi) ? 1 : 0;
+ nTex += CP_VCD_REG_HI_GET_TEX6(__GXData->vcdHi) ? 1 : 0;
+ nTex += CP_VCD_REG_HI_GET_TEX7(__GXData->vcdHi) ? 1 : 0;
+#else
+ nTex = (u32)__cntlzw((__GXData->vcdHi & CP_VCD_REG_HI_TEXALL_MASK)>>CP_VCD_REG_HI_TEXALL_SHIFT);
+ nTex = (33 - nTex) >> 1;
+#endif
+
+ reg = XF_INVERTEXSPEC_F(nCols, nNrm, nTex);
+ GX_WRITE_XF_REG(0x1008, reg, 0);
+ __GXData->bpSentNot = 1;
+}
+
+static inline void SETVCDATTR(GXAttr Attr, GXAttrType Type) {
+ switch (Attr) {
+ case GX_VA_PNMTXIDX: SC_CP_VCD_REG_LO_SET_PMIDX(219, __GXData->vcdLo, Type); break;
+ case GX_VA_TEX0MTXIDX: SC_CP_VCD_REG_LO_SET_T0MIDX(220, __GXData->vcdLo, Type); break;
+ case GX_VA_TEX1MTXIDX: SC_CP_VCD_REG_LO_SET_T1MIDX(221, __GXData->vcdLo, Type); break;
+ case GX_VA_TEX2MTXIDX: SC_CP_VCD_REG_LO_SET_T2MIDX(222, __GXData->vcdLo, Type); break;
+ case GX_VA_TEX3MTXIDX: SC_CP_VCD_REG_LO_SET_T3MIDX(223, __GXData->vcdLo, Type); break;
+ case GX_VA_TEX4MTXIDX: SC_CP_VCD_REG_LO_SET_T4MIDX(224, __GXData->vcdLo, Type); break;
+ case GX_VA_TEX5MTXIDX: SC_CP_VCD_REG_LO_SET_T5MIDX(225, __GXData->vcdLo, Type); break;
+ case GX_VA_TEX6MTXIDX: SC_CP_VCD_REG_LO_SET_T6MIDX(226, __GXData->vcdLo, Type); break;
+ case GX_VA_TEX7MTXIDX: SC_CP_VCD_REG_LO_SET_T7MIDX(227, __GXData->vcdLo, Type); break;
+ case GX_VA_POS: SC_CP_VCD_REG_LO_SET_POS(228, __GXData->vcdLo, Type); break;
+ case GX_VA_NRM:
+ if (Type != GX_NONE) {
+ __GXData->hasNrms = 1;
+ __GXData->hasBiNrms = 0;
+ __GXData->nrmType = Type;
+ } else {
+ __GXData->hasNrms = 0;
+ }
+ break;
+ case GX_VA_NBT:
+ if (Type != GX_NONE) {
+ __GXData->hasBiNrms = 1;
+ __GXData->hasNrms = 0;
+ __GXData->nrmType = Type;
+ } else {
+ __GXData->hasBiNrms = 0;
+ }
+ break;
+ case GX_VA_CLR0: SC_CP_VCD_REG_LO_SET_COL0(253, __GXData->vcdLo, Type); break;
+ case GX_VA_CLR1: SC_CP_VCD_REG_LO_SET_COL1(254, __GXData->vcdLo, Type); break;
+ case GX_VA_TEX0: SC_CP_VCD_REG_HI_SET_TEX0(255, __GXData->vcdHi, Type); break;
+ case GX_VA_TEX1: SC_CP_VCD_REG_HI_SET_TEX1(256, __GXData->vcdHi, Type); break;
+ case GX_VA_TEX2: SC_CP_VCD_REG_HI_SET_TEX2(257, __GXData->vcdHi, Type); break;
+ case GX_VA_TEX3: SC_CP_VCD_REG_HI_SET_TEX3(258, __GXData->vcdHi, Type); break;
+ case GX_VA_TEX4: SC_CP_VCD_REG_HI_SET_TEX4(259, __GXData->vcdHi, Type); break;
+ case GX_VA_TEX5: SC_CP_VCD_REG_HI_SET_TEX5(260, __GXData->vcdHi, Type); break;
+ case GX_VA_TEX6: SC_CP_VCD_REG_HI_SET_TEX6(261, __GXData->vcdHi, Type); break;
+ case GX_VA_TEX7: SC_CP_VCD_REG_HI_SET_TEX7(262, __GXData->vcdHi, Type); break;
+ }
+}
+
+void GXSetVtxDesc(GXAttr attr, GXAttrType type) {
+ CHECK_GXBEGIN(271, "GXSetVtxDesc");
+ CHECK_ATTRNAME(274, attr);
+ CHECK_ATTRNAME2(276, attr);
+ CHECK_ATTRTYPE(278, type);
+ CHECK_MTXIDX(281, attr, type);
+
+ SETVCDATTR(attr, type);
+ if (__GXData->hasNrms || __GXData->hasBiNrms) {
+ SC_CP_VCD_REG_LO_SET_NRM(287, __GXData->vcdLo, __GXData->nrmType);
+ } else {
+ SC_CP_VCD_REG_LO_SET_NRM(0, __GXData->vcdLo, 0);
+ }
+ __GXData->dirtyState |= 8;
+}
+
+void GXSetVtxDescv(const GXVtxDescList *attrPtr) {
+ CHECK_GXBEGIN(313, "GXSetVtxDescv");
+ CHECK_ATTRPTR(314, attrPtr);
+
+ while (attrPtr->attr != GX_VA_NULL) {
+ CHECK_ATTRNAME(318, attrPtr->attr);
+ CHECK_ATTRNAME2(321, attrPtr->attr);
+ CHECK_ATTRTYPE(324, attrPtr->type);
+ SETVCDATTR(attrPtr->attr, attrPtr->type);
+ attrPtr++;
+ }
+
+ if (__GXData->hasNrms || __GXData->hasBiNrms) {
+ SC_CP_VCD_REG_LO_SET_NRM(333, __GXData->vcdLo, __GXData->nrmType);
+ } else {
+ SC_CP_VCD_REG_LO_SET_NRM(0, __GXData->vcdLo, 0);
+ }
+ __GXData->dirtyState |= 8;
+}
+
+void __GXSetVCD(void) {
+ GX_WRITE_CP_STRM_REG_alt(CP_VCD_LO, 0, __GXData->vcdLo, -12);
+ GX_WRITE_CP_STRM_REG_alt(CP_VCD_HI, 0, __GXData->vcdHi, -12);
+ __GXXfVtxSpecs();
+}
+
+void __GXCalculateVLim(void) {
+ static u8 tbl1[] = { 0, 4, 1, 2 };
+ static u8 tbl2[] = { 0, 8, 1, 2 };
+ static u8 tbl3[] = { 0, 12, 1, 2 };
+
+ if (__GXData->vNum != 0) {
+ GXCompCnt nc;
+ u32 vlm;
+ u32 b;
+ u32 vl;
+ u32 vh;
+ u32 va;
+
+ vl = __GXData->vcdLo;
+ vh = __GXData->vcdHi;
+ va = __GXData->vatA[0];
+ nc = CP_VAT_REG_A_GET_NRMCNT(va);
+
+ vlm = ((vl >> 0) & 0x1);
+ vlm += ((vl >> 1) & 0x1);
+ vlm += ((vl >> 2) & 0x1);
+ vlm += ((vl >> 3) & 0x1);
+ vlm += ((vl >> 4) & 0x1);
+ vlm += ((vl >> 5) & 0x1);
+ vlm += ((vl >> 6) & 0x1);
+ vlm += ((vl >> 7) & 0x1);
+ vlm += ((vl >> 8) & 0x1);
+ vlm += tbl3[((vl >> 9) & 0x3)];
+
+ b = (u32)((nc == GX_NRM_NBT) ? 3 : 1);
+
+ vlm += tbl3[((vl >> 11) & 0x3)] * b;
+ vlm += tbl1[((vl >> 13) & 0x3)];
+ vlm += tbl1[((vl >> 15) & 0x3)];
+ vlm += tbl2[((vh >> (0*2)) & 0x3)];
+ vlm += tbl2[((vh >> (1*2)) & 0x3)];
+ vlm += tbl2[((vh >> (2*2)) & 0x3)];
+ vlm += tbl2[((vh >> (3*2)) & 0x3)];
+ vlm += tbl2[((vh >> (4*2)) & 0x3)];
+ vlm += tbl2[((vh >> (5*2)) & 0x3)];
+ vlm += tbl2[((vh >> (6*2)) & 0x3)];
+ vlm += tbl2[((vh >> (7*2)) & 0x3)];
+
+ __GXData->vLim = (u16)vlm;
+ }
+}
+
+void GXGetVtxDesc(GXAttr attr, GXAttrType* type) {
+ u32 cpType;
+
+ CHECK_GXBEGIN(465, "GXGetVtxDesc");
+ CHECK_ATTRNAME3(467, attr);
+
+ switch (attr) {
+ case GX_VA_PNMTXIDX: cpType = CP_VCD_REG_LO_GET_PMIDX(__GXData->vcdLo); break;
+ case GX_VA_TEX0MTXIDX: cpType = CP_VCD_REG_LO_GET_T0MIDX(__GXData->vcdLo); break;
+ case GX_VA_TEX1MTXIDX: cpType = CP_VCD_REG_LO_GET_T1MIDX(__GXData->vcdLo); break;
+ case GX_VA_TEX2MTXIDX: cpType = CP_VCD_REG_LO_GET_T2MIDX(__GXData->vcdLo); break;
+ case GX_VA_TEX3MTXIDX: cpType = CP_VCD_REG_LO_GET_T3MIDX(__GXData->vcdLo); break;
+ case GX_VA_TEX4MTXIDX: cpType = CP_VCD_REG_LO_GET_T4MIDX(__GXData->vcdLo); break;
+ case GX_VA_TEX5MTXIDX: cpType = CP_VCD_REG_LO_GET_T5MIDX(__GXData->vcdLo); break;
+ case GX_VA_TEX6MTXIDX: cpType = CP_VCD_REG_LO_GET_T6MIDX(__GXData->vcdLo); break;
+ case GX_VA_TEX7MTXIDX: cpType = CP_VCD_REG_LO_GET_T7MIDX(__GXData->vcdLo); break;
+ case GX_VA_POS: cpType = CP_VCD_REG_LO_GET_POS(__GXData->vcdLo); break;
+ case GX_VA_NRM: cpType = __GXData->hasNrms ? CP_VCD_REG_LO_GET_NRM(__GXData->vcdLo) : 0; break;
+ case GX_VA_NBT: cpType = __GXData->hasBiNrms ? CP_VCD_REG_LO_GET_NRM(__GXData->vcdLo) : 0; break;
+ case GX_VA_CLR0: cpType = CP_VCD_REG_LO_GET_COL0(__GXData->vcdLo); break;
+ case GX_VA_CLR1: cpType = CP_VCD_REG_LO_GET_COL1(__GXData->vcdLo); break;
+ case GX_VA_TEX0: cpType = CP_VCD_REG_HI_GET_TEX0(__GXData->vcdHi); break;
+ case GX_VA_TEX1: cpType = CP_VCD_REG_HI_GET_TEX1(__GXData->vcdHi); break;
+ case GX_VA_TEX2: cpType = CP_VCD_REG_HI_GET_TEX2(__GXData->vcdHi); break;
+ case GX_VA_TEX3: cpType = CP_VCD_REG_HI_GET_TEX3(__GXData->vcdHi); break;
+ case GX_VA_TEX4: cpType = CP_VCD_REG_HI_GET_TEX4(__GXData->vcdHi); break;
+ case GX_VA_TEX5: cpType = CP_VCD_REG_HI_GET_TEX5(__GXData->vcdHi); break;
+ case GX_VA_TEX6: cpType = CP_VCD_REG_HI_GET_TEX6(__GXData->vcdHi); break;
+ case GX_VA_TEX7: cpType = CP_VCD_REG_HI_GET_TEX7(__GXData->vcdHi); break;
+ default: cpType = 0; break;
+ }
+ *type = cpType;
+}
+
+void GXGetVtxDescv(GXVtxDescList* vcd) {
+ GXAttr attr;
+
+ CHECK_GXBEGIN(518, "GXGetVtxDescv");
+ CHECK_ATTRPTR(520, vcd);
+
+ for (attr = GX_VA_PNMTXIDX; attr <= GX_VA_TEX7; attr++) {
+ vcd[attr].attr = attr;
+ GXGetVtxDesc(attr, &vcd[attr].type);
+ }
+
+ vcd[attr].attr = GX_VA_NBT;
+ GXGetVtxDesc(GX_VA_NBT, &vcd[attr].type);
+
+ attr++;
+ vcd[attr].attr = GX_VA_NULL;
+}
+
+void GXClearVtxDesc(void) {
+ CHECK_GXBEGIN(550, "GXClearVtxDesc");
+ __GXData->vcdLo = 0;
+ SC_CP_VCD_REG_LO_SET_POS(0, __GXData->vcdLo, GX_DIRECT);
+ __GXData->vcdHi = 0;
+ __GXData->hasNrms = 0;
+ __GXData->hasBiNrms = 0;
+ __GXData->dirtyState |= 8;
+}
+
+static inline void SETVAT(u32* va, u32* vb, u32* vc, GXAttr attr, GXCompCnt cnt, GXCompType type, u8 shft) {
+ switch (attr) {
+ case GX_VA_POS:
+ SC_CP_VAT_REG_A_SET_POSCNT(590, *va, cnt);
+ SC_CP_VAT_REG_A_SET_POSFMT(591, *va, type);
+ SC_CP_VAT_REG_A_SET_POSSHFT(592, *va, shft);
+ break;
+ case GX_VA_NRM:
+ case GX_VA_NBT:
+ SC_CP_VAT_REG_A_SET_NRMFMT(600, *va, type);
+ if (cnt == GX_NRM_NBT3) {
+ SC_CP_VAT_REG_A_SET_NRMCNT(0, *va, 1);
+ SC_CP_VAT_REG_A_SET_NORMALINDEX3(0, *va, 1);
+ } else {
+ SC_CP_VAT_REG_A_SET_NRMCNT(606, *va, cnt);
+ SC_CP_VAT_REG_A_SET_NORMALINDEX3(606, *va, 0);
+ }
+ break;
+ case GX_VA_CLR0:
+ SC_CP_VAT_REG_A_SET_COL0CNT(612, *va, cnt);
+ SC_CP_VAT_REG_A_SET_COL0FMT(613, *va, type);
+ break;
+ case GX_VA_CLR1:
+ SC_CP_VAT_REG_A_SET_COL1CNT(616, *va, cnt);
+ SC_CP_VAT_REG_A_SET_COL1FMT(617, *va, type);
+ break;
+ case GX_VA_TEX0:
+ SC_CP_VAT_REG_A_SET_TEX0CNT(620, *va, cnt);
+ SC_CP_VAT_REG_A_SET_TEX0FMT(621, *va, type);
+ SC_CP_VAT_REG_A_SET_TEX0SHFT(622, *va, shft);
+ break;
+ case GX_VA_TEX1:
+ SC_CP_VAT_REG_B_SET_TEX1CNT(625, *vb, cnt);
+ SC_CP_VAT_REG_B_SET_TEX1FMT(626, *vb, type);
+ SC_CP_VAT_REG_B_SET_TEX1SHFT(627, *vb, shft);
+ break;
+ case GX_VA_TEX2:
+ SC_CP_VAT_REG_B_SET_TEX2CNT(630, *vb, cnt);
+ SC_CP_VAT_REG_B_SET_TEX2FMT(631, *vb, type);
+ SC_CP_VAT_REG_B_SET_TEX2SHFT(632, *vb, shft);
+ break;
+ case GX_VA_TEX3:
+ SC_CP_VAT_REG_B_SET_TEX3CNT(635, *vb, cnt);
+ SC_CP_VAT_REG_B_SET_TEX3FMT(636, *vb, type);
+ SC_CP_VAT_REG_B_SET_TEX3SHFT(637, *vb, shft);
+ break;
+ case GX_VA_TEX4:
+ SC_CP_VAT_REG_B_SET_TEX4CNT(640, *vb, cnt);
+ SC_CP_VAT_REG_B_SET_TEX4FMT(641, *vb, type);
+ SC_CP_VAT_REG_C_SET_TEX4SHFT(642, *vc, shft);
+ break;
+ case GX_VA_TEX5:
+ SC_CP_VAT_REG_C_SET_TEX5CNT(645, *vc, cnt);
+ SC_CP_VAT_REG_C_SET_TEX5FMT(646, *vc, type);
+ SC_CP_VAT_REG_C_SET_TEX5SHFT(647, *vc, shft);
+ break;
+ case GX_VA_TEX6:
+ SC_CP_VAT_REG_C_SET_TEX6CNT(650, *vc, cnt);
+ SC_CP_VAT_REG_C_SET_TEX6FMT(651, *vc, type);
+ SC_CP_VAT_REG_C_SET_TEX6SHFT(652, *vc, shft);
+ break;
+ case GX_VA_TEX7:
+ SC_CP_VAT_REG_C_SET_TEX7CNT(655, *vc, cnt);
+ SC_CP_VAT_REG_C_SET_TEX7FMT(656, *vc, type);
+ SC_CP_VAT_REG_C_SET_TEX7SHFT(657, *vc, shft);
+ break;
+ }
+}
+
+void GXSetVtxAttrFmt(GXVtxFmt vtxfmt, GXAttr attr, GXCompCnt cnt, GXCompType type, u8 frac) {
+ u32* va;
+ u32* vb;
+ u32* vc;
+
+ CHECK_GXBEGIN(673, "GXSetVtxAttrFmt");
+ CHECK_VTXFMT(674, vtxfmt);
+ CHECK_ATTRNAME4(678, attr);
+ CHECK_FRAC(679, frac);
+
+ va = &__GXData->vatA[vtxfmt];
+ vb = &__GXData->vatB[vtxfmt];
+ vc = &__GXData->vatC[vtxfmt];
+ SETVAT(va, vb, vc, attr, cnt, type, frac);
+
+#if DEBUG
+ __GXVerifyVATImm(attr, cnt, type, frac);
+#endif
+
+ __GXData->dirtyState |= 0x10;
+ __GXData->dirtyVAT |= (u8)(1 << (u8)vtxfmt);
+}
+
+void GXSetVtxAttrFmtv(GXVtxFmt vtxfmt, const GXVtxAttrFmtList* list) {
+ u32* va;
+ u32* vb;
+ u32* vc;
+
+ CHECK_GXBEGIN(720, "GXSetVtxAttrFmtv");
+ CHECK_LISTPTR(721, list);
+ CHECK_VTXFMT(722, vtxfmt);
+
+ va = &__GXData->vatA[vtxfmt];
+ vb = &__GXData->vatB[vtxfmt];
+ vc = &__GXData->vatC[vtxfmt];
+
+ while (list->attr != GX_VA_NULL) {
+ CHECK_ATTRNAME4(732, list->attr);
+ CHECK_FRAC(733, list->frac);
+ SETVAT(va, vb, vc, list->attr, list->cnt, list->type, list->frac);
+#if DEBUG
+ __GXVerifyVATImm(list->attr, list->cnt, list->type, list->frac);
+#endif
+ list++;
+ }
+ __GXData->dirtyState |= 0x10;
+ __GXData->dirtyVAT |= (u8)(1 << (u8)vtxfmt);
+}
+
+void __GXSetVAT(void) {
+ s32 i;
+ u32 dirty = __GXData->dirtyVAT;
+
+ i = 0;
+ do {
+ if (dirty & 1) {
+ GX_WRITE_SOME_REG4(8, i | 0x70, __GXData->vatA[i], i - 12);
+ GX_WRITE_SOME_REG4(8, i | 0x80, __GXData->vatB[i], i - 12);
+ GX_WRITE_SOME_REG4(8, i | 0x90, __GXData->vatC[i], i - 12);
+ }
+
+ dirty >>= 1;
+ i++;
+ } while (dirty != 0);
+
+ GX_WRITE_U8(0);
+ __GXData->dirtyVAT = 0;
+}
+
+static inline u8 GetFracForNrm(GXCompType type) {
+ u8 frac;
+
+ switch (type) {
+ case GX_S8:
+ frac = 6;
+ break;
+ case GX_S16:
+ frac = 14;
+ break;
+ default:
+ case GX_U16:
+ frac = 0;
+ break;
+ }
+
+ return frac;
+}
+
+void GXGetVtxAttrFmt(GXVtxFmt fmt, GXAttr attr, GXCompCnt* cnt, GXCompType* type, u8* frac) {
+ u32* va;
+ u32* vb;
+ u32* vc;
+
+ CHECK_GXBEGIN(844, "GXGetVtxAttrFmt");
+ CHECK_VTXFMT(845, fmt);
+
+ va = &__GXData->vatA[fmt];
+ vb = &__GXData->vatB[fmt];
+ vc = &__GXData->vatC[fmt];
+
+ switch (attr) {
+ case GX_VA_POS:
+ *cnt = CP_VAT_REG_A_GET_POSCNT(*va);
+ *type = CP_VAT_REG_A_GET_POSFMT(*va);
+ *frac = CP_VAT_REG_A_GET_POSSHFT(*va);
+ return;
+ case GX_VA_NRM:
+ case GX_VA_NBT:
+ *cnt = CP_VAT_REG_A_GET_NRMCNT(*va);
+ if (*cnt == GX_TEX_ST && CP_VAT_REG_A_GET_NORMALINDEX3(*va)) {
+ *cnt = GX_NRM_NBT3;
+ }
+ *type = CP_VAT_REG_A_GET_NRMFMT(*va);
+ *frac = GetFracForNrm(*type);
+ return;
+ case GX_VA_CLR0:
+ *cnt = CP_VAT_REG_A_GET_COL0CNT(*va);
+ *type = CP_VAT_REG_A_GET_COL0FMT(*va);
+ *frac = 0;
+ return;
+ case GX_VA_CLR1:
+ *cnt = CP_VAT_REG_A_GET_COL1CNT(*va);
+ *type = CP_VAT_REG_A_GET_COL1FMT(*va);
+ *frac = 0;
+ return;
+ case GX_VA_TEX0:
+ *cnt = CP_VAT_REG_A_GET_TEX0CNT(*va);
+ *type = CP_VAT_REG_A_GET_TEX0FMT(*va);
+ *frac = CP_VAT_REG_A_GET_TEX0SHFT(*va);
+ return;
+ case GX_VA_TEX1:
+ *cnt = CP_VAT_REG_B_GET_TEX1CNT(*vb);
+ *type = CP_VAT_REG_B_GET_TEX1FMT(*vb);
+ *frac = CP_VAT_REG_B_GET_TEX1SHFT(*vb);
+ return;
+ case GX_VA_TEX2:
+ *cnt = CP_VAT_REG_B_GET_TEX2CNT(*vb);
+ *type = CP_VAT_REG_B_GET_TEX2FMT(*vb);
+ *frac = CP_VAT_REG_B_GET_TEX2SHFT(*vb);
+ return;
+ case GX_VA_TEX3:
+ *cnt = CP_VAT_REG_B_GET_TEX3CNT(*vb);
+ *type = CP_VAT_REG_B_GET_TEX3FMT(*vb);
+ *frac = CP_VAT_REG_B_GET_TEX3SHFT(*vb);
+ return;
+ case GX_VA_TEX4:
+ *cnt = CP_VAT_REG_B_GET_TEX4CNT(*vb);
+ *type = CP_VAT_REG_B_GET_TEX4FMT(*vb);
+ *frac = CP_VAT_REG_C_GET_TEX4SHFT(*vc);
+ return;
+ case GX_VA_TEX5:
+ *cnt = CP_VAT_REG_C_GET_TEX5CNT(*vc);
+ *type = CP_VAT_REG_C_GET_TEX5FMT(*vc);
+ *frac = CP_VAT_REG_C_GET_TEX5SHFT(*vc);
+ return;
+ case GX_VA_TEX6:
+ *cnt = CP_VAT_REG_C_GET_TEX6CNT(*vc);
+ *type = CP_VAT_REG_C_GET_TEX6FMT(*vc);
+ *frac = CP_VAT_REG_C_GET_TEX6SHFT(*vc);
+ return;
+ case GX_VA_TEX7:
+ *cnt = CP_VAT_REG_C_GET_TEX7CNT(*vc);
+ *type = CP_VAT_REG_C_GET_TEX7FMT(*vc);
+ *frac = CP_VAT_REG_C_GET_TEX7SHFT(*vc);
+ return;
+ default:
+ *cnt = GX_POS_XYZ;
+ *type = GX_U8;
+ *frac = 0;
+ return;
+ }
+}
+
+void GXGetVtxAttrFmtv(GXVtxFmt fmt, GXVtxAttrFmtList* vat) {
+ GXAttr attr;
+
+ CHECK_GXBEGIN(941, "GXGetVtxAttrFmtv");
+ CHECK_LISTPTR(942, vat);
+ CHECK_VTXFMT(943, fmt);
+
+ for (attr = GX_VA_POS; attr <= GX_VA_TEX7; attr++) {
+ vat->attr = attr;
+ GXGetVtxAttrFmt(fmt, attr, &vat->cnt, &vat->type, &vat->frac);
+ vat++;
+ }
+
+ vat->attr = GX_VA_NULL;
+}
+
+void GXSetArray(GXAttr attr, void* base_ptr, u8 stride) {
+ GXAttr cpAttr;
+ u32 phyAddr;
+
+ CHECK_GXBEGIN(974, "GXSetArray");
+ attr = (attr == GX_VA_NBT) ? GX_VA_NRM : attr;
+
+ CHECK_ATTRNAME5(977, attr);
+ cpAttr = (GXAttr)(attr - GX_VA_POS);
+ phyAddr = GX_PHY_ADDR(base_ptr);
+
+ GX_WRITE_CP_STRM_REG_alt2(CP_ARRAY_BASE, cpAttr, phyAddr, cpAttr - 12);
+ GX_WRITE_CP_STRM_REG_alt3(CP_ARRAY_STRIDE, cpAttr, (u32)stride, cpAttr - 12);
+}
+
+void GXInvalidateVtxCache(void) {
+ CHECK_GXBEGIN(999, "GXInvalidateVtxCache");
+ GX_WRITE_U8(0x48);
+}
+
+void GXSetTexCoordGen2(GXTexCoordID dst_coord, GXTexGenType func, GXTexGenSrc src_param, u32 mtx, GXBool normalize, u32 pt_texmtx) {
+ u32 reg = 0;
+ u32 row;
+ u32 bumprow;
+ u32 form;
+ GXAttr mtxIdAttr;
+
+ CHECK_GXBEGIN(1041, "GXSetTexCoordGen");
+ ASSERTMSGLINE(1042, dst_coord < GX_MAX_TEXCOORD, "GXSetTexCoordGen: Invalid coordinate Id");
+
+ form = 0;
+ row = bumprow = 5;
+ switch (src_param) {
+ case GX_TG_POS: row = 0; form = 1; break;
+ case GX_TG_NRM: row = 1; form = 1; break;
+ case GX_TG_BINRM: row = 3; form = 1; break;
+ case GX_TG_TANGENT: row = 4; form = 1; break;
+ case GX_TG_COLOR0: row = 2; break;
+ case GX_TG_COLOR1: row = 2; break;
+ case GX_TG_TEX0: row = 5; break;
+ case GX_TG_TEX1: row = 6; break;
+ case GX_TG_TEX2: row = 7; break;
+ case GX_TG_TEX3: row = 8; break;
+ case GX_TG_TEX4: row = 9; break;
+ case GX_TG_TEX5: row = 10; break;
+ case GX_TG_TEX6: row = 11; break;
+ case GX_TG_TEX7: row = 12; break;
+ case GX_TG_TEXCOORD0: bumprow = 5; break;
+ case GX_TG_TEXCOORD1: bumprow = 6; break;
+ case GX_TG_TEXCOORD2: bumprow = 7; break;
+ case GX_TG_TEXCOORD3: bumprow = 8; break;
+ case GX_TG_TEXCOORD4: bumprow = 9; break;
+ case GX_TG_TEXCOORD5: bumprow = 10; break;
+ case GX_TG_TEXCOORD6: bumprow = 11; break;
+ default:
+ ASSERTMSGLINE(1070, 0, "GXSetTexCoordGen: Invalid source parameter");
+ break;
+ }
+
+ switch (func) {
+ case GX_TG_MTX2x4:
+ SC_XF_TEX_SET_PROJECTION(1080, reg, 0);
+ SC_XF_TEX_SET_INPUT_FORM(1080, reg, form);
+ SC_XF_TEX_SET_TEXGEN_TYPE(1082, reg, 0);
+ SC_XF_TEX_SET_SOURCE_ROW(1082, reg, row);
+ break;
+ case GX_TG_MTX3x4:
+ SC_XF_TEX_SET_PROJECTION(1087, reg, 1);
+ SC_XF_TEX_SET_INPUT_FORM(1087, reg, form);
+ SC_XF_TEX_SET_TEXGEN_TYPE(1087, reg, 0);
+ SC_XF_TEX_SET_SOURCE_ROW(1089, reg, row);
+ break;
+ case GX_TG_BUMP0:
+ case GX_TG_BUMP1:
+ case GX_TG_BUMP2:
+ case GX_TG_BUMP3:
+ case GX_TG_BUMP4:
+ case GX_TG_BUMP5:
+ case GX_TG_BUMP6:
+ case GX_TG_BUMP7:
+ ASSERTMSGLINE(1102, src_param >= 12 && src_param <= 18, "GXSetTexCoordGen: Bump source texture value is invalid");
+ SC_XF_TEX_SET_PROJECTION(1104, reg, 0);
+ SC_XF_TEX_SET_INPUT_FORM(1104, reg, form);
+ SC_XF_TEX_SET_TEXGEN_TYPE(1106, reg, 1);
+ SC_XF_TEX_SET_SOURCE_ROW(1106, reg, row);
+ SC_XF_TEX_SET_BUMP_MAP_SOURCE(1107, reg, src_param - 12);
+ SC_XF_TEX_SET_BUMP_MAP_LIGHT(1108, reg, func - 2);
+ break;
+ case GX_TG_SRTG:
+ SC_XF_TEX_SET_PROJECTION(1113, reg, 0);
+ SC_XF_TEX_SET_INPUT_FORM(1113, reg, form);
+ if (src_param == GX_TG_COLOR0) {
+ SC_XF_TEX_SET_TEXGEN_TYPE(0, reg, 2);
+ } else {
+ SC_XF_TEX_SET_TEXGEN_TYPE(0, reg, 3);
+ }
+ SC_XF_TEX_SET_SOURCE_ROW(0, reg, 2);
+ break;
+ default:
+ ASSERTMSGLINE(1124, 0, "GXSetTexCoordGen: Invalid function");
+ break;
+ }
+
+ __GXData->texGenCtrl[dst_coord] = reg;
+ __GXData->dirtyState |= (0x10000 << dst_coord);
+
+ reg = 0;
+ SC_XF_DUALTEX_F_SET_DUALMATRIX_ADRS(1143, reg, pt_texmtx - 64);
+ SC_XF_DUALTEX_F_SET_NORMAL_ENABLE(1144, reg, normalize);
+ __GXData->texGenCtrl2[dst_coord] = reg;
+
+ switch (dst_coord) {
+ case GX_TEXCOORD0: SC_CP_MATIDX_REG_A_SET_TEX0IDX(1158, __GXData->matIdxA, mtx); break;
+ case GX_TEXCOORD1: SC_CP_MATIDX_REG_A_SET_TEX1IDX(1159, __GXData->matIdxA, mtx); break;
+ case GX_TEXCOORD2: SC_CP_MATIDX_REG_A_SET_TEX2IDX(1160, __GXData->matIdxA, mtx); break;
+ case GX_TEXCOORD3: SC_CP_MATIDX_REG_A_SET_TEX3IDX(1161, __GXData->matIdxA, mtx); break;
+ case GX_TEXCOORD4: SC_CP_MATIDX_REG_B_SET_TEX4IDX(1162, __GXData->matIdxB, mtx); break;
+ case GX_TEXCOORD5: SC_CP_MATIDX_REG_B_SET_TEX5IDX(1163, __GXData->matIdxB, mtx); break;
+ case GX_TEXCOORD6: SC_CP_MATIDX_REG_B_SET_TEX6IDX(1164, __GXData->matIdxB, mtx); break;
+ default: SC_CP_MATIDX_REG_B_SET_TEX7IDX(1165, __GXData->matIdxB, mtx); break;
+ }
+
+ mtxIdAttr = dst_coord + 1;
+ __GXData->dirtyState |= 0x4000000;
+}
+
+void GXSetNumTexGens(u8 nTexGens) {
+ CHECK_GXBEGIN(1183, "GXSetNumTexGens");
+
+ SC_GEN_MODE_SET_NTEX(1185, __GXData->genMode, nTexGens);
+ __GXData->dirtyState |= 0x2000004;
+}
diff --git a/src/revolution/gx/GXBump.c b/src/revolution/gx/GXBump.c
new file mode 100644
index 0000000000..0ce04a08b9
--- /dev/null
+++ b/src/revolution/gx/GXBump.c
@@ -0,0 +1,308 @@
+#include <revolution/gx.h>
+#include <revolution/os.h>
+
+#include "__gx.h"
+
+#if DEBUG
+#define GX_WRITE_SOME_REG5(a, b) \
+do { \
+ GX_WRITE_U8(a); \
+ GX_WRITE_U32(b); \
+ __gxVerif->rasRegs[(b & 0xFF000000) >> 24] = b; \
+} while (0)
+#else
+#define GX_WRITE_SOME_REG5(a, b) \
+do { \
+ GX_WRITE_U8(a); \
+ GX_WRITE_U32(b); \
+} while (0)
+#endif
+
+void GXSetTevIndirect(GXTevStageID tev_stage, GXIndTexStageID ind_stage, GXIndTexFormat format, GXIndTexBiasSel bias_sel, GXIndTexMtxID matrix_sel, GXIndTexWrap wrap_s, GXIndTexWrap wrap_t, GXBool add_prev, GXBool utc_lod, GXIndTexAlphaSel alpha_sel) {
+ u32 reg;
+
+ CHECK_GXBEGIN(150, "GXInitIndTexture");
+ reg = 0;
+ SC_BP_CMD_SET_BT(152, reg, ind_stage);
+ SC_BP_CMD_SET_FMT(153, reg, format);
+ SC_BP_CMD_SET_BIAS(154, reg, bias_sel);
+ SC_BP_CMD_SET_BS(155, reg, alpha_sel);
+ SC_BP_CMD_SET_M(156, reg, matrix_sel);
+ SC_BP_CMD_SET_SW(157, reg, wrap_s);
+ SC_BP_CMD_SET_TW(158, reg, wrap_t);
+ SC_BP_CMD_SET_LB(159, reg, utc_lod);
+ SC_BP_CMD_SET_FB(160, reg, add_prev);
+ SC_BP_CMD_SET_RID(161, reg, (0x10 + tev_stage));
+ GX_WRITE_SOME_REG5(GX_LOAD_BP_REG, reg);
+ __GXData->bpSentNot = 0;
+}
+
+void GXSetIndTexMtx(GXIndTexMtxID mtx_id, const f32 offset[2][3], s8 scale_exp) {
+ s32 mtx[6];
+ u32 reg;
+ u32 id;
+
+ CHECK_GXBEGIN(190, "GXSetIndTexMtx");
+
+ switch (mtx_id) {
+ case GX_ITM_0:
+ case GX_ITM_1:
+ case GX_ITM_2:
+ id = mtx_id - 1;
+ break;
+ case GX_ITM_S0:
+ case GX_ITM_S1:
+ case GX_ITM_S2:
+ id = mtx_id - 5;
+ break;
+ case GX_ITM_T0:
+ case GX_ITM_T1:
+ case GX_ITM_T2:
+ id = mtx_id - 9;
+ break;
+ default:
+ id = 0;
+ break;
+ }
+
+ mtx[0] = (int)(1024.0f * offset[0][0]) & 0x7FF;
+ mtx[1] = (int)(1024.0f * offset[1][0]) & 0x7FF;
+ scale_exp += 17;
+ reg = 0;
+ SC_BP_MTXA_SET_MA(212, reg, mtx[0]);
+ SC_BP_MTXA_SET_MB(213, reg, mtx[1]);
+ SC_BP_MTXA_SET_S(214, reg, scale_exp & 3);
+ SC_BP_MTXA_SET_RID(215, reg, id * 3 + 6);
+ GX_WRITE_SOME_REG5(GX_LOAD_BP_REG, reg);
+
+ mtx[2] = (int)(1024.0f * offset[0][1]) & 0x7FF;
+ mtx[3] = (int)(1024.0f * offset[1][1]) & 0x7FF;
+ reg = 0;
+ SC_BP_MTXB_SET_MC(221, reg, mtx[2]);
+ SC_BP_MTXB_SET_MD(222, reg, mtx[3]);
+ SC_BP_MTXB_SET_S(223, reg, (scale_exp >> 2) & 3);
+ SC_BP_MTXB_SET_RID(224, reg, id * 3 + 7);
+ GX_WRITE_SOME_REG5(GX_LOAD_BP_REG, reg);
+
+ mtx[4] = (int)(1024.0f * offset[0][2]) & 0x7FF;
+ mtx[5] = (int)(1024.0f * offset[1][2]) & 0x7FF;
+ reg = 0;
+ SC_BP_MTXC_SET_ME(230, reg, mtx[4]);
+ SC_BP_MTXC_SET_MF(231, reg, mtx[5]);
+ SC_BP_MTXC_SET_S(232, reg, (scale_exp >> 4) & 3);
+ SC_BP_MTXC_SET_RID(233, reg, id * 3 + 8);
+ GX_WRITE_SOME_REG5(GX_LOAD_BP_REG, reg);
+
+ __GXData->bpSentNot = 0;
+}
+
+void GXSetIndTexCoordScale(GXIndTexStageID ind_state, GXIndTexScale scale_s, GXIndTexScale scale_t) {
+ CHECK_GXBEGIN(253, "GXSetIndTexScale");
+
+ switch (ind_state) {
+ case GX_INDTEXSTAGE0:
+ SET_REG_FIELD(257, __GXData->IndTexScale0, 4, 0, scale_s);
+ SET_REG_FIELD(258, __GXData->IndTexScale0, 4, 4, scale_t);
+ SET_REG_FIELD(258, __GXData->IndTexScale0, 8, 24, 0x25);
+ GX_WRITE_SOME_REG5(GX_LOAD_BP_REG, __GXData->IndTexScale0);
+ break;
+ case GX_INDTEXSTAGE1:
+ SET_REG_FIELD(263, __GXData->IndTexScale0, 4, 8, scale_s);
+ SET_REG_FIELD(264, __GXData->IndTexScale0, 4, 12, scale_t);
+ SET_REG_FIELD(264, __GXData->IndTexScale0, 8, 24, 0x25);
+ GX_WRITE_SOME_REG5(GX_LOAD_BP_REG, __GXData->IndTexScale0);
+ break;
+ case GX_INDTEXSTAGE2:
+ SET_REG_FIELD(269, __GXData->IndTexScale1, 4, 0, scale_s);
+ SET_REG_FIELD(270, __GXData->IndTexScale1, 4, 4, scale_t);
+ SET_REG_FIELD(270, __GXData->IndTexScale1, 8, 24, 0x26);
+ GX_WRITE_SOME_REG5(GX_LOAD_BP_REG, __GXData->IndTexScale1);
+ break;
+ case GX_INDTEXSTAGE3:
+ SET_REG_FIELD(275, __GXData->IndTexScale1, 4, 8, scale_s);
+ SET_REG_FIELD(276, __GXData->IndTexScale1, 4, 12, scale_t);
+ SET_REG_FIELD(276, __GXData->IndTexScale1, 8, 24, 0x26);
+ GX_WRITE_SOME_REG5(GX_LOAD_BP_REG, __GXData->IndTexScale1);
+ break;
+ default:
+ ASSERTMSGLINE(281, 0, "GXSetIndTexCoordScale: Invalid Indirect Stage Id");
+ break;
+ }
+ __GXData->bpSentNot = 0;
+}
+
+void GXSetIndTexOrder(GXIndTexStageID ind_stage, GXTexCoordID tex_coord, GXTexMapID tex_map) {
+ CHECK_GXBEGIN(306, "GXSetIndTexOrder");
+
+ if (tex_map == GX_TEXMAP_NULL) {
+ tex_map = GX_TEXMAP0;
+ }
+
+ if (tex_coord == GX_TEXCOORD_NULL) {
+ tex_coord = GX_TEXCOORD0;
+ }
+
+ ASSERTMSGLINE(318, tex_map < GX_MAX_TEXMAP, "GXSetIndTexOrder: Invalid direct texture Id");
+ ASSERTMSGLINE(319, tex_coord < GX_MAX_TEXCOORD, "GXSetIndTexOrder: Invalid texture coord");
+
+ switch (ind_stage) {
+ case GX_INDTEXSTAGE0:
+ SET_REG_FIELD(323, __GXData->iref, 3, 0, tex_map);
+ SET_REG_FIELD(324, __GXData->iref, 3, 3, tex_coord);
+ break;
+ case GX_INDTEXSTAGE1:
+ SET_REG_FIELD(327, __GXData->iref, 3, 6, tex_map);
+ SET_REG_FIELD(328, __GXData->iref, 3, 9, tex_coord);
+ break;
+ case GX_INDTEXSTAGE2:
+ SET_REG_FIELD(331, __GXData->iref, 3, 12, tex_map);
+ SET_REG_FIELD(332, __GXData->iref, 3, 15, tex_coord);
+ break;
+ case GX_INDTEXSTAGE3:
+ SET_REG_FIELD(335, __GXData->iref, 3, 18, tex_map);
+ SET_REG_FIELD(336, __GXData->iref, 3, 21, tex_coord);
+ break;
+ default:
+ ASSERTMSGLINE(339, 0, "GXSetIndTexOrder: Invalid Indirect Stage Id");
+ break;
+ }
+ GX_WRITE_SOME_REG5(GX_LOAD_BP_REG, __GXData->iref);
+ __GXData->dirtyState |= 3;
+ __GXData->bpSentNot = 0;
+}
+
+void GXSetNumIndStages(u8 nIndStages) {
+ CHECK_GXBEGIN(357, "GXSetNumIndStages");
+ ASSERTMSGLINE(359, nIndStages <= 4, "GXSetNumIndStages: Exceeds max. number of indirect texture stages");
+ SET_REG_FIELD(360, __GXData->genMode, 3, 16, nIndStages);
+ __GXData->dirtyState |= 6;
+}
+
+void GXSetTevDirect(GXTevStageID tev_stage) {
+ CHECK_GXBEGIN(377, "GXSetTevDirect");
+ GXSetTevIndirect(tev_stage, GX_INDTEXSTAGE0, GX_ITF_8, GX_ITB_NONE, GX_ITM_OFF, GX_ITW_OFF, GX_ITW_OFF, GX_FALSE, GX_FALSE, GX_ITBA_OFF);
+}
+
+void GXSetTevIndWarp(GXTevStageID tev_stage, GXIndTexStageID ind_stage, u8 signed_offset, u8 replace_mode, GXIndTexMtxID matrix_sel) {
+ GXIndTexWrap wrap = (replace_mode != 0) ? GX_ITW_0 : GX_ITW_OFF;
+
+ CHECK_GXBEGIN(395, "GXSetTevIndWarp");
+ GXSetTevIndirect(tev_stage, ind_stage, GX_ITF_8, (signed_offset != 0) ? GX_ITB_STU : GX_ITB_NONE, matrix_sel, wrap, wrap, GX_FALSE, GX_FALSE, GX_ITBA_OFF);
+}
+
+void GXSetTevIndTile(GXTevStageID tev_stage, GXIndTexStageID ind_stage, u16 tilesize_s,
+ u16 tilesize_t, u16 tilespacing_s, u16 tilespacing_t, GXIndTexFormat format,
+ GXIndTexMtxID matrix_sel, GXIndTexBiasSel bias_sel, GXIndTexAlphaSel alpha_sel)
+{
+ GXIndTexWrap wrap_s;
+ GXIndTexWrap wrap_t;
+ f32 mtx[2][3];
+
+ CHECK_GXBEGIN(429, "GXSetTevIndTile");
+ ASSERTMSGLINE(430, tev_stage < GX_MAX_TEVSTAGE, "GXSetTevIndTile: Invalid tev stage id");
+ ASSERTMSGLINE(431, ind_stage < GX_MAX_INDTEXSTAGE, "GXSetTevIndTile: Invalid indirect stage id");
+
+ switch (tilesize_s) {
+ case 256:
+ wrap_s = GX_ITW_256;
+ break;
+ case 128:
+ wrap_s = GX_ITW_128;
+ break;
+ case 64:
+ wrap_s = GX_ITW_64;
+ break;
+ case 32:
+ wrap_s = GX_ITW_32;
+ break;
+ case 16:
+ wrap_s = GX_ITW_16;
+ break;
+ default:
+ ASSERTMSGLINE(440, 0, "GXSetTevIndTile: Invalid tilesize for S coordinate");
+ wrap_s = GX_ITW_OFF;
+ break;
+ }
+
+ switch (tilesize_t) {
+ case 256:
+ wrap_t = GX_ITW_256;
+ break;
+ case 128:
+ wrap_t = GX_ITW_128;
+ break;
+ case 64:
+ wrap_t = GX_ITW_64;
+ break;
+ case 32:
+ wrap_t = GX_ITW_32;
+ break;
+ case 16:
+ wrap_t = GX_ITW_16;
+ break;
+ default:
+ ASSERTMSGLINE(452, 0, "GXSetTevIndTile: Invalid tilesize for T coordinate");
+ wrap_t = GX_ITW_OFF;
+ break;
+ }
+
+ mtx[0][0] = tilespacing_s / 1024.0f;
+ mtx[0][1] = mtx[0][2] = 0.0f;
+ mtx[1][1] = tilespacing_t / 1024.0f;
+ mtx[1][0] = mtx[1][2] = 0.0f;
+ GXSetIndTexMtx(matrix_sel, mtx, 10);
+ GXSetTevIndirect(tev_stage, ind_stage, format, bias_sel, matrix_sel, wrap_s, wrap_t, GX_FALSE, GX_TRUE, alpha_sel);
+}
+
+void GXSetTevIndBumpST(GXTevStageID tev_stage, GXIndTexStageID ind_stage, GXIndTexMtxID matrix_sel) {
+ GXIndTexMtxID sm;
+ GXIndTexMtxID tm;
+
+ CHECK_GXBEGIN(492, "GXSetTevIndBumpST");
+
+ switch (matrix_sel) {
+ case GX_ITM_0:
+ sm = GX_ITM_S0;
+ tm = GX_ITM_T0;
+ break;
+ case GX_ITM_1:
+ sm = GX_ITM_S1;
+ tm = GX_ITM_T1;
+ break;
+ case GX_ITM_2:
+ sm = GX_ITM_S2;
+ tm = GX_ITM_T2;
+ break;
+ default:
+ ASSERTMSGLINE(509, 0, "GXSetTevIndBumpST: Invalid matrix selection");
+ break;
+ }
+
+ GXSetTevIndirect(tev_stage, ind_stage, GX_ITF_8, GX_ITB_ST, sm, GX_ITW_0, GX_ITW_0, GX_FALSE, GX_FALSE, GX_ITBA_OFF);
+ GXSetTevIndirect(tev_stage + 1, ind_stage, GX_ITF_8, GX_ITB_ST, tm, GX_ITW_0, GX_ITW_0, GX_TRUE, GX_FALSE, GX_ITBA_OFF);
+ GXSetTevIndirect(tev_stage + 2, ind_stage, GX_ITF_8, GX_ITB_NONE, GX_ITM_OFF, GX_ITW_OFF, GX_ITW_OFF, GX_TRUE, GX_FALSE, GX_ITBA_OFF);
+}
+
+void GXSetTevIndBumpXYZ(GXTevStageID tev_stage, GXIndTexStageID ind_stage, GXIndTexMtxID matrix_sel) {
+ CHECK_GXBEGIN(561, "GXSetTevIndBumpXYZ");
+ GXSetTevIndirect(tev_stage, ind_stage, GX_ITF_8, GX_ITB_STU, matrix_sel, GX_ITW_OFF, GX_ITW_OFF, GX_FALSE, GX_FALSE, GX_ITBA_OFF);
+}
+
+void GXSetTevIndRepeat(GXTevStageID tev_stage) {
+ CHECK_GXBEGIN(590, "GXSetTevIndRepeat");
+ GXSetTevIndirect(tev_stage, GX_INDTEXSTAGE0, GX_ITF_8, GX_ITB_NONE, GX_ITM_OFF, GX_ITW_0, GX_ITW_0, GX_TRUE, GX_FALSE, GX_ITBA_OFF);
+}
+
+void __GXUpdateBPMask(void) {}
+
+void __GXSetIndirectMask(u32 mask) {
+ SET_REG_FIELD(668, __GXData->bpMask, 8, ~0xFF, mask);
+
+ GX_WRITE_SOME_REG5(GX_LOAD_BP_REG, __GXData->bpMask);
+ __GXData->bpSentNot = 0;
+}
+
+void __GXFlushTextureState(void) {
+ GX_WRITE_SOME_REG5(GX_LOAD_BP_REG, __GXData->bpMask);
+ __GXData->bpSentNot = 0;
+}
diff --git a/src/revolution/gx/GXDisplayList.c b/src/revolution/gx/GXDisplayList.c
new file mode 100644
index 0000000000..747f9749bf
--- /dev/null
+++ b/src/revolution/gx/GXDisplayList.c
@@ -0,0 +1,99 @@
+#include <string.h>
+
+#include <revolution/gx.h>
+#include <revolution/os.h>
+
+#include "__gx.h"
+
+static __GXFifoObj DisplayListFifo;
+static volatile __GXFifoObj* OldCPUFifo;
+static GXData __savedGXdata;
+
+void GXBeginDisplayList(void* list, u32 size) {
+ __GXFifoObj* CPUFifo = (__GXFifoObj*)GXGetCPUFifo();
+
+ CHECK_GXBEGIN(137, "GXBeginDisplayList");
+ ASSERTMSGLINE(138, !__GXData->inDispList, "GXBeginDisplayList: display list already in progress");
+ ASSERTMSGLINE(139, (size & 0x1F) == 0, "GXBeginDisplayList: size is not 32 byte aligned");
+ ASSERTMSGLINE(140, ((u32)list & 0x1F) == 0, "GXBeginDisplayList: list is not 32 byte aligned");
+
+ if (__GXData->dirtyState != 0) {
+ __GXSetDirtyState();
+ }
+
+ if (__GXData->dlSaveContext != 0) {
+ memcpy(&__savedGXdata, __GXData, sizeof(__savedGXdata));
+ }
+
+ DisplayListFifo.base = (u8*)list;
+ DisplayListFifo.top = (u8*)list + size - 4;
+ DisplayListFifo.size = size;
+ DisplayListFifo.count = 0;
+ DisplayListFifo.rdPtr = list;
+ DisplayListFifo.wrPtr = list;
+ __GXData->inDispList = 1;
+ GXSaveCPUFifo((GXFifoObj*)CPUFifo);
+ OldCPUFifo = CPUFifo;
+ GXSetCPUFifo((GXFifoObj*)&DisplayListFifo);
+ GXResetWriteGatherPipe();
+}
+
+u32 GXEndDisplayList(void) {
+ u32 ov;
+#if DEBUG
+ u32 reg;
+#endif
+ BOOL enabled;
+ u32 cpenable;
+
+ CHECK_GXBEGIN(195, "GXEndDisplayList");
+ ASSERTMSGLINE(196, __GXData->inDispList == TRUE, "GXEndDisplayList: no display list in progress");
+ GXFlush();
+#if DEBUG
+ reg = GX_GET_PI_REG(5);
+ ov = (reg >> 26) & 1;
+#else
+ ov = (GX_GET_PI_REG(5) >> 26) & 1;
+#endif
+ __GXSaveCPUFifoAux(&DisplayListFifo);
+ ASSERTMSGLINE(213, !ov, "GXEndDisplayList: display list commands overflowed buffer");
+ GXSetCPUFifo((GXFifoObj*)OldCPUFifo);
+
+ if (__GXData->dlSaveContext != 0) {
+ enabled = OSDisableInterrupts();
+ cpenable = __GXData->cpEnable;
+ memcpy(__GXData, &__savedGXdata, sizeof(*__GXData));
+ __GXData->cpEnable = cpenable;
+ OSRestoreInterrupts(enabled);
+ }
+
+ __GXData->inDispList = 0;
+ if (!ov) {
+ return DisplayListFifo.count;
+ }
+
+ return 0;
+}
+
+void GXCallDisplayList(void* list, u32 nbytes) {
+ CHECK_GXBEGIN(272, "GXCallDisplayList");
+ ASSERTMSGLINE(273, !__GXData->inDispList, "GXCallDisplayList: display list already in progress");
+ ASSERTMSGLINE(274, (nbytes & 0x1F) == 0, "GXCallDisplayList: nbytes is not 32 byte aligned");
+ ASSERTMSGLINE(275, ((u32)list & 0x1F) == 0, "GXCallDisplayList: list is not 32 byte aligned");
+
+ if (__GXData->dirtyState != 0) {
+ __GXSetDirtyState();
+ }
+
+#if DEBUG
+ __GXShadowDispList(list, nbytes);
+#endif
+
+ if (*(u32*)&__GXData->vNumNot == 0) { // checks both vNum and bpSent
+ __GXSendFlushPrim();
+ }
+
+ GX_WRITE_U8(GX_CMD_CALL_DL);
+ GX_WRITE_U32(list);
+ GX_WRITE_U32(nbytes);
+}
diff --git a/src/revolution/gx/GXDraw.c b/src/revolution/gx/GXDraw.c
new file mode 100644
index 0000000000..30557b0dc6
--- /dev/null
+++ b/src/revolution/gx/GXDraw.c
@@ -0,0 +1,551 @@
+#include <math.h>
+
+#include <revolution/gx.h>
+#include <revolution/os.h>
+
+#include "__gx.h"
+
+static GXVtxDescList vcd[27];
+static GXVtxAttrFmtList vat[27];
+
+static void GetVertState(void) {
+ GXGetVtxDescv(vcd);
+ GXGetVtxAttrFmtv(GX_VTXFMT3, vat);
+ GXClearVtxDesc();
+ GXSetVtxDesc(GX_VA_POS, GX_DIRECT);
+ GXSetVtxDesc(GX_VA_NRM, GX_DIRECT);
+ GXSetVtxAttrFmt(GX_VTXFMT3, GX_VA_POS, GX_POS_XYZ, GX_F32, 0);
+ GXSetVtxAttrFmt(GX_VTXFMT3, GX_VA_NRM, GX_NRM_XYZ, GX_F32, 0);
+}
+
+static void RestoreVertState(void) {
+ GXSetVtxDescv(vcd);
+ GXSetVtxAttrFmtv(GX_VTXFMT3, vat);
+}
+
+static void vsub(f32 p1[3], f32 p2[3], f32 u[3]) {
+ u32 i;
+
+ for (i = 0; i < 3; i++) {
+ u[i] = p2[i] - p1[i];
+ }
+}
+
+static void vcross(f32 u[3], f32 v[3], f32 n[3]) {
+ f32 n1[3];
+
+ n1[0] = (u[1] * v[2]) - (u[2] * v[1]);
+ n1[1] = (u[2] * v[0]) - (u[0] * v[2]);
+ n1[2] = (u[0] * v[1]) - (u[1] * v[0]);
+ n[0] = n1[0];
+ n[1] = n1[1];
+ n[2] = n1[2];
+}
+
+static void normalize(f32 v[3]) {
+ f32 d = sqrtf((v[0] * v[0]) + (v[1] * v[1]) + (v[2] * v[2]));
+
+ ASSERTMSGLINE(137, d != 0.0f, "normalize: zero length vector");
+ v[0] /= d;
+ v[1] /= d;
+ v[2] /= d;
+}
+
+static void myvertex(f32 v[3], f32 n[3]) {
+ GXPosition3f32(v[0], v[1], v[2]);
+ GXNormal3f32(n[0], n[1], n[2]);
+}
+
+static void DumpTriangle(f32 v0[3], f32 v1[3], f32 v2[3]) {
+ GXBegin(GX_TRIANGLES, GX_VTXFMT3, 3);
+ myvertex(v0, v0);
+ myvertex(v1, v1);
+ myvertex(v2, v2);
+ GXEnd();
+}
+
+static void Subdivide(u8 depth, f32 v0[3], f32 v1[3], f32 v2[3]) {
+ f32 v01[3];
+ f32 v12[3];
+ f32 v20[3];
+ u32 i;
+
+ if (depth == 0) {
+ DumpTriangle(v0, v1, v2);
+ return;
+ }
+
+ for (i = 0; i < 3; i++) {
+ v01[i] = v0[i] + v1[i];
+ v12[i] = v1[i] + v2[i];
+ v20[i] = v2[i] + v0[i];
+ }
+
+ normalize(v01);
+ normalize(v12);
+ normalize(v20);
+ Subdivide(depth - 1, v0, v01, v20);
+ Subdivide(depth - 1, v1, v12, v01);
+ Subdivide(depth - 1, v2, v20, v12);
+ Subdivide(depth - 1, v01, v12, v20);
+}
+
+static void SubDivTriangle(u8 depth, u8 i, f32 (*data)[3], u8 (*ndx)[3]) {
+ f32 *x0 = data[ndx[i][0]];
+ f32 *x1 = data[ndx[i][1]];
+ f32 *x2 = data[ndx[i][2]];
+
+ Subdivide(depth, x0, x1, x2);
+}
+
+void GXDrawCylinder(u8 numEdges) {
+ s32 i;
+ f32 top;
+ f32 bottom;
+ f32 x[100];
+ f32 y[100];
+ f32 angle;
+
+ top = 1.0f;
+ bottom = -top;
+ ASSERTMSGLINE(222, numEdges <= 99, "GXDrawCylinder: too many edges");
+
+ GetVertState();
+
+ for (i = 0; i <= numEdges; i++) {
+ angle = (3.1415927f * (2.0f * i)) / numEdges;
+ x[i] = cosf(angle);
+ y[i] = sinf(angle);
+ }
+
+ GXBegin(GX_TRIANGLESTRIP, GX_VTXFMT3, (numEdges + 1) * 2);
+ for (i = 0; i <= numEdges; i++) {
+ GXPosition3f32(x[i], y[i], bottom);
+ GXNormal3f32(x[i], y[i], 0.0f);
+ GXPosition3f32(x[i], y[i], top);
+ GXNormal3f32(x[i], y[i], 0.0f);
+
+ }
+ GXEnd();
+
+ GXBegin(GX_TRIANGLEFAN, GX_VTXFMT3, numEdges + 2);
+ GXPosition3f32(0.0f, 0.0f, top);
+ GXNormal3f32(0.0f, 0.0f, 1.0f);
+ for (i = 0; i <= numEdges; i++) {
+ GXPosition3f32(x[i], -y[i], top);
+ GXNormal3f32(0.0f, 0.0f, 1.0f);
+
+ }
+ GXEnd();
+
+ GXBegin(GX_TRIANGLEFAN, GX_VTXFMT3, numEdges + 2);
+ GXPosition3f32(0.0f, 0.0f, bottom);
+ GXNormal3f32(0.0f, 0.0f, -1.0f);
+ for (i = 0; i <= numEdges; i++) {
+ GXPosition3f32(x[i], y[i], bottom);
+ GXNormal3f32(0.0f, 0.0f, -1.0f);
+ }
+ GXEnd();
+
+ RestoreVertState();
+}
+
+void GXDrawTorus(f32 rc, u8 numc, u8 numt) {
+ GXAttrType ttype;
+ s32 i, j, k;
+ f32 s, t;
+ f32 x, y, z;
+ f32 twopi = 6.2831855f;
+ f32 rt;
+
+ ASSERTMSGLINE(316, rc < 1.0f, "GXDrawTorus: doughnut too fat");
+
+ rt = 1.0f - rc;
+ GXGetVtxDesc(GX_VA_TEX0, &ttype);
+ GetVertState();
+
+ if (ttype != GX_NONE) {
+ GXSetVtxDesc(GX_VA_TEX0, GX_DIRECT);
+ GXSetVtxAttrFmt(GX_VTXFMT3, GX_VA_TEX0, GX_TEX_ST, GX_F32, 0);
+ }
+
+ for (i = 0; i < numc; i++) {
+ GXBegin(GX_TRIANGLESTRIP, GX_VTXFMT3, (numt + 1) * 2);
+ for (j = 0; j <= numt; j++) {
+ for (k = 1; k >= 0; k--) {
+ s = (i + k) % numc;
+ t = j % numt;
+ x = (rt - rc * cosf(s * twopi / numc)) * cosf(t * twopi / numt);
+ y = (rt - rc * cosf(s * twopi / numc)) * sinf(t * twopi / numt);
+ z = rc * sinf(s * twopi / numc);
+ GXPosition3f32(x, y, z);
+ x = -cosf(t * twopi / numt) * cosf(s * twopi / numc);
+ y = -sinf(t * twopi / numt) * cosf(s * twopi / numc);
+ z = sinf(s * twopi / numc);
+ GXNormal3f32(x, y, z);
+ if (ttype != GX_NONE) {
+ GXTexCoord2f32((i + k) / (f32)numc, j / (f32)numt);
+ }
+ }
+ }
+ GXEnd();
+ }
+ RestoreVertState();
+}
+
+void GXDrawSphere(u8 numMajor, u8 numMinor) {
+ GXAttrType ttype;
+ f32 radius = 1.0f;
+ f32 majorStep = 3.1415927f / numMajor;
+ f32 minorStep = 6.2831855f / numMinor;
+ s32 i, j;
+ f32 a, b;
+ f32 r0, r1;
+ f32 z0, z1;
+ f32 c;
+
+ GXGetVtxDesc(GX_VA_TEX0, &ttype);
+ GetVertState();
+
+ if (ttype != GX_NONE) {
+ GXSetVtxDesc(GX_VA_TEX0, GX_DIRECT);
+ GXSetVtxAttrFmt(GX_VTXFMT3, GX_VA_TEX0, GX_TEX_ST, GX_RGBA6, 0);
+ }
+
+ for (i = 0; i < numMajor; i++) {
+ a = i * majorStep;
+ b = a + majorStep;
+ r0 = radius * sinf(a);
+ r1 = radius * sinf(b);
+ z0 = radius * cosf(a);
+ z1 = radius * cosf(b);
+ GXBegin(GX_TRIANGLESTRIP, GX_VTXFMT3, (numMinor + 1) * 2);
+ for (j = 0; j <= numMinor; j++) {
+ f32 x, y;
+ c = j * minorStep;
+ x = cosf(c);
+ y = sinf(c);
+ GXPosition3f32(x * r1, y * r1, z1);
+ GXNormal3f32((x * r1) / radius, (y * r1) / radius, z1 / radius);
+ if (ttype != GX_NONE) {
+ GXTexCoord2f32((f32)j / (f32)numMinor, (f32)(i + 1) / (f32)numMajor);
+ }
+ GXPosition3f32(x * r0, y * r0, z0);
+ GXNormal3f32((x * r0) / radius, (y * r0) / radius, z0 / radius);
+ if (ttype != GX_NONE) {
+ GXTexCoord2f32((f32)j / (f32)numMinor, (f32)i / (f32)numMajor);
+ }
+ }
+ GXEnd();
+ }
+ RestoreVertState();
+}
+
+static void GXDrawCubeFace(f32 nx, f32 ny, f32 nz, f32 tx, f32 ty, f32 tz, f32 bx, f32 by, f32 bz, GXAttrType binormal, GXAttrType texture) {
+ GXPosition3f32(0.57735026f * (nx + tx + bx), 0.57735026f * (ny + ty + by), 0.57735026f * (nz + tz + bz));
+ GXNormal3f32(nx, ny, nz);
+
+ if (binormal != GX_NONE) {
+ GXNormal3f32(tx, ty, tz);
+ GXNormal3f32(bx, by, bz);
+ }
+
+ if (texture != GX_NONE) {
+ GXTexCoord2s8(1, 1);
+ }
+
+ GXPosition3f32(0.57735026f * (nx - tx + bx), 0.57735026f * (ny - ty + by), 0.57735026f * (nz - tz + bz));
+ GXNormal3f32(nx, ny, nz);
+
+ if (binormal != GX_NONE) {
+ GXNormal3f32(tx, ty, tz);
+ GXNormal3f32(bx, by, bz);
+ }
+
+ if (texture != GX_NONE) {
+ GXTexCoord2s8(0, 1);
+ }
+
+ GXPosition3f32(0.57735026f * (nx - tx - bx), 0.57735026f * (ny - ty - by), 0.57735026f * (nz - tz - bz));
+ GXNormal3f32(nx, ny, nz);
+
+ if (binormal != GX_NONE) {
+ GXNormal3f32(tx, ty, tz);
+ GXNormal3f32(bx, by, bz);
+ }
+
+ if (texture != GX_NONE) {
+ GXTexCoord2s8(0, 0);
+ }
+
+ GXPosition3f32(0.57735026f * (nx + tx - bx), 0.57735026f * (ny + ty - by), 0.57735026f * (nz + tz - bz));
+ GXNormal3f32(nx, ny, nz);
+
+ if (binormal != GX_NONE) {
+ GXNormal3f32(tx, ty, tz);
+ GXNormal3f32(bx, by, bz);
+ }
+
+ if (texture != GX_NONE) {
+ GXTexCoord2s8(1, 0);
+ }
+}
+
+void GXDrawCube(void) {
+ GXAttrType ntype;
+ GXAttrType ttype;
+
+ GXGetVtxDesc(GX_VA_NBT, &ntype);
+ GXGetVtxDesc(GX_VA_TEX0, &ttype);
+ GetVertState();
+ if (ntype != GX_NONE) {
+ GXSetVtxDesc(GX_VA_NBT, GX_DIRECT);
+ GXSetVtxAttrFmt(GX_VTXFMT3, GX_VA_NBT, GX_TEX_ST, GX_RGBA6, 0);
+ }
+ if (ttype != GX_NONE) {
+ GXSetVtxDesc(GX_VA_TEX0, GX_DIRECT);
+ GXSetVtxAttrFmt(GX_VTXFMT3, GX_VA_TEX0, GX_TEX_ST, GX_RGB8, 0);
+ }
+
+ GXBegin(GX_QUADS, GX_VTXFMT3, 24);
+ GXDrawCubeFace(-1.0f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, ntype, ttype);
+ GXDrawCubeFace(1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, -1.0f, ntype, ttype);
+ GXDrawCubeFace(0.0f, -1.0f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, ntype, ttype);
+ GXDrawCubeFace(0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, -1.0f, 0.0f, 0.0f, ntype, ttype);
+ GXDrawCubeFace(0.0f, 0.0f, -1.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.0f, ntype, ttype);
+ GXDrawCubeFace(0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, ntype, ttype);
+ GXEnd();
+
+ RestoreVertState();
+}
+
+static u32 polygons[12][5] = {
+ { 0, 12, 10, 11, 16 },
+ { 1, 17, 8, 9, 13 },
+ { 2, 14, 9, 8, 18 },
+ { 3, 19, 11, 10, 15 },
+ { 4, 14, 2, 3, 15 },
+ { 5, 12, 0, 1, 13 },
+ { 6, 17, 1, 0, 16 },
+ { 7, 19, 3, 2, 18 },
+ { 8, 17, 6, 7, 18 },
+ { 9, 14, 4, 5, 13 },
+ { 10, 12, 5, 4, 15 },
+ { 11, 19, 7, 6, 16 },
+};
+
+static f32 verts[20][3] = {
+ { -0.809015f, 0.0f, 0.309015f },
+ { -0.809015f, 0.0f, -0.309015f },
+ { 0.809015f, 0.0f, -0.309015f },
+ { 0.809015f, 0.0f, 0.309015f },
+ { 0.309015f, -0.809015f, 0.0f },
+ { -0.309015f, -0.809015f, 0.0f },
+ { -0.309015f, 0.809015f, 0 },
+ { 0.309015f, 0.809015f, 0 },
+ { 0.0f, 0.309015f, -0.809015f },
+ { 0.0f, -0.309015f, -0.809015f },
+ { 0.0f, -0.309015f, 0.809015f },
+ { 0.0f, 0.309015f, 0.809015f },
+ { -0.5f, -0.5f, 0.5 },
+ { -0.5f, -0.5f, -0.5 },
+ { 0.5f, -0.5f, -0.5 },
+ { 0.5f, -0.5f, 0.5 },
+ { -0.5f, 0.5f, 0.5 },
+ { -0.5f, 0.5f, -0.5 },
+ { 0.5f, 0.5f, -0.5 },
+ { 0.5f, 0.5f, 0.5 },
+};
+
+void GXDrawDodeca(void) {
+ u32 i;
+ f32 *p0;
+ f32 *p1;
+ f32 *p2;
+ f32 u[3];
+ f32 v[3];
+ f32 n[3];
+
+ GetVertState();
+ for (i = 0; i < 12; i++) {
+ p0 = verts[polygons[i][0]];
+ p1 = verts[polygons[i][1]];
+ p2 = verts[polygons[i][2]];
+ vsub(p1, p2, u);
+ vsub(p1, p0, v);
+ vcross(u, v, n);
+ normalize(n);
+ GXBegin(GX_TRIANGLEFAN, GX_VTXFMT3, 5);
+ myvertex(verts[polygons[i][4]], n);
+ myvertex(verts[polygons[i][3]], n);
+ myvertex(p2, n);
+ myvertex(p1, n);
+ myvertex(p0, n);
+ GXEnd();
+ }
+ RestoreVertState();
+}
+
+static f32 odata[6][3] = {
+ { 1.0f, 0.0f, 0.0f },
+ { -1.0f, 0.0f, 0.0f },
+ { 0.0f, 1.0f, 0.0f },
+ { 0.0f, -1.0f, 0.0f },
+ { 0.0f, 0.0f, 1.0f },
+ { 0.0f, 0.0f, -1.0f },
+};
+
+static u8 ondex[8][3] = {
+ { 0, 4, 2 },
+ { 1, 2, 4 },
+ { 0, 3, 4 },
+ { 1, 4, 3 },
+ { 0, 2, 5 },
+ { 1, 5, 2 },
+ { 0, 5, 3 },
+ { 1, 3, 5 },
+};
+
+void GXDrawOctahedron(void) {
+ s32 i;
+
+ GetVertState();
+ for (i = 7; i >= 0; i--) {
+ SubDivTriangle(0, i, odata, ondex);
+ }
+ RestoreVertState();
+}
+
+static f32 idata[12][3] = {
+ { -0.5257311f, 0.0f, 0.8506508f },
+ { 0.5257311f, 0.0f, 0.8506508f },
+ { -0.5257311f, 0.0f, -0.8506508f },
+ { 0.5257311f, 0.0f, -0.8506508f },
+ { 0.0f, 0.8506508f, 0.5257311f },
+ { 0.0f, 0.8506508f, -0.5257311f },
+ { 0.0f, -0.8506508f, 0.5257311f },
+ { 0.0f, -0.8506508f, -0.5257311f },
+ { 0.8506508f, 0.5257311f, 0.0f },
+ { -0.8506508f, 0.5257311f, 0.0f },
+ { 0.8506508f, -0.5257311f, 0.0f },
+ { -0.8506508f, -0.5257311f, 0.0f },
+};
+
+static u8 index[20][3] = {
+ { 0, 4, 1 },
+ { 0, 9, 4 },
+ { 9, 5, 4 },
+ { 4, 5, 8 },
+ { 4, 8, 1 },
+ { 8, 10, 1 },
+ { 8, 3, 10 },
+ { 5, 3, 8 },
+ { 5, 2, 3 },
+ { 2, 7, 3 },
+ { 7, 10, 3 },
+ { 7, 6, 10 },
+ { 7, 11, 6 },
+ { 11, 0, 6 },
+ { 0, 1, 6 },
+ { 6, 1, 10 },
+ { 9, 0, 11 },
+ { 9, 11, 2 },
+ { 9, 2, 5 },
+ { 7, 2, 11 },
+};
+
+void GXDrawIcosahedron(void) {
+ s32 i;
+
+ GetVertState();
+ for (i = 19; i >= 0; i--) {
+ SubDivTriangle(0, i, idata, index);
+ }
+ RestoreVertState();
+}
+
+void GXDrawSphere1(u8 depth) {
+ s32 i;
+
+ GetVertState();
+ for (i = 19; i >= 0; i--) {
+ SubDivTriangle(depth, i, idata, index);
+ }
+ RestoreVertState();
+}
+
+static u32 CmpNormal32(f32 n1[3], f32 n2[3]) {
+ u32 i;
+
+ for (i = 0; i < 3; i++) {
+ if (n1[i] != n2[i])
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static u32 nrm_cnt;
+static f32* nrm_tab;
+
+static void AddNormal(f32 n[3]) {
+ u32 indx;
+ u32 i;
+
+ for (i = 0; i < nrm_cnt; i++) {
+ if (CmpNormal32(n, &nrm_tab[i * 3]))
+ return;
+ }
+ indx = nrm_cnt * 3;
+ nrm_tab[indx + 0] = n[0];
+ nrm_tab[indx + 1] = n[1];
+ nrm_tab[indx + 2] = n[2];
+ nrm_cnt++;
+}
+
+static void SubdivideNrm(u8 depth, f32 v0[3], f32 v1[3], f32 v2[3]) {
+ f32 v01[3];
+ f32 v12[3];
+ f32 v20[3];
+ u32 i;
+
+ if (depth == 0) {
+ AddNormal(v0);
+ AddNormal(v1);
+ AddNormal(v2);
+ return;
+ }
+
+ for (i = 0; i < 3; i++) {
+ v01[i] = v0[i] + v1[i];
+ v12[i] = v1[i] + v2[i];
+ v20[i] = v2[i] + v0[i];
+ }
+
+ normalize(v01);
+ normalize(v12);
+ normalize(v20);
+ SubdivideNrm(depth - 1, v0, v01, v20);
+ SubdivideNrm(depth - 1, v1, v12, v01);
+ SubdivideNrm(depth - 1, v2, v20, v12);
+ SubdivideNrm(depth - 1, v01, v12, v20);
+}
+
+static void SubDivNrm(u8 depth, u8 i, f32 (*data)[3], u8 (*ndx)[3]) {
+ f32* x0 = data[ndx[i][0]];
+ f32* x1 = data[ndx[i][1]];
+ f32* x2 = data[ndx[i][2]];
+
+ SubdivideNrm(depth, x0, x1, x2);
+}
+
+u32 GXGenNormalTable(u8 depth, f32* table) {
+ s32 i;
+
+ nrm_cnt = 0;
+ nrm_tab = table;
+ for (i = 7; i >= 0; i--) {
+ SubDivNrm(depth, i, odata, ondex);
+ }
+
+ return nrm_cnt;
+}
diff --git a/src/revolution/gx/GXFifo.c b/src/revolution/gx/GXFifo.c
new file mode 100644
index 0000000000..b5e3784085
--- /dev/null
+++ b/src/revolution/gx/GXFifo.c
@@ -0,0 +1,614 @@
+#include <revolution/base/PPCArch.h>
+#include <revolution/gx.h>
+#include <revolution/os.h>
+
+#include "__gx.h"
+
+#define TOPHYSICAL(a) (((u32)a) & 0x3FFFFFFF)
+
+static __GXFifoObj CPUFifo;
+static __GXFifoObj GPFifo;
+static GXBool CPUFifoReady = FALSE;
+static GXBool GPFifoReady = FALSE;
+
+static GXBool CPGPLinked;
+static OSThread* __GXCurrentThread;
+static BOOL GXOverflowSuspendInProgress;
+static GXBreakPtCallback BreakPointCB;
+void* __GXCurrentBP;
+static u32 __GXOverflowCount;
+
+#if DEBUG
+static BOOL IsWGPipeRedirected;
+#endif
+
+static void __GXFifoReadEnable(void);
+static void __GXFifoReadDisable(void);
+static void __GXFifoLink(u8 en);
+static void __GXWriteFifoIntEnable(u8 hiWatermarkEn, u8 loWatermarkEn);
+static void __GXWriteFifoIntReset(u8 hiWatermarkClr, u8 loWatermarkClr);
+GXBool CPGPLinkCheck(void);
+
+static void GXOverflowHandler(__OSInterrupt interrupt, OSContext* context) {
+#if DEBUG
+ if (__gxVerif->verifyLevel > GX_WARN_SEVERE) {
+ OSReport("[GXOverflowHandler]");
+ }
+#endif
+ ASSERTLINE(417, !GXOverflowSuspendInProgress);
+
+ __GXOverflowCount++;
+ __GXWriteFifoIntEnable(0, 1);
+ __GXWriteFifoIntReset(1, 0);
+ GXOverflowSuspendInProgress = TRUE;
+
+#if DEBUG
+ if (__gxVerif->verifyLevel > GX_WARN_SEVERE) {
+ OSReport("[GXOverflowHandler Sleeping]");
+ }
+#endif
+ OSSuspendThread(__GXCurrentThread);
+}
+
+static void GXUnderflowHandler(s16 interrupt, OSContext* context) {
+#if DEBUG
+ if (__gxVerif->verifyLevel > GX_WARN_SEVERE) {
+ OSReport("[GXUnderflowHandler]");
+ }
+#endif
+ ASSERTLINE(459, GXOverflowSuspendInProgress);
+
+ OSResumeThread(__GXCurrentThread);
+ GXOverflowSuspendInProgress = FALSE;
+ __GXWriteFifoIntReset(1, 1);
+ __GXWriteFifoIntEnable(1, 0);
+}
+
+static void GXBreakPointHandler(__OSInterrupt interrupt, OSContext* context) {
+ OSContext exceptionContext;
+
+ SC_CP_REG_ENABLE_SET_FIFOBRKINT(0, __GXData->cpEnable, 0);
+ GX_CP_REG_WRITE_U16(CP_ENABLE, __GXData->cpEnable);
+ if (BreakPointCB != 0) {
+ OSClearContext(&exceptionContext);
+ OSSetCurrentContext(&exceptionContext);
+ BreakPointCB();
+ OSClearContext(&exceptionContext);
+ OSSetCurrentContext(context);
+ }
+}
+
+static void GXCPInterruptHandler(__OSInterrupt interrupt, OSContext* context) {
+ __GXData->cpStatus = GX_GET_CP_REG(0);
+ if (CP_REG_ENABLE_GET_UNFLINT(__GXData->cpEnable) && CP_REG_STATUS_GET_UNFL(__GXData->cpStatus)) {
+ GXUnderflowHandler(interrupt, context);
+ }
+
+ if (CP_REG_ENABLE_GET_OVFLINT(__GXData->cpEnable) && CP_REG_STATUS_GET_OVFL(__GXData->cpStatus)) {
+ GXOverflowHandler(interrupt, context);
+ }
+
+ if (CP_REG_ENABLE_GET_FIFOBRKINT(__GXData->cpEnable) && CP_REG_STATUS_GET_FIFOBRK(__GXData->cpStatus)) {
+ GXBreakPointHandler(interrupt, context);
+ }
+}
+
+void GXInitFifoBase(GXFifoObj* fifo, void* base, u32 size) {
+ __GXFifoObj* realFifo = (__GXFifoObj*)fifo;
+
+ ASSERTMSGLINE(582, ((u32)base & 0x1F) == 0, "GXInitFifoBase: base must be 32B aligned");
+ ASSERTMSGLINE(584, base != 0, "GXInitFifoBase: base pointer is NULL");
+ ASSERTMSGLINE(586, (size & 0x1F) == 0, "GXInitFifoBase: size must be 32B aligned");
+ ASSERTMSGLINE(588, size >= 0x10000, "GXInitFifoBase: fifo is not large enough");
+
+ realFifo->base = base;
+ realFifo->top = (u8*)base + size - 4;
+ realFifo->size = size;
+ realFifo->count = 0;
+ GXInitFifoLimits(fifo, size - 0x4000, (size >> 1) & ~0x1F);
+ GXInitFifoPtrs(fifo, base, base);
+}
+
+void GXInitFifoPtrs(GXFifoObj* fifo, void* readPtr, void* writePtr) {
+ __GXFifoObj* realFifo = (__GXFifoObj *)fifo;
+ BOOL enabled;
+
+ ASSERTMSGLINE(628, ((u32)readPtr & 0x1F) == 0, "GXInitFifoPtrs: readPtr not 32B aligned");
+ ASSERTMSGLINE(630, ((u32)writePtr & 0x1F) == 0, "GXInitFifoPtrs: writePtr not 32B aligned");
+ ASSERTMSGLINE(633, realFifo->base <= readPtr && readPtr < realFifo->top, "GXInitFifoPtrs: readPtr not in fifo range");
+ ASSERTMSGLINE(636, realFifo->base <= writePtr && writePtr < realFifo->top, "GXInitFifoPtrs: writePtr not in fifo range");
+
+ enabled = OSDisableInterrupts();
+ realFifo->rdPtr = readPtr;
+ realFifo->wrPtr = writePtr;
+ realFifo->count = (u8*)writePtr - (u8*)readPtr;
+ if (realFifo->count < 0) {
+ realFifo->count += realFifo->size;
+ }
+ OSRestoreInterrupts(enabled);
+}
+
+void GXInitFifoLimits(GXFifoObj* fifo, u32 hiWatermark, u32 loWatermark) {
+ __GXFifoObj* realFifo = (__GXFifoObj*)fifo;
+
+ ASSERTMSGLINE(674, (hiWatermark & 0x1F) == 0, "GXInitFifoLimits: hiWatermark not 32B aligned");
+ ASSERTMSGLINE(676, (loWatermark & 0x1F) == 0, "GXInitFifoLimits: loWatermark not 32B aligned");
+ ASSERTMSGLINE(678, hiWatermark < realFifo->top - realFifo->base, "GXInitFifoLimits: hiWatermark too large");
+ ASSERTMSGLINE(680, loWatermark < hiWatermark, "GXInitFifoLimits: hiWatermark below lo watermark");
+
+ realFifo->hiWatermark = hiWatermark;
+ realFifo->loWatermark = loWatermark;
+}
+
+GXBool CPGPLinkCheck(void) {
+ u32 check = 0;
+ s32 range1, range2, overlap;
+
+ if (!CPUFifoReady || !GPFifoReady) {
+ return GX_FALSE;
+ }
+
+ if (CPUFifo.base == GPFifo.base) {
+ ++check;
+ }
+
+ if (CPUFifo.top == GPFifo.top) {
+ ++check;
+ }
+
+ if (check == 2) {
+ if (CPUFifo.hiWatermark == GPFifo.hiWatermark) {
+ ++check;
+ }
+
+ if (CPUFifo.loWatermark == GPFifo.loWatermark) {
+ ++check;
+ }
+
+ ASSERTMSGLINE(722, check == 4, "GXSetGPFifo/GXSetCPUFifo: mismatch of watermark parameter");
+ return GX_TRUE;
+ }
+
+ range1 = (s32)((u8*)CPUFifo.top - (u8*)GPFifo.base);
+ range2 = (s32)((u8*)GPFifo.top - (u8*)CPUFifo.base);
+ overlap = ((range1 > 0) && (range2 > 0) || (range1 < 0 && (range2 < 0)));
+
+ if (overlap) {
+ OSReport("CPUFifo: %08X - %08X\n", (u32)CPUFifo.base, (u32)CPUFifo.top);
+ OSReport("GP Fifo: %08X - %08X\n", (u32)GPFifo.base, (u32)GPFifo.top);
+ }
+
+ ASSERTMSGLINE(736, !overlap, "GXSetGPFifo/GXSetCPUFifo: invalid overlap of both buffers");
+ return GX_FALSE;
+}
+
+// NONMATCHING DEBUG
+void GXSetCPUFifo(GXFifoObj* fifo) {
+ __GXFifoObj* realFifo = (__GXFifoObj*)fifo;
+ BOOL enabled = OSDisableInterrupts();
+ u32 reg;
+
+ if (fifo == NULL) {
+ CPUFifoReady = GX_FALSE;
+ CPGPLinked = GX_FALSE;
+ CPUFifo.bind_cpu = CPUFifo.bind_gp = GX_FALSE;
+ OSRestoreInterrupts(enabled);
+ return;
+ }
+
+ CPUFifo = *realFifo;
+ CPUFifoReady = GX_TRUE;
+ CPUFifo.bind_cpu = GX_TRUE;
+
+ if (CPGPLinkCheck()) {
+ CPGPLinked = GX_TRUE;
+ CPUFifo.bind_gp = GX_TRUE;
+
+ reg = 0;
+
+ GX_PI_REG_WRITE_U32(PI_REG_CPBAS, TOPHYSICAL(CPUFifo.base));
+ GX_PI_REG_WRITE_U32(PI_REG_CPTOP, TOPHYSICAL(CPUFifo.top));
+
+ SC_PI_CPWRT_REG_SET_CPWRT(787, reg, (GX_PHY_ADDR(TOPHYSICAL(CPUFifo.wrPtr)) >> 5));
+ SC_PI_CPWRT_REG_SET_CPWRAP(0, reg, 0);
+ GX_PI_REG_WRITE_U32(PI_REG_CPWRT, reg);
+
+ __GXWriteFifoIntReset(1, 1);
+ __GXWriteFifoIntEnable(1, 0);
+ __GXFifoLink(1);
+ } else {
+ CPUFifo.bind_gp = GX_FALSE;
+
+ if (CPGPLinked) {
+ __GXFifoLink(0);
+ CPGPLinked = GX_FALSE;
+ }
+
+ __GXWriteFifoIntEnable(0, 0);
+ reg = 0;
+ GX_PI_REG_WRITE_U32(PI_REG_CPBAS, TOPHYSICAL(CPUFifo.base));
+ GX_PI_REG_WRITE_U32(PI_REG_CPTOP, TOPHYSICAL(CPUFifo.top));
+ SC_PI_CPWRT_REG_SET_CPWRT(819, reg, (GX_PHY_ADDR(TOPHYSICAL(CPUFifo.wrPtr)) >> 5));
+ SC_PI_CPWRT_REG_SET_CPWRAP(0, reg, 0);
+ GX_PI_REG_WRITE_U32(PI_REG_CPWRT, reg);
+ }
+
+ PPCSync();
+ OSRestoreInterrupts(enabled);
+}
+
+void GXSetGPFifo(GXFifoObj* fifo) {
+ __GXFifoObj* realFifo = (__GXFifoObj*)fifo;
+ BOOL enabled;
+ u32 stbtmp;
+
+ ASSERTMSGLINE(850, ((u32)realFifo->base & 0x30000000) == 0, "GXSetGPFifo: GX Fifo should be located on MEM1 memory region");
+
+ enabled = OSDisableInterrupts();
+
+ __GXFifoReadDisable();
+ __GXWriteFifoIntEnable(0, 0);
+
+ if (fifo == NULL) {
+ GPFifoReady = GX_FALSE;
+ CPGPLinked = GX_FALSE;
+ __GXFifoLink(GX_FALSE);
+ GPFifo.bind_gp = GPFifo.bind_cpu = GX_FALSE;
+ OSRestoreInterrupts(enabled);
+ return;
+ }
+
+ GPFifo = *realFifo;
+ GPFifoReady = GX_TRUE;
+ GPFifo.bind_gp = GX_TRUE;
+ GX_CP_REG_WRITE_U16(CP_FIFO_BASEL, TOPHYSICAL(GPFifo.base));
+ GX_CP_REG_WRITE_U16(CP_FIFO_TOPL, TOPHYSICAL(GPFifo.top));
+ GX_CP_REG_WRITE_U16(CP_FIFO_COUNTL, GPFifo.count);
+ GX_CP_REG_WRITE_U16(CP_FIFO_WPTRL, TOPHYSICAL(GPFifo.wrPtr));
+ GX_CP_REG_WRITE_U16(CP_FIFO_RPTRL, TOPHYSICAL(GPFifo.rdPtr));
+ GX_CP_REG_WRITE_U16(CP_FIFO_HICNTL, GPFifo.hiWatermark);
+ GX_CP_REG_WRITE_U16(CP_FIFO_LOCNTL, GPFifo.loWatermark);
+ GX_CP_REG_WRITE_U16(CP_FIFO_BASEH, TOPHYSICAL(GPFifo.base) >> 16);
+ GX_CP_REG_WRITE_U16(CP_FIFO_TOPH, TOPHYSICAL(GPFifo.top) >> 16);
+ GX_CP_REG_WRITE_U16(CP_FIFO_COUNTH, GPFifo.count >> 16);
+ GX_CP_REG_WRITE_U16(CP_FIFO_WPTRH, TOPHYSICAL(GPFifo.wrPtr) >> 16);
+ GX_CP_REG_WRITE_U16(CP_FIFO_RPTRH, TOPHYSICAL(GPFifo.rdPtr) >> 16);
+ GX_CP_REG_WRITE_U16(CP_FIFO_HICNTH, GPFifo.hiWatermark >> 16);
+ GX_CP_REG_WRITE_U16(CP_FIFO_LOCNTH, GPFifo.loWatermark >> 16);
+
+ PPCSync();
+
+ if (CPGPLinkCheck()) {
+ CPGPLinked = GX_TRUE;
+ GPFifo.bind_cpu = GX_TRUE;
+ __GXWriteFifoIntEnable(GX_TRUE, GX_FALSE);
+ __GXFifoLink(GX_TRUE);
+ } else {
+ CPGPLinked = GX_FALSE;
+ GPFifo.bind_cpu = GX_FALSE;
+ __GXWriteFifoIntEnable(GX_FALSE, GX_FALSE);
+ __GXFifoLink(GX_FALSE);
+ }
+
+ stbtmp = __GXData->cpEnable;
+ SC_CP_REG_ENABLE_SET_FIFOBRK(0, stbtmp, 0);
+ SC_CP_REG_ENABLE_SET_FIFOBRKINT(0, stbtmp, 0);
+ GX_CP_REG_WRITE_U16(CP_ENABLE, stbtmp);
+ GX_CP_REG_WRITE_U16(CP_ENABLE, __GXData->cpEnable);
+ __GXWriteFifoIntReset(GX_TRUE, GX_TRUE);
+ __GXFifoReadEnable();
+ OSRestoreInterrupts(enabled);
+}
+
+#define CP_READ_2U16(dest,reg,type) \
+{\
+ u32 temp; \
+ temp = (u32)GX_CP_REG_READ_U16(reg##H)<<16; \
+ temp |= (u32)GX_CP_REG_READ_U16(reg##L); \
+ dest = (type)OSPhysicalToCached(temp); \
+}
+
+#define CP_READ_2U16_INT(dest,reg,type) \
+{\
+ u32 temp; \
+ temp = (u32)GX_CP_REG_READ_U16(reg##H)<<16; \
+ temp |= (u32)GX_CP_REG_READ_U16(reg##L); \
+ dest = (type)temp; \
+}
+
+static void __GXSaveFifoCPStat(void) {
+ CP_READ_2U16(GPFifo.rdPtr, CP_FIFO_RPTR, void*);
+ CP_READ_2U16_INT(GPFifo.count, CP_FIFO_COUNT, s32);
+}
+
+static void __GXSaveFifoPIStat(void) {
+ u32 reg = GX_PI_REG_READ_U32(PI_REG_CPWRT);
+ CPUFifo.wrPtr = OSPhysicalToCached(reg & PI_CPWRT_REG_CPWRT_MASK);
+ CPUFifo.wrap = (GXBool)((reg & PI_CPWRT_REG_CPWRAP_MASK) ? GX_TRUE : GX_FALSE);
+}
+
+void __GXSaveFifo(void) {
+ BOOL enabled = OSDisableInterrupts();
+
+ if (CPUFifoReady) {
+ __GXSaveFifoPIStat();
+ }
+
+ if (GPFifoReady) {
+ __GXSaveFifoCPStat();
+ }
+
+ if (CPGPLinked) {
+ CPUFifo.rdPtr = GPFifo.rdPtr;
+ CPUFifo.count = GPFifo.count;
+ GPFifo.wrPtr = CPUFifo.wrPtr;
+ GPFifo.wrap = CPUFifo.wrap;
+ } else if (CPUFifoReady) {
+ CPUFifo.count = (s32)CPUFifo.wrPtr - (s32)CPUFifo.rdPtr;
+ if (CPUFifo.count < 0) {
+ CPUFifo.count += CPUFifo.size;
+ }
+ }
+
+ OSRestoreInterrupts(enabled);
+}
+
+GXBool __GXIsGPFifoReady(void) {
+ return GPFifoReady;
+}
+
+void GXGetGPStatus(GXBool* overhi, GXBool* underlow, GXBool* readIdle, GXBool* cmdIdle, GXBool* brkpt) {
+ __GXData->cpStatus = GX_CP_REG_READ_U16(CP_STATUS);
+ *overhi = (u8)CP_REG_STATUS_GET_OVFL(__GXData->cpStatus);
+ *underlow = (u8)CP_REG_STATUS_GET_UNFL(__GXData->cpStatus);
+ *readIdle = (u8)CP_REG_STATUS_GET_FIFO_RDIDLE(__GXData->cpStatus);
+ *cmdIdle = (u8)CP_REG_STATUS_GET_CPIDLE(__GXData->cpStatus);
+ *brkpt = (u8)CP_REG_STATUS_GET_FIFOBRK(__GXData->cpStatus);
+}
+
+void GXGetFifoPtrs(GXFifoObj* fifo, void** readPtr, void** writePtr) {
+ __GXFifoObj* realFifo = (__GXFifoObj*)fifo;
+ *readPtr = realFifo->rdPtr;
+ *writePtr = realFifo->wrPtr;
+}
+
+void* GXGetFifoBase(const GXFifoObj* fifo) {
+ __GXFifoObj* realFifo = (__GXFifoObj*)fifo;
+
+ return realFifo->base;
+}
+
+u32 GXGetFifoSize(const GXFifoObj* fifo) {
+ __GXFifoObj* realFifo = (__GXFifoObj*)fifo;
+ return realFifo->size;
+}
+
+void GXGetFifoLimits(const GXFifoObj* fifo, u32* hi, u32* lo) {
+ __GXFifoObj* realFifo = (__GXFifoObj*)fifo;
+ *hi = realFifo->hiWatermark;
+ *lo = realFifo->loWatermark;
+}
+
+GXBreakPtCallback GXSetBreakPtCallback(GXBreakPtCallback cb) {
+ GXBreakPtCallback oldcb = BreakPointCB;
+ BOOL enabled = OSDisableInterrupts();
+
+ BreakPointCB = cb;
+ OSRestoreInterrupts(enabled);
+ return oldcb;
+}
+
+void* __GXCurrentBP;
+
+void GXEnableBreakPt(void* break_pt) {
+ BOOL enabled = OSDisableInterrupts();
+
+ __GXFifoReadDisable();
+ GX_CP_REG_WRITE_U16(CP_FIFO_BRKL, TOPHYSICAL(break_pt));
+ GX_CP_REG_WRITE_U16(CP_FIFO_BRKH, TOPHYSICAL(break_pt) >> 16);
+ SC_CP_REG_ENABLE_SET_FIFOBRK(0, __GXData->cpEnable, 0);
+ SC_CP_REG_ENABLE_SET_FIFOBRKINT(0, __GXData->cpEnable, 0);
+ GX_CP_REG_WRITE_U16(CP_ENABLE, __GXData->cpEnable);
+ SC_CP_REG_ENABLE_SET_FIFOBRK(0, __GXData->cpEnable, 1);
+ SC_CP_REG_ENABLE_SET_FIFOBRKINT(0, __GXData->cpEnable, 1);
+ GX_CP_REG_WRITE_U16(CP_ENABLE, __GXData->cpEnable);
+ __GXCurrentBP = break_pt;
+ __GXFifoReadEnable();
+ OSRestoreInterrupts(enabled);
+}
+
+void GXDisableBreakPt(void) {
+ BOOL enabled = OSDisableInterrupts();
+
+ SC_CP_REG_ENABLE_SET_FIFOBRK(0, __GXData->cpEnable, 0);
+ SC_CP_REG_ENABLE_SET_FIFOBRKINT(0, __GXData->cpEnable, 0);
+ GX_CP_REG_WRITE_U16(CP_ENABLE, __GXData->cpEnable);
+ __GXCurrentBP = NULL;
+ OSRestoreInterrupts(enabled);
+}
+
+void __GXFifoInit(void) {
+ __OSSetInterruptHandler(0x11, GXCPInterruptHandler);
+ __OSUnmaskInterrupts(0x4000);
+ __GXCurrentThread = OSGetCurrentThread();
+ GXOverflowSuspendInProgress = FALSE;
+ memset(&CPUFifo, 0, sizeof(__GXFifoObj));
+ memset(&GPFifo, 0, sizeof(__GXFifoObj));
+ CPUFifoReady = GX_FALSE;
+ GPFifoReady = GX_FALSE;
+}
+
+static void __GXFifoReadEnable(void) {
+ SC_CP_REG_ENABLE_SET_FIFORD(0, __GXData->cpEnable, 1);
+ GX_CP_REG_WRITE_U16(CP_ENABLE, (u16)__GXData->cpEnable);
+}
+
+static void __GXFifoReadDisable(void) {
+ SC_CP_REG_ENABLE_SET_FIFORD(0, __GXData->cpEnable, 0);
+ GX_CP_REG_WRITE_U16(CP_ENABLE, (u16)__GXData->cpEnable);
+}
+
+static void __GXFifoLink(u8 en) {
+ SC_CP_REG_ENABLE_SET_WRPTRINC(1385, __GXData->cpEnable, (en ? 0x1 : 0));
+ GX_CP_REG_WRITE_U16(CP_ENABLE, (u16)__GXData->cpEnable);
+}
+
+static void __GXWriteFifoIntEnable(u8 hiWatermarkEn, u8 loWatermarkEn) {
+ SC_CP_REG_ENABLE_SET_OVFLINT(1407, __GXData->cpEnable, hiWatermarkEn);
+ SC_CP_REG_ENABLE_SET_UNFLINT(1408, __GXData->cpEnable, loWatermarkEn);
+ GX_CP_REG_WRITE_U16(CP_ENABLE, (u16)__GXData->cpEnable);
+}
+
+static void __GXWriteFifoIntReset(u8 hiWatermarkClr, u8 loWatermarkClr) {
+ SC_CP_REG_CLR_SET_OVFLINT(1431, __GXData->cpClr, hiWatermarkClr);
+ SC_CP_REG_CLR_SET_UNFLINT(1432, __GXData->cpClr, loWatermarkClr);
+ GX_CP_REG_WRITE_U16(CP_CLR, (u16)__GXData->cpClr);
+}
+
+void __GXCleanGPFifo(void) {
+ BOOL enabled;
+
+ if ( !GPFifoReady) {
+ return;
+ }
+
+ enabled = OSDisableInterrupts();
+ __GXFifoReadDisable();
+ __GXWriteFifoIntEnable(GX_FALSE, GX_FALSE);
+ GPFifo.rdPtr = GPFifo.wrPtr;
+ GPFifo.count = 0;
+ GX_CP_REG_WRITE_U16(CP_FIFO_COUNTL, GPFifo.count);
+ GX_CP_REG_WRITE_U16(CP_FIFO_WPTRL, TOPHYSICAL(GPFifo.wrPtr));
+ GX_CP_REG_WRITE_U16(CP_FIFO_RPTRL, TOPHYSICAL(GPFifo.rdPtr));
+ GX_CP_REG_WRITE_U16(CP_FIFO_COUNTH, GPFifo.count>>16);
+ GX_CP_REG_WRITE_U16(CP_FIFO_WPTRH, TOPHYSICAL(GPFifo.wrPtr)>>16);
+ GX_CP_REG_WRITE_U16(CP_FIFO_RPTRH, TOPHYSICAL(GPFifo.rdPtr)>>16);
+
+ PPCSync();
+
+ if (CPGPLinked) {
+ u32 reg = 0;
+ CPUFifo.rdPtr = GPFifo.rdPtr;
+ CPUFifo.wrPtr = GPFifo.wrPtr;
+ CPUFifo.count = GPFifo.count;
+ SC_PI_CPWRT_REG_SET_CPWRT(1501, reg, (GX_PHY_ADDR(TOPHYSICAL(CPUFifo.wrPtr)) >> 5));
+ SC_PI_CPWRT_REG_SET_CPWRAP(0, reg, 0);
+ GX_PI_REG_WRITE_U32(PI_REG_CPWRT, reg);
+ __GXWriteFifoIntEnable(GX_TRUE, GX_FALSE);
+ __GXFifoLink(GX_TRUE);
+ }
+
+ SC_CP_REG_ENABLE_SET_FIFOBRK(0, __GXData->cpEnable, 0);
+ SC_CP_REG_ENABLE_SET_FIFOBRKINT(0, __GXData->cpEnable, 0);
+ GX_CP_REG_WRITE_U16(CP_ENABLE, __GXData->cpEnable);
+ __GXCurrentBP = 0;
+ __GXWriteFifoIntReset(GX_TRUE, GX_TRUE);
+ __GXFifoReadEnable();
+ OSRestoreInterrupts(enabled);
+}
+
+OSThread* GXSetCurrentGXThread(void) {
+ BOOL enabled;
+ OSThread* prev;
+
+ enabled = OSDisableInterrupts();
+ prev = __GXCurrentThread;
+ ASSERTMSGLINE(1541, !GXOverflowSuspendInProgress, "GXSetCurrentGXThread: Two threads cannot generate GX commands at the same time!");
+ __GXCurrentThread = OSGetCurrentThread();
+ OSRestoreInterrupts(enabled);
+ return prev;
+}
+
+OSThread* GXGetCurrentGXThread(void) {
+ return __GXCurrentThread;
+}
+
+GXFifoObj* GXGetCPUFifo(void) {
+ return (GXFifoObj*)&CPUFifo;
+}
+
+GXFifoObj* GXGetGPFifo(void) {
+ return (GXFifoObj*)&GPFifo;
+}
+
+u32 GXGetOverflowCount(void) {
+ return __GXOverflowCount;
+}
+
+u32 GXResetOverflowCount(void) {
+ u32 oldcount;
+
+ oldcount = __GXOverflowCount;
+ __GXOverflowCount = 0;
+ return oldcount;
+}
+
+// NONMATCHING
+volatile void* GXRedirectWriteGatherPipe(void* ptr) {
+ u32 reg = 0;
+ BOOL enabled = OSDisableInterrupts();
+
+ CHECK_GXBEGIN(0, "GXRedirectWriteGatherPipe");
+ ASSERTLINE(0, OFFSET(ptr, 32) == 0);
+ ASSERTLINE(0, !IsWGPipeRedirected);
+
+#if DEBUG
+ IsWGPipeRedirected = TRUE;
+#endif
+
+ GXFlush();
+ while (PPCMfwpar() & 1) {}
+
+ PPCMtwpar((u32)OSUncachedToPhysical((void*)GXFIFO_ADDR));
+
+ if (CPGPLinked) {
+ __GXFifoLink(0);
+ __GXWriteFifoIntEnable(0, 0);
+ }
+
+ CPUFifo.wrPtr = OSPhysicalToCached(GX_GET_PI_REG(5) & 0xFBFFFFFF);
+ GX_SET_PI_REG(3, 0);
+ GX_SET_PI_REG(4, 0x04000000);
+ SET_REG_FIELD(0, reg, 21, 5, ((u32)ptr & 0x3FFFFFFF) >> 5);
+ reg &= 0xFBFFFFFF;
+ GX_SET_PI_REG(5, reg);
+
+ PPCSync();
+ OSRestoreInterrupts(enabled);
+ return (volatile void *)GXFIFO_ADDR;
+}
+
+// NONMATCHING
+void GXRestoreWriteGatherPipe(void) {
+ u32 reg = 0;
+ u32 i;
+ BOOL enabled;
+
+ ASSERTLINE(1552, IsWGPipeRedirected);
+
+#if DEBUG
+ IsWGPipeRedirected = FALSE;
+#endif
+
+ enabled = OSDisableInterrupts();
+ for (i = 0; i < 31; i++) {
+ GXWGFifo.u8 = 0;
+ }
+
+ PPCSync();
+ while (PPCMfwpar() & 1) {}
+ PPCMtwpar((u32)OSUncachedToPhysical((void *)GXFIFO_ADDR));
+ GX_SET_PI_REG(3, (u32)CPUFifo.base & 0x3FFFFFFF);
+ GX_SET_PI_REG(4, (u32)CPUFifo.top & 0x3FFFFFFF);
+ SET_REG_FIELD(1578, reg, 21, 5, ((u32)CPUFifo.wrPtr & 0x3FFFFFFF) >> 5);
+ reg &= 0xFBFFFFFF;
+ GX_SET_PI_REG(5, reg);
+ if (CPGPLinked) {
+ __GXWriteFifoIntReset(1, 1);
+ __GXWriteFifoIntEnable(1, 0);
+ __GXFifoLink(1);
+ }
+
+ PPCSync();
+ OSRestoreInterrupts(enabled);
+}
diff --git a/src/revolution/gx/GXFrameBuf.c b/src/revolution/gx/GXFrameBuf.c
new file mode 100644
index 0000000000..e22bbb2b77
--- /dev/null
+++ b/src/revolution/gx/GXFrameBuf.c
@@ -0,0 +1,677 @@
+#include <revolution/gx.h>
+#include <revolution/os.h>
+
+#include "__gx.h"
+
+GXRenderModeObj GXNtsc240Ds = {
+ 1,
+ 640, 240, 240,
+ 40, 0,
+ 640, 480,
+ 0,
+ 0,
+ 0,
+ { 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 },
+ { 0, 0, 21, 22, 21, 0, 0 }
+};
+
+GXRenderModeObj GXNtsc240DsAa = {
+ 1, 640, 240, 240, 40, 0, 640, 480, 0, 0, 1, { 3, 2, 9, 6, 3, 10, 3, 2, 9, 6, 3, 10, 9, 2, 3, 6, 9, 10, 9, 2, 3, 6, 9, 10 }, { 0, 0, 21, 22, 21, 0, 0 }
+};
+
+GXRenderModeObj GXNtsc240Int = {
+ 0, 640, 240, 240, 40, 0, 640, 480, 0, 1, 0, { 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 }, { 0, 0, 21, 22, 21, 0, 0 }
+};
+
+GXRenderModeObj GXNtsc240IntAa = {
+ 0, 640, 240, 240, 40, 0, 640, 480, 0, 1, 1, { 3, 2, 9, 6, 3, 10, 3, 2, 9, 6, 3, 10, 9, 2, 3, 6, 9, 10, 9, 2, 3, 6, 9, 10 }, { 0, 0, 21, 22, 21, 0, 0 }
+};
+
+GXRenderModeObj GXNtsc480IntDf = {
+ 0, 640, 480, 480, 40, 0, 640, 480, 1, 0, 0, { 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 }, { 8, 8, 10, 12, 10, 8, 8 }
+};
+
+GXRenderModeObj GXNtsc480Int = {
+ 0, 640, 480, 480, 40, 0, 640, 480, 1, 0, 0, { 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 }, { 0, 0, 21, 22, 21, 0, 0 }
+};
+
+GXRenderModeObj GXNtsc480IntAa = {
+ 0, 640, 242, 480, 40, 0, 640, 480, 1, 0, 1, { 3, 2, 9, 6, 3, 10, 3, 2, 9, 6, 3, 10, 9, 2, 3, 6, 9, 10, 9, 2, 3, 6, 9, 10 }, { 4, 8, 12, 16, 12, 8, 4 }
+};
+
+GXRenderModeObj GXNtsc480Prog = {
+ 2, 640, 480, 480, 40, 0, 640, 480, 0, 0, 0, { 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 }, { 0, 0, 21, 22, 21, 0, 0 }
+};
+
+GXRenderModeObj GXNtsc480ProgSoft = {
+ 2, 640, 480, 480, 40, 0, 640, 480, 0, 0, 0, { 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 }, { 8, 8, 10, 12, 10, 8, 8 }
+};
+
+GXRenderModeObj GXNtsc480ProgAa = {
+ 2, 640, 242, 480, 40, 0, 640, 480, 0, 0, 1, { 3, 2, 9, 6, 3, 10, 3, 2, 9, 6, 3, 10, 9, 2, 3, 6, 9, 10, 9, 2, 3, 6, 9, 10 }, { 4, 8, 12, 16, 12, 8, 4 }
+};
+
+GXRenderModeObj GXMpal240Ds = {9, 640, 240, 240, 40, 0, 640, 480, 0, 0, 0, { 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 }, { 0, 0, 21, 22, 21, 0, 0 } };
+GXRenderModeObj GXMpal240DsAa = {9, 640, 240, 240, 40, 0, 640, 480, 0, 0, 1, { 3, 2, 9, 6, 3, 10, 3, 2, 9, 6, 3, 10, 9, 2, 3, 6, 9, 10, 9, 2, 3, 6, 9, 10 }, { 0, 0, 21, 22, 21, 0, 0 } };
+GXRenderModeObj GXMpal240Int = {8, 640, 240, 240, 40, 0, 640, 480, 0, 1, 0, { 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 }, { 0, 0, 21, 22, 21, 0, 0 } };
+GXRenderModeObj GXMpal240IntAa = {8, 640, 240, 240, 40, 0, 640, 480, 0, 1, 1, { 3, 2, 9, 6, 3, 10, 3, 2, 9, 6, 3, 10, 9, 2, 3, 6, 9, 10, 9, 2, 3, 6, 9, 10 }, { 0, 0, 21, 22, 21, 0, 0 } };
+GXRenderModeObj GXMpal480IntDf = {8, 640, 480, 480, 40, 0, 640, 480, 1, 0, 0, { 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 }, { 8, 8, 10, 12, 10, 8, 8 } };
+GXRenderModeObj GXMpal480Int = {8, 640, 480, 480, 40, 0, 640, 480, 1, 0, 0, { 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 }, { 0, 0, 21, 22, 21, 0, 0 } };
+GXRenderModeObj GXMpal480IntAa = {8, 640, 242, 480, 40, 0, 640, 480, 1, 0, 1, { 3, 2, 9, 6, 3, 10, 3, 2, 9, 6, 3, 10, 9, 2, 3, 6, 9, 10, 9, 2, 3, 6, 9, 10 }, { 4, 8, 12, 16, 12, 8, 4 } };
+
+GXRenderModeObj GXMpal480Prog = {
+ 10,
+ 640, 480, 480,
+ 40, 0,
+ 640, 480,
+ 0,
+ 0,
+ 0,
+ { 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 },
+ { 0, 0, 21, 22, 21, 0, 0 }
+};
+
+GXRenderModeObj GXMpal480ProgSoft = {
+ 10,
+ 640, 480, 480,
+ 40, 0,
+ 640, 480,
+ 0,
+ 0,
+ 0,
+ { 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 },
+ { 8, 8, 10, 12, 10, 8, 8 }
+};
+
+GXRenderModeObj GXMpal480ProgAa = {
+ 10,
+ 640, 242, 480,
+ 40, 0,
+ 640, 480,
+ 0,
+ 0,
+ 1,
+ { 3, 2, 9, 6, 3, 10, 3, 2, 9, 6, 3, 10, 9, 2, 3, 6, 9, 10, 9, 2, 3, 6, 9, 10 },
+ { 4, 8, 12, 16, 12, 8, 4 }
+};
+
+GXRenderModeObj GXPal264Ds = {5, 640, 264, 264, 40, 11, 640, 528, 0, 0, 0, { 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 }, { 0, 0, 21, 22, 21, 0, 0 } };
+GXRenderModeObj GXPal264DsAa = {5, 640, 264, 264, 40, 11, 640, 528, 0, 0, 1, { 3, 2, 9, 6, 3, 10, 3, 2, 9, 6, 3, 10, 9, 2, 3, 6, 9, 10, 9, 2, 3, 6, 9, 10 }, { 0, 0, 21, 22, 21, 0, 0 } };
+GXRenderModeObj GXPal264Int = {4, 640, 264, 264, 40, 23, 640, 528, 0, 1, 0, { 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 }, { 0, 0, 21, 22, 21, 0, 0 } };
+GXRenderModeObj GXPal264IntAa = {4, 640, 264, 264, 40, 23, 640, 528, 0, 1, 1, { 3, 2, 9, 6, 3, 10, 3, 2, 9, 6, 3, 10, 9, 2, 3, 6, 9, 10, 9, 2, 3, 6, 9, 10 }, { 0, 0, 21, 22, 21, 0, 0 } };
+GXRenderModeObj GXPal528IntDf = {4, 640, 528, 528, 40, 23, 640, 528, 1, 0, 0, { 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 }, { 8, 8, 10, 12, 10, 8, 8 } };
+GXRenderModeObj GXPal528Int = {4, 640, 528, 528, 40, 23, 640, 528, 1, 0, 0, { 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 }, { 0, 0, 21, 22, 21, 0, 0 } };
+GXRenderModeObj GXPal524IntAa = {4, 640, 264, 524, 40, 23, 640, 524, 1, 0, 1, { 3, 2, 9, 6, 3, 10, 3, 2, 9, 6, 3, 10, 9, 2, 3, 6, 9, 10, 9, 2, 3, 6, 9, 10 }, { 4, 8, 12, 16, 12, 8, 4 } };
+GXRenderModeObj GXEurgb60Hz240Ds = {21, 640, 240, 240, 40, 0, 640, 480, 0, 0, 0, { 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 }, { 0, 0, 21, 22, 21, 0, 0 } };
+GXRenderModeObj GXEurgb60Hz240DsAa = {21, 640, 240, 240, 40, 0, 640, 480, 0, 0, 1, { 3, 2, 9, 6, 3, 10, 3, 2, 9, 6, 3, 10, 9, 2, 3, 6, 9, 10, 9, 2, 3, 6, 9, 10 }, { 0, 0, 21, 22, 21, 0, 0 } };
+GXRenderModeObj GXEurgb60Hz240Int = {20, 640, 240, 240, 40, 0, 640, 480, 0, 1, 0, { 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 }, { 0, 0, 21, 22, 21, 0, 0 } };
+GXRenderModeObj GXEurgb60Hz240IntAa = {20, 640, 240, 240, 40, 0, 640, 480, 0, 1, 1, { 3, 2, 9, 6, 3, 10, 3, 2, 9, 6, 3, 10, 9, 2, 3, 6, 9, 10, 9, 2, 3, 6, 9, 10 }, { 0, 0, 21, 22, 21, 0, 0 } };
+GXRenderModeObj GXEurgb60Hz480IntDf = {20, 640, 480, 480, 40, 0, 640, 480, 1, 0, 0, { 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 }, { 8, 8, 10, 12, 10, 8, 8 } };
+GXRenderModeObj GXEurgb60Hz480Int = {20, 640, 480, 480, 40, 0, 640, 480, 1, 0, 0, { 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 }, { 0, 0, 21, 22, 21, 0, 0 } };
+GXRenderModeObj GXEurgb60Hz480IntAa = {20, 640, 242, 480, 40, 0, 640, 480, 1, 0, 1, { 3, 2, 9, 6, 3, 10, 3, 2, 9, 6, 3, 10, 9, 2, 3, 6, 9, 10, 9, 2, 3, 6, 9, 10 }, { 4, 8, 12, 16, 12, 8, 4 } };
+
+GXRenderModeObj GXEurgb60Hz480Prog = {
+ 22,
+ 640, 480, 480,
+ 40, 0,
+ 640, 480,
+ 0,
+ 0,
+ 0,
+ { 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 },
+ { 0, 0, 21, 22, 21, 0, 0 }
+};
+
+GXRenderModeObj GXEurgb60Hz480ProgSoft = {
+ 22,
+ 640, 480, 480,
+ 40, 0,
+ 640, 480,
+ 0,
+ 0,
+ 0,
+ { 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 },
+ { 8, 8, 10, 12, 10, 8, 8 }
+};
+
+GXRenderModeObj GXEurgb60Hz480ProgAa = {
+ 22,
+ 640, 242, 480,
+ 40, 0,
+ 640, 480,
+ 0,
+ 0,
+ 1,
+ { 3, 2, 9, 6, 3, 10, 3, 2, 9, 6, 3, 10, 9, 2, 3, 6, 9, 10, 9, 2, 3, 6, 9, 10 },
+ { 4, 8, 12, 16, 12, 8, 4 }
+};
+
+GXRenderModeObj GXRmHW = {1, 320, 240, 240, 40, 0, 640, 480, 0, 0, 0, { 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 }, { 0, 0, 21, 22, 21, 0, 0 } };
+
+void GXAdjustForOverscan(const GXRenderModeObj* rmin, GXRenderModeObj* rmout, u16 hor, u16 ver) {
+ u16 hor2 = hor * 2;
+ u16 ver2 = ver * 2;
+ u32 verf;
+ u32 mode;
+
+ if (rmin != rmout) {
+ *rmout = *rmin;
+ }
+
+ mode = rmin->viTVmode & 3;
+ rmout->fbWidth = rmin->fbWidth - hor2;
+ verf = (ver2 * rmin->efbHeight) / (u32)rmin->xfbHeight;
+ rmout->efbHeight = rmin->efbHeight - verf;
+ if (rmin->xFBmode == VI_XFBMODE_SF && mode == 0) {
+ rmout->xfbHeight = rmin->xfbHeight - ver2 / 2;
+ } else {
+ rmout->xfbHeight = rmin->xfbHeight - ver2;
+ }
+
+ rmout->viWidth = rmin->viWidth - hor2;
+
+ if (mode == 1) {
+ rmout->viHeight = rmin->viHeight - (ver2 * 2);
+ } else {
+ rmout->viHeight = rmin->viHeight - ver2;
+ }
+
+ rmout->viXOrigin = rmin->viXOrigin + hor;
+ rmout->viYOrigin = rmin->viYOrigin + ver;
+}
+
+void GXSetDispCopySrc(u16 left, u16 top, u16 wd, u16 ht) {
+ CHECK_GXBEGIN(1510, "GXSetDispCopySrc");
+
+ __GXData->cpDispSrc = 0;
+
+ SET_REG_FIELD(1513, __GXData->cpDispSrc, 10, 0, left);
+ SET_REG_FIELD(1514, __GXData->cpDispSrc, 10, 10, top);
+ SET_REG_FIELD(1514, __GXData->cpDispSrc, 8, 24, 0x49);
+
+ __GXData->cpDispSize = 0;
+ SET_REG_FIELD(1518, __GXData->cpDispSize, 10, 0, wd - 1);
+ SET_REG_FIELD(1519, __GXData->cpDispSize, 10, 10, ht - 1);
+ SET_REG_FIELD(1519, __GXData->cpDispSize, 8, 24, 0x4A);
+}
+
+
+void GXSetTexCopySrc(u16 left, u16 top, u16 wd, u16 ht) {
+ CHECK_GXBEGIN(1538, "GXSetTexCopySrc");
+
+ __GXData->cpTexSrc = 0;
+ SET_REG_FIELD(1541, __GXData->cpTexSrc, 10, 0, left);
+ SET_REG_FIELD(1542, __GXData->cpTexSrc, 10, 10, top);
+ SET_REG_FIELD(1542, __GXData->cpTexSrc, 8, 24, 0x49);
+
+ __GXData->cpTexSize = 0;
+ SET_REG_FIELD(1546, __GXData->cpTexSize, 10, 0, wd - 1);
+ SET_REG_FIELD(1547, __GXData->cpTexSize, 10, 10, ht - 1);
+ SET_REG_FIELD(1547, __GXData->cpTexSize, 8, 24, 0x4A);
+}
+
+void GXSetDispCopyDst(u16 wd, u16 ht) {
+ u16 stride;
+
+ ASSERTMSGLINE(1568, (wd & 15) == 0, "GXSetDispCopyDst: Width must be a multiple of 16");
+ CHECK_GXBEGIN(1569, "GXSetDispCopyDst");
+
+ stride = (int)wd * 2;
+ __GXData->cpDispStride = 0;
+ SET_REG_FIELD(1575, __GXData->cpDispStride, 10, 0, (stride >> 5) );
+ SET_REG_FIELD(1575, __GXData->cpDispStride, 8, 24, 0x4D);
+}
+
+void GXSetTexCopyDst(u16 wd, u16 ht, GXTexFmt fmt, GXBool mipmap) {
+ u32 rowTiles;
+ u32 colTiles;
+ u32 cmpTiles;
+ u32 peTexFmt;
+ u32 peTexFmtH;
+
+ CHECK_GXBEGIN(1602, "GXSetTexCopyDst");
+
+ __GXData->cpTexZ = 0;
+ peTexFmt = fmt & 0xF;
+ ASSERTMSGLINEV(1633, peTexFmt < 13, "%s: invalid texture format", "GXSetTexCopyDst");
+
+ if (fmt == GX_TF_Z16) {
+ peTexFmt = 0xB;
+ }
+
+ switch (fmt) {
+ case GX_TF_I4:
+ case GX_TF_I8:
+ case GX_TF_IA4:
+ case GX_TF_IA8:
+ case GX_CTF_YUVA8:
+ SET_REG_FIELD(0, __GXData->cpTex, 2, 15, 3);
+ break;
+ default:
+ SET_REG_FIELD(0, __GXData->cpTex, 2, 15, 2);
+ break;
+ }
+
+ __GXData->cpTexZ = (fmt & _GX_TF_ZTF) == _GX_TF_ZTF;
+ peTexFmtH = (peTexFmt & 8) >> 3;
+ SET_REG_FIELD(1656, __GXData->cpTex, 1, 3, peTexFmtH);
+ peTexFmt = peTexFmt & 7;
+ __GetImageTileCount(fmt, wd, ht, &rowTiles, &colTiles, &cmpTiles);
+
+ __GXData->cpTexStride = 0;
+ SET_REG_FIELD(1665, __GXData->cpTexStride, 10, 0, rowTiles * cmpTiles);
+ SET_REG_FIELD(1667, __GXData->cpTexStride, 8, 24, 0x4D);
+ SET_REG_FIELD(1667, __GXData->cpTex, 1, 9, mipmap);
+ SET_REG_FIELD(1668, __GXData->cpTex, 3, 4, peTexFmt);
+}
+
+void GXSetDispCopyFrame2Field(GXCopyMode mode) {
+ CHECK_GXBEGIN(1685, "GXSetDispCopyFrame2Field");
+ SET_REG_FIELD(1686, __GXData->cpDisp, 2, 12, mode);
+ SET_REG_FIELD(1686, __GXData->cpTex, 2, 12, 0);
+}
+
+void GXSetCopyClamp(GXFBClamp clamp) {
+ u8 clmpB;
+ u8 clmpT;
+
+ CHECK_GXBEGIN(1706, "GXSetCopyClamp");
+
+ clmpT = (clamp & GX_CLAMP_TOP) == 1;
+ clmpB = (clamp & GX_CLAMP_BOTTOM) == 2;
+
+ SET_REG_FIELD(1710, __GXData->cpDisp, 1, 0, clmpT);
+ SET_REG_FIELD(1711, __GXData->cpDisp, 1, 1, clmpB);
+
+ SET_REG_FIELD(1713, __GXData->cpTex, 1, 0, clmpT);
+ SET_REG_FIELD(1714, __GXData->cpTex, 1, 1, clmpB);
+}
+
+static u32 __GXGetNumXfbLines(u32 efbHt, u32 iScale) {
+ u32 count;
+ u32 realHt;
+ u32 iScaleD;
+
+ count = (efbHt - 1) * 0x100;
+ realHt = (count / iScale) + 1;
+
+ iScaleD = iScale;
+
+ if (iScaleD > 0x80 && iScaleD < 0x100) {
+ while (iScaleD % 2 == 0) {
+ iScaleD /= 2;
+ }
+
+ if (efbHt % iScaleD == 0) {
+ realHt++;
+ }
+ }
+
+ if (realHt > 0x400) {
+ realHt = 0x400;
+ }
+
+ return realHt;
+}
+
+u16 GXGetNumXfbLines(u16 efbHeight, f32 yScale) {
+ u32 iScale;
+ ASSERTMSGLINE(1761, yScale >= 1.0f, "GXGetNumXfbLines: Vertical scale must be >= 1.0");
+
+ iScale = (u32)(256.0f / yScale) & 0x1FF;
+ return __GXGetNumXfbLines(efbHeight, iScale);
+}
+
+f32 GXGetYScaleFactor(u16 efbHeight, u16 xfbHeight) {
+ f32 fScale;
+ f32 yScale;
+ u32 iScale;
+ u32 tgtHt;
+ u32 realHt;
+
+ ASSERTMSGLINE(1785, xfbHeight <= 1024, "GXGetYScaleFactor: Display copy only supports up to 1024 lines.\n");
+ ASSERTMSGLINE(1787, efbHeight <= xfbHeight, "GXGetYScaleFactor: EFB height should not be greater than XFB height.\n");
+
+ tgtHt = xfbHeight;
+ yScale = (f32)xfbHeight / (f32)efbHeight;
+ iScale = (u32)(256.0f / yScale) & 0x1FF;
+ realHt = __GXGetNumXfbLines(efbHeight, iScale);
+
+ while (realHt > xfbHeight) {
+ tgtHt--;
+ yScale = (f32)tgtHt / (f32)efbHeight;
+ iScale = (u32)(256.0f / yScale) & 0x1FF;
+ realHt = __GXGetNumXfbLines(efbHeight, iScale);
+ }
+
+ fScale = yScale;
+ while (realHt < xfbHeight) {
+ fScale = yScale;
+ tgtHt++;
+ yScale = (f32)tgtHt / (f32)efbHeight;
+ iScale = (u32)(256.0f / yScale) & 0x1FF;
+ realHt = __GXGetNumXfbLines(efbHeight, iScale);
+ }
+
+ return fScale;
+}
+
+u32 GXSetDispCopyYScale(f32 vscale) {
+ u8 enable;
+ u32 iScale;
+ u32 ht;
+ u32 reg;
+
+ CHECK_GXBEGIN(1832, "GXSetDispCopyYScale");
+
+ ASSERTMSGLINE(1834, vscale >= 1.0f, "GXSetDispCopyYScale: Vertical scale must be >= 1.0");
+
+ iScale = (u32) (256.0f / vscale) & 0x1FF;
+ enable = (iScale != 256);
+
+ reg = 0;
+ SET_REG_FIELD(1841, reg, 9, 0, iScale);
+ SET_REG_FIELD(1841, reg, 8, 24, 0x4E);
+ GX_WRITE_RAS_REG(reg);
+ __GXData->bpSentNot = 0;
+ SET_REG_FIELD(1846, __GXData->cpDisp, 1, 10, enable);
+ ht = (u32)GET_REG_FIELD(__GXData->cpDispSize, 10, 10) + 1;
+ return __GXGetNumXfbLines(ht, iScale);
+}
+
+void GXSetCopyClear(GXColor clear_clr, u32 clear_z) {
+ u32 reg;
+
+ CHECK_GXBEGIN(1871, "GXSetCopyClear");
+ ASSERTMSGLINE(1873, clear_z <= 0xFFFFFF, "GXSetCopyClear: Z clear value is out of range");
+
+ reg = 0;
+ SET_REG_FIELD(1876, reg, 8, 0, clear_clr.r);
+ SET_REG_FIELD(1877, reg, 8, 8, clear_clr.a);
+ SET_REG_FIELD(1877, reg, 8, 24, 0x4F);
+ GX_WRITE_RAS_REG(reg);
+
+ reg = 0;
+ SET_REG_FIELD(1882, reg, 8, 0, clear_clr.b);
+ SET_REG_FIELD(1883, reg, 8, 8, clear_clr.g);
+ SET_REG_FIELD(1883, reg, 8, 24, 0x50);
+ GX_WRITE_RAS_REG(reg);
+
+ reg = 0;
+ SET_REG_FIELD(1888, reg, 24, 0, clear_z);
+ SET_REG_FIELD(1888, reg, 8, 24, 0x51);
+ GX_WRITE_RAS_REG(reg);
+ __GXData->bpSentNot = 0;
+}
+
+void GXSetCopyFilter(GXBool aa, const u8 sample_pattern[12][2], GXBool vf, const u8 vfilter[7]) {
+ u32 msLoc[4];
+ u32 coeff0;
+ u32 coeff1;
+
+ CHECK_GXBEGIN(1916, "GXSetCopyFilter");
+
+ if (aa != 0) {
+ msLoc[0] = 0;
+ SET_REG_FIELD(1920, msLoc[0], 4, 0, sample_pattern[0][0]);
+ SET_REG_FIELD(1921, msLoc[0], 4, 4, sample_pattern[0][1]);
+ SET_REG_FIELD(1922, msLoc[0], 4, 8, sample_pattern[1][0]);
+ SET_REG_FIELD(1923, msLoc[0], 4, 12, sample_pattern[1][1]);
+ SET_REG_FIELD(1924, msLoc[0], 4, 16, sample_pattern[2][0]);
+ SET_REG_FIELD(1925, msLoc[0], 4, 20, sample_pattern[2][1]);
+ SET_REG_FIELD(1926, msLoc[0], 8, 24, 1);
+
+ msLoc[1] = 0;
+ SET_REG_FIELD(1929, msLoc[1], 4, 0, sample_pattern[3][0]);
+ SET_REG_FIELD(1930, msLoc[1], 4, 4, sample_pattern[3][1]);
+ SET_REG_FIELD(1931, msLoc[1], 4, 8, sample_pattern[4][0]);
+ SET_REG_FIELD(1932, msLoc[1], 4, 12, sample_pattern[4][1]);
+ SET_REG_FIELD(1933, msLoc[1], 4, 16, sample_pattern[5][0]);
+ SET_REG_FIELD(1934, msLoc[1], 4, 20, sample_pattern[5][1]);
+ SET_REG_FIELD(1935, msLoc[1], 8, 24, 2);
+
+ msLoc[2] = 0;
+ SET_REG_FIELD(1938, msLoc[2], 4, 0, sample_pattern[6][0]);
+ SET_REG_FIELD(1939, msLoc[2], 4, 4, sample_pattern[6][1]);
+ SET_REG_FIELD(1940, msLoc[2], 4, 8, sample_pattern[7][0]);
+ SET_REG_FIELD(1941, msLoc[2], 4, 12, sample_pattern[7][1]);
+ SET_REG_FIELD(1942, msLoc[2], 4, 16, sample_pattern[8][0]);
+ SET_REG_FIELD(1943, msLoc[2], 4, 20, sample_pattern[8][1]);
+ SET_REG_FIELD(1944, msLoc[2], 8, 24, 3);
+
+ msLoc[3] = 0;
+ SET_REG_FIELD(1947, msLoc[3], 4, 0, sample_pattern[9][0]);
+ SET_REG_FIELD(1948, msLoc[3], 4, 4, sample_pattern[9][1]);
+ SET_REG_FIELD(1949, msLoc[3], 4, 8, sample_pattern[10][0]);
+ SET_REG_FIELD(1950, msLoc[3], 4, 12, sample_pattern[10][1]);
+ SET_REG_FIELD(1951, msLoc[3], 4, 16, sample_pattern[11][0]);
+ SET_REG_FIELD(1952, msLoc[3], 4, 20, sample_pattern[11][1]);
+ SET_REG_FIELD(1953, msLoc[3], 8, 24, 4);
+ } else {
+ msLoc[0] = 0x01666666;
+ msLoc[1] = 0x02666666;
+ msLoc[2] = 0x03666666;
+ msLoc[3] = 0x04666666;
+ }
+
+ GX_WRITE_RAS_REG(msLoc[0]);
+ GX_WRITE_RAS_REG(msLoc[1]);
+ GX_WRITE_RAS_REG(msLoc[2]);
+ GX_WRITE_RAS_REG(msLoc[3]);
+
+ coeff0 = 0;
+ SET_REG_FIELD(0, coeff0, 8, 24, 0x53);
+ coeff1 = 0;
+ SET_REG_FIELD(0, coeff1, 8, 24, 0x54);
+
+ if (vf != 0) {
+ SET_REG_FIELD(1977, coeff0, 6, 0, vfilter[0]);
+ SET_REG_FIELD(1978, coeff0, 6, 6, vfilter[1]);
+ SET_REG_FIELD(1979, coeff0, 6, 12, vfilter[2]);
+ SET_REG_FIELD(1980, coeff0, 6, 18, vfilter[3]);
+ SET_REG_FIELD(1981, coeff1, 6, 0, vfilter[4]);
+ SET_REG_FIELD(1982, coeff1, 6, 6, vfilter[5]);
+ SET_REG_FIELD(1983, coeff1, 6, 12, vfilter[6]);
+ } else {
+ SET_REG_FIELD(0, coeff0, 6, 0, 0);
+ SET_REG_FIELD(0, coeff0, 6, 6, 0);
+ SET_REG_FIELD(0, coeff0, 6, 12, 21);
+ SET_REG_FIELD(0, coeff0, 6, 18, 22);
+ SET_REG_FIELD(0, coeff1, 6, 0, 21);
+ SET_REG_FIELD(0, coeff1, 6, 6, 0);
+ SET_REG_FIELD(0, coeff1, 6, 12, 0);
+ }
+
+ GX_WRITE_RAS_REG(coeff0);
+ GX_WRITE_RAS_REG(coeff1);
+ __GXData->bpSentNot = 0;
+}
+
+void GXSetDispCopyGamma(GXGamma gamma) {
+ CHECK_GXBEGIN(2016, "GXSetDispCopyGamma");
+ SET_REG_FIELD(2017, __GXData->cpDisp, 2, 7, gamma);
+}
+
+#if DEBUG
+static void __GXVerifCopy(void* dest, u8 clear) {
+ u8 clmpT;
+ u8 clmpB;
+ u32 x0;
+ u32 y0;
+ u32 dx;
+ u32 dy;
+
+ CHECK_GXBEGIN(2037, "GXCopyDisp");
+
+ clmpT = GET_REG_FIELD(__GXData->cpDisp, 1, 0);
+ clmpB = (u32)GET_REG_FIELD(__GXData->cpDisp, 1, 1);
+ x0 = GET_REG_FIELD(__GXData->cpDispSrc, 10, 0);
+ dx = GET_REG_FIELD(__GXData->cpDispSize, 10, 0) + 1;
+ y0 = GET_REG_FIELD(__GXData->cpDispSrc, 10, 10);
+ dy = GET_REG_FIELD(__GXData->cpDispSize, 10, 10) + 1;
+
+ ASSERTMSGLINE(2047, clmpT || y0 != 0, "GXCopy: Have to set GX_CLAMP_TOP if source top == 0");
+ ASSERTMSGLINE(2049, clmpB || y0 + dy <= 528, "GXCopy: Have to set GX_CLAMP_BOTTOM if source bottom > 528");
+ ASSERTMSGLINE(2054, (__GXData->peCtrl & 7) != 3 || clear == 0, "GXCopy: Can not do clear while pixel type is Z");
+
+ if ((u32) (__GXData->peCtrl & 7) == 5) {
+ ASSERTMSGLINE(2060, clear == 0, "GXCopy: Can not clear YUV framebuffer");
+ ASSERTMSGLINE(2062, (x0 & 3) == 0, "GXCopy: Source x is not multiple of 4 for YUV copy");
+ ASSERTMSGLINE(2064, (y0 & 3) == 0, "GXCopy: Source y is not multiple of 4 for YUV copy");
+ ASSERTMSGLINE(2066, (dx & 3) == 0, "GXCopy: Source width is not multiple of 4 for YUV copy");
+ ASSERTMSGLINE(2068, (dy & 3) == 0, "GXCopy: Source height is not multiple of 4 for YUV copy");
+ } else {
+ ASSERTMSGLINE(2072, (x0 & 1) == 0, "GXCopy: Source x is not multiple of 2 for RGB copy");
+ ASSERTMSGLINE(2074, (y0 & 1) == 0, "GXCopy: Source y is not multiple of 2 for RGB copy");
+ ASSERTMSGLINE(2076, (dx & 1) == 0, "GXCopy: Source width is not multiple of 2 for RGB copy");
+ ASSERTMSGLINE(2078, (dy & 1) == 0, "GXCopy: Source height is not multiple of 2 for RGB copy");
+ }
+
+ ASSERTMSGLINE(2082, ((u32)dest & 0x1F) == 0, "GXCopy: Display destination address not 32B aligned");
+}
+#endif
+
+void GXCopyDisp(void* dest, GXBool clear) {
+ u32 reg;
+ u32 tempPeCtrl;
+ u32 phyAddr;
+ u8 changePeCtrl;
+
+ CHECK_GXBEGIN(2108, "GXCopyDisp");
+
+#if DEBUG
+ __GXVerifCopy(dest, clear);
+#endif
+
+ if (clear) {
+ reg = __GXData->zmode;
+ SET_REG_FIELD(0, reg, 1, 0, 1);
+ SET_REG_FIELD(0, reg, 3, 1, 7);
+ GX_WRITE_RAS_REG(reg);
+
+ reg = __GXData->cmode0;
+ SET_REG_FIELD(0, reg, 1, 0, 0);
+ SET_REG_FIELD(0, reg, 1, 1, 0);
+ GX_WRITE_RAS_REG(reg);
+ }
+
+ changePeCtrl = FALSE;
+
+ if ((clear || (u32)GET_REG_FIELD(__GXData->peCtrl, 3, 0) == 3) && (u32)GET_REG_FIELD(__GXData->peCtrl, 1, 6) == 1) {
+ changePeCtrl = TRUE;
+ tempPeCtrl = __GXData->peCtrl;
+ SET_REG_FIELD(0, tempPeCtrl, 1, 6, 0);
+ GX_WRITE_RAS_REG(tempPeCtrl);
+ }
+
+ GX_WRITE_RAS_REG(__GXData->cpDispSrc);
+ GX_WRITE_RAS_REG(__GXData->cpDispSize);
+ GX_WRITE_RAS_REG(__GXData->cpDispStride);
+
+ phyAddr = (u32)dest & 0x3FFFFFFF;
+ reg = 0;
+ SET_REG_FIELD(2147, reg, 24, 0, phyAddr >> 5);
+ SET_REG_FIELD(2147, reg, 8, 24, 0x4B);
+ GX_WRITE_RAS_REG(reg);
+
+ SET_REG_FIELD(2151, __GXData->cpDisp, 1, 11, clear);
+ SET_REG_FIELD(2151, __GXData->cpDisp, 1, 14, 1);
+ SET_REG_FIELD(2151, __GXData->cpDisp, 8, 24, 0x52);
+ GX_WRITE_RAS_REG(__GXData->cpDisp);
+
+ if (clear) {
+ GX_WRITE_RAS_REG(__GXData->zmode);
+ GX_WRITE_RAS_REG(__GXData->cmode0);
+ }
+
+ if (changePeCtrl) {
+ GX_WRITE_RAS_REG(__GXData->peCtrl);
+ }
+
+ __GXData->bpSentNot = 0;
+}
+
+void GXCopyTex(void* dest, GXBool clear) {
+ u32 reg;
+ u32 tempPeCtrl;
+ u32 phyAddr;
+ u8 changePeCtrl;
+
+ CHECK_GXBEGIN(2191, "GXCopyTex");
+
+#if DEBUG
+ __GXVerifCopy(dest, clear);
+#endif
+ if (clear) {
+ reg = __GXData->zmode;
+ SET_REG_FIELD(0, reg, 1, 0, 1);
+ SET_REG_FIELD(0, reg, 3, 1, 7);
+ GX_WRITE_RAS_REG(reg);
+
+ reg = __GXData->cmode0;
+ SET_REG_FIELD(0, reg, 1, 0, 0);
+ SET_REG_FIELD(0, reg, 1, 1, 0);
+ GX_WRITE_RAS_REG(reg);
+ }
+
+ changePeCtrl = 0;
+ tempPeCtrl = __GXData->peCtrl;
+
+ if (__GXData->cpTexZ && ((tempPeCtrl & 7) != 3)) {
+ changePeCtrl = 1;
+ SET_REG_FIELD(0, tempPeCtrl, 3, 0, 3);
+ }
+
+ if ((clear || ((u32) (tempPeCtrl & 7) == 3)) && ((u32) ((tempPeCtrl & 0x40) >> 6) == 1)) {
+ changePeCtrl = 1;
+ SET_REG_FIELD(0, tempPeCtrl, 1, 6, 0);
+ }
+
+ if (changePeCtrl) {
+ GX_WRITE_RAS_REG(tempPeCtrl);
+ }
+
+ GX_WRITE_RAS_REG(__GXData->cpTexSrc);
+ GX_WRITE_RAS_REG(__GXData->cpTexSize);
+ GX_WRITE_RAS_REG(__GXData->cpTexStride);
+
+ phyAddr = (u32)dest & 0x3FFFFFFF;
+ reg = 0;
+ SET_REG_FIELD(2240, reg, 24, 0, phyAddr >> 5);
+ SET_REG_FIELD(2240, reg, 8, 24, 0x4B);
+ GX_WRITE_RAS_REG(reg);
+
+ SET_REG_FIELD(2244, __GXData->cpTex, 1, 11, clear);
+ SET_REG_FIELD(2244, __GXData->cpTex, 1, 14, 0);
+ SET_REG_FIELD(2244, __GXData->cpTex, 8, 24, 0x52);
+ GX_WRITE_RAS_REG(__GXData->cpTex);
+
+ if (clear) {
+ GX_WRITE_RAS_REG(__GXData->zmode);
+ GX_WRITE_RAS_REG(__GXData->cmode0);
+ }
+
+ if (changePeCtrl) {
+ GX_WRITE_RAS_REG(__GXData->peCtrl);
+ }
+
+ __GXData->bpSentNot = 0;
+}
+
+void GXClearBoundingBox(void) {
+ u32 reg;
+
+ CHECK_GXBEGIN(2278, "GXClearBoundingBox");
+ reg = 0x550003FF;
+ GX_WRITE_RAS_REG(reg);
+ reg = 0x560003FF;
+ GX_WRITE_RAS_REG(reg);
+ __GXData->bpSentNot = 0;
+}
+
+void GXReadBoundingBox(u16* left, u16* top, u16* right, u16* bottom) {
+ *left = GX_GET_PE_REG(8);
+ *top = GX_GET_PE_REG(10);
+ *right = GX_GET_PE_REG(9);
+ *bottom = GX_GET_PE_REG(11);
+}
diff --git a/src/revolution/gx/GXGeometry.c b/src/revolution/gx/GXGeometry.c
new file mode 100644
index 0000000000..f585938fa1
--- /dev/null
+++ b/src/revolution/gx/GXGeometry.c
@@ -0,0 +1,267 @@
+#include <stddef.h>
+#include <revolution/gx.h>
+#include <revolution/os.h>
+
+#include "__gx.h"
+
+static void __GXSetTexGen(u32 tgState);
+static void __GXSetLightChan(u32 chState);
+static void __GXSetAmbMat(u32 amState);
+
+void __GXSetDirtyState(void) {
+ u32 dState = __GXData->dirtyState;
+
+ if (dState & 1) {
+ __GXSetSUTexRegs();
+ }
+ if (dState & 2) {
+ __GXUpdateBPMask();
+ }
+ if (dState & 4) {
+ __GXSetGenMode();
+ }
+ if (dState & 8) {
+ __GXSetVCD();
+ }
+ if (dState & 0x10) {
+ __GXSetVAT();
+ }
+ if (dState & 0x18) {
+ __GXCalculateVLim();
+ }
+
+ dState &= 0xFFFFFF00;
+
+ if (dState) {
+ u32 dStateLocal;
+
+ dStateLocal = dState & 0x00000F00;
+
+ if (dStateLocal) {
+ __GXSetAmbMat(dStateLocal);
+ }
+
+ dStateLocal = dState & (0x01000000|0x0000F000);
+
+ if (dStateLocal) {
+ __GXSetLightChan(dStateLocal);
+ }
+
+ dStateLocal = dState & (0x02000000|0x00FF0000);
+
+ if (dStateLocal) {
+ __GXSetTexGen(dStateLocal);
+ }
+
+ dStateLocal = dState & 0x04000000;
+ if (dStateLocal) {
+ __GXSetMatrixIndex(GX_VA_PNMTXIDX);
+ __GXSetMatrixIndex(GX_VA_TEX4MTXIDX);
+ }
+
+ dStateLocal = dState & 0x10000000;
+ if (dStateLocal) {
+ __GXSetViewport();
+ }
+
+ dStateLocal = dState & 0x8000000;
+ if (dStateLocal) {
+ __GXSetProjection();
+ }
+
+ __GXData->bpSentNot = GX_TRUE;
+ }
+
+ __GXData->dirtyState = 0;
+}
+
+static void __GXSetTexGen(u32 tgState) {
+ u32 d, i, tgIndx = 0x1040;
+ u32 dtgIndx = 0x1050;
+
+ if (tgState & 0x2000000) {
+ d = __GXData->genMode & 0xF;
+ GX_WRITE_XF_REG(0x103F, d, 0);
+ }
+
+ d = (tgState & 0xFF0000) >> 16;
+ i = 0;
+ while (d) {
+ if (d & 0x1) {
+ GX_WRITE_XF_REG(tgIndx, __GXData->texGenCtrl[i], 0);
+ GX_WRITE_XF_REG(dtgIndx, __GXData->texGenCtrl2[i], 0);
+ }
+
+ dtgIndx++;
+ tgIndx++;
+ i++;
+ d >>= 1;
+ }
+}
+
+static void __GXSetLightChan(u32 chState) {
+ u32 d, i, chIndx = 0x100E;
+
+ if (chState & 0x1000000) {
+ d = ((__GXData->genMode & 0x70) >> 4);
+ GX_WRITE_XF_REG(0x1009, d, 0);
+ }
+
+ d = (chState & 0xF000) >> 12;
+ i = 0;
+
+ while (d) {
+ if (d & 0x1) {
+ GX_WRITE_XF_REG(chIndx, __GXData->chanCtrl[i], 0);
+ }
+
+ i++;
+ chIndx++;
+ d >>= 1;
+ }
+}
+
+static void __GXSetAmbMat(u32 amState) {
+ if (amState & 0x100) {
+ GX_WRITE_XF_REG(0x100A, __GXData->ambColor[0], 0);
+ }
+
+ if (amState & 0x200) {
+ GX_WRITE_XF_REG(0x100B, __GXData->ambColor[1], 0);
+ }
+
+ if (amState & 0x400) {
+ GX_WRITE_XF_REG(0x100C, __GXData->matColor[0], 0);
+ }
+
+ if (amState & 0x800) {
+ GX_WRITE_XF_REG(0x100D, __GXData->matColor[1], 0);
+ }
+}
+
+void GXBegin(GXPrimitive type, GXVtxFmt vtxfmt, u16 nverts) {
+ ASSERTMSGLINE(368, vtxfmt < GX_MAX_VTXFMT, "GXBegin: Format Index is out of range");
+ ASSERTMSGLINE(369, !__GXinBegin, "GXBegin: called inside another GXBegin/GXEnd");
+
+ if (__GXData->dirtyState != 0) {
+ __GXSetDirtyState();
+ }
+
+#if DEBUG
+ if (!__GXData->inDispList) {
+ __GXVerifyState(vtxfmt);
+ }
+ __GXinBegin = 1;
+#endif
+
+ if (*(u32*)&__GXData->vNumNot == 0) { // checks both vNum and bpSentNot
+ __GXSendFlushPrim();
+ }
+ GX_WRITE_U8(vtxfmt | type);
+ GX_WRITE_U16(nverts);
+}
+
+void __GXSendFlushPrim(void) {
+ u32 i;
+ u32 numD = __GXData->vNum * __GXData->vLim;
+
+ GX_WRITE_U8(0x98);
+ GX_WRITE_U16(__GXData->vNum);
+ for (i = 0; i < numD; i += 4) {
+ GX_WRITE_U32(0);
+ }
+ __GXData->bpSentNot = 1;
+}
+
+void GXSetLineWidth(u8 width, GXTexOffset texOffsets) {
+ CHECK_GXBEGIN(447, "GXSetLineWidth");
+ SC_SU_LPSIZE_SET_LSIZE(448, __GXData->lpSize, width);
+ SC_SU_LPSIZE_SET_LTOFF(449, __GXData->lpSize, texOffsets);
+ GX_WRITE_RAS_REG(__GXData->lpSize);
+ __GXData->bpSentNot = 0;
+}
+
+void GXGetLineWidth(u8* width, GXTexOffset* texOffsets) {
+ ASSERTMSGLINE(463, width != NULL && texOffsets != NULL, "GXGet*: invalid null pointer");
+
+ *width = SU_LPSIZE_GET_LSIZE(__GXData->lpSize);
+ *texOffsets = SU_LPSIZE_GET_LTOFF(__GXData->lpSize);
+}
+
+void GXSetPointSize(u8 pointSize, GXTexOffset texOffsets) {
+ CHECK_GXBEGIN(491, "GXSetPointSize");
+ SC_SU_LPSIZE_SET_PSIZE(492, __GXData->lpSize, pointSize);
+ SC_SU_LPSIZE_SET_PTOFF(493, __GXData->lpSize, texOffsets);
+ GX_WRITE_RAS_REG(__GXData->lpSize);
+ __GXData->bpSentNot = 0;
+}
+
+void GXGetPointSize(u8* pointSize, GXTexOffset* texOffsets) {
+ ASSERTMSGLINE(507, pointSize != NULL && texOffsets != NULL, "GXGet*: invalid null pointer");
+
+ *pointSize = SU_LPSIZE_GET_PSIZE(__GXData->lpSize);
+ *texOffsets = SU_LPSIZE_GET_PTOFF(__GXData->lpSize);
+}
+
+void GXEnableTexOffsets(GXTexCoordID coord, u8 line_enable, u8 point_enable) {
+ CHECK_GXBEGIN(536, "GXEnableTexOffsets");
+
+ ASSERTMSGLINE(538, coord < GX_MAX_TEXCOORD, "GXEnableTexOffsets: Invalid coordinate Id");
+
+ SC_SU_TS0_SET_LF(540, __GXData->suTs0[coord], line_enable);
+ SC_SU_TS0_SET_PF(541, __GXData->suTs0[coord], point_enable);
+ GX_WRITE_RAS_REG(__GXData->suTs0[coord]);
+ __GXData->bpSentNot = 0;
+}
+
+void GXSetCullMode(GXCullMode mode) {
+ GXCullMode hwMode;
+
+ CHECK_GXBEGIN(564, "GXSetCullMode");
+#if DEBUG
+ switch (mode) {
+ case GX_CULL_FRONT: hwMode = GX_CULL_BACK; break;
+ case GX_CULL_BACK: hwMode = GX_CULL_FRONT; break;
+ default: hwMode = mode; break;
+ }
+#else
+ hwMode = (mode >> 1) & 1;
+ __rlwimi(hwMode, mode, 1, 30, 30);
+#endif
+
+ SC_GEN_MODE_SET_REJECT_EN(577, __GXData->genMode, hwMode);
+ __GXData->dirtyState |= 4;
+}
+
+void GXGetCullMode(GXCullMode* mode) {
+#if DEBUG
+ GXCullMode hwMode = GEN_MODE_GET_REJECT_EN(__GXData->genMode);
+
+ switch (hwMode) {
+ case GX_CULL_FRONT: *mode = GX_CULL_BACK; break;
+ case GX_CULL_BACK: *mode = GX_CULL_FRONT; break;
+ default: *mode = hwMode; break;
+ }
+#else
+ // fake match?
+ GXCullMode hwMode = __GXData->genMode;
+ *mode = ((hwMode >> 0xD) & 0x2) | (((((int)hwMode >> 0xE) & 0x2) >> 0x1));
+#endif
+}
+
+void GXSetCoPlanar(GXBool enable) {
+ u32 reg;
+
+ CHECK_GXBEGIN(620, "GXSetCoPlanar");
+
+ SC_GEN_MODE_SET_ZFREEZE(622, __GXData->genMode, enable);
+ reg = 0xFE080000;
+ GX_WRITE_RAS_REG(reg);
+ GX_WRITE_RAS_REG(__GXData->genMode);
+ __GXData->bpSentNot = 0;
+}
+
+void __GXSetGenMode(void) {
+ GX_WRITE_RAS_REG(__GXData->genMode);
+ __GXData->bpSentNot = 0;
+}
diff --git a/src/revolution/gx/GXInit.c b/src/revolution/gx/GXInit.c
new file mode 100644
index 0000000000..e7ecba2b8b
--- /dev/null
+++ b/src/revolution/gx/GXInit.c
@@ -0,0 +1,567 @@
+#include <string.h>
+
+#include <revolution/base/PPCArch.h>
+#include <revolution/gx.h>
+#include <revolution/os.h>
+#include <revolution/vi.h>
+
+#include "__gx.h"
+
+#ifdef SDK_AUG2010
+#define BUILD_DATE "Aug 23 2010"
+#if DEBUG
+#define BUILD_TIME "17:27:30"
+#else
+#define BUILD_TIME "17:33:06"
+#endif
+#elif SDK_SEP2006
+#define BUILD_DATE "Sep 21 2006"
+#define BUILD_TIME "14:32:13"
+#endif
+
+#ifdef SDK_AUG2010
+#if DEBUG
+const char* __GXVersion = "<< RVL_SDK - GX \tdebug build: "BUILD_DATE" "BUILD_TIME" (0x4302_145) >>";
+#else
+const char* __GXVersion = "<< RVL_SDK - GX \trelease build: "BUILD_DATE" "BUILD_TIME" (0x4302_145) >>";
+#endif
+#elif SDK_SEP2006
+const char* __GXVersion = "<< RVL_SDK - GX \trelease build: "BUILD_DATE" "BUILD_TIME" (0x4200_60422) >>";
+#endif
+
+static GXFifoObj FifoObj;
+
+static GXData gxData;
+GXData* const __GXData = &gxData;
+
+// these are supposed to be in-function static, but it messed up sbss order
+u32 shutdownFuncRegistered;
+u32 calledOnce;
+OSTime time;
+u32 peCount;
+
+volatile void* __memReg;
+volatile void* __peReg;
+volatile void* __cpReg;
+volatile void* __piReg;
+
+#if DEBUG
+GXBool __GXinBegin;
+#endif
+
+static u16 DefaultTexData[] ATTRIBUTE_ALIGN(32) = {
+ 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
+ 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
+};
+
+static GXVtxAttrFmtList GXDefaultVATList[] = {
+ {GX_VA_POS, GX_POS_XYZ, GX_F32, 0},
+ {GX_VA_NRM, GX_NRM_XYZ, GX_F32, 0},
+ {GX_VA_CLR0, GX_CLR_RGBA, GX_RGBA8, 0},
+ {GX_VA_CLR1, GX_CLR_RGBA, GX_RGBA8, 0},
+ {GX_VA_TEX0, GX_TEX_ST, GX_F32, 0},
+ {GX_VA_TEX1, GX_TEX_ST, GX_F32, 0},
+ {GX_VA_TEX2, GX_TEX_ST, GX_F32, 0},
+ {GX_VA_TEX3, GX_TEX_ST, GX_F32, 0},
+ {GX_VA_TEX4, GX_TEX_ST, GX_F32, 0},
+ {GX_VA_TEX5, GX_TEX_ST, GX_F32, 0},
+ {GX_VA_TEX6, GX_TEX_ST, GX_F32, 0},
+ {GX_VA_TEX7, GX_TEX_ST, GX_F32, 0},
+ {GX_VA_NULL, 0, 0, 0},
+};
+
+static f32 GXDefaultProjData[] = {1.0f, 0.0f, 1.0f, 0.0f, -1.0f, -2.0f, 0.0f};
+
+static u32 GXTexRegionAddrTable[] = {
+ 0x00000, 0x10000, 0x20000, 0x30000, 0x40000, 0x50000, 0x60000, 0x70000, 0x08000, 0x18000,
+ 0x28000, 0x38000, 0x48000, 0x58000, 0x68000, 0x78000, 0x00000, 0x90000, 0x20000, 0xB0000,
+ 0x40000, 0x98000, 0x60000, 0xB8000, 0x80000, 0x10000, 0xA0000, 0x30000, 0x88000, 0x50000,
+ 0xA8000, 0x70000, 0x00000, 0x90000, 0x20000, 0xB0000, 0x40000, 0x90000, 0x60000, 0xB0000,
+ 0x80000, 0x10000, 0xA0000, 0x30000, 0x80000, 0x50000, 0xA0000, 0x70000,
+};
+
+// prototypes
+static int __GXShutdown(int final, u32);
+
+static OSShutdownFunctionInfo GXShutdownFuncInfo = {__GXShutdown, 0x7F, NULL, NULL};
+
+asm BOOL IsWriteGatherBufferEmpty(void) {
+ sync
+ mfspr r3, WPAR
+ andi. r3, r3, 1
+}
+
+static void EnableWriteGatherPipe(void) {
+ u32 hid2 = PPCMfhid2();
+
+ PPCMtwpar(OSUncachedToPhysical((void*)GXFIFO_ADDR));
+ hid2 |= 0x40000000;
+ PPCMthid2(hid2);
+}
+
+static void DisableWriteGatherPipe(void) {
+ u32 hid2 = PPCMfhid2();
+
+ hid2 &= ~0x40000000;
+ PPCMthid2(hid2);
+}
+
+static GXTexRegion*__GXDefaultTexRegionCallback(const GXTexObj* t_obj, GXTexMapID id) {
+ GXTexFmt fmt;
+ u8 mm;
+
+ fmt = GXGetTexObjFmt(t_obj);
+ mm = GXGetTexObjMipMap(t_obj);
+ id = (GXTexMapID)(id % GX_MAX_TEXMAP);
+
+ switch (fmt) {
+ case GX_TF_RGBA8:
+ if (mm) {
+ return &__GXData->TexRegions2[id];
+ }
+ return &__GXData->TexRegions1[id];
+ case GX_TF_C4:
+ case GX_TF_C8:
+ case GX_TF_C14X2:
+ return &__GXData->TexRegions0[id];
+ default:
+ if (mm) {
+ return &__GXData->TexRegions1[id];
+ }
+ return &__GXData->TexRegions0[id];
+ }
+}
+
+static GXTlutRegion* __GXDefaultTlutRegionCallback(u32 idx) {
+ if (idx >= 20) {
+ return NULL;
+ }
+ return &__GXData->TlutRegions[idx];
+}
+
+#if DEBUG
+static void __GXDefaultVerifyCallback(GXWarningLevel level, u32 id, const char* msg) {
+ OSReport("Level %1d, Warning %3d: %s\n", level, id, msg);
+}
+#endif
+
+static int __GXShutdown(BOOL final, u32) {
+ u32 reg;
+ u32 peCountNew;
+ OSTime timeNew;
+
+ if (!final) {
+ if (!calledOnce) {
+ peCount = __GXReadMEMCounterU32(0x28, 0x27);
+ time = OSGetTime();
+ calledOnce = 1;
+ return 0;
+ }
+
+ timeNew = OSGetTime();
+ peCountNew = __GXReadMEMCounterU32(0x28, 0x27);
+
+ if (timeNew - time < 10) {
+ return 0;
+ }
+
+ if (peCountNew != peCount) {
+ peCount = peCountNew;
+ time = timeNew;
+ return 0;
+ }
+
+ } else {
+ GXSetBreakPtCallback(NULL);
+ GXSetDrawSyncCallback(NULL);
+ GXSetDrawDoneCallback(NULL);
+
+ GX_WRITE_U32(0);
+ GX_WRITE_U32(0);
+ GX_WRITE_U32(0);
+ GX_WRITE_U32(0);
+ GX_WRITE_U32(0);
+ GX_WRITE_U32(0);
+ GX_WRITE_U32(0);
+ GX_WRITE_U32(0);
+
+ PPCSync();
+
+ reg = CP_REG_ENABLE(0, 0, 0, 0, 0, 0);
+ GX_CP_REG_WRITE_U16(CP_ENABLE, (u16)reg);
+ reg = CP_REG_CLR(1, 1, 0);
+ GX_CP_REG_WRITE_U16(CP_CLR, (u16)reg);
+
+ __GXData->abtWaitPECopy = 1;
+
+ __GXAbort();
+ }
+
+ return 1;
+}
+
+#define SOME_SET_REG_MACRO(reg, size, shift, val) \
+ do { \
+ (reg) = (u32)__rlwimi((u32)(reg), (val), (shift), (32 - (shift) - (size)), (31 - (shift))); \
+ } while (0);
+
+void __GXInitRevisionBits(void) {
+ u32 i;
+
+ for (i = 0; i < 8; i++) {
+ s32 regAddr;
+ SC_CP_VAT_REG_A_SET_BYTEDEQUANT(0, __GXData->vatA[i], 1);
+ SC_CP_VAT_REG_B_SET_VCACHE_ENHANCE(0, __GXData->vatB[i], 1);
+ GX_WRITE_CP_STRM_REG(CP_VAT_B, (s32) i, __GXData->vatB[i]);
+ regAddr = i - 12;
+ }
+
+ {
+ u32 reg1 = 0;
+ u32 reg2 = 0;
+
+ SC_XF_ERROR_F_SET_CTEX_BUG_ENABLE(0, reg1, 1);
+ SC_XF_ERROR_F_SET_TFAN4_BUG_ENABLE(0, reg1, 1);
+ SC_XF_ERROR_F_SET_TFAN16_BUG_ENABLE(0, reg1, 1);
+ SC_XF_ERROR_F_SET_DUALTRAN_REG_ENABLE(0, reg1, 1);
+ SC_XF_ERROR_F_SET_BYPASS_BUG_ENABLE(0, reg1, 1);
+ SC_XF_ERROR_F_SET_FAST_MATRIX_ENABLE(0, reg1, 1);
+ GX_WRITE_XF_REG(0x1000, reg1, 0);
+ SC_XF_DUALTEXTRAN_F_SET_DUALTEXTRAN_ENABLE(0, reg2, 1);
+ GX_WRITE_XF_REG(0x1012, reg2, 0);
+#if DEBUG
+ __gxVerif->xfRegsDirty[0] = 0;
+#endif
+ }
+
+ {
+ u32 reg = 0;
+ SC_PE_CHICKEN_SET_PIWR(0, reg, 1);
+ SC_PE_CHICKEN_SET_TXCPY_FMT(0, reg, 1);
+ SC_PE_CHICKEN_SET_TXCPY_CCV(0, reg, 1);
+ SC_PE_CHICKEN_SET_BLENDOP(0, reg, 1);
+ SC_PE_CHICKEN_SET_RID(0, reg, 0x58);
+ GX_WRITE_RA_REG(reg);
+ }
+}
+
+GXFifoObj* GXInit(void* base, u32 size) {
+ u32 i;
+ u32 reg;
+ u32 freqBase;
+
+ OSRegisterVersion(__GXVersion);
+
+ __GXData->inDispList = FALSE;
+ __GXData->dlSaveContext = TRUE;
+ __GXData->abtWaitPECopy = 1;
+#if DEBUG
+ __GXinBegin = FALSE;
+#endif
+ __GXData->tcsManEnab = FALSE;
+ __GXData->tevTcEnab = FALSE;
+
+ GXSetMisc(GX_MT_XF_FLUSH, 0);
+
+ __piReg = OSPhysicalToUncached(0xC003000);
+ __cpReg = OSPhysicalToUncached(0xC000000);
+ __peReg = OSPhysicalToUncached(0xC001000);
+ __memReg = OSPhysicalToUncached(0xC004000);
+ __GXFifoInit();
+ GXInitFifoBase(&FifoObj, base, size);
+ GXSetCPUFifo(&FifoObj);
+ GXSetGPFifo(&FifoObj);
+
+ if (!shutdownFuncRegistered) {
+ OSRegisterShutdownFunction(&GXShutdownFuncInfo);
+ shutdownFuncRegistered = 1;
+ }
+
+ __GXPEInit();
+ EnableWriteGatherPipe();
+
+ __GXData->genMode = 0;
+ SC_GEN_MODE_SET_RID(0, __GXData->genMode, 0);
+ __GXData->bpMask = 255;
+ SC_BP_IMASK_SET_RID(0, __GXData->bpMask, 0xF);
+ __GXData->lpSize = 0;
+ SC_SU_LPSIZE_SET_RID(0, __GXData->lpSize, 0x22);
+
+ for (i = 0; i < 16; ++i) {
+ __GXData->tevc[i] = 0;
+ __GXData->teva[i] = 0;
+ __GXData->tref[i / 2] = 0;
+ __GXData->texmapId[i] = GX_TEXMAP_NULL;
+ SC_TEV_COLOR_ENV_SET_RID(970, __GXData->tevc[i], (0xC0 + i * 2));
+ SC_TEV_ALPHA_ENV_SET_RID(971, __GXData->teva[i], (0xC1 + i * 2));
+ SC_TEV_KSEL_SET_RID(973, __GXData->tevKsel[i/2], (0xF6 + i / 2));
+ SC_RAS1_TREF_SET_RID(975, __GXData->tref[i/2], (0x28 + i / 2));
+ }
+
+ __GXData->iref = 0;
+ SC_RAS1_IREF_SET_RID(0, __GXData->iref, 0x27);
+
+ for (i = 0; i < 8; ++i) {
+ __GXData->suTs0[i] = 0;
+ __GXData->suTs1[i] = 0;
+ SC_SU_TS0_SET_RID(984, __GXData->suTs0[i], 0x30 + i * 2);
+ SC_SU_TS1_SET_RID(985, __GXData->suTs1[i], 0x31 + i * 2);
+ }
+
+ SC_SU_SCIS0_SET_RID(0, __GXData->suScis0, 0x20);
+ SC_SU_SCIS1_SET_RID(0, __GXData->suScis1, 0x21);
+ SC_PE_CMODE0_SET_RID(0, __GXData->cmode0, 0x41);
+ SC_PE_CMODE1_SET_RID(0, __GXData->cmode1, 0x42);
+ SC_PE_ZMODE_SET_RID(0, __GXData->zmode, 0x40);
+ SC_PE_CONTROL_SET_RID(0, __GXData->peCtrl, 0x43);
+ SC_PE_COPY_CMD_SET_GAMMA(0, __GXData->cpTex, 0);
+
+ __GXData->zScale = (f32)(0xFFFFFF + 1);
+ __GXData->zOffset = 0.0f;
+ __GXData->dirtyState = 0;
+ __GXData->dirtyVAT = FALSE;
+
+#if DEBUG
+ __gxVerif->verifyLevel = GX_WARN_NONE;
+ GXSetVerifyCallback((GXVerifyCallback)__GXDefaultVerifyCallback);
+ for (i = 0; i < 256; i++) {
+ SET_REG_FIELD(0, __gxVerif->rasRegs[i], 8, 24, 0xFF);
+ }
+ memset(__gxVerif->xfRegsDirty, 0, 0x50);
+ memset(__gxVerif->xfMtxDirty, 0, 0x100);
+ memset(__gxVerif->xfNrmDirty, 0, 0x60);
+ memset(__gxVerif->xfLightDirty, 0, 0x80);
+#endif
+
+ freqBase = __OSBusClock / 500;
+ __GXFlushTextureState();
+ reg = (freqBase >> 11) | 0x400 | 0x69000000;
+ GX_WRITE_RAS_REG(reg);
+
+ __GXFlushTextureState();
+ reg = (freqBase / 0x1080) | 0x200 | 0x46000000;
+ GX_WRITE_RAS_REG(reg);
+
+ __GXInitRevisionBits();
+
+ for (i = 0; i < 8; i++) {
+ GXInitTexCacheRegion(&__GXData->TexRegions0[i], GX_FALSE, GXTexRegionAddrTable[i],
+ GX_TEXCACHE_32K, GXTexRegionAddrTable[i + 8], GX_TEXCACHE_32K);
+ GXInitTexCacheRegion(&__GXData->TexRegions1[i], GX_FALSE, GXTexRegionAddrTable[i + 16],
+ GX_TEXCACHE_32K, GXTexRegionAddrTable[i + 24], GX_TEXCACHE_32K);
+ GXInitTexCacheRegion(&__GXData->TexRegions2[i], GX_TRUE, GXTexRegionAddrTable[i + 32],
+ GX_TEXCACHE_32K, GXTexRegionAddrTable[i + 40], GX_TEXCACHE_32K);
+ }
+
+ for (i = 0; i < 16; i++) {
+ GXInitTlutRegion(&__GXData->TlutRegions[i], 0xC0000 + 0x2000 * i, GX_TLUT_256);
+ }
+
+ for (i = 0; i < 4; i++) {
+ GXInitTlutRegion(&__GXData->TlutRegions[i + 16], 0xE0000 + 0x8000 * i, GX_TLUT_1K);
+ }
+
+ {
+ u32 reg = CP_REG_MEMPERFSEL(MEMPERF_ZERO);
+#if DEBUG
+ s32 regAddr;
+#endif
+ GX_CP_REG_WRITE_U16(CP_MEMPERF_SEL, reg);
+
+ SC_CP_STAT_SEL_REG_SET_STALLPERF_SEL(0, __GXData->perfSel, STALLPERF_ZERO);
+ GX_WRITE_CP_STRM_REG(CP_STAT_SEL, 0, __GXData->perfSel);
+#if DEBUG
+ regAddr = -12;
+#endif
+
+ reg = XF_PERF0_F(0, 0, 0, 0);
+ GX_WRITE_XF_REG(0x1006, reg, 0);
+ reg = SU_PERF(0, 0, 0, 0, 0, 0, 0, 0, 0, 0x23);
+ GX_WRITE_RA_REG(reg);
+ reg = RAS_PERF(0, 0, 0, 0, 0, 0x24);
+ GX_WRITE_RA_REG(reg);
+ reg = TX_PERFMODE(0, 0, 0, 0, 0x67);
+ GX_WRITE_RA_REG(reg);
+ }
+
+ __GXSetIndirectMask(0);
+ __GXSetTmemConfig(2);
+ __GXInitGX();
+
+ return &FifoObj;
+}
+
+void __GXInitGX(void) {
+ GXRenderModeObj* rmode;
+ GXTexObj tex_obj;
+ float identity_mtx[3][4];
+ GXColor clear = {64, 64, 64, 255};
+ GXColor black = {0, 0, 0, 0};
+ GXColor white = {255, 255, 255, 255};
+ u32 i;
+
+ switch (VIGetTvFormat()) {
+ case VI_NTSC: rmode = &GXNtsc480IntDf; break;
+ case VI_PAL: rmode = &GXPal528IntDf; break;
+ case VI_EURGB60: rmode = &GXEurgb60Hz480IntDf; break;
+ case VI_MPAL: rmode = &GXMpal480IntDf; break;
+ default:
+ ASSERTMSGLINE(1177, 0, "GXInit: invalid TV format");
+ rmode = &GXNtsc480IntDf;
+ break;
+ }
+
+ GXSetCopyClear(clear, 0xFFFFFF);
+ GXSetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, 0x3C);
+ GXSetTexCoordGen(GX_TEXCOORD1, GX_TG_MTX2x4, GX_TG_TEX1, 0x3C);
+ GXSetTexCoordGen(GX_TEXCOORD2, GX_TG_MTX2x4, GX_TG_TEX2, 0x3C);
+ GXSetTexCoordGen(GX_TEXCOORD3, GX_TG_MTX2x4, GX_TG_TEX3, 0x3C);
+ GXSetTexCoordGen(GX_TEXCOORD4, GX_TG_MTX2x4, GX_TG_TEX4, 0x3C);
+ GXSetTexCoordGen(GX_TEXCOORD5, GX_TG_MTX2x4, GX_TG_TEX5, 0x3C);
+ GXSetTexCoordGen(GX_TEXCOORD6, GX_TG_MTX2x4, GX_TG_TEX6, 0x3C);
+ GXSetTexCoordGen(GX_TEXCOORD7, GX_TG_MTX2x4, GX_TG_TEX7, 0x3C);
+ GXSetNumTexGens(1);
+ GXClearVtxDesc();
+ GXInvalidateVtxCache();
+
+ for (i = GX_VA_POS; i <= GX_LIGHT_ARRAY; i++) {
+ GXSetArray(i, __GXData, 0);
+ }
+
+ for (i = GX_VTXFMT0; i < GX_MAX_VTXFMT; i++) {
+ GXSetVtxAttrFmtv(i, GXDefaultVATList);
+ }
+
+ GXSetLineWidth(6, GX_TO_ZERO);
+ GXSetPointSize(6, GX_TO_ZERO);
+ GXEnableTexOffsets(GX_TEXCOORD0, 0, 0);
+ GXEnableTexOffsets(GX_TEXCOORD1, 0, 0);
+ GXEnableTexOffsets(GX_TEXCOORD2, 0, 0);
+ GXEnableTexOffsets(GX_TEXCOORD3, 0, 0);
+ GXEnableTexOffsets(GX_TEXCOORD4, 0, 0);
+ GXEnableTexOffsets(GX_TEXCOORD5, 0, 0);
+ GXEnableTexOffsets(GX_TEXCOORD6, 0, 0);
+ GXEnableTexOffsets(GX_TEXCOORD7, 0, 0);
+ identity_mtx[0][0] = 1.0f;
+ identity_mtx[0][1] = 0.0f;
+ identity_mtx[0][2] = 0.0f;
+ identity_mtx[0][3] = 0.0f;
+ identity_mtx[1][0] = 0.0f;
+ identity_mtx[1][1] = 1.0f;
+ identity_mtx[1][2] = 0.0f;
+ identity_mtx[1][3] = 0.0f;
+ identity_mtx[2][0] = 0.0f;
+ identity_mtx[2][1] = 0.0f;
+ identity_mtx[2][2] = 1.0f;
+ identity_mtx[2][3] = 0.0f;
+ GXLoadPosMtxImm(identity_mtx, GX_PNMTX0);
+ GXLoadNrmMtxImm(identity_mtx, GX_PNMTX0);
+ GXSetCurrentMtx(GX_PNMTX0);
+ GXLoadTexMtxImm(identity_mtx, GX_IDENTITY, GX_MTX3x4);
+ GXLoadTexMtxImm(identity_mtx, GX_PTIDENTITY, GX_MTX3x4);
+ GXSetViewport(0.0f, 0.0f, rmode->fbWidth, rmode->xfbHeight, 0.0f, 1.0f);
+ GXSetProjectionv(GXDefaultProjData);
+ __GXSetGenMode();
+ GXSetCoPlanar(GX_DISABLE);
+ GXSetCullMode(GX_CULL_BACK);
+ GXSetClipMode(GX_CLIP_ENABLE);
+ GXSetScissor(0, 0, rmode->fbWidth, rmode->efbHeight);
+ GXSetScissorBoxOffset(0, 0);
+ GXSetNumChans(0);
+ GXSetChanCtrl(GX_COLOR0A0, GX_DISABLE, GX_SRC_REG, GX_SRC_VTX, GX_LIGHT_NULL, GX_DF_NONE, GX_AF_NONE);
+ GXSetChanAmbColor(GX_COLOR0A0, black);
+ GXSetChanMatColor(GX_COLOR0A0, white);
+ GXSetChanCtrl(GX_COLOR1A1, GX_DISABLE, GX_SRC_REG, GX_SRC_VTX, GX_LIGHT_NULL, GX_DF_NONE, GX_AF_NONE);
+ GXSetChanAmbColor(GX_COLOR1A1, black);
+ GXSetChanMatColor(GX_COLOR1A1, white);
+ GXInvalidateTexAll();
+ GXSetTexRegionCallback((GXTexRegionCallback)__GXDefaultTexRegionCallback);
+ GXSetTlutRegionCallback(__GXDefaultTlutRegionCallback);
+
+ GXInitTexObj(&tex_obj, DefaultTexData, 4, 4, GX_TF_IA8, GX_CLAMP, GX_CLAMP, 0);
+ GXLoadTexObj(&tex_obj, GX_TEXMAP0);
+ GXLoadTexObj(&tex_obj, GX_TEXMAP1);
+ GXLoadTexObj(&tex_obj, GX_TEXMAP2);
+ GXLoadTexObj(&tex_obj, GX_TEXMAP3);
+ GXLoadTexObj(&tex_obj, GX_TEXMAP4);
+ GXLoadTexObj(&tex_obj, GX_TEXMAP5);
+ GXLoadTexObj(&tex_obj, GX_TEXMAP6);
+ GXLoadTexObj(&tex_obj, GX_TEXMAP7);
+
+ GXSetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR0A0);
+ GXSetTevOrder(GX_TEVSTAGE1, GX_TEXCOORD1, GX_TEXMAP1, GX_COLOR0A0);
+ GXSetTevOrder(GX_TEVSTAGE2, GX_TEXCOORD2, GX_TEXMAP2, GX_COLOR0A0);
+ GXSetTevOrder(GX_TEVSTAGE3, GX_TEXCOORD3, GX_TEXMAP3, GX_COLOR0A0);
+ GXSetTevOrder(GX_TEVSTAGE4, GX_TEXCOORD4, GX_TEXMAP4, GX_COLOR0A0);
+ GXSetTevOrder(GX_TEVSTAGE5, GX_TEXCOORD5, GX_TEXMAP5, GX_COLOR0A0);
+ GXSetTevOrder(GX_TEVSTAGE6, GX_TEXCOORD6, GX_TEXMAP6, GX_COLOR0A0);
+ GXSetTevOrder(GX_TEVSTAGE7, GX_TEXCOORD7, GX_TEXMAP7, GX_COLOR0A0);
+ GXSetTevOrder(GX_TEVSTAGE8, GX_TEXCOORD_NULL, GX_TEXMAP_NULL, GX_COLOR_NULL);
+ GXSetTevOrder(GX_TEVSTAGE9, GX_TEXCOORD_NULL, GX_TEXMAP_NULL, GX_COLOR_NULL);
+ GXSetTevOrder(GX_TEVSTAGE10, GX_TEXCOORD_NULL, GX_TEXMAP_NULL, GX_COLOR_NULL);
+ GXSetTevOrder(GX_TEVSTAGE11, GX_TEXCOORD_NULL, GX_TEXMAP_NULL, GX_COLOR_NULL);
+ GXSetTevOrder(GX_TEVSTAGE12, GX_TEXCOORD_NULL, GX_TEXMAP_NULL, GX_COLOR_NULL);
+ GXSetTevOrder(GX_TEVSTAGE13, GX_TEXCOORD_NULL, GX_TEXMAP_NULL, GX_COLOR_NULL);
+ GXSetTevOrder(GX_TEVSTAGE14, GX_TEXCOORD_NULL, GX_TEXMAP_NULL, GX_COLOR_NULL);
+ GXSetTevOrder(GX_TEVSTAGE15, GX_TEXCOORD_NULL, GX_TEXMAP_NULL, GX_COLOR_NULL);
+
+ GXSetNumTevStages(1);
+ GXSetTevOp(GX_TEVSTAGE0, GX_REPLACE);
+ GXSetAlphaCompare(GX_ALWAYS, 0, GX_AOP_AND, GX_ALWAYS, 0);
+ GXSetZTexture(GX_ZT_DISABLE, GX_TF_Z8, 0);
+
+ for (i = GX_TEVSTAGE0; i < GX_MAX_TEVSTAGE; i++) {
+ GXSetTevKColorSel((GXTevStageID)i, GX_TEV_KCSEL_1_4);
+ GXSetTevKAlphaSel((GXTevStageID)i, GX_TEV_KASEL_1);
+ GXSetTevSwapMode((GXTevStageID)i, GX_TEV_SWAP0, GX_TEV_SWAP0);
+ }
+
+ GXSetTevSwapModeTable(GX_TEV_SWAP0, GX_CH_RED, GX_CH_GREEN, GX_CH_BLUE, GX_CH_ALPHA);
+ GXSetTevSwapModeTable(GX_TEV_SWAP1, GX_CH_RED, GX_CH_RED, GX_CH_RED, GX_CH_ALPHA);
+ GXSetTevSwapModeTable(GX_TEV_SWAP2, GX_CH_GREEN, GX_CH_GREEN, GX_CH_GREEN, GX_CH_ALPHA);
+ GXSetTevSwapModeTable(GX_TEV_SWAP3, GX_CH_BLUE, GX_CH_BLUE, GX_CH_BLUE, GX_CH_ALPHA);
+
+ for (i = GX_TEVSTAGE0; i < GX_MAX_TEVSTAGE; i++)
+ GXSetTevDirect((GXTevStageID)i);
+
+ GXSetNumIndStages(0);
+ GXSetIndTexCoordScale(GX_INDTEXSTAGE0, GX_ITS_1, GX_ITS_1);
+ GXSetIndTexCoordScale(GX_INDTEXSTAGE1, GX_ITS_1, GX_ITS_1);
+ GXSetIndTexCoordScale(GX_INDTEXSTAGE2, GX_ITS_1, GX_ITS_1);
+ GXSetIndTexCoordScale(GX_INDTEXSTAGE3, GX_ITS_1, GX_ITS_1);
+
+ GXSetFog(GX_FOG_NONE, 0.0f, 1.0f, 0.1f, 1.0f, black);
+ GXSetFogRangeAdj(GX_DISABLE, 0, NULL);
+ GXSetBlendMode(GX_BM_NONE, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_CLEAR);
+ GXSetColorUpdate(GX_ENABLE);
+ GXSetAlphaUpdate(GX_ENABLE);
+ GXSetZMode(GX_TRUE, GX_LEQUAL, GX_TRUE);
+ GXSetZCompLoc(GX_TRUE);
+ GXSetDither(GX_ENABLE);
+ GXSetDstAlpha(GX_DISABLE, 0);
+ GXSetPixelFmt(GX_PF_RGB8_Z24, GX_ZC_LINEAR);
+ GXSetFieldMask(GX_ENABLE, GX_ENABLE);
+ GXSetFieldMode((u8)rmode->field_rendering,
+ ((rmode->viHeight == 2 * rmode->xfbHeight) ? GX_ENABLE : GX_DISABLE));
+
+ GXSetDispCopySrc(0, 0, rmode->fbWidth, rmode->efbHeight);
+ GXSetDispCopyDst(rmode->fbWidth, rmode->efbHeight);
+ GXSetDispCopyYScale((f32)(rmode->xfbHeight) / (f32)(rmode->efbHeight));
+ GXSetCopyClamp((GXFBClamp)(GX_CLAMP_TOP | GX_CLAMP_BOTTOM));
+ GXSetCopyFilter(rmode->aa, rmode->sample_pattern, GX_TRUE, rmode->vfilter);
+ GXSetDispCopyGamma(GX_GM_1_0);
+ GXSetDispCopyFrame2Field(GX_COPY_PROGRESSIVE);
+ GXClearBoundingBox();
+
+ GXPokeColorUpdate(GX_TRUE);
+ GXPokeAlphaUpdate(GX_TRUE);
+ GXPokeDither(GX_FALSE);
+ GXPokeBlendMode(GX_BM_NONE, GX_BL_ZERO, GX_BL_ONE, GX_LO_SET);
+ GXPokeAlphaMode(GX_ALWAYS, 0);
+ GXPokeAlphaRead(GX_READ_FF);
+ GXPokeDstAlpha(GX_DISABLE, 0);
+ GXPokeZMode(GX_TRUE, GX_ALWAYS, GX_TRUE);
+
+ GXSetGPMetric(GX_PERF0_NONE, GX_PERF1_NONE);
+ GXClearGPMetric();
+}
diff --git a/src/revolution/gx/GXLight.c b/src/revolution/gx/GXLight.c
new file mode 100644
index 0000000000..b371eb1fae
--- /dev/null
+++ b/src/revolution/gx/GXLight.c
@@ -0,0 +1,571 @@
+#include <math.h>
+#include <revolution/gx.h>
+#include <revolution/os.h>
+
+#include "__gx.h"
+
+// GXLightObj private data
+typedef struct {
+ u32 reserved[3];
+ u32 Color;
+ f32 a[3];
+ f32 k[3];
+ f32 lpos[3];
+ f32 ldir[3];
+} __GXLightObjInt_struct;
+
+void GXInitLightAttn(GXLightObj* lt_obj, f32 a0, f32 a1, f32 a2, f32 k0, f32 k1, f32 k2) {
+ __GXLightObjInt_struct* obj;
+
+ ASSERTMSGLINE(135, lt_obj != NULL, "Light Object Pointer is null");
+ obj = (__GXLightObjInt_struct*)lt_obj;
+ CHECK_GXBEGIN(136, "GXInitLightAttn");
+ obj->a[0] = a0;
+ obj->a[1] = a1;
+ obj->a[2] = a2;
+ obj->k[0] = k0;
+ obj->k[1] = k1;
+ obj->k[2] = k2;
+}
+
+void GXInitLightAttnA(GXLightObj* lt_obj, f32 a0, f32 a1, f32 a2) {
+ __GXLightObjInt_struct* obj;
+
+ ASSERTMSGLINE(143, lt_obj != NULL, "Light Object Pointer is null");
+ obj = (__GXLightObjInt_struct*)lt_obj;
+ CHECK_GXBEGIN(144, "GXInitLightAttnA");
+ obj->a[0] = a0;
+ obj->a[1] = a1;
+ obj->a[2] = a2;
+}
+
+void GXGetLightAttnA(const GXLightObj* lt_obj, f32* a0, f32* a1, f32* a2) {
+ __GXLightObjInt_struct* obj;
+
+ ASSERTMSGLINE(153, lt_obj != NULL, "Light Object Pointer is null");
+ obj = (__GXLightObjInt_struct*)lt_obj;
+ CHECK_GXBEGIN(154, "GXGetLightAttnA");
+ *a0 = obj->a[0];
+ *a1 = obj->a[1];
+ *a2 = obj->a[2];
+}
+
+void GXInitLightAttnK(GXLightObj* lt_obj, f32 k0, f32 k1, f32 k2) {
+ __GXLightObjInt_struct* obj;
+
+ ASSERTMSGLINE(163, lt_obj != NULL, "Light Object Pointer is null");
+ obj = (__GXLightObjInt_struct*)lt_obj;
+ CHECK_GXBEGIN(164, "GXInitLightAttnK");
+ obj->k[0] = k0;
+ obj->k[1] = k1;
+ obj->k[2] = k2;
+}
+
+void GXGetLightAttnK(const GXLightObj* lt_obj, f32* k0, f32* k1, f32* k2) {
+ __GXLightObjInt_struct* obj;
+
+ ASSERTMSGLINE(173, lt_obj != NULL, "Light Object Pointer is null");
+ obj = (__GXLightObjInt_struct*)lt_obj;
+ CHECK_GXBEGIN(174, "GXGetLightAttnK");
+ *k0 = obj->k[0];
+ *k1 = obj->k[1];
+ *k2 = obj->k[2];
+}
+
+void GXInitLightSpot(GXLightObj* lt_obj, f32 cutoff, GXSpotFn spot_func) {
+ f32 a0, a1, a2;
+ f32 r;
+ f32 d;
+ f32 cr;
+ __GXLightObjInt_struct* obj;
+
+ ASSERTMSGLINE(204, lt_obj != NULL, "Light Object Pointer is null");
+ obj = (__GXLightObjInt_struct*)lt_obj;
+ CHECK_GXBEGIN(206, "GXInitLightSpot");
+
+ if (cutoff <= 0.0f || cutoff > 90.0f)
+ spot_func = GX_SP_OFF;
+
+ r = (3.1415927f * cutoff) / 180.0f;
+ cr = cosf(r);
+
+ switch (spot_func) {
+ case GX_SP_FLAT:
+ a0 = -1000.0f * cr;
+ a1 = 1000.0f;
+ a2 = 0.0f;
+ break;
+ case GX_SP_COS:
+ a1 = 1.0f / (1.0f - cr);
+ a0 = -cr * a1;
+ a2 = 0.0f;
+ break;
+ case GX_SP_COS2:
+ a2 = 1.0f / (1.0f - cr);
+ a0 = 0.0f;
+ a1 = -cr * a2;
+ break;
+ case GX_SP_SHARP:
+ d = 1.0f / ((1.0f - cr) * (1.0f - cr));
+ a0 = (cr * (cr - 2.0f)) * d;
+ a1 = 2.0f * d;
+ a2 = -d;
+ break;
+ case GX_SP_RING1:
+ d = 1.0f / ((1.0f - cr) * (1.0f - cr));
+ a2 = -4.0f * d;
+ a0 = a2 * cr;
+ a1 = (4.0f * (1.0f + cr)) * d;
+ break;
+ case GX_SP_RING2:
+ d = 1.0f / ((1.0f - cr) * (1.0f - cr));
+ a0 = 1.0f - ((2.0f * cr * cr) * d);
+ a1 = (4.0f * cr) * d;
+ a2 = -2.0f * d;
+ break;
+ case GX_SP_OFF:
+ default:
+ a0 = 1.0f;
+ a1 = 0.0f;
+ a2 = 0.0f;
+ break;
+ }
+ obj->a[0] = a0;
+ obj->a[1] = a1;
+ obj->a[2] = a2;
+}
+
+void GXInitLightDistAttn(GXLightObj* lt_obj, f32 ref_dist, f32 ref_br, GXDistAttnFn dist_func) {
+ f32 k0, k1, k2;
+ __GXLightObjInt_struct* obj;
+
+ ASSERTMSGLINE(279, lt_obj != NULL, "Light Object Pointer is null");
+ obj = (__GXLightObjInt_struct*)lt_obj;
+ CHECK_GXBEGIN(281, "GXInitLightDistAttn");
+
+ if (ref_dist < 0.0f)
+ dist_func = GX_DA_OFF;
+ if (ref_br <= 0.0f || ref_br >= 1.0f)
+ dist_func = GX_DA_OFF;
+
+ switch (dist_func) {
+ case GX_DA_GENTLE:
+ k0 = 1.0f;
+ k1 = (1.0f - ref_br) / (ref_br * ref_dist);
+ k2 = 0.0f;
+ break;
+ case GX_DA_MEDIUM:
+ k0 = 1.0f;
+ k1 = (0.5f * (1.0f - ref_br)) / (ref_br * ref_dist);
+ k2 = (0.5f * (1.0f - ref_br)) / (ref_br * ref_dist * ref_dist);
+ break;
+ case GX_DA_STEEP:
+ k0 = 1.0f;
+ k1 = 0.0f;
+ k2 = (1.0f - ref_br) / (ref_br * ref_dist * ref_dist);
+ break;
+ case GX_DA_OFF:
+ default:
+ k0 = 1.0f;
+ k1 = 0.0f;
+ k2 = 0.0f;
+ break;
+ }
+
+ obj->k[0] = k0;
+ obj->k[1] = k1;
+ obj->k[2] = k2;
+}
+
+void GXInitLightPos(GXLightObj* lt_obj, f32 x, f32 y, f32 z) {
+ __GXLightObjInt_struct* obj;
+
+ ASSERTMSGLINE(334, lt_obj != NULL, "Light Object Pointer is null");
+ obj = (__GXLightObjInt_struct*)lt_obj;
+ CHECK_GXBEGIN(336, "GXInitLightPos");
+
+ obj->lpos[0] = x;
+ obj->lpos[1] = y;
+ obj->lpos[2] = z;
+}
+
+void GXGetLightPos(const GXLightObj* lt_obj, f32* x, f32* y, f32* z) {
+ __GXLightObjInt_struct* obj;
+
+ ASSERTMSGLINE(339, lt_obj != NULL, "Light Object Pointer is null");
+ obj = (__GXLightObjInt_struct*)lt_obj;
+ CHECK_GXBEGIN(341, "GXGetLightPos");
+
+ *x = obj->lpos[0];
+ *y = obj->lpos[1];
+ *z = obj->lpos[2];
+}
+
+void GXInitLightDir(GXLightObj* lt_obj, f32 nx, f32 ny, f32 nz) {
+ __GXLightObjInt_struct* obj;
+
+ ASSERTMSGLINE(366, lt_obj != NULL, "Light Object Pointer is null");
+ obj = (__GXLightObjInt_struct*)lt_obj;
+
+ obj->ldir[0] = -nx;
+ obj->ldir[1] = -ny;
+ obj->ldir[2] = -nz;
+}
+
+void GXGetLightDir(const GXLightObj* lt_obj, f32* nx, f32* ny, f32* nz) {
+ __GXLightObjInt_struct* obj;
+
+ ASSERTMSGLINE(372, lt_obj != NULL, "Light Object Pointer is null");
+ obj = (__GXLightObjInt_struct*)lt_obj;
+
+ *nx = -obj->ldir[0];
+ *ny = -obj->ldir[1];
+ *nz = -obj->ldir[2];
+}
+
+void GXInitSpecularDir(GXLightObj* lt_obj, f32 nx, f32 ny, f32 nz) {
+ f32 mag;
+ f32 vx;
+ f32 vy;
+ f32 vz;
+ __GXLightObjInt_struct* obj;
+
+ ASSERTMSGLINE(398, lt_obj != NULL, "Light Object Pointer is null");
+ obj = (__GXLightObjInt_struct*)lt_obj;
+ CHECK_GXBEGIN(399, "GXInitSpecularDir");
+
+ vx = -nx;
+ vy = -ny;
+ vz = -nz + 1.0f;
+
+ mag = (vx * vx) + (vy * vy) + (vz * vz);
+ if (mag != 0.0f) {
+ mag = 1.0f / sqrtf(mag);
+ }
+
+ obj->ldir[0] = vx * mag;
+ obj->ldir[1] = vy * mag;
+ obj->ldir[2] = vz * mag;
+ obj->lpos[0] = nx * -1000000000000000000.0f;
+ obj->lpos[1] = ny * -1000000000000000000.0f;
+ obj->lpos[2] = nz * -1000000000000000000.0f;
+}
+
+void GXInitSpecularDirHA(GXLightObj* lt_obj, f32 nx, f32 ny, f32 nz, f32 hx, f32 hy, f32 hz) {
+ __GXLightObjInt_struct* obj;
+
+ ASSERTMSGLINE(436, lt_obj != NULL, "Light Object Pointer is null");
+ obj = (__GXLightObjInt_struct*)lt_obj;
+ CHECK_GXBEGIN(437, "GXInitSpecularHA");
+
+ obj->ldir[0] = hx;
+ obj->ldir[1] = hy;
+ obj->ldir[2] = hz;
+ obj->lpos[0] = nx * -1000000000000000000.0f;
+ obj->lpos[1] = ny * -1000000000000000000.0f;
+ obj->lpos[2] = nz * -1000000000000000000.0f;
+}
+
+void GXInitLightColor(GXLightObj* lt_obj, GXColor color) {
+ __GXLightObjInt_struct* obj;
+
+ ASSERTMSGLINE(468, lt_obj != NULL, "Light Object Pointer is null");
+ obj = (__GXLightObjInt_struct*)lt_obj;
+ CHECK_GXBEGIN(469, "GXInitLightColor");
+
+ obj->Color = *(u32*)&color;
+}
+
+void GXGetLightColor(const GXLightObj* lt_obj, GXColor* color) {
+ __GXLightObjInt_struct* obj;
+
+ ASSERTMSGLINE(476, lt_obj != NULL, "Light Object Pointer is null");
+ obj = (__GXLightObjInt_struct*)lt_obj;
+ CHECK_GXBEGIN(477, "GXGetLightColor");
+
+ *(u32*)color = *(u32*)&obj->Color;
+}
+
+#if DEBUG
+#define WRITE_SOME_LIGHT_REG1(val, addr) \
+do { \
+ u32 xfData = val; \
+ GX_WRITE_U32(val); \
+ VERIF_MTXLIGHT(addr, xfData); \
+} while (0)
+
+#define WRITE_SOME_LIGHT_REG2(val, addr) \
+do { \
+ f32 xfData = val; \
+ GX_WRITE_F32(val); \
+ VERIF_MTXLIGHT(addr, *(u32 *)&xfData); \
+} while (0)
+#else
+#define WRITE_SOME_LIGHT_REG1(val, addr) GX_WRITE_U32(val)
+#define WRITE_SOME_LIGHT_REG2(val, addr) GX_WRITE_F32(val)
+#endif
+
+static inline u32 ConvLightID2Num(GXLightID id) {
+ switch (id) {
+ case GX_LIGHT0: return 0;
+ case GX_LIGHT1: return 1;
+ case GX_LIGHT2: return 2;
+ case GX_LIGHT3: return 3;
+ case GX_LIGHT4: return 4;
+ case GX_LIGHT5: return 5;
+ case GX_LIGHT6: return 6;
+ case GX_LIGHT7: return 7;
+ default: return 8;
+ }
+}
+
+static inline void PushLight(const __REGISTER GXLightObj* lt_obj, __REGISTER void* dest) {
+ __REGISTER u32 zero, color;
+ __REGISTER f32 a0_a1, a2_k0, k1_k2;
+ __REGISTER f32 px_py, pz_dx, dy_dz;
+#ifdef __MWERKS__ // clang-format off
+ asm {
+ lwz color, 12(lt_obj)
+ xor zero, zero, zero
+ psq_l a0_a1, 16(lt_obj), 0, 0
+ psq_l a2_k0, 24(lt_obj), 0, 0
+ psq_l k1_k2, 32(lt_obj), 0, 0
+ psq_l px_py, 40(lt_obj), 0, 0
+ psq_l pz_dx, 48(lt_obj), 0, 0
+ psq_l dy_dz, 56(lt_obj), 0, 0
+
+ stw zero, 0(dest)
+ stw zero, 0(dest)
+ stw zero, 0(dest)
+ stw color, 0(dest)
+ psq_st a0_a1, 0(dest), 0, 0
+ psq_st a2_k0, 0(dest), 0, 0
+ psq_st k1_k2, 0(dest), 0, 0
+ psq_st px_py, 0(dest), 0, 0
+ psq_st pz_dx, 0(dest), 0, 0
+ psq_st dy_dz, 0(dest), 0, 0
+ }
+#endif // clang-format on
+}
+
+void GXLoadLightObjImm(const GXLightObj* lt_obj, GXLightID light) {
+ u32 addr;
+ u32 idx;
+ __GXLightObjInt_struct* obj;
+
+ ASSERTMSGLINE(579, lt_obj != NULL, "Light Object Pointer is null");
+ obj = (__GXLightObjInt_struct*)lt_obj;
+ CHECK_GXBEGIN(580, "GXLoadLightObjImm");
+
+#if DEBUG
+ idx = ConvLightID2Num(light);
+#else
+ idx = 31 - __cntlzw(light);
+#endif
+
+ ASSERTMSGLINE(586, idx < 8, "GXLoadLightObjImm: Invalid Light Id");
+ idx &= 7;
+
+ addr = idx * 0x10 + 0x600;
+ GX_WRITE_U8(0x10);
+ GX_WRITE_U32(addr | 0xF0000);
+
+#if DEBUG
+ WRITE_SOME_LIGHT_REG1(0, addr);
+ WRITE_SOME_LIGHT_REG1(0, addr + 1);
+ WRITE_SOME_LIGHT_REG1(0, addr + 2);
+ WRITE_SOME_LIGHT_REG1(obj->Color, addr + 3);
+ WRITE_SOME_LIGHT_REG2(obj->a[0], addr + 4);
+ WRITE_SOME_LIGHT_REG2(obj->a[1], addr + 5);
+ WRITE_SOME_LIGHT_REG2(obj->a[2], addr + 6);
+ WRITE_SOME_LIGHT_REG2(obj->k[0], addr + 7);
+ WRITE_SOME_LIGHT_REG2(obj->k[1], addr + 8);
+ WRITE_SOME_LIGHT_REG2(obj->k[2], addr + 9);
+ WRITE_SOME_LIGHT_REG2(obj->lpos[0], addr + 10);
+ WRITE_SOME_LIGHT_REG2(obj->lpos[1], addr + 11);
+ WRITE_SOME_LIGHT_REG2(obj->lpos[2], addr + 12);
+ WRITE_SOME_LIGHT_REG2(obj->ldir[0], addr + 13);
+ WRITE_SOME_LIGHT_REG2(obj->ldir[1], addr + 14);
+ WRITE_SOME_LIGHT_REG2(obj->ldir[2], addr + 15);
+#else
+ PushLight(lt_obj, (void*)GXFIFO_ADDR);
+#endif
+
+ __GXData->bpSentNot = 1;
+}
+
+void GXLoadLightObjIndx(u32 lt_obj_indx, GXLightID light) {
+ u32 reg;
+ u32 addr;
+ u32 idx;
+
+ CHECK_GXBEGIN(624, "GXLoadLightObjIndx");
+
+#if DEBUG
+ idx = ConvLightID2Num(light);
+#else
+ idx = 31 - __cntlzw(light);
+#endif
+
+ ASSERTMSGLINE(627, idx < 8, "GXLoadLightObjIndx: Invalid Light Id");
+ idx &= 7;
+
+ addr = idx * 0x10 + 0x600;
+ reg = 0;
+ SET_REG_FIELD(632, reg, 12, 0, addr);
+ SET_REG_FIELD(634, reg, 4, 12, 0xF);
+ SET_REG_FIELD(634, reg, 16, 16, lt_obj_indx);
+ GX_WRITE_U8(0x38);
+ GX_WRITE_U32(reg);
+#if DEBUG
+ __GXShadowIndexState(7, reg);
+#endif
+ __GXData->bpSentNot = 1;
+}
+
+#define GXCOLOR_AS_U32(color) (*((u32*)&(color)))
+
+void GXSetChanAmbColor(GXChannelID chan, GXColor amb_color) {
+ u32 reg = 0;
+ u32 rgb;
+ u32 colIdx;
+
+ CHECK_GXBEGIN(675, "GXSetChanAmbColor");
+
+ switch (chan) {
+ case GX_COLOR0:
+ reg = __GXData->ambColor[GX_COLOR0];
+ rgb = GXCOLOR_AS_U32(amb_color) >> 8;
+ SC_XF_AMBIENT0_F_SET_RGB(689, reg, rgb);
+ colIdx = 0;
+ break;
+ case GX_COLOR1:
+ reg = __GXData->ambColor[GX_COLOR1];
+ rgb = GXCOLOR_AS_U32(amb_color) >> 8;
+ SC_XF_AMBIENT1_F_SET_RGB(704, reg, rgb);
+ colIdx = 1;
+ break;
+ case GX_ALPHA0:
+ reg = __GXData->ambColor[GX_COLOR0];
+ SC_XF_AMBIENT0_F_SET_ALPHA(710, reg, amb_color.a);
+ colIdx = 0;
+ break;
+ case GX_ALPHA1:
+ reg = __GXData->ambColor[GX_COLOR1];
+ SC_XF_AMBIENT1_F_SET_ALPHA(716, reg, amb_color.a);
+ colIdx = 1;
+ break;
+ case GX_COLOR0A0:
+ reg = GXCOLOR_AS_U32(amb_color);
+ colIdx = 0;
+ break;
+ case GX_COLOR1A1:
+ reg = GXCOLOR_AS_U32(amb_color);
+ colIdx = 1;
+ break;
+ default:
+ ASSERTMSGLINE(745, 0, "GXSetChanAmbColor: Invalid Channel Id");
+ return;
+ }
+
+ __GXData->dirtyState |= (0x100 << colIdx);
+ __GXData->ambColor[colIdx] = reg;
+}
+
+void GXSetChanMatColor(GXChannelID chan, GXColor mat_color) {
+ u32 reg = 0;
+ u32 rgb;
+ u32 colIdx;
+
+ CHECK_GXBEGIN(776, "GXSetChanMatColor");
+
+ switch (chan) {
+ case GX_COLOR0:
+ reg = __GXData->matColor[GX_COLOR0];
+ rgb = GXCOLOR_AS_U32(mat_color) >> 8;
+ SC_XF_MATERIAL0_F_SET_RGB(790, reg, rgb);
+ colIdx = 0;
+ break;
+ case GX_COLOR1:
+ reg = __GXData->matColor[GX_COLOR1];
+ rgb = GXCOLOR_AS_U32(mat_color) >> 8;
+ SC_XF_MATERIAL1_F_SET_RGB(805, reg, rgb);
+ colIdx = 1;
+ break;
+ case GX_ALPHA0:
+ reg = __GXData->matColor[GX_COLOR0];
+ SC_XF_MATERIAL0_F_SET_ALPHA(811, reg, mat_color.a);
+ colIdx = 0;
+ break;
+ case GX_ALPHA1:
+ reg = __GXData->matColor[GX_COLOR1];
+ SC_XF_MATERIAL1_F_SET_ALPHA(817, reg, mat_color.a);
+ colIdx = 1;
+ break;
+ case GX_COLOR0A0:
+ reg = GXCOLOR_AS_U32(mat_color);
+ colIdx = 0;
+ break;
+ case GX_COLOR1A1:
+ reg = GXCOLOR_AS_U32(mat_color);
+ colIdx = 1;
+ break;
+ default:
+ ASSERTMSGLINE(846, 0, "GXSetChanMatColor: Invalid Channel Id");
+ return;
+ }
+
+ __GXData->dirtyState |= (0x400 << colIdx);
+ __GXData->matColor[colIdx] = reg;
+}
+
+void GXSetNumChans(u8 nChans) {
+ CHECK_GXBEGIN(871, "GXSetNumChans");
+ ASSERTMSGLINE(872, nChans <= 2, "GXSetNumChans: nChans > 2");
+
+ SC_GEN_MODE_SET_NCOL(874, __GXData->genMode, nChans);
+ __GXData->dirtyState |= 0x1000004;
+}
+
+void GXSetChanCtrl(GXChannelID chan, GXBool enable, GXColorSrc amb_src, GXColorSrc mat_src, u32 light_mask, GXDiffuseFn diff_fn, GXAttnFn attn_fn) {
+ u32 reg;
+ u32 idx;
+
+ CHECK_GXBEGIN(906, "GXSetChanCtrl");
+
+ ASSERTMSGLINE(909, chan >= GX_COLOR0 && chan <= GX_COLOR1A1, "GXSetChanCtrl: Invalid Channel Id");
+
+#if DEBUG
+ if (chan == GX_COLOR0A0)
+ idx = 0;
+ else if (chan == GX_COLOR1A1)
+ idx = 1;
+ else
+ idx = chan;
+#else
+ idx = chan & 0x3;
+#endif
+
+ reg = 0;
+ SC_XF_COLOR0CNTRL_F_SET_LIGHTFUNC(921, reg, enable);
+ SC_XF_COLOR0CNTRL_F_SET_MATERIAL_SRC(922, reg, mat_src);
+ SC_XF_COLOR0CNTRL_F_SET_AMBIENT_SRC(923, reg, amb_src);
+
+ SC_XF_COLOR0CNTRL_F_SET_DIFFUSEATTEN(925, reg, (attn_fn == 0) ? 0 : diff_fn);
+ SC_XF_COLOR0CNTRL_F_SET_ATTENENABLE(926, reg, (attn_fn != 2));
+ SC_XF_COLOR0CNTRL_F_SET_ATTENSELECT(927, reg, (attn_fn != 0));
+
+ SC_XF_COLOR0CNTRL_F_SET_LIGHT0123(939, reg, light_mask & 0xF);
+ SC_XF_COLOR0CNTRL_F_SET_LIGHT4567(940, reg, (light_mask & 0xF0) >> 4);
+
+ __GXData->chanCtrl[idx] = reg;
+ __GXData->dirtyState |= (0x1000<<idx);
+
+ if (chan == GX_COLOR0A0) {
+ __GXData->chanCtrl[GX_ALPHA0] = reg;
+ __GXData->dirtyState |= (0x00001000|0x00004000);
+ } else if (chan == GX_COLOR1A1) {
+ __GXData->chanCtrl[GX_ALPHA1] = reg;
+ __GXData->dirtyState |= (0x00002000|0x00008000);
+ }
+}
diff --git a/src/revolution/gx/GXMisc.c b/src/revolution/gx/GXMisc.c
new file mode 100644
index 0000000000..46980ceeb4
--- /dev/null
+++ b/src/revolution/gx/GXMisc.c
@@ -0,0 +1,482 @@
+#include <stddef.h>
+#include <revolution/base/PPCArch.h>
+#include <revolution/gx.h>
+#include <revolution/os.h>
+
+#include "__gx.h"
+
+static GXDrawSyncCallback TokenCB;
+static GXDrawDoneCallback DrawDoneCB;
+static u8 DrawDone;
+static OSThreadQueue FinishQueue;
+
+void GXSetMisc(GXMiscToken token, u32 val) {
+ switch (token) {
+ case GX_MT_XF_FLUSH:
+ __GXData->vNum = val;
+ __GXData->vNumNot = !__GXData->vNum;
+ __GXData->bpSentNot = 1;
+
+ if (__GXData->vNum != 0) {
+ __GXData->dirtyState |= 8;
+ }
+ break;
+ case GX_MT_DL_SAVE_CONTEXT:
+ ASSERTMSGLINE(235, !__GXData->inDispList, "GXSetMisc: Cannot change DL context setting while making a display list");
+ __GXData->dlSaveContext = (val != 0);
+ break;
+ case GX_MT_ABORT_WAIT_COPYOUT:
+ __GXData->abtWaitPECopy = (val != 0);
+ break;
+ case GX_MT_NULL:
+ break;
+ default:
+#if DEBUG
+ OSReport("GXSetMisc: bad token %d (val %d)\n", token, val);
+#endif
+ break;
+ }
+}
+
+void GXFlush(void) {
+ CHECK_GXBEGIN(284, "GXFlush");
+ if (__GXData->dirtyState) {
+ __GXSetDirtyState();
+ }
+
+ GX_WRITE_U32(0);
+ GX_WRITE_U32(0);
+ GX_WRITE_U32(0);
+ GX_WRITE_U32(0);
+ GX_WRITE_U32(0);
+ GX_WRITE_U32(0);
+ GX_WRITE_U32(0);
+ GX_WRITE_U32(0);
+
+ PPCSync();
+}
+
+void GXResetWriteGatherPipe(void) {
+ while (PPCMfwpar() & 1) {
+ }
+ PPCMtwpar(OSUncachedToPhysical((void*)GXFIFO_ADDR));
+}
+
+static void __GXAbortWait(u32 clocks) {
+ OSTime time0;
+ OSTime time1;
+
+ time0 = OSGetTime();
+ do {
+ time1 = OSGetTime();
+ } while (time1 - time0 <= (clocks / 4));
+}
+
+static void __GXAbortWaitPECopyDone(void) {
+ u32 peCnt0;
+ u32 peCnt1;
+
+ peCnt0 = __GXReadMEMCounterU32(0x28, 0x27);
+ do {
+ peCnt1 = peCnt0;
+ __GXAbortWait(32);
+
+ peCnt0 = __GXReadMEMCounterU32(0x28, 0x27);
+ } while (peCnt0 != peCnt1);
+}
+
+void __GXAbort(void) {
+ if (__GXData->abtWaitPECopy && __GXIsGPFifoReady()) {
+ __GXAbortWaitPECopyDone();
+ }
+
+ __PIRegs[0x18 / 4] = 1;
+ __GXAbortWait(200);
+ __PIRegs[0x18 / 4] = 0;
+ __GXAbortWait(20);
+}
+
+void GXAbortFrame(void) {
+ __GXAbort();
+
+ if (__GXIsGPFifoReady()) {
+ __GXCleanGPFifo();
+ __GXInitRevisionBits();
+ __GXData->dirtyState = 0;
+ GXFlush();
+ }
+}
+
+void GXSetDrawSync(u16 token) {
+ BOOL enabled;
+ u32 reg;
+
+ CHECK_GXBEGIN(489, "GXSetDrawSync");
+
+ enabled = OSDisableInterrupts();
+ reg = token | 0x48000000;
+ GX_WRITE_RA_REG(reg);
+ SC_PE_TOKEN_SET_TOKEN(443, reg, token);
+ SC_PE_TOKEN_SET_RID(443, reg, 0x47);
+ GX_WRITE_RA_REG(reg);
+ GXFlush();
+ OSRestoreInterrupts(enabled);
+ __GXData->bpSentNot = 0;
+}
+
+u16 GXReadDrawSync(void) {
+ u16 token = GX_GET_PE_REG(7);
+ return token;
+}
+
+void GXSetDrawDone(void) {
+ u32 reg;
+ BOOL enabled;
+
+ CHECK_GXBEGIN(489, "GXSetDrawDone");
+ enabled = OSDisableInterrupts();
+ reg = 0x45000002;
+ GX_WRITE_RA_REG(reg);
+ GXFlush();
+ DrawDone = GX_FALSE;
+ OSRestoreInterrupts(enabled);
+ __GXData->bpSentNot = GX_FALSE;
+}
+
+void GXWaitDrawDone(void) {
+ BOOL enabled;
+
+ CHECK_GXBEGIN(527, "GXWaitDrawDone");
+
+ enabled = OSDisableInterrupts();
+ while (!DrawDone) {
+ OSSleepThread(&FinishQueue);
+ }
+ OSRestoreInterrupts(enabled);
+}
+
+void GXDrawDone(void) {
+ CHECK_GXBEGIN(558, "GXDrawDone");
+ GXSetDrawDone();
+ GXWaitDrawDone();
+}
+
+void GXPixModeSync(void) {
+ CHECK_GXBEGIN(580, "GXPixModeSync");
+ GX_WRITE_RA_REG(__GXData->peCtrl);
+ __GXData->bpSentNot = 0;
+}
+
+void GXTexModeSync(void) {
+ u32 reg;
+
+ CHECK_GXBEGIN(625, "GXTexModeSync");
+ reg = 0x63000000;
+ GX_WRITE_RA_REG(reg);
+ __GXData->bpSentNot = 0;
+}
+
+#if DEBUG
+void __GXBypass(u32 reg) {
+ CHECK_GXBEGIN(647, "__GXBypass");
+ GX_WRITE_RA_REG(reg);
+ __GXData->bpSentNot = 0;
+}
+
+u16 __GXReadPEReg(u32 reg) {
+ return GX_GET_PE_REG(reg);
+}
+#endif
+
+void GXPokeAlphaMode(GXCompare func, u8 threshold) {
+ u32 reg;
+
+ reg = (func << 8) | threshold;
+ GX_SET_PE_REG(3, reg);
+}
+
+void GXPokeAlphaRead(GXAlphaReadMode mode) {
+ u32 reg;
+
+ reg = 0;
+ SC_PE_PI_CTL_SET_AFMT(672, reg, mode);
+ SC_PE_PI_CTL_SET_ZFMT(672, reg, 1);
+ GX_SET_PE_REG(4, reg);
+}
+
+void GXPokeAlphaUpdate(GXBool update_enable) {
+ u32 reg;
+
+ reg = GX_GET_PE_REG(1);
+ SC_PE_CMODE0_SET_ALPHA_MASK(683, reg, update_enable);
+ GX_SET_PE_REG(1, reg);
+}
+
+void GXPokeBlendMode(GXBlendMode type, GXBlendFactor src_factor, GXBlendFactor dst_factor, GXLogicOp op) {
+ u32 reg;
+
+ reg = GX_GET_PE_REG(1);
+ SC_PE_CMODE0_SET_BLEND_ENABLE(699, reg, ((type == GX_BM_BLEND) || (type == GX_BM_SUBTRACT)));
+ SC_PE_CMODE0_SET_BLENDOP(700, reg, (type == GX_BM_SUBTRACT));
+ SC_PE_CMODE0_SET_LOGICOP_ENABLE(702, reg, (type == GX_BM_LOGIC));
+ SC_PE_CMODE0_SET_LOGICOP(703, reg, op);
+ SC_PE_CMODE0_SET_SFACTOR(704, reg, src_factor);
+ SC_PE_CMODE0_SET_DFACTOR(705, reg, dst_factor);
+ SC_PE_CMODE0_SET_RID(706, reg, 0x41);
+ GX_SET_PE_REG(1, reg);
+}
+
+void GXPokeColorUpdate(GXBool update_enable) {
+ u32 reg;
+
+ reg = GX_GET_PE_REG(1);
+ SC_PE_CMODE0_SET_COLOR_MASK(717, reg, update_enable);
+ GX_SET_PE_REG(1, reg);
+}
+
+void GXPokeDstAlpha(GXBool enable, u8 alpha) {
+ u32 reg = 0;
+
+ SC_PE_CMODE1_SET_CONSTANT_ALPHA(726, reg, alpha);
+ SC_PE_CMODE1_SET_CONSTANT_ALPHA_ENABLE(727, reg, enable);
+ GX_SET_PE_REG(2, reg);
+}
+
+void GXPokeDither(GXBool dither) {
+ u32 reg;
+
+ reg = GX_GET_PE_REG(1);
+ SC_PE_CMODE0_SET_DITHER_ENABLE(737, reg, dither);
+ GX_SET_PE_REG(1, reg);
+}
+
+void GXPokeZMode(GXBool compare_enable, GXCompare func, GXBool update_enable) {
+ u32 reg = 0;
+
+ SC_PE_ZMODE_SET_ENABLE(746, reg, compare_enable);
+ SC_PE_ZMODE_SET_FUNC(747, reg, func);
+ SC_PE_ZMODE_SET_MASK(748, reg, update_enable);
+ GX_SET_PE_REG(0, reg);
+}
+
+void GXPeekARGB(u16 x, u16 y, u32* color) {
+ u32 addr = (u32)OSPhysicalToUncached(0x08000000);
+
+ SC_PE_PI_EFB_ADDR_SET_X(771, addr, x);
+ SC_PE_PI_EFB_ADDR_SET_Y(772, addr, y);
+ SC_PE_PI_EFB_ADDR_SET_TYPE(773, addr, 0);
+ *color = *(u32*)addr;
+}
+
+void GXPokeARGB(u16 x, u16 y, u32 color) {
+ u32 addr = (u32)OSPhysicalToUncached(0x08000000);
+
+ SC_PE_PI_EFB_ADDR_SET_X(771, addr, x);
+ SC_PE_PI_EFB_ADDR_SET_Y(772, addr, y);
+ SC_PE_PI_EFB_ADDR_SET_TYPE(773, addr, 0);
+ *(u32*)addr = color;
+}
+
+void GXPeekZ(u16 x, u16 y, u32* z) {
+ u32 addr = (u32)OSPhysicalToUncached(0x08000000);
+
+ SC_PE_PI_EFB_ADDR_SET_X(791, addr, x);
+ SC_PE_PI_EFB_ADDR_SET_Y(792, addr, y);
+ SC_PE_PI_EFB_ADDR_SET_TYPE(793, addr, 1);
+ *z = *(u32*)addr;
+}
+
+void GXPokeZ(u16 x, u16 y, u32 z) {
+ u32 addr = (u32)OSPhysicalToUncached(0x08000000);
+
+ SC_PE_PI_EFB_ADDR_SET_X(791, addr, x);
+ SC_PE_PI_EFB_ADDR_SET_Y(792, addr, y);
+ SC_PE_PI_EFB_ADDR_SET_TYPE(793, addr, 1);
+ *(u32*)addr = z;
+}
+
+GXDrawSyncCallback GXSetDrawSyncCallback(GXDrawSyncCallback cb) {
+ GXDrawSyncCallback oldcb;
+ BOOL enabled;
+
+ oldcb = TokenCB;
+ enabled = OSDisableInterrupts();
+ TokenCB = cb;
+ OSRestoreInterrupts(enabled);
+ return oldcb;
+}
+
+static void GXTokenInterruptHandler(__OSInterrupt interrupt, OSContext* context) {
+ u16 token;
+ OSContext exceptionContext;
+ u32 reg;
+
+ token = GX_GET_PE_REG(7);
+ if (TokenCB != NULL) {
+ OSClearContext(&exceptionContext);
+ OSSetCurrentContext(&exceptionContext);
+ TokenCB(token);
+ OSClearContext(&exceptionContext);
+ OSSetCurrentContext(context);
+ }
+ reg = GX_GET_PE_REG(5);
+ SC_PE_INTRCTL_SET_INT0CLR(0, reg, 1);
+ GX_SET_PE_REG(5, reg);
+}
+
+GXDrawDoneCallback GXSetDrawDoneCallback(GXDrawDoneCallback cb) {
+ GXDrawDoneCallback oldcb;
+ BOOL enabled;
+
+ oldcb = DrawDoneCB;
+ enabled = OSDisableInterrupts();
+ DrawDoneCB = cb;
+ OSRestoreInterrupts(enabled);
+ return oldcb;
+}
+
+static void GXFinishInterruptHandler(__OSInterrupt interrupt, OSContext* context) {
+ OSContext exceptionContext;
+ u32 reg = 0;
+
+ reg = GX_GET_PE_REG(5);
+ SC_PE_INTRCTL_SET_INT1CLR(0, reg, 1);
+ GX_SET_PE_REG(5, reg);
+ DrawDone = 1;
+ if (DrawDoneCB != NULL) {
+ OSClearContext(&exceptionContext);
+ OSSetCurrentContext(&exceptionContext);
+ DrawDoneCB();
+ OSClearContext(&exceptionContext);
+ OSSetCurrentContext(context);
+ }
+ OSWakeupThread(&FinishQueue);
+}
+
+void __GXPEInit(void) {
+ u32 reg;
+ __OSSetInterruptHandler(0x12, GXTokenInterruptHandler);
+ __OSSetInterruptHandler(0x13, GXFinishInterruptHandler);
+ OSInitThreadQueue(&FinishQueue);
+ __OSUnmaskInterrupts(0x2000);
+ __OSUnmaskInterrupts(0x1000);
+ reg = GX_GET_PE_REG(5);
+ SC_PE_INTRCTL_SET_INT0CLR(0, reg, 1);
+ SC_PE_INTRCTL_SET_INT1CLR(0, reg, 1);
+ SC_PE_INTRCTL_SET_INT0EN(0, reg, 1);
+ SC_PE_INTRCTL_SET_INT1EN(0, reg, 1);
+ GX_SET_PE_REG(5, reg);
+}
+
+u32 GXCompressZ16(u32 z24, GXZFmt16 zfmt) {
+ u32 z16;
+ u32 z24n;
+ s32 exp;
+ s32 shift;
+#if DEBUG
+#define temp exp
+#else
+ s32 temp;
+ u8 unused[4];
+#endif
+
+ z24n = ~(z24 << 8);
+ temp = __cntlzw(z24n);
+ switch (zfmt) {
+ case GX_ZC_LINEAR:
+ z16 = (z24 >> 8) & 0xFFFF;
+ break;
+ case GX_ZC_NEAR:
+ if (temp > 3) {
+ exp = 3;
+ } else {
+ exp = temp;
+ }
+ if (exp == 3) {
+ shift = 7;
+ } else {
+ shift = 9 - exp;
+ }
+ z16 = ((z24 >> shift) & 0x3FFF & ~0xFFFFC000) | (exp << 14);
+ break;
+ case GX_ZC_MID:
+ if (temp > 7) {
+ exp = 7;
+ } else {
+ exp = temp;
+ }
+ if (exp == 7) {
+ shift = 4;
+ } else {
+ shift = 10 - exp;
+ }
+ z16 = ((z24 >> shift) & 0x1FFF & ~0xFFFFE000) | (exp << 13);
+ break;
+ case GX_ZC_FAR:
+ if (temp > 12) {
+ exp = 12;
+ } else {
+ exp = temp;
+ }
+ if (exp == 12) {
+ shift = 0;
+ } else {
+ shift = 11 - exp;
+ }
+ z16 = ((z24 >> shift) & 0xFFF & ~0xFFFFF000) | (exp << 12);
+ break;
+ default:
+ OSPanic(__FILE__, 1004, "GXCompressZ16: Invalid Z format\n");
+ break;
+ }
+ return z16;
+}
+
+u32 GXDecompressZ16(u32 z16, GXZFmt16 zfmt) {
+ u32 z24;
+ u32 cb1;
+ s32 exp;
+ s32 shift;
+
+ cb1; cb1; cb1; z16; z16; z16; // needed to match
+
+ switch (zfmt) {
+ case GX_ZC_LINEAR:
+ z24 = (z16 << 8) & 0xFFFF00;
+ break;
+ case GX_ZC_NEAR:
+ exp = (z16 >> 14) & 3;
+ if (exp == 3) {
+ shift = 7;
+ } else {
+ shift = 9 - exp;
+ }
+ cb1 = -1 << (24 - exp);
+ z24 = (cb1 | ((z16 & 0x3FFF) << shift)) & 0xFFFFFF;
+ break;
+ case GX_ZC_MID:
+ exp = (z16 >> 13) & 7;
+ if (exp == 7) {
+ shift = 4;
+ } else {
+ shift = 10 - exp;
+ }
+ cb1 = -1 << (24 - exp);
+ z24 = (cb1 | ((z16 & 0x1FFF) << shift)) & 0xFFFFFF;
+ break;
+ case GX_ZC_FAR:
+ exp = (z16 >> 12) & 0xF;
+ if (exp == 12) {
+ shift = 0;
+ } else {
+ shift = 11 - exp;
+ }
+ cb1 = -1 << (24 - exp);
+ z24 = (cb1 | ((z16 & 0xFFF) << shift)) & 0xFFFFFF;
+ break;
+ default:
+ OSPanic(__FILE__, 1054, "GXDecompressZ16: Invalid Z format\n");
+ break;
+ }
+ return z24;
+}
diff --git a/src/revolution/gx/GXPerf.c b/src/revolution/gx/GXPerf.c
new file mode 100644
index 0000000000..26d094e3fa
--- /dev/null
+++ b/src/revolution/gx/GXPerf.c
@@ -0,0 +1,431 @@
+#include <revolution/gx.h>
+#include <revolution/os.h>
+
+#include "__gx.h"
+
+void GXSetGPMetric(GXPerf0 perf0, GXPerf1 perf1) {
+ u32 reg;
+
+ CHECK_GXBEGIN(142, "GXSetGPMetric");
+
+ switch (__GXData->perf0) {
+ case GX_PERF0_VERTICES:
+ case GX_PERF0_CLIP_VTX:
+ case GX_PERF0_CLIP_CLKS:
+ case GX_PERF0_XF_WAIT_IN:
+ case GX_PERF0_XF_WAIT_OUT:
+ case GX_PERF0_XF_XFRM_CLKS:
+ case GX_PERF0_XF_LIT_CLKS:
+ case GX_PERF0_XF_BOT_CLKS:
+ case GX_PERF0_XF_REGLD_CLKS:
+ case GX_PERF0_XF_REGRD_CLKS:
+ case GX_PERF0_CLIP_RATIO:
+ case GX_PERF0_CLOCKS:
+ reg = 0;
+ GX_WRITE_XF_REG(0x1006, reg, 0);
+ break;
+ case GX_PERF0_TRIANGLES:
+ case GX_PERF0_TRIANGLES_CULLED:
+ case GX_PERF0_TRIANGLES_PASSED:
+ case GX_PERF0_TRIANGLES_SCISSORED:
+ case GX_PERF0_TRIANGLES_0TEX:
+ case GX_PERF0_TRIANGLES_1TEX:
+ case GX_PERF0_TRIANGLES_2TEX:
+ case GX_PERF0_TRIANGLES_3TEX:
+ case GX_PERF0_TRIANGLES_4TEX:
+ case GX_PERF0_TRIANGLES_5TEX:
+ case GX_PERF0_TRIANGLES_6TEX:
+ case GX_PERF0_TRIANGLES_7TEX:
+ case GX_PERF0_TRIANGLES_8TEX:
+ case GX_PERF0_TRIANGLES_0CLR:
+ case GX_PERF0_TRIANGLES_1CLR:
+ case GX_PERF0_TRIANGLES_2CLR:
+ reg = 0x23000000;
+ GX_WRITE_RAS_REG(reg);
+ break;
+ case GX_PERF0_QUAD_0CVG:
+ case GX_PERF0_QUAD_NON0CVG:
+ case GX_PERF0_QUAD_1CVG:
+ case GX_PERF0_QUAD_2CVG:
+ case GX_PERF0_QUAD_3CVG:
+ case GX_PERF0_QUAD_4CVG:
+ case GX_PERF0_AVG_QUAD_CNT:
+ reg = 0x24000000;
+ GX_WRITE_RAS_REG(reg);
+ break;
+ case GX_PERF0_NONE:
+ break;
+ default:
+ ASSERTMSGLINE(202, 0, "GXSetGPMetric: Invalid GXPerf0 metric name");
+ break;
+ }
+
+ switch (__GXData->perf1) {
+ case GX_PERF1_TEXELS:
+ case GX_PERF1_TX_IDLE:
+ case GX_PERF1_TX_REGS:
+ case GX_PERF1_TX_MEMSTALL:
+ case GX_PERF1_TC_CHECK1_2:
+ case GX_PERF1_TC_CHECK3_4:
+ case GX_PERF1_TC_CHECK5_6:
+ case GX_PERF1_TC_CHECK7_8:
+ case GX_PERF1_TC_MISS:
+ case GX_PERF1_CLOCKS:
+ reg = 0x67000000;
+ GX_WRITE_RAS_REG(reg);
+ break;
+ case GX_PERF1_VC_ELEMQ_FULL:
+ case GX_PERF1_VC_MISSQ_FULL:
+ case GX_PERF1_VC_MEMREQ_FULL:
+ case GX_PERF1_VC_STATUS7:
+ case GX_PERF1_VC_MISSREP_FULL:
+ case GX_PERF1_VC_STREAMBUF_LOW:
+ case GX_PERF1_VC_ALL_STALLS:
+ case GX_PERF1_VERTICES:
+ SET_REG_FIELD(0, __GXData->perfSel, 4, 4, 0);
+ GX_WRITE_SOME_REG4(8, 0x20, __GXData->perfSel, -12);
+ break;
+ case GX_PERF1_FIFO_REQ:
+ case GX_PERF1_CALL_REQ:
+ case GX_PERF1_VC_MISS_REQ:
+ case GX_PERF1_CP_ALL_REQ:
+ reg = 0;
+ GX_SET_CP_REG(3, reg);
+ break;
+ case GX_PERF1_NONE:
+ break;
+ default:
+ ASSERTMSGLINE(252, 0, "GXSetGPMetric: Invalid GXPerf1 metric name");
+ break;
+ }
+
+ __GXData->perf0 = perf0;
+ switch (__GXData->perf0) {
+ case GX_PERF0_VERTICES:
+ ASSERTMSGLINE(266, 0, "The use of GX_PERF0_VERTICES is prohibited. Use GX_PERF1_VERTICES instead.\n");
+ reg = 0x273;
+ GX_WRITE_XF_REG(0x1006, reg, 0);
+ break;
+ case GX_PERF0_CLIP_VTX: reg = 0x14A; GX_WRITE_XF_REG(0x1006, reg, 0); break;
+ case GX_PERF0_CLIP_CLKS: reg = 0x16B; GX_WRITE_XF_REG(0x1006, reg, 0); break;
+ case GX_PERF0_XF_WAIT_IN: reg = 0x84; GX_WRITE_XF_REG(0x1006, reg, 0); break;
+ case GX_PERF0_XF_WAIT_OUT: reg = 0xC6; GX_WRITE_XF_REG(0x1006, reg, 0); break;
+ case GX_PERF0_XF_XFRM_CLKS: reg = 0x210; GX_WRITE_XF_REG(0x1006, reg, 0); break;
+ case GX_PERF0_XF_LIT_CLKS: reg = 0x252; GX_WRITE_XF_REG(0x1006, reg, 0); break;
+ case GX_PERF0_XF_BOT_CLKS: reg = 0x231; GX_WRITE_XF_REG(0x1006, reg, 0); break;
+ case GX_PERF0_XF_REGLD_CLKS: reg = 0x1AD; GX_WRITE_XF_REG(0x1006, reg, 0); break;
+ case GX_PERF0_XF_REGRD_CLKS: reg = 0x1CE; GX_WRITE_XF_REG(0x1006, reg, 0); break;
+ case GX_PERF0_CLOCKS: reg = 0x21; GX_WRITE_XF_REG(0x1006, reg, 0); break;
+ case GX_PERF0_CLIP_RATIO: reg = 0x153; GX_WRITE_XF_REG(0x1006, reg, 0); break;
+ case GX_PERF0_TRIANGLES: reg = 0x2300AE7F; GX_WRITE_RAS_REG(reg); break;
+ case GX_PERF0_TRIANGLES_CULLED: reg = 0x23008E7F; GX_WRITE_RAS_REG(reg); break;
+ case GX_PERF0_TRIANGLES_PASSED: reg = 0x23009E7F; GX_WRITE_RAS_REG(reg); break;
+ case GX_PERF0_TRIANGLES_SCISSORED: reg = 0x23001E7F; GX_WRITE_RAS_REG(reg); break;
+ case GX_PERF0_TRIANGLES_0TEX: reg = 0x2300AC3F; GX_WRITE_RAS_REG(reg); break;
+ case GX_PERF0_TRIANGLES_1TEX: reg = 0x2300AC7F; GX_WRITE_RAS_REG(reg); break;
+ case GX_PERF0_TRIANGLES_2TEX: reg = 0x2300ACBF; GX_WRITE_RAS_REG(reg); break;
+ case GX_PERF0_TRIANGLES_3TEX: reg = 0x2300ACFF; GX_WRITE_RAS_REG(reg); break;
+ case GX_PERF0_TRIANGLES_4TEX: reg = 0x2300AD3F; GX_WRITE_RAS_REG(reg); break;
+ case GX_PERF0_TRIANGLES_5TEX: reg = 0x2300AD7F; GX_WRITE_RAS_REG(reg); break;
+ case GX_PERF0_TRIANGLES_6TEX: reg = 0x2300ADBF; GX_WRITE_RAS_REG(reg); break;
+ case GX_PERF0_TRIANGLES_7TEX: reg = 0x2300ADFF; GX_WRITE_RAS_REG(reg); break;
+ case GX_PERF0_TRIANGLES_8TEX: reg = 0x2300AE3F; GX_WRITE_RAS_REG(reg); break;
+ case GX_PERF0_TRIANGLES_0CLR: reg = 0x2300A27F; GX_WRITE_RAS_REG(reg); break;
+ case GX_PERF0_TRIANGLES_1CLR: reg = 0x2300A67F; GX_WRITE_RAS_REG(reg); break;
+ case GX_PERF0_TRIANGLES_2CLR: reg = 0x2300AA7F; GX_WRITE_RAS_REG(reg); break;
+ case GX_PERF0_QUAD_0CVG: reg = 0x2402C0C6; GX_WRITE_RAS_REG(reg); break;
+ case GX_PERF0_QUAD_NON0CVG: reg = 0x2402C16B; GX_WRITE_RAS_REG(reg); break;
+ case GX_PERF0_QUAD_1CVG: reg = 0x2402C0E7; GX_WRITE_RAS_REG(reg); break;
+ case GX_PERF0_QUAD_2CVG: reg = 0x2402C108; GX_WRITE_RAS_REG(reg); break;
+ case GX_PERF0_QUAD_3CVG: reg = 0x2402C129; GX_WRITE_RAS_REG(reg); break;
+ case GX_PERF0_QUAD_4CVG: reg = 0x2402C14A; GX_WRITE_RAS_REG(reg); break;
+ case GX_PERF0_AVG_QUAD_CNT: reg = 0x2402C1AD; GX_WRITE_RAS_REG(reg); break;
+ case GX_PERF0_NONE: break;
+ default:
+ ASSERTMSGLINE(512, 0, "GXSetGPMetric: Invalid GXPerf0 metric name");
+ break;
+ }
+
+ __GXData->perf1 = perf1;
+ switch (__GXData->perf1) {
+ case GX_PERF1_TEXELS: reg = 0x67000042; GX_WRITE_RAS_REG(reg); break;
+ case GX_PERF1_TX_IDLE: reg = 0x67000084; GX_WRITE_RAS_REG(reg); break;
+ case GX_PERF1_TX_REGS: reg = 0x67000063; GX_WRITE_RAS_REG(reg); break;
+ case GX_PERF1_TX_MEMSTALL: reg = 0x67000129; GX_WRITE_RAS_REG(reg); break;
+ case GX_PERF1_TC_MISS: reg = 0x67000252; GX_WRITE_RAS_REG(reg); break;
+ case GX_PERF1_CLOCKS: reg = 0x67000021; GX_WRITE_RAS_REG(reg); break;
+ case GX_PERF1_TC_CHECK1_2: reg = 0x6700014B; GX_WRITE_RAS_REG(reg); break;
+ case GX_PERF1_TC_CHECK3_4: reg = 0x6700018D; GX_WRITE_RAS_REG(reg); break;
+ case GX_PERF1_TC_CHECK5_6: reg = 0x670001CF; GX_WRITE_RAS_REG(reg); break;
+ case GX_PERF1_TC_CHECK7_8: reg = 0x67000211; GX_WRITE_RAS_REG(reg); break;
+ case GX_PERF1_VC_ELEMQ_FULL: SET_REG_FIELD(0, __GXData->perfSel, 4, 4, 2); GX_WRITE_SOME_REG4(8, 0x20, __GXData->perfSel, -12); break;
+ case GX_PERF1_VC_MISSQ_FULL: SET_REG_FIELD(0, __GXData->perfSel, 4, 4, 3); GX_WRITE_SOME_REG4(8, 0x20, __GXData->perfSel, -12); break;
+ case GX_PERF1_VC_MEMREQ_FULL: SET_REG_FIELD(0, __GXData->perfSel, 4, 4, 4); GX_WRITE_SOME_REG4(8, 0x20, __GXData->perfSel, -12); break;
+ case GX_PERF1_VC_STATUS7: SET_REG_FIELD(0, __GXData->perfSel, 4, 4, 5); GX_WRITE_SOME_REG4(8, 0x20, __GXData->perfSel, -12); break;
+ case GX_PERF1_VC_MISSREP_FULL: SET_REG_FIELD(0, __GXData->perfSel, 4, 4, 6); GX_WRITE_SOME_REG4(8, 0x20, __GXData->perfSel, -12); break;
+ case GX_PERF1_VC_STREAMBUF_LOW: SET_REG_FIELD(0, __GXData->perfSel, 4, 4, 7); GX_WRITE_SOME_REG4(8, 0x20, __GXData->perfSel, -12); break;
+ case GX_PERF1_VC_ALL_STALLS: SET_REG_FIELD(0, __GXData->perfSel, 4, 4, 9); GX_WRITE_SOME_REG4(8, 0x20, __GXData->perfSel, -12); break;
+ case GX_PERF1_VERTICES: SET_REG_FIELD(0, __GXData->perfSel, 4, 4, 8); GX_WRITE_SOME_REG4(8, 0x20, __GXData->perfSel, -12); break;
+ case GX_PERF1_FIFO_REQ: reg = 2; GX_SET_CP_REG(3, reg); break;
+ case GX_PERF1_CALL_REQ: reg = 3; GX_SET_CP_REG(3, reg); break;
+ case GX_PERF1_VC_MISS_REQ: reg = 4; GX_SET_CP_REG(3, reg); break;
+ case GX_PERF1_CP_ALL_REQ: reg = 5; GX_SET_CP_REG(3, reg); break;
+ case GX_PERF1_NONE: break;
+ default:
+ ASSERTMSGLINE(657, 0, "GXSetGPMetric: Invalid GXPerf1 metric name");
+ break;
+ }
+
+ __GXData->bpSentNot = 0;
+}
+
+#pragma scheduling off
+void GXReadGPMetric(u32* cnt0, u32* cnt1) {
+ u32 cpCtr0, cpCtr1, cpCtr2, cpCtr3;
+
+ cpCtr0 = __GXReadCPCounterU32(32, 33);
+ cpCtr1 = __GXReadCPCounterU32(34, 35);
+ cpCtr2 = __GXReadCPCounterU32(36, 37);
+ cpCtr3 = __GXReadCPCounterU32(38, 39);
+
+ switch (__GXData->perf0) {
+ case GX_PERF0_CLIP_RATIO:
+ *cnt0 = cpCtr1 * 1000 / cpCtr0;
+ break;
+ case GX_PERF0_VERTICES:
+ case GX_PERF0_CLIP_VTX:
+ case GX_PERF0_CLIP_CLKS:
+ case GX_PERF0_XF_WAIT_IN:
+ case GX_PERF0_XF_WAIT_OUT:
+ case GX_PERF0_XF_XFRM_CLKS:
+ case GX_PERF0_XF_LIT_CLKS:
+ case GX_PERF0_XF_BOT_CLKS:
+ case GX_PERF0_XF_REGLD_CLKS:
+ case GX_PERF0_XF_REGRD_CLKS:
+ case GX_PERF0_TRIANGLES:
+ case GX_PERF0_TRIANGLES_CULLED:
+ case GX_PERF0_TRIANGLES_PASSED:
+ case GX_PERF0_TRIANGLES_SCISSORED:
+ case GX_PERF0_TRIANGLES_0TEX:
+ case GX_PERF0_TRIANGLES_1TEX:
+ case GX_PERF0_TRIANGLES_2TEX:
+ case GX_PERF0_TRIANGLES_3TEX:
+ case GX_PERF0_TRIANGLES_4TEX:
+ case GX_PERF0_TRIANGLES_5TEX:
+ case GX_PERF0_TRIANGLES_6TEX:
+ case GX_PERF0_TRIANGLES_7TEX:
+ case GX_PERF0_TRIANGLES_8TEX:
+ case GX_PERF0_TRIANGLES_0CLR:
+ case GX_PERF0_TRIANGLES_1CLR:
+ case GX_PERF0_TRIANGLES_2CLR:
+ case GX_PERF0_QUAD_0CVG:
+ case GX_PERF0_QUAD_NON0CVG:
+ case GX_PERF0_QUAD_1CVG:
+ case GX_PERF0_QUAD_2CVG:
+ case GX_PERF0_QUAD_3CVG:
+ case GX_PERF0_QUAD_4CVG:
+ case GX_PERF0_AVG_QUAD_CNT:
+ case GX_PERF0_CLOCKS:
+ *cnt0 = cpCtr0;
+ break;
+ case GX_PERF0_NONE:
+ *cnt0 = 0;
+ break;
+ default:
+ ASSERTMSGLINE(765, 0, "GXReadGPMetric: Invalid GXPerf0 metric name");
+ *cnt0 = 0;
+ break;
+ }
+
+ switch (__GXData->perf1) {
+ case GX_PERF1_TEXELS:
+ *cnt1 = cpCtr3 * 4;
+ break;
+ case GX_PERF1_TC_CHECK1_2:
+ *cnt1 = cpCtr2 + (cpCtr3 * 2);
+ break;
+ case GX_PERF1_TC_CHECK3_4:
+ *cnt1 = (cpCtr2 * 3) + (cpCtr3 * 4);
+ break;
+ case GX_PERF1_TC_CHECK5_6:
+ *cnt1 = (cpCtr2 * 5) + (cpCtr3 * 6);
+ break;
+ case GX_PERF1_TC_CHECK7_8:
+ *cnt1 = (cpCtr2 * 7) + (cpCtr3 * 8);
+ break;
+ case GX_PERF1_TX_IDLE:
+ case GX_PERF1_TX_REGS:
+ case GX_PERF1_TX_MEMSTALL:
+ case GX_PERF1_TC_MISS:
+ case GX_PERF1_VC_ELEMQ_FULL:
+ case GX_PERF1_VC_MISSQ_FULL:
+ case GX_PERF1_VC_MEMREQ_FULL:
+ case GX_PERF1_VC_STATUS7:
+ case GX_PERF1_VC_MISSREP_FULL:
+ case GX_PERF1_VC_STREAMBUF_LOW:
+ case GX_PERF1_VC_ALL_STALLS:
+ case GX_PERF1_VERTICES:
+ case GX_PERF1_CLOCKS:
+ *cnt1 = cpCtr3;
+ break;
+ case GX_PERF1_FIFO_REQ:
+ case GX_PERF1_CALL_REQ:
+ case GX_PERF1_VC_MISS_REQ:
+ case GX_PERF1_CP_ALL_REQ:
+ *cnt1 = cpCtr2;
+ break;
+ case GX_PERF1_NONE:
+ *cnt1 = 0;
+ break;
+ default:
+ ASSERTMSGLINE(824, 0, "GXReadGPMetric: Invalid GXPerf1 metric name");
+ *cnt1 = 0;
+ break;
+ }
+}
+#pragma scheduling reset
+
+void GXClearGPMetric(void) {
+ u32 reg;
+
+ reg = 4;
+ GX_SET_CP_REG(2, reg);
+}
+
+u32 GXReadGP0Metric(void) {
+ u32 cnt0, cnt1;
+
+ GXReadGPMetric(&cnt0, &cnt1);
+ return cnt0;
+}
+
+u32 GXReadGP1Metric(void) {
+ u32 cnt0, cnt1;
+
+ GXReadGPMetric(&cnt0, &cnt1);
+ return cnt1;
+}
+
+#pragma scheduling off
+void GXReadMemMetric(u32* cp_req, u32* tc_req, u32* cpu_rd_req, u32* cpu_wr_req, u32* dsp_req, u32* io_req, u32* vi_req, u32* pe_req, u32* rf_req, u32* fi_req) {
+ *cp_req = __GXReadMEMCounterU32(26, 25);
+ *tc_req = __GXReadMEMCounterU32(28, 27);
+ *cpu_rd_req = __GXReadMEMCounterU32(30, 29);
+ *cpu_wr_req = __GXReadMEMCounterU32(32, 31);
+ *dsp_req = __GXReadMEMCounterU32(34, 33);
+ *io_req = __GXReadMEMCounterU32(36, 35);
+ *vi_req = __GXReadMEMCounterU32(38, 37);
+ *pe_req = __GXReadMEMCounterU32(40, 39);
+ *rf_req = __GXReadMEMCounterU32(42, 41);
+ *fi_req = __GXReadMEMCounterU32(44, 43);
+}
+#pragma scheduling reset
+
+void GXClearMemMetric(void) {
+ GX_SET_MEM_REG(25, 0);
+ GX_SET_MEM_REG(26, 0);
+ GX_SET_MEM_REG(27, 0);
+ GX_SET_MEM_REG(28, 0);
+ GX_SET_MEM_REG(30, 0);
+ GX_SET_MEM_REG(29, 0);
+ GX_SET_MEM_REG(32, 0);
+ GX_SET_MEM_REG(31, 0);
+ GX_SET_MEM_REG(34, 0);
+ GX_SET_MEM_REG(33, 0);
+ GX_SET_MEM_REG(36, 0);
+ GX_SET_MEM_REG(35, 0);
+ GX_SET_MEM_REG(38, 0);
+ GX_SET_MEM_REG(37, 0);
+ GX_SET_MEM_REG(40, 0);
+ GX_SET_MEM_REG(39, 0);
+ GX_SET_MEM_REG(42, 0);
+ GX_SET_MEM_REG(41, 0);
+ GX_SET_MEM_REG(44, 0);
+ GX_SET_MEM_REG(43, 0);
+}
+
+#pragma scheduling off
+void GXReadPixMetric(u32* top_pixels_in, u32* top_pixels_out, u32* bot_pixels_in, u32* bot_pixels_out, u32* clr_pixels_in, u32* copy_clks) {
+ *top_pixels_in = __GXReadPECounterU32(12, 13) * 4;
+ *top_pixels_out = __GXReadPECounterU32(14, 15) * 4;
+ *bot_pixels_in = __GXReadPECounterU32(16, 17) * 4;
+ *bot_pixels_out = __GXReadPECounterU32(18, 19) * 4;
+ *clr_pixels_in = __GXReadPECounterU32(20, 21) * 4;
+ *copy_clks = __GXReadPECounterU32(22, 23);
+}
+#pragma scheduling reset
+
+void GXClearPixMetric(void) {
+ u32 reg;
+
+ CHECK_GXBEGIN(1163, "GXClearPixMetric");
+
+ reg = 0x57000000;
+ GX_WRITE_RAS_REG(reg);
+ reg = 0x57000AAA;
+ GX_WRITE_RAS_REG(reg);
+ __GXData->bpSentNot = 0;
+}
+
+void GXSetVCacheMetric(GXVCachePerf attr) {
+ u32 reg;
+
+ SET_REG_FIELD(1194, __GXData->perfSel, 4, 0, attr);
+ GX_WRITE_SOME_REG4(8, 0x20, __GXData->perfSel, -12);
+ reg = 1;
+ GX_WRITE_SOME_REG4(8, 0x10, reg, -12);
+}
+
+#pragma scheduling off
+void GXReadVCacheMetric(u32* check, u32* miss, u32* stall) {
+ *check = __GXReadCPCounterU32(40, 41);
+ *miss = __GXReadCPCounterU32(42, 43);
+ *stall = __GXReadCPCounterU32(44, 45);
+}
+#pragma scheduling reset
+
+void GXClearVCacheMetric(void) {
+ GX_WRITE_SOME_REG4(8, 0, 0, -12);
+}
+
+void GXInitXfRasMetric(void) {
+ u32 reg;
+
+ CHECK_GXBEGIN(1293, "GXInitXfRasMetric");
+
+ reg = 0x2402C022;
+ GX_WRITE_RAS_REG(reg);
+ reg = 0x31000;
+ GX_WRITE_XF_REG(6, reg, 0);
+ __GXData->bpSentNot = 1;
+}
+
+#pragma scheduling off
+void GXReadXfRasMetric(u32* xf_wait_in, u32* xf_wait_out, u32* ras_busy, u32* clocks) {
+ *ras_busy = __GXReadCPCounterU32(32, 33);
+ *clocks = __GXReadCPCounterU32(34, 35);
+ *xf_wait_in = __GXReadCPCounterU32(36, 37);
+ *xf_wait_out = __GXReadCPCounterU32(38, 39);
+}
+#pragma scheduling reset
+
+u32 GXReadClksPerVtx(void) {
+ u32 perfCnt;
+ u32 ctrh;
+
+ GXDrawDone();
+ GX_SET_CP_REG(49, 0x1007);
+ GX_SET_CP_REG(48, 0x1007);
+
+ ctrh = GX_GET_CP_REG(50);
+ perfCnt = ctrh >> 8;
+ return perfCnt;
+}
+
+void __GXSetBWDials(u16 cpDial, u16 tcDial, u16 peDial, u16 cpuRdDial, u16 cpuWrDial) {
+ __MEMRegs[9] = cpDial;
+ __MEMRegs[10] = tcDial;
+ __MEMRegs[11] = peDial;
+ __MEMRegs[12] = cpuRdDial;
+ __MEMRegs[13] = cpuWrDial;
+}
diff --git a/src/revolution/gx/GXPixel.c b/src/revolution/gx/GXPixel.c
new file mode 100644
index 0000000000..a68a119305
--- /dev/null
+++ b/src/revolution/gx/GXPixel.c
@@ -0,0 +1,332 @@
+#include <math.h>
+#include <revolution/gx.h>
+#include <revolution/os.h>
+
+#include "__gx.h"
+
+void GXSetFog(GXFogType type, f32 startz, f32 endz, f32 nearz, f32 farz, GXColor color) {
+ u32 fogclr;
+ u32 fog0;
+ u32 fog1;
+ u32 fog2;
+ u32 fog3;
+ f32 A;
+ f32 B;
+ f32 B_mant;
+ f32 C;
+ f32 a;
+ f32 c;
+ u32 B_expn;
+ u32 b_m;
+ u32 b_s;
+ u32 a_hex;
+ u32 c_hex;
+ u32 fsel;
+ u32 proj;
+ u32 rgba;
+
+ fogclr = 0;
+ fog0 = 0;
+ fog1 = 0;
+ fog2 = 0;
+ fog3 = 0;
+
+ CHECK_GXBEGIN(152, "GXSetFog");
+
+ ASSERTMSGLINE(154, farz >= 0.0f, "GXSetFog: The farz should be positive value");
+ ASSERTMSGLINE(155, farz >= nearz, "GXSetFog: The farz should be larger than nearz");
+
+ fsel = type & 7;
+ proj = (type >> 3) & 1;
+
+ if (proj) {
+ if (farz == nearz || endz == startz) {
+ a = 0.0f;
+ c = 0.0f;
+ } else {
+ A = (1.0f / (endz - startz));
+ a = A * (farz - nearz);
+ c = A * (startz - nearz);
+ }
+ } else {
+ if (farz == nearz || endz == startz) {
+ A = 0.0f;
+ B = 0.5f;
+ C = 0.0f;
+ } else {
+ A = (farz * nearz) / ((farz - nearz) * (endz - startz));
+ B = farz / (farz - nearz);
+ C = startz / (endz - startz);
+ }
+
+ B_mant = B;
+ B_expn = 0;
+ while (B_mant > 1.0) {
+ B_mant /= 2.0f;
+ B_expn++;
+ }
+ while (B_mant > 0.0f && B_mant < 0.5) {
+ B_mant *= 2.0f;
+ B_expn--;
+ }
+
+ a = A / (f32) (1 << (B_expn + 1));
+ b_m = 8.388638e6f * B_mant;
+ b_s = B_expn + 1;
+ c = C;
+
+ SET_REG_FIELD(212, fog1, 24, 0, b_m);
+ SET_REG_FIELD(212, fog1, 8, 24, 0xEF);
+
+ SET_REG_FIELD(215, fog2, 5, 0, b_s);
+ SET_REG_FIELD(215, fog2, 8, 24, 0xF0);
+
+ GX_WRITE_RAS_REG(fog1);
+ GX_WRITE_RAS_REG(fog2);
+ }
+
+ a_hex = *(u32*)&a;
+ c_hex = *(u32*)&c;
+
+ SET_REG_FIELD(226, fog0, 11, 0, (a_hex >> 12) & 0x7FF);
+ SET_REG_FIELD(227, fog0, 8, 11, (a_hex >> 23) & 0xFF);
+ SET_REG_FIELD(228, fog0, 1, 19, (a_hex >> 31));
+ SET_REG_FIELD(228, fog0, 8, 24, 0xEE);
+
+ SET_REG_FIELD(231, fog3, 11, 0, (c_hex >> 12) & 0x7FF);
+ SET_REG_FIELD(232, fog3, 8, 11, (c_hex >> 23) & 0xFF);
+ SET_REG_FIELD(233, fog3, 1, 19, (c_hex >> 31));
+ SET_REG_FIELD(234, fog3, 1, 20, proj);
+ SET_REG_FIELD(235, fog3, 3, 21, fsel);
+ SET_REG_FIELD(236, fog3, 8, 24, 0xF1);
+
+ rgba = *(u32*)&color;
+ SET_REG_FIELD(239, fogclr, 24, 0, rgba >> 8);
+ SET_REG_FIELD(240, fogclr, 8, 24, 0xF2);
+
+ GX_WRITE_RAS_REG(fog0);
+ GX_WRITE_RAS_REG(fog3);
+ GX_WRITE_RAS_REG(fogclr);
+
+ __GXData->bpSentNot = 0;
+}
+
+void GXSetFogColor(GXColor color) {
+ u32 rgba;
+ u32 fogclr = 0xF2000000;
+
+ rgba = *(u32*)&color;
+ SET_REG_FIELD(250, fogclr, 24, 0, rgba >> 8);
+ GX_WRITE_RAS_REG(fogclr);
+ __GXData->bpSentNot = 0;
+}
+
+void GXInitFogAdjTable(GXFogAdjTable *table, u16 width, const f32 projmtx[4][4]) {
+ f32 xi;
+ f32 iw;
+ f32 rangeVal;
+ f32 nearZ;
+ f32 sideX;
+ u32 i;
+
+ CHECK_GXBEGIN(275, "GXInitFogAdjTable");
+ ASSERTMSGLINE(276, table != NULL, "GXInitFogAdjTable: table pointer is null");
+ ASSERTMSGLINE(277, width <= 640, "GXInitFogAdjTable: invalid width value");
+
+ if (0.0 == projmtx[3][3]) {
+ nearZ = projmtx[2][3] / (projmtx[2][2] - 1.0f);
+ sideX = nearZ / projmtx[0][0];
+ } else {
+ sideX = 1.0f / projmtx[0][0];
+ nearZ = 1.73205f * sideX;
+ }
+
+ iw = 2.0f / width;
+ for (i = 0; i < 10; i++) {
+ xi = (i + 1) << 5;
+ xi *= iw;
+ xi *= sideX;
+ rangeVal = sqrtf(1.0f + ((xi * xi) / (nearZ * nearZ)));
+ table->r[i] = (u32)(256.0f * rangeVal) & 0xFFF;
+ }
+}
+
+void GXSetFogRangeAdj(GXBool enable, u16 center, const GXFogAdjTable *table) {
+ u32 i;
+ u32 range_adj;
+ u32 range_c;
+
+ CHECK_GXBEGIN(347, "GXSetFogRangeAdj");
+
+ if (enable) {
+ ASSERTMSGLINE(350, table != NULL, "GXSetFogRangeAdj: table pointer is null");
+ for (i = 0; i < 10; i += 2) {
+ range_adj = 0;
+ SET_REG_FIELD(354, range_adj, 12, 0, table->r[i]);
+ SET_REG_FIELD(355, range_adj, 12, 12, table->r[i + 1]);
+ SET_REG_FIELD(356, range_adj, 8, 24, (i >> 1) + 0xE9);
+ GX_WRITE_RAS_REG(range_adj);
+ }
+ }
+ range_c = 0;
+ SET_REG_FIELD(362, range_c, 10, 0, center + 342);
+ SET_REG_FIELD(363, range_c, 1, 10, enable);
+ SET_REG_FIELD(364, range_c, 8, 24, 0xE8);
+ GX_WRITE_RAS_REG(range_c);
+ __GXData->bpSentNot = 0;
+}
+
+void GXSetBlendMode(GXBlendMode type, GXBlendFactor src_factor, GXBlendFactor dst_factor, GXLogicOp op) {
+ u32 reg;
+ u32 blend_en;
+
+ CHECK_GXBEGIN(391, "GXSetBlendMode");
+
+ reg = __GXData->cmode0;
+
+#if DEBUG
+ blend_en = type == GX_BM_BLEND || type == GX_BM_SUBTRACT;
+#endif
+
+ SET_REG_FIELD(405, reg, 1, 11, (type == GX_BM_SUBTRACT));
+#if DEBUG
+ SET_REG_FIELD(408, reg, 1, 0, blend_en);
+#else
+ SET_REG_FIELD(408, reg, 1, 0, type);
+#endif
+ SET_REG_FIELD(409, reg, 1, 1, (type == GX_BM_LOGIC));
+ SET_REG_FIELD(410, reg, 4, 12, op);
+ SET_REG_FIELD(411, reg, 3, 8, src_factor);
+ SET_REG_FIELD(412, reg, 3, 5, dst_factor);
+ GX_WRITE_RAS_REG(reg);
+
+ __GXData->cmode0 = reg;
+ __GXData->bpSentNot = 0;
+}
+
+void GXSetColorUpdate(GXBool update_enable) {
+ u32 reg;
+ CHECK_GXBEGIN(435, "GXSetColorUpdate");
+
+ reg = __GXData->cmode0;
+
+ SET_REG_FIELD(437, reg, 1, 3, update_enable);
+ GX_WRITE_RAS_REG(reg);
+
+ __GXData->cmode0 = reg;
+ __GXData->bpSentNot = 0;
+}
+
+void GXSetAlphaUpdate(GXBool update_enable) {
+ u32 reg;
+ CHECK_GXBEGIN(474, "GXSetAlphaUpdate");
+
+ reg = __GXData->cmode0;
+
+ SET_REG_FIELD(476, reg, 1, 4, update_enable);
+ GX_WRITE_RAS_REG(reg);
+
+ __GXData->cmode0 = reg;
+ __GXData->bpSentNot = 0;
+}
+
+void GXSetZMode(GXBool compare_enable, GXCompare func, GXBool update_enable) {
+ u32 reg;
+ CHECK_GXBEGIN(517, "GXSetZMode");
+
+ reg = __GXData->zmode;
+
+ SET_REG_FIELD(520, reg, 1, 0, compare_enable);
+ SET_REG_FIELD(521, reg, 3, 1, func);
+ SET_REG_FIELD(522, reg, 1, 4, update_enable);
+ GX_WRITE_RAS_REG(reg);
+
+ __GXData->zmode = reg;
+ __GXData->bpSentNot = 0;
+}
+
+void GXSetZCompLoc(GXBool before_tex) {
+ CHECK_GXBEGIN(532, "GXSetZCompLoc");
+ SET_REG_FIELD(533, __GXData->peCtrl, 1, 6, before_tex);
+ GX_WRITE_RAS_REG(__GXData->peCtrl);
+ __GXData->bpSentNot = 0;
+}
+
+void GXSetPixelFmt(GXPixelFmt pix_fmt, GXZFmt16 z_fmt) {
+ u32 oldPeCtrl;
+ u8 aa;
+ static u32 p2f[8] = { 0, 1, 2, 3, 4, 4, 4, 5 };
+
+ CHECK_GXBEGIN(569, "GXSetPixelFmt");
+ oldPeCtrl = __GXData->peCtrl;
+ ASSERTMSGLINE(573, pix_fmt >= GX_PF_RGB8_Z24 && pix_fmt <= GX_PF_YUV420, "Invalid Pixel format");
+ SET_REG_FIELD(575, __GXData->peCtrl, 3, 0, p2f[pix_fmt]);
+ SET_REG_FIELD(576, __GXData->peCtrl, 3, 3, z_fmt);
+
+ if (oldPeCtrl != __GXData->peCtrl) {
+ GX_WRITE_RAS_REG(__GXData->peCtrl);
+
+ aa = ((pix_fmt == GX_PF_RGB565_Z16) ? GX_TRUE : GX_FALSE);
+ SET_REG_FIELD(585, __GXData->genMode, 1, 9, aa);
+ __GXData->dirtyState |= 4;
+ }
+
+ if (p2f[pix_fmt] == 4) {
+ SET_REG_FIELD(592, __GXData->cmode1, 2, 9, (pix_fmt - 4) & 0x3);
+ SET_REG_FIELD(592, __GXData->cmode1, 8, 24, 0x42);
+ GX_WRITE_RAS_REG(__GXData->cmode1);
+ }
+
+ __GXData->bpSentNot = 0;
+}
+
+void GXSetDither(GXBool dither) {
+ u32 reg;
+ CHECK_GXBEGIN(634, "GXSetDither");
+
+ reg = __GXData->cmode0;
+
+ SET_REG_FIELD(637, reg, 1, 2, dither);
+ GX_WRITE_RAS_REG(reg);
+
+ __GXData->cmode0 = reg;
+ __GXData->bpSentNot = 0;
+}
+
+void GXSetDstAlpha(GXBool enable, u8 alpha) {
+ u32 reg;
+ CHECK_GXBEGIN(675, "GXSetDstAlpha");
+
+ reg = __GXData->cmode1;
+
+ SET_REG_FIELD(678, reg, 8, 0, alpha);
+ SET_REG_FIELD(679, reg, 1, 8, enable);
+ GX_WRITE_RAS_REG(reg);
+
+ __GXData->cmode1 = reg;
+ __GXData->bpSentNot = 0;
+}
+
+void GXSetFieldMask(GXBool odd_mask, GXBool even_mask) {
+ u32 reg;
+
+ CHECK_GXBEGIN(702, "GXSetFieldMask");
+ reg = 0;
+ SET_REG_FIELD(704, reg, 1, 0, even_mask);
+ SET_REG_FIELD(705, reg, 1, 1, odd_mask);
+ SET_REG_FIELD(705, reg, 8, 24, 0x44);
+ GX_WRITE_RAS_REG(reg);
+ __GXData->bpSentNot = 0;
+}
+
+void GXSetFieldMode(GXBool field_mode, GXBool half_aspect_ratio) {
+ u32 reg;
+
+ CHECK_GXBEGIN(731, "GXSetFieldMode");
+ SET_REG_FIELD(735, __GXData->lpSize, 1, 22, half_aspect_ratio);
+ GX_WRITE_RAS_REG(__GXData->lpSize);
+ __GXFlushTextureState();
+ reg = field_mode | 0x68000000;
+ GX_WRITE_RAS_REG(reg);
+ __GXFlushTextureState();
+}
diff --git a/src/revolution/gx/GXSave.c b/src/revolution/gx/GXSave.c
new file mode 100644
index 0000000000..5f3ff31413
--- /dev/null
+++ b/src/revolution/gx/GXSave.c
@@ -0,0 +1,528 @@
+#if DEBUG
+
+#include <revolution/gx.h>
+#include <revolution/os.h>
+
+#include "__gx.h"
+
+static const u8* dlist;
+static u32 dlistSize;
+static u32 bytesRead;
+
+// prototypes
+void __GXShadowIndexState(u32 idx_reg, u32 reg_data);
+
+static u8 __ReadMem(void* ptr, u32 sz) {
+ const u8* src;
+ u8* dst;
+ u32 i;
+
+ if (sz > dlistSize - bytesRead) {
+ return FALSE;
+ }
+
+ src = dlist;
+ dst = ptr;
+ for (i = 0; i < sz; i++) {
+ *dst++ = *src++;
+ }
+ bytesRead += sz;
+ dlist += sz;
+ return TRUE;
+}
+
+inline void DPF(char*, ...) {
+ u8 unused[4];
+}
+
+static void __SaveCPRegs(u8 reg, u8 vatIdx, u32 data) {
+ s32 idx;
+
+ DPF("\tCP Stream Reg[0x%x] = 0x%x\n", reg, data);
+
+ switch (reg) {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ break;
+ case 5:
+ __GXData->vcdLo = data;
+ break;
+ case 6:
+ __GXData->vcdHi = data;
+ break;
+ case 7:
+ __GXData->vatA[vatIdx] = data;
+ break;
+ case 8:
+ __GXData->vatB[vatIdx] = data;
+ break;
+ case 9:
+ __GXData->vatC[vatIdx] = data;
+ break;
+ case 10:
+ idx = vatIdx - 0x15;
+ if ((idx >= 0) && (idx < 4)) {
+ __GXData->indexBase[idx] = data;
+ }
+ break;
+ case 11:
+ idx = vatIdx - 0x15;
+ if ((idx >= 0) && (idx < 4)) {
+ __GXData->indexStride[idx] = data;
+ }
+ break;
+ default:
+ if (__gxVerif->verifyLevel >= __gxvWarnLev[GXWARN_DL_INV_CMD]) {
+ __GX_WARN(GXWARN_DL_INV_CMD);
+ }
+ OSReport("[Invalid CP Stream Register Address 0x%x\n]", reg);
+ break;
+ }
+}
+
+static void __ReconstVtxStatus(u8 vatIdx) {
+ u32 vat;
+
+ if (GET_REG_FIELD(__GXData->vcdLo, 2, 11) & 3) {
+ vat = __GXData->vatA[vatIdx];
+ if ((vat >> 9) & 1) {
+ __GXData->hasNrms = 0;
+ __GXData->hasBiNrms = 1;
+ } else {
+ __GXData->hasNrms = 1;
+ __GXData->hasBiNrms = 0;
+ }
+ }
+}
+
+static u32 vtxCompSize[5] = { 1, 1, 2, 2, 4 };
+static int clrCompSize[6] = { 2, 3, 4, 2, 3, 4 };
+
+static u32 GetAttrSize(u8 vatIdx, u32 attrIdx) {
+ u32 vcd;
+ u32 vat;
+ u32 nc;
+
+ switch (attrIdx) {
+ case 0:
+ return GET_REG_FIELD(__GXData->vcdLo, 1, 0) ? 1 : 0;
+ case 1:
+ return GET_REG_FIELD(__GXData->vcdLo, 1, 1) ? 1 : 0;
+ case 2:
+ return GET_REG_FIELD(__GXData->vcdLo, 1, 2) ? 1 : 0;
+ case 3:
+ return GET_REG_FIELD(__GXData->vcdLo, 1, 3) ? 1 : 0;
+ case 4:
+ return GET_REG_FIELD(__GXData->vcdLo, 1, 4) ? 1 : 0;
+ case 5:
+ return GET_REG_FIELD(__GXData->vcdLo, 1, 5) ? 1 : 0;
+ case 6:
+ return GET_REG_FIELD(__GXData->vcdLo, 1, 6) ? 1 : 0;
+ case 7:
+ return GET_REG_FIELD(__GXData->vcdLo, 1, 7) ? 1 : 0;
+ case 8:
+ return GET_REG_FIELD(__GXData->vcdLo, 1, 8) ? 1 : 0;
+ case 9:
+ vcd = __GXData->vcdLo;
+ vat = __GXData->vatA[vatIdx & 0xFF];
+ switch (GET_REG_FIELD(vcd, 2, 9)) {
+ case 0:
+ return 0;
+ case 2:
+ return 1;
+ case 3:
+ return 2;
+ case 1:
+ return ((vat & 1) + 2) * vtxCompSize[(vat >> 1) & 7];
+ }
+ break;
+ case 10:
+ vcd = __GXData->vcdLo;
+ vat = __GXData->vatA[vatIdx & 0xFF];
+
+ switch (GET_REG_FIELD(vcd, 2, 11)) {
+ case 0:
+ return 0;
+ case 2:
+ if ((vat >> 9) & 1 && vat >> 31) {
+ nc = 3;
+ } else {
+ nc = 1;
+ }
+ return nc;
+ case 3:
+ if ((vat >> 9) & 1 && vat >> 31) {
+ nc = 6;
+ } else {
+ nc = 2;
+ }
+ return nc;
+ case 1:
+ if ((vat >> 9) & 1) {
+ nc = 9;
+ } else {
+ nc = 3;
+ }
+ return nc * vtxCompSize[(vat >> 10) & 7];
+ }
+ break;
+ case 11:
+ switch (GET_REG_FIELD(__GXData->vcdLo, 2, 13)) {
+ case 0:
+ return 0;
+ case 2:
+ return 1;
+ case 3:
+ return 2;
+ case 1:
+ vat = __GXData->vatA[vatIdx];
+ return clrCompSize[(vat >> 14) & 7];
+ }
+ break;
+ case 12:
+ switch (GET_REG_FIELD(__GXData->vcdLo, 2, 15)) {
+ case 0:
+ return 0;
+ case 2:
+ return 1;
+ case 3:
+ return 2;
+ case 1:
+ vat = __GXData->vatA[vatIdx];
+ return clrCompSize[(vat >> 18) & 7];
+ }
+ break;
+ case 13:
+ vcd = __GXData->vcdHi;
+ vat = __GXData->vatA[vatIdx & 0xFF];
+ switch (GET_REG_FIELD(vcd, 2, 0)) {
+ case 0:
+ return 0;
+ case 2:
+ return 1;
+ case 3:
+ return 2;
+ case 1:
+ return (((vat >> 21) & 1) + 1) * vtxCompSize[(vat >> 22) & 7];
+ }
+ break;
+ case 14:
+ vcd = __GXData->vcdHi;
+ vat = __GXData->vatB[vatIdx & 0xFF];
+ switch (GET_REG_FIELD(vcd, 2, 2)) {
+ case 0:
+ return 0;
+ case 2:
+ return 1;
+ case 3:
+ return 2;
+ case 1:
+ return (((vat >> 0) & 1) + 1) * vtxCompSize[(vat >> 1) & 7];
+ }
+ break;
+ case 15:
+ vcd = __GXData->vcdHi;
+ vat = __GXData->vatB[vatIdx & 0xFF];
+ switch (GET_REG_FIELD(vcd, 2, 4)) {
+ case 0:
+ return 0;
+ case 2:
+ return 1;
+ case 3:
+ return 2;
+ case 1:
+ return (((vat >> 9) & 1) + 1) * vtxCompSize[(vat >> 10) & 7];
+ }
+ break;
+ case 16:
+ vcd = __GXData->vcdHi;
+ vat = __GXData->vatB[vatIdx & 0xFF];
+ switch (GET_REG_FIELD(vcd, 2, 6)) {
+ case 0:
+ return 0;
+ case 2:
+ return 1;
+ case 3:
+ return 2;
+ case 1:
+ return (((vat >> 18) & 1) + 1) * vtxCompSize[(vat >> 19) & 7];
+ }
+ break;
+ case 17:
+ vcd = __GXData->vcdHi;
+ vat = __GXData->vatB[vatIdx & 0xFF];
+ switch (GET_REG_FIELD(vcd, 2, 8)) {
+ case 0:
+ return 0;
+ case 2:
+ return 1;
+ case 3:
+ return 2;
+ case 1:
+ return (((vat >> 27) & 1) + 1) * vtxCompSize[(vat >> 28) & 7];
+ }
+ break;
+ case 18:
+ vcd = __GXData->vcdHi;
+ vat = __GXData->vatC[vatIdx & 0xFF];
+ switch (GET_REG_FIELD(vcd, 2, 10)) {
+ case 0:
+ return 0;
+ case 2:
+ return 1;
+ case 3:
+ return 2;
+ case 1:
+ return (((vat >> 5) & 1) + 1) * vtxCompSize[(vat >> 6) & 7];
+ }
+ break;
+ case 19:
+ vcd = __GXData->vcdHi;
+ vat = __GXData->vatC[vatIdx & 0xFF];
+ switch (GET_REG_FIELD(vcd, 2, 12)) {
+ case 0:
+ return 0;
+ case 2:
+ return 1;
+ case 3:
+ return 2;
+ case 1:
+ return (((vat >> 14) & 1) + 1) * vtxCompSize[(vat >> 15) & 7];
+ }
+ break;
+ case 20:
+ vcd = __GXData->vcdHi;
+ vat = __GXData->vatC[vatIdx & 0xFF];
+ switch (GET_REG_FIELD(vcd, 2, 14)) {
+ case 0:
+ return 0;
+ case 2:
+ return 1;
+ case 3:
+ return 2;
+ case 1:
+ return (((vat >> 23) & 1) + 1) * vtxCompSize[(vat >> 24) & 7];
+ }
+ break;
+ }
+ return 0;
+}
+
+static void __ParseVertexData(u8 vatIdx) {
+ u16 vcnt;
+ GXAttr attrIdx;
+ u32 vsize;
+
+ if (__ReadMem(&vcnt, 2)) {
+ vsize = 0;
+ for (attrIdx = 0; attrIdx < GX_VA_MAX_ATTR; attrIdx++) {
+ if (attrIdx != GX_VA_NBT) {
+ vsize += GetAttrSize(vatIdx, attrIdx);
+ }
+ }
+ vsize *= vcnt;
+ dlist += vsize;
+ bytesRead += vsize;
+ }
+}
+
+void __GXShadowDispList(void* list, u32 nbytes) {
+ u8 cmd;
+ u8 cmdOp;
+ u8 vatIdx;
+ u32 reg32;
+ u32 d32;
+ u8 reg8;
+ u8 cpAddr;
+ u32 i;
+ u32 addr;
+ u32 cnt;
+
+ if (__gxVerif->verifyLevel == GX_WARN_NONE) {
+ return;
+ }
+
+ dlist = list;
+ dlistSize = nbytes;
+ bytesRead = 0;
+
+ DPF("Displaylist IN\n");
+
+ while (dlistSize > bytesRead) {
+ if (!__ReadMem(&cmd, 1)) {
+ break;
+ }
+ cmdOp = (u32)GET_REG_FIELD((u32)cmd, 5, 3);
+ vatIdx = cmd & 7;
+ switch (cmdOp) {
+ case 0:
+ case 9:
+ break;
+ case 16:
+ case 18:
+ case 19:
+ case 20:
+ case 21:
+ case 22:
+ case 23:
+ __ReconstVtxStatus(vatIdx);
+ __GXVerifyState(vatIdx);
+ __ParseVertexData(vatIdx);
+ break;
+ case 1:
+ if (__ReadMem(&reg8, 1) && __ReadMem(&d32, 4)) {
+ vatIdx = reg8 & 0xF;
+ cpAddr = (reg8& 0xF0) >> 4;
+ __SaveCPRegs(cpAddr, vatIdx, d32);
+ }
+ break;
+ case 2:
+ if (__ReadMem(&reg32, 4)) {
+ cnt = GET_REG_FIELD(reg32, 4, 16) + 1;
+ addr = (u16)reg32;
+ DPF("\tXFReg = 0x%x, Cnt = %d\n", addr, cnt);
+ for (i = 0; i < cnt; i++) {
+ if (__ReadMem(&d32, 4)) {
+ DPF("\tXFData = 0x%x\n", d32);
+ VERIF_MTXLIGHT(addr, d32);
+ addr++;
+ }
+ }
+ }
+ break;
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ if (__ReadMem(&reg32, 4)) {
+ DPF("\tXF_INDEX_LOAD: = 0x%x\n", reg32);
+ __GXShadowIndexState(cmdOp, reg32);
+ }
+ break;
+ case 8:
+ if (__gxVerif->verifyLevel >= __gxvWarnLev[GXWARN_DL_NESTED]) {
+ __GX_WARN(GXWARN_DL_NESTED);
+ }
+ return;
+ case 12:
+ case 13:
+ if (__ReadMem(&reg32, 4)) {
+ DPF("\tSU Bypass = 0x%x\n", reg32);
+ __gxVerif->rasRegs[(reg32 >> 24) & 0xFF] = reg32;
+ }
+ break;
+ default:
+ if (__gxVerif->verifyLevel >= __gxvWarnLev[GXWARN_DL_INV_CMD]) {
+ __GX_WARN(GXWARN_DL_INV_CMD);
+ }
+ OSReport("[Bad Display List Command: 0x%02X\n]", cmdOp);
+ break;
+ }
+ }
+
+ DPF("Displaylist OUT\n");
+}
+
+void __GXShadowIndexState(u32 idx_reg, u32 reg_data) {
+ u32* basePtr;
+ u32* memAddr;
+ u32 cnt;
+ u32 stride;
+ u32 addr;
+ u32 data;
+ u32 index;
+ u32 i;
+
+ i = idx_reg - 4;
+ basePtr = OSPhysicalToCached(__GXData->indexBase[i]);
+ stride = __GXData->indexStride[i];
+ addr = reg_data & 0xFFF;
+ cnt = (reg_data >> 12) & 0xF;
+ index = reg_data >> 16;
+ memAddr = (u32*)((u8*)basePtr + (index * stride));
+ cnt++;
+
+ while (cnt-- != 0) {
+ data = *memAddr;
+ VERIF_MTXLIGHT(addr, data);
+ memAddr = (u32*)((u8*)memAddr + stride);
+ addr++;
+ }
+
+ &data; // needed to match
+}
+
+void __GXPrintShadowState(void) {
+ u32 i;
+ u32 j;
+
+ OSReport("CP State:\n");
+ OSReport("\tvcdLo = 0x%x\n", __GXData->vcdLo);
+ OSReport("\tvcdHi = 0x%x\n", __GXData->vcdHi);
+ OSReport("\thasBiNrms = 0x%x\n", __GXData->hasBiNrms);
+
+ for (i = 0; i < 8; i++) {
+ OSReport("\tVertex Format %d:\n", i);
+ OSReport("\t\tvatA = 0x%x\n", __GXData->vatA[i]);
+ OSReport("\t\tvatB = 0x%x\n", __GXData->vatB[i]);
+ OSReport("\t\tvatC = 0x%x\n", __GXData->vatC[i]);
+ }
+
+ OSReport("\n-------------------------------------\n");
+ OSReport("XF Pos/Tex Matrix State:\n");
+
+ for (i = 0; i < 256; i += 4) {
+ if (__gxVerif->xfMtxDirty[i]) {
+ OSReport("\tXF_MATRIX[%d] = ", i);
+ OSReport("%f, %f, %f, %f\n", *(f32*)&__gxVerif->xfMtx[i], *(f32*)&__gxVerif->xfMtx[i + 1], *(f32*)&__gxVerif->xfMtx[i + 2], *(f32*)&__gxVerif->xfMtx[i + 3]);
+ }
+ }
+
+ OSReport("\n-------------------------------------\n");
+ OSReport("XF Normal Matrix State:\n");
+
+ for (i = 0; i < 96; i += 3) {
+ if (__gxVerif->xfNrmDirty[i]) {
+ OSReport("\tXF_NRM_MTX[%d] = ", i);
+ OSReport("%f, %f, %f\n", *(f32*)&__gxVerif->xfMtx[i], *(f32*)&__gxVerif->xfMtx[i + 1], *(f32*)&__gxVerif->xfMtx[i + 2]);
+ }
+ }
+
+ OSReport("\n-------------------------------------\n");
+ OSReport("XF Light State:\n");
+
+ for (i = 0; i < 128; i += 16) {
+ if (__gxVerif->xfLightDirty[i]) {
+ OSReport("\tXF_LIGHT[%d]:\n", i >> 4);
+ for (j = 0; j < 4; j++) {
+ OSReport("\t\tparam[%d] = 0x%x\n", j, __gxVerif->xfLight[i + j]);
+ }
+ for (j = 4; j < 16; j++) {
+ OSReport("\t\tparam[%d] = %Lg\n", j, *(f32*)&__gxVerif->xfLight[i + j]);
+ }
+ }
+ }
+
+ OSReport("\n-------------------------------------\n");
+ OSReport("XF Register State:\n");
+
+ for (i = 0; i < 80; i++) {
+ if (__gxVerif->xfRegsDirty[i]) {
+ OSReport("\tXF_REG[0x%x] = 0x%x (%f)\n", i, __gxVerif->xfRegs[i], *(f32*)&__gxVerif->xfRegs[i]);
+ }
+ }
+
+ OSReport("\n-------------------------------------\n");
+ OSReport("Raster Registers State:\n");
+
+ for (i = 0; i < 256; i++) {
+ OSReport("\tRAS_REG[0x%x] = 0x%x\n", i, __gxVerif->rasRegs[i]);
+ }
+
+ OSReport("\n-------------------------------------\n");
+}
+
+#endif
diff --git a/src/revolution/gx/GXStubs.c b/src/revolution/gx/GXStubs.c
new file mode 100644
index 0000000000..8716e2ea26
--- /dev/null
+++ b/src/revolution/gx/GXStubs.c
@@ -0,0 +1,5 @@
+#include <revolution/gx.h>
+
+#include "__gx.h"
+
+void __GXSetRange(f32 nearz, f32 fgSideX) {}
diff --git a/src/revolution/gx/GXTev.c b/src/revolution/gx/GXTev.c
new file mode 100644
index 0000000000..1565865615
--- /dev/null
+++ b/src/revolution/gx/GXTev.c
@@ -0,0 +1,465 @@
+#include <revolution/gx.h>
+#include <revolution/os.h>
+
+#include "__gx.h"
+
+static struct {
+ u32 rid : 8;
+ u32 dest : 2;
+ u32 shift : 2;
+ u32 clamp : 1;
+ u32 sub : 1;
+ u32 bias : 2;
+ u32 sela : 4;
+ u32 selb : 4;
+ u32 selc : 4;
+ u32 seld : 4;
+} TEVCOpTableST0[5] = {
+ {192, 0, 0, 1, 0, 0, 15, 8, 10, 15}, // modulate
+ {192, 0, 0, 1, 0, 0, 10, 8, 9, 15}, // decal
+ {192, 0, 0, 1, 0, 0, 10, 12, 8, 15}, // blend
+ {192, 0, 0, 1, 0, 0, 15, 15, 15, 8}, // replace
+ {192, 0, 0, 1, 0, 0, 15, 15, 15, 10}, // passclr
+};
+
+static struct {
+ u32 rid : 8;
+ u32 dest : 2;
+ u32 shift : 2;
+ u32 clamp : 1;
+ u32 sub : 1;
+ u32 bias : 2;
+ u32 sela : 4;
+ u32 selb : 4;
+ u32 selc : 4;
+ u32 seld : 4;
+} TEVCOpTableST1[5] = {
+ {192, 0, 0, 1, 0, 0, 15, 8, 0, 15}, // modulate
+ {192, 0, 0, 1, 0, 0, 0, 8, 9, 15}, // decal
+ {192, 0, 0, 1, 0, 0, 0, 12, 8, 15}, // blend
+ {192, 0, 0, 1, 0, 0, 15, 15, 15, 8}, // replace
+ {192, 0, 0, 1, 0, 0, 15, 15, 15, 0}, // passclr
+};
+
+static struct {
+ u32 rid : 8;
+ u32 dest : 2;
+ u32 shift : 2;
+ u32 clamp : 1;
+ u32 sub : 1;
+ u32 bias : 2;
+ u32 sela : 3;
+ u32 selb : 3;
+ u32 selc : 3;
+ u32 seld : 3;
+ u32 swap : 2;
+ u32 mode : 2;
+} TEVAOpTableST0[5] = {
+ {193, 0, 0, 1, 0, 0, 7, 4, 5, 7, 0, 0}, // modulate
+ {193, 0, 0, 1, 0, 0, 7, 7, 7, 5, 0, 0}, // decal
+ {193, 0, 0, 1, 0, 0, 7, 4, 5, 7, 0, 0}, // blend
+ {193, 0, 0, 1, 0, 0, 7, 7, 7, 4, 0, 0}, // replace
+ {193, 0, 0, 1, 0, 0, 7, 7, 7, 5, 0, 0}, // passclr
+};
+
+static struct {
+ u32 rid : 8;
+ u32 dest : 2;
+ u32 shift : 2;
+ u32 clamp : 1;
+ u32 sub : 1;
+ u32 bias : 2;
+ u32 sela : 3;
+ u32 selb : 3;
+ u32 selc : 3;
+ u32 seld : 3;
+ u32 swap : 2;
+ u32 mode : 2;
+} TEVAOpTableST1[5] = {
+ {193, 0, 0, 1, 0, 0, 7, 4, 0, 7, 0, 0}, // modulate
+ {193, 0, 0, 1, 0, 0, 7, 7, 7, 0, 0, 0}, // decal
+ {193, 0, 0, 1, 0, 0, 7, 4, 0, 7, 0, 0}, // blend
+ {193, 0, 0, 1, 0, 0, 7, 7, 7, 4, 0, 0}, // replace
+ {193, 0, 0, 1, 0, 0, 7, 7, 7, 0, 0, 0}, // passclr
+};
+
+void GXSetTevOp(GXTevStageID id, GXTevMode mode) {
+ u32* ctmp;
+ u32* atmp;
+ u32 tevReg;
+
+ CHECK_GXBEGIN(425, "GXSetTevOp");
+ ASSERTMSGLINE(426, id < GX_MAX_TEVSTAGE, "GXSetTevColor*: Invalid Tev Stage Index");
+ ASSERTMSGLINE(427, mode <= GX_PASSCLR, "GXSetTevOp: Invalid Tev Mode");
+
+ if (id == GX_TEVSTAGE0) {
+ ctmp = (u32*)TEVCOpTableST0 + mode;
+ atmp = (u32*)TEVAOpTableST0 + mode;
+ } else {
+ ctmp = (u32*)TEVCOpTableST1 + mode;
+ atmp = (u32*)TEVAOpTableST1 + mode;
+ }
+
+ tevReg = __GXData->tevc[id];
+ tevReg = (*ctmp & ~0xFF000000) | (tevReg & 0xFF000000);
+ GX_WRITE_RAS_REG(tevReg);
+ __GXData->tevc[id] = tevReg;
+
+ tevReg = __GXData->teva[id];
+ tevReg = (*atmp & ~0xFF00000F) | (tevReg & 0xFF00000F);
+ GX_WRITE_RAS_REG(tevReg);
+ __GXData->teva[id] = tevReg;
+
+ __GXData->bpSentNot = 0;
+}
+
+void GXSetTevColorIn(GXTevStageID stage, GXTevColorArg a, GXTevColorArg b, GXTevColorArg c, GXTevColorArg d) {
+ u32 tevReg;
+
+ CHECK_GXBEGIN(583, "GXSetTevColorIn");
+ ASSERTMSGLINE(584, stage < GX_MAX_TEVSTAGE, "GXSetTevColor*: Invalid Tev Stage Index");
+ ASSERTMSGLINE(585, a <= GX_CC_ZERO, "GXSetTev*In: A/B/C/D argument out of range");
+ ASSERTMSGLINE(586, b <= GX_CC_ZERO, "GXSetTev*In: A/B/C/D argument out of range");
+ ASSERTMSGLINE(587, c <= GX_CC_ZERO, "GXSetTev*In: A/B/C/D argument out of range");
+ ASSERTMSGLINE(588, d <= GX_CC_ZERO, "GXSetTev*In: A/B/C/D argument out of range");
+
+ tevReg = __GXData->tevc[stage];
+ SC_TEV_COLOR_ENV_SET_SELA(591, tevReg, a);
+ SC_TEV_COLOR_ENV_SET_SELB(592, tevReg, b);
+ SC_TEV_COLOR_ENV_SET_SELC(593, tevReg, c);
+ SC_TEV_COLOR_ENV_SET_SELD(594, tevReg, d);
+
+ GX_WRITE_RAS_REG(tevReg);
+ __GXData->tevc[stage] = tevReg;
+ __GXData->bpSentNot = 0;
+}
+
+void GXSetTevAlphaIn(GXTevStageID stage, GXTevAlphaArg a, GXTevAlphaArg b, GXTevAlphaArg c, GXTevAlphaArg d) {
+ u32 tevReg;
+
+ CHECK_GXBEGIN(619, "GXSetTevAlphaIn");
+ ASSERTMSGLINE(620, stage < GX_MAX_TEVSTAGE, "GXSetTevAlpha*: Invalid Tev Stage Index");
+ ASSERTMSGLINE(621, a <= GX_CA_ZERO, "GXSetTev*In: A/B/C/D argument out of range");
+ ASSERTMSGLINE(622, b <= GX_CA_ZERO, "GXSetTev*In: A/B/C/D argument out of range");
+ ASSERTMSGLINE(623, c <= GX_CA_ZERO, "GXSetTev*In: A/B/C/D argument out of range");
+ ASSERTMSGLINE(624, d <= GX_CA_ZERO, "GXSetTev*In: A/B/C/D argument out of range");
+
+ tevReg = __GXData->teva[stage];
+ SC_TEV_ALPHA_ENV_SET_SELA(627, tevReg, a);
+ SC_TEV_ALPHA_ENV_SET_SELB(628, tevReg, b);
+ SC_TEV_ALPHA_ENV_SET_SELC(629, tevReg, c);
+ SC_TEV_ALPHA_ENV_SET_SELD(630, tevReg, d);
+
+ GX_WRITE_RAS_REG(tevReg);
+ __GXData->teva[stage] = tevReg;
+ __GXData->bpSentNot = 0;
+}
+
+void GXSetTevColorOp(GXTevStageID stage, GXTevOp op, GXTevBias bias, GXTevScale scale, GXBool clamp, GXTevRegID out_reg) {
+ u32 tevReg;
+
+ CHECK_GXBEGIN(658, "GXSetTevColorOp");
+ ASSERTMSGLINE(659, stage < GX_MAX_TEVSTAGE, "GXSetTevColor*: Invalid Tev Stage Index");
+
+ tevReg = __GXData->tevc[stage];
+ SC_TEV_COLOR_ENV_SET_SUB(668, tevReg, op & 1);
+ if (op <= 1) {
+ SC_TEV_COLOR_ENV_SET_SHIFT(670, tevReg, scale);
+ SC_TEV_COLOR_ENV_SET_BIAS(671, tevReg, bias);
+ } else {
+ SC_TEV_COLOR_ENV_SET_SHIFT(673, tevReg, (op >> 1) & 3);
+ SC_TEV_COLOR_ENV_SET_BIAS(674, tevReg, 3);
+ }
+ SC_TEV_COLOR_ENV_SET_CLAMP(677, tevReg, clamp);
+ SC_TEV_COLOR_ENV_SET_DEST(678, tevReg, out_reg);
+
+ GX_WRITE_RAS_REG(tevReg);
+ __GXData->tevc[stage] = tevReg;
+ __GXData->bpSentNot = 0;
+}
+
+void GXSetTevAlphaOp(GXTevStageID stage, GXTevOp op, GXTevBias bias, GXTevScale scale, GXBool clamp, GXTevRegID out_reg) {
+ u32 tevReg;
+
+ CHECK_GXBEGIN(704, "GXSetTevAlphaOp");
+ ASSERTMSGLINE(705, stage < GX_MAX_TEVSTAGE, "GXSetTevAlpha*: Invalid Tev Stage Index");
+
+ tevReg = __GXData->teva[stage];
+ SC_TEV_ALPHA_ENV_SET_SUB(713, tevReg, op & 1);
+ if (op <= 1) {
+ SC_TEV_ALPHA_ENV_SET_SHIFT(715, tevReg, scale);
+ SC_TEV_ALPHA_ENV_SET_BIAS(716, tevReg, bias);
+ } else {
+ SC_TEV_ALPHA_ENV_SET_SHIFT(718, tevReg, (op >> 1) & 3);
+ SC_TEV_ALPHA_ENV_SET_BIAS(719, tevReg, 3);
+ }
+ SC_TEV_ALPHA_ENV_SET_CLAMP(722, tevReg, clamp);
+ SC_TEV_ALPHA_ENV_SET_DEST(723, tevReg, out_reg);
+
+ GX_WRITE_RAS_REG(tevReg);
+ __GXData->teva[stage] = tevReg;
+ __GXData->bpSentNot = 0;
+}
+
+void GXSetTevColor(GXTevRegID id, GXColor color) {
+ u32 rgba;
+ u32 regRA;
+ u32 regBG;
+
+ CHECK_GXBEGIN(745, "GXSetTevColor");
+ rgba = *(u32*)&color;
+
+ regRA = (0xE0 + id * 2) << 24;
+ SC_TEV_REGISTERL_SET_R8(750, regRA, rgba >> 24);
+ SC_TEV_REGISTERL_SET_A8(751, regRA, rgba & 0xFF);
+
+ regBG = (0xE1 + id * 2) << 24;
+ SC_TEV_REGISTERH_SET_B8(754, regBG, (rgba >> 8) & 0xFF);
+ SC_TEV_REGISTERH_SET_G8(755, regBG, (rgba >> 16) & 0xFF);
+
+ GX_WRITE_RAS_REG(regRA);
+ GX_WRITE_RAS_REG(regBG);
+ GX_WRITE_RAS_REG(regBG);
+ GX_WRITE_RAS_REG(regBG);
+
+ __GXData->bpSentNot = 0;
+}
+
+void GXSetTevColorS10(GXTevRegID id, GXColorS10 color) {
+ u32 sRG;
+ u32 sBA;
+ u32 regRA;
+ u32 regBG;
+
+ ASSERTMSGLINE(782, color.r >= -1024 && color.r < 1024, "GXSetTevColorS10: Color not in range -1024 to +1023");
+ ASSERTMSGLINE(783, color.g >= -1024 && color.g < 1024, "GXSetTevColorS10: Color not in range -1024 to +1023");
+ ASSERTMSGLINE(784, color.b >= -1024 && color.b < 1024, "GXSetTevColorS10: Color not in range -1024 to +1023");
+ ASSERTMSGLINE(785, color.a >= -1024 && color.a < 1024, "GXSetTevColorS10: Color not in range -1024 to +1023");
+
+ CHECK_GXBEGIN(787, "GXSetTevColorS10");
+ sRG = *(u32*)&color;
+ sBA = *((u32*)&color + 1);
+
+ regRA = (0xE0 + id * 2) << 24;
+ SC_TEV_REGISTERL_SET_R(794, regRA, (sRG >> 16) & 0x7FF);
+ SC_TEV_REGISTERL_SET_A(795, regRA, sBA & 0x7FF);
+
+ regBG = (0xE1 + id * 2) << 24;
+ SC_TEV_REGISTERH_SET_B(798, regBG, (sBA >> 16) & 0x7FF);
+ SC_TEV_REGISTERH_SET_G(799, regBG, sRG & 0x7FF);
+
+ GX_WRITE_RAS_REG(regRA);
+ GX_WRITE_RAS_REG(regBG);
+ GX_WRITE_RAS_REG(regBG);
+ GX_WRITE_RAS_REG(regBG);
+
+ __GXData->bpSentNot = 0;
+}
+
+void GXSetTevKColor(GXTevKColorID id, GXColor color) {
+ u32 rgba;
+ u32 regRA;
+ u32 regBG;
+
+ CHECK_GXBEGIN(838, "GXSetTevKColor");
+ rgba = *(u32*)&color;
+
+ regRA = (0xE0 + id * 2) << 24;
+ SC_TEV_KREGISTERL_SET_R(843, regRA, rgba >> 24);
+ SC_TEV_KREGISTERL_SET_A(844, regRA, rgba & 0xFF);
+ SC_TEV_KREGISTERL_SET_PAD1(845, regRA, 8);
+
+ regBG = (0xE1 + id * 2) << 24;
+ SC_TEV_KREGISTERH_SET_B(848, regBG, (rgba >> 8) & 0xFF);
+ SC_TEV_KREGISTERH_SET_G(849, regBG, (rgba >> 16) & 0xFF);
+ SC_TEV_KREGISTERH_SET_PAD1(850, regBG, 8);
+
+ GX_WRITE_RAS_REG(regRA);
+ GX_WRITE_RAS_REG(regBG);
+
+ __GXData->bpSentNot = 0;
+}
+
+void GXSetTevKColorSel(GXTevStageID stage, GXTevKColorSel sel) {
+ u32* Kreg;
+
+ CHECK_GXBEGIN(877, "GXSetTevKColorSel");
+ ASSERTMSGLINE(878, stage < GX_MAX_TEVSTAGE, "GXSetTevKColor*: Invalid Tev Stage Index");
+
+ Kreg = &__GXData->tevKsel[stage >> 1];
+ if (stage & 1) {
+ SC_TEV_KSEL_SET_KCSEL1(883, *Kreg, sel);
+ } else {
+ SC_TEV_KSEL_SET_KCSEL0(885, *Kreg, sel);
+ }
+
+ GX_WRITE_RAS_REG(*Kreg);
+ __GXData->bpSentNot = 0;
+}
+
+void GXSetTevKAlphaSel(GXTevStageID stage, GXTevKAlphaSel sel) {
+ u32* Kreg;
+
+ CHECK_GXBEGIN(910, "GXSetTevKAlphaSel");
+ ASSERTMSGLINE(911, stage < GX_MAX_TEVSTAGE, "GXSetTevKColor*: Invalid Tev Stage Index");
+
+ Kreg = &__GXData->tevKsel[stage >> 1];
+ if (stage & 1) {
+ SC_TEV_KSEL_SET_KASEL1(916, *Kreg, sel);
+ } else {
+ SC_TEV_KSEL_SET_KASEL0(918, *Kreg, sel);
+ }
+
+ GX_WRITE_RAS_REG(*Kreg);
+ __GXData->bpSentNot = 0;
+}
+
+void GXSetTevSwapMode(GXTevStageID stage, GXTevSwapSel ras_sel, GXTevSwapSel tex_sel) {
+ u32* pTevReg;
+
+ CHECK_GXBEGIN(947, "GXSetTevSwapMode");
+ ASSERTMSGLINE(948, stage < GX_MAX_TEVSTAGE, "GXSetTevSwapMode: Invalid Tev Stage Index");
+
+ pTevReg = &__GXData->teva[stage];
+ SC_TEV_ALPHA_ENV_SET_MODE(951, *pTevReg, ras_sel);
+ SC_TEV_ALPHA_ENV_SET_SWAP(952, *pTevReg, tex_sel);
+
+ GX_WRITE_RAS_REG(*pTevReg);
+ __GXData->bpSentNot = 0;
+}
+
+void GXSetTevSwapModeTable(GXTevSwapSel table, GXTevColorChan red, GXTevColorChan green, GXTevColorChan blue, GXTevColorChan alpha) {
+ u32* Kreg;
+#if !DEBUG
+ // not a real variable, but needed to match release
+ int index = table * 2;
+#endif
+
+ CHECK_GXBEGIN(983, "GXSetTevSwapModeTable");
+ ASSERTMSGLINE(984, table < GX_MAX_TEVSWAP, "GXSetTevSwapModeTable: Invalid Swap Selection Index");
+
+#if DEBUG
+ Kreg = &__GXData->tevKsel[table * 2];
+#else
+ Kreg = &__GXData->tevKsel[index];
+#endif
+ SC_TEV_KSEL_SET_XRB(987, *Kreg, red);
+ SC_TEV_KSEL_SET_XGA(988, *Kreg, green);
+
+ GX_WRITE_RAS_REG(*Kreg);
+
+ Kreg = &__GXData->tevKsel[table * 2 + 1];
+ SC_TEV_KSEL_SET_XRB(992, *Kreg, blue);
+ SC_TEV_KSEL_SET_XGA(993, *Kreg, alpha);
+
+ GX_WRITE_RAS_REG(*Kreg);
+ __GXData->bpSentNot = 0;
+}
+
+void GXSetTevClampMode(void) {
+ ASSERTMSGLINE(1012, 0, "GXSetTevClampMode: not available on this hardware");
+}
+
+void GXSetAlphaCompare(GXCompare comp0, u8 ref0, GXAlphaOp op, GXCompare comp1, u8 ref1) {
+ u32 reg;
+
+ CHECK_GXBEGIN(1051, "GXSetAlphaCompare");
+ reg = 0xF3000000;
+
+ SC_TEV_ALPHAFUNC_SET_A0(1054, reg, ref0);
+ SC_TEV_ALPHAFUNC_SET_A1(1055, reg, ref1);
+ SC_TEV_ALPHAFUNC_SET_OP0(1056, reg, comp0);
+ SC_TEV_ALPHAFUNC_SET_OP1(1057, reg, comp1);
+ SC_TEV_ALPHAFUNC_SET_LOGIC(1058, reg, op);
+
+ GX_WRITE_RAS_REG(reg);
+ __GXData->bpSentNot = 0;
+}
+
+void GXSetZTexture(GXZTexOp op, GXTexFmt fmt, u32 bias) {
+ u32 zenv0;
+ u32 zenv1;
+ u32 type;
+
+ CHECK_GXBEGIN(1082, "GXSetZTexture");
+
+ zenv0 = 0;
+ SC_TEV_Z_ENV_0_SET_ZOFF(1085, zenv0, bias);
+ SC_TEV_Z_ENV_0_SET_RID(1086, zenv0, 0xF4);
+
+ zenv1 = 0;
+ switch (fmt) {
+ case GX_TF_Z8:
+ type = 0;
+ break;
+ case GX_TF_Z16:
+ type = 1;
+ break;
+ case GX_TF_Z24X8:
+ type = 2;
+ break;
+ default:
+ ASSERTMSGLINE(1094, 0, "GXSetZTexture: Invalid z-texture format");
+ type = 2;
+ break;
+ }
+
+ SC_TEV_Z_ENV_1_SET_TYPE(1097, zenv1, type);
+ SC_TEV_Z_ENV_1_SET_OP(1098, zenv1, op);
+ SC_TEV_Z_ENV_1_SET_RID(1099, zenv1, 0xF5);
+
+ GX_WRITE_RAS_REG(zenv0);
+ GX_WRITE_RAS_REG(zenv1);
+ __GXData->bpSentNot = 0;
+}
+
+void GXSetTevOrder(GXTevStageID stage, GXTexCoordID coord, GXTexMapID map, GXChannelID color) {
+ u32* ptref;
+ u32 tmap;
+ u32 tcoord;
+ static int c2r[] = { 0, 1, 0, 1, 0, 1, 7, 5, 6 };
+
+ CHECK_GXBEGIN(1136, "GXSetTevOrder");
+ ASSERTMSGLINE(1137, stage < GX_MAX_TEVSTAGE, "GXSetTevOrder: Invalid Tev Stage Index");
+ ASSERTMSGLINE(1139, coord < GX_MAX_TEXCOORD || coord == GX_TEXCOORD_NULL, "GXSetTevOrder: Invalid Texcoord");
+ ASSERTMSGLINE(1141, (map & ~GX_TEX_DISABLE) < GX_MAX_TEXMAP || map == GX_TEXMAP_NULL, "GXSetTevOrder: Invalid Tex Map");
+ ASSERTMSGLINE(1143, color >= GX_COLOR0A0 && color <= GX_COLOR_NULL, "GXSetTevOrder: Invalid Color Channel ID");
+
+ ptref = &__GXData->tref[stage / 2];
+ __GXData->texmapId[stage] = map;
+
+ tmap = map & ~GX_TEX_DISABLE;
+ tmap = (tmap >= GX_MAX_TEXMAP) ? GX_TEXMAP0 : tmap;
+
+ if (coord >= GX_MAX_TEXCOORD) {
+ tcoord = GX_TEXCOORD0;
+ __GXData->tevTcEnab = __GXData->tevTcEnab & ~(1 << stage);
+ } else {
+ tcoord = coord;
+ __GXData->tevTcEnab = __GXData->tevTcEnab | (1 << stage);
+ }
+
+ if (stage & 1) {
+ SC_RAS1_TREF_SET_TI1(1163, *ptref, tmap);
+ SC_RAS1_TREF_SET_TC1(1164, *ptref, tcoord);
+ SC_RAS1_TREF_SET_CC1(1166, *ptref, (color == GX_COLOR_NULL) ? 7 : c2r[color]);
+ SC_RAS1_TREF_SET_TE1(1168, *ptref, (map != GX_TEXMAP_NULL && !(map & GX_TEX_DISABLE)));
+ } else {
+ SC_RAS1_TREF_SET_TI0(1171, *ptref, tmap);
+ SC_RAS1_TREF_SET_TC0(1172, *ptref, tcoord);
+ SC_RAS1_TREF_SET_CC0(1174, *ptref, (color == GX_COLOR_NULL) ? 7 : c2r[color]);
+ SC_RAS1_TREF_SET_TE0(1176, *ptref, (map != GX_TEXMAP_NULL && !(map & GX_TEX_DISABLE)));
+ }
+
+ GX_WRITE_RAS_REG(*ptref);
+ __GXData->bpSentNot = 0;
+ __GXData->dirtyState |= 1;
+}
+
+void GXSetNumTevStages(u8 nStages) {
+ CHECK_GXBEGIN(1192, "GXSetNumTevStages");
+
+ ASSERTMSGLINE(1194, nStages != 0 && nStages <= 16, "GXSetNumTevStages: Exceed max number of tex stages");
+ SC_GEN_MODE_SET_NTEV(1195, __GXData->genMode, nStages - 1);
+ __GXData->dirtyState |= 4;
+}
diff --git a/src/revolution/gx/GXTexture.c b/src/revolution/gx/GXTexture.c
new file mode 100644
index 0000000000..532294078b
--- /dev/null
+++ b/src/revolution/gx/GXTexture.c
@@ -0,0 +1,1315 @@
+#include <revolution/gx.h>
+#include <revolution/os.h>
+#include <string.h>
+
+#include "__gx.h"
+
+// GXTexObj internal data
+typedef struct __GXTexObjInt_struct {
+ u32 mode0;
+ u32 mode1;
+ u32 image0;
+ u32 image3;
+ void* userData;
+ GXTexFmt fmt;
+ u32 tlutName;
+ u16 loadCnt;
+ u8 loadFmt;
+ u8 flags;
+} __GXTexObjInt;
+
+// GXTexRegion internal data
+typedef struct __GXTexRegionInt_struct {
+ u32 image1;
+ u32 image2;
+ u16 sizeEven;
+ u16 sizeOdd;
+ u8 is32bMipmap;
+ u8 isCached;
+} __GXTexRegionInt;
+
+// GXTlutObj internal data
+typedef struct __GXTlutObjInt_struct {
+ u32 tlut;
+ u32 loadTlut0;
+ u16 numEntries;
+} __GXTlutObjInt;
+
+// GXTlutRegion internal data
+typedef struct __GXTlutRegionInt_struct {
+ u32 loadTlut1;
+ __GXTlutObjInt tlutObj;
+} __GXTlutRegionInt;
+
+u8 GXTexMode0Ids[8] = { 0x80, 0x81, 0x82, 0x83, 0xA0, 0xA1, 0xA2, 0xA3 };
+u8 GXTexMode1Ids[8] = { 0x84, 0x85, 0x86, 0x87, 0xA4, 0xA5, 0xA6, 0xA7 };
+u8 GXTexImage0Ids[8] = { 0x88, 0x89, 0x8A, 0x8B, 0xA8, 0xA9, 0xAA, 0xAB };
+u8 GXTexImage1Ids[8] = { 0x8C, 0x8D, 0x8E, 0x8F, 0xAC, 0xAD, 0xAE, 0xAF };
+u8 GXTexImage2Ids[8] = { 0x90, 0x91, 0x92, 0x93, 0xB0, 0xB1, 0xB2, 0xB3 };
+u8 GXTexImage3Ids[8] = { 0x94, 0x95, 0x96, 0x97, 0xB4, 0xB5, 0xB6, 0xB7 };
+u8 GXTexTlutIds[8] = { 0x98, 0x99, 0x9A, 0x9B, 0xB8, 0xB9, 0xBA, 0xBB };
+static u8 GX2HWFiltConv[6] = { 0x00, 0x04, 0x01, 0x05, 0x02, 0x06 };
+static u8 HW2GXFiltConv[8] = { 0x00, 0x02, 0x04, 0x00, 0x01, 0x03, 0x05, 0x00 };
+
+static void __GXGetTexTileShift(GXTexFmt fmt, u32* rowTileS, u32* colTileS) {
+ switch (fmt) {
+ case GX_TF_I4:
+ case 0x8:
+ case GX_TF_CMPR:
+ case GX_CTF_R4:
+ case GX_CTF_Z4:
+ *rowTileS = 3;
+ *colTileS = 3;
+ break;
+ case GX_TF_I8:
+ case GX_TF_IA4:
+ case 0x9:
+ case GX_TF_Z8:
+ case GX_CTF_RA4:
+ case GX_TF_A8:
+ case GX_CTF_R8:
+ case GX_CTF_G8:
+ case GX_CTF_B8:
+ case GX_CTF_Z8M:
+ case GX_CTF_Z8L:
+ *rowTileS = 3;
+ *colTileS = 2;
+ break;
+ case GX_TF_IA8:
+ case GX_TF_RGB565:
+ case GX_TF_RGB5A3:
+ case GX_TF_RGBA8:
+ case 0xA:
+ case GX_TF_Z16:
+ case GX_TF_Z24X8:
+ case GX_CTF_RA8:
+ case GX_CTF_RG8:
+ case GX_CTF_GB8:
+ case GX_CTF_Z16L:
+ *rowTileS = 2;
+ *colTileS = 2;
+ break;
+ default:
+ *rowTileS = *colTileS = 0;
+ ASSERTMSGLINEV(447, 0, "%s: invalid texture format", "GX");
+ break;
+ }
+}
+
+u32 GXGetTexBufferSize(u16 width, u16 height, u32 format, GXBool mipmap, u8 max_lod) {
+ u32 tileShiftX;
+ u32 tileShiftY;
+ u32 tileBytes;
+ u32 bufferSize;
+ u32 nx;
+ u32 ny;
+ u32 level;
+
+ ASSERTMSGLINEV(463, width <= 1024, "%s: width too large", "GXGetTexBufferSize");
+ ASSERTMSGLINEV(464, height <= 1024, "%s: height too large", "GXGetTexBufferSize");
+
+ __GXGetTexTileShift(format, &tileShiftX, &tileShiftY);
+ if (format == GX_TF_RGBA8 || format == GX_TF_Z24X8) {
+ tileBytes = 64;
+ } else {
+ tileBytes = 32;
+ }
+
+ if (mipmap == GX_TRUE) {
+ nx = 1 << (31 - __cntlzw(width));
+ ASSERTMSGLINEV(482, width == nx, "%s: width must be a power of 2", "GXGetTexBufferSize");
+ ny = 1 << (31 - __cntlzw(height));
+ ASSERTMSGLINEV(485, height == ny, "%s: height must be a power of 2", "GXGetTexBufferSize");
+
+ bufferSize = 0;
+ for (level = 0; level < max_lod; level++) {
+ nx = (width + (1 << tileShiftX) - 1) >> tileShiftX;
+ ny = (height + (1 << tileShiftY) - 1) >> tileShiftY;
+ bufferSize += tileBytes * (nx * ny);
+ if (width == 1 && height == 1) {
+ break;
+ }
+ width = (width > 1) ? width >> 1 : 1;
+ height = (height > 1) ? height >> 1 : 1;
+ }
+ } else {
+ nx = (width + (1 << tileShiftX) - 1) >> tileShiftX;
+ ny = (height + (1 << tileShiftY) - 1) >> tileShiftY;
+ bufferSize = nx * ny * tileBytes;
+ }
+
+ return bufferSize;
+}
+
+void __GetImageTileCount(GXTexFmt fmt, u16 wd, u16 ht, u32* rowTiles, u32* colTiles, u32* cmpTiles) {
+ u32 texRowShift;
+ u32 texColShift;
+
+ __GXGetTexTileShift(fmt, &texRowShift, &texColShift);
+ if (wd == 0) {
+ wd = 1;
+ }
+ if (ht == 0) {
+ ht = 1;
+ }
+ *rowTiles = (wd + (1 << texRowShift) - 1) >> texRowShift;
+ *colTiles = (ht + (1 << texColShift) - 1) >> texColShift;
+ *cmpTiles = (fmt == GX_TF_RGBA8 || fmt == GX_TF_Z24X8) ? 2 : 1;
+}
+
+#define SOME_SET_REG_MACRO(reg, size, shift, val) \
+ do { \
+ (reg) = (u32)__rlwimi((u32)(reg), (val), (shift), (32 - (shift) - (size)), (31 - (shift))); \
+ } while (0);
+
+void GXInitTexObj(GXTexObj* obj, void* image_ptr, u16 width, u16 height, GXTexFmt format, GXTexWrapMode wrap_s, GXTexWrapMode wrap_t, GXBool mipmap) {
+ u32 imageBase;
+ u32 maxLOD;
+ u16 rowT;
+ u16 colT;
+ u32 rowC;
+ u32 colC;
+ __GXTexObjInt* t = (__GXTexObjInt*)obj;
+
+ ASSERTMSGLINE(567, obj, "Texture Object Pointer is null");
+ CHECK_GXBEGIN(569, "GXInitTexObj");
+ ASSERTMSGLINEV(570, width <= 1024, "%s: width too large", "GXInitTexObj");
+ ASSERTMSGLINEV(571, height <= 1024, "%s: height too large", "GXInitTexObj");
+ ASSERTMSGLINEV(573, !(format & _GX_TF_CTF), "%s: invalid texture format", "GXInitTexObj");
+
+#if DEBUG
+ if (wrap_s != GX_CLAMP || mipmap) {
+ u32 mask = 1 << (31 - __cntlzw(width));
+ ASSERTMSGLINEV(583, width == mask, "%s: width must be a power of 2", "GXInitTexObj");
+ }
+ if (wrap_t != GX_CLAMP || mipmap) {
+ u32 mask = 1 << (31 - __cntlzw(height));
+ ASSERTMSGLINEV(588, height == mask, "%s: height must be a power of 2", "GXInitTexObj");
+ }
+#endif
+
+ memset(t, 0, 0x20);
+ SET_REG_FIELD(602, t->mode0, 2, 0, wrap_s);
+ SET_REG_FIELD(603, t->mode0, 2, 2, wrap_t);
+ SET_REG_FIELD(604, t->mode0, 1, 4, 1);
+
+ if (mipmap) {
+ u8 lmax;
+ t->flags |= 1;
+
+ if (format == 8 || format == 9 || format == 10) {
+ SOME_SET_REG_MACRO(t->mode0, 3, 5, 5);
+ } else {
+ SOME_SET_REG_MACRO(t->mode0, 3, 5, 6);
+ }
+
+
+ if (width > height) {
+ maxLOD = 31 - __cntlzw(width);
+ } else {
+ maxLOD = 31 - __cntlzw(height);
+ }
+
+ lmax = 16.0f * maxLOD;
+ SET_REG_FIELD(634, t->mode1, 8, 8, lmax);
+ } else {
+ SOME_SET_REG_MACRO(t->mode0, 3, 5, 4);
+ }
+
+ t->fmt = format;
+ SET_REG_FIELD(648, t->image0, 10, 0, width - 1);
+ SET_REG_FIELD(649, t->image0, 10, 10, height - 1);
+ SET_REG_FIELD(650, t->image0, 4, 20, format & 0xF);
+ ASSERTMSGLINEV(656, ((u32)image_ptr & 0x1F) == 0, "%s: %s pointer not aligned to 32B", "GXInitTexObj", "image");
+ imageBase = GX_PHY_ADDR(image_ptr) >> 5;
+ SET_REG_FIELD(658, t->image3, 24, 0, imageBase);
+
+ switch (format & 0xF) {
+ case GX_TF_I4:
+ case 8:
+ t->loadFmt = 1;
+ rowT = 3;
+ colT = 3;
+ break;
+ case GX_TF_I8:
+ case GX_TF_IA4:
+ case 9:
+ t->loadFmt = 2;
+ rowT = 3;
+ colT = 2;
+ break;
+ case GX_TF_IA8:
+ case GX_TF_RGB565:
+ case GX_TF_RGB5A3:
+ case 10:
+ t->loadFmt = 2;
+ rowT = 2;
+ colT = 2;
+ break;
+ case GX_TF_RGBA8:
+ t->loadFmt = 3;
+ rowT = 2;
+ colT = 2;
+ break;
+ case GX_TF_CMPR:
+ t->loadFmt = 0;
+ rowT = 3;
+ colT = 3;
+ break;
+ default:
+ ASSERTMSGLINEV(701, 0, "%s: invalid texture format", "GXPreLoadEntireTexture");
+ t->loadFmt = 2;
+ rowT = 2;
+ colT = 2;
+ break;
+ }
+
+ rowC = (width + (1 << rowT) - 1) >> rowT;
+ colC = (height + (1 << colT) - 1) >> colT;
+ t->loadCnt = (rowC * colC) & 0x7FFF;
+ t->flags |= 2;
+}
+
+void GXInitTexObjCI(GXTexObj* obj, void* image_ptr, u16 width, u16 height, GXCITexFmt format, GXTexWrapMode wrap_s, GXTexWrapMode wrap_t, GXBool mipmap, u32 tlut_name) {
+ __GXTexObjInt* t = (__GXTexObjInt*)obj;
+
+ ASSERTMSGLINE(739, obj, "Texture Object Pointer is null");
+ CHECK_GXBEGIN(741, "GXInitTexObjCI");
+ GXInitTexObj(obj, image_ptr, width, height, format, wrap_s, wrap_t, mipmap);
+ t->flags &= 0xFFFFFFFD;
+ t->tlutName = tlut_name;
+}
+
+void GXInitTexObjLOD(GXTexObj* obj, GXTexFilter min_filt, GXTexFilter mag_filt, f32 min_lod, f32 max_lod, f32 lod_bias, u8 bias_clamp, u8 do_edge_lod, GXAnisotropy max_aniso) {
+ u8 lbias;
+ u8 lmin;
+ u8 lmax;
+ __GXTexObjInt* t = (__GXTexObjInt*)obj;
+
+ ASSERTMSGLINE(778, obj, "Texture Object Pointer is null");
+ CHECK_GXBEGIN(780, "GXInitTexObjLOD");
+
+ if (lod_bias < -4.0f) {
+ lod_bias = -4.0f;
+ } else if (lod_bias >= 4.0f) {
+ lod_bias = 3.99f;
+ }
+
+ lbias = (u8)(32.0f * lod_bias) & 0xFF;
+ SET_REG_FIELD(790, t->mode0, 8, 9, lbias);
+ ASSERTMSG1LINE(793, (u32)mag_filt <= 1, "%s: invalid mag_filt value", "GXInitTexObjLOD");
+ SET_REG_FIELD(794, t->mode0, 1, 4, (mag_filt == GX_LINEAR) ? 1 : 0);
+ ASSERTMSG1LINE(797, (u32)min_filt <= 5, "%s: invalid min_filt value", "GXInitTexObjLOD");
+ SET_REG_FIELD(798, t->mode0, 3, 5, GX2HWFiltConv[min_filt]);
+ SET_REG_FIELD(800, t->mode0, 1, 8, do_edge_lod ? 0 : 1);
+ SET_REG_FIELD(801, t->mode0, 1, 17, 0);
+ SET_REG_FIELD(801, t->mode0, 1, 18, 0);
+ SET_REG_FIELD(803, t->mode0, 2, 19, max_aniso);
+ SET_REG_FIELD(804, t->mode0, 1, 21, bias_clamp);
+
+ if (min_lod < 0.0f) {
+ min_lod = 0.0f;
+ } else if (min_lod > 10.0f) {
+ min_lod = 10.0f;
+ }
+ lmin = 16.0f * min_lod;
+ if (max_lod < 0.0f) {
+ max_lod = 0.0f;
+ } else if (max_lod > 10.0f) {
+ max_lod = 10.0f;
+ }
+ lmax = 16.0f * max_lod;
+ SET_REG_FIELD(818, t->mode1, 8, 0, lmin);
+ SET_REG_FIELD(819, t->mode1, 8, 8, lmax);
+}
+
+void GXInitTexObjData(GXTexObj* obj, void* image_ptr) {
+ u32 imageBase;
+ __GXTexObjInt* t = (__GXTexObjInt*)obj;
+
+ ASSERTMSGLINE(835, obj, "Texture Object Pointer is null");
+ CHECK_GXBEGIN(837, "GXInitTexObjData");
+ ASSERTMSGLINEV(840, ((u32)image_ptr & 0x1F) == 0, "%s: %s pointer not aligned to 32B", "GXInitTexObjData", "image");
+ imageBase = ((u32)image_ptr >> 5) & 0x01FFFFFF;
+ SET_REG_FIELD(843, t->image3, 21, 0, imageBase);
+}
+
+void GXInitTexObjWrapMode(GXTexObj* obj, GXTexWrapMode sm, GXTexWrapMode tm) {
+ __GXTexObjInt* t = (__GXTexObjInt*)obj;
+
+ ASSERTMSGLINE(860, obj, "Texture Object Pointer is null");
+ CHECK_GXBEGIN(862, "GXInitTexObjWrapMode");
+ SET_REG_FIELD(864, t->mode0, 2, 0, sm);
+ SET_REG_FIELD(865, t->mode0, 2, 2, tm);
+}
+
+void GXInitTexObjTlut(GXTexObj* obj, u32 tlut_name) {
+ __GXTexObjInt* t = (__GXTexObjInt*)obj;
+
+ ASSERTMSGLINE(881, obj, "Texture Object Pointer is null");
+ CHECK_GXBEGIN(883, "GXInitTexObjTlut");
+ t->tlutName = tlut_name;
+}
+
+void GXInitTexObjFilter(GXTexObj* obj, GXTexFilter min_filt, GXTexFilter mag_filt) {
+ __GXTexObjInt* t = (__GXTexObjInt*)obj;
+
+ ASSERTMSGLINE(902, obj, "Texture Object Pointer is null");
+ CHECK_GXBEGIN(904, "GXInitTexObjFilter");
+
+ ASSERTMSG1LINE(907, (u32)mag_filt <= 1, "%s: invalid mag_filt value", "GXInitTexObjFilter");
+ SET_REG_FIELD(908, t->mode0, 1, 4, mag_filt == 1 ? 1 : 0);
+
+ ASSERTMSG1LINE(911, (u32)min_filt <= 5, "%s: invalid min_filt value", "GXInitTexObjFilter");
+ SET_REG_FIELD(912, t->mode0, 3, 5, GX2HWFiltConv[min_filt]);
+}
+
+void GXInitTexObjMaxLOD(GXTexObj* obj, f32 max_lod) {
+ u8 lmax;
+ __GXTexObjInt* t = (__GXTexObjInt*)obj;
+
+ ASSERTMSGLINE(930, obj, "Texture Object Pointer is null");
+ CHECK_GXBEGIN(932, "GXInitTexObjMaxLOD");
+
+ if (max_lod < 0.0f) {
+ max_lod = 0.0f;
+ } else if (max_lod > 10.0f) {
+ max_lod = 10.0f;
+ }
+ lmax = 16.0f * max_lod;
+ SET_REG_FIELD(938, t->mode1, 8, 8, lmax);
+}
+
+void GXInitTexObjMinLOD(GXTexObj* obj, f32 min_lod) {
+ u8 lmin;
+ __GXTexObjInt* t = (__GXTexObjInt*)obj;
+
+ ASSERTMSGLINE(956, obj, "Texture Object Pointer is null");
+ CHECK_GXBEGIN(958, "GXInitTexObjMinLOD");
+
+ if (min_lod < 0.0f) {
+ min_lod = 0.0f;
+ } else if (min_lod > 10.0f) {
+ min_lod = 10.0f;
+ }
+ lmin = 16.0f * min_lod;
+ SET_REG_FIELD(964, t->mode1, 8, 0, lmin);
+}
+
+void GXInitTexObjLODBias(GXTexObj* obj, f32 lod_bias) {
+ u8 lbias;
+ __GXTexObjInt* t = (__GXTexObjInt*)obj;
+
+ ASSERTMSGLINE(982, obj, "Texture Object Pointer is null");
+ CHECK_GXBEGIN(984, "GXInitTexObjLODBias");
+
+ if (lod_bias < -4.0f) {
+ lod_bias = -4.0f;
+ } else if (lod_bias >= 4.0f) {
+ lod_bias = 3.99f;
+ }
+ lbias = 32.0f * lod_bias;
+ SET_REG_FIELD(991, t->mode0, 8, 9, lbias);
+}
+
+void GXInitTexObjBiasClamp(GXTexObj* obj, u8 bias_clamp) {
+ __GXTexObjInt* t = (__GXTexObjInt*)obj;
+
+ ASSERTMSGLINE(1007, obj, "Texture Object Pointer is null");
+ CHECK_GXBEGIN(1009, "GXInitTexObjBiasClamp");
+
+ SET_REG_FIELD(1011, t->mode0, 1, 21, bias_clamp);
+}
+
+void GXInitTexObjEdgeLOD(GXTexObj* obj, u8 do_edge_lod) {
+ __GXTexObjInt* t = (__GXTexObjInt*)obj;
+
+ ASSERTMSGLINE(1027, obj, "Texture Object Pointer is null");
+ CHECK_GXBEGIN(1029, "GXInitTexObjEdgeLOD");
+
+ SET_REG_FIELD(1031, t->mode0, 1, 8, do_edge_lod ? 0 : 1);
+}
+
+void GXInitTexObjMaxAniso(GXTexObj* obj, GXAnisotropy max_aniso) {
+ __GXTexObjInt* t = (__GXTexObjInt*)obj;
+
+ ASSERTMSGLINE(1047, obj, "Texture Object Pointer is null");
+ CHECK_GXBEGIN(1049, "GXInitTexObjMaxAniso");
+
+ SET_REG_FIELD(1051, t->mode0, 2, 19, max_aniso);
+}
+
+void GXInitTexObjUserData(GXTexObj* obj, void* user_data) {
+ __GXTexObjInt* t = (__GXTexObjInt*)obj;
+
+ ASSERTMSGLINE(1068, obj, "Texture Object Pointer is null");
+ CHECK_GXBEGIN(1069, "GXInitTexObjUserData");
+ t->userData = user_data;
+}
+
+void* GXGetTexObjUserData(const GXTexObj* obj) {
+ const __GXTexObjInt* t = (const __GXTexObjInt *)obj;
+
+ ASSERTMSGLINE(1075, obj, "Texture Object Pointer is null");
+ return t->userData;
+}
+
+void GXGetTexObjAll(const GXTexObj* obj, void** image_ptr, u16* width, u16* height, GXTexFmt* format, GXTexWrapMode* wrap_s, GXTexWrapMode* wrap_t, u8* mipmap) {
+ const __GXTexObjInt* t = (const __GXTexObjInt *)obj;
+
+ ASSERTMSGLINE(1095, obj, "Texture Object Pointer is null");
+ *image_ptr = (void *)(GET_REG_FIELD(t->image3, 21, 0) << 5);
+ *width = (u32)GET_REG_FIELD(t->image0, 10, 0) + 1;
+ *height = (u32)GET_REG_FIELD(t->image0, 10, 10) + 1;
+ *format = t->fmt;
+ *wrap_s = GET_REG_FIELD(t->mode0, 2, 0);
+ *wrap_t = GET_REG_FIELD(t->mode0, 2, 2);
+ *mipmap = (t->flags & 1) == 1;
+}
+
+void* GXGetTexObjData(const GXTexObj* to) {
+ const __GXTexObjInt* t = (const __GXTexObjInt *)to;
+
+ ASSERTMSGLINE(1108, to, "Texture Object Pointer is null");
+ return (void*)(GET_REG_FIELD(t->image3, 21, 0) << 5);
+}
+
+u16 GXGetTexObjWidth(const GXTexObj* to) {
+ const __GXTexObjInt* t = (const __GXTexObjInt *)to;
+
+ ASSERTMSGLINE(1116, to, "Texture Object Pointer is null");
+ return (u32)GET_REG_FIELD(t->image0, 10, 0) + 1;
+}
+
+u16 GXGetTexObjHeight(const GXTexObj* to) {
+ const __GXTexObjInt* t = (const __GXTexObjInt *)to;
+
+ ASSERTMSGLINE(1122, to, "Texture Object Pointer is null");
+ return (u32)GET_REG_FIELD(t->image0, 10, 10) + 1;
+}
+
+GXTexFmt GXGetTexObjFmt(const GXTexObj* to) {
+ const __GXTexObjInt* t = (const __GXTexObjInt *)to;
+
+ ASSERTMSGLINE(1128, to, "Texture Object Pointer is null");
+ return t->fmt;
+}
+
+GXTexWrapMode GXGetTexObjWrapS(const GXTexObj* to) {
+ const __GXTexObjInt* t = (const __GXTexObjInt *)to;
+
+ ASSERTMSGLINE(1134, to, "Texture Object Pointer is null");
+ return GET_REG_FIELD(t->mode0, 2, 0);
+}
+
+GXTexWrapMode GXGetTexObjWrapT(const GXTexObj* to) {
+ const __GXTexObjInt* t = (const __GXTexObjInt *)to;
+
+ ASSERTMSGLINE(1140, to, "Texture Object Pointer is null");
+ return GET_REG_FIELD(t->mode0, 2, 2);
+}
+
+GXBool GXGetTexObjMipMap(const GXTexObj* to) {
+ const __GXTexObjInt* t = (const __GXTexObjInt *)to;
+
+ ASSERTMSGLINE(1146, to, "Texture Object Pointer is null");
+ return (t->flags & 1) == 1;
+}
+
+void GXGetTexObjLODAll(const GXTexObj* tex_obj, GXTexFilter* min_filt, GXTexFilter* mag_filt, f32* min_lod, f32* max_lod, f32* lod_bias, u8* bias_clamp, u8* do_edge_lod, GXAnisotropy* max_aniso) {
+ s16 tmp;
+ const __GXTexObjInt* t = (const __GXTexObjInt *)tex_obj;
+
+ ASSERTMSGLINE(1166, tex_obj, "Texture Object Pointer is null");
+ *min_filt = HW2GXFiltConv[GET_REG_FIELD(t->mode0, 3, 5)];
+ *mag_filt = GET_REG_FIELD(t->mode0, 1, 4);
+ *min_lod = (u8)t->mode1 / 16.0f;
+ *max_lod = (u32)GET_REG_FIELD(t->mode1, 8, 8) / 16.0f;
+ tmp = (s32)GET_REG_FIELD(t->mode0, 8, 9);
+ *lod_bias = (s8)tmp / 32.0f;
+ *bias_clamp = (u32)GET_REG_FIELD(t->mode0, 1, 21);
+ *do_edge_lod = !GET_REG_FIELD(t->mode0, 1, 8);
+ *max_aniso = GET_REG_FIELD(t->mode0, 2, 19);
+}
+
+GXTexFilter GXGetTexObjMinFilt(const GXTexObj* tex_obj) {
+ const __GXTexObjInt* t = (const __GXTexObjInt *)tex_obj;
+
+ ASSERTMSGLINE(1182, tex_obj, "Texture Object Pointer is null");
+ return HW2GXFiltConv[GET_REG_FIELD(t->mode0, 3, 5)];
+}
+
+GXTexFilter GXGetTexObjMagFilt(const GXTexObj* tex_obj) {
+ const __GXTexObjInt* t = (const __GXTexObjInt *)tex_obj;
+
+ ASSERTMSGLINE(1189, tex_obj, "Texture Object Pointer is null");
+ return GET_REG_FIELD(t->mode0, 1, 4);
+}
+
+f32 GXGetTexObjMinLOD(const GXTexObj* tex_obj) {
+ const __GXTexObjInt* t = (const __GXTexObjInt *)tex_obj;
+
+ ASSERTMSGLINE(1195, tex_obj, "Texture Object Pointer is null");
+ return (u32)GET_REG_FIELD(t->mode1, 8, 0) / 16.0f;
+}
+
+f32 GXGetTexObjMaxLOD(const GXTexObj* tex_obj) {
+ const __GXTexObjInt* t = (const __GXTexObjInt *)tex_obj;
+
+ ASSERTMSGLINE(1201, tex_obj, "Texture Object Pointer is null");
+ return (u32)GET_REG_FIELD(t->mode1, 8, 8) / 16.0f;
+}
+
+f32 GXGetTexObjLODBias(const GXTexObj* tex_obj) {
+ s16 tmp;
+ const __GXTexObjInt* t = (const __GXTexObjInt *)tex_obj;
+
+ ASSERTMSGLINE(1208, tex_obj, "Texture Object Pointer is null");
+ tmp = (s32)GET_REG_FIELD(t->mode0, 8, 9);
+ return (s8)tmp / 32.0f;
+}
+
+GXBool GXGetTexObjBiasClamp(const GXTexObj* tex_obj) {
+ const __GXTexObjInt* t = (const __GXTexObjInt *)tex_obj;
+
+ ASSERTMSGLINE(1215, tex_obj, "Texture Object Pointer is null");
+ return (u32)GET_REG_FIELD(t->mode0, 1, 21);
+}
+
+GXBool GXGetTexObjEdgeLOD(const GXTexObj* tex_obj) {
+ const __GXTexObjInt* t = (const __GXTexObjInt *)tex_obj;
+
+ ASSERTMSGLINE(1221, tex_obj, "Texture Object Pointer is null");
+ return !GET_REG_FIELD(t->mode0, 1, 8);
+}
+
+GXAnisotropy GXGetTexObjMaxAniso(const GXTexObj* tex_obj) {
+ const __GXTexObjInt* t = (const __GXTexObjInt *)tex_obj;
+
+ ASSERTMSGLINE(1227, tex_obj, "Texture Object Pointer is null");
+ return GET_REG_FIELD(t->mode0, 2, 19);
+}
+
+u32 GXGetTexObjTlut(const GXTexObj* tex_obj) {
+ const __GXTexObjInt* t = (const __GXTexObjInt *)tex_obj;
+
+ ASSERTMSGLINE(1235, tex_obj, "Texture Object Pointer is null");
+ return t->tlutName;
+}
+
+void GXLoadTexObjPreLoaded(GXTexObj* obj, GXTexRegion* region, GXTexMapID id) {
+ __GXTlutRegionInt* tlr;
+ u32 m0;
+ u32 m1;
+ u32 img0;
+ u32 img1;
+ u32 img2;
+ u32 img3;
+ __GXTexObjInt* t = (__GXTexObjInt*)obj;
+ __GXTexRegionInt* r = (__GXTexRegionInt *)region;
+
+ ASSERTMSGLINE(1259, obj, "Texture Object Pointer is null");
+ ASSERTMSGLINE(1259, region, "TexRegion Object Pointer is null");
+ CHECK_GXBEGIN(1261, "GXLoadTexObjPreLoaded");
+ ASSERTMSGLINEV(1262, id < GX_MAX_TEXMAP, "%s: invalid texture map ID", "GXLoadTexObj");
+
+ m0 = t->mode0;
+ m1 = t->mode1;
+ img0 = t->image0;
+ img1 = r->image1;
+ img2 = r->image2;
+ img3 = t->image3;
+
+ SET_REG_FIELD(1273, t->mode0, 8, 24, GXTexMode0Ids[id]);
+ SET_REG_FIELD(1274, t->mode1, 8, 24, GXTexMode1Ids[id]);
+ SET_REG_FIELD(1275, t->image0, 8, 24, GXTexImage0Ids[id]);
+ SET_REG_FIELD(1276, r->image1, 8, 24, GXTexImage1Ids[id]);
+ SET_REG_FIELD(1277, r->image2, 8, 24, GXTexImage2Ids[id]);
+ SET_REG_FIELD(1278, t->image3, 8, 24, GXTexImage3Ids[id]);
+
+ GX_WRITE_RAS_REG(t->mode0);
+ GX_WRITE_RAS_REG(t->mode1);
+ GX_WRITE_RAS_REG(t->image0);
+ GX_WRITE_RAS_REG(r->image1);
+ GX_WRITE_RAS_REG(r->image2);
+ GX_WRITE_RAS_REG(t->image3);
+
+ if (!(t->flags & 2)) {
+ ASSERTMSGLINEV(1289, __GXData->tlutRegionCallback, "%s: Tex/Tlut Region Callback not set", "GXLoadTexObj/PreLoaded");
+ tlr = (__GXTlutRegionInt*)__GXData->tlutRegionCallback(t->tlutName);
+ ASSERTMSGLINEV(1291, tlr, "%s: Tex/Tlut Region Callback returns NULL", "GXLoadTexObj/PreLoaded");
+
+ SET_REG_FIELD(1293, tlr->tlutObj.tlut, 8, 24, GXTexTlutIds[id]);
+ GX_WRITE_RAS_REG(tlr->tlutObj.tlut);
+ }
+
+ __GXData->tImage0[id] = t->image0;
+ __GXData->tMode0[id] = t->mode0;
+ __GXData->dirtyState |= 1;
+ __GXData->bpSentNot = 0;
+}
+
+void GXLoadTexObj(GXTexObj* obj, GXTexMapID id) {
+ GXTexRegion* r;
+
+ CHECK_GXBEGIN(1320, "GXLoadTexObj");
+ ASSERTMSGLINEV(1321, id < 8, "%s: invalid texture map ID", "GXLoadTexObj");
+ ASSERTMSGLINEV(1326, __GXData->texRegionCallback, "%s: Tex/Tlut Region Callback not set", "GXLoadTexObj");
+ r = __GXData->texRegionCallback(obj, id);
+ ASSERTMSGLINEV(1328, r, "%s: Tex/Tlut Region Callback returns NULL", "GXLoadTexObj");
+ GXLoadTexObjPreLoaded(obj, r, id);
+}
+
+void GXInitTlutObj(GXTlutObj* tlut_obj, void* lut, GXTlutFmt fmt, u16 n_entries) {
+ __GXTlutObjInt* t = (__GXTlutObjInt *)tlut_obj;
+
+ ASSERTMSGLINE(1352, tlut_obj, "TLut Object Pointer is null");
+ CHECK_GXBEGIN(1353, "GXInitTlutObj");
+ ASSERTMSGLINEV(1356, n_entries <= 0x4000, "%s: number of entries exceeds maximum", "GXInitTlutObj");
+ ASSERTMSGLINEV(1358, ((u32)lut & 0x1F) == 0, "%s: %s pointer not aligned to 32B", "GXInitTlutObj", "Tlut");
+ t->tlut = 0;
+ SET_REG_FIELD(1361, t->tlut, 2, 10, fmt);
+ SET_REG_FIELD(1362, t->loadTlut0, 24, 0, GX_PHY_ADDR(lut) >> 5);
+ SET_REG_FIELD(1363, t->loadTlut0, 8, 24, 0x64);
+ t->numEntries = n_entries;
+}
+
+void GXGetTlutObjAll(const GXTlutObj* tlut_obj, void **data, GXTlutFmt* format, u16* numEntries) {
+ const __GXTlutObjInt* t = (const __GXTlutObjInt *)tlut_obj;
+
+ ASSERTMSGLINE(1382, tlut_obj, "TLut Object Pointer is null");
+ *data = (void *)(GET_REG_FIELD(t->loadTlut0, 21, 0) << 5);
+ *format = GET_REG_FIELD(t->tlut, 2, 10);
+ *numEntries = t->numEntries;
+}
+
+void* GXGetTlutObjData(const GXTlutObj* tlut_obj) {
+ const __GXTlutObjInt* t = (const __GXTlutObjInt *)tlut_obj;
+
+ ASSERTMSGLINE(1391, tlut_obj, "TLut Object Pointer is null");
+ return (void *)(GET_REG_FIELD(t->loadTlut0, 21, 0) << 5);
+}
+
+GXTlutFmt GXGetTlutObjFmt(const GXTlutObj* tlut_obj) {
+ const __GXTlutObjInt* t = (const __GXTlutObjInt *)tlut_obj;
+
+ ASSERTMSGLINE(1398, tlut_obj, "TLut Object Pointer is null");
+ return GET_REG_FIELD(t->tlut, 2, 10);
+}
+
+u16 GXGetTlutObjNumEntries(const GXTlutObj* tlut_obj) {
+ const __GXTlutObjInt* t = (const __GXTlutObjInt *)tlut_obj;
+
+ ASSERTMSGLINE(1405, tlut_obj, "TLut Object Pointer is null");
+ return t->numEntries;
+}
+
+void GXLoadTlut(GXTlutObj* tlut_obj, u32 tlut_name) {
+ __GXTlutRegionInt* r;
+ u32 tlut_offset;
+ __GXTlutObjInt* t = (__GXTlutObjInt *)tlut_obj;
+
+ ASSERTMSGLINE(1434, tlut_obj, "TLut Object Pointer is null");
+ CHECK_GXBEGIN(1436, "GXLoadTlut");
+ ASSERTMSGLINEV(1437, __GXData->tlutRegionCallback, "%s: Tex/Tlut Region Callback not set", "GXLoadTlut");
+ r = (__GXTlutRegionInt *)__GXData->tlutRegionCallback(tlut_name);
+ ASSERTMSGLINEV(1439, r, "%s: Tex/Tlut Region Callback returns NULL", "GXLoadTlut");
+
+ __GXFlushTextureState();
+ GX_WRITE_RAS_REG(t->loadTlut0);
+ GX_WRITE_RAS_REG(r->loadTlut1);
+ __GXFlushTextureState();
+ tlut_offset = r->loadTlut1 & 0x3FF;
+ SET_REG_FIELD(1455, t->tlut, 10, 0, tlut_offset);
+ r->tlutObj = *t;
+}
+
+void GXInitTexCacheRegion(GXTexRegion* region, u8 is_32b_mipmap, u32 tmem_even, GXTexCacheSize size_even, u32 tmem_odd, GXTexCacheSize size_odd) {
+ u32 WidthExp2;
+ __GXTexRegionInt* t = (__GXTexRegionInt*)region;
+
+ ASSERTMSGLINE(1486, region, "TexRegion Object Pointer is null");
+ CHECK_GXBEGIN(1488, "GXInitTexCacheRegion");
+ ASSERTMSGLINEV(1490, (tmem_even & 0x1F) == 0, "%s: %s pointer not aligned to 32B", "GXInitTexCacheRegion", "tmem even");
+ ASSERTMSGLINEV(1492, (tmem_odd & 0x1F) == 0, "%s: %s pointer not aligned to 32B", "GXInitTexCacheRegion", "tmem odd");
+
+ switch (size_even) {
+ case GX_TEXCACHE_32K:
+ WidthExp2 = 3;
+ break;
+ case GX_TEXCACHE_128K:
+ WidthExp2 = 4;
+ break;
+ case GX_TEXCACHE_512K:
+ WidthExp2 = 5;
+ break;
+ default:
+ ASSERTMSGLINEV(1500, 0, "%s: Invalid %s size", "GXInitTexCacheRegion", "tmem even");
+ break;
+ }
+
+ t->image1 = 0;
+ SET_REG_FIELD(1505, t->image1, 15, 0, tmem_even >> 5);
+ SET_REG_FIELD(1506, t->image1, 3, 15, WidthExp2);
+ SET_REG_FIELD(1507, t->image1, 3, 18, WidthExp2);
+ SET_REG_FIELD(1508, t->image1, 1, 21, 0);
+
+ switch (size_odd) {
+ case GX_TEXCACHE_32K:
+ WidthExp2 = 3;
+ break;
+ case GX_TEXCACHE_128K:
+ WidthExp2 = 4;
+ break;
+ case GX_TEXCACHE_512K:
+ WidthExp2 = 5;
+ break;
+ case GX_TEXCACHE_NONE:
+ WidthExp2 = 0;
+ break;
+ default:
+ ASSERTMSGLINEV(1516, 0, "%s: Invalid %s size", "GXInitTexCacheRegion", "tmem odd");
+ break;
+ }
+
+ t->image2 = 0;
+ SET_REG_FIELD(1521, t->image2, 15, 0, tmem_odd >> 5);
+ SET_REG_FIELD(1522, t->image2, 3, 15, WidthExp2);
+ SET_REG_FIELD(1523, t->image2, 3, 18, WidthExp2);
+ t->is32bMipmap = (u8)is_32b_mipmap;
+ t->isCached = 1;
+}
+
+void GXInitTexPreLoadRegion(GXTexRegion* region, u32 tmem_even, u32 size_even, u32 tmem_odd, u32 size_odd) {
+ __GXTexRegionInt* t = (__GXTexRegionInt *)region;
+
+ ASSERTMSGLINE(1550, region, "TexRegion Object Pointer is null");
+ CHECK_GXBEGIN(1552, "GXInitTexPreLoadRegion");
+ ASSERTMSGLINEV(1554, (tmem_even & 0x1F) == 0, "%s: %s pointer not aligned to 32B", "GXInitTexPreLoadRegion", "tmem even");
+ ASSERTMSGLINEV(1556, (tmem_odd & 0x1F) == 0, "%s: %s pointer not aligned to 32B", "GXInitTexPreLoadRegion", "tmem odd");
+ ASSERTMSGLINEV(1558, (size_even & 0x1F) == 0, "%s: %s pointer not aligned to 32B", "GXInitTexPreLoadRegion", "size even");
+ ASSERTMSGLINEV(1560, (size_odd & 0x1F) == 0, "%s: %s pointer not aligned to 32B", "GXInitTexPreLoadRegion", "size odd");
+
+ t->image1 = 0;
+ SET_REG_FIELD(1564, t->image1, 15, 0, tmem_even >> 5);
+ SET_REG_FIELD(1565, t->image1, 3, 15, 0);
+ SET_REG_FIELD(1566, t->image1, 3, 18, 0);
+ SET_REG_FIELD(1567, t->image1, 1, 21, 1);
+
+ t->image2 = 0;
+ SET_REG_FIELD(1570, t->image2, 15, 0, tmem_odd >> 5);
+ SET_REG_FIELD(1571, t->image2, 3, 15, 0);
+ SET_REG_FIELD(1572, t->image2, 3, 18, 0);
+ t->is32bMipmap = 0;
+ t->isCached = 0;
+ t->sizeEven = (u16)(size_even >> 5);
+ t->sizeOdd = (u16)(size_odd >> 5);
+}
+
+void GXGetTexRegionAll(const GXTexRegion* region, u8* is_cached, u8* is_32b_mipmap, u32* tmem_even, u32* size_even, u32* tmem_odd, u32* size_odd) {
+ const __GXTexRegionInt* t = (const __GXTexRegionInt *)region;
+
+ ASSERTMSGLINE(1601, region, "TexRegion Object Pointer is null");
+ *tmem_even = GET_REG_FIELD(t->image1, 15, 0) << 5;
+ *tmem_odd = GET_REG_FIELD(t->image2, 15, 0) << 5;
+ if (t->isCached) {
+ switch (GET_REG_FIELD(t->image1, 3, 15)) {
+ case 3:
+ *size_even = 0x8000;
+ break;
+ case 4:
+ *size_even = 0x20000;
+ break;
+ case 5:
+ *size_even = 0x80000;
+ break;
+ default:
+ *size_even = 0;
+ break;
+ }
+
+ switch (GET_REG_FIELD(t->image2, 3, 15)) {
+ case 3:
+ *size_odd = 0x8000;
+ break;
+ case 4:
+ *size_odd = 0x20000;
+ break;
+ case 5:
+ *size_odd = 0x80000;
+ break;
+ default:
+ *size_odd = 0;
+ break;
+ }
+ } else {
+ *size_even = t->sizeEven << 5;
+ *size_odd = t->sizeOdd << 5;
+ }
+
+ *is_32b_mipmap = t->is32bMipmap;
+ *is_cached = t->isCached;
+}
+
+void GXInitTlutRegion(GXTlutRegion* region, u32 tmem_addr, GXTlutSize tlut_size) {
+ __GXTlutRegionInt* t = (__GXTlutRegionInt *)region;
+
+ ASSERTMSGLINE(1654, region, "TLutRegion Object Pointer is null");
+ CHECK_GXBEGIN(1656, "GXInitTlutRegion");
+ ASSERTMSGLINEV(1657, (tmem_addr & 0x1FF) == 0, "%s: tmem pointer is not aligned to 512B", "GXInitTlutRegion");
+ ASSERTMSGLINEV(1658, tlut_size <= 0x400, "%s: tlut size exceeds 16K", "GXInitTlutRegion");
+ t->loadTlut1 = 0;
+ tmem_addr -= 0x80000;
+ SET_REG_FIELD(1662, t->loadTlut1, 10, 0, tmem_addr >> 9);
+ SET_REG_FIELD(1663, t->loadTlut1, 11, 10, tlut_size);
+ SET_REG_FIELD(1664, t->loadTlut1, 8, 24, 0x65);
+}
+
+void GXGetTlutRegionAll(const GXTlutRegion* region, u32* tmem_addr, GXTlutSize* tlut_size) {
+ const __GXTlutRegionInt* t = (const __GXTlutRegionInt *)region;
+
+ ASSERTMSGLINE(1682, region, "TLutRegion Object Pointer is null");
+ *tmem_addr = (GET_REG_FIELD(t->loadTlut1, 10, 0) << 9) + 0x80000;
+ *tlut_size = GET_REG_FIELD(t->loadTlut1, 11, 10);
+}
+
+void GXInvalidateTexRegion(GXTexRegion* region) {
+ s32 wle;
+ s32 hle;
+ s32 wlo;
+ s32 hlo;
+ s32 count;
+ u32 reg0;
+ u32 reg1;
+ __GXTexRegionInt* r = (__GXTexRegionInt*)region;
+
+ ASSERTMSGLINE(1705, region, "TexRegion Object Pointer is null");
+ CHECK_GXBEGIN(1707, "GXInvalidateTexRegion");
+
+ wle = GET_REG_FIELD(r->image1, 3, 15) - 1;
+ hle = GET_REG_FIELD(r->image1, 3, 18) - 1;
+ wlo = GET_REG_FIELD(r->image2, 3, 15) - 1;
+ hlo = GET_REG_FIELD(r->image2, 3, 18) - 1;
+ if (wle < 0) {
+ wle = 0;
+ }
+ if (hle < 0) {
+ hle = 0;
+ }
+ if (wlo < 0) {
+ wlo = 0;
+ }
+ if (hlo < 0) {
+ hlo = 0;
+ }
+ count = wle + hle;
+ if (r->is32bMipmap) {
+ count = wlo + hlo - 2 + count;
+ }
+
+ reg0 = 0;
+ SET_REG_FIELD(1724, reg0, 9, 0, GET_REG_FIELD(r->image1, 9, 6));
+ SET_REG_FIELD(1725, reg0, 4, 9, count);
+ SET_REG_FIELD(1724, reg0, 8, 24, 0x66);
+
+ if (wlo != 0) {
+ count = wlo + hlo;
+ if (r->is32bMipmap) {
+ count = wle + hle - 2 + count;
+ }
+ reg1 = 0;
+ SET_REG_FIELD(1736, reg1, 9, 0, GET_REG_FIELD(r->image2, 9, 6));
+ SET_REG_FIELD(1737, reg1, 4, 9, count);
+ SET_REG_FIELD(1738, reg1, 8, 24, 0x66);
+ }
+
+ __GXFlushTextureState();
+ GX_WRITE_RAS_REG(reg0);
+ if (wlo != 0) {
+ GX_WRITE_RAS_REG(reg1);
+ }
+ __GXFlushTextureState();
+
+ reg0; reg1; r; // needed to match
+}
+
+void GXInvalidateTexAll(void) {
+ u32 reg0;
+ u32 reg1;
+
+ CHECK_GXBEGIN(1757, "GXInvalidateTexAll");
+ reg0 = 0x66001000;
+ reg1 = 0x66001100;
+ __GXFlushTextureState();
+ GX_WRITE_RAS_REG(reg0);
+ GX_WRITE_RAS_REG(reg1);
+ __GXFlushTextureState();
+}
+
+GXTexRegionCallback GXSetTexRegionCallback(GXTexRegionCallback f) {
+ GXTexRegionCallback oldcb = __GXData->texRegionCallback;
+
+ __GXData->texRegionCallback = f;
+ return oldcb;
+}
+
+GXTlutRegionCallback GXSetTlutRegionCallback(GXTlutRegionCallback f) {
+ GXTlutRegionCallback oldcb = __GXData->tlutRegionCallback;
+
+ __GXData->tlutRegionCallback = f;
+ return oldcb;
+}
+
+void GXPreLoadEntireTexture(GXTexObj* tex_obj, GXTexRegion* region) {
+ GXBool isMipMap;
+ GXBool is32bit;
+ u32 wd;
+ u32 ht;
+ u32 maxLevelIndex;
+ u32 loadImage0;
+ u32 loadImage1;
+ u32 loadImage2;
+ u32 loadImage3;
+ u32 base;
+ u32 tmem1;
+ u32 tmem2;
+ u32 tmemAR;
+ u32 tmemGB;
+ u32 nTiles;
+#if DEBUG
+ u32 totalOdd;
+ u32 totalEven;
+ u32 count;
+#endif
+ u32 rowTiles;
+ u32 colTiles;
+ u32 cmpTiles;
+ u32 i;
+ __GXTexObjInt* t = (__GXTexObjInt*)tex_obj;
+ __GXTexRegionInt* r = (__GXTexRegionInt*)region;
+
+ ASSERTMSGLINE(1820, tex_obj, "Texture Object Pointer is null");
+ ASSERTMSGLINE(1820, region, "TexRegion Object Pointer is null");
+ CHECK_GXBEGIN(1822, "GXPreLoadEntireTexture");
+ isMipMap = (t->flags & 1) == 1;
+ is32bit = GET_REG_FIELD(t->image0, 4, 20) == 6;
+
+ loadImage0 = 0;
+ SET_REG_FIELD(0, loadImage0, 8, 24, 0x60);
+ base = t->image3 & 0x1FFFFF;
+ SET_REG_FIELD(1831, loadImage0, 21, 0, base);
+
+ loadImage1 = 0;
+ SET_REG_FIELD(0, loadImage1, 8, 24, 0x61);
+ tmem1 = r->image1 & 0x7FFF;
+ SET_REG_FIELD(1837, loadImage1, 15, 0, tmem1);
+
+ loadImage2 = 0;
+ SET_REG_FIELD(0, loadImage2, 8, 24, 0x62);
+ tmem2 = r->image2 & 0x7FFF;
+ SET_REG_FIELD(1843, loadImage2, 15, 0, tmem2);
+
+ loadImage3 = 0;
+ SET_REG_FIELD(0, loadImage3, 8, 24, 0x63);
+ SET_REG_FIELD(1848, loadImage3, 15, 0, t->loadCnt);
+ SET_REG_FIELD(1849, loadImage3, 2, 15, t->loadFmt);
+ maxLevelIndex = 0;
+ nTiles = t->loadCnt;
+
+ if (isMipMap) {
+ wd = GET_REG_FIELD(t->image0, 10, 0) + 1;
+ ht = GET_REG_FIELD(t->image0, 10, 10) + 1;
+ if (wd > ht) {
+ maxLevelIndex = (u16)(31 - __cntlzw(wd));
+ } else {
+ maxLevelIndex = (u16)(31 - __cntlzw(ht));
+ }
+
+#if DEBUG
+ count = nTiles;
+ totalOdd = totalEven = 0;
+
+ for (i = 0; i < maxLevelIndex; i++) {
+ if (i & 1) {
+ if (count == 0) {
+ count = 1;
+ }
+ totalOdd += count;
+ } else {
+ if (count == 0) {
+ count = 1;
+ }
+ totalEven += count;
+ }
+ __GetImageTileCount(t->fmt, wd >> (i + 1), ht >> (i + 1), &rowTiles, &colTiles, &cmpTiles);
+ count = rowTiles * colTiles;
+ }
+#endif
+ } else {
+#if DEBUG
+ totalEven = (nTiles == 0) ? 1 : nTiles;
+ totalOdd = totalEven;
+#endif
+ }
+
+#if DEBUG
+ if (is32bit) {
+ totalOdd = isMipMap ? totalOdd : 0;
+ totalEven = totalEven + totalOdd;
+ ASSERTMSGLINE(1890, totalEven <= r->sizeEven, "GXPreLoadEntireTexture: Even tmem size does not match the texture size");
+ ASSERTMSGLINE(1891, totalEven <= r->sizeOdd, "GXPreLoadEntireTexture: Odd tmem size does not match the texture size");
+ } else if (isMipMap != 0) {
+ if (r->sizeEven > r->sizeOdd) {
+ ASSERTMSGLINE(1896, totalEven <= r->sizeEven, "GXPreLoadEntireTexture: Even tmem size does not match the texture size");
+ ASSERTMSGLINE(1897, totalOdd <= r->sizeOdd, "GXPreLoadEntireTexture: Odd tmem size does not match the texture size");
+ } else {
+ ASSERTMSGLINE(1900, totalEven <= r->sizeOdd, "GXPreLoadEntireTexture: Odd tmem size does not match the texture size");
+ ASSERTMSGLINE(1901, totalOdd <= r->sizeEven, "GXPreLoadEntireTexture: Even tmem size does not match the texture size");
+ }
+ } else if (r->sizeEven > r->sizeOdd) {
+ ASSERTMSGLINE(1906, totalEven <= r->sizeEven, "GXPreLoadEntireTexture: Even tmem size does not match the texture size");
+ } else {
+ ASSERTMSGLINE(1908, totalOdd <= r->sizeOdd, "GXPreLoadEntireTexture: Odd tmem size does not match the texture size");
+ }
+#endif
+
+ __GXFlushTextureState();
+ GX_WRITE_RAS_REG(loadImage0);
+ GX_WRITE_RAS_REG(loadImage1);
+ GX_WRITE_RAS_REG(loadImage2);
+ GX_WRITE_RAS_REG(loadImage3);
+
+ if (maxLevelIndex != 0) {
+ tmemAR = tmem1;
+ tmemGB = tmem2;
+ for (i = 0; i < maxLevelIndex; i++) {
+ if (is32bit) {
+ base += nTiles * 2;
+ tmemAR += nTiles;
+ tmemGB += nTiles;
+ } else {
+ base += nTiles;
+ if (i & 1) {
+ tmemGB += nTiles;
+ } else {
+ tmemAR += nTiles;
+ }
+ }
+ tmem1 = (i & 1) ? tmemAR : tmemGB;
+ tmem2 = (i & 1) ? tmemGB : tmemAR;
+ __GetImageTileCount(t->fmt, (u16) (wd >> (i + 1)), (u16) (ht >> (i + 1)), &rowTiles, &colTiles, &cmpTiles);
+ nTiles = rowTiles * colTiles;
+ SET_REG_FIELD(1957, loadImage0, 21, 0, base);
+ SET_REG_FIELD(1958, loadImage1, 15, 0, tmem1);
+ SET_REG_FIELD(1959, loadImage2, 15, 0, tmem2);
+ SET_REG_FIELD(1960, loadImage3, 15, 0, nTiles);
+ GX_WRITE_RAS_REG(loadImage0);
+ GX_WRITE_RAS_REG(loadImage1);
+ GX_WRITE_RAS_REG(loadImage2);
+ GX_WRITE_RAS_REG(loadImage3);
+ }
+ }
+ __GXFlushTextureState();
+
+ // needed to match debug
+ maxLevelIndex; maxLevelIndex; base; base; base; tmem1; tmem1; tmem2; tmem2;
+}
+
+void GXSetTexCoordScaleManually(GXTexCoordID coord, GXBool enable, u16 ss, u16 ts) {
+ CHECK_GXBEGIN(1991, "GXSetTexCoordScaleManually");
+ ASSERTMSGLINEV(1993, coord < GX_MAX_TEXCOORD, "%s: bad texcoord specified", "GXSetTexCoordScaleManually");
+ __GXData->tcsManEnab = (__GXData->tcsManEnab & ~(1 << coord)) | (enable << coord);
+
+ if (enable) {
+ SET_REG_FIELD(1999, __GXData->suTs0[coord], 16, 0, (u16)(ss - 1));
+ SET_REG_FIELD(2000, __GXData->suTs1[coord], 16, 0, (u16)(ts - 1));
+ GX_WRITE_RAS_REG(__GXData->suTs0[coord]);
+ GX_WRITE_RAS_REG(__GXData->suTs1[coord]);
+ __GXData->bpSentNot = 0;
+ }
+}
+
+void GXSetTexCoordCylWrap(GXTexCoordID coord, u8 s_enable, u8 t_enable) {
+ CHECK_GXBEGIN(2023, "GXSetTexCoordCylWrap");
+ ASSERTMSGLINEV(2025, coord < GX_MAX_TEXCOORD, "%s: bad texcoord specified", "GXSetTexCoordCylWrap");
+ SET_REG_FIELD(2027, __GXData->suTs0[coord], 1, 17, s_enable);
+ SET_REG_FIELD(2028, __GXData->suTs1[coord], 1, 17, t_enable);
+
+ if (__GXData->tcsManEnab & (1 << coord)) {
+ GX_WRITE_RAS_REG(__GXData->suTs0[coord]);
+ GX_WRITE_RAS_REG(__GXData->suTs1[coord]);
+ __GXData->bpSentNot = 0;
+ }
+}
+
+void GXSetTexCoordBias(GXTexCoordID coord, u8 s_enable, u8 t_enable) {
+ CHECK_GXBEGIN(2054, "GXSetTexCoordBias");
+ ASSERTMSGLINEV(2056, coord < GX_MAX_TEXCOORD, "%s: bad texcoord specified", "GXSetTexCoordBias");
+ SET_REG_FIELD(2058, __GXData->suTs0[coord], 1, 16, s_enable);
+ SET_REG_FIELD(2059, __GXData->suTs1[coord], 1, 16, t_enable);
+
+ if (__GXData->tcsManEnab & (1 << coord)) {
+ GX_WRITE_RAS_REG(__GXData->suTs0[coord]);
+ GX_WRITE_RAS_REG(__GXData->suTs1[coord]);
+ __GXData->bpSentNot = 0;
+ }
+}
+
+static void __SetSURegs(u32 tmap, u32 tcoord) {
+ u32 w;
+ u32 h;
+ u8 s_bias;
+ u8 t_bias;
+
+ w = GET_REG_FIELD(__GXData->tImage0[tmap], 10, 0);
+ h = GET_REG_FIELD(__GXData->tImage0[tmap], 10, 10);
+ SET_REG_FIELD(2091, __GXData->suTs0[tcoord], 16, 0, w);
+ SET_REG_FIELD(2092, __GXData->suTs1[tcoord], 16, 0, h);
+ s_bias = GET_REG_FIELD(__GXData->tMode0[tmap], 2, 0) == 1;
+ t_bias = GET_REG_FIELD(__GXData->tMode0[tmap], 2, 2) == 1;
+ SET_REG_FIELD(2098, __GXData->suTs0[tcoord], 1, 16, s_bias);
+ SET_REG_FIELD(2099, __GXData->suTs1[tcoord], 1, 16, t_bias);
+ GX_WRITE_RAS_REG(__GXData->suTs0[tcoord]);
+ GX_WRITE_RAS_REG(__GXData->suTs1[tcoord]);
+ __GXData->bpSentNot = 0;
+}
+
+void __GXSetSUTexRegs(void) {
+ u32 nStages;
+ u32 nIndStages;
+ u32 i;
+ u32 map;
+ u32 tmap;
+ u32 coord;
+ u32* ptref;
+
+ if (__GXData->tcsManEnab != 0xFF) {
+ nStages = GET_REG_FIELD(__GXData->genMode, 4, 10) + 1;
+ nIndStages = GET_REG_FIELD(__GXData->genMode, 3, 16);
+ for (i = 0; i < nIndStages; i++) {
+ switch (i) {
+ case 0:
+ tmap = GET_REG_FIELD(__GXData->iref, 3, 0);
+ coord = GET_REG_FIELD(__GXData->iref, 3, 3);
+ break;
+ case 1:
+ tmap = GET_REG_FIELD(__GXData->iref, 3, 6);
+ coord = GET_REG_FIELD(__GXData->iref, 3, 9);
+ break;
+ case 2:
+ tmap = GET_REG_FIELD(__GXData->iref, 3, 12);
+ coord = GET_REG_FIELD(__GXData->iref, 3, 15);
+ break;
+ case 3:
+ tmap = GET_REG_FIELD(__GXData->iref, 3, 18);
+ coord = GET_REG_FIELD(__GXData->iref, 3, 21);
+ break;
+ }
+ if (!(__GXData->tcsManEnab & (1 << coord))) {
+ __SetSURegs(tmap, coord);
+ }
+ }
+
+ for (i = 0; i < nStages; i++) {
+ ptref = &__GXData->tref[i / 2];
+ map = __GXData->texmapId[i];
+ tmap = map & 0xFFFFFEFF;
+ if (i & 1) {
+ coord = GET_REG_FIELD(*ptref, 3, 15);
+ } else {
+ coord = GET_REG_FIELD(*ptref, 3, 3);
+ }
+ if ((tmap != 0xFF) && !(__GXData->tcsManEnab & (1 << coord)) && (__GXData->tevTcEnab & (1 << i))) {
+ __SetSURegs(tmap, coord);
+ }
+ }
+ }
+}
+
+void __GXGetSUTexSize(GXTexCoordID coord, u16* width, u16* height) {
+ *width = (u16)__GXData->suTs0[coord] + 1;
+ *height = (u16)__GXData->suTs1[coord] + 1;
+}
+
+void __GXSetTmemConfig(u32 config) {
+ switch (config) {
+ case 2:
+ GX_WRITE_RAS_REG(0x8c0d8000);
+ GX_WRITE_RAS_REG(0x900dc000);
+
+ GX_WRITE_RAS_REG(0x8d0d8800);
+ GX_WRITE_RAS_REG(0x910dc800);
+
+ GX_WRITE_RAS_REG(0x8e0d9000);
+ GX_WRITE_RAS_REG(0x920dd000);
+
+ GX_WRITE_RAS_REG(0x8f0d9800);
+ GX_WRITE_RAS_REG(0x930dd800);
+
+ GX_WRITE_RAS_REG(0xac0da000);
+ GX_WRITE_RAS_REG(0xb00dc400);
+
+ GX_WRITE_RAS_REG(0xad0da800);
+ GX_WRITE_RAS_REG(0xb10dcc00);
+
+ GX_WRITE_RAS_REG(0xae0db000);
+ GX_WRITE_RAS_REG(0xb20dd400);
+
+ GX_WRITE_RAS_REG(0xaf0db800);
+ GX_WRITE_RAS_REG(0xb30ddc00);
+ break;
+ case 1:
+ GX_WRITE_RAS_REG(0x8c0d8000);
+ GX_WRITE_RAS_REG(0x900dc000);
+
+ GX_WRITE_RAS_REG(0x8d0d8800);
+ GX_WRITE_RAS_REG(0x910dc800);
+
+ GX_WRITE_RAS_REG(0x8e0d9000);
+ GX_WRITE_RAS_REG(0x920dd000);
+
+ GX_WRITE_RAS_REG(0x8f0d9800);
+ GX_WRITE_RAS_REG(0x930dd800);
+
+ GX_WRITE_RAS_REG(0xac0da000);
+ GX_WRITE_RAS_REG(0xb00de000);
+
+ GX_WRITE_RAS_REG(0xad0da800);
+ GX_WRITE_RAS_REG(0xb10de800);
+
+ GX_WRITE_RAS_REG(0xae0db000);
+ GX_WRITE_RAS_REG(0xb20df000);
+
+ GX_WRITE_RAS_REG(0xaf0db800);
+ GX_WRITE_RAS_REG(0xb30df800);
+
+ break;
+ case 0:
+ default:
+ GX_WRITE_RAS_REG(0x8c0d8000);
+ GX_WRITE_RAS_REG(0x900dc000);
+
+ GX_WRITE_RAS_REG(0x8d0d8400);
+ GX_WRITE_RAS_REG(0x910dc400);
+
+ GX_WRITE_RAS_REG(0x8e0d8800);
+ GX_WRITE_RAS_REG(0x920dc800);
+
+ GX_WRITE_RAS_REG(0x8f0d8c00);
+ GX_WRITE_RAS_REG(0x930dcc00);
+
+ GX_WRITE_RAS_REG(0xac0d9000);
+ GX_WRITE_RAS_REG(0xb00dd000);
+
+ GX_WRITE_RAS_REG(0xad0d9400);
+ GX_WRITE_RAS_REG(0xb10dd400);
+
+ GX_WRITE_RAS_REG(0xae0d9800);
+ GX_WRITE_RAS_REG(0xb20dd800);
+
+ GX_WRITE_RAS_REG(0xaf0d9c00);
+ GX_WRITE_RAS_REG(0xb30ddc00);
+
+ break;
+ }
+}
diff --git a/src/revolution/gx/GXTransform.c b/src/revolution/gx/GXTransform.c
new file mode 100644
index 0000000000..0fb885b459
--- /dev/null
+++ b/src/revolution/gx/GXTransform.c
@@ -0,0 +1,607 @@
+#include <revolution/gx.h>
+#include <revolution/mtx.h>
+#include <revolution/os.h>
+
+#include "__gx.h"
+
+void GXProject(f32 x, f32 y, f32 z, const Mtx mtx, const f32* pm, const f32* vp, f32* sx, f32* sy, f32* sz) {
+ Vec peye;
+ f32 xc;
+ f32 yc;
+ f32 zc;
+ f32 wc;
+
+ ASSERTMSGLINE(181, pm && vp && sx && sy && sz, "GXGet*: invalid null pointer");
+
+ peye.x = mtx[0][3] + ((mtx[0][2] * z) + ((mtx[0][0] * x) + (mtx[0][1] * y)));
+ peye.y = mtx[1][3] + ((mtx[1][2] * z) + ((mtx[1][0] * x) + (mtx[1][1] * y)));
+ peye.z = mtx[2][3] + ((mtx[2][2] * z) + ((mtx[2][0] * x) + (mtx[2][1] * y)));
+ if (pm[0] == 0.0f) {
+ xc = (peye.x * pm[1]) + (peye.z * pm[2]);
+ yc = (peye.y * pm[3]) + (peye.z * pm[4]);
+ zc = pm[6] + (peye.z * pm[5]);
+ wc = 1.0f / -peye.z;
+ } else {
+ xc = pm[2] + (peye.x * pm[1]);
+ yc = pm[4] + (peye.y * pm[3]);
+ zc = pm[6] + (peye.z * pm[5]);
+ wc = 1.0f;
+ }
+ *sx = (vp[2] / 2.0f) + (vp[0] + (wc * (xc * vp[2] / 2.0f)));
+ *sy = (vp[3] / 2.0f) + (vp[1] + (wc * (-yc * vp[3] / 2.0f)));
+ *sz = vp[5] + (wc * (zc * (vp[5] - vp[4])));
+}
+
+static void WriteProjPS(const __REGISTER f32 proj[6], __REGISTER volatile void* dest) {
+ __REGISTER f32 p01, p23, p45;
+
+#ifdef __MWERKS__
+ asm {
+ psq_l p01, 0(proj), 0, 0
+ psq_l p23, 8(proj), 0, 0
+ psq_l p45, 16(proj), 0, 0
+ psq_st p01, 0(dest), 0, 0
+ psq_st p23, 0(dest), 0, 0
+ psq_st p45, 0(dest), 0, 0
+ }
+#endif
+}
+
+static void Copy6Floats(const __REGISTER f32 src[6], __REGISTER volatile f32* dest) {
+ __REGISTER f32 ps01, ps23, ps45;
+
+ asm {
+ psq_l ps01, 0(src), 0, 0
+ psq_l ps23, 8(src), 0, 0
+ psq_l ps45, 16(src), 0, 0
+ psq_st ps01, 0(dest), 0, 0
+ psq_st ps23, 8(dest), 0, 0
+ psq_st ps45, 16(dest), 0, 0
+ }
+}
+
+void __GXSetProjection(void) {
+ u32 reg = 0x00061020;
+ GX_WRITE_U8(0x10);
+ GX_WRITE_U32(reg);
+#if DEBUG
+ GX_WRITE_XF_REG_F(32, __GXData->projMtx[0]);
+ GX_WRITE_XF_REG_F(33, __GXData->projMtx[1]);
+ GX_WRITE_XF_REG_F(34, __GXData->projMtx[2]);
+ GX_WRITE_XF_REG_F(35, __GXData->projMtx[3]);
+ GX_WRITE_XF_REG_F(36, __GXData->projMtx[4]);
+ GX_WRITE_XF_REG_F(37, __GXData->projMtx[5]);
+ GX_WRITE_XF_REG_2(38, __GXData->projType);
+#else
+ WriteProjPS(__GXData->projMtx, (volatile void*)GXFIFO_ADDR);
+ GX_WRITE_U32(__GXData->projType);
+#endif
+}
+
+void GXSetProjection(const Mtx44 mtx, GXProjectionType type) {
+ CHECK_GXBEGIN(307, "GXSetProjection");
+
+ __GXData->projType = type;
+ __GXData->projMtx[0] = mtx[0][0];
+ __GXData->projMtx[2] = mtx[1][1];
+ __GXData->projMtx[4] = mtx[2][2];
+ __GXData->projMtx[5] = mtx[2][3];
+ if (type == GX_ORTHOGRAPHIC) {
+ __GXData->projMtx[1] = mtx[0][3];
+ __GXData->projMtx[3] = mtx[1][3];
+ } else {
+ __GXData->projMtx[1] = mtx[0][2];
+ __GXData->projMtx[3] = mtx[1][2];
+ }
+
+ __GXData->dirtyState |= 0x8000000;
+}
+
+void GXSetProjectionv(const f32* ptr) {
+ CHECK_GXBEGIN(351, "GXSetProjectionv");
+
+ __GXData->projType = ptr[0] == 0.0f ? GX_PERSPECTIVE : GX_ORTHOGRAPHIC;
+
+#if DEBUG
+ __GXData->projMtx[0] = ptr[1];
+ __GXData->projMtx[1] = ptr[2];
+ __GXData->projMtx[2] = ptr[3];
+ __GXData->projMtx[3] = ptr[4];
+ __GXData->projMtx[4] = ptr[5];
+ __GXData->projMtx[5] = ptr[6];
+#else
+ Copy6Floats(&ptr[1], __GXData->projMtx);
+#endif
+
+ __GXData->dirtyState |= 0x8000000;
+}
+
+#define qr0 0
+
+void GXGetProjectionv(f32* ptr) {
+ ASSERTMSGLINE(382, ptr, "GXGet*: invalid null pointer");
+
+ ptr[0] = (u32)__GXData->projType != GX_PERSPECTIVE ? 1.0f : 0.0f;
+
+#if DEBUG
+ ptr[1] = __GXData->projMtx[0];
+ ptr[2] = __GXData->projMtx[1];
+ ptr[3] = __GXData->projMtx[2];
+ ptr[4] = __GXData->projMtx[3];
+ ptr[5] = __GXData->projMtx[4];
+ ptr[6] = __GXData->projMtx[5];
+#else
+ Copy6Floats(__GXData->projMtx, &ptr[1]);
+#endif
+}
+
+static void WriteMTXPS4x3(const __REGISTER f32 mtx[3][4], __REGISTER volatile f32* dest) {
+ __REGISTER f32 a00_a01;
+ __REGISTER f32 a02_a03;
+ __REGISTER f32 a10_a11;
+ __REGISTER f32 a12_a13;
+ __REGISTER f32 a20_a21;
+ __REGISTER f32 a22_a23;
+
+ asm {
+ psq_l a00_a01, 0x00(mtx), 0, qr0
+ psq_l a02_a03, 0x08(mtx), 0, qr0
+ psq_l a10_a11, 0x10(mtx), 0, qr0
+ psq_l a12_a13, 0x18(mtx), 0, qr0
+ psq_l a20_a21, 0x20(mtx), 0, qr0
+ psq_l a22_a23, 0x28(mtx), 0, qr0
+ psq_st a00_a01, 0(dest), 0, qr0
+ psq_st a02_a03, 0(dest), 0, qr0
+ psq_st a10_a11, 0(dest), 0, qr0
+ psq_st a12_a13, 0(dest), 0, qr0
+ psq_st a20_a21, 0(dest), 0, qr0
+ psq_st a22_a23, 0(dest), 0, qr0
+ }
+}
+
+static void WriteMTXPS3x3from3x4(__REGISTER f32 mtx[3][4], __REGISTER volatile f32* dest) {
+ __REGISTER f32 a00_a01;
+ __REGISTER f32 a02_a03;
+ __REGISTER f32 a10_a11;
+ __REGISTER f32 a12_a13;
+ __REGISTER f32 a20_a21;
+ __REGISTER f32 a22_a23;
+
+ asm {
+ psq_l a00_a01, 0x00(mtx), 0, qr0
+ lfs a02_a03, 0x08(mtx)
+ psq_l a10_a11, 0x10(mtx), 0, qr0
+ lfs a12_a13, 0x18(mtx)
+ psq_l a20_a21, 0x20(mtx), 0, qr0
+ lfs a22_a23, 0x28(mtx)
+ psq_st a00_a01, 0(dest), 0, qr0
+ stfs a02_a03, 0(dest)
+ psq_st a10_a11, 0(dest), 0, qr0
+ stfs a12_a13, 0(dest)
+ psq_st a20_a21, 0(dest), 0, qr0
+ stfs a22_a23, 0(dest)
+ }
+}
+
+static void WriteMTXPS3x3(__REGISTER f32 mtx[3][3], __REGISTER volatile f32* dest) {
+ __REGISTER f32 a00_a01;
+ __REGISTER f32 a02_a10;
+ __REGISTER f32 a11_a12;
+ __REGISTER f32 a20_a21;
+ __REGISTER f32 a22_nnn;
+
+ asm {
+ psq_l a00_a01, 0x00(mtx), 0, qr0
+ psq_l a02_a10, 0x08(mtx), 0, qr0
+ psq_l a11_a12, 0x10(mtx), 0, qr0
+ psq_l a20_a21, 0x18(mtx), 0, qr0
+ lfs a22_nnn, 0x20(mtx)
+ psq_st a00_a01, 0(dest), 0, qr0
+ psq_st a02_a10, 0(dest), 0, qr0
+ psq_st a11_a12, 0(dest), 0, qr0
+ psq_st a20_a21, 0(dest), 0, qr0
+ stfs a22_nnn, 0(dest)
+ }
+}
+
+static void WriteMTXPS4x2(const __REGISTER f32 mtx[2][4], __REGISTER volatile f32* dest) {
+ __REGISTER f32 a00_a01;
+ __REGISTER f32 a02_a03;
+ __REGISTER f32 a10_a11;
+ __REGISTER f32 a12_a13;
+
+ asm {
+ psq_l a00_a01, 0x00(mtx), 0, qr0
+ psq_l a02_a03, 0x08(mtx), 0, qr0
+ psq_l a10_a11, 0x10(mtx), 0, qr0
+ psq_l a12_a13, 0x18(mtx), 0, qr0
+ psq_st a00_a01, 0(dest), 0, qr0
+ psq_st a02_a03, 0(dest), 0, qr0
+ psq_st a10_a11, 0(dest), 0, qr0
+ psq_st a12_a13, 0(dest), 0, qr0
+ }
+}
+
+#define GX_WRITE_MTX_ELEM(addr, value) \
+do { \
+ f32 xfData = (value); \
+ GX_WRITE_F32(value); \
+ VERIF_MTXLIGHT((addr), *(u32 *)&xfData); \
+} while (0)
+
+void GXLoadPosMtxImm(const Mtx mtx, u32 id) {
+ u32 reg;
+ u32 addr;
+
+ CHECK_GXBEGIN(516, "GXLoadPosMtxImm");
+
+ addr = id * 4;
+ reg = addr | 0xB0000;
+
+ GX_WRITE_U8(0x10);
+ GX_WRITE_U32(reg);
+#if DEBUG
+ GX_WRITE_MTX_ELEM(addr + 0, mtx[0][0]);
+ GX_WRITE_MTX_ELEM(addr + 1, mtx[0][1]);
+ GX_WRITE_MTX_ELEM(addr + 2, mtx[0][2]);
+ GX_WRITE_MTX_ELEM(addr + 3, mtx[0][3]);
+ GX_WRITE_MTX_ELEM(addr + 4, mtx[1][0]);
+ GX_WRITE_MTX_ELEM(addr + 5, mtx[1][1]);
+ GX_WRITE_MTX_ELEM(addr + 6, mtx[1][2]);
+ GX_WRITE_MTX_ELEM(addr + 7, mtx[1][3]);
+ GX_WRITE_MTX_ELEM(addr + 8, mtx[2][0]);
+ GX_WRITE_MTX_ELEM(addr + 9, mtx[2][1]);
+ GX_WRITE_MTX_ELEM(addr + 10, mtx[2][2]);
+ GX_WRITE_MTX_ELEM(addr + 11, mtx[2][3]);
+#else
+ WriteMTXPS4x3(mtx, &GXWGFifo.f32);
+#endif
+}
+
+void GXLoadPosMtxIndx(u16 mtx_indx, u32 id) {
+ u32 offset;
+ u32 reg;
+
+ CHECK_GXBEGIN(555, "GXLoadPosMtxIndx");
+ offset = id * 4;
+ reg = 0;
+ SET_REG_FIELD(561, reg, 12, 0, offset);
+ SET_REG_FIELD(563, reg, 4, 12, 11);
+ SET_REG_FIELD(563, reg, 16, 16, mtx_indx);
+ GX_WRITE_U8(0x20);
+ GX_WRITE_U32(reg);
+#if DEBUG
+ __GXShadowIndexState(4, reg);
+#endif
+}
+
+void GXLoadNrmMtxImm(const Mtx mtx, u32 id) {
+ u32 reg;
+ u32 addr;
+
+ CHECK_GXBEGIN(597, "GXLoadNrmMtxImm");
+
+ addr = id * 3 + 0x400;
+ reg = addr | 0x80000;
+
+ GX_WRITE_U8(0x10);
+ GX_WRITE_U32(reg);
+#if DEBUG
+ GX_WRITE_MTX_ELEM(addr + 0, mtx[0][0]);
+ GX_WRITE_MTX_ELEM(addr + 1, mtx[0][1]);
+ GX_WRITE_MTX_ELEM(addr + 2, mtx[0][2]);
+ GX_WRITE_MTX_ELEM(addr + 3, mtx[1][0]);
+ GX_WRITE_MTX_ELEM(addr + 4, mtx[1][1]);
+ GX_WRITE_MTX_ELEM(addr + 5, mtx[1][2]);
+ GX_WRITE_MTX_ELEM(addr + 6, mtx[2][0]);
+ GX_WRITE_MTX_ELEM(addr + 7, mtx[2][1]);
+ GX_WRITE_MTX_ELEM(addr + 8, mtx[2][2]);
+#else
+ WriteMTXPS3x3from3x4((void*)mtx, &GXWGFifo.f32);
+#endif
+}
+
+void GXLoadNrmMtxImm3x3(const f32 mtx[3][3], u32 id) {
+ u32 reg;
+ u32 addr;
+
+ CHECK_GXBEGIN(633, "GXLoadNrmMtxImm3x3");
+
+ addr = id * 3 + 0x400;
+ reg = addr | 0x80000;
+
+ GX_WRITE_U8(0x10);
+ GX_WRITE_U32(reg);
+#if DEBUG
+ GX_WRITE_MTX_ELEM(addr + 0, mtx[0][0]);
+ GX_WRITE_MTX_ELEM(addr + 1, mtx[0][1]);
+ GX_WRITE_MTX_ELEM(addr + 2, mtx[0][2]);
+ GX_WRITE_MTX_ELEM(addr + 3, mtx[1][0]);
+ GX_WRITE_MTX_ELEM(addr + 4, mtx[1][1]);
+ GX_WRITE_MTX_ELEM(addr + 5, mtx[1][2]);
+ GX_WRITE_MTX_ELEM(addr + 6, mtx[2][0]);
+ GX_WRITE_MTX_ELEM(addr + 7, mtx[2][1]);
+ GX_WRITE_MTX_ELEM(addr + 8, mtx[2][2]);
+#else
+ WriteMTXPS3x3((void*)mtx, &GXWGFifo.f32);
+#endif
+}
+
+void GXLoadNrmMtxIndx3x3(u16 mtx_indx, u32 id) {
+ u32 offset;
+ u32 reg;
+
+ CHECK_GXBEGIN(679, "GXLoadNrmMtxIndx3x3");
+ offset = id * 3 + 0x400;
+ reg = 0;
+ SET_REG_FIELD(685, reg, 12, 0, offset);
+ SET_REG_FIELD(687, reg, 4, 12, 8);
+ SET_REG_FIELD(687, reg, 16, 16, mtx_indx);
+ GX_WRITE_U8(0x28);
+ GX_WRITE_U32(reg);
+#if DEBUG
+ __GXShadowIndexState(5, reg);
+#endif
+}
+
+void GXSetCurrentMtx(u32 id) {
+ CHECK_GXBEGIN(717, "GXSetCurrentMtx");
+ SET_REG_FIELD(721, __GXData->matIdxA, 6, 0, id);
+ __GXData->dirtyState |= 0x4000000;
+}
+
+void GXLoadTexMtxImm(const f32 mtx[][4], u32 id, GXTexMtxType type) {
+ u32 reg;
+ u32 addr;
+ u32 count;
+
+ CHECK_GXBEGIN(750, "GXLoadTexMtxImm");
+
+ if (id >= GX_PTTEXMTX0) {
+ addr = (id - GX_PTTEXMTX0) * 4 + 0x500;
+ ASSERTMSGLINE(760, type == GX_MTX3x4, "GXLoadTexMtx: Invalid matrix type");
+ } else {
+ addr = id * 4;
+ }
+ count = (type == GX_MTX2x4) ? 8 : 12;
+ reg = addr | ((count - 1) << 16);
+
+ GX_WRITE_U8(0x10);
+ GX_WRITE_U32(reg);
+#if DEBUG
+ GX_WRITE_MTX_ELEM(addr + 0, mtx[0][0]);
+ GX_WRITE_MTX_ELEM(addr + 1, mtx[0][1]);
+ GX_WRITE_MTX_ELEM(addr + 2, mtx[0][2]);
+ GX_WRITE_MTX_ELEM(addr + 3, mtx[0][3]);
+ GX_WRITE_MTX_ELEM(addr + 4, mtx[1][0]);
+ GX_WRITE_MTX_ELEM(addr + 5, mtx[1][1]);
+ GX_WRITE_MTX_ELEM(addr + 6, mtx[1][2]);
+ GX_WRITE_MTX_ELEM(addr + 7, mtx[1][3]);
+ if (type == GX_MTX3x4) {
+ GX_WRITE_MTX_ELEM(addr + 8, mtx[2][0]);
+ GX_WRITE_MTX_ELEM(addr + 9, mtx[2][1]);
+ GX_WRITE_MTX_ELEM(addr + 10, mtx[2][2]);
+ GX_WRITE_MTX_ELEM(addr + 11, mtx[2][3]);
+ }
+#else
+ if (type == GX_MTX3x4) {
+ WriteMTXPS4x3(mtx, &GXWGFifo.f32);
+ } else {
+ WriteMTXPS4x2(mtx, &GXWGFifo.f32);
+ }
+#endif
+}
+
+void GXLoadTexMtxIndx(u16 mtx_indx, u32 id, GXTexMtxType type) {
+ u32 offset;
+ u32 reg;
+ u32 count;
+
+ CHECK_GXBEGIN(813, "GXLoadTexMtxIndx");
+
+ if (id >= GX_PTTEXMTX0) {
+ offset = (id - GX_PTTEXMTX0) * 4 + 0x500;
+ ASSERTMSGLINE(0x337, type == GX_MTX3x4, "GXLoadTexMtx: Invalid matrix type");
+ } else {
+ offset = id * 4;
+ }
+ count = (type == GX_MTX2x4) ? 8 : 12;
+
+ reg = 0;
+ SET_REG_FIELD(830, reg, 12, 0, offset);
+ SET_REG_FIELD(831, reg, 4, 12, (count - 1));
+ SET_REG_FIELD(832, reg, 16, 16, mtx_indx);
+ GX_WRITE_U8(0x30);
+ GX_WRITE_U32(reg);
+#if DEBUG
+ __GXShadowIndexState(6, reg);
+#endif
+}
+
+void __GXSetViewport(void) {
+ f32 sx;
+ f32 sy;
+ f32 sz;
+ f32 ox;
+ f32 oy;
+ f32 oz;
+ f32 zmin;
+ f32 zmax;
+ u32 reg;
+
+ sx = __GXData->vpWd / 2.0f;
+ sy = -__GXData->vpHt / 2.0f;
+ ox = 342.0f + (__GXData->vpLeft + (__GXData->vpWd / 2.0f));
+ oy = 342.0f + (__GXData->vpTop + (__GXData->vpHt / 2.0f));
+
+ zmin = __GXData->vpNearz * __GXData->zScale;
+ zmax = __GXData->vpFarz * __GXData->zScale;
+
+ sz = zmax - zmin;
+ oz = zmax + __GXData->zOffset;
+
+ reg = 0x5101A;
+ GX_WRITE_U8(0x10);
+ GX_WRITE_U32(reg);
+ GX_WRITE_XF_REG_F(26, sx);
+ GX_WRITE_XF_REG_F(27, sy);
+ GX_WRITE_XF_REG_F(28, sz);
+ GX_WRITE_XF_REG_F(29, ox);
+ GX_WRITE_XF_REG_F(30, oy);
+ GX_WRITE_XF_REG_F(31, oz);
+}
+
+void GXSetViewportJitter(f32 left, f32 top, f32 wd, f32 ht, f32 nearz, f32 farz, u32 field) {
+ CHECK_GXBEGIN(912, "GXSetViewport"); // not the correct function name
+
+ if (field == 0) {
+ top -= 0.5f;
+ }
+
+ __GXData->vpLeft = left;
+ __GXData->vpTop = top;
+ __GXData->vpWd = wd;
+ __GXData->vpHt = ht;
+ __GXData->vpNearz = nearz;
+ __GXData->vpFarz = farz;
+
+ __GXData->dirtyState |= 0x10000000;
+}
+
+void GXSetViewport(f32 left, f32 top, f32 wd, f32 ht, f32 nearz, f32 farz) {
+ GXSetViewportJitter(left, top, wd, ht, nearz, farz, 1);
+}
+
+void GXGetViewportv(f32* vp) {
+ ASSERTMSGLINE(977, vp, "GXGet*: invalid null pointer");
+
+#if DEBUG
+ vp[0] = __GXData->vpLeft;
+ vp[1] = __GXData->vpTop;
+ vp[2] = __GXData->vpWd;
+ vp[3] = __GXData->vpHt;
+ vp[4] = __GXData->vpNearz;
+ vp[5] = __GXData->vpFarz;
+#else
+ Copy6Floats(&__GXData->vpLeft, vp);
+#endif
+}
+
+#define GX_WRITE_XF_REG_F_(addr, value) \
+do { \
+ GX_WRITE_U8(0x10); \
+ GX_WRITE_U32(0x1000 + (addr)); \
+ { \
+ f32 xfData = (value); \
+ GX_WRITE_F32(value); \
+ VERIF_XF_REG_alt(addr, *(u32 *)&xfData); \
+ } \
+} while (0)
+
+void GXSetZScaleOffset(f32 scale, f32 offset) {
+ f32 sz;
+ f32 oz;
+ f32 zmin;
+ f32 zmax;
+
+ CHECK_GXBEGIN(996, "GXSetZScaleOffset");
+
+ oz = offset * 1.6777215e7f;
+ __GXData->zOffset = oz;
+
+ sz = (scale * 1.6777215e7f) + 1.0f;
+ __GXData->zScale = sz;
+
+ zmin = __GXData->vpNearz * sz;
+ zmax = __GXData->vpFarz * sz;
+ sz = zmax - zmin;
+ oz = oz + zmax;
+
+ GX_WRITE_XF_REG_F_(0x1C, sz);
+ GX_WRITE_XF_REG_F_(0x1F, oz);
+
+ __GXData->bpSentNot = 1;
+}
+
+void GXSetScissor(u32 left, u32 top, u32 wd, u32 ht) {
+ u32 tp;
+ u32 lf;
+ u32 bm;
+ u32 rt;
+
+ CHECK_GXBEGIN(1057, "GXSetScissor");
+ ASSERTMSGLINE(1058, left < 1706, "GXSetScissor: Left origin > 1708");
+ ASSERTMSGLINE(1059, top < 1706, "GXSetScissor: top origin > 1708");
+ ASSERTMSGLINE(1060, left + wd < 1706, "GXSetScissor: right edge > 1708");
+ ASSERTMSGLINE(1061, top + ht < 1706, "GXSetScissor: bottom edge > 1708");
+
+ tp = top + 342;
+ lf = left + 342;
+ bm = tp + ht - 1;
+ rt = lf + wd - 1;
+
+ SET_REG_FIELD(1068, __GXData->suScis0, 11, 0, tp);
+ SET_REG_FIELD(1069, __GXData->suScis0, 11, 12, lf);
+ SET_REG_FIELD(1071, __GXData->suScis1, 11, 0, bm);
+ SET_REG_FIELD(1072, __GXData->suScis1, 11, 12, rt);
+
+ GX_WRITE_RAS_REG(__GXData->suScis0);
+ GX_WRITE_RAS_REG(__GXData->suScis1);
+ __GXData->bpSentNot = 0;
+}
+
+void GXGetScissor(u32* left, u32* top, u32* wd, u32* ht) {
+ u32 tp;
+ u32 lf;
+ u32 bm;
+ u32 rt;
+
+ ASSERTMSGLINE(1098, left && top && wd && ht, "GXGet*: invalid null pointer");
+
+ tp = __GXData->suScis0 & 0x7FF;
+ lf = (__GXData->suScis0 & 0x7FF000) >> 12;
+ bm = __GXData->suScis1 & 0x7FF;
+ rt = (__GXData->suScis1 & 0x7FF000) >> 12;
+
+ *left = lf - 342;
+ *top = tp - 342;
+ *wd = rt - lf + 1;
+ *ht = bm - tp + 1;
+}
+
+void GXSetScissorBoxOffset(s32 x_off, s32 y_off) {
+ u32 reg = 0;
+ u32 hx;
+ u32 hy;
+
+ CHECK_GXBEGIN(1128, "GXSetScissorBoxOffset");
+
+ ASSERTMSGLINE(1131, !!((u32)(x_off + 342) < 2048), "GXSetScissorBoxOffset: Invalid X offset");
+ ASSERTMSGLINE(1133, !!((u32)(y_off + 342) < 2048), "GXSetScissorBoxOffset: Invalid Y offset");
+
+ hx = (u32)(x_off + 342) >> 1;
+ hy = (u32)(y_off + 342) >> 1;
+
+ SET_REG_FIELD(1138, reg, 10, 0, hx);
+ SET_REG_FIELD(1139, reg, 10, 10, hy);
+ SET_REG_FIELD(1131, reg, 8, 24, 0x59);
+ GX_WRITE_RAS_REG(reg);
+ __GXData->bpSentNot = 0;
+}
+
+void GXSetClipMode(GXClipMode mode) {
+ CHECK_GXBEGIN(1160, "GXSetClipMode");
+ GX_WRITE_XF_REG(0x1005, mode, 0);
+ __GXData->bpSentNot = 1;
+}
+
+void __GXSetMatrixIndex(GXAttr matIdxAttr) {
+ if (matIdxAttr < GX_VA_TEX4MTXIDX) {
+ GX_WRITE_SOME_REG4(8, 0x30, __GXData->matIdxA, -12);
+ GX_WRITE_XF_REG(0x1018, __GXData->matIdxA, 0);
+ } else {
+ GX_WRITE_SOME_REG4(8, 0x40, __GXData->matIdxB, -12);
+ GX_WRITE_XF_REG(0x1019, __GXData->matIdxB, 0);
+ }
+ __GXData->bpSentNot = 1;
+}
diff --git a/src/revolution/gx/GXVerifRAS.c b/src/revolution/gx/GXVerifRAS.c
new file mode 100644
index 0000000000..cdb935069b
--- /dev/null
+++ b/src/revolution/gx/GXVerifRAS.c
@@ -0,0 +1,628 @@
+#if DEBUG
+
+#include <stdio.h>
+
+#include <revolution/gx.h>
+
+#include "__gx.h"
+
+static char __data_0[] = "RGB multisample";
+static char _305[] = "GX_TEVPREV(color)";
+static char _306[] = "GX_TEVPREV(alpha)";
+static char _307[] = "GX_TEVREG0(color)";
+static char _308[] = "GX_TEVREG0(alpha)";
+static char _309[] = "GX_TEVREG1(color)";
+static char _310[] = "GX_TEVREG1(alpha)";
+static char _311[] = "GX_TEVREG2(color)";
+static char _312[] = "GX_TEVREG2(alpha)";
+
+static char* TevRegNames[8] = {0};
+
+#define SOME_GET_REG_MACRO(reg, size, shift) ((u32)((reg) << (shift)) & ((1 << (size)) - 2))
+#define SOME_GET_REG_MACRO2(reg, size, shift) ((u32)((reg) >> (shift)) & ((1 << (size)) - 2))
+
+void __GXVerifySU(void) {
+ s32 scis_l;
+ s32 scis_r;
+ s32 scis_t;
+ s32 scis_b;
+
+ scis_l = (u32)GET_REG_FIELD(__gxVerif->rasRegs[32], 11, 12);
+ scis_t = (u32)GET_REG_FIELD(__gxVerif->rasRegs[32], 11, 0);
+ scis_r = (u32)GET_REG_FIELD(__gxVerif->rasRegs[33], 11, 12);
+ scis_b = (u32)GET_REG_FIELD(__gxVerif->rasRegs[33], 11, 0);
+
+ scis_l = scis_l - (u32)SOME_GET_REG_MACRO(__gxVerif->rasRegs[89], 11, 1);
+ scis_r = scis_r - (u32)SOME_GET_REG_MACRO(__gxVerif->rasRegs[89], 11, 1);
+ scis_t = scis_t - (u32)SOME_GET_REG_MACRO2(__gxVerif->rasRegs[89], 11, 9);
+ scis_b = scis_b - (u32)SOME_GET_REG_MACRO2(__gxVerif->rasRegs[89], 11, 9);
+
+ if (scis_l < 0 && __gxVerif->verifyLevel >= __gxvWarnLev[GXWARN_SCISSOR_RECT_LEFT]) {
+ __GX_WARNF(GXWARN_SCISSOR_RECT_LEFT, 0);
+ }
+
+ if (scis_t < 0 && __gxVerif->verifyLevel >= __gxvWarnLev[GXWARN_SCISSOR_RECT_TOP]) {
+ __GX_WARNF(GXWARN_SCISSOR_RECT_TOP, 0);
+ }
+
+ switch (__gxVerif->rasRegs[67] & 7) {
+ case 4:
+ case 5:
+ if (scis_r > 719 && __gxVerif->verifyLevel >= __gxvWarnLev[GXWARN_SCISSOR_RECT_RIGHT]) {
+ __GX_WARNF(GXWARN_SCISSOR_RECT_RIGHT, 719, "YUV");
+ }
+
+ if (scis_b > 575 && __gxVerif->verifyLevel >= __gxvWarnLev[GXWARN_SCISSOR_RECT_BOT]) {
+ __GX_WARNF(GXWARN_SCISSOR_RECT_BOT, 575, "YUV");
+ }
+ break;
+ case 0:
+ case 1:
+ case 3:
+ if (scis_r > 639 && __gxVerif->verifyLevel >= __gxvWarnLev[GXWARN_SCISSOR_RECT_RIGHT]) {
+ __GX_WARNF(GXWARN_SCISSOR_RECT_RIGHT, 639, "RGB");
+ }
+
+ if (scis_b > 527 && __gxVerif->verifyLevel >= __gxvWarnLev[GXWARN_SCISSOR_RECT_BOT]) {
+ __GX_WARNF(GXWARN_SCISSOR_RECT_BOT, 527, "RGB");
+ }
+ break;
+ case 2:
+ if (scis_r > 639 && __gxVerif->verifyLevel >= __gxvWarnLev[GXWARN_SCISSOR_RECT_RIGHT]) {
+ __GX_WARNF(GXWARN_SCISSOR_RECT_RIGHT, 639, __data_0);
+ }
+
+ if (scis_b > 263 && __gxVerif->verifyLevel >= __gxvWarnLev[GXWARN_SCISSOR_RECT_BOT]) {
+ __GX_WARNF(GXWARN_SCISSOR_RECT_BOT, 263, __data_0);
+ }
+ break;
+ }
+}
+
+void __GXVerifyBUMP(void) {
+ u32 i;
+ u32 nBmp;
+ u32 nTev;
+ u32 nTex;
+ u32 matrix;
+
+ nBmp = GET_REG_FIELD(__gxVerif->rasRegs[0], 3, 16);
+ nTex = GET_REG_FIELD(__gxVerif->rasRegs[0], 4, 0);
+ nTev = GET_REG_FIELD(__gxVerif->rasRegs[0], 4, 10) + 1;
+
+ for (i = 0; i < nTev; i++) {
+ matrix = GET_REG_FIELD(__gxVerif->rasRegs[16 + i], 4, 9);
+ if (__gxVerif->verifyLevel >= GX_WARN_SEVERE) {
+ if ((u32)(__gxVerif->rasRegs[16 + i] & 0xFF000000) + 0x01000000 == 0U && __gxVerif->verifyLevel >= __gxvWarnLev[7]) {
+ sprintf(__gxvDummyStr, __gxvWarnings[7], i);
+ __gxVerif->cb(__gxvWarnLev[7], 7U, __gxvDummyStr);
+ }
+
+ if ((GET_REG_FIELD(__gxVerif->rasRegs[16 + i], 2, 7) != 0 || matrix != 0)
+ && GET_REG_FIELD(__gxVerif->rasRegs[16 + i], 2, 0) >= nBmp && __gxVerif->verifyLevel >= __gxvWarnLev[8]) {
+ sprintf(__gxvDummyStr, __gxvWarnings[8], i);
+ __gxVerif->cb(__gxvWarnLev[8], 8U, __gxvDummyStr);
+ }
+
+ if (matrix != 0) {
+ matrix = (matrix & 3) - 1;
+ if (((u32)(__gxVerif->rasRegs[(matrix * 3) + 6] & 0xFF000000) + 0x01000000 == 0
+ || (u32)(__gxVerif->rasRegs[(matrix * 3) + 7] & 0xFF000000) + 0x01000000 == 0U
+ || (u32)(__gxVerif->rasRegs[(matrix * 3) + 8] & 0xFF000000) + 0x01000000 == 0U) && __gxVerif->verifyLevel >= __gxvWarnLev[9]) {
+ sprintf(__gxvDummyStr, __gxvWarnings[9], matrix, i);
+ __gxVerif->cb(__gxvWarnLev[9], 9U, __gxvDummyStr);
+ }
+ }
+ }
+ }
+
+ if (__gxVerif->verifyLevel >= GX_WARN_SEVERE) {
+ if (nBmp != 0 && (u32)(__gxVerif->rasRegs[0x27] & 0xFF000000) + 0x01000000 == 0 && __gxVerif->verifyLevel >= __gxvWarnLev[10]) {
+ __gxVerif->cb(__gxvWarnLev[10], 0xAU, __gxvWarnings[10]);
+ }
+
+ if (nBmp != 0 && (u32)(__gxVerif->rasRegs[0x25] & 0xFF000000) + 0x01000000 == 0 && __gxVerif->verifyLevel >= __gxvWarnLev[11]) {
+ sprintf(__gxvDummyStr, __gxvWarnings[11], 0U, 1);
+ __gxVerif->cb(__gxvWarnLev[11], 0xBU, __gxvDummyStr);
+ }
+
+ if (nBmp > 2U && (u32)(__gxVerif->rasRegs[0x26] & 0xFF000000) + 0x01000000 == 0 && __gxVerif->verifyLevel >= __gxvWarnLev[11]) {
+ sprintf(__gxvDummyStr, __gxvWarnings[11], 2U, 3);
+ __gxVerif->cb(__gxvWarnLev[11], 0xBU, __gxvDummyStr);
+ }
+
+ if (nBmp != 0 && GET_REG_FIELD(__gxVerif->rasRegs[0x27], 3, 3) >= nTex && __gxVerif->verifyLevel >= __gxvWarnLev[12]) {
+ sprintf(__gxvDummyStr, __gxvWarnings[12], 0U);
+ __gxVerif->cb(__gxvWarnLev[12], 0xCU, __gxvDummyStr);
+ }
+
+ if (nBmp > 1U && GET_REG_FIELD(__gxVerif->rasRegs[0x27], 3, 9) >= nTex && __gxVerif->verifyLevel >= __gxvWarnLev[12]) {
+ sprintf(__gxvDummyStr, __gxvWarnings[12], 1U);
+ __gxVerif->cb(__gxvWarnLev[12], 0xCU, __gxvDummyStr);
+ }
+
+ if (nBmp > 2U && GET_REG_FIELD(__gxVerif->rasRegs[0x27], 3, 15) >= nTex && __gxVerif->verifyLevel >= __gxvWarnLev[12]) {
+ sprintf(__gxvDummyStr, __gxvWarnings[12], 2U);
+ __gxVerif->cb(__gxvWarnLev[12], 0xCU, __gxvDummyStr);
+ }
+
+ if (nBmp > 3U && GET_REG_FIELD(__gxVerif->rasRegs[0x27], 3, 21) >= nTex && __gxVerif->verifyLevel >= __gxvWarnLev[12]) {
+ sprintf(__gxvDummyStr, __gxvWarnings[12], 3U);
+ __gxVerif->cb(__gxvWarnLev[12], 0xCU, __gxvDummyStr);
+ }
+
+ if (nBmp != 0 && GET_REG_FIELD(__gxVerif->rasRegs[0x10], 1, 20) && __gxVerif->verifyLevel >= __gxvWarnLev[13]) {
+ __gxVerif->cb(__gxvWarnLev[13], 0xDU, __gxvWarnings[13]);
+ }
+
+ if (nBmp != 0 && GET_REG_FIELD(__gxVerif->rasRegs[0x10], 2, 7) != 0 && __gxVerif->verifyLevel >= __gxvWarnLev[14]) {
+ __gxVerif->cb(__gxvWarnLev[14], 0xEU, __gxvWarnings[14]);
+ }
+
+ if ((u32)(__gxVerif->rasRegs[0xF] & 0xFF000000) + 0x01000000 == 0 && (nTex != 0 || nBmp != 0) && __gxVerif->verifyLevel >= __gxvWarnLev[15]) {
+ __gxVerif->cb(__gxvWarnLev[15], 0xFU, __gxvWarnings[15]);
+ }
+ }
+}
+
+#define SOMEINDEX(index) (index & 3) + ((index * 8) & ~0x1F)
+
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+
+void __GXVerifyTEX(void) {
+ u32 i;
+ u32 nBmp;
+ u32 nTev;
+ u32 nTex;
+ u32 enabled;
+ u32 texId;
+ u32 direct[8];
+ u32 indirect[8];
+ u32 h2;
+ u32 w2;
+ u32 nlevels;
+
+ nBmp = GET_REG_FIELD(__gxVerif->rasRegs[0], 3, 16);
+ nTex = GET_REG_FIELD(__gxVerif->rasRegs[0], 4, 0);
+ nTev = GET_REG_FIELD(__gxVerif->rasRegs[0], 4, 10) + 1;
+
+ for (i = 0; i < 8; i++) {
+ direct[i] = 0;
+ indirect[i] = 0;
+ }
+
+ for (i = 0; i < nTev + nBmp; i++) {
+ if (i < nTev) {
+ if (__gxVerif->verifyLevel >= 1) {
+ if ((__gxVerif->rasRegs[(i >> 1U) + 0x28] & 0xFF000000) + 0x01000000 == 0U && __gxVerif->verifyLevel >= __gxvWarnLev[16]) {
+ sprintf(__gxvDummyStr, __gxvWarnings[16], i);
+ __gxVerif->cb(__gxvWarnLev[16], 16, __gxvDummyStr);
+ }
+
+ if (i & 1) {
+ enabled = GET_REG_FIELD(__gxVerif->rasRegs[(i >> 1U) + 0x28], 1, 18);
+ if (enabled && (GET_REG_FIELD(__gxVerif->rasRegs[(i >> 1U) + 0x28], 3, 15) >= nTex) && __gxVerif->verifyLevel >= __gxvWarnLev[17]) {
+ sprintf(__gxvDummyStr, __gxvWarnings[17], i);
+ __gxVerif->cb(__gxvWarnLev[17], 17, __gxvDummyStr);
+ }
+ texId = GET_REG_FIELD(__gxVerif->rasRegs[(i >> 1U) + 0x28], 3, 12);
+ } else {
+ enabled = GET_REG_FIELD(__gxVerif->rasRegs[(i >> 1U) + 0x28], 1, 6);
+ if (enabled && (GET_REG_FIELD(__gxVerif->rasRegs[(i >> 1U) + 0x28], 3, 3) >= nTex) && __gxVerif->verifyLevel >= __gxvWarnLev[17]) {
+ sprintf(__gxvDummyStr, __gxvWarnings[17], i);
+ __gxVerif->cb(__gxvWarnLev[17], 17, __gxvDummyStr);
+ }
+ texId = GET_REG_FIELD(__gxVerif->rasRegs[(i >> 1U) + 0x28], 3, 0);
+ }
+
+ if (enabled) {
+ direct[texId] = 1;
+ }
+ }
+ } else {
+ enabled = 1;
+ if ((i - nTev) == 0) {
+ texId = GET_REG_FIELD(__gxVerif->rasRegs[0x27], 3, 0);
+ } else if ((i - nTev) == 1U) {
+ texId = GET_REG_FIELD(__gxVerif->rasRegs[0x27], 3, 6);
+ } else if ((i - nTev) == 2U) {
+ texId = GET_REG_FIELD(__gxVerif->rasRegs[0x27], 3, 12);
+ } else {
+ texId = GET_REG_FIELD(__gxVerif->rasRegs[0x27], 3, 18);
+ }
+
+ if (!indirect[texId] && direct[texId] && __gxVerif->verifyLevel >= __gxvWarnLev[18]) {
+ sprintf(__gxvDummyStr, __gxvWarnings[18], texId);
+ __gxVerif->cb(__gxvWarnLev[18], 18, __gxvDummyStr);
+ }
+ indirect[texId] = 1;
+ }
+
+ if (enabled) {
+ if (__gxVerif->verifyLevel >= GX_WARN_SEVERE) {
+ if (((u32)(__gxVerif->rasRegs[0x80 + SOMEINDEX(texId)] & 0xFF000000) + 0x01000000 == 0
+ || (u32)(__gxVerif->rasRegs[0x84 + SOMEINDEX(texId)] & 0xFF000000) + 0x01000000 == 0
+ || (u32)(__gxVerif->rasRegs[0x88 + SOMEINDEX(texId)] & 0xFF000000) + 0x01000000 == 0
+ || (u32)(__gxVerif->rasRegs[0x8C + SOMEINDEX(texId)] & 0xFF000000) + 0x01000000 == 0
+ || (u32)(__gxVerif->rasRegs[0x90 + SOMEINDEX(texId)] & 0xFF000000) + 0x01000000 == 0) && __gxVerif->verifyLevel >= __gxvWarnLev[19]) {
+ sprintf(__gxvDummyStr, __gxvWarnings[19], texId);
+ __gxVerif->cb(__gxvWarnLev[19], 19, __gxvDummyStr);
+ }
+
+ if (GET_REG_FIELD(__gxVerif->rasRegs[0x8C + SOMEINDEX(texId)], 1, 21) == 0
+ && (u32)(__gxVerif->rasRegs[0x94 + SOMEINDEX(texId)] & 0xFF000000) + 0x01000000 == 0 && __gxVerif->verifyLevel >= __gxvWarnLev[20]) {
+ sprintf(__gxvDummyStr, __gxvWarnings[20], texId);
+ __gxVerif->cb(__gxvWarnLev[20], 20, __gxvDummyStr);
+ }
+
+ if (((u32)GET_REG_FIELD(__gxVerif->rasRegs[0x88 + SOMEINDEX(texId)], 4, 20) == 8
+ || (u32)GET_REG_FIELD(__gxVerif->rasRegs[0x88 + SOMEINDEX(texId)], 4, 20) == 9
+ || (u32)GET_REG_FIELD(__gxVerif->rasRegs[0x88 + SOMEINDEX(texId)], 4, 20) == 10)
+ && (__gxVerif->rasRegs[0x98 + SOMEINDEX(texId)] & 0xFF000000) + 0x01000000 == 0U && __gxVerif->verifyLevel >= __gxvWarnLev[21]) {
+ sprintf(__gxvDummyStr, __gxvWarnings[21], texId);
+ __gxVerif->cb(__gxvWarnLev[21], 21, __gxvDummyStr);
+ }
+
+ if (GET_REG_FIELD(__gxVerif->rasRegs[0x88 + SOMEINDEX(texId)], 10, 0) + 1 == 0) {
+ w2 = 1;
+ } else {
+ w2 = 1;
+ while (!(w2 & (GET_REG_FIELD(__gxVerif->rasRegs[0x88 + SOMEINDEX(texId)], 10, 0) + 1))) {
+ w2 *= 2;
+ }
+ w2 = (GET_REG_FIELD(__gxVerif->rasRegs[0x88 + SOMEINDEX(texId)], 10, 0) + 1) == w2;
+ }
+
+ if (GET_REG_FIELD(__gxVerif->rasRegs[0x88 + SOMEINDEX(texId)], 10, 10) + 1 == 0) {
+ h2 = 1;
+ } else {
+ h2 = 1;
+ while (!(h2 & (GET_REG_FIELD(__gxVerif->rasRegs[0x88 + SOMEINDEX(texId)], 10, 10) + 1))) {
+ h2 *= 2;
+ }
+ h2 = (GET_REG_FIELD(__gxVerif->rasRegs[0x88 + SOMEINDEX(texId)], 10, 10) + 1) == h2;
+ }
+
+ if (GET_REG_FIELD(__gxVerif->rasRegs[0x80 + SOMEINDEX(texId)] & 0xFFFF, 2, 5) && !w2 && __gxVerif->verifyLevel >= __gxvWarnLev[22]) {
+ sprintf(__gxvDummyStr, __gxvWarnings[22], "Width", texId);
+ __gxVerif->cb(__gxvWarnLev[22], 22, __gxvDummyStr);
+ }
+
+ if (GET_REG_FIELD(__gxVerif->rasRegs[0x80 + SOMEINDEX(texId)] & 0xFFFF, 2, 5) && !h2 && __gxVerif->verifyLevel >= __gxvWarnLev[22]) {
+ sprintf(__gxvDummyStr, __gxvWarnings[22], "Height", texId);
+ __gxVerif->cb(__gxvWarnLev[22], 22, __gxvDummyStr);
+ }
+
+ if (GET_REG_FIELD(__gxVerif->rasRegs[0x80 + SOMEINDEX(texId)], 2, 0) && !w2 && __gxVerif->verifyLevel >= __gxvWarnLev[23]) {
+ sprintf(__gxvDummyStr, __gxvWarnings[23], "S", texId);
+ __gxVerif->cb(__gxvWarnLev[23], 23, __gxvDummyStr);
+ }
+
+ if (GET_REG_FIELD(__gxVerif->rasRegs[0x80 + SOMEINDEX(texId)], 2, 2) && !h2 && __gxVerif->verifyLevel >= __gxvWarnLev[23]) {
+ sprintf(__gxvDummyStr, __gxvWarnings[23], "T", texId);
+ __gxVerif->cb(__gxvWarnLev[23], 23, __gxvDummyStr);
+ }
+
+ if (GET_REG_FIELD(__gxVerif->rasRegs[0x80 + SOMEINDEX(texId)] & 0xFFFF, 2, 5) != 0
+ && ((u32)GET_REG_FIELD(__gxVerif->rasRegs[0x88 + SOMEINDEX(texId)], 4, 20) == 8
+ || (u32)GET_REG_FIELD(__gxVerif->rasRegs[0x88 + SOMEINDEX(texId)], 4, 20) == 9
+ || (u32)GET_REG_FIELD(__gxVerif->rasRegs[0x88 + SOMEINDEX(texId)], 4, 20) == 10)
+ && (u32)GET_REG_FIELD(__gxVerif->rasRegs[0x80 + SOMEINDEX(texId)], 3, 5) != 1
+ && (u32)GET_REG_FIELD(__gxVerif->rasRegs[0x80 + SOMEINDEX(texId)], 3, 5) != 5 && __gxVerif->verifyLevel >= __gxvWarnLev[24]) {
+ sprintf(__gxvDummyStr, __gxvWarnings[24], texId);
+ __gxVerif->cb(__gxvWarnLev[24], 24, __gxvDummyStr);
+ }
+
+ if ((u32)GET_REG_FIELD(__gxVerif->rasRegs[0x84 + SOMEINDEX(texId)], 8, 0) > (u32)GET_REG_FIELD(__gxVerif->rasRegs[0x84 + SOMEINDEX(texId)], 8, 8) && __gxVerif->verifyLevel >= __gxvWarnLev[25]) {
+ sprintf(__gxvDummyStr, __gxvWarnings[25], texId);
+ __gxVerif->cb(__gxvWarnLev[25], 25, __gxvDummyStr);
+ }
+
+ for (
+ nlevels = 0;
+ (
+ MAX((u32)GET_REG_FIELD(__gxVerif->rasRegs[0x88 + SOMEINDEX(texId)], 10, 0) + 1,
+ (u32)GET_REG_FIELD(__gxVerif->rasRegs[0x88 + SOMEINDEX(texId)], 10, 10) + 1) >> nlevels
+ ) != 0;
+ nlevels++) {
+ }
+
+ if (GET_REG_FIELD(__gxVerif->rasRegs[0x84 + SOMEINDEX(texId)], 8, 8) > (nlevels - 1) * 16 && __gxVerif->verifyLevel >= __gxvWarnLev[26]) {
+ sprintf(__gxvDummyStr, __gxvWarnings[26], texId);
+ __gxVerif->cb(__gxvWarnLev[26], 26, __gxvDummyStr);
+ }
+
+ if (GET_REG_FIELD(__gxVerif->rasRegs[0x80 + SOMEINDEX(texId)], 1, 21) && GET_REG_FIELD(__gxVerif->rasRegs[0x80 + SOMEINDEX(texId)], 1, 8) && __gxVerif->verifyLevel >= __gxvWarnLev[27]) {
+ sprintf(__gxvDummyStr, __gxvWarnings[27], texId);
+ __gxVerif->cb(__gxvWarnLev[27], 27, __gxvDummyStr);
+ }
+
+ if (GET_REG_FIELD(__gxVerif->rasRegs[0x80 + SOMEINDEX(texId)], 2, 19)
+ && (GET_REG_FIELD(__gxVerif->rasRegs[0x80 + SOMEINDEX(texId)] & 0xFFFF, 2, 5) == 0
+ || (u32)GET_REG_FIELD(__gxVerif->rasRegs[0x80 + SOMEINDEX(texId)], 3, 5) != 6
+ || (u32)GET_REG_FIELD(__gxVerif->rasRegs[0x80 + SOMEINDEX(texId)], 1, 4) != 1
+ || (u32)GET_REG_FIELD(__gxVerif->rasRegs[0x88 + SOMEINDEX(texId)], 4, 20) == 8
+ || (u32)GET_REG_FIELD(__gxVerif->rasRegs[0x88 + SOMEINDEX(texId)], 4, 20) == 9
+ || (u32)GET_REG_FIELD(__gxVerif->rasRegs[0x88 + SOMEINDEX(texId)], 4, 20) == 10
+ || (u32)GET_REG_FIELD(__gxVerif->rasRegs[0x80 + SOMEINDEX(texId)], 1, 8) != 0
+ || (u32)GET_REG_FIELD(__gxVerif->rasRegs[0x80 + SOMEINDEX(texId)], 1, 21)) && __gxVerif->verifyLevel >= __gxvWarnLev[28]) {
+ sprintf(__gxvDummyStr, __gxvWarnings[28], texId);
+ __gxVerif->cb(__gxvWarnLev[28], 28, __gxvDummyStr);
+ }
+ }
+
+ if (GET_REG_FIELD(__gxVerif->rasRegs[0x80 + SOMEINDEX(texId)], 1, 18) != 0) {
+ if (((u32)GET_REG_FIELD(__gxVerif->rasRegs[0x80 + SOMEINDEX(texId)], 3, 5) != 4 || (u32)GET_REG_FIELD(__gxVerif->rasRegs[0x80 + SOMEINDEX(texId)], 1, 4) != 1) && __gxVerif->verifyLevel >= __gxvWarnLev[29]) {
+ sprintf(__gxvDummyStr, __gxvWarnings[29], texId);
+ __gxVerif->cb(__gxvWarnLev[29], 29, __gxvDummyStr);
+ }
+
+ if ((!GET_REG_FIELD(__gxVerif->rasRegs[0x80 + SOMEINDEX(texId)], 1, 17) || (u32)GET_REG_FIELD(__gxVerif->rasRegs[0x88 + SOMEINDEX(texId)], 4, 20) != 1 || (u32)GET_REG_FIELD(__gxVerif->rasRegs[0x80 + SOMEINDEX(texId)], 2, 19) != 0) && __gxVerif->verifyLevel >= __gxvWarnLev[30]) {
+ sprintf(__gxvDummyStr, __gxvWarnings[30], texId);
+ __gxVerif->cb(__gxvWarnLev[30], 30, __gxvDummyStr);
+ }
+ }
+
+ if (GET_REG_FIELD(__gxVerif->rasRegs[0x80 + SOMEINDEX(texId)], 1, 17) != 0) {
+ if (((u32)GET_REG_FIELD(__gxVerif->rasRegs[0x88 + SOMEINDEX(texId)], 4, 20) == 8 || (u32)GET_REG_FIELD(__gxVerif->rasRegs[0x88 + SOMEINDEX(texId)], 4, 20) == 9 || (u32)GET_REG_FIELD(__gxVerif->rasRegs[0x88 + SOMEINDEX(texId)], 4, 20) == 10) && __gxVerif->verifyLevel >= __gxvWarnLev[31]) {
+ sprintf(__gxvDummyStr, __gxvWarnings[31], texId);
+ __gxVerif->cb(__gxvWarnLev[31], 31, __gxvDummyStr);
+ }
+
+ if ((!GET_REG_FIELD(__gxVerif->rasRegs[0x80 + SOMEINDEX(texId)], 1, 18) || 0) && __gxVerif->verifyLevel >= __gxvWarnLev[30]) {
+ sprintf(__gxvDummyStr, __gxvWarnings[30], texId);
+ __gxVerif->cb(__gxvWarnLev[30], 30, __gxvDummyStr);
+ }
+ }
+ }
+ }
+}
+
+void __GXVerifyTEV(void) {
+ u32 i; // r31
+ u32 nTev; // r29
+ u32 nCol; // r28
+ u32 enabled; // r30
+ u32 color; // r27
+ u32 Clh[4]; // r1+0x38
+ u32 Alh[4]; // r1+0x28
+ u32 Cwritten[4]; // r1+0x18
+ u32 Awritten[4]; // r1+0x8
+
+ nTev = GET_REG_FIELD(__gxVerif->rasRegs[0], 4, 10) + 1;
+ nCol = GET_REG_FIELD(__gxVerif->rasRegs[0], 3, 4);
+ nCol;
+
+ for (i = 0; i < 4; i++) {
+ Clh[i] = 0;
+ Alh[i] = 0;
+ Cwritten[i] = 0;
+ Awritten[i] = 0;
+ }
+
+ for (i = 0; i < nTev; i++) {
+ if (__gxVerif->verifyLevel >= GX_WARN_SEVERE
+ && (((u32) ((__gxVerif->rasRegs[(i * 2) + 0xC0] & 0xFF000000) + 0x01000000) == 0U) || ((u32) ((__gxVerif->rasRegs[(i * 2) + 0xC1] & 0xFF000000) + 0x01000000) == 0U))) {
+ sprintf(__gxvDummyStr, __gxvWarnings[32], i);
+ __gxVerif->cb(1, 0x20U, __gxvDummyStr);
+ }
+
+ if (i & 1) {
+ color = GET_REG_FIELD(__gxVerif->rasRegs[(i >> 1U) + 0x28], 3, 19);
+ } else {
+ color = GET_REG_FIELD(__gxVerif->rasRegs[(i >> 1U) + 0x28], 3, 7);
+ }
+
+ if (__gxVerif->verifyLevel >= GX_WARN_MEDIUM && ((color == 0 && nCol < 1) || (color == 1 && nCol < 2))) {
+ sprintf(__gxvDummyStr, __gxvWarnings[33], i);
+ __gxVerif->cb(1, 0x21U, __gxvDummyStr);
+ }
+
+ if (i & 1) {
+ enabled = GET_REG_FIELD(__gxVerif->rasRegs[(i >> 1U) + 0x28], 1, 18);
+ } else {
+ enabled = GET_REG_FIELD(__gxVerif->rasRegs[(i >> 1U) + 0x28], 1, 6);
+ }
+
+ if (__gxVerif->verifyLevel >= GX_WARN_SEVERE) {
+ if (!enabled && ((u32)GET_REG_FIELD(__gxVerif->rasRegs[(i * 2) + 0xC0], 4, 12) == 8 || (u32)GET_REG_FIELD(__gxVerif->rasRegs[(i * 2) + 0xC0], 4, 12) == 9)) {
+ sprintf(__gxvDummyStr, __gxvWarnings[0x22], "A", i);
+ __gxVerif->cb(1, 0x22U, __gxvDummyStr);
+ }
+
+ if (!enabled && ((u32)GET_REG_FIELD(__gxVerif->rasRegs[(i * 2) + 0xC0], 4, 8) == 8 || (u32)GET_REG_FIELD(__gxVerif->rasRegs[(i * 2) + 0xC0], 4, 8) == 9)) {
+ sprintf(__gxvDummyStr, __gxvWarnings[0x22], "B", i);
+ __gxVerif->cb(1, 0x22U, __gxvDummyStr);
+ }
+
+ if (!enabled && ((u32)GET_REG_FIELD(__gxVerif->rasRegs[(i * 2) + 0xC0], 4, 4) == 8 || (u32)GET_REG_FIELD(__gxVerif->rasRegs[(i * 2) + 0xC0], 4, 4) == 9)) {
+ sprintf(__gxvDummyStr, __gxvWarnings[0x22], "C", i);
+ __gxVerif->cb(1, 0x22U, __gxvDummyStr);
+ }
+
+ if (!enabled && ((u32)GET_REG_FIELD(__gxVerif->rasRegs[(i * 2) + 0xC0], 4, 0) == 8 || (u32)GET_REG_FIELD(__gxVerif->rasRegs[(i * 2) + 0xC0], 4, 0) == 9)) {
+ sprintf(__gxvDummyStr, __gxvWarnings[0x22], "D", i);
+ __gxVerif->cb(1, 0x22U, __gxvDummyStr);
+ }
+
+ if (!enabled && (u32)GET_REG_FIELD(__gxVerif->rasRegs[(i * 2) + 0xC1], 3, 13) == 4) {
+ sprintf(__gxvDummyStr, __gxvWarnings[0x23], "A", i);
+ __gxVerif->cb(1, 0x23U, __gxvDummyStr);
+ }
+
+ if (!enabled && (u32)GET_REG_FIELD(__gxVerif->rasRegs[(i * 2) + 0xC1], 3, 10) == 4) {
+ sprintf(__gxvDummyStr, __gxvWarnings[0x23], "B", i);
+ __gxVerif->cb(1, 0x23U, __gxvDummyStr);
+ }
+
+ if (!enabled && (u32)GET_REG_FIELD(__gxVerif->rasRegs[(i * 2) + 0xC1], 3, 7) == 4) {
+ sprintf(__gxvDummyStr, __gxvWarnings[0x23], "C", i);
+ __gxVerif->cb(1, 0x23U, __gxvDummyStr);
+ }
+
+ if (!enabled && (u32)GET_REG_FIELD(__gxVerif->rasRegs[(i * 2) + 0xC1], 3, 4) == 4) {
+ sprintf(__gxvDummyStr, __gxvWarnings[0x23], "D", i);
+ __gxVerif->cb(1, 0x23U, __gxvDummyStr);
+ }
+
+ if ((u32)GET_REG_FIELD(__gxVerif->rasRegs[(i * 2) + 0xC0], 4, 12) <= 7 && ((__gxVerif->rasRegs[GET_REG_FIELD(__gxVerif->rasRegs[(i * 2) + 0xC0], 4, 12) + 0xE1] & 0xFF000000) + 0x01000000) == 0U) {
+ if (GET_REG_FIELD(__gxVerif->rasRegs[(i * 2) + 0xC0], 1, 12) ? !Awritten[GET_REG_FIELD(__gxVerif->rasRegs[(i * 2) + 0xC0], 3, 13)] : !Cwritten[GET_REG_FIELD(__gxVerif->rasRegs[(i * 2) + 0xC0], 3, 13)]) {
+ sprintf(__gxvDummyStr, __gxvWarnings[0x24], "A", i, GET_REG_FIELD(__gxVerif->rasRegs[(i * 2) + 0xC0], 1, 0xCU) ? "alpha" : "color", (__gxVerif->rasRegs[(i * 2) + 0xC0] >> 0xDU) & 7);
+ __gxVerif->cb(1, 0x24U, __gxvDummyStr);
+ }
+ }
+
+ if ((u32)GET_REG_FIELD(__gxVerif->rasRegs[(i * 2) + 0xC0], 4, 8) <= 7 && ((__gxVerif->rasRegs[GET_REG_FIELD(__gxVerif->rasRegs[(i * 2) + 0xC0], 4, 8) + 0xE1] & 0xFF000000) + 0x01000000) == 0U) {
+ if (GET_REG_FIELD(__gxVerif->rasRegs[(i * 2) + 0xC0], 1, 8) ? !Awritten[GET_REG_FIELD(__gxVerif->rasRegs[(i * 2) + 0xC0], 3, 9)] : !Cwritten[GET_REG_FIELD(__gxVerif->rasRegs[(i * 2) + 0xC0], 3, 9)]) {
+ sprintf(__gxvDummyStr, __gxvWarnings[0x24], "B", i, GET_REG_FIELD(__gxVerif->rasRegs[(i * 2) + 0xC0], 1, 8U) ? "alpha" : "color", (__gxVerif->rasRegs[(i * 2) + 0xC0] >> 9U) & 7);
+ __gxVerif->cb(1, 0x24U, __gxvDummyStr);
+ }
+ }
+
+ if ((u32)GET_REG_FIELD(__gxVerif->rasRegs[(i * 2) + 0xC0], 4, 4) <= 7 && ((__gxVerif->rasRegs[GET_REG_FIELD(__gxVerif->rasRegs[(i * 2) + 0xC0], 4, 4) + 0xE1] & 0xFF000000) + 0x01000000) == 0U) {
+ if (GET_REG_FIELD(__gxVerif->rasRegs[(i * 2) + 0xC0], 1, 4) ? !Awritten[GET_REG_FIELD(__gxVerif->rasRegs[(i * 2) + 0xC0], 3, 5)] : !Cwritten[GET_REG_FIELD(__gxVerif->rasRegs[(i * 2) + 0xC0], 3, 5)]) {
+ sprintf(__gxvDummyStr, __gxvWarnings[0x24], "C", i, GET_REG_FIELD(__gxVerif->rasRegs[(i * 2) + 0xC0], 1, 4U) ? "alpha" : "color", (__gxVerif->rasRegs[(i * 2) + 0xC0] >> 5U) & 7);
+ __gxVerif->cb(1, 0x24U, __gxvDummyStr);
+ }
+ }
+
+ if ((u32)GET_REG_FIELD(__gxVerif->rasRegs[(i * 2) + 0xC0], 4, 0) <= 7 && ((__gxVerif->rasRegs[GET_REG_FIELD(__gxVerif->rasRegs[(i * 2) + 0xC0], 4, 0) + 0xE1] & 0xFF000000) + 0x01000000) == 0U) {
+ if (GET_REG_FIELD(__gxVerif->rasRegs[(i * 2) + 0xC0], 1, 0) ? !Awritten[GET_REG_FIELD(__gxVerif->rasRegs[(i * 2) + 0xC0], 3, 1)] : !Cwritten[GET_REG_FIELD(__gxVerif->rasRegs[(i * 2) + 0xC0], 3, 1)]) {
+ sprintf(__gxvDummyStr, __gxvWarnings[0x24], "D", i, (__gxVerif->rasRegs[(i * 2) + 0xC0] & 1) ? "alpha" : "color", (__gxVerif->rasRegs[(i * 2) + 0xC0] >> 1U) & 7);
+ __gxVerif->cb(1, 0x24U, __gxvDummyStr);
+ }
+ }
+
+ if ((u32)GET_REG_FIELD(__gxVerif->rasRegs[(i * 2) + 0xC1], 3, 13) <= 3 && ((__gxVerif->rasRegs[GET_REG_FIELD(__gxVerif->rasRegs[(i * 2) + 0xC1], 4, 14) + 0xE0] & 0xFF000000) + 0x01000000) == 0U && Awritten[GET_REG_FIELD(__gxVerif->rasRegs[(i * 2) + 0xC1], 3, 13)] == 0U) {
+ sprintf(__gxvDummyStr, __gxvWarnings[0x25], "A", i, GET_REG_FIELD(__gxVerif->rasRegs[(i * 2) + 0xC1], 3, 13));
+ __gxVerif->cb(1, 0x25U, __gxvDummyStr);
+ }
+
+ if ((u32)GET_REG_FIELD(__gxVerif->rasRegs[(i * 2) + 0xC1], 3, 10) <= 3 && ((__gxVerif->rasRegs[GET_REG_FIELD(__gxVerif->rasRegs[(i * 2) + 0xC1], 3, 11) + 0xE0] & 0xFF000000) + 0x01000000) == 0U && Awritten[GET_REG_FIELD(__gxVerif->rasRegs[(i * 2) + 0xC1], 3, 10)] == 0U) {
+ sprintf(__gxvDummyStr, __gxvWarnings[0x25], "B", i, GET_REG_FIELD(__gxVerif->rasRegs[(i * 2) + 0xC1], 3, 10));
+ __gxVerif->cb(1, 0x25U, __gxvDummyStr);
+ }
+
+ if ((u32)GET_REG_FIELD(__gxVerif->rasRegs[(i * 2) + 0xC1], 3, 7) <= 3 && ((__gxVerif->rasRegs[GET_REG_FIELD(__gxVerif->rasRegs[(i * 2) + 0xC1], 3, 8) + 0xE0] & 0xFF000000) + 0x01000000) == 0U && Awritten[GET_REG_FIELD(__gxVerif->rasRegs[(i * 2) + 0xC1], 3, 7)] == 0U) {
+ sprintf(__gxvDummyStr, __gxvWarnings[0x25], "C", i, GET_REG_FIELD(__gxVerif->rasRegs[(i * 2) + 0xC1], 3, 7));
+ __gxVerif->cb(1, 0x25U, __gxvDummyStr);
+ }
+
+ if ((u32)GET_REG_FIELD(__gxVerif->rasRegs[(i * 2) + 0xC1], 3, 4) <= 3 && ((__gxVerif->rasRegs[GET_REG_FIELD(__gxVerif->rasRegs[(i * 2) + 0xC1], 3, 3) + 0xE0] & 0xFF000000) + 0x01000000) == 0U && Awritten[GET_REG_FIELD(__gxVerif->rasRegs[(i * 2) + 0xC1], 3, 4)] == 0U) {
+ sprintf(__gxvDummyStr, __gxvWarnings[0x25], "D", i, GET_REG_FIELD(__gxVerif->rasRegs[(i * 2) + 0xC1], 3, 4));
+ __gxVerif->cb(1, 0x25U, __gxvDummyStr);
+ }
+ }
+
+ if (__gxVerif->verifyLevel >= GX_WARN_ALL) {
+ if ((u32)GET_REG_FIELD(__gxVerif->rasRegs[(i * 2) + 0xC0], 4, 12) <= 7) {
+ if (GET_REG_FIELD(__gxVerif->rasRegs[(i * 2) + 0xC0], 1, 12) ? Alh[GET_REG_FIELD(__gxVerif->rasRegs[(i * 2) + 0xC0], 3, 13)] : Clh[GET_REG_FIELD(__gxVerif->rasRegs[(i * 2) + 0xC0], 3, 13)]) {
+ sprintf(__gxvDummyStr, __gxvWarnings[0x26], "A", i, GET_REG_FIELD(__gxVerif->rasRegs[(i * 2) + 0xC0], 1, 12) ? "alpha" : "color", GET_REG_FIELD(__gxVerif->rasRegs[(i * 2) + 0xC0], 3, 13));
+ __gxVerif->cb(3, 0x26U, __gxvDummyStr);
+ }
+ }
+
+ if ((u32)GET_REG_FIELD(__gxVerif->rasRegs[(i * 2) + 0xC0], 4, 8) <= 7) {
+ if (GET_REG_FIELD(__gxVerif->rasRegs[(i * 2) + 0xC0], 1, 8) ? Alh[GET_REG_FIELD(__gxVerif->rasRegs[(i * 2) + 0xC0], 3, 9)] : Clh[GET_REG_FIELD(__gxVerif->rasRegs[(i * 2) + 0xC0], 3, 9)]) {
+ sprintf(__gxvDummyStr, __gxvWarnings[0x26], "B", i, GET_REG_FIELD(__gxVerif->rasRegs[(i * 2) + 0xC0], 1, 8) ? "alpha" : "color", GET_REG_FIELD(__gxVerif->rasRegs[(i * 2) + 0xC0], 3, 9));
+ __gxVerif->cb(3, 0x26U, __gxvDummyStr);
+ }
+ }
+
+ if ((u32)GET_REG_FIELD(__gxVerif->rasRegs[(i * 2) + 0xC0], 4, 4) <= 7) {
+ if (GET_REG_FIELD(__gxVerif->rasRegs[(i * 2) + 0xC0], 1, 4) ? Alh[GET_REG_FIELD(__gxVerif->rasRegs[(i * 2) + 0xC0], 3, 5)] : Clh[GET_REG_FIELD(__gxVerif->rasRegs[(i * 2) + 0xC0], 3, 5)]) {
+ sprintf(__gxvDummyStr, __gxvWarnings[0x26], "C", i, GET_REG_FIELD(__gxVerif->rasRegs[(i * 2) + 0xC0], 1, 4) ? "alpha" : "color", GET_REG_FIELD(__gxVerif->rasRegs[(i * 2) + 0xC0], 3, 5));
+ __gxVerif->cb(3, 0x26U, __gxvDummyStr);
+ }
+ }
+
+ if ((u32)GET_REG_FIELD(__gxVerif->rasRegs[(i * 2) + 0xC1], 3, 13) <= 3 && (u32)Alh[GET_REG_FIELD(__gxVerif->rasRegs[(i * 2) + 0xC1], 3, 13)] != 0) {
+ sprintf(__gxvDummyStr, __gxvWarnings[0x27], "A", i, GET_REG_FIELD(__gxVerif->rasRegs[(i * 2) + 0xC1], 3, 0xDU));
+ __gxVerif->cb(3, 0x27U, __gxvDummyStr);
+ }
+
+ if ((u32)GET_REG_FIELD(__gxVerif->rasRegs[(i * 2) + 0xC1], 3, 10) <= 3 && (u32)Alh[GET_REG_FIELD(__gxVerif->rasRegs[(i * 2) + 0xC1], 3, 10)] != 0) {
+ sprintf(__gxvDummyStr, __gxvWarnings[0x27], "B", i, ((__gxVerif->rasRegs[(i * 2) + 0xC1] >> 0xAU) & 7));
+ __gxVerif->cb(3, 0x27U, __gxvDummyStr);
+ }
+
+ if ((u32)GET_REG_FIELD(__gxVerif->rasRegs[(i * 2) + 0xC1], 3, 7) <= 3 && (u32)Alh[GET_REG_FIELD(__gxVerif->rasRegs[(i * 2) + 0xC1], 3, 7)] != 0) {
+ sprintf(__gxvDummyStr, __gxvWarnings[0x27], "C", i, GET_REG_FIELD(__gxVerif->rasRegs[(i * 2) + 0xC1], 3, 7U));
+ __gxVerif->cb(3, 0x27U, __gxvDummyStr);
+ }
+ }
+ Cwritten[GET_REG_FIELD(__gxVerif->rasRegs[(i * 2) + 0xC0], 2, 22)] = 1;
+ Awritten[GET_REG_FIELD(__gxVerif->rasRegs[(i * 2) + 0xC1], 2, 22)] = 1;
+ Clh[GET_REG_FIELD(__gxVerif->rasRegs[(i * 2) + 0xC0], 2, 22)] = (!GET_REG_FIELD(__gxVerif->rasRegs[(i * 2) + 0xC1], 2, 0) && !GET_REG_FIELD(__gxVerif->rasRegs[(i * 2) + 0xC0], 1, 19));
+ Alh[GET_REG_FIELD(__gxVerif->rasRegs[(i * 2) + 0xC1], 2, 22)] = (!GET_REG_FIELD(__gxVerif->rasRegs[(i * 2) + 0xC1], 2, 0) && !GET_REG_FIELD(__gxVerif->rasRegs[(i * 2) + 0xC1], 1, 19));
+ }
+
+ for (i = 0; i < 4; i++) {
+ if (Cwritten[i] != 0U) {
+ __gxVerif->rasRegs[(i * 2) + 0xE1] = (__gxVerif->rasRegs[(i * 2) + 0xE1] & 0xFFFFFF) | 0xFF000000;
+ }
+ if (Awritten[i] != 0U) {
+ __gxVerif->rasRegs[(i * 2) + 0xE0] = (__gxVerif->rasRegs[(i * 2) + 0xE0] & 0xFFFFFF) | 0xFF000000;
+ }
+ }
+
+ if (GET_REG_FIELD(__gxVerif->rasRegs[0xF5], 2, 2) && __gxVerif->verifyLevel >= 1) {
+ if ((u32) ((__gxVerif->rasRegs[0xF4] & 0xFF000000) + 0x01000000) == 0U) {
+ __gxVerif->cb(1, 0x28U, __gxvWarnings[0x28]);
+ }
+ if (!enabled) {
+ __gxVerif->cb(1, 0x29U, __gxvWarnings[0x29]);
+ }
+ }
+
+ if (__gxVerif->verifyLevel >= GX_WARN_MEDIUM) {
+ if (GET_REG_FIELD(__gxVerif->rasRegs[((nTev - 1) * 2) + 0xC0], 2, 22)) {
+ __gxVerif->cb(2, 0x2AU, __gxvWarnings[0x2A]);
+ }
+ if (GET_REG_FIELD(__gxVerif->rasRegs[((nTev - 1) * 2) + 0xC1], 2, 22)) {
+ __gxVerif->cb(2, 0x2BU, __gxvWarnings[0x2B]);
+ }
+ }
+
+ if (__gxVerif->verifyLevel >= GX_WARN_ALL) {
+ if (!GET_REG_FIELD(__gxVerif->rasRegs[((nTev - 1) * 2) + 0xC1], 2, 0) && !GET_REG_FIELD(__gxVerif->rasRegs[((nTev - 1) * 2) + 0xC0], 1, 19)) {
+ __gxVerif->cb(3, 0x2CU, __gxvWarnings[0x2C]);
+ }
+ if (!GET_REG_FIELD(__gxVerif->rasRegs[((nTev - 1) * 2) + 0xC1], 2, 0) && !GET_REG_FIELD(__gxVerif->rasRegs[((nTev - 1) * 2) + 0xC1], 1, 19)) {
+ __gxVerif->cb(3, 0x2DU, __gxvWarnings[0x2D]);
+ }
+ }
+
+ if (__gxVerif->verifyLevel >= GX_WARN_MEDIUM && GET_REG_FIELD(__gxVerif->rasRegs[0x43], 1, 6) && (GET_REG_FIELD(__gxVerif->rasRegs[0xF3], 2, 22) || ((u32) GET_REG_FIELD(__gxVerif->rasRegs[0xF3], 3, 16) != 7) || ((u32) GET_REG_FIELD(__gxVerif->rasRegs[0xF3], 3, 19) != 7))) {
+ __gxVerif->cb(2, 0x2EU, __gxvWarnings[0x2E]);
+ }
+}
+
+void __GXVerifyPE(void) {
+ u32 i;
+
+ if (__gxVerif->verifyLevel >= GX_WARN_SEVERE && GET_REG_FIELD(__gxVerif->rasRegs[0x41], 1, 0) && GET_REG_FIELD(__gxVerif->rasRegs[0x41], 1, 1) && __gxVerif->verifyLevel >= __gxvWarnLev[0x2F]) {
+ __gxVerif->cb(__gxvWarnLev[0x2F], 0x2FU, __gxvWarnings[0x2F]);
+ }
+
+ if (__gxVerif->verifyLevel >= GX_WARN_MEDIUM) {
+ if (GET_REG_FIELD(__gxVerif->rasRegs[0], 1, 9) && (u32)GET_REG_FIELD(__gxVerif->rasRegs[0x43], 3, 0) != 2 && __gxVerif->verifyLevel >= __gxvWarnLev[0x31]) {
+ __gxVerif->cb(__gxvWarnLev[0x31], 0x31U, __gxvWarnings[0x31]);
+ }
+ if (!GET_REG_FIELD(__gxVerif->rasRegs[0], 1, 9) && (u32)GET_REG_FIELD(__gxVerif->rasRegs[0x43], 3, 0) == 2 && __gxVerif->verifyLevel >= __gxvWarnLev[0x32]) {
+ __gxVerif->cb(__gxvWarnLev[0x32], 0x32U, __gxvWarnings[0x32]);
+ }
+ }
+
+ if (__gxVerif->verifyLevel >= GX_WARN_ALL) {
+ for (i = 0; i < 4; i++) {
+ if (((u32)GET_REG_FIELD(__gxVerif->rasRegs[i + 1], 4, 4) > GET_REG_FIELD(__gxVerif->rasRegs[i + 1], 4, 12) || (u32)GET_REG_FIELD(__gxVerif->rasRegs[i + 1], 4, 12) > (u32)GET_REG_FIELD(__gxVerif->rasRegs[i + 1], 4, 20))
+ && (u32)GET_REG_FIELD(__gxVerif->rasRegs[0x43], 3, 0) == 2 && __gxVerif->verifyLevel >= __gxvWarnLev[0x33]) {
+ sprintf(__gxvDummyStr, __gxvWarnings[0x33], i);
+ __gxVerif->cb(__gxvWarnLev[0x33], 0x33U, __gxvDummyStr);
+ }
+ }
+ }
+}
+
+#endif
diff --git a/src/revolution/gx/GXVerifXF.c b/src/revolution/gx/GXVerifXF.c
new file mode 100644
index 0000000000..8784888afd
--- /dev/null
+++ b/src/revolution/gx/GXVerifXF.c
@@ -0,0 +1,946 @@
+#if DEBUG
+
+#include <stdio.h>
+
+#include <revolution/gx.h>
+
+#include "__gx.h"
+
+static u32 DumpCount;
+static s8 XFBuf[128];
+static u32 numRegularTextures;
+static u32 numBumpmapTextures;
+static u32 numColor0Textures;
+static u32 numColor1Textures;
+static u32 numColorTextures;
+static s32 XFChannel = -1;
+
+static GXAttr TextureEnums[8] = {
+ GX_VA_TEX0,
+ GX_VA_TEX1,
+ GX_VA_TEX2,
+ GX_VA_TEX3,
+ GX_VA_TEX4,
+ GX_VA_TEX5,
+ GX_VA_TEX6,
+ GX_VA_TEX7,
+};
+
+static GXAttr MtxIdxEnums[9] = {
+ GX_VA_PNMTXIDX,
+ GX_VA_TEX0MTXIDX,
+ GX_VA_TEX1MTXIDX,
+ GX_VA_TEX2MTXIDX,
+ GX_VA_TEX3MTXIDX,
+ GX_VA_TEX4MTXIDX,
+ GX_VA_TEX5MTXIDX,
+ GX_VA_TEX6MTXIDX,
+ GX_VA_TEX7MTXIDX,
+};
+
+static u8 lightRegisterNames[13][256] = {
+ "Light Color RGBA",
+ "Cosine Attenuation A0",
+ "Cosine Attenuation A1",
+ "Cosine Attenuation A2",
+ "Distance Attenuation K0",
+ "Distance Attenuation K1",
+ "Distance Attenuation K2",
+ "X Light Position / Infinite Light X Direction",
+ "Y Light Position / Infinite Light Y Direction",
+ "Z Light Position / Infinite Light Z Direction",
+ "X Light Direction / Half Angle X Component",
+ "Y Light Direction / Half Angle Y Component",
+ "Z Light Direction / Half Angle Z Component",
+};
+
+#define LOWORD(var) (((u16 *)&(var))[0])
+#define HIWORD(var) (((u16 *)&(var))[1])
+
+#define BYTE0(var) (((u8 *)&(var))[0])
+#define BYTE1(var) (((u8 *)&(var))[1])
+#define BYTE2(var) (((u8 *)&(var))[2])
+#define BYTE3(var) (((u8 *)&(var))[3])
+
+static void CountTextureTypes(void) {
+ u32 i;
+ u32 texgen_type;
+
+ numRegularTextures = 0;
+ numBumpmapTextures = 0;
+ numColor0Textures = 0;
+ numColor1Textures = 0;
+
+ for (i = 0; i < __gxVerif->xfRegs[0x3F]; i++) {
+ texgen_type = (__gxVerif->xfRegs[i + 64] >> 4) & 7;
+ if (texgen_type == 0) {
+ numRegularTextures++;
+ } else if (texgen_type == 1) {
+ numBumpmapTextures++;
+ } else if (texgen_type == 2) {
+ numColor0Textures++;
+ } else if (texgen_type == 3) {
+ numColor1Textures++;
+ } else {
+ if (__gxVerif->verifyLevel >= __gxvWarnLev[52]) {
+ __GX_WARNF(GXWARN_INVALID_TG_TYPE, texgen_type, i);
+ }
+ }
+ }
+ numColorTextures = numColor0Textures + numColor1Textures;
+}
+
+static void InitializeXFVerifyData(void) {
+ CountTextureTypes();
+}
+
+static void CheckDirty(u32 index, const char* name) {
+ if (!__gxVerif->xfRegsDirty[index - 0x1000] && __gxVerif->verifyLevel >= __gxvWarnLev[53]) {
+ __GX_WARNF(GXWARN_XF_CTRL_UNINIT, index, name);
+ }
+}
+
+static void CheckClean(u32 index, const char* name) {
+ if (__gxVerif->xfRegsDirty[index - 0x1000] && __gxVerif->verifyLevel >= __gxvWarnLev[54]) {
+ __GX_WARNF(GXWARN_XF_CTRL_INIT, index, name);
+ }
+}
+
+static void CheckCTGColors(void) {
+ if ((u32)(__gxVerif->xfRegs[9] & 3) > 2 && __gxVerif->verifyLevel >= __gxvWarnLev[121]) {
+ __GX_WARNF(121, __gxVerif->xfRegs[9] & 3);
+ }
+}
+
+static GXBool __GXVertexPacketHas(GXAttr attr) {
+ switch (attr) {
+ case GX_VA_POS: return GET_REG_FIELD(__GXData->vcdLo, 2, 9) != 0;
+ case GX_VA_NRM: return __GXData->hasNrms ? GET_REG_FIELD(__GXData->vcdLo, 2, 11) != 0 : GX_FALSE;
+ case GX_VA_NBT: return __GXData->hasBiNrms ? GET_REG_FIELD(__GXData->vcdLo, 2, 11) != 0 : GX_FALSE;
+ case GX_VA_CLR0: return GET_REG_FIELD(__GXData->vcdLo, 2, 13) != 0;
+ case GX_VA_CLR1: return GET_REG_FIELD(__GXData->vcdLo, 2, 15) != 0;
+ case GX_VA_TEX0: return GET_REG_FIELD(__GXData->vcdHi, 2, 0) != 0;
+ case GX_VA_TEX1: return GET_REG_FIELD(__GXData->vcdHi, 2, 2) != 0;
+ case GX_VA_TEX2: return GET_REG_FIELD(__GXData->vcdHi, 2, 4) != 0;
+ case GX_VA_TEX3: return GET_REG_FIELD(__GXData->vcdHi, 2, 6) != 0;
+ case GX_VA_TEX4: return GET_REG_FIELD(__GXData->vcdHi, 2, 8) != 0;
+ case GX_VA_TEX5: return GET_REG_FIELD(__GXData->vcdHi, 2, 10) != 0;
+ case GX_VA_TEX6: return GET_REG_FIELD(__GXData->vcdHi, 2, 12) != 0;
+ case GX_VA_TEX7: return GET_REG_FIELD(__GXData->vcdHi, 2, 14) != 0;
+ case GX_VA_PNMTXIDX: return GET_REG_FIELD(__GXData->vcdLo, 1, 0) != 0;
+ case GX_VA_TEX0MTXIDX: return GET_REG_FIELD(__GXData->vcdLo, 1, 1) != 0;
+ case GX_VA_TEX1MTXIDX: return GET_REG_FIELD(__GXData->vcdLo, 1, 2) != 0;
+ case GX_VA_TEX2MTXIDX: return GET_REG_FIELD(__GXData->vcdLo, 1, 3) != 0;
+ case GX_VA_TEX3MTXIDX: return GET_REG_FIELD(__GXData->vcdLo, 1, 4) != 0;
+ case GX_VA_TEX4MTXIDX: return GET_REG_FIELD(__GXData->vcdLo, 1, 5) != 0;
+ case GX_VA_TEX5MTXIDX: return GET_REG_FIELD(__GXData->vcdLo, 1, 6) != 0;
+ case GX_VA_TEX6MTXIDX: return GET_REG_FIELD(__GXData->vcdLo, 1, 7) != 0;
+ case GX_VA_TEX7MTXIDX: return GET_REG_FIELD(__GXData->vcdLo, 1, 8) != 0;
+ default:
+ return GX_FALSE;
+ }
+}
+
+static void CheckVertexPacket(void) {
+ u32 numHostTextures;
+ u32 numHostTexAbsent;
+ u32 i;
+
+ if (!__GXVertexPacketHas(GX_VA_POS) && __gxVerif->verifyLevel >= __gxvWarnLev[57]) {
+ __GX_WARN(GXWARN_VTX_NO_GEOM);
+ }
+
+ if (__GXVertexPacketHas(GX_VA_CLR1) && !__GXVertexPacketHas(GX_VA_CLR0) &&__gxVerif->verifyLevel >= __gxvWarnLev[70]) {
+ __GX_WARN(GXWARN_VCD_CLR_ORDER);
+ }
+
+ numHostTextures = 0;
+ numHostTexAbsent = 0;
+
+ for (i = 0; i < 8; i++) {
+ if (__GXVertexPacketHas(TextureEnums[i])) {
+ numHostTextures += 1;
+ numHostTexAbsent = 0;
+ } else {
+ numHostTexAbsent += 1;
+ }
+ }
+
+ if (numHostTextures + numHostTexAbsent != 8 && __gxVerif->verifyLevel >= __gxvWarnLev[71]) {
+ __GX_WARN(GXWARN_VCD_TEX_ORDER);
+ }
+
+ if ((__gxVerif->xfRegs[8] & 3) == 0 && ((__gxVerif->xfRegs[8] >> 2) & 3) == 0 && (u32)((__gxVerif->xfRegs[8] >> 4) & 0xF) == 0) {
+ u32 numMatrixIndices = 0;
+
+ for (i = 0; i <= 8; i++) {
+ if (__GXVertexPacketHas(MtxIdxEnums[i])) {
+ numMatrixIndices += 1;
+ }
+ }
+
+ if (numMatrixIndices != 0 && __gxVerif->verifyLevel >= __gxvWarnLev[69]) {
+ __GX_WARN(GXWARN_VCD_FMT_UNSUP);
+ }
+ }
+}
+
+static void CheckSourceRows(void) {
+ u32 i;
+
+ for (i = 0; i < numRegularTextures; i++) {
+ switch ((__gxVerif->xfRegs[i + 64] >> 7) & 0x1F) {
+ case 0:
+ if (!__GXVertexPacketHas(GX_VA_POS) && __gxVerif->verifyLevel >= __gxvWarnLev[72]) {
+ __GX_WARNF(GXWARN_TEX_SRC_NPOS, i);
+ }
+ break;
+ case 1:
+ if (!__GXVertexPacketHas(GX_VA_NRM) && !__GXVertexPacketHas(GX_VA_NBT)&& __gxVerif->verifyLevel >= __gxvWarnLev[73]) {
+ __GX_WARNF(GXWARN_TEX_SRC_NNRM, i);
+ }
+ break;
+ case 2:
+ if (!__GXVertexPacketHas(GX_VA_CLR0) && __gxVerif->verifyLevel >= __gxvWarnLev[74]) {
+ __GX_WARNF(GXWARN_TEX_SRC_NCLR0, i);
+ }
+ if (!__GXVertexPacketHas(GX_VA_CLR1) && __gxVerif->verifyLevel >= __gxvWarnLev[75]) {
+ __GX_WARNF(GXWARN_TEX_SRC_NCLR1, i);
+ }
+ break;
+ case 3:
+ case 4:
+ if (!__GXVertexPacketHas(GX_VA_NBT) && __gxVerif->verifyLevel >= __gxvWarnLev[76]) {
+ __GX_WARNF(GXWARN_TEX_SRC_NNBT, i);
+ }
+ break;
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ case 9:
+ case 10:
+ case 11:
+ case 12:
+ if (!__GXVertexPacketHas(TextureEnums[((__gxVerif->xfRegs[i + 64] >> 7) & 0x1F) - 5]) && __gxVerif->verifyLevel >= __gxvWarnLev[77]) {
+ __GX_WARNF(GXWARN_TEX_SRC_NTEX, i, ((__gxVerif->xfRegs[i + 64] >> 7) & 0x1F) - 5);
+ }
+ break;
+ default:
+ if (__gxVerif->verifyLevel >= __gxvWarnLev[78]) {
+ __GX_WARNF(GXWARN_INV_TEX_SRC, i, (__gxVerif->xfRegs[i + 64] >> 7) & 0x1F);
+ }
+ break;
+ }
+ }
+}
+
+static void CheckTextureOrder(void) {
+ u8 done = 0;
+ u32 count = 0;
+
+ while (!done) {
+ if (count == __gxVerif->xfRegs[0x3F] || ((__gxVerif->xfRegs[count + 64] >> 4) & 7)) {
+ done = 1;
+ } else {
+ count += 1;
+ }
+ }
+
+ done = 0;
+ while (done == 0) {
+ if (count == __gxVerif->xfRegs[0x3F]) {
+ done = 1;
+ } else if ((u32)((__gxVerif->xfRegs[count + 64] >> 4) & 7) != 1) {
+ if (!((__gxVerif->xfRegs[count + 64] >> 4) & 7) && __gxVerif->verifyLevel >= __gxvWarnLev[79]) {
+ __GX_WARN(GXWARN_INV_TG_ORDER);
+ }
+ done = 1;
+ } else {
+ count += 1;
+ }
+ }
+
+ done = 0;
+ while (done == 0) {
+ if (count == __gxVerif->xfRegs[0x3F]) {
+ done = 1;
+ } else if (!((__gxVerif->xfRegs[count + 64] >> 4) & 7) || (u32)((__gxVerif->xfRegs[count + 64] >> 4) & 7) == 1) {
+ if (__gxVerif->verifyLevel >= __gxvWarnLev[79]) {
+ __GX_WARN(GXWARN_INV_TG_ORDER);
+ }
+ done = 1;
+ } else {
+ count += 1;
+ }
+ }
+}
+
+static void CheckRAM(u8 Normal, u32 StartingAddress, u32 Count, GXWarnID WarnID, char* Str) {
+ u32 i;
+ u8 printedPreamble;
+ u8 dirtyBit;
+
+ printedPreamble = 0;
+
+ for (i = StartingAddress; i < StartingAddress + Count; i++) {
+ dirtyBit = Normal != 0 ? __gxVerif->xfMtxDirty[i - 0x300] : __gxVerif->xfMtxDirty[i];
+
+ if (dirtyBit == 0) {
+ if (printedPreamble == 0) {
+ if (__gxVerif->verifyLevel >= __gxvWarnLev[WarnID]) {
+ __gxVerif->cb(__gxvWarnLev[WarnID], WarnID, Str);
+ }
+
+ printedPreamble = 1;
+ }
+ }
+ }
+}
+
+static void CheckBumpmapTextures(void) {
+ u32 i;
+ u32 BumpMapSource;
+ u32 BumpMapLight;
+ u32 lightRAMOffset;
+ char Preamble[256];
+
+ if (!__GXVertexPacketHas(GX_VA_PNMTXIDX)) {
+ if ((u32)(__gxVerif->xfRegs[24] & 0x3F) > 30 && __gxVerif->verifyLevel >= __gxvWarnLev[0x50]) {
+ __GX_WARNF(0x50, __gxVerif->xfRegs[24] & 0x3F);
+ }
+ sprintf(Preamble, __gxvWarnings[0x6A], __gxVerif->xfRegs[24] & 0x3F);
+ CheckRAM(1, ((__gxVerif->xfRegs[24] & 0x3F) * 3) + 0x400, 9U, 0x6A, Preamble);
+ }
+
+ for (i = 0; i < numBumpmapTextures; i++) {
+ BumpMapSource = (__gxVerif->xfRegs[numRegularTextures + i + 64] >> 12) & 7;
+ if ((__gxVerif->xfRegs[BumpMapSource + 64] >> 4) & 7 && __gxVerif->verifyLevel >= __gxvWarnLev[0x51]) {
+ __GX_WARNF(0x51, i + numRegularTextures, BumpMapSource);
+ }
+
+ BumpMapLight = (__gxVerif->xfRegs[numRegularTextures + i + 0x40] >> 15) & 7;
+ lightRAMOffset = (BumpMapLight * 0x10) + 0x60A;
+ if (!__gxVerif->xfLightDirty[lightRAMOffset - 0x600 + 0] && __gxVerif->verifyLevel >= __gxvWarnLev[0x52]) {
+ __GX_WARNF(0x52, i + numRegularTextures, BumpMapLight, "X");
+ }
+
+ if (!__gxVerif->xfLightDirty[lightRAMOffset - 0x600 + 1] && __gxVerif->verifyLevel >= __gxvWarnLev[0x52]) {
+ __GX_WARNF(0x52, i + numRegularTextures, BumpMapLight, "Y");
+ }
+
+ if (!__gxVerif->xfLightDirty[lightRAMOffset - 0x600 + 2] && __gxVerif->verifyLevel >= __gxvWarnLev[0x52]) {
+ __GX_WARNF(0x52, i + numRegularTextures, BumpMapLight, "Z");
+ }
+
+ if (!__GXVertexPacketHas(GX_VA_NBT) && __gxVerif->verifyLevel >= __gxvWarnLev[0x53]) {
+ __GX_WARNF(0x53, i);
+ }
+ }
+
+ lightRAMOffset; lightRAMOffset; // needed to match
+}
+
+static void CheckTextureTransformMatrices(void) {
+ u32 i;
+ u32 StartingAddress;
+ u8 MtxIndexInVertexPacket;
+ char Preamble[256];
+ u32 Val;
+
+ for (i = 0; i < numRegularTextures; i++) {
+ MtxIndexInVertexPacket = 0;
+ switch (i) {
+ case 0:
+ StartingAddress = ((__gxVerif->xfRegs[0x18] >> 6U) & 0x3F) << 2;
+ Val = (__gxVerif->xfRegs[0x18] >> 6) & 0x3F;
+ MtxIndexInVertexPacket = __GXVertexPacketHas(GX_VA_TEX0MTXIDX);
+ break;
+ case 1:
+ StartingAddress = ((__gxVerif->xfRegs[0x18] >> 12) & 0x3F) << 2;
+ Val = (__gxVerif->xfRegs[0x18] >> 12) & 0x3F;
+ MtxIndexInVertexPacket = __GXVertexPacketHas(GX_VA_TEX1MTXIDX);
+ break;
+ case 2:
+ StartingAddress = ((__gxVerif->xfRegs[0x18] >> 18) & 0x3F) << 2;
+ Val = (__gxVerif->xfRegs[0x18] >> 18) & 0x3F;
+ MtxIndexInVertexPacket = __GXVertexPacketHas(GX_VA_TEX2MTXIDX);
+ break;
+ case 3:
+ StartingAddress = ((__gxVerif->xfRegs[0x18] >> 24) & 0x3F) << 2;
+ Val = (__gxVerif->xfRegs[0x18] >> 24) & 0x3F;
+ MtxIndexInVertexPacket = __GXVertexPacketHas(GX_VA_TEX3MTXIDX);
+ break;
+ case 4:
+ StartingAddress = (__gxVerif->xfRegs[0x19] & 0x3F) << 2;
+ Val = __gxVerif->xfRegs[0x19] & 0x3F;
+ MtxIndexInVertexPacket = __GXVertexPacketHas(GX_VA_TEX4MTXIDX);
+ break;
+ case 5:
+ StartingAddress = ((__gxVerif->xfRegs[0x19] >> 6) & 0x3F) << 2;
+ Val = (__gxVerif->xfRegs[0x19] >> 6) & 0x3F;
+ MtxIndexInVertexPacket = __GXVertexPacketHas(GX_VA_TEX5MTXIDX);
+ break;
+ case 6:
+ StartingAddress = ((__gxVerif->xfRegs[0x19] >> 12) & 0x3F) << 2;
+ Val = (__gxVerif->xfRegs[0x19] >> 12) & 0x3F;
+ MtxIndexInVertexPacket = __GXVertexPacketHas(GX_VA_TEX6MTXIDX);
+ break;
+ case 7:
+ StartingAddress = ((__gxVerif->xfRegs[0x19] >> 18) & 0x3F) << 2;
+ Val = (__gxVerif->xfRegs[0x19] >> 18) & 0x3F;
+ MtxIndexInVertexPacket = __GXVertexPacketHas(GX_VA_TEX7MTXIDX);
+ break;
+ default:
+ if (__gxVerif->verifyLevel >= __gxvWarnLev[0x54]) {
+ __GX_WARNF(0x54, i);
+ }
+ break;
+ }
+
+ if (MtxIndexInVertexPacket == 0) {
+ u32 Size;
+ sprintf(Preamble, __gxvWarnings[0x6B], i, i, Val);
+ Size = !((__gxVerif->xfRegs[i + 64] >> 1) & 1) ? 8 : 12;
+ CheckRAM(0U, StartingAddress, Size, 0x6B, Preamble);
+ }
+ }
+}
+
+static void CheckInputForms(void) {
+ u32 i;
+
+ for (i = 0; i < numRegularTextures; i++) {
+ switch ((__gxVerif->xfRegs[i + 64] >> 7) & 0x1F) {
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ case 9:
+ case 10:
+ case 11:
+ case 12:
+ if ((__gxVerif->xfRegs[i + 64] >> 2) & 1 && __gxVerif->verifyLevel >= __gxvWarnLev[122]) {
+ __GX_WARNF(122, i, (__gxVerif->xfRegs[i + 64] >> 7) & 0x1F);
+ }
+ }
+ }
+}
+
+static void CheckLight(u32 lightSource) {
+ u32 lightRAMOffset;
+ u8 printedPreamble;
+ u32 i;
+
+ printedPreamble = 0;
+ lightRAMOffset = (lightSource * 0x10) + 0x603;
+ for (i = 0; i < 13; i++) {
+ if (!__gxVerif->xfLightDirty[lightRAMOffset + i - 0x600]) {
+ if (!printedPreamble) {
+ if (__gxVerif->verifyLevel >= __gxvWarnLev[0x6C]) {
+ __GX_WARNF(0x6C, lightSource);
+ }
+
+ printedPreamble = 1;
+ }
+ }
+ }
+}
+
+// NONMATCHING
+static void CheckColor0(void) {
+ char Preamble[256];
+ u8 haveLight;
+ u32 i;
+ u8 lightUsed;
+
+ if ((u8)(BYTE3(__gxVerif->xfRegs[9]) & 3) || numColorTextures != 0) {
+ if (!__gxVerif->xfRegsDirty[14] && __gxVerif->verifyLevel >= __gxvWarnLev[0x7A]) {
+ __GX_WARNF(0x7A, 0x100E, "Color 0 control register");
+ }
+
+ if (!__gxVerif->xfRegsDirty[16] && __gxVerif->verifyLevel >= __gxvWarnLev[0x7A]) {
+ __GX_WARNF(0x7A, 0x1010, "Alpha 0 control register");
+ }
+
+ if (!(BYTE3(__gxVerif->xfRegs[14]) & 1) && !__gxVerif->xfRegsDirty[12] && __gxVerif->verifyLevel >= __gxvWarnLev[0x7B]) {
+ __GX_WARNF(0x7B, 0, 0, 0x100C);
+ }
+
+ if (!((BYTE3(__gxVerif->xfRegs[14]) >> 6) & 1) && !__gxVerif->xfRegsDirty[10] && __gxVerif->verifyLevel >= __gxvWarnLev[0x7C]) {
+ __GX_WARNF(0x7C, 0, 0, 0x100A);
+ }
+
+ if ((u32)((BYTE3(__gxVerif->xfRegs[14]) >> 1) & 1) == 1 || (u32)((BYTE3(__gxVerif->xfRegs[16]) >> 1) & 1) == 1) {
+ haveLight = 0;
+ for (i = 0; i < 8; i++) {
+ lightUsed = 0;
+ switch (i) {
+ case 0:
+ if ((u8)((BYTE3(__gxVerif->xfRegs[14]) >> 2) & 1) || (u8)((BYTE3(__gxVerif->xfRegs[16]) >> 2) & 1)) {
+ lightUsed = 1;
+ }
+ break;
+ case 1:
+ if ((u8)((BYTE3(__gxVerif->xfRegs[14]) >> 3) & 1) || (u8)((BYTE3(__gxVerif->xfRegs[16]) >> 3) & 1)) {
+ lightUsed = 1;
+ }
+ break;
+ case 2:
+ if ((u8)((BYTE3(__gxVerif->xfRegs[14]) >> 4) & 1) || (u8)((BYTE3(__gxVerif->xfRegs[16]) >> 4) & 1)) {
+ lightUsed = 1;
+ }
+ break;
+ case 3:
+ if ((u8)((BYTE3(__gxVerif->xfRegs[14]) >> 5) & 1) || (u8)((BYTE3(__gxVerif->xfRegs[16]) >> 5) & 1)) {
+ lightUsed = 1;
+ }
+ break;
+ case 4:
+ if ((u8)((BYTE2(__gxVerif->xfRegs[14]) >> 3) & 1) || (u8)((BYTE2(__gxVerif->xfRegs[16]) >> 3) & 1)) {
+ lightUsed = 1;
+ }
+ break;
+ case 5:
+ if ((u8)((BYTE2(__gxVerif->xfRegs[14]) >> 4) & 1) || (u8)((BYTE2(__gxVerif->xfRegs[16]) >> 4) & 1)) {
+ lightUsed = 1;
+ }
+ break;
+ case 6:
+ if ((u8)((BYTE2(__gxVerif->xfRegs[14]) >> 5) & 1) || (u8)((BYTE2(__gxVerif->xfRegs[16]) >> 5) & 1)) {
+ lightUsed = 1;
+ }
+ break;
+ case 7:
+ if ((u8)((BYTE2(__gxVerif->xfRegs[14]) >> 6) & 1) || (u8)((BYTE2(__gxVerif->xfRegs[16]) >> 6) & 1)) {
+ lightUsed = 1;
+ }
+ break;
+ }
+ if (lightUsed != 0) {
+ CheckLight(i);
+ haveLight = 1;
+ }
+ }
+
+ if (haveLight != 0) {
+ if (!((BYTE2(__gxVerif->xfRegs[14]) >> 2) & 1) && ((HIWORD(__gxVerif->xfRegs[14]) >> 7) & 3) && __gxVerif->verifyLevel >= __gxvWarnLev[0x59]) {
+ __GX_WARNF(0x59, "COLOR0", "COLOR0");
+ }
+
+ if (!((BYTE2(__gxVerif->xfRegs[16]) >> 2) & 1) && ((HIWORD(__gxVerif->xfRegs[16]) >> 7) & 3) && __gxVerif->verifyLevel >= __gxvWarnLev[0x59]) {
+ __GX_WARNF(0x59, "ALPHA0", "ALPHA0");
+ }
+
+ if (((HIWORD(__gxVerif->xfRegs[14]) >> 7) & 3)
+ || ((u8)((BYTE2(__gxVerif->xfRegs[14]) >> 1) & 1) && ((u32)((BYTE2(__gxVerif->xfRegs[14]) >> 2) & 1) == 1))
+ || ((HIWORD(__gxVerif->xfRegs[16]) >> 7) & 3)
+ || ((u8)((BYTE2(__gxVerif->xfRegs[16]) >> 1) & 1) && ((u32)((BYTE2(__gxVerif->xfRegs[16]) >> 2) & 1) == 1))) {
+ if ((__GXVertexPacketHas(GX_VA_NRM) == 0) && (__GXVertexPacketHas(GX_VA_NBT) == 0) && __gxVerif->verifyLevel >= __gxvWarnLev[0x5A]) {
+ __GX_WARNF(0x5A, 0);
+ }
+ if (__GXVertexPacketHas(GX_VA_PNMTXIDX) == 0) {
+ if ((u32)(BYTE3(__gxVerif->xfRegs[24]) & 0x3F) > 30 && __gxVerif->verifyLevel >= __gxvWarnLev[0x5B]) {
+ __GX_WARNF(0x5B, 0, (BYTE3(__gxVerif->xfRegs[24]) & 0x3F));
+ }
+ sprintf(Preamble, __gxvWarnings[0x6D], 0, (u8)(BYTE3(__gxVerif->xfRegs[24]) & 0x3F));
+ CheckRAM(1, ((BYTE3(__gxVerif->xfRegs[24]) & 0x3F) * 3) + 0x400, 9, 0x6D, Preamble);
+ }
+ }
+ }
+ }
+ }
+}
+
+// NONMATCHING
+static void CheckColor1(void) {
+ u8 usingColor1;
+ char Preamble[256];
+ u8 haveLight;
+ u32 i;
+ u8 lightUsed;
+
+ if (numColorTextures > 1 && ((u32)((BYTE3(__gxVerif->xfRegs[numRegularTextures + numBumpmapTextures + 1 + 64]) >> 4) & 7) == 3)) {
+ usingColor1 = 1;
+ } else {
+ usingColor1 = 0;
+ }
+
+ if ((u32)(BYTE3(__gxVerif->xfRegs[9]) & 3) == 2 || usingColor1) {
+ if (!__gxVerif->xfRegsDirty[15] && __gxVerif->verifyLevel >= __gxvWarnLev[0x7A]) {
+ __GX_WARNF(0x7A, 0x100F, "Color 1 control register");
+ }
+
+ if (!__gxVerif->xfRegsDirty[17] && __gxVerif->verifyLevel >= __gxvWarnLev[0x7A]) {
+ __GX_WARNF(0x7A, 0x1011, "Alpha 1 control register");
+ }
+
+ if (!(BYTE3(__gxVerif->xfRegs[15]) & 1) && !__gxVerif->xfRegsDirty[13] && __gxVerif->verifyLevel >= __gxvWarnLev[0x7B]) {
+ __GX_WARNF(0x7B, 1, 1, 0x100D);
+ }
+
+ if (!((BYTE3(__gxVerif->xfRegs[15]) >> 6) & 1) && !__gxVerif->xfRegsDirty[11] && __gxVerif->verifyLevel >= __gxvWarnLev[0x7C]) {
+ __GX_WARNF(0x7C, 1, 1, 0x100B);
+ }
+
+ if ((u32)((BYTE3(__gxVerif->xfRegs[15]) >> 1) & 1) == 1 || (u32)((BYTE3(__gxVerif->xfRegs[17]) >> 1) & 1) == 1) {
+ haveLight = 0;
+ for (i = 0; i < 8; i++) {
+ lightUsed = 0;
+ switch (i) {
+ case 0:
+ if ((u8)((BYTE3(__gxVerif->xfRegs[15]) >> 2) & 1) || (u8)((BYTE3(__gxVerif->xfRegs[17]) >> 2) & 1)) {
+ lightUsed = 1;
+ }
+ break;
+ case 1:
+ if ((u8)((BYTE3(__gxVerif->xfRegs[15]) >> 3) & 1) || (u8)((BYTE3(__gxVerif->xfRegs[17]) >> 3) & 1)) {
+ lightUsed = 1;
+ }
+ break;
+ case 2:
+ if ((u8)((BYTE3(__gxVerif->xfRegs[15]) >> 4) & 1) || (u8)((BYTE3(__gxVerif->xfRegs[17]) >> 4) & 1)) {
+ lightUsed = 1;
+ }
+ break;
+ case 3:
+ if ((u8)((BYTE3(__gxVerif->xfRegs[15]) >> 5) & 1) || (u8)((BYTE3(__gxVerif->xfRegs[17]) >> 5) & 1)) {
+ lightUsed = 1;
+ }
+ break;
+ case 4:
+ if ((u8)((BYTE2(__gxVerif->xfRegs[15]) >> 3) & 1) || (u8)((BYTE2(__gxVerif->xfRegs[17]) >> 3) & 1)) {
+ lightUsed = 1;
+ }
+ break;
+ case 5:
+ if ((u8)((BYTE2(__gxVerif->xfRegs[15]) >> 4) & 1) || (u8)((BYTE2(__gxVerif->xfRegs[17]) >> 4) & 1)) {
+ lightUsed = 1;
+ }
+ break;
+ case 6:
+ if ((u8)((BYTE2(__gxVerif->xfRegs[15]) >> 5) & 1) || (u8)((BYTE2(__gxVerif->xfRegs[17]) >> 5) & 1)) {
+ lightUsed = 1;
+ }
+ break;
+ case 7:
+ if ((u8)((BYTE2(__gxVerif->xfRegs[15]) >> 6) & 1) || (u8)((BYTE2(__gxVerif->xfRegs[17]) >> 6) & 1)) {
+ lightUsed = 1;
+ }
+ break;
+ }
+ if (lightUsed != 0) {
+ CheckLight(i);
+ haveLight = 1;
+ }
+ }
+
+ if (haveLight != 0) {
+ if (!((BYTE2(__gxVerif->xfRegs[15]) >> 2) & 1) && ((HIWORD(__gxVerif->xfRegs[15]) >> 7) & 3) && __gxVerif->verifyLevel >= __gxvWarnLev[0x59]) {
+ __GX_WARNF(0x59, "COLOR1", "COLOR1");
+ }
+
+ if (!((BYTE2(__gxVerif->xfRegs[17]) >> 2) & 1) && ((HIWORD(__gxVerif->xfRegs[17]) >> 7) & 3) && __gxVerif->verifyLevel >= __gxvWarnLev[0x59]) {
+ __GX_WARNF(0x59, "ALPHA1", "ALPHA1");
+ }
+
+ if (((HIWORD(__gxVerif->xfRegs[15]) >> 7) & 3)
+ || ((u8)((BYTE2(__gxVerif->xfRegs[15]) >> 1) & 1) && ((u32)((BYTE2(__gxVerif->xfRegs[15]) >> 2) & 1) == 1))
+ || ((HIWORD(__gxVerif->xfRegs[17]) >> 7) & 3)
+ || ((u8)((BYTE2(__gxVerif->xfRegs[17]) >> 1) & 1) && ((u32)((BYTE2(__gxVerif->xfRegs[17]) >> 2) & 1) == 1))) {
+ if ((__GXVertexPacketHas(GX_VA_NRM) == 0) && (__GXVertexPacketHas(GX_VA_NBT) == 0) && __gxVerif->verifyLevel >= __gxvWarnLev[0x5A]) {
+ __GX_WARNF(0x5A, 1);
+ }
+ if (__GXVertexPacketHas(GX_VA_PNMTXIDX) == 0) {
+ if ((u32)(BYTE3(__gxVerif->xfRegs[24]) & 0x3F) > 30 && __gxVerif->verifyLevel >= __gxvWarnLev[0x5B]) {
+ __GX_WARNF(0x5B, 1, (u8)(BYTE3(__gxVerif->xfRegs[24]) & 0x3F));
+ }
+ sprintf(Preamble, __gxvWarnings[0x6D], 1, (u8)(BYTE3(__gxVerif->xfRegs[24]) & 0x3F));
+ CheckRAM(1, ((BYTE3(__gxVerif->xfRegs[24]) & 0x3F) * 3) + 0x400, 9, 0x6D, Preamble);
+ }
+ }
+ }
+ }
+ }
+}
+
+static void CheckViewport(void) {
+ f32 vl;
+ f32 vr;
+ f32 vt;
+ f32 vb;
+
+ vl = (*(f32*)&__gxVerif->xfRegs[29] - *(f32*)&__gxVerif->xfRegs[26]) - 342.0f;
+ vt = (*(f32*)&__gxVerif->xfRegs[30] + *(f32*)&__gxVerif->xfRegs[27]) - 342.0f;
+ vr = (*(f32*)&__gxVerif->xfRegs[29] + *(f32*)&__gxVerif->xfRegs[26]) - 342.0f;
+ vb = (*(f32*)&__gxVerif->xfRegs[30] - *(f32*)&__gxVerif->xfRegs[27]) - 342.0f;
+
+ if ((vt < -0.5f || vt > 528.0f) && __gxVerif->verifyLevel >= __gxvWarnLev[0x55]) {
+ __GX_WARNF(0x55, vt);
+ }
+
+ if ((vb < 0.0f || vb > 528.0f) && __gxVerif->verifyLevel >= __gxvWarnLev[0x56]) {
+ __GX_WARNF(0x56, vb);
+ }
+
+ if ((vl < 0.0f || vl > 640.0f) && __gxVerif->verifyLevel >= __gxvWarnLev[0x57]) {
+ __GX_WARNF(0x57, vl);
+ }
+
+ if ((vr < 0.0f || vr > 640.0f) && __gxVerif->verifyLevel >= __gxvWarnLev[0x58]) {
+ __GX_WARNF(0x58, vr);
+ }
+}
+
+static void ComputeSignExponentMantissa(f32 floatVal, u32* sign, u32* exponent, u32* mantissa) {
+ u32 intVal = *(u32*)&floatVal;
+
+ *sign = (intVal >> 31) & 1;
+ *exponent = (intVal >> 23) & 0xFF;
+ *mantissa = intVal & 0x7FFFFF;
+}
+
+static void CheckFloatingPointValue(u8 dirtyBit, u32 value, char* label) {
+ u32 sign;
+ u32 exponent;
+ u32 mantissa;
+ f32 valuef;
+
+ &valuef;
+
+ if ((dirtyBit == 0)) {
+ return;
+ }
+ valuef = *(f32 *)&value;
+ ComputeSignExponentMantissa(valuef, &sign, &exponent, &mantissa);
+
+ if (exponent == 0 && mantissa == 0) {
+ return;
+ }
+
+ if (exponent == 0xFF) {
+ if (__gxVerif->verifyLevel >= 2) {
+ if (mantissa == 0) {
+ if (sign != 0) {
+ if (__gxVerif->verifyLevel >= __gxvWarnLev[0x5C]) {
+ __GX_WARNF(0x5C, label, "-", *(u32 *)&valuef);
+ }
+ } else {
+ if (__gxVerif->verifyLevel >= __gxvWarnLev[0x5C]) {
+ __GX_WARNF(0x5C, label, "+", *(u32 *)&valuef);
+ }
+ }
+ } else {
+ if (__gxVerif->verifyLevel >= __gxvWarnLev[0x5D]) {
+ __GX_WARNF(0x5D, label, *(u32 *)&valuef);
+ }
+ }
+ }
+ } else if (__gxVerif->verifyLevel >= 3) {
+ if (exponent < 0x6BU) {
+ if (__gxVerif->verifyLevel >= __gxvWarnLev[0x5E]) {
+ __GX_WARNF(0x5E, label, valuef, *(u32 *)&valuef);
+ }
+ } else if (exponent > 0x96U) {
+ if (__gxVerif->verifyLevel >= __gxvWarnLev[0x5F]) {
+ __GX_WARNF(0x5F, label, valuef, *(u32 *)&valuef);
+ }
+ }
+ }
+}
+
+static void CheckMatrixRAMRanges(void) {
+ u32 i;
+ char label[256];
+
+ for (i = 0; i <= 255; i++) {
+ sprintf(label, "Geometry/Texture Matrix ram address 0x%04x", i);
+ CheckFloatingPointValue(__gxVerif->xfMtxDirty[i], __gxVerif->xfMtx[i], label);
+ }
+}
+
+static void CheckNormalRAMRanges(void) {
+ u32 i;
+ char label[256];
+
+ for (i = 1024; i <= 1119; i++) {
+ sprintf(label, "Normal Matrix ram address 0x%04x", i);
+ CheckFloatingPointValue(__gxVerif->xfNrmDirty[i - 1024], __gxVerif->xfNrm[i - 1024], label);
+ }
+}
+
+static void CheckDMatrixRAMRanges(void) {
+ u32 i;
+ char label[256];
+
+ for (i = 1280; i <= 1535; i++) {
+ sprintf(label, "Dual Texture Matrix ram address 0x%04x", i);
+ CheckFloatingPointValue(__gxVerif->xfDMtxDirty[i - 1280], __gxVerif->xfDMtx[i - 1280], label);
+ }
+}
+
+static void CheckLightRAMRanges(void) {
+ u32 lightSource;
+ u32 lightRAMOffset;
+ char label[256];
+ u32 i;
+
+ for (lightSource = 0; lightSource < 8; lightSource++) {
+ for (i = 1; i < 13; i++) {
+ lightRAMOffset = ((lightSource << 4) + i) + 0x603;
+ sprintf(label, "Light %d %s (address 0x%04x)", lightSource, lightRegisterNames[i], lightRAMOffset);
+ CheckFloatingPointValue(__gxVerif->xfLightDirty[lightRAMOffset - 0x600], __gxVerif->xfLight[(s32) (lightRAMOffset - 0x600)], label);
+ }
+ }
+
+ i; lightSource; // needed to match
+}
+
+static void CheckControlRAMRanges(void) {
+ CheckFloatingPointValue(__gxVerif->xfRegsDirty[0x1A], __gxVerif->xfRegs[0x1A], "Viewport Scale X");
+ CheckFloatingPointValue(__gxVerif->xfRegsDirty[0x1B], __gxVerif->xfRegs[0x1B], "Viewport Scale Y");
+ CheckFloatingPointValue(__gxVerif->xfRegsDirty[0x1C], __gxVerif->xfRegs[0x1C], "Viewport Scale Z");
+ CheckFloatingPointValue(__gxVerif->xfRegsDirty[0x1D], __gxVerif->xfRegs[0x1D], "Viewport Offset X");
+ CheckFloatingPointValue(__gxVerif->xfRegsDirty[0x1E], __gxVerif->xfRegs[0x1E], "Viewport Offset Y");
+ CheckFloatingPointValue(__gxVerif->xfRegsDirty[0x1F], __gxVerif->xfRegs[0x1F], "Viewport Offset Z");
+ CheckFloatingPointValue(__gxVerif->xfRegsDirty[0x20], __gxVerif->xfRegs[0x20], "Projection Matrix A Value");
+ CheckFloatingPointValue(__gxVerif->xfRegsDirty[0x21], __gxVerif->xfRegs[0x21], "Projection Matrix B Value");
+ CheckFloatingPointValue(__gxVerif->xfRegsDirty[0x22], __gxVerif->xfRegs[0x22], "Projection Matrix C Value");
+ CheckFloatingPointValue(__gxVerif->xfRegsDirty[0x23], __gxVerif->xfRegs[0x23], "Projection Matrix D Value");
+ CheckFloatingPointValue(__gxVerif->xfRegsDirty[0x24], __gxVerif->xfRegs[0x24], "Projection Matrix E Value");
+ CheckFloatingPointValue(__gxVerif->xfRegsDirty[0x25], __gxVerif->xfRegs[0x25], "Projection Matrix F Value");
+}
+
+static void CheckFloatingPointRanges(void) {
+ CheckMatrixRAMRanges();
+ CheckNormalRAMRanges();
+ CheckDMatrixRAMRanges();
+ CheckLightRAMRanges();
+ CheckControlRAMRanges();
+}
+
+static void CheckMatrixIndices(void) {
+ if (!__GXVertexPacketHas(GX_VA_PNMTXIDX)
+ || !__GXVertexPacketHas(GX_VA_TEX0MTXIDX)
+ || !__GXVertexPacketHas(GX_VA_TEX1MTXIDX)
+ || !__GXVertexPacketHas(GX_VA_TEX2MTXIDX)
+ || !__GXVertexPacketHas(GX_VA_TEX3MTXIDX))
+ {
+ CheckDirty(0x1018U, "Geometry & Textures [0-3] transform matrix indices");
+ }
+
+ if (__gxVerif->verifyLevel >= 1 && !__GXVertexPacketHas(GX_VA_PNMTXIDX)) {
+ CheckRAM(0U, (__gxVerif->xfRegs[24] * 4) & 0xFC, 0xCU, 0x6E, __gxvWarnings[0x6E]);
+ }
+
+ if ((!__GXVertexPacketHas(GX_VA_TEX4MTXIDX)
+ || !__GXVertexPacketHas(GX_VA_TEX5MTXIDX)
+ || !__GXVertexPacketHas(GX_VA_TEX6MTXIDX)
+ || !__GXVertexPacketHas(GX_VA_TEX7MTXIDX))
+ && numRegularTextures > 4
+ && __gxVerif->verifyLevel >= 1
+ && !__gxVerif->xfRegsDirty[0x19] && __gxVerif->verifyLevel >= __gxvWarnLev[0x60]) {
+ __GX_WARNF(0x60, numRegularTextures, 0x1019U);
+ }
+}
+
+static void CheckErrors(void) {
+ u32 i;
+ char registerName[80];
+
+ CheckDirty(0x103FU, "Number of XF output textures");
+ CheckDirty(0x1009U, "Number of XF output colors");
+ CheckDirty(0x1008U, "InVertexSpec");
+ CheckDirty(0x101AU, "Viewport ScaleX");
+ CheckDirty(0x101BU, "Viewport ScaleY");
+ CheckDirty(0x101CU, "Viewport ScaleZ");
+ CheckDirty(0x101DU, "Viewport OffsetX");
+ CheckDirty(0x101EU, "Viewport OffsetY");
+ CheckDirty(0x101FU, "Viewport OffsetZ");
+ CheckDirty(0x1020U, "Projection matrix 'A' value");
+ CheckDirty(0x1021U, "Projection matrix 'B' value");
+ CheckDirty(0x1022U, "Projection matrix 'C' value");
+ CheckDirty(0x1023U, "Projection matrix 'D' value");
+ CheckDirty(0x1024U, "Projection matrix 'E' value");
+ CheckDirty(0x1025U, "Projection matrix 'F' value");
+ CheckDirty(0x1026U, "Projection matrix orthographic/perspective select");
+ CheckMatrixIndices();
+
+ if (__gxVerif->verifyLevel >= 1) {
+ if (!(u32)(BYTE3(__gxVerif->xfRegs[9]) & 3) && !__gxVerif->xfRegs[0x3F] && __gxVerif->verifyLevel >= __gxvWarnLev[0x38]) {
+ __GX_WARN(0x38);
+ }
+
+ CheckCTGColors();
+
+ if (__gxVerif->xfRegs[0x3F] > 8 && __gxVerif->verifyLevel >= __gxvWarnLev[0x64]) {
+ __GX_WARNF(0x64, __gxVerif->xfRegs[0x3F], 8);
+ }
+ if (numRegularTextures > 8 && __gxVerif->verifyLevel >= __gxvWarnLev[0x65]) {
+ __GX_WARNF(0x65, numRegularTextures, 8);
+ }
+ if (numBumpmapTextures > 3 && __gxVerif->verifyLevel >= __gxvWarnLev[0x66]) {
+ __GX_WARNF(0x66, numBumpmapTextures, 3);
+ }
+ if (numColorTextures > 2 && __gxVerif->verifyLevel >= __gxvWarnLev[0x67]) {
+ __GX_WARNF(0x67, numColorTextures, 2);
+ }
+ if (numColor0Textures > 1 && __gxVerif->verifyLevel >= __gxvWarnLev[0x69]) {
+ __GX_WARNF(0x69, 0);
+ }
+ if (numColor1Textures > 1 && __gxVerif->verifyLevel >= __gxvWarnLev[0x69]) {
+ __GX_WARNF(0x69, 1);
+ }
+
+ CheckVertexPacket();
+
+ for (i = 0; i < __gxVerif->xfRegs[0x3F]; i++) {
+ sprintf(registerName, "Texture %d settings", i);
+ CheckDirty(i + 0x1040, registerName);
+ }
+
+ CheckSourceRows();
+ CheckTextureOrder();
+ if (numBumpmapTextures != 0) {
+ CheckBumpmapTextures();
+ }
+
+ CheckTextureTransformMatrices();
+ if (numColorTextures != 0 && (u32)((BYTE3(__gxVerif->xfRegs[numRegularTextures + numBumpmapTextures + 64]) >> 4) & 7) != 2 && __gxVerif->verifyLevel >= __gxvWarnLev[0x68]) {
+ __GX_WARN(0x68U);
+ }
+
+ CheckColor0();
+ CheckColor1();
+ CheckViewport();
+ }
+}
+
+static void CheckWarnings(void) {
+ if (__gxVerif->verifyLevel >= 1) {
+ CheckInputForms();
+ }
+
+ CheckClean(0x1000U, "Internal error register");
+ CheckClean(0x1001U, "Internal diagnostic register");
+ CheckClean(0x1002U, "Internal state register 0");
+ CheckClean(0x1003U, "Internal state register 1");
+ CheckClean(0x1004U, "Power savings register");
+
+ if (__gxVerif->verifyLevel >= 2) {
+ CheckFloatingPointRanges();
+ }
+}
+
+static void DumpXFRegisters(void) {
+ static u8 firstTime = 1;
+}
+
+void __GXVerifyXF(void) {
+ InitializeXFVerifyData();
+ CheckErrors();
+ CheckWarnings();
+ DumpCount++;
+}
+
+#endif
diff --git a/src/revolution/gx/GXVerify.c b/src/revolution/gx/GXVerify.c
new file mode 100644
index 0000000000..9650ccb340
--- /dev/null
+++ b/src/revolution/gx/GXVerify.c
@@ -0,0 +1,372 @@
+#if DEBUG
+
+#include <revolution/gx.h>
+
+#include "__gx.h"
+
+static __GXVerifyData __gxVerifData;
+struct __GXVerifyData* __gxVerif = &__gxVerifData;
+
+char* __gxvWarnings[126] = {
+ "Invalid Vertex Format. Normal count must be set to %s.",
+ "Texture size %ld not initialized.",
+ "Left edge of scissor rectangle is less than %d.",
+ "Top edge of scissor rectangle is less than %d.",
+ "Right edge of scissor rectangle is greater than %d in %s mode.",
+ "Bottom edge of scissor rectangle is greater than %d in %s mode.",
+ "%s value for subsample %d in pixel %ld is not 6 when single-sampling.",
+ "Indirect texture command for stage %ld is not set.",
+ "Invalid indirect texture request in TEV stage %ld.",
+ "Indirect matrix %ld requested in stage %d not set.",
+ "Requested indirect textures never initialized.",
+ "Indirect texture coordinate scales %d and %d not set.",
+ "Invalid texture coordinate specified for indirect stage %d.",
+ "Indirect texture feedback accumulation is on in TEV stage 0.",
+ "Indirect bump alpha is enabled in TEV stage 0.",
+ "Indirect vs. direct mask byte never set.",
+ "Texture reference never written for TEV stage %ld.",
+ "Invalid texture coordinate specified for TEV stage %ld.",
+ "Texture %ld is used as both indirect and direct.",
+ "Texture %ld not configured.",
+ "Base pointer for cached texture %ld is not specified.",
+ "TLUT for indexed texture %ld was never set up.",
+ "%s is not a power of 2 for mipmapped texture %ld.",
+ "%s is not GX_CLAMP for non-power-of-2 width in texture %ld.",
+ "Minification filter for texture %ld is not compatible with color index texture format.",
+ "Minimum LOD is greater than maximum LOD in texture %ld.",
+ "Maximum LOD is greater than image's maximum LOD for texture %ld.",
+ "LOD bias clamp shold be used with edge LOD for texture %ld.",
+ "Texture %ld does not meet requirements for anisotropic mipmapping.",
+ "Filters are not linear for field prediction in texture %ld.",
+ "Incomplete rounding mode configuration for texture %ld.",
+ "Rounding color indexed texture %ld.",
+ "Environment for TEV stage %ld not fully set up.",
+ "Invalid color channel selected in TEV stage %ld.",
+ "Argument %s selects null texture in TEV color stage %ld.",
+ "Argument %s selects null texture in TEV alpha stage %ld.",
+ "Color arg %s in TEV stage %ld accesses register %s, which may be dirty.",
+ "Alpha arg %s in TEV stage %ld accesses register %s, which may be dirty.",
+ "Color arg %s in TEV stage %ld accesses register %s, which was last unclamped. Possible wrap-around effect.",
+ "Alpha arg %s in TEV stage %ld accesses register %s, which was last unclamped. Possible wrap-around effect.",
+ "Z texturing enabled, but no Z offset specified.",
+ "Z texturing enabled, but no texture specified for final TEV stage.",
+ "Final TEV stage doesn't write color to register GX_TEVPREV.",
+ "Final TEV stage doesn't write alpha to register GX_TEVPREV.",
+ "Final TEV color stage has no clamping. Possible color wrap-around effect.",
+ "Final TEV alpha stage has no clamping. Possible alpha wrap-around effect.",
+ "Z buffering is before texture, but alpha compare operation is active.",
+ "PE blend and logicop are both on.",
+ "Selected pixel format does not support dithering.",
+ "Multisample enabled but pixel type is not RGB565.",
+ "Pixel type is RGB565 but multisample is not enabled.",
+ "Multisample locations for pixel %ld are not ordered correctly for antialias filter.",
+ "Invalid texgen_type %d for texture %d.",
+ "Register address 0x%04x uninitialized (%s).",
+ "Register address 0x%04x modified (%s), probably should not be.",
+ "Invalid combination of %d output color channels and %d color texgen textures.",
+ "Number of color channels and number of texgens are both zero.",
+ "Vertex packet does not contain position values.",
+ "Mismatched argument setting in vertex attribute. %s should be used with %s.",
+ "GXSetVtxAttrFmt: Normals only support signed types.",
+ "GXSetVtxAttrFmt: Number of fractional bits is fixed for normals. %s uses %d. Your setting will be ignored.",
+ "GXSetVtxAttrFmt: GX_F32 type doesn't refer the frac argument. Your setting will be ignored.",
+ "GXSetVtxAttrFmt: Colors don't refer the frac argument. Your setting will be ignored.",
+ "Invalid value (%d) for INVERTEXSPEC_REG.host_colors.",
+ "XF is not expecting host normals but cp is sending them.",
+ "XF is not expecting host normals, binormals and tangents but cp is sending them.",
+ "XF is expecting host normals but cp is not sending them.",
+ "XF is expecting host normals but cp is sending normals, binormals, and tangents.",
+ "XF is expecting host normals, binormals and tangents but cp is only sending normals.",
+ "This vertex format (Position + Matrix Indices only) is not supported.",
+ "VCD for GX_VA_CLR1 is activated though GX_VA_CLR0 is set to GX_NONE. GX_VA_CLR0 should be used first.",
+ "VCDs for input texture coordinates are not used sequentially from smaller IDs.",
+ "GX_TEXCOORD%d specifies source row of position, but this is not getting sent.",
+ "GX_TEXCOORD%d specifies source row of normal, but this is not getting sent.",
+ "GX_TEXCOORD%d specifies source row of color0, but color0 is not getting sent.",
+ "GX_TEXCOORD%d specifies source row of color1, but color1 is not getting sent.",
+ "GX_TEXCOORD%d specifies source row of binormal or tangent, but this is not getting sent.",
+ "GX_TEXCOORD%d specifies source row of input texture coordinate %d, but this is not getting sent.",
+ "GX_TEXCOORD%d is specifying an invalid source row of %d.",
+ "TexCoordGen types are out of order. GX_TG_MTX2x4/3x4 should come first (if any), followed by GX_TG_BUMP (if any), then GX_TG_SRTG (if any).",
+ "Bumpmap texgen is defined, which requires that binormals and tangents be transformed by a normal matrix, but current matrix index is set to an invalid value (%d) for normal transform.",
+ "GX_TEXCOORD%d (texgen type bumpmap) is referencing texture %d as a source texture, which is not of texgen type regular.",
+ "GX_TEXCOORD%d (texgen type bumpmap) using light source %d, but light's %c position is not defined.",
+ "GX_TEXCOORD%d is defined as texgen type bumpmap, but binormals and tangents are not getting sent.",
+ "Invalid regular texture number (%d)",
+ "Top edge of viewport (%f) is out of recommended range. It may cause incorrect clipping.",
+ "Bottom edge of viewport (%f) is out of recommended range. It may cause incorrect clipping.",
+ "Left edge of viewport (%f) is out of recommended range. It may cause incorrect clipping.",
+ "Right edge of viewport (%f) is out of recommended range. It may cause incorrect clipping.",
+ "Channel %s uses specular function (GX_AF_SPEC), but diffuse function is not GX_DF_NONE.",
+ "Channel %d performs lighting which requires a normal, but this is not getting sent.",
+ "Channel %d performs lighting which requires the normal to be transformed by a normal matrix, but current matrix index is (%d), which may be invalid.",
+ "%s has a value of %sinfinity (%08x), which is probably not intended.",
+ "%s has a value of NaN (%08x), which is probably not intended.",
+ "%s has a value of (%f 0x%08x), which might be unintentionally small.",
+ "%s has a value of (%f 0x%08x), which might be unintentionally large.",
+ "%d regular textures active, but MatrixIndex1 register (0x%04x) uninitialized.",
+ "gen_mode register not initialized.",
+ "Number of XF output textures does not match what downstream units are expecting.",
+ "Number of XF output colors does not match what downstream units are expecting.",
+ "Number of all texgens (%d) > max allowed %d.",
+ "Number of regular(2x4/3x4) type texgens (%d) > max allowed %d.",
+ "Number of bumpmap type texgens (%d) > max allowed %d.",
+ "Number of color texgens (%d) > max allowed %d.",
+ "First color texgen is not referencing COLOR0.",
+ "Color texgen from COLOR%d is used more than once.",
+ "Bumpmap texgen is defined, which requires the normal matrix values pointed by current matrix index (%d) to be loaded, however it may not be loaded yet.",
+ "GX_TEXCOORD%d requires the matrix values pointed by current texture matrix index %d (%d), however it may not be loaded yet.",
+ "GX_LIGHT%d is being referenced, however it may not be loaded yet.",
+ "Channel %d performs lighting which requires the normal matrix values pointed to by the current matrix index (%d), however these values may not be loaded yet.",
+ "Position matrix values pointed to by the current matrix index must be loaded, however they may not be loaded yet.",
+ "Address 0x%04x is uninitialized.",
+ "Register (0x%04x) (%s) is not initialized.",
+ "Display list contains invalid command.",
+ "Nested display list.",
+
+ "GXGetCPUFifo/GXGetGPFifo: GP FIFO read pointer is not idle. Value may be unstable.",
+
+ "XF is not expecting host colors but cp is sending some.",
+ "XF is expecting a host color but cp is not sending one.",
+ "XF is expecting a single host color but cp is sending two.",
+ "XF is expecting two host colors but cp is not sending first color.",
+ "XF is expecting two host colors but cp is not sending second color.",
+ "Invalid number of output colors, %d.",
+ "Regular texture %d specifying a source row of %d which only has 2 elements, but an input form of ABC1.",
+ "Output XF colors or color textures enabled, but register address 0x%04x uninitialized (%s).",
+ "Output XF colors or color textures enabled, COLOR%dCNTRL_REG.material_src == REGISTER, but Material %d register (0x%04x) is not initialized.",
+ "Output XF colors or color textures enabled, COLOR%dCNTRL_REG.ambient_src == REGISTER, but Ambient %d register (0x%04x) is not initialized."
+};
+
+GXWarningLevel __gxvWarnLev[126] = {
+ GX_WARN_SEVERE,
+ GX_WARN_SEVERE,
+ GX_WARN_SEVERE,
+ GX_WARN_SEVERE,
+ GX_WARN_SEVERE,
+ GX_WARN_SEVERE,
+ 4,
+ GX_WARN_SEVERE,
+ GX_WARN_SEVERE,
+ GX_WARN_SEVERE,
+ GX_WARN_SEVERE,
+ GX_WARN_SEVERE,
+ GX_WARN_SEVERE,
+ GX_WARN_SEVERE,
+ GX_WARN_SEVERE,
+ 4,
+ 4,
+ GX_WARN_SEVERE,
+ GX_WARN_ALL,
+ GX_WARN_SEVERE,
+ GX_WARN_SEVERE,
+ GX_WARN_SEVERE,
+ GX_WARN_SEVERE,
+ GX_WARN_SEVERE,
+ GX_WARN_SEVERE,
+ GX_WARN_SEVERE,
+ GX_WARN_SEVERE,
+ GX_WARN_SEVERE,
+ GX_WARN_SEVERE,
+ 4,
+ 4,
+ 4,
+ GX_WARN_SEVERE,
+ GX_WARN_SEVERE,
+ GX_WARN_SEVERE,
+ GX_WARN_SEVERE,
+ GX_WARN_SEVERE,
+ GX_WARN_SEVERE,
+ GX_WARN_ALL,
+ GX_WARN_ALL,
+ 4,
+ GX_WARN_SEVERE,
+ GX_WARN_MEDIUM,
+ GX_WARN_MEDIUM,
+ GX_WARN_ALL,
+ GX_WARN_ALL,
+ GX_WARN_MEDIUM,
+ 4,
+ 4,
+ 4,
+ 4,
+ GX_WARN_ALL,
+ 4,
+ 4,
+ GX_WARN_SEVERE,
+ GX_WARN_SEVERE,
+ GX_WARN_SEVERE,
+ GX_WARN_SEVERE,
+ GX_WARN_SEVERE,
+ GX_WARN_SEVERE,
+ GX_WARN_ALL,
+ GX_WARN_ALL,
+ GX_WARN_ALL,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ GX_WARN_SEVERE,
+ GX_WARN_SEVERE,
+ GX_WARN_SEVERE,
+ GX_WARN_SEVERE,
+ GX_WARN_SEVERE,
+ GX_WARN_SEVERE,
+ GX_WARN_SEVERE,
+ GX_WARN_SEVERE,
+ GX_WARN_SEVERE,
+ 4,
+ GX_WARN_SEVERE,
+ GX_WARN_SEVERE,
+ GX_WARN_SEVERE,
+ GX_WARN_SEVERE,
+ GX_WARN_SEVERE,
+ 4,
+ GX_WARN_MEDIUM,
+ GX_WARN_MEDIUM,
+ GX_WARN_MEDIUM,
+ GX_WARN_MEDIUM,
+ GX_WARN_SEVERE,
+ GX_WARN_SEVERE,
+ GX_WARN_MEDIUM,
+ GX_WARN_MEDIUM,
+ GX_WARN_MEDIUM,
+ GX_WARN_ALL,
+ GX_WARN_ALL,
+ GX_WARN_SEVERE,
+ 4,
+ 4,
+ 4,
+ GX_WARN_SEVERE,
+ GX_WARN_SEVERE,
+ GX_WARN_SEVERE,
+ GX_WARN_SEVERE,
+ GX_WARN_SEVERE,
+ GX_WARN_SEVERE,
+ GX_WARN_MEDIUM,
+ GX_WARN_MEDIUM,
+ GX_WARN_MEDIUM,
+ GX_WARN_MEDIUM,
+ GX_WARN_MEDIUM,
+ GX_WARN_MEDIUM,
+ GX_WARN_MEDIUM,
+ GX_WARN_SEVERE,
+ GX_WARN_SEVERE,
+ GX_WARN_MEDIUM,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+};
+
+char __gxvDummyStr[256];
+
+static void __GXVerifyGlobal(void) {}
+
+static void __GXVerifyCP(GXVtxFmt fmt) {
+ u32 nrmCnt = GET_REG_FIELD(__GXData->vatA[fmt], 1, 9);
+
+ if (__gxVerif->verifyLevel >= GX_WARN_SEVERE) {
+ if (__GXData->hasNrms && nrmCnt != 0) {
+ if (__gxVerif->verifyLevel >= __gxvWarnLev[GXWARN_INVALID_VTX_FMT]) {
+ __GX_WARNF(GXWARN_INVALID_VTX_FMT, "GX_NRM_XYZ");
+ }
+ }
+ else if (__GXData->hasBiNrms && nrmCnt != 1) {
+ if (__gxVerif->verifyLevel >= __gxvWarnLev[GXWARN_INVALID_VTX_FMT]) {
+ __GX_WARNF(GXWARN_INVALID_VTX_FMT, "GX_NRM_NBT or GX_NRM_NBT3");
+ }
+ }
+ }
+}
+
+void __GXVerifyState(GXVtxFmt vtxfmt) {
+ if (__gxVerif->verifyLevel != GX_WARN_NONE) {
+ __GXVerifyGlobal();
+ __GXVerifyCP(vtxfmt);
+ __GXVerifyXF();
+ __GXVerifySU();
+ __GXVerifyBUMP();
+ __GXVerifyTEX();
+ __GXVerifyTEV();
+ __GXVerifyPE();
+ }
+}
+
+void __GXVerifyVATImm(GXAttr attr, GXCompCnt cnt, GXCompType type, u8 frac) {
+ if (__gxVerif->verifyLevel != GX_WARN_NONE) {
+ if (attr == GX_VA_CLR0 || attr == GX_VA_CLR1) {
+ switch (type) {
+ case GX_RGB565:
+ case GX_RGB8:
+ case GX_RGBX8:
+ if (cnt != GX_CLR_RGB && __gxVerif->verifyLevel >= __gxvWarnLev[GXWARN_VAT_MISMATCH]) {
+ __GX_WARNF(GXWARN_VAT_MISMATCH, "RGB format type", "GX_CLR_RGB");
+ }
+ break;
+ case GX_RGBA4:
+ case GX_RGBA6:
+ case GX_RGBA8:
+ if (cnt != GX_CLR_RGBA && __gxVerif->verifyLevel >= __gxvWarnLev[GXWARN_VAT_MISMATCH]) {
+ __GX_WARNF(GXWARN_VAT_MISMATCH, "RGBA format type", "GX_CLR_RGBA");
+ }
+ break;
+ }
+ }
+
+ if (frac != 0) {
+ if (attr == GX_VA_CLR0 || attr == GX_VA_CLR1) {
+ if (__gxVerif->verifyLevel >= __gxvWarnLev[GXWARN_VAT_CLR_FRAC]) {
+ __GX_WARN(GXWARN_VAT_CLR_FRAC);
+ }
+ } else if (type == GX_F32) {
+ if (__gxVerif->verifyLevel >= __gxvWarnLev[GXWARN_VAT_F32_FRAC]) {
+ __GX_WARN(GXWARN_VAT_F32_FRAC);
+ }
+ }
+ }
+
+ if (attr == GX_VA_NRM || attr == GX_VA_NBT) {
+ switch (type) {
+ case GX_S8:
+ if (frac != 6 && __gxVerif->verifyLevel >= __gxvWarnLev[GXWARN_VAT_NRM_FRAC]) {
+ __GX_WARNF(GXWARN_VAT_NRM_FRAC, "GX_S8", 6);
+ }
+ break;
+ case GX_S16:
+ if (frac != 14 && __gxVerif->verifyLevel >= __gxvWarnLev[GXWARN_VAT_NRM_FRAC]) {
+ __GX_WARNF(GXWARN_VAT_NRM_FRAC, "GX_S16", 14);
+ }
+ break;
+ case GX_F32:
+ break;
+ default:
+ if (__gxVerif->verifyLevel >= __gxvWarnLev[GXWARN_VAT_NRM_TYPE]) {
+ __GX_WARN(GXWARN_VAT_NRM_TYPE);
+ }
+ break;
+ }
+ }
+ }
+}
+
+void GXSetVerifyLevel(GXWarningLevel level) {
+ __gxVerif->verifyLevel = level;
+}
+
+GXVerifyCallback GXSetVerifyCallback(GXVerifyCallback cb) {
+ GXVerifyCallback old_cb = __gxVerif->cb;
+
+ __gxVerif->cb = cb;
+ return old_cb;
+}
+
+#endif // DEBUG
diff --git a/src/revolution/gx/GXVert.c b/src/revolution/gx/GXVert.c
new file mode 100644
index 0000000000..d1db47c48a
--- /dev/null
+++ b/src/revolution/gx/GXVert.c
@@ -0,0 +1,86 @@
+#if DEBUG
+#include <revolution/gx.h>
+
+#include "__gx.h"
+
+#define FUNC_1PARAM(name, T) \
+void name##1##T(T x) { GXWGFifo.T = x; }
+
+#define FUNC_2PARAM(name, T) \
+void name##2##T(T x, T y) { GXWGFifo.T = x; GXWGFifo.T = y; }
+
+#define FUNC_3PARAM(name, T) \
+void name##3##T(T x, T y, T z) { GXWGFifo.T = x; GXWGFifo.T = y; GXWGFifo.T = z; }
+
+#define FUNC_4PARAM(name, T) \
+void name##4##T(T x, T y, T z, T w) { GXWGFifo.T = x; GXWGFifo.T = y; GXWGFifo.T = z; GXWGFifo.T = w; }
+
+#define FUNC_INDEX8(name) \
+void name##1x8(u8 x) { GXWGFifo.u8 = x; }
+
+#define FUNC_INDEX16(name) \
+void name##1x16(u16 x) { GXWGFifo.u16 = x; }
+
+// GXCmd
+FUNC_1PARAM(GXCmd, u8)
+FUNC_1PARAM(GXCmd, u16)
+FUNC_1PARAM(GXCmd, u32)
+
+// GXParam
+FUNC_1PARAM(GXParam, u8)
+FUNC_1PARAM(GXParam, u16)
+FUNC_1PARAM(GXParam, u32)
+FUNC_1PARAM(GXParam, s8)
+FUNC_1PARAM(GXParam, s16)
+FUNC_1PARAM(GXParam, s32)
+FUNC_1PARAM(GXParam, f32)
+FUNC_3PARAM(GXParam, f32)
+FUNC_4PARAM(GXParam, f32)
+
+// GXPosition
+FUNC_3PARAM(GXPosition, f32)
+FUNC_3PARAM(GXPosition, u8)
+FUNC_3PARAM(GXPosition, s8)
+FUNC_3PARAM(GXPosition, u16)
+FUNC_3PARAM(GXPosition, s16)
+FUNC_2PARAM(GXPosition, f32)
+FUNC_2PARAM(GXPosition, u8)
+FUNC_2PARAM(GXPosition, s8)
+FUNC_2PARAM(GXPosition, u16)
+FUNC_2PARAM(GXPosition, s16)
+FUNC_INDEX16(GXPosition)
+FUNC_INDEX8(GXPosition)
+
+// GXNormal
+FUNC_3PARAM(GXNormal, f32)
+FUNC_3PARAM(GXNormal, s16)
+FUNC_3PARAM(GXNormal, s8)
+FUNC_INDEX16(GXNormal)
+FUNC_INDEX8(GXNormal)
+
+// GXColor
+FUNC_4PARAM(GXColor, u8)
+FUNC_1PARAM(GXColor, u32)
+FUNC_3PARAM(GXColor, u8)
+FUNC_1PARAM(GXColor, u16)
+FUNC_INDEX16(GXColor)
+FUNC_INDEX8(GXColor)
+
+// GXTexCoord
+FUNC_2PARAM(GXTexCoord, f32)
+FUNC_2PARAM(GXTexCoord, s16)
+FUNC_2PARAM(GXTexCoord, u16)
+FUNC_2PARAM(GXTexCoord, s8)
+FUNC_2PARAM(GXTexCoord, u8)
+FUNC_1PARAM(GXTexCoord, f32)
+FUNC_1PARAM(GXTexCoord, s16)
+FUNC_1PARAM(GXTexCoord, u16)
+FUNC_1PARAM(GXTexCoord, s8)
+FUNC_1PARAM(GXTexCoord, u8)
+FUNC_INDEX16(GXTexCoord)
+FUNC_INDEX8(GXTexCoord)
+
+// GXMatrixIndex
+FUNC_1PARAM(GXMatrixIndex, u8)
+
+#endif // DEBUG
diff --git a/src/revolution/gx/__gx.h b/src/revolution/gx/__gx.h
new file mode 100644
index 0000000000..eb8561b05e
--- /dev/null
+++ b/src/revolution/gx/__gx.h
@@ -0,0 +1,573 @@
+#ifndef _REVOLUTION_GX_INTERNAL_H_
+#define _REVOLUTION_GX_INTERNAL_H_
+
+#include <revolution/gx.h>
+#include <revolution/private/GXRegs.h>
+
+#include "global.h" // IWYU pragma: export
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define PHY_ADDR_MASK ~((~0x3FFF) << 16)
+#define GX_PHY_ADDR(a) ((u32)a & PHY_ADDR_MASK)
+
+// REG VERIF
+
+#if DEBUG
+#define VERIF_XF_REG(addr, value) \
+do { \
+ s32 regAddr = (addr - 0x1000); \
+ if (regAddr >= 0 && regAddr < 0x50) { \
+ __gxVerif->xfRegs[regAddr] = (value); \
+ __gxVerif->xfRegsDirty[regAddr] = 1; \
+ } \
+} while (0)
+
+#define VERIF_XF_REG_alt(addr, value) \
+do { \
+ s32 xfAddr = (addr); \
+ if (xfAddr >= 0 && xfAddr < 0x50) { \
+ __gxVerif->xfRegs[xfAddr] = (value); \
+ __gxVerif->xfRegsDirty[xfAddr] = 1; \
+ } \
+} while (0)
+
+#define VERIF_RAS_REG(value) (__gxVerif->rasRegs[((value) & 0xFF000000) >> 24] = value)
+
+#define VERIF_MTXLIGHT(addr, data) \
+do { \
+ s32 xfAddr; \
+ if (addr < 0x400U) { \
+ __gxVerif->xfMtx[addr] = data; \
+ __gxVerif->xfMtxDirty[addr] = 1; \
+ } else if (addr < 0x500U) { \
+ xfAddr = addr - 0x400; \
+ __gxVerif->xfNrm[xfAddr] = data; \
+ __gxVerif->xfNrmDirty[xfAddr] = 1; \
+ } else if (addr < 0x600U) { \
+ xfAddr = addr - 0x500; \
+ __gxVerif->xfDMtx[xfAddr] = data; \
+ __gxVerif->xfDMtxDirty[xfAddr] = 1; \
+ } else if (addr < 0x680U) { \
+ xfAddr = addr - 0x600; \
+ __gxVerif->xfLight[xfAddr] = data; \
+ __gxVerif->xfLightDirty[xfAddr] = 1; \
+ } else { \
+ xfAddr = addr - 0x1000; \
+ if ((xfAddr >= 0) && (xfAddr < 0x50)) { \
+ __gxVerif->xfRegs[xfAddr] = data; \
+ __gxVerif->xfRegsDirty[xfAddr] = 1; \
+ } \
+ } \
+} while (0)
+#else
+#define VERIF_XF_REG(addr, value) ((void)0)
+#define VERIF_XF_REG_alt(addr, value) ((void)0)
+#define VERIF_RAS_REG(value) ((void)0)
+#endif
+
+// WRITE REG
+
+#if DEBUG
+#define GX_WRITE_XF_REG_2(addr, value) \
+do { \
+ u32 xfData = (value); &xfData; \
+ GX_WRITE_U32(value); \
+ VERIF_XF_REG_alt(addr, xfData); \
+} while (0)
+
+#define GX_WRITE_XF_REG_F(addr, value) \
+do { \
+ f32 xfData = (value); \
+ GX_WRITE_F32(value); \
+ VERIF_XF_REG_alt(addr, *(u32 *)&xfData); \
+} while (0)
+#else
+#define GX_WRITE_XF_REG_2(addr, value) \
+do { \
+ GX_WRITE_U32(value); \
+} while (0)
+
+#define GX_WRITE_XF_REG_F(addr, value) \
+do { \
+ GX_WRITE_F32(value); \
+} while (0)
+#endif
+
+#define GX_WRITE_RAS_REG(value) \
+do { \
+ GX_WRITE_U8(0x61); \
+ GX_WRITE_U32(value); \
+ VERIF_RAS_REG(value); \
+} while (0)
+
+#if DEBUG
+#define GX_WRITE_SOME_REG2(a, b, c, addr) \
+do { \
+ long regAddr; \
+ GX_WRITE_U8(a); \
+ GX_WRITE_U8(b); \
+ GX_WRITE_U32(c); \
+ regAddr = addr; \
+ if (regAddr >= 0 && regAddr < 4) { \
+ __GXData->indexBase[regAddr] = c; \
+ } \
+} while (0)
+#else
+#define GX_WRITE_SOME_REG2(a, b, c, addr) \
+do { \
+ GX_WRITE_U8(a); \
+ GX_WRITE_U8(b); \
+ GX_WRITE_U32(c); \
+} while (0)
+#endif
+
+#if DEBUG
+#define GX_WRITE_SOME_REG3(a, b, c, addr) \
+do { \
+ long regAddr; \
+ GX_WRITE_U8(a); \
+ GX_WRITE_U8(b); \
+ GX_WRITE_U32(c); \
+ regAddr = addr; \
+ if (regAddr >= 0 && regAddr < 4) { \
+ __GXData->indexStride[regAddr] = c; \
+ } \
+} while (0)
+#else
+#define GX_WRITE_SOME_REG3(a, b, c, addr) \
+do { \
+ GX_WRITE_U8(a); \
+ GX_WRITE_U8(b); \
+ GX_WRITE_U32(c); \
+} while (0)
+#endif
+
+#define GX_WRITE_SOME_REG4(a, b, c, addr) \
+do { \
+ long regAddr; \
+ GX_WRITE_U8(a); \
+ GX_WRITE_U8(b); \
+ GX_WRITE_U32(c); \
+ regAddr = addr; \
+} while (0)
+
+// REG MACROS
+#define GET_REG_FIELD(reg, size, shift) ((((unsigned long)(reg)) & (((1 << (size)) - 1) << (shift))) >> (shift))
+
+// TODO: reconcile reg macro differences
+// this one is needed to match non GX libs
+#define OLD_SET_REG_FIELD(line, reg, size, shift, val) \
+do { \
+ ASSERTMSGLINE(line, ((u32)(val) & ~((1 << (size)) - 1)) == 0, "GX Internal: Register field out of range"); \
+ (reg) = ((u32)(reg) & ~(((1 << (size)) - 1) << (shift))) | ((u32)(val) << (shift)); \
+} while (0)
+
+// above doesn't seem to work with GX, only can get it to work with this
+#define SET_REG_FIELD(line, reg, size, shift, val) \
+do { \
+ ASSERTMSGLINE(line, ((u32)(val) & ~((1 << (size)) - 1)) == 0, "GX Internal: Register field out of range"); \
+ (reg) = ((u32)__rlwimi((u32)(reg), (val), (shift), 32 - (shift) - (size), 31 - (shift))); \
+} while (0)
+
+#define CHECK_GXBEGIN(line, name) ASSERTMSGLINE(line, !__GXinBegin, "'" name "' is not allowed between GXBegin/GXEnd")
+
+/* GXAttr */
+void __GXSetVCD(void);
+void __GXSetVAT(void);
+
+/* GXBump */
+void __GXUpdateBPMask(void);
+void __GXFlushTextureState(void);
+
+/* GXFifo */
+// GXFifoObj private data
+typedef struct __GXFifoObj {
+ u8* base;
+ u8* top;
+ u32 size;
+ u32 hiWatermark;
+ u32 loWatermark;
+ void* rdPtr;
+ void* wrPtr;
+ s32 count;
+ u8 wrap;
+ u8 bind_cpu;
+ u8 bind_gp;
+} __GXFifoObj;
+
+void __GXFifoInit(void);
+void __GXCleanGPFifo(void);
+GXBool __GXIsGPFifoReady(void);
+
+/* GXGeometry */
+void __GXSetDirtyState(void);
+void __GXSendFlushPrim(void);
+void __GXSetGenMode(void);
+
+/* GXInit */
+void __GXInitGX();
+void __GXInitRevisionBits(void);
+
+typedef struct __GXData_struct {
+ u16 vNumNot;
+ u16 bpSentNot;
+ u16 vNum;
+ u16 vLim;
+ u32 cpEnable;
+ u32 cpStatus;
+ u32 cpClr;
+ u32 vcdLo;
+ u32 vcdHi;
+ u32 vatA[8];
+ u32 vatB[8];
+ u32 vatC[8];
+ u32 lpSize;
+ u32 matIdxA;
+ u32 matIdxB;
+ u32 indexBase[4];
+ u32 indexStride[4];
+ u32 ambColor[2];
+ u32 matColor[2];
+ u32 chanCtrl[4];
+ u32 texGenCtrl[8];
+ u32 texGenCtrl2[8];
+ u32 suTs0[8];
+ u32 suTs1[8];
+ u32 suScis0;
+ u32 suScis1;
+ u32 tref[8];
+ u32 iref;
+ u32 bpMask;
+ u32 IndTexScale0;
+ u32 IndTexScale1;
+ u32 tevc[16];
+ u32 teva[16];
+ u32 tevKsel[8];
+ u32 cmode0;
+ u32 cmode1;
+ u32 zmode;
+ u32 peCtrl;
+ u32 cpDispSrc;
+ u32 cpDispSize;
+ u32 cpDispStride;
+ u32 cpDisp;
+ u32 cpTexSrc;
+ u32 cpTexSize;
+ u32 cpTexStride;
+ u32 cpTex;
+ u8 cpTexZ;
+ u32 genMode;
+ GXTexRegion TexRegions0[8];
+ GXTexRegion TexRegions1[8];
+ GXTexRegion TexRegions2[8];
+ GXTlutRegion TlutRegions[20];
+ GXTexRegion* (*texRegionCallback)(GXTexObj*, GXTexMapID);
+ GXTlutRegion* (*tlutRegionCallback)(u32);
+ GXAttrType nrmType;
+ u8 hasNrms;
+ u8 hasBiNrms;
+ u32 projType;
+ f32 projMtx[6];
+ f32 vpLeft;
+ f32 vpTop;
+ f32 vpWd;
+ f32 vpHt;
+ f32 vpNearz;
+ f32 vpFarz;
+ f32 zOffset;
+ f32 zScale;
+ u32 tImage0[8];
+ u32 tMode0[8];
+ u32 texmapId[16];
+ u32 tcsManEnab;
+ u32 tevTcEnab;
+ GXPerf0 perf0;
+ GXPerf1 perf1;
+ u32 perfSel;
+ u8 inDispList;
+ u8 dlSaveContext;
+ u8 abtWaitPECopy;
+ u8 dirtyVAT;
+ u32 dirtyState;
+} GXData;
+
+extern GXData* const __GXData;
+
+#if DEBUG
+extern GXBool __GXinBegin;
+#endif
+
+#define GX_GET_MEM_REG(offset) (*(volatile u16*)((volatile u16*)(__memReg) + (offset)))
+#define GX_GET_CP_REG(offset) (*(volatile u16*)((volatile u16*)(__cpReg) + (offset)))
+#define GX_GET_PE_REG(offset) (*(volatile u16*)((volatile u16*)(__peReg) + (offset)))
+#define GX_GET_PI_REG(offset) (*(volatile u32*)((volatile u32*)(__piReg) + (offset)))
+
+#define GX_SET_MEM_REG(offset, val) (*(volatile u16*)((volatile u16*)(__memReg) + (offset)) = val)
+#define GX_SET_CP_REG(offset, val) (*(volatile u16*)((volatile u16*)(__cpReg) + (offset)) = val)
+#define GX_SET_PE_REG(offset, val) (*(volatile u16*)((volatile u16*)(__peReg) + (offset)) = val)
+#define GX_SET_PI_REG(offset, val) (*(volatile u32*)((volatile u32*)(__piReg) + (offset)) = val)
+
+/* GXMisc */
+void __GXBypass(u32 reg);
+u16 __GXReadPEReg(u32 reg);
+void __GXPEInit(void);
+void __GXAbort();
+
+/* GXPerf */
+void __GXSetBWDials(u16 cpDial, u16 tcDial, u16 peDial, u16 cpuRdDial, u16 cpuWrDial);
+
+static inline u32 __GXReadCPCounterU32(u32 regAddrL, u32 regAddrH) {
+ u32 ctrH0;
+ u32 ctrH1;
+ u32 ctrL;
+
+ ctrH0 = GX_GET_CP_REG(regAddrH);
+
+ do {
+ ctrH1 = ctrH0;
+ ctrL = GX_GET_CP_REG(regAddrL);
+ ctrH0 = GX_GET_CP_REG(regAddrH);
+ } while (ctrH0 != ctrH1);
+
+ return (ctrH0 << 0x10) | ctrL;
+}
+
+static inline u32 __GXReadMEMCounterU32(u32 regAddrL, u32 regAddrH) {
+ u32 ctrH0;
+ u32 ctrH1;
+ u32 ctrL;
+
+ ctrH0 = GX_GET_MEM_REG(regAddrH);
+
+ do {
+ ctrH1 = ctrH0;
+ ctrL = GX_GET_MEM_REG(regAddrL);
+ ctrH0 = GX_GET_MEM_REG(regAddrH);
+ } while (ctrH0 != ctrH1);
+
+ return (ctrH0 << 0x10) | ctrL;
+}
+
+static inline u32 __GXReadPECounterU32(u32 regAddrL, u32 regAddrH) {
+ u32 ctrH0;
+ u32 ctrH1;
+ u32 ctrL;
+
+ ctrH0 = GX_GET_PE_REG(regAddrH);
+
+ do {
+ ctrH1 = ctrH0;
+ ctrL = GX_GET_PE_REG(regAddrL);
+ ctrH0 = GX_GET_PE_REG(regAddrH);
+ } while (ctrH0 != ctrH1);
+
+ return (ctrH0 << 0x10) | ctrL;
+}
+
+/* GXSave */
+void __GXShadowDispList(void* list, u32 nbytes);
+void __GXShadowIndexState(u32 idx_reg, u32 reg_data);
+void __GXPrintShadowState(void);
+
+/* GXStubs */
+void __GXSetRange(f32 nearz, f32 fgSideX);
+
+/* GXTexture */
+void __GetImageTileCount(GXTexFmt fmt, u16 wd, u16 ht, u32* rowTiles, u32* colTiles, u32* cmpTiles);
+void __GXSetSUTexRegs(void);
+void __GXGetSUTexSize(GXTexCoordID coord, u16* width, u16* height);
+void __GXSetTmemConfig(u32 config);
+
+/* GXTransform */
+void __GXSetMatrixIndex(GXAttr matIdxAttr);
+void __GXSetProjection(void);
+void __GXSetViewport();
+
+
+/* GXVerifRAS */
+void __GXVerifySU(void);
+void __GXVerifyBUMP(void);
+void __GXVerifyTEX(void);
+void __GXVerifyTEV(void);
+void __GXVerifyPE(void);
+
+/* GXVerif */
+typedef enum {
+ GXWARN_INVALID_VTX_FMT = 0,
+ GXWARN_TEX_SIZE_INIT = 1,
+ GXWARN_SCISSOR_RECT_LEFT = 2,
+ GXWARN_SCISSOR_RECT_TOP = 3,
+ GXWARN_SCISSOR_RECT_RIGHT = 4,
+ GXWARN_SCISSOR_RECT_BOT = 5,
+ GXWARN_SAMPLE_VALUE = 6,
+ GXWARN_BUMP_CMD = 7,
+ GXWARN_INVALID_INDIRECT = 8,
+ GXWARN_INDIRECT_MTX = 9,
+ GXWARN_IND_TEX_NO_INIT = 10,
+ GXWARN_IND_TEX_NO_SCALE = 11,
+ GXWARN_IND_TEX_BUMP = 12,
+ GXWARN_BUMP_ACCUMULATION = 13,
+ GXWARN_BUMP_ALPHA_EN = 14,
+ GXWARN_IND_DIR_MASK = 15,
+ GXWARN_TEV_TEX_REF = 16,
+ GXWARN_TEV_INV_TEX_COORD = 17,
+ GXWARN_IND_DIR_BOTH = 18,
+ GXWARN_TEX_CONFIG = 19,
+ GXWARN_TEX_BASE = 20,
+ GXWARN_TLUT_CONFIG = 21,
+ GXWARN_TEX_POW2 = 22,
+ GXWARN_TEX_CLAMP = 23,
+ GXWARN_TEX_MIN_FILT = 24,
+ GXWARN_MIN_LOD = 25,
+ GXWARN_MAX_LOD = 26,
+ GXWARN_DIAG_LOD = 27,
+ GXWARN_TEX_ANISO = 28,
+ GXWARN_TEX_FIELD = 29,
+ GXWARN_TEX_RND_FP = 30,
+ GXWARN_RND_CLR_INDX = 31,
+ GXWARN_TEV_ENV = 32,
+ GXWARN_TEV_INV_CHAN = 33,
+ GXWARN_TEV_NULL_TEX = 34,
+ GXWARN_TEV_NULL_TEX_A = 35,
+ GXWARN_TEV_DIRTY_REG = 36,
+ GXWARN_TEV_DIRTY_REG_A = 37,
+ GXWARN_TEV_CLR_CLAMP = 38,
+ GXWARN_TEV_A_CLAMP = 39,
+ GXWARN_ZTEX_OFFSET = 40,
+ GXWARN_ZTEX_INVALID = 41,
+ GXWARN_TEV_LAST_CLR = 42,
+ GXWARN_TEV_LAST_A = 43,
+ GXWARN_TEV_LAST_CLR_WRAP = 44,
+ GXWARN_TEV_LAST_A_WRAP = 45,
+ GXWARN_Z_BEFORE_T_A = 46,
+ GXWARN_BLEND_LOGICOP = 47,
+ GXWARN_DITHER_MODE = 48,
+ GXWARN_MULTISAMP0 = 49,
+ GXWARN_MULTISAMP1 = 50,
+ GXWARN_SAMP_ORDER = 51,
+ GXWARN_INVALID_TG_TYPE = 52,
+ GXWARN_XF_CTRL_UNINIT = 53,
+ GXWARN_XF_CTRL_INIT = 54,
+ GXWARN_INV_COLOR_TG_COMB = 55,
+ GXWARN_XF_NO_CLR_TEX = 56,
+ GXWARN_VTX_NO_GEOM = 57,
+ GXWARN_VAT_MISMATCH = 58,
+ GXWARN_VAT_NRM_TYPE = 59,
+ GXWARN_VAT_NRM_FRAC = 60,
+ GXWARN_VAT_F32_FRAC = 61,
+ GXWARN_VAT_CLR_FRAC = 62,
+ GXWARN_INV_IVS_CLR = 63,
+ GXWARN_NRM_XF0_CP1 = 64,
+ GXWARN_NRM_XF0_CP3 = 65,
+ GXWARN_NRM_XF1_CP0 = 66,
+ GXWARN_NRM_XF1_CP3 = 67,
+ GXWARN_NRM_XF3_CP1 = 68,
+ GXWARN_VCD_FMT_UNSUP = 69,
+ GXWARN_VCD_CLR_ORDER = 70,
+ GXWARN_VCD_TEX_ORDER = 71,
+ GXWARN_TEX_SRC_NPOS = 72,
+ GXWARN_TEX_SRC_NNRM = 73,
+ GXWARN_TEX_SRC_NCLR0 = 74,
+ GXWARN_TEX_SRC_NCLR1 = 75,
+ GXWARN_TEX_SRC_NNBT = 76,
+ GXWARN_TEX_SRC_NTEX = 77,
+ GXWARN_INV_TEX_SRC = 78,
+ GXWARN_INV_TG_ORDER = 79,
+ GXWARN_BM_INV_MTX_NDX = 80,
+ GXWARN_BM_INV_TEX = 81,
+ GXWARN_BM_INV_LIT_POS = 82,
+ GXWARN_BM_NO_NBT = 83,
+ GXWARN_INV_TEX_NUM = 84,
+ GXWARN_VIEWPORT_TOP = 85,
+ GXWARN_VIEWPORT_BOTTOM = 86,
+ GXWARN_VIEWPORT_LEFT = 87,
+ GXWARN_VIEWPORT_RIGHT = 88,
+ GXWARN_CLR_INV_SPEC = 89,
+ GXWARN_CLR_NO_NRM = 90,
+ GXWARN_CLR_INV_MTX_NDX = 91,
+ GXWARN_VAL_INFINITY = 92,
+ GXWARN_VAL_NAN = 93,
+ GXWARN_VAL_SMALL = 94,
+ GXWARN_VAL_LARGE = 95,
+ GXWARN_MTX1_UNINIT = 96,
+ GXWARN_GM_UNINIT = 97,
+ GXWARN_TEX_XFN_SUM = 98,
+ GXWARN_CLR_XFN_SUM = 99,
+ GXWARN_INV_NUM_ANY_TEX = 100,
+ GXWARN_INV_NUM_REG_TEX = 101,
+ GXWARN_INV_NUM_BM_TEX = 102,
+ GXWARN_INV_NUM_CLR_TEX = 103,
+ GXWARN_INV_CLR_TEX = 104,
+ GXWARN_DUP_CLR_TEX = 105,
+ GXWARN_BM_INV_MTX_VAL = 106,
+ GXWARN_TEX_INV_MTX_VAL = 107,
+ GXWARN_LIT_INV_REG = 108,
+ GXWARN_CLR_INV_MTX_VAL = 109,
+ GXWARN_INV_MTX_VAL = 110,
+ GXWARN_ADDR_UNINIT = 111,
+ GXWARN_REG_UNINIT = 112,
+ GXWARN_DL_INV_CMD = 113,
+ GXWARN_DL_NESTED = 114,
+ GXWARN_CLR_XF0_CP1 = 115,
+ GXWARN_CLR_XF1_CP0 = 116,
+ GXWARN_CLR_XF1_CP2 = 117,
+ GXWARN_CLR_XF2_CPN1 = 118,
+ GXWARN_CLR_XF2_CPN2 = 119,
+ GXWARN_INV_NUM_COLORS = 120,
+ GXWARN_INV_TG_SRC = 121,
+ GXWARN_CLR_ADDR_UNINIT = 122,
+ GXWARN_CLR_MAT_UNINIT = 123,
+ GXWARN_CLR_AMB_UNINIT = 124,
+ GXWARN_MAX = 125,
+} GXWarnID;
+
+#define __GX_WARN(id) (__gxVerif->cb(__gxvWarnLev[(id)], (id), __gxvWarnings[(id)]))
+#define __GX_WARNF(id, ...) \
+do { \
+ sprintf(__gxvDummyStr, __gxvWarnings[(id)], __VA_ARGS__); \
+ __gxVerif->cb(__gxvWarnLev[(id)], (id), __gxvDummyStr); \
+} while (0)
+
+#define __GX_WARN2(level, id) (__gxVerif->cb(level, (id), __gxvWarnings[(id)]))
+#define __GX_WARN2F(level, id, ...) \
+do { \
+ sprintf(__gxvDummyStr, __gxvWarnings[(id)], __VA_ARGS__); \
+ __gxVerif->cb(level, (id), __gxvDummyStr); \
+} while (0)
+
+typedef struct __GXVerifyData {
+ GXVerifyCallback cb;
+ GXWarningLevel verifyLevel;
+ u32 xfRegs[80];
+ u32 xfMtx[256];
+ u32 xfNrm[96];
+ u32 xfDMtx[256];
+ u32 xfLight[128];
+ u32 rasRegs[256];
+ u8 xfRegsDirty[80];
+ u8 xfMtxDirty[256];
+ u8 xfNrmDirty[96];
+ u8 xfDMtxDirty[256];
+ u8 xfLightDirty[128];
+} __GXVerifyData;
+
+extern __GXVerifyData* __gxVerif;
+extern char* __gxvWarnings[126];
+extern char __gxvDummyStr[256];
+extern GXWarningLevel __gxvWarnLev[];
+
+void __GXVerifyGlobal(void);
+void __GXVerifyCP(GXVtxFmt fmt);
+void __GXVerifyState(GXVtxFmt vtxfmt);
+
+/* GXVerifXF */
+void __GXVerifyXF(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/revolution/hio2/hio2.c b/src/revolution/hio2/hio2.c
new file mode 100644
index 0000000000..d317923650
--- /dev/null
+++ b/src/revolution/hio2/hio2.c
@@ -0,0 +1,273 @@
+#include <revolution/types.h>
+#include <revolution/os.h>
+#include <revolution/exi.h>
+#include <revolution/hio2.h>
+#include <revolution/hw_regs.h>
+
+#ifdef SDK_AUG2010
+#define BUILD_DATE "Aug 23 2010"
+#if DEBUG
+#define BUILD_TIME "17:28:30"
+#else
+#define BUILD_TIME "17:33:06"
+#endif
+#elif SDK_SEP2006
+#define BUILD_DATE "Sep 21 2006"
+#define BUILD_TIME "14:32:13"
+#endif
+
+#ifdef SDK_AUG2010
+#if DEBUG
+const char* __HIO2Version = "<< RVL_SDK - HIO2 \tdebug build: "BUILD_DATE" "BUILD_TIME" (0x4302_145) >>";
+#else
+const char* __HIO2Version = "<< RVL_SDK - HIO2 \trelease build: "BUILD_DATE" "BUILD_TIME" (0x4302_145) >>";
+#endif
+#elif SDK_SEP2006
+const char* __HIO2Version = "<< RVL_SDK - HIO2 \trelease build: "BUILD_DATE" "BUILD_TIME" (0x4200_60422) >>";
+#endif
+
+HIO2Control __HIO2Control[2] = {
+ {HIO2_DEVICE_INVALID, -1, 0, NULL, NULL, NULL, NULL},
+ {HIO2_DEVICE_INVALID, -1, 0, NULL, NULL, NULL, NULL},
+};
+
+static u32 __HIO2ConsoleType;
+static BOOL __HIO2Initialized;
+static int __HIO2LastErrorCode;
+
+static BOOL __HIO2IsInitialized();
+
+static void __HIO2ClearChanInfo(s32 chan) {
+ __HIO2Control[chan].type = HIO2_DEVICE_INVALID;
+ __HIO2Control[chan].chan = -1;
+ __HIO2Control[chan]._0x8 = 0;
+
+ __HIO2Control[chan].exiCallback =
+ __HIO2Control[chan]._0x10 =
+ __HIO2Control[chan]._0x14 =
+ __HIO2Control[chan].disconnectCb = NULL;
+}
+
+static void __HIO2ExtHandler(s32 chan, OSContext* context) {
+ if (__HIO2Control[chan].disconnectCb != NULL) {
+ __HIO2Control[chan].disconnectCb(chan);
+ }
+
+ __HIO2ClearChanInfo(chan);
+ EXISetExiCallback(chan, NULL);
+}
+
+static void __HIO2ExiHandler(s32 chan, OSContext* context) {
+ if (chan == 2) {
+ chan = 0;
+ }
+
+ if (__HIO2Control[chan].exiCallback != NULL) {
+ __HIO2Control[chan].exiCallback(chan);
+ }
+}
+
+BOOL HIO2Init(void) {
+ if (__HIO2Initialized) {
+ HIO2Exit();
+ }
+
+ EXIWait();
+ __HIO2ConsoleType = EXIGetConsoleType();
+
+ if (!(__HIO2ConsoleType & 7)) {
+ __HIO2LastErrorCode = 6;
+ return FALSE;
+ }
+
+ __HIO2LastErrorCode = 0;
+ OSRegisterVersion(__HIO2Version);
+ return __HIO2Initialized = TRUE;
+}
+
+BOOL HIO2EnumDevices(HIO2EnumCallback callback) {
+ s32 chan;
+ u32 id;
+
+ ASSERTLINE(275, callback != NULL);
+
+ if (!__HIO2IsInitialized()) {
+ return FALSE;
+ }
+
+ if (__HIO2Control[0].chan != -1 && __HIO2Control[1].chan != -1) {
+ __HIO2LastErrorCode = 5;
+ return FALSE;
+ }
+
+ for (chan = 0; chan < 2; chan++) {
+ while (EXIProbeEx(chan) == 0) {}
+
+ if (EXIGetID(chan, 0, &id) != 0 && id == 0 && !callback(chan)) {
+ return TRUE;
+ }
+ }
+
+ if (__HIO2ConsoleType & 1) {
+ callback(2);
+ }
+
+ return TRUE;
+}
+
+static BOOL __HIO2IsInitialized(void) {
+ if (!__HIO2Initialized) {
+ __HIO2LastErrorCode = 1;
+ }
+
+ return __HIO2Initialized;
+}
+
+s32 HIO2Open(HIO2DeviceType type, HIO2UnkCallback exiCb, HIO2DisconnectCallback disconnectCb) {
+ s32 chan;
+ s32 device;
+ BOOL enabled;
+ u8 reg;
+
+ if (!__HIO2IsInitialized()) {
+ return -1;
+ }
+
+ switch (type) {
+ case HIO2_DEVICE_UNK_0:
+ case HIO2_DEVICE_UNK_1:
+ chan = type;
+ device = 0;
+ break;
+ case HIO2_DEVICE_UNK_2:
+ if (__HIO2ConsoleType & 1) {
+ chan = 0;
+ device = 1;
+ } else {
+ __HIO2LastErrorCode = 6;
+ return -1;
+ }
+ break;
+ default:
+ __HIO2LastErrorCode = 3;
+ return -1;
+ }
+
+ if (__HIO2Control[chan].chan != -1) {
+ __HIO2LastErrorCode = 4;
+ return -1;
+ }
+
+ if (device == 0) {
+ u32 id = 0;
+ while (EXIProbeEx(chan) == 0) {}
+
+ if (!EXIAttach(chan, __HIO2ExtHandler) || EXIGetID(chan, 0, &id) == 0 || id != 0) {
+ __HIO2LastErrorCode = 7;
+ return -1;
+ }
+ }
+
+ reg = 0xD8;
+ enabled = OSDisableInterrupts();
+
+ if (!EXIWriteReg(chan, device, 0xB4000000, &reg, 1)) {
+ if (device == 0) {
+ EXIDetach(chan);
+ }
+
+ __HIO2LastErrorCode = 8;
+ OSRestoreInterrupts(enabled);
+ return -1;
+ }
+
+ OSRestoreInterrupts(enabled);
+
+ __HIO2Control[chan].type = type;
+ __HIO2Control[chan]._0x8 = device;
+ __HIO2Control[chan].chan = chan;
+ __HIO2Control[chan].exiCallback = exiCb;
+ __HIO2Control[chan]._0x10 = NULL;
+ __HIO2Control[chan]._0x14 = NULL;
+ __HIO2Control[chan].disconnectCb = disconnectCb;
+
+ if (exiCb != NULL) {
+ if (type == HIO2_DEVICE_UNK_2) {
+ EXISetExiCallback(2, __HIO2ExiHandler);
+ } else {
+ EXISetExiCallback(chan, __HIO2ExiHandler);
+ }
+ }
+
+ return chan;
+}
+
+BOOL __HIO2IsValidHandle(s32 handle) {
+ if ((handle == 0 || handle == 1) && __HIO2Control[handle].chan != -1) {
+ return TRUE;
+ }
+
+ __HIO2LastErrorCode = 2;
+ return FALSE;
+}
+
+BOOL HIO2Close(s32 handle) {
+ if (!__HIO2IsInitialized() || !__HIO2IsValidHandle(handle)) {
+ return FALSE;
+ }
+
+ EXISetExiCallback(__HIO2Control[handle].chan, NULL);
+
+ if (__HIO2Control[handle]._0x8 == 0) {
+ EXIDetach(__HIO2Control[handle].chan);
+ }
+
+ __HIO2ClearChanInfo(handle);
+ return TRUE;
+}
+
+BOOL HIO2Read(s32 handle, u32 addr, void* buffer, s32 size) {
+ BOOL rt;
+
+ if (!__HIO2IsInitialized() || !__HIO2IsValidHandle(handle)) {
+ return FALSE;
+ }
+
+ ASSERTLINE(564, (addr & 3) == 0);
+
+ rt = EXIReadRam(handle, __HIO2Control[handle]._0x8, ((addr + 0xD10000) << 6) & 0x3FFFFF00, buffer, size, NULL);
+ if (rt == 0) {
+ __HIO2LastErrorCode = 8;
+ }
+
+ return rt;
+}
+
+BOOL HIO2Write(s32 handle, u32 addr, void* buffer, s32 size) {
+ BOOL rt;
+
+ if (!__HIO2IsInitialized() || !__HIO2IsValidHandle(handle)) {
+ return FALSE;
+ }
+
+ ASSERTLINE(593, (addr & 3) == 0);
+
+ rt = EXIWriteRam(handle, __HIO2Control[handle]._0x8, (((addr + 0xD10000) << 6) & 0x3FFFFF00) | 0x80000000, buffer, size, NULL);
+ if (rt == 0) {
+ __HIO2LastErrorCode = 8;
+ }
+
+ return rt;
+}
+
+void HIO2Exit(void) {
+ s32 handle;
+ for (handle = 0; handle < 2; handle++) {
+ if (__HIO2Control[handle].chan != -1) {
+ HIO2Close(handle);
+ }
+ }
+
+ __HIO2Initialized = FALSE;
+ __HIO2ConsoleType = 0;
+}
diff --git a/src/revolution/mtx/mtx.c b/src/revolution/mtx/mtx.c
new file mode 100644
index 0000000000..5388c29669
--- /dev/null
+++ b/src/revolution/mtx/mtx.c
@@ -0,0 +1,1197 @@
+#include <revolution.h>
+#include <revolution/mtx.h>
+#include <math.h>
+
+static f32 Unit01[2] = {
+ 0.0f,
+ 1.0f
+};
+
+void C_MTXIdentity(Mtx m) {
+ ASSERTMSGLINE(203, m, "MtxIdentity(): NULL Mtx 'm' ");
+ m[0][0] = 1;
+ m[0][1] = 0;
+ m[0][2] = 0;
+ m[0][3] = 0;
+ m[1][0] = 0;
+ m[1][1] = 1;
+ m[1][2] = 0;
+ m[1][3] = 0;
+ m[2][0] = 0;
+ m[2][1] = 0;
+ m[2][2] = 1;
+ m[2][3] = 0;
+}
+
+void PSMTXIdentity(__REGISTER Mtx m) {
+ __REGISTER f32 c_zero = 0.0f;
+ __REGISTER f32 c_one = 1.0f;
+ __REGISTER f32 c_01;
+ __REGISTER f32 c_10;
+
+ asm {
+ psq_st c_zero, 8(m), 0, 0
+ ps_merge01 c_01, c_zero, c_one
+ psq_st c_zero, 24(m), 0, 0
+ ps_merge10 c_10, c_one, c_zero
+ psq_st c_zero, 32(m), 0, 0
+ psq_st c_01, 16(m), 0, 0
+ psq_st c_10, 0(m), 0, 0
+ psq_st c_10, 40(m), 0, 0
+ }
+}
+
+void C_MTXCopy(const Mtx src, Mtx dst) {
+ ASSERTMSGLINE(264, src, "MTXCopy(): NULL MtxPtr 'src' ");
+ ASSERTMSGLINE(265, dst, "MTXCopy(): NULL MtxPtr 'dst' ");
+ if (src != dst) {
+ dst[0][0] = src[0][0];
+ dst[0][1] = src[0][1];
+ dst[0][2] = src[0][2];
+ dst[0][3] = src[0][3];
+ dst[1][0] = src[1][0];
+ dst[1][1] = src[1][1];
+ dst[1][2] = src[1][2];
+ dst[1][3] = src[1][3];
+ dst[2][0] = src[2][0];
+ dst[2][1] = src[2][1];
+ dst[2][2] = src[2][2];
+ dst[2][3] = src[2][3];
+ }
+}
+
+asm void PSMTXCopy(const __REGISTER Mtx src, __REGISTER Mtx dst) {
+ psq_l f0, 0(src), 0, 0
+ psq_st f0, 0(dst), 0, 0
+ psq_l f1, 8(src), 0, 0
+ psq_st f1, 8(dst), 0, 0
+ psq_l f2, 16(src), 0, 0
+ psq_st f2, 16(dst), 0, 0
+ psq_l f3, 24(src), 0, 0
+ psq_st f3, 24(dst), 0, 0
+ psq_l f4, 32(src), 0, 0
+ psq_st f4, 32(dst), 0, 0
+ psq_l f5, 40(src), 0, 0
+ psq_st f5, 40(dst), 0, 0
+}
+
+void C_MTXConcat(const Mtx a, const Mtx b, Mtx ab) {
+ Mtx mTmp;
+ MtxPtr m;
+
+ ASSERTMSGLINE(338, a, "MTXConcat(): NULL MtxPtr 'a' ");
+ ASSERTMSGLINE(339, b, "MTXConcat(): NULL MtxPtr 'b' ");
+ ASSERTMSGLINE(340, ab, "MTXConcat(): NULL MtxPtr 'ab' ");
+
+ if (ab == a || ab == b) {
+ m = mTmp;
+ } else {
+ m = ab;
+ }
+
+ m[0][0] = 0 + a[0][2] * b[2][0] + ((a[0][0] * b[0][0]) + (a[0][1] * b[1][0]));
+ m[0][1] = 0 + a[0][2] * b[2][1] + ((a[0][0] * b[0][1]) + (a[0][1] * b[1][1]));
+ m[0][2] = 0 + a[0][2] * b[2][2] + ((a[0][0] * b[0][2]) + (a[0][1] * b[1][2]));
+ m[0][3] = a[0][3] + (a[0][2] * b[2][3] + (a[0][0] * b[0][3] + (a[0][1] * b[1][3])));
+
+ m[1][0] = 0 + a[1][2] * b[2][0] + ((a[1][0] * b[0][0]) + (a[1][1] * b[1][0]));
+ m[1][1] = 0 + a[1][2] * b[2][1] + ((a[1][0] * b[0][1]) + (a[1][1] * b[1][1]));
+ m[1][2] = 0 + a[1][2] * b[2][2] + ((a[1][0] * b[0][2]) + (a[1][1] * b[1][2]));
+ m[1][3] = a[1][3] + (a[1][2] * b[2][3] + (a[1][0] * b[0][3] + (a[1][1] * b[1][3])));
+
+ m[2][0] = 0 + a[2][2] * b[2][0] + ((a[2][0] * b[0][0]) + (a[2][1] * b[1][0]));
+ m[2][1] = 0 + a[2][2] * b[2][1] + ((a[2][0] * b[0][1]) + (a[2][1] * b[1][1]));
+ m[2][2] = 0 + a[2][2] * b[2][2] + ((a[2][0] * b[0][2]) + (a[2][1] * b[1][2]));
+ m[2][3] = a[2][3] + (a[2][2] * b[2][3] + (a[2][0] * b[0][3] + (a[2][1] * b[1][3])));
+
+ if (m == mTmp) {
+ C_MTXCopy(mTmp, ab);
+ }
+}
+
+asm void PSMTXConcat(const __REGISTER Mtx a, const __REGISTER Mtx b, __REGISTER Mtx ab) {
+ nofralloc
+ stwu r1, -64(r1)
+ psq_l f0, 0(a), 0, 0
+ stfd f14, 8(r1)
+ psq_l f6, 0(b), 0, 0
+ lis r6, Unit01@ha
+ psq_l f7, 8(b), 0, 0
+ stfd f15, 16(r1)
+ addi r6, r6, Unit01@l
+ stfd f31, 40(r1)
+ psq_l f8, 16(b), 0, 0
+ ps_muls0 f12, f6, f0
+ psq_l f2, 16(a), 0, 0
+ ps_muls0 f13, f7, f0
+ psq_l f31, 0(r6), 0, 0
+ ps_muls0 f14, f6, f2
+ psq_l f9, 24(b), 0, 0
+ ps_muls0 f15, f7, f2
+ psq_l f1, 8(a), 0, 0
+ ps_madds1 f12, f8, f0, f12
+ psq_l f3, 24(a), 0, 0
+ ps_madds1 f14, f8, f2, f14
+ psq_l f10, 32(b), 0, 0
+ ps_madds1 f13, f9, f0, f13
+ psq_l f11, 40(b), 0, 0
+ ps_madds1 f15, f9, f2, f15
+ psq_l f4, 32(a), 0, 0
+ psq_l f5, 40(a), 0, 0
+ ps_madds0 f12, f10, f1, f12
+ ps_madds0 f13, f11, f1, f13
+ ps_madds0 f14, f10, f3, f14
+ ps_madds0 f15, f11, f3, f15
+ psq_st f12, 0(ab), 0, 0
+ ps_muls0 f2, f6, f4
+ ps_madds1 f13, f31, f1, f13
+ ps_muls0 f0, f7, f4
+ psq_st f14, 16(ab), 0, 0
+ ps_madds1 f15, f31, f3, f15
+ psq_st f13, 8(ab), 0, 0
+ ps_madds1 f2, f8, f4, f2
+ ps_madds1 f0, f9, f4, f0
+ ps_madds0 f2, f10, f5, f2
+ lfd f14, 8(r1)
+ psq_st f15, 24(ab), 0, 0
+ ps_madds0 f0, f11, f5, f0
+ psq_st f2, 32(ab), 0, 0
+ ps_madds1 f0, f31, f5, f0
+ lfd f15, 16(r1)
+ psq_st f0, 40(ab), 0, 0
+ lfd f31, 40(r1)
+ addi r1, r1, 64
+ blr
+}
+
+void C_MTXConcatArray(const Mtx a, const Mtx* srcBase, Mtx* dstBase, u32 count) {
+ u32 i;
+
+ ASSERTMSGLINE(580, a != 0, "MTXConcatArray(): NULL MtxPtr 'a' ");
+ ASSERTMSGLINE(581, srcBase != 0, "MTXConcatArray(): NULL MtxPtr 'srcBase' ");
+ ASSERTMSGLINE(582, dstBase != 0, "MTXConcatArray(): NULL MtxPtr 'dstBase' ");
+ ASSERTMSGLINE(583, count > 1, "MTXConcatArray(): count must be greater than 1.");
+
+ for (i = 0; i < count; i++) {
+ C_MTXConcat(a, *srcBase, *dstBase);
+ srcBase++;
+ dstBase++;
+ }
+}
+
+#if DEBUG
+#pragma push
+#pragma optimization_level 1
+// This function will not compile at optimization level 0
+#endif
+void PSMTXConcatArray(const __REGISTER Mtx a, const __REGISTER Mtx* srcBase, __REGISTER Mtx* dstBase, __REGISTER u32 count) {
+ __REGISTER f32 va0, va1, va2, va3, va4, va5;
+ __REGISTER f32 vb0, vb1, vb2, vb3, vb4, vb5;
+ __REGISTER f32 vd0, vd1, vd2, vd3, vd4, vd5;
+ __REGISTER f32 u01;
+ __REGISTER f32* u01Ptr = Unit01;
+
+ asm {
+ psq_l va0, 0(a), 0, 0;
+ psq_l va1, 8(a), 0, 0;
+ psq_l va2, 16(a), 0, 0;
+ psq_l va3, 24(a), 0, 0;
+ subi count, count, 1;
+ psq_l va4, 32(a), 0, 0;
+ psq_l va5, 40(a), 0, 0;
+ mtctr count;
+ psq_l u01, 0(u01Ptr), 0, 0;
+ psq_l vb0, 0(srcBase), 0, 0;
+ psq_l vb2, 16(srcBase), 0, 0;
+ ps_muls0 vd0, vb0, va0;
+ ps_muls0 vd2, vb0, va2;
+ ps_muls0 vd4, vb0, va4;
+ psq_l vb4, 32(srcBase), 0, 0;
+ ps_madds1 vd0, vb2, va0, vd0;
+ ps_madds1 vd2, vb2, va2, vd2;
+ ps_madds1 vd4, vb2, va4, vd4;
+ psq_l vb1, 8(srcBase), 0, 0;
+ ps_madds0 vd0, vb4, va1, vd0;
+ ps_madds0 vd2, vb4, va3, vd2;
+ ps_madds0 vd4, vb4, va5, vd4;
+ psq_l vb3, 24(srcBase), 0, 0;
+ psq_st vd0, 0(dstBase), 0, 0;
+ ps_muls0 vd1, vb1, va0;
+ ps_muls0 vd3, vb1, va2;
+ ps_muls0 vd5, vb1, va4;
+ psq_l vb5, 40(srcBase), 0, 0;
+ psq_st vd2, 16(dstBase), 0, 0;
+ ps_madds1 vd1, vb3, va0, vd1;
+ ps_madds1 vd3, vb3, va2, vd3;
+ ps_madds1 vd5, vb3, va4, vd5;
+ _loop:
+ addi srcBase, srcBase, sizeof(Mtx);
+ ps_madds0 vd1, vb5, va1, vd1;
+ ps_madds0 vd3, vb5, va3, vd3;
+ ps_madds0 vd5, vb5, va5, vd5;
+ psq_l vb0, 0(srcBase), 0, 0;
+ psq_st vd4, 32(dstBase), 0, 0;
+ ps_madd vd1, u01, va1, vd1;
+ ps_madd vd3, u01, va3, vd3;
+ ps_madd vd5, u01, va5, vd5;
+ psq_l vb2, 16(srcBase), 0, 0;
+ psq_st vd1, 8(dstBase), 0, 0;
+ ps_muls0 vd0, vb0, va0;
+ ps_muls0 vd2, vb0, va2;
+ ps_muls0 vd4, vb0, va4;
+ psq_l vb4, 32(srcBase), 0, 0;
+ psq_st vd3, 24(dstBase), 0, 0;
+ ps_madds1 vd0, vb2, va0, vd0;
+ ps_madds1 vd2, vb2, va2, vd2;
+ ps_madds1 vd4, vb2, va4, vd4;
+ psq_l vb1, 8(srcBase), 0, 0;
+ psq_st vd5, 40(dstBase), 0, 0;
+ addi dstBase, dstBase, sizeof(Mtx);
+ ps_madds0 vd0, vb4, va1, vd0;
+ ps_madds0 vd2, vb4, va3, vd2;
+ ps_madds0 vd4, vb4, va5, vd4;
+ psq_l vb3, 24(srcBase), 0, 0;
+ psq_st vd0, 0(dstBase), 0, 0;
+ ps_muls0 vd1, vb1, va0;
+ ps_muls0 vd3, vb1, va2;
+ ps_muls0 vd5, vb1, va4;
+ psq_l vb5, 40(srcBase), 0, 0;
+ psq_st vd2, 16(dstBase), 0, 0;
+ ps_madds1 vd1, vb3, va0, vd1;
+ ps_madds1 vd3, vb3, va2, vd3;
+ ps_madds1 vd5, vb3, va4, vd5;
+ bdnz _loop;
+ psq_st vd4, 32(dstBase), 0, 0;
+ ps_madds0 vd1, vb5, va1, vd1;
+ ps_madds0 vd3, vb5, va3, vd3;
+ ps_madds0 vd5, vb5, va5, vd5;
+ ps_madd vd1, u01, va1, vd1;
+ ps_madd vd3, u01, va3, vd3;
+ ps_madd vd5, u01, va5, vd5;
+ psq_st vd1, 8(dstBase), 0, 0;
+ psq_st vd3, 24(dstBase), 0, 0;
+ psq_st vd5, 40(dstBase), 0, 0;
+ }
+}
+#if DEBUG
+#pragma pop
+#endif
+
+void C_MTXTranspose(const Mtx src, Mtx xPose) {
+ Mtx mTmp;
+ MtxPtr m;
+
+ ASSERTMSGLINE(851, src, "MTXTranspose(): NULL MtxPtr 'src' ");
+ ASSERTMSGLINE(852, xPose, "MTXTranspose(): NULL MtxPtr 'xPose' ");
+
+ if (src == xPose) {
+ m = mTmp;
+ } else {
+ m = xPose;
+ }
+
+ m[0][0] = src[0][0];
+ m[0][1] = src[1][0];
+ m[0][2] = src[2][0];
+ m[0][3] = 0;
+ m[1][0] = src[0][1];
+ m[1][1] = src[1][1];
+ m[1][2] = src[2][1];
+ m[1][3] = 0;
+ m[2][0] = src[0][2];
+ m[2][1] = src[1][2];
+ m[2][2] = src[2][2];
+ m[2][3] = 0;
+ if (m == mTmp) {
+ C_MTXCopy(mTmp, xPose);
+ }
+}
+
+void PSMTXTranspose(const __REGISTER Mtx src, __REGISTER Mtx xPose) {
+ __REGISTER f32 c_zero = 0;
+ __REGISTER f32 row0a;
+ __REGISTER f32 row1a;
+ __REGISTER f32 row0b;
+ __REGISTER f32 row1b;
+ __REGISTER f32 trns0;
+ __REGISTER f32 trns1;
+ __REGISTER f32 trns2;
+
+ asm {
+ psq_l row0a, 0(src), 0, 0
+ }
+ xPose[2][3] = c_zero;
+ asm {
+ psq_l row1a, 16(src), 0, 0
+ ps_merge00 trns0, row0a, row1a
+ psq_l row0b, 8(src), 1, 0
+ ps_merge11 trns1, row0a, row1a
+ psq_l row1b, 24(src), 1, 0
+ psq_st trns0, 0(xPose), 0, 0
+ psq_l row0a, 32(src), 0, 0
+ ps_merge00 trns2, row0b, row1b
+ psq_st trns1, 16(xPose), 0, 0
+ ps_merge00 trns0, row0a, c_zero
+ psq_st trns2, 32(xPose), 0, 0
+ ps_merge10 trns1, row0a, c_zero
+ psq_st trns0, 8(xPose), 0, 0
+ }
+ row0b = src[2][2];
+ asm {
+ psq_st trns1, 24(xPose), 0, 0
+ }
+ xPose[2][2] = row0b;
+}
+
+u32 C_MTXInverse(const Mtx src, Mtx inv) {
+ Mtx mTmp;
+ MtxPtr m;
+ f32 det;
+
+ ASSERTMSGLINE(964, src, "MTXInverse(): NULL MtxPtr 'src' ");
+ ASSERTMSGLINE(965, inv, "MTXInverse(): NULL MtxPtr 'inv' ");
+
+ if (src == inv) {
+ m = mTmp;
+ } else {
+ m = inv;
+ }
+ det = ((((src[2][1] * (src[0][2] * src[1][0]))
+ + ((src[2][2] * (src[0][0] * src[1][1]))
+ + (src[2][0] * (src[0][1] * src[1][2]))))
+ - (src[0][2] * (src[2][0] * src[1][1])))
+ - (src[2][2] * (src[1][0] * src[0][1])))
+ - (src[1][2] * (src[0][0] * src[2][1]));
+ if (0 == det) {
+ return 0;
+ }
+ det = 1 / det;
+ m[0][0] = (det * +((src[1][1] * src[2][2]) - (src[2][1] * src[1][2])));
+ m[0][1] = (det * -((src[0][1] * src[2][2]) - (src[2][1] * src[0][2])));
+ m[0][2] = (det * +((src[0][1] * src[1][2]) - (src[1][1] * src[0][2])));
+
+ m[1][0] = (det * -((src[1][0] * src[2][2]) - (src[2][0] * src[1][2])));
+ m[1][1] = (det * +((src[0][0] * src[2][2]) - (src[2][0] * src[0][2])));
+ m[1][2] = (det * -((src[0][0] * src[1][2]) - (src[1][0] * src[0][2])));
+
+ m[2][0] = (det * +((src[1][0] * src[2][1]) - (src[2][0] * src[1][1])));
+ m[2][1] = (det * -((src[0][0] * src[2][1]) - (src[2][0] * src[0][1])));
+ m[2][2] = (det * +((src[0][0] * src[1][1]) - (src[1][0] * src[0][1])));
+
+ m[0][3] = ((-m[0][0] * src[0][3]) - (m[0][1] * src[1][3])) - (m[0][2] * src[2][3]);
+ m[1][3] = ((-m[1][0] * src[0][3]) - (m[1][1] * src[1][3])) - (m[1][2] * src[2][3]);
+ m[2][3] = ((-m[2][0] * src[0][3]) - (m[2][1] * src[1][3])) - (m[2][2] * src[2][3]);
+
+ if (m == mTmp) {
+ C_MTXCopy(mTmp, inv);
+ }
+ return 1;
+}
+
+asm u32 PSMTXInverse(const __REGISTER Mtx src, __REGISTER Mtx inv) {
+ psq_l f0, 0(src), 1, 0
+ psq_l f1, 4(src), 0, 0
+ psq_l f2, 16(src), 1, 0
+ ps_merge10 f6, f1, f0
+ psq_l f3, 20(src), 0, 0
+ psq_l f4, 32(src), 1, 0
+ ps_merge10 f7, f3, f2
+ psq_l f5, 36(src), 0, 0
+ ps_mul f11, f3, f6
+ ps_merge10 f8, f5, f4
+ ps_mul f13, f5, f7
+ ps_msub f11, f1, f7, f11
+ ps_mul f12, f1, f8
+ ps_msub f13, f3, f8, f13
+ ps_mul f10, f3, f4
+ ps_msub f12, f5, f6, f12
+ ps_mul f7, f0, f13
+ ps_mul f9, f0, f5
+ ps_mul f8, f1, f2
+ ps_madd f7, f2, f12, f7
+ ps_sub f6, f6, f6
+ ps_msub f10, f2, f5, f10
+ ps_madd f7, f4, f11, f7
+ ps_msub f9, f1, f4, f9
+ ps_msub f8, f0, f3, f8
+ ps_cmpo0 cr0, f7, f6
+ bne skip_return
+ li r3, 0
+ blr
+skip_return:
+ fres f0, f7
+ ps_add f6, f0, f0
+ ps_mul f5, f7, f0
+ ps_nmsub f0, f0, f5, f6
+ lfs f1, 12(src)
+ ps_muls0 f13, f13, f0
+ lfs f2, 28(src)
+ ps_muls0 f12, f12, f0
+ lfs f3, 44(src)
+ ps_muls0 f11, f11, f0
+ ps_merge00 f5, f13, f12
+ ps_merge11 f4, f13, f12
+ ps_mul f6, f13, f1
+ psq_st f5, 0(inv), 0, 0
+ psq_st f4, 16(inv), 0, 0
+ ps_muls0 f10, f10, f0
+ ps_muls0 f9, f9, f0
+ ps_madd f6, f12, f2, f6
+ psq_st f10, 32(inv), 1, 0
+ ps_muls0 f8, f8, f0
+ ps_nmadd f6, f11, f3, f6
+ psq_st f9, 36(inv), 1, 0
+ ps_mul f7, f10, f1
+ ps_merge00 f5, f11, f6
+ psq_st f8, 40(inv), 1, 0
+ ps_madd f7, f9, f2, f7
+ ps_merge11 f4, f11, f6
+ psq_st f5, 8(inv), 0, 0
+ ps_nmadd f7, f8, f3, f7
+ psq_st f4, 24(inv), 0, 0
+ psq_st f7, 44(inv), 1, 0
+ li r3, 1
+}
+
+u32 C_MTXInvXpose(const Mtx src, Mtx invX) {
+ Mtx mTmp;
+ MtxPtr m;
+ f32 det;
+
+ ASSERTMSGLINE(1185, src, "MTXInvXpose(): NULL MtxPtr 'src' ");
+ ASSERTMSGLINE(1186, invX, "MTXInvXpose(): NULL MtxPtr 'invX' ");
+
+ if (src == invX) {
+ m = mTmp;
+ } else {
+ m = invX;
+ }
+ det = ((((src[2][1] * (src[0][2] * src[1][0]))
+ + ((src[2][2] * (src[0][0] * src[1][1]))
+ + (src[2][0] * (src[0][1] * src[1][2]))))
+ - (src[0][2] * (src[2][0] * src[1][1])))
+ - (src[2][2] * (src[1][0] * src[0][1])))
+ - (src[1][2] * (src[0][0] * src[2][1]));
+ if (0 == det) {
+ return 0;
+ }
+ det = 1 / det;
+ m[0][0] = (det * +((src[1][1] * src[2][2]) - (src[2][1] * src[1][2])));
+ m[0][1] = (det * -((src[1][0] * src[2][2]) - (src[2][0] * src[1][2])));
+ m[0][2] = (det * +((src[1][0] * src[2][1]) - (src[2][0] * src[1][1])));
+
+ m[1][0] = (det * -((src[0][1] * src[2][2]) - (src[2][1] * src[0][2])));
+ m[1][1] = (det * +((src[0][0] * src[2][2]) - (src[2][0] * src[0][2])));
+ m[1][2] = (det * -((src[0][0] * src[2][1]) - (src[2][0] * src[0][1])));
+
+ m[2][0] = (det * +((src[0][1] * src[1][2]) - (src[1][1] * src[0][2])));
+ m[2][1] = (det * -((src[0][0] * src[1][2]) - (src[1][0] * src[0][2])));
+ m[2][2] = (det * +((src[0][0] * src[1][1]) - (src[1][0] * src[0][1])));
+
+ m[0][3] = 0;
+ m[1][3] = 0;
+ m[2][3] = 0;
+
+ if (m == mTmp) {
+ C_MTXCopy(mTmp, invX);
+ }
+ return 1;
+}
+
+asm u32 PSMTXInvXpose(const __REGISTER Mtx src, __REGISTER Mtx invX) {
+ psq_l f0, 0(src), 1, 0
+ psq_l f1, 4(src), 0, 0
+ psq_l f2, 16(src), 1, 0
+ ps_merge10 f6, f1, f0
+ psq_l f3, 20(src), 0, 0
+ psq_l f4, 32(src), 1, 0
+ ps_merge10 f7, f3, f2
+ psq_l f5, 36(src), 0, 0
+ ps_mul f11, f3, f6
+ ps_merge10 f8, f5, f4
+ ps_mul f13, f5, f7
+ ps_msub f11, f1, f7, f11
+ ps_mul f12, f1, f8
+ ps_msub f13, f3, f8, f13
+ ps_msub f12, f5, f6, f12
+ ps_mul f10, f3, f4
+ ps_mul f9, f0, f5
+ ps_mul f8, f1, f2
+ ps_msub f10, f2, f5, f10
+ ps_msub f9, f1, f4, f9
+ ps_msub f8, f0, f3, f8
+ ps_mul f7, f0, f13
+ ps_sub f1, f1, f1
+ ps_madd f7, f2, f12, f7
+ ps_madd f7, f4, f11, f7
+ ps_cmpo0 cr0, f7, f1
+ bne skip_return
+ li r3, 0
+ blr
+skip_return:
+ fres f0, f7
+ psq_st f1, 12(invX), 1, 0
+ ps_add f6, f0, f0
+ ps_mul f5, f0, f0
+ psq_st f1, 28(invX), 1, 0
+ ps_nmsub f0, f7, f5, f6
+ psq_st f1, 44(invX), 1, 0
+ ps_muls0 f13, f13, f0
+ ps_muls0 f12, f12, f0
+ ps_muls0 f11, f11, f0
+ psq_st f13, 0(invX), 0, 0
+ psq_st f12, 16(invX), 0, 0
+ ps_muls0 f10, f10, f0
+ ps_muls0 f9, f9, f0
+ psq_st f11, 32(invX), 0, 0
+ psq_st f10, 8(invX), 1, 0
+ ps_muls0 f8, f8, f0
+ li r3, 1
+ psq_st f9, 24(invX), 1, 0
+ psq_st f8, 40(invX), 1, 0
+}
+
+void C_MTXRotRad(Mtx m, char axis, f32 rad) {
+ f32 sinA;
+ f32 cosA;
+
+ ASSERTMSGLINE(1446, m, "MTXRotRad(): NULL MtxPtr 'm' ");
+ sinA = sinf(rad);
+ cosA = cosf(rad);
+ C_MTXRotTrig(m, axis, sinA, cosA);
+}
+
+void PSMTXRotRad(Mtx m, char axis, f32 rad) {
+ f32 sinA, cosA;
+ sinA = sinf(rad);
+ cosA = cosf(rad);
+ PSMTXRotTrig(m, axis, sinA, cosA);
+}
+
+void C_MTXRotTrig(Mtx m, char axis, f32 sinA, f32 cosA) {
+ ASSERTMSGLINE(1501, m, "MTXRotTrig(): NULL MtxPtr 'm' ");
+ switch(axis) {
+ case 'x':
+ case 'X':
+ m[0][0] = 1;
+ m[0][1] = 0;
+ m[0][2] = 0;
+ m[0][3] = 0;
+ m[1][0] = 0;
+ m[1][1] = cosA;
+ m[1][2] = -sinA;
+ m[1][3] = 0;
+ m[2][0] = 0;
+ m[2][1] = sinA;
+ m[2][2] = cosA;
+ m[2][3] = 0;
+ break;
+ case 'y':
+ case 'Y':
+ m[0][0] = cosA;
+ m[0][1] = 0;
+ m[0][2] = sinA;
+ m[0][3] = 0;
+ m[1][0] = 0;
+ m[1][1] = 1;
+ m[1][2] = 0;
+ m[1][3] = 0;
+ m[2][0] = -sinA;
+ m[2][1] = 0;
+ m[2][2] = cosA;
+ m[2][3] = 0;
+ break;
+ case 'z':
+ case 'Z':
+ m[0][0] = cosA;
+ m[0][1] = -sinA;
+ m[0][2] = 0;
+ m[0][3] = 0;
+ m[1][0] = sinA;
+ m[1][1] = cosA;
+ m[1][2] = 0;
+ m[1][3] = 0;
+ m[2][0] = 0;
+ m[2][1] = 0;
+ m[2][2] = 1;
+ m[2][3] = 0;
+ break;
+ default:
+ ASSERTMSGLINE(1528, FALSE, "MTXRotTrig(): invalid 'axis' value ");
+ break;
+ }
+}
+
+void PSMTXRotTrig(__REGISTER Mtx m, __REGISTER char axis, __REGISTER f32 sinA, __REGISTER f32 cosA) {
+ __REGISTER f32 fc0, fc1, nsinA;
+ __REGISTER f32 fw0, fw1, fw2, fw3;
+
+ asm {
+ frsp sinA, sinA
+ frsp cosA, cosA
+ }
+
+ fc0 = 0.0f;
+ fc1 = 1.0f;
+
+ asm {
+ ori axis, axis, 0x20
+ ps_neg nsinA, sinA
+ cmplwi axis, 'x'
+ beq _case_x
+ cmplwi axis, 'y'
+ beq _case_y
+ cmplwi axis, 'z'
+ beq _case_z
+ b _end
+
+ _case_x:
+ psq_st fc1, 0(m), 1, 0
+ psq_st fc0, 4(m), 0, 0
+ ps_merge00 fw0, sinA, cosA
+ psq_st fc0, 12(m), 0, 0
+ ps_merge00 fw1, cosA, nsinA
+ psq_st fc0, 28(m), 0, 0
+ psq_st fc0, 44(m), 1, 0
+ psq_st fw0, 36(m), 0, 0
+ psq_st fw1, 20(m), 0, 0
+ b _end;
+
+ _case_y:
+ ps_merge00 fw0, cosA, fc0
+ ps_merge00 fw1, fc0, fc1
+ psq_st fc0, 24(m), 0, 0
+ psq_st fw0, 0(m), 0, 0
+ ps_merge00 fw2, nsinA, fc0
+ ps_merge00 fw3, sinA, fc0
+ psq_st fw0, 40(m), 0, 0;
+ psq_st fw1, 16(m), 0, 0;
+ psq_st fw3, 8(m), 0, 0;
+ psq_st fw2, 32(m), 0, 0;
+ b _end;
+
+ _case_z:
+ psq_st fc0, 8(m), 0, 0
+ ps_merge00 fw0, sinA, cosA
+ ps_merge00 fw2, cosA, nsinA
+ psq_st fc0, 24(m), 0, 0
+ psq_st fc0, 32(m), 0, 0
+ ps_merge00 fw1, fc1, fc0
+ psq_st fw0, 16(m), 0, 0
+ psq_st fw2, 0(m), 0, 0
+ psq_st fw1, 40(m), 0, 0
+
+ _end:
+ }
+}
+
+static void __PSMTXRotAxisRadInternal(__REGISTER Mtx m, const __REGISTER Vec* axis, __REGISTER f32 sT, __REGISTER f32 cT) {
+ __REGISTER f32 tT, fc0;
+ __REGISTER f32 tmp0, tmp1, tmp2, tmp3, tmp4;
+ __REGISTER f32 tmp5, tmp6, tmp7, tmp8, tmp9;
+ tmp9 = 0.5f;
+ tmp8 = 3.0f;
+
+ asm {
+ frsp cT, cT;
+ psq_l tmp0, 0(axis), 0, 0;
+ frsp sT, sT;
+ lfs tmp1, 8(axis);
+ ps_mul tmp2, tmp0, tmp0;
+ fadds tmp7, tmp9, tmp9;
+ ps_madd tmp3, tmp1, tmp1, tmp2;
+ fsubs fc0, tmp9, tmp9;
+ ps_sum0 tmp4, tmp3, tmp1, tmp2;
+ fsubs tT, tmp7, cT;
+ frsqrte tmp5, tmp4;
+ fmuls tmp2, tmp5, tmp5;
+ fmuls tmp3, tmp5, tmp9;
+ fnmsubs tmp2, tmp2, tmp4, tmp8;
+ fmuls tmp5, tmp2, tmp3;
+ ps_merge00 cT, cT, cT;
+ ps_muls0 tmp0, tmp0, tmp5;
+ ps_muls0 tmp1, tmp1, tmp5;
+ ps_muls0 tmp4, tmp0, tT;
+ ps_muls0 tmp9, tmp0, sT;
+ ps_muls0 tmp5, tmp1, tT;
+ ps_muls1 tmp3, tmp4, tmp0;
+ ps_muls0 tmp2, tmp4, tmp0;
+ ps_muls0 tmp4, tmp4, tmp1;
+ fnmsubs tmp6, tmp1, sT, tmp3;
+ fmadds tmp7, tmp1, sT, tmp3;
+ ps_neg tmp0, tmp9;
+ ps_sum0 tmp8, tmp4, fc0, tmp9;
+ ps_sum0 tmp2, tmp2, tmp6, cT;
+ ps_sum1 tmp3, cT, tmp7, tmp3;
+ ps_sum0 tmp6, tmp0, fc0, tmp4;
+ psq_st tmp8, 8(m), 0, 0;
+ ps_sum0 tmp0, tmp4, tmp4, tmp0;
+ psq_st tmp2, 0(m), 0, 0;
+ ps_muls0 tmp5, tmp5, tmp1;
+ psq_st tmp3, 16(m), 0, 0;
+ ps_sum1 tmp4, tmp9, tmp0, tmp4;
+ psq_st tmp6, 24(m), 0, 0;
+ ps_sum0 tmp5, tmp5, fc0, cT;
+ psq_st tmp4, 32(m), 0, 0;
+ psq_st tmp5, 40(m), 0, 0;
+ }
+}
+
+void PSMTXRotAxisRad(Mtx m, const Vec* axis, f32 rad) {
+ f32 sinT, cosT;
+
+ sinT = sinf(rad);
+ cosT = cosf(rad);
+
+ __PSMTXRotAxisRadInternal(m, axis, sinT, cosT);
+}
+
+void C_MTXRotAxisRad(Mtx m, const Vec* axis, f32 rad) {
+ Vec vN;
+ f32 s;
+ f32 c;
+ f32 t;
+ f32 x;
+ f32 y;
+ f32 z;
+ f32 xSq;
+ f32 ySq;
+ f32 zSq;
+
+ ASSERTMSGLINE(1676, m, "MTXRotAxisRad(): NULL MtxPtr 'm' ");
+ ASSERTMSGLINE(1677, axis, "MTXRotAxisRad(): NULL VecPtr 'axis' ");
+
+ s = sinf(rad);
+ c = cosf(rad);
+ t = 1 - c;
+ C_VECNormalize(axis, &vN);
+ x = vN.x;
+ y = vN.y;
+ z = vN.z;
+ xSq = (x * x);
+ ySq = (y * y);
+ zSq = (z * z);
+ m[0][0] = (c + (t * xSq));
+ m[0][1] = (y * (t * x)) - (s * z);
+ m[0][2] = (z * (t * x)) + (s * y);
+ m[0][3] = 0;
+ m[1][0] = ((y * (t * x)) + (s * z));
+ m[1][1] = (c + (t * ySq));
+ m[1][2] = ((z * (t * y)) - (s * x));
+ m[1][3] = 0;
+ m[2][0] = ((z * (t * x)) - (s * y));
+ m[2][1] = ((z * (t * y)) + (s * x));
+ m[2][2] = (c + (t * zSq));
+ m[2][3] = 0;
+}
+
+void C_MTXTrans(Mtx m, f32 xT, f32 yT, f32 zT) {
+ ASSERTMSGLINE(1865, m, "MTXTrans(): NULL MtxPtr 'm' ");
+ m[0][0] = 1;
+ m[0][1] = 0;
+ m[0][2] = 0;
+ m[0][3] = xT;
+ m[1][0] = 0;
+ m[1][1] = 1;
+ m[1][2] = 0;
+ m[1][3] = yT;
+ m[2][0] = 0;
+ m[2][1] = 0;
+ m[2][2] = 1;
+ m[2][3] = zT;
+}
+
+void PSMTXTrans(__REGISTER Mtx m, __REGISTER f32 xT, __REGISTER f32 yT, __REGISTER f32 zT) {
+ __REGISTER f32 c0 = 0.0f;
+ __REGISTER f32 c1 = 1.0f;
+
+ asm {
+ stfs xT, 12(m)
+ stfs yT, 28(m)
+ psq_st c0, 4(m), 0, 0
+ psq_st c0, 32(m), 0, 0
+ stfs c0, 16(m)
+ stfs c1, 20(m)
+ stfs c0, 24(m)
+ stfs c1, 40(m)
+ stfs zT, 44(m)
+ stfs c1, 0(m)
+ }
+}
+
+void C_MTXTransApply(const Mtx src, Mtx dst, f32 xT, f32 yT, f32 zT) {
+ ASSERTMSGLINE(1932, src, "MTXTransApply(): NULL MtxPtr 'src' ");
+ ASSERTMSGLINE(1933, dst, "MTXTransApply(): NULL MtxPtr 'src' "); //! wrong assert string
+
+ if (src != dst) {
+ dst[0][0] = src[0][0];
+ dst[0][1] = src[0][1];
+ dst[0][2] = src[0][2];
+ dst[1][0] = src[1][0];
+ dst[1][1] = src[1][1];
+ dst[1][2] = src[1][2];
+ dst[2][0] = src[2][0];
+ dst[2][1] = src[2][1];
+ dst[2][2] = src[2][2];
+ }
+
+ dst[0][3] = (src[0][3] + xT);
+ dst[1][3] = (src[1][3] + yT);
+ dst[2][3] = (src[2][3] + zT);
+}
+
+asm void PSMTXTransApply(const __REGISTER Mtx src, __REGISTER Mtx dst, __REGISTER f32 xT, __REGISTER f32 yT, __REGISTER f32 zT) {
+ nofralloc
+ psq_l fp4, 0(src), 0, 0
+ frsp xT, xT
+ psq_l fp5, 8(src), 0, 0
+ frsp yT, yT
+ psq_l fp7, 24(src), 0, 0
+ frsp zT, zT
+ psq_l fp8, 40(src), 0, 0
+ psq_st fp4, 0(dst), 0, 0
+ ps_sum1 fp5, xT, fp5, fp5
+ psq_l fp6, 16(src), 0, 0
+ psq_st fp5, 8(dst), 0, 0
+ ps_sum1 fp7, yT, fp7, fp7
+ psq_l fp9, 32(src), 0, 0
+ psq_st fp6, 16(dst), 0, 0
+ ps_sum1 fp8, zT, fp8, fp8
+ psq_st fp7, 24(dst), 0, 0
+ psq_st fp9, 32(dst), 0, 0
+ psq_st fp8, 40(dst), 0, 0
+ blr
+}
+
+void C_MTXScale(Mtx m, f32 xS, f32 yS, f32 zS) {
+ ASSERTMSGLINE(2007, m, "MTXScale(): NULL MtxPtr 'm' ");
+ m[0][0] = xS;
+ m[0][1] = 0;
+ m[0][2] = 0;
+ m[0][3] = 0;
+ m[1][0] = 0;
+ m[1][1] = yS;
+ m[1][2] = 0;
+ m[1][3] = 0;
+ m[2][0] = 0;
+ m[2][1] = 0;
+ m[2][2] = zS;
+ m[2][3] = 0;
+}
+
+void PSMTXScale(__REGISTER Mtx m, __REGISTER f32 xS, __REGISTER f32 yS, __REGISTER f32 zS) {
+ __REGISTER f32 c0 = 0.0f;
+
+ asm {
+ stfs xS, 0(m)
+ psq_st c0, 4(m), 0, 0
+ psq_st c0, 12(m), 0, 0
+ stfs yS, 20(m)
+ psq_st c0, 24(m), 0, 0
+ psq_st c0, 32(m), 0, 0
+ stfs zS, 40(m)
+ stfs c0, 44(m)
+ }
+}
+
+void C_MTXScaleApply(const Mtx src, Mtx dst, f32 xS, f32 yS, f32 zS) {
+ ASSERTMSGLINE(2069, src, "MTXScaleApply(): NULL MtxPtr 'src' ");
+ ASSERTMSGLINE(2070, dst, "MTXScaleApply(): NULL MtxPtr 'dst' ");
+ dst[0][0] = (src[0][0] * xS);
+ dst[0][1] = (src[0][1] * xS);
+ dst[0][2] = (src[0][2] * xS);
+ dst[0][3] = (src[0][3] * xS);
+ dst[1][0] = (src[1][0] * yS);
+ dst[1][1] = (src[1][1] * yS);
+ dst[1][2] = (src[1][2] * yS);
+ dst[1][3] = (src[1][3] * yS);
+ dst[2][0] = (src[2][0] * zS);
+ dst[2][1] = (src[2][1] * zS);
+ dst[2][2] = (src[2][2] * zS);
+ dst[2][3] = (src[2][3] * zS);
+}
+
+asm void PSMTXScaleApply(const __REGISTER Mtx src, __REGISTER Mtx dst, __REGISTER f32 xS, __REGISTER f32 yS, __REGISTER f32 zS) {
+ nofralloc
+ frsp xS, xS
+ psq_l fp4, 0(src), 0, 0
+ frsp yS, yS
+ psq_l fp5, 8(src), 0, 0
+ frsp zS, zS
+ ps_muls0 fp4, fp4, xS
+ psq_l fp6, 16(src), 0, 0
+ ps_muls0 fp5, fp5, xS
+ psq_l fp7, 24(src), 0, 0
+ ps_muls0 fp6, fp6, yS
+ psq_l fp8, 32(src), 0, 0
+ psq_st fp4, 0(dst), 0, 0
+ ps_muls0 fp7, fp7, yS
+ psq_l fp2, 40(src), 0, 0
+ psq_st fp5, 8(dst), 0, 0
+ ps_muls0 fp8, fp8, zS
+ psq_st fp6, 16(dst), 0, 0
+ ps_muls0 fp2, fp2, zS
+ psq_st fp7, 24(dst), 0, 0
+ psq_st fp8, 32(dst), 0, 0
+ psq_st fp2, 40(dst), 0, 0
+ blr
+}
+
+void C_MTXQuat(Mtx m, const Quaternion* q) {
+ f32 s;
+ f32 xs;
+ f32 ys;
+ f32 zs;
+ f32 wx;
+ f32 wy;
+ f32 wz;
+ f32 xx;
+ f32 xy;
+ f32 xz;
+ f32 yy;
+ f32 yz;
+ f32 zz;
+
+ ASSERTMSGLINE(2144, m, "MTXQuat(): NULL MtxPtr 'm' ");
+ ASSERTMSGLINE(2145, q, "MTXQuat(): NULL QuaternionPtr 'q' ");
+ ASSERTMSGLINE(2146, q->x || q->y || q->z || q->w, "MTXQuat(): zero-value quaternion ");
+ s = 2 / ((q->w * q->w) + ((q->z * q->z) + ((q->x * q->x) + (q->y * q->y))));
+ xs = q->x * s;
+ ys = q->y * s;
+ zs = q->z * s;
+ wx = q->w * xs;
+ wy = q->w * ys;
+ wz = q->w * zs;
+ xx = q->x * xs;
+ xy = q->x * ys;
+ xz = q->x * zs;
+ yy = q->y * ys;
+ yz = q->y * zs;
+ zz = q->z * zs;
+ m[0][0] = (1 - (yy + zz));
+ m[0][1] = (xy - wz);
+ m[0][2] = (xz + wy);
+ m[0][3] = 0;
+ m[1][0] = (xy + wz);
+ m[1][1] = (1 - (xx + zz));
+ m[1][2] = (yz - wx);
+ m[1][3] = 0;
+ m[2][0] = (xz - wy);
+ m[2][1] = (yz + wx);
+ m[2][2] = (1 - (xx + yy));
+ m[2][3] = 0;
+}
+
+void PSMTXQuat(__REGISTER Mtx m, const __REGISTER Quaternion* q) {
+ __REGISTER f32 c_zero, c_one, c_two, scale;
+ __REGISTER f32 tmp0, tmp1, tmp2, tmp3, tmp4;
+ __REGISTER f32 tmp5, tmp6, tmp7, tmp8, tmp9;
+ c_one = 1.0f;
+
+ asm {
+ psq_l tmp0, 0(q), 0, 0
+ psq_l tmp1, 8(q), 0, 0
+ fsubs c_zero, c_one, c_one
+ fadds c_two, c_one, c_one
+ ps_mul tmp2, tmp0, tmp0
+ ps_merge10 tmp5, tmp0, tmp0
+ ps_madd tmp4, tmp1, tmp1, tmp2
+ ps_mul tmp3, tmp1, tmp1
+ ps_sum0 scale, tmp4, tmp4, tmp4
+ ps_muls1 tmp7, tmp5, tmp1
+ fres tmp9, scale
+ ps_sum1 tmp4, tmp3, tmp4, tmp2
+ ps_nmsub scale, scale, tmp9, c_two
+ ps_muls1 tmp6, tmp1, tmp1
+ ps_mul scale, tmp9, scale
+ ps_sum0 tmp2, tmp2, tmp2, tmp2
+ fmuls scale, scale, c_two
+ ps_madd tmp8, tmp0, tmp5, tmp6
+ ps_msub tmp6, tmp0, tmp5, tmp6
+ psq_st c_zero, 12(m), 1, 0
+ ps_nmsub tmp2, tmp2, scale, c_one
+ ps_nmsub tmp4, tmp4, scale, c_one
+ psq_st c_zero, 44(m), 1, 0
+ ps_mul tmp8, tmp8, scale
+ ps_mul tmp6, tmp6, scale
+ psq_st tmp2, 40(m), 1, 0
+ ps_madds0 tmp5, tmp0, tmp1, tmp7
+ ps_merge00 tmp1, tmp8, tmp4
+ ps_nmsub tmp7, tmp7, c_two, tmp5
+ ps_merge10 tmp0, tmp4, tmp6
+ psq_st tmp1, 16(m), 0, 0
+ ps_mul tmp5, tmp5, scale
+ ps_mul tmp7, tmp7, scale
+ psq_st tmp0, 0(m), 0, 0
+ psq_st tmp5, 8(m), 1, 0
+ ps_merge10 tmp3, tmp7, c_zero
+ ps_merge01 tmp9, tmp7, tmp5
+ psq_st tmp3, 24(m), 0, 0
+ psq_st tmp9, 32(m), 0, 0
+ }
+}
+
+void C_MTXReflect(Mtx m, const Vec* p, const Vec* n) {
+ f32 vxy;
+ f32 vxz;
+ f32 vyz;
+ f32 pdotn;
+
+ vxy = -2 * n->x * n->y;
+ vxz = -2 * n->x * n->z;
+ vyz = -2 * n->y * n->z;
+ pdotn = 2 * C_VECDotProduct(p, n);
+ m[0][0] = (1 - (2 * n->x * n->x));
+ m[0][1] = vxy;
+ m[0][2] = vxz;
+ m[0][3] = (pdotn * n->x);
+ m[1][0] = vxy;
+ m[1][1] = (1 - (2 * n->y * n->y));
+ m[1][2] = vyz;
+ m[1][3] = (pdotn * n->y);
+ m[2][0] = vxz;
+ m[2][1] = vyz;
+ m[2][2] = (1 - (2 * n->z * n->z));
+ m[2][3] = (pdotn * n->z);
+}
+
+void PSMTXReflect(__REGISTER Mtx m, const __REGISTER Vec* p, const __REGISTER Vec* n) {
+ __REGISTER f32 c_one;
+ __REGISTER f32 vn_xy, vn_z1;
+ __REGISTER f32 n2vn_xy, n2vn_z1;
+ __REGISTER f32 pdotn;
+ __REGISTER f32 tmp0, tmp1, tmp2, tmp3;
+ __REGISTER f32 tmp4, tmp5, tmp6, tmp7;
+
+ c_one = 1.0f;
+
+ asm {
+ psq_l vn_z1, 0x8(n), 1, 0
+ psq_l vn_xy, 0x0(n), 0, 0
+ psq_l tmp0, 0x0(p), 0, 0
+ ps_nmadd n2vn_z1, vn_z1, c_one, vn_z1
+ psq_l tmp1, 0x8(p), 1, 0
+ ps_nmadd n2vn_xy, vn_xy, c_one, vn_xy
+ ps_muls0 tmp4, vn_xy, n2vn_z1
+ ps_mul pdotn, n2vn_xy, tmp0
+ ps_muls0 tmp2, vn_xy, n2vn_xy
+ ps_sum0 pdotn, pdotn, pdotn, pdotn
+ ps_muls1 tmp3, vn_xy, n2vn_xy
+ psq_st tmp4, 0x20(m), 0, 0
+ ps_sum0 tmp2, tmp2, tmp2, c_one
+ ps_nmadd pdotn, n2vn_z1, tmp1, pdotn
+ ps_sum1 tmp3, c_one, tmp3, tmp3
+ psq_st tmp2, 0x0(m), 0, 0
+ ps_muls0 tmp5, vn_xy, pdotn
+ ps_merge00 tmp6, n2vn_z1, pdotn
+ psq_st tmp3, 0x10(m), 0, 0
+ ps_merge00 tmp7, tmp4, tmp5
+ ps_muls0 tmp6, tmp6, vn_z1
+ ps_merge11 tmp5, tmp4, tmp5
+ psq_st tmp7, 0x8(m), 0, 0
+ ps_sum0 tmp6, tmp6, tmp6, c_one
+ psq_st tmp5, 0x18(m), 0, 0
+ psq_st tmp6, 0x28(m), 0, 0
+ }
+}
+
+void C_MTXLookAt(Mtx m, const Point3d* camPos, const Vec* camUp, const Point3d* target) {
+ Vec vLook;
+ Vec vRight;
+ Vec vUp;
+
+ ASSERTMSGLINE(2437, m, "MTXLookAt(): NULL MtxPtr 'm' ");
+ ASSERTMSGLINE(2438, camPos, "MTXLookAt(): NULL VecPtr 'camPos' ");
+ ASSERTMSGLINE(2439, camUp, "MTXLookAt(): NULL VecPtr 'camUp' ");
+ ASSERTMSGLINE(2440, target, "MTXLookAt(): NULL Point3dPtr 'target' ");
+
+ vLook.x = camPos->x - target->x;
+ vLook.y = camPos->y - target->y;
+ vLook.z = camPos->z - target->z;
+ VECNormalize(&vLook, &vLook);
+ VECCrossProduct(camUp, &vLook, &vRight);
+ VECNormalize(&vRight, &vRight);
+ VECCrossProduct(&vLook, &vRight, &vUp);
+ m[0][0] = vRight.x;
+ m[0][1] = vRight.y;
+ m[0][2] = vRight.z;
+ m[0][3] = -((camPos->z * vRight.z) + ((camPos->x * vRight.x) + (camPos->y * vRight.y)));
+ m[1][0] = vUp.x;
+ m[1][1] = vUp.y;
+ m[1][2] = vUp.z;
+ m[1][3] = -((camPos->z * vUp.z) + ((camPos->x * vUp.x) + (camPos->y * vUp.y)));
+ m[2][0] = vLook.x;
+ m[2][1] = vLook.y;
+ m[2][2] = vLook.z;
+ m[2][3] = -((camPos->z * vLook.z) + ((camPos->x * vLook.x) + (camPos->y * vLook.y)));
+}
+
+void C_MTXLightFrustum(Mtx m, f32 t, f32 b, f32 l, f32 r, f32 n, f32 scaleS, f32 scaleT, f32 transS, f32 transT) {
+ f32 tmp;
+
+ ASSERTMSGLINE(2541, m, "MTXLightFrustum(): NULL MtxPtr 'm' ");
+ ASSERTMSGLINE(2542, (t != b), "MTXLightFrustum(): 't' and 'b' clipping planes are equal ");
+ ASSERTMSGLINE(2543, (l != r), "MTXLightFrustum(): 'l' and 'r' clipping planes are equal ");
+
+ tmp = 1 / (r - l);
+ m[0][0] = (scaleS * (2 * n * tmp));
+ m[0][1] = 0;
+ m[0][2] = (scaleS * (tmp * (r + l))) - transS;
+ m[0][3] = 0;
+ tmp = 1 / (t - b);
+ m[1][0] = 0;
+ m[1][1] = (scaleT * (2 * n * tmp));
+ m[1][2] = (scaleT * (tmp * (t + b))) - transT;
+ m[1][3] = 0;
+ m[2][0] = 0;
+ m[2][1] = 0;
+ m[2][2] = -1;
+ m[2][3] = 0;
+}
+
+void C_MTXLightPerspective(Mtx m, f32 fovY, f32 aspect, f32 scaleS, f32 scaleT, f32 transS, f32 transT) {
+ f32 angle;
+ f32 cot;
+
+ ASSERTMSGLINE(2604, m, "MTXLightPerspective(): NULL MtxPtr 'm' ");
+ ASSERTMSGLINE(2605, (fovY > 0.0) && (fovY < 180.0), "MTXLightPerspective(): 'fovY' out of range ");
+ ASSERTMSGLINE(2606, 0 != aspect, "MTXLightPerspective(): 'aspect' is 0 ");
+
+ angle = (0.5f * fovY);
+ angle = MTXDegToRad(angle);
+ cot = 1 / tanf(angle);
+ m[0][0] = (scaleS * (cot / aspect));
+ m[0][1] = 0;
+ m[0][2] = -transS;
+ m[0][3] = 0;
+ m[1][0] = 0;
+ m[1][1] = (cot * scaleT);
+ m[1][2] = -transT;
+ m[1][3] = 0;
+ m[2][0] = 0;
+ m[2][1] = 0;
+ m[2][2] = -1;
+ m[2][3] = 0;
+}
+
+void C_MTXLightOrtho(Mtx m, f32 t, f32 b, f32 l, f32 r, f32 scaleS, f32 scaleT, f32 transS, f32 transT) {
+ f32 tmp;
+
+ ASSERTMSGLINE(2672, m, "MTXLightOrtho(): NULL MtxPtr 'm' ");
+ ASSERTMSGLINE(2673, (t != b), "MTXLightOrtho(): 't' and 'b' clipping planes are equal ");
+ ASSERTMSGLINE(2674, (l != r), "MTXLightOrtho(): 'l' and 'r' clipping planes are equal ");
+ tmp = 1 / (r - l);
+ m[0][0] = (2 * tmp * scaleS);
+ m[0][1] = 0;
+ m[0][2] = 0;
+ m[0][3] = (transS + (scaleS * (tmp * -(r + l))));
+ tmp = 1/ (t - b);
+ m[1][0] = 0;
+ m[1][1] = (2 * tmp * scaleT);
+ m[1][2] = 0;
+ m[1][3] = (transT + (scaleT * (tmp * -(t + b))));
+ m[2][0] = 0;
+ m[2][1] = 0;
+ m[2][2] = 0;
+ m[2][3] = 1;
+}
diff --git a/src/revolution/mtx/mtx44.c b/src/revolution/mtx/mtx44.c
new file mode 100644
index 0000000000..3cfedb61b3
--- /dev/null
+++ b/src/revolution/mtx/mtx44.c
@@ -0,0 +1,888 @@
+#include <revolution.h>
+#include <revolution/mtx.h>
+#include <math.h>
+
+f32 mtxUnit[] = {0.0f, 1.0f, 0.5f, 3.0f};
+
+void C_MTXFrustum(Mtx44 m, f32 t, f32 b, f32 l, f32 r, f32 n, f32 f) {
+ f32 tmp;
+
+ ASSERTMSGLINE(105, m, "MTXFrustum(): NULL Mtx44Ptr 'm' ");
+ ASSERTMSGLINE(106, t != b, "MTXFrustum(): 't' and 'b' clipping planes are equal ");
+ ASSERTMSGLINE(107, l != r, "MTXFrustum(): 'l' and 'r' clipping planes are equal ");
+ ASSERTMSGLINE(108, n != f, "MTXFrustum(): 'n' and 'f' clipping planes are equal ");
+ tmp = 1 / (r - l);
+ m[0][0] = (2 * n * tmp);
+ m[0][1] = 0;
+ m[0][2] = (tmp * (r + l));
+ m[0][3] = 0;
+ tmp = 1 / (t - b);
+ m[1][0] = 0;
+ m[1][1] = (2 * n * tmp);
+ m[1][2] = (tmp * (t + b));
+ m[1][3] = 0;
+ m[2][0] = 0;
+ m[2][1] = 0;
+ tmp = 1 / (f - n);
+ m[2][2] = (-n * tmp);
+ m[2][3] = (tmp * -(f * n));
+ m[3][0] = 0;
+ m[3][1] = 0;
+ m[3][2] = -1;
+ m[3][3] = 0;
+}
+
+void C_MTXPerspective(Mtx44 m, f32 fovY, f32 aspect, f32 n, f32 f) {
+ f32 angle;
+ f32 cot;
+ f32 tmp;
+
+ ASSERTMSGLINE(184, m, "MTXPerspective(): NULL Mtx44Ptr 'm' ");
+ ASSERTMSGLINE(185, (fovY > 0.0) && (fovY < 180.0), "MTXPerspective(): 'fovY' out of range ");
+ ASSERTMSGLINE(186, 0.0f != aspect, "MTXPerspective(): 'aspect' is 0 ");
+
+ angle = (0.5f * fovY);
+ angle = MTXDegToRad(angle);
+ cot = 1 / tanf(angle);
+ m[0][0] = (cot / aspect);
+ m[0][1] = 0;
+ m[0][2] = 0;
+ m[0][3] = 0;
+ m[1][0] = 0;
+ m[1][1] = (cot);
+ m[1][2] = 0;
+ m[1][3] = 0;
+ m[2][0] = 0;
+ m[2][1] = 0;
+ tmp = 1 / (f - n);
+ m[2][2] = (-n * tmp);
+ m[2][3] = (tmp * -(f * n));
+ m[3][0] = 0;
+ m[3][1] = 0;
+ m[3][2] = -1;
+ m[3][3] = 0;
+}
+
+void C_MTXOrtho(Mtx44 m, f32 t, f32 b, f32 l, f32 r, f32 n, f32 f) {
+ f32 tmp;
+
+ ASSERTMSGLINE(254, m, "MTXOrtho(): NULL Mtx44Ptr 'm' ");
+ ASSERTMSGLINE(255, t != b, "MTXOrtho(): 't' and 'b' clipping planes are equal ");
+ ASSERTMSGLINE(256, l != r, "MTXOrtho(): 'l' and 'r' clipping planes are equal ");
+ ASSERTMSGLINE(257, n != f, "MTXOrtho(): 'n' and 'f' clipping planes are equal ");
+ tmp = 1 / (r - l);
+ m[0][0] = 2 * tmp;
+ m[0][1] = 0;
+ m[0][2] = 0;
+ m[0][3] = (tmp * -(r + l));
+ tmp = 1 / (t - b);
+ m[1][0] = 0;
+ m[1][1] = 2 * tmp;
+ m[1][2] = 0;
+ m[1][3] = (tmp * -(t + b));
+ m[2][0] = 0;
+ m[2][1] = 0;
+ tmp = 1 / (f - n);
+ m[2][2] = (-1 * tmp);
+ m[2][3] = (-f * tmp);
+ m[3][0] = 0;
+ m[3][1] = 0;
+ m[3][2] = 0;
+ m[3][3] = 1;
+}
+
+void C_MTX44Identity(Mtx44 m) {
+ ASSERTMSGLINE(324, m != 0, "MTX44Identity(): NULL Mtx44 'm' ");
+
+ m[0][0] = 1.0f;
+ m[0][1] = 0.0f;
+ m[0][2] = 0.0f;
+ m[0][3] = 0.0f;
+ m[1][0] = 0.0f;
+ m[1][1] = 1.0f;
+ m[1][2] = 0.0f;
+ m[1][3] = 0.0f;
+ m[2][0] = 0.0f;
+ m[2][1] = 0.0f;
+ m[2][2] = 1.0f;
+ m[2][3] = 0.0f;
+ m[3][0] = 0.0f;
+ m[3][1] = 0.0f;
+ m[3][2] = 0.0f;
+ m[3][3] = 1.0f;
+}
+
+void PSMTX44Identity(__REGISTER Mtx44 m) {
+ __REGISTER f32 c1 = 1.0f;
+ __REGISTER f32 c0 = 0.0f;
+
+ asm {
+ stfs c1, 0x0(m)
+ psq_st c0, 0x4(m), 0, 0
+ psq_st c0, 0xc(m), 0, 0
+ stfs c1, 0x14(m)
+ psq_st c0, 0x18(m), 0, 0
+ psq_st c0, 0x20(m), 0, 0
+ stfs c1, 0x28(m)
+ psq_st c0, 0x2c(m), 0, 0
+ psq_st c0, 0x34(m), 0, 0
+ stfs c1, 0x3c(m)
+ }
+}
+
+void C_MTX44Copy(const Mtx44 src, Mtx44 dst) {
+ ASSERTMSGLINE(382, src != 0, "MTX44Copy(): NULL Mtx44Ptr 'src' ");
+ ASSERTMSGLINE(383, dst != 0, "MTX44Copy(): NULL Mtx44Ptr 'dst' ");
+
+ if (src != dst) {
+ dst[0][0] = src[0][0];
+ dst[0][1] = src[0][1];
+ dst[0][2] = src[0][2];
+ dst[0][3] = src[0][3];
+ dst[1][0] = src[1][0];
+ dst[1][1] = src[1][1];
+ dst[1][2] = src[1][2];
+ dst[1][3] = src[1][3];
+ dst[2][0] = src[2][0];
+ dst[2][1] = src[2][1];
+ dst[2][2] = src[2][2];
+ dst[2][3] = src[2][3];
+ dst[3][0] = src[3][0];
+ dst[3][1] = src[3][1];
+ dst[3][2] = src[3][2];
+ dst[3][3] = src[3][3];
+ }
+}
+
+asm void PSMTX44Copy(const __REGISTER Mtx44 src, __REGISTER Mtx44 dst) {
+ nofralloc
+ psq_l f1, 0x0(src), 0, 0
+ psq_st f1, 0x0(dst), 0, 0
+ psq_l f1, 0x8(src), 0, 0
+ psq_st f1, 0x8(dst), 0, 0
+ psq_l f1, 0x10(src), 0, 0
+ psq_st f1, 0x10(dst), 0, 0
+ psq_l f1, 0x18(src), 0, 0
+ psq_st f1, 0x18(dst), 0, 0
+ psq_l f1, 0x20(src), 0, 0
+ psq_st f1, 0x20(dst), 0, 0
+ psq_l f1, 0x28(src), 0, 0
+ psq_st f1, 0x28(dst), 0, 0
+ psq_l f1, 0x30(src), 0, 0
+ psq_st f1, 0x30(dst), 0, 0
+ psq_l f1, 0x38(src), 0, 0
+ psq_st f1, 0x38(dst), 0, 0
+ blr
+}
+
+void C_MTX44Concat(const Mtx44 a, const Mtx44 b, Mtx44 ab) {
+ Mtx44 mTmp;
+ Mtx44Ptr m;
+
+ ASSERTMSGLINE(454, a, "MTX44Concat(): NULL Mtx44Ptr 'a' ");
+ ASSERTMSGLINE(455, b, "MTX44Concat(): NULL Mtx44Ptr 'b' ");
+ ASSERTMSGLINE(456, ab, "MTX44Concat(): NULL Mtx44Ptr 'ab' ");
+
+ if (ab == a || ab == b) {
+ m = mTmp;
+ } else {
+ m = ab;
+ }
+
+ m[0][0] = (a[0][0] * b[0][0]) + (a[0][1] * b[1][0]) + (a[0][2] * b[2][0]) + (a[0][3] * b[3][0]);
+ m[0][1] = (a[0][0] * b[0][1]) + (a[0][1] * b[1][1]) + (a[0][2] * b[2][1]) + (a[0][3] * b[3][1]);
+ m[0][2] = (a[0][0] * b[0][2]) + (a[0][1] * b[1][2]) + (a[0][2] * b[2][2]) + (a[0][3] * b[3][2]);
+ m[0][3] = (a[0][0] * b[0][3]) + (a[0][1] * b[1][3]) + (a[0][2] * b[2][3]) + (a[0][3] * b[3][3]);
+
+ m[1][0] = (a[1][0] * b[0][0]) + (a[1][1] * b[1][0]) + (a[1][2] * b[2][0]) + (a[1][3] * b[3][0]);
+ m[1][1] = (a[1][0] * b[0][1]) + (a[1][1] * b[1][1]) + (a[1][2] * b[2][1]) + (a[1][3] * b[3][1]);
+ m[1][2] = (a[1][0] * b[0][2]) + (a[1][1] * b[1][2]) + (a[1][2] * b[2][2]) + (a[1][3] * b[3][2]);
+ m[1][3] = (a[1][0] * b[0][3]) + (a[1][1] * b[1][3]) + (a[1][2] * b[2][3]) + (a[1][3] * b[3][3]);
+
+ m[2][0] = (a[2][0] * b[0][0]) + (a[2][1] * b[1][0]) + (a[2][2] * b[2][0]) + (a[2][3] * b[3][0]);
+ m[2][1] = (a[2][0] * b[0][1]) + (a[2][1] * b[1][1]) + (a[2][2] * b[2][1]) + (a[2][3] * b[3][1]);
+ m[2][2] = (a[2][0] * b[0][2]) + (a[2][1] * b[1][2]) + (a[2][2] * b[2][2]) + (a[2][3] * b[3][2]);
+ m[2][3] = (a[2][0] * b[0][3]) + (a[2][1] * b[1][3]) + (a[2][2] * b[2][3]) + (a[2][3] * b[3][3]);
+
+ m[3][0] = (a[3][0] * b[0][0]) + (a[3][1] * b[1][0]) + (a[3][2] * b[2][0]) + (a[3][3] * b[3][0]);
+ m[3][1] = (a[3][0] * b[0][1]) + (a[3][1] * b[1][1]) + (a[3][2] * b[2][1]) + (a[3][3] * b[3][1]);
+ m[3][2] = (a[3][0] * b[0][2]) + (a[3][1] * b[1][2]) + (a[3][2] * b[2][2]) + (a[3][3] * b[3][2]);
+ m[3][3] = (a[3][0] * b[0][3]) + (a[3][1] * b[1][3]) + (a[3][2] * b[2][3]) + (a[3][3] * b[3][3]);
+
+ if (m == mTmp) {
+ C_MTX44Copy(mTmp, ab);
+ }
+}
+
+asm void PSMTX44Concat(const __REGISTER Mtx44 a, const __REGISTER Mtx44 b, __REGISTER Mtx44 ab) {
+ nofralloc
+ psq_l f0, 0x0(a), 0, 0
+ psq_l f2, 0x0(b), 0, 0
+ ps_muls0 f6, f2, f0
+ psq_l f3, 0x10(b), 0, 0
+ psq_l f4, 0x20(b), 0, 0
+ ps_madds1 f6, f3, f0, f6
+ psq_l f1, 0x8(a), 0, 0
+ psq_l f5, 0x30(b), 0, 0
+ ps_madds0 f6, f4, f1, f6
+ psq_l f0, 0x10(a), 0, 0
+ ps_madds1 f6, f5, f1, f6
+ psq_l f1, 0x18(a), 0, 0
+ ps_muls0 f8, f2, f0
+ ps_madds1 f8, f3, f0, f8
+ psq_l f0, 0x20(a), 0, 0
+ ps_madds0 f8, f4, f1, f8
+ ps_madds1 f8, f5, f1, f8
+ psq_l f1, 0x28(a), 0, 0
+ ps_muls0 f10, f2, f0
+ ps_madds1 f10, f3, f0, f10
+ psq_l f0, 0x30(a), 0, 0
+ ps_madds0 f10, f4, f1, f10
+ ps_madds1 f10, f5, f1, f10
+ psq_l f1, 0x38(a), 0, 0
+ ps_muls0 f12, f2, f0
+ psq_l f2, 0x8(b), 0, 0
+ ps_madds1 f12, f3, f0, f12
+ psq_l f0, 0x0(a), 0, 0
+ ps_madds0 f12, f4, f1, f12
+ psq_l f3, 0x18(b), 0, 0
+ ps_madds1 f12, f5, f1, f12
+ psq_l f1, 0x8(a), 0, 0
+ ps_muls0 f7, f2, f0
+ psq_l f4, 0x28(b), 0, 0
+ ps_madds1 f7, f3, f0, f7
+ psq_l f5, 0x38(b), 0, 0
+ ps_madds0 f7, f4, f1, f7
+ psq_l f0, 0x10(a), 0, 0
+ ps_madds1 f7, f5, f1, f7
+ psq_l f1, 0x18(a), 0, 0
+ ps_muls0 f9, f2, f0
+ psq_st f6, 0x0(ab), 0, 0
+ ps_madds1 f9, f3, f0, f9
+ psq_l f0, 0x20(a), 0, 0
+ ps_madds0 f9, f4, f1, f9
+ psq_st f8, 0x10(ab), 0, 0
+ ps_madds1 f9, f5, f1, f9
+ psq_l f1, 0x28(a), 0, 0
+ ps_muls0 f11, f2, f0
+ psq_st f10, 0x20(ab), 0, 0
+ ps_madds1 f11, f3, f0, f11
+ psq_l f0, 0x30(a), 0, 0
+ ps_madds0 f11, f4, f1, f11
+ psq_st f12, 0x30(ab), 0, 0
+ ps_madds1 f11, f5, f1, f11
+ psq_l f1, 0x38(a), 0, 0
+ ps_muls0 f13, f2, f0
+ psq_st f7, 0x8(ab), 0, 0
+ ps_madds1 f13, f3, f0, f13
+ psq_st f9, 0x18(ab), 0, 0
+ ps_madds0 f13, f4, f1, f13
+ psq_st f11, 0x28(ab), 0, 0
+ ps_madds1 f13, f5, f1, f13
+ psq_st f13, 0x38(ab), 0, 0
+ blr
+}
+
+void C_MTX44Transpose(const Mtx44 src, Mtx44 xPose) {
+ Mtx44 mTmp;
+ Mtx44Ptr m;
+
+ ASSERTMSGLINE(637, src, "MTX44Transpose(): NULL Mtx44Ptr 'src' ");
+ ASSERTMSGLINE(638, xPose, "MTX44Transpose(): NULL Mtx44Ptr 'xPose' ");
+
+ if (src == xPose) {
+ m = mTmp;
+ } else {
+ m = xPose;
+ }
+
+ m[0][0] = src[0][0];
+ m[0][1] = src[1][0];
+ m[0][2] = src[2][0];
+ m[0][3] = src[3][0];
+ m[1][0] = src[0][1];
+ m[1][1] = src[1][1];
+ m[1][2] = src[2][1];
+ m[1][3] = src[3][1];
+ m[2][0] = src[0][2];
+ m[2][1] = src[1][2];
+ m[2][2] = src[2][2];
+ m[2][3] = src[3][2];
+ m[3][0] = src[0][3];
+ m[3][1] = src[1][3];
+ m[3][2] = src[2][3];
+ m[3][3] = src[3][3];
+
+ if (m == mTmp) {
+ MTX44Copy(mTmp, xPose);
+ }
+}
+
+asm void PSMTX44Transpose(const __REGISTER Mtx44 src, __REGISTER Mtx44 xPose) {
+ nofralloc
+ psq_l f0, 0x0(src), 0, 0
+ psq_l f1, 0x10(src), 0, 0
+ ps_merge00 f4, f0, f1
+ psq_l f2, 0x8(src), 0, 0
+ psq_st f4, 0x0(xPose), 0, 0
+ ps_merge11 f5, f0, f1
+ psq_l f3, 0x18(src), 0, 0
+ psq_st f5, 0x10(xPose), 0, 0
+ ps_merge00 f4, f2, f3
+ psq_l f0, 0x20(src), 0, 0
+ psq_st f4, 0x20(xPose), 0, 0
+ ps_merge11 f5, f2, f3
+ psq_l f1, 0x30(src), 0, 0
+ psq_st f5, 0x30(xPose), 0, 0
+ ps_merge00 f4, f0, f1
+ psq_l f2, 0x28(src), 0, 0
+ psq_st f4, 0x8(xPose), 0, 0
+ ps_merge11 f5, f0, f1
+ psq_l f3, 0x38(src), 0, 0
+ psq_st f5, 0x18(xPose), 0, 0
+ ps_merge00 f4, f2, f3
+ psq_st f4, 0x28(xPose), 0, 0
+ ps_merge11 f5, f2, f3
+ psq_st f5, 0x38(xPose), 0, 0
+ blr
+}
+
+#define SWAP(a, b) \
+ { \
+ f32 tmp; \
+ tmp = a; \
+ a = b; \
+ b = tmp; \
+ }
+
+u32 C_MTX44Inverse(const Mtx44 src, Mtx44 inv) {
+ Mtx44 gjm;
+ s32 i;
+ s32 j;
+ s32 k;
+ f32 w;
+ f32 max;
+ s32 swp;
+ f32 ftmp;
+
+ ASSERTMSGLINE(734, src, "MTX44Inverse(): NULL Mtx44Ptr 'src' ");
+ ASSERTMSGLINE(735, inv, "MTX44Inverse(): NULL Mtx44Ptr 'inv' ");
+
+ MTX44Copy(src, gjm);
+ MTX44Identity(inv);
+
+ for (i = 0; i < 4; i++) {
+ max = 0.0f;
+ swp = i;
+
+ for (k = i; k < 4; k++) {
+ ftmp = fabsf(gjm[k][i]);
+ if (ftmp > max) {
+ max = ftmp;
+ swp = k;
+ }
+ }
+
+ if (max == 0.0f) {
+ return 0;
+ }
+
+ if (swp != i) {
+ for (k = 0; k < 4; k++) {
+ SWAP(gjm[i][k], gjm[swp][k]);
+ SWAP(inv[i][k], inv[swp][k]);
+ }
+ }
+
+ w = 1.0f / gjm[i][i];
+ for (j = 0; j < 4; j++) {
+ gjm[i][j] *= w;
+ inv[i][j] *= w;
+ }
+
+ for (k = 0; k < 4; k++) {
+ if (k != i) {
+ w = gjm[k][i];
+ for (j = 0; j < 4; j++) {
+ gjm[k][j] -= gjm[i][j] * w;
+ inv[k][j] -= inv[i][j] * w;
+ }
+ }
+ }
+ }
+
+ return 1;
+}
+
+void C_MTX44Trans(Mtx44 m, f32 xT, f32 yT, f32 zT) {
+ ASSERTMSGLINE(835, m, "MTX44Trans(): NULL Mtx44Ptr 'm' ");
+
+ m[0][0] = 1.0f;
+ m[0][1] = 0.0f;
+ m[0][2] = 0.0f;
+ m[0][3] = xT;
+
+ m[1][0] = 0.0f;
+ m[1][1] = 1.0f;
+ m[1][2] = 0.0f;
+ m[1][3] = yT;
+
+ m[2][0] = 0.0f;
+ m[2][1] = 0.0f;
+ m[2][2] = 1.0f;
+ m[2][3] = zT;
+
+ m[3][0] = 0.0f;
+ m[3][1] = 0.0f;
+ m[3][2] = 0.0f;
+ m[3][3] = 1.0f;
+}
+
+void PSMTX44Trans(__REGISTER Mtx44 m, __REGISTER f32 xT, __REGISTER f32 yT, __REGISTER f32 zT) {
+ __REGISTER f32 c_zero = 0.0f;
+ __REGISTER f32 c_one = 1.0f;
+ __REGISTER f32 c_01;
+
+ asm {
+ stfs xT, 0xc(m)
+ stfs yT, 0x1c(m)
+ ps_merge00 c_01, c_zero, c_one
+ stfs zT, 0x2c(m)
+ psq_st c_one, 0x0(m), 1, 0
+ psq_st c_zero, 0x4(m), 0, 0
+ psq_st c_01, 0x10(m), 0, 0
+ psq_st c_zero, 0x18(m), 1, 0
+ psq_st c_zero, 0x20(m), 0, 0
+ psq_st c_one, 0x28(m), 1, 0
+ psq_st c_zero, 0x30(m), 0, 0
+ psq_st c_01, 0x38(m), 0, 0
+ }
+}
+
+void C_MTX44TransApply(const Mtx44 src, Mtx44 dst, f32 xT, f32 yT, f32 zT) {
+ ASSERTMSGLINE(899, src, "MTX44TransApply(): NULL Mtx44Ptr 'src' ");
+ ASSERTMSGLINE(900, dst, "MTX44TransApply(): NULL Mtx44Ptr 'src' "); //! wrong assert string
+
+ if (src != dst) {
+ dst[0][0] = src[0][0];
+ dst[0][1] = src[0][1];
+ dst[0][2] = src[0][2];
+
+ dst[1][0] = src[1][0];
+ dst[1][1] = src[1][1];
+ dst[1][2] = src[1][2];
+
+ dst[2][0] = src[2][0];
+ dst[2][1] = src[2][1];
+ dst[2][2] = src[2][2];
+
+ dst[3][0] = src[3][0];
+ dst[3][1] = src[3][1];
+ dst[3][2] = src[3][2];
+
+ dst[3][3] = src[3][3];
+ }
+
+ dst[0][3] = (src[0][3] + xT);
+ dst[1][3] = (src[1][3] + yT);
+ dst[2][3] = (src[2][3] + zT);
+}
+
+asm void PSMTX44TransApply(const __REGISTER Mtx44 src, __REGISTER Mtx44 dst, __REGISTER f32 xT, __REGISTER f32 yT, __REGISTER f32 zT) {
+ nofralloc
+ psq_l f4, 0x0(src), 0, 0
+ frsp xT, xT
+ psq_l f5, 0x8(src), 0, 0
+ frsp yT, yT
+ psq_l f6, 0x10(src), 0, 0
+ frsp zT, zT
+ psq_l f7, 0x18(src), 0, 0
+ psq_st f4, 0x0(dst), 0, 0
+ ps_sum1 f5, xT, f5, f5
+ psq_l f4, 0x28(src), 0, 0
+ psq_st f6, 0x10(dst), 0, 0
+ ps_sum1 f7, yT, f7, f7
+ psq_l f8, 0x20(src), 0, 0
+ psq_st f5, 0x8(dst), 0, 0
+ ps_sum1 f4, zT, f4, f4
+ psq_st f7, 0x18(dst), 0, 0
+ psq_st f8, 0x20(dst), 0, 0
+ psq_l f5, 0x30(src), 0, 0
+ psq_l f6, 0x38(src), 0, 0
+ psq_st f4, 0x28(dst), 0, 0
+ psq_st f5, 0x30(dst), 0, 0
+ psq_st f6, 0x38(dst), 0, 0
+ blr
+}
+
+void C_MTX44Scale(Mtx44 m, f32 xS, f32 yS, f32 zS) {
+ ASSERTMSGLINE(976, m, "MTX44Scale(): NULL Mtx44Ptr 'm' ");
+ m[0][0] = xS;
+ m[0][1] = 0.0f;
+ m[0][2] = 0.0f;
+ m[0][3] = 0.0f;
+
+ m[1][0] = 0.0f;
+ m[1][1] = yS;
+ m[1][2] = 0.0f;
+ m[1][3] = 0.0f;
+
+ m[2][0] = 0.0f;
+ m[2][1] = 0.0f;
+ m[2][2] = zS;
+ m[2][3] = 0.0f;
+
+ m[3][0] = 0.0f;
+ m[3][1] = 0.0f;
+ m[3][2] = 0.0f;
+ m[3][3] = 1.0f;
+}
+
+void PSMTX44Scale(__REGISTER Mtx44 m, __REGISTER f32 xS, __REGISTER f32 yS, __REGISTER f32 zS) {
+ __REGISTER f32 c_zero = 0.0f;
+ __REGISTER f32 c_one = 1.0f;
+
+ asm {
+ stfs xS, 0x0(m)
+ psq_st c_zero, 0x4(m), 0, 0
+ psq_st c_zero, 0xc(m), 0, 0
+ stfs yS, 0x14(m)
+ psq_st c_zero, 0x18(m), 0, 0
+ psq_st c_zero, 0x20(m), 0, 0
+ stfs zS, 0x28(m)
+ psq_st c_zero, 0x2c(m), 0, 0
+ psq_st c_zero, 0x34(m), 0, 0
+ stfs c_one, 0x3c(m)
+ }
+}
+
+void C_MTX44ScaleApply(const Mtx44 src, Mtx44 dst, f32 xS, f32 yS, f32 zS) {
+ ASSERTMSGLINE(1036, src, "MTX44ScaleApply(): NULL Mtx44Ptr 'src' ");
+ ASSERTMSGLINE(1037, dst, "MTX44ScaleApply(): NULL Mtx44Ptr 'dst' ");
+
+ dst[0][0] = (src[0][0] * xS);
+ dst[0][1] = (src[0][1] * xS);
+ dst[0][2] = (src[0][2] * xS);
+ dst[0][3] = (src[0][3] * xS);
+
+ dst[1][0] = (src[1][0] * yS);
+ dst[1][1] = (src[1][1] * yS);
+ dst[1][2] = (src[1][2] * yS);
+ dst[1][3] = (src[1][3] * yS);
+
+ dst[2][0] = (src[2][0] * zS);
+ dst[2][1] = (src[2][1] * zS);
+ dst[2][2] = (src[2][2] * zS);
+ dst[2][3] = (src[2][3] * zS);
+
+ dst[3][0] = src[3][0];
+ dst[3][1] = src[3][1];
+ dst[3][2] = src[3][2];
+ dst[3][3] = src[3][3];
+}
+
+asm void PSMTX44ScaleApply(const __REGISTER Mtx44 src, __REGISTER Mtx44 dst, __REGISTER f32 xS, __REGISTER f32 yS, __REGISTER f32 zS) {
+ nofralloc
+ psq_l f4, 0x0(src), 0, 0
+ frsp xS, xS
+ psq_l f5, 0x8(src), 0, 0
+ frsp yS, yS
+ psq_l f6, 0x10(src), 0, 0
+ ps_muls0 f4, f4, xS
+ psq_l f7, 0x18(src), 0, 0
+ ps_muls0 f5, f5, xS
+ psq_l f8, 0x20(src), 0, 0
+ frsp zS, zS
+ psq_st f4, 0x0(dst), 0, 0
+ ps_muls0 f6, f6, yS
+ psq_l f9, 0x28(src), 0, 0
+ psq_st f5, 0x8(dst), 0, 0
+ ps_muls0 f7, f7, yS
+ psq_l f10, 0x30(src), 0, 0
+ psq_st f6, 0x10(dst), 0, 0
+ ps_muls0 f8, f8, zS
+ psq_l f11, 0x38(src), 0, 0
+ psq_st f7, 0x18(dst), 0, 0
+ ps_muls0 f9, f9, zS
+ psq_st f8, 0x20(dst), 0, 0
+ psq_st f9, 0x28(dst), 0, 0
+ psq_st f10, 0x30(dst), 0, 0
+ psq_st f11, 0x38(dst), 0, 0
+ blr
+}
+
+void C_MTX44RotRad(Mtx44 m, char axis, f32 rad) {
+ f32 sinA;
+ f32 cosA;
+
+ ASSERTMSGLINE(1118, m, "MTX44RotRad(): NULL Mtx44Ptr 'm' ");
+ sinA = sinf(rad);
+ cosA = cosf(rad);
+ C_MTX44RotTrig(m, axis, sinA, cosA);
+}
+
+void PSMTX44RotRad(Mtx44 m, char axis, f32 rad) {
+ f32 sinA;
+ f32 cosA;
+
+ sinA = sinf(rad);
+ cosA = cosf(rad);
+ PSMTX44RotTrig(m, axis, sinA, cosA);
+}
+
+void C_MTX44RotTrig(Mtx44 m, char axis, f32 sinA, f32 cosA) {
+ ASSERTMSGLINE(1163, m, "MTX44RotTrig(): NULL Mtx44Ptr 'm' ");
+
+ axis |= 0x20;
+ switch(axis) {
+ case 'x':
+ m[0][0] = 1.0f;
+ m[0][1] = 0.0f;
+ m[0][2] = 0.0f;
+ m[0][3] = 0.0f;
+ m[1][0] = 0.0f;
+ m[1][1] = cosA;
+ m[1][2] = -sinA;
+ m[1][3] = 0.0f;
+ m[2][0] = 0.0f;
+ m[2][1] = sinA;
+ m[2][2] = cosA;
+ m[2][3] = 0.0f;
+ m[3][0] = 0.0f;
+ m[3][1] = 0.0f;
+ m[3][2] = 0.0f;
+ m[3][3] = 1.0f;
+ break;
+ case 'y':
+ m[0][0] = cosA;
+ m[0][1] = 0.0f;
+ m[0][2] = sinA;
+ m[0][3] = 0.0f;
+ m[1][0] = 0.0f;
+ m[1][1] = 1.0f;
+ m[1][2] = 0.0f;
+ m[1][3] = 0.0f;
+ m[2][0] = -sinA;
+ m[2][1] = 0.0f;
+ m[2][2] = cosA;
+ m[2][3] = 0.0f;
+ m[3][0] = 0.0f;
+ m[3][1] = 0.0f;
+ m[3][2] = 0.0f;
+ m[3][3] = 1.0f;
+ break;
+ case 'z':
+ m[0][0] = cosA;
+ m[0][1] = -sinA;
+ m[0][2] = 0.0f;
+ m[0][3] = 0.0f;
+ m[1][0] = sinA;
+ m[1][1] = cosA;
+ m[1][2] = 0.0f;
+ m[1][3] = 0.0f;
+ m[2][0] = 0.0f;
+ m[2][1] = 0.0f;
+ m[2][2] = 1.0f;
+ m[2][3] = 0.0f;
+ m[3][0] = 0.0f;
+ m[3][1] = 0.0f;
+ m[3][2] = 0.0f;
+ m[3][3] = 1.0f;
+ break;
+ default:
+ ASSERTMSGLINE(1191, FALSE, "MTX44RotTrig(): invalid 'axis' value ");
+ break;
+ }
+}
+
+void PSMTX44RotTrig(__REGISTER Mtx44 m, __REGISTER char axis, __REGISTER f32 sinA, __REGISTER f32 cosA) {
+ __REGISTER f32 ftmp0;
+ __REGISTER f32 ftmp1;
+ __REGISTER f32 ftmp2;
+ __REGISTER f32 ftmp3;
+ __REGISTER f32 ftmp4;
+
+ __REGISTER f32 c_zero = 0.0f;
+ __REGISTER f32 c_one = 1.0f;
+
+ asm {
+ frsp sinA, sinA
+ ori axis, axis, 0x20
+ frsp cosA, cosA
+ cmplwi axis, 'x'
+ beq L_00001AB4
+ cmplwi axis, 'y'
+ beq L_00001AE8
+ cmplwi axis, 'z'
+ beq L_00001B20
+ b L_00001B54
+ L_00001AB4:
+ psq_st c_one, 0x0(m), 1, 0
+ psq_st c_zero, 0x4(m), 0, 0
+ ps_neg ftmp0, sinA
+ psq_st c_zero, 0xc(m), 0, 0
+ ps_merge00 ftmp1, sinA, cosA
+ psq_st c_zero, 0x1c(m), 0, 0
+ ps_merge00 ftmp0, cosA, ftmp0
+ psq_st c_zero, 0x2c(m), 0, 0
+ psq_st c_zero, 0x34(m), 0, 0
+ psq_st ftmp1, 0x24(m), 0, 0
+ psq_st ftmp0, 0x14(m), 0, 0
+ psq_st c_one, 0x3c(m), 1, 0
+ b L_00001B54
+ L_00001AE8:
+ ps_merge00 ftmp1, cosA, c_zero
+ psq_st c_zero, 0x30(m), 0, 0
+ ps_neg ftmp0, sinA
+ psq_st c_zero, 0x18(m), 0, 0
+ ps_merge00 ftmp3, c_zero, c_one
+ psq_st ftmp1, 0x0(m), 0, 0
+ ps_merge00 ftmp4, ftmp0, c_zero
+ ps_merge00 ftmp2, sinA, c_zero
+ psq_st ftmp3, 0x10(m), 0, 0
+ psq_st ftmp2, 0x8(m), 0, 0
+ psq_st ftmp4, 0x20(m), 0, 0
+ psq_st ftmp1, 0x28(m), 0, 0
+ psq_st ftmp3, 0x38(m), 0, 0
+ b L_00001B54
+ L_00001B20:
+ psq_st c_zero, 0x8(m), 0, 0
+ ps_neg ftmp0, sinA
+ psq_st c_zero, 0x18(m), 0, 0
+ ps_merge00 ftmp1, sinA, cosA
+ psq_st c_zero, 0x20(m), 0, 0
+ ps_merge00 ftmp2, c_one, c_zero
+ psq_st c_zero, 0x30(m), 0, 0
+ ps_merge00 ftmp3, c_zero, c_one
+ psq_st ftmp1, 0x10(m), 0, 0
+ ps_merge00 ftmp4, cosA, ftmp0
+ psq_st ftmp2, 0x28(m), 0, 0
+ psq_st ftmp3, 0x38(m), 0, 0
+ psq_st ftmp4, 0x0(m), 0, 0
+ L_00001B54:
+ }
+}
+
+void C_MTX44RotAxisRad(Mtx44 m, const Vec* axis, f32 rad) {
+ Vec vN;
+ f32 s;
+ f32 c;
+ f32 t;
+ f32 x;
+ f32 y;
+ f32 z;
+ f32 xSq;
+ f32 ySq;
+ f32 zSq;
+
+ ASSERTMSGLINE(1300, m, "MTX44RotAxisRad(): NULL Mtx44Ptr 'm' ");
+ ASSERTMSGLINE(1301, axis, "MTX44RotAxisRad(): NULL VecPtr 'axis' ");
+
+ s = sinf(rad);
+ c = cosf(rad);
+ t = 1 - c;
+
+ C_VECNormalize(axis, &vN);
+
+ x = vN.x;
+ y = vN.y;
+ z = vN.z;
+
+ xSq = (x * x);
+ ySq = (y * y);
+ zSq = (z * z);
+
+ m[0][0] = (c + (t * xSq));
+ m[0][1] = (y * (t * x)) - (s * z);
+ m[0][2] = (z * (t * x)) + (s * y);
+ m[0][3] = 0.0f;
+
+ m[1][0] = ((y * (t * x)) + (s * z));
+ m[1][1] = (c + (t * ySq));
+ m[1][2] = ((z * (t * y)) - (s * x));
+ m[1][3] = 0.0f;
+
+ m[2][0] = ((z * (t * x)) - (s * y));
+ m[2][1] = ((z * (t * y)) + (s * x));
+ m[2][2] = (c + (t * zSq));
+ m[2][3] = 0.0f;
+
+ m[3][0] = 0.0f;
+ m[3][1] = 0.0f;
+ m[3][2] = 0.0f;
+ m[3][3] = 1.0f;
+}
+
+static void __PSMTX44RotAxisRadInternal(__REGISTER Mtx44 m, const __REGISTER Vec* axis, __REGISTER f32 sT, __REGISTER f32 cT) {
+ __REGISTER f32 tT;
+ __REGISTER f32 fc0;
+ __REGISTER f32 tmp0;
+ __REGISTER f32 tmp1;
+ __REGISTER f32 tmp2;
+ __REGISTER f32 tmp3;
+ __REGISTER f32 tmp4;
+ __REGISTER f32 tmp5;
+ __REGISTER f32 tmp6;
+ __REGISTER f32 tmp7;
+ __REGISTER f32 tmp8;
+ __REGISTER f32 tmp9;
+
+ tmp9 = 0.5f;
+ tmp8 = 3.0f;
+
+ asm {
+ frsp cT, cT
+ psq_l tmp0, 0x0(axis), 0, 0
+ frsp sT, sT
+ lfs tmp1, 0x8(axis)
+ ps_mul tmp2, tmp0, tmp0
+ fadds tmp7, tmp9, tmp9
+ ps_madd tmp3, tmp1, tmp1, tmp2
+ fsubs fc0, tmp9, tmp9
+ ps_sum0 tmp4, tmp3, tmp1, tmp2
+ fsubs tT, tmp7, cT
+ frsqrte tmp5, tmp4
+ ps_merge00 tmp7, fc0, tmp7
+ fmuls tmp2, tmp5, tmp5
+ fmuls tmp3, tmp5, tmp9
+ psq_st fc0, 0x30(m), 0, 0
+ fnmsubs tmp2, tmp2, tmp4, tmp8
+ fmuls tmp5, tmp2, tmp3
+ psq_st tmp7, 0x38(m), 0, 0
+ ps_merge00 cT, cT, cT
+ ps_muls0 tmp0, tmp0, tmp5
+ ps_muls0 tmp1, tmp1, tmp5
+ ps_muls0 tmp4, tmp0, tT
+ ps_muls0 tmp9, tmp0, sT
+ ps_muls0 tmp5, tmp1, tT
+ ps_muls1 tmp3, tmp4, tmp0
+ ps_muls0 tmp2, tmp4, tmp0
+ ps_muls0 tmp4, tmp4, tmp1
+ fnmsubs tmp6, tmp1, sT, tmp3
+ fmadds tmp7, tmp1, sT, tmp3
+ ps_neg tmp0, tmp9
+ ps_sum0 tmp8, tmp4, fc0, tmp9
+ ps_sum0 tmp2, tmp2, tmp6, cT
+ ps_sum1 tmp3, cT, tmp7, tmp3
+ ps_sum0 tmp6, tmp0, fc0, tmp4
+ psq_st tmp8, 0x8(m), 0, 0
+ ps_sum0 tmp0, tmp4, tmp4, tmp0
+ psq_st tmp2, 0x0(m), 0, 0
+ ps_muls0 tmp5, tmp5, tmp1
+ psq_st tmp3, 0x10(m), 0, 0
+ ps_sum1 tmp4, tmp9, tmp0, tmp4
+ psq_st tmp6, 0x18(m), 0, 0
+ ps_sum0 tmp5, tmp5, fc0, cT
+ psq_st tmp4, 0x20(m), 0, 0
+ psq_st tmp5, 0x28(m), 0, 0
+ }
+}
+
+void PSMTX44RotAxisRad(Mtx44 m, const Vec* axis, f32 rad) {
+ f32 sinT, cosT;
+
+ sinT = sinf(rad);
+ cosT = cosf(rad);
+
+ __PSMTX44RotAxisRadInternal(m, axis, sinT, cosT);
+}
diff --git a/src/revolution/mtx/mtx44vec.c b/src/revolution/mtx/mtx44vec.c
new file mode 100644
index 0000000000..da92c17ea7
--- /dev/null
+++ b/src/revolution/mtx/mtx44vec.c
@@ -0,0 +1,247 @@
+#include <revolution.h>
+#include <revolution/mtx.h>
+#include "fake_tgmath.h"
+
+void C_MTX44MultVec(const Mtx44 m, const Vec* src, Vec* dst) {
+ Vec vTmp;
+ f32 w;
+
+ ASSERTMSGLINE(67, m, "MTX44MultVec(): NULL Mtx44Ptr 'm' ");
+ ASSERTMSGLINE(68, src, "MTX44MultVec(): NULL VecPtr 'src' ");
+ ASSERTMSGLINE(69, dst, "MTX44MultVec(): NULL VecPtr 'dst' ");
+
+ vTmp.x = m[0][0] * src->x + m[0][1] * src->y + m[0][2] * src->z + m[0][3];
+ vTmp.y = m[1][0] * src->x + m[1][1] * src->y + m[1][2] * src->z + m[1][3];
+ vTmp.z = m[2][0] * src->x + m[2][1] * src->y + m[2][2] * src->z + m[2][3];
+ w = m[3][0] * src->x + m[3][1] * src->y + m[3][2] * src->z + m[3][3];
+ w = 1.0f / w;
+
+ dst->x = vTmp.x * w;
+ dst->y = vTmp.y * w;
+ dst->z = vTmp.z * w;
+}
+
+asm void PSMTX44MultVec(const __REGISTER Mtx44 m, const __REGISTER Vec* src, __REGISTER Vec* dst) {
+ nofralloc
+ psq_l f0, 0x0(src), 0, 0
+ psq_l f2, 0x30(m), 0, 0
+ psq_l f1, 0x8(src), 1, 0
+ ps_mul f4, f0, f2
+ psq_l f3, 0x38(m), 0, 0
+ ps_madd f5, f1, f3, f4
+ ps_merge11 f12, f1, f1
+ ps_sum0 f13, f5, f5, f5
+ psq_l f4, 0x0(m), 0, 0
+ ps_merge00 f13, f13, f13
+ psq_l f5, 0x8(m), 0, 0
+ ps_div f13, f12, f13
+ psq_l f6, 0x10(m), 0, 0
+ psq_l f7, 0x18(m), 0, 0
+ psq_l f8, 0x20(m), 0, 0
+ psq_l f9, 0x28(m), 0, 0
+ ps_mul f4, f0, f4
+ ps_madd f2, f1, f5, f4
+ ps_mul f6, f0, f6
+ ps_madd f3, f1, f7, f6
+ ps_mul f8, f0, f8
+ ps_sum0 f2, f2, f2, f2
+ ps_madd f9, f1, f9, f8
+ ps_sum1 f2, f3, f2, f3
+ ps_sum0 f3, f9, f9, f9
+ ps_mul f2, f2, f13
+ psq_st f2, 0x0(dst), 0, 0
+ ps_mul f3, f3, f13
+ psq_st f3, 0x8(dst), 1, 0
+ blr
+}
+
+void C_MTX44MultVecArray(const Mtx44 m, const Vec* srcBase, Vec* dstBase, u32 count) {
+ u32 i;
+ Vec vTmp;
+ f32 w;
+
+ ASSERTMSGLINE(154, m, "MTX44MultVecArray(): NULL Mtx44Ptr 'm' ");
+ ASSERTMSGLINE(155, srcBase, "MTX44MultVecArray(): NULL VecPtr 'srcBase' ");
+ ASSERTMSGLINE(156, dstBase, "MTX44MultVecArray(): NULL VecPtr 'dstBase' ");
+
+ for(i = 0; i < count; i++) {
+ vTmp.x = m[0][0] * srcBase->x + m[0][1] * srcBase->y + m[0][2] * srcBase->z + m[0][3];
+ vTmp.y = m[1][0] * srcBase->x + m[1][1] * srcBase->y + m[1][2] * srcBase->z + m[1][3];
+ vTmp.z = m[2][0] * srcBase->x + m[2][1] * srcBase->y + m[2][2] * srcBase->z + m[2][3];
+ w = m[3][0] * srcBase->x + m[3][1] * srcBase->y + m[3][2] * srcBase->z + m[3][3];
+ w = 1.0f / w;
+ dstBase->x = vTmp.x * w;
+ dstBase->y = vTmp.y * w;
+ dstBase->z = vTmp.z * w;
+ srcBase++;
+ dstBase++;
+ }
+}
+
+asm void PSMTX44MultVecArray(const __REGISTER Mtx44 m, const __REGISTER Vec* srcBase, __REGISTER Vec* dstBase, __REGISTER u32 count) {
+ nofralloc
+ stwu r1, -0x10(r1)
+ subi count, count, 0x1
+ psq_l f6, 0x30(m), 0, 0
+ mtctr count
+ psq_l f8, 0x0(srcBase), 0, 0
+ subi dstBase, dstBase, 0x4
+ psq_l f7, 0x38(m), 0, 0
+ psq_lu f9, 0x8(srcBase), 1, 0
+ ps_mul f13, f6, f8
+ psq_l f0, 0x0(m), 0, 0
+ stfd f14, 0x8(r1)
+ ps_madd f13, f7, f9, f13
+ psq_l f2, 0x10(m), 0, 0
+ ps_merge11 f14, f9, f9
+ ps_mul f10, f0, f8
+ psq_l f4, 0x20(m), 0, 0
+ ps_mul f11, f2, f8
+ psq_l f1, 0x8(m), 0, 0
+ ps_mul f12, f4, f8
+ psq_l f3, 0x18(m), 0, 0
+ ps_sum0 f13, f13, f13, f13
+ psq_l f5, 0x28(m), 0, 0
+L_00000468:
+ ps_madd f10, f1, f9, f10
+ ps_madd f11, f3, f9, f11
+ ps_madd f12, f5, f9, f12
+ ps_sum0 f10, f10, f10, f10
+ ps_sum0 f11, f11, f11, f11
+ ps_sum0 f12, f12, f12, f12
+ ps_div f13, f14, f13
+ psq_lu f8, 0x4(srcBase), 0, 0
+ psq_lu f9, 0x8(srcBase), 1, 0
+ ps_mul f10, f10, f13
+ psq_stu f10, 0x4(dstBase), 1, 0
+ ps_mul f11, f11, f13
+ psq_stu f11, 0x4(dstBase), 1, 0
+ ps_mul f12, f12, f13
+ psq_stu f12, 0x4(dstBase), 1, 0
+ ps_mul f13, f6, f8
+ ps_mul f10, f0, f8
+ ps_mul f11, f2, f8
+ ps_madd f13, f7, f9, f13
+ ps_mul f12, f4, f8
+ ps_sum0 f13, f13, f13, f13
+ bdnz L_00000468
+ ps_madd f10, f1, f9, f10
+ ps_madd f11, f3, f9, f11
+ ps_madd f12, f5, f9, f12
+ ps_sum0 f10, f10, f10, f10
+ ps_sum0 f11, f11, f11, f11
+ ps_sum0 f12, f12, f12, f12
+ ps_div f13, f14, f13
+ ps_mul f10, f10, f13
+ psq_st f10, 0x4(dstBase), 1, 0
+ ps_mul f11, f11, f13
+ psq_st f11, 0x8(dstBase), 1, 0
+ ps_mul f12, f12, f13
+ psq_st f12, 0xc(dstBase), 1, 0
+ lfd f14, 0x8(r1)
+ addi r1, r1, 0x10
+ blr
+}
+
+void C_MTX44MultVecSR(const Mtx44 m, const Vec* src, Vec* dst) {
+ Vec vTmp;
+
+ ASSERTMSGLINE(288, m, "MTX44MultVecSR(): NULL Mtx44Ptr 'm' ");
+ ASSERTMSGLINE(289, src, "MTX44MultVecSR(): NULL VecPtr 'src' ");
+ ASSERTMSGLINE(290, dst, "MTX44MultVecSR(): NULL VecPtr 'dst' ");
+ vTmp.x = (m[0][2] * src->z) + ((m[0][0] * src->x) + (m[0][1] * src->y));
+ vTmp.y = (m[1][2] * src->z) + ((m[1][0] * src->x) + (m[1][1] * src->y));
+ vTmp.z = (m[2][2] * src->z) + ((m[2][0] * src->x) + (m[2][1] * src->y));
+ dst->x = vTmp.x;
+ dst->y = vTmp.y;
+ dst->z = vTmp.z;
+}
+
+asm void PSMTX44MultVecSR(const __REGISTER Mtx m, const __REGISTER Vec* src, __REGISTER Vec* dst) {
+ nofralloc
+ psq_l f0, 0x0(m), 0, 0
+ psq_l f6, 0x0(src), 0, 0
+ psq_l f2, 0x10(m), 0, 0
+ ps_mul f8, f0, f6
+ psq_l f4, 0x20(m), 0, 0
+ ps_mul f10, f2, f6
+ psq_l f7, 0x8(src), 1, 0
+ ps_mul f12, f4, f6
+ psq_l f3, 0x18(m), 0, 0
+ ps_sum0 f8, f8, f8, f8
+ psq_l f5, 0x28(m), 0, 0
+ ps_sum0 f10, f10, f10, f10
+ psq_l f1, 0x8(m), 0, 0
+ ps_sum0 f12, f12, f12, f12
+ ps_madd f9, f1, f7, f8
+ psq_st f9, 0x0(dst), 1, 0
+ ps_madd f11, f3, f7, f10
+ psq_st f11, 0x4(dst), 1, 0
+ ps_madd f13, f5, f7, f12
+ psq_st f13, 0x8(dst), 1, 0
+ blr
+}
+
+void C_MTX44MultVecArraySR(const Mtx44 m, const Vec* srcBase, Vec* dstBase, u32 count) {
+ u32 i;
+ Vec vTmp;
+
+ ASSERTMSGLINE(379, m, "MTX44MultVecArraySR(): NULL Mtx44Ptr 'm' ");
+ ASSERTMSGLINE(380, srcBase, "MTX44MultVecArraySR(): NULL VecPtr 'srcBase' ");
+ ASSERTMSGLINE(381, dstBase, "MTX44MultVecArraySR(): NULL VecPtr 'dstBase' ");
+
+ for(i = 0; i < count; i++) {
+ vTmp.x = (m[0][2] * srcBase->z) + ((m[0][0] * srcBase->x) + (m[0][1] * srcBase->y));
+ vTmp.y = (m[1][2] * srcBase->z) + ((m[1][0] * srcBase->x) + (m[1][1] * srcBase->y));
+ vTmp.z = (m[2][2] * srcBase->z) + ((m[2][0] * srcBase->x) + (m[2][1] * srcBase->y));
+ dstBase->x = vTmp.x;
+ dstBase->y = vTmp.y;
+ dstBase->z = vTmp.z;
+ srcBase++;
+ dstBase++;
+ }
+}
+
+asm void PSMTX44MultVecArraySR(const __REGISTER Mtx44 m, const __REGISTER Vec* srcBase, __REGISTER Vec* dstBase, __REGISTER u32 count) {
+ nofralloc
+ psq_l f0, 0x0(m), 0, 0
+ subi count, count, 0x1
+ psq_l f6, 0x0(srcBase), 0, 0
+ ps_mul f8, f0, f6
+ psq_l f2, 0x10(m), 0, 0
+ ps_mul f9, f2, f6
+ psq_l f4, 0x20(m), 0, 0
+ psq_lu f7, 0x8(srcBase), 1, 0
+ ps_mul f10, f4, f6
+ psq_l f1, 0x8(m), 1, 0
+ mtctr count
+ psq_l f3, 0x18(m), 1, 0
+ subi dstBase, dstBase, 0x4
+ psq_l f5, 0x28(m), 1, 0
+L_00000890:
+ ps_madd f11, f1, f7, f8
+ psq_lu f6, 0x4(srcBase), 0, 0
+ ps_madd f12, f3, f7, f9
+ ps_madd f13, f5, f7, f10
+ psq_lu f7, 0x8(srcBase), 1, 0
+ ps_sum0 f11, f11, f8, f8
+ psq_stu f11, 0x4(dstBase), 1, 0
+ ps_sum0 f12, f12, f9, f9
+ psq_stu f12, 0x4(dstBase), 1, 0
+ ps_sum0 f13, f13, f10, f10
+ psq_stu f13, 0x4(dstBase), 1, 0
+ ps_mul f8, f0, f6
+ ps_mul f9, f2, f6
+ ps_mul f10, f4, f6
+ bdnz L_00000890
+ ps_madd f11, f1, f7, f8
+ ps_madd f12, f3, f7, f9
+ ps_madd f13, f5, f7, f10
+ ps_sum0 f11, f11, f8, f8
+ psq_stu f11, 0x4(dstBase), 1, 0
+ ps_sum0 f12, f12, f9, f9
+ psq_stu f12, 0x4(dstBase), 1, 0
+ ps_sum0 f13, f13, f10, f10
+ psq_stu f13, 0x4(dstBase), 1, 0
+ blr
+}
diff --git a/src/revolution/mtx/mtxstack.c b/src/revolution/mtx/mtxstack.c
new file mode 100644
index 0000000000..af7956f4f7
--- /dev/null
+++ b/src/revolution/mtx/mtxstack.c
@@ -0,0 +1,108 @@
+#include <revolution.h>
+#include <revolution/mtx.h>
+#include "fake_tgmath.h"
+
+void MTXInitStack(MTXStack* sPtr, u32 numMtx) {
+ ASSERTMSGLINE(74, sPtr, "MTXInitStack(): NULL MtxStackPtr 'sPtr' ");
+ ASSERTMSGLINE(75, sPtr->stackBase, "MTXInitStack(): 'sPtr' contains a NULL ptr to stack memory ");
+ ASSERTMSGLINE(76, numMtx, "MTXInitStack(): 'numMtx' is 0 ");
+
+ sPtr->numMtx = numMtx;
+ sPtr->stackPtr = 0;
+}
+
+MtxPtr MTXPush(MTXStack* sPtr, const Mtx m) {
+ ASSERTMSGLINE(104, sPtr, "MTXPush(): NULL MtxStackPtr 'sPtr' ");
+ ASSERTMSGLINE(105, sPtr->stackBase, "MTXPush(): 'sPtr' contains a NULL ptr to stack memory ");
+ ASSERTMSGLINE(106, m, "MTXPush(): NULL MtxPtr 'm' ");
+
+ if (sPtr->stackPtr == NULL) {
+ sPtr->stackPtr = sPtr->stackBase;
+ MTXCopy(m, sPtr->stackPtr);
+ } else {
+ ASSERTMSGLINE(121, ((((s32)sPtr->stackPtr - (s32)sPtr->stackBase) / 16) / 3) < (sPtr->numMtx - 1), "MTXPush(): stack overflow ");
+ MTXCopy(m, sPtr->stackPtr + 3);
+ sPtr->stackPtr += 3;
+ }
+
+ return sPtr->stackPtr;
+}
+
+MtxPtr MTXPushFwd(MTXStack* sPtr, const Mtx m) {
+ ASSERTMSGLINE(157, sPtr, "MTXPushFwd(): NULL MtxStackPtr 'sPtr' ");
+ ASSERTMSGLINE(158, sPtr->stackBase, "MTXPushFwd(): 'sPtr' contains a NULL ptr to stack memory ");
+ ASSERTMSGLINE(159, m, "MTXPushFwd(): NULL MtxPtr 'm' ");
+
+ if (sPtr->stackPtr == NULL) {
+ sPtr->stackPtr = sPtr->stackBase;
+ MTXCopy(m, sPtr->stackPtr);
+ } else {
+ ASSERTMSGLINE(174, ((((s32)sPtr->stackPtr - (s32)sPtr->stackBase) / 16) / 3) < (sPtr->numMtx - 1), "MTXPushFwd(): stack overflow");
+ MTXConcat(sPtr->stackPtr, m, sPtr->stackPtr + 3);
+ sPtr->stackPtr += 3;
+ }
+
+ return sPtr->stackPtr;
+}
+
+MtxPtr MTXPushInv(MTXStack* sPtr, const Mtx m) {
+ Mtx mInv;
+ ASSERTMSGLINE(216, sPtr, "MTXPushInv(): NULL MtxStackPtr 'sPtr' ");
+ ASSERTMSGLINE(217, sPtr->stackBase, "MTXPushInv(): 'sPtr' contains a NULL ptr to stack memory ");
+ ASSERTMSGLINE(218, m, "MTXPushInv(): NULL MtxPtr 'm' ");
+
+ MTXInverse(m, mInv);
+ if (sPtr->stackPtr == NULL) {
+ sPtr->stackPtr = sPtr->stackBase;
+ MTXCopy(mInv, sPtr->stackPtr);
+ } else {
+ ASSERTMSGLINE(236, ((((s32)sPtr->stackPtr - (s32)sPtr->stackBase) / 16) / 3) < (sPtr->numMtx - 1), "MTXPushInv(): stack overflow");
+ MTXConcat(mInv, sPtr->stackPtr, sPtr->stackPtr + 3);
+ sPtr->stackPtr += 3;
+ }
+
+ return sPtr->stackPtr;
+}
+
+MtxPtr MTXPushInvXpose(MTXStack* sPtr, const Mtx m) {
+ Mtx mIT;
+ ASSERTMSGLINE(279, sPtr, "MTXPushInvXpose(): NULL MtxStackPtr 'sPtr' ");
+ ASSERTMSGLINE(280, sPtr->stackBase, "MTXPushInvXpose(): 'sPtr' contains a NULL ptr to stack memory ");
+ ASSERTMSGLINE(281, m, "MTXPushInvXpose(): NULL MtxPtr 'm' ");
+
+ MTXInverse(m, mIT);
+ MTXTranspose(mIT, mIT);
+ if (sPtr->stackPtr == NULL) {
+ sPtr->stackPtr = sPtr->stackBase;
+ MTXCopy(mIT, sPtr->stackPtr);
+ } else {
+ ASSERTMSGLINE(300, ((((s32)sPtr->stackPtr - (s32)sPtr->stackBase) / 16) / 3) < (sPtr->numMtx - 1), "MTXPushInvXpose(): stack overflow ");
+ MTXConcat(sPtr->stackPtr, mIT, sPtr->stackPtr + 3);
+ sPtr->stackPtr += 3;
+ }
+
+ return sPtr->stackPtr;
+}
+
+MtxPtr MTXPop(MTXStack* sPtr) {
+ ASSERTMSGLINE(328, sPtr, "MTXPop(): NULL MtxStackPtr 'sPtr' ");
+ ASSERTMSGLINE(329, sPtr->stackBase, "MTXPop(): 'sPtr' contains a NULL ptr to stack memory ");
+
+ if (sPtr->stackPtr == NULL) {
+ return NULL;
+ }
+
+ if (sPtr->stackBase == sPtr->stackPtr) {
+ sPtr->stackPtr = NULL;
+ return NULL;
+ }
+
+ sPtr->stackPtr -= 3;
+ return sPtr->stackPtr;
+}
+
+MtxPtr MTXGetStackPtr(const MTXStack* sPtr) {
+ ASSERTMSGLINE(366, sPtr, "MTXGetStackPtr(): NULL MtxStackPtr 'sPtr' ");
+ ASSERTMSGLINE(367, sPtr->stackBase, "MTXGetStackPtr(): 'sPtr' contains a NULL ptr to stack memory ");
+ return sPtr->stackPtr;
+}
diff --git a/src/revolution/mtx/mtxvec.c b/src/revolution/mtx/mtxvec.c
new file mode 100644
index 0000000000..99c5a71a75
--- /dev/null
+++ b/src/revolution/mtx/mtxvec.c
@@ -0,0 +1,204 @@
+#include <revolution.h>
+#include <revolution/mtx.h>
+#include <math.h>
+
+void C_MTXMultVec(const Mtx m, const Vec* src, Vec* dst) {
+ Vec vTmp;
+
+ ASSERTMSGLINE(72, m, "MTXMultVec(): NULL MtxPtr 'm' ");
+ ASSERTMSGLINE(73, src, "MTXMultVec(): NULL VecPtr 'src' ");
+ ASSERTMSGLINE(74, dst, "MTXMultVec(): NULL VecPtr 'dst' ");
+
+ vTmp.x = m[0][3] + ((m[0][2] * src->z) + ((m[0][0] * src->x) + (m[0][1] * src->y)));
+ vTmp.y = m[1][3] + ((m[1][2] * src->z) + ((m[1][0] * src->x) + (m[1][1] * src->y)));
+ vTmp.z = m[2][3] + ((m[2][2] * src->z) + ((m[2][0] * src->x) + (m[2][1] * src->y)));
+ dst->x = vTmp.x;
+ dst->y = vTmp.y;
+ dst->z = vTmp.z;
+}
+
+asm void PSMTXMultVec(const __REGISTER Mtx m, const __REGISTER Vec* src, __REGISTER Vec* dst) {
+ nofralloc
+ psq_l f0, Vec.x(src), 0, 0
+ psq_l f2, 0(m), 0, 0
+ psq_l f1, Vec.z(src), 1, 0
+ ps_mul f4, f2, f0
+ psq_l f3, 8(m), 0, 0
+ ps_madd f5, f3, f1, f4
+ psq_l f8, 16(m), 0, 0
+ ps_sum0 f6, f5, f6, f5
+ psq_l f9, 24(m), 0, 0
+ ps_mul f10, f8, f0
+ psq_st f6, Vec.x(dst), 1, 0
+ ps_madd f11, f9, f1, f10
+ psq_l f2, 32(m), 0, 0
+ ps_sum0 f12, f11, f12, f11
+ psq_l f3, 40(m), 0, 0
+ ps_mul f4, f2, f0
+ psq_st f12, Vec.y(dst), 1, 0
+ ps_madd f5, f3, f1, f4
+ ps_sum0 f6, f5, f6, f5
+ psq_st f6, Vec.z(dst), 1, 0
+ blr
+}
+
+void C_MTXMultVecArray(const Mtx m, const Vec* srcBase, Vec* dstBase, u32 count) {
+ u32 i;
+ Vec vTmp;
+
+ ASSERTMSGLINE(168, m, "MTXMultVecArray(): NULL MtxPtr 'm' ");
+ ASSERTMSGLINE(169, srcBase, "MTXMultVecArray(): NULL VecPtr 'srcBase' ");
+ ASSERTMSGLINE(170, dstBase, "MTXMultVecArray(): NULL VecPtr 'dstBase' ");
+ ASSERTMSGLINE(171, count > 1, "MTXMultVecArray(): count must be greater than 1.");
+
+ for(i = 0; i < count; i++) {
+ vTmp.x = m[0][3] + ((m[0][2] * srcBase->z) + ((m[0][0] * srcBase->x) + (m[0][1] * srcBase->y)));
+ vTmp.y = m[1][3] + ((m[1][2] * srcBase->z) + ((m[1][0] * srcBase->x) + (m[1][1] * srcBase->y)));
+ vTmp.z = m[2][3] + ((m[2][2] * srcBase->z) + ((m[2][0] * srcBase->x) + (m[2][1] * srcBase->y)));
+ dstBase->x = vTmp.x;
+ dstBase->y = vTmp.y;
+ dstBase->z = vTmp.z;
+ srcBase++;
+ dstBase++;
+ }
+}
+
+asm void PSMTXMultVecArray(const __REGISTER Mtx m, const __REGISTER Vec* srcBase, __REGISTER Vec* dstBase, __REGISTER u32 count) {
+ nofralloc
+ psq_l f13, 0x0(m), 0, 0
+ psq_l f12, 0x10(m), 0, 0
+ subi count, count, 0x1
+ psq_l f11, 0x8(m), 0, 0
+ ps_merge00 f0, f13, f12
+ subi dstBase, dstBase, 0x4
+ psq_l f10, 0x18(m), 0, 0
+ ps_merge11 f1, f13, f12
+ mtctr count
+ psq_l f4, 0x20(m), 0, 0
+ ps_merge00 f2, f11, f10
+ psq_l f5, 0x28(m), 0, 0
+ ps_merge11 f3, f11, f10
+ psq_l f6, 0x0(srcBase), 0, 0
+ psq_lu f7, 0x8(srcBase), 1, 0
+ ps_madds0 f8, f0, f6, f3
+ ps_mul f9, f4, f6
+ ps_madds1 f8, f1, f6, f8
+ ps_madd f10, f5, f7, f9
+L_000003C4:
+ psq_lu f6, 0x4(srcBase), 0, 0
+ ps_madds0 f12, f2, f7, f8
+ psq_lu f7, 0x8(srcBase), 1, 0
+ ps_sum0 f13, f10, f9, f10
+ ps_madds0 f8, f0, f6, f3
+ ps_mul f9, f4, f6
+ psq_stu f12, 0x4(dstBase), 0, 0
+ ps_madds1 f8, f1, f6, f8
+ psq_stu f13, 0x8(dstBase), 1, 0
+ ps_madd f10, f5, f7, f9
+ bdnz L_000003C4
+ ps_madds0 f12, f2, f7, f8
+ ps_sum0 f13, f10, f9, f10
+ psq_stu f12, 0x4(dstBase), 0, 0
+ psq_stu f13, 0x8(dstBase), 1, 0
+ blr
+}
+
+void C_MTXMultVecSR(const Mtx m, const Vec* src, Vec* dst) {
+ Vec vTmp;
+
+ ASSERTMSGLINE(319, m, "MTXMultVecSR(): NULL MtxPtr 'm' ");
+ ASSERTMSGLINE(320, src, "MTXMultVecSR(): NULL VecPtr 'src' ");
+ ASSERTMSGLINE(321, dst, "MTXMultVecSR(): NULL VecPtr 'dst' ");
+
+ vTmp.x = (m[0][2] * src->z) + ((m[0][0] * src->x) + (m[0][1] * src->y));
+ vTmp.y = (m[1][2] * src->z) + ((m[1][0] * src->x) + (m[1][1] * src->y));
+ vTmp.z = (m[2][2] * src->z) + ((m[2][0] * src->x) + (m[2][1] * src->y));
+ dst->x = vTmp.x;
+ dst->y = vTmp.y;
+ dst->z = vTmp.z;
+}
+
+asm void PSMTXMultVecSR(const __REGISTER Mtx m, const __REGISTER Vec* src, __REGISTER Vec* dst) {
+ nofralloc
+ psq_l f0, 0x0(m), 0, 0
+ psq_l f6, 0x0(src), 0, 0
+ psq_l f2, 0x10(m), 0, 0
+ ps_mul f8, f0, f6
+ psq_l f4, 0x20(m), 0, 0
+ ps_mul f10, f2, f6
+ psq_l f7, 0x8(src), 1, 0
+ ps_mul f12, f4, f6
+ psq_l f3, 0x18(m), 0, 0
+ ps_sum0 f8, f8, f8, f8
+ psq_l f5, 0x28(m), 0, 0
+ ps_sum0 f10, f10, f10, f10
+ psq_l f1, 0x8(m), 0, 0
+ ps_sum0 f12, f12, f12, f12
+ ps_madd f9, f1, f7, f8
+ psq_st f9, 0x0(dst), 1, 0
+ ps_madd f11, f3, f7, f10
+ psq_st f11, 0x4(dst), 1, 0
+ ps_madd f13, f5, f7, f12
+ psq_st f13, 0x8(dst), 1, 0
+ blr
+}
+
+void C_MTXMultVecArraySR(const Mtx m, const Vec* srcBase, Vec* dstBase, u32 count) {
+ u32 i;
+ Vec vTmp;
+
+ ASSERTMSGLINE(416, m, "MTXMultVecArraySR(): NULL MtxPtr 'm' ");
+ ASSERTMSGLINE(417, srcBase, "MTXMultVecArraySR(): NULL VecPtr 'srcBase' ");
+ ASSERTMSGLINE(418, dstBase, "MTXMultVecArraySR(): NULL VecPtr 'dstBase' ");
+ ASSERTMSGLINE(419, count > 1, "MTXMultVecArraySR(): count must be greater than 1.");
+
+ for(i = 0; i < count; i++) {
+ vTmp.x = (m[0][2] * srcBase->z) + ((m[0][0] * srcBase->x) + (m[0][1] * srcBase->y));
+ vTmp.y = (m[1][2] * srcBase->z) + ((m[1][0] * srcBase->x) + (m[1][1] * srcBase->y));
+ vTmp.z = (m[2][2] * srcBase->z) + ((m[2][0] * srcBase->x) + (m[2][1] * srcBase->y));
+ dstBase->x = vTmp.x;
+ dstBase->y = vTmp.y;
+ dstBase->z = vTmp.z;
+ srcBase++;
+ dstBase++;
+ }
+}
+
+asm void PSMTXMultVecArraySR(const __REGISTER Mtx m, const __REGISTER Vec* srcBase, __REGISTER Vec* dstBase, __REGISTER u32 count) {
+ nofralloc
+ psq_l f13, 0x0(m), 0, 0
+ psq_l f12, 0x10(m), 0, 0
+ subi count, count, 0x1
+ psq_l f11, 0x8(m), 1, 0
+ ps_merge00 f0, f13, f12
+ subi dstBase, dstBase, 0x4
+ psq_l f10, 0x18(m), 1, 0
+ ps_merge11 f1, f13, f12
+ mtctr count
+ psq_l f3, 0x20(m), 0, 0
+ ps_merge00 f2, f11, f10
+ psq_l f4, 0x28(m), 1, 0
+ psq_l f6, 0x0(srcBase), 0, 0
+ psq_lu f7, 0x8(srcBase), 1, 0
+ ps_muls0 f8, f0, f6
+ ps_mul f9, f3, f6
+ ps_madds1 f8, f1, f6, f8
+ ps_madd f10, f4, f7, f9
+L_000007D0:
+ psq_lu f6, 0x4(srcBase), 0, 0
+ ps_madds0 f12, f2, f7, f8
+ psq_lu f7, 0x8(srcBase), 1, 0
+ ps_sum0 f13, f10, f9, f9
+ ps_muls0 f8, f0, f6
+ ps_mul f9, f3, f6
+ psq_stu f12, 0x4(dstBase), 0, 0
+ ps_madds1 f8, f1, f6, f8
+ psq_stu f13, 0x8(dstBase), 1, 0
+ ps_madd f10, f4, f7, f9
+ bdnz L_000007D0
+ ps_madds0 f12, f2, f7, f8
+ ps_sum0 f13, f10, f9, f9
+ psq_stu f12, 0x4(dstBase), 0, 0
+ psq_stu f13, 0x8(dstBase), 1, 0
+ blr
+}
diff --git a/src/revolution/mtx/psmtx.c b/src/revolution/mtx/psmtx.c
new file mode 100644
index 0000000000..ab9aac13db
--- /dev/null
+++ b/src/revolution/mtx/psmtx.c
@@ -0,0 +1,336 @@
+#include <revolution.h>
+#include <revolution/mtx.h>
+#include "fake_tgmath.h"
+
+asm void PSMTXReorder(const __REGISTER Mtx src, __REGISTER ROMtx dest) {
+ psq_l f0, 0(src), 0, 0
+ psq_l f2, 16(src), 0, 0
+ psq_l f4, 32(src), 0, 0
+ psq_l f1, 8(src), 0, 0
+ ps_merge00 f6, f0, f2
+ psq_l f3, 24(src), 0, 0
+ ps_merge01 f12, f4, f0
+ psq_l f5, 40(src), 0, 0
+ ps_merge11 f7, f2, f4
+ psq_st f6, 0(dest), 0, 0
+ ps_merge00 f8, f1, f3
+ psq_st f12, 8(dest), 0, 0
+ ps_merge01 f9, f5, f1
+ psq_st f7, 16(dest), 0, 0
+ ps_merge11 f10, f3, f5
+ psq_st f8, 24(dest), 0, 0
+ psq_st f9, 32(dest), 0, 0
+ psq_st f10, 40(dest), 0, 0
+}
+
+asm void PSMTXROMultVecArray(const __REGISTER ROMtx m, const __REGISTER Vec* srcBase, __REGISTER Vec* dstBase, __REGISTER u32 count) {
+ nofralloc
+ stwu r1, -64(r1)
+ stfd f14, 8(r1)
+ subi r7, count, 1
+ stfd f15, 16(r1)
+ srwi r7, r7, 1
+ stfd f16, 24(r1)
+ stfd f17, 32(r1)
+ stfd f18, 40(r1)
+ mtctr r7
+ psq_l f0, 0(m), 0, 0
+ subi srcBase, srcBase, 8
+ psq_l f1, 8(m), 1, 0
+ subi dstBase, dstBase, 4
+ psq_l f6, 36(m), 0, 0
+ psq_lu f8, 8(srcBase), 0, 0
+ psq_l f7, 44(m), 1, 0
+ psq_lu f9, 8(srcBase), 0, 0
+ ps_madds0 f11, f0, f8, f6
+ psq_l f2, 12(m), 0, 0
+ ps_madds0 f12, f1, f8, f7
+ psq_l f3, 20(m), 1, 0
+ ps_madds1 f13, f0, f9, f6
+ psq_lu f10, 8(srcBase), 0, 0
+ ps_madds1 f14, f1, f9, f7
+ psq_l f5, 32(m), 1, 0
+ ps_madds1 f11, f2, f8, f11
+ ps_madds1 f12, f3, f8, f12
+ psq_l f4, 24(m), 0, 0
+ ps_madds0 f13, f2, f10, f13
+ psq_lu f8, 8(srcBase), 0, 0
+ ps_madds0 f14, f3, f10, f14
+ ps_madds0 f15, f4, f9, f11
+ ps_madds0 f16, f5, f9, f12
+ psq_lu f9, 8(srcBase), 0, 0
+ ps_madds1 f17, f4, f10, f13
+ ps_madds1 f18, f5, f10, f14
+ psq_lu f10, 8(srcBase), 0, 0
+loop:
+ ps_madds0 f11, f0, f8, f6
+ psq_stu f15, 4(dstBase), 0, 0
+ ps_madds0 f12, f1, f8, f7
+ psq_stu f16, 8(dstBase), 1, 0
+ ps_madds1 f13, f0, f9, f6
+ psq_stu f17, 4(dstBase), 0, 0
+ ps_madds1 f14, f1, f9, f7
+ psq_stu f18, 8(dstBase), 1, 0
+ ps_madds1 f11, f2, f8, f11
+ ps_madds1 f12, f3, f8, f12
+ psq_lu f8, 8(srcBase), 0, 0
+ ps_madds0 f13, f2, f10, f13
+ ps_madds0 f14, f3, f10, f14
+ ps_madds0 f15, f4, f9, f11
+ ps_madds0 f16, f5, f9, f12
+ psq_lu f9, 8(srcBase), 0, 0
+ ps_madds1 f17, f4, f10, f13
+ ps_madds1 f18, f5, f10, f14
+ psq_lu f10, 8(srcBase), 0, 0
+ bdnz loop
+ psq_stu f15, 4(dstBase), 0, 0
+ clrlwi. r7, count, 31
+ psq_stu f16, 8(dstBase), 1, 0
+ bne exit
+ psq_stu f17, 4(dstBase), 0, 0
+ psq_stu f18, 8(dstBase), 1, 0
+exit:
+ lfd f14, 8(r1)
+ lfd f15, 16(r1)
+ lfd f16, 24(r1)
+ lfd f17, 32(r1)
+ lfd f18, 40(r1)
+ addi r1, r1, 64
+ blr
+}
+
+asm void PSMTXROSkin2VecArray(const __REGISTER ROMtx m0, const __REGISTER ROMtx m1, const __REGISTER f32* wtBase, const __REGISTER Vec* srcBase, __REGISTER Vec* dstBase, __REGISTER u32 count) {
+ nofralloc
+ stwu r1, -160(r1)
+ stfd f14, 8(r1)
+ stfd f15, 16(r1)
+ stfd f16, 24(r1)
+ stfd f17, 32(r1)
+ stfd f18, 40(r1)
+ stfd f19, 48(r1)
+ stfd f20, 56(r1)
+ stfd f21, 64(r1)
+ stfd f22, 72(r1)
+ stfd f23, 80(r1)
+ stfd f24, 88(r1)
+ stfd f25, 96(r1)
+ stfd f26, 104(r1)
+ stfd f27, 112(r1)
+ stfd f28, 120(r1)
+ stfd f29, 128(r1)
+ stfd f30, 136(r1)
+ subi r9, r8, 1
+ mtctr r9
+ subi srcBase, srcBase, 4
+ subi dstBase, dstBase, 4
+ subi wtBase, wtBase, 4
+ psq_l f14, 0(m0), 0, 0
+ psq_l f22, 0(m1), 0, 0
+ psq_l f15, 8(m0), 1, 0
+ psq_l f23, 8(m1), 1, 0
+ psq_l f16, 12(m0), 0, 0
+ psq_l f24, 12(m1), 0, 0
+ ps_sub f22, f22, f14
+ psq_l f17, 20(m0), 1, 0
+ psq_l f25, 20(m1), 1, 0
+ ps_sub f23, f23, f15
+ psq_l f18, 24(m0), 0, 0
+ psq_l f26, 24(m1), 0, 0
+ ps_sub f24, f24, f16
+ psq_l f19, 32(m0), 1, 0
+ psq_l f27, 32(m1), 1, 0
+ ps_sub f25, f25, f17
+ psq_l f20, 36(m0), 0, 0
+ psq_l f28, 36(m1), 0, 0
+ ps_sub f26, f26, f18
+ psq_l f21, 44(m0), 1, 0
+ psq_l f29, 44(m1), 1, 0
+ ps_sub f27, f27, f19
+ ps_sub f28, f28, f20
+ ps_sub f29, f29, f21
+ psq_lu f30, 4(wtBase), 1, 0
+ psq_lu f8, 4(srcBase), 0, 0
+ psq_lu f9, 8(srcBase), 1, 0
+ ps_madds0 f0, f22, f30, f14
+ ps_madds0 f1, f23, f30, f15
+ ps_madds0 f2, f24, f30, f16
+ ps_madds0 f3, f25, f30, f17
+ ps_madds0 f4, f26, f30, f18
+ ps_madds0 f5, f27, f30, f19
+ ps_madds0 f6, f28, f30, f20
+ ps_madds0 f7, f29, f30, f21
+ ps_madds0 f12, f0, f8, f6
+ ps_madds0 f13, f1, f8, f7
+ psq_lu f30, 4(wtBase), 1, 0
+loop:
+ ps_madds1 f12, f2, f8, f12
+ ps_madds1 f13, f3, f8, f13
+ psq_lu f8, 4(srcBase), 0, 0
+ ps_madds0 f10, f4, f9, f12
+ ps_madds0 f11, f5, f9, f13
+ psq_lu f9, 8(srcBase), 1, 0
+ ps_madds0 f0, f22, f30, f14
+ ps_madds0 f1, f23, f30, f15
+ ps_madds0 f2, f24, f30, f16
+ ps_madds0 f3, f25, f30, f17
+ ps_madds0 f4, f26, f30, f18
+ ps_madds0 f5, f27, f30, f19
+ ps_madds0 f6, f28, f30, f20
+ ps_madds0 f7, f29, f30, f21
+ psq_stu f10, 4(dstBase), 0, 0
+ ps_madds0 f12, f0, f8, f6
+ ps_madds0 f13, f1, f8, f7
+ psq_stu f11, 8(dstBase), 1, 0
+ psq_lu f30, 4(wtBase), 1, 0
+ bdnz loop
+ ps_madds1 f12, f2, f8, f12
+ ps_madds1 f13, f3, f8, f13
+ ps_madds0 f10, f4, f9, f12
+ psq_stu f10, 4(dstBase), 0, 0
+ ps_madds0 f11, f5, f9, f13
+ psq_stu f11, 8(dstBase), 1, 0
+ lfd f14, 8(r1)
+ lfd f15, 16(r1)
+ lfd f16, 24(r1)
+ lfd f17, 32(r1)
+ lfd f18, 40(r1)
+ lfd f19, 48(r1)
+ lfd f20, 56(r1)
+ lfd f21, 64(r1)
+ lfd f22, 72(r1)
+ lfd f23, 80(r1)
+ lfd f24, 88(r1)
+ lfd f25, 96(r1)
+ lfd f26, 104(r1)
+ lfd f27, 112(r1)
+ lfd f28, 120(r1)
+ lfd f29, 128(r1)
+ lfd f30, 136(r1)
+ addi r1, r1, 160
+ blr
+}
+
+asm void PSMTXROMultS16VecArray(const __REGISTER Mtx m, const __REGISTER S16Vec* srcBase, __REGISTER Vec* dstBase, __REGISTER u32 count) {
+ nofralloc
+ stwu r1, -64(r1)
+ stfd f14, 8(r1)
+ subi r7, count, 1
+ stfd f15, 16(r1)
+ srwi r7, r7, 1
+ stfd f16, 24(r1)
+ lis r8, 7
+ stfd f17, 32(r1)
+ mtspr GQR6, r8
+ stfd f18, 40(r1)
+ mtctr r7
+ psq_l f0, 0(m), 0, 0
+ subi srcBase, srcBase, 4
+ psq_l f1, 8(m), 1, 0
+ subi dstBase, dstBase, 4
+ psq_l f6, 36(m), 0, 0
+ psq_lu f8, 4(srcBase), 0, 6
+ psq_l f7, 44(m), 1, 0
+ psq_lu f9, 4(srcBase), 0, 6
+ ps_madds0 f11, f0, f8, f6
+ psq_l f2, 12(m), 0, 0
+ ps_madds0 f12, f1, f8, f7
+ psq_l f3, 20(m), 1, 0
+ ps_madds1 f13, f0, f9, f6
+ psq_lu f10, 4(srcBase), 0, 6
+ ps_madds1 f14, f1, f9, f7
+ psq_l f5, 32(m), 1, 0
+ ps_madds1 f11, f2, f8, f11
+ ps_madds1 f12, f3, f8, f12
+ psq_l f4, 24(m), 0, 0
+ ps_madds0 f13, f2, f10, f13
+ psq_lu f8, 4(srcBase), 0, 6
+ ps_madds0 f14, f3, f10, f14
+ ps_madds0 f15, f4, f9, f11
+ ps_madds0 f16, f5, f9, f12
+ psq_lu f9, 4(srcBase), 0, 6
+ ps_madds1 f17, f4, f10, f13
+ ps_madds1 f18, f5, f10, f14
+ psq_lu f10, 4(srcBase), 0, 6
+loop:
+ ps_madds0 f11, f0, f8, f6
+ psq_stu f15, 4(dstBase), 0, 0
+ ps_madds0 f12, f1, f8, f7
+ psq_stu f16, 8(dstBase), 1, 0
+ ps_madds1 f13, f0, f9, f6
+ psq_stu f17, 4(dstBase), 0, 0
+ ps_madds1 f14, f1, f9, f7
+ psq_stu f18, 8(dstBase), 1, 0
+ ps_madds1 f11, f2, f8, f11
+ ps_madds1 f12, f3, f8, f12
+ psq_lu f8, 4(srcBase), 0, 6
+ ps_madds0 f13, f2, f10, f13
+ ps_madds0 f14, f3, f10, f14
+ ps_madds0 f15, f4, f9, f11
+ ps_madds0 f16, f5, f9, f12
+ psq_lu f9, 4(srcBase), 0, 6
+ ps_madds1 f17, f4, f10, f13
+ ps_madds1 f18, f5, f10, f14
+ psq_lu f10, 4(srcBase), 0, 6
+ bdnz loop
+ psq_stu f15, 4(dstBase), 0, 0
+ clrlwi. r7, count, 31
+ psq_stu f16, 8(dstBase), 1, 0
+ bne exit
+ psq_stu f17, 4(dstBase), 0, 0
+ psq_stu f18, 8(dstBase), 1, 0
+exit:
+ lfd f14, 8(r1)
+ lfd f15, 16(r1)
+ lfd f16, 24(r1)
+ lfd f17, 32(r1)
+ lfd f18, 40(r1)
+ addi r1, r1, 64
+ blr
+}
+
+asm void PSMTXMultS16VecArray(const __REGISTER ROMtx* m, const __REGISTER S16Vec* srcBase, __REGISTER Vec* dstBase, __REGISTER u32 count) {
+ psq_l f0, 0(m), 0, 0
+ lis r7, 7
+ mtspr GQR6, r7
+ psq_l f6, 0(srcBase), 0, 6
+ subi count, count, 1
+ psq_l f7, 4(srcBase), 1, 6
+ mtctr count
+ psq_l f1, 8(m), 0, 0
+ addi srcBase, srcBase, 4
+ psq_l f2, 16(m), 0, 0
+ subi dstBase, dstBase, 4
+ psq_l f3, 24(m), 0, 0
+ ps_mul f8, f0, f6
+ psq_l f4, 32(m), 0, 0
+ ps_mul f10, f2, f6
+ psq_l f5, 40(m), 0, 0
+ ps_mul f12, f4, f6
+ psq_lu f6, 2(srcBase), 0, 6
+ ps_madd f8, f1, f7, f8
+ ps_madd f10, f3, f7, f10
+ ps_madd f12, f5, f7, f12
+ psq_lu f7, 4(srcBase), 1, 6
+ ps_sum0 f9, f8, f8, f8
+loop:
+ ps_sum0 f11, f10, f10, f10
+ ps_mul f8, f0, f6
+ ps_sum0 f13, f12, f12, f12
+ ps_mul f10, f2, f6
+ psq_stu f9, 4(dstBase), 1, 0
+ ps_mul f12, f4, f6
+ psq_stu f11, 4(dstBase), 1, 0
+ ps_madd f8, f1, f7, f8
+ psq_stu f13, 4(dstBase), 1, 0
+ ps_madd f10, f3, f7, f10
+ psq_lu f6, 2(srcBase), 0, 6
+ ps_madd f12, f5, f7, f12
+ psq_lu f7, 4(srcBase), 1, 6
+ ps_sum0 f9, f8, f8, f8
+ bdnz loop
+ ps_sum0 f11, f10, f10, f10
+ ps_sum0 f13, f12, f12, f12
+ psq_stu f9, 4(dstBase), 1, 0
+ psq_stu f11, 4(dstBase), 1, 0
+ psq_stu f13, 4(dstBase), 1, 0
+}
diff --git a/src/revolution/mtx/quat.c b/src/revolution/mtx/quat.c
new file mode 100644
index 0000000000..14964d9bfb
--- /dev/null
+++ b/src/revolution/mtx/quat.c
@@ -0,0 +1,486 @@
+#include <revolution.h>
+#include <revolution/mtx.h>
+#include <math.h>
+
+void C_QUATAdd(const Quaternion* p, const Quaternion* q, Quaternion* r) {
+ ASSERTMSGLINE(77, p, "QUATAdd(): NULL QuaternionPtr 'p' ");
+ ASSERTMSGLINE(78, q, "QUATAdd(): NULL QuaternionPtr 'q' ");
+ ASSERTMSGLINE(79, r, "QUATAdd(): NULL QuaternionPtr 'r' ");
+
+ r->x = p->x + q->x;
+ r->y = p->y + q->y;
+ r->z = p->z + q->z;
+ r->w = p->w + q->w;
+}
+
+void PSQUATAdd(const __REGISTER Quaternion* p, const __REGISTER Quaternion* q, __REGISTER Quaternion* r) {
+ __REGISTER f32 pxy, qxy, rxy, pzw, qzw, rzw;
+
+ asm {
+ psq_l pxy, 0(p), 0, 0
+ psq_l qxy, 0(q), 0, 0
+ ps_add rxy, pxy, qxy
+ psq_st rxy, 0(r), 0, 0
+ psq_l pzw, 8(p), 0, 0
+ psq_l qzw, 8(q), 0, 0
+ ps_add rzw, pzw, qzw
+ psq_st rzw, 8(r), 0, 0
+ }
+}
+
+void C_QUATSubtract(const Quaternion* p, const Quaternion* q, Quaternion* r) {
+ ASSERTMSGLINE(133, p, "QUATSubtract(): NULL QuaternionPtr 'p' ");
+ ASSERTMSGLINE(134, q, "QUATSubtract(): NULL QuaternionPtr 'q' ");
+ ASSERTMSGLINE(135, r, "QUATSubtract(): NULL QuaternionPtr 'r' ");
+
+ r->x = p->x - q->x;
+ r->y = p->y - q->y;
+ r->z = p->z - q->z;
+ r->w = p->w - q->w;
+}
+
+void PSQUATSubtract(const __REGISTER Quaternion* p, const __REGISTER Quaternion* q, __REGISTER Quaternion* r) {
+ __REGISTER f32 pxy, qxy, rxy, pzw, qzw, rzw;
+
+ asm {
+ psq_l pxy, 0(p), 0, 0
+ psq_l qxy, 0(q), 0, 0
+ ps_sub rxy, pxy, qxy
+ psq_st rxy, 0(r), 0, 0
+ psq_l pzw, 8(p), 0, 0
+ psq_l qzw, 8(q), 0, 0
+ ps_sub rzw, pzw, qzw
+ psq_st rzw, 8(r), 0, 0
+ }
+}
+
+void C_QUATMultiply(const Quaternion* p, const Quaternion* q, Quaternion* pq) {
+ Quaternion* r;
+ Quaternion pqTmp;
+
+ ASSERTMSGLINE(193, p, "QUATMultiply(): NULL QuaternionPtr 'p' ");
+ ASSERTMSGLINE(194, q, "QUATMultiply(): NULL QuaternionPtr 'q' ");
+ ASSERTMSGLINE(195, pq, "QUATMultiply(): NULL QuaternionPtr 'pq' ");
+
+ if (p == pq || q == pq){
+ r = &pqTmp;
+ } else {
+ r = pq;
+ }
+
+ r->w = (p->w * q->w) - (p->x * q->x) - (p->y * q->y) - (p->z * q->z);
+ r->x = (p->w * q->x) + (p->x * q->w) + (p->y * q->z) - (p->z * q->y);
+ r->y = (p->w * q->y) + (p->y * q->w) + (p->z * q->x) - (p->x * q->z);
+ r->z = (p->w * q->z) + (p->z * q->w) + (p->x * q->y) - (p->y * q->x);
+
+ if (r == &pqTmp) {
+ *pq = pqTmp;
+ }
+}
+
+void PSQUATMultiply(const __REGISTER Quaternion* p, const __REGISTER Quaternion* q, __REGISTER Quaternion* pq) {
+ __REGISTER f32 pxy, pzw;
+ __REGISTER f32 qxy, qzw;
+ __REGISTER f32 pnxy, pnzw, pnxny, pnznw;
+ __REGISTER f32 rxy, rzw;
+ __REGISTER f32 sxy, szw;
+
+ asm {
+ psq_l pxy, 0x0(p), 0, 0
+ psq_l pzw, 0x8(p), 0, 0
+ psq_l qxy, 0x0(q), 0, 0
+ ps_neg pnxny, pxy
+ psq_l qzw, 0x8(q), 0, 0
+ ps_neg pnznw, pzw
+ ps_merge01 pnxy, pnxny, pxy
+ ps_muls0 rxy, pzw, qxy
+ ps_muls0 rzw, pnxny, qxy
+ ps_merge01 pnzw, pnznw, pzw
+ ps_muls1 szw, pnxy, qxy
+ ps_madds0 rxy, pnxy, qzw, rxy
+ ps_muls1 sxy, pnzw, qxy
+ ps_madds0 rzw, pnzw, qzw, rzw
+ ps_madds1 szw, pnznw, qzw, szw
+ ps_merge10 rxy, rxy, rxy
+ ps_madds1 sxy, pxy, qzw, sxy
+ ps_merge10 rzw, rzw, rzw
+ ps_add rxy, rxy, sxy
+ psq_st rxy, 0x0(pq), 0, 0
+ ps_sub rzw, rzw, szw
+ psq_st rzw, 0x8(pq), 0, 0
+ }
+}
+
+void C_QUATScale(const Quaternion* q, Quaternion* r, f32 scale) {
+ ASSERTMSGLINE(306, q, "QUATScale(): NULL QuaternionPtr 'q' ");
+ ASSERTMSGLINE(307, r, "QUATScale(): NULL QuaternionPtr 'r' ");
+
+ r->x = q->x * scale;
+ r->y = q->y * scale;
+ r->z = q->z * scale;
+ r->w = q->w * scale;
+}
+
+void PSQUATScale(const __REGISTER Quaternion* q, __REGISTER Quaternion* r, __REGISTER f32 scale) {
+ __REGISTER f32 rxy, rzw;
+
+ asm {
+ psq_l rxy, 0(q), 0, 0
+ psq_l rzw, 8(q), 0, 0
+ ps_muls0 rxy, rxy, scale
+ psq_st rxy, 0(r), 0, 0
+ ps_muls0 rzw, rzw, scale
+ psq_st rzw, 8(r), 0, 0
+ }
+}
+
+f32 C_QUATDotProduct(const Quaternion* p, const Quaternion* q) {
+ ASSERTMSGLINE(357, p, "QUATDotProduct(): NULL QuaternionPtr 'p' ");
+ ASSERTMSGLINE(358, q, "QUATDotProduct(): NULL QuaternionPtr 'q' ");
+
+ return (q->x * p->x) + (q->y * p->y) + (q->z * p->z) + (q->w * p->w);
+}
+
+f32 PSQUATDotProduct(const __REGISTER Quaternion* p, const __REGISTER Quaternion* q) {
+ __REGISTER f32 pxy, pzw, qxy, qzw, dp;
+
+ asm {
+ psq_l pxy, 0(p), 0, 0
+ psq_l qxy, 0(q), 0, 0
+ ps_mul dp, pxy, qxy
+ psq_l pzw, 8(p), 0, 0
+ psq_l qzw, 8(q), 0, 0
+ ps_madd dp, pzw, qzw, dp
+ ps_sum0 dp, dp, dp, dp
+ }
+
+ return dp;
+}
+
+void C_QUATNormalize(const Quaternion* src, Quaternion* unit) {
+ f32 mag;
+ ASSERTMSGLINE(407, src, "QUATNormalize(): NULL QuaternionPtr 'src' ");
+ ASSERTMSGLINE(408, unit, "QUATNormalize(): NULL QuaternionPtr 'unit' ");
+
+ mag = (src->x * src->x) + (src->y * src->y) + (src->z * src->z) + (src->w * src->w);
+ if (mag >= 0.00001f) {
+ mag = 1.0f / sqrtf(mag);
+
+ unit->x = src->x * mag;
+ unit->y = src->y * mag;
+ unit->z = src->z * mag;
+ unit->w = src->w * mag;
+ } else {
+ unit->x = unit->y = unit->z = unit->w = 0.0f;
+ }
+}
+
+void PSQUATNormalize(const __REGISTER Quaternion* src, __REGISTER Quaternion* unit) {
+ __REGISTER f32 sxy, szw;
+ __REGISTER f32 mag, rsqmag;
+ __REGISTER f32 diff;
+ __REGISTER f32 c_zero;
+ __REGISTER f32 nwork0, nwork1;
+
+ __REGISTER f32 epsilon = 0.00001f;
+ __REGISTER f32 c_half = 0.5f;
+ __REGISTER f32 c_three = 3.0f;
+
+ asm {
+ psq_l sxy, 0x0(src), 0, 0
+ ps_mul mag, sxy, sxy
+ psq_l szw, 0x8(src), 0, 0
+ ps_sub c_zero, epsilon, epsilon
+ ps_madd mag, szw, szw, mag
+ ps_sum0 mag, mag, mag, mag
+ frsqrte rsqmag, mag
+ ps_sub diff, mag, epsilon
+ fmul nwork0, rsqmag, rsqmag
+ fmul nwork1, rsqmag, c_half
+ fnmsub nwork0, nwork0, mag, c_three
+ fmul rsqmag, nwork0, nwork1
+ ps_sel rsqmag, diff, rsqmag, c_zero
+ ps_muls0 sxy, sxy, rsqmag
+ ps_muls0 szw, szw, rsqmag
+ psq_st sxy, 0x0(unit), 0, 0
+ psq_st szw, 0x8(unit), 0, 0
+ }
+}
+
+void C_QUATInverse(const Quaternion* src, Quaternion* inv) {
+ f32 mag, norminv;
+ ASSERTMSGLINE(498, src, "QUATInverse(): NULL QuaternionPtr 'src' ");
+ ASSERTMSGLINE(499, inv, "QUATInverse(): NULL QuaternionPtr 'inv' ");
+
+ mag = (src->x * src->x) + (src->y * src->y) + (src->z * src->z) + (src->w * src->w);
+ if (mag == 0.0f) {
+ mag = 1.0f;
+ }
+
+ norminv = 1.0f / mag;
+ inv->x = -src->x * norminv;
+ inv->y = -src->y * norminv;
+ inv->z = -src->z * norminv;
+ inv->w = src->w * norminv;
+}
+
+void PSQUATInverse(const __REGISTER Quaternion* src, __REGISTER Quaternion* inv) {
+ __REGISTER f32 sxy, szw;
+ __REGISTER f32 izz, iww;
+ __REGISTER f32 mag, nmag;
+ __REGISTER f32 norminv, nninv;
+ __REGISTER f32 nwork0;
+ __REGISTER f32 c_two;
+ __REGISTER f32 c_zero;
+ __REGISTER f32 c_one = 1.0f;
+
+ asm {
+ psq_l sxy, 0x0(src), 0, 0
+ ps_mul mag, sxy, sxy
+ ps_sub c_zero, c_one, c_one
+ psq_l szw, 0x8(src), 0, 0
+ ps_madd mag, szw, szw, mag
+ ps_add c_two, c_one, c_one
+ ps_sum0 mag, mag, mag, mag
+ fcmpu cr0, mag, c_zero
+ beq L_00000948
+ fres norminv, mag
+ ps_neg nmag, mag
+ ps_nmsub nwork0, mag, norminv, c_two
+ ps_mul norminv, norminv, nwork0
+ b L_0000094C
+ L_00000948:
+ fmr norminv, c_one
+ L_0000094C:
+ ps_neg nninv, norminv
+ ps_muls1 iww, norminv, szw
+ ps_muls0 sxy, sxy, nninv
+ psq_st iww, 0xc(inv), 1, 0
+ ps_muls0 izz, szw, nninv
+ psq_st sxy, 0x0(inv), 0, 0
+ psq_st izz, 0x8(inv), 1, 0
+ }
+}
+
+void C_QUATDivide(const Quaternion* p, const Quaternion* q, Quaternion* r) {
+ Quaternion qtmp;
+ ASSERTMSGLINE(606, p, "QUATDivide(): NULL QuaternionPtr 'p' ");
+ ASSERTMSGLINE(607, q, "QUATDivide(): NULL QuaternionPtr 'q' ");
+ ASSERTMSGLINE(608, r, "QUATDivide(): NULL QuaternionPtr 'r' ");
+
+ C_QUATInverse(q, &qtmp);
+ C_QUATMultiply(&qtmp, p, r);
+}
+
+void PSQUATDivide(const Quaternion* p, const Quaternion* q, Quaternion* r) {
+ Quaternion qtmp;
+
+ PSQUATInverse(q, &qtmp);
+ PSQUATMultiply(&qtmp, p, r);
+}
+
+void C_QUATExp(const Quaternion* q, Quaternion* r) {
+ f32 theta, scale;
+ ASSERTMSGLINE(643, q, "QUATExp(): NULL QuaternionPtr 'q' ");
+ ASSERTMSGLINE(644, r, "QUATExp(): NULL QuaternionPtr 'r' ");
+ ASSERTMSGLINE(647, q->w == 0.0f, "QUATExp(): 'q' is not a pure quaternion. ");
+
+ theta = sqrtf((q->x * q->x) + (q->y * q->y) + (q->z * q->z));
+ scale = 1.0f;
+
+ if (theta > 0.00001f) {
+ scale = sinf(theta) / theta;
+ }
+
+ r->x = scale * q->x;
+ r->y = scale * q->y;
+ r->z = scale * q->z;
+ r->w = cosf(theta);
+}
+
+void C_QUATLogN(const Quaternion* q, Quaternion* r) {
+ f32 theta, scale, mag;
+ ASSERTMSGLINE(676, q, "QUATLogN(): NULL QuaternionPtr 'q' ");
+ ASSERTMSGLINE(677, r, "QUATLogN(): NULL QuaternionPtr 'r' ");
+
+ scale = (q->x * q->x) + (q->y * q->y) + (q->z * q->z);
+
+#if DEBUG
+ mag = scale + (q->z * q->z);
+ if (mag < 1.0f - 0.00001f || mag > 1.0f + 0.00001f || mag > 1.00001f) {}
+#endif
+
+ scale = sqrtf(scale);
+ theta = atan2f(scale, q->w);
+
+ if (scale > 0.0f) {
+ scale = theta / scale;
+ }
+
+ r->x = scale * q->x;
+ r->y = scale * q->y;
+ r->z = scale * q->z;
+ r->w = 0.0f;
+}
+
+void C_QUATMakeClosest(const Quaternion* q, const Quaternion* qto, Quaternion* r) {
+ f32 dot;
+ ASSERTMSGLINE(722, q, "QUATMakeClosest(): NULL QuaternionPtr 'q' ");
+ ASSERTMSGLINE(723, qto, "QUATMakeClosest(): NULL QuaternionPtr 'qto' ");
+ ASSERTMSGLINE(724, r, "QUATMakeClosest(): NULL QuaternionPtr 'r' ");
+
+ dot = (q->x * qto->x) + (q->y * qto->y) + (q->z * qto->z) + (q->w * qto->w);
+ if (dot < 0.0f) {
+ r->x = -q->x;
+ r->y = -q->y;
+ r->z = -q->z;
+ r->w = -q->w;
+ } else {
+ *r = *q;
+ }
+}
+
+void C_QUATRotAxisRad(Quaternion* r, const Vec* axis, f32 rad) {
+ f32 half, sh, ch;
+ Vec nAxis;
+
+ ASSERTMSGLINE(763, r, "QUATRotAxisRad(): NULL QuaternionPtr 'r' ");
+ ASSERTMSGLINE(764, axis, "QUATRotAxisRad(): NULL VecPtr 'axis' ");
+
+ VECNormalize(axis, &nAxis);
+
+ half = rad * 0.5f;
+ sh = sinf(half);
+ ch = cosf(half);
+
+ r->x = sh * nAxis.x;
+ r->y = sh * nAxis.y;
+ r->z = sh * nAxis.z;
+ r->w = ch;
+}
+
+void C_QUATMtx(Quaternion* r, const Mtx m) {
+ f32 tr,s;
+ s32 i, j, k;
+ s32 nxt[3] = {1, 2, 0};
+ f32 q[3];
+
+ ASSERTMSGLINE(791, r, "QUATMtx(): NULL QuaternionPtr 'r' ");
+ ASSERTMSGLINE(792, m, "QUATMtx(): NULL MtxPtr 'm' ");
+
+ tr = m[0][0] + m[1][1] + m[2][2];
+ if (tr > 0.0f) {
+ s = sqrtf(tr + 1.0f);
+ r->w = s * 0.5f;
+ s = 0.5f / s;
+
+ r->x = (m[2][1] - m[1][2]) * s;
+ r->y = (m[0][2] - m[2][0]) * s;
+ r->z = (m[1][0] - m[0][1]) * s;
+ } else {
+ i = 0;
+ if (m[1][1] > m[0][0]) {
+ i = 1;
+ }
+
+ if (m[2][2] > m[i][i]) {
+ i = 2;
+ }
+
+ j = nxt[i];
+ k = nxt[j];
+
+ s = sqrtf((m[i][i] - (m[j][j] + m[k][k])) + 1.0f);
+ q[i] = s * 0.5f;
+
+ if (s != 0.0f) {
+ s = 0.5f / s;
+ }
+
+ r->w = (m[k][j] - m[j][k]) * s;
+ q[j] = (m[i][j] + m[j][i]) * s;
+ q[k] = (m[i][k] + m[k][i]) * s;
+
+ r->x = q[0];
+ r->y = q[1];
+ r->z = q[2];
+ }
+}
+
+void C_QUATLerp(const Quaternion* p, const Quaternion* q, Quaternion* r, f32 t) {
+ ASSERTMSGLINE(842, p, "QUATLerp(): NULL QuaternionPtr 'p' ");
+ ASSERTMSGLINE(843, q, "QUATLerp(): NULL QuaternionPtr 'q' ");
+ ASSERTMSGLINE(844, r, "QUATLerp(): NULL QuaternionPtr 'r' ");
+
+ r->x = t * (q->x - p->x) + p->x;
+ r->y = t * (q->y - p->y) + p->y;
+ r->z = t * (q->z - p->z) + p->z;
+ r->w = t * (q->w - p->w) + p->w;
+}
+
+void C_QUATSlerp(const Quaternion* p, const Quaternion* q, Quaternion* r, f32 t) {
+ f32 theta, sin_th, cos_th;
+ f32 tp, tq;
+
+ ASSERTMSGLINE(874, p, "QUATSlerp(): NULL QuaternionPtr 'p' ");
+ ASSERTMSGLINE(875, q, "QUATSlerp(): NULL QuaternionPtr 'q' ");
+ ASSERTMSGLINE(876, r, "QUATSlerp(): NULL QuaternionPtr 'r' ");
+
+ cos_th = p->x * q->x + p->y * q->y + p->z * q->z + p->w * q->w;
+ tq = 1.0f;
+
+ if (cos_th < 0.0f) {
+ cos_th = -cos_th;
+ tq = -tq;
+ }
+
+ if (cos_th <= 0.99999f) {
+ theta = acosf(cos_th);
+ sin_th = sinf(theta);
+
+ tp = sinf((1.0f - t) * theta) / sin_th;
+ tq *= sinf(t * theta) / sin_th;
+ } else {
+ tp = 1.0f - t;
+ tq *= t;
+ }
+
+ r->x = (tp * p->x) + (tq * q->x);
+ r->y = (tp * p->y) + (tq * q->y);
+ r->z = (tp * p->z) + (tq * q->z);
+ r->w = (tp * p->w) + (tq * q->w);
+}
+
+void C_QUATSquad(const Quaternion* p, const Quaternion* a, const Quaternion* b, const Quaternion* q, Quaternion* r, f32 t) {
+ Quaternion pq, ab;
+ f32 t2;
+
+ ASSERTMSGLINE(927, p, "QUATSquad(): NULL QuaternionPtr 'p' ");
+ ASSERTMSGLINE(928, a, "QUATSquad(): NULL QuaternionPtr 'a' ");
+ ASSERTMSGLINE(929, b, "QUATSquad(): NULL QuaternionPtr 'b' ");
+ ASSERTMSGLINE(930, q, "QUATSquad(): NULL QuaternionPtr 'q' ");
+ ASSERTMSGLINE(931, r, "QUATSquad(): NULL QuaternionPtr 'r' ");
+
+ t2 = 2.0f * t * (1.0f - t);
+ C_QUATSlerp(p, q, &pq, t);
+ C_QUATSlerp(a, b, &ab, t);
+ C_QUATSlerp(&pq, &ab, r, t2);
+}
+
+void C_QUATCompA(const Quaternion* qprev, const Quaternion* q, const Quaternion* qnext, Quaternion* a) {
+ Quaternion qm, qp, lqm, lqp, qpqm, exq;
+
+ ASSERTMSGLINE(958, qprev, "QUATCompA(): NULL QuaternionPtr 'qprev' ");
+ ASSERTMSGLINE(959, q, "QUATCompA(): NULL QuaternionPtr 'q' ");
+ ASSERTMSGLINE(960, qnext, "QUATCompA(): NULL QuaternionPtr 'qnext' ");
+ ASSERTMSGLINE(961, a, "QUATCompA(): NULL QuaternionPtr 'a' ");
+
+ C_QUATDivide(qprev, q, &qm);
+ C_QUATLogN(&qm, &lqm);
+ C_QUATDivide(qnext, q, &qp);
+ C_QUATLogN(&qp, &lqp);
+ C_QUATAdd(&lqp, &lqm, &qpqm);
+ C_QUATScale(&qpqm, &qpqm, -0.25f);
+ C_QUATExp(&qpqm, &exq);
+ C_QUATMultiply(q, &exq, a);
+}
diff --git a/src/revolution/mtx/vec.c b/src/revolution/mtx/vec.c
new file mode 100644
index 0000000000..d2ef0675a1
--- /dev/null
+++ b/src/revolution/mtx/vec.c
@@ -0,0 +1,344 @@
+#include <revolution.h>
+#include <revolution/mtx.h>
+#include <math.h>
+
+void C_VECAdd(const Vec* a, const Vec* b, Vec* ab) {
+ ASSERTMSGLINE(114, a, "VECAdd(): NULL VecPtr 'a' ");
+ ASSERTMSGLINE(115, b, "VECAdd(): NULL VecPtr 'b' ");
+ ASSERTMSGLINE(116, ab, "VECAdd(): NULL VecPtr 'ab' ");
+ ab->x = a->x + b->x;
+ ab->y = a->y + b->y;
+ ab->z = a->z + b->z;
+}
+
+asm void PSVECAdd(const __REGISTER Vec* a, const __REGISTER Vec* b, __REGISTER Vec* ab) {
+ psq_l f2, Vec.x(a), 0, 0
+ psq_l f4, Vec.x(b), 0, 0
+ ps_add f6, f2, f4
+ psq_st f6, Vec.x(ab), 0, 0
+ psq_l f3, Vec.z(a), 1, 0
+ psq_l f5, Vec.z(b), 1, 0
+ ps_add f7, f3, f5
+ psq_st f7, Vec.z(ab), 1, 0
+}
+
+void C_VECSubtract(const Vec* a, const Vec* b, Vec* a_b) {
+ ASSERTMSGLINE(183, a, "VECSubtract(): NULL VecPtr 'a' ");
+ ASSERTMSGLINE(184, b, "VECSubtract(): NULL VecPtr 'b' ");
+ ASSERTMSGLINE(185, a_b, "VECSubtract(): NULL VecPtr 'a_b' ");
+ a_b->x = a->x - b->x;
+ a_b->y = a->y - b->y;
+ a_b->z = a->z - b->z;
+}
+
+asm void PSVECSubtract(const __REGISTER Vec* a, const __REGISTER Vec* b, __REGISTER Vec* a_b) {
+ psq_l f2, Vec.x(a), 0, 0
+ psq_l f4, Vec.x(b), 0, 0
+ ps_sub f6, f2, f4
+ psq_st f6, Vec.x(a_b), 0, 0
+ psq_l f3, Vec.z(a), 1, 0
+ psq_l f5, Vec.z(b), 1, 0
+ ps_sub f7, f3, f5
+ psq_st f7, Vec.z(a_b), 1, 0
+}
+
+void C_VECScale(const Vec* src, Vec* dst, f32 scale) {
+ ASSERTMSGLINE(253, src, "VECScale(): NULL VecPtr 'src' ");
+ ASSERTMSGLINE(254, dst, "VECScale(): NULL VecPtr 'dst' ");
+ dst->x = (src->x * scale);
+ dst->y = (src->y * scale);
+ dst->z = (src->z * scale);
+}
+
+void PSVECScale(const __REGISTER Vec* src, __REGISTER Vec* dst, __REGISTER f32 scale) {
+ __REGISTER f32 vxy, vz, rxy, rz;
+
+ asm {
+ psq_l vxy, 0x0(src), 0, 0
+ psq_l vz, 0x8(src), 1, 0
+ ps_muls0 rxy, vxy, scale
+ psq_st rxy, 0x0(dst), 0, 0
+ ps_muls0 rz, vz, scale
+ psq_st rz, 0x8(dst), 1, 0
+ }
+}
+
+void C_VECNormalize(const Vec* src, Vec* unit) {
+ f32 mag;
+
+ ASSERTMSGLINE(321, src, "VECNormalize(): NULL VecPtr 'src' ");
+ ASSERTMSGLINE(322, unit, "VECNormalize(): NULL VecPtr 'unit' ");
+
+ mag = (src->z * src->z) + ((src->x * src->x) + (src->y * src->y));
+ ASSERTMSGLINE(327, 0.0f != mag, "VECNormalize(): zero magnitude vector ");
+
+ mag = 1.0f/ sqrtf(mag);
+ unit->x = src->x * mag;
+ unit->y = src->y * mag;
+ unit->z = src->z * mag;
+}
+
+void PSVECNormalize(const __REGISTER Vec* src, __REGISTER Vec* unit) {
+ __REGISTER float c_half = 0.5f;
+ __REGISTER float c_three = 3.0f;
+ __REGISTER float v1_xy;
+ __REGISTER float v1_z;
+ __REGISTER float xx_zz;
+ __REGISTER float xx_yy;
+ __REGISTER float sqsum;
+ __REGISTER float rsqrt;
+ __REGISTER float nwork0;
+ __REGISTER float nwork1;
+
+ asm {
+ psq_l v1_xy, 0x0(src), 0, 0
+ ps_mul xx_yy, v1_xy, v1_xy
+ psq_l v1_z, 0x8(src), 1, 0
+ ps_madd xx_zz, v1_z, v1_z, xx_yy
+ ps_sum0 sqsum, xx_zz, v1_z, xx_yy
+ frsqrte rsqrt, sqsum
+ fmuls nwork0, rsqrt, rsqrt
+ fmuls nwork1, rsqrt, c_half
+ fnmsubs nwork0, nwork0, sqsum, c_three
+ fmuls rsqrt, nwork0, nwork1
+ ps_muls0 v1_xy, v1_xy, rsqrt
+ psq_st v1_xy, 0x0(unit), 0, 0
+ ps_muls0 v1_z, v1_z, rsqrt
+ psq_st v1_z, 0x8(unit), 1, 0
+ }
+}
+
+f32 C_VECSquareMag(const Vec* v) {
+ f32 sqmag;
+
+ ASSERTMSGLINE(411, v, "VECMag(): NULL VecPtr 'v' ");
+
+ sqmag = v->z * v->z + ((v->x * v->x) + (v->y * v->y));
+ return sqmag;
+}
+
+f32 PSVECSquareMag(const __REGISTER Vec* v) {
+ __REGISTER f32 vxy, vzz, sqmag;
+
+ asm {
+ psq_l vxy, 0x0(v), 0, 0
+ ps_mul vxy, vxy, vxy
+ lfs vzz, 0x8(v)
+ ps_madd sqmag, vzz, vzz, vxy
+ ps_sum0 sqmag, sqmag, vxy, vxy
+ }
+
+ return sqmag;
+}
+
+f32 C_VECMag(const Vec* v) {
+ return sqrtf(C_VECSquareMag(v));
+}
+
+f32 PSVECMag(const __REGISTER Vec* v) {
+ __REGISTER f32 vxy, vzz;
+ __REGISTER f32 sqmag, rmag;
+ __REGISTER f32 nwork0, nwork1;
+ __REGISTER f32 c_three, c_half, c_zero;
+
+ c_half = 0.5f;
+
+ asm {
+ psq_l vxy, 0x0(v), 0, 0
+ ps_mul vxy, vxy, vxy
+ lfs vzz, 0x8(v)
+ fsubs c_zero, c_half, c_half
+ ps_madd sqmag, vzz, vzz, vxy
+ ps_sum0 sqmag, sqmag, vxy, vxy
+ fcmpu cr0, sqmag, c_zero
+ beq L_000005F0
+ frsqrte rmag, sqmag
+ }
+
+ c_three = 3.0f;
+
+ asm {
+ fmuls nwork0, rmag, rmag
+ fmuls nwork1, rmag, c_half
+ fnmsubs nwork0, nwork0, sqmag, c_three
+ fmuls rmag, nwork0, nwork1
+ fmuls sqmag, sqmag, rmag
+ L_000005F0:
+ }
+
+ return sqmag;
+}
+
+f32 C_VECDotProduct(const Vec* a, const Vec* b) {
+ f32 dot;
+
+ ASSERTMSGLINE(546, a, "VECDotProduct(): NULL VecPtr 'a' ");
+ ASSERTMSGLINE(547, b, "VECDotProduct(): NULL VecPtr 'b' ");
+ dot = (a->z * b->z) + ((a->x * b->x) + (a->y * b->y));
+ return dot;
+}
+
+asm f32 PSVECDotProduct(const __REGISTER Vec* a, const __REGISTER Vec* b) {
+ psq_l f2, Vec.y(a), 0, 0
+ psq_l f3, Vec.y(b), 0, 0
+ ps_mul f2, f2, f3
+ psq_l f5, Vec.x(a), 0, 0
+ psq_l f4, Vec.x(b), 0, 0
+ ps_madd f3, f5, f4, f2
+ ps_sum0 f1, f3, f2, f2
+}
+
+void C_VECCrossProduct(const Vec* a, const Vec* b, Vec* axb) {
+ Vec vTmp;
+
+ ASSERTMSGLINE(608, a, "VECCrossProduct(): NULL VecPtr 'a' ");
+ ASSERTMSGLINE(609, b, "VECCrossProduct(): NULL VecPtr 'b' ");
+ ASSERTMSGLINE(610, axb, "VECCrossProduct(): NULL VecPtr 'axb' ");
+
+ vTmp.x = (a->y * b->z) - (a->z * b->y);
+ vTmp.y = (a->z * b->x) - (a->x * b->z);
+ vTmp.z = (a->x * b->y) - (a->y * b->x);
+ axb->x = vTmp.x;
+ axb->y = vTmp.y;
+ axb->z = vTmp.z;
+}
+
+asm void PSVECCrossProduct(const __REGISTER Vec* a, const __REGISTER Vec* b, __REGISTER Vec* axb) {
+ psq_l f1, Vec.x(b), 0, 0
+ lfs f2, Vec.z(a)
+ psq_l f0, Vec.x(a), 0, 0
+ ps_merge10 f6, f1, f1
+ lfs f3, Vec.z(b)
+ ps_mul f4, f1, f2
+ ps_muls0 f7, f1, f0
+ ps_msub f5, f0, f3, f4
+ ps_msub f8, f0, f6, f7
+ ps_merge11 f9, f5, f5
+ ps_merge01 f10, f5, f8
+ psq_st f9, Vec.x(axb), 1, 0
+ ps_neg f10, f10
+ psq_st f10, Vec.y(axb), 0, 0
+}
+
+void C_VECHalfAngle(const Vec* a, const Vec* b, Vec* half) {
+ Vec aTmp;
+ Vec bTmp;
+ Vec hTmp;
+
+ ASSERTMSGLINE(713, a, "VECHalfAngle(): NULL VecPtr 'a' ");
+ ASSERTMSGLINE(714, b, "VECHalfAngle(): NULL VecPtr 'b' ");
+ ASSERTMSGLINE(715, half, "VECHalfAngle(): NULL VecPtr 'half' ");
+
+ aTmp.x = -a->x;
+ aTmp.y = -a->y;
+ aTmp.z = -a->z;
+ bTmp.x = -b->x;
+ bTmp.y = -b->y;
+ bTmp.z = -b->z;
+
+ VECNormalize(&aTmp, &aTmp);
+ VECNormalize(&bTmp, &bTmp);
+ VECAdd(&aTmp, &bTmp, &hTmp);
+
+ if (VECDotProduct(&hTmp, &hTmp) > 0.0f) {
+ VECNormalize(&hTmp, half);
+ return;
+ }
+ *half = hTmp;
+}
+
+void C_VECReflect(const Vec* src, const Vec* normal, Vec* dst) {
+ f32 cosA;
+ Vec uI;
+ Vec uN;
+
+ ASSERTMSGLINE(769, src, "VECReflect(): NULL VecPtr 'src' ");
+ ASSERTMSGLINE(770, normal, "VECReflect(): NULL VecPtr 'normal' ");
+ ASSERTMSGLINE(771, dst, "VECReflect(): NULL VecPtr 'dst' ");
+
+ uI.x = -src->x;
+ uI.y = -src->y;
+ uI.z = -src->z;
+
+ VECNormalize(&uI, &uI);
+ VECNormalize(normal, &uN);
+
+ cosA = VECDotProduct(&uI, &uN);
+ dst->x = (2.0f * uN.x * cosA) - uI.x;
+ dst->y = (2.0f * uN.y * cosA) - uI.y;
+ dst->z = (2.0f * uN.z * cosA) - uI.z;
+ VECNormalize(dst, dst);
+}
+
+f32 C_VECSquareDistance(const Vec* a, const Vec* b) {
+ Vec diff;
+
+ diff.x = a->x - b->x;
+ diff.y = a->y - b->y;
+ diff.z = a->z - b->z;
+ return (diff.z * diff.z) + ((diff.x * diff.x) + (diff.y * diff.y));
+}
+
+f32 PSVECSquareDistance(const __REGISTER Vec* a, const __REGISTER Vec* b) {
+ __REGISTER f32 v0yz, v1yz, v0xy, v1xy, dyz, dxy;
+ __REGISTER f32 sqdist;
+
+ asm {
+ psq_l v0yz, 0x4(a), 0, 0
+ psq_l v1yz, 0x4(b), 0, 0
+ ps_sub dyz, v0yz, v1yz
+ psq_l v0xy, 0x0(a), 0, 0
+ psq_l v1xy, 0x0(b), 0, 0
+ ps_mul dyz, dyz, dyz
+ ps_sub dxy, v0xy, v1xy
+ ps_madd sqdist, dxy, dxy, dyz
+ ps_sum0 sqdist, sqdist, dyz, dyz
+ }
+
+ return sqdist;
+}
+
+f32 C_VECDistance(const Vec* a, const Vec* b) {
+ return sqrtf(C_VECSquareDistance(a, b));
+}
+
+f32 PSVECDistance(const __REGISTER Vec* a, const __REGISTER Vec* b) {
+ __REGISTER f32 v0yz, v1yz, v0xy, v1xy, dyz, dxy;
+ __REGISTER f32 sqdist, rdist;
+ __REGISTER f32 nwork0, nwork1;
+ __REGISTER f32 c_half, c_three, c_zero;
+
+ asm {
+ psq_l v0yz, 0x4(a), 0, 0
+ psq_l v1yz, 0x4(b), 0, 0
+ ps_sub dyz, v0yz, v1yz
+ psq_l v0xy, 0x0(a), 0, 0
+ psq_l v1xy, 0x0(b), 0, 0
+ ps_mul dyz, dyz, dyz
+ ps_sub dxy, v0xy, v1xy
+ }
+
+ c_half = 0.5f;
+
+ asm {
+ ps_madd sqdist, dxy, dxy, dyz
+ fsubs c_zero, c_half, c_half
+ ps_sum0 sqdist, sqdist, dyz, dyz
+ fcmpu cr0, c_zero, sqdist
+ beq L_00000CBC
+ }
+
+ c_three = 3.0f;
+
+ asm {
+ frsqrte rdist, sqdist
+ fmuls nwork0, rdist, rdist
+ fmuls nwork1, rdist, c_half
+ fnmsubs nwork0, nwork0, sqdist, c_three
+ fmuls rdist, nwork0, nwork1
+ fmuls sqdist, sqdist, rdist
+ L_00000CBC:
+ }
+
+ return sqdist;
+}
diff --git a/src/revolution/sc/scapi.c b/src/revolution/sc/scapi.c
new file mode 100644
index 0000000000..9aa57e7ec1
--- /dev/null
+++ b/src/revolution/sc/scapi.c
@@ -0,0 +1,123 @@
+#include <revolution/sc.h>
+
+u8 SCGetAspectRatio(void) {
+ u8 aspect;
+
+ if (!SCFindU8Item(&aspect, SC_ITEM_ID_IPL_ASPECT_RATIO)) {
+ aspect = 0;
+ } else {
+ if (aspect != 1) {
+ aspect = 0;
+ }
+ }
+
+ return aspect;
+}
+
+s8 SCGetDisplayOffsetH(void) {
+ s8 offset;
+
+ if (!SCFindS8Item(&offset, SC_ITEM_ID_IPL_DISPLAY_OFFSET_H)) {
+ offset = 0;
+ } else {
+ if (offset < -32) {
+ offset = -32;
+ } else if (offset > 32) {
+ offset = 32;
+ }
+ }
+
+ offset &= ~1;
+ return offset;
+}
+
+u8 SCGetEuRgb60Mode(void) {
+ u8 mode;
+
+ if (!SCFindU8Item(&mode, SC_ITEM_ID_IPL_EURGB60_MODE)) {
+ mode = 0;
+ } else {
+ if (mode != 1) {
+ mode = 0;
+ }
+ }
+
+ return mode;
+}
+
+BOOL SCGetIdleMode(SCIdleModeInfo* info) {
+ return SCFindByteArrayItem(info, sizeof(*info), SC_ITEM_ID_IPL_IDLE_MODE);
+}
+
+u8 SCGetLanguage(void) {
+ u8 lang;
+ s8 area;
+
+ if (!SCFindU8Item(&lang, SC_ITEM_ID_IPL_LANGUAGE)) {
+ area = SCGetProductArea();
+
+ if (area == 0) {
+ lang = SC_LANG_JAPANESE;
+ } else {
+ lang = SC_LANG_ENGLISH;
+ }
+ } else {
+ if (lang > SC_LANG_KOREAN) {
+ lang = SC_LANG_ENGLISH;
+ }
+ }
+
+ return lang;
+}
+
+u8 SCGetProgressiveMode(void) {
+ u8 mode;
+
+ if (!SCFindU8Item(&mode, SC_ITEM_ID_IPL_PROGRESSIVE_MODE)) {
+ mode = 0;
+ } else {
+ if (mode != 1) {
+ mode = 0;
+ }
+ }
+
+ return mode;
+}
+
+u8 SCGetScreenSaverMode(void) {
+ u8 mode;
+
+ if (!SCFindU8Item(&mode, SC_ITEM_ID_IPL_SCREEN_SAVER_MODE)) {
+ mode = 1;
+ } else {
+ if (mode != 1) {
+ mode = 0;
+ }
+ }
+
+ return mode;
+}
+
+u8 SCGetSoundMode(void) {
+ u8 mode;
+
+ if (!SCFindU8Item(&mode, SC_ITEM_ID_IPL_SOUND_MODE)) {
+ mode = 1;
+ } else {
+ if (mode != 0 && mode != 1 && mode != 2) {
+ mode = 1;
+ }
+ }
+
+ return mode;
+}
+
+u32 SCGetCounterBias(void) {
+ u32 bias;
+
+ if (!SCFindU32Item(&bias, SC_ITEM_ID_IPL_COUNTER_BIAS)) {
+ bias = 189388800;
+ }
+
+ return bias;
+}
diff --git a/src/revolution/sc/scapi_prdinfo.c b/src/revolution/sc/scapi_prdinfo.c
new file mode 100644
index 0000000000..820d24a4be
--- /dev/null
+++ b/src/revolution/sc/scapi_prdinfo.c
@@ -0,0 +1,136 @@
+#include <revolution/sc.h>
+#include <revolution/os.h>
+#include <string.h>
+
+typedef struct {
+ s8 area;
+ char string[4];
+} SCProductAreaAndString;
+
+static SCProductAreaAndString ProductAreaAndStringTbl[] = {
+ 0, "JPN",
+ 1, "USA",
+ 2, "EUR",
+ 3, "AUS",
+ 4, "BRA",
+ 5, "TWN",
+ 5, "ROC",
+ 6, "KOR",
+ 7, "HKG",
+ 8, "ASI",
+ 9, "LTN",
+ 10, "SAF",
+ 11, "CHN",
+ -1
+};
+
+BOOL __SCF1(const char* keyword, char* buf, u32 bufSize) {
+ BOOL result = FALSE;
+ u8* p = (u8*)OSPhysicalToCached(0x3800);
+ u32 size = 0x100, i, seed = 0x73b5dbfa, kwOffset = 0;
+ u8 data;
+ u32 bufOffset = 0;
+
+ for (i = 0; i < size; i++) {
+ data = p[i];
+
+ if (data != 0) {
+ data ^= seed;
+
+ if (keyword[kwOffset] == '\0') {
+ if (data == '=') {
+ result = TRUE;
+ break;
+ }
+ }
+
+ if (((keyword[kwOffset] ^ data) & 0xDF) == 0) {
+ kwOffset++;
+ }
+ else {
+ kwOffset = 0;
+ }
+ }
+
+ seed = (u32)((seed >> 31) | (seed << 1));
+ }
+
+ if (result) {
+ i++;
+
+ while (i < size && bufOffset < bufSize) {
+ seed = (u32)((seed >> 31) | (seed << 1));
+ data = p[i];
+
+ if (data != 0) {
+ data ^= seed;
+
+ if (data == '\x0d' || data == '\x0a') {
+ data = 0;
+ }
+ }
+
+ buf[bufOffset] = (char)data;
+ bufOffset++;
+ if (data == 0) {
+ return TRUE;
+ }
+
+ i++;
+ }
+ }
+
+ return FALSE;
+}
+
+BOOL SCGetProductAreaString(char* buf, u32 bufSize) {
+ return __SCF1("AREA", buf, bufSize);
+}
+
+s8 SCGetProductArea(void) {
+ char buf[4];
+ SCProductAreaAndString* p = ProductAreaAndStringTbl;
+
+ if (SCGetProductAreaString(buf, sizeof(buf))) {
+ while (p->area != -1) {
+ if (strcmp(p->string, buf) == 0) {
+ return p->area;
+ }
+
+ p++;
+ }
+ }
+
+ return -1;
+}
+
+typedef struct {
+ s8 game;
+ char string[3];
+} SCProductGameRegionAndString;
+
+static SCProductGameRegionAndString ProductGameRegionAndStringTbl[] = {
+ 0, "JP",
+ 1, "US",
+ 2, "EU",
+ 4, "KR",
+ 5, "CN",
+ -1
+};
+
+s8 SCGetProductGameRegion(void) {
+ char buf[3];
+ SCProductGameRegionAndString* p = ProductGameRegionAndStringTbl;
+
+ if (__SCF1("GAME", buf, sizeof(buf))) {
+ while (p->game != -1) {
+ if (strcmp(p->string, buf) == 0) {
+ return p->game;
+ }
+
+ p++;
+ }
+ }
+
+ return -1;
+}
diff --git a/src/revolution/sc/scsystem.c b/src/revolution/sc/scsystem.c
new file mode 100644
index 0000000000..9ddc1fb5c1
--- /dev/null
+++ b/src/revolution/sc/scsystem.c
@@ -0,0 +1,996 @@
+#include <revolution/types.h>
+#include <revolution/sc.h>
+#include <string.h>
+// #include <mem.h>
+
+#ifdef SDK_AUG2010
+#define BUILD_DATE "Aug 23 2010"
+#if DEBUG
+#define BUILD_TIME "17:28:26"
+#else
+#define BUILD_TIME "17:33:06"
+#endif
+#elif SDK_SEP2006
+#define BUILD_DATE "Sep 21 2006"
+#define BUILD_TIME "14:32:13"
+#endif
+
+#ifdef SDK_AUG2010
+#if DEBUG
+const char* __SCVersion = "<< RVL_SDK - SC \tdebug build: " BUILD_DATE " " BUILD_TIME " (0x4302_145) >>";
+#else
+const char* __SCVersion = "<< RVL_SDK - SC \trelease build: " BUILD_DATE " " BUILD_TIME " (0x4302_145) >>";
+#endif
+#elif SDK_SEP2006
+const char* __SCVersion = "<< RVL_SDK - SC \trelease build: " BUILD_DATE " " BUILD_TIME " (0x4200_60422) >>";
+#endif
+
+typedef struct {
+ char* name;
+ SCItemID id;
+} NameAndID;
+
+static NameAndID NameAndIDTbl[38] = {
+ "IPL.CB", SC_ITEM_ID_IPL_COUNTER_BIAS,
+ "IPL.AR", SC_ITEM_ID_IPL_ASPECT_RATIO,
+ "IPL.ARN", SC_ITEM_ID_IPL_AUTORUN_MODE,
+ "IPL.CD", SC_ITEM_ID_IPL_CONFIG_DONE,
+ "IPL.CD2", SC_ITEM_ID_IPL_CONFIG_DONE2,
+ "IPL.DH", SC_ITEM_ID_IPL_DISPLAY_OFFSET_H,
+ "IPL.E60", SC_ITEM_ID_IPL_EURGB60_MODE,
+ "IPL.EULA", SC_ITEM_ID_IPL_EULA,
+ "IPL.FRC", SC_ITEM_ID_IPL_FREE_CHANNEL_APP_COUNT,
+ "IPL.IDL", SC_ITEM_ID_IPL_IDLE_MODE,
+ "IPL.INC", SC_ITEM_ID_IPL_INSTALLED_CHANNEL_APP_COUNT,
+ "IPL.LNG", SC_ITEM_ID_IPL_LANGUAGE,
+ "IPL.NIK", SC_ITEM_ID_IPL_OWNER_NICKNAME,
+ "IPL.PC", SC_ITEM_ID_IPL_PARENTAL_CONTROL,
+ "IPL.PGS", SC_ITEM_ID_IPL_PROGRESSIVE_MODE,
+ "IPL.SSV", SC_ITEM_ID_IPL_SCREEN_SAVER_MODE,
+ "IPL.SADR", SC_ITEM_ID_IPL_SIMPLE_ADDRESS,
+ "IPL.SND", SC_ITEM_ID_IPL_SOUND_MODE,
+ "IPL.UPT", SC_ITEM_ID_IPL_UPDATE_TYPE,
+ "NET.CNF", SC_ITEM_ID_NET_CONFIG,
+ "NET.CTPC", SC_ITEM_ID_NET_CONTENT_RESTRICTIONS,
+ "NET.PROF", SC_ITEM_ID_NET_PROFILE,
+ "NET.WCPC", SC_ITEM_ID_NET_WC_RESTRICTION,
+ "NET.WCFG", SC_ITEM_ID_NET_WC_FLAGS,
+ "DEV.BTM", SC_ITEM_ID_DEV_BOOT_MODE,
+ "DEV.VIM", SC_ITEM_ID_DEV_VIDEO_MODE,
+ "DEV.CTC", SC_ITEM_ID_DEV_COUNTRY_CODE,
+ "DEV.DSM", SC_ITEM_ID_DEV_DRIVESAVING_MODE,
+ "BT.DINF", SC_ITEM_ID_BT_DEVICE_INFO,
+ "BT.CDIF", SC_ITEM_ID_BT_CMPDEV_INFO,
+ "BT.SENS", SC_ITEM_ID_BT_DPD_SENSIBILITY,
+ "BT.SPKV", SC_ITEM_ID_BT_SPEAKER_VOLUME,
+ "BT.MOT", SC_ITEM_ID_BT_MOTOR_MODE,
+ "BT.BAR", SC_ITEM_ID_BT_SENSOR_BAR_POSITION,
+ "DVD.CNF", SC_ITEM_ID_DVD_CONFIG,
+ "WWW.RST", SC_ITEM_ID_WWW_RESTRICTION,
+ "MPLS.MOVIE", SC_ITEM_ID_MOTION_PLUS_MOVIE,
+ "IPL.TID", SC_ITEM_ID_TEMPORARY_TITLE_ID,
+};
+
+static const char ConfDirName[] = "/shared2/sys";
+static const char ConfFileName[] = "/shared2/sys/SYSCONF";
+static const char ProductInfoFileName[] = "/title/00000001/00000002/data/setting.txt";
+
+static u8 ConfBuf[0x4000] ATTRIBUTE_ALIGN(32);
+static u8 ConfBufForFlush[0x4000] ATTRIBUTE_ALIGN(32);
+
+static u8 Initialized;
+static u8 DirtyFlag;
+static u8 IsDevKit;
+static vu8 BgJobStatus = 0;
+static u32 ItemIDOffsetTblOffset;
+static u32 ItemIDMaxPlus1;
+static u32 ItemNumTotal;
+static u32 ItemRestSize;
+static SCControl Control;
+
+static void SetBgJobStatus(u32 status);
+static u32 ParseConfBuf(u8* bufp, u32 bufSize);
+static void ClearConfBuf(u8* bufp);
+static BOOL ParseGetBEValue(const u8* bufp, const u8* bufEndp, u32* varp, u32 varSize);
+static void OpenCallbackFromReload(s32, NANDCommandBlock*);
+static void ReadCallbackFromReload(s32 result, NANDCommandBlock* block);
+static void CloseCallbackFromReloadError(s32 result, NANDCommandBlock* block);
+static void CloseCallbackFromReload(s32 result, NANDCommandBlock* block);
+static void ErrorFromReload(s32 result);
+static void FinishFromReload(void);
+static BOOL UnpackItem(const u8*, SCItem*);
+static void MyNandCallback(s32, NANDCommandBlock* block);
+static void FinishFromFlush(void);
+static void ErrorFromFlush(void);
+
+static BOOL __SCIsDirty(void);
+static void __SCSetDirtyFlag(void);
+static void __SCClearDirtyFlag(void);
+static u8* __SCGetConfBuf(void);
+static u32 __SCGetConfBufSize(void);
+
+void SCInit(void) {
+ BOOL enabled = OSDisableInterrupts();
+
+ if (Initialized) {
+ OSRestoreInterrupts(enabled);
+ return;
+ }
+
+ Initialized = TRUE;
+ SetBgJobStatus(1);
+ OSRestoreInterrupts(enabled);
+
+ OSRegisterVersion(__SCVersion);
+ OSInitThreadQueue(&Control.threadQueue);
+
+ if (OSGetConsoleType() & 0x10000000) {
+ IsDevKit = TRUE;
+ }
+
+ if (NANDInit() != 0 || SCReloadConfFileAsync(__SCGetConfBuf(), __SCGetConfBufSize(), NULL) != 0) {
+ SetBgJobStatus(2);
+ }
+}
+
+static void SetBgJobStatus(u32 status) {
+ BgJobStatus = (u8)status;
+}
+
+u32 SCCheckStatus(void) {
+ BOOL enabled;
+ u32 ret;
+
+ enabled = OSDisableInterrupts();
+ ret = BgJobStatus;
+
+ if (ret == 3) {
+ SetBgJobStatus(1);
+ OSRestoreInterrupts(enabled);
+
+ if (ParseConfBuf(Control.reloadBufp[0], Control.reloadedSize[0]) == 0) {
+ enabled = OSDisableInterrupts();
+
+ if (__SCGetConfBuf() != Control.reloadBufp[0]) {
+ memcpy(__SCGetConfBuf(), Control.reloadBufp[0], __SCGetConfBufSize());
+ }
+
+ __SCClearDirtyFlag();
+ OSRestoreInterrupts(enabled);
+ } else {
+ enabled = OSDisableInterrupts();
+ ClearConfBuf(Control.reloadBufp[0]);
+ __SCClearDirtyFlag();
+ OSRestoreInterrupts(enabled);
+ }
+
+ ret = 0;
+ SetBgJobStatus(ret);
+ } else {
+ OSRestoreInterrupts(enabled);
+ }
+
+ return ret;
+}
+
+s32 SCReloadConfFileAsync(u8* bufp, u32 bufSize, SCReloadConfFileCallback callback) {
+ u32 i;
+
+ ASSERTLINE(392, bufp != NULL);
+ ASSERTLINE(393, ((u32)bufp & 0x1f) == 0);
+
+ if (bufSize < __SCGetConfBufSize()) {
+ return -128;
+ }
+
+ SetBgJobStatus(1);
+ Control.reloadCallback = callback;
+ Control.reloadResult = 0;
+ Control.reloadFileCount = 0;
+
+ for (i = 0; i < 2; i++) {
+ Control.reloadedSize[i] = 0;
+ }
+
+ Control.reloadFileName[0] = ConfFileName;
+ Control.reloadFileName[1] = ProductInfoFileName;
+ Control.reloadBufp[0] = bufp;
+ Control.reloadBufp[1] = (u8*)OSPhysicalToCached(0x3800);
+ Control.reloadSizeExpected[0] = __SCGetConfBufSize();
+ Control.reloadSizeExpected[1] = 0x100;
+ ClearConfBuf(bufp);
+ ItemIDOffsetTblOffset = 0;
+ ItemNumTotal = 0;
+ ItemRestSize = 0;
+ Control.nandNeedClose = FALSE;
+ return NANDPrivateOpenAsync(Control.reloadFileName[Control.reloadFileCount],
+ &Control.nandFileInfo, 1, OpenCallbackFromReload,
+ &Control.nandCommandBlock);
+}
+
+static void OpenCallbackFromReload(s32 result, NANDCommandBlock* block) {
+ if (result == 0) {
+ Control.nandNeedClose = TRUE;
+
+ if (NANDReadAsync(&Control.nandFileInfo, Control.reloadBufp[Control.reloadFileCount],
+ Control.reloadSizeExpected[Control.reloadFileCount],
+ ReadCallbackFromReload, &Control.nandCommandBlock) == 0)
+ {
+ return;
+ }
+ }
+
+ ErrorFromReload(result);
+}
+
+static void ReadCallbackFromReload(s32 result, NANDCommandBlock* block) {
+ if (result == Control.reloadSizeExpected[Control.reloadFileCount]) {
+ Control.reloadedSize[Control.reloadFileCount] = (u32)result;
+ Control.nandNeedClose = FALSE;
+
+ if (NANDCloseAsync(&Control.nandFileInfo, CloseCallbackFromReload,
+ &Control.nandCommandBlock) == 0)
+ {
+ return;
+ }
+ }
+
+ ErrorFromReload((s32)((result == 0) ? -128 : result));
+}
+
+static void CloseCallbackFromReload(s32 result, NANDCommandBlock* block) {
+ if (result == 0) {
+ FinishFromReload();
+ return;
+ }
+
+ ErrorFromReload(result);
+}
+
+void FinishFromReload(void) {
+ u32 status;
+
+nextFile:
+ Control.reloadFileCount++;
+
+ if (Control.reloadFileCount < 2) {
+ Control.nandNeedClose = FALSE;
+
+ if (NANDPrivateOpenAsync(Control.reloadFileName[Control.reloadFileCount],
+ &Control.nandFileInfo, 1, OpenCallbackFromReload,
+ &Control.nandCommandBlock) == 0)
+ {
+ return;
+ }
+
+ goto nextFile;
+ }
+
+ switch (Control.reloadResult) {
+ case 0:
+ status = 3;
+ break;
+ default:
+ case -12:
+ ClearConfBuf(Control.reloadBufp[0]);
+ Control.reloadedSize[0] = Control.reloadSizeExpected[0];
+ status = 3;
+ break;
+ }
+
+ *(u8*)((u8*)OSPhysicalToCached(0x3800) + 0x100 - 1) = '\0';
+
+ if (Control.reloadCallback) {
+ Control.reloadCallback(Control.reloadResult);
+ Control.reloadCallback = NULL;
+ }
+
+ SetBgJobStatus(status);
+}
+
+static void ErrorFromReload(s32 result) {
+ if (Control.reloadFileCount == 0) {
+ Control.reloadResult = result;
+ }
+
+ Control.reloadedSize[Control.reloadFileCount] = 0;
+
+ if (Control.nandNeedClose) {
+ if (NANDCloseAsync(&Control.nandFileInfo, CloseCallbackFromReloadError,
+ &Control.nandCommandBlock) == 0)
+ {
+ return;
+ }
+ }
+
+ FinishFromReload();
+}
+
+static void CloseCallbackFromReloadError(s32 result, NANDCommandBlock* block) {
+ FinishFromReload();
+}
+
+static void ClearConfBuf(u8* bufp) {
+ u32 size = __SCGetConfBufSize();
+ memset(bufp, 0, size);
+
+ if (size > 12) {
+ memcpy(bufp, "SCv0", 4);
+ memcpy(bufp + (size - 4), "SCed", 4);
+ *((u16*)bufp + 3) = 8;
+ }
+}
+
+u32 ParseConfBuf(u8* bufp, u32 bufSize) {
+ u8 *bufTop, *bufEndp;
+ u32 numItems, loopItem;
+ SCItem item;
+ u32 itemOffset;
+ u16* itemOfsp;
+ u16* runtimeRefp;
+ u32 restSize;
+ NameAndID* tblp = NameAndIDTbl;
+ NameAndID* tblEndp;
+ char* name;
+
+ ASSERTLINE(576, bufp != NULL);
+ ASSERTLINE(577, ((u32)bufp & 0x1f) == 0);
+
+ if ((bufSize < 12) || (bufSize > __SCGetConfBufSize())) {
+ goto error;
+ }
+
+ bufTop = bufp;
+ bufEndp = bufp + bufSize - 4;
+ ItemIDMaxPlus1 = SC_ITEM_ID_MAX_PLUS1;
+
+ if (memcmp(bufp, "SCv0", 4) || memcmp(bufEndp, "SCed", 4)) {
+ goto error;
+ }
+
+ bufp += 4;
+
+ if (bufSize < __SCGetConfBufSize()) {
+ u32 expandSize = __SCGetConfBufSize() - bufSize;
+ memset(bufEndp, 0, expandSize);
+ bufEndp += expandSize;
+ memcpy(bufEndp, "SCed", 4);
+ }
+
+ if (ParseGetBEValue(bufp, bufEndp, &numItems, 2) == FALSE) {
+ goto error;
+ }
+
+ bufp += 2;
+ itemOfsp = (u16*)bufp;
+ itemOffset = ((u8*)(itemOfsp + numItems + 1) - bufTop);
+
+ for (loopItem = 0; loopItem < numItems; loopItem++) {
+ if (itemOffset > bufSize || ((u8*)&itemOfsp[loopItem] - bufTop) > bufSize) {
+ goto error;
+ }
+
+ if (itemOffset != itemOfsp[loopItem] || UnpackItem(bufTop + itemOffset, &item) == FALSE) {
+ goto error;
+ }
+
+ itemOffset += item.packedSize;
+ }
+
+ if (itemOffset > bufSize) {
+ goto error;
+ }
+
+ if (itemOffset != itemOfsp[loopItem]) {
+ goto error;
+ }
+
+ runtimeRefp = (u16*)(bufEndp - 2 * (SC_ITEM_ID_MAX_PLUS1));
+
+ if ((u8*)(bufTop + itemOffset) > (u8*)runtimeRefp) {
+ goto error;
+ }
+
+ restSize = (u32)((u8*)runtimeRefp - (u8*)(bufTop + itemOffset));
+ memset(runtimeRefp, 0, (u32)(bufEndp - (u8*)runtimeRefp));
+ runtimeRefp = (u16*)(bufEndp - 2);
+ tblEndp = tblp + ItemIDMaxPlus1;
+
+ while (tblp < tblEndp && (name = tblp->name) != NULL) {
+ u32 nameLen;
+ u8* p;
+
+ nameLen = strlen(name);
+
+ for (loopItem = 0; loopItem < numItems; loopItem++) {
+ p = bufTop + itemOfsp[loopItem];
+
+ if (nameLen == (u32)(((*p) & 0x1F) + 1) &&
+ memcmp(name, p + sizeof(SCType), nameLen) == 0)
+ {
+ runtimeRefp[-tblp->id] = (u16)((u8*)(&itemOfsp[loopItem]) - bufTop);
+ break;
+ }
+ }
+
+ tblp++;
+ }
+
+ ItemIDOffsetTblOffset = (u32)((u8*)runtimeRefp - bufTop);
+ ItemNumTotal = numItems;
+ ItemRestSize = restSize;
+ return 0;
+
+error:
+ return 2;
+}
+
+static BOOL ParseGetBEValue(const u8* bufp, const u8* bufEndp, u32* varp, u32 varSize) {
+ u32 value = 0;
+
+ if ((bufp + varSize) > bufEndp) {
+ return FALSE;
+ }
+
+ while (varSize) {
+ value = (value << 8) | *bufp;
+ bufp++;
+ varSize--;
+ }
+
+ *varp = value;
+ return TRUE;
+}
+
+static BOOL UnpackItem(const u8* bufp, SCItem* itemp) {
+ SCType type;
+
+ ASSERTLINE(698, bufp != NULL);
+ ASSERTLINE(699, itemp != NULL);
+
+ memset(itemp, 0, sizeof(SCItem));
+ type = (u8)(*bufp & 0xE0);
+ itemp->name = (char*)(bufp + sizeof(SCType));
+ itemp->nameLen = (u32)((*bufp & 0x1F) + 1);
+ itemp->data = (u8*)(bufp + sizeof(SCType) + itemp->nameLen);
+
+ switch (type) {
+ case 0x60:
+ case 0xE0:
+ itemp->dataSize = sizeof(u8);
+ break;
+ case 0x80:
+ itemp->dataSize = sizeof(u16);
+ break;
+ case 0xA0:
+ itemp->dataSize = sizeof(u32);
+ break;
+ case 0xC0:
+ itemp->dataSize = sizeof(u64);
+ break;
+ case 0x40:
+ itemp->dataSize = (u32)(*(itemp->data) + 1);
+ itemp->data++;
+ itemp->packedSize++;
+ break;
+ case 0x20:
+ itemp->dataSize = (u32)((((*(itemp->data)) << 8) | (*(itemp->data + 1))) + 1);
+ itemp->data += 2;
+ itemp->packedSize += 2;
+ break;
+ default:
+ goto err;
+ }
+
+ if (type == 0x40 || type == 0x20) {
+ itemp->typeByteArray = 0x40;
+ } else {
+ itemp->typeInteger = type;
+ memcpy(&itemp->integer, itemp->data, itemp->dataSize);
+ }
+
+ itemp->packedSize += sizeof(SCType) + itemp->nameLen + itemp->dataSize;
+
+ {
+ const char* typeString;
+ switch (type) {
+ case 0x60:
+ typeString = "U8/S8";
+ break;
+ case 0xE0:
+ typeString = "BOOL";
+ break;
+ case 0x80:
+ typeString = "U16/S16";
+ break;
+ case 0xA0:
+ typeString = "U32/S32";
+ break;
+ case 0xC0:
+ typeString = "U64/S64";
+ break;
+ case 0x40:
+ case 0x20:
+ typeString = "ARRAY";
+ break;
+ default:
+ typeString = "UNKNOWN";
+ }
+ }
+
+err:
+ return (itemp->dataSize != 0);
+}
+
+static BOOL FindItemByID(SCItemID id, SCItem* itemp) {
+ u8* conf = __SCGetConfBuf();
+ u16* refp;
+
+ if (id < ItemIDMaxPlus1 && ItemIDOffsetTblOffset != 0) {
+ refp = (u16*)(conf + ItemIDOffsetTblOffset);
+ if (refp[-id] != 0) {
+ return UnpackItem(conf + *(u16*)(conf + refp[-id]), itemp);
+ }
+ }
+
+ return FALSE;
+}
+
+void DeleteItemByID(SCItemID id) {
+ u8* conf = __SCGetConfBuf();
+ u32 targetRef, initialTopOfFreeSpace, moveSize, shrinkSize, i;
+ u16 *refp, *itemOfsTop, *itemOfsTargetp, *itemOfsEndp, *itemOfsp;
+
+ if (id < ItemIDMaxPlus1 && ItemIDOffsetTblOffset != 0) {
+ refp = (u16*)(conf + ItemIDOffsetTblOffset);
+ targetRef = refp[-id];
+
+ if (targetRef != 0 && ItemNumTotal != 0) {
+ itemOfsTop = (u16*)(conf + 4);
+ itemOfsTargetp = (u16*)(conf + targetRef);
+ itemOfsEndp = itemOfsTop + ItemNumTotal;
+ initialTopOfFreeSpace = *itemOfsEndp;
+ shrinkSize = 2 + (itemOfsTargetp[1] - itemOfsTargetp[0]);
+
+ moveSize = *itemOfsTargetp - (targetRef + 4);
+ memmove(conf + targetRef, conf + targetRef + 4, moveSize);
+
+ for (itemOfsp = itemOfsEndp - 1; itemOfsp >= itemOfsTop; itemOfsp--) {
+ if (itemOfsp < itemOfsTargetp) {
+ *itemOfsp -= 2;
+ } else {
+ *itemOfsp -= shrinkSize;
+ }
+ }
+
+ memmove(conf + itemOfsTargetp[0], conf + (itemOfsTargetp[0] + shrinkSize),
+ initialTopOfFreeSpace - (itemOfsTargetp[0] + shrinkSize));
+ memset(conf + (initialTopOfFreeSpace - shrinkSize), 0, shrinkSize);
+
+ for (i = 0; i < ItemIDMaxPlus1; i++) {
+ if (refp[-i] < targetRef) {
+ // empty branch?
+ } else if (refp[-i] > targetRef) {
+ refp[-i] -= 2;
+ } else {
+ refp[-i] = 0;
+ }
+ }
+ ItemRestSize += shrinkSize;
+ ItemNumTotal--;
+ *(u16*)(conf + 4) = (u16)ItemNumTotal;
+ __SCSetDirtyFlag();
+ }
+ }
+}
+
+BOOL CreateItemByID(SCItemID id, SCType type, const u8* data, u32 size) {
+ u8* conf = __SCGetConfBuf();
+ u8* p;
+ u32 nameLen;
+ u32 packedSize = sizeof(SCType);
+ NameAndID* tblp = NameAndIDTbl;
+ char* name;
+ u32 topOfFreeSpace;
+ u16* refp;
+ u16* itemOfsTop;
+ u16* itemOfsEndp;
+ u16* itemOfsp;
+
+ if (id < ItemIDMaxPlus1 && data != NULL && ItemNumTotal < 0xFFFF && ItemIDOffsetTblOffset != 0) {
+ switch (type) {
+ case 0xE0:
+ case 0x60:
+ size = 1;
+ break;
+ case 0x80:
+ size = 2;
+ break;
+ case 0xA0:
+ size = 4;
+ break;
+ case 0xC0:
+ size = 8;
+ break;
+ case 0x40:
+ if (size == 0 || size > 65536) {
+ goto error;
+ }
+ if (size > 256) {
+ packedSize += 2;
+ type = 0x20;
+ } else {
+ packedSize += 1;
+ }
+ break;
+
+ default:
+ goto error;
+ }
+
+ packedSize += size;
+
+ while ((name = tblp->name) != NULL) {
+ if (tblp->id == id) {
+ break;
+ }
+
+ tblp++;
+ }
+
+ if (name == NULL) {
+ goto error;
+ }
+
+ nameLen = strlen(name);
+ if (nameLen > 32) {
+ goto error;
+ }
+
+ packedSize += nameLen;
+ if (ItemRestSize < (2 + packedSize)) {
+ goto error;
+ }
+
+ itemOfsTop = (u16*)(conf + 6);
+ itemOfsEndp = itemOfsTop + ItemNumTotal;
+ topOfFreeSpace = *itemOfsEndp;
+ memmove(conf + (itemOfsTop[0] + 2), conf + itemOfsTop[0], topOfFreeSpace - itemOfsTop[0]);
+
+ itemOfsp = itemOfsTop;
+ do {
+ *itemOfsp += 2;
+ itemOfsp++;
+ } while (itemOfsp <= itemOfsEndp);
+
+ topOfFreeSpace = *itemOfsEndp;
+ p = (u8*)(conf + topOfFreeSpace);
+
+ *p = (SCType)(type | (nameLen - 1));
+ memcpy(p + sizeof(SCType), name, nameLen);
+ p += sizeof(SCType) + nameLen;
+ if (type == 0x40) {
+ *p++ = (u8)(size - 1);
+ } else if (type == 0x20) {
+ *p++ = (u8)((size - 1) >> 8);
+ *p++ = (u8)(size - 1);
+ }
+ memcpy(p, data, size);
+ p += size;
+
+ refp = (u16*)(conf + ItemIDOffsetTblOffset);
+ refp[-id] = (u16)((u8*)itemOfsEndp - conf);
+ itemOfsEndp[1] = (u16)(itemOfsEndp[0] + packedSize);
+ ItemRestSize -= 2 + packedSize;
+ ItemNumTotal++;
+ *(u16*)(conf + 4) = (u16)ItemNumTotal;
+ __SCSetDirtyFlag();
+
+ return TRUE;
+ }
+
+error:
+ return FALSE;
+}
+
+BOOL SCFindByteArrayItem(void* data, u32 size, SCItemID id) {
+ SCItem item;
+ BOOL result = FALSE;
+ BOOL enabled = OSDisableInterrupts();
+
+ if (data != NULL && FindItemByID(id, &item) && item.typeByteArray != 0 && item.dataSize == size) {
+ memcpy(data, item.data, size);
+ result = TRUE;
+ }
+
+ OSRestoreInterrupts(enabled);
+ return result;
+}
+
+BOOL SCReplaceByteArrayItem(const void* data, u32 size, SCItemID id) {
+ SCItem item;
+ BOOL result = FALSE;
+ BOOL enabled = OSDisableInterrupts();
+
+ if (data != NULL) {
+ if (FindItemByID(id, &item)) {
+ if (item.typeByteArray != 0 && item.dataSize == size) {
+ if (memcmp(item.data, data, size) != 0) {
+ memcpy(item.data, data, size);
+ __SCSetDirtyFlag();
+ }
+
+ result = TRUE;
+ goto finish;
+ } else {
+ DeleteItemByID(id);
+ }
+ }
+ result = CreateItemByID(id, 0x40, data, size);
+ }
+
+finish:
+ OSRestoreInterrupts(enabled);
+ return result;
+}
+
+BOOL SCFindIntegerItem(void* data, SCItemID id, SCType type) {
+ SCItem item;
+ BOOL result = FALSE;
+ BOOL enabled = OSDisableInterrupts();
+
+ ASSERTLINE(1126, data != NULL);
+ ASSERTLINE(1127, type != 0);
+
+ if (FindItemByID(id, &item) && item.typeInteger == type) {
+ memcpy(data, item.data, item.dataSize);
+ result = TRUE;
+ }
+
+ OSRestoreInterrupts(enabled);
+ return result;
+}
+
+BOOL SCReplaceIntegerItem(const void* data, SCItemID id, SCType type) {
+ SCItem item;
+ BOOL result = FALSE;
+ BOOL enabled = OSDisableInterrupts();
+
+ if (FindItemByID(id, &item)) {
+ if (item.typeInteger == type) {
+ if (memcmp(item.data, data, item.dataSize) != 0) {
+ memcpy(item.data, data, item.dataSize);
+ __SCSetDirtyFlag();
+ }
+
+ result = TRUE;
+ goto finish;
+ } else {
+ DeleteItemByID(id);
+ }
+ }
+
+ result = CreateItemByID(id, type, data, 0);
+
+finish:
+ OSRestoreInterrupts(enabled);
+ return result;
+}
+
+BOOL SCFindU8Item(u8* data, SCItemID id) {
+ return SCFindIntegerItem(data, id, 0x60);
+}
+
+BOOL SCFindS8Item(s8* data, SCItemID id) {
+ return SCFindIntegerItem(data, id, 0x60);
+}
+
+BOOL SCFindU32Item(u32* data, SCItemID id) {
+ return SCFindIntegerItem(data, id, 0xA0);
+}
+
+BOOL SCReplaceU8Item(u8 data, SCItemID id) {
+ return SCReplaceIntegerItem(&data, id, 0x60);
+}
+
+static void __SCFlushSyncCallback(u32 status) {
+ OSWakeupThread(&Control.threadQueue);
+}
+
+void SCFlushAsync(SCFlushCallback callback) {
+ SCControl* ctrl;
+ BOOL enabled;
+ u8 status;
+
+ ctrl = &Control;
+ enabled = OSDisableInterrupts();
+ status = BgJobStatus;
+
+ if (status == 0) {
+ SetBgJobStatus(1);
+
+ if (callback == NULL) {
+ callback = __SCFlushSyncCallback;
+ }
+
+ ctrl->flushCallback = callback;
+ ctrl->flushResult = 0;
+ ctrl->nandNeedClose = FALSE;
+ ctrl->flushSize = __SCGetConfBufSize();
+
+ if (!__SCIsDirty()) {
+ OSRestoreInterrupts(enabled);
+ FinishFromFlush();
+ } else {
+ __SCClearDirtyFlag();
+ memcpy(ConfBufForFlush, __SCGetConfBuf(), __SCGetConfBufSize());
+
+ OSRestoreInterrupts(enabled);
+ ctrl->nandStep = 0;
+
+ if (NANDPrivateGetTypeAsync(ConfFileName, &ctrl->u.nandType, MyNandCallback, &ctrl->nandCommandBlock) != NAND_RESULT_OK) {
+ ErrorFromFlush();
+ }
+ }
+ } else {
+ if (callback != NULL) {
+ callback(status == 1 ? status : 2);
+ }
+
+ OSRestoreInterrupts(enabled);
+ }
+}
+
+void MyNandCallback(s32 result, NANDCommandBlock* block) {
+ SCControl* ctrl = &Control;
+
+ switch (ctrl->nandStep) {
+ case 0:
+ if (result == NAND_RESULT_OK && ctrl->u.nandType == 1) {
+ ctrl->nandStep = 1;
+
+ if (NANDPrivateGetStatusAsync(ConfFileName, &ctrl->u.nandStatus, MyNandCallback, &ctrl->nandCommandBlock) != NAND_RESULT_OK) {
+ break;
+ }
+ } else {
+ goto _case_1_lbl;
+ }
+ return;
+ case 1:
+ if (result == NAND_RESULT_OK && ctrl->u.nandStatus.permission == NAND_PERM_RWALL) {
+ goto _case_5_lbl;
+ }
+ _case_1_lbl:
+ ctrl->nandStep = 2;
+
+ if (NANDPrivateDeleteAsync(ConfFileName, MyNandCallback, &ctrl->nandCommandBlock) != NAND_RESULT_OK) {
+ break;
+ }
+ return;
+ case 2:
+ ctrl->nandStep = 3;
+
+ if (NANDPrivateGetTypeAsync(ConfDirName, &ctrl->u.nandType, MyNandCallback, &ctrl->nandCommandBlock) != NAND_RESULT_OK) {
+ break;
+ }
+ return;
+ case 3:
+ if (result == NAND_RESULT_OK && ctrl->u.nandType == 2) {
+ goto _case_4_lbl;
+ }
+
+ ctrl->nandStep = 4;
+
+ if (NANDPrivateCreateDirAsync(ConfDirName, NAND_PERM_RWALL, 0, MyNandCallback, &ctrl->nandCommandBlock) != NAND_RESULT_OK) {
+ break;
+ }
+ return;
+ case 4:
+ _case_4_lbl:
+ ctrl->nandStep = 5;
+ if (NANDPrivateCreateAsync(ConfFileName, NAND_PERM_RWALL, 0, MyNandCallback, &ctrl->nandCommandBlock) != NAND_RESULT_OK) {
+ break;
+ }
+ return;
+ case 5:
+ _case_5_lbl:
+ ctrl->nandStep = 6;
+ if (NANDPrivateOpenAsync(ConfFileName, &ctrl->nandFileInfo, NAND_ACCESS_WRITE, MyNandCallback, &ctrl->nandCommandBlock) != NAND_RESULT_OK) {
+ break;
+ }
+ return;
+ case 6:
+ if (result != NAND_RESULT_OK) {
+ break;
+ }
+
+ ctrl->nandNeedClose = TRUE;
+ ctrl->nandStep = 7;
+
+ if (NANDWriteAsync(&ctrl->nandFileInfo, ConfBufForFlush, ctrl->flushSize, MyNandCallback, &ctrl->nandCommandBlock) != NAND_RESULT_OK) {
+ break;
+ }
+ return;
+ case 7:
+ if (result != ctrl->flushSize) {
+ break;
+ }
+
+ ctrl->nandNeedClose = FALSE;
+ ctrl->nandStep = 8;
+
+ if (NANDCloseAsync(&ctrl->nandFileInfo, MyNandCallback, &ctrl->nandCommandBlock) != NAND_RESULT_OK) {
+ break;
+ }
+ return;
+ case 8:
+ if (result != NAND_RESULT_OK) {
+ break;
+ }
+ case 9:
+ FinishFromFlush();
+ default:
+ return;
+ }
+
+ ErrorFromFlush();
+}
+
+static void FinishFromFlush(void) {
+ SCControl* ctrl;
+ SCFlushCallback callback;
+
+ ctrl = &Control;
+ if (ctrl->flushResult != 0) {
+ __SCSetDirtyFlag();
+ }
+
+ callback = ctrl->flushCallback;
+ if (callback) {
+ ctrl->flushCallback = NULL;
+ callback(ctrl->flushResult);
+
+ if (ctrl->threadQueue.head != NULL) {
+ OSWakeupThread(&ctrl->threadQueue);
+ }
+ }
+
+ SetBgJobStatus(ctrl->flushResult);
+}
+
+static void ErrorFromFlush(void) {
+ SCControl* ctrl;
+
+ ctrl = &Control;
+ ctrl->flushResult = 2;
+
+ if (ctrl->nandNeedClose) {
+ ctrl->nandStep = 9;
+
+ if (NANDCloseAsync(&ctrl->nandFileInfo, MyNandCallback, &ctrl->nandCommandBlock) == NAND_RESULT_OK) {
+ return;
+ }
+ }
+
+ FinishFromFlush();
+}
+
+static BOOL __SCIsDirty(void) {
+ return DirtyFlag ? TRUE : FALSE;
+}
+
+static void __SCSetDirtyFlag(void) {
+ DirtyFlag = TRUE;
+}
+
+static void __SCClearDirtyFlag(void) {
+ DirtyFlag = FALSE;
+}
+
+static u8* __SCGetConfBuf(void) {
+ return ConfBuf;
+}
+
+static u32 __SCGetConfBufSize(void) {
+ return sizeof(ConfBuf);
+}
diff --git a/src/revolution/si/SIBios.c b/src/revolution/si/SIBios.c
new file mode 100644
index 0000000000..65f24d99eb
--- /dev/null
+++ b/src/revolution/si/SIBios.c
@@ -0,0 +1,821 @@
+#include <revolution/si.h>
+#include <revolution/vi.h>
+
+#include "__os.h"
+
+#define ROUND(n, a) (((u32)(n) + (a)-1) & ~((a)-1))
+
+#ifdef SDK_AUG2010
+#define BUILD_DATE "Aug 23 2010"
+#if DEBUG
+#define BUILD_TIME "17:27:56"
+#else
+#define BUILD_TIME "17:33:06"
+#endif
+#elif SDK_SEP2006
+#define BUILD_DATE "Sep 21 2006"
+#define BUILD_TIME "14:32:13"
+#endif
+
+#ifdef SDK_AUG2010
+#if DEBUG
+const char* __SIVersion = "<< RVL_SDK - SI \tdebug build: "BUILD_DATE" "BUILD_TIME" (0x4302_145) >>";
+#else
+const char* __SIVersion = "<< RVL_SDK - SI \trelease build: "BUILD_DATE" "BUILD_TIME" (0x4302_145) >>";
+#endif
+#elif SDK_SEP2006
+const char* __SIVersion = "<< RVL_SDK - SI \trelease build: "BUILD_DATE" "BUILD_TIME" (0x4200_60422) >>";
+#endif
+
+static SIControl Si = {
+ /* chan */ -1,
+ /* poll */ 0,
+ /* inputBytes */ 0,
+ /* input */ NULL,
+ /* callback */ NULL
+};
+
+static SIPacket Packet[4];
+static OSAlarm Alarm[4];
+static u32 Type[4] = { SI_ERROR_NO_RESPONSE, SI_ERROR_NO_RESPONSE, SI_ERROR_NO_RESPONSE, SI_ERROR_NO_RESPONSE };
+static OSTime TypeTime[4];
+static OSTime XferTime[4];
+static SITypeCallback TypeCallback[4][4];
+static __OSInterruptHandler RDSTHandler[4];
+static BOOL InputBufferValid[4];
+static u32 InputBuffer[4][2];
+static volatile u32 InputBufferVcount[4];
+
+u32 __PADFixBits;
+
+// prototypes
+static u32 CompleteTransfer();
+static void SITransferNext(s32 chan);
+static void SIInterruptHandler(__OSInterrupt interrupt, OSContext* context);
+static int __SITransfer(s32 chan, void* output, u32 outputBytes, void* input, u32 inputBytes, SICallback callback);
+static void AlarmHandler(OSAlarm* alarm, OSContext* context);
+static void GetTypeCallback(s32 chan, u32 error, OSContext* context);
+static int SIGetResponseRaw(s32 chan);
+
+BOOL SIBusy(void) {
+ return (Si.chan != -1) ? TRUE : FALSE;
+}
+
+BOOL SIIsChanBusy(s32 chan) {
+ return Packet[chan].chan != -1 || Si.chan == chan;
+}
+
+static void SIClearTCInterrupt(void) {
+ u32 reg;
+
+ reg = __SIRegs[SI_COMCSR_IDX];
+ reg |= SI_COMCSR_TCINT_MASK;
+ reg &= ~SI_COMCSR_TSTART_MASK;
+ __SIRegs[SI_COMCSR_IDX] = reg;
+}
+
+static u32 CompleteTransfer(void) {
+ u32 sr;
+ u32 i;
+ u32 rLen;
+ u8* input;
+
+ sr = __SIRegs[SI_STATUS_IDX];
+ SIClearTCInterrupt();
+
+ if (Si.chan != -1) {
+ XferTime[Si.chan] = __OSGetSystemTime();
+ input = Si.input;
+ rLen = Si.inputBytes / sizeof(u32);
+ for (i = 0; i < rLen; i++) {
+ *(u32*)input = __SIRegs[0x20 + i];
+ input += 4;
+ }
+
+ rLen = Si.inputBytes & 3;
+ if (rLen != 0) {
+ u32 temp = __SIRegs[i + 32];
+ for (i = 0; i < rLen; i++) {
+ *(input++) = temp >> ((3 - i) * 8);
+ }
+ }
+
+ if (__SIRegs[SI_COMCSR_IDX] & SI_COMCSR_COMERR_MASK) {
+ sr >>= (3 - Si.chan) * 8;
+ sr &= 0xF;
+ if ((sr & 8) != 0 && (Type[Si.chan] & 0x80) == 0) {
+ Type[Si.chan] = 8;
+ }
+
+ if (sr == 0) {
+ sr = 4;
+ }
+ } else {
+ TypeTime[Si.chan] = __OSGetSystemTime();
+ sr = 0;
+ }
+
+ Si.chan = -1;
+ }
+
+ return sr;
+}
+
+static void SITransferNext(s32 chan) {
+ int i;
+ SIPacket* packet;
+
+ for (i = 0; i < 4; i++) {
+ chan++;
+ chan %= 4;
+ packet = &Packet[chan];
+
+ if (packet->chan != -1) {
+ if (packet->fire <= __OSGetSystemTime()) {
+ if (__SITransfer(packet->chan, packet->output, packet->outputBytes, packet->input, packet->inputBytes, packet->callback) != 0) {
+ OSCancelAlarm(&Alarm[chan]);
+ packet->chan = -1;
+ }
+ break;
+ }
+ }
+ }
+}
+
+#define CHAN_NONE -1
+
+static void SIInterruptHandler(__OSInterrupt interrupt, OSContext* context) {
+ u32 reg;
+ s32 chan;
+ u32 sr;
+ SICallback callback;
+ int i;
+ u32 vcount;
+ u32 x;
+
+ reg = __SIRegs[SI_COMCSR_IDX];
+ if ((reg & (SI_COMCSR_TCINT_MASK | SI_COMCSR_TCINTMSK_MASK)) == (SI_COMCSR_TCINT_MASK | SI_COMCSR_TCINTMSK_MASK)) {
+ ASSERTLINE(384, Si.chan != CHAN_NONE);
+
+ chan = Si.chan;
+ sr = CompleteTransfer();
+ callback = Si.callback;
+ Si.callback = NULL;
+ SITransferNext(chan);
+
+ if (callback) {
+ callback(chan, sr, context);
+ }
+
+ sr = __SIRegs[SI_STATUS_IDX];
+ sr &= 0x0F000000 >> (chan << 3);
+ __SIRegs[SI_STATUS_IDX] = sr;
+
+ if (Type[chan] == SI_ERROR_BUSY && !SIIsChanBusy(chan)) {
+ static u32 cmdTypeAndStatus;
+
+ SITransfer(chan, &cmdTypeAndStatus, 1, &Type[chan], 3, &GetTypeCallback, OSMicrosecondsToTicks(65));
+ }
+ }
+
+ if ((reg & (SI_COMCSR_RDSTINT_MASK | SI_COMCSR_RDSTINTMSK_MASK)) == (SI_COMCSR_RDSTINT_MASK | SI_COMCSR_RDSTINTMSK_MASK)) {
+ vcount = 1 + VIGetCurrentLine();
+ x = (Si.poll & (0x3FF << 16)) >> 16;
+
+ for (i = 0; i < 4; i++) {
+ if (SIGetResponseRaw(i)) {
+ InputBufferVcount[i] = vcount;
+ }
+ }
+
+ for (i = 0; i < 4; i++) {
+ if ((Si.poll & (0x80000000 >> (24 + i))) != 0) {
+ if (InputBufferVcount[i] == 0 || ((x >> 1) + InputBufferVcount[i]) < vcount) {
+ return;
+ }
+ }
+ }
+
+ for (i = 0; i < 4; i++) {
+ InputBufferVcount[i] = 0;
+ }
+
+ for (i = 0; i < 4; i++) {
+ if (RDSTHandler[i] != 0) {
+ (*RDSTHandler[i])(interrupt, context);
+ }
+ }
+ }
+}
+
+static BOOL SIEnablePollingInterrupt(BOOL enable) {
+ BOOL enabled;
+ BOOL rc;
+ u32 reg;
+ int i;
+
+ enabled = OSDisableInterrupts();
+ reg = __SIRegs[SI_COMCSR_IDX];
+ rc = ((reg & SI_COMCSR_RDSTINTMSK_MASK) != 0) ? TRUE : FALSE;
+
+ if (enable) {
+ reg |= SI_COMCSR_RDSTINTMSK_MASK;
+
+ for (i = 0; i < 4; i++) {
+ InputBufferVcount[i] = 0;
+ }
+ } else {
+ reg &= ~SI_COMCSR_RDSTINTMSK_MASK;
+ }
+
+ reg &= ~(SI_COMCSR_TCINT_MASK | SI_COMCSR_TSTART_MASK);
+ __SIRegs[SI_COMCSR_IDX] = reg;
+
+ OSRestoreInterrupts(enabled);
+ return rc;
+}
+
+BOOL SIRegisterPollingHandler(__OSInterruptHandler handler) {
+ BOOL enabled;
+ int i;
+
+ enabled = OSDisableInterrupts();
+ for (i = 0; i < 4; i++) {
+ if (RDSTHandler[i] == handler) {
+ OSRestoreInterrupts(enabled);
+ return TRUE;
+ }
+ }
+
+ for (i = 0; i < 4; i++) {
+ if (RDSTHandler[i] == 0) {
+ RDSTHandler[i] = handler;
+ SIEnablePollingInterrupt(TRUE);
+ OSRestoreInterrupts(enabled);
+ return TRUE;
+ }
+ }
+
+ OSRestoreInterrupts(enabled);
+ return FALSE;
+}
+
+BOOL SIUnregisterPollingHandler(__OSInterruptHandler handler) {
+ BOOL enabled;
+ int i;
+
+ enabled = OSDisableInterrupts();
+ for (i = 0; i < 4; i++) {
+ if (RDSTHandler[i] == handler) {
+ RDSTHandler[i] = 0;
+
+ for (i = 0; i < 4; i++) {
+ if (RDSTHandler[i] != 0) {
+ break;
+ }
+ }
+
+ if (i == 4) {
+ SIEnablePollingInterrupt(FALSE);
+ }
+
+ OSRestoreInterrupts(enabled);
+ return TRUE;
+ }
+ }
+
+ OSRestoreInterrupts(enabled);
+ return FALSE;
+}
+
+void SIInit(void) {
+ static BOOL Initialized = FALSE;
+ if (Initialized) {
+ return;
+ }
+
+ OSRegisterVersion(__SIVersion);
+
+ Packet[0].chan = Packet[1].chan = Packet[2].chan = Packet[3].chan = -1;
+ Si.poll = 0;
+ SISetSamplingRate(0);
+
+ do {} while(__SIRegs[SI_COMCSR_IDX] & SI_COMCSR_TSTART_MASK);
+
+ __SIRegs[SI_COMCSR_IDX] = SI_COMCSR_TCINT_MASK;
+ __OSSetInterruptHandler(0x14, SIInterruptHandler);
+ __OSUnmaskInterrupts(0x800);
+
+ SIGetType(0);
+ SIGetType(1);
+ SIGetType(2);
+ SIGetType(3);
+ Initialized = TRUE;
+}
+
+static int __SITransfer(s32 chan, void* output, u32 outputBytes, void* input, u32 inputBytes, SICallback callback) {
+ BOOL enabled;
+ u32 rLen;
+ u32 i;
+ u32 sr;
+ union {
+ u32 val;
+ struct {
+ u32 tcint : 1;
+ u32 tcintmsk : 1;
+ u32 comerr : 1;
+ u32 rdstint : 1;
+ u32 rdstintmsk : 1;
+ u32 pad2 : 4;
+ u32 outlngth : 7;
+ u32 pad1 : 1;
+ u32 inlngth : 7;
+ u32 pad0 : 5;
+ u32 channel : 2;
+ u32 tstart : 1;
+ } f;
+ } comcsr;
+
+ ASSERTMSGLINE(645, (chan >= 0) && (chan < 4), "SITransfer(): invalid channel.");
+ ASSERTMSGLINE(647, (outputBytes != 0) && (outputBytes <= 128), "SITransfer(): output size is out of range (must be 1 to 128).");
+ ASSERTMSGLINE(649, (inputBytes != 0) && (inputBytes <= 128), "SITransfer(): input size is out of range (must be 1 to 128).");
+
+ enabled = OSDisableInterrupts();
+ if (Si.chan != -1) {
+ OSRestoreInterrupts(enabled);
+ return 0;
+ }
+
+ ASSERTLINE(659, (__SIRegs[SI_COMCSR_IDX] & (SI_COMCSR_TSTART_MASK | SI_COMCSR_TCINT_MASK)) == 0);
+ sr = __SIRegs[SI_STATUS_IDX];
+ sr &= (0x0F000000 >> (chan* 8));
+ __SIRegs[SI_STATUS_IDX] = sr;
+
+ Si.chan = chan;
+ Si.callback = callback;
+ Si.inputBytes = inputBytes;
+ Si.input = input;
+
+ rLen = ROUND(outputBytes, 4) / 4;
+ for (i = 0; i < rLen; i++) {
+ __SIRegs[i + 0x20] = ((u32*)output)[i];
+ }
+
+ comcsr.val = __SIRegs[SI_COMCSR_IDX];
+ comcsr.f.tcint = 1;
+ comcsr.f.tcintmsk = callback ? 1 : 0;
+ comcsr.f.outlngth = outputBytes == 0x80 ? 0 : outputBytes;
+ comcsr.f.inlngth = inputBytes == 0x80 ? 0 : inputBytes;
+ comcsr.f.channel = chan;
+ comcsr.f.tstart = 1;
+
+ __SIRegs[SI_COMCSR_IDX] = comcsr.val;
+ OSRestoreInterrupts(enabled);
+ return 1;
+}
+
+u32 SISync(void) {
+ BOOL enabled;
+ u32 sr;
+
+ do {} while(__SIRegs[SI_COMCSR_IDX] & SI_COMCSR_TSTART_MASK);
+
+ enabled = OSDisableInterrupts();
+ sr = CompleteTransfer();
+ SITransferNext(4);
+ OSRestoreInterrupts(enabled);
+ return sr;
+}
+
+u32 SIGetStatus(s32 chan) {
+ BOOL enabled;
+ u32 sr;
+ int chanShift;
+
+ enabled = OSDisableInterrupts();
+ sr = __SIRegs[SI_STATUS_IDX];
+ chanShift = (3 - chan) * 8;
+ sr >>= chanShift;
+
+ if ((sr & 8) != 0) {
+ if ((Type[chan] & SI_ERROR_BUSY) == 0) {
+ Type[chan] = 8;
+ }
+ }
+
+ OSRestoreInterrupts(enabled);
+ return sr;
+}
+
+void SISetCommand(s32 chan, u32 command) {
+ ASSERTMSGLINE(770, (chan >= 0) && (chan < 4), "SISetCommand(): invalid channel.");
+ __SIRegs[chan* 3] = command;
+}
+
+u32 SIGetCommand(s32 chan) {
+ ASSERTMSGLINE(770, (chan >= 0) && (chan < 4), "SIGetCommand(): invalid channel.");
+ return __SIRegs[chan* 3];
+}
+
+void SITransferCommands(void) {
+ __SIRegs[SI_STATUS_IDX] = SI_COMCSR_TCINT_MASK;
+}
+
+u32 SISetXY(u32 x, u32 y) {
+ u32 poll;
+ BOOL enabled;
+
+ ASSERTMSGLINE(821, x >= 8, "SISetXY(): x is out of range (8 <= x <= 1023).");
+ ASSERTMSGLINE(822, x <= 1023, "SISetXY(): x is out of range (8 <= x <= 1023).");
+ ASSERTMSGLINE(823, y <= 255, "SISetXY(): y is out of range (0 <= y <= 255).");
+
+ poll = x << 0x10;
+ poll |= y << 8;
+ enabled = OSDisableInterrupts();
+ Si.poll &= 0xFC0000FF;
+ Si.poll |= poll;
+ poll = Si.poll;
+ __SIRegs[0x30 / 4] = poll;
+ OSRestoreInterrupts(enabled);
+ return poll;
+}
+
+u32 SIEnablePolling(u32 poll) {
+ BOOL enabled;
+ u32 en;
+
+ ASSERTMSGLINE(852, (poll & 0x0FFFFFFF) == 0, "SIEnablePolling(): invalid chan bit(s).");
+ if (poll == 0) {
+ return Si.poll;
+ }
+
+ enabled = OSDisableInterrupts();
+ poll = poll >> 24;
+ en = poll & 0xF0;
+ ASSERTLINE(883, en);
+ poll &= ((en >> 4) | 0x03FFFFF0);
+ poll &= 0xFC0000FF;
+
+ Si.poll &= ~(en >> 4);
+ Si.poll |= poll;
+ poll = Si.poll;
+ SITransferCommands();
+ __SIRegs[0x30 / 4] = poll;
+ OSRestoreInterrupts(enabled);
+ return poll;
+}
+
+u32 SIDisablePolling(u32 poll) {
+ BOOL enabled;
+
+ ASSERTMSGLINE(926, (poll & 0x0FFFFFFF) == 0, "SIDisablePolling(): invalid chan bit(s).");
+ if (poll == 0) {
+ return Si.poll;
+ }
+
+ enabled = OSDisableInterrupts();
+ poll = poll >> 24;
+ poll &= 0xF0;
+ ASSERTLINE(939, poll);
+ poll = Si.poll & ~poll;
+ __SIRegs[0x30 / 4] = poll;
+ Si.poll = poll;
+ OSRestoreInterrupts(enabled);
+ return poll;
+}
+
+static BOOL SIGetResponseRaw(s32 chan) {
+ u32 sr;
+
+ sr = SIGetStatus(chan);
+ if (sr & 0x20) {
+ InputBuffer[chan][0] = __SIRegs[1 + chan * 3];
+ InputBuffer[chan][1] = __SIRegs[2 + chan * 3];
+ InputBufferValid[chan] = TRUE;
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+BOOL SIGetResponse(s32 chan, void* data) {
+ BOOL rc;
+ BOOL enabled;
+
+ ASSERTMSGLINE(989, ((chan >= 0) && (chan < 4)), "SIGetResponse(): invalid channel.");
+ enabled = OSDisableInterrupts();
+ SIGetResponseRaw(chan);
+ rc = InputBufferValid[chan];
+ InputBufferValid[chan] = FALSE;
+
+ if (rc) {
+ ((u32*)data)[0] = InputBuffer[chan][0];
+ ((u32*)data)[1] = InputBuffer[chan][1];
+ }
+
+ OSRestoreInterrupts(enabled);
+ return rc;
+}
+
+static void AlarmHandler(OSAlarm* alarm, OSContext* context) {
+ s32 chan;
+ SIPacket* packet;
+
+ chan = (s32)(alarm - Alarm);
+ ASSERTLINE(1020, 0 <= chan && chan < SI_MAX_CHAN);
+
+ packet = &Packet[chan];
+ if (packet->chan != -1) {
+ ASSERTLINE(1024, packet->fire <= __OSGetSystemTime());
+
+ if (__SITransfer(packet->chan, packet->output, packet->outputBytes, packet->input, packet->inputBytes, packet->callback)) {
+ packet->chan = -1;
+ }
+ }
+}
+
+BOOL SITransfer(s32 chan, void* output, u32 outputBytes, void* input, u32 inputBytes,
+ SICallback callback, OSTime delay) {
+ BOOL enabled;
+ SIPacket* packet;
+ OSTime now;
+ OSTime fire;
+
+ packet = &Packet[chan];
+ enabled = OSDisableInterrupts();
+
+ if (packet->chan != -1 || Si.chan == chan) {
+ OSRestoreInterrupts(enabled);
+ return FALSE;
+ }
+
+ now = __OSGetSystemTime();
+ if (delay == 0) {
+ fire = now;
+ } else {
+ fire = delay + XferTime[chan];
+ }
+
+ if (now < fire) {
+ delay = fire - now;
+ OSSetAlarm(&Alarm[chan], delay, AlarmHandler);
+ } else if (__SITransfer(chan, output, outputBytes, input, inputBytes, callback)) {
+ OSRestoreInterrupts(enabled);
+ return TRUE;
+ }
+
+ packet->chan = chan;
+ packet->output = output;
+ packet->outputBytes = outputBytes;
+ packet->input = input;
+ packet->inputBytes = inputBytes;
+ packet->callback = callback;
+ packet->fire = fire;
+ OSRestoreInterrupts(enabled);
+ return TRUE;
+}
+
+static void CallTypeAndStatusCallback(s32 chan, u32 type) {
+ SITypeCallback callback;
+ int i;
+
+ for (i = 0; i < 4; i++) {
+ callback = TypeCallback[chan][i];
+
+ if (callback != 0) {
+ TypeCallback[chan][i] = 0;
+ (*callback)(chan, type);
+ }
+ }
+}
+
+static void GetTypeCallback(s32 chan, u32 error, OSContext* context) {
+ static u32 cmdFixDevice[4];
+ u32 type;
+ u32 chanBit;
+ int fix;
+ u32 id;
+
+ ASSERTLINE(1155, 0 <= chan && chan < SI_MAX_CHAN);
+
+ ASSERTLINE(1157, (Type[chan] & 0xff) == SI_ERROR_BUSY);
+ Type[chan] &= ~SI_ERROR_BUSY;
+ Type[chan] |= error;
+ TypeTime[chan] = __OSGetSystemTime();
+
+ type = Type[chan];
+ chanBit = 0x80000000 >> chan;
+ fix = __PADFixBits & chanBit;
+ __PADFixBits &= ~chanBit;
+
+ if ((error & 0xF) != 0 || (type & 0x18000000) != 0x08000000 || (type & 0x80000000) == 0 || (type & 0x04000000) != 0) {
+ OSSetWirelessID(chan, 0);
+ CallTypeAndStatusCallback(chan, Type[chan]);
+ } else {
+
+
+ id = OSGetWirelessID(chan) << 8;
+
+ if (fix != 0 && (id & 0x100000) != 0) {
+ cmdFixDevice[chan] = 0x4E000000 | (id & 0xCFFF00) | 0x100000;
+ Type[chan] = SI_ERROR_BUSY;
+ SITransfer(chan, &cmdFixDevice[chan], 3, &Type[chan], 3, &GetTypeCallback, 0);
+ return;
+ }
+
+ if ((type & 0x00100000) != 0) {
+ if ((id & 0xCFFF00) != (type & 0xCFFF00)) {
+ if ((id & 0x100000) == 0) {
+ id = type & 0xCFFF00;
+ id |= 0x100000;
+ OSSetWirelessID(chan, (id >> 8) & 0xFFFF);
+ }
+
+ cmdFixDevice[chan] = 0x4E000000 | id;
+ Type[chan] = SI_ERROR_BUSY;
+ SITransfer(chan, &cmdFixDevice[chan], 3, &Type[chan], 3, &GetTypeCallback, 0);
+ return;
+ }
+ } else {
+ if ((type & 0x40000000) != 0) {
+ id = type & 0xCFFF00;
+ id |= 0x100000;
+ OSSetWirelessID(chan, (id >> 8) & 0xFFFF);
+
+ cmdFixDevice[chan] = 0x4E000000 | id;
+ Type[chan] = SI_ERROR_BUSY;
+ SITransfer(chan, &cmdFixDevice[chan], 3, &Type[chan], 3, &GetTypeCallback, 0);
+ return;
+ }
+
+ OSSetWirelessID(chan, 0);
+ }
+
+ CallTypeAndStatusCallback(chan, Type[chan]);
+ }
+}
+
+u32 SIGetType(s32 chan) {
+ static u32 cmdTypeAndStatus;
+ BOOL enabled;
+ u32 type;
+ OSTime diff;
+
+ enabled = OSDisableInterrupts();
+ ASSERTLINE(1261, 0 <= chan && chan < SI_MAX_CHAN);
+ type = Type[chan];
+ diff = __OSGetSystemTime() - TypeTime[chan];
+ if ((Si.poll & (0x80 >> chan)) != 0) {
+ if (type != 8) {
+ TypeTime[chan] = __OSGetSystemTime();
+ OSRestoreInterrupts(enabled);
+ return type;
+ }
+
+ type = Type[chan] = SI_ERROR_BUSY;
+ } else {
+ if (diff <= OSMillisecondsToTicks(50) && type != 8) {
+ OSRestoreInterrupts(enabled);
+ return type;
+ }
+
+ if (diff <= OSMillisecondsToTicks(75)) {
+ Type[chan] = SI_ERROR_BUSY;
+ } else {
+ type = Type[chan] = SI_ERROR_BUSY;
+ }
+ }
+
+ TypeTime[chan] = __OSGetSystemTime();
+ SITransfer(chan, &cmdTypeAndStatus, 1, &Type[chan], 3, &GetTypeCallback, OSMicrosecondsToTicks(65));
+ OSRestoreInterrupts(enabled);
+ return type;
+}
+
+u32 SIGetTypeAsync(s32 chan, SITypeCallback callback) {
+ BOOL enabled;
+ u32 type;
+
+ enabled = OSDisableInterrupts();
+ type = SIGetType(chan);
+
+ if ((Type[chan] & SI_ERROR_BUSY) != 0) {
+ int i;
+ for (i = 0; i < SI_MAX_TYPE; i++) {
+ if (TypeCallback[chan][i] == callback) {
+ break;
+ }
+
+ if (TypeCallback[chan][i] == 0) {
+ TypeCallback[chan][i] = callback;
+ break;
+ }
+ }
+
+ ASSERTLINE(1342, i < SI_MAX_TYPE);
+ } else {
+ (*callback)(chan, type);
+ }
+
+ OSRestoreInterrupts(enabled);
+ return type;
+}
+
+u32 SIDecodeType(u32 type) {
+ u32 error;
+
+ error = type & 0xFF;
+ type &= ~0xFF;
+
+ if (error & SI_ERROR_NO_RESPONSE) {
+ return SI_ERROR_NO_RESPONSE;
+ }
+
+ if (error & (SI_ERROR_UNKNOWN | SI_ERROR_COLLISION | SI_ERROR_OVER_RUN | SI_ERROR_UNDER_RUN)) {
+ return SI_ERROR_UNKNOWN;
+ }
+
+ if (error != 0) {
+ ASSERTLINE(1371, error == SI_ERROR_BUSY);
+ return SI_ERROR_BUSY;
+ }
+
+ if ((type & SI_TYPE_MASK) == SI_TYPE_N64) {
+ switch (type & 0xFFFF0000) {
+ case SI_N64_MIC:
+ case SI_N64_KEYBOARD:
+ case SI_GBA:
+ case SI_N64_MOUSE:
+ case SI_N64_CONTROLLER:
+ return type & 0xFFFF0000;
+ default:
+ return SI_ERROR_UNKNOWN;
+ }
+ }
+
+ if ((type & SI_TYPE_MASK) != SI_TYPE_DOLPHIN) {
+ return SI_ERROR_UNKNOWN;
+ }
+
+ switch (type & 0xFFFF0000) {
+ case SI_GC_STEERING:
+ case SI_GC_CONTROLLER:
+ return type & 0xFFFF0000;
+ }
+
+ if ((type & 0xFFE00000) == SI_GC_KEYBOARD) {
+ return SI_GC_KEYBOARD;
+ }
+
+ if ((type & SI_GC_WIRELESS) != 0 && (type & SI_WIRELESS_IR) == 0) {
+ if ((type & SI_GC_WAVEBIRD) == SI_GC_WAVEBIRD) {
+ return SI_GC_WAVEBIRD;
+ }
+
+ if ((type & SI_WIRELESS_STATE) == 0) {
+ return SI_GC_RECEIVER;
+ }
+ }
+
+ if ((type & SI_GC_CONTROLLER) == SI_GC_CONTROLLER) {
+ return SI_GC_CONTROLLER;
+ }
+
+ return SI_ERROR_UNKNOWN;
+}
+
+u32 SIProbe(s32 chan) {
+ return SIDecodeType(SIGetType(chan));
+}
+
+char* SIGetTypeString(u32 type) {
+ switch (SIDecodeType(type)) {
+ case SI_ERROR_NO_RESPONSE:
+ return "No response";
+ case SI_ERROR_BUSY:
+ return "Busy";
+ case SI_N64_CONTROLLER:
+ return "N64 controller";
+ case SI_N64_MIC:
+ return "N64 microphone";
+ case SI_N64_KEYBOARD:
+ return "N64 keyboard";
+ case SI_N64_MOUSE:
+ return "N64 mouse";
+ case SI_GBA:
+ return "GameBoy Advance";
+ case SI_GC_CONTROLLER:
+ return "Standard controller";
+ case SI_GC_RECEIVER:
+ return "Wireless receiver";
+ case SI_GC_WAVEBIRD:
+ return "WaveBird controller";
+ case SI_GC_KEYBOARD:
+ return "Keyboard";
+ case SI_GC_STEERING:
+ return "Steering";
+ case SI_ERROR_UNKNOWN:
+ default:
+ return "Unknown";
+ }
+}
diff --git a/src/revolution/si/SISamplingRate.c b/src/revolution/si/SISamplingRate.c
new file mode 100644
index 0000000000..3006110e04
--- /dev/null
+++ b/src/revolution/si/SISamplingRate.c
@@ -0,0 +1,125 @@
+#include <revolution.h>
+#include <revolution/si.h>
+
+#include "__os.h"
+
+#define LATENCY 8
+
+static u32 SamplingRate = 0;
+
+typedef struct XY {
+ u16 line;
+ u8 count;
+} XY;
+
+static XY XYNTSC[12] = {
+ {0x00F6, 0x02},
+ {0x000E, 0x13},
+ {0x001E, 0x09},
+ {0x002C, 0x06},
+ {0x0034, 0x05},
+ {0x0041, 0x04},
+ {0x0057, 0x03},
+ {0x0057, 0x03},
+ {0x0057, 0x03},
+ {0x0083, 0x02},
+ {0x0083, 0x02},
+ {0x0083, 0x02},
+};
+
+static XY XYPAL[12] = {
+ {0x0128, 0x02},
+ {0x000F, 0x15},
+ {0x001D, 0x0B},
+ {0x002D, 0x07},
+ {0x0034, 0x06},
+ {0x003F, 0x05},
+ {0x004E, 0x04},
+ {0x0068, 0x03},
+ {0x0068, 0x03},
+ {0x0068, 0x03},
+ {0x0068, 0x03},
+ {0x009C, 0x02},
+};
+
+void SISetSamplingRate(u32 msec) {
+ XY* xy;
+ BOOL progressive;
+ BOOL enabled;
+
+ ASSERTMSGLINE(387, msec <= 11, "SISetSamplingRate(): out of rage (0 <= msec <= 11)");
+ if (msec > 11) {
+ msec = 11;
+ }
+ enabled = OSDisableInterrupts();
+ SamplingRate = msec;
+
+ switch (VIGetTvFormat()) {
+ case VI_NTSC:
+ case VI_MPAL:
+ case VI_EURGB60:
+ xy = XYNTSC;
+ break;
+ case VI_PAL:
+ xy = XYPAL;
+ break;
+ default:
+ OSReport("SISetSamplingRate: unknown TV format. Use default.");
+ msec = 0;
+ xy = XYNTSC;
+ break;
+ }
+
+ progressive = __VIRegs[VI_CLOCK_SEL] & 1;
+ SISetXY((progressive ? 2 : 1) * xy[msec].line, xy[msec].count);
+ OSRestoreInterrupts(enabled);
+}
+
+void SIRefreshSamplingRate(void) {
+ SISetSamplingRate(SamplingRate);
+}
+
+#if DEBUG
+void __SITestSamplingRate(u32 tvmode) {
+ u32 msec;
+ u32 line;
+ u32 count;
+ XY* xy;
+
+ switch (tvmode) {
+ case VI_NTSC:
+ case VI_MPAL:
+ xy = XYNTSC;
+ for (msec = 0; msec <= 11; msec++) {
+ line = xy[msec].line;
+ count = xy[msec].count;
+ OSReport("%2d[msec]: count %3d, line %3d, last %3d, diff0 %2d.%03d, diff1 %2d.%03d\n",
+ msec, count, line, line * (count - 1) + LATENCY, (line * 636) / 10000, (line * 636) % 10000,
+ ((263 - line * (count - 1)) * 636) / 10000, ((263 - line * (count - 1)) * 636) % 10000);
+ ASSERTLINE(446, line * (count - 1) + LATENCY < 263);
+
+ if (msec != 0) {
+ ASSERTLINE(449, 636 * line < msec * 10000);
+ ASSERTLINE(450, 636 * (263 - line * (count - 1)) < msec * 10000);
+ }
+ }
+ break;
+ case VI_PAL:
+ xy = XYPAL;
+ for (msec = 0; msec <= 11; msec++) {
+ line = xy[msec].line;
+ count = xy[msec].count;
+ OSReport("%2d[msec]: count %3d, line %3d, last %3d, diff0 %2d.%03d, diff1 %2d.%03d\n",
+ msec, count, line, line * (count - 1) + LATENCY, (line * 640) / 10000, (line * 640) % 10000,
+ ((313 - line * (count - 1)) * 640) / 10000, ((313 - line * (count - 1)) * 640) % 10000);
+ ASSERTLINE(470, line * (count - 1) + LATENCY < 313);
+
+ if (msec != 0) {
+ ASSERTLINE(473, 640 * line < msec * 10000);
+ ASSERTLINE(474, 640 * (313 - line * (count - 1)) < msec * 10000);
+ }
+ }
+ break;
+ }
+}
+#endif
diff --git a/src/revolution/vi/__vi.h b/src/revolution/vi/__vi.h
new file mode 100644
index 0000000000..e061de3369
--- /dev/null
+++ b/src/revolution/vi/__vi.h
@@ -0,0 +1,40 @@
+#ifndef _REVOLUTION_VI_INTERNAL_H_
+#define _REVOLUTION_VI_INTERNAL_H_
+
+#include <revolution/vi.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* i2c.c */
+
+int __VISendI2CData(u8 slaveAddr, u8* pData, int nBytes);
+
+/* vi.c */
+
+void __VIInit(VITVMode mode);
+void __VISetAdjustingValues(s16 x, s16 y);
+void __VIGetAdjustingValues(s16* x, s16* y);
+void __VIGetCurrentPosition(s16* x, s16* y);
+BOOL __VIResetDev0Idle();
+
+/* vi3in1.c */
+void __VISetFilter4EURGB60(VIBool enable);
+void __VISetGamma1_0(void);
+void __VISetYUVSEL(VIBool outsel);
+void __VISetCGMS(void);
+void __VISetWSS(void);
+void __VISetClosedCaption(void);
+void __VISetMacrovision(void);
+void __VISetGamma(void);
+void __VISetTrapFilter(void);
+void __VISetRGBOverDrive(void);
+void __VISetRGBModeImm(void);
+void __VISetRevolutionModeSimple(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/revolution/vi/i2c.c b/src/revolution/vi/i2c.c
new file mode 100644
index 0000000000..6996b1368d
--- /dev/null
+++ b/src/revolution/vi/i2c.c
@@ -0,0 +1,230 @@
+#include <revolution/vi.h>
+#include <revolution/os.h>
+
+#include "__vi.h"
+
+static volatile u32 __i2c_ident_flag = 1;
+static volatile u32 __i2c_ident_first = 0;
+
+#define busRd32(addr) (*(volatile u32 *)(addr))
+#define busWrt32(addr, val) (*(volatile u32 *)(addr)) = (val)
+
+void WaitMicroTime(s32 usec) {
+ OSTime t = __OSGetSystemTime();
+
+ while (((__OSGetSystemTime() - t) * 8) / 486 < usec);
+}
+
+static void VICheckI2C(void) {
+ __i2c_ident_flag = 1;
+}
+
+static BOOL __VISetSCL(u32 value) {
+ u32 reg;
+ value &= 1;
+
+ reg = busRd32(0xCD8000C0);
+ reg &= ~(1 << 14);
+ reg = (u32)(reg | (value << 14));
+ busWrt32(0xCD8000C0, reg);
+ return TRUE;
+}
+
+static BOOL __VISetSDA(u32 value) {
+ u32 reg;
+ value &= 1;
+ reg = busRd32(0xCD8000C0);
+ reg &= ~(1 << 15);
+ reg = (u32)(reg | (value << 15));
+ busWrt32(0xCD8000C0, reg);
+ return TRUE;
+}
+
+static u32 __VIGetSDA(void) {
+ u32 reg = busRd32(0xCD8000C8);
+ return (u32)((reg >> 15) & 1);
+}
+
+static void __VIOpenI2C(u32 dir) {
+ u32 reg = busRd32(0xCD8000C4);
+ reg = (u32)(reg & ~(1 << 15));
+ reg = (u32)(reg | (1 << 14) | (dir << 15));
+ busWrt32(0xCD8000C4, reg);
+}
+
+static int wait4ClkHigh(void) {
+ WaitMicroTime(2);
+ return 1;
+}
+
+static int sendSlaveAddr(u8 slaveAddr) {
+ int i;
+
+ if (0 == __i2c_ident_flag) {
+ __VISetSDA(1);
+ } else {
+ __VISetSDA(0);
+ }
+
+ WaitMicroTime(2);
+ __VISetSCL(0);
+
+ for (i = 0; i < 8; i++) {
+ if (slaveAddr & 0x80) {
+ if (0 == __i2c_ident_flag) {
+ __VISetSDA(0);
+ } else {
+ __VISetSDA(1);
+ }
+ } else {
+ if (0 == __i2c_ident_flag) {
+ __VISetSDA(1);
+ } else {
+ __VISetSDA(0);
+ }
+ }
+
+ WaitMicroTime(2);
+ __VISetSCL(1);
+
+ if (wait4ClkHigh() == 0) {
+ return 0;
+ }
+
+ __VISetSCL(0);
+ slaveAddr <<= 1;
+ }
+
+ __VIOpenI2C(0);
+ WaitMicroTime(2);
+ __VISetSCL(1);
+
+ if (wait4ClkHigh() == 0) {
+ return 0;
+ }
+
+ if (1 == __i2c_ident_flag) {
+ if (__VIGetSDA() != 0) {
+ return 0;
+ }
+ }
+
+ if (0 == __i2c_ident_flag) {
+ __VISetSDA(1);
+ } else {
+ __VISetSDA(0);
+ }
+
+ __VIOpenI2C(1);
+ __VISetSCL(0);
+ return 1;
+}
+
+int __VISendI2CData(u8 slaveAddr, u8* pData, int nBytes) {
+ s32 i;
+ u8 data;
+ BOOL enabled;
+
+ if (__i2c_ident_first == 0) {
+ VICheckI2C();
+ __i2c_ident_first = 1;
+ }
+
+ enabled = OSDisableInterrupts();
+
+ __VIOpenI2C(1);
+ __VISetSCL(1);
+
+ if (0 == __i2c_ident_flag) {
+ __VISetSDA(0);
+ } else {
+ __VISetSDA(1);
+ }
+
+ WaitMicroTime(2);
+ WaitMicroTime(2);
+
+ if (sendSlaveAddr(slaveAddr) == 0) {
+ OSRestoreInterrupts(enabled);
+ return 0;
+ }
+
+ __VIOpenI2C(1);
+
+ while (nBytes != 0) {
+ data = *pData++;
+ for (i = 0; i < 8; i++) {
+ if (data & 0x80) {
+ if (0 == __i2c_ident_flag) {
+ __VISetSDA(0);
+ } else {
+ __VISetSDA(1);
+ }
+ } else {
+ if (0 == __i2c_ident_flag) {
+ __VISetSDA(1);
+ } else {
+ __VISetSDA(0);
+ }
+ }
+
+ WaitMicroTime(2);
+ __VISetSCL(1);
+
+ if (wait4ClkHigh() == 0) {
+ OSRestoreInterrupts(enabled);
+ return 0;
+ }
+
+ __VISetSCL(0);
+ data <<= 1;
+ }
+
+ __VIOpenI2C(0);
+ WaitMicroTime(2);
+ __VISetSCL(1);
+
+ if (wait4ClkHigh() == 0) {
+ OSRestoreInterrupts(enabled);
+ return 0;
+ }
+
+ if (1 == __i2c_ident_flag) {
+ if (__VIGetSDA() != 0) {
+ OSRestoreInterrupts(enabled);
+ return 0;
+ }
+ }
+
+ if (0 == __i2c_ident_flag) {
+ __VISetSDA(1);
+ } else {
+ __VISetSDA(0);
+ }
+
+ __VIOpenI2C(1);
+ __VISetSCL(0);
+ nBytes--;
+ }
+
+ __VIOpenI2C(1);
+
+ if (0 == __i2c_ident_flag) {
+ __VISetSDA(1);
+ } else {
+ __VISetSDA(0);
+ }
+
+ WaitMicroTime(2);
+ __VISetSCL(1);
+ WaitMicroTime(2);
+
+ if (0 == __i2c_ident_flag) {
+ __VISetSDA(0);
+ } else {
+ __VISetSDA(1);
+ }
+
+ OSRestoreInterrupts(enabled);
+ return 1;
+}
diff --git a/src/revolution/vi/vi.c b/src/revolution/vi/vi.c
new file mode 100644
index 0000000000..057ba805b4
--- /dev/null
+++ b/src/revolution/vi/vi.c
@@ -0,0 +1,1703 @@
+#include <revolution/gx.h>
+#include <revolution/os.h>
+#include <revolution/vi.h>
+#include <revolution/si.h>
+#include <revolution/sc.h>
+#include <revolution/hw_regs.h>
+
+#include "__gx.h"
+#include "__os.h"
+#include "__vi.h"
+
+// normal assert macros don't match in some places here
+#if DEBUG
+#define VI_ASSERTMSGLINE1(line, cond, msg, arg1) \
+ if (!(cond)) \
+ OSPanic(__FILE__, line, msg, arg1)
+
+#define VI_ASSERTMSGLINE2(line, cond, msg, arg1, arg2) \
+ if (!(cond)) \
+ OSPanic(__FILE__, line, msg, arg1, arg2)
+#else
+#define VI_ASSERTMSGLINE1(line, cond, msg, arg1) (void)0
+#define VI_ASSERTMSGLINE2(line, cond, msg, arg1, arg2) (void)0
+#endif
+
+// extern
+extern DVDCommandBlock __DVDStopMotorCommandBlock;
+
+#ifdef SDK_AUG2010
+#define BUILD_DATE "Aug 23 2010"
+#if DEBUG
+#define BUILD_TIME "17:27:58"
+#else
+#define BUILD_TIME "17:33:06"
+#endif
+#elif SDK_SEP2006
+#define BUILD_DATE "Sep 21 2006"
+#define BUILD_TIME "14:32:13"
+#endif
+
+#ifdef SDK_AUG2010
+#if DEBUG
+const char* __VIVersion = "<< RVL_SDK - VI \tdebug build: "BUILD_DATE" "BUILD_TIME" (0x4302_145) >>";
+#else
+const char* __VIVersion = "<< RVL_SDK - VI \trelease build: "BUILD_DATE" "BUILD_TIME" (0x4302_145) >>";
+#endif
+#elif SDK_SEP2006
+const char* __VIVersion = "<< RVL_SDK - VI \trelease build: "BUILD_DATE" "BUILD_TIME" (0x4200_60422) >>";
+#endif
+
+typedef struct {
+ u8 equ;
+ u16 acv;
+ u16 prbOdd;
+ u16 prbEven;
+ u16 psbOdd;
+ u16 psbEven;
+ u8 bs1;
+ u8 bs2;
+ u8 bs3;
+ u8 bs4;
+ u16 be1;
+ u16 be2;
+ u16 be3;
+ u16 be4;
+ u16 nhlines;
+ u16 hlw;
+ u8 hsy;
+ u8 hcs;
+ u8 hce;
+ u8 hbe640;
+ u16 hbs640;
+ u8 hbeCCIR656;
+ u16 hbsCCIR656;
+} timing_s;
+
+typedef struct {
+ u16 DispPosX;
+ u16 DispPosY;
+ u16 DispSizeX;
+ u16 DispSizeY;
+ u16 AdjustedDispPosX;
+ u16 AdjustedDispPosY;
+ u16 AdjustedDispSizeY;
+ u16 AdjustedPanPosY;
+ u16 AdjustedPanSizeY;
+ u16 FBSizeX;
+ u16 FBSizeY;
+ u16 PanPosX;
+ u16 PanPosY;
+ u16 PanSizeX;
+ u16 PanSizeY;
+ VIXFBMode FBMode;
+ u32 nonInter;
+ u32 tv;
+ u8 wordPerLine;
+ u8 std;
+ u8 wpl;
+ u32 bufAddr;
+ u32 tfbb;
+ u32 bfbb;
+ u8 xof;
+ BOOL black;
+ BOOL threeD;
+ u32 rbufAddr;
+ u32 rtfbb;
+ u32 rbfbb;
+ timing_s* timing;
+} SomeVIStruct;
+
+static BOOL IsInitialized = FALSE;
+
+static volatile u32 retraceCount;
+static volatile u32 flushFlag;
+static volatile u32 flushFlag3in1;
+static volatile u32 vsync_timing_err_cnt = 0;
+static volatile u32 vsync_timing_test_flag = 0;
+
+static volatile BOOL __VIDimming_All_Clear = FALSE;
+static volatile BOOL __VIDimmingFlag_Enable;
+static volatile BOOL __VIDVDStopFlag_Enable;
+static volatile VITimeToDIM g_current_time_to_dim;
+static vu32 THD_TIME_TO_DIMMING = 0;
+static vu32 NEW_TIME_TO_DIMMING = 0;
+static vu32 THD_TIME_TO_DVD_STOP = 0;
+static vu32 _gIdleCount_dimming = 0;
+static vu32 _gIdleCount_dvd = 0;
+static vu32 __VIDimmingFlag_RF_IDLE;
+static vu32 __VIDimmingFlag_SI_IDLE;
+static vu32 __VIDimmingFlag_DEV_IDLE[10];
+static volatile BOOL __VIDimmingState = FALSE;
+
+extern VIVideo Vdac_Flag_Region;
+extern volatile u32 Vdac_Flag_Changed;
+
+static OSThreadQueue retraceQueue;
+
+static void (*PreCB)(u32);
+static void (*PostCB)(u32);
+
+static u32 encoderType;
+static u16 regs[59];
+static volatile u32 __VIDimmingFlag_DEV_IDLE[10];
+static timing_s* CurrTiming;
+static u32 CurrTvMode;
+static u32 NextBufAddr;
+static u32 CurrBufAddr;
+static u16 shdwRegs[59];
+
+static void (*PositionCallback)(s16, s16) = NULL;
+static s16 displayOffsetH = 0;
+static s16 displayOffsetV = 0;
+static volatile u32 changeMode = 0;
+static volatile u64 changed = 0;
+static volatile u32 shdwChangeMode = 0;
+static volatile u64 shdwChanged = 0;
+static u32 FBSet = 0;
+static timing_s* timingExtra = NULL;
+
+#define MARK_CHANGED(index) (changed |= 1LL << (63 - (index)))
+
+static timing_s timing[11] = {
+ { 6, 240, 24, 25, 3, 2, 12, 13, 12, 13, 520, 519, 520, 519, 525, 429, 64, 71, 105, 162, 373, 122, 412 },
+ { 6, 240, 24, 24, 4, 4, 12, 12, 12, 12, 520, 520, 520, 520, 526, 429, 64, 71, 105, 162, 373, 122, 412 },
+ { 5, 287, 35, 36, 1, 0, 13, 12, 11, 10, 619, 618, 617, 620, 625, 432, 64, 75, 106, 172, 380, 133, 420 },
+ { 5, 287, 33, 33, 2, 2, 13, 11, 13, 11, 619, 621, 619, 621, 624, 432, 64, 75, 106, 172, 380, 133, 420 },
+ { 6, 240, 24, 25, 3, 2, 16, 15, 14, 13, 518, 517, 516, 519, 525, 429, 64, 78, 112, 162, 373, 122, 412 },
+ { 6, 240, 24, 24, 4, 4, 16, 14, 16, 14, 518, 520, 518, 520, 526, 429, 64, 78, 112, 162, 373, 122, 412 },
+ { 12, 480, 48, 48, 6, 6, 24, 24, 24, 24, 1038, 1038, 1038, 1038, 1050, 429, 64, 71, 105, 162, 373, 122, 412 },
+ { 12, 480, 44, 44, 10, 10, 24, 24, 24, 24, 1038, 1038, 1038, 1038, 1050, 429, 64, 71, 105, 168, 379, 122, 412 },
+ { 6, 241, 24, 25, 1, 0, 12, 13, 12, 13, 520, 519, 520, 519, 525, 429, 64, 71, 105, 159, 370, 122, 412 },
+ { 12, 480, 48, 48, 6, 6, 24, 24, 24, 24, 1038, 1038, 1038, 1038, 1050, 429, 64, 71, 105, 180, 391, 122, 412 },
+ { 10, 576, 62, 62, 6, 6, 20, 20, 20, 20, 1240, 1240, 1240, 1240, 1250, 432, 64, 75, 106, 172, 380, 122, 412 }
+};
+
+static u16 taps[25] = {
+ 0x01F0, 0x01DC,
+ 0x01AE, 0x0174,
+ 0x0129, 0x00DB,
+ 0x008E, 0x0046,
+ 0x000C, 0x00E2,
+ 0x00CB, 0x00C0,
+ 0x00C4, 0x00CF,
+ 0x00DE, 0x00EC,
+ 0x00FC, 0x0008,
+ 0x000F, 0x0013,
+ 0x0013, 0x000F,
+ 0x000C, 0x0008,
+ 0x0001
+};
+
+GXRenderModeObj GXPal528Prog = {
+ 6,
+ 640, 528, 528,
+ 40, 23,
+ 640, 528,
+ 0,
+ 0,
+ 0,
+ { 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 },
+ { 0, 0, 21, 22, 21, 0, 0 }
+};
+
+GXRenderModeObj GXPal528ProgSoft = {
+ 6,
+ 640, 528, 528,
+ 40, 23,
+ 640, 528,
+ 0,
+ 0,
+ 0,
+ { 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 },
+ { 8, 8, 10, 12, 10, 8, 8 }
+};
+
+GXRenderModeObj GXPal524ProgAa = {
+ 6,
+ 640, 264, 524,
+ 40, 23,
+ 640, 524,
+ 0,
+ 0,
+ 1,
+ { 3, 2, 9, 6, 3, 10, 3, 2, 9, 6, 3, 10, 9, 2, 3, 6, 9, 10, 9, 2, 3, 6, 9, 10 },
+ { 4, 8, 12, 16, 12, 8, 4 }
+};
+
+static BOOL OnShutdown(BOOL final, u32 event);
+static OSShutdownFunctionInfo ShutdownFunctionInfo = {
+ OnShutdown,
+ 127
+};
+
+static SomeVIStruct HorVer;
+
+
+// prototypes
+static u32 getCurrentFieldEvenOdd(void);
+timing_s* __VISetExtraTiming(timing_s* t);
+void __VIEnableRawPositionInterrupt(s16 x, s16 y, void (*callback)(s16, s16));
+void (*__VIDisableRawPositionInterrupt())(s16, s16);
+void __VIDisplayPositionToXY(u32 hct, u32 vct, s16* x, s16* y);
+void __VISetLatchMode(u32 mode);
+int __VIGetLatch0Position(s16* px, s16* py);
+int __VIGetLatch1Position(s16* px, s16* py);
+int __VIGetLatchPosition(u32 port, s16* px, s16* py);
+static void GetCurrentDisplayPosition(u32* hct, u32* vct);
+
+static BOOL OnShutdown(BOOL final, u32 event) {
+ BOOL retval;
+ static BOOL first = TRUE;
+ static u32 count;
+
+ if (final == FALSE) {
+ switch (event) {
+ case 3:
+ case 1:
+ case 2:
+ if (first) {
+ VISetRGBModeImm();
+ VIFlush();
+ count = retraceCount;
+ first = FALSE;
+ retval = FALSE;
+ } else {
+ if (count == retraceCount) {
+ retval = FALSE;
+ } else {
+ retval = TRUE;
+ }
+ }
+ break;
+ case 4:
+ case 0:
+ case 6:
+ case 5:
+ __VISetGamma1_0();
+ retval = TRUE;
+ break;
+ }
+ } else {
+ retval = TRUE;
+ }
+
+ return retval;
+}
+
+static u32 getEncoderType(void) {
+ return 1;
+}
+
+static s32 cntlzd(u64 bit) {
+ u32 hi;
+ u32 lo;
+ s32 value;
+
+ hi = bit >> 32;
+ lo = bit & 0xFFFFFFFF;
+ value = __cntlzw(hi);
+ if (value < 32) {
+ return value;
+ }
+ return __cntlzw(lo) + 32;
+}
+
+static int VISetRegs(void) {
+ s32 regIndex;
+
+ if (shdwChangeMode != 1 || getCurrentFieldEvenOdd() != 0) {
+ while (shdwChanged != 0) {
+ regIndex = cntlzd(shdwChanged);
+ __VIRegs[regIndex] = shdwRegs[regIndex];
+ shdwChanged &= ~((u64)1 << (63 - regIndex));
+ }
+
+ shdwChangeMode = 0;
+ CurrTiming = HorVer.timing;
+ CurrTvMode = HorVer.tv;
+ CurrBufAddr = NextBufAddr;
+ return 1;
+ }
+
+ return 0;
+}
+
+static void __VIRetraceHandler(__OSInterrupt unused, OSContext* context) {
+ OSContext exceptionContext;
+ u16 reg;
+ u32 inter = 0;
+ u32 regIndex;
+ static u32 old_dtvStatus = 999;
+ static u32 old_tvtype = 999;
+ u32 now_dtvStatus = 0;
+ u32 now_tvtype = 0;
+ static BOOL __VIDimmingFlag_Enable_old = TRUE;
+ static BOOL __VIDVDStopFlag_Enable_old = TRUE;
+ u32 i;
+
+ #if DEBUG
+ static u32 dbgCount;
+ #endif
+ static u32 DimmingON_Pending = 0;
+ static u32 DimmingOFF_Pending = 0;
+
+
+ reg = __VIRegs[0x18];
+ if (reg & 0x8000) {
+ __VIRegs[0x18] = reg & ~0x8000;
+ inter |= 1;
+ }
+ reg = __VIRegs[0x1A];
+ if (reg & 0x8000) {
+ __VIRegs[0x1A] = reg & ~0x8000;
+ inter |= 2;
+ }
+ reg = __VIRegs[0x1C];
+ if (reg & 0x8000) {
+ __VIRegs[0x1C] = reg & ~0x8000;
+ inter |= 4;
+ }
+ reg = __VIRegs[0x1E];
+ if (reg & 0x8000) {
+ __VIRegs[0x1E] = reg & ~0x8000;
+ inter |= 8;
+ }
+ reg = __VIRegs[0x1E];
+
+ if ((inter & 4) || (inter & 8)) {
+ OSClearContext(&exceptionContext);
+ OSSetCurrentContext(&exceptionContext);
+
+ if (PositionCallback != 0) {
+ s16 x, y;
+ __VIGetCurrentPosition(&x, &y);
+ (*PositionCallback)(x, y);
+ }
+
+ OSClearContext(&exceptionContext);
+ OSSetCurrentContext(context);
+ return;
+ }
+
+ if (inter == 0) {
+ ASSERTLINE(1350, FALSE);
+ }
+
+ retraceCount += 1;
+ OSClearContext(&exceptionContext);
+ OSSetCurrentContext(&exceptionContext);
+
+ if (PreCB) {
+ PreCB(retraceCount);
+ }
+
+ if (vsync_timing_test_flag) {
+ u32 hcount, vcount;
+
+ GetCurrentDisplayPosition(&hcount, &vcount);
+
+ if(!((vcount == 1) || (vcount == (CurrTiming->nhlines / 2 + 1)))) {
+ vsync_timing_err_cnt++;
+ }
+ }
+
+ if (flushFlag != 0) {
+#if DEBUG
+ dbgCount = 0;
+#endif
+ if (VISetRegs() != 0) {
+ flushFlag = 0;
+ SIRefreshSamplingRate();
+ }
+ }
+
+ now_dtvStatus = VIGetDTVStatus();
+ if(now_dtvStatus != old_dtvStatus) {
+ __VISetYUVSEL((VIBool)now_dtvStatus);
+ }
+
+ old_dtvStatus = now_dtvStatus;
+ now_tvtype = VIGetTvFormat();
+
+ if (now_tvtype != old_tvtype) {
+ if (now_tvtype == VI_EURGB60) {
+ __VISetFilter4EURGB60(VI_ENABLE);
+ }
+ else {
+ __VISetFilter4EURGB60(VI_DISABLE);
+ }
+
+ switch (now_tvtype) {
+ case VI_PAL:
+ switch(g_current_time_to_dim) {
+ case VI_DM_10M:
+ NEW_TIME_TO_DIMMING = 30000;
+ break;
+ case VI_DM_15M:
+ NEW_TIME_TO_DIMMING = 45000;
+ break;
+ default:
+ NEW_TIME_TO_DIMMING = 15000;
+ break;
+ }
+ THD_TIME_TO_DVD_STOP = 90000;
+ break;
+ default:
+ switch(g_current_time_to_dim) {
+ case VI_DM_10M:
+ NEW_TIME_TO_DIMMING = 36000;
+ break;
+ case VI_DM_15M:
+ NEW_TIME_TO_DIMMING = 54000;
+ break;
+ default:
+ NEW_TIME_TO_DIMMING = 18000;
+ break;
+ }
+ THD_TIME_TO_DVD_STOP = 108000;
+ break;
+ }
+
+ _gIdleCount_dimming = 0;
+ _gIdleCount_dvd = 0;
+ }
+
+ old_tvtype = now_tvtype;
+
+ if (flushFlag3in1) {
+ while (Vdac_Flag_Changed) {
+ regIndex = (u32)__cntlzw(Vdac_Flag_Changed);
+ regIndex = (u32)(1 << (31 - regIndex));
+
+ switch(regIndex) {
+ case 1:
+ __VISetCGMS();
+ break;
+ case 2:
+ __VISetWSS();
+ break;
+ case 4:
+ __VISetClosedCaption();
+ break;
+ case 8:
+ __VISetMacrovision();
+ break;
+ case 0x10:
+ __VISetGamma();
+ break;
+ case 0x20:
+ __VISetTrapFilter();
+ break;
+ case 0x40:
+ __VISetRGBOverDrive();
+ break;
+ case 0x80:
+ __VISetRGBModeImm();
+ break;
+ }
+
+ Vdac_Flag_Changed &= ~regIndex;
+ }
+
+ flushFlag3in1 = 0;
+ }
+ #if DEBUG
+ else if (changed != 0) {
+ dbgCount++;
+ if (dbgCount > 60) {
+ OSReport("Warning: VIFlush() was not called for 60 frames although VI settings were changed\n");
+ dbgCount = 0;
+ }
+ }
+ #endif
+
+ if (PostCB) {
+ OSClearContext(&exceptionContext);
+ PostCB(retraceCount);
+ }
+
+ OSWakeupThread(&retraceQueue);
+ OSClearContext(&exceptionContext);
+ OSSetCurrentContext(context);
+
+ if (__VIDimming_All_Clear == TRUE) {
+ if(__OSSetVIForceDimming(FALSE, 0, 0) == TRUE) {
+ __VIDimming_All_Clear = FALSE;
+ _gIdleCount_dimming = 0;
+ }
+ }
+
+ for (i = 0; i < 10; i++) {
+ if(__VIDimmingFlag_DEV_IDLE[i] == 0) {
+ __VIDimmingFlag_DEV_IDLE[0] = 0;
+ break;
+ }
+ }
+
+ if(__VIDimmingFlag_RF_IDLE && __VIDimmingFlag_SI_IDLE && __VIDimmingFlag_DEV_IDLE[0]) {
+ if ((__VIDimmingFlag_Enable == TRUE) && (_gIdleCount_dimming < 0xFFFFFFFF)) {
+ _gIdleCount_dimming++;
+ }
+
+ if ((__VIDVDStopFlag_Enable == TRUE) && (_gIdleCount_dvd < 0xFFFFFFFF)) {
+ _gIdleCount_dvd++;
+ }
+ }
+ else {
+ if (_gIdleCount_dimming >= THD_TIME_TO_DIMMING) {
+ DimmingOFF_Pending = 1;
+ }
+ if (_gIdleCount_dvd >= THD_TIME_TO_DVD_STOP) {
+ __DVDRestartMotor();
+ }
+
+ _gIdleCount_dimming = 0;
+ _gIdleCount_dvd = 0;
+ THD_TIME_TO_DIMMING = NEW_TIME_TO_DIMMING;
+ }
+
+ if (__VIDimmingFlag_Enable_old != __VIDimmingFlag_Enable) {
+ if (__VIDimmingFlag_Enable == FALSE) {
+ if (_gIdleCount_dimming >= THD_TIME_TO_DIMMING) {
+ DimmingOFF_Pending = 1;
+ }
+ }
+
+ _gIdleCount_dimming = 0;
+ THD_TIME_TO_DIMMING = NEW_TIME_TO_DIMMING;
+ }
+
+ if (_gIdleCount_dimming == THD_TIME_TO_DIMMING) {
+ DimmingON_Pending = 1;
+ }
+
+ if (DimmingOFF_Pending) {
+ if (__OSSetVIForceDimming(FALSE, 2, 2) == TRUE) {
+ DimmingOFF_Pending = 0;
+ __VIDimmingState = FALSE;
+ }
+ }
+
+ if (DimmingON_Pending) {
+ if (__OSSetVIForceDimming(TRUE, 2, 2) == TRUE) {
+ DimmingON_Pending = 0;
+ __VIDimmingState = TRUE;
+ }
+ }
+
+ if (__VIDVDStopFlag_Enable_old != __VIDVDStopFlag_Enable) {
+ if (__VIDVDStopFlag_Enable == FALSE) {
+ if (_gIdleCount_dvd >= THD_TIME_TO_DVD_STOP) {
+ __DVDRestartMotor();
+ }
+ }
+ _gIdleCount_dvd = 0;
+ }
+
+ if(_gIdleCount_dvd == THD_TIME_TO_DVD_STOP) {
+ __DVDStopMotorAsync(&__DVDStopMotorCommandBlock, NULL);
+ }
+
+ __VIDimmingFlag_RF_IDLE = 1;
+ __VIDimmingFlag_SI_IDLE = 1;
+
+ for(i = 0; i < 10; i++)
+ {
+ __VIDimmingFlag_DEV_IDLE[i] = 1;
+ }
+
+ __VIDimmingFlag_Enable_old = __VIDimmingFlag_Enable;
+ __VIDVDStopFlag_Enable_old = __VIDVDStopFlag_Enable;
+
+ if ((NEW_TIME_TO_DIMMING > _gIdleCount_dimming) && (__VIDimmingState == FALSE)) {
+ THD_TIME_TO_DIMMING = NEW_TIME_TO_DIMMING;
+ }
+}
+
+VIRetraceCallback VISetPreRetraceCallback(VIRetraceCallback cb) {
+ BOOL enabled;
+ VIRetraceCallback oldcb;
+
+ oldcb = PreCB;
+ enabled = OSDisableInterrupts();
+ PreCB = cb;
+ OSRestoreInterrupts(enabled);
+ return oldcb;
+}
+
+VIRetraceCallback VISetPostRetraceCallback(VIRetraceCallback cb) {
+ BOOL enabled;
+ VIRetraceCallback oldcb;
+
+ oldcb = PostCB;
+ enabled = OSDisableInterrupts();
+ PostCB = cb;
+ OSRestoreInterrupts(enabled);
+ return oldcb;
+}
+
+timing_s* __VISetExtraTiming(timing_s* t) {
+ timing_s* old = timingExtra;
+
+ timingExtra = t;
+ return old;
+}
+
+#pragma dont_inline on
+static timing_s* getTiming(VITVMode mode) {
+ switch (mode) {
+ case VI_TVMODE_NTSC_INT: return &timing[0];
+ case VI_TVMODE_NTSC_DS: return &timing[1];
+ case VI_TVMODE_PAL_INT: return &timing[2];
+ case VI_TVMODE_PAL_DS: return &timing[3];
+ case VI_TVMODE_EURGB60_INT: return &timing[0];
+ case VI_TVMODE_EURGB60_DS: return &timing[1];
+ case VI_TVMODE_MPAL_INT: return &timing[4];
+ case VI_TVMODE_MPAL_DS: return &timing[5];
+ case VI_TVMODE_NTSC_PROG:
+ case VI_TVMODE_MPAL_PROG:
+ case VI_TVMODE_EURGB60_PROG: return &timing[6];
+ case VI_TVMODE_NTSC_3D: return &timing[7];
+ case VI_TVMODE_DEBUG_PAL_INT: return &timing[2];
+ case VI_TVMODE_DEBUG_PAL_DS: return &timing[3];
+ case VI_TVMODE_GCA_INT: return &timing[8];
+ case VI_TVMODE_GCA_PROG: return &timing[9];
+ case VI_TVMODE_PAL_PROG: return &timing[10];
+ case VI_TVMODE_EXTRA_INT:
+ case VI_TVMODE_EXTRA_DS:
+ case VI_TVMODE_EXTRA_PROG:
+ case VI_TVMODE_HD720_PROG: return timingExtra;
+ default:
+ return NULL;
+ }
+}
+#pragma dont_inline reset
+
+void __VIInit(VITVMode mode) {
+ timing_s* tm;
+ u32 nonInter;
+ u32 tv;
+ u32 tvForReg;
+ volatile u32 a;
+ u16 hct;
+ u16 vct;
+
+ nonInter = mode & 3;
+ tv = (u32)mode >> 2;
+ *(u32*)OSPhysicalToCached(0xCC) = tv;
+
+ tm = getTiming(mode);
+ __VIRegs[1] = 2;
+
+ // why?
+ for (a = 0; a < 1000; a++) {}
+
+ __VIRegs[1] = 0;
+ __VIRegs[3] = (u32)tm->hlw;
+ __VIRegs[2] = tm->hce | (tm->hcs << 8);
+ __VIRegs[5] = tm->hsy | ((tm->hbe640 & 0x1FF) << 7);
+ __VIRegs[4] = (tm->hbe640 >> 9) | (tm->hbs640 << 1);
+
+ if (encoderType == 0) {
+ __VIRegs[0x39] = tm->hbeCCIR656 | 0x8000;
+ __VIRegs[0x3A] = (u32)tm->hbsCCIR656;
+ }
+
+ __VIRegs[0] = (u32)tm->equ;
+ __VIRegs[7] = (u32)(tm->prbOdd + (tm->acv * 2) - 2);
+ __VIRegs[6] = (u32)(tm->psbOdd + 2);
+ __VIRegs[9] = (u32)(tm->prbEven + (tm->acv * 2) - 2);
+ __VIRegs[8] = (u32)(tm->psbEven + 2);
+ __VIRegs[11] = tm->bs1 | (tm->be1 << 5);
+ __VIRegs[10] = tm->bs3 | (tm->be3 << 5);
+ __VIRegs[13] = tm->bs2 | (tm->be2 << 5);
+ __VIRegs[12] = tm->bs4 | (tm->be4 << 5);
+ __VIRegs[36] = 0x2828;
+ __VIRegs[27] = 1;
+ __VIRegs[26] = 0x1001;
+ hct = tm->hlw + 1;
+ vct = (tm->nhlines / 2) + 1;
+ __VIRegs[25] = (u16)(u32)hct;
+ __VIRegs[24] = vct | 0x1000;
+
+ switch (tv) {
+ case VI_PAL:
+ case VI_MPAL:
+ case VI_DEBUG:
+ tvForReg = tv;
+ break;
+ default:
+ tvForReg = 0;
+ }
+
+ if (nonInter == 0 || nonInter == 1) {
+ __VIRegs[1] = ((nonInter & 1) << 2) | 1 | (tvForReg << 8);
+ __VIRegs[54] = 0;
+ return;
+ }
+
+ __VIRegs[1] = (tvForReg << 8) | 5;
+ __VIRegs[54] = 1;
+}
+
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#define CLAMP(val, min, max) ((val) > (max) ? (max) : (val) < (min) ? (min) : (val))
+
+static void AdjustPosition(u16 acv) {
+ s32 coeff;
+ s32 frac;
+
+ HorVer.AdjustedDispPosX = CLAMP((s16)HorVer.DispPosX + displayOffsetH, 0, 0x2D0 - HorVer.DispSizeX);
+ coeff = (HorVer.FBMode == VI_XFBMODE_SF) ? 2 : 1;
+ frac = HorVer.DispPosY & 1;
+ HorVer.AdjustedDispPosY = MAX((s16)HorVer.DispPosY + displayOffsetV, frac);
+ HorVer.AdjustedDispSizeY = HorVer.DispSizeY
+ + MIN((s16)HorVer.DispPosY + displayOffsetV - frac, 0)
+ - MAX((s16)HorVer.DispPosY + (s16)HorVer.DispSizeY + displayOffsetV - (((s16)acv * 2) - frac), 0);
+ HorVer.AdjustedPanPosY = HorVer.PanPosY
+ - (MIN((s16)HorVer.DispPosY + displayOffsetV - frac, 0) / coeff);
+ HorVer.AdjustedPanSizeY = HorVer.PanSizeY
+ + (MIN((s16)HorVer.DispPosY + displayOffsetV - frac, 0) / coeff)
+ - (MAX((s16)HorVer.DispPosY + (s16)HorVer.DispSizeY + displayOffsetV - (((s16)acv * 2) - frac), 0) / coeff);
+}
+
+static void ImportAdjustingValues(void) {
+ displayOffsetH = SCGetDisplayOffsetH();
+ displayOffsetV = 0;
+}
+
+void VIInit(void) {
+ u16 dspCfg;
+ u32 value;
+ u32 tv;
+ u32 tvInBootrom;
+
+ if (IsInitialized) {
+ return;
+ }
+
+ OSRegisterVersion(__VIVersion);
+ IsInitialized = TRUE;
+
+ if (!(__VIRegs[1] & 1)) {
+ __VIInit(VI_TVMODE_NTSC_INT);
+ }
+
+ retraceCount = 0;
+ changed = 0;
+ shdwChanged = 0;
+ changeMode = 0;
+ shdwChangeMode = 0;
+ flushFlag = 0;
+ flushFlag3in1 = 0;
+
+ __VIRegs[39] = taps[0] | ((taps[1] & 0x3F) << 10);
+ __VIRegs[38] = (taps[1] >> 6) | (taps[2] << 4);
+ __VIRegs[41] = taps[3] | ((taps[4] & 0x3F) << 10);
+ __VIRegs[40] = (taps[4] >> 6) | (taps[5] << 4);
+ __VIRegs[43] = taps[6] | ((taps[7] & 0x3F) << 10);
+ __VIRegs[42] = (taps[7] >> 6) | (taps[8] << 4);
+ __VIRegs[45] = taps[9] | (taps[10] << 8);
+ __VIRegs[44] = taps[11] | (taps[12] << 8);
+ __VIRegs[47] = taps[13] | (taps[14] << 8);
+ __VIRegs[46] = taps[15] | (taps[16] << 8);
+ __VIRegs[49] = taps[17] | (taps[18] << 8);
+ __VIRegs[48] = taps[19] | (taps[20] << 8);
+ __VIRegs[51] = taps[21] | (taps[22] << 8);
+ __VIRegs[50] = taps[23] | (taps[24] << 8);
+ __VIRegs[56] = 0x280;
+ ImportAdjustingValues();
+
+ tvInBootrom = *(u32*)OSPhysicalToCached(0xCC);
+ dspCfg = __VIRegs[1];
+ HorVer.nonInter = VIGetScanMode();
+ HorVer.tv = ((u32)(dspCfg) & 0x300) >> 8;
+
+ if (tvInBootrom == VI_EURGB60 || (tvInBootrom == VI_PAL && HorVer.tv == VI_NTSC)) {
+ HorVer.tv = VI_EURGB60;
+ }
+
+ tv = (HorVer.tv == 3) ? 0 : HorVer.tv;
+ HorVer.timing = getTiming((tv << 2) + HorVer.nonInter);
+ regs[1] = dspCfg;
+
+ CurrTiming = HorVer.timing;
+ CurrTvMode = HorVer.tv;
+
+ HorVer.DispSizeX = 640;
+ HorVer.DispSizeY = CurrTiming->acv * 2;
+ HorVer.DispPosX = (720 - HorVer.DispSizeX) / 2;
+ HorVer.DispPosY = 0;
+ AdjustPosition(CurrTiming->acv);
+ HorVer.FBSizeX = 640;
+ HorVer.FBSizeY = CurrTiming->acv * 2;
+ HorVer.PanPosX = 0;
+ HorVer.PanPosY = 0;
+ HorVer.PanSizeX = 640;
+ HorVer.PanSizeY = CurrTiming->acv * 2;
+ HorVer.FBMode = 0;
+
+ HorVer.wordPerLine = 40;
+ HorVer.std = 40;
+ HorVer.wpl = 40;
+ HorVer.xof = 0;
+ HorVer.black = 1;
+ HorVer.threeD = 0;
+ OSInitThreadQueue(&retraceQueue);
+ value = __VIRegs[24];
+ value &= ~0x8000;
+#if !DEBUG
+ value = (u16)value;
+#endif
+ __VIRegs[24] = value;
+ value = __VIRegs[26];
+ value = value & ~0x8000;
+#if !DEBUG
+ value = (u16)value;
+#endif
+ __VIRegs[26] = value;
+ PreCB = NULL;
+ PostCB = NULL;
+ __OSSetInterruptHandler(0x18, __VIRetraceHandler);
+ __OSUnmaskInterrupts(0x80);
+ OSRegisterShutdownFunction(&ShutdownFunctionInfo);
+
+ switch(VIGetTvFormat()) {
+ case VI_PAL:
+ THD_TIME_TO_DIMMING = 15000;
+ NEW_TIME_TO_DIMMING = 15000;
+ THD_TIME_TO_DVD_STOP = 90000;
+ break;
+ default:
+ THD_TIME_TO_DIMMING = 18000;
+ NEW_TIME_TO_DIMMING = 18000;
+ THD_TIME_TO_DVD_STOP = 108000;
+ break;
+ }
+
+ _gIdleCount_dimming = 0;
+ _gIdleCount_dvd = 0;
+ g_current_time_to_dim = VI_DM_DEFAULT;
+ __VIDimming_All_Clear = TRUE;
+ __VIDimmingState = FALSE;
+ VIEnableDimming(TRUE);
+
+ VIEnableDVDStopMotor(FALSE);
+ __VISetRevolutionModeSimple();
+}
+
+void VIWaitForRetrace(void) {
+ BOOL enabled;
+ u32 count;
+
+ enabled = OSDisableInterrupts();
+ count = retraceCount;
+ do {
+ OSSleepThread(&retraceQueue);
+ } while (count == retraceCount);
+ OSRestoreInterrupts(enabled);
+}
+
+static void setInterruptRegs(timing_s* tm) {
+#if DEBUG
+ u16 vct, hct;
+#else
+ u16 hct, vct;
+#endif
+ u16 borrow;
+
+ vct = tm->nhlines / 2;
+ borrow = tm->nhlines % 2;
+ hct = (u16)((borrow)? tm->hlw : (u16)0);
+ vct++;
+ hct++;
+ regs[25] = (u16)(u32)hct;
+ MARK_CHANGED(25);
+ regs[24] = vct | 0x1000;
+ MARK_CHANGED(24);
+
+ vct; // fixes regalloc
+}
+
+static void setPicConfig(u16 fbSizeX, VIXFBMode xfbMode, u16 panPosX, u16 panSizeX, u8* wordPerLine, u8* std, u8* wpl, u8* xof) {
+ *wordPerLine = (fbSizeX + 15) / 16;
+ *std = (xfbMode == VI_XFBMODE_SF) ? *wordPerLine : (u8)(*wordPerLine * 2);
+ *xof = panPosX % 16;
+ *wpl = (*xof + panSizeX + 15) / 16;
+ regs[0x24] = *std | (*wpl << 8);
+ changed |= 0x8000000;
+}
+
+static void setBBIntervalRegs(timing_s* tm) {
+ u16 val;
+
+ val = tm->bs1 | (tm->be1 << 5);
+ regs[11] = val;
+ changed |= 0x10000000000000;
+
+ val = tm->bs3 | (tm->be3 << 5);
+ regs[10] = val;
+ changed |= 0x20000000000000;
+
+ val = tm->bs2 | (tm->be2 << 5);
+ regs[13] = val;
+ changed |= 0x4000000000000;
+
+ val = tm->bs4 | (tm->be4 << 5);
+ regs[12] = val;
+ changed |= (1LL << (63-12));
+}
+
+static void setScalingRegs(u16 panSizeX, u16 dispSizeX, BOOL threeD) {
+ u32 scale;
+
+ panSizeX = threeD ? (panSizeX << 1) : panSizeX;
+ if (panSizeX < dispSizeX) {
+ scale = (u32)(dispSizeX + (panSizeX << 8) - 1) / dispSizeX;
+ regs[37] = scale | 0x1000;
+ changed |= 0x04000000;
+ regs[56] = (u32)panSizeX;
+ changed |= 0x80;
+ } else {
+ regs[37] = 0x100;
+ changed |= 0x04000000;
+ }
+}
+
+static void calcFbbs(u32 bufAddr, u16 panPosX, u16 panPosY, u8 wordPerLine, VIXFBMode xfbMode, u16 dispPosY, u32* tfbb, u32* bfbb) {
+ u32 bytesPerLine;
+ u32 xoffInWords;
+
+ xoffInWords = (u32)panPosX / 16;
+ bytesPerLine = (u32)wordPerLine * 32;
+ *tfbb = bufAddr + (xoffInWords << 5) + (bytesPerLine * panPosY);
+ *bfbb = (xfbMode == VI_XFBMODE_SF) ? *tfbb : *tfbb + bytesPerLine;
+
+ if (dispPosY % 2 == 1) {
+ u32 tmp = *tfbb;
+ *tfbb = *bfbb;
+ *bfbb = tmp;
+ }
+
+ *tfbb &= 0x3FFFFFFF;
+ *bfbb &= 0x3FFFFFFF;
+}
+
+static void setFbbRegs(SomeVIStruct* HorVer, u32* tfbb, u32* bfbb, u32* rtfbb, u32* rbfbb) {
+ u32 shifted;
+
+ calcFbbs(HorVer->bufAddr, HorVer->PanPosX, HorVer->AdjustedPanPosY, HorVer->wordPerLine, HorVer->FBMode, HorVer->AdjustedDispPosY, tfbb, bfbb);
+ if (HorVer->threeD) {
+ calcFbbs(HorVer->rbufAddr, HorVer->PanPosX, HorVer->AdjustedPanPosY, HorVer->wordPerLine, HorVer->FBMode, HorVer->AdjustedDispPosY, rtfbb, rbfbb);
+ }
+
+ if (*tfbb < 0x01000000U && *bfbb < 0x01000000U && *rtfbb < 0x01000000U && *rbfbb < 0x01000000U) {
+ shifted = 0;
+ } else {
+ shifted = 1;
+ }
+
+ if (shifted) {
+ *tfbb >>= 5;
+ *bfbb >>= 5;
+ *rtfbb >>= 5;
+ *rbfbb >>= 5;
+ }
+
+ regs[15] = (u16)(*tfbb & 0xFFFF);
+ MARK_CHANGED(15);
+ regs[14] = (shifted << 12) | ((*tfbb >> 16) | (HorVer->xof << 8));
+ MARK_CHANGED(14);
+ regs[19] = (u16)(*bfbb & 0xFFFF);
+ MARK_CHANGED(19);
+ regs[18] = (*bfbb >> 16);
+ MARK_CHANGED(18);
+
+ if (HorVer->threeD) {
+ regs[17] = (u16)(*rtfbb & 0xFFFF);
+ MARK_CHANGED(17);
+ regs[16] = *rtfbb >> 16;
+ MARK_CHANGED(16);
+ regs[21] = (u16)(*rbfbb & 0xFFFF);
+ MARK_CHANGED(21);
+ regs[20] = *rbfbb >> 16;
+ MARK_CHANGED(20);
+ }
+}
+
+static void setHorizontalRegs(timing_s* tm, u16 dispPosX, u16 dispSizeX) {
+ u32 hbe;
+ u32 hbs;
+ u32 hbeLo;
+ u32 hbeHi;
+
+ regs[3] = (u16)(u32)tm->hlw;
+ MARK_CHANGED(3);
+ regs[2] = tm->hce | (tm->hcs << 8);
+ MARK_CHANGED(2);
+
+ if (HorVer.tv == 8) {
+ hbe = (u32)(tm->hbe640 + 172);
+ hbs = tm->hbs640;
+ } else {
+ hbe = (u32)(tm->hbe640 - 40 + dispPosX);
+ hbs = (u32)(tm->hbs640 + 40 + dispPosX - (720 - dispSizeX));
+ }
+
+ hbeLo = hbe & 0x1FF;
+ hbeHi = hbe >> 9;
+
+ regs[5] = tm->hsy | (hbeLo << 7);
+ MARK_CHANGED(5);
+ regs[4] = hbeHi | (hbs * 2);
+ MARK_CHANGED(4);
+}
+
+static void setVerticalRegs(u16 dispPosY, u16 dispSizeY, u8 equ, u16 acv, u16 prbOdd, u16 prbEven, u16 psbOdd, u16 psbEven, BOOL black) {
+ u16 actualPrbOdd;
+ u16 actualPrbEven;
+ u16 actualPsbOdd;
+ u16 actualPsbEven;
+ u16 actualAcv;
+ u16 c;
+ u16 d;
+
+ if (HorVer.nonInter == 2 || HorVer.nonInter == 3) {
+ c = 1;
+ d = 2;
+ } else {
+ c = 2;
+ d = 1;
+ }
+
+ if ((dispPosY % 2) == 0) {
+ actualPrbOdd = prbOdd + (d * dispPosY);
+ actualPsbOdd = psbOdd + (d * (((c * acv) - dispSizeY) - dispPosY));
+ actualPrbEven = prbEven + (d * dispPosY);
+ actualPsbEven = psbEven + (d * (((c * acv) - dispSizeY) - dispPosY));
+ } else {
+ actualPrbOdd = prbEven + (d * dispPosY);
+ actualPsbOdd = psbEven + (d * (((c * acv) - dispSizeY) - dispPosY));
+ actualPrbEven = prbOdd + (d * dispPosY);
+ actualPsbEven = psbOdd + (d * (((c * acv) - dispSizeY) - dispPosY));
+ }
+
+ actualAcv = dispSizeY / c;
+
+ if (black) {
+ actualPrbOdd += 2 * actualAcv - 2;
+ actualPsbOdd += 2;
+ actualPrbEven += 2 * actualAcv - 2;
+ actualPsbEven += 2;
+ actualAcv = 0;
+ }
+
+ regs[0] = equ | (actualAcv << 4);
+ MARK_CHANGED(0);
+ regs[7] = (u16)(u32)actualPrbOdd;
+ MARK_CHANGED(7);
+ regs[6] = (u16)(u32)actualPsbOdd;
+ MARK_CHANGED(6);
+ regs[9] = (u16)(u32)actualPrbEven;
+ MARK_CHANGED(9);
+ regs[8] = (u16)(u32)actualPsbEven;
+ MARK_CHANGED(8);
+}
+
+static void PrintDebugPalCaution(void) {
+ static u32 message;
+
+ if (message == 0) {
+ message = 1;
+ OSReport("***************************************\n");
+ OSReport(" ! ! ! C A U T I O N ! ! ! \n");
+ OSReport("This TV format \"DEBUG_PAL\" is only for \n");
+ OSReport("temporary solution until PAL DAC board \n");
+ OSReport("is available. Please do NOT use this \n");
+ OSReport("mode in real games!!! \n");
+ OSReport("***************************************\n");
+ }
+}
+
+void VIConfigure(const GXRenderModeObj* rm) {
+ timing_s* tm;
+ u32 regDspCfg;
+ u32 regClksel;
+ BOOL enabled;
+ u32 newNonInter;
+ u32 tvInBootrom;
+ u32 tvInGame;
+
+ enabled = OSDisableInterrupts();
+ newNonInter = rm->viTVmode & 3;
+
+ if (HorVer.nonInter != newNonInter) {
+ changeMode = 1;
+ HorVer.nonInter = newNonInter;
+ }
+
+ VI_ASSERTMSGLINE1(2617, (rm->viHeight & 1) == 0,
+ "VIConfigure(): Odd number(%d) is specified to viHeight\n",
+ rm->viHeight);
+
+#if DEBUG
+ if (rm->xFBmode == VI_XFBMODE_DF || newNonInter == VI_TVMODE_NTSC_PROG || newNonInter == 3) {
+ VI_ASSERTMSGLINE2(2624, rm->xfbHeight == rm->viHeight,
+ "VIConfigure(): xfbHeight(%d) is not equal to viHeight(%d) when DF XFB mode or progressive mode is specified\n",
+ rm->xfbHeight, rm->viHeight);
+ }
+
+ if (rm->xFBmode == VI_XFBMODE_SF && newNonInter != VI_TVMODE_NTSC_PROG && newNonInter != 3) {
+ VI_ASSERTMSGLINE2(2632, rm->viHeight == rm->xfbHeight * 2,
+ "VIConfigure(): xfbHeight(%d) is not as twice as viHeight(%d) when SF XFB mode is specified\n",
+ rm->xfbHeight, rm->viHeight);
+ }
+#endif
+
+ tvInGame = (u32)rm->viTVmode >> 2;
+ tvInBootrom = *(u32*)OSPhysicalToCached(0xCC);
+
+ if (tvInGame == VI_DEBUG_PAL) {
+ PrintDebugPalCaution();
+ }
+
+ if (((tvInBootrom != VI_PAL && tvInBootrom != VI_EURGB60) && (tvInGame == VI_PAL || tvInGame == VI_EURGB60)) ||
+ ((tvInBootrom == VI_PAL || tvInBootrom == VI_EURGB60) && (tvInGame != VI_PAL && tvInGame != VI_EURGB60)))
+ {
+ OSPanic(__FILE__, 2651, "VIConfigure(): Tried to change mode from (%d) to (%d), which is forbidden\n", tvInBootrom, tvInGame);
+ }
+
+ if ((tvInGame == VI_NTSC) || (tvInGame == VI_MPAL)) {
+ HorVer.tv = tvInBootrom;
+ } else {
+ HorVer.tv = tvInGame;
+ }
+
+ HorVer.DispPosX = rm->viXOrigin;
+ HorVer.DispPosY = (HorVer.nonInter == 1) ? (u16)(rm->viYOrigin * 2) : rm->viYOrigin;
+ HorVer.DispSizeX = rm->viWidth;
+ HorVer.FBSizeX = rm->fbWidth;
+ HorVer.FBSizeY = rm->xfbHeight;
+ HorVer.FBMode = rm->xFBmode;
+ HorVer.PanSizeX = HorVer.FBSizeX;
+ HorVer.PanSizeY = HorVer.FBSizeY;
+ HorVer.PanPosX = 0;
+ HorVer.PanPosY = 0;
+ HorVer.DispSizeY = (HorVer.nonInter == 2) ? HorVer.PanSizeY :
+ (HorVer.nonInter == 3) ? HorVer.PanSizeY :
+ (HorVer.FBMode == VI_XFBMODE_SF) ? (u16)(HorVer.PanSizeY * 2) :
+ HorVer.PanSizeY;
+ HorVer.threeD = (HorVer.nonInter == 3) ? TRUE : FALSE;
+
+ tm = getTiming((HorVer.tv << 2) + HorVer.nonInter);
+ HorVer.timing = tm;
+
+ AdjustPosition(tm->acv);
+ VI_ASSERTMSGLINE2(2694, rm->viXOrigin <= tm->hlw + 40 - tm->hbe640,
+ "VIConfigure(): viXOrigin(%d) cannot be greater than %d in this TV mode\n",
+ rm->viXOrigin, tm->hlw + 40 - tm->hbe640);
+ VI_ASSERTMSGLINE2(2699, rm->viXOrigin + rm->viWidth >= 680 - tm->hbs640,
+ "VIConfigure(): viXOrigin + viWidth (%d) cannot be less than %d in this TV mode\n",
+ rm->viXOrigin + rm->viWidth, 680 - tm->hbs640);
+
+ setInterruptRegs(tm);
+
+ regDspCfg = regs[1];
+ regClksel = regs[54];
+
+ if (HorVer.nonInter == VI_PROGRESSIVE || HorVer.nonInter == 3) {
+ regDspCfg = (((u32)(regDspCfg)) & ~0x00000004) | (((u32)(1)) << 2);
+
+ if (HorVer.tv == VI_HD720) {
+ regClksel = (((u32)(regClksel)) & ~0x00000001) | 0;
+ } else {
+ regClksel = (((u32)(regClksel)) & ~0x00000001) | 1;
+ }
+
+ } else {
+ OLD_SET_REG_FIELD(2722, regDspCfg, 1, 2, HorVer.nonInter & 1);
+ regClksel = (((u32)(regClksel)) & ~0x00000001);
+ }
+
+ OLD_SET_REG_FIELD(2726, regDspCfg, 1, 3, HorVer.threeD);
+
+ if ((HorVer.tv == VI_PAL) || (HorVer.tv == VI_MPAL) || (HorVer.tv == 3)) {
+ OLD_SET_REG_FIELD(2730, regDspCfg, 2, 8, HorVer.tv);
+ } else {
+ regDspCfg = (((u32)(regDspCfg)) & ~0x00000300);
+ }
+
+ regs[1] = regDspCfg;
+ regs[54] = (u16)regClksel;
+
+ MARK_CHANGED(1);
+ MARK_CHANGED(54);
+
+ setScalingRegs(HorVer.PanSizeX, HorVer.DispSizeX, HorVer.threeD);
+ setHorizontalRegs(tm, HorVer.AdjustedDispPosX, HorVer.DispSizeX);
+ setBBIntervalRegs(tm);
+ setPicConfig(HorVer.FBSizeX, HorVer.FBMode, HorVer.PanPosX, HorVer.PanSizeX, &HorVer.wordPerLine, &HorVer.std, &HorVer.wpl, &HorVer.xof);
+ if (FBSet != 0) {
+ setFbbRegs(&HorVer, &HorVer.tfbb, &HorVer.bfbb, &HorVer.rtfbb, &HorVer.rbfbb);
+ }
+ setVerticalRegs(HorVer.AdjustedDispPosY, HorVer.AdjustedDispSizeY, tm->equ, tm->acv, tm->prbOdd, tm->prbEven, tm->psbOdd, tm->psbEven, HorVer.black);
+ OSRestoreInterrupts(enabled);
+}
+
+void VIConfigurePan(u16 xOrg, u16 yOrg, u16 width, u16 height) {
+ BOOL enabled;
+ timing_s* tm;
+
+#if DEBUG
+ VI_ASSERTMSGLINE1(2788, (xOrg & 1) == 0,
+ "VIConfigurePan(): Odd number(%d) is specified to xOrg\n",
+ xOrg);
+ if (HorVer.FBMode == VI_XFBMODE_DF) {
+ VI_ASSERTMSGLINE1(2793, (height & 1) == 0,
+ "VIConfigurePan(): Odd number(%d) is specified to height when DF XFB mode\n",
+ height);
+ }
+#endif
+ enabled = OSDisableInterrupts();
+ HorVer.PanPosX = xOrg;
+ HorVer.PanPosY = yOrg;
+ HorVer.PanSizeX = width;
+ HorVer.PanSizeY = height;
+ HorVer.DispSizeY = (HorVer.nonInter == 2) ? HorVer.PanSizeY :
+ (HorVer.nonInter == 3) ? HorVer.PanSizeY :
+ (HorVer.FBMode == VI_XFBMODE_SF) ? (u16)(HorVer.PanSizeY * 2) :
+ HorVer.PanSizeY;
+ tm = HorVer.timing;
+ AdjustPosition(tm->acv);
+ setScalingRegs(HorVer.PanSizeX, HorVer.DispSizeX, HorVer.threeD);
+ setPicConfig(HorVer.FBSizeX, HorVer.FBMode, HorVer.PanPosX, HorVer.PanSizeX, &HorVer.wordPerLine, &HorVer.std, &HorVer.wpl, &HorVer.xof);
+ if (FBSet != 0) {
+ setFbbRegs(&HorVer, &HorVer.tfbb, &HorVer.bfbb, &HorVer.rtfbb, &HorVer.rbfbb);
+ }
+ setVerticalRegs(HorVer.AdjustedDispPosY, HorVer.DispSizeY, tm->equ, tm->acv, tm->prbOdd, tm->prbEven, tm->psbOdd, tm->psbEven, HorVer.black);
+ OSRestoreInterrupts(enabled);
+}
+
+void VIFlush(void) {
+ BOOL enabled;
+ s32 regIndex;
+
+ enabled = OSDisableInterrupts();
+ shdwChangeMode |= changeMode;
+ changeMode = 0;
+ shdwChanged |= changed;
+
+ while (changed != 0) {
+ regIndex = cntlzd(changed);
+ shdwRegs[regIndex] = regs[regIndex];
+ changed &= ~((u64)1 << (63 - regIndex));
+ }
+
+ flushFlag = 1;
+ flushFlag3in1 = 1;
+ NextBufAddr = HorVer.bufAddr;
+ OSRestoreInterrupts(enabled);
+}
+
+void VISetNextFrameBuffer(void* fb) {
+ BOOL enabled;
+
+ VI_ASSERTMSGLINE1(2886, ((u32)fb & 0x1F) == 0,
+ "VISetNextFrameBuffer(): Frame buffer address(0x%08x) is not 32byte aligned\n",
+ fb);
+ enabled = OSDisableInterrupts();
+ HorVer.bufAddr = (u32)fb;
+ FBSet = 1;
+ setFbbRegs(&HorVer, &HorVer.tfbb, &HorVer.bfbb, &HorVer.rtfbb, &HorVer.rbfbb);
+ OSRestoreInterrupts(enabled);
+}
+
+void* VIGetNextFrameBuffer(void) {
+ return *(void**)(&NextBufAddr);
+}
+
+void* VIGetCurrentFrameBuffer(void) {
+ return *(void**)(&CurrBufAddr);
+}
+
+void VISetNextRightFrameBuffer(void* fb) {
+ BOOL enabled;
+
+ ASSERTMSGLINEV(2284, ((u32)fb & 0x1F) == 0,
+ "VISetNextFrameBuffer(): Frame buffer address(0x%08x) is not 32byte aligned\n",
+ fb);
+ enabled = OSDisableInterrupts();
+ HorVer.rbufAddr = (u32)fb;
+ FBSet = 1;
+ setFbbRegs(&HorVer, &HorVer.tfbb, &HorVer.bfbb, &HorVer.rtfbb, &HorVer.rbfbb);
+ OSRestoreInterrupts(enabled);
+}
+
+void VISetBlack(BOOL black) {
+ BOOL enabled;
+ timing_s* tm;
+
+ enabled = OSDisableInterrupts();
+ HorVer.black = black;
+ tm = HorVer.timing;
+ setVerticalRegs(HorVer.AdjustedDispPosY, HorVer.DispSizeY, tm->equ, tm->acv, tm->prbOdd, tm->prbEven, tm->psbOdd, tm->psbEven, HorVer.black);
+ OSRestoreInterrupts(enabled);
+}
+
+void VISet3D(BOOL threeD) {
+ BOOL enabled;
+ u32 reg;
+
+ enabled = OSDisableInterrupts();
+ HorVer.threeD = threeD;
+ reg = regs[1];
+ OLD_SET_REG_FIELD(2355, reg, 1, 3, HorVer.threeD);
+ regs[1] = reg;
+ MARK_CHANGED(1);
+ setScalingRegs(HorVer.PanSizeX, HorVer.DispSizeX, HorVer.threeD);
+ OSRestoreInterrupts(enabled);
+}
+
+u32 VIGetRetraceCount(void) {
+ return retraceCount;
+}
+
+static void GetCurrentDisplayPosition(u32* hct, u32* vct) {
+ u32 hcount, vcount0, vcount;
+ vcount = __VIRegs[VI_VERT_COUNT] & 0x7FF;
+
+ do {
+ vcount0 = vcount;
+ hcount = __VIRegs[VI_HORIZ_COUNT] & 0x7FF;
+ vcount = __VIRegs[VI_VERT_COUNT] & 0x7FF;
+ } while (vcount0 != vcount);
+
+ *hct = hcount;
+ *vct = vcount;
+}
+
+static u32 getCurrentHalfLine(void) {
+ u32 hcount, vcount;
+ GetCurrentDisplayPosition(&hcount, &vcount);
+
+ return ((vcount - 1) << 1) + ((hcount - 1) / CurrTiming->hlw);
+}
+
+static u32 getCurrentFieldEvenOdd(void) {
+ return (getCurrentHalfLine() < CurrTiming->nhlines) ? 1 : 0;
+}
+
+u32 VIGetNextField(void) {
+ s32 nextField;
+ BOOL enabled;
+#if !DEBUG
+ u8 unused[4];
+#endif
+
+ enabled = OSDisableInterrupts();
+ nextField = getCurrentFieldEvenOdd() ^ 1;
+ OSRestoreInterrupts(enabled);
+ return nextField ^ (HorVer.AdjustedDispPosY & 1);
+}
+
+u32 VIGetCurrentLine(void) {
+ u32 halfLine;
+ timing_s* tm;
+ BOOL enabled;
+
+ tm = CurrTiming;
+ enabled = OSDisableInterrupts();
+ halfLine = getCurrentHalfLine();
+ OSRestoreInterrupts(enabled);
+ if (halfLine >= tm->nhlines) {
+ halfLine -= tm->nhlines;
+ }
+ return halfLine >> 1U;
+}
+
+u32 VIGetTvFormat(void) {
+ u32 format;
+ BOOL enabled;
+
+ enabled = OSDisableInterrupts();
+
+ switch (CurrTvMode) {
+ case VI_NTSC:
+ case VI_DEBUG:
+ case 6:
+ case 7:
+ case 8:
+ format = VI_NTSC;
+ break;
+ case VI_PAL:
+ case VI_DEBUG_PAL:
+ format = VI_PAL;
+ break;
+ case VI_EURGB60:
+ case VI_MPAL:
+ format = CurrTvMode;
+ break;
+ default:
+ ASSERTLINE(3198, FALSE);
+ }
+
+ OSRestoreInterrupts(enabled);
+ return format;
+}
+
+u32 VIGetScanMode(void) {
+ u32 scanMode;
+ BOOL enabled = OSDisableInterrupts();
+
+ if ((u32)(__VIRegs[54] & 1) == 1) {
+ scanMode = 2;
+ } else if (((u32)(__VIRegs[1] & 4) >> 2) == 0) {
+ scanMode = 0;
+ } else {
+ scanMode = 1;
+ }
+
+ OSRestoreInterrupts(enabled);
+ return scanMode;
+}
+
+u32 VIGetDTVStatus(void) {
+ u32 dtvStatus;
+ BOOL enabled = OSDisableInterrupts();
+
+ dtvStatus = __VIRegs[55] & 3;
+ OSRestoreInterrupts(enabled);
+ return dtvStatus & 1;
+}
+
+void __VISetAdjustingValues(s16 x, s16 y) {
+ BOOL enabled;
+ timing_s* tm;
+
+ ASSERTMSGLINE(2611, (y & 1) == 0, "__VISetAdjustValues(): y offset should be an even number");
+ enabled = OSDisableInterrupts();
+ displayOffsetH = x;
+ displayOffsetV = y;
+ tm = HorVer.timing;
+ AdjustPosition(tm->acv);
+ setHorizontalRegs(tm, HorVer.AdjustedDispPosX, HorVer.DispSizeX);
+ if (FBSet != 0) {
+ setFbbRegs(&HorVer, &HorVer.tfbb, &HorVer.bfbb, &HorVer.rtfbb, &HorVer.rbfbb);
+ }
+ setVerticalRegs(HorVer.AdjustedDispPosY, HorVer.AdjustedDispSizeY, tm->equ, tm->acv, tm->prbOdd, tm->prbEven, tm->psbOdd, tm->psbEven, HorVer.black);
+ OSRestoreInterrupts(enabled);
+}
+
+void __VIGetAdjustingValues(s16* x, s16* y) {
+ BOOL enabled;
+
+ enabled = OSDisableInterrupts();
+ *x = displayOffsetH;
+ *y = displayOffsetV;
+ OSRestoreInterrupts(enabled);
+}
+
+// DEBUG NONMATCHING - wrong reg use, equivalent
+void __VIEnableRawPositionInterrupt(s16 x, s16 y, void (*callback)(s16, s16)) {
+ BOOL enabled;
+ u32 halfLine;
+ u32 halfLineOff;
+
+ enabled = OSDisableInterrupts();
+ __VIRegs[29] = x + 1U;
+ __VIRegs[31] = x + 1U;
+
+ if (HorVer.nonInter == 0) {
+ if (y & 1) {
+ halfLineOff = CurrTiming->prbEven + ((CurrTiming->equ * 3) + CurrTiming->nhlines);
+ __VIRegs[30] = (((halfLineOff / 2) + (y / 2)) + 1) | 0x1000;
+ } else {
+ halfLineOff = CurrTiming->prbOdd + (CurrTiming->equ * 3);
+ __VIRegs[28] = (((halfLineOff / 2) + (y / 2)) + 1) | 0x1000;
+ }
+ } else if (HorVer.nonInter == 1) {
+ ASSERTLINE(2702, (y & 1) == 0);
+ halfLine = CurrTiming->prbOdd + ((CurrTiming->equ * 3)) + y;
+ __VIRegs[28] = ((halfLine / 2) + 1) | 0x1000;
+ __VIRegs[30] = (((halfLine + CurrTiming->nhlines) / 2) + 1) | 0x1000;
+ } else if (HorVer.nonInter == 2) {
+ halfLine = CurrTiming->prbOdd + ((CurrTiming->equ * 3)) + y;
+ __VIRegs[28] = (halfLine + 1) | 0x1000;
+ __VIRegs[30] = 0;
+ }
+
+ PositionCallback = callback;
+ OSRestoreInterrupts(enabled);
+}
+
+void (*__VIDisableRawPositionInterrupt())(s16, s16) {
+ BOOL enabled;
+ void (*old)(s16, s16);
+
+ enabled = OSDisableInterrupts();
+ __VIRegs[28] = 0;
+ __VIRegs[30] = 0;
+
+ old = PositionCallback;
+ PositionCallback = 0;
+ OSRestoreInterrupts(enabled);
+ return old;
+}
+
+void __VIDisplayPositionToXY(u32 hct, u32 vct, s16* x, s16* y) {
+ u32 halfLine = ((vct - 1) << 1) + ((hct - 1) / CurrTiming->hlw);
+
+ if (HorVer.nonInter == VI_INTERLACE) {
+ if (halfLine < CurrTiming->nhlines) {
+ if (halfLine < CurrTiming->equ * 3 + CurrTiming->prbOdd) {
+ *y = -1;
+ } else if (halfLine >= CurrTiming->nhlines - CurrTiming->psbOdd) {
+ *y = -1;
+ } else {
+ *y = (s16)((halfLine - CurrTiming->equ * 3 - CurrTiming->prbOdd) & ~1);
+ }
+ } else {
+ halfLine -= CurrTiming->nhlines;
+
+ if (halfLine < CurrTiming->equ * 3 + CurrTiming->prbEven) {
+ *y = -1;
+ } else if (halfLine >= CurrTiming->nhlines - CurrTiming->psbEven) {
+ *y = -1;
+ } else {
+ *y = (s16)(((halfLine - CurrTiming->equ * 3 - CurrTiming->prbEven) & ~1) + 1);
+ }
+ }
+ } else if (HorVer.nonInter == VI_NON_INTERLACE) {
+ if (halfLine >= CurrTiming->nhlines) {
+ halfLine -= CurrTiming->nhlines;
+ }
+
+ if (halfLine < CurrTiming->equ * 3 + CurrTiming->prbOdd) {
+ *y = -1;
+ } else if (halfLine >= CurrTiming->nhlines - CurrTiming->psbOdd) {
+ *y = -1;
+ } else {
+ *y = (s16)((halfLine - CurrTiming->equ * 3 - CurrTiming->prbOdd) & ~1);
+ }
+ } else if (HorVer.nonInter == VI_PROGRESSIVE) {
+ if (halfLine < CurrTiming->nhlines) {
+ if (halfLine < CurrTiming->equ * 3 + CurrTiming->prbOdd) {
+ *y = -1;
+ } else if (halfLine >= CurrTiming->nhlines - CurrTiming->psbOdd) {
+ *y = -1;
+ } else {
+ *y = (s16)(halfLine - CurrTiming->equ * 3 - CurrTiming->prbOdd);
+ }
+ } else {
+ halfLine -= CurrTiming->nhlines;
+
+ if (halfLine < CurrTiming->equ * 3 + CurrTiming->prbEven) {
+ *y = -1;
+ } else if (halfLine >= CurrTiming->nhlines - CurrTiming->psbEven) {
+ *y = -1;
+ } else
+ *y = (s16)((halfLine - CurrTiming->equ * 3 - CurrTiming->prbEven) & ~1);
+ }
+ }
+
+ *x = (s16)(hct - 1);
+}
+
+void __VIGetCurrentPosition(s16* x, s16* y) {
+ u32 hcount, vcount;
+ GetCurrentDisplayPosition(&hcount, &vcount);
+ __VIDisplayPositionToXY(hcount, vcount, x, y);
+}
+
+void __VISetLatchMode(u32 mode) {
+ u32 reg;
+
+ reg = __VIRegs[1];
+ OLD_SET_REG_FIELD(2834, reg, 2, 4, mode);
+ OLD_SET_REG_FIELD(2835, reg, 2, 6, mode);
+ __VIRegs[1] = reg;
+}
+
+#pragma dont_inline on
+int __VIGetLatch0Position(s16* px, s16* py) {
+ u32 hcount;
+ u32 vcount;
+
+ if (((__VIRegs[32] & 0x8000) >> 15) != 0) {
+ vcount = __VIRegs[32] & 0x7FF;
+ hcount = __VIRegs[33] & 0x7FF;
+ __VIRegs[32] = 0;
+ __VIRegs[33] = 0;
+ __VIDisplayPositionToXY(hcount, vcount, px, py);
+ return 1;
+ }
+
+ *px = *py = -1;
+ return 0;
+}
+#pragma dont_inline reset
+
+#pragma dont_inline on
+int __VIGetLatch1Position(s16* px, s16* py) {
+ u32 hcount;
+ u32 vcount;
+
+ if (((__VIRegs[34] & 0x8000) >> 15) != 0) {
+ vcount = __VIRegs[34] & 0x7FF;
+ hcount = __VIRegs[35] & 0x7FF;
+ __VIRegs[34] = 0;
+ __VIRegs[35] = 0;
+ __VIDisplayPositionToXY(hcount, vcount, px, py);
+ return 1;
+ }
+
+ *px = *py = -1;
+ return 0;
+}
+#pragma dont_inline reset
+
+int __VIGetLatchPosition(u32 port, s16* px, s16* py) {
+ if (port == 0) {
+ return __VIGetLatch0Position(px, py);
+ } else {
+ return __VIGetLatch1Position(px, py);
+ }
+}
+
+BOOL VIEnableDimming(BOOL enable) {
+ u8 value;
+ BOOL old = __VIDimmingFlag_Enable;
+
+ if (enable == TRUE) {
+ value = SCGetScreenSaverMode();
+
+ if (value == 0) {
+ enable = FALSE;
+ }
+ }
+
+ __VIDimmingFlag_Enable = enable;
+ return old;
+}
+
+void VIResetDimmingCount() {
+ __VIResetDev0Idle();
+}
+
+BOOL VIEnableDVDStopMotor(BOOL enable) {
+ BOOL old = __VIDVDStopFlag_Enable;
+ __VIDVDStopFlag_Enable = enable;
+ return old;
+}
+
+BOOL __VIResetSIIdle() {
+ __VIDimmingFlag_SI_IDLE = 0;
+ return TRUE;
+}
+
+BOOL __VIResetDev0Idle() {
+ __VIDimmingFlag_DEV_IDLE[0] = 0;
+ return TRUE;
+}
diff --git a/src/revolution/vi/vi3in1.c b/src/revolution/vi/vi3in1.c
new file mode 100644
index 0000000000..dbc4ef2209
--- /dev/null
+++ b/src/revolution/vi/vi3in1.c
@@ -0,0 +1,501 @@
+#include <revolution/os.h>
+#include <revolution/vi.h>
+
+static VIVideo Vdac_Flag_Region;
+volatile u32 Vdac_Flag_Changed = 0;
+static u32 __current_3in1_video_mode = 0;
+static VIACPType __type;
+static u32 __tvType = 0xFF;
+
+static VIGammaObj gammaSet[] = {
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0030,
+ 0x0397, 0x3B49, 0x0010, 0x001D, 0x0036, 0x0058, 0x0082, 0x00B3, 0x00EB, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1080, 0x1B80, 0xEB00, 0x0000, 0x0028, 0x005A, 0x02DB, 0x0D8D, 0x3049, 0x0010, 0x001D,
+ 0x0036, 0x0058, 0x0082, 0x00B3, 0x00EB, 0x1000, 0x1000, 0x1040, 0x1100, 0x1880, 0x4200, 0xEB00,
+ 0x0000, 0x007A, 0x023C, 0x076D, 0x129C, 0x2724, 0x0010, 0x001D, 0x0036, 0x0058, 0x0082, 0x00B3,
+ 0x00EB, 0x1000, 0x1000, 0x10C0, 0x1580, 0x2900, 0x6200, 0xEB00, 0x004E, 0x0199, 0x052D, 0x0B24,
+ 0x1429, 0x20A4, 0x0010, 0x001D, 0x0036, 0x0058, 0x0082, 0x00B3, 0x00EB, 0x1000, 0x1040, 0x12C0,
+ 0x1DC0, 0x3B00, 0x78C0, 0xEB00, 0x00EC, 0x03D7, 0x0800, 0x0D9E, 0x143E, 0x1BDB, 0x0010, 0x001D,
+ 0x0036, 0x0058, 0x0082, 0x00B3, 0x00EB, 0x1000, 0x10C0, 0x16C0, 0x27C0, 0x4B80, 0x8980, 0xEB00,
+ 0x0276, 0x0666, 0x0A96, 0x0EF3, 0x13AC, 0x1849, 0x0010, 0x001D, 0x0036, 0x0058, 0x0082, 0x00B3,
+ 0x00EB, 0x1000, 0x1200, 0x1C00, 0x3280, 0x59C0, 0x9600, 0xEB00, 0x04EC, 0x08F5, 0x0C96, 0x0FCF,
+ 0x12C6, 0x1580, 0x0010, 0x001D, 0x0036, 0x0058, 0x0082, 0x00B3, 0x00EB, 0x1000, 0x1400, 0x2200,
+ 0x3CC0, 0x6640, 0x9FC0, 0xEB00, 0x0800, 0x0BAE, 0x0E00, 0x1030, 0x11CB, 0x1349, 0x0010, 0x001D,
+ 0x0036, 0x0058, 0x0082, 0x00B3, 0x00EB, 0x1000, 0x1680, 0x28C0, 0x4680, 0x7100, 0xA780, 0xEB00,
+ 0x0BB1, 0x0E14, 0x0F2D, 0x1018, 0x10E5, 0x1180, 0x0010, 0x001D, 0x0036, 0x0058, 0x0082, 0x00B3,
+ 0x00EB, 0x1000, 0x1980, 0x2F80, 0x4FC0, 0x7A00, 0xADC0, 0xEB00, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x10, 0x20, 0x40, 0x60, 0x80, 0xA0, 0xEB, 0x1000, 0x2000, 0x4000,
+ 0x6000, 0x8000, 0xA000, 0xEB00, 0x14EC, 0x11C2, 0x1078, 0x0FB6, 0x0F2F, 0x0EB6, 0x0010, 0x001D,
+ 0x0036, 0x0058, 0x0082, 0x00B3, 0x00EB, 0x1000, 0x2100, 0x3CC0, 0x5FC0, 0x8900, 0xB780, 0xEB00,
+ 0x19D8, 0x1333, 0x10D2, 0x0F6D, 0x0E5E, 0x0DA4, 0x0010, 0x001D, 0x0036, 0x0058, 0x0082, 0x00B3,
+ 0x00EB, 0x1000, 0x2500, 0x4300, 0x66C0, 0x8F40, 0xBB40, 0xEB00, 0x1EC4, 0x147A, 0x110F, 0x0F0C,
+ 0x0DA1, 0x0CB6, 0x0010, 0x001D, 0x0036, 0x0058, 0x0082, 0x00B3, 0x00EB, 0x1000, 0x2900, 0x4900,
+ 0x6D40, 0x94C0, 0xBE80, 0xEB00, 0x2400, 0x1570, 0x110F, 0x0EAA, 0x0D0F, 0x0BDB, 0x0010, 0x001D,
+ 0x0036, 0x0058, 0x0082, 0x00B3, 0x00EB, 0x1000, 0x2D40, 0x4EC0, 0x7300, 0x9980, 0xC180, 0xEB00,
+ 0x293B, 0x163D, 0x110F, 0x0E30, 0x0C7D, 0x0B24, 0x0010, 0x001D, 0x0036, 0x0058, 0x0082, 0x00B3,
+ 0x00EB, 0x1000, 0x3180, 0x5440, 0x7880, 0x9DC0, 0xC400, 0xEB00, 0x2E27, 0x170A, 0x10D2, 0x0DE7,
+ 0x0BEB, 0x0A80, 0x0010, 0x001D, 0x0036, 0x0058, 0x0082, 0x00B3, 0x00EB, 0x1000, 0x3580, 0x5980,
+ 0x7D40, 0xA1C0, 0xC640, 0xEB00, 0x3362, 0x175C, 0x10D2, 0x0D6D, 0x0B6D, 0x09ED, 0x0010, 0x001D,
+ 0x0036, 0x0058, 0x0082, 0x00B3, 0x00EB, 0x1000, 0x39C0, 0x5E40, 0x8200, 0xA540, 0xC840, 0xEB00,
+ 0x384E, 0x17AE, 0x10B4, 0x0D0C, 0x0AF0, 0x096D, 0x0010, 0x001D, 0x0036, 0x0058, 0x0082, 0x00B3,
+ 0x00EB, 0x1000, 0x3DC0, 0x62C0, 0x8640, 0xA880, 0xCA00, 0xEB00, 0x3D3B, 0x1800, 0x105A, 0x0CC3,
+ 0x0A72, 0x0900, 0x0010, 0x001D, 0x0036, 0x0058, 0x0082, 0x00B3, 0x00EB, 0x1000, 0x41C0, 0x6740,
+ 0x8A00, 0xAB80, 0xCB80, 0xEB00, 0x41D8, 0x1828, 0x103C, 0x0C49, 0x0A1F, 0x0892, 0x0010, 0x001D,
+ 0x0036, 0x0058, 0x0082, 0x00B3, 0x00EB, 0x1000, 0x4580, 0x6B40, 0x8DC0, 0xAE00, 0xCD00, 0xEB00,
+ 0x4676, 0x1851, 0x0FE1, 0x0C00, 0x09B6, 0x0836, 0x0010, 0x001D, 0x0036, 0x0058, 0x0082, 0x00B3,
+ 0x00EB, 0x1000, 0x4940, 0x6F40, 0x9100, 0xB080, 0xCE40, 0xEB00, 0x4AC4, 0x187A, 0x0FA5, 0x0B9E,
+ 0x0963, 0x07DB, 0x0010, 0x001D, 0x0036, 0x0058, 0x0082, 0x00B3, 0x00EB, 0x1000, 0x4CC0, 0x7300,
+ 0x9440, 0xB2C0, 0xCF80, 0xEB00, 0x4F13, 0x1851, 0x0F69, 0x0B6D, 0x090F, 0x0780, 0x0010, 0x001D,
+ 0x0036, 0x0058, 0x0082, 0x00B3, 0x00EB, 0x1000, 0x5040, 0x7640, 0x9700, 0xB500, 0xD0C0, 0xEB00,
+ 0x5313, 0x187A, 0x0F0F, 0x0B24, 0x08BC, 0x0736, 0x0010, 0x001D, 0x0036, 0x0058, 0x0082, 0x00B3,
+ 0x00EB, 0x1000, 0x5380, 0x79C0, 0x99C0, 0xB700, 0xD1C0, 0xEB00, 0x5713, 0x1851, 0x0EF0, 0x0AC3,
+ 0x087D, 0x06ED, 0x0010, 0x001D, 0x0036, 0x0058, 0x0082, 0x00B3, 0x00EB, 0x1000, 0x56C0, 0x7CC0,
+ 0x9C80, 0xB8C0, 0xD2C0, 0xEB00, 0x5B13, 0x1828, 0x0E96, 0x0A92, 0x0829, 0x06B6, 0x0010, 0x001D,
+ 0x0036, 0x0058, 0x0082, 0x00B3, 0x00EB, 0x1000, 0x5A00, 0x7FC0, 0x9EC0, 0xBA80, 0xD380, 0xEB00,
+ 0x5EC4, 0x1800, 0x0E78, 0x0A30, 0x0800, 0x066D, 0x0010, 0x001D, 0x0036, 0x0058, 0x0082, 0x00B3,
+ 0x00EB, 0x1000, 0x5D00, 0x8280, 0xA140, 0xBC00, 0xD480, 0xEB00, 0x6276, 0x17D7, 0x0E1E, 0x0A00,
+ 0x07C1, 0x0636, 0x0010, 0x001D, 0x0036, 0x0058, 0x0082, 0x00B3, 0x00EB, 0x1000, 0x6000, 0x8540,
+ 0xA340, 0xBD80, 0xD540, 0xEB00, 0x65D8, 0x17AE, 0x0DE1, 0x09CF, 0x0782, 0x0600, 0x0010, 0x001D,
+ 0x0036, 0x0058, 0x0082, 0x00B3, 0x00EB, 0x1000, 0x62C0, 0x87C0, 0xA540, 0xBF00, 0xD600, 0xEB00,
+ 0x693B, 0x1785, 0x0DA5, 0x0986, 0x0743, 0x05DB, 0x0010, 0x001D, 0x0036, 0x0058, 0x0082, 0x00B3,
+ 0x00EB, 0x1000, 0x6580, 0x8A40, 0xA740, 0xC040, 0xD680, 0xEB00};
+
+VIMacroVisionObj VINtscACPType1 = {0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1B,
+ 0x1B, 0x24, 0x07, 0xF8, 0x00, 0x00, 0x0F, 0x0F, 0x60,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+VIMacroVisionObj VINtscACPType2 = {0x3E, 0x1D, 0x11, 0x25, 0x11, 0x01, 0x07, 0x00, 0x1B,
+ 0x1B, 0x24, 0x07, 0xF8, 0x00, 0x00, 0x0F, 0x0F, 0x60,
+ 0x01, 0x0A, 0x00, 0x05, 0x04, 0x03, 0xFF, 0x00};
+
+VIMacroVisionObj VINtscACPType3 = {0x3E, 0x17, 0x15, 0x21, 0x15, 0x05, 0x05, 0x02, 0x1B,
+ 0x1B, 0x24, 0x07, 0xF8, 0x00, 0x00, 0x0F, 0x0F, 0x60,
+ 0x01, 0x0A, 0x00, 0x05, 0x04, 0x03, 0xFF, 0x00};
+
+VIMacroVisionObj VIPalACPType1 = {0x36, 0x1A, 0x22, 0x2A, 0x22, 0x05, 0x02, 0x00, 0x1C,
+ 0x3D, 0x14, 0x03, 0xFE, 0x01, 0x54, 0xFE, 0x7E, 0x60,
+ 0x00, 0x08, 0x00, 0x04, 0x07, 0x01, 0x55, 0x01};
+
+VIMacroVisionObj VIPalACPType2 = {0x36, 0x1A, 0x22, 0x2A, 0x22, 0x05, 0x02, 0x00, 0x1C,
+ 0x3D, 0x14, 0x03, 0xFE, 0x01, 0x54, 0xFE, 0x7E, 0x60,
+ 0x00, 0x08, 0x00, 0x04, 0x07, 0x01, 0x55, 0x01};
+
+VIMacroVisionObj VIPalACPType3 = {0x36, 0x1A, 0x22, 0x2A, 0x22, 0x05, 0x02, 0x00, 0x1C,
+ 0x3D, 0x14, 0x03, 0xFE, 0x01, 0x54, 0xFE, 0x7E, 0x60,
+ 0x00, 0x08, 0x00, 0x04, 0x07, 0x01, 0x55, 0x01};
+
+VIMacroVisionObj VIEurgb60ACPType1 = {0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1B,
+ 0x1B, 0x24, 0x07, 0xF8, 0x00, 0x00, 0x1E, 0x1E, 0x60,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01};
+
+VIMacroVisionObj VIEurgb60ACPType2 = {0x36, 0x1D, 0x11, 0x25, 0x11, 0x01, 0x07, 0x00, 0x1B,
+ 0x1B, 0x24, 0x07, 0xF8, 0x00, 0x00, 0x1E, 0x1E, 0x60,
+ 0x01, 0x0A, 0x00, 0x05, 0x04, 0x03, 0xFF, 0x01};
+
+VIMacroVisionObj VIEurgb60ACPType3 = {0x36, 0x17, 0x15, 0x21, 0x15, 0x05, 0x05, 0x02, 0x1B,
+ 0x1B, 0x24, 0x07, 0xF8, 0x00, 0x00, 0x1E, 0x1E, 0x60,
+ 0x01, 0x0A, 0x00, 0x05, 0x04, 0x03, 0xFF, 0x01};
+
+VIMacroVisionObj VIMpalACPType1 = {0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1B,
+ 0x1B, 0x24, 0x07, 0xF8, 0x00, 0x00, 0x0F, 0x0F, 0x60,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+VIMacroVisionObj VIMpalACPType2 = {0x36, 0x1D, 0x11, 0x25, 0x11, 0x01, 0x07, 0x00, 0x1B,
+ 0x1B, 0x24, 0x07, 0xF8, 0x00, 0x00, 0x0F, 0x0F, 0x60,
+ 0x01, 0x0A, 0x00, 0x05, 0x04, 0x03, 0xFF, 0x00};
+
+VIMacroVisionObj VIMpalACPType3 = {0x36, 0x17, 0x15, 0x21, 0x15, 0x05, 0x05, 0x02, 0x1B,
+ 0x1B, 0x24, 0x07, 0xF8, 0x00, 0x00, 0x0F, 0x0F, 0x60,
+ 0x01, 0x0A, 0x00, 0x05, 0x04, 0x03, 0xFF, 0x00};
+
+VIMacroVisionObj VIProgressiveACPType = {0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+VIMacroVisionObj VIZeroACPType = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+extern s32 __VISendI2CData(u8, u8*, s32);
+extern void WaitMicroTime(s32);
+
+static void __VISetVideoMode(VIVideo vmode, VIBool outsel) {
+ u8 buffer[2];
+ Vdac_Flag_Region = vmode;
+ buffer[0] = 1;
+ buffer[1] = (u8)((outsel << 5) | Vdac_Flag_Region);
+ __VISendI2CData(0xE0, buffer, 2);
+ WaitMicroTime(2);
+}
+
+static void __VISetCCSEL(VIBool ccsel) {
+ u8 buffer[2];
+
+ buffer[0] = 0x6A;
+ buffer[1] = VI_ENABLE;
+ __VISendI2CData((u8)0xE0, buffer, 2);
+ WaitMicroTime(2);
+}
+
+static void __VISetOverSampling(VIBool os) {
+ u8 buffer[2];
+ buffer[0] = 0x65;
+ buffer[1] = VI_ENABLE;
+ __VISendI2CData((u8)0xE0, buffer, 2);
+ WaitMicroTime(2);
+}
+
+void __VISetVolume(u8 volumeL, u8 volumeR) {
+ u8 buffer[3];
+ buffer[0] = 0x71;
+ buffer[1] = volumeL;
+ buffer[2] = volumeR;
+ __VISendI2CData((u8)0xE0, buffer, 3);
+ WaitMicroTime(2);
+}
+
+void __VISetYUVSEL(VIBool outsel) {
+ u8 buffer[2];
+ u32 tv;
+
+ tv = *(u32*)OSPhysicalToCached(0xCC);
+
+ switch (tv) {
+ case VI_PAL:
+ case VI_EURGB60:
+ Vdac_Flag_Region = VI_VMODE_PAL;
+ break;
+ case VI_MPAL:
+ Vdac_Flag_Region = VI_VMODE_MPAL;
+ break;
+ case VI_NTSC:
+ Vdac_Flag_Region = VI_VMODE_NTSC;
+ break;
+ default:
+ ASSERTMSGLINE(919, FALSE, "Unkwon TV mode!! TV mode is set to NTSC mode.\n");
+ Vdac_Flag_Region = VI_VMODE_NTSC;
+ break;
+ }
+
+ buffer[0] = 0x1;
+ buffer[1] = (u8)((outsel << 5) | Vdac_Flag_Region);
+ __VISendI2CData((u8)0xE0, buffer, 2);
+ WaitMicroTime(2);
+}
+
+void __VISetTiming(VITiming timing) {
+ u8 buffer[2];
+
+ buffer[0] = 0x0;
+ buffer[1] = timing;
+ __VISendI2CData((u8)0xE0, buffer, 2);
+ WaitMicroTime(2);
+}
+
+void __VISetFilter4EURGB60(VIBool enable) {
+ u8 buffer[2];
+ buffer[0] = 0x6E;
+ buffer[1] = (u8)enable;
+ __VISendI2CData((u8)0xE0, buffer, 2);
+ WaitMicroTime(2);
+}
+
+void __VISetVBICtrl(VIBool cgms, VIBool wss, VIBool cc) {
+ u8 buffer[2];
+
+ buffer[0] = 0x2;
+ buffer[1] = (u8)(((~wss & 0x1) << 2) | ((~cgms & 0x1) << 1) | (~cc & 0x1));
+ __VISendI2CData((u8)0xE0, buffer, 2);
+ WaitMicroTime(2);
+}
+
+static u8 __wd0 = 0xFF;
+static u8 __wd1 = 0xFF;
+static u8 __wd2 = 0xFF;
+
+void __VISetCGMS(void) {
+ u8 buffer[3];
+ buffer[0] = 5;
+ buffer[1] = (u8)(((__wd1 & 0xF) << 2) | (__wd0 & 3));
+ buffer[2] = __wd2;
+ __VISendI2CData((u8)0xE0, buffer, 3);
+ WaitMicroTime(2);
+}
+
+void __VISetCGMSClear(void) {
+ __wd0 = 0;
+ __wd1 = 0;
+ __wd2 = 0;
+ __VISetCGMS();
+}
+
+// NOTE: function doesn't exist in TP debug, needed for string data
+void VISetCGMS(void) {
+ OSReport("VISetCGMS(): Tried to set APS Trigger bit(WORD2[3:2]) \nwhen the scan is progressive in DTV mode, which is forbidden\n");
+}
+
+static u8 __gp1 = 0xFF;
+static u8 __gp2 = 0xFF;
+static u8 __gp3 = 0xFF;
+static u8 __gp4 = 0xFF;
+
+void __VISetWSS(void) {
+ u8 buffer[3];
+ buffer[0] = 0x8;
+ buffer[1] = (u8)(((__gp2 & 0xf) << 4) | (__gp1 & 0xf));
+ buffer[2] = (u8)(((__gp4 & 0x7) << 3) | (__gp3 & 0x7));
+ __VISendI2CData((u8)0xE0, buffer, 3);
+ WaitMicroTime(2);
+}
+
+void VISetWSS(u8 gp1, u8 gp2, u8 gp3, u8 gp4) {
+ if ((__gp1 == gp1) && (__gp2 == gp2) && (__gp3 == gp3) && (__gp4 == gp4)) {
+ return;
+ }
+
+ __gp1 = gp1;
+ __gp2 = gp2;
+ __gp3 = gp3;
+ __gp4 = gp4;
+
+ Vdac_Flag_Changed |= 2;
+}
+
+static u8 __cc1 = 0xFF;
+static u8 __cc2 = 0xFF;
+static u8 __cc3 = 0xFF;
+static u8 __cc4 = 0xFF;
+
+void __VISetClosedCaption(void) {
+ u8 buffer[5];
+ buffer[0] = 0x7A;
+ buffer[1] = (u8)(__cc1 & 0x7F);
+ buffer[2] = (u8)(__cc2 & 0x7F);
+ buffer[3] = (u8)(__cc3 & 0x7F);
+ buffer[4] = (u8)(__cc4 & 0x7F);
+ __VISendI2CData((u8)0xE0, buffer, 5);
+ WaitMicroTime(2);
+}
+
+void VISetClosedCaption(u8 cc1, u8 cc2, u8 cc3, u8 cc4) {
+ if ((__cc1 == cc1) && (__cc2 == cc2) && (__cc3 == cc3) && (__cc4 == cc4)) {
+ return;
+ }
+
+ __cc1 = cc1;
+ __cc2 = cc2;
+ __cc3 = cc3;
+ __cc4 = cc4;
+
+ Vdac_Flag_Changed |= 4;
+}
+
+void __VISetMacrovisionImm(VIMacroVisionObj* mparam) {
+ u8 i;
+ u8 buffer[27];
+
+ buffer[0] = 0x40;
+
+ for (i = 1; i < 27; i++) {
+ buffer[i] = mparam->m[i - 1];
+ }
+
+ __VISendI2CData((u8)0xE0, buffer, 27);
+ WaitMicroTime(2);
+}
+
+void __VISetMacrovision(void) {
+ switch (__type) {
+ case 2:
+ switch (__tvType) {
+ case VI_NTSC:
+ __VISetMacrovisionImm(&VINtscACPType1);
+ break;
+ case VI_PAL:
+ __VISetMacrovisionImm(&VIPalACPType1);
+ break;
+ case VI_MPAL:
+ __VISetMacrovisionImm(&VIMpalACPType1);
+ break;
+ case VI_EURGB60:
+ __VISetMacrovisionImm(&VIEurgb60ACPType1);
+ break;
+ }
+ break;
+ case 3:
+ switch (__tvType) {
+ case VI_NTSC:
+ __VISetMacrovisionImm(&VINtscACPType2);
+ break;
+ case VI_PAL:
+ __VISetMacrovisionImm(&VIPalACPType2);
+ break;
+ case VI_MPAL:
+ __VISetMacrovisionImm(&VIMpalACPType2);
+ break;
+ case VI_EURGB60:
+ __VISetMacrovisionImm(&VIEurgb60ACPType2);
+ break;
+ }
+ break;
+ case 4:
+ switch (__tvType) {
+ case VI_NTSC:
+ __VISetMacrovisionImm(&VINtscACPType3);
+ break;
+ case VI_PAL:
+ __VISetMacrovisionImm(&VIPalACPType3);
+ break;
+ case VI_MPAL:
+ __VISetMacrovisionImm(&VIMpalACPType3);
+ break;
+ case VI_EURGB60:
+ __VISetMacrovisionImm(&VIEurgb60ACPType3);
+ break;
+ }
+ break;
+ case 1:
+ __VISetMacrovisionImm(&VIZeroACPType);
+ break;
+ }
+}
+
+void __VISetGammaImm(VIGammaObj* gamma) {
+ u8 i, j;
+ u8 buffer[34];
+
+ buffer[0] = 0x10;
+ i = 1;
+ for (j = 0; j < 6; j++) {
+ buffer[i] = (u8)((gamma->a[j] >> 8) & 0xFF);
+ buffer[i + 1] = (u8)(gamma->a[j] & 0xFF);
+ i += 2;
+ }
+
+ for (j = 0; j < 7; j++) {
+ buffer[i] = gamma->yin[j];
+ i++;
+ }
+
+ for (j = 0; j < 7; j++) {
+ buffer[i] = (u8)((gamma->yout[j] >> 8) & 0xFF);
+ buffer[i + 1] = (u8)(gamma->yout[j] & 0xC0);
+ i += 2;
+ }
+
+ __VISendI2CData((u8)0xE0, buffer, 34);
+ WaitMicroTime(2);
+}
+
+void __VISetGamma1_0(void) {
+ __VISetGammaImm(&gammaSet[10]);
+}
+
+static VIGamma __gamma;
+void __VISetGamma(void) {
+ __VISetGammaImm(&gammaSet[__gamma]);
+}
+
+void VISetGamma(VIGamma gamma) {
+ if (__gamma != gamma) {
+ __gamma = gamma;
+ Vdac_Flag_Changed |= 0x10;
+ }
+}
+
+static VIBool __filter = 0xFF;
+
+void __VISetTrapFilterImm(VIBool filter) {
+ u8 buffer[2];
+ buffer[0] = 0x3;
+
+ if (filter == VI_TRUE) {
+ buffer[1] = VI_FALSE;
+ } else {
+ buffer[1] = VI_TRUE;
+ }
+ __VISendI2CData((u8)0xE0, buffer, 2);
+ WaitMicroTime(2);
+}
+
+void __VISetTrapFilter(void) {
+ u8 buffer[2];
+
+ buffer[0] = 0x3;
+
+ if (__filter == VI_TRUE) {
+ buffer[1] = VI_FALSE;
+ } else {
+ buffer[1] = VI_TRUE;
+ }
+
+ __VISendI2CData((u8)0xE0, buffer, 2);
+ WaitMicroTime(2);
+}
+
+void VISetTrapFilter(VIBool filter) {
+ if (__filter == filter) {
+ return;
+ }
+
+ __filter = filter;
+ Vdac_Flag_Changed |= 0x20;
+}
+
+static VIOverDrive __level;
+
+void __VISetRGBOverDrive(void) {
+ u8 buffer[2];
+
+ if (Vdac_Flag_Region == VI_VMODE_RGB) {
+ buffer[0] = 0xA;
+ buffer[1] = (u8)((__level << 1) | VI_ENABLE);
+ __VISendI2CData((u8)0xE0, buffer, 2);
+ WaitMicroTime(2);
+ } else {
+ buffer[0] = 0xA;
+ buffer[1] = VI_DISABLE;
+ __VISendI2CData((u8)0xE0, buffer, 2);
+ WaitMicroTime(2);
+ }
+}
+
+void VISetRGBOverDrive(VIOverDrive level) {
+ if (__level == level) {
+ return;
+ }
+ __level = level;
+ Vdac_Flag_Changed |= 0x40;
+}
+
+void VISetRGBModeImm(void) {
+ Vdac_Flag_Changed |= 0x80;
+}
+
+void __VISetRGBModeImm(void) {
+ __VISetVideoMode(VI_VMODE_RGB, VI_DISABLE);
+}
+
+void __VISetRevolutionModeSimple(void) {
+ u32 dtv;
+
+ __VISetCCSEL(VI_TRUE);
+ __VISetOverSampling(VI_TRUE);
+ dtv = VIGetDTVStatus();
+ __VISetYUVSEL((VIBool)dtv);
+ __VISetTiming(VI_TMG_GAME);
+ __VISetVolume(0x8E, 0x8E);
+ __VISetVBICtrl(VI_DISABLE, VI_DISABLE, VI_DISABLE);
+ __VISetCGMSClear();
+ VISetWSS(0, 0, 0, 0);
+ __VISetWSS();
+ VISetClosedCaption(0, 0, 0, 0);
+ __VISetClosedCaption();
+ __VISetMacrovisionImm(&VIZeroACPType);
+ VISetRGBOverDrive(VI_ODV_L1);
+ __VISetRGBOverDrive();
+ __VISetTrapFilterImm(VI_DISABLE);
+ __VISetGammaImm(&gammaSet[VI_GM_1_0]);
+ __current_3in1_video_mode = 0;
+}
diff --git a/src/revolution/wenc/wenc.c b/src/revolution/wenc/wenc.c
new file mode 100644
index 0000000000..a738e08f1a
--- /dev/null
+++ b/src/revolution/wenc/wenc.c
@@ -0,0 +1,117 @@
+#include <revolution/wenc.h>
+#include <string.h>
+
+s32 WENCGetEncodeData(WENCInfo* info, u32 flag, const s16* pcmData, s32 samples, u8* adpcmData) {
+ const f64 table[] = {0.89843750, 0.89843750, 0.89843750, 0.89843750,
+ 1.19921875, 1.59765625, 2.00000000, 2.39843750};
+
+ u8* dst;
+ const s16* src;
+ s32 i;
+ s32 da;
+ s32 l3, l2, l1, l0;
+ s32 dlx;
+ s32 xnc;
+ s32 offset;
+ s32 index;
+ s32 encodeSize;
+ s32 xn;
+ s32 dl;
+ s32 qn;
+ s32 dn;
+ s32 dlh;
+ s32 dlq;
+ u8 by;
+
+ encodeSize = (samples + 1) / 2;
+ memset(adpcmData, 0, encodeSize);
+
+ src = pcmData;
+ dst = adpcmData;
+
+ if ((flag & WENC_FLAG_USER_INFO) == 0) {
+ xn = 0;
+ dl = 127;
+ qn = 0;
+ dn = 0;
+ dlh = 0;
+ dlq = 0;
+ } else {
+ xn = info->xn;
+ dl = info->dl;
+ qn = info->qn;
+ dn = info->dn;
+ dlh = info->dlh;
+ dlq = info->dlq;
+ }
+
+ for (i = 0; i < samples; i++) {
+ l3 = l2 = l1 = l0 = 0;
+
+ da = *src++;
+ if (da < xn) {
+ l3 = 1;
+ }
+
+ dn = __abs(da - xn);
+ if (dn >= dl) {
+ l2 = 1;
+ dn -= dl;
+ }
+
+ dlh = dl / 2;
+ if (dn >= dlh) {
+ l1 = 1;
+ dn -= dlh;
+ }
+
+ dlq = dlh / 2;
+ if (dn >= dlq) {
+ l0 = 1;
+ dn -= dlq;
+ }
+
+ dlx = dlq / 2;
+ qn = (1 - l3 * 2) * (dl * l2 + dlh * l1 + dlq * l0 + dlx);
+
+ if (qn > 0xFFFF) {
+ qn = 0xFFFF;
+ }
+ if (qn < -0x10000) {
+ qn = -0x10000;
+ }
+
+ xnc = xn + qn;
+ if (xnc > 0x7FFF) {
+ xnc = 0x7FFF;
+ }
+ if (xnc < -0x8000) {
+ xnc = -0x8000;
+ }
+
+ xn = xnc;
+ offset = (i & 1) == 0 ? 4 : 0;
+
+ by = l3 * 8 + l2 * 4 + l1 * 2 + l0;
+ dst[i / 2] |= by << offset;
+
+ index = l2 * 4 + l1 * 2 + l0;
+ dl *= table[index];
+
+ if (dl <= 127) {
+ dl = 127;
+ }
+ if (dl >= 0x6000) {
+ dl = 0x6000;
+ }
+ }
+
+ info->xn = xn;
+ info->dl = dl;
+ info->qn = qn;
+ info->dn = dn;
+ info->dlh = dlh;
+ info->dlq = dlq;
+
+ return samples;
+}
diff --git a/src/revolution/wpad/WPAD.c b/src/revolution/wpad/WPAD.c
new file mode 100644
index 0000000000..ab34be44bb
--- /dev/null
+++ b/src/revolution/wpad/WPAD.c
@@ -0,0 +1,434 @@
+#include <revolution/os.h>
+#include <revolution/wpad.h>
+#include <revolution/wpad/wud.h>
+
+#include <string.h>
+
+#include "__wpad.h"
+
+wpad_cb_st* __rvl_p_wpadcb[WPAD_MAX_CONTROLLERS];
+
+static u8 _wpadSpeakerVol;
+
+static s8 __wpadGetQueueSize(struct WPADCmdQueue* cmdQueue);
+static u16 __wpadGetBTEBufferStatus(s32 chan);
+static u16 __wpadGetBTMBufferStatus(s32 chan);
+static BOOL __wpadPushCommand(struct WPADCmdQueue* cmdQueue, struct WPADCmd cmdBlk);
+
+void dummyString(void* p_buf) {
+ ASSERTLINE(0, p_buf != NULL);
+}
+
+static u32 __wpadFmt2Size(u32 fmt) {
+ u32 fmtSize;
+
+ switch (fmt) {
+ case WPAD_FMT_FS_BTN:
+ case WPAD_FMT_FS_BTN_ACC:
+ case WPAD_FMT_FS_BTN_ACC_DPD:
+ fmtSize = sizeof(WPADFSStatus);
+ break;
+ case WPAD_FMT_CLASSIC_BTN:
+ case WPAD_FMT_CLASSIC_BTN_ACC:
+ case WPAD_FMT_CLASSIC_BTN_ACC_DPD:
+ case WPAD_FMT_GUITAR:
+ case WPAD_FMT_DRUM:
+ case WPAD_FMT_TAIKO:
+ case WPAD_FMT_TURNTABLE:
+ fmtSize = sizeof(WPADCLStatus);
+ break;
+ case WPAD_FMT_BULK:
+ fmtSize = sizeof(WPADBKStatus);
+ break;
+ case WPAD_FMT_TRAIN:
+ fmtSize = sizeof(WPADTRStatus);
+ break;
+ case 20:
+ fmtSize = 0x32;
+ break;
+ case WPAD_FMT_BALANCE_CHECKER:
+ fmtSize = sizeof(WPADBLStatus);
+ break;
+ case WPAD_FMT_VSM:
+ fmtSize = sizeof(WPADVSStatus);
+ break;
+ case WPAD_FMT_BTN_ACC_DPD_EXTENDED:
+ fmtSize = sizeof(WPADStatusEx);
+ break;
+ case WPAD_FMT_MOTION_PLUS:
+ fmtSize = sizeof(WPADMPStatus);
+ break;
+ default:
+ fmtSize = sizeof(WPADStatus);
+ break;
+ }
+
+ return fmtSize;
+}
+
+u8 WPADGetRadioSensitivity(s32 chan) {
+ wpad_cb_st* p_wpd = __rvl_p_wpadcb[chan];
+ BOOL intrStatus = OSDisableInterrupts();
+ u8 radioSensitivity = p_wpd->radioSensitivity;
+
+ OSRestoreInterrupts(intrStatus);
+ return radioSensitivity;
+}
+
+void WPADRead(s32 chan, WPADStatus* status) {
+ wpad_cb_st* p_wpd = __rvl_p_wpadcb[chan];
+ BOOL intrStatus;
+ u8 rxBufIndex;
+ WPADStatus* rxStatus;
+ u32 fmtSize;
+
+ ASSERTLINE(4013, (0 <= chan) && (chan < WPAD_MAX_CONTROLLERS));
+ ASSERTLINE(4014, status != NULL);
+
+ intrStatus = OSDisableInterrupts();
+
+ rxBufIndex = p_wpd->rxBufIndex != 0 ? 0 : 1;
+ rxStatus = (WPADStatus*)p_wpd->rxBufs[rxBufIndex];
+ fmtSize = __wpadFmt2Size(p_wpd->dataFormat);
+
+ if (rxStatus->err != WPAD_ESUCCESS)
+ fmtSize = sizeof(WPADStatus);
+
+ memcpy(status, rxStatus, fmtSize);
+
+ OSRestoreInterrupts(intrStatus);
+}
+
+BOOL WPADIsSpeakerEnabled(s32 chan) {
+ BOOL enabled;
+ wpad_cb_st* p_wpd = __rvl_p_wpadcb[chan];
+ BOOL intrStatus = OSDisableInterrupts();
+
+ enabled = p_wpd->info.speaker;
+
+ OSRestoreInterrupts(intrStatus);
+ return enabled;
+}
+
+s32 WPADControlSpeaker(s32 chan, u32 command, WPADCallback* cb) {
+ u8 data[7] = {0x00, 0x00, 0xd0, 0x07, 0x40, 0x0c, 0x0e};
+ wpad_cb_st* p_wpd = __rvl_p_wpadcb[chan];
+ BOOL intrStatus = OSDisableInterrupts();
+ BOOL speakerEnabled = p_wpd->info.speaker;
+ BOOL handshakeFinished;
+ s32 status = p_wpd->status;
+
+ handshakeFinished = p_wpd->handshakeFinished;
+
+ OSRestoreInterrupts(intrStatus);
+
+ if (status == WPAD_ENODEV)
+ goto end;
+
+ if (!handshakeFinished) {
+ status = WPAD_EBUSY;
+ goto end;
+ }
+
+ if (command == WPAD_SPEAKER_DISABLE) {
+ if (!speakerEnabled) {
+ status = WPAD_ESUCCESS;
+ goto end;
+ }
+
+ intrStatus = OSDisableInterrupts();
+
+ if (WPADiIsAvailableCmdQueue(&p_wpd->stdCmdQueue, 5)) {
+ WPADiSendMuteSpeaker(&p_wpd->stdCmdQueue, TRUE, NULL);
+ WPADiSendWriteDataCmd(&p_wpd->stdCmdQueue, 0x01, WM_REG_SPEAKER_01, NULL);
+ WPADiSendWriteDataCmd(&p_wpd->stdCmdQueue, 0x00, WM_REG_SPEAKER_09, NULL);
+ WPADiSendEnableSpeaker(&p_wpd->stdCmdQueue, FALSE, NULL);
+ WPADiSendGetContStat(&p_wpd->stdCmdQueue, NULL, cb);
+
+ OSRestoreInterrupts(intrStatus);
+
+ return WPAD_ESUCCESS;
+ }
+
+ status = WPAD_EBUSY;
+ OSRestoreInterrupts(intrStatus);
+ } else {
+ switch (command) {
+ case WPAD_SPEAKER_ENABLE:
+ case WPAD_SPEAKER_CMD_05:
+ intrStatus = OSDisableInterrupts();
+
+ if (WPADiIsAvailableCmdQueue(&p_wpd->stdCmdQueue, 7)) {
+ WPADiSendEnableSpeaker(&p_wpd->stdCmdQueue, TRUE, NULL);
+ WPADiSendMuteSpeaker(&p_wpd->stdCmdQueue, TRUE, NULL);
+ WPADiSendWriteDataCmd(&p_wpd->stdCmdQueue, 0x01, WM_REG_SPEAKER_09, NULL);
+
+ // sends 0x80 instead of 0x08?
+ WPADiSendWriteDataCmd(&p_wpd->stdCmdQueue, 0x80, WM_REG_SPEAKER_01, NULL);
+
+ data[4] = _wpadSpeakerVol;
+ WPADiSendWriteData(&p_wpd->stdCmdQueue, &data, sizeof data, WM_REG_SPEAKER_01,
+ NULL);
+ WPADiSendMuteSpeaker(&p_wpd->stdCmdQueue, FALSE, NULL);
+ WPADiSendGetContStat(&p_wpd->stdCmdQueue, NULL, cb);
+
+ OSRestoreInterrupts(intrStatus);
+
+ return WPAD_ESUCCESS;
+ }
+
+ status = WPAD_EBUSY;
+
+ OSRestoreInterrupts(intrStatus);
+ break;
+ case WPAD_SPEAKER_MUTE:
+ if (!WPADiSendMuteSpeaker(&p_wpd->stdCmdQueue, TRUE, cb)) {
+ status = WPAD_EBUSY;
+ goto end;
+ }
+
+ return WPAD_ESUCCESS;
+ case WPAD_SPEAKER_UNMUTE:
+ if (!WPADiSendMuteSpeaker(&p_wpd->stdCmdQueue, FALSE, cb)) {
+ status = WPAD_EBUSY;
+ goto end;
+ }
+
+ return WPAD_ESUCCESS;
+ case WPAD_SPEAKER_PLAY:
+ if (!WPADiSendWriteDataCmd(&p_wpd->stdCmdQueue, 0x01, WM_REG_SPEAKER_08, cb)) {
+ status = WPAD_EBUSY;
+ goto end;
+ }
+
+ return WPAD_ESUCCESS;
+ }
+ }
+
+end:
+ if (cb)
+ (*cb)(chan, status);
+
+ return status;
+}
+
+BOOL __wpadIsBusyStream(s32 chan) {
+ BOOL intrStatus;
+ wpad_cb_st* p_wpd = __rvl_p_wpadcb[chan];
+ u8 radioQuality;
+ u32 devType;
+ u8 bufferStatus;
+ u16 bteBufferStatus;
+ u16 btmBufferStatus;
+ u8 audioFrames;
+ s8 queueSize;
+ u8 linkNumber;
+
+ intrStatus = OSDisableInterrupts();
+
+ radioQuality = p_wpd->radioQuality;
+ devType = p_wpd->devType;
+ bufferStatus = WUDGetBufferStatus();
+
+ queueSize = __wpadGetQueueSize(&p_wpd->stdCmdQueue);
+
+ bteBufferStatus = __wpadGetBTEBufferStatus(chan);
+ btmBufferStatus = __wpadGetBTMBufferStatus(chan);
+ audioFrames = p_wpd->audioFrames;
+
+ linkNumber = _WUDGetLinkNumber();
+
+ OSRestoreInterrupts(intrStatus);
+
+ if (radioQuality != WPAD_RADIO_QUALITY_GOOD || btmBufferStatus > 3 || bufferStatus == 10 ||
+ bufferStatus >= linkNumber * 2 + 2 || devType == WPAD_DEV_INITIALIZING || queueSize >= 21 ||
+ audioFrames >= 1)
+ {
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+}
+
+s32 WPADSendStreamData(s32 chan, void* p_buf, u16 len) {
+ wpad_cb_st* p_wpd = __rvl_p_wpadcb[chan];
+ BOOL intrStatus;
+ BOOL handshakeFinished;
+ s32 status;
+
+ ASSERTLINE(4528, p_buf != NULL);
+ ASSERTLINE(4529, len >= 0 && len <= 20);
+
+ intrStatus = OSDisableInterrupts();
+
+ status = p_wpd->status;
+ handshakeFinished = p_wpd->handshakeFinished;
+
+ OSRestoreInterrupts(intrStatus);
+
+ if (status == WPAD_ENODEV)
+ return WPAD_ENODEV;
+
+ if (!handshakeFinished)
+ return WPAD_EBUSY;
+
+ if (__wpadIsBusyStream(chan))
+ return WPAD_EBUSY;
+
+ if (!WPADiSendStreamData(&p_wpd->stdCmdQueue, p_buf, len))
+ return WPAD_EBUSY;
+
+ intrStatus = OSDisableInterrupts();
+
+ ++p_wpd->audioFrames;
+
+ OSRestoreInterrupts(intrStatus);
+
+ return WPAD_ESUCCESS;
+}
+
+BOOL WPADiSendEnableSpeaker(struct WPADCmdQueue* cmdQueue, BOOL enabled, WPADCallback* cb) {
+ BOOL success;
+ struct WPADCmd cmdBlk;
+ cmdBlk.reportID = RPTID_ENABLE_SPEAKER;
+ cmdBlk.dataLength = RPT14_SIZE;
+ cmdBlk.dataBuf[RPT14_SPEAKER_ENABLE] = enabled ? 4 : 0;
+ cmdBlk.cmdCB = cb;
+
+ success = __wpadPushCommand(cmdQueue, cmdBlk);
+ return success;
+}
+
+BOOL WPADiSendGetContStat(struct WPADCmdQueue* cmdQueue, WPADInfo* infoOut, WPADCallback* cb) {
+ BOOL success;
+ struct WPADCmd cmdBlk;
+ cmdBlk.reportID = RPTID_REQUEST_STATUS;
+ cmdBlk.dataLength = RPT15_SIZE;
+ cmdBlk.dataBuf[0] = 0;
+ cmdBlk.cmdCB = cb;
+ cmdBlk.statusReportOut = infoOut;
+
+ success = __wpadPushCommand(cmdQueue, cmdBlk);
+ return success;
+}
+
+BOOL WPADiSendWriteDataCmd(struct WPADCmdQueue* cmdQueue, u8 cmd, u32 address, WPADCallback* cb) {
+ return WPADiSendWriteData(cmdQueue, &cmd, sizeof(cmd), address, cb);
+}
+
+BOOL WPADiSendWriteData(struct WPADCmdQueue* cmdQueue, void const* p_buf, u16 len, u32 address,
+ WPADCallback* cb) {
+ BOOL success;
+ u8 packedLen = len & 0x1f;
+ struct WPADCmd cmdBlk;
+
+ ASSERTLINE(5830, len > 0 && len <= 16);
+ ASSERTLINE(5831, p_buf != NULL);
+
+ cmdBlk.reportID = RPTID_WRITE_DATA;
+ cmdBlk.dataLength = RPT16_SIZE;
+ cmdBlk.cmdCB = cb;
+ memcpy(&cmdBlk.dataBuf[RPT16_DATA_DST_ADDRESS], &address, sizeof address);
+ memcpy(&cmdBlk.dataBuf[RPT16_DATA_LENGTH], &packedLen, sizeof packedLen);
+ memcpy(&cmdBlk.dataBuf[RPT16_DATA], p_buf, len);
+
+ success = __wpadPushCommand(cmdQueue, cmdBlk);
+ return success;
+}
+
+BOOL WPADiSendStreamData(struct WPADCmdQueue* cmdQueue, void const* p_buf, u16 len) {
+ BOOL success;
+ u8 packedLen = len << 3;
+ struct WPADCmd cmdBlk;
+
+ ASSERTLINE(5873, len > 0 && len <= 20);
+
+ cmdBlk.reportID = RPTID_SEND_SPEAKER_DATA;
+ cmdBlk.dataLength = sizeof cmdBlk.dataBuf;
+ cmdBlk.dataBuf[RPT18_DATA_LENGTH] = packedLen;
+ cmdBlk.cmdCB = NULL;
+ memcpy(&cmdBlk.dataBuf[RPT18_DATA], p_buf, len);
+
+ success = __wpadPushCommand(cmdQueue, cmdBlk);
+ return success;
+}
+
+BOOL WPADiSendMuteSpeaker(struct WPADCmdQueue* cmdQueue, BOOL muted, WPADCallback* cb) {
+ BOOL success;
+ struct WPADCmd cmdBlk;
+
+ cmdBlk.reportID = RPTID_MUTE_SPEAKER;
+ cmdBlk.dataLength = RPT19_SIZE;
+ cmdBlk.dataBuf[RPT19_SPEAKER_MUTE] = muted ? 4 : 0;
+ cmdBlk.cmdCB = cb;
+
+ success = __wpadPushCommand(cmdQueue, cmdBlk);
+ return success;
+}
+
+BOOL WPADiIsAvailableCmdQueue(struct WPADCmdQueue* cmdQueue, s8 num) {
+ s8 queueSize = __wpadGetQueueSize(cmdQueue);
+
+ if ((u32)(queueSize + num) <= cmdQueue->length - 1)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+static s8 __wpadGetQueueSize(struct WPADCmdQueue* cmdQueue) {
+ BOOL intrStatus = OSDisableInterrupts();
+
+ s8 queueRemaining = cmdQueue->indexIn - cmdQueue->indexOut;
+
+ if (queueRemaining < 0)
+ queueRemaining += cmdQueue->length;
+
+ OSRestoreInterrupts(intrStatus);
+ return queueRemaining;
+}
+
+static BOOL __wpadPushCommand(struct WPADCmdQueue* cmdQueue, struct WPADCmd cmdBlk) {
+ BOOL intrStatus = OSDisableInterrupts();
+
+ if (cmdQueue->length - 1 == (u32)__wpadGetQueueSize(cmdQueue)) {
+ OSRestoreInterrupts(intrStatus);
+
+ return FALSE;
+ }
+
+ memset(&cmdQueue->queue[cmdQueue->indexIn], 0, sizeof cmdQueue->queue[cmdQueue->indexIn]);
+ memcpy(&cmdQueue->queue[cmdQueue->indexIn], &cmdBlk, sizeof cmdQueue->queue[cmdQueue->indexIn]);
+
+ cmdQueue->indexIn = (u32)cmdQueue->indexIn == ((cmdQueue->length) - 1) ? 0 : cmdQueue->indexIn + 1;
+
+ OSRestoreInterrupts(intrStatus);
+ return TRUE;
+}
+
+static u16 __wpadGetBTEBufferStatus(s32 chan) {
+ wpad_cb_st* p_wpd = __rvl_p_wpadcb[chan];
+ BOOL intrStatus = OSDisableInterrupts();
+ s32 status = p_wpd->status;
+ s8 dev_handle = p_wpd->devHandle;
+
+ OSRestoreInterrupts(intrStatus);
+
+ if (status == WPAD_ENODEV)
+ return 0;
+
+ return _WUDGetQueuedSize(dev_handle);
+}
+
+static u16 __wpadGetBTMBufferStatus(s32 chan) {
+ wpad_cb_st* p_wpd = __rvl_p_wpadcb[chan];
+ BOOL intrStatus = OSDisableInterrupts();
+ s32 status = p_wpd->status;
+ s8 dev_handle = p_wpd->devHandle;
+
+ OSRestoreInterrupts(intrStatus);
+
+ if (status == WPAD_ENODEV)
+ return 0;
+
+ return _WUDGetNotAckedSize(dev_handle);
+}
diff --git a/src/revolution/wpad/WUD.c b/src/revolution/wpad/WUD.c
new file mode 100644
index 0000000000..54685342a8
--- /dev/null
+++ b/src/revolution/wpad/WUD.c
@@ -0,0 +1,63 @@
+#include <revolution/os.h>
+#include <revolution/wpad/wud.h>
+
+#include "__wud.h"
+
+wud_cb_st __rvl_wudcb;
+
+static u16 _dev_handle_queue_size[WUD_MAX_DEV_ENTRY];
+static u16 _dev_handle_notack_num[WUD_MAX_DEV_ENTRY];
+
+u8 WUDGetBufferStatus(void) {
+ wud_cb_st* p_wcb = &__rvl_wudcb;
+ u8 ret;
+ BOOL intrStatus = OSDisableInterrupts();
+
+ ret = p_wcb->bufferStatus1 - p_wcb->bufferStatus0;
+
+ OSRestoreInterrupts(intrStatus);
+ return ret;
+}
+
+u16 _WUDGetQueuedSize(s8 dev_handle) {
+ u16 queuedSize;
+ BOOL intrStatus = OSDisableInterrupts();
+
+ if (dev_handle >= 0 && dev_handle < WUD_MAX_DEV_ENTRY)
+ queuedSize = WUDiGetQueueSizeForHandle(dev_handle);
+ else
+ queuedSize = 0;
+
+ OSRestoreInterrupts(intrStatus);
+ return queuedSize;
+}
+
+u16 _WUDGetNotAckedSize(s8 dev_handle) {
+ u16 notAckedSize;
+ BOOL intrStatus = OSDisableInterrupts();
+
+ if (dev_handle >= 0 && dev_handle < WUD_MAX_DEV_ENTRY)
+ notAckedSize = WUDiGetNotAckNumForHandle(dev_handle);
+ else
+ notAckedSize = 0;
+
+ OSRestoreInterrupts(intrStatus);
+ return notAckedSize;
+}
+
+u8 _WUDGetLinkNumber(void) {
+ wud_cb_st* p_wcb = &__rvl_wudcb;
+ BOOL intrStatus = OSDisableInterrupts();
+ u8 linkedNum = p_wcb->linkedNum;
+
+ OSRestoreInterrupts(intrStatus);
+ return linkedNum;
+}
+
+u16 WUDiGetQueueSizeForHandle(u8 dev_handle) {
+ return _dev_handle_queue_size[dev_handle];
+}
+
+u16 WUDiGetNotAckNumForHandle(u8 dev_handle) {
+ return _dev_handle_notack_num[dev_handle];
+}
diff --git a/src/revolution/wpad/__wpad.h b/src/revolution/wpad/__wpad.h
new file mode 100644
index 0000000000..4a14700fab
--- /dev/null
+++ b/src/revolution/wpad/__wpad.h
@@ -0,0 +1,538 @@
+#ifndef _REVOLUTION_WPAD_PRIVATE_H_
+#define _REVOLUTION_WPAD_PRIVATE_H_
+
+#include <revolution/os.h>
+#include <revolution/wpad.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define RPT_REPORT_ID 0
+#define RPT_MAX_SIZE 21
+
+// Limits
+#define RPTID_OUT_LO RPTID_SET_RUMBLE
+#define RPTID_OUT_HI RPTID_SET_DPD_CSB
+
+#define RPTID_IN_LO RPTID_STATUS
+#define RPTID_IN_HI RPTID_DATA_BTN_ACC_DPD18_2
+
+// All outgoing reports
+#define RPT_OUT_FLAGS 1
+
+#define RPT_OUT_FLAG_ENABLE_FEATURE 2
+#define RPT_OUT_FLAG_REQUEST_ACK_RPT 1
+#define RPT_OUT_FLAG_RUMBLE 0
+
+// All incoming reports with Button data (all except 0x3d)
+#define RPT_BTN0 1
+#define RPT_BTN1 2
+
+// All incoming reports with Accelerometer (Standard) data (0x31, 0x33, 0x35, 0x37)
+#define RPT_ACC_OFFSET 1
+#define RPT_ACC_SIZE 3
+
+/*
+ * Outgoing reports (from POV of the host)
+ */
+
+// Report 0x10: Set rumble status
+#define RPTID_SET_RUMBLE 0x10
+#define RPT10_SIZE 1
+
+#define RPT10_RUMBLE 0
+
+// Report 0x11: Set LED lights
+#define RPTID_SET_PORT 0x11 // internally called SetPort
+#define RPT11_SIZE 1
+
+#define RPT11_LED 0
+
+// Report 0x12: Set data reporting mode
+#define RPTID_SET_DATA_REPORT_MODE 0x12
+#define RPT12_SIZE 2
+
+#define RPT12_CONT_REPORT 0
+#define RPT12_DATA_REPORT_MODE 1
+
+// Report 0x13: Enable IR camera
+#define RPTID_ENABLE_DPD 0x13 // internally called the DPD
+#define RPT13_SIZE 1
+
+#define RPT13_DPD_ENABLE 0
+
+// Report 0x14: Enable IR camera
+#define RPTID_ENABLE_SPEAKER 0x14
+#define RPT14_SIZE 1
+
+#define RPT14_SPEAKER_ENABLE 0
+
+// Report 0x15: Request status report
+#define RPTID_REQUEST_STATUS 0x15
+#define RPT15_SIZE 1
+
+// Report 0x16: Write data
+#define RPTID_WRITE_DATA 0x16
+#define RPT16_SIZE RPT_MAX_SIZE
+
+#define RPT16_DATA_DST_ADDRESS 0
+#define RPT16_DATA_LENGTH 4
+#define RPT16_DATA 5
+
+// Report 0x17: Read data
+#define RPTID_READ_DATA 0x17
+#define RPT17_SIZE 6
+
+#define RPT17_DATA_SRC_ADDRESS 0
+#define RPT17_DATA_LENGTH 4
+
+// Report 0x18: Send speaker data
+#define RPTID_SEND_SPEAKER_DATA 0x18
+#define RPT18_SIZE RPT_MAX_SIZE
+
+#define RPT18_DATA_LENGTH 0
+#define RPT18_DATA 1
+
+// Report 0x19: Mute speaker
+#define RPTID_MUTE_SPEAKER 0x19
+#define RPT19_SIZE 1
+
+#define RPT19_SPEAKER_MUTE 0
+
+// Report 0x19: WPADiSendDPDCSB (?)
+#define RPTID_SEND_DPD_CSB 0x1a
+#define RPT1A_SIZE 1
+
+#define RPT1A_DPD_CSB 0
+
+/*
+ * Incoming reports (from POV of the host)
+ */
+
+// Report 0x20: Status report
+#define RPTID_STATUS 0x20
+#define RPT20_FLAGS 3
+#define RPT20_PROTO_FW 5
+#define RPT20_BATTERY 6
+
+// Report 0x21: Read Wiimote data
+#define RPTID_DATA_READ 0x21
+#define RPT21_SIZE_ERR 3
+#define RPT21_ADDR0 4
+#define RPT21_ADDR1 5
+#define RPT21_DATA 6
+
+// Report 0x22: Acknowledgement and request result
+#define RPTID_ACK 0x22
+#define RPT22_ACKED_RPT_ID 3
+#define RPT22_ERR_CODE 4
+
+// Reports 0x23-0x2f: Unused
+
+// Report 0x30: Buttons
+#define RPTID_DATA_BTN 0x30
+
+// Report 0x31: Buttons, Accelerometer (Standard)
+#define RPTID_DATA_BTN_ACC 0x31
+
+// Report 0x32: Buttons, Extension (8 bytes)
+#define RPTID_DATA_BTN_EXT8 0x32
+#define RPT32_EXT_OFFSET 3
+#define RPT32_EXT_LENGTH 8
+
+/* Report 0x33: Buttons, Accelerometer (Standard), IR Camera (Standard, 12
+ * bytes)
+ */
+#define RPTID_DATA_BTN_ACC_DPD12 0x33
+#define RPT33_DPD_OFFSET 6
+#define RPT33_DPD_LENGTH 12
+
+// Report 0x34: Buttons, Extension (19 bytes)
+#define RPTID_DATA_BTN_EXT19 0x34
+#define RPT34_EXT_OFFSET 3
+#define RPT34_EXT_LENGTH 19
+
+// Report 0x35: Buttons, Accelerometer (Standard), Extension (16 bytes)
+#define RPTID_DATA_BTN_ACC_EXT16 0x35
+#define RPT35_EXT_OFFSET 6
+#define RPT35_EXT_LENGTH 16
+
+// Report 0x36: Buttons, IR Camera (Basic, 10 bytes), Extension (9 bytes)
+#define RPTID_DATA_BTN_DPD10_EXT9 0x36
+#define RPT36_DPD_OFFSET 3
+#define RPT36_DPD_LENGTH 10
+#define RPT36_EXT_OFFSET 13
+#define RPT36_EXT_LENGTH 9
+
+/* Report 0x37: Buttons, Accelerometer (Standard), IR Camera (Basic, 10
+ * bytes), Extension (6 bytes)
+ */
+#define RPTID_DATA_BTN_ACC_DPD10_EXT9 0x37
+#define RPT37_DPD_OFFSET 6
+#define RPT37_DPD_LENGTH 10
+#define RPT37_EXT_OFFSET 16
+#define RPT37_EXT_LENGTH 6
+
+// Report 0x38-0x3c: Unused
+
+// Report 0x3d: Extension (21 bytes)
+#define RPTID_DATA_EXT21 0x3d
+#define RPT3D_EXT_OFFSET 1
+#define RPT3D_EXT_LENGTH 21
+
+/* Report 0x3e: Buttons, Accelerometer (Interleaved 1), IR Camera (Full 1,
+ * 18 bytes)
+ */
+#define RPTID_DATA_BTN_ACC_DPD18_1 0x3e
+#define RPT3E_DPD0 0
+#define RPT3E_DPD0_OFFSET 4
+#define RPT3E_DPD1 1
+#define RPT3E_DPD1_OFFSET 13
+
+/* Report 0x3e: Buttons, Accelerometer (Interleaved 2), IR Camera (Full 2,
+ * 18 bytes)
+ */
+#define RPTID_DATA_BTN_ACC_DPD18_2 0x3f
+#define RPT3F_DPD2 2
+#define RPT3F_DPD2_OFFSET 4
+#define RPT3F_DPD3 3
+#define RPT3F_DPD3_OFFSET 13
+
+#define WPAD_EXT_REG_SPEAKER 0xa2
+#define WPAD_EXT_REG_EXTENSION 0xa4
+#define WPAD_EXT_REG_MOTION_PLUS 0xa6
+#define WPAD_EXT_REG_DPD 0xb0
+
+#define RX_BUFFER_SIZE OSRoundUp32B(sizeof(WPADStatusEx))
+#define LINT_NUM_MAX_LENGTH 64
+#define LINT_NUM_MAX_BUFSIZ (1 + LINT_NUM_MAX_LENGTH + 1)
+
+#define WM_MEM_ADDR(addr_) ((addr_) & 0xffff)
+#define WM_EXT_REG_ADDR(type_, addr_) \
+ (((addr_) & 0xffff) | ((WPAD_EXT_REG_##type_) << 16) | (1 << 26))
+
+// https://wiibrew.org/wiki/Wiimote#EEPROM_Memory
+#define WM_ADDR_MEM_DEV_CONFIG_0 WM_MEM_ADDR(0x0000)
+#define WM_ADDR_MEM_GAME_INFO_0 WM_MEM_ADDR(0x002a)
+#define WM_ADDR_MEM_GAME_INFO_1 WM_MEM_ADDR(0x0062)
+
+// out of range??? wiibrew says the wiimote only has 0x1600 bytes of memory
+#define WM_ADDR_MEM_176C WM_MEM_ADDR(0x176c)
+#define WM_ADDR_MEM_1770 WM_MEM_ADDR(0x1770)
+
+/*
+ * Wiimote extension register addresses
+ */
+
+// Speaker register addresses
+#define WM_REG_SPEAKER_01 WM_EXT_REG_ADDR(SPEAKER, 0x01)
+#define WM_REG_SPEAKER_08 WM_EXT_REG_ADDR(SPEAKER, 0x08)
+#define WM_REG_SPEAKER_09 WM_EXT_REG_ADDR(SPEAKER, 0x09)
+
+// Extension register addresses
+#define WM_REG_EXTENSION_CONFIG WM_EXT_REG_ADDR(EXTENSION, 0x20)
+#define WM_REG_EXTENSION_40 WM_EXT_REG_ADDR(EXTENSION, 0x40)
+#define WM_REG_EXTENSION_CERT_PARAM WM_EXT_REG_ADDR(EXTENSION, 0x50)
+#define WM_REG_EXTENSION_F0 WM_EXT_REG_ADDR(EXTENSION, 0xf0)
+#define WM_REG_EXTENSION_CERT_CHALLENGE WM_EXT_REG_ADDR(EXTENSION, 0xf1)
+#define WM_REG_EXTENSION_F2 WM_EXT_REG_ADDR(EXTENSION, 0xf2)
+#define WM_REG_EXTENSION_F3 WM_EXT_REG_ADDR(EXTENSION, 0xf3)
+#define WM_REG_EXTENSION_EXT_TYPE_2 WM_EXT_REG_ADDR(EXTENSION, 0xf6)
+#define WM_REG_EXTENSION_CERT_PROBE WM_EXT_REG_ADDR(EXTENSION, 0xf7)
+#define WM_REG_EXTENSION_FA WM_EXT_REG_ADDR(EXTENSION, 0xfa)
+#define WM_REG_EXTENSION_FB WM_EXT_REG_ADDR(EXTENSION, 0xfb)
+#define WM_REG_EXTENSION_DEV_MODE WM_EXT_REG_ADDR(EXTENSION, 0xfe)
+#define WM_REG_EXTENSION_ID_BYTE WM_EXT_REG_ADDR(EXTENSION, 0xff)
+
+#define WM_EXTENSION_CONFIG_SIZE 0x20 // 0xa40020 - 0xa4003f
+#define WM_EXTENSION_CERT_PARAM_SIZE 0x40 // 0xa40050 - 0xa4008f
+
+// Wiimote Motion Plus register addresses
+#define WM_REG_MPLS_F0 WM_EXT_REG_ADDR(MOTION_PLUS, 0xf0)
+#define WM_REG_MPLS_DEV_MODE WM_EXT_REG_ADDR(MOTION_PLUS, 0xfe)
+#define WM_REG_MPLS_ID_BYTE WM_EXT_REG_ADDR(MOTION_PLUS, 0xff)
+
+// IR camera register addresses
+#define WM_REG_DPD_CONFIG_BLOCK_1 WM_EXT_REG_ADDR(DPD, 0x00)
+#define WM_REG_DPD_CONFIG_BLOCK_2 WM_EXT_REG_ADDR(DPD, 0x1a)
+#define WM_REG_DPD_30 WM_EXT_REG_ADDR(DPD, 0x30)
+#define WM_REG_DPD_DATA_FORMAT WM_EXT_REG_ADDR(DPD, 0x33)
+
+
+#define WPAD_NZFILTER_ACC 0
+#define WPAD_NZFILTER_DPD 1
+#define WPAD_NZFILTER_EXT 2
+#define WPAD_NZFILTER_MPLS 3
+#define WPAD_MAX_NZFILTERS 4
+
+
+#define WPAD_RADIO_QUALITY_GOOD 0 // 80+
+#define WPAD_RADIO_QUALITY_BAD 1 // 80-
+
+
+#define WPAD_DEV_CORE 0
+#define WPAD_DEV_FS 1
+#define WPAD_DEV_CLASSIC 2
+#define WPAD_DEV_BALANCE_CHECKER 3
+#define WPAD_DEV_VSM 4
+#define WPAD_DEV_MOTION_PLUS 5
+#define WPAD_DEV_MPLS_PT_FS 6
+#define WPAD_DEV_MPLS_PT_CLASSIC 7
+
+#define WPAD_DEV_TRAIN 16
+#define WPAD_DEV_GUITAR 17
+#define WPAD_DEV_DRUM 18
+#define WPAD_DEV_TAIKO 19
+#define WPAD_DEV_TURNTABLE 20
+
+// seems to be like maybe general purpose non-specific device types
+// maybe this was for testing or something? idk
+#define WPAD_DEV_BULK_1 21
+#define WPAD_DEV_BULK_2 22
+#define WPAD_DEV_BULK_3 23
+#define WPAD_DEV_BULK_4 24
+#define WPAD_DEV_BULK_5 25
+#define WPAD_DEV_BULK_6 26
+#define WPAD_DEV_BULK_7 27
+#define WPAD_DEV_BULK_8 28
+
+#define WPAD_DEV_MPLS_PT_UNKNOWN 250
+#define WPAD_DEV_251 251
+#define WPAD_DEV_252 252 // invalid device mode?
+#define WPAD_DEV_NONE 253 // sort of like WPAD_ENODEV (see __wpadAbortInitExtension in WPADHIDParser.c)
+#define WPAD_DEV_INITIALIZING 255 // see __a1_20_status_report
+
+
+struct WPADCmd {
+ /* 0x00 */ u32 reportID;
+ /* 0x04 */ u8 dataBuf[RPT_MAX_SIZE];
+ /* 0x1a */ u16 dataLength;
+ /* 0x1c */ void* dstBuf;
+ /* 0x20 */ u16 readLength;
+ /* 0x24 */ u32 readAddress;
+ /* 0x28 */ WPADInfo* statusReportOut;
+ /* 0x2c */ WPADCallback* cmdCB;
+}; // size 0x30
+
+struct WPADCmdQueue {
+ /* 0x00 */ s8 indexOut;
+ /* 0x01 */ s8 indexIn;
+ /* 0x04 */ struct WPADCmd* queue;
+ /* 0x08 */ u32 length;
+}; // size 0x0c
+
+struct WPADDevConfig {
+ /* 0x00 */ DPDObject dpd[WPAD_MAX_DPD_OBJECTS];
+ /* 0x20 */ s16 accX0g;
+ /* 0x22 */ s16 accY0g;
+ /* 0x24 */ s16 accZ0g;
+ /* 0x26 */ s16 accX1g;
+ /* 0x28 */ s16 accY1g;
+ /* 0x2a */ s16 accZ1g;
+ /* 0x2c */ u8 motor;
+ /* 0x2d */ u8 volume;
+ /* 0x30 */ u8 pad0_[2];
+};
+
+typedef struct WPADMplsCalibration {
+ /* 0x00 */ f32 pitchZero;
+ /* 0x04 */ f32 pitchScale;
+ /* 0x08 */ f32 yawZero;
+ /* 0x0c */ f32 yawScale;
+ /* 0x10 */ f32 rollZero;
+ /* 0x14 */ f32 rollScale;
+ /* 0x18 */ s32 degrees;
+} WPADMplsCalibration;
+
+struct WPADExtConfig {
+ union {
+ struct WPADFSConfig {
+ /* 0x00 */ s16 stickXCenter;
+ /* 0x02 */ s16 at_0x02;
+ /* 0x04 */ s16 at_0x04;
+ /* 0x06 */ s16 stickYCenter;
+ /* 0x08 */ s16 at_0x08;
+ /* 0x0a */ s16 at_0x0a;
+ /* 0x0c */ s16 accX0g;
+ /* 0x0e */ s16 accY0g;
+ /* 0x10 */ s16 accZ0g;
+ /* 0x12 */ s16 accX1g;
+ /* 0x14 */ s16 accY1g;
+ /* 0x16 */ s16 accZ1g;
+ } fs; // size 0x1a
+
+ struct WPADCLConfig {
+ /* 0x00 */ s16 lStickXCenter;
+ /* 0x02 */ s16 at_0x02;
+ /* 0x04 */ s16 at_0x04;
+ /* 0x06 */ s16 lStickYCenter;
+ /* 0x08 */ s16 at_0x08;
+ /* 0x0a */ s16 at_0x0a;
+ /* 0x0c */ s16 rStickXCenter;
+ /* 0x0e */ s16 at_0x0e;
+ /* 0x10 */ s16 at_0x10;
+ /* 0x12 */ s16 rStickYCenter;
+ /* 0x14 */ s16 at_0x14;
+ /* 0x16 */ s16 at_0x16;
+ /* 0x18 */ u8 triggerLZero;
+ /* 0x19 */ u8 triggerRZero;
+ } cl; // size 0x1a
+
+ u8 forceUnionSize_[0x1c]; // alignment?
+ }; // size 0x1c
+
+ struct WPADMplsConfig {
+ /* 0x00 */ WPADMplsCalibration high;
+ /* 0x1c */ WPADMplsCalibration low;
+ /* 0x38 */ u32 calibCRC;
+ /* 0x3c */ u16 calibID;
+ } /* 0x1a */ mpls;
+}; // size 0x5c
+
+typedef struct WPADGameInfo {
+ /* 0x00 */ OSTime timestamp;
+ /* 0x08 */ u16 gameTitle[16 + 1];
+ /* 0x2a */ char gameCode[4];
+ /* 0x2e */ u8 gameType;
+ /* 0x2f */ u8 checksum;
+ u8 _pad0[8];
+} WPADGameInfo; // size 0x38
+
+struct WPADMemBlock {
+ /* 0x00 */ BOOL busy;
+ /* 0x04 */ u8 const* data;
+ /* 0x08 */ u16 len;
+ /* 0x0c */ u32 addr;
+ /* 0x10 */ WPADCallback* cb;
+}; // size 0x14
+
+typedef struct /* possibly untagged, like kpad */ {
+ /* 0x000 */ WPADGameInfo gameInfo;
+ /* 0x038 */ s32 at_0x038[2]; /* unknown */ // WPADResult[2]? see __wpadGetGameInfo
+ /* 0x040 */ u8 rxBufMain[RX_BUFFER_SIZE];
+ /* 0x0a0 */ u8 rxBufs[2][RX_BUFFER_SIZE];
+ /* 0x160 */ struct WPADCmdQueue stdCmdQueue;
+ /* 0x16c */ struct WPADCmd stdCmdQueueList[24];
+ /* 0x5ec */ struct WPADCmdQueue extCmdQueue;
+ /* 0x16c */ struct WPADCmd extCmdQueueList[12];
+ /* 0x838 */ WPADInfo info;
+ /* 0x850 */ WPADInfo* infoOut;
+ /* 0x854 */ struct WPADDevConfig devConfig;
+ /* 0x884 */ struct WPADExtConfig extConfig;
+ /* 0x8e0 */ WPADCallback* cmdBlkCB;
+ /* 0x8e4 */ WPADExtensionCallback* extensionCB;
+ /* 0x8e8 */ WPADConnectCallback* connectCB;
+ /* 0x8ec */ WPADSamplingCallback* samplingCB;
+ /* 0x8f0 */ void* samplingBuf;
+ /* 0x8f4 */ u32 samplingBufIndex;
+ /* 0x8f8 */ u32 samplingBufSize;
+ /* 0x8fc */ u32 dataFormat;
+ /* 0x900 */ s32 status;
+ /* 0x904 */ u8 statusReqBusy;
+ /* 0x905 */ u8 devType;
+ /* 0x906 */ u8 devMode;
+ /* 0x907 */ s8 devHandle;
+ /* 0x908 */ int at_0x908; /* unknown */
+ /* 0x90c */ u8 rxBufIndex;
+ /* 0x90d */ s8 at_0x90d; /* unknown */
+ /* 0x90e */ u8 defaultDpdSize; // maybe?
+ /* 0x90f */ u8 currentDpdCommand;
+ /* 0x910 */ u8 pendingDpdCommand;
+ /* 0x911 */ u8 radioQuality;
+ /* 0x912 */ u8 radioQualityOkMs; // see __wpadCalcRadioQuality
+ /* 0x913 */ u8 audioFrames;
+ /* 0x914 */ u32 motorBusy;
+ /* 0x918 */ BOOL motorRunning;
+ /* 0x91c */ BOOL used;
+ /* 0x920 */ BOOL handshakeFinished;
+ /* 0x924 */ int configIndex;
+ /* 0x928 */ OSThreadQueue threadQueue; /* purpose unknown */
+ /* 0x930 */ WPADCallback* vsmCallback;
+ /* 0x934 */ u8 controlMplsBusy;
+ /* 0x935 */ u8 mplsCBReadBuf[2];
+ /* 0x937 */ u8 mplsCBCounter; // idk???
+ /* 0x938 */ u8 pendingMplsCommand;
+ /* 0x939 */ u8 noParseMplsCount;
+ /* 0x93a */ u8 isInitingMpls; // maybe?
+ /* 0x93b */ u8 hasReadExtType2; // maybe?
+ /* 0x93c */ u8 at_0x93c; /* unknown */
+ /* 0x93d */ u8 parseMPState;
+ /* 0x93e */ u8 wmParamOffset;
+ /* 0x93f */ u8 certWorkCounter; // idk???
+ /* 0x940 */ u16 certWorkMs;
+ /* 0x942 */ s16 certStateWorkMs;
+ /* 0x944 */ s8 certChallengeRandomBit;
+ /* 0x945 */ u8 certWorkBusy;
+ /* 0x946 */ s8 certValidityStatus;
+ /* 0x947 */ s8 certState;
+ /* 0x948 */ u32* certParamPtr;
+ /* 0x94c */ u32 certLintX[1 + 16 + 1];
+ /* 0x994 */ u32 certLintY[1 + 16 + 1];
+ /* 0x994 */ u32 certLintBig[LINT_NUM_MAX_BUFSIZ];
+ /* 0xae4 */ int at_0xae4; /* unknown */
+ /* 0xae8 */ OSTime lastControllerDataUpdate;
+ /* 0xaf0 */ u16 filterDiff[WPAD_MAX_NZFILTERS];
+ /* 0xaf8 */ u16 filterSame[WPAD_MAX_NZFILTERS];
+ /* 0xb00 */ OSTime lastReportSendTime;
+ /* 0xb08 */ u8 at_0xb08; /* unknown */
+ /* 0xb09 */ u8 calibrated;
+ /* 0xb0a */ u16 recalibHoldMs;
+ /* 0xb0c */ u8 encryptionKey[16];
+ /* 0xb1c */ u8 decryptAddTable[8];
+ /* 0xb24 */ u8 decryptXorTable[8];
+ /* 0xb2c */ u8 wmReadDataBuf[64];
+ /* 0xb6c */ u8* wmReadDataPtr;
+ /* 0xb70 */ u32 wmReadAddress;
+ /* 0xb74 */ int wmReadHadError;
+ /* 0xb78 */ u16 wmReadLength;
+ /* 0xb7a */ s8 at_0xb7a; /* unknown */
+ /* 0xb7b */ u8 radioSensitivity;
+ /* 0xb7c */ u16 copyOutCount;
+ /* 0xb7e */ u8 sleeping;
+ /* 0xb7f */ u8 lastReportID;
+ /* 0xb80 */ WPADCallback* getInfoCB;
+ /* 0xb84 */ u8 getInfoBusy;
+ /* 0xb85 */ u8 extState;
+ /* 0xb86 */ u8 savePower;
+ /* 0xb87 */ u8 blcBattery;
+ /* 0xb88 */ u8 savedDevType; // maybe?
+ /* 0xb89 */ u8 extWasDisconnected;
+ /* 0xb8a */ s16 reconnectExtMs;
+ /* 0xb8c */ struct WPADMemBlock memBlock;
+ /* 0xba0 */ WPADCallback* controlMplsCB;
+ /* 0xba4 */ u8 parseMPBuf;
+ /* 0xba5 */ u8 certProbeByte;
+ /* 0xba6 */ u8 dpdBusy;
+ /* 0xba7 */ u8 interleaveFlags;
+ /* 0xba8 */ u32 mplsCBReadAddress;
+ /* 0xbac */ u8 mplsCBState;
+ /* 0xbad */ u8 mplsUptimeMs;
+ /* 0xbae */ s8 certMayVerifyByCalibBlock;
+ u8 pad0_[2]; /* unknown (can't be alignment) */
+ /* 0xbb1 */ u8 certProbeStartingValue;
+ /* 0xbb2 */ u16 lastMplsCalibID;
+ /* 0xbb4 */ u32 lastMplsCalibCRC;
+ /* 0xbb8 */ u8 noParseExtCount;
+ /* 0xbb9 */ s8 extErr;
+ /* 0xbba */ u8 extDataLength;
+ /* 0xbbb */ u8 extDevType;
+ /* 0xbbc */ u8 currPwmDuty;
+ /* 0xbbd */ u8 pendingPwmDuty;
+ u8 pad1_[2]; /* unknown (can't be alignment) */
+ /* 0xbc0 */ u8 extDataBuf[32];
+} ATTRIBUTE_ALIGN(32) wpad_cb_st; // size 0xbe0
+
+BOOL WPADiIsAvailableCmdQueue(struct WPADCmdQueue* cmdQueue, s8 num);
+BOOL WPADiSendWriteDataCmd(struct WPADCmdQueue* cmdQueue, u8 cmd, u32 address, WPADCallback* cb);
+BOOL WPADiSendWriteData(struct WPADCmdQueue* cmdQueue, void const* p_buf, u16 len, u32 address, WPADCallback* cb);
+BOOL WPADiSendStreamData(struct WPADCmdQueue* cmdQueue, void const* p_buf, u16 len);
+BOOL WPADiSendMuteSpeaker(struct WPADCmdQueue* cmdQueue, BOOL muted, WPADCallback* cb);
+BOOL WPADiSendEnableSpeaker(struct WPADCmdQueue* cmdQueue, BOOL enabled, WPADCallback* cb);
+BOOL WPADiSendGetContStat(struct WPADCmdQueue* cmdQueue, WPADInfo* infoOut, WPADCallback* cb);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _REVOLUTION_WPAD_PRIVATE_H_
diff --git a/src/revolution/wpad/__wud.h b/src/revolution/wpad/__wud.h
new file mode 100644
index 0000000000..0f3c2499b0
--- /dev/null
+++ b/src/revolution/wpad/__wud.h
@@ -0,0 +1,79 @@
+#ifndef _REVOLUTION_WUD_PRIVATE_H_
+#define _REVOLUTION_WUD_PRIVATE_H_
+
+#include <revolution/os.h>
+#include <revolution/wpad/bte.h>
+#include <revolution/wpad/wud.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define WUD_MAX_DEV_ENTRY_FOR_STD 10
+#define WUD_MAX_DEV_ENTRY_FOR_SMP 6
+
+#define WUD_MAX_DEV_ENTRY 16
+
+#define WUD_DEV_HANDLE_INVALID (-1)
+
+struct WUDDevInfoList {
+ /* 0x00 */ WUDDevInfo* devInfo;
+ /* 0x04 */ struct WUDDevInfoList* prev;
+ /* 0x08 */ struct WUDDevInfoList* next;
+}; // size 0x0c
+
+typedef struct /* possibly untagged, like kpad */ {
+ /* 0x000 */ WUDSyncDeviceCallback* syncStdCB;
+ /* 0x004 */ WUDSyncDeviceCallback* syncSmpCB;
+ /* 0x008 */ WUDClearDeviceCallback* clearDevCB;
+ /* 0x00c */ u8 syncState;
+ /* 0x00d */ u8 deleteState;
+ /* 0x00e */ u8 linkKeyState;
+ /* 0x00f */ u8 stackState;
+ /* 0x010 */ u8 initState;
+ /* 0x011 */ u8 shutdownState;
+ /* 0x012 */ u8 devNums;
+ /* 0x013 */ u8 devSmpNums;
+ /* 0x014 */ struct WUDDevInfoList* smpListHead;
+ /* 0x018 */ struct WUDDevInfoList* smpListTail;
+ /* 0x01c */ struct WUDDevInfoList smpList[WUD_MAX_DEV_ENTRY_FOR_SMP];
+ /* 0x064 */ struct WUDDevInfoList* stdListHead;
+ /* 0x068 */ struct WUDDevInfoList* stdListTail;
+ /* 0x06c */ struct WUDDevInfoList stdList[WUD_MAX_DEV_ENTRY_FOR_STD];
+ /* 0x0e4 */ WUDDevInfo stdDevs[WUD_MAX_DEV_ENTRY_FOR_STD];
+ /* 0x4a4 */ WUDDevInfo smpDevs[WUD_MAX_DEV_ENTRY_FOR_SMP];
+ /* 0x6e4 */ u8 connectedNum;
+ /* 0x6e5 */ u8 linkedNum;
+ /* 0x6e6 */ u8 syncedNum;
+ /* 0x6e7 */ u8 syncSkipChecks;
+ /* 0x6e8 */ s8 syncLoopNum;
+ /* 0x6e9 */ u8 syncType;
+ /* 0x6ea */ u8 connectable;
+ /* 0x6eb */ u8 discoverable;
+ /* 0x6ec */ WUDHidReceiveCallback* hidRecvCB;
+ /* 0x6f0 */ WUDHidConnectCallback* hidConnCB;
+ /* 0x6f4 */ WUDAllocFunc* allocFunc;
+ /* 0x6f8 */ WUDFreeFunc* freeFunc;
+ /* 0x6fc */ BD_ADDR pairAddr;
+ /* 0x702 */ BD_ADDR hostAddr;
+ /* 0x708 */ s8 libStatus;
+ /* 0x709 */ u8 siPortStatus;
+ /* 0x70a */ u8 pmID;
+ /* 0x70b */ s8 syncRssi;
+ /* 0x710 */ OSAlarm alarm;
+ /* 0x740 */ u32 hhFlags; // some flags maybe?
+ /* 0x744 */ u16 bufferStatus0;
+ /* 0x746 */ u16 bufferStatus1;
+ /* 0x748 */ s8 initWaitDeviceUpFrames;
+ /* 0x749 */ s8 waitStartSearchFrames;
+ /* 0x74a */ s16 waitIncomingFrames;
+} wud_cb_st; // size 0x750
+
+u16 WUDiGetQueueSizeForHandle(u8 dev_handle);
+u16 WUDiGetNotAckNumForHandle(u8 dev_handle);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _REVOLUTION_WUD_PRIVATE_H_