/* sparc.c -- Assemble for the SPARC
Copyright (C) 1989 Free Software Foundation, Inc.
This file is 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)
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 "sparc-opcode.h"
#include "struc-symbol.h"
void md_number_to_chars();
void md_create_short_jump();
void md_create_long_jump();
int md_estimate_size_before_relax();
void md_number_to_disp();
void md_number_to_field();
const relax_typeS md_relax_table
[] = { 0 };
/* handle of the OPCODE hash table */
static struct hash_control
*op_hash
= NULL
;
static void s_seg(), s_proc(), s_data1(), s_reserve(), s_common();
extern void s_globl(), s_long(), s_short(), s_space(), cons();
{ "common", s_common
, 0 },
{ "global", s_globl
, 0 },
{ "reserve", s_reserve
, 0 },
int md_short_jump_size
= 4;
int md_long_jump_size
= 4;
int omagic
= (0x103 << 16) | OMAGIC
; /* Magic number for header */
/* This array holds the chars that always start a comment. If the
pre-processor is disabled, these aren't very useful */
char comment_chars
[] = "!"; /* JF removed '|' from comment_chars */
/* This array holds the chars that only start a comment at the beginning of
a line. If the line seems to have the form '# 123 filename'
.line and .file directives will appear in the pre-processed output */
/* Note that input_file.c hand checks for '#' at the beginning of the
first line of the input file. This is because the compiler outputs
#NO_APP at the beginning of its output. */
/* Also note that '/*' will always start a comment */
char line_comment_chars
[] = "#";
/* Chars that can be used to separate mant from exp in floating point nums */
/* Chars that mean this number is a floating point constant */
char FLT_CHARS
[] = "rRsSfFdDxXpP";
/* Also be aware that MAXIMUM_NUMBER_OF_CHARS_FOR_FLOAT may have to be
changed in read.c . Ideally it shouldn't have to know about it at all,
but nothing is ideal around here.
int size_reloc_info
= sizeof(struct reloc_info_sparc
);
static unsigned char octal
[256];
#define isoctal(c) octal[c]
static unsigned char toHex
[256];
* anull bit - causes the branch delay slot instructions to not be executed
static void print_insn(struct sparc_it
*insn
);
static int getExpression(char *str
);
static void print_insn();
static int getExpression();
#define SPECIAL_CASE_SET 1
name
= input_line_pointer
;
if ( * input_line_pointer
!= ',' ) {
as_warn("Expected comma after name");
if ((temp
= get_absolute_expression()) < 0) {
as_warn("BSS length (%d.) <0! Ignored.", temp
);
symbolP
= symbol_find_or_make(name
);
if (strncmp(input_line_pointer
, ",\"bss\"", 6) != 0) {
as_warn("bad .reserve segment: `%s'", input_line_pointer
);
if (symbolP
->sy_other
== 0
&& ((symbolP
->sy_type
== N_BSS
&& symbolP
->sy_value
== local_bss_counter
)
|| ((symbolP
->sy_type
& N_TYPE
) == N_UNDF
&& symbolP
->sy_value
== 0))) {
symbolP
->sy_value
= local_bss_counter
;
symbolP
->sy_type
= N_BSS
;
symbolP
->sy_frag
= & bss_address_frag
;
local_bss_counter
+= temp
;
as_warn( "Ignoring attempt to re-define symbol from %d. to %d.",
symbolP
->sy_value
, local_bss_counter
);
demand_empty_rest_of_line();
register symbolS
* symbolP
;
name
= input_line_pointer
;
/* just after name is now '\0' */
if ( * input_line_pointer
!= ',' ) {
as_warn("Expected comma after symbol-name");
input_line_pointer
++; /* skip ',' */
if ( (temp
= get_absolute_expression ()) < 0 ) {
as_warn(".COMMon length (%d.) <0! Ignored.", temp
);
symbolP
= symbol_find_or_make (name
);
if ( (symbolP
->sy_type
& N_TYPE
) != N_UNDF
||
symbolP
->sy_other
!= 0 || symbolP
->sy_desc
!= 0) {
as_warn( "Ignoring attempt to re-define symbol");
if (symbolP
->sy_value
!= temp
) {
as_warn( "Length of .comm \"%s\" is already %d. Not changed to %d.",
symbolP
->sy_name
, symbolP
->sy_value
, temp
);
symbolP
->sy_value
= temp
;
symbolP
->sy_type
|= N_EXT
;
know(symbolP
->sy_frag
== &zero_address_frag
);
if (strncmp(input_line_pointer
, ",\"bss\"", 6) != 0) {
as_warn("bad .common segment: `%s'", input_line_pointer
);
demand_empty_rest_of_line();
if (strncmp(input_line_pointer
, "\"text\"", 6) == 0) {
if (strncmp(input_line_pointer
, "\"data\"", 6) == 0) {
if (strncmp(input_line_pointer
, "\"data1\"", 7) == 0) {
as_warn("Unknown segment type");
demand_empty_rest_of_line();
demand_empty_rest_of_line();
extern char is_end_of_line
[];
while (!is_end_of_line
[*input_line_pointer
]) {
/* This function is called once, at assembler startup time. It should
set up all the tables, etc. that the MD part of the assembler will need. */
register char *retval
= NULL
;
register unsigned int i
= 0;
as_fatal("Virtual memory exhausted");
const char *name
= sparc_opcodes
[i
].name
;
retval
= hash_insert(op_hash
, name
, &sparc_opcodes
[i
]);
if(retval
!= NULL
&& *retval
!= '\0')
fprintf (stderr
, "internal error: can't hash `%s': %s\n",
sparc_opcodes
[i
].name
, retval
);
if (sparc_opcodes
[i
].match
& sparc_opcodes
[i
].lose
)
fprintf (stderr
, "internal error: losing opcode: `%s' \"%s\"\n",
sparc_opcodes
[i
].name
, sparc_opcodes
[i
].args
);
&& !strcmp(sparc_opcodes
[i
].name
, name
));
as_fatal ("Broken assembler. No assembly attempted.");
for (i
= '0'; i
< '8'; ++i
)
for (i
= '0'; i
<= '9'; ++i
)
for (i
= 'a'; i
<= 'f'; ++i
)
for (i
= 'A'; i
<= 'F'; ++i
)
md_number_to_chars(toP
, the_insn
.opcode
, 4);
/* put out the symbol-dependent stuff */
if (the_insn
.reloc
!= NO_RELOC
) {
frag_now
, /* which frag */
(toP
- frag_now
->fr_literal
), /* where */
the_insn
.exp
.X_add_symbol
,
the_insn
.exp
.X_subtract_symbol
,
the_insn
.exp
.X_add_number
,
assert(the_insn
.reloc
== RELOC_HI22
);
rsd
= (the_insn
.opcode
>> 25) & 0x1f;
the_insn
.opcode
= 0x80102000 | (rsd
<< 25) | (rsd
<< 14);
md_number_to_chars(toP
, the_insn
.opcode
, 4);
frag_now
, /* which frag */
(toP
- frag_now
->fr_literal
), /* where */
the_insn
.exp
.X_add_symbol
,
the_insn
.exp
.X_subtract_symbol
,
the_insn
.exp
.X_add_number
,
struct sparc_opcode
*insn
;
for (s
= str
; islower(*s
) || (*s
>= '0' && *s
<= '3'); ++s
)
as_warn("Unknown opcode: `%s'", str
);
if ((insn
= (struct sparc_opcode
*) hash_find(op_hash
, str
)) == NULL
) {
as_warn("Unknown opcode: `%s'", str
);
bzero(&the_insn
, sizeof(the_insn
));
the_insn
.reloc
= NO_RELOC
;
* Build the opcode, checking as we go to make
* sure that the operands match
for (args
= insn
->args
; ; ++args
) {
case '\0': /* end of args */
case '[': /* these must match exactly */
case '#': /* must be at least one digit */
case 'C': /* coprocessor state register */
if (strncmp(s
, "%csr", 4) == 0) {
case 'b': /* next operand is a coprocessor register */
if (*s
++ == '%' && *s
++ == 'c' && isdigit(*s
)) {
mask
= 10 * (mask
- '0') + (*s
++ - '0');
case 'r': /* next operand must be a register */
case 'f': /* frame pointer */
case 'g': /* global register */
case 'i': /* in register */
case 'l': /* local register */
case 'o': /* out register */
case 's': /* stack pointer */
case 'r': /* any register */
if (!isdigit(c
= *s
++)) {
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
if ((c
= 10 * (c
- '0') + (*s
++ - '0')) >= 32) {
* Got the register, now figure out where
opcode
|= (mask
<< 25) | (mask
<< 14);
case 'e': /* next operand is a floating point register */
if (*s
++ == '%' && *s
++ == 'f' && isdigit(*s
)) {
mask
= 10 * (mask
- '0') + (*s
++ - '0');
if (strncmp(s
, "%fsr", 4) == 0) {
case 'h': /* high 22 bits */
the_insn
.reloc
= RELOC_HI22
;
case 'l': /* 22 bit PC relative immediate */
the_insn
.reloc
= RELOC_WDISP22
;
case 'L': /* 30 bit immediate */
the_insn
.reloc
= RELOC_WDISP30
;
case 'i': /* 13 bit immediate */
the_insn
.reloc
= RELOC_BASE13
;
if ((c
= s
[1]) == 'h' && s
[2] == 'i') {
the_insn
.reloc
= RELOC_HI22
;
} else if (c
== 'l' && s
[2] == 'o') {
the_insn
.reloc
= RELOC_LO10
;
/* Note that if the getExpression() fails, we will still have
created U entries in the symbol table for the 'symbols'
in the input string. Try not to create U symbols for
/* This stuff checks to see if the expression ends
in +%reg If it does, it removes the register from
the expression, and re-sets 's' to point to the
for(s1
=s
;*s1
&& *s1
!=','&& *s1
!=']';s1
++)
if(s1
!=s
&& isdigit(s1
[-1])) {
if(s1
[-2]=='%' && s1
[-3]=='+') {
} else if(index("goli0123456789",s1
[-2]) && s1
[-3]=='%' && s1
[-4]=='+') {
case 'A': /* alternate space */
if (strncmp(s
, "%psr", 4) == 0) {
case 'q': /* floating point queue */
if (strncmp(s
, "%fq", 3) == 0) {
case 'Q': /* coprocessor queue */
if (strncmp(s
, "%cq", 3) == 0) {
if (strcmp(str
, "set") == 0) {
special_case
= SPECIAL_CASE_SET
;
if (strncmp(s
, "%tbr", 4) != 0)
if (strncmp(s
, "%wim", 4) != 0)
if (strncmp(s
, "%y", 2) != 0)
if (&insn
[1] - sparc_opcodes
< NUMOPCODES
&& !strcmp(insn
->name
, insn
[1].name
))
as_warn("Illegal operands");
the_insn
.opcode
= opcode
;
save_in
= input_line_pointer
;
input_line_pointer
= str
;
switch (seg
= expression(&the_insn
.exp
)) {
the_insn
.error
= "bad segment";
expr_end
= input_line_pointer
;
input_line_pointer
=save_in
;
expr_end
= input_line_pointer
;
input_line_pointer
= save_in
;
This is identical to the md_atof in m68k.c. I think this is right,
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.
/* Equal to MAX_PRECISION in atof-ieee.c */
LITTLENUM_TYPE words
[MAX_LITTLENUMS
];
return "Bad call to MD_ATOF()";
t
=atof_ieee(input_line_pointer
,type
,words
);
*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 */
md_number_to_chars(buf
,val
,n
)
md_number_to_imm(buf
,val
,n
, fixP
, seg_type
)
/* if (seg_type != N_TEXT || fixP->fx_r_type == NO_RELOC) { */
if ( (seg_type
!= N_TEXT
&& fixP
->fx_r_type
> RELOC_DISP32
)
|| fixP
->fx_r_type
== NO_RELOC
) {
/* should never get here for current relocs ... */
assert(fixP
->fx_r_type
< NO_RELOC
);
* This is a hack. There should be a better way to
if (fixP
->fx_r_type
== RELOC_WDISP30
&& fixP
->fx_addsy
) {
val
+= fixP
->fx_where
+ fixP
->fx_frag
->fr_address
;
switch (fixP
->fx_r_type
) {
buf
[0] = 0; /* val >> 24; */
buf
[1] = 0; /* val >> 16; */
buf
[2] = 0; /* val >> 8; */
case RELOC_8
: /* These don't seem to ever be needed. */
buf
[0] |= (val
>> 24) & 0x3f;
buf
[1] |= (val
>> 26) & 0x3f;
buf
[2] |= (val
>> 8) & 0x03;
buf
[2] |= (val
>> 8) & 0x1f;
buf
[1] |= (val
>> 16) & 0x3f;
as_warn("bad relocation type: 0x%02x", fixP
->fx_r_type
);
/* should never be called for sparc */
md_create_short_jump(ptr
, from_addr
, to_addr
, frag
, to_symbol
)
fprintf(stderr
, "sparc_create_short_jmp\n");
/* should never be called for sparc */
md_number_to_disp(buf
,val
,n
)
fprintf(stderr
, "md_number_to_disp\n");
/* should never be called for sparc */
md_number_to_field(buf
,val
,fix
)
fprintf(stderr
, "sparc_number_to_field\n");
/* the bit-field entries in the relocation_info struct plays hell
with the byte-order problems of cross-assembly. So as a hack,
I added this mach. dependent ri twiddler. Ugly, but it gets
/* on sparc: first 4 bytes are normal unsigned long address, next three
bytes are index, most sig. byte first. Byte 7 is broken up with
bit 7 as external, bits 6 & 5 unused, and the lower
five bits as relocation type. Next 4 bytes are long int addend. */
/* Thanx and a tip of the hat to Michael Bloom, mb@ttidca.tti.com */
struct reloc_info_sparc
*ri_p
, ri
;
unsigned char the_bytes
[sizeof(*ri_p
)];
md_number_to_chars(the_bytes
, ri
.r_address
, sizeof(ri
.r_address
));
the_bytes
[4] = (ri
.r_index
>> 16) & 0x0ff;
the_bytes
[5] = (ri
.r_index
>> 8) & 0x0ff;
the_bytes
[6] = ri
.r_index
& 0x0ff;
the_bytes
[7] = ((ri
.r_extern
<< 7) & 0x80) | (0 & 0x60) | (ri
.r_type
& 0x1F);
md_number_to_chars(&the_bytes
[8], ri
.r_addend
, sizeof(ri
.r_addend
));
/* now put it back where you found it, Junior... */
bcopy (the_bytes
, (char *)ri_p
, sizeof(*ri_p
));
/* should never be called for sparc */
fprintf(stderr
, "sparc_convert_frag\n");
/* should never be called for sparc */
md_create_long_jump(ptr
, from_addr
, to_addr
, frag
, to_symbol
)
fprintf(stderr
, "sparc_create_long_jump\n");
/* should never be called for sparc */
md_estimate_size_before_relax(fragP
, segtype
)
fprintf(stderr
, "sparc_estimate_size_before_relax\n");
fprintf(stderr
, "ERROR: %s\n");
fprintf(stderr
, "opcode=0x%08x\n", insn
->opcode
);
fprintf(stderr
, "reloc = %s\n", Reloc
[insn
->reloc
]);
fprintf(stderr
, "exp = {\n");
fprintf(stderr
, "\t\tX_add_symbol = %s\n",
(insn
->exp
.X_add_symbol
->sy_name
?
insn
->exp
.X_add_symbol
->sy_name
: "???") : "0");
fprintf(stderr
, "\t\tX_sub_symbol = %s\n",
insn
->exp
.X_subtract_symbol
?
(insn
->exp
.X_subtract_symbol
->sy_name
?
insn
->exp
.X_subtract_symbol
->sy_name
: "???") : "0");
fprintf(stderr
, "\t\tX_add_number = %d\n",
* Sparc relocations are completely different, so it needs
* this machine dependent routine to emit them.
emit_relocations(fixP
, segment_address_in_file
)
relax_addressT segment_address_in_file
;
struct reloc_info_sparc ri
;
register symbolS
*symbolP
;
extern char *next_object_file_charP
;
bzero((char *) &ri
, sizeof(ri
));
for (; fixP
; fixP
= fixP
->fx_next
) {
if (fixP
->fx_r_type
>= NO_RELOC
) {
fprintf(stderr
, "fixP->fx_r_type = %d\n", fixP
->fx_r_type
);
if ((symbolP
= fixP
->fx_addsy
) != NULL
) {
ri
.r_address
= fixP
->fx_frag
->fr_address
+
fixP
->fx_where
- segment_address_in_file
;
if ((symbolP
->sy_type
& N_TYPE
) == N_UNDF
) {
ri
.r_index
= symbolP
->sy_number
;
ri
.r_index
= symbolP
->sy_type
& N_TYPE
;
if (symbolP
&& symbolP
->sy_frag
) {
ri
.r_addend
= symbolP
->sy_frag
->fr_address
;
ri
.r_type
= fixP
->fx_r_type
;
/* ri.r_addend -= fixP->fx_where; */
ri
.r_addend
-= ri
.r_address
;
ri
.r_addend
= fixP
->fx_addnumber
;
/* md_ri_to_chars((char *) &ri, ri); */
append(&next_object_file_charP
, (char *)& ri
, sizeof(ri
));
md_parse_option(argP
,cntP
,vecP
)