| /* s3_cpu.c: IBM System/3 CPU simulator | |
| Copyright (c) 2001-2012, 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 | |
| 19-Mar-12 RMS Changed int to int32 in declarations (Mark Pizzolato) | |
| 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; | |
| 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, CONST 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); | |
| int32 nulldev (int32 opcode, int32 m, int32 n, int32 data); | |
| int32 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, int *count, int *sign); | |
| static void add_decimal (uint8 *dec1, uint8 *dec2, uint8 *result, int *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) | |
| { | |
| 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 */ | |
| sim_printf("\n"); | |
| for (i = 0; i < 3; i++) { | |
| for (j = 0; j < 9; j++) { | |
| sim_printf ("%c", display[i][j]); | |
| } | |
| sim_printf ("\n"); | |
| } | |
| 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 */ | |
| static 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 | |
| */ | |
| static 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, int *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, int *count, int *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, CONST 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; | |
| } |