render a spinning cube using command macros

unfortunately i seem to have broken console compatibility along the way;
it dies after a few frames. i will have to fix this later.
for now, i just really need to commit something.
This commit is contained in:
Connor Olding 2018-08-30 02:58:09 +02:00
parent a95891f4d0
commit be6a0f8261
10 changed files with 996 additions and 157 deletions

1
bin/.gitignore vendored
View File

@ -3,3 +3,4 @@
# files without disassemblies yet:
!6102.bin
!F3DZEX.data.bin
!common.boot.bin

BIN
bin/common.boot.bin Normal file

Binary file not shown.

352
dlist.asm
View File

@ -1,116 +1,266 @@
include "inc/F3DEX2.inc"
include "inc/dlist.inc"
macro _g(variable c, variable a, variable b) {
WriteDL((c << 24) | a, b)
}
framecount:
dw 0
SmoothStep16:
// a0: s15.16?
// v0: s15.16
// v1: lower 16 bits
// t9 = abs(a0)
bgez a0,+
addu t9, r0, a0
subu t9, r0, a0
+
// t9 = triangle_wave(t9)
// note that every minimum and maximum value is repeated once
li t0, 0x1FFFF // period of 2
and t9, t0
lli t0, 0xFFFF
subu t9, t0, t9
bgez t9,+
subiu t1, r0, 1
subu t9, t1, t9
+
subu t9, t0, t9
// t8 = t9 * t9
multu t9, t9
mflo t8
srl t8, 16
// t7 = 3 - 2 * t9
sll t7, t9, 1 // times two
subu t7, r0, t7 // negate
li t0, 3 << 16
addu t7, t0
// t6 = t8 * t7
// instead do a u0.15 * u2.15 multiply to avoid overflow:
srl t8, 1
srl t7, 1
multu t8, t7
mflo t6
srl v0, t6, 14
sll v1, t6, 1
jr ra
nop
WriteDList:
// a0: pointer to receive F3DZEX instructions
// a1: use alt color buffer (boolean)
subiu sp, 0x20
sw ra, 0x10(sp)
sw s0, 0x14(sp)
sw s1, 0x18(sp)
sw s2, 0x1C(sp)
define dpos(0)
macro WriteDL(evaluate L, evaluate R) {
lui t0, ({L} >> 16) & 0xFFFF
lui t1, ({R} >> 16) & 0xFFFF
ori t0, {L} & 0xFFFF
ori t1, {R} & 0xFFFF
sw t0, {dpos}+0(a0)
sw t1, {dpos}+4(a0)
global evaluate dpos({dpos}+8)
if {dpos} >= 0x8000 {
error "much too much"
// FIXME: just add dpos to a0 and set dpos to 0 when this happens
}
}
// G_RDPPIPESYNC
WriteDL(0xE7000000, 0)
// G_TEXTURE (disable tile descriptor; dummy second argument)
WriteDL(0xD7000000, 0xFFFFFFFF)
// G_SETCOMBINE (too complicated to explain here...)
WriteDL(0xFCFFFFFF, 0xFFFE793C)
// G_RDPSETOTHERMODE (set higher flags, clear all lower flags)
// 0011 1000 0010 1100 0011 0000
// G_AD_DISABLE | G_CD_MAGICSQ | G_TC_FILT | G_TF_BILERP |
// G_TT_NONE | G_TL_TILE | G_TD_CLAMP | G_MDSFT_TEXTPERSP |
// G_CYC_FILL | G_PM_NPRIMITIVE
WriteDL(0xEF382C30, 0x00000000)
// G_GEOMETRYMODE
// set some bits (TODO: which?), clear none
WriteDL(0xD9000000, 0x00220405)
// G_SETSCISSOR coordinate order: (top, left), (right, bottom)
WriteDL(0xED000000 | (0 << 14) | (0 << 2), (WIDTH << 14) | (HEIGHT << 2))
// G_SETBLENDCOLOR
// sets alpha component to 8, everything else to 0
WriteDL(0xF9000000, 0x00000008)
// sets near- far-plane clipping? maybe?
// G_MOVEWORD, sets G_MW_CLIP+$0004
WriteDL(0xDB040004, 2)
// G_MOVEWORD, sets G_MW_CLIP+$000C
WriteDL(0xDB04000C, 2)
// G_MOVEWORD, sets G_MW_CLIP+$0014
WriteDL(0xDB040014, 0x10000 - 2)
// G_MOVEWORD, sets G_MW_CLIP+$001C
WriteDL(0xDB04001C, 0x10000 - 2)
// G_SETZIMG, set our z buffer
WriteDL(0xFE000000, VIDEO_Z_BUFFER)
// G_SETCIMG, set our z buffer as a color buffer so we can wipe it
WriteDL(0xFF100000 | (WIDTH - 1), VIDEO_Z_BUFFER)
WriteDL(0xE3000A01, 0x00300000) // G_SETOTHERMODE_H
WriteDL(0xE200001C, 0x00000000) // G_SETOTHERMODE_L
WriteDL(0xF7000000, 0xFFFCFFFC) // G_SETFILLCOLOR to default z value
// G_FILLRECT the whole z buffer
WriteDL(0xF6000000 | ((WIDTH - 1) << 14) | ((HEIGHT - 1) << 2), 0)
WriteDL(0xE7000000, 0) // G_RDPPIPESYNC
// G_SETCIMG, set our color buffer
if HICOLOR {
constant G_SETCIMB_UPPER_WORD(0xFF180000)
} else {
constant G_SETCIMB_UPPER_WORD(0xFF100000)
}
bnez a1,+
nop
WriteDL(G_SETCIMB_UPPER_WORD | (WIDTH - 1), VIDEO_C_BUFFER)
b DListDoneColorImage
la a1, VIDEO_C_IMAGE
j ++
nop
+
global evaluate dpos({dpos}-8) // move pos back so we can overwrite with alt
WriteDL(G_SETCIMB_UPPER_WORD | (WIDTH - 1), VIDEO_C_BUFFER_ALT)
DListDoneColorImage:
la a1, VIDEO_C_IMAGE_ALT
+
WriteDL(0xF8000000, 0x0A0A0A00) // G_SETFOGCOLOR
WriteDL(0xDB080000, 0x3E80C280) // set fog distance (float?)
WriteDL(0xE7000000, 0) // G_RDPPIPESYNC
or s0, a0, r0
or s1, a1, r0
// G_SETFILLCOLOR
if HICOLOR {
WriteDL(0xF7000000, 0x007FFFFF)
} else {
WriteDL(0xF7000000, 0x03FF03FF)
}
// move the object around based on framecount
WriteDL(0xE7000000, 0) // G_RDPPIPESYNC
la t8, framecount
lw t9, 0(t8)
// G_FILLRECT coordinate order: (right, bottom), (top, left)
// note that the coordinates are all inclusive!
WriteDL(0xF6000000 | (199 << 14) | (199 << 2), (100 << 14) | (100 << 2))
// G_RDPPIPESYNC
WriteDL(0xE7000000, 0)
// always finish it off by telling RDP to stop!
// G_RDPFULLSYNC
WriteDL(0xE9000000, 0)
// G_ENDDL
WriteDL(0xDF000000, 0)
jr ra
sll a0, t9, 16 + 7
srl a0, 15
jal SmoothStep16
nop
or s2, v0, r0
la t8, framecount
lw t9, 0(t8)
sll a0, t9, 16 + 7
srl a0, 15
jal SmoothStep16
subiu a0, 0x7FFF
subu s2, r0, s2
addiu s2, 0x7FFF
sll s2, 1
subu v0, r0, v0
addiu v0, 0x7FFF
sll v0, 1
// s2: cos-like
// v0: sin-like
la t8, view_mat1
sh s2, 0x20(t8) // xx lo
sra t9, s2, 16
sh t9, 0x00(t8) // xx hi
subu v0, r0, v0
sh v0, 0x24(t8) // xz lo
sra t9, v0, 16
sh t9, 0x04(t8) // xz hi
subu v0, r0, v0
sh v0, 0x30(t8) // zx lo
sra t9, v0, 16
sh t9, 0x10(t8) // zx hi
sh s2, 0x34(t8) // zz lo
sra t9, s2, 16
sh t9, 0x14(t8) // zz hi
or a0, s0, r0
or a1, s1, r0
// init
gPipeSync()
gSetSegment0(0) // set to 0 so that 00-prefixed addresses are absolute.
gTextureOff()
gSetCombine(15,15,31,4,7,7,7,4, 15,15,31,4,7,7,7,7)
gSetScissor(0, 0, 0, WIDTH, HEIGHT) // TODO: use mode enum
gSetBlendColor(0,0,0,8)
gClipRatio(2)
gSetZImage(VIDEO_Z_IMAGE)
// this overwrites our color image address so we must do it first:
gClearZImage(WIDTH, HEIGHT, VIDEO_Z_IMAGE)
gPipeSync()
WriteCB(G_SETCIMB_UPPER_WORD | (WIDTH - 1))
if HICOLOR {
gSetFillColor(0x444444FF) // dark gray
} else {
gSetFillColor(0x42114211) // dark gray
}
gFillRect(0, 0, WIDTH - 1, HEIGHT - 1)
gPipeSync()
if HICOLOR {
gSetFillColor(0xFFFFFFFF) // white
} else {
gSetFillColor(0xFFFFFFFF) // white
}
gPipeSync()
// note that all the coordinates are inclusive!
if HIRES {
gFillRect(312, 232, 327, 247)
} else {
gFillRect(156, 116, 163, 123)
}
gPipeSync()
gViewport(viewport)
gPerspNorm(0x41)
gMatrix(view_mat0, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_PROJECTION)
gMatrix(view_mat1, G_MTX_NOPUSH | G_MTX_MUL | G_MTX_PROJECTION)
gPipeSync()
gSetCombine(15,15,31,4,7,7,7,4, 15,15,31,4,7,7,7,7)
gSetOtherMode(0, 0)
gGeometryMode(0, G_ZBUFFER | G_SHADE | G_CULL_BACK | G_SHADING_SMOOTH)
gSetSegment6(model)
gMatrix(model_mat, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW)
gDisplayList((6 << 24) | MODEL_START)
// finish.
gFullSync()
gEndList()
la t8, framecount
lw t9, 0(t8)
addiu t9, 1
sw t9, 0(t8)
lw ra, 0x10(sp)
lw s0, 0x14(sp)
lw s1, 0x18(sp)
lw s2, 0x1C(sp)
jr ra
addiu sp, 0x20
print {dpos} / 8, "\n"
align(8)
viewport:
// note that the third parameters here affect the range of Z-buffering.
dh WIDTH/2*4, HEIGHT/2*4, 0x3FF, 0 // scale
dh WIDTH/2*4, HEIGHT/2*4, 0x000, 0 // translation
view_mat0:
dh 0, 0, 0, 0
dh 0, 1, 0, 0
dh 0, 0, 1, -1 // <- FOV?
dh 0, 0, 0, 0
dh 0xC000, 0, 0, 0 // 3/4 for a 4:3 aspect ratio
dh 0, 0, 0, 0
dh 0, 0, 0, 0
dh 0, 0, 0, 0
view_mat1:
// xx 0x00, xy 0x02, xz 0x04, xw 0x06
// yx 0x08, yy 0x0A, yz 0x0C, yw 0x0E
// zx 0x10, zy 0x12, zz 0x14, zw 0x16
// wx 0x18, wy 0x1A, wz 0x1C, ww 0x1E
dh 1, 0, 0, 0
dh 0, 1, 0, 0
dh 0, 0, 1, 0
dh 0, -44, -192, 1
dh 0, 0, 0, 0
dh 0, 0, 0, 0
dh 0, 0, 0, 0
dh 0, 0, 0, 0
model_mat:
MatObject(0,0,0, 0x0800)
identity:
MatEye()
macro _g(variable c, variable a, variable b) {
dw (c << 24) | a, b
}
model: // a colorful cube
constant S(0x400)
// TODO: write a macro for this struct
dh -S, -S, -S, 0, 0, 0, 0x0000, 0x00FF
dh -S, -S, +S, 0, 0, 0, 0x0000, 0xFFFF
dh -S, +S, -S, 0, 0, 0, 0x00FF, 0x00FF
dh -S, +S, +S, 0, 0, 0, 0x00FF, 0xFFFF
dh +S, -S, -S, 0, 0, 0, 0xFF00, 0x00FF
dh +S, -S, +S, 0, 0, 0, 0xFF00, 0xFFFF
dh +S, +S, -S, 0, 0, 0, 0xFFFF, 0x00FF
dh +S, +S, +S, 0, 0, 0, 0xFFFF, 0xFFFF
constant MODEL_START(pc() - model)
gPipeSync()
gVertex(0x06000000, 8, 0)
gQuadTri(0, 1, 3, 2)
gQuadTri(1, 5, 7, 3)
gQuadTri(5, 4, 6, 7)
gQuadTri(4, 0, 2, 6)
gQuadTri(4, 5, 1, 0)
gQuadTri(2, 3, 7, 6)
gEndList()

