/*****************************************************************************
*           Change Log
*  Date     | Change
*-----------+-----------------------------------------------------------------
* 26-Nov-85 | [1.151] Created
*  4-Dec-85 | [1.105] Recognize compare instr as having valid literal
*           | second operand
*  6-Dec-85 | [1.148] Allow extra space for NUL in label
* 28-Dec-85 | [1.174] Allow implicit d-char on no-operand instructions (WM)
* 24-Nov-91 | [1.177] <jmn> memory.h => automem.h
* 24-Nov-91 | [1.177] <jmn> converted to C6.0
* 24-Nov-91 | [1.177] <jmn> mach.h => machmem.h
* 25-Nov-91 | [1.185] <jmn> literal now accepts ', +, -, @ as leading lit
*           | indicators
* 26-Nov-91 | [1.198] <jmn> all calls on expr to evaluate literals specifies
*           | 'false' for the report_undefined
* 26-Nov-91 | [1.204] <jmn> better checking for multiply-defined labels
*           | use startloc.h
* 26-Nov-91 | [1.212] <jmn> added blankop predicate, allow propagation of
*           | DC/DCW opcodes
* 27-Nov-91 | [1.213] <jmn> allow for indented labels
*****************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <boolean.h>
#include <string.h>

#include <line.h>
#include <err.h>
#include <asmops.h>
#include <automem.h> 
#include <machmem.h>
#include <ste.h>
#include <operands.h>
#include <expr.h>
#include <autocode.h>
#include <sym.h>
#include <error.h> 
#include <pass1.h>
#include <operand.h>
#include <startloc.h>
#include <da.h>

static boolean compare;

/* Assembler state */

extern int dot;	/* current location counter */
extern int star;	/* Value of '*' */
extern int sdot;
extern unsigned maxdot;
extern unsigned litoffset;
extern FILE * reader;
extern char line[81];
extern boolean debug;
extern boolean debug_sym;
extern boolean saw_end;

static int lastop = 0;

/****************************************************************************
*                                  isliteral
* Inputs:
*       char * str: String which might be literal operand
* Result: boolean
*       true if literal operand, false if not
****************************************************************************/

boolean isliteral(char * str)
    {
     switch(str[0])
        { /* try leading char */
	 case '@':
	 case '\'':
	 case '+':
	 case '-':
	 case '&':
		 return true;
	 default:
		 if(strrchr(str,'#') || strrchr(str,'='))
		    return true; /* literaloid */
	} /* try leading char */
     return false;
    }

/****************************************************************************
*                                    pass1
* Effect: 
*       Runs pass 1 of the assembler
****************************************************************************/

void pass1()
    {
     star = sdot = dot = STARTLOC;
     maxdot = 0;
     litoffset = 0;
     lastop = 0;

     while(true)
        { /* pass1 loop */
	 int opindex;
	 
	 if(saw_end) 
	    break;

	 readline();
	 if(feof(reader)) 
	    break;

	 if(line[0]=='*') 
	    continue;	/* comment card */

	 sdot = dot;

	 if(blankop() && lastop != 0)
	    { /* propagate it */
	     opindex = lastop + 1;
	    } /* propagate it */
         else
	    { /* new op */
	     opindex = opcode_1();
	     lastop = (optable[opindex].propagate ? opindex : 0);
	    } /* new op */

	 (*optable[opindex].processor)(opindex,1);
	 check_sym("end of processing line in pass 1");
	 maxdot = max(dot,maxdot);
	 fix_da();
	} /* pass1 loop */
     fprintf(stderr,"End of Pass 1\n");

    }

/****************************************************************************
*                                  getlabel
* Result: getlabelval *
*       Pointer to label structure (name is NUL terminated)
*	NULL if label string is empty
* Effects:
*	Allocates a heap string for the label, in result->name.
*	If label is indented result->left is true
****************************************************************************/

