/* Mednafen - Multi-system Emulator
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include <string.h>

#include <boolean.h>
#include <retro_miscellaneous.h>
#include <compat/intrinsics.h>
#include <math/float_minmax.h>

#include "../mednafen-types.h"
#include "../math_ops.h"
#include "../state_helpers.h"
#include "../pgxp/pgxp_gte.h"
#include "../pgxp/pgxp_main.h"
#include "gte.h"

extern bool psx_gte_overclock;

/* Notes:

 AVSZ3/AVSZ4:
	OTZ is MAC0 >> 12
	OTZ overflow/underflow flag is set in an overflow condition even if MAC0 == 0.
	sf field bit has no effect?

 FLAG register:
	Bits present mask: 0xfffff000

	Checksum bit can't be directly set, it's apparently calculated like (bool)(FLAGS & 0x7f87e000)

	Instructions effectively clear it 0 at start. (todo: test "invalid" instructions)

 X/Y FIFO [3] register write pushes a copy down to [2]

*/

/* For compatibility only */
typedef union
{
 int32_t Raw[4][5];
 int16_t Raw16[4][10];
} Matrices_t;

typedef union
{
   struct
   {
      uint8_t R;
      uint8_t G;
      uint8_t B;
      uint8_t CD;
   } c;
   uint8_t Raw8[4];
} gtergb;

typedef struct
{
   int16_t X;
   int16_t Y;
} gtexy;

static uint32_t *CR;
static uint32_t *DR;

static uint32_t REG[64];
#define CR_OFFSET 32

/* Three 3x3 signed 4.12 matrices: rotation (CR[0]), light (CR[8]), and color (CR[16]) */

/* interval between Matrices start location in CR registers (0,8,16) */
#define M_NEXT 8

/* enum for MVMVA Matrix bits 17-18 (mx in DECODE_FIELDS) */
enum {Matrices_Rot,Matrices_Light,Matrices_Color,Matrices_AbbyNormal};

/* enum for MVMVA Translation Vector bits 13-14 (cv in DECODE_FIELDS) */
enum {CRVectors_T,CRVectors_B,CRVectors_FC,CRVectors_Null};

/* T = CR[5]-CR[7] */
#define CRV_T 5
/* B = CR[13]-CR[15] */
#define CRV_B 13
/* FC = CR[21]-CR[23] */
#define CRV_FC 21
/* interval between CRVs */
#define CRV_NEXT 8

/* Control register aliases */
#define OFX (int32_t)CR[24]           /* Screen offset X: signed 16.16 */
#define OFY (int32_t)CR[25]           /* Screen offset Y: signed 16.16 */
#define H (uint16_t)CR[26]   /* Projection plane distance */
#define DQA (int16_t)CR[27]  /* Depth queing coefficient: signed 8.8 */
#define DQB (int32_t)CR[28]           /* Depth queing offset: signed 8.24 */

#define ZSF3 (int16_t)CR[29] /* Scale factor when computing the average of 3 Z values
                                      * (triangle): signed 4.12 */
#define ZSF4 (int16_t)CR[30] /* Scale factor when computing the average of 4 Z values
                                       * (quad): signed 4.12 */

static uint32_t FLAGS;	      /* Overflow flags generated by the GTE commands */

/* Begin DR */
#define Vectors(vec,elm) (int16_t)(DR[(vec*2)+(elm==2?1:0)]>>(elm==1?16:0))
                              /* Five 3x signed words control vectors: translation,
                                 BackgroundColor, FarColor and Zero (which is always equal to
                                 [0, 0, 0]. */

#define VEC3IR 3               /* Vector 3 [IR1,IR2,IR3] */

#define RGB_R (uint8_t)DR[6]      /* RGB color. High byte is passed around but not used in */
#define RGB_G (uint8_t)(DR[6]>>8) /* computations, it often contains a GPU  GP0 command byte. */
#define RGB_B (uint8_t)(DR[6]>>16)
#define RGB_CD (uint8_t)(DR[6]>>24)

#define SET_OTZ(val) DR[7]=(uint16_t)(val)
#define OTZ (uint16_t)DR[7]          /* Z average value */

#define SET_IR(i,val) DR[8+i]=(int16_t)(val)
#define IR(i) (int16_t)DR[8+i]         /* Accumulators for intermediate results, 4 x signed halfwords */
#define IR0 IR(0)
#define IR1 IR(1)
#define IR2 IR(2)
#define IR3 IR(3)

#define XY_FIFO(i) DR[12+i]      /* XY FIFO : 4 x 2 x signed half words */
#define XY_FIFO_X(i) (int16_t)DR[12+i]
#define XY_FIFO_Y(i) (int16_t)(DR[12+i]>>16)

#define SET_Z_FIFO(i,val) DR[16+i]=(uint16_t)(val)
#define Z_FIFO(i) (uint16_t)DR[16+i]    /* Z FIFO : 4 x unsigned half words */

#define RGB_FIFO(i) DR[20+i]    /* RGB color FIFO */
#define RGB_FIFO_R(i) (uint8_t)DR[20+i]
#define RGB_FIFO_G(i) (uint8_t)(DR[20+i]>>8)
#define RGB_FIFO_B(i) (uint8_t)(DR[20+i]>>16)
#define RGB_FIFO_CD(i) (uint8_t)(DR[20+i]>>24)

#define Reg23 DR[23]        /* Register 23: 32bit read/write but not used for anything */

#define SET_MAC(i,val) DR[24+i]=(int32_t)(val)
#define MAC(i) (int32_t)DR[24+i]        /* Accumulators for intermediate results, 4 x signed word */

#define LZCS DR[30]         /* Input value used to compute the 'lzcr' value below */

#define LZCR DR[31]         /* Contains the numbers of leading zeros in LZSC if it's possible
                                 (lzcs[31] is 0) or leading ones if it's negative (lzcs[31] is 1) */

/* End DR */

#include "../../beetle_psx_globals.h"

/* For lightrec compatibility regs pointer must contain 64*32bits,
   with CR directly after DR, use_regs will use provided regs instead
   of internal REG[64] for DR/CR location data will be copied between
   REG[64] and regs pointer to sync them to lightrec*/
void GTE_SwitchRegisters(bool use_regs, uint32_t *regs)
{
   if(use_regs)
   {
      memcpy(regs,REG,sizeof(REG));
      DR = regs;
      CR = regs+CR_OFFSET;
   }
   else
   {
      memcpy(REG,regs,sizeof(REG));
      DR = REG;
      CR = REG+CR_OFFSET;
   }
}

static INLINE uint8_t Sat5(int16_t cc)
{
   if(cc < 0)
      return 0;
   if(cc > 0x1F)
      return 0x1F;
   return(cc);
}

/*
 * Newton-Raphson division table.  (Initialized at startup; do NOT save in save states!)
 * */
static uint8_t DivTable[0x100 + 1];
static INLINE int32_t CalcRecip(uint16_t divisor)
{
 int32_t x = (0x101 + DivTable[(((divisor & 0x7FFF) + 0x40) >> 7)]);
 int32_t tmp = (((int32_t)divisor * -x) + 0x80) >> 8;
 int32_t tmp2 = ((x * (131072 + tmp)) + 0x80) >> 8;

 return(tmp2);
}

void GTE_Init(void)
{
   uint32_t divisor;

   for(divisor = 0x8000; divisor < 0x10000; divisor += 0x80)
   {
      unsigned i;
      uint32_t xa = 512;

      for(i = 1; i < 5; i++)
         xa = (xa * (1024 * 512 - ((divisor >> 7) * xa))) >> 18;

      DivTable[(divisor >> 7) & 0xFF] = ((xa + 1) >> 1) - 0x101;
   }

   /* To avoid a bounds limiting if statement in the emulation code: */
   DivTable[0x100] = DivTable[0xFF];
}


void GTE_Power(void)
{
   /* On real hardware FLAGS is cleared at power-on; the previous code
    * left this register holding whatever it was before reset, which is
    * a determinism bug for any title that reads it without first
    * issuing a flag-clearing GTE op. */
   FLAGS = 0;

   DR = REG;
   CR = REG+CR_OFFSET;

   memset(REG, 0, sizeof(REG));
}

