Logo Search packages:      
Sourcecode: tcc version File versions

c67-gen.c

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

//#define ASSEMBLY_LISTING_C67

/* number of available registers */
#define NB_REGS            24

/* a register can belong to several classes. The classes must be
   sorted from more general to more precise (see gv2() code which does
   assumptions on it). */
#define RC_INT     0x0001     /* generic integer register */
#define RC_FLOAT   0x0002     /* generic float register */
#define RC_EAX     0x0004
#define RC_ST0     0x0008
#define RC_ECX     0x0010
#define RC_EDX     0x0020
#define RC_INT_BSIDE  0x00000040    /* generic integer register  on b side */
#define RC_C67_A4     0x00000100
#define RC_C67_A5     0x00000200
#define RC_C67_B4     0x00000400
#define RC_C67_B5     0x00000800
#define RC_C67_A6     0x00001000
#define RC_C67_A7     0x00002000
#define RC_C67_B6     0x00004000
#define RC_C67_B7     0x00008000
#define RC_C67_A8     0x00010000
#define RC_C67_A9     0x00020000
#define RC_C67_B8     0x00040000
#define RC_C67_B9     0x00080000
#define RC_C67_A10    0x00100000
#define RC_C67_A11    0x00200000
#define RC_C67_B10    0x00400000
#define RC_C67_B11    0x00800000
#define RC_C67_A12    0x01000000
#define RC_C67_A13    0x02000000
#define RC_C67_B12    0x04000000
#define RC_C67_B13    0x08000000
#define RC_IRET    RC_C67_A4  /* function return: integer register */
#define RC_LRET    RC_C67_A5  /* function return: second integer register */
#define RC_FRET    RC_C67_A4  /* function return: float register */

/* pretty names for the registers */
enum {
    TREG_EAX = 0,       // really A2
    TREG_ECX,                 // really A3
    TREG_EDX,                 // really B0
    TREG_ST0,                 // really B1
    TREG_C67_A4,
    TREG_C67_A5,
    TREG_C67_B4,
    TREG_C67_B5,
    TREG_C67_A6,
    TREG_C67_A7,
    TREG_C67_B6,
    TREG_C67_B7,
    TREG_C67_A8,
    TREG_C67_A9,
    TREG_C67_B8,
    TREG_C67_B9,
    TREG_C67_A10,
    TREG_C67_A11,
    TREG_C67_B10,
    TREG_C67_B11,
    TREG_C67_A12,
    TREG_C67_A13,
    TREG_C67_B12,
    TREG_C67_B13,
};

int reg_classes[NB_REGS] = {
                                    /* eax */ RC_INT | RC_FLOAT | RC_EAX,
                                    // only allow even regs for floats (allow for doubles)
    /* ecx */ RC_INT | RC_ECX,
                                                /* edx */ RC_INT | RC_INT_BSIDE | RC_FLOAT | RC_EDX,
                                                // only allow even regs for floats (allow for doubles)
    /* st0 */ RC_INT | RC_INT_BSIDE | RC_ST0,
    /* A4  */ RC_C67_A4,
    /* A5  */ RC_C67_A5,
    /* B4  */ RC_C67_B4,
    /* B5  */ RC_C67_B5,
    /* A6  */ RC_C67_A6,
    /* A7  */ RC_C67_A7,
    /* B6  */ RC_C67_B6,
    /* B7  */ RC_C67_B7,
    /* A8  */ RC_C67_A8,
    /* A9  */ RC_C67_A9,
    /* B8  */ RC_C67_B8,
    /* B9  */ RC_C67_B9,
    /* A10  */ RC_C67_A10,
    /* A11  */ RC_C67_A11,
    /* B10  */ RC_C67_B10,
    /* B11  */ RC_C67_B11,
    /* A12  */ RC_C67_A10,
    /* A13  */ RC_C67_A11,
    /* B12  */ RC_C67_B10,
    /* B13  */ RC_C67_B11
};

/* return registers for function */
#define REG_IRET TREG_C67_A4  /* single word int return register */
#define REG_LRET TREG_C67_A5  /* second word return register (for long long) */
#define REG_FRET TREG_C67_A4  /* float return register */


#define ALWAYS_ASSERT(x) \
do {\
   if (!(x))\
       error("internal compiler error file at %s:%d", __FILE__, __LINE__);\
} while (0)

// although tcc thinks it is passing parameters on the stack,
// the C67 really passes up to the first 10 params in special
// regs or regs pairs (for 64 bit params).  So keep track of
// the stack offsets so we can translate to the appropriate 
// reg (pair)


#define NoCallArgsPassedOnStack 10
int NoOfCurFuncArgs;
int TranslateStackToReg[NoCallArgsPassedOnStack];
int ParamLocOnStack[NoCallArgsPassedOnStack];
int TotalBytesPushedOnStack;

/* defined if function parameters must be evaluated in reverse order */

//#define INVERT_FUNC_PARAMS

/* defined if structures are passed as pointers. Otherwise structures
   are directly pushed on stack. */
//#define FUNC_STRUCT_PARAM_AS_PTR

/* pointer size, in bytes */
#define PTR_SIZE 4

/* long double size and alignment, in bytes */
#define LDOUBLE_SIZE  12
#define LDOUBLE_ALIGN 4
/* maximum alignment (for aligned attribute support) */
#define MAX_ALIGN     8

/******************************************************/
/* ELF defines */

#define EM_TCC_TARGET EM_C60

/* relocation type for 32 bit data relocation */
#define R_DATA_32   R_C60_32
#define R_JMP_SLOT  R_C60_JMP_SLOT
#define R_COPY      R_C60_COPY

#define ELF_START_ADDR 0x00000400
#define ELF_PAGE_SIZE  0x1000

/******************************************************/

static unsigned long func_sub_sp_offset;
static int func_ret_sub;


static BOOL C67_invert_test;
static int C67_compare_reg;

#ifdef ASSEMBLY_LISTING_C67
FILE *f = NULL;
#endif


void C67_g(int c)
{
    int ind1;

#ifdef ASSEMBLY_LISTING_C67
    fprintf(f, " %08X", c);
#endif
    ind1 = ind + 4;
    if (ind1 > (int) cur_text_section->data_allocated)
      section_realloc(cur_text_section, ind1);
    cur_text_section->data[ind] = c & 0xff;
    cur_text_section->data[ind + 1] = (c >> 8) & 0xff;
    cur_text_section->data[ind + 2] = (c >> 16) & 0xff;
    cur_text_section->data[ind + 3] = (c >> 24) & 0xff;
    ind = ind1;
}


/* output a symbol and patch all calls to it */
void gsym_addr(int t, int a)
{
    int n, *ptr;
    while (t) {
      ptr = (int *) (cur_text_section->data + t);
      {
          Sym *sym;

          // extract 32 bit address from MVKH/MVKL
          n = ((*ptr >> 7) & 0xffff);
          n |= ((*(ptr + 1) >> 7) & 0xffff) << 16;

          // define a label that will be relocated

          sym = get_sym_ref(&char_pointer_type, cur_text_section, a, 0);
          greloc(cur_text_section, sym, t, R_C60LO16);
          greloc(cur_text_section, sym, t + 4, R_C60HI16);

          // clear out where the pointer was

          *ptr &= ~(0xffff << 7);
          *(ptr + 1) &= ~(0xffff << 7);
      }
      t = n;
    }
}

void gsym(int t)
{
    gsym_addr(t, ind);
}

// these are regs that tcc doesn't really know about, 
// but asign them unique values so the mapping routines
// can distinquish them

#define C67_A0 105
#define C67_SP 106
#define C67_B3 107
#define C67_FP 108
#define C67_B2 109
#define C67_CREG_ZERO -1      // Special code for no condition reg test


int ConvertRegToRegClass(int r)
{
    // only works for A4-B13

    return RC_C67_A4 << (r - TREG_C67_A4);
}


// map TCC reg to C67 reg number

int C67_map_regn(int r)
{
    if (r == 0)               // normal tcc regs
      return 0x2;       // A2
    else if (r == 1)          // normal tcc regs
      return 3;         // A3
    else if (r == 2)          // normal tcc regs
      return 0;         // B0
    else if (r == 3)          // normal tcc regs
      return 1;         // B1
    else if (r >= TREG_C67_A4 && r <= TREG_C67_B13)   // these form a pattern of alt pairs
      return (((r & 0xfffffffc) >> 1) | (r & 1)) + 2;
    else if (r == C67_A0)
      return 0;         // set to A0 (offset reg)
    else if (r == C67_B2)
      return 2;         // set to B2 (offset reg)
    else if (r == C67_B3)
      return 3;         // set to B3 (return address reg)
    else if (r == C67_SP)
      return 15;        // set to SP (B15) (offset reg)
    else if (r == C67_FP)
      return 15;        // set to FP (A15) (offset reg)
    else if (r == C67_CREG_ZERO)
      return 0;         // Special code for no condition reg test
    else
      ALWAYS_ASSERT(FALSE);

    return 0;
}

// mapping from tcc reg number to 
// C67 register to condition code field
//
// valid condition code regs are:
//
// tcc reg 2 ->B0 -> 1
// tcc reg 3 ->B1 -> 2
// tcc reg 0 -> A2 -> 5
// tcc reg 1 -> A3 -> X
// tcc reg      B2 -> 3

int C67_map_regc(int r)
{
    if (r == 0)               // normal tcc regs
      return 0x5;
    else if (r == 2)          // normal tcc regs
      return 0x1;
    else if (r == 3)          // normal tcc regs
      return 0x2;
    else if (r == C67_B2)     // normal tcc regs
      return 0x3;
    else if (r == C67_CREG_ZERO)
      return 0;         // Special code for no condition reg test
    else
      ALWAYS_ASSERT(FALSE);

    return 0;
}


// map TCC reg to C67 reg side A or B

int C67_map_regs(int r)
{
    if (r == 0)               // normal tcc regs
      return 0x0;
    else if (r == 1)          // normal tcc regs
      return 0x0;
    else if (r == 2)          // normal tcc regs
      return 0x1;
    else if (r == 3)          // normal tcc regs
      return 0x1;
    else if (r >= TREG_C67_A4 && r <= TREG_C67_B13)   // these form a pattern of alt pairs
      return (r & 2) >> 1;
    else if (r == C67_A0)
      return 0;         // set to A side 
    else if (r == C67_B2)
      return 1;         // set to B side 
    else if (r == C67_B3)
      return 1;         // set to B side
    else if (r == C67_SP)
      return 0x1;       // set to SP (B15) B side 
    else if (r == C67_FP)
      return 0x0;       // set to FP (A15) A side 
    else
      ALWAYS_ASSERT(FALSE);

    return 0;
}

int C67_map_S12(char *s)
{
    if (strstr(s, ".S1") != NULL)
      return 0;
    else if (strcmp(s, ".S2"))
      return 1;
    else
      ALWAYS_ASSERT(FALSE);

    return 0;
}

int C67_map_D12(char *s)
{
    if (strstr(s, ".D1") != NULL)
      return 0;
    else if (strcmp(s, ".D2"))
      return 1;
    else
      ALWAYS_ASSERT(FALSE);

    return 0;
}



