1/*	$NetBSD: gsp_inst.c,v 1.10 2006/12/18 20:12:21 christos Exp $	*/
2/*
3 * TMS34010 GSP assembler - Instruction encoding
4 *
5 * Copyright (c) 1993 Paul Mackerras.
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 *    must display the following acknowledgement:
18 *      This product includes software developed by Paul Mackerras.
19 * 4. The name of the author may not be used to endorse or promote products
20 *    derived from this software without specific prior written permission
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
27 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
31 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 */
33
34#include <sys/cdefs.h>
35#ifndef lint
36__RCSID("$NetBSD: gsp_inst.c,v 1.10 2006/12/18 20:12:21 christos Exp $");
37#endif
38
39#include <string.h>
40#include <assert.h>
41#include "gsp_ass.h"
42#include "gsp_code.h"
43
44struct inst {
45	const char *opname;
46	u_int16_t opcode;
47	u_char	class;		/* instruction class + flags */
48	u_char	optypes[4];	/* permissible operand classes */
49};
50
51/* Values for flags in class field */
52#define NOIMM16	0x80		/* can't have 16-bit immediate */
53#define K32	0x40		/* values 1..32 for K-type constant */
54#define IMMCOM	0x20		/* immediate value is complemented */
55#define IMMNEG	0x80		/* immediate value is negated */
56#define NODSJS	0x80		/* can't use 5-bit branch offset */
57#define DSHORT	0x40		/* must use 5-bit offset */
58
59#define CLASS	0x1F
60
61/* Values for class */
62#define NOP	0		/* no operands */
63#define ONEREG	1		/* reg */
64#define TWOREG	2		/* reg, reg */
65#define DYADIC	3		/* immediate or reg, reg */
66#define ADD	(DYADIC|K32)
67#define SUB	(DYADIC|IMMCOM|K32)
68#define CMP	(DYADIC|IMMCOM)
69#define AND	(DYADIC|NOIMM16|IMMCOM)
70#define OR	(DYADIC|NOIMM16)
71#define IMMREG	4		/* immediate, reg */
72#define IMMREGC	(IMMREG|IMMCOM)
73#define LIMREG	(IMMREG|NOIMM16)
74#define LIMREGC	(LIMREG|IMMCOM)
75#define KREG	5		/* short immediate, reg */
76#define K32REG	(KREG|K32)
77#define SRA	(KREG|IMMNEG)
78#define BTST	(KREG|IMMCOM)
79#define CALL	6		/* reg or address */
80#define JUMP	7
81#define CLR	8		/* reg appears twice in encoding */
82#define DSJ	9
83#define DSJEQ	(DSJ|NODSJS)
84#define DSJS	(DSJ|DSHORT)
85#define EXGF	10
86#define SETF	11
87#define FILL	12
88#define LINE	13
89#define PIXBLT	14
90#define PIXT	15
91#define MMFM	16
92#define MOVB	17
93#define MOVE	18
94#define MOVEK	(MOVE|K32)
95#define RETS	19
96#define PSEUDO	20
97
98/* Composite operand classes */
99#define EXREG	(REG|EXPR)
100#define EAREG	(REG|EA)
101#define EXAREG	(REG|EXPR|EA)
102#define OPTOPRN	0x80		/* signals optional operand */
103#define SPEC	(0x10|EXPR)		/* field or length specifier */
104#define OPTREG	(OPTOPRN|REG)
105#define OPTEXPR	(OPTOPRN|EXPR)
106#define OPTSPEC	(OPTOPRN|SPEC)
107#define OPTXREG	(OPTOPRN|EXREG)
108
109#define MIN(a, b)	((a) < (b)? (a): (b))
110
111/*
112 * N.B. This list must be sorted in order of opname.
113 */
114struct inst instructions[] = {
115	{".BLKB", BLKB,	PSEUDO,	{0,	0,	0,	0}},
116	{".BLKL", BLKL,	PSEUDO,	{0,	0,	0,	0}},
117	{".BLKW", BLKW,	PSEUDO,	{0,	0,	0,	0}},
118#ifdef EQU
119	{".EQU", EQU,	PSEUDO,	{0,	0,	0,	0}},
120#endif
121	{".INCLUDE", INCL, PSEUDO, {0,	0,	0,	0}},
122	{".LONG", LONG,	PSEUDO,	{0,	0,	0,	0}},
123	{".ORG", ORG,	PSEUDO,	{0,	0,	0,	0}},
124	{".START",START,PSEUDO,	{0,	0,	0,	0}},
125	{".WORD",WORD,	PSEUDO,	{0,	0,	0,	0}},
126	{"ABS",	0x0380,	ONEREG,	{REG,	0,	0,	0}},
127	{"ADD",	0x4000,	ADD,	{EXREG,	REG,	OPTSPEC,0}},
128	{"ADDC",0x4200, TWOREG,	{REG,	REG,	0,	0}},
129	{"ADDI",0x0B20,	IMMREG,	{EXPR,	REG,	OPTSPEC,0}},
130	{"ADDK",0x1000,	K32REG,	{EXPR,	REG,	0,	0}},
131	{"ADDXY",0xE000,TWOREG,	{REG,	REG,	0,	0}},
132	{"AND",	0x5000,	AND,	{EXREG,	REG,	0,	0}},
133	{"ANDI",0x0B80,	LIMREGC,{EXPR,	REG,	0,	0}},
134	{"ANDN",0x5200,	OR,	{EXREG,	REG,	0,	0}},
135	{"ANDNI",0x0B80,LIMREG,	{EXPR,	REG,	0,	0}},
136	{"BTST",0x1C00,	BTST,	{EXREG,	REG,	0,	0}},
137	{"CALL",0x0920,	CALL,	{EXREG,	0,	0,	0}},
138	{"CALLA",0x0D5F,CALL,	{EXPR,	0,	0,	0}},
139	{"CALLR",0x0D3F,CALL,	{EXPR,	0,	0,	0}},
140	{"CLR",	0x5600,	CLR,	{REG,	0,	0,	0}},
141	{"CLRC",0x0320,	NOP,	{0,	0,	0,	0}},
142	{"CMP",	0x4800,	CMP,	{EXREG,	REG,	OPTSPEC,0}},
143	{"CMPI",0x0B60,	IMMREGC,{EXPR,	REG,	OPTSPEC,0}},
144	{"CMPXY",0xE400,TWOREG,	{REG,	REG,	0,	0}},
145	{"CPW",	0xE600,	TWOREG,	{REG,	REG,	0,	0}},
146	{"CVXYL",0xE800,TWOREG,	{REG,	REG,	0,	0}},
147	{"DEC",	0x1420,	ONEREG,	{REG,	0,	0,	0}},
148	{"DINT",0x0360,	NOP,	{0,	0,	0,	0}},
149	{"DIVS",0x5800,	TWOREG,	{REG,	REG,	0,	0}},
150	{"DIVU",0x5A00,	TWOREG,	{REG,	REG,	0,	0}},
151	{"DRAV",0xF600,	TWOREG,	{REG,	REG,	0,	0}},
152	{"DSJ",	0x0D80,	DSJ,	{REG,	EXPR,	0,	0}},
153	{"DSJEQ",0x0DA0,DSJEQ,	{REG,	EXPR,	0,	0}},
154	{"DSJNE",0x0DC0,DSJEQ,	{REG,	EXPR,	0,	0}},
155	{"DSJS",0x3800,	DSJS,	{REG,	EXPR,	0,	0}},
156	{"EINT",0x0D60,	NOP,	{0,	0,	0,	0}},
157	{"EMU",	0x0100,	NOP,	{0,	0,	0,	0}},
158	{"EXGF",0xD500,	EXGF,	{REG,	OPTSPEC,0,	0}},
159	{"EXGPC",0x0120,ONEREG,	{REG,	0,	0,	0}},
160	{"FILL",0x0FC0,	FILL,	{SPEC,	0,	0,	0}},
161	{"GETPC",0x0140,ONEREG,	{REG,	0,	0,	0}},
162	{"GETST",0x0180,ONEREG,	{REG,	0,	0,	0}},
163	{"INC",	0x1020,	ONEREG,	{REG,	0,	0,	0}},
164	{"JAB",	0xC880,	JUMP,	{EXPR,	0,	0,	0}},
165	{"JAC",	0xC880,	JUMP,	{EXPR,	0,	0,	0}},
166	{"JAEQ",0xCA80,	JUMP,	{EXPR,	0,	0,	0}},
167	{"JAGE",0xC580,	JUMP,	{EXPR,	0,	0,	0}},
168	{"JAGT",0xC780,	JUMP,	{EXPR,	0,	0,	0}},
169	{"JAHI",0xC380,	JUMP,	{EXPR,	0,	0,	0}},
170	{"JAHS",0xC980,	JUMP,	{EXPR,	0,	0,	0}},
171	{"JALE",0xC680,	JUMP,	{EXPR,	0,	0,	0}},
172	{"JALO",0xC880,	JUMP,	{EXPR,	0,	0,	0}},
173	{"JALS",0xC280,	JUMP,	{EXPR,	0,	0,	0}},
174	{"JALT",0xC480,	JUMP,	{EXPR,	0,	0,	0}},
175	{"JAN",	0xCE80,	JUMP,	{EXPR,	0,	0,	0}},
176	{"JANB",0xC980,	JUMP,	{EXPR,	0,	0,	0}},
177	{"JANC",0xC980,	JUMP,	{EXPR,	0,	0,	0}},
178	{"JANE",0xCB80,	JUMP,	{EXPR,	0,	0,	0}},
179	{"JANN",0xCF80,	JUMP,	{EXPR,	0,	0,	0}},
180	{"JANV",0xCD80,	JUMP,	{EXPR,	0,	0,	0}},
181	{"JANZ",0xCB80,	JUMP,	{EXPR,	0,	0,	0}},
182	{"JAP",	0xC180,	JUMP,	{EXPR,	0,	0,	0}},
183	{"JAUC",0xC080,	JUMP,	{EXPR,	0,	0,	0}},
184	{"JAV",	0xCC80,	JUMP,	{EXPR,	0,	0,	0}},
185	{"JAZ",	0xCA80,	JUMP,	{EXPR,	0,	0,	0}},
186	{"JRB",	0xC800,	JUMP,	{EXPR,	OPTSPEC,0,	0}},
187	{"JRC",	0xC800,	JUMP,	{EXPR,	OPTSPEC,0,	0}},
188	{"JREQ",0xCA00,	JUMP,	{EXPR,	OPTSPEC,0,	0}},
189	{"JRGE",0xC500,	JUMP,	{EXPR,	OPTSPEC,0,	0}},
190	{"JRGT",0xC700,	JUMP,	{EXPR,	OPTSPEC,0,	0}},
191	{"JRHI",0xC300,	JUMP,	{EXPR,	OPTSPEC,0,	0}},
192	{"JRHS",0xC900,	JUMP,	{EXPR,	OPTSPEC,0,	0}},
193	{"JRLE",0xC600,	JUMP,	{EXPR,	OPTSPEC,0,	0}},
194	{"JRLO",0xC800,	JUMP,	{EXPR,	OPTSPEC,0,	0}},
195	{"JRLS",0xC200,	JUMP,	{EXPR,	OPTSPEC,0,	0}},
196	{"JRLT",0xC400,	JUMP,	{EXPR,	OPTSPEC,0,	0}},
197	{"JRN",	0xCE00,	JUMP,	{EXPR,	OPTSPEC,0,	0}},
198	{"JRNB",0xC900,	JUMP,	{EXPR,	OPTSPEC,0,	0}},
199	{"JRNC",0xC900,	JUMP,	{EXPR,	OPTSPEC,0,	0}},
200	{"JRNE",0xCB00,	JUMP,	{EXPR,	OPTSPEC,0,	0}},
201	{"JRNN",0xCF00,	JUMP,	{EXPR,	OPTSPEC,0,	0}},
202	{"JRNV",0xCD00,	JUMP,	{EXPR,	OPTSPEC,0,	0}},
203	{"JRNZ",0xCB00,	JUMP,	{EXPR,	OPTSPEC,0,	0}},
204	{"JRP",	0xC100,	JUMP,	{EXPR,	OPTSPEC,0,	0}},
205	{"JRUC",0xC000,	JUMP,	{EXPR,	OPTSPEC,0,	0}},
206	{"JRV",	0xCC00,	JUMP,	{EXPR,	OPTSPEC,0,	0}},
207	{"JRZ",	0xCA00,	JUMP,	{EXPR,	OPTSPEC,0,	0}},
208	{"JUMP",0x0160,	JUMP,	{EXREG,	OPTSPEC,0,	0}},
209	{"LINE",0xDF1A,	LINE,	{SPEC,	0,	0,	0}},
210	{"LMO",	0x6A00,	TWOREG,	{REG,	REG,	0,	0}},
211	{"MMFM",0x09A0,	MMFM,	{REG,	OPTXREG,OPTREG,	OPTREG}},
212	{"MMTM",0x0980,	MMFM,	{REG,	OPTXREG,OPTREG,	OPTREG}},
213	{"MODS",0x6C00,	TWOREG,	{REG,	REG,	0,	0}},
214	{"MODU",0x6E00,	TWOREG,	{REG,	REG,	0,	0}},
215	{"MOVB",0,	MOVB,	{EAREG,	EAREG,	0,	0}},
216	{"MOVE",0x4C00,	MOVEK,	{EXAREG,EAREG,	OPTSPEC,0}},
217	{"MOVI",0x09E0,	IMMREG,	{EXPR,	REG,	OPTSPEC,0}},
218	{"MOVK",0x1800,	K32REG,	{EXPR,	REG,	0,	0}},
219	{"MOVX",0xEC00,	TWOREG,	{REG,	REG,	0,	0}},
220	{"MOVY",0xEE00,	TWOREG,	{REG,	REG,	0,	0}},
221	{"MPYS",0x5C00,	TWOREG,	{REG,	REG,	0,	0}},
222	{"MPYU",0x5E00,	TWOREG,	{REG,	REG,	0,	0}},
223	{"NEG",	0x03A0,	ONEREG,	{REG,	0,	0,	0}},
224	{"NEGB",0x03C0,	ONEREG,	{REG,	0,	0,	0}},
225	{"NOP",	0x0300,	NOP,	{0,	0,	0,	0}},
226	{"NOT",	0x03E0,	ONEREG,	{REG,	0,	0,	0}},
227	{"OR",	0x5400,	OR,	{EXREG,	REG,	0,	0}},
228	{"ORI",	0x0BA0,	LIMREG,	{EXPR,	REG,	0,	0}},
229	{"PIXBLT",0x0F00,PIXBLT,{SPEC,	SPEC,	0,	0}},
230	{"PIXT",0,	PIXT,	{EAREG,	EAREG,	0,	0}},
231	{"POPST",0x01C0,NOP,	{0,	0,	0,	0}},
232	{"PUSHST",0x01E0,NOP,	{0,	0,	0,	0}},
233	{"PUTST",0x01A0,ONEREG,	{REG,	0,	0,	0}},
234	{"RETI",0x0940,	NOP,	{0,	0,	0,	0}},
235	{"RETS",0x0960,	RETS,	{OPTEXPR,0,	0,	0}},
236	{"REV",	0x0020,	ONEREG,	{REG,	0,	0,	0}},
237	{"RL",	0x3000,	KREG,	{EXREG,	REG,	0,	0}},
238	{"SETC",0x0DE0,	NOP,	{0,	0,	0,	0}},
239	{"SETF",0x0540,	SETF,	{EXPR,	EXPR,	OPTSPEC,0}},
240	{"SEXT",0x0500,	EXGF,	{REG,	OPTSPEC,0,	0}},
241	{"SLA",	0x2000,	KREG,	{EXREG,	REG,	0,	0}},
242	{"SLL",	0x2400,	KREG,	{EXREG,	REG,	0,	0}},
243	{"SRA",	0x2800,	SRA,	{EXREG,	REG,	0,	0}},
244	{"SRL",	0x2C00,	SRA,	{EXREG,	REG,	0,	0}},
245	{"SUB",	0x4400,	SUB,	{EXREG,	REG,	OPTSPEC,0}},
246	{"SUBB",0x4600,	TWOREG,	{REG,	REG,	0,	0}},
247	{"SUBI",0x0D00,	IMMREGC,{EXPR,	REG,	OPTSPEC,0}},
248	{"SUBK",0x1400,	K32REG,	{EXPR,	REG,	0,	0}},
249	{"SUBXY",0xE200,TWOREG,	{REG,	REG,	0,	0}},
250	{"TRAP",0x0900,	RETS,	{EXPR,	0,	0,	0}},
251	{"XOR",	0x5600,	OR,	{EXREG,	REG,	0,	0}},
252	{"XORI",0x0BC0,	LIMREG,	{EXPR,	REG,	0,	0}},
253	{"ZEXT",0x0520,	EXGF,	{REG,	OPTSPEC,0,	0}},
254	{NULL,	0,	0,	{0,	0,	0,	0}}
255};
256
257int check_spec(int spec, const char *valid, const char *what);
258void do_statement(char *opcode, operand operands);
259int encode_instr(struct inst *ip, operand ops, int *spec, u_int16_t *iwords);
260int specifier(operand op);
261
262void
263statement(char *opcode, operand operands)
264{
265	do_statement(opcode, operands);
266	free_operands(operands);
267}
268
269void
270do_statement(char *opcode, operand operands)
271{
272	struct inst *ip;
273	int i, req;
274	unsigned nop;
275	operand op;
276	int spec[3];
277	u_int16_t iwords[6];
278
279	ucasify(opcode);
280	i = 1;
281	for( ip = instructions; ip->opname != NULL; ++ip )
282		if( opcode[0] == ip->opname[0] ){
283			i = strcmp(opcode, ip->opname);
284			if( i <= 0 )
285				break;
286		}
287	if( i != 0 ){
288		perr("Unknown instruction code %s", opcode);
289		return;
290	}
291	if( ip->class == PSEUDO ){
292		pseudo(ip->opcode, operands);
293		return;
294	}
295
296	/* Check correspondence of operands with instruction requirements */
297	nop = 0;
298	spec[0] = spec[1] = spec[2] = 0;
299	for( op = operands; op != NULL; op = op->next ){
300		req = ip->optypes[MIN(nop, 3)];
301		if( req == 0 )
302			break;
303		if( (op->type & req) == 0 ){
304			perr("Inappropriate type for operand %u", nop+1);
305			return;
306		}
307		if( (req & ~OPTOPRN) == SPEC ) {
308			if (nop >= sizeof(spec) / sizeof(spec[0])) {
309				perr("Spec out of bounds");
310				return;
311			}
312			/* operand is a field/type/length specifier */
313			spec[nop] = specifier(op);
314		}
315		++nop;
316	}
317	if( nop < 4 && ip->optypes[nop] != 0
318	   && (ip->optypes[nop] & OPTOPRN) == 0 ){
319		perr("Insufficient operands");
320		return;
321	}
322	if( op != NULL )
323		perr("Extra operands ignored");
324
325	i = encode_instr(ip, operands, spec, iwords);
326
327	/* Pass 1 processing */
328	if( !pass2 ){
329		/* for pass 1, just work out the instruction size */
330/*		printf("pc = %#x, size = %d\n", pc, i);	*/
331		pc += i << 4;
332		return;
333	}
334
335	/* Pass 2 processing */
336	if( i > 0 )
337		putcode(iwords, i);
338}
339
340const char *specs[] = { "B", "L", "W", "XY", NULL };
341
342int
343specifier(operand op)
344{
345	const char **sl;
346	expr e;
347	char sp[4];
348
349	if( op->type != EXPR )
350		return '?';
351	e = op->op_u.value;
352	if( e->e_op == CONST ){
353		if( e->e_val == 0 || e->e_val == 1 )
354			return e->e_val + '0';
355	} else if( e->e_op == SYM ){
356		if( strlen(e->e_sym->name) > 2 )
357			return '?';
358		strcpy(sp, e->e_sym->name);
359		ucasify(sp);
360		for( sl = specs; *sl != NULL; ++sl )
361			if( strcmp(*sl, sp) == 0 )
362				return sp[0];
363	}
364	return '?';
365}
366
367int
368check_spec(int spec, const char *valid, const char *what)
369{
370	char *p;
371
372	if( spec == 0 )
373		return 0;
374	p = strchr(valid, spec);
375	if( p == NULL ){
376		perr("Invalid %s specifier", what);
377		return 0;
378	}
379	return p - valid;
380}
381
382u_int16_t code_to_imm[] = {
383	0x0B20,		/* ADDI */
384	0,
385	0x0D00,		/* SUBI */
386	0,
387	0x0B60,		/* CMPI */
388	0,
389	0x09E0,		/* MOVI */
390	0,
391	0x0B80,		/* ANDI */
392	0x0B80, 	/* ANDNI */
393	0x0BA0,		/* ORI */
394	0x0BC0,		/* XORI */
395};
396
397/* Opcodes for MOVE instruction */
398u_int16_t move_opc[7][7] = {
399/*				Source */
400/*	Reg	*Reg	*Reg+	*-Reg	*Reg.XY	*Reg(n)	@addr 	  Dest */
401	{0x4C00,0x8400,	0x9400,	0xA400,	0,	0xB400,	0x05A0}, /* R */
402	{0x8000,0x8800,	0,	0,	0,	0,	0},	/* *R */
403	{0x9000,0,	0x9800,	0,	0,	0xD000,	0xD400}, /* *R+ */
404	{0xA000,0,	0,	0xA800,	0,	0,	0},	/* *-R */
405	{0,	0,	0,	0,	0,	0,	0},	/* *R.XY */
406	{0xB000,0,	0,	0,	0,	0xB800,	0},	/* *R(n) */
407	{0x0580,0,	0,	0,	0,	0,	0x05C0}	/* @adr */
408};
409
410/* Opcodes for MOVB instruction */
411u_int16_t movb_opc[7][7] = {
412/*				Source */
413/*	Reg	*Reg	*Reg+	*-Reg	*Reg.XY	*Reg(n)	@addr 	  Dest */
414	{0,	0x8E00,	0,	0,	0,	0xAE00,	0x07E0},/* R */
415	{0x8C00,0x9C00,	0,	0,	0,	0,	0},	/* *R */
416	{0,	0,	0,	0,	0,	0,	0},	/* *R+ */
417	{0,	0,	0,	0,	0,	0,	0},	/* *-R */
418	{0,	0,	0,	0,	0,	0,	0},	/* *R.XY */
419	{0xAC00,0,	0,	0,	0,	0xBC00,	0},	/* *R(n) */
420	{0x05E0,0,	0,	0,	0,	0,	0x0340}	/* @adr */
421};
422
423/* Opcodes for PIXT instruction */
424u_int16_t pixt_opc[7][7] = {
425/*				Source */
426/*	Reg	*Reg	*Reg+	*-Reg	*Reg.XY	*Reg(n)	@addr 	  Dest */
427	{0,	0xFA00,	0,	0,	0xF200,	0,	0},	/* R */
428	{0xF800,0xFC00,	0,	0,	0,	0,	0},	/* *R */
429	{0,	0,	0,	0,	0,	0,	0},	/* *R+ */
430	{0,	0,	0,	0,	0,	0,	0},	/* *-R */
431	{0xF000,0,	0,	0,	0xF400,	0,	0},	/* *R.XY */
432	{0,	0,	0,	0,	0,	0,	0},	/* *R(n) */
433	{0,	0,	0,	0,	0,	0,	0}	/* @adr */
434};
435
436#define USES_REG(op)	((op)->type == REG \
437			 || ((op)->type == EA && (op)->mode != M_ABSOLUTE))
438#define USES_EXPR(op)	((op)->type == EXPR \
439			 || ((op)->type == EA && (op)->mode >= M_INDEX))
440
441int
442encode_instr(struct inst *ip, operand ops, int *spec, u_int16_t *iwords)
443{
444	int rs, rd;
445	int opc, nw, class, flags, ms, md, off;
446	int mask, file, bit, i;
447	operand op0, op1;
448	unsigned eline[2];
449	int32_t val[2];
450
451	rs = rd = 0;
452	opc = ip->opcode;
453	nw = 1;
454	op0 = ops;
455	if( op0 != NULL ){
456		if( spec[0] == 0 && USES_EXPR(op0) )
457			eval_expr(op0->op_u.value, &val[0], &eline[0]);
458		op1 = ops->next;
459		if( op1 != NULL && spec[1] == 0 && USES_EXPR(op1) )
460			eval_expr(op1->op_u.value, &val[1], &eline[1]);
461	} else
462		op1 = NULL;
463	class = ip->class & CLASS;
464	flags = ip->class & ~CLASS;
465	if (class == MOVE && op0 && op1 && op1->type == REG) {
466		if (op0->type == REG) {
467			class = DYADIC;
468			if ((op0->reg_no & op1->reg_no & GSPA_REGFILE) == 0) {
469				opc += 0x0200;
470				op1->reg_no ^= GSPA_A0 ^ GSPA_B0;
471			}
472		} else if ( op0->type == EXPR )
473			class = DYADIC;
474	}
475	if( class == DYADIC ){
476		/* turn it into TWOREG, IMMREG or KREG */
477		if( op0->type == REG ){
478			class = TWOREG;
479		} else if( (flags & K32) != 0 && eline[0] <= lineno
480			  && spec[2] == 0
481			  && 0 < val[0] && val[0] <= 32 ){
482			/* use 5-bit immediate */
483			class = KREG;
484			opc -= 0x3000;
485			if( opc == 0x1C00 )
486				opc = 0x1800;
487			flags &= ~IMMCOM;
488		} else {
489			class = IMMREG;
490			opc = code_to_imm[(opc - 0x4000) >> 9];
491		}
492		if( (class == TWOREG || class == KREG)
493		   && spec[2] != 0 && op1->next->next == NULL )
494			perr("Extra operands ignored");
495	} else if( class == KREG ){
496		if( op0 && op0->type == REG ){
497			class = TWOREG;
498			if( opc < 0x2000 )
499				opc = 0x4A00;	/* BTST */
500			else
501				opc = (opc >> 1) + 0x5000;
502		}
503	}
504
505	if( op0 != NULL )
506		rs = op0->reg_no;
507	if( op1 != NULL ){
508		rd = op1->reg_no;
509		if( USES_REG(op0) && USES_REG(op1) ){
510			if ((rs & rd & GSPA_REGFILE) == 0)
511				perr("Registers must be in the same register file");
512			/* force SP to the file of the other operand */
513			if (rs == GSPA_SP)
514				rs |= rd;
515			if (rd == GSPA_SP)
516				rd |= rs;
517		}
518	}
519
520	switch( class ){
521	case NOP:			/* no operands */
522		break;
523	case ONEREG:			/* reg */
524		opc |= rs & 0x1F;
525		break;
526	case TWOREG:			/* reg, reg */
527		opc |= ((rs & 0x0F) << 5) | (rd & 0x1F);
528		break;
529	case IMMREG:			/* immediate, reg */
530		opc |= rd & 0x1F;
531		if( (flags & IMMCOM) != 0 )
532			val[0] = ~ val[0];
533		i = check_spec(spec[2], " WL", "length");
534		if( i == 1
535		   || (i == 0 && (flags & NOIMM16) == 0 && eline[0] <= lineno
536		      && (int16_t)val[0] == val[0] )){
537			if( (int16_t) val[0] != val[0] )
538				perr("Value truncated to 16 bits");
539			opc -= 0x20;
540			if( opc == 0x0CE0 )	/* SUBI,W */
541				opc = 0x0BE0;
542			nw = 2;
543		} else {
544			iwords[2] = (val[0] >> 16);
545			nw = 3;
546		}
547		iwords[1] = val[0];
548		break;
549	case KREG:			/* short immediate, reg */
550		opc |= rd & 0x1F;
551		if( val[0] < 0 || ((flags & K32) == 0 && val[0] > 31)
552		   || ((flags & K32) != 0 && val[0] <= 0) || val[0] > 32 )
553			perr("5-bit constant out of range");
554		rs = val[0];
555		if( (flags & IMMCOM) != 0 )
556			rs = ~rs;
557		else if( (flags & IMMNEG) != 0 )
558			rs = -rs;
559		opc |= (rs & 0x1F) << 5;
560		break;
561	case CALL:			/* reg or address */
562		if( op0 && op0->type == REG ){
563			opc |= rs & 0x1F;
564			break;
565		}
566		off = (int)(val[0] - pc - 0x20) >> 4;
567		if( opc == 0x0920 ){		/* CALL */
568			if( eline[0] <= lineno && (int16_t) off == off )
569				opc = 0x0D3F;	/* CALLR */
570			else
571				opc = 0x0D5F;	/* CALLA */
572		}
573		if( opc == 0x0D3F ){	/* CALLR */
574			if( (int16_t) off != off )
575				perr("Displacement too large");
576			iwords[1] = off;
577			nw = 2;
578		} else {		/* CALLA */
579			iwords[1] = val[0];
580			iwords[2] = val[0] >> 16;
581			nw = 3;
582		}
583		break;
584	case JUMP:
585		if( op0 && op0->type == REG ){
586			opc |= rs & 0x1F;
587			break;
588		}
589		off = (int)(val[0] - pc - 0x10) >> 4;
590		if( (opc & 0x80) != 0 )		/* JAcc */
591			i = 2;
592		else
593			i = check_spec(spec[1], " WL", "length");
594		if( opc == 0x0160 ){	/* JUMP */
595			opc = 0xC000;	/* JRUC */
596			if( i == 0 )
597				i = 1;	/* ,W is the default for JUMP */
598		}
599		switch( i ){
600		case 2:		/* JAcc */
601			iwords[1] = val[0];
602			iwords[2] = val[0] >> 16;
603			opc |= 0x80;
604			nw = 3;
605			break;
606		case 1:
607			--off;
608			if( (int16_t) off != off )
609				perr("Displacement too large (word)");
610			iwords[1] = off;
611			nw = 2;
612			break;
613		default:
614			if( off == 0 || off < -127 || off > 127 )
615				perr("Short displacement too large or 0");
616			opc |= off & 0xFF;
617		}
618		break;
619	case CLR:			/* reg appears twice in encoding */
620		opc |= (rs & 0x1F) | ((rs & 0x0F) << 5);
621		break;
622	case DSJ:
623		off = (int)(val[1] - pc - 0x10) >> 4;
624		if( flags == 0 ){	/* DSJ */
625			if( off != 0 && off >= -31 && off <= 31 ){
626				flags = DSHORT;
627				opc = 0x3800;	/* DSJS */
628			}
629		}
630		if( flags == DSHORT ){
631			if( off == 0 || off < -31 || off > 31 )
632				perr("DSJS displacement too large");
633			if( off > 0 )
634				opc |= (off & 0x1F) << 5;
635			else
636				opc |= 0x400 | ((-off & 0x1F) << 5);
637		} else {
638			--off;
639			if( (int16_t) off != off )
640				perr("Displacement too large (word)");
641			iwords[1] = off;
642			nw = 2;
643		}
644		opc |= rs & 0x1F;
645		break;
646	case EXGF:
647		opc |= rs & 0x1F;
648		opc |= check_spec(spec[1], "01", "field") << 9;
649		break;
650	case SETF:
651		rs = val[0];
652		rd = val[1];
653		if( rs <= 0 || rs > 32 )
654			perr("Field size must be 1..32");
655		if( rd != 0 && rd != 1 )
656			perr("Field extension must be 0 or 1");
657		opc |= (rs & 0x1F) | ((rd & 1) << 5);
658		opc |= check_spec(spec[2], "01", "field") << 9;
659		break;
660	case FILL:
661		opc |= check_spec(spec[0], "LX", "array type") << 5;
662		break;
663	case LINE:
664		opc |= check_spec(spec[0], "01", "algorithm") << 7;
665		break;
666	case PIXBLT:
667		rs = check_spec(spec[0], "LXB", "source array type");
668		rd = check_spec(spec[1], "LX", "destination array type");
669		opc |= (rs << 6) | (rd << 5);
670		break;
671	case MMFM:
672		opc |= rs & 0xF;
673		file = rs & GSPA_REGFILE;
674		if( op1 == NULL )
675			mask = 0xFFFF;
676		else if( op1->type == REG ){
677			file &= rd;
678			mask = 0;
679			for( ; op1 != NULL; op1 = op1->next ){
680				rd = op1->reg_no;
681				bit = 1 << (~rd & 0xF);
682				if( file != 0 && (file &= rd) == 0 )
683					perr("Registers must all be in the same file");
684				if( file != 0 && (mask & bit) != 0 )
685					perr("Register name repeated");
686				mask |= bit;
687			}
688		} else {
689			if( val[1] < 0 || val[1] > 0xFFFFL )
690				perr("Mask value out of range");
691			mask = val[1];
692			if( op1->next != NULL )
693				perr("Extra operands ignored");
694		}
695		if ((file & GSPA_A0 & GSPA_REGFILE) == 0)
696			opc |= 0x10;
697		if ((opc & 0x20) != 0) {
698			/* mask reversed for MMFM */
699			rs = 0;
700			for( bit = 16; bit != 0; --bit ){
701				rs <<= 1;
702				rs |= mask & 1;
703				mask >>= 1;
704			}
705			mask = rs;
706		}
707		iwords[1] = mask;
708		nw = 2;
709		break;
710	case PIXT:
711	case MOVB:
712	case MOVE:
713		ms = op0 && op0->type == REG? M_REG: op0->mode;
714		assert(op1 != NULL);
715		md = op1->type == REG? M_REG: op1->mode;
716		opc = class == MOVE? move_opc[md][ms]:
717		      class == MOVB? movb_opc[md][ms]: pixt_opc[md][ms];
718		if( opc == 0 ){
719			perr("Illegal combination of addressing modes");
720			nw = 0;
721			break;
722		}
723		if( ms == M_INDEX ){
724			if( (int16_t) val[0] != val[0] )
725				perr("Source displacement too large");
726			iwords[1] = val[0];
727			nw = 2;
728		} else if( ms == M_ABSOLUTE ){
729			iwords[1] = val[0];
730			iwords[2] = val[0] >> 16;
731			nw = 3;
732			rs = 0;
733		}
734		if( md == M_INDEX ){
735			if( (int16_t) val[1] != val[1] )
736				perr("Destination displacement too large");
737			iwords[nw] = val[1];
738			++nw;
739		} else if( md == M_ABSOLUTE ){
740			iwords[nw] = val[1];
741			iwords[nw+1] = val[1] >> 16;
742			nw += 2;
743			rd = rs;
744			rs = 0;
745		}
746		opc |= (rd & 0x1F) | ((rs & 0xF) << 5);
747		opc |= check_spec(spec[2], "01", "field") << 9;
748		break;
749	case RETS:
750		if( op0 == NULL )
751			val[0] = 0;
752		else if( val[0] < 0 || val[0] > 31 )
753			perr("%s out of range",
754			     (opc > 0x900? "Pop count": "Trap number"));
755		opc |= val[0] & 0x1F;
756		break;
757	default:
758		perr("BUG: unknown instruction class %d", class);
759	}
760	iwords[0] = opc;
761	return nw;
762}
763
764