755 lines
21 KiB
C
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;
|
|
}
|
|
|