void C67_asm(char *s, int a, int b, int c)
{
    BOOL xpath;

#ifdef ASSEMBLY_LISTING_C67
    if (!f) {
      f = fopen("TCC67_out.txt", "wt");
    }
    fprintf(f, "%04X ", ind);
#endif

    if (strstr(s, "MVKL") == s) {
      C67_g((C67_map_regn(b) << 23) |
            ((a & 0xffff) << 7) | (0x0a << 2) | (C67_map_regs(b) << 1));
    } else if (strstr(s, "MVKH") == s) {
      C67_g((C67_map_regn(b) << 23) |
            (((a >> 16) & 0xffff) << 7) |
            (0x1a << 2) | (C67_map_regs(b) << 1));
    } else if (strstr(s, "STW.D SP POST DEC") == s) {
      C67_g((C67_map_regn(a) << 23) |     //src
            (15 << 18) |      //SP B15
            (2 << 13) | //ucst5 (must keep 8 byte boundary !!)
            (0xa << 9) |      //mode a = post dec ucst
            (0 << 8) |  //r (LDDW bit 0)
            (1 << 7) |  //y D1/D2 use B side
            (7 << 4) |  //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU
            (1 << 2) |  //opcode
            (C67_map_regs(a) << 1) |      //side of src
            (0 << 0));  //parallel
    } else if (strstr(s, "STB.D *+SP[A0]") == s) {
      C67_g((C67_map_regn(a) << 23) |     //src
            (15 << 18) |      //base reg A15
            (0 << 13) | //offset reg A0
            (5 << 9) |  //mode 5 = pos offset, base reg + off reg
            (0 << 8) |  //r (LDDW bit 0)
            (0 << 7) |  //y D1/D2 A side
            (3 << 4) |  //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU 
            (1 << 2) |  //opcode
            (C67_map_regs(a) << 1) |      //side of src
            (0 << 0));  //parallel
    } else if (strstr(s, "STH.D *+SP[A0]") == s) {
      C67_g((C67_map_regn(a) << 23) |     //src
            (15 << 18) |      //base reg A15
            (0 << 13) | //offset reg A0
            (5 << 9) |  //mode 5 = pos offset, base reg + off reg
            (0 << 8) |  //r (LDDW bit 0)
            (0 << 7) |  //y D1/D2 A side
            (5 << 4) |  //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU
            (1 << 2) |  //opcode
            (C67_map_regs(a) << 1) |      //side of src
            (0 << 0));  //parallel
    } else if (strstr(s, "STB.D *+SP[A0]") == s) {
      C67_g((C67_map_regn(a) << 23) |     //src
            (15 << 18) |      //base reg A15
            (0 << 13) | //offset reg A0
            (5 << 9) |  //mode 5 = pos offset, base reg + off reg
            (0 << 8) |  //r (LDDW bit 0)
            (0 << 7) |  //y D1/D2 A side
            (3 << 4) |  //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU 
            (1 << 2) |  //opcode
            (C67_map_regs(a) << 1) |      //side of src
            (0 << 0));  //parallel
    } else if (strstr(s, "STH.D *+SP[A0]") == s) {
      C67_g((C67_map_regn(a) << 23) |     //src
            (15 << 18) |      //base reg A15
            (0 << 13) | //offset reg A0
            (5 << 9) |  //mode 5 = pos offset, base reg + off reg
            (0 << 8) |  //r (LDDW bit 0)
            (0 << 7) |  //y D1/D2 A side
            (5 << 4) |  //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU
            (1 << 2) |  //opcode
            (C67_map_regs(a) << 1) |      //side of src
            (0 << 0));  //parallel
    } else if (strstr(s, "STW.D *+SP[A0]") == s) {
      C67_g((C67_map_regn(a) << 23) |     //src
            (15 << 18) |      //base reg A15
            (0 << 13) | //offset reg A0
            (5 << 9) |  //mode 5 = pos offset, base reg + off reg
            (0 << 8) |  //r (LDDW bit 0)
            (0 << 7) |  //y D1/D2 A side
            (7 << 4) |  //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU 
            (1 << 2) |  //opcode
            (C67_map_regs(a) << 1) |      //side of src
            (0 << 0));  //parallel
    } else if (strstr(s, "STW.D *") == s) {
      C67_g((C67_map_regn(a) << 23) |     //src
            (C67_map_regn(b) << 18) |     //base reg A0
            (0 << 13) | //cst5
            (1 << 9) |  //mode 1 = pos cst offset
            (0 << 8) |  //r (LDDW bit 0)
            (C67_map_regs(b) << 7) |      //y D1/D2 base reg side
            (7 << 4) |  //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU 
            (1 << 2) |  //opcode
            (C67_map_regs(a) << 1) |      //side of src
            (0 << 0));  //parallel
    } else if (strstr(s, "STH.D *") == s) {
      C67_g((C67_map_regn(a) << 23) |     //src
            (C67_map_regn(b) << 18) |     //base reg A0
            (0 << 13) | //cst5
            (1 << 9) |  //mode 1 = pos cst offset
            (0 << 8) |  //r (LDDW bit 0)
            (C67_map_regs(b) << 7) |      //y D1/D2 base reg side
            (5 << 4) |  //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU 
            (1 << 2) |  //opcode
            (C67_map_regs(a) << 1) |      //side of src
            (0 << 0));  //parallel
    } else if (strstr(s, "STB.D *") == s) {
      C67_g((C67_map_regn(a) << 23) |     //src
            (C67_map_regn(b) << 18) |     //base reg A0
            (0 << 13) | //cst5
            (1 << 9) |  //mode 1 = pos cst offset
            (0 << 8) |  //r (LDDW bit 0)
            (C67_map_regs(b) << 7) |      //y D1/D2 base reg side
            (3 << 4) |  //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU 
            (1 << 2) |  //opcode
            (C67_map_regs(a) << 1) |      //side of src
            (0 << 0));  //parallel
    } else if (strstr(s, "STW.D +*") == s) {
      ALWAYS_ASSERT(c < 32);
      C67_g((C67_map_regn(a) << 23) |     //src
            (C67_map_regn(b) << 18) |     //base reg A0
            (c << 13) | //cst5
            (1 << 9) |  //mode 1 = pos cst offset
            (0 << 8) |  //r (LDDW bit 0)
            (C67_map_regs(b) << 7) |      //y D1/D2 base reg side
            (7 << 4) |  //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU 
            (1 << 2) |  //opcode
            (C67_map_regs(a) << 1) |      //side of src
            (0 << 0));  //parallel
    } else if (strstr(s, "LDW.D SP PRE INC") == s) {
      C67_g((C67_map_regn(a) << 23) |     //dst
            (15 << 18) |      //base reg B15
            (2 << 13) | //ucst5 (must keep 8 byte boundary)
            (9 << 9) |  //mode 9 = pre inc ucst5
            (0 << 8) |  //r (LDDW bit 0)
            (1 << 7) |  //y D1/D2  B side
            (6 << 4) |  //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU
            (1 << 2) |  //opcode
            (C67_map_regs(a) << 1) |      //side of dst
            (0 << 0));  //parallel
    } else if (strstr(s, "LDDW.D SP PRE INC") == s) {
      C67_g((C67_map_regn(a) << 23) |     //dst
            (15 << 18) |      //base reg B15
            (1 << 13) | //ucst5 (must keep 8 byte boundary)
            (9 << 9) |  //mode 9 = pre inc ucst5
            (1 << 8) |  //r (LDDW bit 1)
            (1 << 7) |  //y D1/D2  B side
            (6 << 4) |  //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU
            (1 << 2) |  //opcode
            (C67_map_regs(a) << 1) |      //side of dst
            (0 << 0));  //parallel
    } else if (strstr(s, "LDW.D *+SP[A0]") == s) {
      C67_g((C67_map_regn(a) << 23) |     //dst
            (15 << 18) |      //base reg A15
            (0 << 13) | //offset reg A0
            (5 << 9) |  //mode 5 = pos offset, base reg + off reg
            (0 << 8) |  //r (LDDW bit 0)
            (0 << 7) |  //y D1/D2  A side
            (6 << 4) |  //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU
            (1 << 2) |  //opcode
            (C67_map_regs(a) << 1) |      //side of dst
            (0 << 0));  //parallel
    } else if (strstr(s, "LDDW.D *+SP[A0]") == s) {
      C67_g((C67_map_regn(a) << 23) |     //dst
            (15 << 18) |      //base reg A15
            (0 << 13) | //offset reg A0
            (5 << 9) |  //mode 5 = pos offset, base reg + off reg
            (1 << 8) |  //r (LDDW bit 1)
            (0 << 7) |  //y D1/D2  A side
            (6 << 4) |  //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU
            (1 << 2) |  //opcode
            (C67_map_regs(a) << 1) |      //side of dst
            (0 << 0));  //parallel
    } else if (strstr(s, "LDH.D *+SP[A0]") == s) {
      C67_g((C67_map_regn(a) << 23) |     //dst
            (15 << 18) |      //base reg A15
            (0 << 13) | //offset reg A0
            (5 << 9) |  //mode 5 = pos offset, base reg + off reg
            (0 << 8) |  //r (LDDW bit 0)
            (0 << 7) |  //y D1/D2  A side
            (4 << 4) |  //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU
            (1 << 2) |  //opcode
            (C67_map_regs(a) << 1) |      //side of dst
            (0 << 0));  //parallel
    } else if (strstr(s, "LDB.D *+SP[A0]") == s) {
      C67_g((C67_map_regn(a) << 23) |     //dst
            (15 << 18) |      //base reg A15
            (0 << 13) | //offset reg A0
            (5 << 9) |  //mode 5 = pos offset, base reg + off reg
            (0 << 8) |  //r (LDDW bit 0)
            (0 << 7) |  //y D1/D2  A side
            (2 << 4) |  //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU
            (1 << 2) |  //opcode
            (C67_map_regs(a) << 1) |      //side of dst
            (0 << 0));  //parallel
    } else if (strstr(s, "LDHU.D *+SP[A0]") == s) {
      C67_g((C67_map_regn(a) << 23) |     //dst
            (15 << 18) |      //base reg A15
            (0 << 13) | //offset reg A0
            (5 << 9) |  //mode 5 = pos offset, base reg + off reg
            (0 << 8) |  //r (LDDW bit 0)
            (0 << 7) |  //y D1/D2  A side
            (0 << 4) |  //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU
            (1 << 2) |  //opcode
            (C67_map_regs(a) << 1) |      //side of dst
            (0 << 0));  //parallel
    } else if (strstr(s, "LDBU.D *+SP[A0]") == s) {
      C67_g((C67_map_regn(a) << 23) |     //dst
            (15 << 18) |      //base reg A15
            (0 << 13) | //offset reg A0
            (5 << 9) |  //mode 5 = pos offset, base reg + off reg
            (0 << 8) |  //r (LDDW bit 0)
            (0 << 7) |  //y D1/D2  A side
            (1 << 4) |  //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU
            (1 << 2) |  //opcode
            (C67_map_regs(a) << 1) |      //side of dst
            (0 << 0));  //parallel
    } else if (strstr(s, "LDW.D *") == s) {
      C67_g((C67_map_regn(b) << 23) |     //dst
            (C67_map_regn(a) << 18) |     //base reg A15
            (0 << 13) | //cst5
            (1 << 9) |  //mode 1 = pos cst offset
            (0 << 8) |  //r (LDDW bit 0)
            (C67_map_regs(a) << 7) |      //y D1/D2  src side
            (6 << 4) |  //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU
            (1 << 2) |  //opcode
            (C67_map_regs(b) << 1) |      //side of dst
            (0 << 0));  //parallel
    } else if (strstr(s, "LDDW.D *") == s) {
      C67_g((C67_map_regn(b) << 23) |     //dst
            (C67_map_regn(a) << 18) |     //base reg A15
            (0 << 13) | //cst5
            (1 << 9) |  //mode 1 = pos cst offset
            (1 << 8) |  //r (LDDW bit 1)
            (C67_map_regs(a) << 7) |      //y D1/D2  src side
            (6 << 4) |  //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU
            (1 << 2) |  //opcode
            (C67_map_regs(b) << 1) |      //side of dst
            (0 << 0));  //parallel
    } else if (strstr(s, "LDH.D *") == s) {
      C67_g((C67_map_regn(b) << 23) |     //dst
            (C67_map_regn(a) << 18) |     //base reg A15
            (0 << 13) | //cst5
            (1 << 9) |  //mode 1 = pos cst offset
            (0 << 8) |  //r (LDDW bit 0)
            (C67_map_regs(a) << 7) |      //y D1/D2  src side
            (4 << 4) |  //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU
            (1 << 2) |  //opcode
            (C67_map_regs(b) << 1) |      //side of dst
            (0 << 0));  //parallel
    } else if (strstr(s, "LDB.D *") == s) {
      C67_g((C67_map_regn(b) << 23) |     //dst
            (C67_map_regn(a) << 18) |     //base reg A15
            (0 << 13) | //cst5
            (1 << 9) |  //mode 1 = pos cst offset
            (0 << 8) |  //r (LDDW bit 0)
            (C67_map_regs(a) << 7) |      //y D1/D2  src side
            (2 << 4) |  //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU 
            (1 << 2) |  //opcode
            (C67_map_regs(b) << 1) |      //side of dst
            (0 << 0));  //parallel
    } else if (strstr(s, "LDHU.D *") == s) {
      C67_g((C67_map_regn(b) << 23) |     //dst
            (C67_map_regn(a) << 18) |     //base reg A15
            (0 << 13) | //cst5
            (1 << 9) |  //mode 1 = pos cst offset
            (0 << 8) |  //r (LDDW bit 0)
            (C67_map_regs(a) << 7) |      //y D1/D2  src side
            (0 << 4) |  //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU 
            (1 << 2) |  //opcode
            (C67_map_regs(b) << 1) |      //side of dst
            (0 << 0));  //parallel
    } else if (strstr(s, "LDBU.D *") == s) {
      C67_g((C67_map_regn(b) << 23) |     //dst
            (C67_map_regn(a) << 18) |     //base reg A15
            (0 << 13) | //cst5
            (1 << 9) |  //mode 1 = pos cst offset
            (0 << 8) |  //r (LDDW bit 0)
            (C67_map_regs(a) << 7) |      //y D1/D2  src side
            (1 << 4) |  //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU
            (1 << 2) |  //opcode
            (C67_map_regs(b) << 1) |      //side of dst
            (0 << 0));  //parallel
    } else if (strstr(s, "LDW.D +*") == s) {
      C67_g((C67_map_regn(b) << 23) |     //dst
            (C67_map_regn(a) << 18) |     //base reg A15
            (1 << 13) | //cst5
            (1 << 9) |  //mode 1 = pos cst offset
            (0 << 8) |  //r (LDDW bit 0)
            (C67_map_regs(a) << 7) |      //y D1/D2  src side
            (6 << 4) |  //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU 
            (1 << 2) |  //opcode
            (C67_map_regs(b) << 1) |      //side of dst
            (0 << 0));  //parallel
    } else if (strstr(s, "CMPLTSP") == s) {
      xpath = C67_map_regs(a) ^ C67_map_regs(b);
      ALWAYS_ASSERT(C67_map_regs(c) == C67_map_regs(a));

      C67_g((C67_map_regn(c) << 23) |     //dst
            (C67_map_regn(b) << 18) |     //src2
            (C67_map_regn(a) << 13) |     //src1
            (xpath << 12) |   //x use cross path for src2
            (0x3a << 6) |     //opcode
            (0x8 << 2) |      //opcode fixed
            (C67_map_regs(c) << 1) |      //side for reg c
            (0 << 0));  //parallel
    } else if (strstr(s, "CMPGTSP") == s) {
      xpath = C67_map_regs(a) ^ C67_map_regs(b);
      ALWAYS_ASSERT(C67_map_regs(c) == C67_map_regs(a));

      C67_g((C67_map_regn(c) << 23) |     //dst
            (C67_map_regn(b) << 18) |     //src2
            (C67_map_regn(a) << 13) |     //src1
            (xpath << 12) |   //x use cross path for src2
            (0x39 << 6) |     //opcode
            (0x8 << 2) |      //opcode fixed
            (C67_map_regs(c) << 1) |      //side for reg c
            (0 << 0));  //parallel
    } else if (strstr(s, "CMPEQSP") == s) {
      xpath = C67_map_regs(a) ^ C67_map_regs(b);
      ALWAYS_ASSERT(C67_map_regs(c) == C67_map_regs(a));

      C67_g((C67_map_regn(c) << 23) |     //dst
            (C67_map_regn(b) << 18) |     //src2
            (C67_map_regn(a) << 13) |     //src1
            (xpath << 12) |   //x use cross path for src2
            (0x38 << 6) |     //opcode
            (0x8 << 2) |      //opcode fixed
            (C67_map_regs(c) << 1) |      //side for reg c
            (0 << 0));  //parallel
    }

    else if (strstr(s, "CMPLTDP") == s) {
      xpath = C67_map_regs(a) ^ C67_map_regs(b);
      ALWAYS_ASSERT(C67_map_regs(c) == C67_map_regs(a));

      C67_g((C67_map_regn(c) << 23) |     //dst
            (C67_map_regn(b) << 18) |     //src2
            (C67_map_regn(a) << 13) |     //src1
            (xpath << 12) |   //x use cross path for src2
            (0x2a << 6) |     //opcode
            (0x8 << 2) |      //opcode fixed
            (C67_map_regs(c) << 1) |      //side for reg c
            (0 << 0));  //parallel
    } else if (strstr(s, "CMPGTDP") == s) {
      xpath = C67_map_regs(a) ^ C67_map_regs(b);
      ALWAYS_ASSERT(C67_map_regs(c) == C67_map_regs(a));

      C67_g((C67_map_regn(c) << 23) |     //dst
            (C67_map_regn(b) << 18) |     //src2
            (C67_map_regn(a) << 13) |     //src1
            (xpath << 12) |   //x use cross path for src2
            (0x29 << 6) |     //opcode
            (0x8 << 2) |      //opcode fixed
            (C67_map_regs(c) << 1) |      //side for reg c
            (0 << 0));  //parallel
    } else if (strstr(s, "CMPEQDP") == s) {
      xpath = C67_map_regs(a) ^ C67_map_regs(b);
      ALWAYS_ASSERT(C67_map_regs(c) == C67_map_regs(a));

      C67_g((C67_map_regn(c) << 23) |     //dst
            (C67_map_regn(b) << 18) |     //src2
            (C67_map_regn(a) << 13) |     //src1
            (xpath << 12) |   //x use cross path for src2
            (0x28 << 6) |     //opcode
            (0x8 << 2) |      //opcode fixed
            (C67_map_regs(c) << 1) |      //side for reg c
            (0 << 0));  //parallel
    } else if (strstr(s, "CMPLT") == s) {
      xpath = C67_map_regs(a) ^ C67_map_regs(b);
      ALWAYS_ASSERT(C67_map_regs(c) == C67_map_regs(a));

      C67_g((C67_map_regn(c) << 23) |     //dst
            (C67_map_regn(b) << 18) |     //src2
            (C67_map_regn(a) << 13) |     //src1
            (xpath << 12) |   //x use cross path for src2
            (0x57 << 5) |     //opcode
            (0x6 << 2) |      //opcode fixed
            (C67_map_regs(c) << 1) |      //side for reg c
            (0 << 0));  //parallel
    } else if (strstr(s, "CMPGT") == s) {
      xpath = C67_map_regs(a) ^ C67_map_regs(b);
      ALWAYS_ASSERT(C67_map_regs(c) == C67_map_regs(a));

      C67_g((C67_map_regn(c) << 23) |     //dst
            (C67_map_regn(b) << 18) |     //src2
            (C67_map_regn(a) << 13) |     //src1
            (xpath << 12) |   //x use cross path for src2
            (0x47 << 5) |     //opcode
            (0x6 << 2) |      //opcode fixed
            (C67_map_regs(c) << 1) |      //side for reg c
            (0 << 0));  //parallel
    } else if (strstr(s, "CMPEQ") == s) {
      xpath = C67_map_regs(a) ^ C67_map_regs(b);
      ALWAYS_ASSERT(C67_map_regs(c) == C67_map_regs(a));

      C67_g((C67_map_regn(c) << 23) |     //dst
            (C67_map_regn(b) << 18) |     //src2
            (C67_map_regn(a) << 13) |     //src1
            (xpath << 12) |   //x use cross path for src2
            (0x53 << 5) |     //opcode
            (0x6 << 2) |      //opcode fixed
            (C67_map_regs(c) << 1) |      //side for reg c
            (0 << 0));  //parallel
    } else if (strstr(s, "CMPLTU") == s) {
      xpath = C67_map_regs(a) ^ C67_map_regs(b);
      ALWAYS_ASSERT(C67_map_regs(c) == C67_map_regs(a));

      C67_g((C67_map_regn(c) << 23) |     //dst
            (C67_map_regn(b) << 18) |     //src2
            (C67_map_regn(a) << 13) |     //src1
            (xpath << 12) |   //x use cross path for src2
            (0x5f << 5) |     //opcode
            (0x6 << 2) |      //opcode fixed
            (C67_map_regs(c) << 1) |      //side for reg c
            (0 << 0));  //parallel
    } else if (strstr(s, "CMPGTU") == s) {
      xpath = C67_map_regs(a) ^ C67_map_regs(b);
      ALWAYS_ASSERT(C67_map_regs(c) == C67_map_regs(a));

      C67_g((C67_map_regn(c) << 23) |     //dst
            (C67_map_regn(b) << 18) |     //src2
            (C67_map_regn(a) << 13) |     //src1
            (xpath << 12) |   //x use cross path for src2
            (0x4f << 5) |     //opcode
            (0x6 << 2) |      //opcode fixed
            (C67_map_regs(c) << 1) |      //side for reg c
            (0 << 0));  //parallel
    } else if (strstr(s, "B DISP") == s) {
      C67_g((0 << 29) | //creg
            (0 << 28) | //z
            (a << 7) |  //cnst
            (0x4 << 2) |      //opcode fixed
            (0 << 1) |  //S0/S1
            (0 << 0));  //parallel
    } else if (strstr(s, "B.") == s) {
      xpath = C67_map_regs(c) ^ 1;

      C67_g((C67_map_regc(b) << 29) |     //creg
            (a << 28) | //inv
            (0 << 23) | //dst
            (C67_map_regn(c) << 18) |     //src2
            (0 << 13) | //
            (xpath << 12) |   //x cross path if !B side
            (0xd << 6) |      //opcode
            (0x8 << 2) |      //opcode fixed
            (1 << 1) |  //must be S2
            (0 << 0));  //parallel
    } else if (strstr(s, "MV.L") == s) {
      xpath = C67_map_regs(b) ^ C67_map_regs(c);

      C67_g((0 << 29) | //creg
            (0 << 28) | //inv
            (C67_map_regn(c) << 23) |     //dst
            (C67_map_regn(b) << 18) |     //src2
            (0 << 13) | //src1 (cst5)
            (xpath << 12) |   //x cross path if opposite sides
            (0x2 << 5) |      //opcode
            (0x6 << 2) |      //opcode fixed
            (C67_map_regs(c) << 1) |      //side of dest
            (0 << 0));  //parallel
    } else if (strstr(s, "SPTRUNC.L") == s) {
      xpath = C67_map_regs(b) ^ C67_map_regs(c);

      C67_g((0 << 29) | //creg
            (0 << 28) | //inv
            (C67_map_regn(c) << 23) |     //dst
            (C67_map_regn(b) << 18) |     //src2
            (0 << 13) | //src1 NA
            (xpath << 12) |   //x cross path if opposite sides
            (0xb << 5) |      //opcode
            (0x6 << 2) |      //opcode fixed
            (C67_map_regs(c) << 1) |      //side of dest
            (0 << 0));  //parallel
    } else if (strstr(s, "DPTRUNC.L") == s) {
      xpath = C67_map_regs(b) ^ C67_map_regs(c);

      C67_g((0 << 29) | //creg
            (0 << 28) | //inv
            (C67_map_regn(c) << 23) |     //dst
            ((C67_map_regn(b) + 1) << 18) |     //src2   WEIRD CPU must specify odd reg for some reason
            (0 << 13) | //src1 NA
            (xpath << 12) |   //x cross path if opposite sides
            (0x1 << 5) |      //opcode
            (0x6 << 2) |      //opcode fixed
            (C67_map_regs(c) << 1) |      //side of dest
            (0 << 0));  //parallel
    } else if (strstr(s, "INTSP.L") == s) {
      xpath = C67_map_regs(b) ^ C67_map_regs(c);

      C67_g((0 << 29) | //creg
            (0 << 28) | //inv
            (C67_map_regn(c) << 23) |     //dst
            (C67_map_regn(b) << 18) |     //src2   
            (0 << 13) | //src1 NA
            (xpath << 12) |   //x cross path if opposite sides
            (0x4a << 5) |     //opcode
            (0x6 << 2) |      //opcode fixed
            (C67_map_regs(c) << 1) |      //side of dest
            (0 << 0));  //parallel
    } else if (strstr(s, "INTSPU.L") == s) {
      xpath = C67_map_regs(b) ^ C67_map_regs(c);

      C67_g((0 << 29) | //creg
            (0 << 28) | //inv
            (C67_map_regn(c) << 23) |     //dst
            (C67_map_regn(b) << 18) |     //src2  
            (0 << 13) | //src1 NA
            (xpath << 12) |   //x cross path if opposite sides
            (0x49 << 5) |     //opcode
            (0x6 << 2) |      //opcode fixed
            (C67_map_regs(c) << 1) |      //side of dest
            (0 << 0));  //parallel
    } else if (strstr(s, "INTDP.L") == s) {
      xpath = C67_map_regs(b) ^ C67_map_regs(c);

      C67_g((0 << 29) | //creg
            (0 << 28) | //inv
            (C67_map_regn(c) << 23) |     //dst
            (C67_map_regn(b) << 18) |     //src2  
            (0 << 13) | //src1 NA
            (xpath << 12) |   //x cross path if opposite sides
            (0x39 << 5) |     //opcode
            (0x6 << 2) |      //opcode fixed
            (C67_map_regs(c) << 1) |      //side of dest
            (0 << 0));  //parallel
    } else if (strstr(s, "INTDPU.L") == s) {
      xpath = C67_map_regs(b) ^ C67_map_regs(c);

      C67_g((0 << 29) | //creg
            (0 << 28) | //inv
            (C67_map_regn(c) << 23) |     //dst
            ((C67_map_regn(b) + 1) << 18) |     //src2   WEIRD CPU must specify odd reg for some reason
            (0 << 13) | //src1 NA
            (xpath << 12) |   //x cross path if opposite sides
            (0x3b << 5) |     //opcode
            (0x6 << 2) |      //opcode fixed
            (C67_map_regs(c) << 1) |      //side of dest
            (0 << 0));  //parallel
    } else if (strstr(s, "SPDP.L") == s) {
      xpath = C67_map_regs(b) ^ C67_map_regs(c);

      C67_g((0 << 29) | //creg
            (0 << 28) | //inv
            (C67_map_regn(c) << 23) |     //dst
            (C67_map_regn(b) << 18) |     //src2
            (0 << 13) | //src1 NA
            (xpath << 12) |   //x cross path if opposite sides
            (0x2 << 6) |      //opcode
            (0x8 << 2) |      //opcode fixed
            (C67_map_regs(c) << 1) |      //side of dest
            (0 << 0));  //parallel
    } else if (strstr(s, "DPSP.L") == s) {
      ALWAYS_ASSERT(C67_map_regs(b) == C67_map_regs(c));

      C67_g((0 << 29) | //creg
            (0 << 28) | //inv
            (C67_map_regn(c) << 23) |     //dst
            ((C67_map_regn(b) + 1) << 18) |     //src2 WEIRD CPU must specify odd reg for some reason
            (0 << 13) | //src1 NA
            (0 << 12) | //x cross path if opposite sides
            (0x9 << 5) |      //opcode
            (0x6 << 2) |      //opcode fixed
            (C67_map_regs(c) << 1) |      //side of dest
            (0 << 0));  //parallel
    } else if (strstr(s, "ADD.L") == s) {
      xpath = C67_map_regs(b) ^ C67_map_regs(c);

      ALWAYS_ASSERT(C67_map_regs(a) == C67_map_regs(c));

      C67_g((0 << 29) | //creg
            (0 << 28) | //inv
            (C67_map_regn(c) << 23) |     //dst
            (C67_map_regn(b) << 18) |     //src2 (possible x path)
            (C67_map_regn(a) << 13) |     //src1 
            (xpath << 12) |   //x cross path if opposite sides
            (0x3 << 5) |      //opcode
            (0x6 << 2) |      //opcode fixed
            (C67_map_regs(c) << 1) |      //side of dest
            (0 << 0));  //parallel
    } else if (strstr(s, "SUB.L") == s) {
      xpath = C67_map_regs(b) ^ C67_map_regs(c);

      ALWAYS_ASSERT(C67_map_regs(a) == C67_map_regs(c));

      C67_g((0 << 29) | //creg
            (0 << 28) | //inv
            (C67_map_regn(c) << 23) |     //dst
            (C67_map_regn(b) << 18) |     //src2 (possible x path)
            (C67_map_regn(a) << 13) |     //src1 
            (xpath << 12) |   //x cross path if opposite sides
            (0x7 << 5) |      //opcode
            (0x6 << 2) |      //opcode fixed
            (C67_map_regs(c) << 1) |      //side of dest
            (0 << 0));  //parallel
    } else if (strstr(s, "OR.L") == s) {
      xpath = C67_map_regs(b) ^ C67_map_regs(c);

      ALWAYS_ASSERT(C67_map_regs(a) == C67_map_regs(c));

      C67_g((0 << 29) | //creg
            (0 << 28) | //inv
            (C67_map_regn(c) << 23) |     //dst
            (C67_map_regn(b) << 18) |     //src2 (possible x path)
            (C67_map_regn(a) << 13) |     //src1 
            (xpath << 12) |   //x cross path if opposite sides
            (0x7f << 5) |     //opcode
            (0x6 << 2) |      //opcode fixed
            (C67_map_regs(c) << 1) |      //side of dest
            (0 << 0));  //parallel
    } else if (strstr(s, "AND.L") == s) {
      xpath = C67_map_regs(b) ^ C67_map_regs(c);

      ALWAYS_ASSERT(C67_map_regs(a) == C67_map_regs(c));

      C67_g((0 << 29) | //creg
            (0 << 28) | //inv
            (C67_map_regn(c) << 23) |     //dst
            (C67_map_regn(b) << 18) |     //src2 (possible x path)
            (C67_map_regn(a) << 13) |     //src1 
            (xpath << 12) |   //x cross path if opposite sides
            (0x7b << 5) |     //opcode
            (0x6 << 2) |      //opcode fixed
            (C67_map_regs(c) << 1) |      //side of dest
            (0 << 0));  //parallel
    } else if (strstr(s, "XOR.L") == s) {
      xpath = C67_map_regs(b) ^ C67_map_regs(c);

      ALWAYS_ASSERT(C67_map_regs(a) == C67_map_regs(c));

      C67_g((0 << 29) | //creg
            (0 << 28) | //inv
            (C67_map_regn(c) << 23) |     //dst
            (C67_map_regn(b) << 18) |     //src2 (possible x path)
            (C67_map_regn(a) << 13) |     //src1 
            (xpath << 12) |   //x cross path if opposite sides
            (0x6f << 5) |     //opcode
            (0x6 << 2) |      //opcode fixed
            (C67_map_regs(c) << 1) |      //side of dest
            (0 << 0));  //parallel
    } else if (strstr(s, "ADDSP.L") == s) {
      xpath = C67_map_regs(b) ^ C67_map_regs(c);

      ALWAYS_ASSERT(C67_map_regs(a) == C67_map_regs(c));

      C67_g((0 << 29) | //creg
            (0 << 28) | //inv
            (C67_map_regn(c) << 23) |     //dst
            (C67_map_regn(b) << 18) |     //src2 (possible x path)
            (C67_map_regn(a) << 13) |     //src1 
            (xpath << 12) |   //x cross path if opposite sides
            (0x10 << 5) |     //opcode
            (0x6 << 2) |      //opcode fixed
            (C67_map_regs(c) << 1) |      //side of dest
            (0 << 0));  //parallel
    } else if (strstr(s, "ADDDP.L") == s) {
      xpath = C67_map_regs(b) ^ C67_map_regs(c);

      ALWAYS_ASSERT(C67_map_regs(a) == C67_map_regs(c));

      C67_g((0 << 29) | //creg
            (0 << 28) | //inv
            (C67_map_regn(c) << 23) |     //dst
            (C67_map_regn(b) << 18) |     //src2 (possible x path)
            (C67_map_regn(a) << 13) |     //src1 
            (xpath << 12) |   //x cross path if opposite sides
            (0x18 << 5) |     //opcode
            (0x6 << 2) |      //opcode fixed
            (C67_map_regs(c) << 1) |      //side of dest
            (0 << 0));  //parallel
    } else if (strstr(s, "SUBSP.L") == s) {
      xpath = C67_map_regs(b) ^ C67_map_regs(c);

      ALWAYS_ASSERT(C67_map_regs(a) == C67_map_regs(c));

      C67_g((0 << 29) | //creg
            (0 << 28) | //inv
            (C67_map_regn(c) << 23) |     //dst
            (C67_map_regn(b) << 18) |     //src2 (possible x path)
            (C67_map_regn(a) << 13) |     //src1 
            (xpath << 12) |   //x cross path if opposite sides
            (0x11 << 5) |     //opcode
            (0x6 << 2) |      //opcode fixed
            (C67_map_regs(c) << 1) |      //side of dest
            (0 << 0));  //parallel
    } else if (strstr(s, "SUBDP.L") == s) {
      xpath = C67_map_regs(b) ^ C67_map_regs(c);

      ALWAYS_ASSERT(C67_map_regs(a) == C67_map_regs(c));

      C67_g((0 << 29) | //creg
            (0 << 28) | //inv
            (C67_map_regn(c) << 23) |     //dst
            (C67_map_regn(b) << 18) |     //src2 (possible x path)
            (C67_map_regn(a) << 13) |     //src1 
            (xpath << 12) |   //x cross path if opposite sides
            (0x19 << 5) |     //opcode
            (0x6 << 2) |      //opcode fixed
            (C67_map_regs(c) << 1) |      //side of dest
            (0 << 0));  //parallel
    } else if (strstr(s, "MPYSP.M") == s) {
      xpath = C67_map_regs(b) ^ C67_map_regs(c);

      ALWAYS_ASSERT(C67_map_regs(a) == C67_map_regs(c));

      C67_g((0 << 29) | //creg
            (0 << 28) | //inv
            (C67_map_regn(c) << 23) |     //dst
            (C67_map_regn(b) << 18) |     //src2 (possible x path)
            (C67_map_regn(a) << 13) |     //src1 
            (xpath << 12) |   //x cross path if opposite sides
            (0x1c << 7) |     //opcode
            (0x0 << 2) |      //opcode fixed
            (C67_map_regs(c) << 1) |      //side of dest
            (0 << 0));  //parallel
    } else if (strstr(s, "MPYDP.M") == s) {
      xpath = C67_map_regs(b) ^ C67_map_regs(c);

      ALWAYS_ASSERT(C67_map_regs(a) == C67_map_regs(c));

      C67_g((0 << 29) | //creg
            (0 << 28) | //inv
            (C67_map_regn(c) << 23) |     //dst
            (C67_map_regn(b) << 18) |     //src2 (possible x path)
            (C67_map_regn(a) << 13) |     //src1 
            (xpath << 12) |   //x cross path if opposite sides
            (0x0e << 7) |     //opcode
            (0x0 << 2) |      //opcode fixed
            (C67_map_regs(c) << 1) |      //side of dest
            (0 << 0));  //parallel
    } else if (strstr(s, "MPYI.M") == s) {
      xpath = C67_map_regs(b) ^ C67_map_regs(c);

      ALWAYS_ASSERT(C67_map_regs(a) == C67_map_regs(c));

      C67_g((0 << 29) | //creg
            (0 << 28) | //inv
            (C67_map_regn(c) << 23) |     //dst
            (C67_map_regn(b) << 18) |     //src2
            (C67_map_regn(a) << 13) |     //src1 (cst5)
            (xpath << 12) |   //x cross path if opposite sides
            (0x4 << 7) |      //opcode
            (0x0 << 2) |      //opcode fixed
            (C67_map_regs(c) << 1) |      //side of dest
            (0 << 0));  //parallel
    } else if (strstr(s, "SHR.S") == s) {
      xpath = C67_map_regs(b) ^ C67_map_regs(c);

      ALWAYS_ASSERT(C67_map_regs(c) == C67_map_regs(a));

      C67_g((0 << 29) | //creg
            (0 << 28) | //inv
            (C67_map_regn(c) << 23) |     //dst
            (C67_map_regn(b) << 18) |     //src2
            (C67_map_regn(a) << 13) |     //src1 
            (xpath << 12) |   //x cross path if opposite sides
            (0x37 << 6) |     //opcode
            (0x8 << 2) |      //opcode fixed
            (C67_map_regs(c) << 1) |      //side of dest
            (0 << 0));  //parallel
    } else if (strstr(s, "SHRU.S") == s) {
      xpath = C67_map_regs(b) ^ C67_map_regs(c);

      ALWAYS_ASSERT(C67_map_regs(c) == C67_map_regs(a));

      C67_g((0 << 29) | //creg
            (0 << 28) | //inv
            (C67_map_regn(c) << 23) |     //dst
            (C67_map_regn(b) << 18) |     //src2
            (C67_map_regn(a) << 13) |     //src1 
            (xpath << 12) |   //x cross path if opposite sides
            (0x27 << 6) |     //opcode
            (0x8 << 2) |      //opcode fixed
            (C67_map_regs(c) << 1) |      //side of dest
            (0 << 0));  //parallel
    } else if (strstr(s, "SHL.S") == s) {
      xpath = C67_map_regs(b) ^ C67_map_regs(c);

      ALWAYS_ASSERT(C67_map_regs(c) == C67_map_regs(a));

      C67_g((0 << 29) | //creg
            (0 << 28) | //inv
            (C67_map_regn(c) << 23) |     //dst
            (C67_map_regn(b) << 18) |     //src2
            (C67_map_regn(a) << 13) |     //src1 
            (xpath << 12) |   //x cross path if opposite sides
            (0x33 << 6) |     //opcode
            (0x8 << 2) |      //opcode fixed
            (C67_map_regs(c) << 1) |      //side of dest
            (0 << 0));  //parallel
    } else if (strstr(s, "||ADDK") == s) {
      xpath = 0;        // no xpath required just use the side of the src/dst

      C67_g((0 << 29) | //creg
            (0 << 28) | //inv
            (C67_map_regn(b) << 23) |     //dst
            (a << 07) | //scst16
            (0x14 << 2) |     //opcode fixed
            (C67_map_regs(b) << 1) |      //side of dst
            (1 << 0));  //parallel
    } else if (strstr(s, "ADDK") == s) {
      xpath = 0;        // no xpath required just use the side of the src/dst

      C67_g((0 << 29) | //creg
            (0 << 28) | //inv
            (C67_map_regn(b) << 23) |     //dst
            (a << 07) | //scst16
            (0x14 << 2) |     //opcode fixed
            (C67_map_regs(b) << 1) |      //side of dst
            (0 << 0));  //parallel
    } else if (strstr(s, "NOP") == s) {
      C67_g(((a - 1) << 13) | //no of cycles
            (0 << 0));  //parallel
    } else
      ALWAYS_ASSERT(FALSE);

#ifdef ASSEMBLY_LISTING_C67
    fprintf(f, " %s %d %d %d\n", s, a, b, c);
#endif

}

