/* vax_cis.c: VAX CIS instructions

   Copyright (c) 2004, Robert M Supnik

   Permission is hereby granted, free of charge, to any person obtaining a
   copy of this software and associated documentation files (the "Software"),
   to deal in the Software without restriction, including without limitation
   the rights to use, copy, modify, merge, publish, distribute, sublicense,
   and/or sell copies of the Software, and to permit persons to whom the
   Software is furnished to do so, subject to the following conditions:

   The above copyright notice and this permission notice shall be included in
   all copies or substantial portions of the Software.

   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
   ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
   IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
   CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

   Except as contained in this notice, the name of Robert M Supnik shall not
   be used in advertising or otherwise to promote the sale, use or other dealings
   in this Software without prior written authorization from Robert M Supnik.

   On a full VAX, this module simulates the VAX commercial instruction set (CIS).
   On a subset VAX, this module implements the emulated instruction fault.

   12-Apr-04	RMS	Cloned from pdp11_cis.c and vax_cpu1.c

   Zero length decimal strings require either zero bytes (trailing) or one byte
   (separate, packed).

   CIS instructions can run for a very long time, so they are interruptible
   and restartable.  In the simulator, string instructions (and EDITPC) are
   interruptible by faults, but decimal instructions run to completion.
   The code is unoptimized.
*/

#include "vax_defs.h"

#if defined (FULL_VAX)

/* Decimal string structure */

#define DSTRLNT		4
#define DSTRMAX		(DSTRLNT - 1)
#define MAXDVAL		429496730			/* 2^32 / 10 */

#define C_SPACE		0x20				/* ASCII chars */
#define C_PLUS		0x2B
#define C_MINUS		0x2D
#define C_ZERO		0x30
#define C_NINE		0x39

struct dstr {
	uint32		sign;
	uint32		val[DSTRLNT];  };

typedef struct dstr DSTR;

static DSTR Dstr_zero = { 0, 0, 0, 0, 0 };
static DSTR Dstr_one = { 0, 0x10, 0, 0, 0 };

extern int32 R[16];
extern int32 PSL;
extern int32 trpirq;
extern int32 p1;
extern int32 fault_PC;
extern int32 ibcnt, ppc;
extern int32 sim_interval;
extern jmp_buf save_env;

int32 ReadDstr (int32 lnt, int32 addr, DSTR *dec, int32 acc);
int32 WriteDstr (int32 lnt, int32 addr, DSTR *dec, int32 v, int32 acc);
int32 SetCCDstr (int32 lnt, DSTR *src, int32 pslv);
int32 AddDstr (DSTR *src1, DSTR *src2, DSTR *dst, int32 cin);
void SubDstr (DSTR *src1, DSTR *src2, DSTR *dst);
int32 CmpDstr (DSTR *src1, DSTR *src2);
int32 TestDstr (DSTR *dsrc);
void ProbeDstr (int32 lnt, int32 addr, int32 acc);
int32 LntDstr (DSTR *dsrc, int32 nz);
uint32 NibbleLshift (DSTR *dsrc, int32 sc, uint32 cin);
uint32 NibbleRshift (DSTR *dsrc, int32 sc, uint32 cin);
int32 WordLshift (DSTR *dsrc, int32 sc);
void WordRshift (DSTR *dsrc, int32 sc);
void CreateTable (DSTR *dsrc, DSTR mtable[10]);
int32 do_crc_4b (int32 crc, int32 tbl, int32 acc);
int32 edit_read_src (int32 inc, int32 acc);
void edit_adv_src (int32 inc);
int32 edit_read_sign (int32 acc);

extern int32 Read (uint32 va, int32 lnt, int32 acc);
extern void Write (uint32 va, int32 val, int32 lnt, int32 acc);
extern int32 eval_int (void);

/* CIS emulator */

