/*****************************************************************************
*           Change Log
*  Date     | Change
*-----------+-----------------------------------------------------------------
* 25-Nov-85 | [1.131] Created
*  8-Dec-85 | [1.157] Set single_cycle_complete at end of run decode
*  8-Dec-85 | [1.157] Created MLC.c from MLCWA.c
* 25-Jan-86 | [1.327] Check for bad length.  Note we don't handle device
*           | move instructions of length 8 yet
* 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
* 22-Dec-94 | [1.600J] JRJ add code to identify I/O operations.
*****************************************************************************/

#include "stdio.h"
#include "btypes.h"
#include "mem1401.h"
#include "boolean.h"
#include "mach.h"
#include "machmem.h"
#include "diag.h"
#include "instr.h"
#include "ifetch.h"
#include "io.h"
#include "alert.h"
#include "alerts.h"

/* Machine State:    */

#define MLC_last (local_microstate+1)

#define MLC_A_complete single_cycle(i_MLC,A_complete)
#define MLC_B_complete single_cycle(i_MLC,B_complete)
#define MLC_A_last     single_cycle(i_MLC,MLC_last)

extern char * single_cycle_decode();

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

			       Move Instruction
	      (Move Left Characters to Wordmark in A- or B-field)


			     Move (Device Address)
			     ---------------------

Instruction Format:

Mnemonic	Op Code	A-address	B-address       d-char
--------        ------- ---------       ---------       ------
MU		M	%Dn		BBB              d

Function: Device-specific.  Cannot be chanined.


			     Move (Two Addresses)
			     --------------------

Instruction format

Mnemonic	Op Code	A-address	B-address
--------        ------- ---------       ---------
MLC		M	AAA		BBB

Function: The data in the A-field is moved to the B-field

Word Marks: If both fields are the same length, only one of the fields must
have a defining word mark.  The first word mark encountered stops the
operantion.  If the word mark is sensed in the A-field, the machine takes
one more B-cycle to move the high-order character from A to B.  At the
end of the operation, the A-address register and the B-address register
contain the address of the storage locations immediately to the left of
the A- and B=fields processed by the instruction.  The data at the A-address
is unaffected by the move operation.  Word marks in both fields are
undisturbed.

Address Registers After Operation:

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

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


			 Move Characters (One Address)
			 -----------------------------
Instruction format

Mnemonic	Op Code	A-address
--------        ------- ---------
MLC		M	AAA		

Function: This format can be used when several A-fields (not necessarily
in sequence) are to be loaded sequentially to the B-field.  It saves program
storage space and time, because the B-address is automatically taken from the
B-address register and does not have to be written or interpreted as part of
the instruction.

Word Marks: A word mark is required over the high-order position of the A- or
B-field.  The first word mark encountered stops the move operation.

Address Registers After Operation:

I-Add		A-Add		B-Add
-----           -----           -----
NSI		A-Lw		Bp-Lw

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


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


/****************************************************************************
*                                   inst_MLC
* result: boolean
*	true if instruction succeeded
*	false if instruction failed
* Effect: 
*	Executes move-characters-to-A-or-B-field-word-mark instruction
****************************************************************************/

boolean inst_MLC()
    {
     boolean quit;
     boolean result;
     boolean cycling;

     tell_op(op_A|op_B);

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

      /*
	If this is an I/O instruction, then the length must be 8.
	Otherwise, it is a regular move instruction, which can be
	chained.
      */

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

      if(io_index_inhibit) {
	switch(io_device) {
	case BCD_U:
		switch(d_char) {
		case BCD_R:
			return(tape_io_read(false));
			break;
		case BCD_W:
			return(tape_io_write(false));
			break;
		default:
			tell("Invalid Tape I/O d character.\n");
			alert(alert_process);
			return(false);
		}
	default:
		tell("Invalid I/O Device.\n");
		alert(alert_ramac);
		break;
	}
      }

      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:
			memory[B_addr] = B = BA8421(A = memory[A_addr]) | WM(memory[B_addr]);
			quit = WM(memory[A_addr]) || WM(memory[B_addr]);
			A_addr--;
			B_addr--;
			break;
	      case MLC_B_complete:
	      case single_cycle_start:
			B = memory[A_addr];
			single_cycle_state = (WM(A) ? MLC_A_last : MLC_A_complete);
			A_addr--;
			cycle = cycle_A;
			cycling = false;
			break;
	      case MLC_A_complete:
			A = B;
			memory[B_addr] = BA8421(B) | WM(memory[B_addr]);
			if(WM(memory[B_addr]))
			   { /* stop */
			    quit = true;
			   } /* stop */
			else
			   { /* continue */
			    single_cycle_state =  MLC_B_complete;
			   } /* continue */
			B_addr--;
			cycle = cycle_B;
			cycling = false;
			break;
	      case MLC_A_last:
			A = B;
			memory[B_addr] = BA8421(B) | WM(memory[B_addr]);
			quit = true;
			B_addr--;
			cycle = cycle_B;
			cycling = false;
			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("MLC");
      return result;
    }
