/*****************************************************************************
*           Change Log
*  Date     | Change
*-----------+-----------------------------------------------------------------
* 16-Nov-85 | [1.80] Created
* 23-Nov-85 | [1.91] Check instructions dispatch vector immediately...flag
*           | illegal operation
* 24-Nov-85 | [1.106] CS operation does not require word mark following
* 12-Dec-85 | [1.179] Handle case 8 properly; turn on instruction length light
*           | for long instructions
* 13-Dec-85 | [1.183] Move previous A-addr to B-addr during A-cycle of
*           | SAR instruction
* 14-Dec-85 | [1.193] Keep hyp. d_char for length 2 and 5 instructions; if
*           | instruction terminates on next fetch cycle, store in d_char
* 24-Dec-85 | [1.236] Turn off 'op fetch' message; no longer needed and
*           | interferes with selective trace facility
* 27-Dec-85 | [1.255] store d_char properly for length 2 instructions
* 30-Dec-85 | [1.276] Detect illegal instructions by also checking for
*           | inst_illegal in the dispatch table, not just NULL
* 30-Dec-85 | [1.279] Added code to support indexing
* 24-Jan-86 | [1.314] check_left_mouse_button => check_mouse_halt
* 25-Feb-86 | [1.379] include<> => include ""
* 31-Jul-86 | [1.405] Made char variables unsigned
*  6-Aug-86 | [1.410] Added NOTREACHED comment for Pre-C
*  6-Aug-86 | [1.410] Added bcd.h dependency
* 18-Nov-91 | [1.428] <jmn> memory.h => mem1401.h, avoid ANSI name
* 22-Dec-94 | [1.600J] JRJ extend array binary to 16 bytes to avoid bad index
* 22-Dec-94 | [1.600J] JRJ add code to set up I/O instructions.
* 20-Jan-95 | [1.600J] JRJ add code to handle CU I/O instruction units.
*****************************************************************************/

/*****************************************************************************
				1401 Simulator

			    Instruction Fetch Logic

*****************************************************************************/

#include "stdio.h"
#include "boolean.h"

#include "mem1401.h"

#include "btypes.h"

#include "instr.h"
#include "state.h"
#include "mach.h"
#include "alerts.h"
#include "modes.h"
#include "dispatch.h"
#include "diag.h"
#include "icycle.h"
#include "bcd.h"
#include "kb.h"
#include "scan.h"
#include "alert.h"
#include "mach.h"
#include "length.h"
#include "data.h"
#include "io.h"

static char binary[] = " \1\2\3\4\5\6\7\10\11\0\1\2\3\4\5";

static unsigned char t_char;	/* temporary d-modifier */

static int index;
static int index_addr;

/****************************************************************************
*                                   cvindex
* Inputs:
*       short zones: Zone bits of index field
* Result: int
*       Address of index register units position
****************************************************************************/

short cvindex(short zones)
    {
     switch(zones)
	{ /* decode */
	 case A_bits: return 89;
	 case B_bits: return 94;
	 case BA_bits: return 99;
	} /* decode */
     return 0;
    }


/****************************************************************************
*                                   ifetch
* Result: boolean
*	true if the instruction can be executed
*	false if there was some error
* Effect:
*       Fetches an instruction.  Decodes its operands, if present
****************************************************************************/

