/*Registers :

  alphaMode
  fbzMode & 0x1f3fff
  fbzColorPath
*/

#ifndef VIDEO_VOODOO_CODEGEN_X86_64_H
#define VIDEO_VOODOO_CODEGEN_X86_64_H

#include <xmmintrin.h>

#define BLOCK_NUM  8
#define BLOCK_MASK (BLOCK_NUM - 1)
#define BLOCK_SIZE 8192

#define LOD_MASK   (LOD_TMIRROR_S | LOD_TMIRROR_T)

/* Suppress a false positive warning on gcc that causes excessive build log spam */
#if __GNUC__ >= 10
#    pragma GCC diagnostic ignored "-Wstringop-overflow"
#endif

typedef struct voodoo_x86_data_t {
    uint8_t  code_block[BLOCK_SIZE];
    int      xdir;
    uint32_t alphaMode;
    uint32_t fbzMode;
    uint32_t fogMode;
    uint32_t fbzColorPath;
    uint32_t textureMode[2];
    uint32_t tLOD[2];
    uint32_t trexInit1;
    int      is_tiled;
} voodoo_x86_data_t;

#if 0
static voodoo_x86_data_t voodoo_x86_data[2][BLOCK_NUM];
#endif

static int last_block[4]          = { 0, 0 };
static int next_block_to_write[4] = { 0, 0 };

#define addbyte(val)                   \
    do {                               \
        code_block[block_pos++] = val; \
    } while (0)

#define addword(val)                                \
    do {                                            \
        *(uint16_t *) &code_block[block_pos] = val; \
        block_pos += 2;                             \
    } while (0)

#define addlong(val)                                \
    do {                                            \
        *(uint32_t *) &code_block[block_pos] = val; \
        block_pos += 4;                             \
    } while (0)

#define addquad(val)                                \
    do {                                            \
        *(uint64_t *) &code_block[block_pos] = val; \
        block_pos += 8;                             \
    } while (0)

#define VOODOO_OFFSETOF_ARRAY(type, field, index) \
    (offsetof(type, field[0]) + ((index) * sizeof(((type *) 0)->field[0])))

#define VOODOO_OFFSETOF_ARRAY_MEMBER(type, field, index, member) \
    (offsetof(type, field[0].member) + ((index) * sizeof(((type *) 0)->field[0])))

static __m128i xmm_01_w; // = 0x0001000100010001ull;
static __m128i xmm_ff_w; // = 0x00ff00ff00ff00ffull;
static __m128i xmm_ff_b; // = 0x00000000ffffffffull;

static __m128i  alookup[257];
static __m128i  aminuslookup[256];
static __m128i  minus_254; // = 0xff02ff02ff02ff02ull;
static __m128i  bilinear_lookup[256 * 2];
static __m128i  xmm_00_ff_w[2];
static uint32_t i_00_ff_w[2] = { 0, 0xff };

static inline int
codegen_texture_fetch(uint8_t *code_block, voodoo_t *voodoo, voodoo_params_t *params, voodoo_state_t *state, int block_pos, int tmu)
{
    if (params->textureMode[tmu] & 1) {
        addbyte(0x48); /*MOV RBX, state->tmu0_s*/
        addbyte(0x8b);
        addbyte(0x9f);
        addlong(tmu ? offsetof(voodoo_state_t, tmu1_s) : offsetof(voodoo_state_t, tmu0_s));
        addbyte(0x48); /*MOV RAX, (1 << 48)*/
        addbyte(0xb8);
        addquad(1ULL << 48);
        addbyte(0x48); /*XOR RDX, RDX*/
        addbyte(0x31);
        addbyte(0xd2);
        addbyte(0x48); /*MOV RCX, state->tmu0_t*/
        addbyte(0x8b);
        addbyte(0x8f);
        addlong(tmu ? offsetof(voodoo_state_t, tmu1_t) : offsetof(voodoo_state_t, tmu0_t));
        addbyte(0x48); /*CMP state->tmu_w, 0*/
        addbyte(0x83);
        addbyte(0xbf);
        addlong(tmu ? offsetof(voodoo_state_t, tmu1_w) : offsetof(voodoo_state_t, tmu0_w));
        addbyte(0);
        addbyte(0x74); /*JZ +*/
        addbyte(7);
        addbyte(0x48); /*IDIV state->tmu_w*/
        addbyte(0xf7);
        addbyte(0xbf);
        addlong(tmu ? offsetof(voodoo_state_t, tmu1_w) : offsetof(voodoo_state_t, tmu0_w));
        addbyte(0x48); /*SAR RBX, 14*/
        addbyte(0xc1);
        addbyte(0xfb);
        addbyte(14);
        addbyte(0x48); /*SAR RCX, 14*/
        addbyte(0xc1);
        addbyte(0xf9);
        addbyte(14);
        addbyte(0x48); /*IMUL RBX, RAX*/
        addbyte(0x0f);
        addbyte(0xaf);
        addbyte(0xd8);
        addbyte(0x48); /*IMUL RCX, RAX*/
        addbyte(0x0f);
        addbyte(0xaf);
        addbyte(0xc8);
        addbyte(0x48); /*SAR RBX, 30*/
        addbyte(0xc1);
        addbyte(0xfb);
        addbyte(30);
        addbyte(0x48); /*SAR RCX, 30*/
        addbyte(0xc1);
        addbyte(0xf9);
        addbyte(30);
        addbyte(0x48); /*BSR EDX, RAX*/
        addbyte(0x0f);
        addbyte(0xbd);
        addbyte(0xd0);
        addbyte(0x48); /*SHL RAX, 8*/
        addbyte(0xc1);
        addbyte(0xe0);
        addbyte(8);
        addbyte(0x89); /*MOV state->tex_t, ECX*/
        addbyte(0x8f);
        addlong(offsetof(voodoo_state_t, tex_t));
        addbyte(0x89); /*MOV ECX, EDX*/
        addbyte(0xd1);
        addbyte(0x83); /*SUB EDX, 19*/
        addbyte(0xea);
        addbyte(19);
        addbyte(0x48); /*SHR RAX, CL*/
        addbyte(0xd3);
        addbyte(0xe8);
        addbyte(0xc1); /*SHL EDX, 8*/
        addbyte(0xe2);
        addbyte(8);
        addbyte(0x25); /*AND EAX, 0xff*/
        addlong(0xff);
        addbyte(0x89); /*MOV state->tex_s, EBX*/
        addbyte(0x9f);
        addlong(offsetof(voodoo_state_t, tex_s));
        addbyte(0x41); /*MOVZX EAX, R9(logtable)[RAX]*/
        addbyte(0x0f);
        addbyte(0xb6);
        addbyte(0x04);
        addbyte(0x01);
        addbyte(0x09); /*OR EAX, EDX*/
        addbyte(0xd0);
        addbyte(0x03); /*ADD EAX, state->lod*/
        addbyte(0x87);
        addlong(VOODOO_OFFSETOF_ARRAY_MEMBER(voodoo_state_t, tmu, tmu, lod));
        addbyte(0x3b); /*CMP EAX, state->lod_min*/
        addbyte(0x87);
        addlong(VOODOO_OFFSETOF_ARRAY(voodoo_state_t, lod_min, tmu));
        addbyte(0x0f); /*CMOVL EAX, state->lod_min*/
        addbyte(0x4c);
        addbyte(0x87);
        addlong(VOODOO_OFFSETOF_ARRAY(voodoo_state_t, lod_min, tmu));
        addbyte(0x3b); /*CMP EAX, state->lod_max*/
        addbyte(0x87);
        addlong(VOODOO_OFFSETOF_ARRAY(voodoo_state_t, lod_max, tmu));
        addbyte(0x0f); /*CMOVNL EAX, state->lod_max*/
        addbyte(0x4d);
        addbyte(0x87);
        addlong(VOODOO_OFFSETOF_ARRAY(voodoo_state_t, lod_max, tmu));
        addbyte(0xc1); /*SHR EAX, 8*/
        addbyte(0xe8);
        addbyte(8);
        addbyte(0x89); /*MOV state->lod, EAX*/
        addbyte(0x87);
        addlong(offsetof(voodoo_state_t, lod));
    } else {
        addbyte(0x48); /*MOV RAX, state->tmu0_s*/
        addbyte(0x8b);
        addbyte(0x87);
        addlong(tmu ? offsetof(voodoo_state_t, tmu1_s) : offsetof(voodoo_state_t, tmu0_s));
        addbyte(0x48); /*MOV RCX, state->tmu0_t*/
        addbyte(0x8b);
        addbyte(0x8f);
        addlong(tmu ? offsetof(voodoo_state_t, tmu1_t) : offsetof(voodoo_state_t, tmu0_t));
        addbyte(0x48); /*SHR RAX, 28*/
        addbyte(0xc1);
        addbyte(0xe8);
        addbyte(28);
        addbyte(0x8b); /*MOV EBX, state->lod_min*/
        addbyte(0x9f);
        addlong(VOODOO_OFFSETOF_ARRAY(voodoo_state_t, lod_min, tmu));
        addbyte(0x48); /*SHR RCX, 28*/
        addbyte(0xc1);
        addbyte(0xe9);
        addbyte(28);
        addbyte(0x48); /*MOV state->tex_s, RAX*/
        addbyte(0x89);
        addbyte(0x87);
        addlong(offsetof(voodoo_state_t, tex_s));
        addbyte(0xc1); /*SHR EBX, 8*/
        addbyte(0xeb);
        addbyte(8);
        addbyte(0x48); /*MOV state->tex_t, RCX*/
        addbyte(0x89);
        addbyte(0x8f);
        addlong(offsetof(voodoo_state_t, tex_t));
        addbyte(0x89); /*MOV state->lod, EBX*/
        addbyte(0x9f);
        addlong(offsetof(voodoo_state_t, lod));
    }

    if (params->fbzColorPath & FBZCP_TEXTURE_ENABLED) {
        if (voodoo->bilinear_enabled && (params->textureMode[tmu] & 6)) {
            addbyte(0xb2); /*MOV DL, 8*/
            addbyte(8);
            addbyte(0x8b); /*MOV ECX, state->lod[RDI]*/
            addbyte(0x8f);
            addlong(offsetof(voodoo_state_t, lod));
            addbyte(0xbd); /*MOV EBP, 1*/
            addlong(1);
            addbyte(0x28); /*SUB DL, CL*/
            addbyte(0xca);
#if 0
            addbyte(0x8a); /*MOV DL, params->tex_shift[RSI+ECX*4]*/
            addbyte(0x94);
            addbyte(0x8e);
            addlong(offsetof(voodoo_params_t, tex_shift));
#endif
            addbyte(0xd3); /*SHL EBP, CL*/
            addbyte(0xe5);
            addbyte(0x8b); /*MOV EAX, state->tex_s[RDI]*/
            addbyte(0x87);
            addlong(offsetof(voodoo_state_t, tex_s));
            addbyte(0xc1); /*SHL EBP, 3*/
            addbyte(0xe5);
            addbyte(3);
            addbyte(0x8b); /*MOV EBX, state->tex_t[RDI]*/
            addbyte(0x9f);
            addlong(offsetof(voodoo_state_t, tex_t));
            if (params->tLOD[tmu] & LOD_TMIRROR_S) {
                addbyte(0xa9); /*TEST EAX, 0x1000*/
                addlong(0x1000);
                addbyte(0x74); /*JZ +*/
                addbyte(2);
                addbyte(0xf7); /*NOT EAX*/
                addbyte(0xd0);
            }
            if (params->tLOD[tmu] & LOD_TMIRROR_T) {
                addbyte(0xf7); /*TEST EBX, 0x1000*/
                addbyte(0xc3);
                addlong(0x1000);
                addbyte(0x74); /*JZ +*/
                addbyte(2);
                addbyte(0xf7); /*NOT EBX*/
                addbyte(0xd3);
            }
            addbyte(0x29); /*SUB EAX, EBP*/
            addbyte(0xe8);
            addbyte(0x29); /*SUB EBX, EBP*/
            addbyte(0xeb);
            addbyte(0xd3); /*SAR EAX, CL*/
            addbyte(0xf8);
            addbyte(0xd3); /*SAR EBX, CL*/
            addbyte(0xfb);
            addbyte(0x89); /*MOV EBP, EAX*/
            addbyte(0xc5);
            addbyte(0x89); /*MOV ECX, EBX*/
            addbyte(0xd9);
            addbyte(0x83); /*AND EBP, 0xf*/
            addbyte(0xe5);
            addbyte(0xf);
            addbyte(0xc1); /*SHL ECX, 4*/
            addbyte(0xe1);
            addbyte(4);
            addbyte(0xc1); /*SAR EAX, 4*/
            addbyte(0xf8);
            addbyte(4);
            addbyte(0x81); /*AND ECX, 0xf0*/
            addbyte(0xe1);
            addlong(0xf0);
            addbyte(0xc1); /*SAR EBX, 4*/
            addbyte(0xfb);
            addbyte(4);
            addbyte(0x09); /*OR EBP, ECX*/
            addbyte(0xcd);
            addbyte(0x8b); /*MOV ECX, state->lod[RDI]*/
            addbyte(0x8f);
            addlong(offsetof(voodoo_state_t, lod));
            addbyte(0xc1); /*SHL EBP, 5*/
            addbyte(0xe5);
            addbyte(5);
            /*EAX = S, EBX = T, ECX = LOD, EDX = tex_shift, ESI=params, EDI=state, EBP = bilinear shift*/
            addbyte(0x48); /*LEA RSI, [RSI+RCX*4]*/
            addbyte(0x8d);
            addbyte(0x34);
            addbyte(0x8e);
            addbyte(0x89); /*MOV ebp_store, EBP*/
            addbyte(0xaf);
            addlong(offsetof(voodoo_state_t, ebp_store));
            addbyte(0x48); /*MOV RBP, state->tex[RDI+RCX*8]*/
            addbyte(0x8b);
            addbyte(0xac);
            addbyte(0xcf);
            addlong(VOODOO_OFFSETOF_ARRAY(voodoo_state_t, tex, tmu));
            addbyte(0x88); /*MOV CL, DL*/
            addbyte(0xd1);
            addbyte(0x89); /*MOV EDX, EBX*/
            addbyte(0xda);
            if (!state->clamp_s[tmu]) {
                addbyte(0x23); /*AND EAX, params->tex_w_mask[ESI]*/
                addbyte(0x86);
                addlong(VOODOO_OFFSETOF_ARRAY(voodoo_params_t, tex_w_mask, tmu));
            }
            addbyte(0x83); /*ADD EDX, 1*/
            addbyte(0xc2);
            addbyte(1);
            if (state->clamp_t[tmu]) {
                addbyte(0x41); /*CMOVS EDX, R10(alookup[0](zero))*/
                addbyte(0x0f);
                addbyte(0x48);
                addbyte(0x12);
                addbyte(0x3b); /*CMP EDX, params->tex_h_mask[ESI]*/
                addbyte(0x96);
                addlong(VOODOO_OFFSETOF_ARRAY(voodoo_params_t, tex_h_mask, tmu));
                addbyte(0x0f); /*CMOVA EDX, params->tex_h_mask[ESI]*/
                addbyte(0x47);
                addbyte(0x96);
                addlong(VOODOO_OFFSETOF_ARRAY(voodoo_params_t, tex_h_mask, tmu));
                addbyte(0x85); /*TEST EBX,EBX*/
                addbyte(0xdb);
                addbyte(0x41); /*CMOVS EBX, R10(alookup[0](zero))*/
                addbyte(0x0f);
                addbyte(0x48);
                addbyte(0x1a);
                addbyte(0x3b); /*CMP EBX, params->tex_h_mask[ESI]*/
                addbyte(0x9e);
                addlong(VOODOO_OFFSETOF_ARRAY(voodoo_params_t, tex_h_mask, tmu));
                addbyte(0x0f); /*CMOVA EBX, params->tex_h_mask[ESI]*/
                addbyte(0x47);
                addbyte(0x9e);
                addlong(VOODOO_OFFSETOF_ARRAY(voodoo_params_t, tex_h_mask, tmu));
            } else {
                addbyte(0x23); /*AND EDX, params->tex_h_mask[ESI]*/
                addbyte(0x96);
                addlong(VOODOO_OFFSETOF_ARRAY(voodoo_params_t, tex_h_mask, tmu));
                addbyte(0x23); /*AND EBX, params->tex_h_mask[ESI]*/
                addbyte(0x9e);
                addlong(VOODOO_OFFSETOF_ARRAY(voodoo_params_t, tex_h_mask, tmu));
            }
            /*EAX = S, EBX = T0, EDX = T1*/
            addbyte(0xd3); /*SHL EBX, CL*/
            addbyte(0xe3);
            addbyte(0xd3); /*SHL EDX, CL*/
            addbyte(0xe2);
            addbyte(0x48); /*LEA RBX,[RBP+RBX*4]*/
            addbyte(0x8d);
            addbyte(0x5c);
            addbyte(0x9d);
            addbyte(0);
            addbyte(0x48); /*LEA RDX,[RBP+RDX*4]*/
            addbyte(0x8d);
            addbyte(0x54);
            addbyte(0x95);
            addbyte(0);
            if (state->clamp_s[tmu]) {
                addbyte(0x8b); /*MOV EBP, params->tex_w_mask[ESI]*/
                addbyte(0xae);
                addlong(VOODOO_OFFSETOF_ARRAY(voodoo_params_t, tex_w_mask, tmu));
                addbyte(0x85); /*TEST EAX, EAX*/
                addbyte(0xc0);
                addbyte(0x8b); /*MOV ebp_store2, RSI*/
                addbyte(0xb7);
                addlong(offsetof(voodoo_state_t, ebp_store));
                addbyte(0x41); /*CMOVS EAX, R10(alookup[0](zero))*/
                addbyte(0x0f);
                addbyte(0x48);
                addbyte(0x02);
                addbyte(0x78); /*JS + - clamp on 0*/
                addbyte(2 + 3 + 2 + 5 + 5 + 2);
                addbyte(0x3b); /*CMP EAX, EBP*/
                addbyte(0xc5);
                addbyte(0x0f); /*CMOVAE EAX, EBP*/
                addbyte(0x43);
                addbyte(0xc5);
                addbyte(0x73); /*JAE + - clamp on +*/
                addbyte(5 + 5 + 2);
            } else {
                addbyte(0x3b); /*CMP EAX, params->tex_w_mask[ESI] - is S at texture edge (ie will wrap/clamp)?*/
                addbyte(0x86);
                addlong(VOODOO_OFFSETOF_ARRAY(voodoo_params_t, tex_w_mask, tmu));
                addbyte(0x8b); /*MOV ebp_store2, ESI*/
                addbyte(0xb7);
                addlong(offsetof(voodoo_state_t, ebp_store));
                addbyte(0x74); /*JE +*/
                addbyte(5 + 5 + 2);
            }

            addbyte(0xf3); /*MOVQ XMM0, [RBX+RAX*4]*/
            addbyte(0x0f);
            addbyte(0x7e);
            addbyte(0x04);
            addbyte(0x83);
            addbyte(0xf3); /*MOVQ XMM1, [RDX+RAX*4]*/
            addbyte(0x0f);
            addbyte(0x7e);
            addbyte(0x0c);
            addbyte(0x82);

            if (state->clamp_s[tmu]) {
                addbyte(0xeb); /*JMP +*/
                addbyte(5 + 5 + 4 + 4);

                /*S clamped - the two S coordinates are the same*/
                addbyte(0x66); /*MOVD XMM0, [RBX+RAX*4]*/
                addbyte(0x0f);
                addbyte(0x6e);
                addbyte(0x04);
                addbyte(0x83);
                addbyte(0x66); /*MOVD XMM1, [RDX+RAX*4]*/
                addbyte(0x0f);
                addbyte(0x6e);
                addbyte(0x0c);
                addbyte(0x82);
                addbyte(0x66); /*PUNPCKLDQ XMM0, XMM0*/
                addbyte(0x0f);
                addbyte(0x62);
                addbyte(0xc0);
                addbyte(0x66); /*PUNPCKLDQ XMM1, XMM1*/
                addbyte(0x0f);
                addbyte(0x62);
                addbyte(0xc9);
            } else {
                addbyte(0xeb); /*JMP +*/
                addbyte(5 + 5 + 5 + 5 + 6 + 6);

                /*S wrapped - the two S coordinates are not contiguous*/
                addbyte(0x66); /*MOVD XMM0, [RBX+EAX*4]*/
                addbyte(0x0f);
                addbyte(0x6e);
                addbyte(0x04);
                addbyte(0x83);
                addbyte(0x66); /*MOVD XMM1, [RDX+EAX*4]*/
                addbyte(0x0f);
                addbyte(0x6e);
                addbyte(0x0c);
                addbyte(0x82);
                addbyte(0x66); /*PINSRW XMM0, [RBX], 2*/
                addbyte(0x0f);
                addbyte(0xc4);
                addbyte(0x03);
                addbyte(0x02);
                addbyte(0x66); /*PINSRW XMM1, [RDX], 2*/
                addbyte(0x0f);
                addbyte(0xc4);
                addbyte(0x0a);
                addbyte(0x02);
                addbyte(0x66); /*PINSRW XMM0, 2[RBX], 3*/
                addbyte(0x0f);
                addbyte(0xc4);
                addbyte(0x43);
                addbyte(0x02);
                addbyte(0x03);
                addbyte(0x66); /*PINSRW XMM1, 2[RDX], 3*/
                addbyte(0x0f);
                addbyte(0xc4);
                addbyte(0x4a);
                addbyte(0x02);
                addbyte(0x03);
            }

            addbyte(0x49); /*MOV R8, bilinear_lookup*/
            addbyte(0xb8);
            addquad((uintptr_t) bilinear_lookup);

            addbyte(0x66); /*PUNPCKLBW XMM0, XMM2*/
            addbyte(0x0f);
            addbyte(0x60);
            addbyte(0xc2);
            addbyte(0x66); /*PUNPCKLBW XMM1, XMM2*/
            addbyte(0x0f);
            addbyte(0x60);
            addbyte(0xca);

            addbyte(0x4c); /*ADD RSI, R8*/
            addbyte(0x01);
            addbyte(0xc6);

            addbyte(0x66); /*PMULLW XMM0, bilinear_lookup[ESI]*/
            addbyte(0x0f);
            addbyte(0xd5);
            addbyte(0x06);
            addbyte(0x66); /*PMULLW XMM1, bilinear_lookup[ESI]+0x10*/
            addbyte(0x0f);
            addbyte(0xd5);
            addbyte(0x4e);
            addbyte(0x10);
            addbyte(0x66); /*PADDW XMM0, XMM1*/
            addbyte(0x0f);
            addbyte(0xfd);
            addbyte(0xc0 | 1 | (0 << 3));
            addbyte(0x66); /*MOV XMM1, XMM0*/
            addbyte(0x0f);
            addbyte(0x6f);
            addbyte(0xc0 | 0 | (1 << 3));
            addbyte(0x66); /*PSRLDQ XMM0, 64*/
            addbyte(0x0f);
            addbyte(0x73);
            addbyte(0xd8);
            addbyte(8);
            addbyte(0x66); /*PADDW XMM0, XMM1*/
            addbyte(0x0f);
            addbyte(0xfd);
            addbyte(0xc0 | 1 | (0 << 3));
            addbyte(0x66); /*PSRLW XMM0, 8*/
            addbyte(0x0f);
            addbyte(0x71);
            addbyte(0xd0 | 0);
            addbyte(8);
            addbyte(0x66); /*PACKUSWB XMM0, XMM0*/
            addbyte(0x0f);
            addbyte(0x67);
            addbyte(0xc0);

            addbyte(0x4c); /*MOV RSI, R15*/
            addbyte(0x89);
            addbyte(0xfe);

            addbyte(0x66); /*MOV EAX, XMM0*/
            addbyte(0x0f);
            addbyte(0x7e);
            addbyte(0xc0);
        } else {
            addbyte(0xb2); /*MOV DL, 8*/
            addbyte(8);
            addbyte(0x8b); /*MOV ECX, state->lod[RDI]*/
            addbyte(0x8f);
            addlong(offsetof(voodoo_state_t, lod));
            addbyte(0x48); /*MOV RBP, state->tex[RDI+RCX*8]*/
            addbyte(0x8b);
            addbyte(0xac);
            addbyte(0xcf);
            addlong(VOODOO_OFFSETOF_ARRAY(voodoo_state_t, tex, tmu));
            addbyte(0x28); /*SUB DL, CL*/
            addbyte(0xca);
            addbyte(0x80); /*ADD CL, 4*/
            addbyte(0xc1);
            addbyte(4);
            addbyte(0x8b); /*MOV EAX, state->tex_s[EDI]*/
            addbyte(0x87);
            addlong(offsetof(voodoo_state_t, tex_s));
            addbyte(0x8b); /*MOV EBX, state->tex_t[EDI]*/
            addbyte(0x9f);
            addlong(offsetof(voodoo_state_t, tex_t));
            if (params->tLOD[tmu] & LOD_TMIRROR_S) {
                addbyte(0xa9); /*TEST EAX, 0x1000*/
                addlong(0x1000);
                addbyte(0x74); /*JZ +*/
                addbyte(2);
                addbyte(0xf7); /*NOT EAX*/
                addbyte(0xd0);
            }
            if (params->tLOD[tmu] & LOD_TMIRROR_T) {
                addbyte(0xf7); /*TEST EBX, 0x1000*/
                addbyte(0xc3);
                addlong(0x1000);
                addbyte(0x74); /*JZ +*/
                addbyte(2);
                addbyte(0xf7); /*NOT EBX*/
                addbyte(0xd3);
            }
            addbyte(0xd3); /*SHR EAX, CL*/
            addbyte(0xe8);
            addbyte(0xd3); /*SHR EBX, CL*/
            addbyte(0xeb);
            if (state->clamp_s[tmu]) {
                addbyte(0x85); /*TEST EAX, EAX*/
                addbyte(0xc0);
                addbyte(0x41); /*CMOVS EAX, R10(alookup[0](zero))*/
                addbyte(0x0f);
                addbyte(0x48);
                addbyte(0x02);
                addbyte(0x3b); /*CMP EAX, params->tex_w_mask[ESI+ECX*4]*/
                addbyte(0x84);
                addbyte(0x8e);
                addlong(VOODOO_OFFSETOF_ARRAY(voodoo_params_t, tex_w_mask, tmu) - 0x10);
                addbyte(0x0f); /*CMOVAE EAX, params->tex_w_mask[ESI+ECX*4]*/
                addbyte(0x43);
                addbyte(0x84);
                addbyte(0x8e);
                addlong(VOODOO_OFFSETOF_ARRAY(voodoo_params_t, tex_w_mask, tmu) - 0x10);

            } else {
                addbyte(0x23); /*AND EAX, params->tex_w_mask-0x10[ESI+ECX*4]*/
                addbyte(0x84);
                addbyte(0x8e);
                addlong(VOODOO_OFFSETOF_ARRAY(voodoo_params_t, tex_w_mask, tmu) - 0x10);
            }
            if (state->clamp_t[tmu]) {
                addbyte(0x85); /*TEST EBX, EBX*/
                addbyte(0xdb);
                addbyte(0x41); /*CMOVS EBX, R10(alookup[0](zero))*/
                addbyte(0x0f);
                addbyte(0x48);
                addbyte(0x1a);
                addbyte(0x3b); /*CMP EBX, params->tex_h_mask[ESI+ECX*4]*/
                addbyte(0x9c);
                addbyte(0x8e);
                addlong(VOODOO_OFFSETOF_ARRAY(voodoo_params_t, tex_h_mask, tmu) - 0x10);
                addbyte(0x0f); /*CMOVAE EBX, params->tex_h_mask[ESI+ECX*4]*/
                addbyte(0x43);
                addbyte(0x9c);
                addbyte(0x8e);
                addlong(VOODOO_OFFSETOF_ARRAY(voodoo_params_t, tex_h_mask, tmu) - 0x10);
            } else {
                addbyte(0x23); /*AND EBX, params->tex_h_mask-0x10[ESI+ECX*4]*/
                addbyte(0x9c);
                addbyte(0x8e);
                addlong(VOODOO_OFFSETOF_ARRAY(voodoo_params_t, tex_h_mask, tmu) - 0x10);
            }
            addbyte(0x88); /*MOV CL, DL*/
            addbyte(0xd1);
            addbyte(0xd3); /*SHL EBX, CL*/
            addbyte(0xe3);
            addbyte(0x01); /*ADD EBX, EAX*/
            addbyte(0xc3);

            addbyte(0x8b); /*MOV EAX, [RBP+RBX*4]*/
            addbyte(0x44);
            addbyte(0x9d);
            addbyte(0);
        }
    }

    return block_pos;
}

