--- binutils-2.17.50.0.13/gas/Makefile.am.orig 2007-03-16 16:48:29.000000000 +0100 +++ binutils-2.17.50.0.13/gas/Makefile.am 2007-03-16 20:26:20.200480821 +0100 @@ -199,7 +199,7 @@ symbols.c \ write.c -CFILES = $(GAS_CFILES) itbl-ops.c cgen.c +CFILES = $(GAS_CFILES) gasp.c itbl-ops.c cgen.c HFILES = \ as.h \ @@ -455,7 +455,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 @@ -517,6 +518,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= @@ -682,6 +682,9 @@ itbl-lex.o: itbl-lex.c itbl-parse.h $(srcdir)/itbl-lex.h $(COMPILE) -c $< $(NO_WERROR) +gasp.o:gasp.c $(INCDIR)/getopt.h $(INCDIR)/safe-ctype.h \ + sb.h macro.h $(INCDIR)/xregex.h $(INCDIR)/xregex2.h + # Disable -Werror, if it has been enabled, since old versions of bison/ # yacc will produce working code which contain compile time warnings. itbl-parse.o: itbl-parse.c itbl-parse.h $(srcdir)/itbl-ops.h $(srcdir)/itbl-lex.h --- binutils-2.17.50.0.13/gas/doc/Makefile.am.orig 2007-03-16 16:48:29.000000000 +0100 +++ binutils-2.17.50.0.13/gas/doc/Makefile.am 2007-03-16 20:27:05.810679469 +0100 @@ -15,7 +15,7 @@ man_MANS = as.1 -info_TEXINFOS = as.texinfo +info_TEXINFOS = as.texinfo gasp.texinfo as_TEXINFOS = asconfig.texi $(CPU_DOCS) AM_MAKEINFOFLAGS = -I "$(srcdir)" -I "$(top_srcdir)/../libiberty" \ --- 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 --- binutils-2.17.50.0.2/gas/gasp.c.orig 1970-01-01 01:00:00.000000000 +0100 +++ binutils-2.17.50.0.2/gas/gasp.c 2006-06-07 11:22:13.774440000 +0200 @@ -0,0 +1,3999 @@ +/* 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 "bfdver.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); +} + +/* Indicate assertion failure. + Arguments: Filename, line number, optional function name. */ + +void +as_assert (const char *file, int line, const char *fn) +{ + fprintf (stderr, _("Internal error!\n")); + if (fn) + fprintf (stderr, _("Assertion failure in %s at %s line %d.\n"), + fn, file, line); + else + fprintf (stderr, _("Assertion failure at %s line %d.\n"), file, line); + fprintf (stderr, _("Please report this bug.\n")); + xexit (EXIT_FAILURE); +} + +#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) {} --- 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 @@ -754,7 +754,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; @@ -786,7 +786,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++; @@ -867,7 +874,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 (); @@ -903,6 +910,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] == '\'')) { @@ -984,7 +1002,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; @@ -1029,7 +1047,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; @@ -1156,7 +1174,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. */ @@ -1189,7 +1207,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; @@ -1224,7 +1242,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); @@ -1285,7 +1303,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; @@ -1318,10 +1336,10 @@ 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 { @@ -1333,7 +1351,7 @@ ++idx; } - while (idx < in->len) + while (idx < in->len && in->ptr[idx] != comment_char) { if (!irpc) idx = get_any_string (idx, in, &f.actual); @@ -1347,7 +1365,7 @@ in_quotes = ! in_quotes; nxt = sb_skip_white (idx + 1, in); - if (nxt >= in->len) + if (nxt >= in->len || in->ptr[nxt] == comment_char) { idx = nxt; break; @@ -1358,7 +1376,7 @@ ++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) --- 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 --- 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_non_macro_line_sb); + err = expand_irp (irpc, 0, &s, &out, get_non_macro_line_sb, '\0'); if (err != NULL) as_bad_where (file, line, "%s", err);