int32 op_cis (int32 *op, int32 cc, int32 opc, int32 acc)
{
int32 i, j, c, t, pop, rpt, V;
int32 match, fill, sign, shift, ncc;
int32 ldivd, ldivr;
uint32 nc, d, result;
t_stat r;
DSTR accum, src1, src2, dst;
DSTR mptable[10];

switch (opc) {						/* case on opcode */

/* MOVTC

   Operands if PSL<fpd> = 0:
	op[0:1]		=	source string descriptor
	op[2]		=	fill character
	op[3]		=	table address
	op[4:5]		=	destination string descriptor

   Registers if PSL<fpd> = 1:
	R[0]		=	delta-PC/fill/source string length
	R[1]		=	source string address
	R[2]		=	number of bytes remaining to move
	R[3]		=	table address
	R[4]		=	destination string length
	R[5]		=	destination string address

   Condition codes:
	NZC		=	set from op[0]:op[4]
	V		=	0

   Registers:
	R0		=	src length remaining, or 0
	R1		=	addr of end of source string + 1
	R2		=	0
	R3		=	table address
	R4		=	0
	R5		=	addr of end of dest string + 1
*/

case MOVTC:
	if (PSL & PSL_FPD) {				/* FPD set? */
	    SETPC (fault_PC + STR_GETDPC (R[0]));	/* reset PC */
	    fill = STR_GETCHR (R[0]);			/* get fill */
	    R[2] = R[2] & STR_LNMASK;			/* remaining move */
	    R[4] = R[4] & STR_LNMASK;
	    }
	else {
	    R[0] = STR_PACK (op[2], op[0]);		/* src len, fill */
	    R[1] = op[1];				/* src addr */
	    fill = op[2];				/* set fill */
	    R[3] = op[3];				/* table addr */
	    R[4] = op[4];				/* dst len */
	    R[5] = op[5];				/* dst addr */
	    CC_CMP_W (op[0], op[4]);			/* set cc's */
	    R[2] = (op[0] < op[4])? op[0]: op[4];	/* remaining move */
	    PSL = PSL | PSL_FPD;			/* set FPD */
	    }
	if (R[2]) {					/* move to do? */
	    int32 mvl;
	    mvl = R[0] & STR_LNMASK;			/* orig move len */
	    if (mvl >= R[4]) mvl = R[4];
	    if (((uint32) R[1]) < ((uint32) R[5])) {	/* backward? */
		while (R[2]) {				/* loop thru char */
		    t = Read ((R[1] + R[2] - 1) & LMASK, L_BYTE, RA);
		    c = Read ((R[3] + t) & LMASK, L_BYTE, RA);
		    Write ((R[5] + R[2] - 1) & LMASK, c, L_BYTE, WA);
		    R[2] = (R[2] - 1) & STR_LNMASK;
		    }
		R[1] = (R[1] + mvl) & LMASK;		/* adv src, dst */
		R[5] = (R[5] + mvl) & LMASK;
		}
	    else {					/* forward */
		while (R[2]) {				/* loop thru char */
		    t = Read (R[1], L_BYTE, RA);	/* read src */
		    c = Read ((R[3] + t) & LMASK, L_BYTE, RA);
		    Write (R[5], c, L_BYTE, WA);	/* write dst */
		    R[1] = (R[1] + 1) & LMASK;		/* adv src, dst */
		    R[2] = (R[2] - 1) & STR_LNMASK;
		    R[5] = (R[5] + 1) & LMASK;
		    }
		}
	    R[0] = (R[0] & ~STR_LNMASK) | ((R[0] - mvl) & STR_LNMASK);
	    R[4] = (R[4] - mvl) & STR_LNMASK;		/* update lengths */
	    }
	while (R[4]) {					/* fill if needed */
	    Write (R[5], fill, L_BYTE, WA);
	    R[4] = (R[4] - 1) & STR_LNMASK;
	    R[5] = (R[5] + 1) & LMASK;			/* adv dst */
	    }
	R[0] = R[0] & STR_LNMASK;			/* mask off state */
	PSL = PSL & ~PSL_FPD;				/* all reg correct */
	return cc;

/* MOVTUC

   Operands:
	op[0:1]		=	source string descriptor
	op[2]		=	escape character
	op[3]		=	table address
	op[4:5]		=	destination string descriptor

   Registers if PSL<fpd> = 1:
	R[0]		=	delta-PC/match/source string length
	R[1]		=	source string address
	R[2]		=	not used
	R[3]		=	table address
	R[4]		=	destination string length
	R[5]		=	destination string address

   Condition codes:
	NZC		=	set from op[0]:op[4]
	V		=	1 if match to escape character

   Registers:
	R0		=	src length remaining, or 0
	R1		=	addr of end of source string + 1
	R2		=	0
	R3		=	table address
	R4		=	dst length remaining, or 0
	R5		=	addr of end of dest string + 1
*/

case MOVTUC:
	if (PSL & PSL_FPD) {				/* FPD set? */
	    SETPC (fault_PC + STR_GETDPC (R[0]));	/* reset PC */
	    fill = STR_GETCHR (R[0]);			/* get match */
	    R[4] = R[4] & STR_LNMASK;
	    }
	else {
	    R[0] = STR_PACK (op[2], op[0]);		/* src len, fill */
	    R[1] = op[1];				/* src addr */
	    fill = op[2];				/* set match */
	    R[3] = op[3];				/* table addr */
	    R[4] = op[4];				/* dst len */
	    R[5] = op[5];				/* dst addr */
	    CC_CMP_W (op[0], op[4]);			/* set cc's */
	    PSL = PSL | PSL_FPD;			/* set FPD */
	    }
	while ((R[0] & STR_LNMASK) && R[4]) {		/* while src & dst */
	    t = Read (R[1], L_BYTE, RA);		/* read src */
	    if (t == fill) {				/* stop char? */
		cc = cc | CC_V;				/* set V, done */
		break;
		}
	    c = Read ((R[3] + t) & LMASK, L_BYTE, RA);	/* translate */
	    Write (R[5], c, L_BYTE, WA);		/* write dst */
	    R[0] = (R[0] & ~STR_LNMASK) | ((R[0] - 1) & STR_LNMASK);
	    R[1] = (R[1] + 1) & LMASK;
	    R[4] = (R[4] - 1) & STR_LNMASK;		/* adv src, dst */
	    R[5] = (R[5] + 1) & LMASK;
	    }
	R[0] = R[0] & STR_LNMASK;			/* mask off state */
	R[2] = 0;
	PSL = PSL & ~PSL_FPD;
	return cc;

/* MATCHC

   Operands:
	op[0:1]		=	substring descriptor
	op[2:3]		=	string descriptor

   Registers if PSL<fpd> = 1:
	R[0]		=	delta-PC/match/substring length
	R[1]		=	substring address
	R[2]		=	source string length
	R[3]		=	source string address

   Condition codes:
	NZ		=	set from R0
	VC		=	0

   Registers:
	R0		=	if match, 0, else, op[0]
	R1		=	if match, op[0] + op[1], else, op[1]
	R2		=	if match, src bytes remaining, else, 0
	R3		=	if match, end of substr, else, op[2] + op[3]

   Notes:
	- If the string is zero length, and the substring is not,
	  the outer loop exits immediately, and the result is
	  "no match"
	- If the substring is zero length, the inner loop always
	  exits immediately, and the result is a "match"
	- If the string is zero length, and the substring is as
	  well, the outer loop executes, the inner loop exits
	  immediately, and the result is a match, but the result
	  is the length of the string (zero)
	- This instruction can potentially run a very long time - worst
	  case execution on a real VAX-11/780 was more than 30 minutes.
	  Accordingly, the instruction tests for interrupts and stops
	  if one is found.
*/

case MATCHC:
	if (PSL & PSL_FPD) {				/* FPD? */
	    SETPC (fault_PC + STR_GETDPC (R[0]));	/* reset PC */
	    R[2] = R[2] & STR_LNMASK;
	    }
	else {
	    R[0] = STR_PACK (0, op[0]);			/* srclen + FPD data */
	    R[1] = op[1];				/* save operands */
	    R[2] = op[2];
	    R[3] = op[3];
	    PSL = PSL | PSL_FPD;			/* set FPD */
	    }
	for (match = 0; R[2] >= (R[0] & STR_LNMASK); ) {
	    for (i = 0, match = 1; match && (i < (R[0] & STR_LNMASK)); i++) {
		c = Read ((R[1] + i) & LMASK, L_BYTE, RA);
		t = Read ((R[3] + i) & LMASK, L_BYTE, RA);
		match = (c == t);			/* continue if match */
	        }					/* end for substring */
	    if (match) break;				/* exit if match */
	    R[2] = (R[2] - 1) & STR_LNMASK;		/* decr src length */
	    R[3] = (R[3] + 1) & LMASK;			/* next string char */
	    if (i >= sim_interval) {			/* done with interval? */
		sim_interval = 0;
		if (r = sim_process_event ()) {		/* presumably WRU */
		    PC = fault_PC;			/* backup up PC */
		    ABORT (r);				/* abort flushes IB */
		    }
		SET_IRQL;				/* update interrupts */
		if (trpirq) ABORT (ABORT_INTR);		/* pending? stop */
		}
	    else sim_interval = sim_interval - i;
	    }						/* end for string */
	R[0] = R[0] & STR_LNMASK;
	if (match) {					/* if match */
	    R[1] = (R[1] + R[0]) & LMASK;
	    R[2] = (R[2] - R[0]) & STR_LNMASK;
	    R[3] = (R[3] + R[0]) & LMASK;
	    R[0] = 0;
	    }
	else {						/* if no match */
	    R[3] = (R[3] + R[2]) & LMASK;
	    R[2] = 0;
	    }
	PSL = PSL & ~PSL_FPD;
	CC_IIZZ_L (R[0]);				/* set cc's */
	return cc;

/* CRC

   Operands:
	op[0]		=	table address
	op[1]		=	initial CRC
	op[2:3]		=	source string descriptor

   Registers if PSL<fpd> = 1:
	R[0]		=	result
	R[1]		=	table address
	R[2]		=	delta-PC/0/source string length
	R[3]		=	source string address

   Condition codes:
	NZ		=	set from result
	VC		=	0

   Registers:
	R0		=	result
	R1		=	0
	R2		=	0
	R3		=	addr + 1 of last byte in source string
*/

case CRC:
	if (PSL & PSL_FPD) {				/* FPD? */
	    SETPC (fault_PC + STR_GETDPC (R[2]));	/* reset PC */
	    }
	else {
	    R[2] = STR_PACK (0, op[2]);			/* srclen + FPD data */
	    R[0] = op[1];				/* save operands */
	    R[1] = op[0];
	    R[3] = op[3];
	    PSL = PSL | PSL_FPD;			/* set FPD */
	    }
	while ((R[2] & STR_LNMASK) != 0) {		/* loop thru chars */
	    c = Read (R[3], L_BYTE, RA);		/* get char */
	    t = R[0] ^ c;				/* XOR to CRC */
	    t = do_crc_4b (t, R[1], acc);		/* do 4b shift */
	    R[0] = do_crc_4b (t, R[1], acc);		/* do 4b shift */
	    R[3] = (R[3] + 1) & LMASK;
	    R[2] = R[2] - 1;
	    }
	R[1] = 0;
	R[2] = 0;
	PSL = PSL & ~PSL_FPD;
	CC_IIZZ_L (R[0]);				/* set cc's */
	return cc;

/* MOVP

   Operands:
	op[0]		=	length
	op[1]		=	source string address
	op[2]		=	dest string address

   Condition codes:
	NZ		=	set from result
	V		=	0
	C		=	unchanged

   Registers:
	R0		=	0
	R1		=	addr of source string
	R2		=	0
	R3		=	addr of dest string
*/

case MOVP:
	if (PSL & PSL_FPD) RSVD_INST_FAULT;
	if (op[0] > 31) RSVD_OPND_FAULT;
	ReadDstr (op[0], op[1], &dst, acc);		/* read source */
	cc = WriteDstr (op[0], op[2], &dst, 0, acc) |	/* write dest */
	    (cc & CC_C);				/* preserve C */
	R[0] = 0;
	R[1] = op[1];
	R[2] = 0;
	R[3] = op[2];
	return cc;

/* ADDP4, ADDP6, SUBP4, SUBP6

   Operands:
	op[0:1]		=	src1 string descriptor
	op[2:3]		=	src2 string descriptor
	(ADDP6, SUBP6 only)
	op[4:5]		=	dest string descriptor

   Condition codes:
	NZV		=	set from result
	C		=	0

   Registers:
	R0		=	0
	R1		=	addr of src1 string
	R2		=	0
	R3		=	addr of src2 string
	(ADDP6, SUBP6 only)
	R4		=	0
	R5		=	addr of dest string	
*/

case ADDP4: case SUBP4:
	op[4] = op[2];					/* copy dst */
	op[5] = op[3];
case ADDP6: case SUBP6:
	if (PSL & PSL_FPD) RSVD_INST_FAULT;
	if ((op[0] > 31) || (op[2] > 31) || (op[4] > 31))
	    RSVD_OPND_FAULT;
	ReadDstr (op[0], op[1], &src1, acc);		/* get src1 */
	ReadDstr (op[2], op[3], &src2, acc);		/* get src2 */
	if (opc & 2) src1.sign = src1.sign ^ 1;		/* sub? invert sign */
	if (src1.sign ^ src2.sign) {			/* opp signs?  sub */
	    if (CmpDstr (&src1, &src2) < 0) {		/* src1 < src2? */
		SubDstr (&src1, &src2, &dst);		/* src2 - src1 */
		dst.sign = src2.sign;			/* sign = src2 */
		}
	    else {
	    	SubDstr (&src2, &src1, &dst);		/* src1 - src2 */
		dst.sign = src1.sign;			/* sign = src1 */
		}
	    V = 0;					/* can't carry */
	    }
	else {						/* addition */
	    V = AddDstr (&src1, &src2, &dst, 0);	/* add magnitudes */
	    dst.sign = src1.sign;			/* set result sign */
	    }
	cc = WriteDstr (op[4], op[5], &dst, V, acc);	/* store result */
	R[0] = 0;
	R[1] = op[1];
	R[2] = 0;
	R[3] = op[3];
	if (opc & 1) {					/* ADDP6, SUBP6? */
	    R[4] = 0;
	    R[5] = op[5];
	    }
	return cc;

/* MULP

   Operands:
	op[0:1]		=	src1 string descriptor
	op[2:3]		=	src2 string descriptor
	op[4:5]		=	dest string descriptor

   Condition codes:
	NZV		=	set from result
	C		=	0

   Registers:
	R0		=	0
	R1		=	addr of src1 string
	R2		=	0
	R3		=	addr of src2 string
	R4		=	0
	R5		=	addr of dest string	
*/

case MULP:
	if (PSL & PSL_FPD) RSVD_INST_FAULT;
	if ((op[0] > 31) || (op[2] > 31) || (op[4] > 31))
	    RSVD_OPND_FAULT;
	dst = Dstr_zero;				/* clear result */
	if (ReadDstr (op[0], op[1], &src1, acc) &&	/* read src1, src2 */
	    ReadDstr (op[2], op[3], &src2, acc)) {	/* if both > 0 */
	    dst.sign = src1.sign ^ src2.sign;		/* sign of result */
	    accum = Dstr_zero;				/* clear accum */
	    NibbleRshift (&src1, 1, 0);			/* shift out sign */
	    CreateTable (&src1, mptable);		/* create *1, *2, ... */
	    for (i = 1; i < (DSTRLNT * 8); i++) {	/* 31 iterations */
		d = (src2.val[i / 8] >> ((i % 8) * 4)) & 0xF;
		if (d > 0)				/* add in digit*mpcnd */
		    AddDstr (&mptable[d], &accum, &accum, 0);
		nc = NibbleRshift (&accum, 1, 0);	/* ac right 4 */
		NibbleRshift (&dst, 1, nc);		/* result right 4 */
		}
	    V = TestDstr (&accum) != 0;			/* if ovflo, set V */
	    }
	else V = 0;					/* result = 0 */
	cc = WriteDstr (op[4], op[5], &dst, V, acc);	/* store result */
	R[0] = 0;
	R[1] = op[1];
	R[2] = 0;
	R[3] = op[3];
	R[4] = 0;
	R[5] = op[5];
	return cc;

/* DIVP

   Operands:
	op[0:1]		=	src1 string descriptor
	op[2:3]		=	src2 string descriptor
	op[4:5]		=	dest string descriptor

   Condition codes:
	NZV		=	set from result
	C		=	0

   Registers:
	R0		=	0
	R1		=	addr of src1 string
	R2		=	0
	R3		=	addr of src2 string
	R4		=	0
	R5		=	addr of dest string	
*/

case DIVP:
	if (PSL & PSL_FPD) RSVD_INST_FAULT;
	if ((op[0] > 31) || (op[2] > 31) || (op[4] > 31))
	    RSVD_OPND_FAULT;
	ldivr = ReadDstr (op[0], op[1], &src1, acc);	/* get divisor */
	if (ldivr == 0) {				/* divisor = 0? */
	    SET_TRAP (TRAP_FLTDIV);			/* dec div trap */
	    return cc;
	    }
	ldivr = LntDstr (&src1, ldivr);			/* get exact length */
	ldivd = ReadDstr (op[2], op[3], &src2, acc);	/* get dividend */
	ldivd = LntDstr (&src2, ldivd);			/* get exact length */
	dst = Dstr_zero;				/* clear dest */
	NibbleRshift (&src1, 1, 0);			/* right justify ops */
	NibbleRshift (&src2, 1, 0);
	if ((t = ldivd - ldivr) >= 0) {			/* any divide to do? */
	    dst.sign = src1.sign ^ src2.sign;		/* calculate sign */
	    WordLshift (&src1, t / 8);			/* align divr to divd */
	    NibbleLshift (&src1, t % 8, 0);
	    CreateTable (&src1, mptable);		/* create *1, *2, ... */
	    for (i = 0; i <= t; i++) {			/* divide loop */
		for (d = 9; d > 0; d--) {		/* find digit */
		    if (CmpDstr (&src2, &mptable[d]) >= 0) {
			SubDstr (&mptable[d], &src2, &src2);
			dst.val[0] = dst.val[0] | d;
			break;
			}				/* end if */
		    }					/* end for */
		NibbleLshift (&src2, 1, 0);		/* shift dividend */
		NibbleLshift (&dst, 1, 0);		/* shift quotient */
		}					/* end divide loop */
	    }						/* end if */
	cc = WriteDstr (op[4], op[5], &dst, 0, acc);	/* store result */
	R[0] = 0;
	R[1] = op[1];
	R[2] = 0;
	R[3] = op[3];
	R[4] = 0;
	R[5] = op[5];
	return cc;

/* CMPP3, CMPP4

   Operands (CMPP3):
	op[0]		=	string length
	op[1], op[2]	=	string lengths

   Operands (CMPP4):
	op[0:1]		=	string1 descriptor
	op[2:3]		=	string2 descriptor

   Condition codes:
	NZ		=	set from comparison
	VC		=	0

   Registers:
	R0		=	0
	R1		=	addr of src1 string
	R2		=	0
	R3		=	addr of src2 string
*/

case CMPP3:
	op[3] = op[2];					/* reposition ops */
	op[2] = op[0];
case CMPP4:
	if (PSL & PSL_FPD) RSVD_INST_FAULT;
	if ((op[0] > 31) || (op[2] > 31)) RSVD_OPND_FAULT;
	ReadDstr (op[0], op[1], &src1, acc);		/* get src1 */
	ReadDstr (op[1], op[2], &src2, acc);		/* get src2 */
	cc = 0;
	if (src1.sign != src2.sign) cc = (src1.sign)? CC_N: 0;
	else {
	    t = CmpDstr (&src1, &src2);			/* compare strings */
	    if (t < 0) cc = (src1.sign? 0: CC_N);
	    else if (t > 0) cc = (src1.sign? CC_N: 0);
	    else cc = CC_Z;
	    }
	R[0] = 0;
	R[1] = op[1];
	R[2] = 0;
	R[3] = op[3];
	return cc ;

/* ASHP

   Operands:
	op[0]		=	shift count
	op[1:2]		=	source string descriptor
	op[3]		=	round digit
	op[4:5]		=	dest string descriptor

   Condition codes:
	NZV		=	set from result
	C		=	0

   Registers:
	R0		=	0
	R1		=	addr of src1 string
	R2		=	0
	R3		=	addr of src2 string
*/

case ASHP:
	if (PSL & PSL_FPD) RSVD_INST_FAULT;
	if ((op[1] > 31) || (op[4] > 31)) RSVD_OPND_FAULT;
	ReadDstr (op[1], op[2], &src1, acc);		/* get source */
	V = 0;						/* init V */
	shift = op[0];					/* get shift count */
	if (shift & BSIGN) {				/* right shift? */
	    shift = BMASK + 1 - shift;			/* !shift! */
	    WordRshift (&src1, shift / 8);		/* do word shifts */	
	    NibbleRshift (&src1, shift % 8, 0);		/* do nibble shifts */
	    t = op[3] & 0xF;				/* get round nibble */
	    if ((t + (src1.val[0] & 0xF)) > 9)		/* rounding needed? */
		AddDstr (&src1, &Dstr_one, &src1, 0);	/* round */
	    src1.val[0] = src1.val[0] & ~0xF;		/* clear sign */
	    }						/* end right shift */
	else if (shift) {				/* left shift? */
	    if (WordLshift (&src1, shift / 8)) V = 1;	/* do word shifts */
	    if (NibbleLshift (&src1, shift % 8, 0)) V = 1;
	    }						/* end left shift */
	cc = WriteDstr (op[4], op[5], &src1, V, acc);	/* store result */
	R[0] = 0;
	R[1] = op[2];
	R[2] = 0;
	R[3] = op[5];
	return cc ;

/* CVTPL

   Operands:
	op[0:1]		=	source string descriptor
	op[2]		=	memory flag/register number
	op[3]		=	memory address

   Condition codes:
	NZV		=	set from result
	C		=	0

   Registers:
	R0		=	0
	R1		=	addr of source string
	R2		=	0
	R3		=	0
*/

case CVTPL:
	if (PSL & PSL_FPD) RSVD_INST_FAULT;
	if (op[0] > 31) RSVD_OPND_FAULT;
	ReadDstr (op[0], op[1], &src1, acc);		/* get source */
	V = result = 0;					/* clear V, result */
	for (i = (DSTRLNT * 8) - 1; i > 0; i--) {	/* loop thru digits */
	    d = (src1.val[i / 8] >> ((i % 8) * 4)) & 0xF;
	    if (d || result || V) {			/* skip initial 0's */
		if (result >= MAXDVAL) V = 1;
		result = ((result * 10) + d) & LMASK;
		if (result < d) V = 1;
		}					/* end if */
	    }						/* end for */
	if (src1.sign) result = (~result + 1) & LMASK;	/* negative? */
	if (src1.sign ^ ((result & LSIGN) != 0)) V = 1;	/* test for overflow */
	if (op[2] >= 0) R[op[2]] = result;
	else Write (op[3], result, L_LONG, WA);
	if (V) SET_TRAP (TRAP_INTOV);
	R[0] = 0;
	R[1] = op[1];
	R[2] = 0;
	R[3] = 0;
	CC_IIZZ_L (result);
	return cc | V;

/* CVTLP

   Operands:
	op[0]		=	source long
	op[1:2]		=	dest string descriptor

   Condition codes:
	NZV		=	set from result
	C		=	0

   Registers:
	R0		=	0
	R1		=	0
	R2		=	0
	R3		=	addr of dest string
*/

case CVTLP:
	if (PSL & PSL_FPD) RSVD_INST_FAULT;
	if (op[1] > 31) RSVD_OPND_FAULT;
	dst = Dstr_zero;				/* clear result */
	result = op[0];
	if ((result & LSIGN) != 0) {
	    dst.sign = 1;
	    result = (~result + 1) & LMASK;
	    }
	for (i = 1; (i < (DSTRLNT * 8)) && result; i++) {
	    d = result % 10;
	    result = result / 10;
	    dst.val[i / 8] = dst.val[i / 8] | (d << ((i % 8) * 4));
	    }
	cc = WriteDstr (op[1], op[2], &dst, 0, acc);	/* write result */
	R[0] = 0;
	R[1] = 0;
	R[2] = 0;
	R[3] = op[2];
	return cc;

/* CVTSP

   Operands:
	op[0:1]		=	source string descriptor
	op[2:3]		=	dest string descriptor

   Condition codes:
	NZV		=	set from result
	C		=	0

   Registers:
	R0		=	0
	R1		=	address of sign byte of source string
	R2		=	0
	R3		=	addr of dest string
*/

case CVTSP:
	if (PSL & PSL_FPD) RSVD_INST_FAULT;
	if ((op[0] > 31) || (op[2] > 31)) RSVD_OPND_FAULT;
	dst = Dstr_zero;				/* clear result */
	t = Read (op[1], L_BYTE, RA);			/* read source sign */
	if (t == C_MINUS) dst.sign = 1;			/* sign -, */
	else if ((t != C_PLUS) && (t != C_SPACE))	/* + or blank? */
	    RSVD_OPND_FAULT;
	for (i = 1; i <= op[0]; i++) {			/* loop thru chars */
	    c = Read ((op[1] + op[0] + 1 - i) & LMASK, L_BYTE, RA);
	    if ((c < C_ZERO) || (c > C_NINE))		/* [0:9]? */
		RSVD_OPND_FAULT;
	    d = c & 0xF;
	    dst.val[i / 8] = dst.val[i / 8] | (d << ((i % 8) * 4));
	    }
	TestDstr (&dst);				/* correct -0 */
	cc = WriteDstr (op[2], op[3], &dst, 0, acc);	/* write result */
	R[0] = 0;
	R[1] = op[1];
	R[2] = 0;
	R[3] = op[3];
	return cc;

/* CVTPS 

   Operands:
	op[0:1]		=	source string descriptor
	op[2:3]		=	dest string descriptor

   Condition codes:
	NZV		=	set from result
	C		=	0

   Registers:
	R0		=	0
	R1		=	addr of source string
	R2		=	0
	R3		=	addr of dest string
*/

case CVTPS:
	if (PSL & PSL_FPD) RSVD_INST_FAULT;
	if ((op[0] > 31) || (op[2] > 31)) RSVD_OPND_FAULT;
	ReadDstr (op[0], op[1], &dst, acc);		/* get src */
	ProbeDstr (op[2], op[3], WA);			/* test dst write */
	Write (op[3], dst.sign? C_MINUS: C_PLUS, L_BYTE, WA);
	for (i = 1; i <= op[2]; i++) {			/* loop thru chars */
	    d = (dst.val[i / 8] >> ((i % 8) * 4)) & 0xF;/* get digit */
	    c = d | C_ZERO;				/* cvt to ASCII */
	    Write ((op[3] + op[2] + 1 - i) & LMASK, c, L_BYTE, WA);
	    }
	cc = SetCCDstr (op[2], &dst, 0);		/* set cc's */
	R[0] = 0;
	R[1] = op[1];
	R[2] = 0;
	R[3] = op[3];
	return cc;

/* CVTTP

   Operands:
	op[0:1]		=	source string descriptor
	op[2]		=	table address
	op[3:4]		=	dest string descriptor

   Condition codes:
	NZV		=	set from result
	C		=	0

   Registers:
	R0		=	0
	R1		=	addr of source string
	R2		=	0
	R3		=	addr of dest string
*/

case CVTTP:
	if (PSL & PSL_FPD) RSVD_INST_FAULT;
	if ((op[0] > 31) || (op[3] > 31)) RSVD_OPND_FAULT;
	dst = Dstr_zero;				/* clear result */
	for (i = 1; i <= op[0]; i++) {			/* loop thru char */
	    c = Read ((op[1] + op[0] - i) & LMASK, L_BYTE, RA); /* read char */
	    if (i != 1) {				/* normal byte? */
		if ((c < C_ZERO) || (c > C_NINE))	/* valid digit? */
		    RSVD_OPND_FAULT;
		d = c & 0xF;
		}
	    else {					/* highest byte */
		t = Read ((op[2] + c) & LMASK, L_BYTE, RA);	/* xlate */
		d = (t >> 4) & 0xF;			/* digit */
		t = t & 0xF;				/* sign */
		if ((d > 0x9) || (t < 0xA)) RSVD_OPND_FAULT;
		if ((t == 0xB) || (t == 0xD)) dst.sign = 1;
		}
	    dst.val[i / 8] = dst.val[i / 8] | (d << ((i % 8) * 4));
	    }
	TestDstr (&dst);				/* correct -0 */
	cc = WriteDstr (op[3], op[4], &dst, 0, acc);	/* write result */
	R[0] = 0;
	R[1] = op[1];
	R[2] = 0;
	R[3] = op[4];
	return cc;

/* CVTPT

   Operands:
	op[0:1]		=	source string descriptor
	op[2]		=	table address
	op[3:4]		=	dest string descriptor

   Condition codes:
	NZV		=	set from result
	C		=	0

   Registers:
	R0		=	0
	R1		=	addr of source string
	R2		=	0
	R3		=	addr of dest string
*/

case CVTPT:
	if (PSL & PSL_FPD) RSVD_INST_FAULT;
	if ((op[0] > 31) || (op[3] > 31)) RSVD_OPND_FAULT;
	ReadDstr (op[0], op[1], &dst, acc);		/* get source */
	ProbeDstr (op[3], op[4], WA);			/* test writeability */
	for (i = 1; i <= op[3]; i++) {			/* loop thru chars */
	    if (i != op[3]) {				/* not last? */
		d = (dst.val[i / 8] >> ((i % 8) * 4)) & 0xF;	/* get digit */
		c = d + C_ZERO;				/* convert */
		}
	    else {					/* translate last */
		t = Read ((op[1] + op[0]) & LMASK, L_BYTE, RA);
		c = Read ((op[2] + t) & LMASK, L_BYTE, RA);
		}
	    Write ((op[4] + op[3] - i) & LMASK, c, L_BYTE, WA);
	    }
	cc = SetCCDstr (op[3], &dst, 0);		/* set cc's */
	R[0] = 0;
	R[1] = op[1];
	R[2] = 0;
	R[3] = op[4];
	return cc;

/* EDITPC

   Operands:
	op[0:1]		=	source string descriptor
	op[2]		=	pattern string address
	op[3]		=	dest string address

   Condition codes:
	N		=	source is negative
	Z		=	source is zero
	V		=	significant digits lost
	C		=	significant digits seen

   Registers at packup:
	R0<31:16>	=	-count of source zeroes to supply
	R0<15:0>	=	remaining source length
	R1		=	source address
	R2<31:24>	=	delta PC
	R2<15:8>	=	sign char
	R2<7:0>		=	fill char
	R3		=	pattern string address
	R4		=	original source length
	R5		=	dest string addr

   Registers at end:
	R0		=	source length
	R1		=	source addr
	R2		=	0
	R3		=	addr of byte containing EO$END
	R4		=	0
	R5		=	addr of end of dst string + 1

   Fault and abort conditions for EDITPC are complicated.  In general:
   - It is safe to take a memory management fault on the read of
     any pattern byte.  After correction of the fault, the pattern
     operator is fetched and executed again.
   - It is safe to take a memory management fault on a write-only
     operation, like fill.  After correction of the fault, the
     pattern operator is fetched and executed again.
   - The move operators do not alter visible state (registers or cc)
     until all memory operations are complete.
*/

case EDITPC:
	if (PSL & PSL_FPD) {				/* FPD set? */
	    SETPC (fault_PC + STR_GETDPC (R[2]));	/* reset PC */
	    fill = ED_GETFILL (R[2]);			/* get fill */
	    sign = ED_GETSIGN (R[2]);			/* get sign */
	    R[0] = R[0] & ~0xFFE0;			/* src len <= 31 */
	    }
	else {						/* new instr */
	    if (op[0] > 31) RSVD_OPND_FAULT;		/* lnt > 31? */
	    d = Read ((op[1] + (op[0] / 2)) & LMASK, L_BYTE, RA) & 0xF;
	    if ((d == 0xB) || (d == 0xD)) {
		cc = CC_N | CC_Z;
		sign = C_MINUS;
		}
	    else {
		cc = CC_Z;
		sign = C_SPACE;
		}
	    fill = C_SPACE;
	    R[0] = R[4] = op[0];			/* src len */
	    R[1] = op[1];				/* src addr */
	    R[2] = STR_PACK (0, (sign << ED_V_SIGN) | (fill << ED_V_FILL));
							/* delta PC, sign, fill */
	    R[3] = op[2];				/* pattern */
	    R[5] = op[3];				/* dst addr */
	    PSL = PSL | PSL_FPD;			/* set FPD */
	    }
	for ( ;; ) {					/* loop thru pattern */
	    pop = Read (R[3], L_BYTE, RA);		/* rd pattern op */
	    if (pop == EO_END) break;			/* end? */
	    if (pop & EO_RPT_FLAG) {			/* repeat class? */
		rpt = pop & EO_RPT_MASK;		/* isolate count */
		if (rpt == 0) RSVD_OPND_FAULT;		/* can't be zero */
		pop = pop & ~EO_RPT_MASK;		/* isolate op */
		}
	    switch (pop) {				/* case on op */
	    case EO_END_FLOAT:				/* end float */
		if (!(cc & CC_C)) {			/* not signif? */
		    Write (R[5], sign, L_BYTE, WA);	/* write sign */
		    R[5] = (R[5] + 1) & LMASK;		/* now fault safe */
		    cc = cc | CC_C;			/* set signif */
		    }
		break;
	    case EO_CLR_SIGNIF:				/* clear signif */
		cc = cc & ~CC_C;			/* clr C */
		break;
	    case EO_SET_SIGNIF:				/* set signif */
		cc = cc | CC_C;				/* set C */
		break;
	    case EO_STORE_SIGN:				/* store sign */
		Write (R[5], sign, L_BYTE, WA);		/* write sign */
		R[5] = (R[5] + 1) & LMASK;		/* now fault safe */
		break;
	    case EO_LOAD_FILL:				/* load fill */
		fill = Read ((R[3] + 1) & LMASK, L_BYTE, RA);
		R[2] = ED_PUTFILL (R[2], fill);		/* now fault safe */
		R[3]++;
		break;
	    case EO_LOAD_SIGN:				/* load sign */
		sign = edit_read_sign (acc);
		break;
	    case EO_LOAD_PLUS:				/* load sign if + */
		if (!(cc & CC_N)) sign = edit_read_sign (acc);
		break;
	    case EO_LOAD_MINUS:				/* load sign if - */
		if (cc & CC_N) sign = edit_read_sign (acc);
		break;
	    case EO_INSERT:				/* insert char */
		c = Read ((R[3] + 1) & LMASK, L_BYTE, RA);
		Write (R[5], ((cc & CC_C)? c: sign), L_BYTE, WA);
		R[5] = (R[5] + 1) & LMASK;		/* now fault safe */
		R[3]++;
		break;
	    case EO_BLANK_ZERO:				/* blank zero */
		t = Read ((R[3] + 1) & LMASK, L_BYTE, RA);
		if (t == 0) RSVD_OPND_FAULT;
		while (t--)				/* repeat and blank */
		    Write ((R[5] - t) & LMASK, fill, L_BYTE, WA);
		R[3]++;					/* now fault safe */
		break;
	    case EO_REPL_SIGN:				/* replace sign */
		t = Read ((R[3] + 1) & LMASK, L_BYTE, RA);
		if (t == 0) RSVD_OPND_FAULT;
		if (cc & CC_Z)
		    Write ((R[5] - t) & LMASK, fill, L_BYTE, WA);
		R[3]++;					/* now fault safe */
		break;
	    case EO_ADJUST_LNT:				/* adjust length */
		t = Read ((R[3] + 1) & LMASK, L_BYTE, RA);
		if ((t == 0) || (t > 31)) RSVD_OPND_FAULT;
		R[0] = R[0] & WMASK;			/* clr old ld zero */
		if (R[0] > t) {				/* decrease */
		    ncc = cc;				/* copy cc's */
		    for (i = 0; i < (R[0] - t); i++) {	/* loop thru src */
			d = edit_read_src (i, acc);	/* get nibble */
		        if (d) ncc = (ncc | CC_V | CC_C) & ~CC_Z;
			}				/* end for */
		    cc = ncc;				/* now fault safe */
		    edit_adv_src (R[0] - t);		/* adv src ptr */
		    }					/* end else */	    
		else R[0] = R[0] | (((R[0] - t) & WMASK) << 16);
		R[3]++;
		break;
	    case EO_FILL:				/* fill */
		for (i = 0; i < rpt; i++)		/* fill string */
		    Write ((R[5] + i) & LMASK, fill, L_BYTE, WA);
		R[5] = (R[5] + rpt) & LMASK;		/* now fault safe */
		break;
	    case EO_MOVE:
		ncc = cc;				/* copy cc's */
		for (i = 0; i < rpt; i++) {		/* for repeat */
		    d = edit_read_src (i, acc);		/* get nibble */
		    if (d) ncc = (ncc | CC_C) & ~CC_Z;	/* test for non-zero */
		    c = (ncc & CC_C)? (d | 0x30): fill;	/* test for signif */
		    Write ((R[5] + i) & LMASK, c, L_BYTE, WA);
		    }					/* end for */
		cc = ncc;				/* now fault safe */
		edit_adv_src (rpt);			/* advance src */
		R[5] = (R[5] + rpt) & LMASK;		/* advance dst */
		break;
	    case EO_FLOAT:
		ncc = cc;				/* copy cc's */
		for (i = j = 0; i < rpt; i++, j++) {	/* for repeat */
		    d = edit_read_src (i, acc);		/* get nibble */
		    if (d && !(ncc & CC_C)) { 		/* nz, signif clear? */
			Write ((R[5] + j) & LMASK, sign, L_BYTE, WA);
			ncc = (ncc | CC_C) & ~CC_Z;	/* set signif */
			j++;				/* extra dst char */
			}				/* end if */
		    c = (ncc & CC_C)? (d | 0x30): fill;	/* test for signif */
		    Write ((R[5] + j) & LMASK, c, L_BYTE, WA);
		    }					/* end for */
		cc = ncc;				/* now fault safe */
		edit_adv_src (rpt);			/* advance src */
		R[5] = (R[5] + j) & LMASK;		/* advance dst */
		break;
	    default:					/* undefined */
		RSVD_OPND_FAULT;
		}					/* end case pattern */
	    R[3] = (R[3] + 1) & LMASK;			/* next pattern byte */
	    }						/* end for pattern */
	if (R[0]) RSVD_OPND_FAULT;			/* pattern too short */
	PSL = PSL & ~PSL_FPD;				/* clear FPD */
	if (cc & CC_Z) cc = cc & ~CC_N;			/* zero? clear n */
	if ((cc & CC_V) && (PSL & PSW_DV)) SET_TRAP (TRAP_DECOVF);
	R[0] = R[4];					/* restore src len */
	R[1] = R[1] - (R[0] >> 1);			/* restore src addr */
	R[2] = R[4] = 0;
	return cc;

default:
	RSVD_INST_FAULT;
	}						/* end case op */
return cc;
}

