/*****************************************************************************
*           Change Log
*  Date     | Change
*-----------+-----------------------------------------------------------------
* 24-Nov-85 | [1.108] Created
* 12-Dec-85 | [1.174] Use tell_op to print data
* 13-Dec-85 | [1.180] Implement single cycle options
* 14-Dec-85 | [1.190] Added conditional branch dispatch table
* 15-Dec-85 | [1.209] Implemented BCV, BC9
* 17-Dec-85 | [1.212] Added B_BAV
* 30-Dec-85 | [1.283] Added tests to branch on I/O error indicator
* 27-Jan-86 | [1.350] declare single_cycle decode
* 25-Feb-86 | [1.379] include <> => include ""
* 31-Jul-86 | [1.405] Made all chars unsigned
* 10-Nov-91 | [1.428] <jmn> converted to Microsoft C 6.0
* 18-Nov-91 | [1.428] <jmn> memory.h => mem1401.h, avoid ANSI name
* 07-Jan-95 | [1.600J] JRJ Implement Tape I/O branches
*****************************************************************************/
#include <stdio.h>
#include <assert.h>

#include "mem1401.h"
#include "boolean.h"

#include "btypes.h"

#include "mach.h"
#include "diag.h"
#include "instr.h"
#include "alerts.h"
#include "alert.h"
#include "logic.h"
#include "switches.h"
#include "poll.h"
#include "ifetch.h"

#include "button.h"
#include "tape.h"

#define B_B_f_complete single_cycle(i_B,B_f_complete)

extern char * single_cycle_decode();

/*****************************************************************************
				1401 Simulator

			   Branch Instruction


			 Branch (Two Addresses + d)
			 --------------------------

Instruction format

Mnemonic	Op Code	I-address	B-address	d-char
--------        ------- ---------       ---------	------
BCE		B	III		BBB		d

Function: This instruction causes the single character at the
B-address to be compared to the d-character.  If it has the same
bit configuration as the d-character, the program branches to the
I-address, otherwise the program continues sequentially.  The d-character
can be any combination of the six BCD code bits (BA 8421)

Word Marks:  Word marks in the location tested have no effect on the
operation.

Address Registers After Operation:

No Branch:

I-Add		A-Add		B-Add
NSI		BI		B-1

Branch

I-Add		A-Add		B-Add
NSI		BI		NSI

Chaining:

Note: Care must be taken if the specified d-character is also the same
d-character than can test and reset an indicator OFF when it is used
with a Bxxxd instruction.  If the indicator is on during the execution of
the Bxxxxxxd instruction, it is turned off.


			 Branch (One Address + d)
			 ------------------------
Instruction format

Mnemonic	Op Code	I-address   d-character
--------        ------- ---------   -----------
BIN		B	III		d
BC9		B	III		9		channel 9
BCV		B	III		@		channel 12
BLC		B	III		A		last card (SS A)
BSS		B	III		B		SS B
BSS		B	III		C		SS C
BSS		B	III		D		SS D
BSS		B	III		E		SS E
BSS		B	III		F		SS F
BSS		B	III		G		SS G
BEF		B	III		K+		End of reel
BER		B	III		L		Tape transmission err
BIN		B	III		N		Access inoperable
BIN		B	III		?+		Reader err, ~I/O chk
BIN		B	III		!+		Punch err, ~I/O chk
BIN		B	III		Ø+		Printer err, ~I/O chk
BIN		B	III		*		Inquiry clear
BU		B	III		/		B != A
BPB		B	III		P		Printer busy
BIN		B	III		Q		Inquiry request
BPCB		B	III		R		Printer carriage busy
BE		B	III		S		B = A
BL		B	III		T		B < A
BH		B	III		U		B > A
BIN		B	III		V		RW parity or RBchk
BIN		B	III		W		Wrong length record
BIN		B	III		X		Unequal addr compare
BIN		B	III		Y		Any-disk-error
BAV		B	III		Z+		Overflow
BIN		B	III		%+		Processing check

Function: The d-character specifies the indicator tested.  If the 
indicator is on, the next instruction is taken from the I-address.  If
the indicator is off, the next sequential instruction is taken.  The table
above shows the d-characters and conditions they test.  

The indicators tested are not turned off by this instruction except as
noted by the + following the d-character in the table.  When carriage tape
channels 9 or 12 are sensed, the corresponding indicators are turned on.
These carriage channel indicators are turned off when any other carriage
tape-channel is sense (refer to Notes in Control Carriage for other
information).  The next compare instruction turns off the compare
indicators.


Word Marks:  Word marks are not affected.

Note: See the notes for the branch-on-character-equal test, above.

Address Registers After Operation:

No branch

I-Add		A-Add		B-Add
-----           -----           -----
NSI		BI		dbb


Branch

I-Add		A-Add		B-Add
-----           -----           -----
NSI		BI		NSI


			 Branch (One Address)
			 --------------------
Instruction format

Mnemonic	Op Code	I-address 
--------        ------- ---------
B		B	III		

Function: The instruction always causes a branch to the address specified
by the I-address portion of the instruction.

This unconditional branch operation is used to interrupt normal program flow
and continue the program at some other point, without testing for specific
conditions.

Word Marks:  A word mark must be associated with the core-storage position
of the next I-address.

Address Registers After Operation:

I-Add		A-Add		B-Add
-----           -----           -----
NSI		BI		NSI

*****************************************************************************/

