1 /*---------------------------------------------------------------------------*
2   Project:  Dolphin GD library
3   File:     GDTexture.c
4 
5   Copyright 2001- 2006 Nintendo. All rights reserved.
6 
7   These coded instructions, statements, and computer programs contain
8   proprietary information of Nintendo of America Inc. and/or Nintendo
9   Company Ltd., and are protected by Federal copyright law.  They may
10   not be disclosed to third parties or copied or duplicated in any form,
11   in whole or in part, without the prior written consent of Nintendo.
12 
13   $Log: GDTexture.c,v $
14   Revision 1.3  2006/02/20 04:24:39  mitu
15   Changed include path from dolphin/ to revolution/.
16 
17   Revision 1.2  2006/02/03 08:54:38  hirose
18   Avoided use of EPPC and use WIN32.
19 
20   Revision 1.1.1.1  2005/05/12 02:15:49  yasuh-to
21   Ported from dolphin source tree.
22 
23 
24     7     2001/10/30 2:11p Hirose
25     Fixed time stamp problem.
26 
27     7     2002/10/17 4:37p Hirose
28     Fixed image type bit setting of GDSetTexPreLoaded().
29 
30     6     2001/10/13 2:27a Hirose
31     Changed GDSetTexCoordScale function.
32 
33     5     2001/10/10 12:06p Carl
34     Fixed filter conversion table.
35 
36     4     2001/10/04 4:46p Carl
37     Added GDLoadTlut, etc.
38 
39     3     10/02/01 5:16p Carl
40     Fixed bug in GDSetTexTlut.
41 
42     2     2001/09/14 6:43p Carl
43     Made OSCachedToPhysical a macro for non-EPPC.
44 
45     1     2001/09/12 1:52p Carl
46     Initial revision of GD: Graphics Display List Library.
47 
48   $NoKeywords: $
49  *---------------------------------------------------------------------------*/
50 
51 #include <revolution/gd.h>
52 #include <revolution/os.h>
53 
54 #if defined(WIN32) && !defined(OSCachedToPhysical)
55 #define OSCachedToPhysical(caddr) ((u32)((u8*)(caddr) - (0x80000000)))
56 #endif
57 
58 /*---------------------------------------------------------------------------*/
59 
60 // The table below is convenient for using the GX default TLUT allocation.
61 // It defines TMEM addresses for 20 TLUT's:
62 // - the first 16 TLUT's are 256 entry
63 // - the final  4 TLUT's are 1024 entry
64 
65 #ifdef DEFINE_TLUT_TABLE
66 #define GX_TMEM_HI  0x80000
67 #define GX_32k      0x08000
68 #define GX_8k       0x02000
69 u32 GXTlutRegions[20] = {
70     GX_TMEM_HI + GX_32k*8 + GX_8k*0,
71     GX_TMEM_HI + GX_32k*8 + GX_8k*1,
72     GX_TMEM_HI + GX_32k*8 + GX_8k*2,
73     GX_TMEM_HI + GX_32k*8 + GX_8k*3,
74     GX_TMEM_HI + GX_32k*8 + GX_8k*4,
75     GX_TMEM_HI + GX_32k*8 + GX_8k*5,
76     GX_TMEM_HI + GX_32k*8 + GX_8k*6,
77     GX_TMEM_HI + GX_32k*8 + GX_8k*7,
78     GX_TMEM_HI + GX_32k*8 + GX_8k*8,
79     GX_TMEM_HI + GX_32k*8 + GX_8k*9,
80     GX_TMEM_HI + GX_32k*8 + GX_8k*10,
81     GX_TMEM_HI + GX_32k*8 + GX_8k*11,
82     GX_TMEM_HI + GX_32k*8 + GX_8k*12,
83     GX_TMEM_HI + GX_32k*8 + GX_8k*13,
84     GX_TMEM_HI + GX_32k*8 + GX_8k*14,
85     GX_TMEM_HI + GX_32k*8 + GX_8k*15,
86     GX_TMEM_HI + GX_32k*8 + GX_8k*16 + GX_32k*0,
87     GX_TMEM_HI + GX_32k*8 + GX_8k*16 + GX_32k*1,
88     GX_TMEM_HI + GX_32k*8 + GX_8k*16 + GX_32k*2,
89     GX_TMEM_HI + GX_32k*8 + GX_8k*16 + GX_32k*3,
90 };
91 #endif
92 
93 // The tables below are used in the library code to map texmap ID's
94 // to register addresses.  If you are linking with GX, you can use
95 // the same tables that are defined within GX.
96 
97 #ifndef LINKING_WITH_GX
98 
99 u8 GDTexMode0Ids[8] = {
100     TX_SETMODE0_I0_ID,
101     TX_SETMODE0_I1_ID,
102     TX_SETMODE0_I2_ID,
103     TX_SETMODE0_I3_ID,
104     TX_SETMODE0_I4_ID,
105     TX_SETMODE0_I5_ID,
106     TX_SETMODE0_I6_ID,
107     TX_SETMODE0_I7_ID,
108 };
109 
110 u8 GDTexMode1Ids[8] = {
111     TX_SETMODE1_I0_ID,
112     TX_SETMODE1_I1_ID,
113     TX_SETMODE1_I2_ID,
114     TX_SETMODE1_I3_ID,
115     TX_SETMODE1_I4_ID,
116     TX_SETMODE1_I5_ID,
117     TX_SETMODE1_I6_ID,
118     TX_SETMODE1_I7_ID,
119 };
120 
121 u8 GDTexImage0Ids[8] = {
122     TX_SETIMAGE0_I0_ID,
123     TX_SETIMAGE0_I1_ID,
124     TX_SETIMAGE0_I2_ID,
125     TX_SETIMAGE0_I3_ID,
126     TX_SETIMAGE0_I4_ID,
127     TX_SETIMAGE0_I5_ID,
128     TX_SETIMAGE0_I6_ID,
129     TX_SETIMAGE0_I7_ID,
130 };
131 
132 u8 GDTexImage1Ids[8] = {
133     TX_SETIMAGE1_I0_ID,
134     TX_SETIMAGE1_I1_ID,
135     TX_SETIMAGE1_I2_ID,
136     TX_SETIMAGE1_I3_ID,
137     TX_SETIMAGE1_I4_ID,
138     TX_SETIMAGE1_I5_ID,
139     TX_SETIMAGE1_I6_ID,
140     TX_SETIMAGE1_I7_ID,
141 };
142 
143 u8 GDTexImage2Ids[8] = {
144     TX_SETIMAGE2_I0_ID,
145     TX_SETIMAGE2_I1_ID,
146     TX_SETIMAGE2_I2_ID,
147     TX_SETIMAGE2_I3_ID,
148     TX_SETIMAGE2_I4_ID,
149     TX_SETIMAGE2_I5_ID,
150     TX_SETIMAGE2_I6_ID,
151     TX_SETIMAGE2_I7_ID,
152 };
153 
154 u8 GDTexImage3Ids[8] = {
155     TX_SETIMAGE3_I0_ID,
156     TX_SETIMAGE3_I1_ID,
157     TX_SETIMAGE3_I2_ID,
158     TX_SETIMAGE3_I3_ID,
159     TX_SETIMAGE3_I4_ID,
160     TX_SETIMAGE3_I5_ID,
161     TX_SETIMAGE3_I6_ID,
162     TX_SETIMAGE3_I7_ID,
163 };
164 
165 u8 GDTexTlutIds[8] = {
166     TX_SETTLUT_I0_ID,
167     TX_SETTLUT_I1_ID,
168     TX_SETTLUT_I2_ID,
169     TX_SETTLUT_I3_ID,
170     TX_SETTLUT_I4_ID,
171     TX_SETTLUT_I5_ID,
172     TX_SETTLUT_I6_ID,
173     TX_SETTLUT_I7_ID,
174 };
175 
176 u8 GD2HWFiltConv[] = {
177     0, // TX_MIN_NEAREST,                     // GX_NEAR,
178     4, // TX_MIN_LINEAR,                      // GX_LINEAR,
179     1, // TX_MIN_NEAREST_MIPMAP_NEAREST,      // GX_NEAR_MIP_NEAR,
180     5, // TX_MIN_LINEAR_MIPMAP_NEAREST,       // GX_LIN_MIP_NEAR,
181     2, // TX_MIN_NEAREST_MIPMAP_LINEAR,       // GX_NEAR_MIP_LIN,
182     6, // TX_MIN_LINEAR_MIPMAP_LINEAR,        // GX_LIN_MIP_LIN
183 };
184 
185 #else
186 
187 // If we're linking with GX, no need to duplicate these arrays; just use GX's values.
188 
189 #define GDTexMode0Ids  GXTexMode0Ids
190 #define GDTexMode1Ids  GXTexMode1Ids
191 #define GDTexImage0Ids GXTexImage0Ids
192 #define GDTexImage1Ids GXTexImage1Ids
193 #define GDTexImage2Ids GXTexImage2Ids
194 #define GDTexImage3Ids GXTexImage3Ids
195 #define GDTexTlutIds   GXTexTlutIds
196 #define GD2HWFiltConv  GX2HWFiltConv
197 
198 #endif
199 
200 /*---------------------------------------------------------------------------*/
201 //  Name:         GDSetTexLookupMode
202 //
203 //  Description:  Sets various texture lookup attributes for a given tex ID.
204 //
205 //  Arguments:    id:    	which texture ID will be set
206 //                ...:   	parameters to set, as indicated by name
207 //
208 //  Returns:      none
209 //
210 /*---------------------------------------------------------------------------*/
211 
GDSetTexLookupMode(GXTexMapID id,GXTexWrapMode wrap_s,GXTexWrapMode wrap_t,GXTexFilter min_filt,GXTexFilter mag_filt,f32 min_lod,f32 max_lod,f32 lod_bias,GXBool bias_clamp,GXBool do_edge_lod,GXAnisotropy max_aniso)212 void GDSetTexLookupMode ( GXTexMapID id,
213                           GXTexWrapMode wrap_s,
214                           GXTexWrapMode wrap_t,
215                           GXTexFilter min_filt,
216                           GXTexFilter mag_filt,
217                           f32 min_lod,
218                           f32 max_lod,
219                           f32 lod_bias,
220                           GXBool bias_clamp,
221                           GXBool do_edge_lod,
222                           GXAnisotropy max_aniso )
223 {
224     GDWriteBPCmd(TX_SETMODE0(wrap_s,
225                              wrap_t,
226                              (mag_filt == GX_LINEAR),
227                              GD2HWFiltConv[min_filt],
228                              !do_edge_lod,
229                              (u8)((s8)(lod_bias*32.0f)),
230                              max_aniso,
231                              bias_clamp,
232                              GDTexMode0Ids[id]));
233     GDWriteBPCmd(TX_SETMODE1((u8)(min_lod*16.0f),
234                              (u8)(max_lod*16.0f),
235                              GDTexMode1Ids[id]));
236 }
237 
238 /*---------------------------------------------------------------------------*/
239 //  Name:         GDSetTexImgAttr
240 //
241 //  Description:  Sets image attributes for a given tex ID.
242 //
243 //  Arguments:    id:    	which texture ID will be set
244 //                ...:   	parameters to set, as indicated by name
245 //
246 //  Returns:      none
247 //
248 /*---------------------------------------------------------------------------*/
249 
GDSetTexImgAttr(GXTexMapID id,u16 width,u16 height,GXTexFmt format)250 void GDSetTexImgAttr ( GXTexMapID id, u16 width, u16 height, GXTexFmt format )
251 {
252     GDWriteBPCmd(TX_SETIMAGE0(width - 1,
253                               height - 1,
254                               format,
255                               GDTexImage0Ids[id]));
256 }
257 
258 /*---------------------------------------------------------------------------*/
259 //  Name:         GDSetTexImgPtr
260 //
261 //  Description:  Sets main-memory address for a given tex ID.
262 //
263 //  Arguments:    id:        	which texture ID will be set
264 //                image_ptr:	 (cached) main-memory address of texture
265 //
266 //  Returns:      	none
267 //
268 /*---------------------------------------------------------------------------*/
269 
GDSetTexImgPtr(GXTexMapID id,void * image_ptr)270 void GDSetTexImgPtr ( GXTexMapID id, void *image_ptr )
271 {
272     GDWriteBPCmd(TX_SETIMAGE3(OSCachedToPhysical(image_ptr)>>5,
273                               GDTexImage3Ids[id]));
274 }
275 
276 /*---------------------------------------------------------------------------*/
277 //  Name:         GDSetTexImgPtrRaw
278 //
279 //  Description:  Sets main-memory address for a given tex ID.
280 //                Doesn't do any modification of the ptr provided.
281 //
282 //  Arguments:    id:             	which texture ID will be set
283 //                image_ptr_raw:  	a u24 parameter to be inserted where the
284 //                               HW-format address would otherwise go.
285 //
286 //  Returns:      none
287 //
288 /*---------------------------------------------------------------------------*/
289 
GDSetTexImgPtrRaw(GXTexMapID id,u32 image_ptr_raw)290 void GDSetTexImgPtrRaw ( GXTexMapID id, u32 image_ptr_raw )
291 {
292     GDWriteBPCmd(TX_SETIMAGE3(image_ptr_raw, GDTexImage3Ids[id]));
293 }
294 
295 /*---------------------------------------------------------------------------*/
296 //  Name:         GDPatchTexImgPtr
297 //
298 //  Description:  Alters texture memory address for a GDSetTexImgAddr command.
299 //                We assume that the DL offset has been moved to the point
300 //                BP_DATA_OFFSET bytes after where the GDSetTexImgAddr command
301 //                was inserted.  Only the texture address is changed.
302 //
303 //  Arguments:    image_ptr:	     (cached) main-memory address of texture
304 //
305 //  Returns:      none
306 //
307 /*---------------------------------------------------------------------------*/
308 
GDPatchTexImgPtr(void * image_ptr)309 void GDPatchTexImgPtr ( void *image_ptr )
310 {
311     // Note: offset MUST be pointing to the 1st byte of register data
312 
313     GDWrite_u24(OSCachedToPhysical(image_ptr)>>5); // write texture address
314 }
315 
316 /*---------------------------------------------------------------------------*/
317 //  Name:         GDSetTexCached
318 //
319 //  Description:  Indicates that a texture is cached; sets cache parameters.
320 //
321 //  Arguments:    id:    	which texture ID will be set
322 //                ...:   	parameters to set, as indicated by name
323 //
324 //  Returns:      none
325 //
326 /*---------------------------------------------------------------------------*/
327 
GDSetTexCached(GXTexMapID id,u32 tmem_even,GXTexCacheSize size_even,u32 tmem_odd,GXTexCacheSize size_odd)328 void GDSetTexCached ( GXTexMapID id,
329                       u32 tmem_even, GXTexCacheSize size_even,
330                       u32 tmem_odd,  GXTexCacheSize size_odd )
331 {
332     GDWriteBPCmd(TX_SETIMAGE1( tmem_even >> 5,
333                                size_even+3,
334                                size_even+3,
335                                0,
336                                GDTexImage1Ids[id]));
337     if (size_odd != GX_TEXCACHE_NONE && tmem_odd < GX_TMEM_MAX)
338     {
339         GDWriteBPCmd(TX_SETIMAGE2( tmem_odd >> 5,
340                                    size_odd+3,
341                                    size_odd+3,
342                                    GDTexImage2Ids[id]));
343     }
344 }
345 
346 /*---------------------------------------------------------------------------*/
347 //  Name:         GDSetTexPreLoaded
348 //
349 //  Description:  Indicates that a texture is preloaded; sets TMEM parameters.
350 //
351 //  Arguments:    id:    	which texture ID will be set
352 //                ...:   	parameters to set, as indicated by name
353 //
354 //  Returns:      none
355 //
356 /*---------------------------------------------------------------------------*/
357 
GDSetTexPreLoaded(GXTexMapID id,u32 tmem_even,u32 tmem_odd)358 void GDSetTexPreLoaded ( GXTexMapID id, u32 tmem_even, u32 tmem_odd )
359 {
360     GDWriteBPCmd(TX_SETIMAGE1( tmem_even >> 5,
361                                0,
362                                0,
363                                1,
364                                GDTexImage1Ids[id]));
365     if (tmem_odd < GX_TMEM_MAX) // need to define this!
366     {
367         GDWriteBPCmd(TX_SETIMAGE2( tmem_odd >> 5,
368                                    0,
369                                    0,
370                                    GDTexImage2Ids[id]));
371     }
372 }
373 
374 /*---------------------------------------------------------------------------*/
375 //  Name:         GDSetTexTlut
376 //
377 //  Description:  Sets TLUT parameters for a given tex ID.
378 //
379 //  Arguments:    id:    	which texture ID will be set
380 //                ...:   	parameters to set, as indicated by name
381 //
382 //  Returns:      none
383 //
384 /*---------------------------------------------------------------------------*/
385 
GDSetTexTlut(GXTexMapID id,u32 tmem_addr,GXTlutFmt format)386 void GDSetTexTlut ( GXTexMapID id, u32 tmem_addr, GXTlutFmt format )
387 {
388     GDWriteBPCmd(TX_SETTLUT( (tmem_addr-GX_TMEM_HALF) >> 9, format,
389                              GDTexTlutIds[id]));
390 }
391 
392 /*---------------------------------------------------------------------------*/
393 //  Name:         GDSetTexCoordScale
394 //
395 //  Description:  Sets texture scale parameters for a given texcoord (not tex!)
396 //                ID.  Uses the register mask to avoid changing the texture
397 //                offset enable bits.
398 //
399 //  Arguments:    coord:     	which texcoord ID will be set
400 //                ...:       	parameters to set, as indicated by name
401 //
402 //  Returns:      none
403 //
404 /*---------------------------------------------------------------------------*/
405 
406 #define SU_TS0_MASK_SETTEXCOORDSCALE \
407     ( 0x00FFFF << SU_TS0_SSIZE_SHIFT )
408 #define SU_TS1_MASK_SETTEXCOORDSCALE \
409     ( 0x00FFFF << SU_TS1_TSIZE_SHIFT )
410 
GDSetTexCoordScale(GXTexCoordID coord,u16 s_scale,u16 t_scale)411 void GDSetTexCoordScale ( GXTexCoordID coord, u16 s_scale, u16 t_scale )
412 {
413     // mask prevents write to bias, wrap and point/line offset enables
414     GDWriteBPCmd(SS_MASK(SU_TS0_MASK_SETTEXCOORDSCALE));
415     GDWriteBPCmd(SU_TS0( s_scale - 1, 0, 0, 0, 0,
416                          SU_SSIZE0_ID + (u32) coord * 2));
417 
418     GDWriteBPCmd(SS_MASK(SU_TS1_MASK_SETTEXCOORDSCALE));
419     GDWriteBPCmd(SU_TS1( t_scale - 1, 0, 0,
420                          SU_TSIZE0_ID + (u32) coord * 2));
421 }
422 
423 /*---------------------------------------------------------------------------*/
424 //  Name:         GDSetTexCoordScale2
425 //
426 //  Description:  Sets texture scale parameters for a given texcoord (not tex!)
427 //                ID. Besides, this API sets texcoord bias and cycle wrap mode
428 //                as well.
429 //
430 //  Arguments:    coord:     	which texcoord ID will be set
431 //                ...:       	parameters to set, as indicated by name
432 //
433 //  Returns:      none
434 //
435 /*---------------------------------------------------------------------------*/
436 
437 #define SU_TS0_MASK_SETTEXCOORDSCALE2 \
438     (( 0x00FFFF << SU_TS0_SSIZE_SHIFT ) | \
439      ( 0x000001 << SU_TS0_BS_SHIFT )    | \
440      ( 0x000001 << SU_TS0_WS_SHIFT ))
441 
GDSetTexCoordScale2(GXTexCoordID coord,u16 s_scale,GXBool s_bias,GXBool s_wrap,u16 t_scale,GXBool t_bias,GXBool t_wrap)442 void GDSetTexCoordScale2 ( GXTexCoordID coord,
443                            u16 s_scale, GXBool s_bias, GXBool s_wrap,
444                            u16 t_scale, GXBool t_bias, GXBool t_wrap)
445 {
446     // mask prevents write to point & line offset enables
447     GDWriteBPCmd(SS_MASK(SU_TS0_MASK_SETTEXCOORDSCALE2));
448     GDWriteBPCmd(SU_TS0( s_scale - 1, s_bias, s_wrap, 0, 0,
449                          SU_SSIZE0_ID + (u32) coord * 2));
450     GDWriteBPCmd(SU_TS1( t_scale - 1, t_bias, t_wrap,
451                          SU_TSIZE0_ID + (u32) coord * 2));
452 }
453 
454 
455 /*---------------------------------------------------------------------------*/
456 //  Name:         GDSetTexCoordScaleandTOEs
457 //
458 //  Description:  Sets texture scale parameters for a given texcoord (not tex!)
459 //                ID.  Also sets the texture offset enable bits (TOEs).
460 //
461 //  Arguments:    coord:     	which texcoord ID will be set
462 //                ...:       	parameters to set, as indicated by name
463 //
464 //  Returns:      none
465 //
466 /*---------------------------------------------------------------------------*/
467 
GDSetTexCoordScaleAndTOEs(GXTexCoordID coord,u16 s_scale,GXBool s_bias,GXBool s_wrap,u16 t_scale,GXBool t_bias,GXBool t_wrap,GXBool line_offset,GXBool point_offset)468 void GDSetTexCoordScaleAndTOEs ( GXTexCoordID coord,
469                                  u16 s_scale, GXBool s_bias, GXBool s_wrap,
470                                  u16 t_scale, GXBool t_bias, GXBool t_wrap,
471                                  GXBool line_offset, GXBool point_offset)
472 {
473     GDWriteBPCmd(SU_TS0( s_scale - 1, s_bias, s_wrap, line_offset, point_offset,
474                          SU_SSIZE0_ID + (u32) coord * 2));
475     GDWriteBPCmd(SU_TS1( t_scale - 1, t_bias, t_wrap,
476                          SU_TSIZE0_ID + (u32) coord * 2));
477 }
478 
479 /*---------------------------------------------------------------------------*/
480 //  Name:         GDLoadTlut
481 //
482 //  Description:  Loads a TLUT from main memory into TMEM.
483 //
484 //  Arguments:    tlut_ptr  : (cached) main-memory address of TLUT
485 //                tmem_addr : destination address of TLUT in TMEM
486 //                size      : size of the TLUT
487 //
488 //  Returns:      none
489 //
490 //  Notes:        For patching, you must save the following offset:
491 //                GDGetCurrOffset() + BP_CMD_LENGTH * 2 + BP_DATA_OFFSET
492 //
493 /*---------------------------------------------------------------------------*/
494 
GDLoadTlut(void * tlut_ptr,u32 tmem_addr,GXTlutSize size)495 void GDLoadTlut( void *tlut_ptr, u32 tmem_addr, GXTlutSize size )
496 {
497     ASSERTMSG((tmem_addr & 0x1ff)==0, "GDLoadTlut: invalid TMEM pointer");
498     ASSERTMSG((size <= 1024), "GDLoadTlut: invalid TLUT size");
499 
500     // Flush the texture state (without modifying the indirect mask)
501     GDWriteBPCmd(SS_MASK(0xffff00));
502     GDWriteBPCmd((BU_IMASK_ID << 24));
503 
504     GDWriteBPCmd(TX_LOADTLUT0( OSCachedToPhysical(tlut_ptr)>>5,
505                              TX_LOADTLUT0_ID));
506 
507     // Writing to this register will initiate the actual loading:
508     GDWriteBPCmd(TX_LOADTLUT1( (tmem_addr-GX_TMEM_HALF) >> 9, size,
509                              TX_LOADTLUT1_ID));
510 
511     // Flush the texture state (without modifying the indirect mask)
512     GDWriteBPCmd(SS_MASK(0xffff00));
513     GDWriteBPCmd((BU_IMASK_ID << 24));
514 }
515 
516 /*---------------------------------------------------------------------------*/
517 //  Name:         GDLoadTlutRaw
518 //
519 //  Description:  Loads a TLUT from main memory into TMEM.
520 //                Doesn't do any modification of the ptr provided.
521 //
522 //  Arguments:    tlut_ptr_raw : a u24 parameter to be inserted where the
523 //                               HW-format address would otherwise go.
524 //                tmem_addr    : destination address of TLUT in TMEM
525 //                size         : size of the TLUT
526 //
527 //  Returns:      none
528 //
529 //  Notes:        For patching, you must save the following offset:
530 //                GDGetCurrOffset() + BP_CMD_LENGTH * 2 + BP_DATA_OFFSET
531 //
532 /*---------------------------------------------------------------------------*/
533 
GDLoadTlutRaw(u32 tlut_ptr_raw,u32 tmem_addr,GXTlutSize size)534 void GDLoadTlutRaw( u32 tlut_ptr_raw, u32 tmem_addr, GXTlutSize size )
535 {
536     ASSERTMSG((size <= 1024), "GDLoadTlut: invalid TLUT size");
537 
538     // Flush the texture state (without modifying the indirect mask)
539     GDWriteBPCmd(SS_MASK(0xffff00));
540     GDWriteBPCmd((BU_IMASK_ID << 24));
541 
542     GDWriteBPCmd(TX_LOADTLUT0( tlut_ptr_raw, TX_LOADTLUT0_ID));
543 
544     // Writing to this register will initiate the actual loading:
545     GDWriteBPCmd(TX_LOADTLUT1( (tmem_addr-GX_TMEM_HALF) >> 9, size,
546                              TX_LOADTLUT1_ID));
547 
548     // Flush the texture state (without modifying the indirect mask)
549     GDWriteBPCmd(SS_MASK(0xffff00));
550     GDWriteBPCmd((BU_IMASK_ID << 24));
551 }
552