/*
*      Z80 - Assembler
*      Copyright (C) 1987-1992 by Udo Munk
*
*      History:
*      17-SEP-1987 Development under Digital Research CP/M 2.2
*      28-JUN-1988 Switched to Unix System V.3
*/

/*
*      Dieses Modul enthaelt die Funktionen zur Bearbeitung
*      aller Pseudo-Op-Codes
*/

#include <stdio.h>
#include <ctype.h>
#include "z80a.h"
#include "z80aglb.h"

/*
*      Diese Funktion behandelt den Pseudo-Op-Code ORG
*/
op_org()
{
       register int i;

       if (!gencode)
               return(0);
       i = eval(operand);
       if (i < pc) {
               asmerr(E_MEMOVR);
               return(0);
       }
       if (pass == 1) {                /* PASS 1 */
               if (!prg_flag) {
                       prg_adr = i;
                       prg_flag++;
               }
       } else {                        /* PASS 2 */
               if (++prg_flag > 2)
                       obj_fill(i - pc);
               sd_flag = 2;
       }
       pc = i;
       return(0);
}

/*
*      Diese Funktion behandelt den Pseudo-Op-Code EQU
*/
op_equ()
{
       struct sym *get_sym();

       if (!gencode)
               return(0);
       if (pass == 1) {                /* Pass 1 */
               if (get_sym(label) == NULL) {
                       sd_val = eval(operand);
                       if (put_sym(label, sd_val))
                               fatal(F_OUTMEM, "symbols");
               } else
                       asmerr(E_MULSYM);
       } else {                        /* Pass 2 */
               sd_flag = 1;
               sd_val = eval(operand);
       }
       return(0);
}

/*
*      Diese Funktion behandelt den Pseudo-Op-Code DEFL
*/
op_dl()
{
       if (!gencode)
               return(0);
       sd_flag = 1;
       sd_val = eval(operand);
       if (put_sym(label, sd_val))
               fatal(F_OUTMEM, "symbols");
       return(0);
}

/*
*      Diese Funktion behandelt den Pseudo-Op-Code DEFS
*/
op_ds()
{
       register int val;

       if (!gencode)
               return(0);
       if (pass == 1)
               if (*label)
                       put_label();
       sd_val = pc;
       sd_flag = 3;
       val = eval(operand);
       if (pass == 2)
               obj_fill(val);
       pc += val;
       return(0);
}

/*
*      Diese Funktion behandelt den Pseudo-Op-Code DEFB
*/
op_db()
{
       register int i;
       register char *p;
       register char *s;

       if (!gencode)
               return(0);
       i = 0;
       p = operand;
       if (pass == 1)
               if (*label)
                       put_label();
       while (*p) {
               if (*p == STRSEP) {
                       p++;
                       while (*p != STRSEP) {
                               if (*p == '\n' || *p == '\0') {
                                       asmerr(E_MISHYP);
                                       goto hyp_error;
                               }
                               ops[i++] = *p++;
                               if (i >= OPCARRAY)
                                       fatal(F_INTERN, "Op-Code buffer overflow");
                       }
                       p++;
               } else {
                       s = tmp;
                       while (*p != ',' && *p != '\0')
                               *s++ = *p++;
                       *s = '\0';
                       ops[i++] = eval(tmp);
                       if (i >= OPCARRAY)
                               fatal(F_INTERN, "Op-Code buffer overflow");
               }
               if (*p == ',')
                       p++;
       }
hyp_error:
       return(i);
}

/*
*      Diese Funktion behandelt den Pseudo-Op-Code DEFM
*/
op_dm()
{
       register int i;
       register char *p;

       if (!gencode)
               return(0);
       i = 0;
       p = operand;
       if (pass == 1)
               if (*label)
                       put_label();
       if (*p != STRSEP) {
               asmerr(E_MISHYP);
               return(0);
       }
       p++;
       while (*p != STRSEP) {
               if (*p == '\n' || *p == '\0') {
                       asmerr(E_MISHYP);
                       break;
               }
               ops[i++] = *p++;
               if (i >= OPCARRAY)
                       fatal(F_INTERN, "Op-Code buffer overflow");
       }
       return(i);
}

/*
*      Diese Funktion behandelt den Pseudo-Op-Code DEFW
*/
op_dw()
{
       register int i, len, temp;
       register char *p;
       register char *s;

       if (!gencode)
               return(0);
       p = operand;
       i = len = 0;
       if (pass == 1)
               if (*label)
                       put_label();
       while (*p) {
               s = tmp;
               while (*p != ',' && *p != '\0')
                       *s++ = *p++;
               *s = '\0';
               if (pass == 2) {
                       temp = eval(tmp);
                       ops[i++] = temp & 0xff;
                       ops[i++] = temp >> 8;
                       if (i >= OPCARRAY)
                               fatal(F_INTERN, "Op-Code buffer overflow");
               }
               len += 2;
               if (*p == ',')
                       p++;
       }
       return(len);
}

