X-Git-Url: http://shamusworld.gotdns.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;ds=sidebyside;f=macro.c;h=6b592d114aedacc3c6a48321e04c351bfaa241bf;hb=c9541a9d4f01e190ffefc20ce07896c7f04d9557;hp=8731fb87e43ad13b21109e440884ddf8dec6812c;hpb=49cce96fba11282e4244187f15be418d5ae5bb8d;p=rmac diff --git a/macro.c b/macro.c index 8731fb8..6b592d1 100644 --- a/macro.c +++ b/macro.c @@ -1,58 +1,65 @@ // -// RMAC - Reboot's Macro Assembler for the Atari Jaguar Console System +// RMAC - Reboot's Macro Assembler for all Atari computers // MACRO.C - Macro Definition and Invocation -// Copyright (C) 199x Landon Dyer, 2011 Reboot and Friends +// Copyright (C) 199x Landon Dyer, 2011-2017 Reboot and Friends // RMAC derived from MADMAC v1.07 Written by Landon Dyer, 1986 -// Source Utilised with the Kind Permission of Landon Dyer +// Source utilised with the kind permission of Landon Dyer // #include "macro.h" -#include "token.h" +#include "debug.h" +#include "direct.h" #include "error.h" #include "expr.h" #include "listing.h" -#include "symbol.h" #include "procln.h" -#include "direct.h" -#include "debug.h" +#include "symbol.h" +#include "token.h" + + +LONG curuniq; // Current macro's unique number +int macnum; // Unique number for macro definition +TOKEN * argPtrs[128]; // 128 arguments ought to be enough for anyone +static int argp; -LONG curuniq; // Current macro's unique number -TOKEN ** argp; // Free spot in argptrs[] -int macnum; // Unique number for macro definition +static LONG macuniq; // Unique-per-macro number +static SYM * curmac; // Macro currently being defined +static VALUE argno; // Formal argument count -static LONG macuniq; // Unique-per-macro number -static SYM * curmac; // Macro currently being defined -static char ** curmln; // Previous macro line (or NULL) -static VALUE argno; // Formal argument count +static LLIST * firstrpt; // First .rept line +static LLIST * nextrpt; // Last .rept line +static int rptlevel; // .rept nesting level -static LONG * firstrpt; // First .rept line -static LONG * nextrpt; // Last .rept line -static int rptlevel; // .rept nesting level +// Function prototypes +static int KWMatch(char *, char *); +static int LNCatch(int (*)(), char *); // -// Initialize Macro Processor +// Initialize macro processor // -void init_macro(void) +void InitMacro(void) { macuniq = 0; macnum = 1; - argp = NULL; - ib_macro(); + argp = 0; } // -// Exit from a Macro; -// o pop any intervening include files and repeat blocks; -// o restore argument stack; -// o pop the macro. +// Exit from a macro; +// -- pop any intervening include files and repeat blocks; +// -- restore argument stack; +// -- pop the macro. // -int exitmac(void) +int ExitMacro(void) { - IMACRO * imacro; - TOKEN ** p; - +WARNING(!!! Bad macro exiting !!!) +/* +This is a problem. Currently, the argument logic just keeps the current +arguments and doesn't save anything if a new macro is called in the middle +of another (nested macros). Need to fix that somehow. +*/ // Pop intervening include files and .rept blocks while (cur_inobj != NULL && cur_inobj->in_type != SRC_IMACRO) fpop(); @@ -65,30 +72,28 @@ int exitmac(void) // o old unique number // ...and then pop the macro. - imacro = cur_inobj->inobj.imacro; + IMACRO * imacro = cur_inobj->inobj.imacro; curuniq = imacro->im_olduniq; - p = --argp; - argp = (TOKEN **)*argp; - - fpop(); - - mjump_align = 0; +// /*TOKEN ** p = */argp--; +// argp = (TOKEN **)*argp; + DEBUG { printf("ExitMacro: argp: %d -> ", argp); } + argp -= imacro->im_nargs; + DEBUG { printf("%d (nargs = %d)\n", argp, imacro->im_nargs); } - return 0; + return fpop(); } // -// Add a Formal Argument to a Macro Definition +// Add a formal argument to a macro definition // int defmac2(char * argname) { - SYM * arg; - if (lookup(argname, MACARG, (int)curmac->sattr) != NULL) - return(error("multiple formal argument definition")); - arg = newsym(argname, MACARG, (int)curmac->sattr); + return error("multiple formal argument definition"); + + SYM * arg = NewSymbol(argname, MACARG, (int)curmac->sattr); arg->svalue = argno++; return OK; @@ -98,39 +103,72 @@ int defmac2(char * argname) // // Add a line to a macro definition; also print lines to listing file (if // enabled). The last line of the macro (containing .endm) is not included in -// the macro. A label on that line will be lost. `endflg' is misleading here. -// It is -1 for all lines but the last one (.endm), when it is 0. +// the macro. A label on that line will be lost. +// notEndFlag is -1 for all lines but the last one (.endm), when it is 0. // -int defmac1(char * ln, int endflg) +int defmac1(char * ln, int notEndFlag) { - PTR p; - LONG len; - if (list_flag) { - listeol(); // Flush previous source line - lstout('.'); // Mark macro definition with period + listeol(); // Flush previous source line + lstout('.'); // Mark macro definition with period } - if (endflg) + // This is just wrong, wrong, wrong. It makes up a weird kind of string with + // a pointer on front, and then uses a ** to manage them: This is a recipe + // for disaster. + // How to manage it then? + // Could use a linked list, like Landon uses everywhere else. +/* +How it works: +Allocate a space big enough for the string + NULL + pointer. +Set the pointer to NULL. +Copy the string to the space after the pointer. +If this is the 1st time through, set the SYM * "svalue" to the pointer. +If this is the 2nd time through, derefence the ** to point to the memory you +just allocated. Then, set the ** to the location of the memory you allocated +for the next pass through. + +This is a really low level way to do a linked list, and by bypassing all the +safety features of the language. Seems like we can do better here. +*/ + if (notEndFlag) { +#if 0 len = strlen(ln) + 1 + sizeof(LONG); -// p.cp = amem(len); p.cp = malloc(len); *p.lp = 0; strcpy(p.cp + sizeof(LONG), ln); // Link line of text onto end of list if (curmln == NULL) - curmac->svalue = (VALUE)p.cp; + curmac->svalue = p.cp; else *curmln = p.cp; curmln = (char **)p.cp; - return 1; // Keep looking + return 1; // Keep looking +#else + if (curmac->lineList == NULL) + { + curmac->lineList = malloc(sizeof(LLIST)); + curmac->lineList->next = NULL; + curmac->lineList->line = strdup(ln); + curmac->last = curmac->lineList; + } + else + { + curmac->last->next = malloc(sizeof(LLIST)); + curmac->last->next->next = NULL; + curmac->last->next->line = strdup(ln); + curmac->last = curmac->last->next; + } + + return 1; // Keep looking +#endif } - else - return 0; // Stop looking at the end + + return 0; // Stop looking at the end } @@ -145,26 +183,25 @@ int defmac1(char * ln, int endflg) // Helper functions: // ----------------- // `defmac1' adds lines of text to the macro definition -// `defmac2' processes the formal arguments (and sticks them into the symbol table) +// `defmac2' processes the formal arguments (and sticks them into the symbol +// table) // -int defmac(void) +int DefineMacro(void) { - char * p; - SYM * mac; - - // Setup entry in symbol table, make sure the macro isn't a duplicate entry, and that - // it doesn't override any processor mnemonic or assembler directive. + // Setup entry in symbol table, make sure the macro isn't a duplicate + // entry, and that it doesn't override any processor mnemonic or assembler + // directive. if (*tok++ != SYMBOL) return error("missing symbol"); - p = (char *)*tok++; + char * name = string[*tok++]; - if (lookup(p, MACRO, 0) != NULL) - return error("multiple macro definition"); + if (lookup(name, MACRO, 0) != NULL) + return error("duplicate macro definition"); - curmac = mac = newsym(p, MACRO, 0); - mac->svalue = 0; - mac->sattr = (WORD)(macnum++); + curmac = NewSymbol(name, MACRO, 0); + curmac->svalue = 0; + curmac->sattr = (WORD)(macnum++); // Parse and define formal arguments in symbol table if (*tok != EOL) @@ -176,8 +213,9 @@ int defmac(void) // Suck in the macro definition; we're looking for an ENDM symbol on a line // by itself to terminate the definition. - curmln = NULL; - lncatch(defmac1, "endm "); +// curmln = NULL; + curmac->lineList = NULL; + LNCatch(defmac1, "endm "); return 0; } @@ -186,58 +224,64 @@ int defmac(void) // // Add lines to a .rept definition // -int defr1(char * ln, int kwno) +int defr1(char * line, int kwno) { - LONG len; - LONG * p; - if (list_flag) { - listeol(); // Flush previous source line - lstout('#'); // Mark this a 'rept' block + listeol(); // Flush previous source line + lstout('#'); // Mark this a 'rept' block } - switch (kwno) + if (kwno == 0) // .endr { - case 0: // .endr if (--rptlevel == 0) - return(0); - goto addln; - case 1: // .rept - ++rptlevel; - default: - addln: - // Allocate length of line + 1('\0') + LONG - len = strlen(ln) + 1 + sizeof(LONG); -// p = (LONG *)amem(len); - p = (LONG *)malloc(len); - *p = 0; - - strcpy((char *)(p + 1), ln); - - if (nextrpt == NULL) - { - firstrpt = p; // First line of rept statement - } - else - { - *nextrpt = (LONG)p; - } - - nextrpt = p; - - return rptlevel; + return 0; + } + else if (kwno == 1) // .rept + rptlevel++; + +//DEBUG { printf(" defr1: line=\"%s\", kwno=%d, rptlevel=%d\n", line, kwno, rptlevel); } + +#if 0 +//MORE stupidity here... +WARNING("!!! Casting (char *) as LONG !!!") + // Allocate length of line + 1('\0') + LONG + LONG * p = (LONG *)malloc(strlen(line) + 1 + sizeof(LONG)); + *p = 0; + strcpy((char *)(p + 1), line); + + if (nextrpt == NULL) + firstrpt = p; // First line of rept statement + else + *nextrpt = (LONG)p; + + nextrpt = p; +#else + if (firstrpt == NULL) + { + firstrpt = malloc(sizeof(LLIST)); + firstrpt->next = NULL; + firstrpt->line = strdup(line); + nextrpt = firstrpt; } + else + { + nextrpt->next = malloc(sizeof(LLIST)); + nextrpt->next->next = NULL; + nextrpt->next->line = strdup(line); + nextrpt = nextrpt->next; + } +#endif + + return rptlevel; } // -// Define a .rept block, this gets hairy because they can be nested +// Handle a .rept block; this gets hairy because they can be nested // -int defrept(void) +int HandleRept(void) { - INOBJ * inobj; - IREPT * irept; VALUE eval; // Evaluate repeat expression @@ -248,13 +292,14 @@ int defrept(void) firstrpt = NULL; nextrpt = NULL; rptlevel = 1; - lncatch(defr1, "endr rept "); + LNCatch(defr1, "endr rept "); +//DEBUG { printf("HandleRept: firstrpt=$%X\n", firstrpt); } // Alloc and init input object if (firstrpt) { - inobj = a_inobj(SRC_IREPT); // Create a new REPT input object - irept = inobj->inobj.irept; + INOBJ * inobj = a_inobj(SRC_IREPT); // Create a new REPT input object + IREPT * irept = inobj->inobj.irept; irept->ir_firstln = firstrpt; irept->ir_nextln = NULL; irept->ir_count = eval; @@ -265,62 +310,61 @@ int defrept(void) // -// Hand off lines of text to the function `lnfunc' until a line containing one -// of the directives in `dirlist' is encountered. Return the number of the -// keyword encountered (0..n) -// -// `dirlist' contains null-seperated terminated keywords. A final null -// terminates the list. Directives are compared to the keywords without regard -// to case. -// -// If `lnfunc' is NULL, then lines are simply skipped. -// If `lnfunc' returns an error, processing is stopped. -// -// `lnfunc' is called with an argument of -1 for every line but the last one, +// Hand off lines of text to the function 'lnfunc' until a line containing one +// of the directives in 'dirlist' is encountered. +// +// 'dirlist' contains space-separated terminated keywords. A final space +// terminates the list. Directives are case-insensitively compared to the +// keywords. +// +// If 'lnfunc' is NULL, then lines are simply skipped. +// If 'lnfunc' returns an error, processing is stopped. +// +// 'lnfunc' is called with an argument of -1 for every line but the last one, // when it is called with an argument of the keyword number that caused the // match. // -int lncatch(int (* lnfunc)(), char * dirlist) +static int LNCatch(int (* lnfunc)(), char * dirlist) { - char * p; - int k; - if (lnfunc != NULL) - ++lnsave; // Tell tokenizer to keep lines + lnsave++; // Tell tokenizer to keep lines - for(;;) + while (1) { - if (tokln() == TKEOF) + if (TokenizeLine() == TKEOF) { - errors("encountered end-of-file looking for '%s'", dirlist); + error("encountered end-of-file looking for '%s'", dirlist); fatal("cannot continue"); } // Test for end condition. Two cases to handle: // // symbol: - p = NULL; - k = -1; + char * p = NULL; + int k = -1; if (*tok == SYMBOL) { + // A string followed by a colon or double colon is a symbol and + // *not* a directive, see if we can find the directive after it if ((tok[2] == ':' || tok[2] == DCOLON)) { - if (tok[3] == SYMBOL) // label: symbol - p = (char *)tok[4]; + if (tok[3] == SYMBOL) + p = string[tok[4]]; } else { - p = (char *)tok[1]; // symbol + // Otherwise, just grab the directive + p = string[tok[1]]; } } if (p != NULL) { - if (*p == '.') // ignore leading '.'s - ++p; + if (*p == '.') // Ignore leading periods + p++; - k = kwmatch(p, dirlist); + k = KWMatch(p, dirlist); } // Hand-off line to function @@ -329,35 +373,30 @@ int lncatch(int (* lnfunc)(), char * dirlist) if (lnfunc != NULL) k = (*lnfunc)(lnbuf, k); - if (!k) + if (k == 0) break; } if (lnfunc != NULL) - --lnsave; // Tell tokenizer to stop keeping lines + lnsave--; // Tell tokenizer to stop keeping lines return 0; } // -// See if the string `kw' matches one of the keywords in `kwlist'. If so, -// return the number of the keyword matched. Return -1 if there was no match. +// See if the string `kw' matches one of the keywords in `kwlist'. If so, +// return the number of the keyword matched. Return -1 if there was no match. // Strings are compared without regard for case. // -int kwmatch(char * kw, char * kwlist) +static int KWMatch(char * kw, char * kwlist) { - char * p; - char c1; - char c2; - int k; - - for(k=0; *kwlist; ++k) + for(int k=0; *kwlist; k++) { - for(p=kw;;) + for(char * p=kw;;) { - c1 = *kwlist++; - c2 = *p++; + char c1 = *kwlist++; + char c2 = *p++; if (c2 >= 'A' && c2 <= 'Z') c2 += 32; @@ -370,11 +409,11 @@ int kwmatch(char * kw, char * kwlist) } // Skip to beginning of next keyword in `kwlist' - while (*kwlist && *kwlist != ' ') + while (*kwlist && (*kwlist != ' ')) ++kwlist; if (*kwlist== ' ') - ++kwlist; + kwlist++; } return -1; @@ -383,40 +422,44 @@ int kwmatch(char * kw, char * kwlist) // // Invoke a macro -// o parse, count and copy arguments -// o push macro's string-stream +// o parse, count and copy arguments +// o push macro's string-stream // -int invokemac(SYM * mac, WORD siz) +int InvokeMacro(SYM * mac, WORD siz) { TOKEN * p = NULL; - IMACRO * imacro; - INOBJ * inobj; int dry_run; - WORD nargs; WORD arg_siz = 0; - TOKEN ** argptr = NULL; - TOKEN * beg_tok; +// TOKEN ** argptr = NULL; +//Doesn't need to be global! (or does it???--it does) +// argp = 0; + DEBUG printf("InvokeMacro: argp: %d -> ", argp); - if ((!strcmp(mac->sname, "mjump") || !strcmp(mac->sname, "mpad")) && !in_main) - { - error("macro cannot be used outside of .gpumain"); - return ERROR; - } - - inobj = a_inobj(SRC_IMACRO); // Alloc and init IMACRO - imacro = inobj->inobj.imacro; + INOBJ * inobj = a_inobj(SRC_IMACRO); // Alloc and init IMACRO + IMACRO * imacro = inobj->inobj.imacro; imacro->im_siz = siz; - nargs = 0; - beg_tok = tok; // 'tok' comes from token.c - - for(dry_run=1;; --dry_run) + WORD nargs = 0; + TOKEN * beg_tok = tok; // 'tok' comes from token.c + TOKEN * startOfArg; + TOKEN * dest; + int stringNum = 0; + int argumentNum = 0; + + for(dry_run=1; ; dry_run--) { for(tok=beg_tok; *tok!=EOL;) { if (dry_run) nargs++; else + { +#if 0 *argptr++ = p; +#else + argPtrs[argp++] = p; + startOfArg = p; +#endif + } // Keep going while tok isn't pointing at a comma or EOL while (*tok != ',' && *tok != EOL) @@ -429,6 +472,7 @@ int invokemac(SYM * mac, WORD siz) { case CONST: case SYMBOL: +//Shamus: Possible bug. ACONST has 2 tokens after it, not just 1 case ACONST: if (dry_run) { @@ -436,7 +480,9 @@ int invokemac(SYM * mac, WORD siz) tok++; } else + { *p++ = *tok++; + } // FALLTHROUGH (picks up the arg after a CONST, SYMBOL or ACONST) default: if (dry_run) @@ -445,7 +491,9 @@ int invokemac(SYM * mac, WORD siz) tok++; } else + { *p++ = *tok++; + } break; } @@ -460,6 +508,31 @@ int invokemac(SYM * mac, WORD siz) // If we hit the comma instead of an EOL, skip over it if (*tok == ',') tok++; + + // Do our QnD token grabbing (this will be redone once we get all + // the data structures fixed as this is a really dirty hack) + if (!dry_run) + { + dest = imacro->argument[argumentNum].token; + stringNum = 0; + + do + { + // Remap strings to point the IMACRO internal token storage + if (*startOfArg == SYMBOL || *startOfArg == STRING) + { + *dest++ = *startOfArg++; + imacro->argument[argumentNum].string[stringNum] = strdup(string[*startOfArg++]); + *dest++ = stringNum++; + } + else + *dest++ = *startOfArg++; + } + while (*startOfArg != EOL); + + *dest = *startOfArg; // Copy EOL... + argumentNum++; + } } // Allocate space for argument ptrs and so on and then go back and @@ -467,93 +540,61 @@ int invokemac(SYM * mac, WORD siz) if (dry_run) { if (nargs != 0) -//Barfing here with memory corruption in glibc. TOKEN is defined as LONG, which is uint32_t -// p = (TOKEN *)malloc(arg_siz + 1); - p = (TOKEN *)malloc(arg_siz + sizeof(TOKEN)); - + p = (TOKEN *)malloc(arg_siz); +// p = (TOKEN *)malloc(arg_siz + sizeof(TOKEN)); + +/* +Shamus: +This construct is meant to deal with nested macros, so the simple minded way +we deal with them now won't work. :-/ Have to think about how to fix. +What we could do is simply move the argp with each call, and move it back by +the number of arguments in the macro that's ending. That would solve the +problem nicely. +[Which we do now. But that uncovered another problem: the token strings are all +stale by the time a nested macro gets to the end. But they're supposed to be +symbols, which means if we put symbol references into the argument token +streams, we can alleviate this problem.] +*/ +#if 0 argptr = (TOKEN **)malloc((nargs + 1) * sizeof(LONG)); *argptr++ = (TOKEN *)argp; argp = argptr; +#else + // We don't need to do anything here since we already advance argp + // when parsing the arguments. +// argp += nargs; +#endif } - else + else break; } + DEBUG { printf("%d\n", argp); } + // Setup imacro: - // o #arguments; - // o -> macro symbol; - // o -> macro definition string list; - // o save 'curuniq', to be restored when the macro pops; - // o bump `macuniq' counter and set 'curuniq' to it; + // o # arguments; + // o -> macro symbol; + // o -> macro definition string list; + // o save 'curuniq', to be restored when the macro pops; + // o bump `macuniq' counter and set 'curuniq' to it; imacro->im_nargs = nargs; imacro->im_macro = mac; - imacro->im_nextln = (TOKEN *)mac->svalue; + imacro->im_nextln = mac->lineList; imacro->im_olduniq = curuniq; curuniq = macuniq++; -/*IMACRO { - IMACRO * im_link; // Pointer to ancient IMACROs - LONG * im_nextln; // Next line to include - WORD im_nargs; // # of arguments supplied on invocation - WORD im_siz; // Size suffix supplied on invocation - LONG im_olduniq; // Old value of 'macuniq' - SYM * im_macro; // Pointer to macro we're in - char im_lnbuf[LNSIZ]; // Line buffer -};*/ + imacro->argBase = argp - nargs; // Shamus: keep track of argument base DEBUG { printf("nargs=%d\n", nargs); - for(nargs=0; nargsim_nargs; ++nargs) + for(nargs=0; nargsim_nargs; nargs++) { printf("arg%d=", nargs); - dumptok(argp[imacro->im_nargs - nargs - 1]); + dumptok(argPtrs[(argp - imacro->im_nargs) + nargs]); } } return OK; } - -// -// Setup inbuilt macros -// -void ib_macro(void) -{ - SYM * mac; - - curmac = mac = newsym("mjump", MACRO, 0); - mac->svalue = 0; - mac->sattr = (WORD)(macnum++); - argno = 0; - defmac2("cc"); - defmac2("addr"); - defmac2("jreg"); - curmln = NULL; - defmac1(" nop", -1); - defmac1(" movei #\\addr,\\jreg", -1); - defmac1(" jump \\cc,(\\jreg)", -1); - defmac1(" nop", -1); - defmac1(" nop", -1); - - curmac = mac = newsym("mjr", MACRO, 0); - mac->svalue = 0; - mac->sattr = (WORD)(macnum++); - argno = 0; - defmac2("cc"); - defmac2("addr"); - curmln = NULL; - defmac1(" jr \\cc,\\addr", -1); - defmac1(" nop", -1); - defmac1(" nop", -1); - - curmac = mac = newsym("mpad", MACRO, 0); - mac->svalue = 0; - mac->sattr = (WORD)(macnum++); - argno = 0; - defmac2("size"); - curmln = NULL; - defmac1(" .rept (\\size/2)", -1); - defmac1(" nop", -1); - defmac1(" .endr", -1); -}