Because some key files have changed, V3.0 should be unzipped to a clean directory. 1. New Features in 3.0-0 1.1 SCP and Libraries - Added ASSIGN/DEASSIGN (logical name) commands. - Changed RESTORE to unconditionally detach files. - Added E11 and TPC format support to magtape library. - Fixed bug in SHOW CONNECTIONS. - Added USE_ADDR64 support 1.2 All magtapes - Magtapes support SIMH format, E11 format, and TPC format (read only). - SET <tape_unit> FORMAT=format sets the specified tape unit's format. - SHOW <tape_unit> FORMAT displays the specified tape unit's format. - Tape format can also be set as part of the ATTACH command, using the -F switch. 1.3 VAX - VAX can be compiled without USE_INT64. - If compiled with USE_INT64 and USE_ADDR64, RQ and TQ controllers support files > 2GB. - VAX ROM has speed control (SET ROM DELAY/NODELAY). 2. Bugs Fixed in 3.01-0 2.1 VAX - Fixed CVTfi bug: integer overflow not set if exponent out of range - Fixed EMODx bugs: o First and second operands reversed o Separated fraction received wrong exponent o Overflow calculation on separated integer incorrect o Fraction not set to zero if exponent out of range - Fixed interval timer and ROM access to pass power-up self-test even on very fast host processors (fixes from Mark Pizzolato). 2.2 1401 - Fixed mnemonic, instruction lengths, and reverse scan length check bug for MCS. - Fixed MCE bug, BS off by 1 if zero suppress. - Fixed chaining bug, D lost if return to SCP. - Fixed H branch, branch occurs after continue. - Added check for invalid 8 character MCW, LCA. - Fixed magtape load-mode end of record response. 2.3 Nova - Fixed DSK variable size interaction with restore. 2.4 PDP-1 - Fixed DT variable size interaction with restore. 2.5 PDP-11 - Fixed DT variable size interaction with restore. - Fixed bug in MMR1 update (found by Tim Stark). - Added XQ features and fixed bugs: o Corrected XQ interrupts on IE state transition (code by Tom Evans). o Added XQ interrupt clear on soft reset. o Removed XQ interrupt when setting XL or RL (multiple people). o Added SET/SHOW XQ STATS. o Added SHOW XQ FILTERS. o Added ability to split received packet into multiple buffers. o Added explicit runt and giant packet processing. 2.6 PDP-18B - Fixed DT, RF variable size interaction with restore. - Fixed MT bug in MTTR. 2.7 PDP-8 - Fixed DT, DF, RF, RX variable size interaction with restore. - Fixed MT bug in SKTR. 2.8 HP2100 - Fixed bug in DP (13210A controller only), DQ read status. - Fixed bug in DP, DQ seek complete. 2.9 GRI - Fixed bug in SC queue pointer management. 3. New Features in 3.0 vs prior releases N/A 4. Bugs Fixed in 3.0 vs prior releases N/A 5. General Notes WARNING: The RESTORE command has changed. RESTORE will now detach an attached file on a unit, if that unit did not have an attached file in the saved configuration. This is required to assure that the unit flags and the file state are consistent. WARNING: The compilation scheme for the PDP-10, PDP-11, and VAX has changed. Use one of the supplied build files, or read the documentation carefully, before compiling any of these simulators.
1832 lines
58 KiB
C
1832 lines
58 KiB
C
/* s3_cpu.c: IBM System/3 CPU simulator
|
||
|
||
Copyright (c) 2001-2003, Charles E. Owen
|
||
HPL & SLC instruction code Copyright (c) 2001 by Henk Stegeman
|
||
Decimal Arithmetic Copyright (c) 2000 by Roger Bowler
|
||
|
||
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 Charles E. Owen shall not
|
||
be used in advertising or otherwise to promote the sale, use or other dealings
|
||
in this Software without prior written authorization from Charles E. Owen.
|
||
|
||
------------------------------------------------------------------------------
|
||
|
||
cpu System/3 (models 10 and 15) central processor
|
||
|
||
The IBM System/3 was a popular small-business computing system introduced
|
||
in 1969 as an entry-level system for businesses that could not afford
|
||
the lowest rungs of the System/360. Its architecture is inspired by and
|
||
in some ways similar to the 360, but to save cost the instruction set is
|
||
much smaller and the I/O channel system greatly simplified. There is no
|
||
compatibilty between the two systems.
|
||
|
||
The original System/3 had two models, 6 and 10, and these came in two
|
||
configurations: card system and disk system. The unique feature of
|
||
the /3 was the use of 96-column cards, although traditional 80-column
|
||
cards were supprted also via attachment of a 1442 reader/punch.
|
||
System/3 is a batch-oriented system, controlled by an operating
|
||
system known as SCP (System Control Program), with it's own job control
|
||
language known as OCL (simpler and more logical than the JCL used on
|
||
the mainframes). Original models did not support multiprogramming
|
||
or any form of interactivity. (There was a hardware dual-program
|
||
facility available on the model 10 at the high end).
|
||
|
||
The line grew throughout the 1970s, overlapping the low end of the 360
|
||
line with the introduction of the model 15. The 15 (and later larger
|
||
variations of the model 12) broke the 64K limit designed in the original
|
||
models by adding a simple address translation unit to support up to 512K
|
||
bytes. The model 15 added a system of storage protection and allowed
|
||
multiprogramming in up to 3 partitions. Communications were added to
|
||
allow support of multiple 3270 terminals and the models 12 and 15 broke
|
||
the batch orientation and facilitated interactive use via the CCP
|
||
(communications control program). The System/3 was effectively replaced
|
||
by the much easier to manage and use System/34 and System/36 at the
|
||
low and middle of the range, and by System/370 or System/38 at the
|
||
high end.
|
||
|
||
This simulator implements the model 10 and model 15. Models 4, 6,
|
||
8, and 12 are not supported (these were technical variations on the
|
||
design which offered no functionality not present on either 10 or 15).
|
||
|
||
The System/3 is a byte-oriented machine with a data path of 8 bits
|
||
in all models, and an address width of 16 bits.
|
||
|
||
The register state for the System/3 CPU is:
|
||
|
||
BAR <0:15> Operand 1 address register
|
||
AAR <0:15> Operand 2 address register
|
||
XR1 <0:15> Index Register 1
|
||
XR2 <0:15> Index Register 2
|
||
PSR <0:15> Condition Register
|
||
IAR [0:9]<0:15> Instruction Address Register (p1, p2, plus 1 for each interrupt)
|
||
ARR [0:9]<0:15> Address Recall Register (p1, p2, plus 1 for each interrupt)
|
||
(The P2 IAR & ARR are used for the Dual Program feature)
|
||
|
||
Instruction formats follow the same basic pattern: a 1-byte opcode, a
|
||
1-byte "Q byte", and one or two addresses following in a format defined
|
||
by the first 4 bits of the opcode:
|
||
|
||
Op Code Q Byte Address(es)
|
||
|
||
0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
|
||
+--+--+--+--+--+--+--+--+ +--+--+--+--+--+--+--+--+ +--+--+--+--+--+--+--...
|
||
| A 1 | A 2 | operation | | (defined by operation)| | Format based on A1, A2
|
||
+--+--+--+--+--+--+--+--+ +--+--+--+--+--+--+--+--+ +--+--+--+--+--+--+--...
|
||
|
||
{ --- } <---------------- Bits 00 = Operand 2 specified by 2-byte direct addr
|
||
Bits 01 = Operand 2 is 1-byte displacement + XR1
|
||
Bits 10 = Operand 2 is 1-byte displacement + XR2
|
||
Bits 11 = Operand 2 is not used
|
||
|
||
{ --- } <---------------------- Bits 00 = Operand 1 specified by 2-byte direct addr
|
||
Bits 01 = Operand 1 is 1-byte displacement + XR1
|
||
Bits 10 = Operand 1 is 1-byte displacement + XR2
|
||
Bits 11 = Operand 1 is not used
|
||
|
||
Instructions come in 3 basic formats, of varying lengths which are determined
|
||
by the top 4 bits of opcode defined above. Minimum instruction length is 3 bytes,
|
||
maximum is 6.
|
||
|
||
1) Command Format (Bits 0-3 are 1111):
|
||
|
||
+------------+ +------------+ +------------+
|
||
| Opcode | | Q-byte | | R-byte +
|
||
+------------+ +------------+ +------------+
|
||
|
||
(The meaning of Q-byte and R-byte defined by the operation)
|
||
|
||
|
||
2) One Address Instructions (either bits 0-1 or bits 2-3 are 01):
|
||
|
||
|
||
Direct Addressing Format:
|
||
|
||
+------------+ +------------+ +-----------+----------+
|
||
| Opcode | | Q-byte | | MSB + LSB +
|
||
+------------+ +------------+ +-----------+----------+
|
||
|
||
Base-Displacement Format:
|
||
|
||
+------------+ +------------+ +------------+
|
||
| Opcode | | Q-byte | |displacement+
|
||
+------------+ +------------+ +------------+
|
||
|
||
Opcodes are 0011xxxx or 1100xxxx.
|
||
|
||
Q-byte can be: 1) An immediate operand
|
||
2) A mask
|
||
3) A branch condition
|
||
4) A data selection
|
||
|
||
2) Two Address Instructions (neither bits 0-1 nor bits 2-3 are both 11):
|
||
|
||
Operand 1 Address Direct (opcodes 0001 or 0010):
|
||
|
||
+------------+ +------------+ +----------+----------+ +------------+
|
||
| Opcode | | Q-byte | | MSB + LSB + |displacement|
|
||
+------------+ +------------+ +----------+----------+ +------------+
|
||
|
||
Operand 2 Address Direct (opcodes 0100 or 1000):
|
||
|
||
+------------+ +------------+ +------------+ +----------+----------+
|
||
| Opcode | | Q-byte | |displacement| | MSB + LSB +
|
||
+------------+ +------------+ +------------+ +----------+----------+
|
||
|
||
Both Addresses Direct (opcode 0000):
|
||
|
||
+------------+ +------------+ +----------+----------+ +-----------+----------+
|
||
| Opcode | | Q-byte | | MSB + LSB + + MSB + LSB +
|
||
+------------+ +------------+ +----------+----------+ +-----------+----------+
|
||
|
||
Both Addresses Displacement (opcodes 0101, 0110, 1001, or 1010):
|
||
|
||
+------------+ +------------+ +------------+ +------------+
|
||
| Opcode | | Q-byte | |displacement| |displacement|
|
||
+------------+ +------------+ +------------+ +------------+
|
||
|
||
|
||
Assembler Mnemonic Format
|
||
-------------------------
|
||
|
||
The assembler format contains the same elements as the machine language operation,
|
||
but not always in the same format. The operation code frequently specifies both
|
||
the opcode and the Q byte, and the top nybble of the opcode is determined by
|
||
the format of the addresses.
|
||
|
||
Addresses take two forms: the direct address in hex, or a relative address
|
||
specified thusly: (byte,XRx) where 'byte' is a 1-byte offset, and XRx is
|
||
either XR1 or XR2 for the two index registers. Use these formats when
|
||
'address' is indicated below:
|
||
|
||
When 'reg' is mentioned, a mnemonic may be used for the register, thusly:
|
||
IAR Instruction Address Register for the current program level
|
||
ARR Address Recall Register for the current program level
|
||
P1IAR IAR for Program Level 1
|
||
P2IAR IAR for Program Level 2
|
||
PSR Program Status Register
|
||
0x01 - Equal
|
||
0x02 - Low
|
||
0x04 - High
|
||
0x08 - Decimal overflow
|
||
0x10 - Test false
|
||
0x20 - Binary overflow
|
||
0x40 - Not used
|
||
0x80 - Not used
|
||
XR1 Index Register 1
|
||
XR2 Index Register 2
|
||
IARx IAR for the interrupt level x (x = 0 thru 7)
|
||
|
||
All other operands mentioned below are single-byte hex, except for the
|
||
length (len) operand of the two-address instructions, which is a decimal length
|
||
in the range 1-256.
|
||
|
||
No-address formats:
|
||
------------------
|
||
|
||
HPL hex,hex Halt Program Level, the operands are the Q and R bytes
|
||
|
||
|
||
One-address formats:
|
||
-------------------
|
||
|
||
A reg,address Add to register
|
||
CLI address,byte Compare Logical Immediate
|
||
MVI address,byte Move Immediate
|
||
TBF address,mask Test Bits Off
|
||
TBN address,mask Test Bits On
|
||
SBF address,mask Set Bits Off
|
||
SBN address,mask Set Bits On
|
||
ST reg,address Store Register
|
||
L reg,address Load Register
|
||
LA reg,address Load Address
|
||
JC address,cond Jump on Condition
|
||
BC address,cond Branch on Condition
|
||
|
||
These operations do not specify a qbyte, it is implicit in the opcode:
|
||
|
||
B address Unconditional branch to address
|
||
BE address Branch Equal
|
||
BNE address Branch Not Equal
|
||
BH address Branch High
|
||
BNH address Branch Not High
|
||
BL address Branch Low
|
||
BNL address Branch Not Low
|
||
BT address Branch True
|
||
BF address Branch False
|
||
BP address Branch Plus
|
||
BM address Branch Minus
|
||
BNP address Branch Not Plus
|
||
BNM address Branch Not Minus
|
||
BZ address Branch Zero
|
||
BNZ address Branch Not Zero
|
||
BOZ address Branch Overflow Zoned
|
||
BOL address Branch Overflow Logical
|
||
BNOZ address Branch No Overflow Zoned
|
||
BNOL address Branch No Overflow Logical
|
||
NOPB address No - never jump
|
||
|
||
(substitute J for B above for a set of Jumps -- 1-byte operand (not 2),
|
||
always jumps forward up to 255 bytes. In this case, 'address' cannot be
|
||
less than the current address, nor greater than the current address + 255)
|
||
|
||
Two-address formats (first address is destination, len is decimal 1-256):
|
||
-------------------
|
||
|
||
MVC address,address,len Move Characters
|
||
CLC address,address,len Compare Logical Characters
|
||
ALC address,address,len Add Logical Characters
|
||
SLC address,address,len Subtract Logical Characters
|
||
ED address,address,len Edit
|
||
ITC address,address,len Insert and Test Characters
|
||
AZ address,address,len Add Zoned Decimal
|
||
SZ address,address,len Subtract Zoned Decimal
|
||
|
||
MNN address,address Move Numeric to Numeric
|
||
MNZ address,address Move Numeric to Zone
|
||
MZZ address,address Move Zone to Zone
|
||
MZN address,address Move Zone to Numeric
|
||
|
||
I/O Format
|
||
----------
|
||
|
||
In the I/O format, there are always 3 fields:
|
||
|
||
da - Device Address 0-15 (decimal)
|
||
m - Modifier 0-1
|
||
n - Function 0-7
|
||
|
||
The meaning of these is entirely defined by the device addressed.
|
||
|
||
There may be an optional control byte, or an optional address (based on
|
||
the type of instruction).
|
||
|
||
SNS da,m,n,address Sense I/O
|
||
LIO da,m,n,address Load I/O
|
||
TIO da,m,n,address Test I/O
|
||
|
||
SIO da,m,n,cc Start I/O -- cc is a control byte
|
||
|
||
APL da,m,n Advance Program Level
|
||
|
||
|
||
|
||
---------------------------------------------
|
||
Here is a handy opcode cross-reference table:
|
||
---------------------------------------------
|
||
|
||
| x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xA xB xC xD xE xF
|
||
---+------------------------------------------------------------------
|
||
0x | - - - - ZAZ - AZ SZ MVX - ED ITC MVC CLC ALC SLC
|
||
1x | - - - - ZAZ - AZ SZ MVX - ED ITC MVC CLC ALC SLC
|
||
2x | - - - - ZAZ - AZ SZ MVX - ED ITC MVC CLC ALC SLC
|
||
3x | SNS LIO - - ST L A - TBN TBF SBN SBF MVI CLI - -
|
||
|
|
||
4x | - - - - ZAZ - AZ SZ MVX - ED ITC MVC CLC ALC SLC
|
||
5x | - - - - ZAZ - AZ SZ MVX - ED ITC MVC CLC ALC SLC
|
||
6x | - - - - ZAZ - AZ SZ MVX - ED ITC MVC CLC ALC SLC
|
||
7x | SNS LIO - - ST L A - TBN TBF SBN SBF MVI CLI - -
|
||
|
|
||
8x | - - - - ZAZ - AZ SZ MVX - ED ITC MVC CLC ALC SLC
|
||
9x | - - - - ZAZ - AZ SZ MVX - ED ITC MVC CLC ALC SLC
|
||
Ax | - - - - ZAZ - AZ SZ MVX - ED ITC MVC CLC ALC SLC
|
||
Bx | SNS LIO - - ST L A - TBN TBF SBN SBF MVI CLI - -
|
||
|
|
||
Cx | BC TIO LA - - - - - - - - - - - - -
|
||
Dx | BC TIO LA - - - - - - - - - - - - -
|
||
Ex | BC TIO LA - - - - - - - - - - - - -
|
||
Fx | HPL APL JC SIO - - - - - - - - - - - -
|
||
|
||
*/
|
||
|
||
/* This routine is the instruction decode routine for System/3.
|
||
It is called from the simulator control program to execute
|
||
instructions in simulated memory, starting at the simulated PC.
|
||
It runs until 'reason' is set non-zero.
|
||
|
||
General notes:
|
||
|
||
1. Reasons to stop. The simulator can be stopped by:
|
||
|
||
HALT instruction
|
||
breakpoint encountered
|
||
program check caused by invalid opcode or qbyte or address or I/O spec
|
||
unknown I/O device and STOP_DEV flag set
|
||
I/O error in I/O simulator
|
||
|
||
2. Interrupts.
|
||
|
||
There are 8 levels of interrupt, each with it's own IAR (program
|
||
counter). When an interrupt occurs, execution begins at the
|
||
location in the IAR for that level interrupt. The program
|
||
must save and restore state. Each device is assigned both a
|
||
level and a priority in hardware. Interrupts are reset via
|
||
an SIO instruction, when this happens, the program level
|
||
IAR resumes control.
|
||
|
||
Interrupts are maintained in the global variable int_req,
|
||
which is zero if no interrupts are pending, otherwise, the
|
||
lower 16 bits represent devices, rightmost bit being device
|
||
0. Each device requesting an interrupt sets its bit on.
|
||
|
||
|
||
3. Non-existent memory. On the System/3, any reference to non-existent
|
||
memory (read or write) causes a program check and machine stop.
|
||
|
||
4. Adding I/O devices. These modules must be modified:
|
||
|
||
ibms3_defs.h add interrupt request definition
|
||
ibms3_cpu.c add IOT mask, PI mask, and routine to dev_table
|
||
ibms3_sys.c add pointer to data structures to sim_devices
|
||
*/
|
||
|
||
#include "s3_defs.h"
|
||
|
||
#define UNIT_V_M15 (UNIT_V_UF) /* Model 15 extensions */
|
||
#define UNIT_M15 (1 << UNIT_V_M15)
|
||
#define UNIT_V_DPF (UNIT_V_UF+1) /* Dual Programming */
|
||
#define UNIT_DPF (1 << UNIT_V_DPF)
|
||
#define UNIT_V_MSIZE (UNIT_V_UF+3) /* dummy mask */
|
||
#define UNIT_MSIZE (1 << UNIT_V_MSIZE)
|
||
|
||
uint8 M[MAXMEMSIZE] = { 0 }; /* memory */
|
||
int32 AAR = 0; /* Operand 1 addr reg */
|
||
int32 BAR = 0; /* Operand 2 addr reg */
|
||
int32 XR1 = 0; /* Index register 1 */
|
||
int32 XR2 = 0; /* Index register 2 */
|
||
int32 PSR = 0; /* Condition Register */
|
||
int32 IAR[10] = { 0 }; /* IAR 0-7 = int level 8=P1 9=P2 */
|
||
int32 ARR[10] = { 0 }; /* ARR 0-7 = int level 8=P1 9=P2 */
|
||
int32 dev_disable = 0; /* interrupt disable mask */
|
||
int32 int_req = 0; /* Interrupt request device bitmap */
|
||
int32 level = 8; /* Current Execution Level*/
|
||
int32 stop_dev = 0; /* stop on ill dev */
|
||
int32 SR = 0; /* Switch Register */
|
||
int32 saved_PC; /* Saved (old) PC) */
|
||
int32 debug_reg = 0; /* set for debug/trace */
|
||
int32 debug_flag = 0; /* 1 when trace.log open */
|
||
FILE *trace;
|
||
extern int32 sim_int_char;
|
||
extern int32 sim_brk_types, sim_brk_dflt, sim_brk_summ; /* breakpoint info */
|
||
|
||
t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw);
|
||
t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw);
|
||
t_stat cpu_reset (DEVICE *dptr);
|
||
t_stat cpu_set_size (UNIT *uptr, int32 val, char *cptr, void *desc);
|
||
t_stat cpu_boot (int32 unitno, DEVICE *dptr1);
|
||
extern int32 pkb (int32 op, int32 m, int32 n, int32 data);
|
||
extern int32 crd (int32 op, int32 m, int32 n, int32 data);
|
||
extern int32 lpt (int32 op, int32 m, int32 n, int32 data);
|
||
extern int32 dsk1 (int32 op, int32 m, int32 n, int32 data);
|
||
extern int32 dsk2 (int32 op, int32 m, int32 n, int32 data);
|
||
extern int32 cpu (int32 op, int32 m, int32 n, int32 data);
|
||
extern t_stat sim_activate (UNIT *uptr, int32 delay);
|
||
extern int32 fprint_sym (FILE *of, int32 addr, unsigned int32 *val,
|
||
UNIT *uptr, int32 sw);
|
||
int32 nulldev (int32 opcode, int32 m, int32 n, int32 data);
|
||
int add_zoned (int32 addr1, int32 len1, int32 addr2, int32 len2);
|
||
int32 subtract_zoned (int32 addr1, int32 len1, int32 addr2, int32 len2);
|
||
static int32 compare(int32 byte1, int32 byte2, int32 cond);
|
||
static int32 condition(int32 qbyte);
|
||
static void store_decimal (int32 addr, int32 len, uint8 *dec, int sign);
|
||
static void load_decimal (int32 addr, int32 len, uint8 *result, int32 *count, int32 *sign);
|
||
static void add_decimal (uint8 *dec1, uint8 *dec2, uint8 *result, int32 *count);
|
||
static void subtract_decimal (uint8 *dec1, uint8 *dec2, uint8 *result, int *count, int *sign);
|
||
int32 GetMem(int32 addr);
|
||
int32 PutMem(int32 addr, int32 data);
|
||
|
||
/* IOT dispatch table */
|
||
|
||
/* System/3 supports only 16 unique device addresses! */
|
||
|
||
struct ndev dev_table[16] = {
|
||
{ 0, 0, &cpu }, /* Device 0: CPU control */
|
||
{ 1, 0, &pkb }, /* Device 1: 5471 console printer/keyboard */
|
||
{ 0, 0, &nulldev },
|
||
{ 0, 0, &nulldev },
|
||
{ 0, 0, &nulldev },
|
||
{ 0, 0, &crd }, /* Device 5: 1442 card reader/punch */
|
||
{ 0, 0, &nulldev }, /* Device 6: 3410 Tape drives 1 & 2 */
|
||
{ 0, 0, &nulldev }, /* Device 7: 3410 Tape drives 3 & 4 */
|
||
{ 0, 0, &nulldev },
|
||
{ 0, 0, &nulldev },
|
||
{ 0, 0, &dsk1 }, /* Device 10: 5444 Disk Drive 1 */
|
||
{ 0, 0, &dsk2 }, /* Device 11: 5444 Disk Drive 2 */
|
||
{ 0, 0, &nulldev }, /* Device 12: 5448 Disk Drive 1 */
|
||
{ 0, 0, &nulldev }, /* DEvice 13: 5448 Disk Drive 2 */
|
||
{ 0, 0, &lpt }, /* Device 14: 1403/5203 Printer */
|
||
{ 0, 0, &nulldev } /* Device 15: 5424 MFCU */
|
||
};
|
||
|
||
/* Priority assigned to interrupt levels */
|
||
|
||
int32 priority[8] = {8, 7, 5, 4, 3, 6, 2, 1};
|
||
|
||
|
||
/* CPU data structures
|
||
|
||
cpu_dev CPU device descriptor
|
||
cpu_unit CPU unit descriptor
|
||
cpu_reg CPU register list
|
||
cpu_mod CPU modifiers list
|
||
*/
|
||
|
||
UNIT cpu_unit = { UDATA (NULL, UNIT_FIX + UNIT_BINK, MAXMEMSIZE) };
|
||
|
||
REG cpu_reg[] = {
|
||
{ HRDATA (IAR, saved_PC, 16), REG_RO },
|
||
{ HRDATA (IAR-P1, IAR[8], 16) },
|
||
{ HRDATA (IAR-P2, IAR[9], 16) },
|
||
{ HRDATA (ARR-P1, ARR[8], 16) },
|
||
{ HRDATA (ARR-P2, ARR[9], 16) },
|
||
{ HRDATA (AAR, AAR, 16) },
|
||
{ HRDATA (BAR, BAR, 16) },
|
||
{ HRDATA (XR1, XR1, 16) },
|
||
{ HRDATA (XR2, XR2, 16) },
|
||
{ HRDATA (PSR, PSR, 16) },
|
||
{ HRDATA (SR, SR, 16) },
|
||
{ HRDATA (INT, int_req, 16), REG_RO },
|
||
{ HRDATA (LEVEL, level, 16) },
|
||
{ HRDATA (IAR0, IAR[0], 16) },
|
||
{ HRDATA (IAR1, IAR[1], 16) },
|
||
{ HRDATA (IAR2, IAR[2], 16) },
|
||
{ HRDATA (IAR3, IAR[3], 16) },
|
||
{ HRDATA (IAR4, IAR[4], 16) },
|
||
{ HRDATA (IAR5, IAR[5], 16) },
|
||
{ HRDATA (IAR6, IAR[6], 16) },
|
||
{ HRDATA (IAR7, IAR[7], 16) },
|
||
{ HRDATA (ARR0, ARR[0], 16) },
|
||
{ HRDATA (ARR1, ARR[1], 16) },
|
||
{ HRDATA (ARR2, ARR[2], 16) },
|
||
{ HRDATA (ARR3, ARR[3], 16) },
|
||
{ HRDATA (ARR4, ARR[4], 16) },
|
||
{ HRDATA (ARR5, ARR[5], 16) },
|
||
{ HRDATA (ARR6, ARR[6], 16) },
|
||
{ HRDATA (ARR7, ARR[7], 16) },
|
||
{ HRDATA (DISABLE, dev_disable, 16), REG_RO },
|
||
{ FLDATA (STOP_DEV, stop_dev, 0) },
|
||
{ HRDATA (WRU, sim_int_char, 8) },
|
||
{ HRDATA (DEBUG, debug_reg, 16) },
|
||
{ NULL } };
|
||
|
||
MTAB cpu_mod[] = {
|
||
{ UNIT_M15, UNIT_M15, "M15", "M15", NULL },
|
||
{ UNIT_M15, 0, "M10", "M10", NULL },
|
||
{ UNIT_DPF, UNIT_DPF, "DPF", "DPF", NULL },
|
||
{ UNIT_DPF, 0, "NODPF", "NODPF", NULL },
|
||
{ UNIT_MSIZE, 8192, NULL, "8K", &cpu_set_size },
|
||
{ UNIT_MSIZE, 16384, NULL, "16K", &cpu_set_size },
|
||
{ UNIT_MSIZE, 32768, NULL, "32K", &cpu_set_size },
|
||
{ UNIT_MSIZE, 49152, NULL, "48K", &cpu_set_size },
|
||
{ UNIT_MSIZE, 65535, NULL, "64K", &cpu_set_size },
|
||
{ 0 } };
|
||
|
||
DEVICE cpu_dev = {
|
||
"CPU", &cpu_unit, cpu_reg, cpu_mod,
|
||
1, 16, 16, 1, 16, 8,
|
||
&cpu_ex, &cpu_dep, &cpu_reset,
|
||
NULL, NULL, NULL };
|
||
|
||
t_stat sim_instr (void)
|
||
{
|
||
extern int32 sim_interval;
|
||
register int32 PC, IR;
|
||
int32 i, j, carry, zero, op1, op2;
|
||
int32 opcode = 0, qbyte = 0, rbyte = 0;
|
||
int32 opaddr, addr1, addr2, dlen1, dlen2, r;
|
||
int32 int_savelevel = 8, intpri, intlev, intdev, intmask;
|
||
int32 devno, devm, devn;
|
||
char display[3][9];
|
||
int32 val [32];
|
||
register t_stat reason;
|
||
|
||
/* Restore register state */
|
||
|
||
PC = IAR[level]; /* load local PC */
|
||
reason = 0;
|
||
|
||
/* Main instruction fetch/decode loop */
|
||
|
||
while (reason == 0) { /* loop until halted */
|
||
if (sim_interval <= 0) { /* check clock queue */
|
||
if (reason = sim_process_event ()) break; }
|
||
|
||
|
||
if (int_req) { /* interrupt? */
|
||
intpri = 16;
|
||
for (i = 0; i < 16; i++) { /* Get highest priority device */
|
||
if ((int_req >> i) & 0x01) {
|
||
intlev = dev_table[i].level;
|
||
if (priority[intlev] < intpri) {
|
||
intdev = i;
|
||
intpri = priority[intlev];
|
||
}
|
||
}
|
||
}
|
||
intmask = 1 << intdev; /* mask is interrupting dev bit */
|
||
int_req = ~int_req & intmask; /* Turn off int_req for device */
|
||
int_savelevel = level; /* save current level for reset */
|
||
level = dev_table[intdev].level; /* get int level from device */
|
||
PC = IAR[level]; /* Use int level IAR for new PC */
|
||
} /* end interrupt */
|
||
|
||
if (sim_brk_summ && sim_brk_test (PC, SWMASK ('E'))) { /* breakpoint? */
|
||
reason = STOP_IBKPT; /* stop simulation */
|
||
break;
|
||
}
|
||
|
||
/* Machine Instruction Execution Here */
|
||
|
||
if ((debug_reg == 0) && debug_flag == 1) {
|
||
fclose(trace);
|
||
debug_flag = 0;
|
||
}
|
||
if (debug_reg) {
|
||
if (!debug_flag) {
|
||
trace = fopen("trace.log", "w");
|
||
debug_flag = 1;
|
||
}
|
||
}
|
||
|
||
if (debug_reg & 0x01) {
|
||
fprintf(trace, "ARR=%04X XR1=%04X XR2=%04X IAR=%04X ", ARR[level], XR1, XR2, PC);
|
||
val[0] = GetMem(PC);
|
||
val[1] = GetMem(PC+1);
|
||
val[2] = GetMem(PC+2);
|
||
val[3] = GetMem(PC+3);
|
||
val[4] = GetMem(PC+4);
|
||
val[5] = GetMem(PC+5);
|
||
fprint_sym(trace, PC, (uint32 *) val, &cpu_unit, SWMASK('M'));
|
||
fprintf(trace, "\n");
|
||
}
|
||
|
||
saved_PC = PC;
|
||
opaddr = GetMem(PC) & 0xf0; /* fetch addressing mode */
|
||
opcode = GetMem(PC) & 0x0f; /* fetch opcode */
|
||
PC = (PC + 1) & AMASK;
|
||
sim_interval = sim_interval - 1;
|
||
|
||
qbyte = GetMem(PC) & 0xff; /* fetch qbyte */
|
||
PC = (PC + 1) & AMASK;
|
||
|
||
if (opaddr == 0xf0) { /* Is it command format? */
|
||
rbyte = GetMem(PC) & 0xff;
|
||
PC = (PC + 1) & AMASK;
|
||
switch (opcode) {
|
||
case 0x00: /* HPL: Halt Program Level */
|
||
for (i = 0; i < 3; i++) {
|
||
for (j = 0; j < 9; j++) {
|
||
display[i][j] = ' ';
|
||
}
|
||
}
|
||
/* First line */
|
||
if (qbyte & 0x04) display[0][2] = '_' ;
|
||
if (rbyte & 0x04) display[0][6] = '_' ;
|
||
/* Second line */
|
||
if (qbyte & 0x08) display[1][1] = '|' ;
|
||
if (rbyte & 0x08) display[1][5] = '|' ;
|
||
if (qbyte & 0x10) display[1][2] = '_' ;
|
||
if (rbyte & 0x10) display[1][6] = '_' ;
|
||
if (qbyte & 0x02) display[1][3] = '|' ;
|
||
if (rbyte & 0x02) display[1][7] = '|' ;
|
||
/* Third line */
|
||
if (qbyte & 0x20) display[2][1] = '|' ;
|
||
if (rbyte & 0x20) display[2][5] = '|' ;
|
||
if (qbyte & 0x40) display[2][2] = '_' ;
|
||
if (rbyte & 0x40) display[2][6] = '_' ;
|
||
if (qbyte & 0x01) display[2][3] = '|' ;
|
||
if (rbyte & 0x01) display[2][7] = '|' ;
|
||
/* Print display segment array */
|
||
printf("\n\r");
|
||
for (i = 0; i < 3; i++) {
|
||
for (j = 0; j < 9; j++) {
|
||
printf ("%c", display[i][j]);
|
||
}
|
||
printf ("\n\r");
|
||
}
|
||
reason = STOP_HALT;
|
||
break;
|
||
case 0x01: /* APL: Advance Program Level */
|
||
devno = (qbyte >> 4) & 0x0f;
|
||
devm = (qbyte >> 3) & 0x01;
|
||
devn = qbyte & 0x07;
|
||
op1 = dev_table[devno].routine(4, devm, devn, rbyte);
|
||
if (op1 & 0x01) {
|
||
if (cpu_unit.flags & UNIT_DPF) { /* Dual Programming? */
|
||
if (level == 8) /* Yes: switch program levels */
|
||
level = 9;
|
||
else
|
||
level = 8;
|
||
PC = IAR[level];
|
||
} else { /* No: Loop on this inst */
|
||
PC = PC - 3;
|
||
}
|
||
}
|
||
reason = (op1 >> 16) & 0xffff;
|
||
break;
|
||
case 0x02: /* JC: Jump on Condition */
|
||
if (condition(qbyte) == 1) {
|
||
PC = (PC + rbyte) & AMASK;
|
||
}
|
||
break;
|
||
case 0x03: /* SIO: Start I/O */
|
||
devno = (qbyte >> 4) & 0x0f;
|
||
devm = (qbyte >> 3) & 0x01;
|
||
devn = qbyte & 0x07;
|
||
reason = dev_table[devno].routine(0, devm, devn, rbyte);
|
||
if (reason == RESET_INTERRUPT) {
|
||
reason = SCPE_OK;
|
||
IAR[level] = PC;
|
||
level = int_savelevel;
|
||
PC = IAR[level];
|
||
}
|
||
break;
|
||
default:
|
||
reason = STOP_INVOP;
|
||
break;
|
||
} /* switch (opcode) */
|
||
IAR[level] = PC;
|
||
continue;
|
||
}
|
||
/* Not command format: fetch the addresses */
|
||
|
||
addr1 = (opaddr >> 6) & 3;
|
||
addr2 = (opaddr >> 4) & 3;
|
||
|
||
switch (addr1) {
|
||
case 0:
|
||
BAR = GetMem(PC) << 8;
|
||
PC = (PC + 1) & AMASK;
|
||
BAR |=GetMem(PC);
|
||
PC = (PC + 1) & AMASK;
|
||
break;
|
||
case 1:
|
||
BAR = GetMem(PC);
|
||
BAR = (BAR + XR1) & AMASK;
|
||
PC = (PC + 1) & AMASK;
|
||
break;
|
||
case 2:
|
||
BAR = GetMem(PC);
|
||
BAR = (BAR + XR2) & AMASK;
|
||
PC = (PC + 1) & AMASK;
|
||
break;
|
||
case 3:
|
||
break;
|
||
default:
|
||
break;
|
||
} /* switch (addr1) */
|
||
|
||
switch (addr2) {
|
||
case 0:
|
||
AAR = GetMem(PC) << 8;
|
||
PC = (PC + 1) & AMASK;
|
||
AAR |= GetMem(PC);
|
||
PC = (PC + 1) & AMASK;
|
||
break;
|
||
case 1:
|
||
AAR = GetMem(PC);
|
||
AAR = (AAR + XR1) & AMASK;
|
||
PC = (PC + 1) & AMASK;
|
||
break;
|
||
case 2:
|
||
AAR = GetMem(PC);
|
||
AAR = (AAR + XR2) & AMASK;
|
||
PC = (PC + 1) & AMASK;
|
||
break;
|
||
case 3:
|
||
break;
|
||
default:
|
||
break;
|
||
} /* switch (addr1) */
|
||
|
||
switch (opaddr) {
|
||
case 0x00:
|
||
case 0x10:
|
||
case 0x20:
|
||
case 0x40:
|
||
case 0x50:
|
||
case 0x60:
|
||
case 0x80:
|
||
case 0x90:
|
||
case 0xa0:
|
||
switch (opcode) {
|
||
case 4: /* ZAZ: Zero and Add Zoned */
|
||
dlen2 = qbyte & 0x0f;
|
||
dlen1 = (qbyte >> 4) & 0xf;
|
||
dlen1 += dlen2;
|
||
op1 = BAR;
|
||
for (i = 0; i < (dlen1+1); i++) {
|
||
PutMem(op1, 0xf0);
|
||
op1--;
|
||
}
|
||
r = add_zoned(BAR, dlen1+1, AAR, dlen2+1);
|
||
PSR &= 0xF8; /* HJS mod */
|
||
switch (r) {
|
||
case 0:
|
||
PSR |= 0x01;
|
||
break;
|
||
case 1:
|
||
PSR |= 0x02;
|
||
break;
|
||
case 2:
|
||
PSR |= 0x04;
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
break;
|
||
case 6: /* AZ: Add Zoned */
|
||
dlen2 = qbyte & 0x0f;
|
||
dlen1 = (qbyte >> 4) & 0xf;
|
||
dlen1 += dlen2;
|
||
r = add_zoned(BAR, dlen1+1, AAR, dlen2+1);
|
||
PSR &= 0xF0;
|
||
switch (r) {
|
||
case 0:
|
||
PSR |= 0x01;
|
||
break;
|
||
case 1:
|
||
PSR |= 0x02;
|
||
break;
|
||
case 2:
|
||
PSR |= 0x04;
|
||
break;
|
||
case 3:
|
||
PSR |= 0x08;
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
break;
|
||
case 7: /* SZ: Subtract Zoned */
|
||
dlen2 = qbyte & 0x0f;
|
||
dlen1 = (qbyte >> 4) & 0xf;
|
||
dlen1 += dlen2;
|
||
r = subtract_zoned(BAR, dlen1+1, AAR, dlen2+1);
|
||
PSR &= 0xF0;
|
||
switch (r) {
|
||
case 0:
|
||
PSR |= 0x01;
|
||
break;
|
||
case 1:
|
||
PSR |= 0x02;
|
||
break;
|
||
case 2:
|
||
PSR |= 0x04;
|
||
break;
|
||
case 3:
|
||
PSR |= 0x08;
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
break;
|
||
case 8: /* MVX: Move Hex */
|
||
op1 = GetMem(BAR);
|
||
op2 = GetMem(AAR);
|
||
switch (qbyte) {
|
||
case 0: /* Zone to zone */
|
||
op1 = (op1 & 0x0F) | (op2 & 0xF0);
|
||
break;
|
||
case 1: /* Numeric to zone */
|
||
op1 = (op1 & 0x0F) | (op2 << 4);
|
||
break;
|
||
case 2: /* Zone to numeric */
|
||
op1 = (op1 & 0xF0) | (op2 >> 4);
|
||
break;
|
||
case 3: /* Numeric to numeric */
|
||
op1 = (op1 & 0xF0) | (op2 & 0x0F);
|
||
break;
|
||
default:
|
||
reason = STOP_INVQ;
|
||
break;
|
||
}
|
||
PutMem(BAR, op1);
|
||
break;
|
||
case 0xa: /* ED: Edit */
|
||
zero = 1;
|
||
PSR &= 0xF8;
|
||
IR = GetMem(AAR);
|
||
if ((IR & 0xf0) != 0xF0)
|
||
PSR |= 0x02;
|
||
else
|
||
PSR |= 0x04;
|
||
while (qbyte > -1) {
|
||
op2 = GetMem(AAR);
|
||
op1 = GetMem(BAR);
|
||
if (op1 == 0x20) {
|
||
op2 |= 0xf0;
|
||
PutMem(BAR, op2);
|
||
AAR--;
|
||
if (op2 != 0xF0) zero = 0;
|
||
}
|
||
BAR--;
|
||
qbyte--;
|
||
}
|
||
if (zero)
|
||
PSR |= 0x01;
|
||
break;
|
||
case 0xb: /* ITC: Insert and Test Chars */
|
||
op2 = GetMem(AAR);
|
||
while (qbyte > -1) {
|
||
op1 = GetMem(BAR);
|
||
if (op1 >= 0xF1 && op1 <= 0xF9)
|
||
break;
|
||
PutMem(BAR, op2);
|
||
BAR++;
|
||
qbyte--;
|
||
}
|
||
ARR[level] = BAR;
|
||
break;
|
||
case 0xc: /* MVC: Move Characters */
|
||
while (qbyte > -1) {
|
||
PutMem(BAR, GetMem(AAR));
|
||
BAR--;
|
||
AAR--;
|
||
qbyte--;
|
||
}
|
||
break;
|
||
case 0xd: /* CLC: Compare Characters */
|
||
PSR &= 0xF8;
|
||
i = BAR = BAR - qbyte;
|
||
j = AAR = AAR - qbyte;
|
||
while (qbyte > -1) {
|
||
if (GetMem(i) > GetMem(j)) {
|
||
PSR |= 0x04;
|
||
break;
|
||
}
|
||
if (GetMem(i) < GetMem(j)) {
|
||
PSR |= 0x02;
|
||
break;
|
||
}
|
||
i++;
|
||
j++;
|
||
qbyte--;
|
||
}
|
||
if (qbyte == -1)
|
||
PSR |= 0x01;
|
||
break;
|
||
case 0xe: /* ALC: Add Logical Characters */
|
||
carry = 0;
|
||
zero = 1;
|
||
while (qbyte > -1) {
|
||
IR = GetMem(BAR) + GetMem(AAR) + carry;
|
||
if (IR & 0x100)
|
||
carry = 1;
|
||
else
|
||
carry = 0;
|
||
if ((IR & 0xFF) != 0) zero = 0; /* HJS mod */
|
||
PutMem(BAR,(IR & 0xFF));
|
||
BAR--;
|
||
AAR--;
|
||
qbyte--;
|
||
}
|
||
PSR &= 0xD8;
|
||
if (zero)
|
||
PSR |= 0x01; /* Equal */
|
||
if (!zero && !carry)
|
||
PSR |= 0x02; /* Low */
|
||
if (!zero && carry)
|
||
PSR |= 0x04; /* High */
|
||
if (carry)
|
||
PSR |= 0x20; /* Overflow */
|
||
break;
|
||
case 0xf: /* SLC: Subtract Logical Characters */
|
||
carry = 1;
|
||
zero = 1;
|
||
while (qbyte > -1) {
|
||
IR = GetMem(BAR) + (0xFF - GetMem(AAR)) + carry;
|
||
if (IR & 0x100)
|
||
carry = 1;
|
||
else
|
||
carry = 0;
|
||
if ((IR & 0xFF) != 0) zero = 0; /* HJS mod */
|
||
PutMem(BAR,(IR & 0xFF));
|
||
BAR--;
|
||
AAR--;
|
||
qbyte--;
|
||
}
|
||
PSR &= 0xF8;
|
||
if (zero)
|
||
PSR |= 0x01; /* Equal */
|
||
if (!zero && !carry)
|
||
PSR |= 0x02; /* Low */
|
||
if (!zero && carry)
|
||
PSR |= 0x04; /* High */
|
||
break;
|
||
default:
|
||
reason = STOP_INVOP;
|
||
break;
|
||
}
|
||
IAR[level] = PC;
|
||
continue;
|
||
break;
|
||
case 0x30:
|
||
case 0x70:
|
||
case 0xb0:
|
||
switch (opcode) {
|
||
case 0: /* SNS: Sense I/O */
|
||
devno = (qbyte >> 4) & 0x0f;
|
||
devm = (qbyte >> 3) & 0x01;
|
||
devn = qbyte & 0x07;
|
||
i = dev_table[devno].routine(3, devm, devn, rbyte);
|
||
PutMem(BAR, i & 0xff);
|
||
BAR--;
|
||
PutMem(BAR, (i >> 8) & 0xff);
|
||
reason = (i >> 16) & 0xffff;
|
||
break;
|
||
case 1: /* LIO: Load I/O */
|
||
devno = (qbyte >> 4) & 0x0f;
|
||
devm = (qbyte >> 3) & 0x01;
|
||
devn = qbyte & 0x07;
|
||
op1 = GetMem(BAR);
|
||
BAR--;
|
||
op1 |= (GetMem(BAR) << 8) & 0xff00;
|
||
reason = dev_table[devno].routine(1, devm, devn, op1);
|
||
break;
|
||
case 4: /* ST: Store Register */
|
||
switch (qbyte) {
|
||
case 0x01:
|
||
PutMem(BAR, XR1 & 0xff);
|
||
BAR--;
|
||
PutMem(BAR, (XR1 >> 8) & 0xff);
|
||
break;
|
||
case 0x02:
|
||
PutMem(BAR, XR2 & 0xff);
|
||
BAR--;
|
||
PutMem(BAR, (XR2 >> 8) & 0xff);
|
||
break;
|
||
case 0x04:
|
||
PutMem(BAR, PSR & 0xFF);
|
||
BAR--;
|
||
PutMem(BAR, 0); /* LCRR, not imp. */
|
||
break;
|
||
case 0x08:
|
||
PutMem(BAR, ARR[level] & 0xff);
|
||
BAR--;
|
||
PutMem(BAR, (ARR[level] >> 8) & 0xff);
|
||
break;
|
||
case 0x10:
|
||
PutMem(BAR, IAR[level] & 0xff);
|
||
BAR--;
|
||
PutMem(BAR, (IAR[level] >> 8) & 0xff);
|
||
break;
|
||
case 0x20:
|
||
PutMem(BAR, IAR[8] & 0xff);
|
||
BAR--;
|
||
PutMem(BAR, (IAR[8] >> 8) & 0xff);
|
||
break;
|
||
case 0x40:
|
||
PutMem(BAR, IAR[9] & 0xff);
|
||
BAR--;
|
||
PutMem(BAR, (IAR[9] >> 8) & 0xff);
|
||
break;
|
||
case 0x80:
|
||
PutMem(BAR, IAR[0] & 0xff);
|
||
BAR--;
|
||
PutMem(BAR, (IAR[0] >> 8) & 0xff);
|
||
break;
|
||
case 0x81:
|
||
PutMem(BAR, IAR[7] & 0xff);
|
||
BAR--;
|
||
PutMem(BAR, (IAR[7] >> 8) & 0xff);
|
||
break;
|
||
case 0x82:
|
||
PutMem(BAR, IAR[6] & 0xff);
|
||
BAR--;
|
||
PutMem(BAR, (IAR[6] >> 8) & 0xff);
|
||
break;
|
||
case 0x84:
|
||
PutMem(BAR, IAR[5] & 0xff);
|
||
BAR--;
|
||
PutMem(BAR, (IAR[5] >> 8) & 0xff);
|
||
break;
|
||
case 0x88:
|
||
PutMem(BAR, IAR[4] & 0xff);
|
||
BAR--;
|
||
PutMem(BAR, (IAR[4] >> 8) & 0xff);
|
||
break;
|
||
case 0x90:
|
||
PutMem(BAR, IAR[3] & 0xff);
|
||
BAR--;
|
||
PutMem(BAR, (IAR[3] >> 8) & 0xff);
|
||
break;
|
||
case 0xA0:
|
||
PutMem(BAR, IAR[2] & 0xff);
|
||
BAR--;
|
||
PutMem(BAR, (IAR[2] >> 8) & 0xff);
|
||
break;
|
||
case 0xC0:
|
||
PutMem(BAR, IAR[1] & 0xff);
|
||
BAR--;
|
||
PutMem(BAR, (IAR[1] >> 8) & 0xff);
|
||
break;
|
||
default:
|
||
reason = STOP_INVQ;
|
||
break;
|
||
}
|
||
break;
|
||
case 5: /* L: Load Register */
|
||
switch (qbyte) {
|
||
case 0x01:
|
||
XR1 = GetMem(BAR) & 0xff;
|
||
BAR--;
|
||
XR1 |= (GetMem(BAR) << 8) & 0xff00;
|
||
break;
|
||
case 0x02:
|
||
XR2 = GetMem(BAR) & 0xff;
|
||
BAR--;
|
||
XR2 |= (GetMem(BAR) << 8) & 0xff00;
|
||
break;
|
||
case 0x04:
|
||
PSR = GetMem(BAR) & 0xff;
|
||
BAR--;
|
||
break;
|
||
case 0x08:
|
||
ARR[level] = GetMem(BAR) & 0xff;
|
||
BAR--;
|
||
ARR[level] |= (GetMem(BAR) << 8) & 0xff00;
|
||
break;
|
||
case 0x10:
|
||
IAR[level] = GetMem(BAR) & 0xff;
|
||
BAR--;
|
||
IAR[level] |= (GetMem(BAR) << 8) & 0xff00;
|
||
PC = IAR[level];
|
||
break;
|
||
case 0x20:
|
||
IAR[8] = GetMem(BAR) & 0xff;
|
||
BAR--;
|
||
IAR[8] |= (GetMem(BAR) << 8) & 0xff00;
|
||
break;
|
||
case 0x40:
|
||
IAR[9] = GetMem(BAR) & 0xff;
|
||
BAR--;
|
||
IAR[9] |= (GetMem(BAR) << 8) & 0xff00;
|
||
break;
|
||
case 0x80:
|
||
IAR[0] = GetMem(BAR) & 0xff;
|
||
BAR--;
|
||
IAR[0] |= (GetMem(BAR) << 8) & 0xff00;
|
||
break;
|
||
case 0x81:
|
||
IAR[7] = GetMem(BAR) & 0xff;
|
||
BAR--;
|
||
IAR[7] |= (GetMem(BAR) << 8) & 0xff00;
|
||
break;
|
||
case 0x82:
|
||
IAR[6] = GetMem(BAR) & 0xff;
|
||
BAR--;
|
||
IAR[6] |= (GetMem(BAR) << 8) & 0xff00;
|
||
break;
|
||
case 0x84:
|
||
IAR[5] = GetMem(BAR) & 0xff;
|
||
BAR--;
|
||
IAR[5] |= (GetMem(BAR) << 8) & 0xff00;
|
||
break;
|
||
case 0x88:
|
||
IAR[4] = GetMem(BAR) & 0xff;
|
||
BAR--;
|
||
IAR[4] |= (GetMem(BAR) << 8) & 0xff00;
|
||
break;
|
||
case 0x90:
|
||
IAR[3] = GetMem(BAR) & 0xff;
|
||
BAR--;
|
||
IAR[3] |= (GetMem(BAR) << 8) & 0xff00;
|
||
break;
|
||
case 0xA0:
|
||
IAR[2] = GetMem(BAR) & 0xff;
|
||
BAR--;
|
||
IAR[2] |= (GetMem(BAR) << 8) & 0xff00;
|
||
break;
|
||
case 0xC0:
|
||
IAR[1] = GetMem(BAR) & 0xff;
|
||
BAR--;
|
||
IAR[1] |= (GetMem(BAR) << 8) & 0xff00;
|
||
break;
|
||
default:
|
||
reason = STOP_INVQ;
|
||
break;
|
||
}
|
||
break;
|
||
case 6: /* A: Add to Register */
|
||
IR = GetMem(BAR) & 0x00ff;
|
||
BAR--;
|
||
IR |= (GetMem(BAR) << 8) & 0xff00;
|
||
switch (qbyte) {
|
||
case 0x01:
|
||
IR += XR1;
|
||
XR1 = IR & AMASK;
|
||
break;
|
||
case 0x02:
|
||
IR += XR2;
|
||
XR2 = IR & AMASK;
|
||
break;
|
||
case 0x04:
|
||
IR += PSR;
|
||
PSR = IR & AMASK;
|
||
break;
|
||
case 0x08:
|
||
IR += ARR[level];
|
||
ARR[level] = IR & AMASK;
|
||
break;
|
||
case 0x10:
|
||
IR += IAR[level];
|
||
IAR[level] = IR & AMASK;
|
||
break;
|
||
case 0x20:
|
||
IR += IAR[8];
|
||
IAR[8] = IR & AMASK;
|
||
break;
|
||
case 0x40:
|
||
IR += IAR[9];
|
||
IAR[9] = IR & AMASK;
|
||
break;
|
||
case 0x80:
|
||
IR += IAR[0];
|
||
IAR[0] = IR & AMASK;
|
||
break;
|
||
case 0x81:
|
||
IR += IAR[7];
|
||
IAR[7] = IR & AMASK;
|
||
break;
|
||
case 0x82:
|
||
IR += IAR[6];
|
||
IAR[6] = IR & AMASK;
|
||
break;
|
||
case 0x84:
|
||
IR += IAR[5];
|
||
IAR[5] = IR & AMASK;
|
||
break;
|
||
case 0x88:
|
||
IR += IAR[4];
|
||
IAR[4] = IR & AMASK;
|
||
break;
|
||
case 0x90:
|
||
IR += IAR[3];
|
||
IAR[3] = IR & AMASK;
|
||
break;
|
||
case 0xA0:
|
||
IR += IAR[2];
|
||
IAR[2] = IR & AMASK;
|
||
break;
|
||
case 0xC0:
|
||
IR += IAR[1];
|
||
IAR[1] = IR & AMASK;
|
||
break;
|
||
default:
|
||
reason = STOP_INVQ;
|
||
break;
|
||
}
|
||
PSR &= 0xD8;
|
||
if ((IR & 0xffff) == 0)
|
||
PSR |= 0x01; /* Zero */
|
||
if ((IR & 0xffff) != 0 && !(IR & 0x10000))
|
||
PSR |= 0x02; /* Low */
|
||
if ((IR & 0xffff) != 0 && (IR & 0x10000))
|
||
PSR |= 0x04; /* High */
|
||
if ((IR & 0x10000))
|
||
PSR |= 0x20; /* Bin overflow */
|
||
break;
|
||
case 8: /* TBN: Test Bits On */
|
||
IR = GetMem(BAR);
|
||
PSR &= 0xFF;
|
||
if ((IR & qbyte) != qbyte)
|
||
PSR |= 0x10;
|
||
break;
|
||
case 9: /* TBF: Test Bits Off */
|
||
IR = GetMem(BAR);
|
||
PSR &= 0xFF;
|
||
if ((IR & qbyte))
|
||
PSR |= 0x10;
|
||
break;
|
||
case 0xa: /* SBN: Set Bits On */
|
||
IR = GetMem(BAR);
|
||
IR |= qbyte;
|
||
PutMem(BAR, IR);
|
||
break;
|
||
case 0xb: /* SBF: Set Bits Off */
|
||
IR = GetMem(BAR);
|
||
IR &= ~qbyte;
|
||
PutMem(BAR, IR);
|
||
break;
|
||
case 0xc: /* MVI: Move Immediate */
|
||
PutMem(BAR, qbyte);
|
||
break;
|
||
case 0xd: /* CLI: Compare Immediate */
|
||
PSR = compare(GetMem(BAR), qbyte, PSR);
|
||
break;
|
||
default:
|
||
reason = STOP_INVOP;
|
||
break;
|
||
}
|
||
IAR[level] = PC;
|
||
continue;
|
||
break;
|
||
case 0xc0:
|
||
case 0xd0:
|
||
case 0xe0:
|
||
switch (opcode) {
|
||
case 0: /* BC: Branch on Condition */
|
||
ARR[level] = AAR & AMASK;
|
||
if (condition(qbyte) == 1) {
|
||
IR = ARR[level];
|
||
ARR[level] = PC & AMASK;
|
||
PC = IR;
|
||
}
|
||
break;
|
||
case 1: /* TIO: Test I/O */
|
||
devno = (qbyte >> 4) & 0x0f;
|
||
devm = (qbyte >> 3) & 0x01;
|
||
devn = qbyte & 0x07;
|
||
op1 = dev_table[devno].routine(2, devm, devn, rbyte);
|
||
if (op1 & 0x01) {
|
||
ARR[level] = AAR & AMASK;
|
||
IR = ARR[level];
|
||
ARR[level] = PC & AMASK;
|
||
PC = IR;
|
||
}
|
||
reason = (op1 >> 16) & 0xffff;
|
||
break;
|
||
case 2: /* LA: Load Address */
|
||
switch (qbyte) {
|
||
case 1:
|
||
XR1 = AAR;
|
||
break;
|
||
case 2:
|
||
XR2 = AAR;
|
||
break;
|
||
default:
|
||
reason = STOP_INVQ;
|
||
break;
|
||
}
|
||
break;
|
||
default:
|
||
reason = STOP_INVOP;
|
||
break;
|
||
} /* switch (opcode) */
|
||
IAR[level] = PC;
|
||
continue;
|
||
|
||
default:
|
||
reason = STOP_INVOP;
|
||
break;
|
||
} /* switch (opaddr) */
|
||
|
||
} /* end while (reason == 0) */
|
||
|
||
/* Simulation halted */
|
||
|
||
saved_PC = PC;
|
||
return reason;
|
||
}
|
||
|
||
/* On models 4-12, these memory functions could be inline, but
|
||
on a model 15 with ATU address mapping must be performed so
|
||
they are in functions here for future expansion.
|
||
*/
|
||
|
||
/* Fetch a byte from memory */
|
||
|
||
int32 GetMem(int32 addr)
|
||
{
|
||
return M[addr] & 0xff;
|
||
}
|
||
|
||
/* Place a byte in memory */
|
||
|
||
int32 PutMem(int32 addr, int32 data)
|
||
{
|
||
M[addr] = data & 0xff;
|
||
return 0;
|
||
}
|
||
|
||
/* Check the condition register against the qbyte and return 1 if true */
|
||
|
||
int32 condition(int32 qbyte)
|
||
{
|
||
int32 r = 0, t, q;
|
||
t = (qbyte & 0xf0) >> 4;
|
||
q = qbyte & 0x0f;
|
||
if (qbyte & 0x80) { /* True if any condition tested = 1*/
|
||
if (((qbyte & 0x3f) & PSR) != 0) r = 1;
|
||
} else { /* True if all conditions tested = 0 */
|
||
if (((qbyte & 0x3f) & PSR) == 0) r = 1;
|
||
}
|
||
/* these bits reset by a test */
|
||
if (qbyte & 0x10)
|
||
PSR &= 0xEF; /* Reset test false if used */
|
||
if (qbyte & 0x08)
|
||
PSR &= 0xF7; /* Reset decimal overflow if tested */
|
||
if (qbyte == 0x00)
|
||
r = 1; /* unconditional branch */
|
||
if (qbyte == 0x80)
|
||
r = 0; /* force no branch */
|
||
if (t >=0 && t < 8 && (q == 7 || q == 0xf))
|
||
r = 0; /* no-op */
|
||
if (t > 7 && t < 0x10 && (q == 7 || q == 0xf))
|
||
r = 1; /* Force branch */
|
||
return (r);
|
||
}
|
||
/* Given operand 1 and operand 2, compares the two and returns
|
||
the System/3 condition register bits appropriately, given the
|
||
condition register initial state in parameter 3
|
||
*/
|
||
|
||
int32 compare(int32 byte1, int32 byte2, int32 cond)
|
||
{
|
||
int32 r;
|
||
|
||
r = cond & 0xF8; /* mask off all but unaffected bits 2,3,4 */
|
||
if (byte1 == byte2)
|
||
r |= 0x01; /* set equal bit */
|
||
if (byte1 < byte2)
|
||
r |= 0x02; /* set less-than bit */
|
||
if (byte1 > byte2)
|
||
r |= 0x04; /* set greater than bit */
|
||
return r;
|
||
}
|
||
|
||
/*-------------------------------------------------------------------*/
|
||
/* Add two zoned decimal operands */
|
||
/* */
|
||
/* Input: */
|
||
/* addr1 Logical address of packed decimal storage operand 1 */
|
||
/* len1 Length minus one of storage operand 1 (range 0-15) */
|
||
/* addr2 Logical address of packed decimal storage operand 2 */
|
||
/* len2 Length minus one of storage operand 2 (range 0-15) */
|
||
/* Output: */
|
||
/* The return value is the condition code: */
|
||
/* 0=result zero, 1=result -ve, 2=result +ve, 3=overflow */
|
||
/* */
|
||
/* A program check may be generated if either logical address */
|
||
/* causes an addressing, translation, or fetch protection */
|
||
/* exception, or if either operand causes a data exception */
|
||
/* because of invalid decimal digits or sign, or if the */
|
||
/* first operand is store protected. Depending on the PSW */
|
||
/* program mask, decimal overflow may cause a program check. */
|
||
/*-------------------------------------------------------------------*/
|
||
int32 add_zoned (int32 addr1, int32 len1, int32 addr2, int32 len2)
|
||
{
|
||
int cc; /* Condition code */
|
||
uint8 dec1[MAX_DECIMAL_DIGITS]; /* Work area for operand 1 */
|
||
uint8 dec2[MAX_DECIMAL_DIGITS]; /* Work area for operand 2 */
|
||
uint8 dec3[MAX_DECIMAL_DIGITS]; /* Work area for result */
|
||
int count1, count2, count3; /* Significant digit counters*/
|
||
int sign1, sign2, sign3; /* Sign of operands & result */
|
||
|
||
/* Load operands into work areas */
|
||
load_decimal (addr1, len1, dec1, &count1, &sign1);
|
||
load_decimal (addr2, len2, dec2, &count2, &sign2);
|
||
|
||
/* Add or subtract operand values */
|
||
if (count2 == 0)
|
||
{
|
||
/* If second operand is zero then result is first operand */
|
||
memcpy (dec3, dec1, MAX_DECIMAL_DIGITS);
|
||
count3 = count1;
|
||
sign3 = sign1;
|
||
}
|
||
else if (count1 == 0)
|
||
{
|
||
/* If first operand is zero then result is second operand */
|
||
memcpy (dec3, dec2, MAX_DECIMAL_DIGITS);
|
||
count3 = count2;
|
||
sign3 = sign2;
|
||
}
|
||
else if (sign1 == sign2)
|
||
{
|
||
/* If signs are equal then add operands */
|
||
add_decimal (dec1, dec2, dec3, &count3);
|
||
sign3 = sign1;
|
||
}
|
||
else
|
||
{
|
||
/* If signs are opposite then subtract operands */
|
||
subtract_decimal (dec1, dec2, dec3, &count3, &sign3);
|
||
if (sign1 < 0) sign3 = -sign3;
|
||
}
|
||
|
||
/* Set condition code */
|
||
cc = (count3 == 0) ? 0 : (sign3 < 1) ? 1 : 2;
|
||
|
||
/* Overflow if result exceeds first operand length */
|
||
if (count3 > len1)
|
||
cc = 3;
|
||
|
||
/* Set positive sign if result is zero */
|
||
if (count3 == 0)
|
||
sign3 = 1;
|
||
|
||
/* Store result into first operand location */
|
||
store_decimal (addr1, len1, dec3, sign3);
|
||
|
||
/* Return condition code */
|
||
return cc;
|
||
|
||
} /* end function add_packed */
|
||
|
||
/*-------------------------------------------------------------------*/
|
||
/* Subtract two zoned decimal operands */
|
||
/* */
|
||
/* Input: */
|
||
/* addr1 Logical address of packed decimal storage operand 1 */
|
||
/* len1 Length minus one of storage operand 1 (range 0-15) */
|
||
/* addr2 Logical address of packed decimal storage operand 2 */
|
||
/* len2 Length minus one of storage operand 2 (range 0-15) */
|
||
/* Output: */
|
||
/* The return value is the condition code: */
|
||
/* 0=result zero, 1=result -ve, 2=result +ve, 3=overflow */
|
||
/* */
|
||
/* A program check may be generated if either logical address */
|
||
/* causes an addressing, translation, or fetch protection */
|
||
/* exception, or if either operand causes a data exception */
|
||
/* because of invalid decimal digits or sign, or if the */
|
||
/* first operand is store protected. Depending on the PSW */
|
||
/* program mask, decimal overflow may cause a program check. */
|
||
/*-------------------------------------------------------------------*/
|
||
int32 subtract_zoned (int32 addr1, int32 len1, int32 addr2, int32 len2)
|
||
{
|
||
int cc; /* Condition code */
|
||
uint8 dec1[MAX_DECIMAL_DIGITS]; /* Work area for operand 1 */
|
||
uint8 dec2[MAX_DECIMAL_DIGITS]; /* Work area for operand 2 */
|
||
uint8 dec3[MAX_DECIMAL_DIGITS]; /* Work area for result */
|
||
int count1, count2, count3; /* Significant digit counters*/
|
||
int sign1, sign2, sign3; /* Sign of operands & result */
|
||
|
||
/* Load operands into work areas */
|
||
load_decimal (addr1, len1, dec1, &count1, &sign1);
|
||
load_decimal (addr2, len2, dec2, &count2, &sign2);
|
||
|
||
/* Add or subtract operand values */
|
||
if (count2 == 0)
|
||
{
|
||
/* If second operand is zero then result is first operand */
|
||
memcpy (dec3, dec1, MAX_DECIMAL_DIGITS);
|
||
count3 = count1;
|
||
sign3 = sign1;
|
||
}
|
||
else if (count1 == 0)
|
||
{
|
||
/* If first operand is zero then result is -second operand */
|
||
memcpy (dec3, dec2, MAX_DECIMAL_DIGITS);
|
||
count3 = count2;
|
||
sign3 = -sign2;
|
||
}
|
||
else if (sign1 != sign2)
|
||
{
|
||
/* If signs are opposite then add operands */
|
||
add_decimal (dec1, dec2, dec3, &count3);
|
||
sign3 = sign1;
|
||
}
|
||
else
|
||
{
|
||
/* If signs are equal then subtract operands */
|
||
subtract_decimal (dec1, dec2, dec3, &count3, &sign3);
|
||
if (sign1 < 0) sign3 = -sign3;
|
||
}
|
||
|
||
/* Set condition code */
|
||
cc = (count3 == 0) ? 0 : (sign3 < 1) ? 1 : 2;
|
||
|
||
/* Overflow if result exceeds first operand length */
|
||
if (count3 > len1)
|
||
cc = 3;
|
||
|
||
/* Set positive sign if result is zero */
|
||
if (count3 == 0)
|
||
sign3 = 1;
|
||
|
||
/* Store result into first operand location */
|
||
store_decimal (addr1, len1, dec3, sign3);
|
||
|
||
/* Return condition code */
|
||
return cc;
|
||
|
||
} /* end function subtract_packed */
|
||
|
||
|
||
/*-------------------------------------------------------------------*/
|
||
/* Add two decimal byte strings as unsigned decimal numbers */
|
||
/* */
|
||
/* Input: */
|
||
/* dec1 A 31-byte area containing the decimal digits of */
|
||
/* the first operand. Each byte contains one decimal */
|
||
/* digit in the low-order 4 bits of the byte. */
|
||
/* dec2 A 31-byte area containing the decimal digits of */
|
||
/* the second operand. Each byte contains one decimal */
|
||
/* digit in the low-order 4 bits of the byte. */
|
||
/* Output: */
|
||
/* result Points to a 31-byte area to contain the result */
|
||
/* digits. One decimal digit is placed in the low-order */
|
||
/* 4 bits of each byte. */
|
||
/* count Points to an integer to receive the number of */
|
||
/* digits in the result excluding leading zeroes. */
|
||
/* This field is set to zero if the result is all zero, */
|
||
/* or to MAX_DECIMAL_DIGITS+1 if overflow occurred. */
|
||
/*-------------------------------------------------------------------*/
|
||
static void add_decimal (uint8 *dec1, uint8 *dec2, uint8 *result, int32 *count)
|
||
{
|
||
int d; /* Decimal digit */
|
||
int i; /* Array subscript */
|
||
int n = 0; /* Significant digit counter */
|
||
int carry = 0; /* Carry indicator */
|
||
|
||
/* Add digits from right to left */
|
||
for (i = MAX_DECIMAL_DIGITS - 1; i >= 0; i--)
|
||
{
|
||
/* Add digits from first and second operands */
|
||
d = dec1[i] + dec2[i] + carry;
|
||
|
||
/* Check for carry into next digit */
|
||
if (d > 9) {
|
||
d -= 10;
|
||
carry = 1;
|
||
} else {
|
||
carry = 0;
|
||
}
|
||
|
||
/* Check for significant digit */
|
||
if (d != 0)
|
||
n = MAX_DECIMAL_DIGITS - i;
|
||
|
||
/* Store digit in result */
|
||
result[i] = d;
|
||
|
||
} /* end for */
|
||
|
||
/* Check for carry out of leftmost digit */
|
||
if (carry)
|
||
n = MAX_DECIMAL_DIGITS + 1;
|
||
|
||
/* Return significant digit counter */
|
||
*count = n;
|
||
|
||
} /* end function add_decimal */
|
||
|
||
/*-------------------------------------------------------------------*/
|
||
/* Subtract two decimal byte strings as unsigned decimal numbers */
|
||
/* */
|
||
/* Input: */
|
||
/* dec1 A 31-byte area containing the decimal digits of */
|
||
/* the first operand. Each byte contains one decimal */
|
||
/* digit in the low-order 4 bits of the byte. */
|
||
/* dec2 A 31-byte area containing the decimal digits of */
|
||
/* the second operand. Each byte contains one decimal */
|
||
/* digit in the low-order 4 bits of the byte. */
|
||
/* Output: */
|
||
/* result Points to a 31-byte area to contain the result */
|
||
/* digits. One decimal digit is placed in the low-order */
|
||
/* 4 bits of each byte. */
|
||
/* count Points to an integer to receive the number of */
|
||
/* digits in the result excluding leading zeroes. */
|
||
/* This field is set to zero if the result is all zero. */
|
||
/* sign -1 if the result is negative (operand2 > operand1) */
|
||
/* +1 if the result is positive (operand2 <= operand1) */
|
||
/*-------------------------------------------------------------------*/
|
||
static void subtract_decimal (uint8 *dec1, uint8 *dec2, uint8 *result, int *count, int *sign)
|
||
{
|
||
int d; /* Decimal digit */
|
||
int i; /* Array subscript */
|
||
int n = 0; /* Significant digit counter */
|
||
int borrow = 0; /* Borrow indicator */
|
||
int rc; /* Return code */
|
||
uint8 *higher; /* -> Higher value operand */
|
||
uint8 *lower; /* -> Lower value operand */
|
||
|
||
/* Compare digits to find which operand has higher numeric value */
|
||
rc = memcmp (dec1, dec2, MAX_DECIMAL_DIGITS);
|
||
|
||
/* Return positive zero result if both operands are equal */
|
||
if (rc == 0) {
|
||
memset (result, 0, MAX_DECIMAL_DIGITS);
|
||
*count = 0;
|
||
*sign = +1;
|
||
return;
|
||
}
|
||
|
||
/* Point to higher and lower value operands and set sign */
|
||
if (rc > 0) {
|
||
higher = dec1;
|
||
lower = dec2;
|
||
*sign = +1;
|
||
} else {
|
||
lower = dec1;
|
||
higher = dec2;
|
||
*sign = -1;
|
||
}
|
||
|
||
/* Subtract digits from right to left */
|
||
for (i = MAX_DECIMAL_DIGITS - 1; i >= 0; i--)
|
||
{
|
||
/* Subtract lower operand digit from higher operand digit */
|
||
d = higher[i] - lower[i] - borrow;
|
||
|
||
/* Check for borrow from next digit */
|
||
if (d < 0) {
|
||
d += 10;
|
||
borrow = 1;
|
||
} else {
|
||
borrow = 0;
|
||
}
|
||
|
||
/* Check for significant digit */
|
||
if (d != 0)
|
||
n = MAX_DECIMAL_DIGITS - i;
|
||
|
||
/* Store digit in result */
|
||
result[i] = d;
|
||
|
||
} /* end for */
|
||
|
||
/* Return significant digit counter */
|
||
*count = n;
|
||
|
||
} /* end function subtract_decimal */
|
||
|
||
/*-------------------------------------------------------------------*/
|
||
/* Load a zoned decimal storage operand into a decimal byte string */
|
||
/* */
|
||
/* Input: */
|
||
/* addr Logical address of zoned decimal storage operand */
|
||
/* len Length minus one of storage operand (range 0-15) */
|
||
/* Output: */
|
||
/* result Points to a 31-byte area into which the decimal */
|
||
/* digits are loaded. One decimal digit is loaded */
|
||
/* into the low-order 4 bits of each byte, and the */
|
||
/* result is padded to the left with high-order zeroes */
|
||
/* if the storage operand contains less than 31 digits. */
|
||
/* count Points to an integer to receive the number of */
|
||
/* digits in the result excluding leading zeroes. */
|
||
/* This field is set to zero if the result is all zero. */
|
||
/* sign Points to an integer which will be set to -1 if a */
|
||
/* negative sign was loaded from the operand, or +1 if */
|
||
/* a positive sign was loaded from the operand. */
|
||
/* */
|
||
/* A program check may be generated if the logical address */
|
||
/* causes an addressing, translation, or fetch protection */
|
||
/* exception, or if the operand causes a data exception */
|
||
/* because of invalid decimal digits or sign. */
|
||
/*-------------------------------------------------------------------*/
|
||
static void load_decimal (int32 addr, int32 len, uint8 *result, int32 *count, int32 *sign)
|
||
{
|
||
int h; /* Hexadecimal digit */
|
||
int i, j; /* Array subscripts */
|
||
int n; /* Significant digit counter */
|
||
|
||
if ((GetMem(addr) & 0xf0) == 0xD0)
|
||
*sign = -1;
|
||
else
|
||
*sign = 1;
|
||
j = len;
|
||
for (i = MAX_DECIMAL_DIGITS; i > -1; i--) {
|
||
if (j) {
|
||
h = GetMem(addr) & 0x0f;
|
||
addr--;
|
||
j--;
|
||
} else {
|
||
h = 0;
|
||
}
|
||
result [i-1] = h;
|
||
if (h > 0) n = i;
|
||
}
|
||
*count = 32 - n;
|
||
|
||
} /* end function load_decimal */
|
||
|
||
/*-------------------------------------------------------------------*/
|
||
/* Store decimal byte string into packed decimal storage operand */
|
||
/* */
|
||
/* Input: */
|
||
/* addr Logical address of packed decimal storage operand */
|
||
/* len Length minus one of storage operand (range 0-15) */
|
||
/* dec A 31-byte area containing the decimal digits to be */
|
||
/* stored. Each byte contains one decimal digit in */
|
||
/* the low-order 4 bits of the byte. */
|
||
/* sign -1 if a negative sign is to be stored, or +1 if a */
|
||
/* positive sign is to be stored. */
|
||
/* */
|
||
/* A program check may be generated if the logical address */
|
||
/* causes an addressing, translation, or protection exception. */
|
||
/*-------------------------------------------------------------------*/
|
||
static void store_decimal (int32 addr, int32 len, uint8 *dec, int sign)
|
||
{
|
||
int i, j, a; /* Array subscripts */
|
||
|
||
j = len;
|
||
a = addr;
|
||
for (i = MAX_DECIMAL_DIGITS; i > -1; i--) {
|
||
if (j) {
|
||
PutMem(a, (dec[i-1] | 0xf0));
|
||
a--;
|
||
j--;
|
||
} else {
|
||
break;
|
||
}
|
||
}
|
||
if (sign == -1) {
|
||
PutMem(addr, (GetMem(addr) & 0x0f));
|
||
PutMem(addr, (GetMem(addr) | 0xf0));
|
||
}
|
||
|
||
} /* end function store_decimal */
|
||
|
||
/* CPU Device Control */
|
||
|
||
int32 cpu (int32 op, int32 m, int32 n, int32 data)
|
||
{
|
||
int32 iodata = 0;
|
||
|
||
switch (op) {
|
||
case 0x00: /* Start IO */
|
||
return SCPE_OK;
|
||
case 0x01: /* LIO */
|
||
return SCPE_OK;
|
||
case 0x02: /* TIO */
|
||
break;
|
||
case 0x03: /* SNS */
|
||
/* SNS CPU gets the data switches */
|
||
iodata = SR;
|
||
break;
|
||
case 0x04: /* APL */
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
return ((SCPE_OK << 16) | iodata);
|
||
}
|
||
|
||
|
||
|
||
/* Null device */
|
||
|
||
int32 nulldev (int32 opcode, int32 m, int32 n, int32 data)
|
||
{
|
||
if (opcode == 1)
|
||
return SCPE_OK; /* Ok to LIO unconfigured devices? */
|
||
return STOP_INVDEV;
|
||
}
|
||
|
||
/* Reset routine */
|
||
|
||
t_stat cpu_reset (DEVICE *dptr)
|
||
{
|
||
int_req = 0;
|
||
level = 8;
|
||
sim_brk_types = sim_brk_dflt = SWMASK ('E');
|
||
return SCPE_OK;
|
||
}
|
||
|
||
/* Memory examine */
|
||
|
||
t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw)
|
||
{
|
||
if (addr >= MEMSIZE) return SCPE_NXM;
|
||
if (vptr != NULL) *vptr = M[addr] & 0xff;
|
||
return SCPE_OK;
|
||
}
|
||
|
||
/* Memory deposit */
|
||
|
||
t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw)
|
||
{
|
||
if (addr >= MEMSIZE) return SCPE_NXM;
|
||
M[addr] = val & 0xff;
|
||
return SCPE_OK;
|
||
}
|
||
|
||
t_stat cpu_set_size (UNIT *uptr, int32 val, char *cptr, void *desc)
|
||
{
|
||
int32 mc = 0;
|
||
uint32 i;
|
||
|
||
if ((val <= 0) || (val > MAXMEMSIZE) || ((val & 07777) != 0))
|
||
return SCPE_ARG;
|
||
for (i = val; i < MEMSIZE; i++) mc = mc | M[i];
|
||
if ((mc != 0) && (!get_yn ("Really truncate memory [N]?", FALSE)))
|
||
return SCPE_OK;
|
||
MEMSIZE = val;
|
||
for (i = MEMSIZE; i < MAXMEMSIZE; i++) M[i] = 0;
|
||
return SCPE_OK;
|
||
}
|
||
|
||
t_stat cpu_boot (int32 unitno, DEVICE *dptr)
|
||
{
|
||
level = 8;
|
||
IAR[8] = 0;
|
||
return SCPE_OK;
|
||
}
|