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