/*****************************************************************************
*           Change Log
*  Date     | Change
*-----------+-----------------------------------------------------------------
* 27-Nov-85 | [1.22] Created
* 30-Nov-85 | [1.57] Handle indexing
* 30-Nov-85 | [1.86] Call datacard to dump memory to output device
*  4-Dec-85 | [1.104] recognize ' for @
* 13-Dec-85 | [1.166] Removed cvbytes to separate module
* 13-Dec-85 | [1.166] Undid previous change
* 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
*  3-Dec-91 | [1.220] <jmn> use symbolic NOPERANDS since some pseudo-ops
*           | have more than 3
*  3-Dec-91 | [1.220] <jmn> use C_bits to determine valid memory locations
*  4-Dec-91 | [1.230] <jmn> use isdata to determine valid memory locations
* 22-Dec-91 | [1.259] <jmn> cvbytes: set C_bits
*****************************************************************************/

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

#include <line.h>
#include <automem.h>
#include <machmem.h>
#include <operand.h>
#include <bcd.h>
#include <boot.h>
#include <operands.h>
#include <instr.h>

char operands[NOPERANDS][81];
extern char line[81];

extern int dot;
extern boolean debug;
extern char * valstr();
extern unsigned start;

#define isdata(x) ( ( (x) & (word_mark | C_bits) ) != 0 )
#define barewm(x)   ( ( (x) & word_mark && !((x) & C_bits) ) )

/****************************************************************************
*                                scan_operands
* Result: int
*       Number of operands found (0,1,2,3,...,NOPERANDS)
* Effect: 
*       Loads the operands[i] fields with the operands
*
*	Operand form	value
*	<empty>		0
*	d		1
*	A		1	
*	A,		2
*	A,d		2
*	A,B		2
*	A,B,		3
*	A,B,d		3
****************************************************************************/

int scan_operands()
    {
     int i;
     int operand;
     char * pos = &line[OPERAND_START];
     char * epos = &line[80];
     boolean more;
     int noperands;
     int len;

     more = true;
     noperands = 0;

     for(operand=0;operand<NOPERANDS && more;operand++)
        { /* scan one operand */
	 int dpos;  /* Destination position */
	 boolean scanned;

	 dpos = 0;

	 scanned = false;		/* Not completely scanned */
	 while(!scanned)
	    { /* scan this operand */
	     char delimiter;

	     switch(*pos)		/* Look for terminator */
		{ /* decode */
		 case '@':		/* literal */
		 case '\'':
			    /*
			       @.......@
                           pos ^                      ^ epos
			    */

		 	    delimiter = *pos;

#if 0
		 	    if(debug)
			    	printf("scan_operands: starting backscan, pos = 0x%x, epos = 0x%x\n",pos,epos);
#endif
		 	    while(*epos-- != delimiter) ;

			    /*
			        @........@
			    pos ^       ^ epos
			    */

			    if(debug)
			    	printf("scan_operands: backscan complete, epos = 0x%x\n",epos);
			    len = epos - pos + 2;
			    strncpy(&operands[operand][dpos],pos,len);
			    operands[operand][dpos + len ] = '\0';

			    if(debug)
			    	printf("scan_operands: operands[%d]=\"%s\"\n",
					operand,operands[operand]);
			    dpos = strlen(operands[operand]);
			    pos = epos+2;

			    /*
			         @........@
				           ^ pos
			    */

			    if(debug)
			    	printf("scan_operands: ready to continue scan, dpos = %d, pos = 0x%x, *pos='%c'\n",
						dpos,pos,*pos);
			    break;
		 case ' ':		/* Space or comma terminate operand */
		 	    if(dpos>0) noperands++;
		 	    more = false;
			    scanned = true;
			    pos++;
			    break;
		 case ',':
		 	    scanned = true;
			    pos++;
			    noperands++;
			    break;
		 default:
		 	operands[operand][dpos] = *pos;
			dpos++;
			pos++;
		} /* decode */
	     operands[operand][dpos] = '\0'; /* Operand is empty to start */
#if 0
	     printf("operands[%d][%d] = \"%s\"\n",operand,dpos,operands[operand]);
#endif
	    } /* scan this operand */

	} /* scan one operand */
     return noperands;

    }

/****************************************************************************
*                                   cvbytes
* Inputs:
*       char * dest: Place to put converted bytes
*	unsigned val: Value to convert
* Effect: 
*       3 bcd characters representing the value are placed in dest[0..2]
*	Clobbers any WM or data flags
****************************************************************************/

