home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
OpenStep 4.2J (Developer)
/
os42jdev.iso
/
NextDeveloper
/
Source
/
GNU
/
cctools
/
as
/
m98k.c
< prev
next >
Wrap
C/C++ Source or Header
|
1997-01-24
|
43KB
|
1,706 lines
#include <ctype.h>
#include <string.h>
#include <mach-o/m98k/reloc.h>
#include "m98k-opcode.h"
#include "as.h"
#include "flonum.h"
#include "expr.h"
#include "hash.h"
#include "read.h"
#include "md.h"
#include "obstack.h"
#include "symbols.h"
#include "messages.h"
#include "atof-ieee.h"
#include "input-scrub.h"
#include "sections.h"
/*
* These are the default cputype and cpusubtype for the m98k architecture.
*/
const cpu_type_t md_cputype = CPU_TYPE_MC98000;
cpu_subtype_t md_cpusubtype = CPU_SUBTYPE_MC98000_ALL;
/* This is the byte sex for the m98k architecture */
const enum byte_sex md_target_byte_sex = BIG_ENDIAN_BYTE_SEX;
/* These characters start a comment anywhere on the line */
const char md_comment_chars[] = ";";
/* These characters only start a comment at the beginning of a line */
const char md_line_comment_chars[] = "#";
/*
* These characters can be used to separate mantissa decimal digits from
* exponent decimal digits in floating point numbers.
*/
const char md_EXP_CHARS[] = "eE";
/*
* The characters after a leading 0 that means this number is a floating point
* constant as in 0f123.456 or 0d1.234E-12 (see md_EXP_CHARS above).
*/
const char md_FLT_CHARS[] = "dDfF";
/*
* This is the machine dependent pseudo opcode table for this target machine.
*/
static void s_reg(
int reg);
const pseudo_typeS md_pseudo_table[] =
{
{"greg", s_reg, 'r' },
{0} /* end of table marker */
};
#define RT(x) (((x) >> 21) & 0x1f)
#define RA(x) (((x) >> 16) & 0x1f)
struct m98k_insn {
unsigned long opcode;
expressionS exp;
enum reloc_type_m98k reloc;
long pcrel;
long pcrel_reloc;
};
/*
* The pointer to the opcode hash table built by md_begin() and used by
* md_assemble() to look up opcodes.
*/
static struct hash_control *op_hash = NULL;
/*
* These aid in the printing of better error messages for parameter syntax
* errors when there is only one mnemonic in the tables.
*/
static unsigned long error_param_count = 0;
static char *error_param_message = NULL;
/*
* These are name names of the known special registers and the numbers assigned
* to them.
*/
struct special_register {
unsigned long number;
char *name;
};
static const struct special_register special_registers[] = {
{ 0, "mq" }, /* 601 only */
{ 1, "xer" },
{ 4, "rtcu" },
{ 5, "rtcl" },
{ 8, "lr" },
{ 9, "ctr" },
{ 18, "dsisr" },
{ 19, "dar" },
{ 22, "dec" },
{ 25, "sdr1" },
{ 26, "srr0" },
{ 27, "srr1" },
{ 272, "sprg0" },
{ 273, "sprg1" },
{ 274, "sprg2" },
{ 275, "sprg3" },
{ 280, "asr" },
{ 281, "rtcd" },
{ 282, "rtci" },
{ 287, "pvr" },
{ 528, "ibat0u" },
{ 529, "ibat0l" },
{ 530, "ibat1u" },
{ 531, "ibat1l" },
{ 532, "ibat2u" },
{ 533, "ibat2l" },
{ 534, "ibat3u" },
{ 535, "ibat3l" },
{ 528, "bat0u" }, /* 601 only */
{ 529, "bat0l" }, /* 601 only */
{ 530, "bat1u" }, /* 601 only */
{ 531, "bat1l" }, /* 601 only */
{ 532, "bat2u" }, /* 601 only */
{ 533, "bat2l" }, /* 601 only */
{ 534, "bat3u" }, /* 601 only */
{ 535, "bat3l" }, /* 601 only */
{ 536, "dbat0u" },
{ 537, "dbat0l" },
{ 538, "dbat1u" },
{ 539, "dbat1l" },
{ 540, "dbat2u" },
{ 541, "dbat2l" },
{ 542, "dbat3u" },
{ 543, "dbat3l" },
{ 1008,"hid0" }, /* 601 only */
{ 1009,"hid1" }, /* 601 only */
{ 1010,"hid2" }, /* 601 only */
{ 1013,"hid5" }, /* 601 only */
{ 1013,"dabr" }, /* 601 only */
{ 1022,"fpecr" },
{ 1023,"pid" }, /* 601 only */
{ 0, "" } /* end of table marker */
};
/*
* These are name names of the condition field special registers and the
* numbers assigned to them.
*/
struct condition_symbol {
unsigned long value;
char *name;
};
static const struct condition_symbol condition_symbols[] = {
{ 0, "lt" }, /* less than */
{ 1, "gt" }, /* greater than */
{ 2, "eq" }, /* equal */
{ 3, "so" }, /* summary overflow */
{ 3, "un" }, /* unordered */
{ 0, "" } /* end of table marker */
};
struct CR_field {
unsigned long value;
char *name;
};
static const struct CR_field CR_fields[] = {
{ 0, "cr0" }, /* CR field 0 */
{ 4, "cr1" }, /* CR field 1 */
{ 8, "cr2" }, /* CR field 2 */
{ 12, "cr3" }, /* CR field 3 */
{ 16, "cr4" }, /* CR field 4 */
{ 20, "cr5" }, /* CR field 5 */
{ 24, "cr6" }, /* CR field 6 */
{ 28, "cr7" }, /* CR field 7 */
{ 0, "" } /* end of table marker */
};
/*
* These are built in macros because they are trivial to implement as macros
* which otherwise be less obvious to do special entries for them.
*/
struct macros {
char *name;
char *body;
};
static const struct macros m98k_macros[] = {
{ "extldi\n", "rldicr $0,$1,$3,$2-1\n" },
{ "extldi.\n", "rldicr. $0,$1,$3,$2-1\n" },
{ "extrdi\n", "rldicl $0,$1,$2+$3,64-$2\n" },
{ "extrdi.\n", "rldicl. $0,$1,$2+$3,64-$2\n" },
{ "insrdi\n", "rldimi $0,$1,64-($3+$2),$3\n" },
{ "insrdi.\n", "rldimi. $0,$1,64-($3+$2),$3\n" },
{ "rotldi\n", "rldicl $0,$1,$2,0\n" },
{ "rotldi.\n", "rldicl. $0,$1,$2,0\n" },
{ "rotrdi\n", "rldicl $0,$1,64-$2,0\n" },
{ "rotrdi.\n", "rldicl. $0,$1,64-$2,0\n" },
{ "rotld\n", "rldcl $0,$1,$2,0\n" },
{ "rotld.\n", "rldcl. $0,$1,$2,0\n" },
{ "sldi\n", "rldicr $0,$1,$2,63-$2\n" },
{ "sldi.\n", "rldicr. $0,$1,$2,63-$2\n" },
{ "srdi\n", "rldicl $0,$1,64-$2,$2\n" },
{ "srdi.\n", "rldicl. $0,$1,64-$2,$2\n" },
{ "clrldi\n", "rldicl $0,$1,0,$2\n" },
{ "clrldi.\n", "rldicl. $0,$1,0,$2\n" },
{ "clrrdi\n", "rldicl $0,$1,0,63-$2\n" },
{ "clrrdi.\n", "rldicl. $0,$1,0,63-$2\n" },
{ "clrlsldi\n","rldic $0,$1,$3,$2-$3\n" },
{ "clrlsldi.\n","rldic. $0,$1,$3,$2-$3\n" },
{ "extlwi\n", "rlwinm $0,$1,$3,0,$2-1\n" },
{ "extlwi.\n", "rlwinm. $0,$1,$3,0,$2-1\n" },
{ "extrwi\n", "rlwinm $0,$1,$2+$3,32-$2,31\n" },
{ "extrwi.\n", "rlwinm. $0,$1,$2+$3,32-$2,31\n" },
{ "inslwi\n", "rlwimi $0,$1,32-$3,$3,($3+$2)-1\n" },
{ "inslwi.\n", "rlwimi. $0,$1,32-$3,$3,($3+$2)-1\n" },
{ "insrwi\n", "rlwimi $0,$1,32-($3+$2),$3,($3+$2)-1\n" },
{ "insrwi.\n", "rlwimi. $0,$1,32-($3+$2),$3,($3+$2)-1\n" },
{ "rotlwi\n", "rlwinm $0,$1,$2,0,31\n" },
{ "rotlwi.\n", "rlwinm. $0,$1,$2,0,31\n" },
{ "rotrwi\n", "rlwinm $0,$1,32-$2,0,31\n" },
{ "rotrwi.\n", "rlwinm. $0,$1,32-$2,0,31\n" },
{ "rotlw\n", "rlwnm $0,$1,$2,0,31\n" },
{ "rotlw.\n", "rlwnm. $0,$1,$2,0,31\n" },
{ "slwi\n", "rlwinm $0,$1,$2,0,31-$2\n" },
{ "slwi.\n", "rlwinm. $0,$1,$2,0,31-$2\n" },
{ "srwi\n", "rlwinm $0,$1,32-$2,$2,31\n" },
{ "srwi.\n", "rlwinm. $0,$1,32-$2,$2,31\n" },
{ "clrlwi\n", "rlwinm $0,$1,0,$2,31\n" },
{ "clrlwi.\n", "rlwinm. $0,$1,0,$2,31\n" },
{ "clrrwi\n", "rlwinm $0,$1,0,0,31-$2\n" },
{ "clrrwi.\n", "rlwinm. $0,$1,0,0,31-$2\n" },
{ "clrlslwi\n","rlwinm $0,$1,$3,$2-$3,31-$2\n" },
{ "clrlslwi.\n","rlwinm. $0,$1,$3,$2-$3,31-$2\n" },
{ "mtxer\n", "mtspr 1,$0\n"},
{ "mfxer\n", "mfspr $0,1\n"},
{ "mtlr\n", "mtspr 8,$0\n"},
{ "mflr\n", "mfspr $0,8\n"},
{ "mtctr\n", "mtspr 9,$0\n"},
{ "mfctr\n", "mfspr $0,9\n"},
{ "mtdsisr\n", "mtspr 18,$0\n"},
{ "mfdsisr\n", "mfspr $0,18\n"},
{ "mtdar\n", "mtspr 19,$0\n"},
{ "mfdar\n", "mfspr $0,19\n"},
{ "mtdec\n", "mtspr 22,$0\n"},
{ "mfdec\n", "mfspr $0,22\n"},
{ "mtsdr1\n", "mtspr 25,$0\n"},
{ "mfsdr1\n", "mfspr $0,25\n"},
{ "mtsrr0\n", "mtspr 26,$0\n"},
{ "mfsrr0\n", "mfspr $0,26\n"},
{ "mtsrr1\n", "mtspr 27,$0\n"},
{ "mfsrr1\n", "mfspr $0,27\n"},
{ "mtsprg\n", "mtspr 272+$0,$1\n"},
{ "mfsprg\n", "mfspr $0,272+$1\n"},
{ "mtasr\n", "mtspr 280,$0\n"},
{ "mfasr\n", "mfspr $0,280\n"},
{ "mtrtcd\n", "mtspr 281,$0\n"},
{ "mfrtcd\n", "mfspr $0,281\n"},
{ "mtrtci\n", "mtspr 282,$0\n"},
{ "mfrtci\n", "mfspr $0,282\n"},
{ "mfpvr\n", "mfspr $0,287\n"},
{ "mtibatu\n", "mtspr 528+2*$0,$1\n"},
{ "mfibatu\n", "mfspr $0,528+2*$1\n"},
{ "mtibatl\n", "mtspr 529+2*$0,$1\n"},
{ "mfibatl\n", "mfspr $0,529+2*$1\n"},
{ "mtdbatu\n", "mtspr 536+2*$0,$1\n"},
{ "mfdbatu\n", "mfspr $0,536+2*$1\n"},
{ "mtdbatl\n", "mtspr 537+2*$0,$1\n"},
{ "mfdbatl\n", "mfspr $0,537+2*$1\n"},
{ "mtbatu\n", "mtspr 528+2*$0,$1\n"},
{ "mfbatu\n", "mfspr $0,528+2*$1\n"},
{ "mtbatl\n", "mtspr 529+2*$0,$1\n"},
{ "mfbatl\n", "mfspr $0,529+2*$1\n"},
{ "subi\n", "addi $0,$1,-($2)\n"},
{ "subis\n", "addis $0,$1,-($2)\n"},
{ "subic\n", "addic $0,$1,-($2)\n"},
{ "subic.\n", "addic. $0,$1,-($2)\n"},
{ "", "" } /* end of table marker */
};
static int calcop(
struct m98k_opcode *format,
char *param,
struct m98k_insn *insn,
char *op,
char prediction);
static char *parse_branch(
char *param,
struct m98k_insn *insn,
struct m98k_opcode *format,
int parcnt);
static char *parse_displacement(
char *param,
struct m98k_insn *insn,
struct m98k_opcode *format,
int parcnt);
static char *parse_immediate(
char *param,
struct m98k_insn *insn,
struct m98k_opcode *format,
int parcnt);
static char *parse_reg(
char *reg_name,
char *param,
struct m98k_insn *insn,
struct m98k_opcode *format,
unsigned long parcnt);
static char *parse_spreg(
char *param,
struct m98k_insn *insn,
struct m98k_opcode *format,
unsigned long parcnt);
static char *parse_bcnd(
char *param,
struct m98k_insn *insn,
struct m98k_opcode *format,
unsigned long parcnt);
static char *parse_crf(
char *param,
struct m98k_insn *insn,
struct m98k_opcode *format,
unsigned long parcnt);
static char *parse_num(
char *param,
struct m98k_insn *insn,
struct m98k_opcode *format,
unsigned long parcnt,
long max_width_zero);
static char *parse_sh(
char *param,
struct m98k_insn *insn,
struct m98k_opcode *format,
unsigned long parcnt);
static char *parse_mb(
char *param,
struct m98k_insn *insn,
struct m98k_opcode *format,
unsigned long parcnt);
/*
* md_begin() is called from main() in as.c before assembly begins. It is used
* to allow target machine dependent initialization.
*/
void
md_begin(void)
{
unsigned long i;
char *name, *retval;
/* initialize the opcode hash table */
op_hash = hash_new();
if(op_hash == NULL)
as_fatal("Could not initialize the opcode hash table");
/* loop until you see the end of the list */
i = 0;
while(*m98k_opcodes[i].name){
name = m98k_opcodes[i].name;
/* hash each mnemonic and record its position */
retval = hash_insert(op_hash, name, (char *)&m98k_opcodes[i]);
if(retval != NULL && *retval != '\0')
as_fatal("Can't hash instruction '%s':%s",
m98k_opcodes[i].name, retval);
/* skip to next unique mnemonic or end of list */
for(i++; strcmp(m98k_opcodes[i].name, name) == 0; i++)
;
}
/*
* Load the builtin macros for extended mnemonics for rotate and
* shift mnemonics.
*/
for(i = 0; *m98k_macros[i].name != '\0'; i++){
input_line_pointer = m98k_macros[i].name;
s_macro(0);
add_to_macro_definition(m98k_macros[i].body);
s_endmacro(0);
}
}
/*
* md_end() is called from main() in as.c after assembly ends. It is used
* to allow target machine dependent clean up.
*/
void
md_end(void)
{
}
/*
* md_parse_option() is called from main() in as.c to parse target machine
* dependent command line options. This routine returns 0 if it is passed an
* option that is not recognized non-zero otherwise.
*/
int
md_parse_option(
char **argP,
int *cntP,
char ***vecP)
{
return(0);
}
/*
* 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();
}
/*
* md_assemble() is passed a pointer to a string that should be a assembly
* statement for the target machine.
*/
void
md_assemble(
char *op)
{
char *param, *thisfrag, prediction;
struct m98k_opcode *format;
struct m98k_insn insn;
unsigned long retry;
/*
* Pick up the instruction and any trailing branch prediction character
* (a trailing '+' or '-' on the instruction).
*/
prediction = '\0';
for(param = op; !isspace(*param) && *param != '\0' ; param++)
prediction = *param;
if(prediction == '+' || prediction == '-')
param[-1] = '\0';
else
prediction = '\0';
if(*param != '\0')
*param++ = '\0';
/* try to find the instruction in the hash table */
if((format = (struct m98k_opcode *)hash_find(op_hash, op)) == NULL){
as_warn("Invalid mnemonic '%s'", op);
return;
}
/* try parsing this instruction into insn */
retry = 0;
error_param_count = 0;
error_param_message = NULL;
while(calcop(format, param, &insn, op, prediction) == 0){
/* if it doesn't parse try the next instruction */
if(strcmp(format->name, format[1].name) == 0){
format++;
retry = 1;
}
else{
if(retry == 0){
if(error_param_message != NULL)
as_warn(error_param_message, error_param_count + 1);
else
as_warn("Parameter syntax error (parameter %lu)",
error_param_count + 1);
}
else
as_warn("Parameter syntax error");
return;
}
}
/*
* Check for invalid forms of instructions. For the following
* instructions: lbzu, lbzux, lhzu, lhzux, lhau, lhaux, lwzu, lwzux,
* lwaux, ldu, ldux
* if RA == 0 or RA == RT the instruction form is invalid.
*/
if((insn.opcode & 0xfc000000) == 0x8c000000 || /* lbzu */
(insn.opcode & 0xfc0007fe) == 0x7c0000ee || /* lbzux */
(insn.opcode & 0xfc000000) == 0xa4000000 || /* lhzu */
(insn.opcode & 0xfc0007fe) == 0x7c00026e || /* lbzux */
(insn.opcode & 0xfc000000) == 0xac000000 || /* lhau */
(insn.opcode & 0xfc0007fe) == 0x7c0002ee || /* lhaux */
(insn.opcode & 0xfc000000) == 0x84000000 || /* lwzu */
(insn.opcode & 0xfc0007fe) == 0x7c00006e || /* lwzux */
(insn.opcode & 0xfc0007fe) == 0x7c0002ea || /* lwaux */
(insn.opcode & 0xfc000000) == 0xe8000000 || /* ldu */
(insn.opcode & 0xfc0007fe) == 0x7c00006a){ /* ldux */
if(RA(insn.opcode) == 0)
as_warn("Invalid form of the instruction (RA must not be 0)");
if(RA(insn.opcode) == RT(insn.opcode))
as_warn("Invalid form of the instruction (RA must not the same "
"as RT)");
}
/*
* For the following instructions: stbu, stbux, sthu, sthux, stwu,
* stwux, stdu, stdux, lfsu, lfsux, lfdu, lfdux, stfsu, stfsux, stfdu,
* stfdux
* if RA == 0 the instruction form is invalid.
*/
if((insn.opcode & 0xfc000000) == 0x9c000000 || /* stbu */
(insn.opcode & 0xfc0007fe) == 0x7c0001ee || /* stbux */
(insn.opcode & 0xfc000000) == 0xb4000000 || /* sthu */
(insn.opcode & 0xfc0007fe) == 0x7c00036e || /* sthux */
(insn.opcode & 0xfc000000) == 0x94000000 || /* stwu */
(insn.opcode & 0xfc0007fe) == 0x7c00016e || /* stwux */
(insn.opcode & 0xfc000003) == 0xf8000001 || /* stdu */
(insn.opcode & 0xfc0007fe) == 0x7c00016a || /* stdux */
(insn.opcode & 0xfc000000) == 0xc4000000 || /* lfsu */
(insn.opcode & 0xfc0007fe) == 0x7c00046e || /* lfsux */
(insn.opcode & 0xfc000000) == 0xcc000000 || /* lfdu */
(insn.opcode & 0xfc0007fe) == 0x7c0004ee || /* lfdux */
(insn.opcode & 0xfc000000) == 0xd4000000 || /* stfsu */
(insn.opcode & 0xfc0007fe) == 0x7c00056e || /* stfsux */
(insn.opcode & 0xfc000000) == 0xdc000000 || /* stfdu */
(insn.opcode & 0xfc0007fe) == 0x7c0005ee){ /* stfdux */
if(RA(insn.opcode) == 0)
as_warn("Invalid form of the instruction (RA must not be 0)");
}
/*
* For the following instructions: lmw, lmd, lswi, lswx
* if RA is in the range of registers to be loaded or RT == RA == 0
* the instruction form is invalid. WHAT does this mean?
*/
if((insn.opcode & 0xfc000000) == 0xb8000000 || /* lmw */
(insn.opcode & 0xfc000003) == 0xe8000003 || /* lmw */
(insn.opcode & 0xfc0007fe) == 0x7c0004aa || /* lswi */
(insn.opcode & 0xfc0007fe) == 0x7c00042a){ /* lswx */
}
/* grow the current frag and plop in the opcode */
thisfrag = frag_more(4);
md_number_to_chars(thisfrag, insn.opcode, 4);
/*
* 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);
}
/*
* 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;
/* if this instruction requires labels mark it for later */
switch(insn.reloc){
case NO_RELOC:
break;
case M98K_RELOC_HI16:
case M98K_RELOC_LO16:
case M98K_RELOC_HA16:
case M98K_RELOC_LO14:
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;
case M98K_RELOC_BR14:
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,
insn.pcrel,
insn.pcrel_reloc,
insn.reloc);
break;
case M98K_RELOC_BR24:
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,
insn.pcrel,
insn.pcrel_reloc,
insn.reloc);
break;
default:
as_warn("Unknown relocation type");
break;
}
}
static
int
calcop(
struct m98k_opcode *format,
char *param,
struct m98k_insn *insn,
char *op,
char prediction)
{
unsigned long parcnt;
/* initial the passed structure */
memset(insn, '\0', sizeof(struct m98k_insn));
insn->opcode = format->opcode;
insn->reloc = NO_RELOC;
/* parse all parameters */
for(parcnt = 0; parcnt < 5 &&
format->ops[parcnt].type != NONE; parcnt++){
error_param_count = parcnt;
switch(format->ops[parcnt].type){
case PCREL:
case BADDR:
param = parse_branch(param, insn, format, parcnt);
break;
case D:
case DS:
param = parse_displacement(param, insn, format, parcnt);
break;
case SI:
case UI:
param = parse_immediate(param, insn, format, parcnt);
break;
case GREG:
case G0REG:
param = parse_reg("r", param, insn, format, parcnt);
break;
case FREG:
param = parse_reg("f", param, insn, format, parcnt);
break;
case SGREG:
param = parse_reg("sr", param, insn, format, parcnt);
break;
case SPREG:
param = parse_spreg(param, insn, format, parcnt);
break;
case BCND:
param = parse_bcnd(param, insn, format, parcnt);
break;
case CRF:
case CRFONLY:
param = parse_crf(param, insn, format, parcnt);
break;
case NUM:
param = parse_num(param, insn, format, parcnt, 0);
break;
case NUM0:
param = parse_num(param, insn, format, parcnt, 1);
break;
case sh:
param = parse_sh(param, insn, format, parcnt);
break;
case mb:
param = parse_mb(param, insn, format, parcnt);
break;
default:
as_fatal("Unknown parameter type");
}
/* see if parser failed or not */
if (param == NULL)
return(0);
}
if(format->ops[0].type == NONE && *param != '\0'){
error_param_message = "too many parameters";
return(0);
}
if(IS_BRANCH_CONDITIONAL(insn->opcode)){
if(prediction != '\0'){
/*
* Set the Y_BIT assuming the displacement is non-negitive.
* If the displacement is negitive then the Y_BIT is flipped
* in md_number_to_imm().
*/
if(prediction == '+')
insn->opcode |= Y_BIT;
else{ /* prediction == '-' */
if((insn->opcode & Y_BIT) != 0)
as_warn("branch prediction ('-') ignored (specified "
"operand has prediction bit set)");
else
insn->opcode &= ~(Y_BIT);
}
}
}
else{
if(prediction != '\0')
as_warn("branch prediction ignored (instruction is not a "
"conditional branch)");
}
return(1);
}
static
char *
parse_displacement(
char *param,
struct m98k_insn *insn,
struct m98k_opcode *format,
int parcnt)
{
unsigned long val;
char *end, *saveptr, *saveparam;
segT seg;
if(parcnt != 1 || format->ops[2].type != G0REG)
as_fatal("internal error, bad table entry for instruction %s "
"(displacement operand not second operand or general "
"register not third operand)", format->name);
/*
* There must be "(rX)" (where X is a number between 0-31) or "(0)"
* at the end of the parameter string. To know out where the
* displacement expression ends determine the begining the "(rX)"
* by looking for the last '(' in the string. The parsing of this
* trailing string will be done in another routine.
*/
end = strrchr(param, '(');
if(end == NULL)
return(NULL);
*end = '\0';
/*
* The expression may have one of the following: hi16(exp), ha16(exp),
* or lo16(exp) around the expression which determines the relocation
* type.
*/
if(strncmp(param,"hi16(",5) == 0){
insn->reloc = M98K_RELOC_HI16;
param += 5;
}
else if(strncmp(param,"ha16(",5) == 0){
insn->reloc = M98K_RELOC_HA16;
param += 5;
}
else if(strncmp(param,"lo16(",5) == 0){
if(format->ops[parcnt].type == DS)
insn->reloc = M98K_RELOC_LO14;
else
insn->reloc = M98K_RELOC_LO16;
param += 5;
}
saveptr = input_line_pointer;
input_line_pointer = param;
seg = expression(&insn->exp);
try_to_make_absolute(&insn->exp);
seg = insn->exp.X_seg;
saveparam = input_line_pointer;
input_line_pointer = saveptr;
*end = '(';
if(insn->reloc != NO_RELOC){
if(*saveparam != ')' || ++saveparam != end)
return(NULL);
}
else{
if(saveparam != end)
return(NULL);
val = insn->exp.X_add_number;
if(seg != SEG_ABSOLUTE){
error_param_message = "Parameter error: expression must be "
"absolute (parameter %lu)";
return(NULL);
}
if(val & 0x8000){
if((val & 0xffff0000) != 0xffff0000){
error_param_message = "Parameter error: expression out of "
"range (parameter %lu)";
return(NULL);
}
val = val & 0xffff;
}
else{
if((val & 0xffff0000) != 0){
error_param_message = "Parameter error: expression out of "
"range (parameter %lu)";
return(NULL);
}
}
if(format->ops[parcnt].type == DS){
if((val & 0x3) != 0){
error_param_message = "Parameter error: expression must be "
"a multiple of 4 (parameter %lu)";
return(NULL);
}
val >>= 2;
}
insn->opcode |= val << format->ops[parcnt].offset;
}
return(saveparam);
}
static
char *
parse_immediate(
char *param,
struct m98k_insn *insn,
struct m98k_opcode *format,
int parcnt)
{
unsigned long val;
char *saveptr, *saveparam;
segT seg;
/*
* The expression may have one of the following: hi16(exp), ha16(exp),
* or lo16(exp) around the expression which determines the relocation
* type.
*/
if(strncmp(param,"hi16(",5) == 0){
insn->reloc = M98K_RELOC_HI16;
param += 5;
}
else if(strncmp(param,"ha16(",5) == 0){
insn->reloc = M98K_RELOC_HA16;
param += 5;
}
else if(strncmp(param,"lo16(",5) == 0){
if(format->ops[parcnt].type == DS)
insn->reloc = M98K_RELOC_LO14;
else
insn->reloc = M98K_RELOC_LO16;
param += 5;
}
saveptr = input_line_pointer;
input_line_pointer = param;
seg = expression(&insn->exp);
try_to_make_absolute(&insn->exp);
seg = insn->exp.X_seg;
saveparam = input_line_pointer;
input_line_pointer = saveptr;
if(insn->reloc != NO_RELOC){
if(*saveparam != ')')
return(NULL);
saveparam++;
if(*saveparam == '\0'){
if(parcnt == 4 || format->ops[parcnt+1].type == NONE)
return(saveparam);
else
return(NULL);
}
else if(*saveparam == ','){
if(parcnt != 4 && format->ops[parcnt+1].type != NONE)
return(saveparam+1);
else
return(NULL);
}
else
return(NULL);
}
else{
val = insn->exp.X_add_number;
if(seg != SEG_ABSOLUTE){
error_param_message = "Parameter error: expression must be "
"absolute (parameter %lu)";
return(NULL);
}
if(format->ops[parcnt].type == SI){
if(val & 0x8000){
if((val & 0xffff0000) != 0xffff0000){
error_param_message = "Parameter error: expression out "
"of range (parameter %lu)";
return(NULL);
}
val = val & 0xffff;
}
else{
if((val & 0xffff0000) != 0){
error_param_message = "Parameter error: expression out "
"of range (parameter %lu)";
return(NULL);
}
}
}
else{
if((val & 0xffff0000) != 0){
error_param_message = "Parameter error: expression out "
"of range (parameter %lu)";
return(NULL);
}
}
if(*saveparam == '\0'){
if(parcnt == 4 || format->ops[parcnt+1].type == NONE){
insn->opcode |= val << format->ops[parcnt].offset;
return(saveparam);
}
else
return(NULL);
}
else if(*saveparam == ','){
if(parcnt != 4 && format->ops[parcnt+1].type != NONE){
insn->opcode |= val << format->ops[parcnt].offset;
return(saveparam+1);
}
else
return(NULL);
}
else
return(NULL);
}
return(saveparam);
}
static
char *
parse_branch(
char *param,
struct m98k_insn *insn,
struct m98k_opcode *format,
int parcnt)
{
char *saveptr, *saveparam;
segT seg;
saveptr = input_line_pointer;
input_line_pointer = param;
seg = expression(&insn->exp);
try_to_make_absolute(&insn->exp);
seg = insn->exp.X_seg;
saveparam = input_line_pointer;
input_line_pointer = saveptr;
insn->pcrel = 0;
insn->pcrel_reloc = 0;
if(format->ops[parcnt].type == PCREL){
/*
* 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']))
insn->pcrel_reloc = 1;
else
insn->pcrel_reloc = 0;
insn->pcrel = 1;
}
switch(format->ops[parcnt].width){
case 14:
insn->reloc = M98K_RELOC_BR14;
break;
case 24:
insn->reloc = M98K_RELOC_BR24;
break;
default:
as_fatal("Unknown branch instruction width %d",
format->ops[parcnt].width);
break;
}
return(saveparam);
}
static
char *
parse_reg(
char *reg_name,
char *param,
struct m98k_insn *insn,
struct m98k_opcode *format,
unsigned long parcnt)
{
unsigned long val, d;
d = 0;
if(*param == '(' && parcnt == 2 &&
(format->ops[1].type == D || format->ops[1].type == DS)){
d = 1;
param++;
}
if(format->ops[parcnt].type == G0REG && *param == '0'){
val = 0;
param++;
}
else{
val = 0;
while(*reg_name){
if(*param++ != *reg_name++)
return(NULL);
}
if(!isdigit(*param))
return(NULL);
while(isdigit(*param))
if((val = val * 10 + *param++ - '0') >=
1 << format->ops[parcnt].width)
return(NULL);
if(format->ops[parcnt].type == G0REG && val == 0){
error_param_message = "Parameter error: r0 not allowed "
"for parameter %lu (code as 0 not r0)";
return(NULL);
}
}
if(*param == '\0'){
if(parcnt == 4 || format->ops[parcnt+1].type == NONE){
insn->opcode |= val << format->ops[parcnt].offset;
return(param);
}
else
return(NULL);
}
else if(*param == ','){
if(parcnt != 4 && format->ops[parcnt+1].type != NONE){
insn->opcode |= val << format->ops[parcnt].offset;
return(param+1);
}
else
return(NULL);
}
else if(d == 1 && *param == ')' && param[1] == '\0'){
if(parcnt == 4 || format->ops[parcnt+1].type == NONE){
insn->opcode |= val << format->ops[parcnt].offset;
return(++param);
}
else
return(NULL);
}
return(NULL);
}
static
char *
parse_spreg(
char *param,
struct m98k_insn *insn,
struct m98k_opcode *format,
unsigned long parcnt)
{
int val;
unsigned long i;
char *saveptr, save_c;
expressionS exp;
segT seg;
saveptr = input_line_pointer;
input_line_pointer = param;
while(*param != ',' && *param != '\0')
param++;
save_c = *param;
*param = '\0';
seg = SEG_ABSOLUTE;
val = 0;
for(i = 0; *special_registers[i].name != '\0'; i++){
if(strcmp(input_line_pointer, special_registers[i].name) == 0){
val = special_registers[i].number;
break;
}
}
if(*special_registers[i].name == '\0'){
seg = expression(&exp);
try_to_make_absolute(&exp);
seg = exp.X_seg;
val = exp.X_add_number;
}
*param = save_c;
input_line_pointer = saveptr;
if(seg != SEG_ABSOLUTE){
error_param_message = "Parameter error: expression must be "
"absolute (parameter %lu)";
return(NULL);
}
if(val > 1024 || val < 0){
error_param_message = "Parameter error: expression out "
"of range (parameter %lu)";
return(NULL);
}
val = ((val & 0x1f) << 5) | ((val >> 5) & 0x1f);
if(*param == '\0'){
if(parcnt == 4 || format->ops[parcnt+1].type == NONE){
insn->opcode |= val << format->ops[parcnt].offset;
return(param);
}
else
return(NULL);
}
else if(*param == ','){
if(parcnt != 4 && format->ops[parcnt+1].type != NONE){
insn->opcode |= val << format->ops[parcnt].offset;
return(param+1);
}
else
return(NULL);
}
return(NULL);
}
static
char *
parse_bcnd(
char *param,
struct m98k_insn *insn,
struct m98k_opcode *format,
unsigned long parcnt)
{
int val;
unsigned long i, j;
char *saveptr, save_c, *plus, save_plus;
expressionS exp;
segT seg;
saveptr = input_line_pointer;
input_line_pointer = param;
while(*param != ',' && *param != '\0')
param++;
save_c = *param;
*param = '\0';
/*
* look for "[CR_field+]condition_symbol".
*/
val = -1;
for(plus = input_line_pointer; *plus != '+' && *plus != '\0'; plus++)
;
if(*plus == '+'){
save_plus = *plus;
*plus = '\0';
for(i = 0; *CR_fields[i].name != '\0'; i++)
if(strcmp(input_line_pointer, CR_fields[i].name) == 0)
break;
*plus = save_plus;
if(*CR_fields[i].name != '\0'){
for(j = 0; *condition_symbols[j].name != '\0'; j++)
if(strcmp(plus+1, condition_symbols[j].name) == 0)
break;
if(*condition_symbols[j].name != '\0'){
val = CR_fields[i].value + condition_symbols[j].value;
}
}
}
else{
for(i = 0; *condition_symbols[i].name != '\0'; i++)
if(strcmp(input_line_pointer, condition_symbols[i].name) == 0)
break;
if(*condition_symbols[i].name != '\0')
val = condition_symbols[i].value;
}
if(val == -1){
seg = expression(&exp);
try_to_make_absolute(&exp);
seg = exp.X_seg;
val = exp.X_add_number;
if(seg != SEG_ABSOLUTE){
error_param_message = "Parameter error: expression must be "
"absolute (parameter %lu)";
*param = save_c;
input_line_pointer = saveptr;
return(NULL);
}
if(val >= (1 << format->ops[parcnt].width) || val < 0){
error_param_message = "Parameter error: expression out "
"of range (parameter %lu)";
*param = save_c;
input_line_pointer = saveptr;
return(NULL);
}
}
*param = save_c;
input_line_pointer = saveptr;
if(*param == '\0'){
if(parcnt == 4 || format->ops[parcnt+1].type == NONE){
insn->opcode |= val << format->ops[parcnt].offset;
return(param);
}
else
return(NULL);
}
else if(*param == ','){
if(parcnt != 4 && format->ops[parcnt+1].type != NONE){
insn->opcode |= val << format->ops[parcnt].offset;
return(param+1);
}
else
return(NULL);
}
return(NULL);
}
static
char *
parse_crf(
char *param,
struct m98k_insn *insn,
struct m98k_opcode *format,
unsigned long parcnt)
{
int val;
unsigned long i;
char *saveptr, save_c;
expressionS exp;
segT seg;
saveptr = input_line_pointer;
input_line_pointer = param;
while(*param != ',' && *param != '\0')
param++;
save_c = *param;
*param = '\0';
val = -1;
for(i = 0; *CR_fields[i].name != '\0'; i++){
if(strcmp(input_line_pointer, CR_fields[i].name) == 0){
val = CR_fields[i].value;
break;
}
}
if(val == -1){
if(format->ops[parcnt].type == CRFONLY){
*param = save_c;
input_line_pointer = saveptr;
return(NULL);
}
seg = expression(&exp);
try_to_make_absolute(&exp);
seg = exp.X_seg;
val = exp.X_add_number;
if(seg != SEG_ABSOLUTE){
error_param_message = "Parameter error: expression must be "
"absolute (parameter %lu)";
*param = save_c;
input_line_pointer = saveptr;
return(NULL);
}
if(val >= (1 << format->ops[parcnt].width) || val < 0){
error_param_message = "Parameter error: expression out "
"of range (parameter %lu)";
*param = save_c;
input_line_pointer = saveptr;
return(NULL);
}
}
*param = save_c;
input_line_pointer = saveptr;
if(*param == '\0'){
if(parcnt == 4 || format->ops[parcnt+1].type == NONE){
insn->opcode |= val << format->ops[parcnt].offset;
return(param);
}
else
return(NULL);
}
else if(*param == ','){
if(parcnt != 4 && format->ops[parcnt+1].type != NONE){
insn->opcode |= val << format->ops[parcnt].offset;
return(param+1);
}
else
return(NULL);
}
return(NULL);
}
static
char *
parse_num(
char *param,
struct m98k_insn *insn,
struct m98k_opcode *format,
unsigned long parcnt,
long max_width_zero)
{
int val;
char *saveptr, save_c;
expressionS exp;
segT seg;
saveptr = input_line_pointer;
input_line_pointer = param;
while(*param != ',' && *param != '\0')
param++;
save_c = *param;
*param = '\0';
seg = expression(&exp);
try_to_make_absolute(&exp);
seg = exp.X_seg;
*param = save_c;
input_line_pointer = saveptr;
val = exp.X_add_number;
if(seg != SEG_ABSOLUTE){
error_param_message = "Parameter error: expression must be "
"absolute (parameter %lu)";
return(NULL);
}
if(max_width_zero){
if(val == (1 << format->ops[parcnt].width))
val = 0;
}
if(val >= (1 << format->ops[parcnt].width) || val < 0){
error_param_message = "Parameter error: expression out "
"of range (parameter %lu)";
return(NULL);
}
if(*param == '\0'){
if(parcnt == 4 || format->ops[parcnt+1].type == NONE){
insn->opcode |= val << format->ops[parcnt].offset;
return(param);
}
else
return(NULL);
}
else if(*param == ','){
if(parcnt != 4 && format->ops[parcnt+1].type != NONE){
insn->opcode |= val << format->ops[parcnt].offset;
return(param+1);
}
else
return(NULL);
}
return(NULL);
}
static
char *
parse_sh(
char *param,
struct m98k_insn *insn,
struct m98k_opcode *format,
unsigned long parcnt)
{
int val;
char *saveptr, save_c;
expressionS exp;
segT seg;
saveptr = input_line_pointer;
input_line_pointer = param;
while(*param != ',' && *param != '\0')
param++;
save_c = *param;
*param = '\0';
seg = expression(&exp);
try_to_make_absolute(&exp);
seg = exp.X_seg;
*param = save_c;
input_line_pointer = saveptr;
val = exp.X_add_number;
if(seg != SEG_ABSOLUTE){
error_param_message = "Parameter error: expression must be "
"absolute (parameter %lu)";
return(NULL);
}
if(val == 64)
val = 0;
if(val >= 64 || val < 0){
error_param_message = "Parameter error: expression out "
"of range (parameter %lu)";
return(NULL);
}
if(*param == '\0'){
if(parcnt == 4 || format->ops[parcnt+1].type == NONE){
insn->opcode |= (val & 0x1f) << 11;
insn->opcode |= ((val >> 5) & 0x1) << 1;
return(param);
}
else
return(NULL);
}
else if(*param == ','){
if(parcnt != 4 && format->ops[parcnt+1].type != NONE){
insn->opcode |= (val & 0x1f) << 11;
insn->opcode |= ((val >> 5) & 0x1) << 1;
return(param+1);
}
else
return(NULL);
}
return(NULL);
}
static
char *
parse_mb(
char *param,
struct m98k_insn *insn,
struct m98k_opcode *format,
unsigned long parcnt)
{
int val;
char *saveptr, save_c;
expressionS exp;
segT seg;
saveptr = input_line_pointer;
input_line_pointer = param;
while(*param != ',' && *param != '\0')
param++;
save_c = *param;
*param = '\0';
seg = expression(&exp);
try_to_make_absolute(&exp);
seg = exp.X_seg;
*param = save_c;
input_line_pointer = saveptr;
val = exp.X_add_number;
if(seg != SEG_ABSOLUTE){
error_param_message = "Parameter error: expression must be "
"absolute (parameter %lu)";
return(NULL);
}
if(val > 64 || val < 0){
error_param_message = "Parameter error: expression out "
"of range (parameter %lu)";
return(NULL);
}
if(*param == '\0'){
if(parcnt == 4 || format->ops[parcnt+1].type == NONE){
insn->opcode |= (val & 0x1f) << 6;
insn->opcode |= ((val >> 5) & 0x1) << 5;
return(param);
}
else
return(NULL);
}
else if(*param == ','){
if(parcnt != 4 && format->ops[parcnt+1].type != NONE){
insn->opcode |= (val & 0x1f) << 6;
insn->opcode |= ((val >> 5) & 0x1) << 5;
return(param+1);
}
else
return(NULL);
}
return(NULL);
}
/*
* md_number_to_chars() is the target machine dependent routine that puts out
* a binary value of size 4, 2, or 1 bytes into the specified buffer. This is
* done in the target machine's byte sex. In this case the byte order is
* big endian.
*/
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();
}
}
/*
* md_number_to_imm() is the target machine dependent routine that puts out
* a binary value of size 4, 2, or 1 bytes into the specified buffer with
* reguard to a possible relocation entry (the fixP->fx_r_type field in the fixS
* structure pointed to by fixP) for the section with the ordinal nsect. This
* is done in the target machine's byte sex using it's relocation types.
* In this case the byte order is big endian.
*/
void
md_number_to_imm(
unsigned char *buf,
long val,
int nbytes,
fixS *fixP,
int nsect)
{
unsigned long opcode;
if(fixP->fx_r_type == NO_RELOC ||
fixP->fx_r_type == M98K_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){
case M98K_RELOC_HI16:
buf[2] = val >> 24;
buf[3] = val >> 16;
break;
case M98K_RELOC_LO16:
buf[2] = val >> 8;
buf[3] = val;
break;
case M98K_RELOC_HA16:
val += 0x00008000;
buf[2] = val >> 24;
buf[3] = val >> 16;
break;
case M98K_RELOC_LO14:
buf[2] = val >> 8;
buf[3] = val & 0xfc;
break;
case M98K_RELOC_BR14:
if((val & 0x00008000) != 0){
opcode = buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3];
if(((opcode) & 0x03e00000) != 0x02800000){
opcode ^= Y_BIT;
buf[0] = opcode >> 24;
buf[1] = opcode >> 16;
buf[2] = opcode >> 8;
buf[3] = opcode;
}
}
if(fixP->fx_pcrel)
val += 4;
buf[2] = val >> 8;
buf[3] |= val & 0xfc;
break;
case M98K_RELOC_BR24:
if(fixP->fx_pcrel)
val += 4;
buf[0] |= (val >> 24) & 0x03;
buf[1] = val >> 16;
buf[2] = val >> 8;
buf[3] |= val & 0xfc;
break;
default:
as_warn("Bad relocation type");
break;
}
}
/*
* md_atof() turns a string pointed to by 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 indirectly through *sizeP.
* An error message is returned, or a string containg only a '\0' for OK.
* For this machine only IEEE single and IEEE double floating-point formats
* are allowed.
*/
char *
md_atof(
int type,
char *litP,
int *sizeP)
{
int prec;
LITTLENUM_TYPE words[6];
LITTLENUM_TYPE *wordP;
char *t;
switch(type){
case 'f':
case 'F':
case 's':
case 'S':
prec = 2;
break;
case 'd':
case 'D':
case 'r':
case 'R':
prec = 4;
break;
default:
*sizeP = 0;
return("Bad call to MD_ATOF()");
}
t = atof_ieee(input_line_pointer, type, words);
if(t != NULL)
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 ""; /* OK */
}
int
md_estimate_size_before_relax(
fragS *fragP,
int segment_type)
{
as_warn("Relaxation should never occur");
return(sizeof(long));
}
const relax_typeS md_relax_table[] = { {0} };
void
md_convert_frag(
fragS *fragP)
{
as_warn("Relaxation should never occur");
}