extern boolean B_uncond(unsigned char d);
extern boolean B_illegal(unsigned char d);
extern boolean B_nop(unsigned char d);
extern boolean B_SS(unsigned char d);
extern boolean B_BAV(unsigned char d);
extern boolean B_GT(unsigned char d);
extern boolean B_LT(unsigned char d);
extern boolean B_EQ(unsigned char d);
extern boolean B_NE(unsigned char d);
extern boolean B_BCV(unsigned char d);
extern boolean B_BC9(unsigned char d);
extern boolean B_perr(unsigned char d);
extern boolean B_EF(unsigned char d);
extern boolean B_ER(unsigned char d);

boolean (* dcodes[64])(unsigned char d) = {
			B_uncond,	/* 0    - sp unconditional    */
			B_illegal,	/* 1    - 1  illegal	      */
			B_illegal,	/* 2    - 2  illegal          */
			B_illegal,	/* 3    - 3  illegal          */
			B_illegal,	/* 4    - 4  illegal	      */
			B_illegal,	/* 5    - 5  illegal	      */
			B_illegal,    	/* 6    - 6  illegal	      */
			B_illegal,	/* 7	- 7  illegal	      */
			B_illegal,	/* 8    - 8  illegal	      */
			B_BC9,		/* 9    - 9  printer ch 9     */
			B_illegal,	/* 10   - 0  illegal	      */
			B_illegal,	/* 11   - #= illegal	      */
			B_BCV,		/* 12   - @' printer ch 12    */
			B_illegal,	/* 13   - :  illegal	      */
			B_illegal,	/* 14	- >  illegal	      */
			B_illegal,	/* 15   - û  illegal	      */
			B_illegal,	/* 16   - b  illegal	      */
			B_NE,		/* 17   - /  B != A	      */
			B_EQ,		/* 18   - S  B = A	      */
			B_LT,		/* 19   - T  B < A	      */
			B_GT,		/* 20   - U  B > A	      */
			B_illegal,	/* 21   - V  RW parity/RBchk  */
			B_illegal,	/* 22	- W  wrong len record */
			B_illegal,	/* 23   - X  disk != addr     */
			B_illegal,	/* 24   - Y  any disk err     */
			B_BAV,		/* 25   - Z  overflow	      */
			B_perr,		/* 26   - Ø  Printer err      */
			B_illegal,	/* 27   - ,  illegal	      */
			B_nop,		/* 28   - %( Processor check  */
			B_illegal,	/* 29   - ^  illegal	      */
			B_illegal,	/* 30   - \  illegal	      */
			B_illegal,	/* 31   - ×  illegal	      */
			B_illegal,	/* 32   - -  illegal	      */
			B_illegal,	/* 33   - J  illegal	      */
			B_EF,		/* 34   - K  End of Reel      */
			B_ER,		/* 35   - L  Tape Xmit error  */
			B_illegal,	/* 36   - M  illegal	      */
			B_illegal,	/* 37   - N  Access inoperable*/
			B_illegal,	/* 38   - O  illegal	      */
			B_nop,		/* 39   - P  printer busy     */
			B_illegal,	/* 40   - Q  Inquiry request  */
			B_nop,		/* 41   - R  Printer carr. bsy*/
			B_perr,		/* 42   - !  Punch err	      */
			B_illegal,	/* 43   - *  Inquiry clear    */
			B_illegal,	/* 44   -    illegal	      */
			B_illegal,	/* 45   - ]  illegal	      */
			B_illegal,	/* 46   - ;  illegal	      */
			B_illegal,	/* 47   -   illegal	      */
			B_illegal,	/* 48   - &+ illegal	      */
			B_SS,		/* 49   - A  Sense Switch A   */
			B_SS,    	/* 50   - B  Sense Switch B   */
			B_SS,		/* 51   - C  Sense Switch C   */
			B_SS,		/* 52   - D  Sense Switch D   */
			B_SS,		/* 53   - E  Sense Switch E   */
			B_SS,		/* 54   - F  Sense Switch F   */
			B_SS,		/* 55   - G  Sense Switch G   */
			B_illegal,	/* 56   - H  illeagl	      */
			B_illegal,	/* 57   - I  illegal	      */
			B_perr,		/* 58   - ?  Reader err	      */
			B_illegal,	/* 59   - .  illegal	      */
			B_illegal,	/* 60   - )  illegal	      */
			B_illegal,	/* 61   - [  illegal	      */
			B_illegal,	/* 62   - <  illegal	      */
			B_illegal	/* 63   - Î  illegal	      */
			    };


