Created
March 20, 2025 18:46
-
-
Save HansKristian-Work/b88066eb8f14be21277c550a6f775956 to your computer and use it in GitHub Desktop.
PlayStation 2 GS Dump - HelloTriangle
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SPDX-FileCopyrightText: 2024 Arntzen Software AS | |
// SPDX-FileContributor: Hans-Kristian Arntzen | |
// SPDX-FileContributor: Runar Heyer | |
// SPDX-License-Identifier: LGPL-3.0+ | |
// Simplified header for standalone demo purposes :) | |
#pragma once | |
#include <stdint.h> | |
#include <stdio.h> | |
namespace GS | |
{ | |
template <typename T> | |
union Reg64 | |
{ | |
Reg64() : desc{} {} | |
inline Reg64(uint64_t bits_) : bits(bits_) {} | |
inline Reg64(const T &desc_) : desc(desc_) {} | |
T desc; | |
uint64_t bits; | |
uint32_t words[2]; | |
static_assert(sizeof(T) == sizeof(uint64_t), "Reg64<T> is not 64-bits."); | |
}; | |
template <typename T> | |
union Reg128 | |
{ | |
Reg128() : desc{} {}; | |
inline Reg128(const T &desc_) : desc(desc_) {} | |
T desc; | |
uint64_t qwords[2]; | |
uint32_t words[4]; | |
static_assert(sizeof(T) == sizeof(uint64_t) * 2, "Reg128<T> is not 128-bits."); | |
}; | |
#define FIELD(name, bits) uint64_t name : bits | |
#define PAD(b) uint64_t : b | |
#define FIELD32(name, bits) uint32_t name : bits | |
#define PAD32(b) uint32_t : b | |
struct TEX0Bits | |
{ | |
FIELD(TBP0, 14); | |
FIELD(TBW, 6); | |
FIELD(PSM, 6); | |
FIELD(TW, 4); | |
FIELD(TH, 4); | |
FIELD(TCC, 1); | |
FIELD(TFX, 2); | |
FIELD(CBP, 14); | |
FIELD(CPSM, 4); | |
FIELD(CSM, 1); | |
FIELD(CSA, 5); | |
FIELD(CLD, 3); | |
enum { MAX_SIZE_LOG2 = 10, MAX_LEVELS = 7 }; | |
enum | |
{ | |
CLD_IGNORE = 0, | |
CLD_LOAD = 1, | |
CLD_LOAD_WRITE_CBP0 = 2, | |
CLD_LOAD_WRITE_CBP1 = 3, | |
CLD_COMPARE_LOAD_CBP0 = 4, | |
CLD_COMPARE_LOAD_CBP1 = 5 | |
}; | |
enum { CSM_LAYOUT_RECT = 0, CSM_LAYOUT_LINE = 1 }; | |
enum { COU_SCALE = 16 }; | |
}; | |
struct TEX1Bits | |
{ | |
FIELD32(LCM, 1); | |
PAD32(1); | |
FIELD32(MXL, 3); | |
FIELD32(MMAG, 1); | |
FIELD32(MMIN, 3); | |
FIELD32(MTBA, 1); | |
PAD32(9); | |
FIELD32(L, 2); | |
PAD32(11); | |
FIELD32(K, 12); | |
PAD32(20); | |
enum | |
{ | |
NEAREST = 0, | |
LINEAR = 1, | |
NEAREST_MIPMAP_NEAREST = 2, | |
NEAREST_MIPMAP_LINEAR = 3, | |
LINEAR_MIPMAP_NEAREST = 4, | |
LINEAR_MIPMAP_LINEAR = 5 | |
}; | |
inline bool mmin_has_mipmap() const { return MMIN > LINEAR && MMIN <= LINEAR_MIPMAP_LINEAR; } | |
inline bool has_mipmap() const { return mmin_has_mipmap() && MXL != 0; } | |
}; | |
struct MIPTBPBits | |
{ | |
FIELD(TBP1, 14); | |
FIELD(TBW1, 6); | |
FIELD(TBP2, 14); | |
FIELD(TBW2, 6); | |
FIELD(TBP3, 14); | |
FIELD(TBW3, 6); | |
PAD(4); | |
}; | |
struct TEXABits | |
{ | |
FIELD32(TA0, 8); | |
PAD32(7); | |
FIELD32(AEM, 1); | |
PAD32(16); | |
FIELD32(TA1, 8); | |
PAD32(24); | |
}; | |
struct TEXCLUTBits | |
{ | |
FIELD32(CBW, 6); | |
FIELD32(COU, 6); | |
FIELD32(COV, 10); | |
PAD32(10); | |
PAD32(32); | |
}; | |
struct FRAMEBits | |
{ | |
FIELD32(FBP, 9); | |
PAD32(7); | |
FIELD32(FBW, 6); | |
PAD32(2); | |
FIELD32(PSM, 6); | |
PAD32(2); | |
FIELD32(FBMSK, 32); | |
inline bool compat(const FRAMEBits &other) const { return FBP == other.FBP && FBW == other.FBW && PSM == other.PSM; } | |
}; | |
struct ZBUFBits | |
{ | |
FIELD32(ZBP, 9); | |
PAD32(15); | |
FIELD32(PSM, 4); | |
PAD32(4); | |
FIELD32(ZMSK, 1); | |
PAD32(31); | |
enum { PSM_MSB = 3 << 4 }; | |
inline bool compat(const ZBUFBits &other) const { return ZBP == other.ZBP && PSM == other.PSM; } | |
}; | |
struct BITBLTBUFBits | |
{ | |
FIELD32(SBP, 14); | |
PAD32(2); | |
FIELD32(SBW, 6); | |
PAD32(2); | |
FIELD32(SPSM, 6); | |
PAD32(2); | |
FIELD32(DBP, 14); | |
PAD32(2); | |
FIELD32(DBW, 6); | |
PAD32(2); | |
FIELD32(DPSM, 6); | |
PAD32(2); | |
}; | |
struct TRXDIRBits | |
{ | |
FIELD32(XDIR, 2); | |
PAD32(30); | |
PAD32(32); | |
}; | |
struct TRXPOSBits | |
{ | |
FIELD32(SSAX, 11); | |
PAD32(5); | |
FIELD32(SSAY, 11); | |
PAD32(5); | |
FIELD32(DSAX, 11); | |
PAD32(5); | |
FIELD32(DSAY, 11); | |
FIELD32(DIR, 2); | |
PAD32(3); | |
}; | |
struct TRXREGBits | |
{ | |
FIELD32(RRW, 12); | |
PAD32(20); | |
FIELD32(RRH, 12); | |
PAD32(20); | |
}; | |
struct RGBAQBits | |
{ | |
FIELD32(R, 8); | |
FIELD32(G, 8); | |
FIELD32(B, 8); | |
FIELD32(A, 8); | |
float Q; | |
}; | |
struct STBits | |
{ | |
float S; | |
float T; | |
}; | |
struct UVBits | |
{ | |
FIELD32(U, 14); | |
PAD32(2); | |
FIELD32(V, 14); | |
PAD32(2); | |
PAD32(32); | |
}; | |
struct XYZFBits | |
{ | |
FIELD32(X, 16); | |
FIELD32(Y, 16); | |
FIELD32(Z, 24); | |
FIELD32(F, 8); | |
}; | |
struct XYZBits | |
{ | |
FIELD32(X, 16); | |
FIELD32(Y, 16); | |
FIELD32(Z, 32); | |
}; | |
struct FOGBits | |
{ | |
PAD32(32); | |
PAD32(24); | |
FIELD32(FOG, 8); | |
}; | |
struct PRMODECONTBits | |
{ | |
FIELD32(AC, 1); | |
PAD32(31); | |
PAD32(32); | |
enum { AC_DEFAULT = 1 }; | |
}; | |
struct PRIMBits | |
{ | |
FIELD32(PRIM, 3); | |
FIELD32(IIP, 1); | |
FIELD32(TME, 1); | |
FIELD32(FGE, 1); | |
FIELD32(ABE, 1); | |
FIELD32(AA1, 1); | |
FIELD32(FST, 1); | |
FIELD32(CTXT, 1); | |
FIELD32(FIX, 1); | |
PAD32(21); | |
PAD32(32); | |
}; | |
struct ALPHABits | |
{ | |
FIELD32(A, 2); | |
FIELD32(B, 2); | |
FIELD32(C, 2); | |
FIELD32(D, 2); | |
PAD32(24); | |
FIELD32(FIX, 8); | |
PAD32(24); | |
}; | |
struct CLAMPBits | |
{ | |
FIELD(WMS, 2); | |
FIELD(WMT, 2); | |
FIELD(MINU, 10); | |
FIELD(MAXU, 10); | |
FIELD(MINV, 10); | |
FIELD(MAXV, 10); | |
PAD(20); | |
enum { REPEAT = 0, CLAMP = 1, REGION_CLAMP = 2, REGION_REPEAT = 3 }; | |
inline bool has_horizontal_repeat() const { return WMS == REPEAT || WMS == REGION_REPEAT; } | |
inline bool has_vertical_repeat() const { return WMT == REPEAT || WMT == REGION_REPEAT; } | |
inline bool has_horizontal_region() const { return WMS >= REGION_CLAMP; } | |
inline bool has_vertical_region() const { return WMT >= REGION_CLAMP; } | |
inline bool has_region_repeat() const { return WMS == REGION_REPEAT || WMT == REGION_REPEAT; } | |
inline bool has_horizontal_clamp() const { return WMS == CLAMP || WMS == REGION_CLAMP; } | |
inline bool has_vertical_clamp() const { return WMT == CLAMP || WMT == REGION_CLAMP; } | |
}; | |
struct COLCLAMPBits | |
{ | |
FIELD32(CLAMP, 1); | |
PAD32(31); | |
PAD32(32); | |
}; | |
struct DIMXBits | |
{ | |
FIELD32(DM00, 3); | |
PAD32(1); | |
FIELD32(DM01, 3); | |
PAD32(1); | |
FIELD32(DM02, 3); | |
PAD32(1); | |
FIELD32(DM03, 3); | |
PAD32(1); | |
FIELD32(DM10, 3); | |
PAD32(1); | |
FIELD32(DM11, 3); | |
PAD32(1); | |
FIELD32(DM12, 3); | |
PAD32(1); | |
FIELD32(DM13, 3); | |
PAD32(1); | |
FIELD32(DM20, 3); | |
PAD32(1); | |
FIELD32(DM21, 3); | |
PAD32(1); | |
FIELD32(DM22, 3); | |
PAD32(1); | |
FIELD32(DM23, 3); | |
PAD32(1); | |
FIELD32(DM30, 3); | |
PAD32(1); | |
FIELD32(DM31, 3); | |
PAD32(1); | |
FIELD32(DM32, 3); | |
PAD32(1); | |
FIELD32(DM33, 3); | |
PAD32(1); | |
}; | |
struct DTHEBits | |
{ | |
FIELD32(DTHE, 1); | |
PAD32(31); | |
PAD32(32); | |
}; | |
struct FBABits | |
{ | |
FIELD32(FBA, 1); | |
PAD32(31); | |
PAD32(32); | |
}; | |
struct FOGCOLBits | |
{ | |
FIELD32(FCR, 8); | |
FIELD32(FCG, 8); | |
FIELD32(FCB, 8); | |
PAD32(8); | |
PAD32(32); | |
}; | |
struct LABELBits | |
{ | |
FIELD32(ID, 32); | |
FIELD32(IDMSK, 32); | |
}; | |
struct PABEBits | |
{ | |
FIELD32(PABE, 1); | |
PAD32(31); | |
PAD32(32); | |
}; | |
struct SCANMSKBits | |
{ | |
FIELD32(MSK, 2); | |
PAD32(30); | |
PAD32(32); | |
enum { MSK_NONE = 0, MSK_SKIP_EVEN = 2, MSK_SKIP_ODD = 3 }; | |
inline bool has_mask() const { return MSK >= MSK_SKIP_EVEN; } | |
}; | |
struct SCISSORBits | |
{ | |
FIELD32(SCAX0, 11); | |
PAD32(5); | |
FIELD32(SCAX1, 11); | |
PAD32(5); | |
FIELD32(SCAY0, 11); | |
PAD32(5); | |
FIELD32(SCAY1, 11); | |
PAD32(5); | |
}; | |
struct TESTBits | |
{ | |
FIELD32(ATE, 1); | |
FIELD32(ATST, 3); | |
FIELD32(AREF, 8); | |
FIELD32(AFAIL, 2); | |
FIELD32(DATE, 1); | |
FIELD32(DATM, 1); | |
FIELD32(ZTE, 1); | |
FIELD32(ZTST, 2); | |
PAD32(13); | |
PAD32(32); | |
enum { ZTE_ENABLED = 1, ZTE_UNDEFINED = 0 }; | |
enum { ZTST_NEVER = 0, ZTST_ALWAYS = 1, ZTST_GEQUAL = 2, ZTST_GREATER = 3 }; | |
inline bool has_z_test() const { return ZTST > ZTST_ALWAYS; }; | |
}; | |
struct XYOFFSETBits | |
{ | |
FIELD32(OFX, 16); | |
PAD32(16); | |
FIELD32(OFY, 16); | |
PAD32(16); | |
}; | |
// Aliased registers with subset of effective state. | |
using TEX2Bits = TEX0Bits; | |
using SIGNALBits = LABELBits; | |
struct PackedRGBAQBits | |
{ | |
FIELD32(R, 8); | |
PAD32(24); | |
FIELD32(G, 8); | |
PAD32(24); | |
FIELD32(B, 8); | |
PAD32(24); | |
FIELD32(A, 8); | |
PAD32(24); | |
}; | |
struct PackedSTBits | |
{ | |
float S; | |
float T; | |
float Q; | |
PAD32(32); | |
}; | |
struct PackedUVBits | |
{ | |
FIELD32(U, 14); | |
PAD32(18); | |
FIELD32(V, 14); | |
PAD32(18); | |
PAD(64); | |
}; | |
struct PackedXYZFBits | |
{ | |
FIELD32(X, 16); | |
PAD32(16); | |
FIELD32(Y, 16); | |
PAD32(16); | |
PAD32(4); | |
FIELD32(Z, 24); | |
PAD32(4); | |
PAD32(4); | |
FIELD32(F, 8); | |
PAD32(3); | |
FIELD32(ADC, 1); | |
PAD32(16); | |
}; | |
struct PackedXYZBits | |
{ | |
FIELD32(X, 16); | |
PAD32(16); | |
FIELD32(Y, 16); | |
PAD32(16); | |
FIELD32(Z, 32); | |
PAD32(15); | |
FIELD32(ADC, 1); | |
PAD32(16); | |
}; | |
struct PackedFOGBits | |
{ | |
PAD32(32); | |
PAD32(32); | |
PAD32(32); | |
PAD32(4); | |
FIELD32(F, 8); | |
PAD32(20); | |
}; | |
struct PackedADBits | |
{ | |
uint64_t data; | |
FIELD32(ADDR, 7); | |
PAD32(25); | |
}; | |
struct GIFTagBits | |
{ | |
FIELD32(NLOOP, 15); | |
FIELD32(EOP, 1); | |
PAD32(16); | |
PAD32(14); | |
FIELD32(PRE, 1); | |
FIELD32(PRIM, 11); | |
FIELD32(FLG, 2); | |
FIELD32(NREG, 4); | |
uint64_t REGS; | |
enum { PACKED = 0, REGLIST = 1, IMAGE = 2, IMAGE_RESERVED = 3 }; | |
}; | |
enum class GIFAddr : uint32_t | |
{ | |
PRIM = 0x0, | |
RGBAQ = 0x1, | |
ST = 0x02, | |
UV = 0x03, | |
XYZF2 = 0x04, | |
XYZ2 = 0x05, | |
TEX0_1 = 0x06, | |
TEX0_2 = 0x07, | |
CLAMP_1 = 0x08, | |
CLAMP_2 = 0x09, | |
FOG = 0xa, | |
RESERVED = 0xb, | |
XYZF3 = 0xc, | |
XYZ3 = 0xd, | |
A_D = 0xe, | |
NOP = 0xf | |
}; | |
enum class PRIMType : uint32_t | |
{ | |
Point = 0, | |
LineList = 1, | |
LineStrip = 2, | |
TriangleList = 3, | |
TriangleStrip = 4, | |
TriangleFan = 5, | |
Sprite = 6, | |
Invalid = 7 | |
}; | |
// For A+D addressing. | |
enum class RegisterAddr : uint32_t | |
{ | |
#define DECL_REG(reg, addr) reg = addr, | |
DECL_REG(PRIM, 0x00) | |
DECL_REG(RGBAQ, 0x01) | |
DECL_REG(ST, 0x02) | |
DECL_REG(UV, 0x03) | |
DECL_REG(XYZF2, 0x04) | |
DECL_REG(XYZ2, 0x05) | |
DECL_REG(TEX0_1, 0x06) | |
DECL_REG(TEX0_2, 0x07) | |
DECL_REG(CLAMP_1, 0x08) | |
DECL_REG(CLAMP_2, 0x09) | |
DECL_REG(RGBAQUndocumented, 0x11) | |
DECL_REG(FOG, 0x0a) | |
DECL_REG(XYZF3, 0x0c) | |
DECL_REG(XYZ3, 0x0d) | |
DECL_REG(TEX1_1, 0x14) | |
DECL_REG(TEX1_2, 0x15) | |
DECL_REG(TEX2_1, 0x16) | |
DECL_REG(TEX2_2, 0x17) | |
DECL_REG(XYOFFSET_1, 0x18) | |
DECL_REG(XYOFFSET_2, 0x19) | |
DECL_REG(PRMODECONT, 0x1a) | |
DECL_REG(PRMODE, 0x1b) | |
DECL_REG(TEXCLUT, 0x1c) | |
DECL_REG(SCANMSK, 0x22) | |
DECL_REG(MIPTBP1_1, 0x34) | |
DECL_REG(MIPTBP1_2, 0x35) | |
DECL_REG(MIPTBP2_1, 0x36) | |
DECL_REG(MIPTBP2_2, 0x37) | |
DECL_REG(TEXA, 0x3b) | |
DECL_REG(FOGCOL, 0x3d) | |
DECL_REG(TEXFLUSH, 0x3f) | |
DECL_REG(SCISSOR_1, 0x40) | |
DECL_REG(SCISSOR_2, 0x41) | |
DECL_REG(ALPHA_1, 0x42) | |
DECL_REG(ALPHA_2, 0x43) | |
DECL_REG(DIMX, 0x44) | |
DECL_REG(DTHE, 0x45) | |
DECL_REG(COLCLAMP, 0x46) | |
DECL_REG(TEST_1, 0x47) | |
DECL_REG(TEST_2, 0x48) | |
DECL_REG(PABE, 0x49) | |
DECL_REG(FBA_1, 0x4a) | |
DECL_REG(FBA_2, 0x4b) | |
DECL_REG(FRAME_1, 0x4c) | |
DECL_REG(FRAME_2, 0x4d) | |
DECL_REG(ZBUF_1, 0x4e) | |
DECL_REG(ZBUF_2, 0x4f) | |
DECL_REG(BITBLTBUF, 0x50) | |
DECL_REG(TRXPOS, 0x51) | |
DECL_REG(TRXREG, 0x52) | |
DECL_REG(TRXDIR, 0x53) | |
DECL_REG(HWREG, 0x54) | |
DECL_REG(SIGNAL, 0x60) | |
DECL_REG(FINISH, 0x61) | |
DECL_REG(LABEL, 0x62) | |
#undef DECL_REG | |
}; | |
struct PMODEBits | |
{ | |
FIELD32(EN1, 1); | |
FIELD32(EN2, 1); | |
FIELD32(CRTMD, 3); | |
FIELD32(MMOD, 1); | |
FIELD32(AMOD, 1); | |
FIELD32(SLBG, 1); | |
FIELD32(ALP, 8); | |
PAD32(16); | |
PAD32(32); | |
enum { SLBG_ALPHA_BLEND_CIRCUIT2 = 0, SLBG_ALPHA_BLEND_BG = 1 }; | |
enum { MMOD_ALPHA_CIRCUIT1 = 0, MMOD_ALPHA_ALP = 1 }; | |
}; | |
struct BGCOLORBits | |
{ | |
FIELD32(R, 8); | |
FIELD32(G, 8); | |
FIELD32(B, 8); | |
PAD32(8); | |
PAD32(32); | |
}; | |
struct BUSDIRBits | |
{ | |
FIELD32(DIR, 1); | |
PAD32(31); | |
PAD32(32); | |
}; | |
struct CSRBits | |
{ | |
FIELD32(SIGNAL, 1); | |
FIELD32(FINISH, 1); | |
FIELD32(HSINT, 1); | |
FIELD32(VSINT, 1); | |
FIELD32(EDWINT, 1); | |
PAD32(3); | |
FIELD32(FLUSH, 1); | |
FIELD32(RESET, 1); | |
PAD32(2); | |
FIELD32(NFIELD, 1); | |
FIELD32(FIELD, 1); | |
FIELD32(FIFO, 2); | |
FIELD32(REV, 8); | |
FIELD32(ID, 8); | |
PAD32(32); | |
}; | |
struct DISPFBBits | |
{ | |
FIELD32(FBP, 9); | |
FIELD32(FBW, 6); | |
FIELD32(PSM, 5); | |
PAD32(12); | |
FIELD32(DBX, 11); | |
FIELD32(DBY, 11); | |
PAD32(10); | |
}; | |
struct DISPLAYBits | |
{ | |
FIELD32(DX, 12); | |
FIELD32(DY, 11); | |
FIELD32(MAGH, 4); | |
FIELD32(MAGV, 2); | |
PAD32(3); | |
FIELD32(DW, 12); | |
FIELD32(DH, 11); | |
PAD32(9); | |
}; | |
struct EXTBUFBits | |
{ | |
FIELD32(EXBP, 14); | |
FIELD32(EXBW, 6); | |
FIELD32(FBIN, 2); | |
FIELD32(WFFMD, 1); | |
FIELD32(EMODA, 2); | |
FIELD32(EMODC, 2); | |
PAD32(5); | |
FIELD32(WDX, 11); | |
FIELD32(WDY, 11); | |
PAD32(10); | |
}; | |
struct EXTDATABits | |
{ | |
FIELD32(SX, 12); | |
FIELD32(SY, 11); | |
FIELD32(SMPH, 4); | |
FIELD32(SMPV, 2); | |
PAD32(3); | |
FIELD32(WW, 12); | |
FIELD32(WH, 11); | |
PAD32(9); | |
}; | |
struct EXTWRITEBits | |
{ | |
FIELD32(WRITE, 1); | |
PAD32(31); | |
PAD32(32); | |
}; | |
struct IMRBits | |
{ | |
PAD32(8); | |
FIELD32(SIGMSK, 1); | |
FIELD32(FINISHMSK, 1); | |
FIELD32(HSMSK, 1); | |
FIELD32(VSMSK, 1); | |
FIELD32(EDWMSK, 1); | |
PAD32(3); | |
PAD32(16); | |
PAD32(32); | |
}; | |
struct SIGLBLIDBits | |
{ | |
FIELD32(SIGID, 32); | |
FIELD32(LBLID, 32); | |
}; | |
struct SMODE1Bits | |
{ | |
FIELD32(RC, 3); | |
FIELD32(LC, 7); | |
FIELD32(T1248, 2); | |
FIELD32(SLCK, 1); | |
FIELD32(CMOD, 2); | |
FIELD32(EX, 1); | |
FIELD32(PRST, 1); | |
FIELD32(SINT, 1); | |
FIELD32(XPCK, 1); | |
FIELD32(PCK2, 2); | |
FIELD32(SPML, 4); | |
FIELD32(GCONT, 1); | |
FIELD32(PHS, 1); | |
FIELD32(PVS, 1); | |
FIELD32(PEHS, 1); | |
FIELD32(PEVS, 1); | |
FIELD32(CLKSEL, 2); | |
FIELD32(NVCK, 1); | |
FIELD32(SLCK2, 1); | |
FIELD32(VCKSEL, 2); | |
FIELD32(VHP, 1); | |
PAD32(27); | |
enum { CMOD_PROGRESSIVE = 0, CMOD_NTSC = 2, CMOD_PAL = 3 }; | |
enum { LC_ANALOG = 32, LC_HDTV = 22 }; | |
enum { | |
CLOCK_DIVIDER_COMPOSITE = 4, | |
CLOCK_DIVIDER_COMPONENT = 2 /* Seems to be the case based on progressive scan games. */, | |
CLOCK_DIVIDER_HDTV = 1 | |
}; | |
}; | |
struct SMODE2Bits | |
{ | |
FIELD32(INT, 1); | |
FIELD32(FFMD, 1); | |
FIELD32(DPMS, 2); | |
PAD32(29); | |
PAD32(32); | |
}; | |
struct SYNCVBits | |
{ | |
FIELD32(VFP, 10); | |
FIELD32(VFPE, 10); | |
FIELD32(VBP, 12); | |
FIELD32(VBPE, 10); | |
FIELD32(VDP, 11); | |
FIELD32(VS, 11); | |
}; | |
struct DummyBits | |
{ | |
uint64_t dummy; | |
}; | |
struct PrivRegisterState | |
{ | |
union | |
{ | |
struct | |
{ | |
alignas(16) PMODEBits pmode; | |
alignas(16) SMODE1Bits smode1; | |
alignas(16) SMODE2Bits smode2; | |
alignas(16) DummyBits srfsh; | |
alignas(16) DummyBits synch1; | |
alignas(16) DummyBits synch2; | |
alignas(16) SYNCVBits syncv; | |
alignas(16) DISPFBBits dispfb1; | |
alignas(16) DISPLAYBits display1; | |
alignas(16) DISPFBBits dispfb2; | |
alignas(16) DISPLAYBits display2; | |
alignas(16) EXTBUFBits extbuf; | |
alignas(16) EXTDATABits extdata; | |
alignas(16) EXTWRITEBits extwrite; | |
alignas(16) BGCOLORBits bgcolor; | |
}; | |
uint64_t qwords_lo[0x200]; | |
}; | |
union | |
{ | |
struct | |
{ | |
alignas(16) CSRBits csr; | |
alignas(16) IMRBits imr; | |
uint64_t pad0_[5]; | |
alignas(16) BUSDIRBits busdir; | |
uint64_t pad1_[7]; | |
alignas(16) SIGLBLIDBits siglblid; | |
}; | |
uint64_t qwords_hi[0x200]; | |
}; | |
}; | |
enum class GSDumpPacketType : uint8_t | |
{ | |
Transfer = 0, | |
Vsync = 1, | |
ReadFIFO = 2, | |
PrivRegisters = 3, | |
}; | |
enum Formats | |
{ | |
PSMCT32 = 0x00, | |
PSMCT24 = 0x01, | |
PSMCT16 = 0x02, | |
PSMCT16S = 0x0a, | |
PSMT8 = 0x13, | |
PSMT4 = 0x14, | |
PSMT8H = 0x1b, | |
PSMT4HL = 0x24, | |
PSMT4HH = 0x2c, | |
PSMZ32 = 0x30, | |
PSMZ24 = 0x31, | |
PSMZ16 = 0x32, | |
PSMZ16S = 0x3a, | |
PS_GPU24 = 0x12 | |
}; | |
namespace Internal | |
{ | |
static inline bool write_u8(FILE *file, uint8_t value) | |
{ | |
return fwrite(&value, sizeof(value), 1, file) == 1; | |
} | |
static inline bool write_u32(FILE *file, uint32_t value) | |
{ | |
return fwrite(&value, sizeof(value), 1, file) == 1; | |
} | |
static inline bool write_data(FILE *file, const void *data, size_t size) | |
{ | |
return fwrite(data, 1, size, file) == size; | |
} | |
} | |
static inline bool write_gif_packet(FILE *file, const void *data, size_t size) | |
{ | |
if (!Internal::write_u8(file, uint8_t(GSDumpPacketType::Transfer))) | |
return false; | |
if (!Internal::write_u8(file, 1)) | |
return false; | |
if (!Internal::write_u32(file, size)) | |
return false; | |
if (!Internal::write_data(file, data, size)) | |
return false; | |
return true; | |
} | |
static inline bool write_vsync(FILE *file, uint8_t field, const PrivRegisterState &priv) | |
{ | |
if (!Internal::write_u8(file, uint8_t(GSDumpPacketType::PrivRegisters))) | |
return false; | |
if (!Internal::write_data(file, &priv, sizeof(priv))) | |
return false; | |
if (!Internal::write_u8(file, uint8_t(GSDumpPacketType::Vsync))) | |
return false; | |
if (!Internal::write_u8(file, field)) | |
return false; | |
return true; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "gs.hpp" | |
using namespace GS; | |
int main() | |
{ | |
FILE *file = fopen("dump.gs", "wb"); | |
if (!file) | |
return 1; | |
// VRAM is 4 MiB. | |
uint32_t fb_address = 1024 * 1024; | |
// Canonical resolution for NTSC without overscan. | |
uint32_t fb_width = 640; | |
uint32_t fb_height = 448; | |
const struct { | |
GIFTagBits tag; | |
PackedADBits prmode; | |
PackedADBits frame; | |
PackedADBits scissor; | |
} prmode = { | |
.tag = { | |
// Loops once to program 3 registers | |
.NLOOP = 1, | |
.EOP = 1, | |
// 128-bit form | |
.FLG = GIFTagBits::PACKED, | |
// Three registers per loop | |
.NREG = 3, | |
// Up to 16 x 4 bits to program 16 | |
// different registers in one go. | |
// A_D is a general "poke" interface | |
// that can access any HW register. | |
// 0x111 splats the bits. | |
.REGS = int(GIFAddr::A_D) * 0x111, | |
}, | |
.prmode = { | |
// Gouraud shading | |
.data = Reg64<PRIMBits>({ .IIP = 1 }).bits, | |
.ADDR = uint8_t(RegisterAddr::PRMODE), | |
}, | |
.frame = { | |
// Programs the frame buffer | |
// with 32-bit color. | |
.data = Reg64<FRAMEBits>({ | |
.FBP = fb_address / 8192, | |
.FBW = fb_width / 64, | |
.PSM = PSMCT32 }).bits, | |
.ADDR = uint8_t(RegisterAddr::FRAME_1), | |
}, | |
.scissor = { | |
.data = Reg64<SCISSORBits>({ | |
.SCAX0 = 0, | |
.SCAX1 = fb_width - 1, | |
.SCAY0 = 0, | |
.SCAY1 = fb_height - 1 }).bits, | |
.ADDR = uint8_t(RegisterAddr::SCISSOR_1), | |
}, | |
}; | |
write_gif_packet(file, &prmode, sizeof(prmode)); | |
const struct { | |
GIFTagBits tag; | |
PackedRGBAQBits rgba; | |
PackedXYZBits xyz0; | |
PackedXYZBits xyz1; | |
} sprite = { | |
.tag = { | |
// One primitive. | |
.NLOOP = 1, | |
.EOP = 1, | |
// Begin a new primitive sequence. | |
.PRE = 1, | |
.PRIM = Reg64<PRIMBits>({ | |
.PRIM = int(PRIMType::Sprite) }).words[0], | |
.FLG = GIFTagBits::PACKED, | |
.NREG = 3, | |
.REGS = | |
int(GIFAddr::RGBAQ) | | |
(int(GIFAddr::XYZ2) * 0x110), | |
}, | |
.rgba = { | |
.R = 0x20, | |
.G = 0x30, | |
.B = 0x40, | |
.A = 0xff, | |
}, | |
.xyz0 = { | |
// Top-left coordinate in 12.4 fixed point. | |
.X = 0 << 4, | |
.Y = 0 << 4, | |
.Z = 0, | |
}, | |
.xyz1 = { | |
// Bottom-right coordinate in 12.4 fixed point. | |
.X = fb_width << 4, | |
.Y = fb_height << 4, | |
.Z = 0, | |
}, | |
}; | |
struct PackedVertex { | |
PackedRGBAQBits rgba; | |
PackedXYZBits xyz; | |
}; | |
const struct { | |
GIFTagBits tag; | |
PackedVertex verts[3]; | |
} triangle = { | |
.tag = { | |
// Three vertices | |
.NLOOP = 3, | |
.EOP = 1, | |
// Begin a new primitive sequence. | |
.PRE = 1, | |
.PRIM = Reg64<PRIMBits>({ | |
.PRIM = int(PRIMType::TriangleList) }).words[0], | |
.FLG = GIFTagBits::PACKED, | |
// Every loop writes RGBA, then kicks vertex | |
.NREG = 2, | |
.REGS = | |
int(GIFAddr::RGBAQ) | | |
(int(GIFAddr::XYZ2) * 0x10), | |
}, | |
.verts = { | |
{ | |
.rgba = { .R = 0xff }, | |
.xyz = { .X = 300 << 4, .Y = 100 << 4 }, | |
}, | |
{ | |
.rgba = { .G = 0xff }, | |
.xyz = { .X = 100 << 4, .Y = 400 << 4 }, | |
}, | |
{ | |
.rgba = { .B = 0xff }, | |
.xyz = { .X = 500 << 4, .Y = 400 << 4 }, | |
}, | |
} | |
}; | |
write_gif_packet(file, &sprite, sizeof(sprite)); | |
write_gif_packet(file, &triangle, sizeof(triangle)); | |
// Program special registers which control the CRTC, aka display controller. | |
PrivRegisterState priv = {}; | |
// Only enable display circuit 1. | |
priv.pmode.EN1 = 1; | |
priv.pmode.EN2 = 0; | |
// Just has to be 1. *shrug* | |
priv.pmode.CRTMD = 1; | |
// Normal NTSC 480i. | |
priv.smode1.CMOD = SMODE1Bits::CMOD_NTSC; | |
priv.smode1.LC = SMODE1Bits::LC_ANALOG; | |
priv.smode2.INT = 1; | |
// Effectively disables alpha blending against BG color. | |
priv.pmode.MMOD = PMODEBits::MMOD_ALPHA_ALP; | |
priv.pmode.SLBG = PMODEBits::SLBG_ALPHA_BLEND_BG; | |
priv.pmode.ALP = 0xff; | |
// Program the framebuffer pointer. | |
priv.dispfb1.FBP = fb_address / 8192; | |
priv.dispfb1.FBW = fb_width / 64; | |
priv.dispfb1.PSM = PSMCT32; | |
priv.dispfb1.DBX = 0; | |
priv.dispfb1.DBY = 0; | |
// Center the display area so it covers the full screen. | |
priv.display1.DX = 640; // Overscan centering. | |
priv.display1.DY = 50; // Overscan centering. | |
priv.display1.MAGH = 4 - 1; // 640 width framebuffer. | |
priv.display1.MAGV = 0; // No scaling vertically. | |
priv.display1.DW = (640 - 1) * SMODE1Bits::CLOCK_DIVIDER_COMPOSITE; | |
priv.display1.DH = 448 - 1; | |
write_vsync(file, 0, priv); | |
fclose(file); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment