/* pdp11_cis.c: PDP-11 CIS optional instruction set simulator | |
Copyright (c) 1993-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. | |
This module simulates the PDP-11 commercial instruction set (CIS). | |
16-Oct-08 RMS Fixed overflow bug in ASHx (Word/NibbleLShift) | |
Fixed bug in DIVx (LntDstr calculation) | |
30-May-06 RMS Added interrupt tests to character instructions | |
Added 11/44 stack probe test to MOVCx (only) | |
22-May-06 RMS Fixed bug in decode table (John Dundas) | |
Fixed bug in ASHP (John Dundas) | |
Fixed bug in write decimal string with mmgt enabled | |
Fixed bug in 0-length strings in multiply/divide | |
16-Sep-04 RMS Fixed bug in CMPP/N of negative strings | |
17-Oct-02 RMS Fixed compiler warning (Hans Pufal) | |
08-Oct-02 RMS Fixed macro definitions | |
The commercial instruction set consists of three instruction formats: | |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ register operands | |
| 0 1 1 1 1 1| 0 0 0 0| opcode | 076030:076057 | |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 076070:076077 | |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ inline operands | |
| 0 1 1 1 1 1| 0 0 0 1| opcode | 076130:076157 | |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 076170:076177 | |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ load descriptors | |
| 0 1 1 1 1 1| 0 0 0 0|op| 1 0| reg | 076020:076027 | |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 076060:076067 | |
The CIS instructions operate on character strings, packed (decimal) | |
strings, and numeric (decimal) strings. Strings are described by | |
a two word descriptor: | |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | |
| length in bytes | char string | |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ descriptor | |
| starting byte address | | |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | |
| |str type| | length | decimal string | |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ descriptor | |
| starting byte address | | |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | |
Decimal string types are: | |
<14:12> data type bytes occupied by n digits | |
0 signed zoned n | |
1 unsigned zone n | |
2 trailing overpunch n | |
3 leading overpunch n | |
4 trailing separate n+1 | |
5 leading separate n+1 | |
6 signed packed n/2 +1 | |
7 unsigned packed n/2 +1 | |
Zero length character strings occupy no memory; zero length decimal strings | |
require either zero bytes (zoned, overpunch) or one byte (separate, packed). | |
CIS instructions can run for a very long time, so they are interruptible | |
and restartable. In the simulator, all instructions run to completion. | |
The code is unoptimized. | |
*/ | |
#include "pdp11_defs.h" | |
/* Opcode bits */ | |
#define INLINE 0100 /* inline */ | |
#define PACKED 0020 /* packed */ | |
#define NUMERIC 0000 /* numeric */ | |
/* Interrupt test latency */ | |
#define INT_TEST 100 | |
/* Operand type definitions */ | |
#define R0_DESC 1 /* descr in R0:R1 */ | |
#define R2_DESC 2 /* descr in R2:R3 */ | |
#define R4_DESC 3 /* descr in R4:R5 */ | |
#define R4_ARG 4 /* argument in R4 */ | |
#define IN_DESC 5 /* inline descriptor */ | |
#define IN_ARG 6 /* inline argument */ | |
#define MAXOPN 4 /* max # operands */ | |
/* Decimal data type definitions */ | |
#define XZ 0 /* signed zoned */ | |
#define UZ 1 /* unsigned zoned */ | |
#define TO 2 /* trailing overpunch */ | |
#define LO 3 /* leading overpunch */ | |
#define TS 4 /* trailing separate */ | |
#define LS 5 /* leading separate */ | |
#define XP 6 /* signed packed */ | |
#define UP 7 /* unsigned packed */ | |
/* Decimal descriptor definitions */ | |
#define DTYP_M 07 /* type mask */ | |
#define DTYP_V 12 /* type position */ | |
#define DLNT_M 037 /* length mask */ | |
#define DLNT_V 0 /* length position */ | |
#define GET_DTYP(x) (((x) >> DTYP_V) & DTYP_M) | |
#define GET_DLNT(x) (((x) >> DLNT_V) & DLNT_M) | |
/* Shift operand definitions */ | |
#define ASHRND_M 017 /* round digit mask */ | |
#define ASHRND_V 8 /* round digit pos */ | |
#define ASHLNT_M 0377 /* shift count mask */ | |
#define ASHLNT_V 0 /* shift length pos */ | |
#define ASHSGN 0200 /* shift sign */ | |
#define GET_ASHRND(x) (((x) >> ASHRND_V) & ASHRND_M) | |
#define GET_ASHLNT(x) (((x) >> ASHLNT_V) & ASHLNT_M) | |
/* Operand array aliases */ | |
#define A1LNT arg[0] | |
#define A1ADR arg[1] | |
#define A2LNT arg[2] | |
#define A2ADR arg[3] | |
#define A3LNT arg[4] | |
#define A3ADR arg[5] | |
#define A1 &arg[0] | |
#define A2 &arg[2] | |
#define A3 &arg[4] | |
/* Condition code macros */ | |
#define GET_BIT(ir,n) (((ir) >> (n)) & 1) | |
#define GET_SIGN_L(ir) GET_BIT((ir), 31) | |
#define GET_SIGN_W(ir) GET_BIT((ir), 15) | |
#define GET_SIGN_B(ir) GET_BIT((ir), 7) | |
#define GET_Z(ir) ((ir) == 0) | |
/* Decimal string structure */ | |
#define DSTRLNT 4 | |
#define DSTRMAX (DSTRLNT - 1) | |
#define MAXDVAL 429496730 /* 2^32 / 10 */ | |
typedef struct { | |
uint32 sign; | |
uint32 val[DSTRLNT]; | |
} DSTR; | |
static DSTR Dstr0 = { 0, {0, 0, 0, 0} }; | |
extern int32 isenable, dsenable; | |
extern int32 N, Z, V, C, fpd, ipl; | |
extern int32 R[8], trap_req; | |
extern uint32 cpu_type; | |
int32 ReadDstr (int32 *dscr, DSTR *dec, int32 flag); | |
void WriteDstr (int32 *dscr, DSTR *dec, int32 flag); | |
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); | |
int32 LntDstr (DSTR *dsrc, int32 nz); | |
uint32 NibbleLshift (DSTR *dsrc, int32 sc); | |
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]); | |
t_bool cis_int_test (int32 cycles, int32 oldpc, t_stat *st); | |
int32 movx_setup (int32 op, int32 *arg); | |
void movx_cleanup (int32 op); | |
extern int32 ReadW (int32 addr); | |
extern void WriteW (int32 data, int32 addr); | |
extern int32 ReadB (int32 addr); | |
extern int32 ReadMB (int32 addr); | |
extern void WriteB (int32 data, int32 addr); | |
extern int32 calc_ints (int32 nipl, int32 trq); | |
/* Table of instruction operands */ | |
static int32 opntab[128][MAXOPN] = { | |
{0, 0, 0, 0}, {0, 0, 0, 0}, /* 000 - 007 */ | |
{0, 0, 0, 0}, {0, 0, 0, 0}, | |
{0, 0, 0, 0}, {0, 0, 0, 0}, | |
{0, 0, 0, 0}, {0, 0, 0, 0}, | |
{0, 0, 0, 0}, {0, 0, 0, 0}, /* 010 - 017 */ | |
{0, 0, 0, 0}, {0, 0, 0, 0}, | |
{0, 0, 0, 0}, {0, 0, 0, 0}, | |
{0, 0, 0, 0}, {0, 0, 0, 0}, | |
{0, 0, 0, 0}, {0, 0, 0, 0}, /* LD2R */ | |
{0, 0, 0, 0}, {0, 0, 0, 0}, | |
{0, 0, 0, 0}, {0, 0, 0, 0}, | |
{0, 0, 0, 0}, {0, 0, 0, 0}, | |
{0, 0, 0, 0}, /* MOVC */ | |
{0, 0, 0, 0}, /* MOVRC */ | |
{0, 0, 0, 0}, /* MOVTC */ | |
{0, 0, 0, 0}, /* 033 */ | |
{0, 0, 0, 0}, {0, 0, 0, 0}, /* 034 - 037 */ | |
{0, 0, 0, 0}, {0, 0, 0, 0}, | |
{0, 0, 0, 0}, /* LOCC */ | |
{0, 0, 0, 0}, /* SKPC */ | |
{0, 0, 0, 0}, /* SCANC */ | |
{0, 0, 0, 0}, /* SPANC */ | |
{0, 0, 0, 0}, /* CMPC */ | |
{0, 0, 0, 0}, /* MATC */ | |
{0, 0, 0, 0}, {0, 0, 0, 0}, /* 046 - 047 */ | |
{R0_DESC, R2_DESC, R4_DESC, 0}, /* ADDN */ | |
{R0_DESC, R2_DESC, R4_DESC, 0}, /* SUBN */ | |
{R0_DESC, R2_DESC, 0, 0}, /* CMPN */ | |
{R0_DESC, 0, 0, 0}, /* CVTNL */ | |
{R0_DESC, R2_DESC, 0, 0}, /* CVTPN */ | |
{R0_DESC, R2_DESC, 0, 0}, /* CVTNP */ | |
{R0_DESC, R2_DESC, R4_ARG, 0}, /* ASHN */ | |
{R0_DESC, 0, 0, 0}, /* CVTLN */ | |
{0, 0, 0, 0}, {0, 0, 0, 0}, /* LD3R */ | |
{0, 0, 0, 0}, {0, 0, 0, 0}, | |
{0, 0, 0, 0}, {0, 0, 0, 0}, | |
{0, 0, 0, 0}, {0, 0, 0, 0}, | |
{R0_DESC, R2_DESC, R4_DESC, 0}, /* ADDP */ | |
{R0_DESC, R2_DESC, R4_DESC, 0}, /* SUBP */ | |
{R0_DESC, R2_DESC, 0, 0}, /* CMPP */ | |
{R0_DESC, 0, 0, 0}, /* CVTPL */ | |
{R0_DESC, R2_DESC, R4_DESC, 0}, /* MULP */ | |
{R0_DESC, R2_DESC, R4_DESC, 0}, /* DIVP */ | |
{R0_DESC, R2_DESC, R4_ARG, 0}, /* ASHP */ | |
{R0_DESC, 0, 0, 0}, /* CVTLP */ | |
{0, 0, 0, 0}, {0, 0, 0, 0}, /* 100 - 107 */ | |
{0, 0, 0, 0}, {0, 0, 0, 0}, | |
{0, 0, 0, 0}, {0, 0, 0, 0}, | |
{0, 0, 0, 0}, {0, 0, 0, 0}, | |
{0, 0, 0, 0}, {0, 0, 0, 0}, /* 110 - 117 */ | |
{0, 0, 0, 0}, {0, 0, 0, 0}, | |
{0, 0, 0, 0}, {0, 0, 0, 0}, | |
{0, 0, 0, 0}, {0, 0, 0, 0}, | |
{0, 0, 0, 0}, {0, 0, 0, 0}, /* 120 - 127 */ | |
{0, 0, 0, 0}, {0, 0, 0, 0}, | |
{0, 0, 0, 0}, {0, 0, 0, 0}, | |
{0, 0, 0, 0}, {0, 0, 0, 0}, | |
{IN_DESC, IN_DESC, IN_ARG, 0}, /* MOVCI */ | |
{IN_DESC, IN_DESC, IN_ARG, 0}, /* MOVRCI */ | |
{IN_DESC, IN_DESC, IN_ARG, IN_ARG}, /* MOVTCI */ | |
{0, 0, 0, 0}, /* 133 */ | |
{0, 0, 0, 0}, {0, 0, 0, 0}, /* 134 - 137 */ | |
{0, 0, 0, 0}, {0, 0, 0, 0}, | |
{IN_DESC, IN_ARG, 0, 0}, /* LOCCI */ | |
{IN_DESC, IN_ARG, 0, 0}, /* SKPCI */ | |
{IN_DESC, IN_DESC, 0, 0}, /* SCANCI */ | |
{IN_DESC, IN_DESC, 0, 0}, /* SPANCI */ | |
{IN_DESC, IN_DESC, IN_ARG, 0}, /* CMPCI */ | |
{IN_DESC, IN_DESC, 0, 0}, /* MATCI */ | |
{0, 0, 0, 0}, {0, 0, 0, 0}, /* 146 - 147 */ | |
{IN_DESC, IN_DESC, IN_DESC, 0}, /* ADDNI */ | |
{IN_DESC, IN_DESC, IN_DESC, 0}, /* SUBNI */ | |
{IN_DESC, IN_DESC, 0, 0}, /* CMPNI */ | |
{IN_DESC, IN_ARG, 0, 0}, /* CVTNLI */ | |
{IN_DESC, IN_DESC, 0, 0}, /* CVTPNI */ | |
{IN_DESC, IN_DESC, 0, 0}, /* CVTNPI */ | |
{IN_DESC, IN_DESC, IN_ARG, 0}, /* ASHNI */ | |
{IN_DESC, IN_DESC, 0, 0}, /* CVTLNI */ | |
{0, 0, 0, 0}, {0, 0, 0, 0}, /* 160 - 167 */ | |
{0, 0, 0, 0}, {0, 0, 0, 0}, | |
{0, 0, 0, 0}, {0, 0, 0, 0}, | |
{0, 0, 0, 0}, {0, 0, 0, 0}, | |
{IN_DESC, IN_DESC, IN_DESC, 0}, /* ADDPI */ | |
{IN_DESC, IN_DESC, IN_DESC, 0}, /* SUBPI */ | |
{IN_DESC, IN_DESC, 0, 0}, /* CMPPI */ | |
{IN_DESC, IN_ARG, 0, 0}, /* CVTPLI */ | |
{IN_DESC, IN_DESC, IN_DESC, 0}, /* MULPI */ | |
{IN_DESC, IN_DESC, IN_DESC, 0}, /* DIVPI */ | |
{IN_DESC, IN_DESC, IN_ARG, 0}, /* ASHPI */ | |
{IN_DESC, IN_DESC, 0, 0} /* CVTLPI */ | |
}; | |
/* ASCII to overpunch table: sign is <7>, digit is <4:0> */ | |
static int32 overbin[128] = { | |
0, 0, 0, 0, 0, 0, 0, 0, /* 000 - 037 */ | |
0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0x80, 0, 0, 0, 0, 0, 0, /* 040 - 077 */ | |
0, 0, 0, 0, 0, 0, 0, 0, | |
0, 1, 2, 3, 4, 5, 6, 7, | |
8, 9, 0x80, 0, 0, 0, 0, 0, | |
0, 1, 2, 3, 4, 5, 6, 7, /* 100 - 137 */ | |
8, 9, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, | |
0x87, 0x88, 0x89, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0x80, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, /* 140 - 177 */ | |
0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0x80, 0, 0 | |
}; | |
/* Overpunch to ASCII table: indexed by sign and digit */ | |
static int32 binover[2][16] = { | |
{'{', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', | |
'0', '0', '0', '0', '0', '0'}, | |
{'}', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', | |
'0', '0', '0', '0', '0', '0'} | |
}; | |
/* CIS emulator */ | |
t_stat cis11 (int32 IR) | |
{ | |
int32 c, i, j, t, op, rn, addr; | |
int32 match, limit, mvlnt, shift; | |
int32 spc, ldivd, ldivr; | |
int32 arg[6]; /* operands */ | |
int32 old_PC; | |
uint32 nc, digit, result; | |
t_stat st; | |
static DSTR accum, src1, src2, dst; | |
static DSTR mptable[10]; | |
static DSTR Dstr1 = { 0, {0x10, 0, 0, 0} }; | |
old_PC = (PC - 2) & 0177777; /* original PC */ | |
op = IR & 0177; /* IR <6:0> */ | |
for (i = j = 0; (i < MAXOPN) && opntab[op][i]; i++) { /* parse operands */ | |
switch (opntab[op][i]) { /* case on op type */ | |
case R0_DESC: | |
arg[j++] = R[0]; | |
arg[j++] = R[1]; | |
break; | |
case R2_DESC: | |
arg[j++] = R[2]; | |
arg[j++] = R[3]; | |
break; | |
case R4_DESC: | |
arg[j++] = R[4]; | |
arg[j++] = R[5]; | |
break; | |
case R4_ARG: | |
arg[j++] = R[4]; | |
break; | |
case IN_DESC: | |
addr = ReadW (PC | isenable); | |
PC = (PC + 2) & 0177777; | |
arg[j++] = ReadW (addr | dsenable); | |
arg[j++] = ReadW (((addr + 2) & 0177777) | dsenable); | |
break; | |
case IN_ARG: | |
arg[j++] = ReadW (PC | isenable); | |
PC = (PC + 2) & 0177777; | |
break; | |
default: | |
return SCPE_IERR; | |
} /* end case */ | |
} /* end for */ | |
switch (op) { /* case on opcode */ | |
/* MOVC, MOVTC, MOVCI, MOVTCI | |
Operands (MOVC, MOVTC): | |
R0, R1 = source string descriptor | |
R2, R3 = dest string descriptor | |
R4<7:0> = fill character | |
R5 = translation table address (MOVTC only) | |
Operands (MOVCI, MOVTCI): | |
A1LNT, A1ADR = source string descriptor | |
A2LNT, A2ADR = dest string descriptor | |
A3LNT<7:0> = fill character | |
A3ADR = translation table address (MOVTCI only) | |
Condition codes: | |
NZVC = set from src.lnt - dst.lnt | |
Registers (MOVC, MOVTC only) | |
R0 = max (0, src.len - dst.len) | |
R1:R3 = 0 | |
R4:R5 = unchanged | |
Notes: | |
- If either the source or destination lengths are zero, | |
the move loops exit immediately. | |
- If the source length does not exceed the destination | |
length, the fill loop exits immediately. | |
*/ | |
case 030: case 032: case 0130: case 0132: | |
if (!fpd) { /* first time? */ | |
mvlnt = movx_setup (op, arg); /* set up reg */ | |
if (R[1] < R[3]) { /* move backwards? */ | |
R[1] = (R[1] + mvlnt) & 0177777; /* bias addresses */ | |
R[3] = (R[3] + mvlnt) & 0177777; | |
} | |
} | |
/* At this point, | |
R0-R5 = arguments | |
M[SP] = move length */ | |
if (R[0] && R[2]) { /* move to do? */ | |
if (R[1] < R[3]) { /* backwards? */ | |
for (i = 0; R[0] && R[2]; ) { /* move loop */ | |
t = ReadB (((R[1] - 1) & 0177777) | dsenable); | |
if (op & 2) | |
t = ReadB (((R[5] + t) & 0177777) | dsenable); | |
WriteB (t, ((R[3] - 1) & 0177777) | dsenable); | |
R[0]--; | |
R[1] = (R[1] - 1) & 0177777; | |
R[2]--; | |
R[3] = (R[3] - 1) & 0177777; | |
if ((++i >= INT_TEST) && R[0] && R[2]) { | |
if (cis_int_test (i, old_PC, &st)) | |
return st; | |
i = 0; | |
} | |
} /* end for lnts */ | |
mvlnt = ReadW (SP | dsenable); /* recover mvlnt */ | |
R[3] = (R[3] + mvlnt) & 0177777; /* end of dst str */ | |
} /* end if bkwd */ | |
else { /* forward */ | |
for (i = 0; R[0] && R[2]; ) { /* move loop */ | |
t = ReadB ((R[1] & 0177777) | dsenable); | |
if (op & 2) | |
t = ReadB (((R[5] + t) & 0177777) | dsenable); | |
WriteB (t, (R[3] & 0177777) | dsenable); | |
R[0]--; | |
R[1] = (R[1] + 1) & 0177777; | |
R[2]--; | |
R[3] = (R[3] + 1) & 0177777; | |
if ((++i >= INT_TEST) && R[0] && R[2]) { | |
if (cis_int_test (i, old_PC, &st)) | |
return st; | |
i = 0; | |
} | |
} /* end for lnts */ | |
} /* end else fwd */ | |
} /* end if move */ | |
for (i = 0; i < R[2]; i++) { | |
WriteB (R[4], ((R[3] + i) & 0177777) | dsenable); | |
} | |
movx_cleanup (op); /* cleanup */ | |
return SCPE_OK; | |
/* MOVRC, MOVRCI | |
Operands (MOVC, MOVTC): | |
R0, R1 = source string descriptor | |
R2, R3 = dest string descriptor | |
R4<7:0> = fill character | |
Operands (MOVCI, MOVTCI): | |
A1LNT, A1ADR = source string descriptor | |
A2LNT, A2ADR = dest string descriptor | |
A3LNT<7:0> = fill character | |
Condition codes: | |
NZVC = set from src.lnt - dst.lnt | |
Registers (MOVRC only) | |
R0 = max (0, src.len - dst.len) | |
R1:R3 = 0 | |
R4:R5 = unchanged | |
Notes: see MOVC, MOVCI | |
*/ | |
case 031: case 0131: | |
if (!fpd) { /* first time? */ | |
mvlnt = movx_setup (op, arg); /* set up reg */ | |
R[1] = (R[1] + R[0] - mvlnt) & 0177777; /* eff move start */ | |
R[3] = (R[3] + R[2] - mvlnt) & 0177777; | |
if (R[1] < R[3]) { /* move backwards? */ | |
R[1] = (R[1] + mvlnt) & 0177777; /* bias addresses */ | |
R[3] = (R[3] + mvlnt) & 0177777; | |
} | |
} | |
/* At this point, | |
R0-R5 = arguments | |
M[SP] = move length */ | |
if (R[0] && R[2]) { /* move to do? */ | |
if (R[1] < R[3]) { /* backwards? */ | |
for (i = 0; R[0] && R[2]; ) { /* move loop */ | |
t = ReadB (((R[1] - 1) & 0177777) | dsenable); | |
WriteB (t, ((R[3] - 1) & 0177777) | dsenable); | |
R[0]--; | |
R[1] = (R[1] - 1) & 0177777; | |
R[2]--; | |
R[3] = (R[3] - 1) & 0177777; | |
if ((++i >= INT_TEST) && R[0] && R[2]) { | |
if (cis_int_test (i, old_PC, &st)) | |
return st; | |
i = 0; | |
} | |
} /* end for lnts */ | |
} /* end if bkwd */ | |
else { /* forward */ | |
for (i = 0; R[0] && R[2]; ) { /* move loop */ | |
t = ReadB ((R[1] & 0177777) | dsenable); | |
WriteB (t, (R[3] & 0177777) | dsenable); | |
R[0]--; | |
R[1] = (R[1] + 1) & 0177777; | |
R[2]--; | |
R[3] = (R[3] + 1) & 0177777; | |
if ((++i >= INT_TEST) && R[0] && R[2]) { | |
if (cis_int_test (i, old_PC, &st)) | |
return st; | |
i = 0; | |
} | |
} /* end for lnts */ | |
mvlnt = ReadW (SP | dsenable); /* recover mvlnt */ | |
R[3] = (R[3] - mvlnt) & 0177777; /* start of dst str */ | |
} /* end else fwd */ | |
} /* end if move */ | |
for (i = 0; i < R[2]; i++) { | |
WriteB (R[4], ((R[3] - R[2] + i) & 0177777) | dsenable); | |
} | |
movx_cleanup (op); /* cleanup */ | |
return SCPE_OK; | |
/* Load descriptors - no operands */ | |
case 020: case 021: case 022: case 023: | |
case 024: case 025: case 026: case 027: | |
case 060: case 061: case 062: case 063: | |
case 064: case 065: case 066: case 067: | |
limit = (op & 040)? 6: 4; | |
rn = IR & 07; /* get register */ | |
t = R[rn]; | |
spc = (rn == 7)? isenable: dsenable; | |
for (j = 0; j < limit; j = j + 2) { /* loop for 2,3 dscr */ | |
addr = ReadW (((t + j) & 0177777) | spc); | |
R[j] = ReadW (addr | dsenable); | |
R[j + 1] = ReadW (((addr + 2) & 0177777) | dsenable); | |
} | |
if (rn >= limit) | |
R[rn] = (R[rn] + limit) & 0177777; | |
return SCPE_OK; | |
/* LOCC, SKPC, LOCCI, SKPCI | |
Operands (LOCC, SKPC): | |
R0, R1 = source string descriptor | |
R4<7:0> = match character | |
Operands (LOCCI, SKPCI): | |
A1LNT, A1ADR = source string descriptor | |
A2LNT<7:0> = match character | |
Condition codes: | |
NZ = set from R0 | |
VC = 0 | |
Registers: | |
R0:R1 = substring descriptor where operation terminated | |
*/ | |
case 0140: case 0141: /* inline */ | |
if (!fpd) { /* FPD clear? */ | |
WriteW (R[4], ((SP - 2) & 0177777) | dsenable); | |
SP = (SP - 2) & 0177777; /* push R4 */ | |
R[0] = A1LNT; /* args to registers */ | |
R[1] = A1ADR; | |
R[4] = A2LNT; | |
} /* fall through */ | |
case 040: case 041: /* register */ | |
fpd = 1; /* set FPD */ | |
R[4] = R[4] & 0377; /* match character */ | |
for (i = 0; R[0] != 0;) { /* loop */ | |
c = ReadB (R[1] | dsenable); /* get char */ | |
if ((c == R[4]) ^ (op & 1)) /* = + LOC, != + SKP? */ | |
break; | |
R[0]--; /* decr count, */ | |
R[1] = (R[1] + 1) & 0177777; /* incr addr */ | |
if ((++i >= INT_TEST) && R[0]) { /* test for intr? */ | |
if (cis_int_test (i, old_PC, &st)) | |
return st; | |
i = 0; | |
} | |
} | |
N = GET_SIGN_W (R[0]); | |
Z = GET_Z (R[0]); | |
V = C = 0; | |
fpd = 0; /* instr done */ | |
if (op & INLINE) { /* inline? */ | |
R[4] = ReadW (SP | dsenable); /* restore R4 */ | |
SP = (SP + 2) & 0177777; | |
} | |
return SCPE_OK; | |
/* SCANC, SPANC, SCANCI, SPANCI | |
Operands (SCANC, SPANC): | |
R0, R1 = source string descriptor | |
R4<7:0> = mask | |
R5 = table address | |
Operands (SCANCI, SPANCI): | |
A1LNT, A1ADR = source string descriptor | |
A2LNT<7:0> = match character | |
A2ADR = table address | |
Condition codes: | |
NZ = set from R0 | |
VC = 0 | |
Registers: | |
R0:R1 = substring descriptor where operation terminated | |
*/ | |
case 0142: case 0143: /* inline */ | |
if (!fpd) { /* FPD clear? */ | |
WriteW (R[4], ((SP - 4) & 0177777) | dsenable); | |
WriteW (R[5], ((SP - 2) & 0177777) | dsenable); | |
SP = (SP - 4) & 0177777; /* push R4, R5 */ | |
R[0] = A1LNT; /* args to registers */ | |
R[1] = A1ADR; | |
R[4] = A2LNT; | |
R[5] = A2ADR; | |
} /* fall through */ | |
case 042: case 043: /* register */ | |
fpd = 1; /* set FPD */ | |
R[4] = R[4] & 0377; /* match character */ | |
for (i = 0; R[0] != 0;) { /* loop */ | |
t = ReadB (R[1] | dsenable); /* get char as index */ | |
c = ReadB (((R[5] + t) & 0177777) | dsenable); | |
if (((c & R[4]) != 0) ^ (op & 1)) /* != + SCN, = + SPN? */ | |
break; | |
R[0]--; /* decr count, */ | |
R[1] = (R[1] + 1) & 0177777; /* incr addr */ | |
if ((++i >= INT_TEST) && R[0]) { /* test for intr? */ | |
if (cis_int_test (i, old_PC, &st)) | |
return st; | |
i = 0; | |
} | |
} | |
N = GET_SIGN_W (R[0]); | |
Z = GET_Z (R[0]); | |
V = C = 0; | |
fpd = 0; /* instr done */ | |
if (op & INLINE) { /* inline? */ | |
R[4] = ReadW (SP | dsenable); /* restore R4, R5 */ | |
R[5] = ReadW (((SP + 2) & 0177777) | dsenable); | |
SP = (SP + 4) & 0177777; | |
} | |
return SCPE_OK; | |
/* CMPC, CMPCI | |
Operands (CMPC): | |
R0, R1 = source1 string descriptor | |
R2, R3 = source2 string descriptor | |
R4<7:0> = fill character | |
Operands (CMPCI): | |
A1LNT, A1ADR = source1 string descriptor | |
A2LNT, A2ADR = source2 string descriptor | |
A3LNT<7:0> = fill character | |
Condition codes: | |
NZVC = set from src1 - src2 at mismatch, or | |
= 0100 if equal | |
Registers (CMPC only): | |
R0:R1 = unmatched source1 substring descriptor | |
R2:R3 = unmatched source2 substring descriptor | |
*/ | |
case 0144: /* inline */ | |
if (!fpd) { /* FPD clear? */ | |
WriteW (R[0], ((SP - 10) & 0177777) | dsenable); | |
WriteW (R[1], ((SP - 8) & 0177777) | dsenable); | |
WriteW (R[2], ((SP - 6) & 0177777) | dsenable); | |
WriteW (R[3], ((SP - 4) & 0177777) | dsenable); | |
WriteW (R[4], ((SP - 2) & 0177777) | dsenable); | |
SP = (SP - 10) & 0177777; /* push R0 - R4 */ | |
R[0] = A1LNT; /* args to registers */ | |
R[1] = A1ADR; | |
R[2] = A2LNT; | |
R[3] = A2ADR; | |
R[4] = A3LNT; | |
} /* fall through */ | |
case 044: /* register */ | |
fpd = 1; /* set FPD */ | |
R[4] = R[4] & 0377; /* mask fill */ | |
c = t = 0; | |
for (i = 0; (R[0] || R[2]); ) { /* until cnts == 0 */ | |
if (R[0]) /* get src1 or fill */ | |
c = ReadB (R[1] | dsenable); | |
else c = R[4]; | |
if (R[2]) /* get src2 or fill */ | |
t = ReadB (R[3] | dsenable); | |
else t = R[4]; | |
if (c != t) /* if diff, done */ | |
break; | |
if (R[0]) { /* if more src1 */ | |
R[0]--; /* decr count, */ | |
R[1] = (R[1] + 1) & 0177777; /* incr addr */ | |
} | |
if (R[2]) { /* if more src2 */ | |
R[2]--; /* decr count, */ | |
R[3] = (R[3] + 1) & 0177777; /* incr addr */ | |
} | |
if ((++i >= INT_TEST) && (R[0] || R[2])) { /* test for intr? */ | |
if (cis_int_test (i, old_PC, &st)) | |
return st; | |
i = 0; | |
} | |
} | |
j = c - t; /* last chars read */ | |
N = GET_SIGN_B (j); /* set cc's */ | |
Z = GET_Z (j); | |
V = GET_SIGN_B ((c ^ t) & (~t ^ j)); | |
C = (c < t); | |
fpd = 0; /* instr done */ | |
if (op & INLINE) { /* inline? */ | |
R[0] = ReadW (SP | dsenable); /* restore R0 - R4 */ | |
R[1] = ReadW (((SP + 2) & 0177777) | dsenable); | |
R[2] = ReadW (((SP + 4) & 0177777) | dsenable); | |
R[3] = ReadW (((SP + 6) & 0177777) | dsenable); | |
R[4] = ReadW (((SP + 8) & 0177777) | dsenable); | |
SP = (SP + 10) & 0177777; | |
} | |
return SCPE_OK; | |
/* MATC, MATCI | |
Operands (MATC): | |
R0, R1 = source string descriptor | |
R2, R3 = substring descriptor | |
Operands (MATCI): | |
A1LNT, A1ADR = source1 string descriptor | |
A2LNT, A2ADR = source2 string descriptor | |
Condition codes: | |
NZ = set from R0 | |
VC = 0 | |
Registers: | |
R0:R1 = source substring descriptor for match | |
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), or "no match" | |
*/ | |
case 0145: /* inline */ | |
if (!fpd) { /* FPD clear? */ | |
WriteW (R[2], ((SP - 4) & 0177777) | dsenable); | |
WriteW (R[3], ((SP - 2) & 0177777) | dsenable); | |
SP = (SP - 4) & 0177777; /* push R2, R3 */ | |
R[0] = A1LNT; /* args to registers */ | |
R[1] = A1ADR; | |
R[2] = A2LNT; | |
R[3] = A2ADR; | |
} /* fall through */ | |
case 0045: /* register */ | |
fpd = 1; | |
for (match = 0; R[0] >= R[2]; ) { /* loop thru string */ | |
for (i = 0, match = 1; match && (i < R[2]); i++) { | |
c = ReadB (((R[1] + i) & 0177777) | dsenable); | |
t = ReadB (((R[3] + i) & 0177777) | dsenable); | |
match = (c == t); /* end for substring */ | |
} | |
if (match) /* exit if match */ | |
break; | |
R[0]--; /* on to next char */ | |
R[1] = (R[1] + 1) & 0177777; | |
if (cis_int_test (i, old_PC, &st)) | |
return st; | |
} | |
if (!match) { /* if no match */ | |
R[1] = (R[1] + R[0]) & 0177777; | |
R[0] = 0; | |
} | |
N = GET_SIGN_W (R[0]); | |
Z = GET_Z (R[0]); | |
V = C = 0; | |
fpd = 0; /* instr done */ | |
if (op & INLINE) { /* inline? */ | |
R[2] = ReadW (SP | dsenable); /* restore R2, R3 */ | |
R[3] = ReadW (((SP + 2) & 0177777) | dsenable); | |
SP = (SP + 4) & 0177777; | |
} | |
return SCPE_OK; | |
/* ADDN, SUBN, ADDP, SUBP, ADDNI, SUBNI, ADDPI, SUBPI | |
Operands: | |
A1LNT, A1ADR = source1 string descriptor | |
A2LNT, A2ADR = source2 string descriptor | |
A3LNT, A3ADR = destination string descriptor | |
Condition codes: | |
NZV = set from result | |
C = 0 | |
Registers (ADDN, ADDP, SUBN, SUBP only): | |
R0:R3 = 0 | |
*/ | |
case 050: case 051: case 070: case 071: | |
case 0150: case 0151: case 0170: case 0171: | |
ReadDstr (A1, &src1, op); /* get source1 */ | |
ReadDstr (A2, &src2, op); /* get source2 */ | |
if (op & 1) /* 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 */ | |
} | |
C = 0; | |
WriteDstr (A3, &dst, op); /* store result */ | |
if ((op & INLINE) == 0) /* if reg, clr reg */ | |
R[0] = R[1] = R[2] = R[3] = 0; | |
return SCPE_OK; | |
/* MULP, MULPI | |
Operands: | |
A1LNT, A1ADR = source1 string descriptor | |
A2LNT, A2ADR = source2 string descriptor | |
A3LNT, A3ADR = destination string descriptor | |
Condition codes: | |
NZV = set from result | |
C = 0 | |
Registers (MULP only): | |
R0:R3 = 0 | |
*/ | |
case 074: case 0174: | |
dst = Dstr0; /* clear result */ | |
if (ReadDstr (A1, &src1, op) && ReadDstr (A2, &src2, op)) { | |
dst.sign = src1.sign ^ src2.sign; /* sign of result */ | |
accum = Dstr0; /* clear accum */ | |
NibbleRshift (&src1, 1, 0); /* shift out sign */ | |
CreateTable (&src1, mptable); /* create *1, *2, ... */ | |
for (i = 1; i < (DSTRLNT * 8); i++) { /* 31 iterations */ | |
digit = (src2.val[i / 8] >> ((i % 8) * 4)) & 0xF; | |
if (digit > 0) /* add in digit*mpcnd */ | |
AddDstr (&mptable[digit], &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 */ | |
C = 0; /* C = 0 */ | |
WriteDstr (A3, &dst, op); /* store result */ | |
if ((op & INLINE) == 0) /* if reg, clr reg */ | |
R[0] = R[1] = R[2] = R[3] = 0; | |
return SCPE_OK; | |
/* DIVP, DIVPI | |
Operands: | |
A1LNT, A1ADR = divisor string descriptor | |
A2LNT, A2ADR = dividend string descriptor | |
A3LNT, A3ADR = destination string descriptor | |
Condition codes: | |
NZV = set from result | |
C = set if divide by zero | |
Registers (DIVP only): | |
R0:R3 = 0 | |
*/ | |
case 075: case 0175: | |
ldivr = ReadDstr (A1, &src1, op); /* get divisor */ | |
if (ldivr == 0) { /* divisor = 0? */ | |
V = C = 1; /* set cc's */ | |
return SCPE_OK; | |
} | |
ldivr = LntDstr (&src1, ldivr); /* get exact length */ | |
ldivd = ReadDstr (A2, &src2, op); /* get dividend */ | |
ldivd = LntDstr (&src2, ldivd); /* get exact length */ | |
dst = Dstr0; /* clear dest */ | |
NibbleRshift (&src1, 1, 0); /* right justify ops */ | |
NibbleRshift (&src2, 1, 0); | |
if ((t = ldivd - ldivr) >= 0) { /* any divide to do? */ | |
WordLshift (&src1, t / 8); /* align divr to divd */ | |
NibbleLshift (&src1, t % 8); | |
CreateTable (&src1, mptable); /* create *1, *2, ... */ | |
for (i = 0; i <= t; i++) { /* divide loop */ | |
for (digit = 9; digit > 0; digit--) { /* find digit */ | |
if (CmpDstr (&src2, &mptable[digit]) >= 0) { | |
SubDstr (&mptable[digit], &src2, &src2); | |
dst.val[0] = dst.val[0] | digit; | |
break; | |
} /* end if */ | |
} /* end for */ | |
NibbleLshift (&src2, 1); /* shift dividend */ | |
NibbleLshift (&dst, 1); /* shift quotient */ | |
} /* end divide loop */ | |
dst.sign = src1.sign ^ src2.sign; /* calculate sign */ | |
} /* end if */ | |
V = C = 0; | |
WriteDstr (A3, &dst, op); /* store result */ | |
if ((op & INLINE) == 0) /* if reg, clr reg */ | |
R[0] = R[1] = R[2] = R[3] = 0; | |
return SCPE_OK; | |
/* CMPN, CMPP, CMPNI, CMPPI | |
Operands: | |
A1LNT, A1ADR = source1 string descriptor | |
A2LNT, A2ADR = source2 string descriptor | |
Condition codes: | |
NZ = set from comparison | |
VC = 0 | |
Registers (CMPN, CMPP only): | |
R0:R3 = 0 | |
*/ | |
case 052: case 072: case 0152: case 0172: | |
ReadDstr (A1, &src1, op); /* get source1 */ | |
ReadDstr (A2, &src2, op); /* get source2 */ | |
N = Z = V = C = 0; | |
if (src1.sign != src2.sign) N = src1.sign; | |
else { | |
t = CmpDstr (&src1, &src2); /* compare strings */ | |
if (t < 0) | |
N = (src1.sign? 0: 1); | |
else if (t > 0) | |
N = (src1.sign? 1: 0); | |
else Z = 1; | |
} | |
if ((op & INLINE) == 0) /* if reg, clr reg */ | |
R[0] = R[1] = R[2] = R[3] = 0; | |
return SCPE_OK; | |
/* ASHN, ASHP, ASHNI, ASHPI | |
Operands: | |
A1LNT, A1ADR = source string descriptor | |
A2LNT, A2ADR = destination string descriptor | |
A3LNT<11:8> = rounding digit | |
A3LNT<7:0> = shift count | |
Condition codes: | |
NZV = set from result | |
C = 0 | |
Registers (ASHN, ASHP only): | |
R0:R1, R4 = 0 | |
*/ | |
case 056: case 076: case 0156: case 0176: | |
ReadDstr (A1, &src1, op); /* get source */ | |
V = C = 0; /* init cc's */ | |
shift = GET_ASHLNT (A3LNT); /* get shift count */ | |
if (shift & ASHSGN) { /* right shift? */ | |
shift = (ASHLNT_M + 1 - shift); /* !shift! */ | |
WordRshift (&src1, shift / 8); /* do word shifts */ | |
NibbleRshift (&src1, shift % 8, 0); /* do nibble shifts */ | |
t = GET_ASHRND (A3LNT); /* get rounding digit */ | |
if ((t + (src1.val[0] & 0xF)) > 9) /* rounding needed? */ | |
AddDstr (&src1, &Dstr1, &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)) /* do nibble shifts */ | |
V = 1; | |
} /* end left shift */ | |
WriteDstr (A2, &src1, op); /* store result */ | |
if ((op & INLINE) == 0) /* if reg, clr reg */ | |
R[0] = R[1] = R[4] = 0; | |
return SCPE_OK; | |
/* CVTPN, CVTPNI | |
Operands: | |
A1LNT, A1ADR = source string descriptor | |
A2LNT, A2ADR = destination string descriptor | |
Condition codes: | |
NZV = set from result | |
C = 0 | |
Registers (CVTPN only): | |
R0:R1 = 0 | |
*/ | |
case 054: case 0154: | |
ReadDstr (A1, &src1, PACKED); /* get source */ | |
V = C = 0; /* init cc's */ | |
WriteDstr (A2, &src1, NUMERIC); /* write dest */ | |
if ((op & INLINE) == 0) /* if reg, clr reg */ | |
R[0] = R[1] = 0; | |
return SCPE_OK; | |
/* CVTNP, CVTNPI | |
Operands: | |
A1LNT, A1ADR = source string descriptor | |
A2LNT, A2ADR = destination string descriptor | |
Condition codes: | |
NZV = set from result | |
C = 0 | |
Registers (CVTNP only): | |
R0:R1 = 0 | |
*/ | |
case 055: case 0155: | |
ReadDstr (A1, &src1, NUMERIC); /* get source */ | |
V = C = 0; /* init cc's */ | |
WriteDstr (A2, &src1, PACKED); /* write dest */ | |
if ((op & INLINE) == 0) /* if reg, clr reg */ | |
R[0] = R[1] = 0; | |
return SCPE_OK; | |
/* CVTNL, CVTPL, CVTNLI, CVTPLI | |
Operands: | |
A1LNT, A1ADR = source string descriptor | |
A2LNT = destination address (inline only) | |
Condition codes: | |
NZV = set from result | |
C = source < 0 and result != 0 | |
Registers (CVTNL, CVTPL only): | |
R0:R1 = 0 | |
R2:R3 = result | |
*/ | |
case 053: case 073: case 0153: case 0173: | |
ReadDstr (A1, &src1, op); /* get source */ | |
V = result = 0; /* clear V, result */ | |
for (i = (DSTRLNT * 8) - 1; i > 0; i--) { /* loop thru digits */ | |
digit = (src1.val[i / 8] >> ((i % 8) * 4)) & 0xF; | |
if (digit || result || V) { /* skip initial 0's */ | |
if (result >= MAXDVAL) | |
V = 1; | |
result = (result * 10) + digit; | |
if (result < digit) | |
V = 1; | |
} /* end if */ | |
} /* end for */ | |
if (src1.sign) | |
result = (~result + 1) & 0xFFFFFFFF; | |
N = GET_SIGN_L (result); | |
Z = GET_Z (result); | |
V = V | (N ^ src1.sign); /* overflow if +2**31 */ | |
C = src1.sign && (Z == 0); /* set C based on std */ | |
if (op & INLINE) { /* inline? */ | |
WriteW (result & 0177777, A2LNT | dsenable); | |
WriteW ((result >> 16) & 0177777, | |
((A2LNT + 2) & 0177777) | dsenable); | |
} | |
else { | |
R[0] = R[1] = 0; | |
R[2] = (result >> 16) & 0177777; | |
R[3] = result & 0177777; | |
} | |
return SCPE_OK; | |
/* CVTLN, CVTLP, CVTLNI, CVTLPI | |
Operands: | |
A1LNT, A1ADR = destination string descriptor | |
A2LNT, A2ADR = source long (CVTLNI, CVTLPI) - VAX format | |
R2:R3 = source long (CVTLN, CVTLP) - EIS format | |
Condition codes: | |
NZV = set from result | |
C = 0 | |
Registers (CVTLN, CVTLP only) | |
R2:R3 = 0 | |
*/ | |
case 057: case 077: | |
result = (R[2] << 16) | R[3]; /* op in EIS format */ | |
R[2] = R[3] = 0; /* clear registers */ | |
goto CVTLx; /* join common code */ | |
case 0157: case 0177: | |
result = (A2ADR << 16) | A2LNT; /* op in VAX format */ | |
CVTLx: | |
dst = Dstr0; /* clear result */ | |
if ((dst.sign = GET_SIGN_L (result))) | |
result = (~result + 1) & 0xFFFFFFFF; | |
for (i = 1; (i < (DSTRLNT * 8)) && result; i++) { | |
digit = result % 10; | |
result = result / 10; | |
dst.val[i / 8] = dst.val[i / 8] | (digit << ((i % 8) * 4)); | |
} | |
V = C = 0; | |
WriteDstr (A1, &dst, op); /* write result */ | |
return SCPE_OK; | |
default: | |
setTRAP (TRAP_ILL); | |
break; | |
} /* end case */ | |
return SCPE_OK; | |
} /* end cis */ | |
/* Get decimal string | |
Arguments: | |
dscr = decimal string descriptor | |
src = decimal string structure | |
flag = numeric/packed flag | |
The routine returns the length in int32's of the non-zero part of | |
the string. | |
This routine plays fast and loose with operand checking, as did the | |
original 11/23 microcode (half of which I wrote). In particular, | |
- If the flag specifies packed, the type is not checked at all. | |
The sign of an unsigned string is assumed to be 0xF (an | |
alternative for +). | |
- If the flag specifies numeric, packed types will be treated | |
as unsigned zoned. | |
- For separate, only the '-' sign is checked, not the '+'. | |
However, to simplify the code elsewhere, digits are range checked, | |
and bad digits are replaced with 0's. | |
*/ | |
int32 ReadDstr (int32 *dscr, DSTR *src, int32 flag) | |
{ | |
int32 c, i, end, lnt, type, t = 0; | |
*src = Dstr0; /* clear result */ | |
type = GET_DTYP (dscr[0]); /* get type */ | |
lnt = GET_DLNT (dscr[0]); /* get string length */ | |
if (flag & PACKED) { /* packed? */ | |
end = lnt / 2; /* last byte */ | |
for (i = 0; i <= end; i++) { /* loop thru string */ | |
c = ReadB (((dscr[1] + end - i) & 0177777) | dsenable); | |
if (i == 0) /* save sign */ | |
t = c & 0xF; | |
if ((i == end) && ((lnt & 1) == 0)) | |
c = c & 0xF; | |
if (c >= 0xA0) /* check hi digit */ | |
c = c & 0xF; | |
if ((c & 0xF) >= 0xA) /* check lo digit */ | |
c = c & 0xF0; | |
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; | |
src->val[0] = src->val[0] & ~0xF; /* clear sign */ | |
} /* end packed */ | |
else { /* numeric */ | |
if (type >= TS) src->sign = (ReadB ((((type == TS)? | |
dscr[1] + lnt: dscr[1] - 1) & 0177777) | dsenable) == '-'); | |
for (i = 1; i <= lnt; i++) { /* loop thru string */ | |
c = ReadB (((dscr[1] + lnt - i) & 0177777) | dsenable); | |
if ((i == 1) && (type == XZ) && ((c & 0xF0) == 0x70)) | |
src->sign = 1; /* signed zoned */ | |
else if (((i == 1) && (type == TO)) || | |
((i == lnt) && (type == LO))) { | |
c = overbin[c & 0177]; /* get sign and digit */ | |
src->sign = c >> 7; /* set sign */ | |
} | |
c = c & 0xF; /* get digit */ | |
if (c > 9) /* range check */ | |
c = 0; | |
src->val[i / 8] = src->val[i / 8] | (c << ((i % 8) * 4)); | |
} /* end for */ | |
} /* end numeric */ | |
return TestDstr (src); /* clean -0 */ | |
} | |
/* Store decimal string | |
Arguments: | |
dsrc = decimal string descriptor | |
src = decimal string structure | |
flag = numeric/packed flag | |
PSW.NZ are also set to their proper values | |
PSW.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, string type | |
is signed, and the result is non-zero or there was overflow | |
- PSW sign is negative if input is negative, string type is | |
signed, and the result is non-zero | |
Thus, the stored sign and the PSW sign will differ in one case: | |
a negative zero generated by overflow is stored with a negative | |
sign, but PSW.N is clear | |
*/ | |
void WriteDstr (int32 *dscr, DSTR *dst, int32 flag) | |
{ | |
int32 c, i, limit, end, type, lnt; | |
uint32 mask; | |
static uint32 masktab[8] = { | |
0xFFFFFFF0, 0xFFFFFF00, 0xFFFFF000, 0xFFFF0000, | |
0xFFF00000, 0xFF000000, 0xF0000000, 0x00000000 | |
}; | |
static int32 unsignedtab[8] = { 0, 1, 0, 0, 0, 0, 0, 1 }; | |
type = GET_DTYP (dscr[0]); /* get type */ | |
lnt = GET_DLNT (dscr[0]); /* get string length */ | |
mask = 0; /* can't ovflo */ | |
Z = 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 */ | |
V = 1; | |
if ((dst->val[i] = dst->val[i] & ~mask)) /* test nz */ | |
Z = 0; | |
} | |
dst->sign = dst->sign & ~unsignedtab[type] & ~(Z & ~V); | |
N = dst->sign & ~Z; /* N = sign, if ~zero */ | |
if (flag & PACKED) { /* packed? */ | |
end = lnt / 2; /* end of string */ | |
if (type == UP) | |
dst->val[0] = dst->val[0] | 0xF; | |
else dst->val[0] = dst->val[0] | 0xC | dst->sign; | |
for (i = 0; i <= end; i++) { /* store string */ | |
c = (dst->val[i / 4] >> ((i % 4) * 8)) & 0xFF; | |
WriteB (c, ((dscr[1] + end - i) & 0177777) | dsenable); | |
} /* end for */ | |
} /* end packed */ | |
else { | |
if (type >= TS) WriteB (dst->sign? '-': '+', (((type == TS)? | |
dscr[1] + lnt: dscr[1] - 1) & 0177777) | dsenable); | |
for (i = 1; i <= lnt; i++) { /* store string */ | |
c = (dst->val[i / 8] >> ((i % 8) * 4)) & 0xF; /* get digit */ | |
if ((i == 1) && (type == XZ) && dst->sign) | |
c = c | 0x70; /* signed zoned */ | |
else if (((i == 1) && (type == TO)) || | |
((i == lnt) && (type == LO))) | |
c = binover[dst->sign][c]; /* get sign and digit */ | |
else c = c | 0x30; /* default */ | |
WriteB (c, ((dscr[1] + lnt - i) & 0177777) |dsenable ); | |
} /* end for */ | |
} /* end numeric */ | |
return; | |
} | |
/* Add decimal string magnitudes | |
Arguments: | |
s1 = source1 decimal string | |
s2 = source2 decimal string | |
ds = destination 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); /* final result */ | |
} | |
return cy; | |
} | |
/* Subtract decimal string magnitudes | |
Arguments: | |
s1 = source1 decimal string | |
s2 = source2 decimal string | |
ds = destination 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++) | |
compl.val[i] = 0x99999999 - s1->val[i]; | |
AddDstr (&compl, s2, ds, 1); /* s1 + ~s2 + 1 */ | |
return; | |
} | |
/* Compare decimal string magnitudes | |
Arguments: | |
s1 = source1 decimal string | |
s2 = source2 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)) & 0xFFFFFFFF; | |
dsrc->val[i] = ((dsrc->val[i] >> s) | | |
cin) & 0xFFFFFFFF; | |
cin = nc; | |
} | |
return cin; | |
} | |
return 0; | |
} | |
/* Nibble shift decimal string left | |
Arguments: | |
dsrc = decimal string structure | |
sc = shift count | |
*/ | |
uint32 NibbleLshift (DSTR *dsrc, int32 sc) | |
{ | |
int32 i, s; | |
uint32 nc, cin; | |
cin = 0; | |
if ((s = sc * 4)) { | |
for (i = 0; i < DSTRLNT; i++) { | |
nc = dsrc->val[i] >> (32 - s); | |
dsrc->val[i] = ((dsrc->val[i] << s) | cin) & 0xFFFFFFFF; | |
cin = nc; | |
} | |
return cin; | |
} | |
return 0; | |
} | |
/* Common setup routine for MOVC class instructions */ | |
int32 movx_setup (int32 op, int32 *arg) | |
{ | |
int32 mvlnt, t; | |
if (CPUT (CPUT_44)) { /* 11/44? */ | |
ReadMB (((SP - 0200) & 0177777) | dsenable); /* probe both blocks */ | |
ReadMB (((SP - 0100) & 0177777) | dsenable); /* in 64W stack area */ | |
} | |
if (op & INLINE) { /* inline */ | |
mvlnt = (A1LNT < A2LNT)? A1LNT: A2LNT; | |
WriteW (mvlnt, ((SP - 14) & 0177777) | dsenable); /* push move length */ | |
WriteW (R[0], ((SP - 12) & 0177777) | dsenable); /* push R0 - R5 */ | |
WriteW (R[1], ((SP - 10) & 0177777) | dsenable); | |
WriteW (R[2], ((SP - 8) & 0177777) | dsenable); | |
WriteW (R[3], ((SP - 6) & 0177777) | dsenable); | |
WriteW (R[4], ((SP - 4) & 0177777) | dsenable); | |
WriteW (R[5], ((SP - 2) & 0177777) | dsenable); | |
SP = (SP - 14) & 0177777; | |
R[0] = A1LNT; /* args to registers */ | |
R[1] = A1ADR; | |
R[2] = A2LNT; | |
R[3] = A2ADR; | |
R[4] = A3LNT; | |
R[5] = A3ADR & 0177777; | |
} | |
else { /* register */ | |
mvlnt = (R[0] < R[2])? R[0]: R[2]; | |
WriteW (mvlnt, ((SP - 2) & 0177777) | dsenable); /* push move length */ | |
SP = (SP - 2) & 0177777; | |
} | |
fpd = 1; | |
t = R[0] - R[2]; /* src.lnt - dst.lnt */ | |
N = GET_SIGN_W (t); /* set cc's from diff */ | |
Z = GET_Z (t); | |
V = GET_SIGN_W ((R[0] ^ R[2]) & (~R[2] ^ t)); | |
C = (R[0] < R[2]); | |
return mvlnt; | |
} | |
/* Common cleanup routine for MOVC class instructions */ | |
void movx_cleanup (int32 op) | |
{ | |
SP = (SP + 2) & 0177777; /* discard mvlnt */ | |
if (op & INLINE) { /* inline? */ | |
R[0] = ReadW (SP | dsenable); /* restore R0 - R5 */ | |
R[1] = ReadW (((SP + 2) & 0177777) | dsenable); | |
R[2] = ReadW (((SP + 4) & 0177777) | dsenable); | |
R[3] = ReadW (((SP + 6) & 0177777) | dsenable); | |
R[4] = ReadW (((SP + 8) & 0177777) | dsenable); | |
R[5] = ReadW (((SP + 10) & 0177777) | dsenable); | |
SP = (SP + 12) & 0177777; | |
} | |
else R[1] = R[2] = R[3] = 0; /* reg, clear R1 - R3 */ | |
fpd = 0; /* instr done */ | |
return; | |
} | |
/* Test for CIS mid-instruction interrupt */ | |
t_bool cis_int_test (int32 cycles, int32 oldpc, t_stat *st) | |
{ | |
while (cycles >= 0) { /* until delay done */ | |
if (sim_interval > cycles) { /* event > delay */ | |
sim_interval = sim_interval - cycles; | |
break; | |
} | |
else { /* event <= delay */ | |
cycles = cycles - sim_interval; /* decr delay */ | |
sim_interval = 0; /* process event */ | |
*st = sim_process_event (); | |
trap_req = calc_ints (ipl, trap_req); /* recalc int req */ | |
if ((*st != SCPE_OK) || /* bad status or */ | |
trap_req & TRAP_INT) { /* interrupt? */ | |
PC = oldpc; /* back out */ | |
return TRUE; | |
} /* end if stop */ | |
} /* end else event */ | |
} /* end while delay */ | |
return FALSE; | |
} |