/* Get packed decimal string

   Arguments:
	lnt	=	decimal string length
	adr	=	decimal string address
	src	=	decimal string structure
	acc	=	access mode

   The routine returns the length in int32's of the non-zero part of
   the string.

   To simplify the code elsewhere, digits are range checked,
   and bad digits cause a fault.
*/

int32 ReadDstr (int32 lnt, int32 adr, DSTR *src, int32 acc)
{
int32 c, i, end, t;

*src = Dstr_zero;					/* clear result */
end = lnt / 2;						/* last byte */
for (i = 0; i <= end; i++) {				/* loop thru string */
	c = Read ((adr + end - i) & LMASK, L_BYTE, RA);	/* get byte */
	if (i == 0) {					/* sign char? */
	    t = c & 0xF;				/* save sign */
	    c = c & 0xF0;  }				/* erase sign */
	if ((i == end) && ((lnt & 1) == 0)) c = c & 0xF;
	if (((c & 0xF0) > 0x90) ||			/* check hi digit */
	    ((c & 0x0F) > 0x09)) RSVD_OPND_FAULT;	/* check lo digit */	
	src->val[i / 4] = src->val[i / 4] | (c << ((i % 4) * 8));
	}						/* end for */
if ((t == 0xB) || (t == 0xD)) src->sign = 1;		/* if -, set sign */
return TestDstr (src);					/* clean -0 */
}
	
