1 /*
2 Copyright (C) 2002 Rice1964
3 
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8 
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 GNU General Public License for more details.
13 
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
17 */
18 #include <stdint.h>
19 #include <algorithm>
20 
21 #include <retro_miscellaneous.h>
22 
23 #include "Combiner.h"
24 #include "Config.h"
25 #include "RenderBase.h"
26 
27 #include "../../Graphics/image_convert.h"
28 
29 #define ALLOW_USE_TEXTURE_FOR_CONSTANTS
30 
31 static const uint8_t sc_Mux32[32] =
32 {
33     MUX_COMBINED, MUX_TEXEL0,   MUX_TEXEL1, MUX_PRIM,
34     MUX_SHADE,    MUX_ENV,      MUX_1,      MUX_COMBINED|MUX_ALPHAREPLICATE,
35     MUX_TEXEL0|MUX_ALPHAREPLICATE, MUX_TEXEL1|MUX_ALPHAREPLICATE, MUX_PRIM|MUX_ALPHAREPLICATE, MUX_SHADE|MUX_ALPHAREPLICATE,
36     MUX_ENV|MUX_ALPHAREPLICATE, MUX_LODFRAC, MUX_PRIMLODFRAC, MUX_K5,       // Actually k5
37     MUX_UNK, MUX_UNK, MUX_UNK, MUX_UNK,
38     MUX_UNK, MUX_UNK, MUX_UNK, MUX_UNK,
39     MUX_UNK, MUX_UNK, MUX_UNK, MUX_UNK,
40     MUX_UNK, MUX_UNK, MUX_UNK, MUX_0
41 };
42 
43 static const uint8_t sc_Mux16[16] =
44 {
45     MUX_COMBINED, MUX_TEXEL0,   MUX_TEXEL1, MUX_PRIM,
46     MUX_SHADE,    MUX_ENV,      MUX_1,      MUX_COMBALPHA,
47     MUX_TEXEL0|MUX_ALPHAREPLICATE, MUX_TEXEL1|MUX_ALPHAREPLICATE, MUX_PRIM|MUX_ALPHAREPLICATE, MUX_SHADE|MUX_ALPHAREPLICATE,
48     MUX_ENV|MUX_ALPHAREPLICATE, MUX_LODFRAC, MUX_PRIMLODFRAC, MUX_0
49 };
50 static const uint8_t sc_Mux8[8] =
51 {
52     MUX_COMBINED, MUX_TEXEL0,   MUX_TEXEL1, MUX_PRIM,
53     MUX_SHADE,    MUX_ENV,      MUX_1,      MUX_0
54 };
55 
56 
57 const char * translatedCombTypes[] =
58 {
59     "0",
60     "1",
61     "COMBINED",
62     "TEXEL0",
63     "TEXEL1",
64     "PRIM",
65     "SHADE",
66     "ENV",
67     "COMBALPHA",
68     "T0_ALPHA_wrong",
69     "T1_ALPHA_wrong",
70     "PRIM_ALPHA_wrong",
71     "SHADE_ALPHA_wrong",
72     "ENV_ALPHA_wrong",
73     "LODFRAC",
74     "PRIMLODFRAC",
75     "K5",
76     "UNK",
77     "FACTOR_PRIM_MINUS_ENV",
78     "FACTOR_ENV_MINUS_PRIM",
79     "FACTOR_1_MINUS_PRIM",
80     "FACTOR_0_MINUS_PRIM",
81     "FACTOR_1_MINUS_ENV",
82     "FACTOR_0_MINUS_ENV",
83     "FACTOR_1_MINUS_PRIMALPHA",
84     "FACTOR_1_MINUS_ENVALPHA",
85     "FACTOR_HALF",
86     "PRIM_X_PRIMALPHA",
87     "1_MINUS_PRIM_X_ENV_PLUS_PRIM",
88     "ENV_X_PRIM",
89     "PRIM_X_1_MINUS_ENV",
90     "PRIM_X_PRIM",
91     "ENV_X_ENV",
92 };
93 
94 const char* muxTypeStrs[] = {
95     "CM_FMT_TYPE_NOT_USED",
96     "CM_FMT_TYPE1_D",
97     "CM_FMT_TYPE2_A_ADD_D",
98     "CM_FMT_TYPE3_A_MOD_C",
99     "CM_FMT_TYPE4_A_SUB_B",
100     "CM_FMT_TYPE5_A_MOD_C_ADD_D",
101     "CM_FMT_TYPE6_A_LERP_B_C",
102     "CM_FMT_TYPE7_A_SUB_B_ADD_D",
103     "CM_FMT_TYPE8_A_SUB_B_MOD_C",
104     "CM_FMT_TYPE9_A_B_C_D",
105     "CM_FMT_TYPE_NOT_CHECKED",
106 };
107 
Decode(uint32_t dwMux0,uint32_t dwMux1)108 void DecodedMux::Decode(uint32_t dwMux0, uint32_t dwMux1)
109 {
110     m_dwMux0 = dwMux0;
111     m_dwMux1 = dwMux1;
112 
113     aRGB0  = uint8_t((dwMux0>>20)&0x0F);  // c1 c1        // a0
114     bRGB0  = uint8_t((dwMux1>>28)&0x0F);  // c1 c2        // b0
115     cRGB0  = uint8_t((dwMux0>>15)&0x1F);  // c1 c3        // c0
116     dRGB0  = uint8_t((dwMux1>>15)&0x07);  // c1 c4        // d0
117 
118     aA0    = uint8_t((dwMux0>>12)&0x07);  // c1 a1        // Aa0
119     bA0    = uint8_t((dwMux1>>12)&0x07);  // c1 a2        // Ab0
120     cA0    = uint8_t((dwMux0>>9 )&0x07);  // c1 a3        // Ac0
121     dA0    = uint8_t((dwMux1>>9 )&0x07);  // c1 a4        // Ad0
122 
123     aRGB1  = uint8_t((dwMux0>>5 )&0x0F);  // c2 c1        // a1
124     bRGB1  = uint8_t((dwMux1>>24)&0x0F);  // c2 c2        // b1
125     cRGB1  = uint8_t((dwMux0    )&0x1F);  // c2 c3        // c1
126     dRGB1  = uint8_t((dwMux1>>6 )&0x07);  // c2 c4        // d1
127 
128     aA1    = uint8_t((dwMux1>>21)&0x07);  // c2 a1        // Aa1
129     bA1    = uint8_t((dwMux1>>3 )&0x07);  // c2 a2        // Ab1
130     cA1    = uint8_t((dwMux1>>18)&0x07);  // c2 a3        // Ac1
131     dA1    = uint8_t((dwMux1    )&0x07);  // c2 a4        // Ad1
132 
133     //This function will translate the decode mux info further, so we can use
134     //the decode data better.
135     //Will translate A,B,C,D to unified presentation
136     aRGB0  = sc_Mux16[aRGB0];
137     bRGB0  = sc_Mux16[bRGB0];
138     cRGB0  = sc_Mux32[cRGB0];
139     dRGB0  = sc_Mux8[dRGB0];
140 
141     aA0    = sc_Mux8[aA0];
142     bA0    = sc_Mux8[bA0];
143     cA0    = sc_Mux8[cA0];
144     dA0    = sc_Mux8[dA0];
145 
146     aRGB1  = sc_Mux16[aRGB1];
147     bRGB1  = sc_Mux16[bRGB1];
148     cRGB1  = sc_Mux32[cRGB1];
149     dRGB1  = sc_Mux8[dRGB1];
150 
151     aA1    = sc_Mux8[aA1];
152     bA1    = sc_Mux8[bA1];
153     cA1    = sc_Mux8[cA1];
154     dA1    = sc_Mux8[dA1];
155 
156     m_bShadeIsUsed[1] = IsUsedInAlphaChannel(MUX_SHADE, MUX_MASK);
157     m_bShadeIsUsed[0] = IsUsedInColorChannel(MUX_SHADE, MUX_MASK);
158     m_bTexel0IsUsed = IsUsed(MUX_TEXEL0, MUX_MASK);
159     m_bTexel1IsUsed = IsUsed(MUX_TEXEL1, MUX_MASK);
160 
161     m_dwShadeColorChannelFlag = 0;
162     m_dwShadeAlphaChannelFlag = 0;
163     m_ColorTextureFlag[0] = 0;
164     m_ColorTextureFlag[1] = 0;
165 }
166 
Count(uint8_t val,int cycle,uint8_t mask)167 int DecodedMux::Count(uint8_t val, int cycle, uint8_t mask)
168 {
169     uint8_t* pmux = m_bytes;
170     int count=0;
171     int start=0;
172     int end=16;
173 
174     if( cycle >= 0 )
175     {
176         start = cycle*4;
177         end = start+4;
178     }
179 
180 
181     for( int i=start; i<end; i++ )
182     {
183         if( (pmux[i]&mask) == (val&mask) )
184         {
185             count++;
186         }
187     }
188 
189     return count;
190 }
191 
192 
IsUsed(uint8_t val,uint8_t mask)193 bool DecodedMux::IsUsed(uint8_t val, uint8_t mask)
194 {
195     uint8_t* pmux = m_bytes;
196     bool isUsed = false;
197     for( int i=0; i<16; i++ )
198     {
199         if( (pmux[i]&mask) == (val&mask) )
200         {
201             isUsed = true;
202             break;
203         }
204     }
205 
206     return isUsed;
207 }
208 
IsUsedInAlphaChannel(uint8_t val,uint8_t mask)209 bool DecodedMux::IsUsedInAlphaChannel(uint8_t val, uint8_t mask)
210 {
211     uint8_t* pmux = m_bytes;
212     bool isUsed = false;
213     for (int i=0; i<16; i++)
214     {
215         if ((i/4)%2 == 0)
216             continue;   //Don't test color channel
217 
218         if ((pmux[i] & mask) == (val & mask))
219         {
220             isUsed = true;
221             break;
222         }
223     }
224 
225     return isUsed;
226 }
227 
IsUsedInColorChannel(uint8_t val,uint8_t mask)228 bool DecodedMux::IsUsedInColorChannel(uint8_t val, uint8_t mask)
229 {
230     uint8_t* pmux = m_bytes;
231     bool isUsed = false;
232     for (int i=0; i<16; i++)
233     {
234         if ((i/4)%2 == 0 && (pmux[i]&mask) == (val&mask))
235         {
236             isUsed = true;
237             break;
238         }
239     }
240 
241     return isUsed;
242 }
243 
244 
IsUsedInCycle(uint8_t val,int cycle,CombineChannel channel,uint8_t mask)245 bool DecodedMux::IsUsedInCycle(uint8_t val, int cycle, CombineChannel channel, uint8_t mask)
246 {
247     cycle *=2;
248     if (channel == ALPHA_CHANNEL)
249         cycle++;
250 
251     uint8_t* pmux = m_bytes;
252     for (int i=0; i<4; i++)
253     {
254         if ((pmux[i+cycle*4]&mask) == (val&mask))
255         {
256             return true;
257         }
258     }
259 
260     return false;
261 }
262 
IsUsedInCycle(uint8_t val,int cycle,uint8_t mask)263 bool DecodedMux::IsUsedInCycle(uint8_t val, int cycle, uint8_t mask)
264 {
265     return IsUsedInCycle(val, cycle/2, cycle%2?ALPHA_CHANNEL:COLOR_CHANNEL, mask);
266 }
267 
268 
ConvertComplements()269 void DecodedMux::ConvertComplements()
270 {
271     //For (A-B)*C+D, if A=1, then we can convert A-B to Ac-0
272     if( aRGB0 != MUX_1 && bRGB0 != MUX_0 )
273     {
274         aRGB0 = bRGB0|MUX_COMPLEMENT;
275         bRGB0 = MUX_0;
276     }
277     if( aRGB1 != MUX_1 && bRGB1 != MUX_0 )
278     {
279         aRGB1 = bRGB1|MUX_COMPLEMENT;
280         bRGB1 = MUX_0;
281     }
282     if( aA0 != MUX_1 && bA0 != MUX_0 )
283     {
284         aA0 = bA0|MUX_COMPLEMENT;
285         bA0 = MUX_0;
286     }
287     if( aA1 != MUX_1 && bA1 != MUX_0 )
288     {
289         aA1 = bA1|MUX_COMPLEMENT;
290         bA1 = MUX_0;
291     }
292 }
293 
294 
GetCombinerFormatType(uint32_t cycle)295 CombinerFormatType DecodedMux::GetCombinerFormatType(uint32_t cycle)
296 {
297     //Analyze the formula
298     /*
299     C=0                 = D
300     A==B                = D
301     B=0, C=1, D=0       = A
302     A=1, B=0, D=0       = C
303     C=1, B==D           = A
304     A=1, C=1, D=0       = 1-B
305     D = 1               = 1
306     */
307     return CM_FMT_TYPE_D;
308 }
309 
Simplify(void)310 void DecodedMuxForPixelShader::Simplify(void)
311 {
312     CheckCombineInCycle1();
313     //Reformat(true);
314 
315     if( g_curRomInfo.bTexture1Hack )
316     {
317         ReplaceVal(MUX_TEXEL1, MUX_TEXEL0, 2, MUX_MASK);
318         ReplaceVal(MUX_TEXEL1, MUX_TEXEL0, 3, MUX_MASK);
319     }
320     splitType[0] = CM_FMT_TYPE_NOT_USED;
321     splitType[1] = CM_FMT_TYPE_NOT_USED;
322     splitType[2] = CM_FMT_TYPE_NOT_USED;
323     splitType[3] = CM_FMT_TYPE_NOT_USED;
324     mType = CM_FMT_TYPE_NOT_USED;
325 
326     m_bTexel0IsUsed = IsUsed(MUX_TEXEL0, MUX_MASK);
327     m_bTexel1IsUsed = IsUsed(MUX_TEXEL1, MUX_MASK);
328 }
329 
Reset(void)330 void DecodedMuxForSemiPixelShader::Reset(void)
331 {
332     Decode(m_dwMux0, m_dwMux1);
333     splitType[0] = CM_FMT_TYPE_NOT_CHECKED;
334     splitType[1] = CM_FMT_TYPE_NOT_CHECKED;
335     splitType[2] = CM_FMT_TYPE_NOT_CHECKED;
336     splitType[3] = CM_FMT_TYPE_NOT_CHECKED;
337 
338     Hack();
339 
340     gRSP.bProcessDiffuseColor = false;
341     gRSP.bProcessSpecularColor = false;
342 
343     CheckCombineInCycle1();
344     if( g_curRomInfo.bTexture1Hack )
345     {
346         ReplaceVal(MUX_TEXEL1, MUX_TEXEL0, 2, MUX_MASK);
347         ReplaceVal(MUX_TEXEL1, MUX_TEXEL0, 3, MUX_MASK);
348     }
349 
350     m_bTexel0IsUsed = IsUsed(MUX_TEXEL0, MUX_MASK);
351     m_bTexel1IsUsed = IsUsed(MUX_TEXEL1, MUX_MASK);
352 }
353 
Simplify(void)354 void DecodedMuxForOGL14V2::Simplify(void)
355 {
356     CheckCombineInCycle1();
357     if (g_curRomInfo.bTexture1Hack)
358     {
359         ReplaceVal(MUX_TEXEL1, MUX_TEXEL0, 2, MUX_MASK);
360         ReplaceVal(MUX_TEXEL1, MUX_TEXEL0, 3, MUX_MASK);
361     }
362     Reformat(true);
363 
364     UseTextureForConstant();
365     Reformat(true);
366 
367     m_bTexel0IsUsed = IsUsed(MUX_TEXEL0, MUX_MASK);
368     m_bTexel1IsUsed = IsUsed(MUX_TEXEL1, MUX_MASK);
369 }
370 
Simplify(void)371 void DecodedMux::Simplify(void)
372 {
373     CheckCombineInCycle1();
374 
375     if (gRDP.otherMode.text_lod)
376         ConvertLODFracTo0();
377 
378     if (g_curRomInfo.bTexture1Hack)
379     {
380         ReplaceVal(MUX_TEXEL1, MUX_TEXEL0, 2, MUX_MASK);
381         ReplaceVal(MUX_TEXEL1, MUX_TEXEL0, 3, MUX_MASK);
382     }
383     Reformat(true);
384 
385     UseShadeForConstant();
386     Reformat(true);
387 
388     if (m_dwShadeColorChannelFlag == MUX_0)
389     {
390         MergeShadeWithConstants();
391         Reformat(true);
392     }
393 
394 #ifdef ALLOW_USE_TEXTURE_FOR_CONSTANTS
395     UseTextureForConstant();
396     for (int i=0; i<2; i++)
397     {
398         if (m_ColorTextureFlag[i] != 0)
399         {
400             if (m_dwShadeColorChannelFlag == m_ColorTextureFlag[i])
401             {
402                 ReplaceVal(MUX_SHADE, MUX_TEXEL0+i, N64Cycle0RGB, MUX_MASK);
403                 ReplaceVal(MUX_SHADE, MUX_TEXEL0+i, N64Cycle1RGB, MUX_MASK);
404                 m_dwShadeColorChannelFlag = 0;
405             }
406             if (m_dwShadeAlphaChannelFlag == m_ColorTextureFlag[i])
407             {
408                 ReplaceVal(MUX_SHADE, MUX_TEXEL0+i, N64Cycle0Alpha, MUX_MASK);
409                 ReplaceVal(MUX_SHADE, MUX_TEXEL0+i, N64Cycle1Alpha, MUX_MASK);
410                 ReplaceVal(MUX_SHADE|MUX_ALPHAREPLICATE, (MUX_TEXEL0+i)|MUX_ALPHAREPLICATE, N64Cycle0RGB, MUX_MASK_WITH_ALPHA);
411                 ReplaceVal(MUX_SHADE|MUX_ALPHAREPLICATE, (MUX_TEXEL0+i)|MUX_ALPHAREPLICATE, N64Cycle1RGB, MUX_MASK_WITH_ALPHA);
412                 m_dwShadeAlphaChannelFlag = 0;
413             }
414         }
415     }
416     Reformat(true);
417 #endif
418 
419     m_bTexel0IsUsed = IsUsed(MUX_TEXEL0, MUX_MASK);
420     m_bTexel1IsUsed = IsUsed(MUX_TEXEL1, MUX_MASK);
421 }
422 
Reformat(bool do_complement)423 void DecodedMux::Reformat(bool do_complement)
424 {
425     if( m_dWords[N64Cycle0RGB] == m_dWords[N64Cycle1RGB] )
426     {
427         aRGB1 = MUX_0;
428         bRGB1 = MUX_0;
429         cRGB1 = MUX_0;
430         dRGB1 = MUX_COMBINED;
431         splitType[N64Cycle1RGB] = CM_FMT_TYPE_NOT_USED;
432     }
433 
434     if( m_dWords[N64Cycle0Alpha] == m_dWords[N64Cycle1Alpha] )
435     {
436         aA1 = MUX_0;
437         bA1 = MUX_0;
438         cA1 = MUX_0;
439         dA1 = MUX_COMBINED;
440         splitType[N64Cycle1Alpha] = CM_FMT_TYPE_NOT_USED;
441     }
442 
443     for( int i=0; i<4; i++ )
444     {
445         if( splitType[i] == CM_FMT_TYPE_NOT_USED )
446         {
447             continue;   //Skip this, it is not used
448         }
449 
450         N64CombinerType &m = m_n64Combiners[i];
451         //if( m.a == MUX_0 || m.c == MUX_0 || m.a ==  m.b ) m.a = m.b = m.c = MUX_0;
452         if( m.c == MUX_0 || m.a ==  m.b )   m.a = m.b = m.c = MUX_0;
453         if( do_complement && (m.b == MUX_1 || m.d == MUX_1) )  m.a = m.b = m.c = MUX_0;
454         if( m.a == MUX_0 && m.b == m.d )
455         {
456             m.a = m.b;
457             m.b = m.d = 0;
458             //Hack for Mario Tennis
459             if( options.enableHackForGames == HACK_FOR_MARIO_TENNIS && m.c == MUX_TEXEL1 )
460             {
461                 if( do_complement )
462                     m.c = MUX_TEXEL0|MUX_COMPLEMENT;
463                 else
464                 {
465                     m.a = m.c;
466                     m.c = m.b;
467                     m.b = m.a;
468                     m.a = MUX_1;
469                 }
470             }
471             //m.c ^= MUX_COMPLEMENT;
472         }
473 
474         //Type 1    == D
475         //Analyze the formula
476         //Check Type 1
477         //D = 1             = D(=1)
478         //C=0               = D
479         //A==B              = D
480         //B=0, C=1, D=0     = A
481         //C=1, B==D         = A
482         //A=1, B=0, D=0     = C
483         //A=1, C=1, D=0     = 1-B
484 
485         splitType[i] = CM_FMT_TYPE_NOT_CHECKED; //All Type 1 will be changed to = D
486         if( m.c == MUX_0 || m.a==m.b || ( do_complement && (m.d == MUX_1 || m.b==MUX_1)) )
487         {
488             splitType[i] = CM_FMT_TYPE_D;   //All Type 1 will be changed to = D
489             m.a = m.b = m.c = MUX_0;
490             if( m.d == MUX_COMBINED && i>=N64Cycle1RGB )    splitType[i] = CM_FMT_TYPE_NOT_USED;
491         }
492         else if( (m.b == MUX_0 && m.c == MUX_1 && m.d == MUX_0 ) || ( m.c == MUX_1 && m.b==m.d ) )
493         {
494             splitType[i] = CM_FMT_TYPE_D;   //All Type 1 will be changed to = D
495             m.d = m.a;
496             m.a =  m.b = m.c = MUX_0;
497             if( m.d == MUX_COMBINED && i>=N64Cycle1RGB )    splitType[i] = CM_FMT_TYPE_NOT_USED;
498         }
499         else if( m.a == MUX_1 && m.b == MUX_0 && m.d == MUX_0 )
500         {
501             splitType[i] = CM_FMT_TYPE_D;   //All Type 1 will be changed to = D
502             m.d = m.c;
503             m.a =  m.b = m.c = MUX_0;
504             if( m.d == MUX_COMBINED && i>=N64Cycle1RGB )    splitType[i] = CM_FMT_TYPE_NOT_USED;
505         }
506         else if( m.a == MUX_1 && m.c == MUX_1 && m.d == MUX_0 && do_complement )
507         {
508             splitType[i] = CM_FMT_TYPE_D;   //All Type 1 will be changed to = D
509             m.d = m.b^MUX_COMPLEMENT;
510             m.a =  m.b = m.c = MUX_0;
511             if( m.d == MUX_COMBINED && i>=N64Cycle1RGB )    splitType[i] = CM_FMT_TYPE_NOT_USED;
512         }
513 
514         if( splitType[i] == CM_FMT_TYPE_NOT_USED )
515             continue;
516 
517         if( splitType[i] == CM_FMT_TYPE_D )
518         {
519             if( (i == N64Cycle0RGB || i == N64Cycle0Alpha) && splitType[i+2]!=CM_FMT_TYPE_NOT_USED )    //Cycle 1's Color or Alpha
520             {
521                 uint8_t saveD = m.d;
522                 for( int j=0; j<4; j++ )
523                 {
524                     if( (m_bytes[j+i*4+8]&MUX_MASK) == MUX_COMBINED )
525                     {
526                         m_bytes[j+i*4+8] = saveD|(m_bytes[j+i*4+8]&0xC0);   //Replace cycle's CMB with D from cycle 1
527                     }
528                 }
529                 m_dWords[i] = m_dWords[i+2];
530                 splitType[i+2]=CM_FMT_TYPE_NOT_USED;
531                 m_dWords[i+2] = 0x02000000;
532                 i=i-1;      // Throw the first cycle result away, use 2nd cycle for the 1st cycle
533                             // and then redo the 1st cycle
534                 continue;
535             }
536 
537             if( (i==2 || i == 3) && (m.d&MUX_MASK) == MUX_COMBINED )
538             {
539                 splitType[i] = CM_FMT_TYPE_NOT_USED;
540             }
541             continue;
542         }
543 
544 
545         //Type 2: A+D   ' ADD
546         //B=0, C=1          = A+D
547         //A=1, B=0          = C+D
548         splitType[i] = CM_FMT_TYPE_A_ADD_D;         //All Type 2 will be changed to = A+D
549         if( m.b == MUX_0 && m.c == MUX_1 )
550         {
551             if( m.d == MUX_TEXEL0 || m.d == MUX_TEXEL1 )    swapbyte(&m.a, &m.d);
552             if( m.a == MUX_COMBINED ) swapbyte(&m.a, &m.d);
553             continue;
554         }
555 
556         if( m.a == MUX_1 && m.b == MUX_0 )
557         {
558             m.a = m.c;          //Change format A+D
559             m.c = MUX_1;
560             if( m.d == MUX_TEXEL0 || m.d == MUX_TEXEL1 )    swapbyte(&m.a, &m.d);
561             continue;
562         }
563 
564 
565         //Type 3: A*C
566         //B=0, D=0          = A*C
567         //A=1, D=0          = (1-A)*C
568         splitType[i] = CM_FMT_TYPE_A_MOD_C;         //A*C
569         if( m.b == MUX_0 && m.d == MUX_0 )
570         {
571             if( m.c == MUX_TEXEL0 || m.c == MUX_TEXEL1 )    swapbyte(&m.a, &m.c);
572             if( m.a == MUX_COMBINED ) swapbyte(&m.a, &m.c);
573             continue;
574         }
575 
576         if( m.a == MUX_1 && m.d == MUX_0 && do_complement )
577         {
578             m.a = m.b^MUX_COMPLEMENT;
579             m.b = MUX_0;
580             if( m.c == MUX_TEXEL0 || m.c == MUX_TEXEL1 )    swapbyte(&m.a, &m.c);
581             if( m.a == MUX_COMBINED ) swapbyte(&m.a, &m.c);
582             continue;
583         }
584 
585         //Type 4: A-B   ' SUB
586         //C=1, D=0          = A-B
587         splitType[i] = CM_FMT_TYPE_A_SUB_B;         //A-B
588         if( m.c == MUX_1 && m.d == MUX_0 )
589         {
590             continue;
591         }
592 
593         //Type 5: A*C+D , ' MULTIPLYADD
594         //B=0               = A*C+D
595         //A=1               = (1-B) * C + D
596         splitType[i] = CM_FMT_TYPE_A_MOD_C_ADD_D;
597         if( m.b == MUX_0 )
598         {
599             if( m.c == MUX_TEXEL0 || m.c == MUX_TEXEL1 )    swapbyte(&m.a, &m.c);
600             if( m.a == MUX_COMBINED ) swapbyte(&m.a, &m.c);
601             continue;
602         }
603 
604         if( m.a == MUX_1 && m.b!=m.d && do_complement )
605         {
606             m.a = m.b^MUX_COMPLEMENT;
607             m.b = MUX_0;
608             if( m.c == MUX_TEXEL0 || m.c == MUX_TEXEL1 )    swapbyte(&m.a, &m.c);
609             if( m.a == MUX_COMBINED ) swapbyte(&m.a, &m.c);
610             continue;
611         }
612 
613         //Type 6: (A-B)*C+B Map to LERP, or BLENDALPHA
614         //D==B
615         splitType[i] = CM_FMT_TYPE_A_LERP_B_C;
616         if( m.b == m.d )
617         {
618             continue;
619         }
620 
621 
622         //Type 7: A-B+D
623         //C=1               = A-B+D
624         splitType[i] = CM_FMT_TYPE_A_SUB_B_ADD_D;
625         if( m.c == MUX_1 )
626         {
627             if( m.c == MUX_TEXEL0 || m.c == MUX_TEXEL1 )    swapbyte(&m.a, &m.c);
628             continue;
629         }
630 
631         //Type 8: (A-B)*C
632         splitType[i] = CM_FMT_TYPE_A_SUB_B_MOD_C;
633         if( m.d == MUX_0 )
634         {
635             continue;
636         }
637 
638         if( m.c == m.d && do_complement )   // (A-B)*C+C   ==> (A + B|C ) * C
639         {
640             m.d = MUX_0;
641             m.b |= MUX_COMPLEMENT;
642             continue;
643         }
644 
645         if( m.a == m.d )
646         {
647             splitType[i] = CM_FMT_TYPE_A_B_C_A;
648             continue;
649         }
650 
651         //Type 9: (A-B)*C+D
652         splitType[i] = CM_FMT_TYPE_A_B_C_D;
653     }
654 
655     if ((splitType[0] == CM_FMT_TYPE_D && splitType[2]!= CM_FMT_TYPE_NOT_USED ) ||  //Cycle 1 Color
656         (!IsUsedInCycle(MUX_COMBINED, 1, COLOR_CHANNEL, MUX_MASK) && !IsUsedInCycle(MUX_COMBINED, 1, ALPHA_CHANNEL, MUX_MASK) && splitType[2]!= CM_FMT_TYPE_NOT_USED))
657     {
658         //Replace cycle 1 color with cycle 2 color because we have already replace cycle2's cmb
659         aRGB0 = aRGB1;
660         bRGB0 = bRGB1;
661         cRGB0 = cRGB1;
662         dRGB0 = dRGB1;
663         aRGB1 = MUX_0;
664         bRGB1 = MUX_0;
665         cRGB1 = MUX_0;
666         dRGB1 = MUX_COMBINED;
667         splitType[0] = splitType[2];
668         splitType[2] = CM_FMT_TYPE_NOT_USED;
669     }
670 
671     if ((splitType[1] == CM_FMT_TYPE_D && splitType[3]!= CM_FMT_TYPE_NOT_USED ) ||  //Cycle 2 Alpha
672         (!IsUsedInCycle(MUX_COMBINED, 1, ALPHA_CHANNEL, MUX_MASK) && !IsUsedInCycle(MUX_COMBINED|MUX_ALPHAREPLICATE, 1, COLOR_CHANNEL, MUX_MASK_WITH_ALPHA) && splitType[3]!= CM_FMT_TYPE_NOT_USED))
673     {
674         //Replace cycle 1 alpha with cycle 2 alpha because we have already replace cycle2's cmb
675         aA0 = aA1;
676         bA0 = bA1;
677         cA0 = cA1;
678         dA0 = dA1;
679         aA1 = MUX_0;
680         bA1 = MUX_0;
681         cA1 = MUX_0;
682         dA1 = MUX_COMBINED;
683         splitType[1] = splitType[3];
684         splitType[3] = CM_FMT_TYPE_NOT_USED;
685     }
686 
687     if( splitType[0] == CM_FMT_TYPE_A_MOD_C && splitType[2] == CM_FMT_TYPE_A_ADD_D )
688     {
689         m_n64Combiners[0].d = (m_n64Combiners[2].a & MUX_MASK) == MUX_COMBINED ? m_n64Combiners[2].d : m_n64Combiners[2].a;
690         splitType[0] = CM_FMT_TYPE_A_MOD_C_ADD_D;
691         splitType[2] = CM_FMT_TYPE_NOT_USED;
692         m_n64Combiners[2].a = MUX_0;
693         m_n64Combiners[2].c = MUX_0;
694         m_n64Combiners[2].d = MUX_COMBINED;
695     }
696 
697     if( splitType[1] == CM_FMT_TYPE_A_MOD_C && splitType[3] == CM_FMT_TYPE_A_ADD_D )
698     {
699         m_n64Combiners[1].d = (m_n64Combiners[3].a & MUX_MASK) == MUX_COMBINED ? m_n64Combiners[3].d : m_n64Combiners[3].a;
700         splitType[1] = CM_FMT_TYPE_A_MOD_C_ADD_D;
701         splitType[3] = CM_FMT_TYPE_NOT_USED;
702         m_n64Combiners[3].a = MUX_0;
703         m_n64Combiners[3].c = MUX_0;
704         m_n64Combiners[3].d = MUX_COMBINED;
705     }
706 
707     signed cond3 = MAX(splitType[0], splitType[1]);
708     signed cond2 = MAX(cond3, splitType[2]);
709     mType = (CombinerFormatType)MAX(cond2,splitType[3]);
710 }
711 
712 const char* MuxGroupStr[4] =
713 {
714     "Color0",
715     "Alpha0",
716     "Color1",
717     "Alpha1",
718 };
719 
FormatStr(uint8_t val,char * buf)720 char* DecodedMux::FormatStr(uint8_t val, char *buf)
721 {
722     if( val == CM_IGNORE_BYTE )
723     {
724         strcpy(buf," ");
725     }
726     else
727     {
728         strcpy(buf, translatedCombTypes[val&MUX_MASK]);
729         if( val&MUX_ALPHAREPLICATE )
730             strcat(buf,"|A");
731         if( val&MUX_COMPLEMENT )
732             strcat(buf,"|C");
733         if( val&MUX_NEG )
734             strcat(buf,"|N");
735     }
736 
737     return buf;
738 }
739 
Display(bool simplified,FILE * fp)740 void DecodedMux::Display(bool simplified, FILE *fp)
741 {
742     DecodedMux decodedMux;
743     DecodedMux *mux;
744     if (simplified)
745     {
746         mux = this;
747     }
748     else
749     {
750         decodedMux.Decode(m_dwMux0, m_dwMux1);
751         mux = &decodedMux;
752     }
753 
754     char buf0[30];
755     char buf1[30];
756     char buf2[30];
757     char buf3[30];
758 
759     for (int i=0; i<2; i++)
760     {
761         for (int j=0; j<2; j++)
762         {
763             N64CombinerType &m = mux->m_n64Combiners[i+2*j];
764             if (fp != NULL)
765             {
766                 fprintf(fp,"%s: (%s - %s) * %s + %s\n", MuxGroupStr[i+2*j], FormatStr(m.a,buf0),
767                     FormatStr(m.b,buf1), FormatStr(m.c,buf2), FormatStr(m.d,buf3));
768             }
769             else
770             {
771                 DebuggerAppendMsg("%s: (%s - %s) * %s + %s\n", MuxGroupStr[i+2*j], FormatStr(m.a,buf0),
772                     FormatStr(m.b,buf1), FormatStr(m.c,buf2), FormatStr(m.d,buf3));
773             }
774         }
775     }
776 }
777 
HowManyConstFactors()778 int DecodedMux::HowManyConstFactors()
779 {
780     int n = 0;
781 
782     if (IsUsed(MUX_PRIM, MUX_MASK))
783         n++;
784 
785     if (IsUsed(MUX_ENV, MUX_MASK))
786         n++;
787 
788     if (IsUsed(MUX_LODFRAC, MUX_MASK))
789         n++;
790 
791     if (IsUsed(MUX_PRIMLODFRAC, MUX_MASK))
792         n++;
793 
794     return n;
795 }
796 
HowManyTextures()797 int DecodedMux::HowManyTextures()
798 {
799     int n = 0;
800 
801     if (IsUsed(MUX_TEXEL0, MUX_MASK))
802         n++;
803 
804     if (IsUsed(MUX_TEXEL1, MUX_MASK))
805         n++;
806 
807     return n;
808 }
809 
CountTexels(void)810 int DecodedMux::CountTexels(void)
811 {
812     int count=0;
813 
814     for (int i=0; i<4; i++)
815     {
816         N64CombinerType &m = m_n64Combiners[i];
817         count = MAX(count, ::CountTexel1Cycle(m));
818         if (count == 2)
819             break;
820     }
821 
822     return count;
823 }
824 
ReplaceVal(uint8_t val1,uint8_t val2,int cycle,uint8_t mask)825 void DecodedMux::ReplaceVal(uint8_t val1, uint8_t val2, int cycle, uint8_t mask)
826 {
827     int start = 0;
828     int end = 16;
829 
830     if (cycle >= 0)
831     {
832         start = cycle*4;
833         end = start+4;
834     }
835 
836     uint8_t* pmux = m_bytes;
837     for (int i=start; i<end; i++)
838     {
839         if ((pmux[i]&mask) == (val1&mask))
840         {
841             pmux[i] &= (~mask);
842             pmux[i] |= val2;
843         }
844     }
845 }
846 
GetCycle(int cycle,CombineChannel channel)847 uint32_t DecodedMux::GetCycle(int cycle, CombineChannel channel)
848 {
849     uint32_t* pmux = m_dWords;
850     if (channel == COLOR_CHANNEL)
851     {
852         return pmux[cycle*2];
853     }
854     else
855     {
856         return pmux[cycle*2+1];
857     }
858 
859 }
860 
GetCycle(int cycle)861 uint32_t DecodedMux::GetCycle(int cycle)
862 {
863     return m_dWords[cycle];
864 }
865 
866 enum ShadeConstMergeType
867 {
868     SHADE_DO_NOTHING,
869     SHADE_ADD_PRIM,             // Shade+PRIM
870     SHADE_ADD_ENV,              // Shade+ENV
871     SHADE_ADD_PRIM_ALPHA,       // Shade+PRIM_ALPHA
872     SHADE_ADD_ENV_ALPHA,        // Shade+ENV_ALPHA
873     SHADE_MINUS_PRIM_PLUS_ENV,
874     SHADE_MINUS_ENV_PLUS_PRIM,
875     SHADE_MOD_ENV,
876 };
877 
878 typedef struct
879 {
880 uint64_t mux; // simplified
881 ShadeConstMergeType op;
882 } ShadeConstMergeMapType;
883 
884 ShadeConstMergeMapType MergeShadeWithConstantsMaps[] =
885 {
886 {0, SHADE_DO_NOTHING},
887 {0x0007000600070006LL, SHADE_MOD_ENV}, // SHADE * ENV
888 };
889 
890 // 0x05070501, 0x00070006       //(1 - PRIM) * ENV + PRIM
891 // 0x00050003, 0x00050003       //(TEXEL0 - 0) * PRIM + 0
892 
MergeShadeWithConstants(void)893 void DecodedMux::MergeShadeWithConstants(void)
894 {
895     // This function should be called after the mux has been simplified
896     // The goal of this function is to merge as many as possible constants with shade
897     // so to reduce the totally number of constants to 0 or 1
898     // And at the same time, to reduce the complexity of the whole mux
899     // so we can implement the mux easier when lower end video cards
900 
901     // We can only try to merge shade with constants for:
902     // 1 cycle mode or 2 cycle mode and shade is not used in the 2nd cycle
903 
904     if (m_bShadeIsUsed[0]) MergeShadeWithConstantsInChannel(COLOR_CHANNEL);
905     if (m_bShadeIsUsed[1]) MergeShadeWithConstantsInChannel(ALPHA_CHANNEL);
906 }
907 
MergeShadeWithConstantsInChannel(CombineChannel channel)908 void DecodedMux::MergeShadeWithConstantsInChannel(CombineChannel channel)
909 {
910     bool usedIn[2];
911     uint32_t cycleVal;
912     int cycleNum;
913 
914     usedIn[0] = IsUsedInCycle(MUX_SHADE, channel,   MUX_MASK);
915     usedIn[1] = IsUsedInCycle(MUX_SHADE, channel+2, MUX_MASK);
916     if( usedIn[0] && usedIn[1] && GetCycle(channel)!=GetCycle(channel+2) )
917     {
918         //Shade is used in more than 1 cycles, and the ways it is used are different
919         //in cycles, so we can not merge shade with const factors
920         return;
921     }
922 
923     if( usedIn[0] )
924     {
925         cycleVal = GetCycle(channel);
926         cycleNum = 0;
927     }
928     else
929     {
930         cycleVal = GetCycle(channel+2);
931         cycleNum = 1;
932     }
933 
934 
935     //Update to here, Shade is either used only in 1 cycle, or the way it is used are totally
936     //the same in different cycles
937 
938     if (cycleVal == 0x06000000 || IsUsedInCycle(MUX_COMBINED, channel+cycleNum*2, MUX_MASK))  // (0-0)*0+Shade
939     {
940         return;
941     }
942 
943     //Now we can merge shade with consts
944     for( int i=0; i<2; i++ )
945     {
946         if( usedIn[i] )
947         {
948             N64CombinerType &m = m_n64Combiners[channel+i*2];
949             if (IsUsedInCycle(MUX_TEXEL0, i*2+channel, MUX_MASK) || IsUsedInCycle(MUX_TEXEL1, i*2+channel, MUX_MASK))
950             {
951                 if( (m.a&MUX_MASK) == MUX_TEXEL0 || (m.a&MUX_MASK) == MUX_TEXEL1 )
952                 {
953                     // m.a is texel, can not merge constant with shade
954                     return;
955                 }
956                 else if( (m.b&MUX_MASK) == MUX_TEXEL0 || (m.b&MUX_MASK) == MUX_TEXEL1 )
957                 {
958                     // m.b is texel, can not merge constant with shade
959                     return;
960                 }
961                 else if(( (m.c&MUX_MASK) == MUX_TEXEL0 || (m.c&MUX_MASK) == MUX_TEXEL1 ) )
962                 {
963                     if( (m.d&MUX_MASK) != MUX_SHADE )
964                     {
965                         cycleVal &= 0x0000FFFF;     // A-B
966                     }
967                     else if( (m.a&MUX_MASK) == MUX_SHADE || (m.b&MUX_MASK) == MUX_SHADE )
968                     {
969                         return;
970                     }
971                 }
972                 else if( (m.d&MUX_MASK) == MUX_TEXEL0 || (m.d&MUX_MASK) == MUX_TEXEL1 )
973                 {
974                     cycleVal &= 0x00FFFFFF;     // (A-B)*C
975                 }
976             }
977             else
978             {
979                 m.a = m.b = m.c = MUX_0;
980                 m.d = MUX_SHADE;
981                 splitType[i*2+channel] = CM_FMT_TYPE_D;
982             }
983         }
984     }
985 
986     if (channel == COLOR_CHANNEL)
987         m_dwShadeColorChannelFlag = cycleVal;
988     else
989         m_dwShadeAlphaChannelFlag = cycleVal;
990 }
991 
992 
MergeConstants(void)993 void DecodedMux::MergeConstants(void)
994 {
995     // This function should be called afte the mux has been simplified
996     // The goal of this function is to merge remain constants and to reduce the
997     // total number of constants, so we can implement the mux easiler
998 
999     // This function should be called after the MergeShadeWithConstants() function
1000 }
1001 
1002 
UseShadeForConstant(void)1003 void DecodedMux::UseShadeForConstant(void)
1004 {
1005     // If shade is not used in the mux, we can use it for constants
1006     // This function should be called after constants have been merged
1007 
1008     bool doAlphaChannel = true;
1009     uint8_t mask = (uint8_t)~MUX_COMPLEMENT;
1010 
1011     int constants = 0;
1012     if (IsUsed(MUX_ENV, MUX_MASK)) constants++;
1013     if (IsUsed(MUX_PRIM, MUX_MASK)) constants++;
1014     if (IsUsed(MUX_LODFRAC, MUX_MASK)) constants++;
1015     if (IsUsed(MUX_PRIMLODFRAC, MUX_MASK)) constants++;
1016 
1017     bool forceToUsed = constants>m_maxConstants;
1018 
1019     if (!IsUsedInColorChannel(MUX_SHADE, MUX_MASK) && (forceToUsed || MAX(splitType[0], splitType[2]) >= CM_FMT_TYPE_A_MOD_C_ADD_D))
1020     {
1021         int countEnv = Count(MUX_ENV, N64Cycle0RGB, mask) + Count(MUX_ENV, N64Cycle1RGB, mask);
1022         int countPrim = Count(MUX_PRIM, N64Cycle0RGB, mask) + Count(MUX_PRIM, N64Cycle1RGB, mask);
1023         if (countEnv+countPrim > 0)
1024         {
1025             if (countPrim >= countEnv)
1026             {
1027                 //TRACE0("Use Shade for PRIM in color channel");
1028                 ReplaceVal(MUX_PRIM, MUX_SHADE, N64Cycle0RGB, MUX_MASK);
1029                 ReplaceVal(MUX_PRIM, MUX_SHADE, N64Cycle1RGB, MUX_MASK);
1030                 m_dwShadeColorChannelFlag = MUX_PRIM;
1031             }
1032             else if (countEnv > 0)
1033             {
1034                 //TRACE0("Use Shade for ENV in color channel");
1035                 ReplaceVal(MUX_ENV, MUX_SHADE, N64Cycle0RGB, MUX_MASK);
1036                 ReplaceVal(MUX_ENV, MUX_SHADE, N64Cycle1RGB, MUX_MASK);
1037                 m_dwShadeColorChannelFlag = MUX_ENV;
1038             }
1039 
1040             if (IsUsedInColorChannel(MUX_SHADE|MUX_ALPHAREPLICATE, mask))
1041             {
1042                 m_dwShadeAlphaChannelFlag = m_dwShadeColorChannelFlag;
1043                 ReplaceVal((uint8_t)m_dwShadeColorChannelFlag, MUX_SHADE, N64Cycle0Alpha, MUX_MASK);
1044                 ReplaceVal((uint8_t)m_dwShadeColorChannelFlag, MUX_SHADE, N64Cycle1Alpha, MUX_MASK);
1045                 doAlphaChannel = false;
1046             }
1047         }
1048     }
1049 
1050     if (doAlphaChannel && !IsUsedInAlphaChannel(MUX_SHADE, MUX_MASK) && !IsUsedInColorChannel(MUX_SHADE|MUX_ALPHAREPLICATE, MUX_MASK_WITH_ALPHA))
1051     {
1052         int countEnv = Count(MUX_ENV|MUX_ALPHAREPLICATE, N64Cycle0RGB, mask) + Count(MUX_ENV|MUX_ALPHAREPLICATE, N64Cycle1RGB, mask);
1053         int countPrim = Count(MUX_PRIM|MUX_ALPHAREPLICATE, N64Cycle0RGB, mask) + Count(MUX_PRIM|MUX_ALPHAREPLICATE, N64Cycle1RGB, mask);
1054 
1055         if (forceToUsed || MAX(splitType[1], splitType[3]) >= CM_FMT_TYPE_A_MOD_C_ADD_D ||
1056             (MAX(splitType[0], splitType[2]) >= CM_FMT_TYPE_A_MOD_C_ADD_D && countEnv+countPrim > 0 ))
1057         {
1058             countEnv = Count(MUX_ENV, N64Cycle0Alpha, MUX_MASK) +
1059                        Count(MUX_ENV, N64Cycle1Alpha, MUX_MASK) +
1060                        Count(MUX_ENV|MUX_ALPHAREPLICATE, N64Cycle0RGB, mask) +
1061                        Count(MUX_ENV|MUX_ALPHAREPLICATE, N64Cycle1RGB, mask);
1062 
1063             countPrim = Count(MUX_PRIM, N64Cycle0Alpha, MUX_MASK) +
1064                         Count(MUX_PRIM, N64Cycle1Alpha, MUX_MASK) +
1065                         Count(MUX_PRIM|MUX_ALPHAREPLICATE, N64Cycle0RGB, mask) +
1066                         Count(MUX_PRIM|MUX_ALPHAREPLICATE, N64Cycle1RGB, mask);
1067 
1068             if (countEnv+countPrim > 0)
1069             {
1070                 if (countPrim > 0 && m_dwShadeColorChannelFlag == MUX_PRIM)
1071                 {
1072                     //TRACE0("Use Shade for PRIM in alpha channel");
1073                     ReplaceVal(MUX_PRIM, MUX_SHADE, N64Cycle0Alpha, MUX_MASK);
1074                     ReplaceVal(MUX_PRIM, MUX_SHADE, N64Cycle1Alpha, MUX_MASK);
1075                     ReplaceVal(MUX_PRIM|MUX_ALPHAREPLICATE, MUX_SHADE|MUX_ALPHAREPLICATE, N64Cycle0RGB, mask);
1076                     ReplaceVal(MUX_PRIM|MUX_ALPHAREPLICATE, MUX_SHADE|MUX_ALPHAREPLICATE, N64Cycle1RGB, mask);
1077                     m_dwShadeAlphaChannelFlag = MUX_PRIM;
1078                 }
1079                 else if (countEnv>0 && m_dwShadeColorChannelFlag == MUX_ENV)
1080                 {
1081                     //TRACE0("Use Shade for PRIM in alpha channel");
1082                     ReplaceVal(MUX_ENV, MUX_SHADE, N64Cycle0Alpha, MUX_MASK);
1083                     ReplaceVal(MUX_ENV, MUX_SHADE, N64Cycle1Alpha, MUX_MASK);
1084                     ReplaceVal(MUX_ENV|MUX_ALPHAREPLICATE, MUX_SHADE|MUX_ALPHAREPLICATE, N64Cycle0RGB, mask);
1085                     ReplaceVal(MUX_ENV|MUX_ALPHAREPLICATE, MUX_SHADE|MUX_ALPHAREPLICATE, N64Cycle1RGB, mask);
1086                     m_dwShadeAlphaChannelFlag = MUX_ENV;
1087                 }
1088                 else if (countPrim >= countEnv)
1089                 {
1090                     //TRACE0("Use Shade for PRIM in alpha channel");
1091                     ReplaceVal(MUX_PRIM, MUX_SHADE, N64Cycle0Alpha, MUX_MASK);
1092                     ReplaceVal(MUX_PRIM, MUX_SHADE, N64Cycle1Alpha, MUX_MASK);
1093                     ReplaceVal(MUX_PRIM|MUX_ALPHAREPLICATE, MUX_SHADE|MUX_ALPHAREPLICATE, N64Cycle0RGB, mask);
1094                     ReplaceVal(MUX_PRIM|MUX_ALPHAREPLICATE, MUX_SHADE|MUX_ALPHAREPLICATE, N64Cycle1RGB, mask);
1095                     m_dwShadeAlphaChannelFlag = MUX_PRIM;
1096                 }
1097                 else if (countEnv > 0)
1098                 {
1099                     //TRACE0("Use Shade for ENV in alpha channel");
1100                     ReplaceVal(MUX_ENV, MUX_SHADE, N64Cycle0Alpha, MUX_MASK);
1101                     ReplaceVal(MUX_ENV, MUX_SHADE, N64Cycle1Alpha, MUX_MASK);
1102                     ReplaceVal(MUX_ENV|MUX_ALPHAREPLICATE, MUX_SHADE|MUX_ALPHAREPLICATE, N64Cycle0RGB, mask);
1103                     ReplaceVal(MUX_ENV|MUX_ALPHAREPLICATE, MUX_SHADE|MUX_ALPHAREPLICATE, N64Cycle1RGB, mask);
1104                     m_dwShadeAlphaChannelFlag = MUX_ENV;
1105                 }
1106             }
1107         }
1108     }
1109 }
1110 
UseTextureForConstant(void)1111 void DecodedMux::UseTextureForConstant(void)
1112 {
1113     int numofconst = HowManyConstFactors();
1114     int numOftex = HowManyTextures();
1115 
1116     if (numofconst > m_maxConstants && numOftex < m_maxTextures)
1117     {
1118         // We can use a texture for a constant
1119         for (int i=0; i<2 && numofconst > m_maxConstants ; i++)
1120         {
1121             if (IsUsed(MUX_TEXEL0+i, MUX_MASK))
1122             {
1123                 continue;   // can not use this texture
1124             }
1125 
1126             if (IsUsed(MUX_PRIM, MUX_MASK))
1127             {
1128                 ReplaceVal(MUX_PRIM, MUX_TEXEL0+i, -1, MUX_MASK);
1129                 m_ColorTextureFlag[i] = MUX_PRIM;
1130                 numofconst--;
1131                 continue;
1132             }
1133 
1134             if (IsUsed(MUX_ENV, MUX_MASK))
1135             {
1136                 ReplaceVal(MUX_ENV, MUX_TEXEL0+i, -1, MUX_MASK);
1137                 m_ColorTextureFlag[i] = MUX_ENV;
1138                 numofconst--;
1139                 continue;
1140             }
1141 
1142             if (IsUsed(MUX_LODFRAC, MUX_MASK))
1143             {
1144                 ReplaceVal(MUX_LODFRAC, MUX_TEXEL0+i, -1, MUX_MASK);
1145                 m_ColorTextureFlag[i] = MUX_LODFRAC;
1146                 numofconst--;
1147                 continue;
1148             }
1149 
1150             if (IsUsed(MUX_PRIMLODFRAC, MUX_MASK))
1151             {
1152                 ReplaceVal(MUX_PRIMLODFRAC, MUX_TEXEL0+i, -1, MUX_MASK);
1153                 m_ColorTextureFlag[i] = MUX_PRIMLODFRAC;
1154                 numofconst--;
1155                 continue;
1156             }
1157         }
1158     }
1159 }
1160 
1161 
UseTextureForConstant(void)1162 void DecodedMuxForOGL14V2::UseTextureForConstant(void)
1163 {
1164     bool envused = IsUsed(MUX_ENV, MUX_MASK);
1165     bool lodused = IsUsed(MUX_LODFRAC, MUX_MASK);
1166 
1167     int numofconst = 0;
1168     if (envused) numofconst++;
1169     if (lodused) numofconst++;
1170 
1171     int numOftex = HowManyTextures();
1172 
1173     if (numofconst > 0 && numOftex < 2)
1174     {
1175         // We can use a texture for a constant
1176         for (int i=0; i<2 && numofconst > 0 ; i++)
1177         {
1178             if (IsUsed(MUX_TEXEL0+i, MUX_MASK))
1179             {
1180                 continue;   // can not use this texture
1181             }
1182 
1183             if (envused)
1184             {
1185                 ReplaceVal(MUX_ENV, MUX_TEXEL0+i, -1, MUX_MASK);
1186                 m_ColorTextureFlag[i] = MUX_ENV;
1187                 numofconst--;
1188                 envused = false;
1189                 continue;
1190             }
1191 
1192             if (IsUsed(MUX_LODFRAC, MUX_MASK))
1193             {
1194                 ReplaceVal(MUX_LODFRAC, MUX_TEXEL0+i, -1, MUX_MASK);
1195                 m_ColorTextureFlag[i] = MUX_LODFRAC;
1196                 numofconst--;
1197                 continue;
1198             }
1199 
1200             if (IsUsed(MUX_PRIMLODFRAC, MUX_MASK))
1201             {
1202                 ReplaceVal(MUX_PRIMLODFRAC, MUX_TEXEL0+i, -1, MUX_MASK);
1203                 m_ColorTextureFlag[i] = MUX_PRIMLODFRAC;
1204                 numofconst--;
1205                 continue;
1206             }
1207         }
1208     }
1209 }
1210 
1211 #ifdef DEBUGGER
1212 extern const char *translatedCombTypes[];
DisplayMuxString(const char * prompt)1213 void DecodedMux::DisplayMuxString(const char *prompt)
1214 {
1215     DebuggerAppendMsg("//Mux=0x%08x%08x\t%s in %s\n", m_dwMux0, m_dwMux1, prompt, g_curRomInfo.szGameName);
1216     Display(false, NULL);
1217     TRACE0("\n");
1218 }
1219 
DisplaySimpliedMuxString(const char * prompt)1220 void DecodedMux::DisplaySimpliedMuxString(const char *prompt)
1221 {
1222     DebuggerAppendMsg("//Simplified Mux=0x%08x%08x\t%s in %s\n", m_dwMux0, m_dwMux1, prompt, g_curRomInfo.szGameName);
1223     DebuggerAppendMsg("Simplified DWORDs=%08X, %08X, %08X, %08X", m_dWords[0],m_dWords[1],m_dWords[2],m_dWords[3]);
1224     Display(true, NULL);
1225     DebuggerAppendMsg("Simplified type: %s", muxTypeStrs[mType]);
1226     if( m_dwShadeColorChannelFlag != 0 )
1227     {
1228         if( m_dwShadeColorChannelFlag == MUX_ENV )
1229             TRACE0("Shade = ENV in color channel")
1230         else if( m_dwShadeColorChannelFlag == MUX_PRIM )
1231             TRACE0("Shade = PRIM in color channel")
1232         else if( m_dwShadeColorChannelFlag == MUX_LODFRAC )
1233             TRACE0("Shade = MUX_LODFRAC in color channel")
1234         else if( m_dwShadeColorChannelFlag == MUX_PRIMLODFRAC )
1235             TRACE0("Shade = MUX_PRIMLODFRAC in color channel")
1236         else
1237             DisplayConstantsWithShade(m_dwShadeColorChannelFlag,COLOR_CHANNEL);
1238     }
1239     if( m_dwShadeAlphaChannelFlag != 0 )
1240     {
1241         if( m_dwShadeAlphaChannelFlag == MUX_ENV )
1242             TRACE0("Shade = ENV in alpha channel")
1243         else if( m_dwShadeAlphaChannelFlag == MUX_PRIM )
1244             TRACE0("Shade = PRIM in alpha channel")
1245         else if( m_dwShadeAlphaChannelFlag == MUX_LODFRAC )
1246             TRACE0("Shade = MUX_LODFRAC in alpha channel")
1247         else if( m_dwShadeAlphaChannelFlag == MUX_PRIMLODFRAC )
1248             TRACE0("Shade = MUX_PRIMLODFRAC in alpha channel")
1249         else
1250             DisplayConstantsWithShade(m_dwShadeAlphaChannelFlag,ALPHA_CHANNEL);
1251     }
1252 
1253     for( int i=0; i<2; i++ )
1254     {
1255         if( m_ColorTextureFlag[i] != 0 )
1256         {
1257             if( m_ColorTextureFlag[i] == MUX_ENV )
1258                 TRACE1("Tex %d = ENV", i)
1259             else if( m_ColorTextureFlag[i] == MUX_PRIM )
1260                 TRACE1("Tex %d = PRIM", i)
1261             else if( m_ColorTextureFlag[i] == MUX_LODFRAC )
1262                 TRACE1("Tex %d = MUX_LODFRAC", i)
1263             else if( m_ColorTextureFlag[i] == MUX_PRIMLODFRAC )
1264                 TRACE1("Tex %d = MUX_PRIMLODFRAC", i)
1265         }
1266     }
1267 
1268 
1269     TRACE0("\n");
1270 }
1271 
DisplayConstantsWithShade(uint32_t flag,CombineChannel channel)1272 void DecodedMux::DisplayConstantsWithShade(uint32_t flag,CombineChannel channel)
1273 {
1274     DebuggerAppendMsg("Shade = %08X in %s channel",flag,channel==COLOR_CHANNEL?"color":"alpha");
1275 }
1276 #else
1277 
1278 extern const char *translatedCombTypes[];
LogMuxString(const char * prompt,FILE * fp)1279 void DecodedMux::LogMuxString(const char *prompt, FILE *fp)
1280 {
1281     fprintf(fp, "//Mux=0x%08x%08x\t%s in %s\n", m_dwMux0, m_dwMux1, prompt, g_curRomInfo.szGameName);
1282     Display(false, fp);
1283     TRACE0("\n");
1284 }
1285 
LogSimpliedMuxString(const char * prompt,FILE * fp)1286 void DecodedMux::LogSimpliedMuxString(const char *prompt, FILE *fp)
1287 {
1288     fprintf(fp, "//Simplified Mux=0x%08x%08x\t%s in %s\n", m_dwMux0, m_dwMux1, prompt, g_curRomInfo.szGameName);
1289     fprintf(fp, "Simplified DWORDs=%08X, %08X, %08X, %08X\n", m_dWords[0],m_dWords[1],m_dWords[2],m_dWords[3]);
1290     Display(true, fp);
1291     fprintf(fp, "Simplified type: %s", muxTypeStrs[mType]);
1292     if( m_dwShadeColorChannelFlag != 0 )
1293     {
1294         if( m_dwShadeColorChannelFlag == MUX_ENV )
1295             TRACE0("Shade = ENV in color channel")
1296         else if( m_dwShadeColorChannelFlag == MUX_PRIM )
1297             TRACE0("Shade = PRIM in color channel")
1298         else if( m_dwShadeColorChannelFlag == MUX_LODFRAC )
1299             TRACE0("Shade = MUX_LODFRAC in color channel")
1300         else if( m_dwShadeColorChannelFlag == MUX_PRIMLODFRAC )
1301             TRACE0("Shade = MUX_PRIMLODFRAC in color channel")
1302         else
1303             LogConstantsWithShade(m_dwShadeColorChannelFlag,COLOR_CHANNEL,fp);
1304     }
1305 
1306     if( m_dwShadeAlphaChannelFlag != 0 )
1307     {
1308         if( m_dwShadeAlphaChannelFlag == MUX_ENV )
1309             TRACE0("Shade = ENV in alpha channel")
1310         else if( m_dwShadeAlphaChannelFlag == MUX_PRIM )
1311             TRACE0("Shade = PRIM in alpha channel")
1312         else if( m_dwShadeAlphaChannelFlag == MUX_LODFRAC )
1313             TRACE0("Shade = MUX_LODFRAC in alpha channel")
1314         else if( m_dwShadeAlphaChannelFlag == MUX_PRIMLODFRAC )
1315             TRACE0("Shade = MUX_PRIMLODFRAC in alpha channel")
1316         else
1317             LogConstantsWithShade(m_dwShadeAlphaChannelFlag,ALPHA_CHANNEL,fp);
1318     }
1319 
1320     for( int i=0; i<2; i++ )
1321     {
1322         if( m_ColorTextureFlag[i] != 0 )
1323         {
1324             if( m_ColorTextureFlag[i] == MUX_ENV )
1325                 TRACE1("Tex %d = ENV", i)
1326             else if( m_ColorTextureFlag[i] == MUX_PRIM )
1327                 TRACE1("Tex %d = PRIM", i)
1328             else if( m_ColorTextureFlag[i] == MUX_LODFRAC )
1329                 TRACE1("Tex %d = MUX_LODFRAC", i)
1330             else if( m_ColorTextureFlag[i] == MUX_PRIMLODFRAC )
1331                 TRACE1("Tex %d = MUX_PRIMLODFRAC", i)
1332         }
1333     }
1334 
1335     TRACE0("\n");
1336 }
1337 
LogConstantsWithShade(uint32_t flag,CombineChannel channel,FILE * fp)1338 void DecodedMux::LogConstantsWithShade(uint32_t flag,CombineChannel channel, FILE *fp)
1339 {
1340     fprintf(fp, "Shade = %08X in %s channel",flag,channel==COLOR_CHANNEL?"color":"alpha");
1341 }
1342 #endif
1343 
1344 
To_AB_Add_CD_Format(void)1345 void DecodedMux::To_AB_Add_CD_Format(void)  // Use by TNT,Geforce
1346 {
1347     // This function should be called after calling reformat
1348     // This function will not be called by default, can be called optionally
1349     // by TNT/Geforce combiner compilers
1350 
1351     for( int i=0; i<2; i++ )
1352     {
1353         N64CombinerType &m0 = m_n64Combiners[i];
1354         N64CombinerType &m1 = m_n64Combiners[i+2];
1355         switch( splitType[i] )
1356         {
1357         case CM_FMT_TYPE_A_SUB_B_ADD_D:     // = A-B+D      can not map very well in 1 stage
1358             if( splitType[i+2] == CM_FMT_TYPE_NOT_USED )
1359             {
1360                 m1.a = m0.d;
1361                 m1.d = MUX_COMBINED;
1362                 splitType[i+2] = CM_FMT_TYPE_A_ADD_D;
1363 
1364                 m0.d = MUX_0;
1365                 splitType[i] = CM_FMT_TYPE_A_SUB_B;
1366             }
1367             else if( splitType[i+2] == CM_FMT_TYPE_A_MOD_C )
1368             {
1369                 if( (m1.c&MUX_MASK) == MUX_COMBINED )   swapbyte(&m1.a, &m1.c);
1370                 m1.b = m1.d = m1.c;
1371                 m1.c = (m0.d | (m1.a & (~MUX_MASK)));
1372                 splitType[i+2] = CM_FMT_TYPE_AB_ADD_CD;
1373 
1374                 m0.d = MUX_0;
1375                 splitType[i] = CM_FMT_TYPE_A_SUB_B;
1376             }
1377             break;
1378         case CM_FMT_TYPE_A_SUB_B_MOD_C:     // = (A-B)*C    can not map very well in 1 stage
1379             m0.d = m0.b;
1380             m0.b = m0.c;
1381             splitType[i] = CM_FMT_TYPE_AB_SUB_CD;
1382             break;
1383         case CM_FMT_TYPE_A_ADD_B_MOD_C:     // = (A+B)*C    can not map very well in 1 stage
1384             m0.d = m0.b;
1385             m0.b = m0.c;
1386             splitType[i] = CM_FMT_TYPE_AB_ADD_CD;
1387             break;
1388         case CM_FMT_TYPE_A_B_C_D:           // = (A-B)*C+D
1389         case CM_FMT_TYPE_A_B_C_A:           // = (A-B)*C+D
1390             if( splitType[i+2] == CM_FMT_TYPE_NOT_USED )
1391             {
1392                 m1.a = m0.d;
1393                 m1.d = MUX_COMBINED;
1394                 splitType[i+2] = CM_FMT_TYPE_A_ADD_D;
1395 
1396                 m0.d = m0.b;
1397                 m0.b = m0.c;
1398                 splitType[i] = CM_FMT_TYPE_AB_SUB_CD;
1399             }
1400             else if( splitType[i+2] == CM_FMT_TYPE_A_MOD_C )
1401             {
1402                 if( (m1.c&MUX_MASK) == MUX_COMBINED )   swapbyte(&m1.a, &m1.c);
1403                 m1.b = m1.d = m1.c;
1404                 m1.c = (m0.d | (m1.a & (~MUX_MASK)));
1405                 splitType[i+2] = CM_FMT_TYPE_AB_ADD_CD;
1406 
1407                 m0.d = m0.b;
1408                 m0.b = m0.c;
1409                 splitType[i] = CM_FMT_TYPE_AB_ADD_CD;
1410             }
1411             break;
1412          default:
1413            break;
1414         }
1415     }
1416 }
1417 
To_AB_Add_C_Format(void)1418 void DecodedMux::To_AB_Add_C_Format(void)   // Use by ATI Radeon
1419 {
1420     // This function should be called after calling reformat
1421     // This function will not be called by default, can be called optionally
1422     // by ATI combiner compilers
1423 }
1424 
CheckCombineInCycle1(void)1425 void DecodedMux::CheckCombineInCycle1(void)
1426 {
1427     if (IsUsedInCycle(MUX_COMBINED, 0, COLOR_CHANNEL, MUX_MASK))
1428         ReplaceVal(MUX_COMBINED, MUX_SHADE, 0, MUX_MASK);
1429 
1430     if (IsUsedInCycle(MUX_COMBALPHA, 0, COLOR_CHANNEL, MUX_MASK))
1431         ReplaceVal(MUX_COMBALPHA, MUX_SHADE|MUX_ALPHAREPLICATE, 0, MUX_MASK);
1432 
1433     if (IsUsedInCycle(MUX_COMBINED, 0, ALPHA_CHANNEL, MUX_MASK))
1434     {
1435         if (cA0 == MUX_COMBINED && cRGB0 == MUX_LODFRAC && bRGB0 == dRGB0 && bA0 == dA0)
1436             cA0 = MUX_LODFRAC;
1437         else
1438             ReplaceVal(MUX_COMBINED, MUX_SHADE, 1, MUX_MASK);
1439     }
1440 
1441     if (IsUsedInCycle(MUX_COMBALPHA, 0, ALPHA_CHANNEL, MUX_MASK))
1442         ReplaceVal(MUX_COMBALPHA, MUX_SHADE, 1, MUX_MASK);
1443 }
1444 
SplitComplexStages()1445 void DecodedMux::SplitComplexStages()
1446 {
1447     for( int i=0; i<2; i++) // Color channel and alpha channel
1448     {
1449         if( splitType[i+2] != CM_FMT_TYPE_NOT_USED )
1450             continue;
1451 
1452         N64CombinerType &m = m_n64Combiners[i];
1453         N64CombinerType &m2 = m_n64Combiners[i+2];
1454 
1455         switch( splitType[i] )
1456         {
1457         case CM_FMT_TYPE_A_MOD_C_ADD_D:     // = A*C+D      can mapped to MULTIPLYADD(arg1,arg2,arg0)
1458             m2.a = m.d;
1459             m2.d = MUX_COMBINED;
1460             m2.c = MUX_1;
1461             m2.b = 0;
1462             splitType[i+2] = CM_FMT_TYPE_A_ADD_D;
1463             m.d = MUX_0;
1464             splitType[i] = CM_FMT_TYPE_A_MOD_C;
1465             break;
1466         case CM_FMT_TYPE_A_SUB_B_ADD_D:     // = A-B+D      can not map very well in 1 stage
1467             m2.a = m.d;
1468             m2.d = MUX_COMBINED;
1469             m2.c = MUX_1;
1470             m2.b=0;
1471             splitType[i+2] = CM_FMT_TYPE_A_ADD_D;
1472             m.d = MUX_0;
1473             splitType[i] = CM_FMT_TYPE_A_SUB_B;
1474             break;
1475         case CM_FMT_TYPE_A_SUB_B_MOD_C:     // = (A-B)*C    can not map very well in 1 stage
1476             m2.a = m.c;
1477             m2.c = MUX_COMBINED;
1478             m2.d = m2.b=0;
1479             splitType[i+2] = CM_FMT_TYPE_A_MOD_C;
1480             m.c = MUX_1;
1481             splitType[i] = CM_FMT_TYPE_A_SUB_B;
1482             break;
1483         case CM_FMT_TYPE_A_ADD_B_MOD_C:     // = (A+B)*C    can not map very well in 1 stage
1484             m2.a = m.c;
1485             m2.c = MUX_COMBINED;
1486             m2.d = m2.b = 0;
1487             splitType[i+2] = CM_FMT_TYPE_A_MOD_C;
1488             m.c = MUX_1;
1489             m.d = m.b;
1490             m.b = MUX_0;
1491             splitType[i] = CM_FMT_TYPE_A_ADD_D;
1492             break;
1493         case CM_FMT_TYPE_A_B_C_D:           // = (A-B)*C+D  can not map very well in 1 stage
1494             m2.a = m.d;
1495             m2.d = MUX_COMBINED;
1496             m2.c = MUX_1;
1497             m2.b = 0;
1498             splitType[i+2] = CM_FMT_TYPE_A_ADD_D;
1499             m.d = MUX_0;
1500             splitType[i] = CM_FMT_TYPE_A_SUB_B_MOD_C;
1501             break;
1502         case CM_FMT_TYPE_A_B_C_A:           // = (A-B)*C+A  can not map very well in 1 stage
1503             m2.a = m.d;
1504             m2.d = MUX_COMBINED;
1505             m2.c = MUX_1;
1506             m2.b = 0;
1507             splitType[i+2] = CM_FMT_TYPE_A_ADD_D;
1508             m.d = MUX_0;
1509             splitType[i] = CM_FMT_TYPE_A_SUB_B_MOD_C;
1510             break;
1511          default:
1512            break;
1513         }
1514     }
1515     //Reformat(true);
1516     //UseShadeForConstant();
1517 }
1518 
1519 
ConvertLODFracTo0()1520 void DecodedMux::ConvertLODFracTo0()
1521 {
1522     ReplaceVal(MUX_LODFRAC, MUX_0, -1, MUX_MASK);
1523     ReplaceVal(MUX_PRIMLODFRAC, MUX_0, -1, MUX_MASK);
1524 }
1525 
1526 
Hack(void)1527 void DecodedMux::Hack(void)
1528 {
1529     if( options.enableHackForGames == HACK_FOR_TONYHAWK )
1530     {
1531         if( gRSP.curTile == 1 )
1532             ReplaceVal(MUX_TEXEL1, MUX_TEXEL0, -1, MUX_MASK);
1533     }
1534     else if( options.enableHackForGames == HACK_FOR_ZELDA || options.enableHackForGames == HACK_FOR_ZELDA_MM)
1535     {
1536         if( m_dwMux1 == 0xfffd9238 && m_dwMux0 == 0x00ffadff )
1537             ReplaceVal(MUX_TEXEL1, MUX_TEXEL0, -1, MUX_MASK);
1538         else if( m_dwMux1 == 0xff5bfff8 && m_dwMux0 == 0x00121603 )
1539         {
1540             // The Zelda road trace
1541             ReplaceVal(MUX_TEXEL1, MUX_0, -1, MUX_MASK);
1542         }
1543     }
1544     else if( options.enableHackForGames == HACK_FOR_MARIO_TENNIS )
1545     {
1546         if( m_dwMux1 == 0xffebdbc0 && m_dwMux0 == 0x00ffb9ff )
1547         {
1548             // Player shadow
1549             //m_decodedMux.dRGB0 = MUX_TEXEL0;
1550             //m_decodedMux.dRGB1 = MUX_COMBINED;
1551             cA1 = MUX_TEXEL0;
1552         }
1553     }
1554     else if( options.enableHackForGames == HACK_FOR_MARIO_GOLF )
1555     {
1556         // Hack for Mario Golf
1557         if( m_dwMux1 == 0xf1ffca7e || m_dwMux0 == 0x00115407 )
1558         {
1559             // The grass
1560             ReplaceVal(MUX_TEXEL0, MUX_TEXEL1, -1, MUX_MASK);
1561         }
1562     }
1563     else if( options.enableHackForGames == HACK_FOR_TOPGEARRALLY )
1564     {
1565         //Mux=0x00317e025ffef3fa    Used in TOP GEAR RALLY
1566         //Color0: (PRIM - ENV) * TEXEL1 + ENV
1567         //Color1: (COMBINED - 0) * TEXEL1 + 0
1568         //Alpha0: (0 - 0) * 0 + TEXEL0
1569         //Alpha1: (0 - 0) * 0 + TEXEL1
1570         if( m_dwMux1 == 0x5ffef3fa || m_dwMux0 == 0x00317e02 )
1571         {
1572             // The grass
1573             //ReplaceVal(MUX_TEXEL0, MUX_TEXEL1, -1, MUX_MASK);
1574             dA1 = MUX_COMBINED;
1575             //aA1 = MUX_COMBINED;
1576             //cA1 = MUX_TEXEL1;
1577             //dA1 = MUX_0;
1578             cRGB1 = MUX_TEXEL0;
1579         }
1580     }
1581 }
1582 
1583