int GTE_StateAction(StateMem *sm, int load, int data_only)
{
   /* Duplicates of CR values, save into savestate for compatibility, don't load */
   static Matrices_t Matrices;
   static union
   {
    int32_t All[4][4];
   } CRVectors;
   int32_t _OFX = OFX;
   int32_t _OFY = OFY;
   uint16_t _H = H;
   int16_t _DQA = DQA;
   int32_t _DQB = DQB;
   int16_t _ZSF3 = ZSF3;
   int16_t _ZSF4 = ZSF4;

   /* compatibility variables, transfer to/from CR/DR[32] during save states.
    * Vectors is zero-initialised because the save path only fills
    * elements [i][0..2]: the [i][3] pads previously went into the
    * savestate as uninitialised stack bytes, making two serializations
    * of identical emulator state differ - poison for anything that
    * compares or hashes states (netplay, runahead diagnostics,
    * serialize-unserialize-serialize fixed-point checks). The load
    * side never reads the pads, so this changes no behaviour. */
   int16_t Vectors[3][4] = {{0}};
   int32_t MAC[4];
   uint16_t _OTZ;
   gtergb RGB;
   int16_t IR[4];
   gtexy XY_FIFO[4];
   uint16_t Z_FIFO[4];
   gtergb RGB_FIFO[3];
   uint32_t _LZCS;
   uint32_t _LZCR;
   uint32_t _Reg23;

   if(!load)
   {
      for(int i = 0; i < 24; i++)
      {
         int which = i;
         uint32_t value = CR[which];
         int we = which >> 3;
         which &= 0x7;

         if(which >= 5)
            CRVectors.All[we][which - 5] = value;
         else
         {
#ifdef MSB_FIRST
            Matrices.Raw[we][which] = (value << 16) | (value >> 16);
#else
            Matrices.Raw[we][which] = value;
#endif
         }
      }

      Vectors[0][0] = DR[0];
      Vectors[0][1] = DR[0] >> 16;

      Vectors[0][2] = DR[1];

      Vectors[1][0] = DR[2];
      Vectors[1][1] = DR[2] >> 16;

      Vectors[1][2] = DR[3];

      Vectors[2][0] = DR[4];
      Vectors[2][1] = DR[4] >> 16;

      Vectors[2][2] = DR[5];

      RGB.c.R = DR[6] >> 0;
      RGB.c.G = DR[6] >> 8;
      RGB.c.B = DR[6] >> 16;
      RGB.c.CD = DR[6] >> 24;

      _OTZ = DR[7];

      IR[0] = DR[8];

      IR[1] = DR[9];

      IR[2] = DR[10];

      IR[3] = DR[11];

      XY_FIFO[0].X = DR[12];
      XY_FIFO[0].Y = DR[12] >> 16;

      XY_FIFO[1].X = DR[13];
      XY_FIFO[1].Y = DR[13] >> 16;

      XY_FIFO[2].X = DR[14];
      XY_FIFO[2].Y = DR[14] >> 16;

      //15 is a mirror of 14
      XY_FIFO[3].X = DR[14];
      XY_FIFO[3].Y = DR[14] >> 16;

      Z_FIFO[0] = DR[16];

      Z_FIFO[1] = DR[17];

      Z_FIFO[2] = DR[18];

      Z_FIFO[3] = DR[19];

      RGB_FIFO[0].c.R = DR[20];
      RGB_FIFO[0].c.G = DR[20] >> 8;
      RGB_FIFO[0].c.B = DR[20] >> 16;
      RGB_FIFO[0].c.CD = DR[20] >> 24;

      RGB_FIFO[1].c.R = DR[21];
      RGB_FIFO[1].c.G = DR[21] >> 8;
      RGB_FIFO[1].c.B = DR[21] >> 16;
      RGB_FIFO[1].c.CD = DR[21] >> 24;

      RGB_FIFO[2].c.R = DR[22];
      RGB_FIFO[2].c.G = DR[22] >> 8;
      RGB_FIFO[2].c.B = DR[22] >> 16;
      RGB_FIFO[2].c.CD = DR[22] >> 24;

      _Reg23 = DR[23];

      MAC[0] = DR[24];

      MAC[1] = DR[25];

      MAC[2] = DR[26];

      MAC[3] = DR[27];

      //28: Overwrites value 9,10,11

      //29: Read-only

      _LZCS = DR[30];
      _LZCR = MDFN_lzcount32(DR[30] ^ ((int32_t)DR[30] >> 31));

      //31: Read-only
   }

   SFORMAT StateRegs[] =
   {
      { CR, (uint32_t)(32 * sizeof(uint32_t)), MDFNSTATE_RLSB32 | 0, "CR" },
      { &FLAGS, sizeof(FLAGS), MDFNSTATE_RLSB | 0, "FLAGS" },

      SFARRAY16(&Matrices.Raw16[0][0], 4 * 10),

      SFARRAY32(&CRVectors.All[0][0], 4 * 4),

      SFVARN(_OFX, "OFX"),
      SFVARN(_OFY, "OFY"),
      SFVARN(_H, "H"),
      SFVARN(_DQA, "DQA"),
      SFVARN(_DQB, "DQB"),

      SFVARN(_ZSF3, "ZSF3"),
      SFVARN(_ZSF4, "ZSF4"),

      SFARRAY16(&Vectors[0][0], 3 * 4),

      SFARRAY(RGB.Raw8, 4),
      SFVARN(_OTZ, "OTZ"),
      SFARRAY16(IR, 4),

      SFVAR(XY_FIFO[0].X),
      SFVAR(XY_FIFO[0].Y),
      SFVAR(XY_FIFO[1].X),
      SFVAR(XY_FIFO[1].Y),
      SFVAR(XY_FIFO[2].X),
      SFVAR(XY_FIFO[2].Y),
      SFVAR(XY_FIFO[3].X),
      SFVAR(XY_FIFO[3].Y),

      SFARRAY16(Z_FIFO, 4),

      SFARRAY(RGB_FIFO[0].Raw8, 4),
      SFARRAY(RGB_FIFO[1].Raw8, 4),
      SFARRAY(RGB_FIFO[2].Raw8, 4),

      SFARRAY32(MAC, 4),

      SFVARN(_LZCS, "LZCS"),
      SFVARN(_LZCR, "LZCR"),
      SFVARN(_Reg23, "Reg23"),

      SFEND
   };
   int ret = MDFNSS_StateAction(sm, load, data_only, StateRegs, "GTE");

   if(load)
   {
      DR[0] = (uint16_t)Vectors[0][0] | ((uint16_t)Vectors[0][1] << 16);
      DR[1] = (int16_t)Vectors[0][2];
      DR[2] = (uint16_t)Vectors[1][0] | ((uint16_t)Vectors[1][1] << 16);
      DR[3] = (int16_t)Vectors[1][2];
      DR[4] = (uint16_t)Vectors[2][0] | ((uint16_t)Vectors[2][1] << 16);
      DR[5] = (int16_t)Vectors[2][2];
      DR[6] = RGB.c.R | (RGB.c.G << 8) | (RGB.c.B << 16) | (RGB.c.CD << 24);
      DR[7] = (uint16_t)OTZ;
      DR[8] = (int16_t)IR[0];
      DR[9] = (int16_t)IR[1];
      DR[10] = (int16_t)IR[2];
      DR[11] = (int16_t)IR[3];
      DR[12] = (uint16_t)XY_FIFO[0].X | ((uint16_t)XY_FIFO[0].Y << 16);
      DR[13] = (uint16_t)XY_FIFO[1].X | ((uint16_t)XY_FIFO[1].Y << 16);
      DR[14] = (uint16_t)XY_FIFO[2].X | ((uint16_t)XY_FIFO[2].Y << 16);
      DR[15] = (uint16_t)XY_FIFO[3].X | ((uint16_t)XY_FIFO[3].Y << 16);
      DR[16] = (uint16_t)Z_FIFO(0);
      DR[17] = (uint16_t)Z_FIFO(1);
      DR[18] = (uint16_t)Z_FIFO(2);
      DR[19] = (uint16_t)Z_FIFO(3);
      DR[20] = RGB_FIFO[0].c.R | (RGB_FIFO[0].c.G << 8) | (RGB_FIFO[0].c.B << 16) | (RGB_FIFO[0].c.CD << 24);
      DR[21] = RGB_FIFO[1].c.R | (RGB_FIFO[1].c.G << 8) | (RGB_FIFO[1].c.B << 16) | (RGB_FIFO[1].c.CD << 24);
      DR[22] = RGB_FIFO[2].c.R | (RGB_FIFO[2].c.G << 8) | (RGB_FIFO[2].c.B << 16) | (RGB_FIFO[2].c.CD << 24);
      DR[23] = Reg23;
      DR[24] = MAC[0];
      DR[25] = MAC[1];
      DR[26] = MAC[2];
      DR[27] = MAC[3];
      DR[28] = Sat5(IR[1] >> 7) | (Sat5(IR[2] >> 7) << 5) | (Sat5(IR[3] >> 7) << 10);
      DR[29] = Sat5(IR[1] >> 7) | (Sat5(IR[2] >> 7) << 5) | (Sat5(IR[3] >> 7) << 10);
      DR[30] = LZCS;
      DR[31] = LZCR;
   }

   return(ret);
}


void GTE_WriteCR(unsigned int which, uint32_t value)
{
   static const uint32_t mask_table[32] = {
      /* 0x00 */
      0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x0000FFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,

      /* 0x08 */
      0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x0000FFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,

      /* 0x10 */
      0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x0000FFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,

      /* 0x18 */
      0xFFFFFFFF, 0xFFFFFFFF, 0x0000FFFF, 0x0000FFFF, 0xFFFFFFFF, 0x0000FFFF, 0x0000FFFF, 0xFFFFFFFF
   };


   value &= mask_table[which];

   CR[which] = value | (CR[which] & ~mask_table[which]);

   if (which == 31)
         CR[31] = (value & 0x7ffff000) | ((value & 0x7f87e000) ? (1 << 31) : 0);
}

uint32_t GTE_ReadCR(unsigned int which)
{
   uint32_t ret = 0;

   ret = CR[which];
   if(which == 4 || which == 12 || which == 20 || which == 26 || which == 27 || which == 29 || which ==30)
      ret = (int16_t)ret;

   return(ret);
}