static inline void
voodoo_generate(uint8_t *code_block, voodoo_t *voodoo, voodoo_params_t *params, voodoo_state_t *state, int depthop)
{
    int block_pos        = 0;
    int z_skip_pos       = 0;
    int a_skip_pos       = 0;
    int amask_skip_pos   = 0;
    int stipple_skip_pos = 0;
    int chroma_skip_pos  = 0;
    int depth_jump_pos   = 0;
    int depth_jump_pos2  = 0;
    int loop_jump_pos    = 0;
#if 0
    xmm_01_w = (__m128i)0x0001000100010001ull;
    xmm_ff_w = (__m128i)0x00ff00ff00ff00ffull;
    xmm_ff_b = (__m128i)0x00000000ffffffffull;
#endif
    xmm_01_w  = _mm_set_epi32(0, 0, 0x00010001, 0x00010001);
    xmm_ff_w  = _mm_set_epi32(0, 0, 0x00ff00ff, 0x00ff00ff);
    xmm_ff_b  = _mm_set_epi32(0, 0, 0, 0x00ffffff);
    minus_254 = _mm_set_epi32(0, 0, 0xff02ff02, 0xff02ff02);
#if 0
    *(uint64_t *)&const_1_48 = 0x45b0000000000000ull;
    block_pos = 0;
    voodoo_get_depth = &code_block[block_pos];
#endif
    /*W at (%esp+4)
      Z at (%esp+12)
      new_depth at (%esp+16)*/
#if 0
    if ((params->fbzMode & FBZ_DEPTH_ENABLE) && (depth_op == DEPTHOP_NEVER)) {
            addbyte(0xC3); /*RET*/
            return;
    }
#endif
    addbyte(0x55); /*PUSH RBP*/
    addbyte(0x57); /*PUSH RDI*/
    addbyte(0x56); /*PUSH RSI*/
    addbyte(0x53); /*PUSH RBX*/
    addbyte(0x41); /*PUSH R12*/
    addbyte(0x54);
    addbyte(0x41); /*PUSH R13*/
    addbyte(0x55);
    addbyte(0x41); /*PUSH R14*/
    addbyte(0x56);
    addbyte(0x41); /*PUSH R15*/
    addbyte(0x57);

    addbyte(0x49); /*MOV R15, xmm_01_w*/
    addbyte(0xbf);
    addquad((uint64_t) (uintptr_t) &xmm_01_w);
    addbyte(0x66); /*MOVDQA XMM8, [R15]*/
    addbyte(0x45);
    addbyte(0x0f);
    addbyte(0x6f);
    addbyte(0x07 | (0 << 3));
    addbyte(0x49); /*MOV R15, xmm_ff_w*/
    addbyte(0xbf);
    addquad((uint64_t) (uintptr_t) &xmm_ff_w);
    addbyte(0x66); /*MOVDQA XMM9, [R15]*/
    addbyte(0x45);
    addbyte(0x0f);
    addbyte(0x6f);
    addbyte(0x07 | (1 << 3));
    addbyte(0x49); /*MOV R15, xmm_ff_b*/
    addbyte(0xbf);
    addquad((uint64_t) (uintptr_t) &xmm_ff_b);
    addbyte(0x66); /*MOVDQA XMM10, [R15]*/
    addbyte(0x45);
    addbyte(0x0f);
    addbyte(0x6f);
    addbyte(0x07 | (2 << 3));
    addbyte(0x49); /*MOV R15, minus_254*/
    addbyte(0xbf);
    addquad((uint64_t) (uintptr_t) &minus_254);
    addbyte(0x66); /*MOVDQA XMM11, [R15]*/
    addbyte(0x45);
    addbyte(0x0f);
    addbyte(0x6f);
    addbyte(0x07 | (3 << 3));

#if _WIN64
    addbyte(0x48); /*MOV RDI, RCX (voodoo_state)*/
    addbyte(0x89);
    addbyte(0xcf);
    addbyte(0x49); /*MOV R15, RDX (voodoo_params)*/
    addbyte(0x89);
    addbyte(0xd7);
    addbyte(0x4d); /*MOV R14, R9 (real_y)*/
    addbyte(0x89);
    addbyte(0xce);
#else
    addbyte(0x49); /*MOV R14, RCX (real_y)*/
    addbyte(0x89);
    addbyte(0xce);
    addbyte(0x49); /*MOV R15, RSI (voodoo_state)*/
    addbyte(0x89);
    addbyte(0xf7);
#endif

    addbyte(0x49); /*MOV R9, logtable*/
    addbyte(0xb8 | (9 & 7));
    addquad((uint64_t) (uintptr_t) &logtable);
    addbyte(0x49); /*MOV R10, alookup*/
    addbyte(0xb8 | (10 & 7));
    addquad((uint64_t) (uintptr_t) &alookup);
    addbyte(0x49); /*MOV R11, aminuslookup*/
    addbyte(0xb8 | (11 & 7));
    addquad((uint64_t) (uintptr_t) &aminuslookup);
    addbyte(0x49); /*MOV R12, xmm_00_ff_w*/
    addbyte(0xb8 | (12 & 7));
    addquad((uint64_t) (uintptr_t) &xmm_00_ff_w);
    addbyte(0x49); /*MOV R13, i_00_ff_w*/
    addbyte(0xb8 | (13 & 7));
    addquad((uint64_t) (uintptr_t) &i_00_ff_w);

    loop_jump_pos = block_pos;
    if (params->fbzMode & FBZ_STIPPLE) {
        /* Stipple enabled. */
        if (params->fbzMode & FBZ_STIPPLE_PATT) {
            /* x64's BT instruction is too slow. So use TEST instead. */
            addbyte(0x4c); /* MOV RBX, R14(real_y)*/
            addbyte(0x89);
            addbyte(0xf3);

            addbyte(0x83); /* AND EBX, 3 */
            addbyte(0xe3);
            addbyte(0x03);

            addbyte(0xc1); /* SHL EBX, 3 */
            addbyte(0xe3);
            addbyte(0x03);

            addbyte(0x8b); /*MOV EAX, state->x[EDI]*/
            addbyte(0x87);
            addlong(offsetof(voodoo_state_t, x));

            addbyte(0xf7); /* NOT EAX */
            addbyte(0xd0);

            addbyte(0x83); /* AND EAX, 7 */
            addbyte(0xe0);
            addbyte(0x07);

            addbyte(0x09); /* OR EAX, EBX */
            addbyte(0xc3);

            addbyte(0x88); /* MOV CL, AL */
            addbyte(0xc1);

            addbyte(0xb8); /* MOV EAX, 1*/
            addlong(1);
            
            addbyte(0xd3); /* SHL EAX, CL */
            addbyte(0xe0);

            addbyte(0x85); /* TEST state->stipple[EDI], EAX */
            addbyte(0x87);
            addlong(offsetof(voodoo_state_t, stipple));

            addbyte(0x0f); /* JZ stipple_skip_pos */
            addbyte(0x84);
            stipple_skip_pos = block_pos;
            addlong(0);
        } else {
            addbyte(0xd1); /* ROR state->stipple[EDI], 1*/
            addbyte(0x8f);
            addlong(offsetof(voodoo_state_t, stipple));

            addbyte(0xf7); /* TEST state->stipple[EDI], 0x80000000 */
            addbyte(0x87);
            addlong(offsetof(voodoo_state_t, stipple));
            addlong(0x80000000);

            addbyte(0x0f); /* JZ stipple_skip_pos */
            addbyte(0x84);
            stipple_skip_pos = block_pos;
            addlong(0);
        }
    }
    addbyte(0x4c); /*MOV RSI, R15*/
    addbyte(0x89);
    addbyte(0xfe);
    if (params->col_tiled || params->aux_tiled) {
        addbyte(0x8b); /*MOV EAX, state->x[EDI]*/
        addbyte(0x87);
        addlong(offsetof(voodoo_state_t, x));
        addbyte(0x89); /*MOV EBX, EAX*/
        addbyte(0xc3);
        addbyte(0x83); /*AND EAX, 63*/
        addbyte(0xe0);
        addbyte(63);
        addbyte(0xc1); /*SHR EBX, 6*/
        addbyte(0xeb);
        addbyte(6);
        addbyte(0xc1); /*SHL EBX, 11  - tile is 128*32, << 12, div 2 because word index*/
        addbyte(0xe3);
        addbyte(11);
        addbyte(0x01); /*ADD EAX, EBX*/
        addbyte(0xd8);
        addbyte(0x89); /*MOV state->x_tiled[EDI], EAX*/
        addbyte(0x87);
        addlong(offsetof(voodoo_state_t, x_tiled));
    }
    addbyte(0x66); /*PXOR XMM2, XMM2*/
    addbyte(0x0f);
    addbyte(0xef);
    addbyte(0xd2);

    if ((params->fbzMode & FBZ_W_BUFFER) || (params->fogMode & (FOG_ENABLE | FOG_CONSTANT | FOG_Z | FOG_ALPHA)) == FOG_ENABLE) {
        addbyte(0xb8); /*MOV new_depth, 0*/
        addlong(0);
        addbyte(0x66); /*TEST w+4, 0xffff*/
        addbyte(0xf7);
        addbyte(0x87);
        addlong(offsetof(voodoo_state_t, w) + 4);
        addword(0xffff);
        addbyte(0x75); /*JNZ got_depth*/
        depth_jump_pos = block_pos;
        addbyte(0);
#if 0
        addbyte(4+5+2+3+2+5+5+3+2+2+2+/*3+*/3+2+6+4+5+2+3);
#endif
        addbyte(0x8b); /*MOV EDX, w*/
        addbyte(0x97);
        addlong(offsetof(voodoo_state_t, w));
        addbyte(0xb8); /*MOV new_depth, 0xf001*/
        addlong(0xf001);
        addbyte(0x89); /*MOV EBX, EDX*/
        addbyte(0xd3);
        addbyte(0xc1); /*SHR EDX, 16*/
        addbyte(0xea);
        addbyte(16);
        addbyte(0x74); /*JZ got_depth*/
        depth_jump_pos2 = block_pos;
        addbyte(0);
#if 0
        addbyte(5+5+3+2+2+2+/*3+*/3+2+6+4+5+2+3);
#endif
        addbyte(0xb9); /*MOV ECX, 19*/
        addlong(19);
        addbyte(0x0f); /*BSR EAX, EDX*/
        addbyte(0xbd);
        addbyte(0xc2);
        addbyte(0xba); /*MOV EDX, 15*/
        addlong(15);
        addbyte(0xf7); /*NOT EBX*/
        addbyte(0xd3);
        addbyte(0x29); /*SUB EDX, EAX - EDX = exp*/
        addbyte(0xc2);
        addbyte(0x29); /*SUB ECX, EDX*/
        addbyte(0xd1);
        addbyte(0xc1); /*SHL EDX, 12*/
        addbyte(0xe2);
        addbyte(12);
        addbyte(0xd3); /*SHR EBX, CL*/
        addbyte(0xeb);
        addbyte(0x81); /*AND EBX, 0xfff - EBX = mant*/
        addbyte(0xe3);
        addlong(0xfff);
        addbyte(0x67); /*LEA EAX, 1[EDX, EBX]*/
        addbyte(0x8d);
        addbyte(0x44);
        addbyte(0x13);
        addbyte(1);
        addbyte(0xbb); /*MOV EBX, 0xffff*/
        addlong(0xffff);
        addbyte(0x39); /*CMP EAX, EBX*/
        addbyte(0xd8);
        addbyte(0x0f); /*CMOVA EAX, EBX*/
        addbyte(0x47);
        addbyte(0xc3);

        if (depth_jump_pos)
            *(uint8_t *) &code_block[depth_jump_pos] = (block_pos - depth_jump_pos) - 1;
        if (depth_jump_pos)
            *(uint8_t *) &code_block[depth_jump_pos2] = (block_pos - depth_jump_pos2) - 1;

        if ((params->fogMode & (FOG_ENABLE | FOG_CONSTANT | FOG_Z | FOG_ALPHA)) == FOG_ENABLE) {
            addbyte(0x89); /*MOV state->w_depth[EDI], EAX*/
            addbyte(0x87);
            addlong(offsetof(voodoo_state_t, w_depth));
        }
    }
    if (!(params->fbzMode & FBZ_W_BUFFER)) {
        addbyte(0x8b); /*MOV EAX, z*/
        addbyte(0x87);
        addlong(offsetof(voodoo_state_t, z));
        addbyte(0xbb); /*MOV EBX, 0xffff*/
        addlong(0xffff);
        addbyte(0x31); /*XOR ECX, ECX*/
        addbyte(0xc9);
        addbyte(0xc1); /*SAR EAX, 12*/
        addbyte(0xf8);
        addbyte(12);
        addbyte(0x0f); /*CMOVS EAX, ECX*/
        addbyte(0x48);
        addbyte(0xc1);
        addbyte(0x39); /*CMP EAX, EBX*/
        addbyte(0xd8);
        addbyte(0x0f); /*CMOVA EAX, EBX*/
        addbyte(0x47);
        addbyte(0xc3);
    }

    if (params->fbzMode & FBZ_DEPTH_BIAS) {
        addbyte(0x03); /*ADD EAX, params->zaColor[ESI]*/
        addbyte(0x86);
        addlong(offsetof(voodoo_params_t, zaColor));
        addbyte(0x25); /*AND EAX, 0xffff*/
        addlong(0xffff);
    }

    addbyte(0x89); /*MOV state->new_depth[EDI], EAX*/
    addbyte(0x87);
    addlong(offsetof(voodoo_state_t, new_depth));

    if ((params->fbzMode & FBZ_DEPTH_ENABLE) && (depthop != DEPTHOP_ALWAYS) && (depthop != DEPTHOP_NEVER)) {
        addbyte(0x8b); /*MOV EBX, state->x[EDI]*/
        addbyte(0x9f);
        if (params->aux_tiled)
            addlong(offsetof(voodoo_state_t, x_tiled));
        else
            addlong(offsetof(voodoo_state_t, x));
        addbyte(0x48); /*MOV RCX, aux_mem[RDI]*/
        addbyte(0x8b);
        addbyte(0x8f);
        addlong(offsetof(voodoo_state_t, aux_mem));
        addbyte(0x0f); /*MOVZX EBX, [ECX+EBX*2]*/
        addbyte(0xb7);
        addbyte(0x1c);
        addbyte(0x59);
        if (params->fbzMode & FBZ_DEPTH_SOURCE) {
            addbyte(0x0f); /*MOVZX EAX, zaColor[RSI]*/
            addbyte(0xb7);
            addbyte(0x86);
            addlong(offsetof(voodoo_params_t, zaColor));
        }
        addbyte(0x39); /*CMP EAX, EBX*/
        addbyte(0xd8);
        if (depthop == DEPTHOP_LESSTHAN) {
            addbyte(0x0f); /*JAE skip*/
            addbyte(0x83);
            z_skip_pos = block_pos;
            addlong(0);
        } else if (depthop == DEPTHOP_EQUAL) {
            addbyte(0x0f); /*JNE skip*/
            addbyte(0x85);
            z_skip_pos = block_pos;
            addlong(0);
        } else if (depthop == DEPTHOP_LESSTHANEQUAL) {
            addbyte(0x0f); /*JA skip*/
            addbyte(0x87);
            z_skip_pos = block_pos;
            addlong(0);
        } else if (depthop == DEPTHOP_GREATERTHAN) {
            addbyte(0x0f); /*JBE skip*/
            addbyte(0x86);
            z_skip_pos = block_pos;
            addlong(0);
        } else if (depthop == DEPTHOP_NOTEQUAL) {
            addbyte(0x0f); /*JE skip*/
            addbyte(0x84);
            z_skip_pos = block_pos;
            addlong(0);
        } else if (depthop == DEPTHOP_GREATERTHANEQUAL) {
            addbyte(0x0f); /*JB skip*/
            addbyte(0x82);
            z_skip_pos = block_pos;
            addlong(0);
        } else
            fatal("Bad depth_op\n");
    } else if ((params->fbzMode & FBZ_DEPTH_ENABLE) && (depthop == DEPTHOP_NEVER)) {
        addbyte(0xC3); /*RET*/
    }

    /*XMM0 = colour*/
    /*XMM2 = 0 (for unpacking*/

    /*EDI = state, ESI = params*/

    if ((params->textureMode[0] & TEXTUREMODE_LOCAL_MASK) == TEXTUREMODE_LOCAL || !voodoo->dual_tmus) {
        /*TMU0 only sampling local colour or only one TMU, only sample TMU0*/
        block_pos = codegen_texture_fetch(code_block, voodoo, params, state, block_pos, 0);

        addbyte(0x66); /*MOVD XMM0, EAX*/
        addbyte(0x0f);
        addbyte(0x6e);
        addbyte(0xc0);
        addbyte(0xc1); /*SHR EAX, 24*/
        addbyte(0xe8);
        addbyte(24);
        addbyte(0x89); /*MOV state->tex_a[RDI], EAX*/
        addbyte(0x87);
        addlong(offsetof(voodoo_state_t, tex_a));
    } else if ((params->textureMode[0] & TEXTUREMODE_MASK) == TEXTUREMODE_PASSTHROUGH) {
        /*TMU0 in pass-through mode, only sample TMU1*/
        block_pos = codegen_texture_fetch(code_block, voodoo, params, state, block_pos, 1);

        addbyte(0x66); /*MOVD XMM0, EAX*/
        addbyte(0x0f);
        addbyte(0x6e);
        addbyte(0xc0);
        addbyte(0xc1); /*SHR EAX, 24*/
        addbyte(0xe8);
        addbyte(24);
        addbyte(0x89); /*MOV state->tex_a[RDI], EAX*/
        addbyte(0x87);
        addlong(offsetof(voodoo_state_t, tex_a));
    } else {
        block_pos = codegen_texture_fetch(code_block, voodoo, params, state, block_pos, 1);

        addbyte(0x66); /*MOVD XMM3, EAX*/
        addbyte(0x0f);
        addbyte(0x6e);
        addbyte(0xd8);
        if ((params->textureMode[1] & TEXTUREMODE_TRILINEAR) && tc_sub_clocal_1) {
            addbyte(0x8b); /*MOV EAX, state->lod*/
            addbyte(0x87);
            addlong(offsetof(voodoo_state_t, lod));
            if (!tc_reverse_blend_1) {
                addbyte(0xbb); /*MOV EBX, 1*/
                addlong(1);
            } else {
                addbyte(0x31); /*XOR EBX, EBX*/
                addbyte(0xdb);
            }
            addbyte(0x83); /*AND EAX, 1*/
            addbyte(0xe0);
            addbyte(1);
            if (!tca_reverse_blend_1) {
                addbyte(0xb9); /*MOV ECX, 1*/
                addlong(1);
            } else {
                addbyte(0x31); /*XOR ECX, ECX*/
                addbyte(0xc9);
            }
            addbyte(0x31); /*XOR EBX, EAX*/
            addbyte(0xc3);
            addbyte(0x31); /*XOR ECX, EAX*/
            addbyte(0xc1);
            addbyte(0xc1); /*SHL EBX, 4*/
            addbyte(0xe3);
            addbyte(4);
            /*EBX = tc_reverse_blend, ECX=tca_reverse_blend*/
        }
        addbyte(0x66); /*PUNPCKLBW XMM3, XMM2*/
        addbyte(0x0f);
        addbyte(0x60);
        addbyte(0xda);
        if (tc_sub_clocal_1) {
            switch (tc_mselect_1) {
                case TC_MSELECT_ZERO:
                    addbyte(0x66); /*PXOR XMM0, XMM0*/
                    addbyte(0x0f);
                    addbyte(0xef);
                    addbyte(0xc0);
                    break;
                case TC_MSELECT_CLOCAL:
                    addbyte(0xf3); /*MOVQ XMM0, XMM3*/
                    addbyte(0x0f);
                    addbyte(0x7e);
                    addbyte(0xc3);
                    break;
                case TC_MSELECT_AOTHER:
                    addbyte(0x66); /*PXOR XMM0, XMM0*/
                    addbyte(0x0f);
                    addbyte(0xef);
                    addbyte(0xc0);
                    break;
                case TC_MSELECT_ALOCAL:
                    addbyte(0xf2); /*PSHUFLW XMM0, XMM3, 0xff*/
                    addbyte(0x0f);
                    addbyte(0x70);
                    addbyte(0xc3);
                    addbyte(0xff);
                    break;
                case TC_MSELECT_DETAIL:
                    addbyte(0xb8); /*MOV EAX, params->detail_bias[1]*/
                    addlong(params->detail_bias[1]);
                    addbyte(0x2b); /*SUB EAX, state->lod*/
                    addbyte(0x87);
                    addlong(offsetof(voodoo_state_t, lod));
                    addbyte(0xba); /*MOV EDX, params->detail_max[1]*/
                    addlong(params->detail_max[1]);
                    addbyte(0xc1); /*SHL EAX, params->detail_scale[1]*/
                    addbyte(0xe0);
                    addbyte(params->detail_scale[1]);
                    addbyte(0x39); /*CMP EAX, EDX*/
                    addbyte(0xd0);
                    addbyte(0x0f); /*CMOVNL EAX, EDX*/
                    addbyte(0x4d);
                    addbyte(0xc2);
                    addbyte(0x66); /*MOVD XMM0, EAX*/
                    addbyte(0x0f);
                    addbyte(0x6e);
                    addbyte(0xc0);
                    addbyte(0xf2); /*PSHUFLW XMM0, XMM0, 0*/
                    addbyte(0x0f);
                    addbyte(0x70);
                    addbyte(0xc0);
                    addbyte(0);
                    break;
                case TC_MSELECT_LOD_FRAC:
                    addbyte(0x66); /*MOVD XMM0, state->lod_frac[1]*/
                    addbyte(0x0f);
                    addbyte(0x6e);
                    addbyte(0x87);
                    addlong(offsetof(voodoo_state_t, lod_frac[1]));
                    addbyte(0xf2); /*PSHUFLW XMM0, XMM0, 0*/
                    addbyte(0x0f);
                    addbyte(0x70);
                    addbyte(0xc0);
                    addbyte(0);
                    break;
            }
            if (params->textureMode[1] & TEXTUREMODE_TRILINEAR) {
                addbyte(0x66); /*PXOR XMM0, R12(xmm_00_ff_w)[EBX]*/
                addbyte(0x41);
                addbyte(0x0f);
                addbyte(0xef);
                addbyte(0x04);
                addbyte(0x1c);
            } else if (!tc_reverse_blend_1) {
                addbyte(0x66); /*PXOR XMM0, XMM9(xmm_ff_w)*/
                addbyte(0x41);
                addbyte(0x0f);
                addbyte(0xef);
                addbyte(0xc1);
            }
            addbyte(0x66); /*PADDW XMM0, XMM8(xmm_01_w)*/
            addbyte(0x41);
            addbyte(0x0f);
            addbyte(0xfd);
            addbyte(0xc0);
            addbyte(0xf3); /*MOVQ XMM1, XMM2*/
            addbyte(0x0f);
            addbyte(0x7e);
            addbyte(0xca);
            addbyte(0xf3); /*MOVQ XMM5, XMM0*/
            addbyte(0x0f);
            addbyte(0x7e);
            addbyte(0xe8);
            addbyte(0x66); /*PMULLW XMM0, XMM3*/
            addbyte(0x0f);
            addbyte(0xd5);
            addbyte(0xc3);
            addbyte(0x66); /*PMULHW XMM5, XMM3*/
            addbyte(0x0f);
            addbyte(0xe5);
            addbyte(0xeb);
            addbyte(0x66); /*PUNPCKLWD XMM0, XMM5*/
            addbyte(0x0f);
            addbyte(0x61);
            addbyte(0xc5);
            addbyte(0x66); /*PSRAD XMM0, 8*/
            addbyte(0x0f);
            addbyte(0x72);
            addbyte(0xe0);
            addbyte(8);
            addbyte(0x66); /*PACKSSDW XMM0, XMM0*/
            addbyte(0x0f);
            addbyte(0x6b);
            addbyte(0xc0);
            addbyte(0x66); /*PSUBW XMM1, XMM0*/
            addbyte(0x0f);
            addbyte(0xf9);
            addbyte(0xc8);
            if (tc_add_clocal_1) {
                addbyte(0x66); /*PADDW XMM1, XMM3*/
                addbyte(0x0f);
                addbyte(0xfd);
                addbyte(0xcb);
            } else if (tc_add_alocal_1) {
                addbyte(0xf2); /*PSHUFLW XMM0, XMM3, 0xff*/
                addbyte(0x0f);
                addbyte(0x70);
                addbyte(0xc3);
                addbyte(0xff);
                addbyte(0x66); /*PADDW XMM1, XMM0*/
                addbyte(0x0f);
                addbyte(0xfd);
                addbyte(0xc8);
            }
            addbyte(0x66); /*PACKUSWB XMM3, XMM1*/
            addbyte(0x0f);
            addbyte(0x67);
            addbyte(0xd9);
            if (tca_sub_clocal_1) {
                addbyte(0x66); /*MOVD EBX, XMM3*/
                addbyte(0x0f);
                addbyte(0x7e);
                addbyte(0xdb);
            }
            addbyte(0x66); /*PUNPCKLBW XMM3, XMM2*/
            addbyte(0x0f);
            addbyte(0x60);
            addbyte(0xda);
        }

        if (tca_sub_clocal_1) {
            addbyte(0xc1); /*SHR EBX, 24*/
            addbyte(0xeb);
            addbyte(24);
            switch (tca_mselect_1) {
                case TCA_MSELECT_ZERO:
                    addbyte(0x31); /*XOR EAX, EAX*/
                    addbyte(0xc0);
                    break;
                case TCA_MSELECT_CLOCAL:
                    addbyte(0x89); /*MOV EAX, EBX*/
                    addbyte(0xd8);
                    break;
                case TCA_MSELECT_AOTHER:
                    addbyte(0x31); /*XOR EAX, EAX*/
                    addbyte(0xc0);
                    break;
                case TCA_MSELECT_ALOCAL:
                    addbyte(0x89); /*MOV EAX, EBX*/
                    addbyte(0xd8);
                    break;
                case TCA_MSELECT_DETAIL:
                    addbyte(0xb8); /*MOV EAX, params->detail_bias[1]*/
                    addlong(params->detail_bias[1]);
                    addbyte(0x2b); /*SUB EAX, state->lod*/
                    addbyte(0x87);
                    addlong(offsetof(voodoo_state_t, lod));
                    addbyte(0xba); /*MOV EDX, params->detail_max[1]*/
                    addlong(params->detail_max[1]);
                    addbyte(0xc1); /*SHL EAX, params->detail_scale[1]*/
                    addbyte(0xe0);
                    addbyte(params->detail_scale[1]);
                    addbyte(0x39); /*CMP EAX, EDX*/
                    addbyte(0xd0);
                    addbyte(0x0f); /*CMOVNL EAX, EDX*/
                    addbyte(0x4d);
                    addbyte(0xc2);
                    break;
                case TCA_MSELECT_LOD_FRAC:
                    addbyte(0x8b); /*MOV EAX, state->lod_frac[1]*/
                    addbyte(0x87);
                    addlong(offsetof(voodoo_state_t, lod_frac[1]));
                    break;
            }
            if (params->textureMode[1] & TEXTUREMODE_TRILINEAR) {
                addbyte(0x41); /*XOR EAX, R13(i_00_ff_w)[ECX*4]*/
                addbyte(0x33);
                addbyte(0x44);
                addbyte(0x8d);
                addbyte(0);
            } else if (!tc_reverse_blend_1) {
                addbyte(0x35); /*XOR EAX, 0xff*/
                addlong(0xff);
            }
            addbyte(0x83); /*ADD EAX, 1*/
            addbyte(0xc0);
            addbyte(1);
            addbyte(0x0f); /*IMUL EAX, EBX*/
            addbyte(0xaf);
            addbyte(0xc3);
            addbyte(0xb9); /*MOV ECX, 0xff*/
            addlong(0xff);
            addbyte(0xf7); /*NEG EAX*/
            addbyte(0xd8);
            addbyte(0xc1); /*SAR EAX, 8*/
            addbyte(0xf8);
            addbyte(8);
            if (tca_add_clocal_1 || tca_add_alocal_1) {
                addbyte(0x01); /*ADD EAX, EBX*/
                addbyte(0xd8);
            }
            addbyte(0x39); /*CMP ECX, EAX*/
            addbyte(0xc1);
            addbyte(0x0f); /*CMOVA ECX, EAX*/
            addbyte(0x47);
            addbyte(0xc8);
            addbyte(0x66); /*PINSRW 3, XMM3, XMM0*/
            addbyte(0x0f);
            addbyte(0xc4);
            addbyte(0xd8);
            addbyte(3);
        }

        block_pos = codegen_texture_fetch(code_block, voodoo, params, state, block_pos, 0);

        addbyte(0x66); /*MOVD XMM0, EAX*/
        addbyte(0x0f);
        addbyte(0x6e);
        addbyte(0xc0);
        addbyte(0x66); /*MOVD XMM7, EAX*/
        addbyte(0x0f);
        addbyte(0x6e);
        addbyte(0xf8);

        if (params->textureMode[0] & TEXTUREMODE_TRILINEAR) {
            addbyte(0x8b); /*MOV EAX, state->lod*/
            addbyte(0x87);
            addlong(offsetof(voodoo_state_t, lod));
            if (!tc_reverse_blend) {
                addbyte(0xbb); /*MOV EBX, 1*/
                addlong(1);
            } else {
                addbyte(0x31); /*XOR EBX, EBX*/
                addbyte(0xdb);
            }
            addbyte(0x83); /*AND EAX, 1*/
            addbyte(0xe0);
            addbyte(1);
            if (!tca_reverse_blend) {
                addbyte(0xb9); /*MOV ECX, 1*/
                addlong(1);
            } else {
                addbyte(0x31); /*XOR ECX, ECX*/
                addbyte(0xc9);
            }
            addbyte(0x31); /*XOR EBX, EAX*/
            addbyte(0xc3);
            addbyte(0x31); /*XOR ECX, EAX*/
            addbyte(0xc1);
            addbyte(0xc1); /*SHL EBX, 4*/
            addbyte(0xe3);
            addbyte(4);
            /*EBX = tc_reverse_blend, ECX=tca_reverse_blend*/
        }

        /*XMM0 = TMU0 output, XMM3 = TMU1 output*/

        addbyte(0x66); /*PUNPCKLBW XMM0, XMM2*/
        addbyte(0x0f);
        addbyte(0x60);
        addbyte(0xc2);
        if (tc_zero_other) {
            addbyte(0x66); /*PXOR XMM1, XMM1*/
            addbyte(0x0f);
            addbyte(0xef);
            addbyte(0xc9);
        } else {
            addbyte(0xf3); /*MOV XMM1, XMM3*/
            addbyte(0x0f);
            addbyte(0x7e);
            addbyte(0xcb);
        }
        if (tc_sub_clocal) {
            addbyte(0x66); /*PSUBW XMM1, XMM0*/
            addbyte(0x0f);
            addbyte(0xf9);
            addbyte(0xc8);
        }

        switch (tc_mselect) {
            case TC_MSELECT_ZERO:
                addbyte(0x66); /*PXOR XMM4, XMM4*/
                addbyte(0x0f);
                addbyte(0xef);
                addbyte(0xe4);
                break;
            case TC_MSELECT_CLOCAL:
                addbyte(0xf3); /*MOV XMM4, XMM0*/
                addbyte(0x0f);
                addbyte(0x7e);
                addbyte(0xe0);
                break;
            case TC_MSELECT_AOTHER:
                addbyte(0xf2); /*PSHUFLW XMM4, XMM3, 3, 3, 3, 3*/
                addbyte(0x0f);
                addbyte(0x70);
                addbyte(0xe3);
                addbyte(0xff);
                break;
            case TC_MSELECT_ALOCAL:
                addbyte(0xf2); /*PSHUFLW XMM4, XMM0, 3, 3, 3, 3*/
                addbyte(0x0f);
                addbyte(0x70);
                addbyte(0xe0);
                addbyte(0xff);
                break;
            case TC_MSELECT_DETAIL:
                addbyte(0xb8); /*MOV EAX, params->detail_bias[0]*/
                addlong(params->detail_bias[0]);
                addbyte(0x2b); /*SUB EAX, state->lod*/
                addbyte(0x87);
                addlong(offsetof(voodoo_state_t, lod));
                addbyte(0xba); /*MOV EDX, params->detail_max[0]*/
                addlong(params->detail_max[0]);
                addbyte(0xc1); /*SHL EAX, params->detail_scale[0]*/
                addbyte(0xe0);
                addbyte(params->detail_scale[0]);
                addbyte(0x39); /*CMP EAX, EDX*/
                addbyte(0xd0);
                addbyte(0x0f); /*CMOVNL EAX, EDX*/
                addbyte(0x4d);
                addbyte(0xc2);
                addbyte(0x66); /*MOVD XMM4, EAX*/
                addbyte(0x0f);
                addbyte(0x6e);
                addbyte(0xe0);
                addbyte(0xf2); /*PSHUFLW XMM4, XMM4, 0*/
                addbyte(0x0f);
                addbyte(0x70);
                addbyte(0xe4);
                addbyte(0);
                break;
            case TC_MSELECT_LOD_FRAC:
                addbyte(0x66); /*MOVD XMM0, state->lod_frac[0]*/
                addbyte(0x0f);
                addbyte(0x6e);
                addbyte(0xa7);
                addlong(offsetof(voodoo_state_t, lod_frac[0]));
                addbyte(0xf2); /*PSHUFLW XMM0, XMM0, 0*/
                addbyte(0x0f);
                addbyte(0x70);
                addbyte(0xe4);
                addbyte(0);
                break;
        }
        if (params->textureMode[0] & TEXTUREMODE_TRILINEAR) {
            addbyte(0x66); /*PXOR XMM4, R12(xmm_00_ff_w)[EBX]*/
            addbyte(0x41);
            addbyte(0x0f);
            addbyte(0xef);
            addbyte(0x24);
            addbyte(0x1c);
        } else if (!tc_reverse_blend) {
            addbyte(0x66); /*PXOR XMM4, XMM9(xmm_ff_w)*/
            addbyte(0x41);
            addbyte(0x0f);
            addbyte(0xef);
            addbyte(0xe1);
        }
        addbyte(0x66); /*PADDW XMM4, XMM8(xmm_01_w)*/
        addbyte(0x41);
        addbyte(0x0f);
        addbyte(0xfd);
        addbyte(0xe0);
        addbyte(0xf3); /*MOVQ XMM5, XMM1*/
        addbyte(0x0f);
        addbyte(0x7e);
        addbyte(0xe9);
        addbyte(0x66); /*PMULLW XMM1, XMM4*/
        addbyte(0x0f);
        addbyte(0xd5);
        addbyte(0xcc);

        if (tca_sub_clocal) {
            addbyte(0x66); /*MOV EBX, XMM7*/
            addbyte(0x0f);
            addbyte(0x7e);
            addbyte(0xfb);
        }

        addbyte(0x66); /*PMULHW XMM5, XMM4*/
        addbyte(0x0f);
        addbyte(0xe5);
        addbyte(0xec);
        addbyte(0x66); /*PUNPCKLWD XMM1, XMM5*/
        addbyte(0x0f);
        addbyte(0x61);
        addbyte(0xcd);
        addbyte(0x66); /*PSRAD XMM1, 8*/
        addbyte(0x0f);
        addbyte(0x72);
        addbyte(0xe1);
        addbyte(8);
        addbyte(0x66); /*PACKSSDW XMM1, XMM1*/
        addbyte(0x0f);
        addbyte(0x6b);
        addbyte(0xc9);

        if (tca_sub_clocal) {
            addbyte(0xc1); /*SHR EBX, 24*/
            addbyte(0xeb);
            addbyte(24);
        }

        if (tc_add_clocal) {
            addbyte(0x66); /*PADDW XMM1, XMM0*/
            addbyte(0x0f);
            addbyte(0xfd);
            addbyte(0xc8);
        } else if (tc_add_alocal) {
            addbyte(0xf2); /*PSHUFLW XMM4, XMM0, 3, 3, 3, 3*/
            addbyte(0x0f);
            addbyte(0x70);
            addbyte(0xe0);
            addbyte(0xff);
            addbyte(0x66); /*PADDW XMM1, XMM4*/
            addbyte(0x0f);
            addbyte(0xfc);
            addbyte(0xcc);
        }
        if (tc_invert_output) {
            addbyte(0x66); /*PXOR XMM1, XMM9(xmm_ff_w)*/
            addbyte(0x41);
            addbyte(0x0f);
            addbyte(0xef);
            addbyte(0xc9);
        }

        addbyte(0x66); /*PACKUSWB XMM0, XMM0*/
        addbyte(0x0f);
        addbyte(0x67);
        addbyte(0xc0);
        addbyte(0x66); /*PACKUSWB XMM3, XMM3*/
        addbyte(0x0f);
        addbyte(0x67);
        addbyte(0xdb);
        addbyte(0x66); /*PACKUSWB XMM1, XMM1*/
        addbyte(0x0f);
        addbyte(0x67);
        addbyte(0xc9);

        if (tca_zero_other) {
            addbyte(0x31); /*XOR EAX, EAX*/
            addbyte(0xc0);
        } else {
            addbyte(0x66); /*MOV EAX, XMM3*/
            addbyte(0x0f);
            addbyte(0x7e);
            addbyte(0xd8);
            addbyte(0xc1); /*SHR EAX, 24*/
            addbyte(0xe8);
            addbyte(24);
        }
        if (tca_sub_clocal) {
            addbyte(0x29); /*SUB EAX, EBX*/
            addbyte(0xd8);
        }
        switch (tca_mselect) {
            case TCA_MSELECT_ZERO:
                addbyte(0x31); /*XOR EBX, EBX*/
                addbyte(0xdb);
                break;
            case TCA_MSELECT_CLOCAL:
                addbyte(0x66); /*MOV EBX, XMM7*/
                addbyte(0x0f);
                addbyte(0x7e);
                addbyte(0xfb);
                addbyte(0xc1); /*SHR EBX, 24*/
                addbyte(0xeb);
                addbyte(24);
                break;
            case TCA_MSELECT_AOTHER:
                addbyte(0x66); /*MOV EBX, XMM3*/
                addbyte(0x0f);
                addbyte(0x7e);
                addbyte(0xdb);
                addbyte(0xc1); /*SHR EBX, 24*/
                addbyte(0xeb);
                addbyte(24);
                break;
            case TCA_MSELECT_ALOCAL:
                addbyte(0x66); /*MOV EBX, XMM7*/
                addbyte(0x0f);
                addbyte(0x7e);
                addbyte(0xfb);
                addbyte(0xc1); /*SHR EBX, 24*/
                addbyte(0xeb);
                addbyte(24);
                break;
            case TCA_MSELECT_DETAIL:
                addbyte(0xbb); /*MOV EBX, params->detail_bias[1]*/
                addlong(params->detail_bias[1]);
                addbyte(0x2b); /*SUB EBX, state->lod*/
                addbyte(0x9f);
                addlong(offsetof(voodoo_state_t, lod));
                addbyte(0xba); /*MOV EDX, params->detail_max[1]*/
                addlong(params->detail_max[1]);
                addbyte(0xc1); /*SHL EBX, params->detail_scale[1]*/
                addbyte(0xe3);
                addbyte(params->detail_scale[1]);
                addbyte(0x39); /*CMP EBX, EDX*/
                addbyte(0xd3);
                addbyte(0x0f); /*CMOVNL EBX, EDX*/
                addbyte(0x4d);
                addbyte(0xda);
                break;
            case TCA_MSELECT_LOD_FRAC:
                addbyte(0x8b); /*MOV EBX, state->lod_frac[0]*/
                addbyte(0x9f);
                addlong(offsetof(voodoo_state_t, lod_frac[0]));
                break;
        }
        if (params->textureMode[0] & TEXTUREMODE_TRILINEAR) {
            addbyte(0x41); /*XOR EBX, R13(i_00_ff_w)[ECX*4]*/
            addbyte(0x33);
            addbyte(0x5c);
            addbyte(0x8d);
            addbyte(0);
        } else if (!tca_reverse_blend) {
            addbyte(0x81); /*XOR EBX, 0xFF*/
            addbyte(0xf3);
            addlong(0xff);
        }

        addbyte(0x83); /*ADD EBX, 1*/
        addbyte(0xc3);
        addbyte(1);
        addbyte(0x0f); /*IMUL EAX, EBX*/
        addbyte(0xaf);
        addbyte(0xc3);
        addbyte(0x31); /*XOR EDX, EDX*/
        addbyte(0xd2);
        addbyte(0xc1); /*SAR EAX, 8*/
        addbyte(0xf8);
        addbyte(8);
        if (tca_add_clocal || tca_add_alocal) {
            addbyte(0x66); /*MOV EBX, XMM7*/
            addbyte(0x0f);
            addbyte(0x7e);
            addbyte(0xfb);
            addbyte(0xc1); /*SHR EBX, 24*/
            addbyte(0xeb);
            addbyte(24);
            addbyte(0x01); /*ADD EAX, EBX*/
            addbyte(0xd8);
        }
        addbyte(0x0f); /*CMOVS EAX, EDX*/
        addbyte(0x48);
        addbyte(0xc2);
        addbyte(0xba); /*MOV EDX, 0xff*/
        addlong(0xff);
        addbyte(0x3d); /*CMP EAX, 0xff*/
        addlong(0xff);
        addbyte(0x0f); /*CMOVA EAX, EDX*/
        addbyte(0x47);
        addbyte(0xc2);
        if (tca_invert_output) {
            addbyte(0x35); /*XOR EAX, 0xff*/
            addlong(0xff);
        }

        addbyte(0x89); /*MOV state->tex_a[EDI], EAX*/
        addbyte(0x87);
        addlong(offsetof(voodoo_state_t, tex_a));

        addbyte(0xf3); /*MOVQ XMM0, XMM1*/
        addbyte(0x0f);
        addbyte(0x7e);
        addbyte(0xc1);
    }
    if (cc_mselect == CC_MSELECT_TEXRGB) {
        addbyte(0xf3); /*MOVD XMM4, XMM0*/
        addbyte(0x0f);
        addbyte(0x7e);
        addbyte(0xe0);
    }

    if ((params->fbzMode & FBZ_CHROMAKEY)) {
        switch (_rgb_sel) {
            case CC_LOCALSELECT_ITER_RGB:
                addbyte(0xf3); /*MOVDQU XMM0, ib*/ /* ir, ig and ib must be in same dqword!*/
                addbyte(0x0f);
                addbyte(0x6f);
                addbyte(0x87);
                addlong(offsetof(voodoo_state_t, ib));
                addbyte(0x66); /*PSRAD XMM0, 12*/
                addbyte(0x0f);
                addbyte(0x72);
                addbyte(0xe0);
                addbyte(12);
                addbyte(0x66); /*PACKSSDW XMM0, XMM0*/
                addbyte(0x0f);
                addbyte(0x6b);
                addbyte(0xc0);
                addbyte(0x66); /*PACKUSWB XMM0, XMM0*/
                addbyte(0x0f);
                addbyte(0x67);
                addbyte(0xc0);
                addbyte(0x66); /*MOVD EAX, XMM0*/
                addbyte(0x0f);
                addbyte(0x7e);
                addbyte(0xc0);
                break;
            case CC_LOCALSELECT_COLOR1:
                addbyte(0x8b); /*MOV EAX, params->color1[RSI]*/
                addbyte(0x86);
                addlong(offsetof(voodoo_params_t, color1));
                break;
            case CC_LOCALSELECT_TEX:
                addbyte(0x66); /*MOVD EAX, XMM0*/
                addbyte(0x0f);
                addbyte(0x7e);
                addbyte(0xc0);
                break;
        }
        addbyte(0x8b); /*MOV EBX, params->chromaKey[ESI]*/
        addbyte(0x9e);
        addlong(offsetof(voodoo_params_t, chromaKey));
        addbyte(0x31); /*XOR EBX, EAX*/
        addbyte(0xc3);
        addbyte(0x81); /*AND EBX, 0xffffff*/
        addbyte(0xe3);
        addlong(0xffffff);
        addbyte(0x0f); /*JE skip*/
        addbyte(0x84);
        chroma_skip_pos = block_pos;
        addlong(0);
    }

    if (voodoo->trexInit1[0] & (1 << 18)) {
        addbyte(0xb8); /*MOV EAX, tmuConfig*/
        addlong(voodoo->tmuConfig);
        addbyte(0x66); /*MOVD XMM0, EAX*/
        addbyte(0x0f);
        addbyte(0x6e);
        addbyte(0xc0);
    }

    if (params->alphaMode & ((1 << 0) | (1 << 4))) {
        /*EBX = a_other*/
        switch (a_sel) {
            case A_SEL_ITER_A:
                addbyte(0x8b); /*MOV EBX, state->ia*/
                addbyte(0x9f);
                addlong(offsetof(voodoo_state_t, ia));
                addbyte(0x31); /*XOR EAX, EAX*/
                addbyte(0xc0);
                addbyte(0xba); /*MOV EDX, 0xff*/
                addlong(0xff);
                addbyte(0xc1); /*SAR EBX, 12*/
                addbyte(0xfb);
                addbyte(12);
                addbyte(0x0f); /*CMOVS EBX, EAX*/
                addbyte(0x48);
                addbyte(0xd8);
                addbyte(0x39); /*CMP EBX, EDX*/
                addbyte(0xd3);
                addbyte(0x0f); /*CMOVA EBX, EDX*/
                addbyte(0x47);
                addbyte(0xda);
                break;
            case A_SEL_TEX:
                addbyte(0x8b); /*MOV EBX, state->tex_a*/
                addbyte(0x9f);
                addlong(offsetof(voodoo_state_t, tex_a));
                break;
            case A_SEL_COLOR1:
                addbyte(0x0f); /*MOVZX EBX, params->color1+3*/
                addbyte(0xb6);
                addbyte(0x9e);
                addlong(offsetof(voodoo_params_t, color1) + 3);
                break;
            default:
                addbyte(0x31); /*XOR EBX, EBX*/
                addbyte(0xdb);
                break;
        }
        if (params->fbzMode & FBZ_ALPHA_MASK) {
            addbyte(0xf7); /*TEST EBX, 1*/
            addbyte(0xc3);
            addlong(1);
            addbyte(0x0f); /* JZ amask_skip_pos */
            addbyte(0x84);
            amask_skip_pos = block_pos;
            addlong(0);
        }
        /*ECX = a_local*/
        switch (cca_localselect) {
            case CCA_LOCALSELECT_ITER_A:
                if (a_sel == A_SEL_ITER_A) {
                    addbyte(0x89); /*MOV ECX, EBX*/
                    addbyte(0xd9);
                } else {
                    addbyte(0x8b); /*MOV ECX, state->ia*/
                    addbyte(0x8f);
                    addlong(offsetof(voodoo_state_t, ia));
                    addbyte(0x31); /*XOR EAX, EAX*/
                    addbyte(0xc0);
                    addbyte(0xba); /*MOV EDX, 0xff*/
                    addlong(0xff);
                    addbyte(0xc1); /*SAR ECX, 12*/
                    addbyte(0xf9);
                    addbyte(12);
                    addbyte(0x0f); /*CMOVS ECX, EAX*/
                    addbyte(0x48);
                    addbyte(0xc8);
                    addbyte(0x39); /*CMP ECX, EDX*/
                    addbyte(0xd1);
                    addbyte(0x0f); /*CMOVA ECX, EDX*/
                    addbyte(0x47);
                    addbyte(0xca);
                }
                break;
            case CCA_LOCALSELECT_COLOR0:
                addbyte(0x0f); /*MOVZX ECX, params->color0+3*/
                addbyte(0xb6);
                addbyte(0x8e);
                addlong(offsetof(voodoo_params_t, color0) + 3);
                break;
            case CCA_LOCALSELECT_ITER_Z:
                addbyte(0x8b); /*MOV ECX, state->z*/
                addbyte(0x8f);
                addlong(offsetof(voodoo_state_t, z));
                if (a_sel != A_SEL_ITER_A) {
                    addbyte(0x31); /*XOR EAX, EAX*/
                    addbyte(0xc0);
                    addbyte(0xba); /*MOV EDX, 0xff*/
                    addlong(0xff);
                }
                addbyte(0xc1); /*SAR ECX, 20*/
                addbyte(0xf9);
                addbyte(20);
                addbyte(0x0f); /*CMOVS ECX, EAX*/
                addbyte(0x48);
                addbyte(0xc8);
                addbyte(0x39); /*CMP ECX, EDX*/
                addbyte(0xd1);
                addbyte(0x0f); /*CMOVA ECX, EDX*/
                addbyte(0x47);
                addbyte(0xca);
                break;

            default:
                addbyte(0xb9); /*MOV ECX, 0xff*/
                addlong(0xff);
                break;
        }

        if (cca_zero_other) {
            addbyte(0x31); /*XOR EDX, EDX*/
            addbyte(0xd2);
        } else {
            addbyte(0x89); /*MOV EDX, EBX*/
            addbyte(0xda);
        }

        if (cca_sub_clocal) {
            addbyte(0x29); /*SUB EDX, ECX*/
            addbyte(0xca);
        }
    }

    if (cc_sub_clocal || cc_mselect == 1 || cc_add == 1) {
        /*XMM1 = local*/
        if (!cc_localselect_override) {
            if (cc_localselect) {
                addbyte(0x66); /*MOVD XMM1, params->color0*/
                addbyte(0x0f);
                addbyte(0x6e);
                addbyte(0x8e);
                addlong(offsetof(voodoo_params_t, color0));
            } else {
                addbyte(0xf3); /*MOVDQU XMM1, ib*/ /* ir, ig and ib must be in same dqword!*/
                addbyte(0x0f);
                addbyte(0x6f);
                addbyte(0x8f);
                addlong(offsetof(voodoo_state_t, ib));
                addbyte(0x66); /*PSRAD XMM1, 12*/
                addbyte(0x0f);
                addbyte(0x72);
                addbyte(0xe1);
                addbyte(12);
                addbyte(0x66); /*PACKSSDW XMM1, XMM1*/
                addbyte(0x0f);
                addbyte(0x6b);
                addbyte(0xc9);
                addbyte(0x66); /*PACKUSWB XMM1, XMM1*/
                addbyte(0x0f);
                addbyte(0x67);
                addbyte(0xc9);
            }
        } else {
            addbyte(0xf6); /*TEST state->tex_a, 0x80*/
            addbyte(0x87);
            addbyte(0x23);
            addlong(offsetof(voodoo_state_t, tex_a));
            addbyte(0x80);
            addbyte(0x74); /*JZ !cc_localselect*/
            addbyte(8 + 2);
            addbyte(0x66); /*MOVD XMM1, params->color0*/
            addbyte(0x0f);
            addbyte(0x6e);
            addbyte(0x8e);
            addlong(offsetof(voodoo_params_t, color0));
            addbyte(0xeb); /*JMP +*/
            addbyte(8 + 5 + 4 + 4);
            /*!cc_localselect:*/
            addbyte(0xf3); /*MOVDQU XMM1, ib*/ /* ir, ig and ib must be in same dqword!*/
            addbyte(0x0f);
            addbyte(0x6f);
            addbyte(0x8f);
            addlong(offsetof(voodoo_state_t, ib));
            addbyte(0x66); /*PSRAD XMM1, 12*/
            addbyte(0x0f);
            addbyte(0x72);
            addbyte(0xe1);
            addbyte(12);
            addbyte(0x66); /*PACKSSDW XMM1, XMM1*/
            addbyte(0x0f);
            addbyte(0x6b);
            addbyte(0xc9);
            addbyte(0x66); /*PACKUSWB XMM1, XMM1*/
            addbyte(0x0f);
            addbyte(0x67);
            addbyte(0xc9);
        }
        addbyte(0x66); /*PUNPCKLBW XMM1, XMM2*/
        addbyte(0x0f);
        addbyte(0x60);
        addbyte(0xca);
    }
    if (!cc_zero_other) {
        if (_rgb_sel == CC_LOCALSELECT_ITER_RGB) {
            addbyte(0xf3); /*MOVDQU XMM0, ib*/ /* ir, ig and ib must be in same dqword!*/
            addbyte(0x0f);
            addbyte(0x6f);
            addbyte(0x87);
            addlong(offsetof(voodoo_state_t, ib));
            addbyte(0x66); /*PSRAD XMM0, 12*/
            addbyte(0x0f);
            addbyte(0x72);
            addbyte(0xe0);
            addbyte(12);
            addbyte(0x66); /*PACKSSDW XMM0, XMM0*/
            addbyte(0x0f);
            addbyte(0x6b);
            addbyte(0xc0);
            addbyte(0x66); /*PACKUSWB XMM0, XMM0*/
            addbyte(0x0f);
            addbyte(0x67);
            addbyte(0xc0);
        } else if (_rgb_sel == CC_LOCALSELECT_TEX) {
#if 0
                        addbyte(0xf3); /*MOVDQU XMM0, state->tex_b*/
                        addbyte(0x0f);
                        addbyte(0x6f);
                        addbyte(0x87);
                        addlong(offsetof(voodoo_state_t, tex_b));
                        addbyte(0x66); /*PACKSSDW XMM0, XMM0*/
                        addbyte(0x0f);
                        addbyte(0x6b);
                        addbyte(0xc0);
                        addbyte(0x66); /*PACKUSWB XMM0, XMM0*/
                        addbyte(0x0f);
                        addbyte(0x67);
                        addbyte(0xc0);
#endif
        } else if (_rgb_sel == CC_LOCALSELECT_COLOR1) {
            addbyte(0x66); /*MOVD XMM0, params->color1*/
            addbyte(0x0f);
            addbyte(0x6e);
            addbyte(0x86);
            addlong(offsetof(voodoo_params_t, color1));
        } else {
            /*MOVD XMM0, src_r*/
        }
        addbyte(0x66); /*PUNPCKLBW XMM0, XMM2*/
        addbyte(0x0f);
        addbyte(0x60);
        addbyte(0xc2);
        if (cc_sub_clocal) {
            addbyte(0x66); /*PSUBW XMM0, XMM1*/
            addbyte(0x0f);
            addbyte(0xf9);
            addbyte(0xc1);
        }
    } else {
        addbyte(0x66); /*PXOR XMM0, XMM0*/
        addbyte(0x0f);
        addbyte(0xef);
        addbyte(0xc0);
        if (cc_sub_clocal) {
            addbyte(0x66); /*PSUBW XMM0, XMM1*/
            addbyte(0x0f);
            addbyte(0xf9);
            addbyte(0xc1);
        }
    }

    if (params->alphaMode & ((1 << 0) | (1 << 4))) {
        if (!(cca_mselect == 0 && cca_reverse_blend == 0)) {
            switch (cca_mselect) {
                case CCA_MSELECT_ALOCAL:
                    addbyte(0x89); /*MOV EAX, ECX*/
                    addbyte(0xc8);
                    break;
                case CCA_MSELECT_AOTHER:
                    addbyte(0x89); /*MOV EAX, EBX*/
                    addbyte(0xd8);
                    break;
                case CCA_MSELECT_ALOCAL2:
                    addbyte(0x89); /*MOV EAX, ECX*/
                    addbyte(0xc8);
                    break;
                case CCA_MSELECT_TEX:
                    addbyte(0x0f); /*MOVZX EAX, state->tex_a*/
                    addbyte(0xb6);
                    addbyte(0x87);
                    addlong(offsetof(voodoo_state_t, tex_a));
                    break;

                case CCA_MSELECT_ZERO:
                default:
                    addbyte(0x31); /*XOR EAX, EAX*/
                    addbyte(0xc0);
                    break;
            }
            if (!cca_reverse_blend) {
                addbyte(0x35); /*XOR EAX, 0xff*/
                addlong(0xff);
            }
            addbyte(0x83); /*ADD EAX, 1*/
            addbyte(0xc0);
            addbyte(1);
            addbyte(0x0f); /*IMUL EDX, EAX*/
            addbyte(0xaf);
            addbyte(0xd0);
            addbyte(0xc1); /*SHR EDX, 8*/
            addbyte(0xea);
            addbyte(8);
        }
    }

    if ((params->alphaMode & ((1 << 0) | (1 << 4)))) {
        addbyte(0x31); /*XOR EAX, EAX*/
        addbyte(0xc0);
    }

    if (!(cc_mselect == 0 && cc_reverse_blend == 0) && cc_mselect == CC_MSELECT_AOTHER) {
        /*Copy a_other to XMM3 before it gets modified*/
        addbyte(0x66); /*MOVD XMM3, EDX*/
        addbyte(0x0f);
        addbyte(0x6e);
        addbyte(0xda);
        addbyte(0xf2); /*PSHUFLW XMM3, XMM3, 0*/
        addbyte(0x0f);
        addbyte(0x70);
        addbyte(0xdb);
        addbyte(0x00);
    }

    if (cca_add && (params->alphaMode & ((1 << 0) | (1 << 4)))) {
        addbyte(0x01); /*ADD EDX, ECX*/
        addbyte(0xca);
    }

    if ((params->alphaMode & ((1 << 0) | (1 << 4)))) {
        addbyte(0x85); /*TEST EDX, EDX*/
        addbyte(0xd2);
        addbyte(0x0f); /*CMOVS EDX, EAX*/
        addbyte(0x48);
        addbyte(0xd0);
        addbyte(0xb8); /*MOV EAX, 0xff*/
        addlong(0xff);
        addbyte(0x81); /*CMP EDX, 0xff*/
        addbyte(0xfa);
        addlong(0xff);
        addbyte(0x0f); /*CMOVA EDX, EAX*/
        addbyte(0x47);
        addbyte(0xd0);
        if (cca_invert_output) {
            addbyte(0x81); /*XOR EDX, 0xff*/
            addbyte(0xf2);
            addlong(0xff);
        }
    }

    if (!(cc_mselect == 0 && cc_reverse_blend == 0)) {
        switch (cc_mselect) {
            case CC_MSELECT_ZERO:
                addbyte(0x66); /*PXOR XMM3, XMM3*/
                addbyte(0x0f);
                addbyte(0xef);
                addbyte(0xdb);
                break;
            case CC_MSELECT_CLOCAL:
                addbyte(0xf3); /*MOV XMM3, XMM1*/
                addbyte(0x0f);
                addbyte(0x7e);
                addbyte(0xd9);
                break;
            case CC_MSELECT_ALOCAL:
                addbyte(0x66); /*MOVD XMM3, ECX*/
                addbyte(0x0f);
                addbyte(0x6e);
                addbyte(0xd9);
                addbyte(0xf2); /*PSHUFLW XMM3, XMM3, 0*/
                addbyte(0x0f);
                addbyte(0x70);
                addbyte(0xdb);
                addbyte(0x00);
                break;
            case CC_MSELECT_AOTHER:
                /*Handled above*/
                break;
            case CC_MSELECT_TEX:
                addbyte(0x66); /*PINSRW XMM3, state->tex_a, 0*/
                addbyte(0x0f);
                addbyte(0xc4);
                addbyte(0x9f);
                addlong(offsetof(voodoo_state_t, tex_a));
                addbyte(0);
                addbyte(0x66); /*PINSRW XMM3, state->tex_a, 1*/
                addbyte(0x0f);
                addbyte(0xc4);
                addbyte(0x9f);
                addlong(offsetof(voodoo_state_t, tex_a));
                addbyte(1);
                addbyte(0x66); /*PINSRW XMM3, state->tex_a, 2*/
                addbyte(0x0f);
                addbyte(0xc4);
                addbyte(0x9f);
                addlong(offsetof(voodoo_state_t, tex_a));
                addbyte(2);
                break;
            case CC_MSELECT_TEXRGB:
                addbyte(0x66); /*PUNPCKLBW XMM4, XMM2*/
                addbyte(0x0f);
                addbyte(0x60);
                addbyte(0xe2);
                addbyte(0xf3); /*MOVQ XMM3, XMM4*/
                addbyte(0x0f);
                addbyte(0x7e);
                addbyte(0xdc);
                break;
            default:
                addbyte(0x66); /*PXOR XMM3, XMM3*/
                addbyte(0x0f);
                addbyte(0xef);
                addbyte(0xdb);
                break;
        }
        addbyte(0xf3); /*MOV XMM4, XMM0*/
        addbyte(0x0f);
        addbyte(0x7e);
        addbyte(0xe0);
        if (!cc_reverse_blend) {
            addbyte(0x66); /*PXOR XMM3, XMM9(xmm_ff_w)*/
            addbyte(0x41);
            addbyte(0x0f);
            addbyte(0xef);
            addbyte(0xd9);
        }
        addbyte(0x66); /*PADDW XMM3, XMM8(xmm_01_w)*/
        addbyte(0x41);
        addbyte(0x0f);
        addbyte(0xfd);
        addbyte(0xd8);
        addbyte(0x66); /*PMULLW XMM0, XMM3*/
        addbyte(0x0f);
        addbyte(0xd5);
        addbyte(0xc3);
        addbyte(0x66); /*PMULHW XMM4, XMM3*/
        addbyte(0x0f);
        addbyte(0xe5);
        addbyte(0xe3);
        addbyte(0x66); /*PUNPCKLWD XMM0, XMM4*/
        addbyte(0x0f);
        addbyte(0x61);
        addbyte(0xc4);
        addbyte(0x66); /*PSRLD XMM0, 8*/
        addbyte(0x0f);
        addbyte(0x72);
        addbyte(0xe0);
        addbyte(8);
        addbyte(0x66); /*PACKSSDW XMM0, XMM0*/
        addbyte(0x0f);
        addbyte(0x6b);
        addbyte(0xc0);
    }

    if (cc_add == 1) {
        addbyte(0x66); /*PADDW XMM0, XMM1*/
        addbyte(0x0f);
        addbyte(0xfd);
        addbyte(0xc1);
    }

    addbyte(0x66); /*PACKUSWB XMM0, XMM0*/
    addbyte(0x0f);
    addbyte(0x67);
    addbyte(0xc0);

    if (cc_invert_output) {
        addbyte(0x66); /*PXOR XMM0, XMM10(xmm_ff_b)*/
        addbyte(0x41);
        addbyte(0x0f);
        addbyte(0xef);
        addbyte(0xc2);
    }

    addbyte(0xf3); /*MOVQ XMM15(colbfog), XMM0 */
    addbyte(0x44);
    addbyte(0x0f);
    addbyte(0x7e);
    addbyte(0xf8);

    if (params->fogMode & FOG_ENABLE) {
        if (params->fogMode & FOG_CONSTANT) {
            addbyte(0x66); /*MOVD XMM3, params->fogColor[ESI]*/
            addbyte(0x0f);
            addbyte(0x6e);
            addbyte(0x9e);
            addlong(offsetof(voodoo_params_t, fogColor));
            addbyte(0x66); /*PADDUSB XMM0, XMM3*/
            addbyte(0x0f);
            addbyte(0xdc);
            addbyte(0xc3);
        } else {
            addbyte(0x66); /*PUNPCKLBW XMM0, XMM2*/
            addbyte(0x0f);
            addbyte(0x60);
            addbyte(0xc2);

            if (!(params->fogMode & FOG_ADD)) {
                addbyte(0x66); /*MOVD XMM3, params->fogColor[ESI]*/
                addbyte(0x0f);
                addbyte(0x6e);
                addbyte(0x9e);
                addlong(offsetof(voodoo_params_t, fogColor));
                addbyte(0x66); /*PUNPCKLBW XMM3, XMM2*/
                addbyte(0x0f);
                addbyte(0x60);
                addbyte(0xda);
            } else {
                addbyte(0x66); /*PXOR XMM3, XMM3*/
                addbyte(0x0f);
                addbyte(0xef);
                addbyte(0xdb);
            }

            if (!(params->fogMode & FOG_MULT)) {
                addbyte(0x66); /*PSUBW XMM3, XMM0*/
                addbyte(0x0f);
                addbyte(0xf9);
                addbyte(0xd8);
            }

            /*Divide by 2 to prevent overflow on multiply*/
            addbyte(0x66); /*PSRAW XMM3, 1*/
            addbyte(0x0f);
            addbyte(0x71);
            addbyte(0xe3);
            addbyte(1);

            switch (params->fogMode & (FOG_Z | FOG_ALPHA)) {
                case 0:
                    addbyte(0x8b); /*MOV EBX, state->w_depth[EDI]*/
                    addbyte(0x9f);
                    addlong(offsetof(voodoo_state_t, w_depth));
                    addbyte(0x89); /*MOV EAX, EBX*/
                    addbyte(0xd8);
                    addbyte(0xc1); /*SHR EBX, 10*/
                    addbyte(0xeb);
                    addbyte(10);
                    addbyte(0xc1); /*SHR EAX, 2*/
                    addbyte(0xe8);
                    addbyte(2);
                    addbyte(0x83); /*AND EBX, 0x3f*/
                    addbyte(0xe3);
                    addbyte(0x3f);
                    addbyte(0x25); /*AND EAX, 0xff*/
                    addlong(0xff);
                    addbyte(0xf6); /*MUL params->fogTable+1[ESI+EBX*2]*/
                    addbyte(0xa4);
                    addbyte(0x5e);
                    addlong(offsetof(voodoo_params_t, fogTable) + 1);
                    addbyte(0x0f); /*MOVZX EBX, params->fogTable[ESI+EBX*2]*/
                    addbyte(0xb6);
                    addbyte(0x9c);
                    addbyte(0x5e);
                    addlong(offsetof(voodoo_params_t, fogTable));
                    addbyte(0xc1); /*SHR EAX, 10*/
                    addbyte(0xe8);
                    addbyte(10);
                    addbyte(0x01); /*ADD EAX, EBX*/
                    addbyte(0xd8);
#if 0
                    int fog_idx = (w_depth >> 10) & 0x3f;

                    fog_a = params->fogTable[fog_idx].fog;
                    fog_a += (params->fogTable[fog_idx].dfog * ((w_depth >> 2) & 0xff)) >> 10;
#endif
                    break;

                case FOG_Z:
                    addbyte(0x8b); /*MOV EAX, state->z[EDI]*/
                    addbyte(0x87);
                    addlong(offsetof(voodoo_state_t, z));
                    addbyte(0xc1); /*SHR EAX, 12*/
                    addbyte(0xe8);
                    addbyte(20);
                    addbyte(0x25); /*AND EAX, 0xff*/
                    addlong(0xff);
#if 0
                    fog_a = (z >> 20) & 0xff;
#endif
                    break;

                case FOG_ALPHA:
                    addbyte(0x8b); /*MOV EAX, state->ia[EDI]*/
                    addbyte(0x87);
                    addlong(offsetof(voodoo_state_t, ia));
                    addbyte(0x31); /*XOR EBX, EBX*/
                    addbyte(0xdb);
                    addbyte(0xc1); /*SAR EAX, 12*/
                    addbyte(0xf8);
                    addbyte(12);
                    addbyte(0x0f); /*CMOVS EAX, EBX*/
                    addbyte(0x48);
                    addbyte(0xc3);
                    addbyte(0xbb); /*MOV EBX, 0xff*/
                    addlong(0xff);
                    addbyte(0x3d); /*CMP EAX, 0xff*/
                    addlong(0xff);
                    addbyte(0x0f); /*CMOVAE EAX, EBX*/
                    addbyte(0x43);
                    addbyte(0xc3);
#if 0
                    fog_a = CLAMP(ia >> 12);
#endif
                    break;

                case FOG_W:
                    addbyte(0x8b); /*MOV EAX, state->w[EDI]+4*/
                    addbyte(0x87);
                    addlong(offsetof(voodoo_state_t, w) + 4);
                    addbyte(0x31); /*XOR EBX, EBX*/
                    addbyte(0xdb);
                    addbyte(0x09); /*OR EAX, EAX*/
                    addbyte(0xc0);
                    addbyte(0x0f); /*CMOVS EAX, EBX*/
                    addbyte(0x48);
                    addbyte(0xc3);
                    addbyte(0xbb); /*MOV EBX, 0xff*/
                    addlong(0xff);
                    addbyte(0x3d); /*CMP EAX, 0xff*/
                    addlong(0xff);
                    addbyte(0x0f); /*CMOVAE EAX, EBX*/
                    addbyte(0x43);
                    addbyte(0xc3);
#if 0
                    fog_a = CLAMP(w >> 32);
#endif
                    break;
            }
            addbyte(0x01); /*ADD EAX, EAX*/
            addbyte(0xc0);

            addbyte(0x66); /*PMULLW XMM3, alookup+4[EAX*8]*/
            addbyte(0x41);
            addbyte(0x0f);
            addbyte(0xd5);
            addbyte(0x5c);
            addbyte(0xc2);
            addbyte(16);
            addbyte(0x66); /*PSRAW XMM3, 7*/
            addbyte(0x0f);
            addbyte(0x71);
            addbyte(0xe3);
            addbyte(7);

            if (params->fogMode & FOG_MULT) {
                addbyte(0xf3); /*MOV XMM0, XMM3*/
                addbyte(0x0f);
                addbyte(0x7e);
                addbyte(0xc3);
            } else {
                addbyte(0x66); /*PADDW XMM0, XMM3*/
                addbyte(0x0f);
                addbyte(0xfd);
                addbyte(0xc3);
            }
            addbyte(0x66); /*PACKUSWB XMM0, XMM0*/
            addbyte(0x0f);
            addbyte(0x67);
            addbyte(0xc0);
        }
    }

    if ((params->alphaMode & 1) && (alpha_func != AFUNC_NEVER) && (alpha_func != AFUNC_ALWAYS)) {
        addbyte(0x0f); /*MOVZX ECX, params->alphaMode+3*/
        addbyte(0xb6);
        addbyte(0x8e);
        addlong(offsetof(voodoo_params_t, alphaMode) + 3);
        addbyte(0x39); /*CMP EDX, ECX*/
        addbyte(0xca);

        switch (alpha_func) {
            case AFUNC_LESSTHAN:
                addbyte(0x0f); /*JAE skip*/
                addbyte(0x83);
                a_skip_pos = block_pos;
                addlong(0);
                break;
            case AFUNC_EQUAL:
                addbyte(0x0f); /*JNE skip*/
                addbyte(0x85);
                a_skip_pos = block_pos;
                addlong(0);
                break;
            case AFUNC_LESSTHANEQUAL:
                addbyte(0x0f); /*JA skip*/
                addbyte(0x87);
                a_skip_pos = block_pos;
                addlong(0);
                break;
            case AFUNC_GREATERTHAN:
                addbyte(0x0f); /*JBE skip*/
                addbyte(0x86);
                a_skip_pos = block_pos;
                addlong(0);
                break;
            case AFUNC_NOTEQUAL:
                addbyte(0x0f); /*JE skip*/
                addbyte(0x84);
                a_skip_pos = block_pos;
                addlong(0);
                break;
            case AFUNC_GREATERTHANEQUAL:
                addbyte(0x0f); /*JB skip*/
                addbyte(0x82);
                a_skip_pos = block_pos;
                addlong(0);
                break;
        }
    } else if ((params->alphaMode & 1) && (alpha_func == AFUNC_NEVER)) {
        addbyte(0xC3); /*RET*/
    }

    if (params->alphaMode & (1 << 4)) {
        if (params->fbzMode & FBZ_ALPHA_ENABLE) {
            addbyte(0x8b); /*MOV EBX, state->x[EDI]*/
            addbyte(0x9f);
            if (params->aux_tiled)
                addlong(offsetof(voodoo_state_t, x_tiled));
            else
                addlong(offsetof(voodoo_state_t, x));
            addbyte(0x48); /*MOV RCX, aux_mem[RDI]*/
            addbyte(0x8b);
            addbyte(0x8f);
            addlong(offsetof(voodoo_state_t, aux_mem));
            addbyte(0x0f); /*MOVZX EBX, [ECX+EBX*2]*/
            addbyte(0xb7);
            addbyte(0x1c);
            addbyte(0x59);
        } else {
            addbyte(0x48); /*MOV RBX, 0xff*/
            addbyte(0xc7);
            addbyte(0xc3);
            addlong(0xff);
        }
        addbyte(0x49); /*MOV R8, rgb565*/
        addbyte(0xb8);
        addquad((uintptr_t) rgb565);
        addbyte(0x8b); /*MOV EAX, state->x[EDI]*/
        addbyte(0x87);
        if (params->col_tiled)
            addlong(offsetof(voodoo_state_t, x_tiled));
        else
            addlong(offsetof(voodoo_state_t, x));
        addbyte(0x48); /*MOV RBP, fb_mem*/
        addbyte(0x8b);
        addbyte(0xaf);
        addlong(offsetof(voodoo_state_t, fb_mem));
        addbyte(0x01); /*ADD EDX, EDX*/
        addbyte(0xd2);
        addbyte(0x01); /*ADD EBX, EBX*/
        addbyte(0xdb);
        addbyte(0x0f); /*MOVZX EAX, [RBP+RAX*2]*/
        addbyte(0xb7);
        addbyte(0x44);
        addbyte(0x45);
        addbyte(0);
        addbyte(0x66); /*PUNPCKLBW XMM0, XMM2*/
        addbyte(0x0f);
        addbyte(0x60);
        addbyte(0xc2);
        addbyte(0x66); /*MOVD XMM4, rgb565[EAX*4]*/
        addbyte(0x41);
        addbyte(0x0f);
        addbyte(0x6e);
        addbyte(0x24);
        addbyte(0x80);
        addbyte(0x66); /*PUNPCKLBW XMM4, XMM2*/
        addbyte(0x0f);
        addbyte(0x60);
        addbyte(0xe2);
        addbyte(0xf3); /*MOV XMM6, XMM4*/
        addbyte(0x0f);
        addbyte(0x7e);
        addbyte(0xf4);

        switch (dest_afunc) {
            case AFUNC_AZERO:
                addbyte(0x66); /*PXOR XMM4, XMM4*/
                addbyte(0x0f);
                addbyte(0xef);
                addbyte(0xe4);
                break;
            case AFUNC_ASRC_ALPHA:
                addbyte(0x66); /*PMULLW XMM4, R10(alookup)[EDX*8]*/
                addbyte(0x41);
                addbyte(0x0f);
                addbyte(0xd5);
                addbyte(0x24);
                addbyte(0xd2);
                addbyte(0xf3); /*MOVQ XMM5, XMM4*/
                addbyte(0x0f);
                addbyte(0x7e);
                addbyte(0xec);
                addbyte(0x66); /*PADDW XMM4, R10(alookup)[1*8]*/
                addbyte(0x41);
                addbyte(0x0f);
                addbyte(0xfd);
                addbyte(0x62);
                addbyte(8 * 2);
                addbyte(0x66); /*PSRLW XMM5, 8*/
                addbyte(0x0f);
                addbyte(0x71);
                addbyte(0xd5);
                addbyte(8);
                addbyte(0x66); /*PADDW XMM4, XMM5*/
                addbyte(0x0f);
                addbyte(0xfd);
                addbyte(0xe5);
                addbyte(0x66); /*PSRLW XMM4, 8*/
                addbyte(0x0f);
                addbyte(0x71);
                addbyte(0xd4);
                addbyte(8);
                break;
            case AFUNC_A_COLOR:
                addbyte(0x66); /*PMULLW XMM4, XMM0*/
                addbyte(0x0f);
                addbyte(0xd5);
                addbyte(0xe0);
                addbyte(0xf3); /*MOVQ XMM5, XMM4*/
                addbyte(0x0f);
                addbyte(0x7e);
                addbyte(0xec);
                addbyte(0x66); /*PADDW XMM4, R10(alookup)[1*8]*/
                addbyte(0x41);
                addbyte(0x0f);
                addbyte(0xfd);
                addbyte(0x62);
                addbyte(8 * 2);
                addbyte(0x66); /*PSRLW XMM5, 8*/
                addbyte(0x0f);
                addbyte(0x71);
                addbyte(0xd5);
                addbyte(8);
                addbyte(0x66); /*PADDW XMM4, XMM5*/
                addbyte(0x0f);
                addbyte(0xfd);
                addbyte(0xe5);
                addbyte(0x66); /*PSRLW XMM4, 8*/
                addbyte(0x0f);
                addbyte(0x71);
                addbyte(0xd4);
                addbyte(8);
                break;
            case AFUNC_ADST_ALPHA:
                addbyte(0x66); /*PMULLW XMM4, R10(alookup)[EBX*8]*/
                addbyte(0x41);
                addbyte(0x0f);
                addbyte(0xd5);
                addbyte(0x24);
                addbyte(0xda);
                addbyte(0xf3); /*MOVQ XMM5, XMM4*/
                addbyte(0x0f);
                addbyte(0x7e);
                addbyte(0xec);
                addbyte(0x66); /*PADDW XMM4, R10(alookup)[1*8]*/
                addbyte(0x41);
                addbyte(0x0f);
                addbyte(0xfd);
                addbyte(0x62);
                addbyte(8 * 2);
                addbyte(0x66); /*PSRLW XMM5, 8*/
                addbyte(0x0f);
                addbyte(0x71);
                addbyte(0xd5);
                addbyte(8);
                addbyte(0x66); /*PADDW XMM4, XMM5*/
                addbyte(0x0f);
                addbyte(0xfd);
                addbyte(0xe5);
                addbyte(0x66); /*PSRLW XMM4, 8*/
                addbyte(0x0f);
                addbyte(0x71);
                addbyte(0xd4);
                addbyte(8);
                break;
            case AFUNC_AONE:
                break;
            case AFUNC_AOMSRC_ALPHA:
                addbyte(0x66); /*PMULLW XMM4, R11(aminuslookup)[EDX*8]*/
                addbyte(0x41);
                addbyte(0x0f);
                addbyte(0xd5);
                addbyte(0x24);
                addbyte(0xd3);
                addbyte(0xf3); /*MOVQ XMM5, XMM4*/
                addbyte(0x0f);
                addbyte(0x7e);
                addbyte(0xec);
                addbyte(0x66); /*PADDW XMM4, R10(alookup)[1*8]*/
                addbyte(0x41);
                addbyte(0x0f);
                addbyte(0xfd);
                addbyte(0x62);
                addbyte(8 * 2);
                addbyte(0x66); /*PSRLW XMM5, 8*/
                addbyte(0x0f);
                addbyte(0x71);
                addbyte(0xd5);
                addbyte(8);
                addbyte(0x66); /*PADDW XMM4, XMM5*/
                addbyte(0x0f);
                addbyte(0xfd);
                addbyte(0xe5);
                addbyte(0x66); /*PSRLW XMM4, 8*/
                addbyte(0x0f);
                addbyte(0x71);
                addbyte(0xd4);
                addbyte(8);
                break;
            case AFUNC_AOM_COLOR:
                addbyte(0xf3); /*MOVQ XMM5, XMM9(xmm_ff_w)*/
                addbyte(0x41);
                addbyte(0x0f);
                addbyte(0x7e);
                addbyte(0xe9);
                addbyte(0x66); /*PSUBW XMM5, XMM0*/
                addbyte(0x0f);
                addbyte(0xf9);
                addbyte(0xe8);
                addbyte(0x66); /*PMULLW XMM4, XMM5*/
                addbyte(0x0f);
                addbyte(0xd5);
                addbyte(0xe5);
                addbyte(0xf3); /*MOVQ XMM5, XMM4*/
                addbyte(0x0f);
                addbyte(0x7e);
                addbyte(0xec);
                addbyte(0x66); /*PADDW XMM4, alookup[1*8]*/
                addbyte(0x41);
                addbyte(0x0f);
                addbyte(0xfd);
                addbyte(0x62);
                addbyte(8 * 2);
                addbyte(0x66); /*PSRLW XMM5, 8*/
                addbyte(0x0f);
                addbyte(0x71);
                addbyte(0xd5);
                addbyte(8);
                addbyte(0x66); /*PADDW XMM4, XMM5*/
                addbyte(0x0f);
                addbyte(0xfd);
                addbyte(0xe5);
                addbyte(0x66); /*PSRLW XMM4, 8*/
                addbyte(0x0f);
                addbyte(0x71);
                addbyte(0xd4);
                addbyte(8);
                break;
            case AFUNC_AOMDST_ALPHA:
                addbyte(0x66); /*PMULLW XMM4, R11(aminuslookup)[EBX*8]*/
                addbyte(0x41);
                addbyte(0x0f);
                addbyte(0xd5);
                addbyte(0x24);
                addbyte(0xdb);
                addbyte(0xf3); /*MOVQ XMM5, XMM4*/
                addbyte(0x0f);
                addbyte(0x7e);
                addbyte(0xec);
                addbyte(0x66); /*PADDW XMM4, R10(alookup)[1*8]*/
                addbyte(0x41);
                addbyte(0x0f);
                addbyte(0xfd);
                addbyte(0x62);
                addbyte(8 * 2);
                addbyte(0x66); /*PSRLW XMM5, 8*/
                addbyte(0x0f);
                addbyte(0x71);
                addbyte(0xd5);
                addbyte(8);
                addbyte(0x66); /*PADDW XMM4, XMM5*/
                addbyte(0x0f);
                addbyte(0xfd);
                addbyte(0xe5);
                addbyte(0x66); /*PSRLW XMM4, 8*/
                addbyte(0x0f);
                addbyte(0x71);
                addbyte(0xd4);
                addbyte(8);
                break;
            case AFUNC_ACOLORBEFOREFOG:
                addbyte(0x66); /*PMULLW XMM4, XMM15(colbfog)*/
                addbyte(0x41);
                addbyte(0x0f);
                addbyte(0xd5);
                addbyte(0xe7);
                addbyte(0xf3); /*MOVQ XMM5, XMM4*/
                addbyte(0x0f);
                addbyte(0x7e);
                addbyte(0xec);
                addbyte(0x66); /*PADDW XMM4, R10(alookup)[1*8]*/
                addbyte(0x41);
                addbyte(0x0f);
                addbyte(0xfd);
                addbyte(0x62);
                addbyte(8 * 2);
                addbyte(0x66); /*PSRLW XMM5, 8*/
                addbyte(0x0f);
                addbyte(0x71);
                addbyte(0xd5);
                addbyte(8);
                addbyte(0x66); /*PADDW XMM4, XMM5*/
                addbyte(0x0f);
                addbyte(0xfd);
                addbyte(0xe5);
                addbyte(0x66); /*PSRLW XMM4, 8*/
                addbyte(0x0f);
                addbyte(0x71);
                addbyte(0xd4);
                addbyte(8);
                break;
        }

        switch (src_afunc) {
            case AFUNC_AZERO:
                addbyte(0x66); /*PXOR XMM0, XMM0*/
                addbyte(0x0f);
                addbyte(0xef);
                addbyte(0xc0);
                break;
            case AFUNC_ASRC_ALPHA:
                addbyte(0x66); /*PMULLW XMM0, R10(alookup)[EDX*8]*/
                addbyte(0x41);
                addbyte(0x0f);
                addbyte(0xd5);
                addbyte(0x04);
                addbyte(0xd2);
                addbyte(0xf3); /*MOVQ XMM5, XMM0*/
                addbyte(0x0f);
                addbyte(0x7e);
                addbyte(0xe8);
                addbyte(0x66); /*PADDW XMM0, R10(alookup)[1*8]*/
                addbyte(0x41);
                addbyte(0x0f);
                addbyte(0xfd);
                addbyte(0x42);
                addbyte(8 * 2);
                addbyte(0x66); /*PSRLW XMM5, 8*/
                addbyte(0x0f);
                addbyte(0x71);
                addbyte(0xd5);
                addbyte(8);
                addbyte(0x66); /*PADDW XMM0, XMM5*/
                addbyte(0x0f);
                addbyte(0xfd);
                addbyte(0xc5);
                addbyte(0x66); /*PSRLW XMM0, 8*/
                addbyte(0x0f);
                addbyte(0x71);
                addbyte(0xd0);
                addbyte(8);
                break;
            case AFUNC_A_COLOR:
                addbyte(0x66); /*PMULLW XMM0, XMM6*/
                addbyte(0x0f);
                addbyte(0xd5);
                addbyte(0xc6);
                addbyte(0xf3); /*MOVQ XMM5, XMM0*/
                addbyte(0x0f);
                addbyte(0x7e);
                addbyte(0xe8);
                addbyte(0x66); /*PADDW XMM0, R10(alookup)[1*8]*/
                addbyte(0x41);
                addbyte(0x0f);
                addbyte(0xfd);
                addbyte(0x42);
                addbyte(8 * 2);
                addbyte(0x66); /*PSRLW XMM5, 8*/
                addbyte(0x0f);
                addbyte(0x71);
                addbyte(0xd5);
                addbyte(8);
                addbyte(0x66); /*PADDW XMM0, XMM5*/
                addbyte(0x0f);
                addbyte(0xfd);
                addbyte(0xc5);
                addbyte(0x66); /*PSRLW XMM0, 8*/
                addbyte(0x0f);
                addbyte(0x71);
                addbyte(0xd0);
                addbyte(8);
                break;
            case AFUNC_ADST_ALPHA:
                addbyte(0x66); /*PMULLW XMM0, R10(alookup)[EBX*8]*/
                addbyte(0x41);
                addbyte(0x0f);
                addbyte(0xd5);
                addbyte(0x04);
                addbyte(0xda);
                addbyte(0xf3); /*MOVQ XMM5, XMM0*/
                addbyte(0x0f);
                addbyte(0x7e);
                addbyte(0xe8);
                addbyte(0x66); /*PADDW XMM0, R10(alookup)[1*8]*/
                addbyte(0x41);
                addbyte(0x0f);
                addbyte(0xfd);
                addbyte(0x42);
                addbyte(8 * 2);
                addbyte(0x66); /*PSRLW XMM5, 8*/
                addbyte(0x0f);
                addbyte(0x71);
                addbyte(0xd5);
                addbyte(8);
                addbyte(0x66); /*PADDW XMM0, XMM5*/
                addbyte(0x0f);
                addbyte(0xfd);
                addbyte(0xc5);
                addbyte(0x66); /*PSRLW XMM0, 8*/
                addbyte(0x0f);
                addbyte(0x71);
                addbyte(0xd0);
                addbyte(8);
                break;
            case AFUNC_AONE:
                break;
            case AFUNC_AOMSRC_ALPHA:
                addbyte(0x66); /*PMULLW XMM0, R11(aminuslookup)[EDX*8]*/
                addbyte(0x41);
                addbyte(0x0f);
                addbyte(0xd5);
                addbyte(0x04);
                addbyte(0xd3);
                addbyte(0xf3); /*MOVQ XMM5, XMM0*/
                addbyte(0x0f);
                addbyte(0x7e);
                addbyte(0xe8);
                addbyte(0x66); /*PADDW XMM0, alookup[1*8]*/
                addbyte(0x41);
                addbyte(0x0f);
                addbyte(0xfd);
                addbyte(0x42);
                addbyte(8 * 2);
                addbyte(0x66); /*PSRLW XMM5, 8*/
                addbyte(0x0f);
                addbyte(0x71);
                addbyte(0xd5);
                addbyte(8);
                addbyte(0x66); /*PADDW XMM0, XMM5*/
                addbyte(0x0f);
                addbyte(0xfd);
                addbyte(0xc5);
                addbyte(0x66); /*PSRLW XMM0, 8*/
                addbyte(0x0f);
                addbyte(0x71);
                addbyte(0xd0);
                addbyte(8);
                break;
            case AFUNC_AOM_COLOR:
                addbyte(0xf3); /*MOVQ XMM5, XMM9(xmm_ff_w)*/
                addbyte(0x41);
                addbyte(0x0f);
                addbyte(0x7e);
                addbyte(0xe9);
                addbyte(0x66); /*PSUBW XMM5, XMM6*/
                addbyte(0x0f);
                addbyte(0xf9);
                addbyte(0xee);
                addbyte(0x66); /*PMULLW XMM0, XMM5*/
                addbyte(0x0f);
                addbyte(0xd5);
                addbyte(0xc5);
                addbyte(0xf3); /*MOVQ XMM5, XMM0*/
                addbyte(0x0f);
                addbyte(0x7e);
                addbyte(0xe8);
                addbyte(0x66); /*PADDW XMM0, alookup[1*8]*/
                addbyte(0x41);
                addbyte(0x0f);
                addbyte(0xfd);
                addbyte(0x42);
                addbyte(8 * 2);
                addbyte(0x66); /*PSRLW XMM5, 8*/
                addbyte(0x0f);
                addbyte(0x71);
                addbyte(0xd5);
                addbyte(8);
                addbyte(0x66); /*PADDW XMM0, XMM5*/
                addbyte(0x0f);
                addbyte(0xfd);
                addbyte(0xc5);
                addbyte(0x66); /*PSRLW XMM0, 8*/
                addbyte(0x0f);
                addbyte(0x71);
                addbyte(0xd0);
                addbyte(8);
                break;
            case AFUNC_AOMDST_ALPHA:
                addbyte(0x66); /*PMULLW XMM0, R11(aminuslookup)[EBX*8]*/
                addbyte(0x41);
                addbyte(0x0f);
                addbyte(0xd5);
                addbyte(0x04);
                addbyte(0xdb);
                addbyte(0xf3); /*MOVQ XMM5, XMM0*/
                addbyte(0x0f);
                addbyte(0x7e);
                addbyte(0xe8);
                addbyte(0x66); /*PADDW XMM0, alookup[1*8]*/
                addbyte(0x41);
                addbyte(0x0f);
                addbyte(0xfd);
                addbyte(0x42);
                addbyte(8 * 2);
                addbyte(0x66); /*PSRLW XMM5, 8*/
                addbyte(0x0f);
                addbyte(0x71);
                addbyte(0xd5);
                addbyte(8);
                addbyte(0x66); /*PADDW XMM0, XMM5*/
                addbyte(0x0f);
                addbyte(0xfd);
                addbyte(0xc5);
                addbyte(0x66); /*PSRLW XMM0, 8*/
                addbyte(0x0f);
                addbyte(0x71);
                addbyte(0xd0);
                addbyte(8);
                break;
            case AFUNC_ASATURATE:
                addbyte(0x89); /* MOV EAX, EBX */
                addbyte(0xd8);
                addbyte(0xd1); /* SHR EAX, 1 */
                addbyte(0xe8);
                addbyte(0x34); /* XOR AL, 0xFF */
                addbyte(0xFF);
                addbyte(0xd1); /* SHL EAX, 1 */
                addbyte(0xe0);
                addbyte(0x39); /* CMP EDX, EAX */
                addbyte(0xc2);
                addbyte(0x0f); /* CMOVB EAX, EDX */
                addbyte(0x42);
                addbyte(0xc2);
                addbyte(0x66); /*PMULLW XMM0, R10(alookup)[EAX*8]*/
                addbyte(0x41);
                addbyte(0x0f);
                addbyte(0xd5);
                addbyte(0x04);
                addbyte(0xc2);
                addbyte(0xf3); /*MOVQ XMM5, XMM0*/
                addbyte(0x0f);
                addbyte(0x7e);
                addbyte(0xe8);
                addbyte(0x66); /*PADDW XMM0, R10(alookup)[1*8]*/
                addbyte(0x41);
                addbyte(0x0f);
                addbyte(0xfd);
                addbyte(0x42);
                addbyte(8 * 2);
                addbyte(0x66); /*PSRLW XMM5, 8*/
                addbyte(0x0f);
                addbyte(0x71);
                addbyte(0xd5);
                addbyte(8);
                addbyte(0x66); /*PADDW XMM0, XMM5*/
                addbyte(0x0f);
                addbyte(0xfd);
                addbyte(0xc5);
                addbyte(0x66); /*PSRLW XMM0, 8*/
                addbyte(0x0f);
                addbyte(0x71);
                addbyte(0xd0);
                addbyte(8);
                break;
        }

        addbyte(0x66); /*PADDW XMM0, XMM4*/
        addbyte(0x0f);
        addbyte(0xfd);
        addbyte(0xc4);

        addbyte(0x66); /*PACKUSWB XMM0, XMM0*/
        addbyte(0x0f);
        addbyte(0x67);
        addbyte(0xc0);

        addbyte(0x31); /*XOR EAX, EAX */
        addbyte(0xc0);

        if (dest_aafunc == 4) {
            addbyte(0xc1); /*SHL EBX, 0x7*/
            addbyte(0xe3);
            addbyte(0x07);
            addbyte(0x01); /*ADD EAX, EBX*/
            addbyte(0xd8);
        }

        if (src_aafunc == 4) {
            addbyte(0xc1); /*SHL EDX, 0x7*/
            addbyte(0xe2);
            addbyte(0x07);
            addbyte(0x01); /*ADD EAX, EDX*/
            addbyte(0xd0);
        }

        addbyte(0xc1); /* SHR EAX, 8 */
        addbyte(0xc8);
        addbyte(0x08);
        addbyte(0x89); /* MOV EDX, EAX */
        addbyte(0xC2);
    }

    if ((params->fbzMode & (FBZ_DEPTH_WMASK | FBZ_ALPHA_ENABLE)) == (FBZ_DEPTH_WMASK | FBZ_ALPHA_ENABLE)) {
        addbyte(0x8b); /*MOV EAX, state->x[EDI]*/
        addbyte(0x87);
        if (params->aux_tiled)
            addlong(offsetof(voodoo_state_t, x_tiled));
        else
            addlong(offsetof(voodoo_state_t, x));
        addbyte(0x48); /*MOV RSI, aux_mem*/
        addbyte(0x8b);
        addbyte(0xb7);
        addlong(offsetof(voodoo_state_t, aux_mem));
        addbyte(0x66); /*MOV [ESI+EAX*2], DX*/
        addbyte(0x89);
        addbyte(0x14);
        addbyte(0x46);
    }

    addbyte(0x8b); /*MOV EDX, state->x[EDI]*/
    addbyte(0x97);
    if (params->col_tiled)
        addlong(offsetof(voodoo_state_t, x_tiled));
    else
        addlong(offsetof(voodoo_state_t, x));

    addbyte(0x66); /*MOV EAX, XMM0*/
    addbyte(0x0f);
    addbyte(0x7e);
    addbyte(0xc0);

    if (params->fbzMode & FBZ_RGB_WMASK) {
        if (dither) {
            addbyte(0x49); /*MOV R8, dither_rb*/
            addbyte(0xb8);
            addquad(dither2x2 ? (uintptr_t) dither_rb2x2 : (uintptr_t) dither_rb);
            addbyte(0x4c); /*MOV ESI, real_y (R14)*/
            addbyte(0x89);
            addbyte(0xf6);
            addbyte(0x0f); /*MOVZX EBX, AH*/ /*G*/
            addbyte(0xb6);
            addbyte(0xdc);
            if (dither2x2) {
                addbyte(0x83); /*AND EDX, 1*/
                addbyte(0xe2);
                addbyte(1);
                addbyte(0x83); /*AND ESI, 1*/
                addbyte(0xe6);
                addbyte(1);
                addbyte(0xc1); /*SHL EBX, 2*/
                addbyte(0xe3);
                addbyte(2);
            } else {
                addbyte(0x83); /*AND EDX, 3*/
                addbyte(0xe2);
                addbyte(3);
                addbyte(0x83); /*AND ESI, 3*/
                addbyte(0xe6);
                addbyte(3);
                addbyte(0xc1); /*SHL EBX, 4*/
                addbyte(0xe3);
                addbyte(4);
            }
            addbyte(0x0f); /*MOVZX ECX, AL*/ /*R*/
            addbyte(0xb6);
            addbyte(0xc8);
            if (dither2x2) {
                addbyte(0xc1); /*SHR EAX, 14*/
                addbyte(0xe8);
                addbyte(14);
                addbyte(0x8d); /*LEA ESI, RDX+RSI*2*/
                addbyte(0x34);
                addbyte(0x72);
            } else {
                addbyte(0xc1); /*SHR EAX, 12*/
                addbyte(0xe8);
                addbyte(12);
                addbyte(0x8d); /*LEA ESI, RDX+RSI*4*/
                addbyte(0x34);
                addbyte(0xb2);
            }
            addbyte(0x8b); /*MOV EDX, state->x[EDI]*/
            addbyte(0x97);
            if (voodoo->col_tiled)
                addlong(offsetof(voodoo_state_t, x_tiled));
            else
                addlong(offsetof(voodoo_state_t, x));
            addbyte(0x4c); /*ADD RSI, R8*/
            addbyte(0x01);
            addbyte(0xc6);
            if (dither2x2) {
                addbyte(0xc1); /*SHL ECX, 2*/
                addbyte(0xe1);
                addbyte(2);
                addbyte(0x25); /*AND EAX, 0x3fc*/ /*B*/
                addlong(0x3fc);
            } else {
                addbyte(0xc1); /*SHL ECX, 4*/
                addbyte(0xe1);
                addbyte(4);
                addbyte(0x25); /*AND EAX, 0xff0*/ /*B*/
                addlong(0xff0);
            }
            addbyte(0x0f); /*MOVZX EBX, dither_g[EBX+ESI]*/
            addbyte(0xb6);
            addbyte(0x9c);
            addbyte(0x1e);
            addlong(dither2x2 ? ((uintptr_t) dither_g2x2 - (uintptr_t) dither_rb2x2) : ((uintptr_t) dither_g - (uintptr_t) dither_rb));
            addbyte(0x0f); /*MOVZX ECX, dither_rb[RCX+RSI]*/
            addbyte(0xb6);
            addbyte(0x0c);
            addbyte(0x0e);
            addbyte(0x0f); /*MOVZX EAX, dither_rb[RAX+RSI]*/
            addbyte(0xb6);
            addbyte(0x04);
            addbyte(0x06);
            addbyte(0xc1); /*SHL EBX, 5*/
            addbyte(0xe3);
            addbyte(5);
            addbyte(0xc1); /*SHL EAX, 11*/
            addbyte(0xe0);
            addbyte(11);
            addbyte(0x09); /*OR EAX, EBX*/
            addbyte(0xd8);
            addbyte(0x09); /*OR EAX, ECX*/
            addbyte(0xc8);
        } else {
            addbyte(0x89); /*MOV EBX, EAX*/
            addbyte(0xc3);
            addbyte(0x0f); /*MOVZX ECX, AH*/
            addbyte(0xb6);
            addbyte(0xcc);
            addbyte(0xc1); /*SHR EAX, 3*/
            addbyte(0xe8);
            addbyte(3);
            addbyte(0xc1); /*SHR EBX, 8*/
            addbyte(0xeb);
            addbyte(8);
            addbyte(0xc1); /*SHL ECX, 3*/
            addbyte(0xe1);
            addbyte(3);
            addbyte(0x81); /*AND EAX, 0x001f*/
            addbyte(0xe0);
            addlong(0x001f);
            addbyte(0x81); /*AND EBX, 0xf800*/
            addbyte(0xe3);
            addlong(0xf800);
            addbyte(0x81); /*AND ECX, 0x07e0*/
            addbyte(0xe1);
            addlong(0x07e0);
            addbyte(0x09); /*OR EAX, EBX*/
            addbyte(0xd8);
            addbyte(0x09); /*OR EAX, ECX*/
            addbyte(0xc8);
        }
        addbyte(0x48); /*MOV RSI, fb_mem*/
        addbyte(0x8b);
        addbyte(0xb7);
        addlong(offsetof(voodoo_state_t, fb_mem));
        addbyte(0x66); /*MOV [ESI+EDX*2], AX*/
        addbyte(0x89);
        addbyte(0x04);
        addbyte(0x56);
    }


    if ((params->fbzMode & (FBZ_DEPTH_WMASK | FBZ_DEPTH_ENABLE)) == (FBZ_DEPTH_WMASK | FBZ_DEPTH_ENABLE) && !(params->fbzMode & FBZ_ALPHA_ENABLE)) {
        addbyte(0x8b); /*MOV EDX, state->x[EDI]*/
        addbyte(0x97);
        if (params->aux_tiled)
            addlong(offsetof(voodoo_state_t, x_tiled));
        else
            addlong(offsetof(voodoo_state_t, x));
        addbyte(0x66); /*MOV AX, new_depth*/
        addbyte(0x8b);
        addbyte(0x87);
        addlong(offsetof(voodoo_state_t, new_depth));
        addbyte(0x48); /*MOV RSI, aux_mem*/
        addbyte(0x8b);
        addbyte(0xb7);
        addlong(offsetof(voodoo_state_t, aux_mem));
        addbyte(0x66); /*MOV [ESI+EDX*2], AX*/
        addbyte(0x89);
        addbyte(0x04);
        addbyte(0x56);
    }

    if (z_skip_pos)
        *(uint32_t *) &code_block[z_skip_pos] = (block_pos - z_skip_pos) - 4;
    if (a_skip_pos)
        *(uint32_t *) &code_block[a_skip_pos] = (block_pos - a_skip_pos) - 4;
    if (chroma_skip_pos)
        *(uint32_t *) &code_block[chroma_skip_pos] = (block_pos - chroma_skip_pos) - 4;
    if (amask_skip_pos)
        *(uint32_t *) &code_block[amask_skip_pos] = (block_pos - amask_skip_pos) - 4;
    if (stipple_skip_pos)
        *(uint32_t *) &code_block[stipple_skip_pos] = (block_pos - stipple_skip_pos) - 4;

    addbyte(0x4c); /*MOV RSI, R15*/
    addbyte(0x89);
    addbyte(0xfe);

    addbyte(0xf3); /*MOVDQU XMM1, state->ib[EDI]*/
    addbyte(0x0f);
    addbyte(0x6f);
    addbyte(0x8f);
    addlong(offsetof(voodoo_state_t, ib));
    addbyte(0xf3); /*MOVDQU XMM3, state->tmu0_s[EDI]*/
    addbyte(0x0f);
    addbyte(0x6f);
    addbyte(0x9f);
    addlong(offsetof(voodoo_state_t, tmu0_s));
    addbyte(0xf3); /*MOVQ XMM4, state->tmu0_w[EDI]*/
    addbyte(0x0f);
    addbyte(0x7e);
    addbyte(0xa7);
    addlong(offsetof(voodoo_state_t, tmu0_w));
    addbyte(0xf3); /*MOVDQU XMM0, params->dBdX[ESI]*/
    addbyte(0x0f);
    addbyte(0x6f);
    addbyte(0x86);
    addlong(offsetof(voodoo_params_t, dBdX));
    addbyte(0x8b); /*MOV EAX, params->dZdX[ESI]*/
    addbyte(0x86);
    addlong(offsetof(voodoo_params_t, dZdX));
    addbyte(0xf3); /*MOVDQU XMM5, params->tmu[0].dSdX[ESI]*/
    addbyte(0x0f);
    addbyte(0x6f);
    addbyte(0xae);
    addlong(offsetof(voodoo_params_t, tmu[0].dSdX));
    addbyte(0xf3); /*MOVQ XMM6, params->tmu[0].dWdX[ESI]*/
    addbyte(0x0f);
    addbyte(0x7e);
    addbyte(0xb6);
    addlong(offsetof(voodoo_params_t, tmu[0].dWdX));

    if (state->xdir > 0) {
        addbyte(0x66); /*PADDD XMM1, XMM0*/
        addbyte(0x0f);
        addbyte(0xfe);
        addbyte(0xc8);
    } else {
        addbyte(0x66); /*PSUBD XMM1, XMM0*/
        addbyte(0x0f);
        addbyte(0xfa);
        addbyte(0xc8);
    }

    addbyte(0xf3); /*MOVQ XMM0, state->w*/
    addbyte(0x0f);
    addbyte(0x7e);
    addbyte(0x87);
    addlong(offsetof(voodoo_state_t, w));
    addbyte(0xf3); /*MOVDQU state->ib, XMM1*/
    addbyte(0x0f);
    addbyte(0x7f);
    addbyte(0x8f);
    addlong(offsetof(voodoo_state_t, ib));
    addbyte(0xf3); /*MOVQ XMM7, params->dWdX*/
    addbyte(0x0f);
    addbyte(0x7e);
    addbyte(0xbe);
    addlong(offsetof(voodoo_params_t, dWdX));

    if (state->xdir > 0) {
        addbyte(0x66); /*PADDQ XMM3, XMM5*/
        addbyte(0x0f);
        addbyte(0xd4);
        addbyte(0xdd);
        addbyte(0x66); /*PADDQ XMM4, XMM6*/
        addbyte(0x0f);
        addbyte(0xd4);
        addbyte(0xe6);
        addbyte(0x66); /*PADDQ XMM0, XMM7*/
        addbyte(0x0f);
        addbyte(0xd4);
        addbyte(0xc7);
        addbyte(0x01); /*ADD state->z[EDI], EAX*/
        addbyte(0x87);
        addlong(offsetof(voodoo_state_t, z));
    } else {
        addbyte(0x66); /*PSUBQ XMM3, XMM5*/
        addbyte(0x0f);
        addbyte(0xfb);
        addbyte(0xdd);
        addbyte(0x66); /*PSUBQ XMM4, XMM6*/
        addbyte(0x0f);
        addbyte(0xfb);
        addbyte(0xe6);
        addbyte(0x66); /*PSUBQ XMM0, XMM7*/
        addbyte(0x0f);
        addbyte(0xfb);
        addbyte(0xc7);
        addbyte(0x29); /*SUB state->z[EDI], EAX*/
        addbyte(0x87);
        addlong(offsetof(voodoo_state_t, z));
    }

    if (voodoo->dual_tmus) {
        addbyte(0xf3); /*MOVDQU XMM5, params->tmu[1].dSdX[ESI]*/
        addbyte(0x0f);
        addbyte(0x6f);
        addbyte(0xae);
        addlong(offsetof(voodoo_params_t, tmu[1].dSdX));
        addbyte(0xf3); /*MOVQ XMM6, params->tmu[1].dWdX[ESI]*/
        addbyte(0x0f);
        addbyte(0x7e);
        addbyte(0xb6);
        addlong(offsetof(voodoo_params_t, tmu[1].dWdX));
    }

    addbyte(0xf3); /*MOVDQU state->tmu0_s, XMM3*/
    addbyte(0x0f);
    addbyte(0x7f);
    addbyte(0x9f);
    addlong(offsetof(voodoo_state_t, tmu0_s));
    addbyte(0x66); /*MOVQ state->tmu0_w, XMM4*/
    addbyte(0x0f);
    addbyte(0xd6);
    addbyte(0xa7);
    addlong(offsetof(voodoo_state_t, tmu0_w));
    addbyte(0x66); /*MOVQ state->w, XMM0*/
    addbyte(0x0f);
    addbyte(0xd6);
    addbyte(0x87);
    addlong(offsetof(voodoo_state_t, w));

    if (voodoo->dual_tmus) {
        addbyte(0xf3); /*MOVDQU XMM3, state->tmu1_s[EDI]*/
        addbyte(0x0f);
        addbyte(0x6f);
        addbyte(0x9f);
        addlong(offsetof(voodoo_state_t, tmu1_s));
        addbyte(0xf3); /*MOVQ XMM4, state->tmu1_w[EDI]*/
        addbyte(0x0f);
        addbyte(0x7e);
        addbyte(0xa7);
        addlong(offsetof(voodoo_state_t, tmu1_w));

        if (state->xdir > 0) {
            addbyte(0x66); /*PADDQ XMM3, XMM5*/
            addbyte(0x0f);
            addbyte(0xd4);
            addbyte(0xdd);
            addbyte(0x66); /*PADDQ XMM4, XMM6*/
            addbyte(0x0f);
            addbyte(0xd4);
            addbyte(0xe6);
        } else {
            addbyte(0x66); /*PSUBQ XMM3, XMM5*/
            addbyte(0x0f);
            addbyte(0xfb);
            addbyte(0xdd);
            addbyte(0x66); /*PSUBQ XMM4, XMM6*/
            addbyte(0x0f);
            addbyte(0xfb);
            addbyte(0xe6);
        }

        addbyte(0xf3); /*MOVDQU state->tmu1_s, XMM3*/
        addbyte(0x0f);
        addbyte(0x7f);
        addbyte(0x9f);
        addlong(offsetof(voodoo_state_t, tmu1_s));
        addbyte(0x66); /*MOVQ state->tmu1_w, XMM4*/
        addbyte(0x0f);
        addbyte(0xd6);
        addbyte(0xa7);
        addlong(offsetof(voodoo_state_t, tmu1_w));
    }

    addbyte(0x83); /*ADD state->pixel_count[EDI], 1*/
    addbyte(0x87);
    addlong(offsetof(voodoo_state_t, pixel_count));
    addbyte(1);

    if (params->fbzColorPath & FBZCP_TEXTURE_ENABLED) {
        if ((params->textureMode[0] & TEXTUREMODE_MASK) == TEXTUREMODE_PASSTHROUGH || (params->textureMode[0] & TEXTUREMODE_LOCAL_MASK) == TEXTUREMODE_LOCAL) {
            addbyte(0x83); /*ADD state->texel_count[EDI], 1*/
            addbyte(0x87);
            addlong(offsetof(voodoo_state_t, texel_count));
            addbyte(1);
        } else {
            addbyte(0x83); /*ADD state->texel_count[EDI], 2*/
            addbyte(0x87);
            addlong(offsetof(voodoo_state_t, texel_count));
            addbyte(2);
        }
    }

    addbyte(0x8b); /*MOV EAX, state->x[EDI]*/
    addbyte(0x87);
    addlong(offsetof(voodoo_state_t, x));

    if (state->xdir > 0) {
        addbyte(0x83); /*ADD state->x[EDI], 1*/
        addbyte(0x87);
        addlong(offsetof(voodoo_state_t, x));
        addbyte(1);
    } else {
        addbyte(0x83); /*SUB state->x[EDI], 1*/
        addbyte(0xaf);
        addlong(offsetof(voodoo_state_t, x));
        addbyte(1);
    }

    addbyte(0x3b); /*CMP EAX, state->x2[EDI]*/
    addbyte(0x87);
    addlong(offsetof(voodoo_state_t, x2));
    addbyte(0x0f); /*JNZ loop_jump_pos*/
    addbyte(0x85);
    addlong(loop_jump_pos - (block_pos + 4));

    addbyte(0x41); /*POP R15*/
    addbyte(0x5f);
    addbyte(0x41); /*POP R14*/
    addbyte(0x5e);
    addbyte(0x41); /*POP R13*/
    addbyte(0x5d);
    addbyte(0x41); /*POP R12*/
    addbyte(0x5c);
    addbyte(0x5b); /*POP RBX*/
    addbyte(0x5e); /*POP RSI*/
    addbyte(0x5f); /*POP RDI*/
    addbyte(0x5d); /*POP RBP*/

    addbyte(0xC3); /*RET*/
}
int voodoo_recomp = 0;
static inline void *
voodoo_get_block(voodoo_t *voodoo, voodoo_params_t *params, voodoo_state_t *state, int odd_even)
{
    int                b               = last_block[odd_even];
    voodoo_x86_data_t *voodoo_x86_data = voodoo->codegen_data;
    voodoo_x86_data_t *data;

    for (uint8_t c = 0; c < 8; c++) {
        data = &voodoo_x86_data[odd_even + c * 4]; //&voodoo_x86_data[odd_even][b];

        if (state->xdir == data->xdir && params->alphaMode == data->alphaMode && params->fbzMode == data->fbzMode && params->fogMode == data->fogMode && params->fbzColorPath == data->fbzColorPath && (voodoo->trexInit1[0] & (1 << 18)) == data->trexInit1 && params->textureMode[0] == data->textureMode[0] && params->textureMode[1] == data->textureMode[1] && (params->tLOD[0] & LOD_MASK) == data->tLOD[0] && (params->tLOD[1] & LOD_MASK) == data->tLOD[1] && ((params->col_tiled || params->aux_tiled) ? 1 : 0) == data->is_tiled) {
            last_block[odd_even] = b;
            return data->code_block;
        }

        b = (b + 1) & 7;
    }
    voodoo_recomp++;
    data = &voodoo_x86_data[odd_even + next_block_to_write[odd_even] * 4];
#if 0
    code_block = data->code_block;
#endif

    voodoo_generate(data->code_block, voodoo, params, state, depth_op);

    data->xdir           = state->xdir;
    data->alphaMode      = params->alphaMode;
    data->fbzMode        = params->fbzMode;
    data->fogMode        = params->fogMode;
    data->fbzColorPath   = params->fbzColorPath;
    data->trexInit1      = voodoo->trexInit1[0] & (1 << 18);
    data->textureMode[0] = params->textureMode[0];
    data->textureMode[1] = params->textureMode[1];
    data->tLOD[0]        = params->tLOD[0] & LOD_MASK;
    data->tLOD[1]        = params->tLOD[1] & LOD_MASK;
    data->is_tiled       = (params->col_tiled || params->aux_tiled) ? 1 : 0;

    next_block_to_write[odd_even] = (next_block_to_write[odd_even] + 1) & 7;

    return data->code_block;
}

