/*****************************************************************************
*           Change Log
*  Date     | Change
*-----------+-----------------------------------------------------------------
* 30-Nov-85 | [1.86] Created
* 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> ignore C bit when moving data
*  3-Dec-91 | [1.220] <jmn> do not put anything in locations which do not
*           | have C_bits
*  4-Dec-91 | [1.230] <jmn> updated loader format to follow specs from
*           | Autocoder manual.  
*           | Added procedure to set wordmarks in uninitialized space
*****************************************************************************/

#include <stdio.h>
#include <boolean.h>
#include <memory.h>

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

extern FILE * punch;
extern boolean debug;
extern unsigned start;

int punched = 0;
/*
	Write bootload card images, then dump memory

#ifdef OBSOLETE
         1111111111222222222233333333334444444444555555555566
1234567890123456789012345678901234567890123456786012345678901

,008015,022029,036040N000000N0000001001
Ltttxxx,tt1tt2,tt3tt4,tt5tt6,tt7tt81001dddddddddddddddddddddddd...dddd
Ltttxxx)tt1tt2,tt3tt4,tt5tt6,tt7tt81001dddddddddddddddddddddddd...dddd
LtttxxxN000000N000000N000000N0000001001ddddddddddddddddddddddddd...ddd
#endif

         111111111122222222223333333333444444444455555555556666666666777777
123456789012345678901234567890123456789012345678601234567890123456789012345
       *      *      *      *      *   *      *      *      *      *   *
,008015,022029,036040,047054,061068/039,072001N000000N000000N0000001040BOOTSTR
.......................................Ltttxxx,tt1tt2,tt3tt4,tt5tt61040nnnn
       1      1      1      1      1   1      1      1      1      1   1
       				       ^       ^  ^   ^  ^   ^  ^  
                                       BOOT    +8 +11 +15+18 +22+25
*/

/* Starting data column  and width */
#define DATA 1
#define DATA_WIDTH 39

/* Starting boot column */
#define BOOT 40
/* Place to put R BOOT instruction */
#define BOOTRB (BOOT+28)

/* Number of wordmarks we can set */
#define NWM 6
#define NAM (NWM+2)
struct {unsigned loc;
	unsigned char op;
       } wordmarks[NAM];

/* Address of hundreds position where cvbytes is done */
unsigned setmarks[NWM] = {BOOT+8,BOOT+11,BOOT+15,BOOT+18,BOOT+22,BOOT+25};
unsigned allmarks[NAM] = {BOOT+1, BOOT+4, BOOT+8,BOOT+11,BOOT+15,BOOT+18,BOOT+22,BOOT+25};

static char card[81];		/* card image (1-based) */
static char lastcard[81];	/* previous card (1-based) */
static char outcard[81];	/* ASCII card (0-based) */
static boolean has_lastcard = false;


#ifdef OBSOLETE
static char loader[] = ",008015,022029,036040N000000N0000001001";
#else
static char loader[] = ",008015,022029,036040,047054,061068/039,"
		       "072001N000000N000000N0000001040BOOTSTRAP";
#endif

void clearwm(void);


/****************************************************************************
*                                  loadcard
* Effect: 
*       Writes out the bootstrap loader card image(s) required
****************************************************************************/

void loadcard()
    {
     fprintf(punch,"%s\n",loader);
     punched++;
    }

/****************************************************************************
*                                    wcard
* Result: void
*       
* Effect: 
*	If there is a card in 'lastcard', punches it out as a self-loading
*	card with a read-card-and-branch back to the bootstrap location
*	Takes the current card image and moves it to 'lastcard'
****************************************************************************/

void wcard()
    {
     if(has_lastcard)
	flushcard(i_R,BOOT);
     memcpy(lastcard,card,sizeof(card));
     has_lastcard = true;
    }

/****************************************************************************
*                                  clearcard
* Result: void
*       
* Effect: 
*       Clears the 'card' buffer to BCD spaces
****************************************************************************/

void clearcard()
    {
     int i;
     for(i=0;i<81;i++) card[i] = 0; /* BCD space */
    }

/****************************************************************************
*                                  flushcard
* Inputs:
*	unsigned char op: Operation for branch
*       unsigned branch: Place to branch to
* Result: void
*       
* Effect: 
*       The buffer called 'lastcard' is written
*       Converts the BCD image in 'lastcard' to the output buffer 'outcard'
*	Right-blank-suppresses the result
*	Punches it and increments the punch count
****************************************************************************/

void flushcard(unsigned char op, unsigned branch)
    {
     int i;

     lastcard[BOOTRB] = op;
     cvbytes(&lastcard[BOOTRB+1],branch);

     for(i=0;i<80;i++)
        { /* write ascii */
	 outcard[i] = bcd_to_ascii(lastcard[i+1]);
	 outcard[i+1] = '\0';
	} /* write ascii */

     /* Blank suppress */

     for(i=79;i>0;i--)
         if(outcard[i] == ' ')
	    outcard[i] = '\0';
	 else
	    break;
     
     fprintf(punch,"%s\n",outcard);
     punched++;
     
     has_lastcard = false;
    }