//r=reg to load, fr=from reg, symbol for relocation, constant

void C67_MVKL(int r, int fc)
{
    C67_asm("MVKL.", fc, r, 0);
}

void C67_MVKH(int r, int fc)
{
    C67_asm("MVKH.", fc, r, 0);
}

void C67_STB_SP_A0(int r)
{
    C67_asm("STB.D *+SP[A0]", r, 0, 0);   // STB  r,*+SP[A0]
}

void C67_STH_SP_A0(int r)
{
    C67_asm("STH.D *+SP[A0]", r, 0, 0);   // STH  r,*+SP[A0]
}

void C67_STW_SP_A0(int r)
{
    C67_asm("STW.D *+SP[A0]", r, 0, 0);   // STW  r,*+SP[A0]
}

void C67_STB_PTR(int r, int r2)
{
    C67_asm("STB.D *", r, r2, 0);   // STB  r, *r2
}

void C67_STH_PTR(int r, int r2)
{
    C67_asm("STH.D *", r, r2, 0);   // STH  r, *r2
}

void C67_STW_PTR(int r, int r2)
{
    C67_asm("STW.D *", r, r2, 0);   // STW  r, *r2
}

void C67_STW_PTR_PRE_INC(int r, int r2, int n)
{
    C67_asm("STW.D +*", r, r2, n);  // STW  r, *+r2
}

