/*****************************************************************************c
*           Change Log
*  Date     | Change
*-----------+-----------------------------------------------------------------
*  3-Dec-91 | [1.220] <jmn> Created
*****************************************************************************/
#include <boolean.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <machmem.h>
#include <asmops.h>
#include <chars.h>
#include <ste.h>
#include <sym.h>
#include <error.h>
#include <err.h>
#include <operand.h>
#include <operands.h>
#include <automem.h>
#include <pass1.h>
#include <pos.h>
#include <automem.h>
#include <bcd.h>
#include <list.h>
#include <expr.h>

ste * last_da = NULL;
ste * next_da = NULL;

/****************************************************************************
*                                    daop_da
* Inputs:
*       int opindex: (not used)
*	int pass: 1 or 2
* Result: boolean
*	true if success
*	false if error
* Effect: 
*       Defines a storage area
****************************************************************************/

boolean daop_da(int opindex, int pass)
    {
     int noperands;
     int B;
     int L;
     char * p;
     int size;
     getlabelval * v;
     ste * sy;
     boolean rm = false;
     boolean gm = false;
     boolean clear = false;
     int ix = 0;
     int i;
     unsigned int addr;


     /*
	We have the following form:
	label	DA	nnnXmmm {,C | ,G | ,Ø ,Xn}*
     */

     check_sym("Before DA");
     noperands = scan_operands();
     if(noperands == 0)
        { /* missing operand */
	 if(pass == 2)
	    error(err_operand,"Missing operand to DA");
	 goto bad_exit;
	} /* missing operand */

     p = strchr(operands[0],'X');
     if(p == NULL)
        { /* no X */
	 if(pass == 2)
	    error(err_operand,"Must be of form BXL; X is missing");
	 goto bad_exit;
	} /* no X */
     *p = '\0';
     B = atoi(operands[0]);
     L = atoi(p+1);
     *p = 'X';

     for(i=1; i<noperands; i++)
        { /* handle remaining operands */
	 if(strcmp(operands[i],"C") == 0)
	    { /* clear */
	     clear = true;
	     continue;
	    } /* clear */
	 if(strcmp(operands[i],"G") == 0)
	    { /* gm */
	     gm = true;
	     continue;
	    } /* gm */
	 if(strcmp(operands[i],"Ø") == 0)
	    { /* rm */
	     rm = true;
	     continue;
	    } /* rm */
	 if(strcmp(operands[i],"X1") == 0 ||
	    strcmp(operands[i],"X2") == 0 ||
	    strcmp(operands[i],"X3") == 0)
	    { /* X */
	     ix = ixbits(operands[i]);
	     continue;
	    } /* X */

	 if(pass == 2)
	    error(err_operand,"Unknown option on DA");
	 goto bad_exit;
	} /* handle remaining operands */

     addr = dot + ix;
     
     size = B * (L + (rm ? 1 : 0)) + (gm ? 1 : 0);

     if(size > 16000)
        { /* bogus */
	 char msg[100];
	 sprintf(msg,"Size %dX%d exceeds 16000!",B,L);
	 if(pass == 2)
	    error(err_operand,msg);
	 goto bad_exit;
	} /* bogus */

     if(size+dot > 16000)
        { /* too big */
	 char msg[80];
	 sprintf(msg,"Memory overflow: DA %dX%d + %d",B,L,dot);
	 if(pass == 2)
	    error(err_operand,msg);
	 goto bad_exit;
	} /* too big */

     v = getlabel();
     if(v != NULL)
        { /* we have a label */
	 if(v->left)
	    { /* illegal */
	     if(pass == 2)
		error(err_label,"Indented label not allowed");
	     goto bad_exit;
	    } /* illegal */
	 sy = lookup(v->name);
	 if(sy == NULL)
	    { /* not found */
	     sy = enter(v->name,addr);
	     if(sy == NULL)
	        { /* failed to allocate */
		 return false;
		} /* failed to allocate */
	     sy->B = B;
	     sy->L = L;
	     sy->W = (rm ? L+1 : L);
	     sy->len = size;
	    } /* not found */
	 else
	    { /* multiply-defined? */
	     if(sy->val != addr)
	        { /* is multiply defined */
		 sy->muldef = true;
		 if(pass == 2)
		    muldef(sy,NULL);
		} /* is multiply defined */
	    } /* multiply-defined? */
	} /* we have a label */
     else
        { /* we have no label */
	 if(pass == 2)
	    error(err_label,"Label required for DA");
	    goto bad_exit;
	} /* we have no label */
	 

     if(clear)
        { /* clear it */

	 for(i=0; i<size;i++)
	    { /* store space */
	     unsigned loc;
	     loc = check_memory(dot+i);
	     memory[loc] = C_bits;
	    } /* store space */
	} /* clear it */
     if(rm)
        { /* set record marks */
	 for(i = L; i < size; i += L+1)
	    { /* set rm */
	     unsigned loc;
	     loc = check_memory(dot + i);
	     memory[loc] = C_bits | ascii_to_bcd(CHAR_RM);
	    } /* set rm */
	} /* set record marks */
     if(gm)
        { /* set gm */
	 unsigned loc;
	 loc = check_memory(dot+size);
	 memory[loc] = C_bits | ascii_to_bcd(CHAR_GM) | word_mark;
	} /* set gm */


     if(pass == 2)
        { /* list it */
	 list_addr(addr);
	 list_line();
	 emit_listing();
	} /* list it */

     next_da = sy;
     dot += size;
     sdot = dot;
     star = dot - 1;
     check_sym("After DA");
     return true;
bad_exit:
     if(pass == 2)
	{ /* list it */
	 list_addr(addr);
	 list_line();
	 emit_listing();
	} /* list it */
     return false;
    }