/****************************************************************************
*                                 datacard
* Inputs:
*       unsigned lb: Lower bound of memory image
*	unsigned ub: Upper bound of memory image
* Result: unsigned
*	New lb location for next call
* Effect: 
*       Writes out boot records for data
****************************************************************************/

unsigned datacard(unsigned lb,unsigned ub)
    {
     unsigned i;
     int wmcnt;
     unsigned maxpos;
     int maxcard;

     if(debug)
         printf("datacard(%u,%u)\n",lb,ub);

     wmcnt = 0;

     /* Clear out the card and the wordmarks table */

     clearwm();

     clearcard();

     for(i=0; lb + i < ub; i++)
        { /* skip over wm and uninit */
	 if(memory[lb+i] & C_bits)
	    { /* real data */
	     lb = lb + i;
	     break;
	    } /* real data */
	} /* skip over wm and uninit */

     /* Normally, we get a free wordmark because we do an MLCWA instruction.
        Thus we can set 9 wordmarks (including the one set by the MLCWA).
	However, if no wordmark is to be set, we can set at most 6 more
	wordmarks, since we must consume two locations to clear the 
	wordmark and of course we lose the one set by the MLCWA
     */

     if(!WM(memory[lb]) )
        { /* we must clear the wm */
	 wordmarks[wmcnt].loc = lb;
	 wordmarks[wmcnt].op = i_CW;
	 wordmarks[wmcnt+1] = wordmarks[wmcnt];
	 wmcnt += 2;
	} /* we must clear the wm */

     
     for(i=0;i<DATA_WIDTH;i++)
        { /* move the next bunch of bytes into the record */
	 /*
	    Note that we have placed at least one character of
	    data here, so this doesn't give us a null move
	 */

	 if( (memory[lb+i] & C_bits) == 0)
	    { /* no data */
	     break;
	    } /* no data */

	 if(lb+i > ub) 
	    break;

	 if(WM(memory[lb+i]) && i > 0) 
	     {
	      /* Can we set another word mark ? */

	      if(wmcnt >= NWM) 
		 break;  /* No, quit loop */

	      /* Yes, record it */

	      wordmarks[wmcnt].loc = lb+i;
	      wordmarks[wmcnt].op = i_SW;
	      wmcnt++;
	     }
	 card[DATA+i] = BA8421M(memory[lb+i]);
	} /* move the next bunch of bytes into the record */

     /* We now have the data in 'card' */

     maxpos = lb+i;	/* Maximum memory position valid in card, +1 */
     maxcard = DATA+i;	/* Maximum column position valid in card, +1 */

     /* Print out the wordmark table */

     if(debug)
        { /* display */
	 printf("wordmarks (%d) = ",wmcnt);
	 for(i=0;i<wmcnt;i++)
	 	printf("[%d]: %s %u ",i,
			(wordmarks[i].op == i_SW ? "SW" : 
			   (wordmarks[i].op == i_CW ? "CW" :
			      (wordmarks[i].op == i_NOP ? "NOP" : "?"))),
			wordmarks[i].loc);
	 printf("\n");
	} /* display */

     /* Now, we know there are no more than NWM word marks in the card;
        select the locations to be set

	There are two cases: We have fewer wordmarks to set than we can set,
	or we have as many.  These are distinguished by the following cases:
	The current active wordmarks entry is valid, or not.  If the index is
	> wmcnt, we have a non valid entry, so we skip it somehow.

	The way we skip an entry is either to duplicate the A address in the B
	address, or make the opcode a NOP, depending on whether we had an odd
	number or even number to set.

	Valid configurations are:

		,AAABBB		; set wms in locations AAA and BBB
		,AAAAAA		; set wm in location AAA 
		N000000		; no wordmark activity at all
		
	We get one wordmark for free, the one in the first location (column
	DATA), which we get via an MLCWA if we need it.  We have to clear it
	if we don't want it.

	If we are setting an 'even' wordmark from our table, we first create a
	set-word-mark instruction (we store the opcode)

	We then assume the wordmark we are setting is the last, and set the A
	and B addresses identically.

	If we are setting an 'odd' wordmark from our table, we take advantage
	of the current instruction, and set its B-address.

	If we are at an even address and there are no more wordmarks to set,
	we put a NOP 0,0 instruction down.

     */

     for(i=0;i<NWM;i++)
        { /* set wordmarks */
	 if(i<wmcnt)
	    { /* set one */
	     if(i%2 == 0)
	       { /* even */
	 	card[setmarks[i]-1] = wordmarks[i].op;
		cvbytes(&card[setmarks[i]],wordmarks[i].loc);
		cvbytes(&card[setmarks[i+1]],wordmarks[i].loc);
	       } /* even */
	    else
	       { /* odd */
	        cvbytes(&card[setmarks[i]],wordmarks[i].loc);
	       } /* odd */
	    } /* set one */
	 else
	    { /* do nothing */
	     if(i%2 == 0)
	        { /* kill whole instruction */
		 card[setmarks[i] - 1] = i_NOP;
		 cvbytes(&card[setmarks[i]],0);
		 cvbytes(&card[setmarks[i+1]],0);
		} /* kill whole instruction */
	    } /* do nothing */
	} /* set wordmarks */

     /* Now put in the read-and-branch instruction, or branch to program
        start 
     */

     if(maxpos > ub)
        { /* transfer control */
     	card[DATA-4] = i_B;

	cvbytes(&card[DATA-3],start);
	} /* transfer control */
     else
        { /* read next */
	 card[DATA-4] = i_R;

	 cvbytes(&card[DATA-3],1);
	} /* read next */

     /* And the initial load instruction */

     card[BOOT] = i_MLCWA;

     cvbytes(&card[BOOT+4],maxpos-1);

     cvbytes(&card[BOOT+1],maxcard-1);

     /* At this point, the card image is complete, and the BCD image is
        found in 'card'
     */

     wcard();

     /*
	Skip over uninitialized storage
     */
     while(maxpos <= ub && (memory[maxpos] & C_bits) == 0)
	maxpos++;

     if(debug)
     	printf("<=datacard = %u\n",maxpos);

     return maxpos;
    }

