gspa/gsp_inst.c
2026-08-11 23:46:39 +09:00

755 lines
21 KiB
C

/* $NetBSD: gsp_inst.c,v 1.12 2025/11/24 08:04:28 nia 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. 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 <string.h>
#include <assert.h>
#include "gsp_ass.h"
#include "gsp_code.h"
struct inst {
const char *opname;
uint16_t opcode;
uint8_t class; /* instruction class + flags */
uint8_t 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, uint16_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];
uint16_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;
}
uint16_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 */
uint16_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 */
uint16_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 */
uint16_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, uint16_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;
}