home *** CD-ROM | disk | FTP | other *** search
/ The C Users' Group Library 1994 August / wc-cdrom-cusersgrouplibrary-1994-08.iso / vol_200 / 219_01 / a65eval.c < prev    next >
Text File  |  1989-01-13  |  13KB  |  520 lines

  1. /*
  2.     HEADER:        CUG219;
  3.     TITLE:        6502 Cross-Assembler (Portable);
  4.     FILENAME:    A65EVAL.C;
  5.     VERSION:    0.1;
  6.     DATE:        08/27/1988;
  7.  
  8.     DESCRIPTION:    "This program lets you use your computer to assemble
  9.             code for the MOS Technology 6502 microprocessors.  The
  10.             program is written in portable C rather than BDS C.
  11.             All assembler features are supported except relocation
  12.             linkage, and macros.";
  13.  
  14.     KEYWORDS:    Software Development, Assemblers, Cross-Assemblers,
  15.             MOS Technology, 6502;
  16.  
  17.     SYSTEM:        CP/M-80, CP/M-86, HP-UX, MSDOS, PCDOS, QNIX;
  18.     COMPILERS:    Aztec C86, Aztec CII, CI-C86, Eco-C, Eco-C88, HP-UX,
  19.             Lattice C, Microsoft C,    QNIX C;
  20.  
  21.     WARNINGS:    "This program is written in as portable C as possible.
  22.             A port to BDS C would be extremely difficult, but see
  23.             volume CUG113.  A port to Toolworks C is untried."
  24.  
  25.     AUTHORS:    William C. Colley III;
  26. */
  27.  
  28. /*
  29.               6502 Cross-Assembler in Portable C
  30.  
  31.            Copyright (c) 1986 William C. Colley, III
  32.  
  33. Revision History:
  34.  
  35. Ver    Date        Description
  36.  
  37. 0.0    NOV 1986    Derived from my 6800/6801 cross-assembler.  WCC3.
  38.  
  39. 0.1    AUG 1988    Fixed a bug in the command line parser that puts it
  40.             into a VERY long loop if the user types a command line
  41.             like "A65 FILE.ASM -L".  WCC3 per Alex Cameron.
  42.  
  43. This file contains the assembler's expression evaluator and lexical analyzer.
  44. The lexical analyzer chops the input character stream up into discrete tokens
  45. that are processed by the expression analyzer and the line assembler.  The
  46. expression analyzer processes the token stream into unsigned results of
  47. arithmetic expressions.
  48. */
  49.  
  50. /*  Get global goodies:  */
  51.  
  52. #include "a65.h"
  53.  
  54. /*  Get access to global mailboxes defined in A65.C:            */
  55.  
  56. extern char line[];
  57. extern int filesp, forwd, pass;
  58. extern unsigned argattr, pc;
  59. extern FILE *filestk[], *source;
  60. extern TOKEN token;
  61.  
  62. /*  Machine opcode argument field parsing routine.  The token stream    */
  63. /*  from the lexical analyzer is processed to extract addressing mode    */
  64. /*  information and (possibly) an actual address that can be reduced to */
  65. /*  an unsigned value.  If an error occurs during the evaluation, the    */
  66. /*  global flag forwd is set to indicate to the line assembler that it    */
  67. /*  should not base certain decisions on the result of the evaluation.    */
  68. /*  The address is passed back as the return value of the function.    */
  69. /*  The addressing mode information is passed back through the global    */
  70. /*  mailbox argattr.                            */
  71.  
  72. static int bad;
  73.  
  74. unsigned do_args()
  75. {
  76.     SCRATCH int c;
  77.     SCRATCH unsigned u;
  78.     TOKEN *lex();
  79.     int popc();
  80.     unsigned eval(), expr();
  81.     void exp_error(), pushc(), trash(), unlex();
  82.  
  83.     argattr = ARGNUM;  u = 0;  bad = FALSE;
  84.     switch (lex() -> attr & TYPE) {
  85.     case REG:   if (token.valu == 'A') argattr = ARGA;
  86.             else exp_error('S');
  87.             break;
  88.  
  89.     case EOL:   argattr = NULL;  break;
  90.  
  91.     case IMM:   argattr = ARGIMM + ARGNUM;
  92.             return expr();
  93.  
  94.     case SEP:   u = 0;  goto have_number;
  95.  
  96.     case OPR:   if (token.valu == '(') {
  97.             bad = FALSE;  u = eval(START2);
  98.             switch (lex() -> attr & TYPE) {
  99.                 case EOL:    exp_error('(');  return 0;
  100.  
  101.                 case SEP:    if ((lex() -> attr & TYPE) != REG ||
  102.                         token.valu != 'X') exp_error('S');
  103.                     else if ((lex() -> attr & TYPE) != OPR
  104.                         || token.valu != ')')
  105.                         exp_error('(');
  106.                     else argattr += (ARGX + ARGIND);
  107.                     return bad ? 0 : u;
  108.  
  109.                 case OPR:    argattr = (ARGIND + ARGNUM);  trash();
  110.                     if ((c = popc()) == '\n') return u;
  111.                     if (c == ',') {
  112.                         if (lex() -> attr & TYPE != REG
  113.                         || token.valu != 'Y')
  114.                         exp_error('S');
  115.                         else argattr += ARGY;
  116.                         return bad ? 0 : u;
  117.                     }
  118.                     argattr = ARGNUM;  pushc(c);
  119.                     token.attr = VAL;  token.valu = u;
  120.             }
  121.             }
  122.  
  123.     case VAL:
  124.     case STR:   unlex();  u = eval(START);
  125.             if ((token.attr & TYPE) != SEP) {
  126.             if ((token.attr & TYPE) != EOL) exp_error('S');
  127.             break;
  128.             }
  129.  
  130. have_number:        if ((lex() -> attr & TYPE) != REG || token.valu == 'A')
  131.             exp_error('S');
  132.             else argattr += (token.valu == 'X' ? ARGX : ARGY);
  133.             break;
  134.     }
  135.     return bad ? 0 : u;
  136. }
  137.  
  138. /*  Expression analysis routine.  The token stream from the lexical    */
  139. /*  analyzer is processed as an arithmetic expression and reduced to an    */
  140. /*  unsigned value.  If an error occurs during the evaluation, the    */
  141. /*  global flag    forwd is set to indicate to the line assembler that it    */
  142. /*  should not base certain decisions on the result of the evaluation.    */
  143.  
  144. unsigned expr()
  145. {
  146.     SCRATCH unsigned u;
  147.     unsigned eval();
  148.  
  149.     bad = FALSE;
  150.     u = eval(START);
  151.     return bad ? 0 : u;
  152. }
  153.  
  154. static unsigned eval(pre)
  155. unsigned pre;
  156. {
  157.    register unsigned op, u, v;
  158.    TOKEN *lex();
  159.    void exp_error(), unlex();
  160.  
  161.    for (;;) {
  162.       u = op = lex() -> valu;
  163.       switch (token.attr & TYPE) {
  164.      case REG:
  165.      case IMM:   exp_error('S');  break;
  166.  
  167.      case SEP:   if (pre != START) unlex();
  168.      case EOL:   exp_error('E');  return;
  169.  
  170.      case OPR:   if (!(token.attr & UNARY)) { exp_error('E');  break; }
  171.              u = (op == '*' ? pc :
  172.             eval((op == '+' || op == '-') ?
  173.                (unsigned) UOP1 : token.attr & PREC));
  174.              switch (op) {
  175.             case '-':   u = word(-u);  break;
  176.  
  177.             case NOT:   u ^= 0xffff;  break;
  178.  
  179.             case HIGH:  u = high(u);  break;
  180.  
  181.             case LOW:   u = low(u);  break;
  182.              }
  183.  
  184.      case VAL:    
  185.      case STR:   for (;;) {
  186.             op = lex() -> valu;
  187.             switch (token.attr & TYPE) {
  188.                case REG:
  189.                case IMM:   exp_error('S');  break;
  190.  
  191.                case SEP:   if (pre != START) unlex();
  192.                case EOL:   if (pre == LPREN) exp_error('(');
  193.                        return u;
  194.  
  195.                case STR:
  196.                case VAL:   exp_error('E');  break;
  197.  
  198.                case OPR:   if (!(token.attr & BINARY)) {
  199.                       exp_error('E');  break;
  200.                        }
  201.                        if ((token.attr & PREC) >= pre) {
  202.                       unlex();  return u;
  203.                        }
  204.                        if (op != ')')
  205.                       v = eval(token.attr & PREC);
  206.                        switch (op) {
  207.                       case '+':   u += v;  break;
  208.  
  209.                       case '-':   u -= v;  break;
  210.  
  211.                       case '*':   u *= v;  break;
  212.  
  213.                       case '/':   u /= v;  break;
  214.  
  215.                       case MOD:   u %= v;  break;
  216.  
  217.                       case AND:   u &= v;  break;
  218.  
  219.                       case OR:    u |= v;  break;
  220.  
  221.                       case XOR:   u ^= v;  break;
  222.  
  223.                       case '<':   u = u < v;  break;
  224.  
  225.                       case LE:    u = u <= v;  break;
  226.  
  227.                       case '=':   u = u == v;  break;
  228.  
  229.                       case GE:    u = u >= v;  break;
  230.  
  231.                       case '>':   u = u > v;  break;
  232.  
  233.                       case NE:    u = u != v;  break;
  234.  
  235.                       case SHL:   if (v > 15)
  236.                              exp_error('E');
  237.                               else u <<= v;
  238.                               break;
  239.  
  240.                       case SHR:   if (v > 15)
  241.                              exp_error('E');
  242.                               else u >>= v;
  243.                               break;
  244.  
  245.                       case ')':   if (pre == LPREN)
  246.                              return u;
  247.                               exp_error('(');
  248.                               break;
  249.                        }
  250.                        clamp(u);
  251.                        break;
  252.             }
  253.              }
  254.              break;
  255.       }
  256.    }
  257. }
  258.  
  259. static void exp_error(c)
  260. char c;
  261. {
  262.     forwd = bad = TRUE;  error(c);
  263. }
  264.  
  265. /*  Lexical analyzer.  The source input character stream is chopped up    */
  266. /*  into its component parts and the pieces are evaluated.  Symbols are    */
  267. /*  looked up, operators are looked up, etc.  Everything gets reduced    */
  268. /*  to an attribute word, a numeric value, and (possibly) a string    */
  269. /*  value.                                */
  270.  
  271. static int oldt = FALSE;
  272. static int quote = FALSE;
  273.  
  274. TOKEN *lex()
  275. {
  276.     SCRATCH char c, *p;
  277.     SCRATCH unsigned b;
  278.     SCRATCH OPCODE *o;
  279.     SCRATCH SYMBOL *s;
  280.     OPCODE *find_operator();
  281.     SYMBOL *find_symbol();
  282.     void exp_error(), make_number(), pops(), pushc(), trash();
  283.  
  284.     if (oldt) { oldt = FALSE;  return &token; }
  285.     trash();
  286.     if (isalph(c = popc())) {
  287.     pushc(c);  pops(token.sval);
  288.     if (o = find_operator(token.sval)) {
  289.         token.attr = o -> attr;
  290.         token.valu = o -> valu;
  291.     }
  292.     else {
  293.         token.attr = VAL;  token.valu = 0;
  294.         if (s = find_symbol(token.sval)) {
  295.         token.valu = s -> valu;
  296.         if (pass == 2 && s -> attr & FORWD) forwd = TRUE;
  297.         }
  298.         else exp_error('U');
  299.     }
  300.     }
  301.     else if (isnum(c)) {
  302.     pushc(c);  pops(token.sval);
  303.     for (p = token.sval; *p; ++p);
  304.     switch (toupper(*--p)) {
  305.         case 'B':    b = 2;  break;
  306.  
  307.         case 'O':
  308.         case 'Q':    b = 8;  break;
  309.  
  310.         default:    ++p;
  311.         case 'D':    b = 10;  break;
  312.  
  313.         case 'H':    b = 16;  break;
  314.     }
  315.     *p = '\0';  make_number(b);
  316.     }
  317.     else switch (c) {
  318.     case '%':   b = 2;  goto num;
  319.  
  320.     case '@':   b = 8;  goto num;
  321.  
  322.     case '$':   b = 16;
  323. num:            pops(token.sval);
  324.             make_number(b);
  325.             break;
  326.  
  327.     case '#':   token.attr = IMM;
  328.             break;
  329.  
  330.     case '(':   token.attr = UNARY + LPREN + OPR;
  331.             goto opr1;
  332.  
  333.     case ')':   token.attr = BINARY + RPREN + OPR;
  334.             goto opr1;
  335.  
  336.     case '+':   token.attr = BINARY + UNARY + ADDIT + OPR;
  337.             goto opr1;
  338.  
  339.     case '-':   token.attr = BINARY + UNARY + ADDIT + OPR;
  340.             goto opr1;
  341.  
  342.     case '*':   token.attr = BINARY + UNARY + MULT + OPR;
  343.             goto opr1;
  344.  
  345.     case '/':   token.attr = BINARY + MULT + OPR;
  346. opr1:            token.valu = c;
  347.             break;
  348.  
  349.     case '<':   token.valu = c;
  350.             if ((c = popc()) == '=') token.valu = LE;
  351.             else if (c == '>') token.valu = NE;
  352.             else pushc(c);
  353.             goto opr2;
  354.  
  355.     case '=':   token.valu = c;
  356.             if ((c = popc()) == '<') token.valu = LE;
  357.             else if (c == '>') token.valu = GE;
  358.             else pushc(c);
  359.             goto opr2;
  360.  
  361.     case '>':   token.valu = c;
  362.             if ((c = popc()) == '<') token.valu = NE;
  363.             else if (c == '=') token.valu = GE;
  364.             else pushc(c);
  365. opr2:            token.attr = BINARY + RELAT + OPR;
  366.             break;
  367.  
  368.     case '\'':
  369.     case '"':   quote = TRUE;  token.attr = STR;
  370.             for (p = token.sval; (*p = popc()) != c; ++p)
  371.             if (*p == '\n') { exp_error('"');  break; }
  372.             *p = '\0';  quote = FALSE;
  373.             if ((token.valu = token.sval[0]) && token.sval[1])
  374.             token.valu = (token.valu << 8) + token.sval[1];
  375.             break;
  376.  
  377.     case ',':   token.attr = SEP;
  378.             break;
  379.  
  380.         case '\n':  token.attr = EOL;
  381.             break;
  382.     }
  383.     return &token;
  384. }
  385.  
  386. static void make_number(base)
  387. unsigned base;
  388. {
  389.     SCRATCH char *p;
  390.     SCRATCH unsigned d;
  391.     void exp_error();
  392.  
  393.     token.attr = VAL;
  394.     token.valu = 0;
  395.     for (p = token.sval; *p; ++p) {
  396.     d = toupper(*p) - (isnum(*p) ? '0' : 'A' - 10);
  397.     token.valu = token.valu * base + d;
  398.     if (!ishex(*p) || d >= base) { exp_error('D');  break; }
  399.     }
  400.     clamp(token.valu);
  401.     return;
  402. }
  403.  
  404. int isalph(c)
  405. char c;
  406. {
  407.     return (c >= 'A' && c <= '~') || c == '!' ||
  408.     c == '&' || c == '.' || c == ':' || c == '?';
  409. }
  410.  
  411. static int isnum(c)
  412. char c;
  413. {
  414.     return c >= '0' && c <= '9';
  415. }
  416.  
  417. static int ishex(c)
  418. char c;
  419. {
  420.     return isnum(c) || ((c = toupper(c)) >= 'A' && c <= 'F');
  421. }
  422.  
  423. static int isalnum(c)
  424. char c;
  425. {
  426.     return isalph(c) || isnum(c);
  427. }
  428.  
  429. /*  Push back the current token into the input stream.  One level of    */
  430. /*  pushback is supported.                        */
  431.  
  432. void unlex()
  433. {
  434.     oldt = TRUE;
  435.     return;
  436. }
  437.  
  438. /*  Get an alphanumeric string into the string value part of the    */
  439. /*  current token.  Leading blank space is trashed.            */
  440.  
  441. void pops(s)
  442. char *s;
  443. {
  444.     void pushc(), trash();
  445.  
  446.     trash();
  447.     for (; isalnum(*s = popc()); ++s);
  448.     pushc(*s);  *s = '\0';
  449.     return;
  450. }
  451.  
  452. /*  Trash blank space and push back the character following it.        */
  453.  
  454. void trash()
  455. {
  456.     SCRATCH char c;
  457.     void pushc();
  458.  
  459.     while ((c = popc()) == ' ');
  460.     pushc(c);
  461.     return;
  462. }
  463.  
  464. /*  Get character from input stream.  This routine does a number of    */
  465. /*  other things while it's passing back characters.  All control    */
  466. /*  characters except \t and \n are ignored.  \t is mapped into ' '.    */
  467. /*  Semicolon is mapped to \n.  In addition, a copy of all input is set    */
  468. /*  up in a line buffer for the benefit of the listing.            */
  469.  
  470. static int oldc, eol;
  471. static char *lptr;
  472.  
  473. int popc()
  474. {
  475.     SCRATCH int c;
  476.  
  477.     if (oldc) { c = oldc;  oldc = '\0';  return c; }
  478.     if (eol) return '\n';
  479.     for (;;) {
  480.     if ((c = getc(source)) != EOF && (c &= 0377) == ';' && !quote) {
  481.         do *lptr++ = c;
  482.         while ((c = getc(source)) != EOF && (c &= 0377) != '\n');
  483.     }
  484.     if (c == EOF) c = '\n';
  485.     if ((*lptr++ = c) >= ' ' && c <= '~') return c;
  486.     if (c == '\n') { eol = TRUE;  *lptr = '\0';  return '\n'; }
  487.     if (c == '\t') return quote ? '\t' : ' ';
  488.     }
  489. }
  490.  
  491. /*  Push character back onto input stream.  Only one level of push-back    */
  492. /*  supported.  \0 cannot be pushed back, but nobody would want to.    */
  493.  
  494. void pushc(c)
  495. char c;
  496. {
  497.     oldc = c;
  498.     return;
  499. }
  500.  
  501. /*  Begin new line of source input.  This routine returns non-zero if    */
  502. /*  EOF    has been reached on the main source file, zero otherwise.    */
  503.  
  504. int newline()
  505. {
  506.     void fatal_error();
  507.  
  508.     oldc = '\0';  lptr = line;
  509.     oldt = eol = FALSE;
  510.     while (feof(source)) {
  511.     if (ferror(source)) fatal_error(ASMREAD);
  512.     if (filesp) {
  513.         fclose(source);
  514.         source = filestk[--filesp];
  515.     }
  516.     else return TRUE;
  517.     }
  518.     return FALSE;
  519. }
  520.