home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
OpenStep 4.2J (Developer)
/
os42jdev.iso
/
NextDeveloper
/
Source
/
GNU
/
cctools
/
as
/
m88k.c
< prev
next >
Wrap
C/C++ Source or Header
|
1997-01-24
|
34KB
|
1,675 lines
/* m88k.c -- Assemble for the 88100
Copyright (C) 1989 Free Software Foundation, Inc.
This file is not yet part of GAS, the GNU Assembler.
GAS is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 1, or (at your option)
any later version.
GAS is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GAS; see the file COPYING. If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
#include <ctype.h>
#include <string.h>
#include <mach-o/m88k/reloc.h>
#include "m88k-opcode.h"
#include "as.h"
#include "flonum.h"
#include "expr.h"
#include "hash.h"
#include "frags.h"
#include "fixes.h"
#include "read.h"
#include "md.h"
#include "obstack.h"
#include "symbols.h"
#include "messages.h"
#include "input-scrub.h"
#include "sections.h"
/*
* These are the default cputype and cpusubtype for the m88k architecture.
*/
const cpu_type_t md_cputype = CPU_TYPE_MC88000;
cpu_subtype_t md_cpusubtype = CPU_SUBTYPE_MC88000_ALL;
/* This is the byte sex for the m88k architecture */
const enum byte_sex md_target_byte_sex = BIG_ENDIAN_BYTE_SEX;
#ifdef NeXT
static long in_delay_slot = 0;
#endif
static char *cmpslot[] = { "**", "**", "eq", "ne", "gt", "le", "lt", "ge",
"hi", "ls", "lo", "hs",
#ifdef m88110
"be", "nb", "he", "nh",
#endif m88110
NULL };
static struct {
char *name;
unsigned int num;
} cndmsk[] = {
{ "eq0", 0x02},
{ "ne0", 0x0d},
{ "gt0", 0x01},
{ "lt0", 0x0c},
{ "ge0", 0x03},
{ "le0", 0x0e},
{ NULL, 0x00},
};
struct m88k_insn {
unsigned long opcode;
expressionS exp;
#ifdef NeXT
enum reloc_type_m88k reloc;
#else
enum reloc_type reloc;
#endif
};
static struct hash_control *op_hash = NULL;
/* These chars start a comment anywhere in a source file (except inside
another comment */
const char md_comment_chars[] = ";";
/* These chars only start a comment at the beginning of a line. */
const char md_line_comment_chars[] = "#";
/* Chars that can be used to separate mant from exp in floating point nums */
const char md_EXP_CHARS[] = "eE";
/* Chars that mean this number is a floating point constant */
/* as in 0f123.456 */
/* or 0H1.234E-12 (see exp chars above) */
const char md_FLT_CHARS[] = "dDfF";
static int calcop(
struct m88k_opcode *format,
char *param,
struct m88k_insn *insn);
static char * parse_reg(
char *param,
struct m88k_insn *insn,
struct m88k_opcode *format,
int parcnt);
#ifdef m88110
static char *parse_ereg(
char *param,
struct m88k_insn *insn,
struct m88k_opcode *format,
int parcnt);
static char *parse_e4rot(
char *param,
struct m88k_insn *insn,
struct m88k_opcode *format,
int parcnt);
static char *parse_xreg(
char *param,
struct m88k_insn *insn,
struct m88k_opcode *format,
int parcnt);
#endif m88110
static char *parse_pcr(
char *param,
struct m88k_insn *insn,
struct m88k_opcode *format,
int parcnt);
static char *parse_cmp(
char *param,
struct m88k_insn *insn,
struct m88k_opcode *format,
int parcnt);
static char *parse_cnd(
char *param,
struct m88k_insn *insn,
struct m88k_opcode *format,
int parcnt);
static char *parse_bf(
char *param,
struct m88k_insn *insn,
struct m88k_opcode *format,
int parcnt);
static char *parse_rot(
char *param,
struct m88k_insn *insn,
struct m88k_opcode *format,
int parcnt);
static char *parse_rsc(
char *param,
struct m88k_insn *insn,
struct m88k_opcode *format,
int parcnt);
static char *parse_cr(
char *param,
struct m88k_insn *insn,
struct m88k_opcode *format,
int parcnt);
static char *parse_fcr(
char *param,
struct m88k_insn *insn,
struct m88k_opcode *format,
int parcnt);
static char *parse_cst(
char *param,
struct m88k_insn *insn,
struct m88k_opcode *format,
int parcnt);
static char *getval(
char *param,
unsigned int *val);
#ifdef NeXT
static void s_reg(
int reg);
static void s_scaled(
int value);
static void s_m88k_abs(
int value);
static void s_no_delay(
int value);
static void s_dot(
int value);
#endif /* NeXT */
const pseudo_typeS md_pseudo_table[] =
{
#ifdef NeXT
{"greg", s_reg, 'r' },
{"xreg", s_reg, 'x' },
{"scaled", s_scaled, 0},
{"abs", s_m88k_abs, 0},
{"no_delay", s_no_delay, 0},
{"dot", s_dot, 0},
#endif
#ifndef NeXT
/* At NeXT we don't allow these */
{"dfloat", float_cons, 'd'},
{"ffloat", float_cons, 'f'},
{"global", s_globl, 0},
{"half", cons, 2 },
{"ln", s_line, 0},
{"zero", s_space, 0},
{"word", cons, 4 },
#endif
{0}
};
#ifdef NeXT
static
void
s_dot(
int value)
{
char *name, *end_name, delim;
symbolS *symbolP;
if( * input_line_pointer == '"')
name = input_line_pointer + 1;
else
name = input_line_pointer;
delim = get_symbol_end();
end_name = input_line_pointer;
*end_name = 0;
symbolP = symbol_find_or_make (name);
symbolP -> sy_type = N_ABS;
symbolP -> sy_other = 0; /* NO_SECT */
symbolP -> sy_value = obstack_next_free(&frags) - frag_now->fr_literal;
symbolP -> sy_frag = &zero_address_frag;
*end_name = delim;
totally_ignore_line();
}
/*
* s_reg() is used to implement ".greg symbol,exp" and ".xreg symbol,exp"
* which set symbol to 1 or 0 depending on if the expression is a general
* register or extended register respectfully. These are intended for use in
* macros.
*/
static
void
s_reg(
int reg)
{
char *name, *end_name, delim;
symbolS *symbolP;
unsigned long n_value, val;
if( * input_line_pointer == '"')
name = input_line_pointer + 1;
else
name = input_line_pointer;
delim = get_symbol_end();
end_name = input_line_pointer;
*end_name = delim;
SKIP_WHITESPACE();
if ( * input_line_pointer != ',' ) {
*end_name = 0;
as_warn("Expected comma after name \"%s\"", name);
*end_name = delim;
ignore_rest_of_line();
return;
}
input_line_pointer ++;
*end_name = 0;
SKIP_WHITESPACE();
n_value = 0;
if (*input_line_pointer == reg || *input_line_pointer == toupper(reg)){
input_line_pointer++;
if(isdigit(*input_line_pointer)){
val = 0;
while (isdigit(*input_line_pointer)){
if ((val = val * 10 + *input_line_pointer++ - '0') > 31)
break;
}
SKIP_WHITESPACE();
if(val <= 31 &&
(*input_line_pointer == '\n' || *input_line_pointer == '@'))
n_value = 1;
}
}
symbolP = symbol_find_or_make (name);
symbolP -> sy_type = N_ABS;
symbolP -> sy_other = 0; /* NO_SECT */
symbolP -> sy_value = n_value;
symbolP -> sy_frag = &zero_address_frag;
*end_name = delim;
totally_ignore_line();
}
/*
* s_scaled() is used to implement ".scaled symbol,exp" which sets symbol to 1
* or 0 depending on if the expression is a scaled general register expression
* "r1[r2]" or not respectfully. This is intended for use in macros.
*/
static
void
s_scaled(
int value)
{
char *name, *end_name, delim;
symbolS *symbolP;
unsigned long n_value, val;
if( * input_line_pointer == '"')
name = input_line_pointer + 1;
else
name = input_line_pointer;
delim = get_symbol_end();
end_name = input_line_pointer;
*end_name = delim;
SKIP_WHITESPACE();
if ( * input_line_pointer != ',' ) {
*end_name = 0;
as_warn("Expected comma after name \"%s\"", name);
*end_name = delim;
ignore_rest_of_line();
return;
}
input_line_pointer ++;
*end_name = 0;
SKIP_WHITESPACE();
n_value = 0;
if (*input_line_pointer == 'r' || *input_line_pointer == 'R'){
input_line_pointer++;
if(isdigit(*input_line_pointer)){
val = 0;
while (isdigit(*input_line_pointer)){
if ((val = val * 10 + *input_line_pointer++ - '0') > 31)
break;
}
SKIP_WHITESPACE();
if(val <= 31 && *input_line_pointer == '['){
input_line_pointer++;
if (*input_line_pointer == 'r' ||
*input_line_pointer == 'R'){
input_line_pointer++;
if(isdigit(*input_line_pointer)){
val = 0;
while (isdigit(*input_line_pointer)){
if ((val = val * 10 +
*input_line_pointer++ - '0') > 31)
break;
}
if(val <= 31 && *input_line_pointer == ']'){
input_line_pointer++;
SKIP_WHITESPACE();
if(*input_line_pointer == '\n' ||
*input_line_pointer == '@')
n_value = 1;
}
}
}
}
}
}
symbolP = symbol_find_or_make (name);
symbolP -> sy_type = N_ABS;
symbolP -> sy_other = 0; /* NO_SECT */
symbolP -> sy_value = n_value;
symbolP -> sy_frag = & zero_address_frag;
*end_name = delim;
totally_ignore_line();
}
/*
* s_m88k_abs() is used to implement ".abs symbol,exp" which sets symbol to 1
* or 0 depending on if the expression is an absolute expression or not
* respectfully. This is intended for use in macros.
*/
static
void
s_m88k_abs(
int value)
{
char *name, *end_name, delim, *start;
symbolS *symbolP;
unsigned long n_value, val, is_reg_exp;
start = input_line_pointer;
if( * input_line_pointer == '"')
name = input_line_pointer + 1;
else
name = input_line_pointer;
delim = get_symbol_end();
end_name = input_line_pointer;
*end_name = delim;
SKIP_WHITESPACE();
if ( * input_line_pointer != ',' ) {
*end_name = 0;
as_warn("Expected comma after name \"%s\"", name);
*end_name = delim;
ignore_rest_of_line();
return;
}
input_line_pointer ++;
*end_name = 0;
SKIP_WHITESPACE();
is_reg_exp = 0;
n_value = 0;
if(*input_line_pointer == 'r' || *input_line_pointer == 'R'){
input_line_pointer++;
if(isdigit(*input_line_pointer)){
val = 0;
while (isdigit(*input_line_pointer)){
if ((val = val * 10 + *input_line_pointer++ - '0') > 31)
break;
}
SKIP_WHITESPACE();
if(val <= 31)
is_reg_exp = 1;
}
}
if(is_reg_exp == 0){
*end_name = delim;
input_line_pointer = start;
s_abs(value);
return;
}
symbolP = symbol_find_or_make (name);
symbolP -> sy_type = N_ABS;
symbolP -> sy_other = 0; /* NO_SECT */
symbolP -> sy_value = n_value;
symbolP -> sy_frag = & zero_address_frag;
*end_name = delim;
totally_ignore_line();
}
/*
* s_no_delay() is used to implement ".no_delay string" which will abort and
* print the string if the last instruction assembled has a delay slot.
* This is intended for use in macros that expand to more than one instruction
* that could be put in delay slots. This is not really correct in it's
* operation in that it is not per-section and does not take into account
* anything other than assembled instructions.
*/
static
void
s_no_delay(
int value)
{
char *p, c;
p = input_line_pointer;
while(*p != '\n' && *p != '@' && *p != '\0')
p++;
c = *p;
*p = '\0';
if(in_delay_slot)
as_fatal("delay slot abort %s detected. Assembly stopping.",
input_line_pointer);
input_line_pointer = p;
*p = c;
}
#endif /* NeXT */
void
md_begin(
void)
{
register char *retval = NULL;
register unsigned int i = 0;
/* initialize hash table */
op_hash = hash_new();
if (op_hash == NULL)
as_fatal("Could not initialize hash table");
/* loop until you see the end of the list */
while (*m88k_opcodes[i].name) {
char *name = m88k_opcodes[i].name;
/* hash each mnemonic and record its position */
retval = hash_insert(op_hash, name, (char *)&m88k_opcodes[i]);
if (retval != NULL && *retval != '\0')
as_fatal("Can't hash instruction '%s':%s",
m88k_opcodes[i].name, retval);
/* skip to next unique mnemonic or end of list */
for (i++; !strcmp(m88k_opcodes[i].name, name); i++);
}
}
int
md_parse_option(
char **argP,
int *cntP,
char ***vecP)
{
return (1);
}
void
md_assemble(
char *op)
{
char *param, *thisfrag;
struct m88k_opcode *format;
struct m88k_insn insn;
#ifdef NeXT
long pcrel_reloc;
#endif
assert(op);
/* skip over instruction to find parameters */
/* *param != '\0' is need for instructions that have no parameters
like rte */
for (param = op; !isspace(*param) && *param != '\0' ; param++);
*param++ = '\0';
/* try to find the instruction in the hash table */
if ((format = (struct m88k_opcode *) hash_find(op_hash, op)) == NULL) {
as_warn("Invalid mnemonic '%s'", op);
return;
}
/* try parsing this instruction into insn */
while (!calcop(format,param,&insn))
/* if it doesn't parse try the next instruction */
if (!strcmp(format->name, format[1].name))
format++;
else {
as_warn("Parameter syntax error");
return;
}
/* grow the current frag and plop in the opcode */
thisfrag = frag_more(4);
md_number_to_chars(thisfrag, insn.opcode, 4);
#ifdef NeXT
in_delay_slot = format->delay_slot;
#endif
#ifdef NeXT /* generate stabs for debugging assembly code */
/*
* If the -g flag is present generate a line number stab for the
* instruction.
*
* See the detailed comments about stabs in read_a_source_file() for a
* description of what is going on here.
*/
if(flagseen['g'] && frchain_now->frch_nsect == text_nsect){
(void)symbol_new(
"",
68 /* N_SLINE */,
text_nsect,
logical_input_line /* n_desc, line number */,
obstack_next_free(&frags) - frag_now->fr_literal,
frag_now);
}
#endif /* NeXT */
#ifdef NeXT /* mark sections containing instructions */
/*
* We are putting a machine instruction in this section so mark it as
* containg some machine instructions.
*/
frchain_now->frch_section.flags |= S_ATTR_SOME_INSTRUCTIONS;
#endif /* NeXT */
#ifdef NeXT
pcrel_reloc = 0;
if (insn.reloc == M88K_RELOC_PC16 || insn.reloc == M88K_RELOC_PC26){
/*
* The NeXT linker has the ability to scatter blocks of
* sections between labels. This requires that brances to
* labels that survive to the link phase must be able to
* be relocated.
*/
if(insn.exp.X_add_symbol != NULL &&
(insn.exp.X_add_symbol->sy_name[0] != 'L' || flagseen ['L']))
pcrel_reloc = 1;
else
pcrel_reloc = 0;
}
#endif /* NeXT */
/* if this instruction requires labels mark it for later */
switch (insn.reloc) {
case NO_RELOC:
break;
case M88K_RELOC_LO16:
case M88K_RELOC_HI16:
fix_new(
frag_now,
#ifdef NeXT
thisfrag - frag_now->fr_literal,
4,
#else
thisfrag - frag_now->fr_literal + 2,
2,
#endif
insn.exp.X_add_symbol,
insn.exp.X_subtract_symbol,
insn.exp.X_add_number,
0, 0,
insn.reloc
);
break;
#ifndef NeXT
case M88K_RELOC_IW16:
fix_new(
frag_now,
thisfrag - frag_now->fr_literal,
4,
insn.exp.X_add_symbol,
insn.exp.X_subtract_symbol,
insn.exp.X_add_number,
0, 0,
insn.reloc
);
break;
#endif /* !defined(NeXT) */
case M88K_RELOC_PC16:
fix_new(
frag_now,
#ifdef NeXT
thisfrag - frag_now->fr_literal,
4,
#else
thisfrag - frag_now->fr_literal + 2,
2,
#endif
insn.exp.X_add_symbol,
insn.exp.X_subtract_symbol,
insn.exp.X_add_number,
1, pcrel_reloc,
insn.reloc
);
break;
case M88K_RELOC_PC26:
fix_new(
frag_now,
thisfrag - frag_now->fr_literal,
4,
insn.exp.X_add_symbol,
insn.exp.X_subtract_symbol,
insn.exp.X_add_number,
1, pcrel_reloc,
insn.reloc
);
break;
default:
as_warn("Unknown relocation type");
break;
}
}
static
int
calcop(
struct m88k_opcode *format,
char *param,
struct m88k_insn *insn)
{
int parcnt;
/* initial the passed structure */
memset(insn, '\0', sizeof(*insn));
insn->reloc = NO_RELOC;
insn->opcode = format->opcode;
/* parse all parameters */
for (parcnt=0; parcnt<3 && format->op[parcnt].type != NIL; parcnt++) {
switch (format->op[parcnt].type) {
case CNST:
param = parse_cst(param, insn, format, parcnt);
break;
case REG:
param = parse_reg(param, insn, format, parcnt);
break;
#ifdef m88110
case EREG:
param = parse_ereg(param, insn, format, parcnt);
break;
case E4ROT:
param = parse_e4rot(param, insn, format,parcnt);
break;
case XREG:
param = parse_xreg(param, insn, format, parcnt);
break;
#endif m88110
case BF:
param = parse_bf(param, insn, format, parcnt);
break;
case ROT:
param = parse_rot(param, insn, format, parcnt);
break;
case REGSC:
param = parse_rsc(param, insn, format, parcnt);
break;
case CRREG:
param = parse_cr(param, insn, format, parcnt);
break;
case FCRREG:
param = parse_fcr(param, insn, format, parcnt);
break;
case PCREL:
param = parse_pcr(param, insn, format, parcnt);
break;
case CONDMASK:
param = parse_cnd(param, insn, format, parcnt);
break;
case CMPRSLT:
param = parse_cmp(param, insn, format, parcnt);
break;
default:
as_fatal("Unknown parameter type");
}
/* see if parser failed or not */
if (param == NULL)
return 0;
}
return 1;
}
static
char *
parse_pcr(
char *param,
struct m88k_insn *insn,
struct m88k_opcode *format,
int parcnt)
{
char *saveptr, *saveparam;
segT seg;
saveptr = input_line_pointer;
input_line_pointer = param;
seg = expression(&insn->exp);
saveparam = input_line_pointer;
input_line_pointer = saveptr;
switch (format->op[parcnt].width) {
case 16: insn->reloc = M88K_RELOC_PC16;
break;
case 26: insn->reloc = M88K_RELOC_PC26;
break;
default: as_warn("Strange PC relative width %d",
format->op[parcnt].width);
break;
}
return saveparam;
}
static
char *
parse_reg(
char *param,
struct m88k_insn *insn,
struct m88k_opcode *format,
int parcnt)
{
unsigned int val = 0;
if (*param != 'r' && *param != 'R')
return NULL;
param++;
if (!isdigit(*param))
return NULL;
while (isdigit(*param))
if ((val = val * 10 + *param++ - '0') > 31)
return NULL;
insn->opcode |= val << format->op[parcnt].offset;
switch (*param) {
case '\0' :
if (parcnt == 2 || format->op[parcnt+1].type == NIL)
return param;
else
return NULL;
case '[' :
if (parcnt != 2 && format->op[parcnt+1].type == REGSC)
return param+1;
else
return NULL;
case ',' :
if (parcnt != 2 && format->op[parcnt+1].type != NIL)
return param+1;
else
return NULL;
}
return NULL;
}
#ifdef m88110
static
char *
parse_ereg(
char *param,
struct m88k_insn *insn,
struct m88k_opcode *format,
int parcnt)
{
unsigned int val = 0;
if (*param != 'r' && *param != 'R')
return NULL;
param++;
if (!isdigit(*param))
return NULL;
while (isdigit(*param))
if ((val = val * 10 + *param++ - '0') > 31)
return NULL;
if((val & 0x1) != 0)
return NULL;
insn->opcode |= val << format->op[parcnt].offset;
switch (*param) {
case '\0' :
if (parcnt == 2 || format->op[parcnt+1].type == NIL)
return param;
else
return NULL;
case '[' :
if (parcnt != 2 && format->op[parcnt+1].type == REGSC)
return param+1;
else
return NULL;
case ',' :
if (parcnt != 2 && format->op[parcnt+1].type != NIL)
return param+1;
else
return NULL;
}
return NULL;
}
static
char *
parse_e4rot(
char *param,
struct m88k_insn *insn,
struct m88k_opcode *format,
int parcnt)
{
int val;
char *saveptr, save_c, *offset_ptr;
expressionS exp;
segT seg;
/* Now step over the '<' and look for the offset expression before a
'>' and the end of line (which is a '\0' when we get here). We
know there is a '\0' where the end of line was because that is
what parse_a_buffer() in read.c does before calling md_assemble */
if (*param++ != '<')
return NULL;
offset_ptr = param;
while(*param != '\0')
param++;
if(param == offset_ptr || param[-1] != '>')
return NULL;
param--;
save_c = *param;
*param = '\0';
saveptr = input_line_pointer;
input_line_pointer = offset_ptr;
seg = expression(&exp);
*param = save_c;
input_line_pointer = saveptr;
val = exp.X_add_number;
if(seg != SEG_ABSOLUTE || val > 60 || (val & 0x3) != 0)
return NULL;
val >>= 2;
insn->opcode |= val << format->op[parcnt].offset;
return param+1;
}
static
char *
parse_xreg(
char *param,
struct m88k_insn *insn,
struct m88k_opcode *format,
int parcnt)
{
unsigned int val = 0;
if (*param != 'x' && *param != 'X')
return NULL;
param++;
if (!isdigit(*param))
return NULL;
while (isdigit(*param))
if ((val = val * 10 + *param++ - '0') > 31)
return NULL;
insn->opcode |= val << format->op[parcnt].offset;
switch (*param) {
case '\0' :
if (parcnt == 2 || format->op[parcnt+1].type == NIL)
return param;
else
return NULL;
case '[' :
if (parcnt != 2 && format->op[parcnt+1].type == REGSC)
return param+1;
else
return NULL;
case ',' :
if (parcnt != 2 && format->op[parcnt+1].type != NIL)
return param+1;
else
return NULL;
}
return NULL;
}
#endif m88110
static
char *
parse_cmp(
char *param,
struct m88k_insn *insn,
struct m88k_opcode *format,
int parcnt)
{
int val;
char *saveptr, save_c, *offset_ptr, c;
expressionS exp;
segT seg;
/* look for the offset expression before a ',' */
c = *param;
if (isdigit(c) || c == '(' || c == '-' || c == '+' || c == '!' ||
c == '~'){
offset_ptr = param;
while(*param != ',')
param++;
if(param == offset_ptr || *param != ',')
return NULL;
save_c = *param;
*param = '\0';
saveptr = input_line_pointer;
input_line_pointer = offset_ptr;
seg = expression(&exp);
*param = save_c;
input_line_pointer = saveptr;
val = exp.X_add_number;
if(seg != SEG_ABSOLUTE ||
val > (1 << format->op[parcnt].width) || val < 0)
return NULL;
} else {
if (isupper(*param))
*param = tolower(*param);
if (isupper(*(param+1)))
*(param+1) = tolower(*(param+1));
for (val=0; cmpslot[val] != NULL; val++)
if (!strncmp(param,cmpslot[val],2))
break;
if (cmpslot[val] == NULL)
return NULL;
param += 2;
}
if (*param++ != ',')
return NULL;
insn->opcode |= val << format->op[parcnt].offset;
return param;
}
static
char *
parse_cnd(
char *param,
struct m88k_insn *insn,
struct m88k_opcode *format,
int parcnt)
{
int val;
char *saveptr, save_c, *offset_ptr, c;
expressionS exp;
segT seg;
/* look for the offset expression before a ',' */
c = *param;
if (isdigit(c) || c == '(' || c == '-' || c == '+' || c == '!' ||
c == '~'){
offset_ptr = param;
while(*param != ',')
param++;
if(param == offset_ptr || *param != ',')
return NULL;
save_c = *param;
*param = '\0';
saveptr = input_line_pointer;
input_line_pointer = offset_ptr;
seg = expression(&exp);
*param = save_c;
input_line_pointer = saveptr;
val = exp.X_add_number;
if(seg != SEG_ABSOLUTE ||
val > (1 << format->op[parcnt].width) || val < 0)
return NULL;
} else {
if (isupper(*param))
*param = tolower(*param);
if (isupper(*(param+1)))
*(param+1) = tolower(*(param+1));
for (val=0; cndmsk[val].name != NULL; val++)
if (!strncmp(param,cndmsk[val].name,3))
break;
if (cndmsk[val].name == NULL)
return NULL;
val = cndmsk[val].num;
param += 3;
}
if (*param++ != ',')
return NULL;
insn->opcode |= val << format->op[parcnt].offset;
return param;
}
static
char *
parse_bf(
char *param,
struct m88k_insn *insn,
struct m88k_opcode *format,
int parcnt)
{
int val, width;
char *saveptr, save_c, *offset_ptr, c;
expressionS exp;
segT seg;
/* We know there is a '\0' where the end of line was because that is
what parse_a_buffer() in read.c does before calling md_assemble */
/* First look for the width expression before a '<' */
saveptr = input_line_pointer;
input_line_pointer = param;
while(*param != '<' && *param != '\0')
param++;
if(*param == '\0'){
input_line_pointer = saveptr;
return NULL;
}
save_c = *param;
*param = '\0';
seg = expression(&exp);
*param = save_c;
input_line_pointer = saveptr;
width = exp.X_add_number;
if(seg != SEG_ABSOLUTE || width > 32 || width < 0)
return NULL;
/* Now step over the '<' and look for the offset expression before a
'>' and the end of line (which is a '\0' when we get here) */
param++;
c = *param;
if (isdigit(c) || c == '(' || c == '-' || c == '+' || c == '!' ||
c == '~'){
offset_ptr = param;
while(*param != '\0')
param++;
if(param != offset_ptr && param[-1] != '>')
return NULL;
param--;
save_c = *param;
*param = '\0';
saveptr = input_line_pointer;
input_line_pointer = offset_ptr;
seg = expression(&exp);
*param = save_c;
input_line_pointer = saveptr;
val = exp.X_add_number;
if(seg != SEG_ABSOLUTE || val > 32 || val < 0)
return NULL;
}
else {
if (isupper(*param))
*param = tolower(*param);
if (isupper(*(param+1)))
*(param+1) = tolower(*(param+1));
for (val=0; cmpslot[val] != NULL; val++)
if (!strncmp(param,cmpslot[val],2))
break;
if (cmpslot[val] == NULL)
return NULL;
param += 2;
}
if (*param != '>')
return NULL;
insn->opcode |= width << 5;
insn->opcode |= val;
return param+1;
}
static
char *
parse_rot(
char *param,
struct m88k_insn *insn,
struct m88k_opcode *format,
int parcnt)
{
int val;
char *saveptr, save_c, *offset_ptr;
expressionS exp;
segT seg;
/* Now step over the '<' and look for the offset expression before a
'>' and the end of line (which is a '\0' when we get here). We
know there is a '\0' where the end of line was because that is
what parse_a_buffer() in read.c does before calling md_assemble */
if (*param++ != '<')
return NULL;
offset_ptr = param;
while(*param != '\0')
param++;
if(param != offset_ptr && param[-1] != '>')
return NULL;
param--;
save_c = *param;
*param = '\0';
saveptr = input_line_pointer;
input_line_pointer = offset_ptr;
seg = expression(&exp);
*param = save_c;
input_line_pointer = saveptr;
val = exp.X_add_number;
if(seg != SEG_ABSOLUTE && (val > 32 || val < 0))
return NULL;
insn->opcode |= val;
return param+1;
}
static
char *
parse_rsc(
char *param,
struct m88k_insn *insn,
struct m88k_opcode *format,
int parcnt)
{
unsigned int val = 0;
if (*param != 'r' && *param != 'R')
return NULL;
param++;
if (!isdigit(*param))
return NULL;
while (isdigit(*param))
if ((val = val * 10 + *param++ - '0') > 31)
return NULL;
insn->opcode |= val << format->op[parcnt].offset;
if (*param != ']' || *(param+1) != '\0')
return NULL;
return param+1;
}
static
char *
parse_cr(
char *param,
struct m88k_insn *insn,
struct m88k_opcode *format,
int parcnt)
{
unsigned int val = 0;
if (strncmp(param, "cr", 2))
return NULL;
param += 2;
if (!isdigit(*param))
return NULL;
while (isdigit(*param))
if ((val = val * 10 + *param++ - '0') > 63)
return NULL;
/*
* the following fix is not as generic as I'd like, but the
* hardware is real picky about this. - bowen@cs.buffalo.edu
* This fix is to make sure the S1 and S2 fields are the same.
*/
insn->opcode |= (insn->opcode & 0x001f0000) >> 16;
insn->opcode |= val << format->op[parcnt].offset;
if (*param != '\0')
return NULL;
return param;
}
static
char *
parse_fcr(
char *param,
struct m88k_insn *insn,
struct m88k_opcode *format,
int parcnt)
{
unsigned int val = 0;
if (strncmp(param, "fcr", 3))
return NULL;
param += 3;
if (!isdigit(*param))
return NULL;
while (isdigit(*param))
if ((val = val * 10 + *param++ - '0') > 63)
return NULL;
/*
* This is to make sure the S1 and S2 fields are the same.
*/
insn->opcode |= (insn->opcode & 0x001f0000) >> 16;
insn->opcode |= val << format->op[parcnt].offset;
if (*param != '\0')
return NULL;
return param;
}
static
char *
parse_cst(
char *param,
struct m88k_insn *insn,
struct m88k_opcode *format,
int parcnt)
{
char c, *saveptr, *saveparam;
unsigned int val, nohilo = 0;
segT seg;
expressionS exp;
c = *param;
if (isdigit(c) || c == '(' || c == '-' || c == '+' || c == '!' ||
c == '~'){
saveptr = input_line_pointer;
input_line_pointer = param;
while(*param != '\0')
param++;
seg = expression(&exp);
input_line_pointer = saveptr;
val = exp.X_add_number;
if(seg != SEG_ABSOLUTE || val > (1 << format->op[parcnt].width))
return NULL;
}
else if (!strncmp(param,"hi16(",5))
if (isdigit(*(param+5))) {
param = getval(param+5,&val);
val = (val & 0xffff0000) >> 16;
if (*param++ != ')')
return NULL;
} else
insn->reloc = M88K_RELOC_HI16;
else if (!strncmp(param,"lo16(",5))
if (isdigit(*(param+5))) {
param = getval(param+5,&val);
val &= 0x0000ffff;
if (*param++ != ')')
return NULL;
} else
insn->reloc = M88K_RELOC_LO16;
#ifndef NeXT
else if (!strncmp(param,"iw16(",5))
if (isdigit(*(param+5))) {
param = getval(param+5,&val);
val &= 0x0000ffff;
if (*param++ != ')')
return NULL;
} else
insn->reloc = M88K_RELOC_IW16;
#endif /* !defined(NeXT) */
else if (*param == 'r' && isdigit(*(param+1)))
return NULL;
else {
insn->reloc = M88K_RELOC_LO16;
nohilo = 1;
}
if (insn->reloc != NO_RELOC) {
saveptr = input_line_pointer;
input_line_pointer = param + (nohilo ? 0 : 5);
seg = expression(&insn->exp);
saveparam = input_line_pointer;
input_line_pointer = saveptr;
if (nohilo) {
if (*saveparam != '\0')
return NULL;
return saveparam;
}
if (*saveparam != ')')
return NULL;
return saveparam+1;
}
if ((1 << format->op[parcnt].width) <= val)
return NULL;
insn->opcode |= val << format->op[parcnt].offset;
if (*param != '\0')
return NULL;
return param;
}
#define isoct(z) (z >= '0' && z <= '7')
#define ishex(z) ((z >= '0' && z <= '9') || (z >= 'a' && z <= 'f') || (z >= 'A' && z <= 'F'))
#define hexval(z) \
(isdigit(z) ? (z) - '0' : \
islower(z) ? (z) - 'a' + 10 : \
(z) - 'A' + 10)
static
char *
getval(
char *param,
unsigned int *val)
{
*val = 0;
if (*param == '0' && (*(param+1) == 'x' || *(param+1) == 'X'))
for (param += 2; ishex(*param); param++)
if (*val > 0x0fffffff)
return param;
else
*val = *val * 16 + hexval(*param);
else if (*param == '0')
for (param++; isoct(*param); param++)
if (*val > 0x1fffffff)
return param;
else
*val = *val * 8 + *param - '0';
else
for (; isdigit(*param); param++)
*val = *val * 10 + *param - '0';
return param;
}
void
md_number_to_chars(
char *buf,
long val,
int nbytes)
{
switch(nbytes) {
case 4:
*buf++ = val >> 24;
*buf++ = val >> 16;
case 2:
*buf++ = val >> 8;
case 1:
*buf = val;
break;
default:
abort();
}
}
void
md_number_to_imm(
unsigned char *buf,
long val,
int nbytes,
fixS *fixP,
int nsect)
{
if(fixP->fx_r_type == NO_RELOC ||
fixP->fx_r_type == M88K_RELOC_VANILLA) {
switch (nbytes) {
case 4:
*buf++ = val >> 24;
*buf++ = val >> 16;
case 2:
*buf++ = val >> 8;
case 1:
*buf = val;
break;
default:
abort();
}
return;
}
switch (fixP->fx_r_type) {
#ifdef NeXT
case M88K_RELOC_LO16:
buf[2] = val >> 8;
buf[3] = val;
break;
case M88K_RELOC_HI16:
buf[2] = val >> 24;
buf[3] = val >> 16;
break;
case M88K_RELOC_PC16:
val += 4;
buf[2] = val >> 10;
buf[3] = val >> 2;
break;
case M88K_RELOC_PC26:
val += 4;
buf[0] |= (val >> 26) & 0x03;
buf[1] = val >> 18;
buf[2] = val >> 10;
buf[3] = val >> 2;
break;
#else /* !defined NeXT */
case M88K_RELOC_LO16:
buf[0] = val >> 8;
buf[1] = val;
break;
case M88K_RELOC_IW16:
buf[2] = val >> 8;
buf[3] = val;
break;
case M88K_RELOC_HI16:
buf[0] = val >> 24;
buf[1] = val >> 16;
break;
case M88K_RELOC_PC16:
val += 4;
buf[0] = val >> 10;
buf[1] = val >> 2;
break;
case M88K_RELOC_PC26:
val += 4;
buf[0] |= (val >> 26) & 0x03;
buf[1] = val >> 18;
buf[2] = val >> 10;
buf[3] = val >> 2;
break;
case M88K_RELOC_32:
buf[0] = val >> 24;
buf[1] = val >> 16;
buf[2] = val >> 8;
buf[3] = val;
break;
#endif /* !defined(NeXT) */
default:
as_warn("Bad relocation type");
break;
}
}
#define MAX_LITTLENUMS 6
/* Turn a string in input_line_pointer into a floating point constant of type
type, and store the appropriate bytes in *litP. The number of LITTLENUMS
emitted is stored in *sizeP . An error message is returned, or NULL on OK.
*/
char *
md_atof(
int type,
char *litP,
int *sizeP)
{
int prec;
LITTLENUM_TYPE words[MAX_LITTLENUMS];
LITTLENUM_TYPE *wordP;
char *t;
char *atof_ieee();
switch(type) {
case 'f':
case 'F':
case 's':
case 'S':
prec = 2;
break;
case 'd':
case 'D':
case 'r':
case 'R':
prec = 4;
break;
case 'x':
case 'X':
prec = 6;
break;
case 'p':
case 'P':
prec = 6;
break;
default:
*sizeP=0;
return "Bad call to MD_ATOF()";
}
t=atof_ieee(input_line_pointer,type,words);
if(t)
input_line_pointer=t;
*sizeP=prec * sizeof(LITTLENUM_TYPE);
for(wordP=words;prec--;) {
md_number_to_chars(litP,(long)(*wordP++),sizeof(LITTLENUM_TYPE));
litP+=sizeof(LITTLENUM_TYPE);
}
return ""; /* Someone should teach Dean about null pointers */
}
const relax_typeS md_relax_table[] = { {0} };
int
md_estimate_size_before_relax(
fragS *fragP,
int segment_type)
{
as_fatal("internal error: Relaxation should never occur");
return(0);
}
void
md_convert_frag(
fragS *fragP)
{
as_fatal("internal error: Relaxation should never occur");
}
void
md_end(
void)
{
}