/****************************************************************************
*                                    B_BAV
* Inputs:
*       unsigned char d: d-character, ignored
* Result: boolean
*       true to branch
*	false to not branch
* Effect: 
*       tests the overflow indicator, resets it if true
****************************************************************************/
/*ARGSUSED */

boolean B_BAV(unsigned char d)
    {
     if(overflow)
        { /* yes */
	 overflow = false;
	 return true;
	} /* yes */
     return false;
    }

/****************************************************************************
*                                    B_BC9
* Inputs:
*       unsigned char d: d-character, ignored
* Result: boolean
*       true to branch
*	false to not branch
* Function:
*	Tests channel 9 on the printer carriage tape
****************************************************************************/
/*ARGSUSED */

extern boolean C9;

boolean B_BC9(unsigned char d)
    {
     return C9;
    }

/****************************************************************************
*                                    B_BCV
* Inputs:
*       unsigned char d: d-character, ignored
* Result: boolean
*       true to branch
*	false to not branch
* Function:
*	Tests channel 12 on the printer carriage tape
****************************************************************************/
/*ARGSUSED */

extern boolean C12;

boolean B_BCV(unsigned char d)
    {
     return C12;
    }

/****************************************************************************
*                                    B_GT
* Inputs:
*       unsigned char d: d-char, ignored
* Result: boolean
*       true if take branch on B>A, false if not to take branch
****************************************************************************/
/*ARGSUSED */

boolean B_GT(unsigned char d)
    {
     return compare_state == logic_BGTA;
    }

/****************************************************************************
*                                    B_LT
* Inputs:
*       unsigned char d: d-char, ignored
* Result: boolean
*       true if take branch on B<A, false if not to take branch
****************************************************************************/
/*ARGSUSED */

boolean B_LT(unsigned char d)
    {
     return compare_state == logic_BLTA;
    }

/****************************************************************************
*                                    B_EQ
* Inputs:
*       unsigned char d: d-char, ignored
* Result: boolean
*       true if take branch on B=A, false if not to take branch
****************************************************************************/
/*ARGSUSED */

boolean B_EQ(unsigned char d)
    {
     return compare_state == logic_BEQA;
    }