/* Store decimal string

   Arguments:
	lnt	=	decimal string length
	adr	=	decimal string address
	dst	=	decimal string structure
	V	=	initial overflow flag
	acc	=	access mode

   Returns condition codes.

   PSL.NZ are also set to their proper values
   PSL.V will be set on overflow; it must be initialized elsewhere
	(to allow for external overflow calculations)

   The rules for the stored sign and the PSW sign are:
	- Stored sign is negative if input is negative, and the result
	  is non-zero or there was overflow
	- PSL sign is negative if input is negative, and the result is
	  non-zero
   Thus, the stored sign and the PSL sign will differ in one case:
   a negative zero generated by overflow is stored with a negative
   sign, but PSL.N is clear
*/

int32 WriteDstr (int32 lnt, int32 adr, DSTR *dst, int32 pslv, int32 acc)
{
int32 c, i, cc, end;

end = lnt / 2;						/* end of string */
ProbeDstr (end, adr, WA);				/* test writeability */
cc = SetCCDstr (lnt, dst, pslv);			/* set cond codes */
dst->val[0] = dst->val[0] | 0xC | dst->sign;		/* set sign */
for (i = 0; i <= end; i++) {				/* store string */
	c = (dst->val[i / 4] >> ((i % 4) * 8)) & 0xFF;
	Write ((adr + end - i) & LMASK, c, L_BYTE, WA);
	}						/* end for */
return cc;
}

