730 lines
27 KiB
C++
730 lines
27 KiB
C++
|
|
// gMatrix: params argument
|
|
// NOTE: the 0 flags are to declare intent. they have no effect.
|
|
constant G_MTX_MODELVIEW(0)
|
|
constant G_MTX_PROJECTION(4)
|
|
constant G_MTX_MUL(0)
|
|
constant G_MTX_LOAD(2)
|
|
constant G_MTX_NOPUSH(0)
|
|
constant G_MTX_PUSH(1)
|
|
|
|
// gMoveWord: index argument
|
|
constant G_MW_MATRIX(0x0)
|
|
constant G_MW_NUMLIGHT(0x2)
|
|
constant G_MW_CLIP(0x4)
|
|
constant G_MW_SEGMENT(0x6)
|
|
constant G_MW_FOG(0x8)
|
|
constant G_MW_LIGHTCOL(0xA)
|
|
constant G_MW_FORCEMTX(0xC)
|
|
constant G_MW_PERSPNORM(0xE)
|
|
|
|
// gMoveWord: offset argument
|
|
constant G_MWO_CLIP_RNX(0x04)
|
|
constant G_MWO_CLIP_RNY(0x0C)
|
|
constant G_MWO_CLIP_RPX(0x14)
|
|
constant G_MWO_CLIP_RPY(0x1C)
|
|
|
|
// gMoveMemory: index argument
|
|
constant G_MV_MMTX(2)
|
|
constant G_MV_PMTX(6)
|
|
constant G_MV_VIEWPORT(8)
|
|
constant G_MV_LIGHT(10)
|
|
constant G_MV_POINT(12)
|
|
constant G_MV_MATRIX(14)
|
|
|
|
// gSetTImage, gSetCImage: fmt argument
|
|
constant G_IM_FMT_RGBA(0)
|
|
constant G_IM_FMT_YUV(1)
|
|
constant G_IM_FMT_CI(2)
|
|
constant G_IM_FMT_IA(3)
|
|
constant G_IM_FMT_I(4)
|
|
|
|
// gSetTImage, gSetCImage: size argument
|
|
constant G_IM_SIZE_4(0)
|
|
constant G_IM_SIZE_8(1)
|
|
constant G_IM_SIZE_16(2)
|
|
constant G_IM_SIZE_32(3)
|
|
constant G_IM_SIZE_DD(5)
|
|
|
|
// gGeometryMode: clearbits, setbits arguments
|
|
constant G_ZBUFFER(1 << 0)
|
|
constant G_SHADE(1 << 2)
|
|
constant G_CULL_FRONT(1 << 9)
|
|
constant G_CULL_BACK(1 << 10)
|
|
constant G_FOG(1 << 16)
|
|
constant G_LIGHTING(1 << 17)
|
|
constant G_TEXTURE_GEN(1 << 18)
|
|
constant G_TEXTURE_GEN_LINEAR(1 << 19)
|
|
constant G_LOD(1 << 20) // unimplemented (presumably no effect)
|
|
constant G_SHADING_SMOOTH(1 << 21)
|
|
constant G_CLIPPING(1 << 23)
|
|
constant G_CULL_BOTH(G_CULL_FRONT | G_CULL_BACK)
|
|
|
|
// gSetOtherModeL: shift amount
|
|
constant G_MDSFT_ALPHACOMPARE(0)
|
|
constant G_MDSFT_ZSRCSEL(2)
|
|
constant G_MDSFT_RENDERMODE(3)
|
|
constant G_MDSFT_BLENDER(16)
|
|
|
|
// gSetOtherModeH: shift amount
|
|
constant G_MDSFT_BLENDMASK(0) // unsupported (presumably no effect)
|
|
constant G_MDSFT_ALPHADITHER(4)
|
|
constant G_MDSFT_RGBDITHER(6)
|
|
constant G_MDSFT_COMBKEY(8)
|
|
constant G_MDSFT_TEXTCONV(9)
|
|
constant G_MDSFT_TEXTFILT(12)
|
|
constant G_MDSFT_TEXTLUT(14)
|
|
constant G_MDSFT_TEXTLOD(16)
|
|
constant G_MDSFT_TEXTDETAIL(17)
|
|
constant G_MDSFT_TEXTPERSP(19)
|
|
constant G_MDSFT_CYCLETYPE(20)
|
|
constant G_MDSFT_COLORDITHER(22) // unsupported (presumably no effect)
|
|
constant G_MDSFT_PIPELINE(23)
|
|
|
|
// gSetOtherModeH gPipelineMode
|
|
constant G_PM_1PRIMITIVE(1 << G_MDSFT_PIPELINE)
|
|
constant G_PM_NPRIMITIVE(0 << G_MDSFT_PIPELINE)
|
|
|
|
// gSetOtherModeH gSetCycleType
|
|
constant G_CYC_1CYCLE(0 << G_MDSFT_CYCLETYPE)
|
|
constant G_CYC_2CYCLE(1 << G_MDSFT_CYCLETYPE)
|
|
constant G_CYC_COPY(2 << G_MDSFT_CYCLETYPE)
|
|
constant G_CYC_FILL(3 << G_MDSFT_CYCLETYPE)
|
|
|
|
// gSetOtherModeH gSetTexturePersp
|
|
constant G_TP_NONE(0 << G_MDSFT_TEXTPERSP)
|
|
constant G_TP_PERSP(1 << G_MDSFT_TEXTPERSP)
|
|
|
|
// gSetOtherModeH gSetTextureDetail
|
|
constant G_TD_CLAMP(0 << G_MDSFT_TEXTDETAIL)
|
|
constant G_TD_SHARPEN(1 << G_MDSFT_TEXTDETAIL)
|
|
constant G_TD_DETAIL(2 << G_MDSFT_TEXTDETAIL)
|
|
|
|
// gSetOtherModeH gSetTextureLOD
|
|
constant G_TL_TILE(0 << G_MDSFT_TEXTLOD)
|
|
constant G_TL_LOD(1 << G_MDSFT_TEXTLOD)
|
|
|
|
// gSetOtherModeH gSetTextureLUT
|
|
constant G_TT_NONE(0 << G_MDSFT_TEXTLUT)
|
|
constant G_TT_RGBA16(2 << G_MDSFT_TEXTLUT)
|
|
constant G_TT_IA16(3 << G_MDSFT_TEXTLUT)
|
|
|
|
// gSetOtherModeH gSetTextureFilter
|
|
constant G_TF_POINT(0 << G_MDSFT_TEXTFILT)
|
|
constant G_TF_AVERAGE(3 << G_MDSFT_TEXTFILT)
|
|
constant G_TF_BILERP(2 << G_MDSFT_TEXTFILT)
|
|
|
|
// gSetOtherModeH gSetTextureConvert
|
|
constant G_TC_CONV(0 << G_MDSFT_TEXTCONV)
|
|
constant G_TC_FILTCONV(5 << G_MDSFT_TEXTCONV)
|
|
constant G_TC_FILT(6 << G_MDSFT_TEXTCONV)
|
|
|
|
// gSetOtherModeH gSetCombineKey
|
|
constant G_CK_NONE(0 << G_MDSFT_COMBKEY)
|
|
constant G_CK_KEY(1 << G_MDSFT_COMBKEY)
|
|
|
|
// gSetOtherModeH gSetColorDither
|
|
constant G_CD_MAGICSQ(0 << G_MDSFT_RGBDITHER)
|
|
constant G_CD_BAYER(1 << G_MDSFT_RGBDITHER)
|
|
constant G_CD_NOISE(2 << G_MDSFT_RGBDITHER)
|
|
constant G_CD_DISABLE(3 << G_MDSFT_RGBDITHER)
|
|
constant G_CD_ENABLE(G_CD_NOISE) // for legacy code
|
|
|
|
// gSetOtherModeH gSetAlphaDither
|
|
constant G_AD_PATTERN(0 << G_MDSFT_ALPHADITHER)
|
|
constant G_AD_NOTPATTERN(1 << G_MDSFT_ALPHADITHER)
|
|
constant G_AD_NOISE(2 << G_MDSFT_ALPHADITHER)
|
|
constant G_AD_DISABLE(3 << G_MDSFT_ALPHADITHER)
|
|
|
|
// gSetOtherModeL gSetAlphaCompare
|
|
constant G_AC_NONE(0 << G_MDSFT_ALPHACOMPARE)
|
|
constant G_AC_THRESHOLD(1 << G_MDSFT_ALPHACOMPARE)
|
|
constant G_AC_DITHER(3 << G_MDSFT_ALPHACOMPARE)
|
|
|
|
// gSetOtherModeL gSetDepthSource
|
|
constant G_ZS_PIXEL(0 << G_MDSFT_ZSRCSEL)
|
|
constant G_ZS_PRIM(1 << G_MDSFT_ZSRCSEL)
|
|
|
|
// gSetOtherModeL gSetRenderMode
|
|
constant AA_EN(0x8) // enable anti-aliasing, except not really?
|
|
// Z stuff
|
|
constant Z_CMP(0x10) // compare pixels to Z buffer (typically used with Z_UPD)
|
|
constant Z_UPD(0x20) // write pixels to Z buffer (typically used with Z_CMP)
|
|
// coverage stuff
|
|
constant IM_RD(0x40)
|
|
constant CLR_ON_CVG(0x80)
|
|
constant CVG_DST_CLAMP(0)
|
|
constant CVG_DST_WRAP(0x100)
|
|
constant CVG_DST_FULL(0x200)
|
|
constant CVG_DST_SAVE(0x300)
|
|
// more Z stuff
|
|
constant ZMODE_OPA(0)
|
|
constant ZMODE_INTER(0x400)
|
|
constant ZMODE_XLU(0x800)
|
|
constant ZMODE_DEC(0xC00)
|
|
// more coverage stuff
|
|
constant CVG_X_ALPHA(0x1000)
|
|
constant ALPHA_CVG_SEL(0x2000)
|
|
// blending
|
|
constant FORCE_BL(0x4000) // no effect?
|
|
|
|
macro gNoOp() {
|
|
// stalls the RDP (and not the RSP?)
|
|
_g(0x00, 0, 0)
|
|
}
|
|
|
|
macro gVertex(variable vaddr, variable numv, variable vbidx) {
|
|
// TODO: document
|
|
if numv < 1 || numv > 32 {; error "numv out of range"; }
|
|
if vbidx < 0 || vbidx > 31 {; error "vbidx out of range"; }
|
|
_g(0x01, (numv << 12) | (((vbidx + numv) & 0x7F) << 1), vaddr)
|
|
}
|
|
|
|
macro gModifyVertex(variable vbidx, variable where, variable val) {
|
|
// TODO: document
|
|
if where != 0x10 && where != 0x14 && where != 0x18 && where != 0x1C {; error "invalid enum for where"; }
|
|
if vbidx < 0 || vbidx > 0x7FFF {; error "vbidx out of range"; }
|
|
_g(0x02, (where << 16) | (vbidx << 1), val)
|
|
}
|
|
|
|
macro gCullList(variable vfirst, variable vlast) {
|
|
// TODO: document
|
|
if vfirst < 0 || vfirst > 0x7FFF {; error "vfirst out of range"; }
|
|
if vlast < 0 || vlast > 0x7FFF {; error "vlast out of range"; }
|
|
_g(0x03, vfirst << 1, vlast << 1)
|
|
}
|
|
|
|
macro gBranchLessZ(variable newdl, variable vbidx, variable zval) {
|
|
// TODO: check ranges, document
|
|
gRdpHalf1(newdl)
|
|
_g(0x04, ((vbidx * 5) << 12) | (vbidx * 2), zval)
|
|
}
|
|
|
|
macro gTri1(variable v0, variable v1, variable v2) {
|
|
// draws two triangles given vertex indices relative to those loaded by gVertex.
|
|
// for G_CULL_FRONT, the vertices should be given in a clockwise order.
|
|
// for G_CULL_BACK, the vertices should be given in a counter-clockwise order.
|
|
if v0 < 0 || v0 > 31 {; error "v0 out of range"; }
|
|
if v1 < 0 || v1 > 31 {; error "v1 out of range"; }
|
|
if v2 < 0 || v2 > 31 {; error "v2 out of range"; }
|
|
_g(0x05, (v0 << 17) | (v1 << 9) | (v2 << 1), 0)
|
|
}
|
|
|
|
macro gTri2(variable a0, variable a1, variable a2, variable b0, variable b1, variable b2) {
|
|
// draws two triangles given vertex indices relative to those loaded by gVertex.
|
|
// some microcodes run this faster than the equivalent two gTri1 calls.
|
|
// for G_CULL_FRONT, the vertices should be given in a clockwise order.
|
|
// for G_CULL_BACK, the vertices should be given in a counter-clockwise order.
|
|
if a0 < 0 || a0 > 31 {; error "a0 out of range"; }
|
|
if a1 < 0 || a1 > 31 {; error "a1 out of range"; }
|
|
if a2 < 0 || a2 > 31 {; error "a2 out of range"; }
|
|
if b0 < 0 || b0 > 31 {; error "b0 out of range"; }
|
|
if b1 < 0 || b1 > 31 {; error "b1 out of range"; }
|
|
if b2 < 0 || b2 > 31 {; error "b2 out of range"; }
|
|
_g(0x06, (a0 << 17) | (a1 << 9) | (a2 << 1), (b0 << 17) | (b1 << 9) | (b2 << 1))
|
|
}
|
|
|
|
macro gQuad(variable v0, variable v1, variable v2, variable v3) {
|
|
// you really should be using gTri2 instead.
|
|
// some microcodes don't support this.
|
|
_g(0x07, (v0 << 17) | (v1 << 9) | (v2 << 1), (v0 << 17) | (v2 << 9) | (v3 << 1))
|
|
}
|
|
|
|
macro gLine3D(variable a, variable b) {
|
|
// TODO: document, implement
|
|
_g(0x08, a, b)
|
|
}
|
|
|
|
// these triangle commands are never used directly,
|
|
// instead, they are generated by the RSP
|
|
// by OR-ing the lowest byte of the current GeometryMode with 0xC8.
|
|
|
|
macro gTriFill(variable a, variable b) {
|
|
// (internal use) fill triangle
|
|
_g(0xC8, a, b)
|
|
}
|
|
|
|
macro gTriFillZ(variable a, variable b) {
|
|
// (internal use) fill triangle utilizing z-buffer
|
|
_g(0xC9, a, b)
|
|
}
|
|
|
|
macro gTriTexture(variable a, variable b) {
|
|
// (internal use) texture triangle
|
|
_g(0xCA, a, b)
|
|
}
|
|
|
|
macro gTriTextureZ(variable a, variable b) {
|
|
// (internal use) texture triangle utilizing z-buffer
|
|
_g(0xCB, a, b)
|
|
}
|
|
|
|
macro gTriShade(variable a, variable b) {
|
|
// (internal use) shade triangle
|
|
_g(0xCC, a, b)
|
|
}
|
|
|
|
macro gTriShadeZ(variable a, variable b) {
|
|
// (internal use) shade triangle utilizing z-buffer
|
|
_g(0xCD, a, b)
|
|
}
|
|
|
|
macro gTriShadeTexture(variable a, variable b) {
|
|
// (internal use) shade and texture triangle
|
|
_g(0xCE, a, b)
|
|
}
|
|
|
|
macro gTriShadeTextureZ(variable a, variable b) {
|
|
// (internal use) shade and texture triangle utilizing z-buffer
|
|
_g(0xCF, a, b)
|
|
}
|
|
|
|
macro gSpecial3(variable a, variable b) {
|
|
// TODO: see if this actually does something
|
|
_g(0xD3, a, b)
|
|
}
|
|
|
|
macro gSpecial2(variable a, variable b) {
|
|
// TODO: see if this actually does something
|
|
_g(0xD4, a, b)
|
|
}
|
|
|
|
macro gSpecial1(variable a, variable b) {
|
|
// TODO: see if this actually does something
|
|
_g(0xD5, a, b)
|
|
}
|
|
|
|
macro gDmaIo(variable flag, variable dmem, variable dram, variable size) {
|
|
if flag != 0 && flag != 1 {; error "invalid enum for flag"; }
|
|
// TODO: check more ranges
|
|
_g(0xD6, (flag << 23) | (((dmem / 8) & 0x3FF) << 12) | (size - 1), dram)
|
|
}
|
|
|
|
macro gTexture(variable scaleS, variable scaleT, variable level, variable tile, variable on) {
|
|
// TODO: document
|
|
// scaleS and scaleT are fractional values; e.g. 0x8000 is 0.5.
|
|
// level + 1: number of mipmaps
|
|
if level < 0 || level > 7 {; error "level out of range"; }
|
|
if tile < 0 || tile > 7 {; error "tile out of range"; }
|
|
if on != 0 && on != 1 {; error "invalid enum for on"; }
|
|
if scaleS < 0 || scaleS > 0xFFFF {; error "scaleS out of range"; }
|
|
if scaleT < 0 || scaleT > 0xFFFF {; error "scaleT out of range"; }
|
|
_g(0xD7, (level << 11) | (tile << 8) | on, (scaleS << 16) | scaleT)
|
|
}
|
|
|
|
macro gPopMatrix(variable num) {
|
|
// TODO: document, better range?
|
|
if num < 0 || num > 0x3FFFFFF {; error "num out of range"; }
|
|
_g(0xD8, 0x380002, num << 6)
|
|
}
|
|
|
|
macro gGeometryMode(variable clearbits, variable setbits) {
|
|
// TODO: document, add convenience macros
|
|
_g(0xD9, clearbits, setbits)
|
|
}
|
|
|
|
macro gMatrix(variable mtxaddr, variable params) {
|
|
// TODO: document, add convenience macros
|
|
if params < 0 || params > 7 {; error "invalid enum for params"; }
|
|
_g(0xDA, 0x380000 | params ^ G_MTX_PUSH, mtxaddr)
|
|
}
|
|
|
|
macro gMoveWord(variable index, variable offset, variable data) {
|
|
// TODO: document, add (more) convenience macros
|
|
if (index & 1) != 0 || index < 0 || index > 0xE {; error "invalid enum for index"; }
|
|
if offset < 0 || offset > 0xFFFF {; error "offset out of range"; }
|
|
if (offset & 3) != 0 {; error "offset unaligned"; }
|
|
_g(0xDB, (index << 16) | offset, data)
|
|
}
|
|
|
|
macro gMoveMemory(variable size, variable index, variable offset, variable addr) {
|
|
// TODO: document
|
|
if size < 1 || size > 0x100 {; error "size out of range"; }
|
|
if (index & 1) != 0 || index < 0 || index > 14 {; "invalid enum for index"; }
|
|
if offset < 0 || offset > 0x7F8 {; error "offset out of range"; }
|
|
if (offset & 7) != 0 {; error "offset unaligned"; }
|
|
variable written_size(((size - 1) / 8) & 0x1F)
|
|
_g(0xDC, (written_size << 19) | (offset << 5) | index, addr)
|
|
}
|
|
|
|
macro gLoadMicrocode(variable tstart, variable dstart, variable dsize) {
|
|
// TODO: document
|
|
if dsize < 0 || dsize > 0x1000 {; error "dsize out of range"; }
|
|
gRdpHalf1(dstart)
|
|
_g(0xDD, dsize, tstart)
|
|
}
|
|
|
|
macro gDisplayList(variable dl) {
|
|
// "calls" another display list,
|
|
// resuming execution from this point after "returning" with gEndList.
|
|
_g(0xDE, 0, dl)
|
|
}
|
|
|
|
macro gBranchList(variable dl) {
|
|
// jumps to another display list.
|
|
_g(0xDE, 1 << 16, dl)
|
|
}
|
|
|
|
macro gEndList() {
|
|
// ends a display list.
|
|
// this either "returns" to the last gBranchList,
|
|
// or in the absence of that, sets signal 2 and breaks.
|
|
_g(0xDF, 0, 0)
|
|
}
|
|
|
|
macro gSpNoOp() {
|
|
// stalls the RSP (and not the RDP?)
|
|
_g(0xE0, 0, 0)
|
|
}
|
|
|
|
macro gRdpHalf1(variable wordhi) {
|
|
// this command is used in other commands to carry additional information.
|
|
_g(0xE1, 0, wordhi)
|
|
}
|
|
|
|
macro gSetOtherModeL(variable shift, variable length, variable data) {
|
|
// TODO: document, add convenience macros.
|
|
// starts clearing bits starting from shift
|
|
// and upwards (towards the MSB) until length is met.
|
|
// TODO: is a length of 0 valid?
|
|
if shift < 0 || shift > 31 {; error "shift out of range"; }
|
|
if length < 1 || length > 32 {; error "length out of range"; }
|
|
if shift + length > 32 {; error "length exceeds word size for given shift"; }
|
|
_g(0xE2, ((32 - shift - length) << 16) | (length - 1), data)
|
|
}
|
|
|
|
macro gSetOtherModeH(variable shift, variable length, variable data) {
|
|
// TODO: document, add convenience macros.
|
|
// starts clearing bits starting from shift
|
|
// and upwards (towards the MSB) until length is met.
|
|
// TODO: is a length of 0 valid?
|
|
if shift < 0 || shift > 31 {; error "shift out of range"; }
|
|
if length < 1 || length > 32 {; error "length out of range"; }
|
|
if shift + length > 32 {; error "length exceeds word size for given shift"; }
|
|
_g(0xE3, ((32 - shift - length) << 16) | (length - 1), data)
|
|
}
|
|
|
|
macro gTextureRect(variable ulx, variable uly, variable lrx, variable lry, variable tile, variable uls, variable ult, variable dsdx, variable dtdy) {
|
|
// arguments: upper-left x, upper-left y, lower-right x, lower-right y,
|
|
// tile descriptor, texture S coord, texture T coord,
|
|
// change in S over X, change in T over Y.
|
|
// TODO: document, check uls/ult ranges
|
|
if ulx < 0 || ulx > 1024 * 4 {; error "ulx out of range"; }
|
|
if uly < 0 || uly > 1024 * 4 {; error "uly out of range"; }
|
|
if lrx < 0 || lrx > 1024 * 4 {; error "lrx out of range"; }
|
|
if lry < 0 || lry > 1024 * 4 {; error "lry out of range"; }
|
|
if tile < 0 || tile > 7 {; error "tile out of range"; }
|
|
_g(0xE4, (lrx << 12) | lry, (tile << 24) | (ulx << 12) | uly)
|
|
gRdpHalf1((uls << 16) | ult)
|
|
gRdpHalf2((dsdx << 16) | dtdy)
|
|
}
|
|
|
|
macro gTextureRect(variable ulx, variable uly, variable lrx, variable lry, variable tile, variable uls, variable ult, variable dtdx, variable dsdy) {
|
|
// same as gTextureRect except flipped along the diagonal:
|
|
// S is instead based on Y, and T is instead based on X.
|
|
// the last two arguments have accordingly different names.
|
|
// TODO: check uls/ult ranges
|
|
if ulx < 0 || ulx > 1024 * 4 {; error "ulx out of range"; }
|
|
if uly < 0 || uly > 1024 * 4 {; error "uly out of range"; }
|
|
if lrx < 0 || lrx > 1024 * 4 {; error "lrx out of range"; }
|
|
if lry < 0 || lry > 1024 * 4 {; error "lry out of range"; }
|
|
if tile < 0 || tile > 7 {; error "tile out of range"; }
|
|
_g(0xE4, (lrx << 12) | lry, (tile << 24) | (ulx << 12) | uly)
|
|
gRdpHalf1((uls << 16) | ult)
|
|
gRdpHalf2((dtdx << 16) | dsdy)
|
|
}
|
|
|
|
macro gRdpLoadSync() {
|
|
// wait for a texture to load.
|
|
_g(0xE6, 0, 0)
|
|
}
|
|
|
|
macro gRdpPipeSync() {
|
|
// wait for the last primitive to finish rendering.
|
|
_g(0xE7, 0, 0)
|
|
}
|
|
|
|
macro gRdpTileSync() {
|
|
// wait for rendering to finish before changing tile attributes.
|
|
// TODO: better document.
|
|
_g(0xE8, 0, 0)
|
|
}
|
|
|
|
macro gRdpFullSync() {
|
|
// finish rendering. usually followed by a final gEndList command.
|
|
_g(0xE9, 0, 0)
|
|
}
|
|
|
|
macro gSetKeyGB() {
|
|
// TODO
|
|
_g(0xEA, 0, 0)
|
|
}
|
|
|
|
macro gSetKeyR() {
|
|
// TODO
|
|
_g(0xEB, 0, 0)
|
|
}
|
|
|
|
macro gSetConvert() {
|
|
// TODO
|
|
_g(0xEC, 0, 0)
|
|
}
|
|
|
|
macro gSetScissor(variable mode, variable ulx, variable uly, variable lrx, variable lry) {
|
|
// constrains rendering to a rectangle.
|
|
// arguments: mode, upper-left x, upper-left y, lower-right x, lower-right y.
|
|
// the lower-right coordinates are exclusive? whereas gFillRect is inclusive.
|
|
if mode != 0 && mode != 2 && mode != 3 {; error "invalid enum for mode"; }
|
|
if ulx < 0 || ulx > 1024 {; error "ulx out of range"; }
|
|
if uly < 0 || uly > 1024 {; error "uly out of range"; }
|
|
if lrx < 0 || lrx > 1024 {; error "lrx out of range"; }
|
|
if lry < 0 || lry > 1024 {; error "lry out of range"; }
|
|
// setting lrx < ulx or lry < uly is probably allowed,
|
|
// though i don't know what it would do.
|
|
_g(0xED, (ulx << 14) | (uly << 2), (lrx << 14) | (lry << 2))
|
|
}
|
|
|
|
macro gSetPrimDepth(variable z, variable dz) {
|
|
// fixes the Z value to a constant for the following primitives.
|
|
// useful for 2.5D effects, maybe?
|
|
// TODO: what are the ranges?
|
|
_g(0xEE, 0, (z << 16) | d)
|
|
}
|
|
|
|
macro gRdpSetOtherMode(variable omodeH, variable omodeL) {
|
|
// like gSetOtherModeL and gSetOtherModeR combined,
|
|
// but always overwriting existing values.
|
|
// TODO: check enums
|
|
_g(0xEF, omodeH, omodeL)
|
|
}
|
|
|
|
macro gLoadTLUT(variable tile, variable count) { // TODO: rename?
|
|
// used for loading color palettes.
|
|
// TODO: document properly, check ranges.
|
|
if tile < 0 || tile > 7 {; error "tile out of range"; }
|
|
_g(0xF0, 0, (tile << 24) | ((count & 0x3FF) << 14))
|
|
}
|
|
|
|
macro gRdpHalf2(variable wordlo) {
|
|
// this command is used in other commands to carry additional information.
|
|
_g(0xF1, 0, wordlo)
|
|
}
|
|
|
|
macro gSetTileSize(variable tile, variable uls, variable ult, variable lrs, variable lrt) {
|
|
// TODO: document, check ranges
|
|
if tile < 0 || tile > 7 {; error "tile out of range"; }
|
|
_g(0xF2, (uls << 12) | ult, (tile << 24) | (lrs << 12) | lrt)
|
|
}
|
|
|
|
macro gLoadBlock(variable tile, variable uls, variable ult, variable texels, variable dxt) {
|
|
// TODO: document, check ranges
|
|
if tile < 0 || tile > 7 {; error "tile out of range"; }
|
|
_g(0xF3, (uls << 12) | ult, (tile << 24) | (texels << 12) | dxt)
|
|
}
|
|
|
|
macro gLoadTile(variable tile, variable uls, variable ult, variable lrs, variable lrt) {
|
|
// TODO: document, check ranges
|
|
if tile < 0 || tile > 7 {; error "tile out of range"; }
|
|
_g(0xF4, (uls << 12) | ult, (tile << 24) | (lrs << 12) | lrt)
|
|
}
|
|
|
|
macro gSetTile() {
|
|
// TODO
|
|
_g(0xF5, 0, 0)
|
|
}
|
|
|
|
macro gFillRect(variable ulx, variable uly, variable lrx, variable lry) {
|
|
// TODO: describe.
|
|
// NOTE: coordinates are inclusive.
|
|
if ulx < 0 || ulx > 1024 {; error "ulx out of range"; }
|
|
if uly < 0 || uly > 1024 {; error "uly out of range"; }
|
|
if lrx < 0 || lrx > 1024 {; error "lrx out of range"; }
|
|
if lry < 0 || lry > 1024 {; error "lry out of range"; }
|
|
_g(0xF6, (lrx << 14) | (lry << 2), (ulx << 14) | (uly << 2))
|
|
}
|
|
|
|
macro gSetFillColor(variable color) {
|
|
// TODO: describe.
|
|
_g(0xF7, 0, color)
|
|
}
|
|
|
|
macro gSetFogColor(variable r, variable g, variable b, variable a) {
|
|
// sets fog color. also used as a general-purpose color register for blending.
|
|
// always given as 8-bit values regardless of any color formats being used.
|
|
if r < 0 || r > 0xFF {; error "red out of range"; }
|
|
if g < 0 || g > 0xFF {; error "green out of range"; }
|
|
if b < 0 || b > 0xFF {; error "blue out of range"; }
|
|
if a < 0 || a > 0xFF {; error "alpha out of range"; }
|
|
_g(0xF8, 0, (r << 24) | (g << 16) | (b << 8) | a)
|
|
}
|
|
|
|
macro gSetBlendColor(variable r, variable g, variable b, variable a) {
|
|
// sets a general-purpose color register for blending.
|
|
// always given as 8-bit values regardless of any color formats being used.
|
|
if r < 0 || r > 0xFF {; error "red out of range"; }
|
|
if g < 0 || g > 0xFF {; error "green out of range"; }
|
|
if b < 0 || b > 0xFF {; error "blue out of range"; }
|
|
if a < 0 || a > 0xFF {; error "alpha out of range"; }
|
|
_g(0xF9, 0, (r << 24) | (g << 16) | (b << 8) | a)
|
|
}
|
|
|
|
macro gSetPrimColor(variable minlevel, variable lodfrac, variable r, variable g, variable b, variable a) {
|
|
// TODO: describe.
|
|
// always given as 8-bit values regardless of any color formats being used.
|
|
if minlevel < 0 || minlevel > 0xFF {; error "minlevel out of range"; }
|
|
if lodfrac < 0 || lodfrac > 0xFF {; error "minlevel out of range"; }
|
|
if r < 0 || r > 0xFF {; error "red out of range"; }
|
|
if g < 0 || g > 0xFF {; error "green out of range"; }
|
|
if b < 0 || b > 0xFF {; error "blue out of range"; }
|
|
if a < 0 || a > 0xFF {; error "alpha out of range"; }
|
|
_g(0xFA, (minlevel << 8) | lodfrac, (r << 24) | (g << 16) | (b << 8) | a)
|
|
}
|
|
|
|
macro gSetEnvColor(variable r, variable g, variable b, variable a) {
|
|
// sets environment color. also used as a general-purpose color register for blending.
|
|
if r < 0 || r > 0xFF {; error "red out of range"; }
|
|
if g < 0 || g > 0xFF {; error "green out of range"; }
|
|
if b < 0 || b > 0xFF {; error "blue out of range"; }
|
|
if a < 0 || a > 0xFF {; error "alpha out of range"; }
|
|
_g(0xFB, 0, (r << 24) | (g << 16) | (b << 8) | a)
|
|
}
|
|
|
|
macro gSetCombine(variable a0, variable b0, variable c0, variable d0, variable Aa0, variable Ab0, variable Ac0, variable Ad0, variable a1, variable b1, variable c1, variable d1, variable Aa1, variable Ab1, variable Ac1, variable Ad1) {
|
|
// TODO: describe.
|
|
if a0 != (a0 & 0xF) {; error "a0 out of range"; }
|
|
if c0 != (c0 & 0x1F) {; error "c0 out of range"; }
|
|
if Aa0 != (Aa0 & 0x7) {; error "Aa0 out of range"; }
|
|
if Ac0 != (Ac0 & 0x7) {; error "Ac0 out of range"; }
|
|
if a1 != (a1 & 0xF) {; error "a1 out of range"; }
|
|
if c1 != (c1 & 0x1F) {; error "c1 out of range"; }
|
|
if b0 != (b0 & 0xF) {; error "b0 out of range"; }
|
|
if b1 != (b1 & 0xF) {; error "b1 out of range"; }
|
|
if Aa1 != (Aa1 & 0x7) {; error "Aa1 out of range"; }
|
|
if Ac1 != (Ac1 & 0x7) {; error "Ac1 out of range"; }
|
|
if d0 != (d0 & 0x7) {; error "d0 out of range"; }
|
|
if Ab0 != (Ab0 & 0x7) {; error "Ab0 out of range"; }
|
|
if Ad0 != (Ad0 & 0x7) {; error "Ad0 out of range"; }
|
|
if d1 != (d1 & 0x7) {; error "d1 out of range"; }
|
|
if Ab1 != (Ab1 & 0x7) {; error "Ab1 out of range"; }
|
|
if Ad1 != (Ad1 & 0x7) {; error "Ad1 out of range"; }
|
|
variable upper((a0 << 20) | (c0 << 15) | (Aa0 << 12) | (Ac0 << 9) | (a1 << 5) | c1)
|
|
variable lower((b0 << 28) | (b1 << 24) | (Aa1 << 21) | (Ac1 << 18) | (d0 << 15) | (Ab0 << 12) | (Ad0 << 9) | (d1 << 6) | (Ab1 << 3) | Ad1)
|
|
_g(0xFC, upper, lower)
|
|
}
|
|
|
|
macro gSetTImage(variable fmt, variable size, variable width, variable imgaddr) {
|
|
// sets the location in RDRAM for storing a texture image.
|
|
// only required for the relevant copy operations.
|
|
//if imgaddr & 0x1FFFFFFF > 0x800000 {; error "address out of range"; }
|
|
if fmt < 0 || fmt > 4 {; error "invalid enum for fmt"; }
|
|
if size < 0 || size > 5 || size == 4 {; error "invalid enum for size"; }
|
|
if width < 1 || width > 1024 {; error "width out of range"; }
|
|
_g(0xFD, (fmt << 21) | (size << 19) | (width - 1), imgaddr)
|
|
}
|
|
|
|
macro gSetZImage(variable imgaddr) {
|
|
// sets the location in RDRAM for storing the Z-buffer.
|
|
// only one is needed, and the format is fixed: (14 + 2) bits per pixel.
|
|
//if imgaddr & 0x1FFFFFFF > 0x800000 {; error "address out of range"; }
|
|
_g(0xFE, 0, imgaddr)
|
|
}
|
|
|
|
macro gSetCImage(variable fmt, variable size, variable width, variable imgaddr) {
|
|
// sets the location in RDRAM for storing the color buffer.
|
|
// sometimes refered to as a framebuffer?
|
|
// do not set this to the same address as the VI color buffer
|
|
// or else you will run into issues and wonder why nothing is blitting.
|
|
// instead, allocate space for two color buffers and swap between them.
|
|
if fmt < 0 || fmt > 4 {; error "invalid enum for fmt"; }
|
|
if size < 0 || size > 5 || size == 4 {; error "invalid enum for size"; }
|
|
if width < 1 || width > 1024 {; error "width out of range"; }
|
|
_g(0xFF, (fmt << 21) | (size << 19) | (width - 1), imgaddr)
|
|
}
|
|
|
|
// from here-on it's just convenience.
|
|
|
|
macro gLoadSync() {; gRdpLoadSync(); }
|
|
macro gPipeSync() {; gRdpPipeSync(); }
|
|
macro gTileSync() {; gRdpTileSync(); }
|
|
macro gFullSync() {; gRdpFullSync(); }
|
|
macro gSetOtherMode(variable omodeH, variable omodeL) {; gRdpSetOtherMode(omodeH, omodeL); }
|
|
|
|
macro gVtx(variable vaddr, variable numv, variable vbidx) {; gVertex(vaddr, numv, vbidx); }
|
|
macro gModifyVtx(variable vbidx, variable where, variable val) {; gModifyVertex(vbidx, where, val); }
|
|
|
|
// TODO: just how useful is this really?
|
|
macro gTextureOff() {; gTexture(0xFFFF, 0xFFFF, 0, 0, 0); }
|
|
|
|
// possible idea for the future: keeping track of segment addresses in bass defines?
|
|
macro gSetSegment0(variable vaddr) {; gMoveWord(G_MW_SEGMENT, 0x0 * 4, vaddr); }
|
|
macro gSetSegment1(variable vaddr) {; gMoveWord(G_MW_SEGMENT, 0x1 * 4, vaddr); }
|
|
macro gSetSegment2(variable vaddr) {; gMoveWord(G_MW_SEGMENT, 0x2 * 4, vaddr); }
|
|
macro gSetSegment3(variable vaddr) {; gMoveWord(G_MW_SEGMENT, 0x3 * 4, vaddr); }
|
|
macro gSetSegment4(variable vaddr) {; gMoveWord(G_MW_SEGMENT, 0x4 * 4, vaddr); }
|
|
macro gSetSegment5(variable vaddr) {; gMoveWord(G_MW_SEGMENT, 0x5 * 4, vaddr); }
|
|
macro gSetSegment6(variable vaddr) {; gMoveWord(G_MW_SEGMENT, 0x6 * 4, vaddr); }
|
|
macro gSetSegment7(variable vaddr) {; gMoveWord(G_MW_SEGMENT, 0x7 * 4, vaddr); }
|
|
macro gSetSegment8(variable vaddr) {; gMoveWord(G_MW_SEGMENT, 0x8 * 4, vaddr); }
|
|
macro gSetSegment9(variable vaddr) {; gMoveWord(G_MW_SEGMENT, 0x9 * 4, vaddr); }
|
|
macro gSetSegmentA(variable vaddr) {; gMoveWord(G_MW_SEGMENT, 0xA * 4, vaddr); }
|
|
macro gSetSegmentB(variable vaddr) {; gMoveWord(G_MW_SEGMENT, 0xB * 4, vaddr); }
|
|
macro gSetSegmentC(variable vaddr) {; gMoveWord(G_MW_SEGMENT, 0xC * 4, vaddr); }
|
|
macro gSetSegmentD(variable vaddr) {; gMoveWord(G_MW_SEGMENT, 0xD * 4, vaddr); }
|
|
macro gSetSegmentE(variable vaddr) {; gMoveWord(G_MW_SEGMENT, 0xE * 4, vaddr); }
|
|
macro gSetSegmentF(variable vaddr) {; gMoveWord(G_MW_SEGMENT, 0xF * 4, vaddr); }
|
|
|
|
// TODO
|
|
macro gInit() {; }
|
|
|
|
macro gQuadTri(variable v0, variable v1, variable v2, variable v3) {
|
|
gTri2(v0, v1, v2, v2, v3, v0)
|
|
}
|
|
|
|
macro gClipRatio(variable n) {
|
|
// specifies the ratio between the clipping and scissoring boxes.
|
|
// Frustrum Ratio?
|
|
if n < 1 || n > 6 {; error "n out of range"; }
|
|
gMoveWord(G_MW_CLIP, G_MWO_CLIP_RNX, n)
|
|
gMoveWord(G_MW_CLIP, G_MWO_CLIP_RNY, n)
|
|
gMoveWord(G_MW_CLIP, G_MWO_CLIP_RPX, 0x10000 - n)
|
|
gMoveWord(G_MW_CLIP, G_MWO_CLIP_RPY, 0x10000 - n)
|
|
}
|
|
|
|
macro gClearZImage(variable width, variable height, variable imgaddr) {
|
|
gSetCImage(G_IM_FMT_RGBA, G_IM_SIZE_16, width, imgaddr)
|
|
gSetOtherModeH(20, 2, 0x00300000) // TODO: use enums
|
|
gSetOtherModeL(3, 1, 0x00000000) // TODO: use enums
|
|
gSetFillColor(0xFFFCFFFC)
|
|
gFillRect(0, 0, width - 1, height - 1)
|
|
}
|
|
|
|
macro gViewport(variable viewport) {
|
|
gMoveMemory(16, G_MV_VIEWPORT, 0, viewport)
|
|
}
|
|
|
|
macro gPerspNorm(variable norm) {
|
|
if norm < 1 || norm > 0xFFFF {; error "norm out of range"; }
|
|
gMoveWord(G_MW_PERSPNORM, 0, norm)
|
|
}
|
|
|
|
// a tool to figure out just what the heck raw gSetCombine commands are doing.
|
|
macro gDisasmCombine(variable upper, variable lower) {
|
|
if (upper >> 24) != 0xFC {; error "that's not a gSetCombine command!"; }
|
|
variable a0((upper >> 20) & 0xF)
|
|
variable c0((upper >> 15) & 0x1F)
|
|
variable Aa0((upper >> 12) & 0x7)
|
|
variable Ac0((upper >> 9) & 0x7)
|
|
variable a1((upper >> 5) & 0xF)
|
|
variable c1(upper & 0x1F)
|
|
variable b0((lower >> 28) & 0xF)
|
|
variable b1((lower >> 24) & 0xF)
|
|
variable Aa1((lower >> 21) & 0x7)
|
|
variable Ac1((lower >> 18) & 0x7)
|
|
variable d0((lower >> 15) & 0x7)
|
|
variable Ab0((lower >> 12) & 0x7)
|
|
variable Ad0((lower >> 9) & 0x7)
|
|
variable d1((lower >> 6) & 0x7)
|
|
variable Ab1((lower >> 3) & 0x7)
|
|
variable Ad1((lower >> 3) & 0x7)
|
|
print "gSetCombine(",a0,",",b0,",",c0,",",d0,",",Aa0,",",Ab0,",",Ac0,",",Ad0,", ",a1,",",b1,",",c1,",",d1,",",Aa1,",",Ab1,",",Ac1,",",Ad1,")\n"
|
|
}
|