void GTE_WriteDR(unsigned int which, uint32_t value)
{
   switch(which & 0x1F)
   {
      case 0:
         DR[0] = value;
         break;

      case 1:
         DR[1] = (int16_t)value;
         break;

      case 2:
         DR[2] = value;
         break;

      case 3:
         DR[3] = (int16_t)value;
         break;

      case 4:
         DR[4] = value;
         break;

      case 5:
         DR[5] = (int16_t)value;
         break;

      case 6:
         DR[6] = value;
         break;

      case 7:
         SET_OTZ(value);
         break;

      case 8:
         SET_IR(0, value);
         break;

      case 9:
         SET_IR(1, value);
         break;

      case 10:
         SET_IR(2, value);
         break;

      case 11:
         SET_IR(3, value);
         break;

      case 12:
         DR[12] = value;
         break;

      case 13:
         DR[13] = value;
         break;

      case 14:
         DR[14] = value;
         DR[15] = value;
         break;

      case 15:
         DR[15] = value;

         DR[12] = DR[13];
         DR[13] = DR[14];
         DR[14] = DR[15];
         break;

      case 16:
         SET_Z_FIFO(0, value);
         break;

      case 17:
         SET_Z_FIFO(1, value);
         break;

      case 18:
         SET_Z_FIFO(2, value);
         break;

      case 19:
         SET_Z_FIFO(3, value);
         break;

      case 20:
         RGB_FIFO(0) = value;
         break;

      case 21:
         RGB_FIFO(1) = value;
         break;

      case 22:
         RGB_FIFO(2) = value;
         break;

      case 23:
         Reg23 = value;
         break;

      case 24:
         SET_MAC(0, value);
         break;

      case 25:
         SET_MAC(1, value);
         break;

      case 26:
         SET_MAC(2, value);
         break;

      case 27:
         SET_MAC(3, value);
         break;

      case 28:
         SET_IR(1, ((value >> 0) & 0x1F) << 7);
         SET_IR(2, ((value >> 5) & 0x1F) << 7);
         SET_IR(3, ((value >> 10) & 0x1F) << 7);
         break;

      case 29:	/* Read-only */
         break;

      case 30:
         LZCS = value;
		     LZCR = MDFN_lzcount32(value ^ (0u - (value >> 31)));
         break;

      case 31:	/* Read-only */
         break;
   }
}

uint32_t GTE_ReadDR(unsigned int which)
{
   uint32_t ret = 0;

   switch(which & 0x1F)
   {
      case 0:
         ret = (uint16_t)Vectors(0, 0) | ((uint16_t)Vectors(0, 1) << 16);
         break;

      case 1:
         ret = (int16_t)Vectors(0, 2);
         break;

      case 2:
         ret = (uint16_t)Vectors(1, 0) | ((uint16_t)Vectors(1, 1) << 16);
         break;

      case 3:
         ret = (int16_t)Vectors(1, 2);
         break;

      case 4:
         ret = (uint16_t)Vectors(2, 0) | ((uint16_t)Vectors(2, 1) << 16);
         break;

      case 5:
         ret = (int16_t)Vectors(2, 2);
         break;

      case 6:
         ret = RGB_R | (RGB_G << 8) | (RGB_B << 16) | (RGB_CD << 24);
         break;

      case 7:
         ret = (uint16_t)OTZ;
         break;

      case 8:
         ret = (int16_t)IR0;
         break;

      case 9:
         ret = (int16_t)IR1;
         break;

      case 10:
         ret = (int16_t)IR2;
         break;

      case 11:
         ret = (int16_t)IR3;
         break;

      case 12:
         ret = (uint16_t)XY_FIFO_X(0) | ((uint16_t)XY_FIFO_Y(0) << 16);
         break;

      case 13:
         ret = (uint16_t)XY_FIFO_X(1) | ((uint16_t)XY_FIFO_Y(1) << 16);
         break;

      case 14:
         ret = (uint16_t)XY_FIFO_X(2) | ((uint16_t)XY_FIFO_Y(2) << 16);
         break;

      case 15:
         ret = (uint16_t)XY_FIFO_X(3) | ((uint16_t)XY_FIFO_Y(3) << 16);
         break;

      case 16:
         ret = (uint16_t)Z_FIFO(0);
         break;

      case 17:
         ret = (uint16_t)Z_FIFO(1);
         break;

      case 18:
         ret = (uint16_t)Z_FIFO(2);
         break;

      case 19:
         ret = (uint16_t)Z_FIFO(3);
         break;

      case 20:
         ret = RGB_FIFO(0);
         break;

      case 21:
         ret = RGB_FIFO(1);
         break;

      case 22:
         ret = RGB_FIFO(2);
         break;

      case 23:
         ret = Reg23;
         break;

      case 24:
         ret = MAC(0);
         break;

      case 25:
         ret = MAC(1);
         break;

      case 26:
         ret = MAC(2);
         break;

      case 27:
         ret = MAC(3);
         break;

      case 28:
      case 29:
         ret = Sat5(IR1 >> 7) | (Sat5(IR2 >> 7) << 5) | (Sat5(IR3 >> 7) << 10);
         break;

      case 30:
         ret = LZCS;
         break;

      case 31:
         ret = LZCR;
         break;
   }
   return(ret);
}

#define sign_x_to_s64(_bits, _value) (((int64_t)((uint64_t)(_value) << (64 - _bits))) >> (64 - _bits))

static INLINE int64_t A_MV(unsigned which, int64_t value)
{
   if(value >= (INT64_C(1) << 43))
      FLAGS |= 1 << (30 - which);

   if(value < -(INT64_C(1) << 43))
      FLAGS |= 1 << (27 - which);

   return sign_x_to_s64(44, value);
}

static INLINE int64_t F(int64_t value)
{
   if(value < -2147483648LL)
   {
      /* flag set here */
      FLAGS |= 1 << 15;
   }

   if(value > 2147483647LL)
   {
      /* flag set here */
      FLAGS |= 1 << 16;
   }
   return(value);
}


/* Truncate i64 value to only keep the low 43 bits + sign and
 * update the flags if an overflow occurs */
static INLINE int64_t i64_to_i44(unsigned which, int64_t value)
{
   if(value >= 0x7ffffffffffLL)
      FLAGS |= 1 << (30 - which);

   if(value < -0x80000000000LL)
      FLAGS |= 1 << (27 - which);

   return (((int64_t)((uint64_t)(value) << (64 - 44))) >> (64 - 44));
}


/* Truncate i32 value to an i16, saturating in case of an
 * overflow and updating the flags if an overflow occurs. If
 * `flags.clamp_negative` is true negative values will be clamped
 * to 0. */
static INLINE int16_t i32_to_i16_saturate(unsigned int which, int32_t value, int lm)
{
   int32_t tmp = lm << 15;

   if(value < (-32768 + tmp))
   {
      /* set flag here */
      FLAGS |= 1 << (24 - which);
      return -32768 + tmp;
   }

   if(value > 32767)
   {
      /* Set flag here */
      FLAGS |= 1 << (24 - which);
      return 32767;
   }

   return(value);
}

static INLINE int16_t Lm_B_PTZ(unsigned int which, int32_t value, int32_t ftv_value, int lm)
{
   int32_t tmp = lm << 15;
   int32_t lo  = -32768 + tmp;

   if(ftv_value < -32768)
      FLAGS |= 1 << (24 - which);

   if(ftv_value > 32767)
      FLAGS |= 1 << (24 - which);

   /* Saturate `value` to [lo, 32767]. The lower bound depends on
    * the lm (limit-mode) flag from the GTE opcode: when set, the
    * lower bound rises to 0 (-32768 + 32768) so the result is
    * unsigned. Was clamp(&value, lo, 32767) from clamp.h. */
   if (value < lo)    value = lo;
   if (value > 32767) value = 32767;

   return value;
}

static INLINE uint8_t Lm_C(unsigned int which, int32_t value)
{
   if(value & ~0xFF)
   {
      /* Set flag here */
      FLAGS |= 1 << (21 - which);	/* Tested with GPF */

      if(value < 0)
         value = 0;

      if(value > 255)
         value = 255;
   }

   return(value);
}

static INLINE int32_t Lm_D(int32_t value, int unchained)
{
   /* Not sure if we should have it as int64, or just chain on to and special case when the F flags are set. */
   if(!unchained)
   {
      if(FLAGS & (1 << 15))
      {
         FLAGS |= 1 << 18;
         return(0);
      }

      if(FLAGS & (1 << 16))
      {
         FLAGS |= 1 << 18;
         return(0xFFFF);
      }
   }

   if(value < 0)
   {
      /* Set flag here */
      value = 0;
      FLAGS |= 1 << 18;	/* Tested with AVSZ3 */
   }
   else if(value > 65535)
   {
      /* Set flag here. */
      value = 65535;
      FLAGS |= 1 << 18;	/* Tested with AVSZ3 */
   }

   return(value);
}

static INLINE int32_t Lm_G(unsigned int which, int32_t value)
{
   if(value < -1024)
   {
      /* Set flag here */
      value = -1024;
      FLAGS |= 1 << (14 - which);
   }

   if(value > 1023)
   {
      /* Set flag here. */
      value = 1023;
      FLAGS |= 1 << (14 - which);
   }

   return(value);
}

/* limit to 4096, not 4095 */
static INLINE int32_t Lm_H(int32_t value)
{
#if 0
   if(FLAGS & (1 << 15))
   {
      value = 0;
      FLAGS |= 1 << 12;
      return value;
   }

   if(FLAGS & (1 << 16))
   {
      value = 4096;
      FLAGS |= 1 << 12;
      return value;
   }
#endif

   if(value < 0)
   {
      value = 0;
      FLAGS |= 1 << 12;
   }

   if(value > 4096)
   {
      value = 4096;
      FLAGS |= 1 << 12;
   }

   return(value);
}

/* Convert a 64bit signed average value to an unsigned halfword
 * while updating the overflow flags */