/* Set CC for decimal string

   Arguments:
	lnt	=	string length
	dst	=	decimal string structure
	pslv	=	initial V

   Output:
	cc	=	condition codes
*/

int32 SetCCDstr (int32 lnt, DSTR *dst, int32 pslv)
{
int32 psln, pslz, i, limit;
uint32 mask;
static uint32 masktab[8] = {
	0xFFFFFFF0, 0xFFFFFF00, 0xFFFFF000, 0xFFFF0000,
	0xFFF00000, 0xFF000000, 0xF0000000, 0x00000000  };

mask = 0;						/* can't ovflo */
pslz = 1;						/* assume all 0's */
limit = lnt / 8;					/* limit for test */
for (i = 0; i < DSTRLNT; i++) {				/* loop thru value */
	if (i == limit) mask = masktab[lnt % 8];	/* at limit, get mask */
	else if (i > limit) mask = 0xFFFFFFFF;		/* beyond, all ovflo */
	if (dst->val[i] & mask) pslv = 1;		/* test for ovflo */
	if (dst->val[i] = dst->val[i] & ~mask) pslz = 0;/* test nz */
	}
dst->sign = dst->sign & ~(pslz & ~pslv);
psln = dst->sign & ~pslz;				/* N = sign, if ~zero */
if (pslv && (PSL & PSW_DV)) SET_TRAP (TRAP_DECOVF);
return (psln? CC_N: 0) | (pslz? CC_Z: 0) | (pslv? CC_V: 0);
}