/*
*      Diese Funktion behandelt die Pseudo-Op-Codes
*      EJECT, LIST, NOLIST, PAGE, PRINT, TITLE, INCLUDE
*/
op_misc(op_code, dummy)
int op_code, dummy;
{
       register char *p, *d;
       static char fn[LENFN];
       static int incnest;
       static struct inc incl[INCNEST];

       if (!gencode)
               return(0);
       sd_flag = 2;
       switch(op_code) {
       case 1:                         /* EJECT */
               if (pass == 2)
                       p_line = ppl;
               break;
       case 2:                         /* LIST */
               if (pass == 2)
                       list_flag = 1;
               break;
       case 3:                         /* NOLIST */
               if (pass == 2)
                       list_flag = 0;
               break;
       case 4:                         /* PAGE */
               if (pass == 2)
                       ppl = eval(operand);
               break;
       case 5:                         /* PRINT */
               if (pass == 1) {
                       p = operand;
                       while (*p) {
                               if (*p != STRSEP)
                                       putchar(*p++);
                               else
                                       p++;
                       }
                       putchar('\n');
               }
               break;
       case 6:                         /* INCLUDE */
               if (incnest >= INCNEST) {
                       asmerr(E_INCNEST);
                       break;
               }
               incl[incnest].inc_line = c_line;
               incl[incnest].inc_fn = srcfn;
               incl[incnest].inc_fp = srcfp;
               incnest++;
               p = line;
               d = fn;
               while(isspace(*p))      /* white space bis INCLUDE ueberlesen */
                       p++;
               while(!isspace(*p))     /* INCLUDE ueberlesen */
                       p++;
               while(isspace(*p))      /* white space bis Filename ueberlesen */
                       p++;
               while(!isspace(*p) && *p != COMMENT) /* Filename uebernehmen */
                       *d++ = *p++;
               *d = '\0';
               if (pass == 1) {        /* PASS 1 */
                       if (!ver_flag)
                               printf("   Include %s\n", fn);
                       p1_file(fn);
               } else {                /* PASS 2 */
                       sd_flag = 2;
                       lst_line(0, 0);
                       if (!ver_flag)
                               printf("   Include %s\n", fn);
                       p2_file(fn);
               }
               incnest--;
               c_line = incl[incnest].inc_line;
               srcfn = incl[incnest].inc_fn;
               srcfp = incl[incnest].inc_fp;
               printf("   Resume  %s\n", srcfn);
               if (list_flag && (pass == 2)) {
                       lst_header();
                       lst_attl();
               }
               sd_flag = 4;
               break;
       case 7:                         /* TITLE */
               if (pass == 2) {
                       p = line;
                       d = title;
                       while (isspace(*p))     /* white space bis TITLE ueberlesen */
                               p++;
                       while (!isspace(*p))    /* TITLE ueberlesen */
                               p++;
                       while (isspace(*p))     /* white space bis Titel ueberlesen */
                               p++;
                       if (*p == STRSEP)
                               p++;
                       while (*p != '\n' && *p != STRSEP && *p != COMMENT)
                               *d++ = *p++;
                       *d = '\0';
               }
               break;
       default:
               fatal(F_INTERN, "illegal opcode for function op_misc");
               break;
       }
       return(0);
}

/*
*      Diese Funktion behandelt die Pseudo-Op-Codes
*      IFDEF, IFNDEF, IFEQ, IFNEQ, ELSE, ENDIF
*/
op_cond(op_code, dummy)
int op_code, dummy;
{
       register char *p, *p1, *p2;
       static int condnest[IFNEST];
       struct sym *get_sym();
       char *strchr();

       switch(op_code) {
       case 1:                         /* IFDEF */
               if (iflevel >= IFNEST) {
                       asmerr(E_IFNEST);
                       break;
               }
               condnest[iflevel++] = gencode;
               if (gencode)
                       if (get_sym(operand) == NULL)
                               gencode = 0;
               break;
       case 2:                         /* IFNDEF */
               if (iflevel >= IFNEST) {
                       asmerr(E_IFNEST);
                       break;
               }
               condnest[iflevel++] = gencode;
               if (gencode)
                       if (get_sym(operand) != NULL)
                               gencode = 0;
               break;
       case 3:                         /* IFEQ */
               if (iflevel >= IFNEST) {
                       asmerr(E_IFNEST);
                       break;
               }
               condnest[iflevel++] = gencode;
               p = operand;
               if (!*p || !(p1 = strchr(operand, ','))) {
                       asmerr(E_MISOPE);
                       break;
               }
               if (gencode) {
                       p2 = tmp;
                       while (*p != ',')
                               *p2++ = *p++;
                       *p2 = '\0';
                       if (eval(tmp) != eval(++p1))
                               gencode = 0;
               }
               break;
       case 4:                         /* IFNEQ */
               if (iflevel >= IFNEST) {
                       asmerr(E_IFNEST);
                       break;
               }
               condnest[iflevel++] = gencode;
               p = operand;
               if (!*p || !(p1 = strchr(operand, ','))) {
                       asmerr(E_MISOPE);
                       break;
               }
               if (gencode) {
                       p2 = tmp;
                       while (*p != ',')
                               *p2++ = *p++;
                       *p2 = '\0';
                       if (eval(tmp) == eval(++p1))
                               gencode = 0;
               }
               break;
       case 98:                        /* ELSE */
               if (!iflevel)
                       asmerr(E_MISIFF);
               else
                       if ((iflevel == 0) || (condnest[iflevel - 1] == 1))
                               gencode = !gencode;
               break;
       case 99:                        /* ENDIF */
               if (!iflevel)
                       asmerr(E_MISIFF);
               else
                       gencode = condnest[--iflevel];
               break;
       default:
               fatal(F_INTERN, "illegal opcode for function op_cond");
               break;
       }
       sd_flag = 2;
       return(0);
}

/*
*      Diese Funktion behandelt die Pseudo-Op-Codes
*      EXTRN und PUBLIC
*/
op_glob(op_code, dummy)
int op_code, dummy;
{
       if (!gencode)
               return(0);
       sd_flag = 2;
       switch(op_code) {
       case 1:                         /* EXTRN */
               break;
       case 2:                         /* PUBLIC */
               break;
       default:
               fatal(F_INTERN, "illegal opcode for function op_glob");
               break;
       }
       return(0);
}