static INLINE uint16_t i64_to_otz(int64_t average, int unchained)
{
   int32_t value = average >> 12;
   /* Not sure if we should have it as int64, or just chain 
    * on to and special case when the F flags are set. */
   if(!unchained)
   {
      if(FLAGS & (1 << 15))
      {
         FLAGS |= 1 << 18;
         return(0);
      }

      if(FLAGS & (1 << 16))
      {
         FLAGS |= 1 << 18;
         return(0xFFFF);
      }
   }

   if(value < 0)
   {
      /* Set flag here */
      FLAGS |= 1 << 18;	/* Tested with AVSZ3 */
      return 0;
   }
   else if(value > 65535)
   {
      /* Set flag here. */
      FLAGS |= 1 << 18;	/* Tested with AVSZ3 */
      return 65535;
   }

   return value;
}

static INLINE void MAC_to_RGB_FIFO(void)
{
   uint32_t packed = Lm_C(0, MAC(1) >> 4) | ((Lm_C(1, MAC(2) >> 4)) << 8) | ((Lm_C(2, MAC(3) >> 4)) << 16) | (RGB_CD << 24);

   RGB_FIFO(0) = RGB_FIFO(1);
   RGB_FIFO(1) = RGB_FIFO(2);
   RGB_FIFO(2) = packed;

   /* PGXP shadow of the ColorFIFO push: the architectural write above is
    * MACn/16 saturated to 8 bits, which discards both the 4 fractional
    * bits (>>4 floors) and any over-range.  Keep the pre-saturation
    * value, in 8-bit scale, alongside the packed word.  MACn/16.0f is
    * exact for |MACn| < 2^24 (division by a power of two only rounds
    * through the int->float conversion of MACn itself); beyond that the
    * value is thousands of times over-range and saturates identically
    * either way, so the shadow requantizes to the architectural bytes
    * bit-exactly across the whole domain.  Same unconditional-call
    * convention as the SXY push in TransformXY. */
   PGXP_pushRGBf((float)MAC(1) / 16.0f,
                 (float)MAC(2) / 16.0f,
                 (float)MAC(3) / 16.0f, packed);
}

static INLINE int16_t Lm_B(unsigned int which, int32_t value, int lm)
{
   int32_t tmp = lm << 15;

   if(value < (-32768 + tmp))
   {
      /* set flag here */
      FLAGS |= 1 << (24 - which);
      value = -32768 + tmp;
   }

   if(value > 32767)
   {
      /* Set flag here */
      FLAGS |= 1 << (24 - which);
      value = 32767;
   }

   return(value);
}

static INLINE void MAC_to_IR(int lm)
{
   SET_IR(1, i32_to_i16_saturate(0, MAC(1), lm));
   SET_IR(2, i32_to_i16_saturate(1, MAC(2), lm));
   SET_IR(3, i32_to_i16_saturate(2, MAC(3), lm));
}

static INLINE void MultiplyMatrixByVector(uint32_t mx, uint32_t v, uint32_t cv, uint32_t sf, int lm)
{
   unsigned i,m;

   for(i = 0; i < 3; i++)
   {
      int64_t tmp;
      int32_t mulr[3];

      tmp = (cv == CRVectors_Null) ? 0 : ((uint64_t)(int64_t)(int32_t)CR[(cv*CRV_NEXT)+CRV_T+i] << 12);

      if(mx == Matrices_AbbyNormal)
      {
         if(i == 0)
         {
            mulr[0] = -(RGB_R << 4);
            mulr[1] = (RGB_R << 4);
            mulr[2] = IR0;
         }
         else
         {
            mulr[0] = (int16_t)CR[i];
            mulr[1] = (int16_t)CR[i];
            mulr[2] = (int16_t)CR[i];
         }
      }
      else
      {
         m = mx*M_NEXT;

         if(i == 0)
         {
            mulr[0] = (int16_t)CR[m];
            mulr[1] = (int16_t)(CR[m] >> 16);
            mulr[2] = (int16_t)CR[m + 1];
         }
         else if(i == 1)
         {
            mulr[0] = (int16_t)(CR[m + 1] >> 16);
            mulr[1] = (int16_t)CR[m + 2];
            mulr[2] = (int16_t)(CR[m + 2] >> 16);
         }
         else
         {
            mulr[0] = (int16_t)CR[m + 3];
            mulr[1] = (int16_t)(CR[m + 3] >> 16);
            mulr[2] = (int16_t)CR[m + 4];
         }
      }

      if(v == VEC3IR)
      {
         mulr[0] *= IR1;
         mulr[1] *= IR2;
         mulr[2] *= IR3;
      }
      else
      {
         mulr[0] *= Vectors(v, 0);
         mulr[1] *= Vectors(v, 1);
         mulr[2] *= Vectors(v, 2);
      }

      tmp = A_MV(i, tmp + mulr[0]);
      if(cv == CRVectors_FC)
      {
         Lm_B(i, tmp >> sf, false);
         tmp = 0;
      }

      tmp = A_MV(i, tmp + mulr[1]);
      tmp = A_MV(i, tmp + mulr[2]);

      SET_MAC(1 + i, tmp >> sf);
   }

   MAC_to_IR(lm);
}

/* precise_z_acc receives the raw 44-bit Z accumulator, i.e. the value that
 * feeds SZ3 *before* the >>12 truncation and the Lm_D saturation.  It is the
 * exact view-space Z in 1/4096 units and exists solely for the PGXP shadow
 * path; the architectural SZ3 written below is untouched. */
static INLINE void MultiplyMatrixByVector_PT(uint32_t mx, uint32_t v, uint32_t cv, uint32_t sf, int lm, int64_t *precise_z_acc)
{
   int64_t tmp[3];
   unsigned i,m;

   for(i = 0; i < 3; i++)
   {
      int32_t mulr[3];

      tmp[i] = (cv == CRVectors_Null) ? 0 : (uint64_t)(int64_t)(int32_t)CR[(cv*CRV_NEXT)+CRV_T+i] << 12;
      m = mx*M_NEXT;

      if(i == 0)
      {
         mulr[0] = (int16_t)CR[m];
         mulr[1] = (int16_t)(CR[m] >> 16);
         mulr[2] = (int16_t)CR[m + 1];
      }
      else if(i == 1)
      {
         mulr[0] = (int16_t)(CR[m + 1] >> 16);
         mulr[1] = (int16_t)CR[m + 2];
         mulr[2] = (int16_t)(CR[m + 2] >> 16);
      }
      else
      {
         mulr[0] = (int16_t)CR[m + 3];
         mulr[1] = (int16_t)(CR[m + 3] >> 16);
         mulr[2] = (int16_t)CR[m + 4];
      }

      mulr[0] *= Vectors(v, 0);
      mulr[1] *= Vectors(v, 1);
      mulr[2] *= Vectors(v, 2);

      tmp[i] = A_MV(i, tmp[i] + mulr[0]);
      tmp[i] = A_MV(i, tmp[i] + mulr[1]);
      tmp[i] = A_MV(i, tmp[i] + mulr[2]);

      SET_MAC(1 + i, tmp[i] >> sf);
   }

   SET_IR(1, Lm_B(0, MAC(1), lm));
   SET_IR(2, Lm_B(1, MAC(2), lm));
   //printf("FTV: %08x %08x\n", CR[cv*CRV_NEXT+CRV_T+2], (uint32)(tmp[2] >> 12));
   SET_IR(3, Lm_B_PTZ(2, MAC(3), tmp[2] >> 12, lm));

   SET_Z_FIFO(0, Z_FIFO(1));
   SET_Z_FIFO(1, Z_FIFO(2));
   SET_Z_FIFO(2, Z_FIFO(3));
   SET_Z_FIFO(3, Lm_D(tmp[2] >> 12, true));

   *precise_z_acc = tmp[2];
}

#define DECODE_FIELDS							\
 const uint32_t sf MDFN_NOWARN_UNUSED = (instr & (1 << 19)) ? 12 : 0;		\
 const uint32_t mx MDFN_NOWARN_UNUSED = (instr >> 17) & 0x3;			\
 const uint32_t v MDFN_NOWARN_UNUSED = (instr >> 15) & 0x3;				\
 const uint32_t cv MDFN_NOWARN_UNUSED = (instr >> 13) & 0x3;	\
 const int lm MDFN_NOWARN_UNUSED = (instr >> 10) & 1;

/* SQR - Square Vector */
static int32_t SQR(uint32_t instr)
{
   unsigned i;
   DECODE_FIELDS;

   /* PSX GTE test fails with this code */
   for (i = 1; i < 4; i++)
   {
      int32_t ir = IR(i);
      SET_MAC(i, (ir * ir) >> sf);
   }

   MAC_to_IR(lm);

   return(5);
}

/* MVMVA - Multiply Vector by Matrix And Vector Add */
static int32_t MVMVA(uint32_t instr)
{
   DECODE_FIELDS;

   MultiplyMatrixByVector(mx, v, cv, sf, lm);

   return(8);
}

static INLINE uint32_t Divide(uint32_t dividend, uint32_t divisor)
{
   if((divisor * 2) > dividend)
   {
      /* GTE-specific division algorithm for dist / z.
       * Returns a saturated 17bit value. */
      unsigned shift_bias = compat_clz_u16(divisor);

      dividend <<= shift_bias;
      divisor <<= shift_bias;

      {
         uint32_t _r = (uint32_t)(((uint64_t)dividend * CalcRecip(divisor | 0x8000) + 32768) >> 16);
         if (_r > 0x1FFFF) _r = 0x1FFFF;
         return _r;
      }
   }

   /* If the Z coordinate is smaller than or equal to half the 
    * projection plane distance, we clip it */

   FLAGS |= 1 << 17;
   return 0x1FFFF;
}