/* Probe decimal string for accessibility */

void ProbeDstr (int32 lnt, int32 addr, int32 acc)
{
Read (addr, L_BYTE, acc);
Read ((addr + lnt) & LMASK, L_BYTE, acc);
return;
}

/* Add decimal string magnitudes

   Arguments:
	s1	=	src1 decimal string
	s2	=	src2 decimal string
	ds	=	dest decimal string
	cy	=	carry in
   Output	=	1 if carry, 0 if no carry

   This algorithm courtesy Anton Chernoff, circa 1992 or even earlier

   We trace the history of a pair of adjacent digits to see how the
   carry is fixed; each parenthesized item is a 4b digit.

   Assume we are adding:
	(a)(b)	I
   +	(x)(y)	J

   First compute I^J:
	(a^x)(b^y)	TMP

   Note that the low bit of each digit is the same as the low bit of
   the sum of the digits, ignoring the carry, since the low bit of the
   sum is the xor of the bits.

   Now compute I+J+66 to get decimal addition with carry forced left
   one digit:
	(a+x+6+carry mod 16)(b+y+6 mod 16)	SUM

   Note that if there was a carry from b+y+6, then the low bit of the
   left digit is different from the expected low bit from the xor.
   If we xor this SUM into TMP, then the low bit of each digit is 1
   if there was a carry, and 0 if not.  We need to subtract 6 from each
   digit that did not have a carry, so take ~(SUM ^ TMP) & 0x11, shift
   it right 4 to the digits that are affected, and subtract 6*adjustment
   (actually, shift it right 3 and subtract 3*adjustment).
*/

