/* extrinsrt r1, r2, src, size, dst: replace bits [dst:dst+size) in r1
 *  with bits [src:src+size) in r2
 *
 * bra(n)z annul: no delay slot
 */

/* Bitfield version of NVC0_3D_VERTEX_ARRAY_PER_INSTANCE[].
 * Args: size, bitfield
 */
.section #mme9097_per_instance_bf
   parm $r3
   mov $r2 0x0
   maddr 0x1620
loop:
   mov $r1 (add $r1 -1)
   send (extrshl $r3 $r2 0x1 0x0)
   exit branz $r1 #loop
   mov $r2 (add $r2 0x1)

/* The comments above the macros describe what they *should* be doing,
 * but we use less functionality for now.
 */

/*
 * for (i = 0; i < 8; ++i)
 *    [NVC0_3D_BLEND_ENABLE(i)] = BIT(i of arg);
 *
 * [3428] = arg;
 *
 * if (arg == 0 || [NVC0_3D_MULTISAMPLE_ENABLE] == 0)
 *    [0d9c] = 0;
 * else
 *    [0d9c] = [342c];
 */
.section #mme9097_blend_enables
   maddr 0x14d8
   send (extrinsrt 0x0 $r1 0x0 0x1 0x0)
   send (extrinsrt 0x0 $r1 0x1 0x1 0x0)
   send (extrinsrt 0x0 $r1 0x2 0x1 0x0)
   send (extrinsrt 0x0 $r1 0x3 0x1 0x0)
   send (extrinsrt 0x0 $r1 0x4 0x1 0x0)
   send (extrinsrt 0x0 $r1 0x5 0x1 0x0)
   exit send (extrinsrt 0x0 $r1 0x6 0x1 0x0)
   send (extrinsrt 0x0 $r1 0x7 0x1 0x0)

/*
 * uint64 limit = (parm(0) << 32) | parm(1);
 * uint64 start = (parm(2) << 32);
 *
 * if (limit) {
 *    start |= parm(3);
 *    --limit;
 * } else {
 *    start |= 1;
 * }
 *
 * [0x1c04 + (arg & 0xf) * 16 + 0] = (start >> 32) & 0xff;
 * [0x1c04 + (arg & 0xf) * 16 + 4] = start & 0xffffffff;
 * [0x1f00 + (arg & 0xf) * 8 + 0] = (limit >> 32) & 0xff;
 * [0x1f00 + (arg & 0xf) * 8 + 4] = limit & 0xffffffff;
 */
.section #mme9097_vertex_array_select
   parm $r2
   parm $r3
   parm $r4
   parm $r5
   mov $r6 (extrinsrt 0x0 $r1 0x0 0x4 0x2)
   mov $r7 (extrinsrt 0x0 $r1 0x0 0x4 0x1)
   maddr $r6 (add $r6 0x1701)
   send $r4
   send $r5
   maddr $r7 (add $r7 0x17c0)
   exit send $r2
   send $r3

/*
 * [GL_POLYGON_MODE_FRONT] = arg;
 *
 * if (BIT(31 of [0x3410]))
 *    [1a24] = 0x7353;
 *
 * if ([NVC0_3D_SP_SELECT(3)] == 0x31 || [NVC0_3D_SP_SELECT(4)] == 0x41)
 *    [02ec] = 0;
 * else
 * if ([GL_POLYGON_MODE_BACK] == GL_LINE || arg == GL_LINE)
 *    [02ec] = BYTE(1 of [0x3410]) << 4;
 * else
 *    [02ec] = BYTE(0 of [0x3410]) << 4;
 */
.section #mme9097_poly_mode_front
   read $r2 0x36c
   read $r3 0x830
   mov $r7 (or $r1 $r2)
   read $r4 0x840
   mov $r2 0x1
   mov $r6 0x60
   mov $r7 (and $r7 $r2)
   braz $r7 #locn_0a_pmf
   maddr 0x36b
   mov $r6 0x200
locn_0a_pmf:
   mov $r7 (or $r3 $r4)
   mov $r7 (and $r7 $r2)
   braz $r7 #locn_0f_pmf
   send $r1
   mov $r6 0x0
locn_0f_pmf:
   exit maddr 0xbb
   send $r6