static INLINE void check_mac_overflow(int64_t value)
{
   if(value < -2147483648LL)
      FLAGS |= 1 << 15;
   if(value > 2147483647LL)
      FLAGS |= 1 << 16;
}

static INLINE void TransformXY(int64_t h_div_sz, float precise_h_div_sz, float precise_z)
{

   /* Widescreen-hack horizontal aspect scale, as Q16 fixed point
    * (round(ratio * 65536)).  Holding the multiplier in fixed point
    * keeps the GTE screen-X output - which is machine state a game
    * can read back via MFC2 - computed entirely in integer math, so
    * it is bit-identical across every build, arch and FPU mode
    * (no x87-vs-SSE precision split, no FMA-contraction divergence).
    * Index by widescreen_hack_aspect_ratio_setting; out-of-range
    * falls through to the 16:9 default, matching the old switch. */
   static const int32_t ws_aspect_q16[7] =
   {
      52429, /* 0: 16:10  (0.80) */
      49152, /* 1: 16:9   (0.75, default) */
      43254, /* 2: 18:9   (0.66) */
      41288, /* 3: 19:9   (0.63) */
      39322, /* 4: 20:9   (0.60) */
      36045, /* 5: 21:9   (0.55) */
      24249  /* 6: 32:9   (0.37) */
   };
   unsigned ws_idx     = (unsigned)widescreen_hack_aspect_ratio_setting;
   int32_t  aspect_q16 = ws_aspect_q16[(ws_idx < 7) ? ws_idx : 1];
   int16_t  tmp;

   if (widescreen_hack)
      SET_MAC(0, F((int64_t)OFX + ((IR1 * h_div_sz * aspect_q16) >> 16)) >> 16);
   else
      SET_MAC(0, F((int64_t)OFX + IR1 * h_div_sz) >> 16);
   tmp = Lm_G(0, MAC(0));

   SET_MAC(0, F((int64_t)OFY + IR2 * h_div_sz) >> 16);

   XY_FIFO(3) = (uint16_t)tmp | ((uint16_t)Lm_G(1, MAC(0)) << 16);

   XY_FIFO(0) = XY_FIFO(1);
   XY_FIFO(1) = XY_FIFO(2);
   XY_FIFO(2) = XY_FIFO(3);

   /* PGXP hack to add subpixel precision as well.
    *
    * Gated on gMode. With PGXP off this whole block was still executing
    * on every transformed vertex: two conversions and a divide for the
    * screen offsets, the widescreen ratio, two multiply-adds, two
    * clamps, and then a call into PGXP_pushSXYZ2f which shuffles the
    * three-deep vertex FIFO and calls PGXP_CacheVertex - all so the
    * callee could check the mode and drop the result. The mode test
    * lived one level too deep.
    *
    * Nothing reads the shadow while gMode is zero: gpu_polygon.c selects
    * its DrawPolygon specialisation on the compile-time PGXP_LIT
    * parameter, so the code that would consult the FIFO is not the code
    * that runs. And because pgxp_precise_z and pgxp_precise_h_div_sz are
    * side-effect free in a non-PGXP_DIAG build, the compiler is free to
    * sink their work into this branch too, which takes the int64 to
    * double conversion and the projection divide off the path as well.
    *
    * PGXP_InvalidateVertexFIFO on the off-to-on transition covers the
    * one case this opens up: entries left behind from before PGXP was
    * enabled. They would in practice be rejected anyway, since
    * PGXP_GetVertex matches the stored value against the command word,
    * but relying on that is relying on a coincidence not happening. */
   if (PGXP_GetModes())
   {
      float fofx       = ((float)OFX / (float)(1 << 16));
      float fofy       = ((float)OFY / (float)(1 << 16));
      /* Same ratio as the integer path above, recovered from the Q16
       * constant (exact: aspect_q16 * 2^-16).  The PGXP shadow stays
       * float because precise_h_div_sz is float; only the ratio source
       * is shared so the two paths cannot drift apart. */
      float aspect     = widescreen_hack ? (float)aspect_q16 / 65536.0f : 1.0f;

      /* Increased precision calculation (sub-pixel precision) */
      float precise_x  = fofx + ((float)IR1 * precise_h_div_sz) * aspect;
      float precise_y  = fofy + ((float)IR2 * precise_h_div_sz);

      /* Pack the freshly-written XY_FIFO[3] into a u32 for PGXP. The
       * previous code did `*((uint32*)&XY_FIFO[3])` which is a strict
       * aliasing violation; memcpy is well-defined and modern compilers
       * fold it into a single mov. */
      uint32_t value;
      memcpy(&value, &XY_FIFO(3), sizeof(value));

      /* Clamp precision values to valid range */
      precise_x = float_max(-0x400, float_min(precise_x, 0x3ff));
      precise_y = float_max(-0x400, float_min(precise_y, 0x3ff));

      PGXP_pushSXYZ2f(precise_x, precise_y, precise_z, value);
   }
}


static INLINE void TransformDQ(int64_t h_div_sz)
{
   SET_MAC(0, F((int64_t)DQB + DQA * h_div_sz));
   SET_IR(0, Lm_H(((int64_t)DQB + DQA * h_div_sz) >> 12));
}

/* Turn the raw 44-bit Z accumulator into the float W the PGXP shadow uses
 * for perspective correction.
 *
 * The architectural SZ3 is Lm_D(acc >> 12) stored through a uint16_t, i.e.
 * integer-truncated and saturated to [0, 0xFFFF].  Reading W back out of
 * Z_FIFO(3), as this code used to, therefore threw away every fractional bit
 * of the transform for no reason: the exact value is still sitting in the
 * accumulator one line above the store.  This is not a new deviation from
 * the integer pipeline either - precise_h_div_sz has always been a true
 * divide rather than the GTE's reciprocal table, so precise_x/y already
 * depart from the architectural result deliberately.  All this does is make
 * x, y and w derive from one exact Z instead of a truncated one.
 *
 * Scope, measured rather than assumed.  Both ends of the range are pinned by
 * clamps that this patch leaves alone, so the affected band is exactly
 * z in (H/2, 0xFFFF):
 *
 *   - Below H/2 the float_max floor dominates and the result is bit-identical
 *     to before.  The floor mirrors Divide()'s own overflow behaviour, so the
 *     near-camera case people usually reach for as the motivating example is
 *     in fact a strict no-op here.
 *   - Above 0xFFFF the ceiling is kept on purpose.  Lifting it is a separate
 *     and genuinely behavioural change: past saturation the shadow would stop
 *     disagreeing with the architectural screen coordinates by fractions of a
 *     pixel and start disagreeing by tens of pixels, which is a different risk
 *     class (the 2D-tolerance heuristic, and any game that clipped against its
 *     own saturated result).  That wants its own patch and its own per-game
 *     testing.
 *
 * Inside the band the recovered fraction is worth at most 2/H in relative W
 * error, peaking just past the floor and decaying as 1/z.  For H = 320 that
 * is 0.62% at z ~ 161, which is about one screen pixel of vertex placement
 * error at the edge of a 320-wide frame; by z = 1000 it is under a tenth of
 * a pixel.  Modest, but sub-pixel vertex placement is the entire point of
 * PGXP, so a whole pixel near the clip plane is on-mission.
 *
 * acc is sign-extended to 44 bits by A_MV, so (double)acc is exact (44 < 53
 * mantissa bits) and scaling by 2^-12 is exact as well - verified over 40M
 * random 44-bit accumulators.  The conversion introduces no rounding of its
 * own and is bit-identical across arch, FPU mode and optimisation level; it
 * cannot become a determinism divergence the way a recomputed floating-point
 * transform could.  W moves by at most one SZ3 unit at any vertex (the bound
 * is 1.0 and is attained, not approached: a z just under an integer can round
 * up to it in the narrowing to float).
 *
 * Pre-existing and untouched: H == 0 with a non-positive Z still yields
 * precise_z == 0 and a non-finite precise_h_div_sz downstream, exactly as
 * before.  Fixing that is orthogonal to this patch. */
static INLINE float pgxp_precise_z(int64_t acc)
{
   double z = (double)acc * (1.0 / 4096.0);

#if PGXP_DIAG
   /* Diagnostic build only; see pgxp_gte.h. A default build emits none
    * of this and is byte-identical to a tree without the counters. */
   if ((++pgxp_z_total & 0x3FFFFFu) == 0)
      PGXP_DiagDump();

   /* Now purely informational - it records how much geometry sits past
    * where SZ3 saturates, rather than gating anything. */
   if (z > 65535.0)
   {
      pgxp_z_ceiling++;
      if (z > pgxp_z_ceiling_max)
         pgxp_z_ceiling_max = z;
   }
#endif

   /* No 0xFFFF ceiling. The architectural SZ3 saturates there because it
    * is a uint16_t, and the shadow used to be clamped to match. Matching
    * it was wrong: past saturation the integer path places a vertex
    * (z / 65535) times too far from the projection centre, so the error
    * is proportional rather than bounded - 1% past the clamp is half a
    * pixel, twice past it is 80 pixels for a vertex 80 pixels out, and it
    * grows without limit. Reproducing that in the shadow discards the one
    * correct answer PGXP has.
    *
    * Removing it cannot regress anything: for z <= 65535 the clamp never
    * fired, so the result is bit-identical to before, and the only inputs
    * whose behaviour changes are the ones the old code got wrong. The 2D
    * tolerance heuristic is self-limiting here rather than a hazard - if
    * it is enabled and the corrected position lands far from the integer
    * one, it substitutes the integer coordinate, which is exactly the old
    * behaviour.
    *
    * The float_max floor stays. That one mirrors Divide()'s own overflow
    * behaviour rather than a storage width, so it is not the same kind of
    * artefact. */
   return float_max(H/2.f, (float)z);
}