int32 AddDstr (DSTR *s1, DSTR *s2, DSTR *ds, int32 cy)
{
int32 i;
uint32 sm1, sm2, tm1, tm2, tm3, tm4;

for (i = 0; i < DSTRLNT; i++) {				/* loop low to high */
	tm1 = s1->val[i] ^ (s2->val[i] + cy);		/* xor operands */
	sm1 = s1->val[i] + (s2->val[i] + cy);		/* sum operands */
	sm2 = sm1 + 0x66666666;				/* force carry out */
	cy = ((sm1 < s1->val[i]) || (sm2 < sm1));	/* check for overflow */
	tm2 = tm1 ^ sm2;				/* get carry flags */
	tm3 = (tm2 >> 3) | (cy << 29);			/* compute adjustment */
	tm4 = 0x22222222 & ~tm3;			/* clear where carry */
	ds->val[i] = (sm2 - (3 * tm4)) & LMASK;		/* final result */
	}
return cy;
}

/* Subtract decimal string magnitudes

   Arguments:
	s1	=	src1 decimal string
	s2	=	src2 decimal string
	ds	=	dest decimal string
   Outputs:		s2 - s1 in ds

   Note: the routine assumes that s1 <= s2

*/

void SubDstr (DSTR *s1, DSTR *s2, DSTR *ds)
{
int32 i;
DSTR compl;

for (i = 0; i < DSTRLNT; i++)				/* 10's comp s2 */
	compl.val[i] = 0x99999999 - s1->val[i];
AddDstr (&compl, s2, ds, 1);				/* s1 + ~s2 + 1 */
return;
}

/* Compare decimal string magnitudes

   Arguments:
	s1	=	src1 decimal string
	s2	=	src2 decimal string
   Output	=	1 if >, 0 if =, -1 if <
*/

int32 CmpDstr (DSTR *s1, DSTR *s2)
{
int32 i;

for (i = DSTRMAX; i >=0; i--) {
	if (s1->val[i] > s2->val[i]) return 1;
	if (s1->val[i] < s2->val[i]) return -1;
	}
return 0;
}

/* Test decimal string for zero

   Arguments:
	dsrc	=	decimal string structure

   Returns the non-zero length of the string, in int32 units
   If the string is zero, the sign is cleared
*/

int32 TestDstr (DSTR *dsrc)
{
int32 i;

for (i = DSTRMAX; i >= 0; i--) if (dsrc->val[i]) return (i + 1);
dsrc->sign = 0;
return 0;
}

/* Get exact length of decimal string

   Arguments:
	dsrc	=	decimal string structure
	nz	=	result from TestDstr
*/

int32 LntDstr (DSTR *dsrc, int32 nz)
{
int32 i;

for (i = 7; i > 0; i--) {
	if ((dsrc->val[nz - 1] >> (i * 4)) & 0xF) break;
	}
return ((nz - 1) * 8) + i;
}

/* Create table of multiples

   Arguments:
	dsrc	=	base decimal string structure
	mtable[10] =	array of decimal string structures

   Note that dsrc has a high order zero nibble; this
	guarantees that the largest multiple won't overflow
   Also note that mtable[0] is not filled in
*/

