diff --git a/build/win32/Cxbx.vcxproj b/build/win32/Cxbx.vcxproj
index c9092863b..b1aef1063 100644
--- a/build/win32/Cxbx.vcxproj
+++ b/build/win32/Cxbx.vcxproj
@@ -262,9 +262,9 @@
-
+
@@ -355,6 +355,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -607,8 +628,8 @@
-
+
diff --git a/build/win32/Cxbx.vcxproj.filters b/build/win32/Cxbx.vcxproj.filters
index 3c42b3e7f..7a598415d 100644
--- a/build/win32/Cxbx.vcxproj.filters
+++ b/build/win32/Cxbx.vcxproj.filters
@@ -229,9 +229,6 @@
Hardware
-
- Hardware
-
Shared
@@ -241,6 +238,9 @@
Hardware
+
+ Hardware
+
@@ -453,9 +453,6 @@
Hardware
-
- Hardware
-
Hardware
@@ -474,6 +471,9 @@
Hardware
+
+ Hardware
+
@@ -728,6 +728,69 @@
HLEDatabase\XOnline
+
+ Hardware
+
+
+ Hardware
+
+
+ Hardware
+
+
+ Hardware
+
+
+ Hardware
+
+
+ Hardware
+
+
+ Hardware
+
+
+ Hardware
+
+
+ Hardware
+
+
+ Hardware
+
+
+ Hardware
+
+
+ Hardware
+
+
+ Hardware
+
+
+ Hardware
+
+
+ Hardware
+
+
+ Hardware
+
+
+ Hardware
+
+
+ Hardware
+
+
+ Hardware
+
+
+ Hardware
+
+
+ Hardware
+
diff --git a/import/OpenXDK/include/xboxkrnl/xboxkrnl.h b/import/OpenXDK/include/xboxkrnl/xboxkrnl.h
index be82f8d87..e915b6555 100644
--- a/import/OpenXDK/include/xboxkrnl/xboxkrnl.h
+++ b/import/OpenXDK/include/xboxkrnl/xboxkrnl.h
@@ -1708,6 +1708,7 @@ KINTERRUPT_MODE;
// ******************************************************************
// * IRQ (Interrupt ReQuest) Priority Levels
// ******************************************************************
+#define PASSIVE_LEVEL 0
#define APC_LEVEL 1
#define DISPATCH_LEVEL 2
#define PROFILE_LEVEL 26
diff --git a/src/Common/Logging.h b/src/Common/Logging.h
index ecdb814ea..77f2cee74 100644
--- a/src/Common/Logging.h
+++ b/src/Common/Logging.h
@@ -231,6 +231,10 @@ extern thread_local std::string _logPrefix;
#define LOG_FUNC_RESULT(r) \
std::cout << _logFuncPrefix << " returns " << r << "\n";
+// LOG_FUNC_RESULT_TYPE logs the function return result using the overloaded << operator of the given type
+#define LOG_FUNC_RESULT_TYPE(type, r) \
+ std::cout << _logFuncPrefix << " returns " << (type)r << "\n";
+
// LOG_FORWARD indicates that an api is implemented by a forward to another API
#define LOG_FORWARD(api) \
LOG_INIT \
@@ -275,6 +279,7 @@ extern thread_local std::string _logPrefix;
#define LOG_FUNC_ARG_OUT(arg)
#define LOG_FUNC_END
#define LOG_FUNC_RESULT(r)
+#define LOG_FUNC_RESULT_TYPE(type, r)
#define LOG_FORWARD(arg)
#define LOG_IGNORED()
#define LOG_UNIMPLEMENTED()
@@ -303,6 +308,9 @@ extern thread_local std::string _logPrefix;
// RETURN logs the given result and then returns it (so this should appear last in functions)
#define RETURN(r) do { LOG_FUNC_RESULT(r) return r; } while (0)
+// RETURN_TYPE logs the given typed result and then returns it (so this should appear last in functions)
+#define RETURN_TYPE(type, r) do { LOG_FUNC_RESULT_TYPE(type, r) return r; } while (0)
+
#define LOG_ONCE(msg, ...) { static bool bFirstTime = true; if(bFirstTime) { bFirstTime = false; DbgPrintf("TRAC: " ## msg, __VA_ARGS__); } }
#define LOG_XBOX_CALL(func) DbgPrintf("TRAC: Xbox " ## func ## "() call\n");
diff --git a/src/CxbxKrnl/CxbxKrnl.cpp b/src/CxbxKrnl/CxbxKrnl.cpp
index 00ac7e1c8..2c1cda78f 100644
--- a/src/CxbxKrnl/CxbxKrnl.cpp
+++ b/src/CxbxKrnl/CxbxKrnl.cpp
@@ -64,7 +64,7 @@ namespace xboxkrnl
#include // For std::ostringstream
#include "devices\EEPROMDevice.h" // For g_EEPROM
-#include "devices\video\EmuNV2A.h" // For InitOpenGLContext
+#include "devices\video\nv2a.h" // For InitOpenGLContext
#include "devices\Xbox.h" // For InitXboxHardware()
#include "devices\LED.h" // For LED::Sequence
@@ -540,7 +540,7 @@ void TriggerPendingConnectedInterrupts()
{
for (int i = 0; i < MAX_BUS_INTERRUPT_LEVEL; i++) {
// If the interrupt is pending and connected, process it
- if (HalSystemInterrupts[i].IsPending() && EmuInterruptList[i]->Connected) {
+ if (HalSystemInterrupts[i].IsPending() && EmuInterruptList[i] && EmuInterruptList[i]->Connected) {
HalSystemInterrupts[i].Trigger(EmuInterruptList[i]);
}
}
diff --git a/src/CxbxKrnl/EmuD3D8.cpp b/src/CxbxKrnl/EmuD3D8.cpp
index ac00acb75..58354aac9 100755
--- a/src/CxbxKrnl/EmuD3D8.cpp
+++ b/src/CxbxKrnl/EmuD3D8.cpp
@@ -3079,7 +3079,7 @@ XTL::X_D3DSurface* WINAPI XTL::EMUPATCH(D3DDevice_GetBackBuffer2)
// Rather than create a new surface, we should forward to the Xbox version of GetBackBuffer,
// This gives us the correct Xbox surface to update.
// We get signatures for both backbuffer functions as it changed in later XDKs
- XB_trampoline(X_D3DSurface, WINAPI, D3DDevice_GetBackBuffer2, (INT));
+ XB_trampoline(X_D3DSurface *, WINAPI, D3DDevice_GetBackBuffer2, (INT));
XB_trampoline(VOID, WINAPI, D3DDevice_GetBackBuffer, (INT, D3DBACKBUFFER_TYPE, X_D3DSurface**));
@@ -3896,7 +3896,7 @@ HRESULT WINAPI XTL::EMUPATCH(D3DDevice_CreatePixelShader)
#if 0 // PatrickvL Dxbx pixel shader translation
// Attempt to recompile PixelShader
- hRet = DxbxUpdateActivePixelShader(pPSDef, hostShaderHandle);
+ hRet = DxbxUpdateActivePixelShader(pPSDef, &hostShaderHandle);
// redirect to windows d3d
DEBUG_D3DRESULT(hRet, "g_pD3DDevice8->CreatePixelShader");
#endif
diff --git a/src/CxbxKrnl/EmuFS.cpp b/src/CxbxKrnl/EmuFS.cpp
index 507615fd8..27f6d3f69 100644
--- a/src/CxbxKrnl/EmuFS.cpp
+++ b/src/CxbxKrnl/EmuFS.cpp
@@ -541,7 +541,7 @@ void EmuGenerateFS(Xbe::TLS *pTLS, void *pTLSData)
InitializeListHead(&(Prcb->DpcListHead));
Prcb->DpcRoutineActive = FALSE;
- NewPcr->Irql = APC_LEVEL; // See KeLowerIrql;
+ NewPcr->Irql = PASSIVE_LEVEL; // See KeLowerIrql;
}
// Initialize a fake PrcbData.CurrentThread
diff --git a/src/CxbxKrnl/EmuKrnl.cpp b/src/CxbxKrnl/EmuKrnl.cpp
index 95dcdcc6d..03b275f9a 100644
--- a/src/CxbxKrnl/EmuKrnl.cpp
+++ b/src/CxbxKrnl/EmuKrnl.cpp
@@ -178,7 +178,7 @@ void KiUnexpectedInterrupt()
void CallSoftwareInterrupt(const xboxkrnl::KIRQL SoftwareIrql)
{
switch (SoftwareIrql) {
- case 0:
+ case PASSIVE_LEVEL:
KiUnexpectedInterrupt();
break;
case APC_LEVEL: // = 1 // HalpApcInterrupt
@@ -393,7 +393,7 @@ XBSYSAPI EXPORTNUM(160) xboxkrnl::KIRQL FASTCALL xboxkrnl::KfRaiseIrql
IN KIRQL NewIrql
)
{
- LOG_FUNC_ONE_ARG(NewIrql);
+ LOG_FUNC_ONE_ARG_TYPE(KIRQL_TYPE, NewIrql);
// Inlined KeGetCurrentIrql() :
PKPCR Pcr = KeGetPcr();
@@ -407,7 +407,7 @@ XBSYSAPI EXPORTNUM(160) xboxkrnl::KIRQL FASTCALL xboxkrnl::KfRaiseIrql
KeBugCheckEx(IRQL_NOT_GREATER_OR_EQUAL, (PVOID)OldIrql, (PVOID)NewIrql, 0, 0);
}
- RETURN(OldIrql);
+ RETURN_TYPE(KIRQL_TYPE, OldIrql);
}
inline int bsr(const uint32_t a) { DWORD result; _BitScanReverse(&result, a); return result; }
@@ -422,7 +422,7 @@ XBSYSAPI EXPORTNUM(161) xboxkrnl::VOID FASTCALL xboxkrnl::KfLowerIrql
IN KIRQL NewIrql
)
{
- LOG_FUNC_ONE_ARG(NewIrql);
+ LOG_FUNC_ONE_ARG_TYPE(KIRQL_TYPE, NewIrql);
KPCR* Pcr = KeGetPcr();
@@ -470,7 +470,7 @@ XBSYSAPI EXPORTNUM(163) xboxkrnl::VOID FASTCALL xboxkrnl::KiUnlockDispatcherData
IN KIRQL OldIrql
)
{
- LOG_FUNC_ONE_ARG(OldIrql);
+ LOG_FUNC_ONE_ARG_TYPE(KIRQL_TYPE, OldIrql);
if (!(KeGetCurrentPrcb()->DpcRoutineActive)) // Avoid KeIsExecutingDpc(), as that logs
HalRequestSoftwareInterrupt(DISPATCH_LEVEL);
diff --git a/src/CxbxKrnl/EmuKrnlDbg.cpp b/src/CxbxKrnl/EmuKrnlDbg.cpp
index d6c0ce880..6d1618dce 100644
--- a/src/CxbxKrnl/EmuKrnlDbg.cpp
+++ b/src/CxbxKrnl/EmuKrnlDbg.cpp
@@ -36,7 +36,7 @@
// ******************************************************************
#define _XBOXKRNL_DEFEXTRN_
-#define LOG_PREFIX "KRNL"
+#define LOG_PREFIX "DBG "
// prevent name collisions
namespace xboxkrnl
@@ -115,8 +115,7 @@ XBSYSAPI EXPORTNUM(8) xboxkrnl::ULONG _cdecl xboxkrnl::DbgPrint
LOG_FUNC_ARG("...")
LOG_FUNC_END;
- if (Format != NULL)
- {
+ if (Format != NULL) {
char szBuffer[MAX_PATH];
va_list argp;
@@ -125,10 +124,8 @@ XBSYSAPI EXPORTNUM(8) xboxkrnl::ULONG _cdecl xboxkrnl::DbgPrint
vsprintf(szBuffer, Format, argp);
va_end(argp);
- //LogPrintf("[EmuKrnl] DbgPrint: %s", szBuffer);
-
- EmuWarning(szBuffer);
- //DbgPrintf(szBuffer);
+ printf(szBuffer); // Note : missing newlines can occur
+ fflush(stdout);
}
RETURN(STATUS_SUCCESS);
diff --git a/src/CxbxKrnl/EmuKrnlHal.cpp b/src/CxbxKrnl/EmuKrnlHal.cpp
index 6c373eba9..26374a907 100644
--- a/src/CxbxKrnl/EmuKrnlHal.cpp
+++ b/src/CxbxKrnl/EmuKrnlHal.cpp
@@ -125,7 +125,7 @@ XBSYSAPI EXPORTNUM(38) xboxkrnl::VOID FASTCALL xboxkrnl::HalClearSoftwareInterru
KIRQL Request
)
{
- LOG_FUNC_ONE_ARG(Request);
+ LOG_FUNC_ONE_ARG_TYPE(KIRQL_TYPE, Request);
// Mask out this interrupt request
DWORD InterruptMask = 1 << Request;
@@ -438,7 +438,7 @@ XBSYSAPI EXPORTNUM(48) xboxkrnl::VOID FASTCALL xboxkrnl::HalRequestSoftwareInter
IN KIRQL Request
)
{
- LOG_FUNC_ONE_ARG(Request);
+ LOG_FUNC_ONE_ARG_TYPE(KIRQL_TYPE, Request);
DWORD InterruptMask = 1 << Request;
diff --git a/src/CxbxKrnl/EmuKrnlKe.cpp b/src/CxbxKrnl/EmuKrnlKe.cpp
index e5ccccab3..6c0226ecf 100644
--- a/src/CxbxKrnl/EmuKrnlKe.cpp
+++ b/src/CxbxKrnl/EmuKrnlKe.cpp
@@ -560,7 +560,7 @@ XBSYSAPI EXPORTNUM(103) xboxkrnl::KIRQL NTAPI xboxkrnl::KeGetCurrentIrql(void)
KPCR* Pcr = KeGetPcr();
KIRQL Irql = (KIRQL)Pcr->Irql;
- RETURN(Irql);
+ RETURN_TYPE(KIRQL_TYPE, Irql);
}
// ******************************************************************
@@ -706,7 +706,7 @@ XBSYSAPI EXPORTNUM(109) xboxkrnl::VOID NTAPI xboxkrnl::KeInitializeInterrupt
LOG_FUNC_ARG(ServiceRoutine)
LOG_FUNC_ARG(ServiceContext)
LOG_FUNC_ARG(Vector)
- LOG_FUNC_ARG(Irql)
+ LOG_FUNC_ARG_TYPE(KIRQL_TYPE, Irql)
LOG_FUNC_ARG(InterruptMode)
LOG_FUNC_ARG(ShareVector)
LOG_FUNC_END;
diff --git a/src/CxbxKrnl/EmuKrnlLogging.cpp b/src/CxbxKrnl/EmuKrnlLogging.cpp
index b9dcbf53d..ad00e49c5 100644
--- a/src/CxbxKrnl/EmuKrnlLogging.cpp
+++ b/src/CxbxKrnl/EmuKrnlLogging.cpp
@@ -237,6 +237,15 @@ ENUM2STR_START(KINTERRUPT_MODE)
ENUM2STR_CASE(Latched)
ENUM2STR_END_and_LOGRENDER(KINTERRUPT_MODE)
+ENUM2STR_START(KIRQL_TYPE)
+ ENUM2STR_CASE_DEF(PASSIVE_LEVEL)
+ ENUM2STR_CASE_DEF(APC_LEVEL)
+ ENUM2STR_CASE_DEF(DISPATCH_LEVEL)
+ ENUM2STR_CASE_DEF(PROFILE_LEVEL)
+ ENUM2STR_CASE_DEF(SYNC_LEVEL)
+ ENUM2STR_CASE_DEF(HIGH_LEVEL)
+ENUM2STR_END_and_LOGRENDER(KIRQL_TYPE)
+
ENUM2STR_START(KWAIT_REASON)
ENUM2STR_CASE(Executive)
ENUM2STR_CASE(FreePage)
diff --git a/src/CxbxKrnl/EmuKrnlLogging.h b/src/CxbxKrnl/EmuKrnlLogging.h
index 37f8b39ef..12b620246 100644
--- a/src/CxbxKrnl/EmuKrnlLogging.h
+++ b/src/CxbxKrnl/EmuKrnlLogging.h
@@ -46,6 +46,7 @@ enum ALLOCATION_TYPE;
enum CREATE_DISPOSITION;
enum CREATE_OPTION;
//enum NTSTATUS;
+enum KIRQL_TYPE; // fake enum, since KIRQL is an unsigned char, which clashes with BOOLEAN
enum PROTECTION_TYPE;
};
@@ -69,6 +70,7 @@ ENUM2STR_HEADER(EXCEPTION_DISPOSITION)
ENUM2STR_HEADER(FILE_INFORMATION_CLASS)
ENUM2STR_HEADER(FS_INFORMATION_CLASS)
ENUM2STR_HEADER(KINTERRUPT_MODE)
+ENUM2STR_HEADER(KIRQL_TYPE) // Not really an enum
ENUM2STR_HEADER(KWAIT_REASON)
ENUM2STR_HEADER(KOBJECTS)
ENUM2STR_HEADER(MODE)
diff --git a/src/devices/video/EmuNV2A.cpp b/src/devices/video/EmuNV2A.cpp
index b4276b6f6..ebf6193fc 100644
--- a/src/devices/video/EmuNV2A.cpp
+++ b/src/devices/video/EmuNV2A.cpp
@@ -52,7 +52,6 @@ namespace xboxkrnl
#include // For PKINTERRUPT, etc.
};
-
#ifdef _MSC_VER // Check if MS Visual C compiler
# pragma comment(lib, "opengl32.lib") // Compiler-specific directive to avoid manually configuration
//# pragma comment(lib, "glu32.lib") // Link libraries
@@ -61,10 +60,6 @@ namespace xboxkrnl
#include // For uint32_t
#include // For std::string
-#include
-#include
-#include
-#include
#include "CxbxKrnl\CxbxKrnl.h"
#include "CxbxKrnl\Emu.h"
@@ -73,480 +68,56 @@ namespace xboxkrnl
#include "CxbxKrnl\HLEIntercept.h"
#include "EmuNV2A.h"
+#include "nv2a.h" // For NV2AState
#include "nv2a_int.h" // from https://github.com/espes/xqemu/tree/xbox/hw/xbox
-#include
+//#include
#include
#include
#include
//#include
-// Public Domain ffs Implementation
-// See: http://snipplr.com/view/22147/stringsh-implementation/
-int ffs(int v)
-{
- unsigned int x = v;
- int c = 1;
-
- /*
- * adapted from from
- * http://graphics.stanford.edu/~seander/bithacks.html#ZerosOnRightBinSearch
- *
- * a modified binary search algorithm to count 0 bits from
- * the right (lsb). This algorithm should work regardless
- * of the size of ints on the platform.
- *
- */
-
- /* a couple special cases */
- if (x == 0) return 0;
- if (x & 1) return 1; /* probably pretty common */
-
- c = 1;
- while ((x & 0xffff) == 0) {
- x >>= 16;
- c += 16;
- }
- if ((x & 0xff) == 0) {
- x >>= 8;
- c += 8;
- }
- if ((x & 0x0f) == 0) {
- x >>= 4;
- c += 4;
- }
- if ((x & 0x03) == 0) {
- x >>= 2;
- c += 2;
- }
-
- c -= (x & 1);
- c += 1; /* ffs() requires indexing bits from 1 */
- /* ie., the lsb is bit 1, not bit 0 */
- return c;
-}
-
-inline int GET_MASK(int v, int mask) {
- return (((v) & (mask)) >> (ffs(mask) - 1));
-};
-
-inline int SET_MASK(int v, int mask, int val) {
- const unsigned int __val = (val);
- const unsigned int __mask = (mask);
-
- (v) &= ~(__mask);
- return (v) |= ((__val) << (ffs(__mask)-1)) & (__mask);
-}
-
-#define CASE_4(v, step) \
- case (v): \
- case (v)+(step) : \
- case (v)+(step) * 2: \
- case (v)+(step) * 3
-
-
-enum FifoMode {
- FIFO_PIO = 0,
- FIFO_DMA = 1,
-};
-
-enum FIFOEngine {
- ENGINE_SOFTWARE = 0,
- ENGINE_GRAPHICS = 1,
- ENGINE_DVD = 2,
-};
-
-typedef struct RAMHTEntry {
- uint32_t handle;
- xbaddr instance;
- enum FIFOEngine engine;
- unsigned int channel_id : 5;
- bool valid;
-} RAMHTEntry;
-
-typedef struct DMAObject {
- unsigned int dma_class;
- unsigned int dma_target;
- xbaddr address;
- xbaddr limit;
-} DMAObject;
-
-typedef struct VertexAttribute {
- bool dma_select;
- xbaddr offset;
-
- /* inline arrays are packed in order?
- * Need to pass the offset to converted attributes */
- unsigned int inline_array_offset;
-
- float inline_value[4];
-
- unsigned int format;
- unsigned int size; /* size of the data type */
- unsigned int count; /* number of components */
- uint32_t stride;
-
- bool needs_conversion;
- uint8_t *converted_buffer;
- unsigned int converted_elements;
- unsigned int converted_size;
- unsigned int converted_count;
-
- float *inline_buffer;
-
- GLint gl_count;
- GLenum gl_type;
- GLboolean gl_normalize;
-
- GLuint gl_converted_buffer;
- GLuint gl_inline_buffer;
-} VertexAttribute;
-
-typedef struct Surface {
- bool draw_dirty;
- bool buffer_dirty;
- bool write_enabled_cache;
- unsigned int pitch;
-
- xbaddr offset;
-} Surface;
-
-typedef struct SurfaceShape {
- unsigned int z_format;
- unsigned int color_format;
- unsigned int zeta_format;
- unsigned int log_width, log_height;
- unsigned int clip_x, clip_y;
- unsigned int clip_width, clip_height;
- unsigned int anti_aliasing;
-} SurfaceShape;
-
-typedef struct TextureShape {
- bool cubemap;
- unsigned int dimensionality;
- unsigned int color_format;
- unsigned int levels;
- unsigned int width, height, depth;
-
- unsigned int min_mipmap_level, max_mipmap_level;
- unsigned int pitch;
-} TextureShape;
-
-typedef struct TextureKey {
- TextureShape state;
- uint64_t data_hash;
- uint8_t* texture_data;
- uint8_t* palette_data;
-} TextureKey;
-
-typedef struct TextureBinding {
- GLenum gl_target;
- GLuint gl_texture;
- unsigned int refcnt;
-} TextureBinding;
-
-typedef struct KelvinState {
- xbaddr dma_notifies;
- xbaddr dma_state;
- xbaddr dma_semaphore;
- unsigned int semaphore_offset;
-} KelvinState;
-
-typedef struct ContextSurfaces2DState {
- xbaddr dma_image_source;
- xbaddr dma_image_dest;
- unsigned int color_format;
- unsigned int source_pitch, dest_pitch;
- xbaddr source_offset, dest_offset;
-
-} ContextSurfaces2DState;
-
-typedef struct ImageBlitState {
- xbaddr context_surfaces;
- unsigned int operation;
- unsigned int in_x, in_y;
- unsigned int out_x, out_y;
- unsigned int width, height;
-
-} ImageBlitState;
-
-typedef struct GraphicsObject {
- uint8_t graphics_class;
- union {
- ContextSurfaces2DState context_surfaces_2d;
-
- ImageBlitState image_blit;
-
- KelvinState kelvin;
- } data;
-} GraphicsObject;
-
-typedef struct GraphicsSubchannel {
- xbaddr object_instance;
- GraphicsObject object;
- uint32_t object_cache[5];
-} GraphicsSubchannel;
-
-typedef struct GraphicsContext {
- bool channel_3d;
- unsigned int subchannel;
-} GraphicsContext;
-
-typedef struct CacheEntry {
- unsigned int method : 14;
- unsigned int subchannel : 3;
- bool nonincreasing;
- uint32_t parameter;
-} CacheEntry;
-
-typedef struct Cache1State {
- unsigned int channel_id;
- FifoMode mode;
-
- /* Pusher state */
- bool push_enabled;
- bool dma_push_enabled;
- bool dma_push_suspended;
- xbaddr dma_instance;
-
- bool method_nonincreasing;
- unsigned int method : 14;
- unsigned int subchannel : 3;
- unsigned int method_count : 24;
- uint32_t dcount;
-
- bool subroutine_active;
- xbaddr subroutine_return;
- xbaddr get_jmp_shadow;
- uint32_t rsvd_shadow;
- uint32_t data_shadow;
- uint32_t error;
-
- bool pull_enabled;
- enum FIFOEngine bound_engines[NV2A_NUM_SUBCHANNELS];
- enum FIFOEngine last_engine;
-
- /* The actual command queue */
- std::mutex mutex;
- std::condition_variable cache_cond;
- std::queue cache;
- std::queue working_cache;
-} Cache1State;
-
-typedef struct ChannelControl {
- xbaddr dma_put;
- xbaddr dma_get;
- uint32_t ref;
-} ChannelControl;
-
-struct {
- uint32_t pending_interrupts;
- uint32_t enabled_interrupts;
- uint32_t regs[NV_PMC_SIZE]; // TODO : union
-} pmc;
-
-struct {
- uint32_t pending_interrupts;
- uint32_t enabled_interrupts;
- std::thread puller_thread;
- Cache1State cache1;
- uint32_t regs[_NV_PFIFO_SIZE]; // TODO : union
-} pfifo;
-
-struct {
- uint32_t regs[NV_PVIDEO_SIZE]; // TODO : union
-} pvideo;
-
-
-struct {
- uint32_t pending_interrupts;
- uint32_t enabled_interrupts;
- uint32_t numerator;
- uint32_t denominator;
- uint32_t alarm_time;
- uint32_t regs[NV_PTIMER_SIZE]; // TODO : union
-} ptimer;
-
-struct {
- uint32_t regs[NV_PFB_SIZE]; // TODO : union
-} pfb;
-
-struct {
- uint32_t pending_interrupts;
- uint32_t enabled_interrupts;
- uint32_t start;
- uint32_t regs[NV_PCRTC_SIZE]; // TODO : union
-} pcrtc;
-
-struct {
- uint32_t core_clock_coeff;
- uint64_t core_clock_freq;
- uint32_t memory_clock_coeff;
- uint32_t video_clock_coeff;
- uint32_t regs[NV_PRAMDAC_SIZE]; // TODO : union
-} pramdac;
-
-struct {
- uint32_t regs[NV_PRAMIN_SIZE]; // TODO : union
-} pramin;
-
-struct {
- ChannelControl channel_control[NV2A_NUM_CHANNELS];
-} user;
-
-// PRMCIO (Actually the VGA controller)
-struct {
- uint8_t cr_index;
- uint8_t cr[256]; /* CRT registers */
-} prmcio;
-
-struct {
- std::mutex mutex;
-
- uint32_t pending_interrupts;
- uint32_t enabled_interrupts;
- std::condition_variable interrupt_cond;
-
- xbaddr context_table;
- xbaddr context_address;
-
-
- unsigned int trapped_method;
- unsigned int trapped_subchannel;
- unsigned int trapped_channel_id;
- uint32_t trapped_data[2];
- uint32_t notify_source;
-
- bool fifo_access;
- std::condition_variable fifo_access_cond;
- std::condition_variable flip_3d;
-
- unsigned int channel_id;
- bool channel_valid;
- GraphicsContext context[NV2A_NUM_CHANNELS];
-
- xbaddr dma_color, dma_zeta;
- Surface surface_color, surface_zeta;
- unsigned int surface_type;
- SurfaceShape surface_shape;
- SurfaceShape last_surface_shape;
-
- xbaddr dma_a, dma_b;
- //GLruCache *texture_cache;
- bool texture_dirty[NV2A_MAX_TEXTURES];
- TextureBinding *texture_binding[NV2A_MAX_TEXTURES];
-
- //GHashTable *shader_cache;
- //ShaderBinding *shader_binding;
-
- bool texture_matrix_enable[NV2A_MAX_TEXTURES];
-
- /* FIXME: Move to NV_PGRAPH_BUMPMAT... */
- float bump_env_matrix[NV2A_MAX_TEXTURES - 1][4]; /* 3 allowed stages with 2x2 matrix each */
-
- // wglContext *gl_context;
- GLuint gl_framebuffer;
- GLuint gl_color_buffer, gl_zeta_buffer;
- GraphicsSubchannel subchannel_data[NV2A_NUM_SUBCHANNELS];
-
- xbaddr dma_report;
- xbaddr report_offset;
- bool zpass_pixel_count_enable;
- unsigned int zpass_pixel_count_result;
- unsigned int gl_zpass_pixel_count_query_count;
- GLuint* gl_zpass_pixel_count_queries;
-
- xbaddr dma_vertex_a, dma_vertex_b;
-
- unsigned int primitive_mode;
-
- bool enable_vertex_program_write;
-
- //uint32_t program_data[NV2A_MAX_TRANSFORM_PROGRAM_LENGTH][VSH_TOKEN_SIZE];
-
- uint32_t vsh_constants[NV2A_VERTEXSHADER_CONSTANTS][4];
- bool vsh_constants_dirty[NV2A_VERTEXSHADER_CONSTANTS];
-
- /* lighting constant arrays */
- uint32_t ltctxa[NV2A_LTCTXA_COUNT][4];
- bool ltctxa_dirty[NV2A_LTCTXA_COUNT];
- uint32_t ltctxb[NV2A_LTCTXB_COUNT][4];
- bool ltctxb_dirty[NV2A_LTCTXB_COUNT];
- uint32_t ltc1[NV2A_LTC1_COUNT][4];
- bool ltc1_dirty[NV2A_LTC1_COUNT];
-
- // should figure out where these are in lighting context
- float light_infinite_half_vector[NV2A_MAX_LIGHTS][3];
- float light_infinite_direction[NV2A_MAX_LIGHTS][3];
- float light_local_position[NV2A_MAX_LIGHTS][3];
- float light_local_attenuation[NV2A_MAX_LIGHTS][3];
-
- //VertexAttribute vertex_attributes[NV2A_VERTEXSHADER_ATTRIBUTES];
-
- unsigned int inline_array_length;
- uint32_t inline_array[NV2A_MAX_BATCH_LENGTH];
- GLuint gl_inline_array_buffer;
-
- unsigned int inline_elements_length;
- uint32_t inline_elements[NV2A_MAX_BATCH_LENGTH];
-
- unsigned int inline_buffer_length;
-
- unsigned int draw_arrays_length;
- unsigned int draw_arrays_max_count;
-
- /* FIXME: Unknown size, possibly endless, 1000 will do for now */
- GLint gl_draw_arrays_start[1000];
- GLsizei gl_draw_arrays_count[1000];
-
- GLuint gl_element_buffer;
- GLuint gl_memory_buffer;
- GLuint gl_vertex_array;
-
- uint32_t regs[NV_PGRAPH_SIZE]; // TODO : union
-} pgraph;
-
-static void update_irq()
+static void update_irq(NV2AState *d)
{
/* PFIFO */
- if (pfifo.pending_interrupts & pfifo.enabled_interrupts) {
- pmc.pending_interrupts |= NV_PMC_INTR_0_PFIFO;
+ if (d->pfifo.pending_interrupts & d->pfifo.enabled_interrupts) {
+ d->pmc.pending_interrupts |= NV_PMC_INTR_0_PFIFO;
} else {
- pmc.pending_interrupts &= ~NV_PMC_INTR_0_PFIFO;
+ d->pmc.pending_interrupts &= ~NV_PMC_INTR_0_PFIFO;
}
/* PCRTC */
- if (pcrtc.pending_interrupts & pcrtc.enabled_interrupts) {
- pmc.pending_interrupts |= NV_PMC_INTR_0_PCRTC;
+ if (d->pcrtc.pending_interrupts & d->pcrtc.enabled_interrupts) {
+ d->pmc.pending_interrupts |= NV_PMC_INTR_0_PCRTC;
} else {
- pmc.pending_interrupts &= ~NV_PMC_INTR_0_PCRTC;
+ d->pmc.pending_interrupts &= ~NV_PMC_INTR_0_PCRTC;
}
/* PGRAPH */
- if (pgraph.pending_interrupts & pgraph.enabled_interrupts) {
- pmc.pending_interrupts |= NV_PMC_INTR_0_PGRAPH;
+ if (d->pgraph.pending_interrupts & d->pgraph.enabled_interrupts) {
+ d->pmc.pending_interrupts |= NV_PMC_INTR_0_PGRAPH;
} else {
- pmc.pending_interrupts &= ~NV_PMC_INTR_0_PGRAPH;
+ d->pmc.pending_interrupts &= ~NV_PMC_INTR_0_PGRAPH;
}
/* TODO : PBUS * /
- if (pbus.pending_interrupts & pbus.enabled_interrupts) {
- pmc.pending_interrupts |= NV_PMC_INTR_0_PBUS;
+ if (d->pbus.pending_interrupts & d->pbus.enabled_interrupts) {
+ d->pmc.pending_interrupts |= NV_PMC_INTR_0_PBUS;
}
else {
- pmc.pending_interrupts &= ~NV_PMC_INTR_0_PBUS;
+ d->pmc.pending_interrupts &= ~NV_PMC_INTR_0_PBUS;
} */
/* TODO : SOFTWARE * /
- if (user.pending_interrupts & user.enabled_interrupts) {
- pmc.pending_interrupts |= NV_PMC_INTR_0_SOFTWARE;
+ if (d->user.pending_interrupts & d->.enabled_interrupts) {
+ d->pmc.pending_interrupts |= NV_PMC_INTR_0_SOFTWARE;
}
else {
- pmc.pending_interrupts &= ~NV_PMC_INTR_0_SOFTWARE;
+ d->pmc.pending_interrupts &= ~NV_PMC_INTR_0_SOFTWARE;
} */
- if (pmc.pending_interrupts && pmc.enabled_interrupts) {
+ if (d->pmc.pending_interrupts && d->pmc.enabled_interrupts) {
HalSystemInterrupts[3].Assert(true);
} else {
HalSystemInterrupts[3].Assert(false);
@@ -556,452 +127,7 @@ static void update_irq()
}
-#define DEBUG_START(DEV) \
-const char *DebugNV_##DEV##(xbaddr addr) \
-{ \
- switch (addr) {
-#define DEBUG_CASE(a) \
- case a: return #a;
-#define DEBUG_CASE_EX(a, c) \
- case a: return #a##c;
-#define DEBUG_END(DEV) \
- default: \
- return "Unknown " #DEV " Address"; \
- } \
-}
-
-DEBUG_START(PMC)
- DEBUG_CASE(NV_PMC_BOOT_0);
- DEBUG_CASE(NV_PMC_BOOT_1);
- DEBUG_CASE(NV_PMC_INTR_0);
- DEBUG_CASE(NV_PMC_INTR_EN_0);
- DEBUG_CASE(NV_PMC_ENABLE);
-DEBUG_END(PMC)
-
-DEBUG_START(PBUS)
- DEBUG_CASE(NV_PBUS_FBIO_RAM)
- DEBUG_CASE_EX(NV_PBUS_PCI_NV_0, ":VENDOR_ID");
- DEBUG_CASE(NV_PBUS_PCI_NV_1);
- DEBUG_CASE_EX(NV_PBUS_PCI_NV_2, ":REVISION_ID");
- DEBUG_CASE_EX(NV_PBUS_PCI_NV_3, ":LATENCY_TIMER");
- DEBUG_CASE(NV_PBUS_PCI_NV_4);
- DEBUG_CASE(NV_PBUS_PCI_NV_5);
- DEBUG_CASE(NV_PBUS_PCI_NV_6);
- DEBUG_CASE(NV_PBUS_PCI_NV_7);
- DEBUG_CASE_EX(NV_PBUS_PCI_NV_11, ":SUBSYSTEM");
- DEBUG_CASE_EX(NV_PBUS_PCI_NV_12, ":ROM_BASE");
- DEBUG_CASE_EX(NV_PBUS_PCI_NV_13, ":CAP_PTR");
- DEBUG_CASE_EX(NV_PBUS_PCI_NV_14, ":RESERVED");
- DEBUG_CASE(NV_PBUS_PCI_NV_15);
- DEBUG_CASE_EX(NV_PBUS_PCI_NV_16, ":SUBSYSTEM");
- DEBUG_CASE(NV_PBUS_PCI_NV_17);
- DEBUG_CASE_EX(NV_PBUS_PCI_NV_18, ":AGP_STATUS");
- DEBUG_CASE_EX(NV_PBUS_PCI_NV_19, ":AGP_COMMAND");
- DEBUG_CASE_EX(NV_PBUS_PCI_NV_20, ":ROM_SHADOW");
- DEBUG_CASE_EX(NV_PBUS_PCI_NV_21, ":VGA");
- DEBUG_CASE_EX(NV_PBUS_PCI_NV_22, ":SCRATCH");
- DEBUG_CASE_EX(NV_PBUS_PCI_NV_23, ":DT_TIMEOUT");
- DEBUG_CASE_EX(NV_PBUS_PCI_NV_24, ":PME");
- DEBUG_CASE_EX(NV_PBUS_PCI_NV_25, ":POWER_STATE");
- DEBUG_CASE_EX(NV_PBUS_PCI_NV_26, ":RESERVED");
-DEBUG_END(PBUS)
-
-DEBUG_START(PFIFO)
- DEBUG_CASE(NV_PFIFO_DELAY_0);
- DEBUG_CASE(NV_PFIFO_DMA_TIMESLICE);
- DEBUG_CASE(NV_PFIFO_TIMESLICE);
- DEBUG_CASE(NV_PFIFO_INTR_0);
- DEBUG_CASE(NV_PFIFO_INTR_EN_0);
- DEBUG_CASE(NV_PFIFO_RAMHT);
- DEBUG_CASE(NV_PFIFO_RAMFC);
- DEBUG_CASE(NV_PFIFO_RAMRO);
- DEBUG_CASE(NV_PFIFO_RUNOUT_STATUS);
- DEBUG_CASE(NV_PFIFO_RUNOUT_PUT_ADDRESS);
- DEBUG_CASE(NV_PFIFO_RUNOUT_GET_ADDRESS);
- DEBUG_CASE(NV_PFIFO_CACHES);
- DEBUG_CASE(NV_PFIFO_MODE);
- DEBUG_CASE(NV_PFIFO_DMA);
- DEBUG_CASE(NV_PFIFO_SIZE)
- DEBUG_CASE(NV_PFIFO_CACHE0_PUSH0);
- DEBUG_CASE(NV_PFIFO_CACHE0_PULL0);
- DEBUG_CASE(NV_PFIFO_CACHE0_HASH);
- DEBUG_CASE(NV_PFIFO_CACHE1_PUSH0);
- DEBUG_CASE(NV_PFIFO_CACHE1_PUSH1);
- DEBUG_CASE(NV_PFIFO_CACHE1_PUT);
- DEBUG_CASE(NV_PFIFO_CACHE1_STATUS);
- DEBUG_CASE(NV_PFIFO_CACHE1_DMA_PUSH);
- DEBUG_CASE(NV_PFIFO_CACHE1_DMA_FETCH);
- DEBUG_CASE(NV_PFIFO_CACHE1_DMA_STATE);
- DEBUG_CASE(NV_PFIFO_CACHE1_DMA_INSTANCE);
- DEBUG_CASE(NV_PFIFO_CACHE1_DMA_CTL);
- DEBUG_CASE(NV_PFIFO_CACHE1_DMA_PUT);
- DEBUG_CASE(NV_PFIFO_CACHE1_DMA_GET);
- DEBUG_CASE(NV_PFIFO_CACHE1_REF);
- DEBUG_CASE(NV_PFIFO_CACHE1_DMA_SUBROUTINE);
- DEBUG_CASE(NV_PFIFO_CACHE1_PULL0);
- DEBUG_CASE(NV_PFIFO_CACHE1_PULL1);
- DEBUG_CASE(NV_PFIFO_CACHE1_HASH);
- DEBUG_CASE(NV_PFIFO_CACHE1_ACQUIRE_0);
- DEBUG_CASE(NV_PFIFO_CACHE1_ACQUIRE_1);
- DEBUG_CASE(NV_PFIFO_CACHE1_ACQUIRE_2);
- DEBUG_CASE(NV_PFIFO_CACHE1_SEMAPHORE);
- DEBUG_CASE(NV_PFIFO_CACHE1_GET);
- DEBUG_CASE(NV_PFIFO_CACHE1_ENGINE);
- DEBUG_CASE(NV_PFIFO_CACHE1_DMA_DCOUNT);
- DEBUG_CASE(NV_PFIFO_CACHE1_DMA_GET_JMP_SHADOW);
- DEBUG_CASE(NV_PFIFO_CACHE1_DMA_RSVD_SHADOW);
- DEBUG_CASE(NV_PFIFO_CACHE1_DMA_DATA_SHADOW);
-DEBUG_END(PFIFO)
-
-DEBUG_START(PRMA)
-DEBUG_END(PRMA)
-
-DEBUG_START(PVIDEO)
- DEBUG_CASE(NV_PVIDEO_DEBUG_2);
- DEBUG_CASE(NV_PVIDEO_DEBUG_3);
- DEBUG_CASE(NV_PVIDEO_INTR);
- DEBUG_CASE(NV_PVIDEO_INTR_EN);
- DEBUG_CASE(NV_PVIDEO_BUFFER);
- DEBUG_CASE(NV_PVIDEO_STOP);
- DEBUG_CASE(NV_PVIDEO_BASE(0));
- DEBUG_CASE(NV_PVIDEO_BASE(1));
- DEBUG_CASE(NV_PVIDEO_LIMIT(0));
- DEBUG_CASE(NV_PVIDEO_LIMIT(1));
- DEBUG_CASE(NV_PVIDEO_LUMINANCE(0));
- DEBUG_CASE(NV_PVIDEO_LUMINANCE(1));
- DEBUG_CASE(NV_PVIDEO_CHROMINANCE(0));
- DEBUG_CASE(NV_PVIDEO_CHROMINANCE(1));
- DEBUG_CASE(NV_PVIDEO_OFFSET(0));
- DEBUG_CASE(NV_PVIDEO_OFFSET(1));
- DEBUG_CASE(NV_PVIDEO_SIZE_IN(0));
- DEBUG_CASE(NV_PVIDEO_SIZE_IN(1));
- DEBUG_CASE(NV_PVIDEO_POINT_IN(0));
- DEBUG_CASE(NV_PVIDEO_POINT_IN(1));
- DEBUG_CASE(NV_PVIDEO_DS_DX(0));
- DEBUG_CASE(NV_PVIDEO_DS_DX(1));
- DEBUG_CASE(NV_PVIDEO_DT_DY(0));
- DEBUG_CASE(NV_PVIDEO_DT_DY(1));
- DEBUG_CASE(NV_PVIDEO_POINT_OUT(0));
- DEBUG_CASE(NV_PVIDEO_POINT_OUT(1));
- DEBUG_CASE(NV_PVIDEO_SIZE_OUT(0));
- DEBUG_CASE(NV_PVIDEO_SIZE_OUT(1));
- DEBUG_CASE(NV_PVIDEO_FORMAT(0));
- DEBUG_CASE(NV_PVIDEO_FORMAT(1));
-DEBUG_END(PVIDEO)
-
-DEBUG_START(PTIMER)
- DEBUG_CASE(NV_PTIMER_INTR_0);
- DEBUG_CASE(NV_PTIMER_INTR_EN_0);
- DEBUG_CASE(NV_PTIMER_NUMERATOR);
- DEBUG_CASE(NV_PTIMER_DENOMINATOR);
- DEBUG_CASE(NV_PTIMER_TIME_0);
- DEBUG_CASE(NV_PTIMER_TIME_1);
- DEBUG_CASE(NV_PTIMER_ALARM_0);
-DEBUG_END(PTIMER)
-
-DEBUG_START(PCOUNTER)
-DEBUG_END(PCOUNTER)
-
-DEBUG_START(PVPE)
-DEBUG_END(PVPE)
-
-DEBUG_START(PTV)
-DEBUG_END(PTV)
-
-DEBUG_START(PRMFB)
-DEBUG_END(PRMFB)
-
-DEBUG_START(PRMVIO)
-DEBUG_END(PRMVIO)
-
-DEBUG_START(PFB)
- DEBUG_CASE(NV_PFB_CFG0)
- DEBUG_CASE(NV_PFB_CFG1)
- DEBUG_CASE(NV_PFB_CSTATUS)
- DEBUG_CASE(NV_PFB_REFCTRL)
- DEBUG_CASE(NV_PFB_NVM) // NV_PFB_NVM_MODE_DISABLE
- DEBUG_CASE(NV_PFB_PIN)
- DEBUG_CASE(NV_PFB_PAD)
- DEBUG_CASE(NV_PFB_TIMING0)
- DEBUG_CASE(NV_PFB_TIMING1)
- DEBUG_CASE(NV_PFB_TIMING2)
- DEBUG_CASE(NV_PFB_TILE(0))
- DEBUG_CASE(NV_PFB_TLIMIT(0))
- DEBUG_CASE(NV_PFB_TSIZE(0))
- DEBUG_CASE(NV_PFB_TSTATUS(0))
- DEBUG_CASE(NV_PFB_TILE(1))
- DEBUG_CASE(NV_PFB_TLIMIT(1))
- DEBUG_CASE(NV_PFB_TSIZE(1))
- DEBUG_CASE(NV_PFB_TSTATUS(1))
- DEBUG_CASE(NV_PFB_TILE(2))
- DEBUG_CASE(NV_PFB_TLIMIT(2))
- DEBUG_CASE(NV_PFB_TSIZE(2))
- DEBUG_CASE(NV_PFB_TSTATUS(2))
- DEBUG_CASE(NV_PFB_TILE(3))
- DEBUG_CASE(NV_PFB_TLIMIT(3))
- DEBUG_CASE(NV_PFB_TSIZE(3))
- DEBUG_CASE(NV_PFB_TSTATUS(3))
- DEBUG_CASE(NV_PFB_TILE(4))
- DEBUG_CASE(NV_PFB_TLIMIT(4))
- DEBUG_CASE(NV_PFB_TSIZE(4))
- DEBUG_CASE(NV_PFB_TSTATUS(4))
- DEBUG_CASE(NV_PFB_TILE(5))
- DEBUG_CASE(NV_PFB_TLIMIT(5))
- DEBUG_CASE(NV_PFB_TSIZE(5))
- DEBUG_CASE(NV_PFB_TSTATUS(5))
- DEBUG_CASE(NV_PFB_TILE(6))
- DEBUG_CASE(NV_PFB_TLIMIT(6))
- DEBUG_CASE(NV_PFB_TSIZE(6))
- DEBUG_CASE(NV_PFB_TSTATUS(6))
- DEBUG_CASE(NV_PFB_TILE(7))
- DEBUG_CASE(NV_PFB_TLIMIT(7))
- DEBUG_CASE(NV_PFB_TSIZE(7))
- DEBUG_CASE(NV_PFB_TSTATUS(7))
- DEBUG_CASE(NV_PFB_MRS)
- DEBUG_CASE(NV_PFB_EMRS)
- DEBUG_CASE(NV_PFB_MRS_EXT)
- DEBUG_CASE(NV_PFB_EMRS_EXT)
- DEBUG_CASE(NV_PFB_REF)
- DEBUG_CASE(NV_PFB_PRE)
- DEBUG_CASE(NV_PFB_ZCOMP(0))
- DEBUG_CASE(NV_PFB_ZCOMP(1))
- DEBUG_CASE(NV_PFB_ZCOMP(2))
- DEBUG_CASE(NV_PFB_ZCOMP(3))
- DEBUG_CASE(NV_PFB_ZCOMP(4))
- DEBUG_CASE(NV_PFB_ZCOMP(5))
- DEBUG_CASE(NV_PFB_ZCOMP(6))
- DEBUG_CASE(NV_PFB_ZCOMP(7))
- DEBUG_CASE(NV_PFB_ZCOMP_OFFSET)
- DEBUG_CASE(NV_PFB_ARB_PREDIVIDER)
- DEBUG_CASE(NV_PFB_ARB_TIMEOUT)
- DEBUG_CASE(NV_PFB_ARB_XFER_REM)
- DEBUG_CASE(NV_PFB_ARB_DIFF_BANK)
- DEBUG_CASE(NV_PFB_CLOSE_PAGE0)
- DEBUG_CASE(NV_PFB_CLOSE_PAGE1)
- DEBUG_CASE(NV_PFB_CLOSE_PAGE2)
- DEBUG_CASE(NV_PFB_BPARB)
- DEBUG_CASE(NV_PFB_CMDQ0)
- DEBUG_CASE(NV_PFB_CMDQ1)
- DEBUG_CASE(NV_PFB_ILL_INSTR)
- DEBUG_CASE(NV_PFB_RT)
- DEBUG_CASE(NV_PFB_AUTOCLOSE)
- DEBUG_CASE(NV_PFB_WBC)
- DEBUG_CASE(NV_PFB_CMDQ_PRT)
- DEBUG_CASE(NV_PFB_CPU_RRQ)
- DEBUG_CASE(NV_PFB_BYPASS);
-DEBUG_END(PFB)
-
-DEBUG_START(PSTRAPS)
-DEBUG_END(PSTRAPS)
-
-DEBUG_START(PGRAPH)
- DEBUG_CASE(NV_PGRAPH_DEBUG_0);
- DEBUG_CASE(NV_PGRAPH_DEBUG_1);
- DEBUG_CASE(NV_PGRAPH_DEBUG_3);
- DEBUG_CASE(NV_PGRAPH_DEBUG_4);
- DEBUG_CASE(NV_PGRAPH_DEBUG_5);
- DEBUG_CASE(NV_PGRAPH_DEBUG_8);
- DEBUG_CASE(NV_PGRAPH_DEBUG_9);
- DEBUG_CASE(NV_PGRAPH_INTR);
- DEBUG_CASE(NV_PGRAPH_NSOURCE);
- DEBUG_CASE(NV_PGRAPH_INTR_EN);
- DEBUG_CASE(NV_PGRAPH_CTX_CONTROL);
- DEBUG_CASE(NV_PGRAPH_CTX_USER);
- DEBUG_CASE(NV_PGRAPH_CTX_SWITCH1);
- DEBUG_CASE(NV_PGRAPH_CTX_SWITCH2);
- DEBUG_CASE(NV_PGRAPH_CTX_SWITCH3);
- DEBUG_CASE(NV_PGRAPH_CTX_SWITCH4);
- DEBUG_CASE(NV_PGRAPH_STATUS);
- DEBUG_CASE(NV_PGRAPH_TRAPPED_ADDR);
- DEBUG_CASE(NV_PGRAPH_TRAPPED_DATA_LOW);
- DEBUG_CASE(NV_PGRAPH_SURFACE);
- DEBUG_CASE(NV_PGRAPH_INCREMENT);
- DEBUG_CASE(NV_PGRAPH_FIFO);
- DEBUG_CASE(NV_PGRAPH_RDI_INDEX);
- DEBUG_CASE(NV_PGRAPH_RDI_DATA);
- DEBUG_CASE(NV_PGRAPH_FFINTFC_ST2);
- DEBUG_CASE(NV_PGRAPH_CHANNEL_CTX_TABLE);
- DEBUG_CASE(NV_PGRAPH_CHANNEL_CTX_POINTER);
- DEBUG_CASE(NV_PGRAPH_CHANNEL_CTX_TRIGGER);
- DEBUG_CASE(NV_PGRAPH_DEBUG_2);
- DEBUG_CASE(NV_PGRAPH_TTILE(0));
- DEBUG_CASE(NV_PGRAPH_TLIMIT(0));
- DEBUG_CASE(NV_PGRAPH_TSIZE(0));
- DEBUG_CASE(NV_PGRAPH_TSTATUS(0));
- DEBUG_CASE(NV_PGRAPH_TTILE(1));
- DEBUG_CASE(NV_PGRAPH_TLIMIT(1));
- DEBUG_CASE(NV_PGRAPH_TSIZE(1));
- DEBUG_CASE(NV_PGRAPH_TSTATUS(1));
- DEBUG_CASE(NV_PGRAPH_TTILE(2));
- DEBUG_CASE(NV_PGRAPH_TLIMIT(2));
- DEBUG_CASE(NV_PGRAPH_TSIZE(2));
- DEBUG_CASE(NV_PGRAPH_TSTATUS(2));
- DEBUG_CASE(NV_PGRAPH_TTILE(3));
- DEBUG_CASE(NV_PGRAPH_TLIMIT(3));
- DEBUG_CASE(NV_PGRAPH_TSIZE(3));
- DEBUG_CASE(NV_PGRAPH_TSTATUS(3));
- DEBUG_CASE(NV_PGRAPH_TTILE(4));
- DEBUG_CASE(NV_PGRAPH_TLIMIT(4));
- DEBUG_CASE(NV_PGRAPH_TSIZE(4));
- DEBUG_CASE(NV_PGRAPH_TSTATUS(4));
- DEBUG_CASE(NV_PGRAPH_TTILE(5));
- DEBUG_CASE(NV_PGRAPH_TLIMIT(5));
- DEBUG_CASE(NV_PGRAPH_TSIZE(5));
- DEBUG_CASE(NV_PGRAPH_TSTATUS(5));
- DEBUG_CASE(NV_PGRAPH_TTILE(6));
- DEBUG_CASE(NV_PGRAPH_TLIMIT(6));
- DEBUG_CASE(NV_PGRAPH_TSIZE(6));
- DEBUG_CASE(NV_PGRAPH_TSTATUS(6));
- DEBUG_CASE(NV_PGRAPH_TTILE(7));
- DEBUG_CASE(NV_PGRAPH_TLIMIT(7));
- DEBUG_CASE(NV_PGRAPH_TSIZE(7));
- DEBUG_CASE(NV_PGRAPH_TSTATUS(7));
- DEBUG_CASE(NV_PGRAPH_ZCOMP(0));
- DEBUG_CASE(NV_PGRAPH_ZCOMP(1));
- DEBUG_CASE(NV_PGRAPH_ZCOMP(2));
- DEBUG_CASE(NV_PGRAPH_ZCOMP(3));
- DEBUG_CASE(NV_PGRAPH_ZCOMP(4));
- DEBUG_CASE(NV_PGRAPH_ZCOMP(5));
- DEBUG_CASE(NV_PGRAPH_ZCOMP(6));
- DEBUG_CASE(NV_PGRAPH_ZCOMP(7));
- DEBUG_CASE(NV_PGRAPH_ZCOMP_OFFSET);
- DEBUG_CASE(NV_PGRAPH_FBCFG0);
- DEBUG_CASE(NV_PGRAPH_FBCFG1);
- DEBUG_CASE(NV_PGRAPH_DEBUG_6);
- DEBUG_CASE(NV_PGRAPH_DEBUG_7);
- DEBUG_CASE(NV_PGRAPH_DEBUG_10);
- DEBUG_CASE(NV_PGRAPH_CSV0_D);
- DEBUG_CASE(NV_PGRAPH_CSV0_C);
- DEBUG_CASE(NV_PGRAPH_CSV1_B);
- DEBUG_CASE(NV_PGRAPH_CSV1_A);
- DEBUG_CASE(NV_PGRAPH_CHEOPS_OFFSET);
- DEBUG_CASE(NV_PGRAPH_BLEND);
- DEBUG_CASE(NV_PGRAPH_BLENDCOLOR);
- DEBUG_CASE(NV_PGRAPH_BORDERCOLOR0);
- DEBUG_CASE(NV_PGRAPH_BORDERCOLOR1);
- DEBUG_CASE(NV_PGRAPH_BORDERCOLOR2);
- DEBUG_CASE(NV_PGRAPH_BORDERCOLOR3);
- DEBUG_CASE(NV_PGRAPH_BUMPOFFSET1);
- DEBUG_CASE(NV_PGRAPH_BUMPSCALE1);
- DEBUG_CASE(NV_PGRAPH_CLEARRECTX);
- DEBUG_CASE(NV_PGRAPH_CLEARRECTY);
- DEBUG_CASE(NV_PGRAPH_COLORCLEARVALUE);
- DEBUG_CASE(NV_PGRAPH_COMBINEFACTOR0);
- DEBUG_CASE(NV_PGRAPH_COMBINEFACTOR1);
- DEBUG_CASE(NV_PGRAPH_COMBINEALPHAI0);
- DEBUG_CASE(NV_PGRAPH_COMBINEALPHAO0);
- DEBUG_CASE(NV_PGRAPH_COMBINECOLORI0);
- DEBUG_CASE(NV_PGRAPH_COMBINECOLORO0);
- DEBUG_CASE(NV_PGRAPH_COMBINECTL);
- DEBUG_CASE(NV_PGRAPH_COMBINESPECFOG0);
- DEBUG_CASE(NV_PGRAPH_COMBINESPECFOG1);
- DEBUG_CASE(NV_PGRAPH_CONTROL_0);
- DEBUG_CASE(NV_PGRAPH_CONTROL_2);
- DEBUG_CASE(NV_PGRAPH_CONTROL_3);
- DEBUG_CASE(NV_PGRAPH_FOGCOLOR);
- DEBUG_CASE(NV_PGRAPH_FOGPARAM0);
- DEBUG_CASE(NV_PGRAPH_FOGPARAM1);
- DEBUG_CASE(NV_PGRAPH_SETUPRASTER);
- DEBUG_CASE(NV_PGRAPH_SHADERCLIPMODE);
- DEBUG_CASE(NV_PGRAPH_SHADERCTL);
- DEBUG_CASE(NV_PGRAPH_SHADERPROG);
- DEBUG_CASE(NV_PGRAPH_SHADOWZSLOPETHRESHOLD);
- DEBUG_CASE(NV_PGRAPH_SPECFOGFACTOR0);
- DEBUG_CASE(NV_PGRAPH_SPECFOGFACTOR1);
- DEBUG_CASE_EX(NV_PGRAPH_TEXADDRESS0, ":_ADDRV");
- DEBUG_CASE(NV_PGRAPH_TEXADDRESS1);
- DEBUG_CASE(NV_PGRAPH_TEXADDRESS2);
- DEBUG_CASE(NV_PGRAPH_TEXADDRESS3);
- DEBUG_CASE(NV_PGRAPH_TEXCTL0_0);
- DEBUG_CASE(NV_PGRAPH_TEXCTL0_1);
- DEBUG_CASE(NV_PGRAPH_TEXCTL0_2);
- DEBUG_CASE(NV_PGRAPH_TEXCTL0_3);
- DEBUG_CASE(NV_PGRAPH_TEXCTL1_0);
- DEBUG_CASE(NV_PGRAPH_TEXCTL1_1);
- DEBUG_CASE(NV_PGRAPH_TEXCTL1_2);
- DEBUG_CASE(NV_PGRAPH_TEXCTL1_3);
- DEBUG_CASE(NV_PGRAPH_TEXCTL2_0);
- DEBUG_CASE(NV_PGRAPH_TEXCTL2_1);
- DEBUG_CASE(NV_PGRAPH_TEXFILTER0);
- DEBUG_CASE(NV_PGRAPH_TEXFILTER1);
- DEBUG_CASE(NV_PGRAPH_TEXFILTER2);
- DEBUG_CASE(NV_PGRAPH_TEXFILTER3);
- DEBUG_CASE(NV_PGRAPH_TEXFMT0);
- DEBUG_CASE(NV_PGRAPH_TEXFMT1);
- DEBUG_CASE(NV_PGRAPH_TEXFMT2);
- DEBUG_CASE(NV_PGRAPH_TEXFMT3);
- DEBUG_CASE(NV_PGRAPH_TEXIMAGERECT0);
- DEBUG_CASE(NV_PGRAPH_TEXIMAGERECT1);
- DEBUG_CASE(NV_PGRAPH_TEXIMAGERECT2);
- DEBUG_CASE(NV_PGRAPH_TEXIMAGERECT3);
- DEBUG_CASE(NV_PGRAPH_TEXOFFSET0);
- DEBUG_CASE(NV_PGRAPH_TEXOFFSET1);
- DEBUG_CASE(NV_PGRAPH_TEXOFFSET2);
- DEBUG_CASE(NV_PGRAPH_TEXOFFSET3);
- DEBUG_CASE(NV_PGRAPH_TEXPALETTE0);
- DEBUG_CASE(NV_PGRAPH_TEXPALETTE1);
- DEBUG_CASE(NV_PGRAPH_TEXPALETTE2);
- DEBUG_CASE(NV_PGRAPH_TEXPALETTE3);
- DEBUG_CASE(NV_PGRAPH_ZSTENCILCLEARVALUE);
- DEBUG_CASE(NV_PGRAPH_ZCLIPMIN);
- DEBUG_CASE(NV_PGRAPH_ZOFFSETBIAS);
- DEBUG_CASE(NV_PGRAPH_ZOFFSETFACTOR);
- DEBUG_CASE(NV_PGRAPH_EYEVEC0);
- DEBUG_CASE(NV_PGRAPH_EYEVEC1);
- DEBUG_CASE(NV_PGRAPH_EYEVEC2);
- DEBUG_CASE(NV_PGRAPH_ZCLIPMAX);
-DEBUG_END(PGRAPH)
-
-DEBUG_START(PCRTC)
- DEBUG_CASE(NV_PCRTC_INTR_0);
- DEBUG_CASE(NV_PCRTC_INTR_EN_0);
- DEBUG_CASE(NV_PCRTC_START);
- DEBUG_CASE(NV_PCRTC_CONFIG);
-DEBUG_END(PCRTC)
-
-DEBUG_START(PRMCIO)
- DEBUG_CASE(VGA_CRT_DC);
- DEBUG_CASE(VGA_CRT_DM);
- DEBUG_CASE(VGA_ATT_R);
- DEBUG_CASE(VGA_ATT_W);
- DEBUG_CASE(VGA_GFX_D);
- DEBUG_CASE(VGA_SEQ_D);
- DEBUG_CASE(VGA_MIS_R);
- DEBUG_CASE(VGA_MIS_W);
- DEBUG_CASE(VGA_FTC_R);
- DEBUG_CASE(VGA_IS1_RC);
- DEBUG_CASE(VGA_IS1_RM);
- DEBUG_CASE(VGA_PEL_D);
- DEBUG_CASE(VGA_PEL_MSK);
- DEBUG_CASE(VGA_CRT_IC);
- DEBUG_CASE(VGA_CRT_IM);
- DEBUG_CASE(VGA_GFX_I);
- DEBUG_CASE(VGA_SEQ_I);
- DEBUG_CASE(VGA_PEL_IW);
- DEBUG_CASE(VGA_PEL_IR);
-DEBUG_END(PRMCIO)
-
-DEBUG_START(PRAMDAC)
- DEBUG_CASE(NV_PRAMDAC_NVPLL_COEFF);
- DEBUG_CASE(NV_PRAMDAC_MPLL_COEFF);
- DEBUG_CASE(NV_PRAMDAC_VPLL_COEFF);
- DEBUG_CASE(NV_PRAMDAC_PLL_TEST_COUNTER);
-
-DEBUG_END(PRAMDAC)
-
-DEBUG_START(PRMDIO)
-DEBUG_END(PRMDIO)
-
-DEBUG_START(PRAMIN)
-DEBUG_END(PRAMIN)
-
-DEBUG_START(USER)
- DEBUG_CASE(NV_USER_DMA_PUT);
- DEBUG_CASE(NV_USER_DMA_GET);
- DEBUG_CASE(NV_USER_REF);
-DEBUG_END(USER)
-
-
+#include "EmuNV2A_DEBUG.cpp"
#define DEBUG_READ32(DEV) DbgPrintf("X86 : Rd32 NV2A " #DEV "(0x%08X) = 0x%08X [Handled %s]\n", addr, result, DebugNV_##DEV##(addr))
@@ -1010,13 +136,13 @@ DEBUG_END(USER)
#define DEBUG_WRITE32(DEV) DbgPrintf("X86 : Wr32 NV2A " #DEV "(0x%08X, 0x%08X) [Handled %s]\n", addr, value, DebugNV_##DEV##(addr))
#define DEBUG_WRITE32_UNHANDLED(DEV) { DbgPrintf("X86 : Wr32 NV2A " #DEV "(0x%08X, 0x%08X) [Unhandled %s]\n", addr, value, DebugNV_##DEV##(addr)); return; }
-#define DEVICE_READ32(DEV) uint32_t EmuNV2A_##DEV##_Read32(xbaddr addr)
+#define DEVICE_READ32(DEV) uint32_t EmuNV2A_##DEV##_Read32(NV2AState *d, xbaddr addr)
#define DEVICE_READ32_SWITCH() uint32_t result = 0; switch (addr)
-#define DEVICE_READ32_REG(dev) result = dev.regs[addr]
+#define DEVICE_READ32_REG(dev) result = d->dev.regs[addr]
#define DEVICE_READ32_END(DEV) DEBUG_READ32(DEV); return result
-#define DEVICE_WRITE32(DEV) void EmuNV2A_##DEV##_Write32(xbaddr addr, uint32_t value)
-#define DEVICE_WRITE32_REG(dev) dev.regs[addr] = value
+#define DEVICE_WRITE32(DEV) void EmuNV2A_##DEV##_Write32(NV2AState *d, xbaddr addr, uint32_t value)
+#define DEVICE_WRITE32_REG(dev) d->dev.regs[addr] = value
#define DEVICE_WRITE32_END(DEV) DEBUG_WRITE32(DEV)
static inline uint32_t ldl_le_p(const void *p)
@@ -1029,11 +155,11 @@ static inline void stl_le_p(uint32_t *p, uint32 v)
*p = v;
}
-static DMAObject nv_dma_load(xbaddr dma_obj_address)
+static DMAObject nv_dma_load(NV2AState *d, xbaddr dma_obj_address)
{
- assert(dma_obj_address < NV_PRAMIN_SIZE);
+ assert(dma_obj_address < d->pramin.ramin_size);
- uint32_t *dma_obj = (uint32_t*)(NV2A_ADDR + NV_PRAMIN_ADDR + dma_obj_address);
+ uint32_t *dma_obj = (uint32_t*)(d->pramin.ramin_ptr + dma_obj_address);
uint32_t flags = ldl_le_p(dma_obj);
uint32_t limit = ldl_le_p(dma_obj + 1);
uint32_t frame = ldl_le_p(dma_obj + 2);
@@ -1047,14 +173,14 @@ static DMAObject nv_dma_load(xbaddr dma_obj_address)
return object;
}
-static void *nv_dma_map(xbaddr dma_obj_address, xbaddr *len)
+static void *nv_dma_map(NV2AState *d, xbaddr dma_obj_address, xbaddr *len)
{
- assert(dma_obj_address < NV_PRAMIN_SIZE);
+ assert(dma_obj_address < d->pramin.ramin_size);
- DMAObject dma = nv_dma_load(dma_obj_address);
+ DMAObject dma = nv_dma_load(d, dma_obj_address);
/* TODO: Handle targets and classes properly */
- printf("dma_map %x, %x, %x %x\n",
+ NV2A_DPRINTF("dma_map %x, %x, %x %x\n",
dma.dma_class, dma.dma_target, dma.address, dma.limit);
dma.address &= 0x07FFFFFF;
@@ -1064,2478 +190,82 @@ static void *nv_dma_map(xbaddr dma_obj_address, xbaddr *len)
return (void*)(MM_SYSTEM_PHYSICAL_MAP + dma.address);
}
-/* pusher should be fine to run from a mimo handler
-* whenever's it's convenient */
-static void pfifo_run_pusher() {
- uint8_t channel_id;
- ChannelControl *control;
- Cache1State *state;
- CacheEntry *command;
- uint8_t *dma;
- xbaddr dma_len;
- uint32_t word;
-
- /* TODO: How is cache1 selected? */
- state = &pfifo.cache1;
- channel_id = state->channel_id;
- control = &user.channel_control[channel_id];
-
- if (!state->push_enabled) return;
-
- /* only handling DMA for now... */
-
- /* Channel running DMA */
- uint32_t channel_modes = pfifo.regs[NV_PFIFO_MODE];
- assert(channel_modes & (1 << channel_id));
- assert(state->mode == FIFO_DMA);
-
- if (!state->dma_push_enabled) return;
- if (state->dma_push_suspended) return;
-
- /* We're running so there should be no pending errors... */
- assert(state->error == NV_PFIFO_CACHE1_DMA_STATE_ERROR_NONE);
-
- dma = (uint8_t*)nv_dma_map(state->dma_instance, &dma_len);
-
- printf("DMA pusher: max 0x%08X, 0x%08X - 0x%08X\n",
- dma_len, control->dma_get, control->dma_put);
-
- /* based on the convenient pseudocode in envytools */
- while (control->dma_get != control->dma_put) {
- if (control->dma_get >= dma_len) {
- state->error = NV_PFIFO_CACHE1_DMA_STATE_ERROR_PROTECTION;
- break;
- }
-
- word = ldl_le_p((uint32_t*)(dma + control->dma_get));
- control->dma_get += 4;
-
- if (state->method_count) {
- /* data word of methods command */
- state->data_shadow = word;
-
- CacheEntry* command = (CacheEntry*)calloc(1, sizeof(CacheEntry));
- command->method = state->method;
- command->subchannel = state->subchannel;
- command->nonincreasing = state->method_nonincreasing;
- command->parameter = word;
-
- std::lock_guard lk(state->mutex);
- state->cache.push(command);
- state->cache_cond.notify_all();
-
- if (!state->method_nonincreasing) {
- state->method += 4;
- }
-
- state->method_count--;
- state->dcount++;
- } else {
- /* no command active - this is the first word of a new one */
- state->rsvd_shadow = word;
- /* match all forms */
- if ((word & 0xe0000003) == 0x20000000) {
- /* old jump */
- state->get_jmp_shadow = control->dma_get;
- control->dma_get = word & 0x1fffffff;
- printf("pb OLD_JMP 0x%08X\n", control->dma_get);
- }
- else if ((word & 3) == 1) {
- /* jump */
- state->get_jmp_shadow = control->dma_get;
- control->dma_get = word & 0xfffffffc;
- printf("pb JMP 0x%08X\n", control->dma_get);
- }
- else if ((word & 3) == 2) {
- /* call */
- if (state->subroutine_active) {
- state->error = NV_PFIFO_CACHE1_DMA_STATE_ERROR_CALL;
- break;
- }
- state->subroutine_return = control->dma_get;
- state->subroutine_active = true;
- control->dma_get = word & 0xfffffffc;
- printf("pb CALL 0x%08X\n", control->dma_get);
- }
- else if (word == 0x00020000) {
- /* return */
- if (!state->subroutine_active) {
- state->error = NV_PFIFO_CACHE1_DMA_STATE_ERROR_RETURN;
- break;
- }
- control->dma_get = state->subroutine_return;
- state->subroutine_active = false;
- printf("pb RET 0x%08X\n", control->dma_get);
- }
- else if ((word & 0xe0030003) == 0) {
- /* increasing methods */
- state->method = word & 0x1fff;
- state->subchannel = (word >> 13) & 7;
- state->method_count = (word >> 18) & 0x7ff;
- state->method_nonincreasing = false;
- state->dcount = 0;
- }
- else if ((word & 0xe0030003) == 0x40000000) {
- /* non-increasing methods */
- state->method = word & 0x1fff;
- state->subchannel = (word >> 13) & 7;
- state->method_count = (word >> 18) & 0x7ff;
- state->method_nonincreasing = true;
- state->dcount = 0;
- }
- else {
- printf("pb reserved cmd 0x%08X - 0x%08X\n",
- control->dma_get, word);
- state->error = NV_PFIFO_CACHE1_DMA_STATE_ERROR_RESERVED_CMD;
- break;
- }
- }
- }
-
- printf("DMA pusher done: max 0x%08X, 0x%08X - 0x%08X\n",
- dma_len, control->dma_get, control->dma_put);
-
- if (state->error) {
- printf("pb error: %d\n", state->error);
- assert(false);
-
- state->dma_push_suspended = true;
-
- pfifo.pending_interrupts |= NV_PFIFO_INTR_0_DMA_PUSHER;
- update_irq();
- }
-}
-
-static uint32_t ramht_hash(uint32_t handle)
-{
- unsigned int ramht_size = 1 << (GET_MASK(pfifo.regs[NV_PFIFO_RAMHT], NV_PFIFO_RAMHT_SIZE_MASK) + 12);
-
- /* XXX: Think this is different to what nouveau calculates... */
- unsigned int bits = ffs(ramht_size) - 2;
-
- uint32_t hash = 0;
- while (handle) {
- hash ^= (handle & ((1 << bits) - 1));
- handle >>= bits;
- }
- hash ^= pfifo.cache1.channel_id << (bits - 4);
-
- return hash;
-}
-
-
-static RAMHTEntry ramht_lookup(uint32_t handle)
-{
- unsigned int ramht_size = 1 << (GET_MASK(pfifo.regs[NV_PFIFO_RAMHT], NV_PFIFO_RAMHT_SIZE_MASK) + 12);
-
- uint32_t hash = ramht_hash(handle);
- assert(hash * 8 < ramht_size);
-
- uint32_t ramht_address =
- GET_MASK(pfifo.regs[NV_PFIFO_RAMHT],
- NV_PFIFO_RAMHT_BASE_ADDRESS_MASK) << 12;
-
- uint8_t *entry_ptr = (uint8_t*)(NV2A_ADDR + NV_PRAMIN_ADDR + ramht_address + hash * 8);
-
- uint32_t entry_handle = ldl_le_p((uint32_t*)entry_ptr);
- uint32_t entry_context = ldl_le_p((uint32_t*)(entry_ptr + 4));
-
- RAMHTEntry entry;
- entry.handle = entry_handle;
- entry.instance = (entry_context & NV_RAMHT_INSTANCE) << 4;
- entry.engine = (FIFOEngine)((entry_context & NV_RAMHT_ENGINE) >> 16);
- entry.channel_id = (entry_context & NV_RAMHT_CHID) >> 24;
- entry.valid = entry_context & NV_RAMHT_STATUS;
-
- return entry;
-}
-
-static void pgraph_context_switch(unsigned int channel_id)
-{
- bool valid = false;
-
- // Scope the lock so that it gets unlocked at end of this block
- {
- std::lock_guard lk(pgraph.mutex);
-
- valid = pgraph.channel_valid && pgraph.channel_id == channel_id;
- if (!valid) {
- pgraph.trapped_channel_id = channel_id;
- }
- }
-
- if (!valid) {
- printf("puller needs to switch to ch %d\n", channel_id);
-
- //qemu_mutex_lock_iothread();
- pgraph.pending_interrupts |= NV_PGRAPH_INTR_CONTEXT_SWITCH;
- update_irq();
-
- std::unique_lock lk(pgraph.mutex);
- //qemu_mutex_unlock_iothread();
-
- while (pgraph.pending_interrupts & NV_PGRAPH_INTR_CONTEXT_SWITCH) {
- pgraph.interrupt_cond.wait(lk);
- }
- }
-}
-
-static void pgraph_wait_fifo_access() {
- std::unique_lock lk(pgraph.mutex);
-
- while (!pgraph.fifo_access) {
- pgraph.fifo_access_cond.wait(lk);
- }
-}
-
-static void pgraph_method_log(unsigned int subchannel, unsigned int graphics_class, unsigned int method, uint32_t parameter) {
- static unsigned int last = 0;
- static unsigned int count = 0;
-
- if (last == 0x1800 && method != last) {
- printf("pgraph method (%d) 0x%08X * %d", subchannel, last, count);
- }
- if (method != 0x1800) {
- const char* method_name = NULL;
- unsigned int nmethod = 0;
- switch (graphics_class) {
- case NV_KELVIN_PRIMITIVE:
- nmethod = method | (0x5c << 16);
- break;
- case NV_CONTEXT_SURFACES_2D:
- nmethod = method | (0x6d << 16);
- break;
- default:
- break;
- }
- /*
- if (nmethod != 0 && nmethod < ARRAY_SIZE(nv2a_method_names)) {
- method_name = nv2a_method_names[nmethod];
- }
- if (method_name) {
- printf("pgraph method (%d): %s (0x%x)\n",
- subchannel, method_name, parameter);
- }
- else {
- */
- printf("pgraph method (%d): 0x%x -> 0x%04x (0x%x)\n",
- subchannel, graphics_class, method, parameter);
- //}
-
- }
- if (method == last) { count++; }
- else { count = 0; }
- last = method;
-}
-
-static void load_graphics_object(xbaddr instance_address, GraphicsObject *obj)
-{
- uint8_t *obj_ptr;
- uint32_t switch1, switch2, switch3;
-
- assert(instance_address < NV_PRAMIN_SIZE);
- obj_ptr = (uint8_t*)(NV2A_ADDR + NV_PRAMIN_ADDR + instance_address);
-
- switch1 = ldl_le_p((uint32_t*)obj_ptr);
- switch2 = ldl_le_p((uint32_t*)(obj_ptr + 4));
- switch3 = ldl_le_p((uint32_t*)(obj_ptr + 8));
-
- obj->graphics_class = switch1 & NV_PGRAPH_CTX_SWITCH1_GRCLASS;
-
- /* init graphics object */
- switch (obj->graphics_class) {
- case NV_KELVIN_PRIMITIVE:
- // kelvin->vertex_attributes[NV2A_VERTEX_ATTR_DIFFUSE].inline_value = 0xFFFFFFF;
- break;
- default:
- break;
- }
-}
-
-static GraphicsObject* lookup_graphics_object(xbaddr instance_address)
-{
- int i;
- for (i = 0; i lk(pgraph.mutex);
-
-// int i;
- GraphicsSubchannel *subchannel_data;
- GraphicsObject *object;
-
- unsigned int slot;
-
- assert(pgraph.channel_valid);
- subchannel_data = &pgraph.subchannel_data[subchannel];
- object = &subchannel_data->object;
-
- ContextSurfaces2DState *context_surfaces_2d = &object->data.context_surfaces_2d;
- ImageBlitState *image_blit = &object->data.image_blit;
- KelvinState *kelvin = &object->data.kelvin;
-
- pgraph_method_log(subchannel, object->graphics_class, method, parameter);
-
- if (method == NV_SET_OBJECT) {
- subchannel_data->object_instance = parameter;
-
- //qemu_mutex_lock_iothread();
- load_graphics_object(parameter, object);
- //qemu_mutex_unlock_iothread();
- return;
- }
-
- switch (object->graphics_class) {
- case NV_CONTEXT_SURFACES_2D: {
- switch (method) {
- case NV062_SET_CONTEXT_DMA_IMAGE_SOURCE:
- context_surfaces_2d->dma_image_source = parameter;
- break;
- case NV062_SET_CONTEXT_DMA_IMAGE_DESTIN:
- context_surfaces_2d->dma_image_dest = parameter;
- break;
- case NV062_SET_COLOR_FORMAT:
- context_surfaces_2d->color_format = parameter;
- break;
- case NV062_SET_PITCH:
- context_surfaces_2d->source_pitch = parameter & 0xFFFF;
- context_surfaces_2d->dest_pitch = parameter >> 16;
- break;
- case NV062_SET_OFFSET_SOURCE:
- context_surfaces_2d->source_offset = parameter & 0x07FFFFFF;
- break;
- case NV062_SET_OFFSET_DESTIN:
- context_surfaces_2d->dest_offset = parameter & 0x07FFFFFF;
- break;
- default:
- EmuWarning("EmuNV2A: Unknown NV_CONTEXT_SURFACES_2D Method: 0x%08X\n", method);
- }
-
- break;
- }
-
- case NV_IMAGE_BLIT: {
- switch (method) {
- case NV09F_SET_CONTEXT_SURFACES:
- image_blit->context_surfaces = parameter;
- break;
- case NV09F_SET_OPERATION:
- image_blit->operation = parameter;
- break;
- case NV09F_CONTROL_POINT_IN:
- image_blit->in_x = parameter & 0xFFFF;
- image_blit->in_y = parameter >> 16;
- break;
- case NV09F_CONTROL_POINT_OUT:
- image_blit->out_x = parameter & 0xFFFF;
- image_blit->out_y = parameter >> 16;
- break;
- case NV09F_SIZE:
- image_blit->width = parameter & 0xFFFF;
- image_blit->height = parameter >> 16;
-
- /* I guess this kicks it off? */
- if (image_blit->operation == NV09F_SET_OPERATION_SRCCOPY) {
-
- printf("NV09F_SET_OPERATION_SRCCOPY\n");
-
- GraphicsObject *context_surfaces_obj = lookup_graphics_object(image_blit->context_surfaces);
- assert(context_surfaces_obj);
- assert(context_surfaces_obj->graphics_class == NV_CONTEXT_SURFACES_2D);
-
- ContextSurfaces2DState *context_surfaces = &context_surfaces_obj->data.context_surfaces_2d;
-
- unsigned int bytes_per_pixel;
- switch (context_surfaces->color_format) {
- case NV062_SET_COLOR_FORMAT_LE_Y8:
- bytes_per_pixel = 1;
- break;
- case NV062_SET_COLOR_FORMAT_LE_R5G6B5:
- bytes_per_pixel = 2;
- break;
- case NV062_SET_COLOR_FORMAT_LE_A8R8G8B8:
- bytes_per_pixel = 4;
- break;
- default:
- printf("Unknown blit surface format: 0x%x\n", context_surfaces->color_format);
- assert(false);
- break;
- }
-
- xbaddr source_dma_len, dest_dma_len;
- uint8_t *source, *dest;
-
- source = (uint8_t*)nv_dma_map(context_surfaces->dma_image_source, &source_dma_len);
- assert(context_surfaces->source_offset < source_dma_len);
- source += context_surfaces->source_offset;
-
- dest = (uint8_t*)nv_dma_map(context_surfaces->dma_image_dest, &dest_dma_len);
- assert(context_surfaces->dest_offset < dest_dma_len);
- dest += context_surfaces->dest_offset;
-
- printf(" - 0x%tx -> 0x%tx\n", source - MM_SYSTEM_PHYSICAL_MAP,dest - MM_SYSTEM_PHYSICAL_MAP);
-
- unsigned int y;
- for (y = 0; yheight; y++) {
- uint8_t *source_row = source
- + (image_blit->in_y + y) * context_surfaces->source_pitch
- + image_blit->in_x * bytes_per_pixel;
-
- uint8_t *dest_row = dest
- + (image_blit->out_y + y) * context_surfaces->dest_pitch
- + image_blit->out_x * bytes_per_pixel;
-
- memmove(dest_row, source_row,
- image_blit->width * bytes_per_pixel);
- }
- }
- else {
- assert(false);
- }
-
- break;
- default:
- EmuWarning("EmuNV2A: Unknown NV_IMAGE_BLIT Method: 0x%08X\n", method);
- }
- break;
- }
-
- case NV_KELVIN_PRIMITIVE: {
- switch (method) {
- case NV097_SET_CONTEXT_DMA_NOTIFIES:
- kelvin->dma_notifies = parameter;
- break;
- case NV097_SET_CONTEXT_DMA_A:
- pgraph.dma_a = parameter;
- break;
- case NV097_SET_CONTEXT_DMA_B:
- pgraph.dma_b = parameter;
- break;
- case NV097_SET_CONTEXT_DMA_STATE:
- kelvin->dma_state = parameter;
- break;
- case NV097_SET_CONTEXT_DMA_COLOR:
- /* try to get any straggling draws in before the surface's changed :/ */
- pgraph_update_surface(false, true, true);
-
- pgraph.dma_color = parameter;
- break;
- case NV097_SET_CONTEXT_DMA_ZETA:
- pgraph.dma_zeta = parameter;
- break;
- case NV097_SET_CONTEXT_DMA_VERTEX_A:
- pgraph.dma_vertex_a = parameter;
- break;
- case NV097_SET_CONTEXT_DMA_VERTEX_B:
- pgraph.dma_vertex_b = parameter;
- break;
- case NV097_SET_CONTEXT_DMA_SEMAPHORE:
- kelvin->dma_semaphore = parameter;
- break;
- case NV097_SET_CONTEXT_DMA_REPORT:
- pgraph.dma_report = parameter;
- break;
- case NV097_SET_SURFACE_CLIP_HORIZONTAL:
- pgraph_update_surface(false, true, true);
-
- pgraph.surface_shape.clip_x =
- GET_MASK(parameter, NV097_SET_SURFACE_CLIP_HORIZONTAL_X);
- pgraph.surface_shape.clip_width =
- GET_MASK(parameter, NV097_SET_SURFACE_CLIP_HORIZONTAL_WIDTH);
- break;
- case NV097_SET_SURFACE_CLIP_VERTICAL:
- pgraph_update_surface(false, true, true);
-
- pgraph.surface_shape.clip_y =
- GET_MASK(parameter, NV097_SET_SURFACE_CLIP_VERTICAL_Y);
- pgraph.surface_shape.clip_height =
- GET_MASK(parameter, NV097_SET_SURFACE_CLIP_VERTICAL_HEIGHT);
- break;
- case NV097_SET_SURFACE_FORMAT:
- pgraph_update_surface(false, true, true);
-
- pgraph.surface_shape.color_format =
- GET_MASK(parameter, NV097_SET_SURFACE_FORMAT_COLOR);
- pgraph.surface_shape.zeta_format =
- GET_MASK(parameter, NV097_SET_SURFACE_FORMAT_ZETA);
- pgraph.surface_type =
- GET_MASK(parameter, NV097_SET_SURFACE_FORMAT_TYPE);
- pgraph.surface_shape.anti_aliasing =
- GET_MASK(parameter, NV097_SET_SURFACE_FORMAT_ANTI_ALIASING);
- pgraph.surface_shape.log_width =
- GET_MASK(parameter, NV097_SET_SURFACE_FORMAT_WIDTH);
- pgraph.surface_shape.log_height =
- GET_MASK(parameter, NV097_SET_SURFACE_FORMAT_HEIGHT);
- break;
- case NV097_SET_SURFACE_PITCH:
- pgraph_update_surface(false, true, true);
-
- pgraph.surface_color.pitch =
- GET_MASK(parameter, NV097_SET_SURFACE_PITCH_COLOR);
- pgraph.surface_zeta.pitch =
- GET_MASK(parameter, NV097_SET_SURFACE_PITCH_ZETA);
- break;
- case NV097_SET_SURFACE_COLOR_OFFSET:
- pgraph_update_surface(false, true, true);
-
- pgraph.surface_color.offset = parameter;
- break;
- case NV097_SET_SURFACE_ZETA_OFFSET:
- pgraph_update_surface(false, true, true);
-
- pgraph.surface_zeta.offset = parameter;
- break;
- case NV097_SET_COMBINER_SPECULAR_FOG_CW0:
- pgraph.regs[NV_PGRAPH_COMBINESPECFOG0] = parameter;
- break;
- case NV097_SET_COMBINER_SPECULAR_FOG_CW1:
- pgraph.regs[NV_PGRAPH_COMBINESPECFOG1] = parameter;
- break;
- CASE_4(NV097_SET_TEXTURE_ADDRESS, 64):
- slot = (method - NV097_SET_TEXTURE_ADDRESS) / 64;
- pgraph.regs[NV_PGRAPH_TEXADDRESS0 + slot * 4] = parameter;
- break;
- case NV097_SET_CONTROL0: {
- pgraph_update_surface(false, true, true);
-
- bool stencil_write_enable =
- parameter & NV097_SET_CONTROL0_STENCIL_WRITE_ENABLE;
- SET_MASK(pgraph.regs[NV_PGRAPH_CONTROL_0],
- NV_PGRAPH_CONTROL_0_STENCIL_WRITE_ENABLE,
- stencil_write_enable);
-
- uint32_t z_format = GET_MASK(parameter, NV097_SET_CONTROL0_Z_FORMAT);
- SET_MASK(pgraph.regs[NV_PGRAPH_SETUPRASTER],
- NV_PGRAPH_SETUPRASTER_Z_FORMAT, z_format);
-
- bool z_perspective =
- parameter & NV097_SET_CONTROL0_Z_PERSPECTIVE_ENABLE;
- SET_MASK(pgraph.regs[NV_PGRAPH_CONTROL_0],
- NV_PGRAPH_CONTROL_0_Z_PERSPECTIVE_ENABLE,
- z_perspective);
- break;
- }
-
- case NV097_SET_FOG_MODE: {
- /* FIXME: There is also NV_PGRAPH_CSV0_D_FOG_MODE */
- unsigned int mode;
- switch (parameter) {
- case NV097_SET_FOG_MODE_V_LINEAR:
- mode = NV_PGRAPH_CONTROL_3_FOG_MODE_LINEAR; break;
- case NV097_SET_FOG_MODE_V_EXP:
- mode = NV_PGRAPH_CONTROL_3_FOG_MODE_EXP; break;
- case NV097_SET_FOG_MODE_V_EXP2:
- mode = NV_PGRAPH_CONTROL_3_FOG_MODE_EXP2; break;
- case NV097_SET_FOG_MODE_V_EXP_ABS:
- mode = NV_PGRAPH_CONTROL_3_FOG_MODE_EXP_ABS; break;
- case NV097_SET_FOG_MODE_V_EXP2_ABS:
- mode = NV_PGRAPH_CONTROL_3_FOG_MODE_EXP2_ABS; break;
- case NV097_SET_FOG_MODE_V_LINEAR_ABS:
- mode = NV_PGRAPH_CONTROL_3_FOG_MODE_LINEAR_ABS; break;
- default:
- assert(false);
- break;
- }
- SET_MASK(pgraph.regs[NV_PGRAPH_CONTROL_3], NV_PGRAPH_CONTROL_3_FOG_MODE,
- mode);
- break;
- }
- case NV097_SET_FOG_GEN_MODE: {
- unsigned int mode;
- switch (parameter) {
- case NV097_SET_FOG_GEN_MODE_V_SPEC_ALPHA:
- mode = NV_PGRAPH_CSV0_D_FOGGENMODE_SPEC_ALPHA; break;
- case NV097_SET_FOG_GEN_MODE_V_RADIAL:
- mode = NV_PGRAPH_CSV0_D_FOGGENMODE_RADIAL; break;
- case NV097_SET_FOG_GEN_MODE_V_PLANAR:
- mode = NV_PGRAPH_CSV0_D_FOGGENMODE_PLANAR; break;
- case NV097_SET_FOG_GEN_MODE_V_ABS_PLANAR:
- mode = NV_PGRAPH_CSV0_D_FOGGENMODE_ABS_PLANAR; break;
- case NV097_SET_FOG_GEN_MODE_V_FOG_X:
- mode = NV_PGRAPH_CSV0_D_FOGGENMODE_FOG_X; break;
- default:
- assert(false);
- break;
- }
- SET_MASK(pgraph.regs[NV_PGRAPH_CSV0_D], NV_PGRAPH_CSV0_D_FOGGENMODE, mode);
- break;
- }
- case NV097_SET_FOG_ENABLE:
- /*
- FIXME: There is also:
- SET_MASK(pgraph.regs[NV_PGRAPH_CSV0_D], NV_PGRAPH_CSV0_D_FOGENABLE,
- parameter);
- */
- SET_MASK(pgraph.regs[NV_PGRAPH_CONTROL_3], NV_PGRAPH_CONTROL_3_FOGENABLE,
- parameter);
- break;
- case NV097_SET_FOG_COLOR: {
- /* PGRAPH channels are ARGB, parameter channels are ABGR */
- uint8_t red = GET_MASK(parameter, NV097_SET_FOG_COLOR_RED);
- uint8_t green = GET_MASK(parameter, NV097_SET_FOG_COLOR_GREEN);
- uint8_t blue = GET_MASK(parameter, NV097_SET_FOG_COLOR_BLUE);
- uint8_t alpha = GET_MASK(parameter, NV097_SET_FOG_COLOR_ALPHA);
- SET_MASK(pgraph.regs[NV_PGRAPH_FOGCOLOR], NV_PGRAPH_FOGCOLOR_RED, red);
- SET_MASK(pgraph.regs[NV_PGRAPH_FOGCOLOR], NV_PGRAPH_FOGCOLOR_GREEN, green);
- SET_MASK(pgraph.regs[NV_PGRAPH_FOGCOLOR], NV_PGRAPH_FOGCOLOR_BLUE, blue);
- SET_MASK(pgraph.regs[NV_PGRAPH_FOGCOLOR], NV_PGRAPH_FOGCOLOR_ALPHA, alpha);
- break;
- }
- case NV097_SET_ALPHA_TEST_ENABLE:
- SET_MASK(pgraph.regs[NV_PGRAPH_CONTROL_0],
- NV_PGRAPH_CONTROL_0_ALPHATESTENABLE, parameter);
- break;
- case NV097_SET_BLEND_ENABLE:
- SET_MASK(pgraph.regs[NV_PGRAPH_BLEND], NV_PGRAPH_BLEND_EN, parameter);
- break;
- case NV097_SET_CULL_FACE_ENABLE:
- SET_MASK(pgraph.regs[NV_PGRAPH_SETUPRASTER],
- NV_PGRAPH_SETUPRASTER_CULLENABLE,
- parameter);
- break;
- case NV097_SET_DEPTH_TEST_ENABLE:
- SET_MASK(pgraph.regs[NV_PGRAPH_CONTROL_0], NV_PGRAPH_CONTROL_0_ZENABLE,
- parameter);
- break;
- case NV097_SET_DITHER_ENABLE:
- SET_MASK(pgraph.regs[NV_PGRAPH_CONTROL_0],
- NV_PGRAPH_CONTROL_0_DITHERENABLE, parameter);
- break;
- case NV097_SET_LIGHTING_ENABLE:
- SET_MASK(pgraph.regs[NV_PGRAPH_CSV0_C], NV_PGRAPH_CSV0_C_LIGHTING,
- parameter);
- break;
- case NV097_SET_SKIN_MODE:
- SET_MASK(pgraph.regs[NV_PGRAPH_CSV0_D], NV_PGRAPH_CSV0_D_SKIN,
- parameter);
- break;
- case NV097_SET_STENCIL_TEST_ENABLE:
- SET_MASK(pgraph.regs[NV_PGRAPH_CONTROL_1],
- NV_PGRAPH_CONTROL_1_STENCIL_TEST_ENABLE, parameter);
- break;
- case NV097_SET_POLY_OFFSET_POINT_ENABLE:
- SET_MASK(pgraph.regs[NV_PGRAPH_SETUPRASTER],
- NV_PGRAPH_SETUPRASTER_POFFSETPOINTENABLE, parameter);
- break;
- case NV097_SET_POLY_OFFSET_LINE_ENABLE:
- SET_MASK(pgraph.regs[NV_PGRAPH_SETUPRASTER],
- NV_PGRAPH_SETUPRASTER_POFFSETLINEENABLE, parameter);
- break;
- case NV097_SET_POLY_OFFSET_FILL_ENABLE:
- SET_MASK(pgraph.regs[NV_PGRAPH_SETUPRASTER],
- NV_PGRAPH_SETUPRASTER_POFFSETFILLENABLE, parameter);
- break;
- case NV097_SET_ALPHA_FUNC:
- SET_MASK(pgraph.regs[NV_PGRAPH_CONTROL_0],
- NV_PGRAPH_CONTROL_0_ALPHAFUNC, parameter & 0xF);
- break;
- case NV097_SET_ALPHA_REF:
- SET_MASK(pgraph.regs[NV_PGRAPH_CONTROL_0],
- NV_PGRAPH_CONTROL_0_ALPHAREF, parameter);
- break;
- case NV097_SET_BLEND_FUNC_SFACTOR: {
- unsigned int factor;
- switch (parameter) {
- case NV097_SET_BLEND_FUNC_SFACTOR_V_ZERO:
- factor = NV_PGRAPH_BLEND_SFACTOR_ZERO; break;
- case NV097_SET_BLEND_FUNC_SFACTOR_V_ONE:
- factor = NV_PGRAPH_BLEND_SFACTOR_ONE; break;
- case NV097_SET_BLEND_FUNC_SFACTOR_V_SRC_COLOR:
- factor = NV_PGRAPH_BLEND_SFACTOR_SRC_COLOR; break;
- case NV097_SET_BLEND_FUNC_SFACTOR_V_ONE_MINUS_SRC_COLOR:
- factor = NV_PGRAPH_BLEND_SFACTOR_ONE_MINUS_SRC_COLOR; break;
- case NV097_SET_BLEND_FUNC_SFACTOR_V_SRC_ALPHA:
- factor = NV_PGRAPH_BLEND_SFACTOR_SRC_ALPHA; break;
- case NV097_SET_BLEND_FUNC_SFACTOR_V_ONE_MINUS_SRC_ALPHA:
- factor = NV_PGRAPH_BLEND_SFACTOR_ONE_MINUS_SRC_ALPHA; break;
- case NV097_SET_BLEND_FUNC_SFACTOR_V_DST_ALPHA:
- factor = NV_PGRAPH_BLEND_SFACTOR_DST_ALPHA; break;
- case NV097_SET_BLEND_FUNC_SFACTOR_V_ONE_MINUS_DST_ALPHA:
- factor = NV_PGRAPH_BLEND_SFACTOR_ONE_MINUS_DST_ALPHA; break;
- case NV097_SET_BLEND_FUNC_SFACTOR_V_DST_COLOR:
- factor = NV_PGRAPH_BLEND_SFACTOR_DST_COLOR; break;
- case NV097_SET_BLEND_FUNC_SFACTOR_V_ONE_MINUS_DST_COLOR:
- factor = NV_PGRAPH_BLEND_SFACTOR_ONE_MINUS_DST_COLOR; break;
- case NV097_SET_BLEND_FUNC_SFACTOR_V_SRC_ALPHA_SATURATE:
- factor = NV_PGRAPH_BLEND_SFACTOR_SRC_ALPHA_SATURATE; break;
- case NV097_SET_BLEND_FUNC_SFACTOR_V_CONSTANT_COLOR:
- factor = NV_PGRAPH_BLEND_SFACTOR_CONSTANT_COLOR; break;
- case NV097_SET_BLEND_FUNC_SFACTOR_V_ONE_MINUS_CONSTANT_COLOR:
- factor = NV_PGRAPH_BLEND_SFACTOR_ONE_MINUS_CONSTANT_COLOR; break;
- case NV097_SET_BLEND_FUNC_SFACTOR_V_CONSTANT_ALPHA:
- factor = NV_PGRAPH_BLEND_SFACTOR_CONSTANT_ALPHA; break;
- case NV097_SET_BLEND_FUNC_SFACTOR_V_ONE_MINUS_CONSTANT_ALPHA:
- factor = NV_PGRAPH_BLEND_SFACTOR_ONE_MINUS_CONSTANT_ALPHA; break;
- default:
- fprintf(stderr, "Unknown blend source factor: 0x%x\n", parameter);
- assert(false);
- break;
- }
- SET_MASK(pgraph.regs[NV_PGRAPH_BLEND], NV_PGRAPH_BLEND_SFACTOR, factor);
-
- break;
- }
-
- case NV097_SET_BLEND_FUNC_DFACTOR: {
- unsigned int factor;
- switch (parameter) {
- case NV097_SET_BLEND_FUNC_DFACTOR_V_ZERO:
- factor = NV_PGRAPH_BLEND_DFACTOR_ZERO; break;
- case NV097_SET_BLEND_FUNC_DFACTOR_V_ONE:
- factor = NV_PGRAPH_BLEND_DFACTOR_ONE; break;
- case NV097_SET_BLEND_FUNC_DFACTOR_V_SRC_COLOR:
- factor = NV_PGRAPH_BLEND_DFACTOR_SRC_COLOR; break;
- case NV097_SET_BLEND_FUNC_DFACTOR_V_ONE_MINUS_SRC_COLOR:
- factor = NV_PGRAPH_BLEND_DFACTOR_ONE_MINUS_SRC_COLOR; break;
- case NV097_SET_BLEND_FUNC_DFACTOR_V_SRC_ALPHA:
- factor = NV_PGRAPH_BLEND_DFACTOR_SRC_ALPHA; break;
- case NV097_SET_BLEND_FUNC_DFACTOR_V_ONE_MINUS_SRC_ALPHA:
- factor = NV_PGRAPH_BLEND_DFACTOR_ONE_MINUS_SRC_ALPHA; break;
- case NV097_SET_BLEND_FUNC_DFACTOR_V_DST_ALPHA:
- factor = NV_PGRAPH_BLEND_DFACTOR_DST_ALPHA; break;
- case NV097_SET_BLEND_FUNC_DFACTOR_V_ONE_MINUS_DST_ALPHA:
- factor = NV_PGRAPH_BLEND_DFACTOR_ONE_MINUS_DST_ALPHA; break;
- case NV097_SET_BLEND_FUNC_DFACTOR_V_DST_COLOR:
- factor = NV_PGRAPH_BLEND_DFACTOR_DST_COLOR; break;
- case NV097_SET_BLEND_FUNC_DFACTOR_V_ONE_MINUS_DST_COLOR:
- factor = NV_PGRAPH_BLEND_DFACTOR_ONE_MINUS_DST_COLOR; break;
- case NV097_SET_BLEND_FUNC_DFACTOR_V_SRC_ALPHA_SATURATE:
- factor = NV_PGRAPH_BLEND_DFACTOR_SRC_ALPHA_SATURATE; break;
- case NV097_SET_BLEND_FUNC_DFACTOR_V_CONSTANT_COLOR:
- factor = NV_PGRAPH_BLEND_DFACTOR_CONSTANT_COLOR; break;
- case NV097_SET_BLEND_FUNC_DFACTOR_V_ONE_MINUS_CONSTANT_COLOR:
- factor = NV_PGRAPH_BLEND_DFACTOR_ONE_MINUS_CONSTANT_COLOR; break;
- case NV097_SET_BLEND_FUNC_DFACTOR_V_CONSTANT_ALPHA:
- factor = NV_PGRAPH_BLEND_DFACTOR_CONSTANT_ALPHA; break;
- case NV097_SET_BLEND_FUNC_DFACTOR_V_ONE_MINUS_CONSTANT_ALPHA:
- factor = NV_PGRAPH_BLEND_DFACTOR_ONE_MINUS_CONSTANT_ALPHA; break;
- default:
- fprintf(stderr, "Unknown blend destination factor: 0x%x\n", parameter);
- assert(false);
- break;
- }
- SET_MASK(pgraph.regs[NV_PGRAPH_BLEND], NV_PGRAPH_BLEND_DFACTOR, factor);
-
- break;
- }
-
- case NV097_SET_BLEND_COLOR:
- pgraph.regs[NV_PGRAPH_BLENDCOLOR] = parameter;
- break;
-
- case NV097_SET_BLEND_EQUATION: {
- unsigned int equation;
- switch (parameter) {
- case NV097_SET_BLEND_EQUATION_V_FUNC_SUBTRACT:
- equation = 0; break;
- case NV097_SET_BLEND_EQUATION_V_FUNC_REVERSE_SUBTRACT:
- equation = 1; break;
- case NV097_SET_BLEND_EQUATION_V_FUNC_ADD:
- equation = 2; break;
- case NV097_SET_BLEND_EQUATION_V_MIN:
- equation = 3; break;
- case NV097_SET_BLEND_EQUATION_V_MAX:
- equation = 4; break;
- case NV097_SET_BLEND_EQUATION_V_FUNC_REVERSE_SUBTRACT_SIGNED:
- equation = 5; break;
- case NV097_SET_BLEND_EQUATION_V_FUNC_ADD_SIGNED:
- equation = 6; break;
- default:
- assert(false);
- break;
- }
- SET_MASK(pgraph.regs[NV_PGRAPH_BLEND], NV_PGRAPH_BLEND_EQN, equation);
-
- break;
- }
-
- case NV097_SET_DEPTH_FUNC:
- SET_MASK(pgraph.regs[NV_PGRAPH_CONTROL_0], NV_PGRAPH_CONTROL_0_ZFUNC,
- parameter & 0xF);
- break;
-
- case NV097_SET_COLOR_MASK: {
- pgraph.surface_color.write_enabled_cache |= pgraph_color_write_enabled();
-
- bool alpha = parameter & NV097_SET_COLOR_MASK_ALPHA_WRITE_ENABLE;
- bool red = parameter & NV097_SET_COLOR_MASK_RED_WRITE_ENABLE;
- bool green = parameter & NV097_SET_COLOR_MASK_GREEN_WRITE_ENABLE;
- bool blue = parameter & NV097_SET_COLOR_MASK_BLUE_WRITE_ENABLE;
- SET_MASK(pgraph.regs[NV_PGRAPH_CONTROL_0],
- NV_PGRAPH_CONTROL_0_ALPHA_WRITE_ENABLE, alpha);
- SET_MASK(pgraph.regs[NV_PGRAPH_CONTROL_0],
- NV_PGRAPH_CONTROL_0_RED_WRITE_ENABLE, red);
- SET_MASK(pgraph.regs[NV_PGRAPH_CONTROL_0],
- NV_PGRAPH_CONTROL_0_GREEN_WRITE_ENABLE, green);
- SET_MASK(pgraph.regs[NV_PGRAPH_CONTROL_0],
- NV_PGRAPH_CONTROL_0_BLUE_WRITE_ENABLE, blue);
- break;
- }
- case NV097_SET_DEPTH_MASK:
- pgraph.surface_zeta.write_enabled_cache |= pgraph_zeta_write_enabled();
-
- SET_MASK(pgraph.regs[NV_PGRAPH_CONTROL_0],
- NV_PGRAPH_CONTROL_0_ZWRITEENABLE, parameter);
- break;
- case NV097_SET_STENCIL_MASK:
- SET_MASK(pgraph.regs[NV_PGRAPH_CONTROL_1],
- NV_PGRAPH_CONTROL_1_STENCIL_MASK_WRITE, parameter);
- break;
- case NV097_SET_STENCIL_FUNC:
- SET_MASK(pgraph.regs[NV_PGRAPH_CONTROL_1],
- NV_PGRAPH_CONTROL_1_STENCIL_FUNC, parameter & 0xF);
- break;
- case NV097_SET_STENCIL_FUNC_REF:
- SET_MASK(pgraph.regs[NV_PGRAPH_CONTROL_1],
- NV_PGRAPH_CONTROL_1_STENCIL_REF, parameter);
- break;
- case NV097_SET_STENCIL_FUNC_MASK:
- SET_MASK(pgraph.regs[NV_PGRAPH_CONTROL_1],
- NV_PGRAPH_CONTROL_1_STENCIL_MASK_READ, parameter);
- break;
- case NV097_SET_STENCIL_OP_FAIL:
- SET_MASK(pgraph.regs[NV_PGRAPH_CONTROL_2],
- NV_PGRAPH_CONTROL_2_STENCIL_OP_FAIL,
- kelvin_map_stencil_op(parameter));
- break;
- case NV097_SET_STENCIL_OP_ZFAIL:
- SET_MASK(pgraph.regs[NV_PGRAPH_CONTROL_2],
- NV_PGRAPH_CONTROL_2_STENCIL_OP_ZFAIL,
- kelvin_map_stencil_op(parameter));
- break;
- case NV097_SET_STENCIL_OP_ZPASS:
- SET_MASK(pgraph.regs[NV_PGRAPH_CONTROL_2],
- NV_PGRAPH_CONTROL_2_STENCIL_OP_ZPASS,
- kelvin_map_stencil_op(parameter));
- break;
-
- case NV097_SET_POLYGON_OFFSET_SCALE_FACTOR:
- pgraph.regs[NV_PGRAPH_ZOFFSETFACTOR] = parameter;
- break;
- case NV097_SET_POLYGON_OFFSET_BIAS:
- pgraph.regs[NV_PGRAPH_ZOFFSETBIAS] = parameter;
- break;
- case NV097_SET_FRONT_POLYGON_MODE:
- SET_MASK(pgraph.regs[NV_PGRAPH_SETUPRASTER],
- NV_PGRAPH_SETUPRASTER_FRONTFACEMODE,
- kelvin_map_polygon_mode(parameter));
- break;
- case NV097_SET_BACK_POLYGON_MODE:
- SET_MASK(pgraph.regs[NV_PGRAPH_SETUPRASTER],
- NV_PGRAPH_SETUPRASTER_BACKFACEMODE,
- kelvin_map_polygon_mode(parameter));
- break;
- case NV097_SET_CLIP_MIN:
- pgraph.regs[NV_PGRAPH_ZCLIPMIN] = parameter;
- break;
- case NV097_SET_CLIP_MAX:
- pgraph.regs[NV_PGRAPH_ZCLIPMAX] = parameter;
- break;
- case NV097_SET_CULL_FACE: {
- unsigned int face;
- switch (parameter) {
- case NV097_SET_CULL_FACE_V_FRONT:
- face = NV_PGRAPH_SETUPRASTER_CULLCTRL_FRONT; break;
- case NV097_SET_CULL_FACE_V_BACK:
- face = NV_PGRAPH_SETUPRASTER_CULLCTRL_BACK; break;
- case NV097_SET_CULL_FACE_V_FRONT_AND_BACK:
- face = NV_PGRAPH_SETUPRASTER_CULLCTRL_FRONT_AND_BACK; break;
- default:
- assert(false);
- break;
- }
- SET_MASK(pgraph.regs[NV_PGRAPH_SETUPRASTER],
- NV_PGRAPH_SETUPRASTER_CULLCTRL,
- face);
- break;
- }
- case NV097_SET_FRONT_FACE: {
- bool ccw;
- switch (parameter) {
- case NV097_SET_FRONT_FACE_V_CW:
- ccw = false; break;
- case NV097_SET_FRONT_FACE_V_CCW:
- ccw = true; break;
- default:
- fprintf(stderr, "Unknown front face: 0x%x\n", parameter);
- assert(false);
- break;
- }
- SET_MASK(pgraph.regs[NV_PGRAPH_SETUPRASTER],
- NV_PGRAPH_SETUPRASTER_FRONTFACE,
- ccw ? 1 : 0);
- break;
- }
- case NV097_SET_NORMALIZATION_ENABLE:
- SET_MASK(pgraph.regs[NV_PGRAPH_CSV0_C],
- NV_PGRAPH_CSV0_C_NORMALIZATION_ENABLE,
- parameter);
- break;
-
- case NV097_SET_LIGHT_ENABLE_MASK:
- SET_MASK(pgraph.regs[NV_PGRAPH_CSV0_D],
- NV_PGRAPH_CSV0_D_LIGHTS,
- parameter);
- break;
-
- CASE_4(NV097_SET_TEXGEN_S, 16) : {
- slot = (method - NV097_SET_TEXGEN_S) / 16;
- unsigned int reg = (slot < 2) ? NV_PGRAPH_CSV1_A
- : NV_PGRAPH_CSV1_B;
- unsigned int mask = (slot % 2) ? NV_PGRAPH_CSV1_A_T1_S
- : NV_PGRAPH_CSV1_A_T0_S;
- SET_MASK(pgraph.regs[reg], mask, kelvin_map_texgen(parameter, 0));
- break;
- }
- CASE_4(NV097_SET_TEXGEN_T, 16) : {
- slot = (method - NV097_SET_TEXGEN_T) / 16;
- unsigned int reg = (slot < 2) ? NV_PGRAPH_CSV1_A
- : NV_PGRAPH_CSV1_B;
- unsigned int mask = (slot % 2) ? NV_PGRAPH_CSV1_A_T1_T
- : NV_PGRAPH_CSV1_A_T0_T;
- SET_MASK(pgraph.regs[reg], mask, kelvin_map_texgen(parameter, 1));
- break;
- }
- CASE_4(NV097_SET_TEXGEN_R, 16) : {
- slot = (method - NV097_SET_TEXGEN_R) / 16;
- unsigned int reg = (slot < 2) ? NV_PGRAPH_CSV1_A
- : NV_PGRAPH_CSV1_B;
- unsigned int mask = (slot % 2) ? NV_PGRAPH_CSV1_A_T1_R
- : NV_PGRAPH_CSV1_A_T0_R;
- SET_MASK(pgraph.regs[reg], mask, kelvin_map_texgen(parameter, 2));
- break;
- }
- CASE_4(NV097_SET_TEXGEN_Q, 16) : {
- slot = (method - NV097_SET_TEXGEN_Q) / 16;
- unsigned int reg = (slot < 2) ? NV_PGRAPH_CSV1_A
- : NV_PGRAPH_CSV1_B;
- unsigned int mask = (slot % 2) ? NV_PGRAPH_CSV1_A_T1_Q
- : NV_PGRAPH_CSV1_A_T0_Q;
- SET_MASK(pgraph.regs[reg], mask, kelvin_map_texgen(parameter, 3));
- break;
- }
- CASE_4(NV097_SET_TEXTURE_MATRIX_ENABLE, 4) :
- slot = (method - NV097_SET_TEXTURE_MATRIX_ENABLE) / 4;
- pgraph.texture_matrix_enable[slot] = parameter;
- break;
-
- case NV097_SET_TEXGEN_VIEW_MODEL:
- SET_MASK(pgraph.regs[NV_PGRAPH_CSV0_D], NV_PGRAPH_CSV0_D_TEXGEN_REF, parameter);
- break;
- case NV097_SET_SEMAPHORE_OFFSET:
- kelvin->semaphore_offset = parameter;
- break;
- case NV097_BACK_END_WRITE_SEMAPHORE_RELEASE: {
-
- pgraph_update_surface(false, true, true);
-
- //qemu_mutex_unlock(&d->pgraph.lock);
- //qemu_mutex_lock_iothread();
-
- xbaddr semaphore_dma_len;
- uint8_t *semaphore_data = (uint8_t*)nv_dma_map(kelvin->dma_semaphore,
- &semaphore_dma_len);
- assert(kelvin->semaphore_offset < semaphore_dma_len);
- semaphore_data += kelvin->semaphore_offset;
-
- stl_le_p((uint32_t*)semaphore_data, parameter);
-
- //qemu_mutex_lock(&d->pgraph.lock);
- //qemu_mutex_unlock_iothread();
-
- break;
- }
- default:
- if (method >= NV097_SET_COMBINER_ALPHA_ICW && method <= NV097_SET_COMBINER_ALPHA_ICW + 28) {
- slot = (method - NV097_SET_COMBINER_ALPHA_ICW) / 4;
- pgraph.regs[NV_PGRAPH_COMBINEALPHAI0 + slot * 4] = parameter;
- break;
- }
-
- if (method >= NV097_SET_PROJECTION_MATRIX && method <= NV097_SET_PROJECTION_MATRIX + 0x3c) {
- slot = (method - NV097_SET_PROJECTION_MATRIX) / 4;
- // pg->projection_matrix[slot] = *(float*)¶meter;
- unsigned int row = NV_IGRAPH_XF_XFCTX_PMAT0 + slot / 4;
- pgraph.vsh_constants[row][slot % 4] = parameter;
- pgraph.vsh_constants_dirty[row] = true;
- break;
- }
-
- if (method >= NV097_SET_MODEL_VIEW_MATRIX && method <= NV097_SET_MODEL_VIEW_MATRIX + 0xfc) {
- slot = (method - NV097_SET_MODEL_VIEW_MATRIX) / 4;
- unsigned int matnum = slot / 16;
- unsigned int entry = slot % 16;
- unsigned int row = NV_IGRAPH_XF_XFCTX_MMAT0 + matnum * 8 + entry / 4;
- pgraph.vsh_constants[row][entry % 4] = parameter;
- pgraph.vsh_constants_dirty[row] = true;
- break;
- }
-
- if (method >= NV097_SET_INVERSE_MODEL_VIEW_MATRIX && method <= NV097_SET_INVERSE_MODEL_VIEW_MATRIX + 0xfc) {
- slot = (method - NV097_SET_INVERSE_MODEL_VIEW_MATRIX) / 4;
- unsigned int matnum = slot / 16;
- unsigned int entry = slot % 16;
- unsigned int row = NV_IGRAPH_XF_XFCTX_IMMAT0 + matnum * 8 + entry / 4;
- pgraph.vsh_constants[row][entry % 4] = parameter;
- pgraph.vsh_constants_dirty[row] = true;
- break;
- }
-
- if (method >= NV097_SET_COMPOSITE_MATRIX && method <= NV097_SET_COMPOSITE_MATRIX + 0x3c) {
- slot = (method - NV097_SET_COMPOSITE_MATRIX) / 4;
- unsigned int row = NV_IGRAPH_XF_XFCTX_CMAT0 + slot / 4;
- pgraph.vsh_constants[row][slot % 4] = parameter;
- pgraph.vsh_constants_dirty[row] = true;
- break;
- }
-
- if (method >= NV097_SET_TEXTURE_MATRIX && method <= NV097_SET_TEXTURE_MATRIX + 0xfc) {
- slot = (method - NV097_SET_TEXTURE_MATRIX) / 4;
- unsigned int tex = slot / 16;
- unsigned int entry = slot % 16;
- unsigned int row = NV_IGRAPH_XF_XFCTX_T0MAT + tex * 8 + entry / 4;
- pgraph.vsh_constants[row][entry % 4] = parameter;
- pgraph.vsh_constants_dirty[row] = true;
- break;
- }
-
- if (method >= NV097_SET_FOG_PARAMS && method <= NV097_SET_FOG_PARAMS + 8) {
- slot = (method - NV097_SET_FOG_PARAMS) / 4;
- if (slot < 2) {
- pgraph.regs[NV_PGRAPH_FOGPARAM0 + slot * 4] = parameter;
- }
- else {
- /* FIXME: No idea where slot = 2 is */
- }
-
- pgraph.ltctxa[NV_IGRAPH_XF_LTCTXA_FOG_K][slot] = parameter;
- pgraph.ltctxa_dirty[NV_IGRAPH_XF_LTCTXA_FOG_K] = true;
- break;
- }
-
- /* Handles NV097_SET_TEXGEN_PLANE_S,T,R,Q */
- if (method >= NV097_SET_TEXGEN_PLANE_S && method <= NV097_SET_TEXGEN_PLANE_S + 0xfc) {
- slot = (method - NV097_SET_TEXGEN_PLANE_S) / 4;
- unsigned int tex = slot / 16;
- unsigned int entry = slot % 16;
- unsigned int row = NV_IGRAPH_XF_XFCTX_TG0MAT + tex * 8 + entry / 4;
- pgraph.vsh_constants[row][entry % 4] = parameter;
- pgraph.vsh_constants_dirty[row] = true;
- break;
- }
-
- if (method >= NV097_SET_FOG_PLANE && method <= NV097_SET_FOG_PLANE + 12) {
- slot = (method - NV097_SET_FOG_PLANE) / 4;
- pgraph.vsh_constants[NV_IGRAPH_XF_XFCTX_FOG][slot] = parameter;
- pgraph.vsh_constants_dirty[NV_IGRAPH_XF_XFCTX_FOG] = true;
- break;
- }
-
- if (method >= NV097_SET_SCENE_AMBIENT_COLOR && method <= NV097_SET_SCENE_AMBIENT_COLOR + 8) {
- slot = (method - NV097_SET_SCENE_AMBIENT_COLOR) / 4;
- // ??
- pgraph.ltctxa[NV_IGRAPH_XF_LTCTXA_FR_AMB][slot] = parameter;
- pgraph.ltctxa_dirty[NV_IGRAPH_XF_LTCTXA_FR_AMB] = true;
- break;
- }
-
- if (method >= NV097_SET_VIEWPORT_OFFSET && method <= NV097_SET_VIEWPORT_OFFSET + 12) {
- slot = (method - NV097_SET_VIEWPORT_OFFSET) / 4;
- pgraph.vsh_constants[NV_IGRAPH_XF_XFCTX_VPOFF][slot] = parameter;
- pgraph.vsh_constants_dirty[NV_IGRAPH_XF_XFCTX_VPOFF] = true;
- break;
- }
-
- if (method >= NV097_SET_EYE_POSITION && method <= NV097_SET_EYE_POSITION + 12) {
- slot = (method - NV097_SET_EYE_POSITION) / 4;
- pgraph.vsh_constants[NV_IGRAPH_XF_XFCTX_EYEP][slot] = parameter;
- pgraph.vsh_constants_dirty[NV_IGRAPH_XF_XFCTX_EYEP] = true;
- break;
- }
-
- if (method >= NV097_SET_COMBINER_FACTOR0 && method <= NV097_SET_COMBINER_FACTOR0 + 28) {
- slot = (method - NV097_SET_COMBINER_FACTOR0) / 4;
- pgraph.regs[NV_PGRAPH_COMBINEFACTOR0 + slot * 4] = parameter;
- break;
- }
-
- if (method >= NV097_SET_COMBINER_FACTOR1 && method <= NV097_SET_COMBINER_FACTOR1 + 28) {
- slot = (method - NV097_SET_COMBINER_FACTOR1) / 4;
- pgraph.regs[NV_PGRAPH_COMBINEFACTOR1 + slot * 4] = parameter;
- break;
- }
-
- if (method >= NV097_SET_COMBINER_ALPHA_OCW && method <= NV097_SET_COMBINER_ALPHA_OCW + 28) {
- slot = (method - NV097_SET_COMBINER_ALPHA_OCW) / 4;
- pgraph.regs[NV_PGRAPH_COMBINEALPHAO0 + slot * 4] = parameter;
- break;
- }
-
- if (method >= NV097_SET_COMBINER_COLOR_ICW && method <= NV097_SET_COMBINER_COLOR_ICW + 28) {
- slot = (method - NV097_SET_COMBINER_COLOR_ICW) / 4;
- pgraph.regs[NV_PGRAPH_COMBINECOLORI0 + slot * 4] = parameter;
- break;
- }
-
- if (method >= NV097_SET_VIEWPORT_SCALE && method <= NV097_SET_VIEWPORT_SCALE + 12) {
- slot = (method - NV097_SET_VIEWPORT_SCALE) / 4;
- pgraph.vsh_constants[NV_IGRAPH_XF_XFCTX_VPSCL][slot] = parameter;
- pgraph.vsh_constants_dirty[NV_IGRAPH_XF_XFCTX_VPSCL] = true;
- break;
- }
-
- EmuWarning("EmuNV2A: Unknown NV_KELVIN_PRIMITIVE Method: 0x%08X\n", method);
- }
- break;
- }
-
- default:
- EmuWarning("EmuNV2A: Unknown Graphics Class/Method 0x%08X/0x%08X\n", object->graphics_class, method);
- break;
- }
-}
-
-static void* pfifo_puller_thread()
-{
- CxbxSetThreadName("Cxbx NV2A FIFO");
-
- Cache1State *state = &pfifo.cache1;
-
- while (true) {
- // Scope the lock so that it automatically unlocks at tne end of this block
- {
- std::unique_lock lk(state->mutex);
-
- while (state->cache.empty() || !state->pull_enabled) {
- state->cache_cond.wait(lk);
- }
-
- // Copy cache to working_cache
- while (!state->cache.empty()) {
- state->working_cache.push(state->cache.front());
- state->cache.pop();
- }
- }
-
- while (!state->working_cache.empty()) {
- CacheEntry* command = state->working_cache.front();
- state->working_cache.pop();
-
- if (command->method == 0) {
- // qemu_mutex_lock_iothread();
- RAMHTEntry entry = ramht_lookup(command->parameter);
- assert(entry.valid);
-
- assert(entry.channel_id == state->channel_id);
- // qemu_mutex_unlock_iothread();
-
- switch (entry.engine) {
- case ENGINE_GRAPHICS:
- pgraph_context_switch(entry.channel_id);
- pgraph_wait_fifo_access();
- pgraph_method(command->subchannel, 0, entry.instance);
- break;
- default:
- assert(false);
- break;
- }
-
- /* the engine is bound to the subchannel */
- std::lock_guard lk(pfifo.cache1.mutex);
- state->bound_engines[command->subchannel] = entry.engine;
- state->last_engine = entry.engine;
- } else if (command->method >= 0x100) {
- /* method passed to engine */
-
- uint32_t parameter = command->parameter;
-
- /* methods that take objects.
- * TODO: Check this range is correct for the nv2a */
- if (command->method >= 0x180 && command->method < 0x200) {
- //qemu_mutex_lock_iothread();
- RAMHTEntry entry = ramht_lookup(parameter);
- assert(entry.valid);
- assert(entry.channel_id == state->channel_id);
- parameter = entry.instance;
- //qemu_mutex_unlock_iothread();
- }
-
- // qemu_mutex_lock(&state->cache_lock);
- enum FIFOEngine engine = state->bound_engines[command->subchannel];
- // qemu_mutex_unlock(&state->cache_lock);
-
- switch (engine) {
- case ENGINE_GRAPHICS:
- pgraph_wait_fifo_access();
- pgraph_method(command->subchannel, command->method, parameter);
- break;
- default:
- assert(false);
- break;
- }
-
- // qemu_mutex_lock(&state->cache_lock);
- state->last_engine = state->bound_engines[command->subchannel];
- // qemu_mutex_unlock(&state->cache_lock);
- }
-
- free(command);
- }
- }
-
- return NULL;
-}
-
-DEVICE_READ32(PMC)
-{
- DEVICE_READ32_SWITCH() {
- case NV_PMC_BOOT_0: // chipset and stepping: NV2A, A02, Rev 0
- result = 0x02A000A2;
- break;
- case NV_PMC_BOOT_1: // Selects big/little endian mode for the card
- result = 0; // When read, returns 0 if in little-endian mode, 0x01000001 if in big-endian mode.
- break;
- case NV_PMC_INTR_0: // Shows which functional units have pending IRQ
- result = pmc.pending_interrupts;
- break;
- case NV_PMC_INTR_EN_0: // Selects which functional units can cause IRQs
- result = pmc.enabled_interrupts;
- break;
- default:
- result = 0;
- //DEVICE_READ32_REG(pmc); // Was : DEBUG_READ32_UNHANDLED(PMC);
- break;
- }
-
- DEVICE_READ32_END(PMC);
-}
-
-DEVICE_WRITE32(PMC)
-{
- switch(addr) {
- case NV_PMC_INTR_0:
- pmc.pending_interrupts &= ~value;
- update_irq();
- break;
- case NV_PMC_INTR_EN_0:
- pmc.enabled_interrupts = value;
- update_irq();
- break;
-
- default:
- //DEVICE_WRITE32_REG(pmc); // Was : DEBUG_WRITE32_UNHANDLED(PMC);
- break;
- }
-
- DEVICE_WRITE32_END(PMC);
-}
-
-
-DEVICE_READ32(PBUS)
-{
- DEVICE_READ32_SWITCH() {
- case NV_PBUS_PCI_NV_0:
- result = 0x10de; // PCI_VENDOR_ID_NVIDIA (?where to return PCI_DEVICE_ID_NVIDIA_NV2A = 0x01b7)
-
- break;
- case NV_PBUS_PCI_NV_1:
- result = 1; // NV_PBUS_PCI_NV_1_IO_SPACE_ENABLED
- break;
- case NV_PBUS_PCI_NV_2:
- result = (0x02 << 24) | 161; // PCI_CLASS_DISPLAY_3D (0x02) Rev 161 (0xA1)
- break;
-
- default:
- DEBUG_READ32_UNHANDLED(PBUS); // TODO : DEVICE_READ32_REG(pbus);
- break;
- }
-
- DEVICE_READ32_END(PBUS);
-}
-
-DEVICE_WRITE32(PBUS)
-{
- switch(addr) {
- case NV_PBUS_PCI_NV_1:
- // TODO : Handle write on NV_PBUS_PCI_NV_1 with 1 (NV_PBUS_PCI_NV_1_IO_SPACE_ENABLED) + 4 (NV_PBUS_PCI_NV_1_BUS_MASTER_ENABLED)
- break;
- default:
- DEBUG_WRITE32_UNHANDLED(PBUS); // TODO : DEVICE_WRITE32_REG(pbus);
- break;
- }
-
- DEVICE_WRITE32_END(PBUS);
-}
-
-
-DEVICE_READ32(PFIFO)
-{
- DEVICE_READ32_SWITCH() {
- case NV_PFIFO_RAMHT:
- result = 0x03000100; // = NV_PFIFO_RAMHT_SIZE_4K | NV_PFIFO_RAMHT_BASE_ADDRESS(NumberOfPaddingBytes >> 12) | NV_PFIFO_RAMHT_SEARCH_128
- break;
- case NV_PFIFO_RAMFC:
- result = 0x00890110; // = ? | NV_PFIFO_RAMFC_SIZE_2K | ?
- break;
- case NV_PFIFO_INTR_0:
- result = pfifo.pending_interrupts;
- break;
- case NV_PFIFO_INTR_EN_0:
- result = pfifo.enabled_interrupts;
- break;
- case NV_PFIFO_RUNOUT_STATUS:
- result = NV_PFIFO_RUNOUT_STATUS_LOW_MARK; /* low mark empty */
- break;
- case NV_PFIFO_CACHE1_PUSH0:
- result = pfifo.cache1.push_enabled;
- break;
- case NV_PFIFO_CACHE1_PUSH1:
- SET_MASK(result, NV_PFIFO_CACHE1_PUSH1_CHID, pfifo.cache1.channel_id);
- SET_MASK(result, NV_PFIFO_CACHE1_PUSH1_MODE, pfifo.cache1.mode);
- break;
- case NV_PFIFO_CACHE1_STATUS: {
- std::lock_guard lk(pfifo.cache1.mutex);
-
- if (pfifo.cache1.cache.empty()) {
- result |= NV_PFIFO_CACHE1_STATUS_LOW_MARK; /* low mark empty */
- }
-
- } break;
- case NV_PFIFO_CACHE1_DMA_PUSH:
- SET_MASK(result, NV_PFIFO_CACHE1_DMA_PUSH_ACCESS,
- pfifo.cache1.dma_push_enabled);
- SET_MASK(result, NV_PFIFO_CACHE1_DMA_PUSH_STATUS,
- pfifo.cache1.dma_push_suspended);
- SET_MASK(result, NV_PFIFO_CACHE1_DMA_PUSH_BUFFER, 1); /* buffer emoty */
- break;
- case NV_PFIFO_CACHE1_DMA_STATE:
- SET_MASK(result, NV_PFIFO_CACHE1_DMA_STATE_METHOD_TYPE,
- pfifo.cache1.method_nonincreasing);
- SET_MASK(result, NV_PFIFO_CACHE1_DMA_STATE_METHOD,
- pfifo.cache1.method >> 2);
- SET_MASK(result, NV_PFIFO_CACHE1_DMA_STATE_SUBCHANNEL,
- pfifo.cache1.subchannel);
- SET_MASK(result, NV_PFIFO_CACHE1_DMA_STATE_METHOD_COUNT,
- pfifo.cache1.method_count);
- SET_MASK(result, NV_PFIFO_CACHE1_DMA_STATE_ERROR,
- pfifo.cache1.error);
- break;
- case NV_PFIFO_CACHE1_DMA_INSTANCE:
- SET_MASK(result, NV_PFIFO_CACHE1_DMA_INSTANCE_ADDRESS_MASK,
- pfifo.cache1.dma_instance >> 4);
- break;
- case NV_PFIFO_CACHE1_DMA_PUT:
- result = user.channel_control[pfifo.cache1.channel_id].dma_put;
- break;
- case NV_PFIFO_CACHE1_DMA_GET:
- result = user.channel_control[pfifo.cache1.channel_id].dma_get;
- break;
- case NV_PFIFO_CACHE1_DMA_SUBROUTINE:
- result = pfifo.cache1.subroutine_return
- | pfifo.cache1.subroutine_active;
- break;
- case NV_PFIFO_CACHE1_PULL0: {
- std::lock_guard lk(pfifo.cache1.mutex);
- result = pfifo.cache1.pull_enabled;
- } break;
- case NV_PFIFO_CACHE1_ENGINE: {
- std::lock_guard lk(pfifo.cache1.mutex);
- for (int i = 0; i < NV2A_NUM_SUBCHANNELS; i++) {
- result |= pfifo.cache1.bound_engines[i] << (i * 2);
- }
-
- } break;
- case NV_PFIFO_CACHE1_DMA_DCOUNT:
- result = pfifo.cache1.dcount;
- break;
- case NV_PFIFO_CACHE1_DMA_GET_JMP_SHADOW:
- result = pfifo.cache1.get_jmp_shadow;
- break;
- case NV_PFIFO_CACHE1_DMA_RSVD_SHADOW:
- result = pfifo.cache1.rsvd_shadow;
- break;
- case NV_PFIFO_CACHE1_DMA_DATA_SHADOW:
- result = pfifo.cache1.data_shadow;
- break;
- default:
- DEVICE_READ32_REG(pfifo); // Was : DEBUG_READ32_UNHANDLED(PFIFO);
- break;
- }
-
- DEVICE_READ32_END(PFIFO);
-}
-
-DEVICE_WRITE32(PFIFO)
-{
- switch(addr) {
- case NV_PFIFO_INTR_0:
- pfifo.pending_interrupts &= ~value;
- update_irq();
- break;
- case NV_PFIFO_INTR_EN_0:
- pfifo.enabled_interrupts = value;
- update_irq();
- break;
- case NV_PFIFO_CACHE1_PUSH0:
- pfifo.cache1.push_enabled = value & NV_PFIFO_CACHE1_PUSH0_ACCESS;
- break;
- case NV_PFIFO_CACHE1_PUSH1:
- pfifo.cache1.channel_id = GET_MASK(value, NV_PFIFO_CACHE1_PUSH1_CHID);
- pfifo.cache1.mode = (FifoMode)GET_MASK(value, NV_PFIFO_CACHE1_PUSH1_MODE);
- assert(pfifo.cache1.channel_id < NV2A_NUM_CHANNELS);
- break;
- case NV_PFIFO_CACHE1_DMA_PUSH:
- pfifo.cache1.dma_push_enabled = GET_MASK(value, NV_PFIFO_CACHE1_DMA_PUSH_ACCESS);
- if (pfifo.cache1.dma_push_suspended && !GET_MASK(value, NV_PFIFO_CACHE1_DMA_PUSH_STATUS)) {
- pfifo.cache1.dma_push_suspended = false;
- pfifo_run_pusher();
- }
- pfifo.cache1.dma_push_suspended = GET_MASK(value, NV_PFIFO_CACHE1_DMA_PUSH_STATUS);
- break;
- case NV_PFIFO_CACHE1_DMA_STATE:
- pfifo.cache1.method_nonincreasing = GET_MASK(value, NV_PFIFO_CACHE1_DMA_STATE_METHOD_TYPE);
- pfifo.cache1.method = GET_MASK(value, NV_PFIFO_CACHE1_DMA_STATE_METHOD) << 2;
- pfifo.cache1.subchannel = GET_MASK(value, NV_PFIFO_CACHE1_DMA_STATE_SUBCHANNEL);
- pfifo.cache1.method_count = GET_MASK(value, NV_PFIFO_CACHE1_DMA_STATE_METHOD_COUNT);
- pfifo.cache1.error = GET_MASK(value, NV_PFIFO_CACHE1_DMA_STATE_ERROR);
- break;
- case NV_PFIFO_CACHE1_DMA_INSTANCE:
- pfifo.cache1.dma_instance = GET_MASK(value, NV_PFIFO_CACHE1_DMA_INSTANCE_ADDRESS_MASK) << 4;
- break;
- case NV_PFIFO_CACHE1_DMA_PUT:
- user.channel_control[pfifo.cache1.channel_id].dma_put = value;
- break;
- case NV_PFIFO_CACHE1_DMA_GET:
- user.channel_control[pfifo.cache1.channel_id].dma_get = value;
- break;
- case NV_PFIFO_CACHE1_DMA_SUBROUTINE:
- pfifo.cache1.subroutine_return = (value & NV_PFIFO_CACHE1_DMA_SUBROUTINE_RETURN_OFFSET);
- pfifo.cache1.subroutine_active = (value & NV_PFIFO_CACHE1_DMA_SUBROUTINE_STATE);
- break;
- case NV_PFIFO_CACHE1_PULL0: {
- std::lock_guard lk(pfifo.cache1.mutex);
-
- if ((value & NV_PFIFO_CACHE1_PULL0_ACCESS)
- && !pfifo.cache1.pull_enabled) {
- pfifo.cache1.pull_enabled = true;
-
- /* the puller thread should wake up */
- pfifo.cache1.cache_cond.notify_all();
- } else if (!(value & NV_PFIFO_CACHE1_PULL0_ACCESS)
- && pfifo.cache1.pull_enabled) {
- pfifo.cache1.pull_enabled = false;
- }
- } break;
- case NV_PFIFO_CACHE1_ENGINE: {
- std::lock_guard lk(pfifo.cache1.mutex);
-
- for (int i = 0; i < NV2A_NUM_SUBCHANNELS; i++) {
- pfifo.cache1.bound_engines[i] = (FIFOEngine)((value >> (i * 2)) & 3);
- }
-
- } break;
- case NV_PFIFO_CACHE1_DMA_DCOUNT:
- pfifo.cache1.dcount = (value & NV_PFIFO_CACHE1_DMA_DCOUNT_VALUE);
- break;
- case NV_PFIFO_CACHE1_DMA_GET_JMP_SHADOW:
- pfifo.cache1.get_jmp_shadow = (value & NV_PFIFO_CACHE1_DMA_GET_JMP_SHADOW_OFFSET);
- break;
- case NV_PFIFO_CACHE1_DMA_RSVD_SHADOW:
- pfifo.cache1.rsvd_shadow = value;
- break;
- case NV_PFIFO_CACHE1_DMA_DATA_SHADOW:
- pfifo.cache1.data_shadow = value;
- break;
- default:
- DEVICE_WRITE32_REG(pfifo); // Was : DEBUG_WRITE32_UNHANDLED(PFIFO);
- break;
- }
-
- DEVICE_WRITE32_END(PFIFO);
-}
-
-DEVICE_READ32(PRMA)
-{
- DEVICE_READ32_SWITCH() {
- default:
- DEBUG_READ32_UNHANDLED(PRMA); // TODO : DEVICE_READ32_REG(prma);
- break;
- }
-
- DEVICE_READ32_END(PRMA);
-}
-
-DEVICE_WRITE32(PRMA)
-{
- switch(addr) {
- default:
- DEBUG_WRITE32_UNHANDLED(PRMA); // TODO : DEVICE_WRITE32_REG(prma);
- break;
- }
-
- DEVICE_WRITE32_END(PRMA);
-}
-
-
-DEVICE_READ32(PVIDEO)
-{
- DEVICE_READ32_SWITCH() {
-
- case NV_PVIDEO_STOP:
- result = 0;
- break;
- default:
- DEVICE_READ32_REG(pvideo);
- break;
- }
-
- DEVICE_READ32_END(PVIDEO);
-}
-
-DEVICE_WRITE32(PVIDEO)
-{
- switch (addr) {
- case NV_PVIDEO_BUFFER:
- pvideo.regs[addr] = value;
- // TODO: vga.enable_overlay = true;
- // pvideo_vga_invalidate(d);
- break;
- case NV_PVIDEO_STOP:
- pvideo.regs[NV_PVIDEO_BUFFER] = 0;
- // TODO: vga.enable_overlay = false;
- //pvideo_vga_invalidate(d);
- break;
- default:
- DEVICE_WRITE32_REG(pvideo);
- break;
- }
-
- DEVICE_WRITE32_END(PVIDEO);
-}
-
-DEVICE_READ32(PCOUNTER)
-{
- DEVICE_READ32_SWITCH() {
- default:
- DEBUG_READ32_UNHANDLED(PCOUNTER); // TODO : DEVICE_READ32_REG(pcounter);
- break;
- }
-
- DEVICE_READ32_END(PCOUNTER);
-}
-
-DEVICE_WRITE32(PCOUNTER)
-{
- switch (addr) {
- default:
- DEBUG_WRITE32_UNHANDLED(PCOUNTER); // TODO : DEVICE_WRITE32_REG(pcounter);
- break;
- }
-
- DEVICE_WRITE32_END(PCOUNTER);
-}
-
-static inline uint64_t muldiv64(uint64_t a, uint32_t b, uint32_t c)
-{
- union {
- uint64_t ll;
- struct {
- uint32_t low, high;
- } l;
- } u, res;
- uint64_t rl, rh;
-
- u.ll = a;
- rl = (uint64_t)u.l.low * (uint64_t)b;
- rh = (uint64_t)u.l.high * (uint64_t)b;
- rh += (rl >> 32);
- res.l.high = rh / c;
- res.l.low = (((rh % c) << 32) + (rl & 0xffffffff)) / c;
- return res.ll;
-}
-
-/* PIMTER - time measurement and time-based alarms */
-static uint32_t ptimer_get_clock()
-{
- // Get time in nanoseconds
- long int time = static_cast(std::chrono::duration_cast(std::chrono::high_resolution_clock::now().time_since_epoch()).count());
-
- return muldiv64(time, pramdac.core_clock_freq * ptimer.numerator, CLOCKS_PER_SEC * ptimer.denominator);
-}
-
-DEVICE_READ32(PTIMER)
-{
- DEVICE_READ32_SWITCH() {
- case NV_PTIMER_INTR_0:
- result = ptimer.pending_interrupts;
- break;
- case NV_PTIMER_INTR_EN_0:
- result = ptimer.enabled_interrupts;
- break;
- case NV_PTIMER_NUMERATOR:
- result = ptimer.numerator;
- break;
- case NV_PTIMER_DENOMINATOR:
- result = ptimer.denominator;
- break;
- case NV_PTIMER_TIME_0:
- result = (ptimer_get_clock() & 0x7ffffff) << 5;
- break;
- case NV_PTIMER_TIME_1:
- result = (ptimer_get_clock() >> 27) & 0x1fffffff;
- break;
- default:
- result = 0;
- //DEVICE_READ32_REG(ptimer); // Was : DEBUG_READ32_UNHANDLED(PTIMER);
- break;
- }
-
- DEVICE_READ32_END(PTIMER);
-}
-
-
-DEVICE_WRITE32(PTIMER)
-{
- switch (addr) {
-
- case NV_PTIMER_INTR_0:
- ptimer.pending_interrupts &= ~value;
- update_irq();
- break;
- case NV_PTIMER_INTR_EN_0:
- ptimer.enabled_interrupts = value;
- update_irq();
- break;
- case NV_PTIMER_DENOMINATOR:
- ptimer.denominator = value;
- break;
- case NV_PTIMER_NUMERATOR:
- ptimer.numerator = value;
- break;
- case NV_PTIMER_ALARM_0:
- ptimer.alarm_time = value;
- break;
- default:
- //DEVICE_WRITE32_REG(ptimer); // Was : DEBUG_WRITE32_UNHANDLED(PTIMER);
- break;
- }
-
- DEVICE_WRITE32_END(PTIMER);
-}
-
-
-DEVICE_READ32(PVPE)
-{
- DEVICE_READ32_SWITCH() {
- default:
- DEBUG_READ32_UNHANDLED(PVPE); // TODO : DEVICE_READ32_REG(pvpe);
- break;
- }
-
- DEVICE_READ32_END(PVPE);
-}
-
-
-DEVICE_WRITE32(PVPE)
-{
- switch (addr) {
- default:
- DEBUG_WRITE32_UNHANDLED(PVPE); // TODO : DEVICE_WRITE32_REG(pvpe);
- break;
- }
-
- DEVICE_WRITE32_END(PVPE);
-}
-
-
-DEVICE_READ32(PTV)
-{
- DEVICE_READ32_SWITCH() {
- default:
- DEBUG_READ32_UNHANDLED(PTV); // TODO : DEVICE_READ32_REG(ptv);
- break;
- }
-
- DEVICE_READ32_END(PTV);
-}
-
-DEVICE_WRITE32(PTV)
-{
- switch (addr) {
- default:
- DEBUG_WRITE32_UNHANDLED(PTV); // TODO : DEVICE_WRITE32_REG(ptv);
- break;
- }
-
- DEVICE_WRITE32_END(PTV);
-}
-
-
-DEVICE_READ32(PRMFB)
-{
- DEVICE_READ32_SWITCH() {
- default:
- DEBUG_READ32_UNHANDLED(PRMFB); // TODO : DEVICE_READ32_REG(prmfb);
- break;
- }
-
- DEVICE_READ32_END(PRMFB);
-}
-
-DEVICE_WRITE32(PRMFB)
-{
- switch (addr) {
- default:
- DEBUG_WRITE32_UNHANDLED(PRMFB); // TODO : DEVICE_WRITE32_REG(prmfb);
- break;
- }
-
- DEVICE_WRITE32_END(PRMFB);
-}
-
-
-DEVICE_READ32(PRMVIO)
-{
- DEVICE_READ32_SWITCH() {
- default:
- DEBUG_READ32_UNHANDLED(PRMVIO); // TODO : DEVICE_READ32_REG(prmvio);
- break;
- }
-
- DEVICE_READ32_END(PRMVIO);
-}
-
-DEVICE_WRITE32(PRMVIO)
-{
- switch (addr) {
- default:
- DEBUG_WRITE32_UNHANDLED(PRMVIO); // TODO : DEVICE_WRITE32_REG(prmvio);
- break;
- }
-
- DEVICE_WRITE32_END(PRMVIO);
-}
-
-
-DEVICE_READ32(PFB)
-{
- DEVICE_READ32_SWITCH() {
- case NV_PFB_CFG0:
- result = 3; // = NV_PFB_CFG0_PART_4
- break;
- case NV_PFB_CSTATUS:
- {
- if (g_bIsChihiro || g_bIsDebug) { result = CONTIGUOUS_MEMORY_CHIHIRO_SIZE; break; }
- result = CONTIGUOUS_MEMORY_XBOX_SIZE;
- }
- break;
- case NV_PFB_WBC:
- result = 0; // = !NV_PFB_WBC_FLUSH
- break;
- default:
- DEVICE_READ32_REG(pfb);
- break;
- }
-
- DEVICE_READ32_END(PFB);
-}
-
-DEVICE_WRITE32(PFB)
-{
- switch (addr) {
- default:
- DEVICE_WRITE32_REG(pfb);
- break;
- }
-
- DEVICE_WRITE32_END(PFB);
-}
-
-
-DEVICE_READ32(PSTRAPS)
-{
- DEVICE_READ32_SWITCH() {
- default:
- DEBUG_READ32_UNHANDLED(PSTRAPS);
- break;
- }
-
- DEVICE_READ32_END(PSTRAPS);
-}
-
-DEVICE_WRITE32(PSTRAPS)
-{
- switch (addr) {
- default:
- DEBUG_WRITE32_UNHANDLED(PSTRAPS);
- break;
- }
-
- DEVICE_WRITE32_END(PSTRAPS);
-}
-
-
-DEVICE_READ32(PGRAPH)
-{
- std::lock_guard lk(pgraph.mutex);
-
- DEVICE_READ32_SWITCH() {
- case NV_PGRAPH_INTR:
- result = pgraph.pending_interrupts;
- break;
- case NV_PGRAPH_INTR_EN:
- result = pgraph.enabled_interrupts;
- break;
- case NV_PGRAPH_NSOURCE:
- result = pgraph.notify_source;
- break;
- case NV_PGRAPH_CTX_USER:
- SET_MASK(result, NV_PGRAPH_CTX_USER_CHANNEL_3D, pgraph.context[pgraph.channel_id].channel_3d);
- SET_MASK(result, NV_PGRAPH_CTX_USER_CHANNEL_3D_VALID, 1);
- SET_MASK(result, NV_PGRAPH_CTX_USER_SUBCH, pgraph.context[pgraph.channel_id].subchannel << 13);
- SET_MASK(result, NV_PGRAPH_CTX_USER_CHID, pgraph.channel_id);
- break;
- case NV_PGRAPH_TRAPPED_ADDR:
- SET_MASK(result, NV_PGRAPH_TRAPPED_ADDR_CHID, pgraph.trapped_channel_id);
- SET_MASK(result, NV_PGRAPH_TRAPPED_ADDR_SUBCH, pgraph.trapped_subchannel);
- SET_MASK(result, NV_PGRAPH_TRAPPED_ADDR_MTHD, pgraph.trapped_method);
- break;
- case NV_PGRAPH_TRAPPED_DATA_LOW:
- result = pgraph.trapped_data[0];
- break;
- case NV_PGRAPH_FIFO:
- SET_MASK(result, NV_PGRAPH_FIFO_ACCESS, pgraph.fifo_access);
- break;
- case NV_PGRAPH_CHANNEL_CTX_TABLE:
- result = pgraph.context_table >> 4;
- break;
- case NV_PGRAPH_CHANNEL_CTX_POINTER:
- result = pgraph.context_address >> 4;
- break;
- default:
- DEVICE_READ32_REG(pgraph); // Was : DEBUG_READ32_UNHANDLED(PGRAPH);
- }
-
- DEVICE_READ32_END(PGRAPH);
-}
-
-static void pgraph_set_context_user(uint32_t value)
-{
- pgraph.channel_id = (value & NV_PGRAPH_CTX_USER_CHID) >> 24;
- pgraph.context[pgraph.channel_id].channel_3d = GET_MASK(value, NV_PGRAPH_CTX_USER_CHANNEL_3D);
- pgraph.context[pgraph.channel_id].subchannel = GET_MASK(value, NV_PGRAPH_CTX_USER_SUBCH);
-}
-
-DEVICE_WRITE32(PGRAPH)
-{
- std::lock_guard lk(pgraph.mutex);
-
- switch (addr) {
- case NV_PGRAPH_INTR:
- pgraph.pending_interrupts &= ~value;
- pgraph.interrupt_cond.notify_all();
- break;
- case NV_PGRAPH_INTR_EN:
- pgraph.enabled_interrupts = value;
- break;
- case NV_PGRAPH_CTX_CONTROL:
- pgraph.channel_valid = (value & NV_PGRAPH_CTX_CONTROL_CHID);
- break;
- case NV_PGRAPH_CTX_USER:
- pgraph_set_context_user(value);
- break;
- case NV_PGRAPH_INCREMENT:
- if (value & NV_PGRAPH_INCREMENT_READ_3D) {
- SET_MASK(pgraph.regs[NV_PGRAPH_SURFACE],
- NV_PGRAPH_SURFACE_READ_3D,
- (GET_MASK(pgraph.regs[NV_PGRAPH_SURFACE],
- NV_PGRAPH_SURFACE_READ_3D) + 1)
- % GET_MASK(pgraph.regs[NV_PGRAPH_SURFACE],
- NV_PGRAPH_SURFACE_MODULO_3D));
-
- pgraph.flip_3d.notify_all();
- }
- break;
- case NV_PGRAPH_FIFO:
- pgraph.fifo_access = GET_MASK(value, NV_PGRAPH_FIFO_ACCESS);
- pgraph.fifo_access_cond.notify_all();
- break;
- case NV_PGRAPH_CHANNEL_CTX_TABLE:
- pgraph.context_table = (value & NV_PGRAPH_CHANNEL_CTX_TABLE_INST) << 4;
- break;
- case NV_PGRAPH_CHANNEL_CTX_POINTER:
- pgraph.context_address =
- (value & NV_PGRAPH_CHANNEL_CTX_POINTER_INST) << 4;
- break;
- case NV_PGRAPH_CHANNEL_CTX_TRIGGER:
- if (value & NV_PGRAPH_CHANNEL_CTX_TRIGGER_READ_IN) {
- printf("PGRAPH: read channel %d context from %0x08X\n",
- pgraph.channel_id, pgraph.context_address);
-
- uint8_t *context_ptr = (uint8_t*)(NV2A_ADDR + NV_PRAMIN_ADDR + pgraph.context_address);
- uint32_t context_user = ldl_le_p((uint32_t*)context_ptr);
-
- printf(" - CTX_USER = 0x%x\n", context_user);
-
-
- pgraph_set_context_user(context_user);
- }
- if (value & NV_PGRAPH_CHANNEL_CTX_TRIGGER_WRITE_OUT) {
- /* do stuff ... */
- }
-
- break;
- default:
- DEVICE_WRITE32_REG(pgraph); // Was : DEBUG_WRITE32_UNHANDLED(PGRAPH);
- break;
- }
-
- DEVICE_WRITE32_END(PGRAPH);
-}
-
-
-DEVICE_READ32(PCRTC)
-{
- DEVICE_READ32_SWITCH() {
-
- case NV_PCRTC_INTR_0:
- result = pcrtc.pending_interrupts;
- break;
- case NV_PCRTC_INTR_EN_0:
- result = pcrtc.enabled_interrupts;
- break;
- case NV_PCRTC_START:
- result = pcrtc.start;
- break;
- default:
- result = 0;
- //DEVICE_READ32_REG(pcrtc); // Was : DEBUG_READ32_UNHANDLED(PCRTC);
- break;
- }
-
- DEVICE_READ32_END(PCRTC);
-}
-
-DEVICE_WRITE32(PCRTC)
-{
- switch (addr) {
-
- case NV_PCRTC_INTR_0:
- pcrtc.pending_interrupts &= ~value;
- update_irq();
- break;
- case NV_PCRTC_INTR_EN_0:
- pcrtc.enabled_interrupts = value;
- update_irq();
- break;
- case NV_PCRTC_START:
- pcrtc.start = value &= 0x07FFFFFF;
- break;
-
- default:
- DEVICE_WRITE32_REG(pcrtc); // Was : DEBUG_WRITE32_UNHANDLED(PCRTC);
- break;
- }
-
- DEVICE_WRITE32_END(PCRTC);
-}
-
-
-DEVICE_READ32(PRMCIO)
-{
- DEVICE_READ32_SWITCH() {
- case VGA_CRT_IM:
- case VGA_CRT_IC:
- result = prmcio.cr_index;
- break;
- case VGA_CRT_DM:
- case VGA_CRT_DC:
- result = prmcio.cr[prmcio.cr_index];
-
- printf("vga: read CR%x = 0x%02x\n", prmcio.cr_index, result);
- break;
- default:
- DEBUG_READ32_UNHANDLED(PRMCIO);
- printf("vga: UNHANDLED ADDR %s\n", addr);
- break;
- }
-
- DEVICE_READ32_END(PRMCIO);
-}
-
-DEVICE_WRITE32(PRMCIO)
-{
- switch (addr) {
- case VGA_CRT_IM:
- case VGA_CRT_IC:
- prmcio.cr_index = value;
- break;
- case VGA_CRT_DM:
- case VGA_CRT_DC:
- printf("vga: write CR%x = 0x%02x\n", prmcio.cr_index, value);
-
- /* handle CR0-7 protection */
- if ((prmcio.cr[VGA_CRTC_V_SYNC_END] & VGA_CR11_LOCK_CR0_CR7) &&
- prmcio.cr_index <= VGA_CRTC_OVERFLOW) {
- /* can always write bit 4 of CR7 */
- if (prmcio.cr_index == VGA_CRTC_OVERFLOW) {
- prmcio.cr[VGA_CRTC_OVERFLOW] = (prmcio.cr[VGA_CRTC_OVERFLOW] & ~0x10) |
- (value & 0x10);
- EmuWarning("TODO: vbe_update_vgaregs");
- //vbe_update_vgaregs();
- }
- return;
- }
-
- prmcio.cr[prmcio.cr_index] = value;
- EmuWarning("TODO: vbe_update_vgaregs");
- //vbe_update_vgaregs();
-
- switch (prmcio.cr_index) {
- case VGA_CRTC_H_TOTAL:
- case VGA_CRTC_H_SYNC_START:
- case VGA_CRTC_H_SYNC_END:
- case VGA_CRTC_V_TOTAL:
- case VGA_CRTC_OVERFLOW:
- case VGA_CRTC_V_SYNC_END:
- case VGA_CRTC_MODE:
- // TODO: s->update_retrace_info(s);
- EmuWarning("TODO: update_retrace_info");
- break;
- }
- break;
- default:
- DEBUG_WRITE32_UNHANDLED(PRMCIO); // TODO : DEVICE_WRITE32_REG(prmcio);
- break;
- }
-
- DEVICE_WRITE32_END(PRMCIO);
-}
-
-
-DEVICE_READ32(PRAMDAC)
-{
- DEVICE_READ32_SWITCH() {
-
- case NV_PRAMDAC_NVPLL_COEFF:
- result = pramdac.core_clock_coeff;
- break;
- case NV_PRAMDAC_MPLL_COEFF:
- result = pramdac.memory_clock_coeff;
- break;
- case NV_PRAMDAC_VPLL_COEFF:
- result = pramdac.video_clock_coeff;
- break;
- case NV_PRAMDAC_PLL_TEST_COUNTER:
- /* emulated PLLs locked instantly? */
- result = NV_PRAMDAC_PLL_TEST_COUNTER_VPLL2_LOCK
- | NV_PRAMDAC_PLL_TEST_COUNTER_NVPLL_LOCK
- | NV_PRAMDAC_PLL_TEST_COUNTER_MPLL_LOCK
- | NV_PRAMDAC_PLL_TEST_COUNTER_VPLL_LOCK;
- break;
-
- default:
- //DEVICE_READ32_REG(pramdac); // Was : DEBUG_READ32_UNHANDLED(PRAMDAC);
- break;
- }
-
- DEVICE_READ32_END(PRAMDAC);
-}
-
-DEVICE_WRITE32(PRAMDAC)
-{
- switch (addr) {
-
- uint32_t m, n, p;
- case NV_PRAMDAC_NVPLL_COEFF:
- pramdac.core_clock_coeff = value;
-
- m = value & NV_PRAMDAC_NVPLL_COEFF_MDIV;
- n = (value & NV_PRAMDAC_NVPLL_COEFF_NDIV) >> 8;
- p = (value & NV_PRAMDAC_NVPLL_COEFF_PDIV) >> 16;
-
- if (m == 0) {
- pramdac.core_clock_freq = 0;
- }
- else {
- pramdac.core_clock_freq = (NV2A_CRYSTAL_FREQ * n)
- / (1 << p) / m;
- }
-
- break;
- case NV_PRAMDAC_MPLL_COEFF:
- pramdac.memory_clock_coeff = value;
- break;
- case NV_PRAMDAC_VPLL_COEFF:
- pramdac.video_clock_coeff = value;
- break;
-
- default:
- //DEVICE_WRITE32_REG(pramdac); // Was : DEBUG_WRITE32_UNHANDLED(PRAMDAC);
- break;
- }
-
- DEVICE_WRITE32_END(PRAMDAC);
-}
-
-
-DEVICE_READ32(PRMDIO)
-{
- DEVICE_READ32_SWITCH() {
- default:
- DEBUG_READ32_UNHANDLED(PRMDIO);
- break;
- }
-
- DEVICE_READ32_END(PRMDIO);
-}
-
-DEVICE_WRITE32(PRMDIO)
-{
- switch (addr) {
- default:
- DEBUG_WRITE32_UNHANDLED(PRMDIO);
- break;
- }
-
- DEVICE_WRITE32_END(PRMDIO);
-}
-
-
-DEVICE_READ32(PRAMIN)
-{
- DEVICE_READ32_SWITCH() {
- default:
- DEVICE_READ32_REG(pramin);
- break;
- }
-
- DEVICE_READ32_END(PRAMIN);
-}
-
-DEVICE_WRITE32(PRAMIN)
-{
- switch (addr) {
- default:
- DEVICE_WRITE32_REG(pramin);
- break;
- }
-
- DEVICE_WRITE32_END(PRAMIN);
-}
-
-
-DEVICE_READ32(USER)
-{
- unsigned int channel_id = addr >> 16;
- assert(channel_id < NV2A_NUM_CHANNELS);
-
- ChannelControl *control = &user.channel_control[channel_id];
- uint32_t channel_modes = pfifo.regs[NV_PFIFO_MODE];
-
- /* PIO Mode */
- if (!channel_modes & (1 << channel_id)) {
- assert(false);
- }
-
- /* DMA Mode */
- addr &= 0xFFFF;
- DEVICE_READ32_SWITCH() {
- case NV_USER_DMA_PUT:
- result = control->dma_put;
- break;
- case NV_USER_DMA_GET:
- result = control->dma_get;
- break;
- case NV_USER_REF:
- result = control->ref;
- break;
- default:
- DEBUG_READ32_UNHANDLED(USER);
- break;
- }
-
- DEVICE_READ32_END(USER);
-}
-
-DEVICE_WRITE32(USER)
-{
- unsigned int channel_id = addr >> 16;
- assert(channel_id < NV2A_NUM_CHANNELS);
-
- ChannelControl *control = &user.channel_control[channel_id];
-
- uint32_t channel_modes = pfifo.regs[NV_PFIFO_MODE];
-
- if (channel_modes & (1 << channel_id)) {
- /* DMA Mode */
- switch (addr & 0xFFFF) {
- case NV_USER_DMA_PUT:
- control->dma_put = value;
-
- if (pfifo.cache1.push_enabled) {
- pfifo_run_pusher();
- }
- break;
- case NV_USER_DMA_GET:
- control->dma_get = value;
- break;
- case NV_USER_REF:
- control->ref = value;
- break;
- default:
- DEBUG_WRITE32_UNHANDLED(USER);
- break;
- }
- }
- else {
- /* PIO Mode */
- assert(false);
- }
-
- DEVICE_WRITE32_END(USER);
-}
-
-
-typedef struct NV2ABlockInfo {
- uint32_t offset;
- uint32_t size;
- uint32_t(*read)(xbaddr addr);
- void(*write)(xbaddr addr, uint32_t value);
-} NV2ABlockInfo;
-
-static const NV2ABlockInfo regions[] = {{
- /* card master control */
- NV_PMC_ADDR, // = 0x000000
- NV_PMC_SIZE, // = 0x001000
- EmuNV2A_PMC_Read32,
- EmuNV2A_PMC_Write32,
- }, {
- /* bus control */
- NV_PBUS_ADDR, // = 0x001000
- NV_PBUS_SIZE, // = 0x001000
- EmuNV2A_PBUS_Read32,
- EmuNV2A_PBUS_Write32,
- }, {
- /* MMIO and DMA FIFO submission to PGRAPH and VPE */
- NV_PFIFO_ADDR, // = 0x002000
- _NV_PFIFO_SIZE, // = 0x002000
- EmuNV2A_PFIFO_Read32,
- EmuNV2A_PFIFO_Write32,
- }, {
- /* access to BAR0/BAR1 from real mode */
- NV_PRMA_ADDR, // = 0x007000
- NV_PRMA_SIZE, // = 0x001000
- EmuNV2A_PRMA_Read32,
- EmuNV2A_PRMA_Write32,
- }, {
- /* video overlay */
- NV_PVIDEO_ADDR, // = 0x008000
- NV_PVIDEO_SIZE, // = 0x001000
- EmuNV2A_PVIDEO_Read32,
- EmuNV2A_PVIDEO_Write32,
- }, {
- /* time measurement and time-based alarms */
- NV_PTIMER_ADDR, // = 0x009000
- NV_PTIMER_SIZE, // = 0x001000
- EmuNV2A_PTIMER_Read32,
- EmuNV2A_PTIMER_Write32,
- }, {
- /* performance monitoring counters */
- NV_PCOUNTER_ADDR, // = 0x00a000
- NV_PCOUNTER_SIZE, // = 0x001000
- EmuNV2A_PCOUNTER_Read32,
- EmuNV2A_PCOUNTER_Write32,
- }, {
- /* MPEG2 decoding engine */
- NV_PVPE_ADDR, // = 0x00b000
- NV_PVPE_SIZE, // = 0x001000
- EmuNV2A_PVPE_Read32,
- EmuNV2A_PVPE_Write32,
- }, {
- /* TV encoder */
- NV_PTV_ADDR, // = 0x00d000
- NV_PTV_SIZE, // = 0x001000
- EmuNV2A_PTV_Read32,
- EmuNV2A_PTV_Write32,
- }, {
- /* aliases VGA memory window */
- NV_PRMFB_ADDR, // = 0x0a0000
- NV_PRMFB_SIZE, // = 0x020000
- EmuNV2A_PRMFB_Read32,
- EmuNV2A_PRMFB_Write32,
- }, {
- /* aliases VGA sequencer and graphics controller registers */
- NV_PRMVIO_ADDR, // = 0x0c0000
- NV_PRMVIO_SIZE, // = 0x008000 // Was 0x001000
- EmuNV2A_PRMVIO_Read32,
- EmuNV2A_PRMVIO_Write32,
- },{
- /* memory interface */
- NV_PFB_ADDR, // = 0x100000
- NV_PFB_SIZE, // = 0x001000
- EmuNV2A_PFB_Read32,
- EmuNV2A_PFB_Write32,
- }, {
- /* straps readout / override */
- NV_PSTRAPS_ADDR, // = 0x101000
- NV_PSTRAPS_SIZE, // = 0x001000
- EmuNV2A_PSTRAPS_Read32,
- EmuNV2A_PSTRAPS_Write32,
- }, {
- /* accelerated 2d/3d drawing engine */
- NV_PGRAPH_ADDR, // = 0x400000
- NV_PGRAPH_SIZE, // = 0x002000
- EmuNV2A_PGRAPH_Read32,
- EmuNV2A_PGRAPH_Write32,
- }, {
- /* more CRTC controls */
- NV_PCRTC_ADDR, // = 0x600000
- NV_PCRTC_SIZE, // = 0x001000
- EmuNV2A_PCRTC_Read32,
- EmuNV2A_PCRTC_Write32,
- }, {
- /* aliases VGA CRTC and attribute controller registers */
- NV_PRMCIO_ADDR, // = 0x601000
- NV_PRMCIO_SIZE, // = 0x001000
- EmuNV2A_PRMCIO_Read32,
- EmuNV2A_PRMCIO_Write32,
- }, {
- /* RAMDAC, cursor, and PLL control */
- NV_PRAMDAC_ADDR, // = 0x680000
- NV_PRAMDAC_SIZE, // = 0x001000
- EmuNV2A_PRAMDAC_Read32,
- EmuNV2A_PRAMDAC_Write32,
- }, {
- /* aliases VGA palette registers */
- NV_PRMDIO_ADDR, // = 0x681000
- NV_PRMDIO_SIZE, // = 0x001000
- EmuNV2A_PRMDIO_Read32,
- EmuNV2A_PRMDIO_Write32,
- }, {
- /* RAMIN access */
- NV_PRAMIN_ADDR, // = 0x700000
- NV_PRAMIN_SIZE, // = 0x100000
- EmuNV2A_PRAMIN_Read32,
- EmuNV2A_PRAMIN_Write32,
- },{
- /* PFIFO MMIO and DMA submission area */
- NV_USER_ADDR, // = 0x800000
- NV_USER_SIZE, // = 0x400000 // Was 0x800000
- EmuNV2A_USER_Read32,
- EmuNV2A_USER_Write32,
- }, {
- /* User area remapped? */
- NV_UREMAP_ADDR, // = 0xC00000
- NV_UREMAP_SIZE, // = 0x400000
- EmuNV2A_USER_Read32, // NOTE : Re-used (*not* EmuNV2A_UREMAP_Read32)
- EmuNV2A_USER_Write32, // NOTE : Re-used (*not* EmuNV2A_UREMAP_Write32)
- }, {
- 0xFFFFFFFF,
- 0,
- nullptr,
- nullptr,
- },
+#include "EmuNV2A_PBUS.cpp"
+#include "EmuNV2A_PCRTC.cpp"
+#include "EmuNV2A_PFB.cpp"
+#include "EmuNV2A_PGRAPH.cpp"
+#include "EmuNV2A_PFIFO.cpp"
+#include "EmuNV2A_PMC.cpp"
+#include "EmuNV2A_PRAMDAC.cpp"
+#include "EmuNV2A_PRMCIO.cpp"
+#include "EmuNV2A_PRMVIO.cpp"
+#include "EmuNV2A_PTIMER.cpp"
+#include "EmuNV2A_PVIDEO.cpp"
+#include "EmuNV2A_USER.cpp"
+
+#include "EmuNV2A_PRMA.cpp"
+#include "EmuNV2A_PCOUNTER.cpp"
+#include "EmuNV2A_PVPE.cpp"
+#include "EmuNV2A_PTV.cpp"
+#include "EmuNV2A_PRMFB.cpp"
+#include "EmuNV2A_PSTRAPS.cpp"
+#include "EmuNV2A_PRMDIO.cpp"
+#include "EmuNV2A_PRAMIN.cpp"
+
+const NV2ABlockInfo regions[] = { // blocktable
+
+// Note : Avoid designated initializers to facilitate C++ builds
+#define ENTRY(OFFSET, SIZE, NAME, RDFUNC, WRFUNC) \
+ { \
+ #NAME, OFFSET, SIZE, \
+ { RDFUNC, WRFUNC }, \
+ }, \
+
+ /* card master control */
+ ENTRY(0x000000, 0x001000, PMC, EmuNV2A_PMC_Read32, EmuNV2A_PMC_Write32)
+ /* bus control */
+ ENTRY(0x001000, 0x001000, PBUS, EmuNV2A_PBUS_Read32, EmuNV2A_PBUS_Write32)
+ /* MMIO and DMA FIFO submission to PGRAPH and VPE */
+ ENTRY(0x002000, 0x002000, PFIFO, EmuNV2A_PFIFO_Read32, EmuNV2A_PFIFO_Write32)
+ /* access to BAR0/BAR1 from real mode */
+ ENTRY(0x007000, 0x001000, PRMA, EmuNV2A_PRMA_Read32, EmuNV2A_PRMA_Write32)
+ /* video overlay */
+ ENTRY(0x008000, 0x001000, PVIDEO, EmuNV2A_PVIDEO_Read32, EmuNV2A_PVIDEO_Write32)
+ /* time measurement and time-based alarms */
+ ENTRY(0x009000, 0x001000, PTIMER, EmuNV2A_PTIMER_Read32, EmuNV2A_PTIMER_Write32)
+ /* performance monitoring counters */
+ ENTRY(0x00a000, 0x001000, PCOUNTER, EmuNV2A_PCOUNTER_Read32, EmuNV2A_PCOUNTER_Write32)
+ /* MPEG2 decoding engine */
+ ENTRY(0x00b000, 0x001000, PVPE, EmuNV2A_PVPE_Read32, EmuNV2A_PVPE_Write32)
+ /* TV encoder */
+ ENTRY(0x00d000, 0x001000, PTV, EmuNV2A_PTV_Read32, EmuNV2A_PTV_Write32)
+ /* aliases VGA memory window */
+ ENTRY(0x0a0000, 0x020000, PRMFB, EmuNV2A_PRMFB_Read32, EmuNV2A_PRMFB_Write32)
+ /* aliases VGA sequencer and graphics controller registers */
+ ENTRY(0x0c0000, 0x008000, PRMVIO, EmuNV2A_PRMVIO_Read32, EmuNV2A_PRMVIO_Write32) // Size was 0x001000
+ /* memory interface */
+ ENTRY(0x100000, 0x001000, PFB, EmuNV2A_PFB_Read32, EmuNV2A_PFB_Write32)
+ /* straps readout / override */
+ ENTRY(0x101000, 0x001000, PSTRAPS, EmuNV2A_PSTRAPS_Read32, EmuNV2A_PSTRAPS_Write32)
+ /* accelerated 2d/3d drawing engine */
+ ENTRY(0x400000, 0x002000, PGRAPH, EmuNV2A_PGRAPH_Read32, EmuNV2A_PGRAPH_Write32)
+ /* more CRTC controls */
+ ENTRY(0x600000, 0x001000, PCRTC, EmuNV2A_PCRTC_Read32, EmuNV2A_PCRTC_Write32)
+ /* aliases VGA CRTC and attribute controller registers */
+ ENTRY(0x601000, 0x001000, PRMCIO, EmuNV2A_PRMCIO_Read32, EmuNV2A_PRMCIO_Write32)
+ /* RAMDAC, cursor, and PLL control */
+ ENTRY(0x680000, 0x001000, PRAMDAC, EmuNV2A_PRAMDAC_Read32, EmuNV2A_PRAMDAC_Write32)
+ /* aliases VGA palette registers */
+ ENTRY(0x681000, 0x001000, PRMDIO, EmuNV2A_PRMDIO_Read32, EmuNV2A_PRMDIO_Write32)
+ /* RAMIN access */
+ ENTRY(0x700000, 0x100000, PRAMIN, EmuNV2A_PRAMIN_Read32, EmuNV2A_PRAMIN_Write32)
+ /* PFIFO MMIO and DMA submission area */
+ ENTRY(0x800000, 0x400000, USER, EmuNV2A_USER_Read32, EmuNV2A_USER_Write32) // Size was 0x800000
+ /* UREMAP User area mirror - TODO : Confirm */
+ ENTRY(0xC00000, 0x400000, UREMAP, EmuNV2A_USER_Read32, EmuNV2A_USER_Write32) // NOTE : Mirror of USER
+ // Terminating entry
+ ENTRY(0xFFFFFF, 0x000000, END, nullptr, nullptr)
+#undef ENTRY
};
const NV2ABlockInfo* EmuNV2A_Block(xbaddr addr)
@@ -3544,7 +274,7 @@ const NV2ABlockInfo* EmuNV2A_Block(xbaddr addr)
const NV2ABlockInfo* block = ®ions[0];
int i = 0;
- while (block->read != nullptr) {
+ while (block->size > 0) {
if (addr >= block->offset && addr < block->offset + block->size) {
return block;
}
@@ -3555,6 +285,9 @@ const NV2ABlockInfo* EmuNV2A_Block(xbaddr addr)
return nullptr;
}
+NV2AState g_nv2a_state;
+NV2AState *d = &g_nv2a_state;
+
uint32_t EmuNV2A_Read(xbaddr addr, int size)
{
const NV2ABlockInfo* block = EmuNV2A_Block(addr);
@@ -3562,11 +295,11 @@ uint32_t EmuNV2A_Read(xbaddr addr, int size)
if (block != nullptr) {
switch (size) {
case sizeof(uint8_t):
- return block->read(addr - block->offset) & 0xFF;
+ return block->ops.read(d, addr - block->offset) & 0xFF;
case sizeof(uint16_t) :
- return block->read(addr - block->offset) & 0xFFFF;
+ return block->ops.read(d, addr - block->offset) & 0xFFFF;
case sizeof(uint32_t) :
- return block->read(addr - block->offset);
+ return block->ops.read(d, addr - block->offset);
default:
EmuWarning("EmuNV2A_Read: Invalid read size: %d", size);
return 0;
@@ -3590,25 +323,25 @@ void EmuNV2A_Write(xbaddr addr, uint32_t value, int size)
case sizeof(uint8_t) :
shift = (addr & 3) * 8;
aligned_addr = addr & ~3;
- aligned_value = block->read(aligned_addr - block->offset);
+ aligned_value = block->ops.read(d, aligned_addr - block->offset);
mask = 0xFF << shift;
// TODO : Must the second byte be written to the next DWORD?
- block->write(aligned_addr - block->offset, (aligned_value & ~mask) | (value << shift));
+ block->ops.write(d, aligned_addr - block->offset, (aligned_value & ~mask) | (value << shift));
return;
case sizeof(uint16_t) :
assert((addr & 1) == 0);
shift = (addr & 2) * 16;
aligned_addr = addr & ~3;
- aligned_value = block->read(addr - block->offset);
+ aligned_value = block->ops.read(d, addr - block->offset);
mask = 0xFFFF << shift;
// TODO : Must the second byte be written to the next DWORD?
- block->write(aligned_addr - block->offset, (aligned_value & ~mask) | (value << shift));
+ block->ops.write(d, aligned_addr - block->offset, (aligned_value & ~mask) | (value << shift));
return;
case sizeof(uint32_t) :
- block->write(addr - block->offset, value);
+ block->ops.write(d, addr - block->offset, value);
return;
default:
EmuWarning("EmuNV2A_Read: Invalid read size: %d", size);
@@ -3893,7 +626,7 @@ void InitOpenGLContext()
// we simulate VBLANK by calling the interrupt at 60Hz
std::thread vblank_thread;
extern std::chrono::time_point> GetNextVBlankTime();
-static void nv2a_vblank_thread()
+static void nv2a_vblank_thread(NV2AState *d)
{
CxbxSetThreadName("Cxbx NV2A VBlank");
@@ -3902,14 +635,18 @@ static void nv2a_vblank_thread()
while (true) {
// Handle VBlank
if (std::chrono::steady_clock::now() > nextVBlankTime) {
- pcrtc.pending_interrupts |= NV_PCRTC_INTR_0_VBLANK;
- update_irq();
+ d->pcrtc.pending_interrupts |= NV_PCRTC_INTR_0_VBLANK;
+ update_irq(d);
nextVBlankTime = GetNextVBlankTime();
}
}
}
-void CxbxReserveNV2AMemory()
+// See NV2ABlockInfo regions[] PRAMIN
+#define NV_PRAMIN_ADDR 0x00700000
+#define NV_PRAMIN_SIZE 0x100000
+
+void CxbxReserveNV2AMemory(NV2AState *d)
{
// Reserve NV2A memory :
void *memory = (void *)VirtualAllocEx(
@@ -3927,36 +664,40 @@ void CxbxReserveNV2AMemory()
GetCurrentThreadId(), NV2A_SIZE / ONE_MB, NV2A_ADDR, NV2A_ADDR + NV2A_SIZE - 1);
// Allocate PRAMIN Region
- memory = VirtualAllocEx(
+ d->pramin.ramin_size = NV_PRAMIN_SIZE;
+ d->pramin.ramin_ptr = (uint8_t*)VirtualAllocEx(
GetCurrentProcess(),
(void*)(NV2A_ADDR + NV_PRAMIN_ADDR),
- NV_PRAMIN_SIZE,
+ d->pramin.ramin_size,
MEM_COMMIT, // No MEM_RESERVE |
PAGE_READWRITE);
- if (memory == NULL) {
+ if (d->pramin.ramin_ptr == NULL) {
EmuWarning("Couldn't allocate NV2A PRAMIN memory");
return;
}
printf("[0x%.4X] INIT: Allocated %d MiB of Xbox NV2A PRAMIN memory at 0x%.8X to 0x%.8X\n",
- GetCurrentThreadId(), NV_PRAMIN_SIZE / ONE_MB, NV2A_ADDR + NV_PRAMIN_ADDR, NV2A_ADDR + NV_PRAMIN_ADDR + NV_PRAMIN_SIZE - 1);
+ GetCurrentThreadId(), d->pramin.ramin_size / ONE_MB, d->pramin.ramin_ptr, d->pramin.ramin_ptr + d->pramin.ramin_size - 1);
}
void EmuNV2A_Init()
{
- CxbxReserveNV2AMemory();
+ CxbxReserveNV2AMemory(d);
- pcrtc.start = 0;
+ d->pcrtc.start = 0;
- pramdac.core_clock_coeff = 0x00011c01; /* 189MHz...? */
- pramdac.core_clock_freq = 189000000;
- pramdac.memory_clock_coeff = 0;
- pramdac.video_clock_coeff = 0x0003C20D; /* 25182Khz...? */
+ d->vram_ptr = (uint8_t*)MM_SYSTEM_PHYSICAL_MAP;
+ d->vram_size = (g_bIsChihiro || g_bIsDebug) ? CONTIGUOUS_MEMORY_CHIHIRO_SIZE : CONTIGUOUS_MEMORY_XBOX_SIZE;
- pfifo.puller_thread = std::thread(pfifo_puller_thread);
+ d->pramdac.core_clock_coeff = 0x00011c01; /* 189MHz...? */
+ d->pramdac.core_clock_freq = 189000000;
+ d->pramdac.memory_clock_coeff = 0;
+ d->pramdac.video_clock_coeff = 0x0003C20D; /* 25182Khz...? */
+
+ d->pfifo.puller_thread = std::thread(pfifo_puller_thread, d);
// Only spawn VBlank thread when LLE is enabled
if (bLLE_GPU) {
- vblank_thread = std::thread(nv2a_vblank_thread);
+ vblank_thread = std::thread(nv2a_vblank_thread, d);
}
}
diff --git a/src/devices/video/EmuNV2A.h b/src/devices/video/EmuNV2A.h
deleted file mode 100644
index 7e3f6cc10..000000000
--- a/src/devices/video/EmuNV2A.h
+++ /dev/null
@@ -1,145 +0,0 @@
-// ******************************************************************
-// *
-// * .,-::::: .,:: .::::::::. .,:: .:
-// * ,;;;'````' `;;;, .,;; ;;;'';;' `;;;, .,;;
-// * [[[ '[[,,[[' [[[__[[\. '[[,,[['
-// * $$$ Y$$$P $$""""Y$$ Y$$$P
-// * `88bo,__,o, oP"``"Yo, _88o,,od8P oP"``"Yo,
-// * "YUMMMMMP",m" "Mm,""YUMMMP" ,m" "Mm,
-// *
-// * Cxbx->Win32->CxbxKrnl->EmuX86.h
-// *
-// * This file is part of the Cxbx project.
-// *
-// * Cxbx and Cxbe are free software; you can redistribute them
-// * and/or modify them under the terms of the GNU General Public
-// * License as published by the Free Software Foundation; either
-// * version 2 of the license, or (at your option) any later version.
-// *
-// * This program is distributed in the hope that it will be useful,
-// * but WITHOUT ANY WARRANTY; without even the implied warranty of
-// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// * GNU General Public License for more details.
-// *
-// * You should have recieved a copy of the GNU General Public License
-// * along with this program; see the file COPYING.
-// * If not, write to the Free Software Foundation, Inc.,
-// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA.
-// *
-// * (c) 2002-2003 Aaron Robinson
-// * (c) 2016 Luke Usher
-// * All rights reserved
-// *
-// ******************************************************************
-#ifndef EMUNV2A_H
-#define EMUNV2A_H
-
-// Valid after PCI init :
-#define NV20_REG_BASE_KERNEL 0xFD000000
-
-#define NV2A_ADDR 0xFD000000
-#define NV2A_SIZE 0x01000000
-
-#define NV_PMC_ADDR 0x00000000
-#define NV_PMC_SIZE 0x001000
-#define NV_PBUS_ADDR 0x00001000
-#define NV_PBUS_SIZE 0x001000
-#define NV_PFIFO_ADDR 0x00002000
-#define _NV_PFIFO_SIZE 0x002000 // Underscore prefix to prevent clash with NV_PFIFO_SIZE
-#define NV_PRMA_ADDR 0x00007000
-#define NV_PRMA_SIZE 0x001000
-#define NV_PVIDEO_ADDR 0x00008000
-#define NV_PVIDEO_SIZE 0x001000
-#define NV_PTIMER_ADDR 0x00009000
-#define NV_PTIMER_SIZE 0x001000
-#define NV_PCOUNTER_ADDR 0x0000A000
-#define NV_PCOUNTER_SIZE 0x001000
-#define NV_PVPE_ADDR 0x0000B000
-#define NV_PVPE_SIZE 0x001000
-#define NV_PTV_ADDR 0x0000D000
-#define NV_PTV_SIZE 0x001000
-#define NV_PRMFB_ADDR 0x000A0000
-#define NV_PRMFB_SIZE 0x020000
-#define NV_PRMVIO_ADDR 0x000C0000
-#define NV_PRMVIO_SIZE 0x008000 // Was 0x001000
-#define NV_PFB_ADDR 0x00100000
-#define NV_PFB_SIZE 0x001000
-#define NV_PSTRAPS_ADDR 0x00101000
-#define NV_PSTRAPS_SIZE 0x001000
-#define NV_PGRAPH_ADDR 0x00400000
-#define NV_PGRAPH_SIZE 0x002000
-#define NV_PCRTC_ADDR 0x00600000
-#define NV_PCRTC_SIZE 0x001000
-#define NV_PRMCIO_ADDR 0x00601000
-#define NV_PRMCIO_SIZE 0x001000
-#define NV_PRAMDAC_ADDR 0x00680000
-#define NV_PRAMDAC_SIZE 0x001000
-#define NV_PRMDIO_ADDR 0x00681000
-#define NV_PRMDIO_SIZE 0x001000
-#define NV_PRAMIN_ADDR 0x00700000
-#define NV_PRAMIN_SIZE 0x100000
-#define NV_USER_ADDR 0x00800000
-#define NV_USER_SIZE 0x400000
-#define NV_UREMAP_ADDR 0x00C00000 // Looks like a mapping of NV_USER_ADDR
-#define NV_UREMAP_SIZE 0x400000
-
-typedef volatile DWORD *PPUSH;
-
-typedef struct {
- DWORD Ignored[0x10];
- PPUSH Put; // On Xbox1, this field is only written to by the CPU (the GPU uses this as a trigger to start executing from the given address)
- PPUSH Get; // On Xbox1, this field is only read from by the CPU (the GPU reflects in here where it is/stopped executing)
- PPUSH Reference; // TODO : xbaddr / void* / DWORD ?
- DWORD Ignored2[0x7ED];
-} Nv2AControlDma;
-
-uint32_t EmuNV2A_Read(xbaddr addr, int size);
-void EmuNV2A_Write(xbaddr addr, uint32_t value, int size);
-
-#define PUSH_TYPE_MASK 0x00000002 // 2 bits
-#define PUSH_TYPE_SHIFT 0
-#define PUSH_TYPE_METHOD 0 // method
-#define PUSH_TYPE_JMP_FAR 1 // jump far
-#define PUSH_TYPE_CALL_FAR 2 // call far
-#define PUSH_TYPE_METHOD_UNUSED 3 // method (unused)
-#define PUSH_METHOD_MASK 0x00001FFC // 12 bits
-#define PUSH_METHOD_SHIFT 0 // Dxbx note : Not 2, because methods are actually DWORD offsets (and thus defined with increments of 4)
-#define PUSH_SUBCH_MASK 0x0000E000 // 3 bits
-#define PUSH_SUBCH_SHIFT 13
-#define PUSH_COUNT_MASK 0x1FFC0000 // 11 bits
-#define PUSH_COUNT_SHIFT 18
-#define PUSH_INSTR_MASK 0xE0000000 // 3 bits
-#define PUSH_INSTR_SHIFT 29
-#define PUSH_INSTR_IMM_INCR 0 // immediate, increment
-#define PUSH_INSTR_JMP_NEAR 1 // near jump
-#define PUSH_INSTR_IMM_NOINC 2 // immediate, no-increment
-#define PUSH_ADDR_FAR_MASK 0xFFFFFFFC // 30 bits
-#define PUSH_ADDR_FAR_SHIFT 0
-#define PUSH_ADDR_NEAR_MASK 0x1FFFFFFC // 27 bits
-#define PUSH_ADDR_NEAR_SHIFT 0 // Cxbx note : Not 2, because methods are actually DWORD offsets (and thus defined with increments of 4)
-
-#define PUSH_TYPE(dwPushCommand) ((dwPushCommand & PUSH_TYPE_MASK) >> PUSH_TYPE_SHIFT)
-#define PUSH_METHOD(dwPushCommand) ((dwPushCommand & PUSH_METHOD_MASK) >> PUSH_METHOD_SHIFT)
-#define PUSH_SUBCH(dwPushCommand) ((dwPushCommand & PUSH_SUBCH_MASK) >> PUSH_SUBCH_SHIFT)
-#define PUSH_COUNT(dwPushCommand) ((dwPushCommand & PUSH_COUNT_MASK) >> PUSH_COUNT_SHIFT)
-#define PUSH_INSTR(dwPushCommand) ((dwPushCommand & PUSH_INSTR_MASK) >> PUSH_INSTR_SHIFT)
-#define PUSH_ADDR_FAR(dwPushCommand) ((dwPushCommand & PUSH_ADDR_FAR_MASK) >> PUSH_ADDR_FAR_SHIFT)
-#define PUSH_ADDR_NEAR(dwPushCommand) ((dwPushCommand & PUSH_ADDR_NEAR_MASK) >> PUSH_ADDR_NEAR_SHIFT)
-
-#define PUSH_METHOD_MAX ((PUSH_METHOD_MASK | 3) >> PUSH_METHOD_SHIFT) // = 8191
-#define PUSH_SUBCH_MAX (PUSH_SUBCH_MASK >> PUSH_SUBCH_SHIFT) // = 7
-#define PUSH_COUNT_MAX (PUSH_COUNT_MASK >> PUSH_COUNT_SHIFT) // = 2047
-
-// Decode push buffer conmmand (inverse of D3DPUSH_ENCODE)
-inline void D3DPUSH_DECODE(const DWORD dwPushCommand, DWORD &dwMethod, DWORD &dwSubCh, DWORD &dwCount)
-{
- dwMethod = PUSH_METHOD(dwPushCommand);
- dwSubCh = PUSH_SUBCH(dwPushCommand);
- dwCount = PUSH_COUNT(dwPushCommand);
-}
-
-void EmuNV2A_Init();
-
-void InitOpenGLContext();
-
-#endif
diff --git a/src/devices/video/EmuNV2A_DEBUG.cpp b/src/devices/video/EmuNV2A_DEBUG.cpp
new file mode 100644
index 000000000..18733e7c5
--- /dev/null
+++ b/src/devices/video/EmuNV2A_DEBUG.cpp
@@ -0,0 +1,446 @@
+#define DEBUG_START(DEV) \
+const char *DebugNV_##DEV##(xbaddr addr) \
+{ \
+ switch (addr) {
+#define DEBUG_CASE(a) \
+ case a: return #a;
+#define DEBUG_CASE_EX(a, c) \
+ case a: return #a##c;
+#define DEBUG_END(DEV) \
+ default: \
+ return "Unknown " #DEV " Address"; \
+ } \
+}
+
+DEBUG_START(PMC)
+ DEBUG_CASE(NV_PMC_BOOT_0);
+ DEBUG_CASE(NV_PMC_BOOT_1);
+ DEBUG_CASE(NV_PMC_INTR_0);
+ DEBUG_CASE(NV_PMC_INTR_EN_0);
+ DEBUG_CASE(NV_PMC_ENABLE);
+DEBUG_END(PMC)
+
+DEBUG_START(PBUS)
+ DEBUG_CASE(NV_PBUS_FBIO_RAM)
+ DEBUG_CASE_EX(NV_PBUS_PCI_NV_0, ":VENDOR_ID");
+ DEBUG_CASE(NV_PBUS_PCI_NV_1);
+ DEBUG_CASE_EX(NV_PBUS_PCI_NV_2, ":REVISION_ID");
+ DEBUG_CASE_EX(NV_PBUS_PCI_NV_3, ":LATENCY_TIMER");
+ DEBUG_CASE(NV_PBUS_PCI_NV_4);
+ DEBUG_CASE(NV_PBUS_PCI_NV_5);
+ DEBUG_CASE(NV_PBUS_PCI_NV_6);
+ DEBUG_CASE(NV_PBUS_PCI_NV_7);
+ DEBUG_CASE_EX(NV_PBUS_PCI_NV_11, ":SUBSYSTEM");
+ DEBUG_CASE_EX(NV_PBUS_PCI_NV_12, ":ROM_BASE");
+ DEBUG_CASE_EX(NV_PBUS_PCI_NV_13, ":CAP_PTR");
+ DEBUG_CASE_EX(NV_PBUS_PCI_NV_14, ":RESERVED");
+ DEBUG_CASE(NV_PBUS_PCI_NV_15);
+ DEBUG_CASE_EX(NV_PBUS_PCI_NV_16, ":SUBSYSTEM");
+ DEBUG_CASE(NV_PBUS_PCI_NV_17);
+ DEBUG_CASE_EX(NV_PBUS_PCI_NV_18, ":AGP_STATUS");
+ DEBUG_CASE_EX(NV_PBUS_PCI_NV_19, ":AGP_COMMAND");
+ DEBUG_CASE_EX(NV_PBUS_PCI_NV_20, ":ROM_SHADOW");
+ DEBUG_CASE_EX(NV_PBUS_PCI_NV_21, ":VGA");
+ DEBUG_CASE_EX(NV_PBUS_PCI_NV_22, ":SCRATCH");
+ DEBUG_CASE_EX(NV_PBUS_PCI_NV_23, ":DT_TIMEOUT");
+ DEBUG_CASE_EX(NV_PBUS_PCI_NV_24, ":PME");
+ DEBUG_CASE_EX(NV_PBUS_PCI_NV_25, ":POWER_STATE");
+ DEBUG_CASE_EX(NV_PBUS_PCI_NV_26, ":RESERVED");
+DEBUG_END(PBUS)
+
+DEBUG_START(PFIFO)
+ DEBUG_CASE(NV_PFIFO_DELAY_0);
+ DEBUG_CASE(NV_PFIFO_DMA_TIMESLICE);
+ DEBUG_CASE(NV_PFIFO_TIMESLICE);
+ DEBUG_CASE(NV_PFIFO_INTR_0);
+ DEBUG_CASE(NV_PFIFO_INTR_EN_0);
+ DEBUG_CASE(NV_PFIFO_RAMHT);
+ DEBUG_CASE(NV_PFIFO_RAMFC);
+ DEBUG_CASE(NV_PFIFO_RAMRO);
+ DEBUG_CASE(NV_PFIFO_RUNOUT_STATUS);
+ DEBUG_CASE(NV_PFIFO_RUNOUT_PUT_ADDRESS);
+ DEBUG_CASE(NV_PFIFO_RUNOUT_GET_ADDRESS);
+ DEBUG_CASE(NV_PFIFO_CACHES);
+ DEBUG_CASE(NV_PFIFO_MODE);
+ DEBUG_CASE(NV_PFIFO_DMA);
+ DEBUG_CASE(NV_PFIFO_SIZE)
+ DEBUG_CASE(NV_PFIFO_CACHE0_PUSH0);
+ DEBUG_CASE(NV_PFIFO_CACHE0_PULL0);
+ DEBUG_CASE(NV_PFIFO_CACHE0_HASH);
+ DEBUG_CASE(NV_PFIFO_CACHE1_PUSH0);
+ DEBUG_CASE(NV_PFIFO_CACHE1_PUSH1);
+ DEBUG_CASE(NV_PFIFO_CACHE1_PUT);
+ DEBUG_CASE(NV_PFIFO_CACHE1_STATUS);
+ DEBUG_CASE(NV_PFIFO_CACHE1_DMA_PUSH);
+ DEBUG_CASE(NV_PFIFO_CACHE1_DMA_FETCH);
+ DEBUG_CASE(NV_PFIFO_CACHE1_DMA_STATE);
+ DEBUG_CASE(NV_PFIFO_CACHE1_DMA_INSTANCE);
+ DEBUG_CASE(NV_PFIFO_CACHE1_DMA_CTL);
+ DEBUG_CASE(NV_PFIFO_CACHE1_DMA_PUT);
+ DEBUG_CASE(NV_PFIFO_CACHE1_DMA_GET);
+ DEBUG_CASE(NV_PFIFO_CACHE1_REF);
+ DEBUG_CASE(NV_PFIFO_CACHE1_DMA_SUBROUTINE);
+ DEBUG_CASE(NV_PFIFO_CACHE1_PULL0);
+ DEBUG_CASE(NV_PFIFO_CACHE1_PULL1);
+ DEBUG_CASE(NV_PFIFO_CACHE1_HASH);
+ DEBUG_CASE(NV_PFIFO_CACHE1_ACQUIRE_0);
+ DEBUG_CASE(NV_PFIFO_CACHE1_ACQUIRE_1);
+ DEBUG_CASE(NV_PFIFO_CACHE1_ACQUIRE_2);
+ DEBUG_CASE(NV_PFIFO_CACHE1_SEMAPHORE);
+ DEBUG_CASE(NV_PFIFO_CACHE1_GET);
+ DEBUG_CASE(NV_PFIFO_CACHE1_ENGINE);
+ DEBUG_CASE(NV_PFIFO_CACHE1_DMA_DCOUNT);
+ DEBUG_CASE(NV_PFIFO_CACHE1_DMA_GET_JMP_SHADOW);
+ DEBUG_CASE(NV_PFIFO_CACHE1_DMA_RSVD_SHADOW);
+ DEBUG_CASE(NV_PFIFO_CACHE1_DMA_DATA_SHADOW);
+DEBUG_END(PFIFO)
+
+DEBUG_START(PRMA)
+DEBUG_END(PRMA)
+
+DEBUG_START(PVIDEO)
+ DEBUG_CASE(NV_PVIDEO_DEBUG_2);
+ DEBUG_CASE(NV_PVIDEO_DEBUG_3);
+ DEBUG_CASE(NV_PVIDEO_INTR);
+ DEBUG_CASE(NV_PVIDEO_INTR_EN);
+ DEBUG_CASE(NV_PVIDEO_BUFFER);
+ DEBUG_CASE(NV_PVIDEO_STOP);
+ DEBUG_CASE(NV_PVIDEO_BASE(0));
+ DEBUG_CASE(NV_PVIDEO_BASE(1));
+ DEBUG_CASE(NV_PVIDEO_LIMIT(0));
+ DEBUG_CASE(NV_PVIDEO_LIMIT(1));
+ DEBUG_CASE(NV_PVIDEO_LUMINANCE(0));
+ DEBUG_CASE(NV_PVIDEO_LUMINANCE(1));
+ DEBUG_CASE(NV_PVIDEO_CHROMINANCE(0));
+ DEBUG_CASE(NV_PVIDEO_CHROMINANCE(1));
+ DEBUG_CASE(NV_PVIDEO_OFFSET(0));
+ DEBUG_CASE(NV_PVIDEO_OFFSET(1));
+ DEBUG_CASE(NV_PVIDEO_SIZE_IN(0));
+ DEBUG_CASE(NV_PVIDEO_SIZE_IN(1));
+ DEBUG_CASE(NV_PVIDEO_POINT_IN(0));
+ DEBUG_CASE(NV_PVIDEO_POINT_IN(1));
+ DEBUG_CASE(NV_PVIDEO_DS_DX(0));
+ DEBUG_CASE(NV_PVIDEO_DS_DX(1));
+ DEBUG_CASE(NV_PVIDEO_DT_DY(0));
+ DEBUG_CASE(NV_PVIDEO_DT_DY(1));
+ DEBUG_CASE(NV_PVIDEO_POINT_OUT(0));
+ DEBUG_CASE(NV_PVIDEO_POINT_OUT(1));
+ DEBUG_CASE(NV_PVIDEO_SIZE_OUT(0));
+ DEBUG_CASE(NV_PVIDEO_SIZE_OUT(1));
+ DEBUG_CASE(NV_PVIDEO_FORMAT(0));
+ DEBUG_CASE(NV_PVIDEO_FORMAT(1));
+DEBUG_END(PVIDEO)
+
+DEBUG_START(PTIMER)
+ DEBUG_CASE(NV_PTIMER_INTR_0);
+ DEBUG_CASE(NV_PTIMER_INTR_EN_0);
+ DEBUG_CASE(NV_PTIMER_NUMERATOR);
+ DEBUG_CASE(NV_PTIMER_DENOMINATOR);
+ DEBUG_CASE(NV_PTIMER_TIME_0);
+ DEBUG_CASE(NV_PTIMER_TIME_1);
+ DEBUG_CASE(NV_PTIMER_ALARM_0);
+DEBUG_END(PTIMER)
+
+DEBUG_START(PCOUNTER)
+DEBUG_END(PCOUNTER)
+
+DEBUG_START(PVPE)
+DEBUG_END(PVPE)
+
+DEBUG_START(PTV)
+DEBUG_END(PTV)
+
+DEBUG_START(PRMFB)
+DEBUG_END(PRMFB)
+
+DEBUG_START(PRMVIO)
+DEBUG_END(PRMVIO)
+
+DEBUG_START(PFB)
+ DEBUG_CASE(NV_PFB_CFG0)
+ DEBUG_CASE(NV_PFB_CFG1)
+ DEBUG_CASE(NV_PFB_CSTATUS)
+ DEBUG_CASE(NV_PFB_REFCTRL)
+ DEBUG_CASE(NV_PFB_NVM) // NV_PFB_NVM_MODE_DISABLE
+ DEBUG_CASE(NV_PFB_PIN)
+ DEBUG_CASE(NV_PFB_PAD)
+ DEBUG_CASE(NV_PFB_TIMING0)
+ DEBUG_CASE(NV_PFB_TIMING1)
+ DEBUG_CASE(NV_PFB_TIMING2)
+ DEBUG_CASE(NV_PFB_TILE(0))
+ DEBUG_CASE(NV_PFB_TLIMIT(0))
+ DEBUG_CASE(NV_PFB_TSIZE(0))
+ DEBUG_CASE(NV_PFB_TSTATUS(0))
+ DEBUG_CASE(NV_PFB_TILE(1))
+ DEBUG_CASE(NV_PFB_TLIMIT(1))
+ DEBUG_CASE(NV_PFB_TSIZE(1))
+ DEBUG_CASE(NV_PFB_TSTATUS(1))
+ DEBUG_CASE(NV_PFB_TILE(2))
+ DEBUG_CASE(NV_PFB_TLIMIT(2))
+ DEBUG_CASE(NV_PFB_TSIZE(2))
+ DEBUG_CASE(NV_PFB_TSTATUS(2))
+ DEBUG_CASE(NV_PFB_TILE(3))
+ DEBUG_CASE(NV_PFB_TLIMIT(3))
+ DEBUG_CASE(NV_PFB_TSIZE(3))
+ DEBUG_CASE(NV_PFB_TSTATUS(3))
+ DEBUG_CASE(NV_PFB_TILE(4))
+ DEBUG_CASE(NV_PFB_TLIMIT(4))
+ DEBUG_CASE(NV_PFB_TSIZE(4))
+ DEBUG_CASE(NV_PFB_TSTATUS(4))
+ DEBUG_CASE(NV_PFB_TILE(5))
+ DEBUG_CASE(NV_PFB_TLIMIT(5))
+ DEBUG_CASE(NV_PFB_TSIZE(5))
+ DEBUG_CASE(NV_PFB_TSTATUS(5))
+ DEBUG_CASE(NV_PFB_TILE(6))
+ DEBUG_CASE(NV_PFB_TLIMIT(6))
+ DEBUG_CASE(NV_PFB_TSIZE(6))
+ DEBUG_CASE(NV_PFB_TSTATUS(6))
+ DEBUG_CASE(NV_PFB_TILE(7))
+ DEBUG_CASE(NV_PFB_TLIMIT(7))
+ DEBUG_CASE(NV_PFB_TSIZE(7))
+ DEBUG_CASE(NV_PFB_TSTATUS(7))
+ DEBUG_CASE(NV_PFB_MRS)
+ DEBUG_CASE(NV_PFB_EMRS)
+ DEBUG_CASE(NV_PFB_MRS_EXT)
+ DEBUG_CASE(NV_PFB_EMRS_EXT)
+ DEBUG_CASE(NV_PFB_REF)
+ DEBUG_CASE(NV_PFB_PRE)
+ DEBUG_CASE(NV_PFB_ZCOMP(0))
+ DEBUG_CASE(NV_PFB_ZCOMP(1))
+ DEBUG_CASE(NV_PFB_ZCOMP(2))
+ DEBUG_CASE(NV_PFB_ZCOMP(3))
+ DEBUG_CASE(NV_PFB_ZCOMP(4))
+ DEBUG_CASE(NV_PFB_ZCOMP(5))
+ DEBUG_CASE(NV_PFB_ZCOMP(6))
+ DEBUG_CASE(NV_PFB_ZCOMP(7))
+ DEBUG_CASE(NV_PFB_ZCOMP_OFFSET)
+ DEBUG_CASE(NV_PFB_ARB_PREDIVIDER)
+ DEBUG_CASE(NV_PFB_ARB_TIMEOUT)
+ DEBUG_CASE(NV_PFB_ARB_XFER_REM)
+ DEBUG_CASE(NV_PFB_ARB_DIFF_BANK)
+ DEBUG_CASE(NV_PFB_CLOSE_PAGE0)
+ DEBUG_CASE(NV_PFB_CLOSE_PAGE1)
+ DEBUG_CASE(NV_PFB_CLOSE_PAGE2)
+ DEBUG_CASE(NV_PFB_BPARB)
+ DEBUG_CASE(NV_PFB_CMDQ0)
+ DEBUG_CASE(NV_PFB_CMDQ1)
+ DEBUG_CASE(NV_PFB_ILL_INSTR)
+ DEBUG_CASE(NV_PFB_RT)
+ DEBUG_CASE(NV_PFB_AUTOCLOSE)
+ DEBUG_CASE(NV_PFB_WBC)
+ DEBUG_CASE(NV_PFB_CMDQ_PRT)
+ DEBUG_CASE(NV_PFB_CPU_RRQ)
+ DEBUG_CASE(NV_PFB_BYPASS);
+DEBUG_END(PFB)
+
+DEBUG_START(PSTRAPS)
+DEBUG_END(PSTRAPS)
+
+DEBUG_START(PGRAPH)
+ DEBUG_CASE(NV_PGRAPH_DEBUG_0);
+ DEBUG_CASE(NV_PGRAPH_DEBUG_1);
+ DEBUG_CASE(NV_PGRAPH_DEBUG_3);
+ DEBUG_CASE(NV_PGRAPH_DEBUG_4);
+ DEBUG_CASE(NV_PGRAPH_DEBUG_5);
+ DEBUG_CASE(NV_PGRAPH_DEBUG_8);
+ DEBUG_CASE(NV_PGRAPH_DEBUG_9);
+ DEBUG_CASE(NV_PGRAPH_INTR);
+ DEBUG_CASE(NV_PGRAPH_NSOURCE);
+ DEBUG_CASE(NV_PGRAPH_INTR_EN);
+ DEBUG_CASE(NV_PGRAPH_CTX_CONTROL);
+ DEBUG_CASE(NV_PGRAPH_CTX_USER);
+ DEBUG_CASE(NV_PGRAPH_CTX_SWITCH1);
+ DEBUG_CASE(NV_PGRAPH_CTX_SWITCH2);
+ DEBUG_CASE(NV_PGRAPH_CTX_SWITCH3);
+ DEBUG_CASE(NV_PGRAPH_CTX_SWITCH4);
+ DEBUG_CASE(NV_PGRAPH_STATUS);
+ DEBUG_CASE(NV_PGRAPH_TRAPPED_ADDR);
+ DEBUG_CASE(NV_PGRAPH_TRAPPED_DATA_LOW);
+ DEBUG_CASE(NV_PGRAPH_SURFACE);
+ DEBUG_CASE(NV_PGRAPH_INCREMENT);
+ DEBUG_CASE(NV_PGRAPH_FIFO);
+ DEBUG_CASE(NV_PGRAPH_RDI_INDEX);
+ DEBUG_CASE(NV_PGRAPH_RDI_DATA);
+ DEBUG_CASE(NV_PGRAPH_FFINTFC_ST2);
+ DEBUG_CASE(NV_PGRAPH_CHANNEL_CTX_TABLE);
+ DEBUG_CASE(NV_PGRAPH_CHANNEL_CTX_POINTER);
+ DEBUG_CASE(NV_PGRAPH_CHANNEL_CTX_TRIGGER);
+ DEBUG_CASE(NV_PGRAPH_DEBUG_2);
+ DEBUG_CASE(NV_PGRAPH_TTILE(0));
+ DEBUG_CASE(NV_PGRAPH_TLIMIT(0));
+ DEBUG_CASE(NV_PGRAPH_TSIZE(0));
+ DEBUG_CASE(NV_PGRAPH_TSTATUS(0));
+ DEBUG_CASE(NV_PGRAPH_TTILE(1));
+ DEBUG_CASE(NV_PGRAPH_TLIMIT(1));
+ DEBUG_CASE(NV_PGRAPH_TSIZE(1));
+ DEBUG_CASE(NV_PGRAPH_TSTATUS(1));
+ DEBUG_CASE(NV_PGRAPH_TTILE(2));
+ DEBUG_CASE(NV_PGRAPH_TLIMIT(2));
+ DEBUG_CASE(NV_PGRAPH_TSIZE(2));
+ DEBUG_CASE(NV_PGRAPH_TSTATUS(2));
+ DEBUG_CASE(NV_PGRAPH_TTILE(3));
+ DEBUG_CASE(NV_PGRAPH_TLIMIT(3));
+ DEBUG_CASE(NV_PGRAPH_TSIZE(3));
+ DEBUG_CASE(NV_PGRAPH_TSTATUS(3));
+ DEBUG_CASE(NV_PGRAPH_TTILE(4));
+ DEBUG_CASE(NV_PGRAPH_TLIMIT(4));
+ DEBUG_CASE(NV_PGRAPH_TSIZE(4));
+ DEBUG_CASE(NV_PGRAPH_TSTATUS(4));
+ DEBUG_CASE(NV_PGRAPH_TTILE(5));
+ DEBUG_CASE(NV_PGRAPH_TLIMIT(5));
+ DEBUG_CASE(NV_PGRAPH_TSIZE(5));
+ DEBUG_CASE(NV_PGRAPH_TSTATUS(5));
+ DEBUG_CASE(NV_PGRAPH_TTILE(6));
+ DEBUG_CASE(NV_PGRAPH_TLIMIT(6));
+ DEBUG_CASE(NV_PGRAPH_TSIZE(6));
+ DEBUG_CASE(NV_PGRAPH_TSTATUS(6));
+ DEBUG_CASE(NV_PGRAPH_TTILE(7));
+ DEBUG_CASE(NV_PGRAPH_TLIMIT(7));
+ DEBUG_CASE(NV_PGRAPH_TSIZE(7));
+ DEBUG_CASE(NV_PGRAPH_TSTATUS(7));
+ DEBUG_CASE(NV_PGRAPH_ZCOMP(0));
+ DEBUG_CASE(NV_PGRAPH_ZCOMP(1));
+ DEBUG_CASE(NV_PGRAPH_ZCOMP(2));
+ DEBUG_CASE(NV_PGRAPH_ZCOMP(3));
+ DEBUG_CASE(NV_PGRAPH_ZCOMP(4));
+ DEBUG_CASE(NV_PGRAPH_ZCOMP(5));
+ DEBUG_CASE(NV_PGRAPH_ZCOMP(6));
+ DEBUG_CASE(NV_PGRAPH_ZCOMP(7));
+ DEBUG_CASE(NV_PGRAPH_ZCOMP_OFFSET);
+ DEBUG_CASE(NV_PGRAPH_FBCFG0);
+ DEBUG_CASE(NV_PGRAPH_FBCFG1);
+ DEBUG_CASE(NV_PGRAPH_DEBUG_6);
+ DEBUG_CASE(NV_PGRAPH_DEBUG_7);
+ DEBUG_CASE(NV_PGRAPH_DEBUG_10);
+ DEBUG_CASE(NV_PGRAPH_CSV0_D);
+ DEBUG_CASE(NV_PGRAPH_CSV0_C);
+ DEBUG_CASE(NV_PGRAPH_CSV1_B);
+ DEBUG_CASE(NV_PGRAPH_CSV1_A);
+ DEBUG_CASE(NV_PGRAPH_CHEOPS_OFFSET);
+ DEBUG_CASE(NV_PGRAPH_BLEND);
+ DEBUG_CASE(NV_PGRAPH_BLENDCOLOR);
+ DEBUG_CASE(NV_PGRAPH_BORDERCOLOR0);
+ DEBUG_CASE(NV_PGRAPH_BORDERCOLOR1);
+ DEBUG_CASE(NV_PGRAPH_BORDERCOLOR2);
+ DEBUG_CASE(NV_PGRAPH_BORDERCOLOR3);
+ DEBUG_CASE(NV_PGRAPH_BUMPOFFSET1);
+ DEBUG_CASE(NV_PGRAPH_BUMPSCALE1);
+ DEBUG_CASE(NV_PGRAPH_CLEARRECTX);
+ DEBUG_CASE(NV_PGRAPH_CLEARRECTY);
+ DEBUG_CASE(NV_PGRAPH_COLORCLEARVALUE);
+ DEBUG_CASE(NV_PGRAPH_COMBINEFACTOR0);
+ DEBUG_CASE(NV_PGRAPH_COMBINEFACTOR1);
+ DEBUG_CASE(NV_PGRAPH_COMBINEALPHAI0);
+ DEBUG_CASE(NV_PGRAPH_COMBINEALPHAO0);
+ DEBUG_CASE(NV_PGRAPH_COMBINECOLORI0);
+ DEBUG_CASE(NV_PGRAPH_COMBINECOLORO0);
+ DEBUG_CASE(NV_PGRAPH_COMBINECTL);
+ DEBUG_CASE(NV_PGRAPH_COMBINESPECFOG0);
+ DEBUG_CASE(NV_PGRAPH_COMBINESPECFOG1);
+ DEBUG_CASE(NV_PGRAPH_CONTROL_0);
+ DEBUG_CASE(NV_PGRAPH_CONTROL_2);
+ DEBUG_CASE(NV_PGRAPH_CONTROL_3);
+ DEBUG_CASE(NV_PGRAPH_FOGCOLOR);
+ DEBUG_CASE(NV_PGRAPH_FOGPARAM0);
+ DEBUG_CASE(NV_PGRAPH_FOGPARAM1);
+ DEBUG_CASE(NV_PGRAPH_SETUPRASTER);
+ DEBUG_CASE(NV_PGRAPH_SHADERCLIPMODE);
+ DEBUG_CASE(NV_PGRAPH_SHADERCTL);
+ DEBUG_CASE(NV_PGRAPH_SHADERPROG);
+ DEBUG_CASE(NV_PGRAPH_SHADOWZSLOPETHRESHOLD);
+ DEBUG_CASE(NV_PGRAPH_SPECFOGFACTOR0);
+ DEBUG_CASE(NV_PGRAPH_SPECFOGFACTOR1);
+ DEBUG_CASE_EX(NV_PGRAPH_TEXADDRESS0, ":_ADDRV");
+ DEBUG_CASE(NV_PGRAPH_TEXADDRESS1);
+ DEBUG_CASE(NV_PGRAPH_TEXADDRESS2);
+ DEBUG_CASE(NV_PGRAPH_TEXADDRESS3);
+ DEBUG_CASE(NV_PGRAPH_TEXCTL0_0);
+ DEBUG_CASE(NV_PGRAPH_TEXCTL0_1);
+ DEBUG_CASE(NV_PGRAPH_TEXCTL0_2);
+ DEBUG_CASE(NV_PGRAPH_TEXCTL0_3);
+ DEBUG_CASE(NV_PGRAPH_TEXCTL1_0);
+ DEBUG_CASE(NV_PGRAPH_TEXCTL1_1);
+ DEBUG_CASE(NV_PGRAPH_TEXCTL1_2);
+ DEBUG_CASE(NV_PGRAPH_TEXCTL1_3);
+ DEBUG_CASE(NV_PGRAPH_TEXCTL2_0);
+ DEBUG_CASE(NV_PGRAPH_TEXCTL2_1);
+ DEBUG_CASE(NV_PGRAPH_TEXFILTER0);
+ DEBUG_CASE(NV_PGRAPH_TEXFILTER1);
+ DEBUG_CASE(NV_PGRAPH_TEXFILTER2);
+ DEBUG_CASE(NV_PGRAPH_TEXFILTER3);
+ DEBUG_CASE(NV_PGRAPH_TEXFMT0);
+ DEBUG_CASE(NV_PGRAPH_TEXFMT1);
+ DEBUG_CASE(NV_PGRAPH_TEXFMT2);
+ DEBUG_CASE(NV_PGRAPH_TEXFMT3);
+ DEBUG_CASE(NV_PGRAPH_TEXIMAGERECT0);
+ DEBUG_CASE(NV_PGRAPH_TEXIMAGERECT1);
+ DEBUG_CASE(NV_PGRAPH_TEXIMAGERECT2);
+ DEBUG_CASE(NV_PGRAPH_TEXIMAGERECT3);
+ DEBUG_CASE(NV_PGRAPH_TEXOFFSET0);
+ DEBUG_CASE(NV_PGRAPH_TEXOFFSET1);
+ DEBUG_CASE(NV_PGRAPH_TEXOFFSET2);
+ DEBUG_CASE(NV_PGRAPH_TEXOFFSET3);
+ DEBUG_CASE(NV_PGRAPH_TEXPALETTE0);
+ DEBUG_CASE(NV_PGRAPH_TEXPALETTE1);
+ DEBUG_CASE(NV_PGRAPH_TEXPALETTE2);
+ DEBUG_CASE(NV_PGRAPH_TEXPALETTE3);
+ DEBUG_CASE(NV_PGRAPH_ZSTENCILCLEARVALUE);
+ DEBUG_CASE(NV_PGRAPH_ZCLIPMIN);
+ DEBUG_CASE(NV_PGRAPH_ZOFFSETBIAS);
+ DEBUG_CASE(NV_PGRAPH_ZOFFSETFACTOR);
+ DEBUG_CASE(NV_PGRAPH_EYEVEC0);
+ DEBUG_CASE(NV_PGRAPH_EYEVEC1);
+ DEBUG_CASE(NV_PGRAPH_EYEVEC2);
+ DEBUG_CASE(NV_PGRAPH_ZCLIPMAX);
+DEBUG_END(PGRAPH)
+
+DEBUG_START(PCRTC)
+ DEBUG_CASE(NV_PCRTC_INTR_0);
+ DEBUG_CASE(NV_PCRTC_INTR_EN_0);
+ DEBUG_CASE(NV_PCRTC_START);
+ DEBUG_CASE(NV_PCRTC_CONFIG);
+DEBUG_END(PCRTC)
+
+DEBUG_START(PRMCIO)
+ DEBUG_CASE(VGA_CRT_DC);
+ DEBUG_CASE(VGA_CRT_DM);
+ DEBUG_CASE(VGA_ATT_R);
+ DEBUG_CASE(VGA_ATT_W);
+ DEBUG_CASE(VGA_GFX_D);
+ DEBUG_CASE(VGA_SEQ_D);
+ DEBUG_CASE(VGA_MIS_R);
+ DEBUG_CASE(VGA_MIS_W);
+ DEBUG_CASE(VGA_FTC_R);
+ DEBUG_CASE(VGA_IS1_RC);
+ DEBUG_CASE(VGA_IS1_RM);
+ DEBUG_CASE(VGA_PEL_D);
+ DEBUG_CASE(VGA_PEL_MSK);
+ DEBUG_CASE(VGA_CRT_IC);
+ DEBUG_CASE(VGA_CRT_IM);
+ DEBUG_CASE(VGA_GFX_I);
+ DEBUG_CASE(VGA_SEQ_I);
+ DEBUG_CASE(VGA_PEL_IW);
+ DEBUG_CASE(VGA_PEL_IR);
+DEBUG_END(PRMCIO)
+
+DEBUG_START(PRAMDAC)
+ DEBUG_CASE(NV_PRAMDAC_NVPLL_COEFF);
+ DEBUG_CASE(NV_PRAMDAC_MPLL_COEFF);
+ DEBUG_CASE(NV_PRAMDAC_VPLL_COEFF);
+ DEBUG_CASE(NV_PRAMDAC_PLL_TEST_COUNTER);
+
+DEBUG_END(PRAMDAC)
+
+DEBUG_START(PRMDIO)
+DEBUG_END(PRMDIO)
+
+DEBUG_START(PRAMIN)
+DEBUG_END(PRAMIN)
+
+DEBUG_START(USER)
+ DEBUG_CASE(NV_USER_DMA_PUT);
+ DEBUG_CASE(NV_USER_DMA_GET);
+ DEBUG_CASE(NV_USER_REF);
+DEBUG_END(USER)
+
+
diff --git a/src/devices/video/EmuNV2A_PBUS.cpp b/src/devices/video/EmuNV2A_PBUS.cpp
new file mode 100644
index 000000000..a30fc2099
--- /dev/null
+++ b/src/devices/video/EmuNV2A_PBUS.cpp
@@ -0,0 +1,39 @@
+/* PBUS - bus control */
+DEVICE_READ32(PBUS)
+{
+ DEVICE_READ32_SWITCH() {
+ case NV_PBUS_PCI_NV_0:
+ result = 0x10de; // PCI_VENDOR_ID_NVIDIA (?where to return PCI_DEVICE_ID_NVIDIA_NV2A = 0x01b7)
+ // TODO : result = pci_get_long(d->dev.config + PCI_VENDOR_ID);
+ break;
+ case NV_PBUS_PCI_NV_1:
+ result = 1; // NV_PBUS_PCI_NV_1_IO_SPACE_ENABLED
+ // TODO : result = pci_get_long(d->dev.config + PCI_COMMAND);
+ break;
+ case NV_PBUS_PCI_NV_2:
+ result = (0x02 << 24) | 161; // PCI_CLASS_DISPLAY_3D (0x02) Rev 161 (0xA1)
+ // TODO : result = pci_get_long(d->dev.config + PCI_CLASS_REVISION);
+ break;
+
+ default:
+ DEBUG_READ32_UNHANDLED(PBUS); // TODO : DEVICE_READ32_REG(pbus);
+ break;
+ }
+
+ DEVICE_READ32_END(PBUS);
+}
+
+DEVICE_WRITE32(PBUS)
+{
+ switch(addr) {
+ case NV_PBUS_PCI_NV_1:
+ // TODO : Handle write on NV_PBUS_PCI_NV_1 with 1 (NV_PBUS_PCI_NV_1_IO_SPACE_ENABLED) + 4 (NV_PBUS_PCI_NV_1_BUS_MASTER_ENABLED)
+ // pci_set_long(d->dev.config + PCI_COMMAND, val);
+ break;
+ default:
+ DEBUG_WRITE32_UNHANDLED(PBUS); // TODO : DEVICE_WRITE32_REG(pbus);
+ break;
+ }
+
+ DEVICE_WRITE32_END(PBUS);
+}
diff --git a/src/devices/video/EmuNV2A_PCOUNTER.cpp b/src/devices/video/EmuNV2A_PCOUNTER.cpp
new file mode 100644
index 000000000..1f31884ce
--- /dev/null
+++ b/src/devices/video/EmuNV2A_PCOUNTER.cpp
@@ -0,0 +1,21 @@
+DEVICE_READ32(PCOUNTER)
+{
+ DEVICE_READ32_SWITCH() {
+ default:
+ DEBUG_READ32_UNHANDLED(PCOUNTER); // TODO : DEVICE_READ32_REG(pcounter);
+ break;
+ }
+
+ DEVICE_READ32_END(PCOUNTER);
+}
+
+DEVICE_WRITE32(PCOUNTER)
+{
+ switch (addr) {
+ default:
+ DEBUG_WRITE32_UNHANDLED(PCOUNTER); // TODO : DEVICE_WRITE32_REG(pcounter);
+ break;
+ }
+
+ DEVICE_WRITE32_END(PCOUNTER);
+}
diff --git a/src/devices/video/EmuNV2A_PCRTC.cpp b/src/devices/video/EmuNV2A_PCRTC.cpp
new file mode 100644
index 000000000..353412f62
--- /dev/null
+++ b/src/devices/video/EmuNV2A_PCRTC.cpp
@@ -0,0 +1,51 @@
+DEVICE_READ32(PCRTC)
+{
+ DEVICE_READ32_SWITCH() {
+
+ case NV_PCRTC_INTR_0:
+ result = d->pcrtc.pending_interrupts;
+ break;
+ case NV_PCRTC_INTR_EN_0:
+ result = d->pcrtc.enabled_interrupts;
+ break;
+ case NV_PCRTC_START:
+ result = d->pcrtc.start;
+ break;
+ default:
+ result = 0;
+ //DEVICE_READ32_REG(pcrtc); // Was : DEBUG_READ32_UNHANDLED(PCRTC);
+ break;
+ }
+
+ DEVICE_READ32_END(PCRTC);
+}
+
+DEVICE_WRITE32(PCRTC)
+{
+ switch (addr) {
+
+ case NV_PCRTC_INTR_0:
+ d->pcrtc.pending_interrupts &= ~value;
+ update_irq(d);
+ break;
+ case NV_PCRTC_INTR_EN_0:
+ d->pcrtc.enabled_interrupts = value;
+ update_irq(d);
+ break;
+ case NV_PCRTC_START:
+ value &= 0x07FFFFFF;
+ // assert(val < memory_region_size(d->vram));
+ d->pcrtc.start = value;
+
+ NV2A_DPRINTF("PCRTC_START - %x %x %x %x\n",
+ d->vram_ptr[value+64], d->vram_ptr[value+64+1],
+ d->vram_ptr[value+64+2], d->vram_ptr[value+64+3]);
+ break;
+
+ default:
+ DEVICE_WRITE32_REG(pcrtc); // Was : DEBUG_WRITE32_UNHANDLED(PCRTC);
+ break;
+ }
+
+ DEVICE_WRITE32_END(PCRTC);
+}
diff --git a/src/devices/video/EmuNV2A_PFB.cpp b/src/devices/video/EmuNV2A_PFB.cpp
new file mode 100644
index 000000000..3253fcc30
--- /dev/null
+++ b/src/devices/video/EmuNV2A_PFB.cpp
@@ -0,0 +1,31 @@
+DEVICE_READ32(PFB)
+{
+ DEVICE_READ32_SWITCH() {
+ case NV_PFB_CFG0:
+ /* 3-4 memory partitions. The debug bios checks this. */
+ result = 3; // = NV_PFB_CFG0_PART_4
+ break;
+ case NV_PFB_CSTATUS:
+ result = d->vram_size;
+ break;
+ case NV_PFB_WBC:
+ result = 0; // = !NV_PFB_WBC_FLUSH /* Flush not pending. */
+ break;
+ default:
+ DEVICE_READ32_REG(pfb);
+ break;
+ }
+
+ DEVICE_READ32_END(PFB);
+}
+
+DEVICE_WRITE32(PFB)
+{
+ switch (addr) {
+ default:
+ DEVICE_WRITE32_REG(pfb);
+ break;
+ }
+
+ DEVICE_WRITE32_END(PFB);
+}
diff --git a/src/devices/video/EmuNV2A_PFIFO.cpp b/src/devices/video/EmuNV2A_PFIFO.cpp
new file mode 100644
index 000000000..7f392f90a
--- /dev/null
+++ b/src/devices/video/EmuNV2A_PFIFO.cpp
@@ -0,0 +1,516 @@
+typedef struct RAMHTEntry {
+ uint32_t handle;
+ xbaddr instance;
+ enum FIFOEngine engine;
+ unsigned int channel_id : 5;
+ bool valid;
+} RAMHTEntry;
+
+static void pfifo_run_pusher(NV2AState *d); // forward declaration
+int pfifo_puller_thread(NV2AState *d);
+static uint32_t ramht_hash(NV2AState *d, uint32_t handle);
+static RAMHTEntry ramht_lookup(NV2AState *d, uint32_t handle); // forward declaration
+
+/* PFIFO - MMIO and DMA FIFO submission to PGRAPH and VPE */
+DEVICE_READ32(PFIFO)
+{
+ DEVICE_READ32_SWITCH() {
+ case NV_PFIFO_RAMHT:
+ result = 0x03000100; // = NV_PFIFO_RAMHT_SIZE_4K | NV_PFIFO_RAMHT_BASE_ADDRESS(NumberOfPaddingBytes >> 12) | NV_PFIFO_RAMHT_SEARCH_128
+ break;
+ case NV_PFIFO_RAMFC:
+ result = 0x00890110; // = ? | NV_PFIFO_RAMFC_SIZE_2K | ?
+ break;
+ case NV_PFIFO_INTR_0:
+ result = d->pfifo.pending_interrupts;
+ break;
+ case NV_PFIFO_INTR_EN_0:
+ result = d->pfifo.enabled_interrupts;
+ break;
+ case NV_PFIFO_RUNOUT_STATUS:
+ result = NV_PFIFO_RUNOUT_STATUS_LOW_MARK; /* low mark empty */
+ break;
+ case NV_PFIFO_CACHE1_PUSH0:
+ result = d->pfifo.cache1.push_enabled;
+ break;
+ case NV_PFIFO_CACHE1_PUSH1:
+ SET_MASK(result, NV_PFIFO_CACHE1_PUSH1_CHID, d->pfifo.cache1.channel_id);
+ SET_MASK(result, NV_PFIFO_CACHE1_PUSH1_MODE, d->pfifo.cache1.mode);
+ break;
+ case NV_PFIFO_CACHE1_STATUS: {
+ std::unique_lock cache_unique_lock(d->pfifo.cache1.cache_lock);
+
+ if (d->pfifo.cache1.cache.empty()) {
+ result |= NV_PFIFO_CACHE1_STATUS_LOW_MARK; /* low mark empty */
+ }
+ break;
+ }
+ case NV_PFIFO_CACHE1_DMA_PUSH:
+ SET_MASK(result, NV_PFIFO_CACHE1_DMA_PUSH_ACCESS,
+ d->pfifo.cache1.dma_push_enabled);
+ SET_MASK(result, NV_PFIFO_CACHE1_DMA_PUSH_STATUS,
+ d->pfifo.cache1.dma_push_suspended);
+ SET_MASK(result, NV_PFIFO_CACHE1_DMA_PUSH_BUFFER, 1); /* buffer emoty */
+ break;
+ case NV_PFIFO_CACHE1_DMA_STATE:
+ SET_MASK(result, NV_PFIFO_CACHE1_DMA_STATE_METHOD_TYPE,
+ d->pfifo.cache1.method_nonincreasing);
+ SET_MASK(result, NV_PFIFO_CACHE1_DMA_STATE_METHOD,
+ d->pfifo.cache1.method >> 2);
+ SET_MASK(result, NV_PFIFO_CACHE1_DMA_STATE_SUBCHANNEL,
+ d->pfifo.cache1.subchannel);
+ SET_MASK(result, NV_PFIFO_CACHE1_DMA_STATE_METHOD_COUNT,
+ d->pfifo.cache1.method_count);
+ SET_MASK(result, NV_PFIFO_CACHE1_DMA_STATE_ERROR,
+ d->pfifo.cache1.error);
+ break;
+ case NV_PFIFO_CACHE1_DMA_INSTANCE:
+ SET_MASK(result, NV_PFIFO_CACHE1_DMA_INSTANCE_ADDRESS_MASK,
+ d->pfifo.cache1.dma_instance >> 4);
+ break;
+ case NV_PFIFO_CACHE1_DMA_PUT:
+ result = d->user.channel_control[d->pfifo.cache1.channel_id].dma_put;
+ break;
+ case NV_PFIFO_CACHE1_DMA_GET:
+ result = d->user.channel_control[d->pfifo.cache1.channel_id].dma_get;
+ break;
+ case NV_PFIFO_CACHE1_DMA_SUBROUTINE:
+ result = d->pfifo.cache1.subroutine_return
+ | d->pfifo.cache1.subroutine_active;
+ break;
+ case NV_PFIFO_CACHE1_PULL0: {
+ std::unique_lock cache_unique_lock(d->pfifo.cache1.cache_lock);
+ result = d->pfifo.cache1.pull_enabled;
+ break;
+ }
+ case NV_PFIFO_CACHE1_ENGINE: {
+ std::unique_lock cache_unique_lock(d->pfifo.cache1.cache_lock);
+ for (int i = 0; i < NV2A_NUM_SUBCHANNELS; i++) {
+ result |= d->pfifo.cache1.bound_engines[i] << (i * 2);
+ }
+ break;
+ }
+ case NV_PFIFO_CACHE1_DMA_DCOUNT:
+ result = d->pfifo.cache1.dcount;
+ break;
+ case NV_PFIFO_CACHE1_DMA_GET_JMP_SHADOW:
+ result = d->pfifo.cache1.get_jmp_shadow;
+ break;
+ case NV_PFIFO_CACHE1_DMA_RSVD_SHADOW:
+ result = d->pfifo.cache1.rsvd_shadow;
+ break;
+ case NV_PFIFO_CACHE1_DMA_DATA_SHADOW:
+ result = d->pfifo.cache1.data_shadow;
+ break;
+ default:
+ DEVICE_READ32_REG(pfifo); // Was : DEBUG_READ32_UNHANDLED(PFIFO);
+ break;
+ }
+
+ DEVICE_READ32_END(PFIFO);
+}
+
+DEVICE_WRITE32(PFIFO)
+{
+ int i;
+
+ switch(addr) {
+ case NV_PFIFO_INTR_0:
+ d->pfifo.pending_interrupts &= ~value;
+ update_irq(d);
+ break;
+ case NV_PFIFO_INTR_EN_0:
+ d->pfifo.enabled_interrupts = value;
+ update_irq(d);
+ break;
+ case NV_PFIFO_CACHE1_PUSH0:
+ d->pfifo.cache1.push_enabled = value & NV_PFIFO_CACHE1_PUSH0_ACCESS;
+ break;
+ case NV_PFIFO_CACHE1_PUSH1:
+ d->pfifo.cache1.channel_id = GET_MASK(value, NV_PFIFO_CACHE1_PUSH1_CHID);
+ d->pfifo.cache1.mode = (FifoMode)GET_MASK(value, NV_PFIFO_CACHE1_PUSH1_MODE);
+ assert(d->pfifo.cache1.channel_id < NV2A_NUM_CHANNELS);
+ break;
+ case NV_PFIFO_CACHE1_DMA_PUSH:
+ d->pfifo.cache1.dma_push_enabled =
+ GET_MASK(value, NV_PFIFO_CACHE1_DMA_PUSH_ACCESS);
+ if (d->pfifo.cache1.dma_push_suspended
+ && !GET_MASK(value, NV_PFIFO_CACHE1_DMA_PUSH_STATUS)) {
+ d->pfifo.cache1.dma_push_suspended = false;
+ pfifo_run_pusher(d);
+ }
+ d->pfifo.cache1.dma_push_suspended =
+ GET_MASK(value, NV_PFIFO_CACHE1_DMA_PUSH_STATUS);
+ break;
+ case NV_PFIFO_CACHE1_DMA_STATE:
+ d->pfifo.cache1.method_nonincreasing =
+ GET_MASK(value, NV_PFIFO_CACHE1_DMA_STATE_METHOD_TYPE);
+ d->pfifo.cache1.method =
+ GET_MASK(value, NV_PFIFO_CACHE1_DMA_STATE_METHOD) << 2;
+ d->pfifo.cache1.subchannel =
+ GET_MASK(value, NV_PFIFO_CACHE1_DMA_STATE_SUBCHANNEL);
+ d->pfifo.cache1.method_count =
+ GET_MASK(value, NV_PFIFO_CACHE1_DMA_STATE_METHOD_COUNT);
+ d->pfifo.cache1.error =
+ GET_MASK(value, NV_PFIFO_CACHE1_DMA_STATE_ERROR);
+ break;
+ case NV_PFIFO_CACHE1_DMA_INSTANCE:
+ d->pfifo.cache1.dma_instance =
+ GET_MASK(value, NV_PFIFO_CACHE1_DMA_INSTANCE_ADDRESS_MASK) << 4;
+ break;
+ case NV_PFIFO_CACHE1_DMA_PUT:
+ d->user.channel_control[d->pfifo.cache1.channel_id].dma_put = value;
+ break;
+ case NV_PFIFO_CACHE1_DMA_GET:
+ d->user.channel_control[d->pfifo.cache1.channel_id].dma_get = value;
+ break;
+ case NV_PFIFO_CACHE1_DMA_SUBROUTINE:
+ d->pfifo.cache1.subroutine_return =
+ (value & NV_PFIFO_CACHE1_DMA_SUBROUTINE_RETURN_OFFSET);
+ d->pfifo.cache1.subroutine_active =
+ (value & NV_PFIFO_CACHE1_DMA_SUBROUTINE_STATE);
+ break;
+ case NV_PFIFO_CACHE1_PULL0: {
+ std::unique_lock cache_unique_lock(d->pfifo.cache1.cache_lock);
+
+ if ((value & NV_PFIFO_CACHE1_PULL0_ACCESS)
+ && !d->pfifo.cache1.pull_enabled) {
+ d->pfifo.cache1.pull_enabled = true;
+
+ /* the puller thread should wake up */
+ d->pfifo.cache1.cache_cond.notify_all();
+ }
+ else if (!(value & NV_PFIFO_CACHE1_PULL0_ACCESS)
+ && d->pfifo.cache1.pull_enabled) {
+ d->pfifo.cache1.pull_enabled = false;
+ }
+
+ break;
+ }
+ case NV_PFIFO_CACHE1_ENGINE: {
+ std::unique_lock cache_unique_lock(d->pfifo.cache1.cache_lock);
+
+ for (i = 0; i < NV2A_NUM_SUBCHANNELS; i++) {
+ d->pfifo.cache1.bound_engines[i] = (FIFOEngine)((value >> (i * 2)) & 3);
+ }
+ break;
+ }
+ case NV_PFIFO_CACHE1_DMA_DCOUNT:
+ d->pfifo.cache1.dcount =
+ (value & NV_PFIFO_CACHE1_DMA_DCOUNT_VALUE);
+ break;
+ case NV_PFIFO_CACHE1_DMA_GET_JMP_SHADOW:
+ d->pfifo.cache1.get_jmp_shadow =
+ (value & NV_PFIFO_CACHE1_DMA_GET_JMP_SHADOW_OFFSET);
+ break;
+ case NV_PFIFO_CACHE1_DMA_RSVD_SHADOW:
+ d->pfifo.cache1.rsvd_shadow = value;
+ break;
+ case NV_PFIFO_CACHE1_DMA_DATA_SHADOW:
+ d->pfifo.cache1.data_shadow = value;
+ break;
+ default:
+ DEVICE_WRITE32_REG(pfifo); // Was : DEBUG_WRITE32_UNHANDLED(PFIFO);
+ break;
+ }
+
+ DEVICE_WRITE32_END(PFIFO);
+}
+
+
+/* pusher should be fine to run from a mimo handler
+* whenever's it's convenient */
+static void pfifo_run_pusher(NV2AState *d) {
+ uint8_t channel_id;
+ ChannelControl *control;
+ Cache1State *state;
+ CacheEntry *command;
+ uint8_t *dma;
+ xbaddr dma_len;
+ uint32_t word;
+
+ /* TODO: How is cache1 selected? */
+ state = &d->pfifo.cache1;
+ channel_id = state->channel_id;
+ control = &d->user.channel_control[channel_id];
+
+ if (!state->push_enabled)
+ return;
+
+
+ /* only handling DMA for now... */
+
+ /* Channel running DMA */
+ uint32_t channel_modes = d->pfifo.regs[NV_PFIFO_MODE];
+ assert(channel_modes & (1 << channel_id));
+ assert(state->mode == FIFO_DMA);
+
+ if (!state->dma_push_enabled)
+ return;
+ if (state->dma_push_suspended)
+ return;
+
+ /* We're running so there should be no pending errors... */
+ assert(state->error == NV_PFIFO_CACHE1_DMA_STATE_ERROR_NONE);
+
+ dma = (uint8_t*)nv_dma_map(d, state->dma_instance, &dma_len);
+
+ NV2A_DPRINTF("DMA pusher: max 0x%08X, 0x%08X - 0x%08X\n",
+ dma_len, control->dma_get, control->dma_put);
+
+ /* based on the convenient pseudocode in envytools */
+ while (control->dma_get != control->dma_put) {
+ if (control->dma_get >= dma_len) {
+
+ state->error = NV_PFIFO_CACHE1_DMA_STATE_ERROR_PROTECTION;
+ break;
+ }
+
+ word = ldl_le_p((uint32_t*)(dma + control->dma_get));
+ control->dma_get += 4;
+
+ if (state->method_count) {
+ /* data word of methods command */
+ state->data_shadow = word;
+
+ command = (CacheEntry*)g_malloc0(sizeof(CacheEntry));
+ command->method = state->method;
+ command->subchannel = state->subchannel;
+ command->nonincreasing = state->method_nonincreasing;
+ command->parameter = word;
+
+ {
+ std::unique_lock cache_unique_lock(d->pfifo.cache1.cache_lock); // UNTESTED
+ state->cache.push(command);
+ state->cache_cond.notify_all();
+ } // end of cache_unique_lock scope
+
+ if (!state->method_nonincreasing) {
+ state->method += 4;
+ }
+ state->method_count--;
+ state->dcount++;
+ } else {
+ /* no command active - this is the first word of a new one */
+ state->rsvd_shadow = word;
+ /* match all forms */
+ if ((word & 0xe0000003) == 0x20000000) {
+ /* old jump */
+ state->get_jmp_shadow = control->dma_get;
+ control->dma_get = word & 0x1fffffff;
+ NV2A_DPRINTF("pb OLD_JMP 0x%08X\n", control->dma_get);
+ } else if ((word & 3) == 1) {
+ /* jump */
+ state->get_jmp_shadow = control->dma_get;
+ control->dma_get = word & 0xfffffffc;
+ NV2A_DPRINTF("pb JMP 0x%08X\n", control->dma_get);
+ } else if ((word & 3) == 2) {
+ /* call */
+ if (state->subroutine_active) {
+ state->error = NV_PFIFO_CACHE1_DMA_STATE_ERROR_CALL;
+ break;
+ }
+ state->subroutine_return = control->dma_get;
+ state->subroutine_active = true;
+ control->dma_get = word & 0xfffffffc;
+ NV2A_DPRINTF("pb CALL 0x%08X\n", control->dma_get);
+ } else if (word == 0x00020000) {
+ /* return */
+ if (!state->subroutine_active) {
+ state->error = NV_PFIFO_CACHE1_DMA_STATE_ERROR_RETURN;
+ break;
+ }
+ control->dma_get = state->subroutine_return;
+ state->subroutine_active = false;
+ NV2A_DPRINTF("pb RET 0x%08X\n", control->dma_get);
+ } else if ((word & 0xe0030003) == 0) {
+ /* increasing methods */
+ state->method = word & 0x1fff;
+ state->subchannel = (word >> 13) & 7;
+ state->method_count = (word >> 18) & 0x7ff;
+ state->method_nonincreasing = false;
+ state->dcount = 0;
+ } else if ((word & 0xe0030003) == 0x40000000) {
+ /* non-increasing methods */
+ state->method = word & 0x1fff;
+ state->subchannel = (word >> 13) & 7;
+ state->method_count = (word >> 18) & 0x7ff;
+ state->method_nonincreasing = true;
+ state->dcount = 0;
+ } else {
+ NV2A_DPRINTF("pb reserved cmd 0x%08X - 0x%08X\n",
+ control->dma_get, word);
+ state->error = NV_PFIFO_CACHE1_DMA_STATE_ERROR_RESERVED_CMD;
+ break;
+ }
+ }
+ }
+
+ NV2A_DPRINTF("DMA pusher done: max 0x%08X, 0x%08X - 0x%08X\n",
+ dma_len, control->dma_get, control->dma_put);
+
+ if (state->error) {
+ NV2A_DPRINTF("pb error: %d\n", state->error);
+ assert(false);
+
+ state->dma_push_suspended = true;
+
+ d->pfifo.pending_interrupts |= NV_PFIFO_INTR_0_DMA_PUSHER;
+ update_irq(d);
+ }
+}
+
+int pfifo_puller_thread(NV2AState *d)
+{
+ CxbxSetThreadName("Cxbx NV2A FIFO");
+
+ Cache1State *state = &(d->pfifo.cache1);
+
+#ifdef COMPILE_OPENGL
+ glo_set_current(d->pgraph.gl_context);
+#endif
+
+ std::unique_lock cache_unique_lock(d->pfifo.cache1.cache_lock, std::defer_lock);
+
+ while (true) {
+
+ cache_unique_lock.lock();
+
+ while (state->cache.empty() || !state->pull_enabled) {
+ state->cache_cond.wait(cache_unique_lock);
+
+ if (d->exiting) {
+ cache_unique_lock.unlock(); // UNTESTED
+#ifdef COMPILE_OPENGL
+ glo_set_current(NULL);
+#endif
+ return 0;
+ }
+ }
+
+ // Copy cache to working_cache
+ while (!state->cache.empty()) {
+ state->working_cache.push(state->cache.front());
+ state->cache.pop();
+ }
+ cache_unique_lock.unlock(); // UNTESTED
+
+ d->pgraph.pgraph_lock.lock(); // UNTESTED
+
+ while (!state->working_cache.empty()) {
+ CacheEntry* command = state->working_cache.front();
+ state->working_cache.pop();
+
+ if (command->method == 0) {
+ // qemu_mutex_lock_iothread();
+ RAMHTEntry entry = ramht_lookup(d, command->parameter);
+ assert(entry.valid);
+
+ assert(entry.channel_id == state->channel_id);
+ // qemu_mutex_unlock_iothread();
+
+ switch (entry.engine) {
+ case ENGINE_GRAPHICS:
+ pgraph_context_switch(d, entry.channel_id);
+ pgraph_wait_fifo_access(d);
+ pgraph_method(d, command->subchannel, 0, entry.instance);
+ break;
+ default:
+ assert(false);
+ break;
+ }
+
+ /* the engine is bound to the subchannel */
+ cache_unique_lock.lock(); // UNTESTED
+ state->bound_engines[command->subchannel] = entry.engine;
+ state->last_engine = entry.engine;
+ cache_unique_lock.unlock(); // UNTESTED
+ } else if (command->method >= 0x100) {
+ /* method passed to engine */
+
+ uint32_t parameter = command->parameter;
+
+ /* methods that take objects.
+ * TODO: Check this range is correct for the nv2a */
+ if (command->method >= 0x180 && command->method < 0x200) {
+ //qemu_mutex_lock_iothread();
+ RAMHTEntry entry = ramht_lookup(d, parameter);
+ assert(entry.valid);
+ assert(entry.channel_id == state->channel_id);
+ parameter = entry.instance;
+ //qemu_mutex_unlock_iothread();
+ }
+
+ // state->cache_lock.lock();
+ enum FIFOEngine engine = state->bound_engines[command->subchannel];
+ // state->cache_lock.unlock();
+
+ switch (engine) {
+ case ENGINE_GRAPHICS:
+ pgraph_wait_fifo_access(d);
+ pgraph_method(d, command->subchannel,
+ command->method, parameter);
+ break;
+ default:
+ assert(false);
+ break;
+ }
+
+ // state->cache_lock.lock();
+ state->last_engine = state->bound_engines[command->subchannel];
+ // state->cache_lock.unlock();
+ }
+
+ g_free(command);
+ }
+
+ d->pgraph.pgraph_lock.unlock();
+ }
+
+ return 0;
+}
+
+static uint32_t ramht_hash(NV2AState *d, uint32_t handle)
+{
+ unsigned int ramht_size =
+ 1 << (GET_MASK(d->pfifo.regs[NV_PFIFO_RAMHT], NV_PFIFO_RAMHT_SIZE_MASK) + 12);
+
+ /* XXX: Think this is different to what nouveau calculates... */
+ unsigned int bits = ffs(ramht_size) - 2;
+
+ uint32_t hash = 0;
+ while (handle) {
+ hash ^= (handle & ((1 << bits) - 1));
+ handle >>= bits;
+ }
+ hash ^= d->pfifo.cache1.channel_id << (bits - 4);
+
+ return hash;
+}
+
+static RAMHTEntry ramht_lookup(NV2AState *d, uint32_t handle)
+{
+ unsigned int ramht_size =
+ 1 << (GET_MASK(d->pfifo.regs[NV_PFIFO_RAMHT], NV_PFIFO_RAMHT_SIZE_MASK) + 12);
+
+ uint32_t hash = ramht_hash(d, handle);
+ assert(hash * 8 < ramht_size);
+
+ uint32_t ramht_address =
+ GET_MASK(d->pfifo.regs[NV_PFIFO_RAMHT],
+ NV_PFIFO_RAMHT_BASE_ADDRESS_MASK) << 12;
+
+ uint8_t *entry_ptr = d->pramin.ramin_ptr + ramht_address + hash * 8;
+
+ uint32_t entry_handle = ldl_le_p((uint32_t*)entry_ptr);
+ uint32_t entry_context = ldl_le_p((uint32_t*)(entry_ptr + 4));
+
+ RAMHTEntry entry;
+ entry.handle = entry_handle;
+ entry.instance = (entry_context & NV_RAMHT_INSTANCE) << 4;
+ entry.engine = (FIFOEngine)((entry_context & NV_RAMHT_ENGINE) >> 16);
+ entry.channel_id = (entry_context & NV_RAMHT_CHID) >> 24;
+ entry.valid = entry_context & NV_RAMHT_STATUS;
+
+ return entry;
+}
diff --git a/src/devices/video/EmuNV2A_PGRAPH.cpp b/src/devices/video/EmuNV2A_PGRAPH.cpp
new file mode 100644
index 000000000..3726beb68
--- /dev/null
+++ b/src/devices/video/EmuNV2A_PGRAPH.cpp
@@ -0,0 +1,4609 @@
+// FIXME
+#if 1
+#define qemu_mutex_lock_iothread(...) do { \
+ d->io_lock.lock(); \
+} while (0)
+#define qemu_mutex_unlock_iothread(...) do { \
+ d->io_lock.unlock(); \
+} while (0)
+#else
+#define qemu_mutex_lock_iothread()
+#define qemu_mutex_unlock_iothread()
+#endif
+
+// Xbox uses 4 KiB pages
+#define TARGET_PAGE_MASK 0xfff
+#define TARGET_PAGE_ALIGN(x) (((x) + 0xfff) & ~0xfff)
+
+static const GLenum pgraph_texture_min_filter_map[] = {
+ 0,
+ GL_NEAREST,
+ GL_LINEAR,
+ GL_NEAREST_MIPMAP_NEAREST,
+ GL_LINEAR_MIPMAP_NEAREST,
+ GL_NEAREST_MIPMAP_LINEAR,
+ GL_LINEAR_MIPMAP_LINEAR,
+ GL_LINEAR, /* TODO: Convolution filter... */
+};
+
+static const GLenum pgraph_texture_mag_filter_map[] = {
+ 0,
+ GL_NEAREST,
+ GL_LINEAR,
+ 0,
+ GL_LINEAR /* TODO: Convolution filter... */
+};
+
+static const GLenum pgraph_texture_addr_map[] = {
+ 0,
+ GL_REPEAT,
+ GL_MIRRORED_REPEAT,
+ GL_CLAMP_TO_EDGE,
+ GL_CLAMP_TO_BORDER,
+ // GL_CLAMP
+};
+
+static const GLenum pgraph_blend_factor_map[] = {
+ GL_ZERO,
+ GL_ONE,
+ GL_SRC_COLOR,
+ GL_ONE_MINUS_SRC_COLOR,
+ GL_SRC_ALPHA,
+ GL_ONE_MINUS_SRC_ALPHA,
+ GL_DST_ALPHA,
+ GL_ONE_MINUS_DST_ALPHA,
+ GL_DST_COLOR,
+ GL_ONE_MINUS_DST_COLOR,
+ GL_SRC_ALPHA_SATURATE,
+ 0,
+ GL_CONSTANT_COLOR,
+ GL_ONE_MINUS_CONSTANT_COLOR,
+ GL_CONSTANT_ALPHA,
+ GL_ONE_MINUS_CONSTANT_ALPHA,
+};
+
+static const GLenum pgraph_blend_equation_map[] = {
+ GL_FUNC_SUBTRACT,
+ GL_FUNC_REVERSE_SUBTRACT,
+ GL_FUNC_ADD,
+ GL_MIN,
+ GL_MAX,
+ GL_FUNC_REVERSE_SUBTRACT,
+ GL_FUNC_ADD,
+};
+
+static const GLenum pgraph_blend_logicop_map[] = {
+ GL_CLEAR,
+ GL_AND,
+ GL_AND_REVERSE,
+ GL_COPY,
+ GL_AND_INVERTED,
+ GL_NOOP,
+ GL_XOR,
+ GL_OR,
+ GL_NOR,
+ GL_EQUIV,
+ GL_INVERT,
+ GL_OR_REVERSE,
+ GL_COPY_INVERTED,
+ GL_OR_INVERTED,
+ GL_NAND,
+ GL_SET,
+};
+
+static const GLenum pgraph_cull_face_map[] = {
+ 0,
+ GL_FRONT,
+ GL_BACK,
+ GL_FRONT_AND_BACK
+};
+
+static const GLenum pgraph_depth_func_map[] = {
+ GL_NEVER,
+ GL_LESS,
+ GL_EQUAL,
+ GL_LEQUAL,
+ GL_GREATER,
+ GL_NOTEQUAL,
+ GL_GEQUAL,
+ GL_ALWAYS,
+};
+
+static const GLenum pgraph_stencil_func_map[] = {
+ GL_NEVER,
+ GL_LESS,
+ GL_EQUAL,
+ GL_LEQUAL,
+ GL_GREATER,
+ GL_NOTEQUAL,
+ GL_GEQUAL,
+ GL_ALWAYS,
+};
+
+static const GLenum pgraph_stencil_op_map[] = {
+ 0,
+ GL_KEEP,
+ GL_ZERO,
+ GL_REPLACE,
+ GL_INCR,
+ GL_DECR,
+ GL_INVERT,
+ GL_INCR_WRAP,
+ GL_DECR_WRAP,
+};
+
+typedef struct ColorFormatInfo {
+ unsigned int bytes_per_pixel;
+ bool linear;
+ GLint gl_internal_format;
+ GLenum gl_format;
+ GLenum gl_type;
+ GLenum gl_swizzle_mask[4];
+} ColorFormatInfo;
+
+// Note : Avoid designated initializers to facilitate C++ builds
+static const ColorFormatInfo kelvin_color_format_map[256] = {
+ //0x00 [NV097_SET_TEXTURE_FORMAT_COLOR_SZ_Y8] =
+ {1, false, GL_R8, GL_RED, GL_UNSIGNED_BYTE,
+ {GL_RED, GL_RED, GL_RED, GL_ONE}},
+ //0x01 [NV097_SET_TEXTURE_FORMAT_COLOR_SZ_AY8] =
+ {1, false, GL_R8, GL_RED, GL_UNSIGNED_BYTE,
+ {GL_RED, GL_RED, GL_RED, GL_RED}},
+ //0x02 [NV097_SET_TEXTURE_FORMAT_COLOR_SZ_A1R5G5B5] =
+ {2, false, GL_RGB5_A1, GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV},
+ //0x03 [NV097_SET_TEXTURE_FORMAT_COLOR_SZ_X1R5G5B5] =
+ {2, false, GL_RGB5, GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV},
+ //0x04 [NV097_SET_TEXTURE_FORMAT_COLOR_SZ_A4R4G4B4] =
+ {2, false, GL_RGBA4, GL_BGRA, GL_UNSIGNED_SHORT_4_4_4_4_REV},
+ //0x05 [NV097_SET_TEXTURE_FORMAT_COLOR_SZ_R5G6B5] =
+ {2, false, GL_RGB565, GL_RGB, GL_UNSIGNED_SHORT_5_6_5},
+ //0x06 [NV097_SET_TEXTURE_FORMAT_COLOR_SZ_A8R8G8B8] =
+ {4, false, GL_RGBA8, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV},
+ //0x07 [NV097_SET_TEXTURE_FORMAT_COLOR_SZ_X8R8G8B8] =
+ {4, false, GL_RGB8, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV},
+ //0x08 [?] =
+ {},
+ //0x09 [?] =
+ {},
+ //0x0A [?] =
+ {},
+
+ /* paletted texture */
+ //0x0B [NV097_SET_TEXTURE_FORMAT_COLOR_SZ_I8_A8R8G8B8] =
+ {1, false, GL_RGBA8, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV},
+
+ //0x0C [NV097_SET_TEXTURE_FORMAT_COLOR_L_DXT1_A1R5G5B5] =
+ {4, false, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, 0, GL_RGBA},
+ //0x0D [?] =
+ {},
+ //0x0E [NV097_SET_TEXTURE_FORMAT_COLOR_L_DXT23_A8R8G8B8] =
+ {4, false, GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, 0, GL_RGBA},
+ //0x0F [NV097_SET_TEXTURE_FORMAT_COLOR_L_DXT45_A8R8G8B8] =
+ {4, false, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, 0, GL_RGBA},
+ //0x10 [NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_A1R5G5B5] =
+ {2, true, GL_RGB5_A1, GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV},
+ //0x11 [NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_R5G6B5] =
+ {2, true, GL_RGB565, GL_RGB, GL_UNSIGNED_SHORT_5_6_5},
+ //0x12 [NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_A8R8G8B8] =
+ {4, true, GL_RGBA8, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV},
+ //0x13 [NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_Y8] =
+ {1, true, GL_R8, GL_RED, GL_UNSIGNED_BYTE,
+ {GL_RED, GL_RED, GL_RED, GL_ONE}},
+ //0x14 [?] =
+ {},
+ //0x15 [?] =
+ {},
+ //0x16 [?] =
+ {},
+ //0x17 [?] =
+ {},
+ //0x18 [?] =
+ {},
+
+ //0x19 [NV097_SET_TEXTURE_FORMAT_COLOR_SZ_A8] =
+ {1, false, GL_R8, GL_RED, GL_UNSIGNED_BYTE,
+ {GL_ONE, GL_ONE, GL_ONE, GL_RED}},
+ //0x1A [NV097_SET_TEXTURE_FORMAT_COLOR_SZ_A8Y8] =
+ {2, false, GL_RG8, GL_RG, GL_UNSIGNED_BYTE,
+ {GL_GREEN, GL_GREEN, GL_GREEN, GL_RED}},
+ //0x1B [NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_AY8] =
+ {1, true, GL_R8, GL_RED, GL_UNSIGNED_BYTE,
+ {GL_RED, GL_RED, GL_RED, GL_RED}},
+ //0x1C [NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_X1R5G5B5] =
+ {2, true, GL_RGB5, GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV},
+ //0x1D [NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_A4R4G4B4] =
+ {2, false, GL_RGBA4, GL_BGRA, GL_UNSIGNED_SHORT_4_4_4_4_REV},
+ //0x1E [NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_X8R8G8B8] =
+ {4, true, GL_RGB8, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV},
+ //0x1F [NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_A8] =
+ {1, true, GL_R8, GL_RED, GL_UNSIGNED_BYTE,
+ {GL_ONE, GL_ONE, GL_ONE, GL_RED}},
+ //0x20 [NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_A8Y8] =
+ {2, true, GL_RG8, GL_RG, GL_UNSIGNED_BYTE,
+ {GL_GREEN, GL_GREEN, GL_GREEN, GL_RED}},
+ //0x21 [?] =
+ {},
+ //0x22 [?] =
+ {},
+ //0x23 [?] =
+ {},
+ //0x24 [NV097_SET_TEXTURE_FORMAT_COLOR_LC_IMAGE_CR8YB8CB8YA8] =
+ { 2, true, GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV }, // TODO: format conversion
+ //0x25 [?] =
+ {},
+ //0x26 [?] =
+ {},
+
+ //0x27 [NV097_SET_TEXTURE_FORMAT_COLOR_SZ_R6G5B5] =
+ {2, false, GL_RGB8_SNORM, GL_RGB, GL_BYTE}, /* FIXME: This might be signed */
+ //0x28 [NV097_SET_TEXTURE_FORMAT_COLOR_SZ_G8B8] =
+ {2, false, GL_RG8_SNORM, GL_RG, GL_BYTE, /* FIXME: This might be signed */
+ {GL_ZERO, GL_RED, GL_GREEN, GL_ONE}},
+ //0x29 [NV097_SET_TEXTURE_FORMAT_COLOR_SZ_R8B8] =
+ {2, false, GL_RG8_SNORM, GL_RG, GL_BYTE, /* FIXME: This might be signed */
+ {GL_RED, GL_ZERO, GL_GREEN, GL_ONE}},
+ //0x2A [?] =
+ {},
+ //0x2B [?] =
+ {},
+ //0x2C [?] =
+ {},
+ //0x2D [?] =
+ {},
+
+
+ /* TODO: format conversion */
+ //0x2E [NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_DEPTH_X8_Y24_FIXED] =
+ {4, true, GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8},
+ //0x2F [?] =
+ {},
+ //0x30 [NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_DEPTH_Y16_FIXED] =
+ {2, true, GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT},
+ //0x31 [?] =
+ {},
+ //0x32 [?] =
+ {},
+ //0x33 [?] =
+ {},
+ //0x34 [?] =
+ {},
+ //0x35 [NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_Y16] =
+ {2, true, GL_R16, GL_RED, GL_UNSIGNED_SHORT,
+ {GL_RED, GL_RED, GL_RED, GL_ONE}},
+ //0x36 [?] =
+ {},
+ //0x37 [?] =
+ {},
+ //0x38 [?] =
+ {},
+ //0x39 [?] =
+ {},
+ //0x3A [NV097_SET_TEXTURE_FORMAT_COLOR_SZ_A8B8G8R8] =
+ {4, false, GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV},
+ //0x3B [?] =
+ {},
+
+ //0x3C [NV097_SET_TEXTURE_FORMAT_COLOR_SZ_R8G8B8A8] =
+ {4, false, GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8},
+ //0x3D [?] =
+ {},
+ //0x3E [?] =
+ {},
+
+ //0x3F [NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_A8B8G8R8] =
+ {4, true, GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV},
+ //0x40 [NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_B8G8R8A8] =
+ {4, true, GL_RGBA8, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8},
+ //0x41 [NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_R8G8B8A8] =
+ {4, true, GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8},
+};
+
+typedef struct SurfaceColorFormatInfo {
+ unsigned int bytes_per_pixel;
+ GLint gl_internal_format;
+ GLenum gl_format;
+ GLenum gl_type;
+} SurfaceColorFormatInfo;
+
+// Note : Avoid designated initializers to facilitate C++ builds
+static const SurfaceColorFormatInfo kelvin_surface_color_format_map[16] = {
+ //0x00 [?] =
+ {},
+ //0x01 [NV097_SET_SURFACE_FORMAT_COLOR_LE_X1R5G5B5_Z1R5G5B5] =
+ {2, GL_RGB5_A1, GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV},
+ //0x02 [NV097_SET_SURFACE_FORMAT_COLOR_LE_X1R5G5B5_O1R5G5B5] =
+ {},
+ //0x03 [NV097_SET_SURFACE_FORMAT_COLOR_LE_R5G6B5] =
+ {2, GL_RGB565, GL_RGB, GL_UNSIGNED_SHORT_5_6_5},
+ //0x04 [NV097_SET_SURFACE_FORMAT_COLOR_LE_X8R8G8B8_Z8R8G8B8] =
+ {4, GL_RGBA8, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV},
+ //0x05 [NV097_SET_SURFACE_FORMAT_COLOR_LE_X8R8G8B8_O8R8G8B8] =
+ {},
+ //0x06 [NV097_SET_SURFACE_FORMAT_COLOR_LE_X1A7R8G8B8_Z1A7R8G8B8] =
+ {},
+ //0x07 [NV097_SET_SURFACE_FORMAT_COLOR_LE_X1A7R8G8B8_O1A7R8G8B8] =
+ {},
+ //0x08 [NV097_SET_SURFACE_FORMAT_COLOR_LE_A8R8G8B8] =
+ {4, GL_RGBA8, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV},
+ //0x09 [NV097_SET_SURFACE_FORMAT_COLOR_LE_B8] =
+ {}, // TODO : {1, GL_R8, GL_R8, GL_UNSIGNED_BYTE}, // PatrickvL guesstimate
+ //0x0A [NV097_SET_SURFACE_FORMAT_COLOR_LE_G8B8] =
+ {},
+ //0x0B [?] =
+ {},
+ //0x0C [?] =
+ {},
+ //0x0D [?] =
+ {},
+ //0x0E [?] =
+ {},
+ //0x0F [?] =
+ {}
+};
+
+static void pgraph_context_switch(NV2AState *d, unsigned int channel_id);
+static void pgraph_set_context_user(NV2AState *d, uint32_t value);
+static void pgraph_wait_fifo_access(NV2AState *d);
+static void pgraph_method(NV2AState *d, unsigned int subchannel, unsigned int method, uint32_t parameter);
+static void pgraph_allocate_inline_buffer_vertices(PGRAPHState *pg, unsigned int attr);
+static void pgraph_finish_inline_buffer_vertex(PGRAPHState *pg);
+#ifdef COMPILE_OPENGL
+static void pgraph_shader_update_constants(PGRAPHState *pg, ShaderBinding *binding, bool binding_changed, bool vertex_program, bool fixed_function);
+#endif // COMPILE_OPENGL
+static void pgraph_bind_shaders(PGRAPHState *pg);
+static bool pgraph_framebuffer_dirty(PGRAPHState *pg);
+static bool pgraph_color_write_enabled(PGRAPHState *pg);
+static bool pgraph_zeta_write_enabled(PGRAPHState *pg);
+static void pgraph_set_surface_dirty(PGRAPHState *pg, bool color, bool zeta);
+static void pgraph_update_surface_part(NV2AState *d, bool upload, bool color);
+static void pgraph_update_surface(NV2AState *d, bool upload, bool color_write, bool zeta_write);
+static void pgraph_bind_textures(NV2AState *d);
+static void pgraph_apply_anti_aliasing_factor(PGRAPHState *pg, unsigned int *width, unsigned int *height);
+static void pgraph_get_surface_dimensions(PGRAPHState *pg, unsigned int *width, unsigned int *height);
+static void pgraph_update_memory_buffer(NV2AState *d, hwaddr addr, hwaddr size, bool f);
+static void pgraph_bind_vertex_attributes(NV2AState *d, unsigned int num_elements, bool inline_data, unsigned int inline_stride);
+static unsigned int pgraph_bind_inline_array(NV2AState *d);
+static void load_graphics_object(NV2AState *d, hwaddr instance_address, GraphicsObject *obj);
+static GraphicsObject* lookup_graphics_object(PGRAPHState *s, hwaddr instance_address);
+static float convert_f16_to_float(uint16_t f16);
+static float convert_f24_to_float(uint32_t f24);
+static uint8_t cliptobyte(int x);
+static void convert_yuy2_to_rgb(const uint8_t *line, unsigned int ix, uint8_t *r, uint8_t *g, uint8_t* b);
+static uint8_t* convert_texture_data(const TextureShape s, const uint8_t *data, const uint8_t *palette_data, unsigned int width, unsigned int height, unsigned int depth, unsigned int row_pitch, unsigned int slice_pitch);
+static void upload_gl_texture(GLenum gl_target, const TextureShape s, const uint8_t *texture_data, const uint8_t *palette_data);
+static TextureBinding* generate_texture(const TextureShape s, const uint8_t *texture_data, const uint8_t *palette_data);
+#ifdef COMPILE_OPENGL
+static guint texture_key_hash(gconstpointer key);
+static gboolean texture_key_equal(gconstpointer a, gconstpointer b);
+static gpointer texture_key_retrieve(gpointer key, gpointer user_data, GError **error);
+static void texture_key_destroy(gpointer data);
+static void texture_binding_destroy(gpointer data);
+static guint shader_hash(gconstpointer key);
+static gboolean shader_equal(gconstpointer a, gconstpointer b);
+#endif // COMPILE_OPENGL
+static unsigned int kelvin_map_stencil_op(uint32_t parameter);
+static unsigned int kelvin_map_polygon_mode(uint32_t parameter);
+static unsigned int kelvin_map_texgen(uint32_t parameter, unsigned int channel);
+static void pgraph_method_log(unsigned int subchannel, unsigned int graphics_class, unsigned int method, uint32_t parameter);
+static uint64_t fnv_hash(const uint8_t *data, size_t len);
+static uint64_t fast_hash(const uint8_t *data, size_t len, unsigned int samples);
+
+/* PGRAPH - accelerated 2d/3d drawing engine */
+DEVICE_READ32(PGRAPH)
+{
+ d->pgraph.pgraph_lock.lock();
+
+ DEVICE_READ32_SWITCH() {
+ case NV_PGRAPH_INTR:
+ result = d->pgraph.pending_interrupts;
+ break;
+ case NV_PGRAPH_INTR_EN:
+ result = d->pgraph.enabled_interrupts;
+ break;
+ case NV_PGRAPH_NSOURCE:
+ result = d->pgraph.notify_source;
+ break;
+ case NV_PGRAPH_CTX_USER:
+ SET_MASK(result, NV_PGRAPH_CTX_USER_CHANNEL_3D,
+ d->pgraph.context[d->pgraph.channel_id].channel_3d);
+ SET_MASK(result, NV_PGRAPH_CTX_USER_CHANNEL_3D_VALID, 1);
+ SET_MASK(result, NV_PGRAPH_CTX_USER_SUBCH,
+ d->pgraph.context[d->pgraph.channel_id].subchannel << 13);
+ SET_MASK(result, NV_PGRAPH_CTX_USER_CHID, d->pgraph.channel_id);
+ break;
+ case NV_PGRAPH_TRAPPED_ADDR:
+ SET_MASK(result, NV_PGRAPH_TRAPPED_ADDR_CHID, d->pgraph.trapped_channel_id);
+ SET_MASK(result, NV_PGRAPH_TRAPPED_ADDR_SUBCH, d->pgraph.trapped_subchannel);
+ SET_MASK(result, NV_PGRAPH_TRAPPED_ADDR_MTHD, d->pgraph.trapped_method);
+ break;
+ case NV_PGRAPH_TRAPPED_DATA_LOW:
+ result = d->pgraph.trapped_data[0];
+ break;
+ case NV_PGRAPH_FIFO:
+ SET_MASK(result, NV_PGRAPH_FIFO_ACCESS, d->pgraph.fifo_access);
+ break;
+ case NV_PGRAPH_CHANNEL_CTX_TABLE:
+ result = d->pgraph.context_table >> 4;
+ break;
+ case NV_PGRAPH_CHANNEL_CTX_POINTER:
+ result = d->pgraph.context_address >> 4;
+ break;
+ default:
+ DEVICE_READ32_REG(pgraph); // Was : DEBUG_READ32_UNHANDLED(PGRAPH);
+ }
+
+ d->pgraph.pgraph_lock.unlock();
+
+// reg_log_read(NV_PGRAPH, addr, r);
+
+ DEVICE_READ32_END(PGRAPH);
+}
+
+static void pgraph_set_context_user(NV2AState *d, uint32_t value)
+{
+ d->pgraph.channel_id = (value & NV_PGRAPH_CTX_USER_CHID) >> 24;
+
+ d->pgraph.context[d->pgraph.channel_id].channel_3d =
+ GET_MASK(value, NV_PGRAPH_CTX_USER_CHANNEL_3D);
+ d->pgraph.context[d->pgraph.channel_id].subchannel =
+ GET_MASK(value, NV_PGRAPH_CTX_USER_SUBCH);
+}
+
+DEVICE_WRITE32(PGRAPH)
+{
+// reg_log_write(NV_PGRAPH, addr, val);
+
+ d->pgraph.pgraph_lock.lock();
+
+ switch (addr) {
+ case NV_PGRAPH_INTR:
+ d->pgraph.pending_interrupts &= ~value;
+ d->pgraph.interrupt_cond.notify_all();
+ break;
+ case NV_PGRAPH_INTR_EN:
+ d->pgraph.enabled_interrupts = value;
+ break;
+ case NV_PGRAPH_CTX_CONTROL:
+ d->pgraph.channel_valid = (value & NV_PGRAPH_CTX_CONTROL_CHID);
+ break;
+ case NV_PGRAPH_CTX_USER:
+ pgraph_set_context_user(d, value);
+ break;
+ case NV_PGRAPH_INCREMENT:
+ if (value & NV_PGRAPH_INCREMENT_READ_3D) {
+ SET_MASK(d->pgraph.regs[NV_PGRAPH_SURFACE],
+ NV_PGRAPH_SURFACE_READ_3D,
+ (GET_MASK(d->pgraph.regs[NV_PGRAPH_SURFACE],
+ NV_PGRAPH_SURFACE_READ_3D) + 1)
+ % GET_MASK(d->pgraph.regs[NV_PGRAPH_SURFACE],
+ NV_PGRAPH_SURFACE_MODULO_3D));
+ d->pgraph.flip_3d_cond.notify_all();
+ }
+ break;
+ case NV_PGRAPH_FIFO:
+ d->pgraph.fifo_access = GET_MASK(value, NV_PGRAPH_FIFO_ACCESS);
+ d->pgraph.fifo_access_cond.notify_all();
+ break;
+ case NV_PGRAPH_CHANNEL_CTX_TABLE:
+ d->pgraph.context_table =
+ (value & NV_PGRAPH_CHANNEL_CTX_TABLE_INST) << 4;
+ break;
+ case NV_PGRAPH_CHANNEL_CTX_POINTER:
+ d->pgraph.context_address =
+ (value & NV_PGRAPH_CHANNEL_CTX_POINTER_INST) << 4;
+ break;
+ case NV_PGRAPH_CHANNEL_CTX_TRIGGER:
+
+ if (value & NV_PGRAPH_CHANNEL_CTX_TRIGGER_READ_IN) {
+ NV2A_DPRINTF("PGRAPH: read channel %d context from %0x08X\n",
+ d->pgraph.channel_id, d->pgraph.context_address);
+
+ uint8_t *context_ptr = d->pramin.ramin_ptr + d->pgraph.context_address;
+ uint32_t context_user = ldl_le_p((uint32_t*)context_ptr);
+
+ NV2A_DPRINTF(" - CTX_USER = 0x%x\n", context_user);
+
+
+ pgraph_set_context_user(d, context_user);
+ }
+ if (value & NV_PGRAPH_CHANNEL_CTX_TRIGGER_WRITE_OUT) {
+ /* do stuff ... */
+ }
+
+ break;
+ default:
+ DEVICE_WRITE32_REG(pgraph); // Was : DEBUG_WRITE32_UNHANDLED(PGRAPH);
+ break;
+ }
+
+ d->pgraph.pgraph_lock.unlock();
+
+ DEVICE_WRITE32_END(PGRAPH);
+}
+
+static void pgraph_method(NV2AState *d,
+ unsigned int subchannel,
+ unsigned int method,
+ uint32_t parameter)
+{
+#ifdef COMPILE_OPENGL
+ unsigned int i;
+#endif
+
+ GraphicsSubchannel *subchannel_data;
+ GraphicsObject *object;
+
+ unsigned int slot;
+
+ PGRAPHState *pg = &d->pgraph;
+
+ assert(pg->channel_valid);
+ subchannel_data = &pg->subchannel_data[subchannel];
+ object = &subchannel_data->object;
+
+ ContextSurfaces2DState *context_surfaces_2d
+ = &object->data.context_surfaces_2d;
+ ImageBlitState *image_blit = &object->data.image_blit;
+ KelvinState *kelvin = &object->data.kelvin;
+
+
+
+ pgraph_method_log(subchannel, object->graphics_class, method, parameter);
+
+ if (method == NV_SET_OBJECT) {
+ subchannel_data->object_instance = parameter;
+
+ //qemu_mutex_lock_iothread();
+ load_graphics_object(d, parameter, object);
+ //qemu_mutex_unlock_iothread();
+ return;
+ }
+
+ /* ugly switch for now */
+ switch (object->graphics_class) {
+
+ case NV_CONTEXT_SURFACES_2D: {
+ switch (method) {
+ case NV062_SET_CONTEXT_DMA_IMAGE_SOURCE:
+ context_surfaces_2d->dma_image_source = parameter;
+ break;
+ case NV062_SET_CONTEXT_DMA_IMAGE_DESTIN:
+ context_surfaces_2d->dma_image_dest = parameter;
+ break;
+ case NV062_SET_COLOR_FORMAT:
+ context_surfaces_2d->color_format = parameter;
+ break;
+ case NV062_SET_PITCH:
+ context_surfaces_2d->source_pitch = parameter & 0xFFFF;
+ context_surfaces_2d->dest_pitch = parameter >> 16;
+ break;
+ case NV062_SET_OFFSET_SOURCE:
+ context_surfaces_2d->source_offset = parameter & 0x07FFFFFF;
+ break;
+ case NV062_SET_OFFSET_DESTIN:
+ context_surfaces_2d->dest_offset = parameter & 0x07FFFFFF;
+ break;
+ default:
+ EmuWarning("EmuNV2A: Unknown NV_CONTEXT_SURFACES_2D Method: 0x%08X\n", method);
+ }
+
+ break;
+ }
+
+ case NV_IMAGE_BLIT: {
+ switch (method) {
+ case NV09F_SET_CONTEXT_SURFACES:
+ image_blit->context_surfaces = parameter;
+ break;
+ case NV09F_SET_OPERATION:
+ image_blit->operation = parameter;
+ break;
+ case NV09F_CONTROL_POINT_IN:
+ image_blit->in_x = parameter & 0xFFFF;
+ image_blit->in_y = parameter >> 16;
+ break;
+ case NV09F_CONTROL_POINT_OUT:
+ image_blit->out_x = parameter & 0xFFFF;
+ image_blit->out_y = parameter >> 16;
+ break;
+ case NV09F_SIZE:
+ image_blit->width = parameter & 0xFFFF;
+ image_blit->height = parameter >> 16;
+
+ /* I guess this kicks it off? */
+ if (image_blit->operation == NV09F_SET_OPERATION_SRCCOPY) {
+
+ NV2A_GL_DPRINTF("NV09F_SET_OPERATION_SRCCOPY\n");
+
+ GraphicsObject *context_surfaces_obj =
+ lookup_graphics_object(pg, image_blit->context_surfaces);
+ assert(context_surfaces_obj);
+ assert(context_surfaces_obj->graphics_class
+ == NV_CONTEXT_SURFACES_2D);
+
+ ContextSurfaces2DState *context_surfaces =
+ &context_surfaces_obj->data.context_surfaces_2d;
+
+ unsigned int bytes_per_pixel;
+ switch (context_surfaces->color_format) {
+ case NV062_SET_COLOR_FORMAT_LE_Y8:
+ bytes_per_pixel = 1;
+ break;
+ case NV062_SET_COLOR_FORMAT_LE_R5G6B5:
+ bytes_per_pixel = 2;
+ break;
+ case NV062_SET_COLOR_FORMAT_LE_A8R8G8B8:
+ bytes_per_pixel = 4;
+ break;
+ default:
+ printf("Unknown blit surface format: 0x%x\n", context_surfaces->color_format);
+ assert(false);
+ break;
+ }
+
+ xbaddr source_dma_len, dest_dma_len;
+ uint8_t *source, *dest;
+
+ source = (uint8_t*)nv_dma_map(d, context_surfaces->dma_image_source,
+ &source_dma_len);
+ assert(context_surfaces->source_offset < source_dma_len);
+ source += context_surfaces->source_offset;
+
+ dest = (uint8_t*)nv_dma_map(d, context_surfaces->dma_image_dest,
+ &dest_dma_len);
+ assert(context_surfaces->dest_offset < dest_dma_len);
+ dest += context_surfaces->dest_offset;
+
+ NV2A_DPRINTF(" - 0x%tx -> 0x%tx\n", source - d->vram_ptr,
+ dest - d->vram_ptr);
+
+ unsigned int y;
+ for (y = 0; yheight; y++) {
+ uint8_t *source_row = source
+ + (image_blit->in_y + y) * context_surfaces->source_pitch
+ + image_blit->in_x * bytes_per_pixel;
+
+ uint8_t *dest_row = dest
+ + (image_blit->out_y + y) * context_surfaces->dest_pitch
+ + image_blit->out_x * bytes_per_pixel;
+
+ memmove(dest_row, source_row,
+ image_blit->width * bytes_per_pixel);
+ }
+
+ } else {
+ assert(false);
+ }
+
+ break;
+ default:
+ EmuWarning("EmuNV2A: Unknown NV_IMAGE_BLIT Method: 0x%08X\n", method);
+ }
+ break;
+ }
+
+ case NV_KELVIN_PRIMITIVE: {
+ switch (method) {
+ case NV097_NO_OPERATION:
+ /* The bios uses nop as a software method call -
+ * it seems to expect a notify interrupt if the parameter isn't 0.
+ * According to a nouveau guy it should still be a nop regardless
+ * of the parameter. It's possible a debug register enables this,
+ * but nothing obvious sticks out. Weird.
+ */
+ if (parameter != 0) {
+ assert(!(pg->pending_interrupts & NV_PGRAPH_INTR_ERROR));
+
+
+ pg->trapped_channel_id = pg->channel_id;
+ pg->trapped_subchannel = subchannel;
+ pg->trapped_method = method;
+ pg->trapped_data[0] = parameter;
+ pg->notify_source = NV_PGRAPH_NSOURCE_NOTIFICATION; /* TODO: check this */
+ pg->pending_interrupts |= NV_PGRAPH_INTR_ERROR;
+
+ pg->pgraph_lock.unlock();
+ qemu_mutex_lock_iothread();
+ update_irq(d);
+ pg->pgraph_lock.lock();
+ qemu_mutex_unlock_iothread();
+
+ while (pg->pending_interrupts & NV_PGRAPH_INTR_ERROR) {
+ pg->interrupt_cond.wait(pg->pgraph_lock);
+ }
+ }
+ break;
+
+ case NV097_WAIT_FOR_IDLE:
+ pgraph_update_surface(d, false, true, true);
+ break;
+
+
+ case NV097_SET_FLIP_READ:
+ SET_MASK(pg->regs[NV_PGRAPH_SURFACE], NV_PGRAPH_SURFACE_READ_3D,
+ parameter);
+ break;
+ case NV097_SET_FLIP_WRITE:
+ SET_MASK(pg->regs[NV_PGRAPH_SURFACE], NV_PGRAPH_SURFACE_WRITE_3D,
+ parameter);
+ break;
+ case NV097_SET_FLIP_MODULO:
+ SET_MASK(pg->regs[NV_PGRAPH_SURFACE], NV_PGRAPH_SURFACE_MODULO_3D,
+ parameter);
+ break;
+ case NV097_FLIP_INCREMENT_WRITE: {
+ NV2A_DPRINTF("flip increment write %d -> ",
+ GET_MASK(pg->regs[NV_PGRAPH_SURFACE],
+ NV_PGRAPH_SURFACE_WRITE_3D));
+ SET_MASK(pg->regs[NV_PGRAPH_SURFACE],
+ NV_PGRAPH_SURFACE_WRITE_3D,
+ (GET_MASK(pg->regs[NV_PGRAPH_SURFACE],
+ NV_PGRAPH_SURFACE_WRITE_3D) + 1)
+ % GET_MASK(pg->regs[NV_PGRAPH_SURFACE],
+ NV_PGRAPH_SURFACE_MODULO_3D));
+ NV2A_DPRINTF("%d\n",
+ GET_MASK(pg->regs[NV_PGRAPH_SURFACE],
+ NV_PGRAPH_SURFACE_WRITE_3D));
+
+#ifdef COMPILE_OPENGL
+ if (glFrameTerminatorGREMEDY) {
+ glFrameTerminatorGREMEDY();
+ }
+#endif // COMPILE_OPENGL
+ break;
+ }
+ case NV097_FLIP_STALL:
+#ifdef COMPILE_OPENGL
+ // HACK HACK HACK
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, pg->gl_framebuffer);
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
+ glBlitFramebuffer(0, 0, 640, 480, 0, 0, 640, 480, GL_COLOR_BUFFER_BIT, GL_NEAREST);
+ SDL_GL_SwapWindow(d->sdl_window); // ugh
+ assert(glGetError() == GL_NO_ERROR);
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, pg->gl_framebuffer);
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, pg->gl_framebuffer);
+ glBindFramebuffer(GL_FRAMEBUFFER, pg->gl_framebuffer);
+ // HACK HACK HACK
+#endif // COMPILE_OPENGL
+
+ pgraph_update_surface(d, false, true, true);
+
+ while (true) {
+ NV2A_DPRINTF("flip stall read: %d, write: %d, modulo: %d\n",
+ GET_MASK(pg->regs[NV_PGRAPH_SURFACE], NV_PGRAPH_SURFACE_READ_3D),
+ GET_MASK(pg->regs[NV_PGRAPH_SURFACE], NV_PGRAPH_SURFACE_WRITE_3D),
+ GET_MASK(pg->regs[NV_PGRAPH_SURFACE], NV_PGRAPH_SURFACE_MODULO_3D));
+
+ uint32_t s = pg->regs[NV_PGRAPH_SURFACE];
+ if (GET_MASK(s, NV_PGRAPH_SURFACE_READ_3D)
+ != GET_MASK(s, NV_PGRAPH_SURFACE_WRITE_3D)) {
+ break;
+ }
+
+ pg->flip_3d_cond.wait(pg->pgraph_lock);
+ }
+ NV2A_DPRINTF("flip stall done\n");
+ break;
+
+ case NV097_SET_CONTEXT_DMA_NOTIFIES:
+ kelvin->dma_notifies = parameter;
+ break;
+ case NV097_SET_CONTEXT_DMA_A:
+ pg->dma_a = parameter;
+ break;
+ case NV097_SET_CONTEXT_DMA_B:
+ pg->dma_b = parameter;
+ break;
+ case NV097_SET_CONTEXT_DMA_STATE:
+ kelvin->dma_state = parameter;
+ break;
+ case NV097_SET_CONTEXT_DMA_COLOR:
+ /* try to get any straggling draws in before the surface's changed :/ */
+ pgraph_update_surface(d, false, true, true);
+
+ pg->dma_color = parameter;
+ break;
+ case NV097_SET_CONTEXT_DMA_ZETA:
+ pg->dma_zeta = parameter;
+ break;
+ case NV097_SET_CONTEXT_DMA_VERTEX_A:
+ pg->dma_vertex_a = parameter;
+ break;
+ case NV097_SET_CONTEXT_DMA_VERTEX_B:
+ pg->dma_vertex_b = parameter;
+ break;
+ case NV097_SET_CONTEXT_DMA_SEMAPHORE:
+ kelvin->dma_semaphore = parameter;
+ break;
+ case NV097_SET_CONTEXT_DMA_REPORT:
+ pg->dma_report = parameter;
+ break;
+
+ case NV097_SET_SURFACE_CLIP_HORIZONTAL:
+ pgraph_update_surface(d, false, true, true);
+
+ pg->surface_shape.clip_x =
+ GET_MASK(parameter, NV097_SET_SURFACE_CLIP_HORIZONTAL_X);
+ pg->surface_shape.clip_width =
+ GET_MASK(parameter, NV097_SET_SURFACE_CLIP_HORIZONTAL_WIDTH);
+ break;
+ case NV097_SET_SURFACE_CLIP_VERTICAL:
+ pgraph_update_surface(d, false, true, true);
+
+ pg->surface_shape.clip_y =
+ GET_MASK(parameter, NV097_SET_SURFACE_CLIP_VERTICAL_Y);
+ pg->surface_shape.clip_height =
+ GET_MASK(parameter, NV097_SET_SURFACE_CLIP_VERTICAL_HEIGHT);
+ break;
+ case NV097_SET_SURFACE_FORMAT:
+ pgraph_update_surface(d, false, true, true);
+
+ pg->surface_shape.color_format =
+ GET_MASK(parameter, NV097_SET_SURFACE_FORMAT_COLOR);
+ pg->surface_shape.zeta_format =
+ GET_MASK(parameter, NV097_SET_SURFACE_FORMAT_ZETA);
+ pg->surface_type =
+ GET_MASK(parameter, NV097_SET_SURFACE_FORMAT_TYPE);
+ pg->surface_shape.anti_aliasing =
+ GET_MASK(parameter, NV097_SET_SURFACE_FORMAT_ANTI_ALIASING);
+ pg->surface_shape.log_width =
+ GET_MASK(parameter, NV097_SET_SURFACE_FORMAT_WIDTH);
+ pg->surface_shape.log_height =
+ GET_MASK(parameter, NV097_SET_SURFACE_FORMAT_HEIGHT);
+ break;
+ case NV097_SET_SURFACE_PITCH:
+ pgraph_update_surface(d, false, true, true);
+
+ pg->surface_color.pitch =
+ GET_MASK(parameter, NV097_SET_SURFACE_PITCH_COLOR);
+ pg->surface_zeta.pitch =
+ GET_MASK(parameter, NV097_SET_SURFACE_PITCH_ZETA);
+ break;
+ case NV097_SET_SURFACE_COLOR_OFFSET:
+ pgraph_update_surface(d, false, true, true);
+
+ pg->surface_color.offset = parameter;
+ break;
+ case NV097_SET_SURFACE_ZETA_OFFSET:
+ pgraph_update_surface(d, false, true, true);
+
+ pg->surface_zeta.offset = parameter;
+ break;
+
+ CASE_8(NV097_SET_COMBINER_ALPHA_ICW, 4) :
+ slot = (method - NV097_SET_COMBINER_ALPHA_ICW) / 4;
+ pg->regs[NV_PGRAPH_COMBINEALPHAI0 + slot * 4] = parameter;
+ break;
+
+ case NV097_SET_COMBINER_SPECULAR_FOG_CW0:
+ pg->regs[NV_PGRAPH_COMBINESPECFOG0] = parameter;
+ break;
+
+ case NV097_SET_COMBINER_SPECULAR_FOG_CW1:
+ pg->regs[NV_PGRAPH_COMBINESPECFOG1] = parameter;
+ break;
+
+ CASE_4(NV097_SET_TEXTURE_ADDRESS, 64):
+ slot = (method - NV097_SET_TEXTURE_ADDRESS) / 64;
+ pg->regs[NV_PGRAPH_TEXADDRESS0 + slot * 4] = parameter;
+ break;
+ case NV097_SET_CONTROL0: {
+ pgraph_update_surface(d, false, true, true);
+
+ bool stencil_write_enable =
+ parameter & NV097_SET_CONTROL0_STENCIL_WRITE_ENABLE;
+ SET_MASK(pg->regs[NV_PGRAPH_CONTROL_0],
+ NV_PGRAPH_CONTROL_0_STENCIL_WRITE_ENABLE,
+ stencil_write_enable);
+
+ uint32_t z_format = GET_MASK(parameter, NV097_SET_CONTROL0_Z_FORMAT);
+ SET_MASK(pg->regs[NV_PGRAPH_SETUPRASTER],
+ NV_PGRAPH_SETUPRASTER_Z_FORMAT, z_format);
+
+ bool z_perspective =
+ parameter & NV097_SET_CONTROL0_Z_PERSPECTIVE_ENABLE;
+ SET_MASK(pg->regs[NV_PGRAPH_CONTROL_0],
+ NV_PGRAPH_CONTROL_0_Z_PERSPECTIVE_ENABLE,
+ z_perspective);
+ break;
+ }
+
+ case NV097_SET_FOG_MODE: {
+ /* FIXME: There is also NV_PGRAPH_CSV0_D_FOG_MODE */
+ unsigned int mode;
+ switch (parameter) {
+ case NV097_SET_FOG_MODE_V_LINEAR:
+ mode = NV_PGRAPH_CONTROL_3_FOG_MODE_LINEAR; break;
+ case NV097_SET_FOG_MODE_V_EXP:
+ mode = NV_PGRAPH_CONTROL_3_FOG_MODE_EXP; break;
+ case NV097_SET_FOG_MODE_V_EXP2:
+ mode = NV_PGRAPH_CONTROL_3_FOG_MODE_EXP2; break;
+ case NV097_SET_FOG_MODE_V_EXP_ABS:
+ mode = NV_PGRAPH_CONTROL_3_FOG_MODE_EXP_ABS; break;
+ case NV097_SET_FOG_MODE_V_EXP2_ABS:
+ mode = NV_PGRAPH_CONTROL_3_FOG_MODE_EXP2_ABS; break;
+ case NV097_SET_FOG_MODE_V_LINEAR_ABS:
+ mode = NV_PGRAPH_CONTROL_3_FOG_MODE_LINEAR_ABS; break;
+ default:
+ assert(false);
+ break;
+ }
+ SET_MASK(pg->regs[NV_PGRAPH_CONTROL_3], NV_PGRAPH_CONTROL_3_FOG_MODE,
+ mode);
+ break;
+ }
+ case NV097_SET_FOG_GEN_MODE: {
+ unsigned int mode;
+ switch (parameter) {
+ case NV097_SET_FOG_GEN_MODE_V_SPEC_ALPHA:
+ mode = NV_PGRAPH_CSV0_D_FOGGENMODE_SPEC_ALPHA; break;
+ case NV097_SET_FOG_GEN_MODE_V_RADIAL:
+ mode = NV_PGRAPH_CSV0_D_FOGGENMODE_RADIAL; break;
+ case NV097_SET_FOG_GEN_MODE_V_PLANAR:
+ mode = NV_PGRAPH_CSV0_D_FOGGENMODE_PLANAR; break;
+ case NV097_SET_FOG_GEN_MODE_V_ABS_PLANAR:
+ mode = NV_PGRAPH_CSV0_D_FOGGENMODE_ABS_PLANAR; break;
+ case NV097_SET_FOG_GEN_MODE_V_FOG_X:
+ mode = NV_PGRAPH_CSV0_D_FOGGENMODE_FOG_X; break;
+ default:
+ assert(false);
+ break;
+ }
+ SET_MASK(pg->regs[NV_PGRAPH_CSV0_D], NV_PGRAPH_CSV0_D_FOGGENMODE, mode);
+ break;
+ }
+ case NV097_SET_FOG_ENABLE:
+ /*
+ FIXME: There is also:
+ SET_MASK(pg->regs[NV_PGRAPH_CSV0_D], NV_PGRAPH_CSV0_D_FOGENABLE,
+ parameter);
+ */
+ SET_MASK(pg->regs[NV_PGRAPH_CONTROL_3], NV_PGRAPH_CONTROL_3_FOGENABLE,
+ parameter);
+ break;
+ case NV097_SET_FOG_COLOR: {
+ /* PGRAPH channels are ARGB, parameter channels are ABGR */
+ uint8_t red = GET_MASK(parameter, NV097_SET_FOG_COLOR_RED);
+ uint8_t green = GET_MASK(parameter, NV097_SET_FOG_COLOR_GREEN);
+ uint8_t blue = GET_MASK(parameter, NV097_SET_FOG_COLOR_BLUE);
+ uint8_t alpha = GET_MASK(parameter, NV097_SET_FOG_COLOR_ALPHA);
+ SET_MASK(pg->regs[NV_PGRAPH_FOGCOLOR], NV_PGRAPH_FOGCOLOR_RED, red);
+ SET_MASK(pg->regs[NV_PGRAPH_FOGCOLOR], NV_PGRAPH_FOGCOLOR_GREEN, green);
+ SET_MASK(pg->regs[NV_PGRAPH_FOGCOLOR], NV_PGRAPH_FOGCOLOR_BLUE, blue);
+ SET_MASK(pg->regs[NV_PGRAPH_FOGCOLOR], NV_PGRAPH_FOGCOLOR_ALPHA, alpha);
+ break;
+ }
+ case NV097_SET_ALPHA_TEST_ENABLE:
+ SET_MASK(pg->regs[NV_PGRAPH_CONTROL_0],
+ NV_PGRAPH_CONTROL_0_ALPHATESTENABLE, parameter);
+ break;
+ case NV097_SET_BLEND_ENABLE:
+ SET_MASK(pg->regs[NV_PGRAPH_BLEND], NV_PGRAPH_BLEND_EN, parameter);
+ break;
+ case NV097_SET_CULL_FACE_ENABLE:
+ SET_MASK(pg->regs[NV_PGRAPH_SETUPRASTER],
+ NV_PGRAPH_SETUPRASTER_CULLENABLE,
+ parameter);
+ break;
+ case NV097_SET_DEPTH_TEST_ENABLE:
+ SET_MASK(pg->regs[NV_PGRAPH_CONTROL_0], NV_PGRAPH_CONTROL_0_ZENABLE,
+ parameter);
+ break;
+ case NV097_SET_DITHER_ENABLE:
+ SET_MASK(pg->regs[NV_PGRAPH_CONTROL_0],
+ NV_PGRAPH_CONTROL_0_DITHERENABLE, parameter);
+ break;
+ case NV097_SET_LIGHTING_ENABLE:
+ SET_MASK(pg->regs[NV_PGRAPH_CSV0_C], NV_PGRAPH_CSV0_C_LIGHTING,
+ parameter);
+ break;
+ case NV097_SET_SKIN_MODE:
+ SET_MASK(pg->regs[NV_PGRAPH_CSV0_D], NV_PGRAPH_CSV0_D_SKIN,
+ parameter);
+ break;
+ case NV097_SET_STENCIL_TEST_ENABLE:
+ SET_MASK(pg->regs[NV_PGRAPH_CONTROL_1],
+ NV_PGRAPH_CONTROL_1_STENCIL_TEST_ENABLE, parameter);
+ break;
+ case NV097_SET_POLY_OFFSET_POINT_ENABLE:
+ SET_MASK(pg->regs[NV_PGRAPH_SETUPRASTER],
+ NV_PGRAPH_SETUPRASTER_POFFSETPOINTENABLE, parameter);
+ break;
+ case NV097_SET_POLY_OFFSET_LINE_ENABLE:
+ SET_MASK(pg->regs[NV_PGRAPH_SETUPRASTER],
+ NV_PGRAPH_SETUPRASTER_POFFSETLINEENABLE, parameter);
+ break;
+ case NV097_SET_POLY_OFFSET_FILL_ENABLE:
+ SET_MASK(pg->regs[NV_PGRAPH_SETUPRASTER],
+ NV_PGRAPH_SETUPRASTER_POFFSETFILLENABLE, parameter);
+ break;
+ case NV097_SET_ALPHA_FUNC:
+ SET_MASK(pg->regs[NV_PGRAPH_CONTROL_0],
+ NV_PGRAPH_CONTROL_0_ALPHAFUNC, parameter & 0xF);
+ break;
+ case NV097_SET_ALPHA_REF:
+ SET_MASK(pg->regs[NV_PGRAPH_CONTROL_0],
+ NV_PGRAPH_CONTROL_0_ALPHAREF, parameter);
+ break;
+ case NV097_SET_BLEND_FUNC_SFACTOR: {
+ unsigned int factor;
+ switch (parameter) {
+ case NV097_SET_BLEND_FUNC_SFACTOR_V_ZERO:
+ factor = NV_PGRAPH_BLEND_SFACTOR_ZERO; break;
+ case NV097_SET_BLEND_FUNC_SFACTOR_V_ONE:
+ factor = NV_PGRAPH_BLEND_SFACTOR_ONE; break;
+ case NV097_SET_BLEND_FUNC_SFACTOR_V_SRC_COLOR:
+ factor = NV_PGRAPH_BLEND_SFACTOR_SRC_COLOR; break;
+ case NV097_SET_BLEND_FUNC_SFACTOR_V_ONE_MINUS_SRC_COLOR:
+ factor = NV_PGRAPH_BLEND_SFACTOR_ONE_MINUS_SRC_COLOR; break;
+ case NV097_SET_BLEND_FUNC_SFACTOR_V_SRC_ALPHA:
+ factor = NV_PGRAPH_BLEND_SFACTOR_SRC_ALPHA; break;
+ case NV097_SET_BLEND_FUNC_SFACTOR_V_ONE_MINUS_SRC_ALPHA:
+ factor = NV_PGRAPH_BLEND_SFACTOR_ONE_MINUS_SRC_ALPHA; break;
+ case NV097_SET_BLEND_FUNC_SFACTOR_V_DST_ALPHA:
+ factor = NV_PGRAPH_BLEND_SFACTOR_DST_ALPHA; break;
+ case NV097_SET_BLEND_FUNC_SFACTOR_V_ONE_MINUS_DST_ALPHA:
+ factor = NV_PGRAPH_BLEND_SFACTOR_ONE_MINUS_DST_ALPHA; break;
+ case NV097_SET_BLEND_FUNC_SFACTOR_V_DST_COLOR:
+ factor = NV_PGRAPH_BLEND_SFACTOR_DST_COLOR; break;
+ case NV097_SET_BLEND_FUNC_SFACTOR_V_ONE_MINUS_DST_COLOR:
+ factor = NV_PGRAPH_BLEND_SFACTOR_ONE_MINUS_DST_COLOR; break;
+ case NV097_SET_BLEND_FUNC_SFACTOR_V_SRC_ALPHA_SATURATE:
+ factor = NV_PGRAPH_BLEND_SFACTOR_SRC_ALPHA_SATURATE; break;
+ case NV097_SET_BLEND_FUNC_SFACTOR_V_CONSTANT_COLOR:
+ factor = NV_PGRAPH_BLEND_SFACTOR_CONSTANT_COLOR; break;
+ case NV097_SET_BLEND_FUNC_SFACTOR_V_ONE_MINUS_CONSTANT_COLOR:
+ factor = NV_PGRAPH_BLEND_SFACTOR_ONE_MINUS_CONSTANT_COLOR; break;
+ case NV097_SET_BLEND_FUNC_SFACTOR_V_CONSTANT_ALPHA:
+ factor = NV_PGRAPH_BLEND_SFACTOR_CONSTANT_ALPHA; break;
+ case NV097_SET_BLEND_FUNC_SFACTOR_V_ONE_MINUS_CONSTANT_ALPHA:
+ factor = NV_PGRAPH_BLEND_SFACTOR_ONE_MINUS_CONSTANT_ALPHA; break;
+ default:
+ fprintf(stderr, "Unknown blend source factor: 0x%x\n", parameter);
+ assert(false);
+ break;
+ }
+ SET_MASK(pg->regs[NV_PGRAPH_BLEND], NV_PGRAPH_BLEND_SFACTOR, factor);
+
+ break;
+ }
+
+ case NV097_SET_BLEND_FUNC_DFACTOR: {
+ unsigned int factor;
+ switch (parameter) {
+ case NV097_SET_BLEND_FUNC_DFACTOR_V_ZERO:
+ factor = NV_PGRAPH_BLEND_DFACTOR_ZERO; break;
+ case NV097_SET_BLEND_FUNC_DFACTOR_V_ONE:
+ factor = NV_PGRAPH_BLEND_DFACTOR_ONE; break;
+ case NV097_SET_BLEND_FUNC_DFACTOR_V_SRC_COLOR:
+ factor = NV_PGRAPH_BLEND_DFACTOR_SRC_COLOR; break;
+ case NV097_SET_BLEND_FUNC_DFACTOR_V_ONE_MINUS_SRC_COLOR:
+ factor = NV_PGRAPH_BLEND_DFACTOR_ONE_MINUS_SRC_COLOR; break;
+ case NV097_SET_BLEND_FUNC_DFACTOR_V_SRC_ALPHA:
+ factor = NV_PGRAPH_BLEND_DFACTOR_SRC_ALPHA; break;
+ case NV097_SET_BLEND_FUNC_DFACTOR_V_ONE_MINUS_SRC_ALPHA:
+ factor = NV_PGRAPH_BLEND_DFACTOR_ONE_MINUS_SRC_ALPHA; break;
+ case NV097_SET_BLEND_FUNC_DFACTOR_V_DST_ALPHA:
+ factor = NV_PGRAPH_BLEND_DFACTOR_DST_ALPHA; break;
+ case NV097_SET_BLEND_FUNC_DFACTOR_V_ONE_MINUS_DST_ALPHA:
+ factor = NV_PGRAPH_BLEND_DFACTOR_ONE_MINUS_DST_ALPHA; break;
+ case NV097_SET_BLEND_FUNC_DFACTOR_V_DST_COLOR:
+ factor = NV_PGRAPH_BLEND_DFACTOR_DST_COLOR; break;
+ case NV097_SET_BLEND_FUNC_DFACTOR_V_ONE_MINUS_DST_COLOR:
+ factor = NV_PGRAPH_BLEND_DFACTOR_ONE_MINUS_DST_COLOR; break;
+ case NV097_SET_BLEND_FUNC_DFACTOR_V_SRC_ALPHA_SATURATE:
+ factor = NV_PGRAPH_BLEND_DFACTOR_SRC_ALPHA_SATURATE; break;
+ case NV097_SET_BLEND_FUNC_DFACTOR_V_CONSTANT_COLOR:
+ factor = NV_PGRAPH_BLEND_DFACTOR_CONSTANT_COLOR; break;
+ case NV097_SET_BLEND_FUNC_DFACTOR_V_ONE_MINUS_CONSTANT_COLOR:
+ factor = NV_PGRAPH_BLEND_DFACTOR_ONE_MINUS_CONSTANT_COLOR; break;
+ case NV097_SET_BLEND_FUNC_DFACTOR_V_CONSTANT_ALPHA:
+ factor = NV_PGRAPH_BLEND_DFACTOR_CONSTANT_ALPHA; break;
+ case NV097_SET_BLEND_FUNC_DFACTOR_V_ONE_MINUS_CONSTANT_ALPHA:
+ factor = NV_PGRAPH_BLEND_DFACTOR_ONE_MINUS_CONSTANT_ALPHA; break;
+ default:
+ fprintf(stderr, "Unknown blend destination factor: 0x%x\n", parameter);
+ assert(false);
+ break;
+ }
+ SET_MASK(pg->regs[NV_PGRAPH_BLEND], NV_PGRAPH_BLEND_DFACTOR, factor);
+
+ break;
+ }
+
+ case NV097_SET_BLEND_COLOR:
+ pg->regs[NV_PGRAPH_BLENDCOLOR] = parameter;
+ break;
+
+ case NV097_SET_BLEND_EQUATION: {
+ unsigned int equation;
+ switch (parameter) {
+ case NV097_SET_BLEND_EQUATION_V_FUNC_SUBTRACT:
+ equation = 0; break;
+ case NV097_SET_BLEND_EQUATION_V_FUNC_REVERSE_SUBTRACT:
+ equation = 1; break;
+ case NV097_SET_BLEND_EQUATION_V_FUNC_ADD:
+ equation = 2; break;
+ case NV097_SET_BLEND_EQUATION_V_MIN:
+ equation = 3; break;
+ case NV097_SET_BLEND_EQUATION_V_MAX:
+ equation = 4; break;
+ case NV097_SET_BLEND_EQUATION_V_FUNC_REVERSE_SUBTRACT_SIGNED:
+ equation = 5; break;
+ case NV097_SET_BLEND_EQUATION_V_FUNC_ADD_SIGNED:
+ equation = 6; break;
+ default:
+ assert(false);
+ break;
+ }
+ SET_MASK(pg->regs[NV_PGRAPH_BLEND], NV_PGRAPH_BLEND_EQN, equation);
+
+ break;
+ }
+
+ case NV097_SET_DEPTH_FUNC:
+ SET_MASK(pg->regs[NV_PGRAPH_CONTROL_0], NV_PGRAPH_CONTROL_0_ZFUNC,
+ parameter & 0xF);
+ break;
+
+ case NV097_SET_COLOR_MASK: {
+ pg->surface_color.write_enabled_cache |= pgraph_color_write_enabled(pg);
+
+ bool alpha = parameter & NV097_SET_COLOR_MASK_ALPHA_WRITE_ENABLE;
+ bool red = parameter & NV097_SET_COLOR_MASK_RED_WRITE_ENABLE;
+ bool green = parameter & NV097_SET_COLOR_MASK_GREEN_WRITE_ENABLE;
+ bool blue = parameter & NV097_SET_COLOR_MASK_BLUE_WRITE_ENABLE;
+ SET_MASK(pg->regs[NV_PGRAPH_CONTROL_0],
+ NV_PGRAPH_CONTROL_0_ALPHA_WRITE_ENABLE, alpha);
+ SET_MASK(pg->regs[NV_PGRAPH_CONTROL_0],
+ NV_PGRAPH_CONTROL_0_RED_WRITE_ENABLE, red);
+ SET_MASK(pg->regs[NV_PGRAPH_CONTROL_0],
+ NV_PGRAPH_CONTROL_0_GREEN_WRITE_ENABLE, green);
+ SET_MASK(pg->regs[NV_PGRAPH_CONTROL_0],
+ NV_PGRAPH_CONTROL_0_BLUE_WRITE_ENABLE, blue);
+ break;
+ }
+ case NV097_SET_DEPTH_MASK:
+ pg->surface_zeta.write_enabled_cache |= pgraph_zeta_write_enabled(pg);
+
+ SET_MASK(pg->regs[NV_PGRAPH_CONTROL_0],
+ NV_PGRAPH_CONTROL_0_ZWRITEENABLE, parameter);
+ break;
+ case NV097_SET_STENCIL_MASK:
+ SET_MASK(pg->regs[NV_PGRAPH_CONTROL_1],
+ NV_PGRAPH_CONTROL_1_STENCIL_MASK_WRITE, parameter);
+ break;
+ case NV097_SET_STENCIL_FUNC:
+ SET_MASK(pg->regs[NV_PGRAPH_CONTROL_1],
+ NV_PGRAPH_CONTROL_1_STENCIL_FUNC, parameter & 0xF);
+ break;
+ case NV097_SET_STENCIL_FUNC_REF:
+ SET_MASK(pg->regs[NV_PGRAPH_CONTROL_1],
+ NV_PGRAPH_CONTROL_1_STENCIL_REF, parameter);
+ break;
+ case NV097_SET_STENCIL_FUNC_MASK:
+ SET_MASK(pg->regs[NV_PGRAPH_CONTROL_1],
+ NV_PGRAPH_CONTROL_1_STENCIL_MASK_READ, parameter);
+ break;
+ case NV097_SET_STENCIL_OP_FAIL:
+ SET_MASK(pg->regs[NV_PGRAPH_CONTROL_2],
+ NV_PGRAPH_CONTROL_2_STENCIL_OP_FAIL,
+ kelvin_map_stencil_op(parameter));
+ break;
+ case NV097_SET_STENCIL_OP_ZFAIL:
+ SET_MASK(pg->regs[NV_PGRAPH_CONTROL_2],
+ NV_PGRAPH_CONTROL_2_STENCIL_OP_ZFAIL,
+ kelvin_map_stencil_op(parameter));
+ break;
+ case NV097_SET_STENCIL_OP_ZPASS:
+ SET_MASK(pg->regs[NV_PGRAPH_CONTROL_2],
+ NV_PGRAPH_CONTROL_2_STENCIL_OP_ZPASS,
+ kelvin_map_stencil_op(parameter));
+ break;
+
+ case NV097_SET_POLYGON_OFFSET_SCALE_FACTOR:
+ pg->regs[NV_PGRAPH_ZOFFSETFACTOR] = parameter;
+ break;
+ case NV097_SET_POLYGON_OFFSET_BIAS:
+ pg->regs[NV_PGRAPH_ZOFFSETBIAS] = parameter;
+ break;
+ case NV097_SET_FRONT_POLYGON_MODE:
+ SET_MASK(pg->regs[NV_PGRAPH_SETUPRASTER],
+ NV_PGRAPH_SETUPRASTER_FRONTFACEMODE,
+ kelvin_map_polygon_mode(parameter));
+ break;
+ case NV097_SET_BACK_POLYGON_MODE:
+ SET_MASK(pg->regs[NV_PGRAPH_SETUPRASTER],
+ NV_PGRAPH_SETUPRASTER_BACKFACEMODE,
+ kelvin_map_polygon_mode(parameter));
+ break;
+ case NV097_SET_CLIP_MIN:
+ pg->regs[NV_PGRAPH_ZCLIPMIN] = parameter;
+ break;
+ case NV097_SET_CLIP_MAX:
+ pg->regs[NV_PGRAPH_ZCLIPMAX] = parameter;
+ break;
+ case NV097_SET_CULL_FACE: {
+ unsigned int face;
+ switch (parameter) {
+ case NV097_SET_CULL_FACE_V_FRONT:
+ face = NV_PGRAPH_SETUPRASTER_CULLCTRL_FRONT; break;
+ case NV097_SET_CULL_FACE_V_BACK:
+ face = NV_PGRAPH_SETUPRASTER_CULLCTRL_BACK; break;
+ case NV097_SET_CULL_FACE_V_FRONT_AND_BACK:
+ face = NV_PGRAPH_SETUPRASTER_CULLCTRL_FRONT_AND_BACK; break;
+ default:
+ assert(false);
+ break;
+ }
+ SET_MASK(pg->regs[NV_PGRAPH_SETUPRASTER],
+ NV_PGRAPH_SETUPRASTER_CULLCTRL,
+ face);
+ break;
+ }
+ case NV097_SET_FRONT_FACE: {
+ bool ccw;
+ switch (parameter) {
+ case NV097_SET_FRONT_FACE_V_CW:
+ ccw = false; break;
+ case NV097_SET_FRONT_FACE_V_CCW:
+ ccw = true; break;
+ default:
+ fprintf(stderr, "Unknown front face: 0x%x\n", parameter);
+ assert(false);
+ break;
+ }
+ SET_MASK(pg->regs[NV_PGRAPH_SETUPRASTER],
+ NV_PGRAPH_SETUPRASTER_FRONTFACE,
+ ccw ? 1 : 0);
+ break;
+ }
+ case NV097_SET_NORMALIZATION_ENABLE:
+ SET_MASK(pg->regs[NV_PGRAPH_CSV0_C],
+ NV_PGRAPH_CSV0_C_NORMALIZATION_ENABLE,
+ parameter);
+ break;
+
+ case NV097_SET_LIGHT_ENABLE_MASK:
+ SET_MASK(pg->regs[NV_PGRAPH_CSV0_D],
+ NV_PGRAPH_CSV0_D_LIGHTS,
+ parameter);
+ break;
+
+ CASE_4(NV097_SET_TEXGEN_S, 16) : {
+ slot = (method - NV097_SET_TEXGEN_S) / 16;
+ unsigned int reg = (slot < 2) ? NV_PGRAPH_CSV1_A
+ : NV_PGRAPH_CSV1_B;
+ unsigned int mask = (slot % 2) ? NV_PGRAPH_CSV1_A_T1_S
+ : NV_PGRAPH_CSV1_A_T0_S;
+ SET_MASK(pg->regs[reg], mask, kelvin_map_texgen(parameter, 0));
+ break;
+ }
+ CASE_4(NV097_SET_TEXGEN_T, 16) : {
+ slot = (method - NV097_SET_TEXGEN_T) / 16;
+ unsigned int reg = (slot < 2) ? NV_PGRAPH_CSV1_A
+ : NV_PGRAPH_CSV1_B;
+ unsigned int mask = (slot % 2) ? NV_PGRAPH_CSV1_A_T1_T
+ : NV_PGRAPH_CSV1_A_T0_T;
+ SET_MASK(pg->regs[reg], mask, kelvin_map_texgen(parameter, 1));
+ break;
+ }
+ CASE_4(NV097_SET_TEXGEN_R, 16) : {
+ slot = (method - NV097_SET_TEXGEN_R) / 16;
+ unsigned int reg = (slot < 2) ? NV_PGRAPH_CSV1_A
+ : NV_PGRAPH_CSV1_B;
+ unsigned int mask = (slot % 2) ? NV_PGRAPH_CSV1_A_T1_R
+ : NV_PGRAPH_CSV1_A_T0_R;
+ SET_MASK(pg->regs[reg], mask, kelvin_map_texgen(parameter, 2));
+ break;
+ }
+ CASE_4(NV097_SET_TEXGEN_Q, 16) : {
+ slot = (method - NV097_SET_TEXGEN_Q) / 16;
+ unsigned int reg = (slot < 2) ? NV_PGRAPH_CSV1_A
+ : NV_PGRAPH_CSV1_B;
+ unsigned int mask = (slot % 2) ? NV_PGRAPH_CSV1_A_T1_Q
+ : NV_PGRAPH_CSV1_A_T0_Q;
+ SET_MASK(pg->regs[reg], mask, kelvin_map_texgen(parameter, 3));
+ break;
+ }
+ CASE_4(NV097_SET_TEXTURE_MATRIX_ENABLE, 4) :
+ slot = (method - NV097_SET_TEXTURE_MATRIX_ENABLE) / 4;
+ pg->texture_matrix_enable[slot] = parameter;
+ break;
+
+ CASE_16(NV097_SET_PROJECTION_MATRIX, 4) : {
+ slot = (method - NV097_SET_PROJECTION_MATRIX) / 4;
+ // pg->projection_matrix[slot] = *(float*)¶meter;
+ unsigned int row = NV_IGRAPH_XF_XFCTX_PMAT0 + slot / 4;
+ pg->vsh_constants[row][slot % 4] = parameter;
+ pg->vsh_constants_dirty[row] = true;
+ break;
+ }
+
+ CASE_64(NV097_SET_MODEL_VIEW_MATRIX, 4) : {
+ slot = (method - NV097_SET_MODEL_VIEW_MATRIX) / 4;
+ unsigned int matnum = slot / 16;
+ unsigned int entry = slot % 16;
+ unsigned int row = NV_IGRAPH_XF_XFCTX_MMAT0 + matnum * 8 + entry / 4;
+ pg->vsh_constants[row][entry % 4] = parameter;
+ pg->vsh_constants_dirty[row] = true;
+ break;
+ }
+
+ CASE_64(NV097_SET_INVERSE_MODEL_VIEW_MATRIX, 4) : {
+ slot = (method - NV097_SET_INVERSE_MODEL_VIEW_MATRIX) / 4;
+ unsigned int matnum = slot / 16;
+ unsigned int entry = slot % 16;
+ unsigned int row = NV_IGRAPH_XF_XFCTX_IMMAT0 + matnum * 8 + entry / 4;
+ pg->vsh_constants[row][entry % 4] = parameter;
+ pg->vsh_constants_dirty[row] = true;
+ break;
+ }
+
+ CASE_16(NV097_SET_COMPOSITE_MATRIX, 4) : {
+ slot = (method - NV097_SET_COMPOSITE_MATRIX) / 4;
+ unsigned int row = NV_IGRAPH_XF_XFCTX_CMAT0 + slot / 4;
+ pg->vsh_constants[row][slot % 4] = parameter;
+ pg->vsh_constants_dirty[row] = true;
+ break;
+ }
+
+ CASE_64(NV097_SET_TEXTURE_MATRIX, 4) : {
+ slot = (method - NV097_SET_TEXTURE_MATRIX) / 4;
+ unsigned int tex = slot / 16;
+ unsigned int entry = slot % 16;
+ unsigned int row = NV_IGRAPH_XF_XFCTX_T0MAT + tex * 8 + entry / 4;
+ pg->vsh_constants[row][entry % 4] = parameter;
+ pg->vsh_constants_dirty[row] = true;
+ break;
+ }
+
+ CASE_3(NV097_SET_FOG_PARAMS, 4) :
+ slot = (method - NV097_SET_FOG_PARAMS) / 4;
+ pg->regs[NV_PGRAPH_FOGPARAM0 + slot * 4] = parameter;
+ /* Cxbx note: slot = 2 is right after slot = 1 */
+ pg->ltctxa[NV_IGRAPH_XF_LTCTXA_FOG_K][slot] = parameter;
+ pg->ltctxa_dirty[NV_IGRAPH_XF_LTCTXA_FOG_K] = true;
+ break;
+
+ /* Handles NV097_SET_TEXGEN_PLANE_S,T,R,Q */
+ CASE_64(NV097_SET_TEXGEN_PLANE_S, 4) : {
+ slot = (method - NV097_SET_TEXGEN_PLANE_S) / 4;
+ unsigned int tex = slot / 16;
+ unsigned int entry = slot % 16;
+ unsigned int row = NV_IGRAPH_XF_XFCTX_TG0MAT + tex * 8 + entry / 4;
+ pg->vsh_constants[row][entry % 4] = parameter;
+ pg->vsh_constants_dirty[row] = true;
+ break;
+ }
+
+ case NV097_SET_TEXGEN_VIEW_MODEL:
+ SET_MASK(pg->regs[NV_PGRAPH_CSV0_D], NV_PGRAPH_CSV0_D_TEXGEN_REF,
+ parameter);
+ break;
+
+ CASE_4(NV097_SET_FOG_PLANE, 4):
+ slot = (method - NV097_SET_FOG_PLANE) / 4;
+ pg->vsh_constants[NV_IGRAPH_XF_XFCTX_FOG][slot] = parameter;
+ pg->vsh_constants_dirty[NV_IGRAPH_XF_XFCTX_FOG] = true;
+ break;
+
+ CASE_3(NV097_SET_SCENE_AMBIENT_COLOR, 4):
+ slot = (method - NV097_SET_SCENE_AMBIENT_COLOR) / 4;
+ // ??
+ pg->ltctxa[NV_IGRAPH_XF_LTCTXA_FR_AMB][slot] = parameter;
+ pg->ltctxa_dirty[NV_IGRAPH_XF_LTCTXA_FR_AMB] = true;
+ break;
+
+ CASE_4(NV097_SET_VIEWPORT_OFFSET, 4):
+ slot = (method - NV097_SET_VIEWPORT_OFFSET) / 4;
+ pg->vsh_constants[NV_IGRAPH_XF_XFCTX_VPOFF][slot] = parameter;
+ pg->vsh_constants_dirty[NV_IGRAPH_XF_XFCTX_VPOFF] = true;
+ break;
+
+ CASE_4(NV097_SET_EYE_POSITION, 4):
+ slot = (method - NV097_SET_EYE_POSITION) / 4;
+ pg->vsh_constants[NV_IGRAPH_XF_XFCTX_EYEP][slot] = parameter;
+ pg->vsh_constants_dirty[NV_IGRAPH_XF_XFCTX_EYEP] = true;
+ break;
+
+ CASE_8(NV097_SET_COMBINER_FACTOR0, 4):
+ slot = (method - NV097_SET_COMBINER_FACTOR0) / 4;
+ pg->regs[NV_PGRAPH_COMBINEFACTOR0 + slot * 4] = parameter;
+ break;
+
+ CASE_8(NV097_SET_COMBINER_FACTOR1, 4):
+ slot = (method - NV097_SET_COMBINER_FACTOR1) / 4;
+ pg->regs[NV_PGRAPH_COMBINEFACTOR1 + slot * 4] = parameter;
+ break;
+
+ CASE_8(NV097_SET_COMBINER_ALPHA_OCW, 4):
+ slot = (method - NV097_SET_COMBINER_ALPHA_OCW) / 4;
+ pg->regs[NV_PGRAPH_COMBINEALPHAO0 + slot * 4] = parameter;
+ break;
+
+ CASE_8(NV097_SET_COMBINER_COLOR_ICW, 4):
+ slot = (method - NV097_SET_COMBINER_COLOR_ICW) / 4;
+ pg->regs[NV_PGRAPH_COMBINECOLORI0 + slot * 4] = parameter;
+ break;
+
+ CASE_4(NV097_SET_VIEWPORT_SCALE, 4):
+ slot = (method - NV097_SET_VIEWPORT_SCALE) / 4;
+ pg->vsh_constants[NV_IGRAPH_XF_XFCTX_VPSCL][slot] = parameter;
+ pg->vsh_constants_dirty[NV_IGRAPH_XF_XFCTX_VPSCL] = true;
+ break;
+
+ CASE_32(NV097_SET_TRANSFORM_PROGRAM, 4) : {
+
+ slot = (method - NV097_SET_TRANSFORM_PROGRAM) / 4;
+
+ int program_load = GET_MASK(pg->regs[NV_PGRAPH_CHEOPS_OFFSET],
+ NV_PGRAPH_CHEOPS_OFFSET_PROG_LD_PTR);
+
+ assert(program_load < NV2A_MAX_TRANSFORM_PROGRAM_LENGTH);
+ pg->program_data[program_load][slot % 4] = parameter;
+
+ if (slot % 4 == 3) {
+ SET_MASK(pg->regs[NV_PGRAPH_CHEOPS_OFFSET],
+ NV_PGRAPH_CHEOPS_OFFSET_PROG_LD_PTR, program_load + 1);
+ }
+
+ break;
+ }
+
+ CASE_32(NV097_SET_TRANSFORM_CONSTANT, 4): {
+
+ slot = (method - NV097_SET_TRANSFORM_CONSTANT) / 4;
+
+ int const_load = GET_MASK(pg->regs[NV_PGRAPH_CHEOPS_OFFSET],
+ NV_PGRAPH_CHEOPS_OFFSET_CONST_LD_PTR);
+
+ assert(const_load < NV2A_VERTEXSHADER_CONSTANTS);
+ // VertexShaderConstant *constant = &pg->constants[const_load];
+ pg->vsh_constants_dirty[const_load] |=
+ (parameter != pg->vsh_constants[const_load][slot%4]);
+ pg->vsh_constants[const_load][slot%4] = parameter;
+
+ if (slot % 4 == 3) {
+ SET_MASK(pg->regs[NV_PGRAPH_CHEOPS_OFFSET],
+ NV_PGRAPH_CHEOPS_OFFSET_CONST_LD_PTR, const_load+1);
+ }
+ break;
+ }
+
+ CASE_3(NV097_SET_VERTEX3F, 4) : {
+ slot = (method - NV097_SET_VERTEX3F) / 4;
+ VertexAttribute *attribute =
+ &pg->vertex_attributes[NV2A_VERTEX_ATTR_POSITION];
+ pgraph_allocate_inline_buffer_vertices(pg, NV2A_VERTEX_ATTR_POSITION);
+ attribute->inline_value[slot] = *(float*)¶meter;
+ attribute->inline_value[3] = 1.0f;
+ if (slot == 2) {
+ pgraph_finish_inline_buffer_vertex(pg);
+ }
+ break;
+ }
+
+ /* Handles NV097_SET_BACK_LIGHT_* */
+ CASE_78(NV097_SET_BACK_LIGHT_AMBIENT_COLOR, 4): {
+ // NV097_SET_BACK_LIGHT_SPECULAR_COLOR 0x00000C18 - 0x00000C00 + 0x1C8 = 0x1E0; /4= 78d
+ slot = (method - NV097_SET_BACK_LIGHT_AMBIENT_COLOR) / 4;
+ unsigned int part = NV097_SET_BACK_LIGHT_AMBIENT_COLOR / 4 + slot % 16;
+ slot /= 16; /* [Light index] */
+ assert(slot < 8);
+ switch(part * 4) {
+ CASE_3(NV097_SET_BACK_LIGHT_AMBIENT_COLOR, 4):
+ part -= NV097_SET_BACK_LIGHT_AMBIENT_COLOR / 4;
+ pg->ltctxb[NV_IGRAPH_XF_LTCTXB_L0_BAMB + slot*6][part] = parameter;
+ pg->ltctxb_dirty[NV_IGRAPH_XF_LTCTXB_L0_BAMB + slot*6] = true;
+ break;
+ CASE_3(NV097_SET_BACK_LIGHT_DIFFUSE_COLOR, 4):
+ part -= NV097_SET_BACK_LIGHT_DIFFUSE_COLOR / 4;
+ pg->ltctxb[NV_IGRAPH_XF_LTCTXB_L0_BDIF + slot*6][part] = parameter;
+ pg->ltctxb_dirty[NV_IGRAPH_XF_LTCTXB_L0_BDIF + slot*6] = true;
+ break;
+ CASE_3(NV097_SET_BACK_LIGHT_SPECULAR_COLOR, 4):
+ part -= NV097_SET_BACK_LIGHT_SPECULAR_COLOR / 4;
+ pg->ltctxb[NV_IGRAPH_XF_LTCTXB_L0_BSPC + slot*6][part] = parameter;
+ pg->ltctxb_dirty[NV_IGRAPH_XF_LTCTXB_L0_BSPC + slot*6] = true;
+ break;
+ default:
+ assert(false);
+ break;
+ }
+ break;
+ }
+ /* Handles all the light source props except for NV097_SET_BACK_LIGHT_* */
+ CASE_253(NV097_SET_LIGHT_AMBIENT_COLOR, 4): {
+ // NV097_SET_LIGHT_LOCAL_ATTENUATION 0x00001068 - 0x00001000 + 0x38C = 0x3F4; /4= 253d
+ slot = (method - NV097_SET_LIGHT_AMBIENT_COLOR) / 4;
+ unsigned int part = NV097_SET_LIGHT_AMBIENT_COLOR / 4 + slot % 32;
+ slot /= 32; /* [Light index] */
+ assert(slot < 8);
+ switch(part * 4) {
+ CASE_3(NV097_SET_LIGHT_AMBIENT_COLOR, 4):
+ part -= NV097_SET_LIGHT_AMBIENT_COLOR / 4;
+ pg->ltctxb[NV_IGRAPH_XF_LTCTXB_L0_AMB + slot*6][part] = parameter;
+ pg->ltctxb_dirty[NV_IGRAPH_XF_LTCTXB_L0_AMB + slot*6] = true;
+ break;
+ CASE_3(NV097_SET_LIGHT_DIFFUSE_COLOR, 4):
+ part -= NV097_SET_LIGHT_DIFFUSE_COLOR / 4;
+ pg->ltctxb[NV_IGRAPH_XF_LTCTXB_L0_DIF + slot*6][part] = parameter;
+ pg->ltctxb_dirty[NV_IGRAPH_XF_LTCTXB_L0_DIF + slot*6] = true;
+ break;
+ CASE_3(NV097_SET_LIGHT_SPECULAR_COLOR, 4):
+ part -= NV097_SET_LIGHT_SPECULAR_COLOR / 4;
+ pg->ltctxb[NV_IGRAPH_XF_LTCTXB_L0_SPC + slot*6][part] = parameter;
+ pg->ltctxb_dirty[NV_IGRAPH_XF_LTCTXB_L0_SPC + slot*6] = true;
+ break;
+ case NV097_SET_LIGHT_LOCAL_RANGE:
+ pg->ltc1[NV_IGRAPH_XF_LTC1_r0 + slot][0] = parameter;
+ pg->ltc1_dirty[NV_IGRAPH_XF_LTC1_r0 + slot] = true;
+ break;
+ CASE_3(NV097_SET_LIGHT_INFINITE_HALF_VECTOR, 4):
+ part -= NV097_SET_LIGHT_INFINITE_HALF_VECTOR / 4;
+ pg->light_infinite_half_vector[slot][part] = *(float*)¶meter;
+ break;
+ CASE_3(NV097_SET_LIGHT_INFINITE_DIRECTION, 4):
+ part -= NV097_SET_LIGHT_INFINITE_DIRECTION / 4;
+ pg->light_infinite_direction[slot][part] = *(float*)¶meter;
+ break;
+ CASE_3(NV097_SET_LIGHT_SPOT_FALLOFF, 4):
+ part -= NV097_SET_LIGHT_SPOT_FALLOFF / 4;
+ pg->ltctxa[NV_IGRAPH_XF_LTCTXA_L0_K + slot*2][part] = parameter;
+ pg->ltctxa_dirty[NV_IGRAPH_XF_LTCTXA_L0_K + slot*2] = true;
+ break;
+ CASE_4(NV097_SET_LIGHT_SPOT_DIRECTION, 4):
+ part -= NV097_SET_LIGHT_SPOT_DIRECTION / 4;
+ pg->ltctxa[NV_IGRAPH_XF_LTCTXA_L0_SPT + slot*2][part] = parameter;
+ pg->ltctxa_dirty[NV_IGRAPH_XF_LTCTXA_L0_SPT + slot*2] = true;
+ break;
+ CASE_3(NV097_SET_LIGHT_LOCAL_POSITION, 4):
+ part -= NV097_SET_LIGHT_LOCAL_POSITION / 4;
+ pg->light_local_position[slot][part] = *(float*)¶meter;
+ break;
+ CASE_3(NV097_SET_LIGHT_LOCAL_ATTENUATION, 4):
+ part -= NV097_SET_LIGHT_LOCAL_ATTENUATION / 4;
+ pg->light_local_attenuation[slot][part] = *(float*)¶meter;
+ break;
+ default:
+ assert(false);
+ break;
+ }
+ break;
+ }
+
+ CASE_4(NV097_SET_VERTEX4F, 4): {
+ slot = (method - NV097_SET_VERTEX4F) / 4;
+ VertexAttribute *attribute =
+ &pg->vertex_attributes[NV2A_VERTEX_ATTR_POSITION];
+ pgraph_allocate_inline_buffer_vertices(pg, NV2A_VERTEX_ATTR_POSITION);
+ attribute->inline_value[slot] = *(float*)¶meter;
+ if (slot == 3) {
+ pgraph_finish_inline_buffer_vertex(pg);
+ }
+ break;
+ }
+
+ CASE_16(NV097_SET_VERTEX_DATA_ARRAY_FORMAT, 4): {
+
+ slot = (method - NV097_SET_VERTEX_DATA_ARRAY_FORMAT) / 4;
+ VertexAttribute *vertex_attribute = &pg->vertex_attributes[slot];
+
+ vertex_attribute->format =
+ GET_MASK(parameter, NV097_SET_VERTEX_DATA_ARRAY_FORMAT_TYPE);
+ vertex_attribute->count =
+ GET_MASK(parameter, NV097_SET_VERTEX_DATA_ARRAY_FORMAT_SIZE);
+ vertex_attribute->stride =
+ GET_MASK(parameter, NV097_SET_VERTEX_DATA_ARRAY_FORMAT_STRIDE);
+
+ NV2A_DPRINTF("vertex data array format=%d, count=%d, stride=%d\n",
+ vertex_attribute->format,
+ vertex_attribute->count,
+ vertex_attribute->stride);
+
+ vertex_attribute->gl_count = vertex_attribute->count;
+
+ switch (vertex_attribute->format) {
+ case NV097_SET_VERTEX_DATA_ARRAY_FORMAT_TYPE_UB_D3D:
+ vertex_attribute->gl_type = GL_UNSIGNED_BYTE;
+ vertex_attribute->gl_normalize = GL_TRUE;
+ vertex_attribute->size = 1;
+ assert(vertex_attribute->count == 4);
+ // http://www.opengl.org/registry/specs/ARB/vertex_array_bgra.txt
+ vertex_attribute->gl_count = GL_BGRA;
+ vertex_attribute->needs_conversion = false;
+ break;
+ case NV097_SET_VERTEX_DATA_ARRAY_FORMAT_TYPE_UB_OGL:
+ vertex_attribute->gl_type = GL_UNSIGNED_BYTE;
+ vertex_attribute->gl_normalize = GL_TRUE;
+ vertex_attribute->size = 1;
+ vertex_attribute->needs_conversion = false;
+ break;
+ case NV097_SET_VERTEX_DATA_ARRAY_FORMAT_TYPE_S1:
+ vertex_attribute->gl_type = GL_SHORT;
+ vertex_attribute->gl_normalize = GL_TRUE;
+ vertex_attribute->size = 2;
+ vertex_attribute->needs_conversion = false;
+ break;
+ case NV097_SET_VERTEX_DATA_ARRAY_FORMAT_TYPE_F:
+ vertex_attribute->gl_type = GL_FLOAT;
+ vertex_attribute->gl_normalize = GL_FALSE;
+ vertex_attribute->size = 4;
+ vertex_attribute->needs_conversion = false;
+ break;
+ case NV097_SET_VERTEX_DATA_ARRAY_FORMAT_TYPE_S32K:
+ vertex_attribute->gl_type = GL_SHORT;
+ vertex_attribute->gl_normalize = GL_FALSE;
+ vertex_attribute->size = 2;
+ vertex_attribute->needs_conversion = false;
+ break;
+ case NV097_SET_VERTEX_DATA_ARRAY_FORMAT_TYPE_CMP:
+ /* 3 signed, normalized components packed in 32-bits. (11,11,10) */
+ vertex_attribute->size = 4;
+ vertex_attribute->gl_type = GL_FLOAT;
+ vertex_attribute->gl_normalize = GL_FALSE;
+ vertex_attribute->needs_conversion = true;
+ vertex_attribute->converted_size = sizeof(float);
+ vertex_attribute->converted_count = 3 * vertex_attribute->count;
+ break;
+ default:
+ fprintf(stderr, "Unknown vertex type: 0x%x\n", vertex_attribute->format);
+ assert(false);
+ break;
+ }
+
+ if (vertex_attribute->needs_conversion) {
+ vertex_attribute->converted_elements = 0;
+ } else {
+ if (vertex_attribute->converted_buffer) {
+ g_free(vertex_attribute->converted_buffer);
+ vertex_attribute->converted_buffer = NULL;
+ }
+ }
+
+ break;
+ }
+
+ CASE_16(NV097_SET_VERTEX_DATA_ARRAY_OFFSET, 4):
+
+ slot = (method - NV097_SET_VERTEX_DATA_ARRAY_OFFSET) / 4;
+
+ pg->vertex_attributes[slot].dma_select =
+ parameter & 0x80000000;
+ pg->vertex_attributes[slot].offset =
+ parameter & 0x7fffffff;
+
+ pg->vertex_attributes[slot].converted_elements = 0;
+
+ break;
+
+ case NV097_SET_LOGIC_OP_ENABLE:
+ SET_MASK(pg->regs[NV_PGRAPH_BLEND],
+ NV_PGRAPH_BLEND_LOGICOP_ENABLE, parameter);
+ break;
+
+ case NV097_SET_LOGIC_OP:
+ SET_MASK(pg->regs[NV_PGRAPH_BLEND],
+ NV_PGRAPH_BLEND_LOGICOP, parameter & 0xF);
+ break;
+
+ case NV097_CLEAR_REPORT_VALUE:
+ /* FIXME: Does this have a value in parameter? Also does this (also?) modify
+ * the report memory block?
+ */
+ if (pg->gl_zpass_pixel_count_query_count) {
+ glDeleteQueries(pg->gl_zpass_pixel_count_query_count,
+ pg->gl_zpass_pixel_count_queries);
+ pg->gl_zpass_pixel_count_query_count = 0;
+ }
+ pg->zpass_pixel_count_result = 0;
+ break;
+
+ case NV097_SET_ZPASS_PIXEL_COUNT_ENABLE:
+ pg->zpass_pixel_count_enable = parameter;
+ break;
+
+ case NV097_GET_REPORT: {
+ /* FIXME: This was first intended to be watchpoint-based. However,
+ * qemu / kvm only supports virtual-address watchpoints.
+ * This'll do for now, but accuracy and performance with other
+ * approaches could be better
+ */
+ uint8_t type = GET_MASK(parameter, NV097_GET_REPORT_TYPE);
+ assert(type == NV097_GET_REPORT_TYPE_ZPASS_PIXEL_CNT);
+ hwaddr offset = GET_MASK(parameter, NV097_GET_REPORT_OFFSET);
+
+ uint64_t timestamp = 0x0011223344556677; /* FIXME: Update timestamp?! */
+ uint32_t done = 0;
+
+#ifdef COMPILE_OPENGL
+ /* FIXME: Multisampling affects this (both: OGL and Xbox GPU),
+ * not sure if CLEARs also count
+ */
+ /* FIXME: What about clipping regions etc? */
+ for(i = 0; i < pg->gl_zpass_pixel_count_query_count; i++) {
+ GLuint gl_query_result;
+ glGetQueryObjectuiv(pg->gl_zpass_pixel_count_queries[i],
+ GL_QUERY_RESULT,
+ &gl_query_result);
+ pg->zpass_pixel_count_result += gl_query_result;
+ }
+ if (pg->gl_zpass_pixel_count_query_count) {
+ glDeleteQueries(pg->gl_zpass_pixel_count_query_count,
+ pg->gl_zpass_pixel_count_queries);
+ }
+ pg->gl_zpass_pixel_count_query_count = 0;
+
+ hwaddr report_dma_len;
+ uint8_t *report_data = (uint8_t*)nv_dma_map(d, pg->dma_report,
+ &report_dma_len);
+ assert(offset < report_dma_len);
+ report_data += offset;
+
+ stq_le_p((uint64_t*)&report_data[0], timestamp);
+ stl_le_p((uint32_t*)&report_data[8], pg->zpass_pixel_count_result);
+ stl_le_p((uint32_t*)&report_data[12], done);
+#endif // COMPILE_OPENGL
+
+ break;
+ }
+
+ CASE_3(NV097_SET_EYE_DIRECTION, 4):
+ slot = (method - NV097_SET_EYE_DIRECTION) / 4;
+ pg->ltctxa[NV_IGRAPH_XF_LTCTXA_EYED][slot] = parameter;
+ pg->ltctxa_dirty[NV_IGRAPH_XF_LTCTXA_EYED] = true;
+ break;
+
+ case NV097_SET_BEGIN_END: {
+ bool depth_test =
+ pg->regs[NV_PGRAPH_CONTROL_0] & NV_PGRAPH_CONTROL_0_ZENABLE;
+ bool stencil_test = pg->regs[NV_PGRAPH_CONTROL_1]
+ & NV_PGRAPH_CONTROL_1_STENCIL_TEST_ENABLE;
+
+ if (parameter == NV097_SET_BEGIN_END_OP_END) {
+#ifdef COMPILE_OPENGL
+
+ assert(pg->shader_binding);
+
+ if (pg->draw_arrays_length) {
+
+ NV2A_GL_DPRINTF(false, "Draw Arrays");
+
+ assert(pg->inline_buffer_length == 0);
+ assert(pg->inline_array_length == 0);
+ assert(pg->inline_elements_length == 0);
+
+ pgraph_bind_vertex_attributes(d, pg->draw_arrays_max_count,
+ false, 0);
+ glMultiDrawArrays(pg->shader_binding->gl_primitive_mode,
+ pg->gl_draw_arrays_start,
+ pg->gl_draw_arrays_count,
+ pg->draw_arrays_length);
+ } else if (pg->inline_buffer_length) {
+
+ NV2A_GL_DPRINTF(false, "Inline Buffer");
+
+ assert(pg->draw_arrays_length == 0);
+ assert(pg->inline_array_length == 0);
+ assert(pg->inline_elements_length == 0);
+
+ for (i = 0; i < NV2A_VERTEXSHADER_ATTRIBUTES; i++) {
+ VertexAttribute *attribute = &pg->vertex_attributes[i];
+
+ if (attribute->inline_buffer) {
+
+ glBindBuffer(GL_ARRAY_BUFFER,
+ attribute->gl_inline_buffer);
+ glBufferData(GL_ARRAY_BUFFER,
+ pg->inline_buffer_length
+ * sizeof(float) * 4,
+ attribute->inline_buffer,
+ GL_DYNAMIC_DRAW);
+
+ /* Clear buffer for next batch */
+ g_free(attribute->inline_buffer);
+ attribute->inline_buffer = NULL;
+
+ glVertexAttribPointer(i, 4, GL_FLOAT, GL_FALSE, 0, 0);
+ glEnableVertexAttribArray(i);
+ } else {
+ glDisableVertexAttribArray(i);
+
+ glVertexAttrib4fv(i, attribute->inline_value);
+ }
+
+ }
+
+ glDrawArrays(pg->shader_binding->gl_primitive_mode,
+ 0, pg->inline_buffer_length);
+ } else if (pg->inline_array_length) {
+
+ NV2A_GL_DPRINTF(false, "Inline Array");
+
+ assert(pg->draw_arrays_length == 0);
+ assert(pg->inline_buffer_length == 0);
+ assert(pg->inline_elements_length == 0);
+
+ unsigned int index_count = pgraph_bind_inline_array(d);
+ glDrawArrays(pg->shader_binding->gl_primitive_mode,
+ 0, index_count);
+ } else if (pg->inline_elements_length) {
+
+ NV2A_GL_DPRINTF(false, "Inline Elements");
+
+ assert(pg->draw_arrays_length == 0);
+ assert(pg->inline_buffer_length == 0);
+ assert(pg->inline_array_length == 0);
+
+ uint32_t max_element = 0;
+ uint32_t min_element = (uint32_t)-1;
+ for (i=0; iinline_elements_length; i++) {
+ max_element = MAX(pg->inline_elements[i], max_element);
+ min_element = MIN(pg->inline_elements[i], min_element);
+ }
+
+ pgraph_bind_vertex_attributes(d, max_element+1, false, 0);
+
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, pg->gl_element_buffer);
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER,
+ pg->inline_elements_length*4,
+ pg->inline_elements,
+ GL_DYNAMIC_DRAW);
+
+ glDrawRangeElements(pg->shader_binding->gl_primitive_mode,
+ min_element, max_element,
+ pg->inline_elements_length,
+ GL_UNSIGNED_INT,
+ (void*)0);
+
+ } else {
+ NV2A_GL_DPRINTF(true, "EMPTY NV097_SET_BEGIN_END");
+ assert(false);
+ }
+
+ /* End of visibility testing */
+ if (pg->zpass_pixel_count_enable) {
+ glEndQuery(GL_SAMPLES_PASSED);
+ }
+
+ NV2A_GL_DGROUP_END();
+ } else {
+ NV2A_GL_DGROUP_BEGIN("NV097_SET_BEGIN_END: 0x%x", parameter);
+ assert(parameter <= NV097_SET_BEGIN_END_OP_POLYGON);
+
+ pgraph_update_surface(d, true, true, depth_test || stencil_test);
+
+ pg->primitive_mode = parameter;
+
+ uint32_t control_0 = pg->regs[NV_PGRAPH_CONTROL_0];
+
+ bool alpha = control_0 & NV_PGRAPH_CONTROL_0_ALPHA_WRITE_ENABLE;
+ bool red = control_0 & NV_PGRAPH_CONTROL_0_RED_WRITE_ENABLE;
+ bool green = control_0 & NV_PGRAPH_CONTROL_0_GREEN_WRITE_ENABLE;
+ bool blue = control_0 & NV_PGRAPH_CONTROL_0_BLUE_WRITE_ENABLE;
+ glColorMask(red, green, blue, alpha);
+ glDepthMask(!!(control_0 & NV_PGRAPH_CONTROL_0_ZWRITEENABLE));
+ glStencilMask(GET_MASK(pg->regs[NV_PGRAPH_CONTROL_1],
+ NV_PGRAPH_CONTROL_1_STENCIL_MASK_WRITE));
+
+ if (pg->regs[NV_PGRAPH_BLEND] & NV_PGRAPH_BLEND_EN) {
+ glEnable(GL_BLEND);
+ uint32_t sfactor = GET_MASK(pg->regs[NV_PGRAPH_BLEND],
+ NV_PGRAPH_BLEND_SFACTOR);
+ uint32_t dfactor = GET_MASK(pg->regs[NV_PGRAPH_BLEND],
+ NV_PGRAPH_BLEND_DFACTOR);
+ assert(sfactor < ARRAY_SIZE(pgraph_blend_factor_map));
+ assert(dfactor < ARRAY_SIZE(pgraph_blend_factor_map));
+ glBlendFunc(pgraph_blend_factor_map[sfactor],
+ pgraph_blend_factor_map[dfactor]);
+
+ uint32_t equation = GET_MASK(pg->regs[NV_PGRAPH_BLEND],
+ NV_PGRAPH_BLEND_EQN);
+ assert(equation < ARRAY_SIZE(pgraph_blend_equation_map));
+ glBlendEquation(pgraph_blend_equation_map[equation]);
+
+ uint32_t blend_color = pg->regs[NV_PGRAPH_BLENDCOLOR];
+ glBlendColor( ((blend_color >> 16) & 0xFF) / 255.0f, /* red */
+ ((blend_color >> 8) & 0xFF) / 255.0f, /* green */
+ (blend_color & 0xFF) / 255.0f, /* blue */
+ ((blend_color >> 24) & 0xFF) / 255.0f);/* alpha */
+ } else {
+ glDisable(GL_BLEND);
+ }
+
+ /* Face culling */
+ if (pg->regs[NV_PGRAPH_SETUPRASTER]
+ & NV_PGRAPH_SETUPRASTER_CULLENABLE) {
+ uint32_t cull_face = GET_MASK(pg->regs[NV_PGRAPH_SETUPRASTER],
+ NV_PGRAPH_SETUPRASTER_CULLCTRL);
+ assert(cull_face < ARRAY_SIZE(pgraph_cull_face_map));
+ glCullFace(pgraph_cull_face_map[cull_face]);
+ glEnable(GL_CULL_FACE);
+ } else {
+ glDisable(GL_CULL_FACE);
+ }
+
+ /* Front-face select */
+ glFrontFace(pg->regs[NV_PGRAPH_SETUPRASTER]
+ & NV_PGRAPH_SETUPRASTER_FRONTFACE
+ ? GL_CCW : GL_CW);
+
+ /* Polygon offset */
+ /* FIXME: GL implementation-specific, maybe do this in VS? */
+ if (pg->regs[NV_PGRAPH_SETUPRASTER] &
+ NV_PGRAPH_SETUPRASTER_POFFSETFILLENABLE) {
+ glEnable(GL_POLYGON_OFFSET_FILL);
+ } else {
+ glDisable(GL_POLYGON_OFFSET_FILL);
+ }
+ if (pg->regs[NV_PGRAPH_SETUPRASTER] &
+ NV_PGRAPH_SETUPRASTER_POFFSETLINEENABLE) {
+ glEnable(GL_POLYGON_OFFSET_LINE);
+ } else {
+ glDisable(GL_POLYGON_OFFSET_LINE);
+ }
+ if (pg->regs[NV_PGRAPH_SETUPRASTER] &
+ NV_PGRAPH_SETUPRASTER_POFFSETPOINTENABLE) {
+ glEnable(GL_POLYGON_OFFSET_POINT);
+ } else {
+ glDisable(GL_POLYGON_OFFSET_POINT);
+ }
+ if (pg->regs[NV_PGRAPH_SETUPRASTER] &
+ (NV_PGRAPH_SETUPRASTER_POFFSETFILLENABLE |
+ NV_PGRAPH_SETUPRASTER_POFFSETLINEENABLE |
+ NV_PGRAPH_SETUPRASTER_POFFSETPOINTENABLE)) {
+ GLfloat zfactor = *(float*)&pg->regs[NV_PGRAPH_ZOFFSETFACTOR];
+ GLfloat zbias = *(float*)&pg->regs[NV_PGRAPH_ZOFFSETBIAS];
+ glPolygonOffset(zfactor, zbias);
+ }
+
+ /* Depth testing */
+ if (depth_test) {
+ glEnable(GL_DEPTH_TEST);
+
+ uint32_t depth_func = GET_MASK(pg->regs[NV_PGRAPH_CONTROL_0],
+ NV_PGRAPH_CONTROL_0_ZFUNC);
+ assert(depth_func < ARRAY_SIZE(pgraph_depth_func_map));
+ glDepthFunc(pgraph_depth_func_map[depth_func]);
+ } else {
+ glDisable(GL_DEPTH_TEST);
+ }
+
+ if (stencil_test) {
+ glEnable(GL_STENCIL_TEST);
+
+ uint32_t stencil_func = GET_MASK(pg->regs[NV_PGRAPH_CONTROL_1],
+ NV_PGRAPH_CONTROL_1_STENCIL_FUNC);
+ uint32_t stencil_ref = GET_MASK(pg->regs[NV_PGRAPH_CONTROL_1],
+ NV_PGRAPH_CONTROL_1_STENCIL_REF);
+ uint32_t func_mask = GET_MASK(pg->regs[NV_PGRAPH_CONTROL_1],
+ NV_PGRAPH_CONTROL_1_STENCIL_MASK_READ);
+ uint32_t op_fail = GET_MASK(pg->regs[NV_PGRAPH_CONTROL_2],
+ NV_PGRAPH_CONTROL_2_STENCIL_OP_FAIL);
+ uint32_t op_zfail = GET_MASK(pg->regs[NV_PGRAPH_CONTROL_2],
+ NV_PGRAPH_CONTROL_2_STENCIL_OP_ZFAIL);
+ uint32_t op_zpass = GET_MASK(pg->regs[NV_PGRAPH_CONTROL_2],
+ NV_PGRAPH_CONTROL_2_STENCIL_OP_ZPASS);
+
+ assert(stencil_func < ARRAY_SIZE(pgraph_stencil_func_map));
+ assert(op_fail < ARRAY_SIZE(pgraph_stencil_op_map));
+ assert(op_zfail < ARRAY_SIZE(pgraph_stencil_op_map));
+ assert(op_zpass < ARRAY_SIZE(pgraph_stencil_op_map));
+
+ glStencilFunc(
+ pgraph_stencil_func_map[stencil_func],
+ stencil_ref,
+ func_mask);
+
+ glStencilOp(
+ pgraph_stencil_op_map[op_fail],
+ pgraph_stencil_op_map[op_zfail],
+ pgraph_stencil_op_map[op_zpass]);
+
+ } else {
+ glDisable(GL_STENCIL_TEST);
+ }
+
+ /* Dither */
+ /* FIXME: GL implementation dependent */
+ if (pg->regs[NV_PGRAPH_CONTROL_0] &
+ NV_PGRAPH_CONTROL_0_DITHERENABLE) {
+ glEnable(GL_DITHER);
+ } else {
+ glDisable(GL_DITHER);
+ }
+
+ pgraph_bind_shaders(pg);
+ pgraph_bind_textures(d);
+
+ //glDisableVertexAttribArray(NV2A_VERTEX_ATTR_DIFFUSE);
+ //glVertexAttrib4f(NV2A_VERTEX_ATTR_DIFFUSE, 1.0f, 1.0f, 1.0f, 1.0f);
+
+
+ unsigned int width, height;
+ pgraph_get_surface_dimensions(pg, &width, &height);
+ pgraph_apply_anti_aliasing_factor(pg, &width, &height);
+ glViewport(0, 0, width, height);
+
+ pg->inline_elements_length = 0;
+ pg->inline_array_length = 0;
+ pg->inline_buffer_length = 0;
+ pg->draw_arrays_length = 0;
+ pg->draw_arrays_max_count = 0;
+
+ /* Visibility testing */
+ if (pg->zpass_pixel_count_enable) {
+ GLuint gl_query;
+ glGenQueries(1, &gl_query);
+ pg->gl_zpass_pixel_count_query_count++;
+ pg->gl_zpass_pixel_count_queries = (GLuint*)g_realloc(
+ pg->gl_zpass_pixel_count_queries,
+ sizeof(GLuint) * pg->gl_zpass_pixel_count_query_count);
+ pg->gl_zpass_pixel_count_queries[
+ pg->gl_zpass_pixel_count_query_count - 1] = gl_query;
+ glBeginQuery(GL_SAMPLES_PASSED, gl_query);
+ }
+
+#endif // COMPILE_OPENGL
+ }
+
+ pgraph_set_surface_dirty(pg, true, depth_test || stencil_test);
+ break;
+ }
+ CASE_4(NV097_SET_TEXTURE_OFFSET, 64):
+ slot = (method - NV097_SET_TEXTURE_OFFSET) / 64;
+ pg->regs[NV_PGRAPH_TEXOFFSET0 + slot * 4] = parameter;
+ pg->texture_dirty[slot] = true;
+ break;
+ CASE_4(NV097_SET_TEXTURE_FORMAT, 64): {
+ slot = (method - NV097_SET_TEXTURE_FORMAT) / 64;
+
+ bool dma_select =
+ GET_MASK(parameter, NV097_SET_TEXTURE_FORMAT_CONTEXT_DMA) == 2;
+ bool cubemap =
+ GET_MASK(parameter, NV097_SET_TEXTURE_FORMAT_CUBEMAP_ENABLE);
+ unsigned int border_source =
+ GET_MASK(parameter, NV097_SET_TEXTURE_FORMAT_BORDER_SOURCE);
+ unsigned int dimensionality =
+ GET_MASK(parameter, NV097_SET_TEXTURE_FORMAT_DIMENSIONALITY);
+ unsigned int color_format =
+ GET_MASK(parameter, NV097_SET_TEXTURE_FORMAT_COLOR);
+ unsigned int levels =
+ GET_MASK(parameter, NV097_SET_TEXTURE_FORMAT_MIPMAP_LEVELS);
+ unsigned int log_width =
+ GET_MASK(parameter, NV097_SET_TEXTURE_FORMAT_BASE_SIZE_U);
+ unsigned int log_height =
+ GET_MASK(parameter, NV097_SET_TEXTURE_FORMAT_BASE_SIZE_V);
+ unsigned int log_depth =
+ GET_MASK(parameter, NV097_SET_TEXTURE_FORMAT_BASE_SIZE_P);
+
+ uint32_t *reg = &pg->regs[NV_PGRAPH_TEXFMT0 + slot * 4];
+ SET_MASK(*reg, NV_PGRAPH_TEXFMT0_CONTEXT_DMA, dma_select);
+ SET_MASK(*reg, NV_PGRAPH_TEXFMT0_CUBEMAPENABLE, cubemap);
+ SET_MASK(*reg, NV_PGRAPH_TEXFMT0_BORDER_SOURCE, border_source);
+ SET_MASK(*reg, NV_PGRAPH_TEXFMT0_DIMENSIONALITY, dimensionality);
+ SET_MASK(*reg, NV_PGRAPH_TEXFMT0_COLOR, color_format);
+ SET_MASK(*reg, NV_PGRAPH_TEXFMT0_MIPMAP_LEVELS, levels);
+ SET_MASK(*reg, NV_PGRAPH_TEXFMT0_BASE_SIZE_U, log_width);
+ SET_MASK(*reg, NV_PGRAPH_TEXFMT0_BASE_SIZE_V, log_height);
+ SET_MASK(*reg, NV_PGRAPH_TEXFMT0_BASE_SIZE_P, log_depth);
+
+ pg->texture_dirty[slot] = true;
+ break;
+ }
+ CASE_4(NV097_SET_TEXTURE_CONTROL0, 64):
+ slot = (method - NV097_SET_TEXTURE_CONTROL0) / 64;
+ pg->regs[NV_PGRAPH_TEXCTL0_0 + slot*4] = parameter;
+ break;
+ CASE_4(NV097_SET_TEXTURE_CONTROL1, 64):
+ slot = (method - NV097_SET_TEXTURE_CONTROL1) / 64;
+ pg->regs[NV_PGRAPH_TEXCTL1_0 + slot*4] = parameter;
+ break;
+ CASE_4(NV097_SET_TEXTURE_FILTER, 64):
+ slot = (method - NV097_SET_TEXTURE_FILTER) / 64;
+ pg->regs[NV_PGRAPH_TEXFILTER0 + slot * 4] = parameter;
+ break;
+ CASE_4(NV097_SET_TEXTURE_IMAGE_RECT, 64):
+ slot = (method - NV097_SET_TEXTURE_IMAGE_RECT) / 64;
+ pg->regs[NV_PGRAPH_TEXIMAGERECT0 + slot * 4] = parameter;
+ pg->texture_dirty[slot] = true;
+ break;
+ CASE_4(NV097_SET_TEXTURE_PALETTE, 64): {
+ slot = (method - NV097_SET_TEXTURE_PALETTE) / 64;
+
+ bool dma_select =
+ GET_MASK(parameter, NV097_SET_TEXTURE_PALETTE_CONTEXT_DMA) == 1;
+ unsigned int length =
+ GET_MASK(parameter, NV097_SET_TEXTURE_PALETTE_LENGTH);
+ unsigned int offset =
+ GET_MASK(parameter, NV097_SET_TEXTURE_PALETTE_OFFSET);
+
+ uint32_t *reg = &pg->regs[NV_PGRAPH_TEXPALETTE0 + slot * 4];
+ SET_MASK(*reg, NV_PGRAPH_TEXPALETTE0_CONTEXT_DMA, dma_select);
+ SET_MASK(*reg, NV_PGRAPH_TEXPALETTE0_LENGTH, length);
+ SET_MASK(*reg, NV_PGRAPH_TEXPALETTE0_OFFSET, offset);
+
+ pg->texture_dirty[slot] = true;
+ break;
+ }
+
+ CASE_4(NV097_SET_TEXTURE_BORDER_COLOR, 64):
+ slot = (method - NV097_SET_TEXTURE_BORDER_COLOR) / 64;
+ pg->regs[NV_PGRAPH_BORDERCOLOR0 + slot * 4] = parameter;
+ break;
+ CASE_4(NV097_SET_TEXTURE_SET_BUMP_ENV_MAT + 0x0, 64):
+ CASE_4(NV097_SET_TEXTURE_SET_BUMP_ENV_MAT + 0x4, 64):
+ CASE_4(NV097_SET_TEXTURE_SET_BUMP_ENV_MAT + 0x8, 64):
+ CASE_4(NV097_SET_TEXTURE_SET_BUMP_ENV_MAT + 0xc, 64):
+ slot = (method - NV097_SET_TEXTURE_SET_BUMP_ENV_MAT) / 4;
+ assert((slot / 16) > 0);
+ slot -= 16;
+ pg->bump_env_matrix[slot / 16][slot % 4] = *(float*)¶meter;
+ break;
+
+ CASE_4(NV097_SET_TEXTURE_SET_BUMP_ENV_SCALE, 64):
+ slot = (method - NV097_SET_TEXTURE_SET_BUMP_ENV_SCALE) / 64;
+ assert(slot > 0);
+ slot--;
+ pg->regs[NV_PGRAPH_BUMPSCALE1 + slot * 4] = parameter;
+ break;
+ CASE_4(NV097_SET_TEXTURE_SET_BUMP_ENV_OFFSET, 64):
+ slot = (method - NV097_SET_TEXTURE_SET_BUMP_ENV_OFFSET) / 64;
+ assert(slot > 0);
+ slot--;
+ pg->regs[NV_PGRAPH_BUMPOFFSET1 + slot * 4] = parameter;
+ break;
+
+ case NV097_ARRAY_ELEMENT16:
+ assert(pg->inline_elements_length < NV2A_MAX_BATCH_LENGTH);
+ pg->inline_elements[
+ pg->inline_elements_length++] = parameter & 0xFFFF;
+ pg->inline_elements[
+ pg->inline_elements_length++] = parameter >> 16;
+ break;
+ case NV097_ARRAY_ELEMENT32:
+ assert(pg->inline_elements_length < NV2A_MAX_BATCH_LENGTH);
+ pg->inline_elements[
+ pg->inline_elements_length++] = parameter;
+ break;
+ case NV097_DRAW_ARRAYS: {
+
+ unsigned int start = GET_MASK(parameter, NV097_DRAW_ARRAYS_START_INDEX);
+ unsigned int count = GET_MASK(parameter, NV097_DRAW_ARRAYS_COUNT)+1;
+
+ pg->draw_arrays_max_count = MAX(pg->draw_arrays_max_count, start + count);
+
+#ifdef COMPILE_OPENGL
+ assert(pg->draw_arrays_length < ARRAY_SIZE(pg->gl_draw_arrays_start));
+#endif // COMPILE_OPENGL
+
+ /* Attempt to connect primitives */
+ if (pg->draw_arrays_length > 0) {
+ unsigned int last_start =
+ pg->gl_draw_arrays_start[pg->draw_arrays_length - 1];
+ GLsizei* last_count =
+ &pg->gl_draw_arrays_count[pg->draw_arrays_length - 1];
+ if (start == (last_start + *last_count)) {
+ *last_count += count;
+ break;
+ }
+ }
+
+ pg->gl_draw_arrays_start[pg->draw_arrays_length] = start;
+ pg->gl_draw_arrays_count[pg->draw_arrays_length] = count;
+ pg->draw_arrays_length++;
+ break;
+ }
+ case NV097_INLINE_ARRAY:
+ assert(pg->inline_array_length < NV2A_MAX_BATCH_LENGTH);
+ pg->inline_array[
+ pg->inline_array_length++] = parameter;
+ break;
+ CASE_3(NV097_SET_EYE_VECTOR, 4):
+ slot = (method - NV097_SET_EYE_VECTOR) / 4;
+ pg->regs[NV_PGRAPH_EYEVEC0 + slot * 4] = parameter;
+ break;
+
+ CASE_32(NV097_SET_VERTEX_DATA2F_M, 4): {
+ slot = (method - NV097_SET_VERTEX_DATA2F_M) / 4;
+ unsigned int part = slot % 2;
+ slot /= 2;
+ VertexAttribute *attribute = &pg->vertex_attributes[slot];
+ pgraph_allocate_inline_buffer_vertices(pg, slot);
+ attribute->inline_value[part] = *(float*)¶meter;
+ /* FIXME: Should these really be set to 0.0 and 1.0 ? Conditions? */
+ attribute->inline_value[2] = 0.0f;
+ attribute->inline_value[3] = 1.0f;
+ if ((slot == 0) && (part == 1)) {
+ pgraph_finish_inline_buffer_vertex(pg);
+ }
+ break;
+ }
+ CASE_64(NV097_SET_VERTEX_DATA4F_M, 4): {
+ slot = (method - NV097_SET_VERTEX_DATA4F_M) / 4;
+ unsigned int part = slot % 4;
+ slot /= 4;
+ VertexAttribute *attribute = &pg->vertex_attributes[slot];
+ pgraph_allocate_inline_buffer_vertices(pg, slot);
+ attribute->inline_value[part] = *(float*)¶meter;
+ if ((slot == 0) && (part == 3)) {
+ pgraph_finish_inline_buffer_vertex(pg);
+ }
+ break;
+ }
+ CASE_16(NV097_SET_VERTEX_DATA2S, 4): {
+ slot = (method - NV097_SET_VERTEX_DATA2S) / 4;
+ assert(false); /* FIXME: Untested! */
+ VertexAttribute *attribute = &pg->vertex_attributes[slot];
+ pgraph_allocate_inline_buffer_vertices(pg, slot);
+ /* FIXME: Is mapping to [-1,+1] correct? */
+ attribute->inline_value[0] = ((int16_t)(parameter & 0xFFFF) * 2.0f + 1)
+ / 65535.0f;
+ attribute->inline_value[1] = ((int16_t)(parameter >> 16) * 2.0f + 1)
+ / 65535.0f;
+ /* FIXME: Should these really be set to 0.0 and 1.0 ? Conditions? */
+ attribute->inline_value[2] = 0.0f;
+ attribute->inline_value[3] = 1.0f;
+ if (slot == 0) {
+ pgraph_finish_inline_buffer_vertex(pg);
+ assert(false); /* FIXME: Untested */
+ }
+ break;
+ }
+ CASE_16(NV097_SET_VERTEX_DATA4UB, 4) : {
+ slot = (method - NV097_SET_VERTEX_DATA4UB) / 4;
+ VertexAttribute *attribute = &pg->vertex_attributes[slot];
+ pgraph_allocate_inline_buffer_vertices(pg, slot);
+ attribute->inline_value[0] = (parameter & 0xFF) / 255.0f;
+ attribute->inline_value[1] = ((parameter >> 8) & 0xFF) / 255.0f;
+ attribute->inline_value[2] = ((parameter >> 16) & 0xFF) / 255.0f;
+ attribute->inline_value[3] = ((parameter >> 24) & 0xFF) / 255.0f;
+ if (slot == 0) {
+ pgraph_finish_inline_buffer_vertex(pg);
+ assert(false); /* FIXME: Untested */
+ }
+ break;
+ }
+ CASE_32(NV097_SET_VERTEX_DATA4S_M, 4) : {
+ slot = (method - NV097_SET_VERTEX_DATA4S_M) / 4;
+ unsigned int part = slot % 2;
+ slot /= 2;
+ assert(false); /* FIXME: Untested! */
+ VertexAttribute *attribute = &pg->vertex_attributes[slot];
+ pgraph_allocate_inline_buffer_vertices(pg, slot);
+ /* FIXME: Is mapping to [-1,+1] correct? */
+ attribute->inline_value[part * 2 + 0] = ((int16_t)(parameter & 0xFFFF)
+ * 2.0f + 1) / 65535.0f;
+ attribute->inline_value[part * 2 + 1] = ((int16_t)(parameter >> 16)
+ * 2.0f + 1) / 65535.0f;
+ if ((slot == 0) && (part == 1)) {
+ pgraph_finish_inline_buffer_vertex(pg);
+ assert(false); /* FIXME: Untested */
+ }
+ break;
+ }
+ case NV097_SET_SEMAPHORE_OFFSET:
+ kelvin->semaphore_offset = parameter;
+ break;
+ case NV097_BACK_END_WRITE_SEMAPHORE_RELEASE: {
+
+ pgraph_update_surface(d, false, true, true);
+
+ //qemu_mutex_unlock(&d->pg->pgraph_lock);
+ //qemu_mutex_lock_iothread();
+
+ xbaddr semaphore_dma_len;
+ uint8_t *semaphore_data = (uint8_t*)nv_dma_map(d, kelvin->dma_semaphore,
+ &semaphore_dma_len);
+ assert(kelvin->semaphore_offset < semaphore_dma_len);
+ semaphore_data += kelvin->semaphore_offset;
+
+ stl_le_p((uint32_t*)semaphore_data, parameter);
+
+ //qemu_mutex_lock(&d->pg->pgraph_lock);
+ //qemu_mutex_unlock_iothread();
+
+ break;
+ }
+ case NV097_SET_ZSTENCIL_CLEAR_VALUE:
+ pg->regs[NV_PGRAPH_ZSTENCILCLEARVALUE] = parameter;
+ break;
+
+ case NV097_SET_COLOR_CLEAR_VALUE:
+ pg->regs[NV_PGRAPH_COLORCLEARVALUE] = parameter;
+ break;
+
+ case NV097_CLEAR_SURFACE: {
+ NV2A_DPRINTF("---------PRE CLEAR ------\n");
+ GLbitfield gl_mask = 0;
+
+ bool write_color = (parameter & NV097_CLEAR_SURFACE_COLOR);
+ bool write_zeta =
+ (parameter & (NV097_CLEAR_SURFACE_Z | NV097_CLEAR_SURFACE_STENCIL));
+#ifdef COMPILE_OPENGL
+
+ if (write_zeta) {
+ uint32_t clear_zstencil =
+ d->pgraph.regs[NV_PGRAPH_ZSTENCILCLEARVALUE];
+ GLint gl_clear_stencil;
+ GLfloat gl_clear_depth;
+
+ /* FIXME: Put these in some lookup table */
+ const float f16_max = 511.9375f;
+ /* FIXME: 7 bits of mantissa unused. maybe use full buffer? */
+ const float f24_max = 3.4027977E38;
+
+ switch(pg->surface_shape.zeta_format) {
+ case NV097_SET_SURFACE_FORMAT_ZETA_Z16: {
+ uint16_t z = clear_zstencil & 0xFFFF;
+ /* FIXME: Remove bit for stencil clear? */
+ if (pg->surface_shape.z_format) {
+ gl_clear_depth = convert_f16_to_float(z) / f16_max;
+ assert(false); /* FIXME: Untested */
+ } else {
+ gl_clear_depth = z / (float)0xFFFF;
+ }
+ break;
+ }
+ case NV097_SET_SURFACE_FORMAT_ZETA_Z24S8: {
+ gl_clear_stencil = clear_zstencil & 0xFF;
+ uint32_t z = clear_zstencil >> 8;
+ if (pg->surface_shape.z_format) {
+ gl_clear_depth = convert_f24_to_float(z) / f24_max;
+ assert(false); /* FIXME: Untested */
+ } else {
+ gl_clear_depth = z / (float)0xFFFFFF;
+ }
+ break;
+ }
+ default:
+ fprintf(stderr, "Unknown zeta surface format: 0x%x\n", pg->surface_shape.zeta_format);
+ assert(false);
+ break;
+ }
+ if (parameter & NV097_CLEAR_SURFACE_Z) {
+ gl_mask |= GL_DEPTH_BUFFER_BIT;
+ glDepthMask(GL_TRUE);
+ glClearDepth(gl_clear_depth);
+ }
+ if (parameter & NV097_CLEAR_SURFACE_STENCIL) {
+ gl_mask |= GL_STENCIL_BUFFER_BIT;
+ glStencilMask(0xff);
+ glClearStencil(gl_clear_stencil);
+ }
+ }
+ if (write_color) {
+ gl_mask |= GL_COLOR_BUFFER_BIT;
+ glColorMask((parameter & NV097_CLEAR_SURFACE_R)
+ ? GL_TRUE : GL_FALSE,
+ (parameter & NV097_CLEAR_SURFACE_G)
+ ? GL_TRUE : GL_FALSE,
+ (parameter & NV097_CLEAR_SURFACE_B)
+ ? GL_TRUE : GL_FALSE,
+ (parameter & NV097_CLEAR_SURFACE_A)
+ ? GL_TRUE : GL_FALSE);
+ uint32_t clear_color = d->pgraph.regs[NV_PGRAPH_COLORCLEARVALUE];
+
+ /* Handle RGB */
+ GLfloat red, green, blue;
+ switch(pg->surface_shape.color_format) {
+ case NV097_SET_SURFACE_FORMAT_COLOR_LE_X1R5G5B5_Z1R5G5B5:
+ case NV097_SET_SURFACE_FORMAT_COLOR_LE_X1R5G5B5_O1R5G5B5:
+ red = ((clear_color >> 10) & 0x1F) / 31.0f;
+ green = ((clear_color >> 5) & 0x1F) / 31.0f;
+ blue = (clear_color & 0x1F) / 31.0f;
+ assert(false); /* Untested */
+ break;
+ case NV097_SET_SURFACE_FORMAT_COLOR_LE_R5G6B5:
+ red = ((clear_color >> 11) & 0x1F) / 31.0f;
+ green = ((clear_color >> 5) & 0x3F) / 63.0f;
+ blue = (clear_color & 0x1F) / 31.0f;
+ break;
+ case NV097_SET_SURFACE_FORMAT_COLOR_LE_X8R8G8B8_Z8R8G8B8:
+ case NV097_SET_SURFACE_FORMAT_COLOR_LE_X8R8G8B8_O8R8G8B8:
+ case NV097_SET_SURFACE_FORMAT_COLOR_LE_X1A7R8G8B8_Z1A7R8G8B8:
+ case NV097_SET_SURFACE_FORMAT_COLOR_LE_X1A7R8G8B8_O1A7R8G8B8:
+ case NV097_SET_SURFACE_FORMAT_COLOR_LE_A8R8G8B8:
+ red = ((clear_color >> 16) & 0xFF) / 255.0f;
+ green = ((clear_color >> 8) & 0xFF) / 255.0f;
+ blue = (clear_color & 0xFF) / 255.0f;
+ break;
+ case NV097_SET_SURFACE_FORMAT_COLOR_LE_B8:
+ case NV097_SET_SURFACE_FORMAT_COLOR_LE_G8B8:
+ /* Xbox D3D doesn't support clearing those */
+ default:
+ red = 1.0f;
+ green = 0.0f;
+ blue = 1.0f;
+ fprintf(stderr, "CLEAR_SURFACE for color_format 0x%x unsupported",
+ pg->surface_shape.color_format);
+ assert(false);
+ break;
+ }
+
+ /* Handle alpha */
+ GLfloat alpha;
+ switch(pg->surface_shape.color_format) {
+ /* FIXME: CLEAR_SURFACE seems to work like memset, so maybe we
+ * also have to clear non-alpha bits with alpha value?
+ * As GL doesn't own those pixels we'd have to do this on
+ * our own in xbox memory.
+ */
+ case NV097_SET_SURFACE_FORMAT_COLOR_LE_X1A7R8G8B8_Z1A7R8G8B8:
+ case NV097_SET_SURFACE_FORMAT_COLOR_LE_X1A7R8G8B8_O1A7R8G8B8:
+ alpha = ((clear_color >> 24) & 0x7F) / 127.0f;
+ assert(false); /* Untested */
+ break;
+ case NV097_SET_SURFACE_FORMAT_COLOR_LE_A8R8G8B8:
+ alpha = ((clear_color >> 24) & 0xFF) / 255.0f;
+ break;
+ default:
+ alpha = 1.0f;
+ break;
+ }
+
+ glClearColor(red, green, blue, alpha);
+ }
+ pgraph_update_surface(d, true, write_color, write_zeta);
+
+ glEnable(GL_SCISSOR_TEST);
+
+ unsigned int xmin = GET_MASK(pg->regs[NV_PGRAPH_CLEARRECTX],
+ NV_PGRAPH_CLEARRECTX_XMIN);
+ unsigned int xmax = GET_MASK(pg->regs[NV_PGRAPH_CLEARRECTX],
+ NV_PGRAPH_CLEARRECTX_XMAX);
+ unsigned int ymin = GET_MASK(pg->regs[NV_PGRAPH_CLEARRECTY],
+ NV_PGRAPH_CLEARRECTY_YMIN);
+ unsigned int ymax = GET_MASK(pg->regs[NV_PGRAPH_CLEARRECTY],
+ NV_PGRAPH_CLEARRECTY_YMAX);
+
+ unsigned int scissor_x = xmin;
+ unsigned int scissor_y = pg->surface_shape.clip_height - ymax - 1;
+
+ unsigned int scissor_width = xmax - xmin + 1;
+ unsigned int scissor_height = ymax - ymin + 1;
+
+ pgraph_apply_anti_aliasing_factor(pg, &scissor_x, &scissor_y);
+ pgraph_apply_anti_aliasing_factor(pg, &scissor_width, &scissor_height);
+
+ /* FIXME: Should this really be inverted instead of ymin? */
+ glScissor(scissor_x, scissor_y, scissor_width, scissor_height);
+
+ NV2A_DPRINTF("------------------CLEAR 0x%x %d,%d - %d,%d %x---------------\n",
+ parameter, xmin, ymin, xmax, ymax, d->pgraph.regs[NV_PGRAPH_COLORCLEARVALUE]);
+
+ /* Dither */
+ /* FIXME: Maybe also disable it here? + GL implementation dependent */
+ if (pg->regs[NV_PGRAPH_CONTROL_0] &
+ NV_PGRAPH_CONTROL_0_DITHERENABLE) {
+ glEnable(GL_DITHER);
+ } else {
+ glDisable(GL_DITHER);
+ }
+
+ glClear(gl_mask);
+
+ glDisable(GL_SCISSOR_TEST);
+#endif // COMPILE_OPENGL
+
+ pgraph_set_surface_dirty(pg, write_color, write_zeta);
+ break;
+ }
+
+ case NV097_SET_CLEAR_RECT_HORIZONTAL:
+ pg->regs[NV_PGRAPH_CLEARRECTX] = parameter;
+ break;
+ case NV097_SET_CLEAR_RECT_VERTICAL:
+ pg->regs[NV_PGRAPH_CLEARRECTY] = parameter;
+ break;
+
+ CASE_2(NV097_SET_SPECULAR_FOG_FACTOR, 4) :
+ slot = (method - NV097_SET_SPECULAR_FOG_FACTOR) / 4;
+ pg->regs[NV_PGRAPH_SPECFOGFACTOR0 + slot * 4] = parameter;
+ break;
+
+ case NV097_SET_SHADER_CLIP_PLANE_MODE:
+ pg->regs[NV_PGRAPH_SHADERCLIPMODE] = parameter;
+ break;
+
+ CASE_8(NV097_SET_COMBINER_COLOR_OCW, 4) :
+ slot = (method - NV097_SET_COMBINER_COLOR_OCW) / 4;
+ pg->regs[NV_PGRAPH_COMBINECOLORO0 + slot * 4] = parameter;
+ break;
+
+ case NV097_SET_COMBINER_CONTROL:
+ pg->regs[NV_PGRAPH_COMBINECTL] = parameter;
+ break;
+
+ case NV097_SET_SHADOW_ZSLOPE_THRESHOLD:
+ pg->regs[NV_PGRAPH_SHADOWZSLOPETHRESHOLD] = parameter;
+ assert(parameter == 0x7F800000); /* FIXME: Unimplemented */
+ break;
+
+ case NV097_SET_SHADER_STAGE_PROGRAM:
+ pg->regs[NV_PGRAPH_SHADERPROG] = parameter;
+ break;
+
+ case NV097_SET_SHADER_OTHER_STAGE_INPUT:
+ pg->regs[NV_PGRAPH_SHADERCTL] = parameter;
+ break;
+
+ case NV097_SET_TRANSFORM_EXECUTION_MODE:
+ SET_MASK(pg->regs[NV_PGRAPH_CSV0_D], NV_PGRAPH_CSV0_D_MODE,
+ GET_MASK(parameter,
+ NV097_SET_TRANSFORM_EXECUTION_MODE_MODE));
+ SET_MASK(pg->regs[NV_PGRAPH_CSV0_D], NV_PGRAPH_CSV0_D_RANGE_MODE,
+ GET_MASK(parameter,
+ NV097_SET_TRANSFORM_EXECUTION_MODE_RANGE_MODE));
+ break;
+ case NV097_SET_TRANSFORM_PROGRAM_CXT_WRITE_EN:
+ pg->enable_vertex_program_write = parameter;
+ break;
+ case NV097_SET_TRANSFORM_PROGRAM_LOAD:
+ assert(parameter < NV2A_MAX_TRANSFORM_PROGRAM_LENGTH);
+ SET_MASK(pg->regs[NV_PGRAPH_CHEOPS_OFFSET],
+ NV_PGRAPH_CHEOPS_OFFSET_PROG_LD_PTR, parameter);
+ break;
+ case NV097_SET_TRANSFORM_PROGRAM_START:
+ assert(parameter < NV2A_MAX_TRANSFORM_PROGRAM_LENGTH);
+ SET_MASK(pg->regs[NV_PGRAPH_CSV0_C],
+ NV_PGRAPH_CSV0_C_CHEOPS_PROGRAM_START, parameter);
+ break;
+ case NV097_SET_TRANSFORM_CONSTANT_LOAD:
+ assert(parameter < NV2A_VERTEXSHADER_CONSTANTS);
+ SET_MASK(pg->regs[NV_PGRAPH_CHEOPS_OFFSET],
+ NV_PGRAPH_CHEOPS_OFFSET_CONST_LD_PTR, parameter);
+ NV2A_DPRINTF("load to %d\n", parameter);
+ break;
+
+ default:
+ NV2A_GL_DPRINTF("EmuNV2A: Unknown NV_KELVIN_PRIMITIVE Method: 0x%08X\n",
+ method);
+ break;
+ }
+ }
+
+ default:
+ NV2A_GL_DPRINTF("EmuNV2A: Unknown Graphics Class/Method 0x%08X/0x%08X\n",
+ object->graphics_class, method);
+ break;
+ }
+
+}
+
+static void pgraph_context_switch(NV2AState *d, unsigned int channel_id)
+{
+ bool valid;
+
+ // Cxbx Note : This isn't present in xqemu / OpenXbox : d->pgraph.pgraph_lock.lock();
+ valid = d->pgraph.channel_valid && d->pgraph.channel_id == channel_id;
+ if (!valid) {
+ d->pgraph.trapped_channel_id = channel_id;
+ }
+ // Cxbx Note : This isn't present in xqemu / OpenXbox : d->pgraph.pgraph_lock.unlock();
+
+ if (!valid) {
+ NV2A_DPRINTF("puller needs to switch to ch %d\n", channel_id);
+
+ d->pgraph.pgraph_lock.unlock();
+ qemu_mutex_lock_iothread();
+ d->pgraph.pending_interrupts |= NV_PGRAPH_INTR_CONTEXT_SWITCH;
+ update_irq(d);
+
+ d->pgraph.pgraph_lock.lock();
+ qemu_mutex_unlock_iothread();
+
+ while (d->pgraph.pending_interrupts & NV_PGRAPH_INTR_CONTEXT_SWITCH) {
+ d->pgraph.interrupt_cond.wait(d->pgraph.pgraph_lock);
+ }
+ }
+}
+
+static void pgraph_wait_fifo_access(NV2AState *d) {
+ while (!d->pgraph.fifo_access) {
+ d->pgraph.fifo_access_cond.wait(d->pgraph.pgraph_lock);
+ }
+}
+
+static void pgraph_method_log(unsigned int subchannel,
+ unsigned int graphics_class,
+ unsigned int method, uint32_t parameter) {
+ static unsigned int last = 0;
+ static unsigned int count = 0;
+
+ if (last == 0x1800 && method != last) {
+ NV2A_GL_DPRINTF("d->pgraph method (%d) 0x%08X * %d",
+ subchannel, last, count);
+ }
+ if (method != 0x1800) {
+ const char* method_name = NULL;
+ unsigned int nmethod = 0;
+ switch (graphics_class) {
+ case NV_KELVIN_PRIMITIVE:
+ nmethod = method | (0x5c << 16);
+ break;
+ case NV_CONTEXT_SURFACES_2D:
+ nmethod = method | (0x6d << 16);
+ break;
+ default:
+ break;
+ }
+ /*
+ if (nmethod != 0 && nmethod < ARRAY_SIZE(nv2a_method_names)) {
+ method_name = nv2a_method_names[nmethod];
+ }
+ if (method_name) {
+ NV2A_DPRINTF("d->pgraph method (%d): %s (0x%x)\n",
+ subchannel, method_name, parameter);
+ } else {
+ */
+ NV2A_DPRINTF("d->pgraph method (%d): 0x%x -> 0x%04x (0x%x)\n",
+ subchannel, graphics_class, method, parameter);
+ //}
+
+ }
+ if (method == last) { count++; }
+ else { count = 0; }
+ last = method;
+}
+
+static void pgraph_allocate_inline_buffer_vertices(PGRAPHState *pg,
+ unsigned int attr)
+{
+ unsigned int i;
+ VertexAttribute *attribute = &pg->vertex_attributes[attr];
+
+ if (attribute->inline_buffer || pg->inline_buffer_length == 0) {
+ return;
+ }
+
+ /* Now upload the previous attribute value */
+ attribute->inline_buffer = (float*)g_malloc(NV2A_MAX_BATCH_LENGTH
+ * sizeof(float) * 4);
+ for (i = 0; i < pg->inline_buffer_length; i++) {
+ memcpy(&attribute->inline_buffer[i * 4],
+ attribute->inline_value,
+ sizeof(float) * 4);
+ }
+}
+
+static void pgraph_finish_inline_buffer_vertex(PGRAPHState *pg)
+{
+ unsigned int i;
+
+ assert(pg->inline_buffer_length < NV2A_MAX_BATCH_LENGTH);
+
+ for (i = 0; i < NV2A_VERTEXSHADER_ATTRIBUTES; i++) {
+ VertexAttribute *attribute = &pg->vertex_attributes[i];
+ if (attribute->inline_buffer) {
+ memcpy(&attribute->inline_buffer[
+ pg->inline_buffer_length * 4],
+ attribute->inline_value,
+ sizeof(float) * 4);
+ }
+ }
+
+ pg->inline_buffer_length++;
+}
+
+void pgraph_init(NV2AState *d)
+{
+ int i;
+
+ PGRAPHState *pg = &d->pgraph;
+
+ pg->pgraph_lock = std::unique_lock(pg->pgraph_mutex, std::defer_lock); // was SDL_CreateMutex();
+ //pg->interrupt_cond = SDL_CreateCond();
+ //pg->fifo_access_cond = SDL_CreateCond();
+ //pg->flip_3d_cond = SDL_CreateCond();
+
+#ifdef COMPILE_OPENGL
+ /* fire up opengl */
+
+ // pg->gl_context = glo_context_create();
+ // assert(pg->gl_context);
+
+#ifdef DEBUG_NV2A_GL
+ glEnable(GL_DEBUG_OUTPUT);
+#endif
+
+ glextensions_init();
+
+ /* DXT textures */
+ // assert(glo_check_extension("GL_EXT_texture_compression_s3tc"));
+ /* Internal RGB565 texture format */
+ // assert(glo_check_extension("GL_ARB_ES2_compatibility"));
+
+ GLint max_vertex_attributes;
+ glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &max_vertex_attributes);
+ assert(max_vertex_attributes >= NV2A_VERTEXSHADER_ATTRIBUTES);
+
+
+ glGenFramebuffers(1, &pg->gl_framebuffer);
+ glBindFramebuffer(GL_FRAMEBUFFER, pg->gl_framebuffer);
+
+ /* need a valid framebuffer to start with */
+ glGenTextures(1, &pg->gl_color_buffer);
+ glBindTexture(GL_TEXTURE_2D, pg->gl_color_buffer);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 640, 480,
+ 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+ GL_TEXTURE_2D, pg->gl_color_buffer, 0);
+
+ assert(glCheckFramebufferStatus(GL_FRAMEBUFFER)
+ == GL_FRAMEBUFFER_COMPLETE);
+
+ //glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
+
+ pg->texture_cache = g_lru_cache_new_full(
+ 0,
+ NULL,
+ texture_key_destroy,
+ 0,
+ NULL,
+ texture_binding_destroy,
+ texture_key_hash,
+ texture_key_equal,
+ texture_key_retrieve,
+ NULL,
+ NULL
+ );
+
+ g_lru_cache_set_max_size(pg->texture_cache, 512);
+
+ pg->shader_cache = g_hash_table_new(shader_hash, shader_equal);
+
+
+ for (i=0; ivertex_attributes[i].gl_converted_buffer);
+ glGenBuffers(1, &pg->vertex_attributes[i].gl_inline_buffer);
+ }
+ glGenBuffers(1, &pg->gl_inline_array_buffer);
+ glGenBuffers(1, &pg->gl_element_buffer);
+
+ glGenBuffers(1, &pg->gl_memory_buffer);
+ glBindBuffer(GL_ARRAY_BUFFER, pg->gl_memory_buffer);
+ glBufferData(GL_ARRAY_BUFFER,
+ d->vram_size,
+ NULL,
+ GL_DYNAMIC_DRAW);
+
+ glGenVertexArrays(1, &pg->gl_vertex_array);
+ glBindVertexArray(pg->gl_vertex_array);
+
+ assert(glGetError() == GL_NO_ERROR);
+
+ // glo_set_current(NULL);
+#endif // COMPILE_OPENGL
+}
+
+void pgraph_destroy(PGRAPHState *pg)
+{
+#ifdef COMPILE_OPENGL
+ SDL_DestroyMutex(pg->pgraph_lock);
+ SDL_DestroyCond(pg->interrupt_cond);
+ SDL_DestroyCond(pg->fifo_access_cond);
+ SDL_DestroyCond(pg->flip_3d_cond);
+
+ // glo_set_current(pg->gl_context);
+
+ if (pg->gl_color_buffer) {
+ glDeleteTextures(1, &pg->gl_color_buffer);
+ }
+ if (pg->gl_zeta_buffer) {
+ glDeleteTextures(1, &pg->gl_zeta_buffer);
+ }
+ glDeleteFramebuffers(1, &pg->gl_framebuffer);
+
+ // TODO: clear out shader cached
+ // TODO: clear out texture cache
+
+ // glo_set_current(NULL);
+
+ // glo_context_destroy(pg->gl_context);
+#endif // COMPILE_OPENGL
+}
+
+#ifdef COMPILE_OPENGL
+static void pgraph_shader_update_constants(PGRAPHState *pg,
+ ShaderBinding *binding,
+ bool binding_changed,
+ bool vertex_program,
+ bool fixed_function)
+{
+ int i, j;
+
+ /* update combiner constants */
+ for (i = 0; i<= 8; i++) {
+ uint32_t constant[2];
+ if (i == 8) {
+ /* final combiner */
+ constant[0] = pg->regs[NV_PGRAPH_SPECFOGFACTOR0];
+ constant[1] = pg->regs[NV_PGRAPH_SPECFOGFACTOR1];
+ } else {
+ constant[0] = pg->regs[NV_PGRAPH_COMBINEFACTOR0 + i * 4];
+ constant[1] = pg->regs[NV_PGRAPH_COMBINEFACTOR1 + i * 4];
+ }
+
+ for (j = 0; j < 2; j++) {
+ GLint loc = binding->psh_constant_loc[i][j];
+ if (loc != -1) {
+ float value[4];
+ value[0] = (float) ((constant[j] >> 16) & 0xFF) / 255.0f;
+ value[1] = (float) ((constant[j] >> 8) & 0xFF) / 255.0f;
+ value[2] = (float) (constant[j] & 0xFF) / 255.0f;
+ value[3] = (float) ((constant[j] >> 24) & 0xFF) / 255.0f;
+
+ glUniform4fv(loc, 1, value);
+ }
+ }
+ }
+ if (binding->alpha_ref_loc != -1) {
+ float alpha_ref = GET_MASK(pg->regs[NV_PGRAPH_CONTROL_0],
+ NV_PGRAPH_CONTROL_0_ALPHAREF) / 255.0f;
+ glUniform1f(binding->alpha_ref_loc, alpha_ref);
+ }
+
+
+ /* For each texture stage */
+ for (i = 0; i < NV2A_MAX_TEXTURES; i++) {
+ // char name[32];
+ GLint loc;
+
+ /* Bump luminance only during stages 1 - 3 */
+ if (i > 0) {
+ loc = binding->bump_mat_loc[i];
+ if (loc != -1) {
+ glUniformMatrix2fv(loc, 1, GL_FALSE, pg->bump_env_matrix[i - 1]);
+ }
+ loc = binding->bump_scale_loc[i];
+ if (loc != -1) {
+ glUniform1f(loc, *(float*)&pg->regs[
+ NV_PGRAPH_BUMPSCALE1 + (i - 1) * 4]);
+ }
+ loc = binding->bump_offset_loc[i];
+ if (loc != -1) {
+ glUniform1f(loc, *(float*)&pg->regs[
+ NV_PGRAPH_BUMPOFFSET1 + (i - 1) * 4]);
+ }
+ }
+
+ }
+
+ if (binding->fog_color_loc != -1) {
+ uint32_t fog_color = pg->regs[NV_PGRAPH_FOGCOLOR];
+ glUniform4f(binding->fog_color_loc,
+ GET_MASK(fog_color, NV_PGRAPH_FOGCOLOR_RED) / 255.0f,
+ GET_MASK(fog_color, NV_PGRAPH_FOGCOLOR_GREEN) / 255.0f,
+ GET_MASK(fog_color, NV_PGRAPH_FOGCOLOR_BLUE) / 255.0f,
+ GET_MASK(fog_color, NV_PGRAPH_FOGCOLOR_ALPHA) / 255.0f);
+ }
+ if (binding->fog_param_loc[0] != -1) {
+ glUniform1f(binding->fog_param_loc[0],
+ *(float*)&pg->regs[NV_PGRAPH_FOGPARAM0]);
+ }
+ if (binding->fog_param_loc[1] != -1) {
+ glUniform1f(binding->fog_param_loc[1],
+ *(float*)&pg->regs[NV_PGRAPH_FOGPARAM1]);
+ }
+
+
+ float zclip_max = *(float*)&pg->regs[NV_PGRAPH_ZCLIPMAX];
+ float zclip_min = *(float*)&pg->regs[NV_PGRAPH_ZCLIPMIN];
+
+ if (fixed_function) {
+ /* update lighting constants */
+ struct {
+ uint32_t* v;
+ bool* dirty;
+ GLint* locs;
+ size_t len;
+ } lighting_arrays[] = {
+ {&pg->ltctxa[0][0], &pg->ltctxa_dirty[0], binding->ltctxa_loc, NV2A_LTCTXA_COUNT},
+ {&pg->ltctxb[0][0], &pg->ltctxb_dirty[0], binding->ltctxb_loc, NV2A_LTCTXB_COUNT},
+ {&pg->ltc1[0][0], &pg->ltc1_dirty[0], binding->ltc1_loc, NV2A_LTC1_COUNT},
+ };
+
+ for (i=0; ilight_infinite_half_vector_loc[i];
+ if (loc != -1) {
+ glUniform3fv(loc, 1, pg->light_infinite_half_vector[i]);
+ }
+ loc = binding->light_infinite_direction_loc[i];
+ if (loc != -1) {
+ glUniform3fv(loc, 1, pg->light_infinite_direction[i]);
+ }
+
+ loc = binding->light_local_position_loc[i];
+ if (loc != -1) {
+ glUniform3fv(loc, 1, pg->light_local_position[i]);
+ }
+ loc = binding->light_local_attenuation_loc[i];
+ if (loc != -1) {
+ glUniform3fv(loc, 1, pg->light_local_attenuation[i]);
+ }
+ }
+
+ /* estimate the viewport by assuming it matches the surface ... */
+ //FIXME: Get surface dimensions?
+ float m11 = 0.5 * pg->surface_shape.clip_width;
+ float m22 = -0.5 * pg->surface_shape.clip_height;
+ float m33 = zclip_max - zclip_min;
+ //float m41 = m11;
+ //float m42 = -m22;
+ float m43 = zclip_min;
+ //float m44 = 1.0f;
+
+ if (m33 == 0.0f) {
+ m33 = 1.0f;
+ }
+ float invViewport[16] = {
+ 1.0f/m11, 0.0f, 0.0f, 0.0f,
+ 0.0f, 1.0f/m22, 0.0f, 0.0f,
+ 0.0f, 0.0f, 1.0f/m33, 0.0f,
+ -1.0f, 1.0f, -m43/m33, 1.0f
+ };
+
+ if (binding->inv_viewport_loc != -1) {
+ glUniformMatrix4fv(binding->inv_viewport_loc,
+ 1, GL_FALSE, &invViewport[0]);
+ }
+
+ }
+
+ /* update vertex program constants */
+ for (i=0; ivsh_constants_dirty[i] && !binding_changed) continue;
+
+ GLint loc = binding->vsh_constant_loc[i];
+ //assert(loc != -1);
+ if (loc != -1) {
+ glUniform4fv(loc, 1, (const GLfloat*)pg->vsh_constants[i]);
+ }
+ pg->vsh_constants_dirty[i] = false;
+ }
+
+ if (binding->surface_size_loc != -1) {
+ glUniform2f(binding->surface_size_loc, pg->surface_shape.clip_width,
+ pg->surface_shape.clip_height);
+ }
+
+ if (binding->clip_range_loc != -1) {
+ glUniform2f(binding->clip_range_loc, zclip_min, zclip_max);
+ }
+
+}
+#endif // COMPILE_OPENGL
+
+static void pgraph_bind_shaders(PGRAPHState *pg)
+{
+ int i, j;
+
+ bool vertex_program = GET_MASK(pg->regs[NV_PGRAPH_CSV0_D],
+ NV_PGRAPH_CSV0_D_MODE) == 2;
+
+ bool fixed_function = GET_MASK(pg->regs[NV_PGRAPH_CSV0_D],
+ NV_PGRAPH_CSV0_D_MODE) == 0;
+
+ int program_start = GET_MASK(pg->regs[NV_PGRAPH_CSV0_C],
+ NV_PGRAPH_CSV0_C_CHEOPS_PROGRAM_START);
+
+#ifdef COMPILE_OPENGL
+ NV2A_GL_DGROUP_BEGIN("%s (VP: %s FFP: %s)", __func__,
+ vertex_program ? "yes" : "no",
+ fixed_function ? "yes" : "no");
+
+ ShaderBinding* old_binding = pg->shader_binding;
+
+ ShaderState state = {
+ .psh = (PshState){
+ /* register combiner stuff */
+ .combiner_control = pg->regs[NV_PGRAPH_COMBINECTL],
+ .shader_stage_program = pg->regs[NV_PGRAPH_SHADERPROG],
+ .other_stage_input = pg->regs[NV_PGRAPH_SHADERCTL],
+ .final_inputs_0 = pg->regs[NV_PGRAPH_COMBINESPECFOG0],
+ .final_inputs_1 = pg->regs[NV_PGRAPH_COMBINESPECFOG1],
+
+ .alpha_test = pg->regs[NV_PGRAPH_CONTROL_0]
+ & NV_PGRAPH_CONTROL_0_ALPHATESTENABLE,
+ .alpha_func = (enum PshAlphaFunc)GET_MASK(pg->regs[NV_PGRAPH_CONTROL_0],
+ NV_PGRAPH_CONTROL_0_ALPHAFUNC),
+ },
+
+ /* fixed function stuff */
+ .skinning = (enum VshSkinning)GET_MASK(pg->regs[NV_PGRAPH_CSV0_D],
+ NV_PGRAPH_CSV0_D_SKIN),
+ .lighting = GET_MASK(pg->regs[NV_PGRAPH_CSV0_C],
+ NV_PGRAPH_CSV0_C_LIGHTING),
+ .normalization = pg->regs[NV_PGRAPH_CSV0_C]
+ & NV_PGRAPH_CSV0_C_NORMALIZATION_ENABLE,
+
+ .fixed_function = fixed_function,
+
+ /* vertex program stuff */
+ .vertex_program = vertex_program,
+ .z_perspective = pg->regs[NV_PGRAPH_CONTROL_0]
+ & NV_PGRAPH_CONTROL_0_Z_PERSPECTIVE_ENABLE,
+
+ /* geometry shader stuff */
+ .primitive_mode = (enum ShaderPrimitiveMode)pg->primitive_mode,
+ .polygon_front_mode = (enum ShaderPolygonMode)GET_MASK(pg->regs[NV_PGRAPH_SETUPRASTER],
+ NV_PGRAPH_SETUPRASTER_FRONTFACEMODE),
+ .polygon_back_mode = (enum ShaderPolygonMode)GET_MASK(pg->regs[NV_PGRAPH_SETUPRASTER],
+ NV_PGRAPH_SETUPRASTER_BACKFACEMODE),
+ };
+
+ state.program_length = 0;
+ memset(state.program_data, 0, sizeof(state.program_data));
+
+ if (vertex_program) {
+ // copy in vertex program tokens
+ for (i = program_start; i < NV2A_MAX_TRANSFORM_PROGRAM_LENGTH; i++) {
+ uint32_t *cur_token = (uint32_t*)&pg->program_data[i];
+ memcpy(&state.program_data[state.program_length],
+ cur_token,
+ VSH_TOKEN_SIZE * sizeof(uint32_t));
+ state.program_length++;
+
+ if (vsh_get_field(cur_token, FLD_FINAL)) {
+ break;
+ }
+ }
+ }
+
+ /* Texgen */
+ for (i = 0; i < 4; i++) {
+ unsigned int reg = (i < 2) ? NV_PGRAPH_CSV1_A : NV_PGRAPH_CSV1_B;
+ for (j = 0; j < 4; j++) {
+ unsigned int masks[] = {
+ (i % 2) ? NV_PGRAPH_CSV1_A_T1_S : NV_PGRAPH_CSV1_A_T0_S,
+ (i % 2) ? NV_PGRAPH_CSV1_A_T1_T : NV_PGRAPH_CSV1_A_T0_T,
+ (i % 2) ? NV_PGRAPH_CSV1_A_T1_R : NV_PGRAPH_CSV1_A_T0_R,
+ (i % 2) ? NV_PGRAPH_CSV1_A_T1_Q : NV_PGRAPH_CSV1_A_T0_Q
+ };
+ state.texgen[i][j] = (enum VshTexgen)GET_MASK(pg->regs[reg], masks[j]);
+ }
+ }
+
+ /* Fog */
+ state.fog_enable = pg->regs[NV_PGRAPH_CONTROL_3]
+ & NV_PGRAPH_CONTROL_3_FOGENABLE;
+ if (state.fog_enable) {
+ /*FIXME: Use CSV0_D? */
+ state.fog_mode = (enum VshFogMode)GET_MASK(pg->regs[NV_PGRAPH_CONTROL_3],
+ NV_PGRAPH_CONTROL_3_FOG_MODE);
+ state.foggen = (enum VshFoggen)GET_MASK(pg->regs[NV_PGRAPH_CSV0_D],
+ NV_PGRAPH_CSV0_D_FOGGENMODE);
+ } else {
+ /* FIXME: Do we still pass the fogmode? */
+ state.fog_mode = (enum VshFogMode)0;
+ state.foggen = (enum VshFoggen)0;
+ }
+
+ /* Texture matrices */
+ for (i = 0; i < 4; i++) {
+ state.texture_matrix_enable[i] = pg->texture_matrix_enable[i];
+ }
+
+ /* Lighting */
+ if (state.lighting) {
+ for (i = 0; i < NV2A_MAX_LIGHTS; i++) {
+ state.light[i] = (enum VshLight)GET_MASK(pg->regs[NV_PGRAPH_CSV0_D],
+ NV_PGRAPH_CSV0_D_LIGHT0 << (i * 2));
+ }
+ }
+
+ for (i = 0; i < 8; i++) {
+ state.psh.rgb_inputs[i] = pg->regs[NV_PGRAPH_COMBINECOLORI0 + i * 4];
+ state.psh.rgb_outputs[i] = pg->regs[NV_PGRAPH_COMBINECOLORO0 + i * 4];
+ state.psh.alpha_inputs[i] = pg->regs[NV_PGRAPH_COMBINEALPHAI0 + i * 4];
+ state.psh.alpha_outputs[i] = pg->regs[NV_PGRAPH_COMBINEALPHAO0 + i * 4];
+ //constant_0[i] = pg->regs[NV_PGRAPH_COMBINEFACTOR0 + i * 4];
+ //constant_1[i] = pg->regs[NV_PGRAPH_COMBINEFACTOR1 + i * 4];
+ }
+
+ for (i = 0; i < 4; i++) {
+ state.psh.rect_tex[i] = false;
+ bool enabled = pg->regs[NV_PGRAPH_TEXCTL0_0 + i*4]
+ & NV_PGRAPH_TEXCTL0_0_ENABLE;
+ unsigned int color_format =
+ GET_MASK(pg->regs[NV_PGRAPH_TEXFMT0 + i*4],
+ NV_PGRAPH_TEXFMT0_COLOR);
+
+ if (enabled && kelvin_color_format_map[color_format].linear) {
+ state.psh.rect_tex[i] = true;
+ }
+
+ for (j = 0; j < 4; j++) {
+ state.psh.compare_mode[i][j] =
+ (pg->regs[NV_PGRAPH_SHADERCLIPMODE] >> (4 * i + j)) & 1;
+ }
+ state.psh.alphakill[i] = pg->regs[NV_PGRAPH_TEXCTL0_0 + i*4]
+ & NV_PGRAPH_TEXCTL0_0_ALPHAKILLEN;
+ }
+
+ ShaderBinding* cached_shader = (ShaderBinding*)g_hash_table_lookup(pg->shader_cache, &state);
+ if (cached_shader) {
+ pg->shader_binding = cached_shader;
+ } else {
+ pg->shader_binding = generate_shaders(state);
+
+ /* cache it */
+ ShaderState *cache_state = (ShaderState *)g_malloc(sizeof(*cache_state));
+ memcpy(cache_state, &state, sizeof(*cache_state));
+ g_hash_table_insert(pg->shader_cache, cache_state,
+ (gpointer)pg->shader_binding);
+ }
+
+ bool binding_changed = (pg->shader_binding != old_binding);
+
+ glUseProgram(pg->shader_binding->gl_program);
+
+ pgraph_shader_update_constants(pg, pg->shader_binding, binding_changed,
+ vertex_program, fixed_function);
+
+ NV2A_GL_DGROUP_END();
+#endif // COMPILE_OPENGL
+}
+
+static bool pgraph_framebuffer_dirty(PGRAPHState *pg)
+{
+ bool shape_changed = memcmp(&pg->surface_shape, &pg->last_surface_shape,
+ sizeof(SurfaceShape)) != 0;
+ if (!shape_changed || (!pg->surface_shape.color_format
+ && !pg->surface_shape.zeta_format)) {
+ return false;
+ }
+ return true;
+}
+
+static bool pgraph_color_write_enabled(PGRAPHState *pg)
+{
+ return pg->regs[NV_PGRAPH_CONTROL_0] & (
+ NV_PGRAPH_CONTROL_0_ALPHA_WRITE_ENABLE
+ | NV_PGRAPH_CONTROL_0_RED_WRITE_ENABLE
+ | NV_PGRAPH_CONTROL_0_GREEN_WRITE_ENABLE
+ | NV_PGRAPH_CONTROL_0_BLUE_WRITE_ENABLE);
+}
+
+static bool pgraph_zeta_write_enabled(PGRAPHState *pg)
+{
+ return pg->regs[NV_PGRAPH_CONTROL_0] & (
+ NV_PGRAPH_CONTROL_0_ZWRITEENABLE
+ | NV_PGRAPH_CONTROL_0_STENCIL_WRITE_ENABLE);
+}
+
+static void pgraph_set_surface_dirty(PGRAPHState *pg, bool color, bool zeta)
+{
+ NV2A_DPRINTF("pgraph_set_surface_dirty(%d, %d) -- %d %d\n",
+ color, zeta,
+ pgraph_color_write_enabled(pg), pgraph_zeta_write_enabled(pg));
+ /* FIXME: Does this apply to CLEARs too? */
+ color = color && pgraph_color_write_enabled(pg);
+ zeta = zeta && pgraph_zeta_write_enabled(pg);
+ pg->surface_color.draw_dirty |= color;
+ pg->surface_zeta.draw_dirty |= zeta;
+}
+
+static void pgraph_update_surface_part(NV2AState *d, bool upload, bool color) {
+ PGRAPHState *pg = &d->pgraph;
+
+ unsigned int width, height;
+ pgraph_get_surface_dimensions(pg, &width, &height);
+ pgraph_apply_anti_aliasing_factor(pg, &width, &height);
+
+ Surface *surface;
+ hwaddr dma_address;
+ GLuint *gl_buffer;
+ unsigned int bytes_per_pixel;
+ GLenum gl_internal_format, gl_format, gl_type, gl_attachment;
+
+ if (color) {
+ surface = &pg->surface_color;
+ dma_address = pg->dma_color;
+ gl_buffer = &pg->gl_color_buffer;
+
+ assert(pg->surface_shape.color_format != 0);
+ //assert(pg->surface_shape.color_format
+ // < ARRAY_SIZE(kelvin_surface_color_format_map));
+ SurfaceColorFormatInfo f =
+ kelvin_surface_color_format_map[pg->surface_shape.color_format];
+ if (f.bytes_per_pixel == 0) {
+ fprintf(stderr, "nv2a: unimplemented color surface format 0x%x\n",
+ pg->surface_shape.color_format);
+ abort();
+ }
+
+ bytes_per_pixel = f.bytes_per_pixel;
+ gl_internal_format = f.gl_internal_format;
+ gl_format = f.gl_format;
+ gl_type = f.gl_type;
+ gl_attachment = GL_COLOR_ATTACHMENT0;
+
+ } else {
+ surface = &pg->surface_zeta;
+ dma_address = pg->dma_zeta;
+ gl_buffer = &pg->gl_zeta_buffer;
+
+ assert(pg->surface_shape.zeta_format != 0);
+ switch (pg->surface_shape.zeta_format) {
+ case NV097_SET_SURFACE_FORMAT_ZETA_Z16:
+ bytes_per_pixel = 2;
+ gl_format = GL_DEPTH_COMPONENT;
+ gl_attachment = GL_DEPTH_ATTACHMENT;
+ if (pg->surface_shape.z_format) {
+ gl_type = GL_HALF_FLOAT;
+ gl_internal_format = GL_DEPTH_COMPONENT32F;
+ } else {
+ gl_type = GL_UNSIGNED_SHORT;
+ gl_internal_format = GL_DEPTH_COMPONENT16;
+ }
+ break;
+ case NV097_SET_SURFACE_FORMAT_ZETA_Z24S8:
+ bytes_per_pixel = 4;
+ gl_format = GL_DEPTH_STENCIL;
+ gl_attachment = GL_DEPTH_STENCIL_ATTACHMENT;
+ if (pg->surface_shape.z_format) {
+ assert(false);
+ gl_type = GL_FLOAT_32_UNSIGNED_INT_24_8_REV;
+ gl_internal_format = GL_DEPTH32F_STENCIL8;
+ } else {
+ gl_type = GL_UNSIGNED_INT_24_8;
+ gl_internal_format = GL_DEPTH24_STENCIL8;
+ }
+ break;
+ default:
+ assert(false);
+ break;
+ }
+ }
+
+
+ DMAObject dma = nv_dma_load(d, dma_address);
+ /* There's a bunch of bugs that could cause us to hit this function
+ * at the wrong time and get a invalid dma object.
+ * Check that it's sane. */
+ assert(dma.dma_class == NV_DMA_IN_MEMORY_CLASS);
+
+ assert(dma.address + surface->offset != 0);
+ assert(surface->offset <= dma.limit);
+ assert(surface->offset + surface->pitch * height <= dma.limit + 1);
+
+ hwaddr data_len;
+ uint8_t *data = (uint8_t*)nv_dma_map(d, dma_address, &data_len);
+
+ /* TODO */
+ // assert(pg->surface_clip_x == 0 && pg->surface_clip_y == 0);
+
+ bool swizzle = (pg->surface_type == NV097_SET_SURFACE_FORMAT_TYPE_SWIZZLE);
+
+ uint8_t *buf = data + surface->offset;
+ if (swizzle) {
+ buf = (uint8_t*)g_malloc(height * surface->pitch);
+ }
+
+ bool dirty = surface->buffer_dirty;
+ if (color) {
+#if 1
+ dirty |= 1;
+#else
+ dirty |= memory_region_test_and_clear_dirty(d->vram,
+ dma.address + surface->offset,
+ surface->pitch * height,
+ DIRTY_MEMORY_NV2A);
+#endif
+ }
+ if (upload && dirty) {
+ /* surface modified (or moved) by the cpu.
+ * copy it into the opengl renderbuffer */
+ assert(!surface->draw_dirty);
+
+ assert(surface->pitch % bytes_per_pixel == 0);
+
+ if (swizzle) {
+ unswizzle_rect(data + surface->offset,
+ width, height,
+ buf,
+ surface->pitch,
+ bytes_per_pixel);
+ }
+
+#ifdef COMPILE_OPENGL
+ if (!color) {
+ /* need to clear the depth_stencil and depth attachment for zeta */
+ glFramebufferTexture2D(GL_FRAMEBUFFER,
+ GL_DEPTH_ATTACHMENT,
+ GL_TEXTURE_2D,
+ 0, 0);
+ glFramebufferTexture2D(GL_FRAMEBUFFER,
+ GL_DEPTH_STENCIL_ATTACHMENT,
+ GL_TEXTURE_2D,
+ 0, 0);
+ }
+
+ glFramebufferTexture2D(GL_FRAMEBUFFER,
+ gl_attachment,
+ GL_TEXTURE_2D,
+ 0, 0);
+
+ if (*gl_buffer) {
+ glDeleteTextures(1, gl_buffer);
+ *gl_buffer = 0;
+ }
+
+ glGenTextures(1, gl_buffer);
+ glBindTexture(GL_TEXTURE_2D, *gl_buffer);
+
+ /* This is VRAM so we can't do this inplace! */
+ uint8_t *flipped_buf = (uint8_t*)g_malloc(width * height * bytes_per_pixel);
+ unsigned int irow;
+ for (irow = 0; irow < height; irow++) {
+ memcpy(&flipped_buf[width * (height - irow - 1)
+ * bytes_per_pixel],
+ &buf[surface->pitch * irow],
+ width * bytes_per_pixel);
+ }
+
+ glTexImage2D(GL_TEXTURE_2D, 0, gl_internal_format,
+ width, height, 0,
+ gl_format, gl_type,
+ flipped_buf);
+
+ g_free(flipped_buf);
+
+ glFramebufferTexture2D(GL_FRAMEBUFFER,
+ gl_attachment,
+ GL_TEXTURE_2D,
+ *gl_buffer, 0);
+
+ assert(glCheckFramebufferStatus(GL_FRAMEBUFFER)
+ == GL_FRAMEBUFFER_COMPLETE);
+#endif // COMPILE_OPENGL
+
+ if (color) {
+ pgraph_update_memory_buffer(d, dma.address + surface->offset,
+ surface->pitch * height, true);
+ }
+ surface->buffer_dirty = false;
+
+
+ uint8_t *out = data + surface->offset + 64;
+ NV2A_DPRINTF("upload_surface %s 0x%" HWADDR_PRIx " - 0x%" HWADDR_PRIx ", "
+ "(0x%" HWADDR_PRIx " - 0x%" HWADDR_PRIx ", "
+ "%d %d, %d %d, %d) - %x %x %x %x\n",
+ color ? "color" : "zeta",
+ dma.address, dma.address + dma.limit,
+ dma.address + surface->offset,
+ dma.address + surface->pitch * height,
+ pg->surface_shape.clip_x, pg->surface_shape.clip_y,
+ pg->surface_shape.clip_width,
+ pg->surface_shape.clip_height,
+ surface->pitch,
+ out[0], out[1], out[2], out[3]);
+
+ }
+
+ if (!upload && surface->draw_dirty) {
+#ifdef COMPILE_OPENGL
+ /* read the opengl framebuffer into the surface */
+
+ glo_readpixels(gl_format, gl_type,
+ bytes_per_pixel, surface->pitch,
+ width, height,
+ buf);
+ assert(glGetError() == GL_NO_ERROR);
+#endif // COMPILE_OPENGL
+
+ if (swizzle) {
+ swizzle_rect(buf,
+ width, height,
+ data + surface->offset,
+ surface->pitch,
+ bytes_per_pixel);
+ }
+
+ // memory_region_set_client_dirty(d->vram,
+ // dma.address + surface->offset,
+ // surface->pitch * height,
+ // DIRTY_MEMORY_VGA);
+
+ if (color) {
+ pgraph_update_memory_buffer(d, dma.address + surface->offset,
+ surface->pitch * height, true);
+ }
+
+ surface->draw_dirty = false;
+ surface->write_enabled_cache = false;
+
+ uint8_t *out = data + surface->offset + 64;
+ NV2A_DPRINTF("read_surface %s 0x%" HWADDR_PRIx " - 0x%" HWADDR_PRIx ", "
+ "(0x%" HWADDR_PRIx " - 0x%" HWADDR_PRIx ", "
+ "%d %d, %d %d, %d) - %x %x %x %x\n",
+ color ? "color" : "zeta",
+ dma.address, dma.address + dma.limit,
+ dma.address + surface->offset,
+ dma.address + surface->pitch * pg->surface_shape.clip_height,
+ pg->surface_shape.clip_x, pg->surface_shape.clip_y,
+ pg->surface_shape.clip_width, pg->surface_shape.clip_height,
+ surface->pitch,
+ out[0], out[1], out[2], out[3]);
+
+ }
+
+ if (swizzle) {
+ g_free(buf);
+ }
+}
+
+static void pgraph_update_surface(NV2AState *d, bool upload,
+ bool color_write, bool zeta_write)
+{
+ PGRAPHState *pg = &d->pgraph;
+
+ pg->surface_shape.z_format = GET_MASK(pg->regs[NV_PGRAPH_SETUPRASTER],
+ NV_PGRAPH_SETUPRASTER_Z_FORMAT);
+
+ /* FIXME: Does this apply to CLEARs too? */
+ color_write = color_write && pgraph_color_write_enabled(pg);
+ zeta_write = zeta_write && pgraph_zeta_write_enabled(pg);
+
+ if (upload && pgraph_framebuffer_dirty(pg)) {
+ assert(!pg->surface_color.draw_dirty);
+ assert(!pg->surface_zeta.draw_dirty);
+
+ pg->surface_color.buffer_dirty = true;
+ pg->surface_zeta.buffer_dirty = true;
+#ifdef COMPILE_OPENGL
+
+ glFramebufferTexture2D(GL_FRAMEBUFFER,
+ GL_COLOR_ATTACHMENT0,
+ GL_TEXTURE_2D,
+ 0, 0);
+
+ if (pg->gl_color_buffer) {
+ glDeleteTextures(1, &pg->gl_color_buffer);
+ pg->gl_color_buffer = 0;
+ }
+
+ glFramebufferTexture2D(GL_FRAMEBUFFER,
+ GL_DEPTH_ATTACHMENT,
+ GL_TEXTURE_2D,
+ 0, 0);
+ glFramebufferTexture2D(GL_FRAMEBUFFER,
+ GL_DEPTH_STENCIL_ATTACHMENT,
+ GL_TEXTURE_2D,
+ 0, 0);
+
+ if (pg->gl_zeta_buffer) {
+ glDeleteTextures(1, &pg->gl_zeta_buffer);
+ pg->gl_zeta_buffer = 0;
+ }
+#endif // COMPILE_OPENGL
+
+ memcpy(&pg->last_surface_shape, &pg->surface_shape,
+ sizeof(SurfaceShape));
+ }
+
+ if ((color_write || (!upload && pg->surface_color.write_enabled_cache))
+ && (upload || pg->surface_color.draw_dirty)) {
+ pgraph_update_surface_part(d, upload, true);
+ }
+
+
+ if ((zeta_write || (!upload && pg->surface_zeta.write_enabled_cache))
+ && (upload || pg->surface_zeta.draw_dirty)) {
+ pgraph_update_surface_part(d, upload, false);
+ }
+
+}
+
+static void pgraph_bind_textures(NV2AState *d)
+{
+#ifdef COMPILE_OPENGL
+ int i;
+ PGRAPHState *pg = &d->pgraph;
+
+ NV2A_GL_DGROUP_BEGIN("%s", __func__);
+
+ for (i=0; iregs[NV_PGRAPH_TEXCTL0_0 + i*4];
+ uint32_t ctl_1 = pg->regs[NV_PGRAPH_TEXCTL1_0 + i*4];
+ uint32_t fmt = pg->regs[NV_PGRAPH_TEXFMT0 + i*4];
+ uint32_t filter = pg->regs[NV_PGRAPH_TEXFILTER0 + i*4];
+ uint32_t address = pg->regs[NV_PGRAPH_TEXADDRESS0 + i*4];
+ uint32_t palette = pg->regs[NV_PGRAPH_TEXPALETTE0 + i*4];
+
+ bool enabled = GET_MASK(ctl_0, NV_PGRAPH_TEXCTL0_0_ENABLE);
+ unsigned int min_mipmap_level =
+ GET_MASK(ctl_0, NV_PGRAPH_TEXCTL0_0_MIN_LOD_CLAMP);
+ unsigned int max_mipmap_level =
+ GET_MASK(ctl_0, NV_PGRAPH_TEXCTL0_0_MAX_LOD_CLAMP);
+
+ unsigned int pitch =
+ GET_MASK(ctl_1, NV_PGRAPH_TEXCTL1_0_IMAGE_PITCH);
+
+ unsigned int dma_select =
+ GET_MASK(fmt, NV_PGRAPH_TEXFMT0_CONTEXT_DMA);
+ bool cubemap =
+ GET_MASK(fmt, NV_PGRAPH_TEXFMT0_CUBEMAPENABLE);
+ unsigned int dimensionality =
+ GET_MASK(fmt, NV_PGRAPH_TEXFMT0_DIMENSIONALITY);
+ unsigned int color_format = GET_MASK(fmt, NV_PGRAPH_TEXFMT0_COLOR);
+ unsigned int levels = GET_MASK(fmt, NV_PGRAPH_TEXFMT0_MIPMAP_LEVELS);
+ unsigned int log_width = GET_MASK(fmt, NV_PGRAPH_TEXFMT0_BASE_SIZE_U);
+ unsigned int log_height = GET_MASK(fmt, NV_PGRAPH_TEXFMT0_BASE_SIZE_V);
+ unsigned int log_depth = GET_MASK(fmt, NV_PGRAPH_TEXFMT0_BASE_SIZE_P);
+
+ unsigned int rect_width =
+ GET_MASK(pg->regs[NV_PGRAPH_TEXIMAGERECT0 + i*4],
+ NV_PGRAPH_TEXIMAGERECT0_WIDTH);
+ unsigned int rect_height =
+ GET_MASK(pg->regs[NV_PGRAPH_TEXIMAGERECT0 + i*4],
+ NV_PGRAPH_TEXIMAGERECT0_HEIGHT);
+
+ unsigned int lod_bias =
+ GET_MASK(filter, NV_PGRAPH_TEXFILTER0_MIPMAP_LOD_BIAS);
+ unsigned int min_filter = GET_MASK(filter, NV_PGRAPH_TEXFILTER0_MIN);
+ unsigned int mag_filter = GET_MASK(filter, NV_PGRAPH_TEXFILTER0_MAG);
+
+ unsigned int addru = GET_MASK(address, NV_PGRAPH_TEXADDRESS0_ADDRU);
+ unsigned int addrv = GET_MASK(address, NV_PGRAPH_TEXADDRESS0_ADDRV);
+ unsigned int addrp = GET_MASK(address, NV_PGRAPH_TEXADDRESS0_ADDRP);
+
+ unsigned int border_source = GET_MASK(fmt,
+ NV_PGRAPH_TEXFMT0_BORDER_SOURCE);
+ uint32_t border_color = pg->regs[NV_PGRAPH_BORDERCOLOR0 + i*4];
+
+ unsigned int offset = pg->regs[NV_PGRAPH_TEXOFFSET0 + i*4];
+
+ bool palette_dma_select =
+ GET_MASK(palette, NV_PGRAPH_TEXPALETTE0_CONTEXT_DMA);
+ unsigned int palette_length_index =
+ GET_MASK(palette, NV_PGRAPH_TEXPALETTE0_LENGTH);
+ unsigned int palette_offset =
+ palette & NV_PGRAPH_TEXPALETTE0_OFFSET;
+
+ unsigned int palette_length = 0;
+ switch (palette_length_index) {
+ case NV_PGRAPH_TEXPALETTE0_LENGTH_256: palette_length = 256; break;
+ case NV_PGRAPH_TEXPALETTE0_LENGTH_128: palette_length = 128; break;
+ case NV_PGRAPH_TEXPALETTE0_LENGTH_64: palette_length = 64; break;
+ case NV_PGRAPH_TEXPALETTE0_LENGTH_32: palette_length = 32; break;
+ default: assert(false); break;
+ }
+
+ /* Check for unsupported features */
+ assert(!(filter & NV_PGRAPH_TEXFILTER0_ASIGNED));
+ assert(!(filter & NV_PGRAPH_TEXFILTER0_RSIGNED));
+ assert(!(filter & NV_PGRAPH_TEXFILTER0_GSIGNED));
+ assert(!(filter & NV_PGRAPH_TEXFILTER0_BSIGNED));
+
+ glActiveTexture(GL_TEXTURE0 + i);
+ if (!enabled) {
+ glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
+ glBindTexture(GL_TEXTURE_RECTANGLE, 0);
+ glBindTexture(GL_TEXTURE_1D, 0);
+ glBindTexture(GL_TEXTURE_2D, 0);
+ glBindTexture(GL_TEXTURE_3D, 0);
+ continue;
+ }
+
+ if (!pg->texture_dirty[i] && pg->texture_binding[i]) {
+ glBindTexture(pg->texture_binding[i]->gl_target,
+ pg->texture_binding[i]->gl_texture);
+ continue;
+ }
+
+ NV2A_DPRINTF(" texture %d is format 0x%x, (r %d, %d or %d, %d, %d; %d%s),"
+ " filter %x %x, levels %d-%d %d bias %d\n",
+ i, color_format,
+ rect_width, rect_height,
+ 1 << log_width, 1 << log_height, 1 << log_depth,
+ pitch,
+ cubemap ? "; cubemap" : "",
+ min_filter, mag_filter,
+ min_mipmap_level, max_mipmap_level, levels,
+ lod_bias);
+
+ assert(color_format < ARRAY_SIZE(kelvin_color_format_map));
+ ColorFormatInfo f = kelvin_color_format_map[color_format];
+ if (f.bytes_per_pixel == 0) {
+ fprintf(stderr, "nv2a: unimplemented texture color format 0x%x\n",
+ color_format);
+ abort();
+ }
+
+ unsigned int width, height, depth;
+ if (f.linear) {
+ assert(dimensionality == 2);
+ width = rect_width;
+ height = rect_height;
+ depth = 1;
+ } else {
+ width = 1 << log_width;
+ height = 1 << log_height;
+ depth = 1 << log_depth;
+
+ /* FIXME: What about 3D mipmaps? */
+ levels = MIN(levels, max_mipmap_level + 1);
+ if (f.gl_format != 0) {
+ /* Discard mipmap levels that would be smaller than 1x1.
+ * FIXME: Is this actually needed?
+ *
+ * >> Level 0: 32 x 4
+ * Level 1: 16 x 2
+ * Level 2: 8 x 1
+ * Level 3: 4 x 1
+ * Level 4: 2 x 1
+ * Level 5: 1 x 1
+ */
+ levels = MIN(levels, MAX(log_width, log_height) + 1);
+ } else {
+ /* OpenGL requires DXT textures to always have a width and
+ * height a multiple of 4. The Xbox and DirectX handles DXT
+ * textures smaller than 4 by padding the reset of the block.
+ *
+ * See:
+ * https://msdn.microsoft.com/en-us/library/windows/desktop/bb204843(v=vs.85).aspx
+ * https://msdn.microsoft.com/en-us/library/windows/desktop/bb694531%28v=vs.85%29.aspx#Virtual_Size
+ *
+ * Work around this for now by discarding mipmap levels that
+ * would result in too-small textures. A correct solution
+ * will be to decompress these levels manually, or add texture
+ * sampling logic.
+ *
+ * >> Level 0: 64 x 8
+ * Level 1: 32 x 4
+ * Level 2: 16 x 2 << Ignored
+ * >> Level 0: 16 x 16
+ * Level 1: 8 x 8
+ * Level 2: 4 x 4 << OK!
+ */
+ if (log_width < 2 || log_height < 2) {
+ /* Base level is smaller than 4x4... */
+ levels = 1;
+ } else {
+ levels = MIN(levels, MIN(log_width, log_height) - 1);
+ }
+ }
+ assert(levels > 0);
+ }
+
+ hwaddr dma_len;
+ uint8_t *texture_data;
+ if (dma_select) {
+ texture_data = (uint8_t*)nv_dma_map(d, pg->dma_b, &dma_len);
+ } else {
+ texture_data = (uint8_t*)nv_dma_map(d, pg->dma_a, &dma_len);
+ }
+ assert(offset < dma_len);
+ texture_data += offset;
+
+ hwaddr palette_dma_len;
+ uint8_t *palette_data;
+ if (palette_dma_select) {
+ palette_data = (uint8_t*)nv_dma_map(d, pg->dma_b, &palette_dma_len);
+ } else {
+ palette_data = (uint8_t*)nv_dma_map(d, pg->dma_a, &palette_dma_len);
+ }
+ assert(palette_offset < palette_dma_len);
+ palette_data += palette_offset;
+
+ NV2A_DPRINTF(" - 0x%tx\n", texture_data - d->vram_ptr);
+
+ size_t length = 0;
+ if (f.linear) {
+ assert(cubemap == false);
+ assert(dimensionality == 2);
+ length = height * pitch;
+ } else {
+ if (dimensionality >= 2) {
+ unsigned int w = width, h = height;
+ int level;
+ if (f.gl_format != 0) {
+ for (level = 0; level < levels; level++) {
+ w = MAX(w, 1); h = MAX(h, 1);
+ length += w * h * f.bytes_per_pixel;
+ w /= 2;
+ h /= 2;
+ }
+ } else {
+ /* Compressed textures are a bit different */
+ unsigned int block_size;
+ if (f.gl_internal_format ==
+ GL_COMPRESSED_RGBA_S3TC_DXT1_EXT) {
+ block_size = 8;
+ } else {
+ block_size = 16;
+ }
+
+ for (level = 0; level < levels; level++) {
+ w = MAX(w, 4); h = MAX(h, 4);
+ length += w/4 * h/4 * block_size;
+ w /= 2; h /= 2;
+ }
+ }
+ if (cubemap) {
+ assert(dimensionality == 2);
+ length *= 6;
+ }
+ if (dimensionality >= 3) {
+ length *= depth;
+ }
+ }
+ }
+
+ TextureShape state = {
+ .cubemap = cubemap,
+ .dimensionality = dimensionality,
+ .color_format = color_format,
+ .levels = levels,
+ .width = width,
+ .height = height,
+ .depth = depth,
+ .min_mipmap_level = min_mipmap_level,
+ .max_mipmap_level = max_mipmap_level,
+ .pitch = pitch,
+ };
+
+#ifdef USE_TEXTURE_CACHE
+ TextureKey key = {
+ .state = state,
+ .data_hash = fast_hash(texture_data, length, 5003)
+ ^ fnv_hash(palette_data, palette_length),
+ .texture_data = texture_data,
+ .palette_data = palette_data,
+ };
+
+ gpointer cache_key = g_malloc(sizeof(TextureKey));
+ memcpy(cache_key, &key, sizeof(TextureKey));
+
+ GError *err;
+ TextureBinding *binding = (TextureBinding *)g_lru_cache_get(pg->texture_cache, cache_key, &err);
+ assert(binding);
+ binding->refcnt++;
+#else
+ TextureBinding *binding = generate_texture(state,
+ texture_data, palette_data);
+#endif
+
+ glBindTexture(binding->gl_target, binding->gl_texture);
+
+
+ if (f.linear) {
+ /* somtimes games try to set mipmap min filters on linear textures.
+ * this could indicate a bug... */
+ switch (min_filter) {
+ case NV_PGRAPH_TEXFILTER0_MIN_BOX_NEARESTLOD:
+ case NV_PGRAPH_TEXFILTER0_MIN_BOX_TENT_LOD:
+ min_filter = NV_PGRAPH_TEXFILTER0_MIN_BOX_LOD0;
+ break;
+ case NV_PGRAPH_TEXFILTER0_MIN_TENT_NEARESTLOD:
+ case NV_PGRAPH_TEXFILTER0_MIN_TENT_TENT_LOD:
+ min_filter = NV_PGRAPH_TEXFILTER0_MIN_TENT_LOD0;
+ break;
+ }
+ }
+
+ glTexParameteri(binding->gl_target, GL_TEXTURE_MIN_FILTER,
+ pgraph_texture_min_filter_map[min_filter]);
+ glTexParameteri(binding->gl_target, GL_TEXTURE_MAG_FILTER,
+ pgraph_texture_mag_filter_map[mag_filter]);
+
+ /* Texture wrapping */
+ assert(addru < ARRAY_SIZE(pgraph_texture_addr_map));
+ glTexParameteri(binding->gl_target, GL_TEXTURE_WRAP_S,
+ pgraph_texture_addr_map[addru]);
+ if (dimensionality > 1) {
+ assert(addrv < ARRAY_SIZE(pgraph_texture_addr_map));
+ glTexParameteri(binding->gl_target, GL_TEXTURE_WRAP_T,
+ pgraph_texture_addr_map[addrv]);
+ }
+ if (dimensionality > 2) {
+ assert(addrp < ARRAY_SIZE(pgraph_texture_addr_map));
+ glTexParameteri(binding->gl_target, GL_TEXTURE_WRAP_R,
+ pgraph_texture_addr_map[addrp]);
+ }
+
+ /* FIXME: Only upload if necessary? [s, t or r = GL_CLAMP_TO_BORDER] */
+ if (border_source == NV_PGRAPH_TEXFMT0_BORDER_SOURCE_COLOR) {
+ GLfloat gl_border_color[] = {
+ /* FIXME: Color channels might be wrong order */
+ ((border_color >> 16) & 0xFF) / 255.0f, /* red */
+ ((border_color >> 8) & 0xFF) / 255.0f, /* green */
+ (border_color & 0xFF) / 255.0f, /* blue */
+ ((border_color >> 24) & 0xFF) / 255.0f /* alpha */
+ };
+ glTexParameterfv(binding->gl_target, GL_TEXTURE_BORDER_COLOR,
+ gl_border_color);
+ }
+
+ if (pg->texture_binding[i]) {
+ texture_binding_destroy(pg->texture_binding[i]);
+ }
+ pg->texture_binding[i] = binding;
+ pg->texture_dirty[i] = false;
+ }
+ NV2A_GL_DGROUP_END();
+#endif // COMPILE_OPENGL
+}
+
+static void pgraph_apply_anti_aliasing_factor(PGRAPHState *pg,
+ unsigned int *width,
+ unsigned int *height)
+{
+ switch (pg->surface_shape.anti_aliasing) {
+ case NV097_SET_SURFACE_FORMAT_ANTI_ALIASING_CENTER_1:
+ break;
+ case NV097_SET_SURFACE_FORMAT_ANTI_ALIASING_CENTER_CORNER_2:
+ if (width) { *width *= 2; }
+ break;
+ case NV097_SET_SURFACE_FORMAT_ANTI_ALIASING_SQUARE_OFFSET_4:
+ if (width) { *width *= 2; }
+ if (height) { *height *= 2; }
+ break;
+ default:
+ assert(false);
+ break;
+ }
+}
+
+static void pgraph_get_surface_dimensions(PGRAPHState *pg,
+ unsigned int *width,
+ unsigned int *height)
+{
+ bool swizzle = (pg->surface_type == NV097_SET_SURFACE_FORMAT_TYPE_SWIZZLE);
+ if (swizzle) {
+ *width = 1 << pg->surface_shape.log_width;
+ *height = 1 << pg->surface_shape.log_height;
+ } else {
+ *width = pg->surface_shape.clip_width;
+ *height = pg->surface_shape.clip_height;
+ }
+}
+
+static void pgraph_update_memory_buffer(NV2AState *d, hwaddr addr, hwaddr size,
+ bool f)
+{
+#ifdef COMPILE_OPENGL
+ glBindBuffer(GL_ARRAY_BUFFER, d->pgraph.gl_memory_buffer);
+
+ hwaddr end = TARGET_PAGE_ALIGN(addr + size);
+ addr &= TARGET_PAGE_MASK;
+ assert(end < d->vram_size);
+ // if (f || memory_region_test_and_clear_dirty(d->vram,
+ // addr,
+ // end - addr,
+ // DIRTY_MEMORY_NV2A)) {
+ glBufferSubData(GL_ARRAY_BUFFER, addr, end - addr, d->vram_ptr + addr);
+ // }
+#endif // COMPILE_OPENGL
+}
+
+static void pgraph_bind_vertex_attributes(NV2AState *d,
+ unsigned int num_elements,
+ bool inline_data,
+ unsigned int inline_stride)
+{
+#ifdef COMPILE_OPENGL
+ int i, j;
+ PGRAPHState *pg = &d->pgraph;
+
+ if (inline_data) {
+ NV2A_GL_DGROUP_BEGIN("%s (num_elements: %d inline stride: %d)",
+ __func__, num_elements, inline_stride);
+ } else {
+ NV2A_GL_DGROUP_BEGIN("%s (num_elements: %d)", __func__, num_elements);
+ }
+
+ for (i=0; ivertex_attributes[i];
+ if (attribute->count) {
+ uint8_t *data;
+ unsigned int in_stride;
+ if (inline_data && attribute->needs_conversion) {
+ data = (uint8_t*)pg->inline_array
+ + attribute->inline_array_offset;
+ in_stride = inline_stride;
+ } else {
+ hwaddr dma_len;
+ if (attribute->dma_select) {
+ data = (uint8_t*)nv_dma_map(d, pg->dma_vertex_b, &dma_len);
+ } else {
+ data = (uint8_t*)nv_dma_map(d, pg->dma_vertex_a, &dma_len);
+ }
+
+ assert(attribute->offset < dma_len);
+ data += attribute->offset;
+
+ in_stride = attribute->stride;
+ }
+
+ if (attribute->needs_conversion) {
+ NV2A_DPRINTF("converted %d\n", i);
+
+ unsigned int out_stride = attribute->converted_size
+ * attribute->converted_count;
+
+ if (num_elements > attribute->converted_elements) {
+ attribute->converted_buffer = (uint8_t*)g_realloc(
+ attribute->converted_buffer,
+ num_elements * out_stride);
+ }
+
+ for (j=attribute->converted_elements; jconverted_buffer + j * out_stride;
+
+ switch (attribute->format) {
+ case NV097_SET_VERTEX_DATA_ARRAY_FORMAT_TYPE_CMP: {
+ uint32_t p = ldl_le_p((uint32_t*)in);
+ float *xyz = (float*)out;
+ xyz[0] = ((int32_t)(((p >> 0) & 0x7FF) << 21) >> 21)
+ / 1023.0f;
+ xyz[1] = ((int32_t)(((p >> 11) & 0x7FF) << 21) >> 21)
+ / 1023.0f;
+ xyz[2] = ((int32_t)(((p >> 22) & 0x3FF) << 22) >> 22)
+ / 511.0f;
+ break;
+ }
+ default:
+ assert(false);
+ break;
+ }
+ }
+
+
+ glBindBuffer(GL_ARRAY_BUFFER, attribute->gl_converted_buffer);
+ if (num_elements != attribute->converted_elements) {
+ glBufferData(GL_ARRAY_BUFFER,
+ num_elements * out_stride,
+ attribute->converted_buffer,
+ GL_DYNAMIC_DRAW);
+ attribute->converted_elements = num_elements;
+ }
+
+
+ glVertexAttribPointer(i,
+ attribute->converted_count,
+ attribute->gl_type,
+ attribute->gl_normalize,
+ out_stride,
+ 0);
+ } else if (inline_data) {
+ glBindBuffer(GL_ARRAY_BUFFER, pg->gl_inline_array_buffer);
+ glVertexAttribPointer(i,
+ attribute->gl_count,
+ attribute->gl_type,
+ attribute->gl_normalize,
+ inline_stride,
+ (void*)(uintptr_t)attribute->inline_array_offset);
+ } else {
+ hwaddr addr = data - d->vram_ptr;
+ pgraph_update_memory_buffer(d, addr,
+ num_elements * attribute->stride,
+ false);
+ glVertexAttribPointer(i,
+ attribute->gl_count,
+ attribute->gl_type,
+ attribute->gl_normalize,
+ attribute->stride,
+ (void*)(uint64_t)addr);
+ }
+ glEnableVertexAttribArray(i);
+ } else {
+ glDisableVertexAttribArray(i);
+
+ glVertexAttrib4fv(i, attribute->inline_value);
+ }
+ }
+ NV2A_GL_DGROUP_END();
+#endif // COMPILE_OPENGL
+}
+
+static unsigned int pgraph_bind_inline_array(NV2AState *d)
+{
+#ifdef COMPILE_OPENGL
+ int i;
+
+ PGRAPHState *pg = &d->pgraph;
+
+ unsigned int offset = 0;
+ for (i=0; ivertex_attributes[i];
+ if (attribute->count) {
+ attribute->inline_array_offset = offset;
+
+ NV2A_DPRINTF("bind inline attribute %d size=%d, count=%d\n",
+ i, attribute->size, attribute->count);
+ offset += attribute->size * attribute->count;
+ assert(offset % 4 == 0);
+ }
+ }
+
+ unsigned int vertex_size = offset;
+
+
+ unsigned int index_count = pg->inline_array_length*4 / vertex_size;
+
+ NV2A_DPRINTF("draw inline array %d, %d\n", vertex_size, index_count);
+
+ glBindBuffer(GL_ARRAY_BUFFER, pg->gl_inline_array_buffer);
+ glBufferData(GL_ARRAY_BUFFER, pg->inline_array_length*4, pg->inline_array,
+ GL_DYNAMIC_DRAW);
+
+ pgraph_bind_vertex_attributes(d, index_count, true, vertex_size);
+
+ return index_count;
+#endif // COMPILE_OPENGL
+}
+
+static void load_graphics_object(NV2AState *d, hwaddr instance_address,
+ GraphicsObject *obj)
+{
+ uint8_t *obj_ptr;
+ uint32_t switch1, switch2, switch3;
+
+ assert(instance_address < d->pramin.ramin_size);
+
+ obj_ptr = d->pramin.ramin_ptr + instance_address;
+
+ switch1 = ldl_le_p((uint32_t*)obj_ptr);
+ switch2 = ldl_le_p((uint32_t*)(obj_ptr + 4));
+ switch3 = ldl_le_p((uint32_t*)(obj_ptr + 8));
+
+ obj->graphics_class = switch1 & NV_PGRAPH_CTX_SWITCH1_GRCLASS;
+
+ /* init graphics object */
+ switch (obj->graphics_class) {
+ case NV_KELVIN_PRIMITIVE:
+ // kelvin->vertex_attributes[NV2A_VERTEX_ATTR_DIFFUSE].inline_value = 0xFFFFFFF;
+ break;
+ default:
+ break;
+ }
+}
+
+static GraphicsObject* lookup_graphics_object(PGRAPHState *s,
+ hwaddr instance_address)
+{
+ int i;
+ for (i = 0; isubchannel_data[i].object_instance == instance_address) {
+ return &s->subchannel_data[i].object;
+ }
+ }
+ return NULL;
+}
+
+/* 16 bit to [0.0, F16_MAX = 511.9375] */
+static float convert_f16_to_float(uint16_t f16) {
+ if (f16 == 0x0000) { return 0.0f; }
+ uint32_t i = (f16 << 11) + 0x3C000000;
+ return *(float*)&i;
+}
+
+/* 24 bit to [0.0, F24_MAX] */
+static float convert_f24_to_float(uint32_t f24) {
+ assert(!(f24 >> 24));
+ f24 &= 0xFFFFFF;
+ if (f24 == 0x000000) { return 0.0f; }
+ uint32_t i = f24 << 7;
+ return *(float*)&i;
+}
+
+static uint8_t cliptobyte(int x)
+{
+ return (uint8_t)((x < 0) ? 0 : ((x > 255) ? 255 : x));
+}
+
+static void convert_yuy2_to_rgb(const uint8_t *line, unsigned int ix,
+ uint8_t *r, uint8_t *g, uint8_t* b) {
+ int c, d, e;
+ c = (int)line[ix * 2] - 16;
+ if (ix % 2) {
+ d = (int)line[ix * 2 - 1] - 128;
+ e = (int)line[ix * 2 + 1] - 128;
+ } else {
+ d = (int)line[ix * 2 + 1] - 128;
+ e = (int)line[ix * 2 + 3] - 128;
+ }
+ *r = cliptobyte((298 * c + 409 * e + 128) >> 8);
+ *g = cliptobyte((298 * c - 100 * d - 208 * e + 128) >> 8);
+ *b = cliptobyte((298 * c + 516 * d + 128) >> 8);
+}
+
+static uint8_t* convert_texture_data(const TextureShape s,
+ const uint8_t *data,
+ const uint8_t *palette_data,
+ unsigned int width,
+ unsigned int height,
+ unsigned int depth,
+ unsigned int row_pitch,
+ unsigned int slice_pitch)
+{
+ if (s.color_format == NV097_SET_TEXTURE_FORMAT_COLOR_SZ_I8_A8R8G8B8) {
+ assert(depth == 1); /* FIXME */
+ uint8_t* converted_data = (uint8_t*)g_malloc(width * height * 4);
+ unsigned int x, y;
+ for (y = 0; y < height; y++) {
+ for (x = 0; x < width; x++) {
+ uint8_t index = data[y * row_pitch + x];
+ uint32_t color = *(uint32_t*)(palette_data + index * 4);
+ *(uint32_t*)(converted_data + y * width * 4 + x * 4) = color;
+ }
+ }
+ return converted_data;
+ } else if (s.color_format
+ == NV097_SET_TEXTURE_FORMAT_COLOR_LC_IMAGE_CR8YB8CB8YA8) {
+ assert(depth == 1); /* FIXME */
+ uint8_t* converted_data = (uint8_t*)g_malloc(width * height * 4);
+ unsigned int x, y;
+ for (y = 0; y < height; y++) {
+ const uint8_t* line = &data[y * s.width * 2];
+ for (x = 0; x < width; x++) {
+ uint8_t* pixel = &converted_data[(y * s.width + x) * 4];
+ /* FIXME: Actually needs uyvy? */
+ convert_yuy2_to_rgb(line, x, &pixel[0], &pixel[1], &pixel[2]);
+ pixel[3] = 255;
+ }
+ }
+ return converted_data;
+ } else if (s.color_format
+ == NV097_SET_TEXTURE_FORMAT_COLOR_SZ_R6G5B5) {
+ assert(depth == 1); /* FIXME */
+ uint8_t *converted_data = (uint8_t*)g_malloc(width * height * 3);
+ unsigned int x, y;
+ for (y = 0; y < height; y++) {
+ for (x = 0; x < width; x++) {
+ uint16_t rgb655 = *(uint16_t*)(data + y * row_pitch + x * 2);
+ int8_t *pixel = (int8_t*)&converted_data[(y * width + x) * 3];
+ /* Maps 5 bit G and B signed value range to 8 bit
+ * signed values. R is probably unsigned.
+ */
+ rgb655 ^= (1 << 9) | (1 << 4);
+ pixel[0] = ((rgb655 & 0xFC00) >> 10) * 0x7F / 0x3F;
+ pixel[1] = ((rgb655 & 0x03E0) >> 5) * 0xFF / 0x1F - 0x80;
+ pixel[2] = (rgb655 & 0x001F) * 0xFF / 0x1F - 0x80;
+ }
+ }
+ return converted_data;
+ } else {
+ return NULL;
+ }
+}
+
+static void upload_gl_texture(GLenum gl_target,
+ const TextureShape s,
+ const uint8_t *texture_data,
+ const uint8_t *palette_data)
+{
+#ifdef COMPILE_OPENGL
+ ColorFormatInfo f = kelvin_color_format_map[s.color_format];
+
+ switch(gl_target) {
+ case GL_TEXTURE_1D:
+ assert(false);
+ break;
+ case GL_TEXTURE_RECTANGLE: {
+ /* Can't handle strides unaligned to pixels */
+ assert(s.pitch % f.bytes_per_pixel == 0);
+ glPixelStorei(GL_UNPACK_ROW_LENGTH,
+ s.pitch / f.bytes_per_pixel);
+
+ uint8_t *converted = convert_texture_data(s, texture_data,
+ palette_data,
+ s.width, s.height, 1,
+ s.pitch, 0);
+
+ glTexImage2D(gl_target, 0, f.gl_internal_format,
+ s.width, s.height, 0,
+ f.gl_format, f.gl_type,
+ converted ? converted : texture_data);
+
+ if (converted) {
+ g_free(converted);
+ }
+
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
+ break;
+ }
+ case GL_TEXTURE_2D:
+ case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
+ case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
+ case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
+ case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
+ case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
+ case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z: {
+
+ unsigned int width = s.width, height = s.height;
+
+ int level;
+ for (level = 0; level < s.levels; level++) {
+ if (f.gl_format == 0) { /* compressed */
+
+ width = MAX(width, 4); height = MAX(height, 4);
+
+ unsigned int block_size;
+ if (f.gl_internal_format == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT) {
+ block_size = 8;
+ } else {
+ block_size = 16;
+ }
+
+ glCompressedTexImage2D(gl_target, level, f.gl_internal_format,
+ width, height, 0,
+ width/4 * height/4 * block_size,
+ texture_data);
+
+ texture_data += width/4 * height/4 * block_size;
+ } else {
+
+ width = MAX(width, 1); height = MAX(height, 1);
+
+ unsigned int pitch = width * f.bytes_per_pixel;
+ uint8_t *unswizzled = (uint8_t*)g_malloc(height * pitch);
+ unswizzle_rect(texture_data, width, height,
+ unswizzled, pitch, f.bytes_per_pixel);
+
+ uint8_t *converted = convert_texture_data(s, unswizzled,
+ palette_data,
+ width, height, 1,
+ pitch, 0);
+
+ glTexImage2D(gl_target, level, f.gl_internal_format,
+ width, height, 0,
+ f.gl_format, f.gl_type,
+ converted ? converted : unswizzled);
+
+ if (converted) {
+ g_free(converted);
+ }
+ g_free(unswizzled);
+
+ texture_data += width * height * f.bytes_per_pixel;
+ }
+
+ width /= 2;
+ height /= 2;
+ }
+
+ break;
+ }
+ case GL_TEXTURE_3D: {
+
+ unsigned int width = s.width, height = s.height, depth = s.depth;
+
+ assert(f.gl_format != 0); /* FIXME: compressed not supported yet */
+ assert(f.linear == false);
+
+ int level;
+ for (level = 0; level < s.levels; level++) {
+
+ unsigned int row_pitch = width * f.bytes_per_pixel;
+ unsigned int slice_pitch = row_pitch * height;
+ uint8_t *unswizzled = (uint8_t*)g_malloc(slice_pitch * depth);
+ unswizzle_box(texture_data, width, height, depth, unswizzled,
+ row_pitch, slice_pitch, f.bytes_per_pixel);
+
+ uint8_t *converted = convert_texture_data(s, unswizzled,
+ palette_data,
+ width, height, depth,
+ row_pitch, slice_pitch);
+
+ glTexImage3D(gl_target, level, f.gl_internal_format,
+ width, height, depth, 0,
+ f.gl_format, f.gl_type,
+ converted ? converted : unswizzled);
+
+ if (converted) {
+ g_free(converted);
+ }
+ g_free(unswizzled);
+
+ texture_data += width * height * depth * f.bytes_per_pixel;
+
+ width /= 2;
+ height /= 2;
+ depth /= 2;
+ }
+ break;
+ }
+ default:
+ assert(false);
+ break;
+ }
+#endif // COMPILE_OPENGL
+}
+
+static TextureBinding* generate_texture(const TextureShape s,
+ const uint8_t *texture_data,
+ const uint8_t *palette_data)
+{
+#ifdef COMPILE_OPENGL
+ ColorFormatInfo f = kelvin_color_format_map[s.color_format];
+
+ /* Create a new opengl texture */
+ GLuint gl_texture;
+ glGenTextures(1, &gl_texture);
+
+ GLenum gl_target;
+ if (s.cubemap) {
+ assert(f.linear == false);
+ assert(s.dimensionality == 2);
+ gl_target = GL_TEXTURE_CUBE_MAP;
+ } else {
+ if (f.linear) {
+ /* linear textures use unnormalised texcoords.
+ * GL_TEXTURE_RECTANGLE_ARB conveniently also does, but
+ * does not allow repeat and mirror wrap modes.
+ * (or mipmapping, but xbox d3d says 'Non swizzled and non
+ * compressed textures cannot be mip mapped.')
+ * Not sure if that'll be an issue. */
+
+ /* FIXME: GLSL 330 provides us with textureSize()! Use that? */
+ gl_target = GL_TEXTURE_RECTANGLE;
+ assert(s.dimensionality == 2);
+ } else {
+ switch(s.dimensionality) {
+ case 1: gl_target = GL_TEXTURE_1D; break;
+ case 2: gl_target = GL_TEXTURE_2D; break;
+ case 3: gl_target = GL_TEXTURE_3D; break;
+ default:
+ assert(false);
+ break;
+ }
+ }
+ }
+
+ glBindTexture(gl_target, gl_texture);
+
+ NV2A_GL_DLABEL(GL_TEXTURE, gl_texture,
+ "format: 0x%02X%s, %d dimensions%s, width: %d, height: %d, depth: %d",
+ s.color_format, f.linear ? "" : " (SZ)",
+ s.dimensionality, s.cubemap ? " (Cubemap)" : "",
+ s.width, s.height, s.depth);
+
+ if (gl_target == GL_TEXTURE_CUBE_MAP) {
+
+ size_t length = 0;
+ unsigned int w = s.width, h = s.height;
+ int level;
+ for (level = 0; level < s.levels; level++) {
+ /* FIXME: This is wrong for compressed textures and textures with 1x? non-square mipmaps */
+ length += w * h * f.bytes_per_pixel;
+ w /= 2;
+ h /= 2;
+ }
+
+ upload_gl_texture(GL_TEXTURE_CUBE_MAP_POSITIVE_X,
+ s, texture_data + 0 * length, palette_data);
+ upload_gl_texture(GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
+ s, texture_data + 1 * length, palette_data);
+ upload_gl_texture(GL_TEXTURE_CUBE_MAP_POSITIVE_Y,
+ s, texture_data + 2 * length, palette_data);
+ upload_gl_texture(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
+ s, texture_data + 3 * length, palette_data);
+ upload_gl_texture(GL_TEXTURE_CUBE_MAP_POSITIVE_Z,
+ s, texture_data + 4 * length, palette_data);
+ upload_gl_texture(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z,
+ s, texture_data + 5 * length, palette_data);
+ } else {
+ upload_gl_texture(gl_target, s, texture_data, palette_data);
+ }
+
+ /* Linear textures don't support mipmapping */
+ if (!f.linear) {
+ glTexParameteri(gl_target, GL_TEXTURE_BASE_LEVEL,
+ s.min_mipmap_level);
+ glTexParameteri(gl_target, GL_TEXTURE_MAX_LEVEL,
+ s.levels - 1);
+ }
+
+ if (f.gl_swizzle_mask[0] != 0 || f.gl_swizzle_mask[1] != 0
+ || f.gl_swizzle_mask[2] != 0 || f.gl_swizzle_mask[3] != 0) {
+ glTexParameteriv(gl_target, GL_TEXTURE_SWIZZLE_RGBA,
+ (const GLint *)f.gl_swizzle_mask);
+ }
+
+ TextureBinding* ret = (TextureBinding *)g_malloc(sizeof(TextureBinding));
+ ret->gl_target = gl_target;
+ ret->gl_texture = gl_texture;
+ ret->refcnt = 1;
+ return ret;
+#endif // COMPILE_OPENGL
+}
+
+#ifdef COMPILE_OPENGL
+/* functions for texture LRU cache */
+static guint texture_key_hash(gconstpointer key)
+{
+ const TextureKey *k = (const TextureKey *)key;
+ uint64_t state_hash = fnv_hash(
+ (const uint8_t*)&k->state, sizeof(TextureShape));
+ return state_hash ^ k->data_hash;
+}
+static gboolean texture_key_equal(gconstpointer a, gconstpointer b)
+{
+ const TextureKey *ak = (const TextureKey *)a, *bk = (const TextureKey *)b;
+ return memcmp(&ak->state, &bk->state, sizeof(TextureShape)) == 0
+ && ak->data_hash == bk->data_hash;
+}
+static gpointer texture_key_retrieve(gpointer key, gpointer user_data, GError **error)
+{
+ const TextureKey *k = (const TextureKey *)key;
+ TextureBinding *v = generate_texture(k->state,
+ k->texture_data,
+ k->palette_data);
+ if (error != NULL) {
+ *error = NULL;
+ }
+ return v;
+}
+static void texture_key_destroy(gpointer data)
+{
+ g_free(data);
+}
+static void texture_binding_destroy(gpointer data)
+{
+ TextureBinding *binding = (TextureBinding *)data;
+ assert(binding->refcnt > 0);
+ binding->refcnt--;
+ if (binding->refcnt == 0) {
+ glDeleteTextures(1, &binding->gl_texture);
+ g_free(binding);
+ }
+}
+
+/* hash and equality for shader cache hash table */
+static guint shader_hash(gconstpointer key)
+{
+ return fnv_hash((const uint8_t *)key, sizeof(ShaderState));
+}
+static gboolean shader_equal(gconstpointer a, gconstpointer b)
+{
+ const ShaderState *as = (const ShaderState *)a, *bs = (const ShaderState *)b;
+ return memcmp(as, bs, sizeof(ShaderState)) == 0;
+}
+#endif // COMPILE_OPENGL
+
+static unsigned int kelvin_map_stencil_op(uint32_t parameter)
+{
+ unsigned int op;
+ switch (parameter) {
+ case NV097_SET_STENCIL_OP_V_KEEP:
+ op = NV_PGRAPH_CONTROL_2_STENCIL_OP_V_KEEP; break;
+ case NV097_SET_STENCIL_OP_V_ZERO:
+ op = NV_PGRAPH_CONTROL_2_STENCIL_OP_V_ZERO; break;
+ case NV097_SET_STENCIL_OP_V_REPLACE:
+ op = NV_PGRAPH_CONTROL_2_STENCIL_OP_V_REPLACE; break;
+ case NV097_SET_STENCIL_OP_V_INCRSAT:
+ op = NV_PGRAPH_CONTROL_2_STENCIL_OP_V_INCRSAT; break;
+ case NV097_SET_STENCIL_OP_V_DECRSAT:
+ op = NV_PGRAPH_CONTROL_2_STENCIL_OP_V_DECRSAT; break;
+ case NV097_SET_STENCIL_OP_V_INVERT:
+ op = NV_PGRAPH_CONTROL_2_STENCIL_OP_V_INVERT; break;
+ case NV097_SET_STENCIL_OP_V_INCR:
+ op = NV_PGRAPH_CONTROL_2_STENCIL_OP_V_INCR; break;
+ case NV097_SET_STENCIL_OP_V_DECR:
+ op = NV_PGRAPH_CONTROL_2_STENCIL_OP_V_DECR; break;
+ default:
+ assert(false);
+ break;
+ }
+ return op;
+}
+
+static unsigned int kelvin_map_polygon_mode(uint32_t parameter)
+{
+ unsigned int mode;
+ switch (parameter) {
+ case NV097_SET_FRONT_POLYGON_MODE_V_POINT:
+ mode = NV_PGRAPH_SETUPRASTER_FRONTFACEMODE_POINT; break;
+ case NV097_SET_FRONT_POLYGON_MODE_V_LINE:
+ mode = NV_PGRAPH_SETUPRASTER_FRONTFACEMODE_LINE; break;
+ case NV097_SET_FRONT_POLYGON_MODE_V_FILL:
+ mode = NV_PGRAPH_SETUPRASTER_FRONTFACEMODE_FILL; break;
+ default:
+ assert(false);
+ break;
+ }
+ return mode;
+}
+
+static unsigned int kelvin_map_texgen(uint32_t parameter, unsigned int channel)
+{
+ assert(channel < 4);
+ unsigned int texgen;
+ switch (parameter) {
+ case NV097_SET_TEXGEN_S_DISABLE:
+ texgen = NV_PGRAPH_CSV1_A_T0_S_DISABLE; break;
+ case NV097_SET_TEXGEN_S_EYE_LINEAR:
+ texgen = NV_PGRAPH_CSV1_A_T0_S_EYE_LINEAR; break;
+ case NV097_SET_TEXGEN_S_OBJECT_LINEAR:
+ texgen = NV_PGRAPH_CSV1_A_T0_S_OBJECT_LINEAR; break;
+ case NV097_SET_TEXGEN_S_SPHERE_MAP:
+ assert(channel < 2);
+ texgen = NV_PGRAPH_CSV1_A_T0_S_SPHERE_MAP; break;
+ case NV097_SET_TEXGEN_S_REFLECTION_MAP:
+ assert(channel < 3);
+ texgen = NV_PGRAPH_CSV1_A_T0_S_REFLECTION_MAP; break;
+ case NV097_SET_TEXGEN_S_NORMAL_MAP:
+ assert(channel < 3);
+ texgen = NV_PGRAPH_CSV1_A_T0_S_NORMAL_MAP; break;
+ default:
+ assert(false);
+ break;
+ }
+ return texgen;
+}
+
+static uint64_t fnv_hash(const uint8_t *data, size_t len)
+{
+ /* 64 bit Fowler/Noll/Vo FNV-1a hash code */
+ uint64_t hval = 0xcbf29ce484222325ULL;
+ const uint8_t *dp = data;
+ const uint8_t *de = data + len;
+ while (dp < de) {
+ hval ^= (uint64_t) *dp++;
+ hval += (hval << 1) + (hval << 4) + (hval << 5) +
+ (hval << 7) + (hval << 8) + (hval << 40);
+ }
+
+ return hval;
+}
+
+static uint64_t fast_hash(const uint8_t *data, size_t len, unsigned int samples)
+{
+#ifdef __SSE4_2__
+ uint64_t h[4] = {len, 0, 0, 0};
+ assert(samples > 0);
+
+ if (len < 8 || len % 8) {
+ return fnv_hash(data, len);
+ }
+
+ assert(len >= 8 && len % 8 == 0);
+ const uint64_t *dp = (const uint64_t*)data;
+ const uint64_t *de = dp + (len / 8);
+ size_t step = len / 8 / samples;
+ if (step == 0) step = 1;
+
+ while (dp < de - step * 3) {
+ h[0] = __builtin_ia32_crc32di(h[0], dp[step * 0]);
+ h[1] = __builtin_ia32_crc32di(h[1], dp[step * 1]);
+ h[2] = __builtin_ia32_crc32di(h[2], dp[step * 2]);
+ h[3] = __builtin_ia32_crc32di(h[3], dp[step * 3]);
+ dp += step * 4;
+ }
+ if (dp < de - step * 0)
+ h[0] = __builtin_ia32_crc32di(h[0], dp[step * 0]);
+ if (dp < de - step * 1)
+ h[1] = __builtin_ia32_crc32di(h[1], dp[step * 1]);
+ if (dp < de - step * 2)
+ h[2] = __builtin_ia32_crc32di(h[2], dp[step * 2]);
+
+ return h[0] + (h[1] << 10) + (h[2] << 21) + (h[3] << 32);
+#else
+ return fnv_hash(data, len);
+#endif
+}
diff --git a/src/devices/video/EmuNV2A_PMC.cpp b/src/devices/video/EmuNV2A_PMC.cpp
new file mode 100644
index 000000000..4b8719b2f
--- /dev/null
+++ b/src/devices/video/EmuNV2A_PMC.cpp
@@ -0,0 +1,47 @@
+/* PMC - card master control */
+DEVICE_READ32(PMC)
+{
+ DEVICE_READ32_SWITCH() {
+ case NV_PMC_BOOT_0: // chipset and stepping: NV2A, A02, Rev 0
+ result = 0x02A000A2;
+ break;
+ case NV_PMC_BOOT_1: // Selects big/little endian mode for the card
+ result = 0; // When read, returns 0 if in little-endian mode, 0x01000001 if in big-endian mode.
+ break;
+ case NV_PMC_INTR_0: // Shows which functional units have pending IRQ
+ result = d->pmc.pending_interrupts;
+ break;
+ case NV_PMC_INTR_EN_0: // Selects which functional units can cause IRQs
+ result = d->pmc.enabled_interrupts;
+ break;
+ default:
+ result = 0;
+ //DEVICE_READ32_REG(PMC); // Was : DEBUG_READ32_UNHANDLED(PMC);
+ break;
+ }
+
+ DEVICE_READ32_END(PMC);
+}
+
+DEVICE_WRITE32(PMC)
+{
+ switch(addr) {
+ case NV_PMC_INTR_0:
+ /* the bits of the interrupts to clear are wrtten */
+ d->pmc.pending_interrupts &= ~value;
+ update_irq(d);
+ break;
+ case NV_PMC_INTR_EN_0:
+ d->pmc.enabled_interrupts = value;
+ update_irq(d);
+ break;
+
+ default:
+ //DEVICE_WRITE32_REG(pmc); // Was : DEBUG_WRITE32_UNHANDLED(PMC);
+ break;
+ }
+
+ DEVICE_WRITE32_END(PMC);
+}
+
+
diff --git a/src/devices/video/EmuNV2A_PRAMDAC.cpp b/src/devices/video/EmuNV2A_PRAMDAC.cpp
new file mode 100644
index 000000000..2cd190d6c
--- /dev/null
+++ b/src/devices/video/EmuNV2A_PRAMDAC.cpp
@@ -0,0 +1,66 @@
+DEVICE_READ32(PRAMDAC)
+{
+ DEVICE_READ32_SWITCH() {
+
+ case NV_PRAMDAC_NVPLL_COEFF:
+ result = d->pramdac.core_clock_coeff;
+ break;
+ case NV_PRAMDAC_MPLL_COEFF:
+ result = d->pramdac.memory_clock_coeff;
+ break;
+ case NV_PRAMDAC_VPLL_COEFF:
+ result = d->pramdac.video_clock_coeff;
+ break;
+ case NV_PRAMDAC_PLL_TEST_COUNTER:
+ /* emulated PLLs locked instantly? */
+ result = NV_PRAMDAC_PLL_TEST_COUNTER_VPLL2_LOCK
+ | NV_PRAMDAC_PLL_TEST_COUNTER_NVPLL_LOCK
+ | NV_PRAMDAC_PLL_TEST_COUNTER_MPLL_LOCK
+ | NV_PRAMDAC_PLL_TEST_COUNTER_VPLL_LOCK;
+ break;
+
+ default:
+ //DEVICE_READ32_REG(pramdac); // Was : DEBUG_READ32_UNHANDLED(PRAMDAC);
+ break;
+ }
+
+ /* Surprisingly, QEMU doesn't handle unaligned access for you properly */
+ // result >>= 32 - 8 * size - 8 * (addr & 3);
+
+ DEVICE_READ32_END(PRAMDAC);
+}
+
+DEVICE_WRITE32(PRAMDAC)
+{
+ switch (addr) {
+
+ uint32_t m, n, p;
+ case NV_PRAMDAC_NVPLL_COEFF:
+ d->pramdac.core_clock_coeff = value;
+
+ m = value & NV_PRAMDAC_NVPLL_COEFF_MDIV;
+ n = (value & NV_PRAMDAC_NVPLL_COEFF_NDIV) >> 8;
+ p = (value & NV_PRAMDAC_NVPLL_COEFF_PDIV) >> 16;
+
+ if (m == 0) {
+ d->pramdac.core_clock_freq = 0;
+ } else {
+ d->pramdac.core_clock_freq = (NV2A_CRYSTAL_FREQ * n)
+ / (1 << p) / m;
+ }
+
+ break;
+ case NV_PRAMDAC_MPLL_COEFF:
+ d->pramdac.memory_clock_coeff = value;
+ break;
+ case NV_PRAMDAC_VPLL_COEFF:
+ d->pramdac.video_clock_coeff = value;
+ break;
+
+ default:
+ //DEVICE_WRITE32_REG(pramdac); // Was : DEBUG_WRITE32_UNHANDLED(PRAMDAC);
+ break;
+ }
+
+ DEVICE_WRITE32_END(PRAMDAC);
+}
diff --git a/src/devices/video/EmuNV2A_PRAMIN.cpp b/src/devices/video/EmuNV2A_PRAMIN.cpp
new file mode 100644
index 000000000..533be20ec
--- /dev/null
+++ b/src/devices/video/EmuNV2A_PRAMIN.cpp
@@ -0,0 +1,13 @@
+DEVICE_READ32(PRAMIN)
+{
+ uint32_t result = *((uint32_t*)(d->pramin.ramin_ptr + addr));
+
+ DEVICE_READ32_END(PRAMIN);
+}
+
+DEVICE_WRITE32(PRAMIN)
+{
+ *((uint32_t*)(d->pramin.ramin_ptr + addr)) = value;
+
+ DEVICE_WRITE32_END(PRAMIN);
+}
diff --git a/src/devices/video/EmuNV2A_PRMA.cpp b/src/devices/video/EmuNV2A_PRMA.cpp
new file mode 100644
index 000000000..0c75aca3e
--- /dev/null
+++ b/src/devices/video/EmuNV2A_PRMA.cpp
@@ -0,0 +1,21 @@
+DEVICE_READ32(PRMA)
+{
+ DEVICE_READ32_SWITCH() {
+ default:
+ DEBUG_READ32_UNHANDLED(PRMA); // TODO : DEVICE_READ32_REG(prma);
+ break;
+ }
+
+ DEVICE_READ32_END(PRMA);
+}
+
+DEVICE_WRITE32(PRMA)
+{
+ switch(addr) {
+ default:
+ DEBUG_WRITE32_UNHANDLED(PRMA); // TODO : DEVICE_WRITE32_REG(prma);
+ break;
+ }
+
+ DEVICE_WRITE32_END(PRMA);
+}
diff --git a/src/devices/video/EmuNV2A_PRMCIO.cpp b/src/devices/video/EmuNV2A_PRMCIO.cpp
new file mode 100644
index 000000000..6283cf19b
--- /dev/null
+++ b/src/devices/video/EmuNV2A_PRMCIO.cpp
@@ -0,0 +1,84 @@
+DEVICE_READ32(PRMCIO)
+{
+ // vga_ioport_read :
+ DEVICE_READ32_SWITCH() {
+ case VGA_CRT_IM:
+ case VGA_CRT_IC:
+ result = d->prmcio.cr_index;
+ break;
+ case VGA_CRT_DM:
+ case VGA_CRT_DC:
+ result = d->prmcio.cr[d->prmcio.cr_index];
+
+ printf("vga: read CR%x = 0x%02x\n", d->prmcio.cr_index, result);
+ break;
+ default:
+ DEBUG_READ32_UNHANDLED(PRMCIO);
+ printf("vga: UNHANDLED ADDR %s\n", addr);
+ break;
+ }
+
+ DEVICE_READ32_END(PRMCIO);
+}
+
+DEVICE_WRITE32(PRMCIO)
+{
+ switch (addr) {
+#if 0 // TODO : Enable
+ case VGA_ATT_W:
+ /* Cromwell sets attrs without enabling VGA_AR_ENABLE_DISPLAY
+ * (which should result in a blank screen).
+ * Either nvidia's hardware is lenient or it is set through
+ * something else. The former seems more likely.
+ */
+ if (d->vga.ar_flip_flop == 0) {
+ value |= VGA_AR_ENABLE_DISPLAY;
+ }
+ break;
+#endif
+ // vga_ioport_write :
+ case VGA_CRT_IM:
+ case VGA_CRT_IC:
+ d->prmcio.cr_index = value;
+ break;
+ case VGA_CRT_DM:
+ case VGA_CRT_DC:
+ printf("vga: write CR%x = 0x%02x\n", d->prmcio.cr_index, value);
+
+ /* handle CR0-7 protection */
+ if ((d->prmcio.cr[VGA_CRTC_V_SYNC_END] & VGA_CR11_LOCK_CR0_CR7) &&
+ d->prmcio.cr_index <= VGA_CRTC_OVERFLOW) {
+ /* can always write bit 4 of CR7 */
+ if (d->prmcio.cr_index == VGA_CRTC_OVERFLOW) {
+ d->prmcio.cr[VGA_CRTC_OVERFLOW] = (d->prmcio.cr[VGA_CRTC_OVERFLOW] & ~0x10) |
+ (value & 0x10);
+ EmuWarning("TODO: vbe_update_vgaregs");
+ //vbe_update_vgaregs();
+ }
+ return;
+ }
+
+ d->prmcio.cr[d->prmcio.cr_index] = value;
+ EmuWarning("TODO: vbe_update_vgaregs");
+ //vbe_update_vgaregs();
+
+ switch (d->prmcio.cr_index) {
+ case VGA_CRTC_H_TOTAL:
+ case VGA_CRTC_H_SYNC_START:
+ case VGA_CRTC_H_SYNC_END:
+ case VGA_CRTC_V_TOTAL:
+ case VGA_CRTC_OVERFLOW:
+ case VGA_CRTC_V_SYNC_END:
+ case VGA_CRTC_MODE:
+ // TODO: s->update_retrace_info(s);
+ EmuWarning("TODO: update_retrace_info");
+ break;
+ }
+ break;
+ default:
+ DEBUG_WRITE32_UNHANDLED(PRMCIO); // TODO : DEVICE_WRITE32_REG(prmcio);
+ break;
+ }
+
+ DEVICE_WRITE32_END(PRMCIO);
+}
diff --git a/src/devices/video/EmuNV2A_PRMDIO.cpp b/src/devices/video/EmuNV2A_PRMDIO.cpp
new file mode 100644
index 000000000..3ddaaedec
--- /dev/null
+++ b/src/devices/video/EmuNV2A_PRMDIO.cpp
@@ -0,0 +1,21 @@
+DEVICE_READ32(PRMDIO)
+{
+ DEVICE_READ32_SWITCH() {
+ default:
+ DEBUG_READ32_UNHANDLED(PRMDIO);
+ break;
+ }
+
+ DEVICE_READ32_END(PRMDIO);
+}
+
+DEVICE_WRITE32(PRMDIO)
+{
+ switch (addr) {
+ default:
+ DEBUG_WRITE32_UNHANDLED(PRMDIO);
+ break;
+ }
+
+ DEVICE_WRITE32_END(PRMDIO);
+}
diff --git a/src/devices/video/EmuNV2A_PRMFB.cpp b/src/devices/video/EmuNV2A_PRMFB.cpp
new file mode 100644
index 000000000..b620b5897
--- /dev/null
+++ b/src/devices/video/EmuNV2A_PRMFB.cpp
@@ -0,0 +1,22 @@
+DEVICE_READ32(PRMFB)
+{
+ DEVICE_READ32_SWITCH() {
+ default:
+ DEBUG_READ32_UNHANDLED(PRMFB); // TODO : DEVICE_READ32_REG(prmfb);
+ break;
+ }
+
+ DEVICE_READ32_END(PRMFB);
+}
+
+DEVICE_WRITE32(PRMFB)
+{
+ switch (addr) {
+ default:
+ DEBUG_WRITE32_UNHANDLED(PRMFB); // TODO : DEVICE_WRITE32_REG(prmfb);
+ break;
+ }
+
+ DEVICE_WRITE32_END(PRMFB);
+}
+
diff --git a/src/devices/video/EmuNV2A_PRMVIO.cpp b/src/devices/video/EmuNV2A_PRMVIO.cpp
new file mode 100644
index 000000000..290252cb1
--- /dev/null
+++ b/src/devices/video/EmuNV2A_PRMVIO.cpp
@@ -0,0 +1,23 @@
+DEVICE_READ32(PRMVIO)
+{
+ // vga_ioport_read
+ DEVICE_READ32_SWITCH() {
+ default:
+ DEBUG_READ32_UNHANDLED(PRMVIO); // TODO : DEVICE_READ32_REG(prmvio);
+ break;
+ }
+
+ DEVICE_READ32_END(PRMVIO);
+}
+
+DEVICE_WRITE32(PRMVIO)
+{
+ // vga_ioport_write
+ switch (addr) {
+ default:
+ DEBUG_WRITE32_UNHANDLED(PRMVIO); // TODO : DEVICE_WRITE32_REG(prmvio);
+ break;
+ }
+
+ DEVICE_WRITE32_END(PRMVIO);
+}
diff --git a/src/devices/video/EmuNV2A_PSTRAPS.cpp b/src/devices/video/EmuNV2A_PSTRAPS.cpp
new file mode 100644
index 000000000..773b6f54a
--- /dev/null
+++ b/src/devices/video/EmuNV2A_PSTRAPS.cpp
@@ -0,0 +1,23 @@
+DEVICE_READ32(PSTRAPS)
+{
+ DEVICE_READ32_SWITCH() {
+ default:
+ DEBUG_READ32_UNHANDLED(PSTRAPS);
+ break;
+ }
+
+ DEVICE_READ32_END(PSTRAPS);
+}
+
+DEVICE_WRITE32(PSTRAPS)
+{
+ switch (addr) {
+ default:
+ DEBUG_WRITE32_UNHANDLED(PSTRAPS);
+ break;
+ }
+
+ DEVICE_WRITE32_END(PSTRAPS);
+}
+
+
diff --git a/src/devices/video/EmuNV2A_PTIMER.cpp b/src/devices/video/EmuNV2A_PTIMER.cpp
new file mode 100644
index 000000000..db3b0777b
--- /dev/null
+++ b/src/devices/video/EmuNV2A_PTIMER.cpp
@@ -0,0 +1,89 @@
+static inline uint64_t muldiv64(uint64_t a, uint32_t b, uint32_t c)
+{
+ union {
+ uint64_t ll;
+ struct {
+ uint32_t low, high;
+ } l;
+ } u, res;
+ uint64_t rl, rh;
+
+ u.ll = a;
+ rl = (uint64_t)u.l.low * (uint64_t)b;
+ rh = (uint64_t)u.l.high * (uint64_t)b;
+ rh += (rl >> 32);
+ res.l.high = rh / c;
+ res.l.low = (((rh % c) << 32) + (rl & 0xffffffff)) / c;
+ return res.ll;
+}
+
+/* PTIMER - time measurement and time-based alarms */
+static uint64_t ptimer_get_clock(NV2AState * d)
+{
+ // Get time in nanoseconds
+ long int time = static_cast(std::chrono::duration_cast(std::chrono::high_resolution_clock::now().time_since_epoch()).count());
+
+ return muldiv64(time,
+ d->pramdac.core_clock_freq * d->ptimer.numerator,
+ CLOCKS_PER_SEC * d->ptimer.denominator);
+}
+
+DEVICE_READ32(PTIMER)
+{
+ DEVICE_READ32_SWITCH() {
+ case NV_PTIMER_INTR_0:
+ result = d->ptimer.pending_interrupts;
+ break;
+ case NV_PTIMER_INTR_EN_0:
+ result = d->ptimer.enabled_interrupts;
+ break;
+ case NV_PTIMER_NUMERATOR:
+ result = d->ptimer.numerator;
+ break;
+ case NV_PTIMER_DENOMINATOR:
+ result = d->ptimer.denominator;
+ break;
+ case NV_PTIMER_TIME_0:
+ result = (ptimer_get_clock(d) & 0x7ffffff) << 5;
+ break;
+ case NV_PTIMER_TIME_1:
+ result = (ptimer_get_clock(d) >> 27) & 0x1fffffff;
+ break;
+ default:
+ result = 0;
+ //DEVICE_READ32_REG(ptimer); // Was : DEBUG_READ32_UNHANDLED(PTIMER);
+ break;
+ }
+
+ DEVICE_READ32_END(PTIMER);
+}
+
+
+DEVICE_WRITE32(PTIMER)
+{
+ switch (addr) {
+
+ case NV_PTIMER_INTR_0:
+ d->ptimer.pending_interrupts &= ~value;
+ update_irq(d);
+ break;
+ case NV_PTIMER_INTR_EN_0:
+ d->ptimer.enabled_interrupts = value;
+ update_irq(d);
+ break;
+ case NV_PTIMER_DENOMINATOR:
+ d->ptimer.denominator = value;
+ break;
+ case NV_PTIMER_NUMERATOR:
+ d->ptimer.numerator = value;
+ break;
+ case NV_PTIMER_ALARM_0:
+ d->ptimer.alarm_time = value;
+ break;
+ default:
+ //DEVICE_WRITE32_REG(ptimer); // Was : DEBUG_WRITE32_UNHANDLED(PTIMER);
+ break;
+ }
+
+ DEVICE_WRITE32_END(PTIMER);
+}
diff --git a/src/devices/video/EmuNV2A_PTV.cpp b/src/devices/video/EmuNV2A_PTV.cpp
new file mode 100644
index 000000000..3c509d774
--- /dev/null
+++ b/src/devices/video/EmuNV2A_PTV.cpp
@@ -0,0 +1,21 @@
+DEVICE_READ32(PTV)
+{
+ DEVICE_READ32_SWITCH() {
+ default:
+ DEBUG_READ32_UNHANDLED(PTV); // TODO : DEVICE_READ32_REG(ptv);
+ break;
+ }
+
+ DEVICE_READ32_END(PTV);
+}
+
+DEVICE_WRITE32(PTV)
+{
+ switch (addr) {
+ default:
+ DEBUG_WRITE32_UNHANDLED(PTV); // TODO : DEVICE_WRITE32_REG(ptv);
+ break;
+ }
+
+ DEVICE_WRITE32_END(PTV);
+}
diff --git a/src/devices/video/EmuNV2A_PVIDEO.cpp b/src/devices/video/EmuNV2A_PVIDEO.cpp
new file mode 100644
index 000000000..250b77866
--- /dev/null
+++ b/src/devices/video/EmuNV2A_PVIDEO.cpp
@@ -0,0 +1,46 @@
+static void pvideo_vga_invalidate(NV2AState *d)
+{
+ int y1 = GET_MASK(d->pvideo.regs[NV_PVIDEO_POINT_OUT(0)],
+ NV_PVIDEO_POINT_OUT_Y);
+ int y2 = y1 + GET_MASK(d->pvideo.regs[NV_PVIDEO_SIZE_OUT(0)],
+ NV_PVIDEO_SIZE_OUT_HEIGHT);
+ NV2A_DPRINTF("pvideo_vga_invalidate %d %d\n", y1, y2);
+ // TODO : vga_invalidate_scanlines(&d->vga, y1, y2);
+}
+
+DEVICE_READ32(PVIDEO)
+{
+ DEVICE_READ32_SWITCH() {
+
+ case NV_PVIDEO_STOP:
+ result = 0;
+ break;
+ default:
+ DEVICE_READ32_REG(pvideo);
+ break;
+ }
+
+ DEVICE_READ32_END(PVIDEO);
+}
+
+DEVICE_WRITE32(PVIDEO)
+{
+ switch (addr) {
+ case NV_PVIDEO_BUFFER:
+ d->pvideo.regs[addr] = value;
+ // TODO : d->vga.enable_overlay = true;
+ pvideo_vga_invalidate(d);
+ break;
+ case NV_PVIDEO_STOP:
+ d->pvideo.regs[NV_PVIDEO_BUFFER] = 0;
+ // TODO : d->vga.enable_overlay = false;
+ pvideo_vga_invalidate(d);
+ break;
+ default:
+ DEVICE_WRITE32_REG(pvideo);
+ break;
+ }
+
+ DEVICE_WRITE32_END(PVIDEO);
+}
+
diff --git a/src/devices/video/EmuNV2A_PVPE.cpp b/src/devices/video/EmuNV2A_PVPE.cpp
new file mode 100644
index 000000000..8dabb82a0
--- /dev/null
+++ b/src/devices/video/EmuNV2A_PVPE.cpp
@@ -0,0 +1,23 @@
+DEVICE_READ32(PVPE)
+{
+ DEVICE_READ32_SWITCH() {
+ default:
+ DEBUG_READ32_UNHANDLED(PVPE); // TODO : DEVICE_READ32_REG(pvpe);
+ break;
+ }
+
+ DEVICE_READ32_END(PVPE);
+}
+
+
+DEVICE_WRITE32(PVPE)
+{
+ switch (addr) {
+ default:
+ DEBUG_WRITE32_UNHANDLED(PVPE); // TODO : DEVICE_WRITE32_REG(pvpe);
+ break;
+ }
+
+ DEVICE_WRITE32_END(PVPE);
+}
+
diff --git a/src/devices/video/EmuNV2A_USER.cpp b/src/devices/video/EmuNV2A_USER.cpp
new file mode 100644
index 000000000..140d66a00
--- /dev/null
+++ b/src/devices/video/EmuNV2A_USER.cpp
@@ -0,0 +1,70 @@
+/* USER - PFIFO MMIO and DMA submission area */
+DEVICE_READ32(USER)
+{
+ unsigned int channel_id = addr >> 16;
+ assert(channel_id < NV2A_NUM_CHANNELS);
+
+ ChannelControl *control = &d->user.channel_control[channel_id];
+
+ uint32_t channel_modes = d->pfifo.regs[NV_PFIFO_MODE];
+
+ /* PIO Mode */
+ if (!channel_modes & (1 << channel_id)) {
+ assert(false);
+ }
+
+ /* DMA Mode */
+ addr &= 0xFFFF;
+ DEVICE_READ32_SWITCH() {
+ case NV_USER_DMA_PUT:
+ result = control->dma_put;
+ break;
+ case NV_USER_DMA_GET:
+ result = control->dma_get;
+ break;
+ case NV_USER_REF:
+ result = control->ref;
+ break;
+ default:
+ DEBUG_READ32_UNHANDLED(USER);
+ break;
+ }
+
+ DEVICE_READ32_END(USER);
+}
+
+DEVICE_WRITE32(USER)
+{
+ unsigned int channel_id = addr >> 16;
+ assert(channel_id < NV2A_NUM_CHANNELS);
+
+ ChannelControl *control = &d->user.channel_control[channel_id];
+
+ uint32_t channel_modes = d->pfifo.regs[NV_PFIFO_MODE];
+ if (channel_modes & (1 << channel_id)) {
+ /* DMA Mode */
+ switch (addr & 0xFFFF) {
+ case NV_USER_DMA_PUT:
+ control->dma_put = value;
+
+ if (d->pfifo.cache1.push_enabled) {
+ pfifo_run_pusher(d);
+ }
+ break;
+ case NV_USER_DMA_GET:
+ control->dma_get = value;
+ break;
+ case NV_USER_REF:
+ control->ref = value;
+ break;
+ default:
+ DEBUG_WRITE32_UNHANDLED(USER);
+ break;
+ }
+ } else {
+ /* PIO Mode */
+ assert(false);
+ }
+
+ DEVICE_WRITE32_END(USER);
+}
diff --git a/src/devices/video/nv2a.cpp b/src/devices/video/nv2a.cpp
index 805e1cd8f..9fb32d9b8 100644
--- a/src/devices/video/nv2a.cpp
+++ b/src/devices/video/nv2a.cpp
@@ -28,6 +28,10 @@
// * If not, write to the Free Software Foundation, Inc.,
// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA.
// *
+// * nv2a.cpp is heavily based on code from XQEMU
+// * Copyright(c) 2012 espes
+// * Copyright(c) 2015 Jannik Vogel
+// * https://github.com/espes/xqemu/blob/xbox/hw/xbox/nv2a.c
// * (c) 2017-2018 Luke Usher
// * (c) 2018 Patrick van Logchem
// *
@@ -38,13 +42,590 @@
#define LOG_PREFIX "NV2A"
-#include "CxbxKrnl\CxbxKrnl.h" // For XBOX_MEMORY_SIZE, DWORD, etc
-#include "EmuNV2A.h" // For now, use EmuNV2A
+// prevent name collisions
+namespace xboxkrnl
+{
+#include // For PKINTERRUPT, etc.
+};
-#include "nv2a.h"
+#ifdef _MSC_VER // Check if MS Visual C compiler
+# pragma comment(lib, "opengl32.lib") // Compiler-specific directive to avoid manually configuration
+//# pragma comment(lib, "glu32.lib") // Link libraries
+# pragma comment(lib, "glew32.lib")
+#endif
+
+#include // For std::string
+#include // For uint32_t
+#include // For __beginthreadex(), etc.
+
+#include "CxbxKrnl\CxbxKrnl.h" // For XBOX_MEMORY_SIZE, DWORD, etc
+#include "CxbxKrnl\Emu.h"
+#include "CxbxKrnl\EmuFS.h"
+#include "CxbxKrnl\EmuKrnl.h"
+#include "CxbxKrnl\HLEIntercept.h"
+
+#include "vga.h"
+#include "nv2a.h" // For NV2AState
+#include "nv2a_int.h" // from https://github.com/espes/xqemu/tree/xbox/hw/xbox
+
+//#include
+#include
+#include
+#include
+//#include
+
+
+static void update_irq(NV2AState *d)
+{
+ /* PFIFO */
+ if (d->pfifo.pending_interrupts & d->pfifo.enabled_interrupts) {
+ d->pmc.pending_interrupts |= NV_PMC_INTR_0_PFIFO;
+ }
+ else {
+ d->pmc.pending_interrupts &= ~NV_PMC_INTR_0_PFIFO;
+ }
+
+ /* PCRTC */
+ if (d->pcrtc.pending_interrupts & d->pcrtc.enabled_interrupts) {
+ d->pmc.pending_interrupts |= NV_PMC_INTR_0_PCRTC;
+ }
+ else {
+ d->pmc.pending_interrupts &= ~NV_PMC_INTR_0_PCRTC;
+ }
+
+ /* PGRAPH */
+ if (d->pgraph.pending_interrupts & d->pgraph.enabled_interrupts) {
+ d->pmc.pending_interrupts |= NV_PMC_INTR_0_PGRAPH;
+ }
+ else {
+ d->pmc.pending_interrupts &= ~NV_PMC_INTR_0_PGRAPH;
+ }
+
+ /* TODO : PBUS * /
+ if (d->pbus.pending_interrupts & d->pbus.enabled_interrupts) {
+ d->pmc.pending_interrupts |= NV_PMC_INTR_0_PBUS;
+ }
+ else {
+ d->pmc.pending_interrupts &= ~NV_PMC_INTR_0_PBUS;
+ } */
+
+ /* TODO : SOFTWARE * /
+ if (d->user.pending_interrupts & d->.enabled_interrupts) {
+ d->pmc.pending_interrupts |= NV_PMC_INTR_0_SOFTWARE;
+ }
+ else {
+ d->pmc.pending_interrupts &= ~NV_PMC_INTR_0_SOFTWARE;
+ } */
+
+ if (d->pmc.pending_interrupts && d->pmc.enabled_interrupts) {
+ HalSystemInterrupts[3].Assert(true);
+ }
+ else {
+ HalSystemInterrupts[3].Assert(false);
+ }
+
+ SwitchToThread();
+}
+
+
+#include "EmuNV2A_DEBUG.cpp"
+
+
+#define DEBUG_READ32(DEV) DbgPrintf("X86 : Read32 NV2A " #DEV "(0x%08X) = 0x%08X [Handle%s]\n", addr, result, DebugNV_##DEV##(addr))
+#define DEBUG_READ32_UNHANDLED(DEV) { DbgPrintf("X86 : Read32 NV2A " #DEV "(0x%08X) = 0x%08X [Unhandle%s]\n", addr, result, DebugNV_##DEV##(addr)); return result; }
+
+#define DEBUG_WRITE32(DEV) DbgPrintf("X86 : Write32 NV2A " #DEV "(0x%08X, 0x%08X) [Handle%s]\n", addr, value, DebugNV_##DEV##(addr))
+#define DEBUG_WRITE32_UNHANDLED(DEV) { DbgPrintf("X86 : Write32 NV2A " #DEV "(0x%08X, 0x%08X) [Unhandle%s]\n", addr, value, DebugNV_##DEV##(addr)); return; }
+
+#define DEVICE_READ32(DEV) uint32_t EmuNV2A_##DEV##_Read32(NV2AState *d, xbaddr addr)
+#define DEVICE_READ32_SWITCH() uint32_t result = 0; switch (addr)
+#define DEVICE_READ32_REG(dev) result = d->dev.regs[addr]
+#define DEVICE_READ32_END(DEV) DEBUG_READ32(DEV); return result
+
+#define DEVICE_WRITE32(DEV) void EmuNV2A_##DEV##_Write32(NV2AState *d, xbaddr addr, uint32_t value)
+#define DEVICE_WRITE32_REG(dev) d->dev.regs[addr] = value
+#define DEVICE_WRITE32_END(DEV) DEBUG_WRITE32(DEV)
+
+static inline uint32_t ldl_le_p(const void *p)
+{
+ return *(uint32_t*)p;
+}
+
+static inline void stl_le_p(uint32_t *p, uint32 v)
+{
+ *p = v;
+}
+
+static DMAObject nv_dma_load(NV2AState *d, xbaddr dma_obj_address)
+{
+ assert(dma_obj_address < d->pramin.ramin_size);
+
+ uint32_t *dma_obj = (uint32_t*)(d->pramin.ramin_ptr + dma_obj_address);
+ uint32_t flags = ldl_le_p(dma_obj);
+ uint32_t limit = ldl_le_p(dma_obj + 1);
+ uint32_t frame = ldl_le_p(dma_obj + 2);
+
+ DMAObject object;
+ object.dma_class = GET_MASK(flags, NV_DMA_CLASS);
+ object.dma_target = GET_MASK(flags, NV_DMA_TARGET);
+ object.address = (frame & NV_DMA_ADDRESS) | GET_MASK(flags, NV_DMA_ADJUST);
+ object.limit = limit;
+
+ return object;
+}
+
+static void *nv_dma_map(NV2AState *d, xbaddr dma_obj_address, xbaddr *len)
+{
+ assert(dma_obj_address < d->pramin.ramin_size);
+
+ DMAObject dma = nv_dma_load(d, dma_obj_address);
+
+ /* TODO: Handle targets and classes properly */
+ NV2A_DPRINTF("dma_map %x, %x, %x %x\n",
+ dma.dma_class, dma.dma_target, dma.address, dma.limit);
+
+ dma.address &= 0x07FFFFFF;
+
+ // assert(dma.address + dma.limit < memory_region_size(d->vram));
+ *len = dma.limit;
+ return d->vram_ptr + dma.address;
+}
+
+#include "EmuNV2A_PBUS.cpp"
+#include "EmuNV2A_PCRTC.cpp"
+#include "EmuNV2A_PFB.cpp"
+#include "EmuNV2A_PGRAPH.cpp"
+#include "EmuNV2A_PFIFO.cpp"
+#include "EmuNV2A_PMC.cpp"
+#include "EmuNV2A_PRAMDAC.cpp"
+#include "EmuNV2A_PRMCIO.cpp"
+#include "EmuNV2A_PRMVIO.cpp"
+#include "EmuNV2A_PTIMER.cpp"
+#include "EmuNV2A_PVIDEO.cpp"
+#include "EmuNV2A_USER.cpp"
+
+#include "EmuNV2A_PRMA.cpp"
+#include "EmuNV2A_PCOUNTER.cpp"
+#include "EmuNV2A_PVPE.cpp"
+#include "EmuNV2A_PTV.cpp"
+#include "EmuNV2A_PRMFB.cpp"
+#include "EmuNV2A_PSTRAPS.cpp"
+#include "EmuNV2A_PRMDIO.cpp"
+#include "EmuNV2A_PRAMIN.cpp"
+
+const NV2ABlockInfo regions[] = { // blocktable
+
+// Note : Avoid designated initializers to facilitate C++ builds
+#define ENTRY(OFFSET, SIZE, NAME, RDFUNC, WRFUNC) \
+ { \
+ #NAME, OFFSET, SIZE, \
+ { RDFUNC, WRFUNC }, \
+ }, \
+
+ /* card master control */
+ ENTRY(0x000000, 0x001000, PMC, EmuNV2A_PMC_Read32, EmuNV2A_PMC_Write32)
+ /* bus control */
+ ENTRY(0x001000, 0x001000, PBUS, EmuNV2A_PBUS_Read32, EmuNV2A_PBUS_Write32)
+ /* MMIO and DMA FIFO submission to PGRAPH and VPE */
+ ENTRY(0x002000, 0x002000, PFIFO, EmuNV2A_PFIFO_Read32, EmuNV2A_PFIFO_Write32)
+ /* access to BAR0/BAR1 from real mode */
+ ENTRY(0x007000, 0x001000, PRMA, EmuNV2A_PRMA_Read32, EmuNV2A_PRMA_Write32)
+ /* video overlay */
+ ENTRY(0x008000, 0x001000, PVIDEO, EmuNV2A_PVIDEO_Read32, EmuNV2A_PVIDEO_Write32)
+ /* time measurement and time-based alarms */
+ ENTRY(0x009000, 0x001000, PTIMER, EmuNV2A_PTIMER_Read32, EmuNV2A_PTIMER_Write32)
+ /* performance monitoring counters */
+ ENTRY(0x00a000, 0x001000, PCOUNTER, EmuNV2A_PCOUNTER_Read32, EmuNV2A_PCOUNTER_Write32)
+ /* MPEG2 decoding engine */
+ ENTRY(0x00b000, 0x001000, PVPE, EmuNV2A_PVPE_Read32, EmuNV2A_PVPE_Write32)
+ /* TV encoder */
+ ENTRY(0x00d000, 0x001000, PTV, EmuNV2A_PTV_Read32, EmuNV2A_PTV_Write32)
+ /* aliases VGA memory window */
+ ENTRY(0x0a0000, 0x020000, PRMFB, EmuNV2A_PRMFB_Read32, EmuNV2A_PRMFB_Write32)
+ /* aliases VGA sequencer and graphics controller registers */
+ ENTRY(0x0c0000, 0x008000, PRMVIO, EmuNV2A_PRMVIO_Read32, EmuNV2A_PRMVIO_Write32) // Size was 0x001000
+ /* memory interface */
+ ENTRY(0x100000, 0x001000, PFB, EmuNV2A_PFB_Read32, EmuNV2A_PFB_Write32)
+ /* straps readout / override */
+ ENTRY(0x101000, 0x001000, PSTRAPS, EmuNV2A_PSTRAPS_Read32, EmuNV2A_PSTRAPS_Write32)
+ /* accelerated 2d/3d drawing engine */
+ ENTRY(0x400000, 0x002000, PGRAPH, EmuNV2A_PGRAPH_Read32, EmuNV2A_PGRAPH_Write32)
+ /* more CRTC controls */
+ ENTRY(0x600000, 0x001000, PCRTC, EmuNV2A_PCRTC_Read32, EmuNV2A_PCRTC_Write32)
+ /* aliases VGA CRTC and attribute controller registers */
+ ENTRY(0x601000, 0x001000, PRMCIO, EmuNV2A_PRMCIO_Read32, EmuNV2A_PRMCIO_Write32)
+ /* RAMDAC, cursor, and PLL control */
+ ENTRY(0x680000, 0x001000, PRAMDAC, EmuNV2A_PRAMDAC_Read32, EmuNV2A_PRAMDAC_Write32)
+ /* aliases VGA palette registers */
+ ENTRY(0x681000, 0x001000, PRMDIO, EmuNV2A_PRMDIO_Read32, EmuNV2A_PRMDIO_Write32)
+ /* RAMIN access */
+ ENTRY(0x700000, 0x100000, PRAMIN, EmuNV2A_PRAMIN_Read32, EmuNV2A_PRAMIN_Write32)
+ /* PFIFO MMIO and DMA submission area */
+ ENTRY(0x800000, 0x400000, USER, EmuNV2A_USER_Read32, EmuNV2A_USER_Write32) // Size was 0x800000
+ /* UREMAP User area mirror - TODO : Confirm */
+ ENTRY(0xC00000, 0x400000, UREMAP, EmuNV2A_USER_Read32, EmuNV2A_USER_Write32) // NOTE : Mirror of USER
+ /* Terminating entry */
+ ENTRY(0xFFFFFF, 0x000000, END, nullptr, nullptr)
+#undef ENTRY
+};
+
+const NV2ABlockInfo* EmuNV2A_Block(xbaddr addr)
+{
+ // Find the block in the block table
+ const NV2ABlockInfo* block = ®ions[0];
+ int i = 0;
+
+ while (block->size > 0) {
+ if (addr >= block->offset && addr < block->offset + block->size) {
+ return block;
+ }
+
+ block = ®ions[++i];
+ }
+
+ return nullptr;
+}
+
+//
+// OPENGL
+//
+
+//
+#define X_D3DTS_STAGECOUNT 4
+
+HDC g_EmuWindowsDC = 0;
+GLuint VertexProgramIDs[4] = { 0, 0, 0, 0 };
+uint ActiveVertexProgramID = 0;
+GLuint TextureIDs[X_D3DTS_STAGECOUNT] = { 0, 0, 0, 0 };
+
+// Vertex shader header, mapping Xbox1 registers to the ARB syntax (original version by KingOfC).
+// Note about the use of 'conventional' attributes in here: Since we prefer to use only one shader
+// for both immediate and deferred mode rendering, we alias all attributes to conventional inputs
+// as much as possible. Only when there's no conventional attribute available, we use generic attributes.
+// So in the following header, we use conventional attributes first, and generic attributes for the
+// rest of the vertex attribute slots. This makes it possible to support immediate and deferred mode
+// rendering with the same shader, and the use of the OpenGL fixed-function pipeline without a shader.
+std::string DxbxVertexShaderHeader =
+"!!ARBvp1.0\n"
+"TEMP R0,R1,R2,R3,R4,R5,R6,R7,R8,R9,R10,R11,R12;\n"
+"ADDRESS A0;\n"
+#ifdef DXBX_OPENGL_CONVENTIONAL
+"ATTRIB v0 = vertex.position;\n" // Was: vertex.attrib[0] (See "conventional" note above)
+"ATTRIB v1 = vertex.%s;\n" // Note : We replace this with "weight" or "attrib[1]" depending GL_ARB_vertex_blend
+"ATTRIB v2 = vertex.normal;\n" // Was: vertex.attrib[2]
+"ATTRIB v3 = vertex.color.primary;\n" // Was: vertex.attrib[3]
+"ATTRIB v4 = vertex.color.secondary;\n" // Was: vertex.attrib[4]
+"ATTRIB v5 = vertex.fogcoord;\n" // Was: vertex.attrib[5]
+"ATTRIB v6 = vertex.attrib[6];\n"
+"ATTRIB v7 = vertex.attrib[7];\n"
+"ATTRIB v8 = vertex.texcoord[0];\n" // Was: vertex.attrib[8]
+"ATTRIB v9 = vertex.texcoord[1];\n" // Was: vertex.attrib[9]
+"ATTRIB v10 = vertex.texcoord[2];\n" // Was: vertex.attrib[10]
+"ATTRIB v11 = vertex.texcoord[3];\n" // Was: vertex.attrib[11]
+#else
+"ATTRIB v0 = vertex.attrib[0];\n"
+"ATTRIB v1 = vertex.attrib[1];\n"
+"ATTRIB v2 = vertex.attrib[2];\n"
+"ATTRIB v3 = vertex.attrib[3];\n"
+"ATTRIB v4 = vertex.attrib[4];\n"
+"ATTRIB v5 = vertex.attrib[5];\n"
+"ATTRIB v6 = vertex.attrib[6];\n"
+"ATTRIB v7 = vertex.attrib[7];\n"
+"ATTRIB v8 = vertex.attrib[8];\n"
+"ATTRIB v9 = vertex.attrib[9];\n"
+"ATTRIB v10 = vertex.attrib[10];\n"
+"ATTRIB v11 = vertex.attrib[11];\n"
+#endif
+"ATTRIB v12 = vertex.attrib[12];\n"
+"ATTRIB v13 = vertex.attrib[13];\n"
+"ATTRIB v14 = vertex.attrib[14];\n"
+"ATTRIB v15 = vertex.attrib[15];\n"
+"OUTPUT oPos = result.position;\n"
+"OUTPUT oD0 = result.color.front.primary;\n"
+"OUTPUT oD1 = result.color.front.secondary;\n"
+"OUTPUT oB0 = result.color.back.primary;\n"
+"OUTPUT oB1 = result.color.back.secondary;\n"
+"OUTPUT oPts = result.pointsize;\n"
+"OUTPUT oFog = result.fogcoord;\n"
+"OUTPUT oT0 = result.texcoord[0];\n"
+"OUTPUT oT1 = result.texcoord[1];\n"
+"OUTPUT oT2 = result.texcoord[2];\n"
+"OUTPUT oT3 = result.texcoord[3];\n"
+"PARAM c[] = { program.env[0..191] };\n" // All constants in 1 array declaration (requires NV_gpu_program4?)
+"PARAM mvp[4] = { state.matrix.mvp };\n";
+
+void SetupPixelFormat(HDC DC)
+{
+ const PIXELFORMATDESCRIPTOR pfd = {
+ /* .nSize = */ sizeof(PIXELFORMATDESCRIPTOR), // size
+ /* .nVersion = */ 1, // version
+ /* .dwFlags = */ PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW | PFD_DOUBLEBUFFER, // support double-buffering
+ /* .iPixelType = */ PFD_TYPE_RGBA, // color type
+ /* .cColorBits = */ 32, // preferred color depth
+ /* .cRedBits = */ 0,
+ /* .cRedShift = */ 0, // color bits (ignored)
+ /* .cGreenBits = */ 0,
+ /* .cGreenShift = */ 0,
+ /* .cBlueBits = */ 0,
+ /* .cBlueShift = */ 0,
+ /* .cAlphaBits = */ 0,
+ /* .cAlphaShift = */ 0, // no alpha buffer
+ /* .cAccumBits = */ 0,
+ /* .cAccumRedBits = */ 0, // no accumulation buffer,
+ /* .cAccumGreenBits = */ 0, // accum bits (ignored)
+ /* .cAccumBlueBits = */ 0,
+ /* .cAccumAlphaBits = */ 0,
+ /* .cDepthBits = */ 16, // depth buffer
+ /* .cStencilBits = */ 0, // no stencil buffer
+ /* .cAuxBuffers = */ 0, // no auxiliary buffers
+ /* .iLayerType= */ PFD_MAIN_PLANE, // main layer
+ /* .bReserved = */ 0,
+ /* .dwLayerMask = */ 0,
+ /* .dwVisibleMask = */ 0,
+ /* .dwDamageMask = */ 0 // no layer, visible, damage masks
+ };
+
+ int PixelFormat = ChoosePixelFormat(DC, &pfd);
+ if (PixelFormat == 0)
+ return;
+
+ if (SetPixelFormat(DC, PixelFormat, &pfd) != TRUE)
+ return;
+}
+
+// From https://github.com/inolen/redream/blob/master/src/video/gl_backend.c
+static int rb_compile_shader(const char *source, GLenum shader_type, GLuint *shader)
+{
+ size_t sourceLength = strlen(source);
+
+ *shader = glCreateShader(shader_type);
+ glShaderSource(*shader, 1, (const GLchar **)&source,
+ (const GLint *)&sourceLength);
+ glCompileShader(*shader);
+
+ GLint compiled;
+ glGetShaderiv(*shader, GL_COMPILE_STATUS, &compiled);
+
+ if (!compiled) {
+ // rb_print_shader_log(*shader);
+ glDeleteShader(*shader);
+ return 0;
+ }
+
+ return 1;
+}
+
+void DxbxCompileShader(std::string Shader)
+{
+ int GLErrorPos;
+
+ // if (MayLog(lfUnit))
+ // DbgPrintf(" NV2A: New vertex program :\n" + Shader);
+
+ /*
+ glProgramStringARB(GL_VERTEX_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, Shader.size(), Shader.c_str());
+
+ // errors are catched
+ glGetIntegerv(GL_PROGRAM_ERROR_POSITION_ARB, &GLErrorPos);
+ */
+ GLuint shader;
+
+ GLErrorPos = rb_compile_shader(Shader.c_str(), GL_VERTEX_SHADER, &shader); // TODO : GL_VERTEX_SHADER_ARB ??
+ /*
+ if (GLErrorPos > 0)
+ {
+ Shader.insert(GLErrorPos, "{ERROR}");
+ EmuWarning("Program error at position %d:", GLErrorPos);
+ EmuWarning((char*)glGetString(GL_PROGRAM_ERROR_STRING_ARB));
+ EmuWarning(Shader.c_str());
+ }
+ */
+}
+void InitOpenGLContext()
+{
+ HGLRC RC;
+ std::string szCode;
+
+ g_EmuWindowsDC = GetDC(g_hEmuWindow); // Actually, you can use any windowed control here
+ SetupPixelFormat(g_EmuWindowsDC);
+
+ RC = wglCreateContext(g_EmuWindowsDC); // makes OpenGL window out of DC
+ wglMakeCurrent(g_EmuWindowsDC, RC); // makes OpenGL window active
+ //ReadImplementationProperties(); // Determine a set of booleans indicating which OpenGL extensions are available
+ //ReadExtensions(); // Assign all OpenGL extension API's (DON'T call them if the extension is not available!)
+
+ // Initialize the viewport :
+ //Viewport.X = 0;
+ //Viewport.Y = 0;
+ //Viewport.Width = g_EmuCDPD.pPresentationParameters.BackBufferWidth;
+ //Viewport.Height = g_EmuCDPD.pPresentationParameters.BackBufferHeight;
+ //Viewport.MinZ = -1.0;
+ //Viewport.MaxZ = 1.0;
+
+ //DxbxUpdateTransformProjection();
+ //DxbxUpdateViewport();
+
+
+ //glutInit();
+ { // rb_init_context();
+ /* link in gl functions at runtime */
+ glewExperimental = GL_TRUE;
+ GLenum err = glewInit();
+ if (err != GLEW_OK) {
+ EmuWarning("GLEW initialization failed: %s", glewGetErrorString(err));
+ return;
+ }
+ }
+
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+ // Switch to left-handed coordinate space (as per http://www.opengl.org/resources/faq/technical/transformations.htm) :
+ // glScalef(1.0, 1.0, -1.0);
+
+ // Set some defaults :
+ glEnable(GL_CULL_FACE);
+ glCullFace(GL_FRONT);
+
+ glEnable(GL_DEPTH_TEST);
+ glDepthFunc(GL_LEQUAL); // Nearer Z coordinates cover further Z
+
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+ glAlphaFunc(GL_GEQUAL, 0.5);
+
+ glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); // GL_LINE for wireframe
+
+ /*
+ // TODO : The following code only works on cards that support the
+ // vertex program extensions (NVidia cards mainly); So for ATI we
+ // have to come up with another solution !!!
+ glGenProgramsARB(4, &VertexProgramIDs[0]);
+ */
+
+#ifdef DXBX_OPENGL_CONVENTIONAL
+ if (GL_ARB_vertex_blend)
+ DxbxVertexShaderHeader = sprintf(DxbxVertexShaderHeader, "weight");
+ else
+ DxbxVertexShaderHeader = sprintf(DxbxVertexShaderHeader, "attrib[1]");
+#endif
+
+ // Precompiled shader for the fixed function pipeline :
+ szCode = DxbxVertexShaderHeader +
+ "# This part adjusts the vertex position by the super-sampling scale & offset :\n"
+ "MOV R0, v0;\n"
+ "RCP R0.w, R0.w;\n"
+ "MUL R0, R0, c[0];\n" // c[-96] in D3D speak - applies SuperSampleScale
+ // Note : Use R12 instead of oPos because this is not yet the final assignment :
+ "ADD R12, R0, c[1];\n" // c[-95] in D3D speak - applies SuperSampleOffset
+
+ "# This part just reads all other components and passes them to the output :\n"
+ "MOV oD0, v3;\n"
+ "MOV oD1, v4;\n"
+ "MOV oFog, v4.w;\n" // specular fog
+ // "MOV oFog, v0.z;\n" // z fog
+ // "RCP oFog, v0.w;\n" // w fog
+ "MOV oPts, v1.x;\n"
+ "MOV oB0, v7;\n"
+ "MOV oB1, v8;\n"
+ "MOV oT0, v9;\n"
+ "MOV oT1, v10;\n"
+ "MOV oT2, v11;\n"
+ "MOV oT3, v12;\n"
+
+ "# This part applies the screen-space transform (not present when '#pragma screenspace' was used) :\n"
+ "MUL R12.xyz, R12, c[58];\n" // c[-38] in D3D speak - see EmuNV2A_ViewportScale,
+ "RCP R1.x, R12.w;\n" // Originally RCC, but that"s not supported in ARBvp1.0 (use "MIN R1, R1, 0" and "MAX R1, R1, 1"?)
+ "MAD R12.xyz, R12, R1.x, c[59];\n" // c[-37] in D3D speak - see EmuNV2A_ViewportOffset
+
+ "# Dxbx addition : Transform the vertex to clip coordinates :\n"
+ "DP4 R0.x, mvp[0], R12;\n"
+ "DP4 R0.y, mvp[1], R12;\n"
+ "DP4 R0.z, mvp[2], R12;\n"
+ "DP4 R0.w, mvp[3], R12;\n"
+ "MOV R12, R0;\n"
+
+ "# Apply Z coord mapping\n"
+ "ADD R12.z, R12.z, R12.z;\n"
+ "ADD R12.z, R12.z, -R12.w;\n"
+
+ "# Here""s the final assignment to oPos :\n"
+ "MOV oPos, R12;\n"
+ "END\n"; // TODO : Check if newline is required?
+
+ // glBindProgramARB(GL_VERTEX_PROGRAM_ARB, VertexProgramIDs[0]);
+ DxbxCompileShader(szCode);
+}
+
+// HACK: Until we implement VGA/proper interrupt generation
+// we simulate VBLANK by calling the interrupt at 60Hz
+std::thread vblank_thread;
+extern std::chrono::time_point> GetNextVBlankTime();
+static void nv2a_vblank_thread(NV2AState *d)
+{
+ CxbxSetThreadName("Cxbx NV2A VBLANK");
+
+ auto nextVBlankTime = GetNextVBlankTime();
+
+ while (true) {
+ // Handle VBlank
+ if (std::chrono::steady_clock::now() > nextVBlankTime) {
+ d->pcrtc.pending_interrupts |= NV_PCRTC_INTR_0_VBLANK;
+ update_irq(d);
+ nextVBlankTime = GetNextVBlankTime();
+ }
+ }
+}
+
+// See NV2ABlockInfo regions[] PRAMIN
+#define NV_PRAMIN_ADDR 0x00700000
+#define NV_PRAMIN_SIZE 0x100000
+
+void CxbxReserveNV2AMemory(NV2AState *d)
+{
+ // Reserve NV2A memory :
+ void *memory = (void *)VirtualAllocEx(
+ GetCurrentProcess(),
+ (void *)NV2A_ADDR,
+ NV2A_SIZE,
+ MEM_RESERVE, // Don't allocate actual physical storage in memory
+ PAGE_NOACCESS); // Any access must result in an access violation exception (handled in EmuException/EmuX86_DecodeException)
+ if (memory == NULL) {
+ EmuWarning("Couldn't reserve NV2A memory, continuing assuming we'll receive (and handle) access violation exceptions anyway...");
+ return;
+ }
+
+ printf("[0x%.4X] INIT: Reserved %d MiB of Xbox NV2A memory at 0x%.8X to 0x%.8X\n",
+ GetCurrentThreadId(), NV2A_SIZE / ONE_MB, NV2A_ADDR, NV2A_ADDR + NV2A_SIZE - 1);
+
+ // Allocate PRAMIN Region
+ d->pramin.ramin_size = NV_PRAMIN_SIZE;
+ d->pramin.ramin_ptr = (uint8_t*)VirtualAllocEx(
+ GetCurrentProcess(),
+ (void*)(NV2A_ADDR + NV_PRAMIN_ADDR),
+ d->pramin.ramin_size,
+ MEM_COMMIT, // No MEM_RESERVE |
+ PAGE_READWRITE);
+ if (d->pramin.ramin_ptr == NULL) {
+ EmuWarning("Couldn't allocate NV2A PRAMIN memory");
+ return;
+ }
+
+ printf("[0x%.4X] INIT: Allocated %d MiB of Xbox NV2A PRAMIN memory at 0x%.8X to 0x%.8X\n",
+ GetCurrentThreadId(), d->pramin.ramin_size / ONE_MB, d->pramin.ramin_ptr, d->pramin.ramin_ptr + d->pramin.ramin_size - 1);
+}
/* NV2ADevice */
+NV2ADevice::NV2ADevice()
+{
+ m_nv2a_state = new NV2AState();
+ pgraph_init(m_nv2a_state);
+}
+
+NV2ADevice::~NV2ADevice()
+{
+ delete m_nv2a_state;
+}
+
// PCI Device functions
void NV2ADevice::Init()
@@ -71,8 +652,26 @@ void NV2ADevice::Init()
m_DeviceId = 0x02A5;
m_VendorId = PCI_VENDOR_ID_NVIDIA;
- // For now, forward to EmuNv2A
- EmuNV2A_Init();
+ NV2AState *d = m_nv2a_state; // glue
+
+ CxbxReserveNV2AMemory(d);
+
+ d->pcrtc.start = 0;
+
+ d->vram_ptr = (uint8_t*)MM_SYSTEM_PHYSICAL_MAP;
+ d->vram_size = (g_bIsChihiro || g_bIsDebug) ? CONTIGUOUS_MEMORY_CHIHIRO_SIZE : CONTIGUOUS_MEMORY_XBOX_SIZE;
+
+ d->pramdac.core_clock_coeff = 0x00011c01; /* 189MHz...? */
+ d->pramdac.core_clock_freq = 189000000;
+ d->pramdac.memory_clock_coeff = 0;
+ d->pramdac.video_clock_coeff = 0x0003C20D; /* 25182Khz...? */
+
+ d->pfifo.puller_thread = std::thread(pfifo_puller_thread, d);
+
+ // Only spawn VBlank thread when LLE is enabled
+ if (bLLE_GPU) {
+ vblank_thread = std::thread(nv2a_vblank_thread, d);
+ }
}
void NV2ADevice::Reset()
@@ -90,41 +689,83 @@ void NV2ADevice::IOWrite(int barIndex, uint32_t port, uint32_t value, unsigned s
uint32_t NV2ADevice::MMIORead(int barIndex, uint32_t addr, unsigned size)
{
+
switch (barIndex) {
- case 0:
- uint32_t value;
- // For now, forward to EmuNV2A
- {
- // Access NV2A regardless weither HLE is disabled or not (ignoring bLLE_GPU)
- value = EmuNV2A_Read(addr, size);
- // Note : EmuNV2A_Read does it's own logging
+ case 0: {
+ // Access NV2A regardless weither HLE is disabled or not (ignoring bLLE_GPU)
+ const NV2ABlockInfo* block = EmuNV2A_Block(addr);
+
+ if (block != nullptr) {
+ switch (size) {
+ case sizeof(uint8_t) :
+ return block->ops.read(m_nv2a_state, addr - block->offset) & 0xFF;
+ case sizeof(uint16_t) :
+ assert((addr & 1) == 0); // TODO : What if this fails?
+
+ return block->ops.read(m_nv2a_state, addr - block->offset) & 0xFFFF;
+ case sizeof(uint32_t) :
+ assert((addr & 3) == 0); // TODO : What if this fails?
+
+ return block->ops.read(m_nv2a_state, addr - block->offset);
+ }
}
- // TODO : call block handler
- return value;
- case 1:
- return 0; // TODO : access physical memory
+ break;
+ }
+ case 1: {
+ // TODO : access physical memory
+ break;
+ }
}
- // TODO : Log unexpected bar access
+ EmuWarning("NV2ADevice::MMIORead: Unhandled barIndex %d, addr %08X, size %d", barIndex, addr, size);
return 0;
}
void NV2ADevice::MMIOWrite(int barIndex, uint32_t addr, uint32_t value, unsigned size)
{
switch (barIndex) {
- case 0:
- // For now, forward to EmuNV2A
- {
- // Access NV2A regardless weither HLE is disabled or not (ignoring bLLE_GPU)
- EmuNV2A_Write(addr, value, size);
- // Note : EmuNV2A_Write does it's own logging
+ case 0: {
+ // Access NV2A regardless weither HLE is disabled or not (ignoring bLLE_GPU)
+ const NV2ABlockInfo* block = EmuNV2A_Block(addr);
+
+ if (block != nullptr) {
+ xbaddr aligned_addr;
+ uint32_t aligned_value;
+ int shift;
+ uint32_t mask;
+
+ switch (size) {
+ case sizeof(uint8_t) :
+ aligned_addr = addr & ~3;
+ aligned_value = block->ops.read(m_nv2a_state, aligned_addr - block->offset);
+ shift = (addr & 3) * 8;
+ mask = 0xFF << shift;
+ block->ops.write(m_nv2a_state, aligned_addr - block->offset, (aligned_value & ~mask) | (value << shift));
+ return;
+ case sizeof(uint16_t) :
+ assert((addr & 1) == 0); // TODO : What if this fails?
+
+ aligned_addr = addr & ~3;
+ aligned_value = block->ops.read(m_nv2a_state, aligned_addr - block->offset);
+ shift = (addr & 2) * 16;
+ mask = 0xFFFF << shift;
+ block->ops.write(m_nv2a_state, aligned_addr - block->offset, (aligned_value & ~mask) | (value << shift));
+ return;
+ case sizeof(uint32_t) :
+ assert((addr & 3) == 0); // TODO : What if this fails?
+
+ block->ops.write(m_nv2a_state, addr - block->offset, value);
+ return;
+ }
}
- // TODO : call block handler
- return;
- case 1:
+
+ break;
+ }
+ case 1: {
// TODO : access physical memory
- return;
+ break;
+ }
}
- // TODO : Log unexpected bar access
+ EmuWarning("NV2ADevice::MMIOWrite: Unhandled barIndex %d, addr %08X, value %08X, size %d", barIndex, addr, value, size);
}
diff --git a/src/devices/video/nv2a.h b/src/devices/video/nv2a.h
index 360bdf2c4..2e6389a32 100644
--- a/src/devices/video/nv2a.h
+++ b/src/devices/video/nv2a.h
@@ -26,7 +26,7 @@
// * If not, write to the Free Software Foundation, Inc.,
// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA.
// *
-// * (c) 2017-2018 Luke Usher
+// * (c) 2016-2018 Luke Usher
// * (c) 2018 Patrick van Logchem
// *
// * All rights reserved
@@ -34,13 +34,597 @@
// ******************************************************************
#pragma once
+#include "Cxbx.h" // For xbaddr
#include "devices\PCIDevice.h" // For PCIDevice
+#include
+#include
+#include
+#include
+
+#include
+
+#include "swizzle.h"
+#include "nv2a_int.h"
+
#define NV2A_ADDR 0xFD000000
#define NV2A_SIZE 0x01000000
+#define NV_PMC_SIZE 0x001000
+#define _NV_PFIFO_SIZE 0x002000 // Underscore prefix to prevent clash with NV_PFIFO_SIZE
+#define NV_PVIDEO_SIZE 0x001000
+#define NV_PTIMER_SIZE 0x001000
+#define NV_PFB_SIZE 0x001000
+#define NV_PGRAPH_SIZE 0x002000
+#define NV_PCRTC_SIZE 0x001000
+#define NV_PRAMDAC_SIZE 0x001000
+
+typedef xbaddr hwaddr; // Compatibility; Cxbx uses xbaddr, xqemu and OpenXbox use hwaddr
+typedef uint32_t value_t; // Compatibility; Cxbx values are uint32_t (xqemu and OpenXbox use uint64_t)
+#define NV2A_DPRINTF(...) printf("[0x????] NV2A: " ## __VA_ARGS__) // Compatibility; TODO : Replace this by something equivalent
+#define NV2A_GL_DPRINTF EmuWarning // Compatibility; TODO : Replace this by something equivalent
+#define VSH_TOKEN_SIZE 4 // Compatibility; TODO : Move this to nv2a_vsh.h
+#define MAX(a,b) ((a)>(b) ? (a) : (b)) // Compatibility
+#define MIN(a,b) ((a)<(b) ? (a) : (b)) // Compatibility
+#undef COMPILE_OPENGL // Compatibility; define this to include all OpenGL calls
+#define HWADDR_PRIx "p" // Compatibility
+#define g_free(x) free(x) // Compatibility
+#define g_malloc(x) malloc(x) // Compatibility
+#define g_malloc0(x) calloc(1, x) // Compatibility
+#define g_realloc(x, y) realloc(x, y) // Compatibility
+
+#define USE_TEXTURE_CACHE
+
+// Public Domain ffs Implementation
+// See: http://snipplr.com/view/22147/stringsh-implementation/
+constexpr int ffs(int v)
+{
+ unsigned int x = v;
+ int c = 1;
+
+ /*
+ * adapted from from
+ * http://graphics.stanford.edu/~seander/bithacks.html#ZerosOnRightBinSearch
+ *
+ * a modified binary search algorithm to count 0 bits from
+ * the right (lsb). This algorithm should work regardless
+ * of the size of ints on the platform.
+ *
+ */
+
+ /* a couple special cases */
+ if (x == 0) return 0;
+ if (x & 1) return 1; /* probably pretty common */
+
+ c = 1;
+ while ((x & 0xffff) == 0) {
+ x >>= 16;
+ c += 16;
+ }
+ if ((x & 0xff) == 0) {
+ x >>= 8;
+ c += 8;
+ }
+ if ((x & 0x0f) == 0) {
+ x >>= 4;
+ c += 4;
+ }
+ if ((x & 0x03) == 0) {
+ x >>= 2;
+ c += 2;
+ }
+
+ c -= (x & 1);
+ c += 1; /* ffs() requires indexing bits from 1 */
+ /* ie., the lsb is bit 1, not bit 0 */
+ return c;
+}
+
+inline int GET_MASK(int v, int mask) {
+ return (((v) & (mask)) >> (ffs(mask) - 1));
+};
+
+inline int SET_MASK(int v, int mask, int val) {
+ const unsigned int __val = (val);
+ const unsigned int __mask = (mask);
+
+ (v) &= ~(__mask);
+ return (v) |= ((__val) << (ffs(__mask)-1)) & (__mask);
+}
+
+// Power-of-two CASE statements
+#define CASE_1(v, step) case (v)
+#define CASE_2(v, step) CASE_1(v, step) : CASE_1(v + (step) * 1, step)
+#define CASE_4(v, step) CASE_2(v, step) : CASE_2(v + (step) * 2, step)
+#define CASE_8(v, step) CASE_4(v, step) : CASE_4(v + (step) * 4, step)
+#define CASE_16(v, step) CASE_8(v, step) : CASE_8(v + (step) * 8, step)
+#define CASE_32(v, step) CASE_16(v, step) : CASE_16(v + (step) * 16, step)
+#define CASE_64(v, step) CASE_32(v, step) : CASE_32(v + (step) * 32, step)
+#define CASE_128(v, step) CASE_64(v, step) : CASE_64(v + (step) * 64, step)
+
+// Non-power-of-two CASE statements
+#define CASE_3(v, step) CASE_2(v, step) : CASE_1(v + (step) * 2, step)
+#define CASE_12(v, step) CASE_8(v, step) : CASE_4(v + (step) * 8, step)
+#define CASE_13(v, step) CASE_8(v, step) : CASE_3(v + (step) * 8, step)
+#define CASE_28(v, step) CASE_16(v, step) : CASE_12(v + (step) * 16, step)
+#define CASE_29(v, step) CASE_16(v, step) : CASE_13(v + (step) * 16, step)
+#define CASE_61(v, step) CASE_32(v, step) : CASE_29(v + (step) * 32, step)
+#define CASE_78(v, step) CASE_64(v, step) : CASE_12(v + (step) * 64, step)
+#define CASE_125(v, step) CASE_64(v, step) : CASE_61(v + (step) * 64, step)
+#define CASE_132(v, step) CASE_128(v, step) : CASE_4(v + (step) * 128, step)
+#define CASE_253(v, step) CASE_128(v, step) : CASE_125(v + (step) * 128, step)
+
+#define NV2A_DEVICE(obj) \
+ OBJECT_CHECK(NV2AState, (obj), "nv2a")
+
+//void reg_log_read(int block, hwaddr addr, uint64_t val);
+//void reg_log_write(int block, hwaddr addr, uint64_t val);
+
+enum FifoMode {
+ FIFO_PIO = 0,
+ FIFO_DMA = 1,
+};
+
+enum FIFOEngine {
+ ENGINE_SOFTWARE = 0,
+ ENGINE_GRAPHICS = 1,
+ ENGINE_DVD = 2,
+};
+
+typedef struct DMAObject {
+ unsigned int dma_class;
+ unsigned int dma_target;
+ xbaddr address;
+ xbaddr limit;
+} DMAObject;
+
+typedef struct VertexAttribute {
+ bool dma_select;
+ xbaddr offset;
+
+ /* inline arrays are packed in order?
+ * Need to pass the offset to converted attributes */
+ unsigned int inline_array_offset;
+
+ float inline_value[4];
+
+ unsigned int format;
+ unsigned int size; /* size of the data type */
+ unsigned int count; /* number of components */
+ uint32_t stride;
+
+ bool needs_conversion;
+ uint8_t *converted_buffer;
+ unsigned int converted_elements;
+ unsigned int converted_size;
+ unsigned int converted_count;
+
+ float *inline_buffer;
+
+ GLint gl_count;
+ GLenum gl_type;
+ GLboolean gl_normalize;
+
+ GLuint gl_converted_buffer;
+ GLuint gl_inline_buffer;
+} VertexAttribute;
+
+typedef struct Surface {
+ bool draw_dirty;
+ bool buffer_dirty;
+ bool write_enabled_cache;
+ unsigned int pitch;
+
+ xbaddr offset;
+} Surface;
+
+typedef struct SurfaceShape {
+ unsigned int z_format;
+ unsigned int color_format;
+ unsigned int zeta_format;
+ unsigned int log_width, log_height;
+ unsigned int clip_x, clip_y;
+ unsigned int clip_width, clip_height;
+ unsigned int anti_aliasing;
+} SurfaceShape;
+
+typedef struct TextureShape {
+ bool cubemap;
+ unsigned int dimensionality;
+ unsigned int color_format;
+ unsigned int levels;
+ unsigned int width, height, depth;
+
+ unsigned int min_mipmap_level, max_mipmap_level;
+ unsigned int pitch;
+} TextureShape;
+
+typedef struct TextureKey {
+ TextureShape state;
+ uint64_t data_hash;
+ uint8_t* texture_data;
+ uint8_t* palette_data;
+} TextureKey;
+
+typedef struct TextureBinding {
+ GLenum gl_target;
+ GLuint gl_texture;
+ unsigned int refcnt;
+} TextureBinding;
+
+typedef struct KelvinState {
+ xbaddr dma_notifies;
+ xbaddr dma_state;
+ xbaddr dma_semaphore;
+ unsigned int semaphore_offset;
+} KelvinState;
+
+typedef struct ContextSurfaces2DState {
+ xbaddr dma_image_source;
+ xbaddr dma_image_dest;
+ unsigned int color_format;
+ unsigned int source_pitch, dest_pitch;
+ xbaddr source_offset, dest_offset;
+
+} ContextSurfaces2DState;
+
+typedef struct ImageBlitState {
+ xbaddr context_surfaces;
+ unsigned int operation;
+ unsigned int in_x, in_y;
+ unsigned int out_x, out_y;
+ unsigned int width, height;
+
+} ImageBlitState;
+
+typedef struct GraphicsObject {
+ uint8_t graphics_class;
+ union {
+ ContextSurfaces2DState context_surfaces_2d;
+
+ ImageBlitState image_blit;
+
+ KelvinState kelvin;
+ } data;
+} GraphicsObject;
+
+typedef struct GraphicsSubchannel {
+ xbaddr object_instance;
+ GraphicsObject object;
+ uint32_t object_cache[5];
+} GraphicsSubchannel;
+
+typedef struct GraphicsContext {
+ bool channel_3d;
+ unsigned int subchannel;
+} GraphicsContext;
+
+
+typedef struct PGRAPHState {
+ std::mutex pgraph_mutex;
+ std::unique_lock pgraph_lock;
+
+ uint32_t pending_interrupts;
+ uint32_t enabled_interrupts;
+ std::condition_variable interrupt_cond;
+
+ xbaddr context_table;
+ xbaddr context_address;
+
+
+ unsigned int trapped_method;
+ unsigned int trapped_subchannel;
+ unsigned int trapped_channel_id;
+ uint32_t trapped_data[2];
+ uint32_t notify_source;
+
+ bool fifo_access;
+ std::condition_variable fifo_access_cond;
+
+ std::condition_variable flip_3d_cond; // Was flip_3d
+
+ unsigned int channel_id;
+ bool channel_valid;
+ GraphicsContext context[NV2A_NUM_CHANNELS];
+
+ xbaddr dma_color, dma_zeta;
+ Surface surface_color, surface_zeta;
+ unsigned int surface_type;
+ SurfaceShape surface_shape;
+ SurfaceShape last_surface_shape;
+
+ xbaddr dma_a, dma_b;
+ //GLruCache *texture_cache;
+ bool texture_dirty[NV2A_MAX_TEXTURES];
+ TextureBinding *texture_binding[NV2A_MAX_TEXTURES];
+
+ //GHashTable *shader_cache;
+ //ShaderBinding *shader_binding;
+
+ bool texture_matrix_enable[NV2A_MAX_TEXTURES];
+
+ /* FIXME: Move to NV_PGRAPH_BUMPMAT... */
+ float bump_env_matrix[NV2A_MAX_TEXTURES - 1][4]; /* 3 allowed stages with 2x2 matrix each */
+
+ // wglContext *gl_context;
+ GLuint gl_framebuffer;
+ GLuint gl_color_buffer, gl_zeta_buffer;
+ GraphicsSubchannel subchannel_data[NV2A_NUM_SUBCHANNELS];
+
+ xbaddr dma_report;
+ xbaddr report_offset;
+ bool zpass_pixel_count_enable;
+ unsigned int zpass_pixel_count_result;
+ unsigned int gl_zpass_pixel_count_query_count;
+ GLuint* gl_zpass_pixel_count_queries;
+
+ xbaddr dma_vertex_a, dma_vertex_b;
+
+ unsigned int primitive_mode;
+
+ bool enable_vertex_program_write;
+
+ uint32_t program_data[NV2A_MAX_TRANSFORM_PROGRAM_LENGTH][VSH_TOKEN_SIZE];
+
+ uint32_t vsh_constants[NV2A_VERTEXSHADER_CONSTANTS][4];
+ bool vsh_constants_dirty[NV2A_VERTEXSHADER_CONSTANTS];
+
+ /* lighting constant arrays */
+ uint32_t ltctxa[NV2A_LTCTXA_COUNT][4];
+ bool ltctxa_dirty[NV2A_LTCTXA_COUNT];
+ uint32_t ltctxb[NV2A_LTCTXB_COUNT][4];
+ bool ltctxb_dirty[NV2A_LTCTXB_COUNT];
+ uint32_t ltc1[NV2A_LTC1_COUNT][4];
+ bool ltc1_dirty[NV2A_LTC1_COUNT];
+
+ // should figure out where these are in lighting context
+ float light_infinite_half_vector[NV2A_MAX_LIGHTS][3];
+ float light_infinite_direction[NV2A_MAX_LIGHTS][3];
+ float light_local_position[NV2A_MAX_LIGHTS][3];
+ float light_local_attenuation[NV2A_MAX_LIGHTS][3];
+
+ VertexAttribute vertex_attributes[NV2A_VERTEXSHADER_ATTRIBUTES];
+
+ unsigned int inline_array_length;
+ uint32_t inline_array[NV2A_MAX_BATCH_LENGTH];
+ GLuint gl_inline_array_buffer;
+
+ unsigned int inline_elements_length;
+ uint32_t inline_elements[NV2A_MAX_BATCH_LENGTH];
+
+ unsigned int inline_buffer_length;
+
+ unsigned int draw_arrays_length;
+ unsigned int draw_arrays_max_count;
+
+ /* FIXME: Unknown size, possibly endless, 1000 will do for now */
+ GLint gl_draw_arrays_start[1000];
+ GLsizei gl_draw_arrays_count[1000];
+
+ GLuint gl_element_buffer;
+ GLuint gl_memory_buffer;
+ GLuint gl_vertex_array;
+
+ uint32_t regs[NV_PGRAPH_SIZE]; // TODO : union
+} PGRAPHState;
+
+
+typedef struct CacheEntry {
+ //QSIMPLEQ_ENTRY(CacheEntry) entry;
+ unsigned int method : 14;
+ unsigned int subchannel : 3;
+ bool nonincreasing;
+ uint32_t parameter;
+} CacheEntry;
+
+typedef struct Cache1State {
+ unsigned int channel_id;
+ FifoMode mode;
+
+ /* Pusher state */
+ bool push_enabled;
+ bool dma_push_enabled;
+ bool dma_push_suspended;
+ xbaddr dma_instance;
+
+ bool method_nonincreasing;
+ unsigned int method : 14;
+ unsigned int subchannel : 3;
+ unsigned int method_count : 24;
+ uint32_t dcount;
+
+ bool subroutine_active;
+ xbaddr subroutine_return;
+ xbaddr get_jmp_shadow;
+ uint32_t rsvd_shadow;
+ uint32_t data_shadow;
+ uint32_t error;
+
+ bool pull_enabled;
+ enum FIFOEngine bound_engines[NV2A_NUM_SUBCHANNELS];
+ enum FIFOEngine last_engine;
+
+ /* The actual command queue */
+ std::mutex cache_lock;
+ std::condition_variable cache_cond;
+ std::queue cache;
+ std::queue working_cache;
+} Cache1State;
+
+typedef struct ChannelControl {
+ xbaddr dma_put;
+ xbaddr dma_get;
+ uint32_t ref;
+} ChannelControl;
+
+typedef struct NV2AState {
+ // PCIDevice dev;
+ // qemu_irq irq;
+ bool exiting;
+
+ // VGACommonState vga;
+ // GraphicHwOps hw_ops;
+ // QEMUTimer *vblank_timer;
+
+ // MemoryRegion *vram;
+ // MemoryRegion vram_pci;
+ uint8_t *vram_ptr;
+ size_t vram_size;
+ // MemoryRegion ramin;
+ struct {
+ uint8_t *ramin_ptr;
+ size_t ramin_size;
+ } pramin;
+
+ // MemoryRegion mmio;
+ // MemoryRegion block_mmio[NV_NUM_BLOCKS];
+
+ struct {
+ uint32_t pending_interrupts;
+ uint32_t enabled_interrupts;
+ uint32_t regs[NV_PMC_SIZE]; // Not in xqemu/openxbox? TODO : union
+ } pmc;
+
+ struct {
+ std::thread puller_thread;
+ uint32_t pending_interrupts;
+ uint32_t enabled_interrupts;
+ Cache1State cache1;
+ uint32_t regs[_NV_PFIFO_SIZE]; // TODO : union
+ } pfifo;
+
+ struct {
+ uint32_t regs[NV_PVIDEO_SIZE]; // TODO : union
+ } pvideo;
+
+ struct {
+ uint32_t pending_interrupts;
+ uint32_t enabled_interrupts;
+ uint32_t numerator;
+ uint32_t denominator;
+ uint32_t alarm_time;
+ uint32_t regs[NV_PTIMER_SIZE]; // Not in xqemu/openxbox? TODO : union
+ } ptimer;
+
+ struct {
+ uint32_t regs[NV_PFB_SIZE]; // TODO : union
+ } pfb;
+
+ struct PGRAPHState pgraph;
+
+ struct {
+ uint32_t pending_interrupts;
+ uint32_t enabled_interrupts;
+ hwaddr start;
+ uint32_t regs[NV_PCRTC_SIZE]; // Not in xqemu/openxbox? TODO : union
+ } pcrtc;
+
+ struct {
+ uint32_t core_clock_coeff;
+ uint64_t core_clock_freq;
+ uint32_t memory_clock_coeff;
+ uint32_t video_clock_coeff;
+ uint32_t regs[NV_PRAMDAC_SIZE]; // Not in xqemu/openxbox? TODO : union
+ } pramdac;
+
+ struct {
+ ChannelControl channel_control[NV2A_NUM_CHANNELS];
+ } user;
+
+ // PRMCIO (Actually the VGA controller)
+ struct {
+ uint8_t cr_index;
+ uint8_t cr[256]; /* CRT registers */
+ } prmcio; // Not in xqemu/openxbox?
+
+ std::mutex io_lock;
+ //SDL_Window *sdl_window;
+
+} NV2AState;
+
+typedef value_t(*read_func)(NV2AState *d, hwaddr addr); //, unsigned int size);
+typedef void(*write_func)(NV2AState *d, hwaddr addr, value_t val); //, unsigned int size);
+
+typedef struct {
+ read_func read;
+ write_func write;
+} MemoryRegionOps;
+
+typedef struct NV2ABlockInfo {
+ const char* name;
+ hwaddr offset;
+ uint64_t size;
+ MemoryRegionOps ops;
+} NV2ABlockInfo;
+
+#if 0
+// Valid after PCI init :
+#define NV20_REG_BASE_KERNEL 0xFD000000
+
+typedef volatile DWORD *PPUSH;
+
+typedef struct {
+ DWORD Ignored[0x10];
+ PPUSH Put; // On Xbox1, this field is only written to by the CPU (the GPU uses this as a trigger to start executing from the given address)
+ PPUSH Get; // On Xbox1, this field is only read from by the CPU (the GPU reflects in here where it is/stopped executing)
+ PPUSH Reference; // TODO : xbaddr / void* / DWORD ?
+ DWORD Ignored2[0x7ED];
+} Nv2AControlDma;
+
+#define PUSH_TYPE_MASK 0x00000002 // 2 bits
+#define PUSH_TYPE_SHIFT 0
+#define PUSH_TYPE_METHOD 0 // method
+#define PUSH_TYPE_JMP_FAR 1 // jump far
+#define PUSH_TYPE_CALL_FAR 2 // call far
+#define PUSH_TYPE_METHOD_UNUSED 3 // method (unused)
+#define PUSH_METHOD_MASK 0x00001FFC // 12 bits
+#define PUSH_METHOD_SHIFT 0 // Dxbx note : Not 2, because methods are actually DWORD offsets (and thus defined with increments of 4)
+#define PUSH_SUBCH_MASK 0x0000E000 // 3 bits
+#define PUSH_SUBCH_SHIFT 13
+#define PUSH_COUNT_MASK 0x1FFC0000 // 11 bits
+#define PUSH_COUNT_SHIFT 18
+#define PUSH_INSTR_MASK 0xE0000000 // 3 bits
+#define PUSH_INSTR_SHIFT 29
+#define PUSH_INSTR_IMM_INCR 0 // immediate, increment
+#define PUSH_INSTR_JMP_NEAR 1 // near jump
+#define PUSH_INSTR_IMM_NOINC 2 // immediate, no-increment
+#define PUSH_ADDR_FAR_MASK 0xFFFFFFFC // 30 bits
+#define PUSH_ADDR_FAR_SHIFT 0
+#define PUSH_ADDR_NEAR_MASK 0x1FFFFFFC // 27 bits
+#define PUSH_ADDR_NEAR_SHIFT 0 // Cxbx note : Not 2, because methods are actually DWORD offsets (and thus defined with increments of 4)
+
+#define PUSH_TYPE(dwPushCommand) ((dwPushCommand & PUSH_TYPE_MASK) >> PUSH_TYPE_SHIFT)
+#define PUSH_METHOD(dwPushCommand) ((dwPushCommand & PUSH_METHOD_MASK) >> PUSH_METHOD_SHIFT)
+#define PUSH_SUBCH(dwPushCommand) ((dwPushCommand & PUSH_SUBCH_MASK) >> PUSH_SUBCH_SHIFT)
+#define PUSH_COUNT(dwPushCommand) ((dwPushCommand & PUSH_COUNT_MASK) >> PUSH_COUNT_SHIFT)
+#define PUSH_INSTR(dwPushCommand) ((dwPushCommand & PUSH_INSTR_MASK) >> PUSH_INSTR_SHIFT)
+#define PUSH_ADDR_FAR(dwPushCommand) ((dwPushCommand & PUSH_ADDR_FAR_MASK) >> PUSH_ADDR_FAR_SHIFT)
+#define PUSH_ADDR_NEAR(dwPushCommand) ((dwPushCommand & PUSH_ADDR_NEAR_MASK) >> PUSH_ADDR_NEAR_SHIFT)
+
+#define PUSH_METHOD_MAX ((PUSH_METHOD_MASK | 3) >> PUSH_METHOD_SHIFT) // = 8191
+#define PUSH_SUBCH_MAX (PUSH_SUBCH_MASK >> PUSH_SUBCH_SHIFT) // = 7
+#define PUSH_COUNT_MAX (PUSH_COUNT_MASK >> PUSH_COUNT_SHIFT) // = 2047
+
+// Decode push buffer conmmand (inverse of D3DPUSH_ENCODE)
+inline void D3DPUSH_DECODE(const DWORD dwPushCommand, DWORD &dwMethod, DWORD &dwSubCh, DWORD &dwCount)
+{
+ dwMethod = PUSH_METHOD(dwPushCommand);
+ dwSubCh = PUSH_SUBCH(dwPushCommand);
+ dwCount = PUSH_COUNT(dwPushCommand);
+}
+#endif
+
+void CxbxReserveNV2AMemory(NV2AState *d);
+
+void InitOpenGLContext();
+
class NV2ADevice : public PCIDevice {
public:
+ // constructor
+ NV2ADevice();
+ // destructor
+ ~NV2ADevice();
+
// PCI Device functions
void Init();
void Reset();
@@ -49,4 +633,6 @@ public:
void IOWrite(int barIndex, uint32_t port, uint32_t value, unsigned size);
uint32_t MMIORead(int barIndex, uint32_t addr, unsigned size);
void MMIOWrite(int barIndex, uint32_t addr, uint32_t value, unsigned size);
+private:
+ NV2AState *m_nv2a_state;
};
diff --git a/src/devices/video/swizzle.cpp b/src/devices/video/swizzle.cpp
new file mode 100644
index 000000000..813e467ec
--- /dev/null
+++ b/src/devices/video/swizzle.cpp
@@ -0,0 +1,163 @@
+/*
+ * QEMU texture swizzling routines
+ *
+ * Copyright (c) 2015 Jannik Vogel
+ * Copyright (c) 2013 espes
+ * Copyright (c) 2007-2010 The Nouveau Project.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see
+ *
+ * Contributions after 2012-01-13 are licensed under the terms of the
+ * GNU GPL, version 2 or (at your option) any later version.
+ */
+
+#include
+#include
+#include
+#include "swizzle.h"
+
+/* This should be pretty straightforward.
+ * It creates a bit pattern like ..zyxzyxzyx from ..xxx, ..yyy and ..zzz
+ * If there are no bits left from any component it will pack the other masks
+ * more tighly (Example: zzxzxzyx = Fewer x than z and even fewer y)
+ */
+static void generate_swizzle_masks(unsigned int width,
+ unsigned int height,
+ unsigned int depth,
+ uint32_t* mask_x,
+ uint32_t* mask_y,
+ uint32_t* mask_z)
+{
+ uint32_t x = 0, y = 0, z = 0;
+ uint32_t bit = 1;
+ uint32_t mask_bit = 1;
+ bool done;
+ do {
+ done = true;
+ if (bit < width) { x |= mask_bit; mask_bit <<= 1; done = false; }
+ if (bit < height) { y |= mask_bit; mask_bit <<= 1; done = false; }
+ if (bit < depth) { z |= mask_bit; mask_bit <<= 1; done = false; }
+ bit <<= 1;
+ } while(!done);
+ assert(((x ^ y) ^ z) == (mask_bit - 1));
+ *mask_x = x;
+ *mask_y = y;
+ *mask_z = z;
+}
+
+/* This fills a pattern with a value if your value has bits abcd and your
+ * pattern is 11010100100 this will return: 0a0b0c00d00
+ */
+static uint32_t fill_pattern(uint32_t pattern, uint32_t value)
+{
+ uint32_t result = 0;
+ uint32_t bit = 1;
+ while(value) {
+ if (pattern & bit) {
+ /* Copy bit to result */
+ result |= value & 1 ? bit : 0;
+ value >>= 1;
+ }
+ bit <<= 1;
+ }
+ return result;
+}
+
+static unsigned int get_swizzled_offset(
+ unsigned int x, unsigned int y, unsigned int z,
+ uint32_t mask_x, uint32_t mask_y, uint32_t mask_z,
+ unsigned int bytes_per_pixel)
+{
+ return bytes_per_pixel * (fill_pattern(mask_x, x)
+ | fill_pattern(mask_y, y)
+ | fill_pattern(mask_z, z));
+}
+
+void swizzle_box(
+ const uint8_t *src_buf,
+ unsigned int width,
+ unsigned int height,
+ unsigned int depth,
+ uint8_t *dst_buf,
+ unsigned int row_pitch,
+ unsigned int slice_pitch,
+ unsigned int bytes_per_pixel)
+{
+ uint32_t mask_x, mask_y, mask_z;
+ generate_swizzle_masks(width, height, depth, &mask_x, &mask_y, &mask_z);
+
+ unsigned int x, y, z;
+ for (z = 0; z < depth; z++) {
+ for (y = 0; y < height; y++) {
+ for (x = 0; x < width; x++) {
+ const uint8_t *src = src_buf
+ + y * row_pitch + x * bytes_per_pixel;
+ uint8_t *dst = dst_buf + get_swizzled_offset(x, y, 0,
+ mask_x, mask_y, 0,
+ bytes_per_pixel);
+ memcpy(dst, src, bytes_per_pixel);
+ }
+ }
+ src_buf += slice_pitch;
+ }
+}
+
+void unswizzle_box(
+ const uint8_t *src_buf,
+ unsigned int width,
+ unsigned int height,
+ unsigned int depth,
+ uint8_t *dst_buf,
+ unsigned int row_pitch,
+ unsigned int slice_pitch,
+ unsigned int bytes_per_pixel)
+{
+ uint32_t mask_x, mask_y, mask_z;
+ generate_swizzle_masks(width, height, depth, &mask_x, &mask_y, &mask_z);
+
+ unsigned int x, y, z;
+ for (z = 0; z < depth; z++) {
+ for (y = 0; y < height; y++) {
+ for (x = 0; x < width; x++) {
+ const uint8_t *src = src_buf
+ + get_swizzled_offset(x, y, z, mask_x, mask_y, mask_z,
+ bytes_per_pixel);
+ uint8_t *dst = dst_buf + y * row_pitch + x * bytes_per_pixel;
+ memcpy(dst, src, bytes_per_pixel);
+ }
+ }
+ dst_buf += slice_pitch;
+ }
+}
+
+void unswizzle_rect(
+ const uint8_t *src_buf,
+ unsigned int width,
+ unsigned int height,
+ uint8_t *dst_buf,
+ unsigned int pitch,
+ unsigned int bytes_per_pixel)
+{
+ unswizzle_box(src_buf, width, height, 1, dst_buf, pitch, 0, bytes_per_pixel);
+}
+
+void swizzle_rect(
+ const uint8_t *src_buf,
+ unsigned int width,
+ unsigned int height,
+ uint8_t *dst_buf,
+ unsigned int pitch,
+ unsigned int bytes_per_pixel)
+{
+ swizzle_box(src_buf, width, height, 1, dst_buf, pitch, 0, bytes_per_pixel);
+}
diff --git a/src/devices/video/swizzle.h b/src/devices/video/swizzle.h
new file mode 100644
index 000000000..6491cdb23
--- /dev/null
+++ b/src/devices/video/swizzle.h
@@ -0,0 +1,64 @@
+/*
+ * QEMU texture swizzling routines
+ *
+ * Copyright (c) 2015 Jannik Vogel
+ * Copyright (c) 2013 espes
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see
+ *
+ * Contributions after 2012-01-13 are licensed under the terms of the
+ * GNU GPL, version 2 or (at your option) any later version.
+ */
+
+#ifndef HW_XBOX_SWIZZLE_H
+#define HW_XBOX_SWIZZLE_H
+
+#include
+
+void swizzle_box(
+ const uint8_t *src_buf,
+ unsigned int width,
+ unsigned int height,
+ unsigned int depth,
+ uint8_t *dst_buf,
+ unsigned int row_pitch,
+ unsigned int slice_pitch,
+ unsigned int bytes_per_pixel);
+
+void unswizzle_box(
+ const uint8_t *src_buf,
+ unsigned int width,
+ unsigned int height,
+ unsigned int depth,
+ uint8_t *dst_buf,
+ unsigned int row_pitch,
+ unsigned int slice_pitch,
+ unsigned int bytes_per_pixel);
+
+void unswizzle_rect(
+ const uint8_t *src_buf,
+ unsigned int width,
+ unsigned int height,
+ uint8_t *dst_buf,
+ unsigned int pitch,
+ unsigned int bytes_per_pixel);
+
+void swizzle_rect(
+ const uint8_t *src_buf,
+ unsigned int width,
+ unsigned int height,
+ uint8_t *dst_buf,
+ unsigned int pitch,
+ unsigned int bytes_per_pixel);
+
+#endif