RESTRICTION: The HP DS disk is not debugged. DO NOT enable this feature for normal operations. WARNING: Massive changes in the PDP-11 make all previous SAVEd file obsolete. Do not attempt to use a PDP-11 SAVE file from a prior release with V3.3! 1. New Features in 3.3 1.1 SCP - Added -p (powerup) qualifier to RESET - Changed SET <unit> ONLINE/OFFLINE to SET <unit> ENABLED/DISABLED - Moved SET DEBUG under SET CONSOLE hierarchy - Added optional parameter value to SHOW command - Added output file option to SHOW command 1.2 PDP-11 - Separated RH Massbus adapter from RP controller - Added TU tape support - Added model emulation framework - Added model details 1.3 VAX - Separated out CVAX-specific features from core instruction simulator - Implemented capability for CIS, octaword, compatibility mode instructions - Added instruction display and parse for compatibility mode - Changed SET CPU VIRTUAL=n to SHOW CPU VIRTUAL=n - Added =n optional parameter to SHOW CPU HISTORY 1.4 Unibus/Qbus simulators (PDP-11, VAX, PDP-10) - Simplified DMA API's - Modified DMA peripherals to use simplified API's 1.5 HP2100 (all changes from Dave Bryan) CPU - moved MP into its own device; added MP option jumpers - modified DMA to allow disabling - modified SET CPU 2100/2116 to truncate memory > 32K - added -F switch to SET CPU to force memory truncation - modified WRU to be REG_HRO - added BRK and DEL to save console settings DR - provided protected tracks and "Writing Enabled" status bit - added "parity error" status return on writes for 12606 - added track origin test for 12606 - added SCP test for 12606 - added "Sector Flag" status bit - added "Read Inhibit" status bit for 12606 - added TRACKPROT modifier LPS - added SET OFFLINE/ONLINE, POWEROFF/POWERON - added fast/realistic timing - added debug printouts LPT - added SET OFFLINE/ONLINE, POWEROFF/POWERON PTR - added paper tape loop mode, DIAG/READER modifiers to PTR - added PV_LEFT to PTR TRLLIM register CLK - modified CLK to permit disable 1.6 IBM 1401, IBM 1620, Interdata 16b, SDS 940, PDP-10 - Added instruction history 1.7 H316, PDP-15, PDP-8 - Added =n optional value to SHOW CPU HISTORY 2. Bugs Fixed in 3.3 2.1 SCP - Fixed comma-separated SET options (from Dave Bryan) - Fixed duplicate HELP displays with user-specified commands 2.2 PDP-10 - Replicated RP register state per drive - Fixed TU to set FCE on short record - Fixed TU to return bit<15> in drive type - Fixed TU format specification, 1:0 are don't cares - Fixed TU handling of TMK status - Fixed TU handling of DONE, ATA at end of operation - Implemented TU write check 2.3 PDP-11 - Replicated RP register state per drive - Fixed RQ, TQ to report correct controller type and stage 1 configuration flags on a Unibus system - Fixed HK CS2<output_ready> flag 2.4 VAX - Fixed parsing of indirect displacement modes in instruction input 2.5 HP2100 (all fixes from Dave Bryan) CPU - fixed S-register behavior on 2116 - fixed LIx/MIx behavior for DMA on 2116 and 2100 - fixed LIx/MIx behavior for empty I/O card slots DP - fixed enable/disable from either device - fixed ANY ERROR status for 12557A interface - fixed unattached drive status for 12557A interface - status cmd without prior STC DC now completes (12557A) - OTA/OTB CC on 13210A interface also does CLC CC - fixed RAR model - fixed seek check on 13210 if sector out of range DQ - fixed enable/disable from either device - shortened xtime from 5 to 3 (drive avg 156KW/second) - fixed not ready/any error status - fixed RAR model DR - fixed enable/disable from either device - fixed sector return in status word - fixed DMA last word write, incomplete sector fill value - fixed 12610 SFC operation - fixed current-sector determination IPL - fixed enable/disable from either device LPS - fixed status returns for error conditions - fixed handling of non-printing characters - fixed handling of characters after column 80 - improved timing model accuracy for RTE LPT - fixed status returns for error conditions - fixed TOF handling so form remains on line 0 SYS - fixed display of CCA/CCB/CCE instructions 2.5 PDP-15 FPP - fixed URFST to mask low 9b of fraction - fixed exception PC setting
1668 lines
56 KiB
C
1668 lines
56 KiB
C
/* i1401_cpu.c: IBM 1401 CPU simulator
|
||
|
||
Copyright (c) 1993-2004, Robert M. Supnik
|
||
|
||
Permission is hereby granted, free of charge, to any person obtaining a
|
||
copy of this software and associated documentation files (the "Software"),
|
||
to deal in the Software without restriction, including without limitation
|
||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||
and/or sell copies of the Software, and to permit persons to whom the
|
||
Software is furnished to do so, subject to the following conditions:
|
||
|
||
The above copyright notice and this permission notice shall be included in
|
||
all copies or substantial portions of the Software.
|
||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||
ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||
|
||
Except as contained in this notice, the name of Robert M Supnik shall not
|
||
be used in advertising or otherwise to promote the sale, use or other dealings
|
||
in this Software without prior written authorization from Robert M Supnik.
|
||
|
||
14-Nov-04 WVS Added column binary support, debug support
|
||
06-Nov-04 RMS Added instruction history
|
||
12-Jul-03 RMS Moved ASCII/BCD tables to included file
|
||
Revised fetch to model hardware
|
||
Removed length checking in fetch phase
|
||
16-Mar-03 RMS 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
|
||
03-Jun-03 RMS Added 1311 support
|
||
22-May-02 RMS Added multiply and divide
|
||
30-Dec-01 RMS Added old PC queue
|
||
30-Nov-01 RMS Added extended SET/SHOW support
|
||
10-Aug-01 RMS Removed register in declarations
|
||
07-Dec-00 RMS Fixed bugs found by Charles Owen
|
||
-- 4,7 char NOPs are legal
|
||
-- 1 char B is chained BCE
|
||
-- MCE moves whole char after first
|
||
14-Apr-99 RMS Changed t_addr to unsigned
|
||
|
||
The register state for the IBM 1401 is:
|
||
|
||
IS I storage address register (PC)
|
||
AS A storage address register (address of first operand)
|
||
BS B storage address register (address of second operand)
|
||
ind[0:63] indicators
|
||
SSA sense switch A
|
||
IOCHK I/O check
|
||
PRCHK process check
|
||
|
||
The IBM 1401 is a variable instruction length, decimal data system.
|
||
Memory consists of 4000, 8000, 12000, or 16000 BCD characters, each
|
||
containing six bits of data and a word mark. There are no general
|
||
registers; all instructions are memory to memory, using explicit
|
||
addresses or an address pointer from a prior instruction.
|
||
|
||
BCD numeric data consists of the low four bits of a character (DIGIT),
|
||
encoded as X, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, X, X, X, X, X. The high
|
||
two bits (ZONE) encode the sign of the data as +, +, -, +. Character
|
||
data uses all six bits of a character. Numeric and character fields are
|
||
delimited by a word mark. Fields are typically processed in descending
|
||
address order (low-order data to high-order data).
|
||
|
||
The 1401 encodes a decimal address, and an index register number, in
|
||
three characters:
|
||
|
||
character zone digit
|
||
addr + 0 <1:0> of thousands hundreds
|
||
addr + 1 index register # tens
|
||
addr + 2 <3:2> of thousands ones
|
||
|
||
Normally the digit values 0, 11, 12, 13, 14, 15 are illegal in addresses.
|
||
However, in indexing, digits are passed through the adder, and illegal
|
||
values are normalized to legal counterparts.
|
||
|
||
The 1401 has six instruction formats:
|
||
|
||
op A and B addresses, if any, from AS and BS
|
||
op d A and B addresses, if any, from AS and BS
|
||
op aaa B address, if any, from BS
|
||
op aaa d B address, if any, from BS
|
||
op aaa bbb
|
||
op aaa bbb d
|
||
|
||
where aaa is the A address, bbb is the B address, and d is a modifier.
|
||
The opcode has word mark set; all other characters have word mark clear.
|
||
*/
|
||
|
||
/* This routine is the instruction decode routine for the IBM 1401.
|
||
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
|
||
illegal addresses or instruction formats
|
||
I/O error in I/O simulator
|
||
|
||
2. Interrupts. The 1401 has no interrupt structure.
|
||
|
||
3. Non-existent memory. On the 1401, references to non-existent
|
||
memory halt the processor.
|
||
|
||
4. Adding I/O devices. These modules must be modified:
|
||
|
||
i1401_cpu.c add device dispatching code to iodisp
|
||
i1401_sys.c add sim_devices table entry
|
||
*/
|
||
|
||
#include "i1401_defs.h"
|
||
#include "i1401_dat.h"
|
||
|
||
#define PCQ_SIZE 64 /* must be 2**n */
|
||
#define PCQ_MASK (PCQ_SIZE - 1)
|
||
#define PCQ_ENTRY pcq[pcq_p = (pcq_p - 1) & PCQ_MASK] = saved_IS
|
||
|
||
#define HIST_MIN 64
|
||
#define HIST_MAX 65536
|
||
struct InstHistory {
|
||
uint16 is;
|
||
uint16 ilnt;
|
||
uint8 inst[MAX_L]; };
|
||
|
||
/* These macros validate addresses. If an addresses error is detected,
|
||
they return an error status to the caller. These macros should only
|
||
be used in a routine that returns a t_stat value.
|
||
*/
|
||
|
||
#define MM(x) x = x - 1; \
|
||
if (x < 0) { \
|
||
x = BA + MAXMEMSIZE - 1; \
|
||
reason = STOP_WRAP; \
|
||
break; }
|
||
|
||
#define PP(x) x = x + 1; \
|
||
if (ADDR_ERR (x)) { \
|
||
x = BA + (x % MAXMEMSIZE); \
|
||
reason = STOP_WRAP; \
|
||
break; }
|
||
|
||
#define BRANCH if (ADDR_ERR (AS)) { \
|
||
reason = STOP_INVBR; \
|
||
break; } \
|
||
if (cpu_unit.flags & XSA) BS = IS; \
|
||
else BS = BA + 0; \
|
||
PCQ_ENTRY; \
|
||
IS = AS;
|
||
|
||
uint8 M[MAXMEMSIZE] = { 0 }; /* main memory */
|
||
int32 saved_IS = 0; /* saved IS */
|
||
int32 AS = 0; /* AS */
|
||
int32 BS = 0; /* BS */
|
||
int32 D = 0; /* modifier */
|
||
int32 as_err = 0, bs_err = 0; /* error flags */
|
||
int32 hb_pend = 0; /* halt br pending */
|
||
uint16 pcq[PCQ_SIZE] = { 0 }; /* PC queue */
|
||
int32 pcq_p = 0; /* PC queue ptr */
|
||
REG *pcq_r = NULL; /* PC queue reg ptr */
|
||
int32 ind[64] = { 0 }; /* indicators */
|
||
int32 ssa = 1; /* sense switch A */
|
||
int32 prchk = 0; /* process check stop */
|
||
int32 iochk = 0; /* I/O check stop */
|
||
int32 hst_p = 0; /* history pointer */
|
||
int32 hst_lnt = 0; /* history length */
|
||
struct InstHistory *hst = NULL; /* instruction history */
|
||
|
||
extern int32 sim_int_char;
|
||
extern int32 sim_emax;
|
||
extern t_value *sim_eval;
|
||
extern FILE *sim_deb;
|
||
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_set_hist (UNIT *uptr, int32 val, char *cptr, void *desc);
|
||
t_stat cpu_show_hist (FILE *st, UNIT *uptr, int32 val, void *desc);
|
||
int32 store_addr_h (int32 addr);
|
||
int32 store_addr_t (int32 addr);
|
||
int32 store_addr_u (int32 addr);
|
||
int32 div_add (int32 ap, int32 bp, int32 aend);
|
||
int32 div_sub (int32 ap, int32 bp, int32 aend);
|
||
void div_sign (int32 dvrc, int32 dvdc, int32 qp, int32 rp);
|
||
t_stat iomod (int32 ilnt, int32 mod, const int32 *tptr);
|
||
t_stat iodisp (int32 dev, int32 unit, int32 flag, int32 mod);
|
||
|
||
extern t_stat read_card (int32 ilnt, int32 mod);
|
||
extern t_stat punch_card (int32 ilnt, int32 mod);
|
||
extern t_stat select_stack (int32 mod);
|
||
extern t_stat carriage_control (int32 mod);
|
||
extern t_stat write_line (int32 ilnt, int32 mod);
|
||
extern t_stat inq_io (int32 flag, int32 mod);
|
||
extern t_stat mt_io (int32 unit, int32 flag, int32 mod);
|
||
extern t_stat dp_io (int32 fnc, int32 flag, int32 mod);
|
||
extern t_stat mt_func (int32 unit, int32 mod);
|
||
extern t_stat sim_activate (UNIT *uptr, int32 delay);
|
||
extern t_stat fprint_sym (FILE *of, t_addr addr, t_value *val, UNIT *uptr, int32 sw);
|
||
|
||
/* CPU data structures
|
||
|
||
cpu_dev CPU device descriptor
|
||
cpu_unit CPU unit descriptor
|
||
cpu_reg CPU register list
|
||
cpu_mod CPU modifier list
|
||
*/
|
||
|
||
UNIT cpu_unit = { UDATA (NULL, UNIT_FIX + UNIT_BCD + STDOPT,
|
||
MAXMEMSIZE) };
|
||
|
||
REG cpu_reg[] = {
|
||
{ DRDATA (IS, saved_IS, 14), PV_LEFT },
|
||
{ DRDATA (AS, AS, 14), PV_LEFT },
|
||
{ DRDATA (BS, BS, 14), PV_LEFT },
|
||
{ FLDATA (ASERR, as_err, 0) },
|
||
{ FLDATA (BSERR, bs_err, 0) },
|
||
{ ORDATA (D, D, 7) },
|
||
{ FLDATA (SSA, ssa, 0) },
|
||
{ FLDATA (SSB, ind[IN_SSB], 0) },
|
||
{ FLDATA (SSC, ind[IN_SSC], 0) },
|
||
{ FLDATA (SSD, ind[IN_SSD], 0) },
|
||
{ FLDATA (SSE, ind[IN_SSE], 0) },
|
||
{ FLDATA (SSF, ind[IN_SSF], 0) },
|
||
{ FLDATA (SSG, ind[IN_SSG], 0) },
|
||
{ FLDATA (EQU, ind[IN_EQU], 0) },
|
||
{ FLDATA (UNEQ, ind[IN_UNQ], 0) },
|
||
{ FLDATA (HIGH, ind[IN_HGH], 0) },
|
||
{ FLDATA (LOW, ind[IN_LOW], 0) },
|
||
{ FLDATA (OVF, ind[IN_OVF], 0) },
|
||
{ FLDATA (IOCHK, iochk, 0) },
|
||
{ FLDATA (PRCHK, prchk, 0) },
|
||
{ FLDATA (HBPEND, hb_pend, 0) },
|
||
{ BRDATA (ISQ, pcq, 10, 14, PCQ_SIZE), REG_RO+REG_CIRC },
|
||
{ DRDATA (ISQP, pcq_p, 6), REG_HRO },
|
||
{ ORDATA (WRU, sim_int_char, 8) },
|
||
{ NULL } };
|
||
|
||
MTAB cpu_mod[] = {
|
||
{ XSA, XSA, "XSA", "XSA", NULL },
|
||
{ XSA, 0, "no XSA", "NOXSA", NULL },
|
||
{ HLE, HLE, "HLE", "HLE", NULL },
|
||
{ HLE, 0, "no HLE", "NOHLE", NULL },
|
||
{ BBE, BBE, "BBE", "BBE", NULL },
|
||
{ BBE, 0, "no BBE", "NOBBE", NULL },
|
||
{ MA, MA, "MA", 0, NULL },
|
||
{ MA, 0, "no MA", 0, NULL },
|
||
{ MR, MR, "MR", "MR", NULL },
|
||
{ MR, 0, "no MR", "NOMR", NULL },
|
||
{ EPE, EPE, "EPE", "EPE", NULL },
|
||
{ EPE, 0, "no EPE", "NOEPE", NULL },
|
||
{ MDV, MDV, "MDV", "MDV", NULL },
|
||
{ MDV, 0, "no MDV", "NOMDV", NULL },
|
||
{ UNIT_MSIZE, 4000, NULL, "4K", &cpu_set_size },
|
||
{ UNIT_MSIZE, 8000, NULL, "8K", &cpu_set_size },
|
||
{ UNIT_MSIZE, 12000, NULL, "12K", &cpu_set_size },
|
||
{ UNIT_MSIZE, 16000, NULL, "16K", &cpu_set_size },
|
||
{ MTAB_XTD|MTAB_VDV|MTAB_NMO|MTAB_SHP, 0, "HISTORY", "HISTORY",
|
||
&cpu_set_hist, &cpu_show_hist },
|
||
{ 0 } };
|
||
|
||
DEVICE cpu_dev = {
|
||
"CPU", &cpu_unit, cpu_reg, cpu_mod,
|
||
1, 10, 14, 1, 8, 7,
|
||
&cpu_ex, &cpu_dep, &cpu_reset,
|
||
NULL, NULL, NULL,
|
||
NULL, DEV_DEBUG };
|
||
|
||
/* Tables */
|
||
|
||
/* Opcode table - length, dispatch, and option flags. This table is
|
||
used by the symbolic input routine to validate instruction lengths */
|
||
|
||
const int32 op_table[64] = {
|
||
0, /* 00: illegal */
|
||
L1 | L2 | L4 | L5, /* read */
|
||
L1 | L2 | L4 | L5, /* write */
|
||
L1 | L2 | L4 | L5, /* write and read */
|
||
L1 | L2 | L4 | L5, /* punch */
|
||
L1 | L4, /* read and punch */
|
||
L1 | L2 | L4 | L5, /* write and read */
|
||
L1 | L2 | L4 | L5, /* write, read, punch */
|
||
L1, /* 10: read feed */
|
||
L1, /* punch feed */
|
||
0, /* illegal */
|
||
L1 | L4 | L7 | AREQ | BREQ | MA, /* modify address */
|
||
L1 | L4 | L7 | AREQ | BREQ | MDV, /* multiply */
|
||
0, /* illegal */
|
||
0, /* illegal */
|
||
0, /* illegal */
|
||
0, /* 20: illegal */
|
||
L1 | L4 | L7 | BREQ | NOWM, /* clear storage */
|
||
L1 | L4 | L7 | AREQ | BREQ, /* subtract */
|
||
0, /* illegal */
|
||
L5 | IO, /* magtape */
|
||
L1 | L8 | BREQ, /* branch wm or zone */
|
||
L1 | L8 | BREQ | BBE, /* branch if bit eq */
|
||
0, /* illegal */
|
||
L1 | L4 | L7 | AREQ | BREQ, /* 30: move zones */
|
||
L1 | L4 | L7 | AREQ | BREQ, /* move supress zero */
|
||
0, /* illegal */
|
||
L1 | L4 | L7 | AREQ | BREQ | NOWM, /* set word mark */
|
||
L1 | L4 | L7 | AREQ | BREQ | MDV, /* divide */
|
||
0, /* illegal */
|
||
0, /* illegal */
|
||
0, /* illegal */
|
||
0, /* 40: illegal */
|
||
0, /* illegal */
|
||
L2 | L5, /* select stacker */
|
||
L1 | L4 | L7 | L8 | BREQ | MLS | IO, /* load */
|
||
L1 | L4 | L7 | L8 | BREQ | MLS | IO, /* move */
|
||
HNOP | L1 | L2 | L4 | L5 | L7 | L8, /* nop */
|
||
0, /* illegal */
|
||
L1 | L4 | L7 | AREQ | BREQ | MR, /* move to record */
|
||
L1 | L4 | AREQ | MLS, /* 50: store A addr */
|
||
0, /* illegal */
|
||
L1 | L4 | L7 | AREQ | BREQ, /* zero and subtract */
|
||
0, /* illegal */
|
||
0, /* illegal */
|
||
0, /* illegal */
|
||
0, /* illegal */
|
||
0, /* illegal */
|
||
0, /* 60: illegal */
|
||
L1 | L4 | L7 | AREQ | BREQ, /* add */
|
||
L1 | L4 | L5 | L8, /* branch */
|
||
L1 | L4 | L7 | AREQ | BREQ, /* compare */
|
||
L1 | L4 | L7 | AREQ | BREQ, /* move numeric */
|
||
L1 | L4 | L7 | AREQ | BREQ, /* move char edit */
|
||
L2 | L5, /* carriage control */
|
||
0, /* illegal */
|
||
L1 | L4 | L7 | AREQ | MLS, /* 70: store B addr */
|
||
0, /* illegal */
|
||
L1 | L4 | L7 | AREQ | BREQ, /* zero and add */
|
||
HNOP | L1 | L2 | L4 | L5 | L7 | L8, /* halt */
|
||
L1 | L4 | L7 | AREQ | BREQ, /* clear word mark */
|
||
0, /* illegal */
|
||
0, /* illegal */
|
||
0 }; /* illegal */
|
||
|
||
const int32 len_table[9] = { 0, L1, L2, 0, L4, L5, 0, L7, L8 };
|
||
|
||
/* Address character conversion tables. Illegal characters are marked by
|
||
the flag BA but also contain the post-adder value for indexing */
|
||
|
||
const int32 hun_table[64] = {
|
||
BA+000, 100, 200, 300, 400, 500, 600, 700,
|
||
800, 900, 000, BA+300, BA+400, BA+500, BA+600, BA+700,
|
||
BA+1000, 1100, 1200, 1300, 1400, 1500, 1600, 1700,
|
||
1800, 1900, 1000, BA+1300, BA+1400, BA+1500, BA+1600, BA+1700,
|
||
BA+2000, 2100, 2200, 2300, 2400, 2500, 2600, 2700,
|
||
2800, 2900, 2000, BA+2300, BA+2400, BA+2500, BA+2600, BA+2700,
|
||
BA+3000, 3100, 3200, 3300, 3400, 3500, 3600, 3700,
|
||
3800, 3900, 3000, BA+3300, BA+3400, BA+3500, BA+3600, BA+3700 };
|
||
|
||
const int32 ten_table[64] = {
|
||
BA+00, 10, 20, 30, 40, 50, 60, 70,
|
||
80, 90, 00, BA+30, BA+40, BA+50, BA+60, BA+70,
|
||
X1+00, X1+10, X1+20, X1+30, X1+40, X1+50, X1+60, X1+70,
|
||
X1+80, X1+90, X1+00, X1+30, X1+40, X1+50, X1+60, X1+70,
|
||
X2+00, X2+10, X2+20, X2+30, X2+40, X2+50, X2+60, X2+70,
|
||
X2+80, X2+90, X2+00, X2+30, X2+40, X2+50, X2+60, X2+70,
|
||
X3+00, X3+10, X3+20, X3+30, X3+40, X3+50, X3+60, X3+70,
|
||
X3+80, X3+90, X3+00, X3+30, X3+40, X3+50, X3+60, X3+70 };
|
||
|
||
const int32 one_table[64] = {
|
||
BA+0, 1, 2, 3, 4, 5, 6, 7,
|
||
8, 9, 0, BA+3, BA+4, BA+5, BA+6, BA+7,
|
||
BA+4000, 4001, 4002, 4003, 4004, 4005, 4006, 4007,
|
||
4008, 4009, 4000, BA+4003, BA+4004, BA+4005, BA+4006, BA+4007,
|
||
BA+8000, 8001, 8002, 8003, 8004, 8005, 8006, 8007,
|
||
8008, 8009, 8000, BA+8003, BA+8004, BA+8005, BA+8006, BA+8007,
|
||
BA+12000, 12001, 12002, 12003, 12004, 12005, 12006, 12007,
|
||
12008, 12009, 12000, BA+12003, BA+12004, BA+12005, BA+12006, BA+12007 };
|
||
|
||
const int32 bin_to_bcd[16] = {
|
||
10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
|
||
|
||
const int32 bcd_to_bin[16] = {
|
||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 3, 4, 5, 6, 7 };
|
||
|
||
/* Indicator resets - a 1 marks an indicator that resets when tested */
|
||
|
||
static const int32 ind_table[64] = {
|
||
0, 0, 0, 0, 0, 0, 0, 0, /* 00 - 07 */
|
||
0, 0, 0, 0, 0, 0, 0, 0, /* 10 - 17 */
|
||
0, 0, 0, 0, 0, 0, 0, 0, /* 20 - 27 */
|
||
0, 1, 1 , 0, 1, 0, 0, 0, /* 30 - 37 */
|
||
0, 0, 1, 0, 0, 0, 0, 0, /* 40 - 47 */
|
||
0, 0, 1, 0, 1, 0, 0, 0, /* 50 - 57 */
|
||
0, 0, 0, 0, 0, 0, 0, 0, /* 60 - 67 */
|
||
0, 0, 1, 0, 0, 0, 0, 0 }; /* 70 - 77 */
|
||
|
||
/* Character collation table for compare with HLE option */
|
||
|
||
static const int32 col_table[64] = {
|
||
000, 067, 070, 071, 072, 073, 074, 075,
|
||
076, 077, 066, 024, 025, 026, 027, 030,
|
||
023, 015, 056, 057, 060, 061, 062, 063,
|
||
064, 065, 055, 016, 017, 020, 021, 022,
|
||
014, 044, 045, 046, 047, 050, 051, 052,
|
||
053, 054, 043, 007, 010, 011, 012, 013,
|
||
006, 032, 033, 034, 035, 036, 037, 040,
|
||
041, 042, 031, 001, 002, 003, 004, 005 };
|
||
|
||
/* Summing table for two decimal digits, converted back to BCD
|
||
Also used for multiplying two decimal digits, converted back to BCD,
|
||
with carry forward
|
||
*/
|
||
|
||
static const int32 sum_table[100] = {
|
||
BCD_ZERO, BCD_ONE, BCD_TWO, BCD_THREE, BCD_FOUR,
|
||
BCD_FIVE, BCD_SIX, BCD_SEVEN, BCD_EIGHT, BCD_NINE,
|
||
BCD_ZERO, BCD_ONE, BCD_TWO, BCD_THREE, BCD_FOUR,
|
||
BCD_FIVE, BCD_SIX, BCD_SEVEN, BCD_EIGHT, BCD_NINE,
|
||
BCD_ZERO, BCD_ONE, BCD_TWO, BCD_THREE, BCD_FOUR,
|
||
BCD_FIVE, BCD_SIX, BCD_SEVEN, BCD_EIGHT, BCD_NINE,
|
||
BCD_ZERO, BCD_ONE, BCD_TWO, BCD_THREE, BCD_FOUR,
|
||
BCD_FIVE, BCD_SIX, BCD_SEVEN, BCD_EIGHT, BCD_NINE,
|
||
BCD_ZERO, BCD_ONE, BCD_TWO, BCD_THREE, BCD_FOUR,
|
||
BCD_FIVE, BCD_SIX, BCD_SEVEN, BCD_EIGHT, BCD_NINE,
|
||
BCD_ZERO, BCD_ONE, BCD_TWO, BCD_THREE, BCD_FOUR,
|
||
BCD_FIVE, BCD_SIX, BCD_SEVEN, BCD_EIGHT, BCD_NINE,
|
||
BCD_ZERO, BCD_ONE, BCD_TWO, BCD_THREE, BCD_FOUR,
|
||
BCD_FIVE, BCD_SIX, BCD_SEVEN, BCD_EIGHT, BCD_NINE,
|
||
BCD_ZERO, BCD_ONE, BCD_TWO, BCD_THREE, BCD_FOUR,
|
||
BCD_FIVE, BCD_SIX, BCD_SEVEN, BCD_EIGHT, BCD_NINE,
|
||
BCD_ZERO, BCD_ONE, BCD_TWO, BCD_THREE, BCD_FOUR,
|
||
BCD_FIVE, BCD_SIX, BCD_SEVEN, BCD_EIGHT, BCD_NINE,
|
||
BCD_ZERO, BCD_ONE, BCD_TWO, BCD_THREE, BCD_FOUR,
|
||
BCD_FIVE, BCD_SIX, BCD_SEVEN, BCD_EIGHT, BCD_NINE };
|
||
|
||
static const int32 cry_table[100] = {
|
||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||
4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
|
||
5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
|
||
6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
|
||
7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
|
||
8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
|
||
9, 9, 9, 9, 9, 9, 9, 9, 9, 9 };
|
||
|
||
/* Legal modifier tables */
|
||
|
||
static const int32 r_mod[] = { BCD_C, -1 };
|
||
static const int32 p_mod[] = { BCD_C, -1 };
|
||
static const int32 w_mod[] = { BCD_S, BCD_SQUARE, -1 };
|
||
static const int32 ss_mod[] = { 1, 2, 4, 8, -1 };
|
||
static const int32 mtf_mod[] = { BCD_B, BCD_E, BCD_M, BCD_R, BCD_U, -1 };
|
||
|
||
t_stat sim_instr (void)
|
||
{
|
||
extern int32 sim_interval;
|
||
int32 IS, ilnt, flags;
|
||
int32 op, xa, t, wm, ioind, dev, unit;
|
||
int32 a, b, i, k, asave, bsave;
|
||
int32 carry, lowprd, sign, ps;
|
||
int32 quo, ahigh, qs;
|
||
int32 qzero, qawm, qbody, qsign, qdollar, qaster, qdecimal;
|
||
t_stat reason, r1, r2;
|
||
|
||
/* Restore saved state */
|
||
|
||
|
||
IS = saved_IS;
|
||
if (as_err) AS = AS | BA; /* flag bad addresses */
|
||
if (bs_err) BS = BS | BA;
|
||
as_err = bs_err = 0; /* reset error flags */
|
||
reason = 0;
|
||
|
||
/* Main instruction fetch/decode loop */
|
||
|
||
while (reason == 0) { /* loop until halted */
|
||
|
||
if (hb_pend) { /* halt br pending? */
|
||
hb_pend = 0; /* clear flag */
|
||
BRANCH; } /* execute branch */
|
||
|
||
saved_IS = IS; /* commit prev instr */
|
||
if (sim_interval <= 0) { /* check clock queue */
|
||
if (reason = sim_process_event ()) break; }
|
||
|
||
if (sim_brk_summ && sim_brk_test (IS, SWMASK ('E'))) { /* breakpoint? */
|
||
reason = STOP_IBKPT; /* stop simulation */
|
||
break; }
|
||
|
||
sim_interval = sim_interval - 1;
|
||
|
||
/* Instruction fetch - 1401 fetch works as follows:
|
||
|
||
- Each character fetched enters the B register. This register is not
|
||
visible; the variable t represents the B register.
|
||
- Except for the first and last cycles, each character fetched enters
|
||
the A register. This register is not visible; the variable D represents
|
||
the A register, because this is the instruction modifier for 2, 5, and 8
|
||
character instructions.
|
||
- At the start of the second cycle (first address character), the A-address
|
||
register and, for most instructions, the B-address register, are cleared
|
||
to blanks. The simulator represents addresses in binary and creates the
|
||
effect of blanks (address is bad) if less than three A-address characters
|
||
are found. Further, the simulator accumulates only the A-address, and
|
||
replicates it to the B-address at the appropriate point.
|
||
- At the start of the fifth cycle (fourth address character), the B-address
|
||
register is cleared to blanks. Again, the simulator creates the effect of
|
||
blanks (address is bad) if less than three B-address characters are found.
|
||
|
||
The 1401 does explicitly check for valid instruction lengths. Most 2,3,5,6
|
||
character instructions will be invalid because the A-address or B-address
|
||
(or both) are invalid.
|
||
*/
|
||
|
||
if ((M[IS] & WM) == 0) { /* I-Op: WM under op? */
|
||
reason = STOP_NOWM; /* no, error */
|
||
break; }
|
||
op = M[IS] & CHAR; /* get opcode */
|
||
flags = op_table[op]; /* get op flags */
|
||
if ((flags == 0) || (flags & ALLOPT & ~cpu_unit.flags)) {
|
||
reason = STOP_NXI; /* illegal inst? */
|
||
break; }
|
||
if (op == OP_SAR) BS = AS; /* SAR? save ASTAR */
|
||
PP (IS);
|
||
|
||
if ((t = M[IS]) & WM) goto CHECK_LENGTH; /* I-1: WM? 1 char inst */
|
||
D = ioind = t; /* could be D char, % */
|
||
AS = hun_table[t]; /* could be A addr */
|
||
PP (IS); /* if %xy, BA is set */
|
||
|
||
if ((t = M[IS]) & WM) { /* I-2: WM? 2 char inst */
|
||
AS = AS | BA; /* ASTAR bad */
|
||
if (!(flags & MLS)) BS = AS;
|
||
goto CHECK_LENGTH; }
|
||
D = dev = t; /* could be D char, dev */
|
||
AS = AS + ten_table[t]; /* build A addr */
|
||
PP (IS);
|
||
|
||
if ((t = M[IS]) & WM) { /* I-3: WM? 3 char inst */
|
||
AS = AS | BA; /* ASTAR bad */
|
||
if (!(flags & MLS)) BS = AS;
|
||
goto CHECK_LENGTH; }
|
||
D = unit = t; /* could be D char, unit */
|
||
if (unit == BCD_ZERO) unit = 0; /* convert unit to binary */
|
||
AS = AS + one_table[t]; /* finish A addr */
|
||
xa = (AS >> V_INDEX) & M_INDEX; /* get index reg */
|
||
if (xa && (ioind != BCD_PERCNT) && (cpu_unit.flags & XSA)) { /* indexed? */
|
||
AS = AS + hun_table[M[xa] & CHAR] + ten_table[M[xa + 1] & CHAR] +
|
||
one_table[M[xa + 2] & CHAR];
|
||
AS = (AS & INDEXMASK) % MAXMEMSIZE; }
|
||
if (!(flags & MLS)) BS = AS; /* not MLS? B = A */
|
||
PP (IS);
|
||
|
||
if ((t = M[IS]) & WM) goto CHECK_LENGTH; /* I-4: WM? 4 char inst */
|
||
if ((op == OP_B) && (t == BCD_BLANK)) goto CHECK_LENGTH; /* BR + space? */
|
||
D = t; /* could be D char */
|
||
BS = hun_table[t]; /* could be B addr */
|
||
PP (IS);
|
||
|
||
if ((t = M[IS]) & WM) { /* I-5: WM? 5 char inst */
|
||
BS = BS | BA; /* BSTAR bad */
|
||
goto CHECK_LENGTH; }
|
||
D = t; /* could be D char */
|
||
BS = BS + ten_table[t]; /* build B addr */
|
||
PP (IS);
|
||
|
||
if ((t = M[IS]) & WM) { /* I-6: WM? 6 char inst */
|
||
BS = BS | BA; /* BSTAR bad */
|
||
goto CHECK_LENGTH; }
|
||
D = t; /* could be D char */
|
||
BS = BS + one_table[t]; /* finish B addr */
|
||
xa = (BS >> V_INDEX) & M_INDEX; /* get index reg */
|
||
if (xa && (cpu_unit.flags & XSA)) { /* indexed? */
|
||
BS = BS + hun_table[M[xa] & CHAR] + ten_table[M[xa + 1] & CHAR] +
|
||
one_table[M[xa + 2] & CHAR];
|
||
BS = (BS & INDEXMASK) % MAXMEMSIZE; }
|
||
PP (IS);
|
||
|
||
if (flags & NOWM) goto CHECK_LENGTH; /* I-7: SWM? done */
|
||
if ((t = M[IS]) & WM) goto CHECK_LENGTH; /* WM? 7 char inst */
|
||
D = t; /* last char is D */
|
||
while (((t = M[IS]) & WM) == 0) { /* I-8: repeats until WM */
|
||
D = t; /* last char is D */
|
||
PP (IS); }
|
||
|
||
CHECK_LENGTH:
|
||
if ((flags & BREQ) && ADDR_ERR (BS)) { /* valid B? */
|
||
reason = STOP_INVB;
|
||
break; }
|
||
if ((flags & AREQ) && ADDR_ERR (AS)) { /* valid A? */
|
||
reason = STOP_INVA;
|
||
break; }
|
||
ilnt = IS - saved_IS; /* get lnt */
|
||
if (hst_lnt) { /* history enabled? */
|
||
hst_p = (hst_p + 1); /* next entry */
|
||
if (hst_p >= hst_lnt) hst_p = 0;
|
||
hst[hst_p].is = saved_IS; /* save IS */
|
||
hst[hst_p].ilnt = ilnt;
|
||
for (i = 0; (i < MAX_L) && (i < ilnt); i++)
|
||
hst[hst_p].inst[i] = M[saved_IS + i];
|
||
}
|
||
if (DEBUG_PRS (cpu_dev)) {
|
||
fprint_val (sim_deb, saved_IS, 10, 5, PV_RSPC);
|
||
fprintf (sim_deb, ": " );
|
||
for (i = 0; i < sim_emax; i++) sim_eval[i] = 0;
|
||
for (i = 0, k = saved_IS; i < sim_emax; i++, k++) {
|
||
if (cpu_ex (&sim_eval[i], k, &cpu_unit, 0) != SCPE_OK) break; }
|
||
fprint_sym (sim_deb, saved_IS, sim_eval, &cpu_unit, SWMASK('M'));
|
||
fprintf (sim_deb, "\n" );
|
||
}
|
||
switch (op) { /* case on opcode */
|
||
|
||
/* Move/load character instructions A check B check
|
||
|
||
MCW copy A to B, preserving B WM, here fetch
|
||
until either A or B WM
|
||
LCA copy A to B, overwriting B WM, here fetch
|
||
until A WM
|
||
|
||
Instruction lengths:
|
||
|
||
1 chained A and B
|
||
2,3 invalid A-address
|
||
4 chained B address
|
||
5,6 invalid B-address
|
||
7 normal
|
||
8+ normal + modifier
|
||
*/
|
||
|
||
case OP_MCW: /* move char */
|
||
if ((ilnt >= 4) && (ioind == BCD_PERCNT)) { /* I/O form? */
|
||
reason = iodisp (dev, unit, MD_NORM, D); /* dispatch I/O */
|
||
break; }
|
||
if (ADDR_ERR (AS)) { /* check A addr */
|
||
reason = STOP_INVA;
|
||
break; }
|
||
do {
|
||
wm = M[AS] | M[BS];
|
||
M[BS] = (M[BS] & WM) | (M[AS] & CHAR); /* move char */
|
||
MM (AS); MM (BS); } /* decr pointers */
|
||
while ((wm & WM) == 0); /* stop on A,B WM */
|
||
break;
|
||
|
||
case OP_LCA: /* load char */
|
||
if ((ilnt >= 4) && (ioind == BCD_PERCNT)) { /* I/O form? */
|
||
reason = iodisp (dev, unit, MD_WM, D);
|
||
break; }
|
||
if (ADDR_ERR (AS)) { /* check A addr */
|
||
reason = STOP_INVA;
|
||
break; }
|
||
do {
|
||
wm = M[BS] = M[AS]; /* move char + wmark */
|
||
MM (AS); MM (BS); } /* decr pointers */
|
||
while ((wm & WM) == 0); /* stop on A WM */
|
||
break;
|
||
|
||
/* Other move instructions A check B check
|
||
|
||
MCM copy A to B, preserving B WM, fetch fetch
|
||
until record or group mark
|
||
MCS copy A to B, clearing B WM, until A WM; fetch fetch
|
||
reverse scan and suppress leading zeroes
|
||
MN copy A char digit to B char digit, fetch fetch
|
||
preserving B zone and WM
|
||
MZ copy A char zone to B char zone, fetch fetch
|
||
preserving B digit and WM
|
||
|
||
Instruction lengths:
|
||
|
||
1 chained
|
||
2,3 invalid A-address
|
||
4 self (B-address = A-address)
|
||
5,6 invalid B-address
|
||
7 normal
|
||
8+ normal + ignored modifier
|
||
*/
|
||
|
||
case OP_MCM: /* move to rec/group */
|
||
do {
|
||
t = M[AS];
|
||
M[BS] = (M[BS] & WM) | (M[AS] & CHAR); /* move char */
|
||
PP (AS); PP (BS); } /* incr pointers */
|
||
while (((t & CHAR) != BCD_RECMRK) && (t != (BCD_GRPMRK + WM)));
|
||
break;
|
||
|
||
case OP_MCS: /* move suppress zero */
|
||
bsave = BS; /* save B start */
|
||
qzero = 1; /* set suppress */
|
||
do {
|
||
wm = M[AS];
|
||
M[BS] = M[AS] & ((BS != bsave)? CHAR: DIGIT); /* copy char */
|
||
MM (AS); MM (BS); } /* decr pointers */
|
||
while ((wm & WM) == 0); /* stop on A WM */
|
||
if (reason) break; /* addr err? stop */
|
||
do {
|
||
PP (BS); /* adv B */
|
||
t = M[BS]; /* get B, cant be WM */
|
||
if ((t == BCD_ZERO) || (t == BCD_COMMA)) {
|
||
if (qzero) M[BS] = 0; }
|
||
else if ((t == BCD_BLANK) || (t == BCD_MINUS)) ;
|
||
else if (((t == BCD_DECIMAL) && (cpu_unit.flags & EPE)) ||
|
||
(t <= BCD_NINE)) qzero = 0;
|
||
else qzero = 1; }
|
||
while (BS < bsave);
|
||
PP (BS); /* BS end is B+1 */
|
||
break;
|
||
|
||
case OP_MN: /* move numeric */
|
||
M[BS] = (M[BS] & ~DIGIT) | (M[AS] & DIGIT); /* move digit */
|
||
MM (AS); MM (BS); /* decr pointers */
|
||
break;
|
||
|
||
case OP_MZ: /* move zone */
|
||
M[BS] = (M[BS] & ~ZONE) | (M[AS] & ZONE); /* move high bits */
|
||
MM (AS); MM (BS); /* decr pointers */
|
||
break;
|
||
|
||
/* Branch instruction A check B check
|
||
|
||
Instruction lengths:
|
||
|
||
1 branch if B char equals d, chained if branch here
|
||
2,3 invalid B-address if branch here
|
||
4 unconditional branch if branch
|
||
5 branch if indicator[d] is set if branch
|
||
6 invalid B-address if branch here
|
||
7 branch if B char equals d, if branch here
|
||
d is last character of B-address
|
||
8 branch if B char equals d if branch here
|
||
*/
|
||
|
||
case OP_B: /* branch */
|
||
if (ilnt == 4) { BRANCH; } /* uncond branch? */
|
||
else if (ilnt == 5) { /* branch on ind? */
|
||
if (ind[D]) { BRANCH; } /* test indicator */
|
||
if (ind_table[D]) ind[D] = 0; } /* reset if needed */
|
||
else { /* branch char eq */
|
||
if (ADDR_ERR (BS)) { /* validate B addr */
|
||
reason = STOP_INVB;
|
||
break; }
|
||
if ((M[BS] & CHAR) == D) { BRANCH; } /* char equal? */
|
||
else { MM (BS); } }
|
||
break;
|
||
|
||
/* Other branch instructions A check B check
|
||
|
||
BWZ branch if (d<0>: B char WM) if branch fetch
|
||
(d<1>: B char zone = d zone)
|
||
BBE branch if B char & d non-zero if branch fetch
|
||
|
||
Instruction lengths:
|
||
1 chained
|
||
2,3 invalid A-address and B-address
|
||
4 self (B-address = A-address, d = last character of A-address)
|
||
5,6 invalid B-address
|
||
7 normal, d = last character of B-address
|
||
8+ normal
|
||
*/
|
||
|
||
case OP_BWZ: /* branch wm or zone */
|
||
if (((D & 1) && (M[BS] & WM)) || /* d1? test wm */
|
||
((D & 2) && ((M[BS] & ZONE) == (D & ZONE)))) /* d2? test zone */
|
||
{ BRANCH; }
|
||
else { MM (BS); } /* decr pointer */
|
||
break;
|
||
|
||
case OP_BBE: /* branch if bit eq */
|
||
if (M[BS] & D & CHAR) { BRANCH; } /* any bits set? */
|
||
else { MM (BS); } /* decr pointer */
|
||
break;
|
||
|
||
/* Arithmetic instructions A check B check
|
||
|
||
ZA move A to B, normalizing A sign, fetch fetch
|
||
preserving B WM, until B WM
|
||
ZS move A to B, complementing A sign, fetch fetch
|
||
preserving B WM, until B WM
|
||
A add A to B fetch fetch
|
||
S subtract A from B fetch fetch
|
||
C compare A to B fetch fetch
|
||
|
||
Instruction lengths:
|
||
|
||
1 chained
|
||
2,3 invalid A-address
|
||
4 self (B-address = A-address)
|
||
5,6 invalid B-address
|
||
7 normal
|
||
8+ normal + ignored modifier
|
||
*/
|
||
|
||
case OP_ZA: case OP_ZS: /* zero and add/sub */
|
||
a = i = 0; /* clear flags */
|
||
do {
|
||
if (a & WM) wm = M[BS] = (M[BS] & WM) | BCD_ZERO;
|
||
else {
|
||
a = M[AS]; /* get A char */
|
||
t = (a & CHAR)? bin_to_bcd[a & DIGIT]: 0;
|
||
wm = M[BS] = (M[BS] & WM) | t; /* move digit */
|
||
MM (AS); }
|
||
if (i == 0) i = M[BS] = M[BS] |
|
||
((((a & ZONE) == BBIT) ^ (op == OP_ZS))? BBIT: ZONE);
|
||
MM (BS); }
|
||
while ((wm & WM) == 0); /* stop on B WM */
|
||
break;
|
||
|
||
case OP_A: case OP_S: /* add/sub */
|
||
bsave = BS; /* save sign pos */
|
||
a = M[AS]; /* get A digit/sign */
|
||
b = M[BS]; /* get B digit/sign */
|
||
MM (AS);
|
||
qsign = ((a & ZONE) == BBIT) ^ ((b & ZONE) == BBIT) ^ (op == OP_S);
|
||
t = bcd_to_bin[a & DIGIT]; /* get A binary */
|
||
t = bcd_to_bin[b & DIGIT] + (qsign? 10 - t: t); /* sum A + B */
|
||
carry = (t >= 10); /* get carry */
|
||
b = (b & ~DIGIT) | sum_table[t]; /* get result */
|
||
if (qsign && ((b & BBIT) == 0)) b = b | ZONE; /* normalize sign */
|
||
M[BS] = b; /* store result */
|
||
MM (BS);
|
||
if (b & WM) { /* b wm? done */
|
||
if (qsign && (carry == 0)) M[bsave] = /* compl, no carry? */
|
||
WM + ((b & ZONE) ^ ABIT) + sum_table[10 - t];
|
||
break; }
|
||
do {
|
||
if (a & WM) a = WM; /* A WM? char = 0 */
|
||
else {
|
||
a = M[AS]; /* else get A */
|
||
MM (AS); }
|
||
b = M[BS]; /* get B */
|
||
t = bcd_to_bin[a & DIGIT]; /* get A binary */
|
||
t = bcd_to_bin[b & DIGIT] + (qsign? 9 - t: t) + carry;
|
||
carry = (t >= 10); /* get carry */
|
||
if ((b & WM) && (qsign == 0)) { /* last, no recomp? */
|
||
M[BS] = WM + sum_table[t] + /* zone add */
|
||
(((a & ZONE) + b + (carry? ABIT: 0)) & ZONE);
|
||
ind[IN_OVF] = carry; } /* ovflo if carry */
|
||
else M[BS] = (b & WM) + sum_table[t]; /* normal add */
|
||
MM (BS); }
|
||
while ((b & WM) == 0); /* stop on B WM */
|
||
if (reason) break; /* address err? */
|
||
if (qsign && (carry == 0)) { /* recompl, no carry? */
|
||
M[bsave] = M[bsave] ^ ABIT; /* XOR sign */
|
||
for (carry = 1; bsave != BS; --bsave) { /* rescan */
|
||
t = 9 - bcd_to_bin[M[bsave] & DIGIT] + carry;
|
||
carry = (t >= 10);
|
||
M[bsave] = (M[bsave] & ~DIGIT) | sum_table[t]; } }
|
||
break;
|
||
|
||
case OP_C: /* compare */
|
||
if (ilnt != 1) { /* if not chained */
|
||
ind[IN_EQU] = 1; /* clear indicators */
|
||
ind[IN_UNQ] = ind[IN_HGH] = ind[IN_LOW] = 0; }
|
||
do {
|
||
a = M[AS]; /* get characters */
|
||
b = M[BS];
|
||
wm = a | b; /* get word marks */
|
||
if ((a & CHAR) != (b & CHAR)) { /* unequal? */
|
||
ind[IN_EQU] = 0; /* set indicators */
|
||
ind[IN_UNQ] = 1;
|
||
ind[IN_HGH] = col_table[b & CHAR] > col_table [a & CHAR];
|
||
ind[IN_LOW] = ind[IN_HGH] ^ 1; }
|
||
MM (AS); MM (BS); } /* decr pointers */
|
||
while ((wm & WM) == 0); /* stop on A, B WM */
|
||
if ((a & WM) && !(b & WM)) { /* short A field? */
|
||
ind[IN_EQU] = ind[IN_LOW] = 0;
|
||
ind[IN_UNQ] = ind[IN_HGH] = 1; }
|
||
if (!(cpu_unit.flags & HLE)) /* no HLE? */
|
||
ind[IN_EQU] = ind[IN_LOW] = ind[IN_HGH] = 0;
|
||
break;
|
||
|
||
/* I/O instructions A check B check
|
||
|
||
R read a card if branch
|
||
W write to line printer if branch
|
||
WR write and read if branch
|
||
P punch a card if branch
|
||
RP read and punch if branch
|
||
WP : write and punch if branch
|
||
WRP write read and punch if branch
|
||
RF read feed (nop)
|
||
PF punch feed (nop)
|
||
SS select stacker if branch
|
||
CC carriage control if branch
|
||
|
||
Instruction lengths:
|
||
|
||
1 normal
|
||
2,3 normal, with modifier
|
||
4 branch; modifier, if any, is last character of branch address
|
||
5 branch + modifier
|
||
6+ normal, with modifier
|
||
*/
|
||
|
||
case OP_R: /* read */
|
||
if (reason = iomod (ilnt, D, r_mod)) break; /* valid modifier? */
|
||
reason = read_card (ilnt, D); /* read card */
|
||
BS = CDR_BUF + CDR_WIDTH;
|
||
if ((ilnt == 4) || (ilnt == 5)) { BRANCH; } /* check for branch */
|
||
break;
|
||
|
||
case OP_W: /* write */
|
||
if (reason = iomod (ilnt, D, w_mod)) break; /* valid modifier? */
|
||
reason = write_line (ilnt, D); /* print line */
|
||
BS = LPT_BUF + LPT_WIDTH;
|
||
if ((ilnt == 4) || (ilnt == 5)) { BRANCH; } /* check for branch */
|
||
break;
|
||
|
||
case OP_P: /* punch */
|
||
if (reason = iomod (ilnt, D, p_mod)) break; /* valid modifier? */
|
||
reason = punch_card (ilnt, D); /* punch card */
|
||
BS = CDP_BUF + CDP_WIDTH;
|
||
if ((ilnt == 4) || (ilnt == 5)) { BRANCH; } /* check for branch */
|
||
break;
|
||
|
||
case OP_WR: /* write and read */
|
||
if (reason = iomod (ilnt, D, w_mod)) break; /* valid modifier? */
|
||
reason = write_line (ilnt, D); /* print line */
|
||
r1 = read_card (ilnt, D); /* read card */
|
||
BS = CDR_BUF + CDR_WIDTH;
|
||
if ((ilnt == 4) || (ilnt == 5)) { BRANCH; } /* check for branch */
|
||
if (reason == SCPE_OK) reason = r1; /* merge errors */
|
||
break;
|
||
|
||
case OP_WP: /* write and punch */
|
||
if (reason = iomod (ilnt, D, w_mod)) break; /* valid modifier? */
|
||
reason = write_line (ilnt, D); /* print line */
|
||
r1 = punch_card (ilnt, D); /* punch card */
|
||
BS = CDP_BUF + CDP_WIDTH;
|
||
if ((ilnt == 4) || (ilnt == 5)) { BRANCH; } /* check for branch */
|
||
if (reason == SCPE_OK) reason = r1; /* merge errors */
|
||
break;
|
||
|
||
case OP_RP: /* read and punch */
|
||
if (reason = iomod (ilnt, D, NULL)) break; /* valid modifier? */
|
||
reason = read_card (ilnt, D); /* read card */
|
||
r1 = punch_card (ilnt, D); /* punch card */
|
||
BS = CDP_BUF + CDP_WIDTH;
|
||
if ((ilnt == 4) || (ilnt == 5)) { BRANCH; } /* check for branch */
|
||
if (reason == SCPE_OK) reason = r1; /* merge errors */
|
||
break;
|
||
|
||
case OP_WRP: /* write, read, punch */
|
||
if (reason = iomod (ilnt, D, w_mod)) break; /* valid modifier? */
|
||
reason = write_line (ilnt, D); /* print line */
|
||
r1 = read_card (ilnt, D); /* read card */
|
||
r2 = punch_card (ilnt, D); /* punch card */
|
||
BS = CDP_BUF + CDP_WIDTH;
|
||
if ((ilnt == 4) || (ilnt == 5)) { BRANCH; } /* check for branch */
|
||
if (reason == SCPE_OK) reason = (r1 == SCPE_OK)? r2: r1;
|
||
break;
|
||
|
||
case OP_SS: /* select stacker */
|
||
if (reason = iomod (ilnt, D, ss_mod)) break; /* valid modifier? */
|
||
if (reason = select_stack (D)) break; /* sel stack, error? */
|
||
if ((ilnt == 4) || (ilnt == 5)) { BRANCH; } /* check for branch */
|
||
break;
|
||
|
||
case OP_CC: /* carriage control */
|
||
if (reason = carriage_control (D)) break; /* car ctrl, error? */
|
||
if ((ilnt == 4) || (ilnt == 5)) { BRANCH; } /* check for branch */
|
||
break;
|
||
|
||
/* MTF - magtape functions - must be at least 4 characters
|
||
|
||
Instruction lengths:
|
||
|
||
1-3 invalid I/O address
|
||
4 normal, d-character is unit
|
||
5 normal
|
||
6+ normal, d-character is last character
|
||
*/
|
||
|
||
case OP_MTF: /* magtape function */
|
||
if (ilnt < 4) reason = STOP_INVL; /* too short? */
|
||
else if (ioind != BCD_PERCNT) reason = STOP_INVA;
|
||
else if (reason = iomod (ilnt, D, mtf_mod)) break; /* valid modifier? */
|
||
reason = mt_func (unit, D); /* mt func, error? */
|
||
break; /* can't branch */
|
||
|
||
case OP_RF: case OP_PF: /* read, punch feed */
|
||
break; /* nop's */
|
||
|
||
/* Move character and edit
|
||
|
||
Control flags
|
||
qsign sign of A field (0 = +, 1 = minus)
|
||
qawm A field WM seen and processed
|
||
qzero zero suppression enabled
|
||
qbody in body (copying A field characters)
|
||
qdollar EPE only; $ seen in body
|
||
qaster EPE only; * seen in body
|
||
qdecimal EPE only; . seen on first rescan
|
||
|
||
MCE operates in one to three scans, the first of which has three phases
|
||
|
||
1 right to left qbody = 0, qawm = 0 => right status
|
||
qbody = 1, qawm = 0 => body
|
||
qbody = 0, qawm = 1 => left status
|
||
2 left to right
|
||
3 right to left, extended print end only
|
||
|
||
The first A field character is masked to its digit part, all others
|
||
are copied intact
|
||
|
||
Instruction lengths:
|
||
|
||
1 chained
|
||
2,3 invalid A-address
|
||
4 self (B-address = A-address)
|
||
5,6 invalid B-address
|
||
7 normal
|
||
8+ normal + ignored modifier
|
||
*/
|
||
|
||
case OP_MCE: /* edit */
|
||
a = M[AS]; /* get A char */
|
||
b = M[BS]; /* get B char */
|
||
if (a & WM) { /* one char A field? */
|
||
reason = STOP_MCE1;
|
||
break; }
|
||
if (b & WM) { /* one char B field? */
|
||
reason = STOP_MCE2;
|
||
break; }
|
||
t = a & DIGIT; MM (AS); /* get A digit */
|
||
qsign = ((a & ZONE) == BBIT); /* get A field sign */
|
||
qawm = qzero = qbody = 0; /* clear other flags */
|
||
qdollar = qaster = qdecimal = 0; /* clear EPE flags */
|
||
|
||
/* Edit pass 1 - from right to left, under B field control
|
||
|
||
* in status or !epe, skip B; else, set qaster, repl with A
|
||
$ in status or !epe, skip B; else, set qdollar, repl with A
|
||
0 in right status or body, if !qzero, set A WM; set qzero, repl with A
|
||
else, if !qzero, skip B; else, if (!B WM) set B WM
|
||
blank in right status or body, repl with A; else, skip B
|
||
C,R,- in status, blank B; else, skip B
|
||
, in status, blank B, else, skip B
|
||
& blank B
|
||
*/
|
||
|
||
do {
|
||
b = M[BS]; /* get B char */
|
||
M[BS] = M[BS] & ~WM; /* clr WM */
|
||
switch (b & CHAR) { /* case on B char */
|
||
case BCD_ASTER: /* * */
|
||
if (!qbody || qdollar || !(cpu_unit.flags & EPE)) break;
|
||
qaster = 1; /* flag */
|
||
goto A_CYCLE; /* take A cycle */
|
||
case BCD_DOLLAR: /* $ */
|
||
if (!qbody || qaster || !(cpu_unit.flags & EPE)) break;
|
||
qdollar = 1; /* flag */
|
||
goto A_CYCLE; /* take A cycle */
|
||
case BCD_ZERO: /* 0 */
|
||
if (qawm && !qzero && !(b & WM)) {
|
||
M[BS] = BCD_ZERO + WM; /* mark with WM */
|
||
qzero = 1; /* flag supress */
|
||
break; }
|
||
if (!qzero) t = t | WM; /* first? set WM */
|
||
qzero = 1; /* flag supress */
|
||
/* fall through */
|
||
case BCD_BLANK: /* blank */
|
||
if (qawm) break; /* any A left? */
|
||
A_CYCLE:
|
||
M[BS] = t; /* copy char */
|
||
if (a & WM) { /* end of A field? */
|
||
qbody = 0; /* end body */
|
||
qawm = 1; }
|
||
else {
|
||
qbody = 1; /* in body */
|
||
a = M[AS]; MM (AS); /* next A */
|
||
t = a & CHAR; }
|
||
break;
|
||
case BCD_C: case BCD_R: case BCD_MINUS: /* C, R, - */
|
||
if (!qsign && !qbody) M[BS] = BCD_BLANK;
|
||
break;
|
||
case BCD_COMMA: /* , */
|
||
if (!qbody) M[BS] = BCD_BLANK; /* bl if status */
|
||
break;
|
||
case BCD_AMPER: /* & */
|
||
M[BS] = BCD_BLANK; /* blank B field */
|
||
break; } /* end switch */
|
||
MM (BS); } /* decr B pointer */
|
||
while ((b & WM) == 0); /* stop on B WM */
|
||
|
||
if (reason) break; /* address err? */
|
||
if (!qawm || !qzero) { /* rescan? */
|
||
if (qdollar) reason = STOP_MCE3; /* error if $ */
|
||
break; }
|
||
|
||
/* Edit pass 2 - from left to right, supressing zeroes */
|
||
|
||
do {
|
||
b = M[++BS]; /* get B char */
|
||
switch (b & CHAR) { /* case on B char */
|
||
case BCD_ONE: case BCD_TWO: case BCD_THREE:
|
||
case BCD_FOUR: case BCD_FIVE: case BCD_SIX:
|
||
case BCD_SEVEN: case BCD_EIGHT: case BCD_NINE:
|
||
qzero = 0; /* turn off supr */
|
||
break;
|
||
case BCD_ZERO: case BCD_COMMA: /* 0 or , */
|
||
if (qzero && !qdecimal) /* if supr, blank */
|
||
M[BS] = qaster? BCD_ASTER: BCD_BLANK;
|
||
break;
|
||
case BCD_BLANK: /* blank */
|
||
if (qaster) M[BS] = BCD_ASTER; /* if EPE *, repl */
|
||
break;
|
||
case BCD_DECIMAL: /* . */
|
||
if (qzero && (cpu_unit.flags & EPE))
|
||
qdecimal = 1; /* flag for EPE */
|
||
case BCD_PERCNT: case BCD_WM: case BCD_BS:
|
||
case BCD_TS: case BCD_MINUS:
|
||
break; /* ignore */
|
||
default: /* other */
|
||
qzero = 1; /* restart supr */
|
||
break; } } /* end case, do */
|
||
while ((b & WM) == 0);
|
||
|
||
M[BS] = M[BS] & ~WM; /* clear B WM */
|
||
if (!qdollar && !(qdecimal && qzero)) { /* rescan again? */
|
||
BS++; /* BS = addr WM + 1 */
|
||
break; }
|
||
if (qdecimal && qzero) qdollar = 0; /* no digits? clr $ */
|
||
|
||
/* Edit pass 3 (extended print only) - from right to left */
|
||
|
||
for (;; ) { /* until chars */
|
||
b = M[BS]; /* get B char */
|
||
if ((b == BCD_BLANK) && qdollar) { /* blank & flt $? */
|
||
M[BS] = BCD_DOLLAR; /* insert $ */
|
||
break; } /* exit for */
|
||
if (b == BCD_DECIMAL) { /* decimal? */
|
||
M[BS] = qaster? BCD_ASTER: BCD_BLANK;
|
||
break; } /* exit for */
|
||
if ((b == BCD_ZERO) && !qdollar) /* 0 & ~flt $ */
|
||
M[BS] = qaster? BCD_ASTER: BCD_BLANK;
|
||
BS--; } /* end for */
|
||
break; /* done at last! */
|
||
|
||
/* Multiply. Comments from the PDP-10 based simulator by Len Fehskens.
|
||
|
||
Multiply, with variable length operands, is necessarily done the same
|
||
way you do it with paper and pencil, except that partial products are
|
||
added into the incomplete final product as they are computed, rather
|
||
than at the end. The 1401 multiplier format allows the product to
|
||
be developed in place, without scratch storage.
|
||
|
||
The A field contains the multiplicand, length LD. The B field must be
|
||
LD + 1 + length of multiplier. Locate the low order multiplier digit,
|
||
and at the same time zero out the product field. Then compute the sign
|
||
of the result.
|
||
|
||
Instruction lengths:
|
||
|
||
1 chained
|
||
2,3 invalid A-address
|
||
4 self (B-address = A-address)
|
||
5,6 invalid B-address
|
||
7 normal
|
||
8+ normal + ignored modifier
|
||
*/
|
||
|
||
case OP_MUL:
|
||
asave = AS; bsave = lowprd = BS; /* save AS, BS */
|
||
do {
|
||
a = M[AS]; /* get mpcd char */
|
||
M[BS] = BCD_ZERO; /* zero prod */
|
||
MM (AS); MM (BS); } /* decr pointers */
|
||
while ((a & WM) == 0); /* until A WM */
|
||
if (reason) break; /* address err? */
|
||
M[BS] = BCD_ZERO; /* zero hi prod */
|
||
MM (BS); /* addr low mpyr */
|
||
sign = ((M[asave] & ZONE) == BBIT) ^ ((M[BS] & ZONE) == BBIT);
|
||
|
||
/* Outer loop on multiplier (BS) and product digits (ps),
|
||
inner loop on multiplicand digits (AS).
|
||
AS and ps cannot produce an address error.
|
||
*/
|
||
|
||
do {
|
||
ps = bsave; /* ptr to prod */
|
||
AS = asave; /* ptr to mpcd */
|
||
carry = 0; /* init carry */
|
||
b = M[BS]; /* get mpyr char */
|
||
do {
|
||
a = M[AS]; /* get mpcd char */
|
||
t = (bcd_to_bin[a & DIGIT] * /* mpyr * mpcd */
|
||
bcd_to_bin[b & DIGIT]) + /* + c + partial prod */
|
||
carry + bcd_to_bin[M[ps] & DIGIT];
|
||
carry = cry_table[t];
|
||
M[ps] = (M[ps] & WM) | sum_table[t];
|
||
MM (AS); ps--; }
|
||
while ((a & WM) == 0); /* until mpcd done */
|
||
M[BS] = (M[BS] & WM) | BCD_ZERO; /* zero mpyr just used */
|
||
t = bcd_to_bin[M[ps] & DIGIT] + carry; /* add carry to prod */
|
||
M[ps] = (M[ps] & WM) | sum_table[t]; /* store */
|
||
bsave--; /* adv prod ptr */
|
||
MM (BS); } /* adv mpyr ptr */
|
||
while ((b & WM) == 0); /* until mpyr done */
|
||
M[lowprd] = M[lowprd] | ZONE; /* assume + */
|
||
if (sign) M[lowprd] = M[lowprd] & ~ABIT; /* if minus, B only */
|
||
break;
|
||
|
||
/* Divide. Comments from the PDP-10 based simulator by Len Fehskens.
|
||
|
||
Divide is done, like multiply, pretty much the same way you do it with
|
||
pencil and paper; successive subtraction of the divisor from a substring
|
||
of the dividend while counting up the corresponding quotient digit.
|
||
|
||
Let LS be the length of the divisor, LD the length of the dividend:
|
||
- AS points to the low order divisor digit.
|
||
- BS points to the high order dividend digit.
|
||
- The low order dividend digit is identified by sign (zone) bits.
|
||
- To the left of the dividend is a zero field of length LS + 1.
|
||
The low quotient is at low dividend - LS - 1. As BS points to the
|
||
high dividend, the low dividend is at BS + LD - 1, so the low
|
||
quotient is at BS + LD - LS - 2. The longest possible quotient is
|
||
LD - LS + 1, so the first possible non-zero quotient bit will be
|
||
found as BS - 2.
|
||
|
||
This pointer calculation assumes that the divisor has no leading zeroes.
|
||
For each leading zero, the start of the quotient will be one position
|
||
further left.
|
||
|
||
Start by locating the high order non-zero digit of the divisor. This
|
||
also tests for a divide by zero.
|
||
|
||
Instruction lengths:
|
||
|
||
1 chained
|
||
2,3 invalid A-address
|
||
4 self (B-address = A-address)
|
||
5,6 invalid B-address
|
||
7 normal
|
||
8+ normal + ignored modifier
|
||
*/
|
||
|
||
case OP_DIV:
|
||
asave = AS; ahigh = -1;
|
||
do {
|
||
a = M[AS]; /* get dvr char */
|
||
if ((a & CHAR) != BCD_ZERO) ahigh = AS; /* mark non-zero */
|
||
MM (AS); }
|
||
while ((a & WM) == 0);
|
||
if (reason) break; /* address err? */
|
||
if (ahigh < 0) { /* div? by zero */
|
||
ind[IN_OVF] = 1; /* set ovf indic */
|
||
qs = bsave = BS; /* quo, dividend */
|
||
do {
|
||
b = M[bsave]; /* find end divd */
|
||
PP (bsave); } /* marked by zone */
|
||
while ((b & ZONE) == 0);
|
||
if (reason) break; /* address err? */
|
||
if (ADDR_ERR (qs)) { /* address err? */
|
||
reason = STOP_WRAP; /* address wrap? */
|
||
break; }
|
||
div_sign (M[asave], b, qs - 1, bsave - 1); /* set signs */
|
||
BS = (BS - 2) - (asave - (AS + 1)); /* final bs */
|
||
break; }
|
||
bsave = BS + (asave - ahigh); /* end subdivd */
|
||
qs = (BS - 2) - (ahigh - (AS + 1)); /* quo start */
|
||
|
||
/* Divide loop - done with subroutines to keep the code clean.
|
||
In the loop,
|
||
|
||
asave = low order divisor
|
||
bsave = low order subdividend
|
||
qs = current quotient digit
|
||
*/
|
||
|
||
do {
|
||
quo = 0; /* clear quo digit */
|
||
if (ADDR_ERR (qs) || ADDR_ERR (bsave)) {
|
||
reason = STOP_WRAP; /* address wrap? */
|
||
break; }
|
||
b = M[bsave]; /* save low divd */
|
||
do {
|
||
t = div_sub (asave, bsave, ahigh); /* subtract */
|
||
quo++; } /* incr quo digit */
|
||
while (t == 0); /* until borrow */
|
||
div_add (asave, bsave, ahigh); quo--; /* restore */
|
||
M[qs] = (M[qs] & WM) | sum_table[quo]; /* store quo digit */
|
||
bsave++; qs++; } /* adv divd, quo */
|
||
while ((b & ZONE) == 0); /* until B sign */
|
||
if (reason) break; /* address err? */
|
||
|
||
/* At this point,
|
||
|
||
AS = high order divisor - 1
|
||
asave = unit position of divisor
|
||
b = unit character of dividend
|
||
bsave = unit position of remainder + 1
|
||
qs = unit position of quotient + 1
|
||
*/
|
||
|
||
div_sign (M[asave], b, qs - 1, bsave - 1); /* set signs */
|
||
BS = qs - 2; /* BS = quo 10's pos */
|
||
break;
|
||
|
||
/* Word mark instructions A check B check
|
||
|
||
SWM set WM on A char and B char fetch fetch
|
||
CWM clear WM on A char and B char fetch fetch
|
||
|
||
Instruction lengths:
|
||
|
||
1 chained
|
||
2,3 invalid A-address
|
||
4 one operand (B-address = A-address)
|
||
5,6 invalid B-address
|
||
7 two operands (SWM cannot be longer than 7)
|
||
8+ two operands + ignored modifier
|
||
*/
|
||
|
||
case OP_SWM: /* set word mark */
|
||
M[BS] = M[BS] | WM; /* set A field mark */
|
||
M[AS] = M[AS] | WM; /* set B field mark */
|
||
MM (AS); MM (BS); /* decr pointers */
|
||
break;
|
||
|
||
case OP_CWM: /* clear word mark */
|
||
M[BS] = M[BS] & ~WM; /* clear A field mark */
|
||
M[AS] = M[AS] & ~WM; /* clear B field mark */
|
||
MM (AS); MM (BS); /* decr pointers */
|
||
break;
|
||
|
||
/* Clear storage instruction A check B check
|
||
|
||
CS clear from B down to nearest hundreds if branch fetch
|
||
address
|
||
|
||
Instruction lengths:
|
||
|
||
1 chained
|
||
2,3 invalid A-address and B-address
|
||
4 one operand (B-address = A-address)
|
||
5,6 invalid B-address
|
||
7 branch
|
||
8+ one operand, branch ignored
|
||
*/
|
||
|
||
case OP_CS: /* clear storage */
|
||
t = (BS / 100) * 100; /* lower bound */
|
||
while (BS >= t) M[BS--] = 0; /* clear region */
|
||
if (BS < 0) BS = BS + MEMSIZE; /* wrap if needed */
|
||
if (ilnt == 7) { BRANCH; } /* branch variant? */
|
||
break;
|
||
|
||
/* Modify address instruction A check B check
|
||
|
||
MA add A addr and B addr, store at B addr fetch fetch
|
||
|
||
Instruction lengths:
|
||
1 chained
|
||
2,3 invalid A-address and B-address
|
||
4 self (B-address = A-address)
|
||
5,6 invalid B-address
|
||
7 normal
|
||
8+ normal + ignored modifier
|
||
*/
|
||
|
||
case OP_MA: /* modify address */
|
||
a = one_table[M[AS] & CHAR]; MM (AS); /* get A address */
|
||
a = a + ten_table[M[AS] & CHAR]; MM (AS);
|
||
a = a + hun_table[M[AS] & CHAR]; MM (AS);
|
||
b = one_table[M[BS] & CHAR]; MM (BS); /* get B address */
|
||
b = b + ten_table[M[BS] & CHAR]; MM (BS);
|
||
b = b + hun_table[M[BS] & CHAR]; MM (BS);
|
||
t = ((a + b) & INDEXMASK) % MAXMEMSIZE; /* compute sum */
|
||
M[BS + 3] = (M[BS + 3] & WM) | store_addr_u (t);
|
||
M[BS + 2] = (M[BS + 2] & (WM + ZONE)) | store_addr_t (t);
|
||
M[BS + 1] = (M[BS + 1] & WM) | store_addr_h (t);
|
||
if (((a % 4000) + (b % 4000)) >= 4000) BS = BS + 2; /* carry? */
|
||
break;
|
||
|
||
/* Store address instructions A-check B-check
|
||
|
||
SAR store A* at A addr fetch
|
||
SBR store B* at A addr fetch
|
||
|
||
Instruction lengths:
|
||
1 chained
|
||
2,3 invalid A-address
|
||
4 normal
|
||
5+ B-address overwritten from instruction;
|
||
invalid address ignored
|
||
*/
|
||
|
||
case OP_SAR: case OP_SBR: /* store A, B reg */
|
||
M[AS] = (M[AS] & WM) | store_addr_u (BS); MM (AS);
|
||
M[AS] = (M[AS] & WM) | store_addr_t (BS); MM (AS);
|
||
M[AS] = (M[AS] & WM) | store_addr_h (BS); MM (AS);
|
||
break;
|
||
|
||
/* NOP - no validity checking, all instructions length ok */
|
||
|
||
case OP_NOP: /* nop */
|
||
break;
|
||
|
||
/* HALT - unless length = 4 (branch), no validity checking; all lengths ok */
|
||
|
||
case OP_H: /* halt */
|
||
if (ilnt == 4) hb_pend = 1; /* set pending branch */
|
||
reason = STOP_HALT; /* stop simulator */
|
||
saved_IS = IS; /* commit instruction */
|
||
break;
|
||
|
||
default:
|
||
reason = STOP_NXI; /* unimplemented */
|
||
break; } /* end switch */
|
||
} /* end while */
|
||
|
||
/* Simulation halted */
|
||
|
||
as_err = ADDR_ERR (AS); /* get addr err flags */
|
||
bs_err = ADDR_ERR (BS);
|
||
AS = AS & ADDRMASK; /* clean addresses */
|
||
BS = BS & ADDRMASK;
|
||
pcq_r->qptr = pcq_p; /* update pc q ptr */
|
||
return reason;
|
||
} /* end sim_instr */
|
||
|
||
/* store addr_x - convert address to BCD character in x position
|
||
|
||
Inputs:
|
||
addr = address to convert
|
||
Outputs:
|
||
char = converted address character
|
||
*/
|
||
|
||
int32 store_addr_h (int32 addr)
|
||
{
|
||
int32 thous;
|
||
|
||
thous = (addr / 1000) & 03;
|
||
return bin_to_bcd[(addr % 1000) / 100] | (thous << V_ZONE);
|
||
}
|
||
|
||
int32 store_addr_t (int32 addr)
|
||
{
|
||
return bin_to_bcd[(addr % 100) / 10];
|
||
}
|
||
|
||
int32 store_addr_u (int32 addr)
|
||
{
|
||
int32 thous;
|
||
|
||
thous = (addr / 1000) & 014;
|
||
return bin_to_bcd[addr % 10] | (thous << (V_ZONE - 2));
|
||
}
|
||
|
||
/* div_add - add string for divide */
|
||
|
||
int32 div_add (int32 ap, int32 bp, int32 aend)
|
||
{
|
||
int32 a, b, c, r;
|
||
|
||
c = 0; /* init carry */
|
||
do { a = M[ap]; b = M[bp]; /* get operands */
|
||
r = bcd_to_bin[b & DIGIT] + /* sum digits + c */
|
||
bcd_to_bin[a & DIGIT] + c;
|
||
c = (r >= 10); /* set carry out */
|
||
M[bp] = sum_table[r]; /* store result */
|
||
ap--; bp--; }
|
||
while (ap >= aend);
|
||
return c;
|
||
}
|
||
|
||
/* div_sub - substract string for divide */
|
||
|
||
int32 div_sub (int32 ap, int32 bp, int32 aend)
|
||
{
|
||
int32 a, b, c, r;
|
||
|
||
c = 0; /* init borrow */
|
||
do { a = M[ap]; b = M[bp]; /* get operands */
|
||
r = bcd_to_bin[b & DIGIT] - /* a - b - borrow */
|
||
bcd_to_bin[a & DIGIT] - c;
|
||
c = (r < 0); /* set borrow out */
|
||
M[bp] = sum_table[r + 10]; /* store result */
|
||
ap--; bp--; }
|
||
while (ap >= aend);
|
||
b = M[bp] & CHAR; /* borrow position */
|
||
if (b != BCD_ZERO) { /* non-zero? */
|
||
r = bcd_to_bin[b & DIGIT] - c; /* subtract borrow */
|
||
M[bp] = sum_table[r]; /* store result */
|
||
return 0; } /* subtract worked */
|
||
return c; /* return borrow */
|
||
}
|
||
|
||
/* div_sign - set signs for divide */
|
||
|
||
void div_sign (int32 dvrc, int32 dvdc, int32 qp, int32 rp)
|
||
{
|
||
int32 sign = dvrc & ZONE; /* divisor sign */
|
||
|
||
M[rp] = M[rp] | ZONE; /* assume rem pos */
|
||
if (sign == BBIT) M[rp] = M[rp] & ~ABIT; /* if dvr -, rem - */
|
||
M[qp] = M[qp] | ZONE; /* assume quo + */
|
||
if (((dvdc & ZONE) == BBIT) ^ (sign == BBIT)) /* dvr,dvd diff? */
|
||
M[qp] = M[qp] & ~ABIT; /* make quo - */
|
||
return;
|
||
}
|
||
|
||
/* iomod - check on I/O modifiers
|
||
|
||
Inputs:
|
||
ilnt = instruction length
|
||
mod = modifier character
|
||
tptr = pointer to table of modifiers, end is -1
|
||
Output:
|
||
status = SCPE_OK if ok, STOP_INVM if invalid
|
||
*/
|
||
|
||
t_stat iomod (int32 ilnt, int32 mod, const int32 *tptr)
|
||
{
|
||
if ((ilnt != 2) && (ilnt != 5) && (ilnt < 8)) return SCPE_OK;
|
||
if (tptr == NULL) return STOP_INVM;
|
||
do { if (mod == *tptr++) return SCPE_OK; }
|
||
while (*tptr >= 0);
|
||
return STOP_INVM;
|
||
}
|
||
|
||
/* iodisp - dispatch load or move to I/O routine
|
||
|
||
Inputs:
|
||
dev = device number
|
||
unit = unit number
|
||
flag = move (MD_NORM) vs load (MD_WM)
|
||
mod = modifier
|
||
*/
|
||
|
||
t_stat iodisp (int32 dev, int32 unit, int32 flag, int32 mod)
|
||
{
|
||
if (dev == IO_INQ) return inq_io (flag, mod); /* inq terminal? */
|
||
if (dev == IO_DP) return dp_io (unit, flag, mod); /* disk pack? */
|
||
if (dev == IO_MT) return mt_io (unit, flag, mod); /* magtape? */
|
||
if (dev == IO_MTB) { /* binary? */
|
||
if (flag == MD_WM) return STOP_INVM; /* invalid */
|
||
return mt_io (unit, MD_BIN, mod); }
|
||
return STOP_NXD; /* not implemented */
|
||
}
|
||
|
||
/* Reset routine */
|
||
|
||
t_stat cpu_reset (DEVICE *dptr)
|
||
{
|
||
int32 i;
|
||
|
||
for (i = 0; i < 64; i++) ind[i] = 0; /* clr indicators */
|
||
ind[IN_UNC] = 1; /* ind[0] always on */
|
||
AS = 0; as_err = 1; /* clear AS */
|
||
BS = 0; bs_err = 1; /* clear BS */
|
||
D = 0; /* clear D */
|
||
hb_pend = 0; /* no halt br */
|
||
pcq_r = find_reg ("ISQ", NULL, dptr);
|
||
if (pcq_r) pcq_r->qptr = 0;
|
||
else return SCPE_IERR;
|
||
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] & (WM + CHAR);
|
||
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 & (WM + CHAR);
|
||
return SCPE_OK;
|
||
}
|
||
|
||
/* Memory size change */
|
||
|
||
t_stat cpu_set_size (UNIT *uptr, int32 val, char *cptr, void *desc)
|
||
{
|
||
int32 mc = 0;
|
||
uint32 i;
|
||
|
||
if ((val <= 0) || (val > MAXMEMSIZE) || ((val % 1000) != 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;
|
||
if (MEMSIZE > 4000) cpu_unit.flags = cpu_unit.flags | MA;
|
||
else cpu_unit.flags = cpu_unit.flags & ~MA;
|
||
return SCPE_OK;
|
||
}
|
||
|
||
/* Set history */
|
||
|
||
t_stat cpu_set_hist (UNIT *uptr, int32 val, char *cptr, void *desc)
|
||
{
|
||
int32 i, lnt;
|
||
t_stat r;
|
||
|
||
if (cptr == NULL) {
|
||
for (i = 0; i < hst_lnt; i++) hst[i].ilnt = 0;
|
||
hst_p = 0;
|
||
return SCPE_OK; }
|
||
lnt = (int32) get_uint (cptr, 10, HIST_MAX, &r);
|
||
if ((r != SCPE_OK) || (lnt && (lnt < HIST_MIN))) return SCPE_ARG;
|
||
hst_p = 0;
|
||
if (hst_lnt) {
|
||
free (hst);
|
||
hst_lnt = 0;
|
||
hst = NULL; }
|
||
if (lnt) {
|
||
hst = calloc (sizeof (struct InstHistory), lnt);
|
||
if (hst == NULL) return SCPE_MEM;
|
||
hst_lnt = lnt; }
|
||
return SCPE_OK;
|
||
}
|
||
|
||
/* Show history */
|
||
|
||
t_stat cpu_show_hist (FILE *st, UNIT *uptr, int32 val, void *desc)
|
||
{
|
||
int32 i, k, di, lnt;
|
||
char *cptr = (char *) desc;
|
||
t_value sim_eval[MAX_L + 1];
|
||
t_stat r;
|
||
struct InstHistory *h;
|
||
extern t_stat fprint_sym (FILE *ofile, t_addr addr, t_value *val,
|
||
UNIT *uptr, int32 sw);
|
||
|
||
if (hst_lnt == 0) return SCPE_NOFNC; /* enabled? */
|
||
if (cptr) {
|
||
lnt = (int32) get_uint (cptr, 10, hst_lnt, &r);
|
||
if ((r != SCPE_OK) || (lnt == 0)) return SCPE_ARG; }
|
||
else lnt = hst_lnt;
|
||
di = hst_p - lnt; /* work forward */
|
||
if (di < 0) di = di + hst_lnt;
|
||
fprintf (st, "IS IR\n\n");
|
||
for (k = 0; k < lnt; k++) { /* print specified */
|
||
h = &hst[(++di) % hst_lnt]; /* entry pointer */
|
||
if (h->ilnt) { /* instruction? */
|
||
fprintf (st, "%05d ", h->is);
|
||
for (i = 0; i < h->ilnt; i++)
|
||
sim_eval[i] = h->inst[i];
|
||
sim_eval[h->ilnt] = WM;
|
||
if ((fprint_sym (st, h->is, sim_eval, &cpu_unit, SWMASK ('M'))) > 0) {
|
||
fprintf (st, "(undefined)");
|
||
for (i = 0; i < h->ilnt; i++)
|
||
fprintf (st, "% 02o", h->inst[i]);
|
||
}
|
||
fputc ('\n', st); /* end line */
|
||
} /* end else instruction */
|
||
} /* end for */
|
||
return SCPE_OK;
|
||
}
|