void C67_PUSH(int r)
{
    C67_asm("STW.D SP POST DEC", r, 0, 0);      // STW  r,*SP--
}

void C67_LDW_SP_A0(int r)
{
    C67_asm("LDW.D *+SP[A0]", r, 0, 0);   // LDW  *+SP[A0],r
}

void C67_LDDW_SP_A0(int r)
{
    C67_asm("LDDW.D *+SP[A0]", r, 0, 0);  // LDDW  *+SP[A0],r
}

void C67_LDH_SP_A0(int r)
{
    C67_asm("LDH.D *+SP[A0]", r, 0, 0);   // LDH  *+SP[A0],r
}

void C67_LDB_SP_A0(int r)
{
    C67_asm("LDB.D *+SP[A0]", r, 0, 0);   // LDB  *+SP[A0],r
}

void C67_LDHU_SP_A0(int r)
{
    C67_asm("LDHU.D *+SP[A0]", r, 0, 0);  // LDHU  *+SP[A0],r
}

void C67_LDBU_SP_A0(int r)
{
    C67_asm("LDBU.D *+SP[A0]", r, 0, 0);  // LDBU  *+SP[A0],r
}

void C67_LDW_PTR(int r, int r2)
{
    C67_asm("LDW.D *", r, r2, 0);   // LDW  *r,r2
}