/****************************************************************************
*                                    daop_sub
* Inputs:
*       int opindex: (not used)
* Result: boolean
*       true if success
*	false if error
* Effect: 
*       Processes a sub-DA
****************************************************************************/

boolean daop_sub(int opindex, int pass)
    {
     getlabelval * v;
     int noperands;
     ste * sy;
     unsigned left;
     unsigned right;
     unsigned i;
     unsigned start;
     unsigned addr;

     /*
	label		l,h
	label		m
     */

     sy = last_da;

     noperands = scan_operands();
     switch(noperands)
        { /* how many? */
	 case 0:
		 if(pass == 2)
		    error(err_operand,"Missing operand on DA entry");
		 goto bad_exit;
	 case 1:
		 right = atoi(operands[0]);
		 if(right < 0 || right >= sy->L)
		    { /* illegal val */
		     if(pass == 2)
			error(err_operand,"Value is outside record size");
		     goto bad_exit;
		    } /* illegal val */
		 break;
	 case 2:
		 left  = atoi(operands[0]);
		 right = atoi(operands[1]);
		 if(left < 0 || left >= sy->L)
		    { /* illegal left */
		     if(pass == 2)
			error(err_operand,"Left field value outside record size");
		     goto bad_exit;
		    } /* illegal left */
		 if(right < 0 || right >= sy->L)
		    { /* illegal right */
		     if(pass == 2)
			error(err_operand,"Right field value outside record size");
		     goto bad_exit;
		    } /* illegal right */
		 if(right < left)
		    { /* inverse range */
		     if(pass == 2)
			error(err_operand,"Left of field must be < right of field");
		     goto bad_exit;
		    } /* inverse range */
		 start = unindexed(last_da->val);
		 for(i=start; i+left < start + sy->len; i+= sy->W)
		    { /* place WMs */
		     unsigned loc;
		     loc = check_memory(i+left);
		     /* Note that we do not set the C_bits here because
			we only want to set a WM
		     */
		     memory[loc] |= word_mark;
		    } /* place WMs */
		 break;
	 default:
		 if(pass == 2)
		    error(err_operand,"Too many operands on DA entry");
		 goto bad_exit;
	} /* how many? */

     addr = right + last_da->val;

     /*
	Now process the label (if any)
     */

     v = getlabel();

     if(v != NULL)
        { /* process label */
	 sy = lookup(v->name);
	 if(sy != NULL)
	    { /* duplicate? */
	     if(pass == 1)
	        { /* multiply-defined */
		 sy->muldef = true;
		} /* multiply-defined */

	     if(pass == 2 && sy->val != addr)
		{ /* duplicate */
		 muldef(sy,NULL);
		} /* duplicate */
	    } /* duplicate? */
	 else
	    { /* not dup */
	     sy = enter(v->name, addr);
	    } /* not dup */
	} /* process label */

     if(pass == 2)
	{ /* list it */
	 list_addr(addr);
	 list_line();
	 emit_listing();
	} /* list it */

     next_da = last_da;
     return true;

bad_exit:
     if(pass == 2)
	{ /* list it */
	 list_addr(addr);
	 list_line();
	 emit_listing();
	} /* list it */
     next_da = last_da;
     return false;
    }

/****************************************************************************
*                                    daop
* Inputs:
*       int opindex: used to determine main or sub
*	int pass: 1 | 2
* Result: boolean
*       true if correct
*	false if error
* Effect: 
*       Processes a DA declaration or its subdeclaration
****************************************************************************/

boolean daop(int opindex, int pass)
    {
     switch(optable[opindex].opcode)
        { /* decode option */
	 case 0: /* 'DA' */
		 return daop_da(opindex,pass);
	 case 1: /* subentry */
		 return daop_sub(opindex,pass);
	} /* decode option */
    }

/****************************************************************************
*                                   fix_da
* Result: void
*       
* Effect: 
*       Moves next_da to last_da and clears next_da
* Notes:
*	A DA declaration always sets 'next_da'
****************************************************************************/

void fix_da()
    {
     last_da = next_da;
     next_da = NULL;
    }