/****************************************************************************
*                                    B_NE
* Inputs:
*       unsigned char d: d-char, ignored
* Result: boolean
*       true if take branch on B!=A, false if not to take branch
****************************************************************************/
/*ARGSUSED */

boolean B_NE(unsigned char d)
    {
     return compare_state != logic_BEQA;
    }

/****************************************************************************
*                                    B_SS
* Inputs:
*       unsigned char d: d-character we have encountered
* Result: boolean
*       true if we are to take branch
*	false if we are not to take branch
* Function:
*	Tests the sense switches.  If Sense Switch A is on, and it is the
*	Last Card condition rather than the switch, will reset it.
****************************************************************************/

boolean B_SS(unsigned char d)
    {
     return test_SS((int)d - 49);
    }


/****************************************************************************
*				    B_EF
* Inputs:
*       unsigned char d: d-character we have encountered
* Result: boolean
*       true if we are to take branch
*	false if we are not to take branch
* Function:
*	Tests the tape indicate flags of all drives.  (Note the 1401
*	Tape Instruction manual A24-3069-2):
*
*	"This instruction must be executed immediately following a tape read
*	or write operation to ensure correct results and reset the EOR
*	inicator OFF, if it is ON.  If another tape unit is selected before
*	a BRANCH IF END-OF-REEL INDICATOR ON instruction is executed, the
*	indicator remains ON, and a false EOR condition specifying the
*	wrong tape unit results."
*
*	So, this program simply surveys all of the tape drives.  If *any*
*	of them have their TAPE INDICATE set, the END-OF-REEL indicator is
*	held to be on.  It also resets *all* of them.
****************************************************************************/
boolean B_EF(unsigned char d)
{
	int i;
	boolean indicator = false;
	tapedrive *tape;

	for(i=0; i < MAXDRIVES; ++i) {
		assert((tape = tapes[i]) != NULL);
		if(tape->indicate.active) {
			indicator = true;
			tape->indicate.active = false;
			if(!tape_transfer_error) {
				IO_indicator(alert_tape);
			}
		}
	}
	return(indicator);
}

/****************************************************************************
*				    B_ER
* Inputs:
*       unsigned char d: d-character we have encountered
* Result: boolean
*       true if we are to take branch
*	false if we are not to take branch
* Function:
*	Tests the parity error flags of all drives.  (Note the 1401
*	Tape Instruction manual A24-3069-2):
*
*	"The BRANCH IF TAPE ERROR instruction must be given after a tape
*	read or write operation because any tape operation on any tape unit
*	causes the indicator to turn off."
*
*	As a result, the tape_transfer_error flag is a global.
****************************************************************************/
boolean B_ER(unsigned char d)
{
	boolean indicator;

	indicator = tape_transfer_error;
	tape_transfer_error = false;
	IO_indicator(alert_tape);

	return(indicator);
}



/****************************************************************************
*                                   B_perr
* Inputs:
*       unsigned char d: d-character of instruction
* Result: boolean
*       true to branch
*	false to not branch
* Effect:
*       if the process I/O indicator was on, turn it off
****************************************************************************/

boolean B_perr(unsigned char d)
    {
     switch(d)
	{ /* decode */
	 case 26: /* Ø - printer error */
		return IO_indicator(alert_printer);
	 case 42: /* ! - punch error */
		return IO_indicator(alert_punch);
	 case 58: /* ? - reader error */
		return IO_indicator(alert_reader);
	} /* decode */
    return false;
    }

/****************************************************************************
*                                  B_illegal
* Inputs:
*       unsigned char d: d-char we have encountered
* Result: boolean
*	false, always
* Effect:
*       Initiates a process halt because of an illegal d-character
****************************************************************************/
/*ARGSUSED */

boolean B_illegal(unsigned char d)
    {
     bad_d("Branch",d);
     alert(alert_process);
     return false;
    }

/****************************************************************************
*                                    B_nop
* Inputs:
*       unsigned char d: d-char of conditional branch
* Result: boolean
*       false, always
****************************************************************************/
/*ARGSUSED */

