3 Copyright (C) 1989-1995 Alan R. Baldwin
4 721 Berkeley St., Kent, Ohio 44240
6 This program is free software; you can redistribute it and/or modify it
7 under the terms of the GNU General Public License as published by the
8 Free Software Foundation; either version 3, or (at your option) any
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/>. */
20 * 29-Oct-97 JLH pass ";!" comments to output file
33 * The module asmain.c includes the command argument parser,
34 * the three pass sequencer, and the machine independent
35 * assembler parsing code.
37 * asmain.c contains the following functions:
38 * VOID main(argc, argv)
41 * FILE * afile(fn, ft, wf)
46 * asmain.c contains the array char *usetxt[] which
47 * references the usage text strings printed by usage().
50 static const char *search_path[100];
51 static int search_path_length;
54 * The search_path_append is used to append another directory to the end
55 * of the include file search path.
58 * The directory to be added to the path.
61 search_path_append(const char *dir)
63 if (search_path_length < sizeof(search_path)/sizeof(char*))
65 search_path[search_path_length++] = dir;
70 * The search_path_fopen function is used to open the named file. If
71 * the file isn't in the current directory, the search path is then used
72 * to build a series of possible file names, and attempts to open them.
73 * The first found is used.
76 * The name of the file to be opened.
78 * The mode of the file to be opened.
80 * what the fopen function would return on success, or NULL if the
81 * file is not anywhere in the search path.
84 search_path_fopen(const char *filename, const char *mode)
89 fp = fopen(filename, mode);
90 if (fp != NULL || filename[0] == '/' || filename[0] == '\\')
92 for (j = 0; j < search_path_length; ++j)
96 strncpy(path, search_path[j], sizeof(path));
97 if ((path[strlen(path) - 1] != '/') &&
98 (path[strlen(path) - 1] != DIR_SEPARATOR_CHAR))
100 strncat(path, DIR_SEPARATOR_STRING, sizeof(path));
102 strncat(path, filename, sizeof(path));
103 fp = fopen(path, mode);
111 /*)Function VOID main(argc, argv)
113 * int argc argument count
114 * char * argv array of pointers to argument strings
116 * The function main() is the entry point to the assembler.
117 * The purpose of main() is to (1) parse the command line
118 * arguments for options and source file specifications and
119 * (2) to process the source files through the 3 pass assembler.
120 * Before each assembler pass various variables are initialized
121 * and source files are rewound to their beginning. During each
122 * assembler pass each assembler-source text line is processed.
123 * After each assembler pass the assembler information is flushed
124 * to any opened output files and the if-else-endif processing
125 * is checked for proper termination.
127 * The function main() is also responsible for opening all
128 * output files (REL, LST, and SYM), sequencing the global (-g)
129 * and all-global (-a) variable definitions, and dumping the
130 * REL file header information.
133 * char * p pointer to argument string
134 * int c character from argument string
135 * int i argument loop counter
136 * area * ap pointer to area structure
139 * int aflag -a, make all symbols global flag
140 * char afn[] afile() constructed filespec
141 * area * areap pointer to an area structure
142 * int cb[] array of assembler output values
143 * int cbt[] array of assembler relocation types
144 * describing the data in cb[]
145 * int cfile current file handle index
146 * of input assembly files
147 * int * cp pointer to assembler output array cb[]
148 * int * cpt pointer to assembler relocation type
150 * char eb[] array of generated error codes
151 * char * ep pointer into error list array eb[]
152 * int fflag -f(f), relocations flagged flag
153 * int flevel IF-ELSE-ENDIF flag will be non
154 * zero for false conditional case
155 * Addr_T fuzz tracks pass to pass changes in the
156 * address of symbols caused by
157 * variable length instruction formats
158 * int gflag -g, make undefined symbols global flag
159 * char ib[] assembler-source text line
160 * int inpfil count of assembler
161 * input files specified
162 * int ifcnd[] array of IF statement condition
163 * values (0 = FALSE) indexed by tlevel
164 * int iflvl[] array of IF-ELSE-ENDIF flevel
165 * values indexed by tlevel
166 * int incfil current file handle index
168 * char * ip pointer into the assembler-source
170 * jmp_buf jump_env compiler dependent structure
171 * used by setjmp() and longjmp()
172 * int lflag -l, generate listing flag
173 * int line current assembler source
175 * int lop current line number on page
176 * int oflag -o, generate relocatable output flag
177 * int jflag -j, generate debug info flag
178 * int page current page number
179 * int pflag enable listing pagination
180 * int pass assembler pass number
181 * int radix current number conversion radix:
182 * 2 (binary), 8 (octal), 10 (decimal),
184 * int sflag -s, generate symbol table flag
185 * char srcfn[][] array of source file names
186 * int srcline[] current source file line
187 * char stb[] Subtitle string buffer
188 * sym * symp pointer to a symbol structure
189 * int tlevel current conditional level
190 * int xflag -x, listing radix flag
191 * FILE * lfp list output file handle
192 * FILE * ofp relocation output file handle
193 * FILE * tfp symbol table output file handle
194 * FILE * sfp[] array of assembler-source file handles
197 * FILE * afile() asmain.c
198 * VOID allglob() assym.c
199 * VOID asexit() asmain.c
200 * VOID diag() assubr.c
201 * VOID err() assubr.c
202 * int fprintf() c-library
203 * int as_getline() aslex.c
204 * VOID list() aslist.c
205 * VOID lstsym() aslist.c
206 * VOID minit() ___mch.c
207 * VOID newdot() asmain.c
208 * VOID outchk() asout.c
209 * VOID outgsd() asout.c
210 * int rewind() c-library
211 * int setjmp() c-library
212 * VOID symglob() assym.c
213 * VOID syminit() assym.c
214 * VOID usage() asmain.c
217 * Completion of main() completes the assembly process.
218 * REL, LST, and/or SYM files may be generated.
225 main(int argc, char *argv[])
231 /*fprintf(stdout, "\n");*/
234 for (i=1; i<argc; ++i) {
240 while ((c = *p++) != 0)
260 search_path_append(p);
265 case 'j': /* JLH: debug info */
268 ++oflag; /* force object */
315 if (++inpfil == MAXFIL) {
316 fprintf(stderr, "too many input files\n");
319 sfp[inpfil] = afile(p, "", 0);
320 strcpy(srcfn[inpfil],afn);
323 lfp = afile(p, "lst", 1);
325 ofp = afile(p, "rel", 1);
326 // save the file name if we have to delete it on error
330 tfp = afile(p, "sym", 1);
337 for (pass=0; pass<3; ++pass) {
338 if (gflag && pass == 1)
340 if (aflag && pass == 1)
342 if (oflag && pass == 2)
356 for (i = 0; i <= inpfil; i++)
369 while (as_getline()) {
375 /* JLH: if line begins with ";!", then
376 * pass this comment on to the output file
378 if (oflag && (pass == 1) &&
379 (ip[0] == ';') && (ip[1] == '!'))
381 fprintf(ofp, "%s\n", ip );
384 if (setjmp(jump_env) == 0)
392 newdot(dot.s_area); /* Flush area info */
393 if (flevel || tlevel)
397 outchk(HUGE, HUGE); /* Flush */
404 //printf ("aserr: %d\n", aserr);
405 //printf ("fatalErrors: %d\n", fatalErrors);
407 return 0; // hush the compiler
410 /*)Function VOID asexit(i)
414 * The function asexit() explicitly closes all open
415 * files and then terminates the program.
421 * FILE * ifp[] array of include-file file handles
422 * FILE * lfp list output file handle
423 * FILE * ofp relocation output file handle
424 * FILE * tfp symbol table output file handle
425 * FILE * sfp[] array of assembler-source file handles
428 * int fclose() c-library
429 * VOID exit() c-library
432 * All files closed. Program terminates.
440 if (lfp != NULL) fclose(lfp);
441 if (ofp != NULL) fclose(ofp);
442 if (tfp != NULL) fclose(tfp);
444 for (j=0; j<MAXFIL && sfp[j] != NULL; j++) {
448 /*for (j=0; j<MAXINC && ifp[j] != NULL; j++) {
452 // remove output file
453 printf ("removing %s\n", relFile);
459 /*)Function VOID asmbl()
461 * The function asmbl() scans the assembler-source text for
462 * (1) labels, global labels, equates, global equates, and local
463 * symbols, (2) .if, .else, .endif, and .page directives,
464 * (3) machine independent assembler directives, and (4) machine
465 * dependent mnemonics.
468 * mne * mp pointer to a mne structure
469 * sym * sp pointer to a sym structure
470 * tsym * tp pointer to a tsym structure
471 * int c character from assembler-source
473 * area * ap pointer to an area structure
474 * expr e1 expression structure
475 * char id[] id string
476 * char opt[] options string
477 * char fn[] filename string
478 * char * p pointer into a string
479 * int d temporary value
480 * int n temporary value
481 * int uaf user area options flag
482 * int uf area options
485 * area * areap pointer to an area structure
486 * char ctype[] array of character types, one per
488 * int flevel IF-ELSE-ENDIF flag will be non
489 * zero for false conditional case
490 * Addr_T fuzz tracks pass to pass changes in the
491 * address of symbols caused by
492 * variable length instruction formats
493 * int ifcnd[] array of IF statement condition
494 * values (0 = FALSE) indexed by tlevel
495 * int iflvl[] array of IF-ELSE-ENDIF flevel
496 * values indexed by tlevel
497 * FILE * ifp[] array of include-file file handles
498 * char incfn[][] array of include file names
499 * int incline[] current include file line
500 * int incfil current file handle index
502 * Addr_T laddr address of current assembler line
503 * or value of .if argument
504 * int lmode listing mode
505 * int lop current line number on page
506 * char module[] module name string
507 * int pass assembler pass number
508 * int radix current number conversion radix:
509 * 2 (binary), 8 (octal), 10 (decimal),
511 * char stb[] Subtitle string buffer
512 * sym * symp pointer to a symbol structure
513 * char tb[] Title string buffer
514 * int tlevel current conditional level
517 * Addr_T absexpr() asexpr.c
518 * area * alookup() assym.c
519 * VOID clrexpr() asexpr.c
520 * int digit() asexpr.c
521 * char endline() aslex.c
522 * VOID err() assubr.c
523 * VOID expr() asexpr.c
524 * FILE * fopen() c-library
526 * VOID getid() aslex.c
527 * int getmap() aslex.c
528 * char getnb() aslex.c
529 * VOID getst() aslex.c
530 * sym * lookup() assym.c
531 * VOID machine() ___mch.c
532 * mne * mlookup() assym.c
534 * VOID * new() assym.c
535 * VOID newdot() asmain.c
536 * VOID outall() asout.c
537 * VOID outab() asout.c
538 * VOID outchk() asout.c
539 * VOID outrb() asout.c
540 * VOID outrw() asout.c
541 * VOID phase() asmain.c
542 * VOID qerr() assubr.c
543 * char * strcpy() c-library
544 * char * strncpy() c-library
545 * VOID unget() aslex.c
551 register struct mne *mp;
552 register struct sym *sp;
553 register struct tsym *tp;
562 static struct area *abs_ap; /* pointer to current absolute area structure */
567 if ((c=endline()) == 0) { return; }
569 * If the first character is a digit then assume
570 * a local symbol is being specified. The symbol
571 * must end with $: to be valid.
573 * Construct a tsym structure at the first
574 * occurance of the symbol. Flag the symbol
575 * as multiply defined if not the first occurance.
577 * Load area, address, and fuzz values
578 * into structure tsym.
580 * Check for assembler phase error and
581 * multiply defined error.
583 if (ctype[c] & DIGIT) {
587 while ((d = digit(c, 10)) >= 0) {
591 if (c != '$' || get() != ':')
596 if (n == tp->t_num) {
603 tp=(struct tsym *) new (sizeof(struct tsym));
604 tp->t_lnk = symp->s_tsym;
607 tp->t_area = dot.s_area;
608 tp->t_addr = dot.s_addr;
613 if (n == tp->t_num) {
620 fuzz = tp->t_addr - dot.s_addr;
621 tp->t_area = dot.s_area;
622 tp->t_addr = dot.s_addr;
624 phase(tp->t_area, tp->t_addr);
625 if (tp->t_flg & S_MDF)
636 * If the first character is a letter then assume a label,
637 * symbol, assembler directive, or assembler mnemonic is
640 if ((ctype[c] & LETTER) == 0) {
650 * If the next character is a : then a label is being processed.
651 * A double :: defines a global label. If this is new label
652 * then create a symbol structure.
654 * Flag multiply defined labels.
656 * Load area, address, and fuzz values
657 * into structure symp.
659 * Check for assembler phase error and
660 * multiply defined error.
665 if ((c = get()) != ':') {
673 if ((symp->s_type != S_NEW) &&
674 ((symp->s_flag & S_ASG) == 0))
675 symp->s_flag |= S_MDF;
677 fuzz = symp->s_addr - dot.s_addr;
678 symp->s_type = S_USER;
679 symp->s_area = dot.s_area;
680 symp->s_addr = dot.s_addr;
682 if (symp->s_flag & S_MDF)
684 phase(symp->s_area, symp->s_addr);
687 symp->s_flag |= S_GBL;
693 * If the next character is a = then an equate is being processed.
694 * A double == defines a global equate. If this is new variable
695 * then create a symbol structure.
700 if ((c = get()) != '=') {
709 if (e1.e_flag || e1.e_base.e_ap != dot.s_area)
712 if (sp->s_type != S_NEW && (sp->s_flag & S_ASG) == 0) {
716 sp->s_area = e1.e_base.e_ap;
717 sp->s_addr = laddr = e1.e_addr;
726 lmode = flevel ? SLIST : CLIST;
727 if ((mp = mlookup(id)) == NULL) {
733 * If we have gotten this far then we have found an
734 * assembler directive or an assembler mnemonic.
736 * Check for .if, .else, .endif, and .page directives
737 * which are not controlled by the conditional flags
739 switch (mp->m_type) {
743 if (tlevel < MAXIF) {
746 iflvl[tlevel] = flevel;
759 if (++flevel > (iflvl[tlevel]+1)) {
763 if (--flevel < iflvl[tlevel]) {
772 flevel = iflvl[tlevel--];
790 * If we are not in a false state for .if/.else then
791 * process the assembler directives here.
793 switch (mp->m_type) {
797 laddr = dot.s_addr = (dot.s_addr + 1) & ~1;
803 laddr = dot.s_addr |= 1;
812 if (mp->m_type == S_BYTE) {
817 } while ((c = getnb()) == ',');
823 if ((d = getnb()) == '\0')
825 while ((c = getmap(d)) >= 0)
827 if (mp->m_type == S_ASCIZ)
832 if ((d = getnb()) == '\0')
836 if ((n = getmap(d)) >= 0) {
849 dot.s_addr += e1.e_addr*mp->m_valu;
855 if ((c = getnb()) != 0) {
857 if (p < &tb[NTITL-1])
859 } while ((c = get()) != 0);
868 if ((c = getnb()) != 0) {
870 if (p < &stb[NSBTL-1])
872 } while ((c = get()) != 0);
880 getst(id, getnb()); // a module can start with a digit
885 strncpy(module, id, NCPS);
893 if ((c = getnb()) != 0) {
895 if (p < &optsdcc[NINPUT-1])
897 } while ((c = get()) != 0);
902 /*if (pass == 0) printf("optsdcc=%s\n", optsdcc);*/
910 } while ((c = getnb()) == ',');
919 if ((c = getnb()) == '(') {
923 if (mp && mp->m_type == S_ATYP) {
929 } while ((c = getnb()) == ',');
935 if ((ap = alookup(id)) != NULL) {
936 if (uaf && uf != ap->a_flag)
939 ap = (struct area *) new (sizeof(struct area));
941 strncpy(ap->a_id, id, NCPS);
942 ap->a_ref = areap->a_ref + 1;
946 ap->a_flag = uaf ? uf : (A_CON|A_REL);
951 if (dot.s_area->a_flag & A_ABS)
956 if (dot.s_area->a_flag & A_ABS) {
960 sprintf(buf, "%s%x", abs_ap->a_id, org_cnt++);
961 if ((ap = alookup(buf)) == NULL) {
962 ap = (struct area *) new (sizeof(struct area));
965 strncpy(ap->a_id, buf, NCPS);
966 ap->a_ref = areap->a_ref + 1;
973 dot.s_addr = dot.s_org = laddr;
1017 while ((c = get()) != d) {
1018 if (p < &fn[PATH_MAX-1]) {
1025 if ((++incfil == MAXINC) ||
1026 (ifp[incfil] = search_path_fopen(fn, "r")) == NULL) {
1031 incline[incfil] = 0;
1032 strcpy(incfn[incfil],fn);
1042 if (!as_strcmpi(id, "on"))
1044 /* Quick sanity check: size of
1045 * Addr_T must be at least 24 bits.
1047 if (sizeof(Addr_T) < 3)
1051 "Cannot enable Flat24 mode: "
1052 "host system must have 24 bit "
1053 "or greater integers.\n");
1060 else if (!as_strcmpi(id, "off"))
1075 printf("as8051: ds390 flat mode %sabled.\n",
1076 flat24Mode ? "en" : "dis");
1082 * If not an assembler directive then go to
1083 * the machine dependent function which handles
1084 * all the assembler mnemonics.
1088 /* if cdb information then generate the line info */
1089 if (cflag && (pass == 1))
1092 /* JLH: if -j, generate a line number symbol */
1093 if (jflag && (pass == 1))
1102 /*)Function FILE * afile(fn, ft, wf)
1104 * char * fn file specification string
1105 * char * ft file type string
1106 * int wf read(0)/write(1) flag
1108 * The function afile() opens a file for reading or writing.
1109 * (1) If the file type specification string ft
1110 * is not NULL then a file specification is
1111 * constructed with the file path\name in fn
1112 * and the extension in ft.
1113 * (2) If the file type specification string ft
1114 * is NULL then the file specification is
1115 * constructed from fn. If fn does not have
1116 * a file type then the default source file
1117 * type dsft is appended to the file specification.
1119 * afile() returns a file handle for the opened file or aborts
1120 * the assembler on an open error.
1123 * int c character value
1124 * FILE * fp filehandle for opened file
1125 * char * p1 pointer to filespec string fn
1126 * char * p2 pointer to filespec string fb
1127 * char * p3 pointer to filetype string ft
1130 * char afn[] afile() constructed filespec
1131 * char dsft[] default assembler file type string
1132 * char afn[] constructed file specification string
1135 * VOID asexit() asmain.c
1136 * FILE * fopen() c_library
1137 * int fprintf() c_library
1140 * File is opened for read or write.
1144 afile(char *fn, char *ft, int wf)
1146 register char *p2, *p3;
1154 p2 = strrchr (afn, FSEPX); // search last '.'
1156 p2 = afn + strlen (afn);
1157 if (p2 > &afn[PATH_MAX-4]) // truncate filename, if it's too long
1158 p2 = &afn[PATH_MAX-4];
1161 // choose a file-extension
1162 if (*p3 == 0) { // extension supplied?
1163 p3 = strrchr (fn, FSEPX); // no: extension in fn?
1167 p3 = dsft; // no: default extension
1170 while ((c = *p3++) != 0) { // strncpy
1171 if (p2 < &afn[PATH_MAX-1])
1176 if ((fp = fopen(afn, wf?"w":"r")) == NULL) {
1177 fprintf(stderr, "%s: cannot %s.\n", afn, wf?"create":"open");
1183 /*)Function VOID newdot(nap)
1185 * area * nap pointer to the new area structure
1187 * The function newdot():
1188 * (1) copies the current values of fuzz and the last
1189 * address into the current area referenced by dot
1190 * (2) loads dot with the pointer to the new area and
1191 * loads the fuzz and last address parameters
1192 * (3) outall() is called to flush any remaining
1193 * bufferred code from the old area to the output
1196 * area * oap pointer to old area
1199 * sym dot defined as sym[0]
1200 * Addr_T fuzz tracks pass to pass changes in the
1201 * address of symbols caused by
1202 * variable length instruction formats
1208 * Current area saved, new area loaded, buffers flushed.
1212 newdot(register struct area *nap)
1214 register struct area *oap;
1217 /* fprintf (stderr, "%s dot.s_area->a_size: %d dot.s_addr: %d\n",
1218 oap->a_id, dot.s_area->a_size, dot.s_addr); */
1220 if (oap->a_flag & A_OVR) {
1221 // the size of an overlay is the biggest size encountered
1222 if (oap->a_size < dot.s_addr) {
1223 oap->a_size = dot.s_addr;
1225 } else if (oap->a_flag & A_ABS) {
1226 oap->a_addr = dot.s_org;
1227 oap->a_size += dot.s_addr - dot.s_org;
1228 dot.s_addr = dot.s_org = 0;
1231 oap->a_size = dot.s_addr;
1233 if (nap->a_flag & A_OVR) {
1234 // a new overlay starts at 0, no fuzz
1237 } else if (nap->a_flag & A_ABS) {
1238 // a new absolute starts at org, no fuzz
1239 dot.s_addr = dot.s_org;
1242 dot.s_addr = nap->a_size;
1249 /*)Function VOID phase(ap, a)
1251 * area * ap pointer to area
1252 * Addr_T a address in area
1254 * Function phase() compares the area ap and address a
1255 * with the current area dot.s_area and address dot.s_addr
1256 * to determine if the position of the symbol has changed
1257 * between assembler passes.
1263 * sym * dot defined as sym[0]
1269 * The p error is invoked if the area and/or address
1274 phase(struct area *ap, Addr_T a)
1276 if (ap != dot.s_area || a != dot.s_addr)
1281 "Usage: [-dqxjgalopsf][ -I<dir> ] file1 [file2 file3 ...]",
1282 " d decimal listing",
1284 " x hex listing (default)",
1285 " j add line number and debug information to file", /* JLH */
1286 " g undefined symbols made global",
1287 " a all user symbols made global",
1288 " l create list output file1[LST]",
1289 " o create object output file1[REL]",
1290 " s create symbol output file1[SYM]",
1291 " c generate sdcdb debug information",
1292 " p disable listing pagination",
1293 " f flag relocatable references by ` in listing file",
1294 " ff flag relocatable references by mode in listing file",
1295 "-I<dir> Add the named directory to the include file",
1296 " search path. This option may be used more than once.",
1297 " Directories are searched in the order given.",
1302 /*)Function VOID usage()
1304 * The function usage() outputs to the stderr device the
1305 * assembler name and version and a list of valid assembler options.
1308 * char ** dp pointer to an array of
1309 * text string pointers.
1312 * char cpu[] assembler type string
1313 * char * usetxt[] array of string pointers
1316 * VOID asexit() asmain.c
1317 * int fprintf() c_library
1320 * program is terminated
1328 fprintf(stderr, "\nASxxxx Assembler %s (%s)\n\n", VERSION, cpu);
1329 for (dp = usetxt; *dp; dp++)
1330 fprintf(stderr, "%s\n", *dp);