void
voodoo_codegen_init(voodoo_t *voodoo)
{
    voodoo->codegen_data = plat_mmap(sizeof(voodoo_x86_data_t) * BLOCK_NUM * 4, 1);

    for (uint16_t c = 0; c < 256; c++) {
        int d[4];
        int _ds = c & 0xf;
        int dt  = c >> 4;

        alookup[c]      = _mm_set_epi32(0, 0, c | (c << 16), c | (c << 16));
        aminuslookup[c] = _mm_set_epi32(0, 0, (255 - c) | ((255 - c) << 16), (255 - c) | ((255 - c) << 16));

        d[0] = (16 - _ds) * (16 - dt);
        d[1] = _ds * (16 - dt);
        d[2] = (16 - _ds) * dt;
        d[3] = _ds * dt;

        bilinear_lookup[c * 2]     = _mm_set_epi32(d[1] | (d[1] << 16), d[1] | (d[1] << 16), d[0] | (d[0] << 16), d[0] | (d[0] << 16));
        bilinear_lookup[c * 2 + 1] = _mm_set_epi32(d[3] | (d[3] << 16), d[3] | (d[3] << 16), d[2] | (d[2] << 16), d[2] | (d[2] << 16));
    }
    alookup[256]   = _mm_set_epi32(0, 0, 256 | (256 << 16), 256 | (256 << 16));
    xmm_00_ff_w[0] = _mm_set_epi32(0, 0, 0, 0);
    xmm_00_ff_w[1] = _mm_set_epi32(0, 0, 0xff | (0xff << 16), 0xff | (0xff << 16));
}

void
voodoo_codegen_close(voodoo_t *voodoo)
{
    plat_munmap(voodoo->codegen_data, sizeof(voodoo_x86_data_t) * BLOCK_NUM * 4);
}

#endif /*VIDEO_VOODOO_CODEGEN_X86_64_H*/
