From be6a0f82610cf6387ccf41b6fff709980cbbb921 Mon Sep 17 00:00:00 2001 From: Connor Olding Date: Thu, 30 Aug 2018 02:58:09 +0200 Subject: [PATCH] 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. --- bin/.gitignore | 1 + bin/common.boot.bin | Bin 0 -> 208 bytes dlist.asm | 352 +++++++++++++++++-------- inc/F3DEX2.inc | 619 ++++++++++++++++++++++++++++++++++++++++++++ inc/dlist.inc | 56 ++++ inc/kernel.inc | 2 +- inc/main.inc | 39 ++- inc/n64_sp.inc | 9 + kernel.asm | 10 +- main.asm | 65 +++-- 10 files changed, 996 insertions(+), 157 deletions(-) create mode 100644 bin/common.boot.bin create mode 100644 inc/F3DEX2.inc create mode 100644 inc/dlist.inc diff --git a/bin/.gitignore b/bin/.gitignore index 88f5125..64f15a6 100644 --- a/bin/.gitignore +++ b/bin/.gitignore @@ -3,3 +3,4 @@ # files without disassemblies yet: !6102.bin !F3DZEX.data.bin +!common.boot.bin diff --git a/bin/common.boot.bin b/bin/common.boot.bin new file mode 100644 index 0000000000000000000000000000000000000000..a6013bfbb47c8e1c980d84b857b846c575a173f7 GIT binary patch literal 208 zcmaKmF$%&!5Ji7B3)vK5w@_3FGH7GDf`#QLN3gQ@ATC;HkwfJuUcf83X5G|IF$0g` z{a-_`q_9K3fs@i+rE-A}fHwo)9jHBKA$~AU18vjA3x?qc8o}Nm%mI~fIq`!zflo=O tNo#a#-$6N|$}oGV&hTA> 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() diff --git a/inc/F3DEX2.inc b/inc/F3DEX2.inc new file mode 100644 index 0000000..67abb4c --- /dev/null +++ b/inc/F3DEX2.inc @@ -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" +} diff --git a/inc/dlist.inc b/inc/dlist.inc new file mode 100644 index 0000000..b734ff0 --- /dev/null +++ b/inc/dlist.inc @@ -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 +} diff --git a/inc/kernel.inc b/inc/kernel.inc index 3d43063..dce47c6 100644 --- a/inc/kernel.inc +++ b/inc/kernel.inc @@ -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) diff --git a/inc/main.inc b/inc/main.inc index b2dfd71..0470be9 100644 --- a/inc/main.inc +++ b/inc/main.inc @@ -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) diff --git a/inc/n64_sp.inc b/inc/n64_sp.inc index b758df1..b6ef3c7 100644 --- a/inc/n64_sp.inc +++ b/inc/n64_sp.inc @@ -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 +} diff --git a/kernel.asm b/kernel.asm index 6f4a10e..8d75d70 100644 --- a/kernel.asm +++ b/kernel.asm @@ -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 diff --git a/main.asm b/main.asm index c471551..421f6a6 100644 --- a/main.asm +++ b/main.asm @@ -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"