619
inc/F3DEX2.inc Normal file
View File

@ -0,0 +1,619 @@
// 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_SHADING_SMOOTH(1 << 21)
constant G_CLIPPING(1 << 23)
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) {
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"
}

56
inc/dlist.inc Normal file
View File

@ -0,0 +1,56 @@
if HICOLOR {
constant G_SETCIMB_UPPER_WORD(0xFF180000)
} else {
constant G_SETCIMB_UPPER_WORD(0xFF100000)
}
define dpos(0)
macro WriteDL(evaluate L, evaluate R) {
lui t0, ({L} >> 16) & 0xFFFF
lui t1, ({R} >> 16) & 0xFFFF
ori t0, {L} & 0xFFFF
ori t1, {R} & 0xFFFF
sw t0, {dpos}+0(a0)
sw t1, {dpos}+4(a0)
global evaluate dpos({dpos}+8)
if {dpos} >= 0x2000 {
addiu a0, 0x2000
global evaluate dpos({dpos}-0x2000)
}
}
macro WriteCB(evaluate L) {
lui t0, ({L} >> 16) & 0xFFFF
ori t0, {L} & 0xFFFF
sw t0, {dpos}+0(a0)
sw a1, {dpos}+4(a0)
global evaluate dpos({dpos}+8)
if {dpos} >= 0x2000 {
addiu a0, 0x2000
global evaluate dpos({dpos}-0x2000)
}
}
macro MatEye() {
dh 0x0001, 0x0000, 0x0000, 0x0000
dh 0x0000, 0x0001, 0x0000, 0x0000
dh 0x0000, 0x0000, 0x0001, 0x0000
dh 0x0000, 0x0000, 0x0000, 0x0001
dh 0x0000, 0x0000, 0x0000, 0x0000
dh 0x0000, 0x0000, 0x0000, 0x0000
dh 0x0000, 0x0000, 0x0000, 0x0000
dh 0x0000, 0x0000, 0x0000, 0x0000
}
macro MatObject(variable x, variable y, variable z, variable scale) {
dh scale >> 16, 0, 0, 0
dh 0, scale >> 16, 0, 0
dh 0, 0, scale >> 16, 0
dh x >> 16, y >> 16, z >> 16, 1
dh scale, 0, 0, 0
dh 0, scale, 0, 0
dh 0, 0, scale, 0
dh x, y, z, 0
}