void C67_LDDW_PTR(int r, int r2)
{
    C67_asm("LDDW.D *", r, r2, 0);  // LDDW  *r,r2
}

void C67_LDH_PTR(int r, int r2)
{
    C67_asm("LDH.D *", r, r2, 0);   // LDH  *r,r2
}

void C67_LDB_PTR(int r, int r2)
{
    C67_asm("LDB.D *", r, r2, 0);   // LDB  *r,r2
}

void C67_LDHU_PTR(int r, int r2)
{
    C67_asm("LDHU.D *", r, r2, 0);  // LDHU  *r,r2
}

void C67_LDBU_PTR(int r, int r2)
{
    C67_asm("LDBU.D *", r, r2, 0);  // LDBU  *r,r2
}

void C67_LDW_PTR_PRE_INC(int r, int r2)
{
    C67_asm("LDW.D +*", r, r2, 0);  // LDW  *+r,r2
}

void C67_POP(int r)
{
    C67_asm("LDW.D SP PRE INC", r, 0, 0); // LDW  *++SP,r
}

void C67_POP_DW(int r)
{
    C67_asm("LDDW.D SP PRE INC", r, 0, 0);      // LDDW  *++SP,r
}

void C67_CMPLT(int s1, int s2, int dst)
{
    C67_asm("CMPLT.L1", s1, s2, dst);
}

void C67_CMPGT(int s1, int s2, int dst)
{
    C67_asm("CMPGT.L1", s1, s2, dst);
}

void C67_CMPEQ(int s1, int s2, int dst)
{
    C67_asm("CMPEQ.L1", s1, s2, dst);
}

void C67_CMPLTU(int s1, int s2, int dst)
{
    C67_asm("CMPLTU.L1", s1, s2, dst);
}

void C67_CMPGTU(int s1, int s2, int dst)
{
    C67_asm("CMPGTU.L1", s1, s2, dst);
}


void C67_CMPLTSP(int s1, int s2, int dst)
{
    C67_asm("CMPLTSP.S1", s1, s2, dst);
}

void C67_CMPGTSP(int s1, int s2, int dst)
{
    C67_asm("CMPGTSP.S1", s1, s2, dst);
}

void C67_CMPEQSP(int s1, int s2, int dst)
{
    C67_asm("CMPEQSP.S1", s1, s2, dst);
}

void C67_CMPLTDP(int s1, int s2, int dst)
{
    C67_asm("CMPLTDP.S1", s1, s2, dst);
}

void C67_CMPGTDP(int s1, int s2, int dst)
{
    C67_asm("CMPGTDP.S1", s1, s2, dst);
}

void C67_CMPEQDP(int s1, int s2, int dst)
{
    C67_asm("CMPEQDP.S1", s1, s2, dst);
}


void C67_IREG_B_REG(int inv, int r1, int r2)    // [!R] B  r2
{
    C67_asm("B.S2", inv, r1, r2);
}


// call with how many 32 bit words to skip
// (0 would branch to the branch instruction)

void C67_B_DISP(int disp)     //  B  +2  Branch with constant displacement
{
    // Branch point is relative to the 8 word fetch packet
    //
    // we will assume the text section always starts on an 8 word (32 byte boundary)
    //
    // so add in how many words into the fetch packet the branch is


    C67_asm("B DISP", disp + ((ind & 31) >> 2), 0, 0);
}

void C67_NOP(int n)
{
    C67_asm("NOP", n, 0, 0);
}

void C67_ADDK(int n, int r)
{
    ALWAYS_ASSERT(abs(n) < 32767);

    C67_asm("ADDK", n, r, 0);
}

void C67_ADDK_PARALLEL(int n, int r)
{
    ALWAYS_ASSERT(abs(n) < 32767);

    C67_asm("||ADDK", n, r, 0);
}

void C67_Adjust_ADDK(int *inst, int n)
{
    ALWAYS_ASSERT(abs(n) < 32767);

    *inst = (*inst & (~(0xffff << 7))) | ((n & 0xffff) << 7);
}

void C67_MV(int r, int v)
{
    C67_asm("MV.L", 0, r, v);
}


void C67_DPTRUNC(int r, int v)
{
    C67_asm("DPTRUNC.L", 0, r, v);
}

void C67_SPTRUNC(int r, int v)
{
    C67_asm("SPTRUNC.L", 0, r, v);
}

void C67_INTSP(int r, int v)
{
    C67_asm("INTSP.L", 0, r, v);
}

void C67_INTDP(int r, int v)
{
    C67_asm("INTDP.L", 0, r, v);
}