getlabelval * getlabel()
    {
     static getlabelval v;
     char * p;
     char * symname;
     char * sym = &line[LABEL_START];
     int len;
     char * t = sym;
     boolean left = false;
     
     if(*sym == ' ' && *(sym+1) != ' ')
        { /* indented label */
	 t = sym+1;
	 left = true;
	} /* indented label */
     p = strchr(t,' ');	/* get end of symbol */
     if(p==NULL) 
	return NULL;	/* unexplained failure */
     len = p - t;
     if(len == 0) 
	return NULL;	/* No label */
     if(len > 6)
        { /* indented label */
	 /* We get here if we had an indented label that ran into the
	    opcode field.  The label is 6 chars long or we would not
	    have had a len > 6:
	    	_FOOBARMLCWA FOO
		^           ^
	 */
	 len = 6;
	} /* indented label */

     symname = malloc(len+1);  /* +1 plus one for NUL */
     strncpy(symname,t,len);	/* count includes terminal NUL */
     symname[len] = '\0';
     if(debug_sym)
        printf("Symbol @0x%x length %d = \"%-*.*s\"\n",
     			symname, len, len, len, symname);
     v.name = symname;
     v.left = left;
     return &v;
    }

/****************************************************************************
*                                    label_1
* Result: ste *
*	Symbol table entry of label
*	NULL if no label, or error occurred
* Effect: 
*       Enters the label in the symbol table
****************************************************************************/

ste * label_1()
    {
     getlabelval * v;
     ste * sy = NULL;

     v = getlabel();

     if(v != NULL)
        { /* enter label */
	 if(v->left == ' ')
	    { /* right-indented */
	     error(err_label,"Indented label not permitted here");
	     return NULL;
	    } /* right-indented */
#if 1
	 sy = lookup(v->name);
	 if(sy != NULL)
	    { /* multiply defined */
	     sy->muldef = true;
	     free(v->name);		       /* free string from getlabel */
	    } /* multiply defined */
	 else
	    { /* unique */
	     sy = enter(v->name,dot);
	    } /* unique */
#else
	 sy = enter(v->name,dot);
#endif
	} /* enter label */
    return sy;
    }

/****************************************************************************
*                                  legal_op
* Inputs:
*       int i: Table index
* Result: boolean
*       true if legal, false if not
****************************************************************************/

boolean legal_op(int i)
    {
     int opp;
     for(opp=0;opp<OP_LENGTH;opp++)
         { /* compare */
	  if(line[opp+OP_START] != optable[i].mnemonic[opp]) 
	     return false;
	 } /* compare */
      return true;
    }

/****************************************************************************
*                                   blankop
* Result: boolean
*       true if opcode field is entirely blank
*	false if opcode field contains something
****************************************************************************/

boolean blankop()
    {
     int opp;

     for(opp=0;opp<OP_LENGTH;opp++)
         { /* compare */
	  if(line[opp+OP_START] != ' ') 
	     return false;
	 } /* compare */
      return true;
     
    }

/****************************************************************************
*                                   opcode_1
* Result: int
*	index into the opcode table
* Effect: 
*       Processes the opcode
****************************************************************************/

int opcode_1()
    {
     int i;
     boolean found = false;
     
     compare = false;
     
     for(i=0;strlen(optable[i].mnemonic)>0;i++)
        { /* check op */

	 found = legal_op(i);
	 if(found) 
	    break;

	} /* check op */

     if(!found)
        { /* error */
	 char msg[80];
	 sprintf(msg,"Illegal operation '%.5s'",&line[OP_START]);
	 error(err_opcode,msg);
	 i = 0;
	} /* error */

     if(strcmp(optable[i].mnemonic,"C    ") == 0) 
	compare = true;

     return i;
    }

/****************************************************************************
*                                  operands_1
* Inputs:
*	int opindex: Index into operator table
* Effect: 
*       Processes the operands field
****************************************************************************/