/* Companion to pgxp_precise_z: the shadow's h/z, with the H == 0 corner
 * handled the way the integer pipeline handles it.
 *
 * precise_z is zero only when H is zero. The floor in pgxp_precise_z is
 * H/2, so any non-zero H keeps it strictly positive; H == 0 with a
 * non-positive view-space Z is the one input that reaches zero, and
 * (float)0 / 0.0f there is a 0/0.
 *
 * That divide was not producing a NaN in the renderer - the
 * float_max/float_min clamp at the end of TransformXY absorbs it, and
 * does so identically under all three float_minmax.h backends (SSE2
 * min_ss/max_ss, C99 fminf/fmaxf, and the ternary fallback all yield the
 * bound), so there was no arch-dependent divergence either. What it did
 * produce is worse than useless in a quieter way: every affected vertex
 * got pinned to the clamp corner at +1023 instead of following the
 * integer path, which puts stray geometry in the frame rather than a
 * visibly broken primitive. The 2D tolerance net in gpu_polygon.c would
 * normally replace such an outlier with the native coordinate, but that
 * option defaults to disabled (psx_pgxp_2d_tol == -1), so in the default
 * configuration nothing catches it.
 *
 * Divide() answers this same corner by clipping: with H == 0 and SZ3 == 0
 * the `(divisor * 2) > dividend` test fails, FLAGS bit 17 is raised and
 * it returns the saturated 0x1FFFF. Returning that value scaled out of
 * Q16 keeps the shadow on the integer path's answer instead of inventing
 * one, and incidentally retires a latent -fsanitize=float-divide-by-zero
 * trip in the sweep. */
static INLINE float pgxp_precise_h_div_sz(float precise_z)
{
   if (precise_z > 0.0f)
      return (float)H / precise_z;

   return (float)0x1FFFF / 65536.0f;
}

static int32_t RTPS(uint32_t instr)
{
 int64_t h_div_sz;
 int64_t precise_z_acc;
 float precise_z;
 float precise_h_div_sz;
 DECODE_FIELDS;

 MultiplyMatrixByVector_PT(Matrices_Rot, 0, CRVectors_T, sf, lm, &precise_z_acc);
 h_div_sz = Divide(H, Z_FIFO(3));

 precise_z = pgxp_precise_z(precise_z_acc);
 precise_h_div_sz  = pgxp_precise_h_div_sz(precise_z);

 TransformXY(h_div_sz, precise_h_div_sz, precise_z);
 TransformDQ(h_div_sz);

 return(15);
}

static int32_t RTPT(uint32_t instr)
{
 int i;
 DECODE_FIELDS;

 for(i = 0; i < 3; i++)
 {
  int64_t h_div_sz;
  int64_t precise_z_acc;
  float precise_z;
  float precise_h_div_sz;

  MultiplyMatrixByVector_PT(Matrices_Rot, i, CRVectors_T, sf, lm, &precise_z_acc);
  h_div_sz = Divide(H, Z_FIFO(3));

  precise_z = pgxp_precise_z(precise_z_acc);
  precise_h_div_sz  = pgxp_precise_h_div_sz(precise_z);

  TransformXY(h_div_sz, precise_h_div_sz, precise_z);

  if(i == 2)
   TransformDQ(h_div_sz);
 }

 return(23);
}

static INLINE void NormColor(uint32_t sf, int lm, uint32_t v)
{
   MultiplyMatrixByVector(Matrices_Light, v, CRVectors_Null, sf, lm);

   MultiplyMatrixByVector(Matrices_Color, VEC3IR, CRVectors_B, sf, lm);

   MAC_to_RGB_FIFO();
}

static int32_t NCS(uint32_t instr)
{
   const uint32_t sf = (instr & (1 << 19)) ? 12 : 0;
   const int      lm = (instr >> 10) & 1;

   NormColor(sf, lm, 0);

   return(14);
}

static int32_t NCT(uint32_t instr)
{
   const uint32_t sf = (instr & (1 << 19)) ? 12 : 0;
   const int      lm = (instr >> 10) & 1;

   NormColor(sf, lm, 0);
   NormColor(sf, lm, 1);
   NormColor(sf, lm, 2);

   return(30);
}

/* NCC - Normal Color Color */
static INLINE void NCC(uint32_t vector_index, uint32_t sf, int lm)
{
   MultiplyMatrixByVector(Matrices_Light, vector_index, CRVectors_Null, sf, lm);
   MultiplyMatrixByVector(Matrices_Color, VEC3IR, CRVectors_B, sf, lm);

   SET_MAC(1, ((RGB_R << 4) * IR1) >> sf);
   SET_MAC(2, ((RGB_G << 4) * IR2) >> sf);
   SET_MAC(3, ((RGB_B << 4) * IR3) >> sf);

   MAC_to_IR(lm);
   MAC_to_RGB_FIFO();
}

static int32_t NCCS(uint32_t instr)
{
   const uint32_t sf = (instr & (1 << 19)) ? 12 : 0;
   const int      lm = (instr >> 10) & 1;

   NCC(0, sf, lm);
   return(17);
}


static int32_t NCCT(uint32_t instr)
{
   const uint32_t sf = (instr & (1 << 19)) ? 12 : 0;
   const int      lm = (instr >> 10) & 1;

   NCC(0, sf, lm);
   NCC(1, sf, lm);
   NCC(2, sf, lm);

   return(39);
}

static INLINE void DPC(uint32_t instr)
{
   int i;
   int32_t RGB_temp[3];
   const uint32_t sf   = (instr & (1 << 19)) ? 12 : 0;
   const int      lm   = (instr >> 10) & 1;

   RGB_temp[0] = RGB_FIFO_R(0) << 4;
   RGB_temp[1] = RGB_FIFO_G(0) << 4;
   RGB_temp[2] = RGB_FIFO_B(0) << 4;

   for(i = 0; i < 3; i++)
   {
      SET_MAC(1 + i, i64_to_i44(i, ((int64_t)((uint64_t)(int64_t)(int32_t)CR[CRV_FC+i] << 12) - (int32_t)((uint32_t)RGB_temp[i] << 12))) >> sf);
      SET_MAC(1 + i, i64_to_i44(i, ((int64_t)((uint64_t)(int64_t)RGB_temp[i] << 12) + IR0 * i32_to_i16_saturate(i, MAC(1 + i), false))) >> sf);
   }

   MAC_to_IR(lm);

   /* Cue recorded only when the op ran with the standard shift and inside
    * the saturation-free region: with sf == 0 the first stage saturates on
    * everything and with an out-of-i16 difference the hardware clamps the
    * step, so the renderer's unsaturated mix would diverge from the console
    * at the very endpoints the feature promises to match. Byte-range differences cannot clamp here; the check is uniformity.
    * Skipping the cue degrades to the architectural post-fog bytes. */
   if (sf)
   {
      int fog_ok = 1;
      { int fi; for (fi = 0; fi < 3; fi++) {
         int64_t fog_diff = ((int64_t)((uint64_t)(int64_t)(int32_t)CR[CRV_FC+fi] << 12) - (int64_t)((uint32_t)RGB_temp[fi] << 12));
         if ((fog_diff >> 12) > 32767 || (fog_diff >> 12) < -32768)
            fog_ok = 0;
      } }
      if (fog_ok)
      {
      /* Depth-cue sidecar for linear-light fog at the renderer: the exact
       * pre-cue colour, far colour and blend factor, in 8-bit-scale floats.
       * RGB_temp is the FIFO byte <<4, so /16 is the byte. FC is 1.27.4 so /16 lands on 8-bit scale; t is IR0/4096.
       * Consumed by the next PGXP_pushRGBf (inside MAC_to_RGB_FIFO). */
      PGXP_GTE_SetFogContext((float)RGB_temp[0] / 16.0f,
            (float)RGB_temp[1] / 16.0f,
            (float)RGB_temp[2] / 16.0f,
            (float)(int32_t)CR[CRV_FC]     / 16.0f,
            (float)(int32_t)CR[CRV_FC + 1] / 16.0f,
            (float)(int32_t)CR[CRV_FC + 2] / 16.0f,
            (float)IR0 / 4096.0f);
      }
   }
   MAC_to_RGB_FIFO();
}


