home *** CD-ROM | disk | FTP | other *** search
/ C/C++ Interactive Guide / c-cplusplus-interactive-guide.iso / c_ref / csource4 / 276_01 / az80.c < prev    next >
Encoding:
C/C++ Source or Header  |  1989-10-03  |  18.6 KB  |  786 lines

  1. /*
  2.     HEADER:        CUG276;
  3.     TITLE:        Z-80 Cross-Assembler (Portable);
  4.     FILENAME:    AZ80.C;
  5.     VERSION:    0.1;
  6.     DATE:        08/27/1988;
  7.     SEE-ALSO:    AZ80.H;
  8.     AUTHORS:    William C. Colley III;
  9. */
  10.  
  11. /*
  12.               Z-80 Cross-Assembler in Portable C
  13.  
  14.         Copyright (c) 1986-1988 William C. Colley, III
  15.  
  16. Revision History:
  17.  
  18. Ver    Date        Description
  19.  
  20. 0.0    JUNE 1988    Derived from my S-6 cross-assembler.  WCC3.
  21.  
  22. 0.1    AUG 1988    Fixed a bug in the command line parser that puts it
  23.             into a VERY long loop if the user types a command line
  24.             like "AZ80 FILE.ASM -L".  WCC3 per Alex Cameron.
  25.  
  26. This file contains the main program and line assembly routines for the
  27. assembler.  The main program parses the command line, feeds the source lines
  28. to the line assembly routine, and sends the results to the listing and object
  29. file output routines.  It also coordinates the activities of everything.  The
  30. line assembly routines uses the expression analyzer and the lexical analyzer
  31. to parse the source line and convert it into the object bytes that it
  32. represents.
  33. */
  34.  
  35. /*  Get global goodies:  */
  36.  
  37. #include "az80.h"
  38.  
  39. /*  Define global mailboxes for all modules:                */
  40.  
  41. char errcode, line[MAXLINE + 1], title[MAXLINE];
  42. int pass = 0;
  43. int eject, filesp, forwd, listhex;
  44. unsigned address, bytes, errors, listleft, obj[MAXLINE], pagelen, pc;
  45. FILE *filestk[FILES], *source;
  46. TOKEN arg, token;
  47.  
  48. /*  Mainline routine.  This routine parses the command line, sets up    */
  49. /*  the assembler at the beginning of each pass, feeds the source text    */
  50. /*  to the line assembler, feeds the result to the listing and hex file    */
  51. /*  drivers, and cleans everything up at the end of the run.        */
  52.  
  53. static int done, ifsp, off;
  54.  
  55. void main(argc,argv)
  56. int argc;
  57. char **argv;
  58. {
  59.     SCRATCH unsigned *o;
  60.     int newline();
  61.     void asm_line();
  62.     void lclose(), lopen(), lputs();
  63.     void hclose(), hopen(), hputc();
  64.     void error(), fatal_error(), warning();
  65.  
  66.     printf("Z-80 Cross-Assembler (Portable) Ver 0.1\n");
  67.     printf("Copyright (c) 1986-1988 William C. Colley, III\n\n");
  68.  
  69.     while (--argc > 0) {
  70.     if (**++argv == '-') {
  71.         switch (toupper(*++*argv)) {
  72.         case 'L':   if (!*++*argv) {
  73.                 if (!--argc) { warning(NOLST);  break; }
  74.                 else ++argv;
  75.                 }
  76.                 lopen(*argv);
  77.                 break;
  78.  
  79.         case 'O':   if (!*++*argv) {
  80.                 if (!--argc) { warning(NOHEX);  break; }
  81.                 else ++argv;
  82.                 }
  83.                 hopen(*argv);
  84.                 break;
  85.  
  86.         default:    warning(BADOPT);
  87.         }
  88.     }
  89.     else if (filestk[0]) warning(TWOASM);
  90.     else if (!(filestk[0] = fopen(*argv,"r"))) fatal_error(ASMOPEN);
  91.     }
  92.     if (!filestk[0]) fatal_error(NOASM);
  93.  
  94.     while (++pass < 3) {
  95.     fseek(source = filestk[0],0L,0);  done = off = FALSE;
  96.     errors = filesp = ifsp = pagelen = pc = 0;  title[0] = '\0';
  97.     while (!done) {
  98.         errcode = ' ';
  99.         if (newline()) {
  100.         error('*');
  101.         strcpy(line,"\tEND\n");
  102.         done = eject = TRUE;  listhex = FALSE;
  103.         bytes = 0;
  104.         }
  105.         else asm_line();
  106.         pc = word(pc + bytes);
  107.         if (pass == 2) {
  108.         lputs();
  109.         for (o = obj; bytes--; hputc(*o++));
  110.         }
  111.     }
  112.     }
  113.  
  114.     fclose(filestk[0]);  lclose();  hclose();
  115.  
  116.     if (errors) printf("%d Error(s)\n",errors);
  117.     else printf("No Errors\n");
  118.  
  119.     exit(errors);
  120. }
  121.  
  122. /*  Line assembly routine.  This routine gets the contents of the    */
  123. /*  argument field from the source file using the expression evaluator    */
  124. /*  and lexical analyzer.  It makes all validity checks on the        */
  125. /*  arguments validity, fills a buffer with the machine code bytes and    */
  126. /*  returns nothing.                            */
  127.  
  128. static char label[MAXLINE];
  129. static int ifstack[IFDEPTH] = { ON };
  130.  
  131. static OPCODE *opcod;
  132.  
  133. void asm_line()
  134. {
  135.     SCRATCH char *p;
  136.     SCRATCH int i;
  137.     int isalph(), popc();
  138.     OPCODE *find_code(), *find_operator();
  139.     void do_label(), flush(), normal_op(), pseudo_op();
  140.     void error(), pops(), pushc(), trash();
  141.  
  142.     address = pc;  bytes = 0;  eject = forwd = listhex = FALSE;
  143.     for (i = 0; i < BIGINST; obj[i++] = NOP);
  144.  
  145.     label[0] = '\0';
  146.     if ((i = popc()) != ' ' && i != '\n') {
  147.     if (isalph(i)) {
  148.         pushc(i);  pops(label);
  149.         for (p = label;  *(p + 1);  ++p);
  150.         if (*p == ':') *p = '\0';
  151.         if (find_operator(label)) { label[0] = '\0';  error('L'); }
  152.     }
  153.     else {
  154.         error('L');
  155.         while ((i = popc()) != ' ' && i != '\n');
  156.     }
  157.     }
  158.  
  159.     trash();  opcod = NULL;
  160.     if ((i = popc()) != '\n') {
  161.     if (!isalph(i)) error('S');
  162.     else {
  163.         pushc(i);  pops(token.sval);
  164.         if (!(opcod = find_code(token.sval))) error('O');
  165.     }
  166.     if (!opcod) { listhex = TRUE;  bytes = BIGINST; }
  167.     }
  168.  
  169.     if (opcod && opcod -> attr & ISIF) { if (label[0]) error('L'); }
  170.     else if (off) { listhex = FALSE;  flush();  return; }
  171.  
  172.     if (!opcod) { do_label();  flush(); }
  173.     else {
  174.     listhex = TRUE;
  175.     if (opcod -> attr & PSEUDO) pseudo_op();
  176.     else normal_op();
  177.     while ((i = popc()) != '\n') if (i != ' ') error('T');
  178.     }
  179.     source = filestk[filesp];
  180.     return;
  181. }
  182.  
  183. static void flush()
  184. {
  185.     while (popc() != '\n');
  186. }
  187.  
  188. static void do_label()
  189. {
  190.     SCRATCH SYMBOL *l;
  191.     SYMBOL *find_symbol(), *new_symbol();
  192.     void error();
  193.  
  194.     if (label[0]) {
  195.     listhex = TRUE;
  196.     if (pass == 1) {
  197.         if (!((l = new_symbol(label)) -> attr)) {
  198.         l -> attr = FORWD + VAL;
  199.         l -> valu = pc;
  200.         }
  201.     }
  202.     else {
  203.         if (l = find_symbol(label)) {
  204.         l -> attr = VAL;
  205.         if (l -> valu != pc) error('M');
  206.         }
  207.         else error('P');
  208.     }
  209.     }
  210. }
  211.  
  212. static void normal_op()
  213. {
  214.     SCRATCH unsigned opcode, tmp;
  215.     unsigned *o, *p;
  216.     static unsigned IM_tbl[] = { 0x00, 0x10, 0x18 };
  217.     int popc();
  218.     unsigned expr(), get_arg();
  219.     void do_label(), error(), grab_comma(), insert_prebyte(), pushc(), trash();
  220.     TOKEN *lex();
  221.  
  222.     do_label();  opcode = opcod -> valu;  o = obj;
  223.     if (opcode > 0xff) *o++ = high(opcode);
  224.     *o = low(opcode);
  225.     switch (opcod -> attr & OPTYPE) {
  226.     case LD:
  227.         p = o;
  228.         switch (get_arg()) {
  229.         case IX:
  230.         case IY:    insert_prebyte(&o,arg.attr);
  231.  
  232.         case BC:
  233.         case DE:
  234.         case HL:
  235.         case SP:    *o = 0x01 + ((arg.attr & 007) << 3);
  236.                 grab_comma();
  237.                 switch (get_arg()) {
  238.                     case NUM_IND:   if (*o == 0x21) *o += 0x09;
  239.                             else {
  240.                             *o += 0x4a;
  241.                             insert_prebyte(&o,
  242.                                 arg.attr);
  243.                             }
  244.  
  245.                     case NUM:        *++o = low(arg.valu);
  246.                             *++o = high(arg.valu);
  247.                             break;
  248.  
  249.                     case IX:
  250.                     case IY:        insert_prebyte(&o,
  251.                             arg.attr);
  252.  
  253.                     case HL:        if (*o == 0x31) {
  254.                             *o = 0xf9;  break;
  255.                             }
  256.  
  257.                     default:        error(arg.attr < NUM ?
  258.                             'R' : 'S');
  259.                             break;
  260.                 }
  261.                 break;
  262.  
  263.         case I:        *o++ = 0xed;  *o = 0x47;  goto do_ld_i;
  264.  
  265.         case R:        *o++ = 0xed;  *o = 0x4f;  goto do_ld_i;
  266.  
  267.         case BC_IND:    *o = 0x02;  goto do_ld_i;
  268.  
  269.         case DE_IND:    *o = 0x12;
  270. do_ld_i:            grab_comma();
  271.                 if (get_arg() != A)
  272.                     error(arg.attr < NUM ? 'R' : 'S');
  273.                 break;
  274.  
  275.         case NUM_IND:    *++o = low(arg.valu);
  276.                 *++o = high(arg.valu);
  277.                 grab_comma();  *p = 0x02;
  278.                 switch (get_arg()) {
  279.                     case A:        *p = 0x32;  break;
  280.  
  281.                     case BC:
  282.                     case DE:
  283.                     case SP:        *p = 0x43;
  284.  
  285.                     case IX:
  286.                     case IY:        *p += (arg.attr & 007)
  287.                             << 3;
  288.                             insert_prebyte(&o,
  289.                             arg.attr);
  290.                             break;
  291.  
  292.                     case HL:        *p = 0x22;  break;
  293.  
  294.                     default:        error(arg.attr < NUM ?
  295.                             'R' : 'S');
  296.                             break;
  297.                 }
  298.                 break;
  299.  
  300.         case A:        grab_comma();
  301.                 switch (get_arg()) {
  302.                     case BC_IND:    *o = 0x0a;  break;
  303.  
  304.                     case DE_IND:    *o = 0x1a;  break;
  305.  
  306.                     case I:        *o++ = 0xed;  *o = 0x57;
  307.                             break;
  308.  
  309.                     case R:        *o++ = 0xed;  *o = 0x5f;
  310.                             break;
  311.  
  312.                     case NUM_IND:   *o++ = 0x3a;
  313.                             *o++ = low(arg.valu);
  314.                             *o = high(arg.valu);
  315.                             break;
  316.  
  317.                     default:        *o += 0x38;  goto do_ld_a;
  318.                 }
  319.                 break;                
  320.  
  321.         case IX_IND:
  322.         case IY_IND:    insert_prebyte(&o,arg.attr);  ++p;
  323.                 *++o = low(arg.valu);  arg.attr = HL_IND;
  324.  
  325.         default:    if (arg.attr <= A) {
  326.                     *p += (arg.attr & 007) << 3;
  327.                     grab_comma();  get_arg();
  328. do_ld_a:                if (arg.attr == NUM) {
  329.                     *p -= 0x3a;
  330.                     if (arg.valu > 0xff &&
  331.                         arg.valu < 0xff80) {
  332.                         *++o = 0;  error('V');
  333.                     }
  334.                     else *++o = low(arg.valu);
  335.                     break;
  336.                     }
  337.                     if (arg.attr <= A || arg.attr ==
  338.                     IX_IND || arg.attr == IY_IND) {
  339.                     if ((*p += arg.attr & 007) == 0x76) {
  340.                         *p = 0x06;  error('R');  break;
  341.                     }
  342.                     if (arg.attr > A) {
  343.                         insert_prebyte(&o,arg.attr);
  344.                         *++o = low(arg.valu);
  345.                     }
  346.                     break;
  347.                     }
  348.                 }
  349.                 error(arg.attr < NUM ? 'R' : 'S');  break;
  350.         }
  351.         break;
  352.  
  353.     case POP:
  354.         switch (get_arg()) {
  355.         case IX:
  356.         case IY:    insert_prebyte(&o,arg.attr);
  357.                 arg.attr = HL;
  358.  
  359.         default:    if (arg.attr < BC || arg.attr > AF)
  360.                     error(arg.attr < NUM ? 'R' : 'S');
  361.                 else *o += (arg.attr & 007) << 3;
  362.                 break;
  363.         }
  364.         break;
  365.  
  366.     case EX:
  367.         tmp = get_arg();  grab_comma();
  368.         switch (tmp) {
  369.         case SP_IND:    if (get_arg() == IX || arg.attr == IY)
  370.                     insert_prebyte(&o,arg.attr);
  371.                 else if (arg.attr != HL)
  372.                     error(arg.attr < NUM ? 'R' : 'S');
  373.                 break;
  374.  
  375.         case DE:    if (get_arg() == HL) *o = 0xeb;
  376.                 else error(arg.attr < NUM ? 'R' : 'S');
  377.                 break;
  378.  
  379.         case AF:    if (get_arg() == AF) {
  380.                     *o = 0x08;  trash();
  381.                     if ((tmp = popc()) == '\'') break;
  382.                     pushc(tmp);  error('S');  break;
  383.                 }
  384.                 error(arg.attr < NUM ? 'R' : 'S');
  385.                 break;
  386.         }
  387.         break;        
  388.  
  389.     case ADD:
  390.         if (get_arg() == IX || arg.attr == IY) {
  391.         insert_prebyte(&o,arg.attr);
  392.         tmp = arg.attr;  arg.attr = HL;
  393.         }
  394.         else tmp = HL;
  395.         goto do_adc;    
  396.  
  397.     case ADC:
  398.         get_arg();  tmp = HL;
  399. do_adc:        if (arg.attr == HL) {
  400.         switch (*o) {
  401.             case 0x80:    *o = 0x09;  break;
  402.  
  403.             case 0x88:    *o++ = 0xed;  *o = 0x4a;  break;
  404.  
  405.             case 0x98:    *o++ = 0xed;  *o = 0x42;  break;
  406.         }
  407.         grab_comma();
  408.         if (get_arg() == BC || arg.attr == DE || arg.attr == SP ||
  409.             arg.attr == tmp) *o += (arg.attr & 007) << 3;
  410.         else error(arg.attr < NUM ? 'R' : 'S');
  411.         break;
  412.         }
  413.         if (arg.attr != A) { error('S');  break; }
  414.         grab_comma();
  415.  
  416.     case CP:
  417.         switch (get_arg()) {
  418.         case NUM_IND:
  419.         case NONE:    error('S');  break;
  420.  
  421.         case NUM:    *o++ += 0x46;
  422.                 if (arg.valu > 0xff && arg.valu < 0xff80) {
  423.                     *o = 0;  error('V');
  424.                 }
  425.                 else *o = low(arg.valu);
  426.                 break;
  427.  
  428.         case IX_IND:
  429.         case IY_IND:    insert_prebyte(&o,arg.attr);  *o++ += 0x06;
  430.                 *o = low(arg.valu);  break;
  431.  
  432.         default:    if (arg.attr > A) error('R');
  433.                 else *o += arg.attr & 007;
  434.                 break;
  435.         }
  436.         break;
  437.  
  438.     case DEC:
  439.         p = o;
  440.         switch (get_arg()) {
  441.         case NUM:
  442.         case NUM_IND:
  443.         case NONE:    error('S');  break;
  444.  
  445.         case IX:
  446.         case IY:    insert_prebyte(&o,arg.attr);
  447.  
  448.         case SP:
  449.         case HL:
  450.         case DE:
  451.         case BC:    *o = (arg.attr & 007 ^ *o) << 3 ^ 0x23;
  452.                 break;
  453.  
  454.         case IX_IND:
  455.         case IY_IND:    insert_prebyte(&o,arg.attr);  ++p;
  456.                 *++o = arg.valu;  arg.attr = HL_IND;
  457.  
  458.         default:    if (arg.attr > A) error('R');
  459.                 else *p += (arg.attr & 007) << 3;
  460.                 break;
  461.         }
  462.         break;
  463.  
  464.     case BIT:
  465.         if (get_arg() != NUM) error('S');
  466.         else if (arg.valu > 7) error('V');
  467.         else *o += arg.valu << 3;
  468.         grab_comma();
  469.  
  470.     case RLC:
  471.         switch (get_arg()) {
  472.         case NUM:
  473.         case NUM_IND:
  474.         case NONE:    error('S');  break;
  475.  
  476.         case IX_IND:
  477.         case IY_IND:    insert_prebyte(&o,arg.attr); *(o + 1) = *o;
  478.                 *o++ = low(arg.valu);  arg.attr = HL_IND;
  479.  
  480.         default:    if (arg.attr > A) error('R');
  481.                 else *o += arg.attr & 007;
  482.                 break;
  483.         }
  484.         break;
  485.         
  486.  
  487.     case JR:
  488.         if (get_arg() == NUM) { *o -= 0x08;  goto do_djnz; }
  489.         if (arg.attr == C) arg.attr = CY;
  490.         if (arg.attr < NZ || arg.attr > CY) {
  491.         *++o = 0xfe;  error('S');  break;
  492.         }
  493.         *o += (arg.attr & 007) << 3;  grab_comma();
  494.  
  495.     case DJNZ:
  496.         if (get_arg() != NUM) { *++o = 0xfe;  error('S');  break; }
  497. do_djnz:    if ((tmp = arg.valu - (pc + 2)) > 0x7f && tmp < 0xff80) {
  498.         *++o = 0xfe;  error('B');
  499.         }
  500.         else *++o = low(tmp);
  501.         break;
  502.  
  503.     case JP:
  504.         switch (get_arg()) {
  505.         case IX_IND:
  506.         case IY_IND:    insert_prebyte(&o,arg.attr);
  507.                 if (arg.valu) error('V');
  508.  
  509.         case HL_IND:    *o = 0xe9;  break;
  510.  
  511.         default:    goto do_call;
  512.         }
  513.         break;
  514.  
  515.     case CALL:
  516.         get_arg();
  517. do_call:    if (arg.attr == NUM) *o += *o == 0xc4 ? 0x09 : 0x01;
  518.         else {
  519.         if (arg.attr == C) arg.attr = CY;
  520.         if (arg.attr < NZ || arg.attr > M) error('S');
  521.         else *o += (arg.attr & 007) << 3;
  522.         grab_comma();
  523.         if (get_arg() != NUM) error('S');
  524.         }
  525.         *++o = low(arg.valu);  *++o = high(arg.valu);  break;
  526.  
  527.     case RET:
  528.         if (get_arg() == NONE) { *o += 0x09;  break; }
  529.         if (arg.attr == C) arg.attr = CY;
  530.         if (arg.attr < NZ || arg.attr > M) error('S');
  531.         else *o += (arg.attr & 007) << 3;
  532.         break;
  533.  
  534.     case IN:
  535.         tmp = get_arg();  grab_comma();
  536.         switch (get_arg()) {
  537.         case C_IND:    *(o - 1) = 0xed;  *o = 0x40;
  538.                 if (tmp <= L || tmp == A)
  539.                     *o += (tmp & 007) << 3;
  540.                 else error(tmp < NUM ? 'R' : 'S');
  541.                 break;
  542.  
  543.         case NUM_IND:    if (arg.valu <= 0xff) {
  544.                     *o = arg.valu;
  545.                     if (tmp != A) error(tmp < NUM ? 'R' : 'S');
  546.                 }
  547.                 else error('V');
  548.                 break;
  549.  
  550.         case NUM:    error('S');  break;
  551.  
  552.         default:    error('R');  break;
  553.         }
  554.         break;
  555.  
  556.     case OUT:
  557.         switch (get_arg()) {
  558.         case C_IND:    *(o - 1) = 0xed;  *o = 0x41;  grab_comma();
  559.                 if (get_arg() <= L || arg.attr == A)
  560.                     *o += (arg.attr & 007) << 3;
  561.                 else error(arg.attr < NUM ? 'R' : 'S');
  562.                 break;
  563.  
  564.         case NUM_IND:    if (arg.valu <= 0xff) {
  565.                     *o = arg.valu;  grab_comma();
  566.                     if (get_arg() != A)
  567.                     error(arg.attr < NUM ? 'R' : 'S');
  568.                 }
  569.                 else error('V');
  570.                 break;
  571.  
  572.         case NUM:    error('S');  break;
  573.  
  574.         default:    error('R');  break;
  575.         }
  576.         break;
  577.  
  578.     case RST:
  579.         if (get_arg() != NUM) error('S');
  580.         else if (arg.valu & 0xffc7) error('V');
  581.         else *o |= arg.valu;
  582.         break;
  583.  
  584.     case IM:
  585.         if (get_arg() != NUM) error('S');
  586.         else if (arg.valu > 2) error('V');
  587.         else *o |= IM_tbl[arg.valu];
  588.  
  589.     case NO_ARGS:
  590.         break;
  591.     }
  592.     if ((lex() -> attr & TYPE) != EOL) error('T');
  593.     bytes = (o - obj) + 1;  return;
  594. }
  595.  
  596. static void grab_comma()
  597. {
  598.     void error(), unlex();
  599.     TOKEN *lex();
  600.  
  601.     if ((lex() -> attr & TYPE) != SEP) { error('S');  unlex(); }
  602. }
  603.  
  604. static void insert_prebyte(optr,pb)
  605. unsigned **optr;
  606. unsigned pb;
  607. {
  608.     SCRATCH unsigned *q;
  609.  
  610.     for (q = ++*optr; q > obj ; --q) *q = *(q - 1);
  611.     if (pb == IX || pb == IX_IND) *q = 0xdd;
  612.     else if (pb == IY || pb == IY_IND) *q = 0xfd;
  613.     else *q = 0xed;
  614.     return;
  615. }
  616.  
  617. static void pseudo_op()
  618. {
  619.     SCRATCH char *s;
  620.     SCRATCH unsigned *o, u;
  621.     SCRATCH SYMBOL *l;
  622.     int popc();
  623.     unsigned expr();
  624.     SYMBOL *find_symbol(), *new_symbol();
  625.     TOKEN *lex();
  626.     void do_label(), error(), fatal_error(), hseek();
  627.     void pushc(), trash(), unlex();
  628.  
  629.     o = obj;
  630.     switch (opcod -> valu) {
  631.     case DB:
  632.     case DC:    do_label();
  633.             do {
  634.             if ((lex() -> attr & TYPE) == SEP) {
  635.                 *o++ = 0;  ++bytes;
  636.             }
  637.             else if (token.attr == STR) {
  638.                 trash();  pushc(u = popc());
  639.                 if (u != ',' && u != '\n') goto do_byte;
  640.                 for (s = token.sval; *s; *o++ = *s++) ++bytes;
  641.                 lex();
  642.             }
  643.             else {
  644. do_byte:            unlex();
  645.                 if ((u = expr()) > 0xff && u < 0xff80) {
  646.                 u = 0;  error('V');
  647.                 }
  648.                 *o++ = low(u);  ++bytes;
  649.             }
  650.             } while ((token.attr & TYPE) == SEP);
  651.             if (bytes && opcod -> valu == DC) *(o - 1) |= 0x80;
  652.             break;
  653.  
  654.     case DS:    do_label();
  655.             u = word(pc + expr());
  656.             if (forwd) error('P');
  657.             else {
  658.             pc = u;
  659.             if (pass == 2) hseek(pc);
  660.             }
  661.             break;
  662.  
  663.     case DW:    do_label();
  664.             do {
  665.             if ((lex() -> attr & TYPE) == SEP) u = 0;
  666.             else { unlex();  u = expr(); }
  667.             *o++ = low(u);  *o++ = high(u);
  668.             bytes += 2;
  669.             } while ((token.attr & TYPE) == SEP);
  670.             break;
  671.  
  672.     case ELSE:  listhex = FALSE;
  673.             if (ifsp) off = (ifstack[ifsp] = -ifstack[ifsp]) != ON;
  674.             else error('I');
  675.             break;
  676.  
  677.     case END:   do_label();
  678.             if (filesp) { listhex = FALSE;  error('*'); }
  679.             else {
  680.             done = eject = TRUE;
  681.             if (pass == 2 && (lex() -> attr & TYPE) != EOL) {
  682.                 unlex();  hseek(address = expr());
  683.             }
  684.             if (ifsp) error('I');
  685.             }
  686.             break;
  687.  
  688.     case ENDIF: listhex = FALSE;
  689.             if (ifsp) off = ifstack[--ifsp] != ON;
  690.             else error('I');
  691.             break;
  692.  
  693.     case EQU:   if (label[0]) {
  694.             if (pass == 1) {
  695.                 if (!((l = new_symbol(label)) -> attr)) {
  696.                 l -> attr = FORWD + VAL;
  697.                 address = expr();
  698.                 if (!forwd) l -> valu = address;
  699.                 }
  700.             }
  701.             else {
  702.                 if (l = find_symbol(label)) {
  703.                 l -> attr = VAL;
  704.                 address = expr();
  705.                 if (forwd) error('P');
  706.                 if (l -> valu != address) error('M');
  707.                 }
  708.                 else error('P');
  709.             }
  710.             }
  711.             else error('L');
  712.             break;
  713.  
  714.     case IF:    if (++ifsp == IFDEPTH) fatal_error(IFOFLOW);
  715.             address = expr();
  716.             if (forwd) { error('P');  address = TRUE; }
  717.             if (off) { listhex = FALSE;  ifstack[ifsp] = NULL; }
  718.             else {
  719.             ifstack[ifsp] = address ? ON : OFF;
  720.             if (!address) off = TRUE;
  721.             }
  722.             break;
  723.  
  724.     case INCL:  listhex = FALSE;  do_label();
  725.             if ((lex() -> attr & TYPE) == STR) {
  726.             if (++filesp == FILES) fatal_error(FLOFLOW);
  727.             if (!(filestk[filesp] = fopen(token.sval,"r"))) {
  728.                 --filesp;  error('V');
  729.             }
  730.             }
  731.             else error('S');
  732.             break;
  733.  
  734.     case ORG:   u = expr();
  735.             if (forwd) error('P');
  736.             else {
  737.             pc = address = u;
  738.             if (pass == 2) hseek(pc);
  739.             }
  740.             do_label();
  741.             break;
  742.  
  743.     case PAGE:  listhex = FALSE;  do_label();
  744.             if ((lex() -> attr & TYPE) != EOL) {
  745.             unlex();  pagelen = expr();
  746.             if (pagelen > 0 && pagelen < 3) {
  747.                 pagelen = 0;  error('V');
  748.             }
  749.             }
  750.             eject = TRUE;
  751.             break;
  752.  
  753.     case TITLE: listhex = FALSE;  do_label();
  754.             if ((lex() -> attr & TYPE) == EOL) title[0] = '\0';
  755.             else if ((token.attr & TYPE) != STR) error('S');
  756.             else strcpy(title,token.sval);
  757.             break;
  758.  
  759.     case VAR:   if (label[0]) {
  760.             if (pass == 1) {
  761.                 if (!((l = new_symbol(label)) -> attr)
  762.                 || (l -> attr & SOFT)) {
  763.                 l -> attr = FORWD + SOFT + VAL;
  764.                 address = expr();
  765.                 if (!forwd) l -> valu = address;
  766.                 }
  767.             }
  768.             else {
  769.                 if (l = find_symbol(label)) {
  770.                 address = expr();
  771.                 if (forwd) error('P');
  772.                 else if (l -> attr & SOFT) {
  773.                     l -> attr = SOFT + VAL;
  774.                     l -> valu = address;
  775.                 }
  776.                 else error('M');
  777.                 }
  778.                 else error('P');
  779.             }
  780.             }
  781.             else error('L');
  782.             break;
  783.     }
  784.     return;
  785. }
  786.