/* asmain.c */
/*
* Copyright (C) 1989-2012 Alan R. Baldwin
*
* This program 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 3 of the License, or
* (at your option) any later version.
*
* This program 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 this program. If not, see .
*
*
* Alan R. Baldwin
* 721 Berkeley St.
* Kent, Ohio 44240
*
*
* With enhancements from
*
* John L. Hartman (JLH)
* jhartman at compuserve dot com
*
* Boisy G. Pitre (BGP)
* boisy at boisypitre dot com
*
* Mike McCarty
* mike dot mccarty at sbcglobal dot net
*/
#include
#include
#include "sdas.h"
#include "dbuf_string.h"
#include "asxxxx.h"
/*)Module asmain.c
*
* The module asmain.c includes the command argument parser,
* the three pass sequencer, and the machine independent
* assembler parsing code.
*
* asmain.c contains the following functions:
* int main(argc, argv)
* VOID asexit(n)
* VOID asmbl()
* VOID equate()
* FILE * afile(fn, ft, wf)
* int fndidx(str)
* int intsiz()
* VOID newdot(nap)
* VOID phase(ap, a)
* VOID usage()
*
* asmain.c contains the array char *usetxt[] which
* references the usage text strings printed by usage().
*/
/* sdas specific */
static const char *search_path[100];
static int search_path_length;
/**
* The search_path_append is used to append another directory to the end
* of the include file search path.
*
* @param dir
* The directory to be added to the path.
*/
void
search_path_append(const char *dir)
{
if (search_path_length < sizeof(search_path)/sizeof(char*))
{
search_path[search_path_length++] = dir;
}
}
/**
* The create_temp_path function is used to build a temporary file path
* by concatenating dir and filename. If len >= 0 then only the left
* substring of dir with length len is used to build the file path.
*
* @param dir
* The directory part of the path.
* @param len
* < 0: use the whole dir as the directory part of the path.
* >= 0: the length of dir to use as the directory part of the path.
* @param filename
* The filename to be appended to the directory part of the path.
* @returns
* The constructed path.
*/
static const char *
create_temp_path(const char * dir, int len, const char * filename)
{
static struct dbuf_s dbuf;
const char * path;
if (!dbuf_is_initialized(&dbuf))
dbuf_init (&dbuf, 1024);
dbuf_set_length(&dbuf, 0);
dbuf_append_str(&dbuf, dir);
if (len >= 0)
dbuf_set_length(&dbuf, len);
path = dbuf_c_str(&dbuf);
if ((path[strlen(path) - 1] != '/') &&
(path[strlen(path) - 1] != DIR_SEPARATOR_CHAR)) {
dbuf_append_char(&dbuf, DIR_SEPARATOR_CHAR);
}
dbuf_append_str(&dbuf, filename);
path = dbuf_c_str(&dbuf);
return path;
}
/**
* The search_path_fopen function is used to open the named file. If
* the file isn't in the current directory, the search path is then used
* to build a series of possible file names, and attempts to open them.
* The first found is used.
*
* @param filename
* The name of the file to be opened.
* @param mode
* The mode of the file to be opened.
* @returns
* what the fopen function would return on success, or NULL if the
* file is not anywhere in the search path.
*/
static FILE *
search_path_fopen(const char *filename, const char *mode)
{
FILE *fp;
int j;
fp = fopen(filename, mode);
if (fp != NULL || filename[0] == '/' || filename[0] == '\\')
return fp;
/*
* Try the path of the file opening the include file
*/
fp = fopen(create_temp_path(afn, afp, filename), mode);
if (fp != NULL)
return fp;
for (j = 0; j < search_path_length; ++j) {
fp = fopen(create_temp_path(search_path[j], -1, filename), mode);
if (fp != NULL)
return fp;
}
errno = ENOENT;
return NULL;
}
/* end sdas specific */
/*)Function int main(argc, argv)
*
* int argc argument count
* char * argv array of pointers to argument strings
*
* The function main() is the entry point to the assembler.
* The purpose of main() is to (1) parse the command line
* arguments for options and source file specifications and
* (2) to process the source files through the 3 pass assembler.
* Before each assembler pass various variables are initialized
* and source files are rewound to their beginning. During each
* assembler pass each assembler-source text line is processed.
* After each assembler pass the assembler information is flushed
* to any opened output files and the if-else-endif processing
* is checked for proper termination.
*
* The function main() is also responsible for opening all
* output files (REL, LST, and SYM), sequencing the global (-g)
* and all-global (-a) variable definitions, and dumping the
* REL file header information.
*
* local variables:
* char * p pointer to argument string
* int c character from argument string
* int i argument loop counter
* area * ap pointer to area structure
*
* global variables:
* int aflag -a, make all symbols global flag
* char afn[] afile() constructed filespec
* int afp afile constructed path length
* area * areap pointer to an area structure
* asmf * asmc pointer to current assembler file structure
* int asmline assembler source file line number
* asmf * asmp pointer to first assembler file structure
* int aserr assembler error counter
* int bflag -b(b), listing mode flag
* int cb[] array of assembler output values
* int cbt[] array of assembler relocation types
* describing the data in cb[]
* int * cp pointer to assembler output array cb[]
* int * cpt pointer to assembler relocation type
* output array cbt[]
* char eb[] array of generated error codes
* char * ep pointer into error list array eb[]
* int fflag -f(f), relocations flagged flag
* int flevel IF-ELSE-ENDIF flag will be non
* zero for false conditional case
* a_uint fuzz tracks pass to pass changes in the
* address of symbols caused by
* variable length instruction formats
* int gflag -g, make undefined symbols global flag
* char * ib string buffer containing
* assembler-source text line for processing
* char * ic string buffer containing
* assembler-source text line for listing
* int ifcnd[] array of IF statement condition
* values (0 = FALSE) indexed by tlevel
* int iflvl[] array of IF-ELSE-ENDIF flevel
* values indexed by tlevel
* int incfil current include file count
* int incline include source file line number
* char * ip pointer into the assembler-source
* text line in ib[]
* jmp_buf jump_env compiler dependent structure
* used by setjmp() and longjmp()
* int lflag -l, generate listing flag
* int line current assembler source
* line number
* int lnlist current LIST-NLIST state
* int lop current line number on page
* int maxinc maximum include file nesting counter
* int oflag -o, generate relocatable output flag
* int jflag -j, generate debug info flag
* int page current page number
* int pflag disable listing pagination
* int pass assembler pass number
* int radix current number conversion radix:
* 2 (binary), 8 (octal), 10 (decimal),
* 16 (hexadecimal)
* int sflag -s, generate symbol table flag
* int srcline current source line number
* char stb[] Subtitle string buffer
* sym * symp pointer to a symbol structure
* int tlevel current conditional level
* int uflag -u, disable .list/.nlist processing
* int wflag -w, enable wide listing format
* int xflag -x, listing radix flag
* int zflag -z, disable symbol case sensitivity
* FILE * lfp list output file handle
* FILE * ofp relocation output file handle
* FILE * tfp symbol table output file handle
*
* called functions:
* FILE * afile() asmain.c
* VOID allglob() assym.c
* VOID asexit() asmain.c
* VOID diag() assubr.c
* VOID err() assubr.c
* VOID exprmasks() asexpr.c
* int fprintf() c_library
* int int32siz() asmain.c
* VOID list() aslist.c
* VOID lstsym() aslist.c
* VOID mcrinit() asmcro.c
* VOID minit() ___mch.c
* char * new() assym.c
* VOID newdot() asmain.c
* int nxtline() aslex.c
* VOID outbuf() asout.c
* VOID outchk() asout.c
* VOID outgsd() asout.c
* int rewind() c_library
* int setjmp() c_library
* char * strcpy() c_library
* VOID symglob() assym.c
* VOID syminit() assym.c
* VOID usage() asmain.c
*
* side effects:
* Completion of main() completes the assembly process.
* REL, LST, and/or SYM files may be generated.
*/
/* sdas specific */
char relFile[FILSPC];
/* end sdas specific */
int
main(int argc, char *argv[])
{
char *p = NULL;
char *q;
int c, i;
struct area *ap;
if (intsiz() < 4) {
fprintf(stderr, "?ASxxxx-Error-Size of INT32 is not 32 bits or larger.\n\n");
exit(ER_FATAL);
}
/* sdas specific */
/* sdas initialization */
sdas_init(argv[0]);
/* end sdas specific */
if (!is_sdas())
fprintf(stdout, "\n");
q = NULL;
asmc = NULL;
asmp = NULL;
for (i=1; inext = (struct asmf *)
new (sizeof (struct asmf));
asmc = asmc->next;
}
asmc->next = NULL;
asmc->objtyp = T_ASM;
asmc->line = 0;
asmc->flevel = 0;
asmc->tlevel = 0;
asmc->lnlist = LIST_NORM;
asmc->fp = afile(p, "", 0);
strcpy(asmc->afn,afn);
asmc->afp = afp;
}
}
if (asmp == NULL)
usage(ER_WARNING);
if (lflag)
lfp = afile(q, "lst", 1);
/* sdas specific */
if (oflag) {
ofp = afile(q, (is_sdas() && p != q) ? "" : "rel", 1);
// save the file name if we have to delete it on error
strcpy(relFile,afn);
}
/* end sdas specific */
if (sflag)
tfp = afile(q, "sym", 1);
exprmasks(2);
syminit();
for (pass=0; pass<3; ++pass) {
aserr = 0;
if (gflag && pass == 1)
symglob();
if (aflag && pass == 1)
allglob();
if (oflag && pass == 2)
outgsd();
flevel = 0;
tlevel = 0;
lnlist = LIST_NORM;
ifcnd[0] = 0;
iflvl[0] = 0;
radix = 10;
page = 0;
/* sdas specific */
org_cnt = 0;
/* end sdas specific */
stb[0] = 0;
lop = NLPP;
incfil = 0;
maxinc = 0;
srcline = 0;
asmline = 0;
incline = 0;
asmc = asmp;
while (asmc) {
if (asmc->fp)
rewind(asmc->fp);
asmc = asmc->next;
}
asmc = asmp;
strcpy(afn, asmc->afn);
afp = asmc->afp;
ap = areap;
while (ap) {
ap->a_fuzz = 0;
ap->a_size = 0;
ap = ap->a_ap;
}
fuzz = 0;
dot.s_addr = 0;
dot.s_area = &dca;
outbuf("I");
outchk(0,0);
symp = ˙
mcrinit();
minit();
while (nxtline()) {
cp = cb;
cpt = cbt;
ep = eb;
ip = ib;
/* JLH: if line begins with ";!", then
* pass this comment on to the output file
*/
if (oflag && (pass == 1) && (ip[0] == ';') && (ip[1] == '!')) {
fprintf(ofp, "%s\n", ip );
}
opcycles = OPCY_NONE;
if (setjmp(jump_env) == 0)
asmbl();
if (pass == 2) {
diag();
list();
}
}
newdot(dot.s_area); /* Flush area info */
}
if (flevel || tlevel) {
err('i');
fprintf(stderr, "?ASxxxx-Error- at end of assembly\n");
fprintf(stderr, " %s\n", geterr('i'));
}
if (oflag)
outchk(ASXHUGE, ASXHUGE); /* Flush */
if (sflag) {
lstsym(tfp);
} else
if (lflag) {
lstsym(lfp);
}
asexit(aserr ? ER_ERROR : ER_NONE);
return(0);
}
/*)Function int intsiz()
*
* The function intsiz() returns the size of INT32
*
* local variables:
* none
*
* global variables:
* none
*
* functions called:
* none
*
* side effects:
* none
*/
int
intsiz(void)
{
return(sizeof(INT32));
}
/*)Function VOID asexit(i)
*
* int i exit code
*
* The function asexit() explicitly closes all open
* files and then terminates the program.
*
* local variables:
* int j loop counter
*
* global variables:
* asmf * asmc pointer to current assembler file structure
* asmf * asmp pointer to first assembler file structure
* FILE * lfp list output file handle
* FILE * ofp relocation output file handle
* FILE * tfp symbol table output file handle
*
* functions called:
* int fclose() c_library
* VOID exit() c_library
*
* side effects:
* All files closed. Program terminates.
*/
VOID
asexit(int i)
{
if (lfp != NULL) fclose(lfp);
if (ofp != NULL) fclose(ofp);
if (tfp != NULL) fclose(tfp);
while (asmc != NULL) {
if ((asmc->objtyp == T_INCL) && (asmc->fp != NULL)) {
fclose(asmc->fp);
}
asmc = asmc->next;
}
asmc = asmp;
while (asmc != NULL) {
if ((asmc->objtyp == T_ASM) && (asmc->fp != NULL)) {
fclose(asmc->fp);
}
asmc = asmc->next;
}
/* sdas specific */
if (i) {
/* remove output file */
printf ("removing %s\n", relFile);
remove(relFile);
}
/* end sdas specific */
exit(i);
}
/*)Function VOID asmbl()
*
* The function asmbl() scans the assembler-source text for
* (1) labels, global labels, equates, global equates, and local
* symbols, (2) .if, .else, .endif, and .page directives,
* (3) machine independent assembler directives, (4) macros and
* macro definitions, and (5) machine dependent mnemonics.
*
* local variables:
* mne * mp pointer to a mne structure
* mne * xp pointer to a mne structure
* mcrdef *np pointer to a macro definition structure
* sym * sp pointer to a sym structure
* tsym * tp pointer to a tsym structure
* int c character from assembler-source
* text line
* area * ap pointer to an area structure
* expr e1 expression structure
* char id[] id string
* char opt[] options string
* char fn[] filename string
* char * p pointer into a string
* int d temporary value
* int uaf user area options flag
* int uf area options
* a_uint n temporary value
* a_uint v temporary value
* int flags temporary flag
* FILE * fp include file handle
* int m_type mnemonic type
*
* global variables:
* area * areap pointer to an area structure
* char ctype[] array of character types, one per
* ASCII character
* int flevel IF-ELSE-ENDIF flag will be non
* zero for false conditional case
* int ftflevel; IIFF-IIFT-IIFTF FLAG
* int lnlist current LIST-NLIST state
* a_uint fuzz tracks pass to pass changes in the
* address of symbols caused by
* variable length instruction formats
* int ifcnd[] array of IF statement condition
* values (0 = FALSE) indexed by tlevel
* int iflvl[] array of IF-ELSE-ENDIF flevel
* values indexed by tlevel
* int incline current include file line
* int incfil current include file count
* a_uint laddr address of current assembler line
* or value of .if argument
* int lmode listing mode
* int lop current line number on page
* char module[] module name string
* int pass assembler pass number
* int radix current number conversion radix:
* 2 (binary), 8 (octal), 10 (decimal),
* 16 (hexadecimal)
* char stb[] Subtitle string buffer
* sym * symp pointer to a symbol structure
* char tb[] Title string buffer
* int tlevel current conditional level
* int jflag -j, generate debug info flag
*
* functions called:
* a_uint absexpr() asexpr.c
* area * alookup() assym.c
* VOID clrexpr() asexpr.c
* int digit() asexpr.c
* char endline() aslex.c
* VOID equate() asmain.c
* VOID err() assubr.c
* VOID expr() asexpr.c
* FILE * fopen() c_library
* int get() aslex.c
* VOID getid() aslex.c
* int getmap() aslex.c
* int getnb() aslex.c
* VOID getst() aslex.c
* VOID getxstr() asmcro.c
* sym * lookup() assym.c
* VOID machine() ___mch.c
* int macro() asmcro.c
* int mcrprc() asmcro.c
* mne * mlookup() assym.c
* int more() aslex.c
* mcrdef *nlookup() asmcro.c
* char * new() assym.c
* VOID newdot() asmain.c
* VOID outall() asout.c
* VOID outab() asout.c
* VOID outchk() asout.c
* VOID outrb() asout.c
* VOID outrw() asout.c
* VOID phase() asmain.c
* VOID qerr() assubr.c
* char * strcpy() c_library
* char * strncpy() c_library
* char * strsto() assym.c
* VOID unget() aslex.c
*/
VOID
asmbl(void)
{
struct mne *mp, *xp;
struct mcrdef *np;
struct sym *sp;
struct tsym *tp;
int c;
struct area *ap;
struct expr e1;
char id[NCPS];
char equ[NCPS];
char *equ_ip;
int equtype;
char opt[NCPS];
char fn[FILSPC+FILSPC];
char *p;
int d, uaf, uf;
a_uint n, v;
int flags;
FILE * fp;
int m_type;
/* sdas specific */
static struct area *abs_ap; /* pointer to current absolute area structure */
/* end sdas specific */
laddr = dot.s_addr;
lmode = SLIST;
/*
* Check iiff-iift-iiftf processing
*/
if (ftflevel != 0) {
flevel = ftflevel - 1;
ftflevel = 0;
}
/*
* Check if Building a Macro
* Check if Exiting a Macro
*/
if (mcrprc(O_CHECK) != 0) {
return;
}
loop:
if ((c=endline()) == 0) { return; }
/*
* If the first character is a digit then assume
* a reusable symbol is being specified. The symbol
* must end with $: to be valid.
* pass 0:
* Construct a tsym structure at the first
* occurance of the symbol. Flag the symbol
* as multiply defined if not the first occurance.
* pass 1:
* Load area, address, and fuzz values
* into structure tsym.
* pass 2:
* Check for assembler phase error and
* multiply defined error.
*/
if (ctype[c] & DIGIT) {
if (flevel)
return;
n = 0;
while ((d = digit(c, 10)) >= 0) {
n = 10*n + d;
c = get();
}
if (c != '$' || get() != ':')
qerr();
tp = symp->s_tsym;
if (pass == 0) {
while (tp) {
if (n == tp->t_num) {
tp->t_flg |= S_MDF;
break;
}
tp = tp->t_lnk;
}
if (tp == NULL) {
tp=(struct tsym *) new (sizeof(struct tsym));
tp->t_lnk = symp->s_tsym;
tp->t_num = n;
tp->t_flg = 0;
tp->t_area = dot.s_area;
tp->t_addr = dot.s_addr;
symp->s_tsym = tp;
}
} else {
while (tp) {
if (n == tp->t_num) {
break;
}
tp = tp->t_lnk;
}
if (tp) {
if (pass == 1) {
fuzz = tp->t_addr - dot.s_addr;
tp->t_area = dot.s_area;
tp->t_addr = dot.s_addr;
} else {
phase(tp->t_area, tp->t_addr);
if (tp->t_flg & S_MDF)
err('m');
}
} else {
err('u');
}
}
lmode = ALIST;
goto loop;
}
/*
* If the first character is a letter then assume a label,
* symbol, assembler directive, or assembler mnemonic is
* being processed.
*/
if ((ctype[c] & LETTER) == 0) {
if (flevel) {
return;
} else {
qerr();
}
}
getid(id, c);
c = getnb();
/*
* If the next character is a : then a label is being processed.
* A double :: defines a global label. If this is a new label
* then create a symbol structure.
* pass 0:
* Flag multiply defined labels.
* pass 1:
* Load area, address, and fuzz values
* into structure symp.
* pass 2:
* Check for assembler phase error and
* multiply defined error.
*/
if (c == ':') {
if (flevel)
return;
if ((c = get()) != ':') {
unget(c);
c = 0;
}
symp = lookup(id);
if (symp == &dot)
qerr();
if (pass == 0) {
if ((symp->s_type != S_NEW) && ((symp->s_flag & S_ASG) == 0))
symp->s_flag |= S_MDF;
}
if (symp->s_flag & S_MDF)
err('m');
symp->s_type = S_USER;
phase(symp->s_area, symp->s_addr);
fuzz = symp->s_addr - dot.s_addr;
symp->s_area = dot.s_area;
symp->s_addr = dot.s_addr;
if (c) {
symp->s_flag |= S_GBL;
}
lmode = ALIST;
goto loop;
}
/*
* If the next character is a = then an equate is being processed.
*
* Syntax:
* [labels] sym = value defines an equate.
* [labels] sym == value defines a global equate.
* [labels] sym =: value defines an internal machine equate.
* If this is a new variable then create a symbol structure.
*/
if (c == '=') {
if (flevel)
return;
switch (c = get()) {
case '=': equtype = O_GBLEQU; break;
case ':': equtype = O_LCLEQU; break;
default: equtype = O_EQU; unget(c); break;
}
equate(id, &e1, equtype);
goto loop;
}
unget(c);
/*
* Check for Equates if 'id' is not an Assembler Directive
*
* Syntax:
* [labels] sym .equ value defines an equate
* [labels] sym .glbequ value defines a global equate
* [labels] sym .lclequ value defines a local equate
*/
if ((mlookup(id) == NULL) && (nlookup(id) == NULL)) {
if (flevel)
return;
/*
* Alternates for =, ==, and =:
*/
equ_ip = ip; /* Save current char pointer for case equate not found. */
getid(equ, -1);
if ((mp = mlookup(equ)) == NULL || mp->m_type != S_EQU) {
ip = equ_ip;
} else {
equate(id, &e1, mp->m_valu);
goto loop;
}
}
/*
* Completed scan for labels , equates, and symbols.
*/
lmode = flevel ? SLIST : CLIST;
/*
* An assembler directive, mnemonic, or macro is
* required to continue processing line.
*/
mp = mlookup(id);
np = nlookup(id);
if ((mp == NULL) && (np == NULL)) {
if (!flevel) {
err('o');
}
return;
}
/*
* If we have gotten this far then we have found an
* assembler directive an assembler mnemonic or
* an assembler macro.
*
* Check for .if[], .iif[], .else, .endif,
* .list, .nlist, and .page directives.
*/
m_type = (mp != NULL) ? mp->m_type : ~0;
switch (m_type) {
case S_CONDITIONAL:
/*
* BGP - .ifeq, .ifne, .ifgt, .iflt, .ifge, .ifle
*/
if (mp->m_valu < O_IFEND) {
if (mp->m_valu == O_IF) {
/*
* Process conditionals of the form
*
* .if cnd(,) arg1 (, arg2)
*
* where cnd is one of the following:
*
* eq ne
* gt lt ge le
* def ndef
* b nb idn dif
* t f tf
*/
p = ip;
strcpy(id,".if");
getid(&id[3],getnb());
xp = mlookup(id);
if ((xp != NULL) &&
(xp->m_type == S_CONDITIONAL) &&
(xp->m_valu != O_IF)) {
mp = xp;
comma(0);
} else {
ip = p;
}
}
if (flevel) {
n = 0;
} else {
switch (mp->m_valu) {
case O_IF:
case O_IFNE: /* .if ne,.... */
case O_IFEQ: /* .if eq,.... */
case O_IFGT: /* .if gt,.... */
case O_IFLT: /* .if lt,.... */
case O_IFGE: /* .if ge,.... */
case O_IFLE: /* .if le,.... */
n = absexpr();
switch (mp->m_valu) {
default:
case O_IF:
case O_IFNE: n = (((v_sint) n) != 0); break;
case O_IFEQ: n = (((v_sint) n) == 0); break;
case O_IFGT: n = (((v_sint) n) > 0); break;
case O_IFLT: n = (((v_sint) n) < 0); break;
case O_IFGE: n = (((v_sint) n) >= 0); break;
case O_IFLE: n = (((v_sint) n) <= 0); break;
}
break;
case O_IFF: /* .if f */
case O_IFT: /* .if t */
case O_IFTF: /* .if tf */
n = 0;
break;
default:
n = 0;
qerr();
break;
}
}
switch (mp->m_valu) {
default:
if (tlevel < MAXIF) {
++tlevel;
ifcnd[tlevel] = (int) n;
iflvl[tlevel] = flevel;
if (!n) {
++flevel;
}
} else {
err('i');
}
if (!iflvl[tlevel]) {
lmode = ELIST;
laddr = n;
} else {
lmode = SLIST;
}
break;
case O_IFF: /* .if f */
case O_IFT: /* .if t */
case O_IFTF: /* .if tf */
if (tlevel == 0) {
err('i');
lmode = SLIST;
break;
}
if (iflvl[tlevel] == 0) {
if (ifcnd[tlevel]) {
switch (mp->m_valu) {
default:
case O_IFF: flevel = 1; break;
case O_IFT: flevel = 0; break;
case O_IFTF: flevel = 0; break;
}
} else {
switch (mp->m_valu) {
default:
case O_IFF: flevel = 0; break;
case O_IFT: flevel = 1; break;
case O_IFTF: flevel = 0; break;
}
}
lmode = ELIST;
laddr = flevel ? 0 : 1;
} else {
lmode = SLIST;
}
break;
}
return;
} else
if (mp->m_valu < O_IIFEND) {
if (mp->m_valu == O_IIF) {
/*
* Process conditionals of the form
*
* .iif cnd(,) arg1 (, arg2)
*
* where cnd is one of the following:
*
* eq ne
* gt lt ge le
* def ndef
* b nb idn dif
* t f tf
*/
p = ip;
strcpy(id,".iif");
getid(&id[4],getnb());
xp = mlookup(id);
if ((xp != NULL) &&
(xp->m_type == S_CONDITIONAL) &&
(xp->m_valu != O_IIF)) {
mp = xp;
comma(0);
} else {
ip = p;
}
}
switch (mp->m_valu) {
case O_IIFF: /* .iif f */
case O_IIFT: /* .iif t */
case O_IIFTF: /* .iif tf */
if (tlevel == 0) {
err('i');
lmode = SLIST;
return;
}
if (iflvl[tlevel] == 0) {
ftflevel = flevel + 1;
if (ifcnd[tlevel] != 0) {
switch (mp->m_valu) {
default:
case O_IIFF: flevel = 1; break;
case O_IIFT: flevel = 0; break;
case O_IIFTF: flevel = 0; break;
}
} else {
switch (mp->m_valu) {
default:
case O_IIFF: flevel = 0; break;
case O_IIFT: flevel = 1; break;
case O_IIFTF: flevel = 0; break;
}
}
}
n = flevel ? 0 : 1;
/*
* Skip trailing ','
*/
comma(0);
lmode = SLIST;
if (n) {
goto loop;
}
return;
default:
if (flevel) {
return;
}
break;
}
switch (mp->m_valu) {
case O_IIF:
case O_IIFNE: /* .iif ne,.... */
case O_IIFEQ: /* .iif eq,.... */
case O_IIFGT: /* .iif gt,.... */
case O_IIFLT: /* .iif lt,.... */
case O_IIFGE: /* .iif ge,.... */
case O_IIFLE: /* .iif le,.... */
n = absexpr();
switch (mp->m_valu) {
default:
case O_IIF:
case O_IIFNE: n = (((v_sint) n) != 0); break;
case O_IIFEQ: n = (((v_sint) n) == 0); break;
case O_IIFGT: n = (((v_sint) n) > 0); break;
case O_IIFLT: n = (((v_sint) n) < 0); break;
case O_IIFGE: n = (((v_sint) n) >= 0); break;
case O_IIFLE: n = (((v_sint) n) <= 0); break;
}
break;
default:
n = 0;
qerr();
break;
}
/*
* Skip trailing ','
*/
comma(0);
lmode = SLIST;
if (n) {
goto loop;
}
return;
}
switch (mp->m_valu) {
case O_ELSE:
if (tlevel != 0) {
if (ifcnd[tlevel]) {
flevel = iflvl[tlevel] + 1;
ifcnd[tlevel] = 0;
} else {
flevel = iflvl[tlevel];
ifcnd[tlevel] = 1;
}
if (!iflvl[tlevel]) {
lmode = ELIST;
laddr = ifcnd[tlevel];
return;
}
} else {
err('i');
}
lmode = SLIST;
return;
case O_ENDIF:
if (tlevel) {
flevel = iflvl[tlevel--];
} else {
err('i');
}
lmode = SLIST;
return;
default:
break;
}
qerr();
break;
case S_LISTING:
flags = 0;
while ((c=endline()) != 0) {
if (c == ',') {
c = getnb();
}
if (c == '(') {
do {
if ((c = getnb()) == '!') {
flags |= LIST_NOT;
} else {
unget(c);
getid(id, -1);
if (symeq(id, "err", 1)) { flags |= LIST_ERR; } else
if (symeq(id, "loc", 1)) { flags |= LIST_LOC; } else
if (symeq(id, "bin", 1)) { flags |= LIST_BIN; } else
if (symeq(id, "eqt", 1)) { flags |= LIST_EQT; } else
if (symeq(id, "cyc", 1)) { flags |= LIST_CYC; } else
if (symeq(id, "lin", 1)) { flags |= LIST_LIN; } else
if (symeq(id, "src", 1)) { flags |= LIST_SRC; } else
if (symeq(id, "pag", 1)) { flags |= LIST_PAG; } else
if (symeq(id, "lst", 1)) { flags |= LIST_LST; } else
if (symeq(id, "md" , 1)) { flags |= LIST_MD; } else
if (symeq(id, "me" , 1)) { flags |= LIST_ME; } else
if (symeq(id, "meb", 1)) { flags |= LIST_MEB; } else {
err('u');
}
}
c = endline();
} while (c == ',') ;
if (c != ')') {
qerr();
}
} else {
unget(c);
if (absexpr()) {
flags |= LIST_TORF;
} else {
flags &= ~LIST_TORF;
}
}
}
if (!(flags & LIST_TORF) && flevel) {
return;
}
if (flags & ~LIST_TORF) {
if (flags & LIST_NOT) {
switch(mp->m_valu) {
case O_LIST: lnlist = LIST_NONE; break;
case O_NLIST: lnlist = LIST_NORM; break;
default: break;
}
}
if (flags & LIST_BITS) {
switch(mp->m_valu) {
case O_LIST: lnlist |= (flags & LIST_BITS); break;
case O_NLIST: lnlist &= ~(flags & LIST_BITS); break;
default: break;
}
}
} else {
switch(mp->m_valu) {
case O_LIST: lnlist = LIST_NORM; break;
case O_NLIST: lnlist = LIST_NONE; break;
default: break;
}
}
lmode = (lnlist & LIST_LST) ? SLIST : NLIST;
return;
case S_PAGE:
lmode = NLIST;
if (more()) {
n = absexpr() ? 1 : 0;
} else {
n = 0;
}
if (!n && flevel)
return;
lop = NLPP;
return;
default:
break;
}
if (flevel)
return;
/*
* If we are not in a false state for .if/.else then
* process the assembler directives here.
*/
switch (m_type) {
case S_HEADER:
switch(mp->m_valu) {
case O_TITLE:
p = tb;
if ((c = getnb()) != 0) {
do {
if (p < &tb[NTITL-1])
*p++ = c;
} while ((c = get()) != 0);
}
*p = 0;
unget(c);
lmode = SLIST;
break;
case O_SBTTL:
p = stb;
if ((c = getnb()) != 0) {
do {
if (p < &stb[NSBTL-1])
*p++ = c;
} while ((c = get()) != 0);
}
*p = 0;
unget(c);
lmode = SLIST;
break;
default:
break;
}
break;
case S_MODUL:
getst(id, getnb()); // a module can start with a digit
if (pass == 0) {
if (module[0]) {
err('m');
} else {
strncpy(module, id, NCPS);
}
}
lmode = SLIST;
break;
case S_INCL:
lmode = SLIST;
if (incfil > maxinc) {
maxinc = incfil;
}
/*
* Copy the .include file specification
*/
getdstr(fn, FILSPC + FILSPC);
/*
* Open File
*/
if ((fp = search_path_fopen(fn, "r")) == NULL) {
--incfil;
err('i');
} else {
asmi = (struct asmf *) new (sizeof (struct asmf));
asmi->next = asmc;
asmi->objtyp = T_INCL;
asmi->line = srcline;
asmi->flevel = flevel;
asmi->tlevel = tlevel;
asmi->lnlist = lnlist;
asmi->fp = fp;
asmi->afp = afptmp;
strcpy(asmi->afn,afntmp);
if (lnlist & LIST_PAG) {
lop = NLPP;
}
}
break;
/* sdas specific */
case S_OPTSDCC:
optsdcc = strsto(ip);
lmode = SLIST;
return; /* line consumed */
/* end sdas specific */
case S_AREA:
getid(id, -1);
uaf = 0;
uf = A_CON|A_REL;
if ((c = getnb()) == '(') {
do {
getid(opt, -1);
mp = mlookup(opt);
if (mp && mp->m_type == S_ATYP) {
++uaf;
v = mp->m_valu;
uf |= (int) v;
} else {
err('u');
}
} while ((c = getnb()) == ',');
if (c != ')')
qerr();
} else {
unget(c);
}
if ((ap = alookup(id)) != NULL) {
if (uaf && uf != ap->a_flag)
err('m');
} else {
ap = (struct area *) new (sizeof(struct area));
ap->a_ap = areap;
ap->a_id = strsto(id);
ap->a_ref = areap->a_ref + 1;
/* sdas specific */
ap->a_addr = 0;
/* end sdas specific */
ap->a_size = 0;
ap->a_fuzz = 0;
ap->a_flag = uaf ? uf : (A_CON|A_REL);
areap = ap;
}
newdot(ap);
lmode = SLIST;
if (dot.s_area->a_flag & A_ABS)
abs_ap = ap;
break;
case S_ORG:
if (dot.s_area->a_flag & A_ABS) {
char buf[NCPS];
outall();
laddr = absexpr();
sprintf(buf, "%s%x", abs_ap->a_id, org_cnt++);
if ((ap = alookup(buf)) == NULL) {
ap = (struct area *) new (sizeof(struct area));
*ap = *areap;
ap->a_ap = areap;
ap->a_id = strsto(buf);
ap->a_ref = areap->a_ref + 1;
ap->a_size = 0;
ap->a_fuzz = 0;
areap = ap;
}
newdot(ap);
dot.s_addr = dot.s_org = laddr;
} else {
err('o');
}
outall();
lmode = ALIST;
break;
case S_RADIX:
if (more()) {
switch (getnb()) {
case 'b':
case 'B':
radix = 2;
break;
case '@':
case 'o':
case 'O':
case 'q':
case 'Q':
radix = 8;
break;
case 'd':
case 'D':
radix = 10;
break;
case 'h':
case 'H':
case 'x':
case 'X':
radix = 16;
break;
default:
radix = 10;
qerr();
break;
}
} else {
radix = 10;
}
lmode = SLIST;
break;
case S_GLOBL:
do {
getid(id, -1);
sp = lookup(id);
sp->s_flag &= ~S_LCL;
sp->s_flag |= S_GBL;
} while (comma(0));
lmode = SLIST;
break;
case S_LOCAL:
do {
getid(id, -1);
sp = lookup(id);
sp->s_flag &= ~S_GBL;
sp->s_flag |= S_LCL;
} while (comma(0));
lmode = SLIST;
break;
case S_EQU:
/*
* Syntax:
* [labels] .equ sym, value defines an equate
* [labels] .glbequ sym, value defines a global equate
* [labels] .lclequ sym, value defines a local equate
*/
getid(id, -1);
comma(1);
equate(id, &e1, mp->m_valu);
break;
case S_DATA:
switch (mp->m_valu) {
case O_1BYTE:
case O_2BYTE:
do {
clrexpr(&e1);
expr(&e1, 0);
if (mp->m_valu == O_1BYTE) {
outrb(&e1, R_NORM);
} else {
outrw(&e1, R_NORM);
}
} while ((c = getnb()) == ',');
unget(c);
break;
default:
break;
}
break;
/* sdas z80 specific */
case S_FLOAT:
do {
double f1, f2;
unsigned int mantissa, exponent;
char readbuffer[80];
getid(readbuffer, ' '); /* Hack :) */
if ((c = getnb()) == '.') {
getid(&readbuffer[strlen(readbuffer)], '.');
}
else
unget(c);
f1 = strtod(readbuffer, (char **)NULL);
/* Convert f1 to a gb-lib type fp
* 24 bit mantissa followed by 7 bit exp and 1 bit sign
*/
if (f1 != 0) {
f2 = floor(log(fabs(f1)) / log(2)) + 1;
mantissa = (unsigned int) ((0x1000000 * fabs(f1)) / exp(f2 * log(2)));
mantissa &= 0xffffff;
exponent = (unsigned int) (f2 + 0x40) ;
if (f1 < 0)
exponent |=0x80;
}
else {
mantissa = 0;
exponent = 0;
}
outab(mantissa & 0xff);
outab((mantissa >> 8) & 0xff);
outab((mantissa >> 16) & 0xff);
outab(exponent & 0xff);
} while ((c = getnb()) == ',');
unget(c);
break;
/* end sdas z80 specific */
/* sdas hc08 specific */
case S_ULEB128:
case S_SLEB128:
do {
a_uint val = absexpr();
int bit = sizeof(val)*8 - 1;
int impliedBit;
if (mp->m_type == S_ULEB128) {
impliedBit = 0;
} else {
impliedBit = (val & (1 << bit)) ? 1 : 0;
}
while ((bit>0) && (((val & (1 << bit)) ? 1 : 0) == impliedBit)) {
bit--;
}
if (mp->m_type == S_SLEB128) {
bit++;
}
while (bit>=0) {
if (bit<7) {
outab(val & 0x7f);
} else {
outab(0x80 | (val & 0x7f));
}
bit -= 7;
val >>= 7;
}
} while ((c = getnb()) == ',');
unget(c);
break;
/* end sdas hc08 specific */
case S_BLK:
clrexpr(&e1);
expr(&e1, 0);
outchk(ASXHUGE,ASXHUGE);
dot.s_addr += e1.e_addr*mp->m_valu;
lmode = BLIST;
break;
case S_ASCIX:
switch(mp->m_valu) {
case O_ASCII:
case O_ASCIZ:
if ((d = getnb()) == '\0')
qerr();
while ((c = getmap(d)) >= 0)
outab(c);
if (mp->m_valu == O_ASCIZ)
outab(0);
break;
case O_ASCIS:
if ((d = getnb()) == '\0')
qerr();
c = getmap(d);
while (c >= 0) {
int n2;
if ((n2 = getmap(d)) >= 0) {
outab(c);
} else {
outab(c | 0x80);
}
n = n2;
c = n2;
}
break;
default:
break;
}
break;
case S_BOUNDARY:
switch(mp->m_valu) {
case O_EVEN:
outall();
laddr = dot.s_addr = (dot.s_addr + 1) & ~1;
lmode = ALIST;
break;
case O_ODD:
outall();
laddr = dot.s_addr |= 1;
lmode = ALIST;
break;
case O_BNDRY:
v = absexpr();
n = dot.s_addr % v;
if (n != 0) {
dot.s_addr += (v - n);
}
outall();
laddr = dot.s_addr;
lmode = ALIST;
break;
default:
break;
}
break;
case S_MACRO:
lmode = SLIST;
mcrprc((int) mp->m_valu);
return;
/*
* If not an assembler directive then go to the
* macro function or machine dependent function
* which handles all the assembler mnemonics.
*
* MACRO Definitions take precedence
* over machine specific mnemonics.
*/
default:
if (np != NULL) {
macro(np);
} else {
machine(mp);
}
/*
* Include Files and Macros are not Debugged
*/
if (asmc->objtyp == T_ASM) {
#if NOICE
/*
* NoICE JLH
* if -j, generate a line number symbol
*/
if (jflag && (pass == 1)) {
DefineNoICE_Line();
}
#endif
#if SDCDB
/*
* SDCC Debug Information
* if cdb information then generate the line info
*/
if (yflag && (pass == 1)) {
DefineSDCC_Line();
}
#endif
}
break;
}
if (is_sdas()) {
if ((c = endline()) != 0) {
err('q');
}
}
else {
goto loop;
}
}
/*)Function VOID equate(id,e1,equtype)
*
* char * id ident to equate
* struct expr * e1 value of equate
* a_uint equtype equate type (O_EQU,O_LCLEQU,O_GBLEQU)
*
* The function equate() installs an equate of a
* given type.
*
* equate has no return value
*
* local variables:
* struct sym * sp symbol being equated
*
* global variables:
* lmode set to ELIST
*
* functions called:
* VOID clrexpr() asexpr.c
* VOID expr() asexpr.c
* VOID err() assubr.c
* sym * lookup() assym.c
* VOID outall() asout.c
* VOID rerr() assubr.c
*
* side effects:
* A new symbol may be created.
* Symbol parameters are updated.
*/
VOID
equate(char *id, struct expr *e1, a_uint equtype)
{
struct sym *sp;
clrexpr(e1);
expr(e1, 0);
sp = lookup(id);
if (sp == &dot) {
outall();
if (e1->e_flag || e1->e_base.e_ap != dot.s_area)
err('.');
} else {
switch(equtype) {
case O_EQU:
default:
break;
case O_GBLEQU:
sp->s_flag &= ~S_LCL;
sp->s_flag |= S_GBL;
break;
case O_LCLEQU:
sp->s_flag &= ~S_GBL;
sp->s_flag |= S_LCL;
break;
}
if (e1->e_flag && (e1->e_base.e_sp->s_type == S_NEW)) {
rerr();
} else {
sp->s_area = e1->e_base.e_ap;
}
sp->s_flag |= S_ASG;
sp->s_type = S_USER;
}
sp->s_addr = laddr = e1->e_addr;
lmode = ELIST;
}
/*)Function FILE * afile(fn, ft, wf)
*
* char * fn file specification string
* char * ft file type string
* int wf read(0)/write(1) flag
*
* The function afile() opens a file for reading or writing.
*
* afile() returns a file handle for the opened file or aborts
* the assembler on an open error.
*
* local variables:
* FILE * fp file handle for opened file
*
* global variables:
* char afn[] afile() constructed filespec
* int afp afile() constructed path length
* char afntmp[] afilex() constructed filespec
* int afptmp afilex() constructed path length
*
* functions called:
* VOID asexit() asmain.c
* VOID afilex() asmain.c
* FILE * fopen() c_library
* int fprintf() c_library
* char * strcpy() c_library
*
* side effects:
* File is opened for read or write.
*/
FILE *
afile(char *fn, char *ft, int wf)
{
FILE *fp;
afilex(fn, ft);
if ((fp = fopen(afntmp, wf?"w":"r")) == NULL) {
fprintf(stderr, "?ASxxxx-Error- : \"%s\"\n", wf?"create":"open", afntmp);
asexit(ER_FATAL);
}
strcpy(afn, afntmp);
afp = afptmp;
return (fp);
}
/*)Function VOID afilex(fn, ft)
*
* char * fn file specification string
* char * ft file type string
*
* The function afilex() processes the file specification string:
* (1) If the file type specification string ft
* is not NULL then a file specification is
* constructed with the file path\name in fn
* and the extension in ft.
* (2) If the file type specification string ft
* is NULL then the file specification is
* constructed from fn. If fn does not have
* a file type then the default source file
* type dsft is appended to the file specification.
*
* afilex() aborts the assembler on a file specification length error.
*
* local variables:
* int c character value
* char * p1 pointer into filespec string afntmp
* char * p2 pointer to filetype string ft
*
* global variables:
* char afntmp[] afilex() constructed filespec
* int afptmp afilex() constructed path length
* char dsft[] default assembler file type string
*
* functions called:
* VOID asexit() asmain.c
* int fndidx() asmain.c
* int fprintf() c_library
* char * strcpy() c_library
* int strlen() c_library
*
* side effects:
* File specification string may be modified.
*/
VOID
afilex(char *fn, char *ft)
{
char *p1, *p2;
int c;
if (strlen(fn) > (FILSPC-7)) {
fprintf(stderr, "?ASxxxx-Error- : \"%s\"\n", fn);
asexit(ER_FATAL);
}
/*
* Save the File Name Index
*/
strcpy(afntmp, fn);
afptmp = fndidx(afntmp);
/*
* Skip to File Extension separator
*/
p1 = strrchr(&afntmp[afptmp], FSEPX);
/*
* Copy File Extension
*/
p2 = ft;
// choose a file-extension
if (*p2 == 0) {
// no extension supplied
if (p1 == NULL) {
// no extension in fn: use default extension
p2 = dsft;
} else {
p2 = strrchr(&fn[afptmp], FSEPX) + 1;
}
}
if (p1 == NULL) {
p1 = &afntmp[strlen(afntmp)];
}
*p1++ = FSEPX;
while ((c = *p2++) != 0) {
if (p1 < &afntmp[FILSPC-1])
*p1++ = c;
}
*p1++ = 0;
}
/*)Function int fndidx(str)
*
* char * str file specification string
*
* The function fndidx() scans the file specification string
* to find the index to the file name. If the file
* specification contains a 'path' then the index will
* be non zero.
*
* fndidx() returns the index value.
*
* local variables:
* char * p1 temporary pointer
* char * p2 temporary pointer
*
* global variables:
* none
*
* functions called:
* char * strrchr() c_library
*
* side effects:
* none
*/
int
fndidx(char *str)
{
char *p1, *p2;
/*
* Skip Path Delimiters
*/
p1 = str;
if ((p2 = strrchr(p1, ':')) != NULL) { p1 = p2 + 1; }
if ((p2 = strrchr(p1, '/')) != NULL) { p1 = p2 + 1; }
if ((p2 = strrchr(p1, '\\')) != NULL) { p1 = p2 + 1; }
return((int) (p1 - str));
}
/*)Function VOID newdot(nap)
*
* area * nap pointer to the new area structure
*
* The function newdot():
* (1) copies the current values of fuzz and the last
* address into the current area referenced by dot
* (2) loads dot with the pointer to the new area and
* loads the fuzz and last address parameters
* (3) outall() is called to flush any remaining
* bufferred code from the old area to the output
*
* local variables:
* area * oap pointer to old area
*
* global variables:
* sym dot defined as sym[0]
* a_uint fuzz tracks pass to pass changes in the
* address of symbols caused by
* variable length instruction formats
*
* functions called:
* none
*
* side effects:
* Current area saved, new area loaded, buffers flushed.
*/
VOID
newdot(struct area *nap)
{
struct area *oap;
oap = dot.s_area;
/* fprintf (stderr, "%s dot.s_area->a_size: %d dot.s_addr: %d\n",
oap->a_id, dot.s_area->a_size, dot.s_addr); */
oap->a_fuzz = fuzz;
if (oap->a_flag & A_OVR) {
// the size of an overlay is the biggest size encountered
if (oap->a_size < dot.s_addr) {
oap->a_size = dot.s_addr;
}
}
else if (oap->a_flag & A_ABS) {
oap->a_addr = dot.s_org;
oap->a_size += dot.s_addr - dot.s_org;
dot.s_addr = dot.s_org = 0;
}
else {
oap->a_addr = 0;
oap->a_size = dot.s_addr;
}
if (nap->a_flag & A_OVR) {
// a new overlay starts at 0, no fuzz
dot.s_addr = 0;
fuzz = 0;
}
else if (nap->a_flag & A_ABS) {
// a new absolute starts at org, no fuzz
dot.s_addr = dot.s_org;
fuzz = 0;
}
else {
dot.s_addr = nap->a_size;
fuzz = nap->a_fuzz;
}
dot.s_area = nap;
outall();
}
/*)Function VOID phase(ap, a)
*
* area * ap pointer to area
* a_uint a address in area
*
* Function phase() compares the area ap and address a
* with the current area dot.s_area and address dot.s_addr
* to determine if the position of the symbol has changed
* between assembler passes.
*
* local variables:
* none
*
* global varaibles:
* sym * dot defined as sym[0]
*
* functions called:
* none
*
* side effects:
* The p error is invoked if the area and/or address
* has changed.
*/
VOID
phase(struct area *ap, a_uint a)
{
if (ap != dot.s_area || a != dot.s_addr)
err('p');
}
char *usetxt[] = {
"Usage: [-Options] file",
"Usage: [-Options] outfile file1 [file2 file3 ...]",
" -d Decimal listing",
" -q Octal listing",
" -x Hex listing (default)",
" -g Undefined symbols made global",
" -a All user symbols made global",
" -b Display .define substitutions in listing",
" -bb and display without .define substitutions",
" -c Disable instruction cycle count in listing",
#if NOICE
" -j Enable NoICE Debug Symbols",
#endif
#if SDCDB
" -y Enable SDCC Debug Symbols",
#endif
" -l Create list file/outfile[.lst]",
" -o Create object file/outfile[.rel]",
" -s Create symbol file/outfile[.sym]",
" -p Disable automatic listing pagination",
" -u Disable .list/.nlist processing",
" -w Wide listing format for symbol table",
" -z Disable case sensitivity for symbols",
" -f Flag relocatable references by ` in listing file",
" -ff Flag relocatable references by mode in listing file",
" -I Add the named directory to the include file",
" search path. This option may be used more than once.",
" Directories are searched in the order given.",
"",
NULL
};
/*)Function VOID usage(n)
*
* int n exit code
*
* The function usage() outputs to the stderr device the
* assembler name and version and a list of valid assembler options.
*
* local variables:
* char ** dp pointer to an array of
* text string pointers.
*
* global variables:
* char cpu[] assembler type string
* char * usetxt[] array of string pointers
*
* functions called:
* VOID asexit() asmain.c
* int fprintf() c_library
*
* side effects:
* program is terminated
*/
VOID
usage(int n)
{
char **dp;
fprintf(stderr, "\n%s Assembler %s (%s)\n\n", is_sdas() ? "sdas" : "ASxxxx", VERSION, cpu);
fprintf(stderr, "\nCopyright (C) %s Alan R. Baldwin", COPYRIGHT);
fprintf(stderr, "\nThis program comes with ABSOLUTELY NO WARRANTY.\n\n");
for (dp = usetxt; *dp; dp++)
fprintf(stderr, "%s\n", *dp);
asexit(n);
}