/****************************************************************************
*                                   clearwm
* Result: void
*       
* Effect: 
*       Clears the wordmarks table
****************************************************************************/

void clearwm(void)
    {
     int i;

     for(i=0;i<NAM;i++) 
        { /* clear wm table */
	 wordmarks[i].loc = 0;
	 wordmarks[i].op  = i_NOP;
	} /* clear wm table */
    }

/****************************************************************************
*                                   dumpam
* Inputs:
*       int wmcnt: Number of wordmarks set
* Result: void
*       
* Effect: 
*       Emits a card which contains nothing but SW commands (or NOPs if
*	wmcnt < NAM).  
* Notes:
*	We have two states: an even number of operands and an odd
*	number of operands.  We show here as if we had 4 entries.
*	We have either all valid operands or the balance in NOPs set
*	by clearwm()
*
*	In the examples below, the table entries are shown as op-operand
*	pairs, and the resulting instructions are machine code
*
*	table				Instructions
*	=======================		===================
*	, aaa N 000 N 000 N 000		,aaaaaaN000000
*	, aaa , bbb N 000 N 000		,aaabbbN000000
*	, aaa , bbb , ccc N 000		,aaabbb,cccccc
*	, aaa , bbb , ccc , ddd		,aaabbb,cccddd
*	===== ===== ===== =====		1      1
*wordmarks[0]   [1]   [2]   [3]	         ^  ^   ^  ^
*				allmarks[0][1] [2][3]
****************************************************************************/

void dumpam(int wmcnt)
    {
     int i;

     if(wmcnt == 0)
	return;

     clearcard();

     for(i=1; i<=NAM; i++)
        { /* emit each wm */
	 int wm = i-1;

	 /*
	    Lay down the operand for this WM.  It may be
	    the even or the odd operand.  If it is the odd
	    operand, it overwrites the previously duplicated
	    even operand.  
	 */

	 if(!(wm & 1))
	    { /* even */

	     cvbytes(&card[allmarks[wm]],wordmarks[wm].loc);

	     /*
		We are putting down the first operand of an
		instruction, so we duplicate the address in the
		second position.  We know that +1 exists because
		there are an even number of entries in the table,
		so any even index has a corresponding odd index
		above it.
	     */
	     cvbytes(&card[allmarks[wm+1]],wordmarks[wm].loc);

	     /* Install the opcode */

	     card[allmarks[wm] - 1] = wordmarks[wm].op;

	    } /* even */
	 else
	    { /* odd */
	     /*
		If we are putting down the second operand, we fill it
		in only if the opcode is not a NOP; otherwise we 
		inherit the second operand from the even-address
		computation which preceded it.
	     */
	     if(wordmarks[wm].op != i_NOP)
		cvbytes(&card[allmarks[wm]],wordmarks[wm].loc);
	    } /* odd */
	} /* emit each wm */

     clearwm();
     wcard();
    }

/****************************************************************************
*                                   wmcard
* Inputs:
*       unsigned lb: Lower bound
*	unsigned ub: Upper bound
* Result: void
*       
* Effect: 
*       Issues cards that set WMs in unitialized locations
****************************************************************************/

void wmcard(unsigned lb, unsigned ub)
    {
     int wmcnt = 0;

     for(; lb <= ub; lb++)
        { /* scan */
	 if(memory[lb] & C_bits)
	    { /* skip initialized memory */
	     continue;
	    } /* skip initialized memory */

         if(WM(memory[lb]))
	    { /* uninit WM loc */
	     if(wmcnt >= NAM)
	        { /* dump current set */
		 dumpam(wmcnt);
		 wmcnt = 0;
		} /* dump current set */
	     wmcnt++;
	     wordmarks[wmcnt-1].op = i_SW;
	     wordmarks[wmcnt-1].loc = lb;
	    } /* uninit WM loc */
	} /* scan */
     dumpam(wmcnt);
    }
