/* vax_cis.c: VAX CIS instructions | |
Copyright (c) 2004-2008, 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. | |
16-Oct-08 RMS Fixed bug in ASHP left overflow calc (Word/NibbleLShift) | |
Fixed bug in DIVx (LntDstr calculation) | |
28-May-08 RMS Inlined physical memory routines | |
16-May-06 RMS Fixed bug in length calculation (Tim Stark) | |
03-May-06 RMS Fixed MOVTC, MOVTUC to preserve cc's through page faults | |
Fixed MOVTUC to stop on translated == escape | |
Fixed CVTPL to set registers before destination reg write | |
Fixed CVTPL to set correct cc bit on overflow | |
Fixed EDITPC to preserve cc's through page faults | |
Fixed EDITPC EO$BLANK_ZERO count, cc test | |
Fixed EDITPC EO$INSERT to insert fill instead of blank | |
Fixed EDITPC EO$LOAD_PLUS/MINUS to skip character | |
(Tim Stark) | |
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 | |
typedef struct { | |
uint32 sign; | |
uint32 val[DSTRLNT]; | |
} 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 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; | |
int32 ldivd, ldivr; | |
int32 lenl, lenp; | |
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] = saved cc's/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 */ | |
cc = (R[4] >> 16) & CC_MASK; /* restore cc's */ | |
} | |
else { | |
CC_CMP_W (op[0], op[4]); /* set cc's */ | |
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] | ((cc & CC_MASK) << 16); /* dst len + cc's */ | |
R[5] = op[5]; /* dst addr */ | |
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] & STR_LNMASK)) | |
mvl = R[4] & STR_LNMASK; | |
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; | |
} | |
} /* update lengths */ | |
R[0] = (R[0] & ~STR_LNMASK) | ((R[0] - mvl) & STR_LNMASK); | |
R[4] = (R[4] & ~STR_LNMASK) | ((R[4] - mvl) & STR_LNMASK); | |
} | |
while (R[4] & STR_LNMASK) { /* fill if needed */ | |
Write (R[5], fill, L_BYTE, WA); | |
R[4] = (R[4] & ~STR_LNMASK) | ((R[4] - 1) & STR_LNMASK); | |
R[5] = (R[5] + 1) & LMASK; /* adv dst */ | |
} | |
R[0] = R[0] & STR_LNMASK; /* mask off state */ | |
R[4] = 0; | |
PSL = PSL & ~PSL_FPD; | |
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] = saved condition codes | |
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; | |
cc = R[2] & CC_MASK; /* restore cc's */ | |
} | |
else { | |
CC_CMP_W (op[0], op[4]); /* set cc's */ | |
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 */ | |
R[2] = cc; /* save 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 */ | |
c = Read ((R[3] + t) & LMASK, L_BYTE, RA); /* translate */ | |
if (c == fill) { /* stop char? */ | |
cc = cc | CC_V; /* set V, done */ | |
break; | |
} | |
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) /* exit if match */ | |
break; | |
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) /* pending? stop */ | |
ABORT (ABORT_INTR); | |
} | |
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) || (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) || (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) /* sub? invert sign */ | |
src1.sign = src1.sign ^ 1; | |
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) || (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) || (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) || (op[0] > 31) || (op[2] > 31)) | |
RSVD_OPND_FAULT; | |
ReadDstr (op[0], op[1], &src1, acc); /* get src1 */ | |
ReadDstr (op[2], op[3], &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) || (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)) /* do word shifts */ | |
V = 1; | |
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) || (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) /* negative? */ | |
result = (~result + 1) & LMASK; | |
if (src1.sign ^ ((result & LSIGN) != 0)) /* test for overflow */ | |
V = 1; | |
if (op[2] < 0) /* if mem, store result */ | |
Write (op[3], result, L_LONG, WA); /* before reg update */ | |
R[0] = 0; /* update registers */ | |
R[1] = op[1]; | |
R[2] = 0; | |
R[3] = 0; | |
if (op[2] >= 0) /* if reg, store result */ | |
R[op[2]] = result; /* after reg update */ | |
if (V && (PSL & PSW_IV)) /* ovflo and IV? trap */ | |
SET_TRAP (TRAP_INTOV); | |
CC_IIZZ_L (result); | |
return cc | (V? CC_V: 0); | |
/* 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) || (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) || (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) /* sign -, */ | |
dst.sign = 1; | |
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) || (op[0] > 31) || (op[2] > 31)) | |
RSVD_OPND_FAULT; | |
lenl = ReadDstr (op[0], op[1], &dst, acc); /* get source, lw len */ | |
lenp = LntDstr (&dst, lenl); /* get exact nz src len */ | |
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[0], &dst, 0); /* set cc's */ | |
if (lenp > op[2]) { /* src fit in dst? */ | |
cc = cc | CC_V; /* set ovflo */ | |
if (PSL & PSW_DV) SET_TRAP (TRAP_DECOVF); /* if enabled, trap */ | |
} | |
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) || (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) || (op[0] > 31) || (op[3] > 31)) | |
RSVD_OPND_FAULT; | |
lenl = ReadDstr (op[0], op[1], &dst, acc); /* get source, lw len */ | |
lenp = LntDstr (&dst, lenl); /* get exact src len */ | |
ProbeDstr (op[3], op[4], WA); /* test writeability */ | |
for (i = 1; i <= op[3]; i++) { /* loop thru chars */ | |
if (i != 1) { /* 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] / 2)) & 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[0], &dst, 0); /* set cc's from src */ | |
if (lenp > op[3]) { /* src fit in dst? */ | |
cc = cc | CC_V; /* set ovflo */ | |
if (PSL & PSW_DV) SET_TRAP (TRAP_DECOVF); /* if enabled, trap */ | |
} | |
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<19:16> = condition codes | |
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 saved 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 */ | |
cc = ED_GETCC (R[2]); /* get cc's */ | |
R[0] = R[0] & ~0xFFE0; /* src len <= 31 */ | |
} | |
else { /* new instr */ | |
if (op[0] > 31) /* lnt > 31? */ | |
RSVD_OPND_FAULT; | |
t = Read ((op[1] + (op[0] / 2)) & LMASK, L_BYTE, RA) & 0xF; | |
if ((t == 0xB) || (t == 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 (cc, (sign << ED_V_SIGN) | (fill << ED_V_FILL)); | |
/* delta PC, cc, 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) /* end? */ | |
break; | |
if (pop & EO_RPT_FLAG) { /* repeat class? */ | |
rpt = pop & EO_RPT_MASK; /* isolate count */ | |
if (rpt == 0) /* can't be zero */ | |
RSVD_OPND_FAULT; | |
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); | |
R[3]++; | |
break; | |
case EO_LOAD_PLUS: /* load sign if + */ | |
if (!(cc & CC_N)) | |
sign = edit_read_sign (acc); | |
R[3]++; | |
break; | |
case EO_LOAD_MINUS: /* load sign if - */ | |
if (cc & CC_N) | |
sign = edit_read_sign (acc); | |
R[3]++; | |
break; | |
case EO_INSERT: /* insert char */ | |
c = Read ((R[3] + 1) & LMASK, L_BYTE, RA); | |
Write (R[5], ((cc & CC_C)? c: fill), 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; | |
if (cc & CC_Z) { /* zero? */ | |
do { /* repeat and blank */ | |
Write ((R[5] - t) & LMASK, fill, L_BYTE, WA); | |
} while (--t); | |
} | |
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 */ | |
for (i = 0; i < (R[0] - t); i++) { /* loop thru src */ | |
d = edit_read_src (i, acc); /* get nibble */ | |
if (d) | |
cc = (cc | CC_V | CC_C) & ~CC_Z; | |
} /* end for */ | |
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: | |
for (i = 0; i < rpt; i++) { /* for repeat */ | |
d = edit_read_src (i, acc); /* get nibble */ | |
if (d) /* test for non-zero */ | |
cc = (cc | CC_C) & ~CC_Z; | |
c = (cc & CC_C)? (d | 0x30): fill; /* test for signif */ | |
Write ((R[5] + i) & LMASK, c, L_BYTE, WA); | |
} /* end for */ | |
edit_adv_src (rpt); /* advance src */ | |
R[5] = (R[5] + rpt) & LMASK; /* advance dst */ | |
break; | |
case EO_FLOAT: | |
for (i = j = 0; i < rpt; i++, j++) { /* for repeat */ | |
d = edit_read_src (i, acc); /* get nibble */ | |
if (d && !(cc & CC_C)) { /* nz, signif clear? */ | |
Write ((R[5] + j) & LMASK, sign, L_BYTE, WA); | |
cc = (cc | CC_C) & ~CC_Z; /* set signif */ | |
j++; /* extra dst char */ | |
} /* end if */ | |
c = (cc & CC_C)? (d | 0x30): fill; /* test for signif */ | |
Write ((R[5] + j) & LMASK, c, L_BYTE, WA); | |
} /* end for */ | |
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 */ | |
R[2] = ED_PUTCC (R[2], cc); /* update cc's */ | |
} /* end for pattern */ | |
if (R[0]) /* pattern too short */ | |
RSVD_OPND_FAULT; | |
PSL = PSL & ~PSL_FPD; /* clear FPD */ | |
if (cc & CC_Z) /* zero? clear n */ | |
cc = cc & ~CC_N; | |
if ((cc & CC_V) && (PSL & PSW_DV)) /* overflow & trap enabled? */ | |
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)) *//* check lo digit */ | |
/* RSVD_OPND_FAULT; */ | |
src->val[i / 4] = src->val[i / 4] | (c << ((i % 4) * 8)); | |
} /* end for */ | |
if ((t == 0xB) || (t == 0xD)) /* if -, set sign */ | |
src->sign = 1; | |
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) /* at limit, get mask */ | |
mask = masktab[lnt % 8]; | |
else if (i > limit) /* beyond, all ovflo */ | |
mask = 0xFFFFFFFF; | |
if (dst->val[i] & mask) /* test for ovflo */ | |
pslv = 1; | |
dst->val[i] = dst->val[i] & ~mask; /* clr digits past end */ | |
if (dst->val[i]) /* test nz */ | |
pslz = 0; | |
} | |
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; | |
if (nz == 0) | |
return 0; | |
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 >= sc) | |
dsrc->val[i] = dsrc->val[i - sc]; | |
else { | |
c |= dsrc->val[i]; | |
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, nc; | |
if ((s = sc * 4)) { | |
for (i = DSTRMAX; i >= 0; i--) { | |
nc = (dsrc->val[i] << (32 - s)) & LMASK; | |
dsrc->val[i] = ((dsrc->val[i] >> s) | | |
cin) & LMASK; | |
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, nc; | |
if ((s = sc * 4)) { | |
for (i = 0; i < DSTRLNT; i++) { | |
nc = dsrc->val[i] >> (32 - s); | |
dsrc->val[i] = ((dsrc->val[i] << s) | | |
cin) & LMASK; | |
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) /* more? return 0 */ | |
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) /* more to do? */ | |
return; | |
inc = (R[0] >> 16) & 0x1F; /* get excess */ | |
if (inc == 0) /* more to do? */ | |
return; | |
} | |
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 */ | |
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; | |
/* 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 |