/*
 * [GL_POLYGON_MODE_BACK] = arg;
 *
 * if (BIT(31 of [0x3410]))
 *    [1a24] = 0x7353;
 *
 * if ([NVC0_3D_SP_SELECT(3)] == 0x31 || [NVC0_3D_SP_SELECT(4)] == 0x41)
 *    [02ec] = 0;
 * else
 * if ([GL_POLYGON_MODE_FRONT] == GL_LINE || arg == GL_LINE)
 *    [02ec] = BYTE(1 of [0x3410]) << 4;
 * else
 *    [02ec] = BYTE(0 of [0x3410]) << 4;
 */
/* NOTE: 0x3410 = 0x80002006 by default,
 *  POLYGON_MODE == GL_LINE check replaced by (MODE & 1)
 *  SP_SELECT(i) == (i << 4) | 1 check replaced by SP_SELECT(i) & 1
 */
.section #mme9097_poly_mode_back
   read $r2 0x36b
   read $r3 0x830
   mov $r7 (or $r1 $r2)
   read $r4 0x840
   mov $r2 0x1
   mov $r6 0x60
   mov $r7 (and $r7 $r2)
   braz $r7 #locn_0a_pmb
   maddr 0x36c
   mov $r6 0x200
locn_0a_pmb:
   mov $r7 (or $r3 $r4)
   mov $r7 (and $r7 $r2)
   braz $r7 #locn_0f_pmb
   send $r1
   mov $r6 0x0
locn_0f_pmb:
   exit maddr 0xbb
   send $r6

/*
 * [NVC0_3D_SP_SELECT(4)] = arg
 *
 * if BIT(31 of [0x3410]) == 0
 *    [1a24] = 0x7353;
 *
 * if ([NVC0_3D_SP_SELECT(3)] == 0x31 || arg == 0x41)
 *    [02ec] = 0
 * else
 * if (any POLYGON MODE == LINE)
 *    [02ec] = BYTE(1 of [3410]) << 4;
 * else
 *    [02ec] = BYTE(0 of [3410]) << 4; // 02ec valid bits are 0xff1
 */
.section #mme9097_gp_select
   read $r2 0x36b
   read $r3 0x36c
   mov $r7 (or $r2 $r3)
   read $r4 0x830
   mov $r2 0x1
   mov $r6 0x60
   mov $r7 (and $r7 $r2)
   braz $r7 #locn_0a_gs
   maddr 0x840
   mov $r6 0x200
locn_0a_gs:
   mov $r7 (or $r1 $r4)
   mov $r7 (and $r7 $r2)
   braz $r7 #locn_0f_gs
   send $r1
   mov $r6 0x0
locn_0f_gs:
   exit maddr 0xbb
   send $r6

/*
 * [NVC0_3D_SP_SELECT(3)] = arg
 *
 * if BIT(31 of [0x3410]) == 0
 *    [1a24] = 0x7353;
 *
 * if (arg == 0x31) {
 *    if (BIT(2 of [0x3430])) {
 *       int i = 15; do { --i; } while(i);
 *       [0x1a2c] = 0;
 *    }
 * }
 *
 * if ([NVC0_3D_SP_SELECT(4)] == 0x41 || arg == 0x31)
 *    [02ec] = 0
 * else
 * if ([any POLYGON_MODE] == GL_LINE)
 *    [02ec] = BYTE(1 of [3410]) << 4;
 * else
 *    [02ec] = BYTE(0 of [3410]) << 4;
 */
.section #mme9097_tep_select
   read $r2 0x36b
   read $r3 0x36c
   mov $r7 (or $r2 $r3)
   read $r4 0x840
   mov $r2 0x1
   mov $r6 0x60
   mov $r7 (and $r7 $r2)
   braz $r7 #locn_0a_ts
   maddr 0x830
   mov $r6 0x200
locn_0a_ts:
   mov $r7 (or $r1 $r4)
   mov $r7 (and $r7 $r2)
   braz $r7 #locn_0f_ts
   send $r1
   mov $r6 0x0
locn_0f_ts:
   exit maddr 0xbb
   send $r6

/* NVC0_3D_MACRO_DRAW_ELEMENTS_INDIRECT
 *
 * NOTE: Saves and restores VB_ELEMENT,INSTANCE_BASE.
 * Forcefully sets VERTEX_ID_BASE to the value of VB_ELEMENT_BASE.
 *
 * arg     = mode
 * parm[0] = start_drawid
 * parm[1] = numparams
 * parm[2 + 5n + 0] = count
 * parm[2 + 5n + 1] = instance_count
 * parm[2 + 5n + 2] = start
 * parm[2 + 5n + 3] = index_bias
 * parm[2 + 5n + 4] = start_instance
 *
 * SCRATCH[0] = saved VB_ELEMENT_BASE
 * SCRATCH[1] = saved VB_INSTANCE_BASE
 */