void CreateTable (DSTR *dsrc, DSTR mtable[10])
{
int32 (i);

mtable[1] = *dsrc;
for (i = 2; i < 10; i++)
	AddDstr (&mtable[1], &mtable[i-1], &mtable[i], 0);
return;
}

/* Word shift right

   Arguments:
	dsrc	=	decimal string structure
	sc	=	shift count
*/

void WordRshift (DSTR *dsrc, int32 sc)
{
int32 i;

if (sc) {
	for (i = 0; i < DSTRLNT; i++) {
	    if ((i + sc) < DSTRLNT) dsrc->val[i] = dsrc->val[i + sc];
	    else dsrc->val[i] = 0;
	    }
	}
return;
}

/* Word shift left

   Arguments:
	dsrc	=	decimal string structure
	sc	=	shift count
*/

int32 WordLshift (DSTR *dsrc, int32 sc)
{
int32 i, c;

c = 0;
if (sc) {
	for (i = DSTRMAX; i >= 0; i--) {
	    if (i > (DSTRMAX - sc)) c = c | dsrc->val[i];
	    if ((i - sc) >= 0) dsrc->val[i] = dsrc->val[i - sc];
	    else dsrc->val[i] = 0;
	    }
	}
return c;
}		

/* Nibble shift decimal string right

   Arguments:
	dsrc	=	decimal string structure
	sc	=	shift count
	cin	=	carry in
*/

uint32 NibbleRshift (DSTR *dsrc, int32 sc, uint32 cin)
{
int32 i, s, rs, nc;

if (s = sc * 4) {
	rs = 32 - s;
	for (i = DSTRMAX; i >= 0; i--) {
	    nc = dsrc->val[i];
	    dsrc->val[i] = ((dsrc->val[i] >> s) |
		(cin << rs)) & 0xFFFFFFFF;
	    cin = nc;
	    }
	return cin;
	}
return 0;
}

/* Nibble shift decimal string left

   Arguments:
	dsrc	=	decimal string structure
	sc	=	shift count
	cin	=	carry in
*/

uint32 NibbleLshift (DSTR *dsrc, int32 sc, uint32 cin)
{
int32 i, s, rs, nc;

if (s = sc * 4) {
	rs = 32 - s;
	for (i = 0; i < DSTRLNT; i++) {
	    nc = dsrc->val[i];
	    dsrc->val[i] = ((dsrc->val[i] << s) |
		(cin >> rs)) & 0xFFFFFFFF;
	    cin = nc;
	    }
	return cin;
	}
return 0;
}

/* Do 4b of CRC calculation

   Arguments:
	crc	=	current CRC ^ char
	tbl	=	16 lw table base

   Output:
	new CRC
*/

int32 do_crc_4b (int32 crc, int32 tbl, int32 acc)
{
int32 idx = (crc & 0xF) << 2;
int32 t;

crc = (crc >> 4) & 0x0FFFFFFF;
t = Read ((tbl + idx) & LMASK, L_LONG, RA);
return crc ^ t;
}

/* Edit routines */

int32 edit_read_src (int32 inc, int32 acc)
{
int32 c, r0, r1;

if (R[0] & LSIGN) {					/* ld zeroes? */
	r0 = (R[0] + (inc << 16)) & LMASK;		/* retire increment */
	if (r0 & LSIGN) return 0;			/* more? return 0 */
	inc = (r0 >> 16) & 0x1F;			/* effective inc */
	}
r1 = (R[1] + (inc / 2) + ((~R[0] & inc) & 1)) & LMASK;	/* eff addr */
r0 = (R[0] - inc) & 0x1F;				/* eff lnt left */
if (r0 == 0) {						/* nothing left? */
	R[0] = -1;					/* out of input */
	RSVD_OPND_FAULT;
	}
c = Read (r1, L_BYTE, RA);
return (((r0 & 1)? (c >> 4): c) & 0xF);
}

void edit_adv_src (int32 inc)
{
if (R[0] & LSIGN) {					/* ld zeroes? */
	R[0] = (R[0] + (inc << 16)) & LMASK;		/* retire 0's */
	if (R[0] & LSIGN) return;			/* more to do? */
	inc = (R[0] >> 16) & 0x1F;			/* get excess */
	if (inc == 0) return;				/* more to do? */
	}
R[1] = (R[1] + (inc / 2) + ((~R[0] & inc) & 1)) & LMASK;/* retire src */
R[0] = (R[0] - inc) & 0x1F;
return;
}

int32 edit_read_sign (int32 acc)
{
int32 sign;

sign = Read ((R[3] + 1) & LMASK, L_BYTE, RA);		/* read */
R[2] = ED_PUTSIGN (R[2], sign);				/* now fault safe */
R[3] = (R[3] + 1) & LMASK;
return sign;
}

#else

extern int32 R[16];
extern int32 PSL;
extern int32 SCBB;
extern int32 fault_PC;
extern int32 ibcnt, ppc;
extern int32 pcq[PCQ_SIZE];
extern int32 pcq_p;
extern jmp_buf save_env;

extern int32 Read (uint32 va, int32 lnt, int32 acc);
extern void Write (uint32 va, int32 val, int32 lnt, int32 acc);
extern int32 ReadLP (uint32 pa);

/* CIS instructions - invoke emulator interface

	opnd[0:5] =	six operands to be pushed (if PSL<fpd> = 0)
	cc	=	condition codes
	opc	=	opcode

   If FPD is set, push old PC and PSL on stack, vector thru SCB.
   If FPD is clear, push opcode, old PC, operands, new PC, and PSL
	on stack, vector thru SCB.
   In both cases, the exception occurs in the current mode.
*/

int32 op_cis (int32 *opnd, int32 cc, int32 opc, int32 acc)
{
int32 vec;

if (PSL & PSL_FPD) {					/* FPD set? */
	Read (SP - 1, L_BYTE, WA);			/* wchk stack */
	Write (SP - 8, fault_PC, L_LONG, WA);		/* push old PC */	
	Write (SP - 4, PSL | cc, L_LONG, WA);		/* push PSL */
	SP = SP - 8;					/* decr stk ptr */
	vec = ReadLP ((SCBB + SCB_EMULFPD) & PAMASK);  }
else {	if (opc == CVTPL)				/* CVTPL? .wl */
	    opnd[2] = (opnd[2] >= 0)? ~opnd[2]: opnd[3];
	Read (SP - 1, L_BYTE, WA);			/* wchk stack */
	Write (SP - 48, opc, L_LONG, WA);		/* push opcode */
	Write (SP - 44, fault_PC, L_LONG, WA);		/* push old PC */
	Write (SP - 40, opnd[0], L_LONG, WA);		/* push operands */
	Write (SP - 36, opnd[1], L_LONG, WA);
	Write (SP - 32, opnd[2], L_LONG, WA);
	Write (SP - 28, opnd[3], L_LONG, WA);
	Write (SP - 24, opnd[4], L_LONG, WA);
	Write (SP - 20, opnd[5], L_LONG, WA);
	Write (SP - 8, PC, L_LONG, WA);			/* push cur PC */
	Write (SP - 4, PSL | cc, L_LONG, WA);		/* push PSL */
	SP = SP - 48;					/* decr stk ptr */
	vec = ReadLP ((SCBB + SCB_EMULATE) & PAMASK);  }
PSL = PSL & ~(PSL_TP | PSL_FPD | PSW_DV | PSW_FU | PSW_IV | PSW_T);
JUMP (vec & ~03);					/* set new PC */
return 0;						/* set new cc's */
}

#endif
