4 * Copyright (C) 1989-2021 Alan R. Baldwin
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
25 * With enhancements from
27 * John L. Hartman (JLH)
28 * jhartman at compuserve dot com
30 * Boisy G. Pitre (BGP)
31 * boisy at boisypitre dot com
34 * mike dot mccarty at sbcglobal dot net
40 #include "dbuf_string.h"
45 * The module asmain.c includes the command argument parser,
46 * the three pass sequencer, and the machine independent
47 * assembler parsing code.
49 * asmain.c contains the following functions:
50 * int main(argc, argv)
53 * VOID equate(id, e1, equtype)
54 * FILE * afile(fn, ft, wf)
61 * asmain.c contains the array char *usetxt[] which
62 * references the usage text strings printed by usage().
66 static const char *search_path
[100];
67 static int search_path_length
;
70 * The search_path_append is used to append another directory to the end
71 * of the include file search path.
74 * The directory to be added to the path.
77 search_path_append(const char *dir
)
79 if (search_path_length
< sizeof(search_path
)/sizeof(char*))
81 search_path
[search_path_length
++] = dir
;
86 * The create_temp_path function is used to build a temporary file path
87 * by concatenating dir and filename. If len >= 0 then only the left
88 * substring of dir with length len is used to build the file path.
91 * The directory part of the path.
93 * < 0: use the whole dir as the directory part of the path.
94 * >= 0: the length of dir to use as the directory part of the path.
96 * The filename to be appended to the directory part of the path.
98 * The constructed path.
101 create_temp_path(const char * dir
, int len
, const char * filename
)
103 static struct dbuf_s dbuf
;
106 if (!dbuf_is_initialized(&dbuf
))
107 dbuf_init (&dbuf
, 1024);
109 dbuf_set_length(&dbuf
, 0);
110 dbuf_append_str(&dbuf
, dir
);
112 dbuf_set_length(&dbuf
, len
);
113 path
= dbuf_c_str(&dbuf
);
114 if ((path
[strlen(path
) - 1] != '/') &&
115 (path
[strlen(path
) - 1] != DIR_SEPARATOR_CHAR
)) {
116 dbuf_append_char(&dbuf
, DIR_SEPARATOR_CHAR
);
118 dbuf_append_str(&dbuf
, filename
);
119 path
= dbuf_c_str(&dbuf
);
124 * The search_path_fopen function is used to open the named file. If
125 * the file isn't in the current directory, the search path is then used
126 * to build a series of possible file names, and attempts to open them.
127 * The first found is used.
130 * The name of the file to be opened.
132 * The mode of the file to be opened.
134 * what the fopen function would return on success, or NULL if the
135 * file is not anywhere in the search path.
138 search_path_fopen(const char *filename
, const char *mode
)
143 fp
= fopen(filename
, mode
);
144 if (fp
!= NULL
|| filename
[0] == '/' || filename
[0] == '\\')
148 * Try the path of the file opening the include file
150 fp
= fopen(create_temp_path(afn
, afp
, filename
), mode
);
154 for (j
= 0; j
< search_path_length
; ++j
) {
155 fp
= fopen(create_temp_path(search_path
[j
], -1, filename
), mode
);
162 /* end sdas specific */
164 /*)Function int main(argc, argv)
166 * int argc argument count
167 * char * argv array of pointers to argument strings
169 * The function main() is the entry point to the assembler.
170 * The purpose of main() is to (1) parse the command line
171 * arguments for options and source file specifications and
172 * (2) to process the source files through the 3 pass assembler.
173 * Before each assembler pass various variables are initialized
174 * and source files are rewound to their beginning. During each
175 * assembler pass each assembler-source text line is processed.
176 * After each assembler pass the assembler information is flushed
177 * to any opened output files and the if-else-endif processing
178 * is checked for proper termination.
180 * The function main() is also responsible for opening all
181 * output files (REL, LST, and SYM), sequencing the global (-g)
182 * and all-global (-a) variable definitions, and dumping the
183 * REL file header information.
186 * char * p pointer to argument string
187 * int c character from argument string
188 * int i argument loop counter
189 * area * ap pointer to area structure
190 * def * dp pointer to def structure
193 * int aflag -a, make all symbols global flag
194 * char afn[] afile() constructed filespec
195 * int afp afile constructed path length
196 * area * areap pointer to an area structure
197 * asmf * asmc pointer to current assembler file structure
198 * int asmline assembler source file line number
199 * asmf * asmp pointer to first assembler file structure
200 * int aserr assembler error counter
201 * int bflag -b(b), listing mode flag
202 * int cb[] array of assembler output values
203 * int cbt[] array of assembler relocation types
204 * describing the data in cb[]
205 * int * cp pointer to assembler output array cb[]
206 * int * cpt pointer to assembler relocation type
208 * time_t curtim current time string pointer
209 * def * defp pointer to a def structure
210 * char eb[] array of generated error codes
211 * char * ep pointer into error list array eb[]
212 * int fflag -f(f), relocations flagged flag
213 * int flevel IF-ELSE-ENDIF flag will be non
214 * zero for false conditional case
215 * a_uint fuzz tracks pass to pass changes in the
216 * address of symbols caused by
217 * variable length instruction formats
218 * int gflag -g, make undefined symbols global flag
219 * char * ib string buffer containing
220 * assembler-source text line for processing
221 * char * ic string buffer containing
222 * assembler-source text line for listing
223 * int ifcnd[] array of IF statement condition
224 * values (0 = FALSE) indexed by tlevel
225 * int iflvl[] array of IF-ELSE-ENDIF flevel
226 * values indexed by tlevel
227 * int incfil current include file count
228 * int incline include source file line number
229 * char * ip pointer into the assembler-source
231 * jmp_buf jump_env compiler dependent structure
232 * used by setjmp() and longjmp()
233 * int lflag -l, generate listing flag
234 * int line current assembler source
236 * int lnlist current LIST-NLIST state
237 * int lop current line number on page
238 * int maxinc maximum include file nesting counter
239 * int nflag -n, don't resolve global assigned value symbols flag
240 * int oflag -o, generate relocatable output flag
241 * int jflag -j, generate debug info flag
242 * int page current page number
243 * int pflag disable listing pagination
244 * int pass assembler pass number
245 * int radix current number conversion radix:
246 * 2 (binary), 8 (octal), 10 (decimal),
248 * int sflag -s, generate symbol table flag
249 * int srcline current source line number
250 * char stb[] Subtitle string buffer
251 * sym * symp pointer to a symbol structure
252 * int tlevel current conditional level
253 * int uflag -u, disable .list/.nlist processing
254 * int wflag -w, enable wide listing format
255 * int xflag -x, listing radix flag
256 * int zflag -z, disable symbol case sensitivity
257 * FILE * lfp list output file handle
258 * FILE * ofp relocation output file handle
259 * FILE * tfp symbol table output file handle
262 * FILE * afile() asmain.c
263 * VOID allglob() assym.c
264 * VOID asexit() asmain.c
265 * VOID diag() assubr.c
266 * VOID err() assubr.c
267 * VOID exprmasks() asexpr.c
268 * int fprintf() c_library
269 * int int32siz() asmain.c
270 * VOID list() aslist.c
271 * VOID lstsym() aslist.c
272 * VOID mcrinit() asmcro.c
273 * VOID minit() ___mch.c
274 * char * new() assym.c
275 * VOID newdot() asmain.c
276 * int nxtline() aslex.c
277 * VOID outbuf() asout.c
278 * VOID outchk() asout.c
279 * VOID outgsd() asout.c
280 * int rewind() c_library
281 * int setjmp() c_library
282 * char * strcpy() c_library
283 * VOID symglob() assym.c
284 * VOID syminit() assym.c
285 * time_t time() c_library
286 * VOID usage() asmain.c
289 * Completion of main() completes the assembly process.
290 * REL, LST, and/or SYM files may be generated.
294 char relFile
[FILSPC
];
295 /* end sdas specific */
298 main(int argc
, char *argv
[])
307 /* sdas initialization */
309 /* end sdas specific */
317 fprintf(stderr
, "?ASxxxx-Error-Size of INT32 is not 32 bits or larger.\n\n");
322 fprintf(stdout
, "\n");
326 for (i
=1; i
<argc
; ++i
) {
331 fprintf(stderr
, "?ASxxxx-Error-Options come first.\n");
335 while ((c
= *p
++) != 0) {
338 * -h Show the help list
347 /* TODO: collides with new Insert assembler line */
349 search_path_append(p
);
356 * -o Create object file/outfile[.rel]
364 * Create Listing Output
372 * Create Symbol Output
383 * -x Hex listing (default)
384 * -b Display .define substitutions in listing
385 * -bb and display without .define substitutions
386 * -c Disable instruction cycle count in listing
387 * -f Flag relocatable references by ` in listing file
388 * -ff Flag relocatable references by mode in listing file
389 * -k Disable error output to listing file
390 * -p Disable automatic listing pagination
391 * -u Disable .list/.nlist processing
392 * -w Wide listing format for symbol table
416 ++cflag
; /* Cycle counts in listing */
426 // ++kflag; /* not yet implemented */
445 * Assembly Processing Options:",
446 * -v Enable out of range signed / unsigned errors
455 * -a All user symbols made global
456 * -g Undefined symbols made global
457 * -z Disable case sensitivity for symbols
470 /* TODO: collides with new Specify number of assembler scanning passes */
482 * -j Enable NoICE Debug Symbols
483 * -y Enable SDCC Debug Symbols
486 case 'j': /* NoICE Debug JLH */
489 ++oflag
; /* force object */
494 case 'y': /* SDCC Debug */
502 * -r list line numbers in .hst help file
503 * -rr list line numbers of NON listed lines
504 * -t show internal block allocations
508 // ++rflag; /* not yet implemented */
520 fprintf(stderr
, "?ASxxxx-Warning-Unkown option -%c ignored\n", c
);
532 fprintf(stderr
, "?ASxxxx-Error-Options come first.\n");
536 asmp
= (struct asmf
*)
537 new (sizeof (struct asmf
));
540 asmc
->next
= (struct asmf
*)
541 new (sizeof (struct asmf
));
545 asmc
->objtyp
= T_ASM
;
549 asmc
->lnlist
= LIST_NORM
;
550 asmc
->fp
= afile(p
, "", 0);
551 strcpy(asmc
->afn
,afn
);
557 fprintf(stderr
, "?ASxxxx-Error-Missing input file(s)\n");
561 lfp
= afile(q
, "lst", 1);
564 ofp
= afile(q
, (is_sdas() && p
!= q
) ? "" : "rel", 1);
565 // save the file name if we have to delete it on error
568 /* end sdas specific */
570 tfp
= afile(q
, "sym", 1);
574 for (pass
=0; pass
<3; ++pass
) {
576 if (gflag
&& pass
== 1)
578 if (aflag
&& pass
== 1)
580 if (oflag
&& pass
== 2)
596 /* end sdas specific */
605 strcpy(afn
, asmc
->afn
);
633 /* JLH: if line begins with ";!", then
634 * pass this comment on to the output file
636 if (oflag
&& (pass
== 1) && (ip
[0] == ';') && (ip
[1] == '!')) {
637 fprintf(ofp
, "%s\n", ip
);
640 opcycles
= OPCY_NONE
;
641 if (setjmp(jump_env
) == 0)
648 newdot(dot
.s_area
); /* Flush area info */
650 if (flevel
|| tlevel
) {
652 fprintf(stderr
, "?ASxxxx-Error-<i> at end of assembly\n");
653 fprintf(stderr
, " %s\n", geterr('i'));
656 outchk(ASXHUGE
, ASXHUGE
); /* Flush */
663 asexit(aserr
? ER_ERROR
: ER_NONE
);
667 /*)Function int intsiz()
669 * The function intsiz() returns the size of INT32
687 return(sizeof(INT32
));
690 /*)Function VOID asexit(i)
694 * The function asexit() explicitly closes all open
695 * files and then terminates the program.
701 * asmf * asmc pointer to current assembler file structure
702 * asmf * asmp pointer to first assembler file structure
703 * FILE * lfp list output file handle
704 * FILE * ofp relocation output file handle
705 * FILE * tfp symbol table output file handle
706 * FILE * stdout standard output handle
707 * int maxinc maximum include file level
708 * int maxmcr maximum macro expansion level
709 * int mcrblk macro allocation in 1K blocks
712 * int fclose() c_library
713 * int fprintf() c_library
714 * VOID exit() c_library
717 * All files closed. Program terminates.
723 if (lfp
!= NULL
) fclose(lfp
);
724 if (ofp
!= NULL
) fclose(ofp
);
725 if (tfp
!= NULL
) fclose(tfp
);
727 while (asmc
!= NULL
) {
728 if ((asmc
->objtyp
== T_INCL
) && (asmc
->fp
!= NULL
)) {
735 while (asmc
!= NULL
) {
736 if ((asmc
->objtyp
== T_ASM
) && (asmc
->fp
!= NULL
)) {
743 /* remove output file */
744 printf ("removing %s\n", relFile
);
747 /* end sdas specific */
750 fprintf(stderr
, "?ASxxxx-Info-maxinc(include file level) = %3d\n", maxinc
);
751 fprintf(stderr
, "?ASxxxx-Info-maxmcr(macro expansion level) = %3d\n", maxmcr
);
752 //fprintf(stderr, "?ASxxxx-Info-asmblk(1K Byte Allocations) = %3d\n", asmblk);
753 fprintf(stderr
, "?ASxxxx-Info-mcrblk(1K Byte Allocations) = %3d\n", mcrblk
);
754 fprintf(stderr
, "\n");
760 /*)Function VOID asmbl()
762 * The function asmbl() scans the assembler-source text for
763 * (1) labels, global labels, equates, global equates, and local
764 * symbols, (2) .if, .else, .endif, and .page directives,
765 * (3) machine independent assembler directives, (4) macros and
766 * macro definitions, and (5) machine dependent mnemonics.
769 * mne * mp pointer to a mne structure
770 * mne * xp pointer to a mne structure
771 * mcrdef *np pointer to a macro definition structure
772 * sym * sp pointer to a sym structure
773 * tsym * tp pointer to a tsym structure
774 * int c character from assembler-source
776 * area * ap pointer to an area structure
777 * def * dp pointer to a definition structure
778 * expr e1 expression structure
779 * char id[] id string
780 * char opt[] options string
781 * char fn[] filename string
782 * char * p pointer into a string
783 * int d temporary value
784 * int uaf user area options flag
785 * int uf area options
786 * a_uint n temporary value
787 * a_uint v temporary value
788 * int flags temporary flag
789 * FILE * fp include file handle
790 * int m_type mnemonic type
793 * area * areap pointer to an area structure
794 * char ctype[] array of character types, one per
796 * int flevel IF-ELSE-ENDIF flag will be non
797 * zero for false conditional case
798 * int ftflevel; IIFF-IIFT-IIFTF FLAG
799 * int lnlist current LIST-NLIST state
800 * a_uint fuzz tracks pass to pass changes in the
801 * address of symbols caused by
802 * variable length instruction formats
803 * int ifcnd[] array of IF statement condition
804 * values (0 = FALSE) indexed by tlevel
805 * int iflvl[] array of IF-ELSE-ENDIF flevel
806 * values indexed by tlevel
807 * int incline current include file line
808 * int incfil current include file count
809 * a_uint laddr address of current assembler line
810 * or value of .if argument
811 * int lmode listing mode
812 * int lop current line number on page
813 * char module[] module name string
814 * int pass assembler pass number
815 * int radix current number conversion radix:
816 * 2 (binary), 8 (octal), 10 (decimal),
818 * char stb[] Subtitle string buffer
819 * sym * symp pointer to a symbol structure
820 * char tb[] Title string buffer
821 * int tlevel current conditional level
822 * int jflag -j, generate debug info flag
825 * a_uint absexpr() asexpr.c
826 * area * alookup() assym.c
827 * def * dlookup() assym.c
828 * VOID clrexpr() asexpr.c
829 * int digit() asexpr.c
830 * char endline() aslex.c
831 * VOID equate() asmain.c
832 * VOID err() assubr.c
833 * VOID expr() asexpr.c
834 * FILE * search_path_fopen() asmain.c
835 * int fseek() c_library
837 * VOID getid() aslex.c
838 * int getmap() aslex.c
839 * int getnb() aslex.c
840 * VOID getst() aslex.c
841 * VOID getxstr() asmcro.c
842 * sym * lookup() assym.c
843 * VOID machine() ___mch.c
844 * int macro() asmcro.c
845 * int mcrprc() asmcro.c
846 * mne * mlookup() assym.c
848 * mcrdef *nlookup() asmcro.c
849 * char * new() assym.c
850 * VOID newdot() asmain.c
851 * VOID outall() asout.c
852 * VOID outab() asout.c
853 * VOID outchk() asout.c
854 * VOID outrb() asout.c
855 * VOID outrw() asout.c
856 * VOID phase() asmain.c
857 * VOID qerr() assubr.c
858 * char * strcpy() c_library
859 * char * strncpy() c_library
860 * char * strsto() assym.c
861 * VOID unget() aslex.c
880 char fn
[FILSPC
+FILSPC
];
888 static struct area
*abs_ap
; /* pointer to current absolute area structure */
889 /* end sdas specific */
895 * Check iiff-iift-iiftf processing
898 flevel
= ftflevel
- 1;
903 * Check if Building a Macro
904 * Check if Exiting a Macro
906 if (mcrprc(O_CHECK
) != 0) {
911 if ((c
=endline()) == 0) { return; }
914 * If the first character is a digit then assume
915 * a reusable symbol is being specified. The symbol
916 * must end with $: to be valid.
918 * Construct a tsym structure at the first
919 * occurance of the symbol. Flag the symbol
920 * as multiply defined if not the first occurance.
922 * Load area, address, and fuzz values
923 * into structure tsym.
925 * Check for assembler phase error and
926 * multiply defined error.
928 if (ctype
[c
] & DIGIT
) {
932 while ((d
= digit(c
, 10)) >= 0) {
936 if (c
!= '$' || get() != ':') {
941 if (n
== tp
->t_num
) {
950 tp
=(struct tsym
*) new (sizeof(struct tsym
));
951 tp
->t_area
= dot
.s_area
;
952 tp
->t_addr
= dot
.s_addr
;
953 tp
->t_lnk
= symp
->s_tsym
;
958 if (tp
->t_flg
& S_MDF
)
960 phase(tp
->t_area
, tp
->t_addr
);
961 fuzz
= tp
->t_addr
- dot
.s_addr
;
962 tp
->t_area
= dot
.s_area
;
963 tp
->t_addr
= dot
.s_addr
;
968 * If the first character is a letter then assume a label,
969 * symbol, assembler directive, or assembler mnemonic is
972 if ((ctype
[c
] & LETTER
) == 0) {
980 /* Skip white space to next character */
983 * If the next character is a : then a label is being processed.
984 * A double :: defines a global label. If this is a new label
985 * then create a symbol structure.
987 * Flag multiply defined labels.
989 * Load area, address, and fuzz values
990 * into structure symp.
992 * Check for assembler phase error and
993 * multiply defined error.
998 if ((c
= get()) != ':') {
1006 if ((symp
->s_type
!= S_NEW
) && ((symp
->s_flag
& S_ASG
) == 0))
1007 symp
->s_flag
|= S_MDF
;
1009 if (symp
->s_flag
& S_MDF
)
1011 symp
->s_type
= S_USER
;
1012 phase(symp
->s_area
, symp
->s_addr
);
1013 fuzz
= symp
->s_addr
- dot
.s_addr
;
1014 symp
->s_area
= dot
.s_area
;
1015 symp
->s_addr
= dot
.s_addr
;
1017 symp
->s_flag
|= S_GBL
;
1023 * If the next character is a = then an equate is being processed.
1026 * [labels] sym = value defines an equate.
1027 * [labels] sym == value defines a global equate.
1028 * [labels] sym =: value defines an internal machine equate.
1029 * If this is a new variable then create a symbol structure.
1034 switch (c
= get()) {
1035 case '=': equtype
= O_GBLEQU
; break;
1036 case ':': equtype
= O_LCLEQU
; break;
1037 default: equtype
= O_EQU
; unget(c
); break;
1039 equate(id
, &e1
, equtype
);
1044 * Check for Equates if 'id' is not an Assembler Directive
1047 * [labels] sym .equ value defines an equate
1048 * [labels] sym .glbequ value defines a global equate
1049 * [labels] sym .lclequ value defines a local equate
1051 if ((mlookup(id
) == NULL
) && (nlookup(id
) == NULL
)) {
1055 * Alternates for =, ==, and =:
1057 equ_ip
= ip
; /* Save current char pointer for case equate not found. */
1058 if (more() && (ctype
[c
= getnb()] == LETTER
)) {
1060 if ((mp
= mlookup(equ
)) == NULL
|| mp
->m_type
!= S_EQU
) {
1063 equate(id
, &e1
, mp
->m_valu
);
1069 * Completed scan for labels , equates, and symbols.
1071 lmode
= flevel
? SLIST
: CLIST
;
1073 * An assembler directive, mnemonic, or macro is
1074 * required to continue processing line.
1078 if ((mp
== NULL
) && (np
== NULL
)) {
1085 * If we have gotten this far then we have found an
1086 * assembler directive an assembler mnemonic or
1087 * an assembler macro.
1089 * Check for .if[], .iif[], .else, .endif,
1090 * .list, .nlist, and .page directives.
1092 m_type
= (mp
!= NULL
) ? mp
->m_type
: ~0;
1098 * BGP - .ifeq, .ifne, .ifgt, .iflt, .ifge, .ifle
1100 if (mp
->m_valu
< O_IFEND
) {
1101 if (mp
->m_valu
== O_IF
) {
1103 * Process conditionals of the form
1105 * .if cnd(,) arg1 (, arg2)
1107 * where cnd is one of the following:
1117 getid(&id
[3],getnb());
1120 (xp
->m_type
== S_CONDITIONAL
) &&
1121 (xp
->m_valu
!= O_IF
)) {
1131 switch (mp
->m_valu
) {
1133 case O_IFNE
: /* .if ne,.... */
1134 case O_IFEQ
: /* .if eq,.... */
1135 case O_IFGT
: /* .if gt,.... */
1136 case O_IFLT
: /* .if lt,.... */
1137 case O_IFGE
: /* .if ge,.... */
1138 case O_IFLE
: /* .if le,.... */
1140 switch (mp
->m_valu
) {
1143 case O_IFNE
: n
= (((v_sint
) n
) != 0); break;
1144 case O_IFEQ
: n
= (((v_sint
) n
) == 0); break;
1145 case O_IFGT
: n
= (((v_sint
) n
) > 0); break;
1146 case O_IFLT
: n
= (((v_sint
) n
) < 0); break;
1147 case O_IFGE
: n
= (((v_sint
) n
) >= 0); break;
1148 case O_IFLE
: n
= (((v_sint
) n
) <= 0); break;
1152 case O_IFB
: /* .if b,.... */
1153 case O_IFNB
: /* .if nb,.... */
1154 n
= comma(0) ? 0 : 1;
1162 switch (mp
->m_valu
) {
1164 case O_IFB
: n
= (((v_sint
) n
) == 0); break;
1165 case O_IFNB
: n
= (((v_sint
) n
) != 0); break;
1169 case O_IFDEF
: /* .if def,.... */
1170 case O_IFNDEF
: /* .if ndef,.... */
1172 if (((dp
= dlookup(id
)) != NULL
) && (dp
->d_dflag
!= 0)) {
1175 if ((sp
= slookup(id
)) != NULL
) {
1176 n
= (sp
->s_type
== S_USER
) ? 1 : 0;
1180 switch (mp
->m_valu
) {
1182 case O_IFDEF
: n
= (((v_sint
) n
) != 0); break;
1183 case O_IFNDEF
: n
= (((v_sint
) n
) == 0); break;
1187 case O_IFIDN
: /* .if idn,.... */
1188 case O_IFDIF
: /* .if dif,.... */
1192 n
= symeq(id
, equ
, zflag
);
1193 switch (mp
->m_valu
) {
1195 case O_IFIDN
: n
= (((v_sint
) n
) != 0); break;
1196 case O_IFDIF
: n
= (((v_sint
) n
) == 0); break;
1200 case O_IFF
: /* .if f */
1201 case O_IFT
: /* .if t */
1202 case O_IFTF
: /* .if tf */
1212 switch (mp
->m_valu
) {
1214 if (tlevel
< MAXIF
) {
1216 ifcnd
[tlevel
] = (int) n
;
1217 iflvl
[tlevel
] = flevel
;
1224 if (!iflvl
[tlevel
]) {
1232 case O_IFF
: /* .if f */
1233 case O_IFT
: /* .if t */
1234 case O_IFTF
: /* .if tf */
1240 if (iflvl
[tlevel
] == 0) {
1241 if (ifcnd
[tlevel
]) {
1242 switch (mp
->m_valu
) {
1244 case O_IFF
: flevel
= 1; break;
1245 case O_IFT
: flevel
= 0; break;
1246 case O_IFTF
: flevel
= 0; break;
1249 switch (mp
->m_valu
) {
1251 case O_IFF
: flevel
= 0; break;
1252 case O_IFT
: flevel
= 1; break;
1253 case O_IFTF
: flevel
= 0; break;
1257 laddr
= flevel
? 0 : 1;
1265 if (mp
->m_valu
< O_IIFEND
) {
1266 if (mp
->m_valu
== O_IIF
) {
1268 * Process conditionals of the form
1270 * .iif cnd(,) arg1 (, arg2)
1272 * where cnd is one of the following:
1282 getid(&id
[4],getnb());
1285 (xp
->m_type
== S_CONDITIONAL
) &&
1286 (xp
->m_valu
!= O_IIF
)) {
1293 switch (mp
->m_valu
) {
1294 case O_IIFF
: /* .iif f */
1295 case O_IIFT
: /* .iif t */
1296 case O_IIFTF
: /* .iif tf */
1302 if (iflvl
[tlevel
] == 0) {
1303 ftflevel
= flevel
+ 1;
1304 if (ifcnd
[tlevel
] != 0) {
1305 switch (mp
->m_valu
) {
1307 case O_IIFF
: flevel
= 1; break;
1308 case O_IIFT
: flevel
= 0; break;
1309 case O_IIFTF
: flevel
= 0; break;
1312 switch (mp
->m_valu
) {
1314 case O_IIFF
: flevel
= 0; break;
1315 case O_IIFT
: flevel
= 1; break;
1316 case O_IIFTF
: flevel
= 0; break;
1337 switch (mp
->m_valu
) {
1339 case O_IIFNE
: /* .iif ne,.... */
1340 case O_IIFEQ
: /* .iif eq,.... */
1341 case O_IIFGT
: /* .iif gt,.... */
1342 case O_IIFLT
: /* .iif lt,.... */
1343 case O_IIFGE
: /* .iif ge,.... */
1344 case O_IIFLE
: /* .iif le,.... */
1346 switch (mp
->m_valu
) {
1349 case O_IIFNE
: n
= (((v_sint
) n
) != 0); break;
1350 case O_IIFEQ
: n
= (((v_sint
) n
) == 0); break;
1351 case O_IIFGT
: n
= (((v_sint
) n
) > 0); break;
1352 case O_IIFLT
: n
= (((v_sint
) n
) < 0); break;
1353 case O_IIFGE
: n
= (((v_sint
) n
) >= 0); break;
1354 case O_IIFLE
: n
= (((v_sint
) n
) <= 0); break;
1358 case O_IIFB
: /* .iif b,.... */
1359 case O_IIFNB
: /* .iif nb,.... */
1360 n
= comma(0) ? 0 : 1;
1368 switch (mp
->m_valu
) {
1370 case O_IIFB
: n
= (((v_sint
) n
) == 0); break;
1371 case O_IIFNB
: n
= (((v_sint
) n
) != 0); break;
1375 case O_IIFDEF
: /* .iif def,.... */
1376 case O_IIFNDEF
: /* .iif ndef,.... */
1378 if (((dp
= dlookup(id
)) != NULL
) && (dp
->d_dflag
!= 0)) {
1381 if ((sp
= slookup(id
)) != NULL
) {
1382 n
= (sp
->s_type
== S_USER
) ? 1 : 0;
1386 switch (mp
->m_valu
) {
1388 case O_IIFDEF
: n
= (((v_sint
) n
) != 0); break;
1389 case O_IIFNDEF
: n
= (((v_sint
) n
) == 0); break;
1393 case O_IIFIDN
: /* .iif idn,.... */
1394 case O_IIFDIF
: /* .iif dif,.... */
1398 n
= symeq(id
, equ
, zflag
);
1399 switch (mp
->m_valu
) {
1401 case O_IIFIDN
: n
= (((v_sint
) n
) != 0); break;
1402 case O_IIFDIF
: n
= (((v_sint
) n
) == 0); break;
1422 switch (mp
->m_valu
) {
1425 if (ifcnd
[tlevel
]) {
1426 flevel
= iflvl
[tlevel
] + 1;
1429 flevel
= iflvl
[tlevel
];
1432 if (!iflvl
[tlevel
]) {
1434 laddr
= ifcnd
[tlevel
];
1445 flevel
= iflvl
[tlevel
--];
1460 while ((c
=endline()) != 0) {
1466 if ((c
= getnb()) == '!') {
1471 if (symeq(id
, "err", 1)) { flags
|= LIST_ERR
; } else
1472 if (symeq(id
, "loc", 1)) { flags
|= LIST_LOC
; } else
1473 if (symeq(id
, "bin", 1)) { flags
|= LIST_BIN
; } else
1474 if (symeq(id
, "eqt", 1)) { flags
|= LIST_EQT
; } else
1475 if (symeq(id
, "cyc", 1)) { flags
|= LIST_CYC
; } else
1476 if (symeq(id
, "lin", 1)) { flags
|= LIST_LIN
; } else
1477 if (symeq(id
, "src", 1)) { flags
|= LIST_SRC
; } else
1478 if (symeq(id
, "pag", 1)) { flags
|= LIST_PAG
; } else
1479 if (symeq(id
, "lst", 1)) { flags
|= LIST_LST
; } else
1480 if (symeq(id
, "md" , 1)) { flags
|= LIST_MD
; } else
1481 if (symeq(id
, "me" , 1)) { flags
|= LIST_ME
; } else
1482 if (symeq(id
, "meb", 1)) { flags
|= LIST_MEB
; } else {
1487 } while (c
== ',') ;
1496 flags
&= ~LIST_TORF
;
1500 if (!(flags
& LIST_TORF
) && flevel
) {
1503 if (flags
& ~LIST_TORF
) {
1504 if (flags
& LIST_NOT
) {
1505 switch(mp
->m_valu
) {
1506 case O_LIST
: lnlist
= LIST_NONE
; break;
1507 case O_NLIST
: lnlist
= LIST_NORM
; break;
1511 if (flags
& LIST_BITS
) {
1512 switch(mp
->m_valu
) {
1513 case O_LIST
: lnlist
|= (flags
& LIST_BITS
); break;
1514 case O_NLIST
: lnlist
&= ~(flags
& LIST_BITS
); break;
1519 switch(mp
->m_valu
) {
1520 case O_LIST
: lnlist
= LIST_NORM
; break;
1521 case O_NLIST
: lnlist
= LIST_NONE
; break;
1525 lmode
= (lnlist
& LIST_LST
) ? SLIST
: NLIST
;
1531 n
= absexpr() ? 1 : 0;
1547 * If we are not in a false state for .if/.else then
1548 * process the assembler directives here.
1553 switch(mp
->m_valu
) {
1556 if ((c
= getnb()) != 0) {
1558 if (p
< &tb
[NTITL
-1])
1560 } while ((c
= get()) != 0);
1569 if ((c
= getnb()) != 0) {
1571 if (p
< &stb
[NSBTL
-1])
1573 } while ((c
= get()) != 0);
1586 getst(id
, getnb()); // a module can start with a digit
1591 strncpy(module
, id
, NCPS
);
1598 switch(mp
->m_valu
) {
1601 if (incfil
> maxinc
) {
1605 * Copy the .include file specification
1607 getdstr(fn
, FILSPC
+ FILSPC
);
1611 if ((fp
= search_path_fopen(fn
, "r")) == NULL
) {
1615 asmi
= (struct asmf
*) new (sizeof (struct asmf
));
1617 asmi
->objtyp
= T_INCL
;
1618 asmi
->line
= srcline
;
1619 asmi
->flevel
= flevel
;
1620 asmi
->tlevel
= tlevel
;
1621 asmi
->lnlist
= lnlist
;
1624 strcpy(asmi
->afn
,afntmp
);
1625 if (lnlist
& LIST_PAG
) {
1633 * Copy the .incbin file specification
1635 getdstr(fn
, FILSPC
+ FILSPC
);
1636 /* ported from ASXXXX 5.40 */
1642 if (more() && !comma(0))
1643 skp
= (int) absexpr();
1654 cnt
= (int) absexpr();
1658 if ((fp
= search_path_fopen(fn
, "rb")) == NULL
) {
1659 xerr('i', "File not found.");
1665 fseek(fp
, skp
, SEEK_SET
);
1666 if (fread(&c
, 1, 1, fp
) != 1) {
1667 xerr('i', "Offset past End-Of-File.");
1670 fseek(fp
, skp
, SEEK_SET
);
1675 if (fread(&c
, 1, 1, fp
) == 1) {
1689 xerr('i', "Internal ___PST.C Error.");
1696 optsdcc
= strsto(ip
);
1698 return; /* line consumed */
1699 /* end sdas specific */
1705 if ((c
= getnb()) == '(') {
1709 if (mp
&& mp
->m_type
== S_ATYP
) {
1716 } while ((c
= getnb()) == ',');
1722 if ((ap
= alookup(id
)) != NULL
) {
1723 if (uaf
&& uf
!= ap
->a_flag
)
1726 ap
= (struct area
*) new (sizeof(struct area
));
1728 ap
->a_id
= strsto(id
);
1729 ap
->a_ref
= areap
->a_ref
+ 1;
1732 /* end sdas specific */
1735 ap
->a_flag
= uaf
? uf
: (A_CON
|A_REL
);
1740 if (dot
.s_area
->a_flag
& A_ABS
)
1745 if ((dot
.s_area
->a_flag
& A_ABS
) == A_ABS
) {
1750 sprintf(buf
, "%s%x", abs_ap
->a_id
, org_cnt
++);
1751 if ((ap
= alookup(buf
)) == NULL
) {
1752 ap
= (struct area
*) new (sizeof(struct area
));
1755 ap
->a_id
= strsto(buf
);
1756 ap
->a_ref
= areap
->a_ref
+ 1;
1762 dot
.s_addr
= dot
.s_org
= laddr
;
1772 switch (ccase
[getnb() & 0x7F]) {
1773 case 'b': radix
= 2; break; /* B */
1776 case 'q': radix
= 8; break; /* Q */
1777 case 'd': radix
= 10; break; /* D */
1779 case 'x': radix
= 16; break; /* X */
1780 default: /* 2, 8, 10, 16 */
1785 if ((v
== 2) || (v
== 8) ||
1786 (v
== 10) || (v
== 16)) {
1805 sp
->s_flag
&= ~S_LCL
;
1806 sp
->s_flag
|= S_GBL
;
1815 sp
->s_flag
&= ~S_GBL
;
1816 sp
->s_flag
|= S_LCL
;
1824 * [labels] .equ sym, value defines an equate
1825 * [labels] .glbequ sym, value defines a global equate
1826 * [labels] .lclequ sym, value defines a local equate
1830 equate(id
, &e1
, mp
->m_valu
);
1834 switch (mp
->m_valu
) {
1840 if (mp
->m_valu
== O_1BYTE
) {
1845 } while ((c
= getnb()) == ',');
1853 /* sdas z80 specific */
1857 unsigned int mantissa
, exponent
;
1858 char readbuffer
[80];
1860 getid(readbuffer
, ' '); /* Hack :) */
1861 if ((c
= getnb()) == '.') {
1862 getid(&readbuffer
[strlen(readbuffer
)], '.');
1867 f1
= strtod(readbuffer
, (char **)NULL
);
1868 /* Convert f1 to a gb-lib type fp
1869 * 24 bit mantissa followed by 7 bit exp and 1 bit sign
1873 f2
= floor(log(fabs(f1
)) / log(2)) + 1;
1874 mantissa
= (unsigned int) ((0x1000000 * fabs(f1
)) / exp(f2
* log(2)));
1875 mantissa
&= 0xffffff;
1876 exponent
= (unsigned int) (f2
+ 0x40) ;
1885 outab(mantissa
& 0xff);
1886 outab((mantissa
>> 8) & 0xff);
1887 outab((mantissa
>> 16) & 0xff);
1888 outab(exponent
& 0xff);
1890 } while ((c
= getnb()) == ',');
1893 /* end sdas z80 specific */
1895 /* sdas hc08 specific */
1899 a_uint val
= absexpr();
1900 int bit
= sizeof(val
)*8 - 1;
1903 if (mp
->m_type
== S_ULEB128
) {
1906 impliedBit
= (val
& (1 << bit
)) ? 1 : 0;
1908 while ((bit
>0) && (((val
& (1 << bit
)) ? 1 : 0) == impliedBit
)) {
1911 if (mp
->m_type
== S_SLEB128
) {
1918 outab(0x80 | (val
& 0x7f));
1923 } while ((c
= getnb()) == ',');
1926 /* end sdas hc08 specific */
1931 outchk(ASXHUGE
,ASXHUGE
);
1932 dot
.s_addr
+= e1
.e_addr
*mp
->m_valu
;
1937 switch(mp
->m_valu
) {
1940 if ((d
= getnb()) == '\0')
1942 while ((c
= getmap(d
)) >= 0)
1944 if (mp
->m_valu
== O_ASCIZ
)
1949 if ((d
= getnb()) == '\0')
1954 if ((n2
= getmap(d
)) >= 0) {
1970 * Extract the .(un)define key word.
1974 switch(mp
->m_valu
) {
1977 * Extract the substitution string
1985 * Verify the definition or
1986 * add a new definition.
1990 if (!symeq(opt
, dp
->d_define
, zflag
)) {
1994 dp
->d_define
= strsto(opt
);
1998 dp
= (struct def
*) new (sizeof(struct def
));
2000 dp
->d_id
= strsto(id
);
2001 dp
->d_define
= strsto(opt
);
2009 * Find and undefine the definition.
2024 switch(mp
->m_valu
) {
2027 laddr
= dot
.s_addr
= (dot
.s_addr
+ 1) & ~1;
2033 laddr
= dot
.s_addr
|= 1;
2041 dot
.s_addr
+= (v
- n
);
2056 * Print the .msg message
2058 getdstr(fn
, FILSPC
+FILSPC
);
2069 if (e1
.e_addr
!= 0) {
2079 mcrprc((int) mp
->m_valu
);
2083 * If not an assembler directive then go to the
2084 * macro function or machine dependent function
2085 * which handles all the assembler mnemonics.
2087 * MACRO Definitions take precedence
2088 * over machine specific mnemonics.
2098 * Include Files and Macros are not Debugged
2100 if (asmc
->objtyp
== T_ASM
) {
2105 * if -j, generate a line number symbol
2107 if (jflag
&& (pass
== 1)) {
2114 * SDCC Debug Information
2115 * if cdb information then generate the line info
2117 if (yflag
&& (pass
== 1)) {
2127 if ((c
= endline()) != 0) {
2136 /*)Function VOID equate(id,e1,equtype)
2138 * char * id ident to equate
2139 * struct expr * e1 value of equate
2140 * a_uint equtype equate type (O_EQU,O_LCLEQU,O_GBLEQU)
2142 * The function equate() installs an equate of a
2145 * equate has no return value
2148 * struct sym * sp symbol being equated
2151 * lmode set to ELIST
2154 * VOID clrexpr() asexpr.c
2155 * VOID expr() asexpr.c
2156 * VOID err() assubr.c
2157 * sym * lookup() assym.c
2158 * VOID outall() asout.c
2159 * VOID rerr() assubr.c
2162 * A new symbol may be created.
2163 * Symbol parameters are updated.
2167 equate(char *id
, struct expr
*e1
, a_uint equtype
)
2178 if (e1
->e_flag
|| e1
->e_base
.e_ap
!= dot
.s_area
)
2187 sp
->s_flag
&= ~S_LCL
;
2188 sp
->s_flag
|= S_GBL
;
2192 sp
->s_flag
&= ~S_GBL
;
2193 sp
->s_flag
|= S_LCL
;
2197 if (e1
->e_flag
&& (e1
->e_base
.e_sp
->s_type
== S_NEW
)) {
2200 sp
->s_area
= e1
->e_base
.e_ap
;
2202 sp
->s_flag
|= S_ASG
;
2203 sp
->s_type
= S_USER
;
2206 sp
->s_addr
= laddr
= e1
->e_addr
;
2210 /*)Function FILE * afile(fn, ft, wf)
2212 * char * fn file specification string
2213 * char * ft file type string
2214 * int wf read(0)/write(1) flag
2216 * The function afile() opens a file for reading or writing.
2218 * afile() returns a file handle for the opened file or aborts
2219 * the assembler on an open error.
2222 * FILE * fp file handle for opened file
2225 * char afn[] afile() constructed filespec
2226 * int afp afile() constructed path length
2227 * char afntmp[] afilex() constructed filespec
2228 * int afptmp afilex() constructed path length
2231 * VOID asexit() asmain.c
2232 * VOID afilex() asmain.c
2233 * FILE * fopen() c_library
2234 * int fprintf() c_library
2235 * char * strcpy() c_library
2238 * File is opened for read or write.
2242 afile(char *fn
, char *ft
, int wf
)
2250 * Select (Binary) Read/Write
2254 case 0: frmt
= "r"; break;
2255 /* open file -- use "b" flag to write LF line endings on all host platforms */
2256 /* this is currently set to match old sdas behavior */
2257 case 1: frmt
= "wb"; break;
2259 case 2: frmt
= "rn"; break;
2260 case 3: frmt
= "wn"; break;
2262 case 2: frmt
= "rb"; break;
2263 case 3: frmt
= "wb"; break;
2267 if ((fp
= fopen(afntmp
, frmt
)) == NULL
) {
2268 fprintf(stderr
, "?ASxxxx-Error-<cannot %s> : \"%s\"\n", (frmt
[0] == 'w')?"create":"open", afntmp
);
2272 strcpy(afn
, afntmp
);
2278 /*)Function VOID afilex(fn, ft)
2280 * char * fn file specification string
2281 * char * ft file type string
2283 * The function afilex() processes the file specification string:
2284 * (1) If the file type specification string ft
2285 * is not NULL then a file specification is
2286 * constructed with the file path\name in fn
2287 * and the extension in ft.
2288 * (2) If the file type specification string ft
2289 * is NULL then the file specification is
2290 * constructed from fn. If fn does not have
2291 * a file type then the default source file
2292 * type dsft is appended to the file specification.
2294 * afilex() aborts the assembler on a file specification length error.
2297 * int c character value
2298 * char * p1 pointer into filespec string afntmp
2299 * char * p2 pointer to filetype string ft
2302 * char afntmp[] afilex() constructed filespec
2303 * int afptmp afilex() constructed path length
2304 * char dsft[] default assembler file type string
2307 * VOID asexit() asmain.c
2308 * int fndidx() asmain.c
2309 * int fprintf() c_library
2310 * char * strcpy() c_library
2311 * int strlen() c_library
2314 * File specification string may be modified.
2318 afilex(char *fn
, char *ft
)
2323 if (strlen(fn
) > (FILSPC
-7)) {
2324 fprintf(stderr
, "?ASxxxx-Error-<filspc to long> : \"%s\"\n", fn
);
2329 * Save the File Name Index
2332 afptmp
= fndidx(afntmp
);
2335 * Skip to File Extension separator
2337 p1
= strrchr(&afntmp
[afptmp
], FSEPX
);
2340 * Copy File Extension
2344 // choose a file-extension
2346 // no extension supplied
2348 // no extension in fn: use default extension
2351 p2
= strrchr(&fn
[afptmp
], FSEPX
) + 1;
2355 p1
= &afntmp
[strlen(afntmp
)];
2358 while ((c
= *p2
++) != 0) {
2359 if (p1
< &afntmp
[FILSPC
-1])
2365 /*)Function int fndidx(str)
2367 * char * str file specification string
2369 * The function fndidx() scans the file specification string
2370 * to find the index to the file name. If the file
2371 * specification contains a 'path' then the index will
2374 * fndidx() returns the index value.
2377 * char * p1 temporary pointer
2378 * char * p2 temporary pointer
2384 * char * strrchr() c_library
2396 * Skip Path Delimiters
2399 if ((p2
= strrchr(p1
, ':')) != NULL
) { p1
= p2
+ 1; }
2400 if ((p2
= strrchr(p1
, '/')) != NULL
) { p1
= p2
+ 1; }
2401 if ((p2
= strrchr(p1
, '\\')) != NULL
) { p1
= p2
+ 1; }
2403 return((int) (p1
- str
));
2406 /*)Function VOID newdot(nap)
2408 * area * nap pointer to the new area structure
2410 * The function newdot():
2411 * (1) copies the current values of fuzz and the last
2412 * address into the current area referenced by dot
2413 * (2) loads dot with the pointer to the new area and
2414 * loads the fuzz and last address parameters
2415 * (3) outall() is called to flush any remaining
2416 * bufferred code from the old area to the output
2419 * area * oap pointer to old area
2422 * sym dot defined as sym[0]
2423 * a_uint fuzz tracks pass to pass changes in the
2424 * address of symbols caused by
2425 * variable length instruction formats
2431 * Current area saved, new area loaded, buffers flushed.
2435 newdot(struct area
*nap
)
2440 /* fprintf (stderr, "%s dot.s_area->a_size: %d dot.s_addr: %d\n",
2441 oap->a_id, dot.s_area->a_size, dot.s_addr); */
2443 if (oap
->a_flag
& A_OVR
) {
2444 // the size of an overlay is the biggest size encountered
2445 if (oap
->a_size
< dot
.s_addr
) {
2446 oap
->a_size
= dot
.s_addr
;
2449 else if (oap
->a_flag
& A_ABS
) {
2450 oap
->a_addr
= dot
.s_org
;
2451 oap
->a_size
+= dot
.s_addr
- dot
.s_org
;
2452 dot
.s_addr
= dot
.s_org
= 0;
2456 oap
->a_size
= dot
.s_addr
;
2458 if (nap
->a_flag
& A_OVR
) {
2459 // a new overlay starts at 0, no fuzz
2463 else if (nap
->a_flag
& A_ABS
) {
2464 // a new absolute starts at org, no fuzz
2465 dot
.s_addr
= dot
.s_org
;
2469 dot
.s_addr
= nap
->a_size
;
2476 /*)Function VOID phase(ap, a)
2478 * area * ap pointer to area
2479 * a_uint a address in area
2481 * Function phase() compares the area ap and address a
2482 * with the current area dot.s_area and address dot.s_addr
2483 * to determine if the position of the symbol has changed
2484 * between assembler passes.
2490 * sym * dot defined as sym[0]
2496 * The p error is invoked if the area and/or address
2501 phase(struct area
*ap
, a_uint a
)
2503 if (ap
!= dot
.s_area
|| a
!= dot
.s_addr
)
2508 "Usage: [-Options] [-Option with arg] file",
2509 "Usage: [-Options] [-Option with arg] outfile file1 [file2 ...]",
2510 " -h or NO ARGUMENTS Show this help list",
2512 " -I Add the named directory to the include file",
2513 " search path. This option may be used more than once.",
2514 " Directories are searched in the order given.",
2516 " -l Create list file/outfile[.lst]",
2517 " -o Create object file/outfile[.rel]",
2518 " -s Create symbol file/outfile[.sym]",
2520 " -d Decimal listing",
2521 " -q Octal listing",
2522 " -x Hex listing (default)",
2523 " -b Display .define substitutions in listing",
2524 " -bb and display without .define substitutions",
2525 " -c Disable instruction cycle count in listing",
2526 " -f Flag relocatable references by ` in listing file",
2527 " -ff Flag relocatable references by mode in listing file",
2528 " -p Disable automatic listing pagination",
2529 " -u Disable .list/.nlist processing",
2530 " -w Wide listing format for symbol table",
2532 " -v Enable out of range signed / unsigned errors",
2534 " -a All user symbols made global",
2535 " -g Undefined symbols made global",
2536 " -n Don't resolve global assigned value symbols",
2537 " -z Disable case sensitivity for symbols",
2538 #if (NOICE || SDCDB)
2541 " -j Enable NoICE Debug Symbols",
2544 " -y Enable SDCC Debug Symbols",
2551 /*)Function VOID usage()
2553 * The function usage() outputs to the stderr device the
2554 * assembler name and version and a list of valid assembler options.
2557 * char ** dp pointer to an array of
2558 * text string pointers.
2561 * char cpu[] assembler type string
2562 * char * usetxt[] array of string pointers
2565 * int fprintf() c_library
2576 fprintf(stderr
, "\n%s Assembler %s (%s)\n\n", is_sdas() ? "sdas" : "ASxxxx", VERSION
, cpu
);
2577 fprintf(stderr
, "\nCopyright (C) %s Alan R. Baldwin", COPYRIGHT
);
2578 fprintf(stderr
, "\nThis program comes with ABSOLUTELY NO WARRANTY.\n\n");
2579 for (dp
= usetxt
; *dp
; dp
++) {
2580 fprintf(stderr
, "%s\n", *dp
);