void cvbytes(char * dest,unsigned val)
    {
     unsigned baseval;

     baseval = base(val);	/* base address value */

     if(debug)
     	printf("cvbytes(0x%x,%u (%s))\n",dest,val,valstr(val));
     if(val == expr_bad)
        { /* had error */
	 dest[0] = dest[1] = dest[2] = ascii_to_bcd('.') | C_bits;
	 return;
	} /* had error */
	 
     dest[2] = (char) (val % 10);   /* low order bcd char is units */
     if(dest[2] == '\0') dest[2] = 10;	/* 8-2 for 0 */

#if debug_cvbytes
     printf("dest[2]=%d (%c)\n",dest[2],bcd_to_ascii(dest[2]));
#endif

     dest[1] = (char) ((baseval / 10) % 10);
     if(dest[1] == '\0') dest[1] = 10;	/* 8-2 for 0 */

#if debug_cvbytes
     printf("dest[1]=%d (%c)\n",dest[1],bcd_to_ascii(dest[1]));
#endif

     dest[0] = (char) ((baseval / 100) % 10);
     if(dest[0] == '\0') dest[0] = 10;	/* 8-2 for 0 */

#if debug_cvbytes
     printf("dest[0]=%d (%c)\n",dest[0],bcd_to_ascii(dest[0]));
#endif

     /* That takes care of the easy stuff!  000..999 now stored */

     /* Now do decoding for 1000..3999	(A,B,AB = 1,2,3 on tens)
        and for 4000-15000		(A,B,AB = 1,2,3 * 4000 on units )
        and index registers

	range		hundreds	units
	 000-  999	-		-
	1000- 1999	A		-
	2000- 2999	B		-
	3000- 3999	AB		-
	4000- 4999	-		-
	5000- 5999	A		A
	6000- 6999	B		A
	7000- 7999	AB		A
	8000- 8999	-		-
	9000- 9999	A		B
       10000-10999	B		B
       11000-11999	AB		B
       12000-12999	-		-
       13000-13999	A		AB
       14000-14999	B		AB
       15000-15999	AB		AB
     */

     switch((baseval/1000) % 4)
        { /* hundreds zone */
	 case 0:  break;
	 case 1:  dest[0] |= A_bits;
	 	  break;
	 case 2:  dest[0] |= B_bits;
	 	  break;
	 case 3:  dest[0] |= BA_bits;
	 	  break;
	} /* hundreds zone */

     switch((baseval/4000) % 4)
        { /* units zone */
	 case 0:  break;
	 case 1:  dest[2] |= A_bits;
	 	  break;
	 case 2:  dest[2] |= B_bits;
	 	  break;
	 case 3:  dest[2] |= BA_bits;
	 	  break;
	} /* units zone */
	 
     /* Now decode the index value */

     switch(ixreg(val))
        { /* index */
	 case 0:  break;
	 case 1:  dest[1] |= A_bits;
	 	  break;
	 case 2:  dest[1] |= B_bits;
	 	  break;
	 case 3:  dest[1] |= BA_bits;
	 	  break;
	} /* index */
     
     dest[0] |= C_bits;
     dest[1] |= C_bits;
     dest[2] |= C_bits;
    }

/****************************************************************************
*                             debug_write_operand
* Inputs:
*	unsigned val: Value which was to be converted
*       char * bytes: Byte array written
* Effect: 
*       displays debug info
****************************************************************************/

void debug_write_operand(unsigned val,char * bytes)
    {
#if debug_cvbytes
     int i;
     printf("write_operand(%s) => \"",valstr(val));
     for(i=0;i<3;i++) printf("%c",bcd_to_ascii(bytes[i]));
     printf("\" (");
     for(i=0;i<3;i++) printf("\\%03o",bytes[i]);
     printf(")\n");
#endif
    }

/****************************************************************************
*                                write_operand
* Inputs:
*       unsigned val: Value to write as operand, uses encoded X1,X2,X3
* Effect: 
*       Writes 3 characters in memory at "dot", advances dot
****************************************************************************/

void write_operand(unsigned val)
    {
     char bytes[4];
     int i;

     cvbytes(bytes,val);
     debug_write_operand(val,bytes);
     for(i=0;i<3;i++)
        { /* deposit */
	 memory[dot] = C_bits | bytes[i];
	 dot++;
	} /* deposit */
    }

/****************************************************************************
*                               write_dchar
* Inputs:
*       char d: d-char value, in bcd
* Effect: 
*       Writes the d-char at dot
****************************************************************************/

void write_dchar(char d)
    {
     memory[dot] = C_bits | d;
     dot++;
    }

/****************************************************************************
*                                 dump_memory
* Effect: 
*       Dumps memory to output device
****************************************************************************/

void dump_memory()
    {
     int i;
     int lb;
     int ub;
     int nlb;

     for(lb=1;lb<16000;lb++) if(memory[lb]&C_bits) break;
     for(ub=15999;ub>0;ub--) if(memory[ub]&C_bits) break;

     printf("loc      data   %d..%d\n",lb,ub);
     printf("-----    ----\n");
     if(!WM(memory[lb])) printf("%5d:    ",lb);
     for(i=lb;i<=ub;i++)
        { /* dump memory */
	 if(WM(memory[i]))
	    { /* new field */
	     printf("\n%5d:    ",i);
	    } /* new field */
	 printf("%c",bcd_to_ascii(memory[i]));
	} /* dump memory */
     printf("\n");
	 
    }

/****************************************************************************
*                                 punch_card
* Effect: 
*       Punches cards on virtual card punch
****************************************************************************/

void punch_cards()
    {
     int i;
     int lb;
     int ub;
     int nlb;

     for(lb=1;lb<16000;lb++) if(memory[lb] & C_bits) break;
     for(ub=15999;ub>0;ub--) if(memory[ub] & C_bits) break;

     loadcard();

     nlb = lb;

     while(nlb <= ub)
        { /* write card */
	 nlb = datacard(nlb,ub);
	} /* write card */

     /* Now find all locations which have WM bits but no C_bits (so we
	want to set wms without disturbing the data which might have
	been already placed)
     */
     for(lb=1;lb<16000;lb++) if(barewm(memory[lb])) break;
     for(ub=15999;ub>0;ub--) if(barewm(memory[ub])) break;

     if(ub >= lb)
	wmcard(lb,ub);

     flushcard(i_B,start);
    }
