/*****************************************************************************
*           Change Log
*  Date     | Change
*-----------+-----------------------------------------------------------------
* 27-Nov-85 | [1.20] Created
*  4-Dec-85 | [1.104] star is set to dot-1
*  4-Dec-85 | [1.113] Accept = for #
* 11-Dec-85 | [1.164] Reject any characters not in the bcd character set.
* 20-Dec-85 | [1.170] Give error if unrecognized operand for DCW
* 20-Dec-85 | [1.171] Issue error only on pass 1
* 24-Nov-91 | [1.177] <jmn> converted to C6.0
* 24-Nov-91 | [1.177] <jmn> memory.h => automem.h, avoid conflict with ANSI
*           | name 
* 24-Nov-91 | [1.177] <jmn> mach.h => machmem.h
*  3-Dec-91 | [1.220] <jmn> store C_bit with all stored data so that we
*           | can tell difference between initialized and passed-over storage
*  3-Dec-91 | [1.220] <jmn> use CHAR_GM for groupmark
* 22-Dec-91 | [1.257] <jmn> allow & for signed constant
* 22-Dec-91 | [1.257] <jmn> handle symbolic addresses e.g. DCW +FOO
*****************************************************************************/
#include <boolean.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#include <pos.h> 
#include <machmem.h>
#include <automem.h>
#include <operands.h>
#include <err.h>
#include <bcd.h>
#include <expr.h>
#include <list.h>
#include <ste.h>
#include <sym.h>
#include <ds.h>
#include <error.h>
#include <operand.h>
#include <chars.h>


/****************************************************************************
*				 signed_const
* Inputs:
*       int sign:	<0 for minus bits
*			>0 for plus bits
*			=0 for no bits
*	boolean need_wm: true if wm required, false otherwise
*	int pass: Pass number
* Effect: 
*       Declares a signed constant.  This may be of the form
*	+nnn or -nnn for digits, or +NAME or -NAME for address
*	values
****************************************************************************/

static void signed_const(int sign,boolean need_wm,int pass)
    {
     /* operands[0] is our constant */

     char str[80];
     int width;
     int val;
     int twidth;
     int i;

     if(sign==0)
     	strcpy(str,operands[0]);
     else
        strcpy(str,&operands[0][1]);

     if(isalpha(str[0]))
        { /* name */
	 ste * sy;
	 char msg[80];
	 int loc = dot;
	 unsigned int addr;

	 if(!expr(str, &addr, true, expr_lit_illegal, pass, false))
	    { /* failed */
	     dot += 3; /* names take up 3 characters */
	     return;
	    } /* failed */
	 if(pass == 2)
	    { /* lay it down */
	     unsigned ho = dot;

	     if(sign < 0)
		addr = comp16K(addr);
	     write_operand(addr);
	     if(need_wm)
		memory[ho] |= word_mark;
	    } /* lay it down */
	 return;
	} /* name */
     width = strlen(str);

     /* simple syntax check: twidth == width? */

     val = atoi(str);

#if 0  /* rewrite for C6.0 */
     if(twidth != width)
        { /* bogus */
	 error(err_operand,"Illegal characters in numeric constant");
	} /* bogus */
#endif

     for(i=0;i<width;i++)
        { /* store number */
	 unsigned loc;
	 char bcd;

	 loc = check_memory(dot+i);
	 bcd = ascii_to_bcd(str[i]);
	 if(bcd > 63 || bcd < 0)
	    { /* illegal character */
	     error(err_operand,"Illegal BCD character in literal");
	     bcd = 0;	/* Make it a space */
	    } /* illegal character */
	 memory[loc] = C_bits | bcd;

	} /* store number */

     if(need_wm)
        { /* store WM */
	 unsigned loc;

	 loc = check_memory(dot);

	 memory[loc] |= word_mark;
	} /* store WM */

     if(sign < 0)
        { /* store minus */
	 unsigned loc;
	 loc = check_memory(dot + width - 1);

	 memory[loc] |= B_bits;
	} /* store minus */
     else
     if(sign > 0)
        { /* store plus */
	 unsigned loc;
	 loc = check_memory(dot + width - 1);

	 memory[loc] |= BA_bits;
	} /* store plus */

     dot += width;
    }

/****************************************************************************
*                                     dc
* Inputs:
*	int pass: Pass number we are on
*       boolean need_wm: True to put wordmark in field
*			 False for no word mark
* Result: int
*	Amount of dot offset
* Effect: 
*       Defines storage with or without word mark as indicated
****************************************************************************/

