Merge pull request #923 from PatrickvL/NV2A_Split

NV2A split
This commit is contained in:
Luke Usher 2018-02-17 11:49:59 +00:00 committed by GitHub
commit 52679b9fac
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
40 changed files with 8038 additions and 3605 deletions

View File

@ -262,9 +262,9 @@
<ClInclude Include="..\..\src\devices\SMBus.h" />
<ClInclude Include="..\..\src\devices\SMCDevice.h" />
<ClInclude Include="..\..\src\devices\SMDevice.h" />
<ClInclude Include="..\..\src\devices\video\EmuNV2A.h" />
<ClInclude Include="..\..\src\devices\video\nv2a.h" />
<ClInclude Include="..\..\src\devices\video\nv2a_int.h" />
<ClInclude Include="..\..\src\devices\video\swizzle.h" />
<ClInclude Include="..\..\src\devices\video\vga.h" />
<ClInclude Include="..\..\src\devices\Xbox.h" />
</ItemGroup>
@ -355,6 +355,27 @@
<None Include="..\..\src\CxbxKrnl\HLEDataBase\XOnline.1.0.5788.inl" />
<None Include="..\..\src\CxbxKrnl\HLEDataBase\XOnline.1.0.5849.inl" />
<None Include="..\..\src\CxbxKrnl\HLEDataBase\XOnline.OOVPA.inl" />
<None Include="..\..\src\devices\video\EmuNV2A_DEBUG.cpp" />
<None Include="..\..\src\devices\video\EmuNV2A_PBUS.cpp" />
<None Include="..\..\src\devices\video\EmuNV2A_PCOUNTER.cpp" />
<None Include="..\..\src\devices\video\EmuNV2A_PCRTC.cpp" />
<None Include="..\..\src\devices\video\EmuNV2A_PFB.cpp" />
<None Include="..\..\src\devices\video\EmuNV2A_PFIFO.cpp" />
<None Include="..\..\src\devices\video\EmuNV2A_PGRAPH.cpp" />
<None Include="..\..\src\devices\video\EmuNV2A_PMC.cpp" />
<None Include="..\..\src\devices\video\EmuNV2A_PRAMDAC.cpp" />
<None Include="..\..\src\devices\video\EmuNV2A_PRAMIN.cpp" />
<None Include="..\..\src\devices\video\EmuNV2A_PRMA.cpp" />
<None Include="..\..\src\devices\video\EmuNV2A_PRMCIO.cpp" />
<None Include="..\..\src\devices\video\EmuNV2A_PRMDIO.cpp" />
<None Include="..\..\src\devices\video\EmuNV2A_PRMFB.cpp" />
<None Include="..\..\src\devices\video\EmuNV2A_PRMVIO.cpp" />
<None Include="..\..\src\devices\video\EmuNV2A_PSTRAPS.cpp" />
<None Include="..\..\src\devices\video\EmuNV2A_PTIMER.cpp" />
<None Include="..\..\src\devices\video\EmuNV2A_PTV.cpp" />
<None Include="..\..\src\devices\video\EmuNV2A_PVIDEO.cpp" />
<None Include="..\..\src\devices\video\EmuNV2A_PVPE.cpp" />
<None Include="..\..\src\devices\video\EmuNV2A_USER.cpp" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="..\..\resource\Cxbx.rc">
@ -607,8 +628,8 @@
<ClCompile Include="..\..\src\devices\SMBus.cpp" />
<ClCompile Include="..\..\src\devices\SMCDevice.cpp" />
<ClCompile Include="..\..\src\devices\SMDevice.cpp" />
<ClCompile Include="..\..\src\devices\video\EmuNV2A.cpp" />
<ClCompile Include="..\..\src\devices\video\nv2a.cpp" />
<ClCompile Include="..\..\src\devices\video\swizzle.cpp" />
<ClCompile Include="..\..\src\devices\Xbox.cpp" />
</ItemGroup>
<ItemGroup>

View File

