* This code is derived from software copyrighted by the Free Software
* Modified 1991 by Donn Seeley at UUNET Technologies, Inc.
static char sccsid
[] = "@(#)read.c 6.4 (Berkeley) 5/8/91";
/* read.c - read a source file -
Copyright (C) 1986,1987 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. */
#define MASK_CHAR (0xFF) /* If your chars aren't 8 bits, you will
change this a bit. But then, GNU isn't
spozed to run on your machine anyway.
(RMS is so shortsighted sometimes.)
#define MAXIMUM_NUMBER_OF_CHARS_FOR_FLOAT (16)
/* This is the largest known floating point */
/* format (for now). It will grow when we */
/* do 4361 style flonums. */
/* Routines that read assembler source text to build spagetti in memory. */
/* Another group of these functions is in the as-expr.c module */
#include "struc-symbol.h"
char * input_line_pointer
; /* -> next char of source file to parse. */
The following table is indexed by
[ (char) ] and will
break if
a
char does
not have exactly
256 states (hopefully
0:255!) !
const char /* used by is_... macros. our ctype[] */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* @ABCDEFGHIJKLMNO */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* PQRSTUVWXYZ[\]^_ */
0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, /* _!"#$%&'()*+,-./ */
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, /* 0123456789:;<=>? */
0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* @ABCDEFGHIJKLMNO */
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 3, /* PQRSTUVWXYZ[\]^_ */
0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* `abcdefghijklmno */
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, /* pqrstuvwxyz{|}~. */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
* Out: TRUE if this character ends a line.
const char is_end_of_line
[256] = {
_
, _
, _
, _
, _
, _
, _
, _
, _
, _
,99, _
, _
, _
, _
, _
, /* @abcdefghijklmno */
_
, _
, _
, _
, _
, _
, _
, _
, _
, _
, _
, _
, _
, _
, _
, _
, /* */
_
, _
, _
, _
, _
, _
, _
, _
, _
, _
, _
, _
, _
, _
, _
, _
, /* */
_
, _
, _
, _
, _
, _
, _
, _
, _
, _
, _
,99, _
, _
, _
, _
, /* 0123456789:;<=>? */
_
, _
, _
, _
, _
, _
, _
, _
, _
, _
, _
, _
, _
, _
, _
, _
, /* */
_
, _
, _
, _
, _
, _
, _
, _
, _
, _
, _
, _
, _
, _
, _
, _
, /* */
_
, _
, _
, _
, _
, _
, _
, _
, _
, _
, _
, _
, _
, _
, _
, _
, /* */
_
, _
, _
, _
, _
, _
, _
, _
, _
, _
, _
, _
, _
, _
, _
, _
, /* */
_
, _
, _
, _
, _
, _
, _
, _
, _
, _
, _
, _
, _
, _
, _
, _
, /* */
_
, _
, _
, _
, _
, _
, _
, _
, _
, _
, _
, _
, _
, _
, _
, _
, /* */
_
, _
, _
, _
, _
, _
, _
, _
, _
, _
, _
, _
, _
, _
, _
, _
, /* */
_
, _
, _
, _
, _
, _
, _
, _
, _
, _
, _
, _
, _
, _
, _
, _
, /* */
_
, _
, _
, _
, _
, _
, _
, _
, _
, _
, _
, _
, _
, _
, _
, _
/* */
/* Functions private to this file. */
static char* demand_copy_C_string();
static char* demand_copy_string();
void demand_empty_rest_of_line();
long int get_absolute_expression();
static char get_absolute_expression_and_terminator();
static segT
get_known_segmented_expression();
void ignore_rest_of_line();
static int is_it_end_of_statement();
static void pseudo_set();
extern char line_comment_chars
[];
static char * buffer_limit
; /* -> 1 + last char in buffer. */
static char * bignum_low
; /* Lowest char of bignum. */
static char * bignum_limit
; /* 1st illegal address of bignum. */
static char * bignum_high
; /* Highest char of bignum. */
/* May point to (bignum_start-1). */
/* Never >= bignum_limit. */
static char *old_buffer
= 0; /* JF a hack */
struct broken_word
*broken_words
;
int new_broken_words
= 0;
static void grow_bignum ();
static int next_char_of_string ();
obstack_begin( ¬es
, 5000 );
#define BIGNUM_BEGIN_SIZE (16)
bignum_low
= xmalloc((long)BIGNUM_BEGIN_SIZE
);
bignum_limit
= bignum_low
+ BIGNUM_BEGIN_SIZE
;
/* set up pseudo-op tables */
static struct hash_control
*
po_hash
= NULL
; /* use before set up: NULL-> address error */
void s_abort(), s_align(), s_comm(), s_data();
void s_desc(), s_even(), s_file(), s_fill();
void s_globl(), s_lcomm(), s_line(), s_lsym();
void s_org(), s_set(), s_space(), s_text();
void s_gdbline(), s_gdblinetab();
void s_gdbbeg(), s_gdbblock(), s_gdbend(), s_gdbsym();
static const pseudo_typeS
{ "ascii", stringer
, 0 },
{ "asciz", stringer
, 1 },
{ "double", float_cons
, 'd' },
{ "float", float_cons
, 'f' },
{ "gdbbeg", s_gdbbeg
, 0 },
{ "gdbblock", s_gdbblock
, 0 },
{ "gdbend", s_gdbend
, 0 },
{ "gdbsym", s_gdbsym
, 0 },
{ "gdbline", s_gdbline
, 0 },
{ "gdblinetab",s_gdblinetab
, 0 },
{ "octa", big_cons
, 16 },
{ "single", float_cons
, 'f' },
{ NULL
} /* end sentinel */
char * errtxt
; /* error text */
const pseudo_typeS
* pop
;
errtxt
= ""; /* OK so far */
for (pop
=potable
; pop
->poc_name
&& !*errtxt
; pop
++)
errtxt
= hash_insert (po_hash
, pop
->poc_name
, (char *)pop
);
for(pop
=md_pseudo_table
; pop
->poc_name
&& !*errtxt
; pop
++)
errtxt
= hash_insert (po_hash
, pop
->poc_name
, (char *)pop
);
as_fatal ("error constructing pseudo-op table");
* File has already been opened, and will be closed by our caller.
* We read the file, putting things into a web that
* represents what we have been reading.
read_a_source_file (buffer
)
char * buffer
; /* 1st character of each buffer of lines is here. */
register char * s
; /* string of symbol, '\0' appended */
/* register struct frag * fragP; JF unused */ /* a frag we just made */
void gdb_block_position();
void gdb_symbols_fixup();
subseg_new (SEG_TEXT
, 0);
while ( buffer_limit
= input_scrub_next_buffer (&buffer
) )
{ /* We have another line to parse. */
know( buffer_limit
[-1] == '\n' ); /* Must have a sentinel. */
input_line_pointer
= buffer
;
contin
: /* JF this goto is my fault I admit it. Someone brave please re-write
the whole input section here? Pleeze??? */
while ( input_line_pointer
< buffer_limit
)
{ /* We have more of this buffer to parse. */
* We now have input_line_pointer -> 1st char of next line.
* If input_line_pointer [-1] == '\n' then we just
* scanned another line: so bump line counters.
if (input_line_pointer
[-1] == '\n')
* We are at the begining of a line, or similar place.
* We expect a well-formed assembler statement.
* A "symbol-name:" is a statement.
* Depending on what compiler is used, the order of these tests
* may vary to catch most common case 1st.
* Each test is independent of all other tests at the (top) level.
* PLEASE make a compiler that doesn't use this assembler.
* It is crufty to waste a compiler's time encoding things for this
* assembler, which then wastes more time decoding it.
* (And communicating via (linear) files is silly!
* If you must pass stuff, please pass a tree!)
if ( (c
= * input_line_pointer
++) == '\t' || c
== ' ' || c
=='\f')
c
= * input_line_pointer
++;
know( c
!= ' ' ); /* No further leading whitespace. */
* C is the 1st significant character.
* Input_line_pointer points after that character.
if ( is_name_beginner(c
) )
{ /* want user-defined label or pseudo/opcode */
s
= -- input_line_pointer
;
c
= get_symbol_end(); /* name's delimiter */
* C is character after symbol.
* That character's place in the input line is now '\0'.
* S points to the beginning of the symbol.
* [In case of pseudo-op, s -> '.'.]
* Input_line_pointer -> '\0' where c was.
/* set line number for function definition */
colon(s
); /* user-defined label */
* input_line_pointer
++ = ':'; /* Put ':' back for error messages' sake. */
/* Input_line_pointer -> after ':'. */
else if(c
=='=' || input_line_pointer
[1]=='=') /* JF deal with FOO=BAR */
demand_empty_rest_of_line();
{ /* expect pseudo-op or machine instruction */
* WARNING: c has next char, which may be end-of-line.
* We lookup the pseudo-op table with s+1 because we
* already know that the pseudo-op begins with a '.'.
pop
= (pseudo_typeS
*) hash_find (po_hash
, s
+1);
/* Print the error msg now, while we still can */
as_bad("Unknown pseudo-op: '%s'",s
);
/* Put it back for error messages etc. */
* input_line_pointer
= c
;
/* The following skip of whitespace is compulsory. */
/* A well shaped space is sometimes all that seperates keyword from operands. */
if ( c
== ' ' || c
== '\t' )
{ /* Skip seperator after keyword. */
* Input_line is restored.
* Input_line_pointer -> 1st non-blank char
* after pseudo-operation.
(*pop
->poc_handler
)(pop
->poc_val
);
{ /* machine instruction */
/* If source file debugging, emit a stab. */
/* WARNING: c has char, which may be end-of-line. */
/* Also: input_line_pointer -> `\0` where c was. */
* input_line_pointer
= c
;
while ( ! is_end_of_line
[* input_line_pointer
] )
c
= * input_line_pointer
;
* input_line_pointer
= '\0';
md_assemble (s
); /* Assemble 1 instruction. */
* input_line_pointer
++ = c
;
/* We resume loop AFTER the end-of-line from this instruction */
} /* if (is_name_beginner(c) */
if ( is_end_of_line
[c
] )
{ /* local label ("4:") */
if( *input_line_pointer
=='$')
if ( * input_line_pointer
++ == ':' )
as_bad( "Spurious digit %d.", temp
);
if(c
&& index(line_comment_chars
,c
)) { /* Its a comment. Better say APP or NO_APP */
extern char *scrub_string
,*scrub_last_string
;
continue; /* We ignore it */
ends
=strstr(s
,"#NO_APP\n");
/* The end of the #APP wasn't in this buffer. We
keep reading in buffers until we find the #NO_APP
that goes with this #APP There is one. The specs
tmp_buf
=xmalloc(tmp_len
);
bcopy(s
,tmp_buf
,tmp_len
);
new_tmp
= input_scrub_next_buffer(&buffer
);
input_line_pointer
= buffer
;
ends
= strstr(buffer
,"#NO_APP\n");
tmp_buf
=xrealloc(tmp_buf
,tmp_len
+num
);
bcopy(buffer
,tmp_buf
+tmp_len
,num
);
input_line_pointer
= ends
? ends
+8 : NULL
;
input_line_pointer
=ends
+8;
scrub_last_string
= ends
;
ch
=do_scrub_next_char(scrub_from_string
,scrub_to_string
);
if(new_tmp
==new_buf
+new_length
) {
new_buf
=xrealloc(new_buf
,new_length
+100);
new_tmp
=new_buf
+new_length
;
old_input
=input_line_pointer
;
input_line_pointer
=new_buf
;
as_bad("Junk character %d.",c
);
} /* while (input_line_pointer<buffer_limit )*/
input_line_pointer
=old_input
;
} /* while (more bufrers to scan) */
} /* read_a_source_file() */
as_fatal(".abort detected. Abandoning ship.");
register unsigned int temp
;
register long int temp_fill
;
temp
= get_absolute_expression ();
#define MAX_ALIGNMENT (1 << 15)
if ( temp
> MAX_ALIGNMENT
) {
as_bad("Alignment too large: %d. assumed.", temp
= MAX_ALIGNMENT
);
* For the sparc, `.align (1<<n)' actually means `.align n'
* so we have to convert it.
for (i
= 0; (temp
& 1) == 0; temp
>>= 1, ++i
)
as_bad("Alignment not a power of 2");
if (*input_line_pointer
== ',') {
temp_fill
= get_absolute_expression ();
/* Only make a frag if we HAVE to. . . */
if (temp
&& ! need_pass_2
)
frag_align (temp
, (int)temp_fill
);
demand_empty_rest_of_line();
register long int temp_fill
;
temp
= get_absolute_expression ();
#define MAX_ALIGNMENT (15)
if ( temp
> MAX_ALIGNMENT
)
as_bad("Alignment too large: %d. assumed.", temp
= MAX_ALIGNMENT
);
as_bad("Alignment negative. 0 assumed.");
if ( *input_line_pointer
== ',' ) {
temp_fill
= get_absolute_expression ();
/* Only make a frag if we HAVE to. . . */
if ( temp
&& ! need_pass_2
)
frag_align (temp
, (int)temp_fill
);
demand_empty_rest_of_line();
register symbolS
* symbolP
;
name
= input_line_pointer
;
/* just after name is now '\0' */
if ( * input_line_pointer
!= ',' ) {
as_bad("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
) {
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
;
symbolP
->sy_other
= const_flag
;
know( symbolP
-> sy_frag
== &zero_address_frag
);
demand_empty_rest_of_line();
temp
= get_absolute_expression ();
subseg_new (SEG_DATA
, (subsegT
)temp
);
demand_empty_rest_of_line();
temp
= get_absolute_expression ();
subseg_new (SEG_DATA
, (subsegT
)temp
);
demand_empty_rest_of_line();
register symbolS
* symbolP
;
* Frob invented at RMS' request. Set the n_desc of a symbol.
name
= input_line_pointer
;
symbolP
= symbol_table_lookup (name
);
if ( * input_line_pointer
!= ',' ) {
as_bad("Expected comma after name \"%s\"", name
);
temp
= get_absolute_expression ();
symbolP
= symbol_find_or_make (name
);
symbolP
-> sy_desc
= temp
;
demand_empty_rest_of_line();
/* Some assemblers tolerate immediately following '"' */
if ( s
= demand_copy_string( & length
) ) {
new_logical_line (s
, -1);
demand_empty_rest_of_line();
register long int temp_fill
;
if ( get_absolute_expression_and_terminator(& temp_repeat
) != ',' ) {
input_line_pointer
--; /* Backup over what was not a ','. */
as_warn("Expect comma after rep-size in .fill");
if ( get_absolute_expression_and_terminator( & temp_size
) != ',' ) {
input_line_pointer
--; /* Backup over what was not a ','. */
as_warn("Expected comma after size in .fill");
* This is to be compatible with BSD 4.2 AS, not for any rational reason.
#define BSD_FILL_SIZE_CROCK_8 (8)
if ( temp_size
> BSD_FILL_SIZE_CROCK_8
) {
as_bad(".fill size clamped to %d.", BSD_FILL_SIZE_CROCK_8
);
temp_size
= BSD_FILL_SIZE_CROCK_8
;
as_warn("Size negative: .fill ignored.");
} else if ( temp_repeat
<= 0 ) {
as_warn("Repeat < 0, .fill ignored");
temp_fill
= get_absolute_expression ();
if ( temp_size
&& !need_pass_2
) {
p
= frag_var (rs_fill
, (int)temp_size
, (int)temp_size
, (relax_substateT
)0, (symbolS
*)0, temp_repeat
, (char *)0);
bzero (p
, (int)temp_size
);
* The magic number BSD_FILL_SIZE_CROCK_4 is from BSD 4.2 VAX flavoured AS.
* The following bizzare behaviour is to be compatible with above.
* I guess they tried to take up to 8 bytes from a 4-byte expression
* and they forgot to sign extend. Un*x Sux.
#define BSD_FILL_SIZE_CROCK_4 (4)
md_number_to_chars (p
, temp_fill
, temp_size
> BSD_FILL_SIZE_CROCK_4
? BSD_FILL_SIZE_CROCK_4
: (int)temp_size
);
* Note: .fill (),0 emits no frag (since we are asked to .fill 0 bytes)
* but emits no error message because it seems a legal thing to do.
* It is a degenerate case of .fill but could be emitted by a compiler.
demand_empty_rest_of_line();
temp
= get_absolute_expression ();
as_warn( "Block number <0. Ignored." );
gdb_block_beg ( (long int) temp
, frag_now
, (long int)(obstack_next_free(& frags
) - frag_now
-> fr_literal
));
demand_empty_rest_of_line ();
if (get_absolute_expression_and_terminator (&temp
) != ',') {
as_warn( "expected comma before position in .gdbblock");
position
= get_absolute_expression ();
gdb_block_position ((long int) temp
, (long int) position
);
demand_empty_rest_of_line ();
temp
= get_absolute_expression ();
as_warn( "Block number <0. Ignored." );
gdb_block_end ( (long int) temp
, frag_now
, (long int)(obstack_next_free(& frags
) - frag_now
-> fr_literal
));
demand_empty_rest_of_line ();
register symbolS
* symbolP
;
name
= input_line_pointer
;
symbolP
= symbol_find_or_make (name
);
if ( * input_line_pointer
!= ',' ) {
as_warn("Expected comma after name");
if ( (temp
= get_absolute_expression ()) < 0 ) {
as_warn("Bad GDB symbol file offset (%d.) <0! Ignored.", temp
);
gdb_symbols_fixup (symbolP
, (long int)temp
);
demand_empty_rest_of_line ();
if(get_absolute_expression_and_terminator(&file_number
) != ',') {
as_warn("expected comman after filenum in .gdbline");
lineno
=get_absolute_expression();
gdb_line(file_number
,lineno
);
demand_empty_rest_of_line();
if(get_absolute_expression_and_terminator(&file_number
) != ',') {
as_warn("expected comman after filenum in .gdblinetab");
offset
=get_absolute_expression();
gdb_line_tab(file_number
,offset
);
demand_empty_rest_of_line();
register symbolS
* symbolP
;
name
= input_line_pointer
;
symbolP
= symbol_find_or_make (name
);
* input_line_pointer
= c
;
symbolP
-> sy_type
|= N_EXT
;
if(*input_line_pointer
=='\n')
demand_empty_rest_of_line();
register symbolS
* symbolP
;
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 ( symbolP
-> sy_other
== 0
&& symbolP
-> sy_desc
== 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();
/* Assume delimiter is part of expression. */
/* BSD4.2 as fails with delightful bug, so we */
/* are not being incompatible here. */
new_logical_line ((char *)NULL
, (int)(get_absolute_expression ()));
demand_empty_rest_of_line();
register symbolS
*symbolP
;
/* we permit ANY expression: BSD4.2 demands constants */
name
= input_line_pointer
;
if ( * input_line_pointer
!= ',' ) {
as_warn("Expected comma after name \"%s\"", name
);
segment
= expression (& exp
);
if ( segment
!= SEG_ABSOLUTE
&& segment
!= SEG_DATA
&&
segment
!= SEG_TEXT
&& segment
!= SEG_BSS
) {
as_bad("Bad expression: %s", seg_name
[(int)segment
]);
know( segment
== SEG_ABSOLUTE
|| segment
== SEG_DATA
|| segment
== SEG_TEXT
|| segment
== SEG_BSS
);
symbolP
= symbol_new (name
,(unsigned char)(seg_N_TYPE
[(int) segment
]),
0, 0, (valueT
)(exp
. X_add_number
), & zero_address_frag
);
demand_empty_rest_of_line();
register long int temp_fill
;
* Don't believe the documentation of BSD 4.2 AS.
* There is no such thing as a sub-segment-relative origin.
* Any absolute origin is given a warning, then assumed to be segment-relative.
* Any segmented origin expression ("foo+42") had better be in the right
* segment or the .org is ignored.
* BSD 4.2 AS warns if you try to .org backwards. We cannot because we
* never know sub-segment sizes when we are reading code.
* BSD will crash trying to emit -ve numbers of filler bytes in certain
* .orgs. We don't crash, but see as-write for that code.
* Don't make frag if need_pass_2==TRUE.
segment
= get_known_segmented_expression(& exp
);
if ( *input_line_pointer
== ',' ) {
temp_fill
= get_absolute_expression ();
if (segment
!= now_seg
&& segment
!= SEG_ABSOLUTE
)
as_warn("Illegal segment \"%s\". Segment \"%s\" assumed.",
seg_name
[(int) segment
], seg_name
[(int) now_seg
]);
p
= frag_var (rs_org
, 1, 1, (relax_substateT
)0, exp
. X_add_symbol
,
exp
. X_add_number
, (char *)0);
} /* if (ok to make frag) */
demand_empty_rest_of_line();
register symbolS
*symbolP
;
* Especial apologies for the random logic:
* this just grew, and could be parsed much more simply!
name
= input_line_pointer
;
delim
= get_symbol_end();
end_name
= input_line_pointer
;
if ( * input_line_pointer
!= ',' ) {
as_warn("Expected comma after name \"%s\"", name
);
if(name
[0]=='.' && name
[1]=='\0') {
/* Turn '. = mumble' into a .org mumble */
segment
= get_known_segmented_expression(& exp
);
if (segment
!= now_seg
&& segment
!= SEG_ABSOLUTE
)
as_warn("Illegal segment \"%s\". Segment \"%s\" assumed.",
seg_name
[(int) segment
], seg_name
[(int) now_seg
]);
ptr
= frag_var (rs_org
, 1, 1, (relax_substateT
)0, exp
.X_add_symbol
,
exp
.X_add_number
, (char *)0);
} /* if (ok to make frag) */
symbolP
= symbol_find_or_make (name
);
demand_empty_rest_of_line ();
register long int temp_fill
;
/* Just like .fill, but temp_size = 1 */
if ( get_absolute_expression_and_terminator( & temp_repeat
) == ',' ) {
temp_fill
= get_absolute_expression ();
input_line_pointer
--; /* Backup over what was not a ','. */
if ( temp_repeat
<= 0 ) {
as_warn("Repeat < 0, .space ignored");
p
= frag_var (rs_fill
, 1, 1, (relax_substateT
)0, (symbolS
*)0,
demand_empty_rest_of_line();
temp
= get_absolute_expression ();
subseg_new (SEG_TEXT
, (subsegT
)temp
);
demand_empty_rest_of_line();
/*( JF was static, but can't be if machine dependent pseudo-ops are to use it */
demand_empty_rest_of_line()
if ( is_end_of_line
[* input_line_pointer
] )
/* Return having already swallowed end-of-line. */
} /* Return pointing just after end-of-line. */
ignore_rest_of_line() /* For suspect lines: gives warning. */
if ( ! is_end_of_line
[* input_line_pointer
])
as_warn("Rest of line ignored. 1st junk character valued %d (%c)."
, * input_line_pointer
, *input_line_pointer
);
while ( input_line_pointer
< buffer_limit
&& ! is_end_of_line
[* input_line_pointer
] )
input_line_pointer
++; /* Return pointing just after end-of-line. */
know( is_end_of_line
[input_line_pointer
[-1]] );
* Handle .stabX directives, which used to be open-coded.
* So much creeping featurism overloaded the semantics that we decided
* to put all .stabX thinking in one place. Here.
* We try to make any .stabX directive legal. Other people's AS will often
* do assembly-time consistency checks: eg assigning meaning to n_type bits
* and "protecting" you from setting them to certain values. (They also zero
* certain bits before emitting symbols. Tut tut.)
* If an expression is not absolute we either gripe or use the relocation
* information. Other people's assemblers silently forget information they
* don't need and invent information they need that you didn't supply.
* .stabX directives always make a symbol table entry. It may be junk if
* the rest of your .stabX directive is malformed.
register symbolS
* symbolP
;
int goof
; /* TRUE if we have aborted. */
* Enter with input_line_pointer pointing past .stabX and any following
goof
= FALSE
; /* JF who forgot this?? */
string
= demand_copy_C_string (& length
);
if (* input_line_pointer
== ',')
as_warn( "I need a comma after symbol's name" );
* Input_line_pointer->after ','. String -> symbol name.
symbolP
= symbol_new (string
, 0,0,0,0,(struct frag
*)0);
symbolP
->sy_name
= NULL
; /* .stabd feature. */
symbolP
->sy_value
= obstack_next_free(& frags
) - frag_now
->fr_literal
;
symbolP
->sy_frag
= frag_now
;
symbolP
->sy_frag
= &zero_address_frag
;
symbolP
->sy_frag
= & zero_address_frag
;
if (get_absolute_expression_and_terminator (& longint
) == ',')
symbolP
->sy_type
= saved_type
= longint
;
as_warn( "I want a comma after the n_type expression" );
input_line_pointer
--; /* Backup over a non-',' char. */
if (get_absolute_expression_and_terminator (& longint
) == ',')
symbolP
->sy_other
= longint
;
as_warn( "I want a comma after the n_other expression" );
input_line_pointer
--; /* Backup over a non-',' char. */
symbolP
->sy_desc
= get_absolute_expression ();
if (what
== 's' || what
== 'n') {
if (* input_line_pointer
!= ',') {
as_warn( "I want a comma after the n_desc expression" );
if ((! goof
) && (what
=='s' || what
=='n')) {
symbolP
->sy_type
= saved_type
;
demand_empty_rest_of_line ();
* In: Pointer to a symbol.
* Input_line_pointer -> expression.
* Out: Input_line_pointer -> just after any whitespace after expression.
* Tried to set symbol to value of expression.
* Will change sy_type, sy_value, sy_frag;
* May set need_pass_2 == TRUE.
know( symbolP
); /* NULL pointer is logic error. */
ext
=(symbolP
->sy_type
&N_EXT
);
if ((segment
= expression( & exp
)) == SEG_NONE
)
as_warn( "Missing expression: absolute 0 assumed" );
exp
. X_seg
= SEG_ABSOLUTE
;
as_warn( "%s number illegal. Absolute 0 assumed.",
exp
. X_add_number
> 0 ? "Bignum" : "Floating-Point" );
symbolP
-> sy_type
= N_ABS
| ext
;
symbolP
-> sy_frag
= & zero_address_frag
;
as_warn("No expression: Using absolute 0");
symbolP
-> sy_type
= N_ABS
| ext
;
symbolP
-> sy_frag
= & zero_address_frag
;
if (exp
.X_add_symbol
&& exp
.X_subtract_symbol
&& (exp
.X_add_symbol
->sy_type
& N_TYPE
)
== (exp
.X_subtract_symbol
->sy_type
& N_TYPE
)) {
if(exp
.X_add_symbol
->sy_frag
!= exp
.X_subtract_symbol
->sy_frag
) {
as_bad("Unknown expression: symbols %s and %s are in different frags.",exp
.X_add_symbol
->sy_name
, exp
.X_subtract_symbol
->sy_name
);
exp
.X_add_number
+=exp
.X_add_symbol
->sy_value
- exp
.X_subtract_symbol
->sy_value
;
as_warn( "Complex expression. Absolute segment assumed." );
symbolP
-> sy_type
= N_ABS
| ext
;
symbolP
-> sy_value
= exp
. X_add_number
;
symbolP
-> sy_frag
= & zero_address_frag
;
symbolP
-> sy_type
= seg_N_TYPE
[(int) segment
] | ext
;
symbolP
-> sy_value
= exp
. X_add_number
+ exp
. X_add_symbol
-> sy_value
;
symbolP
-> sy_frag
= exp
. X_add_symbol
-> sy_frag
;
case SEG_PASS1
: /* Not an error. Just try another pass. */
symbolP
->sy_forward
=exp
.X_add_symbol
;
as_warn("Unknown expression");
know( need_pass_2
== TRUE
);
symbolP
->sy_forward
=exp
.X_add_symbol
;
/* as_warn("unknown symbol"); */
/* need_pass_2 = TRUE; */
* stabs(file), stabf(func) and stabd(line) -- for the purpose of
* source file debugging of assembly files, generate file,
* function and line number stabs, respectively.
* These functions have corresponding functions named
* filestab(), funcstab() and linestab() in input-scrub.c,
* where logical files and logical line numbers are handled.
/* .stabs "file",100,0,0,. */
obstack_next_free(& frags
) - frag_now
->fr_literal
,
static int void_undefined
= 1;
/* crudely filter uninteresting labels: require an initial '_' */
/* assembly functions are assumed to have void type */
/* .stabs "void:t15=15",128,0,0,0 */
(void) symbol_new("void:t1=1",
/* .stabs "func:F1",36,0,0,. */
symbolP
= symbol_new((char *) 0,
obstack_next_free(& frags
) - frag_now
->fr_literal
,
obstack_grow(¬es
, func
, strlen(func
));
obstack_1grow(¬es
, ':');
obstack_1grow(¬es
, 'F');
obstack_1grow(¬es
, '1');
obstack_1grow(¬es
, '\0');
symbolP
->sy_name
= obstack_finish(¬es
);
(void) symbol_new((char *)0,
obstack_next_free(& frags
) - frag_now
->fr_literal
,
* CONStruct more frag of .bytes, or .words etc.
* Should need_pass_2 be TRUE then emit no frag(s).
* This understands EXPRESSIONS, as opposed to big_cons().
* This has a split personality. We use expression() to read the
* value. We can detect if the value won't fit in a byte or word.
* But we can't detect if expression() discarded significant digits
* in the case of a long. Not worth the crocks required to fix it.
cons(nbytes
) /* worker to do .byte etc statements */
/* clobbers input_line_pointer, checks */
register int nbytes
; /* 1=.byte, 2=.word, 4=.long */
register long int mask
; /* High-order bits we will left-truncate, */
/* but includes sign bit also. */
register long int get
; /* what we get */
register long int use
; /* get after truncation. */
register long int unmask
; /* what bits we will store */
* Input_line_pointer -> 1st char after pseudo-op-code and could legally
* be a end-of-line. (Or, less legally an eof - which we cope with.)
/* JF << of >= number of bits in the object is undefined. In particular
SPARC (Sun 4) has problems */
if(nbytes
>=sizeof(long int))
mask
= ~0 << (BITS_PER_CHAR
* nbytes
); /* Don't store these bits. */
unmask
= ~ mask
; /* Do store these bits. */
"Do this mod if you want every overflow check to assume SIGNED 2's complement data.";
mask
= ~ (unmask
>> 1); /* Includes sign bit now. */
* The following awkward logic is to parse ZERO or more expressions,
* comma seperated. Recall an expression includes its leading &
* trailing blanks. We fake a leading ',' if there is (supposed to
* be) a 1st expression, and keep demanding 1 expression for each ','.
if (is_it_end_of_statement())
input_line_pointer
++; /* Matches end-of-loop 'correction'. */
segment
= expression( &exp
); /* At least scan over the expression. */
{ /* Still worthwhile making frags. */
/* Don't call this if we are going to junk this pass anyway! */
know( segment
!= SEG_PASS1
);
if ( segment
== SEG_DIFFERENCE
&& exp
. X_add_symbol
== NULL
)
as_warn( "Subtracting symbol \"%s\"(segment\"%s\") is too hard. Absolute segment assumed.",
exp
. X_subtract_symbol
-> sy_name
,
seg_name
[(int) N_TYPE_seg
[exp
. X_subtract_symbol
-> sy_type
& N_TYPE
]]);
/* Leave exp . X_add_number alone. */
as_warn( "%s number illegal. Absolute 0 assumed.",
exp
. X_add_number
> 0 ? "Bignum" : "Floating-Point");
md_number_to_chars (p
, (long)0, nbytes
);
as_warn( "0 assumed for missing expression" );
know( exp
. X_add_symbol
== NULL
);
/* fall into SEG_ABSOLUTE */
get
= exp
. X_add_number
;
if ( (get
& mask
) && (get
& mask
) != mask
)
{ /* Leading bits contain both 0s & 1s. */
as_warn("Value x%x truncated to x%x.", get
, use
);
md_number_to_chars (p
, use
, nbytes
); /* put bytes in right order. */
x
=(struct broken_word
*)xmalloc(sizeof(struct broken_word
));
x
->next_broken_word
=broken_words
;
x
->sub
=exp
.X_subtract_symbol
;
x
->addnum
=exp
.X_add_number
;
/* Else Fall through into. . . */
#if defined(SPARC) || defined(I860)
fix_new (frag_now
, p
- frag_now
-> fr_literal
, nbytes
,
exp
. X_add_symbol
, exp
. X_subtract_symbol
,
exp
. X_add_number
, 0, RELOC_32
);
fix_new_ns32k (frag_now
, p
- frag_now
-> fr_literal
, nbytes
,
exp
. X_add_symbol
, exp
. X_subtract_symbol
,
exp
. X_add_number
, 0, 0, 2, 0, 0);
#if !defined(SPARC) && !defined(NS32K) && !defined(I860)
fix_new (frag_now
, p
- frag_now
-> fr_literal
, nbytes
,
exp
. X_add_symbol
, exp
. X_subtract_symbol
,
c
= * input_line_pointer
++;
input_line_pointer
--; /* Put terminator back into stream. */
demand_empty_rest_of_line();
* CONStruct more frag(s) of .quads, or .octa etc.
* Makes 0 or more new frags.
* If need_pass_2 == TRUE, generate no frag.
* This understands only bignums, not expressions. Cons() understands
* Constants recognised are '0...'(octal) '0x...'(hex) '...'(decimal).
* This creates objects with struct obstack_control objs, destroying
* any context objs held about a partially completed object. Beware!
* I think it sucks to have 2 different types of integers, with 2
* routines to read them, store them etc.
* It would be nicer to permit bignums in expressions and only
* complain if the result overflowed. However, due to "efficiency"...
big_cons(nbytes
) /* worker to do .quad etc statements */
/* clobbers input_line_pointer, checks */
register int nbytes
; /* 8=.quad 16=.octa ... */
register char c
; /* input_line_pointer -> c. */
register long int length
; /* Number of chars in an object. */
register int digit
; /* Value of 1 digit. */
register int carry
; /* For multi-precision arithmetic. */
register int work
; /* For multi-precision arithmetic. */
register char * p
; /* For multi-precision arithmetic. */
extern char hex_value
[]; /* In hex_value.c. */
* The following awkward logic is to parse ZERO or more strings,
* comma seperated. Recall an expression includes its leading &
* trailing blanks. We fake a leading ',' if there is (supposed to
* be) a 1st expression, and keep demanding 1 expression for each ','.
if (is_it_end_of_statement())
c
= * input_line_pointer
;
/* C contains 1st non-blank character of what we hope is a number. */
c
= * ++ input_line_pointer
;
c
= * ++ input_line_pointer
;
* This feature (?) is here to stop people worrying about
* mysterious zero constants: which is what they get when
* they completely omit digits.
if (hex_value
[c
] >= radix
)
as_warn( "Missing digits. 0 assumed." );
bignum_high
= bignum_low
- 1; /* Start constant with 0 chars. */
for( ; (digit
= hex_value
[c
]) < radix
; c
= * ++ input_line_pointer
)
/* Multiply existing number by radix, then add digit. */
for (p
=bignum_low
; p
<= bignum_high
; p
++)
work
= (*p
& MASK_CHAR
) * radix
+ carry
;
carry
= work
>> BITS_PER_CHAR
;
* bignum_high
= carry
& MASK_CHAR
;
know( (carry
& ~ MASK_CHAR
) == 0);
length
= bignum_high
- bignum_low
+ 1;
as_warn( "Most significant bits truncated in integer constant." );
register long int leading_zeroes
;
for(leading_zeroes
= nbytes
- length
;
bcopy (bignum_low
, p
, (int)nbytes
);
/* C contains character after number. */
c
= * input_line_pointer
;
/* C contains 1st non-blank character after number. */
demand_empty_rest_of_line();
grow_bignum() /* Extend bignum by 1 char. */
register long int length
;
if (bignum_high
>= bignum_limit
)
length
= bignum_limit
- bignum_low
;
bignum_low
= xrealloc (bignum_low
, length
+ length
);
bignum_high
= bignum_low
+ length
;
bignum_limit
= bignum_low
+ length
+ length
;
* CONStruct some more frag chars of .floats .ffloats etc.
* Makes 0 or more new frags.
* If need_pass_2 == TRUE, no frags are emitted.
* This understands only floating literals, not expressions. Sorry.
* A floating constant is defined by atof_generic(), except it is preceded
* by 0d 0f 0g or 0h. After observing the STRANGE way my BSD AS does its
* reading, I decided to be incompatible. This always tries to give you
* rounded bits to the precision of the pseudo-op. Former AS did premature
* truncatation, restored noisy bits instead of trailing 0s AND gave you
* a choice of 2 flavours of noise according to which of 2 floating-point
* scanners you directed AS to use.
* In: input_line_pointer -> whitespace before, or '0' of flonum.
void /* JF was static, but can't be if VAX.C is goning to use it */
float_cons(float_type
) /* Worker to do .float etc statements. */
/* Clobbers input_line-pointer, checks end-of-line. */
register float_type
; /* 'f':.ffloat ... 'F':.float ... */
int length
; /* Number of chars in an object. */
register char * err
; /* Error from scanning floating literal. */
char temp
[MAXIMUM_NUMBER_OF_CHARS_FOR_FLOAT
];
* The following awkward logic is to parse ZERO or more strings,
* comma seperated. Recall an expression includes its leading &
* trailing blanks. We fake a leading ',' if there is (supposed to
* be) a 1st expression, and keep demanding 1 expression for each ','.
if (is_it_end_of_statement())
++ input_line_pointer
; /* -> past termintor. */
/* input_line_pointer -> 1st char of a flonum (we hope!). */
/* Skip any 0{letter} that may be present. Don't even check if the
* letter is legal. Someone may invent a "z" format and this routine
* has no use for such information. Lusers beware: you get
* diagnostics if your input is ill-conditioned.
if(input_line_pointer
[0]=='0' && isalpha(input_line_pointer
[1]))
err
= md_atof (float_type
, temp
, &length
);
know( length
<= MAXIMUM_NUMBER_OF_CHARS_FOR_FLOAT
);
as_warn( "Bad floating literal: %s", err
);
/* Input_line_pointer -> just after end-of-line. */
c
= 0; /* Break out of loop. */
c
= * input_line_pointer
++;
/* C contains 1st non-white character after number. */
/* input_line_pointer -> just after terminator (c). */
-- input_line_pointer
; /* -> terminator (is not ','). */
demand_empty_rest_of_line();
* We read 0 or more ',' seperated, double-quoted strings.
* Caller should have checked need_pass_2 is FALSE because we don't check it.
stringer(append_zero
) /* Worker to do .ascii etc statements. */
/* Checks end-of-line. */
register int append_zero
; /* 0: don't append '\0', else 1 */
/* register char * p; JF unused */
/* register int length; JF unused */ /* Length of string we read, excluding */
/* trailing '\0' implied by closing quote. */
/* register char * where; JF unused */
/* register fragS * fragP; JF unused */
* The following awkward logic is to parse ZERO or more strings,
* comma seperated. Recall a string expression includes spaces
* before the opening '\"' and spaces after the closing '\"'.
* We fake a leading ',' if there is (supposed to be)
* a 1st, expression. We keep demanding expressions for each
if (is_it_end_of_statement())
++ input_line_pointer
; /* Compensate for end of loop. */
for ( ; c
== ','; c
= *input_line_pointer
++)
if (* input_line_pointer
== '\"')
++ input_line_pointer
; /* -> 1st char of string. */
while ( (c
= next_char_of_string()) >= 0)
know( input_line_pointer
[-1] == '\"' );
as_warn( "Expected \"-ed string" );
demand_empty_rest_of_line();
c
= * input_line_pointer
++;
switch (c
= * input_line_pointer
++)
for (number
= 0; isdigit(c
); c
= * input_line_pointer
++)
number
= number
* 8 + c
- '0';
/* as_fatal( "Unterminated string - use app!" ); */
/* To be compatible with BSD 4.2 as: give the luser a linefeed!! */
as_warn( "Bad escaped character in string, '?' assumed" );
get_segmented_expression ( expP
)
register expressionS
* expP
;
if ( (retval
= expression( expP
)) == SEG_PASS1
|| retval
== SEG_NONE
|| retval
== SEG_BIG
)
as_warn("Expected address expression: absolute 0 assumed");
retval
= expP
-> X_seg
= SEG_ABSOLUTE
;
expP
-> X_add_number
= 0;
expP
-> X_add_symbol
= expP
-> X_subtract_symbol
= 0;
return (retval
); /* SEG_ ABSOLUTE,UNKNOWN,DATA,TEXT,BSS */
get_known_segmented_expression ( expP
)
register expressionS
* expP
;
if ( (retval
= get_segmented_expression (expP
)) == SEG_UNKNOWN
name1
= expP
-> X_add_symbol
? expP
-> X_add_symbol
-> sy_name
: "";
name2
= expP
-> X_subtract_symbol
? expP
-> X_subtract_symbol
-> sy_name
: "";
as_warn("Symbols \"%s\" \"%s\" are undefined: absolute 0 assumed.",
as_warn("Symbol \"%s\" undefined: absolute 0 assumed.",
retval
= expP
-> X_seg
= SEG_ABSOLUTE
;
expP
-> X_add_number
= 0;
expP
-> X_add_symbol
= expP
-> X_subtract_symbol
= NULL
;
know( retval
== SEG_ABSOLUTE
|| retval
== SEG_DATA
|| retval
== SEG_TEXT
|| retval
== SEG_BSS
|| retval
== SEG_DIFFERENCE
);
} /* get_known_segmented_expression() */
/* static */ long int /* JF was static, but can't be if the MD pseudos are to use it */
get_absolute_expression ()
if ( (s
= expression(& exp
)) != SEG_ABSOLUTE
)
as_warn( "Bad Absolute Expression, absolute 0 assumed.");
return (exp
. X_add_number
);
static char /* return terminator */
get_absolute_expression_and_terminator( val_pointer
)
long int * val_pointer
; /* return value of expression */
* val_pointer
= get_absolute_expression ();
return ( * input_line_pointer
++ );
* Like demand_copy_string, but return NULL if the string contains any '\0's.
* Give a warning if that happens.
demand_copy_C_string (len_pointer
)
if (s
= demand_copy_string (len_pointer
))
for (len
= * len_pointer
;
as_warn( "This string may not contain \'\\0\'" );
* Demand string, but return a safe (=private) copy of the string.
* Return NULL if we can't read a string here.
demand_copy_string (lenP
)
if (* input_line_pointer
== '\"')
input_line_pointer
++; /* Skip opening quote. */
while ( (c
= next_char_of_string()) >= 0 ) {
obstack_1grow ( ¬es
, c
);
/* JF this next line is so demand_copy_C_string will return a null
obstack_1grow(¬es
,'\0');
retval
=obstack_finish( ¬es
);
as_warn( "Missing string" );
* is_it_end_of_statement()
* In: Input_line_pointer -> next character.
* Do: Skip input_line_pointer over all whitespace.
* Out: TRUE if input_line_pointer -> end-of-line.
return (is_end_of_line
[* input_line_pointer
]);
register struct symbol
* symbolP
; /* symbol we are working with */
if(*input_line_pointer
=='=')
while(*input_line_pointer
==' ' || *input_line_pointer
=='\t')
if(sym_name
[0]=='.' && sym_name
[1]=='\0') {
/* Turn '. = mumble' into a .org mumble */
segment
= get_known_segmented_expression(& exp
);
if (segment
!= now_seg
&& segment
!= SEG_ABSOLUTE
)
as_warn("Illegal segment \"%s\". Segment \"%s\" assumed.",
seg_name
[(int) segment
], seg_name
[(int) now_seg
]);
p
= frag_var (rs_org
, 1, 1, (relax_substateT
)0, exp
.X_add_symbol
,
exp
.X_add_number
, (char *)0);
} /* if (ok to make frag) */
symbolP
=symbol_find_or_make(sym_name
);