void C67_INTSPU(int r, int v)
{
    C67_asm("INTSPU.L", 0, r, v);
}

void C67_INTDPU(int r, int v)
{
    C67_asm("INTDPU.L", 0, r, v);
}

void C67_SPDP(int r, int v)
{
    C67_asm("SPDP.L", 0, r, v);
}

void C67_DPSP(int r, int v)   // note regs must be on the same side
{
    C67_asm("DPSP.L", 0, r, v);
}

void C67_ADD(int r, int v)
{
    C67_asm("ADD.L", v, r, v);
}

void C67_SUB(int r, int v)
{
    C67_asm("SUB.L", v, r, v);
}

void C67_AND(int r, int v)
{
    C67_asm("AND.L", v, r, v);
}

void C67_OR(int r, int v)
{
    C67_asm("OR.L", v, r, v);
}

void C67_XOR(int r, int v)
{
    C67_asm("XOR.L", v, r, v);
}

void C67_ADDSP(int r, int v)
{
    C67_asm("ADDSP.L", v, r, v);
}

void C67_SUBSP(int r, int v)
{
    C67_asm("SUBSP.L", v, r, v);
}

void C67_MPYSP(int r, int v)
{
    C67_asm("MPYSP.M", v, r, v);
}

void C67_ADDDP(int r, int v)
{
    C67_asm("ADDDP.L", v, r, v);
}

void C67_SUBDP(int r, int v)
{
    C67_asm("SUBDP.L", v, r, v);
}

void C67_MPYDP(int r, int v)
{
    C67_asm("MPYDP.M", v, r, v);
}

void C67_MPYI(int r, int v)
{
    C67_asm("MPYI.M", v, r, v);
}

void C67_SHL(int r, int v)
{
    C67_asm("SHL.S", r, v, v);
}

void C67_SHRU(int r, int v)
{
    C67_asm("SHRU.S", r, v, v);
}

void C67_SHR(int r, int v)
{
    C67_asm("SHR.S", r, v, v);
}



/* load 'r' from value 'sv' */
void load(int r, SValue * sv)
{
    int v, t, ft, fc, fr, size = 0, element;
    BOOL Unsigned = false;
    SValue v1;

    fr = sv->r;
    ft = sv->type.t;
    fc = sv->c.ul;

    v = fr & VT_VALMASK;
    if (fr & VT_LVAL) {
      if (v == VT_LLOCAL) {
          v1.type.t = VT_INT;
          v1.r = VT_LOCAL | VT_LVAL;
          v1.c.ul = fc;
          load(r, &v1);
          fr = r;
      } else if ((ft & VT_BTYPE) == VT_LDOUBLE) {
          error("long double not supported");
      } else if ((ft & VT_TYPE) == VT_BYTE) {
          size = 1;
      } else if ((ft & VT_TYPE) == (VT_BYTE | VT_UNSIGNED)) {
          size = 1;
          Unsigned = TRUE;
      } else if ((ft & VT_TYPE) == VT_SHORT) {
          size = 2;
      } else if ((ft & VT_TYPE) == (VT_SHORT | VT_UNSIGNED)) {
          size = 2;
          Unsigned = TRUE;
      } else if ((ft & VT_BTYPE) == VT_DOUBLE) {
          size = 8;
      } else {
          size = 4;
      }

      // check if fc is a positive reference on the stack, 
      // if it is tcc is referencing what it thinks is a parameter
      // on the stack, so check if it is really in a register.


      if (v == VT_LOCAL && fc > 0) {
          int stack_pos = 8;

          for (t = 0; t < NoCallArgsPassedOnStack; t++) {
            if (fc == stack_pos)
                break;

            stack_pos += TranslateStackToReg[t];
          }

          // param has been pushed on stack, get it like a local var

          fc = ParamLocOnStack[t] - 8;
      }

      if ((fr & VT_VALMASK) < VT_CONST)   // check for pure indirect
      {
          if (size == 1) {
            if (Unsigned)
                C67_LDBU_PTR(v, r); // LDBU  *v,r
            else
                C67_LDB_PTR(v, r);  // LDB  *v,r
          } else if (size == 2) {
            if (Unsigned)
                C67_LDHU_PTR(v, r); // LDHU  *v,r
            else
                C67_LDH_PTR(v, r);  // LDH  *v,r
          } else if (size == 4) {
            C67_LDW_PTR(v, r);      // LDW  *v,r
          } else if (size == 8) {
            C67_LDDW_PTR(v, r);     // LDDW  *v,r
          }

          C67_NOP(4);         // NOP 4
          return;
      } else if (fr & VT_SYM) {
          greloc(cur_text_section, sv->sym, ind, R_C60LO16);      // rem the inst need to be patched
          greloc(cur_text_section, sv->sym, ind + 4, R_C60HI16);


          C67_MVKL(C67_A0, fc);     //r=reg to load,  constant
          C67_MVKH(C67_A0, fc);     //r=reg to load,  constant


          if (size == 1) {
            if (Unsigned)
                C67_LDBU_PTR(C67_A0, r);  // LDBU  *A0,r
            else
                C67_LDB_PTR(C67_A0, r);   // LDB  *A0,r
          } else if (size == 2) {
            if (Unsigned)
                C67_LDHU_PTR(C67_A0, r);  // LDHU  *A0,r
            else
                C67_LDH_PTR(C67_A0, r);   // LDH  *A0,r
          } else if (size == 4) {
            C67_LDW_PTR(C67_A0, r); // LDW  *A0,r
          } else if (size == 8) {
            C67_LDDW_PTR(C67_A0, r);      // LDDW  *A0,r
          }

          C67_NOP(4);         // NOP 4
          return;
      } else {
          element = size;

          // divide offset in bytes to create element index
          C67_MVKL(C67_A0, (fc / element) + 8 / element);   //r=reg to load,  constant
          C67_MVKH(C67_A0, (fc / element) + 8 / element);   //r=reg to load,  constant

          if (size == 1) {
            if (Unsigned)
                C67_LDBU_SP_A0(r);  // LDBU  r, SP[A0]
            else
                C67_LDB_SP_A0(r);   // LDB  r, SP[A0]
          } else if (size == 2) {
            if (Unsigned)
                C67_LDHU_SP_A0(r);  // LDHU  r, SP[A0]
            else
                C67_LDH_SP_A0(r);   // LDH  r, SP[A0]
          } else if (size == 4) {
            C67_LDW_SP_A0(r); // LDW  r, SP[A0]
          } else if (size == 8) {
            C67_LDDW_SP_A0(r);      // LDDW  r, SP[A0]
          }


          C67_NOP(4);         // NOP 4
          return;
      }
    } else {
      if (v == VT_CONST) {
          if (fr & VT_SYM) {
            greloc(cur_text_section, sv->sym, ind, R_C60LO16);    // rem the inst need to be patched
            greloc(cur_text_section, sv->sym, ind + 4, R_C60HI16);
          }
          C67_MVKL(r, fc);    //r=reg to load, constant
          C67_MVKH(r, fc);    //r=reg to load, constant
      } else if (v == VT_LOCAL) {
          C67_MVKL(r, fc + 8);      //r=reg to load, constant C67 stack points to next free
          C67_MVKH(r, fc + 8);      //r=reg to load, constant
          C67_ADD(C67_FP, r); // MV v,r   v -> r
      } else if (v == VT_CMP) {
          C67_MV(C67_compare_reg, r);     // MV v,r   v -> r
      } else if (v == VT_JMP || v == VT_JMPI) {
          t = v & 1;
          C67_B_DISP(4);      //  Branch with constant displacement, skip over this branch, load, nop, load
          C67_MVKL(r, t);     //  r=reg to load, 0 or 1 (do this while branching)
          C67_NOP(4);         //  NOP 4
          gsym(fc);           //  modifies other branches to branch here
          C67_MVKL(r, t ^ 1); //  r=reg to load, 0 or 1
      } else if (v != r) {
          C67_MV(v, r); // MV v,r   v -> r

          if ((ft & VT_BTYPE) == VT_DOUBLE)
            C67_MV(v + 1, r + 1);   // MV v,r   v -> r
      }
    }
}


/* store register 'r' in lvalue 'v' */
void store(int r, SValue * v)
{
    int fr, bt, ft, fc, size, t, element;

    ft = v->type.t;
    fc = v->c.ul;
    fr = v->r & VT_VALMASK;
    bt = ft & VT_BTYPE;
    /* XXX: incorrect if float reg to reg */

    if (bt == VT_LDOUBLE) {
      error("long double not supported");
    } else {
      if (bt == VT_SHORT)
          size = 2;
      else if (bt == VT_BYTE)
          size = 1;
      else if (bt == VT_DOUBLE)
          size = 8;
      else
          size = 4;

      if ((v->r & VT_VALMASK) == VT_CONST) {
          /* constant memory reference */

          if (v->r & VT_SYM) {
            greloc(cur_text_section, v->sym, ind, R_C60LO16);     // rem the inst need to be patched
            greloc(cur_text_section, v->sym, ind + 4, R_C60HI16);
          }
          C67_MVKL(C67_A0, fc);     //r=reg to load,  constant
          C67_MVKH(C67_A0, fc);     //r=reg to load,  constant

          if (size == 1)
            C67_STB_PTR(r, C67_A0); // STB  r, *A0
          else if (size == 2)
            C67_STH_PTR(r, C67_A0); // STH  r, *A0
          else if (size == 4 || size == 8)
            C67_STW_PTR(r, C67_A0); // STW  r, *A0

          if (size == 8)
            C67_STW_PTR_PRE_INC(r + 1, C67_A0, 1);    // STW  r, *+A0[1]
      } else if ((v->r & VT_VALMASK) == VT_LOCAL) {
          // check case of storing to passed argument that
          // tcc thinks is on the stack but for C67 is
          // passed as a reg.  However it may have been
          // saved to the stack, if that reg was required
          // for a call to a child function

          if (fc > 0)         // argument ??
          {
            // walk through sizes and figure which param

            int stack_pos = 8;

            for (t = 0; t < NoCallArgsPassedOnStack; t++) {
                if (fc == stack_pos)
                  break;

                stack_pos += TranslateStackToReg[t];
            }

            // param has been pushed on stack, get it like a local var
            fc = ParamLocOnStack[t] - 8;
          }

          if (size == 8)
            element = 4;
          else
            element = size;

          // divide offset in bytes to create word index
          C67_MVKL(C67_A0, (fc / element) + 8 / element);   //r=reg to load,  constant
          C67_MVKH(C67_A0, (fc / element) + 8 / element);   //r=reg to load,  constant



          if (size == 1)
            C67_STB_SP_A0(r); // STB  r, SP[A0]
          else if (size == 2)
            C67_STH_SP_A0(r); // STH  r, SP[A0]
          else if (size == 4 || size == 8)
            C67_STW_SP_A0(r); // STW  r, SP[A0]

          if (size == 8) {
            C67_ADDK(1, C67_A0);    //  ADDK 1,A0
            C67_STW_SP_A0(r + 1);   //  STW  r, SP[A0]
          }
      } else {
          if (size == 1)
            C67_STB_PTR(r, fr);     // STB  r, *fr
          else if (size == 2)
            C67_STH_PTR(r, fr);     // STH  r, *fr
          else if (size == 4 || size == 8)
            C67_STW_PTR(r, fr);     // STW  r, *fr

          if (size == 8) {
            C67_STW_PTR_PRE_INC(r + 1, fr, 1);  // STW  r, *+fr[1]
          }
      }
    }
}

