#include <sys/cdefs.h>
#ifndef lint
__RCSID("$NetBSD: gsp_inst.c,v 1.12 2025/11/24 08:04:28 nia Exp $");
#endif
#include <string.h>
#include <assert.h>
#include "gsp_ass.h"
#include "gsp_code.h"
struct inst {
const char *opname;
u_int16_t opcode;
u_char class;
u_char optypes[4];
};
#define NOIMM16 0x80
#define K32 0x40
#define IMMCOM 0x20
#define IMMNEG 0x80
#define NODSJS 0x80
#define DSHORT 0x40
#define CLASS 0x1F
#define NOP 0
#define ONEREG 1
#define TWOREG 2
#define DYADIC 3
#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
#define IMMREGC (IMMREG|IMMCOM)
#define LIMREG (IMMREG|NOIMM16)
#define LIMREGC (LIMREG|IMMCOM)
#define KREG 5
#define K32REG (KREG|K32)
#define SRA (KREG|IMMNEG)
#define BTST (KREG|IMMCOM)
#define CALL 6
#define JUMP 7
#define CLR 8
#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
#define EXREG (REG|EXPR)
#define EAREG (REG|EA)
#define EXAREG (REG|EXPR|EA)
#define OPTOPRN 0x80
#define SPEC (0x10|EXPR)
#define OPTREG (OPTOPRN|REG)
#define OPTEXPR (OPTOPRN|EXPR)
#define OPTSPEC (OPTOPRN|SPEC)
#define OPTXREG (OPTOPRN|EXREG)
#define MIN(a, b) ((a) < (b)? (a): (b))
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;
}
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;
}
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);
if( !pass2 ){
pc += i << 4;
return;
}
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,
0,
0x0D00,
0,
0x0B60,
0,
0x09E0,
0,
0x0B80,
0x0B80,
0x0BA0,
0x0BC0,
};
u_int16_t move_opc[7][7] = {
{0x4C00,0x8400, 0x9400, 0xA400, 0, 0xB400, 0x05A0},
{0x8000,0x8800, 0, 0, 0, 0, 0},
{0x9000,0, 0x9800, 0, 0, 0xD000, 0xD400},
{0xA000,0, 0, 0xA800, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0},
{0xB000,0, 0, 0, 0, 0xB800, 0},
{0x0580,0, 0, 0, 0, 0, 0x05C0}
};
u_int16_t movb_opc[7][7] = {
{0, 0x8E00, 0, 0, 0, 0xAE00, 0x07E0},
{0x8C00,0x9C00, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0},
{0xAC00,0, 0, 0, 0, 0xBC00, 0},
{0x05E0,0, 0, 0, 0, 0, 0x0340}
};
u_int16_t pixt_opc[7][7] = {
{0, 0xFA00, 0, 0, 0xF200, 0, 0},
{0xF800,0xFC00, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0},
{0xF000,0, 0, 0, 0xF400, 0, 0},
{0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0}
};
#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 ){
if( op0->type == REG ){
class = TWOREG;
} else if( (flags & K32) != 0 && eline[0] <= lineno
&& spec[2] == 0
&& 0 < val[0] && val[0] <= 32 ){
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;
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");
if (rs == GSPA_SP)
rs |= rd;
if (rd == GSPA_SP)
rd |= rs;
}
}
switch( class ){
case NOP:
break;
case ONEREG:
opc |= rs & 0x1F;
break;
case TWOREG:
opc |= ((rs & 0x0F) << 5) | (rd & 0x1F);
break;
case IMMREG:
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 )
opc = 0x0BE0;
nw = 2;
} else {
iwords[2] = (val[0] >> 16);
nw = 3;
}
iwords[1] = val[0];
break;
case KREG:
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:
if( op0 && op0->type == REG ){
opc |= rs & 0x1F;
break;
}
off = (int)(val[0] - pc - 0x20) >> 4;
if( opc == 0x0920 ){
if( eline[0] <= lineno && (int16_t) off == off )
opc = 0x0D3F;
else
opc = 0x0D5F;
}
if( opc == 0x0D3F ){
if( (int16_t) off != off )
perr("Displacement too large");
iwords[1] = off;
nw = 2;
} else {
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 )
i = 2;
else
i = check_spec(spec[1], " WL", "length");
if( opc == 0x0160 ){
opc = 0xC000;
if( i == 0 )
i = 1;
}
switch( i ){
case 2:
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:
opc |= (rs & 0x1F) | ((rs & 0x0F) << 5);
break;
case DSJ:
off = (int)(val[1] - pc - 0x10) >> 4;
if( flags == 0 ){
if( off != 0 && off >= -31 && off <= 31 ){
flags = DSHORT;
opc = 0x3800;
}
}
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) {
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;
}