@ -229,9 +229,6 @@
<ClCompile Include="..\..\src\devices\EmuNVNet.cpp">
<Filter>Hardware</Filter>
</ClCompile>
<ClCompile Include="..\..\src\devices\video\EmuNV2A.cpp">
<Filter>Hardware</Filter>
</ClCompile>
<ClCompile Include="..\..\src\Common\XbePrinter.cpp">
<Filter>Shared</Filter>
</ClCompile>
@ -241,6 +238,9 @@
<ClCompile Include="..\..\src\devices\MCPXDevice.cpp">
<Filter>Hardware</Filter>
</ClCompile>
<ClCompile Include="..\..\src\devices\video\swizzle.cpp">
<Filter>Hardware</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\src\Cxbx\DlgControllerConfig.h">
@ -453,9 +453,6 @@
<ClInclude Include="..\..\src\devices\EmuNVNet.h">
<Filter>Hardware</Filter>
</ClInclude>
<ClInclude Include="..\..\src\devices\video\EmuNV2A.h">
<Filter>Hardware</Filter>
</ClInclude>
<ClInclude Include="..\..\src\devices\video\nv2a_int.h">
<Filter>Hardware</Filter>
</ClInclude>
@ -474,6 +471,9 @@
<ClInclude Include="..\..\src\devices\MCPXDevice.h">
<Filter>Hardware</Filter>
</ClInclude>
<ClInclude Include="..\..\src\devices\video\swizzle.h">
<Filter>Hardware</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<None Include="..\..\resource\Splash.jpg">
@ -728,6 +728,69 @@
<None Include="..\..\src\CxbxKrnl\HLEDataBase\XOnline.OOVPA.inl">
<Filter>HLEDatabase\XOnline</Filter>
</None>
<None Include="..\..\src\devices\video\EmuNV2A_DEBUG.cpp">
<Filter>Hardware</Filter>
</None>
<None Include="..\..\src\devices\video\EmuNV2A_PBUS.cpp">
<Filter>Hardware</Filter>
</None>
<None Include="..\..\src\devices\video\EmuNV2A_PCOUNTER.cpp">
<Filter>Hardware</Filter>
</None>
<None Include="..\..\src\devices\video\EmuNV2A_PCRTC.cpp">
<Filter>Hardware</Filter>
</None>
<None Include="..\..\src\devices\video\EmuNV2A_PFB.cpp">
<Filter>Hardware</Filter>
</None>
<None Include="..\..\src\devices\video\EmuNV2A_PFIFO.cpp">
<Filter>Hardware</Filter>
</None>
<None Include="..\..\src\devices\video\EmuNV2A_PGRAPH.cpp">
<Filter>Hardware</Filter>
</None>
<None Include="..\..\src\devices\video\EmuNV2A_PMC.cpp">
<Filter>Hardware</Filter>
</None>
<None Include="..\..\src\devices\video\EmuNV2A_PRAMDAC.cpp">
<Filter>Hardware</Filter>
</None>
<None Include="..\..\src\devices\video\EmuNV2A_PRAMIN.cpp">
<Filter>Hardware</Filter>
</None>
<None Include="..\..\src\devices\video\EmuNV2A_PRMA.cpp">
<Filter>Hardware</Filter>
</None>
<None Include="..\..\src\devices\video\EmuNV2A_PRMCIO.cpp">
<Filter>Hardware</Filter>
</None>
<None Include="..\..\src\devices\video\EmuNV2A_PRMDIO.cpp">
<Filter>Hardware</Filter>
</None>
<None Include="..\..\src\devices\video\EmuNV2A_PRMFB.cpp">
<Filter>Hardware</Filter>
</None>
<None Include="..\..\src\devices\video\EmuNV2A_PRMVIO.cpp">
<Filter>Hardware</Filter>
</None>
<None Include="..\..\src\devices\video\EmuNV2A_PSTRAPS.cpp">
<Filter>Hardware</Filter>
</None>
<None Include="..\..\src\devices\video\EmuNV2A_PTIMER.cpp">
<Filter>Hardware</Filter>
</None>
<None Include="..\..\src\devices\video\EmuNV2A_PTV.cpp">
<Filter>Hardware</Filter>
</None>
<None Include="..\..\src\devices\video\EmuNV2A_PVIDEO.cpp">
<Filter>Hardware</Filter>
</None>
<None Include="..\..\src\devices\video\EmuNV2A_PVPE.cpp">
<Filter>Hardware</Filter>
</None>
<None Include="..\..\src\devices\video\EmuNV2A_USER.cpp">
<Filter>Hardware</Filter>
</None>
</ItemGroup>
<ItemGroup>
<Filter Include="GUI">

View File

@ -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

View File

@ -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");

View File

@ -64,7 +64,7 @@ namespace xboxkrnl
#include <sstream> // 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]);
}
}

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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);

View File

@ -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;

View File

@ -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;

View File

@ -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)

View File

@ -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)

File diff suppressed because it is too large Load Diff

View File

@ -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 <caustik@caustik.com>
// * (c) 2016 Luke Usher <luke.usher@outlook.com>
// * 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

View File

@ -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)

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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<std::mutex> 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<std::mutex> cache_unique_lock(d->pfifo.cache1.cache_lock);
result = d->pfifo.cache1.pull_enabled;
break;
}
case NV_PFIFO_CACHE1_ENGINE: {
std::unique_lock<std::mutex> 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<std::mutex> 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<std::mutex> 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<std::mutex> 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<std::mutex> 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;
}

File diff suppressed because it is too large Load Diff

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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<long int>(std::chrono::duration_cast<std::chrono::nanoseconds>(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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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 <luke.usher@outlook.com>
// * (c) 2018 Patrick van Logchem <pvanlogchem@gmail.com>
// *
@ -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 <xboxkrnl/xboxkrnl.h> // 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 <string> // For std::string
#include <distorm.h> // For uint32_t
#include <process.h> // 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 <gl\glew.h>
#include <gl\GL.h>
#include <gl\GLU.h>
#include <cassert>
//#include <gl\glut.h>
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 = &regions[0];
int i = 0;
while (block->size > 0) {
if (addr >= block->offset && addr < block->offset + block->size) {
return block;
}
block = &regions[++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<std::chrono::steady_clock, std::chrono::duration<double, std::nano>> 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);
}

View File

@ -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 <luke.usher@outlook.com>
// * (c) 2016-2018 Luke Usher <luke.usher@outlook.com>
// * (c) 2018 Patrick van Logchem <pvanlogchem@gmail.com>
// *
// * All rights reserved
@ -34,13 +34,597 @@
// ******************************************************************
#pragma once
#include "Cxbx.h" // For xbaddr
#include "devices\PCIDevice.h" // For PCIDevice
#include <mutex>
#include <condition_variable>
#include <queue>
#include <thread>
#include <GL/glew.h>
#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<std::mutex> 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<CacheEntry*> cache;
std::queue<CacheEntry*> 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;
};

View File

@ -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 <http://www.gnu.org/licenses/>
*
* Contributions after 2012-01-13 are licensed under the terms of the
* GNU GPL, version 2 or (at your option) any later version.
*/
#include <stdint.h>
#include <string.h>
#include <assert.h>
#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);
}

View File

@ -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 <http://www.gnu.org/licenses/>
*
* 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 <stdint.h>
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