.section #mme9097_draw_elts_indirect
   read $r6 0x50d /* VB_ELEMENT_BASE */
   read $r7 0x50e /* VB_INSTANCE_BASE */
   maddr 0x1d00
   send $r6 /* SCRATCH[0] = VB_ELEMENT_BASE */
   send $r7 /* SCRATCH[1] = VB_INSTANCE_BASE */
   parm $r6 /* start_drawid */
   parm $r7 /* numparams */
dei_draw_again:
   parm $r3 /* count */
   parm $r2 /* instance_count */
   parm $r4 maddr 0x5f7 /* INDEX_BATCH_FIRST, start */
   parm $r4 send $r4 /* index_bias, send start */
   maddr 0x18e3 /* CB_POS */
   send 0x1a0 /* 256 + 160 */
   braz $r2 #dei_end
   parm $r5 send $r4 /* start_instance, send index_bias */
   send $r5 /* send start_instance */
   send $r6 /* draw id */
   maddr 0x150d /* VB_ELEMENT,INSTANCE_BASE */
   send $r4
   send $r5
   maddr 0x446
   send $r4
   mov $r4 0x1
   mov $r1 (extrinsrt $r1 0x0 0 1 26) /* clear INSTANCE_NEXT */
dei_again:
   maddr 0x586 /* VERTEX_BEGIN_GL */
   send $r1 /* mode */
   maddr 0x5f8 /* INDEX_BATCH_COUNT */
   send $r3 /* count */
   mov $r2 (sub $r2 $r4)
   maddrsend 0x585 /* VERTEX_END_GL */
   branz $r2 #dei_again
   mov $r1 (extrinsrt $r1 $r4 0 1 26) /* set INSTANCE_NEXT */
dei_end:
   mov $r7 (add $r7 -1)
   branz $r7 #dei_draw_again
   mov $r6 (add $r6 1)
   read $r6 0xd00
   read $r7 0xd01
   maddr 0x150d /* VB_ELEMENT,INSTANCE_BASE */
   send $r6
   send $r7
   exit maddr 0x446
   send $r6

/* NVC0_3D_MACRO_DRAW_ARRAYS_INDIRECT:
 *
 * NOTE: Saves and restores VB_INSTANCE_BASE.
 *
 * arg     = mode
 * parm[0] = start_drawid
 * parm[1] = numparams
 * parm[2 + 4n + 0] = count
 * parm[2 + 4n + 1] = instance_count
 * parm[2 + 4n + 2] = start
 * parm[2 + 4n + 3] = start_instance
 */
.section #mme9097_draw_arrays_indirect
   read $r5 0x50e /* VB_INSTANCE_BASE */
   parm $r6 /* start_drawid */
   parm $r7 /* numparams */
dai_draw_again:
   parm $r2 /* count */
   parm $r3 /* instance_count */
   parm $r4 maddr 0x35d /* VERTEX_BUFFER_FIRST, start */
   braz $r3 #dai_end
   parm $r4 send $r4 /* start_instance */
   maddr 0x18e3 /* CB_POS */
   send 0x1a0 /* 256 + 160 */
   send 0x0 /* send 0 as base_vertex */
   send $r4 /* send start_instance */
   send $r6 /* draw id */
   maddr 0x50e /* VB_INSTANCE_BASE */
   send $r4
   mov $r4 0x1
   mov $r1 (extrinsrt $r1 0x0 0 1 26) /* clear INSTANCE_NEXT */
dai_again:
   maddr 0x586 /* VERTEX_BEGIN_GL */
   send $r1 /* mode */
   maddr 0x35e /* VERTEX_BUFFER_COUNT */
   send $r2
   mov $r3 (sub $r3 $r4)
   maddrsend 0x585 /* VERTEX_END_GL */
   branz $r3 #dai_again
   mov $r1 (extrinsrt $r1 $r4 0 1 26) /* set INSTANCE_NEXT */
dai_end:
   mov $r7 (add $r7 -1)
   branz $r7 #dai_draw_again
   mov $r6 (add $r6 1)
   exit maddr 0x50e /* VB_INSTANCE_BASE to restore */
   send $r5