/* DCPL - Depth Cue Color Light */
static int32_t DCPL(uint32_t instr)
{
   int i;
   int32_t RGB_temp[3];
   int32_t IR_temp[3] = { IR1, IR2, IR3 };
   const uint32_t sf  = (instr & (1 << 19)) ? 12 : 0;
   const int      lm  = (instr >> 10) & 1;

   RGB_temp[0] = RGB_R << 4;
   RGB_temp[1] = RGB_G << 4;
   RGB_temp[2] = RGB_B << 4;

   for(i = 0; i < 3; i++)
   {
      SET_MAC(1 + i, i64_to_i44(i, ((int64_t)((uint64_t)(int64_t)(int32_t)CR[CRV_FC+i] << 12) - RGB_temp[i] * IR_temp[i])) >> sf);
      SET_MAC(1 + i, i64_to_i44(i, (RGB_temp[i] * IR_temp[i] + IR0 * i32_to_i16_saturate(i, MAC(1 + i), false))) >> sf);
   }

   MAC_to_IR(lm);

   /* Cue recorded only when the op ran with the standard shift and inside
    * the saturation-free region: with sf == 0 the first stage saturates on
    * everything and with an out-of-i16 difference the hardware clamps the
    * step, so the renderer's unsaturated mix would diverge from the console
    * at the very endpoints the feature promises to match. DCPL's lit term reaches the clamp with bright over-range IR.
    * Skipping the cue degrades to the architectural post-fog bytes. */
   if (sf)
   {
      int fog_ok = 1;
      { int fi; for (fi = 0; fi < 3; fi++) {
         int64_t fog_diff = ((int64_t)((uint64_t)(int64_t)(int32_t)CR[CRV_FC+fi] << 12) - (int64_t)RGB_temp[fi] * IR_temp[fi]);
         if ((fog_diff >> 12) > 32767 || (fog_diff >> 12) < -32768)
            fog_ok = 0;
      } }
      if (fog_ok)
      {
      /* Depth-cue sidecar for linear-light fog at the renderer: the exact
       * pre-cue colour, far colour and blend factor, in 8-bit-scale floats.
       * Pre-cue here is the lit colour RGB*IR: RGB_temp is byte<<4 and IR is 1.3.12, so /65536 (16*4096) lands on 8-bit scale. FC is 1.27.4 so /16 lands on 8-bit scale; t is IR0/4096.
       * Consumed by the next PGXP_pushRGBf (inside MAC_to_RGB_FIFO). */
      PGXP_GTE_SetFogContext((float)((int64_t)RGB_temp[0] * IR_temp[0]) / 65536.0f,
            (float)((int64_t)RGB_temp[1] * IR_temp[1]) / 65536.0f,
            (float)((int64_t)RGB_temp[2] * IR_temp[2]) / 65536.0f,
            (float)(int32_t)CR[CRV_FC]     / 16.0f,
            (float)(int32_t)CR[CRV_FC + 1] / 16.0f,
            (float)(int32_t)CR[CRV_FC + 2] / 16.0f,
            (float)IR0 / 4096.0f);
      }
   }
   MAC_to_RGB_FIFO();


   return(8);
}


static int32_t DPCS(uint32_t instr)
{
   int i;
   int32_t RGB_temp[3];
   const uint32_t sf  = (instr & (1 << 19)) ? 12 : 0;
   const int      lm  = (instr >> 10) & 1;

   //assert(sf);
   RGB_temp[0] = RGB_R << 4;
   RGB_temp[1] = RGB_G << 4;
   RGB_temp[2] = RGB_B << 4;

   for(i = 0; i < 3; i++)
   {
      SET_MAC(1 + i, i64_to_i44(i, ((int64_t)((uint64_t)(int64_t)(int32_t)CR[CRV_FC+i] << 12) - (int32_t)((uint32_t)RGB_temp[i] << 12))) >> sf);
      SET_MAC(1 + i, i64_to_i44(i, ((int64_t)((uint64_t)(int64_t)RGB_temp[i] << 12) + IR0 * i32_to_i16_saturate(i, MAC(1 + i), false))) >> sf);
   }

   MAC_to_IR(lm);
   /* Cue recorded only when the op ran with the standard shift and inside
    * the saturation-free region: with sf == 0 the first stage saturates on
    * everything and with an out-of-i16 difference the hardware clamps the
    * step, so the renderer's unsaturated mix would diverge from the console
    * at the very endpoints the feature promises to match. Byte-range differences cannot clamp here; the check is uniformity.
    * Skipping the cue degrades to the architectural post-fog bytes. */
   if (sf)
   {
      int fog_ok = 1;
      { int fi; for (fi = 0; fi < 3; fi++) {
         int64_t fog_diff = ((int64_t)((uint64_t)(int64_t)(int32_t)CR[CRV_FC+fi] << 12) - (int64_t)((uint32_t)RGB_temp[fi] << 12));
         if ((fog_diff >> 12) > 32767 || (fog_diff >> 12) < -32768)
            fog_ok = 0;
      } }
      if (fog_ok)
      {
      /* Depth-cue sidecar for linear-light fog at the renderer: the exact
       * pre-cue colour, far colour and blend factor, in 8-bit-scale floats.
       * RGB_temp is the colour-register byte <<4, so /16 is the byte. FC is 1.27.4 so /16 lands on 8-bit scale; t is IR0/4096.
       * Consumed by the next PGXP_pushRGBf (inside MAC_to_RGB_FIFO). */
      PGXP_GTE_SetFogContext((float)RGB_temp[0] / 16.0f,
            (float)RGB_temp[1] / 16.0f,
            (float)RGB_temp[2] / 16.0f,
            (float)(int32_t)CR[CRV_FC]     / 16.0f,
            (float)(int32_t)CR[CRV_FC + 1] / 16.0f,
            (float)(int32_t)CR[CRV_FC + 2] / 16.0f,
            (float)IR0 / 4096.0f);
      }
   }
   MAC_to_RGB_FIFO();

   return(8);
}

/* DPCT - Depth Cue Triple */
static int32_t DPCT(uint32_t instr)
{
   /* Each call uses the oldest entry in the RGB FIFO
    * and pushes the result at the top so the three calls
    * will process and replace the entire contents of the FIFO. */
   DPC(instr);
   DPC(instr);
   DPC(instr);

   return(17);
}

/* INTPL - Interpolate Between a vector and the far color */

static int32_t INTPL(uint32_t instr)
{
   const uint32_t sf = (instr & (1 << 19)) ? 12 : 0;
   const int      lm = (instr >> 10) & 1;

   SET_MAC(1, i64_to_i44(0, ((int64_t)((uint64_t)(int64_t)(int32_t)CR[CRV_FC] << 12) - (int32_t)((uint32_t)(int32_t)IR1 << 12))) >> sf);
   SET_MAC(2, i64_to_i44(1, ((int64_t)((uint64_t)(int64_t)(int32_t)CR[CRV_FC+1] << 12) - (int32_t)((uint32_t)(int32_t)IR2 << 12))) >> sf);
   SET_MAC(3, i64_to_i44(2, ((int64_t)((uint64_t)(int64_t)(int32_t)CR[CRV_FC+2] << 12) - (int32_t)((uint32_t)(int32_t)IR3 << 12))) >> sf);

   SET_MAC(1, i64_to_i44(0, ((int64_t)((uint64_t)(int64_t)IR1 << 12) + IR0 * i32_to_i16_saturate(0, MAC(1), false)) >> sf));
   SET_MAC(2, i64_to_i44(1, ((int64_t)((uint64_t)(int64_t)IR2 << 12) + IR0 * i32_to_i16_saturate(1, MAC(2), false)) >> sf));
   SET_MAC(3, i64_to_i44(2, ((int64_t)((uint64_t)(int64_t)IR3 << 12) + IR0 * i32_to_i16_saturate(2, MAC(3), false)) >> sf));

   MAC_to_IR(lm);
   /* Same guards as the other cue ops; IR reaches i16 full range, so the
    * difference against FC genuinely can clamp here. */
   if (sf)
   {
      int fog_ok = 1;
      int32_t fog_ir[3];
      fog_ir[0] = IR1; fog_ir[1] = IR2; fog_ir[2] = IR3;
      { int fi; for (fi = 0; fi < 3; fi++) {
         int64_t fog_diff = ((int64_t)((uint64_t)(int64_t)(int32_t)CR[CRV_FC+fi] << 12) - (int64_t)((uint32_t)fog_ir[fi] << 12));
         if ((fog_diff >> 12) > 32767 || (fog_diff >> 12) < -32768)
            fog_ok = 0;
      } }
      if (fog_ok)
      {
      /* Depth-cue sidecar for linear-light fog at the renderer: the exact
       * pre-cue colour, far colour and blend factor, in 8-bit-scale floats.
       * Pre-cue is the IR vector, 1.3.12: /16 lands on 8-bit scale. FC is 1.27.4 so /16 lands on 8-bit scale; t is IR0/4096.
       * Consumed by the next PGXP_pushRGBf (inside MAC_to_RGB_FIFO). */
      PGXP_GTE_SetFogContext((float)IR1 / 16.0f,
            (float)IR2 / 16.0f,
            (float)IR3 / 16.0f,
            (float)(int32_t)CR[CRV_FC]     / 16.0f,
            (float)(int32_t)CR[CRV_FC + 1] / 16.0f,
            (float)(int32_t)CR[CRV_FC + 2] / 16.0f,
            (float)IR0 / 4096.0f);
      }
   }
   MAC_to_RGB_FIFO();

   return(8);
}


static INLINE void NormColorDepthCue(uint32_t instr, uint32_t v)
{
   const uint32_t sf = (instr & (1 << 19)) ? 12 : 0;
   const int      lm = (instr >> 10) & 1;

   MultiplyMatrixByVector(Matrices_Light, v, CRVectors_Null, sf, lm);

   /* Use the custom 4th vector (VEC3IR) that stores the intermediate
    * values. This vector does not exist in the real hardware
    * (at least not in the registers), it's just a hack to make
    * the code simpler. */
   MultiplyMatrixByVector(Matrices_Color, VEC3IR, CRVectors_B, sf, lm);

   DCPL(instr);
}

/* NCDS - Normal Color Depth Cue Single vector */
static int32_t NCDS(uint32_t instr)
{
   NormColorDepthCue(instr, 0);

   return(19);
}

/* NDCT - Normal Color Depth Cue Triple */
static int32_t NCDT(uint32_t instr)
{
   NormColorDepthCue(instr, 0);
   NormColorDepthCue(instr, 1);
   NormColorDepthCue(instr, 2);

   return(44);
}