boolean ifetch()
    {
     /*----------------------------------------------------------------
     All information about the actions of the machine are taken from
     "Systems Operation Reference Manual: IBM 1401 Data Processing System/
     IBM 1460 Data Processing System", IBM File Number 1401/1460-01,
     Form A24-3067-1, Copyright IBM, November 1964.
     ----------------------------------------------------------------*/

     int temp;

     cycle = cycle_I;

     while(true)
	{ /* instruction fetch  */

	 if(check_mouse_halt()) return false;

	 if(reset_mandatory) return false;	/* We had an error */

	 switch(I_cycle)
	    { /* fetch */
/* ? */	     case Iop:  /* opcode fetch */
		      /* O
			 OF
			 OAAA
			 OIIIF
			 OAAABBB
			 OAAABBBF
			 ^ you are here
		      */

		    /*--------------------------------------------------------
		       The operation code enters the B-register and the
		       OP-register.   Because this is the first I-cycle,
		       the A-register is left undisturbed
		    --------------------------------------------------------*/

		    B = memory[I_addr];
		    OP = B;


#if 1
		    if(diagnostics_on)
		       { /* trace */
			sprintf(diag_buffer,"%d: Op fetch %c%d ('%c')",
					I_addr,
					(WM(OP) ? '^' : ' '),
					BA8421(OP),
					bcd_to_ascii(BA8421(OP)));
			tell(diag_buffer);
		       } /* trace */
#endif
		    if((B & word_mark) == 0)
		       { /* missing word mark! */
			alert(alert_process);
			light_O(true);
			I_addr++;
			break;
		       } /* missing word mark! */

		     if(instructions[BA8421(OP)] == NULL ||
			instructions[BA8421(OP)] == inst_illegal)
			{ /* no dispatch address */
			 inst_illegal();
			 light_O(true);
			 alert(alert_process);
			 return false;
			} /* no dispatch address */
		     I_cycle = I1;
		     I_addr++;		/* NSI position */

		    break;
/* O? */     case I1:	/* First address cycle */
		      /* O
			 OF
			 OAAA
			 OIIIF
			 OAAABBB
			 OAAABBBF
			  ^ you are here
		      */

		    /*--------------------------------------------------------
		    The A-Address register is set to blanks during the first
		    part of this cycle for all instructions.  The B-address
		    is reset to blanks during the first part of the cycle
		    for all operations except Move, Load, Store A- and B-
		    Registers operations.  During the I-1 cycle, the second
		    instruction character (first character of the A-address)
		    enters the hundreds position of the A- and B- address
		    registers and the A-register by way of the B-register.
		    --------------------------------------------------------*/

		    B = memory[I_addr];
		    index = 0;		/* Assume no indexing */

		    if((B & word_mark) != 0)
		       { /* new instruction found */

			 /* Our operation is of the 'O' variety */

			return true;
		       } /* new instruction found */

		    A = B;

			/* The A_addr and B_addr are both loaded;
			   It is presumed the first character is the
			   hundreds position, and the BA bits determine
			   the thousands.  Remember that 0 is 8-2.
			*/

			/*
			   Reset flag that inhibits indexing for I/O
			   instructions.  Actual test for "%" deferred into
			   op specific stuff for efficiency.  JRJ
			*/
			io_index_inhibit = false;

			temp = ZONEBITS_DOWN(B)*1000 + binary[NUMBITS(B)] * 100;

			switch(BA8421(OP))
			   { /* inhibit B-store */
			    case i_SAR:
					B_addr = A_addr;	/* Save Ap in B */

					A_addr = temp;
					break;
			    case i_SBR:
			    case i_MLCWA:
			    case i_MLC:
			    case i_CU:
				/* If 1st character of A addr is %, set I/O
				   flag to inhibit indexing.  Set address
				   to 0 (A kludge, because this simulator does
				   not keep a real character A address reg).
				   JRJ
				*/
				if(io_index_inhibit =
				   (BA8421(B) == ascii_to_bcd('%'))) {
					temp = 0;
				}
				A_addr = temp;
					break;
			    default:
				A_addr = B_addr = temp;
			   } /* inhibit B-store */
			t_char = B; /* in case we terminate in next cycle */

			I_cycle = I2;
			I_addr++;		/* NSI position */

			break;
/* Ox? */     case I2:  /* Case 2: second address cycle */
		      /* O
			 OF
			 OAAA
			 OIIIF
			 OAAABBB
			 OAAABBBF
			   ^ you are here
		      */

		    /*--------------------------------------------------------
		    --------------------------------------------------------*/

		    B = memory[I_addr];


		    if((B & word_mark) != 0)
		       { /* new instruction found */

			/* Our operation is of the 'OF' variety */

			d_char = t_char;

			return true;
		       } /* new instruction found */

		    A = B;

		    /* The A and B registers are both loaded.  This is the
		       tens position.  If it has zone bits, they represent
		       indexing.

		       (Unless, of course, this is an I/O instruction.
		       However, the zone bits get cleared in the A address
		       register anyway.  Really!  JRJ).
		    */

		    index = ZONEBITS(B);

		    A_addr += binary[NUMBITS(B)]*10;
			switch(BA8421(OP))
			   { /* inhibit B-store */
			    case i_MLCWA:
			    case i_MLC:
			    case i_CU:
				if(io_index_inhibit) {
					index = 0;	/* inhibits cycles */
					io_device = B;
				}
				break;
			    case i_SAR:
			    case i_SBR:
					break;
			    default:
				B_addr = A_addr;
			   } /* inhibit B-store */

			I_cycle = I3;
			I_addr++;		/* NSI position */
		    break;

/* Oxx? */     case I3:	/* Units fetch */
			/* O
			   OF
			   OAAA
			   OIIIF
			   OAAABBB
			   OAAABBBF
			      ^ you are here
			*/

		    /*--------------------------------------------------------
		    --------------------------------------------------------*/

		    B = memory[I_addr];

		    if((B & word_mark) != 0)
		       { /* new instruction found */

			/* There is no legal instruction format of this
			   length
			*/

			alert(alert_process);
			break;
		       } /* new instruction found */

		    A = B;

		    /* We have the units position */

		    A_addr += ZONEBITS_DOWN(B)*4000 + binary[NUMBITS(B)];
		    d_char = B;

			switch(BA8421(OP))
			   { /* inhibit B-store */
			    case i_MLCWA:
			    case i_MLC:
			    case i_CU:
				if(io_index_inhibit) {
					io_unit = B;
				}
			    case i_SAR:
			    case i_SBR:
					break;
			    default:
				B_addr = A_addr;
			   } /* inhibit B-store */

			if(index==0)
			   { /* no indexing */			    
			    I_cycle = I4;
			    I_addr++;		/* NSI position */
			   } /* no indexing */			    
			else
				I_cycle = I3X1;
		    break;

/* Oxxx+Xn */   case I3X1: /* Units index cycle */

			index_addr = cvindex(index);

			B = memory[index_addr];

			index_addr--;

			if(ZONEBITS(B))
			   { /* big number */
			    switch(ZONEBITS(B))
			       { /* zone decode */
				case A_bits:
					A_addr += 4000;
					break;
				case B_bits:
					A_addr += 8000;
					break;
				case BA_bits:
					A_addr += 12000;
					break;
			       } /* zone decode */
			   } /* big number */

			A_addr += binary[NUMBITS(B)];

			I_cycle = I3X10;
			break;

		case I3X10: /* Fetch tens position of index */
			B = memory[index_addr];
			index_addr--;
			A_addr += 10 * binary[NUMBITS(B)];
			I_cycle = I3X100;
			break;

		case I3X100:
			B = memory[index_addr];
			index_addr--;
			A_addr += 100 * binary[NUMBITS(B)];
			if(ZONEBITS(B))
			   { /* complex */
			    switch(ZONEBITS(B))
			       { /* thousands decode */
				case A_bits: A_addr += 1000;
						break;
				case B_bits: A_addr += 2000;
						break;
				case BA_bits: A_addr += 3000;
						break;
			       } /* thousands decode */
			   } /* complex */

			A_addr %= 16000;

			switch(BA8421(OP))
			   { /* inhibit B-store */
			    case i_MLCWA:
			    case i_MLC:
			    case i_SAR:
			    case i_SBR:
			    		break;
			    default:
			    	B_addr = A_addr;
			   } /* inhibit B-store */

			I_cycle = I4;
			I_addr++;		/* NSI position */
			break;

/* Oxxx? */     case I4: /* Start next field */

	     	      /* O
		         OF
			 OAAA
			 OIIIF
			 OAAABBB
			 OAAABBBF
			     ^ you are here
		      */

		    /*--------------------------------------------------------
		    The B-address register is reset at the beginning of this
		    cycle.  The fifth instruction character (first character
		    of the B-address) enters the hundreds position of the
		    B-address register, and the A-register through the 
		    B-register.
		    --------------------------------------------------------*/

		    index = 0;

		    B = memory[I_addr];

		    if((B & word_mark) != 0)
		       { /* new instruction found */

			/* We have an instruction of the form OAAA */

			return true;

		       } /* new instruction found */

		    A = B;

			/* The B_addr register is loaded;
			   It is presumed the first character is the
			   hundreds position, and the BA bits determine
			   the thousands.  Remember that 0 is 8-2.
			*/

		    B_addr = ZONEBITS_DOWN(B)*1000 + binary[NUMBITS(B)] * 100;

		    t_char = B; /* in case we terminate in next cycle */

		    I_cycle = I5;
		    I_addr++;		/* NSI position */
		    break;
		    
/* OAAAF? */   case I5: /* Fetch tens of B-field */
	     	      /* O
		         OF
			 OAAA
			 OIIIF
			 OAAABBB
			 OAAABBBF
			      ^ you are here
		      */

		    /*--------------------------------------------------------
		    --------------------------------------------------------*/

	            B = memory[I_addr];

		    if((B & word_mark) != 0)
		       { /* new instruction found */

			/* We have an instruction of the 'OIIIF' variety */

			d_char = t_char;

			return true;
		       } /* new instruction found */

		    A = B; 
		    
		    /* The B address register is loaded.  This is the 
		       tens position.  If it has zone bits, they represent
		       indexing
		    */
		    
		    index = ZONEBITS(B);

		    B_addr += binary[NUMBITS(B)]*10;
		    I_cycle = I6;
		    I_addr++;		/* NSI position */
		    break;
/* OAAABB? */  case I6: /* Fetch units position of B-field */
	     	      /* O
		         OF
			 OAAA
			 OIIIF
			 OAAABBB
			 OAAABBBF
			       ^ you are here
		      */
		    /*--------------------------------------------------------
		    --------------------------------------------------------*/

	            B = memory[I_addr];

		    if((B & word_mark) != 0)
		       { /* new instruction found */

			/* There is no legal instruction of this length */

			alert(alert_process);
			break;
		       } /* new instruction found */

		    A = B;

		    B_addr += ZONEBITS_DOWN(B)*4000 + binary[NUMBITS(B)];

		    B_addr %= 16000;

		    if(index==0)
		       { /* no indexing */
		    	I_cycle = I7;
			I_addr++;		/* NSI position */
		       } /* no indexing */
		    else
		        I_cycle = I6X1;
		    break;

/* Oxxx+Xn */   case I6X1: /* Units index cycle */

			index_addr = cvindex(index);

			B = memory[index_addr];

			index_addr--;

			if(ZONEBITS(B))
			   { /* big number */
			    switch(ZONEBITS(B))
			       { /* zone decode */
				case A_bits:
					B_addr += 4000;
					break;
				case B_bits:
					B_addr += 8000;
					break;
				case BA_bits:
					B_addr += 12000;
					break;
			       } /* zone decode */
			   } /* big number */

			B_addr += binary[NUMBITS(B)];

			I_cycle = I6X10;
			break;

		case I6X10: /* Fetch tens position of index */

			B = memory[index_addr];
			index_addr--;
			B_addr += 10 * binary[NUMBITS(B)];
			I_cycle = I6X100;
			break;
		case I6X100:
			B = memory[index_addr];
			index_addr--;
			B_addr += 100 * binary[NUMBITS(B)];
			if(ZONEBITS(B))
			   { /* complex */
			    switch(ZONEBITS(B))
			       { /* thousands decode */
				case A_bits: B_addr += 1000;
						break;
				case B_bits: B_addr += 2000;
						break;
				case BA_bits: B_addr += 3000;
						break;
			       } /* thousands decode */
			   } /* complex */

			I_cycle = I7;
			I_addr++;		/* NSI position */
			break;

/* OAAABBB? */ case I7: /* Fetch modifier */
	     	      /* O
		         OF
			 OAAA
			 OIIIF
			 OAAABBB
			 OAAABBBF
			        ^ you are here
		      */

		    /*--------------------------------------------------------
		    --------------------------------------------------------*/

	     	    B = memory[I_addr];

		    if((B & word_mark) != 0 || BA8421(OP) == i_SW ||
		    		BA8421(OP) == i_CS)
		       { /* new instruction found, or operation has
			    all fields filled */

			/* We have form 'OAAABBB' */

			return true;

		       } /* new instruction found */

		    I_cycle = I8;
		    I_addr++;		/* NSI position */

		    d_char = B;

		    break;
             case I8:   /* About to fetch opcode of next instruction */

	     	    B = memory[I_addr];
		    if((B & word_mark) != 0)
		       { /* valid */
			return true;
		       } /* valid */
		    set_inst_length();
		    alert(alert_process);
		    break;
	     default:
	     		/* We should handle indefinite-length NOP someday */

			tell("Instruction too long during fetch");
			alert(alert_process);
			break;
	    } /* fetch */

	 if(current_mode == mode_sing_cyc ||
	    current_mode == mode_adr_stop) 
	       { /* step */
#if 0
		if(diagnostics_on)
		   { /* report */
		    switch(I_cycle)
		       { /* display */
			case 1:  /* op only */
				sprintf(diag_buffer,"Fetch: %c: %s",
					bcd_to_ascii(BA8421(OP)),
					opcodes[BA8421(OP)]);
				break;
			case 2: /* op + d or op+A100 */
				sprintf(diag_buffer,"Fetch: %c%c: %s %dxx | %s %c",
					bcd_to_ascii(BA8421(OP)),
					bcd_to_ascii(B),
					opcodes[BA8421(OP)],
					A_addr/100,
					opcodes[BA8421(OP)],
					bcd_to_ascii(d_char));
				break;
			/* unfinished...*/
		       } /* display */
		    tell(diag_buffer);
		   } /* report */
#endif
	        return false;
	       } /* step */

	} /* instruction fetch  */

     /*NOTREACHED */
     return false;

    }

/****************************************************************************
*                               illegal_length
* Effect: 
*       Writes a diagnostic message if diagnostics enabled; sets appropriate
*	alert
****************************************************************************/

void illegal_length()
    {
     if(diagnostics_on)
	{ /* report */
	 sprintf(diag_buffer,"%d: %s has illegal length %d",
	 		I_addr - I_cycle,
	 		opcodes[BA8421(OP)],I_cycle);
	 tell(diag_buffer);
	} /* report */
     alert(alert_process);

    }