View File

@ -21,7 +21,7 @@ constant K_STACK(0x0C00 - 0x10)
constant K_XXD(0x0C00) // size: 0x400 (any larger and you overwrite kernel code)
// note this gets subtracted by 0x10 and the stack grows *backwards.*
constant K_STACK_INIT_BASE(0x803F)
constant K_STACK_INIT(0x800F9000) // size: a measly 36 KiB
// internal interrupt enum: (0 means no known interrupt/exception)
constant K_INT_TLB_REFILL(1)

View File

@ -1,5 +1,7 @@
// settings:
constant K_DEBUG(0) // slows down interrupt handling to enable debug routines
constant HIRES(1)
constant HICOLOR(1)
constant MAIN_BASE(0x800E)
constant MAIN_COUNTS(0x0010)
@ -9,23 +11,32 @@ constant MAIN_DLIST(0x1000)
constant MAIN_DLIST_SIZE(0xF000)
constant MAIN_DLIST_JUMPER(MAIN_DLIST - 0xA8)
// video settings
constant WIDTH(640)
constant HEIGHT(480)
constant HICOLOR(1)
if HIRES {
constant WIDTH(640)
constant HEIGHT(480)
} else {
constant WIDTH(320)
constant HEIGHT(240)
}
constant VIDEO_C_BUFFER_SIZE(WIDTH * HEIGHT * (HICOLOR * 2 + 2))
constant VIDEO_Z_BUFFER_SIZE(WIDTH * HEIGHT * 2)
constant VIDEO_SOMETHING_SIZE(0x18000)
constant VIDEO_STACK_SIZE(0x400)
constant VIDEO_YIELD_SIZE(0xC00)
constant TASK_YIELDED(0x0001)
constant TASK_DP_WAIT(0x0002)
constant TASK_LOADABLE(0x0004)
constant TASK_SP_ONLY(0x0008)
constant VIDEO_C_IMAGE_SIZE(WIDTH * HEIGHT * (HICOLOR * 2 + 2))
constant VIDEO_Z_IMAGE_SIZE(WIDTH * HEIGHT * 2)
constant VIDEO_OUTPUT_SIZE(0x18000) // technically a buffer?
constant VIDEO_STACK_SIZE(0x400) // used for dlist calls, pushing matrices, etc?
constant VIDEO_YIELD_SIZE(0xC00) // stores and restores DMEM.
// NOTE: these should be row-aligned to avoid caching issues and DMA issues.
constant VIDEO_END(0x80400000)
constant VIDEO_C_BUFFER(VIDEO_END - VIDEO_C_BUFFER_SIZE)
constant VIDEO_C_BUFFER_ALT(VIDEO_C_BUFFER - VIDEO_C_BUFFER_SIZE)
constant VIDEO_Z_BUFFER(VIDEO_C_BUFFER_ALT - VIDEO_Z_BUFFER_SIZE)
constant VIDEO_SOMETHING(VIDEO_Z_BUFFER - VIDEO_SOMETHING_SIZE)
constant VIDEO_STACK(VIDEO_SOMETHING - VIDEO_STACK_SIZE)
constant VIDEO_C_IMAGE(VIDEO_END - VIDEO_C_IMAGE_SIZE)
constant VIDEO_C_IMAGE_ALT(VIDEO_C_IMAGE - VIDEO_C_IMAGE_SIZE)
constant VIDEO_Z_IMAGE(VIDEO_C_IMAGE_ALT - VIDEO_Z_IMAGE_SIZE)
constant VIDEO_OUTPUT(VIDEO_Z_IMAGE - VIDEO_OUTPUT_SIZE)
constant VIDEO_STACK(VIDEO_OUTPUT - VIDEO_STACK_SIZE)
constant VIDEO_YIELD(VIDEO_STACK - VIDEO_YIELD_SIZE)
constant VIDEO_START(VIDEO_YIELD)