/* CC - Color Color */
static int32_t CC(uint32_t instr)
{
   const uint32_t     sf = (instr & (1 << 19)) ? 12 : 0;
   const int          lm = (instr >> 10) & 1;

   MultiplyMatrixByVector(Matrices_Color, VEC3IR, CRVectors_B, sf, lm);

   SET_MAC(1, ((RGB_R << 4) * IR1) >> sf);
   SET_MAC(2, ((RGB_G << 4) * IR2) >> sf);
   SET_MAC(3, ((RGB_B << 4) * IR3) >> sf);

   MAC_to_IR(lm);
   MAC_to_RGB_FIFO();

   return(11);
}

static int32_t CDP(uint32_t instr)
{
   const uint32_t sf = (instr & (1 << 19)) ? 12 : 0;
   const int      lm = (instr >> 10) & 1;

   MultiplyMatrixByVector(Matrices_Color, VEC3IR, CRVectors_B, sf, lm);

   DCPL(instr);

   return(13);
}

/* Normal Clipping */
static int32_t NCLIP(uint32_t instr)
{
   int16_t x0     = XY_FIFO_X(0);
   int16_t y0     = XY_FIFO_Y(0);
   int16_t x1     = XY_FIFO_X(1);
   int16_t y1     = XY_FIFO_Y(1);
   int16_t x2     = XY_FIFO_X(2);
   int16_t y2     = XY_FIFO_Y(2);
   int64_t native_sum = (int64_t)x0 * (y1 - y2) +
                        (int64_t)x1 * (y2 - y0) +
                        (int64_t)x2 * (y0 - y1);
   int32_t sum = F(native_sum);

   /* PGXP_NCLIP wants the three XY_FIFO entries packed as u32. The
    * previous code did `*((uint32*)&XY_FIFO[N])` which is a strict
    * aliasing violation; memcpy is well-defined and modern compilers
    * fold it into a single mov per call. */
   if (PGXP_GetModes() & PGXP_NCLIP_IMPL) {
      uint32_t v0, v1, v2;
      memcpy(&v0, &XY_FIFO(0), sizeof(v0));
      memcpy(&v1, &XY_FIFO(1), sizeof(v1));
      memcpy(&v2, &XY_FIFO(2), sizeof(v2));
      if (PGXP_NCLIP_valid(v0, v1, v2))
         sum = PGXP_NCLIP_preserve_magnitude(sum, PGXP_NCLIP_sign());
   }

   SET_MAC(0, sum);

   return(8);
}

/* Average three Z Values */
static int32_t AVSZ3(uint32_t instr)
{
   uint32_t z1     = Z_FIFO(1);
   uint32_t z2     = Z_FIFO(2);
   uint32_t z3     = Z_FIFO(3);
   uint64_t sum    = z1 + z2 + z3;
   /* The average factor should generally be set to 1/3th of 
    * the ordering table size. So for instance, for a table of
    * 1024 entries, it should be set at 341 to use the full
    * table granularity. */
   int64_t zsf3    = ZSF3;
   int64_t average = zsf3 * sum;

   check_mac_overflow(average);

   SET_MAC(0, (int32_t)average);
   SET_OTZ(i64_to_otz(MAC(0), false));

   return(5);
}

/* Average four Z values */
static int32_t AVSZ4(uint32_t instr)
{
   uint32_t z0     = Z_FIFO(0);
   uint32_t z1     = Z_FIFO(1);
   uint32_t z2     = Z_FIFO(2);
   uint32_t z3     = Z_FIFO(3);
   uint64_t sum    = z0 + z1 + z2 + z3;
   /* The average factor should generally be set to 1/4th of
    * the ordering table size. So for instance, for a table of
    * 1024 entries, it should be set at 256 to use the full
    * table granularity. */
   int64_t zsf4    = ZSF4;
   int64_t average = zsf4 * sum;

   check_mac_overflow(average);

   SET_MAC(0, (int32_t)average);
   SET_OTZ(i64_to_otz(MAC(0), false));

   return(5);
}


/* -32768 * -32768 - 32767 * -32768 = 2147450880
 * (2 ^ 31) - 1 =		      2147483647 */
static int32_t OP(uint32_t instr)
{
   const uint32_t sf = (instr & (1 << 19)) ? 12 : 0;
   const int      lm = (instr >> 10) & 1;
   int32_t       ir1 = IR1;
   int32_t       ir2 = IR2;
   int32_t       ir3 = IR3;
   int32_t        r0 = (int16_t)CR[0];
   int32_t        r1 = (int16_t)CR[2];
   int32_t        r2 = (int16_t)CR[4];

   SET_MAC(1, (r1 * ir3 - r2 * ir2) >> sf);
   SET_MAC(2, (r2 * ir1 - r0 * ir3) >> sf);
   SET_MAC(3, (r0 * ir2 - r1 * ir1) >> sf);

   MAC_to_IR(lm);

   return(6);
}

static int32_t GPF(uint32_t instr)
{
   const uint32_t sf = (instr & (1 << 19)) ? 12 : 0;
   const int      lm = (instr >> 10) & 1;

   SET_MAC(1, (IR0 * IR1) >> sf);
   SET_MAC(2, (IR0 * IR2) >> sf);
   SET_MAC(3, (IR0 * IR3) >> sf);

   MAC_to_IR(lm);

   MAC_to_RGB_FIFO();

   return(5);
}

static int32_t GPL(uint32_t instr)
{
   const uint32_t sf = (instr & (1 << 19)) ? 12 : 0;
   const int      lm = (instr >> 10) & 1;

   SET_MAC(1, i64_to_i44(0, (int64_t)((uint64_t)(int64_t)MAC(1) << sf) + (IR0 * IR1)) >> sf);
   SET_MAC(2, i64_to_i44(1, (int64_t)((uint64_t)(int64_t)MAC(2) << sf) + (IR0 * IR2)) >> sf);
   SET_MAC(3, i64_to_i44(2, (int64_t)((uint64_t)(int64_t)MAC(3) << sf) + (IR0 * IR3)) >> sf);

   MAC_to_IR(lm);

   MAC_to_RGB_FIFO();

   return(5);
}

/*

---------------------------------------------------------------------------------------------
| 24 23 22 21 20 | 19 | 18 17 | 16 15 | 14 13 | 12  11 | 10 | 9  8  7  6 | 5  4  3  2  1  0 |
|-------------------------------------------------------------------------------------------|
|    (unused)    | sf |  mx   |   v   |   cv  |(unused)| lm |  (unused)  |     opcode       |
---------------------------------------------------------------------------------------------
 (unused) = unused, ignored

 sf = shift 12

 mx = matrix selection

 v = source vector

 cv = add vector(translation/back/far color(bugged)/none)

 (unused) = unused, ignored

 lm = limit negative results to 0

 (unused) = unused, ignored

 opcode = operation code 
*/

/* GTE opcode dispatch table.  One entry per 6-bit GTE function code; NULL
 * entries are the "no active operation" cases (the historically commented-out
 * / unstable opcodes), which leave ret at its default of 1.  The aliases that
 * the original switch encoded by case fall-through are reproduced explicitly:
 *   0x00 -> RTPS (alternate of 0x01)
 *   0x1A -> DCPL (alternate of 0x29)
 * This table is the single source of truth for both GTE_Instruction() and the
 * recompiler entry point GTE_ExecuteOp(); keeping the mapping in data rather
 * than a switch lets the dynarec resolve a GTE function to a direct handler at
 * block-compile time instead of re-dispatching the 6-bit code at run time. */
typedef int32_t (*gte_op_func)(uint32_t instr);

static const gte_op_func gte_op_table[0x40] =
{
   /* 0x00 */ RTPS,  RTPS,  NULL,  NULL,  NULL,  NULL,  NCLIP, NULL,
   /* 0x08 */ NULL,  NULL,  NULL,  NULL,  OP,    NULL,  NULL,  NULL,
   /* 0x10 */ DPCS,  INTPL, MVMVA, NCDS,  CDP,   NULL,  NCDT,  NULL,
   /* 0x18 */ NULL,  NULL,  DCPL,  NCCS,  CC,    NULL,  NCS,   NULL,
   /* 0x20 */ NCT,   NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,
   /* 0x28 */ SQR,   DCPL,  DPCT,  NULL,  NULL,  AVSZ3, AVSZ4, NULL,
   /* 0x30 */ RTPT,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,
   /* 0x38 */ NULL,  NULL,  NULL,  NULL,  NULL,  GPF,   GPL,   NCCT
};

/* Execute a single GTE operation by 6-bit function code.  Returns the op's
 * raw latency value (the "cycles" figure, before the -1 adjustment and before
 * the psx_gte_overclock override that GTE_Instruction applies).  Does NOT
 * touch the FLAGS checksum / CR[31] writeback or reset FLAGS: callers that
 * need full GTE_Instruction semantics must do that themselves (GTE_Instruction
 * does; the recompiler emits the equivalent epilogue inline).  A code with no
 * active operation returns 1, matching the original switch's default. */
int32_t GTE_ExecuteOp(unsigned code, uint32_t instr)
{
   gte_op_func f = gte_op_table[code & 0x3f];

   if (f)
      return (*f)(instr);

   return 1;
}

int32_t GTE_Instruction(uint32_t instr)
{
   const unsigned code = instr & 0x3F;
   int32_t ret;

   FLAGS = 0;

   ret = GTE_ExecuteOp(code, instr);

   /* Overclock: force all GTE instruction to have 1 cycle latency */
   if (psx_gte_overclock)
      ret = 1;

   if(FLAGS & 0x7f87e000)
      FLAGS |= 1u << 31;

   CR[31] = FLAGS;

   return(ret - 1);
}
