1/* m88k.c -- Assemble for the 88100
2   Copyright (C) 1989 Free Software Foundation, Inc.
3
4This file is not yet part of GAS, the GNU Assembler.
5
6GAS is free software; you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
8the Free Software Foundation; either version 1, or (at your option)
9any later version.
10
11GAS is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14GNU General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along with GAS; see the file COPYING.  If not, write to
18the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
19
20#include <ctype.h>
21#include <string.h>
22#include <stdlib.h>
23#include <mach-o/m88k/reloc.h>
24#include "m88k-opcode.h"
25#include "as.h"
26#include "flonum.h"
27#include "expr.h"
28#include "hash.h"
29#include "frags.h"
30#include "fixes.h"
31#include "read.h"
32#include "md.h"
33#include "obstack.h"
34#include "symbols.h"
35#include "messages.h"
36#include "input-scrub.h"
37#include "sections.h"
38
39/*
40 * These are the default cputype and cpusubtype for the m88k architecture.
41 */
42const cpu_type_t md_cputype = CPU_TYPE_MC88000;
43cpu_subtype_t md_cpusubtype = CPU_SUBTYPE_MC88000_ALL;
44
45/* This is the byte sex for the m88k architecture */
46const enum byte_sex md_target_byte_sex = BIG_ENDIAN_BYTE_SEX;
47
48#ifdef NeXT_MOD
49static int32_t in_delay_slot = 0;
50#endif
51
52static char *cmpslot[] = { "**", "**", "eq", "ne", "gt", "le", "lt", "ge",
53			   "hi", "ls", "lo", "hs",
54#ifdef m88110
55			   "be", "nb", "he", "nh",
56#endif /* m88110 */
57			    NULL };
58
59static struct {
60	char *name;
61	unsigned int num;
62
63} cndmsk[] = {
64		{ "eq0", 0x02},
65		{ "ne0", 0x0d},
66		{ "gt0", 0x01},
67		{ "lt0", 0x0c},
68		{ "ge0", 0x03},
69		{ "le0", 0x0e},
70		{ NULL,  0x00},
71	      };
72
73struct m88k_insn {
74        uint32_t opcode;
75        expressionS exp;
76#ifdef NeXT_MOD
77        int reloc;
78#else
79        enum reloc_type reloc;
80#endif
81};
82
83static struct hash_control *op_hash = NULL;
84
85/* These chars start a comment anywhere in a source file (except inside
86   another comment */
87const char md_comment_chars[] = ";";
88
89/* These chars only start a comment at the beginning of a line. */
90const char md_line_comment_chars[] = "#";
91
92/* Chars that can be used to separate mant from exp in floating point nums */
93const char md_EXP_CHARS[] = "eE";
94
95/* Chars that mean this number is a floating point constant */
96/* as in 0f123.456 */
97/* or    0H1.234E-12 (see exp chars above) */
98const char md_FLT_CHARS[] = "dDfF";
99
100static int calcop(
101    struct m88k_opcode *format,
102    char *param,
103    struct m88k_insn *insn);
104static char * parse_reg(
105    char *param,
106    struct m88k_insn *insn,
107    struct m88k_opcode *format,
108    int parcnt);
109#ifdef m88110
110static char *parse_ereg(
111    char *param,
112    struct m88k_insn *insn,
113    struct m88k_opcode *format,
114    int parcnt);
115static char *parse_e4rot(
116    char *param,
117    struct m88k_insn *insn,
118    struct m88k_opcode *format,
119    int parcnt);
120static char *parse_xreg(
121    char *param,
122    struct m88k_insn *insn,
123    struct m88k_opcode *format,
124    int parcnt);
125#endif /* m88110 */
126static char *parse_pcr(
127    char *param,
128    struct m88k_insn *insn,
129    struct m88k_opcode *format,
130    int parcnt);
131static char *parse_cmp(
132    char *param,
133    struct m88k_insn *insn,
134    struct m88k_opcode *format,
135    int parcnt);
136static char *parse_cnd(
137    char *param,
138    struct m88k_insn *insn,
139    struct m88k_opcode *format,
140    int parcnt);
141static char *parse_bf(
142    char *param,
143    struct m88k_insn *insn,
144    struct m88k_opcode *format,
145    int parcnt);
146static char *parse_rot(
147    char *param,
148    struct m88k_insn *insn,
149    struct m88k_opcode *format,
150    int parcnt);
151static char *parse_rsc(
152    char *param,
153    struct m88k_insn *insn,
154    struct m88k_opcode *format,
155    int parcnt);
156static char *parse_cr(
157    char *param,
158    struct m88k_insn *insn,
159    struct m88k_opcode *format,
160    int parcnt);
161static char *parse_fcr(
162    char *param,
163    struct m88k_insn *insn,
164    struct m88k_opcode *format,
165    int parcnt);
166static char *parse_cst(
167    char *param,
168    struct m88k_insn *insn,
169    struct m88k_opcode *format,
170    int parcnt);
171static char *getval(
172    char *param,
173    unsigned int *val);
174#ifdef NeXT_MOD
175static void s_reg(
176    uintptr_t reg);
177static void s_scaled(
178    uintptr_t value);
179static void s_m88k_abs(
180    uintptr_t value);
181static void s_no_delay(
182    uintptr_t value);
183static void s_dot(
184    uintptr_t value);
185#endif /* NeXT_MOD */
186
187const pseudo_typeS md_pseudo_table[] =
188{
189#ifdef NeXT_MOD
190	{"greg", s_reg, 'r' },
191	{"xreg", s_reg, 'x' },
192	{"scaled", s_scaled, 0},
193	{"abs", s_m88k_abs, 0},
194	{"no_delay", s_no_delay, 0},
195	{"dot", s_dot, 0},
196#endif
197#ifndef NeXT_MOD
198  	/* At NeXT we don't allow these */
199	{"dfloat", float_cons, 'd'},
200	{"ffloat", float_cons, 'f'},
201	{"global", s_globl, 0},
202	{"half", cons, 2 },
203	{"ln", s_line, 0},
204	{"zero", s_space, 0},
205	{"word", cons, 4 },
206#endif
207	{0}
208};
209
210#ifdef NeXT_MOD
211static
212void
213s_dot(
214uintptr_t value)
215{
216	char *name, *end_name, delim;
217	symbolS *symbolP;
218
219	if( * input_line_pointer == '"')
220	  name = input_line_pointer + 1;
221	else
222	  name = input_line_pointer;
223	delim = get_symbol_end();
224	end_name = input_line_pointer;
225	*end_name = 0;
226
227	symbolP = symbol_find_or_make (name);
228	symbolP -> sy_type = N_ABS;
229	symbolP -> sy_other = 0; /* NO_SECT */
230	symbolP -> sy_value = obstack_next_free(&frags) - frag_now->fr_literal;
231	symbolP -> sy_frag = &zero_address_frag;
232
233	*end_name = delim;
234	totally_ignore_line();
235}
236/*
237 * s_reg() is used to implement ".greg symbol,exp" and ".xreg symbol,exp"
238 * which set symbol to 1 or 0 depending on if the expression is a general
239 * register or extended register respectfully.  These are intended for use in
240 * macros.
241 */
242static
243void
244s_reg(
245uintptr_t reg)
246{
247	char *name, *end_name, delim;
248	symbolS *symbolP;
249	uint32_t n_value, val;
250
251	if( * input_line_pointer == '"')
252	  name = input_line_pointer + 1;
253	else
254	  name = input_line_pointer;
255	delim = get_symbol_end();
256	end_name = input_line_pointer;
257	*end_name = delim;
258	SKIP_WHITESPACE();
259	if ( * input_line_pointer != ',' ) {
260		*end_name = 0;
261		as_warn("Expected comma after name \"%s\"", name);
262		*end_name = delim;
263		ignore_rest_of_line();
264		return;
265	}
266	input_line_pointer ++;
267	*end_name = 0;
268
269	SKIP_WHITESPACE();
270	n_value = 0;
271	if (*input_line_pointer == reg || *input_line_pointer == toupper(reg)){
272	    input_line_pointer++;
273	    if(isdigit(*input_line_pointer)){
274		val = 0;
275		while (isdigit(*input_line_pointer)){
276		    if ((val = val * 10 + *input_line_pointer++ - '0') > 31)
277			break;
278		}
279		SKIP_WHITESPACE();
280		if(val <= 31 &&
281		   (*input_line_pointer == '\n' || *input_line_pointer == '@'))
282		    n_value = 1;
283	    }
284	}
285
286	symbolP = symbol_find_or_make (name);
287	symbolP -> sy_type = N_ABS;
288	symbolP -> sy_other = 0; /* NO_SECT */
289	symbolP -> sy_value = n_value;
290	symbolP -> sy_frag = &zero_address_frag;
291
292	*end_name = delim;
293	totally_ignore_line();
294}
295
296/*
297 * s_scaled() is used to implement ".scaled symbol,exp" which sets symbol to 1
298 * or 0 depending on if the expression is a scaled general register expression
299 * "r1[r2]" or not respectfully.  This is intended for use in macros.
300 */
301static
302void
303s_scaled(
304uintptr_t value)
305{
306	char *name, *end_name, delim;
307	symbolS *symbolP;
308	uint32_t n_value, val;
309
310	if( * input_line_pointer == '"')
311	  name = input_line_pointer + 1;
312	else
313	  name = input_line_pointer;
314	delim = get_symbol_end();
315	end_name = input_line_pointer;
316	*end_name = delim;
317	SKIP_WHITESPACE();
318	if ( * input_line_pointer != ',' ) {
319		*end_name = 0;
320		as_warn("Expected comma after name \"%s\"", name);
321		*end_name = delim;
322		ignore_rest_of_line();
323		return;
324	}
325	input_line_pointer ++;
326	*end_name = 0;
327
328	SKIP_WHITESPACE();
329	n_value = 0;
330	if (*input_line_pointer == 'r' || *input_line_pointer == 'R'){
331	    input_line_pointer++;
332	    if(isdigit(*input_line_pointer)){
333		val = 0;
334		while (isdigit(*input_line_pointer)){
335		    if ((val = val * 10 + *input_line_pointer++ - '0') > 31)
336			break;
337		}
338		SKIP_WHITESPACE();
339		if(val <= 31 && *input_line_pointer == '['){
340		    input_line_pointer++;
341		    if (*input_line_pointer == 'r' ||
342			*input_line_pointer == 'R'){
343			input_line_pointer++;
344			if(isdigit(*input_line_pointer)){
345			    val = 0;
346			    while (isdigit(*input_line_pointer)){
347				if ((val = val * 10 +
348					   *input_line_pointer++ - '0') > 31)
349				    break;
350			    }
351			    if(val <= 31 && *input_line_pointer == ']'){
352				input_line_pointer++;
353				SKIP_WHITESPACE();
354				if(*input_line_pointer == '\n' ||
355				   *input_line_pointer == '@')
356		    		    n_value = 1;
357			    }
358			}
359		    }
360		}
361	    }
362	}
363
364	symbolP = symbol_find_or_make (name);
365	symbolP -> sy_type = N_ABS;
366	symbolP -> sy_other = 0; /* NO_SECT */
367	symbolP -> sy_value = n_value;
368	symbolP -> sy_frag = & zero_address_frag;
369
370	*end_name = delim;
371	totally_ignore_line();
372}
373
374/*
375 * s_m88k_abs() is used to implement ".abs symbol,exp" which sets symbol to 1
376 * or 0 depending on if the expression is an absolute expression or not
377 * respectfully.  This is intended for use in macros.
378 */
379static
380void
381s_m88k_abs(
382uintptr_t value)
383{
384	char *name, *end_name, delim, *start;
385	symbolS *symbolP;
386	uint32_t n_value, val, is_reg_exp;
387
388	start = input_line_pointer;
389	if( * input_line_pointer == '"')
390	  name = input_line_pointer + 1;
391	else
392	  name = input_line_pointer;
393	delim = get_symbol_end();
394	end_name = input_line_pointer;
395	*end_name = delim;
396	SKIP_WHITESPACE();
397	if ( * input_line_pointer != ',' ) {
398		*end_name = 0;
399		as_warn("Expected comma after name \"%s\"", name);
400		*end_name = delim;
401		ignore_rest_of_line();
402		return;
403	}
404	input_line_pointer ++;
405	*end_name = 0;
406
407	SKIP_WHITESPACE();
408	is_reg_exp = 0;
409	n_value = 0;
410	if(*input_line_pointer == 'r' || *input_line_pointer == 'R'){
411	    input_line_pointer++;
412	    if(isdigit(*input_line_pointer)){
413		val = 0;
414		while (isdigit(*input_line_pointer)){
415		    if ((val = val * 10 + *input_line_pointer++ - '0') > 31)
416			break;
417		}
418		SKIP_WHITESPACE();
419		if(val <= 31)
420		    is_reg_exp = 1;
421	    }
422	}
423	if(is_reg_exp == 0){
424	    *end_name = delim;
425	    input_line_pointer = start;
426	    s_abs(value);
427	    return;
428	}
429
430	symbolP = symbol_find_or_make (name);
431	symbolP -> sy_type = N_ABS;
432	symbolP -> sy_other = 0; /* NO_SECT */
433	symbolP -> sy_value = n_value;
434	symbolP -> sy_frag = & zero_address_frag;
435	*end_name = delim;
436
437	totally_ignore_line();
438}
439
440/*
441 * s_no_delay() is used to implement ".no_delay string" which will abort and
442 * print the string if the last instruction assembled has a delay slot.
443 * This is intended for use in macros that expand to more than one instruction
444 * that could be put in delay slots.  This is not really correct in it's
445 * operation in that it is not per-section and does not take into account
446 * anything other than assembled instructions.
447 */
448static
449void
450s_no_delay(
451uintptr_t value)
452{
453	char *p, c;
454
455	p = input_line_pointer;
456	while(*p != '\n' && *p != '@' && *p != '\0')
457	    p++;
458	c = *p;
459	*p = '\0';
460
461	if(in_delay_slot)
462	    as_fatal("delay slot abort %s detected.  Assembly stopping.",
463		     input_line_pointer);
464	input_line_pointer = p;
465	*p = c;
466}
467#endif /* NeXT_MOD */
468
469void
470md_begin(
471void)
472{
473	const char *retval = NULL;
474	register unsigned int i = 0;
475
476	/* initialize hash table */
477
478	op_hash = hash_new();
479	if (op_hash == NULL)
480		as_fatal("Could not initialize hash table");
481
482	/* loop until you see the end of the list */
483
484	while (*m88k_opcodes[i].name) {
485		char *name = m88k_opcodes[i].name;
486
487		/* hash each mnemonic and record its position */
488
489		retval = hash_insert(op_hash, name, (char *)&m88k_opcodes[i]);
490
491		if (retval != NULL && *retval != '\0')
492			as_fatal("Can't hash instruction '%s':%s",
493					m88k_opcodes[i].name, retval);
494
495		/* skip to next unique mnemonic or end of list */
496
497		for (i++; !strcmp(m88k_opcodes[i].name, name); i++);
498	}
499}
500
501int
502md_parse_option(
503char **argP,
504int *cntP,
505char ***vecP)
506{
507	return (1);
508}
509
510void
511md_assemble(
512char *op)
513{
514	char *param, *thisfrag;
515	struct m88k_opcode *format;
516	struct m88k_insn insn;
517#ifdef NeXT_MOD
518	int32_t pcrel_reloc;
519#endif
520
521	assert(op);
522
523	/* skip over instruction to find parameters */
524
525	/* *param != '\0' is need for instructions that have no parameters
526	   like rte */
527	for (param = op; !isspace(*param) && *param != '\0' ; param++);
528	*param++ = '\0';
529
530	/* try to find the instruction in the hash table */
531
532	if ((format = (struct m88k_opcode *) hash_find(op_hash, op)) == NULL) {
533		as_warn("Invalid mnemonic '%s'", op);
534		return;
535	}
536
537	/* try parsing this instruction into insn */
538
539	while (!calcop(format,param,&insn))
540
541		/* if it doesn't parse try the next instruction */
542
543		if (!strcmp(format->name, format[1].name))
544			format++;
545		else {
546			as_warn("Parameter syntax error");
547			return;
548		}
549
550	/* grow the current frag and plop in the opcode */
551
552	thisfrag = frag_more(4);
553	md_number_to_chars(thisfrag, insn.opcode, 4);
554#ifdef NeXT_MOD
555	in_delay_slot = format->delay_slot;
556#endif
557#ifdef NeXT_MOD	/* generate stabs for debugging assembly code */
558	/*
559	 * If the -g flag is present generate a line number stab for the
560	 * instruction.
561	 *
562	 * See the detailed comments about stabs in read_a_source_file() for a
563	 * description of what is going on here.
564	 */
565	if(flagseen['g'] && frchain_now->frch_nsect == text_nsect){
566	    (void)symbol_new(
567		  "",
568		  68 /* N_SLINE */,
569		  text_nsect,
570		  logical_input_line /* n_desc, line number */,
571		  obstack_next_free(&frags) - frag_now->fr_literal,
572		  frag_now);
573	}
574#endif /* NeXT_MOD */
575
576#ifdef NeXT_MOD	/* mark sections containing instructions */
577	/*
578	 * We are putting a machine instruction in this section so mark it as
579	 * containg some machine instructions.
580	 */
581	frchain_now->frch_section.flags |= S_ATTR_SOME_INSTRUCTIONS;
582#endif /* NeXT_MOD */
583
584#ifdef NeXT_MOD
585	pcrel_reloc = 0;
586	if (insn.reloc == M88K_RELOC_PC16 || insn.reloc == M88K_RELOC_PC26){
587	    /*
588	     * The NeXT linker has the ability to scatter blocks of
589	     * sections between labels.  This requires that brances to
590	     * labels that survive to the link phase must be able to
591	     * be relocated.
592	     */
593	    if(insn.exp.X_add_symbol != NULL &&
594	       (insn.exp.X_add_symbol->sy_name[0] != 'L' || flagseen ['L']))
595		pcrel_reloc = 1;
596	    else
597		pcrel_reloc = 0;
598	}
599#endif /* NeXT_MOD */
600
601	/* if this instruction requires labels mark it for later */
602	switch (insn.reloc) {
603		case M88K_RELOC_LO16:
604		case M88K_RELOC_HI16:
605				fix_new(
606					frag_now,
607#ifdef NeXT_MOD
608					thisfrag - frag_now->fr_literal,
609					4,
610#else
611					thisfrag - frag_now->fr_literal + 2,
612					2,
613#endif
614					insn.exp.X_add_symbol,
615					insn.exp.X_subtract_symbol,
616					insn.exp.X_add_number,
617					0, 0,
618					insn.reloc
619				);
620				break;
621
622#ifndef NeXT_MOD
623		case M88K_RELOC_IW16:
624				fix_new(
625					frag_now,
626					thisfrag - frag_now->fr_literal,
627					4,
628					insn.exp.X_add_symbol,
629					insn.exp.X_subtract_symbol,
630					insn.exp.X_add_number,
631					0, 0,
632					insn.reloc
633				);
634				break;
635#endif /* !defined(NeXT_MOD) */
636
637		case M88K_RELOC_PC16:
638				fix_new(
639					frag_now,
640#ifdef NeXT_MOD
641					thisfrag - frag_now->fr_literal,
642					4,
643#else
644					thisfrag - frag_now->fr_literal + 2,
645					2,
646#endif
647					insn.exp.X_add_symbol,
648					insn.exp.X_subtract_symbol,
649					insn.exp.X_add_number,
650					1, pcrel_reloc,
651					insn.reloc
652				);
653				break;
654
655		case M88K_RELOC_PC26:
656				fix_new(
657					frag_now,
658					thisfrag - frag_now->fr_literal,
659					4,
660					insn.exp.X_add_symbol,
661					insn.exp.X_subtract_symbol,
662					insn.exp.X_add_number,
663					1, pcrel_reloc,
664					insn.reloc
665				);
666				break;
667
668		default:
669				as_warn("Unknown relocation type");
670				break;
671	}
672}
673
674static
675int
676calcop(
677struct m88k_opcode *format,
678char *param,
679struct m88k_insn *insn)
680{
681	int parcnt;
682
683	/* initial the passed structure */
684
685	memset(insn, '\0', sizeof(*insn));
686	insn->reloc = NO_RELOC;
687	insn->opcode = format->opcode;
688
689	/* parse all parameters */
690
691	for (parcnt=0; parcnt<3 && format->op[parcnt].type != NIL; parcnt++) {
692
693		switch (format->op[parcnt].type) {
694
695			case CNST:
696				param = parse_cst(param, insn, format, parcnt);
697				break;
698
699			case REG:
700				param = parse_reg(param, insn, format, parcnt);
701				break;
702#ifdef m88110
703			case EREG:
704				param = parse_ereg(param, insn, format, parcnt);
705				break;
706
707			case E4ROT:
708				param = parse_e4rot(param, insn, format,parcnt);
709				break;
710
711			case XREG:
712				param = parse_xreg(param, insn, format, parcnt);
713				break;
714#endif /* m88110 */
715			case BF:
716				param = parse_bf(param, insn, format, parcnt);
717				break;
718
719			case ROT:
720				param = parse_rot(param, insn, format, parcnt);
721				break;
722
723			case REGSC:
724				param = parse_rsc(param, insn, format, parcnt);
725				break;
726
727			case CRREG:
728				param = parse_cr(param, insn, format, parcnt);
729				break;
730
731			case FCRREG:
732				param = parse_fcr(param, insn, format, parcnt);
733				break;
734
735			case PCREL:
736				param = parse_pcr(param, insn, format, parcnt);
737				break;
738
739			case CONDMASK:
740				param = parse_cnd(param, insn, format, parcnt);
741				break;
742
743			case CMPRSLT:
744				param = parse_cmp(param, insn, format, parcnt);
745				break;
746
747			default:
748				as_fatal("Unknown parameter type");
749		}
750
751		/* see if parser failed or not */
752
753		if (param == NULL)
754			return 0;
755	}
756
757	return 1;
758}
759
760static
761char *
762parse_pcr(
763char *param,
764struct m88k_insn *insn,
765struct m88k_opcode *format,
766int parcnt)
767{
768	char *saveptr, *saveparam;
769	segT seg;
770
771	saveptr = input_line_pointer;
772	input_line_pointer = param;
773
774	seg = expression(&insn->exp);
775
776	saveparam = input_line_pointer;
777	input_line_pointer = saveptr;
778
779	switch (format->op[parcnt].width) {
780
781		case 16: insn->reloc = M88K_RELOC_PC16;
782			 break;
783
784		case 26: insn->reloc = M88K_RELOC_PC26;
785			 break;
786
787		default: as_warn("Strange PC relative width %d",
788						format->op[parcnt].width);
789			 break;
790	}
791
792	return saveparam;
793}
794
795static
796char *
797parse_reg(
798char *param,
799struct m88k_insn *insn,
800struct m88k_opcode *format,
801int parcnt)
802{
803	unsigned int val = 0;
804
805	if (*param != 'r' && *param != 'R')
806		return NULL;
807
808	param++;
809
810	if (!isdigit(*param))
811		return NULL;
812
813	while (isdigit(*param))
814		if ((val = val * 10 + *param++ - '0') > 31)
815			return NULL;
816
817	insn->opcode |= val << format->op[parcnt].offset;
818
819	switch (*param) {
820
821		case '\0' :
822			if (parcnt == 2 || format->op[parcnt+1].type == NIL)
823				return param;
824			else
825				return NULL;
826
827		case '['  :
828			if (parcnt != 2 && format->op[parcnt+1].type == REGSC)
829				return param+1;
830			else
831				return NULL;
832
833		case ','  :
834			if (parcnt != 2 && format->op[parcnt+1].type != NIL)
835				return param+1;
836			else
837				return NULL;
838	}
839
840	return NULL;
841}
842
843#ifdef m88110
844static
845char *
846parse_ereg(
847char *param,
848struct m88k_insn *insn,
849struct m88k_opcode *format,
850int parcnt)
851{
852	unsigned int val = 0;
853
854	if (*param != 'r' && *param != 'R')
855		return NULL;
856
857	param++;
858
859	if (!isdigit(*param))
860		return NULL;
861
862	while (isdigit(*param))
863		if ((val = val * 10 + *param++ - '0') > 31)
864			return NULL;
865
866	if((val & 0x1) != 0)
867		return NULL;
868
869	insn->opcode |= val << format->op[parcnt].offset;
870
871	switch (*param) {
872
873		case '\0' :
874			if (parcnt == 2 || format->op[parcnt+1].type == NIL)
875				return param;
876			else
877				return NULL;
878
879		case '['  :
880			if (parcnt != 2 && format->op[parcnt+1].type == REGSC)
881				return param+1;
882			else
883				return NULL;
884
885		case ','  :
886			if (parcnt != 2 && format->op[parcnt+1].type != NIL)
887				return param+1;
888			else
889				return NULL;
890	}
891
892	return NULL;
893}
894
895static
896char *
897parse_e4rot(
898char *param,
899struct m88k_insn *insn,
900struct m88k_opcode *format,
901int parcnt)
902{
903	int val;
904	char *saveptr, save_c, *offset_ptr;
905        expressionS exp;
906	segT seg;
907
908	/* Now step over the '<' and look for the offset expression before a
909	   '>' and the end of line (which is a '\0' when we get here).  We
910	   know there is a '\0' where the end of line was because that is
911	   what parse_a_buffer() in read.c does before calling md_assemble */
912	if (*param++ != '<')
913		return NULL;
914	offset_ptr = param;
915	while(*param != '\0')
916		param++;
917	if(param == offset_ptr || param[-1] != '>')
918		return NULL;
919	param--;
920	save_c = *param;
921	*param = '\0';
922	saveptr = input_line_pointer;
923	input_line_pointer = offset_ptr;
924	seg = expression(&exp);
925	*param = save_c;
926	input_line_pointer = saveptr;
927	val = exp.X_add_number;
928	if(seg != SEG_ABSOLUTE || val > 60 || (val & 0x3) != 0)
929		return NULL;
930
931	val >>= 2;
932	insn->opcode |= val << format->op[parcnt].offset;
933
934	return param+1;
935}
936
937static
938char *
939parse_xreg(
940char *param,
941struct m88k_insn *insn,
942struct m88k_opcode *format,
943int parcnt)
944{
945	unsigned int val = 0;
946
947	if (*param != 'x' && *param != 'X')
948		return NULL;
949
950	param++;
951
952	if (!isdigit(*param))
953		return NULL;
954
955	while (isdigit(*param))
956		if ((val = val * 10 + *param++ - '0') > 31)
957			return NULL;
958
959	insn->opcode |= val << format->op[parcnt].offset;
960
961	switch (*param) {
962
963		case '\0' :
964			if (parcnt == 2 || format->op[parcnt+1].type == NIL)
965				return param;
966			else
967				return NULL;
968
969		case '['  :
970			if (parcnt != 2 && format->op[parcnt+1].type == REGSC)
971				return param+1;
972			else
973				return NULL;
974
975		case ','  :
976			if (parcnt != 2 && format->op[parcnt+1].type != NIL)
977				return param+1;
978			else
979				return NULL;
980	}
981
982	return NULL;
983}
984#endif /* m88110 */
985
986static
987char *
988parse_cmp(
989char *param,
990struct m88k_insn *insn,
991struct m88k_opcode *format,
992int parcnt)
993{
994	int val;
995	char *saveptr, save_c, *offset_ptr, c;
996        expressionS exp;
997	segT seg;
998
999	/* look for the offset expression before a ',' */
1000	c = *param;
1001	if (isdigit(c) || c == '(' || c == '-' || c == '+' || c == '!' ||
1002	    c == '~'){
1003		offset_ptr = param;
1004		while(*param != ',')
1005			param++;
1006		if(param == offset_ptr || *param != ',')
1007			return NULL;
1008		save_c = *param;
1009		*param = '\0';
1010		saveptr = input_line_pointer;
1011		input_line_pointer = offset_ptr;
1012		seg = expression(&exp);
1013		*param = save_c;
1014		input_line_pointer = saveptr;
1015		val = exp.X_add_number;
1016		if(seg != SEG_ABSOLUTE ||
1017		   val > (1 << format->op[parcnt].width) || val < 0)
1018			return NULL;
1019	} else {
1020		if (isupper(*param))
1021			*param = tolower(*param);
1022
1023		if (isupper(*(param+1)))
1024			*(param+1) = tolower(*(param+1));
1025
1026		for (val=0; cmpslot[val] != NULL; val++)
1027			if (!strncmp(param,cmpslot[val],2))
1028				break;
1029
1030		if (cmpslot[val] == NULL)
1031			return NULL;
1032
1033		param += 2;
1034	}
1035
1036	if (*param++ != ',')
1037		return NULL;
1038
1039	insn->opcode |= val << format->op[parcnt].offset;
1040
1041	return param;
1042}
1043
1044static
1045char *
1046parse_cnd(
1047char *param,
1048struct m88k_insn *insn,
1049struct m88k_opcode *format,
1050int parcnt)
1051{
1052	int val;
1053	char *saveptr, save_c, *offset_ptr, c;
1054        expressionS exp;
1055	segT seg;
1056
1057	/* look for the offset expression before a ',' */
1058	c = *param;
1059	if (isdigit(c) || c == '(' || c == '-' || c == '+' || c == '!' ||
1060	    c == '~'){
1061		offset_ptr = param;
1062		while(*param != ',')
1063			param++;
1064		if(param == offset_ptr || *param != ',')
1065			return NULL;
1066		save_c = *param;
1067		*param = '\0';
1068		saveptr = input_line_pointer;
1069		input_line_pointer = offset_ptr;
1070		seg = expression(&exp);
1071		*param = save_c;
1072		input_line_pointer = saveptr;
1073		val = exp.X_add_number;
1074		if(seg != SEG_ABSOLUTE ||
1075		   val > (1 << format->op[parcnt].width) || val < 0)
1076			return NULL;
1077	} else {
1078		if (isupper(*param))
1079			*param = tolower(*param);
1080
1081		if (isupper(*(param+1)))
1082			*(param+1) = tolower(*(param+1));
1083
1084		for (val=0; cndmsk[val].name != NULL; val++)
1085			if (!strncmp(param,cndmsk[val].name,3))
1086				break;
1087
1088		if (cndmsk[val].name == NULL)
1089			return NULL;
1090
1091		val = cndmsk[val].num;
1092
1093		param += 3;
1094	}
1095
1096	if (*param++ != ',')
1097		return NULL;
1098
1099	insn->opcode |= val << format->op[parcnt].offset;
1100
1101	return param;
1102}
1103
1104static
1105char *
1106parse_bf(
1107char *param,
1108struct m88k_insn *insn,
1109struct m88k_opcode *format,
1110int parcnt)
1111{
1112	int val, width;
1113	char *saveptr, save_c, *offset_ptr, c;
1114        expressionS exp;
1115	segT seg;
1116
1117	/* We know there is a '\0' where the end of line was because that is
1118	   what parse_a_buffer() in read.c does before calling md_assemble */
1119
1120	/* First look for the width expression before a '<' */
1121	saveptr = input_line_pointer;
1122	input_line_pointer = param;
1123	while(*param != '<' && *param != '\0')
1124		param++;
1125	if(*param == '\0'){
1126		input_line_pointer = saveptr;
1127		return NULL;
1128	}
1129	save_c = *param;
1130	*param = '\0';
1131	seg = expression(&exp);
1132	*param = save_c;
1133	input_line_pointer = saveptr;
1134	width = exp.X_add_number;
1135	if(seg != SEG_ABSOLUTE || width > 32 || width < 0)
1136		return NULL;
1137
1138	/* Now step over the '<' and look for the offset expression before a
1139	   '>' and the end of line (which is a '\0' when we get here) */
1140	param++;
1141	c = *param;
1142	if (isdigit(c) || c == '(' || c == '-' || c == '+' || c == '!' ||
1143	    c == '~'){
1144		offset_ptr = param;
1145		while(*param != '\0')
1146			param++;
1147		if(param != offset_ptr && param[-1] != '>')
1148			return NULL;
1149		param--;
1150		save_c = *param;
1151		*param = '\0';
1152		saveptr = input_line_pointer;
1153		input_line_pointer = offset_ptr;
1154		seg = expression(&exp);
1155		*param = save_c;
1156		input_line_pointer = saveptr;
1157		val = exp.X_add_number;
1158		if(seg != SEG_ABSOLUTE || val > 32 || val < 0)
1159			return NULL;
1160	}
1161	else {
1162		if (isupper(*param))
1163			*param = tolower(*param);
1164
1165		if (isupper(*(param+1)))
1166			*(param+1) = tolower(*(param+1));
1167
1168		for (val=0; cmpslot[val] != NULL; val++)
1169			if (!strncmp(param,cmpslot[val],2))
1170				break;
1171
1172		if (cmpslot[val] == NULL)
1173			return NULL;
1174
1175		param += 2;
1176	}
1177	if (*param != '>')
1178		return NULL;
1179	insn->opcode |= width << 5;
1180	insn->opcode |= val;
1181
1182	return param+1;
1183}
1184
1185static
1186char *
1187parse_rot(
1188char *param,
1189struct m88k_insn *insn,
1190struct m88k_opcode *format,
1191int parcnt)
1192{
1193	int val;
1194	char *saveptr, save_c, *offset_ptr;
1195        expressionS exp;
1196	segT seg;
1197
1198	/* Now step over the '<' and look for the offset expression before a
1199	   '>' and the end of line (which is a '\0' when we get here).  We
1200	   know there is a '\0' where the end of line was because that is
1201	   what parse_a_buffer() in read.c does before calling md_assemble */
1202	if (*param++ != '<')
1203		return NULL;
1204	offset_ptr = param;
1205	while(*param != '\0')
1206		param++;
1207	if(param != offset_ptr && param[-1] != '>')
1208		return NULL;
1209	param--;
1210	save_c = *param;
1211	*param = '\0';
1212	saveptr = input_line_pointer;
1213	input_line_pointer = offset_ptr;
1214	seg = expression(&exp);
1215	*param = save_c;
1216	input_line_pointer = saveptr;
1217	val = exp.X_add_number;
1218	if(seg != SEG_ABSOLUTE && (val > 32 || val < 0))
1219		return NULL;
1220
1221	insn->opcode |= val;
1222
1223	return param+1;
1224}
1225
1226static
1227char *
1228parse_rsc(
1229char *param,
1230struct m88k_insn *insn,
1231struct m88k_opcode *format,
1232int parcnt)
1233{
1234	unsigned int val = 0;
1235
1236	if (*param != 'r' && *param != 'R')
1237		return NULL;
1238
1239	param++;
1240
1241	if (!isdigit(*param))
1242		return NULL;
1243
1244	while (isdigit(*param))
1245		if ((val = val * 10 + *param++ - '0') > 31)
1246			return NULL;
1247
1248	insn->opcode |= val << format->op[parcnt].offset;
1249
1250	if (*param != ']' || *(param+1) != '\0')
1251		return NULL;
1252
1253	return param+1;
1254}
1255
1256static
1257char *
1258parse_cr(
1259char *param,
1260struct m88k_insn *insn,
1261struct m88k_opcode *format,
1262int parcnt)
1263{
1264	unsigned int val = 0;
1265
1266	if (strncmp(param, "cr", 2))
1267		return NULL;
1268
1269	param += 2;
1270
1271	if (!isdigit(*param))
1272		return NULL;
1273
1274	while (isdigit(*param))
1275		if ((val = val * 10 + *param++ - '0') > 63)
1276			return NULL;
1277
1278	/*
1279	 * the following fix is not as generic as I'd like, but the
1280	 * hardware is real picky about this.	- bowen@cs.buffalo.edu
1281	 * This fix is to make sure the S1 and S2 fields are the same.
1282	 */
1283	insn->opcode |= (insn->opcode & 0x001f0000) >> 16;
1284
1285	insn->opcode |= val << format->op[parcnt].offset;
1286
1287	if (*param != '\0')
1288		return NULL;
1289
1290	return param;
1291}
1292
1293static
1294char *
1295parse_fcr(
1296char *param,
1297struct m88k_insn *insn,
1298struct m88k_opcode *format,
1299int parcnt)
1300{
1301	unsigned int val = 0;
1302
1303	if (strncmp(param, "fcr", 3))
1304		return NULL;
1305
1306	param += 3;
1307
1308	if (!isdigit(*param))
1309		return NULL;
1310
1311	while (isdigit(*param))
1312		if ((val = val * 10 + *param++ - '0') > 63)
1313			return NULL;
1314
1315	/*
1316	 * This is to make sure the S1 and S2 fields are the same.
1317	 */
1318	insn->opcode |= (insn->opcode & 0x001f0000) >> 16;
1319
1320	insn->opcode |= val << format->op[parcnt].offset;
1321
1322	if (*param != '\0')
1323		return NULL;
1324
1325	return param;
1326}
1327
1328static
1329char *
1330parse_cst(
1331char *param,
1332struct m88k_insn *insn,
1333struct m88k_opcode *format,
1334int parcnt)
1335{
1336	char c, *saveptr, *saveparam;
1337	int val, nohilo = 0;
1338	segT seg;
1339        expressionS exp;
1340
1341	c = *param;
1342	if (isdigit(c) || c == '(' || c == '-' || c == '+' || c == '!' ||
1343	    c == '~'){
1344		saveptr = input_line_pointer;
1345		input_line_pointer = param;
1346		while(*param != '\0')
1347			param++;
1348		seg = expression(&exp);
1349		input_line_pointer = saveptr;
1350		val = exp.X_add_number;
1351		if(seg != SEG_ABSOLUTE || val > (1 << format->op[parcnt].width))
1352			return NULL;
1353	}
1354	else if (!strncmp(param,"hi16(",5))
1355
1356		if (isdigit(*(param+5))) {
1357			param = getval(param+5, (unsigned int *)&val);
1358			val = (val & 0xffff0000) >> 16;
1359			if (*param++ != ')')
1360				return NULL;
1361
1362		} else
1363			insn->reloc = M88K_RELOC_HI16;
1364	else if (!strncmp(param,"lo16(",5))
1365
1366		if (isdigit(*(param+5))) {
1367			param = getval(param+5, (unsigned int *)&val);
1368			val &= 0x0000ffff;
1369			if (*param++ != ')')
1370				return NULL;
1371
1372		} else
1373			insn->reloc = M88K_RELOC_LO16;
1374
1375#ifndef NeXT_MOD
1376	else if (!strncmp(param,"iw16(",5))
1377
1378		if (isdigit(*(param+5))) {
1379			param = getval(param+5,&val);
1380			val &= 0x0000ffff;
1381			if (*param++ != ')')
1382				return NULL;
1383
1384		} else
1385			insn->reloc = M88K_RELOC_IW16;
1386#endif /* !defined(NeXT_MOD) */
1387
1388	else if (*param == 'r' && isdigit(*(param+1)))
1389
1390		return NULL;
1391
1392	else {
1393		insn->reloc = M88K_RELOC_LO16;
1394		nohilo = 1;
1395	}
1396
1397	if (insn->reloc != NO_RELOC) {
1398
1399		saveptr = input_line_pointer;
1400		input_line_pointer = param + (nohilo ? 0 : 5);
1401
1402		seg = expression(&insn->exp);
1403
1404		saveparam = input_line_pointer;
1405		input_line_pointer = saveptr;
1406
1407		if (nohilo) {
1408
1409			if (*saveparam != '\0')
1410				return NULL;
1411
1412			return saveparam;
1413		}
1414
1415		if (*saveparam != ')')
1416			return NULL;
1417
1418		return saveparam+1;
1419	}
1420
1421	if ((1 << format->op[parcnt].width) <= val)
1422		return NULL;
1423
1424	insn->opcode |= val << format->op[parcnt].offset;
1425
1426	if (*param != '\0')
1427		return NULL;
1428
1429	return param;
1430}
1431
1432#define isoct(z) (z >= '0' && z <= '7')
1433#define ishex(z) ((z >= '0' && z <= '9') || (z >= 'a' && z <= 'f') || (z >= 'A' && z <= 'F'))
1434#define hexval(z) \
1435  (isdigit(z) ? (z) - '0' :		\
1436   islower(z) ? (z) - 'a' + 10 :	\
1437   (z) - 'A' + 10)
1438
1439static
1440char *
1441getval(
1442char *param,
1443unsigned int *val)
1444{
1445	*val = 0;
1446
1447	if (*param == '0' && (*(param+1) == 'x' || *(param+1) == 'X'))
1448
1449		for (param += 2; ishex(*param); param++)
1450
1451			if (*val > 0x0fffffff)
1452				return param;
1453			else
1454				*val = *val * 16 + hexval(*param);
1455
1456	else if (*param == '0')
1457
1458		for (param++; isoct(*param); param++)
1459
1460			if (*val > 0x1fffffff)
1461				return param;
1462			else
1463				*val = *val * 8 + *param - '0';
1464
1465	else
1466
1467		for (; isdigit(*param); param++)
1468
1469			*val = *val * 10 + *param - '0';
1470
1471	return param;
1472}
1473
1474void
1475md_number_to_chars(
1476char *buf,
1477signed_expr_t val,
1478int nbytes)
1479{
1480	switch(nbytes) {
1481
1482		case 4:
1483			*buf++ = val >> 24;
1484			*buf++ = val >> 16;
1485		case 2:
1486			*buf++ = val >> 8;
1487		case 1:
1488			*buf = val;
1489			break;
1490
1491		default:
1492			abort();
1493	}
1494}
1495
1496void
1497md_number_to_imm(
1498unsigned char *buf,
1499signed_expr_t val,
1500int nbytes,
1501fixS *fixP,
1502int nsect)
1503{
1504	if(fixP->fx_r_type == NO_RELOC ||
1505	   fixP->fx_r_type == M88K_RELOC_VANILLA) {
1506		switch (nbytes) {
1507			case 4:
1508				*buf++ = val >> 24;
1509				*buf++ = val >> 16;
1510			case 2:
1511				*buf++ = val >> 8;
1512			case 1:
1513				*buf = val;
1514				break;
1515
1516			default:
1517				abort();
1518		}
1519		return;
1520	}
1521
1522	switch (fixP->fx_r_type) {
1523#ifdef NeXT_MOD
1524			case M88K_RELOC_LO16:
1525				buf[2] = val >> 8;
1526				buf[3] = val;
1527				break;
1528			case M88K_RELOC_HI16:
1529				buf[2] = val >> 24;
1530				buf[3] = val >> 16;
1531				break;
1532
1533			case M88K_RELOC_PC16:
1534				val += 4;
1535				buf[2] = val >> 10;
1536				buf[3] = val >> 2;
1537				break;
1538
1539			case M88K_RELOC_PC26:
1540				val += 4;
1541				buf[0] |= (val >> 26) & 0x03;
1542				buf[1] = val >> 18;
1543				buf[2] = val >> 10;
1544				buf[3] = val >> 2;
1545				break;
1546#else /* !defined NeXT_MOD */
1547			case M88K_RELOC_LO16:
1548				buf[0] = val >> 8;
1549				buf[1] = val;
1550				break;
1551
1552			case M88K_RELOC_IW16:
1553				buf[2] = val >> 8;
1554				buf[3] = val;
1555				break;
1556
1557			case M88K_RELOC_HI16:
1558				buf[0] = val >> 24;
1559				buf[1] = val >> 16;
1560				break;
1561
1562			case M88K_RELOC_PC16:
1563				val += 4;
1564				buf[0] = val >> 10;
1565				buf[1] = val >> 2;
1566				break;
1567
1568			case M88K_RELOC_PC26:
1569				val += 4;
1570				buf[0] |= (val >> 26) & 0x03;
1571				buf[1] = val >> 18;
1572				buf[2] = val >> 10;
1573				buf[3] = val >> 2;
1574				break;
1575
1576			case M88K_RELOC_32:
1577				buf[0] = val >> 24;
1578				buf[1] = val >> 16;
1579				buf[2] = val >> 8;
1580				buf[3] = val;
1581				break;
1582#endif /* !defined(NeXT_MOD) */
1583
1584			default:
1585				as_warn("Bad relocation type");
1586				break;
1587	}
1588}
1589
1590#define MAX_LITTLENUMS 6
1591
1592/* Turn a string in input_line_pointer into a floating point constant of type
1593   type, and store the appropriate bytes in *litP.  The number of LITTLENUMS
1594   emitted is stored in *sizeP .  An error message is returned, or NULL on OK.
1595 */
1596char *
1597md_atof(
1598int type,
1599char *litP,
1600int *sizeP)
1601{
1602	int	prec;
1603	LITTLENUM_TYPE words[MAX_LITTLENUMS];
1604	LITTLENUM_TYPE *wordP;
1605	char	*t;
1606	char	*atof_ieee();
1607
1608	switch(type) {
1609	case 'f':
1610	case 'F':
1611	case 's':
1612	case 'S':
1613		prec = 2;
1614		break;
1615
1616	case 'd':
1617	case 'D':
1618	case 'r':
1619	case 'R':
1620		prec = 4;
1621		break;
1622
1623	case 'x':
1624	case 'X':
1625		prec = 6;
1626		break;
1627
1628	case 'p':
1629	case 'P':
1630		prec = 6;
1631		break;
1632
1633	default:
1634		*sizeP=0;
1635		return "Bad call to MD_ATOF()";
1636	}
1637	t=atof_ieee(input_line_pointer,type,words);
1638	if(t)
1639		input_line_pointer=t;
1640
1641	*sizeP=prec * sizeof(LITTLENUM_TYPE);
1642	for(wordP=words;prec--;) {
1643		md_number_to_chars(litP,(int32_t)(*wordP++),sizeof(LITTLENUM_TYPE));
1644		litP+=sizeof(LITTLENUM_TYPE);
1645	}
1646	return "";	/* Someone should teach Dean about null pointers */
1647}
1648
1649const relax_typeS md_relax_table[] = { {0} };
1650
1651int
1652md_estimate_size_before_relax(
1653fragS *fragP,
1654int segment_type)
1655{
1656	as_fatal("internal error: Relaxation should never occur");
1657	return(0);
1658}
1659
1660void
1661md_convert_frag(
1662fragS *fragP)
1663{
1664	as_fatal("internal error: Relaxation should never occur");
1665}
1666
1667void
1668md_end(
1669void)
1670{
1671}
1672