/* 'is_jmp' is '1' if it is a jump */
static void gcall_or_jmp(int is_jmp)
{
    int r;
    Sym *sym;

    if ((vtop->r & (VT_VALMASK | VT_LVAL)) == VT_CONST) {
      /* constant case */
      if (vtop->r & VT_SYM) {
          /* relocation case */

          // get add into A0, then start the jump B3

          greloc(cur_text_section, vtop->sym, ind, R_C60LO16);    // rem the inst need to be patched
          greloc(cur_text_section, vtop->sym, ind + 4, R_C60HI16);

          C67_MVKL(C67_A0, 0);      //r=reg to load, constant
          C67_MVKH(C67_A0, 0);      //r=reg to load, constant
          C67_IREG_B_REG(0, C67_CREG_ZERO, C67_A0);   //  B.S2x  A0

          if (is_jmp) {
            C67_NOP(5); // simple jump, just put NOP
          } else {
            // Call, must load return address into B3 during delay slots

            sym = get_sym_ref(&char_pointer_type, cur_text_section, ind + 12, 0);   // symbol for return address
            greloc(cur_text_section, sym, ind, R_C60LO16);  // rem the inst need to be patched
            greloc(cur_text_section, sym, ind + 4, R_C60HI16);
            C67_MVKL(C67_B3, 0);    //r=reg to load, constant
            C67_MVKH(C67_B3, 0);    //r=reg to load, constant
            C67_NOP(3); // put remaining NOPs
          }
      } else {
          /* put an empty PC32 relocation */
          ALWAYS_ASSERT(FALSE);
      }
    } else {
      /* otherwise, indirect call */
      r = gv(RC_INT);
      C67_IREG_B_REG(0, C67_CREG_ZERO, r);      //  B.S2x  r

      if (is_jmp) {
          C67_NOP(5);         // simple jump, just put NOP
      } else {
          // Call, must load return address into B3 during delay slots

          sym = get_sym_ref(&char_pointer_type, cur_text_section, ind + 12, 0);     // symbol for return address
          greloc(cur_text_section, sym, ind, R_C60LO16);    // rem the inst need to be patched
          greloc(cur_text_section, sym, ind + 4, R_C60HI16);
          C67_MVKL(C67_B3, 0);      //r=reg to load, constant
          C67_MVKH(C67_B3, 0);      //r=reg to load, constant
          C67_NOP(3);         // put remaining NOPs
      }
    }
}

/* generate function call with address in (vtop->t, vtop->c) and free function
   context. Stack entry is popped */
void gfunc_call(int nb_args)
{
    int i, r, size = 0;
    int args_sizes[NoCallArgsPassedOnStack];

    if (nb_args > NoCallArgsPassedOnStack) {
      error("more than 10 function params not currently supported");
      // handle more than 10, put some on the stack
    }

    for (i = 0; i < nb_args; i++) {
      if ((vtop->type.t & VT_BTYPE) == VT_STRUCT) {
          ALWAYS_ASSERT(FALSE);
      } else if ((vtop->type.t & VT_BTYPE) == VT_STRUCT) {
          ALWAYS_ASSERT(FALSE);
      } else {
          /* simple type (currently always same size) */
          /* XXX: implicit cast ? */


          if ((vtop->type.t & VT_BTYPE) == VT_LLONG) {
            error("long long not supported");
          } else if ((vtop->type.t & VT_BTYPE) == VT_LDOUBLE) {
            error("long double not supported");
          } else if ((vtop->type.t & VT_BTYPE) == VT_DOUBLE) {
            size = 8;
          } else {
            size = 4;
          }

          // put the parameter into the corresponding reg (pair)

          r = gv(RC_C67_A4 << (2 * i));

          // must put on stack because with 1 pass compiler , no way to tell
          // if an up coming nested call might overwrite these regs

          C67_PUSH(r);

          if (size == 8) {
            C67_STW_PTR_PRE_INC(r + 1, C67_SP, 3);    // STW  r, *+SP[3] (go back and put the other)
          }
          args_sizes[i] = size;
      }
      vtop--;
    }
    // POP all the params on the stack into registers for the
    // immediate call (in reverse order)

    for (i = nb_args - 1; i >= 0; i--) {

      if (args_sizes[i] == 8)
          C67_POP_DW(TREG_C67_A4 + i * 2);
      else
          C67_POP(TREG_C67_A4 + i * 2);
    }
    gcall_or_jmp(0);
    vtop--;
}


// to be compatible with Code Composer for the C67
// the first 10 parameters must be passed in registers
// (pairs for 64 bits) starting wit; A4:A5, then B4:B5 and
// ending with B12:B13.
//
// When a call is made, if the caller has its parameters
// in regs A4-B13 these must be saved before/as the call 
// parameters are loaded and restored upon return (or if/when needed).

/* generate function prolog of type 't' */
void gfunc_prolog(CType * func_type)
{
    int addr, align, size, func_call, i;
    Sym *sym;
    CType *type;

    sym = func_type->ref;
    func_call = sym->r;
    addr = 8;
    /* if the function returns a structure, then add an
       implicit pointer parameter */
    func_vt = sym->type;
    if ((func_vt.t & VT_BTYPE) == VT_STRUCT) {
      func_vc = addr;
      addr += 4;
    }

    NoOfCurFuncArgs = 0;

    /* define parameters */
    while ((sym = sym->next) != NULL) {
      type = &sym->type;
      sym_push(sym->v & ~SYM_FIELD, type, VT_LOCAL | VT_LVAL, addr);
      size = type_size(type, &align);
      size = (size + 3) & ~3;

      // keep track of size of arguments so
      // we can translate where tcc thinks they
      // are on the stack into the appropriate reg

      TranslateStackToReg[NoOfCurFuncArgs] = size;
      NoOfCurFuncArgs++;

#ifdef FUNC_STRUCT_PARAM_AS_PTR
      /* structs are passed as pointer */
      if ((type->t & VT_BTYPE) == VT_STRUCT) {
          size = 4;
      }
#endif
      addr += size;
    }
    func_ret_sub = 0;
    /* pascal type call ? */
    if (func_call == FUNC_STDCALL)
      func_ret_sub = addr - 8;

    C67_MV(C67_FP, C67_A0);   //  move FP -> A0
    C67_MV(C67_SP, C67_FP);   //  move SP -> FP

    // place all the args passed in regs onto the stack

    loc = 0;
    for (i = 0; i < NoOfCurFuncArgs; i++) {

      ParamLocOnStack[i] = loc;     // remember where the param is
      loc += -8;

      C67_PUSH(TREG_C67_A4 + i * 2);

      if (TranslateStackToReg[i] == 8) {
          C67_STW_PTR_PRE_INC(TREG_C67_A4 + i * 2 + 1, C67_SP, 3);      // STW  r, *+SP[1] (go back and put the other)
      }
    }

    TotalBytesPushedOnStack = -loc;

    func_sub_sp_offset = ind; // remember where we put the stack instruction 
    C67_ADDK(0, C67_SP);      //  ADDK.L2 loc,SP  (just put zero temporarily)

    C67_PUSH(C67_A0);
    C67_PUSH(C67_B3);
}

/* generate function epilog */
void gfunc_epilog(void)
{
    {
      int local = (-loc + 7) & -8;  // stack must stay aligned to 8 bytes for LDDW instr
      C67_POP(C67_B3);
      C67_NOP(4);       // NOP wait for load
      C67_IREG_B_REG(0, C67_CREG_ZERO, C67_B3); //  B.S2  B3
      C67_POP(C67_FP);
      C67_ADDK(local, C67_SP);      //  ADDK.L2 loc,SP  
      C67_Adjust_ADDK((int *) (cur_text_section->data +
                         func_sub_sp_offset),
                  -local + TotalBytesPushedOnStack);
      C67_NOP(3);       // NOP 
    }
}

/* generate a jump to a label */
int gjmp(int t)
{
    int ind1 = ind;

    C67_MVKL(C67_A0, t);      //r=reg to load,  constant
    C67_MVKH(C67_A0, t);      //r=reg to load,  constant
    C67_IREG_B_REG(0, C67_CREG_ZERO, C67_A0);   // [!R] B.S2x  A0
    C67_NOP(5);
    return ind1;
}

/* generate a jump to a fixed address */
void gjmp_addr(int a)
{
    Sym *sym;
    // I guess this routine is used for relative short
    // local jumps, for now just handle it as the general
    // case

    // define a label that will be relocated

    sym = get_sym_ref(&char_pointer_type, cur_text_section, a, 0);
    greloc(cur_text_section, sym, ind, R_C60LO16);
    greloc(cur_text_section, sym, ind + 4, R_C60HI16);

    gjmp(0);                  // place a zero there later the symbol will be added to it
}

/* generate a test. set 'inv' to invert test. Stack entry is popped */
int gtst(int inv, int t)
{
    int ind1, n;
    int v, *p;

    v = vtop->r & VT_VALMASK;
    if (v == VT_CMP) {
      /* fast case : can jump directly since flags are set */
      // C67 uses B2 sort of as flags register
      ind1 = ind;
      C67_MVKL(C67_A0, t);    //r=reg to load, constant
      C67_MVKH(C67_A0, t);    //r=reg to load, constant

      if (C67_compare_reg != TREG_EAX &&  // check if not already in a conditional test reg
          C67_compare_reg != TREG_EDX &&
          C67_compare_reg != TREG_ST0 && C67_compare_reg != C67_B2) {
          C67_MV(C67_compare_reg, C67_B2);
          C67_compare_reg = C67_B2;
      }

      C67_IREG_B_REG(C67_invert_test ^ inv, C67_compare_reg, C67_A0);   // [!R] B.S2x  A0
      C67_NOP(5);
      t = ind1;         //return where we need to patch

    } else if (v == VT_JMP || v == VT_JMPI) {
      /* && or || optimization */
      if ((v & 1) == inv) {
          /* insert vtop->c jump list in t */
          p = &vtop->c.i;

          // I guess the idea is to traverse to the
          // null at the end of the list and store t
          // there

          n = *p;
          while (n != 0) {
            p = (int *) (cur_text_section->data + n);

            // extract 32 bit address from MVKH/MVKL
            n = ((*p >> 7) & 0xffff);
            n |= ((*(p + 1) >> 7) & 0xffff) << 16;
          }
          *p |= (t & 0xffff) << 7;
          *(p + 1) |= ((t >> 16) & 0xffff) << 7;
          t = vtop->c.i;

      } else {
          t = gjmp(t);
          gsym(vtop->c.i);
      }
    } else {
      if (is_float(vtop->type.t)) {
          vpushi(0);
          gen_op(TOK_NE);
      }
      if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) {
          /* constant jmp optimization */
          if ((vtop->c.i != 0) != inv)
            t = gjmp(t);
      } else {
          // I think we need to get the value on the stack
          // into a register, test it, and generate a branch
          // return the address of the branch, so it can be
          // later patched

          v = gv(RC_INT);     // get value into a reg 
          ind1 = ind;
          C67_MVKL(C67_A0, t);      //r=reg to load, constant
          C67_MVKH(C67_A0, t);      //r=reg to load, constant

          if (v != TREG_EAX &&      // check if not already in a conditional test reg
            v != TREG_EDX && v != TREG_ST0 && v != C67_B2) {
            C67_MV(v, C67_B2);
            v = C67_B2;
          }

          C67_IREG_B_REG(inv, v, C67_A0); // [!R] B.S2x  A0
          C67_NOP(5);
          t = ind1;           //return where we need to patch
          ind1 = ind;
      }
    }
    vtop--;
    return t;
}