void operands_1(int opindex)
    {
     int i;
     int noperands;
     unsigned val;

     noperands = scan_operands();

     if(debug)
        { /* print debug stuff */
	 printf("%d operands:\n",noperands);
	 for(i=0;i<noperands;i++)
	    { /* process operand */
	     printf("     %d: \"%s\"\n",i,operands[i]);
	    } /* process operand */
	} /* print debug stuff */


     switch(noperands)
        { /* noperand decode */
	 case 0: /* No operands */
	 	star++;
		if(optable[opindex].d != ' ')
		   { /* implicit d-char */
		    star++;
		    dot++;
		   } /* implicit d-char */
	 	break;	/* do nothing */
	 case 1:
	 	switch(optable[opindex].format)
		   { /* format decode */
/* d */		    case op_d:
			star++;
			dot++;
			break;
/* III */	    case op_I:
		        star += 3;
		        dot += 3;
			if(optable[opindex].d != ' ')
			   { /* implicit d-char */
			    star++;
			    dot++;
			   } /* implicit d-char */
			break;
/* AAA */	    case op_A:
		        star += 3;
		        dot += 3;
			break;
/* AAA d */	    case op_A|op_d:
		    	star++;
		        dot++;
			break;
/* III d */	    case op_I|op_d:
		    	star++;
		        dot++;
			break;
/* AAA BBB */	    case op_A|op_B:
			if(isliteral(operands[0])) 
			   expr(operands[0],&val,false,expr_lit_pool,1,false);
		        star += 3;
		        dot += 3;
			break;
/* III BBB */	    case op_I|op_B:
		        star += 3;
		        dot += 3;
			if(optable[opindex].d != ' ')
			   { /* implicit d-char */
			    star++;
			    dot++;
			   } /* implicit d-char */
			break;
/* AAA BBB d */	    case op_A|op_B|op_d:
			if(isliteral(operands[0])) 
			   expr(operands[0],&val,false,expr_lit_pool,1,false);
		        dot += 3;
			break;
/* III BBB d */	    case op_I|op_B|op_d:
		        dot += 3;
			break;
		   } /* format decode */
		break;
	 case 2:
	 	switch(optable[opindex].format)
		   { /* format decode */
/* d */		    case op_d:
		    	error(err_operand,"Too many operands");
		        star += 4;
			dot += 4;
			break;
/* AAA */	    case op_A:
		    	error(err_operand,"Too many operands");
		        star += 3;
			dot += 3;
			break;
/* III */	    case op_I:
		    	error(err_operand,"Too many operands");
		        star += 3;
			dot += 3;
			if(optable[opindex].d != ' ')
			   { /* implicit d-char */
			    star++;
			    dot++;
			   } /* implicit d-char */
			break;
/* AAA d */	    case op_A|op_d:
		        star += 4;
		        dot += 4;
			break;
/* III d */	    case op_I|op_d:
		        star += 4;
		        dot += 4;
			break;
/* AAA BBB */	    case op_A|op_B:
			if(isliteral(operands[0])) 
			   expr(operands[0],&val,false,expr_lit_pool,1,false);
			if(compare && isliteral(operands[1])) 
			   expr(operands[1],&val,false,expr_lit_pool,1,false);
		        star += 6;
		    	dot += 6;
			break;
/* III BBB */	    case op_I|op_B:
		        star += 6;
		    	dot += 6;
			if(optable[opindex].d != ' ')
			   { /* implicit d-char */
			    star++;
			    dot++;
			   } /* implicit d-char */
			break;
/* AAA BBB d */	    case op_A|op_B|op_d:
			if(isliteral(operands[0])) 
			   expr(operands[0],&val,false,expr_lit_pool,1,false);
		        star += 6;
		        dot += 6;
			break;
/* III BBB d */	    case op_I|op_B|op_d:
		        star += 6;
		        dot += 6;
			break;
		   } /* format decode */
		break;
	 case 3:
	 	switch(optable[opindex].format)
		   { /* format decode */
/* d */		    case op_d:
		    	error(err_operand,"Too many operands");
		        star += 7;
			dot += 7;
			break;
/* AAA */	    case op_A:
		    	error(err_operand,"Too many operands");
		        star += 7;
			dot += 7;
			break;
/* III */	    case op_I:
		    	error(err_operand,"Too many operands");
		        star += 7;
			dot += 7;
			break;
/* d     */
/* AAA d */	    case op_A|op_d:
		    	error(err_operand,"Too many operands");
		        star += 7;
			dot += 7;
			break;
/* III d */	    case op_I|op_d:
		    	error(err_operand,"Too many operands");
		        star += 7;
			dot += 7;
			break;
/* AAA BBB */	    case op_A|op_B:
			if(isliteral(operands[0])) 
			   expr(operands[0],&val,false,expr_lit_pool,1,false);
		    	error(err_operand,"Too many operands");
		        star += 7;
			dot += 7;
			break;
/* AAA BBB d */	    case op_A|op_B|op_d:
			if(isliteral(operands[0])) 
			   expr(operands[0],&val,false,expr_lit_pool,1,false);
		        star += 7;
		        dot += 7;
			break;
/* III BBB d */	    case op_I|op_B|op_d:
		        star += 7;
		        dot += 7;
			break;
		   } /* format decode */
		break;
	} /* noperand decode */
    }