/* NVC0_3D_MACRO_DRAW_ELEMENTS_INDIRECT_COUNT
 *
 * NOTE: Saves and restores VB_ELEMENT,INSTANCE_BASE.
 * Forcefully sets VERTEX_ID_BASE to the value of VB_ELEMENT_BASE.
 *
 * arg     = mode
 * parm[0] = start_drawid
 * parm[1] = numparams
 * parm[2] = totaldraws
 * parm[3 + 5n + 0] = count
 * parm[3 + 5n + 1] = instance_count
 * parm[3 + 5n + 2] = start
 * parm[3 + 5n + 3] = index_bias
 * parm[3 + 5n + 4] = start_instance
 *
 * SCRATCH[0] = saved VB_ELEMENT_BASE
 * SCRATCH[1] = saved VB_INSTANCE_BASE
 * SCRATCH[2] = draws left
 */
.section #mme9097_draw_elts_indirect_count
   read $r6 0x50d /* VB_ELEMENT_BASE */
   read $r7 0x50e /* VB_INSTANCE_BASE */
   maddr 0x1d00
   send $r6 /* SCRATCH[0] = VB_ELEMENT_BASE */
   send $r7 /* SCRATCH[1] = VB_INSTANCE_BASE */
   parm $r6 /* start_drawid */
   parm $r7 /* numparams */
   parm $r5 /* totaldraws */
   mov $r5 (sub $r5 $r6) /* draws left */
   braz $r5 #deic_runout
   mov $r3 (extrinsrt 0x0 $r5 31 1 0) /* extract high bit */
   branz $r3 #deic_runout
   send $r5
deic_draw_again:
   parm $r3 /* count */
   parm $r2 /* instance_count */
   parm $r4 maddr 0x5f7 /* INDEX_BATCH_FIRST, start */
   parm $r4 send $r4 /* index_bias, send start */
   maddr 0x18e3 /* CB_POS */
   send 0x1a0 /* 256 + 160 */
   braz $r2 #deic_end
   parm $r5 send $r4 /* start_instance, send index_bias */
   send $r5 /* send start_instance */
   send $r6 /* draw id */
   maddr 0x150d /* VB_ELEMENT,INSTANCE_BASE */
   send $r4
   send $r5
   maddr 0x446
   send $r4
   mov $r4 0x1
   mov $r1 (extrinsrt $r1 0x0 0 1 26) /* clear INSTANCE_NEXT */
deic_again:
   maddr 0x586 /* VERTEX_BEGIN_GL */
   send $r1 /* mode */
   maddr 0x5f8 /* INDEX_BATCH_COUNT */
   send $r3 /* count */
   mov $r2 (sub $r2 $r4)
   maddrsend 0x585 /* VERTEX_END_GL */
   branz $r2 #deic_again
   mov $r1 (extrinsrt $r1 $r4 0 1 26) /* set INSTANCE_NEXT */
deic_end:
   read $r5 0xd02
   mov $r5 (add $r5 -1)
   braz $r5 #deic_runout_check
   mov $r7 (add $r7 -1)
   maddr 0xd02
   send $r5
   branz $r7 #deic_draw_again
   mov $r6 (add $r6 1)
deic_restore:
   read $r6 0xd00
   read $r7 0xd01
   maddr 0x150d /* VB_ELEMENT,INSTANCE_BASE */
   send $r6
   send $r7
   exit maddr 0x446
   send $r6
deic_runout:
   parm $r2
   parm $r2
   parm $r2
   parm $r2
   parm $r2
   mov $r7 (add $r7 -1)
deic_runout_check:
   branz annul $r7 #deic_runout
   bra annul #deic_restore

/* NVC0_3D_MACRO_DRAW_ARRAYS_INDIRECT_COUNT:
 *
 * NOTE: Saves and restores VB_INSTANCE_BASE.
 *
 * arg     = mode
 * parm[0] = start_drawid
 * parm[1] = numparams
 * parm[2] = totaldraws
 * parm[3 + 4n + 0] = count
 * parm[3 + 4n + 1] = instance_count
 * parm[3 + 4n + 2] = start
 * parm[3 + 4n + 3] = start_instance
 *
 * SCRATCH[0] = VB_INSTANCE_BASE
 */