static int dc(int pass,boolean need_wm)
    {
     int val;
     int noperands;
     boolean GMWM = false;
     int cnt;
     int i;
     int old_dot = dot;
     int offset = 0;

     /* Check the operand.  If a literal, declare that space.  If
        an expression, allocate that much space
     */

     check_sym("dc - before scan");
     noperands = scan_operands();
     check_sym("dc - after scan");

     if(noperands == 0)
        { /* bogus */
	 if(pass==1)
	    error(err_operand,"DC/DCW requires operand");
         if(pass == 2)
	    { /* list it */
	     list_line();
	     emit_listing();
	    } /* list it */
	 return 0;
	} /* bogus */

     if(noperands == 2)
        { /* want GMWM? */
	 if(strcmp(operands[1],"G") == 0)
	 	GMWM = true;
	 else
	    { /* bogus */
	     if(pass==1)
	         error(err_operand,"DC/DCW must have only one operand");
	     if(pass == 2)
	        { /* list it */
		 list_line();
		 emit_listing();
		} /* list it */
	     return 0;
	    } /* bogus */
	} /* want GMWM? */
	 

     /* Valid values are:
ok     	+ddd	(ddd digits)  stores that many digits with AB bits on L/O
ok	-ddd	(ddd digits)  stores that many digits with B bit on L/O
ok	ddd	(ddd digits)  stores that many digits with no zones on L/O
ok	#dd	(dd digits)   stores that many spaces
ok	@...@   	      stores alpha constant
nyi	+expr		      stores address constant
	<any>,G		      indicates GM/WM must follow
     */

     switch(operands[0][0])
        { /* decode constant */
	 case '#':	/* space: #dd */
	 case '=':
	 	check_sym("processing # in dc");
	 	val = atoi(&operands[0][1]);
#if 0 /* rewrite for C6.0 */
		if(cnt != strlen(operands[0])-1)
		   { /* bad characters */
		    if(pass==1)
		       error(err_operand,"Bad characters in # value");
		    /* return; */
		   } /* bad characters */
#endif
		if(val+dot > 16000)
		   { /* too big */
		    char msg[80];
		    sprintf(msg,"Memory overflow: #%d + %d",val,dot);
		    if(pass==2)
		       error(err_operand,msg);
		    return 0;
		   } /* too big */
		for(i=0;i<val;i++)
		   { /* store it */
		    unsigned loc;

		    loc = check_memory(dot+i);
		    memory[loc] = C_bits | 0;
		   } /* store it */

		if(need_wm)
		   { /* mark it */
		    unsigned loc;

		    loc = check_memory(dot);
		    memory[loc] |= word_mark;
		   } /* mark it */
		dot += val;
	 	check_sym("processed # in dc");
		break;
	case '@': /* literal */
		check_sym("entering literal in dc");
		if(dot+strlen(operands[0])-2>16000)
		   { /* overflow */
		    if(pass==1)
		        error(err_operand,"Memory overflow");
		    return 0;
		   } /* overflow */
		for(i=1;i<strlen(operands[0])-1;i++)
		   { /* store it */
		    unsigned loc;

		    loc = check_memory(dot-1+i);

		    memory[loc] = C_bits | ascii_to_bcd(operands[0][i]);
		   } /* store it */

		if(need_wm)
		   { /* mark it */
		    memory[check_memory(dot)] |= word_mark;
		   } /* mark it */

		dot += strlen(operands[0]) - 2;

		if(GMWM)
		   memory[check_memory(dot)] = C_bits | ascii_to_bcd(CHAR_GM) | word_mark;
		    
		check_sym("entered literal in dc");
		break;
	case '+':
	case '&':
		 signed_const(1,need_wm,pass);
		 break;
	case '-':
		signed_const(-1,need_wm,pass);
		break;
	case '0':
	case '1':
	case '2':
	case '3':
	case '4':
	case '5':
	case '6':
	case '7':
	case '8':
	case '9':
		signed_const(0,need_wm,pass);
		break;
        default:
		if(pass==2)
		   error(err_operand,"Illegal character in constant");
		break;
	} /* decode constant */

     offset = dslabel(pass,0, dot - old_dot);

     sdot = dot;
     star = dot - 1;

     if(GMWM) 
        { /* gmwm */
	 dot++;
	 offset += 1;
	} /* gmwm */
     sdot = dot;
     star = dot - 1;
     return offset;
    }

/****************************************************************************
*                                    dcop
* Inputs:
*       int opindex: (not used)
*	int pass: 1 or 2
* Effect: 
*       Defines storage without word mark
****************************************************************************/

void dcop(int opindex,int pass)
    {
     int dotoffset;

     dotoffset = dc(pass,false);
     if(pass==2)
        { /* list it */
	 list_addr(dot-1-dotoffset);
	 list_line();
	 emit_listing();
	} /* list it */
     return;
    }

/****************************************************************************
*                                    dcwop
* Inputs:
*       int opindex: (not used)
*	int pass: 1 or 2
* Effect: 
*       Defines storage with word mark
****************************************************************************/

void dcwop(int opindex,int pass)
    {
     int dotoffset;
     dotoffset = dc(pass,true);

     if(pass==2)
        { /* list it */
	 list_addr(dot-1-dotoffset);
	 list_line();
	 emit_listing();
	} /* list it */
     return;
    }
