From f2c5c7a1203799eeaf04ec5df1438d6ffba3ef19 Mon Sep 17 00:00:00 2001 From: water111 Date: Sun, 30 Aug 2026 17:45:42 -0700 Subject: [PATCH] up to sync-info --- decompiler/config/jak1/demacro.jsonc | 43 + game/CMakeLists.txt | 2 - game/mips2c/jak1_functions/tfrag.cpp | 912 ------ game/mips2c/jak1_functions/tie_methods.cpp | 1962 ------------ game/mips2c/mips2c_table.cpp | 10 +- goal_src/jak1/engine/dma/dma-buffer.gc | 14 +- goal_src/jak1/engine/gfx/shrub/shrub-work.gc | 35 +- .../jak1/engine/gfx/shrub/shrubbery-ee-asm.gc | 1988 ++++++++++++ goal_src/jak1/engine/gfx/shrub/shrubbery.gc | 2080 +------------ .../jak1/engine/gfx/tfrag/tfrag-methods.gc | 1176 ++------ goal_src/jak1/engine/gfx/tfrag/tfrag-near.gc | 4 +- goal_src/jak1/engine/gfx/tfrag/tfrag-work.gc | 2 +- goal_src/jak1/engine/gfx/tfrag/tfrag.gc | 2562 ++++++++-------- .../jak1/engine/gfx/tie/tie-methods-ee-asm.gc | 2261 ++++++++++++++ goal_src/jak1/engine/gfx/tie/tie-methods.gc | 2668 +---------------- goal_src/jak1/engine/gfx/tie/tie-near.gc | 2 +- goal_src/jak1/engine/gfx/tie/tie-work.gc | 31 - goal_src/jak1/engine/gfx/tie/tie.gc | 137 +- goal_src/jak1/engine/util/sync-info.gc | 69 +- test/common/test_demacro.cpp | 51 + 20 files changed, 6144 insertions(+), 9865 deletions(-) delete mode 100644 game/mips2c/jak1_functions/tfrag.cpp delete mode 100644 game/mips2c/jak1_functions/tie_methods.cpp create mode 100644 goal_src/jak1/engine/gfx/shrub/shrubbery-ee-asm.gc create mode 100644 goal_src/jak1/engine/gfx/tie/tie-methods-ee-asm.gc diff --git a/decompiler/config/jak1/demacro.jsonc b/decompiler/config/jak1/demacro.jsonc index ede16dbf96..adeb1ff370 100644 --- a/decompiler/config/jak1/demacro.jsonc +++ b/decompiler/config/jak1/demacro.jsonc @@ -305,6 +305,14 @@ "rewrite": "(dma-buffer-add-cnt-vif2 $buf 0 $vif0 $vif1)" }, + { + // Explicit transfer sizes retain a qwc field in the dma-tag constructor. + "name": "dma-buffer-add-cnt-vif2-qwc-object-packet", + "match": + "(let* (($alias $buf) ($packet (the-as object (-> $alias base)))) (set! (-> (the-as dma-packet $packet) dma) (new 'static 'dma-tag :qwc $qwc :id (dma-tag-id cnt))) (set! (-> (the-as dma-packet $packet) vif0) $vif0) (set! (-> (the-as dma-packet $packet) vif1) $vif1) (set! (-> $alias base) (&+ (the-as pointer $packet) 16)))", + "rewrite": + "(dma-buffer-add-cnt-vif2 $buf $qwc $vif0 $vif1)" + }, { "name": "dma-buffer-add-cnt-vif2-typed-packet", "match": @@ -312,6 +320,41 @@ "rewrite": "(dma-buffer-add-cnt-vif2 $buf 0 $vif0 $vif1)" }, + { + "name": "dma-buffer-add-cnt-vif2-qwc-typed-packet", + "match": + "(let* (($alias $buf) ($packet (the-as dma-packet (-> $alias base)))) (set! (-> $packet dma) (new 'static 'dma-tag :qwc $qwc :id (dma-tag-id cnt))) (set! (-> $packet vif0) $vif0) (set! (-> $packet vif1) $vif1) (set! (-> $alias base) (&+ (the-as pointer $packet) 16)))", + "rewrite": + "(dma-buffer-add-cnt-vif2 $buf $qwc $vif0 $vif1)" + }, + { + "name": "dma-buffer-add-next-vif2-object-packet", + "match": + "(let (($packet (the-as object (-> $buf base)))) (set! (-> (the-as dma-packet $packet) dma) (new 'static 'dma-tag :id (dma-tag-id next) :addr $addr)) (set! (-> (the-as dma-packet $packet) vif0) $vif0) (set! (-> (the-as dma-packet $packet) vif1) $vif1) (set! (-> $buf base) (&+ (the-as pointer $packet) 16)))", + "rewrite": + "(dma-buffer-add-next-vif2 $buf 0 $addr $vif0 $vif1)" + }, + { + "name": "dma-buffer-add-next-vif2-no-address-object-packet", + "match": + "(let (($packet (the-as object (-> $buf base)))) (set! (-> (the-as dma-packet $packet) dma) (new 'static 'dma-tag :id (dma-tag-id next))) (set! (-> (the-as dma-packet $packet) vif0) $vif0) (set! (-> (the-as dma-packet $packet) vif1) $vif1) (set! (-> $buf base) (&+ (the-as pointer $packet) 16)))", + "rewrite": + "(dma-buffer-add-next-vif2 $buf 0 0 $vif0 $vif1)" + }, + { + "name": "dma-buffer-add-next-vif2-typed-packet", + "match": + "(let (($packet (the-as dma-packet (-> $buf base)))) (set! (-> $packet dma) (new 'static 'dma-tag :id (dma-tag-id next) :addr $addr)) (set! (-> $packet vif0) $vif0) (set! (-> $packet vif1) $vif1) (set! (-> $buf base) (&+ (the-as pointer $packet) 16)))", + "rewrite": + "(dma-buffer-add-next-vif2 $buf 0 $addr $vif0 $vif1)" + }, + { + "name": "dma-buffer-add-next-vif2-no-address-typed-packet", + "match": + "(let (($packet (the-as dma-packet (-> $buf base)))) (set! (-> $packet dma) (new 'static 'dma-tag :id (dma-tag-id next))) (set! (-> $packet vif0) $vif0) (set! (-> $packet vif1) $vif1) (set! (-> $buf base) (&+ (the-as pointer $packet) 16)))", + "rewrite": + "(dma-buffer-add-next-vif2 $buf 0 0 $vif0 $vif1)" + }, { "name": "dma-buffer-add-gif-tag-object-packet", "match": diff --git a/game/CMakeLists.txt b/game/CMakeLists.txt index 80b23c315b..e0fcfb9bba 100644 --- a/game/CMakeLists.txt +++ b/game/CMakeLists.txt @@ -172,8 +172,6 @@ set(RUNTIME_SOURCE mips2c/jak1_functions/sparticle.cpp mips2c/jak1_functions/test_func.cpp mips2c/jak1_functions/texture.cpp - mips2c/jak1_functions/tfrag.cpp - mips2c/jak1_functions/tie_methods.cpp mips2c/jak1_functions/time_of_day.cpp mips2c/jak2_functions/bones.cpp mips2c/jak2_functions/collide_cache.cpp diff --git a/game/mips2c/jak1_functions/tfrag.cpp b/game/mips2c/jak1_functions/tfrag.cpp deleted file mode 100644 index 6baf04440a..0000000000 --- a/game/mips2c/jak1_functions/tfrag.cpp +++ /dev/null @@ -1,912 +0,0 @@ - -//--------------------------MIPS2C--------------------- -#include "game/kernel/jak1/kscheme.h" -#include "game/mips2c/mips2c_private.h" -using namespace jak1; -namespace Mips2C::jak1 { -namespace draw_inline_array_tfrag { -struct Cache { - void* tfrag_work; // *tfrag-work* - void* fake_scratchpad_data; // *fake-scratchpad-data* - void* transform_regs; -} cache; - -// t0 = tfrag work -// t8 = tfrags -// t9 = tfrag count -// clang-format off -u64 execute(void* ctxt) { - auto* c = (ExecutionContext*)ctxt; - load_vfs_from_tf_regs(cache.transform_regs, c); - bool bc = false; - c->daddiu(sp, sp, -128); // daddiu sp, sp, -128 - c->sd(ra, 0, sp); // sd ra, 0(sp) - c->sq(s0, 16, sp); // sq s0, 16(sp) - c->sq(s1, 32, sp); // sq s1, 32(sp) - c->sq(s2, 48, sp); // sq s2, 48(sp) - c->sq(s3, 64, sp); // sq s3, 64(sp) - c->sq(s4, 80, sp); // sq s4, 80(sp) - c->sq(s5, 96, sp); // sq s5, 96(sp) - c->sq(gp, 112, sp); // sq gp, 112(sp) - c->lui(t2, 5120); // lui t2, 5120 = (0x14000000) - c->lw(v1, 4, a3); // lw v1, 4(a3) - c->lui(t3, 4096); // lui t3, 4096 = (0x10000000) - c->lui(t1, 4096); // lui t1, 4096 = (0x10000000) - // Unknown instr: sync.l - // Unknown instr: cache dxwbin v1, 0 - // Unknown instr: sync.l - // Unknown instr: cache dxwbin v1, 1 - // Unknown instr: sync.l - c->load_symbol(t0, cache.tfrag_work); // lw t0, *tfrag-work*(s7) - c->ori(t4, t3, 54272); // ori t4, t3, 54272 = (0x1000D400) SPR TO - c->ori(t1, t1, 53248); // ori t1, t1, 53248 = (0x1000D000) SPR FROM - - // patched access to scratchpad - // c->lui(t5, 28672); // lui t5, 28672 = (0x70000000) - get_fake_spad_addr(t5, cache.fake_scratchpad_data, 0, c); - - c->lqc2(vf3, 80, t0); // lqc2 vf3, 80(t0) - c->sw(a3, 176, t0); // sw a3, 176(t0) - c->ori(a3, t5, 2064); // ori a3, t5, 2064 - c->addiu(t3, r0, 0); // addiu t3, r0, 0 - c->ori(t5, t5, 1040); // ori t5, t5, 1040 - c->vmax_bc(DEST::xyzw, BC::w, vf1, vf0, vf0); // vmaxw.xyzw vf1, vf0, vf0 - c->lh(t7, 0, a0); // lh t7, 0(a0) - c->lqc2(vf4, 96, t0); // lqc2 vf4, 96(t0) - c->addiu(a1, a1, -4); // addiu a1, a1, -4 - c->addiu(t6, r0, 0); // addiu t6, r0, 0 - c->mov64(ra, a3); // or ra, a3, r0 - - block_1: - bc = c->sgpr64(t7) != 0; // bne t7, r0, L42 - // nop // sll r0, r0, 0 - if (bc) {goto block_4;} // branch non-likely - - c->addiu(a0, a0, 2); // addiu a0, a0, 2 - c->addiu(a1, a1, 1024); // addiu a1, a1, 1024 - c->daddiu(a2, a2, -16); // daddiu a2, a2, -16 - c->lh(t7, 0, a0); // lh t7, 0(a0) - bc = ((s64)c->sgpr64(a2)) <= 0; // blez a2, L69 - // nop // sll r0, r0, 0 - if (bc) {goto block_55;} // branch non-likely - - //beq r0, r0, L41 // beq r0, r0, L41 - // nop // sll r0, r0, 0 - goto block_1; // branch always - - - block_4: - - // this block is just waiting for any in-progress SPR TO's to end - // we can just skip it. - /* - c->lw(t7, 0, t4); // lw t7, 0(t4) - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - c->andi(t7, t7, 256); // andi t7, t7, 256 - // nop // sll r0, r0, 0 - bc = c->sgpr64(t7) != 0; // bne t7, r0, L42 - // nop // sll r0, r0, 0 - if (bc) {goto block_4;} // branch non-likely - */ - - // this is setting up a scratchpad TO transfer. - { - // set MADR - //c->sw(a1, 16, t4); // sw a1, 16(t4) - u32 madr = c->sgpr64(a1); - - c->xori(t7, t5, 1024); // xori t7, t5, 1024 - - // set SADR - //c->sw(t7, 128, t4); // sw t7, 128(t4) - u32 sadr = c->sgpr64(t7); - - c->addiu(t7, r0, 64); // addiu t7, r0, 64 - - // set QWC - //c->sw(t7, 32, t4); // sw t7, 32(t4) - u32 qwc = c->sgpr64(t7); - - c->addiu(t7, r0, 256); // addiu t7, r0, 256 - - // GO! - //c->sw(t7, 0, t4); // sw t7, 0(t4) - spad_to_dma(cache.fake_scratchpad_data, madr, sadr, qwc); - // nop // sll r0, r0, 0 - } - - - block_6: - c->mov64(gp, a0); // or gp, a0, r0 - // fprintf(stderr, "block_6: gp = 0x%lx\n", c->sgpr64(gp)); - c->xori(t5, t5, 1024); // xori t5, t5, 1024 - c->daddiu(a0, a0, 2); // daddiu a0, a0, 2 - c->mov64(t9, a0); // or t9, a0, r0 - c->mov64(t8, t5); // or t8, t5, r0 - c->daddiu(t7, a2, -16); // daddiu t7, a2, -16 - bc = ((s64)c->sgpr64(t7)) > 0; // bgtz t7, L45 - c->lh(t7, 0, a0); // lh t7, 0(a0) - if (bc) {goto block_10;} // branch non-likely - - //beq r0, r0, L48 // beq r0, r0, L48 - // nop // sll r0, r0, 0 - goto block_14; // branch always - - - block_8: - c->daddiu(a2, a2, -16); // daddiu a2, a2, -16 - c->addiu(a0, a0, 2); // addiu a0, a0, 2 - bc = ((s64)c->sgpr64(a2)) <= 0; // blez a2, L48 - c->lh(t7, 0, a0); // lh t7, 0(a0) - if (bc) {goto block_14;} // branch non-likely - - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - - block_10: - bc = c->sgpr64(t7) == 0; // beq t7, r0, L44 - c->addiu(a1, a1, 1024); // addiu a1, a1, 1024 - if (bc) {goto block_8;} // branch non-likely - - - // this is waiting on spad transfer and incrementing wait counts - // block_11: - /* - c->lw(t7, 0, t4); // lw t7, 0(t4) - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - c->andi(t7, t7, 256); // andi t7, t7, 256 - // nop // sll r0, r0, 0 - bc = c->sgpr64(t7) == 0; // beq t7, r0, L47 - // nop // sll r0, r0, 0 - if (bc) {goto block_13;} // branch non-likely - - // nop // sll r0, r0, 0 - c->lw(t7, 188, t0); // lw t7, 188(t0) - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - c->daddiu(t7, t7, 1); // daddiu t7, t7, 1 - // nop // sll r0, r0, 0 - c->sw(t7, 188, t0); // sw t7, 188(t0) - //beq r0, r0, L46 // beq r0, r0, L46 - // nop // sll r0, r0, 0 - goto block_11; // branch always - */ - - - // tfrag bank loop? - // block_13: - { - //c->sw(a1, 16, t4); // sw a1, 16(t4) - u32 madr = c->sgpr64(a1); - c->xori(t7, t5, 1024); // xori t7, t5, 1024 - //c->sw(t7, 128, t4); // sw t7, 128(t4) - u32 sadr = c->sgpr64(t7); - c->addiu(t7, r0, 64); // addiu t7, r0, 64 - //c->sw(t7, 32, t4); // sw t7, 32(t4) - u32 qwc = c->sgpr64(t7); - c->addiu(t7, r0, 256); // addiu t7, r0, 256 - //beq r0, r0, L49 // beq r0, r0, L49 - //c->sw(t7, 0, t4); // sw t7, 0(t4) - spad_to_dma(cache.fake_scratchpad_data, madr, sadr, qwc); - } - goto block_16; // branch always - - - block_14: - /* - c->lw(t7, 0, t4); // lw t7, 0(t4) - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - c->andi(t7, t7, 256); // andi t7, t7, 256 - // nop // sll r0, r0, 0 - bc = c->sgpr64(t7) == 0; // beq t7, r0, L49 - // nop // sll r0, r0, 0 - if (bc) {goto block_16;} // branch non-likely - - // nop // sll r0, r0, 0 - c->lw(t7, 188, t0); // lw t7, 188(t0) - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - c->daddiu(t7, t7, 1); // daddiu t7, t7, 1 - // nop // sll r0, r0, 0 - c->sw(t7, 188, t0); // sw t7, 188(t0) - //beq r0, r0, L48 // beq r0, r0, L48 - // nop // sll r0, r0, 0 - goto block_14; // branch always - */ - - - block_16: - c->lb(t7, 0, gp); // lb t7, 0(gp) - c->addiu(gp, gp, 1); // addiu gp, gp, 1 - // nop // sll r0, r0, 0 - c->sw(gp, 160, t0); // sw gp, 160(t0) - bc = c->sgpr64(t7) != 0; // bne t7, r0, L50 - c->sw(t9, 164, t0); // sw t9, 164(t0) - if (bc) {goto block_18;} // branch non-likely - - c->daddiu(a2, a2, -8); // daddiu a2, a2, -8 - c->addiu(t8, t8, 512); // addiu t8, t8, 512 - //beq r0, r0, L65 // beq r0, r0, L65 - // nop // sll r0, r0, 0 - goto block_47; // branch always - - - block_18: - c->addiu(t9, r0, 128); // addiu t9, r0, 128 - c->lqc2(vf2, 16, t8); // lqc2 vf2, 16(t8) - - block_19: - c->daddiu(gp, t6, -124); // daddiu gp, t6, -124 - // fprintf(stderr, "block_19: gp = 0x%lx\n", c->sgpr64(gp)); - // nop // sll r0, r0, 0 - bc = ((s64)c->sgpr64(gp)) <= 0; // blez gp, L54 - // nop // sll r0, r0, 0 - if (bc) {goto block_23;} // branch non-likely - - - // block_20: - /* - c->lw(ra, 0, t1); // lw ra, 0(t1) - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - c->andi(ra, ra, 256); // andi ra, ra, 256 - // nop // sll r0, r0, 0 - bc = c->sgpr64(ra) == 0; // beq ra, r0, L53 - // nop // sll r0, r0, 0 - if (bc) {goto block_22;} // branch non-likely - - // nop // sll r0, r0, 0 - c->lw(ra, 184, t0); // lw ra, 184(t0) - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - c->daddiu(ra, ra, 1); // daddiu ra, ra, 1 - // nop // sll r0, r0, 0 - c->sw(ra, 184, t0); // sw ra, 184(t0) - //beq r0, r0, L52 // beq r0, r0, L52 - // nop // sll r0, r0, 0 - goto block_20; // branch always - */ - - - // block_22: - { - //c->sw(a3, 128, t1); // sw a3, 128(t1) - u32 sadr = c->sgpr64(a3); - c->xori(a3, a3, 6144); // xori a3, a3, 6144 - //c->sw(v1, 16, t1); // sw v1, 16(t1) - u32 madr = c->sgpr64(v1); - c->sll(ra, t6, 4); // sll ra, t6, 4 - c->addu(v1, v1, ra); // addu v1, v1, ra - c->mov64(ra, a3); // or ra, a3, r0 - //c->sw(t6, 32, t1); // sw t6, 32(t1) - u32 qwc = c->sgpr64(t6); - c->addiu(t6, r0, 256); // addiu t6, r0, 256 - //c->sw(t6, 0, t1); // sw t6, 0(t1) - spad_from_dma(cache.fake_scratchpad_data, madr, sadr, qwc); - c->addiu(t6, r0, 0); // addiu t6, r0, 0 - } - - block_23: - c->and_(gp, t7, t9); // and gp, t7, t9 - c->vmula_bc(DEST::xyzw, BC::x, vf16, vf2); // vmulax.xyzw acc, vf16, vf2 - bc = c->sgpr64(gp) == 0; // beq gp, r0, L64 - c->lwu(gp, 36, t8); // lwu gp, 36(t8) - if (bc) {goto block_46;} // branch non-likely - - c->vmadda_bc(DEST::xyzw, BC::y, vf17, vf2); // vmadday.xyzw acc, vf17, vf2 - c->lbu(s5, 45, t8); // lbu s5, 45(t8) - c->vmadda_bc(DEST::xyzw, BC::z, vf18, vf2); // vmaddaz.xyzw acc, vf18, vf2 - c->sw(gp, 4, t0); // sw gp, 4(t0) - c->vmsuba_bc(DEST::xyzw, BC::w, vf19, vf0); // vmsubaw.xyzw acc, vf19, vf0 - c->sh(s5, 0, t0); // sh s5, 0(t0) - c->vmadd_bc(DEST::xyzw, BC::w, vf5, vf1, vf2); // vmaddw.xyzw vf5, vf1, vf2 - c->lwu(gp, 32, t8); // lwu gp, 32(t8) - // fprintf(stderr, "block_23-0 gp = 0x%lx\n", c->sgpr64(gp)); - c->vmula_bc(DEST::xyzw, BC::w, vf27, vf0); // vmulaw.xyzw acc, vf27, vf0 - c->lbu(s5, 47, t8); // lbu s5, 47(t8) - c->vmadda_bc(DEST::xyzw, BC::x, vf24, vf2); // vmaddax.xyzw acc, vf24, vf2 - c->sw(gp, 20, t0); // sw gp, 20(t0) - c->vmadda_bc(DEST::xyzw, BC::y, vf25, vf2); // vmadday.xyzw acc, vf25, vf2 - c->sh(s5, 16, t0); // sh s5, 16(t0) - c->vmadda_bc(DEST::xyzw, BC::z, vf26, vf2); // vmaddaz.xyzw acc, vf26, vf2 - c->lwu(gp, 32, t8); // lwu gp, 32(t8) - c->mov128_gpr_vf(s5, vf5); // qmfc2.i s5, vf5 - c->lbu(s4, 44, t8); // lbu s4, 44(t8) - c->vmadd_bc(DEST::xyzw, BC::w, vf6, vf1, vf2); // vmaddw.xyzw vf6, vf1, vf2 - c->sw(gp, 36, t0); // sw gp, 36(t0) - c->vmsub_bc(DEST::xyzw, BC::w, vf8, vf1, vf2); // vmsubw.xyzw vf8, vf1, vf2 - c->sh(s4, 32, t0); // sh s4, 32(t0) - c->pcgtw(s5, r0, s5); // pcgtw s5, r0, s5 - c->lwu(gp, 40, t8); // lwu gp, 40(t8) - // fprintf(stderr, "block_23-1: gp = 0x%lx\n", c->sgpr64(gp)); - c->ppach(s5, r0, s5); // ppach s5, r0, s5 - c->lbu(s4, 46, t8); // lbu s4, 46(t8) - c->vadd_bc(DEST::xyzw, BC::z, vf6, vf3, vf6); // vaddz.xyzw vf6, vf3, vf6 - c->sw(gp, 52, t0); // sw gp, 52(t0) - c->vadd_bc(DEST::xyzw, BC::z, vf7, vf3, vf8); // vaddz.xyzw vf7, vf3, vf8 - c->sw(t3, 12, t0); // sw t3, 12(t0) - bc = c->sgpr64(s5) != 0; // bne s5, r0, L63 - c->sh(s4, 48, t0); // sh s4, 48(t0) - if (bc) {goto block_45;} // branch non-likely - - c->vmini(DEST::xyzw, vf4, vf4, vf8); // vmini.xyzw vf4, vf4, vf8 - c->sw(t3, 28, t0); // sw t3, 28(t0) - // nop // sll r0, r0, 0 - c->lbu(s5, 53, t8); // lbu s5, 53(t8) - c->mov128_gpr_vf(gp, vf6); // qmfc2.i gp, vf6 - c->sw(t3, 44, t0); // sw t3, 44(t0) - c->mov128_gpr_vf(s3, vf7); // qmfc2.i s3, vf7 - c->lbu(s4, 56, t8); // lbu s4, 56(t8) - c->pcgtw(s2, r0, gp); // pcgtw s2, r0, gp - c->lw(gp, 12, t8); // lw gp, 12(t8) - // fprintf(stderr, "loaded gp: 0x%lx from tfragment: 0x%lx\n", c->sgpr64(gp), c->sgpr64(t8)); - c->pcgtw(s3, r0, s3); // pcgtw s3, r0, s3 - c->sb(s4, 76, t0); // sb s4, 76(t0) - c->pinteh(s4, s2, s3); // pinteh s4, s2, s3 - c->lbu(s2, 54, t8); // lbu s2, 54(t8) - c->ppacb(s3, r0, s4); // ppacb s3, r0, s4 - c->lbu(s1, 55, t8); // lbu s1, 55(t8) - bc = c->sgpr64(s3) == 0; // beq s3, r0, L56 - c->dsrl32(s4, s3, 8); // dsrl32 s4, s3, 8 - if (bc) {goto block_36;} // branch non-likely - - bc = c->sgpr64(s2) == 0; // beq s2, r0, L56 - // nop // sll r0, r0, 0 - if (bc) {goto block_36;} // branch non-likely - - bc = c->sgpr64(s1) == 0; // beq s1, r0, L55 - c->dsrl(s5, s3, 16); // dsrl s5, s3, 16 - if (bc) {goto block_33;} // branch non-likely - - bc = c->sgpr64(s5) == 0; // beq s5, r0, L55 - c->dsrl32(s5, s3, 24); // dsrl32 s5, s3, 24 - if (bc) {goto block_33;} // branch non-likely - - bc = c->sgpr64(s5) != 0; // bne s5, r0, L64 - c->addiu(s5, s1, 3); // addiu s5, s1, 3 - if (bc) {goto block_46;} // branch non-likely - - c->sra(s4, s5, 2); // sra s4, s5, 2 - c->mov64(s5, s1); // or s5, s1, r0 - c->sll(t3, s4, 2); // sll t3, s4, 2 - c->sh(s4, 64, t0); // sh s4, 64(t0) - // nop // sll r0, r0, 0 - c->sb(t3, 78, t0); // sb t3, 78(t0) - c->daddiu(t6, t6, 3); // daddiu t6, t6, 3 - c->lq(s2, 32, t0); // lq s2, 32(t0) - // nop // sll r0, r0, 0 - c->lq(s1, 48, t0); // lq s1, 48(t0) - // nop // sll r0, r0, 0 - c->lq(t3, 64, t0); // lq t3, 64(t0) - c->sq(s2, 0, ra); // sq s2, 0(ra) - // nop // sll r0, r0, 0 - c->sq(s1, 16, ra); // sq s1, 16(ra) - c->dsrl32(s2, s3, 16); // dsrl32 s2, s3, 16 - c->sq(t3, 32, ra); // sq t3, 32(ra) - c->daddiu(ra, ra, 48); // daddiu ra, ra, 48 - bc = c->sgpr64(s2) != 0; // bne s2, r0, L57 - c->ori(t3, t2, 18); // ori t3, t2, 18 - if (bc) {goto block_38;} // branch non-likely - - c->dsrl32(t3, s3, 8); // dsrl32 t3, s3, 8 - // nop // sll r0, r0, 0 - bc = c->sgpr64(t3) != 0; // bne t3, r0, L57 - c->ori(t3, t2, 16); // ori t3, t2, 16 - if (bc) {goto block_38;} // branch non-likely - - //beq r0, r0, L57 // beq r0, r0, L57 - c->ori(t3, t2, 14); // ori t3, t2, 14 - goto block_38; // branch always - - - block_33: - bc = c->sgpr64(s4) != 0; // bne s4, r0, L64 - c->addiu(s5, s2, 3); // addiu s5, s2, 3 - if (bc) {goto block_46;} // branch non-likely - - c->sra(s4, s5, 2); // sra s4, s5, 2 - c->mov64(s5, s2); // or s5, s2, r0 - c->sll(t3, s4, 2); // sll t3, s4, 2 - c->sh(s4, 64, t0); // sh s4, 64(t0) - // nop // sll r0, r0, 0 - c->sb(t3, 78, t0); // sb t3, 78(t0) - c->daddiu(t6, t6, 2); // daddiu t6, t6, 2 - c->lq(s2, 16, t0); // lq s2, 16(t0) - // nop // sll r0, r0, 0 - c->lq(t3, 64, t0); // lq t3, 64(t0) - c->sq(s2, 0, ra); // sq s2, 0(ra) - c->dsrl(s3, s3, 8); // dsrl s3, s3, 8 - c->sq(t3, 16, ra); // sq t3, 16(ra) - c->daddiu(ra, ra, 32); // daddiu ra, ra, 32 - bc = c->sgpr64(s3) != 0; // bne s3, r0, L57 - c->ori(t3, t2, 10); // ori t3, t2, 10 - if (bc) {goto block_38;} // branch non-likely - - //beq r0, r0, L57 // beq r0, r0, L57 - c->ori(t3, t2, 8); // ori t3, t2, 8 - goto block_38; // branch always - - - block_36: - bc = c->sgpr64(s4) != 0; // bne s4, r0, L64 - c->addiu(s4, s5, 3); // addiu s4, s5, 3 - if (bc) {goto block_46;} // branch non-likely - - c->sra(s4, s4, 2); // sra s4, s4, 2 - // nop // sll r0, r0, 0 - c->sll(t3, s4, 2); // sll t3, s4, 2 - c->sh(s4, 64, t0); // sh s4, 64(t0) - // nop // sll r0, r0, 0 - c->sb(t3, 78, t0); // sb t3, 78(t0) - c->ori(t3, t2, 6); // ori t3, t2, 6 - c->lq(s3, 0, t0); // lq s3, 0(t0) - c->daddiu(t6, t6, 2); // daddiu t6, t6, 2 - c->lq(s2, 64, t0); // lq s2, 64(t0) - c->sq(s3, 0, ra); // sq s3, 0(ra) - // nop // sll r0, r0, 0 - c->sq(s2, 16, ra); // sq s2, 16(ra) - c->daddiu(ra, ra, 32); // daddiu ra, ra, 32 - - block_38: - c->addiu(s3, r0, 127); // addiu s3, r0, 127 - c->daddu(s2, t6, s4); // daddu s2, t6, s4 - c->dsubu(s3, s3, s2); // dsubu s3, s3, s2 - // nop // sll r0, r0, 0 - bc = ((s64)c->sgpr64(s3)) >= 0; // bgez s3, L60 - // nop // sll r0, r0, 0 - if (bc) {goto block_42;} // branch non-likely - - - // block_39: - /* - c->lw(ra, 0, t1); // lw ra, 0(t1) - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - c->andi(ra, ra, 256); // andi ra, ra, 256 - // nop // sll r0, r0, 0 - bc = c->sgpr64(ra) == 0; // beq ra, r0, L59 - // nop // sll r0, r0, 0 - if (bc) {goto block_41;} // branch non-likely - - // nop // sll r0, r0, 0 - c->lw(ra, 184, t0); // lw ra, 184(t0) - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - c->daddiu(ra, ra, 1); // daddiu ra, ra, 1 - // nop // sll r0, r0, 0 - c->sw(ra, 184, t0); // sw ra, 184(t0) - //beq r0, r0, L58 // beq r0, r0, L58 - // nop // sll r0, r0, 0 - goto block_39; // branch always - */ - - - // block_41: - { - //c->sw(a3, 128, t1); // sw a3, 128(t1) - u32 sadr = c->sgpr64(a3); - c->xori(a3, a3, 6144); // xori a3, a3, 6144 - //c->sw(v1, 16, t1); // sw v1, 16(t1) - u32 madr = c->sgpr64(v1); - c->sll(ra, t6, 4); // sll ra, t6, 4 - c->addu(v1, v1, ra); // addu v1, v1, ra - c->mov64(ra, a3); // or ra, a3, r0 - //c->sw(t6, 32, t1); // sw t6, 32(t1) - u32 qwc = c->sgpr64(t6); - c->addiu(t6, r0, 256); // addiu t6, r0, 256 - //c->sw(t6, 0, t1); // sw t6, 0(t1) - spad_from_dma(cache.fake_scratchpad_data, madr, sadr, qwc); - c->addiu(t6, r0, 0); // addiu t6, r0, 0 - } - - block_42: - c->daddu(t6, t6, s4); // daddu t6, t6, s4 - c->sw(t8, 168, t0); // sw t8, 168(t0) - c->ld(s4, 0, gp); // ld s4, 0(gp) - c->daddiu(t8, gp, 8); // daddiu t8, gp, 8 - c->daddiu(gp, s5, -4); // daddiu gp, s5, -4 - // fprintf(stderr, "block_42: gp = 0x%lx\n", c->sgpr64(gp)); - c->lq(s5, 128, t0); // lq s5, 128(t0) - c->pextlh(s4, r0, s4); // pextlh s4, r0, s4 - c->mfc1(r0, f31); // mfc1 r0, f31 - c->paddw(s2, s4, s5); // paddw s2, s4, s5 - c->mfc1(r0, f31); // mfc1 r0, f31 - c->lw(s4, 0, s2); // lw s4, 0(s2) - c->dsra32(s3, s2, 0); // dsra32 s3, s2, 0 - c->lw(s3, 0, s3); // lw s3, 0(s3) - c->pcpyud(s1, s2, s2); // pcpyud s1, s2, s2 - c->lw(s2, 0, s1); // lw s2, 0(s1) - c->dsra32(s1, s1, 0); // dsra32 s1, s1, 0 - bc = ((s64)c->sgpr64(gp)) <= 0; // blez gp, L62 - c->lw(s1, 0, s1); // lw s1, 0(s1) - if (bc) {goto block_44;} // branch non-likely - - - block_43: - c->ld(s0, 0, t8); // ld s0, 0(t8) - c->daddiu(ra, ra, 16); // daddiu ra, ra, 16 - c->daddiu(t8, t8, 8); // daddiu t8, t8, 8 - c->sw(s4, -16, ra); // sw s4, -16(ra) - c->daddiu(gp, gp, -4); // daddiu gp, gp, -4 - c->sw(s3, -12, ra); // sw s3, -12(ra) - c->pextlh(s4, r0, s0); // pextlh s4, r0, s0 - c->sw(s2, -8, ra); // sw s2, -8(ra) - c->paddw(s2, s4, s5); // paddw s2, s4, s5 - // this one is storing a 0! - c->sw(s1, -4, ra); // sw s1, -4(ra) - c->lw(s4, 0, s2); // lw s4, 0(s2) - c->dsra32(s3, s2, 0); // dsra32 s3, s2, 0 - c->lw(s3, 0, s3); // lw s3, 0(s3) - c->pcpyud(s1, s2, s2); // pcpyud s1, s2, s2 - c->lw(s2, 0, s1); // lw s2, 0(s1) - c->dsra32(s1, s1, 0); // dsra32 s1, s1, 0 - bc = ((s64)c->sgpr64(gp)) > 0; // bgtz gp, L61 - c->lw(s1, 0, s1); // lw s1, 0(s1) - if (bc) {goto block_43;} // branch non-likely - - - block_44: - c->daddiu(ra, ra, 16); // daddiu ra, ra, 16 - c->lw(t8, 168, t0); // lw t8, 168(t0) - // nop // sll r0, r0, 0 - c->sw(s4, -16, ra); // sw s4, -16(ra) - // nop // sll r0, r0, 0 - c->sw(s3, -12, ra); // sw s3, -12(ra) - // nop // sll r0, r0, 0 - c->sw(s2, -8, ra); // sw s2, -8(ra) - // nop // sll r0, r0, 0 - c->sw(s1, -4, ra); // sw s1, -4(ra) - - block_45: - c->xor_(t7, t7, t9); // xor t7, t7, t9 - // nop // sll r0, r0, 0 - - block_46: - c->daddiu(t8, t8, 64); // daddiu t8, t8, 64 - c->srl(t9, t9, 1); // srl t9, t9, 1 - c->addiu(a2, a2, -1); // addiu a2, a2, -1 - // nop // sll r0, r0, 0 - bc = c->sgpr64(t9) != 0; // bne t9, r0, L51 - c->lqc2(vf2, 16, t8); // lqc2 vf2, 16(t8) - if (bc) {goto block_19;} // branch non-likely - - - block_47: - // nop // sll r0, r0, 0 - c->lw(gp, 160, t0); // lw gp, 160(t0) - // fprintf(stderr, "block_47: gp = 0x%lx\n", c->sgpr64(gp)); - // nop // sll r0, r0, 0 - c->lw(t9, 164, t0); // lw t9, 164(t0) - bc = c->sgpr64(gp) != c->sgpr64(t9); // bne gp, t9, L49 - c->sb(t7, -1, gp); // sb t7, -1(gp) - if (bc) {goto block_16;} // branch non-likely - - bc = ((s64)c->sgpr64(a2)) > 0; // bgtz a2, L43 - // nop // sll r0, r0, 0 - if (bc) {goto block_6;} // branch non-likely - - bc = c->sgpr64(t6) == 0; // beq t6, r0, L68 - // nop // sll r0, r0, 0 - if (bc) {goto block_53;} // branch non-likely - - - // block_50: - /* - c->lw(a0, 0, t1); // lw a0, 0(t1) - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - c->andi(a0, a0, 256); // andi a0, a0, 256 - // nop // sll r0, r0, 0 - bc = c->sgpr64(a0) == 0; // beq a0, r0, L67 - // nop // sll r0, r0, 0 - if (bc) {goto block_52;} // branch non-likely - - // nop // sll r0, r0, 0 - c->lw(a0, 184, t0); // lw a0, 184(t0) - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - c->daddiu(a0, a0, 1); // daddiu a0, a0, 1 - // nop // sll r0, r0, 0 - c->sw(a0, 184, t0); // sw a0, 184(t0) - //beq r0, r0, L66 // beq r0, r0, L66 - // nop // sll r0, r0, 0 - goto block_50; // branch always - */ - - - // block_52: - { - //c->sw(a3, 128, t1); // sw a3, 128(t1) - u32 sadr = c->sgpr64(a3); - c->xori(a0, a3, 6144); // xori a0, a3, 6144 - //c->sw(v1, 16, t1); // sw v1, 16(t1) - u32 madr = c->sgpr64(v1); - c->sll(a1, t6, 4); // sll a1, t6, 4 - c->addu(v1, v1, a1); // addu v1, v1, a1 - c->mov64(a0, a0); // or a0, a0, r0 - //c->sw(t6, 32, t1); // sw t6, 32(t1) - u32 qwc = c->sgpr64(t6); - c->addiu(a0, r0, 256); // addiu a0, r0, 256 - //c->sw(a0, 0, t1); // sw a0, 0(t1) - spad_from_dma(cache.fake_scratchpad_data, madr, sadr, qwc); - c->addiu(a0, r0, 0); // addiu a0, r0, 0 - } - - block_53: - /* - c->lw(a0, 0, t1); // lw a0, 0(t1) - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - c->andi(a0, a0, 256); // andi a0, a0, 256 - // nop // sll r0, r0, 0 - bc = c->sgpr64(a0) == 0; // beq a0, r0, L69 - // nop // sll r0, r0, 0 - if (bc) {goto block_55;} // branch non-likely - - // nop // sll r0, r0, 0 - c->lw(a0, 184, t0); // lw a0, 184(t0) - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - c->daddiu(a0, a0, 1); // daddiu a0, a0, 1 - // nop // sll r0, r0, 0 - c->sw(a0, 184, t0); // sw a0, 184(t0) - //beq r0, r0, L68 // beq r0, r0, L68 - // nop // sll r0, r0, 0 - goto block_53; // branch always - */ - - - block_55: - c->lw(a0, 176, t0); // lw a0, 176(t0) - // nop // sll r0, r0, 0 - c->sw(t3, 172, t0); // sw t3, 172(t0) - // nop // sll r0, r0, 0 - c->sqc2(vf4, 112, t0); // sqc2 vf4, 112(t0) - // nop // sll r0, r0, 0 - c->sw(v1, 4, a0); // sw v1, 4(a0) - // nop // sll r0, r0, 0 - c->gprs[v0].du64[0] = 0; // or v0, r0, r0 - c->ld(ra, 0, sp); // ld ra, 0(sp) - c->lq(gp, 112, sp); // lq gp, 112(sp) - c->lq(s5, 96, sp); // lq s5, 96(sp) - c->lq(s4, 80, sp); // lq s4, 80(sp) - c->lq(s3, 64, sp); // lq s3, 64(sp) - c->lq(s2, 48, sp); // lq s2, 48(sp) - c->lq(s1, 32, sp); // lq s1, 32(sp) - c->lq(s0, 16, sp); // lq s0, 16(sp) - //jr ra // jr ra - c->daddiu(sp, sp, 128); // daddiu sp, sp, 128 - goto end_of_function; // return - - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - end_of_function: - return c->gprs[v0].du64[0]; -} - -// clang-format on -void link() { - cache.tfrag_work = intern_from_c("*tfrag-work*").c(); - cache.fake_scratchpad_data = intern_from_c("*fake-scratchpad-data*").c(); - cache.transform_regs = intern_from_c("*transform-regs*").c(); - gLinkedFunctionTable.reg("draw-inline-array-tfrag", execute, 512); -} - -} // namespace draw_inline_array_tfrag -} // namespace Mips2C::jak1 - -// clang-format off -//--------------------------MIPS2C--------------------- -#include "game/mips2c/mips2c_private.h" - -namespace Mips2C::jak1 { -namespace stats_tfrag_asm { -struct Cache { - void* tfrag_work; // *tfrag-work* - void* transform_regs; -} cache; - -u64 execute(void* ctxt) { - auto* c = (ExecutionContext*)ctxt; - bool bc = false; - load_vfs_from_tf_regs(cache.transform_regs, c); - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - c->load_symbol(v1, cache.tfrag_work); // lw v1, *tfrag-work*(s7) - // nop // sll r0, r0, 0 - c->lw(a1, 4, a0); // lw a1, 4(a0) - // nop // sll r0, r0, 0 - c->lqc2(vf10, 12, a0); // lqc2 vf10, 12(a0) - bc = c->sgpr64(a1) == 0; // beq a1, r0, L7 - c->lqc2(vf14, 80, v1); // lqc2 vf14, 80(v1) - if (bc) {goto block_15;} // branch non-likely - - c->vmula_bc(DEST::xyzw, BC::x, vf16, vf10); // vmulax.xyzw acc, vf16, vf10 - c->lb(a2, 49, a0); // lb a2, 49(a0) - c->vmadda_bc(DEST::xyzw, BC::y, vf17, vf10); // vmadday.xyzw acc, vf17, vf10 - c->lb(a2, 50, a0); // lb a2, 50(a0) - c->vmadda_bc(DEST::xyzw, BC::z, vf18, vf10); // vmaddaz.xyzw acc, vf18, vf10 - c->lb(a0, 51, a0); // lb a0, 51(a0) - c->vmsub_bc(DEST::xyzw, BC::w, vf9, vf19, vf0); // vmsubw.xyzw vf9, vf19, vf0 - // nop // sll r0, r0, 0 - c->vmula_bc(DEST::xyzw, BC::w, vf27, vf0); // vmulaw.xyzw acc, vf27, vf0 - // nop // sll r0, r0, 0 - c->vmadda_bc(DEST::xyzw, BC::x, vf24, vf10); // vmaddax.xyzw acc, vf24, vf10 - // nop // sll r0, r0, 0 - c->vmadda_bc(DEST::xyzw, BC::y, vf25, vf10); // vmadday.xyzw acc, vf25, vf10 - // nop // sll r0, r0, 0 - c->vadd_bc(DEST::xyzw, BC::w, vf9, vf9, vf10); // vaddw.xyzw vf9, vf9, vf10 - // nop // sll r0, r0, 0 - c->vmadd_bc(DEST::xyzw, BC::z, vf11, vf26, vf10); // vmaddz.xyzw vf11, vf26, vf10 - // nop // sll r0, r0, 0 - c->vadd_bc(DEST::xyzw, BC::w, vf12, vf11, vf10); // vaddw.xyzw vf12, vf11, vf10 - // nop // sll r0, r0, 0 - c->vsub_bc(DEST::xyzw, BC::w, vf13, vf11, vf10); // vsubw.xyzw vf13, vf11, vf10 - // nop // sll r0, r0, 0 - c->vsub_bc(DEST::xyzw, BC::z, vf11, vf0, vf12); // vsubz.xyzw vf11, vf0, vf12 - // nop // sll r0, r0, 0 - c->mov128_gpr_vf(a3, vf9); // qmfc2.i a3, vf9 - // nop // sll r0, r0, 0 - c->pcgtw(a3, r0, a3); // pcgtw a3, r0, a3 - c->mfc1(r0, f31); // mfc1 r0, f31 - c->ppach(a3, r0, a3); // ppach a3, r0, a3 - c->mfc1(r0, f31); // mfc1 r0, f31 - bc = c->sgpr64(a3) != 0; // bne a3, r0, L7 - c->vadd_bc(DEST::xyzw, BC::z, vf12, vf14, vf12); // vaddz.xyzw vf12, vf14, vf12 - if (bc) {goto block_15;} // branch non-likely - - // nop // sll r0, r0, 0 - c->vadd_bc(DEST::xyzw, BC::z, vf13, vf14, vf13); // vaddz.xyzw vf13, vf14, vf13 - c->mov128_gpr_vf(t0, vf12); // qmfc2.i t0, vf12 - // nop // sll r0, r0, 0 - c->mov128_gpr_vf(a3, vf13); // qmfc2.i a3, vf13 - // nop // sll r0, r0, 0 - c->pcgtw(t0, r0, t0); // pcgtw t0, r0, t0 - c->mfc1(r0, f31); // mfc1 r0, f31 - c->pcgtw(a3, r0, a3); // pcgtw a3, r0, a3 - c->mfc1(r0, f31); // mfc1 r0, f31 - c->pinteh(a3, t0, a3); // pinteh a3, t0, a3 - c->mfc1(r0, f31); // mfc1 r0, f31 - c->ppacb(a3, r0, a3); // ppacb a3, r0, a3 - c->mfc1(r0, f31); // mfc1 r0, f31 - bc = c->sgpr64(a3) == 0; // beq a3, r0, L5 - // nop // sll r0, r0, 0 - if (bc) {goto block_12;} // branch non-likely - - bc = c->sgpr64(a2) == 0; // beq a2, r0, L5 - // nop // sll r0, r0, 0 - if (bc) {goto block_12;} // branch non-likely - - bc = c->sgpr64(a0) == 0; // beq a0, r0, L3 - c->dsrl(a0, a3, 16); // dsrl a0, a3, 16 - if (bc) {goto block_9;} // branch non-likely - - bc = c->sgpr64(a0) == 0; // beq a0, r0, L3 - // nop // sll r0, r0, 0 - if (bc) {goto block_9;} // branch non-likely - - c->dsrl32(a0, a3, 24); // dsrl32 a0, a3, 24 - c->lbu(a2, 4, a1); // lbu a2, 4(a1) - bc = c->sgpr64(a0) != 0; // bne a0, r0, L2 - c->lw(a0, 148, v1); // lw a0, 148(v1) - if (bc) {goto block_8;} // branch non-likely - - // nop // sll r0, r0, 0 - c->lw(a0, 144, v1); // lw a0, 144(v1) - - block_8: - // nop // sll r0, r0, 0 - c->lbu(v1, 12, a1); // lbu v1, 12(a1) - // nop // sll r0, r0, 0 - c->lw(a3, 4, a0); // lw a3, 4(a0) - // nop // sll r0, r0, 0 - c->lw(a1, 8, a0); // lw a1, 8(a0) - c->addu(a2, a3, a2); // addu a2, a3, a2 - c->sw(a2, 4, a0); // sw a2, 4(a0) - // nop // sll r0, r0, 0 - c->lh(a2, 2, a0); // lh a2, 2(a0) - c->daddu(v1, a1, v1); // daddu v1, a1, v1 - c->sw(v1, 8, a0); // sw v1, 8(a0) - c->daddiu(v1, a2, 1); // daddiu v1, a2, 1 - c->sh(v1, 2, a0); // sh v1, 2(a0) - //beq r0, r0, L7 // beq r0, r0, L7 - // nop // sll r0, r0, 0 - goto block_15; // branch always - - - block_9: - c->dsrl32(a0, a3, 8); // dsrl32 a0, a3, 8 - c->lbu(a2, 2, a1); // lbu a2, 2(a1) - bc = c->sgpr64(a0) != 0; // bne a0, r0, L4 - c->lw(a0, 148, v1); // lw a0, 148(v1) - if (bc) {goto block_11;} // branch non-likely - - // nop // sll r0, r0, 0 - c->lw(a0, 144, v1); // lw a0, 144(v1) - - block_11: - // nop // sll r0, r0, 0 - c->lbu(v1, 10, a1); // lbu v1, 10(a1) - // nop // sll r0, r0, 0 - c->lw(a3, 4, a0); // lw a3, 4(a0) - // nop // sll r0, r0, 0 - c->lw(a1, 8, a0); // lw a1, 8(a0) - c->addu(a2, a3, a2); // addu a2, a3, a2 - c->sw(a2, 4, a0); // sw a2, 4(a0) - // nop // sll r0, r0, 0 - c->lh(a2, 2, a0); // lh a2, 2(a0) - c->daddu(v1, a1, v1); // daddu v1, a1, v1 - c->sw(v1, 8, a0); // sw v1, 8(a0) - c->daddiu(v1, a2, 1); // daddiu v1, a2, 1 - c->sh(v1, 2, a0); // sh v1, 2(a0) - //beq r0, r0, L7 // beq r0, r0, L7 - // nop // sll r0, r0, 0 - goto block_15; // branch always - - - block_12: - c->dsrl32(a0, a3, 8); // dsrl32 a0, a3, 8 - c->lbu(a2, 0, a1); // lbu a2, 0(a1) - bc = c->sgpr64(a0) != 0; // bne a0, r0, L6 - c->lw(a0, 148, v1); // lw a0, 148(v1) - if (bc) {goto block_14;} // branch non-likely - - //beq r0, r0, L6 // beq r0, r0, L6 - c->lw(a0, 144, v1); // lw a0, 144(v1) - goto block_14; // branch always - - - block_14: - // nop // sll r0, r0, 0 - c->lbu(v1, 8, a1); // lbu v1, 8(a1) - // nop // sll r0, r0, 0 - c->lw(a3, 4, a0); // lw a3, 4(a0) - // nop // sll r0, r0, 0 - c->lw(a1, 8, a0); // lw a1, 8(a0) - c->addu(a2, a3, a2); // addu a2, a3, a2 - c->sw(a2, 4, a0); // sw a2, 4(a0) - // nop // sll r0, r0, 0 - c->lh(a2, 2, a0); // lh a2, 2(a0) - c->daddu(v1, a1, v1); // daddu v1, a1, v1 - c->sw(v1, 8, a0); // sw v1, 8(a0) - c->daddiu(v1, a2, 1); // daddiu v1, a2, 1 - c->sh(v1, 2, a0); // sh v1, 2(a0) - - block_15: - c->gprs[v0].du64[0] = 0; // or v0, r0, r0 - //jr ra // jr ra - c->daddu(sp, sp, r0); // daddu sp, sp, r0 - goto end_of_function; // return - - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - end_of_function: - return c->gprs[v0].du64[0]; -} - -void link() { - cache.tfrag_work = intern_from_c("*tfrag-work*").c(); - cache.transform_regs = intern_from_c("*transform-regs*").c(); - gLinkedFunctionTable.reg("stats-tfrag-asm", execute, 512); -} - -} // namespace stats_tfrag_asm -} // namespace Mips2C diff --git a/game/mips2c/jak1_functions/tie_methods.cpp b/game/mips2c/jak1_functions/tie_methods.cpp deleted file mode 100644 index 0100333907..0000000000 --- a/game/mips2c/jak1_functions/tie_methods.cpp +++ /dev/null @@ -1,1962 +0,0 @@ - -//--------------------------MIPS2C--------------------- -#include "game/kernel/jak1/kscheme.h" -#include "game/mips2c/mips2c_private.h" -using namespace jak1; -namespace Mips2C::jak1 { -namespace draw_inline_array_instance_tie { -struct Cache { - void* fake_scratchpad_data; // *fake-scratchpad-data* - void* instance_tie_work_copy; // *instance-tie-work-copy* - void* wind_work; // *wind-work* - void* math_camera; // *math-camera* -} cache; - -Vf background_vu0_data[16]; - -u16 vcallms_42(ExecutionContext* c) { - // TODO - // lq.xyzw vf16, 0(vi00) | nop - c->vfs[vf16].vf = background_vu0_data[0]; - // lq.xyzw vf17, 1(vi00) | nop - c->vfs[vf17].vf = background_vu0_data[1]; - // lq.xyzw vf18, 2(vi00) | nop - c->vfs[vf18].vf = background_vu0_data[2]; - // lq.xyzw vf19, 3(vi00) | nop - c->vfs[vf19].vf = background_vu0_data[3]; - // lq.xyzw vf28, 8(vi00) | mulax.xyzw ACC, vf16, vf02 - c->vfs[vf28].vf = background_vu0_data[8]; - c->acc.vf.mula(Mask::xyzw, c->vfs[vf16].vf, c->vfs[vf02].vf.x()); - - // lq.xyzw vf29, 9(vi00) | madday.xyzw ACC, vf17, vf02 - c->vfs[vf29].vf = background_vu0_data[9]; - c->acc.vf.madda(Mask::xyzw, c->vfs[vf17].vf, c->vfs[vf02].vf.y()); - - // lq.xyzw vf30, 10(vi00) | maddaz.xyzw ACC, vf18, vf02 - c->vfs[vf30].vf = background_vu0_data[10]; - c->acc.vf.madda(Mask::xyzw, c->vfs[vf18].vf, c->vfs[vf02].vf.z()); - - // lq.xyzw vf31, 11(vi00) | msubaw.xyzw ACC, vf19, vf00 - c->vfs[vf31].vf = background_vu0_data[11]; - c->acc.vf.msuba(Mask::xyzw, c->vfs[vf19].vf, 1.f); - - // lq.xyzw vf24, 4(vi00) | maddw.xyzw vf04, vf01, vf02 - c->vfs[vf24].vf = background_vu0_data[4]; - u16 vi01 = - c->acc.vf.madd_flag(Mask::xyzw, c->vfs[vf04].vf, c->vfs[vf01].vf, c->vfs[vf02].vf.w()) & 0xf0; - - // lq.xyzw vf25, 5(vi00) | mulax.xyzw ACC, vf28, vf02 - c->vfs[vf25].vf = background_vu0_data[5]; - c->acc.vf.mula(Mask::xyzw, c->vfs[vf28].vf, c->vfs[vf02].vf.x()); - // lq.xyzw vf26, 6(vi00) | madday.xyzw ACC, vf29, vf02 - c->vfs[vf26].vf = background_vu0_data[6]; - c->acc.vf.madda(Mask::xyzw, c->vfs[vf29].vf, c->vfs[vf02].vf.y()); - // lq.xyzw vf27, 7(vi00) | maddaz.xyzw ACC, vf30, vf02 - c->vfs[vf27].vf = background_vu0_data[7]; - c->acc.vf.madda(Mask::xyzw, c->vfs[vf30].vf, c->vfs[vf02].vf.z()); - // fmand vi01, vi02 | maddw.xyzw vf05, vf31, vf00 - c->acc.vf.madd(Mask::xyzw, c->vfs[vf05].vf, c->vfs[vf31].vf, 1.f); - // nop | mulax.xyzw ACC, vf24, vf02 - c->acc.vf.mula(Mask::xyzw, c->vfs[vf24].vf, c->vfs[vf02].vf.x()); - // nop | madday.xyzw ACC, vf25, vf02 - c->acc.vf.madda(Mask::xyzw, c->vfs[vf25].vf, c->vfs[vf02].vf.y()); - // nop | maddaz.xyzw ACC, vf26, vf02 :e - c->acc.vf.madda(Mask::xyzw, c->vfs[vf26].vf, c->vfs[vf02].vf.z()); - // nop | maddw.xyzw vf06, vf27, vf00 - c->acc.vf.madd(Mask::xyzw, c->vfs[vf06].vf, c->vfs[vf27].vf, 1.f); - - return vi01; -} - -void vcallms_29(ExecutionContext* c) { - // lq.xyzw vf24, 4(vi00) | nop - c->vfs[vf24].vf = background_vu0_data[4]; - // lq.xyzw vf25, 5(vi00) | nop - c->vfs[vf25].vf = background_vu0_data[5]; - // lq.xyzw vf26, 6(vi00) | nop :e - c->vfs[vf26].vf = background_vu0_data[6]; - // lq.xyzw vf27, 7(vi00) | nop - c->vfs[vf27].vf = background_vu0_data[7]; -} - -/*! - * Set up the VU0 context like it would be for background. See background-upload-vu0 in - * background.gc for more details. - */ -void init_background(ExecutionContext* c) { - // this first part is GOAL to init VU1 vf registers from the math camera. - - // lw v1, *math-camera*(s7) - c->load_symbol(v1, cache.math_camera); - // lqc2 vf16, 860(v1) - c->lqc2(vf16, 860, v1); - // lqc2 vf17, 876(v1) - c->lqc2(vf17, 876, v1); - // lqc2 vf18, 892(v1) - c->lqc2(vf18, 892, v1); - // lqc2 vf19, 908(v1) - c->lqc2(vf19, 908, v1); - // lqc2 vf20, 988(v1) - c->lqc2(vf20, 988, v1); - // lqc2 vf21, 1004(v1) - c->lqc2(vf21, 1004, v1); - // lqc2 vf22, 1020(v1) - c->lqc2(vf22, 1020, v1); - // lqc2 vf23, 1036(v1) - c->lqc2(vf23, 1036, v1); - // lqc2 vf24, 364(v1) - c->lqc2(vf24, 364, v1); - // lqc2 vf25, 380(v1) - c->lqc2(vf25, 380, v1); - // lqc2 vf26, 396(v1) - c->lqc2(vf26, 396, v1); - // lqc2 vf27, 412(v1) - c->lqc2(vf27, 412, v1); - // lqc2 vf28, 572(v1) - c->lqc2(vf28, 572, v1); - // lqc2 vf29, 588(v1) - c->lqc2(vf29, 588, v1); - // lqc2 vf30, 604(v1) - c->lqc2(vf30, 604, v1); - // lqc2 vf31, 620(v1) - c->lqc2(vf31, 620, v1); - // lqc2 vf31, 620(v1) - c->lqc2(vf31, 620, v1); - - // dump them in VU0 data memory for fast access later. - // sq.xyzw vf24, 4(vi00) | maxw.xyzw vf01, vf00, vf00 - c->vfs[vf01].vf.fill(1.f); - background_vu0_data[4] = c->vfs[vf24].vf; - // sq.xyzw vf25, 5(vi00) | nop - background_vu0_data[5] = c->vfs[vf25].vf; - // sq.xyzw vf26, 6(vi00) | nop - background_vu0_data[6] = c->vfs[vf26].vf; - // sq.xyzw vf27, 7(vi00) | nop - background_vu0_data[7] = c->vfs[vf27].vf; - // sq.xyzw vf16, 0(vi00) | mulz.xyzw vf24, vf01, vf24 - background_vu0_data[0] = c->vfs[vf16].vf; - c->vfs[vf24].vf.mul(Mask::xyzw, c->vfs[vf01].vf, c->vfs[vf24].vf.z()); - // sq.xyzw vf17, 1(vi00) | mulz.xyzw vf25, vf01, vf25 - background_vu0_data[1] = c->vfs[vf17].vf; - c->vfs[vf25].vf.mul(Mask::xyzw, c->vfs[vf01].vf, c->vfs[vf25].vf.z()); - // sq.xyzw vf18, 2(vi00) | mulz.xyzw vf26, vf01, vf26 - background_vu0_data[2] = c->vfs[vf18].vf; - c->vfs[vf26].vf.mul(Mask::xyzw, c->vfs[vf01].vf, c->vfs[vf26].vf.z()); - // sq.xyzw vf19, 3(vi00) | mulz.xyzw vf27, vf01, vf27 - background_vu0_data[3] = c->vfs[vf19].vf; - c->vfs[vf27].vf.mul(Mask::xyzw, c->vfs[vf01].vf, c->vfs[vf27].vf.z()); - // sq.xyzw vf24, 12(vi00) | nop - background_vu0_data[12] = c->vfs[vf24].vf; - // sq.xyzw vf25, 13(vi00) | nop - background_vu0_data[13] = c->vfs[vf25].vf; - // sq.xyzw vf26, 14(vi00) | nop - background_vu0_data[14] = c->vfs[vf26].vf; - // sq.xyzw vf27, 15(vi00) | nop - background_vu0_data[15] = c->vfs[vf27].vf; - // sq.xyzw vf28, 8(vi00) | nop - background_vu0_data[8] = c->vfs[vf28].vf; - // sq.xyzw vf29, 9(vi00) | nop - background_vu0_data[9] = c->vfs[vf29].vf; - // sq.xyzw vf30, 10(vi00) | nop - background_vu0_data[10] = c->vfs[vf30].vf; - // sq.xyzw vf31, 11(vi00) | nop :e - background_vu0_data[11] = c->vfs[vf31].vf; - // iaddiu vi02, vi00, 0xf0 | nop -} - -// clang-format off -u64 execute(void* ctxt) { - auto* c = (ExecutionContext*)ctxt; - init_background(c); - bool bc = false; - u16 vi01 = 0; - - u32 madr, sadr, qwc; - c->daddiu(sp, sp, -128); // daddiu sp, sp, -128 - c->sd(ra, 0, sp); // sd ra, 0(sp) - c->sq(s0, 16, sp); // sq s0, 16(sp) - c->sq(s1, 32, sp); // sq s1, 32(sp) - c->sq(s2, 48, sp); // sq s2, 48(sp) - c->sq(s3, 64, sp); // sq s3, 64(sp) - c->sq(s4, 80, sp); // sq s4, 80(sp) - c->sq(s5, 96, sp); // sq s5, 96(sp) - c->sq(gp, 112, sp); // sq gp, 112(sp) - get_fake_spad_addr(t4, cache.fake_scratchpad_data, 0, c);// lui t4, 28672 - c->lw(v1, 4, a3); // lw v1, 4(a3) - c->lui(t1, 4096); // lui t1, 4096 - c->lui(t2, 4096); // lui t2, 4096 - // sync.l - // cache dxwbin v1, 0 - // sync.l - // cache dxwbin v1, 1 - // sync.l - c->load_symbol(t0, cache.instance_tie_work_copy); // lw t0, *instance-tie-work-copy*(s7) - c->ori(t1, t1, 54272); // ori t1, t1, 54272 SPR TO - c->sw(a3, 396, t0); // sw a3, 396(t0) - c->ori(a3, t2, 53248); // ori a3, t2, 53248 SPR FROM - c->load_symbol(t5, cache.wind_work); // lw t5, *wind-work*(s7) - c->lw(t6, 0, a0); // lw t6, 0(a0) - c->ori(t2, t4, 16); // ori t2, t4, 16 - c->vmax_bc(DEST::xyzw, BC::w, vf1, vf0, vf0); // vmaxw.xyzw vf1, vf0, vf0 - c->addiu(t3, a1, -4); // addiu t3, a1, -4 - c->ori(a1, t4, 4112); // ori a1, t4, 4112 - c->sw(t1, 400, t0); // sw t1, 400(t0) - c->addiu(t9, r0, 0); // addiu t9, r0, 0 - c->sw(a3, 404, t0); // sw a3, 404(t0) - c->mov64(t8, a1); // or t8, a1, r0 - c->sw(t5, 408, t0); // sw t5, 408(t0) - // nop // sll r0, r0, 0 - c->lqc2(vf3, 64, t0); // lqc2 vf3, 64(t0) - // nop // sll r0, r0, 0 - c->sw(r0, 432, t0); // sw r0, 432(t0) - - block_1: - bc = c->sgpr64(t6) != 0; // bne t6, r0, L139 - // nop // sll r0, r0, 0 - if (bc) {goto block_4;} // branch non-likely - - c->addiu(a0, a0, 4); // addiu a0, a0, 4 - c->addiu(t3, t3, 2048); // addiu t3, t3, 2048 - c->daddiu(a2, a2, -32); // daddiu a2, a2, -32 - c->lw(t6, 0, a0); // lw t6, 0(a0) - bc = ((s64)c->sgpr64(a2)) <= 0; // blez a2, L177 - // nop // sll r0, r0, 0 - if (bc) {goto block_70;} // branch non-likely - - //beq r0, r0, L138 // beq r0, r0, L138 - // nop // sll r0, r0, 0 - goto block_1; // branch always - - - block_4: - /* Wait for SPR TO to be free. - c->lw(t4, 0, t1); // lw t4, 0(t1) - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - c->andi(t4, t4, 256); // andi t4, t4, 256 - // nop // sll r0, r0, 0 - bc = c->sgpr64(t4) != 0; // bne t4, r0, L139 - // nop // sll r0, r0, 0 - if (bc) {goto block_4;} // branch non-likely - */ - - // c->sw(t3, 16, t1); // sw t3, 16(t1) - madr = c->sgpr64(t3); - c->xori(t4, t2, 2048); // xori t4, t2, 2048 - // c->sw(t4, 128, t1); // sw t4, 128(t1) - sadr = c->sgpr64(t4); - c->addiu(t4, r0, 128); // addiu t4, r0, 128 - // c->sw(t4, 32, t1); // sw t4, 32(t1) - qwc = c->sgpr64(t4); - c->addiu(t4, r0, 256); // addiu t4, r0, 256 - // c->sw(t4, 0, t1); // sw t4, 0(t1) - spad_to_dma(cache.fake_scratchpad_data, madr, sadr, qwc); - // nop // sll r0, r0, 0 - - block_6: - c->mov64(ra, a0); // or ra, a0, r0 - c->xori(t2, t2, 2048); // xori t2, t2, 2048 - c->daddiu(a0, a0, 4); // daddiu a0, a0, 4 - c->mov64(t7, a0); // or t7, a0, r0 - c->mov64(t4, t2); // or t4, t2, r0 - c->daddiu(t6, a2, -32); // daddiu t6, a2, -32 - bc = ((s64)c->sgpr64(t6)) > 0; // bgtz t6, L142 - c->lw(t6, 0, a0); // lw t6, 0(a0) - if (bc) {goto block_11;} // branch non-likely - - //beq r0, r0, L145 // beq r0, r0, L145 - // nop // sll r0, r0, 0 - goto block_15; // branch always - - // nop // sll r0, r0, 0 - c->lw(v1, 400, r0); // lw v1, 400(r0) - - block_9: - c->daddiu(a2, a2, -32); // daddiu a2, a2, -32 - c->addiu(a0, a0, 4); // addiu a0, a0, 4 - bc = ((s64)c->sgpr64(a2)) <= 0; // blez a2, L145 - c->lw(t6, 0, a0); // lw t6, 0(a0) - if (bc) {goto block_15;} // branch non-likely - - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - - block_11: - bc = c->sgpr64(t6) == 0; // beq t6, r0, L141 - c->addiu(t3, t3, 2048); // addiu t3, t3, 2048 - if (bc) {goto block_9;} // branch non-likely - - /* Wait on DMA TO - block_12: - c->lw(t6, 0, t1); // lw t6, 0(t1) - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - c->andi(t6, t6, 256); // andi t6, t6, 256 - // nop // sll r0, r0, 0 - bc = c->sgpr64(t6) == 0; // beq t6, r0, L144 - // nop // sll r0, r0, 0 - if (bc) {goto block_14;} // branch non-likely - - // nop // sll r0, r0, 0 - c->lw(t6, 444, t0); // lw t6, 444(t0) - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - c->daddiu(t6, t6, 1); // daddiu t6, t6, 1 - // nop // sll r0, r0, 0 - c->sw(t6, 444, t0); // sw t6, 444(t0) - //beq r0, r0, L143 // beq r0, r0, L143 - // nop // sll r0, r0, 0 - goto block_12; // branch always - */ - - - // block_14: - // c->sw(t3, 16, t1); // sw t3, 16(t1) - madr = c->sgpr64(t3); - c->xori(t6, t2, 2048); // xori t6, t2, 2048 - // c->sw(t6, 128, t1); // sw t6, 128(t1) - sadr = c->sgpr64(t6); - c->addiu(t6, r0, 128); // addiu t6, r0, 128 - // c->sw(t6, 32, t1); // sw t6, 32(t1) - qwc = c->sgpr64(t6); - c->addiu(t6, r0, 256); // addiu t6, r0, 256 - //beq r0, r0, L146 // beq r0, r0, L146 - // c->sw(t6, 0, t1); // sw t6, 0(t1) - spad_to_dma(cache.fake_scratchpad_data, madr, sadr, qwc); - goto block_17; // branch always - - - block_15: - /* - c->lw(t6, 0, t1); // lw t6, 0(t1) - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - c->andi(t6, t6, 256); // andi t6, t6, 256 - // nop // sll r0, r0, 0 - bc = c->sgpr64(t6) == 0; // beq t6, r0, L146 - // nop // sll r0, r0, 0 - if (bc) {goto block_17;} // branch non-likely - - // nop // sll r0, r0, 0 - c->lw(t6, 444, t0); // lw t6, 444(t0) - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - c->daddiu(t6, t6, 1); // daddiu t6, t6, 1 - // nop // sll r0, r0, 0 - c->sw(t6, 444, t0); // sw t6, 444(t0) - //beq r0, r0, L145 // beq r0, r0, L145 - // nop // sll r0, r0, 0 - goto block_15; // branch always - */ - - - block_17: - c->lb(t6, 0, ra); // lb t6, 0(ra) - c->addiu(ra, ra, 1); // addiu ra, ra, 1 - // nop // sll r0, r0, 0 - c->sw(ra, 412, t0); // sw ra, 412(t0) - bc = c->sgpr64(t6) != 0; // bne t6, r0, L147 - c->sw(t7, 416, t0); // sw t7, 416(t0) - if (bc) {goto block_19;} // branch non-likely - - c->daddiu(a2, a2, -8); // daddiu a2, a2, -8 - c->addiu(t4, t4, 512); // addiu t4, t4, 512 - //beq r0, r0, L173 // beq r0, r0, L173 - // nop // sll r0, r0, 0 - goto block_62; // branch always - - - block_19: - c->addiu(t7, r0, 128); // addiu t7, r0, 128 - c->lqc2(vf2, 16, t4); // lqc2 vf2, 16(t4) - - block_20: - c->daddiu(ra, t9, -246); // daddiu ra, t9, -246 - // nop // sll r0, r0, 0 - bc = ((s64)c->sgpr64(ra)) <= 0; // blez ra, L151 - // vcallms 42 - vi01 = vcallms_42(c); - if (bc) {goto block_24;} // branch non-likely - - /* - block_21: - c->lw(t8, 0, a3); // lw t8, 0(a3) - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - c->andi(t8, t8, 256); // andi t8, t8, 256 - // nop // sll r0, r0, 0 - bc = c->sgpr64(t8) == 0; // beq t8, r0, L150 - // nop // sll r0, r0, 0 - if (bc) {goto block_23;} // branch non-likely - - // nop // sll r0, r0, 0 - c->lw(t8, 440, t0); // lw t8, 440(t0) - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - c->daddiu(t8, t8, 1); // daddiu t8, t8, 1 - // nop // sll r0, r0, 0 - c->sw(t8, 440, t0); // sw t8, 440(t0) - //beq r0, r0, L149 // beq r0, r0, L149 - // nop // sll r0, r0, 0 - goto block_21; // branch always - */ - - - // block_23: - // c->sw(a1, 128, a3); // sw a1, 128(a3) - sadr = c->sgpr64(a1); - c->xori(a1, a1, 12288); // xori a1, a1, 12288 - // c->sw(v1, 16, a3); // sw v1, 16(a3) - madr = c->sgpr64(v1); - c->sll(t8, t9, 4); // sll t8, t9, 4 - c->addu(v1, v1, t8); // addu v1, v1, t8 - c->mov64(t8, a1); // or t8, a1, r0 - // c->sw(t9, 32, a3); // sw t9, 32(a3) - qwc = c->sgpr64(t9); - c->addiu(t9, r0, 256); // addiu t9, r0, 256 - // c->sw(t9, 0, a3); // sw t9, 0(a3) - spad_from_dma(cache.fake_scratchpad_data, madr, sadr, qwc); - c->addiu(t9, r0, 0); // addiu t9, r0, 0 - - block_24: - // nop // sll r0, r0, 0 - c->lw(ra, 12, t4); // lw ra, 12(t4) - c->and_(gp, t6, t7); // and gp, t6, t7 - c->ld(s5, 56, t4); // ld s5, 56(t4) - bc = c->sgpr64(gp) == 0; // beq gp, r0, L172 - c->ld(s2, 32, t4); // ld s2, 32(t4) - if (bc) {goto block_61;} // branch non-likely - - c->sll(gp, t9, 4); // sll gp, t9, 4 - c->ld(s4, 40, t4); // ld s4, 40(t4) - c->pextlh(s3, s5, r0); // pextlh s3, s5, r0 - c->ld(s5, 48, t4); // ld s5, 48(t4) - c->psraw(s3, s3, 10); // psraw s3, s3, 10 - c->lq(s1, 28, ra); // lq s1, 28(ra) - c->pextlh(s2, s2, r0); // pextlh s2, s2, r0 - c->lq(s0, 44, ra); // lq s0, 44(ra) - c->psraw(s2, s2, 16); // psraw s2, s2, 16 - c->mov128_vf_gpr(vf14, s1); // qmtc2.ni vf14, s1 - c->pextlh(s4, s4, r0); // pextlh s4, s4, r0 - c->mov128_vf_gpr(vf15, s0); // qmtc2.ni vf15, s0 - c->psraw(s4, s4, 16); // psraw s4, s4, 16 - c->mov128_vf_gpr(vf13, s3); // qmtc2.ni vf13, s3 - c->pextlh(s5, s5, r0); // pextlh s5, s5, r0 - c->mov128_vf_gpr(vf10, s2); // qmtc2.ni vf10, s2 - c->psraw(s3, s5, 16); // psraw s3, s5, 16 - c->lhu(s2, 62, t4); // lhu s2, 62(t4) - c->addu(gp, gp, v1); // addu gp, gp, v1 - c->mov128_vf_gpr(vf11, s4); // qmtc2.ni vf11, s4 - c->dsll(s5, s2, 4); // dsll s5, s2, 4 - c->mov128_vf_gpr(vf12, s3); // qmtc2.ni vf12, s3 - c->daddu(s4, s2, t5); // daddu s4, s2, t5 - c->lw(s2, 408, t0); // lw s2, 408(t0) - c->andi(s4, s4, 63); // andi s4, s4, 63 - c->lw(s3, 384, t0); // lw s3, 384(t0) - c->sll(s1, s4, 4); // sll s1, s4, 4 - c->lw(s4, 4, ra); // lw s4, 4(ra) - c->daddu(s5, s3, s5); // daddu s5, s3, s5 - c->addu(s3, s1, s2); // addu s3, s1, s2 - c->andi(s1, s4, 1); // andi s1, s4, 1 - c->andi(s4, s4, 2); // andi s4, s4, 2 - bc = c->sgpr64(s1) != 0; // bne s1, r0, L172 - c->gprs[s1].du64[0] = vi01; // cfc2.ni s1, vi1 - if (bc) {goto block_61;} // branch non-likely - - c->vitof0(DEST::xyzw, vf13, vf13); // vitof0.xyzw vf13, vf13 - c->lw(t5, 1324, s2); // lw t5, 1324(s2) - bc = c->sgpr64(s1) != 0; // bne s1, r0, L172 - c->lqc2(vf25, 112, t0); // lqc2 vf25, 112(t0) - if (bc) {goto block_61;} // branch non-likely - - // nop // sll r0, r0, 0 - c->lqc2(vf16, 16, t0); // lqc2 vf16, 16(t0) - // nop // sll r0, r0, 0 - c->lqc2(vf17, 32, t0); // lqc2 vf17, 32(t0) - c->vmula_bc(DEST::xyzw, BC::z, vf1, vf6); // vmulaz.xyzw acc, vf1, vf6 - c->sw(gp, 196, t0); // sw gp, 196(t0) - c->vmsub_bc(DEST::xyzw, BC::w, vf8, vf1, vf2); // vmsubw.xyzw vf8, vf1, vf2 - c->sw(gp, 276, t0); // sw gp, 276(t0) - c->vadd(DEST::xyz, vf5, vf0, vf0); // vadd.xyz vf5, vf0, vf0 - // nop // sll r0, r0, 0 - c->vadd(DEST::xyz, vf13, vf13, vf2); // vadd.xyz vf13, vf13, vf2 - // nop // sll r0, r0, 0 - // Unknown instr: vmula.xyzw acc, vf1, vf1 - c->vmula(DEST::xyzw, vf1, vf1); - // nop // sll r0, r0, 0 - c->vsub(DEST::xyzw, vf14, vf8, vf14); // vsub.xyzw vf14, vf8, vf14 - // nop // sll r0, r0, 0 - c->vadd_bc(DEST::w, BC::w, vf5, vf5, vf17); // vaddw.w vf5, vf5, vf17 - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - c->lqc2(vf30, 80, t0); // lqc2 vf30, 80(t0) - c->vmini(DEST::xyzw, vf25, vf8, vf25); // vmini.xyzw vf25, vf8, vf25 - // nop // sll r0, r0, 0 - // Unknown instr: vmsub.xyz vf15, vf14, vf15 - c->vmsub(DEST::xyz, vf15, vf14, vf15); - // nop // sll r0, r0, 0 - c->vmini_bc(DEST::w, BC::y, vf5, vf5, vf16); // vminiy.w vf5, vf5, vf16 - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - c->lqc2(vf24, 128, t0); // lqc2 vf24, 128(t0) - // nop // sll r0, r0, 0 - c->sqc2(vf25, 112, t0); // sqc2 vf25, 112(t0) - c->vmini(DEST::xyz, vf15, vf15, vf1); // vmini.xyz vf15, vf15, vf1 - // nop // sll r0, r0, 0 - c->vmax_bc(DEST::w, BC::x, vf5, vf5, vf16); // vmaxx.w vf5, vf5, vf16 - // nop // sll r0, r0, 0 - c->vsub_bc(DEST::xyzw, BC::z, vf16, vf8, vf16); // vsubz.xyzw vf16, vf8, vf16 - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - c->lqc2(vf25, 144, t0); // lqc2 vf25, 144(t0) - // nop // sll r0, r0, 0 - c->lqc2(vf26, 160, t0); // lqc2 vf26, 160(t0) - // nop // sll r0, r0, 0 - c->lqc2(vf27, 176, t0); // lqc2 vf27, 176(t0) - c->vmula_bc(DEST::xyzw, BC::x, vf24, vf2); // vmulax.xyzw acc, vf24, vf2 - // nop // sll r0, r0, 0 - c->vmadda_bc(DEST::xyzw, BC::y, vf25, vf2); // vmadday.xyzw acc, vf25, vf2 - // nop // sll r0, r0, 0 - c->vmadda_bc(DEST::xyzw, BC::z, vf26, vf2); // vmaddaz.xyzw acc, vf26, vf2 - // nop // sll r0, r0, 0 - c->vmsuba_bc(DEST::xyzw, BC::w, vf27, vf0); // vmsubaw.xyzw acc, vf27, vf0 - // nop // sll r0, r0, 0 - c->vmsub_bc(DEST::xyzw, BC::w, vf24, vf1, vf2); // vmsubw.xyzw vf24, vf1, vf2 - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - c->mov128_gpr_vf(s2, vf16); // qmfc2.i s2, vf16 - c->vmul_bc(DEST::xyzw, BC::w, vf28, vf15, vf30); // vmulw.xyzw vf28, vf15, vf30 - // nop // sll r0, r0, 0 - c->vmul_bc(DEST::xyzw, BC::w, vf29, vf15, vf30); // vmulw.xyzw vf29, vf15, vf30 - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - c->lqc2(vf19, 0, t0); // lqc2 vf19, 0(t0) - c->vitof12(DEST::xyzw, vf10, vf10); // vitof12.xyzw vf10, vf10 - // nop // sll r0, r0, 0 - c->pcgtw(s1, r0, s2); // pcgtw s1, r0, s2 - c->mov128_gpr_vf(s0, vf24); // qmfc2.i s0, vf24 - c->vmul_bc(DEST::xyzw, BC::x, vf28, vf1, vf28); // vmulx.xyzw vf28, vf1, vf28 - // nop // sll r0, r0, 0 - c->vmul_bc(DEST::xyzw, BC::z, vf29, vf1, vf29); // vmulz.xyzw vf29, vf1, vf29 - c->lw(s2, 56, ra); // lw s2, 56(ra) - c->pcgtw(s0, r0, s0); // pcgtw s0, r0, s0 - c->sqc2(vf5, 80, t8); // sqc2 vf5, 80(t8) - c->ppach(s0, r0, s0); // ppach s0, r0, s0 - c->sw(s4, 80, t8); // sw s4, 80(t8) - c->or_(s1, s0, s1); // or s1, s0, s1 - c->sqc2(vf14, 96, t0); // sqc2 vf14, 96(t0) - c->ppacb(s1, r0, s1); // ppacb s1, r0, s1 - c->mfc1(r0, f31); // mfc1 r0, f31 - bc = c->sgpr64(s2) == 0; // beq s2, r0, L153 - c->sw(s1, 84, t8); // sw s1, 84(t8) - if (bc) {goto block_33;} // branch non-likely - - c->vftoi0(DEST::zw, vf28, vf28); // vftoi0.zw vf28, vf28 - c->ld(s1, 8, s5); // ld s1, 8(s5) - c->vftoi0(DEST::zw, vf29, vf29); // vftoi0.zw vf29, vf29 - c->ld(s2, 0, s5); // ld s2, 0(s5) - c->pextlw(s1, r0, s1); // pextlw s1, r0, s1 - c->lqc2(vf16, 12, s3); // lqc2 vf16, 12(s3) - c->pextlw(s3, r0, s2); // pextlw s3, r0, s2 - c->mov128_vf_gpr(vf18, s1); // qmtc2.i vf18, s1 - // nop // sll r0, r0, 0 - c->mov128_vf_gpr(vf17, s3); // qmtc2.i vf17, s3 - // Unknown instr: vmula.xyzw acc, vf16, vf1 - c->vmula(DEST::xyzw, vf16, vf1); - // nop // sll r0, r0, 0 - c->vmsuba_bc(DEST::xyzw, BC::x, vf18, vf19); // vmsubax.xyzw acc, vf18, vf19 - // nop // sll r0, r0, 0 - c->vmsub_bc(DEST::xyzw, BC::y, vf16, vf17, vf19); // vmsuby.xyzw vf16, vf17, vf19 - // nop // sll r0, r0, 0 - c->vsub_bc(DEST::x, BC::x, vf28, vf30, vf15); // vsubx.x vf28, vf30, vf15 - // nop // sll r0, r0, 0 - c->vsub_bc(DEST::x, BC::z, vf29, vf1, vf15); // vsubz.x vf29, vf1, vf15 - // nop // sll r0, r0, 0 - c->vitof0(DEST::zw, vf28, vf28); // vitof0.zw vf28, vf28 - // nop // sll r0, r0, 0 - c->vmula_bc(DEST::xyzw, BC::z, vf16, vf19); // vmulaz.xyzw acc, vf16, vf19 - // nop // sll r0, r0, 0 - c->vmadd(DEST::xyzw, vf18, vf1, vf18); // vmadd.xyzw vf18, vf1, vf18 - // nop // sll r0, r0, 0 - c->vitof0(DEST::zw, vf29, vf29); // vitof0.zw vf29, vf29 - // nop // sll r0, r0, 0 - c->vadd_bc(DEST::y, BC::y, vf28, vf0, vf0); // vaddy.y vf28, vf0, vf0 - // nop // sll r0, r0, 0 - c->vadd_bc(DEST::y, BC::y, vf29, vf0, vf0); // vaddy.y vf29, vf0, vf0 - // nop // sll r0, r0, 0 - c->vmula_bc(DEST::xyzw, BC::z, vf18, vf19); // vmulaz.xyzw acc, vf18, vf19 - // nop // sll r0, r0, 0 - c->vmadd(DEST::xyzw, vf17, vf17, vf1); // vmadd.xyzw vf17, vf17, vf1 - // nop // sll r0, r0, 0 - c->vitof12(DEST::xyzw, vf11, vf11); // vitof12.xyzw vf11, vf11 - // nop // sll r0, r0, 0 - c->vitof12(DEST::xyzw, vf12, vf12); // vitof12.xyzw vf12, vf12 - // nop // sll r0, r0, 0 - c->vsub_bc(DEST::w, BC::w, vf28, vf30, vf28); // vsubw.w vf28, vf30, vf28 - // nop // sll r0, r0, 0 - c->vmini_bc(DEST::xyzw, BC::w, vf17, vf17, vf0); // vminiw.xyzw vf17, vf17, vf0 - // nop // sll r0, r0, 0 - c->vsub_bc(DEST::w, BC::w, vf29, vf30, vf29); // vsubw.w vf29, vf30, vf29 - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - c->mov128_gpr_vf(s3, vf18); // qmfc2.i s3, vf18 - c->vmax_bc(DEST::xyzw, BC::w, vf27, vf17, vf19); // vmaxw.xyzw vf27, vf17, vf19 - // nop // sll r0, r0, 0 - c->ppacw(s3, r0, s3); // ppacw s3, r0, s3 - c->mfc1(r0, f31); // mfc1 r0, f31 - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - c->vmul_bc(DEST::xyzw, BC::w, vf27, vf27, vf15); // vmulw.xyzw vf27, vf27, vf15 - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - c->vmula_bc(DEST::yw, BC::x, vf0, vf0); // vmulax.yw acc, vf0, vf0 - // nop // sll r0, r0, 0 - c->vmula_bc(DEST::xz, BC::y, vf27, vf10); // vmulay.xz acc, vf27, vf10 - // nop // sll r0, r0, 0 - c->vmadd(DEST::xyzw, vf10, vf1, vf10); // vmadd.xyzw vf10, vf1, vf10 - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - c->mov128_gpr_vf(s2, vf27); // qmfc2.i s2, vf27 - c->vmula_bc(DEST::yw, BC::x, vf0, vf0); // vmulax.yw acc, vf0, vf0 - c->lw(s1, 436, t0); // lw s1, 436(t0) - c->vmula_bc(DEST::xz, BC::y, vf27, vf11); // vmulay.xz acc, vf27, vf11 - // nop // sll r0, r0, 0 - c->vmadd(DEST::xyzw, vf11, vf1, vf11); // vmadd.xyzw vf11, vf1, vf11 - // nop // sll r0, r0, 0 - bc = c->sgpr64(s1) != c->sgpr64(s7); // bne s1, s7, L152 - c->ppacw(s2, r0, s2); // ppacw s2, r0, s2 - if (bc) {goto block_31;} // branch non-likely - - c->vmula_bc(DEST::yw, BC::x, vf0, vf0); // vmulax.yw acc, vf0, vf0 - c->sd(s3, 8, s5); // sd s3, 8(s5) - c->vmula_bc(DEST::xz, BC::y, vf27, vf12); // vmulay.xz acc, vf27, vf12 - c->sd(s2, 0, s5); // sd s2, 0(s5) - bc = c->sgpr64(s4) != 0; // bne s4, r0, L164 - c->vmadd(DEST::xyzw, vf12, vf1, vf12); // vmadd.xyzw vf12, vf1, vf12 - if (bc) {goto block_49;} // branch non-likely - - //beq r0, r0, L154 // beq r0, r0, L154 - // nop // sll r0, r0, 0 - goto block_34; // branch always - - - block_31: - c->vmula_bc(DEST::yw, BC::x, vf0, vf0); // vmulax.yw acc, vf0, vf0 - // nop // sll r0, r0, 0 - c->vmula_bc(DEST::xz, BC::y, vf27, vf12); // vmulay.xz acc, vf27, vf12 - // nop // sll r0, r0, 0 - bc = c->sgpr64(s4) != 0; // bne s4, r0, L164 - c->vmadd(DEST::xyzw, vf12, vf1, vf12); // vmadd.xyzw vf12, vf1, vf12 - if (bc) {goto block_49;} // branch non-likely - - //beq r0, r0, L154 // beq r0, r0, L154 - // nop // sll r0, r0, 0 - goto block_34; // branch always - - - block_33: - c->vftoi0(DEST::zw, vf28, vf28); // vftoi0.zw vf28, vf28 - // nop // sll r0, r0, 0 - c->vftoi0(DEST::zw, vf29, vf29); // vftoi0.zw vf29, vf29 - // nop // sll r0, r0, 0 - c->vsub_bc(DEST::x, BC::x, vf28, vf30, vf15); // vsubx.x vf28, vf30, vf15 - // nop // sll r0, r0, 0 - c->vsub_bc(DEST::x, BC::z, vf29, vf1, vf15); // vsubz.x vf29, vf1, vf15 - // nop // sll r0, r0, 0 - c->vitof0(DEST::zw, vf28, vf28); // vitof0.zw vf28, vf28 - // nop // sll r0, r0, 0 - c->vitof0(DEST::zw, vf29, vf29); // vitof0.zw vf29, vf29 - // nop // sll r0, r0, 0 - c->vadd_bc(DEST::y, BC::y, vf28, vf0, vf0); // vaddy.y vf28, vf0, vf0 - // nop // sll r0, r0, 0 - c->vadd_bc(DEST::y, BC::y, vf29, vf0, vf0); // vaddy.y vf29, vf0, vf0 - // nop // sll r0, r0, 0 - c->vsub_bc(DEST::w, BC::w, vf28, vf30, vf28); // vsubw.w vf28, vf30, vf28 - // nop // sll r0, r0, 0 - c->vsub_bc(DEST::w, BC::w, vf29, vf30, vf29); // vsubw.w vf29, vf30, vf29 - // nop // sll r0, r0, 0 - c->vitof12(DEST::xyzw, vf11, vf11); // vitof12.xyzw vf11, vf11 - // nop // sll r0, r0, 0 - bc = c->sgpr64(s4) != 0; // bne s4, r0, L164 - c->vitof12(DEST::xyzw, vf12, vf12); // vitof12.xyzw vf12, vf12 - if (bc) {goto block_49;} // branch non-likely - - - block_34: - // nop // sll r0, r0, 0 - c->lw(s5, 84, t8); // lw s5, 84(t8) - // nop // sll r0, r0, 0 - c->lw(s4, 108, t0); // lw s4, 108(t0) - c->addiu(t9, t9, 6); // addiu t9, t9, 6 - c->lw(s3, 104, t0); // lw s3, 104(t0) - bc = c->sgpr64(s5) != 0; // bne s5, r0, L158 - c->vsub_bc(DEST::w, BC::w, vf10, vf10, vf10); // vsubw.w vf10, vf10, vf10 - if (bc) {goto block_41;} // branch non-likely - - bc = ((s64)c->sgpr64(s4)) > 0; // bgtz s4, L156 - // nop // sll r0, r0, 0 - if (bc) {goto block_39;} // branch non-likely - - bc = ((s64)c->sgpr64(s3)) > 0; // bgtz s3, L155 - // nop // sll r0, r0, 0 - if (bc) {goto block_38;} // branch non-likely - - // nop // sll r0, r0, 0 - c->lh(s4, 78, ra); // lh s4, 78(ra) - // nop // sll r0, r0, 0 - c->lw(s5, 64, ra); // lw s5, 64(ra) - c->daddiu(s4, s4, 1); // daddiu s4, s4, 1 - c->sqc2(vf28, 64, t8); // sqc2 vf28, 64(t8) - c->vmula_bc(DEST::xyzw, BC::x, vf20, vf10); // vmulax.xyzw acc, vf20, vf10 - c->addiu(gp, gp, 96); // addiu gp, gp, 96 - c->vmadda_bc(DEST::xyzw, BC::y, vf21, vf10); // vmadday.xyzw acc, vf21, vf10 - c->sw(gp, 64, ra); // sw gp, 64(ra) - c->vmadd_bc(DEST::xyzw, BC::z, vf10, vf22, vf10); // vmaddz.xyzw vf10, vf22, vf10 - c->sh(s4, 78, ra); // sh s4, 78(ra) - c->vmula_bc(DEST::xyzw, BC::x, vf20, vf11); // vmulax.xyzw acc, vf20, vf11 - c->lbu(s4, 109, ra); // lbu s4, 109(ra) - c->vmadda_bc(DEST::xyzw, BC::y, vf21, vf11); // vmadday.xyzw acc, vf21, vf11 - c->lhu(gp, 118, ra); // lhu gp, 118(ra) - c->vmadd_bc(DEST::xyzw, BC::z, vf11, vf22, vf11); // vmaddz.xyzw vf11, vf22, vf11 - c->lbu(s3, 113, ra); // lbu s3, 113(ra) - //beq r0, r0, L157 // beq r0, r0, L157 - // nop // sll r0, r0, 0 - goto block_40; // branch always - - - block_38: - // nop // sll r0, r0, 0 - c->lh(s4, 80, ra); // lh s4, 80(ra) - // nop // sll r0, r0, 0 - c->lw(s5, 68, ra); // lw s5, 68(ra) - c->daddiu(s4, s4, 1); // daddiu s4, s4, 1 - c->sqc2(vf29, 64, t8); // sqc2 vf29, 64(t8) - c->vmula_bc(DEST::xyzw, BC::x, vf20, vf10); // vmulax.xyzw acc, vf20, vf10 - c->addiu(gp, gp, 96); // addiu gp, gp, 96 - c->vmadda_bc(DEST::xyzw, BC::y, vf21, vf10); // vmadday.xyzw acc, vf21, vf10 - c->sw(gp, 68, ra); // sw gp, 68(ra) - c->vmadd_bc(DEST::xyzw, BC::z, vf10, vf22, vf10); // vmaddz.xyzw vf10, vf22, vf10 - c->sh(s4, 80, ra); // sh s4, 80(ra) - c->vmula_bc(DEST::xyzw, BC::x, vf20, vf11); // vmulax.xyzw acc, vf20, vf11 - c->lbu(s4, 110, ra); // lbu s4, 110(ra) - c->vmadda_bc(DEST::xyzw, BC::y, vf21, vf11); // vmadday.xyzw acc, vf21, vf11 - c->lhu(gp, 120, ra); // lhu gp, 120(ra) - c->vmadd_bc(DEST::xyzw, BC::z, vf11, vf22, vf11); // vmaddz.xyzw vf11, vf22, vf11 - c->lbu(s3, 114, ra); // lbu s3, 114(ra) - //beq r0, r0, L157 // beq r0, r0, L157 - // nop // sll r0, r0, 0 - goto block_40; // branch always - - - block_39: - // nop // sll r0, r0, 0 - c->lh(s4, 82, ra); // lh s4, 82(ra) - // nop // sll r0, r0, 0 - c->lw(s5, 72, ra); // lw s5, 72(ra) - c->daddiu(s4, s4, 1); // daddiu s4, s4, 1 - c->sqc2(vf30, 64, t8); // sqc2 vf30, 64(t8) - c->vmula_bc(DEST::xyzw, BC::x, vf20, vf10); // vmulax.xyzw acc, vf20, vf10 - c->addiu(gp, gp, 96); // addiu gp, gp, 96 - c->vmadda_bc(DEST::xyzw, BC::y, vf21, vf10); // vmadday.xyzw acc, vf21, vf10 - c->sw(gp, 72, ra); // sw gp, 72(ra) - c->vmadd_bc(DEST::xyzw, BC::z, vf10, vf22, vf10); // vmaddz.xyzw vf10, vf22, vf10 - c->sh(s4, 82, ra); // sh s4, 82(ra) - c->vmula_bc(DEST::xyzw, BC::x, vf20, vf11); // vmulax.xyzw acc, vf20, vf11 - c->lbu(s4, 111, ra); // lbu s4, 111(ra) - c->vmadda_bc(DEST::xyzw, BC::y, vf21, vf11); // vmadday.xyzw acc, vf21, vf11 - c->lhu(gp, 122, ra); // lhu gp, 122(ra) - c->vmadd_bc(DEST::xyzw, BC::z, vf11, vf22, vf11); // vmaddz.xyzw vf11, vf22, vf11 - c->lbu(s3, 115, ra); // lbu s3, 115(ra) - - block_40: - c->vmula_bc(DEST::xyzw, BC::x, vf20, vf12); // vmulax.xyzw acc, vf20, vf12 - c->lq(s2, 224, t0); // lq s2, 224(t0) - c->vmadda_bc(DEST::xyzw, BC::y, vf21, vf12); // vmadday.xyzw acc, vf21, vf12 - c->lq(s1, 240, t0); // lq s1, 240(t0) - c->vmadd_bc(DEST::xyzw, BC::z, vf12, vf22, vf12); // vmaddz.xyzw vf12, vf22, vf12 - c->dsll(gp, gp, 4); // dsll gp, gp, 4 - c->vmula_bc(DEST::xyzw, BC::x, vf20, vf13); // vmulax.xyzw acc, vf20, vf13 - c->daddu(s3, s3, ra); // daddu s3, s3, ra - c->vmadda_bc(DEST::xyzw, BC::y, vf21, vf13); // vmadday.xyzw acc, vf21, vf13 - // nop // sll r0, r0, 0 - c->vmadda_bc(DEST::xyzw, BC::z, vf22, vf13); // vmaddaz.xyzw acc, vf22, vf13 - // nop // sll r0, r0, 0 - c->vmadd_bc(DEST::xyzw, BC::w, vf13, vf23, vf0); // vmaddw.xyzw vf13, vf23, vf0 - // nop // sll r0, r0, 0 - c->sqc2(vf10, 0, t8); // sqc2 vf10, 0(t8) - // nop // sll r0, r0, 0 - c->sqc2(vf11, 16, t8); // sqc2 vf11, 16(t8) - c->movz(s2, s1, s5); // movz s2, s1, s5 - c->sqc2(vf12, 32, t8); // sqc2 vf12, 32(t8) - c->daddiu(t8, t8, 96); // daddiu t8, t8, 96 - //beq r0, r0, L159 // beq r0, r0, L159 - c->sqc2(vf13, -48, t8); // sqc2 vf13, -48(t8) - goto block_42; // branch always - - - block_41: - // nop // sll r0, r0, 0 - c->lqc2(vf24, 320, t0); // lqc2 vf24, 320(t0) - // nop // sll r0, r0, 0 - c->lqc2(vf25, 336, t0); // lqc2 vf25, 336(t0) - // nop // sll r0, r0, 0 - c->lqc2(vf26, 352, t0); // lqc2 vf26, 352(t0) - // nop // sll r0, r0, 0 - c->lqc2(vf27, 368, t0); // lqc2 vf27, 368(t0) - // nop // sll r0, r0, 0 - c->lh(s4, 76, ra); // lh s4, 76(ra) - // nop // sll r0, r0, 0 - c->lw(s5, 60, ra); // lw s5, 60(ra) - c->daddiu(s4, s4, 1); // daddiu s4, s4, 1 - c->sqc2(vf28, 64, t8); // sqc2 vf28, 64(t8) - c->vmula_bc(DEST::xyzw, BC::x, vf24, vf10); // vmulax.xyzw acc, vf24, vf10 - c->addiu(gp, gp, 96); // addiu gp, gp, 96 - c->vmadda_bc(DEST::xyzw, BC::y, vf25, vf10); // vmadday.xyzw acc, vf25, vf10 - c->sw(gp, 60, ra); // sw gp, 60(ra) - c->vmadd_bc(DEST::xyzw, BC::z, vf10, vf26, vf10); // vmaddz.xyzw vf10, vf26, vf10 - c->sh(s4, 76, ra); // sh s4, 76(ra) - c->vmula_bc(DEST::xyzw, BC::x, vf24, vf11); // vmulax.xyzw acc, vf24, vf11 - c->lbu(s4, 108, ra); // lbu s4, 108(ra) - c->vmadda_bc(DEST::xyzw, BC::y, vf25, vf11); // vmadday.xyzw acc, vf25, vf11 - c->lhu(gp, 116, ra); // lhu gp, 116(ra) - c->vmadd_bc(DEST::xyzw, BC::z, vf11, vf26, vf11); // vmaddz.xyzw vf11, vf26, vf11 - c->lbu(s3, 112, ra); // lbu s3, 112(ra) - c->vmula_bc(DEST::xyzw, BC::x, vf24, vf12); // vmulax.xyzw acc, vf24, vf12 - c->lq(s2, 224, t0); // lq s2, 224(t0) - c->vmadda_bc(DEST::xyzw, BC::y, vf25, vf12); // vmadday.xyzw acc, vf25, vf12 - c->lq(s1, 240, t0); // lq s1, 240(t0) - c->vmadd_bc(DEST::xyzw, BC::z, vf12, vf26, vf12); // vmaddz.xyzw vf12, vf26, vf12 - c->dsll(gp, gp, 4); // dsll gp, gp, 4 - c->vmula_bc(DEST::xyzw, BC::x, vf24, vf13); // vmulax.xyzw acc, vf24, vf13 - c->daddu(s3, s3, ra); // daddu s3, s3, ra - c->vmadda_bc(DEST::xyzw, BC::y, vf25, vf13); // vmadday.xyzw acc, vf25, vf13 - // nop // sll r0, r0, 0 - c->vmadda_bc(DEST::xyzw, BC::z, vf26, vf13); // vmaddaz.xyzw acc, vf26, vf13 - // nop // sll r0, r0, 0 - c->vmadd_bc(DEST::xyzw, BC::w, vf13, vf27, vf0); // vmaddw.xyzw vf13, vf27, vf0 - // nop // sll r0, r0, 0 - c->sqc2(vf10, 0, t8); // sqc2 vf10, 0(t8) - // nop // sll r0, r0, 0 - c->sqc2(vf11, 16, t8); // sqc2 vf11, 16(t8) - // nop // sll r0, r0, 0 - c->sqc2(vf12, 32, t8); // sqc2 vf12, 32(t8) - c->movz(s2, s1, s5); // movz s2, s1, s5 - c->sqc2(vf13, 48, t8); // sqc2 vf13, 48(t8) - c->daddiu(t8, t8, 96); // daddiu t8, t8, 96 - - block_42: - // nop // sll r0, r0, 0 - c->lw(ra, 8, t4); // lw ra, 8(t4) - // nop // sll r0, r0, 0 - c->sq(s2, 256, t0); // sq s2, 256(t0) - // nop // sll r0, r0, 0 - c->lbu(s2, 144, s3); // lbu s2, 144(s3) - c->addu(s1, gp, ra); // addu s1, gp, ra - c->sw(s5, 260, t0); // sw s5, 260(t0) - c->daddiu(t9, t9, 3); // daddiu t9, t9, 3 - c->sw(s1, 212, t0); // sw s1, 212(t0) - c->sll(s1, s2, 2); // sll s1, s2, 2 - c->sh(s2, 208, t0); // sh s2, 208(t0) - c->sll(s2, s2, 4); // sll s2, s2, 4 - c->sb(s1, 222, t0); // sb s1, 222(t0) - c->daddu(gp, gp, s2); // daddu gp, gp, s2 - c->lq(s2, 192, t0); // lq s2, 192(t0) - c->daddiu(s5, s5, 48); // daddiu s5, s5, 48 - c->lq(s1, 208, t0); // lq s1, 208(t0) - c->daddiu(t8, t8, 48); // daddiu t8, t8, 48 - c->lq(s0, 256, t0); // lq s0, 256(t0) - c->daddiu(s3, s3, 1); // daddiu s3, s3, 1 - c->sq(s2, -48, t8); // sq s2, -48(t8) - c->daddiu(s4, s4, -1); // daddiu s4, s4, -1 - c->sq(s1, -32, t8); // sq s1, -32(t8) - bc = ((s64)c->sgpr64(s4)) <= 0; // blez s4, L172 - c->sq(s0, -16, t8); // sq s0, -16(t8) - if (bc) {goto block_61;} // branch non-likely - - - block_43: - c->daddiu(s2, t9, -252); // daddiu s2, t9, -252 - // nop // sll r0, r0, 0 - bc = ((s64)c->sgpr64(s2)) <= 0; // blez s2, L163 - // nop // sll r0, r0, 0 - if (bc) {goto block_47;} // branch non-likely - - /* - block_44: - c->lw(t8, 0, a3); // lw t8, 0(a3) - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - c->andi(t8, t8, 256); // andi t8, t8, 256 - // nop // sll r0, r0, 0 - bc = c->sgpr64(t8) == 0; // beq t8, r0, L162 - // nop // sll r0, r0, 0 - if (bc) {goto block_46;} // branch non-likely - - // nop // sll r0, r0, 0 - c->lw(t8, 440, t0); // lw t8, 440(t0) - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - c->daddiu(t8, t8, 1); // daddiu t8, t8, 1 - // nop // sll r0, r0, 0 - c->sw(t8, 440, t0); // sw t8, 440(t0) - //beq r0, r0, L161 // beq r0, r0, L161 - // nop // sll r0, r0, 0 - goto block_44; // branch always - */ - - - // block_46: - // c->sw(a1, 128, a3); // sw a1, 128(a3) - sadr = c->sgpr64(a1); - c->xori(a1, a1, 12288); // xori a1, a1, 12288 - // c->sw(v1, 16, a3); // sw v1, 16(a3) - madr = c->sgpr64(v1); - c->sll(t8, t9, 4); // sll t8, t9, 4 - c->addu(v1, v1, t8); // addu v1, v1, t8 - c->mov64(t8, a1); // or t8, a1, r0 - // c->sw(t9, 32, a3); // sw t9, 32(a3) - qwc = c->sgpr64(t9); - c->addiu(t9, r0, 256); // addiu t9, r0, 256 - // c->sw(t9, 0, a3); // sw t9, 0(a3) - spad_from_dma(cache.fake_scratchpad_data, madr, sadr, qwc); - c->addiu(t9, r0, 0); // addiu t9, r0, 0 - - block_47: - // nop // sll r0, r0, 0 - c->lbu(s2, 144, s3); // lbu s2, 144(s3) - c->addu(s1, gp, ra); // addu s1, gp, ra - c->sw(s5, 260, t0); // sw s5, 260(t0) - c->daddiu(t9, t9, 3); // daddiu t9, t9, 3 - c->sw(s1, 212, t0); // sw s1, 212(t0) - c->sll(s1, s2, 2); // sll s1, s2, 2 - c->sh(s2, 208, t0); // sh s2, 208(t0) - c->sll(s2, s2, 4); // sll s2, s2, 4 - c->sb(s1, 222, t0); // sb s1, 222(t0) - c->daddu(gp, gp, s2); // daddu gp, gp, s2 - c->lq(s2, 192, t0); // lq s2, 192(t0) - c->daddiu(s5, s5, 48); // daddiu s5, s5, 48 - c->lq(s1, 208, t0); // lq s1, 208(t0) - c->daddiu(t8, t8, 48); // daddiu t8, t8, 48 - c->lq(s0, 256, t0); // lq s0, 256(t0) - c->daddiu(s3, s3, 1); // daddiu s3, s3, 1 - c->sq(s2, -48, t8); // sq s2, -48(t8) - c->daddiu(s4, s4, -1); // daddiu s4, s4, -1 - c->sq(s1, -32, t8); // sq s1, -32(t8) - bc = ((s64)c->sgpr64(s4)) > 0; // bgtz s4, L160 - c->sq(s0, -16, t8); // sq s0, -16(t8) - if (bc) {goto block_43;} // branch non-likely - - //beq r0, r0, L172 // beq r0, r0, L172 - // nop // sll r0, r0, 0 - goto block_61; // branch always - - - block_49: - c->vmul(DEST::xyz, vf16, vf6, vf6); // vmul.xyz vf16, vf6, vf6 - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - c->lqc2(vf9, 124, ra); // lqc2 vf9, 124(ra) - c->vsub_bc(DEST::w, BC::w, vf10, vf10, vf10); // vsubw.w vf10, vf10, vf10 - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - c->vadda_bc(DEST::x, BC::y, vf16, vf16); // vadday.x acc, vf16, vf16 - // nop // sll r0, r0, 0 - c->vmadd_bc(DEST::x, BC::z, vf16, vf1, vf16); // vmaddz.x vf16, vf1, vf16 - // nop // sll r0, r0, 0 - c->vsqrt(vf16, BC::x); // vsqrt Q, vf16.x - // nop // sll r0, r0, 0 - c->vmula_bc(DEST::xyzw, BC::y, vf1, vf9); // vmulay.xyzw acc, vf1, vf9 - // nop // sll r0, r0, 0 - c->vmadda_bc(DEST::xyzw, BC::w, vf1, vf2); // vmaddaw.xyzw acc, vf1, vf2 - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - c->vwaitq(); // vwaitq - // Unknown instr: vmsubq.xyzw vf16, vf1, Q - c->vmsubq(DEST::xyzw, vf16, vf1); - // nop // sll r0, r0, 0 - c->vmul_bc(DEST::xyzw, BC::x, vf16, vf16, vf9); // vmulx.xyzw vf16, vf16, vf9 - // nop // sll r0, r0, 0 - c->vmax_bc(DEST::x, BC::x, vf16, vf16, vf0); // vmaxx.x vf16, vf16, vf0 - // nop // sll r0, r0, 0 - c->vmini_bc(DEST::x, BC::y, vf16, vf16, vf3); // vminiy.x vf16, vf16, vf3 - // nop // sll r0, r0, 0 - c->vftoi0(DEST::xyzw, vf16, vf16); // vftoi0.xyzw vf16, vf16 - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - c->mov128_gpr_vf(s5, vf16); // qmfc2.i s5, vf16 - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - c->andi(s5, s5, 255); // andi s5, s5, 255 - // nop // sll r0, r0, 0 - bc = c->sgpr64(s5) == 0; // beq s5, r0, L154 - // nop // sll r0, r0, 0 - if (bc) {goto block_34;} // branch non-likely - - // Unknown instr: vcallms 29 - vcallms_29(c); - c->sw(s4, 432, t0); // sw s4, 432(t0) - // nop // sll r0, r0, 0 - c->lw(s4, 108, t0); // lw s4, 108(t0) - c->addiu(t9, t9, 6); // addiu t9, t9, 6 - c->lw(s3, 104, t0); // lw s3, 104(t0) - // nop // sll r0, r0, 0 - c->sw(s5, 80, t8); // sw s5, 80(t8) - bc = ((s64)c->sgpr64(s4)) > 0; // bgtz s4, L166 - // nop // sll r0, r0, 0 - if (bc) {goto block_54;} // branch non-likely - - bc = ((s64)c->sgpr64(s3)) > 0; // bgtz s3, L165 - // nop // sll r0, r0, 0 - if (bc) {goto block_53;} // branch non-likely - - // nop // sll r0, r0, 0 - c->lh(s4, 86, ra); // lh s4, 86(ra) - // nop // sll r0, r0, 0 - c->lw(s5, 96, ra); // lw s5, 96(ra) - c->daddiu(s4, s4, 1); // daddiu s4, s4, 1 - c->sqc2(vf28, 64, t8); // sqc2 vf28, 64(t8) - c->vmula_bc(DEST::xyzw, BC::x, vf24, vf10); // vmulax.xyzw acc, vf24, vf10 - c->addiu(gp, gp, 96); // addiu gp, gp, 96 - c->vmadda_bc(DEST::xyzw, BC::y, vf25, vf10); // vmadday.xyzw acc, vf25, vf10 - c->sw(gp, 96, ra); // sw gp, 96(ra) - c->vmadd_bc(DEST::xyzw, BC::z, vf10, vf26, vf10); // vmaddz.xyzw vf10, vf26, vf10 - c->sh(s4, 86, ra); // sh s4, 86(ra) - c->vmula_bc(DEST::xyzw, BC::x, vf24, vf11); // vmulax.xyzw acc, vf24, vf11 - c->lbu(s3, 109, ra); // lbu s3, 109(ra) - c->vmadda_bc(DEST::xyzw, BC::y, vf25, vf11); // vmadday.xyzw acc, vf25, vf11 - c->lhu(gp, 118, ra); // lhu gp, 118(ra) - c->vmadd_bc(DEST::xyzw, BC::z, vf11, vf26, vf11); // vmaddz.xyzw vf11, vf26, vf11 - c->lbu(s4, 113, ra); // lbu s4, 113(ra) - //beq r0, r0, L167 // beq r0, r0, L167 - // nop // sll r0, r0, 0 - goto block_55; // branch always - - - block_53: - // nop // sll r0, r0, 0 - c->lh(s4, 88, ra); // lh s4, 88(ra) - // nop // sll r0, r0, 0 - c->lw(s5, 100, ra); // lw s5, 100(ra) - c->daddiu(s4, s4, 1); // daddiu s4, s4, 1 - c->sqc2(vf29, 64, t8); // sqc2 vf29, 64(t8) - c->vmula_bc(DEST::xyzw, BC::x, vf24, vf10); // vmulax.xyzw acc, vf24, vf10 - c->addiu(gp, gp, 96); // addiu gp, gp, 96 - c->vmadda_bc(DEST::xyzw, BC::y, vf25, vf10); // vmadday.xyzw acc, vf25, vf10 - c->sw(gp, 100, ra); // sw gp, 100(ra) - c->vmadd_bc(DEST::xyzw, BC::z, vf10, vf26, vf10); // vmaddz.xyzw vf10, vf26, vf10 - c->sh(s4, 88, ra); // sh s4, 88(ra) - c->vmula_bc(DEST::xyzw, BC::x, vf24, vf11); // vmulax.xyzw acc, vf24, vf11 - c->lbu(s3, 110, ra); // lbu s3, 110(ra) - c->vmadda_bc(DEST::xyzw, BC::y, vf25, vf11); // vmadday.xyzw acc, vf25, vf11 - c->lhu(gp, 120, ra); // lhu gp, 120(ra) - c->vmadd_bc(DEST::xyzw, BC::z, vf11, vf26, vf11); // vmaddz.xyzw vf11, vf26, vf11 - c->lbu(s4, 114, ra); // lbu s4, 114(ra) - //beq r0, r0, L167 // beq r0, r0, L167 - // nop // sll r0, r0, 0 - goto block_55; // branch always - - - block_54: - // nop // sll r0, r0, 0 - c->lh(s4, 90, ra); // lh s4, 90(ra) - // nop // sll r0, r0, 0 - c->lw(s5, 104, ra); // lw s5, 104(ra) - c->daddiu(s4, s4, 1); // daddiu s4, s4, 1 - c->sqc2(vf30, 64, t8); // sqc2 vf30, 64(t8) - c->vmula_bc(DEST::xyzw, BC::x, vf24, vf10); // vmulax.xyzw acc, vf24, vf10 - c->addiu(gp, gp, 96); // addiu gp, gp, 96 - c->vmadda_bc(DEST::xyzw, BC::y, vf25, vf10); // vmadday.xyzw acc, vf25, vf10 - c->sw(gp, 104, ra); // sw gp, 104(ra) - c->vmadd_bc(DEST::xyzw, BC::z, vf10, vf26, vf10); // vmaddz.xyzw vf10, vf26, vf10 - c->sh(s4, 90, ra); // sh s4, 90(ra) - c->vmula_bc(DEST::xyzw, BC::x, vf24, vf11); // vmulax.xyzw acc, vf24, vf11 - c->lbu(s3, 111, ra); // lbu s3, 111(ra) - c->vmadda_bc(DEST::xyzw, BC::y, vf25, vf11); // vmadday.xyzw acc, vf25, vf11 - c->lhu(gp, 122, ra); // lhu gp, 122(ra) - c->vmadd_bc(DEST::xyzw, BC::z, vf11, vf26, vf11); // vmaddz.xyzw vf11, vf26, vf11 - c->lbu(s4, 115, ra); // lbu s4, 115(ra) - - block_55: - c->vmula_bc(DEST::xyzw, BC::x, vf24, vf12); // vmulax.xyzw acc, vf24, vf12 - c->dsll(gp, gp, 4); // dsll gp, gp, 4 - c->vmadda_bc(DEST::xyzw, BC::y, vf25, vf12); // vmadday.xyzw acc, vf25, vf12 - c->daddu(s4, s4, ra); // daddu s4, s4, ra - c->vmadd_bc(DEST::xyzw, BC::z, vf12, vf26, vf12); // vmaddz.xyzw vf12, vf26, vf12 - // nop // sll r0, r0, 0 - c->vmula_bc(DEST::xyzw, BC::x, vf24, vf13); // vmulax.xyzw acc, vf24, vf13 - // nop // sll r0, r0, 0 - c->vmadda_bc(DEST::xyzw, BC::y, vf25, vf13); // vmadday.xyzw acc, vf25, vf13 - // nop // sll r0, r0, 0 - c->vmadda_bc(DEST::xyzw, BC::z, vf26, vf13); // vmaddaz.xyzw acc, vf26, vf13 - // nop // sll r0, r0, 0 - c->vmadd_bc(DEST::xyzw, BC::w, vf13, vf27, vf0); // vmaddw.xyzw vf13, vf27, vf0 - // nop // sll r0, r0, 0 - c->sqc2(vf10, 0, t8); // sqc2 vf10, 0(t8) - // nop // sll r0, r0, 0 - c->sqc2(vf11, 16, t8); // sqc2 vf11, 16(t8) - // nop // sll r0, r0, 0 - c->sqc2(vf12, 32, t8); // sqc2 vf12, 32(t8) - // nop // sll r0, r0, 0 - c->sqc2(vf13, 48, t8); // sqc2 vf13, 48(t8) - c->daddiu(t8, t8, 96); // daddiu t8, t8, 96 - // nop // sll r0, r0, 0 - c->lw(ra, 8, t4); // lw ra, 8(t4) - // nop // sll r0, r0, 0 - c->lbu(s2, 144, s4); // lbu s2, 144(s4) - c->addu(s1, gp, ra); // addu s1, gp, ra - c->sw(s5, 284, t0); // sw s5, 284(t0) - c->daddiu(t9, t9, 3); // daddiu t9, t9, 3 - c->sw(s1, 292, t0); // sw s1, 292(t0) - c->sll(s1, s2, 4); // sll s1, s2, 4 - c->sh(s2, 288, t0); // sh s2, 288(t0) - c->daddu(gp, gp, s1); // daddu gp, gp, s1 - c->lq(s2, 272, t0); // lq s2, 272(t0) - c->daddiu(s5, s5, 48); // daddiu s5, s5, 48 - c->lq(s1, 288, t0); // lq s1, 288(t0) - c->daddiu(t8, t8, 48); // daddiu t8, t8, 48 - c->lq(s0, 304, t0); // lq s0, 304(t0) - c->daddiu(s4, s4, 1); // daddiu s4, s4, 1 - c->sq(s2, -48, t8); // sq s2, -48(t8) - c->daddiu(s3, s3, -1); // daddiu s3, s3, -1 - c->sq(s1, -32, t8); // sq s1, -32(t8) - bc = ((s64)c->sgpr64(s3)) <= 0; // blez s3, L172 - c->sq(s0, -16, t8); // sq s0, -16(t8) - if (bc) {goto block_61;} // branch non-likely - - - block_56: - c->daddiu(s2, t9, -252); // daddiu s2, t9, -252 - // nop // sll r0, r0, 0 - bc = ((s64)c->sgpr64(s2)) <= 0; // blez s2, L171 - // nop // sll r0, r0, 0 - if (bc) {goto block_60;} // branch non-likely - - /* - block_57: - c->lw(t8, 0, a3); // lw t8, 0(a3) - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - c->andi(t8, t8, 256); // andi t8, t8, 256 - // nop // sll r0, r0, 0 - bc = c->sgpr64(t8) == 0; // beq t8, r0, L170 - // nop // sll r0, r0, 0 - if (bc) {goto block_59;} // branch non-likely - - // nop // sll r0, r0, 0 - c->lw(t8, 440, t0); // lw t8, 440(t0) - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - c->daddiu(t8, t8, 1); // daddiu t8, t8, 1 - // nop // sll r0, r0, 0 - c->sw(t8, 440, t0); // sw t8, 440(t0) - //beq r0, r0, L169 // beq r0, r0, L169 - // nop // sll r0, r0, 0 - goto block_57; // branch always - */ - - - // block_59: - // c->sw(a1, 128, a3); // sw a1, 128(a3) - sadr = c->sgpr64(a1); - c->xori(a1, a1, 12288); // xori a1, a1, 12288 - // c->sw(v1, 16, a3); // sw v1, 16(a3) - madr = c->sgpr64(v1); - c->sll(t8, t9, 4); // sll t8, t9, 4 - c->addu(v1, v1, t8); // addu v1, v1, t8 - c->mov64(t8, a1); // or t8, a1, r0 - // c->sw(t9, 32, a3); // sw t9, 32(a3) - qwc = c->sgpr64(t9); - c->addiu(t9, r0, 256); // addiu t9, r0, 256 - // c->sw(t9, 0, a3); // sw t9, 0(a3) - spad_from_dma(cache.fake_scratchpad_data, madr, sadr, qwc); - c->addiu(t9, r0, 0); // addiu t9, r0, 0 - - block_60: - // nop // sll r0, r0, 0 - c->lbu(s2, 144, s4); // lbu s2, 144(s4) - c->addu(s1, gp, ra); // addu s1, gp, ra - c->sw(s5, 284, t0); // sw s5, 284(t0) - c->daddiu(t9, t9, 3); // daddiu t9, t9, 3 - c->sw(s1, 292, t0); // sw s1, 292(t0) - c->sll(s1, s2, 4); // sll s1, s2, 4 - c->sh(s2, 288, t0); // sh s2, 288(t0) - c->daddu(gp, gp, s1); // daddu gp, gp, s1 - c->lq(s2, 272, t0); // lq s2, 272(t0) - c->daddiu(s5, s5, 48); // daddiu s5, s5, 48 - c->lq(s1, 288, t0); // lq s1, 288(t0) - c->daddiu(t8, t8, 48); // daddiu t8, t8, 48 - c->lq(s0, 304, t0); // lq s0, 304(t0) - c->daddiu(s4, s4, 1); // daddiu s4, s4, 1 - c->sq(s2, -48, t8); // sq s2, -48(t8) - c->daddiu(s3, s3, -1); // daddiu s3, s3, -1 - c->sq(s1, -32, t8); // sq s1, -32(t8) - bc = ((s64)c->sgpr64(s3)) > 0; // bgtz s3, L168 - c->sq(s0, -16, t8); // sq s0, -16(t8) - if (bc) {goto block_56;} // branch non-likely - - - block_61: - c->addiu(a2, a2, -1); // addiu a2, a2, -1 - c->srl(t7, t7, 1); // srl t7, t7, 1 - c->daddiu(t4, t4, 64); // daddiu t4, t4, 64 - // nop // sll r0, r0, 0 - bc = c->sgpr64(t7) != 0; // bne t7, r0, L148 - c->lqc2(vf2, 16, t4); // lqc2 vf2, 16(t4) - if (bc) {goto block_20;} // branch non-likely - - - block_62: - // nop // sll r0, r0, 0 - c->lw(ra, 412, t0); // lw ra, 412(t0) - // nop // sll r0, r0, 0 - c->lw(t7, 416, t0); // lw t7, 416(t0) - bc = c->sgpr64(ra) != c->sgpr64(t7); // bne ra, t7, L146 - // nop // sll r0, r0, 0 - if (bc) {goto block_17;} // branch non-likely - - bc = ((s64)c->sgpr64(a2)) > 0; // bgtz a2, L140 - // nop // sll r0, r0, 0 - if (bc) {goto block_6;} // branch non-likely - - bc = c->sgpr64(t9) == 0; // beq t9, r0, L176 - // nop // sll r0, r0, 0 - if (bc) {goto block_68;} // branch non-likely - - /* - block_65: - c->lw(a0, 0, a3); // lw a0, 0(a3) - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - c->andi(a0, a0, 256); // andi a0, a0, 256 - // nop // sll r0, r0, 0 - bc = c->sgpr64(a0) == 0; // beq a0, r0, L175 - // nop // sll r0, r0, 0 - if (bc) {goto block_67;} // branch non-likely - - // nop // sll r0, r0, 0 - c->lw(a0, 440, t0); // lw a0, 440(t0) - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - c->daddiu(a0, a0, 1); // daddiu a0, a0, 1 - // nop // sll r0, r0, 0 - c->sw(a0, 440, t0); // sw a0, 440(t0) - //beq r0, r0, L174 // beq r0, r0, L174 - // nop // sll r0, r0, 0 - goto block_65; // branch always - */ - - - // block_67: - // c->sw(a1, 128, a3); // sw a1, 128(a3) - sadr = c->sgpr64(a1); - c->xori(a0, a1, 12288); // xori a0, a1, 12288 - // c->sw(v1, 16, a3); // sw v1, 16(a3) - madr = c->sgpr64(v1); - c->sll(a1, t9, 4); // sll a1, t9, 4 - c->addu(v1, v1, a1); // addu v1, v1, a1 - c->mov64(a0, a0); // or a0, a0, r0 - // c->sw(t9, 32, a3); // sw t9, 32(a3) - qwc = c->sgpr64(t9); - c->addiu(a0, r0, 256); // addiu a0, r0, 256 - // c->sw(a0, 0, a3); // sw a0, 0(a3) - spad_from_dma(cache.fake_scratchpad_data, madr, sadr, qwc); - c->addiu(a0, r0, 0); // addiu a0, r0, 0 - - block_68: - /* - c->lw(a0, 0, a3); // lw a0, 0(a3) - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - c->andi(a0, a0, 256); // andi a0, a0, 256 - // nop // sll r0, r0, 0 - bc = c->sgpr64(a0) == 0; // beq a0, r0, L177 - // nop // sll r0, r0, 0 - if (bc) {goto block_70;} // branch non-likely - - // nop // sll r0, r0, 0 - c->lw(a0, 440, t0); // lw a0, 440(t0) - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - c->daddiu(a0, a0, 1); // daddiu a0, a0, 1 - // nop // sll r0, r0, 0 - c->sw(a0, 440, t0); // sw a0, 440(t0) - //beq r0, r0, L176 // beq r0, r0, L176 - // nop // sll r0, r0, 0 - goto block_68; // branch always - */ - - - block_70: - c->lw(a0, 396, t0); // lw a0, 396(t0) - // nop // sll r0, r0, 0 - c->sw(v1, 4, a0); // sw v1, 4(a0) - // nop // sll r0, r0, 0 - c->gprs[v0].du64[0] = 0; // or v0, r0, r0 - c->ld(ra, 0, sp); // ld ra, 0(sp) - c->lq(gp, 112, sp); // lq gp, 112(sp) - c->lq(s5, 96, sp); // lq s5, 96(sp) - c->lq(s4, 80, sp); // lq s4, 80(sp) - c->lq(s3, 64, sp); // lq s3, 64(sp) - c->lq(s2, 48, sp); // lq s2, 48(sp) - c->lq(s1, 32, sp); // lq s1, 32(sp) - c->lq(s0, 16, sp); // lq s0, 16(sp) - //jr ra // jr ra - c->daddiu(sp, sp, 128); // daddiu sp, sp, 128 - goto end_of_function; // return - - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - end_of_function: - return c->gprs[v0].du64[0]; -} - -void link() { - cache.fake_scratchpad_data = intern_from_c("*fake-scratchpad-data*").c(); - cache.instance_tie_work_copy = intern_from_c("*instance-tie-work-copy*").c(); - cache.wind_work = intern_from_c("*wind-work*").c(); - cache.math_camera = intern_from_c("*math-camera*").c(); - gLinkedFunctionTable.reg("draw-inline-array-instance-tie", execute, 512); -} - -} // namespace draw_inline_array_instance_tie -} // namespace Mips2C - - -//--------------------------MIPS2C--------------------- -#include "game/mips2c/mips2c_private.h" - -namespace Mips2C::jak1 { -namespace draw_inline_array_prototype_tie_generic_asm { -struct Cache { - void* fake_scratchpad_data; // *fake-scratchpad-data* - void* prototype_tie_work; // *prototype-tie-work* -} cache; - -void block29_call(ExecutionContext* c) { - bool bc; - u32 sadr, madr, qwc; - // block_29: - c->addiu(t6, t6, 32); // addiu t6, t6, 32 - c->sw(t5, 232, t0); // sw t5, 232(t0) - - block_30: - c->addiu(t7, a0, 4); // addiu t7, a0, 4 - c->addiu(t8, r0, 255); // addiu t8, r0, 255 - c->dsubu(t8, t8, t7); // dsubu t8, t8, t7 - c->lw(t7, 0, t6); // lw t7, 0(t6) - bc = ((s64)c->sgpr64(t8)) >= 0; // bgez t8, L131 - c->lhu(t8, 30, t6); // lhu t8, 30(t6) - if (bc) {goto block_34;} // branch non-likely - - /* - block_31: - c->lw(a1, 0, t1); // lw a1, 0(t1) - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - c->andi(a1, a1, 256); // andi a1, a1, 256 - // nop // sll r0, r0, 0 - bc = c->sgpr64(a1) == 0; // beq a1, r0, L130 - // nop // sll r0, r0, 0 - if (bc) {goto block_33;} // branch non-likely - - // nop // sll r0, r0, 0 - c->lw(a1, 284, t0); // lw a1, 284(t0) - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - c->daddiu(a1, a1, 1); // daddiu a1, a1, 1 - // nop // sll r0, r0, 0 - c->sw(a1, 284, t0); // sw a1, 284(t0) - //beq r0, r0, L129 // beq r0, r0, L129 - // nop // sll r0, r0, 0 - goto block_31; // branch always - */ - - - // block_33: - // c->sw(t2, 128, t1); // sw t2, 128(t1) - sadr = c->sgpr64(t2); - c->xori(t2, t2, 4096); // xori t2, t2, 4096 - // c->sw(v1, 16, t1); // sw v1, 16(t1) - madr = c->sgpr64(v1); - c->sll(a1, a0, 4); // sll a1, a0, 4 - c->addu(v1, v1, a1); // addu v1, v1, a1 - c->mov64(a1, t2); // or a1, t2, r0 - // c->sw(a0, 32, t1); // sw a0, 32(t1) - qwc = c->sgpr64(a0); - c->addiu(a0, r0, 256); // addiu a0, r0, 256 - // c->sw(a0, 0, t1); // sw a0, 0(t1) - spad_from_dma(cache.fake_scratchpad_data, madr, sadr, qwc); - c->addiu(a0, r0, 0); // addiu a0, r0, 0 - - block_34: - // nop // sll r0, r0, 0 - c->lhu(t9, 28, t6); // lhu t9, 28(t6) - c->daddu(t8, t8, t9); // daddu t8, t8, t9 - c->lw(t9, 4, t6); // lw t9, 4(t6) - c->daddiu(a0, a0, 4); // daddiu a0, a0, 4 - c->lhu(gp, 32, t6); // lhu gp, 32(t6) - c->sll(s5, a0, 4); // sll s5, a0, 4 - c->sw(t7, 212, t0); // sw t7, 212(t0) - c->daddu(t7, s5, v1); // daddu t7, s5, v1 - c->sh(t8, 208, t0); // sh t8, 208(t0) - // nop // sll r0, r0, 0 - c->sw(t7, 220, t0); // sw t7, 220(t0) - // nop // sll r0, r0, 0 - c->sw(a2, 260, t0); // sw a2, 260(t0) - // nop // sll r0, r0, 0 - c->sw(t9, 228, t0); // sw t9, 228(t0) - // nop // sll r0, r0, 0 - c->sh(gp, 224, t0); // sh gp, 224(t0) - // nop // sll r0, r0, 0 - c->lw(t7, 48, t6); // lw t7, 48(t6) - // nop // sll r0, r0, 0 - c->lhu(t8, 52, t6); // lhu t8, 52(t6) - // nop // sll r0, r0, 0 - c->sw(t7, 244, t0); // sw t7, 244(t0) - // nop // sll r0, r0, 0 - c->sh(t8, 240, t0); // sh t8, 240(t0) - // nop // sll r0, r0, 0 - c->lq(t7, 208, t0); // lq t7, 208(t0) - // nop // sll r0, r0, 0 - c->lq(t8, 224, t0); // lq t8, 224(t0) - // nop // sll r0, r0, 0 - c->lq(t9, 240, t0); // lq t9, 240(t0) - // nop // sll r0, r0, 0 - c->lq(gp, 256, t0); // lq gp, 256(t0) - // nop // sll r0, r0, 0 - c->sq(t7, 0, a1); // sq t7, 0(a1) - c->daddiu(t5, t5, -1); // daddiu t5, t5, -1 - c->sq(t8, 16, a1); // sq t8, 16(a1) - c->daddiu(a2, a2, 48); // daddiu a2, a2, 48 - c->sq(t9, 32, a1); // sq t9, 32(a1) - c->sq(gp, 48, a1); // sq gp, 48(a1) - c->daddiu(a1, a1, 64); // daddiu a1, a1, 64 - bc = ((s64)c->sgpr64(t5)) > 0; // bgtz t5, L128 - c->daddiu(t6, t6, 64); // daddiu t6, t6, 64 - if (bc) {goto block_30;} // branch non-likely - - //jr ra // jr ra - // nop // sll r0, r0, 0 -} - -u64 execute(void* ctxt) { - auto* c = (ExecutionContext*)ctxt; - bool bc = false; - u32 madr, sadr, qwc; - c->daddiu(sp, sp, -112); // daddiu sp, sp, -112 - c->sd(ra, 0, sp); // sd ra, 0(sp) - c->sq(s1, 16, sp); // sq s1, 16(sp) - c->sq(s2, 32, sp); // sq s2, 32(sp) - c->sq(s3, 48, sp); // sq s3, 48(sp) - c->sq(s4, 64, sp); // sq s4, 64(sp) - c->sq(s5, 80, sp); // sq s5, 80(sp) - c->sq(gp, 96, sp); // sq gp, 96(sp) - // nop // sll r0, r0, 0 - get_fake_spad_addr(a3, cache.fake_scratchpad_data, 0, c);// lui a3, 28672 - c->lw(v1, 4, a0); // lw v1, 4(a0) - c->lui(t1, 4096); // lui t1, 4096 - c->lui(t2, 4096); // lui t2, 4096 - // sync.l - // cache dxwbin v1, 0 - // sync.l - // cache dxwbin v1, 1 - // sync.l - c->load_symbol(t0, cache.prototype_tie_work); // lw t0, *prototype-tie-work*(s7) - c->ori(t1, t1, 53248); // ori t1, t1, 53248 - c->ori(t4, t2, 54272); // ori t4, t2, 54272 SPR TO - c->ori(t3, a3, 16); // ori t3, a3, 16 - c->ori(t2, a3, 2064); // ori t2, a3, 2064 - c->sw(a0, 10260, a3); // sw a0, 10260(a3) - c->daddiu(t7, a1, -1); // daddiu t7, a1, -1 - // nop // sll r0, r0, 0 - c->lw(t6, 12, a2); // lw t6, 12(a2) - c->addiu(a0, r0, 0); // addiu a0, r0, 0 - c->mov64(a1, t2); // or a1, t2, r0 - - block_1: - // nop // sll r0, r0, 0 - c->lq(t5, 92, t6); // lq t5, 92(t6) - c->daddiu(t8, a2, 4); // daddiu t8, a2, 4 - c->sw(t7, 10256, a3); // sw t7, 10256(a3) - c->dsrl32(a2, t5, 0); // dsrl32 a2, t5, 0 - c->sw(t8, 280, t0); // sw t8, 280(t0) - c->pcpyud(t7, t5, t5); // pcpyud t7, t5, t5 - c->lw(t9, 140, t6); // lw t9, 140(t6) - c->or_(t7, a2, t7); // or t7, a2, t7 - c->lw(a2, 108, t6); // lw a2, 108(t6) - bc = c->sgpr64(t7) == 0; // beq t7, r0, L126 - c->lw(t8, 4, a3); // lw t8, 4(a3) - if (bc) {goto block_27;} // branch non-likely - - // nop // sll r0, r0, 0 - c->lq(t7, 12, t6); // lq t7, 12(t6) - // nop // sll r0, r0, 0 - c->sq(t5, 10272, a3); // sq t5, 10272(a3) - // nop // sll r0, r0, 0 - c->sw(a2, 10304, a3); // sw a2, 10304(a3) - // nop // sll r0, r0, 0 - c->sq(t7, 10288, a3); // sq t7, 10288(a3) - // nop // sll r0, r0, 0 - c->ld(a2, 272, t0); // ld a2, 272(t0) - // nop // sll r0, r0, 0 - c->lw(t7, 4, t9); // lw t7, 4(t9) - c->daddiu(ra, t9, 12); // daddiu ra, t9, 12 - c->lq(t5, 1852, t8); // lq t5, 1852(t8) - c->sra(t9, t7, 2); // sra t9, t7, 2 - // nop // sll r0, r0, 0 - c->addu(t9, t9, a0); // addu t9, t9, a0 - c->addiu(gp, r0, 221); // addiu gp, r0, 221 - c->dsubu(t9, gp, t9); // dsubu t9, gp, t9 - // nop // sll r0, r0, 0 - bc = ((s64)c->sgpr64(t9)) >= 0; // bgez t9, L114 - // nop // sll r0, r0, 0 - if (bc) {goto block_6;} // branch non-likely - - /* - block_3: - c->lw(a1, 0, t1); // lw a1, 0(t1) - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - c->andi(a1, a1, 256); // andi a1, a1, 256 - // nop // sll r0, r0, 0 - bc = c->sgpr64(a1) == 0; // beq a1, r0, L113 - // nop // sll r0, r0, 0 - if (bc) {goto block_5;} // branch non-likely - - // nop // sll r0, r0, 0 - c->lw(a1, 284, t0); // lw a1, 284(t0) - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - c->daddiu(a1, a1, 1); // daddiu a1, a1, 1 - // nop // sll r0, r0, 0 - c->sw(a1, 284, t0); // sw a1, 284(t0) - //beq r0, r0, L112 // beq r0, r0, L112 - // nop // sll r0, r0, 0 - goto block_3; // branch always - */ - - - // block_5: - // c->sw(t2, 128, t1); // sw t2, 128(t1) - sadr = c->sgpr64(t2); - c->xori(t2, t2, 4096); // xori t2, t2, 4096 - // c->sw(v1, 16, t1); // sw v1, 16(t1) - madr = c->sgpr64(v1); - c->sll(a1, a0, 4); // sll a1, a0, 4 - c->addu(v1, v1, a1); // addu v1, v1, a1 - c->mov64(a1, t2); // or a1, t2, r0 - // c->sw(a0, 32, t1); // sw a0, 32(t1) - qwc = c->sgpr64(a0); - c->addiu(a0, r0, 256); // addiu a0, r0, 256 - // c->sw(a0, 0, t1); // sw a0, 0(t1) - spad_from_dma(cache.fake_scratchpad_data, madr, sadr, qwc); - c->addiu(a0, r0, 0); // addiu a0, r0, 0 - - block_6: - c->addiu(t7, t7, 31); // addiu t7, t7, 31 - c->lw(t6, 132, t6); // lw t6, 132(t6) - c->sra(t7, t7, 5); // sra t7, t7, 5 - c->addiu(a0, a0, 1); // addiu a0, a0, 1 - c->sll(t7, t7, 3); // sll t7, t7, 3 - c->sw(t6, 200, t0); // sw t6, 200(t0) - c->addu(a0, a0, t7); // addu a0, a0, t7 - c->sh(t7, 192, t0); // sh t7, 192(t0) - c->sll(t9, t7, 2); // sll t9, t7, 2 - c->lq(t6, 1868, t8); // lq t6, 1868(t8) - // nop // sll r0, r0, 0 - c->lq(gp, 192, t0); // lq gp, 192(t0) - // nop // sll r0, r0, 0 - c->lq(t7, 1884, t8); // lq t7, 1884(t8) - // nop // sll r0, r0, 0 - c->sq(gp, 0, a1); // sq gp, 0(a1) - c->addiu(a1, a1, 16); // addiu a1, a1, 16 - c->lq(t8, 1900, t8); // lq t8, 1900(t8) - - /* - block_7: - c->lw(gp, 0, t4); // lw gp, 0(t4) - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - c->andi(gp, gp, 256); // andi gp, gp, 256 - // nop // sll r0, r0, 0 - bc = c->sgpr64(gp) == 0; // beq gp, r0, L116 - // nop // sll r0, r0, 0 - if (bc) {goto block_9;} // branch non-likely - - // nop // sll r0, r0, 0 - c->lw(gp, 288, t0); // lw gp, 288(t0) - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - c->daddiu(gp, gp, 1); // daddiu gp, gp, 1 - // nop // sll r0, r0, 0 - c->sw(gp, 288, t0); // sw gp, 288(t0) - //beq r0, r0, L115 // beq r0, r0, L115 - // nop // sll r0, r0, 0 - goto block_7; // branch always - */ - - - // block_9: - // c->sw(ra, 16, t4); // sw ra, 16(t4) - madr = c->sgpr64(ra); - c->daddiu(t9, t9, -32); // daddiu t9, t9, -32 - // c->sw(t3, 128, t4); // sw t3, 128(t4) - sadr = c->sgpr64(t3); - c->addiu(gp, r0, 64); // addiu gp, r0, 64 - // c->sw(gp, 32, t4); // sw gp, 32(t4) - qwc = c->sgpr64(gp); - c->addiu(gp, r0, 256); // addiu gp, r0, 256 - // c->sw(gp, 0, t4); // sw gp, 0(t4) - spad_to_dma(cache.fake_scratchpad_data, madr, sadr, qwc); - c->daddiu(ra, ra, 1024); // daddiu ra, ra, 1024 - - block_10: - c->mov64(s5, t3); // or s5, t3, r0 - c->xori(t3, t3, 1024); // xori t3, t3, 1024 - bc = ((s64)c->sgpr64(t9)) <= 0; // blez t9, L120 - c->daddiu(t9, t9, -32); // daddiu t9, t9, -32 - if (bc) {goto block_14;} // branch non-likely - - /* - block_11: - c->lw(gp, 0, t4); // lw gp, 0(t4) - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - c->andi(gp, gp, 256); // andi gp, gp, 256 - // nop // sll r0, r0, 0 - bc = c->sgpr64(gp) == 0; // beq gp, r0, L119 - // nop // sll r0, r0, 0 - if (bc) {goto block_13;} // branch non-likely - - // nop // sll r0, r0, 0 - c->lw(gp, 288, t0); // lw gp, 288(t0) - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - c->daddiu(gp, gp, 1); // daddiu gp, gp, 1 - // nop // sll r0, r0, 0 - c->sw(gp, 288, t0); // sw gp, 288(t0) - //beq r0, r0, L118 // beq r0, r0, L118 - // nop // sll r0, r0, 0 - goto block_11; // branch always - */ - - - // block_13: - // c->sw(ra, 16, t4); // sw ra, 16(t4) - madr = c->sgpr64(ra); - // nop // sll r0, r0, 0 - // c->sw(t3, 128, t4); // sw t3, 128(t4) - sadr = c->sgpr64(t3); - c->addiu(gp, r0, 64); // addiu gp, r0, 64 - // c->sw(gp, 32, t4); // sw gp, 32(t4) - qwc = c->sgpr64(gp); - c->addiu(gp, r0, 256); // addiu gp, r0, 256 - // c->sw(gp, 0, t4); // sw gp, 0(t4) - spad_to_dma(cache.fake_scratchpad_data, madr, sadr, qwc); - c->daddiu(ra, ra, 1024); // daddiu ra, ra, 1024 - //beq r0, r0, L121 // beq r0, r0, L121 - // nop // sll r0, r0, 0 - goto block_16; // branch always - - - block_14: - /* - c->lw(gp, 0, t4); // lw gp, 0(t4) - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - c->andi(gp, gp, 256); // andi gp, gp, 256 - // nop // sll r0, r0, 0 - bc = c->sgpr64(gp) == 0; // beq gp, r0, L121 - // nop // sll r0, r0, 0 - if (bc) {goto block_16;} // branch non-likely - - // nop // sll r0, r0, 0 - c->lw(gp, 288, t0); // lw gp, 288(t0) - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - c->daddiu(gp, gp, 1); // daddiu gp, gp, 1 - // nop // sll r0, r0, 0 - c->sw(gp, 288, t0); // sw gp, 288(t0) - //beq r0, r0, L120 // beq r0, r0, L120 - // nop // sll r0, r0, 0 - goto block_14; // branch always - */ - - - block_16: - c->addiu(gp, a1, 128); // addiu gp, a1, 128 - c->lq(s2, 12, s5); // lq s2, 12(s5) - // nop // sll r0, r0, 0 - c->lq(s4, 28, s5); // lq s4, 28(s5) - c->pextlb(s3, r0, s2); // pextlb s3, r0, s2 - c->mfc1(r0, f31); // mfc1 r0, f31 - c->pextub(s2, r0, s2); // pextub s2, r0, s2 - c->mfc1(r0, f31); // mfc1 r0, f31 - c->pmulth(r0, s3, t5); // pmulth r0, s3, t5 - c->mfc1(r0, f31); // mfc1 r0, f31 - c->pextlb(s3, r0, s4); // pextlb s3, r0, s4 - c->mfc1(r0, f31); // mfc1 r0, f31 - c->pmaddh(r0, s2, t6); // pmaddh r0, s2, t6 - c->mfc1(r0, f31); // mfc1 r0, f31 - c->pextub(s4, r0, s4); // pextub s4, r0, s4 - c->mfc1(r0, f31); // mfc1 r0, f31 - c->pmaddh(r0, s3, t7); // pmaddh r0, s3, t7 - c->lq(s3, 44, s5); // lq s3, 44(s5) - c->addiu(s5, s5, 32); // addiu s5, s5, 32 - // nop // sll r0, r0, 0 - c->pmaddh(r0, s4, t8); // pmaddh r0, s4, t8 - c->lq(s4, 28, s5); // lq s4, 28(s5) - c->pextlb(s2, r0, s3); // pextlb s2, r0, s3 - c->mfc1(r0, f31); // mfc1 r0, f31 - - block_17: - c->pextub(s3, r0, s3); // pextub s3, r0, s3 - c->mfc1(r0, f31); // mfc1 r0, f31 - c->pmfhl_lh(s1); // pmfhl.lh s1 - c->mfc1(r0, f31); // mfc1 r0, f31 - c->pmulth(r0, s2, t5); // pmulth r0, s2, t5 - c->mfc1(r0, f31); // mfc1 r0, f31 - c->psrlh(s2, s1, 6); // psrlh s2, s1, 6 - c->mfc1(r0, f31); // mfc1 r0, f31 - c->pcpyud(s1, s2, s2); // pcpyud s1, s2, s2 - c->mfc1(r0, f31); // mfc1 r0, f31 - c->paddh(s2, s1, s2); // paddh s2, s1, s2 - c->mfc1(r0, f31); // mfc1 r0, f31 - c->pminh(s2, s2, a2); // pminh s2, s2, a2 - c->mfc1(r0, f31); // mfc1 r0, f31 - c->ppacb(s1, r0, s2); // ppacb s1, r0, s2 - c->mfc1(r0, f31); // mfc1 r0, f31 - c->pextlb(s2, r0, s4); // pextlb s2, r0, s4 - c->mfc1(r0, f31); // mfc1 r0, f31 - c->pmaddh(r0, s3, t6); // pmaddh r0, s3, t6 - c->sw(s1, 0, a1); // sw s1, 0(a1) - c->pextub(s4, r0, s4); // pextub s4, r0, s4 - c->mfc1(r0, f31); // mfc1 r0, f31 - c->pmaddh(r0, s2, t7); // pmaddh r0, s2, t7 - c->lq(s3, 44, s5); // lq s3, 44(s5) - c->addiu(s5, s5, 32); // addiu s5, s5, 32 - c->addiu(a1, a1, 4); // addiu a1, a1, 4 - c->pmaddh(r0, s4, t8); // pmaddh r0, s4, t8 - c->lq(s4, 28, s5); // lq s4, 28(s5) - bc = c->sgpr64(a1) != c->sgpr64(gp); // bne a1, gp, L122 - c->pextlb(s2, r0, s3); // pextlb s2, r0, s3 - if (bc) {goto block_17;} // branch non-likely - - bc = ((s64)c->sgpr64(t9)) >= 0; // bgez t9, L117 - // nop // sll r0, r0, 0 - if (bc) {goto block_10;} // branch non-likely - - // nop // sll r0, r0, 0 - c->lw(a2, 10272, a3); // lw a2, 10272(a3) - // nop // sll r0, r0, 0 - c->lw(t6, 10292, a3); // lw t6, 10292(a3) - bc = c->sgpr64(a2) == 0; // beq a2, r0, L123 - c->lbu(t5, 10305, a3); // lbu t5, 10305(a3) - if (bc) {goto block_21;} // branch non-likely - - // Unknown instr: bgezal r0, L127 - block29_call(c); - // nop // sll r0, r0, 0 - - - - block_21: - // nop // sll r0, r0, 0 - c->lw(a2, 10276, a3); // lw a2, 10276(a3) - // nop // sll r0, r0, 0 - c->lw(t6, 10292, a3); // lw t6, 10292(a3) - bc = c->sgpr64(a2) == 0; // beq a2, r0, L124 - c->lbu(t5, 10305, a3); // lbu t5, 10305(a3) - if (bc) {goto block_23;} // branch non-likely - - // Unknown instr: bgezal r0, L127 - // nop // sll r0, r0, 0 - block29_call(c); - - - block_23: - // nop // sll r0, r0, 0 - c->lw(a2, 10280, a3); // lw a2, 10280(a3) - // nop // sll r0, r0, 0 - c->lw(t6, 10296, a3); // lw t6, 10296(a3) - bc = c->sgpr64(a2) == 0; // beq a2, r0, L125 - c->lbu(t5, 10306, a3); // lbu t5, 10306(a3) - if (bc) {goto block_25;} // branch non-likely - - // Unknown instr: bgezal r0, L127 - // nop // sll r0, r0, 0 - block29_call(c); - - - block_25: - // nop // sll r0, r0, 0 - c->lw(a2, 10284, a3); // lw a2, 10284(a3) - // nop // sll r0, r0, 0 - c->lw(t6, 10300, a3); // lw t6, 10300(a3) - bc = c->sgpr64(a2) == 0; // beq a2, r0, L126 - c->lbu(t5, 10307, a3); // lbu t5, 10307(a3) - if (bc) {goto block_27;} // branch non-likely - - // Unknown instr: bgezal r0, L127 - // nop // sll r0, r0, 0 - block29_call(c); - - - block_27: - // nop // sll r0, r0, 0 - c->lw(a2, 280, t0); // lw a2, 280(t0) - // nop // sll r0, r0, 0 - c->lw(t5, 10256, a3); // lw t5, 10256(a3) - // nop // sll r0, r0, 0 - c->lw(t6, 12, a2); // lw t6, 12(a2) - // nop // sll r0, r0, 0 - c->sw(r0, -56, a1); // sw r0, -56(a1) - bc = c->sgpr64(t5) != 0; // bne t5, r0, L111 - c->daddiu(t7, t5, -1); // daddiu t7, t5, -1 - if (bc) {goto block_1;} // branch non-likely - - //beq r0, r0, L132 // beq r0, r0, L132 - c->sw(r0, -52, a1); // sw r0, -52(a1) - goto block_36; // branch always - - - ASSERT(false); - - block_36: - bc = c->sgpr64(a0) == 0; // beq a0, r0, L135 - // nop // sll r0, r0, 0 - if (bc) {goto block_40;} // branch non-likely - - /* - block_37: - c->lw(a1, 0, t1); // lw a1, 0(t1) - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - c->andi(a1, a1, 256); // andi a1, a1, 256 - // nop // sll r0, r0, 0 - bc = c->sgpr64(a1) == 0; // beq a1, r0, L134 - // nop // sll r0, r0, 0 - if (bc) {goto block_39;} // branch non-likely - - // nop // sll r0, r0, 0 - c->lw(a1, 284, t0); // lw a1, 284(t0) - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - c->daddiu(a1, a1, 1); // daddiu a1, a1, 1 - // nop // sll r0, r0, 0 - c->sw(a1, 284, t0); // sw a1, 284(t0) - //beq r0, r0, L133 // beq r0, r0, L133 - // nop // sll r0, r0, 0 - goto block_37; // branch always - */ - - - // block_39: - // c->sw(t2, 128, t1); // sw t2, 128(t1) - sadr = c->sgpr64(t2); - // nop // sll r0, r0, 0 - // c->sw(v1, 16, t1); // sw v1, 16(t1) - madr = c->sgpr64(v1); - c->sll(a1, a0, 4); // sll a1, a0, 4 - c->addu(v1, v1, a1); // addu v1, v1, a1 - // nop // sll r0, r0, 0 - // c->sw(a0, 32, t1); // sw a0, 32(t1) - qwc = c->sgpr64(a0); - c->addiu(a0, r0, 256); // addiu a0, r0, 256 - // c->sw(a0, 0, t1); // sw a0, 0(t1) - spad_from_dma(cache.fake_scratchpad_data, madr, sadr, qwc); - // nop // sll r0, r0, 0 - - block_40: - /* - c->lw(a0, 0, t1); // lw a0, 0(t1) - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - c->andi(a0, a0, 256); // andi a0, a0, 256 - // nop // sll r0, r0, 0 - bc = c->sgpr64(a0) == 0; // beq a0, r0, L136 - // nop // sll r0, r0, 0 - if (bc) {goto block_42;} // branch non-likely - - // nop // sll r0, r0, 0 - c->lw(a0, 284, t0); // lw a0, 284(t0) - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - c->daddiu(a0, a0, 1); // daddiu a0, a0, 1 - // nop // sll r0, r0, 0 - c->sw(a0, 284, t0); // sw a0, 284(t0) - //beq r0, r0, L135 // beq r0, r0, L135 - // nop // sll r0, r0, 0 - goto block_40; // branch always - */ - - - // block_42: - c->lw(a0, 10260, a3); // lw a0, 10260(a3) - // nop // sll r0, r0, 0 - c->sw(v1, 4, a0); // sw v1, 4(a0) - // nop // sll r0, r0, 0 - c->gprs[v0].du64[0] = 0; // or v0, r0, r0 - c->ld(ra, 0, sp); // ld ra, 0(sp) - c->lq(gp, 96, sp); // lq gp, 96(sp) - c->lq(s5, 80, sp); // lq s5, 80(sp) - c->lq(s4, 64, sp); // lq s4, 64(sp) - c->lq(s3, 48, sp); // lq s3, 48(sp) - c->lq(s2, 32, sp); // lq s2, 32(sp) - c->lq(s1, 16, sp); // lq s1, 16(sp) - //jr ra // jr ra - c->daddiu(sp, sp, 112); // daddiu sp, sp, 112 - goto end_of_function; // return - - // nop // sll r0, r0, 0 - // nop // sll r0, r0, 0 - end_of_function: - return c->gprs[v0].du64[0]; -} - -void link() { - cache.fake_scratchpad_data = intern_from_c("*fake-scratchpad-data*").c(); - cache.prototype_tie_work = intern_from_c("*prototype-tie-work*").c(); - gLinkedFunctionTable.reg("draw-inline-array-prototype-tie-generic-asm", execute, 256); -} - -} // namespace draw_inline_array_prototype_tie_generic_asm -} // namespace Mips2C diff --git a/game/mips2c/mips2c_table.cpp b/game/mips2c/mips2c_table.cpp index 54f6d145ea..d27b485460 100644 --- a/game/mips2c/mips2c_table.cpp +++ b/game/mips2c/mips2c_table.cpp @@ -41,8 +41,6 @@ namespace init_boundary_regs { extern void link(); } namespace render_boundary_quad { extern void link(); } namespace render_boundary_tri { extern void link(); } namespace draw_boundary_polygon { extern void link(); } -namespace draw_inline_array_tfrag { extern void link(); } -namespace stats_tfrag_asm { extern void link(); } namespace time_of_day_interp_colors_scratch { extern void link(); } namespace method_12_collide_mesh { extern void link(); } namespace method_11_collide_mesh { extern void link(); } @@ -97,8 +95,6 @@ namespace shadow_scissor_top { extern void link(); } namespace shadow_scissor_edges { extern void link(); } namespace shadow_calc_dual_verts { extern void link(); } namespace shadow_xform_verts { extern void link(); } -namespace draw_inline_array_instance_tie { extern void link(); } -namespace draw_inline_array_prototype_tie_generic_asm { extern void link(); } } // namespace jak1 namespace jak2 { @@ -397,7 +393,6 @@ PerGameVersion>> gMips2C {"load-boundary", {jak1::init_boundary_regs::link, jak1::render_boundary_quad::link, jak1::render_boundary_tri::link, jak1::draw_boundary_polygon::link}}, - {"tfrag", {jak1::draw_inline_array_tfrag::link, jak1::stats_tfrag_asm::link}}, {"time-of-day", {jak1::time_of_day_interp_colors_scratch::link}}, {"collide-probe", {jak1::collide_probe_node::link, jak1::collide_probe_instance_tie::link}}, {"collide-mesh", @@ -434,10 +429,7 @@ PerGameVersion>> gMips2C jak1::shadow_find_single_edges::link, jak1::shadow_find_facing_single_tris::link, jak1::shadow_init_vars::link, jak1::shadow_scissor_top::link, jak1::shadow_scissor_edges::link, jak1::shadow_calc_dual_verts::link, - jak1::shadow_xform_verts::link}}, - {"tie-methods", - {jak1::draw_inline_array_instance_tie::link, - jak1::draw_inline_array_prototype_tie_generic_asm::link}}}, + jak1::shadow_xform_verts::link}}}, /////////// JAK 2 {{"collide-func", {jak2::collide_do_primitives::link, jak2::moving_sphere_triangle_intersect::link}}, diff --git a/goal_src/jak1/engine/dma/dma-buffer.gc b/goal_src/jak1/engine/dma/dma-buffer.gc index c1244a2536..42effd1550 100644 --- a/goal_src/jak1/engine/dma/dma-buffer.gc +++ b/goal_src/jak1/engine/dma/dma-buffer.gc @@ -127,6 +127,17 @@ (set! (-> ,pkt vif0) ,vif0) (set! (-> ,pkt vif1) ,vif1)))) +(defmacro dma-buffer-add-next-vif2 (buf qwc addr vif0 vif1) + "Append a NEXT dma-packet. It transfers qwc quadwords following the tag, then continues at addr; + vif0 and vif1 occupy the tag-transfer command words." + (with-gensyms (pkt) + `(dma-buffer-add-base-type ,buf + ,pkt + dma-packet + (set! (-> ,pkt dma) (new 'static 'dma-tag :id (dma-tag-id next) :qwc ,qwc :addr (the-as int ,addr))) + (set! (-> ,pkt vif0) ,vif0) + (set! (-> ,pkt vif1) ,vif1)))) + (defmacro dma-buffer-add-ret (buf) "Append a RET dma-packet with two VIF no-ops." (with-gensyms (pkt) @@ -255,4 +266,5 @@ "Bind a dma-buffer to a variable and use it on a block to allow adding things to a new bucket. usage: (with-dma-buffer-add-bucket ((buffer-name buffer) bucket-id) &rest body) example: (with-dma-buffer-add-bucket ((buf (-> (current-frame) debug-buf)) (bucket-id debug-no-zbuf)) ...)" - `(let ((,(caar bindings) ,(cadar bindings))) (with-dma-bucket (,(caar bindings) ,bucket-group ,(cadr bindings)) ,@body))) + `(let ((,(caar bindings) ,(cadar bindings))) + (with-dma-bucket (,(caar bindings) ,bucket-group ,(cadr bindings)) ,@body))) diff --git a/goal_src/jak1/engine/gfx/shrub/shrub-work.gc b/goal_src/jak1/engine/gfx/shrub/shrub-work.gc index 072a35d4e6..20a32bc468 100644 --- a/goal_src/jak1/engine/gfx/shrub/shrub-work.gc +++ b/goal_src/jak1/engine/gfx/shrub/shrub-work.gc @@ -9,29 +9,6 @@ ;; DECOMP BEGINS ;; The static templates shrubbery.gc copies and patches while it builds the frame's shrub DMA chains. -;; -;; matrix-tmpl and count-tmpl are one twenty-position cycle over the two VU1 instance rings. mod-count -;; is the position: 0 through 9 fill ring A's ten slots and 10 through 19 fill ring B's. Position k's -;; matrix packet unpacks five quadwords to that ring's slot, and the count packet at a group boundary -;; writes how many instances the ring holds. -;; -;; The DMA tag ids are the interesting part. Records are appended backwards -- each new tag's ADDR is -;; patched to the record appended before it -- so the chain runs newest-first, and a next tag is all -;; that is needed in the middle of a group. At the four group boundaries, positions 0, 9, 10 and 19, -;; the tag is a cnt instead, so the chain flows on by adjacency into whatever the EE wrote -;; immediately after it: the count packet when a ring has just filled, or an MSCALF that runs the -;; draw entry when the next group opens. Read in DMA order, one prototype's chain is the current -;; partial group followed by each earlier full group in reverse, each ending in its own MSCALF. -;; -;; mscalf-tmpl and mscalf-ret-tmpl are that MSCALF, in next and ret flavours. The chain is entered -;; with a call, so the earliest group -- the last one the DMAC reaches -- has to return instead of -;; jumping onward, and the builder picks the ret flavour when the bucket has no earlier group. -;; -;; The six near packets pair two matrix destinations with three packed-stream destinations, so the EE -;; can rotate to a free packet while an earlier chain still names the last one. start-bank records -;; which of the two model-init entries each ring position needs; entry 17 leaves the draw entry -;; reading ring A and entry 21 leaves it reading ring B, which is why positions 1 through 10 want one -;; and 0 and 11 through 19 want the other. (define *instance-shrub-work* (new 'static 'instance-shrub-work @@ -507,8 +484,6 @@ :start-bank (new 'static 'array uint8 20 #x0 #x1 #x1 #x1 #x1 #x1 #x1 #x1 #x1 #x1 #x1 #x0 #x0 #x0 #x0 #x0 #x0 #x0 #x0 #x0))) -;; Both flavours start the draw entry. Patched here rather than written into the static data because -;; the entry address is a property of the microprogram, not of the packet. (set! (-> *instance-shrub-work* mscalf-tmpl vif0 imm) SHRUB-VU-ENTRY-DRAW) (set! (-> *instance-shrub-work* mscalf-ret-tmpl vif0 imm) SHRUB-VU-ENTRY-DRAW) @@ -587,10 +562,8 @@ (shr (shl (+ stream-vu-address 2) 54) 54) :num (-> fragment vtx-cnt))) (set! (-> dma-state base) (&+ (the-as pointer vertex-packet) 16))) - (let* ((dma-state dma-buf) - (run-packet (the-as object (-> dma-state base)))) - (set! (-> (the-as dma-packet run-packet) dma) (new 'static 'dma-tag :id (dma-tag-id cnt))) - (set! (-> (the-as dma-packet run-packet) vif0) (new 'static 'vif-tag :imm #x404 :cmd (vif-cmd stcycl))) - (set! (-> (the-as dma-packet run-packet) vif1) (new 'static 'vif-tag :imm #xa :cmd (vif-cmd mscal) :msk #x1)) - (set! (-> dma-state base) (&+ (the-as pointer run-packet) 16))) + (dma-buffer-add-cnt-vif2 dma-buf + 0 + (new 'static 'vif-tag :imm #x404 :cmd (vif-cmd stcycl)) + (new 'static 'vif-tag :imm #xa :cmd (vif-cmd mscal) :msk #x1)) dma-buf) diff --git a/goal_src/jak1/engine/gfx/shrub/shrubbery-ee-asm.gc b/goal_src/jak1/engine/gfx/shrub/shrubbery-ee-asm.gc new file mode 100644 index 0000000000..2dfa49c26b --- /dev/null +++ b/goal_src/jak1/engine/gfx/shrub/shrubbery-ee-asm.gc @@ -0,0 +1,1988 @@ + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; EE-side conveniences +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; Scratchpad. instance-shrub-dma is overlaid on the work area of the terrain-context, which is what +;; keeps the level header at the bottom of the scratchpad intact -- and is where the otherwise +;; mysterious sixteen in the bank addresses comes from. +(defmacro spr-offset (type &rest path) + `(+ (offset-of terrain-context work) (offset-of ,type ,@path))) + +;; Alternating a pair of banks is one xor of whatever bits their two addresses disagree on. Deriving +;; the masks documents the surprising part for free: the input pair toggles #x1470 rather than the +;; #x1450 the 5200-byte bank spacing suggests, because both addresses carry the work-area bias. +(defconstant SHRUB-INSTANCE-INPUT-FLIP + (logxor (spr-offset instance-shrub-dma instancea) (spr-offset instance-shrub-dma instanceb))) + +(defconstant SHRUB-OUTPUT-FLIP + (logxor (spr-offset instance-shrub-dma outa) (spr-offset instance-shrub-dma outb))) + +;; An output bank holds 128 quadwords. The largest thing one leaf can add to it is 21 quadwords -- +;; a billboard's fourteen plus a seven-quadword instance record -- and the near builder's records +;; are twelve, so each pass hands the bank back while that much room is still left. +(defconstant SHRUB-OUTPUT-BANK-QWC + (/ (- (spr-offset instance-shrub-dma outb) (spr-offset instance-shrub-dma outa)) 16)) + +(defconstant SHRUB-OUTPUT-FLUSH-AT (- SHRUB-OUTPUT-BANK-QWC 22)) + +(defconstant SHRUB-NEAR-OUTPUT-FLUSH-AT (- SHRUB-OUTPUT-BANK-QWC 13)) + + +;; Chain mode with tag transfer enabled, plus the start bit. The node chain's own tags are copied +;; into the scratchpad ahead of each run of instances, which is how the run length the EE stashed in +;; the tag's unused VIF word arrives with the data. +(defconstant DMA-CHCR-CHAIN-TTE-START 324) + +;; A DMA tag's quadword count is its first halfword and its address is its second word, so patching +;; either one in a near-shrub template is a byte or word store rather than a field write. A VIF +;; tag's NUM is its third byte. These three name the displacements instead of leaving 16, 20, 30, +;; 32, 36, 46 ... spread through the builder. +(defmacro shrub-near-tag-qwc (field) + `(offset-of shrub-near-packet ,field dma)) + +(defmacro shrub-near-tag-addr (field) + `(+ (offset-of shrub-near-packet ,field dma) 4)) + +(defmacro shrub-near-unpack-num (field) + `(+ (offset-of shrub-near-packet ,field vif1) 2)) + +(#when PC_PORT + ;; The PC renderer does not execute the shrub VU1 program. + (define shrub-vu1-block (new 'static 'vu-function :length #x0 :qlength #x0))) + +(#unless PC_PORT + (defvu1 shrub-vu1-block + ;; Register aliases for the draw entry. The three setup entries borrow most of these + ;; registers as anonymous quadword shuttles and address temporaries, so they keep their + ;; physical spellings; the names below mean what they say from #x067 onwards. + ;; + ;; The draw loop is a two-deep software pipeline: the -a and -b halves each carry one + ;; vertex from packed input through the transform, the perspective divide, the palette + ;; multiply and the store, one stage apart, so the divide of one vertex overlaps the + ;; transform of the next. That is why every register comes in an -a and a -b copy and why + ;; the same fifteen-pair body appears over and over below. + ;; + ;; vi10 and vf23 through vf27 are deliberately left physical. vi10 holds two unrelated + ;; things -- the -64 end-of-strip sentinel through the loop, then the GIF packet offset + ;; pulled out of kick-base at the end of an instance. vf23 and vf24 are quadwords lifted + ;; straight out of the model's object stream and vf25, vf26 and vf27 are built at entry 0 + ;; with only their z lane set; all five end up in the first quadword of a vertex's output + ;; group, and which of the five is stored is what the strip tests below decide. Naming + ;; them would be inventing a meaning the instructions do not establish. + (rlet ((code-a :reg vi01) ;; strip control code for the -a vertex, from clr-a.w + (dest-a :reg vi02) ;; GIF output address for the -a vertex + (area-sign :reg vi03) ;; sign flag of the screen-space edge cross product + (code-b :reg vi04) ;; strip control code for the -b vertex, from clr-b.w + (dest-b :reg vi05) ;; GIF output address for the -b vertex + (vtx-ptr :reg vi06) ;; cursor into the expanded vertex stream + (clr-ptr :reg vi07) ;; cursor into the expanded color stream + (stq-ptr :reg vi08) ;; cursor into the expanded texture-coordinate stream + (obj-ptr :reg vi09) ;; cursor into the model's object-record stream + (model-bank :reg vi11) ;; base of the model bank being drawn + (stream-qwc :reg vi12) ;; quadwords per stream, so the three cursors are one apart + (matrix-ptr :reg vi13) ;; cursor into the instance ring + (inst-count :reg vi14) ;; instances left in this ring + (kick-addr :reg vi15) ;; start of the GIF packet handed to XGKICK + (vtx-a :reg vf01) ;; packed input vertex, then its clip-space position + (pos-a :reg vf02) ;; screen position, then 12.4 fixed point + (clr-a :reg vf03) ;; model color; w carries the strip control code + (clr-a-lit :reg vf04) ;; model color times the instance color + (stq-a :reg vf05) ;; texture coordinate + (stq-a-q :reg vf06) ;; texture coordinate divided through by w + (slot-a :reg vf07) ;; vertex plus gifbuf; w is this vertex's output address + (vtx-b :reg vf08) + (pos-b :reg vf09) + (clr-b :reg vf10) + (clr-b-lit :reg vf11) + (stq-b :reg vf12) + (stq-b-q :reg vf13) + (slot-b :reg vf14) + (edge-0 :reg vf15) ;; the two screen-space edges whose cross product gives + (edge-1 :reg vf16) ;; the signed area of the triangle about to be emitted + (gifbuf :reg vf17) ;; the two GIF buffer addresses, biased and duplicated + (t-mtx0 :reg vf18) ;; instance transform, rows 0 through 3 + (t-mtx1 :reg vf19) + (t-mtx2 :reg vf20) + (t-mtx3 :reg vf21) + (inst-color :reg vf22) ;; this instance's time-of-day color + (kick-base :reg vf28) ;; biased GIF packet offset for the current instance + (fog-clamp :reg vf29) ;; fog-min and fog-max + (view-consts :reg vf30) ;; y is 2^23, z and w are pfog0 and pfog1 + (adgif-giftag :reg vf31) ;; the four-register A+D tag written ahead of each shader + ) + ;; Entry 0. Load the three quadwords of camera constants and build the address vectors + ;; the rest of the program runs on. + ;; + ;; gifbuf ends up holding the two GIF buffer addresses duplicated as (1 0 1 0) so that + ;; mr32, which rotates the lanes by one, swaps which buffer is in x while leaving the + ;; pair intact -- the same double-buffer trick the other background renderers use. The + ;; addresses are kept as floats biased by 2^23 (view-consts.y), because a small integer + ;; added to 2^23 lands in the low mantissa bits of an exactly representable float: adding + ;; a vertex's output offset to a buffer address is then one vector add, and mtir pulls + ;; the resulting VU address straight back out of the lane. The subtract-from-vf00 and + ;; subtract-from-self steps in the entry-17 path below are the same arithmetic run + ;; backwards to reach the second buffer. + (vu-pair (lq.xyzw adgif-giftag vi00 (shrub-view-const texture-giftag)) (nop)) ;; #x000 + (vu-pair (lq.xyzw view-consts vi00 (shrub-view-const consts)) (nop)) ;; #x001 + (vu-pair (lq.xyzw fog-clamp vi00 (shrub-view-const fog-clamp)) (nop)) ;; #x002 + (vu-pair (iaddiu vi11 vi00 SHRUB-VU-MODEL-BANK-B) (nop)) ;; #x003 + (vu-pair (iaddiu vi01 vi00 SHRUB-VU-GIF-BUFFER-1) (nop)) ;; #x004 + (vu-pair (mfir.x gifbuf vi01) (nop)) ;; #x005 + (vu-pair (mfir.z gifbuf vi01) (nop)) ;; #x006 + (vu-pair (iaddiu vi01 vi00 SHRUB-VU-GIF-BUFFER-0) (nop)) ;; #x007 + (vu-pair (mfir.y gifbuf vi01) (nop)) ;; #x008 + (vu-pair (mfir.w gifbuf vi01) (nop)) ;; #x009 + ;; Still entry 0: three of the five candidate first-quadwords for a vertex's output + ;; group get their z lane. The E bit at #x00f stops the program and #x010 runs in its + ;; delay pair, which is where the biased buffer addresses are finally formed. + (vu-pair (iaddi vi01 vi00 0x1) (nop)) ;; #x00a + (vu-pair (mfir.z vf25 vi01) (nop)) ;; #x00b + (vu-pair (iaddi vi01 vi00 0x1) (itof0.xyzw gifbuf gifbuf)) ;; #x00c + (vu-pair (mfir.z vf26 vi01) (nop)) ;; #x00d + (vu-pair (iaddi vi01 vi00 0x1) (nop)) ;; #x00e + (vu-pair (mfir.z vf27 vi01) (nop :e)) ;; #x00f + (vu-pair (nop) (addy.xyzw gifbuf gifbuf view-consts)) ;; #x010 + ;; Entries 17 and 21 prepare a model that VIF has just unpacked into one of the two + ;; banks. They are the same code with one difference -- which instance ring the following + ;; draw entry will read -- so they both fall into model-init-common after selecting it. + ;; + ;; Three things happen here, once per model rather than once per instance, which is the + ;; whole point of splitting them out of the draw entry: + ;; + ;; * the object stream is scattered into both GIF output buffers. Each quadword of that + ;; stream carries its own destination in its w lane, so the loop is: read a quadword, + ;; mtir the offset out of w, store the quadword at that offset in buffer 0 and again + ;; at the same offset in buffer 1. This is what lays down the static shape of the + ;; packet -- the strip tags and the per-shader register writes -- so the draw loop + ;; only has to fill in the vertex triples between them. + ;; * each shader becomes five quadwords: the four-register A+D tag in adgif-giftag + ;; followed by the four register writes the exporter packed, again into both buffers. + ;; * the packed vertex, color and texture-coordinate streams are converted in place to + ;; the formats the transform wants: 16-bit vertices and colors to float, texture + ;; coordinates from 20.12 fixed point. Two quadwords per iteration, expanding over + ;; the same addresses they were read from. + (vu-pair (iaddiu vi01 vi00 SHRUB-VU-MODEL-BANK-SUM) (subx.xz gifbuf vf00 gifbuf)) ;; #x011 + (vu-pair (isub vi11 vi01 vi11) (addy.xyzw kick-base vf00 view-consts)) ;; #x012 + (vu-pair (b model-init-common) (nop)) ;; #x013 + (vu-pair (iaddiu vi13 vi00 SHRUB-VU-RING-B) (nop)) ;; #x014 + (vu-pair (iaddiu vi01 vi00 SHRUB-VU-MODEL-BANK-SUM) (subx.xz gifbuf vf00 gifbuf)) ;; #x015 + (vu-pair (isub vi11 vi01 vi11) (addy.xyzw kick-base vf00 view-consts)) ;; #x016 + (vu-pair (iaddiu vi13 vi00 SHRUB-VU-RING-A) (nop)) ;; #x017 + (label model-init-common) + (vu-pair (isw.y vi13 vi00 (shrub-view-const mtx-buf-ptr)) (nop)) ;; #x018 + (vu-pair (iaddi vi09 vi11 0x1) (suby.xz gifbuf gifbuf gifbuf)) ;; #x019 + (vu-pair (ilwr.y vi05 vi11) (nop)) ;; #x01a + (vu-pair (ilwr.x vi15 vi11) (nop)) ;; #x01b + (vu-pair (lqi.xyzw vf01 vi09) (nop)) ;; #x01c + (vu-pair (nop) (addx.xz gifbuf gifbuf view-consts)) ;; #x01d + (vu-pair (nop) (nop)) ;; #x01e + (vu-pair (iaddi vi15 vi15 -0x1) (nop)) ;; #x01f + (vu-pair (mtir vi04 vf01.w) (nop)) ;; #x020 + (vu-pair (mtir vi02 gifbuf.x) (nop)) ;; #x021 + (vu-pair (ibne vi00 vi15 relocate-object-loop) (nop)) ;; #x022 + (vu-pair (mtir vi03 gifbuf.y) (nop)) ;; #x023 + (vu-pair (nop) (addz.y kick-base kick-base adgif-giftag)) ;; #x024 + (label relocate-object-loop) + (vu-pair (lqi.xyzw vf02 vi09) (nop)) ;; #x025 + (vu-pair (iadd vi01 vi04 vi02) (nop)) ;; #x026 + (vu-pair (iadd vi14 vi04 vi03) (nop)) ;; #x027 + (vu-pair (iaddi vi05 vi05 -0x1) (nop)) ;; #x028 + (vu-pair (mtir vi04 vf02.w) (nop)) ;; #x029 + (vu-pair (sq.xyzw vf01 vi01 0) (nop)) ;; #x02a + (vu-pair (ibeq vi00 vi05 begin-shader-relocation) (nop)) ;; #x02b + (vu-pair (sq.xyzw vf01 vi14 0) (nop)) ;; #x02c + (vu-pair (lqi.xyzw vf01 vi09) (nop)) ;; #x02d + (vu-pair (iadd vi01 vi04 vi02) (nop)) ;; #x02e + (vu-pair (iadd vi14 vi04 vi03) (nop)) ;; #x02f + (vu-pair (iaddi vi05 vi05 -0x1) (nop)) ;; #x030 + (vu-pair (mtir vi04 vf01.w) (nop)) ;; #x031 + (vu-pair (sq.xyzw vf02 vi01 0) (nop)) ;; #x032 + (vu-pair (ibne vi00 vi05 relocate-object-loop) (nop)) ;; #x033 + (vu-pair (sq.xyzw vf02 vi14 0) (nop)) ;; #x034 + (label begin-shader-relocation) + (vu-pair (iaddi vi09 vi09 -0x1) (nop)) ;; #x035 + (vu-pair (ilwr.x vi05 vi11) (nop)) ;; #x036 + (label relocate-shader-loop) + (vu-pair (lqi.xyzw vf01 vi09) (nop)) ;; #x037 + (vu-pair (lqi.xyzw vf02 vi09) (nop)) ;; #x038 + (vu-pair (lqi.xyzw vf03 vi09) (nop)) ;; #x039 + (vu-pair (lqi.xyzw vf04 vi09) (nop)) ;; #x03a + (vu-pair (mtir vi04 vf01.w) (nop)) ;; #x03b + (vu-pair (iaddi vi05 vi05 -0x1) (nop)) ;; #x03c + (vu-pair (nop) (nop)) ;; #x03d + (vu-pair (nop) (nop)) ;; #x03e + (vu-pair (iadd vi01 vi04 vi02) (nop)) ;; #x03f + (vu-pair (sq.xyzw adgif-giftag vi01 0) (nop)) ;; #x040 + (vu-pair (sq.xyzw vf01 vi01 1) (nop)) ;; #x041 + (vu-pair (sq.xyzw vf02 vi01 2) (nop)) ;; #x042 + (vu-pair (sq.xyzw vf03 vi01 3) (nop)) ;; #x043 + (vu-pair (sq.xyzw vf04 vi01 4) (nop)) ;; #x044 + (vu-pair (iadd vi01 vi04 vi03) (nop)) ;; #x045 + (vu-pair (sq.xyzw adgif-giftag vi01 0) (nop)) ;; #x046 + (vu-pair (sq.xyzw vf01 vi01 1) (nop)) ;; #x047 + (vu-pair (sq.xyzw vf02 vi01 2) (nop)) ;; #x048 + (vu-pair (sq.xyzw vf03 vi01 3) (nop)) ;; #x049 + (vu-pair (ibne vi00 vi05 relocate-shader-loop) (nop)) ;; #x04a + (vu-pair (sq.xyzw vf04 vi01 4) (nop)) ;; #x04b + (vu-pair (ilwr.z vi12 vi11) (nop)) ;; #x04c + (vu-pair (ilwr.w vi06 vi11) (nop)) ;; #x04d + (vu-pair (isw.x vi09 vi00 (shrub-view-const tex-start-ptr)) (nop)) ;; #x04e + (vu-pair (iaddi vi15 vi00 0x1) (nop)) ;; #x04f + (vu-pair (ior vi05 vi12 vi00) (nop)) ;; #x050 + (vu-pair (iadd vi06 vi06 vi11) (nop)) ;; #x051 + (vu-pair (iadd vi07 vi06 vi12) (nop)) ;; #x052 + (vu-pair (iadd vi08 vi07 vi12) (nop)) ;; #x053 + (vu-pair (iaddi vi05 vi05 0x1) (nop)) ;; #x054 + (label expand-model-stream-loop) + (vu-pair (lq.xyzw vf01 vi06 0) (nop)) ;; #x055 + (vu-pair (lq.xyz vf03 vi07 0) (nop)) ;; #x056 + (vu-pair (lq.xyz vf05 vi08 0) (nop)) ;; #x057 + (vu-pair (iaddi vi05 vi05 -0x2) (nop)) ;; #x058 + (vu-pair (lq.xyzw vf08 vi06 1) (itof0.xyzw vf01 vf01)) ;; #x059 + (vu-pair (lq.xyz vf10 vi07 1) (itof0.xyzw vf03 vf03)) ;; #x05a + (vu-pair (lq.xyz vf12 vi08 1) (itof12.xyzw vf05 vf05)) ;; #x05b + (vu-pair (nop) (nop)) ;; #x05c + (vu-pair (sqi.xyzw vf01 vi06) (itof0.xyzw vf08 vf08)) ;; #x05d + (vu-pair (sqi.xyz vf03 vi07) (itof0.xyzw vf10 vf10)) ;; #x05e + (vu-pair (ibeq vi00 vi05 model-init-done) (itof12.xyzw vf12 vf12)) ;; #x05f + (vu-pair (sqi.xyz vf05 vi08) (nop)) ;; #x060 + (vu-pair (sqi.xyzw vf08 vi06) (nop)) ;; #x061 + (vu-pair (sqi.xyz vf10 vi07) (nop)) ;; #x062 + (vu-pair (ibne vi15 vi05 expand-model-stream-loop) (nop)) ;; #x063 + (vu-pair (sqi.xyz vf12 vi08) (nop)) ;; #x064 + (label model-init-done) + (vu-pair (nop) (nop :e)) ;; #x065 + (vu-pair (nop) (nop)) ;; #x066 + ;; Entry 103 draws one ring of instances against the model prepared above. + ;; + ;; Setup: flip the saved ring pointer to the ring this call owns, read its count word, + ;; and load the first instance's four transform rows and its time-of-day color. Fog + ;; comes out of the translation row: row3.z times pfog0 plus pfog1, clamped by fog-max, + ;; and converted to 12.4, so the whole instance is fogged as one flat value rather than + ;; per vertex. The three stream cursors are then laid out one stream-qwc apart from the + ;; model header's own base, and the pipeline is primed with the first two vertices. + ;; + ;; Steady state, per vertex: add gifbuf to the packed vertex so its w lane becomes an + ;; absolute output address, transform it by the four rows, start the divide by w, scale + ;; the texture coordinate by the reciprocal, multiply the model color by the instance + ;; color, convert the screen position to 12.4, and store the resulting triple at the + ;; address the vertex named. There is no output cursor and no ordering constraint between + ;; vertices, which is exactly what lets two of them stay in flight. + ;; + ;; The test between the halves is a triangle facing test done in screen space, and it is + ;; the reason the body below is repeated so many times. edge-0 and edge-1 are the two + ;; screen-space edges leaving the shared vertex; mulax/msubax across their x and y lanes + ;; forms the 2-D cross product, whose sign is the triangle's winding, and fsand lifts + ;; that sign bit out of the status flags into area-sign. Each vertex carries its own + ;; expected winding in the w lane of its color quadword -- which alternates along a + ;; triangle strip -- so comparing code against area-sign says whether this triangle faces + ;; the camera. A negative code instead means end of strip. Rather than branch on the + ;; result and stall, the loop is unrolled once per (pipeline half, comparison outcome) + ;; pair, which is why the same fifteen pairs appear as the strip-stage-* variants and why + ;; the only thing that actually differs between them is which quadword lands in the first + ;; slot of the vertex's output group. + (vu-pair (ilw.y matrix-ptr vi00 (shrub-view-const mtx-buf-ptr)) (nop)) ;; #x067 + (vu-pair (iaddiu code-a vi00 SHRUB-VU-RING-SUM) (nop)) ;; #x068 + (vu-pair (ilwr.w vtx-ptr model-bank) (nop)) ;; #x069 + (vu-pair (ilw.x obj-ptr vi00 (shrub-view-const tex-start-ptr)) (nop)) ;; #x06a + (vu-pair (isub matrix-ptr code-a matrix-ptr) (nop)) ;; #x06b + (vu-pair (isw.y matrix-ptr vi00 (shrub-view-const mtx-buf-ptr)) (nop)) ;; #x06c + (vu-pair (ilwr.x inst-count matrix-ptr) (nop)) ;; #x06d + (vu-pair (lq.xyzw t-mtx3 matrix-ptr (shrub-matrix-qword mat vector 3)) (nop)) ;; #x06e + (vu-pair (lq.xyzw t-mtx0 matrix-ptr (shrub-matrix-qword mat vector 0)) (nop)) ;; #x06f + (vu-pair (lq.xyzw t-mtx1 matrix-ptr (shrub-matrix-qword mat vector 1)) (nop)) ;; #x070 + (vu-pair (lq.xyzw t-mtx2 matrix-ptr (shrub-matrix-qword mat vector 2)) (nop)) ;; #x071 + (vu-pair (lq.xyzw inst-color matrix-ptr (shrub-matrix-qword color)) (mulaz.w ACC t-mtx3 view-consts)) ;; #x072 + (vu-pair (iaddi matrix-ptr matrix-ptr (+ SHRUB-VU-MATRIX-STRIDE 1)) (maddw.w pos-b view-consts vf00)) ;; #x073 + (vu-pair (iadd vtx-ptr vtx-ptr model-bank) (nop)) ;; #x074 + (vu-pair (iadd clr-ptr vtx-ptr stream-qwc) (nop)) ;; #x075 + (vu-pair (iadd stq-ptr clr-ptr stream-qwc) (nop)) ;; #x076 + (vu-pair (move.w clr-a-lit inst-color) (miniy.w pos-b pos-b fog-clamp)) ;; #x077 + (vu-pair (move.w clr-b-lit inst-color) (nop)) ;; #x078 + (vu-pair (lqi.xyzw vtx-a vtx-ptr) (nop)) ;; #x079 + (vu-pair (lqi.xyzw stq-a stq-ptr) (nop)) ;; #x07a + (vu-pair (lqi.xyzw clr-a clr-ptr) (ftoi4.w pos-a pos-b)) ;; #x07b + (vu-pair (lqi.xyzw vtx-b vtx-ptr) (ftoi4.w pos-b pos-b)) ;; #x07c + (vu-pair (lqi.xyzw stq-b stq-ptr) (add.xyzw slot-a vtx-a gifbuf)) ;; #x07d + (vu-pair (lqi.xyzw clr-b clr-ptr) (mulaw.xyzw ACC t-mtx3 vf00)) ;; #x07e + (vu-pair (nop) (maddax.xyzw ACC t-mtx0 vtx-a)) ;; #x07f + (vu-pair (nop) (madday.xyzw ACC t-mtx1 vtx-a)) ;; #x080 + (vu-pair (nop) (maddz.xyzw vtx-a t-mtx2 vtx-a)) ;; #x081 + (vu-pair (nop) (add.xyzw slot-b vtx-b gifbuf)) ;; #x082 + (vu-pair (nop) (mulaw.xyzw ACC t-mtx3 vf00)) ;; #x083 + (vu-pair (nop) (maddax.xyzw ACC t-mtx0 vtx-b)) ;; #x084 + (vu-pair (div Q vf00.w vtx-a.w) (madday.xyzw ACC t-mtx1 vtx-b)) ;; #x085 + (vu-pair (nop) (maddz.xyzw vtx-b t-mtx2 vtx-b)) ;; #x086 + (vu-pair (nop) (nop)) ;; #x087 + (vu-pair (nop) (nop)) ;; #x088 + (vu-pair (nop) (nop)) ;; #x089 + (vu-pair (nop) (nop)) ;; #x08a + (vu-pair (nop) (nop)) ;; #x08b + (vu-pair (nop) (mul.xyz pos-a vtx-a Q)) ;; #x08c + (vu-pair (nop) (mul.xyzw stq-a-q stq-a Q)) ;; #x08d + (vu-pair (lqi.xyzw vtx-a vtx-ptr) (mul.xyz clr-a-lit clr-a inst-color)) ;; #x08e + (vu-pair (div Q vf00.w vtx-b.w) (nop)) ;; #x08f + (vu-pair (lqi.xyzw stq-a stq-ptr) (nop)) ;; #x090 + (vu-pair (mtir dest-a slot-a.w) (nop)) ;; #x091 + (vu-pair (lqi.xyzw clr-a clr-ptr) (nop)) ;; #x092 + (vu-pair (nop) (nop)) ;; #x093 + (vu-pair (nop) (add.xyzw slot-a vtx-a gifbuf)) ;; #x094 + (vu-pair (nop) (mulaw.xyzw ACC t-mtx3 vf00)) ;; #x095 + (vu-pair (nop) (maddax.xyzw ACC t-mtx0 vtx-a)) ;; #x096 + (vu-pair (nop) (madday.xyzw ACC t-mtx1 vtx-a)) ;; #x097 + (vu-pair (nop) (maddz.xyzw vtx-a t-mtx2 vtx-a)) ;; #x098 + (vu-pair (nop) (mul.xyz pos-b vtx-b Q)) ;; #x099 + (vu-pair (nop) (mul.xyzw stq-b-q stq-b Q)) ;; #x09a + (label vertex-pipe-a-even) + (vu-pair (lqi.xyzw vtx-b vtx-ptr) (mul.xyz clr-b-lit clr-b inst-color)) ;; #x09b + (vu-pair (div Q vf00.w vtx-a.w) (nop)) ;; #x09c + (vu-pair (lqi.xyzw stq-b stq-ptr) (nop)) ;; #x09d + (vu-pair (mtir dest-b slot-b.w) (nop)) ;; #x09e + (vu-pair (lqi.xyzw clr-b clr-ptr) (sub.xyzw edge-0 pos-a pos-b)) ;; #x09f + (vu-pair (sq.xyzw stq-a-q dest-a 1) (ftoi4.xyz pos-a pos-a)) ;; #x0a0 + (vu-pair (sq.xyzw clr-a-lit dest-a 2) (add.xyzw slot-b vtx-b gifbuf)) ;; #x0a1 + (vu-pair (lqi.xyzw vf23 obj-ptr) (mulaw.xyzw ACC t-mtx3 vf00)) ;; #x0a2 + (vu-pair (lqi.xyzw vf24 obj-ptr) (maddax.xyzw ACC t-mtx0 vtx-b)) ;; #x0a3 + (vu-pair (sq.xyzw pos-a dest-a 3) (madday.xyzw ACC t-mtx1 vtx-b)) ;; #x0a4 + (vu-pair (isubiu vi10 vi00 0x40) (maddz.xyzw vtx-b t-mtx2 vtx-b)) ;; #x0a5 + (label vertex-pipe-b-even) + (vu-pair (mtir code-a clr-a.w) (mul.xyz pos-a vtx-a Q)) ;; #x0a6 + (vu-pair (sq.xyzw vf25 dest-a 0) (mul.xyzw stq-a-q stq-a Q)) ;; #x0a7 + (vu-pair (lqi.xyzw vtx-a vtx-ptr) (mul.xyz clr-a-lit clr-a inst-color)) ;; #x0a8 + (vu-pair (div Q vf00.w vtx-b.w) (nop)) ;; #x0a9 + (vu-pair (lqi.xyzw stq-a stq-ptr) (nop)) ;; #x0aa + (vu-pair (mtir dest-a slot-a.w) (nop)) ;; #x0ab + (vu-pair (lqi.xyzw clr-a clr-ptr) (sub.xyzw edge-1 pos-b pos-a)) ;; #x0ac + (vu-pair (sq.xyzw stq-b-q dest-b 1) (ftoi4.xyz pos-b pos-b)) ;; #x0ad + (vu-pair (sq.xyzw clr-b-lit dest-b 2) (add.xyzw slot-a vtx-a gifbuf)) ;; #x0ae + (vu-pair (nop) (mulaw.xyzw ACC t-mtx3 vf00)) ;; #x0af + (vu-pair (nop) (maddax.xyzw ACC t-mtx0 vtx-a)) ;; #x0b0 + (vu-pair (sq.xyzw pos-b dest-b 3) (madday.xyzw ACC t-mtx1 vtx-a)) ;; #x0b1 + (vu-pair (nop) (maddz.xyzw vtx-a t-mtx2 vtx-a)) ;; #x0b2 + (vu-pair (mtir code-b clr-b.w) (mul.xyz pos-b vtx-b Q)) ;; #x0b3 + (vu-pair (sq.xyzw vf25 dest-b 0) (mul.xyzw stq-b-q stq-b Q)) ;; #x0b4 + (vu-pair (lqi.xyzw vtx-b vtx-ptr) (mul.xyz clr-b-lit clr-b inst-color)) ;; #x0b5 + (vu-pair (div Q vf00.w vtx-a.w) (nop)) ;; #x0b6 + (vu-pair (lqi.xyzw stq-b stq-ptr) (mulay.x ACC edge-0 edge-1)) ;; #x0b7 + (vu-pair (mtir dest-b slot-b.w) (msubay.x ACC edge-1 edge-0)) ;; #x0b8 + (vu-pair (lqi.xyzw clr-b clr-ptr) (sub.xyzw edge-0 pos-a pos-b)) ;; #x0b9 + (vu-pair (sq.xyzw stq-a-q dest-a 1) (ftoi4.xyz pos-a pos-a)) ;; #x0ba + (vu-pair (sq.xyzw clr-a-lit dest-a 2) (add.xyzw slot-b vtx-b gifbuf)) ;; #x0bb + (vu-pair (fsand area-sign 0x2) (mulaw.xyzw ACC t-mtx3 vf00)) ;; #x0bc + (vu-pair (ibltz code-a strip-stage-a0) (maddax.xyzw ACC t-mtx0 vtx-b)) ;; #x0bd + (vu-pair (sq.xyzw pos-a dest-a 3) (madday.xyzw ACC t-mtx1 vtx-b)) ;; #x0be + (label compare-strip-edge-a) + (vu-pair (ibeq code-a area-sign select-strip-path-b) (maddz.xyzw vtx-b t-mtx2 vtx-b)) ;; #x0bf + (vu-pair (mtir code-a clr-a.w) (mul.xyz pos-a vtx-a Q)) ;; #x0c0 + (vu-pair (b select-strip-path-a) (nop)) ;; #x0c1 + (vu-pair (sq.xyzw vf23 dest-a 0) (mul.xyzw stq-a-q stq-a Q)) ;; #x0c2 + (vu-pair (nop) (mul.xyzw stq-a-q stq-a Q)) ;; #x0c3 + (label vertex-pipe-a-odd) + (vu-pair (lqi.xyzw vtx-a vtx-ptr) (mul.xyz clr-a-lit clr-a inst-color)) ;; #x0c4 + (vu-pair (div Q vf00.w vtx-b.w) (nop)) ;; #x0c5 + (vu-pair (lqi.xyzw stq-a stq-ptr) (nop)) ;; #x0c6 + (vu-pair (mtir dest-a slot-a.w) (nop)) ;; #x0c7 + (vu-pair (lqi.xyzw clr-a clr-ptr) (sub.xyzw edge-1 pos-b pos-a)) ;; #x0c8 + (vu-pair (sq.xyzw stq-b-q dest-b 1) (ftoi4.xyz pos-b pos-b)) ;; #x0c9 + (vu-pair (sq.xyzw clr-b-lit dest-b 2) (add.xyzw slot-a vtx-a gifbuf)) ;; #x0ca + (vu-pair (lqi.xyzw vf23 obj-ptr) (mulaw.xyzw ACC t-mtx3 vf00)) ;; #x0cb + (vu-pair (lqi.xyzw vf24 obj-ptr) (maddax.xyzw ACC t-mtx0 vtx-a)) ;; #x0cc + (vu-pair (sq.xyzw pos-b dest-b 3) (madday.xyzw ACC t-mtx1 vtx-a)) ;; #x0cd + (vu-pair (isubiu vi10 vi00 0x40) (maddz.xyzw vtx-a t-mtx2 vtx-a)) ;; #x0ce + (label vertex-pipe-b-odd) + (vu-pair (mtir code-b clr-b.w) (mul.xyz pos-b vtx-b Q)) ;; #x0cf + (vu-pair (sq.xyzw vf25 dest-b 0) (mul.xyzw stq-b-q stq-b Q)) ;; #x0d0 + (vu-pair (lqi.xyzw vtx-b vtx-ptr) (mul.xyz clr-b-lit clr-b inst-color)) ;; #x0d1 + (vu-pair (div Q vf00.w vtx-a.w) (nop)) ;; #x0d2 + (vu-pair (lqi.xyzw stq-b stq-ptr) (nop)) ;; #x0d3 + (vu-pair (mtir dest-b slot-b.w) (nop)) ;; #x0d4 + (vu-pair (lqi.xyzw clr-b clr-ptr) (sub.xyzw edge-0 pos-a pos-b)) ;; #x0d5 + (vu-pair (sq.xyzw stq-a-q dest-a 1) (ftoi4.xyz pos-a pos-a)) ;; #x0d6 + (vu-pair (sq.xyzw clr-a-lit dest-a 2) (add.xyzw slot-b vtx-b gifbuf)) ;; #x0d7 + (vu-pair (nop) (mulaw.xyzw ACC t-mtx3 vf00)) ;; #x0d8 + (vu-pair (nop) (maddax.xyzw ACC t-mtx0 vtx-b)) ;; #x0d9 + (vu-pair (sq.xyzw pos-a dest-a 3) (madday.xyzw ACC t-mtx1 vtx-b)) ;; #x0da + (vu-pair (nop) (maddz.xyzw vtx-b t-mtx2 vtx-b)) ;; #x0db + (vu-pair (mtir code-a clr-a.w) (mul.xyz pos-a vtx-a Q)) ;; #x0dc + (vu-pair (sq.xyzw vf25 dest-a 0) (mul.xyzw stq-a-q stq-a Q)) ;; #x0dd + (vu-pair (lqi.xyzw vtx-a vtx-ptr) (mul.xyz clr-a-lit clr-a inst-color)) ;; #x0de + (vu-pair (div Q vf00.w vtx-b.w) (nop)) ;; #x0df + (vu-pair (lqi.xyzw stq-a stq-ptr) (mulay.x ACC edge-0 edge-1)) ;; #x0e0 + (vu-pair (mtir dest-a slot-a.w) (msubay.x ACC edge-1 edge-0)) ;; #x0e1 + (vu-pair (lqi.xyzw clr-a clr-ptr) (sub.xyzw edge-1 pos-b pos-a)) ;; #x0e2 + (vu-pair (sq.xyzw stq-b-q dest-b 1) (ftoi4.xyz pos-b pos-b)) ;; #x0e3 + (vu-pair (sq.xyzw clr-b-lit dest-b 2) (add.xyzw slot-a vtx-a gifbuf)) ;; #x0e4 + (vu-pair (fsand area-sign 0x2) (mulaw.xyzw ACC t-mtx3 vf00)) ;; #x0e5 + (vu-pair (ibltz code-b reload-strip-b) (maddax.xyzw ACC t-mtx0 vtx-a)) ;; #x0e6 + (vu-pair (sq.xyzw pos-b dest-b 3) (madday.xyzw ACC t-mtx1 vtx-a)) ;; #x0e7 + (label compare-strip-edge-b) + (vu-pair (ibeq code-b area-sign advance-strip-a) (maddz.xyzw vtx-a t-mtx2 vtx-a)) ;; #x0e8 + (vu-pair (mtir code-b clr-b.w) (mul.xyz pos-b vtx-b Q)) ;; #x0e9 + (vu-pair (b continue-strip-b) (nop)) ;; #x0ea + (vu-pair (sq.xyzw vf23 dest-b 0) (mul.xyzw stq-b-q stq-b Q)) ;; #x0eb + (label continue-strip-a) + (vu-pair (sq.xyzw vf26 dest-a 0) (mul.xyzw stq-a-q stq-a Q)) ;; #x0ec + (label select-strip-path-a) + (vu-pair (lqi.xyzw vtx-a vtx-ptr) (mul.xyz clr-a-lit clr-a inst-color)) ;; #x0ed + (vu-pair (div Q vf00.w vtx-b.w) (nop)) ;; #x0ee + (vu-pair (lqi.xyzw stq-a stq-ptr) (mulay.x ACC edge-0 edge-1)) ;; #x0ef + (vu-pair (mtir dest-a slot-a.w) (msubay.x ACC edge-1 edge-0)) ;; #x0f0 + (vu-pair (lqi.xyzw clr-a clr-ptr) (sub.xyzw edge-1 pos-b pos-a)) ;; #x0f1 + (vu-pair (sq.xyzw stq-b-q dest-b 1) (ftoi4.xyz pos-b pos-b)) ;; #x0f2 + (vu-pair (sq.xyzw clr-b-lit dest-b 2) (add.xyzw slot-a vtx-a gifbuf)) ;; #x0f3 + (vu-pair (fsand area-sign 0x2) (mulaw.xyzw ACC t-mtx3 vf00)) ;; #x0f4 + (vu-pair (ibltz code-b flush-strip-a) (maddax.xyzw ACC t-mtx0 vtx-a)) ;; #x0f5 + (vu-pair (sq.xyzw pos-b dest-b 3) (madday.xyzw ACC t-mtx1 vtx-a)) ;; #x0f6 + (vu-pair (ibeq code-b area-sign advance-strip-a) (maddz.xyzw vtx-a t-mtx2 vtx-a)) ;; #x0f7 + (vu-pair (mtir code-b clr-b.w) (mul.xyz pos-b vtx-b Q)) ;; #x0f8 + (vu-pair (sq.xyzw vf26 dest-b 0) (mul.xyzw stq-b-q stq-b Q)) ;; #x0f9 + (label continue-strip-b) + (vu-pair (lqi.xyzw vtx-b vtx-ptr) (mul.xyz clr-b-lit clr-b inst-color)) ;; #x0fa + (vu-pair (div Q vf00.w vtx-a.w) (nop)) ;; #x0fb + (vu-pair (lqi.xyzw stq-b stq-ptr) (mulay.x ACC edge-0 edge-1)) ;; #x0fc + (vu-pair (mtir dest-b slot-b.w) (msubay.x ACC edge-1 edge-0)) ;; #x0fd + (vu-pair (lqi.xyzw clr-b clr-ptr) (sub.xyzw edge-0 pos-a pos-b)) ;; #x0fe + (vu-pair (sq.xyzw stq-a-q dest-a 1) (ftoi4.xyz pos-a pos-a)) ;; #x0ff + (vu-pair (sq.xyzw clr-a-lit dest-a 2) (add.xyzw slot-b vtx-b gifbuf)) ;; #x100 + (vu-pair (fsand area-sign 0x2) (mulaw.xyzw ACC t-mtx3 vf00)) ;; #x101 + (vu-pair (ibltz code-a reload-strip-a) (maddax.xyzw ACC t-mtx0 vtx-b)) ;; #x102 + (vu-pair (sq.xyzw pos-a dest-a 3) (madday.xyzw ACC t-mtx1 vtx-b)) ;; #x103 + (vu-pair (ibne code-a area-sign continue-strip-a) (maddz.xyzw vtx-b t-mtx2 vtx-b)) ;; #x104 + (vu-pair (mtir code-a clr-a.w) (mul.xyz pos-a vtx-a Q)) ;; #x105 + (label select-strip-path-b) + (vu-pair (b dispatch-strip-state) (nop)) ;; #x106 + (vu-pair (sq.xyzw vf24 dest-a 0) (mul.xyzw stq-a-q stq-a Q)) ;; #x107 + (label advance-strip-a) + (vu-pair (b finish-strip-a) (nop)) ;; #x108 + (vu-pair (sq.xyzw vf24 dest-b 0) (mul.xyzw stq-b-q stq-b Q)) ;; #x109 + (label advance-strip-b) + (vu-pair (sq.xyzw vf27 dest-a 0) (mul.xyzw stq-a-q stq-a Q)) ;; #x10a + (label dispatch-strip-state) + (vu-pair (lqi.xyzw vtx-a vtx-ptr) (mul.xyz clr-a-lit clr-a inst-color)) ;; #x10b + (vu-pair (div Q vf00.w vtx-b.w) (nop)) ;; #x10c + (vu-pair (lqi.xyzw stq-a stq-ptr) (mulax.y ACC edge-0 edge-1)) ;; #x10d + (vu-pair (mtir dest-a slot-a.w) (msubax.y ACC edge-1 edge-0)) ;; #x10e + (vu-pair (lqi.xyzw clr-a clr-ptr) (sub.xyzw edge-1 pos-b pos-a)) ;; #x10f + (vu-pair (sq.xyzw stq-b-q dest-b 1) (ftoi4.xyz pos-b pos-b)) ;; #x110 + (vu-pair (sq.xyzw clr-b-lit dest-b 2) (add.xyzw slot-a vtx-a gifbuf)) ;; #x111 + (vu-pair (fsand area-sign 0x2) (mulaw.xyzw ACC t-mtx3 vf00)) ;; #x112 + (vu-pair (ibltz code-b loop-strip-a) (maddax.xyzw ACC t-mtx0 vtx-a)) ;; #x113 + (vu-pair (sq.xyzw pos-b dest-b 3) (madday.xyzw ACC t-mtx1 vtx-a)) ;; #x114 + (vu-pair (ibeq code-b area-sign finish-strip-b) (maddz.xyzw vtx-a t-mtx2 vtx-a)) ;; #x115 + (vu-pair (mtir code-b clr-b.w) (mul.xyz pos-b vtx-b Q)) ;; #x116 + (vu-pair (sq.xyzw vf27 dest-b 0) (mul.xyzw stq-b-q stq-b Q)) ;; #x117 + (label finish-strip-a) + (vu-pair (lqi.xyzw vtx-b vtx-ptr) (mul.xyz clr-b-lit clr-b inst-color)) ;; #x118 + (vu-pair (div Q vf00.w vtx-a.w) (nop)) ;; #x119 + (vu-pair (lqi.xyzw stq-b stq-ptr) (mulax.y ACC edge-0 edge-1)) ;; #x11a + (vu-pair (mtir dest-b slot-b.w) (msubax.y ACC edge-1 edge-0)) ;; #x11b + (vu-pair (lqi.xyzw clr-b clr-ptr) (sub.xyzw edge-0 pos-a pos-b)) ;; #x11c + (vu-pair (sq.xyzw stq-a-q dest-a 1) (ftoi4.xyz pos-a pos-a)) ;; #x11d + (vu-pair (sq.xyzw clr-a-lit dest-a 2) (add.xyzw slot-b vtx-b gifbuf)) ;; #x11e + (vu-pair (fsand area-sign 0x2) (mulaw.xyzw ACC t-mtx3 vf00)) ;; #x11f + (vu-pair (ibltz code-a flush-strip-b) (maddax.xyzw ACC t-mtx0 vtx-b)) ;; #x120 + (vu-pair (sq.xyzw pos-a dest-a 3) (madday.xyzw ACC t-mtx1 vtx-b)) ;; #x121 + (vu-pair (ibne code-a area-sign advance-strip-b) (maddz.xyzw vtx-b t-mtx2 vtx-b)) ;; #x122 + (vu-pair (mtir code-a clr-a.w) (mul.xyz pos-a vtx-a Q)) ;; #x123 + (vu-pair (b select-strip-path-a) (nop)) ;; #x124 + (vu-pair (sq.xyzw vf23 dest-a 0) (mul.xyzw stq-a-q stq-a Q)) ;; #x125 + (label finish-strip-b) + (vu-pair (b continue-strip-b) (nop)) ;; #x126 + (vu-pair (sq.xyzw vf23 dest-b 0) (mul.xyzw stq-b-q stq-b Q)) ;; #x127 + (label flush-strip-a) + (vu-pair (ibne vi10 code-b strip-stage-a1) (nop)) ;; #x128 + (vu-pair (iaddiu code-b code-b 0x80) (nop)) ;; #x129 + (vu-pair (lqi.xyzw vf23 obj-ptr) (nop)) ;; #x12a + (vu-pair (b vertex-pipe-b-odd) (nop)) ;; #x12b + (vu-pair (lqi.xyzw vf24 obj-ptr) (maddz.xyzw vtx-a t-mtx2 vtx-a)) ;; #x12c + (label reload-strip-a) + (vu-pair (ibne vi10 code-a kick-first-buffer) (nop)) ;; #x12d + (vu-pair (iaddiu code-a code-a 0x80) (nop)) ;; #x12e + (vu-pair (lqi.xyzw vf23 obj-ptr) (nop)) ;; #x12f + (vu-pair (b vertex-pipe-b-even) (nop)) ;; #x130 + (vu-pair (lqi.xyzw vf24 obj-ptr) (maddz.xyzw vtx-b t-mtx2 vtx-b)) ;; #x131 + (label loop-strip-a) + (vu-pair (ibne vi10 code-b reload-first-buffer) (nop)) ;; #x132 + (vu-pair (iaddiu code-b code-b 0x80) (nop)) ;; #x133 + (vu-pair (lqi.xyzw vf23 obj-ptr) (nop)) ;; #x134 + (vu-pair (b vertex-pipe-b-odd) (nop)) ;; #x135 + (vu-pair (lqi.xyzw vf24 obj-ptr) (maddz.xyzw vtx-a t-mtx2 vtx-a)) ;; #x136 + (label flush-strip-b) + (vu-pair (ibne vi10 code-a reload-second-buffer) (nop)) ;; #x137 + (vu-pair (iaddiu code-a code-a 0x80) (nop)) ;; #x138 + (vu-pair (lqi.xyzw vf23 obj-ptr) (nop)) ;; #x139 + (vu-pair (b vertex-pipe-b-even) (nop)) ;; #x13a + (vu-pair (lqi.xyzw vf24 obj-ptr) (maddz.xyzw vtx-b t-mtx2 vtx-b)) ;; #x13b + (label reload-strip-b) + (vu-pair (ibeq vi10 code-b compare-strip-edge-b) (nop)) ;; #x13c + (vu-pair (iaddiu code-b code-b 0x80) (nop)) ;; #x13d + (vu-pair (ilwr.w vtx-ptr model-bank) (maddz.xyzw vtx-a t-mtx2 vtx-a)) ;; #x13e + (vu-pair (mr32.xyzw gifbuf gifbuf) (mul.xyz pos-b vtx-b Q)) ;; #x13f + (vu-pair (lqi.xyzw t-mtx0 matrix-ptr) (nop)) ;; #x140 + (vu-pair (lqi.xyzw t-mtx1 matrix-ptr) (nop)) ;; #x141 + (vu-pair (lqi.xyzw t-mtx2 matrix-ptr) (nop)) ;; #x142 + (vu-pair (lqi.xyzw t-mtx3 matrix-ptr) (nop)) ;; #x143 + (vu-pair (iadd vtx-ptr vtx-ptr model-bank) (nop)) ;; #x144 + (vu-pair (iadd clr-ptr vtx-ptr stream-qwc) (nop)) ;; #x145 + (vu-pair (iadd stq-ptr clr-ptr stream-qwc) (nop)) ;; #x146 + (vu-pair (ibeq code-b area-sign store-obj-1-and-resume-d0) (nop)) ;; #x147 + (vu-pair (mtir code-b clr-b.w) (nop)) ;; #x148 + (vu-pair (b strip-stage-a2) (nop)) ;; #x149 + (vu-pair (sq.xyzw vf23 dest-b 0) (mul.xyzw stq-b-q stq-b Q)) ;; #x14a + (label strip-stage-a0) + (vu-pair (ibeq vi10 code-a compare-strip-edge-a) (nop)) ;; #x14b + (vu-pair (iaddiu code-a code-a 0x80) (nop)) ;; #x14c + (vu-pair (ilwr.w vtx-ptr model-bank) (maddz.xyzw vtx-b t-mtx2 vtx-b)) ;; #x14d + (vu-pair (mr32.xyzw gifbuf gifbuf) (mul.xyz pos-a vtx-a Q)) ;; #x14e + (vu-pair (lqi.xyzw t-mtx0 matrix-ptr) (nop)) ;; #x14f + (vu-pair (lqi.xyzw t-mtx1 matrix-ptr) (nop)) ;; #x150 + (vu-pair (lqi.xyzw t-mtx2 matrix-ptr) (nop)) ;; #x151 + (vu-pair (lqi.xyzw t-mtx3 matrix-ptr) (nop)) ;; #x152 + (vu-pair (iadd vtx-ptr vtx-ptr model-bank) (nop)) ;; #x153 + (vu-pair (iadd clr-ptr vtx-ptr stream-qwc) (nop)) ;; #x154 + (vu-pair (iadd stq-ptr clr-ptr stream-qwc) (nop)) ;; #x155 + (vu-pair (ibeq code-a area-sign store-obj-1-and-resume-e0) (nop)) ;; #x156 + (vu-pair (mtir code-a clr-a.w) (nop)) ;; #x157 + (vu-pair (b strip-stage-c0) (nop)) ;; #x158 + (vu-pair (sq.xyzw vf23 dest-a 0) (mul.xyzw stq-a-q stq-a Q)) ;; #x159 + (label strip-stage-a1) + (vu-pair (ilwr.w vtx-ptr model-bank) (maddz.xyzw vtx-a t-mtx2 vtx-a)) ;; #x15a + (vu-pair (mr32.xyzw gifbuf gifbuf) (mul.xyz pos-b vtx-b Q)) ;; #x15b + (vu-pair (lqi.xyzw t-mtx0 matrix-ptr) (nop)) ;; #x15c + (vu-pair (lqi.xyzw t-mtx1 matrix-ptr) (nop)) ;; #x15d + (vu-pair (lqi.xyzw t-mtx2 matrix-ptr) (nop)) ;; #x15e + (vu-pair (lqi.xyzw t-mtx3 matrix-ptr) (nop)) ;; #x15f + (vu-pair (iadd vtx-ptr vtx-ptr model-bank) (nop)) ;; #x160 + (vu-pair (iadd clr-ptr vtx-ptr stream-qwc) (nop)) ;; #x161 + (vu-pair (iadd stq-ptr clr-ptr stream-qwc) (nop)) ;; #x162 + (vu-pair (ibeq code-b area-sign store-obj-1-and-resume-d0) (nop)) ;; #x163 + (vu-pair (mtir code-b clr-b.w) (nop)) ;; #x164 + (vu-pair (sq.xyzw vf26 dest-b 0) (mul.xyzw stq-b-q stq-b Q)) ;; #x165 + (label strip-stage-a2) + (vu-pair (lqi.xyzw vtx-b vtx-ptr) (mul.xyz clr-b-lit clr-b inst-color)) ;; #x166 + (vu-pair (div Q vf00.w vtx-a.w) (nop)) ;; #x167 + (vu-pair (lqi.xyzw stq-b stq-ptr) (mulay.x ACC edge-0 edge-1)) ;; #x168 + (vu-pair (mtir dest-b slot-b.w) (msubay.x ACC edge-1 edge-0)) ;; #x169 + (vu-pair (lqi.xyzw clr-b clr-ptr) (sub.xyzw edge-0 pos-a pos-b)) ;; #x16a + (vu-pair (sq.xyzw stq-a-q dest-a 1) (ftoi4.xyz pos-a pos-a)) ;; #x16b + (vu-pair (sq.xyzw clr-a-lit dest-a 2) (add.xyzw slot-b vtx-b gifbuf)) ;; #x16c + (vu-pair (fsand area-sign 0x2) (mulaw.xyzw ACC t-mtx3 vf00)) ;; #x16d + (vu-pair (ilw.x obj-ptr vi00 (shrub-view-const tex-start-ptr)) (maddax.xyzw ACC t-mtx0 vtx-b)) ;; #x16e + (vu-pair (sq.xyzw pos-a dest-a 3) (madday.xyzw ACC t-mtx1 vtx-b)) ;; #x16f + (vu-pair (ibeq code-a area-sign store-obj-1-and-resume-d1) (maddz.xyzw vtx-b t-mtx2 vtx-b)) ;; #x170 + (vu-pair (mtir code-a clr-a.w) (mul.xyz pos-a vtx-a Q)) ;; #x171 + (vu-pair (sq.xyzw vf26 dest-a 0) (mul.xyzw stq-a-q stq-a Q)) ;; #x172 + (label strip-stage-a3) + (vu-pair (lqi.xyzw vtx-a vtx-ptr) (mul.xyz clr-a-lit clr-a inst-color)) ;; #x173 + (vu-pair (div Q vf00.w vtx-b.w) (nop)) ;; #x174 + (vu-pair (lqi.xyzw stq-a stq-ptr) (mulay.x ACC edge-0 edge-1)) ;; #x175 + (vu-pair (mtir dest-a slot-a.w) (msubay.x ACC edge-1 edge-0)) ;; #x176 + (vu-pair (lqi.xyzw clr-a clr-ptr) (sub.xyzw edge-1 pos-b pos-a)) ;; #x177 + (vu-pair (sq.xyzw stq-b-q dest-b 1) (ftoi4.xyz pos-b pos-b)) ;; #x178 + (vu-pair (sq.xyzw clr-b-lit dest-b 2) (add.xyzw slot-a vtx-a gifbuf)) ;; #x179 + (vu-pair (fsand area-sign 0x2) (mulaw.xyzw ACC t-mtx3 vf00)) ;; #x17a + (vu-pair (mtir vi10 kick-base.x) (maddax.xyzw ACC t-mtx0 vtx-a)) ;; #x17b + (vu-pair (sq.xyzw pos-b dest-b 3) (madday.xyzw ACC t-mtx1 vtx-a)) ;; #x17c + (vu-pair (lqi.xyzw inst-color matrix-ptr) (mulz.w pos-b t-mtx3 view-consts)) ;; #x17d + (vu-pair (ibeq code-b area-sign store-obj-1-and-resume-d2) (maddz.xyzw vtx-a t-mtx2 vtx-a)) ;; #x17e + (vu-pair (mtir code-b clr-b.w) (mul.xyz pos-b vtx-b Q)) ;; #x17f + (vu-pair (sq.xyzw vf26 dest-b 0) (mul.xyzw stq-b-q stq-b Q)) ;; #x180 + (label strip-stage-b0) + (vu-pair (lqi.xyzw vtx-b vtx-ptr) (mul.xyz clr-b-lit clr-b inst-color)) ;; #x181 + (vu-pair (div Q vf00.w vtx-a.w) (mulay.x ACC edge-0 edge-1)) ;; #x182 + (vu-pair (lqi.xyzw stq-b stq-ptr) (addw.w pos-b view-consts pos-b)) ;; #x183 + (vu-pair (mtir dest-b slot-b.w) (msubay.x ACC edge-1 edge-0)) ;; #x184 + (vu-pair (lqi.xyzw clr-b clr-ptr) (sub.xyzw edge-0 pos-a pos-b)) ;; #x185 + (vu-pair (sq.xyzw stq-a-q dest-a 1) (ftoi4.xyz pos-a pos-a)) ;; #x186 + (vu-pair (sq.xyzw clr-a-lit dest-a 2) (add.xyzw slot-b vtx-b gifbuf)) ;; #x187 + (vu-pair (fsand area-sign 0x2) (mulaw.xyzw ACC t-mtx3 vf00)) ;; #x188 + (vu-pair (iaddi inst-count inst-count -0x1) (maddax.xyzw ACC t-mtx0 vtx-b)) ;; #x189 + (vu-pair (sq.xyzw pos-a dest-a 3) (madday.xyzw ACC t-mtx1 vtx-b)) ;; #x18a + (vu-pair (mtir kick-addr gifbuf.x) (miniy.w pos-b pos-b fog-clamp)) ;; #x18b + (vu-pair (ibeq code-a area-sign store-obj-1-and-kick-second) (maddz.xyzw vtx-b t-mtx2 vtx-b)) ;; #x18c + (vu-pair (mtir code-a clr-a.w) (mul.xyz pos-a vtx-a Q)) ;; #x18d + (vu-pair (sq.xyzw vf26 dest-a 0) (mul.xyzw stq-a-q stq-a Q)) ;; #x18e + (label strip-stage-b1) + (vu-pair (move.w clr-a-lit inst-color) (addy.x kick-base vf00 kick-base)) ;; #x18f + (vu-pair (move.w clr-b-lit inst-color) (nop)) ;; #x190 + (vu-pair (iadd kick-addr kick-addr vi10) (nop)) ;; #x191 + (vu-pair (ibne vi00 inst-count vertex-pipe-a-odd) (ftoi4.w pos-a pos-b)) ;; #x192 + ;; Kick the first output-buffer/parity variant. The following three exits cover the other + ;; buffer and strip-parity combinations, sharing the branch tails at the end of the block. + (vu-pair (xgkick kick-addr) (ftoi4.w pos-b pos-b)) ;; #x193 + (vu-pair (nop) (nop :e)) ;; #x194 + (vu-pair (nop) (nop)) ;; #x195 + (label kick-first-buffer) + (vu-pair (ilwr.w vtx-ptr model-bank) (maddz.xyzw vtx-b t-mtx2 vtx-b)) ;; #x196 + (vu-pair (mr32.xyzw gifbuf gifbuf) (mul.xyz pos-a vtx-a Q)) ;; #x197 + (vu-pair (lqi.xyzw t-mtx0 matrix-ptr) (nop)) ;; #x198 + (vu-pair (lqi.xyzw t-mtx1 matrix-ptr) (nop)) ;; #x199 + (vu-pair (lqi.xyzw t-mtx2 matrix-ptr) (nop)) ;; #x19a + (vu-pair (lqi.xyzw t-mtx3 matrix-ptr) (nop)) ;; #x19b + (vu-pair (iadd vtx-ptr vtx-ptr model-bank) (nop)) ;; #x19c + (vu-pair (iadd clr-ptr vtx-ptr stream-qwc) (nop)) ;; #x19d + (vu-pair (iadd stq-ptr clr-ptr stream-qwc) (nop)) ;; #x19e + (vu-pair (ibeq code-a area-sign store-obj-1-and-resume-e0) (nop)) ;; #x19f + (vu-pair (mtir code-a clr-a.w) (nop)) ;; #x1a0 + (vu-pair (sq.xyzw vf26 dest-a 0) (mul.xyzw stq-a-q stq-a Q)) ;; #x1a1 + (label strip-stage-c0) + (vu-pair (lqi.xyzw vtx-a vtx-ptr) (mul.xyz clr-a-lit clr-a inst-color)) ;; #x1a2 + (vu-pair (div Q vf00.w vtx-b.w) (nop)) ;; #x1a3 + (vu-pair (lqi.xyzw stq-a stq-ptr) (mulay.x ACC edge-0 edge-1)) ;; #x1a4 + (vu-pair (mtir dest-a slot-a.w) (msubay.x ACC edge-1 edge-0)) ;; #x1a5 + (vu-pair (lqi.xyzw clr-a clr-ptr) (sub.xyzw edge-1 pos-b pos-a)) ;; #x1a6 + (vu-pair (sq.xyzw stq-b-q dest-b 1) (ftoi4.xyz pos-b pos-b)) ;; #x1a7 + (vu-pair (sq.xyzw clr-b-lit dest-b 2) (add.xyzw slot-a vtx-a gifbuf)) ;; #x1a8 + (vu-pair (fsand area-sign 0x2) (mulaw.xyzw ACC t-mtx3 vf00)) ;; #x1a9 + (vu-pair (ilw.x obj-ptr vi00 (shrub-view-const tex-start-ptr)) (maddax.xyzw ACC t-mtx0 vtx-a)) ;; #x1aa + (vu-pair (sq.xyzw pos-b dest-b 3) (madday.xyzw ACC t-mtx1 vtx-a)) ;; #x1ab + (vu-pair (ibeq code-b area-sign store-obj-1-and-resume-e1) (maddz.xyzw vtx-a t-mtx2 vtx-a)) ;; #x1ac + (vu-pair (mtir code-b clr-b.w) (mul.xyz pos-b vtx-b Q)) ;; #x1ad + (vu-pair (sq.xyzw vf26 dest-b 0) (mul.xyzw stq-b-q stq-b Q)) ;; #x1ae + (label strip-stage-c1) + (vu-pair (lqi.xyzw vtx-b vtx-ptr) (mul.xyz clr-b-lit clr-b inst-color)) ;; #x1af + (vu-pair (div Q vf00.w vtx-a.w) (nop)) ;; #x1b0 + (vu-pair (lqi.xyzw stq-b stq-ptr) (mulay.x ACC edge-0 edge-1)) ;; #x1b1 + (vu-pair (mtir dest-b slot-b.w) (msubay.x ACC edge-1 edge-0)) ;; #x1b2 + (vu-pair (lqi.xyzw clr-b clr-ptr) (sub.xyzw edge-0 pos-a pos-b)) ;; #x1b3 + (vu-pair (sq.xyzw stq-a-q dest-a 1) (ftoi4.xyz pos-a pos-a)) ;; #x1b4 + (vu-pair (sq.xyzw clr-a-lit dest-a 2) (add.xyzw slot-b vtx-b gifbuf)) ;; #x1b5 + (vu-pair (fsand area-sign 0x2) (mulaw.xyzw ACC t-mtx3 vf00)) ;; #x1b6 + (vu-pair (mtir vi10 kick-base.x) (maddax.xyzw ACC t-mtx0 vtx-b)) ;; #x1b7 + (vu-pair (sq.xyzw pos-a dest-a 3) (madday.xyzw ACC t-mtx1 vtx-b)) ;; #x1b8 + (vu-pair (lqi.xyzw inst-color matrix-ptr) (mulz.w pos-a t-mtx3 view-consts)) ;; #x1b9 + (vu-pair (ibeq code-a area-sign store-obj-1-and-resume-e2) (maddz.xyzw vtx-b t-mtx2 vtx-b)) ;; #x1ba + (vu-pair (mtir code-a clr-a.w) (mul.xyz pos-a vtx-a Q)) ;; #x1bb + (vu-pair (sq.xyzw vf26 dest-a 0) (mul.xyzw stq-a-q stq-a Q)) ;; #x1bc + (label strip-stage-c2) + (vu-pair (lqi.xyzw vtx-a vtx-ptr) (mul.xyz clr-a-lit clr-a inst-color)) ;; #x1bd + (vu-pair (div Q vf00.w vtx-b.w) (mulay.x ACC edge-0 edge-1)) ;; #x1be + (vu-pair (lqi.xyzw stq-a stq-ptr) (addw.w pos-a view-consts pos-a)) ;; #x1bf + (vu-pair (mtir dest-a slot-a.w) (msubay.x ACC edge-1 edge-0)) ;; #x1c0 + (vu-pair (lqi.xyzw clr-a clr-ptr) (sub.xyzw edge-1 pos-b pos-a)) ;; #x1c1 + (vu-pair (sq.xyzw stq-b-q dest-b 1) (ftoi4.xyz pos-b pos-b)) ;; #x1c2 + (vu-pair (sq.xyzw clr-b-lit dest-b 2) (add.xyzw slot-a vtx-a gifbuf)) ;; #x1c3 + (vu-pair (fsand area-sign 0x2) (mulaw.xyzw ACC t-mtx3 vf00)) ;; #x1c4 + (vu-pair (iaddi inst-count inst-count -0x1) (maddax.xyzw ACC t-mtx0 vtx-a)) ;; #x1c5 + (vu-pair (sq.xyzw pos-b dest-b 3) (madday.xyzw ACC t-mtx1 vtx-a)) ;; #x1c6 + (vu-pair (mtir kick-addr gifbuf.x) (miniy.w pos-a pos-a fog-clamp)) ;; #x1c7 + (vu-pair (ibeq code-b area-sign store-obj-1-and-kick-final) (maddz.xyzw vtx-a t-mtx2 vtx-a)) ;; #x1c8 + (vu-pair (mtir code-b clr-b.w) (mul.xyz pos-b vtx-b Q)) ;; #x1c9 + (vu-pair (sq.xyzw vf26 dest-b 0) (mul.xyzw stq-b-q stq-b Q)) ;; #x1ca + (label strip-stage-c3) + (vu-pair (move.w clr-a-lit inst-color) (addy.x kick-base vf00 kick-base)) ;; #x1cb + (vu-pair (move.w clr-b-lit inst-color) (nop)) ;; #x1cc + (vu-pair (iadd kick-addr kick-addr vi10) (nop)) ;; #x1cd + (vu-pair (ibne vi00 inst-count vertex-pipe-a-even) (ftoi4.w pos-b pos-a)) ;; #x1ce + (vu-pair (xgkick kick-addr) (ftoi4.w pos-a pos-a)) ;; #x1cf + (vu-pair (nop) (nop :e)) ;; #x1d0 + (vu-pair (nop) (nop)) ;; #x1d1 + (label reload-first-buffer) + (vu-pair (ilwr.w vtx-ptr model-bank) (maddz.xyzw vtx-a t-mtx2 vtx-a)) ;; #x1d2 + (vu-pair (mr32.xyzw gifbuf gifbuf) (mul.xyz pos-b vtx-b Q)) ;; #x1d3 + (vu-pair (lqi.xyzw t-mtx0 matrix-ptr) (nop)) ;; #x1d4 + (vu-pair (lqi.xyzw t-mtx1 matrix-ptr) (nop)) ;; #x1d5 + (vu-pair (lqi.xyzw t-mtx2 matrix-ptr) (nop)) ;; #x1d6 + (vu-pair (lqi.xyzw t-mtx3 matrix-ptr) (nop)) ;; #x1d7 + (vu-pair (iadd vtx-ptr vtx-ptr model-bank) (nop)) ;; #x1d8 + (vu-pair (iadd clr-ptr vtx-ptr stream-qwc) (nop)) ;; #x1d9 + (vu-pair (iadd stq-ptr clr-ptr stream-qwc) (nop)) ;; #x1da + (vu-pair (ibeq code-b area-sign store-obj-0-and-resume-a2) (nop)) ;; #x1db + (vu-pair (mtir code-b clr-b.w) (nop)) ;; #x1dc + (vu-pair (sq.xyzw vf27 dest-b 0) (mul.xyzw stq-b-q stq-b Q)) ;; #x1dd + (label strip-stage-d0) + (vu-pair (lqi.xyzw vtx-b vtx-ptr) (mul.xyz clr-b-lit clr-b inst-color)) ;; #x1de + (vu-pair (div Q vf00.w vtx-a.w) (nop)) ;; #x1df + (vu-pair (lqi.xyzw stq-b stq-ptr) (mulax.y ACC edge-0 edge-1)) ;; #x1e0 + (vu-pair (mtir dest-b slot-b.w) (msubax.y ACC edge-1 edge-0)) ;; #x1e1 + (vu-pair (lqi.xyzw clr-b clr-ptr) (sub.xyzw edge-0 pos-a pos-b)) ;; #x1e2 + (vu-pair (sq.xyzw stq-a-q dest-a 1) (ftoi4.xyz pos-a pos-a)) ;; #x1e3 + (vu-pair (sq.xyzw clr-a-lit dest-a 2) (add.xyzw slot-b vtx-b gifbuf)) ;; #x1e4 + (vu-pair (fsand area-sign 0x2) (mulaw.xyzw ACC t-mtx3 vf00)) ;; #x1e5 + (vu-pair (ilw.x obj-ptr vi00 (shrub-view-const tex-start-ptr)) (maddax.xyzw ACC t-mtx0 vtx-b)) ;; #x1e6 + (vu-pair (sq.xyzw pos-a dest-a 3) (madday.xyzw ACC t-mtx1 vtx-b)) ;; #x1e7 + (vu-pair (ibeq code-a area-sign store-obj-0-and-resume-a3) (maddz.xyzw vtx-b t-mtx2 vtx-b)) ;; #x1e8 + (vu-pair (mtir code-a clr-a.w) (mul.xyz pos-a vtx-a Q)) ;; #x1e9 + (vu-pair (sq.xyzw vf27 dest-a 0) (mul.xyzw stq-a-q stq-a Q)) ;; #x1ea + (label strip-stage-d1) + (vu-pair (lqi.xyzw vtx-a vtx-ptr) (mul.xyz clr-a-lit clr-a inst-color)) ;; #x1eb + (vu-pair (div Q vf00.w vtx-b.w) (nop)) ;; #x1ec + (vu-pair (lqi.xyzw stq-a stq-ptr) (mulax.y ACC edge-0 edge-1)) ;; #x1ed + (vu-pair (mtir dest-a slot-a.w) (msubax.y ACC edge-1 edge-0)) ;; #x1ee + (vu-pair (lqi.xyzw clr-a clr-ptr) (sub.xyzw edge-1 pos-b pos-a)) ;; #x1ef + (vu-pair (sq.xyzw stq-b-q dest-b 1) (ftoi4.xyz pos-b pos-b)) ;; #x1f0 + (vu-pair (sq.xyzw clr-b-lit dest-b 2) (add.xyzw slot-a vtx-a gifbuf)) ;; #x1f1 + (vu-pair (fsand area-sign 0x2) (mulaw.xyzw ACC t-mtx3 vf00)) ;; #x1f2 + (vu-pair (mtir vi10 kick-base.x) (maddax.xyzw ACC t-mtx0 vtx-a)) ;; #x1f3 + (vu-pair (sq.xyzw pos-b dest-b 3) (madday.xyzw ACC t-mtx1 vtx-a)) ;; #x1f4 + (vu-pair (lqi.xyzw inst-color matrix-ptr) (mulz.w pos-b t-mtx3 view-consts)) ;; #x1f5 + (vu-pair (ibeq code-b area-sign store-obj-0-and-resume-b0) (maddz.xyzw vtx-a t-mtx2 vtx-a)) ;; #x1f6 + (vu-pair (mtir code-b clr-b.w) (mul.xyz pos-b vtx-b Q)) ;; #x1f7 + (vu-pair (sq.xyzw vf27 dest-b 0) (mul.xyzw stq-b-q stq-b Q)) ;; #x1f8 + (label strip-stage-d2) + (vu-pair (lqi.xyzw vtx-b vtx-ptr) (mul.xyz clr-b-lit clr-b inst-color)) ;; #x1f9 + (vu-pair (div Q vf00.w vtx-a.w) (mulax.y ACC edge-0 edge-1)) ;; #x1fa + (vu-pair (lqi.xyzw stq-b stq-ptr) (addw.w pos-b view-consts pos-b)) ;; #x1fb + (vu-pair (mtir dest-b slot-b.w) (msubax.y ACC edge-1 edge-0)) ;; #x1fc + (vu-pair (lqi.xyzw clr-b clr-ptr) (sub.xyzw edge-0 pos-a pos-b)) ;; #x1fd + (vu-pair (sq.xyzw stq-a-q dest-a 1) (ftoi4.xyz pos-a pos-a)) ;; #x1fe + (vu-pair (sq.xyzw clr-a-lit dest-a 2) (add.xyzw slot-b vtx-b gifbuf)) ;; #x1ff + (vu-pair (fsand area-sign 0x2) (mulaw.xyzw ACC t-mtx3 vf00)) ;; #x200 + (vu-pair (iaddi inst-count inst-count -0x1) (maddax.xyzw ACC t-mtx0 vtx-b)) ;; #x201 + (vu-pair (sq.xyzw pos-a dest-a 3) (madday.xyzw ACC t-mtx1 vtx-b)) ;; #x202 + (vu-pair (mtir kick-addr gifbuf.x) (miniy.w pos-b pos-b fog-clamp)) ;; #x203 + (vu-pair (ibeq code-a area-sign store-obj-0-and-resume-b1) (maddz.xyzw vtx-b t-mtx2 vtx-b)) ;; #x204 + (vu-pair (mtir code-a clr-a.w) (mul.xyz pos-a vtx-a Q)) ;; #x205 + (vu-pair (sq.xyzw vf27 dest-a 0) (mul.xyzw stq-a-q stq-a Q)) ;; #x206 + (label kick-second-buffer) + (vu-pair (move.w clr-a-lit inst-color) (addy.x kick-base vf00 kick-base)) ;; #x207 + (vu-pair (move.w clr-b-lit inst-color) (nop)) ;; #x208 + (vu-pair (iadd kick-addr kick-addr vi10) (nop)) ;; #x209 + (vu-pair (ibne vi00 inst-count vertex-pipe-a-odd) (ftoi4.w pos-a pos-b)) ;; #x20a + (vu-pair (xgkick kick-addr) (ftoi4.w pos-b pos-b)) ;; #x20b + (vu-pair (nop) (nop :e)) ;; #x20c + (vu-pair (nop) (nop)) ;; #x20d + (label reload-second-buffer) + (vu-pair (ilwr.w vtx-ptr model-bank) (maddz.xyzw vtx-b t-mtx2 vtx-b)) ;; #x20e + (vu-pair (mr32.xyzw gifbuf gifbuf) (mul.xyz pos-a vtx-a Q)) ;; #x20f + (vu-pair (lqi.xyzw t-mtx0 matrix-ptr) (nop)) ;; #x210 + (vu-pair (lqi.xyzw t-mtx1 matrix-ptr) (nop)) ;; #x211 + (vu-pair (lqi.xyzw t-mtx2 matrix-ptr) (nop)) ;; #x212 + (vu-pair (lqi.xyzw t-mtx3 matrix-ptr) (nop)) ;; #x213 + (vu-pair (iadd vtx-ptr vtx-ptr model-bank) (nop)) ;; #x214 + (vu-pair (iadd clr-ptr vtx-ptr stream-qwc) (nop)) ;; #x215 + (vu-pair (iadd stq-ptr clr-ptr stream-qwc) (nop)) ;; #x216 + (vu-pair (ibeq code-a area-sign store-obj-0-and-resume-c0) (nop)) ;; #x217 + (vu-pair (mtir code-a clr-a.w) (nop)) ;; #x218 + (vu-pair (sq.xyzw vf27 dest-a 0) (mul.xyzw stq-a-q stq-a Q)) ;; #x219 + (label strip-stage-e0) + (vu-pair (lqi.xyzw vtx-a vtx-ptr) (mul.xyz clr-a-lit clr-a inst-color)) ;; #x21a + (vu-pair (div Q vf00.w vtx-b.w) (nop)) ;; #x21b + (vu-pair (lqi.xyzw stq-a stq-ptr) (mulax.y ACC edge-0 edge-1)) ;; #x21c + (vu-pair (mtir dest-a slot-a.w) (msubax.y ACC edge-1 edge-0)) ;; #x21d + (vu-pair (lqi.xyzw clr-a clr-ptr) (sub.xyzw edge-1 pos-b pos-a)) ;; #x21e + (vu-pair (sq.xyzw stq-b-q dest-b 1) (ftoi4.xyz pos-b pos-b)) ;; #x21f + (vu-pair (sq.xyzw clr-b-lit dest-b 2) (add.xyzw slot-a vtx-a gifbuf)) ;; #x220 + (vu-pair (fsand area-sign 0x2) (mulaw.xyzw ACC t-mtx3 vf00)) ;; #x221 + (vu-pair (ilw.x obj-ptr vi00 (shrub-view-const tex-start-ptr)) (maddax.xyzw ACC t-mtx0 vtx-a)) ;; #x222 + (vu-pair (sq.xyzw pos-b dest-b 3) (madday.xyzw ACC t-mtx1 vtx-a)) ;; #x223 + (vu-pair (ibeq code-b area-sign store-obj-0-and-resume-c1) (maddz.xyzw vtx-a t-mtx2 vtx-a)) ;; #x224 + (vu-pair (mtir code-b clr-b.w) (mul.xyz pos-b vtx-b Q)) ;; #x225 + (vu-pair (sq.xyzw vf27 dest-b 0) (mul.xyzw stq-b-q stq-b Q)) ;; #x226 + (label strip-stage-e1) + (vu-pair (lqi.xyzw vtx-b vtx-ptr) (mul.xyz clr-b-lit clr-b inst-color)) ;; #x227 + (vu-pair (div Q vf00.w vtx-a.w) (nop)) ;; #x228 + (vu-pair (lqi.xyzw stq-b stq-ptr) (mulax.y ACC edge-0 edge-1)) ;; #x229 + (vu-pair (mtir dest-b slot-b.w) (msubax.y ACC edge-1 edge-0)) ;; #x22a + (vu-pair (lqi.xyzw clr-b clr-ptr) (sub.xyzw edge-0 pos-a pos-b)) ;; #x22b + (vu-pair (sq.xyzw stq-a-q dest-a 1) (ftoi4.xyz pos-a pos-a)) ;; #x22c + (vu-pair (sq.xyzw clr-a-lit dest-a 2) (add.xyzw slot-b vtx-b gifbuf)) ;; #x22d + (vu-pair (fsand area-sign 0x2) (mulaw.xyzw ACC t-mtx3 vf00)) ;; #x22e + (vu-pair (mtir vi10 kick-base.x) (maddax.xyzw ACC t-mtx0 vtx-b)) ;; #x22f + (vu-pair (sq.xyzw pos-a dest-a 3) (madday.xyzw ACC t-mtx1 vtx-b)) ;; #x230 + (vu-pair (lqi.xyzw inst-color matrix-ptr) (mulz.w pos-a t-mtx3 view-consts)) ;; #x231 + (vu-pair (ibeq code-a area-sign store-obj-0-and-resume-c2) (maddz.xyzw vtx-b t-mtx2 vtx-b)) ;; #x232 + (vu-pair (mtir code-a clr-a.w) (mul.xyz pos-a vtx-a Q)) ;; #x233 + (vu-pair (sq.xyzw vf27 dest-a 0) (mul.xyzw stq-a-q stq-a Q)) ;; #x234 + (label strip-stage-e2) + (vu-pair (lqi.xyzw vtx-a vtx-ptr) (mul.xyz clr-a-lit clr-a inst-color)) ;; #x235 + (vu-pair (div Q vf00.w vtx-b.w) (mulax.y ACC edge-0 edge-1)) ;; #x236 + (vu-pair (lqi.xyzw stq-a stq-ptr) (addw.w pos-a view-consts pos-a)) ;; #x237 + (vu-pair (mtir dest-a slot-a.w) (msubax.y ACC edge-1 edge-0)) ;; #x238 + (vu-pair (lqi.xyzw clr-a clr-ptr) (sub.xyzw edge-1 pos-b pos-a)) ;; #x239 + (vu-pair (sq.xyzw stq-b-q dest-b 1) (ftoi4.xyz pos-b pos-b)) ;; #x23a + (vu-pair (sq.xyzw clr-b-lit dest-b 2) (add.xyzw slot-a vtx-a gifbuf)) ;; #x23b + (vu-pair (fsand area-sign 0x2) (mulaw.xyzw ACC t-mtx3 vf00)) ;; #x23c + (vu-pair (iaddi inst-count inst-count -0x1) (maddax.xyzw ACC t-mtx0 vtx-a)) ;; #x23d + (vu-pair (sq.xyzw pos-b dest-b 3) (madday.xyzw ACC t-mtx1 vtx-a)) ;; #x23e + (vu-pair (mtir kick-addr gifbuf.x) (miniy.w pos-a pos-a fog-clamp)) ;; #x23f + (vu-pair (ibeq code-b area-sign store-obj-0-and-resume-c3) (maddz.xyzw vtx-a t-mtx2 vtx-a)) ;; #x240 + (vu-pair (mtir code-b clr-b.w) (mul.xyz pos-b vtx-b Q)) ;; #x241 + (vu-pair (sq.xyzw vf27 dest-b 0) (mul.xyzw stq-b-q stq-b Q)) ;; #x242 + (label kick-final-buffer) + (vu-pair (move.w clr-a-lit inst-color) (addy.x kick-base vf00 kick-base)) ;; #x243 + (vu-pair (move.w clr-b-lit inst-color) (nop)) ;; #x244 + (vu-pair (iadd kick-addr kick-addr vi10) (nop)) ;; #x245 + (vu-pair (ibne vi00 inst-count vertex-pipe-a-even) (ftoi4.w pos-b pos-a)) ;; #x246 + (vu-pair (xgkick kick-addr) (ftoi4.w pos-a pos-a)) ;; #x247 + (vu-pair (nop) (nop :e)) ;; #x248 + (vu-pair (nop) (nop)) ;; #x249 + (label store-obj-1-and-resume-d0) + (vu-pair (b strip-stage-d0) (nop)) ;; #x24a + (vu-pair (sq.xyzw vf24 dest-b 0) (mul.xyzw stq-b-q stq-b Q)) ;; #x24b + (label store-obj-1-and-resume-d1) + (vu-pair (b strip-stage-d1) (nop)) ;; #x24c + (vu-pair (sq.xyzw vf24 dest-a 0) (mul.xyzw stq-a-q stq-a Q)) ;; #x24d + (label store-obj-1-and-resume-d2) + (vu-pair (b strip-stage-d2) (nop)) ;; #x24e + (vu-pair (sq.xyzw vf24 dest-b 0) (mul.xyzw stq-b-q stq-b Q)) ;; #x24f + (label store-obj-1-and-kick-second) + (vu-pair (b kick-second-buffer) (nop)) ;; #x250 + (vu-pair (sq.xyzw vf24 dest-a 0) (mul.xyzw stq-a-q stq-a Q)) ;; #x251 + (label store-obj-1-and-resume-e0) + (vu-pair (b strip-stage-e0) (nop)) ;; #x252 + (vu-pair (sq.xyzw vf24 dest-a 0) (mul.xyzw stq-a-q stq-a Q)) ;; #x253 + (label store-obj-1-and-resume-e1) + (vu-pair (b strip-stage-e1) (nop)) ;; #x254 + (vu-pair (sq.xyzw vf24 dest-b 0) (mul.xyzw stq-b-q stq-b Q)) ;; #x255 + (label store-obj-1-and-resume-e2) + (vu-pair (b strip-stage-e2) (nop)) ;; #x256 + (vu-pair (sq.xyzw vf24 dest-a 0) (mul.xyzw stq-a-q stq-a Q)) ;; #x257 + (label store-obj-1-and-kick-final) + (vu-pair (b kick-final-buffer) (nop)) ;; #x258 + (vu-pair (sq.xyzw vf24 dest-b 0) (mul.xyzw stq-b-q stq-b Q)) ;; #x259 + (label store-obj-0-and-resume-a2) + (vu-pair (b strip-stage-a2) (nop)) ;; #x25a + (vu-pair (sq.xyzw vf23 dest-b 0) (mul.xyzw stq-b-q stq-b Q)) ;; #x25b + (label store-obj-0-and-resume-a3) + (vu-pair (b strip-stage-a3) (nop)) ;; #x25c + (vu-pair (sq.xyzw vf23 dest-a 0) (mul.xyzw stq-a-q stq-a Q)) ;; #x25d + (label store-obj-0-and-resume-b0) + (vu-pair (b strip-stage-b0) (nop)) ;; #x25e + (vu-pair (sq.xyzw vf23 dest-b 0) (mul.xyzw stq-b-q stq-b Q)) ;; #x25f + (label store-obj-0-and-resume-b1) + (vu-pair (b strip-stage-b1) (nop)) ;; #x260 + (vu-pair (sq.xyzw vf23 dest-a 0) (mul.xyzw stq-a-q stq-a Q)) ;; #x261 + (label store-obj-0-and-resume-c0) + (vu-pair (b strip-stage-c0) (nop)) ;; #x262 + (vu-pair (sq.xyzw vf23 dest-a 0) (mul.xyzw stq-a-q stq-a Q)) ;; #x263 + (label store-obj-0-and-resume-c1) + (vu-pair (b strip-stage-c1) (nop)) ;; #x264 + (vu-pair (sq.xyzw vf23 dest-b 0) (mul.xyzw stq-b-q stq-b Q)) ;; #x265 + (label store-obj-0-and-resume-c2) + (vu-pair (b strip-stage-c2) (nop)) ;; #x266 + (vu-pair (sq.xyzw vf23 dest-a 0) (mul.xyzw stq-a-q stq-a Q)) ;; #x267 + (label store-obj-0-and-resume-c3) + (vu-pair (b strip-stage-c3) (nop)) ;; #x268 + (vu-pair (sq.xyzw vf23 dest-b 0) (mul.xyzw stq-b-q stq-b Q)) ;; #x269 + ))) + +(#unless PC_PORT + (defun draw-inline-array-instance-shrub ((dma-buf dma-buffer) (nodes drawable) (node-count int) (buckets (inline-array prototype-bucket-shrub))) + "Walk node-count shrub BVH roots, cull them, classify every surviving leaf instance by prototype + and camera distance, and append its record to that prototype's near, opaque, translucent or + billboard chain. Instance records arrive through the scratchpad and the output packets are + assembled there too, so both transfers and the EE run at once. VU0 does the sphere-versus-plane + test, the camera depth, the instance transform and the wind spring." + (declare (asm-func none)) + ;; The function never reads an instance or writes a packet in main memory if it can avoid it. + ;; Instances come in through two 5200-byte scratchpad banks, one being filled by the toSPR + ;; channel while the EE reads the other; finished packets go out of two 2048-byte banks, each + ;; handed to the fromSPR channel as soon as it is within one record of full. The node walk drives + ;; the input side: every time it collects a batch of leaf spans it writes a small DMA tag chain + ;; describing them and starts a chain-mode transfer, so the instances for the next batch are on + ;; their way while the current batch is being classified. + ;; + ;; Field accesses go through typed base registers, so instance-shrub-work, the prototype bucket, + ;; the leaf record, the wind state and the two DMA channel register banks all read as fields. + ;; Numeric displacements are left only where the address really is untyped: the packet layouts + ;; being assembled inside an output bank. + ;; + ;; VU0 state, established by background-upload-vu0 and by the entries called below: + ;; vf1 1.0 in every lane + ;; vf2 the point handed to VU0 -- a node or instance bounding sphere + ;; vf4/vf6/vf7 entry 17 and 33 results: plane distances and camera depth + ;; vf16..vf19 the background camera matrix + ;; vf20..vf31 the shrub work area's planes, guard planes, hvdf offset and perspective rows + ;; vi1 the reject flags VU0 leaves behind + (rlet ((work :reg t0 :type instance-shrub-work) + (memory-cursor :reg v1) + (output-bank :reg t1) + (packet-cursor :reg t3) + (packet-qwc :reg a3) + (wind-work :reg t2 :type wind-work) + (hashed-wind :reg t7 :type wind-work) + (from-spr-channel :reg a0 :type dma-bank-spr) + (to-spr-channel :reg t4 :type dma-bank-spr) + (node-stack :reg t4 :type instance-shrub-work) + (bucket :reg t4 :type prototype-bucket-shrub) + (near-geometry :reg t4 :type drawable-group) + (cached-bucket :reg a1) + (prev-bucket :reg a1 :type prototype-bucket-shrub) + (instance :reg a2) + (chain-cursor :reg a2) + (wind-state :reg a2 :type wind-vector) + (instance-bucket :reg a2 :type prototype-bucket-shrub) + (node :reg t8 :type draw-node) + (fragment :reg t8 :type generic-shrub-fragment) + (near-packet :reg t9) + (scratch-base :reg t3) + (instance-bank :reg t6) + (dma-buf :reg a0 :type dma-buffer) + (nodes :reg a1) + (node-count :reg a2) + (buckets :reg a3)) + ;; Almost nothing here is arithmetic; it is the addresses the rest of the function runs on. + ;; + ;; Both scratchpad DMA channel register banks share the upper half of their address with the + ;; scratchpad bank bases, so two lui instructions cover four addresses. t5 first carries the + ;; toSPR channel address and is then reused for one of the node chain buffers; t7, t9, gp and + ;; ra are short-lived temporaries throughout the function and keep their physical spellings. + ;; + ;; The two node chain buffers are the surprising part. They are 128-byte staging areas for the + ;; DMA tags that fetch instance records, and they must be cache-line aligned because the EE + ;; writes them through the cache and then hands them to the DMAC -- hence the AND with -64. The + ;; three dummy quadwords in front of chaina exist to give that rounding somewhere to land. + (asm-block setup-instance-walk + (label initialize-instance-walk) + (add.i sp sp -32) + (s.d ra sp) + (s.q gp sp 16) + (lui scratch-base #x7000) + (l.w memory-cursor (-> dma-buf base)) + (lui t2 #x1000) + (lui output-bank #x1000) + ;; Write back and invalidate the two cache ways covering the DMA cursor before the + ;; fromSPR channel starts writing behind the EE's back. + (sync.l) + (cache dxwbin memory-cursor 0) + (sync.l) + (cache dxwbin memory-cursor 1) + (sync.l) + (m! work *instance-shrub-work*) + (ori t5 t2 #xd400) + (s.w dma-buf (-> work dma-buffer)) + (ori from-spr-channel output-bank #xd000) + (m! wind-work *wind-work*) + (ori output-bank scratch-base (spr-offset instance-shrub-dma outa)) + (s.w r0 (-> work chains)) + (m node-stack work) + (l.vf vf3 (-> work constants)) + (s.w t5 (-> work to-spr)) + (ori instance-bank scratch-base (spr-offset instance-shrub-dma instancea)) + ;; The near-packet cursor starts at the last of the six and counts down, so the + ;; first near shrub of the frame uses a packet no earlier chain can still name. + (add.i t7 r0 (* 5 (type-size shrub-near-packet))) + (s.w buckets (-> work prototypes)) + (add.i packet-cursor r0 0) + (s.w buckets (-> work bucket-ptr)) + (add.i packet-qwc r0 0) + (s.w nodes (-> node-stack node 0)) + (m packet-cursor output-bank) + (s.w node-count (-> node-stack length 0)) + (add.i cached-bucket r0 -1) + (s.w t7 (-> work current-shrub-near-packet)) + (add.i t7 work (offset-of instance-shrub-work chaina)) + (s.w instance-bank (-> work src-ptr)) + (add.i a2 work (offset-of instance-shrub-work chainb)) + (s.w instance-bank (-> work instance-ptr)) + (add.i t6 r0 -64) + (s.w t5 (-> work to-spr)) + (and t5 t7 t6) + (s.w from-spr-channel (-> work from-spr)) + (and a2 a2 t6) + (s.w t5 (-> work chain-ptr)) + (add.i t5 r0 -1) + (s.w a2 (-> work chain-ptr-next)) + (nop!) + (s.w t4 (-> work stack-ptr)) + (nop!) + (s.w t5 (-> work last-shrubs)) + (nop!) + (s.w r0 (-> work flags)) + (nop!) + (s.w r0 (-> work inst-count)) + (nop!) + (s.w r0 (-> work node-count))) + ;; Walk the BVH with an explicit stack, because a shrub tree has no fixed depth and no fixed + ;; child count. work.node and work.length are six-entry arrays a constant distance apart, so + ;; one cursor addresses both and its distance from the work area is the current depth. Seven + ;; levels is the limit, which is what t5 counts down. + ;; + ;; VU0 entry 17 turns a node's bounding sphere into four side-plane distances and a camera + ;; depth. Any negative plane distance rejects the whole subtree, and so does a negative depth. + ;; Otherwise a node either descends -- push its child span -- or, if its children are leaves, + ;; contributes one ref tag to the chain being built, with the leaf count stashed in the tag's + ;; VIF word where the scratchpad transfer will never look at it. + (asm-block walk-node-stack + (label scan-node-stack) + (callms 17) + (l.w t4 (-> work stack-ptr)) + (add.i t5 r0 7) + (l.w a2 (-> work chain-ptr)) + (nop!) + (nop!) + (nop!) + (nop!) + (nop!) + (nop!) + (nop!) + (nop!) + (nop!) + (nop!) + (label test-node) + (sub t7 t4 work) + (l.w t6 (-> node-stack length 0)) + (b.lt t7 r0 finish-node-stack-pass :delay (l.w node (-> node-stack node 0))) + (b.z t6 skip-empty-node :delay (l.vf vf2 (-> node bsphere))) + (nop!) + (l.vf vf6 node -4) + (vmulax.xyzw acc vf16 vf2) + (l.bu t6 (-> node flags)) + (vmadday.xyzw acc vf17 vf2) + (l.w t7 (-> node child)) + (vmaddaz.xyzw acc vf18 vf2) + (l.bu node (-> node child-count)) + (vmsubaw.xyzw acc vf19 vf0) + (l.q t9 (-> work dma-ref)) + (vmaddw.xyzw vf4 vf1 vf2) + (s.w t7 (-> node-stack node 1)) + (vmulaw.xyzw acc vf1 vf6) + (s.w node (-> node-stack length 1)) + (vmsubax.xyzw acc vf24 vf2) + (s.q t9 chain-cursor) + (vmsubay.xyzw acc vf25 vf2) + (add.i t9 t7 -4) + (vmsubaz.xyzw acc vf26 vf2) + (sll t7 t8 2) + (m ra vf4) + (add t7 t7 t8) + (vmsubaw.xyzw acc vf27 vf0) + (s.w t9 chain-cursor 4) + (vmaddw.xyzw vf7 vf1 vf2) + (s.w node chain-cursor (offset-of dma-packet vif0)) + (pcgtw t8 r0 ra) + (l.w t9 (-> node-stack length 0)) + (ppach ra r0 t8) + (l.w node (-> node-stack node 0)) + (b.nz ra reject-or-finish-node :delay (s.b t7 chain-cursor)) + (nop!) + (nop!) + (add.i t7 t9 -1) + (m t9 vf7) + (add.i t8 t8 32) + (nop!) + (b.lt t9 r0 reject-or-finish-node :delay (nop!)) + (b.z t6 descend-visible-node :delay (nop!)) + (b test-node :delay (add.i t4 t4 4)) + (label descend-visible-node) + (add.i a2 a2 16) + (s.w t7 (-> node-stack length 0)) + (add.i t5 t5 -1) + (s.w node (-> node-stack node 0)) + (b.le t5 r0 finish-node-stack-pass :delay (sub t6 t4 work)) + (b.gt t7 r0 test-node :delay (nop!)) + (b.le t6 r0 finish-node-stack-pass :delay (add.i t4 t4 -4)) + (label reject-or-finish-node) + (nop!) + (l.w t7 (-> node-stack length 0)) + (nop!) + (l.w t6 (-> node-stack node 0)) + (add.i t7 t7 -1) + (sub t8 t4 work) + (add.i t6 t6 32) + (s.w t7 (-> node-stack length 0)) + (b.gt t7 r0 test-node :delay (s.w t6 (-> node-stack node 0))) + (b.le t8 r0 finish-node-stack-pass :delay (nop!)) + (label skip-empty-node) + (b reject-or-finish-node :delay (add.i t4 t4 -4))) + ;; Hand the finished node chain to the toSPR channel and start filling the other one. The chain + ;; is a run of ref tags plus a terminating end tag, sent in chain mode with tag transfer + ;; enabled: each tag is copied into the scratchpad ahead of the instances it fetches, which is + ;; how the leaf count travels with the data and why the instance cursor starts one quadword into + ;; the bank. The two cache lines holding the chain are written back first, because the EE built + ;; it through the cache and the DMAC will read main memory. + (asm-block flush-node-chain + (label finish-node-stack-pass) + (nop!) + (s.w t4 (-> work stack-ptr)) + (nop!) + (l.w t5 (-> work chain-ptr)) + (nop!) + (l.w t4 (-> work to-spr)) + (b.eq t5 a2 finish-node-walk :delay (l.q t5 (-> work dma-end))) + (dma-wait-counting-stalls! t6 to-spr-channel (-> work wait-to-spr) + wait-to-scratchpad swap-node-chains-and-start-dma) + (label swap-node-chains-and-start-dma) + (nop!) + (l.w t6 (-> work chains)) + (nop!) + (s.q t5 chain-cursor) + (l.w t5 (-> work chain-ptr)) + (add.i a2 r0 DMA-CHCR-CHAIN-TTE-START) + (l.w t7 (-> work chain-ptr-next)) + (ori t8 r0 #xffff) + (s.w t5 (-> work chain-ptr-next)) + (add.i t6 t6 1) + (s.w t7 (-> work chain-ptr)) + (m t7 t5) + (nop!) + (s.w t6 (-> work chains)) + (nop!) + (l.w t6 (-> work instance-ptr)) + (sync.l) + (cache dxwbin t7 0) + (sync.l) + (cache dxwbin t7 1) + (sync.l) + (add.i t7 t7 64) + (sync.l) + (cache dxwbin t7 0) + (sync.l) + (cache dxwbin t7 1) + (sync.l) + (s.w t6 (-> to-spr-channel sadr)) + (s.w t5 (-> to-spr-channel tadr)) + (xor.i t5 t6 SHRUB-INSTANCE-INPUT-FLIP) + (s.w r0 (-> to-spr-channel qwc)) + (sync.l) + (s.w a2 (-> to-spr-channel chcr)) + (sync.l) + (nop!) + (s.w t5 (-> work src-ptr)) + (b choose-next-pass :delay (s.w t5 (-> work instance-ptr))) + (label finish-node-walk) + (b.lt cached-bucket r0 done :delay (l.w a2 (-> work instance-ptr))) + (nop!) + (s.w r0 (-> work last-shrubs)) + (nop!) + (xor.i a2 a2 SHRUB-INSTANCE-INPUT-FLIP) + (nop!) + (s.w a2 (-> work src-ptr)) + (nop!) + (s.w a2 (-> work instance-ptr)) + (dma-wait-counting-stalls! a2 to-spr-channel (-> work wait-to-spr) + wait-for-final-node-dma choose-next-pass) + (label choose-next-pass) + (b.ge cached-bucket r0 finish-instance-pass :delay (l.w a2 (-> work src-ptr))) + (b scan-node-stack :delay (add.i cached-bucket r0 #x2710))) + ;; One leaf. Return the output bank first if this instance could overflow it, then let VU0 entry + ;; 33 test the instance's own bounding sphere and produce its camera depth. Consecutive + ;; instances usually share a prototype, so the bucket's four list heads live in the work area + ;; and are written back only when the bucket index changes -- cached-bucket holds the index they + ;; belong to, with 10000 meaning "nothing cached yet" and -1, before the first pass, "no + ;; instance has been looked at at all". + (asm-block load-next-instance + (label load-instance) + (add.i t4 packet-qwc (- SHRUB-OUTPUT-FLUSH-AT)) + (l.vf vf2 instance (offset-of instance-shrubbery bsphere)) + (b.le t4 r0 transform-instance :delay (l.bu t4 instance (offset-of instance-shrubbery bucket-index))) + (nop!) + (l.w from-spr-channel (-> work from-spr)) + (nop!) + (nop!) + (dma-wait-counting-stalls! packet-cursor from-spr-channel (-> work wait-from-spr) + wait-for-instance-upload upload-instance-bank) + (dma-return-output-bank! output-bank SHRUB-OUTPUT-FLIP packet-cursor packet-qwc memory-cursor from-spr-channel + upload-instance-bank) + (label transform-instance) + (callms 33) + (l.w t5 (-> work flags)) + (b.eq cached-bucket t4 classify-instance :delay (add.i t6 cached-bucket -10000)) + (b.z t6 prepare-instance-record :delay (l.w prev-bucket (-> work bucket-ptr))) + (nop!) + (l.q t5 (-> work last 0)) + (nop!) + (l.q t6 (-> work next 0)) + (nop!) + (l.q t7 (-> work count 0)) + (nop!) + (s.q t5 (-> prev-bucket last-clear)) + (nop!) + (s.q t6 (-> prev-bucket next-clear)) + (nop!) + (s.q t7 (-> prev-bucket count-clear-qword)) + (label prepare-instance-record) + (m cached-bucket t4) + (l.w t5 (-> work prototypes)) + (add.i t6 r0 112) + (s.q r0 (-> work last 0)) + (multu3 t4 t4 t6) + (s.q r0 (-> work next 0)) + (add t4 t5 t4) + (s.q r0 (-> work count 0)) + (nop!) + (s.w t4 (-> work bucket-ptr)) + (nop!) + (l.w t5 (-> bucket flags)) + (nop!) + (l.vf vf15 (-> bucket rdists)) + (and.i t5 t5 1) + (l.vf vf14 (-> bucket dists)) + (vmul.xyz vf15 vf15 vf3) + (s.w t5 (-> work flags))) + ;; Unpack the instance and decide what it becomes. + ;; + ;; The 4x4 origin is stored as sixteen 16-bit values; pextlh spreads each row into a quadword + ;; and the VU0 conversions turn the rotation rows into 20.12 floats and the translation into + ;; whole units. The color index in the leaf selects one RGBA from the tree's 1024-entry + ;; time-of-day table, and the byte spread and shift turn it into the four floats that scale + ;; every vertex of this instance. + ;; + ;; The distance work produces one number that decides everything: a fade running 0 to 128 + ;; across the band between this geometry and the next coarser one. 128 means the model has + ;; faded out completely, so the instance becomes a billboard and the wind path runs instead; + ;; below that the model is drawn with the fade in its alpha and 128 minus the fade is handed to + ;; the billboard, so the two crossfade. A prototype with no billboard geometry takes the flat + ;; path and is simply drawn. + (asm-block classify-instance + (label classify-instance) + (b.nz t5 advance-instance :delay (l.d t5 instance (offset-of instance-shrubbery origin data 12))) + (nop!) + (l.d t4 instance (offset-of instance-shrubbery origin data 0)) + (pextlh t5 t5 r0) + (l.d t6 instance (offset-of instance-shrubbery origin data 4)) + (sra.w t7 t5 10) + (l.d t5 instance (offset-of instance-shrubbery origin data 8)) + (pextlh t8 t4 r0) + (l.hu t4 instance (offset-of instance-shrubbery color-indices)) + (sra.w t8 t8 16) + (l.q t9 instance (offset-of instance-shrubbery flat-normal)) + (pextlh t6 t6 r0) + (m.ni vf13 t7) + (sra.w t6 t6 16) + (m.ni vf18 t9) + (pextlh t5 t5 r0) + (m.ni vf10 t8) + (sra.w t5 t5 16) + (m.ni vf11 t6) + (add t4 t4 work) + (m.ni vf12 t5) + (nop!) + (cfc2.i t5 vi1) + (vitof0.xyzw vf13 vf13) + (l.w t6 t4 (offset-of instance-shrub-work colors)) + (b.nz t5 advance-instance :delay (l.q t4 (-> work color-constant))) + (pextlb t5 r0 t6) + (l.vf vf4 (-> work hmge-d)) + (pextlh t5 r0 t5) + (l.vf vf25 (-> work min-dist)) + (vsub.xyzw vf9 vf6 vf14) + (nop!) + (sll.w t6 t5 8) + (mmi-nop!) + (add.w t4 t6 t4) + (mmi-nop!) + (vmula.xyzw acc vf1 vf3) + (nop!) + (vmsub.xyzw vf9 vf9 vf15) + (s.q t5 (-> work bb-color)) + (vadd.xyz vf13 vf13 vf2) + (s.q t4 (-> work color)) + (vsubw.xyzw vf8 vf6 vf2) + (nop!) + (vitof12.xyzw vf10 vf10) + (nop!) + (vmini.xyzw vf9 vf9 vf3) + (l.w t4 (-> work bucket-ptr)) + (vadd.xyz vf18 vf18 vf13) + (nop!) + (vmulax.xyzw acc vf28 vf13) + (l.w t4 (-> bucket geometry 3)) + (vmadday.xyzw acc vf29 vf13) + (nop!) + (vmaxx.xyzw vf9 vf9 vf0) + (nop!) + (vmaddaz.xyzw acc vf30 vf13) + (nop!) + (vmaddw.xyzw vf5 vf31 vf0) + (nop!) + (vitof12.xyzw vf11 vf11) + (nop!) + (ftoi.vf vf19 vf9) + (nop!) + (vmini.xyzw vf25 vf8 vf25) + (nop!) + (vsubz.xyzw vf4 vf8 vf4) + (add.i t5 r0 128) + (vitof12.xyzw vf12 vf12) + (add.i t6 r0 255) + (vmulw.y vf9 vf9 vf15) + (nop!) + (nop!) + (m t7 vf19) + (vdiv Q vf3.w vf5.w) + (nop!) + (and t6 t7 t6) + (nop!) + (sub t7 t5 t6) + (s.w t6 (-> work color w)) + (b.eq t5 t6 prepare-wind-and-billboard :delay (s.vf vf25 (-> work min-dist))) + (b.z t4 build-flat-instance :delay (s.w t7 (-> work bb-color w))) + (vmulax.xyzw acc vf28 vf18) + (l.q t4 (-> work billboard-tmpl)) + (vmadday.xyzw acc vf29 vf18) + (l.q t5 (-> work billboard-tmpl gif)) + (vmaddaz.xyzw acc vf30 vf18) + (l.w t6 (-> work last 3)) + (vmaddw.xyzw vf18 vf31 vf0) + (l.w t7 (-> work next 3)) + (sll t8 packet-qwc 4) + (l.vf vf8 (-> work hvdf-offset)) + (add t8 t8 memory-cursor) + (l.vf vf7 instance (offset-of instance-shrubbery flat-normal)) + (vmulaq.xyz acc vf5 Q) + (l.q a2 (-> work bb-color)) + (vmulaw.w acc vf5 vf0) + (movz t6 t8 t6) + (vmadd.xyzw vf5 vf1 vf8) + (l.hu t9 (-> work count 3)) + (vmulq.w vf19 vf7 Q) + (nop!) + (add.i t9 t9 1) + (l.vf vf6 (-> work billboard-const)) + (vmulq.xyzw vf26 vf1 Q) + (s.w t6 (-> work last 3)) + (vmulq.xyzw vf27 vf1 Q) + (s.w t8 (-> work next 3)) + (vnop) + (nop!) + (vmaxz.w vf5 vf5 vf6) + (s.h t9 (-> work count 3)) + (vdiv Q vf3.w vf18.w) + (nop!) + (vmulax.xyzw acc vf20 vf10) + (s.q t4 packet-cursor) + (vaddx.x vf26 vf0 vf0) + (s.q t5 packet-cursor 16) + (vminiw.w vf5 vf5 vf6) + (s.q a2 packet-cursor 48) + (vmadday.xyzw acc vf21 vf10) + (s.q a2 packet-cursor 96) + (vmaddz.xyzw vf10 vf22 vf10) + (s.q a2 packet-cursor 144) + (vmulaw.w acc vf18 vf0) + (s.q a2 packet-cursor 192) + (vmulaq.xyz acc vf18 Q) + (s.w t7 packet-cursor 4) + (vmadd.xyzw vf18 vf1 vf8) + (nop!) + (vmulq.w vf8 vf7 Q) + (nop!) + (vmulq.xyzw vf24 vf1 Q) + (nop!) + (vmulq.xyzw vf25 vf1 Q) + (nop!) + (vmaxz.w vf18 vf18 vf6) + (nop!) + (vadd.xy vf24 vf0 vf0) + (nop!) + (vaddy.y vf25 vf0 vf0) + (nop!) + (vmulax.xyzw acc vf20 vf11) + (nop!) + (vminiw.w vf18 vf18 vf6) + (nop!) + (vmadday.xyzw acc vf21 vf11) + (nop!) + (vmaddz.xyzw vf11 vf22 vf11) + (nop!) + (vmulax.xyzw acc vf20 vf12) + (nop!) + (vsub.xyzw vf16 vf18 vf5) + (nop!) + (vmadday.xyzw acc vf21 vf12) + (nop!) + (vmaddz.xyzw vf12 vf22 vf12) + (nop!) + (vmulax.xyzw acc vf20 vf13) + (nop!) + (vaddy.y vf16 vf16 vf16) + (nop!) + (vmadday.xyzw acc vf21 vf13) + (nop!) + (vmaddaz.xyzw acc vf22 vf13) + (nop!) + (vmaddw.xyzw vf13 vf23 vf0) + (nop!) + (vmul.xy vf17 vf16 vf16) + (nop!) + (nop!) + (s.vf vf24 packet-cursor 32) + (nop!) + (s.vf vf25 packet-cursor 80) + (nop!) + (s.vf vf26 packet-cursor 128) + (vaddy.x vf17 vf17 vf17) + (nop!) + (nop!) + (s.vf vf27 packet-cursor 176) + (vmulw.xyzw vf2 vf18 vf0) + (nop!) + (vmulw.xyzw vf4 vf18 vf0) + (nop!) + (vrsqrt Q vf0.w vf17.x) + (nop!) + (nop!) + (vwaitq) + (vmulq.xy vf17 vf16 Q) + (nop!) + (nop!) + (nop!) + (nop!) + (nop!) + (nop!) + (nop!) + (vsuby.x vf16 vf0 vf17) + (nop!) + (vaddx.y vf16 vf0 vf17) + (nop!) + (nop!) + (s.vf vf10 packet-cursor 240) + (nop!) + (s.vf vf11 packet-cursor 256) + (vmulw.xy vf8 vf16 vf8) + (nop!) + (vmulw.xy vf19 vf16 vf19) + (nop!) + (nop!) + (l.q a2 (-> work color)) + (nop!) + (nop!) + (vmul.xy vf8 vf8 vf6) + (nop!) + (vmul.xy vf19 vf19 vf6) + (nop!) + (vmulw.xyzw vf6 vf5 vf0) + (nop!) + (vmulw.xyzw vf7 vf5 vf0) + (s.q a2 packet-cursor 304) + (vadd.xy vf2 vf18 vf8) + (nop!) + (vsub.xy vf4 vf18 vf8) + (nop!) + (vadd.xy vf6 vf5 vf19) + (nop!) + (vsub.xy vf7 vf5 vf19) + (nop!) + (vftoi4.xyzw vf2 vf2) + (nop!) + (vftoi4.xyzw vf4 vf4) + (add.i packet-cursor packet-cursor 224) + (vftoi4.xyzw vf6 vf6) + (add.i packet-qwc packet-qwc 14) + (vftoi4.xyzw vf7 vf7) + (l.w a2 (-> work color w)) + (nop!) + (s.vf vf2 packet-cursor -160) + (nop!) + (s.vf vf4 packet-cursor -112) + (nop!) + (s.vf vf6 packet-cursor -64) + (b.z a2 advance-instance :delay (s.vf vf7 packet-cursor -16)) + (b append-first-lod-record :delay (nop!)) + (label build-flat-instance) + (b.z t6 advance-instance :delay (vmulax.xyzw acc vf20 vf10)) + (vmadday.xyzw acc vf21 vf10) + (l.q a2 (-> work color)) + (vmaddz.xyzw vf10 vf22 vf10) + (nop!) + (vmulax.xyzw acc vf20 vf11) + (nop!) + (vmadday.xyzw acc vf21 vf11) + (nop!) + (vmaddz.xyzw vf11 vf22 vf11) + (nop!) + (vmulax.xyzw acc vf20 vf12) + (nop!) + (vmadday.xyzw acc vf21 vf12) + (nop!) + (vmaddz.xyzw vf12 vf22 vf12) + (nop!) + (vmulax.xyzw acc vf20 vf13) + (nop!) + (vmadday.xyzw acc vf21 vf13) + (nop!) + (vmaddaz.xyzw acc vf22 vf13) + (nop!) + (vmaddw.xyzw vf13 vf23 vf0) + (s.q a2 packet-cursor 80) + (nop!) + (s.vf vf10 packet-cursor 16) + (nop!) + (s.vf vf11 packet-cursor 32) + (label append-first-lod-record) + (sll a2 packet-qwc 4) + (l.hu t4 (-> work mod-count 2)) + (add t5 a2 memory-cursor) + (l.hu t7 (-> work count 2)) + (sll t6 t4 4) + (l.w a2 (-> work next 2)) + (add t8 t6 work) + (l.w t6 (-> work last 2)) + (add.i t7 t7 1) + (l.q t8 t8 (offset-of instance-shrub-work matrix-tmpl)) + (add.i packet-qwc packet-qwc 6) + (s.h t7 (-> work count 2)) + (add.i t7 t4 1) + (s.q t8 packet-cursor) + (add.i t8 t7 -20) + (s.vf vf12 packet-cursor 48) + (movz t7 r0 t8) + (s.vf vf13 packet-cursor 64) + (add.i t8 t4 -10) + (s.h t7 (-> work mod-count 2)) + (add.i packet-cursor packet-cursor 96) + (s.w a2 packet-cursor -92) + (b.z t4 use-first-shared-matrix :delay (s.w t5 (-> work next 2))) + (b.nz t8 select-first-matrix :delay (nop!)) + (label use-first-shared-matrix) + (nop!) + (l.q t4 (-> work mscalf-tmpl)) + (nop!) + (l.q t7 (-> work mscalf-ret-tmpl)) + (nop!) + (s.w t5 (-> work last 2)) + (nop!) + (movz t4 t7 t6) + (add.i packet-qwc packet-qwc 1) + (s.q t4 packet-cursor) + (nop!) + (s.w a2 packet-cursor 4) + (b advance-instance :delay (add.i packet-cursor packet-cursor 16)) + (label select-first-matrix) + (add.i t5 t4 -9) + (nop!) + (b.z t5 use-first-indexed-matrix :delay (add.i t4 t4 -19)) + (b.nz t4 advance-instance :delay (nop!)) + (label use-first-indexed-matrix) + (nop!) + (sll t4 t7 4) + (nop!) + (add t4 t4 work) + (add.i packet-qwc packet-qwc 1) + (l.q t4 t4 (offset-of instance-shrub-work count-tmpl)) + (nop!) + (nop!) + (nop!) + (s.q t4 packet-cursor) + (nop!) + (s.w a2 packet-cursor 4) + (b advance-instance :delay (add.i packet-cursor packet-cursor 16))) + ;; Shear the instance into the wind, and if it has faded to a billboard, build the quad. + ;; + ;; Each instance owns two words of spring state in the level's wind-vectors array, indexed by + ;; the wind-index packed into its transform. The driving force is one of 64 shared wind vectors + ;; chosen by hashing that index against the current wind tick, exactly as + ;; wind-get-hashed-index does, so neighbouring bushes sway out of phase without anyone storing + ;; a phase. The spring is integrated here and written straight back, and its result shears the + ;; instance's rows. + ;; + ;; The billboard is a camera-facing rectangle built from the leaf's own flat-normal and + ;; flat-hwidth: the half-width along the screen right vector, the normal for up, four corners + ;; converted to 12.4 and written as a GS packet. The paused flag selects an alternate axis. + (asm-block shear-instance-into-wind + (label prepare-wind-and-billboard) + (nop!) + (l.w t4 (-> wind-work wind-time)) + (nop!) + (l.hu t5 instance (offset-of instance-shrubbery wind-index)) + (nop!) + (l.w a2 (-> work wind-vectors)) + (sll t6 t5 4) + (l.vf vf19 (-> work wind-const)) + (add a2 a2 t6) + (add t4 t5 t4) + (and.i t5 t4 63) + (l.d t4 (-> wind-state wind-vel)) + (sll t6 t5 4) + (l.d t5 (-> wind-state wind-pos)) + (add t7 t6 wind-work) + (m t6 vf4) + (pextlw t4 r0 t4) + (l.vf vf16 (-> hashed-wind wind-array 0)) + (pextlw t5 r0 t5) + (m vf18 t4) + (nop!) + (m vf17 t5) + (vmula.xyzw acc vf16 vf1) + (nop!) + (vmsubax.xyzw acc vf18 vf19) + (nop!) + (vmsuby.xyzw vf16 vf17 vf19) + (nop!) + (pcgtw t5 r0 t6) + (mmi-nop!) + (nop!) + (nop!) + (nop!) + (l.vf vf24 (-> work guard-plane 0)) + (vmulaz.xyzw acc vf16 vf19) + (nop!) + (vmadd.xyzw vf18 vf1 vf18) + (nop!) + (nop!) + (l.vf vf25 (-> work guard-plane 1)) + (nop!) + (l.vf vf26 (-> work guard-plane 2)) + (nop!) + (l.vf vf27 (-> work guard-plane 3)) + (vmulaz.xyzw acc vf18 vf19) + (nop!) + (vmadd.xyzw vf17 vf17 vf1) + (nop!) + (vmulax.xyzw acc vf24 vf2) + (nop!) + (vmadday.xyzw acc vf25 vf2) + (nop!) + (vmaddaz.xyzw acc vf26 vf2) + (nop!) + (vminiw.xyzw vf17 vf17 vf0) + (nop!) + (vmsubaw.xyzw acc vf27 vf0) + (nop!) + (vmsubw.xyzw vf24 vf1 vf2) + (nop!) + (nop!) + (m t4 vf18) + (vmaxw.xyzw vf27 vf17 vf19) + (nop!) + (ppacw t4 r0 t4) + (mmi-nop!) + (nop!) + (nop!) + (nop!) + (m t6 vf24) + (vmuly.xyzw vf27 vf27 vf9) + (nop!) + (pcgtw t6 r0 t6) + (mmi-nop!) + (ppach t6 r0 t6) + (mmi-nop!) + (vmulax.yw acc vf0 vf0) + (nop!) + (vmulay.xz acc vf27 vf10) + (nop!) + (vmadd.xyzw vf10 vf1 vf10) + (nop!) + (or t5 t6 t5) + (m t6 vf27) + (vmulax.yw acc vf0 vf0) + (l.w t7 (-> work paused)) + (vmulay.xz acc vf27 vf11) + (nop!) + (vmadd.xyzw vf11 vf1 vf11) + (nop!) + (b.ne t7 s7 use-alternate-billboard-axis :delay (ppacw t6 r0 t6)) + (vmulax.yw acc vf0 vf0) + (s.d t4 (-> wind-state wind-vel)) + (vmulay.xz acc vf27 vf12) + (s.d t6 (-> wind-state wind-pos)) + (b.nz t5 finish-billboard-instance :delay (vmadd.xyzw vf12 vf1 vf12)) + (b append-second-lod-record :delay (nop!)) + (label use-alternate-billboard-axis) + (vmulax.yw acc vf0 vf0) + (nop!) + (vmulay.xz acc vf27 vf12) + (nop!) + (b.nz t5 finish-billboard-instance :delay (vmadd.xyzw vf12 vf1 vf12))) + ;; The wind path's own record append: the same six quadwords as the flat path, but linked into + ;; the translucent geometry's chain rather than the opaque one, so a swaying shrub and a still + ;; one end up in different draws. + (asm-block append-billboard-instance + (label append-second-lod-record) + (vmulax.xyzw acc vf20 vf10) + (l.q a2 (-> work color)) + (vmadday.xyzw acc vf21 vf10) + (nop!) + (vmaddz.xyzw vf10 vf22 vf10) + (nop!) + (vmulax.xyzw acc vf20 vf11) + (nop!) + (vmadday.xyzw acc vf21 vf11) + (nop!) + (vmaddz.xyzw vf11 vf22 vf11) + (nop!) + (vmulax.xyzw acc vf20 vf12) + (nop!) + (vmadday.xyzw acc vf21 vf12) + (nop!) + (vmaddz.xyzw vf12 vf22 vf12) + (nop!) + (vmulax.xyzw acc vf20 vf13) + (nop!) + (vmadday.xyzw acc vf21 vf13) + (nop!) + (vmaddaz.xyzw acc vf22 vf13) + (nop!) + (vmaddw.xyzw vf13 vf23 vf0) + (s.q a2 packet-cursor 80) + (nop!) + (s.vf vf10 packet-cursor 16) + (nop!) + (s.vf vf11 packet-cursor 32) + (sll a2 packet-qwc 4) + (l.hu t4 (-> work mod-count 1)) + (add t5 a2 memory-cursor) + (l.hu t7 (-> work count 1)) + (sll t6 t4 4) + (l.w a2 (-> work next 1)) + (add t8 t6 work) + (l.w t6 (-> work last 1)) + (add.i t7 t7 1) + (l.q t8 t8 (offset-of instance-shrub-work matrix-tmpl)) + (add.i packet-qwc packet-qwc 6) + (s.h t7 (-> work count 1)) + (add.i t7 t4 1) + (s.q t8 packet-cursor) + (add.i t8 t7 -20) + (s.vf vf12 packet-cursor 48) + (movz t7 r0 t8) + (s.vf vf13 packet-cursor 64) + (add.i t8 t4 -10) + (s.h t7 (-> work mod-count 1)) + (add.i packet-cursor packet-cursor 96) + (s.w a2 packet-cursor -92) + (b.z t4 use-second-shared-matrix :delay (s.w t5 (-> work next 1))) + (b.nz t8 select-second-matrix :delay (nop!)) + (label use-second-shared-matrix) + (nop!) + (l.q t4 (-> work mscalf-tmpl)) + (nop!) + (l.q t7 (-> work mscalf-ret-tmpl)) + (nop!) + (s.w t5 (-> work last 1)) + (nop!) + (movz t4 t7 t6) + (add.i packet-qwc packet-qwc 1) + (s.q t4 packet-cursor) + (nop!) + (s.w a2 packet-cursor 4) + (b advance-instance :delay (add.i packet-cursor packet-cursor 16)) + (label select-second-matrix) + (add.i t5 t4 -9) + (nop!) + (b.z t5 use-second-indexed-matrix :delay (add.i t4 t4 -19)) + (b.nz t4 advance-instance :delay (nop!)) + (label use-second-indexed-matrix) + (nop!) + (sll t4 t7 4) + (nop!) + (add t4 t4 work) + (add.i packet-qwc packet-qwc 1) + (l.q t4 t4 (offset-of instance-shrub-work count-tmpl)) + (nop!) + (nop!) + (nop!) + (s.q t4 packet-cursor) + (nop!) + (s.w a2 packet-cursor 4) + (b advance-instance :delay (add.i packet-cursor packet-cursor 16))) + ;; A near shrub goes to the generic renderer, which wants a full matrix and one upload per + ;; fragment rather than a compact instance record. Each fragment gets one shrub-near-packet -- + ;; matrix, control stream, texture coordinates, colors, vertices, MSCAL -- patched from that + ;; fragment's own qwc and stream addresses and then copied into the output bank. The six + ;; templates are used round-robin because an earlier chain may still name the one just used, and + ;; every fragment rechecks the output bank, since a single instance can emit many of them. + (asm-block emit-near-instances + (label finish-billboard-instance) + (vmulax.xyzw acc vf28 vf10) + (l.vf vf24 (-> work bb-color)) + (vmadday.xyzw acc vf29 vf10) + (nop!) + (vmaddz.xyzw vf10 vf30 vf10) + (nop!) + (vmulax.xyzw acc vf28 vf11) + (nop!) + (vmadday.xyzw acc vf29 vf11) + (l.hu t4 (-> work near-count)) + (vmaddz.xyzw vf11 vf30 vf11) + (l.w a2 (-> work bucket-ptr)) + (vmulax.xyzw acc vf28 vf12) + (add.i t8 t4 1) + (vmadday.xyzw acc vf29 vf12) + (s.h t8 (-> work near-count)) + (vmaddz.xyzw vf12 vf30 vf12) + (l.w t4 (-> instance-bucket geometry 0)) + (vmulax.xyzw acc vf28 vf13) + (l.w t5 (-> work near-next)) + (vmadday.xyzw acc vf29 vf13) + (l.h t6 (-> near-geometry length)) + (vmaddaz.xyzw acc vf30 vf13) + (l.w a2 (-> work near-last)) + (vmaddw.xyzw vf13 vf31 vf0) + (l.w t7 (-> work current-shrub-near-packet)) + (vitof0.xyz vf24 vf24) + (s.h t8 (-> work count 0)) + (label refill-instance-bank) + (add.i t8 packet-qwc (- SHRUB-NEAR-OUTPUT-FLUSH-AT)) + (nop!) + (b.le t8 r0 advance-near-packet :delay (l.w fragment (-> near-geometry data 0))) + (dma-wait-counting-stalls! packet-cursor from-spr-channel (-> work wait-from-spr) + wait-for-instance-refill start-instance-refill) + (dma-return-output-bank! output-bank SHRUB-OUTPUT-FLIP packet-cursor packet-qwc memory-cursor from-spr-channel + start-instance-refill) + (label advance-near-packet) + (add t9 t7 work) + (add.i t7 t7 -144) + (add.i t4 t4 4) + (add.i t9 t9 (offset-of instance-shrub-work shrub-near-packets)) + (b.ge t7 r0 emit-near-instance :delay (l.q ra near-packet)) + (nop!) + (add.i t7 r0 720) + (label emit-near-instance) + ;; Near shrubs use the full matrix packet template. Rotate through the six near-packet slots so + ;; matrix uploads can be linked without overwriting a packet still referenced by the DMA chain. + (nop!) + (s.w t5 near-packet (shrub-near-tag-addr mscal-tmpl)) + (sll t5 packet-qwc 4) + (s.q ra packet-cursor) + (add t5 t5 memory-cursor) + (s.vf vf10 packet-cursor 16) + (movz a2 t5 a2) + (s.vf vf11 packet-cursor 32) + (add.i packet-qwc packet-qwc 12) + (s.vf vf12 packet-cursor 48) + (nop!) + (l.w ra (-> fragment vtx-cnt)) + (nop!) + (s.vf vf13 packet-cursor 64) + (nop!) + (s.vf vf24 packet-cursor 80) + (nop!) + (s.w ra packet-cursor 96) + (nop!) + (l.w ra (-> fragment cnt)) + (nop!) + (l.bu gp (-> fragment cnt-qwc)) + (nop!) + (s.w ra near-packet (shrub-near-tag-addr header-tmpl)) + (nop!) + (s.b gp near-packet (shrub-near-tag-qwc header-tmpl)) + (nop!) + (s.b gp near-packet (shrub-near-unpack-num header-tmpl)) + (nop!) + (l.w ra (-> fragment stq)) + (nop!) + (l.bu gp (-> fragment stq-qwc)) + (nop!) + (s.w ra near-packet (shrub-near-tag-addr stq-tmpl)) + (nop!) + (s.b gp near-packet (shrub-near-tag-qwc stq-tmpl)) + (nop!) + (l.w ra (-> fragment col)) + (nop!) + (l.bu gp (-> fragment col-qwc)) + (nop!) + (s.w ra near-packet (shrub-near-tag-addr color-tmpl)) + (nop!) + (s.b gp near-packet (shrub-near-tag-qwc color-tmpl)) + (nop!) + (l.w ra (-> fragment vtx)) + (nop!) + (l.bu gp (-> fragment vtx-qwc)) + (nop!) + (s.w ra near-packet (shrub-near-tag-addr vertex-tmpl)) + (nop!) + (s.b gp near-packet (shrub-near-tag-qwc vertex-tmpl)) + (nop!) + (l.w t8 (-> fragment vtx-cnt)) + (nop!) + (l.q ra near-packet (offset-of shrub-near-packet header-tmpl)) + (nop!) + (s.b t8 near-packet (shrub-near-unpack-num stq-tmpl)) + (nop!) + (s.b t8 near-packet (shrub-near-unpack-num color-tmpl)) + (nop!) + (s.b t8 near-packet (shrub-near-unpack-num vertex-tmpl)) + (nop!) + (s.q ra packet-cursor 112) + (nop!) + (l.q t8 near-packet (offset-of shrub-near-packet stq-tmpl)) + (nop!) + (l.q ra near-packet (offset-of shrub-near-packet color-tmpl)) + (nop!) + (s.q t8 packet-cursor 128) + (nop!) + (s.q ra packet-cursor 144) + (nop!) + (l.q t8 near-packet (offset-of shrub-near-packet vertex-tmpl)) + (nop!) + (l.q t9 near-packet (offset-of shrub-near-packet mscal-tmpl)) + (nop!) + (s.q t8 packet-cursor 160) + (add.i packet-cursor packet-cursor 192) + (s.q t9 packet-cursor -16) + (add.i t6 t6 -1) + (nop!) + (b.gt t6 r0 refill-instance-bank :delay (nop!)) + (nop!) + (s.w t7 (-> work current-shrub-near-packet)) + (lui t4 #x1000) + (s.w t5 (-> work near-next)) + (ori t4 t4 #xd400) + (s.w a2 (-> work near-last)) + (nop!) + (nop!)) + ;; VU0 entry 25 reloads the plane and camera registers the transform and wind code above + ;; borrowed, so the next leaf is tested with the same convention. Then step the leaf cursor by + ;; one instance-shrubbery and count down the run this scratchpad tag described. + (asm-block advance-instance + (label advance-instance) + (callms 25) + (l.w a2 (-> work src-ptr)) + (nop!) + (l.w t4 (-> work shrub-count)) + (add.i a2 a2 80) + (nop!) + (add.i t4 t4 -1) + (s.w a2 (-> work src-ptr)) + (b.gt t4 r0 load-instance :delay (s.w t4 (-> work shrub-count)))) + ;; A run is finished. Read the next scratchpad tag: a nonzero leaf count starts another run, and + ;; if the node walk left more work the whole pass repeats. Otherwise flush the cached bucket + ;; heads, return the last partial output bank, and wait for both directions of scratchpad + ;; traffic before publishing the advanced DMA-buffer cursor -- the fromSPR channel is still + ;; writing into that buffer, so the store has to be last. + ;; + ;; a1, a2, t2 and t3 are four anonymous quadword shuttles in this block, which is why their + ;; physical spellings come back here. + (asm-block finish-instance-pass + (label finish-instance-pass) + (nop!) + (l.w t4 chain-cursor (offset-of dma-packet vif0)) + (add.i a2 a2 16) + (l.w t5 (-> work last-shrubs)) + (nop!) + (s.w a2 (-> work src-ptr)) + (b.nz t4 load-instance :delay (s.w t4 (-> work shrub-count))) + (b.nz t5 scan-node-stack :delay (nop!)) + (nop!) + (l.w prev-bucket (-> work bucket-ptr)) + (nop!) + (l.q a2 (-> work last 0)) + (nop!) + (l.q t2 (-> work next 0)) + (nop!) + (l.q t3 (-> work count 0)) + (nop!) + (s.q a2 (-> prev-bucket last-clear)) + (nop!) + (s.q t2 (-> prev-bucket next-clear)) + (nop!) + (s.q t3 (-> prev-bucket count-clear-qword)) + (b.z packet-qwc wait-for-output-dma :delay (nop!)) + (nop!) + (l.w from-spr-channel (-> work from-spr)) + (nop!) + (nop!) + (dma-wait-counting-stalls! a1 from-spr-channel (-> work wait-from-spr) + wait-for-final-instance-upload upload-final-instance-bank) + (label upload-final-instance-bank) + (s.w memory-cursor (-> from-spr-channel madr)) + (sll a1 packet-qwc 4) + (s.w output-bank (-> from-spr-channel sadr)) + (xor.i a2 output-bank SHRUB-OUTPUT-FLIP) + (add memory-cursor memory-cursor a1) + (m a1 a2) + (s.w packet-qwc (-> from-spr-channel qwc)) + (add.i a1 r0 DMA-CHCR-STR) + (s.w a1 (-> from-spr-channel chcr)) + (add.i a1 r0 0) + (dma-wait-counting-stalls! a1 from-spr-channel (-> work wait-from-spr) + wait-for-output-dma commit-dma-buffer) + (label commit-dma-buffer) + (l.w dma-buf (-> work dma-buffer)) + (nop!) + (nop!) + (nop!) + (s.w memory-cursor (-> dma-buf base)) + (nop!) + (label done) + (m v0 r0) + (l.d ra sp) + (l.q gp sp 16) + (jr ra :delay (add.i sp sp 32)) + (nop!) + (nop!) + (nop!))))) \ No newline at end of file diff --git a/goal_src/jak1/engine/gfx/shrub/shrubbery.gc b/goal_src/jak1/engine/gfx/shrub/shrubbery.gc index 32b044e794..4da96c48f7 100644 --- a/goal_src/jak1/engine/gfx/shrub/shrubbery.gc +++ b/goal_src/jak1/engine/gfx/shrub/shrubbery.gc @@ -10,36 +10,32 @@ (require "engine/gfx/shrub/shrub-work.gc") ;; SHRUB -- the instanced foliage renderer. +;; (see assembly in shrubbery-ee-asm.gc) ;; ;; Shrub draws the small repeated background props: bushes, grass tufts, cattails, palm fronds, -;; barrels. Like TIE it is built around prototypes and instances. A prototype is authored once and -;; exported four times, at four levels of detail; an instance is one 80-byte placement of that -;; prototype somewhere in the level. Upload one prototype's geometry to VU1 and then feed it every -;; instance that chose it, and a level can afford several thousand bushes. +;; etc. Like TIE it is built around prototypes and instances. A prototype is authored once and +;; exported four times, at four levels of detail. An instance is one 80-byte placement of that +;; prototype somewhere in the level. ;; ;; The four geometries of a prototype-bucket-shrub, nearest first: ;; -;; 0 prototype-generic-shrub handed to the generic renderer, which is the only path that -;; clips, so this is what a shrub becomes when it is close enough -;; to cross the screen edge or the near plane. +;; 0 prototype-generic-shrub handed to the generic renderer, used when the geometry needs +;; to be clipped. ;; 1 prototype-shrubbery the ordinary VU1 path, one or more packed shrubbery fragments. ;; 2 prototype-trans-shrubbery the same format with alpha-test fail enabled, so the model can -;; dissolve as it hands over to the billboard. +;; fade out. ;; 3 billboard a single camera-facing quad, built by the EE as raw GS packets; ;; VU1 is not involved at all. ;; -;; An instance picks exactly one of the four from its camera distance and the prototype's dists / -;; rdists, and only that one. There is no morph between levels and no blending of two geometries: -;; the crossfade between the model and its billboard is done with alpha, which is what the fade -;; value computed in classify-instance is for. That is also why shrub has no interpolated points, -;; no dvert stream, and no per-fragment level of detail -- the whole prototype swaps at once. +;; An instance picks one of the four geometries from its camera distance and the prototype's dists / +;; rdists. There is no morph between levels and no blending of two geometries, other than the fade-out +;; of the transparent version. ;; ;; Color. Each level's shrub tree owns one time-of-day palette, interpolated once per frame into ;; the 1024-entry colors table in instance-shrub-work. An instance stores one index into that table -;; and that one color multiplies the whole instance; the per-vertex colors in the packed model are -;; only a modulation on top of it. So two bushes from the same prototype can be lit differently for -;; the price of two bytes, but a single bush is lit flatly. This is the cheap end of the same idea -;; TIE spends a per-vertex index stream on. +;; and that one color multiplies the whole instance. +;; So two bushes from the same prototype can be lit differently, but a single bush is lit by only +;; a single color. ;; ;; Visibility. Shrub does not participate in the packed visibility strings -- unpack-vis below is a ;; deliberate no-op. Culling is entirely the draw-node BVH: each node's bounding sphere is tested @@ -48,12 +44,12 @@ ;; distance selects a geometry. The tree allows any child count and any leaf depth, so the walk ;; carries its own six-entry node/count stack instead of draw-node-cull's fixed-depth bit walk. ;; -;; How the work divides. +;; Passes: ;; ;; EE, instance pass (draw-inline-array-instance-shrub): walk the BVH, cull, classify, shear the ;; instance into the wind, and append a compact record to the chosen geometry's list. Instances ;; arrive through the scratchpad in bulk and the records are assembled in the scratchpad too, so -;; the EE is reading one bank while the DMAC fills the other and returns the finished one. +;; the EE is reading one bank while DMA fills the other and returns the finished one. ;; ;; EE, prototype pass (draw-prototype-inline-array-shrub): for each geometry that got anything, ;; terminate its list with a count packet, then upload the prototype's model once and CALL the @@ -63,8 +59,7 @@ ;; colors in place; entry 103 transforms one ring of instance matrices against that model and ;; writes ST / RGBAQ / XYZF2 triples into a GIF output buffer. ;; -;; The instance chain is built backwards and read forwards, which is worth following once because -;; every packet template in shrub-work.gc exists to serve it. Each new instance record is a DMA tag +;; The instance chain is built backwards and read forwards. Each new instance record is a DMA tag ;; whose ADDR is patched to the record appended before it, so the chain runs newest-first. Records ;; are grouped ten at a time, alternating between two VU matrix rings, and mod-count says which ;; slot in that twenty-position cycle comes next. At each group boundary the tag is a cnt rather @@ -75,8 +70,7 @@ ;; earlier full group in reverse, each ending in its own MSCALF, then ret. Ten instances per draw ;; call, and no matrix is ever uploaded twice. ;; -;; Shrub VU1 memory map. Everything except the two GIF output buffers has a GOAL type describing it, -;; so the addresses below are derived from those types rather than transcribed. +;; Shrub VU1 memory map. Everything except the two GIF output buffers has a GOAL type describing it. ;; ;; 0 - 2 shrub-view-data: giftag, camera constants, fog clamp. Entry 0 copies all three ;; quadwords into registers, which is why the model bank may start at 2 and overwrite @@ -138,6 +132,8 @@ "Log in each five-quadword adgif shader record in this generic shrub fragment." (let ((texture-count (/ (-> this cnt-qwc) (the-as uint 5)))) (dotimes (i (the-as int texture-count)) + ;; no remap needed since shrub data references the level's combined tpages + ;; already. (adgif-shader-login-no-remap (-> this textures i)))) this) @@ -200,868 +196,10 @@ (defmethod login ((this drawable-tree-instance-shrub)) "Log in the tree's prototype array when it is present." - (if (nonzero? (-> this info prototype-inline-array-shrub)) (login (-> this info prototype-inline-array-shrub))) + (if (nonzero? (-> this info prototype-inline-array-shrub)) + (login (-> this info prototype-inline-array-shrub))) this) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; EE-side conveniences -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -;; Scratchpad. instance-shrub-dma is overlaid on the work area of the terrain-context, which is what -;; keeps the level header at the bottom of the scratchpad intact -- and is where the otherwise -;; mysterious sixteen in the bank addresses comes from. -(defmacro spr-offset (type &rest path) - `(+ (offset-of terrain-context work) (offset-of ,type ,@path))) - -;; Alternating a pair of banks is one xor of whatever bits their two addresses disagree on. Deriving -;; the masks documents the surprising part for free: the input pair toggles #x1470 rather than the -;; #x1450 the 5200-byte bank spacing suggests, because both addresses carry the work-area bias. -(defconstant SHRUB-INSTANCE-INPUT-FLIP - (logxor (spr-offset instance-shrub-dma instancea) (spr-offset instance-shrub-dma instanceb))) - -(defconstant SHRUB-OUTPUT-FLIP - (logxor (spr-offset instance-shrub-dma outa) (spr-offset instance-shrub-dma outb))) - -;; An output bank holds 128 quadwords. The largest thing one leaf can add to it is 21 quadwords -- -;; a billboard's fourteen plus a seven-quadword instance record -- and the near builder's records -;; are twelve, so each pass hands the bank back while that much room is still left. -(defconstant SHRUB-OUTPUT-BANK-QWC - (/ (- (spr-offset instance-shrub-dma outb) (spr-offset instance-shrub-dma outa)) 16)) - -(defconstant SHRUB-OUTPUT-FLUSH-AT (- SHRUB-OUTPUT-BANK-QWC 22)) - -(defconstant SHRUB-NEAR-OUTPUT-FLUSH-AT (- SHRUB-OUTPUT-BANK-QWC 13)) - - -;; Chain mode with tag transfer enabled, plus the start bit. The node chain's own tags are copied -;; into the scratchpad ahead of each run of instances, which is how the run length the EE stashed in -;; the tag's unused VIF word arrives with the data. -(defconstant DMA-CHCR-CHAIN-TTE-START 324) - -;; A DMA tag's quadword count is its first halfword and its address is its second word, so patching -;; either one in a near-shrub template is a byte or word store rather than a field write. A VIF -;; tag's NUM is its third byte. These three name the displacements instead of leaving 16, 20, 30, -;; 32, 36, 46 ... spread through the builder. -(defmacro shrub-near-tag-qwc (field) - `(offset-of shrub-near-packet ,field dma)) - -(defmacro shrub-near-tag-addr (field) - `(+ (offset-of shrub-near-packet ,field dma) 4)) - -(defmacro shrub-near-unpack-num (field) - `(+ (offset-of shrub-near-packet ,field vif1) 2)) - -(#when PC_PORT - ;; The PC renderer does not execute the shrub VU1 program. - (define shrub-vu1-block (new 'static 'vu-function :length #x0 :qlength #x0))) - -(#unless PC_PORT - (defvu1 shrub-vu1-block - ;; Register aliases for the draw entry. The three setup entries borrow most of these - ;; registers as anonymous quadword shuttles and address temporaries, so they keep their - ;; physical spellings; the names below mean what they say from #x067 onwards. - ;; - ;; The draw loop is a two-deep software pipeline: the -a and -b halves each carry one - ;; vertex from packed input through the transform, the perspective divide, the palette - ;; multiply and the store, one stage apart, so the divide of one vertex overlaps the - ;; transform of the next. That is why every register comes in an -a and a -b copy and why - ;; the same fifteen-pair body appears over and over below. - ;; - ;; vi10 and vf23 through vf27 are deliberately left physical. vi10 holds two unrelated - ;; things -- the -64 end-of-strip sentinel through the loop, then the GIF packet offset - ;; pulled out of kick-base at the end of an instance. vf23 and vf24 are quadwords lifted - ;; straight out of the model's object stream and vf25, vf26 and vf27 are built at entry 0 - ;; with only their z lane set; all five end up in the first quadword of a vertex's output - ;; group, and which of the five is stored is what the strip tests below decide. Naming - ;; them would be inventing a meaning the instructions do not establish. - (rlet ((code-a :reg vi01) ;; strip control code for the -a vertex, from clr-a.w - (dest-a :reg vi02) ;; GIF output address for the -a vertex - (area-sign :reg vi03) ;; sign flag of the screen-space edge cross product - (code-b :reg vi04) ;; strip control code for the -b vertex, from clr-b.w - (dest-b :reg vi05) ;; GIF output address for the -b vertex - (vtx-ptr :reg vi06) ;; cursor into the expanded vertex stream - (clr-ptr :reg vi07) ;; cursor into the expanded color stream - (stq-ptr :reg vi08) ;; cursor into the expanded texture-coordinate stream - (obj-ptr :reg vi09) ;; cursor into the model's object-record stream - (model-bank :reg vi11) ;; base of the model bank being drawn - (stream-qwc :reg vi12) ;; quadwords per stream, so the three cursors are one apart - (matrix-ptr :reg vi13) ;; cursor into the instance ring - (inst-count :reg vi14) ;; instances left in this ring - (kick-addr :reg vi15) ;; start of the GIF packet handed to XGKICK - (vtx-a :reg vf01) ;; packed input vertex, then its clip-space position - (pos-a :reg vf02) ;; screen position, then 12.4 fixed point - (clr-a :reg vf03) ;; model color; w carries the strip control code - (clr-a-lit :reg vf04) ;; model color times the instance color - (stq-a :reg vf05) ;; texture coordinate - (stq-a-q :reg vf06) ;; texture coordinate divided through by w - (slot-a :reg vf07) ;; vertex plus gifbuf; w is this vertex's output address - (vtx-b :reg vf08) - (pos-b :reg vf09) - (clr-b :reg vf10) - (clr-b-lit :reg vf11) - (stq-b :reg vf12) - (stq-b-q :reg vf13) - (slot-b :reg vf14) - (edge-0 :reg vf15) ;; the two screen-space edges whose cross product gives - (edge-1 :reg vf16) ;; the signed area of the triangle about to be emitted - (gifbuf :reg vf17) ;; the two GIF buffer addresses, biased and duplicated - (t-mtx0 :reg vf18) ;; instance transform, rows 0 through 3 - (t-mtx1 :reg vf19) - (t-mtx2 :reg vf20) - (t-mtx3 :reg vf21) - (inst-color :reg vf22) ;; this instance's time-of-day color - (kick-base :reg vf28) ;; biased GIF packet offset for the current instance - (fog-clamp :reg vf29) ;; fog-min and fog-max - (view-consts :reg vf30) ;; y is 2^23, z and w are pfog0 and pfog1 - (adgif-giftag :reg vf31) ;; the four-register A+D tag written ahead of each shader - ) - ;; Entry 0. Load the three quadwords of camera constants and build the address vectors - ;; the rest of the program runs on. - ;; - ;; gifbuf ends up holding the two GIF buffer addresses duplicated as (1 0 1 0) so that - ;; mr32, which rotates the lanes by one, swaps which buffer is in x while leaving the - ;; pair intact -- the same double-buffer trick the other background renderers use. The - ;; addresses are kept as floats biased by 2^23 (view-consts.y), because a small integer - ;; added to 2^23 lands in the low mantissa bits of an exactly representable float: adding - ;; a vertex's output offset to a buffer address is then one vector add, and mtir pulls - ;; the resulting VU address straight back out of the lane. The subtract-from-vf00 and - ;; subtract-from-self steps in the entry-17 path below are the same arithmetic run - ;; backwards to reach the second buffer. - (vu-pair (lq.xyzw adgif-giftag vi00 (shrub-view-const texture-giftag)) (nop)) ;; #x000 - (vu-pair (lq.xyzw view-consts vi00 (shrub-view-const consts)) (nop)) ;; #x001 - (vu-pair (lq.xyzw fog-clamp vi00 (shrub-view-const fog-clamp)) (nop)) ;; #x002 - (vu-pair (iaddiu vi11 vi00 SHRUB-VU-MODEL-BANK-B) (nop)) ;; #x003 - (vu-pair (iaddiu vi01 vi00 SHRUB-VU-GIF-BUFFER-1) (nop)) ;; #x004 - (vu-pair (mfir.x gifbuf vi01) (nop)) ;; #x005 - (vu-pair (mfir.z gifbuf vi01) (nop)) ;; #x006 - (vu-pair (iaddiu vi01 vi00 SHRUB-VU-GIF-BUFFER-0) (nop)) ;; #x007 - (vu-pair (mfir.y gifbuf vi01) (nop)) ;; #x008 - (vu-pair (mfir.w gifbuf vi01) (nop)) ;; #x009 - ;; Still entry 0: three of the five candidate first-quadwords for a vertex's output - ;; group get their z lane. The E bit at #x00f stops the program and #x010 runs in its - ;; delay pair, which is where the biased buffer addresses are finally formed. - (vu-pair (iaddi vi01 vi00 0x1) (nop)) ;; #x00a - (vu-pair (mfir.z vf25 vi01) (nop)) ;; #x00b - (vu-pair (iaddi vi01 vi00 0x1) (itof0.xyzw gifbuf gifbuf)) ;; #x00c - (vu-pair (mfir.z vf26 vi01) (nop)) ;; #x00d - (vu-pair (iaddi vi01 vi00 0x1) (nop)) ;; #x00e - (vu-pair (mfir.z vf27 vi01) (nop :e)) ;; #x00f - (vu-pair (nop) (addy.xyzw gifbuf gifbuf view-consts)) ;; #x010 - ;; Entries 17 and 21 prepare a model that VIF has just unpacked into one of the two - ;; banks. They are the same code with one difference -- which instance ring the following - ;; draw entry will read -- so they both fall into model-init-common after selecting it. - ;; - ;; Three things happen here, once per model rather than once per instance, which is the - ;; whole point of splitting them out of the draw entry: - ;; - ;; * the object stream is scattered into both GIF output buffers. Each quadword of that - ;; stream carries its own destination in its w lane, so the loop is: read a quadword, - ;; mtir the offset out of w, store the quadword at that offset in buffer 0 and again - ;; at the same offset in buffer 1. This is what lays down the static shape of the - ;; packet -- the strip tags and the per-shader register writes -- so the draw loop - ;; only has to fill in the vertex triples between them. - ;; * each shader becomes five quadwords: the four-register A+D tag in adgif-giftag - ;; followed by the four register writes the exporter packed, again into both buffers. - ;; * the packed vertex, color and texture-coordinate streams are converted in place to - ;; the formats the transform wants: 16-bit vertices and colors to float, texture - ;; coordinates from 20.12 fixed point. Two quadwords per iteration, expanding over - ;; the same addresses they were read from. - (vu-pair (iaddiu vi01 vi00 SHRUB-VU-MODEL-BANK-SUM) (subx.xz gifbuf vf00 gifbuf)) ;; #x011 - (vu-pair (isub vi11 vi01 vi11) (addy.xyzw kick-base vf00 view-consts)) ;; #x012 - (vu-pair (b model-init-common) (nop)) ;; #x013 - (vu-pair (iaddiu vi13 vi00 SHRUB-VU-RING-B) (nop)) ;; #x014 - (vu-pair (iaddiu vi01 vi00 SHRUB-VU-MODEL-BANK-SUM) (subx.xz gifbuf vf00 gifbuf)) ;; #x015 - (vu-pair (isub vi11 vi01 vi11) (addy.xyzw kick-base vf00 view-consts)) ;; #x016 - (vu-pair (iaddiu vi13 vi00 SHRUB-VU-RING-A) (nop)) ;; #x017 - (label model-init-common) - (vu-pair (isw.y vi13 vi00 (shrub-view-const mtx-buf-ptr)) (nop)) ;; #x018 - (vu-pair (iaddi vi09 vi11 0x1) (suby.xz gifbuf gifbuf gifbuf)) ;; #x019 - (vu-pair (ilwr.y vi05 vi11) (nop)) ;; #x01a - (vu-pair (ilwr.x vi15 vi11) (nop)) ;; #x01b - (vu-pair (lqi.xyzw vf01 vi09) (nop)) ;; #x01c - (vu-pair (nop) (addx.xz gifbuf gifbuf view-consts)) ;; #x01d - (vu-pair (nop) (nop)) ;; #x01e - (vu-pair (iaddi vi15 vi15 -0x1) (nop)) ;; #x01f - (vu-pair (mtir vi04 vf01.w) (nop)) ;; #x020 - (vu-pair (mtir vi02 gifbuf.x) (nop)) ;; #x021 - (vu-pair (ibne vi00 vi15 relocate-object-loop) (nop)) ;; #x022 - (vu-pair (mtir vi03 gifbuf.y) (nop)) ;; #x023 - (vu-pair (nop) (addz.y kick-base kick-base adgif-giftag)) ;; #x024 - (label relocate-object-loop) - (vu-pair (lqi.xyzw vf02 vi09) (nop)) ;; #x025 - (vu-pair (iadd vi01 vi04 vi02) (nop)) ;; #x026 - (vu-pair (iadd vi14 vi04 vi03) (nop)) ;; #x027 - (vu-pair (iaddi vi05 vi05 -0x1) (nop)) ;; #x028 - (vu-pair (mtir vi04 vf02.w) (nop)) ;; #x029 - (vu-pair (sq.xyzw vf01 vi01 0) (nop)) ;; #x02a - (vu-pair (ibeq vi00 vi05 begin-shader-relocation) (nop)) ;; #x02b - (vu-pair (sq.xyzw vf01 vi14 0) (nop)) ;; #x02c - (vu-pair (lqi.xyzw vf01 vi09) (nop)) ;; #x02d - (vu-pair (iadd vi01 vi04 vi02) (nop)) ;; #x02e - (vu-pair (iadd vi14 vi04 vi03) (nop)) ;; #x02f - (vu-pair (iaddi vi05 vi05 -0x1) (nop)) ;; #x030 - (vu-pair (mtir vi04 vf01.w) (nop)) ;; #x031 - (vu-pair (sq.xyzw vf02 vi01 0) (nop)) ;; #x032 - (vu-pair (ibne vi00 vi05 relocate-object-loop) (nop)) ;; #x033 - (vu-pair (sq.xyzw vf02 vi14 0) (nop)) ;; #x034 - (label begin-shader-relocation) - (vu-pair (iaddi vi09 vi09 -0x1) (nop)) ;; #x035 - (vu-pair (ilwr.x vi05 vi11) (nop)) ;; #x036 - (label relocate-shader-loop) - (vu-pair (lqi.xyzw vf01 vi09) (nop)) ;; #x037 - (vu-pair (lqi.xyzw vf02 vi09) (nop)) ;; #x038 - (vu-pair (lqi.xyzw vf03 vi09) (nop)) ;; #x039 - (vu-pair (lqi.xyzw vf04 vi09) (nop)) ;; #x03a - (vu-pair (mtir vi04 vf01.w) (nop)) ;; #x03b - (vu-pair (iaddi vi05 vi05 -0x1) (nop)) ;; #x03c - (vu-pair (nop) (nop)) ;; #x03d - (vu-pair (nop) (nop)) ;; #x03e - (vu-pair (iadd vi01 vi04 vi02) (nop)) ;; #x03f - (vu-pair (sq.xyzw adgif-giftag vi01 0) (nop)) ;; #x040 - (vu-pair (sq.xyzw vf01 vi01 1) (nop)) ;; #x041 - (vu-pair (sq.xyzw vf02 vi01 2) (nop)) ;; #x042 - (vu-pair (sq.xyzw vf03 vi01 3) (nop)) ;; #x043 - (vu-pair (sq.xyzw vf04 vi01 4) (nop)) ;; #x044 - (vu-pair (iadd vi01 vi04 vi03) (nop)) ;; #x045 - (vu-pair (sq.xyzw adgif-giftag vi01 0) (nop)) ;; #x046 - (vu-pair (sq.xyzw vf01 vi01 1) (nop)) ;; #x047 - (vu-pair (sq.xyzw vf02 vi01 2) (nop)) ;; #x048 - (vu-pair (sq.xyzw vf03 vi01 3) (nop)) ;; #x049 - (vu-pair (ibne vi00 vi05 relocate-shader-loop) (nop)) ;; #x04a - (vu-pair (sq.xyzw vf04 vi01 4) (nop)) ;; #x04b - (vu-pair (ilwr.z vi12 vi11) (nop)) ;; #x04c - (vu-pair (ilwr.w vi06 vi11) (nop)) ;; #x04d - (vu-pair (isw.x vi09 vi00 (shrub-view-const tex-start-ptr)) (nop)) ;; #x04e - (vu-pair (iaddi vi15 vi00 0x1) (nop)) ;; #x04f - (vu-pair (ior vi05 vi12 vi00) (nop)) ;; #x050 - (vu-pair (iadd vi06 vi06 vi11) (nop)) ;; #x051 - (vu-pair (iadd vi07 vi06 vi12) (nop)) ;; #x052 - (vu-pair (iadd vi08 vi07 vi12) (nop)) ;; #x053 - (vu-pair (iaddi vi05 vi05 0x1) (nop)) ;; #x054 - (label expand-model-stream-loop) - (vu-pair (lq.xyzw vf01 vi06 0) (nop)) ;; #x055 - (vu-pair (lq.xyz vf03 vi07 0) (nop)) ;; #x056 - (vu-pair (lq.xyz vf05 vi08 0) (nop)) ;; #x057 - (vu-pair (iaddi vi05 vi05 -0x2) (nop)) ;; #x058 - (vu-pair (lq.xyzw vf08 vi06 1) (itof0.xyzw vf01 vf01)) ;; #x059 - (vu-pair (lq.xyz vf10 vi07 1) (itof0.xyzw vf03 vf03)) ;; #x05a - (vu-pair (lq.xyz vf12 vi08 1) (itof12.xyzw vf05 vf05)) ;; #x05b - (vu-pair (nop) (nop)) ;; #x05c - (vu-pair (sqi.xyzw vf01 vi06) (itof0.xyzw vf08 vf08)) ;; #x05d - (vu-pair (sqi.xyz vf03 vi07) (itof0.xyzw vf10 vf10)) ;; #x05e - (vu-pair (ibeq vi00 vi05 model-init-done) (itof12.xyzw vf12 vf12)) ;; #x05f - (vu-pair (sqi.xyz vf05 vi08) (nop)) ;; #x060 - (vu-pair (sqi.xyzw vf08 vi06) (nop)) ;; #x061 - (vu-pair (sqi.xyz vf10 vi07) (nop)) ;; #x062 - (vu-pair (ibne vi15 vi05 expand-model-stream-loop) (nop)) ;; #x063 - (vu-pair (sqi.xyz vf12 vi08) (nop)) ;; #x064 - (label model-init-done) - (vu-pair (nop) (nop :e)) ;; #x065 - (vu-pair (nop) (nop)) ;; #x066 - ;; Entry 103 draws one ring of instances against the model prepared above. - ;; - ;; Setup: flip the saved ring pointer to the ring this call owns, read its count word, - ;; and load the first instance's four transform rows and its time-of-day color. Fog - ;; comes out of the translation row: row3.z times pfog0 plus pfog1, clamped by fog-max, - ;; and converted to 12.4, so the whole instance is fogged as one flat value rather than - ;; per vertex. The three stream cursors are then laid out one stream-qwc apart from the - ;; model header's own base, and the pipeline is primed with the first two vertices. - ;; - ;; Steady state, per vertex: add gifbuf to the packed vertex so its w lane becomes an - ;; absolute output address, transform it by the four rows, start the divide by w, scale - ;; the texture coordinate by the reciprocal, multiply the model color by the instance - ;; color, convert the screen position to 12.4, and store the resulting triple at the - ;; address the vertex named. There is no output cursor and no ordering constraint between - ;; vertices, which is exactly what lets two of them stay in flight. - ;; - ;; The test between the halves is a triangle facing test done in screen space, and it is - ;; the reason the body below is repeated so many times. edge-0 and edge-1 are the two - ;; screen-space edges leaving the shared vertex; mulax/msubax across their x and y lanes - ;; forms the 2-D cross product, whose sign is the triangle's winding, and fsand lifts - ;; that sign bit out of the status flags into area-sign. Each vertex carries its own - ;; expected winding in the w lane of its color quadword -- which alternates along a - ;; triangle strip -- so comparing code against area-sign says whether this triangle faces - ;; the camera. A negative code instead means end of strip. Rather than branch on the - ;; result and stall, the loop is unrolled once per (pipeline half, comparison outcome) - ;; pair, which is why the same fifteen pairs appear as the strip-stage-* variants and why - ;; the only thing that actually differs between them is which quadword lands in the first - ;; slot of the vertex's output group. - (vu-pair (ilw.y matrix-ptr vi00 (shrub-view-const mtx-buf-ptr)) (nop)) ;; #x067 - (vu-pair (iaddiu code-a vi00 SHRUB-VU-RING-SUM) (nop)) ;; #x068 - (vu-pair (ilwr.w vtx-ptr model-bank) (nop)) ;; #x069 - (vu-pair (ilw.x obj-ptr vi00 (shrub-view-const tex-start-ptr)) (nop)) ;; #x06a - (vu-pair (isub matrix-ptr code-a matrix-ptr) (nop)) ;; #x06b - (vu-pair (isw.y matrix-ptr vi00 (shrub-view-const mtx-buf-ptr)) (nop)) ;; #x06c - (vu-pair (ilwr.x inst-count matrix-ptr) (nop)) ;; #x06d - (vu-pair (lq.xyzw t-mtx3 matrix-ptr (shrub-matrix-qword mat vector 3)) (nop)) ;; #x06e - (vu-pair (lq.xyzw t-mtx0 matrix-ptr (shrub-matrix-qword mat vector 0)) (nop)) ;; #x06f - (vu-pair (lq.xyzw t-mtx1 matrix-ptr (shrub-matrix-qword mat vector 1)) (nop)) ;; #x070 - (vu-pair (lq.xyzw t-mtx2 matrix-ptr (shrub-matrix-qword mat vector 2)) (nop)) ;; #x071 - (vu-pair (lq.xyzw inst-color matrix-ptr (shrub-matrix-qword color)) (mulaz.w ACC t-mtx3 view-consts)) ;; #x072 - (vu-pair (iaddi matrix-ptr matrix-ptr (+ SHRUB-VU-MATRIX-STRIDE 1)) (maddw.w pos-b view-consts vf00)) ;; #x073 - (vu-pair (iadd vtx-ptr vtx-ptr model-bank) (nop)) ;; #x074 - (vu-pair (iadd clr-ptr vtx-ptr stream-qwc) (nop)) ;; #x075 - (vu-pair (iadd stq-ptr clr-ptr stream-qwc) (nop)) ;; #x076 - (vu-pair (move.w clr-a-lit inst-color) (miniy.w pos-b pos-b fog-clamp)) ;; #x077 - (vu-pair (move.w clr-b-lit inst-color) (nop)) ;; #x078 - (vu-pair (lqi.xyzw vtx-a vtx-ptr) (nop)) ;; #x079 - (vu-pair (lqi.xyzw stq-a stq-ptr) (nop)) ;; #x07a - (vu-pair (lqi.xyzw clr-a clr-ptr) (ftoi4.w pos-a pos-b)) ;; #x07b - (vu-pair (lqi.xyzw vtx-b vtx-ptr) (ftoi4.w pos-b pos-b)) ;; #x07c - (vu-pair (lqi.xyzw stq-b stq-ptr) (add.xyzw slot-a vtx-a gifbuf)) ;; #x07d - (vu-pair (lqi.xyzw clr-b clr-ptr) (mulaw.xyzw ACC t-mtx3 vf00)) ;; #x07e - (vu-pair (nop) (maddax.xyzw ACC t-mtx0 vtx-a)) ;; #x07f - (vu-pair (nop) (madday.xyzw ACC t-mtx1 vtx-a)) ;; #x080 - (vu-pair (nop) (maddz.xyzw vtx-a t-mtx2 vtx-a)) ;; #x081 - (vu-pair (nop) (add.xyzw slot-b vtx-b gifbuf)) ;; #x082 - (vu-pair (nop) (mulaw.xyzw ACC t-mtx3 vf00)) ;; #x083 - (vu-pair (nop) (maddax.xyzw ACC t-mtx0 vtx-b)) ;; #x084 - (vu-pair (div Q vf00.w vtx-a.w) (madday.xyzw ACC t-mtx1 vtx-b)) ;; #x085 - (vu-pair (nop) (maddz.xyzw vtx-b t-mtx2 vtx-b)) ;; #x086 - (vu-pair (nop) (nop)) ;; #x087 - (vu-pair (nop) (nop)) ;; #x088 - (vu-pair (nop) (nop)) ;; #x089 - (vu-pair (nop) (nop)) ;; #x08a - (vu-pair (nop) (nop)) ;; #x08b - (vu-pair (nop) (mul.xyz pos-a vtx-a Q)) ;; #x08c - (vu-pair (nop) (mul.xyzw stq-a-q stq-a Q)) ;; #x08d - (vu-pair (lqi.xyzw vtx-a vtx-ptr) (mul.xyz clr-a-lit clr-a inst-color)) ;; #x08e - (vu-pair (div Q vf00.w vtx-b.w) (nop)) ;; #x08f - (vu-pair (lqi.xyzw stq-a stq-ptr) (nop)) ;; #x090 - (vu-pair (mtir dest-a slot-a.w) (nop)) ;; #x091 - (vu-pair (lqi.xyzw clr-a clr-ptr) (nop)) ;; #x092 - (vu-pair (nop) (nop)) ;; #x093 - (vu-pair (nop) (add.xyzw slot-a vtx-a gifbuf)) ;; #x094 - (vu-pair (nop) (mulaw.xyzw ACC t-mtx3 vf00)) ;; #x095 - (vu-pair (nop) (maddax.xyzw ACC t-mtx0 vtx-a)) ;; #x096 - (vu-pair (nop) (madday.xyzw ACC t-mtx1 vtx-a)) ;; #x097 - (vu-pair (nop) (maddz.xyzw vtx-a t-mtx2 vtx-a)) ;; #x098 - (vu-pair (nop) (mul.xyz pos-b vtx-b Q)) ;; #x099 - (vu-pair (nop) (mul.xyzw stq-b-q stq-b Q)) ;; #x09a - (label vertex-pipe-a-even) - (vu-pair (lqi.xyzw vtx-b vtx-ptr) (mul.xyz clr-b-lit clr-b inst-color)) ;; #x09b - (vu-pair (div Q vf00.w vtx-a.w) (nop)) ;; #x09c - (vu-pair (lqi.xyzw stq-b stq-ptr) (nop)) ;; #x09d - (vu-pair (mtir dest-b slot-b.w) (nop)) ;; #x09e - (vu-pair (lqi.xyzw clr-b clr-ptr) (sub.xyzw edge-0 pos-a pos-b)) ;; #x09f - (vu-pair (sq.xyzw stq-a-q dest-a 1) (ftoi4.xyz pos-a pos-a)) ;; #x0a0 - (vu-pair (sq.xyzw clr-a-lit dest-a 2) (add.xyzw slot-b vtx-b gifbuf)) ;; #x0a1 - (vu-pair (lqi.xyzw vf23 obj-ptr) (mulaw.xyzw ACC t-mtx3 vf00)) ;; #x0a2 - (vu-pair (lqi.xyzw vf24 obj-ptr) (maddax.xyzw ACC t-mtx0 vtx-b)) ;; #x0a3 - (vu-pair (sq.xyzw pos-a dest-a 3) (madday.xyzw ACC t-mtx1 vtx-b)) ;; #x0a4 - (vu-pair (isubiu vi10 vi00 0x40) (maddz.xyzw vtx-b t-mtx2 vtx-b)) ;; #x0a5 - (label vertex-pipe-b-even) - (vu-pair (mtir code-a clr-a.w) (mul.xyz pos-a vtx-a Q)) ;; #x0a6 - (vu-pair (sq.xyzw vf25 dest-a 0) (mul.xyzw stq-a-q stq-a Q)) ;; #x0a7 - (vu-pair (lqi.xyzw vtx-a vtx-ptr) (mul.xyz clr-a-lit clr-a inst-color)) ;; #x0a8 - (vu-pair (div Q vf00.w vtx-b.w) (nop)) ;; #x0a9 - (vu-pair (lqi.xyzw stq-a stq-ptr) (nop)) ;; #x0aa - (vu-pair (mtir dest-a slot-a.w) (nop)) ;; #x0ab - (vu-pair (lqi.xyzw clr-a clr-ptr) (sub.xyzw edge-1 pos-b pos-a)) ;; #x0ac - (vu-pair (sq.xyzw stq-b-q dest-b 1) (ftoi4.xyz pos-b pos-b)) ;; #x0ad - (vu-pair (sq.xyzw clr-b-lit dest-b 2) (add.xyzw slot-a vtx-a gifbuf)) ;; #x0ae - (vu-pair (nop) (mulaw.xyzw ACC t-mtx3 vf00)) ;; #x0af - (vu-pair (nop) (maddax.xyzw ACC t-mtx0 vtx-a)) ;; #x0b0 - (vu-pair (sq.xyzw pos-b dest-b 3) (madday.xyzw ACC t-mtx1 vtx-a)) ;; #x0b1 - (vu-pair (nop) (maddz.xyzw vtx-a t-mtx2 vtx-a)) ;; #x0b2 - (vu-pair (mtir code-b clr-b.w) (mul.xyz pos-b vtx-b Q)) ;; #x0b3 - (vu-pair (sq.xyzw vf25 dest-b 0) (mul.xyzw stq-b-q stq-b Q)) ;; #x0b4 - (vu-pair (lqi.xyzw vtx-b vtx-ptr) (mul.xyz clr-b-lit clr-b inst-color)) ;; #x0b5 - (vu-pair (div Q vf00.w vtx-a.w) (nop)) ;; #x0b6 - (vu-pair (lqi.xyzw stq-b stq-ptr) (mulay.x ACC edge-0 edge-1)) ;; #x0b7 - (vu-pair (mtir dest-b slot-b.w) (msubay.x ACC edge-1 edge-0)) ;; #x0b8 - (vu-pair (lqi.xyzw clr-b clr-ptr) (sub.xyzw edge-0 pos-a pos-b)) ;; #x0b9 - (vu-pair (sq.xyzw stq-a-q dest-a 1) (ftoi4.xyz pos-a pos-a)) ;; #x0ba - (vu-pair (sq.xyzw clr-a-lit dest-a 2) (add.xyzw slot-b vtx-b gifbuf)) ;; #x0bb - (vu-pair (fsand area-sign 0x2) (mulaw.xyzw ACC t-mtx3 vf00)) ;; #x0bc - (vu-pair (ibltz code-a strip-stage-a0) (maddax.xyzw ACC t-mtx0 vtx-b)) ;; #x0bd - (vu-pair (sq.xyzw pos-a dest-a 3) (madday.xyzw ACC t-mtx1 vtx-b)) ;; #x0be - (label compare-strip-edge-a) - (vu-pair (ibeq code-a area-sign select-strip-path-b) (maddz.xyzw vtx-b t-mtx2 vtx-b)) ;; #x0bf - (vu-pair (mtir code-a clr-a.w) (mul.xyz pos-a vtx-a Q)) ;; #x0c0 - (vu-pair (b select-strip-path-a) (nop)) ;; #x0c1 - (vu-pair (sq.xyzw vf23 dest-a 0) (mul.xyzw stq-a-q stq-a Q)) ;; #x0c2 - (vu-pair (nop) (mul.xyzw stq-a-q stq-a Q)) ;; #x0c3 - (label vertex-pipe-a-odd) - (vu-pair (lqi.xyzw vtx-a vtx-ptr) (mul.xyz clr-a-lit clr-a inst-color)) ;; #x0c4 - (vu-pair (div Q vf00.w vtx-b.w) (nop)) ;; #x0c5 - (vu-pair (lqi.xyzw stq-a stq-ptr) (nop)) ;; #x0c6 - (vu-pair (mtir dest-a slot-a.w) (nop)) ;; #x0c7 - (vu-pair (lqi.xyzw clr-a clr-ptr) (sub.xyzw edge-1 pos-b pos-a)) ;; #x0c8 - (vu-pair (sq.xyzw stq-b-q dest-b 1) (ftoi4.xyz pos-b pos-b)) ;; #x0c9 - (vu-pair (sq.xyzw clr-b-lit dest-b 2) (add.xyzw slot-a vtx-a gifbuf)) ;; #x0ca - (vu-pair (lqi.xyzw vf23 obj-ptr) (mulaw.xyzw ACC t-mtx3 vf00)) ;; #x0cb - (vu-pair (lqi.xyzw vf24 obj-ptr) (maddax.xyzw ACC t-mtx0 vtx-a)) ;; #x0cc - (vu-pair (sq.xyzw pos-b dest-b 3) (madday.xyzw ACC t-mtx1 vtx-a)) ;; #x0cd - (vu-pair (isubiu vi10 vi00 0x40) (maddz.xyzw vtx-a t-mtx2 vtx-a)) ;; #x0ce - (label vertex-pipe-b-odd) - (vu-pair (mtir code-b clr-b.w) (mul.xyz pos-b vtx-b Q)) ;; #x0cf - (vu-pair (sq.xyzw vf25 dest-b 0) (mul.xyzw stq-b-q stq-b Q)) ;; #x0d0 - (vu-pair (lqi.xyzw vtx-b vtx-ptr) (mul.xyz clr-b-lit clr-b inst-color)) ;; #x0d1 - (vu-pair (div Q vf00.w vtx-a.w) (nop)) ;; #x0d2 - (vu-pair (lqi.xyzw stq-b stq-ptr) (nop)) ;; #x0d3 - (vu-pair (mtir dest-b slot-b.w) (nop)) ;; #x0d4 - (vu-pair (lqi.xyzw clr-b clr-ptr) (sub.xyzw edge-0 pos-a pos-b)) ;; #x0d5 - (vu-pair (sq.xyzw stq-a-q dest-a 1) (ftoi4.xyz pos-a pos-a)) ;; #x0d6 - (vu-pair (sq.xyzw clr-a-lit dest-a 2) (add.xyzw slot-b vtx-b gifbuf)) ;; #x0d7 - (vu-pair (nop) (mulaw.xyzw ACC t-mtx3 vf00)) ;; #x0d8 - (vu-pair (nop) (maddax.xyzw ACC t-mtx0 vtx-b)) ;; #x0d9 - (vu-pair (sq.xyzw pos-a dest-a 3) (madday.xyzw ACC t-mtx1 vtx-b)) ;; #x0da - (vu-pair (nop) (maddz.xyzw vtx-b t-mtx2 vtx-b)) ;; #x0db - (vu-pair (mtir code-a clr-a.w) (mul.xyz pos-a vtx-a Q)) ;; #x0dc - (vu-pair (sq.xyzw vf25 dest-a 0) (mul.xyzw stq-a-q stq-a Q)) ;; #x0dd - (vu-pair (lqi.xyzw vtx-a vtx-ptr) (mul.xyz clr-a-lit clr-a inst-color)) ;; #x0de - (vu-pair (div Q vf00.w vtx-b.w) (nop)) ;; #x0df - (vu-pair (lqi.xyzw stq-a stq-ptr) (mulay.x ACC edge-0 edge-1)) ;; #x0e0 - (vu-pair (mtir dest-a slot-a.w) (msubay.x ACC edge-1 edge-0)) ;; #x0e1 - (vu-pair (lqi.xyzw clr-a clr-ptr) (sub.xyzw edge-1 pos-b pos-a)) ;; #x0e2 - (vu-pair (sq.xyzw stq-b-q dest-b 1) (ftoi4.xyz pos-b pos-b)) ;; #x0e3 - (vu-pair (sq.xyzw clr-b-lit dest-b 2) (add.xyzw slot-a vtx-a gifbuf)) ;; #x0e4 - (vu-pair (fsand area-sign 0x2) (mulaw.xyzw ACC t-mtx3 vf00)) ;; #x0e5 - (vu-pair (ibltz code-b reload-strip-b) (maddax.xyzw ACC t-mtx0 vtx-a)) ;; #x0e6 - (vu-pair (sq.xyzw pos-b dest-b 3) (madday.xyzw ACC t-mtx1 vtx-a)) ;; #x0e7 - (label compare-strip-edge-b) - (vu-pair (ibeq code-b area-sign advance-strip-a) (maddz.xyzw vtx-a t-mtx2 vtx-a)) ;; #x0e8 - (vu-pair (mtir code-b clr-b.w) (mul.xyz pos-b vtx-b Q)) ;; #x0e9 - (vu-pair (b continue-strip-b) (nop)) ;; #x0ea - (vu-pair (sq.xyzw vf23 dest-b 0) (mul.xyzw stq-b-q stq-b Q)) ;; #x0eb - (label continue-strip-a) - (vu-pair (sq.xyzw vf26 dest-a 0) (mul.xyzw stq-a-q stq-a Q)) ;; #x0ec - (label select-strip-path-a) - (vu-pair (lqi.xyzw vtx-a vtx-ptr) (mul.xyz clr-a-lit clr-a inst-color)) ;; #x0ed - (vu-pair (div Q vf00.w vtx-b.w) (nop)) ;; #x0ee - (vu-pair (lqi.xyzw stq-a stq-ptr) (mulay.x ACC edge-0 edge-1)) ;; #x0ef - (vu-pair (mtir dest-a slot-a.w) (msubay.x ACC edge-1 edge-0)) ;; #x0f0 - (vu-pair (lqi.xyzw clr-a clr-ptr) (sub.xyzw edge-1 pos-b pos-a)) ;; #x0f1 - (vu-pair (sq.xyzw stq-b-q dest-b 1) (ftoi4.xyz pos-b pos-b)) ;; #x0f2 - (vu-pair (sq.xyzw clr-b-lit dest-b 2) (add.xyzw slot-a vtx-a gifbuf)) ;; #x0f3 - (vu-pair (fsand area-sign 0x2) (mulaw.xyzw ACC t-mtx3 vf00)) ;; #x0f4 - (vu-pair (ibltz code-b flush-strip-a) (maddax.xyzw ACC t-mtx0 vtx-a)) ;; #x0f5 - (vu-pair (sq.xyzw pos-b dest-b 3) (madday.xyzw ACC t-mtx1 vtx-a)) ;; #x0f6 - (vu-pair (ibeq code-b area-sign advance-strip-a) (maddz.xyzw vtx-a t-mtx2 vtx-a)) ;; #x0f7 - (vu-pair (mtir code-b clr-b.w) (mul.xyz pos-b vtx-b Q)) ;; #x0f8 - (vu-pair (sq.xyzw vf26 dest-b 0) (mul.xyzw stq-b-q stq-b Q)) ;; #x0f9 - (label continue-strip-b) - (vu-pair (lqi.xyzw vtx-b vtx-ptr) (mul.xyz clr-b-lit clr-b inst-color)) ;; #x0fa - (vu-pair (div Q vf00.w vtx-a.w) (nop)) ;; #x0fb - (vu-pair (lqi.xyzw stq-b stq-ptr) (mulay.x ACC edge-0 edge-1)) ;; #x0fc - (vu-pair (mtir dest-b slot-b.w) (msubay.x ACC edge-1 edge-0)) ;; #x0fd - (vu-pair (lqi.xyzw clr-b clr-ptr) (sub.xyzw edge-0 pos-a pos-b)) ;; #x0fe - (vu-pair (sq.xyzw stq-a-q dest-a 1) (ftoi4.xyz pos-a pos-a)) ;; #x0ff - (vu-pair (sq.xyzw clr-a-lit dest-a 2) (add.xyzw slot-b vtx-b gifbuf)) ;; #x100 - (vu-pair (fsand area-sign 0x2) (mulaw.xyzw ACC t-mtx3 vf00)) ;; #x101 - (vu-pair (ibltz code-a reload-strip-a) (maddax.xyzw ACC t-mtx0 vtx-b)) ;; #x102 - (vu-pair (sq.xyzw pos-a dest-a 3) (madday.xyzw ACC t-mtx1 vtx-b)) ;; #x103 - (vu-pair (ibne code-a area-sign continue-strip-a) (maddz.xyzw vtx-b t-mtx2 vtx-b)) ;; #x104 - (vu-pair (mtir code-a clr-a.w) (mul.xyz pos-a vtx-a Q)) ;; #x105 - (label select-strip-path-b) - (vu-pair (b dispatch-strip-state) (nop)) ;; #x106 - (vu-pair (sq.xyzw vf24 dest-a 0) (mul.xyzw stq-a-q stq-a Q)) ;; #x107 - (label advance-strip-a) - (vu-pair (b finish-strip-a) (nop)) ;; #x108 - (vu-pair (sq.xyzw vf24 dest-b 0) (mul.xyzw stq-b-q stq-b Q)) ;; #x109 - (label advance-strip-b) - (vu-pair (sq.xyzw vf27 dest-a 0) (mul.xyzw stq-a-q stq-a Q)) ;; #x10a - (label dispatch-strip-state) - (vu-pair (lqi.xyzw vtx-a vtx-ptr) (mul.xyz clr-a-lit clr-a inst-color)) ;; #x10b - (vu-pair (div Q vf00.w vtx-b.w) (nop)) ;; #x10c - (vu-pair (lqi.xyzw stq-a stq-ptr) (mulax.y ACC edge-0 edge-1)) ;; #x10d - (vu-pair (mtir dest-a slot-a.w) (msubax.y ACC edge-1 edge-0)) ;; #x10e - (vu-pair (lqi.xyzw clr-a clr-ptr) (sub.xyzw edge-1 pos-b pos-a)) ;; #x10f - (vu-pair (sq.xyzw stq-b-q dest-b 1) (ftoi4.xyz pos-b pos-b)) ;; #x110 - (vu-pair (sq.xyzw clr-b-lit dest-b 2) (add.xyzw slot-a vtx-a gifbuf)) ;; #x111 - (vu-pair (fsand area-sign 0x2) (mulaw.xyzw ACC t-mtx3 vf00)) ;; #x112 - (vu-pair (ibltz code-b loop-strip-a) (maddax.xyzw ACC t-mtx0 vtx-a)) ;; #x113 - (vu-pair (sq.xyzw pos-b dest-b 3) (madday.xyzw ACC t-mtx1 vtx-a)) ;; #x114 - (vu-pair (ibeq code-b area-sign finish-strip-b) (maddz.xyzw vtx-a t-mtx2 vtx-a)) ;; #x115 - (vu-pair (mtir code-b clr-b.w) (mul.xyz pos-b vtx-b Q)) ;; #x116 - (vu-pair (sq.xyzw vf27 dest-b 0) (mul.xyzw stq-b-q stq-b Q)) ;; #x117 - (label finish-strip-a) - (vu-pair (lqi.xyzw vtx-b vtx-ptr) (mul.xyz clr-b-lit clr-b inst-color)) ;; #x118 - (vu-pair (div Q vf00.w vtx-a.w) (nop)) ;; #x119 - (vu-pair (lqi.xyzw stq-b stq-ptr) (mulax.y ACC edge-0 edge-1)) ;; #x11a - (vu-pair (mtir dest-b slot-b.w) (msubax.y ACC edge-1 edge-0)) ;; #x11b - (vu-pair (lqi.xyzw clr-b clr-ptr) (sub.xyzw edge-0 pos-a pos-b)) ;; #x11c - (vu-pair (sq.xyzw stq-a-q dest-a 1) (ftoi4.xyz pos-a pos-a)) ;; #x11d - (vu-pair (sq.xyzw clr-a-lit dest-a 2) (add.xyzw slot-b vtx-b gifbuf)) ;; #x11e - (vu-pair (fsand area-sign 0x2) (mulaw.xyzw ACC t-mtx3 vf00)) ;; #x11f - (vu-pair (ibltz code-a flush-strip-b) (maddax.xyzw ACC t-mtx0 vtx-b)) ;; #x120 - (vu-pair (sq.xyzw pos-a dest-a 3) (madday.xyzw ACC t-mtx1 vtx-b)) ;; #x121 - (vu-pair (ibne code-a area-sign advance-strip-b) (maddz.xyzw vtx-b t-mtx2 vtx-b)) ;; #x122 - (vu-pair (mtir code-a clr-a.w) (mul.xyz pos-a vtx-a Q)) ;; #x123 - (vu-pair (b select-strip-path-a) (nop)) ;; #x124 - (vu-pair (sq.xyzw vf23 dest-a 0) (mul.xyzw stq-a-q stq-a Q)) ;; #x125 - (label finish-strip-b) - (vu-pair (b continue-strip-b) (nop)) ;; #x126 - (vu-pair (sq.xyzw vf23 dest-b 0) (mul.xyzw stq-b-q stq-b Q)) ;; #x127 - (label flush-strip-a) - (vu-pair (ibne vi10 code-b strip-stage-a1) (nop)) ;; #x128 - (vu-pair (iaddiu code-b code-b 0x80) (nop)) ;; #x129 - (vu-pair (lqi.xyzw vf23 obj-ptr) (nop)) ;; #x12a - (vu-pair (b vertex-pipe-b-odd) (nop)) ;; #x12b - (vu-pair (lqi.xyzw vf24 obj-ptr) (maddz.xyzw vtx-a t-mtx2 vtx-a)) ;; #x12c - (label reload-strip-a) - (vu-pair (ibne vi10 code-a kick-first-buffer) (nop)) ;; #x12d - (vu-pair (iaddiu code-a code-a 0x80) (nop)) ;; #x12e - (vu-pair (lqi.xyzw vf23 obj-ptr) (nop)) ;; #x12f - (vu-pair (b vertex-pipe-b-even) (nop)) ;; #x130 - (vu-pair (lqi.xyzw vf24 obj-ptr) (maddz.xyzw vtx-b t-mtx2 vtx-b)) ;; #x131 - (label loop-strip-a) - (vu-pair (ibne vi10 code-b reload-first-buffer) (nop)) ;; #x132 - (vu-pair (iaddiu code-b code-b 0x80) (nop)) ;; #x133 - (vu-pair (lqi.xyzw vf23 obj-ptr) (nop)) ;; #x134 - (vu-pair (b vertex-pipe-b-odd) (nop)) ;; #x135 - (vu-pair (lqi.xyzw vf24 obj-ptr) (maddz.xyzw vtx-a t-mtx2 vtx-a)) ;; #x136 - (label flush-strip-b) - (vu-pair (ibne vi10 code-a reload-second-buffer) (nop)) ;; #x137 - (vu-pair (iaddiu code-a code-a 0x80) (nop)) ;; #x138 - (vu-pair (lqi.xyzw vf23 obj-ptr) (nop)) ;; #x139 - (vu-pair (b vertex-pipe-b-even) (nop)) ;; #x13a - (vu-pair (lqi.xyzw vf24 obj-ptr) (maddz.xyzw vtx-b t-mtx2 vtx-b)) ;; #x13b - (label reload-strip-b) - (vu-pair (ibeq vi10 code-b compare-strip-edge-b) (nop)) ;; #x13c - (vu-pair (iaddiu code-b code-b 0x80) (nop)) ;; #x13d - (vu-pair (ilwr.w vtx-ptr model-bank) (maddz.xyzw vtx-a t-mtx2 vtx-a)) ;; #x13e - (vu-pair (mr32.xyzw gifbuf gifbuf) (mul.xyz pos-b vtx-b Q)) ;; #x13f - (vu-pair (lqi.xyzw t-mtx0 matrix-ptr) (nop)) ;; #x140 - (vu-pair (lqi.xyzw t-mtx1 matrix-ptr) (nop)) ;; #x141 - (vu-pair (lqi.xyzw t-mtx2 matrix-ptr) (nop)) ;; #x142 - (vu-pair (lqi.xyzw t-mtx3 matrix-ptr) (nop)) ;; #x143 - (vu-pair (iadd vtx-ptr vtx-ptr model-bank) (nop)) ;; #x144 - (vu-pair (iadd clr-ptr vtx-ptr stream-qwc) (nop)) ;; #x145 - (vu-pair (iadd stq-ptr clr-ptr stream-qwc) (nop)) ;; #x146 - (vu-pair (ibeq code-b area-sign store-obj-1-and-resume-d0) (nop)) ;; #x147 - (vu-pair (mtir code-b clr-b.w) (nop)) ;; #x148 - (vu-pair (b strip-stage-a2) (nop)) ;; #x149 - (vu-pair (sq.xyzw vf23 dest-b 0) (mul.xyzw stq-b-q stq-b Q)) ;; #x14a - (label strip-stage-a0) - (vu-pair (ibeq vi10 code-a compare-strip-edge-a) (nop)) ;; #x14b - (vu-pair (iaddiu code-a code-a 0x80) (nop)) ;; #x14c - (vu-pair (ilwr.w vtx-ptr model-bank) (maddz.xyzw vtx-b t-mtx2 vtx-b)) ;; #x14d - (vu-pair (mr32.xyzw gifbuf gifbuf) (mul.xyz pos-a vtx-a Q)) ;; #x14e - (vu-pair (lqi.xyzw t-mtx0 matrix-ptr) (nop)) ;; #x14f - (vu-pair (lqi.xyzw t-mtx1 matrix-ptr) (nop)) ;; #x150 - (vu-pair (lqi.xyzw t-mtx2 matrix-ptr) (nop)) ;; #x151 - (vu-pair (lqi.xyzw t-mtx3 matrix-ptr) (nop)) ;; #x152 - (vu-pair (iadd vtx-ptr vtx-ptr model-bank) (nop)) ;; #x153 - (vu-pair (iadd clr-ptr vtx-ptr stream-qwc) (nop)) ;; #x154 - (vu-pair (iadd stq-ptr clr-ptr stream-qwc) (nop)) ;; #x155 - (vu-pair (ibeq code-a area-sign store-obj-1-and-resume-e0) (nop)) ;; #x156 - (vu-pair (mtir code-a clr-a.w) (nop)) ;; #x157 - (vu-pair (b strip-stage-c0) (nop)) ;; #x158 - (vu-pair (sq.xyzw vf23 dest-a 0) (mul.xyzw stq-a-q stq-a Q)) ;; #x159 - (label strip-stage-a1) - (vu-pair (ilwr.w vtx-ptr model-bank) (maddz.xyzw vtx-a t-mtx2 vtx-a)) ;; #x15a - (vu-pair (mr32.xyzw gifbuf gifbuf) (mul.xyz pos-b vtx-b Q)) ;; #x15b - (vu-pair (lqi.xyzw t-mtx0 matrix-ptr) (nop)) ;; #x15c - (vu-pair (lqi.xyzw t-mtx1 matrix-ptr) (nop)) ;; #x15d - (vu-pair (lqi.xyzw t-mtx2 matrix-ptr) (nop)) ;; #x15e - (vu-pair (lqi.xyzw t-mtx3 matrix-ptr) (nop)) ;; #x15f - (vu-pair (iadd vtx-ptr vtx-ptr model-bank) (nop)) ;; #x160 - (vu-pair (iadd clr-ptr vtx-ptr stream-qwc) (nop)) ;; #x161 - (vu-pair (iadd stq-ptr clr-ptr stream-qwc) (nop)) ;; #x162 - (vu-pair (ibeq code-b area-sign store-obj-1-and-resume-d0) (nop)) ;; #x163 - (vu-pair (mtir code-b clr-b.w) (nop)) ;; #x164 - (vu-pair (sq.xyzw vf26 dest-b 0) (mul.xyzw stq-b-q stq-b Q)) ;; #x165 - (label strip-stage-a2) - (vu-pair (lqi.xyzw vtx-b vtx-ptr) (mul.xyz clr-b-lit clr-b inst-color)) ;; #x166 - (vu-pair (div Q vf00.w vtx-a.w) (nop)) ;; #x167 - (vu-pair (lqi.xyzw stq-b stq-ptr) (mulay.x ACC edge-0 edge-1)) ;; #x168 - (vu-pair (mtir dest-b slot-b.w) (msubay.x ACC edge-1 edge-0)) ;; #x169 - (vu-pair (lqi.xyzw clr-b clr-ptr) (sub.xyzw edge-0 pos-a pos-b)) ;; #x16a - (vu-pair (sq.xyzw stq-a-q dest-a 1) (ftoi4.xyz pos-a pos-a)) ;; #x16b - (vu-pair (sq.xyzw clr-a-lit dest-a 2) (add.xyzw slot-b vtx-b gifbuf)) ;; #x16c - (vu-pair (fsand area-sign 0x2) (mulaw.xyzw ACC t-mtx3 vf00)) ;; #x16d - (vu-pair (ilw.x obj-ptr vi00 (shrub-view-const tex-start-ptr)) (maddax.xyzw ACC t-mtx0 vtx-b)) ;; #x16e - (vu-pair (sq.xyzw pos-a dest-a 3) (madday.xyzw ACC t-mtx1 vtx-b)) ;; #x16f - (vu-pair (ibeq code-a area-sign store-obj-1-and-resume-d1) (maddz.xyzw vtx-b t-mtx2 vtx-b)) ;; #x170 - (vu-pair (mtir code-a clr-a.w) (mul.xyz pos-a vtx-a Q)) ;; #x171 - (vu-pair (sq.xyzw vf26 dest-a 0) (mul.xyzw stq-a-q stq-a Q)) ;; #x172 - (label strip-stage-a3) - (vu-pair (lqi.xyzw vtx-a vtx-ptr) (mul.xyz clr-a-lit clr-a inst-color)) ;; #x173 - (vu-pair (div Q vf00.w vtx-b.w) (nop)) ;; #x174 - (vu-pair (lqi.xyzw stq-a stq-ptr) (mulay.x ACC edge-0 edge-1)) ;; #x175 - (vu-pair (mtir dest-a slot-a.w) (msubay.x ACC edge-1 edge-0)) ;; #x176 - (vu-pair (lqi.xyzw clr-a clr-ptr) (sub.xyzw edge-1 pos-b pos-a)) ;; #x177 - (vu-pair (sq.xyzw stq-b-q dest-b 1) (ftoi4.xyz pos-b pos-b)) ;; #x178 - (vu-pair (sq.xyzw clr-b-lit dest-b 2) (add.xyzw slot-a vtx-a gifbuf)) ;; #x179 - (vu-pair (fsand area-sign 0x2) (mulaw.xyzw ACC t-mtx3 vf00)) ;; #x17a - (vu-pair (mtir vi10 kick-base.x) (maddax.xyzw ACC t-mtx0 vtx-a)) ;; #x17b - (vu-pair (sq.xyzw pos-b dest-b 3) (madday.xyzw ACC t-mtx1 vtx-a)) ;; #x17c - (vu-pair (lqi.xyzw inst-color matrix-ptr) (mulz.w pos-b t-mtx3 view-consts)) ;; #x17d - (vu-pair (ibeq code-b area-sign store-obj-1-and-resume-d2) (maddz.xyzw vtx-a t-mtx2 vtx-a)) ;; #x17e - (vu-pair (mtir code-b clr-b.w) (mul.xyz pos-b vtx-b Q)) ;; #x17f - (vu-pair (sq.xyzw vf26 dest-b 0) (mul.xyzw stq-b-q stq-b Q)) ;; #x180 - (label strip-stage-b0) - (vu-pair (lqi.xyzw vtx-b vtx-ptr) (mul.xyz clr-b-lit clr-b inst-color)) ;; #x181 - (vu-pair (div Q vf00.w vtx-a.w) (mulay.x ACC edge-0 edge-1)) ;; #x182 - (vu-pair (lqi.xyzw stq-b stq-ptr) (addw.w pos-b view-consts pos-b)) ;; #x183 - (vu-pair (mtir dest-b slot-b.w) (msubay.x ACC edge-1 edge-0)) ;; #x184 - (vu-pair (lqi.xyzw clr-b clr-ptr) (sub.xyzw edge-0 pos-a pos-b)) ;; #x185 - (vu-pair (sq.xyzw stq-a-q dest-a 1) (ftoi4.xyz pos-a pos-a)) ;; #x186 - (vu-pair (sq.xyzw clr-a-lit dest-a 2) (add.xyzw slot-b vtx-b gifbuf)) ;; #x187 - (vu-pair (fsand area-sign 0x2) (mulaw.xyzw ACC t-mtx3 vf00)) ;; #x188 - (vu-pair (iaddi inst-count inst-count -0x1) (maddax.xyzw ACC t-mtx0 vtx-b)) ;; #x189 - (vu-pair (sq.xyzw pos-a dest-a 3) (madday.xyzw ACC t-mtx1 vtx-b)) ;; #x18a - (vu-pair (mtir kick-addr gifbuf.x) (miniy.w pos-b pos-b fog-clamp)) ;; #x18b - (vu-pair (ibeq code-a area-sign store-obj-1-and-kick-second) (maddz.xyzw vtx-b t-mtx2 vtx-b)) ;; #x18c - (vu-pair (mtir code-a clr-a.w) (mul.xyz pos-a vtx-a Q)) ;; #x18d - (vu-pair (sq.xyzw vf26 dest-a 0) (mul.xyzw stq-a-q stq-a Q)) ;; #x18e - (label strip-stage-b1) - (vu-pair (move.w clr-a-lit inst-color) (addy.x kick-base vf00 kick-base)) ;; #x18f - (vu-pair (move.w clr-b-lit inst-color) (nop)) ;; #x190 - (vu-pair (iadd kick-addr kick-addr vi10) (nop)) ;; #x191 - (vu-pair (ibne vi00 inst-count vertex-pipe-a-odd) (ftoi4.w pos-a pos-b)) ;; #x192 - ;; Kick the first output-buffer/parity variant. The following three exits cover the other - ;; buffer and strip-parity combinations, sharing the branch tails at the end of the block. - (vu-pair (xgkick kick-addr) (ftoi4.w pos-b pos-b)) ;; #x193 - (vu-pair (nop) (nop :e)) ;; #x194 - (vu-pair (nop) (nop)) ;; #x195 - (label kick-first-buffer) - (vu-pair (ilwr.w vtx-ptr model-bank) (maddz.xyzw vtx-b t-mtx2 vtx-b)) ;; #x196 - (vu-pair (mr32.xyzw gifbuf gifbuf) (mul.xyz pos-a vtx-a Q)) ;; #x197 - (vu-pair (lqi.xyzw t-mtx0 matrix-ptr) (nop)) ;; #x198 - (vu-pair (lqi.xyzw t-mtx1 matrix-ptr) (nop)) ;; #x199 - (vu-pair (lqi.xyzw t-mtx2 matrix-ptr) (nop)) ;; #x19a - (vu-pair (lqi.xyzw t-mtx3 matrix-ptr) (nop)) ;; #x19b - (vu-pair (iadd vtx-ptr vtx-ptr model-bank) (nop)) ;; #x19c - (vu-pair (iadd clr-ptr vtx-ptr stream-qwc) (nop)) ;; #x19d - (vu-pair (iadd stq-ptr clr-ptr stream-qwc) (nop)) ;; #x19e - (vu-pair (ibeq code-a area-sign store-obj-1-and-resume-e0) (nop)) ;; #x19f - (vu-pair (mtir code-a clr-a.w) (nop)) ;; #x1a0 - (vu-pair (sq.xyzw vf26 dest-a 0) (mul.xyzw stq-a-q stq-a Q)) ;; #x1a1 - (label strip-stage-c0) - (vu-pair (lqi.xyzw vtx-a vtx-ptr) (mul.xyz clr-a-lit clr-a inst-color)) ;; #x1a2 - (vu-pair (div Q vf00.w vtx-b.w) (nop)) ;; #x1a3 - (vu-pair (lqi.xyzw stq-a stq-ptr) (mulay.x ACC edge-0 edge-1)) ;; #x1a4 - (vu-pair (mtir dest-a slot-a.w) (msubay.x ACC edge-1 edge-0)) ;; #x1a5 - (vu-pair (lqi.xyzw clr-a clr-ptr) (sub.xyzw edge-1 pos-b pos-a)) ;; #x1a6 - (vu-pair (sq.xyzw stq-b-q dest-b 1) (ftoi4.xyz pos-b pos-b)) ;; #x1a7 - (vu-pair (sq.xyzw clr-b-lit dest-b 2) (add.xyzw slot-a vtx-a gifbuf)) ;; #x1a8 - (vu-pair (fsand area-sign 0x2) (mulaw.xyzw ACC t-mtx3 vf00)) ;; #x1a9 - (vu-pair (ilw.x obj-ptr vi00 (shrub-view-const tex-start-ptr)) (maddax.xyzw ACC t-mtx0 vtx-a)) ;; #x1aa - (vu-pair (sq.xyzw pos-b dest-b 3) (madday.xyzw ACC t-mtx1 vtx-a)) ;; #x1ab - (vu-pair (ibeq code-b area-sign store-obj-1-and-resume-e1) (maddz.xyzw vtx-a t-mtx2 vtx-a)) ;; #x1ac - (vu-pair (mtir code-b clr-b.w) (mul.xyz pos-b vtx-b Q)) ;; #x1ad - (vu-pair (sq.xyzw vf26 dest-b 0) (mul.xyzw stq-b-q stq-b Q)) ;; #x1ae - (label strip-stage-c1) - (vu-pair (lqi.xyzw vtx-b vtx-ptr) (mul.xyz clr-b-lit clr-b inst-color)) ;; #x1af - (vu-pair (div Q vf00.w vtx-a.w) (nop)) ;; #x1b0 - (vu-pair (lqi.xyzw stq-b stq-ptr) (mulay.x ACC edge-0 edge-1)) ;; #x1b1 - (vu-pair (mtir dest-b slot-b.w) (msubay.x ACC edge-1 edge-0)) ;; #x1b2 - (vu-pair (lqi.xyzw clr-b clr-ptr) (sub.xyzw edge-0 pos-a pos-b)) ;; #x1b3 - (vu-pair (sq.xyzw stq-a-q dest-a 1) (ftoi4.xyz pos-a pos-a)) ;; #x1b4 - (vu-pair (sq.xyzw clr-a-lit dest-a 2) (add.xyzw slot-b vtx-b gifbuf)) ;; #x1b5 - (vu-pair (fsand area-sign 0x2) (mulaw.xyzw ACC t-mtx3 vf00)) ;; #x1b6 - (vu-pair (mtir vi10 kick-base.x) (maddax.xyzw ACC t-mtx0 vtx-b)) ;; #x1b7 - (vu-pair (sq.xyzw pos-a dest-a 3) (madday.xyzw ACC t-mtx1 vtx-b)) ;; #x1b8 - (vu-pair (lqi.xyzw inst-color matrix-ptr) (mulz.w pos-a t-mtx3 view-consts)) ;; #x1b9 - (vu-pair (ibeq code-a area-sign store-obj-1-and-resume-e2) (maddz.xyzw vtx-b t-mtx2 vtx-b)) ;; #x1ba - (vu-pair (mtir code-a clr-a.w) (mul.xyz pos-a vtx-a Q)) ;; #x1bb - (vu-pair (sq.xyzw vf26 dest-a 0) (mul.xyzw stq-a-q stq-a Q)) ;; #x1bc - (label strip-stage-c2) - (vu-pair (lqi.xyzw vtx-a vtx-ptr) (mul.xyz clr-a-lit clr-a inst-color)) ;; #x1bd - (vu-pair (div Q vf00.w vtx-b.w) (mulay.x ACC edge-0 edge-1)) ;; #x1be - (vu-pair (lqi.xyzw stq-a stq-ptr) (addw.w pos-a view-consts pos-a)) ;; #x1bf - (vu-pair (mtir dest-a slot-a.w) (msubay.x ACC edge-1 edge-0)) ;; #x1c0 - (vu-pair (lqi.xyzw clr-a clr-ptr) (sub.xyzw edge-1 pos-b pos-a)) ;; #x1c1 - (vu-pair (sq.xyzw stq-b-q dest-b 1) (ftoi4.xyz pos-b pos-b)) ;; #x1c2 - (vu-pair (sq.xyzw clr-b-lit dest-b 2) (add.xyzw slot-a vtx-a gifbuf)) ;; #x1c3 - (vu-pair (fsand area-sign 0x2) (mulaw.xyzw ACC t-mtx3 vf00)) ;; #x1c4 - (vu-pair (iaddi inst-count inst-count -0x1) (maddax.xyzw ACC t-mtx0 vtx-a)) ;; #x1c5 - (vu-pair (sq.xyzw pos-b dest-b 3) (madday.xyzw ACC t-mtx1 vtx-a)) ;; #x1c6 - (vu-pair (mtir kick-addr gifbuf.x) (miniy.w pos-a pos-a fog-clamp)) ;; #x1c7 - (vu-pair (ibeq code-b area-sign store-obj-1-and-kick-final) (maddz.xyzw vtx-a t-mtx2 vtx-a)) ;; #x1c8 - (vu-pair (mtir code-b clr-b.w) (mul.xyz pos-b vtx-b Q)) ;; #x1c9 - (vu-pair (sq.xyzw vf26 dest-b 0) (mul.xyzw stq-b-q stq-b Q)) ;; #x1ca - (label strip-stage-c3) - (vu-pair (move.w clr-a-lit inst-color) (addy.x kick-base vf00 kick-base)) ;; #x1cb - (vu-pair (move.w clr-b-lit inst-color) (nop)) ;; #x1cc - (vu-pair (iadd kick-addr kick-addr vi10) (nop)) ;; #x1cd - (vu-pair (ibne vi00 inst-count vertex-pipe-a-even) (ftoi4.w pos-b pos-a)) ;; #x1ce - (vu-pair (xgkick kick-addr) (ftoi4.w pos-a pos-a)) ;; #x1cf - (vu-pair (nop) (nop :e)) ;; #x1d0 - (vu-pair (nop) (nop)) ;; #x1d1 - (label reload-first-buffer) - (vu-pair (ilwr.w vtx-ptr model-bank) (maddz.xyzw vtx-a t-mtx2 vtx-a)) ;; #x1d2 - (vu-pair (mr32.xyzw gifbuf gifbuf) (mul.xyz pos-b vtx-b Q)) ;; #x1d3 - (vu-pair (lqi.xyzw t-mtx0 matrix-ptr) (nop)) ;; #x1d4 - (vu-pair (lqi.xyzw t-mtx1 matrix-ptr) (nop)) ;; #x1d5 - (vu-pair (lqi.xyzw t-mtx2 matrix-ptr) (nop)) ;; #x1d6 - (vu-pair (lqi.xyzw t-mtx3 matrix-ptr) (nop)) ;; #x1d7 - (vu-pair (iadd vtx-ptr vtx-ptr model-bank) (nop)) ;; #x1d8 - (vu-pair (iadd clr-ptr vtx-ptr stream-qwc) (nop)) ;; #x1d9 - (vu-pair (iadd stq-ptr clr-ptr stream-qwc) (nop)) ;; #x1da - (vu-pair (ibeq code-b area-sign store-obj-0-and-resume-a2) (nop)) ;; #x1db - (vu-pair (mtir code-b clr-b.w) (nop)) ;; #x1dc - (vu-pair (sq.xyzw vf27 dest-b 0) (mul.xyzw stq-b-q stq-b Q)) ;; #x1dd - (label strip-stage-d0) - (vu-pair (lqi.xyzw vtx-b vtx-ptr) (mul.xyz clr-b-lit clr-b inst-color)) ;; #x1de - (vu-pair (div Q vf00.w vtx-a.w) (nop)) ;; #x1df - (vu-pair (lqi.xyzw stq-b stq-ptr) (mulax.y ACC edge-0 edge-1)) ;; #x1e0 - (vu-pair (mtir dest-b slot-b.w) (msubax.y ACC edge-1 edge-0)) ;; #x1e1 - (vu-pair (lqi.xyzw clr-b clr-ptr) (sub.xyzw edge-0 pos-a pos-b)) ;; #x1e2 - (vu-pair (sq.xyzw stq-a-q dest-a 1) (ftoi4.xyz pos-a pos-a)) ;; #x1e3 - (vu-pair (sq.xyzw clr-a-lit dest-a 2) (add.xyzw slot-b vtx-b gifbuf)) ;; #x1e4 - (vu-pair (fsand area-sign 0x2) (mulaw.xyzw ACC t-mtx3 vf00)) ;; #x1e5 - (vu-pair (ilw.x obj-ptr vi00 (shrub-view-const tex-start-ptr)) (maddax.xyzw ACC t-mtx0 vtx-b)) ;; #x1e6 - (vu-pair (sq.xyzw pos-a dest-a 3) (madday.xyzw ACC t-mtx1 vtx-b)) ;; #x1e7 - (vu-pair (ibeq code-a area-sign store-obj-0-and-resume-a3) (maddz.xyzw vtx-b t-mtx2 vtx-b)) ;; #x1e8 - (vu-pair (mtir code-a clr-a.w) (mul.xyz pos-a vtx-a Q)) ;; #x1e9 - (vu-pair (sq.xyzw vf27 dest-a 0) (mul.xyzw stq-a-q stq-a Q)) ;; #x1ea - (label strip-stage-d1) - (vu-pair (lqi.xyzw vtx-a vtx-ptr) (mul.xyz clr-a-lit clr-a inst-color)) ;; #x1eb - (vu-pair (div Q vf00.w vtx-b.w) (nop)) ;; #x1ec - (vu-pair (lqi.xyzw stq-a stq-ptr) (mulax.y ACC edge-0 edge-1)) ;; #x1ed - (vu-pair (mtir dest-a slot-a.w) (msubax.y ACC edge-1 edge-0)) ;; #x1ee - (vu-pair (lqi.xyzw clr-a clr-ptr) (sub.xyzw edge-1 pos-b pos-a)) ;; #x1ef - (vu-pair (sq.xyzw stq-b-q dest-b 1) (ftoi4.xyz pos-b pos-b)) ;; #x1f0 - (vu-pair (sq.xyzw clr-b-lit dest-b 2) (add.xyzw slot-a vtx-a gifbuf)) ;; #x1f1 - (vu-pair (fsand area-sign 0x2) (mulaw.xyzw ACC t-mtx3 vf00)) ;; #x1f2 - (vu-pair (mtir vi10 kick-base.x) (maddax.xyzw ACC t-mtx0 vtx-a)) ;; #x1f3 - (vu-pair (sq.xyzw pos-b dest-b 3) (madday.xyzw ACC t-mtx1 vtx-a)) ;; #x1f4 - (vu-pair (lqi.xyzw inst-color matrix-ptr) (mulz.w pos-b t-mtx3 view-consts)) ;; #x1f5 - (vu-pair (ibeq code-b area-sign store-obj-0-and-resume-b0) (maddz.xyzw vtx-a t-mtx2 vtx-a)) ;; #x1f6 - (vu-pair (mtir code-b clr-b.w) (mul.xyz pos-b vtx-b Q)) ;; #x1f7 - (vu-pair (sq.xyzw vf27 dest-b 0) (mul.xyzw stq-b-q stq-b Q)) ;; #x1f8 - (label strip-stage-d2) - (vu-pair (lqi.xyzw vtx-b vtx-ptr) (mul.xyz clr-b-lit clr-b inst-color)) ;; #x1f9 - (vu-pair (div Q vf00.w vtx-a.w) (mulax.y ACC edge-0 edge-1)) ;; #x1fa - (vu-pair (lqi.xyzw stq-b stq-ptr) (addw.w pos-b view-consts pos-b)) ;; #x1fb - (vu-pair (mtir dest-b slot-b.w) (msubax.y ACC edge-1 edge-0)) ;; #x1fc - (vu-pair (lqi.xyzw clr-b clr-ptr) (sub.xyzw edge-0 pos-a pos-b)) ;; #x1fd - (vu-pair (sq.xyzw stq-a-q dest-a 1) (ftoi4.xyz pos-a pos-a)) ;; #x1fe - (vu-pair (sq.xyzw clr-a-lit dest-a 2) (add.xyzw slot-b vtx-b gifbuf)) ;; #x1ff - (vu-pair (fsand area-sign 0x2) (mulaw.xyzw ACC t-mtx3 vf00)) ;; #x200 - (vu-pair (iaddi inst-count inst-count -0x1) (maddax.xyzw ACC t-mtx0 vtx-b)) ;; #x201 - (vu-pair (sq.xyzw pos-a dest-a 3) (madday.xyzw ACC t-mtx1 vtx-b)) ;; #x202 - (vu-pair (mtir kick-addr gifbuf.x) (miniy.w pos-b pos-b fog-clamp)) ;; #x203 - (vu-pair (ibeq code-a area-sign store-obj-0-and-resume-b1) (maddz.xyzw vtx-b t-mtx2 vtx-b)) ;; #x204 - (vu-pair (mtir code-a clr-a.w) (mul.xyz pos-a vtx-a Q)) ;; #x205 - (vu-pair (sq.xyzw vf27 dest-a 0) (mul.xyzw stq-a-q stq-a Q)) ;; #x206 - (label kick-second-buffer) - (vu-pair (move.w clr-a-lit inst-color) (addy.x kick-base vf00 kick-base)) ;; #x207 - (vu-pair (move.w clr-b-lit inst-color) (nop)) ;; #x208 - (vu-pair (iadd kick-addr kick-addr vi10) (nop)) ;; #x209 - (vu-pair (ibne vi00 inst-count vertex-pipe-a-odd) (ftoi4.w pos-a pos-b)) ;; #x20a - (vu-pair (xgkick kick-addr) (ftoi4.w pos-b pos-b)) ;; #x20b - (vu-pair (nop) (nop :e)) ;; #x20c - (vu-pair (nop) (nop)) ;; #x20d - (label reload-second-buffer) - (vu-pair (ilwr.w vtx-ptr model-bank) (maddz.xyzw vtx-b t-mtx2 vtx-b)) ;; #x20e - (vu-pair (mr32.xyzw gifbuf gifbuf) (mul.xyz pos-a vtx-a Q)) ;; #x20f - (vu-pair (lqi.xyzw t-mtx0 matrix-ptr) (nop)) ;; #x210 - (vu-pair (lqi.xyzw t-mtx1 matrix-ptr) (nop)) ;; #x211 - (vu-pair (lqi.xyzw t-mtx2 matrix-ptr) (nop)) ;; #x212 - (vu-pair (lqi.xyzw t-mtx3 matrix-ptr) (nop)) ;; #x213 - (vu-pair (iadd vtx-ptr vtx-ptr model-bank) (nop)) ;; #x214 - (vu-pair (iadd clr-ptr vtx-ptr stream-qwc) (nop)) ;; #x215 - (vu-pair (iadd stq-ptr clr-ptr stream-qwc) (nop)) ;; #x216 - (vu-pair (ibeq code-a area-sign store-obj-0-and-resume-c0) (nop)) ;; #x217 - (vu-pair (mtir code-a clr-a.w) (nop)) ;; #x218 - (vu-pair (sq.xyzw vf27 dest-a 0) (mul.xyzw stq-a-q stq-a Q)) ;; #x219 - (label strip-stage-e0) - (vu-pair (lqi.xyzw vtx-a vtx-ptr) (mul.xyz clr-a-lit clr-a inst-color)) ;; #x21a - (vu-pair (div Q vf00.w vtx-b.w) (nop)) ;; #x21b - (vu-pair (lqi.xyzw stq-a stq-ptr) (mulax.y ACC edge-0 edge-1)) ;; #x21c - (vu-pair (mtir dest-a slot-a.w) (msubax.y ACC edge-1 edge-0)) ;; #x21d - (vu-pair (lqi.xyzw clr-a clr-ptr) (sub.xyzw edge-1 pos-b pos-a)) ;; #x21e - (vu-pair (sq.xyzw stq-b-q dest-b 1) (ftoi4.xyz pos-b pos-b)) ;; #x21f - (vu-pair (sq.xyzw clr-b-lit dest-b 2) (add.xyzw slot-a vtx-a gifbuf)) ;; #x220 - (vu-pair (fsand area-sign 0x2) (mulaw.xyzw ACC t-mtx3 vf00)) ;; #x221 - (vu-pair (ilw.x obj-ptr vi00 (shrub-view-const tex-start-ptr)) (maddax.xyzw ACC t-mtx0 vtx-a)) ;; #x222 - (vu-pair (sq.xyzw pos-b dest-b 3) (madday.xyzw ACC t-mtx1 vtx-a)) ;; #x223 - (vu-pair (ibeq code-b area-sign store-obj-0-and-resume-c1) (maddz.xyzw vtx-a t-mtx2 vtx-a)) ;; #x224 - (vu-pair (mtir code-b clr-b.w) (mul.xyz pos-b vtx-b Q)) ;; #x225 - (vu-pair (sq.xyzw vf27 dest-b 0) (mul.xyzw stq-b-q stq-b Q)) ;; #x226 - (label strip-stage-e1) - (vu-pair (lqi.xyzw vtx-b vtx-ptr) (mul.xyz clr-b-lit clr-b inst-color)) ;; #x227 - (vu-pair (div Q vf00.w vtx-a.w) (nop)) ;; #x228 - (vu-pair (lqi.xyzw stq-b stq-ptr) (mulax.y ACC edge-0 edge-1)) ;; #x229 - (vu-pair (mtir dest-b slot-b.w) (msubax.y ACC edge-1 edge-0)) ;; #x22a - (vu-pair (lqi.xyzw clr-b clr-ptr) (sub.xyzw edge-0 pos-a pos-b)) ;; #x22b - (vu-pair (sq.xyzw stq-a-q dest-a 1) (ftoi4.xyz pos-a pos-a)) ;; #x22c - (vu-pair (sq.xyzw clr-a-lit dest-a 2) (add.xyzw slot-b vtx-b gifbuf)) ;; #x22d - (vu-pair (fsand area-sign 0x2) (mulaw.xyzw ACC t-mtx3 vf00)) ;; #x22e - (vu-pair (mtir vi10 kick-base.x) (maddax.xyzw ACC t-mtx0 vtx-b)) ;; #x22f - (vu-pair (sq.xyzw pos-a dest-a 3) (madday.xyzw ACC t-mtx1 vtx-b)) ;; #x230 - (vu-pair (lqi.xyzw inst-color matrix-ptr) (mulz.w pos-a t-mtx3 view-consts)) ;; #x231 - (vu-pair (ibeq code-a area-sign store-obj-0-and-resume-c2) (maddz.xyzw vtx-b t-mtx2 vtx-b)) ;; #x232 - (vu-pair (mtir code-a clr-a.w) (mul.xyz pos-a vtx-a Q)) ;; #x233 - (vu-pair (sq.xyzw vf27 dest-a 0) (mul.xyzw stq-a-q stq-a Q)) ;; #x234 - (label strip-stage-e2) - (vu-pair (lqi.xyzw vtx-a vtx-ptr) (mul.xyz clr-a-lit clr-a inst-color)) ;; #x235 - (vu-pair (div Q vf00.w vtx-b.w) (mulax.y ACC edge-0 edge-1)) ;; #x236 - (vu-pair (lqi.xyzw stq-a stq-ptr) (addw.w pos-a view-consts pos-a)) ;; #x237 - (vu-pair (mtir dest-a slot-a.w) (msubax.y ACC edge-1 edge-0)) ;; #x238 - (vu-pair (lqi.xyzw clr-a clr-ptr) (sub.xyzw edge-1 pos-b pos-a)) ;; #x239 - (vu-pair (sq.xyzw stq-b-q dest-b 1) (ftoi4.xyz pos-b pos-b)) ;; #x23a - (vu-pair (sq.xyzw clr-b-lit dest-b 2) (add.xyzw slot-a vtx-a gifbuf)) ;; #x23b - (vu-pair (fsand area-sign 0x2) (mulaw.xyzw ACC t-mtx3 vf00)) ;; #x23c - (vu-pair (iaddi inst-count inst-count -0x1) (maddax.xyzw ACC t-mtx0 vtx-a)) ;; #x23d - (vu-pair (sq.xyzw pos-b dest-b 3) (madday.xyzw ACC t-mtx1 vtx-a)) ;; #x23e - (vu-pair (mtir kick-addr gifbuf.x) (miniy.w pos-a pos-a fog-clamp)) ;; #x23f - (vu-pair (ibeq code-b area-sign store-obj-0-and-resume-c3) (maddz.xyzw vtx-a t-mtx2 vtx-a)) ;; #x240 - (vu-pair (mtir code-b clr-b.w) (mul.xyz pos-b vtx-b Q)) ;; #x241 - (vu-pair (sq.xyzw vf27 dest-b 0) (mul.xyzw stq-b-q stq-b Q)) ;; #x242 - (label kick-final-buffer) - (vu-pair (move.w clr-a-lit inst-color) (addy.x kick-base vf00 kick-base)) ;; #x243 - (vu-pair (move.w clr-b-lit inst-color) (nop)) ;; #x244 - (vu-pair (iadd kick-addr kick-addr vi10) (nop)) ;; #x245 - (vu-pair (ibne vi00 inst-count vertex-pipe-a-even) (ftoi4.w pos-b pos-a)) ;; #x246 - (vu-pair (xgkick kick-addr) (ftoi4.w pos-a pos-a)) ;; #x247 - (vu-pair (nop) (nop :e)) ;; #x248 - (vu-pair (nop) (nop)) ;; #x249 - (label store-obj-1-and-resume-d0) - (vu-pair (b strip-stage-d0) (nop)) ;; #x24a - (vu-pair (sq.xyzw vf24 dest-b 0) (mul.xyzw stq-b-q stq-b Q)) ;; #x24b - (label store-obj-1-and-resume-d1) - (vu-pair (b strip-stage-d1) (nop)) ;; #x24c - (vu-pair (sq.xyzw vf24 dest-a 0) (mul.xyzw stq-a-q stq-a Q)) ;; #x24d - (label store-obj-1-and-resume-d2) - (vu-pair (b strip-stage-d2) (nop)) ;; #x24e - (vu-pair (sq.xyzw vf24 dest-b 0) (mul.xyzw stq-b-q stq-b Q)) ;; #x24f - (label store-obj-1-and-kick-second) - (vu-pair (b kick-second-buffer) (nop)) ;; #x250 - (vu-pair (sq.xyzw vf24 dest-a 0) (mul.xyzw stq-a-q stq-a Q)) ;; #x251 - (label store-obj-1-and-resume-e0) - (vu-pair (b strip-stage-e0) (nop)) ;; #x252 - (vu-pair (sq.xyzw vf24 dest-a 0) (mul.xyzw stq-a-q stq-a Q)) ;; #x253 - (label store-obj-1-and-resume-e1) - (vu-pair (b strip-stage-e1) (nop)) ;; #x254 - (vu-pair (sq.xyzw vf24 dest-b 0) (mul.xyzw stq-b-q stq-b Q)) ;; #x255 - (label store-obj-1-and-resume-e2) - (vu-pair (b strip-stage-e2) (nop)) ;; #x256 - (vu-pair (sq.xyzw vf24 dest-a 0) (mul.xyzw stq-a-q stq-a Q)) ;; #x257 - (label store-obj-1-and-kick-final) - (vu-pair (b kick-final-buffer) (nop)) ;; #x258 - (vu-pair (sq.xyzw vf24 dest-b 0) (mul.xyzw stq-b-q stq-b Q)) ;; #x259 - (label store-obj-0-and-resume-a2) - (vu-pair (b strip-stage-a2) (nop)) ;; #x25a - (vu-pair (sq.xyzw vf23 dest-b 0) (mul.xyzw stq-b-q stq-b Q)) ;; #x25b - (label store-obj-0-and-resume-a3) - (vu-pair (b strip-stage-a3) (nop)) ;; #x25c - (vu-pair (sq.xyzw vf23 dest-a 0) (mul.xyzw stq-a-q stq-a Q)) ;; #x25d - (label store-obj-0-and-resume-b0) - (vu-pair (b strip-stage-b0) (nop)) ;; #x25e - (vu-pair (sq.xyzw vf23 dest-b 0) (mul.xyzw stq-b-q stq-b Q)) ;; #x25f - (label store-obj-0-and-resume-b1) - (vu-pair (b strip-stage-b1) (nop)) ;; #x260 - (vu-pair (sq.xyzw vf23 dest-a 0) (mul.xyzw stq-a-q stq-a Q)) ;; #x261 - (label store-obj-0-and-resume-c0) - (vu-pair (b strip-stage-c0) (nop)) ;; #x262 - (vu-pair (sq.xyzw vf23 dest-a 0) (mul.xyzw stq-a-q stq-a Q)) ;; #x263 - (label store-obj-0-and-resume-c1) - (vu-pair (b strip-stage-c1) (nop)) ;; #x264 - (vu-pair (sq.xyzw vf23 dest-b 0) (mul.xyzw stq-b-q stq-b Q)) ;; #x265 - (label store-obj-0-and-resume-c2) - (vu-pair (b strip-stage-c2) (nop)) ;; #x266 - (vu-pair (sq.xyzw vf23 dest-a 0) (mul.xyzw stq-a-q stq-a Q)) ;; #x267 - (label store-obj-0-and-resume-c3) - (vu-pair (b strip-stage-c3) (nop)) ;; #x268 - (vu-pair (sq.xyzw vf23 dest-b 0) (mul.xyzw stq-b-q stq-b Q)) ;; #x269 - ))) - (defun shrub-num-tris ((shrub shrubbery)) "Return the number of triangles encoded by shrub: display vertices minus two strip-start vertices for each triangle strip." @@ -1104,13 +242,7 @@ ;; leaves that integer in the low mantissa bits, which is how the program moves VU addresses in ;; and out of float lanes all through the draw entry. (set! (-> view-data texture-giftag tag) (new 'static 'gif-tag64 :nloop #x1 :nreg #x4)) - (set! (-> view-data texture-giftag regs) - (new 'static - 'gif-tag-regs - :regs0 (gif-reg-id a+d) - :regs1 (gif-reg-id a+d) - :regs2 (gif-reg-id a+d) - :regs3 (gif-reg-id a+d))) + (set! (-> view-data texture-giftag regs) (gs-reg-list a+d a+d a+d a+d)) (set! (-> view-data texture-giftag word 3) (the-as uint #x40a00000)) (set! (-> view-data tex-start-ptr) (the-as int 25167696.0)) (set! (-> view-data mtx-buf-ptr) (the-as int 8388608.0)) @@ -1142,6 +274,8 @@ (* (+ (* inner-factor 2) 15 (* 5 factor-c) (* 13 factor-a)) repeat-count) 53)) +(define shrub-vu1-block (new 'static 'vu-function :length #x0 :qlength #x0)) + (defun shrub-do-init-frame ((dma-buf dma-buffer)) "Upload and initialize the shrub VU1 program and install its VIF row, column, and mask state." ;; Upload the program and the three quadwords of camera-dependent view constants. @@ -1149,26 +283,23 @@ (shrub-upload-view-data dma-buf) ;; Run entry zero, then wait for it: everything after this depends on the buffer-address vectors ;; it builds, and the unpack state installed below must not race the microprogram. - (let* ((dma-state dma-buf) - (init-packet (the-as object (-> dma-state base)))) - (set! (-> (the-as dma-packet init-packet) dma) (new 'static 'dma-tag :id (dma-tag-id cnt))) - (set! (-> (the-as dma-packet init-packet) vif0) - (new 'static 'vif-tag :cmd (vif-cmd mscalf) :msk #x1 :imm SHRUB-VU-ENTRY-INIT)) - (set! (-> (the-as dma-packet init-packet) vif1) (new 'static 'vif-tag :cmd (vif-cmd flushe) :msk #x1)) - (set! (-> dma-state base) (&+ (the-as pointer init-packet) 16))) + (dma-buffer-add-cnt-vif2 dma-buf + 0 + (new 'static 'vif-tag :cmd (vif-cmd mscalf) :msk #x1 :imm #x0) + (new 'static 'vif-tag :cmd (vif-cmd flushe) :msk #x1)) (let* ((dma-state dma-buf) (vif-state-packet (the-as object (-> dma-state base)))) (set! (-> (the-as dma-packet vif-state-packet) dma) (new 'static 'dma-tag :qwc #x3 :id (dma-tag-id cnt))) (set! (-> (the-as dma-packet vif-state-packet) vif0) (new 'static 'vif-tag)) (set! (-> (the-as dma-packet vif-state-packet) vif1) (new 'static 'vif-tag)) (set! (-> dma-state base) (&+ (the-as pointer vif-state-packet) 16))) + (let ((vif-state (-> dma-buf base))) ;; Unpack state for the packed model streams, installed once per pass because the streams ;; themselves are static and carry only their own unpack codes. MASK is 0xa0a0a0a0: two bits per ;; component per cycle, selecting COL for the z and w of every unpacked quadword and leaving x and ;; y to the data. COL is 4096, which is 1.0 in the 20.12 format the microprogram converts from, so ;; a two-component texture coordinate arrives as (s t 1.0 1.0) and the perspective-correct q falls ;; out of the same multiply as s and t. ROW's 0x8080 biases the components that come from it. - (let ((vif-state (-> dma-buf base))) (set! (-> (the-as (pointer vif-tag) vif-state) 0) (new 'static 'vif-tag :cmd (vif-cmd strow) :msk #x1)) (set! (-> (the-as (pointer uint32) vif-state) 1) (the-as uint #x8080)) (set! (-> (the-as (pointer uint32) vif-state) 2) (the-as uint #x8080)) @@ -1187,20 +318,14 @@ (defun shrub-init-frame ((dma-buf dma-buffer) (test gs-test)) "Initialize the shrub VU1 program, VIF state, and GS TEST register in dma-buf." - ;; Initialize VU1 and its persistent VIF state before changing the GS state for this pass. (shrub-do-init-frame dma-buf) - ;; Send one A+D write for TEST_1. (let* ((dma-state dma-buf) (direct-packet (the-as object (-> dma-state base)))) (set! (-> (the-as dma-packet direct-packet) dma) (new 'static 'dma-tag :qwc #x2 :id (dma-tag-id cnt))) (set! (-> (the-as dma-packet direct-packet) vif0) (new 'static 'vif-tag)) (set! (-> (the-as dma-packet direct-packet) vif1) (new 'static 'vif-tag :imm #x2 :cmd (vif-cmd direct) :msk #x1)) (set! (-> dma-state base) (the-as pointer (the-as gs-gif-tag (&+ (the-as pointer direct-packet) 16))))) - (let* ((dma-state dma-buf) - (giftag (the-as object (-> dma-state base)))) - (set! (-> (the-as gs-gif-tag giftag) tag) (new 'static 'gif-tag64 :nloop #x1 :eop #x1 :nreg #x1)) - (set! (-> (the-as gs-gif-tag giftag) regs) GIF_REGS_ALL_AD) - (set! (-> dma-state base) (&+ (the-as pointer giftag) 16))) + (dma-buffer-add-gif-tag dma-buf (new 'static 'gif-tag64 :nloop #x1 :eop #x1 :nreg #x1) GIF_REGS_ALL_AD) (let ((test-packet (-> dma-buf base))) (set! (-> (the-as (pointer gs-test) test-packet) 0) test) (set! (-> (the-as (pointer gs-reg64) test-packet) 1) (gs-reg64 test-1)) @@ -1231,1152 +356,13 @@ ;; and start-bank in shrub-work.gc says which one this ring position needs. (cond ((= start-bank 1) - (let* ((dma-state dma-buf) - (run-packet (the-as object (-> dma-state base)))) - (set! (-> (the-as dma-packet run-packet) dma) (new 'static 'dma-tag :id (dma-tag-id cnt))) - (set! (-> (the-as dma-packet run-packet) vif0) (new 'static 'vif-tag)) - (set! (-> (the-as dma-packet run-packet) vif1) (new 'static 'vif-tag :cmd (vif-cmd mscal) :msk #x1 :imm SHRUB-VU-ENTRY-INIT-MODEL-A)) - (set! (-> dma-state base) (&+ (the-as pointer run-packet) 16)))) + (dma-buffer-add-cnt-vif2 dma-buf 0 (new 'static 'vif-tag) (new 'static 'vif-tag :cmd (vif-cmd mscal) :msk #x1 :imm #x11))) (else - (let* ((dma-state dma-buf) - (run-packet (the-as object (-> dma-state base)))) - (set! (-> (the-as dma-packet run-packet) dma) (new 'static 'dma-tag :id (dma-tag-id cnt))) - (set! (-> (the-as dma-packet run-packet) vif0) (new 'static 'vif-tag)) - (set! (-> (the-as dma-packet run-packet) vif1) (new 'static 'vif-tag :cmd (vif-cmd mscal) :msk #x1 :imm SHRUB-VU-ENTRY-INIT-MODEL-B)) - (set! (-> dma-state base) (&+ (the-as pointer run-packet) 16))))) + (dma-buffer-add-cnt-vif2 dma-buf 0 (new 'static 'vif-tag) (new 'static 'vif-tag :cmd (vif-cmd mscal) :msk #x1 :imm #x15)))) ;; Hand the other bank to the next model. (set! *shrub-state* (- SHRUB-VU-MODEL-BANK-SUM *shrub-state*)) #f) -(#unless PC_PORT - (defun draw-inline-array-instance-shrub ((dma-buf dma-buffer) (nodes drawable) (node-count int) (buckets (inline-array prototype-bucket-shrub))) - "Walk node-count shrub BVH roots, cull them, classify every surviving leaf instance by prototype - and camera distance, and append its record to that prototype's near, opaque, translucent or - billboard chain. Instance records arrive through the scratchpad and the output packets are - assembled there too, so both transfers and the EE run at once. VU0 does the sphere-versus-plane - test, the camera depth, the instance transform and the wind spring." - (declare (asm-func none)) - ;; The function never reads an instance or writes a packet in main memory if it can avoid it. - ;; Instances come in through two 5200-byte scratchpad banks, one being filled by the toSPR - ;; channel while the EE reads the other; finished packets go out of two 2048-byte banks, each - ;; handed to the fromSPR channel as soon as it is within one record of full. The node walk drives - ;; the input side: every time it collects a batch of leaf spans it writes a small DMA tag chain - ;; describing them and starts a chain-mode transfer, so the instances for the next batch are on - ;; their way while the current batch is being classified. - ;; - ;; Field accesses go through typed base registers, so instance-shrub-work, the prototype bucket, - ;; the leaf record, the wind state and the two DMA channel register banks all read as fields. - ;; Numeric displacements are left only where the address really is untyped: the packet layouts - ;; being assembled inside an output bank. - ;; - ;; VU0 state, established by background-upload-vu0 and by the entries called below: - ;; vf1 1.0 in every lane - ;; vf2 the point handed to VU0 -- a node or instance bounding sphere - ;; vf4/vf6/vf7 entry 17 and 33 results: plane distances and camera depth - ;; vf16..vf19 the background camera matrix - ;; vf20..vf31 the shrub work area's planes, guard planes, hvdf offset and perspective rows - ;; vi1 the reject flags VU0 leaves behind - (rlet ((work :reg t0 :type instance-shrub-work) - (memory-cursor :reg v1) - (output-bank :reg t1) - (packet-cursor :reg t3) - (packet-qwc :reg a3) - (wind-work :reg t2 :type wind-work) - (hashed-wind :reg t7 :type wind-work) - (from-spr-channel :reg a0 :type dma-bank-spr) - (to-spr-channel :reg t4 :type dma-bank-spr) - (node-stack :reg t4 :type instance-shrub-work) - (bucket :reg t4 :type prototype-bucket-shrub) - (near-geometry :reg t4 :type drawable-group) - (cached-bucket :reg a1) - (prev-bucket :reg a1 :type prototype-bucket-shrub) - (instance :reg a2) - (chain-cursor :reg a2) - (wind-state :reg a2 :type wind-vector) - (instance-bucket :reg a2 :type prototype-bucket-shrub) - (node :reg t8 :type draw-node) - (fragment :reg t8 :type generic-shrub-fragment) - (near-packet :reg t9) - (scratch-base :reg t3) - (instance-bank :reg t6) - (dma-buf :reg a0 :type dma-buffer) - (nodes :reg a1) - (node-count :reg a2) - (buckets :reg a3)) - ;; Almost nothing here is arithmetic; it is the addresses the rest of the function runs on. - ;; - ;; Both scratchpad DMA channel register banks share the upper half of their address with the - ;; scratchpad bank bases, so two lui instructions cover four addresses. t5 first carries the - ;; toSPR channel address and is then reused for one of the node chain buffers; t7, t9, gp and - ;; ra are short-lived temporaries throughout the function and keep their physical spellings. - ;; - ;; The two node chain buffers are the surprising part. They are 128-byte staging areas for the - ;; DMA tags that fetch instance records, and they must be cache-line aligned because the EE - ;; writes them through the cache and then hands them to the DMAC -- hence the AND with -64. The - ;; three dummy quadwords in front of chaina exist to give that rounding somewhere to land. - (asm-block setup-instance-walk - (label initialize-instance-walk) - (add.i sp sp -32) - (s.d ra sp) - (s.q gp sp 16) - (lui scratch-base #x7000) - (l.w memory-cursor (-> dma-buf base)) - (lui t2 #x1000) - (lui output-bank #x1000) - ;; Write back and invalidate the two cache ways covering the DMA cursor before the - ;; fromSPR channel starts writing behind the EE's back. - (sync.l) - (cache dxwbin memory-cursor 0) - (sync.l) - (cache dxwbin memory-cursor 1) - (sync.l) - (m! work *instance-shrub-work*) - (ori t5 t2 #xd400) - (s.w dma-buf (-> work dma-buffer)) - (ori from-spr-channel output-bank #xd000) - (m! wind-work *wind-work*) - (ori output-bank scratch-base (spr-offset instance-shrub-dma outa)) - (s.w r0 (-> work chains)) - (m node-stack work) - (l.vf vf3 (-> work constants)) - (s.w t5 (-> work to-spr)) - (ori instance-bank scratch-base (spr-offset instance-shrub-dma instancea)) - ;; The near-packet cursor starts at the last of the six and counts down, so the - ;; first near shrub of the frame uses a packet no earlier chain can still name. - (add.i t7 r0 (* 5 (type-size shrub-near-packet))) - (s.w buckets (-> work prototypes)) - (add.i packet-cursor r0 0) - (s.w buckets (-> work bucket-ptr)) - (add.i packet-qwc r0 0) - (s.w nodes (-> node-stack node 0)) - (m packet-cursor output-bank) - (s.w node-count (-> node-stack length 0)) - (add.i cached-bucket r0 -1) - (s.w t7 (-> work current-shrub-near-packet)) - (add.i t7 work (offset-of instance-shrub-work chaina)) - (s.w instance-bank (-> work src-ptr)) - (add.i a2 work (offset-of instance-shrub-work chainb)) - (s.w instance-bank (-> work instance-ptr)) - (add.i t6 r0 -64) - (s.w t5 (-> work to-spr)) - (and t5 t7 t6) - (s.w from-spr-channel (-> work from-spr)) - (and a2 a2 t6) - (s.w t5 (-> work chain-ptr)) - (add.i t5 r0 -1) - (s.w a2 (-> work chain-ptr-next)) - (nop!) - (s.w t4 (-> work stack-ptr)) - (nop!) - (s.w t5 (-> work last-shrubs)) - (nop!) - (s.w r0 (-> work flags)) - (nop!) - (s.w r0 (-> work inst-count)) - (nop!) - (s.w r0 (-> work node-count))) - ;; Walk the BVH with an explicit stack, because a shrub tree has no fixed depth and no fixed - ;; child count. work.node and work.length are six-entry arrays a constant distance apart, so - ;; one cursor addresses both and its distance from the work area is the current depth. Seven - ;; levels is the limit, which is what t5 counts down. - ;; - ;; VU0 entry 17 turns a node's bounding sphere into four side-plane distances and a camera - ;; depth. Any negative plane distance rejects the whole subtree, and so does a negative depth. - ;; Otherwise a node either descends -- push its child span -- or, if its children are leaves, - ;; contributes one ref tag to the chain being built, with the leaf count stashed in the tag's - ;; VIF word where the scratchpad transfer will never look at it. - (asm-block walk-node-stack - (label scan-node-stack) - (callms 17) - (l.w t4 (-> work stack-ptr)) - (add.i t5 r0 7) - (l.w a2 (-> work chain-ptr)) - (nop!) - (nop!) - (nop!) - (nop!) - (nop!) - (nop!) - (nop!) - (nop!) - (nop!) - (nop!) - (label test-node) - (sub t7 t4 work) - (l.w t6 (-> node-stack length 0)) - (b.lt t7 r0 finish-node-stack-pass :delay (l.w node (-> node-stack node 0))) - (b.z t6 skip-empty-node :delay (l.vf vf2 (-> node bsphere))) - (nop!) - (l.vf vf6 node -4) - (vmulax.xyzw acc vf16 vf2) - (l.bu t6 (-> node flags)) - (vmadday.xyzw acc vf17 vf2) - (l.w t7 (-> node child)) - (vmaddaz.xyzw acc vf18 vf2) - (l.bu node (-> node child-count)) - (vmsubaw.xyzw acc vf19 vf0) - (l.q t9 (-> work dma-ref)) - (vmaddw.xyzw vf4 vf1 vf2) - (s.w t7 (-> node-stack node 1)) - (vmulaw.xyzw acc vf1 vf6) - (s.w node (-> node-stack length 1)) - (vmsubax.xyzw acc vf24 vf2) - (s.q t9 chain-cursor) - (vmsubay.xyzw acc vf25 vf2) - (add.i t9 t7 -4) - (vmsubaz.xyzw acc vf26 vf2) - (sll t7 t8 2) - (m ra vf4) - (add t7 t7 t8) - (vmsubaw.xyzw acc vf27 vf0) - (s.w t9 chain-cursor 4) - (vmaddw.xyzw vf7 vf1 vf2) - (s.w node chain-cursor (offset-of dma-packet vif0)) - (pcgtw t8 r0 ra) - (l.w t9 (-> node-stack length 0)) - (ppach ra r0 t8) - (l.w node (-> node-stack node 0)) - (b.nz ra reject-or-finish-node :delay (s.b t7 chain-cursor)) - (nop!) - (nop!) - (add.i t7 t9 -1) - (m t9 vf7) - (add.i t8 t8 32) - (nop!) - (b.lt t9 r0 reject-or-finish-node :delay (nop!)) - (b.z t6 descend-visible-node :delay (nop!)) - (b test-node :delay (add.i t4 t4 4)) - (label descend-visible-node) - (add.i a2 a2 16) - (s.w t7 (-> node-stack length 0)) - (add.i t5 t5 -1) - (s.w node (-> node-stack node 0)) - (b.le t5 r0 finish-node-stack-pass :delay (sub t6 t4 work)) - (b.gt t7 r0 test-node :delay (nop!)) - (b.le t6 r0 finish-node-stack-pass :delay (add.i t4 t4 -4)) - (label reject-or-finish-node) - (nop!) - (l.w t7 (-> node-stack length 0)) - (nop!) - (l.w t6 (-> node-stack node 0)) - (add.i t7 t7 -1) - (sub t8 t4 work) - (add.i t6 t6 32) - (s.w t7 (-> node-stack length 0)) - (b.gt t7 r0 test-node :delay (s.w t6 (-> node-stack node 0))) - (b.le t8 r0 finish-node-stack-pass :delay (nop!)) - (label skip-empty-node) - (b reject-or-finish-node :delay (add.i t4 t4 -4))) - ;; Hand the finished node chain to the toSPR channel and start filling the other one. The chain - ;; is a run of ref tags plus a terminating end tag, sent in chain mode with tag transfer - ;; enabled: each tag is copied into the scratchpad ahead of the instances it fetches, which is - ;; how the leaf count travels with the data and why the instance cursor starts one quadword into - ;; the bank. The two cache lines holding the chain are written back first, because the EE built - ;; it through the cache and the DMAC will read main memory. - (asm-block flush-node-chain - (label finish-node-stack-pass) - (nop!) - (s.w t4 (-> work stack-ptr)) - (nop!) - (l.w t5 (-> work chain-ptr)) - (nop!) - (l.w t4 (-> work to-spr)) - (b.eq t5 a2 finish-node-walk :delay (l.q t5 (-> work dma-end))) - (dma-wait-counting-stalls! t6 to-spr-channel (-> work wait-to-spr) - wait-to-scratchpad swap-node-chains-and-start-dma) - (label swap-node-chains-and-start-dma) - (nop!) - (l.w t6 (-> work chains)) - (nop!) - (s.q t5 chain-cursor) - (l.w t5 (-> work chain-ptr)) - (add.i a2 r0 DMA-CHCR-CHAIN-TTE-START) - (l.w t7 (-> work chain-ptr-next)) - (ori t8 r0 #xffff) - (s.w t5 (-> work chain-ptr-next)) - (add.i t6 t6 1) - (s.w t7 (-> work chain-ptr)) - (m t7 t5) - (nop!) - (s.w t6 (-> work chains)) - (nop!) - (l.w t6 (-> work instance-ptr)) - (sync.l) - (cache dxwbin t7 0) - (sync.l) - (cache dxwbin t7 1) - (sync.l) - (add.i t7 t7 64) - (sync.l) - (cache dxwbin t7 0) - (sync.l) - (cache dxwbin t7 1) - (sync.l) - (s.w t6 (-> to-spr-channel sadr)) - (s.w t5 (-> to-spr-channel tadr)) - (xor.i t5 t6 SHRUB-INSTANCE-INPUT-FLIP) - (s.w r0 (-> to-spr-channel qwc)) - (sync.l) - (s.w a2 (-> to-spr-channel chcr)) - (sync.l) - (nop!) - (s.w t5 (-> work src-ptr)) - (b choose-next-pass :delay (s.w t5 (-> work instance-ptr))) - (label finish-node-walk) - (b.lt cached-bucket r0 done :delay (l.w a2 (-> work instance-ptr))) - (nop!) - (s.w r0 (-> work last-shrubs)) - (nop!) - (xor.i a2 a2 SHRUB-INSTANCE-INPUT-FLIP) - (nop!) - (s.w a2 (-> work src-ptr)) - (nop!) - (s.w a2 (-> work instance-ptr)) - (dma-wait-counting-stalls! a2 to-spr-channel (-> work wait-to-spr) - wait-for-final-node-dma choose-next-pass) - (label choose-next-pass) - (b.ge cached-bucket r0 finish-instance-pass :delay (l.w a2 (-> work src-ptr))) - (b scan-node-stack :delay (add.i cached-bucket r0 #x2710))) - ;; One leaf. Return the output bank first if this instance could overflow it, then let VU0 entry - ;; 33 test the instance's own bounding sphere and produce its camera depth. Consecutive - ;; instances usually share a prototype, so the bucket's four list heads live in the work area - ;; and are written back only when the bucket index changes -- cached-bucket holds the index they - ;; belong to, with 10000 meaning "nothing cached yet" and -1, before the first pass, "no - ;; instance has been looked at at all". - (asm-block load-next-instance - (label load-instance) - (add.i t4 packet-qwc (- SHRUB-OUTPUT-FLUSH-AT)) - (l.vf vf2 instance (offset-of instance-shrubbery bsphere)) - (b.le t4 r0 transform-instance :delay (l.bu t4 instance (offset-of instance-shrubbery bucket-index))) - (nop!) - (l.w from-spr-channel (-> work from-spr)) - (nop!) - (nop!) - (dma-wait-counting-stalls! packet-cursor from-spr-channel (-> work wait-from-spr) - wait-for-instance-upload upload-instance-bank) - (dma-return-output-bank! output-bank SHRUB-OUTPUT-FLIP packet-cursor packet-qwc memory-cursor from-spr-channel - upload-instance-bank) - (label transform-instance) - (callms 33) - (l.w t5 (-> work flags)) - (b.eq cached-bucket t4 classify-instance :delay (add.i t6 cached-bucket -10000)) - (b.z t6 prepare-instance-record :delay (l.w prev-bucket (-> work bucket-ptr))) - (nop!) - (l.q t5 (-> work last 0)) - (nop!) - (l.q t6 (-> work next 0)) - (nop!) - (l.q t7 (-> work count 0)) - (nop!) - (s.q t5 (-> prev-bucket last-clear)) - (nop!) - (s.q t6 (-> prev-bucket next-clear)) - (nop!) - (s.q t7 (-> prev-bucket count-clear-qword)) - (label prepare-instance-record) - (m cached-bucket t4) - (l.w t5 (-> work prototypes)) - (add.i t6 r0 112) - (s.q r0 (-> work last 0)) - (multu3 t4 t4 t6) - (s.q r0 (-> work next 0)) - (add t4 t5 t4) - (s.q r0 (-> work count 0)) - (nop!) - (s.w t4 (-> work bucket-ptr)) - (nop!) - (l.w t5 (-> bucket flags)) - (nop!) - (l.vf vf15 (-> bucket rdists)) - (and.i t5 t5 1) - (l.vf vf14 (-> bucket dists)) - (vmul.xyz vf15 vf15 vf3) - (s.w t5 (-> work flags))) - ;; Unpack the instance and decide what it becomes. - ;; - ;; The 4x4 origin is stored as sixteen 16-bit values; pextlh spreads each row into a quadword - ;; and the VU0 conversions turn the rotation rows into 20.12 floats and the translation into - ;; whole units. The color index in the leaf selects one RGBA from the tree's 1024-entry - ;; time-of-day table, and the byte spread and shift turn it into the four floats that scale - ;; every vertex of this instance. - ;; - ;; The distance work produces one number that decides everything: a fade running 0 to 128 - ;; across the band between this geometry and the next coarser one. 128 means the model has - ;; faded out completely, so the instance becomes a billboard and the wind path runs instead; - ;; below that the model is drawn with the fade in its alpha and 128 minus the fade is handed to - ;; the billboard, so the two crossfade. A prototype with no billboard geometry takes the flat - ;; path and is simply drawn. - (asm-block classify-instance - (label classify-instance) - (b.nz t5 advance-instance :delay (l.d t5 instance (offset-of instance-shrubbery origin data 12))) - (nop!) - (l.d t4 instance (offset-of instance-shrubbery origin data 0)) - (pextlh t5 t5 r0) - (l.d t6 instance (offset-of instance-shrubbery origin data 4)) - (sra.w t7 t5 10) - (l.d t5 instance (offset-of instance-shrubbery origin data 8)) - (pextlh t8 t4 r0) - (l.hu t4 instance (offset-of instance-shrubbery color-indices)) - (sra.w t8 t8 16) - (l.q t9 instance (offset-of instance-shrubbery flat-normal)) - (pextlh t6 t6 r0) - (m.ni vf13 t7) - (sra.w t6 t6 16) - (m.ni vf18 t9) - (pextlh t5 t5 r0) - (m.ni vf10 t8) - (sra.w t5 t5 16) - (m.ni vf11 t6) - (add t4 t4 work) - (m.ni vf12 t5) - (nop!) - (cfc2.i t5 vi1) - (vitof0.xyzw vf13 vf13) - (l.w t6 t4 (offset-of instance-shrub-work colors)) - (b.nz t5 advance-instance :delay (l.q t4 (-> work color-constant))) - (pextlb t5 r0 t6) - (l.vf vf4 (-> work hmge-d)) - (pextlh t5 r0 t5) - (l.vf vf25 (-> work min-dist)) - (vsub.xyzw vf9 vf6 vf14) - (nop!) - (sll.w t6 t5 8) - (mmi-nop!) - (add.w t4 t6 t4) - (mmi-nop!) - (vmula.xyzw acc vf1 vf3) - (nop!) - (vmsub.xyzw vf9 vf9 vf15) - (s.q t5 (-> work bb-color)) - (vadd.xyz vf13 vf13 vf2) - (s.q t4 (-> work color)) - (vsubw.xyzw vf8 vf6 vf2) - (nop!) - (vitof12.xyzw vf10 vf10) - (nop!) - (vmini.xyzw vf9 vf9 vf3) - (l.w t4 (-> work bucket-ptr)) - (vadd.xyz vf18 vf18 vf13) - (nop!) - (vmulax.xyzw acc vf28 vf13) - (l.w t4 (-> bucket geometry 3)) - (vmadday.xyzw acc vf29 vf13) - (nop!) - (vmaxx.xyzw vf9 vf9 vf0) - (nop!) - (vmaddaz.xyzw acc vf30 vf13) - (nop!) - (vmaddw.xyzw vf5 vf31 vf0) - (nop!) - (vitof12.xyzw vf11 vf11) - (nop!) - (ftoi.vf vf19 vf9) - (nop!) - (vmini.xyzw vf25 vf8 vf25) - (nop!) - (vsubz.xyzw vf4 vf8 vf4) - (add.i t5 r0 128) - (vitof12.xyzw vf12 vf12) - (add.i t6 r0 255) - (vmulw.y vf9 vf9 vf15) - (nop!) - (nop!) - (m t7 vf19) - (vdiv Q vf3.w vf5.w) - (nop!) - (and t6 t7 t6) - (nop!) - (sub t7 t5 t6) - (s.w t6 (-> work color w)) - (b.eq t5 t6 prepare-wind-and-billboard :delay (s.vf vf25 (-> work min-dist))) - (b.z t4 build-flat-instance :delay (s.w t7 (-> work bb-color w))) - (vmulax.xyzw acc vf28 vf18) - (l.q t4 (-> work billboard-tmpl)) - (vmadday.xyzw acc vf29 vf18) - (l.q t5 (-> work billboard-tmpl gif)) - (vmaddaz.xyzw acc vf30 vf18) - (l.w t6 (-> work last 3)) - (vmaddw.xyzw vf18 vf31 vf0) - (l.w t7 (-> work next 3)) - (sll t8 packet-qwc 4) - (l.vf vf8 (-> work hvdf-offset)) - (add t8 t8 memory-cursor) - (l.vf vf7 instance (offset-of instance-shrubbery flat-normal)) - (vmulaq.xyz acc vf5 Q) - (l.q a2 (-> work bb-color)) - (vmulaw.w acc vf5 vf0) - (movz t6 t8 t6) - (vmadd.xyzw vf5 vf1 vf8) - (l.hu t9 (-> work count 3)) - (vmulq.w vf19 vf7 Q) - (nop!) - (add.i t9 t9 1) - (l.vf vf6 (-> work billboard-const)) - (vmulq.xyzw vf26 vf1 Q) - (s.w t6 (-> work last 3)) - (vmulq.xyzw vf27 vf1 Q) - (s.w t8 (-> work next 3)) - (vnop) - (nop!) - (vmaxz.w vf5 vf5 vf6) - (s.h t9 (-> work count 3)) - (vdiv Q vf3.w vf18.w) - (nop!) - (vmulax.xyzw acc vf20 vf10) - (s.q t4 packet-cursor) - (vaddx.x vf26 vf0 vf0) - (s.q t5 packet-cursor 16) - (vminiw.w vf5 vf5 vf6) - (s.q a2 packet-cursor 48) - (vmadday.xyzw acc vf21 vf10) - (s.q a2 packet-cursor 96) - (vmaddz.xyzw vf10 vf22 vf10) - (s.q a2 packet-cursor 144) - (vmulaw.w acc vf18 vf0) - (s.q a2 packet-cursor 192) - (vmulaq.xyz acc vf18 Q) - (s.w t7 packet-cursor 4) - (vmadd.xyzw vf18 vf1 vf8) - (nop!) - (vmulq.w vf8 vf7 Q) - (nop!) - (vmulq.xyzw vf24 vf1 Q) - (nop!) - (vmulq.xyzw vf25 vf1 Q) - (nop!) - (vmaxz.w vf18 vf18 vf6) - (nop!) - (vadd.xy vf24 vf0 vf0) - (nop!) - (vaddy.y vf25 vf0 vf0) - (nop!) - (vmulax.xyzw acc vf20 vf11) - (nop!) - (vminiw.w vf18 vf18 vf6) - (nop!) - (vmadday.xyzw acc vf21 vf11) - (nop!) - (vmaddz.xyzw vf11 vf22 vf11) - (nop!) - (vmulax.xyzw acc vf20 vf12) - (nop!) - (vsub.xyzw vf16 vf18 vf5) - (nop!) - (vmadday.xyzw acc vf21 vf12) - (nop!) - (vmaddz.xyzw vf12 vf22 vf12) - (nop!) - (vmulax.xyzw acc vf20 vf13) - (nop!) - (vaddy.y vf16 vf16 vf16) - (nop!) - (vmadday.xyzw acc vf21 vf13) - (nop!) - (vmaddaz.xyzw acc vf22 vf13) - (nop!) - (vmaddw.xyzw vf13 vf23 vf0) - (nop!) - (vmul.xy vf17 vf16 vf16) - (nop!) - (nop!) - (s.vf vf24 packet-cursor 32) - (nop!) - (s.vf vf25 packet-cursor 80) - (nop!) - (s.vf vf26 packet-cursor 128) - (vaddy.x vf17 vf17 vf17) - (nop!) - (nop!) - (s.vf vf27 packet-cursor 176) - (vmulw.xyzw vf2 vf18 vf0) - (nop!) - (vmulw.xyzw vf4 vf18 vf0) - (nop!) - (vrsqrt Q vf0.w vf17.x) - (nop!) - (nop!) - (vwaitq) - (vmulq.xy vf17 vf16 Q) - (nop!) - (nop!) - (nop!) - (nop!) - (nop!) - (nop!) - (nop!) - (vsuby.x vf16 vf0 vf17) - (nop!) - (vaddx.y vf16 vf0 vf17) - (nop!) - (nop!) - (s.vf vf10 packet-cursor 240) - (nop!) - (s.vf vf11 packet-cursor 256) - (vmulw.xy vf8 vf16 vf8) - (nop!) - (vmulw.xy vf19 vf16 vf19) - (nop!) - (nop!) - (l.q a2 (-> work color)) - (nop!) - (nop!) - (vmul.xy vf8 vf8 vf6) - (nop!) - (vmul.xy vf19 vf19 vf6) - (nop!) - (vmulw.xyzw vf6 vf5 vf0) - (nop!) - (vmulw.xyzw vf7 vf5 vf0) - (s.q a2 packet-cursor 304) - (vadd.xy vf2 vf18 vf8) - (nop!) - (vsub.xy vf4 vf18 vf8) - (nop!) - (vadd.xy vf6 vf5 vf19) - (nop!) - (vsub.xy vf7 vf5 vf19) - (nop!) - (vftoi4.xyzw vf2 vf2) - (nop!) - (vftoi4.xyzw vf4 vf4) - (add.i packet-cursor packet-cursor 224) - (vftoi4.xyzw vf6 vf6) - (add.i packet-qwc packet-qwc 14) - (vftoi4.xyzw vf7 vf7) - (l.w a2 (-> work color w)) - (nop!) - (s.vf vf2 packet-cursor -160) - (nop!) - (s.vf vf4 packet-cursor -112) - (nop!) - (s.vf vf6 packet-cursor -64) - (b.z a2 advance-instance :delay (s.vf vf7 packet-cursor -16)) - (b append-first-lod-record :delay (nop!)) - (label build-flat-instance) - (b.z t6 advance-instance :delay (vmulax.xyzw acc vf20 vf10)) - (vmadday.xyzw acc vf21 vf10) - (l.q a2 (-> work color)) - (vmaddz.xyzw vf10 vf22 vf10) - (nop!) - (vmulax.xyzw acc vf20 vf11) - (nop!) - (vmadday.xyzw acc vf21 vf11) - (nop!) - (vmaddz.xyzw vf11 vf22 vf11) - (nop!) - (vmulax.xyzw acc vf20 vf12) - (nop!) - (vmadday.xyzw acc vf21 vf12) - (nop!) - (vmaddz.xyzw vf12 vf22 vf12) - (nop!) - (vmulax.xyzw acc vf20 vf13) - (nop!) - (vmadday.xyzw acc vf21 vf13) - (nop!) - (vmaddaz.xyzw acc vf22 vf13) - (nop!) - (vmaddw.xyzw vf13 vf23 vf0) - (s.q a2 packet-cursor 80) - (nop!) - (s.vf vf10 packet-cursor 16) - (nop!) - (s.vf vf11 packet-cursor 32) - (label append-first-lod-record) - (sll a2 packet-qwc 4) - (l.hu t4 (-> work mod-count 2)) - (add t5 a2 memory-cursor) - (l.hu t7 (-> work count 2)) - (sll t6 t4 4) - (l.w a2 (-> work next 2)) - (add t8 t6 work) - (l.w t6 (-> work last 2)) - (add.i t7 t7 1) - (l.q t8 t8 (offset-of instance-shrub-work matrix-tmpl)) - (add.i packet-qwc packet-qwc 6) - (s.h t7 (-> work count 2)) - (add.i t7 t4 1) - (s.q t8 packet-cursor) - (add.i t8 t7 -20) - (s.vf vf12 packet-cursor 48) - (movz t7 r0 t8) - (s.vf vf13 packet-cursor 64) - (add.i t8 t4 -10) - (s.h t7 (-> work mod-count 2)) - (add.i packet-cursor packet-cursor 96) - (s.w a2 packet-cursor -92) - (b.z t4 use-first-shared-matrix :delay (s.w t5 (-> work next 2))) - (b.nz t8 select-first-matrix :delay (nop!)) - (label use-first-shared-matrix) - (nop!) - (l.q t4 (-> work mscalf-tmpl)) - (nop!) - (l.q t7 (-> work mscalf-ret-tmpl)) - (nop!) - (s.w t5 (-> work last 2)) - (nop!) - (movz t4 t7 t6) - (add.i packet-qwc packet-qwc 1) - (s.q t4 packet-cursor) - (nop!) - (s.w a2 packet-cursor 4) - (b advance-instance :delay (add.i packet-cursor packet-cursor 16)) - (label select-first-matrix) - (add.i t5 t4 -9) - (nop!) - (b.z t5 use-first-indexed-matrix :delay (add.i t4 t4 -19)) - (b.nz t4 advance-instance :delay (nop!)) - (label use-first-indexed-matrix) - (nop!) - (sll t4 t7 4) - (nop!) - (add t4 t4 work) - (add.i packet-qwc packet-qwc 1) - (l.q t4 t4 (offset-of instance-shrub-work count-tmpl)) - (nop!) - (nop!) - (nop!) - (s.q t4 packet-cursor) - (nop!) - (s.w a2 packet-cursor 4) - (b advance-instance :delay (add.i packet-cursor packet-cursor 16))) - ;; Shear the instance into the wind, and if it has faded to a billboard, build the quad. - ;; - ;; Each instance owns two words of spring state in the level's wind-vectors array, indexed by - ;; the wind-index packed into its transform. The driving force is one of 64 shared wind vectors - ;; chosen by hashing that index against the current wind tick, exactly as - ;; wind-get-hashed-index does, so neighbouring bushes sway out of phase without anyone storing - ;; a phase. The spring is integrated here and written straight back, and its result shears the - ;; instance's rows. - ;; - ;; The billboard is a camera-facing rectangle built from the leaf's own flat-normal and - ;; flat-hwidth: the half-width along the screen right vector, the normal for up, four corners - ;; converted to 12.4 and written as a GS packet. The paused flag selects an alternate axis. - (asm-block shear-instance-into-wind - (label prepare-wind-and-billboard) - (nop!) - (l.w t4 (-> wind-work wind-time)) - (nop!) - (l.hu t5 instance (offset-of instance-shrubbery wind-index)) - (nop!) - (l.w a2 (-> work wind-vectors)) - (sll t6 t5 4) - (l.vf vf19 (-> work wind-const)) - (add a2 a2 t6) - (add t4 t5 t4) - (and.i t5 t4 63) - (l.d t4 (-> wind-state wind-vel)) - (sll t6 t5 4) - (l.d t5 (-> wind-state wind-pos)) - (add t7 t6 wind-work) - (m t6 vf4) - (pextlw t4 r0 t4) - (l.vf vf16 (-> hashed-wind wind-array 0)) - (pextlw t5 r0 t5) - (m vf18 t4) - (nop!) - (m vf17 t5) - (vmula.xyzw acc vf16 vf1) - (nop!) - (vmsubax.xyzw acc vf18 vf19) - (nop!) - (vmsuby.xyzw vf16 vf17 vf19) - (nop!) - (pcgtw t5 r0 t6) - (mmi-nop!) - (nop!) - (nop!) - (nop!) - (l.vf vf24 (-> work guard-plane 0)) - (vmulaz.xyzw acc vf16 vf19) - (nop!) - (vmadd.xyzw vf18 vf1 vf18) - (nop!) - (nop!) - (l.vf vf25 (-> work guard-plane 1)) - (nop!) - (l.vf vf26 (-> work guard-plane 2)) - (nop!) - (l.vf vf27 (-> work guard-plane 3)) - (vmulaz.xyzw acc vf18 vf19) - (nop!) - (vmadd.xyzw vf17 vf17 vf1) - (nop!) - (vmulax.xyzw acc vf24 vf2) - (nop!) - (vmadday.xyzw acc vf25 vf2) - (nop!) - (vmaddaz.xyzw acc vf26 vf2) - (nop!) - (vminiw.xyzw vf17 vf17 vf0) - (nop!) - (vmsubaw.xyzw acc vf27 vf0) - (nop!) - (vmsubw.xyzw vf24 vf1 vf2) - (nop!) - (nop!) - (m t4 vf18) - (vmaxw.xyzw vf27 vf17 vf19) - (nop!) - (ppacw t4 r0 t4) - (mmi-nop!) - (nop!) - (nop!) - (nop!) - (m t6 vf24) - (vmuly.xyzw vf27 vf27 vf9) - (nop!) - (pcgtw t6 r0 t6) - (mmi-nop!) - (ppach t6 r0 t6) - (mmi-nop!) - (vmulax.yw acc vf0 vf0) - (nop!) - (vmulay.xz acc vf27 vf10) - (nop!) - (vmadd.xyzw vf10 vf1 vf10) - (nop!) - (or t5 t6 t5) - (m t6 vf27) - (vmulax.yw acc vf0 vf0) - (l.w t7 (-> work paused)) - (vmulay.xz acc vf27 vf11) - (nop!) - (vmadd.xyzw vf11 vf1 vf11) - (nop!) - (b.ne t7 s7 use-alternate-billboard-axis :delay (ppacw t6 r0 t6)) - (vmulax.yw acc vf0 vf0) - (s.d t4 (-> wind-state wind-vel)) - (vmulay.xz acc vf27 vf12) - (s.d t6 (-> wind-state wind-pos)) - (b.nz t5 finish-billboard-instance :delay (vmadd.xyzw vf12 vf1 vf12)) - (b append-second-lod-record :delay (nop!)) - (label use-alternate-billboard-axis) - (vmulax.yw acc vf0 vf0) - (nop!) - (vmulay.xz acc vf27 vf12) - (nop!) - (b.nz t5 finish-billboard-instance :delay (vmadd.xyzw vf12 vf1 vf12))) - ;; The wind path's own record append: the same six quadwords as the flat path, but linked into - ;; the translucent geometry's chain rather than the opaque one, so a swaying shrub and a still - ;; one end up in different draws. - (asm-block append-billboard-instance - (label append-second-lod-record) - (vmulax.xyzw acc vf20 vf10) - (l.q a2 (-> work color)) - (vmadday.xyzw acc vf21 vf10) - (nop!) - (vmaddz.xyzw vf10 vf22 vf10) - (nop!) - (vmulax.xyzw acc vf20 vf11) - (nop!) - (vmadday.xyzw acc vf21 vf11) - (nop!) - (vmaddz.xyzw vf11 vf22 vf11) - (nop!) - (vmulax.xyzw acc vf20 vf12) - (nop!) - (vmadday.xyzw acc vf21 vf12) - (nop!) - (vmaddz.xyzw vf12 vf22 vf12) - (nop!) - (vmulax.xyzw acc vf20 vf13) - (nop!) - (vmadday.xyzw acc vf21 vf13) - (nop!) - (vmaddaz.xyzw acc vf22 vf13) - (nop!) - (vmaddw.xyzw vf13 vf23 vf0) - (s.q a2 packet-cursor 80) - (nop!) - (s.vf vf10 packet-cursor 16) - (nop!) - (s.vf vf11 packet-cursor 32) - (sll a2 packet-qwc 4) - (l.hu t4 (-> work mod-count 1)) - (add t5 a2 memory-cursor) - (l.hu t7 (-> work count 1)) - (sll t6 t4 4) - (l.w a2 (-> work next 1)) - (add t8 t6 work) - (l.w t6 (-> work last 1)) - (add.i t7 t7 1) - (l.q t8 t8 (offset-of instance-shrub-work matrix-tmpl)) - (add.i packet-qwc packet-qwc 6) - (s.h t7 (-> work count 1)) - (add.i t7 t4 1) - (s.q t8 packet-cursor) - (add.i t8 t7 -20) - (s.vf vf12 packet-cursor 48) - (movz t7 r0 t8) - (s.vf vf13 packet-cursor 64) - (add.i t8 t4 -10) - (s.h t7 (-> work mod-count 1)) - (add.i packet-cursor packet-cursor 96) - (s.w a2 packet-cursor -92) - (b.z t4 use-second-shared-matrix :delay (s.w t5 (-> work next 1))) - (b.nz t8 select-second-matrix :delay (nop!)) - (label use-second-shared-matrix) - (nop!) - (l.q t4 (-> work mscalf-tmpl)) - (nop!) - (l.q t7 (-> work mscalf-ret-tmpl)) - (nop!) - (s.w t5 (-> work last 1)) - (nop!) - (movz t4 t7 t6) - (add.i packet-qwc packet-qwc 1) - (s.q t4 packet-cursor) - (nop!) - (s.w a2 packet-cursor 4) - (b advance-instance :delay (add.i packet-cursor packet-cursor 16)) - (label select-second-matrix) - (add.i t5 t4 -9) - (nop!) - (b.z t5 use-second-indexed-matrix :delay (add.i t4 t4 -19)) - (b.nz t4 advance-instance :delay (nop!)) - (label use-second-indexed-matrix) - (nop!) - (sll t4 t7 4) - (nop!) - (add t4 t4 work) - (add.i packet-qwc packet-qwc 1) - (l.q t4 t4 (offset-of instance-shrub-work count-tmpl)) - (nop!) - (nop!) - (nop!) - (s.q t4 packet-cursor) - (nop!) - (s.w a2 packet-cursor 4) - (b advance-instance :delay (add.i packet-cursor packet-cursor 16))) - ;; A near shrub goes to the generic renderer, which wants a full matrix and one upload per - ;; fragment rather than a compact instance record. Each fragment gets one shrub-near-packet -- - ;; matrix, control stream, texture coordinates, colors, vertices, MSCAL -- patched from that - ;; fragment's own qwc and stream addresses and then copied into the output bank. The six - ;; templates are used round-robin because an earlier chain may still name the one just used, and - ;; every fragment rechecks the output bank, since a single instance can emit many of them. - (asm-block emit-near-instances - (label finish-billboard-instance) - (vmulax.xyzw acc vf28 vf10) - (l.vf vf24 (-> work bb-color)) - (vmadday.xyzw acc vf29 vf10) - (nop!) - (vmaddz.xyzw vf10 vf30 vf10) - (nop!) - (vmulax.xyzw acc vf28 vf11) - (nop!) - (vmadday.xyzw acc vf29 vf11) - (l.hu t4 (-> work near-count)) - (vmaddz.xyzw vf11 vf30 vf11) - (l.w a2 (-> work bucket-ptr)) - (vmulax.xyzw acc vf28 vf12) - (add.i t8 t4 1) - (vmadday.xyzw acc vf29 vf12) - (s.h t8 (-> work near-count)) - (vmaddz.xyzw vf12 vf30 vf12) - (l.w t4 (-> instance-bucket geometry 0)) - (vmulax.xyzw acc vf28 vf13) - (l.w t5 (-> work near-next)) - (vmadday.xyzw acc vf29 vf13) - (l.h t6 (-> near-geometry length)) - (vmaddaz.xyzw acc vf30 vf13) - (l.w a2 (-> work near-last)) - (vmaddw.xyzw vf13 vf31 vf0) - (l.w t7 (-> work current-shrub-near-packet)) - (vitof0.xyz vf24 vf24) - (s.h t8 (-> work count 0)) - (label refill-instance-bank) - (add.i t8 packet-qwc (- SHRUB-NEAR-OUTPUT-FLUSH-AT)) - (nop!) - (b.le t8 r0 advance-near-packet :delay (l.w fragment (-> near-geometry data 0))) - (dma-wait-counting-stalls! packet-cursor from-spr-channel (-> work wait-from-spr) - wait-for-instance-refill start-instance-refill) - (dma-return-output-bank! output-bank SHRUB-OUTPUT-FLIP packet-cursor packet-qwc memory-cursor from-spr-channel - start-instance-refill) - (label advance-near-packet) - (add t9 t7 work) - (add.i t7 t7 -144) - (add.i t4 t4 4) - (add.i t9 t9 (offset-of instance-shrub-work shrub-near-packets)) - (b.ge t7 r0 emit-near-instance :delay (l.q ra near-packet)) - (nop!) - (add.i t7 r0 720) - (label emit-near-instance) - ;; Near shrubs use the full matrix packet template. Rotate through the six near-packet slots so - ;; matrix uploads can be linked without overwriting a packet still referenced by the DMA chain. - (nop!) - (s.w t5 near-packet (shrub-near-tag-addr mscal-tmpl)) - (sll t5 packet-qwc 4) - (s.q ra packet-cursor) - (add t5 t5 memory-cursor) - (s.vf vf10 packet-cursor 16) - (movz a2 t5 a2) - (s.vf vf11 packet-cursor 32) - (add.i packet-qwc packet-qwc 12) - (s.vf vf12 packet-cursor 48) - (nop!) - (l.w ra (-> fragment vtx-cnt)) - (nop!) - (s.vf vf13 packet-cursor 64) - (nop!) - (s.vf vf24 packet-cursor 80) - (nop!) - (s.w ra packet-cursor 96) - (nop!) - (l.w ra (-> fragment cnt)) - (nop!) - (l.bu gp (-> fragment cnt-qwc)) - (nop!) - (s.w ra near-packet (shrub-near-tag-addr header-tmpl)) - (nop!) - (s.b gp near-packet (shrub-near-tag-qwc header-tmpl)) - (nop!) - (s.b gp near-packet (shrub-near-unpack-num header-tmpl)) - (nop!) - (l.w ra (-> fragment stq)) - (nop!) - (l.bu gp (-> fragment stq-qwc)) - (nop!) - (s.w ra near-packet (shrub-near-tag-addr stq-tmpl)) - (nop!) - (s.b gp near-packet (shrub-near-tag-qwc stq-tmpl)) - (nop!) - (l.w ra (-> fragment col)) - (nop!) - (l.bu gp (-> fragment col-qwc)) - (nop!) - (s.w ra near-packet (shrub-near-tag-addr color-tmpl)) - (nop!) - (s.b gp near-packet (shrub-near-tag-qwc color-tmpl)) - (nop!) - (l.w ra (-> fragment vtx)) - (nop!) - (l.bu gp (-> fragment vtx-qwc)) - (nop!) - (s.w ra near-packet (shrub-near-tag-addr vertex-tmpl)) - (nop!) - (s.b gp near-packet (shrub-near-tag-qwc vertex-tmpl)) - (nop!) - (l.w t8 (-> fragment vtx-cnt)) - (nop!) - (l.q ra near-packet (offset-of shrub-near-packet header-tmpl)) - (nop!) - (s.b t8 near-packet (shrub-near-unpack-num stq-tmpl)) - (nop!) - (s.b t8 near-packet (shrub-near-unpack-num color-tmpl)) - (nop!) - (s.b t8 near-packet (shrub-near-unpack-num vertex-tmpl)) - (nop!) - (s.q ra packet-cursor 112) - (nop!) - (l.q t8 near-packet (offset-of shrub-near-packet stq-tmpl)) - (nop!) - (l.q ra near-packet (offset-of shrub-near-packet color-tmpl)) - (nop!) - (s.q t8 packet-cursor 128) - (nop!) - (s.q ra packet-cursor 144) - (nop!) - (l.q t8 near-packet (offset-of shrub-near-packet vertex-tmpl)) - (nop!) - (l.q t9 near-packet (offset-of shrub-near-packet mscal-tmpl)) - (nop!) - (s.q t8 packet-cursor 160) - (add.i packet-cursor packet-cursor 192) - (s.q t9 packet-cursor -16) - (add.i t6 t6 -1) - (nop!) - (b.gt t6 r0 refill-instance-bank :delay (nop!)) - (nop!) - (s.w t7 (-> work current-shrub-near-packet)) - (lui t4 #x1000) - (s.w t5 (-> work near-next)) - (ori t4 t4 #xd400) - (s.w a2 (-> work near-last)) - (nop!) - (nop!)) - ;; VU0 entry 25 reloads the plane and camera registers the transform and wind code above - ;; borrowed, so the next leaf is tested with the same convention. Then step the leaf cursor by - ;; one instance-shrubbery and count down the run this scratchpad tag described. - (asm-block advance-instance - (label advance-instance) - (callms 25) - (l.w a2 (-> work src-ptr)) - (nop!) - (l.w t4 (-> work shrub-count)) - (add.i a2 a2 80) - (nop!) - (add.i t4 t4 -1) - (s.w a2 (-> work src-ptr)) - (b.gt t4 r0 load-instance :delay (s.w t4 (-> work shrub-count)))) - ;; A run is finished. Read the next scratchpad tag: a nonzero leaf count starts another run, and - ;; if the node walk left more work the whole pass repeats. Otherwise flush the cached bucket - ;; heads, return the last partial output bank, and wait for both directions of scratchpad - ;; traffic before publishing the advanced DMA-buffer cursor -- the fromSPR channel is still - ;; writing into that buffer, so the store has to be last. - ;; - ;; a1, a2, t2 and t3 are four anonymous quadword shuttles in this block, which is why their - ;; physical spellings come back here. - (asm-block finish-instance-pass - (label finish-instance-pass) - (nop!) - (l.w t4 chain-cursor (offset-of dma-packet vif0)) - (add.i a2 a2 16) - (l.w t5 (-> work last-shrubs)) - (nop!) - (s.w a2 (-> work src-ptr)) - (b.nz t4 load-instance :delay (s.w t4 (-> work shrub-count))) - (b.nz t5 scan-node-stack :delay (nop!)) - (nop!) - (l.w prev-bucket (-> work bucket-ptr)) - (nop!) - (l.q a2 (-> work last 0)) - (nop!) - (l.q t2 (-> work next 0)) - (nop!) - (l.q t3 (-> work count 0)) - (nop!) - (s.q a2 (-> prev-bucket last-clear)) - (nop!) - (s.q t2 (-> prev-bucket next-clear)) - (nop!) - (s.q t3 (-> prev-bucket count-clear-qword)) - (b.z packet-qwc wait-for-output-dma :delay (nop!)) - (nop!) - (l.w from-spr-channel (-> work from-spr)) - (nop!) - (nop!) - (dma-wait-counting-stalls! a1 from-spr-channel (-> work wait-from-spr) - wait-for-final-instance-upload upload-final-instance-bank) - (label upload-final-instance-bank) - (s.w memory-cursor (-> from-spr-channel madr)) - (sll a1 packet-qwc 4) - (s.w output-bank (-> from-spr-channel sadr)) - (xor.i a2 output-bank SHRUB-OUTPUT-FLIP) - (add memory-cursor memory-cursor a1) - (m a1 a2) - (s.w packet-qwc (-> from-spr-channel qwc)) - (add.i a1 r0 DMA-CHCR-STR) - (s.w a1 (-> from-spr-channel chcr)) - (add.i a1 r0 0) - (dma-wait-counting-stalls! a1 from-spr-channel (-> work wait-from-spr) - wait-for-output-dma commit-dma-buffer) - (label commit-dma-buffer) - (l.w dma-buf (-> work dma-buffer)) - (nop!) - (nop!) - (nop!) - (s.w memory-cursor (-> dma-buf base)) - (nop!) - (label done) - (m v0 r0) - (l.d ra sp) - (l.q gp sp 16) - (jr ra :delay (add.i sp sp 32)) - (nop!) - (nop!) - (nop!))))) - (#unless PC_PORT (defun draw-prototype-inline-array-shrub ((prototype-count int) (buckets (inline-array prototype-bucket-shrub))) "Finish and submit the near, opaque, translucent, and billboard DMA chains accumulated for @@ -2586,8 +572,6 @@ (set! (-> usage data 33 total) (-> usage data 33 used)))) insert-result)))))) -;; TODO drawing... - (defun draw-drawable-tree-instance-shrub ((tree drawable-tree-instance-shrub) (level level)) "Clear the frame's shrub prototype buckets, traverse tree when shrub rendering is enabled, submit the generated chains, update scratchpad wait statistics, and store the nearest shrub distance in diff --git a/goal_src/jak1/engine/gfx/tfrag/tfrag-methods.gc b/goal_src/jak1/engine/gfx/tfrag/tfrag-methods.gc index 1cf662f58d..e39ef034ca 100644 --- a/goal_src/jak1/engine/gfx/tfrag/tfrag-methods.gc +++ b/goal_src/jak1/engine/gfx/tfrag/tfrag-methods.gc @@ -7,23 +7,12 @@ (require "engine/gfx/background/background.gc") (require "engine/collide/collide-shape-h.gc") -;; The terrain hierarchy: drawable-tree traversal, visibility propagation down the draw-node depths, -;; and the four category draw functions that turn a culled tree into far and near DMA streams. -;; -;; Nothing here chooses a detail level. Culling here is purely visibility, and the far draw function -;; picks each fragment's mesh from its own bounding sphere; see classify-fragment-lod in tfrag.gc for -;; why that choice has to be made per fragment from the whole sphere rather than per tree or from a -;; node. - ;; DECOMP BEGINS (defun edge-debug-lines ((edge-lists (array vector-array))) "Draw the selected edge lists as pairs of white, translucent, depth-independent debug lines. *display-strip-lines* selects lists by bit position." - ;; These edge lists are debug data and are not present in the retail game. (when (nonzero? edge-lists) - (#when PC_PORT - (format 0 "bad~%")) (dotimes (i (-> edge-lists length)) (when (logtest? *display-strip-lines* (ash 1 i)) (let ((edges (-> edge-lists i))) @@ -32,7 +21,7 @@ (bucket-id debug-no-zbuf) (-> edges data (* edge-index 2)) (-> edges data (+ (* edge-index 2) 1)) - (new 'static 'rgba :r #xff :g #xff :b #xff :a #x80) + (static-rgba #xff #xff #xff #x80) #f (the-as rgba -1))))))) (none)) @@ -41,836 +30,223 @@ ;; tree draw functions ;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Calling draw on a top-level terrain tree only queues the tree and its level. finish-background -;; later consumes those queues and builds the DMA streams here, after all ordinary draw traversal -;; has finished. +;; Each tree type has its own draw function. They are mostly structured the same, other than this ordering. +(defmacro finish-tfrag-perf-read-first! (stat wait-to-spr wait-from-spr) + `(begin + (read! ,stat) + (update-wait-stats ,stat (the-as uint 0) ,wait-to-spr ,wait-from-spr))) + +(defmacro finish-tfrag-perf-wait-first! (stat wait-to-spr wait-from-spr) + `(begin + (update-wait-stats ,stat (the-as uint 0) ,wait-to-spr ,wait-from-spr) + (read! ,stat))) + +;; Build one of the original EE terrain draw functions. Renderer masks, bucket pairs, TEST state, +;; alpha mode, and performance-counter ordering are parameters because they differ by category. (#unless PC_PORT - (defun draw-drawable-tree-tfrag ((tree drawable-tree-tfrag)) - "Cull the tree hierarchy, build the opaque far and near terrain DMA streams, submit them to the - current level's terrain buckets, and account for the DMA storage used." - (local-vars (visibility-bits (pointer uint8))) - (when (logtest? *vu1-enable-user* (vu1-renderer-mask tfrag)) - (let ((last-array-index (+ (-> tree length) -1))) - ;; Refine the scratchpad visibility list one draw-node depth at a time. The BSP pass has - ;; already left one bit per drawable id in vis-list; the byte at a node's own id holds the - ;; bits for its up-to-eight children. Each pass reads the current depth's bits and clears the - ;; child byte of any node whose own bit is off or whose bounding sphere is outside a camera - ;; side plane, so what survives walks down the tree in place. Nothing else is produced: the - ;; list is the output. The last array holds fragments rather than nodes, hence length - 1 - ;; passes, and its bits are what the two draw loops below read. - (when (nonzero? last-array-index) - (dotimes (depth-index last-array-index) - (let* ((parent-array (-> tree arrays depth-index)) - (child-array (-> tree arrays (+ depth-index 1))) - (parent-vis-byte-index (/ (-> (the-as drawable-inline-array-node parent-array) data 0 id) 8)) - (child-vis-byte-index (/ (-> (the-as drawable-inline-array-node child-array) data 0 id) 8)) - (parent-visibility (&-> (scratchpad-object terrain-context) work background vis-list parent-vis-byte-index)) - (child-visibility (&-> (scratchpad-object terrain-context) work background vis-list child-vis-byte-index))) - (draw-node-cull child-visibility - parent-visibility - (-> (the-as drawable-inline-array-node parent-array) data) - (-> (the-as drawable-inline-array-node parent-array) length))))) - (let* ((fragment-array (the-as drawable-inline-array-tfrag (-> tree arrays last-array-index))) - (fragments (-> fragment-array data)) - (fragment-count (-> fragment-array length))) - (set! visibility-bits (&-> (scratchpad-object terrain-context) work background vis-list (/ (-> fragments 0 id) 8))) - (let ((dma-start (-> (current-frame) global-buf base))) - ;; Far and near are two passes over the same fragment array into two buckets. Both open a - ;; chain in the global buffer, end it with a next tag and hand the span to the bucket - ;; system, which patches the chains together before the DMA goes out. + (defmacro define-tfrag-tree-draw (name tree-type fragment-array-type renderer-mask far-bucket-0 far-bucket-1 near-bucket-0 near-bucket-1 test alpha-blend far-perf-finish ee-docstring pc-docstring dma-bindings &rest epilogue) + `(defun ,name ((tree ,tree-type)) + ,ee-docstring + (when (logtest? *vu1-enable-user* (vu1-renderer-mask ,renderer-mask)) + (let ((last-array-index (+ (-> tree length) -1))) + (when (nonzero? last-array-index) + ;; update visibility bits based on frustum culling. + ;; This iterates through each depth level of the BVH and runs the optimized draw-node-cull function, + ;; which avoids testing children whose parents are flagged as not visible due to precomputed or frustum culling. + (dotimes (depth-index last-array-index) + (let* ((parent-array (-> tree arrays depth-index)) + (child-array (-> tree arrays (+ depth-index 1))) + (parent-vis-byte-index (/ (-> (the-as drawable-inline-array-node parent-array) data 0 id) 8)) + (child-vis-byte-index (/ (-> (the-as drawable-inline-array-node child-array) data 0 id) 8)) + (parent-visibility (&-> (scratchpad-object terrain-context) work background vis-list parent-vis-byte-index)) + (child-visibility (&-> (scratchpad-object terrain-context) work background vis-list child-vis-byte-index))) + (draw-node-cull child-visibility + parent-visibility + (-> (the-as drawable-inline-array-node parent-array) data) + (-> (the-as drawable-inline-array-node parent-array) length))))) + (let* ((fragment-array (the-as ,fragment-array-type (-> tree arrays last-array-index))) + (fragments (-> fragment-array data)) + (fragment-count (-> fragment-array length)) + (visibility-bits (&-> (scratchpad-object terrain-context) work background vis-list (/ (-> fragments 0 id) 8))) + ,@dma-bindings) + ;; "far" (not-near) drawing (with-dma-buffer-add-bucket ((far-dma-buf (-> (current-frame) global-buf)) (if (zero? (-> (scratchpad-object terrain-context) bsp lev-index)) - (bucket-id tfrag-0) - (bucket-id tfrag-1))) + (bucket-id ,far-bucket-0) + (bucket-id ,far-bucket-1))) (set! (-> *tfrag-work* wait-to-spr) (the-as uint 0)) (set! (-> *tfrag-work* wait-from-spr) (the-as uint 0)) - (tfrag-init-buffer far-dma-buf - (new 'static 'gs-test :ate #x1 :atst (gs-atest greater-equal) :aref #x26 :zte #x1 :ztst (gs-ztest greater-equal)) - 0) + (tfrag-init-buffer far-dma-buf ,test ,alpha-blend) (reset! (-> *perf-stats* data 5)) (draw-inline-array-tfrag visibility-bits (the-as (pointer tfragment) fragments) fragment-count far-dma-buf) - (read! (-> *perf-stats* data 5)) - (update-wait-stats (-> *perf-stats* data 5) - (the-as uint 0) - (-> *tfrag-work* wait-to-spr) - (-> *tfrag-work* wait-from-spr)) + (,far-perf-finish (-> *perf-stats* data 5) (-> *tfrag-work* wait-to-spr) (-> *tfrag-work* wait-from-spr)) (tfrag-end-buffer far-dma-buf)) + ;; near (clipping) drawing (with-dma-buffer-add-bucket ((near-dma-buf (-> (current-frame) global-buf)) (if (zero? (-> (scratchpad-object terrain-context) bsp lev-index)) - (bucket-id tfrag-near-0) - (bucket-id tfrag-near-1))) + (bucket-id ,near-bucket-0) + (bucket-id ,near-bucket-1))) (set! (-> *tfrag-work* near-wait-to-spr) (the-as uint 0)) (set! (-> *tfrag-work* near-wait-from-spr) (the-as uint 0)) - (tfrag-near-init-buffer near-dma-buf - (new 'static 'gs-test :ate #x1 :atst (gs-atest greater-equal) :aref #x26 :zte #x1 :ztst (gs-ztest greater-equal)) - 0) + (tfrag-near-init-buffer near-dma-buf ,test ,alpha-blend) (reset! (-> *perf-stats* data 6)) (draw-inline-array-tfrag-near visibility-bits (the-as (pointer tfragment) fragments) fragment-count near-dma-buf) - (read! (-> *perf-stats* data 6)) - (update-wait-stats (-> *perf-stats* data 6) - (the-as uint 0) - (-> *tfrag-work* near-wait-to-spr) - (-> *tfrag-work* near-wait-from-spr)) + (finish-tfrag-perf-read-first! (-> *perf-stats* data 6) + (-> *tfrag-work* near-wait-to-spr) + (-> *tfrag-work* near-wait-from-spr)) (tfrag-near-end-buffer near-dma-buf)) - ;; Both streams are charged to the tfragment row of the DMA usage report. - (let ((usage *dma-mem-usage*)) - (when (nonzero? usage) - (set! (-> usage length) (max 2 (-> usage length))) - (set! (-> usage data 1 name) "tfragment") - (+! (-> usage data 1 count) 1) - (+! (-> usage data 1 used) (&- (-> (current-frame) global-buf base) (the-as uint dma-start))) - (set! (-> usage data 1 total) (-> usage data 1 used)))))))) - (none))) + ,@epilogue))) + (none)))) +;; PC version, much simpler. (#when PC_PORT - (defun draw-drawable-tree-tfrag ((arg0 drawable-tree-tfrag) (lev level)) - "Build and submit the opaque terrain packet for lev. The PC packet includes the level's - time-of-day state and name for the native renderer." - (local-vars (r0-0 none) (a0-20 int) (a0-22 int) (a0-38 int) (a0-40 int) (sv-16 (pointer uint8))) - (when (logtest? *vu1-enable-user* (vu1-renderer-mask tfrag)) - ;; first, do draw node culling. - ;; the culling is done in batches, doing all nodes at one depth before going to the enxt. - ;; only arrays of draw nodes can be culled - tfragments themselves can't (they are the wrong size) - (let ((s5-0 (+ (-> arg0 length) -1))) ;; get the number of trees with draw-nodes (see doc, the last one is always tfragments) - ;; TODO - #| - (when (nonzero? s5-0) ;; only if we have draw-nodes (levels like INT have only like 2 tfrags) - (dotimes (s4-0 s5-0) ;; loop over tree depths - - ;; not sure of the details yet, but we take two levels as inputs - (let* ((v1-7 (-> arg0 arrays s4-0)) - (a0-4 (-> arg0 arrays (+ s4-0 1))) - (a1-1 (/ (-> (the-as drawable-inline-array-node v1-7) data 0 id) 8)) - (a0-6 (/ (-> (the-as drawable-inline-array-node a0-4) data 0 id) 8)) - (a1-3 (&-> (scratchpad-object terrain-context) work background vis-list a1-1)) - (a0-8 (&-> (scratchpad-object terrain-context) work background vis-list a0-6)) - ) - (draw-node-cull - a0-8 - a1-3 - (-> (the-as drawable-inline-array-node v1-7) data) - (-> (the-as drawable-inline-array-node v1-7) length) - ) - ) - ) - ) - |# - ;; draw, using the full list of all tfrags (not tree format) - (let* ((v1-13 (the-as drawable-inline-array-tfrag (-> arg0 arrays s5-0))) - (s4-1 (-> v1-13 data)) - (s3-0 (-> v1-13 length))) - ;; pointer to vis data for the first tfrag - (set! sv-16 (&-> (scratchpad-object terrain-context) work background vis-list (/ (-> s4-1 0 id) 8))) - ;; all DMA - (let ((s5-1 (-> *display* frames (-> *display* on-screen) frame global-buf base))) - ;; (format *stdcon* " #x~X~%" s5-1) - ;; DMA for TFRAG - ;; clear stats - ;; initialize dma buffer - ;; do the draw! - ;;(format 0 "DRAW: ~D~%" s3-0) - ;; (draw-inline-array-tfrag sv-16 (the-as drawable-inline-array s4-1) s3-0 s1-0) - ;; update stats for the draw - ;; finish dma buffer - ;; close dma packet - (with-dma-buffer-add-bucket ((s1-0 (-> *display* frames (-> *display* on-screen) frame global-buf)) (the-as bucket-id - (if (zero? (-> (scratchpad-object terrain-context) bsp lev-index)) (bucket-id tfrag-0) (bucket-id tfrag-1)))) :bucket-group (-> *display* frames (-> *display* on-screen) frame bucket-group) (set! (-> *tfrag-work* wait-to-spr) (the-as uint 0)) (set! (-> *tfrag-work* wait-from-spr) (the-as uint 0)) (tfrag-init-buffer s1-0 - (new 'static 'gs-test :ate #x1 :atst (gs-atest greater-equal) :aref #x26 :zte #x1 :ztst (gs-ztest greater-equal)) - 0 - lev) (reset! (-> *perf-stats* data 5)) (read! (-> *perf-stats* data 5)) (update-wait-stats (-> *perf-stats* data 5) - (the-as uint 0) - (-> *tfrag-work* wait-to-spr) - (-> *tfrag-work* wait-from-spr)) (tfrag-end-buffer s1-0)) - ;; (format *stdcon* " #x~X~%" (-> *display* frames (-> *display* on-screen) frame global-buf base)) - ;; DMA for TFRAG NEAR - ; (let* ((s1-1 (-> *display* frames (-> *display* on-screen) frame global-buf)) - ; (s2-1 (-> s1-1 base)) - ; ) - ; (set! (-> *tfrag-work* near-wait-to-spr) (the-as uint 0)) - ; (set! (-> *tfrag-work* near-wait-from-spr) (the-as uint 0)) - ; (tfrag-near-init-buffer - ; s1-1 - ; (new 'static 'gs-test :ate #x1 :atst (gs-atest greater-equal) :aref #x26 :zte #x1 :ztst (gs-ztest greater-equal)) - ; 0 - ; ) - ; (reset! (-> *perf-stats* data 6)) - ; (draw-inline-array-tfrag-near sv-16 (the-as drawable-inline-array s4-1) s3-0 s1-1) - ; (read! (-> *perf-stats* data 6)) - ; (update-wait-stats (-> *perf-stats* data 6) (the-as uint 0) (-> *tfrag-work* near-wait-to-spr) (-> *tfrag-work* near-wait-from-spr)) - ; (tfrag-near-end-buffer s1-1) - ; (let ((a3-6 (-> s1-1 base))) - ; (let ((v1-62 (the-as object (-> s1-1 base)))) - ; (set! (-> (the-as dma-packet v1-62) dma) (new 'static 'dma-tag :id (dma-tag-id next))) - ; (set! (-> (the-as dma-packet v1-62) vif0) (new 'static 'vif-tag)) - ; (set! (-> (the-as dma-packet v1-62) vif1) (new 'static 'vif-tag)) - ; (set! (-> s1-1 base) (&+ (the-as pointer v1-62) 16)) - ; ) - ; (dma-bucket-insert-tag - ; (-> *display* frames (-> *display* on-screen) frame bucket-group) - ; (the-as bucket-id (if (zero? (-> (scratchpad-object terrain-context) bsp lev-index)) - ; (bucket-id tfrag-near-0) - ; (bucket-id tfrag-near-1) - ; ) - ; ) - ; s2-1 - ; (the-as (pointer dma-tag) a3-6) - ; ) - ; ) - ; ) - ;; DMA memory stats - (let ((v1-69 *dma-mem-usage*)) - (when (nonzero? v1-69) - (set! (-> v1-69 length) (max 2 (-> v1-69 length))) - (set! (-> v1-69 data 1 name) "tfragment") - (+! (-> v1-69 data 1 count) 1) - (+! (-> v1-69 data 1 used) (&- (-> *display* frames (-> *display* on-screen) frame global-buf base) (the-as uint s5-1))) - (set! (-> v1-69 data 1 total) (-> v1-69 data 1 used)))))))) - (none))) - -(#unless PC_PORT - (defun draw-drawable-tree-trans-tfrag ((tree drawable-tree-trans-tfrag)) - "Cull the tree hierarchy and build alpha-blended far and near terrain streams using the - translucent TEST state and buckets." - (local-vars (visibility-bits (pointer uint8))) - (when (logtest? *vu1-enable-user* (vu1-renderer-mask trans-tfrag)) - (let ((last-array-index (+ (-> tree length) -1))) - ;; Walk the visibility list down the node depths in place; see draw-drawable-tree-tfrag. - (when (nonzero? last-array-index) - (dotimes (depth-index last-array-index) - (let* ((parent-array (-> tree arrays depth-index)) - (child-array (-> tree arrays (+ depth-index 1))) - (parent-vis-byte-index (/ (-> (the-as drawable-inline-array-node parent-array) data 0 id) 8)) - (child-vis-byte-index (/ (-> (the-as drawable-inline-array-node child-array) data 0 id) 8)) - (parent-visibility (&-> (scratchpad-object terrain-context) work background vis-list parent-vis-byte-index)) - (child-visibility (&-> (scratchpad-object terrain-context) work background vis-list child-vis-byte-index))) - (draw-node-cull child-visibility - parent-visibility - (-> (the-as drawable-inline-array-node parent-array) data) - (-> (the-as drawable-inline-array-node parent-array) length))))) - (let* ((fragment-array (-> tree arrays last-array-index)) - (fragments (&+ fragment-array 32)) - (fragment-count (-> fragment-array length))) - (set! visibility-bits (&-> (scratchpad-object terrain-context) work background vis-list (/ (-> fragments id) 8))) - (with-dma-buffer-add-bucket ((far-dma-buf (-> (current-frame) global-buf)) + (defmacro define-tfrag-tree-draw (name tree-type fragment-array-type renderer-mask far-bucket-0 far-bucket-1 near-bucket-0 near-bucket-1 test alpha-blend far-perf-finish ee-docstring pc-docstring dma-bindings &rest epilogue) + `(defun ,name ((tree ,tree-type) (lev level)) + ,pc-docstring + (when (logtest? *vu1-enable-user* (vu1-renderer-mask ,renderer-mask)) + (let ((dma-start (-> (current-frame) global-buf base))) + (with-dma-buffer-add-bucket ((dma-buf (-> (current-frame) global-buf)) (if (zero? (-> (scratchpad-object terrain-context) bsp lev-index)) - (bucket-id tfrag-trans-0) - (bucket-id tfrag-trans-1))) + (bucket-id ,far-bucket-0) + (bucket-id ,far-bucket-1))) (set! (-> *tfrag-work* wait-to-spr) (the-as uint 0)) (set! (-> *tfrag-work* wait-from-spr) (the-as uint 0)) - (tfrag-init-buffer far-dma-buf - (new 'static - 'gs-test - :ate #x1 - :atst (gs-atest greater-equal) - :aref #x7e - :afail #x1 - :zte #x1 - :ztst (gs-ztest greater-equal)) - 1) + (tfrag-init-buffer dma-buf ,test ,alpha-blend lev) (reset! (-> *perf-stats* data 5)) - (draw-inline-array-tfrag visibility-bits (the-as (pointer tfragment) fragments) fragment-count far-dma-buf) - (update-wait-stats (-> *perf-stats* data 5) - (the-as uint 0) - (-> *tfrag-work* wait-to-spr) - (-> *tfrag-work* wait-from-spr)) - (read! (-> *perf-stats* data 5)) - (tfrag-end-buffer far-dma-buf)) - (with-dma-buffer-add-bucket ((near-dma-buf (-> (current-frame) global-buf)) - (if (zero? (-> (scratchpad-object terrain-context) bsp lev-index)) - (bucket-id tfrag-trans-near-0) - (bucket-id tfrag-trans-near-1))) - (set! (-> *tfrag-work* near-wait-to-spr) (the-as uint 0)) - (set! (-> *tfrag-work* near-wait-from-spr) (the-as uint 0)) - (tfrag-near-init-buffer near-dma-buf - (new 'static - 'gs-test - :ate #x1 - :atst (gs-atest greater-equal) - :aref #x7e - :afail #x1 - :zte #x1 - :ztst (gs-ztest greater-equal)) - 1) - (reset! (-> *perf-stats* data 6)) - (draw-inline-array-tfrag-near visibility-bits (the-as (pointer tfragment) fragments) fragment-count near-dma-buf) - (read! (-> *perf-stats* data 6)) - (update-wait-stats (-> *perf-stats* data 6) - (the-as uint 0) - (-> *tfrag-work* near-wait-to-spr) - (-> *tfrag-work* near-wait-from-spr)) - (tfrag-near-end-buffer near-dma-buf))))) + (,far-perf-finish (-> *perf-stats* data 5) (-> *tfrag-work* wait-to-spr) (-> *tfrag-work* wait-from-spr)) + (tfrag-end-buffer dma-buf)) + (dma-mem-usage-add! tfragment 1 (&- (-> (current-frame) global-buf base) (the-as uint dma-start))))) + (none)))) + +;; standard TFRAG drawing +(define-tfrag-tree-draw draw-drawable-tree-tfrag + drawable-tree-tfrag + drawable-inline-array-tfrag + tfrag + tfrag-0 + tfrag-1 + tfrag-near-0 + tfrag-near-1 + (new 'static 'gs-test :ate #x1 :atst (gs-atest greater-equal) :aref #x26 :zte #x1 :ztst (gs-ztest greater-equal)) + 0 + finish-tfrag-perf-read-first! + "Cull the tree hierarchy, build the opaque far and near terrain DMA streams, submit them to the + current level's terrain buckets, and account for the DMA storage used." + "Build and submit the opaque terrain packet for lev. The PC packet includes the level's + time-of-day state and name for the native renderer." + ((dma-start (-> (current-frame) global-buf base))) + (dma-mem-usage-add! tfragment 1 (&- (-> (current-frame) global-buf base) (the-as uint dma-start)))) + +(define-tfrag-tree-draw draw-drawable-tree-trans-tfrag + drawable-tree-trans-tfrag + drawable-inline-array-trans-tfrag + trans-tfrag + tfrag-trans-0 + tfrag-trans-1 + tfrag-trans-near-0 + tfrag-trans-near-1 + (new 'static + 'gs-test + :ate #x1 + :atst (gs-atest greater-equal) + :aref #x7e + :afail #x1 + :zte #x1 + :ztst (gs-ztest greater-equal)) + 1 + finish-tfrag-perf-wait-first! + "Cull the tree hierarchy and build alpha-blended far and near terrain streams using the + translucent TEST state and buckets." + "Build and submit the translucent terrain packet for lev using its alpha-test state." + ()) + +(define-tfrag-tree-draw draw-drawable-tree-dirt-tfrag + drawable-tree-dirt-tfrag + drawable-inline-array-trans-tfrag + trans-tfrag + tfrag-dirt-0 + tfrag-dirt-1 + tfrag-dirt-near-0 + tfrag-dirt-near-1 + (new 'static 'gs-test :ate #x1 :afail #x1 :zte #x1 :ztst (gs-ztest greater-equal)) + 1 + finish-tfrag-perf-wait-first! + "Cull the tree hierarchy and build alpha-blended far and near dirt-terrain streams using their + dedicated TEST state and buckets." + "Build and submit the dirt-terrain packet for lev using its alpha-test state." + ()) + +(define-tfrag-tree-draw draw-drawable-tree-ice-tfrag + drawable-tree-ice-tfrag + drawable-inline-array-trans-tfrag + trans-tfrag + tfrag-ice-0 + tfrag-ice-1 + tfrag-ice-near-0 + tfrag-ice-near-1 + (new 'static 'gs-test :ate #x1 :atst (gs-atest always) :afail #x1 :zte #x1 :ztst (gs-ztest greater-equal)) + 1 + finish-tfrag-perf-wait-first! + "Cull the tree hierarchy and build alpha-blended far and near ice-terrain streams using their + dedicated TEST state and buckets." + "Build and submit the ice-terrain packet for lev using its alpha-test state." + ()) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; queue methods +;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defmacro define-tfrag-tree-draw-method (tree-type count-field trees-field levels-field docstring) + `(defmethod draw ((this ,tree-type) (submitted-tree ,tree-type) (frame display-frame)) + ,docstring + (let* ((queue-index (-> *background-work* ,count-field)) + (level-index (-> (scratchpad-object terrain-context) bsp lev-index)) + (lev (-> *level* level level-index))) + (set! (-> *background-work* ,trees-field queue-index) this) + (set! (-> *background-work* ,levels-field queue-index) lev)) + (+! (-> *background-work* ,count-field) 1) (none))) -(#when PC_PORT - (defun draw-drawable-tree-trans-tfrag ((arg0 drawable-tree-trans-tfrag) (lev level)) - "Build and submit the translucent terrain packet for lev using its alpha-test state." - (local-vars (r0-0 none) (a0-18 int) (a0-20 int) (a0-35 int) (a0-37 int) (sv-16 (pointer uint8))) - (when (logtest? *vu1-enable-user* (vu1-renderer-mask trans-tfrag)) - (let ((s5-0 (+ (-> arg0 length) -1))) - #| - TODO - (when (nonzero? s5-0) - (dotimes (s4-0 s5-0) - (let* ((v1-7 (-> arg0 arrays s4-0)) - (a0-4 (-> arg0 arrays (+ s4-0 1))) - (a1-1 (/ (-> (the-as drawable-inline-array-node v1-7) data 0 id) 8)) - (a0-6 (/ (-> (the-as drawable-inline-array-node a0-4) data 0 id) 8)) - (a1-3 (&-> (the-as terrain-context #x70000000) work background vis-list a1-1)) - (a0-8 (&-> (the-as terrain-context #x70000000) work background vis-list a0-6)) - ) - (draw-node-cull - a0-8 - a1-3 - (-> (the-as drawable-inline-array-node v1-7) data) - (-> (the-as drawable-inline-array-node v1-7) length) - ) - ) - ) - ) - |# - (let* ((v1-13 (-> arg0 arrays s5-0)) - (s5-1 (&+ v1-13 32)) - (s4-1 (-> v1-13 length))) - (set! sv-16 (&-> (scratchpad-object terrain-context) work background vis-list (/ (-> s5-1 id) 8))) - ;; (draw-inline-array-tfrag sv-16 s5-1 s4-1 s2-0) - (with-dma-buffer-add-bucket ((s2-0 (-> *display* frames (-> *display* on-screen) frame global-buf)) (the-as bucket-id - (if (zero? (-> (scratchpad-object terrain-context) bsp lev-index)) (bucket-id tfrag-trans-0) (bucket-id tfrag-trans-1)))) :bucket-group (-> *display* frames (-> *display* on-screen) frame bucket-group) (set! (-> *tfrag-work* wait-to-spr) (the-as uint 0)) (set! (-> *tfrag-work* wait-from-spr) (the-as uint 0)) (tfrag-init-buffer s2-0 - (new 'static - 'gs-test - :ate #x1 - :atst (gs-atest greater-equal) - :aref #x7e - :afail #x1 - :zte #x1 - :ztst (gs-ztest greater-equal)) - 1 - lev) (reset! (-> *perf-stats* data 5)) (update-wait-stats (-> *perf-stats* data 5) - (the-as uint 0) - (-> *tfrag-work* wait-to-spr) - (-> *tfrag-work* wait-from-spr)) (read! (-> *perf-stats* data 5)) (tfrag-end-buffer s2-0)) - #| - TODO - (let* - ((s2-1 (-> *display* frames (-> *display* on-screen) frame global-buf)) - (s3-1 (-> s2-1 base)) - ) - (set! (-> *tfrag-work* near-wait-to-spr) (the-as uint 0)) - (set! (-> *tfrag-work* near-wait-from-spr) (the-as uint 0)) - (tfrag-near-init-buffer - s2-1 - (new 'static 'gs-test - :ate #x1 - :atst (gs-atest greater-equal) - :aref #x7e - :afail #x1 - :zte #x1 - :ztst (gs-ztest greater-equal) - ) - 1 - ) - (let* ((v1-48 (-> *perf-stats* data 6)) - (a0-32 (-> v1-48 ctrl)) - ) - (+! (-> v1-48 count) 1) - (b! (zero? a0-32) cfg-15 :delay (nop!)) - (.mtc0 Perf r0-0) - (.sync.l) - (.sync.p) - (.mtpc pcr0 r0-0) - (.mtpc pcr1 r0-0) - (.sync.l) - (.sync.p) - (.mtc0 Perf a0-32) - ) - (.sync.l) - (.sync.p) - (label cfg-15) - 0 - (draw-inline-array-tfrag-near sv-16 s5-1 s4-1 s2-1) - (let ((v1-51 (-> *perf-stats* data 6))) - (b! (zero? (-> v1-51 ctrl)) cfg-17 :delay (nop!)) - (.mtc0 Perf r0-0) - (.sync.l) - (.sync.p) - (.mfpc a0-35 pcr0) - (+! (-> v1-51 accum0) a0-35) - (.mfpc a0-37 pcr1) - (+! (-> v1-51 accum1) a0-37) - ) - (label cfg-17) - 0 - (update-wait-stats - (-> *perf-stats* data 6) - (the-as uint 0) - (-> *tfrag-work* near-wait-to-spr) - (-> *tfrag-work* near-wait-from-spr) - ) - (tfrag-near-end-buffer s2-1) - (let ((a3-6 (-> s2-1 base))) - (let ((v1-58 (the-as object (-> s2-1 base)))) - (set! - (-> (the-as dma-packet v1-58) dma) - (new 'static 'dma-tag :id (dma-tag-id next)) - ) - (set! (-> (the-as dma-packet v1-58) vif0) (new 'static 'vif-tag)) - (set! (-> (the-as dma-packet v1-58) vif1) (new 'static 'vif-tag)) - (set! (-> s2-1 base) (&+ (the-as pointer v1-58) 16)) - ) - (dma-bucket-insert-tag - (-> *display* frames (-> *display* on-screen) frame bucket-group) - (the-as - bucket-id - (if (zero? (-> (the-as terrain-context #x70000000) bsp lev-index)) - 33 - 40 - ) - ) - s3-1 - (the-as (pointer dma-tag) a3-6) - ) - ) - ) - |# - ))) - (none))) +(define-tfrag-tree-draw-method drawable-tree-tfrag + tfrag-tree-count + tfrag-trees + tfrag-levels + "Queue this terrain tree and its owning level for the background renderer.") -(#unless PC_PORT - (defun draw-drawable-tree-dirt-tfrag ((tree drawable-tree-dirt-tfrag)) - "Cull the tree hierarchy and build alpha-blended far and near dirt-terrain streams using their - dedicated TEST state and buckets." - (local-vars (visibility-bits (pointer uint8))) - ;; The dirt and ice trees are gated by the trans-tfrag enable bit, not one of their own. - (when (logtest? *vu1-enable-user* (vu1-renderer-mask trans-tfrag)) - (let ((last-array-index (+ (-> tree length) -1))) - ;; Walk the visibility list down the node depths in place; see draw-drawable-tree-tfrag. - (when (nonzero? last-array-index) - (dotimes (depth-index last-array-index) - (let* ((parent-array (-> tree arrays depth-index)) - (child-array (-> tree arrays (+ depth-index 1))) - (parent-vis-byte-index (/ (-> (the-as drawable-inline-array-node parent-array) data 0 id) 8)) - (child-vis-byte-index (/ (-> (the-as drawable-inline-array-node child-array) data 0 id) 8)) - (parent-visibility (&-> (scratchpad-object terrain-context) work background vis-list parent-vis-byte-index)) - (child-visibility (&-> (scratchpad-object terrain-context) work background vis-list child-vis-byte-index))) - (draw-node-cull child-visibility - parent-visibility - (-> (the-as drawable-inline-array-node parent-array) data) - (-> (the-as drawable-inline-array-node parent-array) length))))) - (let* ((fragment-array (-> tree arrays last-array-index)) - (fragments (&+ fragment-array 32)) - (fragment-count (-> fragment-array length))) - (set! visibility-bits (&-> (scratchpad-object terrain-context) work background vis-list (/ (-> fragments id) 8))) - (with-dma-buffer-add-bucket ((far-dma-buf (-> (current-frame) global-buf)) - (if (zero? (-> (scratchpad-object terrain-context) bsp lev-index)) - (bucket-id tfrag-dirt-0) - (bucket-id tfrag-dirt-1))) - (set! (-> *tfrag-work* wait-to-spr) (the-as uint 0)) - (set! (-> *tfrag-work* wait-from-spr) (the-as uint 0)) - (tfrag-init-buffer far-dma-buf (new 'static 'gs-test :ate #x1 :afail #x1 :zte #x1 :ztst (gs-ztest greater-equal)) 1) - (reset! (-> *perf-stats* data 5)) - (draw-inline-array-tfrag visibility-bits (the-as (pointer tfragment) fragments) fragment-count far-dma-buf) - (update-wait-stats (-> *perf-stats* data 5) - (the-as uint 0) - (-> *tfrag-work* wait-to-spr) - (-> *tfrag-work* wait-from-spr)) - (read! (-> *perf-stats* data 5)) - (tfrag-end-buffer far-dma-buf)) - (with-dma-buffer-add-bucket ((near-dma-buf (-> (current-frame) global-buf)) - (if (zero? (-> (scratchpad-object terrain-context) bsp lev-index)) - (bucket-id tfrag-dirt-near-0) - (bucket-id tfrag-dirt-near-1))) - (set! (-> *tfrag-work* near-wait-to-spr) (the-as uint 0)) - (set! (-> *tfrag-work* near-wait-from-spr) (the-as uint 0)) - (tfrag-near-init-buffer near-dma-buf (new 'static 'gs-test :ate #x1 :afail #x1 :zte #x1 :ztst (gs-ztest greater-equal)) 1) - (reset! (-> *perf-stats* data 6)) - (draw-inline-array-tfrag-near visibility-bits (the-as (pointer tfragment) fragments) fragment-count near-dma-buf) - (read! (-> *perf-stats* data 6)) - (update-wait-stats (-> *perf-stats* data 6) - (the-as uint 0) - (-> *tfrag-work* near-wait-to-spr) - (-> *tfrag-work* near-wait-from-spr)) - (tfrag-near-end-buffer near-dma-buf))))) - (none))) +(define-tfrag-tree-draw-method drawable-tree-trans-tfrag + trans-tfrag-tree-count + trans-tfrag-trees + trans-tfrag-levels + "Queue this translucent terrain tree and its owning level for the background renderer.") -(#when PC_PORT - (defun draw-drawable-tree-dirt-tfrag ((arg0 drawable-tree-dirt-tfrag) (lev level)) - "Build and submit the dirt-terrain packet for lev using its alpha-test state." - (local-vars (r0-0 none) (a0-18 int) (a0-20 int) (a0-35 int) (a0-37 int) (sv-16 (pointer uint8))) - (when (logtest? *vu1-enable-user* (vu1-renderer-mask trans-tfrag)) - (let ((s5-0 (+ (-> arg0 length) -1))) - #| - TODO - (when (nonzero? s5-0) - (dotimes (s4-0 s5-0) - (let* ((v1-7 (-> arg0 arrays s4-0)) - (a0-4 (-> arg0 arrays (+ s4-0 1))) - (a1-1 (/ (-> (the-as drawable-inline-array-node v1-7) data 0 id) 8)) - (a0-6 (/ (-> (the-as drawable-inline-array-node a0-4) data 0 id) 8)) - (a1-3 - (&-> - (the-as terrain-context #x70000000) - work - background - vis-list - a1-1 - ) - ) - (a0-8 - (&-> - (the-as terrain-context #x70000000) - work - background - vis-list - a0-6 - ) - ) - ) - (draw-node-cull - a0-8 - a1-3 - (-> (the-as drawable-inline-array-node v1-7) data) - (-> (the-as drawable-inline-array-node v1-7) length) - ) - ) - ) - ) - |# - (let* ((v1-13 (-> arg0 arrays s5-0)) - (s5-1 (&+ v1-13 32)) - (s4-1 (-> v1-13 length))) - (set! sv-16 (&-> (scratchpad-object terrain-context) work background vis-list (/ (-> s5-1 id) 8))) - ;; (draw-inline-array-tfrag sv-16 s5-1 s4-1 s2-0) - (with-dma-buffer-add-bucket ((s2-0 (-> *display* frames (-> *display* on-screen) frame global-buf)) (if (zero? (-> (scratchpad-object terrain-context) bsp lev-index)) (bucket-id tfrag-dirt-0) (bucket-id tfrag-dirt-1))) :bucket-group (-> *display* frames (-> *display* on-screen) frame bucket-group) (set! (-> *tfrag-work* wait-to-spr) (the-as uint 0)) (set! (-> *tfrag-work* wait-from-spr) (the-as uint 0)) (tfrag-init-buffer s2-0 (new 'static 'gs-test :ate #x1 :afail #x1 :zte #x1 :ztst (gs-ztest greater-equal)) 1 lev) (reset! (-> *perf-stats* data 5)) (update-wait-stats (-> *perf-stats* data 5) - (the-as uint 0) - (-> *tfrag-work* wait-to-spr) - (-> *tfrag-work* wait-from-spr)) (read! (-> *perf-stats* data 5)) (tfrag-end-buffer s2-0)) - #| - (let* - ((s2-1 (-> *display* frames (-> *display* on-screen) frame global-buf)) - (s3-1 (-> s2-1 base)) - ) - (set! (-> *tfrag-work* near-wait-to-spr) (the-as uint 0)) - (set! (-> *tfrag-work* near-wait-from-spr) (the-as uint 0)) - (tfrag-near-init-buffer - s2-1 - (new 'static 'gs-test - :ate #x1 - :afail #x1 - :zte #x1 - :ztst (gs-ztest greater-equal) - ) - 1 - ) - (let* ((v1-48 (-> *perf-stats* data 6)) - (a0-32 (-> v1-48 ctrl)) - ) - (+! (-> v1-48 count) 1) - (b! (zero? a0-32) cfg-15 :delay (nop!)) - (.mtc0 Perf r0-0) - (.sync.l) - (.sync.p) - (.mtpc pcr0 r0-0) - (.mtpc pcr1 r0-0) - (.sync.l) - (.sync.p) - (.mtc0 Perf a0-32) - ) - (.sync.l) - (.sync.p) - (label cfg-15) - 0 - (draw-inline-array-tfrag-near sv-16 s5-1 s4-1 s2-1) - (let ((v1-51 (-> *perf-stats* data 6))) - (b! (zero? (-> v1-51 ctrl)) cfg-17 :delay (nop!)) - (.mtc0 Perf r0-0) - (.sync.l) - (.sync.p) - (.mfpc a0-35 pcr0) - (+! (-> v1-51 accum0) a0-35) - (.mfpc a0-37 pcr1) - (+! (-> v1-51 accum1) a0-37) - ) - (label cfg-17) - 0 - (update-wait-stats - (-> *perf-stats* data 6) - (the-as uint 0) - (-> *tfrag-work* near-wait-to-spr) - (-> *tfrag-work* near-wait-from-spr) - ) - (tfrag-near-end-buffer s2-1) - (let ((a3-6 (-> s2-1 base))) - (let ((v1-58 (the-as object (-> s2-1 base)))) - (set! - (-> (the-as dma-packet v1-58) dma) - (new 'static 'dma-tag :id (dma-tag-id next)) - ) - (set! (-> (the-as dma-packet v1-58) vif0) (new 'static 'vif-tag)) - (set! (-> (the-as dma-packet v1-58) vif1) (new 'static 'vif-tag)) - (set! (-> s2-1 base) (&+ (the-as pointer v1-58) 16)) - ) - (dma-bucket-insert-tag - (-> *display* frames (-> *display* on-screen) frame bucket-group) - (the-as - bucket-id - (if (zero? (-> (the-as terrain-context #x70000000) bsp lev-index)) - 35 - 42 - ) - ) - s3-1 - (the-as (pointer dma-tag) a3-6) - ) - ) - ) - |# - ))) - (none))) +(define-tfrag-tree-draw-method drawable-tree-dirt-tfrag + dirt-tfrag-tree-count + dirt-tfrag-trees + dirt-tfrag-levels + "Queue this dirt terrain tree and its owning level for the background renderer.") -(#unless PC_PORT - (defun draw-drawable-tree-ice-tfrag ((tree drawable-tree-ice-tfrag)) - "Cull the tree hierarchy and build alpha-blended far and near ice-terrain streams using their - dedicated TEST state and buckets." - (local-vars (visibility-bits (pointer uint8))) - ;; The dirt and ice trees are gated by the trans-tfrag enable bit, not one of their own. - (when (logtest? *vu1-enable-user* (vu1-renderer-mask trans-tfrag)) - (let ((last-array-index (+ (-> tree length) -1))) - ;; Walk the visibility list down the node depths in place; see draw-drawable-tree-tfrag. - (when (nonzero? last-array-index) - (dotimes (depth-index last-array-index) - (let* ((parent-array (-> tree arrays depth-index)) - (child-array (-> tree arrays (+ depth-index 1))) - (parent-vis-byte-index (/ (-> (the-as drawable-inline-array-node parent-array) data 0 id) 8)) - (child-vis-byte-index (/ (-> (the-as drawable-inline-array-node child-array) data 0 id) 8)) - (parent-visibility (&-> (scratchpad-object terrain-context) work background vis-list parent-vis-byte-index)) - (child-visibility (&-> (scratchpad-object terrain-context) work background vis-list child-vis-byte-index))) - (draw-node-cull child-visibility - parent-visibility - (-> (the-as drawable-inline-array-node parent-array) data) - (-> (the-as drawable-inline-array-node parent-array) length))))) - (let* ((fragment-array (-> tree arrays last-array-index)) - (fragments (&+ fragment-array 32)) - (fragment-count (-> fragment-array length))) - (set! visibility-bits (&-> (scratchpad-object terrain-context) work background vis-list (/ (-> fragments id) 8))) - (with-dma-buffer-add-bucket ((far-dma-buf (-> (current-frame) global-buf)) - (if (zero? (-> (scratchpad-object terrain-context) bsp lev-index)) - (bucket-id tfrag-ice-0) - (bucket-id tfrag-ice-1))) - (set! (-> *tfrag-work* wait-to-spr) (the-as uint 0)) - (set! (-> *tfrag-work* wait-from-spr) (the-as uint 0)) - (tfrag-init-buffer far-dma-buf - (new 'static 'gs-test :ate #x1 :atst (gs-atest always) :afail #x1 :zte #x1 :ztst (gs-ztest greater-equal)) - 1) - (reset! (-> *perf-stats* data 5)) - (draw-inline-array-tfrag visibility-bits (the-as (pointer tfragment) fragments) fragment-count far-dma-buf) - (update-wait-stats (-> *perf-stats* data 5) - (the-as uint 0) - (-> *tfrag-work* wait-to-spr) - (-> *tfrag-work* wait-from-spr)) - (read! (-> *perf-stats* data 5)) - (tfrag-end-buffer far-dma-buf)) - (with-dma-buffer-add-bucket ((near-dma-buf (-> (current-frame) global-buf)) - (if (zero? (-> (scratchpad-object terrain-context) bsp lev-index)) - (bucket-id tfrag-ice-near-0) - (bucket-id tfrag-ice-near-1))) - (set! (-> *tfrag-work* near-wait-to-spr) (the-as uint 0)) - (set! (-> *tfrag-work* near-wait-from-spr) (the-as uint 0)) - (tfrag-near-init-buffer near-dma-buf - (new 'static 'gs-test :ate #x1 :atst (gs-atest always) :afail #x1 :zte #x1 :ztst (gs-ztest greater-equal)) - 1) - (reset! (-> *perf-stats* data 6)) - (draw-inline-array-tfrag-near visibility-bits (the-as (pointer tfragment) fragments) fragment-count near-dma-buf) - (read! (-> *perf-stats* data 6)) - (update-wait-stats (-> *perf-stats* data 6) - (the-as uint 0) - (-> *tfrag-work* near-wait-to-spr) - (-> *tfrag-work* near-wait-from-spr)) - (tfrag-near-end-buffer near-dma-buf))))) - (none))) +(define-tfrag-tree-draw-method drawable-tree-ice-tfrag + ice-tfrag-tree-count + ice-tfrag-trees + ice-tfrag-levels + "Queue this ice terrain tree and its owning level for the background renderer.") -(#when PC_PORT - (defun draw-drawable-tree-ice-tfrag ((arg0 drawable-tree-ice-tfrag) (lev level)) - "Build and submit the ice-terrain packet for lev using its alpha-test state." - (local-vars (r0-0 none) (a0-18 int) (a0-20 int) (a0-35 int) (a0-37 int) (sv-16 (pointer uint8))) - (when (logtest? *vu1-enable-user* (vu1-renderer-mask trans-tfrag)) - (let ((s5-0 (+ (-> arg0 length) -1))) - #| - TODO - (when (nonzero? s5-0) - (dotimes (s4-0 s5-0) - (let* ((v1-7 (-> arg0 arrays s4-0)) - (a0-4 (-> arg0 arrays (+ s4-0 1))) - (a1-1 (/ (-> (the-as drawable-inline-array-node v1-7) data 0 id) 8)) - (a0-6 (/ (-> (the-as drawable-inline-array-node a0-4) data 0 id) 8)) - (a1-3 (&-> (the-as terrain-context #x70000000) work background vis-list a1-1)) - (a0-8 (&-> (the-as terrain-context #x70000000) work background vis-list a0-6)) - ) - (draw-node-cull - a0-8 - a1-3 - (-> (the-as drawable-inline-array-node v1-7) data) - (-> (the-as drawable-inline-array-node v1-7) length) - ) - ) - ) - ) - |# - (let* ((v1-13 (-> arg0 arrays s5-0)) - (s5-1 (&+ v1-13 32)) - (s4-1 (-> v1-13 length))) - (set! sv-16 (&-> (scratchpad-object terrain-context) work background vis-list (/ (-> s5-1 id) 8))) - ;; (draw-inline-array-tfrag sv-16 s5-1 s4-1 s2-0) - (with-dma-buffer-add-bucket ((s2-0 (-> *display* frames (-> *display* on-screen) frame global-buf)) (the-as bucket-id (if (zero? (-> (scratchpad-object terrain-context) bsp lev-index)) 36 43))) :bucket-group (-> *display* frames (-> *display* on-screen) frame bucket-group) (set! (-> *tfrag-work* wait-to-spr) (the-as uint 0)) (set! (-> *tfrag-work* wait-from-spr) (the-as uint 0)) (tfrag-init-buffer s2-0 - (new 'static 'gs-test :ate #x1 :atst (gs-atest always) :afail #x1 :zte #x1 :ztst (gs-ztest greater-equal)) - 1 - lev) (reset! (-> *perf-stats* data 5)) (update-wait-stats (-> *perf-stats* data 5) - (the-as uint 0) - (-> *tfrag-work* wait-to-spr) - (-> *tfrag-work* wait-from-spr)) (read! (-> *perf-stats* data 5)) (tfrag-end-buffer s2-0)) - #| - (let* - ((s2-1 (-> *display* frames (-> *display* on-screen) frame global-buf)) - (s3-1 (-> s2-1 base)) - ) - (set! (-> *tfrag-work* near-wait-to-spr) (the-as uint 0)) - (set! (-> *tfrag-work* near-wait-from-spr) (the-as uint 0)) - (tfrag-near-init-buffer - s2-1 - (new 'static 'gs-test - :ate #x1 - :atst (gs-atest always) - :afail #x1 - :zte #x1 - :ztst (gs-ztest greater-equal) - ) - 1 - ) - (let* ((v1-48 (-> *perf-stats* data 6)) - (a0-32 (-> v1-48 ctrl)) - ) - (+! (-> v1-48 count) 1) - (b! (zero? a0-32) cfg-15 :delay (nop!)) - (.mtc0 Perf r0-0) - (.sync.l) - (.sync.p) - (.mtpc pcr0 r0-0) - (.mtpc pcr1 r0-0) - (.sync.l) - (.sync.p) - (.mtc0 Perf a0-32) - ) - (.sync.l) - (.sync.p) - (label cfg-15) - 0 - (draw-inline-array-tfrag-near sv-16 s5-1 s4-1 s2-1) - (let ((v1-51 (-> *perf-stats* data 6))) - (b! (zero? (-> v1-51 ctrl)) cfg-17 :delay (nop!)) - (.mtc0 Perf r0-0) - (.sync.l) - (.sync.p) - (.mfpc a0-35 pcr0) - (+! (-> v1-51 accum0) a0-35) - (.mfpc a0-37 pcr1) - (+! (-> v1-51 accum1) a0-37) - ) - (label cfg-17) - 0 - (update-wait-stats - (-> *perf-stats* data 6) - (the-as uint 0) - (-> *tfrag-work* near-wait-to-spr) - (-> *tfrag-work* near-wait-from-spr) - ) - (tfrag-near-end-buffer s2-1) - (let ((a3-6 (-> s2-1 base))) - (let ((v1-58 (the-as object (-> s2-1 base)))) - (set! - (-> (the-as dma-packet v1-58) dma) - (new 'static 'dma-tag :id (dma-tag-id next)) - ) - (set! (-> (the-as dma-packet v1-58) vif0) (new 'static 'vif-tag)) - (set! (-> (the-as dma-packet v1-58) vif1) (new 'static 'vif-tag)) - (set! (-> s2-1 base) (&+ (the-as pointer v1-58) 16)) - ) - (dma-bucket-insert-tag - (-> *display* frames (-> *display* on-screen) frame bucket-group) - (the-as - bucket-id - (if (zero? (-> (the-as terrain-context #x70000000) bsp lev-index)) - 37 - 44 - ) - ) - s3-1 - (the-as (pointer dma-tag) a3-6) - ) - ) - ) - |# - ))) - (none))) +(define-tfrag-tree-draw-method drawable-tree-lowres-tfrag + lowres-tfrag-tree-count + lowres-tfrag-trees + lowres-tfrag-levels + "Queue this low-resolution terrain tree and its owning level for the background renderer.") -(defmethod draw ((this drawable-tree-tfrag) (submitted-tree drawable-tree-tfrag) (frame display-frame)) - "Queue this terrain tree and its owning level for the background renderer." - (let* ((queue-index (-> *background-work* tfrag-tree-count)) - (level-index (-> (scratchpad-object terrain-context) bsp lev-index)) - (lev (-> *level* level level-index))) - (set! (-> *background-work* tfrag-trees queue-index) this) - (set! (-> *background-work* tfrag-levels queue-index) lev)) - (+! (-> *background-work* tfrag-tree-count) 1) - (none)) +(define-tfrag-tree-draw-method drawable-tree-lowres-trans-tfrag + lowres-trans-tfrag-tree-count + lowres-trans-tfrag-trees + lowres-trans-tfrag-levels + "Queue this low-resolution translucent terrain tree and its owning level for the background renderer.") -(defmethod draw ((this drawable-tree-trans-tfrag) (submitted-tree drawable-tree-trans-tfrag) (frame display-frame)) - "Queue this translucent terrain tree and its owning level for the background renderer." - (let* ((queue-index (-> *background-work* trans-tfrag-tree-count)) - (level-index (-> (scratchpad-object terrain-context) bsp lev-index)) - (lev (-> *level* level level-index))) - (set! (-> *background-work* trans-tfrag-trees queue-index) this) - (set! (-> *background-work* trans-tfrag-levels queue-index) lev)) - (+! (-> *background-work* trans-tfrag-tree-count) 1) - (none)) - -(defmethod draw ((this drawable-tree-dirt-tfrag) (submitted-tree drawable-tree-dirt-tfrag) (frame display-frame)) - "Queue this dirt terrain tree and its owning level for the background renderer." - (let* ((queue-index (-> *background-work* dirt-tfrag-tree-count)) - (level-index (-> (scratchpad-object terrain-context) bsp lev-index)) - (lev (-> *level* level level-index))) - (set! (-> *background-work* dirt-tfrag-trees queue-index) this) - (set! (-> *background-work* dirt-tfrag-levels queue-index) lev)) - (+! (-> *background-work* dirt-tfrag-tree-count) 1) - (none)) - -(defmethod draw ((this drawable-tree-ice-tfrag) (submitted-tree drawable-tree-ice-tfrag) (frame display-frame)) - "Queue this ice terrain tree and its owning level for the background renderer." - (let* ((queue-index (-> *background-work* ice-tfrag-tree-count)) - (level-index (-> (scratchpad-object terrain-context) bsp lev-index)) - (lev (-> *level* level level-index))) - (set! (-> *background-work* ice-tfrag-trees queue-index) this) - (set! (-> *background-work* ice-tfrag-levels queue-index) lev)) - (+! (-> *background-work* ice-tfrag-tree-count) 1) - (none)) - -(defmethod draw ((this drawable-tree-lowres-tfrag) (submitted-tree drawable-tree-lowres-tfrag) (frame display-frame)) - "Queue this low-resolution terrain tree and its owning level for the background renderer." - (let* ((queue-index (-> *background-work* lowres-tfrag-tree-count)) - (level-index (-> (scratchpad-object terrain-context) bsp lev-index)) - (lev (-> *level* level level-index))) - (set! (-> *background-work* lowres-tfrag-trees queue-index) this) - (set! (-> *background-work* lowres-tfrag-levels queue-index) lev)) - (+! (-> *background-work* lowres-tfrag-tree-count) 1) - (none)) - -(defmethod draw ((this drawable-tree-lowres-trans-tfrag) (submitted-tree drawable-tree-lowres-trans-tfrag) (frame display-frame)) - "Queue this low-resolution translucent terrain tree and its owning level for the background - renderer." - (let* ((queue-index (-> *background-work* lowres-trans-tfrag-tree-count)) - (level-index (-> (scratchpad-object terrain-context) bsp lev-index)) - (lev (-> *level* level level-index))) - (set! (-> *background-work* lowres-trans-tfrag-trees queue-index) this) - (set! (-> *background-work* lowres-trans-tfrag-levels queue-index) lev)) - (+! (-> *background-work* lowres-trans-tfrag-tree-count) 1) - (none)) +;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; statistics methods +;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defmethod collect-stats ((this tfragment)) "Classify this fragment against the camera and add the selected detail stream's counts to the @@ -878,122 +254,100 @@ (stats-tfrag-asm this) (none)) -(defmethod collect-stats ((this drawable-tree-tfrag)) - "Configure normal-terrain statistics and traverse every array in this tree." - (when (logtest? *vu1-enable-user* (vu1-renderer-mask tfrag)) - (set! (-> *tfrag-work* vu1-enable-tfrag) (the-as int (logand *vu1-enable-user* (vu1-renderer-mask tfrag)))) - (set! (-> *tfrag-work* vu1-enable-tfrag-near) (the-as int (logand *vu1-enable-user* (vu1-renderer-mask tfrag)))) - (set! (-> *tfrag-work* tr-stat-tfrag) (-> *terrain-stats* tfrag)) - (set! (-> *tfrag-work* tr-stat-tfrag-near) (-> *terrain-stats* tfrag-near)) - (let ((distance-data (-> *tfrag-work* frag-dists quad))) (set! (-> *tfrag-work* frag-dists quad) distance-data)) - (dotimes (i (-> this length)) - (collect-stats (-> this arrays i)))) - (none)) +(defmacro define-tfrag-tree-collect-stats (tree-type renderer-mask far-stat near-stat docstring) + `(defmethod collect-stats ((this ,tree-type)) + ,docstring + (when (logtest? *vu1-enable-user* (vu1-renderer-mask ,renderer-mask)) + (set! (-> *tfrag-work* vu1-enable-tfrag) (the-as int (logand *vu1-enable-user* (vu1-renderer-mask ,renderer-mask)))) + (set! (-> *tfrag-work* vu1-enable-tfrag-near) (the-as int (logand *vu1-enable-user* (vu1-renderer-mask ,renderer-mask)))) + (set! (-> *tfrag-work* tr-stat-tfrag) (-> *terrain-stats* ,far-stat)) + (set! (-> *tfrag-work* tr-stat-tfrag-near) (-> *terrain-stats* ,near-stat)) + (vector-copy! (-> *tfrag-work* frag-dists) (-> *tfrag-work* frag-dists)) + (dotimes (i (-> this length)) + (collect-stats (-> this arrays i)))) + (none))) -(defmethod collect-stats ((this drawable-tree-lowres-tfrag)) - "Configure normal-terrain statistics and traverse every low-resolution array in this tree." - (when (logtest? *vu1-enable-user* (vu1-renderer-mask tfrag)) - (set! (-> *tfrag-work* vu1-enable-tfrag) (the-as int (logand *vu1-enable-user* (vu1-renderer-mask tfrag)))) - (set! (-> *tfrag-work* vu1-enable-tfrag-near) (the-as int (logand *vu1-enable-user* (vu1-renderer-mask tfrag)))) - (set! (-> *tfrag-work* tr-stat-tfrag) (-> *terrain-stats* tfrag)) - (set! (-> *tfrag-work* tr-stat-tfrag-near) (-> *terrain-stats* tfrag-near)) - (let ((distance-data (-> *tfrag-work* frag-dists quad))) (set! (-> *tfrag-work* frag-dists quad) distance-data)) - (dotimes (i (-> this length)) - (collect-stats (-> this arrays i)))) - (none)) +(define-tfrag-tree-collect-stats drawable-tree-tfrag + tfrag + tfrag + tfrag-near + "Configure normal-terrain statistics and traverse every array in this tree.") -(defmethod collect-stats ((this drawable-tree-trans-tfrag)) - "Configure translucent-terrain statistics and traverse every array in this tree." - (when (logtest? *vu1-enable-user* (vu1-renderer-mask trans-tfrag)) - (set! (-> *tfrag-work* vu1-enable-tfrag) (the-as int (logand *vu1-enable-user* (vu1-renderer-mask trans-tfrag)))) - (set! (-> *tfrag-work* vu1-enable-tfrag-near) (the-as int (logand *vu1-enable-user* (vu1-renderer-mask trans-tfrag)))) - (set! (-> *tfrag-work* tr-stat-tfrag) (-> *terrain-stats* trans-tfrag)) - (set! (-> *tfrag-work* tr-stat-tfrag-near) (-> *terrain-stats* trans-tfrag-near)) - (let ((distance-data (-> *tfrag-work* frag-dists quad))) (set! (-> *tfrag-work* frag-dists quad) distance-data)) - (dotimes (i (-> this length)) - (collect-stats (-> this arrays i)))) - (none)) +(define-tfrag-tree-collect-stats drawable-tree-lowres-tfrag + tfrag + tfrag + tfrag-near + "Configure normal-terrain statistics and traverse every low-resolution array in this tree.") -(defmethod collect-stats ((this drawable-tree-lowres-trans-tfrag)) - "Configure translucent-terrain statistics and traverse every low-resolution array in this - tree." - (when (logtest? *vu1-enable-user* (vu1-renderer-mask trans-tfrag)) - (set! (-> *tfrag-work* vu1-enable-tfrag) (the-as int (logand *vu1-enable-user* (vu1-renderer-mask trans-tfrag)))) - (set! (-> *tfrag-work* vu1-enable-tfrag-near) (the-as int (logand *vu1-enable-user* (vu1-renderer-mask trans-tfrag)))) - (set! (-> *tfrag-work* tr-stat-tfrag) (-> *terrain-stats* trans-tfrag)) - (set! (-> *tfrag-work* tr-stat-tfrag-near) (-> *terrain-stats* trans-tfrag-near)) - (let ((distance-data (-> *tfrag-work* frag-dists quad))) (set! (-> *tfrag-work* frag-dists quad) distance-data)) - (dotimes (i (-> this length)) - (collect-stats (-> this arrays i)))) - (none)) +(define-tfrag-tree-collect-stats drawable-tree-trans-tfrag + trans-tfrag + trans-tfrag + trans-tfrag-near + "Configure translucent-terrain statistics and traverse every array in this tree.") -(defmethod collect-stats ((this drawable-tree-dirt-tfrag)) - "Configure translucent-terrain statistics and traverse every dirt array in this tree." - (when (logtest? *vu1-enable-user* (vu1-renderer-mask trans-tfrag)) - (set! (-> *tfrag-work* vu1-enable-tfrag) (the-as int (logand *vu1-enable-user* (vu1-renderer-mask trans-tfrag)))) - (set! (-> *tfrag-work* vu1-enable-tfrag-near) (the-as int (logand *vu1-enable-user* (vu1-renderer-mask trans-tfrag)))) - (set! (-> *tfrag-work* tr-stat-tfrag) (-> *terrain-stats* trans-tfrag)) - (set! (-> *tfrag-work* tr-stat-tfrag-near) (-> *terrain-stats* trans-tfrag-near)) - (let ((distance-data (-> *tfrag-work* frag-dists quad))) (set! (-> *tfrag-work* frag-dists quad) distance-data)) - (dotimes (i (-> this length)) - (collect-stats (-> this arrays i)))) - (none)) +(define-tfrag-tree-collect-stats drawable-tree-lowres-trans-tfrag + trans-tfrag + trans-tfrag + trans-tfrag-near + "Configure translucent-terrain statistics and traverse every low-resolution array in this tree.") -(defmethod collect-stats ((this drawable-tree-ice-tfrag)) - "Configure translucent-terrain statistics and traverse every ice array in this tree." - (when (logtest? *vu1-enable-user* (vu1-renderer-mask trans-tfrag)) - (set! (-> *tfrag-work* vu1-enable-tfrag) (the-as int (logand *vu1-enable-user* (vu1-renderer-mask trans-tfrag)))) - (set! (-> *tfrag-work* vu1-enable-tfrag-near) (the-as int (logand *vu1-enable-user* (vu1-renderer-mask trans-tfrag)))) - (set! (-> *tfrag-work* tr-stat-tfrag) (-> *terrain-stats* trans-tfrag)) - (set! (-> *tfrag-work* tr-stat-tfrag-near) (-> *terrain-stats* trans-tfrag-near)) - (let ((distance-data (-> *tfrag-work* frag-dists quad))) (set! (-> *tfrag-work* frag-dists quad) distance-data)) - (dotimes (i (-> this length)) - (collect-stats (-> this arrays i)))) - (none)) +(define-tfrag-tree-collect-stats drawable-tree-dirt-tfrag + trans-tfrag + trans-tfrag + trans-tfrag-near + "Configure translucent-terrain statistics and traverse every dirt array in this tree.") -(defmethod collect-stats ((this drawable-inline-array-tfrag)) - "Accumulate statistics for the visible fragments in this inline array." - (when (logtest? *vu1-enable-user* (vu1-renderer-mask tfrag)) - (dotimes (i (-> this length)) - (let ((fragment (-> this data i))) (if (vis-cull (-> fragment id)) (collect-stats fragment))))) - (none)) +(define-tfrag-tree-collect-stats drawable-tree-ice-tfrag + trans-tfrag + trans-tfrag + trans-tfrag-near + "Configure translucent-terrain statistics and traverse every ice array in this tree.") -(defmethod collect-stats ((this drawable-inline-array-trans-tfrag)) - "Accumulate statistics for the visible translucent fragments in this inline array." - (when (logtest? *vu1-enable-user* (vu1-renderer-mask trans-tfrag)) - (dotimes (i (-> this length)) - (let ((fragment (-> this data i))) (if (vis-cull (-> fragment id)) (collect-stats fragment))))) - (none)) +(defmacro define-tfrag-inline-array-collect-stats (array-type renderer-mask docstring) + `(defmethod collect-stats ((this ,array-type)) + ,docstring + (when (logtest? *vu1-enable-user* (vu1-renderer-mask ,renderer-mask)) + (dotimes (i (-> this length)) + (let ((fragment (-> this data i))) + (if (vis-cull (-> fragment id)) + (collect-stats fragment))))) + (none))) -(defmethod debug-draw ((this drawable-tree-tfrag) (submitted-tree drawable) (frame display-frame)) - "Draw debug geometry for every array in this tree while terrain rendering is enabled." - (when (logtest? *vu1-enable-user* (vu1-renderer-mask tfrag)) - (dotimes (i (-> this length)) - (let ((child (-> this arrays i))) (debug-draw child child frame)))) - (none)) +(define-tfrag-inline-array-collect-stats drawable-inline-array-tfrag + tfrag + "Accumulate statistics for the visible fragments in this inline array.") -(defmethod debug-draw ((this drawable-tree-trans-tfrag) (submitted-tree drawable) (frame display-frame)) - "Draw debug geometry for every array in this tree while terrain rendering is enabled." - (when (logtest? *vu1-enable-user* (vu1-renderer-mask tfrag)) - (dotimes (i (-> this length)) - (let ((child (-> this arrays i))) (debug-draw child child frame)))) - (none)) +(define-tfrag-inline-array-collect-stats drawable-inline-array-trans-tfrag + trans-tfrag + "Accumulate statistics for the visible translucent fragments in this inline array.") + +;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; debug methods +;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defmacro define-tfrag-tree-debug-draw (tree-type renderer-mask) + `(defmethod debug-draw ((this ,tree-type) (submitted-tree drawable) (frame display-frame)) + "Draw debug geometry for every array in this tree while terrain rendering is enabled." + (when (logtest? *vu1-enable-user* (vu1-renderer-mask ,renderer-mask)) + (dotimes (i (-> this length)) + (let ((child (-> this arrays i))) + (debug-draw child child frame)))) + (none))) + +(define-tfrag-tree-debug-draw drawable-tree-tfrag tfrag) + +(define-tfrag-tree-debug-draw drawable-tree-trans-tfrag tfrag) (defmethod debug-draw ((this drawable-inline-array-tfrag) (submitted-array drawable) (frame display-frame)) "Draw debug geometry for every visible fragment in this inline array." (dotimes (i (-> this length)) - (let ((fragment (-> this data i))) (if (vis-cull (-> fragment id)) (debug-draw fragment fragment frame)))) + (let ((fragment (-> this data i))) + (if (vis-cull (-> fragment id)) + (debug-draw fragment fragment frame)))) (none)) (defmethod debug-draw ((this tfragment) (submitted-fragment drawable) (frame display-frame)) "Draw this fragment's saved edge-debug geometry." (-> frame global-buf) (edge-debug-lines (-> this debug-data debug-lines)) - (#when PC_PORT - (add-debug-sphere #t - (bucket-id debug) - (-> this bsphere) - (-> this bsphere w) - (new 'static 'rgba :r #xff :g #xff :b #xff :a #x80))) - ;; (add-debug-x #t (bucket-id debug) (-> this bsphere) (new 'static 'rgba :r #xff :g #xff :b #xff :a #x80)) (none)) diff --git a/goal_src/jak1/engine/gfx/tfrag/tfrag-near.gc b/goal_src/jak1/engine/gfx/tfrag/tfrag-near.gc index c776ab80fb..98d56b4b8e 100644 --- a/goal_src/jak1/engine/gfx/tfrag/tfrag-near.gc +++ b/goal_src/jak1/engine/gfx/tfrag/tfrag-near.gc @@ -5,8 +5,10 @@ (require "engine/gfx/tfrag/tfrag-h.gc") (require "engine/math/matrix-h.gc") (require "engine/camera/cam-debug-h.gc") + +;; TFRAG-NEAR renders tfrags with clipping for triangles that intersect the view frustum. +;; This is unused on PC where OpenGL handles clipping in the normal path. (#when PC_PORT - ;; The PC terrain renderer consumes extracted mesh data and does not execute this VU1 program. (define tnear-vu1-block (new 'static 'vu-function :length #x0 :qlength #x0))) ;; VU1 data memory for the near-terrain program. The two input banks and the shared frame constants sit diff --git a/goal_src/jak1/engine/gfx/tfrag/tfrag-work.gc b/goal_src/jak1/engine/gfx/tfrag/tfrag-work.gc index 2a617b84a2..b0b17250f9 100644 --- a/goal_src/jak1/engine/gfx/tfrag/tfrag-work.gc +++ b/goal_src/jak1/engine/gfx/tfrag/tfrag-work.gc @@ -57,7 +57,7 @@ ;; lookup starts at scratchpad byte 6160, one quadword past the colors overlay's nominal start. ;; ;; Colors are the only part of a terrain patch that is not already sitting in the level file as a VIF -;; chain, because they follow the time of day. The CNT template above unpacks them V4-8 on a 1/2 cycle +;; chain, because they change based on time-of-day. The CNT template above unpacks them V4-8 on a 1/2 cycle ;; so they land in the odd VU quadwords, which is exactly the space the vertex positions leave empty ;; when they unpack on a 2/1 cycle - one vertex is a position quadword followed by its color quadword. ;; tfragment.color-offset is where in that interleave the patch's colors start. diff --git a/goal_src/jak1/engine/gfx/tfrag/tfrag.gc b/goal_src/jak1/engine/gfx/tfrag/tfrag.gc index 3839ee6426..817c0e73ec 100644 --- a/goal_src/jak1/engine/gfx/tfrag/tfrag.gc +++ b/goal_src/jak1/engine/gfx/tfrag/tfrag.gc @@ -6,13 +6,10 @@ (require "engine/gfx/background/background-h.gc") (require "engine/gfx/tfrag/tfrag-near.gc") -;; Terrain fragments are collected into inline arrays under a drawable tree. The background -;; renderer queues those trees, then builds the terrain DMA stream here after visibility has been -;; computed. ;; ;; How the work splits between the EE and VU1 ;; ------------------------------------------ -;; The EE never touches a vertex. Terrain geometry lives in the level file as ready-made VIF chains, +;; The EE never touches a vertex. TFRAG geometry lives in the level file as ready-made VIF chains, ;; three per patch, and all the EE does is decide which chain to reference and which microprogram ;; entry to call. Per batch of sixteen fragments it: ;; @@ -24,7 +21,7 @@ ;; *tfrag-work*.color-ptr and appends them as a CNT packet, because the time-of-day palette ;; changes every frame while the geometry never does; ;; - writes REF tags pointing at the level's chains, plus an MSCAL, into one of two 128-quadword -;; output banks and hands the full one to the VIF DMA while filling the other. +;; output banks and hands the full one to the DMA while filling the other. ;; ;; VU1 owns everything geometric. It unpacks the chain into one of two 328-quadword input banks ;; (alternating, so the next patch uploads while this one draws), runs the LOD collapse over the @@ -32,8 +29,7 @@ ;; one of two GIF buffers, and kicks. Shared constants sit above both input banks and are uploaded ;; once per frame by add-tfrag-data. ;; -;; The detail hierarchy, because the names run the opposite way to the detail and a reader will -;; otherwise assume "base" is the finest +;; The detail hierarchy: ;; -------------------------------------------------------------------------------------------------- ;; A patch's vertices are one nested list: the base vertices, then the level-0 additions, then the ;; level-1 additions. Each addition splits an edge of the mesh below it, so: @@ -42,66 +38,90 @@ ;; level-0 stream base + level-0 additions ;; level-1 stream base + level-0 + level-1 additions, finest, drawn when CLOSEST ;; -;; The classifier steps DOWN, level 1 to level 0 to base, as the patch recedes, and the VU unwinds one -;; subdivision level at a time, the coarser one first. Unwinding slides each added point onto the +;; The classifier steps DOWN, level 1 to level 0 to base, as the patch recedes, and the VU collapses one +;; subdivision level at a time, the coarser one first. Collapsing slides each added point onto the ;; midpoint of the edge it splits, so it never merges into another point on the way; it just stops ;; contributing, and the level below it is then an exact description of the same surface. A point's -;; parents are always at a strictly coarser level, so the hierarchy is only ever two deep and nothing -;; is chasing anything. +;; parents are always at a strictly coarser level. ;; ;; What spans the EE and VU halves is the seam rule: the VU decides how far a point has been unwound ;; from that point's own depth and per-frame globals only, and the EE only drops a subdivision level -;; once the WHOLE bounding sphere is past the distance where that level has finished unwinding. Those +;; once the WHOLE bounding sphere is past the distance where that level has finished collapsing. Those ;; two together mean a patch never abandons detail that a neighbour is still displaying. See ;; classify-fragment-lod below for the part of the argument that lives on this side. ;; VU1 data-memory map. Quadword addresses; the two input banks alternate under VIF TOP, so a ;; bank-relative address is used directly as an offset from the TOP register. (defconstant TFRAG-VU-BANK-0 0) + (defconstant TFRAG-VU-BANK-1 328) -(defconstant TFRAG-VU-CONTROL 0) ;; tfrag-control, five quadwords, bank relative -(defconstant TFRAG-VU-CAMERA 5) ;; camera transform, four rows, bank relative + +(defconstant TFRAG-VU-CONTROL 0) ;; tfrag-control, five quadwords, bank relative + +(defconstant TFRAG-VU-CAMERA 5) ;; camera transform, four rows, bank relative ;; The fourteen shared quadwords of tfrag-data, uploaded once per frame at TFRAG-VU-CONSTANTS. (defconstant TFRAG-VU-CONSTANTS 656) + (defconstant TFRAG-VU-FOG 656) -(defconstant TFRAG-VU-VAL 657) ;; (0.5, 1.0, 2048.0, 0.0) + +(defconstant TFRAG-VU-VAL 657) ;; (0.5, 1.0, 2048.0, 0.0) + (defconstant TFRAG-VU-STRGIF 658) + (defconstant TFRAG-VU-FANGIF 659) + (defconstant TFRAG-VU-ADGIF 660) + (defconstant TFRAG-VU-HVDF-OFFSET 661) + (defconstant TFRAG-VU-HMGE-SCALE 662) + (defconstant TFRAG-VU-INVH-SCALE 663) + (defconstant TFRAG-VU-AMBIENT 664) + (defconstant TFRAG-VU-GUARD 665) + ;; tfrag-dists: slope pair then intercept pair, level 0 then level 1. The w lane of each slope ;; entry is that level's collapse distance. (defconstant TFRAG-VU-K0-LEVEL0 666) + (defconstant TFRAG-VU-K0-LEVEL1 667) + (defconstant TFRAG-VU-K1-LEVEL0 668) + (defconstant TFRAG-VU-K1-LEVEL1 669) ;; Two GIF output buffers. One is written while the other is being read out by xgkick. (defconstant TFRAG-VU-GIF-BUF-0 672) + (defconstant TFRAG-VU-GIF-BUF-1 848) ;; Scratchpad bank alternation. Each mask is whatever bits the two bank offsets disagree on, which for ;; the packet banks is not the bank size: they are 2048 bytes long but sit at 2048 and 4096, so the ;; mask is #x1800. As a literal that looks like a typo, so derive it. (defconstant TFRAG-INPUT-BANK-FLIP (logxor (offset-of tfrag-dma banka) (offset-of tfrag-dma bankb))) + (defconstant TFRAG-PACKET-BANK-FLIP (logxor (offset-of tfrag-dma outa) (offset-of tfrag-dma outb))) ;; Microprogram entry points, as the EE spells them in its MSCAL immediates. These seven are the ones ;; the EE actually selects; the other seven even addresses either terminate at once or duplicate ;; TFRAG-VU-DRAW-BASE, and nothing reaches them. The names are reconstructed - the original source's ;; names for them are not recovered. -(defconstant TFRAG-VU-INIT 0) ;; set up both GIF buffers, once per frame -(defconstant TFRAG-VU-DRAW-BASE 6) ;; the coarsest mesh; no simplification to do -(defconstant TFRAG-VU-DRAW-LEVEL0 10) ;; level 0, whole patch inside its distance band -(defconstant TFRAG-VU-DRAW-LEVEL0-CROSSING 8) ;; level 0, patch straddles the far end of that band -(defconstant TFRAG-VU-DRAW-LEVEL1 16) ;; level 1, whole patch inside its distance band -(defconstant TFRAG-VU-DRAW-LEVEL1-CROSSING 14) ;; level 1, patch straddles the far end of that band -(defconstant TFRAG-VU-DRAW-LEVEL1-CLIPPED 18) ;; level 1, near enough to need the guard-volume clip +(defconstant TFRAG-VU-INIT 0) ;; set up both GIF buffers, once per frame + +(defconstant TFRAG-VU-DRAW-BASE 6) ;; the coarsest mesh; no simplification to do + +(defconstant TFRAG-VU-DRAW-LEVEL0 10) ;; level 0, whole patch inside its distance band + +(defconstant TFRAG-VU-DRAW-LEVEL0-CROSSING 8) ;; level 0, patch straddles the far end of that band + +(defconstant TFRAG-VU-DRAW-LEVEL1 16) ;; level 1, whole patch inside its distance band + +(defconstant TFRAG-VU-DRAW-LEVEL1-CROSSING 14) ;; level 1, patch straddles the far end of that band + +(defconstant TFRAG-VU-DRAW-LEVEL1-CLIPPED 18) ;; level 1, near enough to need the guard-volume clip ;;;;;;;;;;;;;;;;;;;;;;;; ;; basic methods @@ -167,8 +187,8 @@ (set! (-> usage data (+ category 5) name) "tfragment-color") (+! (-> usage data (+ category 5) count) 1) (let ((packed-color-bytes (if (logtest? flags (mem-usage-flags prototype-data)) - 0 - (the-as int (* (+ (-> this num-base-colors) (-> this num-level0-colors) (-> this num-level1-colors)) 2))))) + 0 + (the-as int (* (+ (-> this num-base-colors) (-> this num-level0-colors) (-> this num-level1-colors)) 2))))) (+! (-> usage data (+ category 5) used) packed-color-bytes) (+! (-> usage data (+ category 5) total) (logand -16 (+ packed-color-bytes 15)))) (set! (-> usage data (+ category 6) name) "tfragment-debug")) @@ -254,30 +274,22 @@ ;; position = own * weight.y + (endpoint1 + endpoint2) * weight.x ;; ;; with weight.y falling 1 -> 0 and weight.x rising 0 -> 0.5 across the level's distance band, so the -;; far end of the slide is exactly the midpoint of the two endpoints. Color blends the same way, and -;; the texture coordinate needs no blend because the exporter already stores the edge midpoint. +;; far end of the slide is exactly the midpoint of the two endpoints. Color blends the same way. +;; It's important that these weights are computed per-vertex rather than per-patch, so a vertex +;; shared in two-patches is drawn at the same position in both patches. ;; -;; EDGE RETIREMENT is the second half of it, and the term is worth pinning down because everything -;; below refers to it. A vertex is authored displaced from the midpoint - that displacement is the -;; detail it exists to add - so its own distance is the midpoint's distance plus whatever the -;; displacement contributes. That means a vertex can still be short of the end of its band while the -;; edge it splits is entirely past it, and the slide alone would leave a residual bump on an edge that -;; is supposed to be flat by now. So the program also asks a question about the EDGE rather than the -;; vertex: are BOTH of its endpoints past the distance where this level finishes simplifying? When they -;; are, the edge has RETIRED, and the vertex is forced the rest of the way rather than interpolated -;; there - its point record is overwritten with the record of one endpoint, so it draws as a duplicate -;; of that endpoint and its triangles go to zero area. Same resulting surface, reached in one step. +;; There is one edge case: when the endpoints of an edge are past the band, but the displaced vertex is not. +;; In this case, adjacent fragments could disagree on whether to upload the midpoint vertex. +;; Their solution here is to simply "retire" the midpoint vertex if both edge endpoints are in the lower-lod region. +;; This likely introduces a tiny pop as the decision to retire is made, but avoids any t-junction artifacts. ;; -;; The reason for asking about the endpoints instead of the vertex is that the endpoints are shared with -;; the neighbouring patch and are not displaced, so both patches reach the same answer at the same -;; camera distance. That is the whole seam argument; classify-fragment-lod carries the rest of it. ;; ;; -------------------------------------------------------------------------------------------------- ;; 3. The data layout ;; -------------------------------------------------------------------------------------------------- -;; Six kinds of thing, all uploaded into one VU input bank, all located by tfrag-control at its front: +;; This is confusing: there's two levels of indirection for vertex data here. ;; -;; vertices position and color, and nothing else ptr-vtxdata +;; vertices position and color, and nothing else ptr-vtxdata ;; point records texture coordinate, plus which vertex to use ptr-base/level0/level1-points ;; endpoint tables the second endpoint of each split edge ptr-interpolated-0/1 ;; the index list which point records to draw, in order ptr-draw-points @@ -319,7 +331,7 @@ ;; wrap seam, a shader change - gets a SHARED record instead: same vertex address, a different texture ;; coordinate, no endpoint data of its own, and crucially not walked by the blend loop. A second main ;; record would blend the same vertex twice; that is why the shared records are a separate array with a -;; separate count. "Main" is descriptive; the control block names only the shared ones. +;; separate count. ;; ;; The consequence is retirement. Retiring a point rewrites a record, and every record naming that point ;; has to be rewritten or it will still point at a vertex the blend loop never stored. The blend pass @@ -327,8 +339,7 @@ ;; tables, so it can run the same both-endpoints test. ;; ;; Each level's shared array ABUTS its main array exactly: ptr-shared-level0-points is -;; ptr-level0-points + num-level0-points, and likewise for base and level 1. Contiguous and disjoint, -;; which is what lets one VIF command unpack both. +;; ptr-level0-points + num-level0-points, and likewise for base and level 1. One VIF unpack handles both. ;; ;; An ENDPOINT TABLE holds the OTHER endpoint of each split edge - the one the record's z lane does not ;; name. There is one per level, because each level's points split the edges of a different mesh: @@ -371,15 +382,12 @@ ;; -------------------------------------------------------------------------------------------------- ;; 4. The VU program ;; -------------------------------------------------------------------------------------------------- -;; Buffering first, because every loop below exists in two copies because of it. ;; ;; input banks two, double-buffered by the VIF. Every entry begins with xtop, and every pass and ;; renderer has a duplicated bank-1 copy of itself selected by ibne against ;; input-bank, because the load and store immediates differ by TFRAG-VU-BANK-1. ;; Handover is the VIF's, at the MSCAL. -;; vertex array double-buffered for free: ptr-vtxdata is bank-relative, so the transformed -;; vertices live in the input bank and ARE the uploaded data, rewritten in place. -;; No second allocation and nothing copied to get there. +;; vertex array vertices are modified in the input bank directly ;; GIF output two buffers, addresses in the x and y lanes of gif-buffers, swapped by mr32 at ;; every kick. Two is enough because the gather writes through one sequential ;; cursor, so only one buffer is ever being filled. @@ -394,8 +402,7 @@ ;; and it is what the LEVEL1 entries use instead. ;; ;; There is no pass that transforms every vertex. Whatever the prefix does not cover is transformed -;; by the blend loop that owns it, which is why a straight-through walk can coexist with vertices -;; that end up never transformed at all - see pass 3. +;; by the blend loop. ;; 2. morph-level0-points. Walks ptr-level0-points; for each record, loads the vertex it names, ;; transforms it, slides it toward the midpoint of its two endpoints by that vertex's own distance, ;; and stores it back in place. So this loop is where a level-0 vertex gets transformed, not pass 1. @@ -501,18 +508,6 @@ ;; ;; See tie.gc for that side; this comment does not repeat it. ;; -;; -------------------------------------------------------------------------------------------------- -;; Whose words these are -;; -------------------------------------------------------------------------------------------------- -;; tfrag-control supplies the vocabulary for everything structural. From it: points, and base, level-0 -;; and level-1 as their groups; shared; interpolated, which is what the game calls a point that splits -;; an edge and hence the table naming its second endpoint; vtxdata, draw-points, strip-data, -;; texture-data. -;; -;; Endpoint, midpoint, retire, collapse and duplicate are descriptive terms adopted while reading the -;; instruction stream, as are the entry-point constant names above. Nothing in the data distinguishes -;; the two endpoints of an edge by name, so "parent A" and "parent B" in the register aliases below are -;; only a way of telling the z lane from the endpoint-table entry. ;; ;; -------------------------------------------------------------------------------------------------- ;; Register conventions @@ -526,9 +521,9 @@ ;; own nested rlet and only the values that live for its whole body are named. (#unless PC_PORT (defvu1 tfrag-vu1-block - (rlet ((input-bank :reg vi14) ;; VIF TOP: 0 or TFRAG-VU-BANK-1 - (subroutine-link :reg vi15) ;; bal link, or a resume address loaded by hand - (fog :reg vf01)) ;; TFRAG-VU-FOG; never holds anything else + (rlet ((input-bank :reg vi14) ;; VIF TOP: 0 or TFRAG-VU-BANK-1 + (subroutine-link :reg vi15) ;; bal link, or a resume address loaded by hand + (fog :reg vf01)) ;; TFRAG-VU-FOG; never holds anything else ;; Even entry addresses. Two things vary: how much preparation the incoming stream needs before a ;; renderer can look at it, and whether that renderer has to guard-clip. Entries 4, 6, 12 and 20 have no ;; preparation at all, so their renderer converts and transforms positions itself; the rest hand their @@ -627,14 +622,14 @@ (vu-pair (mfir.z vf03 vi14) (nop)) ;; #x04c (vu-pair (mfir.w vf03 vi01) (nop :e)) ;; #x04d (vu-pair (lq.xyzw vf04 vi00 TFRAG-VU-AMBIENT) (nop)) ;; #x04e - (rlet ((input-bank :reg vi14) ;; VIF TOP: 0 or TFRAG-VU-BANK-1 - (subroutine-link :reg vi15) ;; bal link, or a resume address loaded by hand - (fog :reg vf01)) ;; TFRAG-VU-FOG; never holds anything else - (rlet ((points-left :reg vi02) ;; how many points still to transform + (rlet ((input-bank :reg vi14) ;; VIF TOP: 0 or TFRAG-VU-BANK-1 + (subroutine-link :reg vi15) ;; bal link, or a resume address loaded by hand + (fog :reg vf01)) ;; TFRAG-VU-FOG; never holds anything else + (rlet ((points-left :reg vi02) ;; how many points still to transform (base-count :reg vi04) (level1-count :reg vi01) - (load-cursor :reg vi05) ;; walks the vertex array - (store-cursor :reg vi06) ;; trails it by three vertices + (load-cursor :reg vi05) ;; walks the vertex array + (store-cursor :reg vi06) ;; trails it by three vertices (val :reg vf02) (cam-row-x :reg vf05) (cam-row-y :reg vf06) @@ -644,7 +639,7 @@ (pos-1 :reg vf13) (pos-2 :reg vf14) (pos-3 :reg vf15) - (color-0 :reg vf16) ;; colors only get an itof + (color-0 :reg vf16) ;; colors only get an itof (color-1 :reg vf17) (color-2 :reg vf18) (color-3 :reg vf19)) @@ -723,15 +718,15 @@ (vu-pair (jr subroutine-link) (nop)) ;; #x088 (vu-pair (nop) (nop))) ;; #x089 (rlet ((points-left :reg vi02) - (point-cursor :reg vi03) ;; this level's point records - (endpoint-cursor :reg vi04) ;; walks the level's endpoint table, four addresses per quadword + (point-cursor :reg vi03) ;; this level's point records + (endpoint-cursor :reg vi04) ;; walks the level's endpoint table, four addresses per quadword (own-vertex-0 :reg vi05) (own-vertex-1 :reg vi06) (parent-record :reg vi07) (parent-b-vertex :reg vi08) (parent-a-vertex :reg vi09) (store-vertex :reg vi10) - (val :reg vf02) ;; (0.5, 1.0, 2048.0, 0.0) + (val :reg vf02) ;; (0.5, 1.0, 2048.0, 0.0) (cam-row-x :reg vf05) (cam-row-y :reg vf06) (cam-row-z :reg vf07) @@ -742,8 +737,8 @@ (color-1 :reg vf15) (weight-0 :reg vf16) (weight-1 :reg vf17) - (k0 :reg vf18) ;; slope pair for this level - (k1 :reg vf19) ;; intercept pair + (k0 :reg vf18) ;; slope pair for this level + (k1 :reg vf19) ;; intercept pair (parent-b-pos :reg vf20) (parent-b-color :reg vf21) (parent-a-pos :reg vf22) @@ -1006,7 +1001,7 @@ (parent-b-vertex :reg vi09) (parent-a-vertex :reg vi10) (store-vertex :reg vi11) - (parent-b-collapsed :reg vi01) ;; sign flag of parent-b.w - collapse-dist + (parent-b-collapsed :reg vi01) ;; sign flag of parent-b.w - collapse-dist (parent-a-collapsed :reg vi12) (val :reg vf02) (cam-row-x :reg vf05) @@ -1027,7 +1022,7 @@ (parent-a-color :reg vf23) (parent-sum-pos :reg vf24) (parent-sum-color :reg vf25) - (collapse-dist :reg vf26) ;; k0s[level].w, broadcast in w only + (collapse-dist :reg vf26) ;; k0s[level].w, broadcast in w only (replacement-record :reg vf27)) ;; The same unwind, with the edge-retirement test added. Everything above is driven by the point's OWN ;; depth, and a point is authored displaced from the midpoint - that displacement is the detail it exists @@ -1421,7 +1416,8 @@ (vu-pair (ilw.w own-vertex-0 point-cursor (+ TFRAG-VU-BANK-1 2)) (mulaw.xy ACC k1 vf00)) ;; #x25d (vu-pair (nop) (maddw.xy weight-1 k0 pos-1)) ;; #x25e (vu-pair (ibne vi00 points-left collapse-points-bank1-stage1) (add.xyzw parent-sum-pos parent-b-pos parent-a-pos)) ;; #x25f - (vu-pair (sq.xyzw replacement-record point-cursor TFRAG-VU-BANK-1) (add.xyzw parent-sum-color parent-b-color parent-a-color)) ;; #x260 + (vu-pair (sq.xyzw replacement-record point-cursor TFRAG-VU-BANK-1) + (add.xyzw parent-sum-color parent-b-color parent-a-color)) ;; #x260 (vu-pair (jr subroutine-link) (nop)) ;; #x261 (vu-pair (nop) (nop)) ;; #x262 (label substitute-parent-bank1-stage1) @@ -1429,7 +1425,8 @@ (vu-pair (ilw.w own-vertex-1 point-cursor (+ TFRAG-VU-BANK-1 3)) (mulaw.xy ACC k1 vf00)) ;; #x264 (vu-pair (nop) (maddw.xy weight-0 k0 pos-0)) ;; #x265 (vu-pair (ibne vi00 points-left collapse-points-bank1-stage2) (add.xyzw parent-sum-pos parent-b-pos parent-a-pos)) ;; #x266 - (vu-pair (sq.xyzw replacement-record point-cursor (+ TFRAG-VU-BANK-1 1)) (add.xyzw parent-sum-color parent-b-color parent-a-color)) ;; #x267 + (vu-pair (sq.xyzw replacement-record point-cursor (+ TFRAG-VU-BANK-1 1)) + (add.xyzw parent-sum-color parent-b-color parent-a-color)) ;; #x267 (vu-pair (jr subroutine-link) (nop)) ;; #x268 (vu-pair (nop) (nop)) ;; #x269 (label substitute-parent-bank1-stage2) @@ -1437,7 +1434,8 @@ (vu-pair (ilw.w own-vertex-0 point-cursor (+ TFRAG-VU-BANK-1 4)) (mulaw.xy ACC k1 vf00)) ;; #x26b (vu-pair (iaddi endpoint-cursor endpoint-cursor 0x1) (maddw.xy weight-1 k0 pos-1)) ;; #x26c (vu-pair (ibne vi00 points-left collapse-points-bank1-stage3) (add.xyzw parent-sum-pos parent-b-pos parent-a-pos)) ;; #x26d - (vu-pair (sq.xyzw replacement-record point-cursor (+ TFRAG-VU-BANK-1 2)) (add.xyzw parent-sum-color parent-b-color parent-a-color)) ;; #x26e + (vu-pair (sq.xyzw replacement-record point-cursor (+ TFRAG-VU-BANK-1 2)) + (add.xyzw parent-sum-color parent-b-color parent-a-color)) ;; #x26e (vu-pair (jr subroutine-link) (nop)) ;; #x26f (vu-pair (nop) (nop)) ;; #x270 (label substitute-parent-bank1-stage3) @@ -1445,7 +1443,8 @@ (vu-pair (ilw.w own-vertex-1 point-cursor (+ TFRAG-VU-BANK-1 5)) (mulaw.xy ACC k1 vf00)) ;; #x272 (vu-pair (iaddi point-cursor point-cursor 0x4) (maddw.xy weight-0 k0 pos-0)) ;; #x273 (vu-pair (ibne vi00 points-left collapse-points-bank1-stage0) (add.xyzw parent-sum-pos parent-b-pos parent-a-pos)) ;; #x274 - (vu-pair (sq.xyzw replacement-record point-cursor (+ TFRAG-VU-BANK-1 -1)) (add.xyzw parent-sum-color parent-b-color parent-a-color)) ;; #x275 + (vu-pair (sq.xyzw replacement-record point-cursor (+ TFRAG-VU-BANK-1 -1)) + (add.xyzw parent-sum-color parent-b-color parent-a-color)) ;; #x275 (vu-pair (jr subroutine-link) (nop)) ;; #x276 (vu-pair (nop) (nop))) ;; #x277 (rlet ((shared-count :reg vi11) @@ -1459,7 +1458,7 @@ (parent-b-vertex :reg vi08) (parent-a-vertex :reg vi09) (parent-collapsed :reg vi01) - (point-record :reg vf06) ;; read with lqi, so its lanes come out through mtir + (point-record :reg vf06) ;; read with lqi, so its lanes come out through mtir (parent-b-pos :reg vf07) (parent-a-pos :reg vf08) (replacement-record :reg vf09) @@ -1700,25 +1699,25 @@ (vu-pair (iaddi endpoint-cursor endpoint-cursor 0x1) (nop)) ;; #x32f (vu-pair (b collapse-shared-bank1-stage0) (nop)) ;; #x330 (vu-pair (lqi.xyzw point-record point-cursor) (subw.w vf00 parent-a-pos collapse-dist))) ;; #x331 - (rlet ((gif-buffers :reg vf03) ;; the two GIF buffer addresses, rotated by mr32 + (rlet ((gif-buffers :reg vf03) ;; the two GIF buffer addresses, rotated by mr32 (adgif-tag :reg vf05) (strip-tag :reg vf06) - (shader-0 :reg vf29) ;; the five adgif quadwords, copied straight through + (shader-0 :reg vf29) ;; the five adgif quadwords, copied straight through (shader-1 :reg vf30) (shader-2 :reg vf31) - (draw-point :reg vi02) ;; the point record draw-cursor currently names - (draw-cursor :reg vi03) ;; walks ptr-draw-points - (vertex :reg vi04) ;; the vertex that record names: position, then color - (chunk-end :reg vi05) ;; last quadword this chunk may write + (draw-point :reg vi02) ;; the point record draw-cursor currently names + (draw-cursor :reg vi03) ;; walks ptr-draw-points + (vertex :reg vi04) ;; the vertex that record names: position, then color + (chunk-end :reg vi05) ;; last quadword this chunk may write (gif-cursor :reg vi06) (shader-base :reg vi08) - (strip-cursor :reg vi09) ;; walks ptr-strip-data - (chunk-start :reg vi10) ;; the strip tag whose NLOOP gets patched - (gif-eop :reg vi11) ;; GIF tag EOP bit; two adds because iaddiu is 15 bits + (strip-cursor :reg vi09) ;; walks ptr-strip-data + (chunk-start :reg vi10) ;; the strip tag whose NLOOP gets patched + (gif-eop :reg vi11) ;; GIF tag EOP bit; two adds because iaddiu is 15 bits (strip-verts :reg vi12) (shader-cursor :reg vi13)) - (rlet ((tag-word :reg vi07) ;; a strip tag's first word, read back to OR in EOP - (ambient :reg vf04)) ;; only in the terminating pair + (rlet ((tag-word :reg vi07) ;; a strip tag's first word, read back to OR in EOP + (ambient :reg vf04)) ;; only in the terminating pair ;; Strip closer shared by three of the four renderers: finish the current GIF chunk, kick it, and pick the ;; next strip length and adgif shader out of the draw address book. The renderers arrive here with a ;; return address they loaded into subroutine-link by hand rather than through bal, so each one can resume @@ -1814,15 +1813,15 @@ (vu-pair (xgkick vi01) (nop)) ;; #x37f (vu-pair (lq.xyzw ambient vi00 TFRAG-VU-AMBIENT) (nop :e)) ;; #x380 (vu-pair (nop) (nop))) ;; #x381 - (rlet ((st-bias :reg vf02) ;; 2048.0, taken out of the shader block + (rlet ((st-bias :reg vf02) ;; 2048.0, taken out of the shader block (hvdf-offset :reg vf07) (hmge-scale :reg vf08) - (point-record :reg vf25) ;; ST and vertex address, read as floats + (point-record :reg vf25) ;; ST and vertex address, read as floats (pos-0 :reg vf09) (pos-1 :reg vf10) (pos-2 :reg vf11) (pos-3 :reg vf12) - (clip-0 :reg vf13) ;; position scaled for the guard-volume test + (clip-0 :reg vf13) ;; position scaled for the guard-volume test (clip-1 :reg vf14) (clip-2 :reg vf15) (clip-3 :reg vf16) @@ -2567,8 +2566,8 @@ ;; vi14's input-bank job is finished by the time a strip closes, so the base-mesh closer ;; borrows it as the tag scratch. Two names for one register rather than one that lies. (rlet ((tag-word :reg vi14) - (vert-tally :reg vi07) ;; reset here; nothing ever reads it - (ambient :reg vf04)) ;; only in the terminating pair + (vert-tally :reg vi07) ;; reset here; nothing ever reads it + (ambient :reg vf04)) ;; only in the terminating pair ;; Strip closer for the base-mesh renderer alone. Same job as the shared one above; a separate copy ;; because the base renderer's register conventions differ from everyone else's. (label close-base-strip) @@ -2662,7 +2661,7 @@ (vu-pair (nop) (nop :e)) ;; #x650 (vu-pair (nop) (nop))) ;; #x651 (rlet ((val :reg vf02) - (vert-tally :reg vi07) ;; counted per vertex and never read + (vert-tally :reg vi07) ;; counted per vertex and never read (hvdf-offset :reg vf10) (hmge-scale :reg vf11) (point-record :reg vf28) @@ -2965,8 +2964,8 @@ alpha-blend selects the GS ABE bit; *subdivide-draw-mode* can replace textured strips and fans with wireframe lines or untextured geometry." (let ((camera *math-camera*)) - (set-vector! (-> data fog) (-> camera pfog0) (-> camera fog-min) (-> camera fog-max) 3072.0) - (set-vector! (-> data val) 0.5 1.0 2048.0 0.0) + (set-vector! (-> data fog) (-> camera pfog0) (-> camera fog-min) (-> camera fog-max) (meters 0.75)) + (set-vector! (-> data val) 0.5 1.0 (meters 0.5) 0.0) (set-vector! (-> data ambient) 1.0 1.0 1.0 1.0) (cond ((zero? *subdivide-draw-mode*) @@ -3014,12 +3013,10 @@ :nreg #x3 :prim (new 'static 'gs-prim :prim (gs-prim-type tri-fan) :iip #x1 :fge #x1 :abe alpha-blend))))) - (set! (-> data strgif regs) - (new 'static 'gif-tag-regs :regs0 (gif-reg-id st) :regs1 (gif-reg-id rgbaq) :regs2 (gif-reg-id xyzf2))) - (set! (-> data fangif regs) - (new 'static 'gif-tag-regs :regs0 (gif-reg-id st) :regs1 (gif-reg-id rgbaq) :regs2 (gif-reg-id xyzf2))) + (set! (-> data strgif regs) (gs-reg-list st rgbaq xyzf2)) + (set! (-> data fangif regs) (gs-reg-list st rgbaq xyzf2)) (set! (-> data adgif tag) (new 'static 'gif-tag64 :nloop #x5 :nreg #x1)) - (set! (-> data adgif regs) (new 'static 'gif-tag-regs :regs0 (gif-reg-id a+d))) + (set! (-> data adgif regs) (gs-reg-list a+d)) (vector-copy! (-> data hvdf-offset) (-> camera hvdf-off)) (vector-copy! (-> data hmge-scale) (-> camera hmge-scale)) (vector-copy! (-> data invh-scale) (-> camera inv-hmge-scale)) @@ -3114,7 +3111,7 @@ (set! (-> data-ptr 21) (-> *math-camera* perspective vector 2 quad)) (set! (-> data-ptr 22) (-> *math-camera* perspective vector 3 quad)) (if (<= 32 (length (symbol->string (bsp-name lev)))) - (format #t "level name ~a is too long!! must be shorter than 32 characters~%" (bsp-name lev))) + (format #t "level name ~a is too long!! must be shorter than 32 characters~%" (bsp-name lev))) (charp<-string (the (pointer uint8) (&-> data-ptr 23)) (symbol->string (bsp-name lev)))) (&+! (-> dma-buf base) (* 16 25))) @@ -3159,97 +3156,41 @@ ;; buffer ;;;;;;;;;;;;;;;;;;;;;;;; -(#unless PC_PORT - (defun tfrag-init-buffer ((dma-buf dma-buffer) (test gs-test) (alpha-blend int)) - "Upload the terrain VU1 program, install TEST_1, both camera-matrix banks, and shared constants, - set the VIF double-buffer base and offset, and clear the frame's terrain counters." - (dma-buffer-add-vu-function dma-buf tfrag-vu1-block 1) - (let* ((dma-state dma-buf) - (direct-packet (the-as object (-> dma-state base)))) - (set! (-> (the-as dma-packet direct-packet) dma) (new 'static 'dma-tag :qwc #x2 :id (dma-tag-id cnt))) - (set! (-> (the-as dma-packet direct-packet) vif0) (new 'static 'vif-tag)) - (set! (-> (the-as dma-packet direct-packet) vif1) (new 'static 'vif-tag :imm #x2 :cmd (vif-cmd direct) :msk #x1)) - (set! (-> dma-state base) (&+ (the-as pointer direct-packet) 16))) - (let* ((dma-state dma-buf) - (giftag (the-as object (-> dma-state base)))) - (set! (-> (the-as gs-gif-tag giftag) tag) (new 'static 'gif-tag64 :nloop #x1 :eop #x1 :nreg #x1)) - (set! (-> (the-as gs-gif-tag giftag) regs) GIF_REGS_ALL_AD) - (set! (-> dma-state base) (&+ (the-as pointer giftag) 16))) - (let* ((dma-state dma-buf) - (test-packet (-> dma-state base))) - (set! (-> (the-as (pointer gs-test) test-packet) 0) test) - (set! (-> (the-as (pointer gs-reg64) test-packet) 1) (gs-reg64 test-1)) - (set! (-> dma-state base) (&+ test-packet 16))) - (add-tfrag-mtx-0 dma-buf) - (add-tfrag-mtx-1 dma-buf) - (add-tfrag-data dma-buf alpha-blend) - (let ((vif-state-packet (the-as object (-> dma-buf base)))) - (set! (-> (the-as dma-packet vif-state-packet) dma) (new 'static 'dma-tag :id (dma-tag-id cnt))) - (set! (-> (the-as dma-packet vif-state-packet) vif0) (new 'static 'vif-tag :cmd (vif-cmd base))) - (set! (-> (the-as dma-packet vif-state-packet) vif1) (new 'static 'vif-tag :imm #x148 :cmd (vif-cmd offset))) - (set! (-> dma-buf base) (&+ (the-as pointer vif-state-packet) 16))) - (set! (-> *tfrag-work* last-call) (the-as uint 0)) - (set! (-> t-stat cnt) 0) - (set! (-> t-stat tris) 0) - (set! (-> t-stat tfaces) 0) - (set! (-> t-stat tfrags) 0) - (set! (-> t-stat dtris) 0) - (set! (-> t-stat dma-cnt) 0) - (set! (-> t-stat dma-dta) 0) - (set! (-> t-stat dma-tex) 0) - (set! (-> t-stat strips) 0) - (set! (-> t-stat drawpoints) 0) - (set! (-> t-stat base-verts) 0) - (set! (-> t-stat level0-verts) 0) - (set! (-> t-stat level1-verts) 0) - (none))) - -(#when PC_PORT - (defun tfrag-init-buffer ((dma-buf dma-buffer) (test gs-test) (alpha-blend int) (lev level)) - "Upload the terrain VU1 program, install TEST_1, both camera-matrix banks, and shared constants, - set the VIF double-buffer base and offset, and clear the frame's terrain counters. The PC - packet also receives the level's time-of-day state and name." - (dma-buffer-add-vu-function dma-buf tfrag-vu1-block 1) - (let* ((dma-state dma-buf) - (direct-packet (the-as object (-> dma-state base)))) - (set! (-> (the-as dma-packet direct-packet) dma) (new 'static 'dma-tag :qwc #x2 :id (dma-tag-id cnt))) - (set! (-> (the-as dma-packet direct-packet) vif0) (new 'static 'vif-tag)) - (set! (-> (the-as dma-packet direct-packet) vif1) (new 'static 'vif-tag :imm #x2 :cmd (vif-cmd direct) :msk #x1)) - (set! (-> dma-state base) (&+ (the-as pointer direct-packet) 16))) - (let* ((dma-state dma-buf) - (giftag (the-as object (-> dma-state base)))) - (set! (-> (the-as gs-gif-tag giftag) tag) (new 'static 'gif-tag64 :nloop #x1 :eop #x1 :nreg #x1)) - (set! (-> (the-as gs-gif-tag giftag) regs) GIF_REGS_ALL_AD) - (set! (-> dma-state base) (&+ (the-as pointer giftag) 16))) - (let* ((dma-state dma-buf) - (test-packet (-> dma-state base))) - (set! (-> (the-as (pointer gs-test) test-packet) 0) test) - (set! (-> (the-as (pointer gs-reg64) test-packet) 1) (gs-reg64 test-1)) - (set! (-> dma-state base) (&+ test-packet 16))) - (add-tfrag-mtx-0 dma-buf) - (add-tfrag-mtx-1 dma-buf) - (add-tfrag-data dma-buf alpha-blend) - (add-pc-tfrag3-data dma-buf lev) - (let ((vif-state-packet (the-as object (-> dma-buf base)))) - (set! (-> (the-as dma-packet vif-state-packet) dma) (new 'static 'dma-tag :id (dma-tag-id cnt))) - (set! (-> (the-as dma-packet vif-state-packet) vif0) (new 'static 'vif-tag :cmd (vif-cmd base))) - (set! (-> (the-as dma-packet vif-state-packet) vif1) (new 'static 'vif-tag :imm #x148 :cmd (vif-cmd offset))) - (set! (-> dma-buf base) (&+ (the-as pointer vif-state-packet) 16))) - (set! (-> *tfrag-work* last-call) (the-as uint 0)) - (set! (-> t-stat cnt) 0) - (set! (-> t-stat tris) 0) - (set! (-> t-stat tfaces) 0) - (set! (-> t-stat tfrags) 0) - (set! (-> t-stat dtris) 0) - (set! (-> t-stat dma-cnt) 0) - (set! (-> t-stat dma-dta) 0) - (set! (-> t-stat dma-tex) 0) - (set! (-> t-stat strips) 0) - (set! (-> t-stat drawpoints) 0) - (set! (-> t-stat base-verts) 0) - (set! (-> t-stat level0-verts) 0) - (set! (-> t-stat level1-verts) 0) - (none))) +(defun tfrag-init-buffer ((dma-buf dma-buffer) (test gs-test) (alpha-blend int) (lev level)) + "Upload the terrain VU1 program, install TEST_1, both camera-matrix banks, and shared constants, + set the VIF double-buffer base and offset, and clear the frame's terrain counters." + (dma-buffer-add-vu-function dma-buf tfrag-vu1-block 1) + (dma-buffer-add-cnt-vif2 dma-buf #x2 (new 'static 'vif-tag) (new 'static 'vif-tag :imm #x2 :cmd (vif-cmd direct) :msk #x1)) + (dma-buffer-add-gif-tag dma-buf (new 'static 'gif-tag64 :nloop #x1 :eop #x1 :nreg #x1) GIF_REGS_ALL_AD) + (let* ((dma-state dma-buf) + (test-packet (-> dma-state base))) + (set! (-> (the-as (pointer gs-test) test-packet) 0) test) + (set! (-> (the-as (pointer gs-reg64) test-packet) 1) (gs-reg64 test-1)) + (set! (-> dma-state base) (&+ test-packet 16))) + (add-tfrag-mtx-0 dma-buf) + (add-tfrag-mtx-1 dma-buf) + (add-tfrag-data dma-buf alpha-blend) + (add-pc-tfrag3-data dma-buf lev) + (let ((vif-state-packet (the-as object (-> dma-buf base)))) + (set! (-> (the-as dma-packet vif-state-packet) dma) (new 'static 'dma-tag :id (dma-tag-id cnt))) + (set! (-> (the-as dma-packet vif-state-packet) vif0) (new 'static 'vif-tag :cmd (vif-cmd base))) + (set! (-> (the-as dma-packet vif-state-packet) vif1) (new 'static 'vif-tag :imm #x148 :cmd (vif-cmd offset))) + (set! (-> dma-buf base) (&+ (the-as pointer vif-state-packet) 16))) + (set! (-> *tfrag-work* last-call) (the-as uint 0)) + (set! (-> t-stat cnt) 0) + (set! (-> t-stat tris) 0) + (set! (-> t-stat tfaces) 0) + (set! (-> t-stat tfrags) 0) + (set! (-> t-stat dtris) 0) + (set! (-> t-stat dma-cnt) 0) + (set! (-> t-stat dma-dta) 0) + (set! (-> t-stat dma-tex) 0) + (set! (-> t-stat strips) 0) + (set! (-> t-stat drawpoints) 0) + (set! (-> t-stat base-verts) 0) + (set! (-> t-stat level0-verts) 0) + (set! (-> t-stat level1-verts) 0) + (none)) (defun tfrag-end-buffer ((dma-buf dma-buffer)) "Patch the final terrain VU call into the DMA stream, wait for VU1, and restore the VIF mode, row, @@ -3277,9 +3218,6 @@ (set! (-> dma-state base) (&+ finish-packet 48))) (none)) -(#when PC_PORT - (def-mips2c draw-inline-array-tfrag (function (pointer uint8) (pointer tfragment) int dma-buffer none))) - ;; The far-terrain packet builder. Visibility is walked sixteen fragments at a time: one 1024-byte ;; scratchpad bank receives that batch's sixteen 64-byte headers by toSPR DMA while packets are built ;; out of the other, and the two 2048-byte packet banks alternate the same way against the fromSPR @@ -3300,523 +3238,510 @@ ;; are short-lived and keep physical names; ra in particular is both a scratch for DMA-status ;; polling and the scratchpad packet cursor. (rlet ((work :reg t0 :type tfrag-work) - (frag :reg t8 :type tfragment) ;; scratchpad record: RAW, addresses the type tag - (color-cursor :reg t8) ;; what t8 is while colors are gathered - (to-spr-channel :reg t4 :type dma-bank-spr) ;; headers, main memory to scratchpad + (frag :reg t8 :type tfragment) ;; scratchpad record: RAW, addresses the type tag + (color-cursor :reg t8) ;; what t8 is while colors are gathered + (to-spr-channel :reg t4 :type dma-bank-spr) ;; headers, main memory to scratchpad (from-spr-channel :reg t1 :type dma-bank-spr) ;; packets, scratchpad to the VIF buffer - (mscal-tag :reg t2) ;; VIF MSCAL opcode, entry address ORed in - (packet-qwc :reg t6) ;; quadwords built into the current bank - (out-cursor :reg v1) ;; write position in the dma-buffer - (dma-buf :reg a3 :type dma-buffer)) ;; the argument, before a3 becomes the packet bank - (label tfrag-draw-entry) - (add.i sp sp -128) - (s.d ra sp) - (s.q s0 sp 16) - (s.q s1 sp 32) - (s.q s2 sp 48) - (s.q s3 sp 64) - (s.q s4 sp 80) - (s.q s5 sp 96) - (s.q gp sp 112) - (lui mscal-tag #x1400) - (l.w out-cursor (-> dma-buf base)) - (lui t3 #x1000) - (lui from-spr-channel #x1000) - (sync.l) - (cache dxwbin out-cursor 0) - (sync.l) - (cache dxwbin out-cursor 1) - (sync.l) - (m! work *tfrag-work*) - (ori to-spr-channel t3 #xd400) - (ori from-spr-channel from-spr-channel #xd000) - (lui t5 #x7000) - (l.vf vf3 (-> work frag-dists)) - (s.w a3 (-> work dma-buffer)) - (ori a3 t5 2064) - (add.i t3 r0 0) - (ori t5 t5 1040) - (max.w.vf vf1 vf0 vf0) - (l.h t7 a0) - (l.vf vf4 (-> work max-dist)) - (add.i a1 a1 -4) - (add.i packet-qwc r0 0) - (m ra a3) - (label skip-invisible-batch) - (b.nz t7 start-first-header-dma :delay (nop!)) - (add.i a0 a0 2) - (add.i a1 a1 1024) - (add.i a2 a2 -16) - (l.h t7 a0) - (b.le a2 r0 tfrag-draw-return :delay (nop!)) - (b skip-invisible-batch :delay (nop!)) - (label start-first-header-dma) - (l.w t7 (-> to-spr-channel chcr)) - (nop!) - (nop!) - (nop!) - (and.i t7 t7 DMA-CHCR-STR) - (nop!) - (b.nz t7 start-first-header-dma :delay (nop!)) - (s.w a1 (-> to-spr-channel madr)) - (xor.i t7 t5 TFRAG-INPUT-BANK-FLIP) - (s.w t7 (-> to-spr-channel sadr)) - (add.i t7 r0 64) - (s.w t7 (-> to-spr-channel qwc)) - (add.i t7 r0 DMA-CHCR-STR) - (s.w t7 (-> to-spr-channel chcr)) - (nop!) - ;; One pass of the outer loop covers sixteen fragments: their headers are DMAed into one scratchpad input - ;; bank while packets are built out of the other, so the header fetch is always a batch ahead. - (label begin-batch) - (m gp a0) - (xor.i t5 t5 TFRAG-INPUT-BANK-FLIP) - (add.i a0 a0 2) - (m t9 a0) - (m frag t5) - (add.i t7 a2 -16) - (b.gt t7 r0 find-next-visible-batch :delay (l.h t7 a0)) - (b wait-last-header-dma :delay (nop!)) - (label skip-empty-batch) - (add.i a2 a2 -16) - (add.i a0 a0 2) - (b.le a2 r0 wait-last-header-dma :delay (l.h t7 a0)) - (nop!) - (nop!) - (label find-next-visible-batch) - (b.z t7 skip-empty-batch :delay (add.i a1 a1 1024)) - (label wait-header-dma) - (l.w t7 (-> to-spr-channel chcr)) - (nop!) - (nop!) - (nop!) - (and.i t7 t7 DMA-CHCR-STR) - (nop!) - (b.z t7 start-header-dma :delay (nop!)) - (nop!) - (l.w t7 (-> work wait-to-spr)) - (nop!) - (nop!) - (nop!) - (add.i t7 t7 1) - (nop!) - (s.w t7 (-> work wait-to-spr)) - (b wait-header-dma :delay (nop!)) - (label start-header-dma) - (s.w a1 (-> to-spr-channel madr)) - (xor.i t7 t5 TFRAG-INPUT-BANK-FLIP) - (s.w t7 (-> to-spr-channel sadr)) - (add.i t7 r0 64) - (s.w t7 (-> to-spr-channel qwc)) - (add.i t7 r0 DMA-CHCR-STR) - (b read-visibility-byte :delay (s.w t7 (-> to-spr-channel chcr))) - (label wait-last-header-dma) - (l.w t7 (-> to-spr-channel chcr)) - (nop!) - (nop!) - (nop!) - (and.i t7 t7 DMA-CHCR-STR) - (nop!) - (b.z t7 read-visibility-byte :delay (nop!)) - (nop!) - (l.w t7 (-> work wait-to-spr)) - (nop!) - (nop!) - (nop!) - (add.i t7 t7 1) - (nop!) - (s.w t7 (-> work wait-to-spr)) - (b wait-last-header-dma :delay (nop!)) - (label read-visibility-byte) - (l.b t7 gp) - (add.i gp gp 1) - (nop!) - (s.w gp (-> work cur-vis-bits)) - (b.nz t7 begin-visible-group :delay (s.w t9 (-> work end-vis-bits))) - (add.i a2 a2 -8) - (add.i frag frag 512) - (b group-done :delay (nop!)) - (label begin-visible-group) - (add.i t9 r0 128) - (l.vf vf2 (-> frag bsphere)) - (label fragment-loop) - (add.i gp packet-qwc -124) - (nop!) - (b.le gp r0 classify-fragment-lod :delay (nop!)) - ;; Hand the finished packet bank to the fromSPR channel and continue in the other one. The flip mask is - ;; whatever bits the two bank offsets disagree on, which is not the bank size. - (label wait-packet-bank-free) - (l.w ra (-> from-spr-channel chcr)) - (nop!) - (nop!) - (nop!) - (and.i ra ra DMA-CHCR-STR) - (nop!) - (b.z ra send-packet-bank :delay (nop!)) - (nop!) - (l.w ra (-> work wait-from-spr)) - (nop!) - (nop!) - (nop!) - (add.i ra ra 1) - (nop!) - (s.w ra (-> work wait-from-spr)) - (b wait-packet-bank-free :delay (nop!)) - (label send-packet-bank) - (s.w a3 (-> from-spr-channel sadr)) - (xor.i a3 a3 TFRAG-PACKET-BANK-FLIP) - (s.w out-cursor (-> from-spr-channel madr)) - (sll ra packet-qwc 4) - (add out-cursor out-cursor ra) - (m ra a3) - (s.w packet-qwc (-> from-spr-channel qwc)) - (add.i packet-qwc r0 DMA-CHCR-STR) - (s.w packet-qwc (-> from-spr-channel chcr)) - (add.i packet-qwc r0 0) - ;; Pick this fragment's detail level, and with it the microprogram entry. - - ;; vf24..vf27 give camera depth in world units and vf16..vf19 the four culling planes, so one accumulator - ;; chain produces the sphere's near and far points against the planes and another produces depth+radius - ;; and depth-radius. Adding frag-dists - which holds the thresholds already negated - compares the sphere - ;; against all four distance bands at once, and pinteh/ppacb squeezes the eight answers down to eight - ;; bytes of one register: for each of meters[0], meters[1], meters[2] and the close threshold, one byte - ;; saying part of the sphere is nearer and one saying all of it is. - - ;; THE SEAM RULE LIVES HERE. A level is only given up when NO PART of the sphere is nearer than that - ;; level's collapse distance, never when merely the center is. A vertex on the border between two patches - ;; sits inside both of their spheres, so if this patch abandons level 0 that vertex is past meters[0] in - ;; the neighbour too, where the VU has already slid it onto the midpoint of its parent edge - position, - ;; color and texture coordinate. The neighbour's finer strip then covers the same surface as our coarser - ;; one, provided the two patches name the same two parents for that vertex, which is the exporter's side of - ;; the bargain and not something this code can check. Compare the sphere centre instead of its near edge - ;; and the runtime side of the bargain is broken outright: adjacent patches would start disagreeing about - ;; detail while their shared vertices were still displaced, which is what tears terrain open. - - ;; Streams that do not exist (num-level0-colors or num-level1-colors zero) fall through to the next - ;; coarser one, and a fragment whose whole sphere is inside the close threshold is left for the near - ;; renderer. - - ;; Interleaved with the arithmetic, all five DMA templates in the work area are filled in from the - ;; fragment header before it is known which of them will be used - the loads are free in the empty - ;; integer slots and it saves a branch. Each geometry template gets an address and a quadword count, - ;; and the pairings are the four windows into the patch's single contiguous VIF block that tfragment - ;; documents: base takes dma-base with dma-qwc[1], level-0 takes dma-common with dma-qwc[3], common - ;; takes that same dma-common with dma-qwc[0], and level-1 takes dma-level-1 with dma-qwc[2]. Level 0 - ;; and common sharing one address with two different counts is the whole trick - how much detail is - ;; how far down the block you transfer. The color template's VIF1 unpack then takes its destination - ;; from color-offset and its NUM from whichever cumulative color count matches the chosen level. - (label classify-fragment-lod) - (and gp t7 t9) - (mula.x.vf vf16 vf2) - (b.z gp next-fragment :delay (l.wu gp (-> frag dma-base))) - (madda.y.vf vf17 vf2) - (l.bu s5 (-> frag dma-qwc 1)) - (madda.z.vf vf18 vf2) - (s.w gp (-> work base-tmpl dma addr)) - (msuba.w.vf vf19 vf0) - (s.h s5 (-> work base-tmpl dma qwc)) - (madd.w.vf vf5 vf1 vf2) - (l.wu gp (-> frag dma-common)) - (mula.w.vf vf27 vf0) - (l.bu s5 (-> frag dma-qwc 3)) - (madda.x.vf vf24 vf2) - (s.w gp (-> work level-0-tmpl dma addr)) - (madda.y.vf vf25 vf2) - (s.h s5 (-> work level-0-tmpl dma qwc)) - (madda.z.vf vf26 vf2) - (l.wu gp (-> frag dma-common)) - (m s5 vf5) - (l.bu s4 (-> frag dma-qwc 0)) - (madd.w.vf vf6 vf1 vf2) - (s.w gp (-> work common-tmpl dma addr)) - (msub.w.vf vf8 vf1 vf2) - (s.h s4 (-> work common-tmpl dma qwc)) - (pcgt.w s5 r0 s5) - (l.wu gp (-> frag dma-level-1)) - (ppach s5 r0 s5) - (l.bu s4 (-> frag dma-qwc 2)) - (add.z.vf vf6 vf3 vf6) - (s.w gp (-> work level-1-tmpl dma addr)) - (add.z.vf vf7 vf3 vf8) - (s.w t3 (-> work base-tmpl vif1)) - (b.nz s5 fragment-culled :delay (s.h s4 (-> work level-1-tmpl dma qwc))) - (min.vf vf4 vf4 vf8) - (s.w t3 (-> work level-0-tmpl vif1)) - (nop!) - (l.bu s5 (-> frag num-base-colors)) - (m gp vf6) - (s.w t3 (-> work common-tmpl vif1)) - (m s3 vf7) - (l.bu s4 (-> frag color-offset)) - (pcgt.w s2 r0 gp) - (l.w gp (-> frag color-indices)) - (pcgt.w s3 r0 s3) - (s.b s4 (-> work color-tmpl vif1)) - (pinteh s4 s2 s3) - (l.bu s2 (-> frag num-level0-colors)) - (ppacb s3 r0 s4) - (l.bu s1 (-> frag num-level1-colors)) - (b.z s3 select-base-stream :delay (srl32 s4 s3 8)) - (b.z s2 select-base-stream :delay (nop!)) - (b.z s1 select-level0-stream :delay (srl s5 s3 16)) - (b.z s5 select-level0-stream :delay (srl32 s5 s3 24)) - (b.nz s5 next-fragment :delay (add.i s5 s1 3)) - (sra s4 s5 2) - (m s5 s1) - (sll t3 s4 2) - (s.h s4 (-> work color-tmpl dma qwc)) - (nop!) - (s.b t3 (-> work color-tmpl vif1 num)) - (add.i packet-qwc packet-qwc 3) - (l.q s2 (-> work common-tmpl dma qwc)) - (nop!) - (l.q s1 (-> work level-1-tmpl dma qwc)) - (nop!) - (l.q t3 (-> work color-tmpl dma qwc)) - (s.q s2 ra) - (nop!) - (s.q s1 ra 16) - (srl32 s2 s3 16) - (s.q t3 ra 32) - (add.i ra ra 48) - (b.nz s2 check-packet-bank-room :delay (ori t3 mscal-tag TFRAG-VU-DRAW-LEVEL1-CLIPPED)) - (srl32 t3 s3 8) - (nop!) - (b.nz t3 check-packet-bank-room :delay (ori t3 mscal-tag TFRAG-VU-DRAW-LEVEL1)) - (b check-packet-bank-room :delay (ori t3 mscal-tag TFRAG-VU-DRAW-LEVEL1-CROSSING)) - ;; Level 0: the level-0 template plus this frame's colors. TFRAG-VU-DRAW-LEVEL0 when the whole sphere is nearer than - ;; meters[0], because then no level-0 point can be past its collapse distance and the microprogram can - ;; use the loop without the substitution test; TFRAG-VU-DRAW-LEVEL0-CROSSING when it straddles it. - (label select-level0-stream) - (b.nz s4 next-fragment :delay (add.i s5 s2 3)) - (sra s4 s5 2) - (m s5 s2) - (sll t3 s4 2) - (s.h s4 (-> work color-tmpl dma qwc)) - (nop!) - (s.b t3 (-> work color-tmpl vif1 num)) - (add.i packet-qwc packet-qwc 2) - (l.q s2 (-> work level-0-tmpl dma qwc)) - (nop!) - (l.q t3 (-> work color-tmpl dma qwc)) - (s.q s2 ra) - (srl s3 s3 8) - (s.q t3 ra 16) - (add.i ra ra 32) - (b.nz s3 check-packet-bank-room :delay (ori t3 mscal-tag TFRAG-VU-DRAW-LEVEL0)) - (b check-packet-bank-room :delay (ori t3 mscal-tag TFRAG-VU-DRAW-LEVEL0-CROSSING)) - ;; Base mesh: one template, no detail. This is also where a fragment with no level-0 data ends up - ;; regardless of distance, which is why TFRAG-VU-DRAW-BASE's renderer is the one that guard-clips. - (label select-base-stream) - (b.nz s4 next-fragment :delay (add.i s4 s5 3)) - (sra s4 s4 2) - (nop!) - (sll t3 s4 2) - (s.h s4 (-> work color-tmpl dma qwc)) - (nop!) - (s.b t3 (-> work color-tmpl vif1 num)) - (ori t3 mscal-tag TFRAG-VU-DRAW-BASE) - (l.q s3 (-> work base-tmpl quad)) - (add.i packet-qwc packet-qwc 2) - (l.q s2 (-> work color-tmpl dma qwc)) - (s.q s3 ra) - (nop!) - (s.q s2 ra 16) - (add.i ra ra 32) - (label check-packet-bank-room) - (add.i s3 r0 127) - (add s2 packet-qwc s4) - (sub s3 s3 s2) - (nop!) - (b.ge s3 r0 expand-fragment-colors :delay (nop!)) - (label wait-packet-bank-free-2) - (l.w ra (-> from-spr-channel chcr)) - (nop!) - (nop!) - (nop!) - (and.i ra ra DMA-CHCR-STR) - (nop!) - (b.z ra send-packet-bank-2 :delay (nop!)) - (nop!) - (l.w ra (-> work wait-from-spr)) - (nop!) - (nop!) - (nop!) - (add.i ra ra 1) - (nop!) - (s.w ra (-> work wait-from-spr)) - (b wait-packet-bank-free-2 :delay (nop!)) - (label send-packet-bank-2) - (s.w a3 (-> from-spr-channel sadr)) - (xor.i a3 a3 TFRAG-PACKET-BANK-FLIP) - (s.w out-cursor (-> from-spr-channel madr)) - (sll ra packet-qwc 4) - (add out-cursor out-cursor ra) - (m ra a3) - (s.w packet-qwc (-> from-spr-channel qwc)) - (add.i packet-qwc r0 DMA-CHCR-STR) - (s.w packet-qwc (-> from-spr-channel chcr)) - (add.i packet-qwc r0 0) - ;; Expand the fragment's packed uint16 color indices into scratchpad RGBA addresses. pextlh widens four - ;; indices into four 32-bit lanes at once and color-ptr holds the same scratchpad base in all four lanes, - ;; so one add produces four independent gather addresses; the four loads then walk out of the vector - ;; register through sra32 and pcpyud. Colors are the only part of a terrain patch that changes with time - ;; of day, which is why they are rebuilt per frame instead of living in the level file. - ;; - ;; t8 is needed as the index cursor here, so the fragment address it was holding is parked in - ;; work.src-ptr for the duration and reloaded at the end - that is the whole purpose of that field. For - ;; the length of the loop t8 is color-cursor and not frag, and the doubleword load takes four packed - ;; uint16 indices in one go rather than reading any tfragment field. - ;; Same color gather as the far builder, and the same register borrow: the fragment address in t8 is - ;; parked in work.src-ptr while t8 walks the packed uint16 indices as color-cursor, then reloaded. The - ;; doubleword load reads four indices at once, not a tfragment field. - (label expand-fragment-colors) - (add packet-qwc packet-qwc s4) - (s.w frag (-> work src-ptr)) - (l.d s4 gp) - (add.i color-cursor gp 8) - (add.i gp s5 -4) - (l.q s5 (-> work color-ptr)) - (pextlh s4 r0 s4) - (mmi-nop!) - (add.w s2 s4 s5) - (mmi-nop!) - (l.w s4 s2) - (sra32 s3 s2 0) - (l.w s3 s3) - (pcpyud s1 s2 s2) - (l.w s2 s1) - (sra32 s1 s1 0) - (b.le gp r0 expand-colors-tail :delay (l.w s1 s1)) - (label expand-colors-loop) - (l.d s0 color-cursor) ;; four packed color indices at once - (add.i ra ra 16) - (add.i color-cursor color-cursor 8) - (s.w s4 ra -16) - (add.i gp gp -4) - (s.w s3 ra -12) - (pextlh s4 r0 s0) - (s.w s2 ra -8) - (add.w s2 s4 s5) - (s.w s1 ra -4) - (l.w s4 s2) - (sra32 s3 s2 0) - (l.w s3 s3) - (pcpyud s1 s2 s2) - (l.w s2 s1) - (sra32 s1 s1 0) - (b.gt gp r0 expand-colors-loop :delay (l.w s1 s1)) - (label expand-colors-tail) - (add.i ra ra 16) - (l.w frag (-> work src-ptr)) - (nop!) - (s.w s4 ra -16) - (nop!) - (s.w s3 ra -12) - (nop!) - (s.w s2 ra -8) - (nop!) - (s.w s1 ra -4) - (label fragment-culled) - (xor t7 t7 t9) - (nop!) - (label next-fragment) - (add.i frag frag 64) - (srl t9 t9 1) - (add.i a2 a2 -1) - (nop!) - (b.nz t9 fragment-loop :delay (l.vf vf2 (-> frag bsphere))) - (label group-done) - (nop!) - (l.w gp (-> work cur-vis-bits)) - (nop!) - (l.w t9 (-> work end-vis-bits)) - (b.ne gp t9 read-visibility-byte :delay (s.b t7 gp -1)) - (b.gt a2 r0 begin-batch :delay (nop!)) - (b.z packet-qwc wait-packet-dma-idle :delay (nop!)) - (label wait-final-packet-dma) - (l.w a0 (-> from-spr-channel chcr)) - (nop!) - (nop!) - (nop!) - (and.i a0 a0 DMA-CHCR-STR) - (nop!) - (b.z a0 send-final-packet-bank :delay (nop!)) - (nop!) - (l.w a0 (-> work wait-from-spr)) - (nop!) - (nop!) - (nop!) - (add.i a0 a0 1) - (nop!) - (s.w a0 (-> work wait-from-spr)) - (b wait-final-packet-dma :delay (nop!)) - (label send-final-packet-bank) - (s.w a3 (-> from-spr-channel sadr)) - (xor.i a0 a3 #x1800) - (s.w out-cursor (-> from-spr-channel madr)) - (sll a1 packet-qwc 4) - (add out-cursor out-cursor a1) - (m a0 a0) - (s.w packet-qwc (-> from-spr-channel qwc)) - (add.i a0 r0 DMA-CHCR-STR) - (s.w a0 (-> from-spr-channel chcr)) - (add.i a0 r0 0) - (label wait-packet-dma-idle) - (l.w a0 (-> from-spr-channel chcr)) - (nop!) - (nop!) - (nop!) - (and.i a0 a0 DMA-CHCR-STR) - (nop!) - (b.z a0 tfrag-draw-return :delay (nop!)) - (nop!) - (l.w a0 (-> work wait-from-spr)) - (nop!) - (nop!) - (nop!) - (add.i a0 a0 1) - (nop!) - (s.w a0 (-> work wait-from-spr)) - (b wait-packet-dma-idle :delay (nop!)) - (label tfrag-draw-return) - (l.w a0 (-> work dma-buffer)) - (nop!) - (s.w t3 (-> work last-call)) - (nop!) - (s.vf vf4 (-> work min-dist)) - (nop!) - (s.w out-cursor a0 4) - (nop!) - (m v0 r0) - (l.d ra sp) - (l.q gp sp 112) - (l.q s5 sp 96) - (l.q s4 sp 80) - (l.q s3 sp 64) - (l.q s2 sp 48) - (l.q s1 sp 32) - (l.q s0 sp 16) - (jr ra :delay (add.i sp sp 128)) - (nop!) - (nop!) - (nop!)))) + (mscal-tag :reg t2) ;; VIF MSCAL opcode, entry address ORed in + (packet-qwc :reg t6) ;; quadwords built into the current bank + (out-cursor :reg v1) ;; write position in the dma-buffer + (dma-buf :reg a3 :type dma-buffer)) ;; the argument, before a3 becomes the packet bank + (label tfrag-draw-entry) + (add.i sp sp -128) + (s.d ra sp) + (s.q s0 sp 16) + (s.q s1 sp 32) + (s.q s2 sp 48) + (s.q s3 sp 64) + (s.q s4 sp 80) + (s.q s5 sp 96) + (s.q gp sp 112) + (lui mscal-tag #x1400) + (l.w out-cursor (-> dma-buf base)) + (lui t3 #x1000) + (lui from-spr-channel #x1000) + (sync.l) + (cache dxwbin out-cursor 0) + (sync.l) + (cache dxwbin out-cursor 1) + (sync.l) + (m! work *tfrag-work*) + (ori to-spr-channel t3 #xd400) + (ori from-spr-channel from-spr-channel #xd000) + (lui t5 #x7000) + (l.vf vf3 (-> work frag-dists)) + (s.w a3 (-> work dma-buffer)) + (ori a3 t5 2064) + (add.i t3 r0 0) + (ori t5 t5 1040) + (max.w.vf vf1 vf0 vf0) + (l.h t7 a0) + (l.vf vf4 (-> work max-dist)) + (add.i a1 a1 -4) + (add.i packet-qwc r0 0) + (m ra a3) + (label skip-invisible-batch) + (b.nz t7 start-first-header-dma :delay (nop!)) + (add.i a0 a0 2) + (add.i a1 a1 1024) + (add.i a2 a2 -16) + (l.h t7 a0) + (b.le a2 r0 tfrag-draw-return :delay (nop!)) + (b skip-invisible-batch :delay (nop!)) + (label start-first-header-dma) + (l.w t7 (-> to-spr-channel chcr)) + (nop!) + (nop!) + (nop!) + (and.i t7 t7 DMA-CHCR-STR) + (nop!) + (b.nz t7 start-first-header-dma :delay (nop!)) + (s.w a1 (-> to-spr-channel madr)) + (xor.i t7 t5 TFRAG-INPUT-BANK-FLIP) + (s.w t7 (-> to-spr-channel sadr)) + (add.i t7 r0 64) + (s.w t7 (-> to-spr-channel qwc)) + (add.i t7 r0 DMA-CHCR-STR) + (s.w t7 (-> to-spr-channel chcr)) + (nop!) + ;; One pass of the outer loop covers sixteen fragments: their headers are DMAed into one scratchpad input + ;; bank while packets are built out of the other, so the header fetch is always a batch ahead. + (label begin-batch) + (m gp a0) + (xor.i t5 t5 TFRAG-INPUT-BANK-FLIP) + (add.i a0 a0 2) + (m t9 a0) + (m frag t5) + (add.i t7 a2 -16) + (b.gt t7 r0 find-next-visible-batch :delay (l.h t7 a0)) + (b wait-last-header-dma :delay (nop!)) + (label skip-empty-batch) + (add.i a2 a2 -16) + (add.i a0 a0 2) + (b.le a2 r0 wait-last-header-dma :delay (l.h t7 a0)) + (nop!) + (nop!) + (label find-next-visible-batch) + (b.z t7 skip-empty-batch :delay (add.i a1 a1 1024)) + (label wait-header-dma) + (l.w t7 (-> to-spr-channel chcr)) + (nop!) + (nop!) + (nop!) + (and.i t7 t7 DMA-CHCR-STR) + (nop!) + (b.z t7 start-header-dma :delay (nop!)) + (nop!) + (l.w t7 (-> work wait-to-spr)) + (nop!) + (nop!) + (nop!) + (add.i t7 t7 1) + (nop!) + (s.w t7 (-> work wait-to-spr)) + (b wait-header-dma :delay (nop!)) + (label start-header-dma) + (s.w a1 (-> to-spr-channel madr)) + (xor.i t7 t5 TFRAG-INPUT-BANK-FLIP) + (s.w t7 (-> to-spr-channel sadr)) + (add.i t7 r0 64) + (s.w t7 (-> to-spr-channel qwc)) + (add.i t7 r0 DMA-CHCR-STR) + (b read-visibility-byte :delay (s.w t7 (-> to-spr-channel chcr))) + (label wait-last-header-dma) + (l.w t7 (-> to-spr-channel chcr)) + (nop!) + (nop!) + (nop!) + (and.i t7 t7 DMA-CHCR-STR) + (nop!) + (b.z t7 read-visibility-byte :delay (nop!)) + (nop!) + (l.w t7 (-> work wait-to-spr)) + (nop!) + (nop!) + (nop!) + (add.i t7 t7 1) + (nop!) + (s.w t7 (-> work wait-to-spr)) + (b wait-last-header-dma :delay (nop!)) + (label read-visibility-byte) + (l.b t7 gp) + (add.i gp gp 1) + (nop!) + (s.w gp (-> work cur-vis-bits)) + (b.nz t7 begin-visible-group :delay (s.w t9 (-> work end-vis-bits))) + (add.i a2 a2 -8) + (add.i frag frag 512) + (b group-done :delay (nop!)) + (label begin-visible-group) + (add.i t9 r0 128) + (l.vf vf2 (-> frag bsphere)) + (label fragment-loop) + (add.i gp packet-qwc -124) + (nop!) + (b.le gp r0 classify-fragment-lod :delay (nop!)) + ;; Hand the finished packet bank to the fromSPR channel and continue in the other one. The flip mask is + ;; whatever bits the two bank offsets disagree on, which is not the bank size. + (label wait-packet-bank-free) + (l.w ra (-> from-spr-channel chcr)) + (nop!) + (nop!) + (nop!) + (and.i ra ra DMA-CHCR-STR) + (nop!) + (b.z ra send-packet-bank :delay (nop!)) + (nop!) + (l.w ra (-> work wait-from-spr)) + (nop!) + (nop!) + (nop!) + (add.i ra ra 1) + (nop!) + (s.w ra (-> work wait-from-spr)) + (b wait-packet-bank-free :delay (nop!)) + (label send-packet-bank) + (s.w a3 (-> from-spr-channel sadr)) + (xor.i a3 a3 TFRAG-PACKET-BANK-FLIP) + (s.w out-cursor (-> from-spr-channel madr)) + (sll ra packet-qwc 4) + (add out-cursor out-cursor ra) + (m ra a3) + (s.w packet-qwc (-> from-spr-channel qwc)) + (add.i packet-qwc r0 DMA-CHCR-STR) + (s.w packet-qwc (-> from-spr-channel chcr)) + (add.i packet-qwc r0 0) + ;; Pick this fragment's detail level, and with it the microprogram entry. + ;; vf24..vf27 give camera depth in world units and vf16..vf19 the four culling planes, so one accumulator + ;; chain produces the sphere's near and far points against the planes and another produces depth+radius + ;; and depth-radius. Adding frag-dists - which holds the thresholds already negated - compares the sphere + ;; against all four distance bands at once, and pinteh/ppacb squeezes the eight answers down to eight + ;; bytes of one register: for each of meters[0], meters[1], meters[2] and the close threshold, one byte + ;; saying part of the sphere is nearer and one saying all of it is. + ;; THE SEAM RULE LIVES HERE. A level is only given up when NO PART of the sphere is nearer than that + ;; level's collapse distance, never when merely the center is. A vertex on the border between two patches + ;; sits inside both of their spheres, so if this patch abandons level 0 that vertex is past meters[0] in + ;; the neighbour too, where the VU has already slid it onto the midpoint of its parent edge - position, + ;; color and texture coordinate. The neighbour's finer strip then covers the same surface as our coarser + ;; one, provided the two patches name the same two parents for that vertex, which is the exporter's side of + ;; the bargain and not something this code can check. Compare the sphere centre instead of its near edge + ;; and the runtime side of the bargain is broken outright: adjacent patches would start disagreeing about + ;; detail while their shared vertices were still displaced, which is what tears terrain open. + ;; Streams that do not exist (num-level0-colors or num-level1-colors zero) fall through to the next + ;; coarser one, and a fragment whose whole sphere is inside the close threshold is left for the near + ;; renderer. + ;; Interleaved with the arithmetic, all five DMA templates in the work area are filled in from the + ;; fragment header before it is known which of them will be used - the loads are free in the empty + ;; integer slots and it saves a branch. Each geometry template gets an address and a quadword count, + ;; and the pairings are the four windows into the patch's single contiguous VIF block that tfragment + ;; documents: base takes dma-base with dma-qwc[1], level-0 takes dma-common with dma-qwc[3], common + ;; takes that same dma-common with dma-qwc[0], and level-1 takes dma-level-1 with dma-qwc[2]. Level 0 + ;; and common sharing one address with two different counts is the whole trick - how much detail is + ;; how far down the block you transfer. The color template's VIF1 unpack then takes its destination + ;; from color-offset and its NUM from whichever cumulative color count matches the chosen level. + (label classify-fragment-lod) + (and gp t7 t9) + (mula.x.vf vf16 vf2) + (b.z gp next-fragment :delay (l.wu gp (-> frag dma-base))) + (madda.y.vf vf17 vf2) + (l.bu s5 (-> frag dma-qwc 1)) + (madda.z.vf vf18 vf2) + (s.w gp (-> work base-tmpl dma addr)) + (msuba.w.vf vf19 vf0) + (s.h s5 (-> work base-tmpl dma qwc)) + (madd.w.vf vf5 vf1 vf2) + (l.wu gp (-> frag dma-common)) + (mula.w.vf vf27 vf0) + (l.bu s5 (-> frag dma-qwc 3)) + (madda.x.vf vf24 vf2) + (s.w gp (-> work level-0-tmpl dma addr)) + (madda.y.vf vf25 vf2) + (s.h s5 (-> work level-0-tmpl dma qwc)) + (madda.z.vf vf26 vf2) + (l.wu gp (-> frag dma-common)) + (m s5 vf5) + (l.bu s4 (-> frag dma-qwc 0)) + (madd.w.vf vf6 vf1 vf2) + (s.w gp (-> work common-tmpl dma addr)) + (msub.w.vf vf8 vf1 vf2) + (s.h s4 (-> work common-tmpl dma qwc)) + (pcgt.w s5 r0 s5) + (l.wu gp (-> frag dma-level-1)) + (ppach s5 r0 s5) + (l.bu s4 (-> frag dma-qwc 2)) + (add.z.vf vf6 vf3 vf6) + (s.w gp (-> work level-1-tmpl dma addr)) + (add.z.vf vf7 vf3 vf8) + (s.w t3 (-> work base-tmpl vif1)) + (b.nz s5 fragment-culled :delay (s.h s4 (-> work level-1-tmpl dma qwc))) + (min.vf vf4 vf4 vf8) + (s.w t3 (-> work level-0-tmpl vif1)) + (nop!) + (l.bu s5 (-> frag num-base-colors)) + (m gp vf6) + (s.w t3 (-> work common-tmpl vif1)) + (m s3 vf7) + (l.bu s4 (-> frag color-offset)) + (pcgt.w s2 r0 gp) + (l.w gp (-> frag color-indices)) + (pcgt.w s3 r0 s3) + (s.b s4 (-> work color-tmpl vif1)) + (pinteh s4 s2 s3) + (l.bu s2 (-> frag num-level0-colors)) + (ppacb s3 r0 s4) + (l.bu s1 (-> frag num-level1-colors)) + (b.z s3 select-base-stream :delay (srl32 s4 s3 8)) + (b.z s2 select-base-stream :delay (nop!)) + (b.z s1 select-level0-stream :delay (srl s5 s3 16)) + (b.z s5 select-level0-stream :delay (srl32 s5 s3 24)) + (b.nz s5 next-fragment :delay (add.i s5 s1 3)) + (sra s4 s5 2) + (m s5 s1) + (sll t3 s4 2) + (s.h s4 (-> work color-tmpl dma qwc)) + (nop!) + (s.b t3 (-> work color-tmpl vif1 num)) + (add.i packet-qwc packet-qwc 3) + (l.q s2 (-> work common-tmpl dma qwc)) + (nop!) + (l.q s1 (-> work level-1-tmpl dma qwc)) + (nop!) + (l.q t3 (-> work color-tmpl dma qwc)) + (s.q s2 ra) + (nop!) + (s.q s1 ra 16) + (srl32 s2 s3 16) + (s.q t3 ra 32) + (add.i ra ra 48) + (b.nz s2 check-packet-bank-room :delay (ori t3 mscal-tag TFRAG-VU-DRAW-LEVEL1-CLIPPED)) + (srl32 t3 s3 8) + (nop!) + (b.nz t3 check-packet-bank-room :delay (ori t3 mscal-tag TFRAG-VU-DRAW-LEVEL1)) + (b check-packet-bank-room :delay (ori t3 mscal-tag TFRAG-VU-DRAW-LEVEL1-CROSSING)) + ;; Level 0: the level-0 template plus this frame's colors. TFRAG-VU-DRAW-LEVEL0 when the whole sphere is nearer than + ;; meters[0], because then no level-0 point can be past its collapse distance and the microprogram can + ;; use the loop without the substitution test; TFRAG-VU-DRAW-LEVEL0-CROSSING when it straddles it. + (label select-level0-stream) + (b.nz s4 next-fragment :delay (add.i s5 s2 3)) + (sra s4 s5 2) + (m s5 s2) + (sll t3 s4 2) + (s.h s4 (-> work color-tmpl dma qwc)) + (nop!) + (s.b t3 (-> work color-tmpl vif1 num)) + (add.i packet-qwc packet-qwc 2) + (l.q s2 (-> work level-0-tmpl dma qwc)) + (nop!) + (l.q t3 (-> work color-tmpl dma qwc)) + (s.q s2 ra) + (srl s3 s3 8) + (s.q t3 ra 16) + (add.i ra ra 32) + (b.nz s3 check-packet-bank-room :delay (ori t3 mscal-tag TFRAG-VU-DRAW-LEVEL0)) + (b check-packet-bank-room :delay (ori t3 mscal-tag TFRAG-VU-DRAW-LEVEL0-CROSSING)) + ;; Base mesh: one template, no detail. This is also where a fragment with no level-0 data ends up + ;; regardless of distance, which is why TFRAG-VU-DRAW-BASE's renderer is the one that guard-clips. + (label select-base-stream) + (b.nz s4 next-fragment :delay (add.i s4 s5 3)) + (sra s4 s4 2) + (nop!) + (sll t3 s4 2) + (s.h s4 (-> work color-tmpl dma qwc)) + (nop!) + (s.b t3 (-> work color-tmpl vif1 num)) + (ori t3 mscal-tag TFRAG-VU-DRAW-BASE) + (l.q s3 (-> work base-tmpl quad)) + (add.i packet-qwc packet-qwc 2) + (l.q s2 (-> work color-tmpl dma qwc)) + (s.q s3 ra) + (nop!) + (s.q s2 ra 16) + (add.i ra ra 32) + (label check-packet-bank-room) + (add.i s3 r0 127) + (add s2 packet-qwc s4) + (sub s3 s3 s2) + (nop!) + (b.ge s3 r0 expand-fragment-colors :delay (nop!)) + (label wait-packet-bank-free-2) + (l.w ra (-> from-spr-channel chcr)) + (nop!) + (nop!) + (nop!) + (and.i ra ra DMA-CHCR-STR) + (nop!) + (b.z ra send-packet-bank-2 :delay (nop!)) + (nop!) + (l.w ra (-> work wait-from-spr)) + (nop!) + (nop!) + (nop!) + (add.i ra ra 1) + (nop!) + (s.w ra (-> work wait-from-spr)) + (b wait-packet-bank-free-2 :delay (nop!)) + (label send-packet-bank-2) + (s.w a3 (-> from-spr-channel sadr)) + (xor.i a3 a3 TFRAG-PACKET-BANK-FLIP) + (s.w out-cursor (-> from-spr-channel madr)) + (sll ra packet-qwc 4) + (add out-cursor out-cursor ra) + (m ra a3) + (s.w packet-qwc (-> from-spr-channel qwc)) + (add.i packet-qwc r0 DMA-CHCR-STR) + (s.w packet-qwc (-> from-spr-channel chcr)) + (add.i packet-qwc r0 0) + ;; Expand the fragment's packed uint16 color indices into scratchpad RGBA addresses. pextlh widens four + ;; indices into four 32-bit lanes at once and color-ptr holds the same scratchpad base in all four lanes, + ;; so one add produces four independent gather addresses; the four loads then walk out of the vector + ;; register through sra32 and pcpyud. Colors are the only part of a terrain patch that changes with time + ;; of day, which is why they are rebuilt per frame instead of living in the level file. + ;; + ;; t8 is needed as the index cursor here, so the fragment address it was holding is parked in + ;; work.src-ptr for the duration and reloaded at the end - that is the whole purpose of that field. For + ;; the length of the loop t8 is color-cursor and not frag, and the doubleword load takes four packed + ;; uint16 indices in one go rather than reading any tfragment field. + ;; Same color gather as the far builder, and the same register borrow: the fragment address in t8 is + ;; parked in work.src-ptr while t8 walks the packed uint16 indices as color-cursor, then reloaded. The + ;; doubleword load reads four indices at once, not a tfragment field. + (label expand-fragment-colors) + (add packet-qwc packet-qwc s4) + (s.w frag (-> work src-ptr)) + (l.d s4 gp) + (add.i color-cursor gp 8) + (add.i gp s5 -4) + (l.q s5 (-> work color-ptr)) + (pextlh s4 r0 s4) + (mmi-nop!) + (add.w s2 s4 s5) + (mmi-nop!) + (l.w s4 s2) + (sra32 s3 s2 0) + (l.w s3 s3) + (pcpyud s1 s2 s2) + (l.w s2 s1) + (sra32 s1 s1 0) + (b.le gp r0 expand-colors-tail :delay (l.w s1 s1)) + (label expand-colors-loop) + (l.d s0 color-cursor) ;; four packed color indices at once + (add.i ra ra 16) + (add.i color-cursor color-cursor 8) + (s.w s4 ra -16) + (add.i gp gp -4) + (s.w s3 ra -12) + (pextlh s4 r0 s0) + (s.w s2 ra -8) + (add.w s2 s4 s5) + (s.w s1 ra -4) + (l.w s4 s2) + (sra32 s3 s2 0) + (l.w s3 s3) + (pcpyud s1 s2 s2) + (l.w s2 s1) + (sra32 s1 s1 0) + (b.gt gp r0 expand-colors-loop :delay (l.w s1 s1)) + (label expand-colors-tail) + (add.i ra ra 16) + (l.w frag (-> work src-ptr)) + (nop!) + (s.w s4 ra -16) + (nop!) + (s.w s3 ra -12) + (nop!) + (s.w s2 ra -8) + (nop!) + (s.w s1 ra -4) + (label fragment-culled) + (xor t7 t7 t9) + (nop!) + (label next-fragment) + (add.i frag frag 64) + (srl t9 t9 1) + (add.i a2 a2 -1) + (nop!) + (b.nz t9 fragment-loop :delay (l.vf vf2 (-> frag bsphere))) + (label group-done) + (nop!) + (l.w gp (-> work cur-vis-bits)) + (nop!) + (l.w t9 (-> work end-vis-bits)) + (b.ne gp t9 read-visibility-byte :delay (s.b t7 gp -1)) + (b.gt a2 r0 begin-batch :delay (nop!)) + (b.z packet-qwc wait-packet-dma-idle :delay (nop!)) + (label wait-final-packet-dma) + (l.w a0 (-> from-spr-channel chcr)) + (nop!) + (nop!) + (nop!) + (and.i a0 a0 DMA-CHCR-STR) + (nop!) + (b.z a0 send-final-packet-bank :delay (nop!)) + (nop!) + (l.w a0 (-> work wait-from-spr)) + (nop!) + (nop!) + (nop!) + (add.i a0 a0 1) + (nop!) + (s.w a0 (-> work wait-from-spr)) + (b wait-final-packet-dma :delay (nop!)) + (label send-final-packet-bank) + (s.w a3 (-> from-spr-channel sadr)) + (xor.i a0 a3 #x1800) + (s.w out-cursor (-> from-spr-channel madr)) + (sll a1 packet-qwc 4) + (add out-cursor out-cursor a1) + (m a0 a0) + (s.w packet-qwc (-> from-spr-channel qwc)) + (add.i a0 r0 DMA-CHCR-STR) + (s.w a0 (-> from-spr-channel chcr)) + (add.i a0 r0 0) + (label wait-packet-dma-idle) + (l.w a0 (-> from-spr-channel chcr)) + (nop!) + (nop!) + (nop!) + (and.i a0 a0 DMA-CHCR-STR) + (nop!) + (b.z a0 tfrag-draw-return :delay (nop!)) + (nop!) + (l.w a0 (-> work wait-from-spr)) + (nop!) + (nop!) + (nop!) + (add.i a0 a0 1) + (nop!) + (s.w a0 (-> work wait-from-spr)) + (b wait-packet-dma-idle :delay (nop!)) + (label tfrag-draw-return) + (l.w a0 (-> work dma-buffer)) + (nop!) + (s.w t3 (-> work last-call)) + (nop!) + (s.vf vf4 (-> work min-dist)) + (nop!) + (s.w out-cursor a0 4) + (nop!) + (m v0 r0) + (l.d ra sp) + (l.q gp sp 112) + (l.q s5 sp 96) + (l.q s4 sp 80) + (l.q s3 sp 64) + (l.q s2 sp 48) + (l.q s1 sp 32) + (l.q s0 sp 16) + (jr ra :delay (add.i sp sp 128)) + (nop!) + (nop!) + (nop!)))) (defun tfrag-near-init-buffer ((dma-buf dma-buffer) (test gs-test) (alpha-blend int)) "Initialize the near-terrain DMA stream with its VU1 program, TEST_1 state, both camera-matrix banks, shared constants, and VIF double-buffer base and offset." (dma-buffer-add-vu-function dma-buf tnear-vu1-block 1) - (let* ((dma-state dma-buf) - (direct-packet (the-as object (-> dma-state base)))) - (set! (-> (the-as dma-packet direct-packet) dma) (new 'static 'dma-tag :qwc #x2 :id (dma-tag-id cnt))) - (set! (-> (the-as dma-packet direct-packet) vif0) (new 'static 'vif-tag)) - (set! (-> (the-as dma-packet direct-packet) vif1) (new 'static 'vif-tag :imm #x2 :cmd (vif-cmd direct) :msk #x1)) - (set! (-> dma-state base) (&+ (the-as pointer direct-packet) 16))) - (let* ((dma-state dma-buf) - (giftag (the-as object (-> dma-state base)))) - (set! (-> (the-as gs-gif-tag giftag) tag) (new 'static 'gif-tag64 :nloop #x1 :eop #x1 :nreg #x1)) - (set! (-> (the-as gs-gif-tag giftag) regs) GIF_REGS_ALL_AD) - (set! (-> dma-state base) (&+ (the-as pointer giftag) 16))) + (dma-buffer-add-cnt-vif2 dma-buf #x2 (new 'static 'vif-tag) (new 'static 'vif-tag :imm #x2 :cmd (vif-cmd direct) :msk #x1)) + (dma-buffer-add-gif-tag dma-buf (new 'static 'gif-tag64 :nloop #x1 :eop #x1 :nreg #x1) GIF_REGS_ALL_AD) (let* ((dma-state dma-buf) (test-packet (-> dma-state base))) (set! (-> (the-as (pointer gs-test) test-packet) 0) test) @@ -3859,8 +3784,6 @@ (set! (-> dma-state base) (&+ finish-packet 48))) (none)) -(define-extern draw-inline-array-tfrag-near (function (pointer uint8) (pointer tfragment) int dma-buffer none)) - ;; The near path uses the same scratchpad and output double buffering, but does not run the far ;; distance classifier. It selects the available base/detail stream, expands packed color indices, ;; and calls TNEAR-VU-DRAW for each packet. Register roles match the far function. @@ -3873,451 +3796,505 @@ ;; TNEAR-VU-DRAW, the program with the real polygon clipper. ra, gp, t5, t7, t9 and ;; s0..s5 are short-lived and keep physical names. (rlet ((work :reg t0 :type tfrag-work) - (frag :reg t8 :type tfragment) ;; scratchpad record: RAW, addresses the type tag - (color-cursor :reg t8) ;; what t8 is while colors are gathered - (to-spr-channel :reg t3 :type dma-bank-spr) ;; headers, main memory to scratchpad + (frag :reg t8 :type tfragment) ;; scratchpad record: RAW, addresses the type tag + (color-cursor :reg t8) ;; what t8 is while colors are gathered + (to-spr-channel :reg t3 :type dma-bank-spr) ;; headers, main memory to scratchpad (from-spr-channel :reg t1 :type dma-bank-spr) ;; packets, scratchpad to the VIF buffer - (mscal-tag :reg t2) ;; VIF MSCAL opcode, entry address ORed in - (packet-qwc :reg t6) ;; quadwords built into the current bank - (out-cursor :reg v1) ;; write position in the dma-buffer - (dma-buf :reg a3 :type dma-buffer)) ;; the argument, before a3 becomes the packet bank - (label tfrag-near-draw-entry) - (add.i sp sp -128) - (s.d ra sp) - (s.q s0 sp 16) - (s.q s1 sp 32) - (s.q s2 sp 48) - (s.q s3 sp 64) - (s.q s4 sp 80) - (s.q s5 sp 96) - (s.q gp sp 112) - (lui mscal-tag #x1400) - (l.w out-cursor (-> dma-buf base)) - (lui to-spr-channel #x1000) - (lui from-spr-channel #x1000) - (sync.l) - (cache dxwbin out-cursor 0) - (sync.l) - (cache dxwbin out-cursor 1) - (sync.l) - (m! work *tfrag-work*) - (ori to-spr-channel to-spr-channel #xd400) - (ori from-spr-channel from-spr-channel #xd000) - (lui t4 #x7000) - (s.w dma-buf (-> work dma-buffer)) - (add.i t5 r0 0) - (ori dma-buf t4 2064) - (l.vf vf3 (-> work frag-dists)) - (ori t4 t4 1040) - (l.h t7 a0) - (max.w.vf vf1 vf0 vf0) - (add.i a1 a1 -4) - (add.i packet-qwc r0 0) - (nop!) - (m ra dma-buf) - (label skip-invisible-batch) - (b.nz t7 start-first-header-dma :delay (nop!)) - (add.i a0 a0 2) - (add.i a1 a1 1024) - (add.i a2 a2 -16) - (l.h t7 a0) - (b.le a2 r0 tfrag-near-draw-return :delay (nop!)) - (b skip-invisible-batch :delay (nop!)) - (label start-first-header-dma) - (l.w t7 (-> to-spr-channel chcr)) - (nop!) - (nop!) - (nop!) - (and.i t7 t7 DMA-CHCR-STR) - (nop!) - (b.nz t7 start-first-header-dma :delay (nop!)) - (s.w a1 (-> to-spr-channel madr)) - (xor.i t7 t4 TFRAG-INPUT-BANK-FLIP) - (s.w t7 (-> to-spr-channel sadr)) - (add.i t7 r0 64) - (s.w t7 (-> to-spr-channel qwc)) - (add.i t7 r0 DMA-CHCR-STR) - (s.w t7 (-> to-spr-channel chcr)) - (nop!) - (label begin-batch) - (m gp a0) - (xor.i t4 t4 TFRAG-INPUT-BANK-FLIP) - (add.i a0 a0 2) - (m t9 a0) - (m frag t4) - (add.i t7 a2 -16) - (b.gt t7 r0 find-next-visible-batch :delay (l.h t7 a0)) - (b wait-last-header-dma :delay (nop!)) - (label skip-empty-batch) - (add.i a2 a2 -16) - (add.i a0 a0 2) - (b.le a2 r0 wait-last-header-dma :delay (l.h t7 a0)) - (nop!) - (nop!) - (label find-next-visible-batch) - (b.z t7 skip-empty-batch :delay (add.i a1 a1 1024)) - (label wait-header-dma) - (l.w t7 (-> to-spr-channel chcr)) - (nop!) - (nop!) - (nop!) - (and.i t7 t7 DMA-CHCR-STR) - (nop!) - (b.z t7 start-header-dma :delay (nop!)) - (nop!) - (l.w t7 (-> work near-wait-to-spr)) - (nop!) - (nop!) - (nop!) - (add.i t7 t7 1) - (nop!) - (s.w t7 (-> work near-wait-to-spr)) - (b wait-header-dma :delay (nop!)) - (label start-header-dma) - (s.w a1 (-> to-spr-channel madr)) - (xor.i t7 t4 TFRAG-INPUT-BANK-FLIP) - (s.w t7 (-> to-spr-channel sadr)) - (add.i t7 r0 64) - (s.w t7 (-> to-spr-channel qwc)) - (add.i t7 r0 DMA-CHCR-STR) - (b read-visibility-byte :delay (s.w t7 (-> to-spr-channel chcr))) - (label wait-last-header-dma) - (l.w t7 (-> to-spr-channel chcr)) - (nop!) - (nop!) - (nop!) - (and.i t7 t7 DMA-CHCR-STR) - (nop!) - (b.z t7 read-visibility-byte :delay (nop!)) - (nop!) - (l.w t7 (-> work near-wait-to-spr)) - (nop!) - (nop!) - (nop!) - (add.i t7 t7 1) - (nop!) - (s.w t7 (-> work near-wait-to-spr)) - (b wait-last-header-dma :delay (nop!)) - (label read-visibility-byte) - (l.b t7 gp) - (add.i gp gp 1) - (nop!) - (s.w gp (-> work cur-vis-bits)) - (b.nz t7 begin-visible-group :delay (s.w t9 (-> work end-vis-bits))) - (add.i a2 a2 -8) - (add.i frag frag 512) - (b group-done :delay (nop!)) - (label begin-visible-group) - (add.i t9 r0 128) - (l.vf vf2 (-> frag bsphere)) - (label fragment-loop) - (nop!) - (l.hu gp (-> frag id)) - (nop!) - (l.w s5 (-> work test-id)) - (b.ne gp s5 wait-packet-bank-free :delay (nop!)) - (nop!) - (nop!) - (label wait-packet-bank-free) - (add.i gp packet-qwc -124) - (nop!) - (b.le gp r0 select-finest-stream :delay (nop!)) - (label send-packet-bank) - (l.w ra (-> from-spr-channel chcr)) - (nop!) - (nop!) - (nop!) - (and.i ra ra DMA-CHCR-STR) - (nop!) - (b.z ra send-packet-bank-body :delay (nop!)) - (nop!) - (l.w ra (-> work near-wait-from-spr)) - (nop!) - (nop!) - (nop!) - (add.i ra ra 1) - (nop!) - (s.w ra (-> work near-wait-from-spr)) - (b send-packet-bank :delay (nop!)) - (label send-packet-bank-body) - (s.w dma-buf (-> from-spr-channel sadr)) - (xor.i dma-buf dma-buf TFRAG-PACKET-BANK-FLIP) - (s.w out-cursor (-> from-spr-channel madr)) - (sll ra packet-qwc 4) - (add out-cursor out-cursor ra) - (m ra dma-buf) - (s.w packet-qwc (-> from-spr-channel qwc)) - (add.i packet-qwc r0 DMA-CHCR-STR) - (s.w packet-qwc (-> from-spr-channel chcr)) - (add.i packet-qwc r0 0) - (label select-finest-stream) - (and s5 t7 t9) - (l.bu gp (-> frag num-base-colors)) - (b.z s5 next-fragment :delay (l.bu s5 (-> frag color-offset))) - (nop!) - (l.bu s4 (-> frag num-level0-colors)) - (nop!) - (l.bu s3 (-> frag num-level1-colors)) - (nop!) - (s.b s5 (-> work color-tmpl vif1)) - (b.z s4 select-base-stream :delay (l.w s5 (-> frag color-indices))) - (b.z s3 select-level0-stream :delay (nop!)) - (add.i s4 s3 3) - (m gp s3) - (sra s4 s4 2) - (s.w t5 (-> work common-tmpl vif1)) - (sll t5 s4 2) - (s.h s4 (-> work color-tmpl dma)) - (nop!) - (s.b t5 (-> work color-tmpl vif1 num)) - (add.i packet-qwc packet-qwc 3) - (l.wu t5 (-> frag dma-chain 0)) - (nop!) - (l.bu s3 (-> frag dma-qwc 0)) - (nop!) - (s.w t5 (-> work common-tmpl dma addr)) - (nop!) - (s.h s3 (-> work common-tmpl dma)) - (nop!) - (l.wu t5 (-> frag dma-chain 2)) - (nop!) - (l.bu s3 (-> frag dma-qwc 2)) - (nop!) - (s.w t5 (-> work level-1-tmpl dma addr)) - (nop!) - (s.h s3 (-> work level-1-tmpl dma)) - (l.q t5 (-> work common-tmpl dma)) - (nop!) - (l.q s3 (-> work level-1-tmpl dma)) - (nop!) - (l.q s2 (-> work color-tmpl dma)) - (nop!) - (s.q t5 ra) - (nop!) - (s.q s3 ra 16) - (nop!) - (s.q s2 ra 32) - (add.i ra ra 48) - (b check-packet-bank-room :delay (ori t5 mscal-tag TNEAR-VU-DRAW)) - (label select-level0-stream) - (add.i s3 s4 3) - (m gp s4) - (sra s4 s3 2) - (s.w t5 (-> work level-0-tmpl vif1)) - (sll t5 s4 2) - (s.h s4 (-> work color-tmpl dma)) - (nop!) - (s.b t5 (-> work color-tmpl vif1 num)) - (add.i packet-qwc packet-qwc 2) - (l.wu t5 (-> frag dma-chain 0)) - (nop!) - (l.bu s3 (-> frag dma-qwc 3)) - (nop!) - (s.w t5 (-> work level-0-tmpl dma addr)) - (nop!) - (s.h s3 (-> work level-0-tmpl dma)) - (l.q t5 (-> work level-0-tmpl dma)) - (nop!) - (l.q s3 (-> work color-tmpl dma)) - (nop!) - (s.q t5 ra) - (nop!) - (s.q s3 ra 16) - (add.i ra ra 32) - (b check-packet-bank-room :delay (ori t5 mscal-tag TNEAR-VU-DRAW)) - (label select-base-stream) - (add.i s4 gp 3) - (nop!) - (sra s4 s4 2) - (s.w t5 (-> work base-tmpl vif1)) - (sll t5 s4 2) - (s.h s4 (-> work color-tmpl dma)) - (nop!) - (s.b t5 (-> work color-tmpl vif1 num)) - (add.i packet-qwc packet-qwc 2) - (l.wu t5 (-> frag dma-chain 1)) - (nop!) - (l.bu s3 (-> frag dma-qwc 1)) - (nop!) - (s.w t5 (-> work base-tmpl dma addr)) - (nop!) - (s.h s3 (-> work base-tmpl dma)) - (l.q t5 (-> work base-tmpl dma)) - (nop!) - (l.q s3 (-> work color-tmpl dma)) - (nop!) - (s.q t5 ra) - (ori t5 mscal-tag TNEAR-VU-DRAW) - (s.q s3 ra 16) - (add.i ra ra 32) - (label check-packet-bank-room) - (add.i s3 r0 127) - (add s2 packet-qwc s4) - (sub s3 s3 s2) - (nop!) - (b.ge s3 r0 expand-fragment-colors :delay (nop!)) - (label wait-packet-bank-free-2) - (l.w ra (-> from-spr-channel chcr)) - (nop!) - (nop!) - (nop!) - (and.i ra ra DMA-CHCR-STR) - (nop!) - (b.z ra send-packet-bank-2 :delay (nop!)) - (nop!) - (l.w ra (-> work near-wait-from-spr)) - (nop!) - (nop!) - (nop!) - (add.i ra ra 1) - (nop!) - (s.w ra (-> work near-wait-from-spr)) - (b wait-packet-bank-free-2 :delay (nop!)) - (label send-packet-bank-2) - (s.w dma-buf (-> from-spr-channel sadr)) - (xor.i dma-buf dma-buf TFRAG-PACKET-BANK-FLIP) - (s.w out-cursor (-> from-spr-channel madr)) - (sll ra packet-qwc 4) - (add out-cursor out-cursor ra) - (m ra dma-buf) - (s.w packet-qwc (-> from-spr-channel qwc)) - (add.i packet-qwc r0 DMA-CHCR-STR) - (s.w packet-qwc (-> from-spr-channel chcr)) - (add.i packet-qwc r0 0) - ;; Same color gather as the far builder, and the same register borrow: the fragment address in t8 is - ;; parked in work.src-ptr while t8 walks the packed uint16 indices as color-cursor, then reloaded. The - ;; doubleword load reads four indices at once, not a tfragment field. - (label expand-fragment-colors) - (add packet-qwc packet-qwc s4) - (s.w frag (-> work src-ptr)) - (l.d s4 s5) - (add.i color-cursor s5 8) - (add.i gp gp -4) - (l.q s5 (-> work color-ptr)) - (pextlh s4 r0 s4) - (mmi-nop!) - (add.w s2 s4 s5) - (mmi-nop!) - (l.w s4 s2) - (sra32 s3 s2 0) - (l.w s3 s3) - (pcpyud s1 s2 s2) - (l.w s2 s1) - (sra32 s1 s1 0) - (b.le gp r0 expand-colors-tail :delay (l.w s1 s1)) - (label expand-colors-loop) - (l.d s0 color-cursor) ;; four packed color indices at once - (add.i ra ra 16) - (add.i color-cursor color-cursor 8) - (s.w s4 ra -16) - (add.i gp gp -4) - (s.w s3 ra -12) - (pextlh s4 r0 s0) - (s.w s2 ra -8) - (add.w s2 s4 s5) - (s.w s1 ra -4) - (l.w s4 s2) - (sra32 s3 s2 0) - (l.w s3 s3) - (pcpyud s1 s2 s2) - (l.w s2 s1) - (sra32 s1 s1 0) - (b.gt gp r0 expand-colors-loop :delay (l.w s1 s1)) - (label expand-colors-tail) - (add.i ra ra 16) - (l.w frag (-> work src-ptr)) - (nop!) - (s.w s4 ra -16) - (nop!) - (s.w s3 ra -12) - (nop!) - (s.w s2 ra -8) - (nop!) - (s.w s1 ra -4) - (xor t7 t7 t9) - (nop!) - (label next-fragment) - (add.i frag frag 64) - (srl t9 t9 1) - (add.i a2 a2 -1) - (nop!) - (b.nz t9 fragment-loop :delay (l.vf vf2 (-> frag bsphere))) - (label group-done) - (nop!) - (l.w gp (-> work cur-vis-bits)) - (nop!) - (l.w t9 (-> work end-vis-bits)) - (b.ne gp t9 read-visibility-byte :delay (s.b t7 gp -1)) - (b.gt a2 r0 begin-batch :delay (nop!)) - (b.z packet-qwc wait-packet-dma-idle :delay (nop!)) - (label wait-final-packet-dma) - (l.w a0 (-> from-spr-channel chcr)) - (nop!) - (nop!) - (nop!) - (and.i a0 a0 DMA-CHCR-STR) - (nop!) - (b.z a0 send-final-packet-bank :delay (nop!)) - (nop!) - (l.w a0 (-> work near-wait-from-spr)) - (nop!) - (nop!) - (nop!) - (add.i a0 a0 1) - (nop!) - (s.w a0 (-> work near-wait-from-spr)) - (b wait-final-packet-dma :delay (nop!)) - (label send-final-packet-bank) - (s.w dma-buf (-> from-spr-channel sadr)) - (xor.i a0 dma-buf #x1800) - (s.w out-cursor (-> from-spr-channel madr)) - (sll a1 packet-qwc 4) - (add out-cursor out-cursor a1) - (m a0 a0) - (s.w packet-qwc (-> from-spr-channel qwc)) - (add.i a0 r0 DMA-CHCR-STR) - (s.w a0 (-> from-spr-channel chcr)) - (add.i a0 r0 0) - (label wait-packet-dma-idle) - (l.w a0 (-> from-spr-channel chcr)) - (nop!) - (nop!) - (nop!) - (and.i a0 a0 DMA-CHCR-STR) - (nop!) - (b.z a0 tfrag-near-draw-return :delay (nop!)) - (nop!) - (l.w a0 (-> work near-wait-from-spr)) - (nop!) - (nop!) - (nop!) - (add.i a0 a0 1) - (nop!) - (s.w a0 (-> work near-wait-from-spr)) - (b wait-packet-dma-idle :delay (nop!)) - (label tfrag-near-draw-return) - (l.w a0 (-> work dma-buffer)) - (nop!) - (s.w t5 (-> work last-call)) - (nop!) - (s.w out-cursor a0 4) - (nop!) - (m v0 r0) - (l.d ra sp) - (l.q gp sp 112) - (l.q s5 sp 96) - (l.q s4 sp 80) - (l.q s3 sp 64) - (l.q s2 sp 48) - (l.q s1 sp 32) - (l.q s0 sp 16) - (jr ra :delay (add.i sp sp 128)) - (nop!) - (nop!) - (nop!)))) + (mscal-tag :reg t2) ;; VIF MSCAL opcode, entry address ORed in + (packet-qwc :reg t6) ;; quadwords built into the current bank + (out-cursor :reg v1) ;; write position in the dma-buffer + (dma-buf :reg a3 :type dma-buffer)) ;; the argument, before a3 becomes the packet bank + (label tfrag-near-draw-entry) + (add.i sp sp -128) + (s.d ra sp) + (s.q s0 sp 16) + (s.q s1 sp 32) + (s.q s2 sp 48) + (s.q s3 sp 64) + (s.q s4 sp 80) + (s.q s5 sp 96) + (s.q gp sp 112) + (lui mscal-tag #x1400) + (l.w out-cursor (-> dma-buf base)) + (lui to-spr-channel #x1000) + (lui from-spr-channel #x1000) + (sync.l) + (cache dxwbin out-cursor 0) + (sync.l) + (cache dxwbin out-cursor 1) + (sync.l) + (m! work *tfrag-work*) + (ori to-spr-channel to-spr-channel #xd400) + (ori from-spr-channel from-spr-channel #xd000) + (lui t4 #x7000) + (s.w dma-buf (-> work dma-buffer)) + (add.i t5 r0 0) + (ori dma-buf t4 2064) + (l.vf vf3 (-> work frag-dists)) + (ori t4 t4 1040) + (l.h t7 a0) + (max.w.vf vf1 vf0 vf0) + (add.i a1 a1 -4) + (add.i packet-qwc r0 0) + (nop!) + (m ra dma-buf) + (label skip-invisible-batch) + (b.nz t7 start-first-header-dma :delay (nop!)) + (add.i a0 a0 2) + (add.i a1 a1 1024) + (add.i a2 a2 -16) + (l.h t7 a0) + (b.le a2 r0 tfrag-near-draw-return :delay (nop!)) + (b skip-invisible-batch :delay (nop!)) + (label start-first-header-dma) + (l.w t7 (-> to-spr-channel chcr)) + (nop!) + (nop!) + (nop!) + (and.i t7 t7 DMA-CHCR-STR) + (nop!) + (b.nz t7 start-first-header-dma :delay (nop!)) + (s.w a1 (-> to-spr-channel madr)) + (xor.i t7 t4 TFRAG-INPUT-BANK-FLIP) + (s.w t7 (-> to-spr-channel sadr)) + (add.i t7 r0 64) + (s.w t7 (-> to-spr-channel qwc)) + (add.i t7 r0 DMA-CHCR-STR) + (s.w t7 (-> to-spr-channel chcr)) + (nop!) + (label begin-batch) + (m gp a0) + (xor.i t4 t4 TFRAG-INPUT-BANK-FLIP) + (add.i a0 a0 2) + (m t9 a0) + (m frag t4) + (add.i t7 a2 -16) + (b.gt t7 r0 find-next-visible-batch :delay (l.h t7 a0)) + (b wait-last-header-dma :delay (nop!)) + (label skip-empty-batch) + (add.i a2 a2 -16) + (add.i a0 a0 2) + (b.le a2 r0 wait-last-header-dma :delay (l.h t7 a0)) + (nop!) + (nop!) + (label find-next-visible-batch) + (b.z t7 skip-empty-batch :delay (add.i a1 a1 1024)) + (label wait-header-dma) + (l.w t7 (-> to-spr-channel chcr)) + (nop!) + (nop!) + (nop!) + (and.i t7 t7 DMA-CHCR-STR) + (nop!) + (b.z t7 start-header-dma :delay (nop!)) + (nop!) + (l.w t7 (-> work near-wait-to-spr)) + (nop!) + (nop!) + (nop!) + (add.i t7 t7 1) + (nop!) + (s.w t7 (-> work near-wait-to-spr)) + (b wait-header-dma :delay (nop!)) + (label start-header-dma) + (s.w a1 (-> to-spr-channel madr)) + (xor.i t7 t4 TFRAG-INPUT-BANK-FLIP) + (s.w t7 (-> to-spr-channel sadr)) + (add.i t7 r0 64) + (s.w t7 (-> to-spr-channel qwc)) + (add.i t7 r0 DMA-CHCR-STR) + (b read-visibility-byte :delay (s.w t7 (-> to-spr-channel chcr))) + (label wait-last-header-dma) + (l.w t7 (-> to-spr-channel chcr)) + (nop!) + (nop!) + (nop!) + (and.i t7 t7 DMA-CHCR-STR) + (nop!) + (b.z t7 read-visibility-byte :delay (nop!)) + (nop!) + (l.w t7 (-> work near-wait-to-spr)) + (nop!) + (nop!) + (nop!) + (add.i t7 t7 1) + (nop!) + (s.w t7 (-> work near-wait-to-spr)) + (b wait-last-header-dma :delay (nop!)) + (label read-visibility-byte) + (l.b t7 gp) + (add.i gp gp 1) + (nop!) + (s.w gp (-> work cur-vis-bits)) + (b.nz t7 begin-visible-group :delay (s.w t9 (-> work end-vis-bits))) + (add.i a2 a2 -8) + (add.i frag frag 512) + (b group-done :delay (nop!)) + (label begin-visible-group) + (add.i t9 r0 128) + (l.vf vf2 (-> frag bsphere)) + (label fragment-loop) + (nop!) + (l.hu gp (-> frag id)) + (nop!) + (l.w s5 (-> work test-id)) + (b.ne gp s5 wait-packet-bank-free :delay (nop!)) + (nop!) + (nop!) + (label wait-packet-bank-free) + (add.i gp packet-qwc -124) + (nop!) + (b.le gp r0 select-finest-stream :delay (nop!)) + (label send-packet-bank) + (l.w ra (-> from-spr-channel chcr)) + (nop!) + (nop!) + (nop!) + (and.i ra ra DMA-CHCR-STR) + (nop!) + (b.z ra send-packet-bank-body :delay (nop!)) + (nop!) + (l.w ra (-> work near-wait-from-spr)) + (nop!) + (nop!) + (nop!) + (add.i ra ra 1) + (nop!) + (s.w ra (-> work near-wait-from-spr)) + (b send-packet-bank :delay (nop!)) + (label send-packet-bank-body) + (s.w dma-buf (-> from-spr-channel sadr)) + (xor.i dma-buf dma-buf TFRAG-PACKET-BANK-FLIP) + (s.w out-cursor (-> from-spr-channel madr)) + (sll ra packet-qwc 4) + (add out-cursor out-cursor ra) + (m ra dma-buf) + (s.w packet-qwc (-> from-spr-channel qwc)) + (add.i packet-qwc r0 DMA-CHCR-STR) + (s.w packet-qwc (-> from-spr-channel chcr)) + (add.i packet-qwc r0 0) + (label select-finest-stream) + (and s5 t7 t9) + (l.bu gp (-> frag num-base-colors)) + (b.z s5 next-fragment :delay (l.bu s5 (-> frag color-offset))) + (nop!) + (l.bu s4 (-> frag num-level0-colors)) + (nop!) + (l.bu s3 (-> frag num-level1-colors)) + (nop!) + (s.b s5 (-> work color-tmpl vif1)) + (b.z s4 select-base-stream :delay (l.w s5 (-> frag color-indices))) + (b.z s3 select-level0-stream :delay (nop!)) + (add.i s4 s3 3) + (m gp s3) + (sra s4 s4 2) + (s.w t5 (-> work common-tmpl vif1)) + (sll t5 s4 2) + (s.h s4 (-> work color-tmpl dma)) + (nop!) + (s.b t5 (-> work color-tmpl vif1 num)) + (add.i packet-qwc packet-qwc 3) + (l.wu t5 (-> frag dma-chain 0)) + (nop!) + (l.bu s3 (-> frag dma-qwc 0)) + (nop!) + (s.w t5 (-> work common-tmpl dma addr)) + (nop!) + (s.h s3 (-> work common-tmpl dma)) + (nop!) + (l.wu t5 (-> frag dma-chain 2)) + (nop!) + (l.bu s3 (-> frag dma-qwc 2)) + (nop!) + (s.w t5 (-> work level-1-tmpl dma addr)) + (nop!) + (s.h s3 (-> work level-1-tmpl dma)) + (l.q t5 (-> work common-tmpl dma)) + (nop!) + (l.q s3 (-> work level-1-tmpl dma)) + (nop!) + (l.q s2 (-> work color-tmpl dma)) + (nop!) + (s.q t5 ra) + (nop!) + (s.q s3 ra 16) + (nop!) + (s.q s2 ra 32) + (add.i ra ra 48) + (b check-packet-bank-room :delay (ori t5 mscal-tag TNEAR-VU-DRAW)) + (label select-level0-stream) + (add.i s3 s4 3) + (m gp s4) + (sra s4 s3 2) + (s.w t5 (-> work level-0-tmpl vif1)) + (sll t5 s4 2) + (s.h s4 (-> work color-tmpl dma)) + (nop!) + (s.b t5 (-> work color-tmpl vif1 num)) + (add.i packet-qwc packet-qwc 2) + (l.wu t5 (-> frag dma-chain 0)) + (nop!) + (l.bu s3 (-> frag dma-qwc 3)) + (nop!) + (s.w t5 (-> work level-0-tmpl dma addr)) + (nop!) + (s.h s3 (-> work level-0-tmpl dma)) + (l.q t5 (-> work level-0-tmpl dma)) + (nop!) + (l.q s3 (-> work color-tmpl dma)) + (nop!) + (s.q t5 ra) + (nop!) + (s.q s3 ra 16) + (add.i ra ra 32) + (b check-packet-bank-room :delay (ori t5 mscal-tag TNEAR-VU-DRAW)) + (label select-base-stream) + (add.i s4 gp 3) + (nop!) + (sra s4 s4 2) + (s.w t5 (-> work base-tmpl vif1)) + (sll t5 s4 2) + (s.h s4 (-> work color-tmpl dma)) + (nop!) + (s.b t5 (-> work color-tmpl vif1 num)) + (add.i packet-qwc packet-qwc 2) + (l.wu t5 (-> frag dma-chain 1)) + (nop!) + (l.bu s3 (-> frag dma-qwc 1)) + (nop!) + (s.w t5 (-> work base-tmpl dma addr)) + (nop!) + (s.h s3 (-> work base-tmpl dma)) + (l.q t5 (-> work base-tmpl dma)) + (nop!) + (l.q s3 (-> work color-tmpl dma)) + (nop!) + (s.q t5 ra) + (ori t5 mscal-tag TNEAR-VU-DRAW) + (s.q s3 ra 16) + (add.i ra ra 32) + (label check-packet-bank-room) + (add.i s3 r0 127) + (add s2 packet-qwc s4) + (sub s3 s3 s2) + (nop!) + (b.ge s3 r0 expand-fragment-colors :delay (nop!)) + (label wait-packet-bank-free-2) + (l.w ra (-> from-spr-channel chcr)) + (nop!) + (nop!) + (nop!) + (and.i ra ra DMA-CHCR-STR) + (nop!) + (b.z ra send-packet-bank-2 :delay (nop!)) + (nop!) + (l.w ra (-> work near-wait-from-spr)) + (nop!) + (nop!) + (nop!) + (add.i ra ra 1) + (nop!) + (s.w ra (-> work near-wait-from-spr)) + (b wait-packet-bank-free-2 :delay (nop!)) + (label send-packet-bank-2) + (s.w dma-buf (-> from-spr-channel sadr)) + (xor.i dma-buf dma-buf TFRAG-PACKET-BANK-FLIP) + (s.w out-cursor (-> from-spr-channel madr)) + (sll ra packet-qwc 4) + (add out-cursor out-cursor ra) + (m ra dma-buf) + (s.w packet-qwc (-> from-spr-channel qwc)) + (add.i packet-qwc r0 DMA-CHCR-STR) + (s.w packet-qwc (-> from-spr-channel chcr)) + (add.i packet-qwc r0 0) + ;; Same color gather as the far builder, and the same register borrow: the fragment address in t8 is + ;; parked in work.src-ptr while t8 walks the packed uint16 indices as color-cursor, then reloaded. The + ;; doubleword load reads four indices at once, not a tfragment field. + (label expand-fragment-colors) + (add packet-qwc packet-qwc s4) + (s.w frag (-> work src-ptr)) + (l.d s4 s5) + (add.i color-cursor s5 8) + (add.i gp gp -4) + (l.q s5 (-> work color-ptr)) + (pextlh s4 r0 s4) + (mmi-nop!) + (add.w s2 s4 s5) + (mmi-nop!) + (l.w s4 s2) + (sra32 s3 s2 0) + (l.w s3 s3) + (pcpyud s1 s2 s2) + (l.w s2 s1) + (sra32 s1 s1 0) + (b.le gp r0 expand-colors-tail :delay (l.w s1 s1)) + (label expand-colors-loop) + (l.d s0 color-cursor) ;; four packed color indices at once + (add.i ra ra 16) + (add.i color-cursor color-cursor 8) + (s.w s4 ra -16) + (add.i gp gp -4) + (s.w s3 ra -12) + (pextlh s4 r0 s0) + (s.w s2 ra -8) + (add.w s2 s4 s5) + (s.w s1 ra -4) + (l.w s4 s2) + (sra32 s3 s2 0) + (l.w s3 s3) + (pcpyud s1 s2 s2) + (l.w s2 s1) + (sra32 s1 s1 0) + (b.gt gp r0 expand-colors-loop :delay (l.w s1 s1)) + (label expand-colors-tail) + (add.i ra ra 16) + (l.w frag (-> work src-ptr)) + (nop!) + (s.w s4 ra -16) + (nop!) + (s.w s3 ra -12) + (nop!) + (s.w s2 ra -8) + (nop!) + (s.w s1 ra -4) + (xor t7 t7 t9) + (nop!) + (label next-fragment) + (add.i frag frag 64) + (srl t9 t9 1) + (add.i a2 a2 -1) + (nop!) + (b.nz t9 fragment-loop :delay (l.vf vf2 (-> frag bsphere))) + (label group-done) + (nop!) + (l.w gp (-> work cur-vis-bits)) + (nop!) + (l.w t9 (-> work end-vis-bits)) + (b.ne gp t9 read-visibility-byte :delay (s.b t7 gp -1)) + (b.gt a2 r0 begin-batch :delay (nop!)) + (b.z packet-qwc wait-packet-dma-idle :delay (nop!)) + (label wait-final-packet-dma) + (l.w a0 (-> from-spr-channel chcr)) + (nop!) + (nop!) + (nop!) + (and.i a0 a0 DMA-CHCR-STR) + (nop!) + (b.z a0 send-final-packet-bank :delay (nop!)) + (nop!) + (l.w a0 (-> work near-wait-from-spr)) + (nop!) + (nop!) + (nop!) + (add.i a0 a0 1) + (nop!) + (s.w a0 (-> work near-wait-from-spr)) + (b wait-final-packet-dma :delay (nop!)) + (label send-final-packet-bank) + (s.w dma-buf (-> from-spr-channel sadr)) + (xor.i a0 dma-buf #x1800) + (s.w out-cursor (-> from-spr-channel madr)) + (sll a1 packet-qwc 4) + (add out-cursor out-cursor a1) + (m a0 a0) + (s.w packet-qwc (-> from-spr-channel qwc)) + (add.i a0 r0 DMA-CHCR-STR) + (s.w a0 (-> from-spr-channel chcr)) + (add.i a0 r0 0) + (label wait-packet-dma-idle) + (l.w a0 (-> from-spr-channel chcr)) + (nop!) + (nop!) + (nop!) + (and.i a0 a0 DMA-CHCR-STR) + (nop!) + (b.z a0 tfrag-near-draw-return :delay (nop!)) + (nop!) + (l.w a0 (-> work near-wait-from-spr)) + (nop!) + (nop!) + (nop!) + (add.i a0 a0 1) + (nop!) + (s.w a0 (-> work near-wait-from-spr)) + (b wait-packet-dma-idle :delay (nop!)) + (label tfrag-near-draw-return) + (l.w a0 (-> work dma-buffer)) + (nop!) + (s.w t5 (-> work last-call)) + (nop!) + (s.w out-cursor a0 4) + (nop!) + (m v0 r0) + (l.d ra sp) + (l.q gp sp 112) + (l.q s5 sp 96) + (l.q s4 sp 80) + (l.q s3 sp 64) + (l.q s2 sp 48) + (l.q s1 sp 32) + (l.q s0 sp 16) + (jr ra :delay (add.i sp sp 128)) + (nop!) + (nop!) + (nop!)))) (#when PC_PORT - (def-mips2c stats-tfrag-asm (function tfragment none))) + (defun stats-tfrag-asm ((fragment tfragment)) + "Classify a visible terrain fragment and accumulate the statistics for the selected detail level." + (let ((debug (-> fragment debug-data))) + (when (nonzero? debug) + (let* ((sphere (-> fragment bsphere)) + (camera *math-camera*) + (radius (-> sphere w)) + (inside-frustum? (and (>= (+ (* (-> camera plane 0 x) (-> sphere x)) + (* (-> camera plane 1 x) (-> sphere y)) + (* (-> camera plane 2 x) (-> sphere z)) + (- (-> camera plane 3 x)) + radius) + 0.0) + (>= (+ (* (-> camera plane 0 y) (-> sphere x)) + (* (-> camera plane 1 y) (-> sphere y)) + (* (-> camera plane 2 y) (-> sphere z)) + (- (-> camera plane 3 y)) + radius) + 0.0) + (>= (+ (* (-> camera plane 0 z) (-> sphere x)) + (* (-> camera plane 1 z) (-> sphere y)) + (* (-> camera plane 2 z) (-> sphere z)) + (- (-> camera plane 3 z)) + radius) + 0.0) + (>= (+ (* (-> camera plane 0 w) (-> sphere x)) + (* (-> camera plane 1 w) (-> sphere y)) + (* (-> camera plane 2 w) (-> sphere z)) + (- (-> camera plane 3 w)) + radius) + 0.0)))) + (when inside-frustum? + (let* ((work *tfrag-work*) + (depth (+ (* (-> camera camera-rot vector 0 z) (-> sphere x)) + (* (-> camera camera-rot vector 1 z) (-> sphere y)) + (* (-> camera camera-rot vector 2 z) (-> sphere z)) + (-> camera camera-rot vector 3 z))) + (near-edge (- depth radius)) + (far-edge (+ depth radius)) + (detail-level (cond + ((or (>= near-edge (- (-> work frag-dists x))) (zero? (-> fragment num-level0-colors))) 0) + ((or (>= near-edge (- (-> work frag-dists y))) (zero? (-> fragment num-level1-colors))) 1) + (else 2))) + (near-stat? + ;; The lower streams count as near when either the whole sphere is inside the + ;; level-1 threshold or any part has crossed the close threshold. + (if (= detail-level 2) + (< far-edge (- (-> work frag-dists w))) + (or (< far-edge (- (-> work frag-dists z))) (< near-edge (- (-> work frag-dists w)))))) + (stat (if near-stat? (-> work tr-stat-tfrag-near) (-> work tr-stat-tfrag)))) + ;; The original loads only the low byte of each uint16 debug count. + (+! (-> stat tris) (logand (-> debug stats num-tris detail-level) #xff)) + (+! (-> stat dverts) (logand (-> debug stats num-dverts detail-level) #xff)) + (+! (-> stat fragments) 1)))))) + (none))) ;; Transform the fragment bounding sphere with the camera rows in *tfrag-work*. Fragments outside ;; the homogeneous guard range do not contribute. The remaining packed detail tests select one of @@ -4329,145 +4306,138 @@ ;; been chosen, so both get a second name. a0 becomes a pointer to the tr-stat the fragment counts ;; toward, and v1 becomes a running dvert total; in between a0 briefly holds a color count and then ;; shifted flag bytes, and those keep the physical spelling because they are not either thing. - - ;; Note which pointer convention frag follows here. It is an ordinary GOAL basic pointer, so a - ;; displacement is four less than the offset the deftype shows - which is why bsphere is reached at - ;; 12 and the color counts at 49, 50 and 51. The scratchpad copies that the two draw functions walk - ;; are biased the other way and address the type tag, so the same field is four higher there. Reading - ;; one convention with the other silently shifts every field by one word. - ;; The vector registers are the shared background camera convention rather than anything local: ;; vf16..vf19 are the culling planes and vf24..vf27 the camera transform, exactly as the LOD ;; classifier uses them. - (rlet ((frag :reg a0 :type tfragment) ;; BASIC pointer, unlike the draw functions - (stat :reg a0 :type tr-stat) ;; what a0 becomes once a level is chosen - (debug :reg a1 :type tfragment-debug-data) ;; the exporter's per-level triangle counts + (rlet ((frag :reg a0 :type tfragment) ;; BASIC pointer, unlike the draw functions + (stat :reg a0 :type tr-stat) ;; what a0 becomes once a level is chosen + (debug :reg a1 :type tfragment-debug-data) ;; the exporter's per-level triangle counts (work :reg v1 :type tfrag-work) - (dvert-total :reg v1)) ;; what v1 becomes in the accumulate blocks - (label stats-tfrag-entry) - (nop!) - (nop!) - (m! work *tfrag-work*) - (nop!) - (l.w debug (-> frag debug-data)) - (nop!) - (l.vf vf10 (-> frag bsphere)) - (b.z debug stats-tfrag-asm-finish-stats :delay (l.vf vf14 (-> work frag-dists))) - (mula.x.vf vf16 vf10) - (l.b a2 (-> frag num-base-colors)) - (madda.y.vf vf17 vf10) - (l.b a2 (-> frag num-level0-colors)) - (madda.z.vf vf18 vf10) - (l.b a0 (-> frag num-level1-colors)) - (msub.w.vf vf9 vf19 vf0) - (nop!) - (mula.w.vf vf27 vf0) - (nop!) - (madda.x.vf vf24 vf10) - (nop!) - (madda.y.vf vf25 vf10) - (nop!) - (add.w.vf vf9 vf9 vf10) - (nop!) - (madd.z.vf vf11 vf26 vf10) - (nop!) - (add.w.vf vf12 vf11 vf10) - (nop!) - (sub.w.vf vf13 vf11 vf10) - (nop!) - (sub.z.vf vf11 vf0 vf12) - (nop!) - (m a3 vf9) - (nop!) - (pcgt.w a3 r0 a3) - (mmi-nop!) - (ppach a3 r0 a3) - (mmi-nop!) - (b.nz a3 stats-tfrag-asm-finish-stats :delay (add.z.vf vf12 vf14 vf12)) - (nop!) - (add.z.vf vf13 vf14 vf13) - (m t0 vf12) - (nop!) - (m a3 vf13) - (nop!) - (pcgt.w t0 r0 t0) - (mmi-nop!) - (pcgt.w a3 r0 a3) - (mmi-nop!) - (pinteh a3 t0 a3) - (mmi-nop!) - (ppacb a3 r0 a3) - (mmi-nop!) - (b.z a3 stats-tfrag-asm-select-base-stat :delay (nop!)) - (b.z a2 stats-tfrag-asm-select-base-stat :delay (nop!)) - (b.z a0 stats-tfrag-asm-select-middle-stat :delay (srl a0 a3 16)) - (b.z a0 stats-tfrag-asm-select-middle-stat :delay (nop!)) - (srl32 a0 a3 24) - (l.bu a2 (-> debug stats num-tris 2)) - (b.nz a0 stats-tfrag-asm-accumulate-selected-high :delay (l.w stat (-> work tr-stat-tfrag-near))) - (nop!) - (l.w stat (-> work tr-stat-tfrag)) - (label stats-tfrag-asm-accumulate-selected-high) - (nop!) - (l.bu dvert-total (-> debug stats num-dverts 2)) - (nop!) - (l.w a3 (-> stat tris)) - (nop!) - (l.w a1 (-> stat dverts)) - (add a2 a3 a2) - (s.w a2 (-> stat tris)) - (nop!) - (l.h a2 (-> stat fragments)) - (add dvert-total a1 dvert-total) - (s.w dvert-total (-> stat dverts)) - (add.i dvert-total a2 1) - (s.h dvert-total (-> stat fragments)) - (b stats-tfrag-asm-finish-stats :delay (nop!)) - (label stats-tfrag-asm-select-middle-stat) - (srl32 a0 a3 8) - (l.bu a2 (-> debug stats num-tris 1)) - (b.nz a0 stats-tfrag-asm-accumulate-selected-middle :delay (l.w stat (-> work tr-stat-tfrag-near))) - (nop!) - (l.w stat (-> work tr-stat-tfrag)) - (label stats-tfrag-asm-accumulate-selected-middle) - (nop!) - (l.bu dvert-total (-> debug stats num-dverts 1)) - (nop!) - (l.w a3 (-> stat tris)) - (nop!) - (l.w a1 (-> stat dverts)) - (add a2 a3 a2) - (s.w a2 (-> stat tris)) - (nop!) - (l.h a2 (-> stat fragments)) - (add dvert-total a1 dvert-total) - (s.w dvert-total (-> stat dverts)) - (add.i dvert-total a2 1) - (s.h dvert-total (-> stat fragments)) - (b stats-tfrag-asm-finish-stats :delay (nop!)) - (label stats-tfrag-asm-select-base-stat) - (srl32 a0 a3 8) - (l.bu a2 (-> debug stats num-tris 0)) - (b.nz a0 stats-tfrag-asm-accumulate-selected-base :delay (l.w stat (-> work tr-stat-tfrag-near))) - (b stats-tfrag-asm-accumulate-selected-base :delay (l.w stat (-> work tr-stat-tfrag))) - (label stats-tfrag-asm-accumulate-selected-base) - (nop!) - (l.bu dvert-total (-> debug stats num-dverts 0)) - (nop!) - (l.w a3 (-> stat tris)) - (nop!) - (l.w a1 (-> stat dverts)) - (add a2 a3 a2) - (s.w a2 (-> stat tris)) - (nop!) - (l.h a2 (-> stat fragments)) - (add dvert-total a1 dvert-total) - (s.w dvert-total (-> stat dverts)) - (add.i dvert-total a2 1) - (s.h dvert-total (-> stat fragments)) - (label stats-tfrag-asm-finish-stats) - (m v0 r0) - (jr ra :delay (add sp sp r0)) - (nop!) - (nop!) - (nop!)))) + (dvert-total :reg v1)) ;; what v1 becomes in the accumulate blocks + (label stats-tfrag-entry) + (nop!) + (nop!) + (m! work *tfrag-work*) + (nop!) + (l.w debug (-> frag debug-data)) + (nop!) + (l.vf vf10 (-> frag bsphere)) + (b.z debug stats-tfrag-asm-finish-stats :delay (l.vf vf14 (-> work frag-dists))) + (mula.x.vf vf16 vf10) + (l.b a2 (-> frag num-base-colors)) + (madda.y.vf vf17 vf10) + (l.b a2 (-> frag num-level0-colors)) + (madda.z.vf vf18 vf10) + (l.b a0 (-> frag num-level1-colors)) + (msub.w.vf vf9 vf19 vf0) + (nop!) + (mula.w.vf vf27 vf0) + (nop!) + (madda.x.vf vf24 vf10) + (nop!) + (madda.y.vf vf25 vf10) + (nop!) + (add.w.vf vf9 vf9 vf10) + (nop!) + (madd.z.vf vf11 vf26 vf10) + (nop!) + (add.w.vf vf12 vf11 vf10) + (nop!) + (sub.w.vf vf13 vf11 vf10) + (nop!) + (sub.z.vf vf11 vf0 vf12) + (nop!) + (m a3 vf9) + (nop!) + (pcgt.w a3 r0 a3) + (mmi-nop!) + (ppach a3 r0 a3) + (mmi-nop!) + (b.nz a3 stats-tfrag-asm-finish-stats :delay (add.z.vf vf12 vf14 vf12)) + (nop!) + (add.z.vf vf13 vf14 vf13) + (m t0 vf12) + (nop!) + (m a3 vf13) + (nop!) + (pcgt.w t0 r0 t0) + (mmi-nop!) + (pcgt.w a3 r0 a3) + (mmi-nop!) + (pinteh a3 t0 a3) + (mmi-nop!) + (ppacb a3 r0 a3) + (mmi-nop!) + (b.z a3 stats-tfrag-asm-select-base-stat :delay (nop!)) + (b.z a2 stats-tfrag-asm-select-base-stat :delay (nop!)) + (b.z a0 stats-tfrag-asm-select-middle-stat :delay (srl a0 a3 16)) + (b.z a0 stats-tfrag-asm-select-middle-stat :delay (nop!)) + (srl32 a0 a3 24) + (l.bu a2 (-> debug stats num-tris 2)) + (b.nz a0 stats-tfrag-asm-accumulate-selected-high :delay (l.w stat (-> work tr-stat-tfrag-near))) + (nop!) + (l.w stat (-> work tr-stat-tfrag)) + (label stats-tfrag-asm-accumulate-selected-high) + (nop!) + (l.bu dvert-total (-> debug stats num-dverts 2)) + (nop!) + (l.w a3 (-> stat tris)) + (nop!) + (l.w a1 (-> stat dverts)) + (add a2 a3 a2) + (s.w a2 (-> stat tris)) + (nop!) + (l.h a2 (-> stat fragments)) + (add dvert-total a1 dvert-total) + (s.w dvert-total (-> stat dverts)) + (add.i dvert-total a2 1) + (s.h dvert-total (-> stat fragments)) + (b stats-tfrag-asm-finish-stats :delay (nop!)) + (label stats-tfrag-asm-select-middle-stat) + (srl32 a0 a3 8) + (l.bu a2 (-> debug stats num-tris 1)) + (b.nz a0 stats-tfrag-asm-accumulate-selected-middle :delay (l.w stat (-> work tr-stat-tfrag-near))) + (nop!) + (l.w stat (-> work tr-stat-tfrag)) + (label stats-tfrag-asm-accumulate-selected-middle) + (nop!) + (l.bu dvert-total (-> debug stats num-dverts 1)) + (nop!) + (l.w a3 (-> stat tris)) + (nop!) + (l.w a1 (-> stat dverts)) + (add a2 a3 a2) + (s.w a2 (-> stat tris)) + (nop!) + (l.h a2 (-> stat fragments)) + (add dvert-total a1 dvert-total) + (s.w dvert-total (-> stat dverts)) + (add.i dvert-total a2 1) + (s.h dvert-total (-> stat fragments)) + (b stats-tfrag-asm-finish-stats :delay (nop!)) + (label stats-tfrag-asm-select-base-stat) + (srl32 a0 a3 8) + (l.bu a2 (-> debug stats num-tris 0)) + (b.nz a0 stats-tfrag-asm-accumulate-selected-base :delay (l.w stat (-> work tr-stat-tfrag-near))) + (b stats-tfrag-asm-accumulate-selected-base :delay (l.w stat (-> work tr-stat-tfrag))) + (label stats-tfrag-asm-accumulate-selected-base) + (nop!) + (l.bu dvert-total (-> debug stats num-dverts 0)) + (nop!) + (l.w a3 (-> stat tris)) + (nop!) + (l.w a1 (-> stat dverts)) + (add a2 a3 a2) + (s.w a2 (-> stat tris)) + (nop!) + (l.h a2 (-> stat fragments)) + (add dvert-total a1 dvert-total) + (s.w dvert-total (-> stat dverts)) + (add.i dvert-total a2 1) + (s.h dvert-total (-> stat fragments)) + (label stats-tfrag-asm-finish-stats) + (m v0 r0) + (jr ra :delay (add sp sp r0)) + (nop!) + (nop!) + (nop!)))) diff --git a/goal_src/jak1/engine/gfx/tie/tie-methods-ee-asm.gc b/goal_src/jak1/engine/gfx/tie/tie-methods-ee-asm.gc new file mode 100644 index 0000000000..fd1dbaae6c --- /dev/null +++ b/goal_src/jak1/engine/gfx/tie/tie-methods-ee-asm.gc @@ -0,0 +1,2261 @@ +;;-*-Lisp-*- +(in-package goal) +(bundles "ENGINE.CGO" "GAME.CGO") +(require "engine/gfx/tie/tie.gc") +(require "engine/gfx/background/wind-h.gc") +(require "engine/level/bsp.gc") +(require "kernel/gstate.gc") +(require "engine/draw/draw-node-h.gc") +(require "engine/gfx/generic/generic-h.gc") +(require "engine/gfx/background/background.gc") +(require "engine/camera/cam-interface-h.gc") +(require "engine/collide/collide-shape-h.gc") +(require "engine/gfx/tie/tie-work.gc") +(require "engine/gfx/background/subdivide.gc") + +;; TIE's four EE packet builders are the densest code in the renderer, and most of that density is +;; bookkeeping rather than arithmetic: waiting on a scratchpad DMA channel, handing back a full output +;; bank, and reaching into one of the two work areas. The forms below carry that weight so the +;; interesting parts stay visible. They emit exactly the instructions listed and nothing else. + +;; The scratchpad is a terrain-context: a small header naming the level and its mood, then a work area +;; that each renderer overlays with its own record. The builders keep their base register at the +;; scratchpad base and reach the header through it directly, so a field of the overlaid record needs the +;; work area's own offset added. That is where the otherwise mysterious sixteen comes from. +(defmacro spr-offset (type &rest path) + `(+ (offset-of terrain-context work) (offset-of ,type ,@path))) + +;; Flipping between a pair of scratchpad banks is one xor, because each pair differs in exactly the +;; bits its two offsets disagree on. Deriving the masks from the layout keeps the surprising ones +;; honest: the instance output pair toggles #x3000, not the #x1000 the 4 KiB bank size suggests, +;; because outa sits at 4096 and outb at 8192. +(defconstant TIE-INSTANCE-INPUT-FLIP (logxor (offset-of instance-tie-dma banka) (offset-of instance-tie-dma bankb))) + +(defconstant TIE-INSTANCE-OUTPUT-FLIP (logxor (offset-of instance-tie-dma outa) (offset-of instance-tie-dma outb))) + +;; One visibility word covers 32 instances, which is also one input bank's worth. +(defconstant TIE-INSTANCE-BLOCK-SIZE (- (offset-of instance-tie-dma bankb) (offset-of instance-tie-dma banka))) + +(defconstant TIE-PROTOTYPE-PALETTE-FLIP + (logxor (offset-of prototype-tie-dma colora) (offset-of prototype-tie-dma colorb))) + +(defconstant TIE-PROTOTYPE-OUTPUT-FLIP (logxor (offset-of prototype-tie-dma outa) (offset-of prototype-tie-dma outb))) + +(#unless PC_PORT + (defun draw-inline-array-instance-tie ((visibility pointer) (instances (inline-array instance-tie)) (instance-count int) (dma-buf dma-buffer)) + "Cull and transform the visible TIE instances, append per-LOD instance records to their + prototype buckets, and return the completed packet blocks to dma-buf." + (declare (asm-func none) (allow-saved-regs)) + ;; The instance pass never touches main memory directly if it can help it. Instances arrive 32 at a + ;; time into one of two 2 KiB scratchpad banks while the EE reads the other; finished packets go out + ;; of two 4 KiB banks, each returned as soon as it holds 256 quadwords. The level's visibility + ;; string is scanned a word at a time, and a word of zeroes skips 32 instances without so much as + ;; starting a transfer -- which is most of them, most frames. + ;; + ;; Field accesses through a typed base register carry their own displacement, so instance-tie-work, + ;; the instance record, the prototype bucket, and the DMA channel registers all read as fields. + ;; Numeric displacements are left only where the address really is untyped: the fixed scratchpad + ;; bank bases and the packet layouts assembled inside them. + (rlet ((instance-work :reg t0 :type instance-tie-work) + (to-spr-channel :reg t1 :type dma-bank-spr) + (input-bank :reg t2) + (instance-block :reg t3) + (scratch-base :reg t4 :type terrain-context) + (instance :reg t4 :type instance-tie) + (wind-time :reg t5) + (vis-bits :reg t6) + (vis-mask :reg t7) + ;; The six-quadword instance record under construction. Advancing by 96 and then writing the + ;; three command quadwords behind the cursor is why the negative displacements below are raw. + (packet-cursor :reg t8 :type tie-matrix) + (packet-qwc :reg t9) + (memory-cursor :reg v1) + ;; Two of the arguments are consumed during setup and their registers then carry something + ;; else for the rest of the function. Both names are given so each half reads correctly. + (dma-buf :reg a3 :type dma-buffer) + (from-spr-channel :reg a3 :type dma-bank-spr) + (instances :reg a1) + (output-bank :reg a1) + (vis-word-ptr :reg a0) + (instance-count :reg a2) + ;; The instance's prototype, reached through the pointer cached in the instance record. + (bucket :reg ra :type prototype-bucket-tie)) + ;; gp and s0 through s5 are short-lived temporaries whose meaning changes every few + ;; instructions, so they keep their physical names; each block says what they are holding. + ;; ra doubles as the saved visibility cursor between blocks and as the bucket pointer inside + ;; the classifier, which is why both spellings appear. + ;; + ;; VU0 registers, established by background-upload-vu0 and the background VU0 program and live + ;; for this whole function: + ;; vf1 1.0 in every lane + ;; vf2 the point handed to VU0 -- here, the instance's bounding sphere + ;; vf4/5/6 VU0 entry 42's results: plane distances, camera-temp times the point, and + ;; camera rotation times the point + ;; vf20..23 the ordinary background camera matrix + ;; vi1 the MAC sign flags VU0 leaves for the reject test + (asm-block setup-instance-stream + (label tie-instance-entry) + (add.i sp sp -128) + (s.d ra sp) + (s.q s0 sp 16) + (s.q s1 sp 32) + (s.q s2 sp 48) + (s.q s3 sp 64) + (s.q s4 sp 80) + (s.q s5 sp 96) + (s.q gp sp 112) + ;; t4 is the scratchpad base until the instance cursor takes it over. Both DMA channel + ;; addresses share the upper half of SPR_TO_BANK and SPR_FROM_BANK, so one lui does for + ;; both of them and for the bank bases. + (lui scratch-base #x7000) + (l.w memory-cursor (-> dma-buf base)) + (lui to-spr-channel #x1000) + (lui input-bank #x1000) + ;; Write back and invalidate the two cache ways covering the DMA cursor before the + ;; fromSPR channel starts writing behind the EE's back. + (sync.l) + (cache dxwbin memory-cursor 0) + (sync.l) + (cache dxwbin memory-cursor 1) + (sync.l) + (m! instance-work *instance-tie-work-copy*) + (ori to-spr-channel to-spr-channel #xd400) + (s.w dma-buf (-> instance-work dma-buffer)) + (ori from-spr-channel input-bank #xd000) + (m! wind-time *wind-work*) + (l.w vis-bits vis-word-ptr) + (ori input-bank instance (spr-offset instance-tie-dma banka)) + ;; 1.0 in every lane, used throughout as the multiply-add identity. + (max.w.vf vf1 vf0 vf0) + ;; The visibility walk pre-decrements, so the instance cursor starts one word low. + (add.i instance-block instances -4) + (ori output-bank instance (spr-offset instance-tie-dma outa)) + (s.w to-spr-channel (-> instance-work to-spr)) + (add.i packet-qwc r0 0) + (s.w from-spr-channel (-> instance-work from-spr)) + (m packet-cursor output-bank) + (s.w wind-time (-> instance-work wind-work)) + (nop!) + (l.vf vf3 (-> instance-work constant)) + (nop!) + (s.w r0 (-> instance-work flags))) + ;; Find the first occupied 32-instance visibility word. Empty words need no input transfer. + (asm-block stream-visible-instance-blocks + (label tie-instance-find-visible-word) + (b.nz vis-bits tie-instance-wait-first-instance-upload :delay (nop!)) + (add.i vis-word-ptr vis-word-ptr 4) + (add.i instance-block instance-block TIE-INSTANCE-BLOCK-SIZE) + (add.i instance-count instance-count -32) + (l.w vis-bits vis-word-ptr) + (b.le instance-count r0 tie-instance-finish :delay (nop!)) + (b tie-instance-find-visible-word :delay (nop!)) + ;; The first occupied block can start once the previous to-scratchpad transfer completes. + (label tie-instance-wait-first-instance-upload) + (l.w instance (-> to-spr-channel chcr)) + (nop! :count 3) + (and.i instance instance DMA-CHCR-STR) + (nop!) + (b.nz instance tie-instance-wait-first-instance-upload :delay (nop!)) + (s.w instance-block (-> to-spr-channel madr)) + (xor.i instance input-bank TIE-INSTANCE-INPUT-FLIP) + (s.w instance (-> to-spr-channel sadr)) + (add.i instance r0 128) + (s.w instance (-> to-spr-channel qwc)) + (add.i instance r0 DMA-CHCR-STR) + (s.w instance (-> to-spr-channel chcr)) + (nop!) + ;; Preserve this word's visibility address, select the other input bank, and look ahead for the + ;; next occupied word while the current block is consumed. + (label tie-instance-begin-instance-block) + (m ra vis-word-ptr) + (xor.i input-bank input-bank TIE-INSTANCE-INPUT-FLIP) + (add.i vis-word-ptr vis-word-ptr 4) + (m vis-mask vis-word-ptr) + (m instance input-bank) + (add.i vis-bits instance-count -32) + (b.gt vis-bits r0 tie-instance-find-next-visible-word :delay (l.w vis-bits vis-word-ptr)) + (b tie-instance-finish-instance-upload :delay (nop!)) + (nop!) + (l.w memory-cursor r0 400) + ;; Skip an empty look-ahead word, keeping its instance address in lockstep with visibility. + (label tie-instance-advance-visibility-word) + (add.i instance-count instance-count -32) + (add.i vis-word-ptr vis-word-ptr 4) + (b.le instance-count r0 tie-instance-finish-instance-upload :delay (l.w vis-bits vis-word-ptr)) + (nop! :count 2) + ;; Do not overwrite the input bank until the outstanding to-scratchpad DMA is finished. + (label tie-instance-find-next-visible-word) + (b.z vis-bits tie-instance-advance-visibility-word :delay (add.i instance-block instance-block TIE-INSTANCE-BLOCK-SIZE)) + (dma-wait-counting-stalls! t6 + to-spr-channel + (-> instance-work wait-to-spr) + tie-instance-wait-instance-upload + tie-instance-start-instance-upload) + ;; Transfer the next occupied 32-instance block into the bank not currently being read. + (label tie-instance-start-instance-upload) + (s.w instance-block (-> to-spr-channel madr)) + (xor.i vis-bits input-bank TIE-INSTANCE-INPUT-FLIP) + (s.w vis-bits (-> to-spr-channel sadr)) + (add.i vis-bits r0 128) + (s.w vis-bits (-> to-spr-channel qwc)) + (add.i vis-bits r0 DMA-CHCR-STR) + (b tie-instance-next-visibility-byte :delay (s.w vis-bits (-> to-spr-channel chcr))) + ;; At the tail, wait for the last input block rather than starting another look-ahead transfer. + (dma-wait-counting-stalls! t6 + to-spr-channel + (-> instance-work wait-to-spr) + tie-instance-finish-instance-upload + tie-instance-next-visibility-byte) + ;; Consume eight visibility bits at a time. A zero byte skips eight instance records at once. + (label tie-instance-next-visibility-byte) + (l.b vis-bits ra) + (add.i ra ra 1) + (nop!) + (s.w ra (-> instance-work cur-vis-bits)) + (b.nz vis-bits tie-instance-begin-visible-byte :delay (s.w vis-mask (-> instance-work end-vis-bits))) + (add.i instance-count instance-count -8) + (add.i instance instance 512) + (b tie-instance-finish-visibility-byte :delay (nop!))) + ;; Each set bit selects one 64-byte instance. VU0 entry 42 transforms its bounds through + ;; the background matrices and returns the common-plane reject bits before any packet work. + (asm-block cull-visible-instances + (label tie-instance-begin-visible-byte) + (add.i vis-mask r0 128) + (l.vf vf2 (-> instance bsphere)) + (label tie-instance-next-instance) + (add.i ra packet-qwc -246) + (nop!) + (b.le ra r0 tie-instance-classify-instance :delay (callms 42)) + ;; A transformed instance needs at most ten qwords beyond the current packet tail. Return a + ;; nearly full bank before classifying it so one instance never crosses output banks. + (dma-wait-counting-stalls! packet-cursor + from-spr-channel + (-> instance-work wait-from-spr) + tie-instance-wait-output-space + tie-instance-flush-output) + ;; Submit the occupied qwords and immediately switch to the other 4 KiB output bank. + (dma-return-output-bank! output-bank + TIE-INSTANCE-OUTPUT-FLIP + packet-cursor + packet-qwc + memory-cursor + from-spr-channel + tie-instance-flush-output)) + ;; Unpack the instance, decide what it is, and shear it into the wind. + ;; + ;; VU0 entry 42 has already turned the instance's bounding sphere into plane distances and a + ;; camera-space position, so the reject test and the LOD distance are both available before any + ;; packet work happens. From the distance the code derives three things at once: whether the + ;; instance is near enough to need the clipping renderer, which of the prototype's four + ;; geometries to use, and the morph weight that blends the chosen geometry toward the next + ;; coarser one. Ordinary TIE uses geometries 1 through 3, the near path uses 0, and Generic + ;; keeps its own four lists. + ;; + ;; VU0 registers through this block: + ;; vf8 camera-space distance to the instance, broadcast (z minus bounding radius) + ;; vf10..vf13 the four instance rows, unpacked and then sheared, sent to VU1 as-is + ;; vf14/vf15 the prototype's dists and rdists, then the LOD fractions derived from them + ;; vf16..vf19 wind temporaries and wind-const + ;; vf28..vf30 the morph quadword for LOD 1, 2 and 3 + ;; + ;; A morph quadword is (1 - f, unused, trunc(256 f), 256 - trunc(256 f)) where f falls from one + ;; to zero across the LOD band. x collapses interpolated vertices onto their neighbours and the + ;; zw pair blends their colors; see the interp-point pipeline in tie.gc for why those two sum to + ;; 256 instead of one. Geometry 3 uses far-morph itself, (1, 0, 0, 256), which is fully + ;; collapsed and fully blended. + (asm-block classify-instance-and-apply-wind + (label tie-instance-classify-instance) + ;; Unpack one instance. Its transform is stored as a matrix4h: sixteen int16 in four + ;; doublewords, which pextlh spreads one per 32-bit lane (each value landing in the upper + ;; half) and an arithmetic shift then brings back down. The three rotation rows shift by 16, + ;; so they arrive as plain sign-extended integers and itof with twelve fraction bits reads + ;; them as 1.12 fixed point. The translation row shifts by only 10, which leaves it + ;; multiplied by 64 -- world units, at 64 per int16 step. Sixty-four bytes of instance + ;; therefore reconstruct a full transform, which is the entire reason TIE can afford + ;; thousands of placements. + ;; + ;; Two things can reject the instance: the visibility bit unpacked from the level's + ;; visibility string, and the prototype's own "do not draw" flag. + (nop!) + (l.w bucket (-> instance bucket-ptr)) + (and gp vis-bits vis-mask) + (l.d s5 (-> instance origin long 3)) ;; translation row + (b.z gp tie-instance-advance-instance :delay (l.d s2 (-> instance origin long 0))) + (sll gp packet-qwc 4) + (l.d s4 (-> instance origin long 1)) + (pextlh s3 s5 r0) + (l.d s5 (-> instance origin long 2)) + (sra.w s3 s3 10) ;; sign extend and keep the implied factor of 64 + (l.q s1 (-> bucket dists)) + (pextlh s2 s2 r0) + (l.q s0 (-> bucket rdists)) + (sra.w s2 s2 16) ;; rotation rows only need the sign extension + (m.ni vf14 s1) + (pextlh s4 s4 r0) + (m.ni vf15 s0) + (sra.w s4 s4 16) + (m.ni vf13 s3) + (pextlh s5 s5 r0) + (m.ni vf10 s2) + (sra.w s3 s5 16) + ;; Two wind lookups, both keyed on the instance's authored wind-index. The gust sample + ;; itself is picked out of the 64-entry ring by (index + wind-time) & 63, so neighbouring + ;; objects do not all sway in phase and the whole field drifts as time advances. The + ;; instance's own spring state lives in the level's wind-vectors array at its index. + (l.hu s2 (-> instance wind-index)) + (add gp gp memory-cursor) ;; main-memory address this packet will land at + (m.ni vf11 s4) + (sll s5 s2 4) + (m.ni vf12 s3) + (add s4 s2 wind-time) + (l.w s2 (-> instance-work wind-work)) + (and.i s4 s4 63) + (l.w s3 (-> instance-work wind-vectors)) + (sll s1 s4 4) + (l.w s4 (-> bucket flags)) + (add s5 s3 s5) ;; s5 = &wind-vectors[wind-index] + (add s3 s1 s2) ;; s3 = &wind-work.wind-array[(wind-index + wind-time) & 63] + (and.i s1 s4 1) ;; prototype flag 0: never draw this prototype + (and.i s4 s4 2) ;; prototype flag 1: route this prototype through Generic + (b.nz s1 tie-instance-advance-instance :delay (m.ni s1 vi1)) + (itof.vf vf13 vf13) + ;; Read the wind clock for the next instance. On the first instance through this loop the + ;; register still holds the wind-work pointer left over from setup, so instance zero hashes + ;; against that instead -- it lands in the same 64-entry ring either way, so it only picks a + ;; different gust sample. + (l.w wind-time s2 (-> wind-work wind-time)) + (b.nz s1 tie-instance-advance-instance :delay (l.vf vf25 (-> instance-work min-dist))) + ;; Transform the prototype bounds into camera space. The guard-plane minimum supplies the + ;; near classification, while the two distance vectors select morph state and ordinary LOD. + (nop!) + (l.vf vf16 (-> instance-work hmge-d)) + (nop!) + (l.vf vf17 (-> instance-work hvdf-offset)) + (mula.z.vf vf1 vf6) + (s.w gp (-> instance-work upload-color-0 dma addr)) + (msub.w.vf vf8 vf1 vf2) + (s.w gp (-> instance-work generic-color-0 dma addr)) + (add.vf.xyz vf5 vf0 vf0) + (nop!) + (add.vf.xyz vf13 vf13 vf2) + (nop!) + (mula.vf vf1 vf1) + (nop!) + (sub.vf vf14 vf8 vf14) + (nop!) + (add.w.vf.w vf5 vf5 vf17) + (nop! :count 2) + (l.vf vf30 (-> instance-work far-morph)) + (min.vf vf25 vf8 vf25) + (nop!) + (msub.vf.xyz vf15 vf14 vf15) + (nop!) + (min.y.vf.w vf5 vf5 vf16) + (nop! :count 2) + (l.vf vf24 (-> instance-work guard-plane 0)) + (nop!) + (s.vf vf25 (-> instance-work min-dist)) + (min.vf.xyz vf15 vf15 vf1) + (nop!) + (max.x.vf.w vf5 vf5 vf16) + (nop!) + (sub.z.vf vf16 vf8 vf16) + (nop! :count 2) + (l.vf vf25 (-> instance-work guard-plane 1)) + (nop!) + (l.vf vf26 (-> instance-work guard-plane 2)) + (nop!) + (l.vf vf27 (-> instance-work guard-plane 3)) + (mula.x.vf vf24 vf2) + (nop!) + (madda.y.vf vf25 vf2) + (nop!) + (madda.z.vf vf26 vf2) + (nop!) + (msuba.w.vf vf27 vf0) + (nop!) + (msub.w.vf vf24 vf1 vf2) + (nop! :count 2) + (m s2 vf16) + (mul.w.vf vf28 vf15 vf30) + (nop!) + (mul.w.vf vf29 vf15 vf30) + (nop! :count 2) + (l.vf vf19 (-> instance-work wind-const)) + (itof.vf vf10 vf10 :fixed 12) + (nop!) + (pcgt.w s1 r0 s2) + (m s0 vf24) + (mul.x.vf vf28 vf1 vf28) + (nop!) + (mul.z.vf vf29 vf1 vf29) + (l.w s2 (-> bucket stiffness)) + (pcgt.w s0 r0 s0) + (s.vf vf5 (-> packet-cursor fog)) + (ppach s0 r0 s0) + (s.w s4 (-> packet-cursor fog)) + (or s1 s0 s1) + (s.vf vf14 (-> instance-work dist-test)) + (ppacb s1 r0 s1) + (mmi-nop!) + ;; fog.y takes the reject flags: bit set means this instance needs the near renderer. + (b.z s2 tie-instance-no-wind :delay (s.w s1 packet-cursor 84)) + ;; The wind spring. A prototype with zero stiffness skipped straight past this, so anything + ;; that reaches here is meant to sway. wind-vectors[wind-index] is this instance's persistent + ;; state -- two packed vector2w, a position and a velocity -- and wind-array[hash] is the gust + ;; sample driving it. wind-const supplies the spring coefficients: 0.5 damping, a force scale + ;; of 100, a timestep of 1/60, and -1.0 as the lower clamp against vf0.w's +1.0. + ;; + ;; The state is integer, so it is widened to floats with pextlw, integrated, clamped, and + ;; packed back down with ppacw before being stored. The result is scaled by the prototype's + ;; stiffness and then applied to the instance rows as a shear: each row's xz picks up a + ;; multiple of its own y, so geometry at the base of the object does not move and the top + ;; leans. That is the whole wind effect -- no per-vertex work, no extra VU pass, just three + ;; multiply-adds on the transform before it is sent. + (ftoi.vf.zw vf28 vf28) + (l.d s1 s5 8) + (ftoi.vf.zw vf29 vf29) + (l.d s2 s5) + (pextlw s1 r0 s1) + (l.vf vf16 s3 12) + (pextlw s3 r0 s2) + (m vf18 s1) + (nop!) + (m vf17 s3) + (mula.vf vf16 vf1) + (nop!) + (msuba.x.vf vf18 vf19) + (nop!) + (msub.y.vf vf16 vf17 vf19) + (nop!) + (sub.x.vf.x vf28 vf30 vf15) + (nop!) + (sub.z.vf.x vf29 vf1 vf15) + (nop!) + (itof.vf.zw vf28 vf28) + (nop!) + (mula.z.vf vf16 vf19) + (nop!) + (madd.vf vf18 vf1 vf18) + (nop!) + (itof.vf.zw vf29 vf29) + (nop!) + (add.y.vf.y vf28 vf0 vf0) + (nop!) + (add.y.vf.y vf29 vf0 vf0) + (nop!) + (mula.z.vf vf18 vf19) + (nop!) + (madd.vf vf17 vf17 vf1) + (nop!) + (itof.vf vf11 vf11 :fixed 12) + (nop!) + (itof.vf vf12 vf12 :fixed 12) + (nop!) + (sub.w.vf.w vf28 vf30 vf28) + (nop!) + (min.w.vf vf17 vf17 vf0) + (nop!) + (sub.w.vf.w vf29 vf30 vf29) + (nop! :count 4) + (m s3 vf18) + (max.w.vf vf27 vf17 vf19) + (nop!) + (ppacw s3 r0 s3) + (mmi-nop!) + (nop! :count 4) + (mul.w.vf vf27 vf27 vf15) + (nop! :count 5) + (mula.x.vf.yw vf0 vf0) + (nop!) + (mula.y.vf.xz vf27 vf10) + (nop!) + (madd.vf vf10 vf1 vf10) + (nop! :count 2) + (m s2 vf27) + (mula.x.vf.yw vf0 vf0) + (l.w s1 (-> instance-work paused)) + (mula.y.vf.xz vf27 vf11) + (nop!) + (madd.vf vf11 vf1 vf11) + (nop!) + (b.ne s1 s7 tie-instance-skip-wind-writeback :delay (ppacw s2 r0 s2)) + (mula.x.vf.yw vf0 vf0) + (s.d s3 s5 8) + (mula.y.vf.xz vf27 vf12) + (s.d s2 s5) + (b.nz s4 tie-instance-build-generic-records :delay (madd.vf vf12 vf1 vf12)) + (b tie-instance-build-ordinary-records :delay (nop!)) + ;; With the game paused the shear is still applied, but the spring state is not written back, + ;; so everything holds its current lean instead of continuing to sway behind the menu. + (label tie-instance-skip-wind-writeback) + (mula.x.vf.yw vf0 vf0) + (nop!) + (mula.y.vf.xz vf27 vf12) + (nop!) + (b.nz s4 tie-instance-build-generic-records :delay (madd.vf vf12 vf1 vf12)) + (b tie-instance-build-ordinary-records :delay (nop!)) + ;; Stiffness zero: no wind. Finish the LOD distance and morph values and convert the two + ;; remaining rotation rows, leaving the transform unsheared. + (label tie-instance-no-wind) + (ftoi.vf.zw vf28 vf28) + (nop!) + (ftoi.vf.zw vf29 vf29) + (nop!) + (sub.x.vf.x vf28 vf30 vf15) + (nop!) + (sub.z.vf.x vf29 vf1 vf15) + (nop!) + (itof.vf.zw vf28 vf28) + (nop!) + (itof.vf.zw vf29 vf29) + (nop!) + (add.y.vf.y vf28 vf0 vf0) + (nop!) + (add.y.vf.y vf29 vf0 vf0) + (nop!) + (sub.w.vf.w vf28 vf30 vf28) + (nop!) + (sub.w.vf.w vf29 vf30 vf29) + (nop!) + (itof.vf vf11 vf11 :fixed 12) + (nop!) + (b.nz s4 tie-instance-build-generic-records :delay (itof.vf vf12 vf12 :fixed 12))) + ;; Build ordinary or near instance records. The three projected-distance thresholds choose + ;; among ordinary variants 1..3; a near classification uses variant 0 and its near matrix. + (asm-block build-ordinary-instance-records + (label tie-instance-build-ordinary-records) + (nop!) + (l.w s5 packet-cursor 84) ;; fog.y: the near-renderer reject flags stored above + (nop!) + (l.w s4 (-> instance-work dist-test w)) + (add.i packet-qwc packet-qwc 6) + (l.w s3 (-> instance-work dist-test z)) + (b.nz s5 tie-instance-emit-near-record :delay (sub.w.vf.w vf10 vf10 vf10)) + (b.gt s4 r0 tie-instance-select-ordinary-lod-three :delay (nop!)) + (b.gt s3 r0 tie-instance-select-ordinary-lod-two :delay (nop!)) + (nop!) + (l.h s4 (-> bucket count 1)) + (nop!) + (l.w s5 (-> bucket next 1)) + (add.i s4 s4 1) + (s.vf vf28 (-> packet-cursor morph)) + (mula.x.vf vf20 vf10) + (add.i gp gp 96) ;; one instance record, tie-matrix sized + (madda.y.vf vf21 vf10) + (s.w gp (-> bucket next 1)) + (madd.z.vf vf10 vf22 vf10) + (s.h s4 (-> bucket count 1)) + (mula.x.vf vf20 vf11) + (l.bu s4 (-> bucket frag-count 1)) + (madda.y.vf vf21 vf11) + (l.hu gp (-> bucket base-qw 1)) + (madd.z.vf vf11 vf22 vf11) + (l.bu s3 (-> bucket index-start 1)) + (b tie-instance-emit-ordinary-record :delay (nop!)) + ;; Geometry variant two uses the middle distance result and its own list head and fragment data. + (label tie-instance-select-ordinary-lod-two) + (nop!) + (l.h s4 (-> bucket count 2)) + (nop!) + (l.w s5 (-> bucket next 2)) + (add.i s4 s4 1) + (s.vf vf29 (-> packet-cursor morph)) + (mula.x.vf vf20 vf10) + (add.i gp gp 96) ;; one instance record, tie-matrix sized + (madda.y.vf vf21 vf10) + (s.w gp (-> bucket next 2)) + (madd.z.vf vf10 vf22 vf10) + (s.h s4 (-> bucket count 2)) + (mula.x.vf vf20 vf11) + (l.bu s4 (-> bucket frag-count 2)) + (madda.y.vf vf21 vf11) + (l.hu gp (-> bucket base-qw 2)) + (madd.z.vf vf11 vf22 vf11) + (l.bu s3 (-> bucket index-start 2)) + (b tie-instance-emit-ordinary-record :delay (nop!)) + ;; Geometry variant three is the farthest ordinary representation. + (label tie-instance-select-ordinary-lod-three) + (nop!) + (l.h s4 (-> bucket count 3)) + (nop!) + (l.w s5 (-> bucket next 3)) + (add.i s4 s4 1) + (s.vf vf30 (-> packet-cursor morph)) + (mula.x.vf vf20 vf10) + (add.i gp gp 96) ;; one instance record, tie-matrix sized + (madda.y.vf vf21 vf10) + (s.w gp (-> bucket next 3)) + (madd.z.vf vf10 vf22 vf10) + (s.h s4 (-> bucket count 3)) + (mula.x.vf vf20 vf11) + (l.bu s4 (-> bucket frag-count 3)) + (madda.y.vf vf21 vf11) + (l.hu gp (-> bucket base-qw 3)) + (madd.z.vf vf11 vf22 vf11) + (l.bu s3 (-> bucket index-start 3)) + ;; Transform the four instance rows by the ordinary camera matrix and append the six-qword + ;; instance record expected by the ordinary TIE VU1 input. + (label tie-instance-emit-ordinary-record) + (mula.x.vf vf20 vf12) + (l.q s2 (-> instance-work upload-color-2)) + (madda.y.vf vf21 vf12) + (l.q s1 (-> instance-work upload-color-ret)) + (madd.z.vf vf12 vf22 vf12) + (sll gp gp 4) + (mula.x.vf vf20 vf13) + (add s3 s3 ra) + (madda.y.vf vf21 vf13) + (nop!) + (madda.z.vf vf22 vf13) + (nop!) + (madd.w.vf vf13 vf23 vf0) + (nop!) + (s.vf vf10 (-> packet-cursor mat vector 0)) + (nop!) + (s.vf vf11 (-> packet-cursor mat vector 1)) + (mov.z s2 s1 s5) + (s.vf vf12 (-> packet-cursor mat vector 2)) + (add.i packet-cursor packet-cursor 96) ;; one instance record + (b tie-instance-append-ordinary-command :delay (s.vf vf13 packet-cursor -48)) + ;; Variant zero uses the near projection matrix so geometry which crosses the camera plane can + ;; be clipped before perspective division. + (label tie-instance-emit-near-record) + (nop!) + (l.vf vf24 (-> instance-work tie-near-perspective-matrix 0)) + (nop!) + (l.vf vf25 (-> instance-work tie-near-perspective-matrix 1)) + (nop!) + (l.vf vf26 (-> instance-work tie-near-perspective-matrix 2)) + (nop!) + (l.vf vf27 (-> instance-work tie-near-perspective-matrix 3)) + (nop!) + (l.h s4 (-> bucket count 0)) + (nop!) + (l.w s5 (-> bucket next 0)) + (add.i s4 s4 1) + (s.vf vf28 (-> packet-cursor morph)) + (mula.x.vf vf24 vf10) + (add.i gp gp 96) ;; one instance record, tie-matrix sized + (madda.y.vf vf25 vf10) + (s.w gp (-> bucket next 0)) + (madd.z.vf vf10 vf26 vf10) + (s.h s4 (-> bucket count 0)) + (mula.x.vf vf24 vf11) + (l.bu s4 (-> bucket frag-count 0)) + (madda.y.vf vf25 vf11) + (l.hu gp (-> bucket base-qw 0)) + (madd.z.vf vf11 vf26 vf11) + (l.bu s3 (-> bucket index-start 0)) + (mula.x.vf vf24 vf12) + (l.q s2 (-> instance-work upload-color-2)) + (madda.y.vf vf25 vf12) + (l.q s1 (-> instance-work upload-color-ret)) + (madd.z.vf vf12 vf26 vf12) + (sll gp gp 4) + (mula.x.vf vf24 vf13) + (add s3 s3 ra) + (madda.y.vf vf25 vf13) + (nop!) + (madda.z.vf vf26 vf13) + (nop!) + (madd.w.vf vf13 vf27 vf0) + (nop!) + (s.vf vf10 (-> packet-cursor mat vector 0)) + (nop!) + (s.vf vf11 (-> packet-cursor mat vector 1)) + (nop!) + (s.vf vf12 (-> packet-cursor mat vector 2)) + (mov.z s2 s1 s5) + (s.vf vf13 (-> packet-cursor mat vector 3)) + (add.i packet-cursor packet-cursor 96) ;; one instance record + ;; Append the three-qword command template which links this instance record to the selected + ;; prototype/LOD list. Continue emitting commands while fragments remain in that geometry. + (label tie-instance-append-ordinary-command) + (nop!) + (l.w ra instance (offset-of instance-tie color-indices)) + (nop!) + (s.q s2 (-> instance-work upload-color-temp)) + (nop!) + (l.bu s2 s3 144) + (add s1 gp ra) + (s.w s5 (-> instance-work upload-color-temp dma addr)) + (add.i packet-qwc packet-qwc 3) + (s.w s1 (-> instance-work upload-color-1 dma addr)) + (sll s1 s2 2) + (s.h s2 (-> instance-work upload-color-1)) + (sll s2 s2 4) + (s.b s1 (-> instance-work upload-color-1 vif1 num)) + (add gp gp s2) + (l.q s2 (-> instance-work upload-color-0)) + (add.i s5 s5 48) + (l.q s1 (-> instance-work upload-color-1)) + (add.i packet-cursor packet-cursor 48) + (l.q s0 (-> instance-work upload-color-temp)) + (add.i s3 s3 1) + (s.q s2 packet-cursor -48) + (add.i s4 s4 -1) + (s.q s1 packet-cursor -32) + (b.le s4 r0 tie-instance-advance-instance :delay (s.q s0 packet-cursor -16)) + ;; Every additional fragment reuses the transformed rows and appends only another list command. + ;; Leave four qwords free so the command itself cannot straddle an output-bank transfer. + (label tie-instance-append-more-ordinary-commands) + (add.i s2 packet-qwc -252) + (nop!) + (b.le s2 r0 tie-instance-append-ordinary-after-flush :delay (nop!)) + (dma-wait-counting-stalls! packet-cursor + from-spr-channel + (-> instance-work wait-from-spr) + tie-instance-wait-ordinary-output + tie-instance-flush-ordinary-output) + ;; Return the full bank, then resume the same prototype list in the alternate bank. + (dma-return-output-bank! output-bank + TIE-INSTANCE-OUTPUT-FLIP + packet-cursor + packet-qwc + memory-cursor + from-spr-channel + tie-instance-flush-ordinary-output) + (label tie-instance-append-ordinary-after-flush) + (nop!) + (l.bu s2 s3 144) + (add s1 gp ra) + (s.w s5 (-> instance-work upload-color-temp dma addr)) + (add.i packet-qwc packet-qwc 3) + (s.w s1 (-> instance-work upload-color-1 dma addr)) + (sll s1 s2 2) + (s.h s2 (-> instance-work upload-color-1)) + (sll s2 s2 4) + (s.b s1 (-> instance-work upload-color-1 vif1 num)) + (add gp gp s2) + (l.q s2 (-> instance-work upload-color-0)) + (add.i s5 s5 48) + (l.q s1 (-> instance-work upload-color-1)) + (add.i packet-cursor packet-cursor 48) + (l.q s0 (-> instance-work upload-color-temp)) + (add.i s3 s3 1) + (s.q s2 packet-cursor -48) + (add.i s4 s4 -1) + (s.q s1 packet-cursor -32) + (b.gt s4 r0 tie-instance-append-more-ordinary-commands :delay (s.q s0 packet-cursor -16)) + (b tie-instance-advance-instance :delay (nop!))) + ;; Generic uses the same transformed instance but keeps its own per-geometry chains. + ;; + ;; First it works out how strongly this instance should show its environment map. vf6 is the + ;; camera-rotation-transformed position, so squaring and summing its lanes and taking the square root + ;; gives the distance; envmap-fade-far plus the bounding radius minus that distance, scaled by + ;; envmap-rfade, is the fade fraction, clamped to the 0..128 range in constant.y and truncated to + ;; eight bits. A result of zero means the instance is past its reflection range, and since the + ;; environment map was the only reason to route it through GENERIC at all, it drops straight back to + ;; the ordinary renderer. + ;; + ;; The fade goes out in the instance record's fifth quadword, where generic-tie reads it back as the + ;; scale on the environment tint. instance-tie-work's refl-fade-fac and refl-fade-end are the + ;; global ends of the same curve. + (asm-block build-generic-instance-records + (label tie-instance-build-generic-records) + (mul.vf.xyz vf16 vf6 vf6) + (nop! :count 2) + (l.vf vf9 (-> bucket envmap-rfade)) + (sub.w.vf.w vf10 vf10 vf10) + (nop! :count 3) + (adda.y.vf.x vf16 vf16) + (nop!) + (madd.z.vf.x vf16 vf1 vf16) + (nop!) + (sqrt.x Q vf16) + (nop!) + (mula.y.vf vf1 vf9) + (nop!) + (madda.w.vf vf1 vf2) + (nop! :count 2) + (waitq) + (msubq.vf vf16 vf1 Q) + (nop!) + (mul.x.vf vf16 vf16 vf9) + (nop!) + (max.x.vf.x vf16 vf16 vf0) + (nop!) + (min.y.vf.x vf16 vf16 vf3) + (nop!) + (ftoi.vf vf16 vf16) + (nop! :count 2) + (m s5 vf16) + (nop! :count 2) + (and.i s5 s5 255) + (nop!) + (b.z s5 tie-instance-build-ordinary-records :delay (nop!)) + (callms 29) + (s.w s4 (-> instance-work flags)) + (nop!) + (l.w s4 (-> instance-work dist-test w)) + (add.i packet-qwc packet-qwc 6) + (l.w s3 (-> instance-work dist-test z)) + (nop!) + (s.w s5 (-> packet-cursor fog)) + (b.gt s4 r0 tie-instance-select-generic-lod-three :delay (nop!)) + (b.gt s3 r0 tie-instance-select-generic-lod-two :delay (nop!)) + (nop!) + (l.h s4 (-> bucket generic-count 1)) + (nop!) + (l.w s5 (-> bucket generic-next 1)) + (add.i s4 s4 1) + (s.vf vf28 (-> packet-cursor morph)) + (mula.x.vf vf24 vf10) + (add.i gp gp 96) ;; one instance record, tie-matrix sized + (madda.y.vf vf25 vf10) + (s.w gp (-> bucket generic-next 1)) + (madd.z.vf vf10 vf26 vf10) + (s.h s4 (-> bucket generic-count 1)) + (mula.x.vf vf24 vf11) + (l.bu s3 (-> bucket frag-count 1)) + (madda.y.vf vf25 vf11) + (l.hu gp (-> bucket base-qw 1)) + (madd.z.vf vf11 vf26 vf11) + (l.bu s4 (-> bucket index-start 1)) + (b tie-instance-emit-generic-record :delay (nop!)) + ;; Select Generic geometry two when the middle distance threshold was crossed. + (label tie-instance-select-generic-lod-two) + (nop!) + (l.h s4 (-> bucket generic-count 2)) + (nop!) + (l.w s5 (-> bucket generic-next 2)) + (add.i s4 s4 1) + (s.vf vf29 (-> packet-cursor morph)) + (mula.x.vf vf24 vf10) + (add.i gp gp 96) ;; one instance record, tie-matrix sized + (madda.y.vf vf25 vf10) + (s.w gp (-> bucket generic-next 2)) + (madd.z.vf vf10 vf26 vf10) + (s.h s4 (-> bucket generic-count 2)) + (mula.x.vf vf24 vf11) + (l.bu s3 (-> bucket frag-count 2)) + (madda.y.vf vf25 vf11) + (l.hu gp (-> bucket base-qw 2)) + (madd.z.vf vf11 vf26 vf11) + (l.bu s4 (-> bucket index-start 2)) + (b tie-instance-emit-generic-record :delay (nop!)) + ;; Select the farthest Generic representation. + (label tie-instance-select-generic-lod-three) + (nop!) + (l.h s4 (-> bucket generic-count 3)) + (nop!) + (l.w s5 (-> bucket generic-next 3)) + (add.i s4 s4 1) + (s.vf vf30 (-> packet-cursor morph)) + (mula.x.vf vf24 vf10) + (add.i gp gp 96) ;; one instance record, tie-matrix sized + (madda.y.vf vf25 vf10) + (s.w gp (-> bucket generic-next 3)) + (madd.z.vf vf10 vf26 vf10) + (s.h s4 (-> bucket generic-count 3)) + (mula.x.vf vf24 vf11) + (l.bu s3 (-> bucket frag-count 3)) + (madda.y.vf vf25 vf11) + (l.hu gp (-> bucket base-qw 3)) + (madd.z.vf vf11 vf26 vf11) + (l.bu s4 (-> bucket index-start 3)) + ;; Transform the four rows with the Generic matrix, store the environment-map fade, and append + ;; this instance to the selected Generic prototype chain. + (label tie-instance-emit-generic-record) + (mula.x.vf vf24 vf12) + (sll gp gp 4) + (madda.y.vf vf25 vf12) + (add s4 s4 ra) + (madd.z.vf vf12 vf26 vf12) + (nop!) + (mula.x.vf vf24 vf13) + (nop!) + (madda.y.vf vf25 vf13) + (nop!) + (madda.z.vf vf26 vf13) + (nop!) + (madd.w.vf vf13 vf27 vf0) + (nop!) + (s.vf vf10 (-> packet-cursor mat vector 0)) + (nop!) + (s.vf vf11 (-> packet-cursor mat vector 1)) + (nop!) + (s.vf vf12 (-> packet-cursor mat vector 2)) + (nop!) + (s.vf vf13 (-> packet-cursor mat vector 3)) + (add.i packet-cursor packet-cursor 96) ;; one instance record + (nop!) + (l.w ra instance (offset-of instance-tie color-indices)) + (nop!) + (l.bu s2 s4 144) + (add s1 gp ra) + (s.w s5 (-> instance-work generic-color-0 vif1)) + (add.i packet-qwc packet-qwc 3) + (s.w s1 (-> instance-work generic-color-1 dma addr)) + (sll s1 s2 4) + (s.h s2 (-> instance-work generic-color-1)) + (add gp gp s1) + (l.q s2 (-> instance-work generic-color-0)) + (add.i s5 s5 48) + (l.q s1 (-> instance-work generic-color-1)) + (add.i packet-cursor packet-cursor 48) + (l.q s0 (-> instance-work generic-color-end)) + (add.i s4 s4 1) + (s.q s2 packet-cursor -48) + (add.i s3 s3 -1) + (s.q s1 packet-cursor -32) + (b.le s3 r0 tie-instance-advance-instance :delay (s.q s0 packet-cursor -16)) + ;; Additional Generic fragments share the transformed rows but each needs its own list command. + (label tie-instance-append-more-generic-commands) + (add.i s2 packet-qwc -252) + (nop!) + (b.le s2 r0 tie-instance-append-generic-after-flush :delay (nop!)) + (dma-wait-counting-stalls! packet-cursor + from-spr-channel + (-> instance-work wait-from-spr) + tie-instance-wait-generic-output + tie-instance-flush-generic-output) + ;; Return the full bank before continuing the Generic list in the alternate bank. + (dma-return-output-bank! output-bank + TIE-INSTANCE-OUTPUT-FLIP + packet-cursor + packet-qwc + memory-cursor + from-spr-channel + tie-instance-flush-generic-output) + (label tie-instance-append-generic-after-flush) + (nop!) + (l.bu s2 s4 144) + (add s1 gp ra) + (s.w s5 (-> instance-work generic-color-0 vif1)) + (add.i packet-qwc packet-qwc 3) + (s.w s1 (-> instance-work generic-color-1 dma addr)) + (sll s1 s2 4) + (s.h s2 (-> instance-work generic-color-1)) + (add gp gp s1) + (l.q s2 (-> instance-work generic-color-0)) + (add.i s5 s5 48) + (l.q s1 (-> instance-work generic-color-1)) + (add.i packet-cursor packet-cursor 48) + (l.q s0 (-> instance-work generic-color-end)) + (add.i s4 s4 1) + (s.q s2 packet-cursor -48) + (add.i s3 s3 -1) + (s.q s1 packet-cursor -32) + (b.gt s3 r0 tie-instance-append-more-generic-commands :delay (s.q s0 packet-cursor -16))) + ;; Shift to the next visibility bit and its corresponding 64-byte instance. + (asm-block advance-instance-stream + (label tie-instance-advance-instance) + (add.i instance-count instance-count -1) + (srl vis-mask vis-mask 1) + (add.i instance instance 64) + (nop!) + (b.nz vis-mask tie-instance-next-instance :delay (l.vf vf2 (-> instance bsphere))) + ;; Restore the saved visibility cursors after the byte, then continue within this scratchpad + ;; block or move to the next occupied 32-instance block. + (label tie-instance-finish-visibility-byte) + (nop!) + (l.w ra (-> instance-work cur-vis-bits)) + (nop!) + (l.w vis-mask (-> instance-work end-vis-bits)) + (b.ne ra vis-mask tie-instance-next-visibility-byte :delay (nop!)) + (b.gt instance-count r0 tie-instance-begin-instance-block :delay (nop!)) + (b.z packet-qwc tie-instance-wait-final-output :delay (nop!))) + ;; Return the last partial packet block, wait for it to leave scratchpad, and publish the + ;; advanced main-memory cursor back to the caller's dma-buffer. + ;; Wait until the previous bank is free before submitting the final partial bank. + (asm-block finish-instance-output + (dma-wait-counting-stalls! a0 + from-spr-channel + (-> instance-work wait-from-spr) + tie-instance-wait-final-output-space + tie-instance-flush-final-output) + ;; Unlike a full-bank flush, the last transfer uses exactly the occupied qword count. + (label tie-instance-flush-final-output) + (s.w a1 (-> from-spr-channel sadr)) + (xor.i a0 a1 TIE-INSTANCE-OUTPUT-FLIP) + (s.w memory-cursor (-> from-spr-channel madr)) + (sll a1 packet-qwc 4) + (add memory-cursor memory-cursor a1) + (m a0 a0) + (s.w packet-qwc (-> from-spr-channel qwc)) + (add.i a0 r0 DMA-CHCR-STR) + (s.w a0 (-> from-spr-channel chcr)) + (add.i a0 r0 0) + ;; The caller cannot reuse the scratchpad output until the final from-scratchpad DMA completes. + (dma-wait-counting-stalls! a0 + from-spr-channel + (-> instance-work wait-from-spr) + tie-instance-wait-final-output + tie-instance-finish) + (label tie-instance-finish) + (l.w a0 (-> instance-work dma-buffer)) + (nop!) + (s.w memory-cursor (-> dma-buf base)) + (nop!) + (m v0 r0) + (l.d ra sp) + (l.q gp sp 112) + (l.q s5 sp 96) + (l.q s4 sp 80) + (l.q s3 sp 64) + (l.q s2 sp 48) + (l.q s1 sp 32) + (l.q s0 sp 16) + (jr ra :delay (add.i sp sp 128)) + (nop! :count 2) + (nop!))))) + +;; The EE TIE renderer is a three-stage pipeline. The instance pass culls placements, chooses the +;; near, ordinary, or Generic LOD, and links compact instance records into the matching prototype +;; bucket. The prototype passes then expand that prototype's time-of-day palette while alternating +;; two scratchpad palette banks, upload every populated geometry fragment, and alternate two output +;; banks while the finished DMA stream returns to main memory. VU1 consumes one prototype fragment +;; at a time, alternates two instance-input banks so VIF can fill one while VU1 reads the other, +;; and cycles three GIF-output banks so it can build the next fragment without overwriting data +;; still waiting for the GIF. Prototype geometry is therefore uploaded once and reused by all +;; instance records which selected it. + +(#unless PC_PORT + (defun draw-inline-array-prototype-tie-generic-asm ((dma-buf dma-buffer) (prototype-count int) (prototypes prototype-array-tie)) + "Expand the selected TIE palettes and build Generic fragment chains for every populated + prototype geometry variant." + (declare (asm-func none) (allow-saved-regs)) + ;; The instance pass has already linked records into each prototype's four Generic lists. This + ;; pass turns those lists and the prototype fragments into the compact stream consumed later by + ;; generic-tie, while overlapping main-memory transfers with palette and packet construction. + ;; Keep palette uploads in the two 1 KiB scratchpad color banks and packet output in the + ;; two 4 KiB output banks. Main-memory writes are issued in blocks of at most 256 qwords. + ;; + ;; The prototype scratchpad holds two 1 KiB palette banks, two 4 KiB output banks, and the + ;; prototype-tie-dma state area, all reached with spr-offset. prototype-tie-work fields and the DMA + ;; channel registers are read as fields off their typed base registers. + (rlet ((prototype-work :reg t0 :type prototype-tie-work) + (from-spr-channel :reg t1 :type dma-bank-spr) + (output-bank :reg t2) + (palette-bank :reg t3) + (to-spr-channel :reg t4 :type dma-bank-spr) + (dma-buf :reg a0 :type dma-buffer) ;; the argument, before a0 becomes the packet qword count + (scratch-base :reg a3 :type terrain-context) + (mood :reg t8 :type mood-context) + (fragment :reg t6 :type tie-fragment) + (memory-cursor :reg v1)) + ;; The registers not aliased above genuinely change meaning every few instructions and keep their + ;; physical names: a0 is the caller's dma-buffer, then the count of quadwords occupied in the + ;; current output bank, then a DMA status word; a1 is the write cursor inside that bank, borrowed + ;; for channel status and byte counts; t5 through t9 pass around the bucket, its palette, its + ;; geometry pointers, the four time-of-day weight vectors, and finally the fragment cursor. + ;; Each block says what they are holding while it runs. + (asm-block setup-generic-prototype-stream + (label tie-generic-prototype-entry) + (add.i sp sp -112) + (s.d ra sp) + (s.q s1 sp 16) + (s.q s2 sp 32) + (s.q s3 sp 48) + (s.q s4 sp 64) + (s.q s5 sp 80) + (s.q gp sp 96) + (nop!) + (lui scratch-base #x7000) + (l.w memory-cursor (-> dma-buf base)) + (lui from-spr-channel #x1000) + (lui output-bank #x1000) + (sync.l) + (cache dxwbin memory-cursor 0) + (sync.l) + (cache dxwbin memory-cursor 1) + (sync.l) + (m! prototype-work *prototype-tie-work*) + (ori from-spr-channel from-spr-channel #xd000) + (ori to-spr-channel output-bank #xd400) + (ori palette-bank scratch-base 16) + (ori output-bank scratch-base 2064) + (s.w a0 scratch-base (spr-offset prototype-tie-dma dma-buffer)) + (add.i t7 a1 -1) + (nop!) + (l.w t6 a2 (offset-of prototype-array-tie data)) + (add.i a0 r0 0) + (m a1 output-bank)) + ;; One prototype per outer iteration. Its four Generic list heads say which of its geometries got + ;; instances; if all four are empty the prototype is skipped without reading anything else. Otherwise + ;; the heads, geometry pointers, fragment counts, and palette are copied into the scratchpad state + ;; area so the fragment loop can work from scratchpad rather than chasing main memory. + (asm-block prepare-generic-prototype + (label tie-generic-prototype-next-prototype) + (nop!) + (l.q t5 t6 (offset-of prototype-bucket-tie generic-next)) + (add.i t8 a2 4) + (s.w t7 scratch-base (spr-offset prototype-tie-dma length)) + (srl32 a2 t5 0) + (s.w t8 (-> prototype-work prototype-array)) + (pcpyud t7 t5 t5) + (l.w t9 t6 (offset-of prototype-bucket-tie tie-colors)) + (or t7 a2 t7) + (l.w a2 t6 (offset-of prototype-bucket-tie frag-count)) + (b.z t7 tie-generic-prototype-advance-prototype :delay (l.w mood (-> scratch-base bsp mood))) + (nop!) + (l.q t7 t6 (offset-of prototype-bucket-tie geometry)) + (nop!) + (s.q t5 scratch-base (spr-offset prototype-tie-dma next 0)) + (nop!) + (s.w a2 scratch-base (spr-offset prototype-tie-dma frag-count 0)) + (nop!) + (s.q t7 scratch-base (spr-offset prototype-tie-dma geometry 0)) + (nop!) + (l.d a2 (-> prototype-work clamp)) + (nop!) + (l.w t7 t9 4) + (add.i ra t9 12) + (l.q t5 (-> mood itimes 0)) + (sra t9 t7 2) + (nop!) + (add t9 t9 a0) + (add.i gp r0 221) + (sub t9 gp t9) + (nop!) + (b.ge t9 r0 tie-generic-prototype-upload-color-palette :delay (nop!))) + ;; A palette is up to 128 entries, which is 32 output quadwords plus its packet header, so it can + ;; easily be the thing that overflows the bank. Check first, return the bank if so, and only then + ;; start blending. + (asm-block upload-and-expand-generic-palette + (dma-wait-counting-stalls! a1 + from-spr-channel + (-> prototype-work generic-wait-from-spr) + tie-generic-prototype-wait-packet-output + tie-generic-prototype-flush-packet-output) + ;; Return the occupied bank and continue palette construction in the other output bank. + (dma-return-output-bank! output-bank + TIE-PROTOTYPE-OUTPUT-FLIP + a1 + a0 + memory-cursor + from-spr-channel + tie-generic-prototype-flush-packet-output) + ;; Round the entry count up to a whole 1 KiB chunk -- 32 entries of 32 bytes each -- and reserve + ;; the packet header plus the eight output quadwords the chunk will produce. + (label tie-generic-prototype-upload-color-palette) + (add.i t7 t7 31) + (l.w t6 t6 132) + (sra t7 t7 5) + (add.i a0 a0 1) + (sll t7 t7 3) + (s.w t6 (-> prototype-work generic-palette vif0)) + (add a0 a0 t7) + (s.h t7 (-> prototype-work generic-palette)) + (sll t9 t7 2) + (l.q t6 (-> mood itimes 1)) + (nop!) + (l.q gp (-> prototype-work generic-palette)) + (nop!) + (l.q t7 (-> mood itimes 2)) + (nop!) + (s.q gp a1) + (add.i a1 a1 16) + (l.q t8 (-> mood itimes 3)) + ;; The source colors come from main memory, so they are staged through the two 1 KiB color + ;; banks: start the next kilobyte's transfer, blend the kilobyte that just landed, repeat. + (dma-wait-counting-stalls! gp + to-spr-channel + (-> prototype-work generic-wait-to-spr) + tie-generic-prototype-wait-color-upload + tie-generic-prototype-start-color-upload) + ;; First kilobyte in flight. + (label tie-generic-prototype-start-color-upload) + (s.w ra (-> to-spr-channel madr)) + (add.i t9 t9 -32) + (s.w palette-bank (-> to-spr-channel sadr)) + (add.i gp r0 64) + (s.w gp (-> to-spr-channel qwc)) + (add.i gp r0 DMA-CHCR-STR) + (s.w gp (-> to-spr-channel chcr)) + (add.i ra ra 1024) + (label tie-generic-prototype-next-color-chunk) + (m s5 palette-bank) + (xor.i palette-bank palette-bank TIE-PROTOTYPE-PALETTE-FLIP) + (b.le t9 r0 tie-generic-prototype-finish-color-upload :delay (add.i t9 t9 -32)) + ;; Wait before reusing a color bank for the next 1 KiB palette chunk. + (dma-wait-counting-stalls! gp + to-spr-channel + (-> prototype-work generic-wait-to-spr) + tie-generic-prototype-wait-next-color-upload + tie-generic-prototype-start-next-color-upload) + ;; Kick off the next kilobyte before blending this one, so the transfer hides behind the + ;; multiply-accumulate loop rather than in front of it. + (label tie-generic-prototype-start-next-color-upload) + (s.w ra (-> to-spr-channel madr)) + (nop!) + (s.w palette-bank (-> to-spr-channel sadr)) + (add.i gp r0 64) + (s.w gp (-> to-spr-channel qwc)) + (add.i gp r0 DMA-CHCR-STR) + (s.w gp (-> to-spr-channel chcr)) + (add.i ra ra 1024) + (b tie-generic-prototype-expand-color-block :delay (nop!)) + ;; The last kilobyte has no successor to hide behind, so this is the one wait that is always + ;; paid in full. + (dma-wait-counting-stalls! gp + to-spr-channel + (-> prototype-work generic-wait-to-spr) + tie-generic-prototype-finish-color-upload + tie-generic-prototype-expand-color-block) + ;; Prime the blend loop with the first two source quadwords. + ;; + ;; A palette entry is not one color, it is eight -- one per time-of-day slot -- packed as + ;; 32 bytes. mood-context.itimes holds the eight weights for the current time of day, each + ;; replicated once per channel, which fills exactly four quadwords of halfwords. So one + ;; pmulth plus three pmaddh blend all eight slots of one entry across all four channels at + ;; once, and the whole level's palette becomes 32 words per kilobyte of source. + (label tie-generic-prototype-expand-color-block) + (add.i gp a1 128) + (l.q s2 s5 (offset-of time-of-day-palette data)) + (nop!) + (l.q s4 s5 (+ (offset-of time-of-day-palette data) 16)) + (pextlb s3 r0 s2) + (mmi-nop!) + (pextub s2 r0 s2) + (mmi-nop!) + (pmulth r0 s3 t5) + (mmi-nop!) + (pextlb s3 r0 s4) + (mmi-nop!) + (pmaddh r0 s2 t6) + (mmi-nop!) + (pextub s4 r0 s4) + (mmi-nop!) + (pmaddh r0 s3 t7) + (l.q s3 s5 (+ (offset-of time-of-day-palette data) 32)) + (add.i s5 s5 32) + (nop!) + (pmaddh r0 s4 t8) + (l.q s4 s5 (+ (offset-of time-of-day-palette data) 16)) + (pextlb s2 r0 s3) + (mmi-nop!) + ;; One entry per iteration. pmfhl.lh collects the four packed accumulators, the shift by six + ;; divides out the weights' 6-bit fraction, pcpyud plus add.h folds the two halves of the + ;; sum together, pminh clamps every channel against the packed limit in prototype-tie-work, + ;; and ppacb squeezes the four halfwords back down to one RGBA word. Thirty-two words from + ;; the current 1 KiB source chunk. + (label tie-generic-prototype-expand-eight-colors) + (pextub s3 r0 s3) + (mmi-nop!) + (pmfhl.lh s1) + (mmi-nop!) + (pmulth r0 s2 t5) + (mmi-nop!) + (srl.h s2 s1 6) + (mmi-nop!) + (pcpyud s1 s2 s2) + (mmi-nop!) + (add.h s2 s1 s2) + (mmi-nop!) + (pminh s2 s2 a2) + (mmi-nop!) + (ppacb s1 r0 s2) + (mmi-nop!) + (pextlb s2 r0 s4) + (mmi-nop!) + (pmaddh r0 s3 t6) + (s.w s1 a1) + (pextub s4 r0 s4) + (mmi-nop!) + (pmaddh r0 s2 t7) + (l.q s3 s5 (+ (offset-of time-of-day-palette data) 32)) + (add.i s5 s5 32) + (add.i a1 a1 4) + (pmaddh r0 s4 t8) + (l.q s4 s5 (+ (offset-of time-of-day-palette data) 16)) + (b.ne a1 gp tie-generic-prototype-expand-eight-colors :delay (pextlb s2 r0 s3)) + (b.ge t9 r0 tie-generic-prototype-next-color-chunk :delay (nop!)) + (nop!) + (l.w a2 scratch-base (spr-offset prototype-tie-dma next 0)) + (nop!) + (l.w t6 scratch-base (spr-offset prototype-tie-dma geometry 1)) + (b.z a2 + tie-generic-prototype-select-geometry-one + :delay + (l.bu t5 scratch-base (spr-offset prototype-tie-dma frag-count 1))) + (bgezal r0 tie-generic-prototype-begin-fragments :delay (nop!))) + ;; Four nearly identical selectors, one per Generic geometry, each testing its list head and calling + ;; the shared fragment loop if it is populated. They fall through into each other rather than + ;; looping, because the return address is what tells the fragment loop which selector to come back + ;; to -- cheaper than keeping a variant index around. + (asm-block select-generic-geometry + (label tie-generic-prototype-select-geometry-one) + (nop!) + (l.w a2 scratch-base (spr-offset prototype-tie-dma next 1)) + (nop!) + (l.w t6 scratch-base (spr-offset prototype-tie-dma geometry 1)) + (b.z a2 + tie-generic-prototype-select-geometry-two + :delay + (l.bu t5 scratch-base (spr-offset prototype-tie-dma frag-count 1))) + (bgezal r0 tie-generic-prototype-begin-fragments :delay (nop!)) + ;; Test Generic variant two. + (label tie-generic-prototype-select-geometry-two) + (nop!) + (l.w a2 scratch-base (spr-offset prototype-tie-dma next 2)) + (nop!) + (l.w t6 scratch-base (spr-offset prototype-tie-dma geometry 2)) + (b.z a2 + tie-generic-prototype-select-geometry-three + :delay + (l.bu t5 scratch-base (spr-offset prototype-tie-dma frag-count 2))) + (bgezal r0 tie-generic-prototype-begin-fragments :delay (nop!)) + ;; Test the final Generic variant before advancing to the next prototype bucket. + (label tie-generic-prototype-select-geometry-three) + (nop!) + (l.w a2 scratch-base (spr-offset prototype-tie-dma next 3)) + (nop!) + (l.w t6 scratch-base (spr-offset prototype-tie-dma geometry 3)) + (b.z a2 + tie-generic-prototype-advance-prototype + :delay + (l.bu t5 scratch-base (spr-offset prototype-tie-dma frag-count 3))) + (bgezal r0 tie-generic-prototype-begin-fragments :delay (nop!)) + ;; Advance the prototype-array cursor and clear the tail words which terminate its Generic + ;; chain. The saved prototype count controls the outer loop. + (label tie-generic-prototype-advance-prototype) + (nop!) + (l.w a2 (-> prototype-work prototype-array)) + (nop!) + (l.w t5 scratch-base (spr-offset prototype-tie-dma length)) + (nop!) + (l.w t6 a2 12) + (nop!) + (s.w r0 a1 -56) + (b.nz t5 tie-generic-prototype-next-prototype :delay (add.i t7 t5 -1)) + (b tie-generic-prototype-finish-output :delay (s.w r0 a1 -52)) + ;; A local call shares the fragment loop between all four geometry selectors; ra returns to the + ;; selector which should test the next variant. + (label tie-generic-prototype-begin-fragments) + (add.i t6 t6 32) + (s.w t5 (-> prototype-work generic-model-1 vif1))) + ;; Emit one fragment: patch the upload template with this fragment's stream addresses and counts and + ;; append it, followed by the CALL that runs the geometry's instance list. Check the bank has room + ;; first -- a fragment packet must not be split across two transfers, since the DMA controller would + ;; execute half a chain. + (asm-block emit-generic-fragments + (label tie-generic-prototype-next-fragment) + (add.i t7 a0 4) + (add.i t8 r0 255) + (sub t8 t8 t7) + (l.w t7 (-> fragment gif-ref)) + (b.ge t8 r0 tie-generic-prototype-emit-fragment :delay (l.hu t8 (-> fragment gif-count))) + ;; Wait for the previous bank before returning a packet which would overflow this one. + (dma-wait-counting-stalls! a1 + from-spr-channel + (-> prototype-work generic-wait-from-spr) + tie-generic-prototype-wait-fragment-output + tie-generic-prototype-flush-fragment-output) + ;; Switch output banks and reset the occupied-qword count. + (dma-return-output-bank! output-bank + TIE-PROTOTYPE-OUTPUT-FLIP + a1 + a0 + memory-cursor + from-spr-channel + tie-generic-prototype-flush-fragment-output) + ;; Patch the four-qword Generic model template with this fragment's stream counts, output + ;; address, instance-list head, and Generic data reference, then append it to the output bank. + (label tie-generic-prototype-emit-fragment) + (nop!) + (l.hu t9 (-> fragment tex-count)) + (add t8 t8 t9) + (l.w t9 (-> fragment point-ref)) + (add.i a0 a0 4) + (l.hu gp (-> fragment vertex-count)) + (sll s5 a0 4) + (s.w t7 (-> prototype-work generic-model-0 dma addr)) + (add t7 s5 memory-cursor) + (s.h t8 (-> prototype-work generic-model-0)) + (nop!) + (s.w t7 (-> prototype-work generic-model-0 vif1)) + (nop!) + (s.w a2 (-> prototype-work generic-model-next dma addr)) + (nop!) + (s.w t9 (-> prototype-work generic-model-1 dma addr)) + (nop!) + (s.h gp (-> prototype-work generic-model-1)) + (nop!) + (l.w t7 (-> fragment generic-ref)) + (nop!) + (l.hu t8 (-> fragment generic-count)) + (nop!) + (s.w t7 (-> prototype-work generic-model-2 dma addr)) + (nop!) + (s.h t8 (-> prototype-work generic-model-2)) + (nop!) + (l.q t7 (-> prototype-work generic-model-0)) + (nop!) + (l.q t8 (-> prototype-work generic-model-1)) + (nop!) + (l.q t9 (-> prototype-work generic-model-2)) + (nop!) + (l.q gp (-> prototype-work generic-model-next)) + (nop!) + (s.q t7 a1) + (add.i t5 t5 -1) + (s.q t8 a1 16) + (add.i a2 a2 48) + (s.q t9 a1 32) + (s.q gp a1 48) + (add.i a1 a1 64) + (b.gt t5 r0 tie-generic-prototype-next-fragment :delay (add.i fragment fragment 64)) + (jr ra :delay (nop!))) + ;; Last bank out. This transfer uses the exact occupied quadword count rather than a full 256, and + ;; unlike every other flush the code has to wait for it to land before publishing the advanced cursor + ;; back into the caller's dma-buffer. An empty bank skips the transfer entirely. + (asm-block finish-generic-prototype-output + (label tie-generic-prototype-finish-output) + (b.z a0 tie-generic-prototype-wait-output-dma :delay (nop!)) + (dma-wait-counting-stalls! a1 + from-spr-channel + (-> prototype-work generic-wait-from-spr) + tie-generic-prototype-wait-final-output + tie-generic-prototype-flush-final-output) + ;; Submit exactly the occupied part of the last output bank. + (label tie-generic-prototype-flush-final-output) + (s.w output-bank (-> from-spr-channel sadr)) + (nop!) + (s.w memory-cursor (-> from-spr-channel madr)) + (sll a1 a0 4) + (add memory-cursor memory-cursor a1) + (nop!) + (s.w a0 (-> from-spr-channel qwc)) + (add.i a0 r0 DMA-CHCR-STR) + (s.w a0 (-> from-spr-channel chcr)) + (nop!) + ;; Wait until the last bank has reached main memory before returning it to the caller. + (dma-wait-counting-stalls! a0 + from-spr-channel + (-> prototype-work generic-wait-from-spr) + tie-generic-prototype-wait-output-dma + tie-generic-prototype-finish) + (label tie-generic-prototype-finish) + (l.w a0 scratch-base (spr-offset prototype-tie-dma dma-buffer)) + (nop!) + (s.w memory-cursor (-> dma-buf base)) + (nop!) + (m v0 r0) + (l.d ra sp) + (l.q gp sp 96) + (l.q s5 sp 80) + (l.q s4 sp 64) + (l.q s3 sp 48) + (l.q s2 sp 32) + (l.q s1 sp 16) + (jr ra :delay (add.i sp sp 112)) + (nop!) + (nop!))))) + +(#unless PC_PORT + (defun draw-inline-array-prototype-tie-asm ((dma-buf dma-buffer) (prototype-count int) (prototypes prototype-array-tie)) + "Expand the selected TIE palettes and build ordinary TIE fragment chains for geometry + variants one through three." + (declare (asm-func none) (allow-saved-regs)) + ;; Instance records are already grouped by prototype and LOD. This pass supplies each populated + ;; fragment with its list, converts the prototype's time-of-day colors, and emits the VIF stream + ;; consumed by the ordinary TIE VU1 program. + ;; Alternate the 1 KiB palette banks and 4 KiB output banks so color conversion, packet + ;; construction, and scratchpad DMA can overlap. + ;; + ;; The scratchpad layout matches the Generic pass. spr-offset reaches the ordinary list heads, + ;; geometry pointers, fragment counts, and persistent output state in prototype-tie-dma; the packet + ;; templates and wait counters are fields of prototype-tie-work. + (rlet ((prototype-work :reg t0 :type prototype-tie-work) + (from-spr-channel :reg t1 :type dma-bank-spr) + (output-bank :reg t2) + (palette-bank :reg t3) + (to-spr-channel :reg t4 :type dma-bank-spr) + (dma-buf :reg a0 :type dma-buffer) ;; the argument, before a0 becomes the packet qword count + (scratch-base :reg a3 :type terrain-context) + (mood :reg t8 :type mood-context) + (fragment :reg t6 :type tie-fragment) + (memory-cursor :reg v1)) + ;; The registers not aliased above genuinely change meaning every few instructions and keep their + ;; physical names: a0 is the caller's dma-buffer, then the count of quadwords occupied in the + ;; current output bank, then a DMA status word; a1 is the write cursor inside that bank, borrowed + ;; for channel status and byte counts; t5 through t9 pass around the bucket, its palette, its + ;; geometry pointers, the four time-of-day weight vectors, and finally the fragment cursor. + ;; Each block says what they are holding while it runs. + (asm-block setup-ordinary-prototype-stream + (label tie-prototype-entry) + (add.i sp sp -112) + (s.d ra sp) + (s.q s1 sp 16) + (s.q s2 sp 32) + (s.q s3 sp 48) + (s.q s4 sp 64) + (s.q s5 sp 80) + (s.q gp sp 96) + (nop!) + (lui scratch-base #x7000) + (l.w memory-cursor (-> dma-buf base)) + (lui from-spr-channel #x1000) + (lui output-bank #x1000) + (sync.l) + (cache dxwbin memory-cursor 0) + (sync.l) + (cache dxwbin memory-cursor 1) + (sync.l) + (m! prototype-work *prototype-tie-work*) + (ori from-spr-channel from-spr-channel #xd000) + (ori to-spr-channel output-bank #xd400) + (ori palette-bank scratch-base 16) + (ori output-bank scratch-base 2064) + (s.w a0 scratch-base (spr-offset prototype-tie-dma dma-buffer)) + (add.i t7 a1 -1) + (nop!) + (l.w t5 a2 (offset-of prototype-array-tie data)) + (add.i a0 r0 0) + (m a1 output-bank)) + ;; One prototype per outer iteration. Geometry 0 belongs to the near renderer, so this pass only + ;; looks at 1 through 3; if none of their list heads got an instance the prototype is skipped without + ;; reading anything else. Otherwise the heads, geometry pointers, fragment counts, and palette are + ;; copied into the scratchpad state area so the fragment loop can work from scratchpad rather than + ;; chasing main memory. + (asm-block prepare-ordinary-prototype + (label tie-prototype-next-prototype) + (nop!) + (l.q t6 t5 (offset-of prototype-bucket-tie next)) + (add.i t8 a2 4) + (s.w t7 scratch-base (spr-offset prototype-tie-dma length)) + (srl32 a2 t6 0) + (s.w t8 (-> prototype-work prototype-array)) + (pcpyud t8 t6 t6) + (l.w t7 t5 (offset-of prototype-bucket-tie tie-colors)) + (or t8 a2 t8) + (l.w a2 t5 (offset-of prototype-bucket-tie frag-count)) + (b.z t8 tie-prototype-advance-prototype :delay (l.w mood (-> scratch-base bsp mood))) + (nop!) + (l.q t5 t5 (offset-of prototype-bucket-tie geometry)) + (nop!) + (s.q t6 scratch-base (spr-offset prototype-tie-dma next 0)) + (nop!) + (s.w a2 scratch-base (spr-offset prototype-tie-dma frag-count 0)) + (nop!) + (s.q t5 scratch-base (spr-offset prototype-tie-dma geometry 0)) + (nop!) + (l.d a2 (-> prototype-work clamp)) + (nop!) + (l.w t6 t7 4) + (add.i ra t7 12) + (l.q t5 (-> mood itimes 0)) + (sra t7 t6 2) + (nop!) + (add t7 t7 a0) + (add.i t9 r0 221) + (sub t7 t9 t7) + (nop!) + (b.ge t7 r0 tie-prototype-upload-color-palette :delay (nop!))) + ;; A palette is up to 128 entries, which is 32 output quadwords plus its packet header, so it can + ;; easily be the thing that overflows the bank. Check first, return the bank if so, and only then + ;; start blending. + (asm-block upload-and-expand-ordinary-palette + (dma-wait-counting-stalls! a1 + from-spr-channel + (-> prototype-work wait-from-spr) + tie-prototype-wait-packet-output + tie-prototype-flush-packet-output) + ;; Submit the occupied qwords and switch to the alternate output bank. + (dma-return-output-bank! output-bank + TIE-PROTOTYPE-OUTPUT-FLIP + a1 + a0 + memory-cursor + from-spr-channel + tie-prototype-flush-packet-output) + ;; Round the entry count up to a whole 1 KiB chunk -- 32 entries of 32 bytes each -- and reserve + ;; the packet header plus the eight output quadwords the chunk will produce. + (label tie-prototype-upload-color-palette) + (add.i t7 t6 31) + (l.q t6 (-> prototype-work upload-palette-0 quad)) + (sra t7 t7 5) + (add.i a0 a0 2) + (sll t9 t7 5) + (s.q t6 a1) + (sra t6 t9 2) + (s.b t9 (-> prototype-work upload-palette-1 vif1 num)) + (add a0 a0 t6) + (s.h t6 (-> prototype-work upload-palette-1)) + (nop!) + (l.q t6 (-> mood itimes 1)) + (nop!) + (l.q gp (-> prototype-work upload-palette-1)) + (nop!) + (l.q t7 (-> mood itimes 2)) + (nop!) + (s.q gp a1 16) + (add.i a1 a1 32) + (l.q t8 (-> mood itimes 3)) + ;; The source colors come from main memory, so they are staged through the two 1 KiB color + ;; banks: start the next kilobyte's transfer, blend the kilobyte that just landed, repeat. + (dma-wait-counting-stalls! gp + to-spr-channel + (-> prototype-work wait-to-spr) + tie-prototype-wait-color-upload + tie-prototype-start-color-upload) + ;; Start the first palette transfer. + (label tie-prototype-start-color-upload) + (s.w ra (-> to-spr-channel madr)) + (add.i t9 t9 -32) + (s.w palette-bank (-> to-spr-channel sadr)) + (add.i gp r0 64) + (s.w gp (-> to-spr-channel qwc)) + (add.i gp r0 DMA-CHCR-STR) + (s.w gp (-> to-spr-channel chcr)) + (add.i ra ra 1024) + (label tie-prototype-next-color-chunk) + (m s5 palette-bank) + (xor.i palette-bank palette-bank TIE-PROTOTYPE-PALETTE-FLIP) + (b.le t9 r0 tie-prototype-finish-color-upload :delay (add.i t9 t9 -32)) + ;; Wait before reusing a color bank for the next 1 KiB source chunk. + (dma-wait-counting-stalls! gp + to-spr-channel + (-> prototype-work wait-to-spr) + tie-prototype-wait-next-color-upload + tie-prototype-start-next-color-upload) + ;; Kick off the next kilobyte before blending this one, so the transfer hides behind the + ;; multiply-accumulate loop rather than in front of it. + (label tie-prototype-start-next-color-upload) + (s.w ra (-> to-spr-channel madr)) + (nop!) + (s.w palette-bank (-> to-spr-channel sadr)) + (add.i gp r0 64) + (s.w gp (-> to-spr-channel qwc)) + (add.i gp r0 DMA-CHCR-STR) + (s.w gp (-> to-spr-channel chcr)) + (add.i ra ra 1024) + (b tie-prototype-expand-color-block :delay (nop!)) + ;; The last kilobyte has no successor to hide behind, so this is the one wait that is always + ;; paid in full. + (dma-wait-counting-stalls! gp + to-spr-channel + (-> prototype-work wait-to-spr) + tie-prototype-finish-color-upload + tie-prototype-expand-color-block) + ;; Prime the blend loop with the first two source quadwords. + ;; + ;; A palette entry is not one color, it is eight -- one per time-of-day slot -- packed as + ;; 32 bytes. mood-context.itimes holds the eight weights for the current time of day, each + ;; replicated once per channel, which fills exactly four quadwords of halfwords. So one + ;; pmulth plus three pmaddh blend all eight slots of one entry across all four channels at + ;; once, and the whole level's palette becomes 32 words per kilobyte of source. + (label tie-prototype-expand-color-block) + (add.i gp a1 128) + (l.q s2 s5 (offset-of time-of-day-palette data)) + (nop!) + (l.q s4 s5 (+ (offset-of time-of-day-palette data) 16)) + (pextlb s3 r0 s2) + (mmi-nop!) + (pextub s2 r0 s2) + (mmi-nop!) + (pmulth r0 s3 t5) + (mmi-nop!) + (pextlb s3 r0 s4) + (mmi-nop!) + (pmaddh r0 s2 t6) + (mmi-nop!) + (pextub s4 r0 s4) + (mmi-nop!) + (pmaddh r0 s3 t7) + (l.q s3 s5 (+ (offset-of time-of-day-palette data) 32)) + (add.i s5 s5 32) + (nop!) + (pmaddh r0 s4 t8) + (l.q s4 s5 (+ (offset-of time-of-day-palette data) 16)) + (pextlb s2 r0 s3) + (mmi-nop!) + ;; One entry per iteration. pmfhl.lh collects the four packed accumulators, the shift by six + ;; divides out the weights' 6-bit fraction, pcpyud plus add.h folds the two halves of the + ;; sum together, pminh clamps every channel against the packed limit in prototype-tie-work, + ;; and ppacb squeezes the four halfwords back down to one RGBA word. Thirty-two words from + ;; the current 1 KiB source chunk for the VU1 palette. + (label tie-prototype-expand-eight-colors) + (pextub s3 r0 s3) + (mmi-nop!) + (pmfhl.lh s1) + (mmi-nop!) + (pmulth r0 s2 t5) + (mmi-nop!) + (srl.h s2 s1 6) + (mmi-nop!) + (pcpyud s1 s2 s2) + (mmi-nop!) + (add.h s2 s1 s2) + (mmi-nop!) + (pminh s2 s2 a2) + (mmi-nop!) + (ppacb s1 r0 s2) + (mmi-nop!) + (pextlb s2 r0 s4) + (mmi-nop!) + (pmaddh r0 s3 t6) + (s.w s1 a1) + (pextub s4 r0 s4) + (mmi-nop!) + (pmaddh r0 s2 t7) + (l.q s3 s5 (+ (offset-of time-of-day-palette data) 32)) + (add.i s5 s5 32) + (add.i a1 a1 4) + (pmaddh r0 s4 t8) + (l.q s4 s5 (+ (offset-of time-of-day-palette data) 16)) + (b.ne a1 gp tie-prototype-expand-eight-colors :delay (pextlb s2 r0 s3)) + (b.ge t9 r0 tie-prototype-next-color-chunk :delay (nop!)) + (nop!) + (l.w a2 scratch-base (spr-offset prototype-tie-dma next 1)) + (nop!) + (l.w t6 scratch-base (spr-offset prototype-tie-dma geometry 1)) + (b.z a2 tie-prototype-select-geometry-two :delay (l.bu t5 scratch-base (spr-offset prototype-tie-dma frag-count 1))) + (bgezal r0 tie-prototype-begin-fragments :delay (nop!))) + ;; Three nearly identical selectors, one per ordinary geometry, each testing its list head and calling + ;; the shared fragment loop if it is populated. They fall through into each other rather than looping, + ;; because the return address is what tells the fragment loop which selector to come back to. + (asm-block select-ordinary-geometry + (label tie-prototype-select-geometry-two) + (nop!) + (l.w a2 scratch-base (spr-offset prototype-tie-dma next 2)) + (nop!) + (l.w t6 scratch-base (spr-offset prototype-tie-dma geometry 2)) + (b.z a2 tie-prototype-select-geometry-three :delay (l.bu t5 scratch-base (spr-offset prototype-tie-dma frag-count 2))) + (bgezal r0 tie-prototype-begin-fragments :delay (nop!)) + ;; Variant three is the final ordinary LOD. + (label tie-prototype-select-geometry-three) + (nop!) + (l.w a2 scratch-base (spr-offset prototype-tie-dma next 3)) + (nop!) + (l.w t6 scratch-base (spr-offset prototype-tie-dma geometry 3)) + (b.z a2 tie-prototype-advance-prototype :delay (l.bu t5 scratch-base (spr-offset prototype-tie-dma frag-count 3))) + (bgezal r0 tie-prototype-begin-fragments :delay (nop!)) + ;; Advance the prototype-array cursor after all populated ordinary variants are emitted. + (label tie-prototype-advance-prototype) + (nop!) + (l.w a2 (-> prototype-work prototype-array)) + (nop!) + (l.w t6 scratch-base (spr-offset prototype-tie-dma length)) + (nop!) + (l.w t5 a2 (offset-of prototype-array-tie data)) + (b.nz t6 tie-prototype-next-prototype :delay (add.i t7 t6 -1)) + (b tie-prototype-finish-output :delay (nop!)) + ;; A local call shares the fragment loop between the three LOD selectors. + (label tie-prototype-begin-fragments) + (add.i t6 t6 32) + (nop!)) + ;; Emit one fragment: patch the upload template with this fragment's stream addresses and counts and + ;; append it, followed by the CALL that runs the geometry's instance list. Check the bank has room + ;; first -- a fragment packet must not be split across two transfers, since the DMA controller would + ;; execute half a chain. + (asm-block emit-ordinary-fragments + (label tie-prototype-next-fragment) + (add.i t7 a0 4) + (add.i t8 r0 255) + (sub t7 t8 t7) + (l.w t8 (-> fragment gif-ref)) + (b.ge t7 r0 tie-prototype-emit-fragment :delay (l.hu t7 (-> fragment gif-count))) + ;; Wait for the previous output bank before flushing a packet that would overflow this one. + (dma-wait-counting-stalls! a1 + from-spr-channel + (-> prototype-work wait-from-spr) + tie-prototype-wait-fragment-output + tie-prototype-flush-fragment-output) + ;; Submit this bank and continue the same geometry in the alternate bank. + (dma-return-output-bank! output-bank + TIE-PROTOTYPE-OUTPUT-FLIP + a1 + a0 + memory-cursor + from-spr-channel + tie-prototype-flush-fragment-output) + ;; Patch the four-qword ordinary model template with the fragment's vertex/triangle counts, + ;; stream reference, palette reference, and selected instance-list head. + (label tie-prototype-emit-fragment) + (nop!) + (l.w s5 (-> fragment point-ref)) + (nop!) + (l.hu s4 (-> fragment tex-count)) + (nop!) + (l.hu gp (-> fragment vertex-count)) + (nop!) + (s.w t8 (-> prototype-work upload-model-0 dma addr)) + (nop!) + (s.h s4 (-> prototype-work upload-model-0)) + (add.i t9 s4 #x4000) + (s.b s4 (-> prototype-work upload-model-0 vif1 num)) + (sll s4 s4 4) + (s.w s5 (-> prototype-work upload-model-2 dma addr)) + (add t8 t8 s4) + (s.h gp (-> prototype-work upload-model-2)) + (sll gp gp 1) + (s.w a2 (-> prototype-work upload-model-3 dma addr)) + (nop!) + (s.b gp (-> prototype-work upload-model-2 vif1 num)) + (nop!) + (s.w t8 (-> prototype-work upload-model-1 dma addr)) + (nop!) + (s.h t7 (-> prototype-work upload-model-1)) + (sll t7 t7 2) + (s.h t9 (-> prototype-work upload-model-1 vif1)) + (nop!) + (s.b t7 (-> prototype-work upload-model-1 vif1 num)) + (nop!) + (l.q t7 (-> prototype-work upload-model-0)) + (nop!) + (l.q t8 (-> prototype-work upload-model-1)) + (nop!) + (l.q t9 (-> prototype-work upload-model-2)) + (nop!) + (l.q gp (-> prototype-work upload-model-3)) + (add.i a0 a0 4) + (s.q t7 a1) + (add.i t5 t5 -1) + (s.q t8 a1 16) + (add.i a2 a2 48) + (s.q t9 a1 32) + (s.q gp a1 48) + (add.i a1 a1 64) + (b.gt t5 r0 tie-prototype-next-fragment :delay (add.i fragment fragment 64)) + (jr ra :delay (nop!))) + ;; Last bank out. This transfer uses the exact occupied quadword count rather than a full 256, and + ;; unlike every other flush the code has to wait for it to land before publishing the advanced cursor + ;; back into the caller's dma-buffer. An empty bank skips the transfer entirely. + (asm-block finish-ordinary-prototype-output + (label tie-prototype-finish-output) + (b.z a0 tie-prototype-wait-output-dma :delay (nop!)) + (dma-wait-counting-stalls! a1 + from-spr-channel + (-> prototype-work wait-from-spr) + tie-prototype-wait-final-output + tie-prototype-flush-final-output) + ;; Submit exactly the occupied qwords in the last bank. + (label tie-prototype-flush-final-output) + (s.w output-bank (-> from-spr-channel sadr)) + (nop!) + (s.w memory-cursor (-> from-spr-channel madr)) + (sll a1 a0 4) + (add memory-cursor memory-cursor a1) + (nop!) + (s.w a0 (-> from-spr-channel qwc)) + (add.i a0 r0 DMA-CHCR-STR) + (s.w a0 (-> from-spr-channel chcr)) + (nop!) + ;; Wait for the final transfer before publishing the advanced dma-buffer cursor. + (dma-wait-counting-stalls! a0 + from-spr-channel + (-> prototype-work wait-from-spr) + tie-prototype-wait-output-dma + tie-prototype-finish) + (label tie-prototype-finish) + (l.w a0 scratch-base (spr-offset prototype-tie-dma dma-buffer)) + (nop!) + (s.w memory-cursor (-> dma-buf base)) + (nop!) + (m v0 r0) + (l.d ra sp) + (l.q gp sp 96) + (l.q s5 sp 80) + (l.q s4 sp 64) + (l.q s3 sp 48) + (l.q s2 sp 32) + (l.q s1 sp 16) + (jr ra :delay (add.i sp sp 112)) + (nop!) + (nop!)))) + (defun draw-inline-array-prototype-tie-near-asm ((dma-buf dma-buffer) (prototype-count int) (prototypes prototype-array-tie)) + "Expand the selected TIE palettes and build near-TIE fragment chains for geometry variant + zero." + (declare (asm-func none) (allow-saved-regs)) + ;; Near instances use a separate VU1 program because geometry crossing the camera plane must be + ;; clipped before perspective division. The packet format otherwise shares the prototype palette + ;; expansion and scratchpad output machinery used by ordinary TIE. + ;; Use the same palette and output double buffers as ordinary TIE, but consume only geometry + ;; variant zero, whose instances were classified for the near-camera clipping path. + ;; + ;; Near TIE uses variant zero from the same prototype state area and the five-quadword near-model + ;; packet in prototype-tie-work: one extra V4-8 stream at VU address 30 carrying the per-vertex data + ;; the clipper needs, and one more MSCAL, which is the only structural difference from the ordinary + ;; builder. + (rlet ((prototype-work :reg t0 :type prototype-tie-work) + (from-spr-channel :reg t1 :type dma-bank-spr) + (output-bank :reg t2) + (palette-bank :reg t3) + (to-spr-channel :reg t4 :type dma-bank-spr) + (dma-buf :reg a0 :type dma-buffer) ;; the argument, before a0 becomes the packet qword count + (scratch-base :reg a3 :type terrain-context) + (mood :reg t8 :type mood-context) + (fragment :reg t6 :type tie-fragment) + (memory-cursor :reg v1)) + ;; The registers not aliased above genuinely change meaning every few instructions and keep their + ;; physical names: a0 is the caller's dma-buffer, then the count of quadwords occupied in the + ;; current output bank, then a DMA status word; a1 is the write cursor inside that bank, borrowed + ;; for channel status and byte counts; t5 through t9 pass around the bucket, its palette, its + ;; geometry pointers, the four time-of-day weight vectors, and finally the fragment cursor. + ;; Each block says what they are holding while it runs. + (asm-block setup-near-prototype-stream + (label tie-near-prototype-entry) + (add.i sp sp -112) + (s.d ra sp) + (s.q s1 sp 16) + (s.q s2 sp 32) + (s.q s3 sp 48) + (s.q s4 sp 64) + (s.q s5 sp 80) + (s.q gp sp 96) + (nop!) + (lui scratch-base #x7000) + (l.w memory-cursor (-> dma-buf base)) + (lui from-spr-channel #x1000) + (lui output-bank #x1000) + (sync.l) + (cache dxwbin memory-cursor 0) + (sync.l) + (cache dxwbin memory-cursor 1) + (sync.l) + (m! prototype-work *prototype-tie-work*) + (ori from-spr-channel from-spr-channel #xd000) + (ori to-spr-channel output-bank #xd400) + (ori palette-bank scratch-base 16) + (ori output-bank scratch-base 2064) + (s.w a0 scratch-base (spr-offset prototype-tie-dma dma-buffer)) + (add.i t7 a1 -1) + (nop!) + (l.w t5 a2 12) + (add.i a0 r0 0) + (m a1 output-bank)) + ;; One prototype per outer iteration, and only geometry 0 -- the one the instance pass assigns to + ;; anything close enough to need clipping. A prototype whose variant-zero list is empty is skipped + ;; without reading anything else; otherwise its list head, geometry pointer, fragment count, and + ;; palette are copied into the scratchpad state area. + (asm-block prepare-near-prototype + (label tie-near-prototype-next-prototype) + (nop!) + (l.w t6 t5 (offset-of prototype-bucket-tie next)) + (add.i a2 a2 4) + (s.w t7 scratch-base (spr-offset prototype-tie-dma length)) + (nop!) + (s.w a2 (-> prototype-work prototype-array)) + (nop!) + (l.w t7 t5 (offset-of prototype-bucket-tie tie-colors)) + (nop!) + (l.w a2 t5 (offset-of prototype-bucket-tie frag-count)) + (b.z t6 tie-near-prototype-advance-prototype :delay (l.w mood (-> scratch-base bsp mood))) + (nop!) + (l.q t5 t5 (offset-of prototype-bucket-tie geometry)) + (nop!) + (s.q t6 scratch-base (spr-offset prototype-tie-dma next 0)) + (nop!) + (s.w a2 scratch-base (spr-offset prototype-tie-dma frag-count 0)) + (nop!) + (s.q t5 scratch-base (spr-offset prototype-tie-dma geometry 0)) + (nop!) + (l.d a2 (-> prototype-work clamp)) + (nop!) + (l.w t6 t7 4) + (add.i ra t7 12) + (l.q t5 (-> mood itimes 0)) + (sra t7 t6 2) + (nop!) + (add t7 t7 a0) + (add.i t9 r0 221) + (sub t7 t9 t7) + (nop!) + (b.ge t7 r0 tie-near-prototype-upload-color-palette :delay (nop!))) + ;; A palette is up to 128 entries, which is 32 output quadwords plus its packet header, so it can + ;; easily be the thing that overflows the bank. Check first, return the bank if so, and only then + ;; start blending. + (asm-block upload-and-expand-near-palette + (dma-wait-counting-stalls! a1 + from-spr-channel + (-> prototype-work near-wait-from-spr) + tie-near-prototype-wait-packet-output + tie-near-prototype-flush-packet-output) + ;; Submit the occupied qwords and switch to the alternate output bank. + (dma-return-output-bank! output-bank + TIE-PROTOTYPE-OUTPUT-FLIP + a1 + a0 + memory-cursor + from-spr-channel + tie-near-prototype-flush-packet-output) + ;; Round the entry count up to a whole 1 KiB chunk -- 32 entries of 32 bytes each -- and reserve + ;; the packet header plus the eight output quadwords the chunk will produce. + (label tie-near-prototype-upload-color-palette) + (add.i t7 t6 31) + (l.q t6 (-> prototype-work upload-palette-0 quad)) + (sra t7 t7 5) + (add.i a0 a0 2) + (sll t9 t7 5) + (s.q t6 a1) + (sra t6 t9 2) + (s.b t9 (-> prototype-work upload-palette-1 vif1 num)) + (add a0 a0 t6) + (s.h t6 (-> prototype-work upload-palette-1)) + (nop!) + (l.q t6 (-> mood itimes 1)) + (nop!) + (l.q gp (-> prototype-work upload-palette-1)) + (nop!) + (l.q t7 (-> mood itimes 2)) + (nop!) + (s.q gp a1 16) + (add.i a1 a1 32) + (l.q t8 (-> mood itimes 3)) + ;; The source colors come from main memory, so they are staged through the two 1 KiB color + ;; banks: start the next kilobyte's transfer, blend the kilobyte that just landed, repeat. + (dma-wait-counting-stalls! gp + to-spr-channel + (-> prototype-work near-wait-to-spr) + tie-near-prototype-wait-color-upload + tie-near-prototype-start-color-upload) + ;; Start the first palette transfer. + (label tie-near-prototype-start-color-upload) + (s.w ra (-> to-spr-channel madr)) + (add.i t9 t9 -32) + (s.w palette-bank (-> to-spr-channel sadr)) + (add.i gp r0 64) + (s.w gp (-> to-spr-channel qwc)) + (add.i gp r0 DMA-CHCR-STR) + (s.w gp (-> to-spr-channel chcr)) + (add.i ra ra 1024) + (label tie-near-prototype-next-color-chunk) + (m s5 palette-bank) + (xor.i palette-bank palette-bank TIE-PROTOTYPE-PALETTE-FLIP) + (b.le t9 r0 tie-near-prototype-finish-color-upload :delay (add.i t9 t9 -32)) + ;; Wait before reusing a color bank for the next 1 KiB source chunk. + (dma-wait-counting-stalls! gp + to-spr-channel + (-> prototype-work near-wait-to-spr) + tie-near-prototype-wait-next-color-upload + tie-near-prototype-start-next-color-upload) + ;; Kick off the next kilobyte before blending this one, so the transfer hides behind the + ;; multiply-accumulate loop rather than in front of it. + (label tie-near-prototype-start-next-color-upload) + (s.w ra (-> to-spr-channel madr)) + (nop!) + (s.w palette-bank (-> to-spr-channel sadr)) + (add.i gp r0 64) + (s.w gp (-> to-spr-channel qwc)) + (add.i gp r0 DMA-CHCR-STR) + (s.w gp (-> to-spr-channel chcr)) + (add.i ra ra 1024) + (b tie-near-prototype-expand-color-block :delay (nop!)) + ;; Wait for the last palette block, which has no following transfer to cover its latency. + (dma-wait-counting-stalls! gp + to-spr-channel + (-> prototype-work near-wait-to-spr) + tie-near-prototype-finish-color-upload + tie-near-prototype-expand-color-block) + ;; Prime the blend loop with the first two source quadwords. + ;; + ;; A palette entry is not one color, it is eight -- one per time-of-day slot -- packed as + ;; 32 bytes. mood-context.itimes holds the eight weights for the current time of day, each + ;; replicated once per channel, which fills exactly four quadwords of halfwords. So one + ;; pmulth plus three pmaddh blend all eight slots of one entry across all four channels at + ;; once, and the whole level's palette becomes 32 words per kilobyte of source. + (label tie-near-prototype-expand-color-block) + (add.i gp a1 128) + (l.q s2 s5 (offset-of time-of-day-palette data)) + (nop!) + (l.q s4 s5 (+ (offset-of time-of-day-palette data) 16)) + (pextlb s3 r0 s2) + (mmi-nop!) + (pextub s2 r0 s2) + (mmi-nop!) + (pmulth r0 s3 t5) + (mmi-nop!) + (pextlb s3 r0 s4) + (mmi-nop!) + (pmaddh r0 s2 t6) + (mmi-nop!) + (pextub s4 r0 s4) + (mmi-nop!) + (pmaddh r0 s3 t7) + (l.q s3 s5 (+ (offset-of time-of-day-palette data) 32)) + (add.i s5 s5 32) + (nop!) + (pmaddh r0 s4 t8) + (l.q s4 s5 (+ (offset-of time-of-day-palette data) 16)) + (pextlb s2 r0 s3) + (mmi-nop!) + ;; One entry per iteration. pmfhl.lh collects the four packed accumulators, the shift by six + ;; divides out the weights' 6-bit fraction, pcpyud plus add.h folds the two halves of the + ;; sum together, pminh clamps every channel against the packed limit in prototype-tie-work, + ;; and ppacb squeezes the four halfwords back down to one RGBA word. Thirty-two words from + ;; the current 1 KiB source chunk for the VU1 palette. + (label tie-near-prototype-expand-eight-colors) + (pextub s3 r0 s3) + (mmi-nop!) + (pmfhl.lh s1) + (mmi-nop!) + (pmulth r0 s2 t5) + (mmi-nop!) + (srl.h s2 s1 6) + (mmi-nop!) + (pcpyud s1 s2 s2) + (mmi-nop!) + (add.h s2 s1 s2) + (mmi-nop!) + (pminh s2 s2 a2) + (mmi-nop!) + (ppacb s1 r0 s2) + (mmi-nop!) + (pextlb s2 r0 s4) + (mmi-nop!) + (pmaddh r0 s3 t6) + (s.w s1 a1) + (pextub s4 r0 s4) + (mmi-nop!) + (pmaddh r0 s2 t7) + (l.q s3 s5 (+ (offset-of time-of-day-palette data) 32)) + (add.i s5 s5 32) + (add.i a1 a1 4) + (pmaddh r0 s4 t8) + (l.q s4 s5 (+ (offset-of time-of-day-palette data) 16)) + (b.ne a1 gp tie-near-prototype-expand-eight-colors :delay (pextlb s2 r0 s3)) + (b.ge t9 r0 tie-near-prototype-next-color-chunk :delay (nop!)) + (nop!) + (l.w a2 scratch-base (spr-offset prototype-tie-dma next 0)) + (nop!) + (l.w t6 scratch-base (spr-offset prototype-tie-dma geometry 0)) + (nop!) + (l.bu t5 scratch-base (spr-offset prototype-tie-dma frag-count 0)) + (add.i t6 t6 32) + (nop!)) + ;; Emit one fragment, five template quadwords instead of four: the extra V4-8 stream at VU address 30 + ;; carries the per-vertex data the clipper needs, and there is one more MSCAL. Otherwise identical to + ;; the ordinary builder, including the rule that a fragment packet may not be split across two + ;; transfers. + (asm-block emit-near-fragments + (label tie-near-prototype-next-fragment) + (add.i t7 a0 4) + (add.i t8 r0 255) + (sub t7 t8 t7) + (l.w t8 (-> fragment gif-ref)) + (b.ge t7 r0 tie-near-prototype-emit-fragment :delay (l.hu t7 (-> fragment gif-count))) + ;; Wait for the previous output bank before flushing a packet that would overflow this one. + (dma-wait-counting-stalls! a1 + from-spr-channel + (-> prototype-work near-wait-from-spr) + tie-near-prototype-wait-fragment-output + tie-near-prototype-flush-fragment-output) + ;; Submit this bank and continue the same prototype in the alternate bank. + (dma-return-output-bank! output-bank + TIE-PROTOTYPE-OUTPUT-FLIP + a1 + a0 + memory-cursor + from-spr-channel + tie-near-prototype-flush-fragment-output) + ;; Patch the five-qword near model template with ordinary and clipping-stream references, + ;; vertex/triangle counts, palette state, and the variant-zero instance-list head. + (label tie-near-prototype-emit-fragment) + (nop!) + (l.w s5 (-> fragment dp-ref)) + (nop!) + (l.w s4 (-> fragment dp-qwc)) + (nop!) + (l.hu s3 (-> fragment tex-count)) + (nop!) + (l.w ra (-> fragment point-ref)) + (nop!) + (l.hu gp (-> fragment vertex-count)) + (nop!) + (s.w t8 (-> prototype-work upload-near-model-0 dma addr)) + (nop!) + (s.h s3 (-> prototype-work upload-near-model-0)) + (add.i t9 s3 #x4000) + (s.b s3 (-> prototype-work upload-near-model-0 vif1 num)) + (sll s3 s3 4) + (s.h s4 (-> prototype-work upload-near-model-2)) + (sll s4 s4 2) + (s.w s5 (-> prototype-work upload-near-model-2 dma addr)) + (add t8 t8 s3) + (s.b s4 (-> prototype-work upload-near-model-2 vif1 num)) + (nop!) + (s.h gp (-> prototype-work upload-near-model-3)) + (sll gp gp 1) + (s.w ra (-> prototype-work upload-near-model-3 dma addr)) + (nop!) + (s.b gp (-> prototype-work upload-near-model-3 vif1 num)) + (nop!) + (s.w t8 (-> prototype-work upload-near-model-1 dma addr)) + (nop!) + (s.h t7 (-> prototype-work upload-near-model-1)) + (sll t7 t7 2) + (s.h t9 (-> prototype-work upload-near-model-1 vif1)) + (nop!) + (s.b t7 (-> prototype-work upload-near-model-1 vif1 num)) + (nop!) + (s.w a2 (-> prototype-work upload-near-model-4 dma addr)) + (nop!) + (l.q t7 (-> prototype-work upload-near-model-0)) + (nop!) + (l.q t8 (-> prototype-work upload-near-model-1)) + (nop!) + (l.q t9 (-> prototype-work upload-near-model-2)) + (nop!) + (l.q ra (-> prototype-work upload-near-model-3)) + (nop!) + (l.q gp (-> prototype-work upload-near-model-4)) + (nop!) + (s.q t7 a1) + (add.i a0 a0 5) + (s.q t8 a1 16) + (add.i t5 t5 -1) + (s.q t9 a1 32) + (add.i a2 a2 48) + (s.q ra a1 48) + (s.q gp a1 64) + (add.i a1 a1 80) + (b.gt t5 r0 tie-near-prototype-next-fragment :delay (add.i fragment fragment 64)) + ;; Advance after every near fragment in this prototype has been appended. + (label tie-near-prototype-advance-prototype) + (nop!) + (l.w a2 (-> prototype-work prototype-array)) + (nop!) + (l.w t6 scratch-base (spr-offset prototype-tie-dma length)) + (nop!) + (l.w t5 a2 12) + (b.nz t6 tie-near-prototype-next-prototype :delay (add.i t7 t6 -1)) + (b tie-near-prototype-finish-output :delay (nop!))) + ;; Last bank out. This transfer uses the exact occupied quadword count rather than a full 256, and + ;; unlike every other flush the code has to wait for it to land before publishing the advanced cursor + ;; back into the caller's dma-buffer. An empty bank skips the transfer entirely. + (asm-block finish-near-prototype-output + (label tie-near-prototype-finish-output) + (b.z a0 tie-near-prototype-wait-output-dma :delay (nop!)) + (dma-wait-counting-stalls! a1 + from-spr-channel + (-> prototype-work near-wait-from-spr) + tie-near-prototype-wait-final-output + tie-near-prototype-flush-final-output) + ;; Submit exactly the occupied qwords in the last bank. + (label tie-near-prototype-flush-final-output) + (s.w output-bank (-> from-spr-channel sadr)) + (nop!) + (s.w memory-cursor (-> from-spr-channel madr)) + (sll a1 a0 4) + (add memory-cursor memory-cursor a1) + (nop!) + (s.w a0 (-> from-spr-channel qwc)) + (add.i a0 r0 DMA-CHCR-STR) + (s.w a0 (-> from-spr-channel chcr)) + (nop!) + ;; Wait for the last transfer before returning its main-memory cursor. + (dma-wait-counting-stalls! a0 + from-spr-channel + (-> prototype-work near-wait-from-spr) + tie-near-prototype-wait-output-dma + tie-near-prototype-finish) + (label tie-near-prototype-finish) + (l.w a0 scratch-base (spr-offset prototype-tie-dma dma-buffer)) + (nop!) + (s.w memory-cursor (-> dma-buf base)) + (nop!) + (m v0 r0) + (l.d ra sp) + (l.q gp sp 96) + (l.q s5 sp 80) + (l.q s4 sp 64) + (l.q s3 sp 48) + (l.q s2 sp 32) + (l.q s1 sp 16) + (jr ra :delay (add.i sp sp 112)) + (nop!) + (nop!))))) diff --git a/goal_src/jak1/engine/gfx/tie/tie-methods.gc b/goal_src/jak1/engine/gfx/tie/tie-methods.gc index f822749146..1b7c2df90e 100644 --- a/goal_src/jak1/engine/gfx/tie/tie-methods.gc +++ b/goal_src/jak1/engine/gfx/tie/tie-methods.gc @@ -12,2136 +12,6 @@ (require "engine/collide/collide-shape-h.gc") (require "engine/gfx/tie/tie-work.gc") (require "engine/gfx/background/subdivide.gc") -;; TIE's four EE packet builders are the densest code in the renderer, and most of that density is -;; bookkeeping rather than arithmetic: waiting on a scratchpad DMA channel, handing back a full output -;; bank, and reaching into one of the two work areas. The forms below carry that weight so the -;; interesting parts stay visible. They emit exactly the instructions listed and nothing else. - -;; The scratchpad is a terrain-context: a small header naming the level and its mood, then a work area -;; that each renderer overlays with its own record. The builders keep their base register at the -;; scratchpad base and reach the header through it directly, so a field of the overlaid record needs the -;; work area's own offset added. That is where the otherwise mysterious sixteen comes from. -(defmacro spr-offset (type &rest path) - `(+ (offset-of terrain-context work) (offset-of ,type ,@path))) - -;; Flipping between a pair of scratchpad banks is one xor, because each pair differs in exactly the -;; bits its two offsets disagree on. Deriving the masks from the layout keeps the surprising ones -;; honest: the instance output pair toggles #x3000, not the #x1000 the 4 KiB bank size suggests, -;; because outa sits at 4096 and outb at 8192. -(defconstant TIE-INSTANCE-INPUT-FLIP - (logxor (offset-of instance-tie-dma banka) (offset-of instance-tie-dma bankb))) - -(defconstant TIE-INSTANCE-OUTPUT-FLIP - (logxor (offset-of instance-tie-dma outa) (offset-of instance-tie-dma outb))) - -;; One visibility word covers 32 instances, which is also one input bank's worth. -(defconstant TIE-INSTANCE-BLOCK-SIZE - (- (offset-of instance-tie-dma bankb) (offset-of instance-tie-dma banka))) - -(defconstant TIE-PROTOTYPE-PALETTE-FLIP - (logxor (offset-of prototype-tie-dma colora) (offset-of prototype-tie-dma colorb))) - -(defconstant TIE-PROTOTYPE-OUTPUT-FLIP - (logxor (offset-of prototype-tie-dma outa) (offset-of prototype-tie-dma outb))) - -(#when PC_PORT - (def-mips2c draw-inline-array-instance-tie (function pointer (inline-array instance-tie) int dma-buffer none))) - -(#unless PC_PORT - (defun draw-inline-array-instance-tie ((visibility pointer) (instances (inline-array instance-tie)) (instance-count int) (dma-buf dma-buffer)) - "Cull and transform the visible TIE instances, append per-LOD instance records to their - prototype buckets, and return the completed packet blocks to dma-buf." - (declare (asm-func none) (allow-saved-regs)) - ;; The instance pass never touches main memory directly if it can help it. Instances arrive 32 at a - ;; time into one of two 2 KiB scratchpad banks while the EE reads the other; finished packets go out - ;; of two 4 KiB banks, each returned as soon as it holds 256 quadwords. The level's visibility - ;; string is scanned a word at a time, and a word of zeroes skips 32 instances without so much as - ;; starting a transfer -- which is most of them, most frames. - ;; - ;; Field accesses through a typed base register carry their own displacement, so instance-tie-work, - ;; the instance record, the prototype bucket, and the DMA channel registers all read as fields. - ;; Numeric displacements are left only where the address really is untyped: the fixed scratchpad - ;; bank bases and the packet layouts assembled inside them. - (rlet ((instance-work :reg t0 :type instance-tie-work) - (to-spr-channel :reg t1 :type dma-bank-spr) - (input-bank :reg t2) - (instance-block :reg t3) - (scratch-base :reg t4 :type terrain-context) - (instance :reg t4 :type instance-tie) - (wind-time :reg t5) - (vis-bits :reg t6) - (vis-mask :reg t7) - ;; The six-quadword instance record under construction. Advancing by 96 and then writing the - ;; three command quadwords behind the cursor is why the negative displacements below are raw. - (packet-cursor :reg t8 :type tie-matrix) - (packet-qwc :reg t9) - (memory-cursor :reg v1) - ;; Two of the arguments are consumed during setup and their registers then carry something - ;; else for the rest of the function. Both names are given so each half reads correctly. - (dma-buf :reg a3 :type dma-buffer) - (from-spr-channel :reg a3 :type dma-bank-spr) - (instances :reg a1) - (output-bank :reg a1) - (vis-word-ptr :reg a0) - (instance-count :reg a2) - ;; The instance's prototype, reached through the pointer cached in the instance record. - (bucket :reg ra :type prototype-bucket-tie)) - ;; gp and s0 through s5 are short-lived temporaries whose meaning changes every few - ;; instructions, so they keep their physical names; each block says what they are holding. - ;; ra doubles as the saved visibility cursor between blocks and as the bucket pointer inside - ;; the classifier, which is why both spellings appear. - ;; - ;; VU0 registers, established by background-upload-vu0 and the background VU0 program and live - ;; for this whole function: - ;; vf1 1.0 in every lane - ;; vf2 the point handed to VU0 -- here, the instance's bounding sphere - ;; vf4/5/6 VU0 entry 42's results: plane distances, camera-temp times the point, and - ;; camera rotation times the point - ;; vf20..23 the ordinary background camera matrix - ;; vi1 the MAC sign flags VU0 leaves for the reject test - (asm-block setup-instance-stream - (label tie-instance-entry) - (add.i sp sp -128) - (s.d ra sp) - (s.q s0 sp 16) - (s.q s1 sp 32) - (s.q s2 sp 48) - (s.q s3 sp 64) - (s.q s4 sp 80) - (s.q s5 sp 96) - (s.q gp sp 112) - ;; t4 is the scratchpad base until the instance cursor takes it over. Both DMA channel - ;; addresses share the upper half of SPR_TO_BANK and SPR_FROM_BANK, so one lui does for - ;; both of them and for the bank bases. - (lui scratch-base #x7000) - (l.w memory-cursor (-> dma-buf base)) - (lui to-spr-channel #x1000) - (lui input-bank #x1000) - ;; Write back and invalidate the two cache ways covering the DMA cursor before the - ;; fromSPR channel starts writing behind the EE's back. - (sync.l) - (cache dxwbin memory-cursor 0) - (sync.l) - (cache dxwbin memory-cursor 1) - (sync.l) - (m! instance-work *instance-tie-work-copy*) - (ori to-spr-channel to-spr-channel #xd400) - (s.w dma-buf (-> instance-work dma-buffer)) - (ori from-spr-channel input-bank #xd000) - (m! wind-time *wind-work*) - (l.w vis-bits vis-word-ptr) - (ori input-bank instance (spr-offset instance-tie-dma banka)) - ;; 1.0 in every lane, used throughout as the multiply-add identity. - (max.w.vf vf1 vf0 vf0) - ;; The visibility walk pre-decrements, so the instance cursor starts one word low. - (add.i instance-block instances -4) - (ori output-bank instance (spr-offset instance-tie-dma outa)) - (s.w to-spr-channel (-> instance-work to-spr)) - (add.i packet-qwc r0 0) - (s.w from-spr-channel (-> instance-work from-spr)) - (m packet-cursor output-bank) - (s.w wind-time (-> instance-work wind-work)) - (nop!) - (l.vf vf3 (-> instance-work constant)) - (nop!) - (s.w r0 (-> instance-work flags))) - ;; Find the first occupied 32-instance visibility word. Empty words need no input transfer. - (asm-block stream-visible-instance-blocks - (label tie-instance-find-visible-word) - (b.nz vis-bits tie-instance-wait-first-instance-upload :delay (nop!)) - (add.i vis-word-ptr vis-word-ptr 4) - (add.i instance-block instance-block TIE-INSTANCE-BLOCK-SIZE) - (add.i instance-count instance-count -32) - (l.w vis-bits vis-word-ptr) - (b.le instance-count r0 tie-instance-finish :delay (nop!)) - (b tie-instance-find-visible-word :delay (nop!)) - ;; The first occupied block can start once the previous to-scratchpad transfer completes. - (label tie-instance-wait-first-instance-upload) - (l.w instance (-> to-spr-channel chcr)) - (nop! :count 3) - (and.i instance instance DMA-CHCR-STR) - (nop!) - (b.nz instance tie-instance-wait-first-instance-upload :delay (nop!)) - (s.w instance-block (-> to-spr-channel madr)) - (xor.i instance input-bank TIE-INSTANCE-INPUT-FLIP) - (s.w instance (-> to-spr-channel sadr)) - (add.i instance r0 128) - (s.w instance (-> to-spr-channel qwc)) - (add.i instance r0 DMA-CHCR-STR) - (s.w instance (-> to-spr-channel chcr)) - (nop!) - ;; Preserve this word's visibility address, select the other input bank, and look ahead for the - ;; next occupied word while the current block is consumed. - (label tie-instance-begin-instance-block) - (m ra vis-word-ptr) - (xor.i input-bank input-bank TIE-INSTANCE-INPUT-FLIP) - (add.i vis-word-ptr vis-word-ptr 4) - (m vis-mask vis-word-ptr) - (m instance input-bank) - (add.i vis-bits instance-count -32) - (b.gt vis-bits r0 tie-instance-find-next-visible-word :delay (l.w vis-bits vis-word-ptr)) - (b tie-instance-finish-instance-upload :delay (nop!)) - (nop!) - (l.w memory-cursor r0 400) - ;; Skip an empty look-ahead word, keeping its instance address in lockstep with visibility. - (label tie-instance-advance-visibility-word) - (add.i instance-count instance-count -32) - (add.i vis-word-ptr vis-word-ptr 4) - (b.le instance-count r0 tie-instance-finish-instance-upload :delay (l.w vis-bits vis-word-ptr)) - (nop! :count 2) - ;; Do not overwrite the input bank until the outstanding to-scratchpad DMA is finished. - (label tie-instance-find-next-visible-word) - (b.z vis-bits tie-instance-advance-visibility-word :delay (add.i instance-block instance-block TIE-INSTANCE-BLOCK-SIZE)) - (dma-wait-counting-stalls! t6 to-spr-channel (-> instance-work wait-to-spr) - tie-instance-wait-instance-upload tie-instance-start-instance-upload) - ;; Transfer the next occupied 32-instance block into the bank not currently being read. - (label tie-instance-start-instance-upload) - (s.w instance-block (-> to-spr-channel madr)) - (xor.i vis-bits input-bank TIE-INSTANCE-INPUT-FLIP) - (s.w vis-bits (-> to-spr-channel sadr)) - (add.i vis-bits r0 128) - (s.w vis-bits (-> to-spr-channel qwc)) - (add.i vis-bits r0 DMA-CHCR-STR) - (b tie-instance-next-visibility-byte :delay (s.w vis-bits (-> to-spr-channel chcr))) - ;; At the tail, wait for the last input block rather than starting another look-ahead transfer. - (dma-wait-counting-stalls! t6 to-spr-channel (-> instance-work wait-to-spr) - tie-instance-finish-instance-upload tie-instance-next-visibility-byte) - ;; Consume eight visibility bits at a time. A zero byte skips eight instance records at once. - (label tie-instance-next-visibility-byte) - (l.b vis-bits ra) - (add.i ra ra 1) - (nop!) - (s.w ra (-> instance-work cur-vis-bits)) - (b.nz vis-bits tie-instance-begin-visible-byte :delay (s.w vis-mask (-> instance-work end-vis-bits))) - (add.i instance-count instance-count -8) - (add.i instance instance 512) - (b tie-instance-finish-visibility-byte :delay (nop!))) - ;; Each set bit selects one 64-byte instance. VU0 entry 42 transforms its bounds through - ;; the background matrices and returns the common-plane reject bits before any packet work. - (asm-block cull-visible-instances - (label tie-instance-begin-visible-byte) - (add.i vis-mask r0 128) - (l.vf vf2 (-> instance bsphere)) - (label tie-instance-next-instance) - (add.i ra packet-qwc -246) - (nop!) - (b.le ra r0 tie-instance-classify-instance :delay (callms 42)) - ;; A transformed instance needs at most ten qwords beyond the current packet tail. Return a - ;; nearly full bank before classifying it so one instance never crosses output banks. - (dma-wait-counting-stalls! packet-cursor from-spr-channel (-> instance-work wait-from-spr) - tie-instance-wait-output-space tie-instance-flush-output) - ;; Submit the occupied qwords and immediately switch to the other 4 KiB output bank. - (dma-return-output-bank! output-bank TIE-INSTANCE-OUTPUT-FLIP packet-cursor packet-qwc memory-cursor from-spr-channel - tie-instance-flush-output)) - ;; Unpack the instance, decide what it is, and shear it into the wind. - ;; - ;; VU0 entry 42 has already turned the instance's bounding sphere into plane distances and a - ;; camera-space position, so the reject test and the LOD distance are both available before any - ;; packet work happens. From the distance the code derives three things at once: whether the - ;; instance is near enough to need the clipping renderer, which of the prototype's four - ;; geometries to use, and the morph weight that blends the chosen geometry toward the next - ;; coarser one. Ordinary TIE uses geometries 1 through 3, the near path uses 0, and Generic - ;; keeps its own four lists. - ;; - ;; VU0 registers through this block: - ;; vf8 camera-space distance to the instance, broadcast (z minus bounding radius) - ;; vf10..vf13 the four instance rows, unpacked and then sheared, sent to VU1 as-is - ;; vf14/vf15 the prototype's dists and rdists, then the LOD fractions derived from them - ;; vf16..vf19 wind temporaries and wind-const - ;; vf28..vf30 the morph quadword for LOD 1, 2 and 3 - ;; - ;; A morph quadword is (1 - f, unused, trunc(256 f), 256 - trunc(256 f)) where f falls from one - ;; to zero across the LOD band. x collapses interpolated vertices onto their neighbours and the - ;; zw pair blends their colors; see the interp-point pipeline in tie.gc for why those two sum to - ;; 256 instead of one. Geometry 3 uses far-morph itself, (1, 0, 0, 256), which is fully - ;; collapsed and fully blended. - (asm-block classify-instance-and-apply-wind - (label tie-instance-classify-instance) - ;; Unpack one instance. Its transform is stored as a matrix4h: sixteen int16 in four - ;; doublewords, which pextlh spreads one per 32-bit lane (each value landing in the upper - ;; half) and an arithmetic shift then brings back down. The three rotation rows shift by 16, - ;; so they arrive as plain sign-extended integers and itof with twelve fraction bits reads - ;; them as 1.12 fixed point. The translation row shifts by only 10, which leaves it - ;; multiplied by 64 -- world units, at 64 per int16 step. Sixty-four bytes of instance - ;; therefore reconstruct a full transform, which is the entire reason TIE can afford - ;; thousands of placements. - ;; - ;; Two things can reject the instance: the visibility bit unpacked from the level's - ;; visibility string, and the prototype's own "do not draw" flag. - (nop!) - (l.w bucket (-> instance bucket-ptr)) - (and gp vis-bits vis-mask) - (l.d s5 (-> instance origin long 3)) ;; translation row - (b.z gp tie-instance-advance-instance :delay (l.d s2 (-> instance origin long 0))) - (sll gp packet-qwc 4) - (l.d s4 (-> instance origin long 1)) - (pextlh s3 s5 r0) - (l.d s5 (-> instance origin long 2)) - (sra.w s3 s3 10) ;; sign extend and keep the implied factor of 64 - (l.q s1 (-> bucket dists)) - (pextlh s2 s2 r0) - (l.q s0 (-> bucket rdists)) - (sra.w s2 s2 16) ;; rotation rows only need the sign extension - (m.ni vf14 s1) - (pextlh s4 s4 r0) - (m.ni vf15 s0) - (sra.w s4 s4 16) - (m.ni vf13 s3) - (pextlh s5 s5 r0) - (m.ni vf10 s2) - (sra.w s3 s5 16) - ;; Two wind lookups, both keyed on the instance's authored wind-index. The gust sample - ;; itself is picked out of the 64-entry ring by (index + wind-time) & 63, so neighbouring - ;; objects do not all sway in phase and the whole field drifts as time advances. The - ;; instance's own spring state lives in the level's wind-vectors array at its index. - (l.hu s2 (-> instance wind-index)) - (add gp gp memory-cursor) ;; main-memory address this packet will land at - (m.ni vf11 s4) - (sll s5 s2 4) - (m.ni vf12 s3) - (add s4 s2 wind-time) - (l.w s2 (-> instance-work wind-work)) - (and.i s4 s4 63) - (l.w s3 (-> instance-work wind-vectors)) - (sll s1 s4 4) - (l.w s4 (-> bucket flags)) - (add s5 s3 s5) ;; s5 = &wind-vectors[wind-index] - (add s3 s1 s2) ;; s3 = &wind-work.wind-array[(wind-index + wind-time) & 63] - (and.i s1 s4 1) ;; prototype flag 0: never draw this prototype - (and.i s4 s4 2) ;; prototype flag 1: route this prototype through Generic - (b.nz s1 tie-instance-advance-instance :delay (m.ni s1 vi1)) - (itof.vf vf13 vf13) - ;; Read the wind clock for the next instance. On the first instance through this loop the - ;; register still holds the wind-work pointer left over from setup, so instance zero hashes - ;; against that instead -- it lands in the same 64-entry ring either way, so it only picks a - ;; different gust sample. - (l.w wind-time s2 (-> wind-work wind-time)) - (b.nz s1 tie-instance-advance-instance :delay (l.vf vf25 (-> instance-work min-dist))) - ;; Transform the prototype bounds into camera space. The guard-plane minimum supplies the - ;; near classification, while the two distance vectors select morph state and ordinary LOD. - (nop!) - (l.vf vf16 (-> instance-work hmge-d)) - (nop!) - (l.vf vf17 (-> instance-work hvdf-offset)) - (mula.z.vf vf1 vf6) - (s.w gp (-> instance-work upload-color-0 dma addr)) - (msub.w.vf vf8 vf1 vf2) - (s.w gp (-> instance-work generic-color-0 dma addr)) - (add.vf.xyz vf5 vf0 vf0) - (nop!) - (add.vf.xyz vf13 vf13 vf2) - (nop!) - (mula.vf vf1 vf1) - (nop!) - (sub.vf vf14 vf8 vf14) - (nop!) - (add.w.vf.w vf5 vf5 vf17) - (nop! :count 2) - (l.vf vf30 (-> instance-work far-morph)) - (min.vf vf25 vf8 vf25) - (nop!) - (msub.vf.xyz vf15 vf14 vf15) - (nop!) - (min.y.vf.w vf5 vf5 vf16) - (nop! :count 2) - (l.vf vf24 (-> instance-work guard-plane 0)) - (nop!) - (s.vf vf25 (-> instance-work min-dist)) - (min.vf.xyz vf15 vf15 vf1) - (nop!) - (max.x.vf.w vf5 vf5 vf16) - (nop!) - (sub.z.vf vf16 vf8 vf16) - (nop! :count 2) - (l.vf vf25 (-> instance-work guard-plane 1)) - (nop!) - (l.vf vf26 (-> instance-work guard-plane 2)) - (nop!) - (l.vf vf27 (-> instance-work guard-plane 3)) - (mula.x.vf vf24 vf2) - (nop!) - (madda.y.vf vf25 vf2) - (nop!) - (madda.z.vf vf26 vf2) - (nop!) - (msuba.w.vf vf27 vf0) - (nop!) - (msub.w.vf vf24 vf1 vf2) - (nop! :count 2) - (m s2 vf16) - (mul.w.vf vf28 vf15 vf30) - (nop!) - (mul.w.vf vf29 vf15 vf30) - (nop! :count 2) - (l.vf vf19 (-> instance-work wind-const)) - (itof.vf vf10 vf10 :fixed 12) - (nop!) - (pcgt.w s1 r0 s2) - (m s0 vf24) - (mul.x.vf vf28 vf1 vf28) - (nop!) - (mul.z.vf vf29 vf1 vf29) - (l.w s2 (-> bucket stiffness)) - (pcgt.w s0 r0 s0) - (s.vf vf5 (-> packet-cursor fog)) - (ppach s0 r0 s0) - (s.w s4 (-> packet-cursor fog)) - (or s1 s0 s1) - (s.vf vf14 (-> instance-work dist-test)) - (ppacb s1 r0 s1) - (mmi-nop!) - ;; fog.y takes the reject flags: bit set means this instance needs the near renderer. - (b.z s2 tie-instance-no-wind :delay (s.w s1 packet-cursor 84)) - ;; The wind spring. A prototype with zero stiffness skipped straight past this, so anything - ;; that reaches here is meant to sway. wind-vectors[wind-index] is this instance's persistent - ;; state -- two packed vector2w, a position and a velocity -- and wind-array[hash] is the gust - ;; sample driving it. wind-const supplies the spring coefficients: 0.5 damping, a force scale - ;; of 100, a timestep of 1/60, and -1.0 as the lower clamp against vf0.w's +1.0. - ;; - ;; The state is integer, so it is widened to floats with pextlw, integrated, clamped, and - ;; packed back down with ppacw before being stored. The result is scaled by the prototype's - ;; stiffness and then applied to the instance rows as a shear: each row's xz picks up a - ;; multiple of its own y, so geometry at the base of the object does not move and the top - ;; leans. That is the whole wind effect -- no per-vertex work, no extra VU pass, just three - ;; multiply-adds on the transform before it is sent. - (ftoi.vf.zw vf28 vf28) - (l.d s1 s5 8) - (ftoi.vf.zw vf29 vf29) - (l.d s2 s5) - (pextlw s1 r0 s1) - (l.vf vf16 s3 12) - (pextlw s3 r0 s2) - (m vf18 s1) - (nop!) - (m vf17 s3) - (mula.vf vf16 vf1) - (nop!) - (msuba.x.vf vf18 vf19) - (nop!) - (msub.y.vf vf16 vf17 vf19) - (nop!) - (sub.x.vf.x vf28 vf30 vf15) - (nop!) - (sub.z.vf.x vf29 vf1 vf15) - (nop!) - (itof.vf.zw vf28 vf28) - (nop!) - (mula.z.vf vf16 vf19) - (nop!) - (madd.vf vf18 vf1 vf18) - (nop!) - (itof.vf.zw vf29 vf29) - (nop!) - (add.y.vf.y vf28 vf0 vf0) - (nop!) - (add.y.vf.y vf29 vf0 vf0) - (nop!) - (mula.z.vf vf18 vf19) - (nop!) - (madd.vf vf17 vf17 vf1) - (nop!) - (itof.vf vf11 vf11 :fixed 12) - (nop!) - (itof.vf vf12 vf12 :fixed 12) - (nop!) - (sub.w.vf.w vf28 vf30 vf28) - (nop!) - (min.w.vf vf17 vf17 vf0) - (nop!) - (sub.w.vf.w vf29 vf30 vf29) - (nop! :count 4) - (m s3 vf18) - (max.w.vf vf27 vf17 vf19) - (nop!) - (ppacw s3 r0 s3) - (mmi-nop!) - (nop! :count 4) - (mul.w.vf vf27 vf27 vf15) - (nop! :count 5) - (mula.x.vf.yw vf0 vf0) - (nop!) - (mula.y.vf.xz vf27 vf10) - (nop!) - (madd.vf vf10 vf1 vf10) - (nop! :count 2) - (m s2 vf27) - (mula.x.vf.yw vf0 vf0) - (l.w s1 (-> instance-work paused)) - (mula.y.vf.xz vf27 vf11) - (nop!) - (madd.vf vf11 vf1 vf11) - (nop!) - (b.ne s1 s7 tie-instance-skip-wind-writeback :delay (ppacw s2 r0 s2)) - (mula.x.vf.yw vf0 vf0) - (s.d s3 s5 8) - (mula.y.vf.xz vf27 vf12) - (s.d s2 s5) - (b.nz s4 tie-instance-build-generic-records :delay (madd.vf vf12 vf1 vf12)) - (b tie-instance-build-ordinary-records :delay (nop!)) - ;; With the game paused the shear is still applied, but the spring state is not written back, - ;; so everything holds its current lean instead of continuing to sway behind the menu. - (label tie-instance-skip-wind-writeback) - (mula.x.vf.yw vf0 vf0) - (nop!) - (mula.y.vf.xz vf27 vf12) - (nop!) - (b.nz s4 tie-instance-build-generic-records :delay (madd.vf vf12 vf1 vf12)) - (b tie-instance-build-ordinary-records :delay (nop!)) - ;; Stiffness zero: no wind. Finish the LOD distance and morph values and convert the two - ;; remaining rotation rows, leaving the transform unsheared. - (label tie-instance-no-wind) - (ftoi.vf.zw vf28 vf28) - (nop!) - (ftoi.vf.zw vf29 vf29) - (nop!) - (sub.x.vf.x vf28 vf30 vf15) - (nop!) - (sub.z.vf.x vf29 vf1 vf15) - (nop!) - (itof.vf.zw vf28 vf28) - (nop!) - (itof.vf.zw vf29 vf29) - (nop!) - (add.y.vf.y vf28 vf0 vf0) - (nop!) - (add.y.vf.y vf29 vf0 vf0) - (nop!) - (sub.w.vf.w vf28 vf30 vf28) - (nop!) - (sub.w.vf.w vf29 vf30 vf29) - (nop!) - (itof.vf vf11 vf11 :fixed 12) - (nop!) - (b.nz s4 tie-instance-build-generic-records :delay (itof.vf vf12 vf12 :fixed 12))) - ;; Build ordinary or near instance records. The three projected-distance thresholds choose - ;; among ordinary variants 1..3; a near classification uses variant 0 and its near matrix. - (asm-block build-ordinary-instance-records - (label tie-instance-build-ordinary-records) - (nop!) - (l.w s5 packet-cursor 84) ;; fog.y: the near-renderer reject flags stored above - (nop!) - (l.w s4 (-> instance-work dist-test w)) - (add.i packet-qwc packet-qwc 6) - (l.w s3 (-> instance-work dist-test z)) - (b.nz s5 tie-instance-emit-near-record :delay (sub.w.vf.w vf10 vf10 vf10)) - (b.gt s4 r0 tie-instance-select-ordinary-lod-three :delay (nop!)) - (b.gt s3 r0 tie-instance-select-ordinary-lod-two :delay (nop!)) - (nop!) - (l.h s4 (-> bucket count 1)) - (nop!) - (l.w s5 (-> bucket next 1)) - (add.i s4 s4 1) - (s.vf vf28 (-> packet-cursor morph)) - (mula.x.vf vf20 vf10) - (add.i gp gp 96) ;; one instance record, tie-matrix sized - (madda.y.vf vf21 vf10) - (s.w gp (-> bucket next 1)) - (madd.z.vf vf10 vf22 vf10) - (s.h s4 (-> bucket count 1)) - (mula.x.vf vf20 vf11) - (l.bu s4 (-> bucket frag-count 1)) - (madda.y.vf vf21 vf11) - (l.hu gp (-> bucket base-qw 1)) - (madd.z.vf vf11 vf22 vf11) - (l.bu s3 (-> bucket index-start 1)) - (b tie-instance-emit-ordinary-record :delay (nop!)) - ;; Geometry variant two uses the middle distance result and its own list head and fragment data. - (label tie-instance-select-ordinary-lod-two) - (nop!) - (l.h s4 (-> bucket count 2)) - (nop!) - (l.w s5 (-> bucket next 2)) - (add.i s4 s4 1) - (s.vf vf29 (-> packet-cursor morph)) - (mula.x.vf vf20 vf10) - (add.i gp gp 96) ;; one instance record, tie-matrix sized - (madda.y.vf vf21 vf10) - (s.w gp (-> bucket next 2)) - (madd.z.vf vf10 vf22 vf10) - (s.h s4 (-> bucket count 2)) - (mula.x.vf vf20 vf11) - (l.bu s4 (-> bucket frag-count 2)) - (madda.y.vf vf21 vf11) - (l.hu gp (-> bucket base-qw 2)) - (madd.z.vf vf11 vf22 vf11) - (l.bu s3 (-> bucket index-start 2)) - (b tie-instance-emit-ordinary-record :delay (nop!)) - ;; Geometry variant three is the farthest ordinary representation. - (label tie-instance-select-ordinary-lod-three) - (nop!) - (l.h s4 (-> bucket count 3)) - (nop!) - (l.w s5 (-> bucket next 3)) - (add.i s4 s4 1) - (s.vf vf30 (-> packet-cursor morph)) - (mula.x.vf vf20 vf10) - (add.i gp gp 96) ;; one instance record, tie-matrix sized - (madda.y.vf vf21 vf10) - (s.w gp (-> bucket next 3)) - (madd.z.vf vf10 vf22 vf10) - (s.h s4 (-> bucket count 3)) - (mula.x.vf vf20 vf11) - (l.bu s4 (-> bucket frag-count 3)) - (madda.y.vf vf21 vf11) - (l.hu gp (-> bucket base-qw 3)) - (madd.z.vf vf11 vf22 vf11) - (l.bu s3 (-> bucket index-start 3)) - ;; Transform the four instance rows by the ordinary camera matrix and append the six-qword - ;; instance record expected by the ordinary TIE VU1 input. - (label tie-instance-emit-ordinary-record) - (mula.x.vf vf20 vf12) - (l.q s2 (-> instance-work upload-color-2)) - (madda.y.vf vf21 vf12) - (l.q s1 (-> instance-work upload-color-ret)) - (madd.z.vf vf12 vf22 vf12) - (sll gp gp 4) - (mula.x.vf vf20 vf13) - (add s3 s3 ra) - (madda.y.vf vf21 vf13) - (nop!) - (madda.z.vf vf22 vf13) - (nop!) - (madd.w.vf vf13 vf23 vf0) - (nop!) - (s.vf vf10 (-> packet-cursor mat vector 0)) - (nop!) - (s.vf vf11 (-> packet-cursor mat vector 1)) - (mov.z s2 s1 s5) - (s.vf vf12 (-> packet-cursor mat vector 2)) - (add.i packet-cursor packet-cursor 96) ;; one instance record - (b tie-instance-append-ordinary-command :delay (s.vf vf13 packet-cursor -48)) - ;; Variant zero uses the near projection matrix so geometry which crosses the camera plane can - ;; be clipped before perspective division. - (label tie-instance-emit-near-record) - (nop!) - (l.vf vf24 (-> instance-work tie-near-perspective-matrix 0)) - (nop!) - (l.vf vf25 (-> instance-work tie-near-perspective-matrix 1)) - (nop!) - (l.vf vf26 (-> instance-work tie-near-perspective-matrix 2)) - (nop!) - (l.vf vf27 (-> instance-work tie-near-perspective-matrix 3)) - (nop!) - (l.h s4 (-> bucket count 0)) - (nop!) - (l.w s5 (-> bucket next 0)) - (add.i s4 s4 1) - (s.vf vf28 (-> packet-cursor morph)) - (mula.x.vf vf24 vf10) - (add.i gp gp 96) ;; one instance record, tie-matrix sized - (madda.y.vf vf25 vf10) - (s.w gp (-> bucket next 0)) - (madd.z.vf vf10 vf26 vf10) - (s.h s4 (-> bucket count 0)) - (mula.x.vf vf24 vf11) - (l.bu s4 (-> bucket frag-count 0)) - (madda.y.vf vf25 vf11) - (l.hu gp (-> bucket base-qw 0)) - (madd.z.vf vf11 vf26 vf11) - (l.bu s3 (-> bucket index-start 0)) - (mula.x.vf vf24 vf12) - (l.q s2 (-> instance-work upload-color-2)) - (madda.y.vf vf25 vf12) - (l.q s1 (-> instance-work upload-color-ret)) - (madd.z.vf vf12 vf26 vf12) - (sll gp gp 4) - (mula.x.vf vf24 vf13) - (add s3 s3 ra) - (madda.y.vf vf25 vf13) - (nop!) - (madda.z.vf vf26 vf13) - (nop!) - (madd.w.vf vf13 vf27 vf0) - (nop!) - (s.vf vf10 (-> packet-cursor mat vector 0)) - (nop!) - (s.vf vf11 (-> packet-cursor mat vector 1)) - (nop!) - (s.vf vf12 (-> packet-cursor mat vector 2)) - (mov.z s2 s1 s5) - (s.vf vf13 (-> packet-cursor mat vector 3)) - (add.i packet-cursor packet-cursor 96) ;; one instance record - ;; Append the three-qword command template which links this instance record to the selected - ;; prototype/LOD list. Continue emitting commands while fragments remain in that geometry. - (label tie-instance-append-ordinary-command) - (nop!) - (l.w ra instance (offset-of instance-tie color-indices)) - (nop!) - (s.q s2 (-> instance-work upload-color-temp)) - (nop!) - (l.bu s2 s3 144) - (add s1 gp ra) - (s.w s5 (-> instance-work upload-color-temp dma addr)) - (add.i packet-qwc packet-qwc 3) - (s.w s1 (-> instance-work upload-color-1 dma addr)) - (sll s1 s2 2) - (s.h s2 (-> instance-work upload-color-1)) - (sll s2 s2 4) - (s.b s1 (-> instance-work upload-color-1 vif1 num)) - (add gp gp s2) - (l.q s2 (-> instance-work upload-color-0)) - (add.i s5 s5 48) - (l.q s1 (-> instance-work upload-color-1)) - (add.i packet-cursor packet-cursor 48) - (l.q s0 (-> instance-work upload-color-temp)) - (add.i s3 s3 1) - (s.q s2 packet-cursor -48) - (add.i s4 s4 -1) - (s.q s1 packet-cursor -32) - (b.le s4 r0 tie-instance-advance-instance :delay (s.q s0 packet-cursor -16)) - ;; Every additional fragment reuses the transformed rows and appends only another list command. - ;; Leave four qwords free so the command itself cannot straddle an output-bank transfer. - (label tie-instance-append-more-ordinary-commands) - (add.i s2 packet-qwc -252) - (nop!) - (b.le s2 r0 tie-instance-append-ordinary-after-flush :delay (nop!)) - (dma-wait-counting-stalls! packet-cursor from-spr-channel (-> instance-work wait-from-spr) - tie-instance-wait-ordinary-output tie-instance-flush-ordinary-output) - ;; Return the full bank, then resume the same prototype list in the alternate bank. - (dma-return-output-bank! output-bank TIE-INSTANCE-OUTPUT-FLIP packet-cursor packet-qwc memory-cursor from-spr-channel - tie-instance-flush-ordinary-output) - (label tie-instance-append-ordinary-after-flush) - (nop!) - (l.bu s2 s3 144) - (add s1 gp ra) - (s.w s5 (-> instance-work upload-color-temp dma addr)) - (add.i packet-qwc packet-qwc 3) - (s.w s1 (-> instance-work upload-color-1 dma addr)) - (sll s1 s2 2) - (s.h s2 (-> instance-work upload-color-1)) - (sll s2 s2 4) - (s.b s1 (-> instance-work upload-color-1 vif1 num)) - (add gp gp s2) - (l.q s2 (-> instance-work upload-color-0)) - (add.i s5 s5 48) - (l.q s1 (-> instance-work upload-color-1)) - (add.i packet-cursor packet-cursor 48) - (l.q s0 (-> instance-work upload-color-temp)) - (add.i s3 s3 1) - (s.q s2 packet-cursor -48) - (add.i s4 s4 -1) - (s.q s1 packet-cursor -32) - (b.gt s4 r0 tie-instance-append-more-ordinary-commands :delay (s.q s0 packet-cursor -16)) - (b tie-instance-advance-instance :delay (nop!))) - ;; Generic uses the same transformed instance but keeps its own per-geometry chains. - ;; - ;; First it works out how strongly this instance should show its environment map. vf6 is the - ;; camera-rotation-transformed position, so squaring and summing its lanes and taking the square root - ;; gives the distance; envmap-fade-far plus the bounding radius minus that distance, scaled by - ;; envmap-rfade, is the fade fraction, clamped to the 0..128 range in constant.y and truncated to - ;; eight bits. A result of zero means the instance is past its reflection range, and since the - ;; environment map was the only reason to route it through GENERIC at all, it drops straight back to - ;; the ordinary renderer. - ;; - ;; The fade goes out in the instance record's fifth quadword, where generic-tie reads it back as the - ;; scale on the environment tint. instance-tie-work's refl-fade-fac and refl-fade-end are the - ;; global ends of the same curve. - (asm-block build-generic-instance-records - (label tie-instance-build-generic-records) - (mul.vf.xyz vf16 vf6 vf6) - (nop! :count 2) - (l.vf vf9 (-> bucket envmap-rfade)) - (sub.w.vf.w vf10 vf10 vf10) - (nop! :count 3) - (adda.y.vf.x vf16 vf16) - (nop!) - (madd.z.vf.x vf16 vf1 vf16) - (nop!) - (sqrt.x Q vf16) - (nop!) - (mula.y.vf vf1 vf9) - (nop!) - (madda.w.vf vf1 vf2) - (nop! :count 2) - (waitq) - (msubq.vf vf16 vf1 Q) - (nop!) - (mul.x.vf vf16 vf16 vf9) - (nop!) - (max.x.vf.x vf16 vf16 vf0) - (nop!) - (min.y.vf.x vf16 vf16 vf3) - (nop!) - (ftoi.vf vf16 vf16) - (nop! :count 2) - (m s5 vf16) - (nop! :count 2) - (and.i s5 s5 255) - (nop!) - (b.z s5 tie-instance-build-ordinary-records :delay (nop!)) - (callms 29) - (s.w s4 (-> instance-work flags)) - (nop!) - (l.w s4 (-> instance-work dist-test w)) - (add.i packet-qwc packet-qwc 6) - (l.w s3 (-> instance-work dist-test z)) - (nop!) - (s.w s5 (-> packet-cursor fog)) - (b.gt s4 r0 tie-instance-select-generic-lod-three :delay (nop!)) - (b.gt s3 r0 tie-instance-select-generic-lod-two :delay (nop!)) - (nop!) - (l.h s4 (-> bucket generic-count 1)) - (nop!) - (l.w s5 (-> bucket generic-next 1)) - (add.i s4 s4 1) - (s.vf vf28 (-> packet-cursor morph)) - (mula.x.vf vf24 vf10) - (add.i gp gp 96) ;; one instance record, tie-matrix sized - (madda.y.vf vf25 vf10) - (s.w gp (-> bucket generic-next 1)) - (madd.z.vf vf10 vf26 vf10) - (s.h s4 (-> bucket generic-count 1)) - (mula.x.vf vf24 vf11) - (l.bu s3 (-> bucket frag-count 1)) - (madda.y.vf vf25 vf11) - (l.hu gp (-> bucket base-qw 1)) - (madd.z.vf vf11 vf26 vf11) - (l.bu s4 (-> bucket index-start 1)) - (b tie-instance-emit-generic-record :delay (nop!)) - ;; Select Generic geometry two when the middle distance threshold was crossed. - (label tie-instance-select-generic-lod-two) - (nop!) - (l.h s4 (-> bucket generic-count 2)) - (nop!) - (l.w s5 (-> bucket generic-next 2)) - (add.i s4 s4 1) - (s.vf vf29 (-> packet-cursor morph)) - (mula.x.vf vf24 vf10) - (add.i gp gp 96) ;; one instance record, tie-matrix sized - (madda.y.vf vf25 vf10) - (s.w gp (-> bucket generic-next 2)) - (madd.z.vf vf10 vf26 vf10) - (s.h s4 (-> bucket generic-count 2)) - (mula.x.vf vf24 vf11) - (l.bu s3 (-> bucket frag-count 2)) - (madda.y.vf vf25 vf11) - (l.hu gp (-> bucket base-qw 2)) - (madd.z.vf vf11 vf26 vf11) - (l.bu s4 (-> bucket index-start 2)) - (b tie-instance-emit-generic-record :delay (nop!)) - ;; Select the farthest Generic representation. - (label tie-instance-select-generic-lod-three) - (nop!) - (l.h s4 (-> bucket generic-count 3)) - (nop!) - (l.w s5 (-> bucket generic-next 3)) - (add.i s4 s4 1) - (s.vf vf30 (-> packet-cursor morph)) - (mula.x.vf vf24 vf10) - (add.i gp gp 96) ;; one instance record, tie-matrix sized - (madda.y.vf vf25 vf10) - (s.w gp (-> bucket generic-next 3)) - (madd.z.vf vf10 vf26 vf10) - (s.h s4 (-> bucket generic-count 3)) - (mula.x.vf vf24 vf11) - (l.bu s3 (-> bucket frag-count 3)) - (madda.y.vf vf25 vf11) - (l.hu gp (-> bucket base-qw 3)) - (madd.z.vf vf11 vf26 vf11) - (l.bu s4 (-> bucket index-start 3)) - ;; Transform the four rows with the Generic matrix, store the environment-map fade, and append - ;; this instance to the selected Generic prototype chain. - (label tie-instance-emit-generic-record) - (mula.x.vf vf24 vf12) - (sll gp gp 4) - (madda.y.vf vf25 vf12) - (add s4 s4 ra) - (madd.z.vf vf12 vf26 vf12) - (nop!) - (mula.x.vf vf24 vf13) - (nop!) - (madda.y.vf vf25 vf13) - (nop!) - (madda.z.vf vf26 vf13) - (nop!) - (madd.w.vf vf13 vf27 vf0) - (nop!) - (s.vf vf10 (-> packet-cursor mat vector 0)) - (nop!) - (s.vf vf11 (-> packet-cursor mat vector 1)) - (nop!) - (s.vf vf12 (-> packet-cursor mat vector 2)) - (nop!) - (s.vf vf13 (-> packet-cursor mat vector 3)) - (add.i packet-cursor packet-cursor 96) ;; one instance record - (nop!) - (l.w ra instance (offset-of instance-tie color-indices)) - (nop!) - (l.bu s2 s4 144) - (add s1 gp ra) - (s.w s5 (-> instance-work generic-color-0 vif1)) - (add.i packet-qwc packet-qwc 3) - (s.w s1 (-> instance-work generic-color-1 dma addr)) - (sll s1 s2 4) - (s.h s2 (-> instance-work generic-color-1)) - (add gp gp s1) - (l.q s2 (-> instance-work generic-color-0)) - (add.i s5 s5 48) - (l.q s1 (-> instance-work generic-color-1)) - (add.i packet-cursor packet-cursor 48) - (l.q s0 (-> instance-work generic-color-end)) - (add.i s4 s4 1) - (s.q s2 packet-cursor -48) - (add.i s3 s3 -1) - (s.q s1 packet-cursor -32) - (b.le s3 r0 tie-instance-advance-instance :delay (s.q s0 packet-cursor -16)) - ;; Additional Generic fragments share the transformed rows but each needs its own list command. - (label tie-instance-append-more-generic-commands) - (add.i s2 packet-qwc -252) - (nop!) - (b.le s2 r0 tie-instance-append-generic-after-flush :delay (nop!)) - (dma-wait-counting-stalls! packet-cursor from-spr-channel (-> instance-work wait-from-spr) - tie-instance-wait-generic-output tie-instance-flush-generic-output) - ;; Return the full bank before continuing the Generic list in the alternate bank. - (dma-return-output-bank! output-bank TIE-INSTANCE-OUTPUT-FLIP packet-cursor packet-qwc memory-cursor from-spr-channel - tie-instance-flush-generic-output) - (label tie-instance-append-generic-after-flush) - (nop!) - (l.bu s2 s4 144) - (add s1 gp ra) - (s.w s5 (-> instance-work generic-color-0 vif1)) - (add.i packet-qwc packet-qwc 3) - (s.w s1 (-> instance-work generic-color-1 dma addr)) - (sll s1 s2 4) - (s.h s2 (-> instance-work generic-color-1)) - (add gp gp s1) - (l.q s2 (-> instance-work generic-color-0)) - (add.i s5 s5 48) - (l.q s1 (-> instance-work generic-color-1)) - (add.i packet-cursor packet-cursor 48) - (l.q s0 (-> instance-work generic-color-end)) - (add.i s4 s4 1) - (s.q s2 packet-cursor -48) - (add.i s3 s3 -1) - (s.q s1 packet-cursor -32) - (b.gt s3 r0 tie-instance-append-more-generic-commands :delay (s.q s0 packet-cursor -16))) - ;; Shift to the next visibility bit and its corresponding 64-byte instance. - (asm-block advance-instance-stream - (label tie-instance-advance-instance) - (add.i instance-count instance-count -1) - (srl vis-mask vis-mask 1) - (add.i instance instance 64) - (nop!) - (b.nz vis-mask tie-instance-next-instance :delay (l.vf vf2 (-> instance bsphere))) - ;; Restore the saved visibility cursors after the byte, then continue within this scratchpad - ;; block or move to the next occupied 32-instance block. - (label tie-instance-finish-visibility-byte) - (nop!) - (l.w ra (-> instance-work cur-vis-bits)) - (nop!) - (l.w vis-mask (-> instance-work end-vis-bits)) - (b.ne ra vis-mask tie-instance-next-visibility-byte :delay (nop!)) - (b.gt instance-count r0 tie-instance-begin-instance-block :delay (nop!)) - (b.z packet-qwc tie-instance-wait-final-output :delay (nop!))) - ;; Return the last partial packet block, wait for it to leave scratchpad, and publish the - ;; advanced main-memory cursor back to the caller's dma-buffer. - ;; Wait until the previous bank is free before submitting the final partial bank. - (asm-block finish-instance-output - (dma-wait-counting-stalls! a0 from-spr-channel (-> instance-work wait-from-spr) - tie-instance-wait-final-output-space tie-instance-flush-final-output) - ;; Unlike a full-bank flush, the last transfer uses exactly the occupied qword count. - (label tie-instance-flush-final-output) - (s.w a1 (-> from-spr-channel sadr)) - (xor.i a0 a1 TIE-INSTANCE-OUTPUT-FLIP) - (s.w memory-cursor (-> from-spr-channel madr)) - (sll a1 packet-qwc 4) - (add memory-cursor memory-cursor a1) - (m a0 a0) - (s.w packet-qwc (-> from-spr-channel qwc)) - (add.i a0 r0 DMA-CHCR-STR) - (s.w a0 (-> from-spr-channel chcr)) - (add.i a0 r0 0) - ;; The caller cannot reuse the scratchpad output until the final from-scratchpad DMA completes. - (dma-wait-counting-stalls! a0 from-spr-channel (-> instance-work wait-from-spr) - tie-instance-wait-final-output tie-instance-finish) - (label tie-instance-finish) - (l.w a0 (-> instance-work dma-buffer)) - (nop!) - (s.w memory-cursor (-> dma-buf base)) - (nop!) - (m v0 r0) - (l.d ra sp) - (l.q gp sp 112) - (l.q s5 sp 96) - (l.q s4 sp 80) - (l.q s3 sp 64) - (l.q s2 sp 48) - (l.q s1 sp 32) - (l.q s0 sp 16) - (jr ra :delay (add.i sp sp 128)) - (nop! :count 2) - (nop!))))) - -(#when PC_PORT - (def-mips2c draw-inline-array-prototype-tie-generic-asm (function dma-buffer int prototype-array-tie none))) - -;; The EE TIE renderer is a three-stage pipeline. The instance pass culls placements, chooses the -;; near, ordinary, or Generic LOD, and links compact instance records into the matching prototype -;; bucket. The prototype passes then expand that prototype's time-of-day palette while alternating -;; two scratchpad palette banks, upload every populated geometry fragment, and alternate two output -;; banks while the finished DMA stream returns to main memory. VU1 consumes one prototype fragment -;; at a time, alternates two instance-input banks so VIF can fill one while VU1 reads the other, -;; and cycles three GIF-output banks so it can build the next fragment without overwriting data -;; still waiting for the GIF. Prototype geometry is therefore uploaded once and reused by all -;; instance records which selected it. - -(#unless PC_PORT - (defun draw-inline-array-prototype-tie-generic-asm ((dma-buf dma-buffer) (prototype-count int) (prototypes prototype-array-tie)) - "Expand the selected TIE palettes and build Generic fragment chains for every populated - prototype geometry variant." - (declare (asm-func none) (allow-saved-regs)) - ;; The instance pass has already linked records into each prototype's four Generic lists. This - ;; pass turns those lists and the prototype fragments into the compact stream consumed later by - ;; generic-tie, while overlapping main-memory transfers with palette and packet construction. - ;; Keep palette uploads in the two 1 KiB scratchpad color banks and packet output in the - ;; two 4 KiB output banks. Main-memory writes are issued in blocks of at most 256 qwords. - ;; - ;; The prototype scratchpad holds two 1 KiB palette banks, two 4 KiB output banks, and the - ;; prototype-tie-dma state area, all reached with spr-offset. prototype-tie-work fields and the DMA - ;; channel registers are read as fields off their typed base registers. - (rlet ((prototype-work :reg t0 :type prototype-tie-work) - (from-spr-channel :reg t1 :type dma-bank-spr) - (output-bank :reg t2) - (palette-bank :reg t3) - (to-spr-channel :reg t4 :type dma-bank-spr) - (dma-buf :reg a0 :type dma-buffer) ;; the argument, before a0 becomes the packet qword count - (scratch-base :reg a3 :type terrain-context) - (mood :reg t8 :type mood-context) - (fragment :reg t6 :type tie-fragment) - (memory-cursor :reg v1)) - ;; The registers not aliased above genuinely change meaning every few instructions and keep their - ;; physical names: a0 is the caller's dma-buffer, then the count of quadwords occupied in the - ;; current output bank, then a DMA status word; a1 is the write cursor inside that bank, borrowed - ;; for channel status and byte counts; t5 through t9 pass around the bucket, its palette, its - ;; geometry pointers, the four time-of-day weight vectors, and finally the fragment cursor. - ;; Each block says what they are holding while it runs. - (asm-block setup-generic-prototype-stream - (label tie-generic-prototype-entry) - (add.i sp sp -112) - (s.d ra sp) - (s.q s1 sp 16) - (s.q s2 sp 32) - (s.q s3 sp 48) - (s.q s4 sp 64) - (s.q s5 sp 80) - (s.q gp sp 96) - (nop!) - (lui scratch-base #x7000) - (l.w memory-cursor (-> dma-buf base)) - (lui from-spr-channel #x1000) - (lui output-bank #x1000) - (sync.l) - (cache dxwbin memory-cursor 0) - (sync.l) - (cache dxwbin memory-cursor 1) - (sync.l) - (m! prototype-work *prototype-tie-work*) - (ori from-spr-channel from-spr-channel #xd000) - (ori to-spr-channel output-bank #xd400) - (ori palette-bank scratch-base 16) - (ori output-bank scratch-base 2064) - (s.w a0 scratch-base (spr-offset prototype-tie-dma dma-buffer)) - (add.i t7 a1 -1) - (nop!) - (l.w t6 a2 (offset-of prototype-array-tie data)) - (add.i a0 r0 0) - (m a1 output-bank)) - ;; One prototype per outer iteration. Its four Generic list heads say which of its geometries got - ;; instances; if all four are empty the prototype is skipped without reading anything else. Otherwise - ;; the heads, geometry pointers, fragment counts, and palette are copied into the scratchpad state - ;; area so the fragment loop can work from scratchpad rather than chasing main memory. - (asm-block prepare-generic-prototype - (label tie-generic-prototype-next-prototype) - (nop!) - (l.q t5 t6 (offset-of prototype-bucket-tie generic-next)) - (add.i t8 a2 4) - (s.w t7 scratch-base (spr-offset prototype-tie-dma length)) - (srl32 a2 t5 0) - (s.w t8 (-> prototype-work prototype-array)) - (pcpyud t7 t5 t5) - (l.w t9 t6 (offset-of prototype-bucket-tie tie-colors)) - (or t7 a2 t7) - (l.w a2 t6 (offset-of prototype-bucket-tie frag-count)) - (b.z t7 tie-generic-prototype-advance-prototype :delay (l.w mood (-> scratch-base bsp mood))) - (nop!) - (l.q t7 t6 (offset-of prototype-bucket-tie geometry)) - (nop!) - (s.q t5 scratch-base (spr-offset prototype-tie-dma next 0)) - (nop!) - (s.w a2 scratch-base (spr-offset prototype-tie-dma frag-count 0)) - (nop!) - (s.q t7 scratch-base (spr-offset prototype-tie-dma geometry 0)) - (nop!) - (l.d a2 (-> prototype-work clamp)) - (nop!) - (l.w t7 t9 4) - (add.i ra t9 12) - (l.q t5 (-> mood itimes 0)) - (sra t9 t7 2) - (nop!) - (add t9 t9 a0) - (add.i gp r0 221) - (sub t9 gp t9) - (nop!) - (b.ge t9 r0 tie-generic-prototype-upload-color-palette :delay (nop!))) - ;; A palette is up to 128 entries, which is 32 output quadwords plus its packet header, so it can - ;; easily be the thing that overflows the bank. Check first, return the bank if so, and only then - ;; start blending. - (asm-block upload-and-expand-generic-palette - (dma-wait-counting-stalls! a1 from-spr-channel (-> prototype-work generic-wait-from-spr) - tie-generic-prototype-wait-packet-output tie-generic-prototype-flush-packet-output) - ;; Return the occupied bank and continue palette construction in the other output bank. - (dma-return-output-bank! output-bank TIE-PROTOTYPE-OUTPUT-FLIP a1 a0 memory-cursor from-spr-channel - tie-generic-prototype-flush-packet-output) - ;; Round the entry count up to a whole 1 KiB chunk -- 32 entries of 32 bytes each -- and reserve - ;; the packet header plus the eight output quadwords the chunk will produce. - (label tie-generic-prototype-upload-color-palette) - (add.i t7 t7 31) - (l.w t6 t6 132) - (sra t7 t7 5) - (add.i a0 a0 1) - (sll t7 t7 3) - (s.w t6 (-> prototype-work generic-palette vif0)) - (add a0 a0 t7) - (s.h t7 (-> prototype-work generic-palette)) - (sll t9 t7 2) - (l.q t6 (-> mood itimes 1)) - (nop!) - (l.q gp (-> prototype-work generic-palette)) - (nop!) - (l.q t7 (-> mood itimes 2)) - (nop!) - (s.q gp a1) - (add.i a1 a1 16) - (l.q t8 (-> mood itimes 3)) - ;; The source colors come from main memory, so they are staged through the two 1 KiB color - ;; banks: start the next kilobyte's transfer, blend the kilobyte that just landed, repeat. - (dma-wait-counting-stalls! gp to-spr-channel (-> prototype-work generic-wait-to-spr) - tie-generic-prototype-wait-color-upload tie-generic-prototype-start-color-upload) - ;; First kilobyte in flight. - (label tie-generic-prototype-start-color-upload) - (s.w ra (-> to-spr-channel madr)) - (add.i t9 t9 -32) - (s.w palette-bank (-> to-spr-channel sadr)) - (add.i gp r0 64) - (s.w gp (-> to-spr-channel qwc)) - (add.i gp r0 DMA-CHCR-STR) - (s.w gp (-> to-spr-channel chcr)) - (add.i ra ra 1024) - (label tie-generic-prototype-next-color-chunk) - (m s5 palette-bank) - (xor.i palette-bank palette-bank TIE-PROTOTYPE-PALETTE-FLIP) - (b.le t9 r0 tie-generic-prototype-finish-color-upload :delay (add.i t9 t9 -32)) - ;; Wait before reusing a color bank for the next 1 KiB palette chunk. - (dma-wait-counting-stalls! gp to-spr-channel (-> prototype-work generic-wait-to-spr) - tie-generic-prototype-wait-next-color-upload tie-generic-prototype-start-next-color-upload) - ;; Kick off the next kilobyte before blending this one, so the transfer hides behind the - ;; multiply-accumulate loop rather than in front of it. - (label tie-generic-prototype-start-next-color-upload) - (s.w ra (-> to-spr-channel madr)) - (nop!) - (s.w palette-bank (-> to-spr-channel sadr)) - (add.i gp r0 64) - (s.w gp (-> to-spr-channel qwc)) - (add.i gp r0 DMA-CHCR-STR) - (s.w gp (-> to-spr-channel chcr)) - (add.i ra ra 1024) - (b tie-generic-prototype-expand-color-block :delay (nop!)) - ;; The last kilobyte has no successor to hide behind, so this is the one wait that is always - ;; paid in full. - (dma-wait-counting-stalls! gp to-spr-channel (-> prototype-work generic-wait-to-spr) - tie-generic-prototype-finish-color-upload tie-generic-prototype-expand-color-block) - ;; Prime the blend loop with the first two source quadwords. - ;; - ;; A palette entry is not one color, it is eight -- one per time-of-day slot -- packed as - ;; 32 bytes. mood-context.itimes holds the eight weights for the current time of day, each - ;; replicated once per channel, which fills exactly four quadwords of halfwords. So one - ;; pmulth plus three pmaddh blend all eight slots of one entry across all four channels at - ;; once, and the whole level's palette becomes 32 words per kilobyte of source. - (label tie-generic-prototype-expand-color-block) - (add.i gp a1 128) - (l.q s2 s5 (offset-of time-of-day-palette data)) - (nop!) - (l.q s4 s5 (+ (offset-of time-of-day-palette data) 16)) - (pextlb s3 r0 s2) - (mmi-nop!) - (pextub s2 r0 s2) - (mmi-nop!) - (pmulth r0 s3 t5) - (mmi-nop!) - (pextlb s3 r0 s4) - (mmi-nop!) - (pmaddh r0 s2 t6) - (mmi-nop!) - (pextub s4 r0 s4) - (mmi-nop!) - (pmaddh r0 s3 t7) - (l.q s3 s5 (+ (offset-of time-of-day-palette data) 32)) - (add.i s5 s5 32) - (nop!) - (pmaddh r0 s4 t8) - (l.q s4 s5 (+ (offset-of time-of-day-palette data) 16)) - (pextlb s2 r0 s3) - (mmi-nop!) - ;; One entry per iteration. pmfhl.lh collects the four packed accumulators, the shift by six - ;; divides out the weights' 6-bit fraction, pcpyud plus add.h folds the two halves of the - ;; sum together, pminh clamps every channel against the packed limit in prototype-tie-work, - ;; and ppacb squeezes the four halfwords back down to one RGBA word. Thirty-two words from - ;; the current 1 KiB source chunk. - (label tie-generic-prototype-expand-eight-colors) - (pextub s3 r0 s3) - (mmi-nop!) - (pmfhl.lh s1) - (mmi-nop!) - (pmulth r0 s2 t5) - (mmi-nop!) - (srl.h s2 s1 6) - (mmi-nop!) - (pcpyud s1 s2 s2) - (mmi-nop!) - (add.h s2 s1 s2) - (mmi-nop!) - (pminh s2 s2 a2) - (mmi-nop!) - (ppacb s1 r0 s2) - (mmi-nop!) - (pextlb s2 r0 s4) - (mmi-nop!) - (pmaddh r0 s3 t6) - (s.w s1 a1) - (pextub s4 r0 s4) - (mmi-nop!) - (pmaddh r0 s2 t7) - (l.q s3 s5 (+ (offset-of time-of-day-palette data) 32)) - (add.i s5 s5 32) - (add.i a1 a1 4) - (pmaddh r0 s4 t8) - (l.q s4 s5 (+ (offset-of time-of-day-palette data) 16)) - (b.ne a1 gp tie-generic-prototype-expand-eight-colors :delay (pextlb s2 r0 s3)) - (b.ge t9 r0 tie-generic-prototype-next-color-chunk :delay (nop!)) - (nop!) - (l.w a2 scratch-base (spr-offset prototype-tie-dma next 0)) - (nop!) - (l.w t6 scratch-base (spr-offset prototype-tie-dma geometry 1)) - (b.z a2 - tie-generic-prototype-select-geometry-one - :delay - (l.bu t5 scratch-base (spr-offset prototype-tie-dma frag-count 1))) - (bgezal r0 tie-generic-prototype-begin-fragments :delay (nop!))) - ;; Four nearly identical selectors, one per Generic geometry, each testing its list head and calling - ;; the shared fragment loop if it is populated. They fall through into each other rather than - ;; looping, because the return address is what tells the fragment loop which selector to come back - ;; to -- cheaper than keeping a variant index around. - (asm-block select-generic-geometry - (label tie-generic-prototype-select-geometry-one) - (nop!) - (l.w a2 scratch-base (spr-offset prototype-tie-dma next 1)) - (nop!) - (l.w t6 scratch-base (spr-offset prototype-tie-dma geometry 1)) - (b.z a2 - tie-generic-prototype-select-geometry-two - :delay - (l.bu t5 scratch-base (spr-offset prototype-tie-dma frag-count 1))) - (bgezal r0 tie-generic-prototype-begin-fragments :delay (nop!)) - ;; Test Generic variant two. - (label tie-generic-prototype-select-geometry-two) - (nop!) - (l.w a2 scratch-base (spr-offset prototype-tie-dma next 2)) - (nop!) - (l.w t6 scratch-base (spr-offset prototype-tie-dma geometry 2)) - (b.z a2 - tie-generic-prototype-select-geometry-three - :delay - (l.bu t5 scratch-base (spr-offset prototype-tie-dma frag-count 2))) - (bgezal r0 tie-generic-prototype-begin-fragments :delay (nop!)) - ;; Test the final Generic variant before advancing to the next prototype bucket. - (label tie-generic-prototype-select-geometry-three) - (nop!) - (l.w a2 scratch-base (spr-offset prototype-tie-dma next 3)) - (nop!) - (l.w t6 scratch-base (spr-offset prototype-tie-dma geometry 3)) - (b.z a2 - tie-generic-prototype-advance-prototype - :delay - (l.bu t5 scratch-base (spr-offset prototype-tie-dma frag-count 3))) - (bgezal r0 tie-generic-prototype-begin-fragments :delay (nop!)) - ;; Advance the prototype-array cursor and clear the tail words which terminate its Generic - ;; chain. The saved prototype count controls the outer loop. - (label tie-generic-prototype-advance-prototype) - (nop!) - (l.w a2 (-> prototype-work prototype-array)) - (nop!) - (l.w t5 scratch-base (spr-offset prototype-tie-dma length)) - (nop!) - (l.w t6 a2 12) - (nop!) - (s.w r0 a1 -56) - (b.nz t5 tie-generic-prototype-next-prototype :delay (add.i t7 t5 -1)) - (b tie-generic-prototype-finish-output :delay (s.w r0 a1 -52)) - ;; A local call shares the fragment loop between all four geometry selectors; ra returns to the - ;; selector which should test the next variant. - (label tie-generic-prototype-begin-fragments) - (add.i t6 t6 32) - (s.w t5 (-> prototype-work generic-model-1 vif1))) - ;; Emit one fragment: patch the upload template with this fragment's stream addresses and counts and - ;; append it, followed by the CALL that runs the geometry's instance list. Check the bank has room - ;; first -- a fragment packet must not be split across two transfers, since the DMA controller would - ;; execute half a chain. - (asm-block emit-generic-fragments - (label tie-generic-prototype-next-fragment) - (add.i t7 a0 4) - (add.i t8 r0 255) - (sub t8 t8 t7) - (l.w t7 (-> fragment gif-ref)) - (b.ge t8 r0 tie-generic-prototype-emit-fragment :delay (l.hu t8 (-> fragment gif-count))) - ;; Wait for the previous bank before returning a packet which would overflow this one. - (dma-wait-counting-stalls! a1 from-spr-channel (-> prototype-work generic-wait-from-spr) - tie-generic-prototype-wait-fragment-output tie-generic-prototype-flush-fragment-output) - ;; Switch output banks and reset the occupied-qword count. - (dma-return-output-bank! output-bank TIE-PROTOTYPE-OUTPUT-FLIP a1 a0 memory-cursor from-spr-channel - tie-generic-prototype-flush-fragment-output) - ;; Patch the four-qword Generic model template with this fragment's stream counts, output - ;; address, instance-list head, and Generic data reference, then append it to the output bank. - (label tie-generic-prototype-emit-fragment) - (nop!) - (l.hu t9 (-> fragment tex-count)) - (add t8 t8 t9) - (l.w t9 (-> fragment point-ref)) - (add.i a0 a0 4) - (l.hu gp (-> fragment vertex-count)) - (sll s5 a0 4) - (s.w t7 (-> prototype-work generic-model-0 dma addr)) - (add t7 s5 memory-cursor) - (s.h t8 (-> prototype-work generic-model-0)) - (nop!) - (s.w t7 (-> prototype-work generic-model-0 vif1)) - (nop!) - (s.w a2 (-> prototype-work generic-model-next dma addr)) - (nop!) - (s.w t9 (-> prototype-work generic-model-1 dma addr)) - (nop!) - (s.h gp (-> prototype-work generic-model-1)) - (nop!) - (l.w t7 (-> fragment generic-ref)) - (nop!) - (l.hu t8 (-> fragment generic-count)) - (nop!) - (s.w t7 (-> prototype-work generic-model-2 dma addr)) - (nop!) - (s.h t8 (-> prototype-work generic-model-2)) - (nop!) - (l.q t7 (-> prototype-work generic-model-0)) - (nop!) - (l.q t8 (-> prototype-work generic-model-1)) - (nop!) - (l.q t9 (-> prototype-work generic-model-2)) - (nop!) - (l.q gp (-> prototype-work generic-model-next)) - (nop!) - (s.q t7 a1) - (add.i t5 t5 -1) - (s.q t8 a1 16) - (add.i a2 a2 48) - (s.q t9 a1 32) - (s.q gp a1 48) - (add.i a1 a1 64) - (b.gt t5 r0 tie-generic-prototype-next-fragment :delay (add.i fragment fragment 64)) - (jr ra :delay (nop!))) - ;; Last bank out. This transfer uses the exact occupied quadword count rather than a full 256, and - ;; unlike every other flush the code has to wait for it to land before publishing the advanced cursor - ;; back into the caller's dma-buffer. An empty bank skips the transfer entirely. - (asm-block finish-generic-prototype-output - (label tie-generic-prototype-finish-output) - (b.z a0 tie-generic-prototype-wait-output-dma :delay (nop!)) - (dma-wait-counting-stalls! a1 from-spr-channel (-> prototype-work generic-wait-from-spr) - tie-generic-prototype-wait-final-output tie-generic-prototype-flush-final-output) - ;; Submit exactly the occupied part of the last output bank. - (label tie-generic-prototype-flush-final-output) - (s.w output-bank (-> from-spr-channel sadr)) - (nop!) - (s.w memory-cursor (-> from-spr-channel madr)) - (sll a1 a0 4) - (add memory-cursor memory-cursor a1) - (nop!) - (s.w a0 (-> from-spr-channel qwc)) - (add.i a0 r0 DMA-CHCR-STR) - (s.w a0 (-> from-spr-channel chcr)) - (nop!) - ;; Wait until the last bank has reached main memory before returning it to the caller. - (dma-wait-counting-stalls! a0 from-spr-channel (-> prototype-work generic-wait-from-spr) - tie-generic-prototype-wait-output-dma tie-generic-prototype-finish) - (label tie-generic-prototype-finish) - (l.w a0 scratch-base (spr-offset prototype-tie-dma dma-buffer)) - (nop!) - (s.w memory-cursor (-> dma-buf base)) - (nop!) - (m v0 r0) - (l.d ra sp) - (l.q gp sp 96) - (l.q s5 sp 80) - (l.q s4 sp 64) - (l.q s3 sp 48) - (l.q s2 sp 32) - (l.q s1 sp 16) - (jr ra :delay (add.i sp sp 112)) - (nop!) - (nop!))))) - -(#unless PC_PORT - (defun draw-inline-array-prototype-tie-asm ((dma-buf dma-buffer) (prototype-count int) (prototypes prototype-array-tie)) - "Expand the selected TIE palettes and build ordinary TIE fragment chains for geometry - variants one through three." - (declare (asm-func none) (allow-saved-regs)) - ;; Instance records are already grouped by prototype and LOD. This pass supplies each populated - ;; fragment with its list, converts the prototype's time-of-day colors, and emits the VIF stream - ;; consumed by the ordinary TIE VU1 program. - ;; Alternate the 1 KiB palette banks and 4 KiB output banks so color conversion, packet - ;; construction, and scratchpad DMA can overlap. - ;; - ;; The scratchpad layout matches the Generic pass. spr-offset reaches the ordinary list heads, - ;; geometry pointers, fragment counts, and persistent output state in prototype-tie-dma; the packet - ;; templates and wait counters are fields of prototype-tie-work. - (rlet ((prototype-work :reg t0 :type prototype-tie-work) - (from-spr-channel :reg t1 :type dma-bank-spr) - (output-bank :reg t2) - (palette-bank :reg t3) - (to-spr-channel :reg t4 :type dma-bank-spr) - (dma-buf :reg a0 :type dma-buffer) ;; the argument, before a0 becomes the packet qword count - (scratch-base :reg a3 :type terrain-context) - (mood :reg t8 :type mood-context) - (fragment :reg t6 :type tie-fragment) - (memory-cursor :reg v1)) - ;; The registers not aliased above genuinely change meaning every few instructions and keep their - ;; physical names: a0 is the caller's dma-buffer, then the count of quadwords occupied in the - ;; current output bank, then a DMA status word; a1 is the write cursor inside that bank, borrowed - ;; for channel status and byte counts; t5 through t9 pass around the bucket, its palette, its - ;; geometry pointers, the four time-of-day weight vectors, and finally the fragment cursor. - ;; Each block says what they are holding while it runs. - (asm-block setup-ordinary-prototype-stream - (label tie-prototype-entry) - (add.i sp sp -112) - (s.d ra sp) - (s.q s1 sp 16) - (s.q s2 sp 32) - (s.q s3 sp 48) - (s.q s4 sp 64) - (s.q s5 sp 80) - (s.q gp sp 96) - (nop!) - (lui scratch-base #x7000) - (l.w memory-cursor (-> dma-buf base)) - (lui from-spr-channel #x1000) - (lui output-bank #x1000) - (sync.l) - (cache dxwbin memory-cursor 0) - (sync.l) - (cache dxwbin memory-cursor 1) - (sync.l) - (m! prototype-work *prototype-tie-work*) - (ori from-spr-channel from-spr-channel #xd000) - (ori to-spr-channel output-bank #xd400) - (ori palette-bank scratch-base 16) - (ori output-bank scratch-base 2064) - (s.w a0 scratch-base (spr-offset prototype-tie-dma dma-buffer)) - (add.i t7 a1 -1) - (nop!) - (l.w t5 a2 (offset-of prototype-array-tie data)) - (add.i a0 r0 0) - (m a1 output-bank)) - ;; One prototype per outer iteration. Geometry 0 belongs to the near renderer, so this pass only - ;; looks at 1 through 3; if none of their list heads got an instance the prototype is skipped without - ;; reading anything else. Otherwise the heads, geometry pointers, fragment counts, and palette are - ;; copied into the scratchpad state area so the fragment loop can work from scratchpad rather than - ;; chasing main memory. - (asm-block prepare-ordinary-prototype - (label tie-prototype-next-prototype) - (nop!) - (l.q t6 t5 (offset-of prototype-bucket-tie next)) - (add.i t8 a2 4) - (s.w t7 scratch-base (spr-offset prototype-tie-dma length)) - (srl32 a2 t6 0) - (s.w t8 (-> prototype-work prototype-array)) - (pcpyud t8 t6 t6) - (l.w t7 t5 (offset-of prototype-bucket-tie tie-colors)) - (or t8 a2 t8) - (l.w a2 t5 (offset-of prototype-bucket-tie frag-count)) - (b.z t8 tie-prototype-advance-prototype :delay (l.w mood (-> scratch-base bsp mood))) - (nop!) - (l.q t5 t5 (offset-of prototype-bucket-tie geometry)) - (nop!) - (s.q t6 scratch-base (spr-offset prototype-tie-dma next 0)) - (nop!) - (s.w a2 scratch-base (spr-offset prototype-tie-dma frag-count 0)) - (nop!) - (s.q t5 scratch-base (spr-offset prototype-tie-dma geometry 0)) - (nop!) - (l.d a2 (-> prototype-work clamp)) - (nop!) - (l.w t6 t7 4) - (add.i ra t7 12) - (l.q t5 (-> mood itimes 0)) - (sra t7 t6 2) - (nop!) - (add t7 t7 a0) - (add.i t9 r0 221) - (sub t7 t9 t7) - (nop!) - (b.ge t7 r0 tie-prototype-upload-color-palette :delay (nop!))) - ;; A palette is up to 128 entries, which is 32 output quadwords plus its packet header, so it can - ;; easily be the thing that overflows the bank. Check first, return the bank if so, and only then - ;; start blending. - (asm-block upload-and-expand-ordinary-palette - (dma-wait-counting-stalls! a1 from-spr-channel (-> prototype-work wait-from-spr) - tie-prototype-wait-packet-output tie-prototype-flush-packet-output) - ;; Submit the occupied qwords and switch to the alternate output bank. - (dma-return-output-bank! output-bank TIE-PROTOTYPE-OUTPUT-FLIP a1 a0 memory-cursor from-spr-channel - tie-prototype-flush-packet-output) - ;; Round the entry count up to a whole 1 KiB chunk -- 32 entries of 32 bytes each -- and reserve - ;; the packet header plus the eight output quadwords the chunk will produce. - (label tie-prototype-upload-color-palette) - (add.i t7 t6 31) - (l.q t6 (-> prototype-work upload-palette-0 quad)) - (sra t7 t7 5) - (add.i a0 a0 2) - (sll t9 t7 5) - (s.q t6 a1) - (sra t6 t9 2) - (s.b t9 (-> prototype-work upload-palette-1 vif1 num)) - (add a0 a0 t6) - (s.h t6 (-> prototype-work upload-palette-1)) - (nop!) - (l.q t6 (-> mood itimes 1)) - (nop!) - (l.q gp (-> prototype-work upload-palette-1)) - (nop!) - (l.q t7 (-> mood itimes 2)) - (nop!) - (s.q gp a1 16) - (add.i a1 a1 32) - (l.q t8 (-> mood itimes 3)) - ;; The source colors come from main memory, so they are staged through the two 1 KiB color - ;; banks: start the next kilobyte's transfer, blend the kilobyte that just landed, repeat. - (dma-wait-counting-stalls! gp to-spr-channel (-> prototype-work wait-to-spr) - tie-prototype-wait-color-upload tie-prototype-start-color-upload) - ;; Start the first palette transfer. - (label tie-prototype-start-color-upload) - (s.w ra (-> to-spr-channel madr)) - (add.i t9 t9 -32) - (s.w palette-bank (-> to-spr-channel sadr)) - (add.i gp r0 64) - (s.w gp (-> to-spr-channel qwc)) - (add.i gp r0 DMA-CHCR-STR) - (s.w gp (-> to-spr-channel chcr)) - (add.i ra ra 1024) - (label tie-prototype-next-color-chunk) - (m s5 palette-bank) - (xor.i palette-bank palette-bank TIE-PROTOTYPE-PALETTE-FLIP) - (b.le t9 r0 tie-prototype-finish-color-upload :delay (add.i t9 t9 -32)) - ;; Wait before reusing a color bank for the next 1 KiB source chunk. - (dma-wait-counting-stalls! gp to-spr-channel (-> prototype-work wait-to-spr) - tie-prototype-wait-next-color-upload tie-prototype-start-next-color-upload) - ;; Kick off the next kilobyte before blending this one, so the transfer hides behind the - ;; multiply-accumulate loop rather than in front of it. - (label tie-prototype-start-next-color-upload) - (s.w ra (-> to-spr-channel madr)) - (nop!) - (s.w palette-bank (-> to-spr-channel sadr)) - (add.i gp r0 64) - (s.w gp (-> to-spr-channel qwc)) - (add.i gp r0 DMA-CHCR-STR) - (s.w gp (-> to-spr-channel chcr)) - (add.i ra ra 1024) - (b tie-prototype-expand-color-block :delay (nop!)) - ;; The last kilobyte has no successor to hide behind, so this is the one wait that is always - ;; paid in full. - (dma-wait-counting-stalls! gp to-spr-channel (-> prototype-work wait-to-spr) - tie-prototype-finish-color-upload tie-prototype-expand-color-block) - ;; Prime the blend loop with the first two source quadwords. - ;; - ;; A palette entry is not one color, it is eight -- one per time-of-day slot -- packed as - ;; 32 bytes. mood-context.itimes holds the eight weights for the current time of day, each - ;; replicated once per channel, which fills exactly four quadwords of halfwords. So one - ;; pmulth plus three pmaddh blend all eight slots of one entry across all four channels at - ;; once, and the whole level's palette becomes 32 words per kilobyte of source. - (label tie-prototype-expand-color-block) - (add.i gp a1 128) - (l.q s2 s5 (offset-of time-of-day-palette data)) - (nop!) - (l.q s4 s5 (+ (offset-of time-of-day-palette data) 16)) - (pextlb s3 r0 s2) - (mmi-nop!) - (pextub s2 r0 s2) - (mmi-nop!) - (pmulth r0 s3 t5) - (mmi-nop!) - (pextlb s3 r0 s4) - (mmi-nop!) - (pmaddh r0 s2 t6) - (mmi-nop!) - (pextub s4 r0 s4) - (mmi-nop!) - (pmaddh r0 s3 t7) - (l.q s3 s5 (+ (offset-of time-of-day-palette data) 32)) - (add.i s5 s5 32) - (nop!) - (pmaddh r0 s4 t8) - (l.q s4 s5 (+ (offset-of time-of-day-palette data) 16)) - (pextlb s2 r0 s3) - (mmi-nop!) - ;; One entry per iteration. pmfhl.lh collects the four packed accumulators, the shift by six - ;; divides out the weights' 6-bit fraction, pcpyud plus add.h folds the two halves of the - ;; sum together, pminh clamps every channel against the packed limit in prototype-tie-work, - ;; and ppacb squeezes the four halfwords back down to one RGBA word. Thirty-two words from - ;; the current 1 KiB source chunk for the VU1 palette. - (label tie-prototype-expand-eight-colors) - (pextub s3 r0 s3) - (mmi-nop!) - (pmfhl.lh s1) - (mmi-nop!) - (pmulth r0 s2 t5) - (mmi-nop!) - (srl.h s2 s1 6) - (mmi-nop!) - (pcpyud s1 s2 s2) - (mmi-nop!) - (add.h s2 s1 s2) - (mmi-nop!) - (pminh s2 s2 a2) - (mmi-nop!) - (ppacb s1 r0 s2) - (mmi-nop!) - (pextlb s2 r0 s4) - (mmi-nop!) - (pmaddh r0 s3 t6) - (s.w s1 a1) - (pextub s4 r0 s4) - (mmi-nop!) - (pmaddh r0 s2 t7) - (l.q s3 s5 (+ (offset-of time-of-day-palette data) 32)) - (add.i s5 s5 32) - (add.i a1 a1 4) - (pmaddh r0 s4 t8) - (l.q s4 s5 (+ (offset-of time-of-day-palette data) 16)) - (b.ne a1 gp tie-prototype-expand-eight-colors :delay (pextlb s2 r0 s3)) - (b.ge t9 r0 tie-prototype-next-color-chunk :delay (nop!)) - (nop!) - (l.w a2 scratch-base (spr-offset prototype-tie-dma next 1)) - (nop!) - (l.w t6 scratch-base (spr-offset prototype-tie-dma geometry 1)) - (b.z a2 tie-prototype-select-geometry-two :delay (l.bu t5 scratch-base (spr-offset prototype-tie-dma frag-count 1))) - (bgezal r0 tie-prototype-begin-fragments :delay (nop!))) - ;; Three nearly identical selectors, one per ordinary geometry, each testing its list head and calling - ;; the shared fragment loop if it is populated. They fall through into each other rather than looping, - ;; because the return address is what tells the fragment loop which selector to come back to. - (asm-block select-ordinary-geometry - (label tie-prototype-select-geometry-two) - (nop!) - (l.w a2 scratch-base (spr-offset prototype-tie-dma next 2)) - (nop!) - (l.w t6 scratch-base (spr-offset prototype-tie-dma geometry 2)) - (b.z a2 - tie-prototype-select-geometry-three - :delay - (l.bu t5 scratch-base (spr-offset prototype-tie-dma frag-count 2))) - (bgezal r0 tie-prototype-begin-fragments :delay (nop!)) - ;; Variant three is the final ordinary LOD. - (label tie-prototype-select-geometry-three) - (nop!) - (l.w a2 scratch-base (spr-offset prototype-tie-dma next 3)) - (nop!) - (l.w t6 scratch-base (spr-offset prototype-tie-dma geometry 3)) - (b.z a2 tie-prototype-advance-prototype :delay (l.bu t5 scratch-base (spr-offset prototype-tie-dma frag-count 3))) - (bgezal r0 tie-prototype-begin-fragments :delay (nop!)) - ;; Advance the prototype-array cursor after all populated ordinary variants are emitted. - (label tie-prototype-advance-prototype) - (nop!) - (l.w a2 (-> prototype-work prototype-array)) - (nop!) - (l.w t6 scratch-base (spr-offset prototype-tie-dma length)) - (nop!) - (l.w t5 a2 (offset-of prototype-array-tie data)) - (b.nz t6 tie-prototype-next-prototype :delay (add.i t7 t6 -1)) - (b tie-prototype-finish-output :delay (nop!)) - ;; A local call shares the fragment loop between the three LOD selectors. - (label tie-prototype-begin-fragments) - (add.i t6 t6 32) - (nop!)) - ;; Emit one fragment: patch the upload template with this fragment's stream addresses and counts and - ;; append it, followed by the CALL that runs the geometry's instance list. Check the bank has room - ;; first -- a fragment packet must not be split across two transfers, since the DMA controller would - ;; execute half a chain. - (asm-block emit-ordinary-fragments - (label tie-prototype-next-fragment) - (add.i t7 a0 4) - (add.i t8 r0 255) - (sub t7 t8 t7) - (l.w t8 (-> fragment gif-ref)) - (b.ge t7 r0 tie-prototype-emit-fragment :delay (l.hu t7 (-> fragment gif-count))) - ;; Wait for the previous output bank before flushing a packet that would overflow this one. - (dma-wait-counting-stalls! a1 from-spr-channel (-> prototype-work wait-from-spr) - tie-prototype-wait-fragment-output tie-prototype-flush-fragment-output) - ;; Submit this bank and continue the same geometry in the alternate bank. - (dma-return-output-bank! output-bank TIE-PROTOTYPE-OUTPUT-FLIP a1 a0 memory-cursor from-spr-channel - tie-prototype-flush-fragment-output) - ;; Patch the four-qword ordinary model template with the fragment's vertex/triangle counts, - ;; stream reference, palette reference, and selected instance-list head. - (label tie-prototype-emit-fragment) - (nop!) - (l.w s5 (-> fragment point-ref)) - (nop!) - (l.hu s4 (-> fragment tex-count)) - (nop!) - (l.hu gp (-> fragment vertex-count)) - (nop!) - (s.w t8 (-> prototype-work upload-model-0 dma addr)) - (nop!) - (s.h s4 (-> prototype-work upload-model-0)) - (add.i t9 s4 #x4000) - (s.b s4 (-> prototype-work upload-model-0 vif1 num)) - (sll s4 s4 4) - (s.w s5 (-> prototype-work upload-model-2 dma addr)) - (add t8 t8 s4) - (s.h gp (-> prototype-work upload-model-2)) - (sll gp gp 1) - (s.w a2 (-> prototype-work upload-model-3 dma addr)) - (nop!) - (s.b gp (-> prototype-work upload-model-2 vif1 num)) - (nop!) - (s.w t8 (-> prototype-work upload-model-1 dma addr)) - (nop!) - (s.h t7 (-> prototype-work upload-model-1)) - (sll t7 t7 2) - (s.h t9 (-> prototype-work upload-model-1 vif1)) - (nop!) - (s.b t7 (-> prototype-work upload-model-1 vif1 num)) - (nop!) - (l.q t7 (-> prototype-work upload-model-0)) - (nop!) - (l.q t8 (-> prototype-work upload-model-1)) - (nop!) - (l.q t9 (-> prototype-work upload-model-2)) - (nop!) - (l.q gp (-> prototype-work upload-model-3)) - (add.i a0 a0 4) - (s.q t7 a1) - (add.i t5 t5 -1) - (s.q t8 a1 16) - (add.i a2 a2 48) - (s.q t9 a1 32) - (s.q gp a1 48) - (add.i a1 a1 64) - (b.gt t5 r0 tie-prototype-next-fragment :delay (add.i fragment fragment 64)) - (jr ra :delay (nop!))) - ;; Last bank out. This transfer uses the exact occupied quadword count rather than a full 256, and - ;; unlike every other flush the code has to wait for it to land before publishing the advanced cursor - ;; back into the caller's dma-buffer. An empty bank skips the transfer entirely. - (asm-block finish-ordinary-prototype-output - (label tie-prototype-finish-output) - (b.z a0 tie-prototype-wait-output-dma :delay (nop!)) - (dma-wait-counting-stalls! a1 from-spr-channel (-> prototype-work wait-from-spr) - tie-prototype-wait-final-output tie-prototype-flush-final-output) - ;; Submit exactly the occupied qwords in the last bank. - (label tie-prototype-flush-final-output) - (s.w output-bank (-> from-spr-channel sadr)) - (nop!) - (s.w memory-cursor (-> from-spr-channel madr)) - (sll a1 a0 4) - (add memory-cursor memory-cursor a1) - (nop!) - (s.w a0 (-> from-spr-channel qwc)) - (add.i a0 r0 DMA-CHCR-STR) - (s.w a0 (-> from-spr-channel chcr)) - (nop!) - ;; Wait for the final transfer before publishing the advanced dma-buffer cursor. - (dma-wait-counting-stalls! a0 from-spr-channel (-> prototype-work wait-from-spr) - tie-prototype-wait-output-dma tie-prototype-finish) - (label tie-prototype-finish) - (l.w a0 scratch-base (spr-offset prototype-tie-dma dma-buffer)) - (nop!) - (s.w memory-cursor (-> dma-buf base)) - (nop!) - (m v0 r0) - (l.d ra sp) - (l.q gp sp 96) - (l.q s5 sp 80) - (l.q s4 sp 64) - (l.q s3 sp 48) - (l.q s2 sp 32) - (l.q s1 sp 16) - (jr ra :delay (add.i sp sp 112)) - (nop!) - (nop!)))) - (defun draw-inline-array-prototype-tie-near-asm ((dma-buf dma-buffer) (prototype-count int) (prototypes prototype-array-tie)) - "Expand the selected TIE palettes and build near-TIE fragment chains for geometry variant - zero." - (declare (asm-func none) (allow-saved-regs)) - ;; Near instances use a separate VU1 program because geometry crossing the camera plane must be - ;; clipped before perspective division. The packet format otherwise shares the prototype palette - ;; expansion and scratchpad output machinery used by ordinary TIE. - ;; Use the same palette and output double buffers as ordinary TIE, but consume only geometry - ;; variant zero, whose instances were classified for the near-camera clipping path. - ;; - ;; Near TIE uses variant zero from the same prototype state area and the five-quadword near-model - ;; packet in prototype-tie-work: one extra V4-8 stream at VU address 30 carrying the per-vertex data - ;; the clipper needs, and one more MSCAL, which is the only structural difference from the ordinary - ;; builder. - (rlet ((prototype-work :reg t0 :type prototype-tie-work) - (from-spr-channel :reg t1 :type dma-bank-spr) - (output-bank :reg t2) - (palette-bank :reg t3) - (to-spr-channel :reg t4 :type dma-bank-spr) - (dma-buf :reg a0 :type dma-buffer) ;; the argument, before a0 becomes the packet qword count - (scratch-base :reg a3 :type terrain-context) - (mood :reg t8 :type mood-context) - (fragment :reg t6 :type tie-fragment) - (memory-cursor :reg v1)) - ;; The registers not aliased above genuinely change meaning every few instructions and keep their - ;; physical names: a0 is the caller's dma-buffer, then the count of quadwords occupied in the - ;; current output bank, then a DMA status word; a1 is the write cursor inside that bank, borrowed - ;; for channel status and byte counts; t5 through t9 pass around the bucket, its palette, its - ;; geometry pointers, the four time-of-day weight vectors, and finally the fragment cursor. - ;; Each block says what they are holding while it runs. - (asm-block setup-near-prototype-stream - (label tie-near-prototype-entry) - (add.i sp sp -112) - (s.d ra sp) - (s.q s1 sp 16) - (s.q s2 sp 32) - (s.q s3 sp 48) - (s.q s4 sp 64) - (s.q s5 sp 80) - (s.q gp sp 96) - (nop!) - (lui scratch-base #x7000) - (l.w memory-cursor (-> dma-buf base)) - (lui from-spr-channel #x1000) - (lui output-bank #x1000) - (sync.l) - (cache dxwbin memory-cursor 0) - (sync.l) - (cache dxwbin memory-cursor 1) - (sync.l) - (m! prototype-work *prototype-tie-work*) - (ori from-spr-channel from-spr-channel #xd000) - (ori to-spr-channel output-bank #xd400) - (ori palette-bank scratch-base 16) - (ori output-bank scratch-base 2064) - (s.w a0 scratch-base (spr-offset prototype-tie-dma dma-buffer)) - (add.i t7 a1 -1) - (nop!) - (l.w t5 a2 12) - (add.i a0 r0 0) - (m a1 output-bank)) - ;; One prototype per outer iteration, and only geometry 0 -- the one the instance pass assigns to - ;; anything close enough to need clipping. A prototype whose variant-zero list is empty is skipped - ;; without reading anything else; otherwise its list head, geometry pointer, fragment count, and - ;; palette are copied into the scratchpad state area. - (asm-block prepare-near-prototype - (label tie-near-prototype-next-prototype) - (nop!) - (l.w t6 t5 (offset-of prototype-bucket-tie next)) - (add.i a2 a2 4) - (s.w t7 scratch-base (spr-offset prototype-tie-dma length)) - (nop!) - (s.w a2 (-> prototype-work prototype-array)) - (nop!) - (l.w t7 t5 (offset-of prototype-bucket-tie tie-colors)) - (nop!) - (l.w a2 t5 (offset-of prototype-bucket-tie frag-count)) - (b.z t6 tie-near-prototype-advance-prototype :delay (l.w mood (-> scratch-base bsp mood))) - (nop!) - (l.q t5 t5 (offset-of prototype-bucket-tie geometry)) - (nop!) - (s.q t6 scratch-base (spr-offset prototype-tie-dma next 0)) - (nop!) - (s.w a2 scratch-base (spr-offset prototype-tie-dma frag-count 0)) - (nop!) - (s.q t5 scratch-base (spr-offset prototype-tie-dma geometry 0)) - (nop!) - (l.d a2 (-> prototype-work clamp)) - (nop!) - (l.w t6 t7 4) - (add.i ra t7 12) - (l.q t5 (-> mood itimes 0)) - (sra t7 t6 2) - (nop!) - (add t7 t7 a0) - (add.i t9 r0 221) - (sub t7 t9 t7) - (nop!) - (b.ge t7 r0 tie-near-prototype-upload-color-palette :delay (nop!))) - ;; A palette is up to 128 entries, which is 32 output quadwords plus its packet header, so it can - ;; easily be the thing that overflows the bank. Check first, return the bank if so, and only then - ;; start blending. - (asm-block upload-and-expand-near-palette - (dma-wait-counting-stalls! a1 from-spr-channel (-> prototype-work near-wait-from-spr) - tie-near-prototype-wait-packet-output tie-near-prototype-flush-packet-output) - ;; Submit the occupied qwords and switch to the alternate output bank. - (dma-return-output-bank! output-bank TIE-PROTOTYPE-OUTPUT-FLIP a1 a0 memory-cursor from-spr-channel - tie-near-prototype-flush-packet-output) - ;; Round the entry count up to a whole 1 KiB chunk -- 32 entries of 32 bytes each -- and reserve - ;; the packet header plus the eight output quadwords the chunk will produce. - (label tie-near-prototype-upload-color-palette) - (add.i t7 t6 31) - (l.q t6 (-> prototype-work upload-palette-0 quad)) - (sra t7 t7 5) - (add.i a0 a0 2) - (sll t9 t7 5) - (s.q t6 a1) - (sra t6 t9 2) - (s.b t9 (-> prototype-work upload-palette-1 vif1 num)) - (add a0 a0 t6) - (s.h t6 (-> prototype-work upload-palette-1)) - (nop!) - (l.q t6 (-> mood itimes 1)) - (nop!) - (l.q gp (-> prototype-work upload-palette-1)) - (nop!) - (l.q t7 (-> mood itimes 2)) - (nop!) - (s.q gp a1 16) - (add.i a1 a1 32) - (l.q t8 (-> mood itimes 3)) - ;; The source colors come from main memory, so they are staged through the two 1 KiB color - ;; banks: start the next kilobyte's transfer, blend the kilobyte that just landed, repeat. - (dma-wait-counting-stalls! gp to-spr-channel (-> prototype-work near-wait-to-spr) - tie-near-prototype-wait-color-upload tie-near-prototype-start-color-upload) - ;; Start the first palette transfer. - (label tie-near-prototype-start-color-upload) - (s.w ra (-> to-spr-channel madr)) - (add.i t9 t9 -32) - (s.w palette-bank (-> to-spr-channel sadr)) - (add.i gp r0 64) - (s.w gp (-> to-spr-channel qwc)) - (add.i gp r0 DMA-CHCR-STR) - (s.w gp (-> to-spr-channel chcr)) - (add.i ra ra 1024) - (label tie-near-prototype-next-color-chunk) - (m s5 palette-bank) - (xor.i palette-bank palette-bank TIE-PROTOTYPE-PALETTE-FLIP) - (b.le t9 r0 tie-near-prototype-finish-color-upload :delay (add.i t9 t9 -32)) - ;; Wait before reusing a color bank for the next 1 KiB source chunk. - (dma-wait-counting-stalls! gp to-spr-channel (-> prototype-work near-wait-to-spr) - tie-near-prototype-wait-next-color-upload tie-near-prototype-start-next-color-upload) - ;; Kick off the next kilobyte before blending this one, so the transfer hides behind the - ;; multiply-accumulate loop rather than in front of it. - (label tie-near-prototype-start-next-color-upload) - (s.w ra (-> to-spr-channel madr)) - (nop!) - (s.w palette-bank (-> to-spr-channel sadr)) - (add.i gp r0 64) - (s.w gp (-> to-spr-channel qwc)) - (add.i gp r0 DMA-CHCR-STR) - (s.w gp (-> to-spr-channel chcr)) - (add.i ra ra 1024) - (b tie-near-prototype-expand-color-block :delay (nop!)) - ;; Wait for the last palette block, which has no following transfer to cover its latency. - (dma-wait-counting-stalls! gp to-spr-channel (-> prototype-work near-wait-to-spr) - tie-near-prototype-finish-color-upload tie-near-prototype-expand-color-block) - ;; Prime the blend loop with the first two source quadwords. - ;; - ;; A palette entry is not one color, it is eight -- one per time-of-day slot -- packed as - ;; 32 bytes. mood-context.itimes holds the eight weights for the current time of day, each - ;; replicated once per channel, which fills exactly four quadwords of halfwords. So one - ;; pmulth plus three pmaddh blend all eight slots of one entry across all four channels at - ;; once, and the whole level's palette becomes 32 words per kilobyte of source. - (label tie-near-prototype-expand-color-block) - (add.i gp a1 128) - (l.q s2 s5 (offset-of time-of-day-palette data)) - (nop!) - (l.q s4 s5 (+ (offset-of time-of-day-palette data) 16)) - (pextlb s3 r0 s2) - (mmi-nop!) - (pextub s2 r0 s2) - (mmi-nop!) - (pmulth r0 s3 t5) - (mmi-nop!) - (pextlb s3 r0 s4) - (mmi-nop!) - (pmaddh r0 s2 t6) - (mmi-nop!) - (pextub s4 r0 s4) - (mmi-nop!) - (pmaddh r0 s3 t7) - (l.q s3 s5 (+ (offset-of time-of-day-palette data) 32)) - (add.i s5 s5 32) - (nop!) - (pmaddh r0 s4 t8) - (l.q s4 s5 (+ (offset-of time-of-day-palette data) 16)) - (pextlb s2 r0 s3) - (mmi-nop!) - ;; One entry per iteration. pmfhl.lh collects the four packed accumulators, the shift by six - ;; divides out the weights' 6-bit fraction, pcpyud plus add.h folds the two halves of the - ;; sum together, pminh clamps every channel against the packed limit in prototype-tie-work, - ;; and ppacb squeezes the four halfwords back down to one RGBA word. Thirty-two words from - ;; the current 1 KiB source chunk for the VU1 palette. - (label tie-near-prototype-expand-eight-colors) - (pextub s3 r0 s3) - (mmi-nop!) - (pmfhl.lh s1) - (mmi-nop!) - (pmulth r0 s2 t5) - (mmi-nop!) - (srl.h s2 s1 6) - (mmi-nop!) - (pcpyud s1 s2 s2) - (mmi-nop!) - (add.h s2 s1 s2) - (mmi-nop!) - (pminh s2 s2 a2) - (mmi-nop!) - (ppacb s1 r0 s2) - (mmi-nop!) - (pextlb s2 r0 s4) - (mmi-nop!) - (pmaddh r0 s3 t6) - (s.w s1 a1) - (pextub s4 r0 s4) - (mmi-nop!) - (pmaddh r0 s2 t7) - (l.q s3 s5 (+ (offset-of time-of-day-palette data) 32)) - (add.i s5 s5 32) - (add.i a1 a1 4) - (pmaddh r0 s4 t8) - (l.q s4 s5 (+ (offset-of time-of-day-palette data) 16)) - (b.ne a1 gp tie-near-prototype-expand-eight-colors :delay (pextlb s2 r0 s3)) - (b.ge t9 r0 tie-near-prototype-next-color-chunk :delay (nop!)) - (nop!) - (l.w a2 scratch-base (spr-offset prototype-tie-dma next 0)) - (nop!) - (l.w t6 scratch-base (spr-offset prototype-tie-dma geometry 0)) - (nop!) - (l.bu t5 scratch-base (spr-offset prototype-tie-dma frag-count 0)) - (add.i t6 t6 32) - (nop!)) - ;; Emit one fragment, five template quadwords instead of four: the extra V4-8 stream at VU address 30 - ;; carries the per-vertex data the clipper needs, and there is one more MSCAL. Otherwise identical to - ;; the ordinary builder, including the rule that a fragment packet may not be split across two - ;; transfers. - (asm-block emit-near-fragments - (label tie-near-prototype-next-fragment) - (add.i t7 a0 4) - (add.i t8 r0 255) - (sub t7 t8 t7) - (l.w t8 (-> fragment gif-ref)) - (b.ge t7 r0 tie-near-prototype-emit-fragment :delay (l.hu t7 (-> fragment gif-count))) - ;; Wait for the previous output bank before flushing a packet that would overflow this one. - (dma-wait-counting-stalls! a1 from-spr-channel (-> prototype-work near-wait-from-spr) - tie-near-prototype-wait-fragment-output tie-near-prototype-flush-fragment-output) - ;; Submit this bank and continue the same prototype in the alternate bank. - (dma-return-output-bank! output-bank TIE-PROTOTYPE-OUTPUT-FLIP a1 a0 memory-cursor from-spr-channel - tie-near-prototype-flush-fragment-output) - ;; Patch the five-qword near model template with ordinary and clipping-stream references, - ;; vertex/triangle counts, palette state, and the variant-zero instance-list head. - (label tie-near-prototype-emit-fragment) - (nop!) - (l.w s5 (-> fragment dp-ref)) - (nop!) - (l.w s4 (-> fragment dp-qwc)) - (nop!) - (l.hu s3 (-> fragment tex-count)) - (nop!) - (l.w ra (-> fragment point-ref)) - (nop!) - (l.hu gp (-> fragment vertex-count)) - (nop!) - (s.w t8 (-> prototype-work upload-near-model-0 dma addr)) - (nop!) - (s.h s3 (-> prototype-work upload-near-model-0)) - (add.i t9 s3 #x4000) - (s.b s3 (-> prototype-work upload-near-model-0 vif1 num)) - (sll s3 s3 4) - (s.h s4 (-> prototype-work upload-near-model-2)) - (sll s4 s4 2) - (s.w s5 (-> prototype-work upload-near-model-2 dma addr)) - (add t8 t8 s3) - (s.b s4 (-> prototype-work upload-near-model-2 vif1 num)) - (nop!) - (s.h gp (-> prototype-work upload-near-model-3)) - (sll gp gp 1) - (s.w ra (-> prototype-work upload-near-model-3 dma addr)) - (nop!) - (s.b gp (-> prototype-work upload-near-model-3 vif1 num)) - (nop!) - (s.w t8 (-> prototype-work upload-near-model-1 dma addr)) - (nop!) - (s.h t7 (-> prototype-work upload-near-model-1)) - (sll t7 t7 2) - (s.h t9 (-> prototype-work upload-near-model-1 vif1)) - (nop!) - (s.b t7 (-> prototype-work upload-near-model-1 vif1 num)) - (nop!) - (s.w a2 (-> prototype-work upload-near-model-4 dma addr)) - (nop!) - (l.q t7 (-> prototype-work upload-near-model-0)) - (nop!) - (l.q t8 (-> prototype-work upload-near-model-1)) - (nop!) - (l.q t9 (-> prototype-work upload-near-model-2)) - (nop!) - (l.q ra (-> prototype-work upload-near-model-3)) - (nop!) - (l.q gp (-> prototype-work upload-near-model-4)) - (nop!) - (s.q t7 a1) - (add.i a0 a0 5) - (s.q t8 a1 16) - (add.i t5 t5 -1) - (s.q t9 a1 32) - (add.i a2 a2 48) - (s.q ra a1 48) - (s.q gp a1 64) - (add.i a1 a1 80) - (b.gt t5 r0 tie-near-prototype-next-fragment :delay (add.i fragment fragment 64)) - ;; Advance after every near fragment in this prototype has been appended. - (label tie-near-prototype-advance-prototype) - (nop!) - (l.w a2 (-> prototype-work prototype-array)) - (nop!) - (l.w t6 scratch-base (spr-offset prototype-tie-dma length)) - (nop!) - (l.w t5 a2 12) - (b.nz t6 tie-near-prototype-next-prototype :delay (add.i t7 t6 -1)) - (b tie-near-prototype-finish-output :delay (nop!))) - ;; Last bank out. This transfer uses the exact occupied quadword count rather than a full 256, and - ;; unlike every other flush the code has to wait for it to land before publishing the advanced cursor - ;; back into the caller's dma-buffer. An empty bank skips the transfer entirely. - (asm-block finish-near-prototype-output - (label tie-near-prototype-finish-output) - (b.z a0 tie-near-prototype-wait-output-dma :delay (nop!)) - (dma-wait-counting-stalls! a1 from-spr-channel (-> prototype-work near-wait-from-spr) - tie-near-prototype-wait-final-output tie-near-prototype-flush-final-output) - ;; Submit exactly the occupied qwords in the last bank. - (label tie-near-prototype-flush-final-output) - (s.w output-bank (-> from-spr-channel sadr)) - (nop!) - (s.w memory-cursor (-> from-spr-channel madr)) - (sll a1 a0 4) - (add memory-cursor memory-cursor a1) - (nop!) - (s.w a0 (-> from-spr-channel qwc)) - (add.i a0 r0 DMA-CHCR-STR) - (s.w a0 (-> from-spr-channel chcr)) - (nop!) - ;; Wait for the last transfer before returning its main-memory cursor. - (dma-wait-counting-stalls! a0 from-spr-channel (-> prototype-work near-wait-from-spr) - tie-near-prototype-wait-output-dma tie-near-prototype-finish) - (label tie-near-prototype-finish) - (l.w a0 scratch-base (spr-offset prototype-tie-dma dma-buffer)) - (nop!) - (s.w memory-cursor (-> dma-buf base)) - (nop!) - (m v0 r0) - (l.d ra sp) - (l.q gp sp 96) - (l.q s5 sp 80) - (l.q s4 sp 64) - (l.q s3 sp 48) - (l.q s2 sp 32) - (l.q s1 sp 16) - (jr ra :delay (add.i sp sp 112)) - (nop!) - (nop!))))) ;; DECOMP BEGINS @@ -2155,140 +25,90 @@ (set! (-> dma-buf base) (the pointer (&+ packet 16))) (set! (-> (the (pointer uint128) (-> dma-buf base))) (if (and *time-of-day-context* (nonzero? *time-of-day-context*)) - (-> *time-of-day-context* current-sun env-color quad) - (the uint128 0))) + (-> *time-of-day-context* current-sun env-color quad) + (the uint128 0))) (set! (-> dma-buf base) (the pointer (&+ packet 32)))))) (defun tie-init-buffers ((unused-dma-buf dma-buffer)) "Splice the TIE and near-TIE initialization and shutdown packets around each nonempty level - bucket. The dma-buf argument is unused; packets are allocated from the active display frame's - global buffer. Call this after all TIE drawing has appended its bucket chains." - ;; the TIE buckets are only used by TIE - so we can safely splice things at the beginning/end without - ;; messing things up. - (let ((bucket (-> *display* frames (-> *display* on-screen) frame bucket-group (bucket-id tie-0)))) - ;; only if we have something in the bucket. + bucket. The dma-buf argument is unused; packets are allocated from the active display frame's + global buffer. Call this after all TIE drawing has appended its bucket chains." + (let ((bucket (-> (current-frame) bucket-group 9))) (when (!= bucket (-> bucket last)) - (let* ((dma-buf (-> *display* frames (-> *display* on-screen) frame global-buf)) + (let* ((dma-buf (-> (current-frame) global-buf)) (init-start (-> dma-buf base))) - ;; add initialization data (tie-init-engine dma-buf (new 'static 'gs-test :atst (gs-atest not-equal) :zte #x1 :ztst (gs-ztest greater-equal)) 0) - ;; patch to the start - (let ((next-packet (the-as object (-> dma-buf base)))) - (set! (-> (the-as dma-packet next-packet) dma) (new 'static 'dma-tag :id (dma-tag-id next) :addr (-> bucket next))) - (set! (-> (the-as dma-packet next-packet) vif0) (new 'static 'vif-tag)) - (set! (-> (the-as dma-packet next-packet) vif1) (new 'static 'vif-tag)) - (set! (-> dma-buf base) (&+ (the-as pointer next-packet) 16))) + (dma-buffer-add-next-vif2 dma-buf 0 (-> bucket next) (new 'static 'vif-tag) (new 'static 'vif-tag)) (set! (-> bucket next) (the-as uint init-start))))) - (let ((bucket (-> *display* frames (-> *display* on-screen) frame bucket-group (bucket-id tie-0)))) - ;; only only if the bucket is nonempty + (let ((bucket (-> (current-frame) bucket-group 9))) (when (!= bucket (-> bucket last)) - (let* ((dma-buf (-> *display* frames (-> *display* on-screen) frame global-buf)) + (let* ((dma-buf (-> (current-frame) global-buf)) (shutdown-start (-> dma-buf base))) - ;; add the end data at the end. (tie-end-buffer dma-buf) (let ((chain-end (-> dma-buf base))) - (let ((next-packet (the-as object (-> dma-buf base)))) - (set! (-> (the-as dma-packet next-packet) dma) (new 'static 'dma-tag :id (dma-tag-id next))) - (set! (-> (the-as dma-packet next-packet) vif0) (new 'static 'vif-tag)) - (set! (-> (the-as dma-packet next-packet) vif1) (new 'static 'vif-tag)) - (set! (-> dma-buf base) (&+ (the-as pointer next-packet) 16))) + (dma-buffer-add-next-vif2 dma-buf 0 0 (new 'static 'vif-tag) (new 'static 'vif-tag)) (set! (-> (the-as (pointer uint32) (-> bucket last)) 1) (the-as uint shutdown-start)) (set! (-> bucket last) (the-as (pointer dma-tag) chain-end)))))) - ;; same as above, but for level 1's tie. - (let ((bucket (-> *display* frames (-> *display* on-screen) frame bucket-group (bucket-id tie-1)))) + (let ((bucket (-> (current-frame) bucket-group 16))) (when (!= bucket (-> bucket last)) - (let* ((dma-buf (-> *display* frames (-> *display* on-screen) frame global-buf)) + (let* ((dma-buf (-> (current-frame) global-buf)) (init-start (-> dma-buf base))) (tie-init-engine dma-buf (new 'static 'gs-test :atst (gs-atest not-equal) :zte #x1 :ztst (gs-ztest greater-equal)) 0) - (let ((next-packet (the-as object (-> dma-buf base)))) - (set! (-> (the-as dma-packet next-packet) dma) (new 'static 'dma-tag :id (dma-tag-id next) :addr (-> bucket next))) - (set! (-> (the-as dma-packet next-packet) vif0) (new 'static 'vif-tag)) - (set! (-> (the-as dma-packet next-packet) vif1) (new 'static 'vif-tag)) - (set! (-> dma-buf base) (&+ (the-as pointer next-packet) 16))) + (dma-buffer-add-next-vif2 dma-buf 0 (-> bucket next) (new 'static 'vif-tag) (new 'static 'vif-tag)) (set! (-> bucket next) (the-as uint init-start))))) - (let ((bucket (-> *display* frames (-> *display* on-screen) frame bucket-group (bucket-id tie-1)))) + (let ((bucket (-> (current-frame) bucket-group 16))) (when (!= bucket (-> bucket last)) - (let* ((dma-buf (-> *display* frames (-> *display* on-screen) frame global-buf)) + (let* ((dma-buf (-> (current-frame) global-buf)) (shutdown-start (-> dma-buf base))) (tie-end-buffer dma-buf) (let ((chain-end (-> dma-buf base))) - (let ((next-packet (the-as object (-> dma-buf base)))) - (set! (-> (the-as dma-packet next-packet) dma) (new 'static 'dma-tag :id (dma-tag-id next))) - (set! (-> (the-as dma-packet next-packet) vif0) (new 'static 'vif-tag)) - (set! (-> (the-as dma-packet next-packet) vif1) (new 'static 'vif-tag)) - (set! (-> dma-buf base) (&+ (the-as pointer next-packet) 16))) + (dma-buffer-add-next-vif2 dma-buf 0 0 (new 'static 'vif-tag) (new 'static 'vif-tag)) (set! (-> (the-as (pointer uint32) (-> bucket last)) 1) (the-as uint shutdown-start)) (set! (-> bucket last) (the-as (pointer dma-tag) chain-end)))))) (#unless PC_PORT - ;; level 0's tie near - (let ((bucket (-> *display* frames (-> *display* on-screen) frame bucket-group (bucket-id tie-near-0)))) + (let ((bucket (-> (current-frame) bucket-group 8))) (when (!= bucket (-> bucket last)) - (let* ((dma-buf (-> *display* frames (-> *display* on-screen) frame global-buf)) + (let* ((dma-buf (-> (current-frame) global-buf)) (init-start (-> dma-buf base))) (tie-near-init-engine dma-buf (new 'static 'gs-test :ate #x1 :atst (gs-atest greater-equal) :aref #x26 :zte #x1 :ztst (gs-ztest greater-equal)) 0) - (let ((next-packet (the-as object (-> dma-buf base)))) - (set! (-> (the-as dma-packet next-packet) dma) (new 'static 'dma-tag :id (dma-tag-id next) :addr (-> bucket next))) - (set! (-> (the-as dma-packet next-packet) vif0) (new 'static 'vif-tag)) - (set! (-> (the-as dma-packet next-packet) vif1) (new 'static 'vif-tag)) - (set! (-> dma-buf base) (&+ (the-as pointer next-packet) 16))) + (dma-buffer-add-next-vif2 dma-buf 0 (-> bucket next) (new 'static 'vif-tag) (new 'static 'vif-tag)) (set! (-> bucket next) (the-as uint init-start))))) - (let ((bucket (-> *display* frames (-> *display* on-screen) frame bucket-group (bucket-id tie-near-0)))) + (let ((bucket (-> (current-frame) bucket-group 8))) (when (!= bucket (-> bucket last)) - (let* ((dma-buf (-> *display* frames (-> *display* on-screen) frame global-buf)) + (let* ((dma-buf (-> (current-frame) global-buf)) (shutdown-start (-> dma-buf base))) (tie-near-end-buffer dma-buf) (let ((chain-end (-> dma-buf base))) - (let ((next-packet (the-as object (-> dma-buf base)))) - (set! (-> (the-as dma-packet next-packet) dma) (new 'static 'dma-tag :id (dma-tag-id next))) - (set! (-> (the-as dma-packet next-packet) vif0) (new 'static 'vif-tag)) - (set! (-> (the-as dma-packet next-packet) vif1) (new 'static 'vif-tag)) - (set! (-> dma-buf base) (&+ (the-as pointer next-packet) 16))) + (dma-buffer-add-next-vif2 dma-buf 0 0 (new 'static 'vif-tag) (new 'static 'vif-tag)) (set! (-> (the-as (pointer uint32) (-> bucket last)) 1) (the-as uint shutdown-start)) (set! (-> bucket last) (the-as (pointer dma-tag) chain-end)))))) - ;; level 1's tie near - (let ((bucket (-> *display* frames (-> *display* on-screen) frame bucket-group (bucket-id tie-near-1)))) + (let ((bucket (-> (current-frame) bucket-group 15))) (when (!= bucket (-> bucket last)) - (let* ((dma-buf (-> *display* frames (-> *display* on-screen) frame global-buf)) + (let* ((dma-buf (-> (current-frame) global-buf)) (init-start (-> dma-buf base))) (tie-near-init-engine dma-buf (new 'static 'gs-test :ate #x1 :atst (gs-atest greater-equal) :aref #x26 :zte #x1 :ztst (gs-ztest greater-equal)) 0) - (let ((next-packet (the-as object (-> dma-buf base)))) - (set! (-> (the-as dma-packet next-packet) dma) (new 'static 'dma-tag :id (dma-tag-id next) :addr (-> bucket next))) - (set! (-> (the-as dma-packet next-packet) vif0) (new 'static 'vif-tag)) - (set! (-> (the-as dma-packet next-packet) vif1) (new 'static 'vif-tag)) - (set! (-> dma-buf base) (&+ (the-as pointer next-packet) 16))) + (dma-buffer-add-next-vif2 dma-buf 0 (-> bucket next) (new 'static 'vif-tag) (new 'static 'vif-tag)) (set! (-> bucket next) (the-as uint init-start))))) - (let ((bucket (-> *display* frames (-> *display* on-screen) frame bucket-group (bucket-id tie-near-1)))) + (let ((bucket (-> (current-frame) bucket-group 15))) (when (!= bucket (-> bucket last)) - (let* ((dma-buf (-> *display* frames (-> *display* on-screen) frame global-buf)) + (let* ((dma-buf (-> (current-frame) global-buf)) (shutdown-start (-> dma-buf base))) (tie-near-end-buffer dma-buf) (let ((chain-end (-> dma-buf base))) - (let ((next-packet (the-as object (-> dma-buf base)))) - (set! (-> (the-as dma-packet next-packet) dma) (new 'static 'dma-tag :id (dma-tag-id next))) - (set! (-> (the-as dma-packet next-packet) vif0) (new 'static 'vif-tag)) - (set! (-> (the-as dma-packet next-packet) vif1) (new 'static 'vif-tag)) - (set! (-> dma-buf base) (&+ (the-as pointer next-packet) 16))) + (dma-buffer-add-next-vif2 dma-buf 0 0 (new 'static 'vif-tag) (new 'static 'vif-tag)) (set! (-> (the-as (pointer uint32) (-> bucket last)) 1) (the-as uint shutdown-start)) (set! (-> bucket last) (the-as (pointer dma-tag) chain-end))))))) 0 (none)) -;;;;;;;;;;;;;;;;; -;; TIE debug -;;;;;;;;;;;;;;;;; - -;; most of this doesn't really do anything. - -;; An inclusive range of instances to debug. (deftype tie-instance-debug (structure) ((max-instance uint32) (min-instance uint32))) -;; unused (define *tie* (new 'global 'tie-instance-debug)) (defun tie-debug-between ((min-instance uint) (max-instance uint)) @@ -2307,17 +127,12 @@ "Do nothing. This retained debug entry has no body." (none)) -;; unused (define *pke-hack* (new 'global 'vector)) -;; draw-inline-array-instance-tie -;; draw-inline-array-prototype-tie-generic-asm -;; draw-inline-array-prototype-tie-asm -;; draw-inline-array-prototype-tie-near-asm - (defmethod login ((this drawable-tree-instance-tie)) "Log in every child drawable in this TIE instance tree." - (if (nonzero? (-> this prototypes prototype-array-tie)) (login (-> this prototypes prototype-array-tie))) + (if (nonzero? (-> this prototypes prototype-array-tie)) + (login (-> this prototypes prototype-array-tie))) (dotimes (i (-> this length)) (login (-> this data i))) this) @@ -2325,7 +140,6 @@ (#when PC_PORT (defun add-pc-wind-data ((dma-buf dma-buffer)) "Append the paused flag and current TIE wind workspace for the PC renderer." - ;; packet to send 84 qw's (let ((packet (the-as dma-packet (-> dma-buf base)))) (set! (-> packet dma) (new 'static 'dma-tag :id (dma-tag-id cnt) :qwc 84)) (set! (-> packet vif0) (new 'static 'vif-tag)) @@ -2338,11 +152,18 @@ (#unless PC_PORT (defun draw-drawable-tree-instance-tie ((tie-tree drawable-tree-instance-tie) (lev level)) "Cull the TIE tree, group visible instances by prototype and distance-selected geometry variant, - build the enabled Generic, ordinary TIE, and near-TIE packet chains, insert those chains in the - owning level's buckets, update renderer timing and DMA-memory statistics, and publish the nearest - instance distance to the level." - (local-vars (instance-count int)) - ;; Cull the hierarchy into the scratchpad visibility list before touching the instance leaves. + build the enabled Generic, ordinary TIE, and near-TIE packet chains, insert those chains in the + owning level's buckets, update renderer timing and DMA-memory statistics, and publish the nearest + instance distance to the level." + (local-vars + (instance-counter-0 int) + (instance-counter-1 int) + (generic-counter-0 int) + (generic-counter-1 int) + (tie-counter-0 int) + (tie-counter-1 int) + (near-counter-0 int) + (near-counter-1 int)) (when (logtest? *vu1-enable-user* (vu1-renderer-mask tie-near tie generic)) (set! (-> *instance-tie-work* first-generic-prototype) (the-as uint 0)) (set! (-> *instance-tie-work* wind-vectors) (-> tie-tree prototypes wind-vectors)) @@ -2370,15 +191,13 @@ 0) (let* ((instances (-> (the-as drawable-inline-array-instance-tie instance-array) data)) (visibility (&-> (scratchpad-object terrain-context) work background vis-list (/ (-> instances 0 id) 8))) - (dma-buf (-> *display* frames (-> *display* on-screen) frame global-buf))) - (set! instance-count (-> (the-as drawable-inline-array-node instance-array) length)) + (dma-buf (-> (current-frame) global-buf)) + (instance-count (-> (the-as drawable-inline-array-node instance-array) length))) (when (nonzero? instance-count) (let* ((gsf-base-offset (logand (the-as int *gsf-buffer*) 8191)) (work-copy-offset (logand (the-as int (&- (logand (the-as int (&-> prototypes data -512)) 8191) (the-as uint gsf-base-offset))) 8191))) (set! *instance-tie-work-copy* (the-as instance-tie-work (+ (the-as int *gsf-buffer*) work-copy-offset)))) - ;; The instance pass fills per-prototype lists and counts. Each prototype pass then - ;; consumes only its populated variants and appends one renderer-specific chain. - (let ((instance-dma-start (-> *display* frames (-> *display* on-screen) frame global-buf base))) + (let ((instance-dma-start (-> (current-frame) global-buf base))) (quad-copy! (the-as pointer *instance-tie-work-copy*) (the-as pointer *instance-tie-work*) 28) (set! (-> *instance-tie-work-copy* wait-to-spr) (the-as uint 0)) (set! (-> *instance-tie-work-copy* wait-from-spr) (the-as uint 0)) @@ -2389,20 +208,12 @@ (the-as uint 0) (-> *instance-tie-work-copy* wait-to-spr) (-> *instance-tie-work-copy* wait-from-spr)) - (let ((min-distance (-> *instance-tie-work-copy* min-dist quad))) - (set! (-> *instance-tie-work* min-dist quad) min-distance)) + (vector-copy! (-> *instance-tie-work* min-dist) (-> *instance-tie-work-copy* min-dist)) (set! (-> *instance-tie-work* flags) (-> *instance-tie-work-copy* flags)) - (let ((usage *dma-mem-usage*)) - (when (nonzero? usage) - (set! (-> usage length) (max 10 (-> usage length))) - (set! (-> usage data 9 name) "tie-fragment") - (+! (-> usage data 9 count) 1) - (+! (-> usage data 9 used) - (&- (-> *display* frames (-> *display* on-screen) frame global-buf base) (the-as uint instance-dma-start))) - (set! (-> usage data 9 total) (-> usage data 9 used))))) + (dma-mem-usage-add! tie-fragment 1 (&- (-> (current-frame) global-buf base) (the-as uint instance-dma-start)))) (when (logtest? *vu1-enable-user* (vu1-renderer-mask generic)) (when (logtest? (-> *instance-tie-work* flags) (instance-tie-work-flag has-generic)) - (let ((generic-dma-start (-> *display* frames (-> *display* on-screen) frame global-buf base))) + (let ((generic-dma-start (-> (current-frame) global-buf base))) (set! (-> *prototype-tie-work* generic-wait-to-spr) (the-as uint 0)) (set! (-> *prototype-tie-work* generic-wait-from-spr) (the-as uint 0)) (set! (-> *instance-tie-work* first-generic-prototype) (the-as uint (-> dma-buf base))) @@ -2413,43 +224,37 @@ (the-as uint 0) (-> *prototype-tie-work* generic-wait-to-spr) (-> *prototype-tie-work* generic-wait-from-spr)) - (let ((usage *dma-mem-usage*)) - (when (nonzero? usage) - (set! (-> usage length) (max 18 (-> usage length))) - (set! (-> usage data 17 name) "tie-generic") - (+! (-> usage data 17 count) 1) - (+! (-> usage data 17 used) - (&- (-> *display* frames (-> *display* on-screen) frame global-buf base) (the-as uint generic-dma-start))) - (set! (-> usage data 17 total) (-> usage data 17 used))))))) + (dma-mem-usage-add! tie-generic 1 (&- (-> (current-frame) global-buf base) (the-as uint generic-dma-start)))))) (when (logtest? *vu1-enable-user* (vu1-renderer-mask tie)) - (let ((tie-dma-start (-> *display* frames (-> *display* on-screen) frame global-buf base))) + (let ((tie-dma-start (-> (current-frame) global-buf base))) (when (logtest? *vu1-enable-user* (vu1-renderer-mask tie)) - (with-dma-buffer-add-bucket ((tie-dma-buf (-> *display* frames (-> *display* on-screen) frame global-buf)) (if (zero? (-> lev index)) (bucket-id tie-0) (bucket-id tie-1))) :bucket-group (-> *display* frames (-> *display* on-screen) frame bucket-group) (set! (-> *prototype-tie-work* wait-to-spr) (the-as uint 0)) (set! (-> *prototype-tie-work* wait-from-spr) (the-as uint 0)) (reset! (-> *perf-stats* data 11)) (draw-inline-array-prototype-tie-asm tie-dma-buf prototype-count prototypes) (read! (-> *perf-stats* data 11)) (update-wait-stats (-> *perf-stats* data 11) - (the-as uint 0) - (-> *prototype-tie-work* wait-to-spr) - (-> *prototype-tie-work* wait-from-spr)))) - (let ((usage *dma-mem-usage*)) - (when (nonzero? usage) - (set! (-> usage length) (max 10 (-> usage length))) - (set! (-> usage data 9 name) "tie-fragment") - (+! (-> usage data 9 count) 1) - (+! (-> usage data 9 used) - (&- (-> *display* frames (-> *display* on-screen) frame global-buf base) (the-as uint tie-dma-start))) - (set! (-> usage data 9 total) (-> usage data 9 used)))))) + (with-dma-buffer-add-bucket ((tie-dma-buf (-> (current-frame) global-buf)) (if (zero? (-> lev index)) (bucket-id tie-0) (bucket-id tie-1))) + (set! (-> *prototype-tie-work* wait-to-spr) (the-as uint 0)) + (set! (-> *prototype-tie-work* wait-from-spr) (the-as uint 0)) + (reset! (-> *perf-stats* data 11)) + (draw-inline-array-prototype-tie-asm tie-dma-buf prototype-count prototypes) + (read! (-> *perf-stats* data 11)) + (update-wait-stats (-> *perf-stats* data 11) + (the-as uint 0) + (-> *prototype-tie-work* wait-to-spr) + (-> *prototype-tie-work* wait-from-spr)))) + (dma-mem-usage-add! tie-fragment 1 (&- (-> (current-frame) global-buf base) (the-as uint tie-dma-start))))) (when (logtest? *vu1-enable-user* (vu1-renderer-mask tie-near)) - (let ((near-dma-start (-> *display* frames (-> *display* on-screen) frame global-buf base))) - (with-dma-buffer-add-bucket ((near-dma-buf (-> *display* frames (-> *display* on-screen) frame global-buf)) (if (zero? (-> lev index)) (bucket-id tie-near-0) (bucket-id tie-near-1))) :bucket-group (-> *display* frames (-> *display* on-screen) frame bucket-group) (set! (-> *prototype-tie-work* near-wait-to-spr) (the-as uint 0)) (set! (-> *prototype-tie-work* near-wait-from-spr) (the-as uint 0)) (reset! (-> *perf-stats* data 12)) (draw-inline-array-prototype-tie-near-asm near-dma-buf prototype-count prototypes) (read! (-> *perf-stats* data 12)) (update-wait-stats (-> *perf-stats* data 12) - (the-as uint 0) - (-> *prototype-tie-work* near-wait-to-spr) - (-> *prototype-tie-work* near-wait-from-spr))) - (let ((usage *dma-mem-usage*)) - (when (nonzero? usage) - (set! (-> usage length) (max 16 (-> usage length))) - (set! (-> usage data 15 name) "tie-near") - (+! (-> usage data 15 count) 1) - (+! (-> usage data 15 used) - (&- (-> *display* frames (-> *display* on-screen) frame global-buf base) (the-as uint near-dma-start))) - (set! (-> usage data 15 total) (-> usage data 15 used)))))))))) + (let ((near-dma-start (-> (current-frame) global-buf base))) + (with-dma-buffer-add-bucket ((near-dma-buf (-> (current-frame) global-buf)) + (if (zero? (-> lev index)) + (bucket-id tie-near-0) + (bucket-id tie-near-1))) + (set! (-> *prototype-tie-work* near-wait-to-spr) (the-as uint 0)) + (set! (-> *prototype-tie-work* near-wait-from-spr) (the-as uint 0)) + (reset! (-> *perf-stats* data 12)) + (draw-inline-array-prototype-tie-near-asm near-dma-buf prototype-count prototypes) + (read! (-> *perf-stats* data 12)) + (update-wait-stats (-> *perf-stats* data 12) + (the-as uint 0) + (-> *prototype-tie-work* near-wait-to-spr) + (-> *prototype-tie-work* near-wait-from-spr))) + (dma-mem-usage-add! tie-near 1 (&- (-> (current-frame) global-buf base) (the-as uint near-dma-start))))))))) 0) (set! (-> lev closest-object 5) (-> *instance-tie-work* min-dist x)) 0 @@ -2457,194 +262,22 @@ (#when PC_PORT (defun draw-drawable-tree-instance-tie ((tie-tree drawable-tree-instance-tie) (lev level)) - "Cull the TIE tree and build the enabled Generic, ordinary TIE, and near-TIE packet chains for - its owning level." - ;; only if one of our renderers is enabled. - (when (logtest? *vu1-enable-user* (vu1-renderer-mask tie-near tie generic)) - ;; setup work (TODO, what uses TIE wind?) - (set! (-> *instance-tie-work* first-generic-prototype) (the-as uint 0)) - (set! (-> *instance-tie-work* wind-vectors) (-> tie-tree prototypes wind-vectors)) - ;; - (let ((last-array-index (+ (-> tie-tree length) -1))) ;; number of arrays of draw-nodes (depth of the BVH tree, not counting instance leaves) - ;; perform draw node culling. - ;; Note: It's okay to skip this. The visible list right now will just be the occlusion string - ;; The PC renderer won't see this (and has its own version of culling that's plenty fast) - ;; The instance drawing will end up looking at too many instances, but this is fine - it - ;; should reject those, and it plenty fast. - #| - (when (nonzero? last-array-index) - (dotimes (depth-index last-array-index) - (let* ((parent-array (-> tie-tree data depth-index)) - (child-array (-> tie-tree data (+ depth-index 1))) - (parent-vis-byte-index (/ (-> (the-as drawable-inline-array-node parent-array) data 0 id) 8)) - (child-vis-byte-index (/ (-> (the-as drawable-inline-array-node child-array) data 0 id) 8)) - (parent-visibility (+ parent-vis-byte-index #x38b0 #x70000000)) - (child-visibility (+ child-vis-byte-index #x38b0 #x70000000)) - ) - (draw-node-cull - (the-as pointer child-visibility) - (the-as pointer parent-visibility) - (-> (the-as drawable-inline-array-node parent-array) data) - (-> (the-as drawable-inline-array-node parent-array) length) - ) - ) - ) - ) - |# - (let* ((instance-array (-> tie-tree data last-array-index)) ;; leaves - (prototypes (-> tie-tree prototypes prototype-array-tie)) ;; prototypes - (prototype-count (-> prototypes length)) ;; number of prototypes - ) - (dotimes (i prototype-count) ;; loop over prototypes, zero stuff?? - (let ((prototype (-> prototypes array-data i))) - (set! (-> prototype next-clear) (the-as uint128 0)) - (set! (-> prototype generic-count-clear) (the-as uint128 0)) - (set! (-> prototype generic-next-clear) (the-as uint128 0))) - 0) - (let* ((instances (-> (the-as drawable-inline-array-instance-tie instance-array) data)) ;; the inline array of instances - ;; (visibility (&-> (scratchpad-object terrain-context) work background vis-list (if (zero? (-> tie-tree length)) 0 (/ (-> instances 0 id) 8)))) ;; vis for first. - (dma-buf (-> *display* frames (-> *display* on-screen) frame global-buf)) ;; dma buf to write to - ) - ;; (set! instance-count (-> (the-as drawable-inline-array-node instance-array) length)) - ;; if we actually have things to draw - (when #t ;; (nonzero? sv-16) - ;; this is some buffer for the generic renderer - (let* ((gsf-base-offset (logand (the-as int *gsf-buffer*) 8191)) - (work-copy-offset (logand (the-as int (&- (logand (the-as int (&-> (-> prototypes data) -512)) 8191) (the-as uint gsf-base-offset))) 8191))) - ;; not sure why, but we'll use some gsf-buffer space to store an instance-tie-work - ;; all the external stuff will dump into *instance-tie-work*, and we'll make a copy that's used - ;; in the actual DMA generation code. - (set! *instance-tie-work-copy* (the-as instance-tie-work (+ (the-as int *gsf-buffer*) work-copy-offset)))) - ;;; TIE instance Drawing - ;; we do the instances first so the prototypes that aren't drawn can be skipped. - (let ((instance-dma-start (-> *display* frames (-> *display* on-screen) frame global-buf base))) - ;; actually copy the work - (quad-copy! (the-as pointer *instance-tie-work-copy*) (the-as pointer *instance-tie-work*) 28) - ;; clear perf counting stuff - (set! (-> *instance-tie-work-copy* wait-to-spr) (the-as uint 0)) - (set! (-> *instance-tie-work-copy* wait-from-spr) (the-as uint 0)) - (reset! (-> *perf-stats* data 9)) - ;; DRAW! - ;; note: this is a bit wasteful because we only care about generic ties. - ;; non-generics are drawn fully in C++, but we're computing unused stuff here. - ;; This ends up being so fast it's probably not worth worrying about yet. - ;; (when (not *use-etie*) - ;; (with-profiler "tie-instance" - ;; (draw-inline-array-instance-tie visibility instances instance-count dma-buf))) - ;; finish perf stats - (read! (-> *perf-stats* data 9)) - (update-wait-stats (-> *perf-stats* data 9) - (the-as uint 0) - (-> *instance-tie-work-copy* wait-to-spr) - (-> *instance-tie-work-copy* wait-from-spr)) - ;; copy out things from instance tie work - (let ((min-distance (-> *instance-tie-work-copy* min-dist quad))) - (set! (-> *instance-tie-work* min-dist quad) min-distance)) - (set! (-> *instance-tie-work* flags) (-> *instance-tie-work-copy* flags)) - ;; update memory usage - (let ((usage *dma-mem-usage*)) - (when (nonzero? usage) - (set! (-> usage length) (max 10 (-> usage length))) - (set! (-> usage data 9 name) "tie-fragment") - (+! (-> usage data 9 count) 1) - (+! (-> usage data 9 used) - (&- (-> *display* frames (-> *display* on-screen) frame global-buf base) (the-as uint instance-dma-start))) - (set! (-> usage data 9 total) (-> usage data 9 used))))) - ;; Generic TIE prototype drawing - (when (and (logtest? *vu1-enable-user* (vu1-renderer-mask generic)) (not *use-etie*)) - (when (logtest? (-> *instance-tie-work* flags) (instance-tie-work-flag has-generic)) - (let ((generic-dma-start (-> *display* frames (-> *display* on-screen) frame global-buf base))) - (set! (-> *prototype-tie-work* generic-wait-to-spr) (the-as uint 0)) - (set! (-> *prototype-tie-work* generic-wait-from-spr) (the-as uint 0)) - (set! (-> *instance-tie-work* first-generic-prototype) (the-as uint (-> dma-buf base))) - ;; hack, I expect this to overwrite this. - (set! (-> (the (pointer uint64) (-> dma-buf base))) #xdeadbeefdeadbeef) - (reset! (-> *perf-stats* data 10)) - (with-profiler "tie-generic-protos" - (draw-inline-array-prototype-tie-generic-asm dma-buf prototype-count prototypes)) - (read! (-> *perf-stats* data 10)) - (update-wait-stats (-> *perf-stats* data 10) - (the-as uint 0) - (-> *prototype-tie-work* generic-wait-to-spr) - (-> *prototype-tie-work* generic-wait-from-spr)) - ;; Note: we don't add to a bucket. This lives in some buffer somewhere and generic will take care of actually adding it. - (let ((usage *dma-mem-usage*)) - (when (nonzero? usage) - (set! (-> usage length) (max 18 (-> usage length))) - (set! (-> usage data 17 name) "tie-generic") - (+! (-> usage data 17 count) 1) - (+! (-> usage data 17 used) - (&- (-> *display* frames (-> *display* on-screen) frame global-buf base) (the-as uint generic-dma-start))) - (set! (-> usage data 17 total) (-> usage data 17 used))))))) - ;; Normal TIE prototype drawing - (when (logtest? *vu1-enable-user* (vu1-renderer-mask tie)) - (let ((tie-dma-start (-> *display* frames (-> *display* on-screen) frame global-buf base))) - (when (logtest? *vu1-enable-user* (vu1-renderer-mask tie)) - ;;(draw-inline-array-prototype-tie-asm tie-dma-buf prototype-count prototypes) - ;; this actually generates real drawing DMA, so add it to the appropriate bucket. - (with-dma-buffer-add-bucket ((tie-dma-buf (-> *display* frames (-> *display* on-screen) frame global-buf)) (if (zero? (-> lev index)) (bucket-id tie-0) (bucket-id tie-1))) :bucket-group (-> *display* frames (-> *display* on-screen) frame bucket-group) (set! (-> *prototype-tie-work* wait-to-spr) (the-as uint 0)) (set! (-> *prototype-tie-work* wait-from-spr) (the-as uint 0)) (reset! (-> *perf-stats* data 11)) (add-pc-tfrag3-data tie-dma-buf (-> *level* data (-> (scratchpad-object terrain-context) bsp lev-index))) (add-pc-wind-data tie-dma-buf) (pc-add-tie-envmap-info tie-dma-buf) (read! (-> *perf-stats* data 11)) (update-wait-stats (-> *perf-stats* data 11) - (the-as uint 0) - (-> *prototype-tie-work* wait-to-spr) - (-> *prototype-tie-work* wait-from-spr)))) - (let ((usage *dma-mem-usage*)) - (when (nonzero? usage) - (set! (-> usage length) (max 10 (-> usage length))) - (set! (-> usage data 9 name) "tie-fragment") - (+! (-> usage data 9 count) 1) - (+! (-> usage data 9 used) - (&- (-> *display* frames (-> *display* on-screen) frame global-buf base) (the-as uint tie-dma-start))) - (set! (-> usage data 9 total) (-> usage data 9 used)))))) - #| - (when (logtest? *vu1-enable-user* (vu1-renderer-mask tie-near)) - (let ((near-dma-start (-> *display* frames (-> *display* on-screen) frame global-buf base))) - (let* ((near-dma-buf (-> *display* frames (-> *display* on-screen) frame global-buf)) - (near-packet-start (-> near-dma-buf base)) - ) - (set! (-> *prototype-tie-work* near-wait-to-spr) (the-as uint 0)) - (set! (-> *prototype-tie-work* near-wait-from-spr) (the-as uint 0)) - (reset! (-> *perf-stats* data 12)) - ;;(draw-inline-array-prototype-tie-near-asm near-dma-buf prototype-count prototypes) - (read! (-> *perf-stats* data 12)) - (update-wait-stats (-> *perf-stats* data 12) (the-as uint 0) - (-> *prototype-tie-work* near-wait-to-spr) - (-> *prototype-tie-work* near-wait-from-spr) - ) - (let ((near-packet-end (-> near-dma-buf base))) - (let ((near-next-packet (the-as object (-> near-dma-buf base)))) - (set! (-> (the-as dma-packet near-next-packet) dma) (new 'static 'dma-tag :id (dma-tag-id next))) - (set! (-> (the-as dma-packet near-next-packet) vif0) (new 'static 'vif-tag)) - (set! (-> (the-as dma-packet near-next-packet) vif1) (new 'static 'vif-tag)) - (set! (-> near-dma-buf base) (&+ (the-as pointer near-next-packet) 16)) - ) - (dma-bucket-insert-tag - (-> *display* frames (-> *display* on-screen) frame bucket-group) - (if (zero? (-> lev index)) - (bucket-id tie-near-0) - (bucket-id tie-near-1) - ) - near-packet-start - (the-as (pointer dma-tag) near-packet-end) - ) - ) - ) - (let ((usage *dma-mem-usage*)) - (when (nonzero? usage) - (set! (-> usage length) (max 16 (-> usage length))) - (set! (-> usage data 15 name) "tie-near") - (+! (-> usage data 15 count) 1) - (+! (-> usage data 15 used) - (&- (-> *display* frames (-> *display* on-screen) frame global-buf base) (the-as uint near-dma-start)) - ) - (set! (-> usage data 15 total) (-> usage data 15 used)) - ) - ) - ) - ) - |# - )))) - 0) - (set! (-> lev closest-object 5) (-> *instance-tie-work* min-dist x)) - 0 + "Submit the native PC TIE renderer data for this tree's level." + (when (logtest? *vu1-enable-user* (vu1-renderer-mask tie)) + (let ((tie-dma-start (-> (current-frame) global-buf base))) + (with-dma-buffer-add-bucket ((tie-dma-buf (-> (current-frame) global-buf)) (if (zero? (-> lev index)) (bucket-id tie-0) (bucket-id tie-1))) + (set! (-> *prototype-tie-work* wait-to-spr) (the-as uint 0)) + (set! (-> *prototype-tie-work* wait-from-spr) (the-as uint 0)) + (reset! (-> *perf-stats* data 11)) + (add-pc-tfrag3-data tie-dma-buf (-> *level* data (-> (scratchpad-object terrain-context) bsp lev-index))) + (add-pc-wind-data tie-dma-buf) + (pc-add-tie-envmap-info tie-dma-buf) + (read! (-> *perf-stats* data 11)) + (update-wait-stats (-> *perf-stats* data 11) + (the-as uint 0) + (-> *prototype-tie-work* wait-to-spr) + (-> *prototype-tie-work* wait-from-spr))) + (dma-mem-usage-add! tie-fragment 1 (&- (-> (current-frame) global-buf base) (the-as uint tie-dma-start))))) (none))) (defmethod draw ((this drawable-tree-instance-tie) (submitted-tree drawable-tree-instance-tie) (frame display-frame)) @@ -2660,23 +293,60 @@ (defmethod collect-stats ((this drawable-tree-instance-tie)) "Accumulate the enabled Generic, ordinary TIE, and near-TIE prototype, fragment, instance, triangle, and display-vertex counts produced by the most recent draw." + (#when PC_PORT + ;; The native renderer does not build the EE's per-prototype instance chains, so reproduce the + ;; counters that collect-stats consumes. PC TIE has no near path: visible instances select + ;; ordinary/Generic geometry 1..3 from the same depth-minus-radius tests as the EE. + (let* ((prototypes (-> this prototypes prototype-array-tie)) + (instance-array + (the-as drawable-inline-array-instance-tie (-> this data (+ (-> this length) -1)))) + (visibility + (the-as (pointer uint8) (-> (scratchpad-object terrain-context) work background vis-list))) + (camera *math-camera*) + (camera-space (new-stack-vector0))) + (dotimes (i (-> prototypes length)) + (set! (-> prototypes array-data i generic-count-clear) (the-as uint128 0))) + (dotimes (i (-> instance-array length)) + (let* ((instance (-> instance-array data i)) + (instance-id (-> instance id)) + (visibility-byte (-> visibility (sar instance-id 3))) + (visibility-mask (sar #x80 (logand instance-id 7))) + (prototype (-> instance bucket-ptr))) + (when (and (logtest? visibility-byte visibility-mask) + (not (logtest? (-> prototype flags) 1)) + (sphere-in-view-frustum? (the-as sphere (-> instance bsphere)))) + (vector-copy! camera-space (-> instance bsphere)) + (set! (-> camera-space w) 1.0) + (vector-matrix*! camera-space camera-space (-> camera camera-rot)) + (let* ((radius (-> instance bsphere w)) + (lod-distance (- (-> camera-space z) radius)) + (geometry-index (cond + ((> lod-distance (-> prototype far-plane)) 3) + ((> lod-distance (-> prototype mid-plane)) 2) + (else 1))) + (generic? (and (logtest? (-> prototype flags) 2) + (>= (* (+ (-> prototype envmap-fade-far) + radius + (- (vector-length camera-space))) + (-> prototype envmap-rfade)) + 1.0)))) + (if generic? + (+! (-> prototype generic-count geometry-index) 1) + (+! (-> prototype count geometry-index) 1)))))))) (when (logtest? *vu1-enable-user* (vu1-renderer-mask tie-near tie generic)) (-> this data (+ (-> this length) -1)) (let ((prototypes (-> this prototypes prototype-array-tie))) (dotimes (i (-> prototypes length)) (let ((prototype (-> prototypes array-data i))) - ;; Generic can use all four variants; ordinary TIE uses 1..3 and near TIE uses 0. (when (logtest? *vu1-enable-user* (vu1-renderer-mask generic)) (let ((geometry-index 0) (last-geometry-index 3)) (while (>= last-geometry-index geometry-index) (let ((instance-count (-> prototype generic-count geometry-index)) - (geometry (the-as prototype-tie (-> prototype geometry geometry-index)))) + (geometry (-> prototype geometry-override geometry-index))) (when (nonzero? instance-count) (let ((fragment (the-as object (-> geometry data))) (fragment-count (-> geometry length))) - ;; Groups and fragments count unique geometry; instances count placements. - ;; Triangle and display-vertex totals therefore scale by instance-count. (+! (-> *terrain-stats* tie-generic groups) 1) (+! (-> *terrain-stats* tie-generic fragments) fragment-count) (+! (-> *terrain-stats* tie-generic instances) instance-count) @@ -2692,7 +362,7 @@ (last-geometry-index 3)) (while (>= last-geometry-index geometry-index) (let ((instance-count (-> prototype count geometry-index)) - (geometry (the-as prototype-tie (-> prototype geometry geometry-index)))) + (geometry (-> prototype geometry-override geometry-index))) (when (nonzero? instance-count) (let ((fragment (the-as object (-> geometry data))) (fragment-count (-> geometry length))) @@ -2708,7 +378,7 @@ (+! geometry-index 1)))) (when (logtest? *vu1-enable-user* (vu1-renderer-mask tie-near)) (let ((instance-count (-> prototype count 0)) - (geometry (the-as prototype-tie (-> prototype geometry 0)))) + (geometry (-> prototype geometry-override 0))) (when (nonzero? instance-count) (let ((fragment (the-as object (-> geometry data))) (fragment-count (-> geometry length))) @@ -2729,64 +399,37 @@ (let* ((prototypes (-> this prototypes prototype-array-tie)) (prototype-count (-> prototypes length))) (dotimes (i prototype-count) - (let ((geometry (-> prototypes array-data i geometry 0))) (debug-draw geometry geometry frame)))) + (let ((geometry (-> prototypes array-data i geometry-override 0))) + (debug-draw geometry geometry frame)))) (none)) -;;;;;;;;;;;;;;;;; -;; TIE collision -;;;;;;;;;;;;;;;;; - -;; note: the first three methods appear twice in the original code. -;; modified for PC: check length before colliding. - (defmethod collide-with-box ((this drawable-tree-instance-tie) (count int) (result collide-list)) - "Append collision geometry from every child of this nonempty tree that intersects the active - collision box. The count argument is part of the drawable collision ABI and is unused here." - (when (nonzero? (-> this length)) - (collide-with-box (-> this data 0) (-> this length) result)) + (collide-with-box (-> this data 0) (-> this length) result) 0 (none)) (defmethod collide-y-probe ((this drawable-tree-instance-tie) (count int) (result collide-list)) - "Append collision geometry from every child of this nonempty tree that intersects the active - vertical probe. The count argument is part of the drawable collision ABI and is unused here." - (when (nonzero? (-> this length)) - (collide-y-probe (-> this data 0) (-> this length) result)) + (collide-y-probe (-> this data 0) (-> this length) result) 0 (none)) (defmethod collide-ray ((this drawable-tree-instance-tie) (count int) (result collide-list)) - "Append collision geometry from every child of this nonempty tree that intersects the active - swept-sphere ray. The count argument is part of the drawable collision ABI and is unused here." - (when (nonzero? (-> this length)) - (collide-ray (-> this data 0) (-> this length) result)) + (collide-ray (-> this data 0) (-> this length) result) 0 (none)) (defmethod collide-with-box ((this drawable-inline-array-instance-tie) (count int) (result collide-list)) - "Append collision geometry from every instance in this nonempty inline array that intersects - the active collision box. The count argument is part of the drawable collision ABI and is - unused here." - (when (nonzero? (-> this length)) - (collide-with-box (the-as instance-tie (-> this data)) (-> this length) result)) + (collide-with-box (the-as instance-tie (-> this data)) (-> this length) result) 0 (none)) (defmethod collide-y-probe ((this drawable-inline-array-instance-tie) (count int) (result collide-list)) - "Append collision geometry from every instance in this nonempty inline array that intersects - the active vertical probe. The count argument is part of the drawable collision ABI and is - unused here." - (when (nonzero? (-> this length)) - (collide-y-probe (the-as instance-tie (-> this data)) (-> this length) result)) + (collide-y-probe (the-as instance-tie (-> this data)) (-> this length) result) 0 (none)) (defmethod collide-ray ((this drawable-inline-array-instance-tie) (count int) (result collide-list)) - "Append collision geometry from every instance in this nonempty inline array that intersects - the active swept-sphere ray. The count argument is part of the drawable collision ABI and is - unused here." - (when (nonzero? (-> this length)) - (collide-ray (the-as instance-tie (-> this data)) (-> this length) result)) + (collide-ray (the-as instance-tie (-> this data)) (-> this length) result) 0 (none)) @@ -2794,26 +437,11 @@ "Restore the saved TIE debugging camera position, orientation, and field of view." (let ((camera-position (new-stack-vector0)) (camera-rotation (new-stack-matrix0))) - (set! (-> camera-position x) 1246582.6) - (set! (-> camera-position y) 57026.02) - (set! (-> camera-position z) -490734.78) - (set! (-> camera-position w) 1.0) - (set! (-> camera-rotation vector 0 x) -0.9873) - (set! (-> camera-rotation vector 0 y) 0.0) - (set! (-> camera-rotation vector 0 z) -0.1587) - (set! (-> camera-rotation vector 0 w) 0.0) - (set! (-> camera-rotation vector 1 x) 0.0014) - (set! (-> camera-rotation vector 1 y) 0.9999) - (set! (-> camera-rotation vector 1 z) -0.0092) - (set! (-> camera-rotation vector 1 w) 0.0) - (set! (-> camera-rotation vector 2 x) 0.1587) - (set! (-> camera-rotation vector 2 y) -0.0093) - (set! (-> camera-rotation vector 2 z) -0.9872) - (set! (-> camera-rotation vector 2 w) 0.0) - (set! (-> camera-rotation vector 3 x) 0.0) - (set! (-> camera-rotation vector 3 y) 0.0) - (set! (-> camera-rotation vector 3 z) 0.0) - (set! (-> camera-rotation vector 3 w) 1.0) + (set-vector! camera-position 1246582.6 57026.02 -490734.78 1.0) + (set-vector! (-> camera-rotation vector 0) -0.9873 0.0 -0.1587 0.0) + (set-vector! (-> camera-rotation vector 1) 0.0014 0.9999 -0.0092 0.0) + (set-vector! (-> camera-rotation vector 2) 0.1587 -0.0093 -0.9872 0.0) + (set-vector! (-> camera-rotation vector 3) 0.0 0.0 0.0 1.0) (debug-set-camera-pos-rot! camera-position camera-rotation)) (send-event *camera* 'set-fov 11650.845) (none)) diff --git a/goal_src/jak1/engine/gfx/tie/tie-near.gc b/goal_src/jak1/engine/gfx/tie/tie-near.gc index 5404263b26..25aea4b755 100644 --- a/goal_src/jak1/engine/gfx/tie/tie-near.gc +++ b/goal_src/jak1/engine/gfx/tie/tie-near.gc @@ -11,7 +11,7 @@ ;; Ordinary TIE is allowed to be careless about the view volume: it either rejects a fragment ;; outright or projects it and lets the GS scissor the result. That stops working the moment a ;; triangle straddles the camera plane, because dividing by a w near zero throws coordinates off to -;; infinity and the GS has no way to recover. So the instance pass hands anything that comes near the +;; infinity and the GS uses fixed point. So the instance pass hands anything that comes near the ;; guard volume to this renderer instead, which keeps positions homogeneous long enough to clip them ;; properly. ;; diff --git a/goal_src/jak1/engine/gfx/tie/tie-work.gc b/goal_src/jak1/engine/gfx/tie/tie-work.gc index 1353b9d17b..209f272771 100644 --- a/goal_src/jak1/engine/gfx/tie/tie-work.gc +++ b/goal_src/jak1/engine/gfx/tie/tie-work.gc @@ -5,20 +5,6 @@ (require "engine/dma/dma-buffer.gc") (require "engine/math/vector-h.gc") -;; The DMA and VIF packets TIE builds its chains out of, initialized once at load time. -;; -;; Nothing here is sent as it stands. The packet builders in tie-methods.gc copy these quadwords into -;; scratchpad and patch in the addresses, transfer lengths, and chain links for the instance or -;; fragment they are working on. Keeping the constant words in a static template means the inner loop -;; writes three or four words instead of assembling a packet from scratch, which matters when it is -;; doing it once per instance. -;; -;; Fields left at zero below are the patched ones. The suspicious-looking VIF immediates are VU -;; addresses: #x80c6 is 198 with its mask bits, #xc0cc is 204, #x4346 is 838. -;; -;; The instance stream is six V4-32 quadwords to VU 198 -- the transform, morph, and fog -- followed -;; by packed V4-8 palette indices at 204. upload-color-2 and upload-color-ret are the same upload with -;; different endings: both start VU entry 0, one continues the DMA chain and one returns from it. (define *instance-tie-work* (new 'static 'instance-tie-work @@ -57,24 +43,9 @@ :refl-fade-fac -0.000625 :refl-fade-end 409600.0)) -;; Both endings start entry 0 with an MSCAL; only their DMA tag differs. Assigned after the static -;; definition because the same field is written twice and the second write is the one that survives. (set! (-> *instance-tie-work* upload-color-2 vif1) (new 'static 'vif-tag :cmd (vif-cmd mscal) :msk #x1)) - (set! (-> *instance-tie-work* upload-color-ret vif1) (new 'static 'vif-tag :cmd (vif-cmd mscal) :msk #x1)) -;; The same idea for prototype geometry, where the templates are bigger and there are three sets of -;; them: ordinary, near, and Generic. -;; -;; A fragment upload is a palette (128 packed colors to VU 838), then the shaders and strip templates -;; as V4-32 at VU 0, then the packed points as V4-8 at 0 and V4-16 at 50. The near path inserts one -;; more V4-8 block at 30 for the data its clipper needs. -;; -;; Two MSCALs are threaded through the middle of that. Entry 4 is empty and exists only to wait for -;; the previous VU1 invocation to finish reading the area about to be overwritten; entry 6 installs the -;; newly arrived fragment's shader and strip templates into both of its GIF output banks. Putting them -;; inside the upload rather than before it lets the palette transfer overlap the previous fragment's -;; drawing. (define *prototype-tie-work* (new 'static 'prototype-tie-work @@ -165,8 +136,6 @@ (new 'static 'dma-packet :dma (new 'static 'dma-tag :id (dma-tag-id next))) :clamp #x8000ff00ff00ff)) -;; Thread the two MSCALs into both upload chains. Same reason as the instance endings above: these -;; fields are written twice and the second write wins. (set! (-> *prototype-tie-work* upload-model-1 vif0) (new 'static 'vif-tag :imm #x4 :cmd (vif-cmd mscal) :msk #x1)) (set! (-> *prototype-tie-work* upload-model-3 vif0) (new 'static 'vif-tag :imm #x6 :cmd (vif-cmd mscal) :msk #x1)) diff --git a/goal_src/jak1/engine/gfx/tie/tie.gc b/goal_src/jak1/engine/gfx/tie/tie.gc index 45650d670f..85c11465cd 100644 --- a/goal_src/jak1/engine/gfx/tie/tie.gc +++ b/goal_src/jak1/engine/gfx/tie/tie.gc @@ -12,33 +12,20 @@ ;; Each fragment carries its own adgif shaders, packed points, strip packet templates, and a shared ;; time-of-day color palette; each instance record carries a transform, a wind phase, a LOD morph ;; weight, and the palette indices its vertices should use. Upload the fragment once, then let VU1 -;; chew through every instance that picked it. That is the whole trick, and it is why a level can -;; afford several thousand crates and huts. +;; chew through every instance that picked it. ;; -;; The three interesting details, in the order you will meet them below: -;; -;; * Level of detail is continuous, not stepped, and it is a tessellation rather than a swap. The -;; "tessellating" in the name is literal: a coarser geometry is the same surface with one level of -;; subdivision removed. A vertex that exists only at the finer level -- an interpolated point, or -;; "dvert" -- was created by splitting an edge, and it stores two vectors: where it was authored, -;; and the displacement that puts it back on the edge it split. It also stores the palette indices +;; * Level of detail uses a similar strategy to TFRAG where vertices collapse back to the midpoint +;; of an edge, creating degenerate triangles that can be removed without any popping. +;; These use a different format than tfrag: each simplifying vertex stores its own position +;; and the vector to move it back to the edge. It also stores the palette indices ;; of that edge's two endpoints. Given t = the instance's morph weight, rising from 0 at the near ;; edge of the LOD band to 1 at the far edge: ;; ;; position = authored + t * displacement ;; color = (1 - t) * own + t * (color(parent1) + color(parent2)) / 2 ;; -;; so at t = 1 the vertex has moved onto its parents' edge and taken that edge's shading, the -;; surface it belongs to *is* the coarser surface, and the triangles about to disappear are already -;; degenerate. Nothing pops. The reciprocal band lengths in the prototype's rdists are what make t -;; reach 1 precisely at the plane where the coarser geometry takes over. -;; -;; Note which half of that is pinned by the hardware and which is data. The color blend is always -;; the plain average of the two parents -- the 0.5 is an immediate in the microprogram, not -;; something the exporter chooses. The displacement is authored, so nothing here forces the vertex -;; to land on the edge midpoint specifically; it is only the color that assumes it did. A collapse -;; to any other point on the edge would still be geometrically crack-free but would shade very -;; slightly wrong, so midpoint is the reading the two halves agree on. +;; so at t = 1 the vertex has moved onto its parents' edge and taken that edge's shading, so +;; the degenerate triangle can be removed. ;; ;; T-junctions and cracks are avoided by a separate and much blunter decision: the LOD is chosen ;; per instance, not per fragment, so every fragment of a prototype is always at the same @@ -56,14 +43,9 @@ ;; owns by an offset carried in its own data. Nothing walks a strip list afterwards to collect ;; vertices. ;; -;; Worth knowing because TFRAG, the other background renderer, is the opposite: it transforms into an +;; TFRAG is the opposite design for GIF building: it transforms into an ;; intermediate array and then a separate pass walks an exported draw-point table, follows each entry -;; to a vertex, and copies it into the GIF buffer through a sequential cursor -- a gather. Almost -;; every structural difference between the two follows from that one choice, including why TFRAG can -;; redirect a strip by rewriting one record while TIE cannot, and why TIE needs three rotating output -;; banks where TFRAG needs two. -;; -;; Three consequences here, and they explain most of what looks odd further down: +;; to a vertex, and copies it into the GIF buffer through a sequential cursor -- a gather. ;; ;; There is no index buffer, so a vertex used by two triangle strips needs a slot in each. The ;; exporter finds those and gives them a second offset; the renderer transforms once and stores the @@ -73,10 +55,7 @@ ;; tag's count per fragment per frame. So every conditional below changes *what* goes into a slot, ;; never *whether* a slot is filled. ;; -;; And there is no per-vertex branch in the steady state: the tails are handled by duplicating the -;; loop once per slot instead of testing, which is why the same twenty instructions appear as flush, -;; short, and dual-destination variants. -;; * GIF output rotates through three banks, not two, and the reason is entry 6 rather than entry 0. +;; * GIF output rotates through three banks, not two, to double buffer fragment and instance swaps. ;; Within one fragment the two banks named by gifbuf alternate per instance in the ordinary ;; double-buffered way: each entry-0 invocation rotates gifbuf, fills gifbuf.x, kicks it, and ;; stops, so the bank being filled is never the bank the GIF is draining. The third bank exists @@ -86,7 +65,6 @@ ;; the freshly kicked one alone. With two banks it would have to overwrite geometry the GIF had not ;; read yet. ;; -;; What lives where, since the data is spread across four levels of ownership and it matters which: ;; ;; per level the visibility string. One bit per instance, in BVH order, so a word of zeroes skips ;; 32 instances without a transfer. @@ -100,17 +78,11 @@ ;; ;; The palette is the part worth being clear about. The colors themselves belong to the prototype and are ;; expanded and uploaded once per prototype per frame, to VU 838, where all of its fragments and all of -;; its instances read the same 128 entries. What each instance owns is not colors but *indices* into -;; them -- one per vertex, packed four to a quadword for base points and one quadword per interpolated -;; point, uploaded to VU 204 with the instance's transform. So two crates built from the same prototype -;; share every color yet can be lit completely differently, one standing in sunlight and one in shadow, -;; at the cost of one byte per vertex. +;; its instances read the same 128 entries. Each prototype has its own indices into +;; that per-prototype pallete. ;; -;; That is also why the palette upload sits *outside* the fragment loop and the index upload *inside* -;; the per-instance chain: the expensive thing happens once, the cheap thing happens thousands of times. -;; -;; GENERIC models can also be routed into the TIE packet path; see generic-tie-convert and -;; generic-tie-execute. +;; TIE can emit to generic to handle environment maps, but we don't need this on PC: instead we just +;; send all TIE to the PC renderers which handles environment mapping. ;; DECOMP BEGINS @@ -137,7 +109,8 @@ "Return the array header size plus storage for its active inline TIE instances." (the-as int (+ (-> drawable-inline-array-instance-tie size) (* (+ (-> this length) -1) 64)))) -;; INFO: this function exists in multiple non-identical object files +;; This method is mistakenly duplicated, this earlier definition is overwritten by the later one. +;; OpenGOAL flags this as an error, so we comment out on PC. (#unless PC_PORT (defmethod login ((this drawable-tree-instance-tie)) "Leave this TIE instance tree unchanged." @@ -223,8 +196,8 @@ (dotimes (i (-> this debug-lines length)) (+! (-> usage data 14 count) (-> (the-as (pointer int32) (-> this debug-lines i)) 0)) (let ((debug-line-bytes (asize-of (the-as basic (-> this debug-lines i))))) - ;; TODO: the count is charged to tie-debug above, but the bytes are charged to tie-colors - ;; here. Verify which category the report intends. + ;; ????: the count is charged to tie-debug above, but the bytes are charged to tie-colors + ;; here. (+! (-> usage data 12 used) debug-line-bytes) (+! (-> usage data 12 total) (logand -16 (+ debug-line-bytes 15)))))) (label cfg-13) @@ -286,11 +259,6 @@ ;; TIE Renderer ;;;;;;;;;;;;;;;;;;;;;;;;;; -;; The nine constant quadwords uploaded to VU1 966 through 974 once per frame. Six carry packet -;; templates and the persistent buffer state installed by entry 8; the last three are the alpha-test -;; packet and its two selectable TEST states. The overlays let the setup code write each quadword -;; through whichever view is convenient -- GS types for the tags, vector for the float-biased -;; addresses, qword for the integer bank addresses -- without a pile of casts. (deftype tie-consts (structure) ((data uint32 24) (vector vector 6 :inline :overlay-at (-> data 0)) @@ -352,23 +320,18 @@ ;; draining. The third bank is for the fragment boundary. Entry 6 has to write templates into *both* ;; banks it will use, and at that moment the last instance of the previous fragment has an outstanding ;; kick on one of the two banks it was using. Rotating in the longest-idle bank keeps entry 6 away from -;; it. Two banks would not be enough; four would buy nothing. +;; it. ;; -;; Deriving the third address by subtracting the two live ones from their sum costs two vector -;; subtractions and no integer lookup, which is the only reason extra.x exists. ;; ;; Vertices are not assigned to a bank or a slot by their position in the stream. Each vertex carries ;; its own destination offset in the w lane of its position quadword; add the current bank's biased -;; address, mtir it out, and that is where the triplet goes. No output cursor, no ordering constraint -;; between vertices -- which is exactly what lets the render loops keep four of them in flight. +;; address, mtir it out, and that is where the triplet goes. ;; ;; That addressing scheme is also how reuse works. The strip templates are prebuilt with a fixed slot ;; per vertex per strip, so there is no index buffer and a vertex belonging to two strips has to appear ;; in both. Such a vertex carries a second offset in the w lane of its ST quadword and the ;; dual-destination loop variants store the identical ST, RGBAQ and XYZF2 triplet to both slots -- same -;; texture coordinates, same time-of-day color, same screen position, transformed once. Anything else -;; would show: a position difference opens a crack, and an ST or color difference draws a visible seam -;; down the shared edge. Both offsets are inside the bank currently being filled. +;; texture coordinates, same time-of-day color, same screen position, transformed once. ;; ;; The four group-terminating addresses arrive as offsets relative to whichever bank the exporter had ;; in mind, so kick-addr remembers the base they are currently expressed against and each instance @@ -1714,14 +1677,8 @@ BASE/OFFSET, mode, and cycle state for the packed instance stream. alpha-blend controls the geometry primitive's ABE bit." (when (logtest? *vu1-enable-user* (vu1-renderer-mask tie)) - ;; Install the microprogram and set the caller-selected TEST state before any TIE packets. (dma-buffer-add-vu-function dma-buf tie-vu1-block 1) - (let* ((v1-3 dma-buf) - (direct-packet (the-as dma-packet (-> v1-3 base)))) - (set! (-> direct-packet dma) (new 'static 'dma-tag :qwc #x2 :id (dma-tag-id cnt))) - (set! (-> direct-packet vif0) (new 'static 'vif-tag)) - (set! (-> direct-packet vif1) (new 'static 'vif-tag :imm #x2 :cmd (vif-cmd direct) :msk #x1)) - (set! (-> v1-3 base) (&+ (the-as pointer direct-packet) 16))) + (dma-buffer-add-cnt-vif2 dma-buf #x2 (new 'static 'vif-tag) (new 'static 'vif-tag :imm #x2 :cmd (vif-cmd direct) :msk #x1)) (let* ((v1-4 dma-buf) (direct-gif (the-as gs-gif-tag (-> v1-4 base)))) (set! (-> direct-gif tag) (new 'static 'gif-tag64 :nloop #x1 :eop #x1 :nreg #x1)) @@ -1732,7 +1689,6 @@ (set! (-> (the-as (pointer gs-test) test-data) 0) test-state) (set! (-> (the-as (pointer gs-reg64) test-data) 1) (gs-reg64 test-1)) (set! (-> v1-5 base) (&+ test-data 16))) - ;; Upload tie-consts to VU address 966. (let ((constant-qwc 9)) (let* ((v1-6 dma-buf) (constant-packet (the-as dma-packet (-> v1-6 base)))) @@ -1742,27 +1698,13 @@ (set! (-> v1-6 base) (&+ (the-as pointer constant-packet) 16))) (tie-init-consts (the-as tie-consts (-> dma-buf base)) alpha-blend) (&+! (-> dma-buf base) (* constant-qwc 16))) - ;; Entry 8 initializes the persistent GIF and instance-bank registers. - (let* ((v1-9 dma-buf) - (init-packet (the-as dma-packet (-> v1-9 base)))) - (set! (-> init-packet dma) (new 'static 'dma-tag :id (dma-tag-id cnt))) - (set! (-> init-packet vif0) (new 'static 'vif-tag :imm #x8 :cmd (vif-cmd mscalf) :msk #x1)) - (set! (-> init-packet vif1) (new 'static 'vif-tag :cmd (vif-cmd flusha) :msk #x1)) - (set! (-> v1-9 base) (&+ (the-as pointer init-packet) 16))) - ;; #x4b000000 is 2^23. When an upload selects VIF's additive mode, adding a small packed - ;; integer places it in the mantissa of an exactly representable float; mtir can recover the - ;; low address or index bits without a separate integer conversion. - (let* ((v1-10 dma-buf) - (row-packet (the-as dma-packet (-> v1-10 base)))) - (set! (-> row-packet dma) (new 'static 'dma-tag :qwc #x2 :id (dma-tag-id cnt))) - (set! (-> row-packet vif0) (new 'static 'vif-tag)) - (set! (-> row-packet vif1) (new 'static 'vif-tag :cmd (vif-cmd strow) :msk #x1)) - (set! (-> v1-10 base) (&+ (the-as pointer row-packet) 16))) + (dma-buffer-add-cnt-vif2 dma-buf + 0 + (new 'static 'vif-tag :imm #x8 :cmd (vif-cmd mscalf) :msk #x1) + (new 'static 'vif-tag :cmd (vif-cmd flusha) :msk #x1)) + (dma-buffer-add-cnt-vif2 dma-buf #x2 (new 'static 'vif-tag) (new 'static 'vif-tag :cmd (vif-cmd strow) :msk #x1)) (let ((row-data (the-as (pointer uint32) (-> dma-buf base)))) - (set! (-> (the-as (inline-array vector4w) row-data) 0 x) #x4b000000) - (set! (-> (the-as (inline-array vector4w) row-data) 0 y) #x4b000000) - (set! (-> (the-as (inline-array vector4w) row-data) 0 z) #x4b000000) - (set! (-> (the-as (inline-array vector4w) row-data) 0 w) #x4b000000) + (set-vector! (-> (the-as (inline-array vector4w) row-data) 0) #x4b000000 #x4b000000 #x4b000000 #x4b000000) (set! (-> (the-as (pointer vif-tag) row-data) 4) (new 'static 'vif-tag :cmd (vif-cmd base))) (set! (-> (the-as (pointer vif-tag) row-data) 5) (new 'static 'vif-tag :imm #x2c :cmd (vif-cmd offset))) (set! (-> (the-as (pointer vif-tag) row-data) 6) (new 'static 'vif-tag :cmd (vif-cmd stmod))) @@ -1776,32 +1718,15 @@ "Append the TIE shutdown packets, restoring the normal GS TEST state and clearing the VIF mask, mode, and ROW state changed by tie-init-engine." (when (logtest? *vu1-enable-user* (vu1-renderer-mask tie)) - ;; Restore the ordinary alpha/depth test used after TIE drawing. - (let* ((v1-3 dma-buf) - (direct-packet (the-as dma-packet (-> v1-3 base)))) - (set! (-> direct-packet dma) (new 'static 'dma-tag :qwc #x2 :id (dma-tag-id cnt))) - (set! (-> direct-packet vif0) (new 'static 'vif-tag)) - (set! (-> direct-packet vif1) (new 'static 'vif-tag :imm #x2 :cmd (vif-cmd direct) :msk #x1)) - (set! (-> v1-3 base) (&+ (the-as pointer direct-packet) 16))) - (let* ((v1-4 dma-buf) - (direct-gif (the-as gs-gif-tag (-> v1-4 base)))) - (set! (-> direct-gif tag) (new 'static 'gif-tag64 :nloop #x1 :eop #x1 :nreg #x1)) - (set! (-> direct-gif regs) GIF_REGS_ALL_AD) - (set! (-> v1-4 base) (&+ (the-as pointer direct-gif) 16))) + (dma-buffer-add-cnt-vif2 dma-buf #x2 (new 'static 'vif-tag) (new 'static 'vif-tag :imm #x2 :cmd (vif-cmd direct) :msk #x1)) + (dma-buffer-add-gif-tag dma-buf (new 'static 'gif-tag64 :nloop #x1 :eop #x1 :nreg #x1) GIF_REGS_ALL_AD) (let* ((v1-5 dma-buf) (test-data (-> v1-5 base))) (set! (-> (the-as (pointer gs-test) test-data) 0) (new 'static 'gs-test :atst (gs-atest not-equal) :zte #x1 :ztst (gs-ztest greater-equal))) (set! (-> (the-as (pointer gs-reg64) test-data) 1) (gs-reg64 test-1)) (set! (-> v1-5 base) (&+ test-data 16))) - ;; Clear the VIF mask, run the empty entry 4 to finish the VU invocation, and restore the - ;; default mode and all-zero ROW register for the following renderer. - (let* ((v1-6 dma-buf) - (mask-packet (the-as dma-packet (-> v1-6 base)))) - (set! (-> mask-packet dma) (new 'static 'dma-tag :qwc #x2 :id (dma-tag-id cnt))) - (set! (-> mask-packet vif0) (new 'static 'vif-tag :cmd (vif-cmd stmask))) - (set! (-> mask-packet vif1) (new 'static 'vif-tag)) - (set! (-> v1-6 base) (&+ (the-as pointer mask-packet) 16))) + (dma-buffer-add-cnt-vif2 dma-buf #x2 (new 'static 'vif-tag :cmd (vif-cmd stmask)) (new 'static 'vif-tag)) (let* ((v1-7 dma-buf) (reset-data (the-as (pointer uint32) (-> v1-7 base)))) (set! (-> (the-as (pointer vif-tag) reset-data) 0) (new 'static 'vif-tag :imm #x4 :cmd (vif-cmd mscalf) :msk #x1)) diff --git a/goal_src/jak1/engine/util/sync-info.gc b/goal_src/jak1/engine/util/sync-info.gc index 13e548b60b..707cb97344 100644 --- a/goal_src/jak1/engine/util/sync-info.gc +++ b/goal_src/jak1/engine/util/sync-info.gc @@ -9,7 +9,7 @@ (defmethod setup-params! ((this sync-info) (period uint) (phase float) (ease-out float) (ease-in float)) "Set the period in 300 Hz game-time ticks and the initial phase as a fraction of that period. - The base clock ignores ease-out and ease-in, which are retained for the shared subtype interface." + The base sync-info class ignores ease-out and ease-in." (set! (-> this period) period) (let* ((period-float (the float period)) (phase-ticks (* phase period-float))) @@ -75,9 +75,8 @@ phase; the period is converted to 300 Hz game-time ticks. Return true when the resource exists, otherwise install the supplied defaults and return false. The base clock ignores default-ease-out and default-ease-in." - (local-vars (sync-tag res-tag)) - (set! sync-tag (new 'static 'res-tag)) - (let ((params (res-lump-data (-> proc entity) 'sync pointer :tag-ptr (& sync-tag)))) + (let* ((sync-tag (new 'static 'res-tag)) + (params (res-lump-data (-> proc entity) 'sync pointer :tag-ptr (& sync-tag)))) (cond (params (setup-params! this @@ -86,53 +85,57 @@ 0.15 0.15) #t) - (else (setup-params! this default-period default-phase 0.15 0.15) #f)))) + (else + (setup-params! this default-period default-phase 0.15 0.15) + #f)))) (defmethod load-params! ((this sync-info-eased) (proc process) (default-period uint) (default-phase float) (default-ease-out float) (default-ease-in float)) "Load the sync resource from proc. Four floats supply period seconds, initial phase, ease-out, and ease-in; a two-value resource uses the supplied easing defaults. Return true when the resource exists, otherwise install every supplied default and return false." - (local-vars (sync-tag res-tag)) - (set! sync-tag (new 'static 'res-tag)) - (let ((params (res-lump-data (-> proc entity) 'sync pointer :tag-ptr (& sync-tag)))) + (let* ((sync-tag (new 'static 'res-tag)) + (params (res-lump-data (-> proc entity) 'sync pointer :tag-ptr (& sync-tag)))) (cond (params (if (>= (-> sync-tag elt-count) (the-as uint 4)) - (setup-params! this - (the-as uint (the int (* 300.0 (-> (the-as (pointer float) params) 0)))) - (-> (the-as (pointer float) params) 1) - (-> (the-as (pointer float) params) 2) - (-> (the-as (pointer float) params) 3)) - (setup-params! this - (the-as uint (the int (* 300.0 (-> (the-as (pointer float) params) 0)))) - (-> (the-as (pointer float) params) 1) - default-ease-out - default-ease-in)) + (setup-params! this + (the-as uint (the int (* 300.0 (-> (the-as (pointer float) params) 0)))) + (-> (the-as (pointer float) params) 1) + (-> (the-as (pointer float) params) 2) + (-> (the-as (pointer float) params) 3)) + (setup-params! this + (the-as uint (the int (* 300.0 (-> (the-as (pointer float) params) 0)))) + (-> (the-as (pointer float) params) 1) + default-ease-out + default-ease-in)) #t) - (else (setup-params! this default-period default-phase default-ease-out default-ease-in) #f)))) + (else + (setup-params! this default-period default-phase default-ease-out default-ease-in) + #f)))) (defmethod load-params! ((this sync-info-paused) (proc process) (default-period uint) (default-phase float) (default-pause-after-out float) (default-pause-after-in float)) "Load the sync resource from proc. Four floats supply period seconds, initial phase, pause-after-out, and pause-after-in; a two-value resource uses the supplied pause defaults. Return true when the resource exists, otherwise install every supplied default and return false." - (local-vars (sync-tag res-tag)) - (set! sync-tag (new 'static 'res-tag)) - (let ((params (res-lump-data (-> proc entity) 'sync pointer :tag-ptr (& sync-tag)))) + (let* ((sync-tag (new 'static 'res-tag)) + (params (res-lump-data (-> proc entity) 'sync pointer :tag-ptr (& sync-tag)))) (cond (params (if (>= (-> sync-tag elt-count) (the-as uint 4)) - (setup-params! this - (the-as uint (the int (* 300.0 (-> (the-as (pointer float) params) 0)))) - (-> (the-as (pointer float) params) 1) - (-> (the-as (pointer float) params) 2) - (-> (the-as (pointer float) params) 3)) - (setup-params! this - (the-as uint (the int (* 300.0 (-> (the-as (pointer float) params) 0)))) - (-> (the-as (pointer float) params) 1) - default-pause-after-out - default-pause-after-in)) + (setup-params! this + (the-as uint (the int (* 300.0 (-> (the-as (pointer float) params) 0)))) + (-> (the-as (pointer float) params) 1) + (-> (the-as (pointer float) params) 2) + (-> (the-as (pointer float) params) 3)) + (setup-params! this + (the-as uint (the int (* 300.0 (-> (the-as (pointer float) params) 0)))) + (-> (the-as (pointer float) params) 1) + default-pause-after-out + default-pause-after-in)) #t) - (else (setup-params! this default-period default-phase default-pause-after-out default-pause-after-in) #f)))) + (else + (setup-params! this default-period default-phase default-pause-after-out default-pause-after-in) + #f)))) (defmethod get-current-phase-no-mod ((this sync-info)) "Return the base clock's wrapped phase from 0 through 1 without applying a subtype's easing diff --git a/test/common/test_demacro.cpp b/test/common/test_demacro.cpp index efda47c7e8..c469d4b738 100644 --- a/test/common/test_demacro.cpp +++ b/test/common/test_demacro.cpp @@ -286,6 +286,57 @@ TEST(Demacro, Jak1RecognizesDmaBucketConstruction) { )"); } +TEST(Demacro, Jak1RecognizesDmaCntPacketsWithExplicitQwc) { + const auto rules = + demacro::load_rules(file_util::get_file_path({"decompiler/config/jak1/demacro.jsonc"})); + const std::string source = R"((begin + (let* ((dma-state dma-buf) + (mask-packet (the-as object (-> dma-state base)))) + (set! (-> (the-as dma-packet mask-packet) dma) + (new 'static 'dma-tag :qwc #x3 :id (dma-tag-id cnt))) + (set! (-> (the-as dma-packet mask-packet) vif0) + (new 'static 'vif-tag :cmd (vif-cmd stmask))) + (set! (-> (the-as dma-packet mask-packet) vif1) (new 'static 'vif-tag)) + (set! (-> dma-state base) (&+ (the-as pointer mask-packet) 16))) + (let* ((dma-state dma-buf) + (packet (the-as dma-packet (-> dma-state base)))) + (set! (-> packet dma) (new 'static 'dma-tag :qwc qwc :id (dma-tag-id cnt))) + (set! (-> packet vif0) vif0) + (set! (-> packet vif1) vif1) + (set! (-> dma-state base) (&+ (the-as pointer packet) 16)))) +)"; + const auto result = demacro::rewrite(source, rules); + EXPECT_EQ(result.rewrite_count(), 2); + EXPECT_EQ(result.source, R"((begin + (dma-buffer-add-cnt-vif2 dma-buf #x3 (new 'static 'vif-tag :cmd (vif-cmd stmask)) (new 'static 'vif-tag)) + (dma-buffer-add-cnt-vif2 dma-buf qwc vif0 vif1)) +)"); +} + +TEST(Demacro, Jak1RecognizesDmaNextPackets) { + const auto rules = + demacro::load_rules(file_util::get_file_path({"decompiler/config/jak1/demacro.jsonc"})); + const std::string source = R"((begin + (let ((packet (the-as object (-> dma-buf base)))) + (set! (-> (the-as dma-packet packet) dma) + (new 'static 'dma-tag :id (dma-tag-id next) :addr next-address)) + (set! (-> (the-as dma-packet packet) vif0) vif0) + (set! (-> (the-as dma-packet packet) vif1) vif1) + (set! (-> dma-buf base) (&+ (the-as pointer packet) 16))) + (let ((packet (the-as dma-packet (-> dma-buf base)))) + (set! (-> packet dma) (new 'static 'dma-tag :id (dma-tag-id next))) + (set! (-> packet vif0) (new 'static 'vif-tag)) + (set! (-> packet vif1) (new 'static 'vif-tag)) + (set! (-> dma-buf base) (&+ (the-as pointer packet) 16)))) +)"; + const auto result = demacro::rewrite(source, rules); + EXPECT_EQ(result.rewrite_count(), 2); + EXPECT_EQ(result.source, R"((begin + (dma-buffer-add-next-vif2 dma-buf 0 next-address vif0 vif1) + (dma-buffer-add-next-vif2 dma-buf 0 0 (new 'static 'vif-tag) (new 'static 'vif-tag))) +)"); +} + TEST(Demacro, Jak1RecognizesInlinedFontEnumSetters) { const auto rules = demacro::load_rules(file_util::get_file_path({"decompiler/config/jak1/demacro.jsonc"}));