/* $NetBSD: gsp_inst.c,v 1.11 2009/04/15 08:26:35 lukem Exp $ */ /* * TMS34010 GSP assembler - Instruction encoding * * Copyright (c) 1993 Paul Mackerras. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Paul Mackerras. * 4. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #ifndef lint __RCSID("$NetBSD: gsp_inst.c,v 1.11 2009/04/15 08:26:35 lukem Exp $"); #endif #include #include #include "gsp_ass.h" #include "gsp_code.h" struct inst { const char *opname; u_int16_t opcode; u_char class; /* instruction class + flags */ u_char optypes[4]; /* permissible operand classes */ }; /* Values for flags in class field */ #define NOIMM16 0x80 /* can't have 16-bit immediate */ #define K32 0x40 /* values 1..32 for K-type constant */ #define IMMCOM 0x20 /* immediate value is complemented */ #define IMMNEG 0x80 /* immediate value is negated */ #define NODSJS 0x80 /* can't use 5-bit branch offset */ #define DSHORT 0x40 /* must use 5-bit offset */ #define CLASS 0x1F /* Values for class */ #define NOP 0 /* no operands */ #define ONEREG 1 /* reg */ #define TWOREG 2 /* reg, reg */ #define DYADIC 3 /* immediate or reg, reg */ #define ADD (DYADIC|K32) #define SUB (DYADIC|IMMCOM|K32) #define CMP (DYADIC|IMMCOM) #define AND (DYADIC|NOIMM16|IMMCOM) #define OR (DYADIC|NOIMM16) #define IMMREG 4 /* immediate, reg */ #define IMMREGC (IMMREG|IMMCOM) #define LIMREG (IMMREG|NOIMM16) #define LIMREGC (LIMREG|IMMCOM) #define KREG 5 /* short immediate, reg */ #define K32REG (KREG|K32) #define SRA (KREG|IMMNEG) #define BTST (KREG|IMMCOM) #define CALL 6 /* reg or address */ #define JUMP 7 #define CLR 8 /* reg appears twice in encoding */ #define DSJ 9 #define DSJEQ (DSJ|NODSJS) #define DSJS (DSJ|DSHORT) #define EXGF 10 #define SETF 11 #define FILL 12 #define LINE 13 #define PIXBLT 14 #define PIXT 15 #define MMFM 16 #define MOVB 17 #define MOVE 18 #define MOVEK (MOVE|K32) #define RETS 19 #define PSEUDO 20 /* Composite operand classes */ #define EXREG (REG|EXPR) #define EAREG (REG|EA) #define EXAREG (REG|EXPR|EA) #define OPTOPRN 0x80 /* signals optional operand */ #define SPEC (0x10|EXPR) /* field or length specifier */ #define OPTREG (OPTOPRN|REG) #define OPTEXPR (OPTOPRN|EXPR) #define OPTSPEC (OPTOPRN|SPEC) #define OPTXREG (OPTOPRN|EXREG) #define MIN(a, b) ((a) < (b)? (a): (b)) /* * N.B. This list must be sorted in order of opname. */ struct inst instructions[] = { {".BLKB", BLKB, PSEUDO, {0, 0, 0, 0}}, {".BLKL", BLKL, PSEUDO, {0, 0, 0, 0}}, {".BLKW", BLKW, PSEUDO, {0, 0, 0, 0}}, #ifdef EQU {".EQU", EQU, PSEUDO, {0, 0, 0, 0}}, #endif {".INCLUDE", INCL, PSEUDO, {0, 0, 0, 0}}, {".LONG", LONG, PSEUDO, {0, 0, 0, 0}}, {".ORG", ORG, PSEUDO, {0, 0, 0, 0}}, {".START",START,PSEUDO, {0, 0, 0, 0}}, {".WORD",WORD, PSEUDO, {0, 0, 0, 0}}, {"ABS", 0x0380, ONEREG, {REG, 0, 0, 0}}, {"ADD", 0x4000, ADD, {EXREG, REG, OPTSPEC,0}}, {"ADDC",0x4200, TWOREG, {REG, REG, 0, 0}}, {"ADDI",0x0B20, IMMREG, {EXPR, REG, OPTSPEC,0}}, {"ADDK",0x1000, K32REG, {EXPR, REG, 0, 0}}, {"ADDXY",0xE000,TWOREG, {REG, REG, 0, 0}}, {"AND", 0x5000, AND, {EXREG, REG, 0, 0}}, {"ANDI",0x0B80, LIMREGC,{EXPR, REG, 0, 0}}, {"ANDN",0x5200, OR, {EXREG, REG, 0, 0}}, {"ANDNI",0x0B80,LIMREG, {EXPR, REG, 0, 0}}, {"BTST",0x1C00, BTST, {EXREG, REG, 0, 0}}, {"CALL",0x0920, CALL, {EXREG, 0, 0, 0}}, {"CALLA",0x0D5F,CALL, {EXPR, 0, 0, 0}}, {"CALLR",0x0D3F,CALL, {EXPR, 0, 0, 0}}, {"CLR", 0x5600, CLR, {REG, 0, 0, 0}}, {"CLRC",0x0320, NOP, {0, 0, 0, 0}}, {"CMP", 0x4800, CMP, {EXREG, REG, OPTSPEC,0}}, {"CMPI",0x0B60, IMMREGC,{EXPR, REG, OPTSPEC,0}}, {"CMPXY",0xE400,TWOREG, {REG, REG, 0, 0}}, {"CPW", 0xE600, TWOREG, {REG, REG, 0, 0}}, {"CVXYL",0xE800,TWOREG, {REG, REG, 0, 0}}, {"DEC", 0x1420, ONEREG, {REG, 0, 0, 0}}, {"DINT",0x0360, NOP, {0, 0, 0, 0}}, {"DIVS",0x5800, TWOREG, {REG, REG, 0, 0}}, {"DIVU",0x5A00, TWOREG, {REG, REG, 0, 0}}, {"DRAV",0xF600, TWOREG, {REG, REG, 0, 0}}, {"DSJ", 0x0D80, DSJ, {REG, EXPR, 0, 0}}, {"DSJEQ",0x0DA0,DSJEQ, {REG, EXPR, 0, 0}}, {"DSJNE",0x0DC0,DSJEQ, {REG, EXPR, 0, 0}}, {"DSJS",0x3800, DSJS, {REG, EXPR, 0, 0}}, {"EINT",0x0D60, NOP, {0, 0, 0, 0}}, {"EMU", 0x0100, NOP, {0, 0, 0, 0}}, {"EXGF",0xD500, EXGF, {REG, OPTSPEC,0, 0}}, {"EXGPC",0x0120,ONEREG, {REG, 0, 0, 0}}, {"FILL",0x0FC0, FILL, {SPEC, 0, 0, 0}}, {"GETPC",0x0140,ONEREG, {REG, 0, 0, 0}}, {"GETST",0x0180,ONEREG, {REG, 0, 0, 0}}, {"INC", 0x1020, ONEREG, {REG, 0, 0, 0}}, {"JAB", 0xC880, JUMP, {EXPR, 0, 0, 0}}, {"JAC", 0xC880, JUMP, {EXPR, 0, 0, 0}}, {"JAEQ",0xCA80, JUMP, {EXPR, 0, 0, 0}}, {"JAGE",0xC580, JUMP, {EXPR, 0, 0, 0}}, {"JAGT",0xC780, JUMP, {EXPR, 0, 0, 0}}, {"JAHI",0xC380, JUMP, {EXPR, 0, 0, 0}}, {"JAHS",0xC980, JUMP, {EXPR, 0, 0, 0}}, {"JALE",0xC680, JUMP, {EXPR, 0, 0, 0}}, {"JALO",0xC880, JUMP, {EXPR, 0, 0, 0}}, {"JALS",0xC280, JUMP, {EXPR, 0, 0, 0}}, {"JALT",0xC480, JUMP, {EXPR, 0, 0, 0}}, {"JAN", 0xCE80, JUMP, {EXPR, 0, 0, 0}}, {"JANB",0xC980, JUMP, {EXPR, 0, 0, 0}}, {"JANC",0xC980, JUMP, {EXPR, 0, 0, 0}}, {"JANE",0xCB80, JUMP, {EXPR, 0, 0, 0}}, {"JANN",0xCF80, JUMP, {EXPR, 0, 0, 0}}, {"JANV",0xCD80, JUMP, {EXPR, 0, 0, 0}}, {"JANZ",0xCB80, JUMP, {EXPR, 0, 0, 0}}, {"JAP", 0xC180, JUMP, {EXPR, 0, 0, 0}}, {"JAUC",0xC080, JUMP, {EXPR, 0, 0, 0}}, {"JAV", 0xCC80, JUMP, {EXPR, 0, 0, 0}}, {"JAZ", 0xCA80, JUMP, {EXPR, 0, 0, 0}}, {"JRB", 0xC800, JUMP, {EXPR, OPTSPEC,0, 0}}, {"JRC", 0xC800, JUMP, {EXPR, OPTSPEC,0, 0}}, {"JREQ",0xCA00, JUMP, {EXPR, OPTSPEC,0, 0}}, {"JRGE",0xC500, JUMP, {EXPR, OPTSPEC,0, 0}}, {"JRGT",0xC700, JUMP, {EXPR, OPTSPEC,0, 0}}, {"JRHI",0xC300, JUMP, {EXPR, OPTSPEC,0, 0}}, {"JRHS",0xC900, JUMP, {EXPR, OPTSPEC,0, 0}}, {"JRLE",0xC600, JUMP, {EXPR, OPTSPEC,0, 0}}, {"JRLO",0xC800, JUMP, {EXPR, OPTSPEC,0, 0}}, {"JRLS",0xC200, JUMP, {EXPR, OPTSPEC,0, 0}}, {"JRLT",0xC400, JUMP, {EXPR, OPTSPEC,0, 0}}, {"JRN", 0xCE00, JUMP, {EXPR, OPTSPEC,0, 0}}, {"JRNB",0xC900, JUMP, {EXPR, OPTSPEC,0, 0}}, {"JRNC",0xC900, JUMP, {EXPR, OPTSPEC,0, 0}}, {"JRNE",0xCB00, JUMP, {EXPR, OPTSPEC,0, 0}}, {"JRNN",0xCF00, JUMP, {EXPR, OPTSPEC,0, 0}}, {"JRNV",0xCD00, JUMP, {EXPR, OPTSPEC,0, 0}}, {"JRNZ",0xCB00, JUMP, {EXPR, OPTSPEC,0, 0}}, {"JRP", 0xC100, JUMP, {EXPR, OPTSPEC,0, 0}}, {"JRUC",0xC000, JUMP, {EXPR, OPTSPEC,0, 0}}, {"JRV", 0xCC00, JUMP, {EXPR, OPTSPEC,0, 0}}, {"JRZ", 0xCA00, JUMP, {EXPR, OPTSPEC,0, 0}}, {"JUMP",0x0160, JUMP, {EXREG, OPTSPEC,0, 0}}, {"LINE",0xDF1A, LINE, {SPEC, 0, 0, 0}}, {"LMO", 0x6A00, TWOREG, {REG, REG, 0, 0}}, {"MMFM",0x09A0, MMFM, {REG, OPTXREG,OPTREG, OPTREG}}, {"MMTM",0x0980, MMFM, {REG, OPTXREG,OPTREG, OPTREG}}, {"MODS",0x6C00, TWOREG, {REG, REG, 0, 0}}, {"MODU",0x6E00, TWOREG, {REG, REG, 0, 0}}, {"MOVB",0, MOVB, {EAREG, EAREG, 0, 0}}, {"MOVE",0x4C00, MOVEK, {EXAREG,EAREG, OPTSPEC,0}}, {"MOVI",0x09E0, IMMREG, {EXPR, REG, OPTSPEC,0}}, {"MOVK",0x1800, K32REG, {EXPR, REG, 0, 0}}, {"MOVX",0xEC00, TWOREG, {REG, REG, 0, 0}}, {"MOVY",0xEE00, TWOREG, {REG, REG, 0, 0}}, {"MPYS",0x5C00, TWOREG, {REG, REG, 0, 0}}, {"MPYU",0x5E00, TWOREG, {REG, REG, 0, 0}}, {"NEG", 0x03A0, ONEREG, {REG, 0, 0, 0}}, {"NEGB",0x03C0, ONEREG, {REG, 0, 0, 0}}, {"NOP", 0x0300, NOP, {0, 0, 0, 0}}, {"NOT", 0x03E0, ONEREG, {REG, 0, 0, 0}}, {"OR", 0x5400, OR, {EXREG, REG, 0, 0}}, {"ORI", 0x0BA0, LIMREG, {EXPR, REG, 0, 0}}, {"PIXBLT",0x0F00,PIXBLT,{SPEC, SPEC, 0, 0}}, {"PIXT",0, PIXT, {EAREG, EAREG, 0, 0}}, {"POPST",0x01C0,NOP, {0, 0, 0, 0}}, {"PUSHST",0x01E0,NOP, {0, 0, 0, 0}}, {"PUTST",0x01A0,ONEREG, {REG, 0, 0, 0}}, {"RETI",0x0940, NOP, {0, 0, 0, 0}}, {"RETS",0x0960, RETS, {OPTEXPR,0, 0, 0}}, {"REV", 0x0020, ONEREG, {REG, 0, 0, 0}}, {"RL", 0x3000, KREG, {EXREG, REG, 0, 0}}, {"SETC",0x0DE0, NOP, {0, 0, 0, 0}}, {"SETF",0x0540, SETF, {EXPR, EXPR, OPTSPEC,0}}, {"SEXT",0x0500, EXGF, {REG, OPTSPEC,0, 0}}, {"SLA", 0x2000, KREG, {EXREG, REG, 0, 0}}, {"SLL", 0x2400, KREG, {EXREG, REG, 0, 0}}, {"SRA", 0x2800, SRA, {EXREG, REG, 0, 0}}, {"SRL", 0x2C00, SRA, {EXREG, REG, 0, 0}}, {"SUB", 0x4400, SUB, {EXREG, REG, OPTSPEC,0}}, {"SUBB",0x4600, TWOREG, {REG, REG, 0, 0}}, {"SUBI",0x0D00, IMMREGC,{EXPR, REG, OPTSPEC,0}}, {"SUBK",0x1400, K32REG, {EXPR, REG, 0, 0}}, {"SUBXY",0xE200,TWOREG, {REG, REG, 0, 0}}, {"TRAP",0x0900, RETS, {EXPR, 0, 0, 0}}, {"XOR", 0x5600, OR, {EXREG, REG, 0, 0}}, {"XORI",0x0BC0, LIMREG, {EXPR, REG, 0, 0}}, {"ZEXT",0x0520, EXGF, {REG, OPTSPEC,0, 0}}, {NULL, 0, 0, {0, 0, 0, 0}} }; int check_spec(int spec, const char *valid, const char *what); void do_statement(char *opcode, operand operands); int encode_instr(struct inst *ip, operand ops, int *spec, u_int16_t *iwords); int specifier(operand op); void statement(char *opcode, operand operands) { do_statement(opcode, operands); free_operands(operands); } void do_statement(char *opcode, operand operands) { struct inst *ip; int i, req; unsigned nop; operand op; int spec[3]; u_int16_t iwords[6]; ucasify(opcode); i = 1; for( ip = instructions; ip->opname != NULL; ++ip ) if( opcode[0] == ip->opname[0] ){ i = strcmp(opcode, ip->opname); if( i <= 0 ) break; } if( i != 0 ){ perr("Unknown instruction code %s", opcode); return; } if( ip->class == PSEUDO ){ pseudo(ip->opcode, operands); return; } /* Check correspondence of operands with instruction requirements */ nop = 0; spec[0] = spec[1] = spec[2] = 0; for( op = operands; op != NULL; op = op->next ){ req = ip->optypes[MIN(nop, 3)]; if( req == 0 ) break; if( (op->type & req) == 0 ){ perr("Inappropriate type for operand %u", nop+1); return; } if( (req & ~OPTOPRN) == SPEC ) { if (nop >= sizeof(spec) / sizeof(spec[0])) { perr("Spec out of bounds"); return; } /* operand is a field/type/length specifier */ spec[nop] = specifier(op); } ++nop; } if( nop < 4 && ip->optypes[nop] != 0 && (ip->optypes[nop] & OPTOPRN) == 0 ){ perr("Insufficient operands"); return; } if( op != NULL ) perr("Extra operands ignored"); i = encode_instr(ip, operands, spec, iwords); /* Pass 1 processing */ if( !pass2 ){ /* for pass 1, just work out the instruction size */ /* printf("pc = %#x, size = %d\n", pc, i); */ pc += i << 4; return; } /* Pass 2 processing */ if( i > 0 ) putcode(iwords, i); } const char *specs[] = { "B", "L", "W", "XY", NULL }; int specifier(operand op) { const char **sl; expr e; char sp[4]; if( op->type != EXPR ) return '?'; e = op->op_u.value; if( e->e_op == CONST ){ if( e->e_val == 0 || e->e_val == 1 ) return e->e_val + '0'; } else if( e->e_op == SYM ){ if( strlen(e->e_sym->name) > 2 ) return '?'; strcpy(sp, e->e_sym->name); ucasify(sp); for( sl = specs; *sl != NULL; ++sl ) if( strcmp(*sl, sp) == 0 ) return sp[0]; } return '?'; } int check_spec(int spec, const char *valid, const char *what) { char *p; if( spec == 0 ) return 0; p = strchr(valid, spec); if( p == NULL ){ perr("Invalid %s specifier", what); return 0; } return p - valid; } u_int16_t code_to_imm[] = { 0x0B20, /* ADDI */ 0, 0x0D00, /* SUBI */ 0, 0x0B60, /* CMPI */ 0, 0x09E0, /* MOVI */ 0, 0x0B80, /* ANDI */ 0x0B80, /* ANDNI */ 0x0BA0, /* ORI */ 0x0BC0, /* XORI */ }; /* Opcodes for MOVE instruction */ u_int16_t move_opc[7][7] = { /* Source */ /* Reg *Reg *Reg+ *-Reg *Reg.XY *Reg(n) @addr Dest */ {0x4C00,0x8400, 0x9400, 0xA400, 0, 0xB400, 0x05A0}, /* R */ {0x8000,0x8800, 0, 0, 0, 0, 0}, /* *R */ {0x9000,0, 0x9800, 0, 0, 0xD000, 0xD400}, /* *R+ */ {0xA000,0, 0, 0xA800, 0, 0, 0}, /* *-R */ {0, 0, 0, 0, 0, 0, 0}, /* *R.XY */ {0xB000,0, 0, 0, 0, 0xB800, 0}, /* *R(n) */ {0x0580,0, 0, 0, 0, 0, 0x05C0} /* @adr */ }; /* Opcodes for MOVB instruction */ u_int16_t movb_opc[7][7] = { /* Source */ /* Reg *Reg *Reg+ *-Reg *Reg.XY *Reg(n) @addr Dest */ {0, 0x8E00, 0, 0, 0, 0xAE00, 0x07E0},/* R */ {0x8C00,0x9C00, 0, 0, 0, 0, 0}, /* *R */ {0, 0, 0, 0, 0, 0, 0}, /* *R+ */ {0, 0, 0, 0, 0, 0, 0}, /* *-R */ {0, 0, 0, 0, 0, 0, 0}, /* *R.XY */ {0xAC00,0, 0, 0, 0, 0xBC00, 0}, /* *R(n) */ {0x05E0,0, 0, 0, 0, 0, 0x0340} /* @adr */ }; /* Opcodes for PIXT instruction */ u_int16_t pixt_opc[7][7] = { /* Source */ /* Reg *Reg *Reg+ *-Reg *Reg.XY *Reg(n) @addr Dest */ {0, 0xFA00, 0, 0, 0xF200, 0, 0}, /* R */ {0xF800,0xFC00, 0, 0, 0, 0, 0}, /* *R */ {0, 0, 0, 0, 0, 0, 0}, /* *R+ */ {0, 0, 0, 0, 0, 0, 0}, /* *-R */ {0xF000,0, 0, 0, 0xF400, 0, 0}, /* *R.XY */ {0, 0, 0, 0, 0, 0, 0}, /* *R(n) */ {0, 0, 0, 0, 0, 0, 0} /* @adr */ }; #define USES_REG(op) ((op)->type == REG \ || ((op)->type == EA && (op)->mode != M_ABSOLUTE)) #define USES_EXPR(op) ((op)->type == EXPR \ || ((op)->type == EA && (op)->mode >= M_INDEX)) int encode_instr(struct inst *ip, operand ops, int *spec, u_int16_t *iwords) { int rs, rd; int opc, nw, class, flags, ms, md, off; int mask, file, bit, i; operand op0, op1; unsigned eline[2]; int32_t val[2]; rs = rd = 0; opc = ip->opcode; nw = 1; op0 = ops; if( op0 != NULL ){ if( spec[0] == 0 && USES_EXPR(op0) ) eval_expr(op0->op_u.value, &val[0], &eline[0]); op1 = ops->next; if( op1 != NULL && spec[1] == 0 && USES_EXPR(op1) ) eval_expr(op1->op_u.value, &val[1], &eline[1]); } else op1 = NULL; class = ip->class & CLASS; flags = ip->class & ~CLASS; if (class == MOVE && op0 && op1 && op1->type == REG) { if (op0->type == REG) { class = DYADIC; if ((op0->reg_no & op1->reg_no & GSPA_REGFILE) == 0) { opc += 0x0200; op1->reg_no ^= GSPA_A0 ^ GSPA_B0; } } else if ( op0->type == EXPR ) class = DYADIC; } if( class == DYADIC ){ /* turn it into TWOREG, IMMREG or KREG */ if( op0->type == REG ){ class = TWOREG; } else if( (flags & K32) != 0 && eline[0] <= lineno && spec[2] == 0 && 0 < val[0] && val[0] <= 32 ){ /* use 5-bit immediate */ class = KREG; opc -= 0x3000; if( opc == 0x1C00 ) opc = 0x1800; flags &= ~IMMCOM; } else { class = IMMREG; opc = code_to_imm[(opc - 0x4000) >> 9]; } if( (class == TWOREG || class == KREG) && spec[2] != 0 && op1->next->next == NULL ) perr("Extra operands ignored"); } else if( class == KREG ){ if( op0 && op0->type == REG ){ class = TWOREG; if( opc < 0x2000 ) opc = 0x4A00; /* BTST */ else opc = (opc >> 1) + 0x5000; } } if( op0 != NULL ) rs = op0->reg_no; if( op1 != NULL ){ rd = op1->reg_no; if( USES_REG(op0) && USES_REG(op1) ){ if ((rs & rd & GSPA_REGFILE) == 0) perr("Registers must be in the same register file"); /* force SP to the file of the other operand */ if (rs == GSPA_SP) rs |= rd; if (rd == GSPA_SP) rd |= rs; } } switch( class ){ case NOP: /* no operands */ break; case ONEREG: /* reg */ opc |= rs & 0x1F; break; case TWOREG: /* reg, reg */ opc |= ((rs & 0x0F) << 5) | (rd & 0x1F); break; case IMMREG: /* immediate, reg */ opc |= rd & 0x1F; if( (flags & IMMCOM) != 0 ) val[0] = ~ val[0]; i = check_spec(spec[2], " WL", "length"); if( i == 1 || (i == 0 && (flags & NOIMM16) == 0 && eline[0] <= lineno && (int16_t)val[0] == val[0] )){ if( (int16_t) val[0] != val[0] ) perr("Value truncated to 16 bits"); opc -= 0x20; if( opc == 0x0CE0 ) /* SUBI,W */ opc = 0x0BE0; nw = 2; } else { iwords[2] = (val[0] >> 16); nw = 3; } iwords[1] = val[0]; break; case KREG: /* short immediate, reg */ opc |= rd & 0x1F; if( val[0] < 0 || ((flags & K32) == 0 && val[0] > 31) || ((flags & K32) != 0 && val[0] <= 0) || val[0] > 32 ) perr("5-bit constant out of range"); rs = val[0]; if( (flags & IMMCOM) != 0 ) rs = ~rs; else if( (flags & IMMNEG) != 0 ) rs = -rs; opc |= (rs & 0x1F) << 5; break; case CALL: /* reg or address */ if( op0 && op0->type == REG ){ opc |= rs & 0x1F; break; } off = (int)(val[0] - pc - 0x20) >> 4; if( opc == 0x0920 ){ /* CALL */ if( eline[0] <= lineno && (int16_t) off == off ) opc = 0x0D3F; /* CALLR */ else opc = 0x0D5F; /* CALLA */ } if( opc == 0x0D3F ){ /* CALLR */ if( (int16_t) off != off ) perr("Displacement too large"); iwords[1] = off; nw = 2; } else { /* CALLA */ iwords[1] = val[0]; iwords[2] = val[0] >> 16; nw = 3; } break; case JUMP: if( op0 && op0->type == REG ){ opc |= rs & 0x1F; break; } off = (int)(val[0] - pc - 0x10) >> 4; if( (opc & 0x80) != 0 ) /* JAcc */ i = 2; else i = check_spec(spec[1], " WL", "length"); if( opc == 0x0160 ){ /* JUMP */ opc = 0xC000; /* JRUC */ if( i == 0 ) i = 1; /* ,W is the default for JUMP */ } switch( i ){ case 2: /* JAcc */ iwords[1] = val[0]; iwords[2] = val[0] >> 16; opc |= 0x80; nw = 3; break; case 1: --off; if( (int16_t) off != off ) perr("Displacement too large (word)"); iwords[1] = off; nw = 2; break; default: if( off == 0 || off < -127 || off > 127 ) perr("Short displacement too large or 0"); opc |= off & 0xFF; } break; case CLR: /* reg appears twice in encoding */ opc |= (rs & 0x1F) | ((rs & 0x0F) << 5); break; case DSJ: off = (int)(val[1] - pc - 0x10) >> 4; if( flags == 0 ){ /* DSJ */ if( off != 0 && off >= -31 && off <= 31 ){ flags = DSHORT; opc = 0x3800; /* DSJS */ } } if( flags == DSHORT ){ if( off == 0 || off < -31 || off > 31 ) perr("DSJS displacement too large"); if( off > 0 ) opc |= (off & 0x1F) << 5; else opc |= 0x400 | ((-off & 0x1F) << 5); } else { --off; if( (int16_t) off != off ) perr("Displacement too large (word)"); iwords[1] = off; nw = 2; } opc |= rs & 0x1F; break; case EXGF: opc |= rs & 0x1F; opc |= check_spec(spec[1], "01", "field") << 9; break; case SETF: rs = val[0]; rd = val[1]; if( rs <= 0 || rs > 32 ) perr("Field size must be 1..32"); if( rd != 0 && rd != 1 ) perr("Field extension must be 0 or 1"); opc |= (rs & 0x1F) | ((rd & 1) << 5); opc |= check_spec(spec[2], "01", "field") << 9; break; case FILL: opc |= check_spec(spec[0], "LX", "array type") << 5; break; case LINE: opc |= check_spec(spec[0], "01", "algorithm") << 7; break; case PIXBLT: rs = check_spec(spec[0], "LXB", "source array type"); rd = check_spec(spec[1], "LX", "destination array type"); opc |= (rs << 6) | (rd << 5); break; case MMFM: opc |= rs & 0xF; file = rs & GSPA_REGFILE; if( op1 == NULL ) mask = 0xFFFF; else if( op1->type == REG ){ file &= rd; mask = 0; for( ; op1 != NULL; op1 = op1->next ){ rd = op1->reg_no; bit = 1 << (~rd & 0xF); if( file != 0 && (file &= rd) == 0 ) perr("Registers must all be in the same file"); if( file != 0 && (mask & bit) != 0 ) perr("Register name repeated"); mask |= bit; } } else { if( val[1] < 0 || val[1] > 0xFFFFL ) perr("Mask value out of range"); mask = val[1]; if( op1->next != NULL ) perr("Extra operands ignored"); } if ((file & GSPA_A0 & GSPA_REGFILE) == 0) opc |= 0x10; if ((opc & 0x20) != 0) { /* mask reversed for MMFM */ rs = 0; for( bit = 16; bit != 0; --bit ){ rs <<= 1; rs |= mask & 1; mask >>= 1; } mask = rs; } iwords[1] = mask; nw = 2; break; case PIXT: case MOVB: case MOVE: ms = op0 && op0->type == REG? M_REG: op0->mode; assert(op1 != NULL); md = op1->type == REG? M_REG: op1->mode; opc = class == MOVE? move_opc[md][ms]: class == MOVB? movb_opc[md][ms]: pixt_opc[md][ms]; if( opc == 0 ){ perr("Illegal combination of addressing modes"); nw = 0; break; } if( ms == M_INDEX ){ if( (int16_t) val[0] != val[0] ) perr("Source displacement too large"); iwords[1] = val[0]; nw = 2; } else if( ms == M_ABSOLUTE ){ iwords[1] = val[0]; iwords[2] = val[0] >> 16; nw = 3; rs = 0; } if( md == M_INDEX ){ if( (int16_t) val[1] != val[1] ) perr("Destination displacement too large"); iwords[nw] = val[1]; ++nw; } else if( md == M_ABSOLUTE ){ iwords[nw] = val[1]; iwords[nw+1] = val[1] >> 16; nw += 2; rd = rs; rs = 0; } opc |= (rd & 0x1F) | ((rs & 0xF) << 5); opc |= check_spec(spec[2], "01", "field") << 9; break; case RETS: if( op0 == NULL ) val[0] = 0; else if( val[0] < 0 || val[0] > 31 ) perr("%s out of range", (opc > 0x900? "Pop count": "Trap number")); opc |= val[0] & 0x1F; break; default: perr("BUG: unknown instruction class %d", class); } iwords[0] = opc; return nw; }