/*****************************************************************************
*           Change Log
*  Date     | Change
*-----------+-----------------------------------------------------------------
* 27-Dec-85 | [1.244] Created
* 25-Jan-86 | [1.327] Check for illegal length
* 27-Jan-86 | [1.350] declare single_cycle_decode
* 25-Feb-86 | [1.379] include <> => include ""
* 18-Nov-91 | [1.428] <jmn> memory.h => mem1401.h, avoid ANSI name
*****************************************************************************/

#include "stdio.h"

#include "mem1401.h"
#include "btypes.h"
#include "boolean.h"
#include "mach.h"
#include "diag.h"
#include "instr.h"
#include "ifetch.h"

extern char * single_cycle_decode();

/*****************************************************************************
				1401 Simulator

			 Zero and Subtract Instruction


		       Zero and Subtract (Two Addresses)
		       ---------------------------------

Instruction format

Mnemonic	Op Code	A-address	B-address
--------        ------- ---------       ---------
ZS		!	AAA		BBB

Function: The data in the A-field is subtracted from a zeroed B-field.  The
result is stored in the B-field.  Technically, this is accomplished by moving
the A-field to the B-field.  The high order positions of the B-field are set
to zero if the B-field is longer than the A-field.  The data from the A-field
moves directly from the A-register to storage.  Zone bits are stripped from
all positions except the units position.  Blanks in the A-field are stored as
blanks in the B-field.

Word Marks: The B-field must have a defining word mark, because it is this
word mark which actually stops the add operation.

The A-field must have a word mark only if it is shorter than the B-field.
In this case, the transmission of data from the A-field stops after the
A-field word mark is sensed.  Zeros are then inserted in the A-register
until the B-field word mark is sensed.

If the A-field is longer than the B-field, the high-order positions of
the A-field that exceed the limits imposed by the B-field are not processed.

Address Registers After Operation:

I-Add		A-Add		B-Add
-----		-----		-----
NSI		A-Lw		B-Lb

Chaining: This instruction can be chained to the preceding operation (if
that instruction left usable address-register contents) by supplying
only the operation code.


			Zero and Subtract (One Address)
			-------------------------------
Instruction format

Mnemonic	Op Code	A-address
--------        ------- ---------
ZS		!	AAA		

Function: This format of the zero and add instruction is used to strip the
A-field of all zone bits, except in the units position (sign).  The
A-field sign is inverted.

Word marks: The A-field must have a word mark in the high-order position.

Address Registers After Operation:

I-Add		A-Add		B-Add
-----           -----           -----
NSI		A-La		A-La

Chaining: This instruction can be chained to the preceding operation (if
that instruction left usable address-register contents) by supplying
only the operation code.


*****************************************************************************/

static boolean a_done;
#define ZS_A_f_complete single_cycle(i_ZS,A_f_complete)
#define ZS_B_s_complete single_cycle(i_ZS,B_s_complete)


/****************************************************************************
*                                   inst_ZS
* result: boolean
*	true if instruction succeeded
*	false if instruction failed
* Effect: 
*	Executes zero-and-add instruction
****************************************************************************/

boolean inst_ZS()
    {
     boolean quit;
     boolean result;
     boolean cycling;
     boolean run;

     tell_op(op_A|op_B);

     switch(I_cycle)
         {
	  case 1: /* chained */
	  case 4: /* one-address */
	  case 7: /* two-address */
	  	break;
	  default: /* Illegal length */
	  	illegal_length();
	  	return false;
	 }

      result = true;
      cycling = true;
      quit = false;

      while(cycling)
         { /* move chars */
	  if(bad_address(A_addr))
	     { /* bogus */
	      cycle = cycle_A;
	      result = false;
	      break;
	     } /* bogus */

	  if(bad_address(B_addr))
	     { /* bogus */
	      cycle = cycle_B;
	      result = false;
	      break;
	     } /* bogus */
	      
	  switch(single_cycle_state)
	     { /* state decode */
	      case single_cycle_run:
	      		B = memory[A_addr];
			run = true;
			A = B;
			if(ZONEBITS(B) == B_bits) A |= BA_bits;
			    else
			    A = NUMBITS(A) | B_bits;
			A = BA8421(A);
			A_addr--;
			a_done = WM(B);
			/* FALLS THRU */
              case ZS_A_f_complete:
	      		/* Store the value we have just fetched */
			memory[B_addr] = A | WM(memory[B_addr]);
			quit = WM(memory[B_addr]);
			B_addr--;
			cycling = run;
			single_cycle_state = ZS_B_s_complete;
			break;
	      case single_cycle_start:
	      		B = memory[A_addr];
			run = false;
			A = B;
			if(ZONEBITS(B) == B_bits) A |= BA_bits;
			    else
			    A = NUMBITS(A) | B_bits;
			A = BA8421(A);
			single_cycle_state = ZS_A_f_complete;
			A_addr--;
			a_done = WM(B);
			cycle = cycle_A;
			cycling = run;
			break;
	      case ZS_B_s_complete:
	      		if(a_done)
				B = 10;	/* BCD 0 */
			else
			   { /* fetch A */
			    B = memory[A_addr];
			    A_addr--;
			    a_done = WM(B);
			   } /* fetch A */
			A = NUMBITS(B);
			cycle = cycle_A;
			cycling = run;
			single_cycle_state = ZS_A_f_complete;
			break;
	      default:
	      		sprintf(diag_buffer,"Illegal microstate %s",single_cycle_decode());
			tell(diag_buffer);
			result = false;
			cycling = false;
			break;
	     } /* state decode */
	      if(quit) 
	      	{
		 single_cycle_state = single_cycle_complete;
		 break;
		}
 	  
	 } /* move chars */
	  
      tell_new_state("ZS");
      return result;	
    }
