diff -urNbB binutils-2.16.91.0.2.org/gas/Makefile.am binutils-2.16.91.0.2/gas/Makefile.am --- binutils-2.16.91.0.2.org/gas/Makefile.am 2005-07-20 19:27:27.000000000 +0000 +++ binutils-2.16.91.0.2/gas/Makefile.am 2005-07-21 18:35:02.000000000 +0000 @@ -204,7 +204,7 @@ symbols.c \ write.c -CFILES = $(GAS_CFILES) itbl-ops.c +CFILES = $(GAS_CFILES) gasp.c itbl-ops.c HFILES = \ as.h \ @@ -466,7 +466,8 @@ # Note: GASP is now deprecated and has been removed. It is still # available in the CVS archive or older binutils releases if it is needed. -noinst_PROGRAMS = as-new +# ...and it is needed for few packages in distribution. +noinst_PROGRAMS = as-new gasp-new noinst_SCRIPTS = $(GDBINIT) EXTRA_SCRIPTS = .gdbinit @@ -530,6 +531,10 @@ as.h asintl.h bignum.h bit_fix.h config.h emul.h expr.h flonum.h \ frags.h hash.h listing.h obj.h read.h symbols.h tc.h write.h +gasp_new_SOURCES = gasp.c macro.c sb.c hash.c +gasp_new_LDADD = ../libiberty/libiberty.a $(INTLLIBS) +gasp_new_DEPENDENCIES = ../libiberty/libiberty.a $(INTLDEPS) + EXPECT = expect RUNTEST = runtest RUNTESTFLAGS= @@ -2508,6 +2513,8 @@ $(INCDIR)/obstack.h subsegs.h struc-symbol.h write.o: write.c $(INCDIR)/symcat.h subsegs.h $(INCDIR)/obstack.h \ output-file.h dwarf2dbg.h +gasp.o: gasp.c $(INCDIR)/getopt.h $(INCDIR)/safe-ctype.h \ + sb.h macro.h $(INCDIR)/xregex.h $(INCDIR)/xregex2.h itbl-ops.o: itbl-ops.c itbl-ops.h $(INCDIR)/symcat.h e-crisaout.o: $(srcdir)/config/e-crisaout.c $(INCDIR)/symcat.h \ emul-target.h diff -urNbB binutils-2.16.91.0.2.org/gas/doc/Makefile.am binutils-2.16.91.0.2/gas/doc/Makefile.am --- binutils-2.16.91.0.2.org/gas/doc/Makefile.am 2005-07-20 19:27:27.000000000 +0000 +++ binutils-2.16.91.0.2/gas/doc/Makefile.am 2005-07-21 18:31:04.000000000 +0000 @@ -15,7 +15,7 @@ man_MANS = as.1 -info_TEXINFOS = as.texinfo +info_TEXINFOS = as.texinfo gasp.texinfo asconfig.texi: $(CONFIG).texi rm -f asconfig.texi diff -urNbB binutils-2.16.91.0.2.org/gas/doc/gasp.texinfo binutils-2.16.91.0.2/gas/doc/gasp.texinfo --- binutils-2.16.91.0.2.org/gas/doc/gasp.texinfo 1970-01-01 00:00:00.000000000 +0000 +++ binutils-2.16.91.0.2/gas/doc/gasp.texinfo 2005-07-21 18:31:04.000000000 +0000 @@ -0,0 +1,1456 @@ +\input texinfo @c -*- Texinfo -*- +@setfilename gasp.info +@c +@c This file documents the assembly preprocessor "GASP" +@c +@c Copyright 1994, 1995, 2000, 2002 Free Software Foundation, Inc. +@c +@c Permission is granted to copy, distribute and/or modify this document +@c under the terms of the GNU Free Documentation License, Version 1.1 +@c or any later version published by the Free Software Foundation; +@c with no Invariant Sections, with no Front-Cover Texts, and with no +@c Back-Cover Texts. A copy of the license is included in the +@c section entitled "GNU Free Documentation License". + +@ifinfo +@format +START-INFO-DIR-ENTRY +* gasp: (gasp). The GNU Assembler Preprocessor +END-INFO-DIR-ENTRY +@end format +@end ifinfo + +@syncodeindex ky cp +@syncodeindex fn cp + +@finalout +@setchapternewpage odd +@settitle GASP +@titlepage +@c FIXME boring title +@title GASP, an assembly preprocessor +@subtitle for GASP version 1 +@sp 1 +@subtitle March 1994 +@author Roland Pesch +@page + +@tex +{\parskip=0pt \hfill Cygnus Support\par +} +@end tex + +@vskip 0pt plus 1filll +Copyright @copyright{} 1994, 1995, 2000, 2002 Free Software Foundation, Inc. + + Permission is granted to copy, distribute and/or modify this document + under the terms of the GNU Free Documentation License, Version 1.1 + or any later version published by the Free Software Foundation; + with no Invariant Sections, with no Front-Cover Texts, and with no + Back-Cover Texts. A copy of the license is included in the + section entitled "GNU Free Documentation License". + +@end titlepage + +@ifinfo +Copyright @copyright{} 1994, 1995, 2000, 2002 Free Software Foundation, Inc. + +@ignore +Permission is granted to process this file through TeX and print the +results, provided the printed document carries a copying permission +notice identical to this one except for the removal of this paragraph +(this paragraph not being relevant to the printed manual). +@end ignore + + Permission is granted to copy, distribute and/or modify this document + under the terms of the GNU Free Documentation License, Version 1.1 + or any later version published by the Free Software Foundation; + with no Invariant Sections, with no Front-Cover Texts, and with no + Back-Cover Texts. A copy of the license is included in the + section entitled "GNU Free Documentation License". + + +@node Top +@top GASP + +GASP is a preprocessor for assembly programs. + +This file describes version 1 of GASP. + +Steve Chamberlain wrote GASP; Roland Pesch wrote this manual. + +@menu +* Overview:: What is GASP? +* Invoking GASP:: Command line options. +* Commands:: Preprocessor commands. +* GNU Free Documentation License:: GNU Free Documentation License +* Index:: Index. +@end menu +@end ifinfo + +@node Overview +@chapter What is GASP? + +The primary purpose of the @sc{gnu} assembler is to assemble the output of +other programs---notably compilers. When you have to hand-code +specialized routines in assembly, that means the @sc{gnu} assembler is +an unfriendly processor: it has no directives for macros, conditionals, +or many other conveniences that you might expect. + +In some cases you can simply use the C preprocessor, or a generalized +preprocessor like @sc{m4}; but this can be awkward, since none of these +things are designed with assembly in mind. + +@sc{gasp} fills this need. It is expressly designed to provide the +facilities you need with hand-coded assembly code. Implementing it as a +preprocessor, rather than part of the assembler, allows the maximum +flexibility: you can use it with hand-coded assembly, without paying a +penalty of added complexity in the assembler you use for compiler +output. + +@emph{Note} The use of @sc{gasp} has now been deprecated. Anything +that it could do can now be done by the macro facilities built into +@sc{gas} itself. At some point in the future the @sc{gasp} sources will +be removed entirely from the binutils distribution. + +Here is a small example to give the flavor of @sc{gasp}. This input to +@sc{gasp} + +@cartouche +@example + .MACRO saveregs from=8 to=14 +count .ASSIGNA \from + ! save r\from..r\to + .AWHILE \&count LE \to + mov r\&count,@@-sp +count .ASSIGNA \&count + 1 + .AENDW + .ENDM + + saveregs from=12 + +bar: mov #H'dead+10,r0 +foo .SDATAC "hello"<10> + .END +@end example +@end cartouche + +@noindent +generates this assembly program: + +@cartouche +@example + ! save r12..r14 + mov r12,@@-sp + mov r13,@@-sp + mov r14,@@-sp + +bar: mov #57005+10,r0 +foo: .byte 6,104,101,108,108,111,10 +@end example +@end cartouche + +@node Invoking GASP +@chapter Command Line Options + +@c FIXME! Or is there a simpler way, calling from GAS option? +The simplest way to use @sc{gasp} is to run it as a filter and assemble +its output. In Unix and its ilk, you can do this, for example: + +@c FIXME! GASP filename suffix convention? +@example +$ gasp prog.asm | as -o prog.o +@end example + +Naturally, there are also a few command-line options to allow you to +request variations on this basic theme. Here is the full set of +possibilities for the @sc{gasp} command line. + +@example +gasp [ -a | --alternate ] + [ -c @var{char} | --commentchar @var{char} ] + [ -d | --debug ] [ -h | --help ] [ -M | --mri ] + [ -o @var{outfile} | --output @var{outfile} ] + [ -p | --print ] [ -s | --copysource ] + [ -u | --unreasonable ] [ -v | --version ] + @var{infile} @dots{} +@end example + +@ftable @code +@item @var{infile} @dots{} +@c FIXME! Why not stdin as default infile? +The input file names. You must specify at least one input file; if you +specify more, @sc{gasp} preprocesses them all, concatenating the output +in the order you list the @var{infile} arguments. + +Mark the end of each input file with the preprocessor command +@code{.END}. @xref{Other Commands,, Miscellaneous commands}. + +@item -a +@itemx --alternate +Use alternative macro syntax. @xref{Alternate,, Alternate macro +syntax}, for a discussion of how this syntax differs from the default +@sc{gasp} syntax. + +@cindex comment character, changing +@cindex semicolon, as comment +@cindex exclamation mark, as comment +@cindex shriek, as comment +@cindex bang, as comment +@cindex @code{!} default comment char +@cindex @code{;} as comment char +@item -c '@var{char}' +@itemx --commentchar '@var{char}' +Use @var{char} as the comment character. The default comment character +is @samp{!}. For example, to use a semicolon as the comment character, +specify @w{@samp{-c ';'}} on the @sc{gasp} command line. Since +assembler command characters often have special significance to command +shells, it is a good idea to quote or escape @var{char} when you specify +a comment character. + +For the sake of simplicity, all examples in this manual use the default +comment character @samp{!}. + +@item -d +@itemx --debug +Show debugging statistics. In this version of @sc{gasp}, this option +produces statistics about the string buffers that @sc{gasp} allocates +internally. For each defined buffersize @var{s}, @sc{gasp} shows the +number of strings @var{n} that it allocated, with a line like this: + +@example +strings size @var{s} : @var{n} +@end example + +@noindent +@sc{gasp} displays these statistics on the standard error stream, when +done preprocessing. + +@item -h +@itemx --help +Display a summary of the @sc{gasp} command line options. + +@item -M +@itemx --mri +Use MRI compatibility mode. Using this option causes @sc{gasp} to +accept the syntax and pseudo-ops used by the Microtec Research +@code{ASM68K} assembler. + +@item -o @var{outfile} +@itemx --output @var{outfile} +Write the output in a file called @var{outfile}. If you do not use the +@samp{-o} option, @sc{gasp} writes its output on the standard output +stream. + +@item -p +@itemx --print +Print line numbers. @sc{gasp} obeys this option @emph{only} if you also +specify @samp{-s} to copy source lines to its output. With @samp{-s +-p}, @sc{gasp} displays the line number of each source line copied +(immediately after the comment character at the beginning of the line). + +@item -s +@itemx --copysource +Copy the source lines to the output file. Use this option +to see the effect of each preprocessor line on the @sc{gasp} output. +@sc{gasp} places a comment character (@samp{!} by default) at +the beginning of each source line it copies, so that you can use this +option and still assemble the result. + +@item -u +@itemx --unreasonable +Bypass ``unreasonable expansion'' limit. Since you can define @sc{gasp} +macros inside other macro definitions, the preprocessor normally +includes a sanity check. If your program requires more than 1,000 +nested expansions, @sc{gasp} normally exits with an error message. Use +this option to turn off this check, allowing unlimited nested +expansions. + +@item -v +@itemx --version +Display the @sc{gasp} version number. +@end ftable + +@node Commands +@chapter Preprocessor Commands + +@sc{gasp} commands have a straightforward syntax that fits in well with +assembly conventions. In general, a command extends for a line, and may +have up to three fields: an optional label, the command itself, and +optional arguments to the command. You can write commands in upper or +lower case, though this manual shows them in upper case. @xref{Syntax +Details,, Details of the GASP syntax}, for more information. + +@menu +* Conditionals:: +* Loops:: +* Variables:: +* Macros:: +* Data:: +* Listings:: +* Other Commands:: +* Syntax Details:: +* Alternate:: +@end menu + +@node Conditionals +@section Conditional assembly + +The conditional-assembly directives allow you to include or exclude +portions of an assembly depending on how a pair of expressions, or a +pair of strings, compare. + +The overall structure of conditionals is familiar from many other +contexts. @code{.AIF} marks the start of a conditional, and precedes +assembly for the case when the condition is true. An optional +@code{.AELSE} precedes assembly for the converse case, and an +@code{.AENDI} marks the end of the condition. + +@c FIXME! Why doesn't -u turn off this check? +You may nest conditionals up to a depth of 100; @sc{gasp} rejects +nesting beyond that, because it may indicate a bug in your macro +structure. + +@c FIXME! Why isn't there something like cpp's -D option? Conditionals +@c would be much more useful if there were. +Conditionals are primarily useful inside macro definitions, where you +often need different effects depending on argument values. +@xref{Macros,, Defining your own directives}, for details about defining +macros. + +@ftable @code +@item .AIF @var{expra} @var{cmp} @var{exprb} +@itemx .AIF "@var{stra}" @var{cmp} "@var{strb}" + +The governing condition goes on the same line as the @code{.AIF} +preprocessor command. You may compare either two strings, or two +expressions. + +When you compare strings, only two conditional @var{cmp} comparison +operators are available: @samp{EQ} (true if @var{stra} and @var{strb} +are identical), and @samp{NE} (the opposite). + +When you compare two expressions, @emph{both expressions must be +absolute} (@pxref{Expressions,, Arithmetic expressions in GASP}). You +can use these @var{cmp} comparison operators with expressions: + +@ftable @code +@item EQ +Are @var{expra} and @var{exprb} equal? (For strings, are @var{stra} and +@var{strb} identical?) + +@item NE +Are @var{expra} and @var{exprb} different? (For strings, are @var{stra} +and @var{strb} different? + +@item LT +Is @var{expra} less than @var{exprb}? (Not allowed for strings.) + +@item LE +Is @var{expra} less than or equal to @var{exprb}? (Not allowed for strings.) + +@item GT +Is @var{expra} greater than @var{exprb}? (Not allowed for strings.) + +@item GE +Is @var{expra} greater than or equal to @var{exprb}? (Not allowed for +strings.) +@end ftable + +@item .AELSE +Marks the start of assembly code to be included if the condition fails. +Optional, and only allowed within a conditional (between @code{.AIF} and +@code{.AENDI}). + +@item .AENDI +Marks the end of a conditional assembly. +@end ftable + +@node Loops +@section Repetitive sections of assembly + +Two preprocessor directives allow you to repeatedly issue copies of the +same block of assembly code. + +@ftable @code +@item .AREPEAT @var{aexp} +@itemx .AENDR +If you simply need to repeat the same block of assembly over and over a +fixed number of times, sandwich one instance of the repeated block +between @code{.AREPEAT} and @code{.AENDR}. Specify the number of +copies as @var{aexp} (which must be an absolute expression). For +example, this repeats two assembly statements three times in succession: + +@cartouche +@example + .AREPEAT 3 + rotcl r2 + div1 r0,r1 + .AENDR +@end example +@end cartouche + +@item .AWHILE @var{expra} @var{cmp} @var{exprb} +@itemx .AENDW +@itemx .AWHILE @var{stra} @var{cmp} @var{strb} +@itemx .AENDW +To repeat a block of assembly depending on a conditional test, rather +than repeating it for a specific number of times, use @code{.AWHILE}. +@code{.AENDW} marks the end of the repeated block. The conditional +comparison works exactly the same way as for @code{.AIF}, with the same +comparison operators (@pxref{Conditionals,, Conditional assembly}). + +Since the terms of the comparison must be absolute expression, +@code{.AWHILE} is primarily useful within macros. @xref{Macros,, +Defining your own directives}. +@end ftable + +@cindex loops, breaking out of +@cindex breaking out of loops +You can use the @code{.EXITM} preprocessor directive to break out of +loops early (as well as to break out of macros). @xref{Macros,, +Defining your own directives}. + +@node Variables +@section Preprocessor variables + +You can use variables in @sc{gasp} to represent strings, registers, or +the results of expressions. + +You must distinguish two kinds of variables: +@enumerate +@item +Variables defined with @code{.EQU} or @code{.ASSIGN}. To evaluate this +kind of variable in your assembly output, simply mention its name. For +example, these two lines define and use a variable @samp{eg}: + +@cartouche +@example +eg .EQU FLIP-64 + @dots{} + mov.l eg,r0 +@end example +@end cartouche + +@emph{Do not use} this kind of variable in conditional expressions or +while loops; @sc{gasp} only evaluates these variables when writing +assembly output. + +@item +Variables for use during preprocessing. You can define these +with @code{.ASSIGNC} or @code{.ASSIGNA}. To evaluate this +kind of variable, write @samp{\&} before the variable name; for example, + +@cartouche +@example +opcit .ASSIGNA 47 + @dots{} + .AWHILE \&opcit GT 0 + @dots{} + .AENDW +@end example +@end cartouche + +@sc{gasp} treats macro arguments almost the same way, but to evaluate +them you use the prefix @samp{\} rather than @samp{\&}. +@xref{Macros,, Defining your own directives}. +@end enumerate + +@ftable @code +@item @var{pvar} .EQU @var{expr} +@c FIXME! Anything to beware of re GAS directive of same name? +Assign preprocessor variable @var{pvar} the value of the expression +@var{expr}. There are no restrictions on redefinition; use @samp{.EQU} +with the same @var{pvar} as often as you find it convenient. + +@item @var{pvar} .ASSIGN @var{expr} +Almost the same as @code{.EQU}, save that you may not redefine +@var{pvar} using @code{.ASSIGN} once it has a value. +@c FIXME!! Supposed to work this way, apparently, but on 9feb94 works +@c just like .EQU + +@item @var{pvar} .ASSIGNA @var{aexpr} +Define a variable with a numeric value, for use during preprocessing. +@var{aexpr} must be an absolute expression. You can redefine variables +with @code{.ASSIGNA} at any time. + +@item @var{pvar} .ASSIGNC "@var{str}" +Define a variable with a string value, for use during preprocessing. +You can redefine variables with @code{.ASSIGNC} at any time. + +@item @var{pvar} .REG (@var{register}) +Use @code{.REG} to define a variable that represents a register. In +particular, @var{register} is @emph{not evaluated} as an expression. +You may use @code{.REG} at will to redefine register variables. +@end ftable + +All these directives accept the variable name in the ``label'' position, +that is at the left margin. You may specify a colon after the variable +name if you wish; the first example above could have started @samp{eg:} +with the same effect. + +@c pagebreak makes for better aesthetics---ensures macro and expansion together +@page +@node Macros +@section Defining your own directives + +The commands @code{.MACRO} and @code{.ENDM} allow you to define macros +that generate assembly output. You can use these macros with a syntax +similar to built-in @sc{gasp} or assembler directives. For example, +this definition specifies a macro @code{SUM} that adds together a range of +consecutive registers: + +@cartouche +@example + .MACRO SUM FROM=0, TO=9 + ! \FROM \TO + mov r\FROM,r10 +COUNT .ASSIGNA \FROM+1 + .AWHILE \&COUNT LE \TO + add r\&COUNT,r10 +COUNT .ASSIGNA \&COUNT+1 + .AENDW + .ENDM +@end example +@end cartouche + +@noindent +With that definition, @samp{SUM 0,5} generates this assembly output: + +@cartouche +@example + ! 0 5 + mov r0,r10 + add r1,r10 + add r2,r10 + add r3,r10 + add r4,r10 + add r5,r10 +@end example +@end cartouche + +@ftable @code +@item .MACRO @var{macname} +@itemx .MACRO @var{macname} @var{macargs} @dots{} +Begin the definition of a macro called @var{macname}. If your macro +definition requires arguments, specify their names after the macro name, +separated by commas or spaces. You can supply a default value for any +macro argument by following the name with @samp{=@var{deflt}}. For +example, these are all valid @code{.MACRO} statements: + +@table @code +@item .MACRO COMM +Begin the definition of a macro called @code{COMM}, which takes no +arguments. + +@item .MACRO PLUS1 P, P1 +@itemx .MACRO PLUS1 P P1 +Either statement begins the definition of a macro called @code{PLUS1}, +which takes two arguments; within the macro definition, write +@samp{\P} or @samp{\P1} to evaluate the arguments. + +@item .MACRO RESERVE_STR P1=0 P2 +Begin the definition of a macro called @code{RESERVE_STR}, with two +arguments. The first argument has a default value, but not the second. +After the definition is complete, you can call the macro either as +@samp{RESERVE_STR @var{a},@var{b}} (with @samp{\P1} evaluating to +@var{a} and @samp{\P2} evaluating to @var{b}), or as @samp{RESERVE_STR +,@var{b}} (with @samp{\P1} evaluating as the default, in this case +@samp{0}, and @samp{\P2} evaluating to @var{b}). +@end table + +When you call a macro, you can specify the argument values either by +position, or by keyword. For example, @samp{SUM 9,17} is equivalent to +@samp{SUM TO=17, FROM=9}. Macro arguments are preprocessor variables +similar to the variables you define with @samp{.ASSIGNA} or +@samp{.ASSIGNC}; in particular, you can use them in conditionals or for +loop control. (The only difference is the prefix you write to evaluate +the variable: for a macro argument, write @samp{\@var{argname}}, but for +a preprocessor variable, write @samp{\&@var{varname}}.) + +@item @var{name} .MACRO +@itemx @var{name} .MACRO ( @var{macargs} @dots{} ) +@c FIXME check: I think no error _and_ no args recognized if I use form +@c NAME .MACRO ARG ARG +An alternative form of introducing a macro definition: specify the macro +name in the label position, and the arguments (if any) between +parentheses after the name. Defaulting rules and usage work the same +way as for the other macro definition syntax. + +@item .ENDM +Mark the end of a macro definition. + +@item .EXITM +Exit early from the current macro definition, @code{.AREPEAT} loop, or +@code{.AWHILE} loop. + +@cindex number of macros executed +@cindex macros, count executed +@item \@@ +@sc{gasp} maintains a counter of how many macros it has +executed in this pseudo-variable; you can copy that number to your +output with @samp{\@@}, but @emph{only within a macro definition}. + +@item LOCAL @var{name} [ , @dots{} ] +@emph{Warning: @code{LOCAL} is only available if you select ``alternate +macro syntax'' with @samp{-a} or @samp{--alternate}.} @xref{Alternate,, +Alternate macro syntax}. + +Generate a string replacement for each of the @var{name} arguments, and +replace any instances of @var{name} in each macro expansion. The +replacement string is unique in the assembly, and different for each +separate macro expansion. @code{LOCAL} allows you to write macros that +define symbols, without fear of conflict between separate macro expansions. +@end ftable + +@node Data +@section Data output + +In assembly code, you often need to specify working areas of memory; +depending on the application, you may want to initialize such memory or +not. @sc{gasp} provides preprocessor directives to help you avoid +repetitive coding for both purposes. + +You can use labels as usual to mark the data areas. + +@menu +* Initialized:: +* Uninitialized:: +@end menu + +@node Initialized +@subsection Initialized data + +These are the @sc{gasp} directives for initialized data, and the standard +@sc{gnu} assembler directives they expand to: + +@ftable @code +@item .DATA @var{expr}, @var{expr}, @dots{} +@itemx .DATA.B @var{expr}, @var{expr}, @dots{} +@itemx .DATA.W @var{expr}, @var{expr}, @dots{} +@itemx .DATA.L @var{expr}, @var{expr}, @dots{} +Evaluate arithmetic expressions @var{expr}, and emit the corresponding +@code{as} directive (labelled with @var{lab}). The unqualified +@code{.DATA} emits @samp{.long}; @code{.DATA.B} emits @samp{.byte}; +@code{.DATA.W} emits @samp{.short}; and @code{.DATA.L} emits +@samp{.long}. + +For example, @samp{foo .DATA 1,2,3} emits @samp{foo: .long 1,2,3}. + +@item .DATAB @var{repeat}, @var{expr} +@itemx .DATAB.B @var{repeat}, @var{expr} +@itemx .DATAB.W @var{repeat}, @var{expr} +@itemx .DATAB.L @var{repeat}, @var{expr} +@c FIXME! Looks like gasp accepts and ignores args after 2nd. +Make @code{as} emit @var{repeat} copies of the value of the expression +@var{expr} (using the @code{as} directive @code{.fill}). +@samp{.DATAB.B} repeats one-byte values; @samp{.DATAB.W} repeats +two-byte values; and @samp{.DATAB.L} repeats four-byte values. +@samp{.DATAB} without a suffix repeats four-byte values, just like +@samp{.DATAB.L}. + +@c FIXME! Allowing zero might be useful for edge conditions in macros. +@var{repeat} must be an absolute expression with a positive value. + +@item .SDATA "@var{str}" @dots{} +String data. Emits a concatenation of bytes, precisely as you specify +them (in particular, @emph{nothing is added to mark the end} of the +string). @xref{Constants,, String and numeric constants}, for details +about how to write strings. @code{.SDATA} concatenates multiple +arguments, making it easy to switch between string representations. You +can use commas to separate the individual arguments for clarity, if you +choose. + +@item .SDATAB @var{repeat}, "@var{str}" @dots{} +Repeated string data. The first argument specifies how many copies of +the string to emit; the remaining arguments specify the string, in the +same way as the arguments to @code{.SDATA}. + +@item .SDATAZ "@var{str}" @dots{} +Zero-terminated string data. Just like @code{.SDATA}, except that +@code{.SDATAZ} writes a zero byte at the end of the string. + +@item .SDATAC "@var{str}" @dots{} +Count-prefixed string data. Just like @code{.SDATA}, except that +@sc{gasp} precedes the string with a leading one-byte count. For +example, @samp{.SDATAC "HI"} generates @samp{.byte 2,72,73}. Since the +count field is only one byte, you can only use @code{.SDATAC} for +strings less than 256 bytes in length. +@end ftable + +@node Uninitialized +@subsection Uninitialized data + +@c FIXME! .space different on some platforms, notably HPPA. Config? +Use the @code{.RES}, @code{.SRES}, @code{.SRESC}, and @code{.SRESZ} +directives to reserve memory and leave it uninitialized. @sc{gasp} +resolves these directives to appropriate calls of the @sc{gnu} +@code{as} @code{.space} directive. + +@ftable @code +@item .RES @var{count} +@itemx .RES.B @var{count} +@itemx .RES.W @var{count} +@itemx .RES.L @var{count} +Reserve room for @var{count} uninitialized elements of data. The +suffix specifies the size of each element: @code{.RES.B} reserves +@var{count} bytes, @code{.RES.W} reserves @var{count} pairs of bytes, +and @code{.RES.L} reserves @var{count} quartets. @code{.RES} without a +suffix is equivalent to @code{.RES.L}. + +@item .SRES @var{count} +@itemx .SRES.B @var{count} +@itemx .SRES.W @var{count} +@itemx .SRES.L @var{count} +@c FIXME! This is boring. Shouldn't it at least have a different +@c default size? (e.g. the "S" suggests "string", for which .B +@c would be more appropriate) +@code{.SRES} is a synonym for @samp{.RES}. + +@item .SRESC @var{count} +@itemx .SRESC.B @var{count} +@itemx .SRESC.W @var{count} +@itemx .SRESC.L @var{count} +Like @code{.SRES}, but reserves space for @code{@var{count}+1} elements. + +@item .SRESZ @var{count} +@itemx .SRESZ.B @var{count} +@itemx .SRESZ.W @var{count} +@itemx .SRESZ.L @var{count} +Like @code{.SRES}, but reserves space for @code{@var{count}+1} elements. +@end ftable + +@node Listings +@section Assembly listing control + +The @sc{gasp} listing-control directives correspond to +related @sc{gnu} @code{as} directives. + +@ftable @code +@item .PRINT LIST +@itemx .PRINT NOLIST +Print control. This directive emits the @sc{gnu} @code{as} directive +@code{.list} or @code{.nolist}, according to its argument. @xref{List,, +@code{.list}, as.info, Using as}, for details on how these directives +interact. + +@item .FORM LIN=@var{ln} +@itemx .FORM COL=@var{cols} +@itemx .FORM LIN=@var{ln} COL=@var{cols} +Specify the page size for assembly listings: @var{ln} represents the +number of lines, and @var{cols} the number of columns. You may specify +either page dimension independently, or both together. If you do not +specify the number of lines, @sc{gasp} assumes 60 lines; if you do not +specify the number of columns, @sc{gasp} assumes 132 columns. +(Any values you may have specified in previous instances of @code{.FORM} +do @emph{not} carry over as defaults.) Emits the @code{.psize} +assembler directive. + +@item .HEADING @var{string} +Specify @var{string} as the title of your assembly listings. Emits +@samp{.title "@var{string}"}. + +@item .PAGE +Force a new page in assembly listings. Emits @samp{.eject}. +@end ftable + +@node Other Commands +@section Miscellaneous commands + +@ftable @code +@item .ALTERNATE +Use the alternate macro syntax henceforth in the assembly. +@xref{Alternate,, Alternate macro syntax}. + +@item .ORG +@c FIXME! This is very strange, since _GAS_ understands .org +This command is recognized, but not yet implemented. @sc{gasp} +generates an error message for programs that use @code{.ORG}. + +@item .RADIX @var{s} +@c FIXME no test cases in testsuite/gasp +@sc{gasp} understands numbers in any of base two, eight, ten, or +sixteen. You can encode the base explicitly in any numeric constant +(@pxref{Constants,, String and numeric constants}). If you write +numbers without an explicit indication of the base, the most recent +@samp{.RADIX @var{s}} command determines how they are interpreted. +@var{s} is a single letter, one of the following: + +@table @code +@item .RADIX B +Base 2. + +@item .RADIX Q +Base 8. + +@item .RADIX D +Base 10. This is the original default radix. + +@item .RADIX H +Base 16. +@end table + +You may specify the argument @var{s} in lower case (any of @samp{bqdh}) +with the same effects. + +@item .EXPORT @var{name} +@itemx .GLOBAL @var{name} +@c FIXME! No test cases in testsuite/gasp +Declare @var{name} global (emits @samp{.global @var{name}}). The two +directives are synonymous. + +@item .PROGRAM +No effect: @sc{gasp} accepts this directive, and silently ignores it. + +@item .END +Mark end of each preprocessor file. @sc{gasp} issues a warning if it +reaches end of file without seeing this command. + +@item .INCLUDE "@var{str}" +Preprocess the file named by @var{str}, as if its contents appeared +where the @code{.INCLUDE} directive does. @sc{gasp} imposes a maximum +limit of 30 stacked include files, as a sanity check. +@c FIXME! Why is include depth not affected by -u? + +@item .ALIGN @var{size} +@c FIXME! Why is this not utterly pointless? +Evaluate the absolute expression @var{size}, and emit the assembly +instruction @samp{.align @var{size}} using the result. +@end ftable + +@node Syntax Details +@section Details of the GASP syntax + +Since @sc{gasp} is meant to work with assembly code, its statement +syntax has no surprises for the assembly programmer. + +@cindex whitespace +@emph{Whitespace} (blanks or tabs; @emph{not} newline) is partially +significant, in that it delimits up to three fields in a line. The +amount of whitespace does not matter; you may line up fields in separate +lines if you wish, but @sc{gasp} does not require that. + +@cindex fields of @sc{gasp} source line +@cindex label field +The @emph{first field}, an optional @dfn{label}, must be flush left in a +line (with no leading whitespace) if it appears at all. You may use a +colon after the label if you wish; @sc{gasp} neither requires the colon +nor objects to it (but will not include it as part of the label name). + +@cindex directive field +The @emph{second field}, which must appear after some whitespace, +contains a @sc{gasp} or assembly @dfn{directive}. + +@cindex argument fields +Any @emph{further fields} on a line are @dfn{arguments} to the +directive; you can separate them from one another using either commas or +whitespace. + +@menu +* Markers:: +* Constants:: +* Symbols:: +* Expressions:: +* String Builtins:: +@end menu + +@node Markers +@subsection Special syntactic markers + +@sc{gasp} recognizes a few special markers: to delimit comments, to +continue a statement on the next line, to separate symbols from other +characters, and to copy text to the output literally. (One other +special marker, @samp{\@@}, works only within macro definitions; +@pxref{Macros,, Defining your own directives}.) + +@cindex comments +The trailing part of any @sc{gasp} source line may be a @dfn{comment}. +A comment begins with the first unquoted comment character (@samp{!} by +default), or an escaped or doubled comment character (@samp{\!} or +@samp{!!} by default), and extends to the end of a line. You can +specify what comment character to use with the @samp{-c} option +(@pxref{Invoking GASP,, Command Line Options}). The two kinds of +comment markers lead to slightly different treatment: + +@table @code +@item ! +A single, un-escaped comment character generates an assembly comment in +the @sc{gasp} output. @sc{gasp} evaluates any preprocessor variables +(macro arguments, or variables defined with @code{.ASSIGNA} or +@code{.ASSIGNC}) present. For example, a macro that begins like this + +@example + .MACRO SUM FROM=0, TO=9 + ! \FROM \TO +@end example + +@noindent +issues as the first line of output a comment that records the +values you used to call the macro. + +@c comments, preprocessor-only +@c preprocessor-only comments +@c GASP-only comments +@item \! +@itemx !! +Either an escaped comment character, or a double comment character, +marks a @sc{gasp} source comment. @sc{gasp} does not copy such comments +to the assembly output. +@end table + +@cindex continuation character +@kindex + +To @emph{continue a statement} on the next line of the file, begin the +second line with the character @samp{+}. + +@cindex literal copy to output +@cindex copying literally to output +@cindex preprocessing, avoiding +@cindex avoiding preprocessing +Occasionally you may want to prevent @sc{gasp} from preprocessing some +particular bit of text. To @emph{copy literally} from the @sc{gasp} +source to its output, place @samp{\(} before the string to copy, and +@samp{)} at the end. For example, write @samp{\(\!)} if you need the +characters @samp{\!} in your assembly output. + +@cindex symbol separator +@cindex text, separating from symbols +@cindex symbols, separating from text +To @emph{separate a preprocessor variable} from text to appear +immediately after its value, write a single quote (@code{'}). For +example, @samp{.SDATA "\P'1"} writes a string built by concatenating the +value of @code{P} and the digit @samp{1}. (You cannot achieve this by +writing just @samp{\P1}, since @samp{P1} is itself a valid name for a +preprocessor variable.) + +@node Constants +@subsection String and numeric constants + +There are two ways of writing @dfn{string constants} in @sc{gasp}: as +literal text, and by numeric byte value. Specify a string literal +between double quotes (@code{"@var{str}"}). Specify an individual +numeric byte value as an absolute expression between angle brackets +(@code{<@var{expr}>}. Directives that output strings allow you to +specify any number of either kind of value, in whatever order is +convenient, and concatenate the result. (Alternate syntax mode +introduces a number of alternative string notations; @pxref{Alternate,, +Alternate macro syntax}.) + +@c Details of numeric notation, e.g. base prefixes +You can write @dfn{numeric constants} either in a specific base, or in +whatever base is currently selected (either 10, or selected by the most +recent @code{.RADIX}). + +To write a number in a @emph{specific base}, use the pattern +@code{@var{s}'@var{ddd}}: a base specifier character @var{s}, followed +by a single quote followed by digits @var{ddd}. The base specifier +character matches those you can specify with @code{.RADIX}: @samp{B} for +base 2, @samp{Q} for base 8, @samp{D} for base 10, and @samp{H} for base +16. (You can write this character in lower case if you prefer.) + +You can write floating point constants using the same syntax recognised +by GAS @ref{Flonums,,Flonums,as,The GNU Assembler.}. A constraint is +that these constants will be interpreted as decimal values irrespective +of the currently selected base. + +@c FIXME! What are rules for recognizing number in deflt base? Whatever +@c is left over after parsing other things?? + +@node Symbols +@subsection Symbols + +@sc{gasp} recognizes symbol names that start with any alphabetic character, +@samp{_}, or @samp{$}, and continue with any of the same characters or +with digits. Label names follow the same rules. + +@node Expressions +@subsection Arithmetic expressions in GASP + +@cindex absolute expressions +@cindex relocatable expressions +There are two kinds of expressions, depending on their result: +@dfn{absolute} expressions, which resolve to a constant (that is, they +do not involve any values unknown to @sc{gasp}), and @dfn{relocatable} +expressions, which must reduce to the form + +@example +@var{addsym}+@var{const}-@var{subsym} +@end example + +@noindent +where @var{addsym} and @var{subsym} are assembly symbols of unknown +value, and @var{const} is a constant. + +Arithmetic for @sc{gasp} expressions follows very similar rules to C. +You can use parentheses to change precedence; otherwise, arithmetic +primitives have decreasing precedence in the order of the following +list. + +@enumerate +@item +Single-argument @code{+} (identity), @code{-} (arithmetic opposite), or +@code{~} (bitwise negation). @emph{The argument must be an absolute +expression.} + +@item +@code{*} (multiplication) and @code{/} (division). @emph{Both arguments +must be absolute expressions.} + +@item +@code{+} (addition) and @code{-} (subtraction). @emph{At least one argument +must be absolute.} +@c FIXME! Actually, subtraction doesn't check for this. + +@item +@code{&} (bitwise and). @emph{Both arguments must be absolute.} + +@item +@c FIXME! I agree ~ is a better notation than ^ for xor, but is the +@c improvement worth differing from C? +@code{|} (bitwise or) and @code{~} (bitwise exclusive or; @code{^} in +C). @emph{Both arguments must be absolute.} +@end enumerate + +@node String Builtins +@subsection String primitives + +You can use these primitives to manipulate strings (in the argument +field of @sc{gasp} statements): + +@ftable @code +@item .LEN("@var{str}") +Calculate the length of string @code{"@var{str}"}, as an absolute +expression. For example, @samp{.RES.B .LEN("sample")} reserves six +bytes of memory. + +@item .INSTR("@var{string}", "@var{seg}", @var{ix}) +Search for the first occurrence of @var{seg} after position @var{ix} of +@var{string}. For example, @samp{.INSTR("ABCDEFG", "CDE", 0)} evaluates +to the absolute result @code{2}. + +The result is @code{-1} if @var{seg} does not occur in @var{string} +after position @var{ix}. + +@item .SUBSTR("@var{string}",@var{start},@var{len}) +The substring of @var{string} beginning at byte number @var{start} and +extending for @var{len} bytes. +@end ftable + +@node Alternate +@section Alternate macro syntax + +If you specify @samp{-a} or @samp{--alternate} on the @sc{gasp} command +line, the preprocessor uses somewhat different syntax. This syntax is +reminiscent of the syntax of Phar Lap macro assembler, but it +is @emph{not} meant to be a full emulation of Phar Lap or similar +assemblers. In particular, @sc{gasp} does not support directives such +as @code{DB} and @code{IRP}, even in alternate syntax mode. + +In particular, @samp{-a} (or @samp{--alternate}) elicits these +differences: + +@table @emph +@item Preprocessor directives +You can use @sc{gasp} preprocessor directives without a leading @samp{.} +dot. For example, you can write @samp{SDATA} with the same effect as +@samp{.SDATA}. + +@item LOCAL +One additional directive, @code{LOCAL}, is available. @xref{Macros,, +Defining your own directives}, for an explanation of how to use +@code{LOCAL}. + +@need 2000 +@item String delimiters +You can write strings delimited in these other ways besides +@code{"@var{string}"}: + +@table @code +@item '@var{string}' +You can delimit strings with single-quote charaters. + +@item <@var{string}> +You can delimit strings with matching angle brackets. +@end table + +@item single-character string escape +To include any single character literally in a string (even if the +character would otherwise have some special meaning), you can prefix the +character with @samp{!} (an exclamation mark). For example, you can +write @samp{<4.3 !> 5.4!!>} to get the literal text @samp{4.3 > 5.4!}. + +@item Expression results as strings +You can write @samp{%@var{expr}} to evaluate the expression @var{expr} +and use the result as a string. +@end table + +@node GNU Free Documentation License +@chapter GNU Free Documentation License + + GNU Free Documentation License + + Version 1.1, March 2000 + + Copyright (C) 2000 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + +0. PREAMBLE + +The purpose of this License is to make a manual, textbook, or other +written document "free" in the sense of freedom: to assure everyone +the effective freedom to copy and redistribute it, with or without +modifying it, either commercially or noncommercially. Secondarily, +this License preserves for the author and publisher a way to get +credit for their work, while not being considered responsible for +modifications made by others. + +This License is a kind of "copyleft", which means that derivative +works of the document must themselves be free in the same sense. It +complements the GNU General Public License, which is a copyleft +license designed for free software. + +We have designed this License in order to use it for manuals for free +software, because free software needs free documentation: a free +program should come with manuals providing the same freedoms that the +software does. But this License is not limited to software manuals; +it can be used for any textual work, regardless of subject matter or +whether it is published as a printed book. We recommend this License +principally for works whose purpose is instruction or reference. + + +1. APPLICABILITY AND DEFINITIONS + +This License applies to any manual or other work that contains a +notice placed by the copyright holder saying it can be distributed +under the terms of this License. The "Document", below, refers to any +such manual or work. Any member of the public is a licensee, and is +addressed as "you". + +A "Modified Version" of the Document means any work containing the +Document or a portion of it, either copied verbatim, or with +modifications and/or translated into another language. + +A "Secondary Section" is a named appendix or a front-matter section of +the Document that deals exclusively with the relationship of the +publishers or authors of the Document to the Document's overall subject +(or to related matters) and contains nothing that could fall directly +within that overall subject. (For example, if the Document is in part a +textbook of mathematics, a Secondary Section may not explain any +mathematics.) The relationship could be a matter of historical +connection with the subject or with related matters, or of legal, +commercial, philosophical, ethical or political position regarding +them. + +The "Invariant Sections" are certain Secondary Sections whose titles +are designated, as being those of Invariant Sections, in the notice +that says that the Document is released under this License. + +The "Cover Texts" are certain short passages of text that are listed, +as Front-Cover Texts or Back-Cover Texts, in the notice that says that +the Document is released under this License. + +A "Transparent" copy of the Document means a machine-readable copy, +represented in a format whose specification is available to the +general public, whose contents can be viewed and edited directly and +straightforwardly with generic text editors or (for images composed of +pixels) generic paint programs or (for drawings) some widely available +drawing editor, and that is suitable for input to text formatters or +for automatic translation to a variety of formats suitable for input +to text formatters. A copy made in an otherwise Transparent file +format whose markup has been designed to thwart or discourage +subsequent modification by readers is not Transparent. A copy that is +not "Transparent" is called "Opaque". + +Examples of suitable formats for Transparent copies include plain +ASCII without markup, Texinfo input format, LaTeX input format, SGML +or XML using a publicly available DTD, and standard-conforming simple +HTML designed for human modification. Opaque formats include +PostScript, PDF, proprietary formats that can be read and edited only +by proprietary word processors, SGML or XML for which the DTD and/or +processing tools are not generally available, and the +machine-generated HTML produced by some word processors for output +purposes only. + +The "Title Page" means, for a printed book, the title page itself, +plus such following pages as are needed to hold, legibly, the material +this License requires to appear in the title page. For works in +formats which do not have any title page as such, "Title Page" means +the text near the most prominent appearance of the work's title, +preceding the beginning of the body of the text. + + +2. VERBATIM COPYING + +You may copy and distribute the Document in any medium, either +commercially or noncommercially, provided that this License, the +copyright notices, and the license notice saying this License applies +to the Document are reproduced in all copies, and that you add no other +conditions whatsoever to those of this License. You may not use +technical measures to obstruct or control the reading or further +copying of the copies you make or distribute. However, you may accept +compensation in exchange for copies. If you distribute a large enough +number of copies you must also follow the conditions in section 3. + +You may also lend copies, under the same conditions stated above, and +you may publicly display copies. + + +3. COPYING IN QUANTITY + +If you publish printed copies of the Document numbering more than 100, +and the Document's license notice requires Cover Texts, you must enclose +the copies in covers that carry, clearly and legibly, all these Cover +Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on +the back cover. Both covers must also clearly and legibly identify +you as the publisher of these copies. The front cover must present +the full title with all words of the title equally prominent and +visible. You may add other material on the covers in addition. +Copying with changes limited to the covers, as long as they preserve +the title of the Document and satisfy these conditions, can be treated +as verbatim copying in other respects. + +If the required texts for either cover are too voluminous to fit +legibly, you should put the first ones listed (as many as fit +reasonably) on the actual cover, and continue the rest onto adjacent +pages. + +If you publish or distribute Opaque copies of the Document numbering +more than 100, you must either include a machine-readable Transparent +copy along with each Opaque copy, or state in or with each Opaque copy +a publicly-accessible computer-network location containing a complete +Transparent copy of the Document, free of added material, which the +general network-using public has access to download anonymously at no +charge using public-standard network protocols. If you use the latter +option, you must take reasonably prudent steps, when you begin +distribution of Opaque copies in quantity, to ensure that this +Transparent copy will remain thus accessible at the stated location +until at least one year after the last time you distribute an Opaque +copy (directly or through your agents or retailers) of that edition to +the public. + +It is requested, but not required, that you contact the authors of the +Document well before redistributing any large number of copies, to give +them a chance to provide you with an updated version of the Document. + + +4. MODIFICATIONS + +You may copy and distribute a Modified Version of the Document under +the conditions of sections 2 and 3 above, provided that you release +the Modified Version under precisely this License, with the Modified +Version filling the role of the Document, thus licensing distribution +and modification of the Modified Version to whoever possesses a copy +of it. In addition, you must do these things in the Modified Version: + +A. Use in the Title Page (and on the covers, if any) a title distinct + from that of the Document, and from those of previous versions + (which should, if there were any, be listed in the History section + of the Document). You may use the same title as a previous version + if the original publisher of that version gives permission. +B. List on the Title Page, as authors, one or more persons or entities + responsible for authorship of the modifications in the Modified + Version, together with at least five of the principal authors of the + Document (all of its principal authors, if it has less than five). +C. State on the Title page the name of the publisher of the + Modified Version, as the publisher. +D. Preserve all the copyright notices of the Document. +E. Add an appropriate copyright notice for your modifications + adjacent to the other copyright notices. +F. Include, immediately after the copyright notices, a license notice + giving the public permission to use the Modified Version under the + terms of this License, in the form shown in the Addendum below. +G. Preserve in that license notice the full lists of Invariant Sections + and required Cover Texts given in the Document's license notice. +H. Include an unaltered copy of this License. +I. Preserve the section entitled "History", and its title, and add to + it an item stating at least the title, year, new authors, and + publisher of the Modified Version as given on the Title Page. If + there is no section entitled "History" in the Document, create one + stating the title, year, authors, and publisher of the Document as + given on its Title Page, then add an item describing the Modified + Version as stated in the previous sentence. +J. Preserve the network location, if any, given in the Document for + public access to a Transparent copy of the Document, and likewise + the network locations given in the Document for previous versions + it was based on. These may be placed in the "History" section. + You may omit a network location for a work that was published at + least four years before the Document itself, or if the original + publisher of the version it refers to gives permission. +K. In any section entitled "Acknowledgements" or "Dedications", + preserve the section's title, and preserve in the section all the + substance and tone of each of the contributor acknowledgements + and/or dedications given therein. +L. Preserve all the Invariant Sections of the Document, + unaltered in their text and in their titles. Section numbers + or the equivalent are not considered part of the section titles. +M. Delete any section entitled "Endorsements". Such a section + may not be included in the Modified Version. +N. Do not retitle any existing section as "Endorsements" + or to conflict in title with any Invariant Section. + +If the Modified Version includes new front-matter sections or +appendices that qualify as Secondary Sections and contain no material +copied from the Document, you may at your option designate some or all +of these sections as invariant. To do this, add their titles to the +list of Invariant Sections in the Modified Version's license notice. +These titles must be distinct from any other section titles. + +You may add a section entitled "Endorsements", provided it contains +nothing but endorsements of your Modified Version by various +parties--for example, statements of peer review or that the text has +been approved by an organization as the authoritative definition of a +standard. + +You may add a passage of up to five words as a Front-Cover Text, and a +passage of up to 25 words as a Back-Cover Text, to the end of the list +of Cover Texts in the Modified Version. Only one passage of +Front-Cover Text and one of Back-Cover Text may be added by (or +through arrangements made by) any one entity. If the Document already +includes a cover text for the same cover, previously added by you or +by arrangement made by the same entity you are acting on behalf of, +you may not add another; but you may replace the old one, on explicit +permission from the previous publisher that added the old one. + +The author(s) and publisher(s) of the Document do not by this License +give permission to use their names for publicity for or to assert or +imply endorsement of any Modified Version. + + +5. COMBINING DOCUMENTS + +You may combine the Document with other documents released under this +License, under the terms defined in section 4 above for modified +versions, provided that you include in the combination all of the +Invariant Sections of all of the original documents, unmodified, and +list them all as Invariant Sections of your combined work in its +license notice. + +The combined work need only contain one copy of this License, and +multiple identical Invariant Sections may be replaced with a single +copy. If there are multiple Invariant Sections with the same name but +different contents, make the title of each such section unique by +adding at the end of it, in parentheses, the name of the original +author or publisher of that section if known, or else a unique number. +Make the same adjustment to the section titles in the list of +Invariant Sections in the license notice of the combined work. + +In the combination, you must combine any sections entitled "History" +in the various original documents, forming one section entitled +"History"; likewise combine any sections entitled "Acknowledgements", +and any sections entitled "Dedications". You must delete all sections +entitled "Endorsements." + + +6. COLLECTIONS OF DOCUMENTS + +You may make a collection consisting of the Document and other documents +released under this License, and replace the individual copies of this +License in the various documents with a single copy that is included in +the collection, provided that you follow the rules of this License for +verbatim copying of each of the documents in all other respects. + +You may extract a single document from such a collection, and distribute +it individually under this License, provided you insert a copy of this +License into the extracted document, and follow this License in all +other respects regarding verbatim copying of that document. + + +7. AGGREGATION WITH INDEPENDENT WORKS + +A compilation of the Document or its derivatives with other separate +and independent documents or works, in or on a volume of a storage or +distribution medium, does not as a whole count as a Modified Version +of the Document, provided no compilation copyright is claimed for the +compilation. Such a compilation is called an "aggregate", and this +License does not apply to the other self-contained works thus compiled +with the Document, on account of their being thus compiled, if they +are not themselves derivative works of the Document. + +If the Cover Text requirement of section 3 is applicable to these +copies of the Document, then if the Document is less than one quarter +of the entire aggregate, the Document's Cover Texts may be placed on +covers that surround only the Document within the aggregate. +Otherwise they must appear on covers around the whole aggregate. + + +8. TRANSLATION + +Translation is considered a kind of modification, so you may +distribute translations of the Document under the terms of section 4. +Replacing Invariant Sections with translations requires special +permission from their copyright holders, but you may include +translations of some or all Invariant Sections in addition to the +original versions of these Invariant Sections. You may include a +translation of this License provided that you also include the +original English version of this License. In case of a disagreement +between the translation and the original English version of this +License, the original English version will prevail. + + +9. TERMINATION + +You may not copy, modify, sublicense, or distribute the Document except +as expressly provided for under this License. Any other attempt to +copy, modify, sublicense or distribute the Document is void, and will +automatically terminate your rights under this License. However, +parties who have received copies, or rights, from you under this +License will not have their licenses terminated so long as such +parties remain in full compliance. + + +10. FUTURE REVISIONS OF THIS LICENSE + +The Free Software Foundation may publish new, revised versions +of the GNU Free Documentation License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. See +http://www.gnu.org/copyleft/. + +Each version of the License is given a distinguishing version number. +If the Document specifies that a particular numbered version of this +License "or any later version" applies to it, you have the option of +following the terms and conditions either of that specified version or +of any later version that has been published (not as a draft) by the +Free Software Foundation. If the Document does not specify a version +number of this License, you may choose any version ever published (not +as a draft) by the Free Software Foundation. + + +ADDENDUM: How to use this License for your documents + +To use this License in a document you have written, include a copy of +the License in the document and put the following copyright and +license notices just after the title page: + +@smallexample + Copyright (c) YEAR YOUR NAME. + Permission is granted to copy, distribute and/or modify this document + under the terms of the GNU Free Documentation License, Version 1.1 + or any later version published by the Free Software Foundation; + with the Invariant Sections being LIST THEIR TITLES, with the + Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST. + A copy of the license is included in the section entitled "GNU + Free Documentation License". +@end smallexample + +If you have no Invariant Sections, write "with no Invariant Sections" +instead of saying which ones are invariant. If you have no +Front-Cover Texts, write "no Front-Cover Texts" instead of +"Front-Cover Texts being LIST"; likewise for Back-Cover Texts. + +If your document contains nontrivial examples of program code, we +recommend releasing these examples in parallel under your choice of +free software license, such as the GNU General Public License, +to permit their use in free software. + +@node Index +@unnumbered Index + +@printindex cp + +@contents +@bye diff -urNbB binutils-2.16.91.0.2.org/gas/gasp.c binutils-2.16.91.0.2/gas/gasp.c --- binutils-2.16.91.0.2.org/gas/gasp.c 1970-01-01 00:00:00.000000000 +0000 +++ binutils-2.16.91.0.2/gas/gasp.c 2005-07-21 18:31:04.000000000 +0000 @@ -0,0 +1,3983 @@ +/* gasp.c - Gnu assembler preprocessor main program. + Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002 + Free Software Foundation, Inc. + + Written by Steve and Judy Chamberlain of Cygnus Support, + sac@cygnus.com + + This file is part of GASP, the GNU Assembler Preprocessor. + + GASP 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 2, or (at your option) + any later version. + + GASP 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 GASP; see the file COPYING. If not, write to the Free + Software Foundation, 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. */ + +/* +This program translates the input macros and stuff into a form +suitable for gas to consume. + + gasp [-sdhau] [-c char] [-o ] * + + -s copy source to output + -c comments are started with instead of ! + -u allow unreasonable stuff + -p print line numbers + -d print debugging stats + -s semi colons start comments + -a use alternate syntax + Pseudo ops can start with or without a . + Labels have to be in first column. + -I specify include dir + Macro arg parameters subsituted by name, don't need the &. + String can start with ' too. + Strings can be surrounded by <..> + A % in a string evaluates the expression + Literal char in a string with ! +*/ + +#include "config.h" +#include "bin-bugs.h" + +#include +#include +#include +#include "getopt.h" + +#ifdef HAVE_STDLIB_H +#include +#endif + +#ifdef NEED_MALLOC_DECLARATION +extern char *malloc (); +#endif + +#include "ansidecl.h" +#include "libiberty.h" +#include "safe-ctype.h" +#include "sb.h" +#include "macro.h" +#include "asintl.h" +#include "xregex.h" + +char *program_version = "1.2"; + +/* This is normally declared in as.h, but we don't include that. We + need the function because other files linked with gasp.c might call + it. */ +extern void as_abort PARAMS ((const char *, int, const char *)); + +/* The default obstack chunk size. If we set this to zero, the + obstack code will use whatever will fit in a 4096 byte block. This + is used by the hash table code used by macro.c. */ +int chunksize = 0; + +#define MAX_INCLUDES 30 /* Maximum include depth. */ +#define MAX_REASONABLE 1000 /* Maximum number of expansions. */ + +int unreasonable; /* -u on command line. */ +int stats; /* -d on command line. */ +int print_line_number; /* -p flag on command line. */ +int copysource; /* -c flag on command line. */ +int warnings; /* Number of WARNINGs generated so far. */ +int errors; /* Number of ERRORs generated so far. */ +int fatals; /* Number of fatal ERRORs generated so far (either 0 or 1). */ +int alternate = 0; /* -a on command line. */ +int mri = 0; /* -M on command line. */ +char comment_char = '!'; +int radix = 10; /* Default radix. */ + +int had_end; /* Seen .END. */ + +/* The output stream. */ +FILE *outfile; + +/* The attributes of each character are stored as a bit pattern + chartype, which gives us quick tests. */ + +#define FIRSTBIT 1 +#define NEXTBIT 2 +#define SEPBIT 4 +#define WHITEBIT 8 +#define COMMENTBIT 16 +#define BASEBIT 32 +#define ISCOMMENTCHAR(x) (chartype[(unsigned char)(x)] & COMMENTBIT) +#define ISFIRSTCHAR(x) (chartype[(unsigned char)(x)] & FIRSTBIT) +#define ISNEXTCHAR(x) (chartype[(unsigned char)(x)] & NEXTBIT) +#define ISSEP(x) (chartype[(unsigned char)(x)] & SEPBIT) +#define ISWHITE(x) (chartype[(unsigned char)(x)] & WHITEBIT) +#define ISBASE(x) (chartype[(unsigned char)(x)] & BASEBIT) +static char chartype[256]; + +/* Conditional assembly uses the `ifstack'. Each aif pushes another + entry onto the stack, and sets the on flag if it should. The aelse + sets hadelse, and toggles on. An aend pops a level. We limit to + 100 levels of nesting, not because we're facists pigs with read + only minds, but because more than 100 levels of nesting is probably + a bug in the user's macro structure. */ + +#define IFNESTING 100 +struct { + int on; /* Is the level being output. */ + int hadelse; /* Has an aelse been seen. */ +} ifstack[IFNESTING]; + +int ifi; + +/* The final and intermediate results of expression evaluation are kept in + exp_t's. Note that a symbol is not an sb, but a pointer into the input + line. It must be coped somewhere safe before the next line is read in. */ + +typedef struct { + char *name; + int len; +} symbol; + +typedef struct { + int value; /* Constant part. */ + symbol add_symbol; /* Name part. */ + symbol sub_symbol; /* Name part. */ +} exp_t; + +/* Hashing is done in a pretty standard way. A hash_table has a + pointer to a vector of pointers to hash_entrys, and the size of the + vector. A hash_entry contains a union of all the info we like to + store in hash table. If there is a hash collision, hash_entries + with the same hash are kept in a chain. */ + +/* What the data in a hash_entry means. */ +typedef enum { + hash_integer, /* Name->integer mapping. */ + hash_string, /* Name->string mapping. */ + hash_macro, /* Name is a macro. */ + hash_formal /* Name is a formal argument. */ +} hash_type; + +typedef struct hs { + sb key; /* Symbol name. */ + hash_type type; /* Symbol meaning. */ + union { + sb s; + int i; + struct macro_struct *m; + struct formal_struct *f; + } value; + struct hs *next; /* Next hash_entry with same hash key. */ +} hash_entry; + +typedef struct { + hash_entry **table; + int size; +} hash_table; + +/* How we nest files and expand macros etc. + + We keep a stack of of include_stack structs. Each include file + pushes a new level onto the stack. We keep an sb with a pushback + too. unget chars are pushed onto the pushback sb, getchars first + checks the pushback sb before reading from the input stream. + + Small things are expanded by adding the text of the item onto the + pushback sb. Larger items are grown by pushing a new level and + allocating the entire pushback buf for the item. Each time + something like a macro is expanded, the stack index is changed. We + can then perform an exitm by popping all entries off the stack with + the same stack index. If we're being reasonable, we can detect + recusive expansion by checking the index is reasonably small. */ + +typedef enum { + include_file, include_repeat, include_while, include_macro +} include_type; + +struct include_stack { + sb pushback; /* Current pushback stream. */ + int pushback_index; /* Next char to read from stream. */ + FILE *handle; /* Open file. */ + sb name; /* Name of file. */ + int linecount; /* Number of lines read so far. */ + include_type type; + int index; /* Index of this layer. */ +} include_stack[MAX_INCLUDES]; + +struct include_stack *sp; +#define isp (sp - include_stack) + +/* Include file list. */ + +typedef struct include_path { + struct include_path *next; + sb path; +} include_path; + +include_path *paths_head; +include_path *paths_tail; + +static void quit PARAMS ((void)); +static void hash_new_table PARAMS ((int, hash_table *)); +static int hash PARAMS ((sb *)); +static hash_entry *hash_create PARAMS ((hash_table *, sb *)); +static void hash_add_to_string_table PARAMS ((hash_table *, sb *, sb *, int)); +static void hash_add_to_int_table PARAMS ((hash_table *, sb *, int)); +static hash_entry *hash_lookup PARAMS ((hash_table *, sb *)); +static void checkconst PARAMS ((int, exp_t *)); +static int is_flonum PARAMS ((int, sb *)); +static int chew_flonum PARAMS ((int, sb *, sb *)); +static int sb_strtol PARAMS ((int, sb *, int, int *)); +static int level_0 PARAMS ((int, sb *, exp_t *)); +static int level_1 PARAMS ((int, sb *, exp_t *)); +static int level_2 PARAMS ((int, sb *, exp_t *)); +static int level_3 PARAMS ((int, sb *, exp_t *)); +static int level_4 PARAMS ((int, sb *, exp_t *)); +static int level_5 PARAMS ((int, sb *, exp_t *)); +static int exp_parse PARAMS ((int, sb *, exp_t *)); +static void exp_string PARAMS ((exp_t *, sb *)); +static int exp_get_abs PARAMS ((const char *, int, sb *, int *)); +#if 0 +static void strip_comments PARAMS ((sb *)); +#endif +static void unget PARAMS ((int)); +static void include_buf PARAMS ((sb *, sb *, include_type, int)); +static void include_print_where_line PARAMS ((FILE *)); +static void include_print_line PARAMS ((FILE *)); +static int get_line PARAMS ((sb *)); +static int grab_label PARAMS ((sb *, sb *)); +static void change_base PARAMS ((int, sb *, sb *)); +static void do_end PARAMS ((sb *)); +static void do_assign PARAMS ((int, int, sb *)); +static void do_radix PARAMS ((sb *)); +static int get_opsize PARAMS ((int, sb *, int *)); +static int eol PARAMS ((int, sb *)); +static void do_data PARAMS ((int, sb *, int)); +static void do_datab PARAMS ((int, sb *)); +static void do_align PARAMS ((int, sb *)); +static void do_res PARAMS ((int, sb *, int)); +static void do_export PARAMS ((sb *)); +static void do_print PARAMS ((int, sb *)); +static void do_heading PARAMS ((int, sb *)); +static void do_page PARAMS ((void)); +static void do_form PARAMS ((int, sb *)); +static int get_any_string PARAMS ((int, sb *, sb *, int, int)); +static int skip_openp PARAMS ((int, sb *)); +static int skip_closep PARAMS ((int, sb *)); +static int dolen PARAMS ((int, sb *, sb *)); +static int doinstr PARAMS ((int, sb *, sb *)); +static int dosubstr PARAMS ((int, sb *, sb *)); +static void process_assigns PARAMS ((int, sb *, sb *)); +static int get_and_process PARAMS ((int, sb *, sb *)); +static void process_file PARAMS ((void)); +static void free_old_entry PARAMS ((hash_entry *)); +static void do_assigna PARAMS ((int, sb *)); +static void do_assignc PARAMS ((int, sb *)); +static void do_reg PARAMS ((int, sb *)); +static int condass_lookup_name PARAMS ((sb *, int, sb *, int)); +static int whatcond PARAMS ((int, sb *, int *)); +static int istrue PARAMS ((int, sb *)); +static void do_aif PARAMS ((int, sb *)); +static void do_aelse PARAMS ((void)); +static void do_aendi PARAMS ((void)); +static int condass_on PARAMS ((void)); +static void do_if PARAMS ((int, sb *, int)); +static int get_mri_string PARAMS ((int, sb *, sb *, int)); +static void do_ifc PARAMS ((int, sb *, int)); +static void do_aendr PARAMS ((void)); +static void do_awhile PARAMS ((int, sb *)); +static void do_aendw PARAMS ((void)); +static void do_exitm PARAMS ((void)); +static void do_arepeat PARAMS ((int, sb *)); +static void do_endm PARAMS ((void)); +static void do_irp PARAMS ((int, sb *, int)); +static void do_local PARAMS ((int, sb *)); +static void do_macro PARAMS ((int, sb *)); +static int macro_op PARAMS ((int, sb *)); +static int getstring PARAMS ((int, sb *, sb *)); +static void do_sdata PARAMS ((int, sb *, int)); +static void do_sdatab PARAMS ((int, sb *)); +static int new_file PARAMS ((const char *)); +static void do_include PARAMS ((int, sb *)); +static void include_pop PARAMS ((void)); +static int get PARAMS ((void)); +static int linecount PARAMS ((void)); +static int include_next_index PARAMS ((void)); +static void chartype_init PARAMS ((void)); +static int process_pseudo_op PARAMS ((int, sb *, sb *)); +static void add_keyword PARAMS ((const char *, int)); +static void process_init PARAMS ((void)); +static void do_define PARAMS ((const char *)); +static void show_usage PARAMS ((FILE *, int)); +static void show_help PARAMS ((void)); + +/* --- functions recently removed from sb.c --- */ + +/* put a null at the end of the sb at in and return the start of the + * string, so that it can be used as an arg to printf %s. */ + +static char * +sb_name (sb *in) +{ + /* stick a null on the end of the string */ + sb_add_char (in, 0); + return in->ptr; +} + +/* print the sb at ptr to the output file */ + +static void +sb_print (FILE *outfile, sb *ptr) +{ + int i; + int nc = 0; + + for (i = 0; i < ptr->len; i++) + { + if (nc) + { + fprintf (outfile, ","); + } + fprintf (outfile, "%d", ptr->ptr[i]); + nc = 1; + } +} + +/* print the sb at ptr to the output file */ + +static void +sb_print_at (FILE *outfile, int idx, sb *ptr) +{ + int i; + for (i = idx; i < ptr->len; i++) + putc (ptr->ptr[i], outfile); +} + +#define FATAL(x) \ + do \ + { \ + include_print_where_line (stderr); \ + fprintf x; \ + fatals++; \ + quit (); \ + } \ + while (0) + +#define ERROR(x) \ + do \ + { \ + include_print_where_line (stderr); \ + fprintf x; \ + errors++; \ + } \ + while (0) + +#define WARNING(x) \ + do \ + { \ + include_print_where_line (stderr); \ + fprintf x; \ + warnings++; \ + } \ + while (0) + +/* Exit the program and return the right ERROR code. */ + +static void +quit () +{ + int exitcode; + if (fatals + errors) + exitcode = 1; + else + exitcode = 0; + + exit (exitcode); +} + +/* Hash table maintenance. */ + +/* Build a new hash table with size buckets + and fill in the info at ptr. */ + +static void +hash_new_table (size, ptr) + int size; + hash_table *ptr; +{ + int i; + ptr->size = size; + ptr->table = (hash_entry **) xmalloc (size * (sizeof (hash_entry *))); + /* Fill with null-pointer, not zero-bit-pattern. */ + for (i = 0; i < size; i++) + ptr->table[i] = 0; +} + +/* Calculate and return the hash value of the sb at key. */ + +static int +hash (key) + sb *key; +{ + int k = 0x1234; + int i; + char *p = key->ptr; + for (i = 0; i < key->len; i++) + { + k ^= (k << 2) ^ *p; + p++; + } + return k & 0xf0fff; +} + +/* Look up key in hash_table tab. If present, then return it, + otherwise build a new one and fill it with hash_integer. */ + +static hash_entry * +hash_create (tab, key) + hash_table *tab; + sb *key; +{ + int k = hash (key) % tab->size; + hash_entry *p; + hash_entry **table = tab->table; + + p = table[k]; + + while (1) + { + if (!p) + { + hash_entry *n = (hash_entry *) xmalloc (sizeof (hash_entry)); + n->next = table[k]; + sb_new (&n->key); + sb_add_sb (&n->key, key); + table[k] = n; + n->type = hash_integer; + return n; + } + if (strncmp (table[k]->key.ptr, key->ptr, key->len) == 0) + { + return p; + } + p = p->next; + } +} + +/* Add sb name with key into hash_table tab. + If replacing old value and again, then ERROR. */ + +static void +hash_add_to_string_table (tab, key, name, again) + hash_table *tab; + sb *key; + sb *name; + int again; +{ + hash_entry *ptr = hash_create (tab, key); + if (ptr->type == hash_integer) + { + sb_new (&ptr->value.s); + } + if (ptr->value.s.len) + { + if (!again) + ERROR ((stderr, _("redefinition not allowed\n"))); + } + + ptr->type = hash_string; + sb_reset (&ptr->value.s); + + sb_add_sb (&ptr->value.s, name); +} + +/* Add integer name to hash_table tab with sb key. */ + +static void +hash_add_to_int_table (tab, key, name) + hash_table *tab; + sb *key; + int name; +{ + hash_entry *ptr = hash_create (tab, key); + ptr->value.i = name; +} + +/* Look up sb key in hash_table tab. + If found, return hash_entry result, else 0. */ + +static hash_entry * +hash_lookup (tab, key) + hash_table *tab; + sb *key; +{ + int k = hash (key) % tab->size; + hash_entry **table = tab->table; + hash_entry *p = table[k]; + while (p) + { + if (p->key.len == key->len + && strncmp (p->key.ptr, key->ptr, key->len) == 0) + return p; + p = p->next; + } + return 0; +} + +/* expressions + + are handled in a really simple recursive decent way. each bit of + the machine takes an index into an sb and a pointer to an exp_t, + modifies the *exp_t and returns the index of the first character + past the part of the expression parsed. + + expression precedence: + ( ) + unary + - ~ + * / + + - + & + | ~ +*/ + +/* Make sure that the exp_t at term is constant. + If not the give the op ERROR. */ + +static void +checkconst (op, term) + int op; + exp_t *term; +{ + if (term->add_symbol.len + || term->sub_symbol.len) + { + ERROR ((stderr, _("the %c operator cannot take non-absolute arguments.\n"), op)); + } +} + +/* Chew the flonum from the string starting at idx. Adjust idx to + point to the next character after the flonum. */ + +static int +chew_flonum (idx, string, out) + int idx; + sb *string; + sb *out; +{ + sb buf; + regex_t reg; + regmatch_t match; + + /* Duplicate and null terminate `string'. */ + sb_new (&buf); + sb_add_sb (&buf, string); + sb_add_char (&buf, '\0'); + + if (regcomp (®, "([0-9]*\\.[0-9]+([eE][+-]?[0-9]+)?)", REG_EXTENDED) != 0) + return idx; + if (regexec (®, &buf.ptr[idx], 1, &match, 0) != 0) + return idx; + + /* Copy the match to the output. */ + assert (match.rm_eo >= match.rm_so); + sb_add_buffer (out, &buf.ptr[idx], match.rm_eo - match.rm_so); + + sb_kill (&buf); + regfree (®); + idx += match.rm_eo; + return idx; +} + +static int +is_flonum (idx, string) + int idx; + sb *string; +{ + sb buf; + regex_t reg; + int rc; + + /* Duplicate and null terminate `string'. */ + sb_new (&buf); + sb_add_sb (&buf, string); + sb_add_char (&buf, '\0'); + + if (regcomp (®, "^[0-9]*\\.[0-9]+([eE][+-]?[0-9]+)?", REG_EXTENDED) != 0) + return 0; + + rc = regexec (®, &buf.ptr[idx], 0, NULL, 0); + sb_kill (&buf); + regfree (®); + return (rc == 0); +} + +/* Turn the number in string at idx into a number of base, fill in + ptr, and return the index of the first character not in the number. */ + +static int +sb_strtol (idx, string, base, ptr) + int idx; + sb *string; + int base; + int *ptr; +{ + int value = 0; + idx = sb_skip_white (idx, string); + + while (idx < string->len) + { + int ch = string->ptr[idx]; + int dig = 0; + if (ISDIGIT (ch)) + dig = ch - '0'; + else if (ch >= 'a' && ch <= 'f') + dig = ch - 'a' + 10; + else if (ch >= 'A' && ch <= 'F') + dig = ch - 'A' + 10; + else + break; + + if (dig >= base) + break; + + value = value * base + dig; + idx++; + } + *ptr = value; + return idx; +} + +static int +level_0 (idx, string, lhs) + int idx; + sb *string; + exp_t *lhs; +{ + lhs->add_symbol.len = 0; + lhs->add_symbol.name = 0; + + lhs->sub_symbol.len = 0; + lhs->sub_symbol.name = 0; + + idx = sb_skip_white (idx, string); + + lhs->value = 0; + + if (ISDIGIT (string->ptr[idx])) + { + idx = sb_strtol (idx, string, 10, &lhs->value); + } + else if (ISFIRSTCHAR (string->ptr[idx])) + { + int len = 0; + lhs->add_symbol.name = string->ptr + idx; + while (idx < string->len && ISNEXTCHAR (string->ptr[idx])) + { + idx++; + len++; + } + lhs->add_symbol.len = len; + } + else if (string->ptr[idx] == '"') + { + sb acc; + sb_new (&acc); + ERROR ((stderr, _("string where expression expected.\n"))); + idx = getstring (idx, string, &acc); + sb_kill (&acc); + } + else + { + ERROR ((stderr, _("can't find primary in expression.\n"))); + idx++; + } + return sb_skip_white (idx, string); +} + +static int +level_1 (idx, string, lhs) + int idx; + sb *string; + exp_t *lhs; +{ + idx = sb_skip_white (idx, string); + + switch (string->ptr[idx]) + { + case '+': + idx = level_1 (idx + 1, string, lhs); + break; + case '~': + idx = level_1 (idx + 1, string, lhs); + checkconst ('~', lhs); + lhs->value = ~lhs->value; + break; + case '-': + { + symbol t; + idx = level_1 (idx + 1, string, lhs); + lhs->value = -lhs->value; + t = lhs->add_symbol; + lhs->add_symbol = lhs->sub_symbol; + lhs->sub_symbol = t; + break; + } + case '(': + idx++; + idx = level_5 (sb_skip_white (idx, string), string, lhs); + if (string->ptr[idx] != ')') + ERROR ((stderr, _("misplaced closing parens.\n"))); + else + idx++; + break; + default: + idx = level_0 (idx, string, lhs); + break; + } + return sb_skip_white (idx, string); +} + +static int +level_2 (idx, string, lhs) + int idx; + sb *string; + exp_t *lhs; +{ + exp_t rhs; + + idx = level_1 (idx, string, lhs); + + while (idx < string->len && (string->ptr[idx] == '*' + || string->ptr[idx] == '/')) + { + char op = string->ptr[idx++]; + idx = level_1 (idx, string, &rhs); + switch (op) + { + case '*': + checkconst ('*', lhs); + checkconst ('*', &rhs); + lhs->value *= rhs.value; + break; + case '/': + checkconst ('/', lhs); + checkconst ('/', &rhs); + if (rhs.value == 0) + ERROR ((stderr, _("attempt to divide by zero.\n"))); + else + lhs->value /= rhs.value; + break; + } + } + return sb_skip_white (idx, string); +} + +static int +level_3 (idx, string, lhs) + int idx; + sb *string; + exp_t *lhs; +{ + exp_t rhs; + + idx = level_2 (idx, string, lhs); + + while (idx < string->len + && (string->ptr[idx] == '+' + || string->ptr[idx] == '-')) + { + char op = string->ptr[idx++]; + idx = level_2 (idx, string, &rhs); + switch (op) + { + case '+': + lhs->value += rhs.value; + if (lhs->add_symbol.name && rhs.add_symbol.name) + { + ERROR ((stderr, _("can't add two relocatable expressions\n"))); + } + /* Change nn+symbol to symbol + nn. */ + if (rhs.add_symbol.name) + { + lhs->add_symbol = rhs.add_symbol; + } + break; + case '-': + lhs->value -= rhs.value; + lhs->sub_symbol = rhs.add_symbol; + break; + } + } + return sb_skip_white (idx, string); +} + +static int +level_4 (idx, string, lhs) + int idx; + sb *string; + exp_t *lhs; +{ + exp_t rhs; + + idx = level_3 (idx, string, lhs); + + while (idx < string->len && + string->ptr[idx] == '&') + { + char op = string->ptr[idx++]; + idx = level_3 (idx, string, &rhs); + switch (op) + { + case '&': + checkconst ('&', lhs); + checkconst ('&', &rhs); + lhs->value &= rhs.value; + break; + } + } + return sb_skip_white (idx, string); +} + +static int +level_5 (idx, string, lhs) + int idx; + sb *string; + exp_t *lhs; +{ + exp_t rhs; + + idx = level_4 (idx, string, lhs); + + while (idx < string->len + && (string->ptr[idx] == '|' || string->ptr[idx] == '~')) + { + char op = string->ptr[idx++]; + idx = level_4 (idx, string, &rhs); + switch (op) + { + case '|': + checkconst ('|', lhs); + checkconst ('|', &rhs); + lhs->value |= rhs.value; + break; + case '~': + checkconst ('~', lhs); + checkconst ('~', &rhs); + lhs->value ^= rhs.value; + break; + } + } + return sb_skip_white (idx, string); +} + +/* Parse the expression at offset idx into string, fill up res with + the result. Return the index of the first char past the + expression. */ + +static int +exp_parse (idx, string, res) + int idx; + sb *string; + exp_t *res; +{ + return level_5 (sb_skip_white (idx, string), string, res); +} + +/* Turn the expression at exp into text and glue it onto the end of + string. */ + +static void +exp_string (exp, string) + exp_t *exp; + sb *string; +{ + int np = 0; + int ad = 0; + sb_reset (string); + + if (exp->add_symbol.len) + { + sb_add_buffer (string, exp->add_symbol.name, exp->add_symbol.len); + np = 1; + ad = 1; + } + if (exp->value) + { + char buf[20]; + if (np) + sb_add_char (string, '+'); + sprintf (buf, "%d", exp->value); + sb_add_string (string, buf); + np = 1; + ad = 1; + } + if (exp->sub_symbol.len) + { + sb_add_char (string, '-'); + sb_add_buffer (string, exp->add_symbol.name, exp->add_symbol.len); + np = 0; + ad = 1; + } + + if (!ad) + sb_add_char (string, '0'); +} + +/* Parse the expression at offset idx into sb in. Return the value in + val. If the expression is not constant, give ERROR emsg. Return + the index of the first character past the end of the expression. */ + +static int +exp_get_abs (emsg, idx, in, val) + const char *emsg; + int idx; + sb *in; + int *val; +{ + exp_t res; + idx = exp_parse (idx, in, &res); + if (res.add_symbol.len || res.sub_symbol.len) + ERROR ((stderr, "%s", emsg)); + *val = res.value; + return idx; +} + +/* Current label parsed from line. */ +sb label; + +/* Hash table for all assigned variables. */ +hash_table assign_hash_table; + +/* Hash table for keyword. */ +hash_table keyword_hash_table; + +/* Hash table for eq variables. */ +hash_table vars; + +#define in_comment ';' + +#if 0 +static void +strip_comments (out) + sb *out; +{ + char *s = out->ptr; + int i = 0; + for (i = 0; i < out->len; i++) + { + if (ISCOMMENTCHAR (s[i])) + { + out->len = i; + return; + } + } +} +#endif + +/* Push back character ch so that it can be read again. */ + +static void +unget (ch) + int ch; +{ + if (ch == '\n') + { + sp->linecount--; + } + if (sp->pushback_index) + sp->pushback_index--; + else + sb_add_char (&sp->pushback, ch); +} + +/* Push the sb ptr onto the include stack, with the given name, type + and index. */ + +static void +include_buf (name, ptr, type, index) + sb *name; + sb *ptr; + include_type type; + int index; +{ + sp++; + if (sp - include_stack >= MAX_INCLUDES) + FATAL ((stderr, _("unreasonable nesting.\n"))); + sb_new (&sp->name); + sb_add_sb (&sp->name, name); + sp->handle = 0; + sp->linecount = 1; + sp->pushback_index = 0; + sp->type = type; + sp->index = index; + sb_new (&sp->pushback); + sb_add_sb (&sp->pushback, ptr); +} + +/* Used in ERROR messages, print info on where the include stack is + onto file. */ + +static void +include_print_where_line (file) + FILE *file; +{ + struct include_stack *p = include_stack + 1; + + while (p <= sp) + { + fprintf (file, "%s:%d ", sb_name (&p->name), p->linecount - 1); + p++; + } +} + +/* Used in listings, print the line number onto file. */ + +static void +include_print_line (file) + FILE *file; +{ + int n; + struct include_stack *p = include_stack + 1; + + n = fprintf (file, "%4d", p->linecount); + p++; + while (p <= sp) + { + n += fprintf (file, ".%d", p->linecount); + p++; + } + while (n < 8 * 3) + { + fprintf (file, " "); + n++; + } +} + +/* Read a line from the top of the include stack into sb in. */ + +static int +get_line (in) + sb *in; +{ + int online = 0; + int more = 1, ch = 0; + + if (copysource) + { + putc (comment_char, outfile); + if (print_line_number) + include_print_line (outfile); + } + + while (1) + { + ch = get (); + + while (ch == '\r') + ch = get (); + + if (ch == EOF) + { + if (online) + { + WARNING ((stderr, _("End of file not at start of line.\n"))); + if (copysource) + putc ('\n', outfile); + ch = '\n'; + } + else + more = 0; + break; + } + + if (copysource) + { + putc (ch, outfile); + } + + if (ch == '\n') + { + ch = get (); + online = 0; + if (ch == '+') + { + /* Continued line. */ + if (copysource) + { + putc (comment_char, outfile); + putc ('+', outfile); + } + ch = get (); + } + else + { + if (ch != EOF) + unget (ch); + ch = '\n'; break; + } + } + else + { + sb_add_char (in, ch); + } + online++; + } + + return more ? ch : 0; +} + +/* Find a label from sb in and put it in out. */ + +static int +grab_label (in, out) + sb *in; + sb *out; +{ + int i = 0; + sb_reset (out); + if (ISFIRSTCHAR (in->ptr[i]) || in->ptr[i] == '\\') + { + sb_add_char (out, in->ptr[i]); + i++; + while ((ISNEXTCHAR (in->ptr[i]) + || in->ptr[i] == '\\' + || in->ptr[i] == '&') + && i < in->len) + { + sb_add_char (out, in->ptr[i]); + i++; + } + } + return i; +} + +/* Find all strange base stuff and turn into decimal. Also + find all the other numbers and convert them from the default radix. */ + +static void +change_base (idx, in, out) + int idx; + sb *in; + sb *out; +{ + char buffer[20]; + + while (idx < in->len) + { + if (in->ptr[idx] == '\\' + && idx + 1 < in->len + && in->ptr[idx + 1] == '(') + { + idx += 2; + while (idx < in->len + && in->ptr[idx] != ')') + { + sb_add_char (out, in->ptr[idx]); + idx++; + } + if (idx < in->len) + idx++; + } + else if (idx < in->len - 1 && in->ptr[idx + 1] == '\'' && ! mri) + { + int base; + int value; + switch (in->ptr[idx]) + { + case 'b': + case 'B': + base = 2; + break; + case 'q': + case 'Q': + base = 8; + break; + case 'h': + case 'H': + base = 16; + break; + case 'd': + case 'D': + base = 10; + break; + default: + ERROR ((stderr, _("Illegal base character %c.\n"), in->ptr[idx])); + base = 10; + break; + } + + idx = sb_strtol (idx + 2, in, base, &value); + sprintf (buffer, "%d", value); + sb_add_string (out, buffer); + } + else if (ISFIRSTCHAR (in->ptr[idx])) + { + /* Copy entire names through quickly. */ + sb_add_char (out, in->ptr[idx]); + idx++; + while (idx < in->len && ISNEXTCHAR (in->ptr[idx])) + { + sb_add_char (out, in->ptr[idx]); + idx++; + } + } + else if (is_flonum (idx, in)) + { + idx = chew_flonum (idx, in, out); + } + else if (ISDIGIT (in->ptr[idx])) + { + int value; + /* All numbers must start with a digit, let's chew it and + spit out decimal. */ + idx = sb_strtol (idx, in, radix, &value); + sprintf (buffer, "%d", value); + sb_add_string (out, buffer); + + /* Skip all undigsested letters. */ + while (idx < in->len && ISNEXTCHAR (in->ptr[idx])) + { + sb_add_char (out, in->ptr[idx]); + idx++; + } + } + else if (in->ptr[idx] == '"' || in->ptr[idx] == '\'') + { + char tchar = in->ptr[idx]; + /* Copy entire names through quickly. */ + sb_add_char (out, in->ptr[idx]); + idx++; + while (idx < in->len && in->ptr[idx] != tchar) + { + sb_add_char (out, in->ptr[idx]); + idx++; + } + } + else + { + /* Nothing special, just pass it through. */ + sb_add_char (out, in->ptr[idx]); + idx++; + } + } + +} + +/* .end */ + +static void +do_end (in) + sb *in; +{ + had_end = 1; + if (mri) + fprintf (outfile, "%s\n", sb_name (in)); +} + +/* .assign */ + +static void +do_assign (again, idx, in) + int again; + int idx; + sb *in; +{ + /* Stick label in symbol table with following value. */ + exp_t e; + sb acc; + + sb_new (&acc); + idx = exp_parse (idx, in, &e); + exp_string (&e, &acc); + hash_add_to_string_table (&assign_hash_table, &label, &acc, again); + sb_kill (&acc); +} + +/* .radix [b|q|d|h] */ + +static void +do_radix (ptr) + sb *ptr; +{ + int idx = sb_skip_white (0, ptr); + switch (ptr->ptr[idx]) + { + case 'B': + case 'b': + radix = 2; + break; + case 'q': + case 'Q': + radix = 8; + break; + case 'd': + case 'D': + radix = 10; + break; + case 'h': + case 'H': + radix = 16; + break; + default: + ERROR ((stderr, _("radix is %c must be one of b, q, d or h"), radix)); + } +} + +/* Parse off a .b, .w or .l. */ + +static int +get_opsize (idx, in, size) + int idx; + sb *in; + int *size; +{ + *size = 4; + if (in->ptr[idx] == '.') + { + idx++; + } + switch (in->ptr[idx]) + { + case 'b': + case 'B': + *size = 1; + break; + case 'w': + case 'W': + *size = 2; + break; + case 'l': + case 'L': + *size = 4; + break; + case ' ': + case '\t': + break; + default: + ERROR ((stderr, _("size must be one of b, w or l, is %c.\n"), in->ptr[idx])); + break; + } + idx++; + + return idx; +} + +static int +eol (idx, line) + int idx; + sb *line; +{ + idx = sb_skip_white (idx, line); + if (idx < line->len + && ISCOMMENTCHAR(line->ptr[idx])) + return 1; + if (idx >= line->len) + return 1; + return 0; +} + +/* .data [.b|.w|.l] * + or d[bwl] * */ + +static void +do_data (idx, in, size) + int idx; + sb *in; + int size; +{ + int opsize = 4; + char *opname = ".yikes!"; + sb acc; + sb_new (&acc); + + if (!size) + { + idx = get_opsize (idx, in, &opsize); + } + else + { + opsize = size; + } + switch (opsize) + { + case 4: + opname = ".long"; + break; + case 2: + opname = ".short"; + break; + case 1: + opname = ".byte"; + break; + } + + fprintf (outfile, "%s\t", opname); + + idx = sb_skip_white (idx, in); + + if (alternate + && idx < in->len + && in->ptr[idx] == '"') + { + int i; + idx = getstring (idx, in, &acc); + for (i = 0; i < acc.len; i++) + { + if (i) + fprintf (outfile, ","); + fprintf (outfile, "%d", acc.ptr[i]); + } + } + else + { + while (!eol (idx, in)) + { + exp_t e; + idx = exp_parse (idx, in, &e); + exp_string (&e, &acc); + sb_add_char (&acc, 0); + fprintf (outfile, "%s", acc.ptr); + if (idx < in->len && in->ptr[idx] == ',') + { + fprintf (outfile, ","); + idx++; + } + } + } + sb_kill (&acc); + sb_print_at (outfile, idx, in); + fprintf (outfile, "\n"); +} + +/* .datab [.b|.w|.l] , */ + +static void +do_datab (idx, in) + int idx; + sb *in; +{ + int opsize; + int repeat; + int fill; + + idx = get_opsize (idx, in, &opsize); + + idx = exp_get_abs (_("datab repeat must be constant.\n"), idx, in, &repeat); + idx = sb_skip_comma (idx, in); + idx = exp_get_abs (_("datab data must be absolute.\n"), idx, in, &fill); + + fprintf (outfile, ".fill\t%d,%d,%d\n", repeat, opsize, fill); +} + +/* .align */ + +static void +do_align (idx, in) + int idx; + sb *in; +{ + int al, have_fill, fill; + + idx = exp_get_abs (_("align needs absolute expression.\n"), idx, in, &al); + idx = sb_skip_white (idx, in); + have_fill = 0; + fill = 0; + if (! eol (idx, in)) + { + idx = sb_skip_comma (idx, in); + idx = exp_get_abs (_(".align needs absolute fill value.\n"), idx, in, + &fill); + have_fill = 1; + } + + fprintf (outfile, ".align %d", al); + if (have_fill) + fprintf (outfile, ",%d", fill); + fprintf (outfile, "\n"); +} + +/* .res[.b|.w|.l] */ + +static void +do_res (idx, in, type) + int idx; + sb *in; + int type; +{ + int size = 4; + int count = 0; + + idx = get_opsize (idx, in, &size); + while (!eol (idx, in)) + { + idx = sb_skip_white (idx, in); + if (in->ptr[idx] == ',') + idx++; + idx = exp_get_abs (_("res needs absolute expression for fill count.\n"), idx, in, &count); + + if (type == 'c' || type == 'z') + count++; + + fprintf (outfile, ".space %d\n", count * size); + } +} + +/* .export */ + +static void +do_export (in) + sb *in; +{ + fprintf (outfile, ".global %s\n", sb_name (in)); +} + +/* .print [list] [nolist] */ + +static void +do_print (idx, in) + int idx; + sb *in; +{ + idx = sb_skip_white (idx, in); + while (idx < in->len) + { + if (strncasecmp (in->ptr + idx, "LIST", 4) == 0) + { + fprintf (outfile, ".list\n"); + idx += 4; + } + else if (strncasecmp (in->ptr + idx, "NOLIST", 6) == 0) + { + fprintf (outfile, ".nolist\n"); + idx += 6; + } + idx++; + } +} + +/* .head */ + +static void +do_heading (idx, in) + int idx; + sb *in; +{ + sb head; + sb_new (&head); + idx = getstring (idx, in, &head); + fprintf (outfile, ".title \"%s\"\n", sb_name (&head)); + sb_kill (&head); +} + +/* .page */ + +static void +do_page () +{ + fprintf (outfile, ".eject\n"); +} + +/* .form [lin=] [col=] */ + +static void +do_form (idx, in) + int idx; + sb *in; +{ + int lines = 60; + int columns = 132; + idx = sb_skip_white (idx, in); + + while (idx < in->len) + { + + if (strncasecmp (in->ptr + idx, "LIN=", 4) == 0) + { + idx += 4; + idx = exp_get_abs (_("form LIN= needs absolute expresssion.\n"), idx, in, &lines); + } + + if (strncasecmp (in->ptr + idx, _("COL="), 4) == 0) + { + idx += 4; + idx = exp_get_abs (_("form COL= needs absolute expresssion.\n"), idx, in, &columns); + } + + idx++; + } + fprintf (outfile, ".psize %d,%d\n", lines, columns); + +} + +/* Fetch string from the input stream, + rules: + 'Bxyx -> return 'Bxyza + % -> return string of decimal value of x + "" -> return string + xyx -> return xyz +*/ + +static int +get_any_string (idx, in, out, expand, pretend_quoted) + int idx; + sb *in; + sb *out; + int expand; + int pretend_quoted; +{ + sb_reset (out); + idx = sb_skip_white (idx, in); + + if (idx < in->len) + { + if (in->len > 2 && in->ptr[idx + 1] == '\'' && ISBASE (in->ptr[idx])) + { + while (!ISSEP (in->ptr[idx])) + sb_add_char (out, in->ptr[idx++]); + } + else if (in->ptr[idx] == '%' + && alternate + && expand) + { + int val; + char buf[20]; + /* Turns the next expression into a string. */ + /* xgettext: no-c-format */ + idx = exp_get_abs (_("% operator needs absolute expression"), + idx + 1, + in, + &val); + sprintf (buf, "%d", val); + sb_add_string (out, buf); + } + else if (in->ptr[idx] == '"' + || in->ptr[idx] == '<' + || (alternate && in->ptr[idx] == '\'')) + { + if (alternate && expand) + { + /* Keep the quotes. */ + sb_add_char (out, '\"'); + + idx = getstring (idx, in, out); + sb_add_char (out, '\"'); + + } + else + { + idx = getstring (idx, in, out); + } + } + else + { + while (idx < in->len + && (in->ptr[idx] == '"' + || in->ptr[idx] == '\'' + || pretend_quoted + || !ISSEP (in->ptr[idx]))) + { + if (in->ptr[idx] == '"' + || in->ptr[idx] == '\'') + { + char tchar = in->ptr[idx]; + sb_add_char (out, in->ptr[idx++]); + while (idx < in->len + && in->ptr[idx] != tchar) + sb_add_char (out, in->ptr[idx++]); + if (idx == in->len) + return idx; + } + sb_add_char (out, in->ptr[idx++]); + } + } + } + + return idx; +} + +/* Skip along sb in starting at idx, suck off whitespace a ( and more + whitespace. Return the idx of the next char. */ + +static int +skip_openp (idx, in) + int idx; + sb *in; +{ + idx = sb_skip_white (idx, in); + if (in->ptr[idx] != '(') + ERROR ((stderr, _("misplaced ( .\n"))); + idx = sb_skip_white (idx + 1, in); + return idx; +} + +/* Skip along sb in starting at idx, suck off whitespace a ) and more + whitespace. Return the idx of the next char. */ + +static int +skip_closep (idx, in) + int idx; + sb *in; +{ + idx = sb_skip_white (idx, in); + if (in->ptr[idx] != ')') + ERROR ((stderr, _("misplaced ).\n"))); + idx = sb_skip_white (idx + 1, in); + return idx; +} + +/* .len */ + +static int +dolen (idx, in, out) + int idx; + sb *in; + sb *out; +{ + + sb stringout; + char buffer[10]; + + sb_new (&stringout); + idx = skip_openp (idx, in); + idx = get_and_process (idx, in, &stringout); + idx = skip_closep (idx, in); + sprintf (buffer, "%d", stringout.len); + sb_add_string (out, buffer); + + sb_kill (&stringout); + return idx; +} + +/* .instr */ + +static int +doinstr (idx, in, out) + int idx; + sb *in; + sb *out; +{ + sb string; + sb search; + int i; + int start; + int res; + char buffer[10]; + + sb_new (&string); + sb_new (&search); + idx = skip_openp (idx, in); + idx = get_and_process (idx, in, &string); + idx = sb_skip_comma (idx, in); + idx = get_and_process (idx, in, &search); + idx = sb_skip_comma (idx, in); + if (ISDIGIT (in->ptr[idx])) + { + idx = exp_get_abs (_(".instr needs absolute expresson.\n"), idx, in, &start); + } + else + { + start = 0; + } + idx = skip_closep (idx, in); + res = -1; + for (i = start; i < string.len; i++) + { + if (strncmp (string.ptr + i, search.ptr, search.len) == 0) + { + res = i; + break; + } + } + sprintf (buffer, "%d", res); + sb_add_string (out, buffer); + sb_kill (&string); + sb_kill (&search); + return idx; +} + +static int +dosubstr (idx, in, out) + int idx; + sb *in; + sb *out; +{ + sb string; + int pos; + int len; + sb_new (&string); + + idx = skip_openp (idx, in); + idx = get_and_process (idx, in, &string); + idx = sb_skip_comma (idx, in); + idx = exp_get_abs (_("need absolute position.\n"), idx, in, &pos); + idx = sb_skip_comma (idx, in); + idx = exp_get_abs (_("need absolute length.\n"), idx, in, &len); + idx = skip_closep (idx, in); + + if (len < 0 || pos < 0 || + pos > string.len + || pos + len > string.len) + { + sb_add_string (out, " "); + } + else + { + sb_add_char (out, '"'); + while (len > 0) + { + sb_add_char (out, string.ptr[pos++]); + len--; + } + sb_add_char (out, '"'); + } + sb_kill (&string); + return idx; +} + +/* Scan line, change tokens in the hash table to their replacements. */ + +static void +process_assigns (idx, in, buf) + int idx; + sb *in; + sb *buf; +{ + while (idx < in->len) + { + hash_entry *ptr; + if (in->ptr[idx] == '\\' + && idx + 1 < in->len + && in->ptr[idx + 1] == '(') + { + do + { + sb_add_char (buf, in->ptr[idx]); + idx++; + } + while (idx < in->len && in->ptr[idx - 1] != ')'); + } + else if (in->ptr[idx] == '\\' + && idx + 1 < in->len + && in->ptr[idx + 1] == '&') + { + idx = condass_lookup_name (in, idx + 2, buf, 1); + } + else if (in->ptr[idx] == '\\' + && idx + 1 < in->len + && in->ptr[idx + 1] == '$') + { + idx = condass_lookup_name (in, idx + 2, buf, 0); + } + else if (idx + 3 < in->len + && in->ptr[idx] == '.' + && TOUPPER (in->ptr[idx + 1]) == 'L' + && TOUPPER (in->ptr[idx + 2]) == 'E' + && TOUPPER (in->ptr[idx + 3]) == 'N') + idx = dolen (idx + 4, in, buf); + else if (idx + 6 < in->len + && in->ptr[idx] == '.' + && TOUPPER (in->ptr[idx + 1]) == 'I' + && TOUPPER (in->ptr[idx + 2]) == 'N' + && TOUPPER (in->ptr[idx + 3]) == 'S' + && TOUPPER (in->ptr[idx + 4]) == 'T' + && TOUPPER (in->ptr[idx + 5]) == 'R') + idx = doinstr (idx + 6, in, buf); + else if (idx + 7 < in->len + && in->ptr[idx] == '.' + && TOUPPER (in->ptr[idx + 1]) == 'S' + && TOUPPER (in->ptr[idx + 2]) == 'U' + && TOUPPER (in->ptr[idx + 3]) == 'B' + && TOUPPER (in->ptr[idx + 4]) == 'S' + && TOUPPER (in->ptr[idx + 5]) == 'T' + && TOUPPER (in->ptr[idx + 6]) == 'R') + idx = dosubstr (idx + 7, in, buf); + else if (ISFIRSTCHAR (in->ptr[idx])) + { + /* May be a simple name subsitution, see if we have a word. */ + sb acc; + int cur = idx + 1; + while (cur < in->len + && (ISNEXTCHAR (in->ptr[cur]))) + cur++; + + sb_new (&acc); + sb_add_buffer (&acc, in->ptr + idx, cur - idx); + ptr = hash_lookup (&assign_hash_table, &acc); + if (ptr) + { + /* Found a definition for it. */ + sb_add_sb (buf, &ptr->value.s); + } + else + { + /* No definition, just copy the word. */ + sb_add_sb (buf, &acc); + } + sb_kill (&acc); + idx = cur; + } + else + { + sb_add_char (buf, in->ptr[idx++]); + } + } +} + +static int +get_and_process (idx, in, out) + int idx; + sb *in; + sb *out; +{ + sb t; + sb_new (&t); + idx = get_any_string (idx, in, &t, 1, 0); + process_assigns (0, &t, out); + sb_kill (&t); + return idx; +} + +static void +process_file () +{ + sb line; + sb t1, t2; + sb acc; + sb label_in; + int more; + + sb_new (&line); + sb_new (&t1); + sb_new (&t2); + sb_new (&acc); + sb_new (&label_in); + sb_reset (&line); + more = get_line (&line); + while (more) + { + /* Find any label and pseudo op that we're intested in. */ + int l; + if (line.len == 0) + { + if (condass_on ()) + fprintf (outfile, "\n"); + } + else if (mri + && (line.ptr[0] == '*' + || line.ptr[0] == '!')) + { + /* MRI line comment. */ + fprintf (outfile, "%s", sb_name (&line)); + } + else + { + l = grab_label (&line, &label_in); + sb_reset (&label); + + if (line.ptr[l] == ':') + l++; + while (ISWHITE (line.ptr[l]) && l < line.len) + l++; + + if (label_in.len) + { + int do_assigns; + + /* Munge the label, unless this is EQU or ASSIGN. */ + do_assigns = 1; + if (l < line.len + && (line.ptr[l] == '.' || alternate || mri)) + { + int lx = l; + + if (line.ptr[lx] == '.') + ++lx; + if (lx + 3 <= line.len + && strncasecmp ("EQU", line.ptr + lx, 3) == 0 + && (lx + 3 == line.len + || ! ISFIRSTCHAR (line.ptr[lx + 3]))) + do_assigns = 0; + else if (lx + 6 <= line.len + && strncasecmp ("ASSIGN", line.ptr + lx, 6) == 0 + && (lx + 6 == line.len + || ! ISFIRSTCHAR (line.ptr[lx + 6]))) + do_assigns = 0; + } + + if (do_assigns) + process_assigns (0, &label_in, &label); + else + sb_add_sb (&label, &label_in); + } + + if (l < line.len) + { + if (process_pseudo_op (l, &line, &acc)) + { + + } + else if (condass_on ()) + { + if (macro_op (l, &line)) + { + + } + else + { + { + if (label.len) + { + fprintf (outfile, "%s:\t", sb_name (&label)); + } + else + fprintf (outfile, "\t"); + sb_reset (&t1); + process_assigns (l, &line, &t1); + sb_reset (&t2); + change_base (0, &t1, &t2); + fprintf (outfile, "%s\n", sb_name (&t2)); + } + } + } + } + else + { + /* Only a label on this line. */ + if (label.len && condass_on ()) + { + fprintf (outfile, "%s:\n", sb_name (&label)); + } + } + } + + if (had_end) + break; + sb_reset (&line); + more = get_line (&line); + } + + if (!had_end && !mri) + WARNING ((stderr, _("END missing from end of file.\n"))); +} + +static void +free_old_entry (ptr) + hash_entry *ptr; +{ + if (ptr) + { + if (ptr->type == hash_string) + sb_kill (&ptr->value.s); + } +} + +/* name: .ASSIGNA */ + +static void +do_assigna (idx, in) + int idx; + sb *in; +{ + sb tmp; + int val; + sb_new (&tmp); + + process_assigns (idx, in, &tmp); + idx = exp_get_abs (_(".ASSIGNA needs constant expression argument.\n"), 0, &tmp, &val); + + if (!label.len) + { + ERROR ((stderr, _(".ASSIGNA without label.\n"))); + } + else + { + hash_entry *ptr = hash_create (&vars, &label); + free_old_entry (ptr); + ptr->type = hash_integer; + ptr->value.i = val; + } + sb_kill (&tmp); +} + +/* name: .ASSIGNC */ + +static void +do_assignc (idx, in) + int idx; + sb *in; +{ + sb acc; + sb_new (&acc); + idx = getstring (idx, in, &acc); + + if (!label.len) + { + ERROR ((stderr, _(".ASSIGNS without label.\n"))); + } + else + { + hash_entry *ptr = hash_create (&vars, &label); + free_old_entry (ptr); + ptr->type = hash_string; + sb_new (&ptr->value.s); + sb_add_sb (&ptr->value.s, &acc); + } + sb_kill (&acc); +} + +/* name: .REG (reg) */ + +static void +do_reg (idx, in) + int idx; + sb *in; +{ + /* Remove reg stuff from inside parens. */ + sb what; + if (!mri) + idx = skip_openp (idx, in); + else + idx = sb_skip_white (idx, in); + sb_new (&what); + while (idx < in->len + && (mri + ? ! eol (idx, in) + : in->ptr[idx] != ')')) + { + sb_add_char (&what, in->ptr[idx]); + idx++; + } + hash_add_to_string_table (&assign_hash_table, &label, &what, 1); + sb_kill (&what); +} + +static int +condass_lookup_name (inbuf, idx, out, warn) + sb *inbuf; + int idx; + sb *out; + int warn; +{ + hash_entry *ptr; + sb condass_acc; + sb_new (&condass_acc); + + while (idx < inbuf->len + && ISNEXTCHAR (inbuf->ptr[idx])) + { + sb_add_char (&condass_acc, inbuf->ptr[idx++]); + } + + if (inbuf->ptr[idx] == '\'') + idx++; + ptr = hash_lookup (&vars, &condass_acc); + + if (!ptr) + { + if (warn) + { + WARNING ((stderr, _("Can't find preprocessor variable %s.\n"), sb_name (&condass_acc))); + } + else + { + sb_add_string (out, "0"); + } + } + else + { + if (ptr->type == hash_integer) + { + char buffer[30]; + sprintf (buffer, "%d", ptr->value.i); + sb_add_string (out, buffer); + } + else + { + sb_add_sb (out, &ptr->value.s); + } + } + sb_kill (&condass_acc); + return idx; +} + +#define EQ 1 +#define NE 2 +#define GE 3 +#define LT 4 +#define LE 5 +#define GT 6 +#define NEVER 7 + +static int +whatcond (idx, in, val) + int idx; + sb *in; + int *val; +{ + int cond; + + idx = sb_skip_white (idx, in); + cond = NEVER; + if (idx + 1 < in->len) + { + char *p; + char a, b; + + p = in->ptr + idx; + a = TOUPPER (p[0]); + b = TOUPPER (p[1]); + if (a == 'E' && b == 'Q') + cond = EQ; + else if (a == 'N' && b == 'E') + cond = NE; + else if (a == 'L' && b == 'T') + cond = LT; + else if (a == 'L' && b == 'E') + cond = LE; + else if (a == 'G' && b == 'T') + cond = GT; + else if (a == 'G' && b == 'E') + cond = GE; + } + if (cond == NEVER) + { + ERROR ((stderr, _("Comparison operator must be one of EQ, NE, LT, LE, GT or GE.\n"))); + cond = NEVER; + } + idx = sb_skip_white (idx + 2, in); + *val = cond; + return idx; +} + +static int +istrue (idx, in) + int idx; + sb *in; +{ + int res; + sb acc_a; + sb cond; + sb acc_b; + sb_new (&acc_a); + sb_new (&cond); + sb_new (&acc_b); + idx = sb_skip_white (idx, in); + + if (in->ptr[idx] == '"') + { + int cond; + int same; + /* This is a string comparision. */ + idx = getstring (idx, in, &acc_a); + idx = whatcond (idx, in, &cond); + idx = getstring (idx, in, &acc_b); + same = acc_a.len == acc_b.len + && (strncmp (acc_a.ptr, acc_b.ptr, acc_a.len) == 0); + + if (cond != EQ && cond != NE) + { + ERROR ((stderr, _("Comparison operator for strings must be EQ or NE\n"))); + res = 0; + } + else + res = (cond != EQ) ^ same; + } + else + /* This is a numeric expression. */ + { + int vala; + int valb; + int cond; + idx = exp_get_abs (_("Conditional operator must have absolute operands.\n"), idx, in, &vala); + idx = whatcond (idx, in, &cond); + idx = sb_skip_white (idx, in); + if (in->ptr[idx] == '"') + { + WARNING ((stderr, _("String compared against expression.\n"))); + res = 0; + } + else + { + idx = exp_get_abs (_("Conditional operator must have absolute operands.\n"), idx, in, &valb); + switch (cond) + { + default: + res = 42; + break; + case EQ: + res = vala == valb; + break; + case NE: + res = vala != valb; + break; + case LT: + res = vala < valb; + break; + case LE: + res = vala <= valb; + break; + case GT: + res = vala > valb; + break; + case GE: + res = vala >= valb; + break; + case NEVER: + res = 0; + break; + } + } + } + + sb_kill (&acc_a); + sb_kill (&cond); + sb_kill (&acc_b); + return res; +} + +/* .AIF */ + +static void +do_aif (idx, in) + int idx; + sb *in; +{ + if (ifi >= IFNESTING) + { + FATAL ((stderr, _("AIF nesting unreasonable.\n"))); + } + ifi++; + ifstack[ifi].on = ifstack[ifi - 1].on ? istrue (idx, in) : 0; + ifstack[ifi].hadelse = 0; +} + +/* .AELSE */ + +static void +do_aelse () +{ + ifstack[ifi].on = ifstack[ifi - 1].on ? !ifstack[ifi].on : 0; + if (ifstack[ifi].hadelse) + { + ERROR ((stderr, _("Multiple AELSEs in AIF.\n"))); + } + ifstack[ifi].hadelse = 1; +} + +/* .AENDI */ + +static void +do_aendi () +{ + if (ifi != 0) + { + ifi--; + } + else + { + ERROR ((stderr, _("AENDI without AIF.\n"))); + } +} + +static int +condass_on () +{ + return ifstack[ifi].on; +} + +/* MRI IFEQ, IFNE, IFLT, IFLE, IFGE, IFGT. */ + +static void +do_if (idx, in, cond) + int idx; + sb *in; + int cond; +{ + int val; + int res; + + if (ifi >= IFNESTING) + { + FATAL ((stderr, _("IF nesting unreasonable.\n"))); + } + + idx = exp_get_abs (_("Conditional operator must have absolute operands.\n"), + idx, in, &val); + switch (cond) + { + default: + case EQ: res = val == 0; break; + case NE: res = val != 0; break; + case LT: res = val < 0; break; + case LE: res = val <= 0; break; + case GE: res = val >= 0; break; + case GT: res = val > 0; break; + } + + ifi++; + ifstack[ifi].on = ifstack[ifi - 1].on ? res : 0; + ifstack[ifi].hadelse = 0; +} + +/* Get a string for the MRI IFC or IFNC pseudo-ops. */ + +static int +get_mri_string (idx, in, val, terminator) + int idx; + sb *in; + sb *val; + int terminator; +{ + idx = sb_skip_white (idx, in); + + if (idx < in->len + && in->ptr[idx] == '\'') + { + sb_add_char (val, '\''); + for (++idx; idx < in->len; ++idx) + { + sb_add_char (val, in->ptr[idx]); + if (in->ptr[idx] == '\'') + { + ++idx; + if (idx >= in->len + || in->ptr[idx] != '\'') + break; + } + } + idx = sb_skip_white (idx, in); + } + else + { + int i; + + while (idx < in->len + && in->ptr[idx] != terminator) + { + sb_add_char (val, in->ptr[idx]); + ++idx; + } + i = val->len - 1; + while (i >= 0 && ISWHITE (val->ptr[i])) + --i; + val->len = i + 1; + } + + return idx; +} + +/* MRI IFC, IFNC */ + +static void +do_ifc (idx, in, ifnc) + int idx; + sb *in; + int ifnc; +{ + sb first; + sb second; + int res; + + if (ifi >= IFNESTING) + { + FATAL ((stderr, _("IF nesting unreasonable.\n"))); + } + + sb_new (&first); + sb_new (&second); + + idx = get_mri_string (idx, in, &first, ','); + + if (idx >= in->len || in->ptr[idx] != ',') + { + ERROR ((stderr, _("Bad format for IF or IFNC.\n"))); + return; + } + + idx = get_mri_string (idx + 1, in, &second, ';'); + + res = (first.len == second.len + && strncmp (first.ptr, second.ptr, first.len) == 0); + res ^= ifnc; + + ifi++; + ifstack[ifi].on = ifstack[ifi - 1].on ? res : 0; + ifstack[ifi].hadelse = 0; +} + +/* .ENDR */ + +static void +do_aendr () +{ + if (!mri) + ERROR ((stderr, _("AENDR without a AREPEAT.\n"))); + else + ERROR ((stderr, _("ENDR without a REPT.\n"))); +} + +/* .AWHILE */ + +static void +do_awhile (idx, in) + int idx; + sb *in; +{ + int line = linecount (); + sb exp; + sb sub; + int doit; + + sb_new (&sub); + sb_new (&exp); + + process_assigns (idx, in, &exp); + doit = istrue (0, &exp); + + if (! buffer_and_nest ("AWHILE", "AENDW", &sub, get_line)) + FATAL ((stderr, _("AWHILE without a AENDW at %d.\n"), line - 1)); + + /* Turn + .AWHILE exp + foo + .AENDW + into + foo + .AWHILE exp + foo + .ENDW + */ + + if (doit) + { + int index = include_next_index (); + + sb copy; + sb_new (©); + sb_add_sb (©, &sub); + sb_add_sb (©, in); + sb_add_string (©, "\n"); + sb_add_sb (©, &sub); + sb_add_string (©, "\t.AENDW\n"); + /* Push another WHILE. */ + include_buf (&exp, ©, include_while, index); + sb_kill (©); + } + sb_kill (&exp); + sb_kill (&sub); +} + +/* .AENDW */ + +static void +do_aendw () +{ + ERROR ((stderr, _("AENDW without a AENDW.\n"))); +} + +/* .EXITM + + Pop things off the include stack until the type and index changes. */ + +static void +do_exitm () +{ + include_type type = sp->type; + if (type == include_repeat + || type == include_while + || type == include_macro) + { + int index = sp->index; + include_pop (); + while (sp->index == index + && sp->type == type) + { + include_pop (); + } + } +} + +/* .AREPEAT */ + +static void +do_arepeat (idx, in) + int idx; + sb *in; +{ + int line = linecount (); + sb exp; /* Buffer with expression in it. */ + sb copy; /* Expanded repeat block. */ + sb sub; /* Contents of AREPEAT. */ + int rc; + int ret; + char buffer[30]; + + sb_new (&exp); + sb_new (©); + sb_new (&sub); + process_assigns (idx, in, &exp); + idx = exp_get_abs (_("AREPEAT must have absolute operand.\n"), 0, &exp, &rc); + if (!mri) + ret = buffer_and_nest ("AREPEAT", "AENDR", &sub, get_line); + else + ret = buffer_and_nest ("REPT", "ENDR", &sub, get_line); + if (! ret) + FATAL ((stderr, _("AREPEAT without a AENDR at %d.\n"), line - 1)); + if (rc > 0) + { + /* Push back the text following the repeat, and another repeat block + so + .AREPEAT 20 + foo + .AENDR + gets turned into + foo + .AREPEAT 19 + foo + .AENDR + */ + int index = include_next_index (); + sb_add_sb (©, &sub); + if (rc > 1) + { + if (!mri) + sprintf (buffer, "\t.AREPEAT %d\n", rc - 1); + else + sprintf (buffer, "\tREPT %d\n", rc - 1); + sb_add_string (©, buffer); + sb_add_sb (©, &sub); + if (!mri) + sb_add_string (©, " .AENDR\n"); + else + sb_add_string (©, " ENDR\n"); + } + + include_buf (&exp, ©, include_repeat, index); + } + sb_kill (&exp); + sb_kill (&sub); + sb_kill (©); +} + +/* .ENDM */ + +static void +do_endm () +{ + ERROR ((stderr, _(".ENDM without a matching .MACRO.\n"))); +} + +/* MRI IRP pseudo-op. */ + +static void +do_irp (idx, in, irpc) + int idx; + sb *in; + int irpc; +{ + const char *err; + sb out; + + sb_new (&out); + + err = expand_irp (irpc, idx, in, &out, get_line, comment_char); + if (err != NULL) + ERROR ((stderr, "%s\n", err)); + + fprintf (outfile, "%s", sb_terminate (&out)); + + sb_kill (&out); +} + +/* Macro processing. */ + +/* Parse off LOCAL n1, n2,... Invent a label name for it. */ + +static void +do_local (idx, line) + int idx ATTRIBUTE_UNUSED; + sb *line ATTRIBUTE_UNUSED; +{ + ERROR ((stderr, _("LOCAL outside of MACRO"))); +} + +static void +do_macro (idx, in) + int idx; + sb *in; +{ + const char *err; + int line = linecount (); + + err = define_macro (idx, in, &label, get_line, "[gasp]", line, (const char **) NULL); + if (err != NULL) + ERROR ((stderr, _("macro at line %d: %s\n"), line - 1, err)); +} + +static int +macro_op (idx, in) + int idx; + sb *in; +{ + const char *err; + sb out; + sb name; + + if (! macro_defined) + return 0; + + sb_terminate (in); + if (! check_macro (in->ptr + idx, &out, comment_char, &err, NULL)) + return 0; + + if (err != NULL) + ERROR ((stderr, "%s\n", err)); + + sb_new (&name); + sb_add_string (&name, _("macro expansion")); + + include_buf (&name, &out, include_macro, include_next_index ()); + + sb_kill (&name); + sb_kill (&out); + + return 1; +} + +/* String handling. */ + +static int +getstring (idx, in, acc) + int idx; + sb *in; + sb *acc; +{ + idx = sb_skip_white (idx, in); + + while (idx < in->len + && (in->ptr[idx] == '"' + || in->ptr[idx] == '<' + || (in->ptr[idx] == '\'' && alternate))) + { + if (in->ptr[idx] == '<') + { + if (alternate || mri) + { + int nest = 0; + idx++; + while ((in->ptr[idx] != '>' || nest) + && idx < in->len) + { + if (in->ptr[idx] == '!') + { + idx++; + sb_add_char (acc, in->ptr[idx++]); + } + else + { + if (in->ptr[idx] == '>') + nest--; + if (in->ptr[idx] == '<') + nest++; + sb_add_char (acc, in->ptr[idx++]); + } + } + idx++; + } + else + { + int code; + idx++; + idx = exp_get_abs (_("Character code in string must be absolute expression.\n"), + idx, in, &code); + sb_add_char (acc, code); + + if (in->ptr[idx] != '>') + ERROR ((stderr, _("Missing > for character code.\n"))); + idx++; + } + } + else if (in->ptr[idx] == '"' || in->ptr[idx] == '\'') + { + char tchar = in->ptr[idx]; + idx++; + while (idx < in->len) + { + if (alternate && in->ptr[idx] == '!') + { + idx++; + sb_add_char (acc, in->ptr[idx++]); + } + else + { + if (in->ptr[idx] == tchar) + { + idx++; + if (idx >= in->len || in->ptr[idx] != tchar) + break; + } + sb_add_char (acc, in->ptr[idx]); + idx++; + } + } + } + } + + return idx; +} + +/* .SDATA[C|Z] */ + +static void +do_sdata (idx, in, type) + int idx; + sb *in; + int type; +{ + int nc = 0; + int pidx = -1; + sb acc; + sb_new (&acc); + fprintf (outfile, ".byte\t"); + + while (!eol (idx, in)) + { + int i; + sb_reset (&acc); + idx = sb_skip_white (idx, in); + while (!eol (idx, in)) + { + pidx = idx = get_any_string (idx, in, &acc, 0, 1); + if (type == 'c') + { + if (acc.len > 255) + { + ERROR ((stderr, _("string for SDATAC longer than 255 characters (%d).\n"), acc.len)); + } + fprintf (outfile, "%d", acc.len); + nc = 1; + } + + for (i = 0; i < acc.len; i++) + { + if (nc) + { + fprintf (outfile, ","); + } + fprintf (outfile, "%d", acc.ptr[i]); + nc = 1; + } + + if (type == 'z') + { + if (nc) + fprintf (outfile, ","); + fprintf (outfile, "0"); + } + idx = sb_skip_comma (idx, in); + if (idx == pidx) + break; + } + if (!alternate && in->ptr[idx] != ',' && idx != in->len) + { + fprintf (outfile, "\n"); + ERROR ((stderr, _("illegal character in SDATA line (0x%x).\n"), + in->ptr[idx])); + break; + } + idx++; + } + sb_kill (&acc); + fprintf (outfile, "\n"); +} + +/* .SDATAB */ + +static void +do_sdatab (idx, in) + int idx; + sb *in; +{ + int repeat; + int i; + sb acc; + sb_new (&acc); + + idx = exp_get_abs (_("Must have absolute SDATAB repeat count.\n"), idx, in, &repeat); + if (repeat <= 0) + { + ERROR ((stderr, _("Must have positive SDATAB repeat count (%d).\n"), repeat)); + repeat = 1; + } + + idx = sb_skip_comma (idx, in); + idx = getstring (idx, in, &acc); + + for (i = 0; i < repeat; i++) + { + if (i) + fprintf (outfile, "\t"); + fprintf (outfile, ".byte\t"); + sb_print (outfile, &acc); + fprintf (outfile, "\n"); + } + sb_kill (&acc); + +} + +static int +new_file (name) + const char *name; +{ + FILE *newone = fopen (name, "r"); + if (!newone) + return 0; + + if (isp == MAX_INCLUDES) + FATAL ((stderr, _("Unreasonable include depth (%ld).\n"), (long) isp)); + + sp++; + sp->handle = newone; + + sb_new (&sp->name); + sb_add_string (&sp->name, name); + + sp->linecount = 1; + sp->pushback_index = 0; + sp->type = include_file; + sp->index = 0; + sb_new (&sp->pushback); + return 1; +} + +static void +do_include (idx, in) + int idx; + sb *in; +{ + sb t; + sb cat; + include_path *includes; + + sb_new (&t); + sb_new (&cat); + + if (! mri) + idx = getstring (idx, in, &t); + else + { + idx = sb_skip_white (idx, in); + while (idx < in->len && ! ISWHITE (in->ptr[idx])) + { + sb_add_char (&t, in->ptr[idx]); + ++idx; + } + } + + for (includes = paths_head; includes; includes = includes->next) + { + sb_reset (&cat); + sb_add_sb (&cat, &includes->path); + sb_add_char (&cat, '/'); + sb_add_sb (&cat, &t); + if (new_file (sb_name (&cat))) + { + break; + } + } + if (!includes) + { + if (! new_file (sb_name (&t))) + FATAL ((stderr, _("Can't open include file `%s'.\n"), sb_name (&t))); + } + sb_kill (&cat); + sb_kill (&t); +} + +static void +include_pop () +{ + if (sp != include_stack) + { + if (sp->handle) + fclose (sp->handle); + sp--; + } +} + +/* Get the next character from the include stack. If there's anything + in the pushback buffer, take that first. If we're at eof, pop from + the stack and try again. Keep the linecount up to date. */ + +static int +get () +{ + int r; + + if (sp->pushback.len != sp->pushback_index) + { + r = (char) (sp->pushback.ptr[sp->pushback_index++]); + /* When they've all gone, reset the pointer. */ + if (sp->pushback_index == sp->pushback.len) + { + sp->pushback.len = 0; + sp->pushback_index = 0; + } + } + else if (sp->handle) + { + r = getc (sp->handle); + } + else + r = EOF; + + if (r == EOF && isp) + { + include_pop (); + r = get (); + while (r == EOF && isp) + { + include_pop (); + r = get (); + } + return r; + } + if (r == '\n') + { + sp->linecount++; + } + + return r; +} + +static int +linecount () +{ + return sp->linecount; +} + +static int +include_next_index () +{ + static int index; + if (!unreasonable + && index > MAX_REASONABLE) + FATAL ((stderr, _("Unreasonable expansion (-u turns off check).\n"))); + return ++index; +} + +/* Initialize the chartype vector. */ + +static void +chartype_init () +{ + int x; + for (x = 0; x < 256; x++) + { + if (ISALPHA (x) || x == '_' || x == '$') + chartype[x] |= FIRSTBIT; + + if (mri && x == '.') + chartype[x] |= FIRSTBIT; + + if (ISDIGIT (x) || ISALPHA (x) || x == '_' || x == '$') + chartype[x] |= NEXTBIT; + + if (x == ' ' || x == '\t' || x == ',' || x == '"' || x == ';' + || x == '"' || x == '<' || x == '>' || x == ')' || x == '(') + chartype[x] |= SEPBIT; + + if (x == 'b' || x == 'B' + || x == 'q' || x == 'Q' + || x == 'h' || x == 'H' + || x == 'd' || x == 'D') + chartype [x] |= BASEBIT; + + if (x == ' ' || x == '\t') + chartype[x] |= WHITEBIT; + + if (x == comment_char) + chartype[x] |= COMMENTBIT; + } +} + +/* What to do with all the keywords. */ +#define PROCESS 0x1000 /* Run substitution over the line. */ +#define LAB 0x2000 /* Spit out the label. */ + +#define K_EQU (PROCESS|1) +#define K_ASSIGN (PROCESS|2) +#define K_REG (PROCESS|3) +#define K_ORG (PROCESS|4) +#define K_RADIX (PROCESS|5) +#define K_DATA (LAB|PROCESS|6) +#define K_DATAB (LAB|PROCESS|7) +#define K_SDATA (LAB|PROCESS|8) +#define K_SDATAB (LAB|PROCESS|9) +#define K_SDATAC (LAB|PROCESS|10) +#define K_SDATAZ (LAB|PROCESS|11) +#define K_RES (LAB|PROCESS|12) +#define K_SRES (LAB|PROCESS|13) +#define K_SRESC (LAB|PROCESS|14) +#define K_SRESZ (LAB|PROCESS|15) +#define K_EXPORT (LAB|PROCESS|16) +#define K_GLOBAL (LAB|PROCESS|17) +#define K_PRINT (LAB|PROCESS|19) +#define K_FORM (LAB|PROCESS|20) +#define K_HEADING (LAB|PROCESS|21) +#define K_PAGE (LAB|PROCESS|22) +#define K_IMPORT (LAB|PROCESS|23) +#define K_PROGRAM (LAB|PROCESS|24) +#define K_END (PROCESS|25) +#define K_INCLUDE (PROCESS|26) +#define K_IGNORED (PROCESS|27) +#define K_ASSIGNA (PROCESS|28) +#define K_ASSIGNC (29) +#define K_AIF (PROCESS|30) +#define K_AELSE (PROCESS|31) +#define K_AENDI (PROCESS|32) +#define K_AREPEAT (PROCESS|33) +#define K_AENDR (PROCESS|34) +#define K_AWHILE (35) +#define K_AENDW (PROCESS|36) +#define K_EXITM (37) +#define K_MACRO (PROCESS|38) +#define K_ENDM (39) +#define K_ALIGN (PROCESS|LAB|40) +#define K_ALTERNATE (41) +#define K_DB (LAB|PROCESS|42) +#define K_DW (LAB|PROCESS|43) +#define K_DL (LAB|PROCESS|44) +#define K_LOCAL (45) +#define K_IFEQ (PROCESS|46) +#define K_IFNE (PROCESS|47) +#define K_IFLT (PROCESS|48) +#define K_IFLE (PROCESS|49) +#define K_IFGE (PROCESS|50) +#define K_IFGT (PROCESS|51) +#define K_IFC (PROCESS|52) +#define K_IFNC (PROCESS|53) +#define K_IRP (PROCESS|54) +#define K_IRPC (PROCESS|55) + +struct keyword { + char *name; + int code; + int extra; +}; + +static struct keyword kinfo[] = { + { "EQU", K_EQU, 0 }, + { "ALTERNATE", K_ALTERNATE, 0 }, + { "ASSIGN", K_ASSIGN, 0 }, + { "REG", K_REG, 0 }, + { "ORG", K_ORG, 0 }, + { "RADIX", K_RADIX, 0 }, + { "DATA", K_DATA, 0 }, + { "DB", K_DB, 0 }, + { "DW", K_DW, 0 }, + { "DL", K_DL, 0 }, + { "DATAB", K_DATAB, 0 }, + { "SDATA", K_SDATA, 0 }, + { "SDATAB", K_SDATAB, 0 }, + { "SDATAZ", K_SDATAZ, 0 }, + { "SDATAC", K_SDATAC, 0 }, + { "RES", K_RES, 0 }, + { "SRES", K_SRES, 0 }, + { "SRESC", K_SRESC, 0 }, + { "SRESZ", K_SRESZ, 0 }, + { "EXPORT", K_EXPORT, 0 }, + { "GLOBAL", K_GLOBAL, 0 }, + { "PRINT", K_PRINT, 0 }, + { "FORM", K_FORM, 0 }, + { "HEADING", K_HEADING, 0 }, + { "PAGE", K_PAGE, 0 }, + { "PROGRAM", K_IGNORED, 0 }, + { "END", K_END, 0 }, + { "INCLUDE", K_INCLUDE, 0 }, + { "ASSIGNA", K_ASSIGNA, 0 }, + { "ASSIGNC", K_ASSIGNC, 0 }, + { "AIF", K_AIF, 0 }, + { "AELSE", K_AELSE, 0 }, + { "AENDI", K_AENDI, 0 }, + { "AREPEAT", K_AREPEAT, 0 }, + { "AENDR", K_AENDR, 0 }, + { "EXITM", K_EXITM, 0 }, + { "MACRO", K_MACRO, 0 }, + { "ENDM", K_ENDM, 0 }, + { "AWHILE", K_AWHILE, 0 }, + { "ALIGN", K_ALIGN, 0 }, + { "AENDW", K_AENDW, 0 }, + { "ALTERNATE", K_ALTERNATE, 0 }, + { "LOCAL", K_LOCAL, 0 }, + { NULL, 0, 0 } +}; + +/* Although the conditional operators are handled by gas, we need to + handle them here as well, in case they are used in a recursive + macro to end the recursion. */ + +static struct keyword mrikinfo[] = { + { "IFEQ", K_IFEQ, 0 }, + { "IFNE", K_IFNE, 0 }, + { "IFLT", K_IFLT, 0 }, + { "IFLE", K_IFLE, 0 }, + { "IFGE", K_IFGE, 0 }, + { "IFGT", K_IFGT, 0 }, + { "IFC", K_IFC, 0 }, + { "IFNC", K_IFNC, 0 }, + { "ELSEC", K_AELSE, 0 }, + { "ENDC", K_AENDI, 0 }, + { "MEXIT", K_EXITM, 0 }, + { "REPT", K_AREPEAT, 0 }, + { "IRP", K_IRP, 0 }, + { "IRPC", K_IRPC, 0 }, + { "ENDR", K_AENDR, 0 }, + { NULL, 0, 0 } +}; + +/* Look for a pseudo op on the line. If one's there then call + its handler. */ + +static int +process_pseudo_op (idx, line, acc) + int idx; + sb *line; + sb *acc; +{ + int oidx = idx; + + if (line->ptr[idx] == '.' || alternate || mri) + { + /* Scan forward and find pseudo name. */ + char *in; + hash_entry *ptr; + + char *s; + char *e; + if (line->ptr[idx] == '.') + idx++; + in = line->ptr + idx; + s = in; + e = s; + sb_reset (acc); + + while (idx < line->len && *e && ISFIRSTCHAR (*e)) + { + sb_add_char (acc, *e); + e++; + idx++; + } + + ptr = hash_lookup (&keyword_hash_table, acc); + + if (!ptr) + { +#if 0 + /* This one causes lots of pain when trying to preprocess + ordinary code. */ + WARNING ((stderr, _("Unrecognised pseudo op `%s'.\n"), + sb_name (acc))); +#endif + return 0; + } + if (ptr->value.i & LAB) + { + /* Output the label. */ + if (label.len) + { + fprintf (outfile, "%s:\t", sb_name (&label)); + } + else + fprintf (outfile, "\t"); + } + + if (mri && ptr->value.i == K_END) + { + sb t; + + sb_new (&t); + sb_add_buffer (&t, line->ptr + oidx, idx - oidx); + fprintf (outfile, "\t%s", sb_name (&t)); + sb_kill (&t); + } + + if (ptr->value.i & PROCESS) + { + /* Polish the rest of the line before handling the pseudo op. */ +#if 0 + strip_comments (line); +#endif + sb_reset (acc); + process_assigns (idx, line, acc); + sb_reset (line); + change_base (0, acc, line); + idx = 0; + } + if (!condass_on ()) + { + switch (ptr->value.i) + { + case K_AIF: + do_aif (idx, line); + break; + case K_AELSE: + do_aelse (); + break; + case K_AENDI: + do_aendi (); + break; + } + return 1; + } + else + { + switch (ptr->value.i) + { + case K_ALTERNATE: + alternate = 1; + macro_init (1, mri, 0, exp_get_abs); + return 1; + case K_AELSE: + do_aelse (); + return 1; + case K_AENDI: + do_aendi (); + return 1; + case K_ORG: + ERROR ((stderr, _("ORG command not allowed.\n"))); + break; + case K_RADIX: + do_radix (line); + return 1; + case K_DB: + do_data (idx, line, 1); + return 1; + case K_DW: + do_data (idx, line, 2); + return 1; + case K_DL: + do_data (idx, line, 4); + return 1; + case K_DATA: + do_data (idx, line, 0); + return 1; + case K_DATAB: + do_datab (idx, line); + return 1; + case K_SDATA: + do_sdata (idx, line, 0); + return 1; + case K_SDATAB: + do_sdatab (idx, line); + return 1; + case K_SDATAC: + do_sdata (idx, line, 'c'); + return 1; + case K_SDATAZ: + do_sdata (idx, line, 'z'); + return 1; + case K_ASSIGN: + do_assign (0, 0, line); + return 1; + case K_AIF: + do_aif (idx, line); + return 1; + case K_AREPEAT: + do_arepeat (idx, line); + return 1; + case K_AENDW: + do_aendw (); + return 1; + case K_AWHILE: + do_awhile (idx, line); + return 1; + case K_AENDR: + do_aendr (); + return 1; + case K_EQU: + do_assign (1, idx, line); + return 1; + case K_ALIGN: + do_align (idx, line); + return 1; + case K_RES: + do_res (idx, line, 0); + return 1; + case K_SRES: + do_res (idx, line, 's'); + return 1; + case K_INCLUDE: + do_include (idx, line); + return 1; + case K_LOCAL: + do_local (idx, line); + return 1; + case K_MACRO: + do_macro (idx, line); + return 1; + case K_ENDM: + do_endm (); + return 1; + case K_SRESC: + do_res (idx, line, 'c'); + return 1; + case K_PRINT: + do_print (idx, line); + return 1; + case K_FORM: + do_form (idx, line); + return 1; + case K_HEADING: + do_heading (idx, line); + return 1; + case K_PAGE: + do_page (); + return 1; + case K_GLOBAL: + case K_EXPORT: + do_export (line); + return 1; + case K_IMPORT: + return 1; + case K_SRESZ: + do_res (idx, line, 'z'); + return 1; + case K_IGNORED: + return 1; + case K_END: + do_end (line); + return 1; + case K_ASSIGNA: + do_assigna (idx, line); + return 1; + case K_ASSIGNC: + do_assignc (idx, line); + return 1; + case K_EXITM: + do_exitm (); + return 1; + case K_REG: + do_reg (idx, line); + return 1; + case K_IFEQ: + do_if (idx, line, EQ); + return 1; + case K_IFNE: + do_if (idx, line, NE); + return 1; + case K_IFLT: + do_if (idx, line, LT); + return 1; + case K_IFLE: + do_if (idx, line, LE); + return 1; + case K_IFGE: + do_if (idx, line, GE); + return 1; + case K_IFGT: + do_if (idx, line, GT); + return 1; + case K_IFC: + do_ifc (idx, line, 0); + return 1; + case K_IFNC: + do_ifc (idx, line, 1); + return 1; + case K_IRP: + do_irp (idx, line, 0); + return 1; + case K_IRPC: + do_irp (idx, line, 1); + return 1; + } + } + } + return 0; +} + +/* Add a keyword to the hash table. */ + +static void +add_keyword (name, code) + const char *name; + int code; +{ + sb label; + int j; + + sb_new (&label); + sb_add_string (&label, name); + + hash_add_to_int_table (&keyword_hash_table, &label, code); + + sb_reset (&label); + for (j = 0; name[j]; j++) + sb_add_char (&label, name[j] - 'A' + 'a'); + hash_add_to_int_table (&keyword_hash_table, &label, code); + + sb_kill (&label); +} + +/* Build the keyword hash table - put each keyword in the table twice, + once upper and once lower case. */ + +static void +process_init () +{ + int i; + + for (i = 0; kinfo[i].name; i++) + add_keyword (kinfo[i].name, kinfo[i].code); + + if (mri) + { + for (i = 0; mrikinfo[i].name; i++) + add_keyword (mrikinfo[i].name, mrikinfo[i].code); + } +} + +static void +do_define (string) + const char *string; +{ + sb label; + int res = 1; + hash_entry *ptr; + sb_new (&label); + + while (*string) + { + if (*string == '=') + { + sb value; + sb_new (&value); + string++; + while (*string) + { + sb_add_char (&value, *string); + string++; + } + exp_get_abs (_("Invalid expression on command line.\n"), + 0, &value, &res); + sb_kill (&value); + break; + } + sb_add_char (&label, *string); + + string++; + } + + ptr = hash_create (&vars, &label); + free_old_entry (ptr); + ptr->type = hash_integer; + ptr->value.i = res; + sb_kill (&label); +} + +char *program_name; + +/* The list of long options. */ +static struct option long_options[] = +{ + { "alternate", no_argument, 0, 'a' }, + { "include", required_argument, 0, 'I' }, + { "commentchar", required_argument, 0, 'c' }, + { "copysource", no_argument, 0, 's' }, + { "debug", no_argument, 0, 'd' }, + { "help", no_argument, 0, 'h' }, + { "mri", no_argument, 0, 'M' }, + { "output", required_argument, 0, 'o' }, + { "print", no_argument, 0, 'p' }, + { "unreasonable", no_argument, 0, 'u' }, + { "version", no_argument, 0, 'v' }, + { "define", required_argument, 0, 'd' }, + { NULL, no_argument, 0, 0 } +}; + +/* Show a usage message and exit. */ +static void +show_usage (file, status) + FILE *file; + int status; +{ + fprintf (file, _("\ +Usage: %s \n\ + [-a] [--alternate] enter alternate macro mode\n\ + [-c char] [--commentchar char] change the comment character from !\n\ + [-d] [--debug] print some debugging info\n\ + [-h] [--help] print this message\n\ + [-M] [--mri] enter MRI compatibility mode\n\ + [-o out] [--output out] set the output file\n\ + [-p] [--print] print line numbers\n"), program_name); + fprintf (file, _("\ + [-s] [--copysource] copy source through as comments \n\ + [-u] [--unreasonable] allow unreasonable nesting\n\ + [-v] [--version] print the program version\n\ + [-Dname=value] create preprocessor variable called name, with value\n\ + [-Ipath] add to include path list\n\ + [in-file]\n")); + if (status == 0) + printf (_("Report bugs to %s\n"), REPORT_BUGS_TO); + exit (status); +} + +/* Display a help message and exit. */ + +static void +show_help () +{ + printf (_("%s: Gnu Assembler Macro Preprocessor\n"), program_name); + show_usage (stdout, 0); +} + +int main PARAMS ((int, char **)); + +int +main (argc, argv) + int argc; + char **argv; +{ + int opt; + char *out_name = 0; + sp = include_stack; + + ifstack[0].on = 1; + ifi = 0; + +#if defined (HAVE_SETLOCALE) && defined (HAVE_LC_MESSAGES) + setlocale (LC_MESSAGES, ""); +#endif +#if defined (HAVE_SETLOCALE) + setlocale (LC_CTYPE, ""); +#endif + bindtextdomain (PACKAGE, LOCALEDIR); + textdomain (PACKAGE); + + program_name = argv[0]; + xmalloc_set_program_name (program_name); + + hash_new_table (101, &keyword_hash_table); + hash_new_table (101, &assign_hash_table); + hash_new_table (101, &vars); + + sb_new (&label); + + while ((opt = getopt_long (argc, argv, "I:sdhavc:upo:D:M", long_options, + (int *) NULL)) + != EOF) + { + switch (opt) + { + case 'o': + out_name = optarg; + break; + case 'u': + unreasonable = 1; + break; + case 'I': + { + include_path *p = (include_path *) xmalloc (sizeof (include_path)); + p->next = NULL; + sb_new (&p->path); + sb_add_string (&p->path, optarg); + if (paths_tail) + paths_tail->next = p; + else + paths_head = p; + paths_tail = p; + } + break; + case 'p': + print_line_number = 1; + break; + case 'c': + comment_char = optarg[0]; + break; + case 'a': + alternate = 1; + break; + case 's': + copysource = 1; + break; + case 'd': + stats = 1; + break; + case 'D': + do_define (optarg); + break; + case 'M': + mri = 1; + comment_char = ';'; + break; + case 'h': + show_help (); + /* NOTREACHED */ + case 'v': + /* This output is intended to follow the GNU standards document. */ + printf (_("GNU assembler pre-processor %s\n"), program_version); + printf (_("Copyright 1996 Free Software Foundation, Inc.\n")); + printf (_("\ +This program is free software; you may redistribute it under the terms of\n\ +the GNU General Public License. This program has absolutely no warranty.\n")); + exit (0); + /* NOTREACHED */ + case 0: + break; + default: + show_usage (stderr, 1); + /* NOTREACHED */ + } + } + + process_init (); + + macro_init (alternate, mri, 0, exp_get_abs); + + if (out_name) + { + outfile = fopen (out_name, "w"); + if (!outfile) + { + fprintf (stderr, _("%s: Can't open output file `%s'.\n"), + program_name, out_name); + exit (1); + } + } + else + { + outfile = stdout; + } + + chartype_init (); + if (!outfile) + outfile = stdout; + + /* Process all the input files. */ + + while (optind < argc) + { + if (new_file (argv[optind])) + { + process_file (); + } + else + { + fprintf (stderr, _("%s: Can't open input file `%s'.\n"), + program_name, argv[optind]); + exit (1); + } + optind++; + } + + quit (); + return 0; +} + +/* This function is used because an abort in some of the other files + may be compiled into as_abort because they include as.h. */ + +void +as_abort (file, line, fn) + const char *file, *fn; + int line; +{ + fprintf (stderr, _("Internal error, aborting at %s line %d"), file, line); + if (fn) + fprintf (stderr, " in %s", fn); + fprintf (stderr, _("\nPlease report this bug.\n")); + exit (1); +} + +/* duplicated from as internals */ + +static void +identify (char *file) +{ + static int identified; + + if (identified) + return; + identified++; + + if (file) + fprintf (stderr, "%s: ", file); + fprintf (stderr, _("Assembler messages:\n")); +} + +/* The number of warnings issued. */ +static int warning_count; + +/* The common portion of as_warn and as_warn_where. */ + +static void +as_warn_internal (char *file, unsigned int line, char *buffer) +{ + ++warning_count; + + identify (file); + if (file) + fprintf (stderr, "%s:%u: ", file, line); + fprintf (stderr, _("Warning: ")); + fputs (buffer, stderr); + (void) putc ('\n', stderr); +} + +/* Send to stderr a string as a warning, and locate warning + in input file(s). + Please only use this for when we have some recovery action. + Please explain in string (which may have '\n's) what recovery was + done. */ + +void +as_warn (const char *format, ...) +{ + va_list args; + char buffer[2000]; + + if (1 /*!flag_no_warnings*/) + { + va_start (args, format); + vsprintf (buffer, format, args); + va_end (args); + as_warn_internal ((char *) NULL, 0, buffer); + } +} + +/* Like as_bad but the file name and line number are passed in. + Unfortunately, we have to repeat the function in order to handle + the varargs correctly and portably. */ + +void +as_warn_where (char *file, unsigned int line, const char *format, ...) +{ + va_list args; + char buffer[2000]; + + if (1 /*!flag_no_warnings*/) + { + va_start (args, format); + vsprintf (buffer, format, args); + va_end (args); + as_warn_internal (file, line, buffer); + } +} + +/* Nonzero if we've hit a 'bad error', and should not write an obj file, + and exit with a nonzero error code. */ + +static int error_count; + +/* The common portion of as_bad and as_bad_where. */ + +static void +as_bad_internal (char *file, unsigned int line, char *buffer) +{ + ++error_count; + + identify (file); + if (file) + fprintf (stderr, "%s:%u: ", file, line); + fprintf (stderr, _("Error: ")); + fputs (buffer, stderr); + (void) putc ('\n', stderr); +} + +/* Send to stderr a string as a warning, and locate warning in input + file(s). Please us when there is no recovery, but we want to + continue processing but not produce an object file. + Please explain in string (which may have '\n's) what recovery was + done. */ + +void +as_bad (const char *format, ...) +{ + va_list args; + char buffer[2000]; + + va_start (args, format); + vsprintf (buffer, format, args); + va_end (args); + + as_bad_internal ((char *) NULL, 0, buffer); +} + +/* Like as_bad but the file name and line number are passed in. + Unfortunately, we have to repeat the function in order to handle + the varargs correctly and portably. */ + +void +as_bad_where (char *file, unsigned int line, const char *format, ...) +{ + va_list args; + char buffer[2000]; + + va_start (args, format); + vsprintf (buffer, format, args); + va_end (args); + + as_bad_internal (file, line, buffer); +} + +#ifndef LEX_AT +/* The m88k unfortunately uses @ as a label beginner. */ +#define LEX_AT 0 +#endif + +#ifndef LEX_BR +/* The RS/6000 assembler uses {,},[,] as parts of symbol names. */ +#define LEX_BR 0 +#endif + +#ifndef LEX_PCT +/* The Delta 68k assembler permits % inside label names. */ +#define LEX_PCT 0 +#endif + +#ifndef LEX_QM +/* The PowerPC Windows NT assemblers permits ? inside label names. */ +#define LEX_QM 0 +#endif + +#ifndef LEX_HASH +/* The IA-64 assembler uses # as a suffix designating a symbol. We include + it in the symbol and strip it out in tc_canonicalize_symbol_name. */ +#define LEX_HASH 0 +#endif + +#ifndef LEX_DOLLAR +/* The a29k assembler does not permits labels to start with $. */ +#define LEX_DOLLAR 3 +#endif + +#ifndef LEX_TILDE +/* The Delta 68k assembler permits ~ at start of label names. */ +#define LEX_TILDE 0 +#endif + +/* Used by is_... macros. our ctype[]. */ +char lex_type[256] = { + 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, LEX_HASH, LEX_DOLLAR, LEX_PCT, 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, LEX_QM, /* 0123456789:;<=>? */ + LEX_AT, 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, LEX_BR, 0, LEX_BR, 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, LEX_BR, 0, LEX_BR, LEX_TILDE, 0, /* pqrstuvwxyz{|}~. */ + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 +}; + +/* just a stub to satisfy unused function in sb.o */ +int do_scrub_chars (int (*get) (char*, int), char *tostart, int tolen) {} diff -urNbB binutils-2.16.91.0.2.org/gas/macro.c binutils-2.16.91.0.2/gas/macro.c --- binutils-2.16.91.0.2.org/gas/macro.c 2005-05-10 22:46:44.000000000 +0000 +++ binutils-2.16.91.0.2/gas/macro.c 2005-07-21 18:31:04.000000000 +0000 @@ -77,8 +77,8 @@ static int get_apost_token (int, sb *, sb *, int); static int sub_actual (int, sb *, sb *, struct hash_control *, int, sb *, int); static const char *macro_expand_body - (sb *, sb *, formal_entry *, struct hash_control *, const macro_entry *); -static const char *macro_expand (int, sb *, macro_entry *, sb *); + (sb *, sb *, formal_entry *, struct hash_control *, const macro_entry *, int); +static const char *macro_expand (int, sb *, macro_entry *, sb *, int); static void free_macro(macro_entry *); #define ISWHITE(x) ((x) == ' ' || (x) == '\t') @@ -762,7 +762,7 @@ static const char * macro_expand_body (sb *in, sb *out, formal_entry *formals, - struct hash_control *formal_hash, const macro_entry *macro) + struct hash_control *formal_hash, const macro_entry *macro, int comment_char) { sb t; int src = 0, inquote = 0, macro_line = 0; @@ -794,7 +794,14 @@ else if (in->ptr[src] == '\\') { src++; - if (src < in->len && in->ptr[src] == '(') + if (in->ptr[src] == comment_char && comment_char != '\0') + { + /* This is a comment, just drop the rest of the line. */ + while (src < in->len + && in->ptr[src] != '\n') + src++; + } + else if (src < in->len && in->ptr[src] == '(') { /* Sub in till the next ')' literally. */ src++; @@ -875,7 +882,7 @@ else { src = sb_skip_white (src + 5, in); - while (in->ptr[src] != '\n') + while (in->ptr[src] != '\n' && in->ptr[src] != comment_char) { const char *name; formal_entry *f = new_formal (); @@ -911,6 +918,17 @@ } } } + else if (comment_char != '\0' + && in->ptr[src] == comment_char + && src + 1 < in->len + && in->ptr[src + 1] == comment_char + && !inquote) + { + /* Two comment chars in a row cause the rest of the line to + be dropped. */ + while (src < in->len && in->ptr[src] != '\n') + src++; + } else if (in->ptr[src] == '"' || (macro_mri && in->ptr[src] == '\'')) { @@ -992,7 +1010,7 @@ body. */ static const char * -macro_expand (int idx, sb *in, macro_entry *m, sb *out) +macro_expand (int idx, sb *in, macro_entry *m, sb *out, int comment_char) { sb t; formal_entry *ptr; @@ -1038,7 +1056,7 @@ /* Peel off the actuals and store them away in the hash tables' actuals. */ idx = sb_skip_white (idx, in); - while (idx < in->len) + while (idx < in->len && in->ptr[idx] != comment_char) { int scan; @@ -1167,7 +1185,7 @@ sb_add_string (&ptr->actual, buffer); } - err = macro_expand_body (&m->sub, out, m->formals, m->formal_hash, m); + err = macro_expand_body (&m->sub, out, m->formals, m->formal_hash, m, comment_char); } /* Discard any unnamed formal arguments. */ @@ -1200,7 +1218,7 @@ *EXPAND. Return 1 if a macro is found, 0 otherwise. */ int -check_macro (const char *line, sb *expand, +check_macro (const char *line, sb *expand, int comment_char, const char **error, macro_entry **info) { const char *s; @@ -1235,7 +1253,7 @@ sb_add_char (&line_sb, *s++); sb_new (expand); - *error = macro_expand (0, &line_sb, macro, expand); + *error = macro_expand (0, &line_sb, macro, expand, comment_char); sb_kill (&line_sb); @@ -1296,7 +1314,7 @@ success, or an error message otherwise. */ const char * -expand_irp (int irpc, int idx, sb *in, sb *out, int (*get_line) (sb *)) +expand_irp (int irpc, int idx, sb *in, sb *out, int (*get_line) (sb *), int comment_char) { sb sub; formal_entry f; @@ -1329,16 +1347,16 @@ sb_reset (out); idx = sb_skip_comma (idx, in); - if (idx >= in->len) + if (idx >= in->len || in->ptr[idx] == comment_char) { /* Expand once with a null string. */ - err = macro_expand_body (&sub, out, &f, h, 0); + err = macro_expand_body (&sub, out, &f, h, 0, comment_char); } else { if (irpc && in->ptr[idx] == '"') ++idx; - while (idx < in->len) + while (idx < in->len && in->ptr[idx] != comment_char) { if (!irpc) idx = get_any_string (idx, in, &f.actual); @@ -1349,7 +1367,7 @@ int nxt; nxt = sb_skip_white (idx + 1, in); - if (nxt >= in->len) + if (nxt >= in->len || in->ptr[nxt] == comment_char) { idx = nxt; break; @@ -1359,7 +1377,7 @@ sb_add_char (&f.actual, in->ptr[idx]); ++idx; } - err = macro_expand_body (&sub, out, &f, h, 0); + err = macro_expand_body (&sub, out, &f, h, 0, comment_char); if (err != NULL) break; if (!irpc) diff -urNbB binutils-2.16.91.0.2.org/gas/macro.h binutils-2.16.91.0.2/gas/macro.h --- binutils-2.16.91.0.2.org/gas/macro.h 2005-05-10 22:46:44.000000000 +0000 +++ binutils-2.16.91.0.2/gas/macro.h 2005-07-21 18:31:04.000000000 +0000 @@ -90,8 +90,8 @@ extern void macro_mri_mode (int); extern const char *define_macro (int, sb *, sb *, int (*) (sb *), char *, unsigned int, const char **); -extern int check_macro (const char *, sb *, const char **, macro_entry **); +extern int check_macro (const char *, sb *, int, const char **, macro_entry **); extern void delete_macro (const char *); -extern const char *expand_irp (int, int, sb *, sb *, int (*) (sb *)); +extern const char *expand_irp (int, int, sb *, sb *, int (*) (sb *), int); #endif diff -urNbB binutils-2.16.91.0.2.org/gas/macro.h.orig binutils-2.16.91.0.2/gas/macro.h.orig --- binutils-2.16.91.0.2.org/gas/macro.h.orig 1970-01-01 00:00:00.000000000 +0000 +++ binutils-2.16.91.0.2/gas/macro.h.orig 2005-05-10 22:46:44.000000000 +0000 @@ -0,0 +1,97 @@ +/* macro.h - header file for macro support for gas + Copyright 1994, 1995, 1996, 1997, 1998, 2000, 2002, 2003, 2004 + Free Software Foundation, Inc. + + Written by Steve and Judy Chamberlain of Cygnus Support, + sac@cygnus.com + + 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 2, or (at your option) + any later version. + + GAS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GAS; see the file COPYING. If not, write to the Free + Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +#ifndef MACRO_H + +#define MACRO_H + +#include "ansidecl.h" +#include "sb.h" + +/* Structures used to store macros. + + Each macro knows its name and included text. It gets built with a + list of formal arguments, and also keeps a hash table which points + into the list to speed up formal search. Each formal knows its + name and its default value. Each time the macro is expanded, the + formals get the actual values attached to them. */ + +/* Describe the formal arguments to a macro. */ + +typedef struct formal_struct { + struct formal_struct *next; /* Next formal in list. */ + sb name; /* Name of the formal. */ + sb def; /* The default value. */ + sb actual; /* The actual argument (changed on each expansion). */ + int index; /* The index of the formal 0..formal_count - 1. */ + enum formal_type + { + FORMAL_OPTIONAL, + FORMAL_REQUIRED, + FORMAL_VARARG + } type; /* The kind of the formal. */ +} formal_entry; + +/* Other values found in the index field of a formal_entry. */ +#define QUAL_INDEX (-1) +#define NARG_INDEX (-2) +#define LOCAL_INDEX (-3) + +/* Describe the macro. */ + +typedef struct macro_struct +{ + sb sub; /* Substitution text. */ + int formal_count; /* Number of formal args. */ + formal_entry *formals; /* Pointer to list of formal_structs. */ + struct hash_control *formal_hash; /* Hash table of formals. */ + const char *name; /* Macro name. */ + char *file; /* File the macro was defined in. */ + unsigned int line; /* Line number of definition. */ +} macro_entry; + +/* Whether any macros have been defined. */ + +extern int macro_defined; + +/* The macro nesting level. */ + +extern int macro_nest; + +/* The macro hash table. */ + +extern struct hash_control *macro_hash; + +extern int buffer_and_nest (const char *, const char *, sb *, int (*) (sb *)); +extern void macro_init + (int, int, int, int (*) (const char *, int, sb *, int *)); +extern void macro_set_alternate (int); +extern void macro_mri_mode (int); +extern const char *define_macro + (int, sb *, sb *, int (*) (sb *), char *, unsigned int, const char **); +extern int check_macro (const char *, sb *, const char **, macro_entry **); +extern void delete_macro (const char *); +extern const char *expand_irp (int, int, sb *, sb *, int (*) (sb *)); + +#endif diff -urNbB binutils-2.16.91.0.2.org/gas/read.c binutils-2.16.91.0.2/gas/read.c --- binutils-2.16.91.0.2.org/gas/read.c 2005-06-22 20:53:34.000000000 +0000 +++ binutils-2.16.91.0.2/gas/read.c 2005-07-21 18:31:04.000000000 +0000 @@ -555,7 +555,7 @@ const char *err; macro_entry *macro; - if (check_macro (line, &out, &err, ¯o)) + if (check_macro (line, &out, '\0', &err, ¯o)) { if (err != NULL) as_bad ("%s", err); @@ -1958,7 +1958,7 @@ sb_new (&out); - err = expand_irp (irpc, 0, &s, &out, get_line_sb); + err = expand_irp (irpc, 0, &s, &out, get_line_sb, '\0'); if (err != NULL) as_bad_where (file, line, "%s", err); diff -urNbB binutils-2.16.91.0.2.org/gas/read.c.orig binutils-2.16.91.0.2/gas/read.c.orig --- binutils-2.16.91.0.2.org/gas/read.c.orig 1970-01-01 00:00:00.000000000 +0000 +++ binutils-2.16.91.0.2/gas/read.c.orig 2005-06-22 20:53:34.000000000 +0000 @@ -0,0 +1,5322 @@ +/* read.c - read a source file - + Copyright 1986, 1987, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, + 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 + 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 2, or (at your option) +any later version. + +GAS is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GAS; see the file COPYING. If not, write to the Free +Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA +02110-1301, USA. */ + +/* If your chars aren't 8 bits, you will change this a bit (eg. to 0xFF). + But then, GNU isn't spozed to run on your machine anyway. + (RMS is so shortsighted sometimes.) */ +#define MASK_CHAR ((int)(unsigned char) -1) + +/* This is the largest known floating point format (for now). It will + grow when we do 4361 style flonums. */ +#define MAXIMUM_NUMBER_OF_CHARS_FOR_FLOAT (16) + +/* Routines that read assembler source text to build spaghetti in memory. + Another group of these functions is in the expr.c module. */ + +#include "as.h" +#include "safe-ctype.h" +#include "subsegs.h" +#include "sb.h" +#include "macro.h" +#include "obstack.h" +#include "listing.h" +#include "ecoff.h" +#include "dw2gencfi.h" + +#ifndef TC_START_LABEL +#define TC_START_LABEL(x,y) (x == ':') +#endif + +/* Set by the object-format or the target. */ +#ifndef TC_IMPLICIT_LCOMM_ALIGNMENT +#define TC_IMPLICIT_LCOMM_ALIGNMENT(SIZE, P2VAR) \ + do \ + { \ + if ((SIZE) >= 8) \ + (P2VAR) = 3; \ + else if ((SIZE) >= 4) \ + (P2VAR) = 2; \ + else if ((SIZE) >= 2) \ + (P2VAR) = 1; \ + else \ + (P2VAR) = 0; \ + } \ + while (0) +#endif + +char *input_line_pointer; /*->next char of source file to parse. */ + +#if BITS_PER_CHAR != 8 +/* The following table is indexed by[(char)] and will break if + a char does not have exactly 256 states (hopefully 0:255!)! */ +die horribly; +#endif + +#ifndef LEX_AT +/* The m88k unfortunately uses @ as a label beginner. */ +#define LEX_AT 0 +#endif + +#ifndef LEX_BR +/* The RS/6000 assembler uses {,},[,] as parts of symbol names. */ +#define LEX_BR 0 +#endif + +#ifndef LEX_PCT +/* The Delta 68k assembler permits % inside label names. */ +#define LEX_PCT 0 +#endif + +#ifndef LEX_QM +/* The PowerPC Windows NT assemblers permits ? inside label names. */ +#define LEX_QM 0 +#endif + +#ifndef LEX_HASH +/* The IA-64 assembler uses # as a suffix designating a symbol. We include + it in the symbol and strip it out in tc_canonicalize_symbol_name. */ +#define LEX_HASH 0 +#endif + +#ifndef LEX_DOLLAR +/* The a29k assembler does not permits labels to start with $. */ +#define LEX_DOLLAR 3 +#endif + +#ifndef LEX_TILDE +/* The Delta 68k assembler permits ~ at start of label names. */ +#define LEX_TILDE 0 +#endif + +/* Used by is_... macros. our ctype[]. */ +char lex_type[256] = { + 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, LEX_HASH, LEX_DOLLAR, LEX_PCT, 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, LEX_QM, /* 0123456789:;<=>? */ + LEX_AT, 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, LEX_BR, 0, LEX_BR, 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, LEX_BR, 0, LEX_BR, LEX_TILDE, 0, /* pqrstuvwxyz{|}~. */ + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 +}; + +/* In: a character. + Out: 1 if this character ends a line. */ +char is_end_of_line[256] = { +#ifdef CR_EOL + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, /* @abcdefghijklmno */ +#else + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, /* @abcdefghijklmno */ +#endif + 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, /* 0123456789:;<=>? */ + 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, /* */ + 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 /* */ +}; + +#ifndef TC_CASE_SENSITIVE +char original_case_string[128]; +#endif + +/* Functions private to this file. */ + +static char *buffer; /* 1st char of each buffer of lines is here. */ +static char *buffer_limit; /*->1 + last char in buffer. */ + +/* TARGET_BYTES_BIG_ENDIAN is required to be defined to either 0 or 1 + in the tc-.h file. See the "Porting GAS" section of the + internals manual. */ +int target_big_endian = TARGET_BYTES_BIG_ENDIAN; + +/* Variables for handling include file directory table. */ + +/* Table of pointers to directories to search for .include's. */ +char **include_dirs; + +/* How many are in the table. */ +int include_dir_count; + +/* Length of longest in table. */ +int include_dir_maxlen = 1; + +#ifndef WORKING_DOT_WORD +struct broken_word *broken_words; +int new_broken_words; +#endif + +/* The current offset into the absolute section. We don't try to + build frags in the absolute section, since no data can be stored + there. We just keep track of the current offset. */ +addressT abs_section_offset; + +/* If this line had an MRI style label, it is stored in this variable. + This is used by some of the MRI pseudo-ops. */ +symbolS *line_label; + +/* This global variable is used to support MRI common sections. We + translate such sections into a common symbol. This variable is + non-NULL when we are in an MRI common section. */ +symbolS *mri_common_symbol; + +/* In MRI mode, after a dc.b pseudo-op with an odd number of bytes, we + need to align to an even byte boundary unless the next pseudo-op is + dc.b, ds.b, or dcb.b. This variable is set to 1 if an alignment + may be needed. */ +static int mri_pending_align; + +#ifndef NO_LISTING +#ifdef OBJ_ELF +/* This variable is set to be non-zero if the next string we see might + be the name of the source file in DWARF debugging information. See + the comment in emit_expr for the format we look for. */ +static int dwarf_file_string; +#endif +#endif + +static void do_s_func (int end_p, const char *default_prefix); +static void do_align (int, char *, int, int); +static void s_align (int, int); +static void s_altmacro (int); +static void s_bad_end (int); +static int hex_float (int, char *); +static segT get_known_segmented_expression (expressionS * expP); +static void pobegin (void); +static int get_line_sb (sb *); +static void generate_file_debug (void); +static char *_find_end_of_line (char *, int, int); + +void +read_begin (void) +{ + const char *p; + + pobegin (); + obj_read_begin_hook (); + + /* Something close -- but not too close -- to a multiple of 1024. + The debugging malloc I'm using has 24 bytes of overhead. */ + obstack_begin (¬es, chunksize); + obstack_begin (&cond_obstack, chunksize); + + /* Use machine dependent syntax. */ + for (p = line_separator_chars; *p; p++) + is_end_of_line[(unsigned char) *p] = 1; + /* Use more. FIXME-SOMEDAY. */ + + if (flag_mri) + lex_type['?'] = 3; +} + +#ifndef TC_ADDRESS_BYTES +#ifdef BFD_ASSEMBLER +#define TC_ADDRESS_BYTES address_bytes + +static inline int +address_bytes (void) +{ + /* Choose smallest of 1, 2, 4, 8 bytes that is large enough to + contain an address. */ + int n = (stdoutput->arch_info->bits_per_address - 1) / 8; + n |= n >> 1; + n |= n >> 2; + n += 1; + return n; +} +#endif +#endif + +/* Set up pseudo-op tables. */ + +static struct hash_control *po_hash; + +static const pseudo_typeS potable[] = { + {"abort", s_abort, 0}, + {"align", s_align_ptwo, 0}, + {"altmacro", s_altmacro, 1}, + {"ascii", stringer, 0}, + {"asciz", stringer, 1}, + {"balign", s_align_bytes, 0}, + {"balignw", s_align_bytes, -2}, + {"balignl", s_align_bytes, -4}, +/* block */ + {"byte", cons, 1}, + {"comm", s_comm, 0}, + {"common", s_mri_common, 0}, + {"common.s", s_mri_common, 1}, + {"data", s_data, 0}, + {"dc", cons, 2}, +#ifdef TC_ADDRESS_BYTES + {"dc.a", cons, 0}, +#endif + {"dc.b", cons, 1}, + {"dc.d", float_cons, 'd'}, + {"dc.l", cons, 4}, + {"dc.s", float_cons, 'f'}, + {"dc.w", cons, 2}, + {"dc.x", float_cons, 'x'}, + {"dcb", s_space, 2}, + {"dcb.b", s_space, 1}, + {"dcb.d", s_float_space, 'd'}, + {"dcb.l", s_space, 4}, + {"dcb.s", s_float_space, 'f'}, + {"dcb.w", s_space, 2}, + {"dcb.x", s_float_space, 'x'}, + {"ds", s_space, 2}, + {"ds.b", s_space, 1}, + {"ds.d", s_space, 8}, + {"ds.l", s_space, 4}, + {"ds.p", s_space, 12}, + {"ds.s", s_space, 4}, + {"ds.w", s_space, 2}, + {"ds.x", s_space, 12}, + {"debug", s_ignore, 0}, +#ifdef S_SET_DESC + {"desc", s_desc, 0}, +#endif +/* dim */ + {"double", float_cons, 'd'}, +/* dsect */ + {"eject", listing_eject, 0}, /* Formfeed listing. */ + {"else", s_else, 0}, + {"elsec", s_else, 0}, + {"elseif", s_elseif, (int) O_ne}, + {"end", s_end, 0}, + {"endc", s_endif, 0}, + {"endfunc", s_func, 1}, + {"endif", s_endif, 0}, + {"endm", s_bad_end, 0}, + {"endr", s_bad_end, 1}, +/* endef */ + {"equ", s_set, 0}, + {"equiv", s_set, 1}, + {"err", s_err, 0}, + {"error", s_errwarn, 1}, + {"exitm", s_mexit, 0}, +/* extend */ + {"extern", s_ignore, 0}, /* We treat all undef as ext. */ + {"appfile", s_app_file, 1}, + {"appline", s_app_line, 0}, + {"fail", s_fail, 0}, + {"file", s_app_file, 0}, + {"fill", s_fill, 0}, + {"float", float_cons, 'f'}, + {"format", s_ignore, 0}, + {"func", s_func, 0}, + {"global", s_globl, 0}, + {"globl", s_globl, 0}, + {"hword", cons, 2}, + {"if", s_if, (int) O_ne}, + {"ifb", s_ifb, 1}, + {"ifc", s_ifc, 0}, + {"ifdef", s_ifdef, 0}, + {"ifeq", s_if, (int) O_eq}, + {"ifeqs", s_ifeqs, 0}, + {"ifge", s_if, (int) O_ge}, + {"ifgt", s_if, (int) O_gt}, + {"ifle", s_if, (int) O_le}, + {"iflt", s_if, (int) O_lt}, + {"ifnb", s_ifb, 0}, + {"ifnc", s_ifc, 1}, + {"ifndef", s_ifdef, 1}, + {"ifne", s_if, (int) O_ne}, + {"ifnes", s_ifeqs, 1}, + {"ifnotdef", s_ifdef, 1}, + {"incbin", s_incbin, 0}, + {"include", s_include, 0}, + {"int", cons, 4}, + {"irp", s_irp, 0}, + {"irep", s_irp, 0}, + {"irpc", s_irp, 1}, + {"irepc", s_irp, 1}, + {"lcomm", s_lcomm, 0}, + {"lflags", listing_flags, 0}, /* Listing flags. */ + {"linkonce", s_linkonce, 0}, + {"list", listing_list, 1}, /* Turn listing on. */ + {"llen", listing_psize, 1}, + {"long", cons, 4}, + {"lsym", s_lsym, 0}, + {"macro", s_macro, 0}, + {"mexit", s_mexit, 0}, + {"mri", s_mri, 0}, + {".mri", s_mri, 0}, /* Special case so .mri works in MRI mode. */ + {"name", s_ignore, 0}, + {"noaltmacro", s_altmacro, 0}, + {"noformat", s_ignore, 0}, + {"nolist", listing_list, 0}, /* Turn listing off. */ + {"nopage", listing_nopage, 0}, + {"octa", cons, 16}, + {"offset", s_struct, 0}, + {"org", s_org, 0}, + {"p2align", s_align_ptwo, 0}, + {"p2alignw", s_align_ptwo, -2}, + {"p2alignl", s_align_ptwo, -4}, + {"page", listing_eject, 0}, + {"plen", listing_psize, 0}, + {"print", s_print, 0}, + {"psize", listing_psize, 0}, /* Set paper size. */ + {"purgem", s_purgem, 0}, + {"quad", cons, 8}, + {"rep", s_rept, 0}, + {"rept", s_rept, 0}, + {"rva", s_rva, 4}, + {"sbttl", listing_title, 1}, /* Subtitle of listing. */ +/* scl */ +/* sect */ + {"set", s_set, 0}, + {"short", cons, 2}, + {"single", float_cons, 'f'}, +/* size */ + {"space", s_space, 0}, + {"skip", s_space, 0}, + {"sleb128", s_leb128, 1}, + {"spc", s_ignore, 0}, + {"stabd", s_stab, 'd'}, + {"stabn", s_stab, 'n'}, + {"stabs", s_stab, 's'}, + {"string", stringer, 1}, + {"struct", s_struct, 0}, +/* tag */ + {"text", s_text, 0}, + + /* This is for gcc to use. It's only just been added (2/94), so gcc + won't be able to use it for a while -- probably a year or more. + But once this has been released, check with gcc maintainers + before deleting it or even changing the spelling. */ + {"this_GCC_requires_the_GNU_assembler", s_ignore, 0}, + /* If we're folding case -- done for some targets, not necessarily + all -- the above string in an input file will be converted to + this one. Match it either way... */ + {"this_gcc_requires_the_gnu_assembler", s_ignore, 0}, + + {"title", listing_title, 0}, /* Listing title. */ + {"ttl", listing_title, 0}, +/* type */ + {"uleb128", s_leb128, 0}, +/* use */ +/* val */ + {"xcom", s_comm, 0}, + {"xdef", s_globl, 0}, + {"xref", s_ignore, 0}, + {"xstabs", s_xstab, 's'}, + {"warning", s_errwarn, 0}, + {"word", cons, 2}, + {"zero", s_space, 0}, + {NULL, NULL, 0} /* End sentinel. */ +}; + +static offsetT +get_absolute_expr (expressionS *exp) +{ + expression (exp); + if (exp->X_op != O_constant) + { + if (exp->X_op != O_absent) + as_bad (_("bad or irreducible absolute expression")); + exp->X_add_number = 0; + } + return exp->X_add_number; +} + +offsetT +get_absolute_expression (void) +{ + expressionS exp; + + return get_absolute_expr (&exp); +} + +static int pop_override_ok = 0; +static const char *pop_table_name; + +void +pop_insert (const pseudo_typeS *table) +{ + const char *errtxt; + const pseudo_typeS *pop; + for (pop = table; pop->poc_name; pop++) + { + errtxt = hash_insert (po_hash, pop->poc_name, (char *) pop); + if (errtxt && (!pop_override_ok || strcmp (errtxt, "exists"))) + as_fatal (_("error constructing %s pseudo-op table: %s"), pop_table_name, + errtxt); + } +} + +#ifndef md_pop_insert +#define md_pop_insert() pop_insert(md_pseudo_table) +#endif + +#ifndef obj_pop_insert +#define obj_pop_insert() pop_insert(obj_pseudo_table) +#endif + +#ifndef cfi_pop_insert +#define cfi_pop_insert() pop_insert(cfi_pseudo_table) +#endif + +static void +pobegin (void) +{ + po_hash = hash_new (); + + /* Do the target-specific pseudo ops. */ + pop_table_name = "md"; + md_pop_insert (); + + /* Now object specific. Skip any that were in the target table. */ + pop_table_name = "obj"; + pop_override_ok = 1; + obj_pop_insert (); + + /* Now portable ones. Skip any that we've seen already. */ + pop_table_name = "standard"; + pop_insert (potable); + +#ifdef TARGET_USE_CFIPOP + pop_table_name = "cfi"; + pop_override_ok = 1; + cfi_pop_insert (); +#endif +} + +#define HANDLE_CONDITIONAL_ASSEMBLY() \ + if (ignore_input ()) \ + { \ + char *eol = find_end_of_line (input_line_pointer, flag_m68k_mri); \ + input_line_pointer = (input_line_pointer <= buffer_limit \ + && eol >= buffer_limit) \ + ? buffer_limit \ + : eol + 1; \ + continue; \ + } + +/* This function is used when scrubbing the characters between #APP + and #NO_APP. */ + +static char *scrub_string; +static char *scrub_string_end; + +static int +scrub_from_string (char *buf, int buflen) +{ + int copy; + + copy = scrub_string_end - scrub_string; + if (copy > buflen) + copy = buflen; + memcpy (buf, scrub_string, copy); + scrub_string += copy; + return copy; +} + +/* Helper function of read_a_source_file, which tries to expand a macro. */ +static int +try_macro (char term, const char *line) +{ + sb out; + const char *err; + macro_entry *macro; + + if (check_macro (line, &out, &err, ¯o)) + { + if (err != NULL) + as_bad ("%s", err); + *input_line_pointer++ = term; + input_scrub_include_sb (&out, + input_line_pointer, 1); + sb_kill (&out); + buffer_limit = + input_scrub_next_buffer (&input_line_pointer); +#ifdef md_macro_info + md_macro_info (macro); +#endif + return 1; + } + return 0; +} + +/* We read the file, putting things into a web that represents what we + have been reading. */ +void +read_a_source_file (char *name) +{ + register char c; + register char *s; /* String of symbol, '\0' appended. */ + register int temp; + pseudo_typeS *pop; + +#ifdef WARN_COMMENTS + found_comment = 0; +#endif + + buffer = input_scrub_new_file (name); + + listing_file (name); + listing_newline (NULL); + register_dependency (name); + + /* Generate debugging information before we've read anything in to denote + this file as the "main" source file and not a subordinate one + (e.g. N_SO vs N_SOL in stabs). */ + generate_file_debug (); + + while ((buffer_limit = input_scrub_next_buffer (&input_line_pointer)) != 0) + { /* We have another line to parse. */ +#ifndef NO_LISTING + /* In order to avoid listing macro expansion lines with labels + multiple times, keep track of which line was last issued. */ + static char *last_eol; + + last_eol = NULL; +#endif + know (buffer_limit[-1] == '\n'); /* Must have a sentinel. */ + + 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 (is_end_of_line[(unsigned char) input_line_pointer[-1]]) + { +#ifdef md_start_line_hook + md_start_line_hook (); +#endif + if (input_line_pointer[-1] == '\n') + bump_line_counters (); + + line_label = NULL; + + if (LABELS_WITHOUT_COLONS || flag_m68k_mri) + { + /* Text at the start of a line must be a label, we + run down and stick a colon in. */ + if (is_name_beginner (*input_line_pointer)) + { + char *line_start = input_line_pointer; + char c; + int mri_line_macro; + + LISTING_NEWLINE (); + HANDLE_CONDITIONAL_ASSEMBLY (); + + c = get_symbol_end (); + + /* In MRI mode, the EQU and MACRO pseudoops must + be handled specially. */ + mri_line_macro = 0; + if (flag_m68k_mri) + { + char *rest = input_line_pointer + 1; + + if (*rest == ':') + ++rest; + if (*rest == ' ' || *rest == '\t') + ++rest; + if ((strncasecmp (rest, "EQU", 3) == 0 + || strncasecmp (rest, "SET", 3) == 0) + && (rest[3] == ' ' || rest[3] == '\t')) + { + input_line_pointer = rest + 3; + equals (line_start, + strncasecmp (rest, "SET", 3) == 0); + continue; + } + if (strncasecmp (rest, "MACRO", 5) == 0 + && (rest[5] == ' ' + || rest[5] == '\t' + || is_end_of_line[(unsigned char) rest[5]])) + mri_line_macro = 1; + } + + /* In MRI mode, we need to handle the MACRO + pseudo-op specially: we don't want to put the + symbol in the symbol table. */ + if (!mri_line_macro +#ifdef TC_START_LABEL_WITHOUT_COLON + && TC_START_LABEL_WITHOUT_COLON(c, + input_line_pointer) +#endif + ) + line_label = colon (line_start); + else + line_label = symbol_create (line_start, + absolute_section, + (valueT) 0, + &zero_address_frag); + + *input_line_pointer = c; + if (c == ':') + input_line_pointer++; + } + } + } + + /* We are at the beginning 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 == 0) + c = *input_line_pointer++; + + know (c != ' '); /* No further leading whitespace. */ + +#ifndef NO_LISTING + /* If listing is on, and we are expanding a macro, then give + the listing code the contents of the expanded line. */ + if (listing) + { + if ((listing & LISTING_MACEXP) && macro_nest > 0) + { + char *copy; + int len; + + /* Find the end of the current expanded macro line. */ + s = find_end_of_line (input_line_pointer - 1, flag_m68k_mri); + + if (s != last_eol) + { + last_eol = s; + /* Copy it for safe keeping. Also give an indication of + how much macro nesting is involved at this point. */ + len = s - (input_line_pointer - 1); + copy = (char *) xmalloc (len + macro_nest + 2); + memset (copy, '>', macro_nest); + copy[macro_nest] = ' '; + memcpy (copy + macro_nest + 1, input_line_pointer - 1, len); + copy[macro_nest + 1 + len] = '\0'; + + /* Install the line with the listing facility. */ + listing_newline (copy); + } + } + else + listing_newline (NULL); + } +#endif + /* 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. */ + HANDLE_CONDITIONAL_ASSEMBLY (); + + 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. */ + if (TC_START_LABEL (c, input_line_pointer)) + { + if (flag_m68k_mri) + { + char *rest = input_line_pointer + 1; + + /* In MRI mode, \tsym: set 0 is permitted. */ + if (*rest == ':') + ++rest; + + if (*rest == ' ' || *rest == '\t') + ++rest; + + if ((strncasecmp (rest, "EQU", 3) == 0 + || strncasecmp (rest, "SET", 3) == 0) + && (rest[3] == ' ' || rest[3] == '\t')) + { + input_line_pointer = rest + 3; + equals (s, 1); + continue; + } + } + + line_label = colon (s); /* User-defined label. */ + /* Put ':' back for error messages' sake. */ + *input_line_pointer++ = ':'; +#ifdef tc_check_label + tc_check_label (line_label); +#endif + /* Input_line_pointer->after ':'. */ + SKIP_WHITESPACE (); + } + else if ((c == '=' + || ((c == ' ' || c == '\t') + && input_line_pointer[1] == '=')) +#ifdef TC_EQUAL_IN_INSN + && !TC_EQUAL_IN_INSN (c, s) +#endif + ) + { + equals (s, 1); + demand_empty_rest_of_line (); + } + else + { + /* Expect pseudo-op or machine instruction. */ + pop = NULL; + +#ifndef TC_CASE_SENSITIVE + { + char *s2 = s; + + strncpy (original_case_string, s2, sizeof (original_case_string)); + original_case_string[sizeof (original_case_string) - 1] = 0; + + while (*s2) + { + *s2 = TOLOWER (*s2); + s2++; + } + } +#endif + if (NO_PSEUDO_DOT || flag_m68k_mri) + { + /* The MRI assembler and the m88k use pseudo-ops + without a period. */ + pop = (pseudo_typeS *) hash_find (po_hash, s); + if (pop != NULL && pop->poc_handler == NULL) + pop = NULL; + } + + if (pop != NULL + || (!flag_m68k_mri && *s == '.')) + { + /* PSEUDO - OP. + + 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 '.'. */ + + if (pop == NULL) + pop = (pseudo_typeS *) hash_find (po_hash, s + 1); + if (pop && !pop->poc_handler) + pop = NULL; + + /* In MRI mode, we may need to insert an + automatic alignment directive. What a hack + this is. */ + if (mri_pending_align + && (pop == NULL + || !((pop->poc_handler == cons + && pop->poc_val == 1) + || (pop->poc_handler == s_space + && pop->poc_val == 1) +#ifdef tc_conditional_pseudoop + || tc_conditional_pseudoop (pop) +#endif + || pop->poc_handler == s_if + || pop->poc_handler == s_ifdef + || pop->poc_handler == s_ifc + || pop->poc_handler == s_ifeqs + || pop->poc_handler == s_else + || pop->poc_handler == s_endif + || pop->poc_handler == s_globl + || pop->poc_handler == s_ignore))) + { + do_align (1, (char *) NULL, 0, 0); + mri_pending_align = 0; + + if (line_label != NULL) + { + symbol_set_frag (line_label, frag_now); + S_SET_VALUE (line_label, frag_now_fix ()); + } + } + + /* Print the error msg now, while we still can. */ + if (pop == NULL) + { + char *end = input_line_pointer; + + *input_line_pointer = c; + s_ignore (0); + c = *--input_line_pointer; + *input_line_pointer = '\0'; + if (! macro_defined || ! try_macro (c, s)) + { + *end = '\0'; + as_bad (_("unknown pseudo-op: `%s'"), s); + *input_line_pointer++ = c; + } + continue; + } + + /* 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 separates + keyword from operands. */ + if (c == ' ' || c == '\t') + input_line_pointer++; + + /* Input_line is restored. + Input_line_pointer->1st non-blank char + after pseudo-operation. */ + (*pop->poc_handler) (pop->poc_val); + + /* If that was .end, just get out now. */ + if (pop->poc_handler == s_end) + goto quit; + } + else + { + /* WARNING: c has char, which may be end-of-line. */ + /* Also: input_line_pointer->`\0` where c was. */ + *input_line_pointer = c; + input_line_pointer = _find_end_of_line (input_line_pointer, flag_m68k_mri, 1); + c = *input_line_pointer; + *input_line_pointer = '\0'; + + generate_lineno_debug (); + + if (macro_defined && try_macro (c, s)) + continue; + + if (mri_pending_align) + { + do_align (1, (char *) NULL, 0, 0); + mri_pending_align = 0; + if (line_label != NULL) + { + symbol_set_frag (line_label, frag_now); + S_SET_VALUE (line_label, frag_now_fix ()); + } + } + + md_assemble (s); /* Assemble 1 instruction. */ + + *input_line_pointer++ = c; + + /* We resume loop AFTER the end-of-line from + this instruction. */ + } + } + continue; + } + + /* Empty statement? */ + if (is_end_of_line[(unsigned char) c]) + continue; + + if ((LOCAL_LABELS_DOLLAR || LOCAL_LABELS_FB) && ISDIGIT (c)) + { + /* local label ("4:") */ + char *backup = input_line_pointer; + + HANDLE_CONDITIONAL_ASSEMBLY (); + + temp = c - '0'; + + /* Read the whole number. */ + while (ISDIGIT (*input_line_pointer)) + { + temp = (temp * 10) + *input_line_pointer - '0'; + ++input_line_pointer; + } + + if (LOCAL_LABELS_DOLLAR + && *input_line_pointer == '$' + && *(input_line_pointer + 1) == ':') + { + input_line_pointer += 2; + + if (dollar_label_defined (temp)) + { + as_fatal (_("label \"%d$\" redefined"), temp); + } + + define_dollar_label (temp); + colon (dollar_label_name (temp, 0)); + continue; + } + + if (LOCAL_LABELS_FB + && *input_line_pointer++ == ':') + { + fb_label_instance_inc (temp); + colon (fb_label_name (temp, 0)); + continue; + } + + input_line_pointer = backup; + } /* local label ("4:") */ + + if (c && strchr (line_comment_chars, c)) + { /* Its a comment. Better say APP or NO_APP. */ + sb sbuf; + char *ends; + char *new_buf; + char *new_tmp; + unsigned int new_length; + char *tmp_buf = 0; + + bump_line_counters (); + s = input_line_pointer; + if (strncmp (s, "APP\n", 4)) + continue; /* We ignore it */ + s += 4; + + sb_new (&sbuf); + ends = strstr (s, "#NO_APP\n"); + + if (!ends) + { + unsigned int tmp_len; + unsigned int num; + + /* 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 + guarantee it... */ + tmp_len = buffer_limit - s; + tmp_buf = xmalloc (tmp_len + 1); + memcpy (tmp_buf, s, tmp_len); + do + { + new_tmp = input_scrub_next_buffer (&buffer); + if (!new_tmp) + break; + else + buffer_limit = new_tmp; + input_line_pointer = buffer; + ends = strstr (buffer, "#NO_APP\n"); + if (ends) + num = ends - buffer; + else + num = buffer_limit - buffer; + + tmp_buf = xrealloc (tmp_buf, tmp_len + num); + memcpy (tmp_buf + tmp_len, buffer, num); + tmp_len += num; + } + while (!ends); + + input_line_pointer = ends ? ends + 8 : NULL; + + s = tmp_buf; + ends = s + tmp_len; + + } + else + { + input_line_pointer = ends + 8; + } + + scrub_string = s; + scrub_string_end = ends; + + new_length = ends - s; + new_buf = (char *) xmalloc (new_length); + new_tmp = new_buf; + for (;;) + { + int space; + int size; + + space = (new_buf + new_length) - new_tmp; + size = do_scrub_chars (scrub_from_string, new_tmp, space); + + if (size < space) + { + new_tmp[size] = 0; + break; + } + + new_buf = xrealloc (new_buf, new_length + 100); + new_tmp = new_buf + new_length; + new_length += 100; + } + + if (tmp_buf) + free (tmp_buf); + + /* We've "scrubbed" input to the preferred format. In the + process we may have consumed the whole of the remaining + file (and included files). We handle this formatted + input similar to that of macro expansion, letting + actual macro expansion (possibly nested) and other + input expansion work. Beware that in messages, line + numbers and possibly file names will be incorrect. */ + sb_add_string (&sbuf, new_buf); + input_scrub_include_sb (&sbuf, input_line_pointer, 0); + sb_kill (&sbuf); + buffer_limit = input_scrub_next_buffer (&input_line_pointer); + free (new_buf); + continue; + } + + HANDLE_CONDITIONAL_ASSEMBLY (); + +#ifdef tc_unrecognized_line + if (tc_unrecognized_line (c)) + continue; +#endif + input_line_pointer--; + /* Report unknown char as ignored. */ + demand_empty_rest_of_line (); + } + +#ifdef md_after_pass_hook + md_after_pass_hook (); +#endif + } + + quit: + +#ifdef md_cleanup + md_cleanup (); +#endif + /* Close the input file. */ + input_scrub_close (); +#ifdef WARN_COMMENTS + { + if (warn_comment && found_comment) + as_warn_where (found_comment_file, found_comment, + "first comment found here"); + } +#endif +} + +/* Convert O_constant expression EXP into the equivalent O_big representation. + Take the sign of the number from X_unsigned rather than X_add_number. */ + +static void +convert_to_bignum (expressionS *exp) +{ + valueT value; + unsigned int i; + + value = exp->X_add_number; + for (i = 0; i < sizeof (exp->X_add_number) / CHARS_PER_LITTLENUM; i++) + { + generic_bignum[i] = value & LITTLENUM_MASK; + value >>= LITTLENUM_NUMBER_OF_BITS; + } + /* Add a sequence of sign bits if the top bit of X_add_number is not + the sign of the original value. */ + if ((exp->X_add_number < 0) != !exp->X_unsigned) + generic_bignum[i++] = exp->X_unsigned ? 0 : LITTLENUM_MASK; + exp->X_op = O_big; + exp->X_add_number = i; +} + +/* For most MRI pseudo-ops, the line actually ends at the first + nonquoted space. This function looks for that point, stuffs a null + in, and sets *STOPCP to the character that used to be there, and + returns the location. + + Until I hear otherwise, I am going to assume that this is only true + for the m68k MRI assembler. */ + +char * +mri_comment_field (char *stopcp) +{ + char *s; +#ifdef TC_M68K + int inquote = 0; + + know (flag_m68k_mri); + + for (s = input_line_pointer; + ((!is_end_of_line[(unsigned char) *s] && *s != ' ' && *s != '\t') + || inquote); + s++) + { + if (*s == '\'') + inquote = !inquote; + } +#else + for (s = input_line_pointer; + !is_end_of_line[(unsigned char) *s]; + s++) + ; +#endif + *stopcp = *s; + *s = '\0'; + + return s; +} + +/* Skip to the end of an MRI comment field. */ + +void +mri_comment_end (char *stop, int stopc) +{ + know (flag_mri); + + input_line_pointer = stop; + *stop = stopc; + while (!is_end_of_line[(unsigned char) *input_line_pointer]) + ++input_line_pointer; +} + +void +s_abort (int ignore ATTRIBUTE_UNUSED) +{ + as_fatal (_(".abort detected. Abandoning ship.")); +} + +/* Guts of .align directive. N is the power of two to which to align. + FILL may be NULL, or it may point to the bytes of the fill pattern. + LEN is the length of whatever FILL points to, if anything. MAX is + the maximum number of characters to skip when doing the alignment, + or 0 if there is no maximum. */ + +static void +do_align (int n, char *fill, int len, int max) +{ + if (now_seg == absolute_section) + { + if (fill != NULL) + while (len-- > 0) + if (*fill++ != '\0') + { + as_warn (_("ignoring fill value in absolute section")); + break; + } + fill = NULL; + len = 0; + } + +#ifdef md_flush_pending_output + md_flush_pending_output (); +#endif +#ifdef md_do_align + md_do_align (n, fill, len, max, just_record_alignment); +#endif + + /* Only make a frag if we HAVE to... */ + if (n != 0 && !need_pass_2) + { + if (fill == NULL) + { + if (subseg_text_p (now_seg)) + frag_align_code (n, max); + else + frag_align (n, 0, max); + } + else if (len <= 1) + frag_align (n, *fill, max); + else + frag_align_pattern (n, fill, len, max); + } + +#ifdef md_do_align + just_record_alignment: ATTRIBUTE_UNUSED_LABEL +#endif + + record_alignment (now_seg, n - OCTETS_PER_BYTE_POWER); +} + +/* Handle the .align pseudo-op. A positive ARG is a default alignment + (in bytes). A negative ARG is the negative of the length of the + fill pattern. BYTES_P is non-zero if the alignment value should be + interpreted as the byte boundary, rather than the power of 2. */ + +#ifdef BFD_ASSEMBLER +#define ALIGN_LIMIT (stdoutput->arch_info->bits_per_address - 1) +#else +#define ALIGN_LIMIT 15 +#endif + +static void +s_align (int arg, int bytes_p) +{ + unsigned int align_limit = ALIGN_LIMIT; + unsigned int align; + char *stop = NULL; + char stopc; + offsetT fill = 0; + int max; + int fill_p; + + if (flag_mri) + stop = mri_comment_field (&stopc); + + if (is_end_of_line[(unsigned char) *input_line_pointer]) + { + if (arg < 0) + align = 0; + else + align = arg; /* Default value from pseudo-op table. */ + } + else + { + align = get_absolute_expression (); + SKIP_WHITESPACE (); + } + + if (bytes_p) + { + /* Convert to a power of 2. */ + if (align != 0) + { + unsigned int i; + + for (i = 0; (align & 1) == 0; align >>= 1, ++i) + ; + if (align != 1) + as_bad (_("alignment not a power of 2")); + + align = i; + } + } + + if (align > align_limit) + { + align = align_limit; + as_warn (_("alignment too large: %u assumed"), align); + } + + if (*input_line_pointer != ',') + { + fill_p = 0; + max = 0; + } + else + { + ++input_line_pointer; + if (*input_line_pointer == ',') + fill_p = 0; + else + { + fill = get_absolute_expression (); + SKIP_WHITESPACE (); + fill_p = 1; + } + + if (*input_line_pointer != ',') + max = 0; + else + { + ++input_line_pointer; + max = get_absolute_expression (); + } + } + + if (!fill_p) + { + if (arg < 0) + as_warn (_("expected fill pattern missing")); + do_align (align, (char *) NULL, 0, max); + } + else + { + int fill_len; + + if (arg >= 0) + fill_len = 1; + else + fill_len = -arg; + if (fill_len <= 1) + { + char fill_char; + + fill_char = fill; + do_align (align, &fill_char, fill_len, max); + } + else + { + char ab[16]; + + if ((size_t) fill_len > sizeof ab) + abort (); + md_number_to_chars (ab, fill, fill_len); + do_align (align, ab, fill_len, max); + } + } + + demand_empty_rest_of_line (); + + if (flag_mri) + mri_comment_end (stop, stopc); +} + +/* Handle the .align pseudo-op on machines where ".align 4" means + align to a 4 byte boundary. */ + +void +s_align_bytes (int arg) +{ + s_align (arg, 1); +} + +/* Handle the .align pseudo-op on machines where ".align 4" means align + to a 2**4 boundary. */ + +void +s_align_ptwo (int arg) +{ + s_align (arg, 0); +} + +/* Switch in and out of alternate macro mode. */ + +void +s_altmacro (int on) +{ + demand_empty_rest_of_line (); + macro_set_alternate (on); +} + +symbolS * +s_comm_internal (int param, + symbolS *(*comm_parse_extra) (int, symbolS *, addressT)) +{ + char *name; + char c; + char *p; + offsetT temp, size; + symbolS *symbolP = NULL; + char *stop = NULL; + char stopc; + expressionS exp; + + if (flag_mri) + stop = mri_comment_field (&stopc); + + name = input_line_pointer; + c = get_symbol_end (); + /* Just after name is now '\0'. */ + p = input_line_pointer; + *p = c; + + if (name == p) + { + as_bad (_("expected symbol name")); + ignore_rest_of_line (); + goto out; + } + + SKIP_WHITESPACE (); + + /* Accept an optional comma after the name. The comma used to be + required, but Irix 5 cc does not generate it for .lcomm. */ + if (*input_line_pointer == ',') + input_line_pointer++; + + temp = get_absolute_expr (&exp); + size = temp; +#ifdef BFD_ASSEMBLER + size &= ((offsetT) 2 << (stdoutput->arch_info->bits_per_address - 1)) - 1; +#endif + if (exp.X_op == O_absent) + { + as_bad (_("missing size expression")); + ignore_rest_of_line (); + goto out; + } + else if (temp != size || !exp.X_unsigned) + { + as_warn (_("size (%ld) out of range, ignored"), (long) temp); + ignore_rest_of_line (); + goto out; + } + + *p = 0; + symbolP = symbol_find_or_make (name); + if (S_IS_DEFINED (symbolP) && !S_IS_COMMON (symbolP)) + { + symbolP = NULL; + as_bad (_("symbol `%s' is already defined"), name); + *p = c; + ignore_rest_of_line (); + goto out; + } + + size = S_GET_VALUE (symbolP); + if (size == 0) + size = temp; + else if (size != temp) + as_warn (_("size of \"%s\" is already %ld; not changing to %ld"), + name, (long) size, (long) temp); + + *p = c; + if (comm_parse_extra != NULL) + symbolP = (*comm_parse_extra) (param, symbolP, size); + else + { + S_SET_VALUE (symbolP, (valueT) size); + S_SET_EXTERNAL (symbolP); +#ifdef OBJ_VMS + { + extern int flag_one; + if (size == 0 || !flag_one) + S_GET_OTHER (symbolP) = const_flag; + } +#endif + } + + demand_empty_rest_of_line (); + out: + if (flag_mri) + mri_comment_end (stop, stopc); + return symbolP; +} + +void +s_comm (int ignore) +{ + s_comm_internal (ignore, NULL); +} + +/* The MRI COMMON pseudo-op. We handle this by creating a common + symbol with the appropriate name. We make s_space do the right + thing by increasing the size. */ + +void +s_mri_common (int small ATTRIBUTE_UNUSED) +{ + char *name; + char c; + char *alc = NULL; + symbolS *sym; + offsetT align; + char *stop = NULL; + char stopc; + + if (!flag_mri) + { + s_comm (0); + return; + } + + stop = mri_comment_field (&stopc); + + SKIP_WHITESPACE (); + + name = input_line_pointer; + if (!ISDIGIT (*name)) + c = get_symbol_end (); + else + { + do + { + ++input_line_pointer; + } + while (ISDIGIT (*input_line_pointer)); + + c = *input_line_pointer; + *input_line_pointer = '\0'; + + if (line_label != NULL) + { + alc = (char *) xmalloc (strlen (S_GET_NAME (line_label)) + + (input_line_pointer - name) + + 1); + sprintf (alc, "%s%s", name, S_GET_NAME (line_label)); + name = alc; + } + } + + sym = symbol_find_or_make (name); + *input_line_pointer = c; + if (alc != NULL) + free (alc); + + if (*input_line_pointer != ',') + align = 0; + else + { + ++input_line_pointer; + align = get_absolute_expression (); + } + + if (S_IS_DEFINED (sym) && !S_IS_COMMON (sym)) + { + as_bad (_("symbol `%s' is already defined"), S_GET_NAME (sym)); + ignore_rest_of_line (); + mri_comment_end (stop, stopc); + return; + } + + S_SET_EXTERNAL (sym); + mri_common_symbol = sym; + +#ifdef S_SET_ALIGN + if (align != 0) + S_SET_ALIGN (sym, align); +#endif + + if (line_label != NULL) + { + expressionS exp; + exp.X_op = O_symbol; + exp.X_add_symbol = sym; + exp.X_add_number = 0; + symbol_set_value_expression (line_label, &exp); + symbol_set_frag (line_label, &zero_address_frag); + S_SET_SEGMENT (line_label, expr_section); + } + + /* FIXME: We just ignore the small argument, which distinguishes + COMMON and COMMON.S. I don't know what we can do about it. */ + + /* Ignore the type and hptype. */ + if (*input_line_pointer == ',') + input_line_pointer += 2; + if (*input_line_pointer == ',') + input_line_pointer += 2; + + demand_empty_rest_of_line (); + + mri_comment_end (stop, stopc); +} + +void +s_data (int ignore ATTRIBUTE_UNUSED) +{ + segT section; + register int temp; + + temp = get_absolute_expression (); + if (flag_readonly_data_in_text) + { + section = text_section; + temp += 1000; + } + else + section = data_section; + + subseg_set (section, (subsegT) temp); + +#ifdef OBJ_VMS + const_flag = 0; +#endif + demand_empty_rest_of_line (); +} + +/* Handle the .appfile pseudo-op. This is automatically generated by + do_scrub_chars when a preprocessor # line comment is seen with a + file name. This default definition may be overridden by the object + or CPU specific pseudo-ops. This function is also the default + definition for .file; the APPFILE argument is 1 for .appfile, 0 for + .file. */ + +void +s_app_file_string (char *file, int appfile ATTRIBUTE_UNUSED) +{ +#ifdef LISTING + if (listing) + listing_source_file (file); +#endif + register_dependency (file); +#ifdef obj_app_file + obj_app_file (file, appfile); +#endif +} + +void +s_app_file (int appfile) +{ + register char *s; + int length; + + /* Some assemblers tolerate immediately following '"'. */ + if ((s = demand_copy_string (&length)) != 0) + { + /* If this is a fake .appfile, a fake newline was inserted into + the buffer. Passing -2 to new_logical_line tells it to + account for it. */ + int may_omit + = (!new_logical_line (s, appfile ? -2 : -1) && appfile); + + /* In MRI mode, the preprocessor may have inserted an extraneous + backquote. */ + if (flag_m68k_mri + && *input_line_pointer == '\'' + && is_end_of_line[(unsigned char) input_line_pointer[1]]) + ++input_line_pointer; + + demand_empty_rest_of_line (); + if (!may_omit) + s_app_file_string (s, appfile); + } +} + +/* Handle the .appline pseudo-op. This is automatically generated by + do_scrub_chars when a preprocessor # line comment is seen. This + default definition may be overridden by the object or CPU specific + pseudo-ops. */ + +void +s_app_line (int ignore ATTRIBUTE_UNUSED) +{ + int l; + + /* The given number is that of the next line. */ + l = get_absolute_expression () - 1; + if (l < 0) + /* Some of the back ends can't deal with non-positive line numbers. + Besides, it's silly. */ + as_warn (_("line numbers must be positive; line number %d rejected"), + l + 1); + else + { + new_logical_line ((char *) NULL, l); +#ifdef LISTING + if (listing) + listing_source_line (l); +#endif + } + demand_empty_rest_of_line (); +} + +/* Handle the .end pseudo-op. Actually, the real work is done in + read_a_source_file. */ + +void +s_end (int ignore ATTRIBUTE_UNUSED) +{ + if (flag_mri) + { + /* The MRI assembler permits the start symbol to follow .end, + but we don't support that. */ + SKIP_WHITESPACE (); + if (!is_end_of_line[(unsigned char) *input_line_pointer] + && *input_line_pointer != '*' + && *input_line_pointer != '!') + as_warn (_("start address not supported")); + } +} + +/* Handle the .err pseudo-op. */ + +void +s_err (int ignore ATTRIBUTE_UNUSED) +{ + as_bad (_(".err encountered")); + demand_empty_rest_of_line (); +} + +/* Handle the .error and .warning pseudo-ops. */ + +void +s_errwarn (int err) +{ + int len; + /* The purpose for the conditional assignment is not to + internationalize the directive itself, but that we need a + self-contained message, one that can be passed like the + demand_copy_C_string return value, and with no assumption on the + location of the name of the directive within the message. */ + char *msg + = (err ? _(".error directive invoked in source file") + : _(".warning directive invoked in source file")); + + if (!is_it_end_of_statement ()) + { + if (*input_line_pointer != '\"') + { + as_bad (_("%s argument must be a string"), + err ? ".error" : ".warning"); + ignore_rest_of_line (); + return; + } + + msg = demand_copy_C_string (&len); + if (msg == NULL) + return; + } + + if (err) + as_bad ("%s", msg); + else + as_warn ("%s", msg); + demand_empty_rest_of_line (); +} + +/* Handle the MRI fail pseudo-op. */ + +void +s_fail (int ignore ATTRIBUTE_UNUSED) +{ + offsetT temp; + char *stop = NULL; + char stopc; + + if (flag_mri) + stop = mri_comment_field (&stopc); + + temp = get_absolute_expression (); + if (temp >= 500) + as_warn (_(".fail %ld encountered"), (long) temp); + else + as_bad (_(".fail %ld encountered"), (long) temp); + + demand_empty_rest_of_line (); + + if (flag_mri) + mri_comment_end (stop, stopc); +} + +void +s_fill (int ignore ATTRIBUTE_UNUSED) +{ + expressionS rep_exp; + long size = 1; + register long fill = 0; + char *p; + +#ifdef md_flush_pending_output + md_flush_pending_output (); +#endif + + get_known_segmented_expression (&rep_exp); + if (*input_line_pointer == ',') + { + input_line_pointer++; + size = get_absolute_expression (); + if (*input_line_pointer == ',') + { + input_line_pointer++; + fill = get_absolute_expression (); + } + } + + /* This is to be compatible with BSD 4.2 AS, not for any rational reason. */ +#define BSD_FILL_SIZE_CROCK_8 (8) + if (size > BSD_FILL_SIZE_CROCK_8) + { + as_warn (_(".fill size clamped to %d"), BSD_FILL_SIZE_CROCK_8); + size = BSD_FILL_SIZE_CROCK_8; + } + if (size < 0) + { + as_warn (_("size negative; .fill ignored")); + size = 0; + } + else if (rep_exp.X_op == O_constant && rep_exp.X_add_number <= 0) + { + if (rep_exp.X_add_number < 0) + as_warn (_("repeat < 0; .fill ignored")); + size = 0; + } + + if (size && !need_pass_2) + { + if (rep_exp.X_op == O_constant) + { + p = frag_var (rs_fill, (int) size, (int) size, + (relax_substateT) 0, (symbolS *) 0, + (offsetT) rep_exp.X_add_number, + (char *) 0); + } + else + { + /* We don't have a constant repeat count, so we can't use + rs_fill. We can get the same results out of rs_space, + but its argument is in bytes, so we must multiply the + repeat count by size. */ + + symbolS *rep_sym; + rep_sym = make_expr_symbol (&rep_exp); + if (size != 1) + { + expressionS size_exp; + size_exp.X_op = O_constant; + size_exp.X_add_number = size; + + rep_exp.X_op = O_multiply; + rep_exp.X_add_symbol = rep_sym; + rep_exp.X_op_symbol = make_expr_symbol (&size_exp); + rep_exp.X_add_number = 0; + rep_sym = make_expr_symbol (&rep_exp); + } + + p = frag_var (rs_space, (int) size, (int) size, + (relax_substateT) 0, rep_sym, (offsetT) 0, (char *) 0); + } + + memset (p, 0, (unsigned int) size); + + /* The magic number BSD_FILL_SIZE_CROCK_4 is from BSD 4.2 VAX + flavoured AS. The following bizarre 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. */ +#define BSD_FILL_SIZE_CROCK_4 (4) + md_number_to_chars (p, (valueT) fill, + (size > BSD_FILL_SIZE_CROCK_4 + ? BSD_FILL_SIZE_CROCK_4 + : (int) 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 (); +} + +void +s_globl (int ignore ATTRIBUTE_UNUSED) +{ + char *name; + int c; + symbolS *symbolP; + char *stop = NULL; + char stopc; + + if (flag_mri) + stop = mri_comment_field (&stopc); + + do + { + name = input_line_pointer; + c = get_symbol_end (); + symbolP = symbol_find_or_make (name); + S_SET_EXTERNAL (symbolP); + + *input_line_pointer = c; + SKIP_WHITESPACE (); + c = *input_line_pointer; + if (c == ',') + { + input_line_pointer++; + SKIP_WHITESPACE (); + if (is_end_of_line[(unsigned char) *input_line_pointer]) + c = '\n'; + } + } + while (c == ','); + + demand_empty_rest_of_line (); + + if (flag_mri) + mri_comment_end (stop, stopc); +} + +/* Handle the MRI IRP and IRPC pseudo-ops. */ + +void +s_irp (int irpc) +{ + char *file, *eol; + unsigned int line; + sb s; + const char *err; + sb out; + + as_where (&file, &line); + + sb_new (&s); + eol = find_end_of_line (input_line_pointer, 0); + sb_add_buffer (&s, input_line_pointer, eol - input_line_pointer); + input_line_pointer = eol; + + sb_new (&out); + + err = expand_irp (irpc, 0, &s, &out, get_line_sb); + if (err != NULL) + as_bad_where (file, line, "%s", err); + + sb_kill (&s); + + input_scrub_include_sb (&out, input_line_pointer, 1); + sb_kill (&out); + buffer_limit = input_scrub_next_buffer (&input_line_pointer); +} + +/* Handle the .linkonce pseudo-op. This tells the assembler to mark + the section to only be linked once. However, this is not supported + by most object file formats. This takes an optional argument, + which is what to do about duplicates. */ + +void +s_linkonce (int ignore ATTRIBUTE_UNUSED) +{ + enum linkonce_type type; + + SKIP_WHITESPACE (); + + type = LINKONCE_DISCARD; + + if (!is_end_of_line[(unsigned char) *input_line_pointer]) + { + char *s; + char c; + + s = input_line_pointer; + c = get_symbol_end (); + if (strcasecmp (s, "discard") == 0) + type = LINKONCE_DISCARD; + else if (strcasecmp (s, "one_only") == 0) + type = LINKONCE_ONE_ONLY; + else if (strcasecmp (s, "same_size") == 0) + type = LINKONCE_SAME_SIZE; + else if (strcasecmp (s, "same_contents") == 0) + type = LINKONCE_SAME_CONTENTS; + else + as_warn (_("unrecognized .linkonce type `%s'"), s); + + *input_line_pointer = c; + } + +#ifdef obj_handle_link_once + obj_handle_link_once (type); +#else /* ! defined (obj_handle_link_once) */ +#ifdef BFD_ASSEMBLER + { + flagword flags; + + if ((bfd_applicable_section_flags (stdoutput) & SEC_LINK_ONCE) == 0) + as_warn (_(".linkonce is not supported for this object file format")); + + flags = bfd_get_section_flags (stdoutput, now_seg); + flags |= SEC_LINK_ONCE; + switch (type) + { + default: + abort (); + case LINKONCE_DISCARD: + flags |= SEC_LINK_DUPLICATES_DISCARD; + break; + case LINKONCE_ONE_ONLY: + flags |= SEC_LINK_DUPLICATES_ONE_ONLY; + break; + case LINKONCE_SAME_SIZE: + flags |= SEC_LINK_DUPLICATES_SAME_SIZE; + break; + case LINKONCE_SAME_CONTENTS: + flags |= SEC_LINK_DUPLICATES_SAME_CONTENTS; + break; + } + if (!bfd_set_section_flags (stdoutput, now_seg, flags)) + as_bad (_("bfd_set_section_flags: %s"), + bfd_errmsg (bfd_get_error ())); + } +#else /* ! defined (BFD_ASSEMBLER) */ + as_warn (_(".linkonce is not supported for this object file format")); +#endif /* ! defined (BFD_ASSEMBLER) */ +#endif /* ! defined (obj_handle_link_once) */ + + demand_empty_rest_of_line (); +} + +void +bss_alloc (symbolS *symbolP, addressT size, int align) +{ + char *pfrag; + segT current_seg = now_seg; + subsegT current_subseg = now_subseg; + segT bss_seg = bss_section; + +#if defined (TC_MIPS) || defined (TC_ALPHA) + if (OUTPUT_FLAVOR == bfd_target_ecoff_flavour + || OUTPUT_FLAVOR == bfd_target_elf_flavour) + { + /* For MIPS and Alpha ECOFF or ELF, small objects are put in .sbss. */ + if (size <= bfd_get_gp_size (stdoutput)) + { + bss_seg = subseg_new (".sbss", 1); + seg_info (bss_seg)->bss = 1; +#ifdef BFD_ASSEMBLER + if (!bfd_set_section_flags (stdoutput, bss_seg, SEC_ALLOC)) + as_warn (_("error setting flags for \".sbss\": %s"), + bfd_errmsg (bfd_get_error ())); +#endif + } + } +#endif + subseg_set (bss_seg, 1); + + if (align) + { + record_alignment (bss_seg, align); + frag_align (align, 0, 0); + } + + /* Detach from old frag. */ + if (S_GET_SEGMENT (symbolP) == bss_seg) + symbol_get_frag (symbolP)->fr_symbol = NULL; + + symbol_set_frag (symbolP, frag_now); + pfrag = frag_var (rs_org, 1, 1, 0, symbolP, size, NULL); + *pfrag = 0; + +#ifdef S_SET_SIZE + S_SET_SIZE (symbolP, size); +#endif + S_SET_SEGMENT (symbolP, bss_seg); + +#ifdef OBJ_COFF + /* The symbol may already have been created with a preceding + ".globl" directive -- be careful not to step on storage class + in that case. Otherwise, set it to static. */ + if (S_GET_STORAGE_CLASS (symbolP) != C_EXT) + S_SET_STORAGE_CLASS (symbolP, C_STAT); +#endif /* OBJ_COFF */ + + subseg_set (current_seg, current_subseg); +} + +offsetT +parse_align (int align_bytes) +{ + expressionS exp; + addressT align; + + SKIP_WHITESPACE (); + if (*input_line_pointer != ',') + { + no_align: + as_bad (_("expected alignment after size")); + ignore_rest_of_line (); + return -1; + } + + input_line_pointer++; + SKIP_WHITESPACE (); + + align = get_absolute_expr (&exp); + if (exp.X_op == O_absent) + goto no_align; + + if (!exp.X_unsigned) + { + as_warn (_("alignment negative; 0 assumed")); + align = 0; + } + + if (align_bytes && align != 0) + { + /* convert to a power of 2 alignment */ + unsigned int alignp2 = 0; + while ((align & 1) == 0) + align >>= 1, ++alignp2; + if (align != 1) + { + as_bad (_("alignment not a power of 2")); + ignore_rest_of_line (); + return -1; + } + align = alignp2; + } + return align; +} + +/* Called from s_comm_internal after symbol name and size have been + parsed. NEEDS_ALIGN is 0 if it was an ".lcomm" (2 args only), + 1 if this was a ".bss" directive which has a 3rd argument + (alignment as a power of 2), or 2 if this was a ".bss" directive + with alignment in bytes. */ + +symbolS * +s_lcomm_internal (int needs_align, symbolS *symbolP, addressT size) +{ + addressT align = 0; + + if (needs_align) + { + align = parse_align (needs_align - 1); + if (align == (addressT) -1) + return NULL; + } + else + /* Assume some objects may require alignment on some systems. */ + TC_IMPLICIT_LCOMM_ALIGNMENT (size, align); + + bss_alloc (symbolP, size, align); + return symbolP; +} + +void +s_lcomm (int needs_align) +{ + s_comm_internal (needs_align, s_lcomm_internal); +} + +void +s_lcomm_bytes (int needs_align) +{ + s_comm_internal (needs_align * 2, s_lcomm_internal); +} + +void +s_lsym (int ignore ATTRIBUTE_UNUSED) +{ + register char *name; + register char c; + register char *p; + expressionS exp; + register symbolS *symbolP; + + /* We permit ANY defined expression: BSD4.2 demands constants. */ + name = input_line_pointer; + c = get_symbol_end (); + p = input_line_pointer; + *p = c; + + if (name == p) + { + as_bad (_("expected symbol name")); + ignore_rest_of_line (); + return; + } + + SKIP_WHITESPACE (); + + if (*input_line_pointer != ',') + { + *p = 0; + as_bad (_("expected comma after \"%s\""), name); + *p = c; + ignore_rest_of_line (); + return; + } + + input_line_pointer++; + expression (&exp); + + if (exp.X_op != O_constant + && exp.X_op != O_register) + { + as_bad (_("bad expression")); + ignore_rest_of_line (); + return; + } + + *p = 0; + symbolP = symbol_find_or_make (name); + + /* FIXME-SOON I pulled a (&& symbolP->sy_other == 0 && + symbolP->sy_desc == 0) out of this test because coff doesn't have + those fields, and I can't see when they'd ever be tripped. I + don't think I understand why they were here so I may have + introduced a bug. As recently as 1.37 didn't have this test + anyway. xoxorich. */ + + if (S_GET_SEGMENT (symbolP) == undefined_section + && S_GET_VALUE (symbolP) == 0) + { + /* The name might be an undefined .global symbol; be sure to + keep the "external" bit. */ + S_SET_SEGMENT (symbolP, + (exp.X_op == O_constant + ? absolute_section + : reg_section)); + S_SET_VALUE (symbolP, (valueT) exp.X_add_number); + } + else + { + as_bad (_("symbol `%s' is already defined"), name); + } + + *p = c; + demand_empty_rest_of_line (); +} + +/* Read a line into an sb. Returns the character that ended the line + or zero if there are no more lines. */ + +static int +get_line_sb (sb *line) +{ + char *eol; + + if (input_line_pointer[-1] == '\n') + bump_line_counters (); + + if (input_line_pointer >= buffer_limit) + { + buffer_limit = input_scrub_next_buffer (&input_line_pointer); + if (buffer_limit == 0) + return 0; + } + + eol = find_end_of_line (input_line_pointer, flag_m68k_mri); + sb_add_buffer (line, input_line_pointer, eol - input_line_pointer); + input_line_pointer = eol; + + /* Don't skip multiple end-of-line characters, because that breaks support + for the IA-64 stop bit (;;) which looks like two consecutive end-of-line + characters but isn't. Instead just skip one end of line character and + return the character skipped so that the caller can re-insert it if + necessary. */ + return *input_line_pointer++; +} + +/* Define a macro. This is an interface to macro.c. */ + +void +s_macro (int ignore ATTRIBUTE_UNUSED) +{ + char *file, *eol; + unsigned int line; + sb s; + const char *err; + const char *name; + + as_where (&file, &line); + + sb_new (&s); + eol = find_end_of_line (input_line_pointer, 0); + sb_add_buffer (&s, input_line_pointer, eol - input_line_pointer); + input_line_pointer = eol; + + if (line_label != NULL) + { + sb label; + + sb_new (&label); + sb_add_string (&label, S_GET_NAME (line_label)); + err = define_macro (0, &s, &label, get_line_sb, file, line, &name); + sb_kill (&label); + } + else + err = define_macro (0, &s, NULL, get_line_sb, file, line, &name); + if (err != NULL) + as_bad_where (file, line, err, name); + else + { + if (line_label != NULL) + { + S_SET_SEGMENT (line_label, absolute_section); + S_SET_VALUE (line_label, 0); + symbol_set_frag (line_label, &zero_address_frag); + } + + if (((NO_PSEUDO_DOT || flag_m68k_mri) + && hash_find (po_hash, name) != NULL) + || (!flag_m68k_mri + && *name == '.' + && hash_find (po_hash, name + 1) != NULL)) + as_warn_where (file, + line, + _("attempt to redefine pseudo-op `%s' ignored"), + name); + } + + sb_kill (&s); +} + +/* Handle the .mexit pseudo-op, which immediately exits a macro + expansion. */ + +void +s_mexit (int ignore ATTRIBUTE_UNUSED) +{ + cond_exit_macro (macro_nest); + buffer_limit = input_scrub_next_buffer (&input_line_pointer); +} + +/* Switch in and out of MRI mode. */ + +void +s_mri (int ignore ATTRIBUTE_UNUSED) +{ + int on, old_flag; + + on = get_absolute_expression (); + old_flag = flag_mri; + if (on != 0) + { + flag_mri = 1; +#ifdef TC_M68K + flag_m68k_mri = 1; +#endif + macro_mri_mode (1); + } + else + { + flag_mri = 0; +#ifdef TC_M68K + flag_m68k_mri = 0; +#endif + macro_mri_mode (0); + } + + /* Operator precedence changes in m68k MRI mode, so we need to + update the operator rankings. */ + expr_set_precedence (); + +#ifdef MRI_MODE_CHANGE + if (on != old_flag) + MRI_MODE_CHANGE (on); +#endif + + demand_empty_rest_of_line (); +} + +/* Handle changing the location counter. */ + +static void +do_org (segT segment, expressionS *exp, int fill) +{ + if (segment != now_seg && segment != absolute_section) + as_bad (_("invalid segment \"%s\""), segment_name (segment)); + + if (now_seg == absolute_section) + { + if (fill != 0) + as_warn (_("ignoring fill value in absolute section")); + if (exp->X_op != O_constant) + { + as_bad (_("only constant offsets supported in absolute section")); + exp->X_add_number = 0; + } + abs_section_offset = exp->X_add_number; + } + else + { + char *p; + symbolS *sym = exp->X_add_symbol; + offsetT off = exp->X_add_number * OCTETS_PER_BYTE; + + if (exp->X_op != O_constant && exp->X_op != O_symbol) + { + /* Handle complex expressions. */ + sym = make_expr_symbol (exp); + off = 0; + } + + p = frag_var (rs_org, 1, 1, (relax_substateT) 0, sym, off, (char *) 0); + *p = fill; + } +} + +void +s_org (int ignore ATTRIBUTE_UNUSED) +{ + register segT segment; + expressionS exp; + register long temp_fill; + +#ifdef md_flush_pending_output + md_flush_pending_output (); +#endif + + /* The m68k MRI assembler has a different meaning for .org. It + means to create an absolute section at a given address. We can't + support that--use a linker script instead. */ + if (flag_m68k_mri) + { + as_bad (_("MRI style ORG pseudo-op not supported")); + ignore_rest_of_line (); + return; + } + + /* 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 negative 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==1. */ + segment = get_known_segmented_expression (&exp); + if (*input_line_pointer == ',') + { + input_line_pointer++; + temp_fill = get_absolute_expression (); + } + else + temp_fill = 0; + + if (!need_pass_2) + do_org (segment, &exp, temp_fill); + + demand_empty_rest_of_line (); +} + +/* Handle parsing for the MRI SECT/SECTION pseudo-op. This should be + called by the obj-format routine which handles section changing + when in MRI mode. It will create a new section, and return it. It + will set *TYPE to the section type: one of 'C' (code), 'D' (data), + 'M' (mixed), or 'R' (romable). If BFD_ASSEMBLER is defined, the + flags will be set in the section. */ + +void +s_mri_sect (char *type ATTRIBUTE_UNUSED) +{ +#ifdef TC_M68K + + char *name; + char c; + segT seg; + + SKIP_WHITESPACE (); + + name = input_line_pointer; + if (!ISDIGIT (*name)) + c = get_symbol_end (); + else + { + do + { + ++input_line_pointer; + } + while (ISDIGIT (*input_line_pointer)); + + c = *input_line_pointer; + *input_line_pointer = '\0'; + } + + name = xstrdup (name); + + *input_line_pointer = c; + + seg = subseg_new (name, 0); + + if (*input_line_pointer == ',') + { + int align; + + ++input_line_pointer; + align = get_absolute_expression (); + record_alignment (seg, align); + } + + *type = 'C'; + if (*input_line_pointer == ',') + { + c = *++input_line_pointer; + c = TOUPPER (c); + if (c == 'C' || c == 'D' || c == 'M' || c == 'R') + *type = c; + else + as_bad (_("unrecognized section type")); + ++input_line_pointer; + +#ifdef BFD_ASSEMBLER + { + flagword flags; + + flags = SEC_NO_FLAGS; + if (*type == 'C') + flags = SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_CODE; + else if (*type == 'D' || *type == 'M') + flags = SEC_ALLOC | SEC_LOAD | SEC_DATA; + else if (*type == 'R') + flags = SEC_ALLOC | SEC_LOAD | SEC_DATA | SEC_READONLY | SEC_ROM; + if (flags != SEC_NO_FLAGS) + { + if (!bfd_set_section_flags (stdoutput, seg, flags)) + as_warn (_("error setting flags for \"%s\": %s"), + bfd_section_name (stdoutput, seg), + bfd_errmsg (bfd_get_error ())); + } + } +#endif + } + + /* Ignore the HP type. */ + if (*input_line_pointer == ',') + input_line_pointer += 2; + + demand_empty_rest_of_line (); + +#else /* ! TC_M68K */ +#ifdef TC_I960 + + char *name; + char c; + segT seg; + + SKIP_WHITESPACE (); + + name = input_line_pointer; + c = get_symbol_end (); + + name = xstrdup (name); + + *input_line_pointer = c; + + seg = subseg_new (name, 0); + + if (*input_line_pointer != ',') + *type = 'C'; + else + { + char *sectype; + + ++input_line_pointer; + SKIP_WHITESPACE (); + sectype = input_line_pointer; + c = get_symbol_end (); + if (*sectype == '\0') + *type = 'C'; + else if (strcasecmp (sectype, "text") == 0) + *type = 'C'; + else if (strcasecmp (sectype, "data") == 0) + *type = 'D'; + else if (strcasecmp (sectype, "romdata") == 0) + *type = 'R'; + else + as_warn (_("unrecognized section type `%s'"), sectype); + *input_line_pointer = c; + } + + if (*input_line_pointer == ',') + { + char *seccmd; + + ++input_line_pointer; + SKIP_WHITESPACE (); + seccmd = input_line_pointer; + c = get_symbol_end (); + if (strcasecmp (seccmd, "absolute") == 0) + { + as_bad (_("absolute sections are not supported")); + *input_line_pointer = c; + ignore_rest_of_line (); + return; + } + else if (strcasecmp (seccmd, "align") == 0) + { + int align; + + *input_line_pointer = c; + align = get_absolute_expression (); + record_alignment (seg, align); + } + else + { + as_warn (_("unrecognized section command `%s'"), seccmd); + *input_line_pointer = c; + } + } + + demand_empty_rest_of_line (); + +#else /* ! TC_I960 */ + /* The MRI assembler seems to use different forms of .sect for + different targets. */ + as_bad ("MRI mode not supported for this target"); + ignore_rest_of_line (); +#endif /* ! TC_I960 */ +#endif /* ! TC_M68K */ +} + +/* Handle the .print pseudo-op. */ + +void +s_print (int ignore ATTRIBUTE_UNUSED) +{ + char *s; + int len; + + s = demand_copy_C_string (&len); + if (s != NULL) + printf ("%s\n", s); + demand_empty_rest_of_line (); +} + +/* Handle the .purgem pseudo-op. */ + +void +s_purgem (int ignore ATTRIBUTE_UNUSED) +{ + if (is_it_end_of_statement ()) + { + demand_empty_rest_of_line (); + return; + } + + do + { + char *name; + char c; + + SKIP_WHITESPACE (); + name = input_line_pointer; + c = get_symbol_end (); + delete_macro (name); + *input_line_pointer = c; + SKIP_WHITESPACE (); + } + while (*input_line_pointer++ == ','); + + --input_line_pointer; + demand_empty_rest_of_line (); +} + +/* Handle the .endm/.endr pseudo-ops. */ + +static void +s_bad_end (int endr) +{ + as_warn (_(".end%c encountered without preceeding %s"), + endr ? 'r' : 'm', + endr ? ".rept, .irp, or .irpc" : ".macro"); + demand_empty_rest_of_line (); +} + +/* Handle the .rept pseudo-op. */ + +void +s_rept (int ignore ATTRIBUTE_UNUSED) +{ + int count; + + count = get_absolute_expression (); + + do_repeat (count, "REPT", "ENDR"); +} + +/* This function provides a generic repeat block implementation. It allows + different directives to be used as the start/end keys. */ + +void +do_repeat (int count, const char *start, const char *end) +{ + sb one; + sb many; + + sb_new (&one); + if (!buffer_and_nest (start, end, &one, get_line_sb)) + { + as_bad (_("%s without %s"), start, end); + return; + } + + sb_new (&many); + while (count-- > 0) + sb_add_sb (&many, &one); + + sb_kill (&one); + + input_scrub_include_sb (&many, input_line_pointer, 1); + sb_kill (&many); + buffer_limit = input_scrub_next_buffer (&input_line_pointer); +} + +/* Skip to end of current repeat loop; EXTRA indicates how many additional + input buffers to skip. Assumes that conditionals preceding the loop end + are properly nested. + + This function makes it easier to implement a premature "break" out of the + loop. The EXTRA arg accounts for other buffers we might have inserted, + such as line substitutions. */ + +void +end_repeat (int extra) +{ + cond_exit_macro (macro_nest); + while (extra-- >= 0) + buffer_limit = input_scrub_next_buffer (&input_line_pointer); +} + +static void +assign_symbol (char *name, int no_reassign) +{ + symbolS *symbolP; + + if (name[0] == '.' && name[1] == '\0') + { + /* Turn '. = mumble' into a .org mumble. */ + segT segment; + expressionS exp; + + segment = get_known_segmented_expression (&exp); + + if (!need_pass_2) + do_org (segment, &exp, 0); + + return; + } + + if ((symbolP = symbol_find (name)) == NULL + && (symbolP = md_undefined_symbol (name)) == NULL) + { + symbolP = symbol_find_or_make (name); +#ifndef NO_LISTING + /* When doing symbol listings, play games with dummy fragments living + outside the normal fragment chain to record the file and line info + for this symbol. */ + if (listing & LISTING_SYMBOLS) + { + extern struct list_info_struct *listing_tail; + fragS *dummy_frag = (fragS *) xcalloc (1, sizeof (fragS)); + dummy_frag->line = listing_tail; + dummy_frag->fr_symbol = symbolP; + symbol_set_frag (symbolP, dummy_frag); + } +#endif +#ifdef OBJ_COFF + /* "set" symbols are local unless otherwise specified. */ + SF_SET_LOCAL (symbolP); +#endif + } + + /* Permit register names to be redefined. */ + if (no_reassign + && S_IS_DEFINED (symbolP) + && S_GET_SEGMENT (symbolP) != reg_section) + as_bad (_("symbol `%s' is already defined"), name); + + pseudo_set (symbolP); +} + +/* Handle the .equ, .equiv and .set directives. If EQUIV is 1, then + this is .equiv, and it is an error if the symbol is already + defined. */ + +void +s_set (int equiv) +{ + char *name; + char delim; + char *end_name; + + /* Especial apologies for the random logic: + this just grew, and could be parsed much more simply! + Dean in haste. */ + name = input_line_pointer; + delim = get_symbol_end (); + end_name = input_line_pointer; + *end_name = delim; + + if (name == end_name) + { + as_bad (_("expected symbol name")); + ignore_rest_of_line (); + return; + } + + SKIP_WHITESPACE (); + + if (*input_line_pointer != ',') + { + *end_name = 0; + as_bad (_("expected comma after \"%s\""), name); + *end_name = delim; + ignore_rest_of_line (); + return; + } + + input_line_pointer++; + *end_name = 0; + + assign_symbol (name, equiv); + *end_name = delim; + + demand_empty_rest_of_line (); +} + +void +s_space (int mult) +{ + expressionS exp; + expressionS val; + char *p = 0; + char *stop = NULL; + char stopc; + int bytes; + +#ifdef md_flush_pending_output + md_flush_pending_output (); +#endif + + if (flag_mri) + stop = mri_comment_field (&stopc); + + /* In m68k MRI mode, we need to align to a word boundary, unless + this is ds.b. */ + if (flag_m68k_mri && mult > 1) + { + if (now_seg == absolute_section) + { + abs_section_offset += abs_section_offset & 1; + if (line_label != NULL) + S_SET_VALUE (line_label, abs_section_offset); + } + else if (mri_common_symbol != NULL) + { + valueT val; + + val = S_GET_VALUE (mri_common_symbol); + if ((val & 1) != 0) + { + S_SET_VALUE (mri_common_symbol, val + 1); + if (line_label != NULL) + { + expressionS *symexp; + + symexp = symbol_get_value_expression (line_label); + know (symexp->X_op == O_symbol); + know (symexp->X_add_symbol == mri_common_symbol); + symexp->X_add_number += 1; + } + } + } + else + { + do_align (1, (char *) NULL, 0, 0); + if (line_label != NULL) + { + symbol_set_frag (line_label, frag_now); + S_SET_VALUE (line_label, frag_now_fix ()); + } + } + } + + bytes = mult; + + expression (&exp); + + SKIP_WHITESPACE (); + if (*input_line_pointer == ',') + { + ++input_line_pointer; + expression (&val); + } + else + { + val.X_op = O_constant; + val.X_add_number = 0; + } + + if (val.X_op != O_constant + || val.X_add_number < - 0x80 + || val.X_add_number > 0xff + || (mult != 0 && mult != 1 && val.X_add_number != 0)) + { + if (exp.X_op != O_constant) + as_bad (_("unsupported variable size or fill value")); + else + { + offsetT i; + + if (mult == 0) + mult = 1; + bytes = mult * exp.X_add_number; + for (i = 0; i < exp.X_add_number; i++) + emit_expr (&val, mult); + } + } + else + { + if (exp.X_op == O_constant) + { + long repeat; + + repeat = exp.X_add_number; + if (mult) + repeat *= mult; + bytes = repeat; + if (repeat <= 0) + { + if (!flag_mri) + as_warn (_(".space repeat count is zero, ignored")); + else if (repeat < 0) + as_warn (_(".space repeat count is negative, ignored")); + goto getout; + } + + /* If we are in the absolute section, just bump the offset. */ + if (now_seg == absolute_section) + { + abs_section_offset += repeat; + goto getout; + } + + /* If we are secretly in an MRI common section, then + creating space just increases the size of the common + symbol. */ + if (mri_common_symbol != NULL) + { + S_SET_VALUE (mri_common_symbol, + S_GET_VALUE (mri_common_symbol) + repeat); + goto getout; + } + + if (!need_pass_2) + p = frag_var (rs_fill, 1, 1, (relax_substateT) 0, (symbolS *) 0, + (offsetT) repeat, (char *) 0); + } + else + { + if (now_seg == absolute_section) + { + as_bad (_("space allocation too complex in absolute section")); + subseg_set (text_section, 0); + } + + if (mri_common_symbol != NULL) + { + as_bad (_("space allocation too complex in common section")); + mri_common_symbol = NULL; + } + + if (!need_pass_2) + p = frag_var (rs_space, 1, 1, (relax_substateT) 0, + make_expr_symbol (&exp), (offsetT) 0, (char *) 0); + } + + if (p) + *p = val.X_add_number; + } + + getout: + + /* In MRI mode, after an odd number of bytes, we must align to an + even word boundary, unless the next instruction is a dc.b, ds.b + or dcb.b. */ + if (flag_mri && (bytes & 1) != 0) + mri_pending_align = 1; + + demand_empty_rest_of_line (); + + if (flag_mri) + mri_comment_end (stop, stopc); +} + +/* This is like s_space, but the value is a floating point number with + the given precision. This is for the MRI dcb.s pseudo-op and + friends. */ + +void +s_float_space (int float_type) +{ + offsetT count; + int flen; + char temp[MAXIMUM_NUMBER_OF_CHARS_FOR_FLOAT]; + char *stop = NULL; + char stopc; + + if (flag_mri) + stop = mri_comment_field (&stopc); + + count = get_absolute_expression (); + + SKIP_WHITESPACE (); + if (*input_line_pointer != ',') + { + as_bad (_("missing value")); + ignore_rest_of_line (); + if (flag_mri) + mri_comment_end (stop, stopc); + return; + } + + ++input_line_pointer; + + SKIP_WHITESPACE (); + + /* Skip any 0{letter} that may be present. Don't even check if the + * letter is legal. */ + if (input_line_pointer[0] == '0' + && ISALPHA (input_line_pointer[1])) + input_line_pointer += 2; + + /* Accept :xxxx, where the x's are hex digits, for a floating point + with the exact digits specified. */ + if (input_line_pointer[0] == ':') + { + flen = hex_float (float_type, temp); + if (flen < 0) + { + ignore_rest_of_line (); + if (flag_mri) + mri_comment_end (stop, stopc); + return; + } + } + else + { + char *err; + + err = md_atof (float_type, temp, &flen); + know (flen <= MAXIMUM_NUMBER_OF_CHARS_FOR_FLOAT); + know (flen > 0); + if (err) + { + as_bad (_("bad floating literal: %s"), err); + ignore_rest_of_line (); + if (flag_mri) + mri_comment_end (stop, stopc); + return; + } + } + + while (--count >= 0) + { + char *p; + + p = frag_more (flen); + memcpy (p, temp, (unsigned int) flen); + } + + demand_empty_rest_of_line (); + + if (flag_mri) + mri_comment_end (stop, stopc); +} + +/* Handle the .struct pseudo-op, as found in MIPS assemblers. */ + +void +s_struct (int ignore ATTRIBUTE_UNUSED) +{ + char *stop = NULL; + char stopc; + + if (flag_mri) + stop = mri_comment_field (&stopc); + abs_section_offset = get_absolute_expression (); + subseg_set (absolute_section, 0); + demand_empty_rest_of_line (); + if (flag_mri) + mri_comment_end (stop, stopc); +} + +void +s_text (int ignore ATTRIBUTE_UNUSED) +{ + register int temp; + + temp = get_absolute_expression (); + subseg_set (text_section, (subsegT) temp); + demand_empty_rest_of_line (); +#ifdef OBJ_VMS + const_flag &= ~IN_DEFAULT_SECTION; +#endif +} + + +/* Verify that we are at the end of a line. If not, issue an error and + skip to EOL. */ + +void +demand_empty_rest_of_line (void) +{ + SKIP_WHITESPACE (); + if (is_end_of_line[(unsigned char) *input_line_pointer]) + input_line_pointer++; + else + { + if (ISPRINT (*input_line_pointer)) + as_bad (_("junk at end of line, first unrecognized character is `%c'"), + *input_line_pointer); + else + as_bad (_("junk at end of line, first unrecognized character valued 0x%x"), + *input_line_pointer); + ignore_rest_of_line (); + } + + /* Return pointing just after end-of-line. */ + know (is_end_of_line[(unsigned char) input_line_pointer[-1]]); +} + +/* Silently advance to the end of line. Use this after already having + issued an error about something bad. */ + +void +ignore_rest_of_line (void) +{ + while (input_line_pointer < buffer_limit + && !is_end_of_line[(unsigned char) *input_line_pointer]) + input_line_pointer++; + + input_line_pointer++; + + /* Return pointing just after end-of-line. */ + know (is_end_of_line[(unsigned char) input_line_pointer[-1]]); +} + +/* Sets frag for given symbol to zero_address_frag, except when the + symbol frag is already set to a dummy listing frag. */ + +static void +set_zero_frag (symbolS *symbolP) +{ + if (symbol_get_frag (symbolP)->fr_type != rs_dummy) + symbol_set_frag (symbolP, &zero_address_frag); +} + +/* 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 symbols type, value, and frag; */ + +void +pseudo_set (symbolS *symbolP) +{ + expressionS exp; + segT seg; +#if (defined (OBJ_AOUT) || defined (OBJ_BOUT)) && ! defined (BFD_ASSEMBLER) + int ext; +#endif /* OBJ_AOUT or OBJ_BOUT */ + + know (symbolP); /* NULL pointer is logic error. */ + + (void) expression (&exp); + + if (exp.X_op == O_illegal) + as_bad (_("illegal expression")); + else if (exp.X_op == O_absent) + as_bad (_("missing expression")); + else if (exp.X_op == O_big) + { + if (exp.X_add_number > 0) + as_bad (_("bignum invalid")); + else + as_bad (_("floating point number invalid")); + } + else if (exp.X_op == O_subtract + && SEG_NORMAL (S_GET_SEGMENT (exp.X_add_symbol)) + && (symbol_get_frag (exp.X_add_symbol) + == symbol_get_frag (exp.X_op_symbol))) + { + exp.X_op = O_constant; + exp.X_add_number = (S_GET_VALUE (exp.X_add_symbol) + - S_GET_VALUE (exp.X_op_symbol)); + } + + if (symbol_section_p (symbolP)) + { + as_bad ("attempt to set value of section symbol"); + return; + } +#if (defined (OBJ_AOUT) || defined (OBJ_BOUT)) && ! defined (BFD_ASSEMBLER) + ext = S_IS_EXTERNAL (symbolP); +#endif /* OBJ_AOUT or OBJ_BOUT */ + + switch (exp.X_op) + { + case O_illegal: + case O_absent: + case O_big: + exp.X_add_number = 0; + /* Fall through. */ + case O_constant: + S_SET_SEGMENT (symbolP, absolute_section); + S_SET_VALUE (symbolP, (valueT) exp.X_add_number); + set_zero_frag (symbolP); + break; + + case O_register: + S_SET_SEGMENT (symbolP, reg_section); + S_SET_VALUE (symbolP, (valueT) exp.X_add_number); + set_zero_frag (symbolP); + break; + + case O_symbol: + seg = S_GET_SEGMENT (exp.X_add_symbol); + /* For x=undef+const, create an expression symbol. + For x=x+const, just update x except when x is an undefined symbol + For x=defined+const, evaluate x. */ + if (symbolP == exp.X_add_symbol + && (seg != undefined_section + || !symbol_constant_p (symbolP))) + { + *symbol_X_add_number (symbolP) += exp.X_add_number; + break; + } + else if (seg != undefined_section) + { + symbolS *s = exp.X_add_symbol; + + if (S_IS_COMMON (s)) + as_bad (_("`%s' can't be equated to common symbol '%s'"), + S_GET_NAME (symbolP), S_GET_NAME (s)); + + S_SET_SEGMENT (symbolP, seg); + S_SET_VALUE (symbolP, exp.X_add_number + S_GET_VALUE (s)); + symbol_set_frag (symbolP, symbol_get_frag (s)); + copy_symbol_attributes (symbolP, s); + break; + } + /* Fall thru */ + + default: + /* The value is some complex expression. + Set segment and frag back to that of a newly created symbol. */ + S_SET_SEGMENT (symbolP, undefined_section); + symbol_set_value_expression (symbolP, &exp); + set_zero_frag (symbolP); + break; + } + +#if (defined (OBJ_AOUT) || defined (OBJ_BOUT)) && ! defined (BFD_ASSEMBLER) + if (ext) + S_SET_EXTERNAL (symbolP); + else + S_CLEAR_EXTERNAL (symbolP); +#endif /* OBJ_AOUT or OBJ_BOUT */ +} + +/* cons() + + CONStruct more frag of .bytes, or .words etc. + Should need_pass_2 be 1 then emit no frag(s). + This understands EXPRESSIONS. + + Bug (?) + + 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. */ + +/* Select a parser for cons expressions. */ + +/* Some targets need to parse the expression in various fancy ways. + You can define TC_PARSE_CONS_EXPRESSION to do whatever you like + (for example, the HPPA does this). Otherwise, you can define + BITFIELD_CONS_EXPRESSIONS to permit bitfields to be specified, or + REPEAT_CONS_EXPRESSIONS to permit repeat counts. If none of these + are defined, which is the normal case, then only simple expressions + are permitted. */ + +#ifdef TC_M68K +static void +parse_mri_cons (expressionS *exp, unsigned int nbytes); +#endif + +#ifndef TC_PARSE_CONS_EXPRESSION +#ifdef BITFIELD_CONS_EXPRESSIONS +#define TC_PARSE_CONS_EXPRESSION(EXP, NBYTES) parse_bitfield_cons (EXP, NBYTES) +static void +parse_bitfield_cons (expressionS *exp, unsigned int nbytes); +#endif +#ifdef REPEAT_CONS_EXPRESSIONS +#define TC_PARSE_CONS_EXPRESSION(EXP, NBYTES) parse_repeat_cons (EXP, NBYTES) +static void +parse_repeat_cons (expressionS *exp, unsigned int nbytes); +#endif + +/* If we haven't gotten one yet, just call expression. */ +#ifndef TC_PARSE_CONS_EXPRESSION +#define TC_PARSE_CONS_EXPRESSION(EXP, NBYTES) expression (EXP) +#endif +#endif + +void +do_parse_cons_expression (expressionS *exp, + int nbytes ATTRIBUTE_UNUSED) +{ + TC_PARSE_CONS_EXPRESSION (exp, nbytes); +} + + +/* Worker to do .byte etc statements. + Clobbers input_line_pointer and checks end-of-line. */ + +static void +cons_worker (register int nbytes, /* 1=.byte, 2=.word, 4=.long. */ + int rva) +{ + int c; + expressionS exp; + char *stop = NULL; + char stopc; + +#ifdef md_flush_pending_output + md_flush_pending_output (); +#endif + + if (flag_mri) + stop = mri_comment_field (&stopc); + + if (is_it_end_of_statement ()) + { + demand_empty_rest_of_line (); + if (flag_mri) + mri_comment_end (stop, stopc); + return; + } + +#ifdef TC_ADDRESS_BYTES + if (nbytes == 0) + nbytes = TC_ADDRESS_BYTES (); +#endif + +#ifdef md_cons_align + md_cons_align (nbytes); +#endif + + c = 0; + do + { +#ifdef TC_M68K + if (flag_m68k_mri) + parse_mri_cons (&exp, (unsigned int) nbytes); + else +#endif + TC_PARSE_CONS_EXPRESSION (&exp, (unsigned int) nbytes); + + if (rva) + { + if (exp.X_op == O_symbol) + exp.X_op = O_symbol_rva; + else + as_fatal (_("rva without symbol")); + } + emit_expr (&exp, (unsigned int) nbytes); + ++c; + } + while (*input_line_pointer++ == ','); + + /* In MRI mode, after an odd number of bytes, we must align to an + even word boundary, unless the next instruction is a dc.b, ds.b + or dcb.b. */ + if (flag_mri && nbytes == 1 && (c & 1) != 0) + mri_pending_align = 1; + + input_line_pointer--; /* Put terminator back into stream. */ + + demand_empty_rest_of_line (); + + if (flag_mri) + mri_comment_end (stop, stopc); +} + +void +cons (int size) +{ + cons_worker (size, 0); +} + +void +s_rva (int size) +{ + cons_worker (size, 1); +} + +/* Put the contents of expression EXP into the object file using + NBYTES bytes. If need_pass_2 is 1, this does nothing. */ + +void +emit_expr (expressionS *exp, unsigned int nbytes) +{ + operatorT op; + register char *p; + valueT extra_digit = 0; + + /* Don't do anything if we are going to make another pass. */ + if (need_pass_2) + return; + + dot_value = frag_now_fix (); + +#ifndef NO_LISTING +#ifdef OBJ_ELF + /* When gcc emits DWARF 1 debugging pseudo-ops, a line number will + appear as a four byte positive constant in the .line section, + followed by a 2 byte 0xffff. Look for that case here. */ + { + static int dwarf_line = -1; + + if (strcmp (segment_name (now_seg), ".line") != 0) + dwarf_line = -1; + else if (dwarf_line >= 0 + && nbytes == 2 + && exp->X_op == O_constant + && (exp->X_add_number == -1 || exp->X_add_number == 0xffff)) + listing_source_line ((unsigned int) dwarf_line); + else if (nbytes == 4 + && exp->X_op == O_constant + && exp->X_add_number >= 0) + dwarf_line = exp->X_add_number; + else + dwarf_line = -1; + } + + /* When gcc emits DWARF 1 debugging pseudo-ops, a file name will + appear as a 2 byte TAG_compile_unit (0x11) followed by a 2 byte + AT_sibling (0x12) followed by a four byte address of the sibling + followed by a 2 byte AT_name (0x38) followed by the name of the + file. We look for that case here. */ + { + static int dwarf_file = 0; + + if (strcmp (segment_name (now_seg), ".debug") != 0) + dwarf_file = 0; + else if (dwarf_file == 0 + && nbytes == 2 + && exp->X_op == O_constant + && exp->X_add_number == 0x11) + dwarf_file = 1; + else if (dwarf_file == 1 + && nbytes == 2 + && exp->X_op == O_constant + && exp->X_add_number == 0x12) + dwarf_file = 2; + else if (dwarf_file == 2 + && nbytes == 4) + dwarf_file = 3; + else if (dwarf_file == 3 + && nbytes == 2 + && exp->X_op == O_constant + && exp->X_add_number == 0x38) + dwarf_file = 4; + else + dwarf_file = 0; + + /* The variable dwarf_file_string tells stringer that the string + may be the name of the source file. */ + if (dwarf_file == 4) + dwarf_file_string = 1; + else + dwarf_file_string = 0; + } +#endif +#endif + + if (check_eh_frame (exp, &nbytes)) + return; + + op = exp->X_op; + + /* Allow `.word 0' in the absolute section. */ + if (now_seg == absolute_section) + { + if (op != O_constant || exp->X_add_number != 0) + as_bad (_("attempt to store value in absolute section")); + abs_section_offset += nbytes; + return; + } + + /* Handle a negative bignum. */ + if (op == O_uminus + && exp->X_add_number == 0 + && symbol_get_value_expression (exp->X_add_symbol)->X_op == O_big + && symbol_get_value_expression (exp->X_add_symbol)->X_add_number > 0) + { + int i; + unsigned long carry; + + exp = symbol_get_value_expression (exp->X_add_symbol); + + /* Negate the bignum: one's complement each digit and add 1. */ + carry = 1; + for (i = 0; i < exp->X_add_number; i++) + { + unsigned long next; + + next = (((~(generic_bignum[i] & LITTLENUM_MASK)) + & LITTLENUM_MASK) + + carry); + generic_bignum[i] = next & LITTLENUM_MASK; + carry = next >> LITTLENUM_NUMBER_OF_BITS; + } + + /* We can ignore any carry out, because it will be handled by + extra_digit if it is needed. */ + + extra_digit = (valueT) -1; + op = O_big; + } + + if (op == O_absent || op == O_illegal) + { + as_warn (_("zero assumed for missing expression")); + exp->X_add_number = 0; + op = O_constant; + } + else if (op == O_big && exp->X_add_number <= 0) + { + as_bad (_("floating point number invalid")); + exp->X_add_number = 0; + op = O_constant; + } + else if (op == O_register) + { + as_warn (_("register value used as expression")); + op = O_constant; + } + + p = frag_more ((int) nbytes); + +#ifndef WORKING_DOT_WORD + /* If we have the difference of two symbols in a word, save it on + the broken_words list. See the code in write.c. */ + if (op == O_subtract && nbytes == 2) + { + struct broken_word *x; + + x = (struct broken_word *) xmalloc (sizeof (struct broken_word)); + x->next_broken_word = broken_words; + broken_words = x; + x->seg = now_seg; + x->subseg = now_subseg; + x->frag = frag_now; + x->word_goes_here = p; + x->dispfrag = 0; + x->add = exp->X_add_symbol; + x->sub = exp->X_op_symbol; + x->addnum = exp->X_add_number; + x->added = 0; + x->use_jump = 0; + new_broken_words++; + return; + } +#endif + + /* If we have an integer, but the number of bytes is too large to + pass to md_number_to_chars, handle it as a bignum. */ + if (op == O_constant && nbytes > sizeof (valueT)) + { + extra_digit = exp->X_unsigned ? 0 : -1; + convert_to_bignum (exp); + op = O_big; + } + + if (op == O_constant) + { + register valueT get; + register valueT use; + register valueT mask; + valueT hibit; + register valueT unmask; + + /* JF << of >= number of bits in the object is undefined. In + particular SPARC (Sun 4) has problems. */ + if (nbytes >= sizeof (valueT)) + { + mask = 0; + if (nbytes > sizeof (valueT)) + hibit = 0; + else + hibit = (valueT) 1 << (nbytes * BITS_PER_CHAR - 1); + } + else + { + /* Don't store these bits. */ + mask = ~(valueT) 0 << (BITS_PER_CHAR * nbytes); + hibit = (valueT) 1 << (nbytes * BITS_PER_CHAR - 1); + } + + unmask = ~mask; /* Do store these bits. */ + +#ifdef NEVER + "Do this mod if you want every overflow check to assume SIGNED 2's complement data."; + mask = ~(unmask >> 1); /* Includes sign bit now. */ +#endif + + get = exp->X_add_number; + use = get & unmask; + if ((get & mask) != 0 + && ((get & mask) != mask + || (get & hibit) == 0)) + { /* Leading bits contain both 0s & 1s. */ + as_warn (_("value 0x%lx truncated to 0x%lx"), + (unsigned long) get, (unsigned long) use); + } + /* Put bytes in right order. */ + md_number_to_chars (p, use, (int) nbytes); + } + else if (op == O_big) + { + unsigned int size; + LITTLENUM_TYPE *nums; + + know (nbytes % CHARS_PER_LITTLENUM == 0); + + size = exp->X_add_number * CHARS_PER_LITTLENUM; + if (nbytes < size) + { + as_warn (_("bignum truncated to %d bytes"), nbytes); + size = nbytes; + } + + if (target_big_endian) + { + while (nbytes > size) + { + md_number_to_chars (p, extra_digit, CHARS_PER_LITTLENUM); + nbytes -= CHARS_PER_LITTLENUM; + p += CHARS_PER_LITTLENUM; + } + + nums = generic_bignum + size / CHARS_PER_LITTLENUM; + while (size >= CHARS_PER_LITTLENUM) + { + --nums; + md_number_to_chars (p, (valueT) *nums, CHARS_PER_LITTLENUM); + size -= CHARS_PER_LITTLENUM; + p += CHARS_PER_LITTLENUM; + } + } + else + { + nums = generic_bignum; + while (size >= CHARS_PER_LITTLENUM) + { + md_number_to_chars (p, (valueT) *nums, CHARS_PER_LITTLENUM); + ++nums; + size -= CHARS_PER_LITTLENUM; + p += CHARS_PER_LITTLENUM; + nbytes -= CHARS_PER_LITTLENUM; + } + + while (nbytes >= CHARS_PER_LITTLENUM) + { + md_number_to_chars (p, extra_digit, CHARS_PER_LITTLENUM); + nbytes -= CHARS_PER_LITTLENUM; + p += CHARS_PER_LITTLENUM; + } + } + } + else + { + memset (p, 0, nbytes); + + /* Now we need to generate a fixS to record the symbol value. + This is easy for BFD. For other targets it can be more + complex. For very complex cases (currently, the HPPA and + NS32K), you can define TC_CONS_FIX_NEW to do whatever you + want. For simpler cases, you can define TC_CONS_RELOC to be + the name of the reloc code that should be stored in the fixS. + If neither is defined, the code uses NO_RELOC if it is + defined, and otherwise uses 0. */ + +#ifdef BFD_ASSEMBLER +#ifdef TC_CONS_FIX_NEW + TC_CONS_FIX_NEW (frag_now, p - frag_now->fr_literal, nbytes, exp); +#else + { + bfd_reloc_code_real_type r; + + switch (nbytes) + { + case 1: + r = BFD_RELOC_8; + break; + case 2: + r = BFD_RELOC_16; + break; + case 4: + r = BFD_RELOC_32; + break; + case 8: + r = BFD_RELOC_64; + break; + default: + as_bad (_("unsupported BFD relocation size %u"), nbytes); + r = BFD_RELOC_32; + break; + } + fix_new_exp (frag_now, p - frag_now->fr_literal, (int) nbytes, exp, + 0, r); + } +#endif +#else +#ifdef TC_CONS_FIX_NEW + TC_CONS_FIX_NEW (frag_now, p - frag_now->fr_literal, nbytes, exp); +#else + /* Figure out which reloc number to use. Use TC_CONS_RELOC if + it is defined, otherwise use NO_RELOC if it is defined, + otherwise use 0. */ +#ifndef TC_CONS_RELOC +#ifdef NO_RELOC +#define TC_CONS_RELOC NO_RELOC +#else +#define TC_CONS_RELOC 0 +#endif +#endif + fix_new_exp (frag_now, p - frag_now->fr_literal, (int) nbytes, exp, 0, + TC_CONS_RELOC); +#endif /* TC_CONS_FIX_NEW */ +#endif /* BFD_ASSEMBLER */ + } +} + +#ifdef BITFIELD_CONS_EXPRESSIONS + +/* i960 assemblers, (eg, asm960), allow bitfields after ".byte" as + w:x,y:z, where w and y are bitwidths and x and y are values. They + then pack them all together. We do a little better in that we allow + them in words, longs, etc. and we'll pack them in target byte order + for you. + + The rules are: pack least significant bit first, if a field doesn't + entirely fit, put it in the next unit. Overflowing the bitfield is + explicitly *not* even a warning. The bitwidth should be considered + a "mask". + + To use this function the tc-XXX.h file should define + BITFIELD_CONS_EXPRESSIONS. */ + +static void +parse_bitfield_cons (exp, nbytes) + expressionS *exp; + unsigned int nbytes; +{ + unsigned int bits_available = BITS_PER_CHAR * nbytes; + char *hold = input_line_pointer; + + (void) expression (exp); + + if (*input_line_pointer == ':') + { + /* Bitfields. */ + long value = 0; + + for (;;) + { + unsigned long width; + + if (*input_line_pointer != ':') + { + input_line_pointer = hold; + break; + } /* Next piece is not a bitfield. */ + + /* In the general case, we can't allow + full expressions with symbol + differences and such. The relocation + entries for symbols not defined in this + assembly would require arbitrary field + widths, positions, and masks which most + of our current object formats don't + support. + + In the specific case where a symbol + *is* defined in this assembly, we + *could* build fixups and track it, but + this could lead to confusion for the + backends. I'm lazy. I'll take any + SEG_ABSOLUTE. I think that means that + you can use a previous .set or + .equ type symbol. xoxorich. */ + + if (exp->X_op == O_absent) + { + as_warn (_("using a bit field width of zero")); + exp->X_add_number = 0; + exp->X_op = O_constant; + } /* Implied zero width bitfield. */ + + if (exp->X_op != O_constant) + { + *input_line_pointer = '\0'; + as_bad (_("field width \"%s\" too complex for a bitfield"), hold); + *input_line_pointer = ':'; + demand_empty_rest_of_line (); + return; + } /* Too complex. */ + + if ((width = exp->X_add_number) > (BITS_PER_CHAR * nbytes)) + { + as_warn (_("field width %lu too big to fit in %d bytes: truncated to %d bits"), + width, nbytes, (BITS_PER_CHAR * nbytes)); + width = BITS_PER_CHAR * nbytes; + } /* Too big. */ + + if (width > bits_available) + { + /* FIXME-SOMEDAY: backing up and reparsing is wasteful. */ + input_line_pointer = hold; + exp->X_add_number = value; + break; + } /* Won't fit. */ + + /* Skip ':'. */ + hold = ++input_line_pointer; + + (void) expression (exp); + if (exp->X_op != O_constant) + { + char cache = *input_line_pointer; + + *input_line_pointer = '\0'; + as_bad (_("field value \"%s\" too complex for a bitfield"), hold); + *input_line_pointer = cache; + demand_empty_rest_of_line (); + return; + } /* Too complex. */ + + value |= ((~(-1 << width) & exp->X_add_number) + << ((BITS_PER_CHAR * nbytes) - bits_available)); + + if ((bits_available -= width) == 0 + || is_it_end_of_statement () + || *input_line_pointer != ',') + { + break; + } /* All the bitfields we're gonna get. */ + + hold = ++input_line_pointer; + (void) expression (exp); + } + + exp->X_add_number = value; + exp->X_op = O_constant; + exp->X_unsigned = 1; + } +} + +#endif /* BITFIELD_CONS_EXPRESSIONS */ + +/* Handle an MRI style string expression. */ + +#ifdef TC_M68K +static void +parse_mri_cons (exp, nbytes) + expressionS *exp; + unsigned int nbytes; +{ + if (*input_line_pointer != '\'' + && (input_line_pointer[1] != '\'' + || (*input_line_pointer != 'A' + && *input_line_pointer != 'E'))) + TC_PARSE_CONS_EXPRESSION (exp, nbytes); + else + { + unsigned int scan; + unsigned int result = 0; + + /* An MRI style string. Cut into as many bytes as will fit into + a nbyte chunk, left justify if necessary, and separate with + commas so we can try again later. */ + if (*input_line_pointer == 'A') + ++input_line_pointer; + else if (*input_line_pointer == 'E') + { + as_bad (_("EBCDIC constants are not supported")); + ++input_line_pointer; + } + + input_line_pointer++; + for (scan = 0; scan < nbytes; scan++) + { + if (*input_line_pointer == '\'') + { + if (input_line_pointer[1] == '\'') + { + input_line_pointer++; + } + else + break; + } + result = (result << 8) | (*input_line_pointer++); + } + + /* Left justify. */ + while (scan < nbytes) + { + result <<= 8; + scan++; + } + + /* Create correct expression. */ + exp->X_op = O_constant; + exp->X_add_number = result; + + /* Fake it so that we can read the next char too. */ + if (input_line_pointer[0] != '\'' || + (input_line_pointer[0] == '\'' && input_line_pointer[1] == '\'')) + { + input_line_pointer -= 2; + input_line_pointer[0] = ','; + input_line_pointer[1] = '\''; + } + else + input_line_pointer++; + } +} +#endif /* TC_M68K */ + +#ifdef REPEAT_CONS_EXPRESSIONS + +/* Parse a repeat expression for cons. This is used by the MIPS + assembler. The format is NUMBER:COUNT; NUMBER appears in the + object file COUNT times. + + To use this for a target, define REPEAT_CONS_EXPRESSIONS. */ + +static void +parse_repeat_cons (exp, nbytes) + expressionS *exp; + unsigned int nbytes; +{ + expressionS count; + register int i; + + expression (exp); + + if (*input_line_pointer != ':') + { + /* No repeat count. */ + return; + } + + ++input_line_pointer; + expression (&count); + if (count.X_op != O_constant + || count.X_add_number <= 0) + { + as_warn (_("unresolvable or nonpositive repeat count; using 1")); + return; + } + + /* The cons function is going to output this expression once. So we + output it count - 1 times. */ + for (i = count.X_add_number - 1; i > 0; i--) + emit_expr (exp, nbytes); +} + +#endif /* REPEAT_CONS_EXPRESSIONS */ + +/* Parse a floating point number represented as a hex constant. This + permits users to specify the exact bits they want in the floating + point number. */ + +static int +hex_float (int float_type, char *bytes) +{ + int length; + int i; + + switch (float_type) + { + case 'f': + case 'F': + case 's': + case 'S': + length = 4; + break; + + case 'd': + case 'D': + case 'r': + case 'R': + length = 8; + break; + + case 'x': + case 'X': + length = 12; + break; + + case 'p': + case 'P': + length = 12; + break; + + default: + as_bad (_("unknown floating type type '%c'"), float_type); + return -1; + } + + /* It would be nice if we could go through expression to parse the + hex constant, but if we get a bignum it's a pain to sort it into + the buffer correctly. */ + i = 0; + while (hex_p (*input_line_pointer) || *input_line_pointer == '_') + { + int d; + + /* The MRI assembler accepts arbitrary underscores strewn about + through the hex constant, so we ignore them as well. */ + if (*input_line_pointer == '_') + { + ++input_line_pointer; + continue; + } + + if (i >= length) + { + as_warn (_("floating point constant too large")); + return -1; + } + d = hex_value (*input_line_pointer) << 4; + ++input_line_pointer; + while (*input_line_pointer == '_') + ++input_line_pointer; + if (hex_p (*input_line_pointer)) + { + d += hex_value (*input_line_pointer); + ++input_line_pointer; + } + if (target_big_endian) + bytes[i] = d; + else + bytes[length - i - 1] = d; + ++i; + } + + if (i < length) + { + if (target_big_endian) + memset (bytes + i, 0, length - i); + else + memset (bytes, 0, length - i); + } + + return length; +} + +/* float_cons() + + CONStruct some more frag chars of .floats .ffloats etc. + Makes 0 or more new frags. + If need_pass_2 == 1, 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 + truncation, 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 +float_cons (/* Clobbers input_line-pointer, checks end-of-line. */ + register int float_type /* 'f':.ffloat ... 'F':.float ... */) +{ + register char *p; + int length; /* Number of chars in an object. */ + register char *err; /* Error from scanning floating literal. */ + char temp[MAXIMUM_NUMBER_OF_CHARS_FOR_FLOAT]; + + if (is_it_end_of_statement ()) + { + demand_empty_rest_of_line (); + return; + } + +#ifdef md_flush_pending_output + md_flush_pending_output (); +#endif + + do + { + /* input_line_pointer->1st char of a flonum (we hope!). */ + SKIP_WHITESPACE (); + + /* 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])) + input_line_pointer += 2; + + /* Accept :xxxx, where the x's are hex digits, for a floating + point with the exact digits specified. */ + if (input_line_pointer[0] == ':') + { + ++input_line_pointer; + length = hex_float (float_type, temp); + if (length < 0) + { + ignore_rest_of_line (); + return; + } + } + else + { + err = md_atof (float_type, temp, &length); + know (length <= MAXIMUM_NUMBER_OF_CHARS_FOR_FLOAT); + know (length > 0); + if (err) + { + as_bad (_("bad floating literal: %s"), err); + ignore_rest_of_line (); + return; + } + } + + if (!need_pass_2) + { + int count; + + count = 1; + +#ifdef REPEAT_CONS_EXPRESSIONS + if (*input_line_pointer == ':') + { + expressionS count_exp; + + ++input_line_pointer; + expression (&count_exp); + + if (count_exp.X_op != O_constant + || count_exp.X_add_number <= 0) + as_warn (_("unresolvable or nonpositive repeat count; using 1")); + else + count = count_exp.X_add_number; + } +#endif + + while (--count >= 0) + { + p = frag_more (length); + memcpy (p, temp, (unsigned int) length); + } + } + SKIP_WHITESPACE (); + } + while (*input_line_pointer++ == ','); + + /* Put terminator back into stream. */ + --input_line_pointer; + demand_empty_rest_of_line (); +} + +/* Return the size of a LEB128 value. */ + +static inline int +sizeof_sleb128 (offsetT value) +{ + register int size = 0; + register unsigned byte; + + do + { + byte = (value & 0x7f); + /* Sadly, we cannot rely on typical arithmetic right shift behaviour. + Fortunately, we can structure things so that the extra work reduces + to a noop on systems that do things "properly". */ + value = (value >> 7) | ~(-(offsetT)1 >> 7); + size += 1; + } + while (!(((value == 0) && ((byte & 0x40) == 0)) + || ((value == -1) && ((byte & 0x40) != 0)))); + + return size; +} + +static inline int +sizeof_uleb128 (valueT value) +{ + register int size = 0; + register unsigned byte; + + do + { + byte = (value & 0x7f); + value >>= 7; + size += 1; + } + while (value != 0); + + return size; +} + +int +sizeof_leb128 (valueT value, int sign) +{ + if (sign) + return sizeof_sleb128 ((offsetT) value); + else + return sizeof_uleb128 (value); +} + +/* Output a LEB128 value. */ + +static inline int +output_sleb128 (char *p, offsetT value) +{ + register char *orig = p; + register int more; + + do + { + unsigned byte = (value & 0x7f); + + /* Sadly, we cannot rely on typical arithmetic right shift behaviour. + Fortunately, we can structure things so that the extra work reduces + to a noop on systems that do things "properly". */ + value = (value >> 7) | ~(-(offsetT)1 >> 7); + + more = !((((value == 0) && ((byte & 0x40) == 0)) + || ((value == -1) && ((byte & 0x40) != 0)))); + if (more) + byte |= 0x80; + + *p++ = byte; + } + while (more); + + return p - orig; +} + +static inline int +output_uleb128 (char *p, valueT value) +{ + char *orig = p; + + do + { + unsigned byte = (value & 0x7f); + value >>= 7; + if (value != 0) + /* More bytes to follow. */ + byte |= 0x80; + + *p++ = byte; + } + while (value != 0); + + return p - orig; +} + +int +output_leb128 (char *p, valueT value, int sign) +{ + if (sign) + return output_sleb128 (p, (offsetT) value); + else + return output_uleb128 (p, value); +} + +/* Do the same for bignums. We combine sizeof with output here in that + we don't output for NULL values of P. It isn't really as critical as + for "normal" values that this be streamlined. */ + +static inline int +output_big_sleb128 (char *p, LITTLENUM_TYPE *bignum, int size) +{ + char *orig = p; + valueT val = 0; + int loaded = 0; + unsigned byte; + + /* Strip leading sign extensions off the bignum. */ + while (size > 1 + && bignum[size - 1] == LITTLENUM_MASK + && bignum[size - 2] > LITTLENUM_MASK / 2) + size--; + + do + { + /* OR in the next part of the littlenum. */ + val |= (*bignum << loaded); + loaded += LITTLENUM_NUMBER_OF_BITS; + size--; + bignum++; + + /* Add bytes until there are less than 7 bits left in VAL + or until every non-sign bit has been written. */ + do + { + byte = val & 0x7f; + loaded -= 7; + val >>= 7; + if (size > 0 + || val != ((byte & 0x40) == 0 ? 0 : ((valueT) 1 << loaded) - 1)) + byte |= 0x80; + + if (orig) + *p = byte; + p++; + } + while ((byte & 0x80) != 0 && loaded >= 7); + } + while (size > 0); + + /* Mop up any left-over bits (of which there will be less than 7). */ + if ((byte & 0x80) != 0) + { + /* Sign-extend VAL. */ + if (val & (1 << (loaded - 1))) + val |= ~0 << loaded; + if (orig) + *p = val & 0x7f; + p++; + } + + return p - orig; +} + +static inline int +output_big_uleb128 (char *p, LITTLENUM_TYPE *bignum, int size) +{ + char *orig = p; + valueT val = 0; + int loaded = 0; + unsigned byte; + + /* Strip leading zeros off the bignum. */ + /* XXX: Is this needed? */ + while (size > 0 && bignum[size - 1] == 0) + size--; + + do + { + if (loaded < 7 && size > 0) + { + val |= (*bignum << loaded); + loaded += 8 * CHARS_PER_LITTLENUM; + size--; + bignum++; + } + + byte = val & 0x7f; + loaded -= 7; + val >>= 7; + + if (size > 0 || val) + byte |= 0x80; + + if (orig) + *p = byte; + p++; + } + while (byte & 0x80); + + return p - orig; +} + +static int +output_big_leb128 (char *p, LITTLENUM_TYPE *bignum, int size, int sign) +{ + if (sign) + return output_big_sleb128 (p, bignum, size); + else + return output_big_uleb128 (p, bignum, size); +} + +/* Generate the appropriate fragments for a given expression to emit a + leb128 value. */ + +static void +emit_leb128_expr (expressionS *exp, int sign) +{ + operatorT op = exp->X_op; + unsigned int nbytes; + + if (op == O_absent || op == O_illegal) + { + as_warn (_("zero assumed for missing expression")); + exp->X_add_number = 0; + op = O_constant; + } + else if (op == O_big && exp->X_add_number <= 0) + { + as_bad (_("floating point number invalid")); + exp->X_add_number = 0; + op = O_constant; + } + else if (op == O_register) + { + as_warn (_("register value used as expression")); + op = O_constant; + } + else if (op == O_constant + && sign + && (exp->X_add_number < 0) != !exp->X_unsigned) + { + /* We're outputting a signed leb128 and the sign of X_add_number + doesn't reflect the sign of the original value. Convert EXP + to a correctly-extended bignum instead. */ + convert_to_bignum (exp); + op = O_big; + } + + /* Let check_eh_frame know that data is being emitted. nbytes == -1 is + a signal that this is leb128 data. It shouldn't optimize this away. */ + nbytes = (unsigned int) -1; + if (check_eh_frame (exp, &nbytes)) + abort (); + + /* Let the backend know that subsequent data may be byte aligned. */ +#ifdef md_cons_align + md_cons_align (1); +#endif + + if (op == O_constant) + { + /* If we've got a constant, emit the thing directly right now. */ + + valueT value = exp->X_add_number; + int size; + char *p; + + size = sizeof_leb128 (value, sign); + p = frag_more (size); + output_leb128 (p, value, sign); + } + else if (op == O_big) + { + /* O_big is a different sort of constant. */ + + int size; + char *p; + + size = output_big_leb128 (NULL, generic_bignum, exp->X_add_number, sign); + p = frag_more (size); + output_big_leb128 (p, generic_bignum, exp->X_add_number, sign); + } + else + { + /* Otherwise, we have to create a variable sized fragment and + resolve things later. */ + + frag_var (rs_leb128, sizeof_uleb128 (~(valueT) 0), 0, sign, + make_expr_symbol (exp), 0, (char *) NULL); + } +} + +/* Parse the .sleb128 and .uleb128 pseudos. */ + +void +s_leb128 (int sign) +{ + expressionS exp; + +#ifdef md_flush_pending_output + md_flush_pending_output (); +#endif + + do + { + expression (&exp); + emit_leb128_expr (&exp, sign); + } + while (*input_line_pointer++ == ','); + + input_line_pointer--; + demand_empty_rest_of_line (); +} + +/* We read 0 or more ',' separated, double-quoted strings. + Caller should have checked need_pass_2 is FALSE because we don't + check it. */ + +void +stringer (/* Worker to do .ascii etc statements. */ + /* Checks end-of-line. */ + register int append_zero /* 0: don't append '\0', else 1. */) +{ + register unsigned int c; + char *start; + +#ifdef md_flush_pending_output + md_flush_pending_output (); +#endif + + /* The following awkward logic is to parse ZERO or more strings, + comma separated. 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 ()) + { + c = 0; /* Skip loop. */ + ++input_line_pointer; /* Compensate for end of loop. */ + } + else + { + c = ','; /* Do loop. */ + } + /* If we have been switched into the abs_section then we + will not have an obstack onto which we can hang strings. */ + if (now_seg == absolute_section) + { + as_bad (_("strings must be placed into a section")); + c = 0; + ignore_rest_of_line (); + } + + while (c == ',' || c == '<' || c == '"') + { + SKIP_WHITESPACE (); + switch (*input_line_pointer) + { + case '\"': + ++input_line_pointer; /*->1st char of string. */ + start = input_line_pointer; + while (is_a_char (c = next_char_of_string ())) + { + FRAG_APPEND_1_CHAR (c); + } + if (append_zero) + { + FRAG_APPEND_1_CHAR (0); + } + know (input_line_pointer[-1] == '\"'); + +#ifndef NO_LISTING +#ifdef OBJ_ELF + /* In ELF, when gcc is emitting DWARF 1 debugging output, it + will emit .string with a filename in the .debug section + after a sequence of constants. See the comment in + emit_expr for the sequence. emit_expr will set + dwarf_file_string to non-zero if this string might be a + source file name. */ + if (strcmp (segment_name (now_seg), ".debug") != 0) + dwarf_file_string = 0; + else if (dwarf_file_string) + { + c = input_line_pointer[-1]; + input_line_pointer[-1] = '\0'; + listing_source_file (start); + input_line_pointer[-1] = c; + } +#endif +#endif + + break; + case '<': + input_line_pointer++; + c = get_single_number (); + FRAG_APPEND_1_CHAR (c); + if (*input_line_pointer != '>') + { + as_bad (_("expected ")); + } + input_line_pointer++; + break; + case ',': + input_line_pointer++; + break; + } + SKIP_WHITESPACE (); + c = *input_line_pointer; + } + + demand_empty_rest_of_line (); +} /* stringer() */ + +/* FIXME-SOMEDAY: I had trouble here on characters with the + high bits set. We'll probably also have trouble with + multibyte chars, wide chars, etc. Also be careful about + returning values bigger than 1 byte. xoxorich. */ + +unsigned int +next_char_of_string (void) +{ + register unsigned int c; + + c = *input_line_pointer++ & CHAR_MASK; + switch (c) + { + case '\"': + c = NOT_A_CHAR; + break; + + case '\n': + as_warn (_("unterminated string; newline inserted")); + bump_line_counters (); + break; + +#ifndef NO_STRING_ESCAPES + case '\\': + switch (c = *input_line_pointer++) + { + case 'b': + c = '\b'; + break; + + case 'f': + c = '\f'; + break; + + case 'n': + c = '\n'; + break; + + case 'r': + c = '\r'; + break; + + case 't': + c = '\t'; + break; + + case 'v': + c = '\013'; + break; + + case '\\': + case '"': + break; /* As itself. */ + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + long number; + int i; + + for (i = 0, number = 0; + ISDIGIT (c) && i < 3; + c = *input_line_pointer++, i++) + { + number = number * 8 + c - '0'; + } + + c = number & 0xff; + } + --input_line_pointer; + break; + + case 'x': + case 'X': + { + long number; + + number = 0; + c = *input_line_pointer++; + while (ISXDIGIT (c)) + { + if (ISDIGIT (c)) + number = number * 16 + c - '0'; + else if (ISUPPER (c)) + number = number * 16 + c - 'A' + 10; + else + number = number * 16 + c - 'a' + 10; + c = *input_line_pointer++; + } + c = number & 0xff; + --input_line_pointer; + } + break; + + case '\n': + /* To be compatible with BSD 4.2 as: give the luser a linefeed!! */ + as_warn (_("unterminated string; newline inserted")); + c = '\n'; + bump_line_counters (); + break; + + default: + +#ifdef ONLY_STANDARD_ESCAPES + as_bad (_("bad escaped character in string")); + c = '?'; +#endif /* ONLY_STANDARD_ESCAPES */ + + break; + } + break; +#endif /* ! defined (NO_STRING_ESCAPES) */ + + default: + break; + } + return (c); +} + +static segT +get_segmented_expression (register expressionS *expP) +{ + register segT retval; + + retval = expression (expP); + if (expP->X_op == O_illegal + || expP->X_op == O_absent + || expP->X_op == O_big) + { + as_bad (_("expected address expression")); + expP->X_op = O_constant; + expP->X_add_number = 0; + retval = absolute_section; + } + return retval; +} + +static segT +get_known_segmented_expression (register expressionS *expP) +{ + register segT retval; + + if ((retval = get_segmented_expression (expP)) == undefined_section) + { + /* There is no easy way to extract the undefined symbol from the + expression. */ + if (expP->X_add_symbol != NULL + && S_GET_SEGMENT (expP->X_add_symbol) != expr_section) + as_warn (_("symbol \"%s\" undefined; zero assumed"), + S_GET_NAME (expP->X_add_symbol)); + else + as_warn (_("some symbol undefined; zero assumed")); + retval = absolute_section; + expP->X_op = O_constant; + expP->X_add_number = 0; + } + know (retval == absolute_section || SEG_NORMAL (retval)); + return (retval); +} + +char /* Return terminator. */ +get_absolute_expression_and_terminator (long *val_pointer /* Return value of expression. */) +{ + /* FIXME: val_pointer should probably be offsetT *. */ + *val_pointer = (long) 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. */ + +char * +demand_copy_C_string (int *len_pointer) +{ + register char *s; + + if ((s = demand_copy_string (len_pointer)) != 0) + { + register int len; + + for (len = *len_pointer; len > 0; len--) + { + if (*s == 0) + { + s = 0; + len = 1; + *len_pointer = 0; + as_bad (_("this string may not contain \'\\0\'")); + } + } + } + + return s; +} + +/* Demand string, but return a safe (=private) copy of the string. + Return NULL if we can't read a string here. */ + +char * +demand_copy_string (int *lenP) +{ + register unsigned int c; + register int len; + char *retval; + + len = 0; + SKIP_WHITESPACE (); + if (*input_line_pointer == '\"') + { + input_line_pointer++; /* Skip opening quote. */ + + while (is_a_char (c = next_char_of_string ())) + { + obstack_1grow (¬es, c); + len++; + } + /* JF this next line is so demand_copy_C_string will return a + null terminated string. */ + obstack_1grow (¬es, '\0'); + retval = obstack_finish (¬es); + } + else + { + as_bad (_("missing string")); + retval = NULL; + ignore_rest_of_line (); + } + *lenP = len; + return (retval); +} + +/* In: Input_line_pointer->next character. + + Do: Skip input_line_pointer over all whitespace. + + Out: 1 if input_line_pointer->end-of-line. */ + +int +is_it_end_of_statement (void) +{ + SKIP_WHITESPACE (); + return (is_end_of_line[(unsigned char) *input_line_pointer]); +} + +void +equals (char *sym_name, int reassign) +{ + char *stop = NULL; + char stopc; + + input_line_pointer++; + if (*input_line_pointer == '=') + input_line_pointer++; + + while (*input_line_pointer == ' ' || *input_line_pointer == '\t') + input_line_pointer++; + + if (flag_mri) + stop = mri_comment_field (&stopc); + + assign_symbol (sym_name, !reassign); + + if (flag_mri) + { + demand_empty_rest_of_line (); + mri_comment_end (stop, stopc); + } +} + +/* .incbin -- include a file verbatim at the current location. */ + +void +s_incbin (int x ATTRIBUTE_UNUSED) +{ + FILE * binfile; + char * path; + char * filename; + char * binfrag; + long skip = 0; + long count = 0; + long bytes; + int len; + +#ifdef md_flush_pending_output + md_flush_pending_output (); +#endif + + SKIP_WHITESPACE (); + filename = demand_copy_string (& len); + if (filename == NULL) + return; + + SKIP_WHITESPACE (); + + /* Look for optional skip and count. */ + if (* input_line_pointer == ',') + { + ++ input_line_pointer; + skip = get_absolute_expression (); + + SKIP_WHITESPACE (); + + if (* input_line_pointer == ',') + { + ++ input_line_pointer; + + count = get_absolute_expression (); + if (count == 0) + as_warn (_(".incbin count zero, ignoring `%s'"), filename); + + SKIP_WHITESPACE (); + } + } + + demand_empty_rest_of_line (); + + /* Try opening absolute path first, then try include dirs. */ + binfile = fopen (filename, FOPEN_RB); + if (binfile == NULL) + { + int i; + + path = xmalloc ((unsigned long) len + include_dir_maxlen + 5); + + for (i = 0; i < include_dir_count; i++) + { + sprintf (path, "%s/%s", include_dirs[i], filename); + + binfile = fopen (path, FOPEN_RB); + if (binfile != NULL) + break; + } + + if (binfile == NULL) + as_bad (_("file not found: %s"), filename); + } + else + path = xstrdup (filename); + + if (binfile) + { + long file_len; + + register_dependency (path); + + /* Compute the length of the file. */ + if (fseek (binfile, 0, SEEK_END) != 0) + { + as_bad (_("seek to end of .incbin file failed `%s'"), path); + goto done; + } + file_len = ftell (binfile); + + /* If a count was not specified use the remainder of the file. */ + if (count == 0) + count = file_len - skip; + + if (skip < 0 || count < 0 || file_len < 0 || skip + count > file_len) + { + as_bad (_("skip (%ld) or count (%ld) invalid for file size (%ld)"), + skip, count, file_len); + goto done; + } + + if (fseek (binfile, skip, SEEK_SET) != 0) + { + as_bad (_("could not skip to %ld in file `%s'"), skip, path); + goto done; + } + + /* Allocate frag space and store file contents in it. */ + binfrag = frag_more (count); + + bytes = fread (binfrag, 1, count, binfile); + if (bytes < count) + as_warn (_("truncated file `%s', %ld of %ld bytes read"), + path, bytes, count); + } +done: + if (binfile != NULL) + fclose (binfile); + if (path) + free (path); +} + +/* .include -- include a file at this point. */ + +void +s_include (int arg ATTRIBUTE_UNUSED) +{ + char *filename; + int i; + FILE *try; + char *path; + + if (!flag_m68k_mri) + { + filename = demand_copy_string (&i); + if (filename == NULL) + { + /* demand_copy_string has already printed an error and + called ignore_rest_of_line. */ + return; + } + } + else + { + SKIP_WHITESPACE (); + i = 0; + while (!is_end_of_line[(unsigned char) *input_line_pointer] + && *input_line_pointer != ' ' + && *input_line_pointer != '\t') + { + obstack_1grow (¬es, *input_line_pointer); + ++input_line_pointer; + ++i; + } + + obstack_1grow (¬es, '\0'); + filename = obstack_finish (¬es); + while (!is_end_of_line[(unsigned char) *input_line_pointer]) + ++input_line_pointer; + } + + demand_empty_rest_of_line (); + path = xmalloc ((unsigned long) i + include_dir_maxlen + 5 /* slop */ ); + + for (i = 0; i < include_dir_count; i++) + { + strcpy (path, include_dirs[i]); + strcat (path, "/"); + strcat (path, filename); + if (0 != (try = fopen (path, FOPEN_RT))) + { + fclose (try); + goto gotit; + } + } + + free (path); + path = filename; +gotit: + /* malloc Storage leak when file is found on path. FIXME-SOMEDAY. */ + register_dependency (path); + input_scrub_insert_file (path); +} + +void +add_include_dir (char *path) +{ + int i; + + if (include_dir_count == 0) + { + include_dirs = (char **) xmalloc (2 * sizeof (*include_dirs)); + include_dirs[0] = "."; /* Current dir. */ + include_dir_count = 2; + } + else + { + include_dir_count++; + include_dirs = + (char **) realloc (include_dirs, + include_dir_count * sizeof (*include_dirs)); + } + + include_dirs[include_dir_count - 1] = path; /* New one. */ + + i = strlen (path); + if (i > include_dir_maxlen) + include_dir_maxlen = i; +} + +/* Output debugging information to denote the source file. */ + +static void +generate_file_debug (void) +{ + if (debug_type == DEBUG_STABS) + stabs_generate_asm_file (); +} + +/* Output line number debugging information for the current source line. */ + +void +generate_lineno_debug (void) +{ + switch (debug_type) + { + case DEBUG_UNSPECIFIED: + case DEBUG_NONE: + case DEBUG_DWARF: + break; + case DEBUG_STABS: + stabs_generate_asm_lineno (); + break; + case DEBUG_ECOFF: + ecoff_generate_asm_lineno (); + break; + case DEBUG_DWARF2: + /* ??? We could here indicate to dwarf2dbg.c that something + has changed. However, since there is additional backend + support that is required (calling dwarf2_emit_insn), we + let dwarf2dbg.c call as_where on its own. */ + break; + } +} + +/* Output debugging information to mark a function entry point or end point. + END_P is zero for .func, and non-zero for .endfunc. */ + +void +s_func (int end_p) +{ + do_s_func (end_p, NULL); +} + +/* Subroutine of s_func so targets can choose a different default prefix. + If DEFAULT_PREFIX is NULL, use the target's "leading char". */ + +static void +do_s_func (int end_p, const char *default_prefix) +{ + /* Record the current function so that we can issue an error message for + misplaced .func,.endfunc, and also so that .endfunc needs no + arguments. */ + static char *current_name; + static char *current_label; + + if (end_p) + { + if (current_name == NULL) + { + as_bad (_("missing .func")); + ignore_rest_of_line (); + return; + } + + if (debug_type == DEBUG_STABS) + stabs_generate_asm_endfunc (current_name, current_label); + + current_name = current_label = NULL; + } + else /* ! end_p */ + { + char *name, *label; + char delim1, delim2; + + if (current_name != NULL) + { + as_bad (_(".endfunc missing for previous .func")); + ignore_rest_of_line (); + return; + } + + name = input_line_pointer; + delim1 = get_symbol_end (); + name = xstrdup (name); + *input_line_pointer = delim1; + SKIP_WHITESPACE (); + if (*input_line_pointer != ',') + { + if (default_prefix) + asprintf (&label, "%s%s", default_prefix, name); + else + { + char leading_char = 0; +#ifdef BFD_ASSEMBLER + leading_char = bfd_get_symbol_leading_char (stdoutput); +#endif + /* Missing entry point, use function's name with the leading + char prepended. */ + if (leading_char) + asprintf (&label, "%c%s", leading_char, name); + else + label = name; + } + } + else + { + ++input_line_pointer; + SKIP_WHITESPACE (); + label = input_line_pointer; + delim2 = get_symbol_end (); + label = xstrdup (label); + *input_line_pointer = delim2; + } + + if (debug_type == DEBUG_STABS) + stabs_generate_asm_func (name, label); + + current_name = name; + current_label = label; + } + + demand_empty_rest_of_line (); +} + +void +s_ignore (int arg ATTRIBUTE_UNUSED) +{ + ignore_rest_of_line (); +} + +void +read_print_statistics (FILE *file) +{ + hash_print_statistics (file, "pseudo-op table", po_hash); +} + +/* Inserts the given line into the input stream. + + This call avoids macro/conditionals nesting checking, since the contents of + the line are assumed to replace the contents of a line already scanned. + + An appropriate use of this function would be substitution of input lines when + called by md_start_line_hook(). The given line is assumed to already be + properly scrubbed. */ + +void +input_scrub_insert_line (const char *line) +{ + sb newline; + sb_new (&newline); + sb_add_string (&newline, line); + input_scrub_include_sb (&newline, input_line_pointer, 0); + sb_kill (&newline); + buffer_limit = input_scrub_next_buffer (&input_line_pointer); +} + +/* Insert a file into the input stream; the path must resolve to an actual + file; no include path searching or dependency registering is performed. */ + +void +input_scrub_insert_file (char *path) +{ + input_scrub_include_file (path, input_line_pointer); + buffer_limit = input_scrub_next_buffer (&input_line_pointer); +} + +/* Find the end of a line, considering quotation and escaping of quotes. */ + +#if !defined(TC_SINGLE_QUOTE_STRINGS) && defined(SINGLE_QUOTE_STRINGS) +# define TC_SINGLE_QUOTE_STRINGS 1 +#endif + +static char * +_find_end_of_line (char *s, int mri_string, int insn ATTRIBUTE_UNUSED) +{ + char inquote = '\0'; + int inescape = 0; + + while (!is_end_of_line[(unsigned char) *s] + || (inquote && !ISCNTRL (*s)) + || (inquote == '\'' && flag_mri) +#ifdef TC_EOL_IN_INSN + || (insn && TC_EOL_IN_INSN (s)) +#endif + ) + { + if (mri_string && *s == '\'') + inquote ^= *s; + else if (inescape) + inescape = 0; + else if (*s == '\\') + inescape = 1; + else if (!inquote + ? *s == '"' +#ifdef TC_SINGLE_QUOTE_STRINGS + || (TC_SINGLE_QUOTE_STRINGS && *s == '\'') +#endif + : *s == inquote) + inquote ^= *s; + ++s; + } + if (inquote) + as_warn (_("missing closing `%c'"), inquote); + if (inescape) + as_warn (_("stray `\\'")); + return s; +} + +char * +find_end_of_line (char *s, int mri_string) +{ + return _find_end_of_line (s, mri_string, 0); +}