boolean B_nop(unsigned char d)
    {
     return false;
    }

/****************************************************************************
*                                    B_uncond
* Inputs:
*       unsigned char d: d-char of conditional branch (space means 
*		unconditional, which is the only value this will ever see)
* Result: boolean
*       true, always
****************************************************************************/
/*ARGSUSED */

boolean B_uncond(unsigned char d)
    {
     return true;
    }

/****************************************************************************
*                                   inst_B
* Result: boolean
*       true if processing should continue
*	false if processing should halt
* Effect:
*       Executes the unconditional, conditional, or character branch
****************************************************************************/

boolean inst_B()
    {

     tell_op(op_A|op_B|op_d);

     switch(I_cycle)
         {

/* B */
	  case 1: /* chained */

		if(bad_address(B_addr)) return false;

		switch(single_cycle_state)
		   { /* state decode */
		    case single_cycle_run:
		    	B = memory[B_addr];

			if(BA8421(B) == d_char)
			   { /* take branch */
			    NI_addr = A_addr;
			    B_addr = I_addr;
			    poll();
			   } /* take branch */
			else
			   { /* don't branch */
			    B_addr--;
			   } /* don't branch */
			single_cycle_state = single_cycle_complete;
			break;
		    case single_cycle_start:
		    	B = memory[B_addr];
			B_addr--;
			cycle = cycle_B;
			single_cycle_state = B_B_f_complete;
			break;
		    case B_B_f_complete:
			if(BA8421(B) == d_char)
			   { /* take branch */
			    NI_addr = A_addr;
			    B_addr = I_addr;
			    poll();
			   } /* take branch */
		        single_cycle_state = single_cycle_complete;
		    default:
			  sprintf(diag_buffer,"Illegal microstate %s",single_cycle_decode());
			  tell(diag_buffer);
			  single_cycle_state = single_cycle_complete;
		   } /* state decode */
		tell_new_state("BCE");
		break;

/* Biii */
	  case 4: /* one-address */
		NI_addr = A_addr;
		B_addr = I_addr;
		poll();
		single_cycle_state = single_cycle_complete;
		tell_new_state("B");
		break;

/* Biiid */
	  case 5: /* one address + d */

		/* Check the condition, reset if necessary */

		if(dcodes[d_char] == NULL)
		   { /* missing d-char */
		    bad_d("No branch dispatch",d_char);
		    alert(alert_process);
		    return false;
		   } /* missing d-char */

		if((*dcodes[d_char])(d_char))
		   { /* take branch */
		    NI_addr = A_addr;
		    B_addr = I_addr;
		    poll();
		   } /* take branch */

		single_cycle_state = single_cycle_complete;
		
		tell_new_state("BIN");
		
		break;

/* Biiibbbd */
	  case 8: /* two-address + d */

		if(bad_address(B_addr)) return false;

		switch(single_cycle_state)
		   { /* state decode */
		    case single_cycle_run:
		    	B = memory[B_addr];

			if(BA8421(B) == d_char)
			   { /* take branch */
			    NI_addr = A_addr;
			    B_addr = I_addr;
			    poll();
			   } /* take branch */
			else
			   { /* don't branch */
			    B_addr--;
			   } /* don't branch */
			single_cycle_state = single_cycle_complete;
			break;
		    case single_cycle_start:
		    	B = memory[B_addr];
			A = d_char;	/* this may not be right... */
			B_addr--;
			cycle = cycle_B;
			single_cycle_state = B_B_f_complete;
			break;
		    case B_B_f_complete:
			if(BA8421(B) == d_char)
			   { /* take branch */
			    NI_addr = A_addr;
			    B_addr = I_addr;
			    poll();
			   } /* take branch */
		        single_cycle_state = single_cycle_complete;
		   } /* state decode */

		/* be sure to reset any indicators as described in 'notes' */
		single_cycle_state = single_cycle_complete;
		tell_new_state("BCE");
		break;
	  default: /* Illegal */
	  	illegal_length();
	  	return false;
	 }

      return true;
    }