/* generate an integer binary operation */
void gen_opi(int op)
{
    int r, fr, opc, t;

    switch (op) {
    case '+':
    case TOK_ADDC1:           /* add with carry generation */
      opc = 0;
      gen_op8:


// C67 can't do const compares, must load into a reg
// so just go to gv2 directly - tktk



      if (op >= TOK_ULT && op <= TOK_GT)
          gv2(RC_INT_BSIDE, RC_INT);      // make sure r (src1) is on the B Side of CPU
      else
          gv2(RC_INT, RC_INT);

      r = vtop[-1].r;
      fr = vtop[0].r;

      C67_compare_reg = C67_B2;


      if (op == TOK_LT) {
          C67_CMPLT(r, fr, C67_B2);
          C67_invert_test = false;
      } else if (op == TOK_GE) {
          C67_CMPLT(r, fr, C67_B2);
          C67_invert_test = true;
      } else if (op == TOK_GT) {
          C67_CMPGT(r, fr, C67_B2);
          C67_invert_test = false;
      } else if (op == TOK_LE) {
          C67_CMPGT(r, fr, C67_B2);
          C67_invert_test = true;
      } else if (op == TOK_EQ) {
          C67_CMPEQ(r, fr, C67_B2);
          C67_invert_test = false;
      } else if (op == TOK_NE) {
          C67_CMPEQ(r, fr, C67_B2);
          C67_invert_test = true;
      } else if (op == TOK_ULT) {
          C67_CMPLTU(r, fr, C67_B2);
          C67_invert_test = false;
      } else if (op == TOK_UGE) {
          C67_CMPLTU(r, fr, C67_B2);
          C67_invert_test = true;
      } else if (op == TOK_UGT) {
          C67_CMPGTU(r, fr, C67_B2);
          C67_invert_test = false;
      } else if (op == TOK_ULE) {
          C67_CMPGTU(r, fr, C67_B2);
          C67_invert_test = true;
      } else if (op == '+')
          C67_ADD(fr, r);     // ADD  r,fr,r
      else if (op == '-')
          C67_SUB(fr, r);     // SUB  r,fr,r
      else if (op == '&')
          C67_AND(fr, r);     // AND  r,fr,r
      else if (op == '|')
          C67_OR(fr, r);      // OR  r,fr,r
      else if (op == '^')
          C67_XOR(fr, r);     // XOR  r,fr,r
      else
          ALWAYS_ASSERT(FALSE);

      vtop--;
      if (op >= TOK_ULT && op <= TOK_GT) {
          vtop->r = VT_CMP;
          vtop->c.i = op;
      }
      break;
    case '-':
    case TOK_SUBC1:           /* sub with carry generation */
      opc = 5;
      goto gen_op8;
    case TOK_ADDC2:           /* add with carry use */
      opc = 2;
      goto gen_op8;
    case TOK_SUBC2:           /* sub with carry use */
      opc = 3;
      goto gen_op8;
    case '&':
      opc = 4;
      goto gen_op8;
    case '^':
      opc = 6;
      goto gen_op8;
    case '|':
      opc = 1;
      goto gen_op8;
    case '*':
    case TOK_UMULL:
      gv2(RC_INT, RC_INT);
      r = vtop[-1].r;
      fr = vtop[0].r;
      vtop--;
      C67_MPYI(fr, r);  // 32 bit bultiply  fr,r,fr
      C67_NOP(8);       // NOP 8 for worst case
      break;
    case TOK_SHL:
      gv2(RC_INT_BSIDE, RC_INT_BSIDE);    // shift amount must be on same side as dst
      r = vtop[-1].r;
      fr = vtop[0].r;
      vtop--;
      C67_SHL(fr, r);         // arithmetic/logical shift
      break;

    case TOK_SHR:
      gv2(RC_INT_BSIDE, RC_INT_BSIDE);    // shift amount must be on same side as dst
      r = vtop[-1].r;
      fr = vtop[0].r;
      vtop--;
      C67_SHRU(fr, r);  // logical shift
      break;

    case TOK_SAR:
      gv2(RC_INT_BSIDE, RC_INT_BSIDE);    // shift amount must be on same side as dst
      r = vtop[-1].r;
      fr = vtop[0].r;
      vtop--;
      C67_SHR(fr, r);         // arithmetic shift
      break;

    case '/':
      t = TOK__divi;
      call_func:
      vswap();
      /* call generic idiv function */
      vpush_global_sym(&func_old_type, t);
      vrott(3);
      gfunc_call(2);
      vpushi(0);
      vtop->r = REG_IRET;
      vtop->r2 = VT_CONST;
      break;
    case TOK_UDIV:
    case TOK_PDIV:
      t = TOK__divu;
      goto call_func;
    case '%':
      t = TOK__remi;
      goto call_func;
    case TOK_UMOD:
      t = TOK__remu;
      goto call_func;

    default:
      opc = 7;
      goto gen_op8;
    }
}

/* generate a floating point operation 'v = t1 op t2' instruction. The
   two operands are guaranted to have the same floating point type */
/* XXX: need to use ST1 too */
void gen_opf(int op)
{
    int ft, fc, fr, r;

    if (op >= TOK_ULT && op <= TOK_GT)
      gv2(RC_EDX, RC_EAX);    // make sure src2 is on b side
    else
      gv2(RC_FLOAT, RC_FLOAT);      // make sure src2 is on b side

    ft = vtop->type.t;
    fc = vtop->c.ul;
    r = vtop->r;
    fr = vtop[-1].r;


    if ((ft & VT_BTYPE) == VT_LDOUBLE)
      error("long doubles not supported");

    if (op >= TOK_ULT && op <= TOK_GT) {

      r = vtop[-1].r;
      fr = vtop[0].r;

      C67_compare_reg = C67_B2;

      if (op == TOK_LT) {
          if ((ft & VT_BTYPE) == VT_DOUBLE)
            C67_CMPLTDP(r, fr, C67_B2);
          else
            C67_CMPLTSP(r, fr, C67_B2);

          C67_invert_test = false;
      } else if (op == TOK_GE) {
          if ((ft & VT_BTYPE) == VT_DOUBLE)
            C67_CMPLTDP(r, fr, C67_B2);
          else
            C67_CMPLTSP(r, fr, C67_B2);

          C67_invert_test = true;
      } else if (op == TOK_GT) {
          if ((ft & VT_BTYPE) == VT_DOUBLE)
            C67_CMPGTDP(r, fr, C67_B2);
          else
            C67_CMPGTSP(r, fr, C67_B2);

          C67_invert_test = false;
      } else if (op == TOK_LE) {
          if ((ft & VT_BTYPE) == VT_DOUBLE)
            C67_CMPGTDP(r, fr, C67_B2);
          else
            C67_CMPGTSP(r, fr, C67_B2);

          C67_invert_test = true;
      } else if (op == TOK_EQ) {
          if ((ft & VT_BTYPE) == VT_DOUBLE)
            C67_CMPEQDP(r, fr, C67_B2);
          else
            C67_CMPEQSP(r, fr, C67_B2);

          C67_invert_test = false;
      } else if (op == TOK_NE) {
          if ((ft & VT_BTYPE) == VT_DOUBLE)
            C67_CMPEQDP(r, fr, C67_B2);
          else
            C67_CMPEQSP(r, fr, C67_B2);

          C67_invert_test = true;
      } else {
          ALWAYS_ASSERT(FALSE);
      }
      vtop->r = VT_CMP; // tell TCC that result is in "flags" actually B2
    } else {
      if (op == '+') {
          if ((ft & VT_BTYPE) == VT_DOUBLE) {
            C67_ADDDP(r, fr); // ADD  fr,r,fr
            C67_NOP(6);
          } else {
            C67_ADDSP(r, fr); // ADD  fr,r,fr
            C67_NOP(3);
          }
          vtop--;
      } else if (op == '-') {
          if ((ft & VT_BTYPE) == VT_DOUBLE) {
            C67_SUBDP(r, fr); // SUB  fr,r,fr
            C67_NOP(6);
          } else {
            C67_SUBSP(r, fr); // SUB  fr,r,fr
            C67_NOP(3);
          }
          vtop--;
      } else if (op == '*') {
          if ((ft & VT_BTYPE) == VT_DOUBLE) {
            C67_MPYDP(r, fr); // MPY  fr,r,fr
            C67_NOP(9);
          } else {
            C67_MPYSP(r, fr); // MPY  fr,r,fr
            C67_NOP(3);
          }
          vtop--;
      } else if (op == '/') {
          if ((ft & VT_BTYPE) == VT_DOUBLE) {
            // must call intrinsic DP floating point divide
            vswap();
            /* call generic idiv function */
            vpush_global_sym(&func_old_type, TOK__divd);
            vrott(3);
            gfunc_call(2);
            vpushi(0);
            vtop->r = REG_FRET;
            vtop->r2 = REG_LRET;

          } else {
            // must call intrinsic SP floating point divide
            vswap();
            /* call generic idiv function */
            vpush_global_sym(&func_old_type, TOK__divf);
            vrott(3);
            gfunc_call(2);
            vpushi(0);
            vtop->r = REG_FRET;
            vtop->r2 = VT_CONST;
          }
      } else
          ALWAYS_ASSERT(FALSE);


    }
}


/* convert integers to fp 't' type. Must handle 'int', 'unsigned int'
   and 'long long' cases. */
void gen_cvt_itof(int t)
{
    int r;

    gv(RC_INT);
    r = vtop->r;

    if ((t & VT_BTYPE) == VT_DOUBLE) {
      if (t & VT_UNSIGNED)
          C67_INTDPU(r, r);
      else
          C67_INTDP(r, r);

      C67_NOP(4);
      vtop->type.t = VT_DOUBLE;
    } else {
      if (t & VT_UNSIGNED)
          C67_INTSPU(r, r);
      else
          C67_INTSP(r, r);
      C67_NOP(3);
      vtop->type.t = VT_FLOAT;
    }

}

/* convert fp to int 't' type */
/* XXX: handle long long case */
void gen_cvt_ftoi(int t)
{
    int r;

    gv(RC_FLOAT);
    r = vtop->r;

    if (t != VT_INT)
      error("long long not supported");
    else {
      if ((vtop->type.t & VT_BTYPE) == VT_DOUBLE) {
          C67_DPTRUNC(r, r);
          C67_NOP(3);
      } else {
          C67_SPTRUNC(r, r);
          C67_NOP(3);
      }

      vtop->type.t = VT_INT;

    }
}

/* convert from one floating point type to another */
void gen_cvt_ftof(int t)
{
    int r, r2;

    if ((vtop->type.t & VT_BTYPE) == VT_DOUBLE &&
      (t & VT_BTYPE) == VT_FLOAT) {
      // convert double to float

      gv(RC_FLOAT);           // get it in a register pair

      r = vtop->r;

      C67_DPSP(r, r);         // convert it to SP same register
      C67_NOP(3);

      vtop->type.t = VT_FLOAT;
      vtop->r2 = VT_CONST;    // set this as unused
    } else if ((vtop->type.t & VT_BTYPE) == VT_FLOAT &&
             (t & VT_BTYPE) == VT_DOUBLE) {
      // convert float to double

      gv(RC_FLOAT);           // get it in a register

      r = vtop->r;

      if (r == TREG_EAX) {    // make sure the paired reg is avail
          r2 = get_reg(RC_ECX);
      } else if (r == TREG_EDX) {
          r2 = get_reg(RC_ST0);
      } else {
          ALWAYS_ASSERT(FALSE);
            r2 = 0; /* avoid warning */
        }

      C67_SPDP(r, r);         // convert it to DP same register
      C67_NOP(1);

      vtop->type.t = VT_DOUBLE;
      vtop->r2 = r2;          // set this as unused
    } else {
      ALWAYS_ASSERT(FALSE);
    }
}

/* computed goto support */
void ggoto(void)
{
    gcall_or_jmp(1);
    vtop--;
}

/* end of X86 code generator */
/*************************************************************/

Generated by  Doxygen 1.6.0   Back to index