.section #mme9097_draw_arrays_indirect_count
   read $r5 0x50e /* VB_INSTANCE_BASE */
   maddr 0xd00
   parm $r6 send $r5 /* start_drawid, save VB_INSTANCE_BASE */
   parm $r7 /* numparams */
   parm $r5 /* totaldraws */
   mov $r5 (sub $r5 $r6) /* draws left */
   braz $r5 #daic_runout
   mov $r3 (extrinsrt 0x0 $r5 31 1 0) /* extract high bit */
   branz annul $r3 #daic_runout
daic_draw_again:
   parm $r2 /* count */
   parm $r3 /* instance_count */
   parm $r4 maddr 0x35d /* VERTEX_BUFFER_FIRST, start */
   braz $r3 #daic_end
   parm $r4 send $r4 /* start_instance */
   maddr 0x18e3 /* CB_POS */
   send 0x1a0 /* 256 + 160 */
   send 0x0 /* send 0 as base_vertex */
   send $r4 /* send start_instance */
   send $r6 /* draw id */
   maddr 0x50e /* VB_INSTANCE_BASE */
   send $r4
   mov $r4 0x1
   mov $r1 (extrinsrt $r1 0x0 0 1 26) /* clear INSTANCE_NEXT */
daic_again:
   maddr 0x586 /* VERTEX_BEGIN_GL */
   send $r1 /* mode */
   maddr 0x35e /* VERTEX_BUFFER_COUNT */
   send $r2
   mov $r3 (sub $r3 $r4)
   maddrsend 0x585 /* VERTEX_END_GL */
   branz $r3 #daic_again
   mov $r1 (extrinsrt $r1 $r4 0 1 26) /* set INSTANCE_NEXT */
daic_end:
   mov $r5 (add $r5 -1)
   braz $r5 #daic_runout_check
   mov $r7 (add $r7 -1)
   branz $r7 #daic_draw_again
   mov $r6 (add $r6 1)
daic_restore:
   read $r5 0xd00
   exit maddr 0x50e /* VB_INSTANCE_BASE to restore */
   send $r5
daic_runout:
   parm $r2
   parm $r2
   parm $r2
   parm $r2
   mov $r7 (add $r7 -1)
daic_runout_check:
   branz annul $r7 #daic_runout
   bra annul #daic_restore

/* NVC0_3D_MACRO_QUERY_BUFFER_WRITE:
 *
 * This is a combination macro for all of our query buffer object needs.
 * It has the option to clamp results to a configurable amount, as well as
 * to write out one or two words.
 *
 * We use the query engine to write out the values, and expect the query
 * address to point to the right place.
 *
 * arg = clamp value (0 means unclamped). clamped means just 1 written value.
 * parm[0] = LSB of end value
 * parm[1] = MSB of end value
 * parm[2] = LSB of start value
 * parm[3] = MSB of start value
 * parm[4] = desired sequence
 * parm[5] = actual sequence
 * parm[6] = query high address
 * parm[7] = query low address
 */
.section #mme9097_query_buffer_write
   parm $r2
   parm $r3
   parm $r4
   parm $r5 maddr 0x16c0 /* QUERY_ADDRESS_HIGH */
   parm $r6
   parm $r7
   mov $r6 (sub $r7 $r6) /* actual - desired */
   mov $r6 (sbb 0x0 0x0) /* if there was underflow, not reached yet */
   parm $r7
   exit braz $r6 #qbw_ready
   parm $r6
qbw_ready:
   mov $r2 (sub $r2 $r4)
   braz $r1 #qbw_postclamp
   mov $r3 (sbb $r3 $r5)
   branz annul $r3 #qbw_clamp
   mov $r4 (sub $r1 $r2)
   mov $r4 (sbb 0x0 0x0)
   braz annul $r4 #qbw_postclamp
qbw_clamp:
   mov $r2 $r1
qbw_postclamp:
   send $r7
   send $r6
   send $r2
   branz $r1 #qbw_done
   mov $r4 0x1000
   send (extrinsrt 0x0 $r4 0x0 0x10 0x10)
   maddr 0x16c0 /* QUERY_ADDRESS_HIGH */
   mov $r5 0x4
   mov $r6 (add $r6 $r5)
   mov $r7 (adc $r7 0x0)
   send $r7
   send $r6
   send $r3
qbw_done:
   exit send (extrinsrt 0x0 $r4 0x0 0x10 0x10)
   maddrsend 0x44