View File

@ -81,3 +81,12 @@ macro SP_BUSY_WAIT() {
bnez t0,-
nop
}
macro SP_WAIT() {
lui a0, SP_BASE
-
lw t0, SP_STATUS(a0)
andi t0, 1
beqz t0,-
nop
}

View File

@ -21,12 +21,12 @@ Start:
bne t1, t2,-
addiu t0, t0, 0x10
// do whatever this does.
li a0, 0x01000800
// flush denormals to 0 and enable invalid operations
li a0, 0x01000800 // TODO: use flag constants
ctc1 a0, CP1_FCSR
//
// is this just anti-gameshark BS?
lui a0, 0x0490
mtc0 a0, CP0_WatchLo // is this just anti-gameshark BS?
mtc0 a0, CP0_WatchLo
// initialize the N64 so it doesn't immediately die.
SI_WAIT()
@ -74,7 +74,7 @@ Start:
// SP defaults to RSP instruction memory: 0xA4001FF0
// we can do better than that.
lui sp, K_STACK_INIT_BASE
li sp, K_STACK_INIT
// SP should always be 8-byte aligned
// so that SD and LD instructions don't fail on it.
// we also need 4 empty words for storing

View File

@ -9,7 +9,7 @@ include "inc/main.inc"
include "inc/kernel.inc"
output "test.z64", create
fill 1052672 // Set ROM Size
fill 1052672 // ROM size
origin 0x00000000
base 0x80000000
@ -17,7 +17,7 @@ base 0x80000000
include "header.asm"
insert "bin/6102.bin"
if origin() != 0x1000 {
error "bad header or bootcode; combined sized should be exactly 0x1000"
error "bad header or bootcode; combined size should be exactly 0x1000"
}
include "kernel.asm"
@ -28,13 +28,15 @@ Main:
mfc0 t0, CP0_Count
sw t0, MAIN_COUNTS+0(s0)
if 0 {
// decompress our picture
la a0, LZ_BAKU + 4
lw a3, -4(a0) // load uncompressed size from the file itself
li a1, LZ_BAKU.size - 4
li a2, VIDEO_C_BUFFER
// jal LzDecomp
li a2, VIDEO_C_IMAGE
jal LzDecomp
nop
}
mfc0 t0, CP0_Count
nop; nop; nop; nop
@ -55,7 +57,7 @@ Main:
WriteString(KS_Newline)
Test3D:
// write the jump to our actual instructions
// write the jump to our actual commands
lui a0, MAIN_BASE
lui t0, 0xDE01 // jump (no push)
ori t1, a0, MAIN_DLIST
@ -82,13 +84,7 @@ Start3D:
lli t0, SP_SG2_CLR | SP_SG1_CLR | SP_SG0_CLR | SP_IOB_SET
sw t0, SP_STATUS(a0)
// wait
lui a0, SP_BASE
-
lw t0, SP_STATUS(a0)
andi t0, 1
beqz t0,-
nop
SP_WAIT()
// set RSP PC to IMEM+$0
lui a0, SP_PC_BASE
@ -115,20 +111,14 @@ Start3D:
SetIntMask()
MainLoop:
// wait on SP
lui a0, SP_BASE
-
lw t0, SP_STATUS(a0)
andi t0, 1
beqz t0,-
nop
SP_WAIT()
// wait on VI too
-
lui t0, VI_BASE
lw t0, VI_V_CURRENT_LINE(t0)
// until line <= 10
sltiu t0, 11
// until line <= 3
sltiu t0, 4 // larger values seem to die on N64 (cen64 has no problem)
beqz t0,-
nop
@ -139,12 +129,12 @@ MainLoop:
beqz s1, SwitchToAlt
nop
SwitchToMain:
la t0, VIDEO_C_BUFFER_ALT
la t0, VIDEO_C_IMAGE_ALT
sw t0, VI_ORIGIN(a0)
j Start3D
lli s1, 0
SwitchToAlt:
la t0, VIDEO_C_BUFFER
la t0, VIDEO_C_IMAGE
sw t0, VI_ORIGIN(a0)
j Start3D
lli s1, 1
@ -153,9 +143,9 @@ KSL(SNewFrame, "next frame")
SetupScreen:
if HICOLOR {
ScreenNTSC(WIDTH, HEIGHT, BPP32|INTERLACE|AA_MODE_2, VIDEO_C_BUFFER | UNCACHED)
ScreenNTSC(WIDTH, HEIGHT, BPP32|INTERLACE|AA_MODE_2, VIDEO_C_IMAGE | UNCACHED)
} else {
ScreenNTSC(WIDTH, HEIGHT, BPP16|AA_MODE_2, VIDEO_C_BUFFER | UNCACHED)
ScreenNTSC(WIDTH, HEIGHT, BPP16|AA_MODE_2, VIDEO_C_IMAGE | UNCACHED)
}
jr ra
nop
@ -166,9 +156,9 @@ PushVideoTask:
sw ra, 0x10(sp)
lli t0, 1 // mode: video
lli t1, 2 // flags: ???
li t2, F3DZEX_BOOT // does not need masking for some reason
li t3, F3DZEX_BOOT.size
lli t1, TASK_DP_WAIT // flags
li t2, UCODE_BOOT // does not need masking (not actually used?)
li t3, UCODE_BOOT.size
li t4, F3DZEX_IMEM & ADDR_MASK
li t5, F3DZEX_IMEM.size
li t6, F3DZEX_DMEM & ADDR_MASK
@ -181,10 +171,12 @@ PushVideoTask:
sw t5, 0x14(a0)
sw t6, 0x18(a0)
sw t7, 0x1C(a0)
li t0, VIDEO_STACK & ADDR_MASK // used for DList calls and returns?
li t1, VIDEO_STACK_SIZE
li t2, VIDEO_SOMETHING & ADDR_MASK
li t3, (VIDEO_SOMETHING & ADDR_MASK) + VIDEO_SOMETHING_SIZE // end pointer (not size!)
li t2, VIDEO_OUTPUT & ADDR_MASK
// most commercial games re-use the yield pointer, so i assume it's fine:
li t3, VIDEO_YIELD & ADDR_MASK // stores output buffer size
li t4, ((MAIN_BASE << 16) | MAIN_DLIST_JUMPER) & ADDR_MASK // initial DList
lli t5, 8 // size of one jump command. this is ignored and 0xA8 is used instead
li t6, VIDEO_YIELD & ADDR_MASK
@ -223,8 +215,8 @@ PushRSPTask:
nop
LoadRSPBoot:
la t2, F3DZEX_BOOT & ADDR_MASK
li t3, F3DZEX_BOOT.size
la t2, UCODE_BOOT & ADDR_MASK
li t3, UCODE_BOOT.size
subiu t3, t3, 1 // DMA quirk
SP_DMA_WAIT() // clobbers t0, t5
la t1, 0xA4001000
@ -237,11 +229,12 @@ LoadRSPBoot:
include "lzss.baku.unsafe.asm"
include "dlist.asm"
align(16); insert F3DZEX_BOOT, "bin/F3DZEX2.boot.bin"
align(16); insert F3DZEX_DMEM, "bin/F3DZEX2.data.bin"
align(16); insert UCODE_BOOT, "bin/common.boot.bin"
align(16); insert F3DZEX_IMEM, "bin/F3DZEX2.bin"
align(16); insert FONT, "res/dwarf.1bpp"
align(16); insert LZ_BAKU, "res/Image.baku.lzss"
align(16); insert F3DZEX_DMEM, "bin/F3DZEX2.data.bin"
//align(16); insert FONT, "res/dwarf.1bpp"
//align(16); insert LZ_BAKU, "res/Image.baku.lzss"
if pc() > (MAIN_BASE << 16) {
error "ran out of memory for code and data"