1. New Features in 2.10-2 The build procedures have changed. There is only one UNIX makefile. To compile without Ethernet support, simply type gmake {target|all} To compile with Ethernet support, type gmake USE_NETWORK=1 {target|all} The Mingw batch files require Mingw release 2 and invoke the Unix makefile. There are still separate batch files for compilation with or without Ethernet support. 1.1 SCP and Libraries - The EVAL command will evaluate a symbolic type-in and display it in numeric form. - The ! command (with no arguments) will launch the host operating system command shell. The ! command (with an argument) executes the argument as a host operating system command. (Code from Mark Pizzolato) - Telnet sessions now recognize BREAK. How a BREAK is transmitted dependent on the particular Telnet client. (Code from Mark Pizzolato) - The sockets library includes code for active connections as well as listening connections. - The RESTORE command will restore saved memory size, if the simulator supports dynamic memory resizing. 1.2 PDP-1 - The PDP-1 supports the Type 24 serial drum (based on recently discovered documents). 1.3 18b PDP's - The PDP-4 supports the Type 24 serial drum (based on recently discovered documents). 1.4 PDP-11 - The PDP-11 implements a stub DEUNA/DELUA (XU). The real XU module will be included in a later release. 1.5 PDP-10 - The PDP-10 implements a stub DEUNA/DELUA (XU). The real XU module will be included in a later release. 1.6 HP 2100 - The IOP microinstruction set is supported for the 21MX as well as the 2100. - The HP2100 supports the Access Interprocessor Link (IPL). 1.7 VAX - If the VAX console is attached to a Telnet session, BREAK is interpreted as console halt. - The SET/SHOW HISTORY commands enable and display a history of the most recently executed instructions. (Code from Mark Pizzolato) 1.8 Terminals Multiplexors - BREAK detection was added to the HP, DEC, and Interdata terminal multiplexors. 1.9 Interdata 16b and 32b - First release. UNIX is not yet working. 1.10 SDS 940 - First release. 2. Bugs Fixed in 2.10-2 - PDP-11 console must default to 7b for early UNIX compatibility. - PDP-11/VAX TMSCP emulator was using the wrong packet length for read/write end packets. - Telnet IAC+IAC processing was fixed, both for input and output (found by Mark Pizzolato). - PDP-11/VAX Ethernet setting flag bits wrong for chained descriptors (found by Mark Pizzolato). 3. New Features in 2.10 vs prior releases 3.1 SCP and Libraries - The VT emulation package has been replaced by the capability to remote the console to a Telnet session. Telnet clients typically have more complete and robust VT100 emulation. - Simulated devices may now have statically allocated buffers, in addition to dynamically allocated buffers or disk-based data stores. - The DO command now takes substitutable arguments (max 9). In command files, %n represents substitutable argument n. - The initial command line is now interpreted as the command name and substitutable arguments for a DO command. This is backward compatible to prior versions. - The initial command line parses switches. -Q is interpreted as quiet mode; informational messages are suppressed. - The HELP command now takes an optional argument. HELP <cmd> types help on the specified command. - Hooks have been added for implementing GUI-based consoles, as well as simulator-specific command extensions. A few internal data structures and definitions have changed. - Two new routines (tmxr_open_master, tmxr_close_master) have been added to sim_tmxr.c. The calling sequence for sim_accept_conn has been changed in sim_sock.c. - The calling sequence for the VM boot routine has been modified to add an additional parameter. - SAVE now saves, and GET now restores, controller and unit flags. - Library sim_ether.c has been added for Ethernet support. 3.2 VAX - Non-volatile RAM (NVR) can behave either like a memory or like a disk-based peripheral. If unattached, it behaves like memory and is saved and restored by SAVE and RESTORE, respectively. If attached, its contents are loaded from disk by ATTACH and written back to disk at DETACH and EXIT. - SHOW <device> VECTOR displays the device's interrupt vector. A few devices allow the vector to be changed with SET <device> VECTOR=nnn. - SHOW CPU IOSPACE displays the I/O space address map. - The TK50 (TMSCP tape) has been added. - The DEQNA/DELQA (Qbus Ethernet controllers) have been added. - Autoconfiguration support has been added. - The paper tape reader has been removed from vax_stddev.c and now references a common implementation file, dec_pt.h. - Examine and deposit switches now work on all devices, not just the CPU. - Device address conflicts are not detected until simulation starts. 3.3 PDP-11 - SHOW <device> VECTOR displays the device's interrupt vector. Most devices allow the vector to be changed with SET <device> VECTOR=nnn. - SHOW CPU IOSPACE displays the I/O space address map. - The TK50 (TMSCP tape), RK611/RK06/RK07 (cartridge disk), RX211 (double density floppy), and KW11P programmable clock have been added. - The DEQNA/DELQA (Qbus Ethernet controllers) have been added. - Autoconfiguration support has been added. - The paper tape reader has been removed from pdp11_stddev.c and now references a common implementation file, dec_pt.h. - Device bootstraps now use the actual CSR specified by the SET ADDRESS command, rather than just the default CSR. Note that PDP-11 operating systems may NOT support booting with non-standard addresses. - Specifying more than 256KB of memory, or changing the bus configuration, causes all peripherals that are not compatible with the current bus configuration to be disabled. - Device address conflicts are not detected until simulation starts. 3.4 PDP-10 - SHOW <device> VECTOR displays the device's interrupt vector. A few devices allow the vector to be changed with SET <device> VECTOR=nnn. - SHOW CPU IOSPACE displays the I/O space address map. - The RX211 (double density floppy) has been added; it is off by default. - The paper tape now references a common implementation file, dec_pt.h. - Device address conflicts are not detected until simulation starts. 3.5 PDP-1 - DECtape (then known as MicroTape) support has been added. - The line printer and DECtape can be disabled and enabled. 3.6 PDP-8 - The RX28 (double density floppy) has been added as an option to the existing RX8E controller. - SHOW <device> DEVNO displays the device's device number. Most devices allow the device number to be changed with SET <device> DEVNO=nnn. - Device number conflicts are not detected until simulation starts. 3.7 IBM 1620 - The IBM 1620 simulator has been released. 3.8 AltairZ80 - A hard drive has been added for increased storage. - Several bugs have been fixed. 3.9 HP 2100 - The 12845A has been added and made the default line printer (LPT). The 12653A has been renamed LPS and is off by default. It also supports the diagnostic functions needed to run the DCPC and DMS diagnostics. - The 12557A/13210A disk defaults to the 13210A (7900/7901). - The 12559A magtape is off by default. - New CPU options (EAU/NOEAU) enable/disable the extended arithmetic instructions for the 2116. These instructions are standard on the 2100 and 21MX. - New CPU options (MPR/NOMPR) enable/disable memory protect for the 2100 and 21MX. - New CPU options (DMS/NODMS) enable/disable the dynamic mapping instructions for the 21MX. - The 12539 timebase generator autocalibrates. 3.10 Simulated Magtapes - Simulated magtapes recognize end of file and the marker 0xFFFFFFFF as end of medium. Only the TMSCP tape simulator can generate an end of medium marker. - The error handling in simulated magtapes was overhauled to be consistent through all simulators. 3.11 Simulated DECtapes - Added support for RT11 image file format (256 x 16b) to DECtapes. 4. Bugs Fixed in 2.10 vs prior releases - TS11/TSV05 was not simulating the XS0_MOT bit, causing failures under VMS. In addition, two of the CTL options were coded interchanged. - IBM 1401 tape was not setting a word mark under group mark for load mode reads. This caused the diagnostics to crash. - SCP bugs in ssh_break and set_logon were fixed (found by Dave Hittner). - Numerous bugs in the HP 2100 extended arithmetic, floating point, 21MX, DMS, and IOP instructions were fixed. Bugs were also fixed in the memory protect and DMS functions. The moving head disks (DP, DQ) were revised to simulate the hardware more accurately. Missing functions in DQ (address skip, read address) were added. - PDP-10 tape wouldn't boot, and then wouldn't read (reported by Michael Thompson and Harris Newman, respectively) - PDP-1 typewriter is half duplex, with only one shift state for both input and output (found by Derek Peschel) 5. General Notes WARNING: V2.10 has reorganized and renamed some of the definition files for the PDP-10, PDP-11, and VAX. Be sure to delete all previous source files before you unpack the Zip archive, or unpack it into a new directory structure. WARNING: V2.10 has a new, more comprehensive save file format. Restoring save files from previous releases will cause 'invalid register' errors and loss of CPU option flags, device enable/ disable flags, unit online/offline flags, and unit writelock flags. WARNING: If you are using Visual Studio .NET through the IDE, be sure to turn off the /Wp64 flag in the project settings, or dozens of spurious errors will be generated. WARNING: Compiling Ethernet support under Windows requires extra steps; see the Ethernet readme file. Ethernet support is currently available only for Windows, Linux, NetBSD, and OpenBSD.
1404 lines
48 KiB
C
1404 lines
48 KiB
C
/* i1401_cpu.c: IBM 1401 CPU simulator
|
||
|
||
Copyright (c) 1993-2002, 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.
|
||
|
||
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"
|
||
|
||
#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
|
||
|
||
/* 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 as_err = 0, bs_err = 0; /* error flags */
|
||
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 */
|
||
extern int32 sim_int_char;
|
||
extern int32 sim_brk_types, sim_brk_dflt, sim_brk_summ; /* breakpoint info */
|
||
|
||
t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw);
|
||
t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw);
|
||
t_stat cpu_reset (DEVICE *dptr);
|
||
t_stat cpu_set_size (UNIT *uptr, int32 val, char *cptr, void *desc);
|
||
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);
|
||
|
||
/* 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) },
|
||
{ 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) },
|
||
{ BRDATA (ISQ, pcq, 10, 14, PCQ_SIZE), REG_RO+REG_CIRC },
|
||
{ ORDATA (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 },
|
||
{ 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 };
|
||
|
||
/* Tables */
|
||
|
||
/* Opcode table - length, dispatch, and option flags. This table is also
|
||
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 */
|
||
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 */
|
||
L7 | AREQ | BREQ, /* move supress zero */
|
||
0, /* illegal */
|
||
L1 | L4 | L7 | AREQ | BREQ | NOWM, /* set word mark */
|
||
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 | L4 | L7, /* 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 | L4, /* 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 };
|
||
|
||
/* ASCII to BCD conversion */
|
||
|
||
const char ascii_to_bcd[128] = {
|
||
000, 000, 000, 000, 000, 000, 000, 000, /* 000 - 037 */
|
||
000, 000, 000, 000, 000, 000, 000, 000,
|
||
000, 000, 000, 000, 000, 000, 000, 000,
|
||
000, 000, 000, 000, 000, 000, 000, 000,
|
||
000, 052, 077, 013, 053, 034, 060, 032, /* 040 - 077 */
|
||
017, 074, 054, 037, 033, 040, 073, 021,
|
||
012, 001, 002, 003, 004, 005, 006, 007,
|
||
010, 011, 015, 056, 076, 035, 016, 072,
|
||
014, 061, 062, 063, 064, 065, 066, 067, /* 100 - 137 */
|
||
070, 071, 041, 042, 043, 044, 045, 046,
|
||
047, 050, 051, 022, 023, 024, 025, 026,
|
||
027, 030, 031, 075, 036, 055, 020, 057,
|
||
000, 061, 062, 063, 064, 065, 066, 067, /* 140 - 177 */
|
||
070, 071, 041, 042, 043, 044, 045, 046,
|
||
047, 050, 051, 022, 023, 024, 025, 026,
|
||
027, 030, 031, 000, 000, 000, 000, 000 };
|
||
|
||
/* BCD to ASCII conversion - also the "full" print chain */
|
||
|
||
char bcd_to_ascii[64] = {
|
||
' ', '1', '2', '3', '4', '5', '6', '7',
|
||
'8', '9', '0', '#', '@', ':', '>', '(',
|
||
'^', '/', 'S', 'T', 'U', 'V', 'W', 'X',
|
||
'Y', 'Z', '\'', ',', '%', '=', '\\', '+',
|
||
'-', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
|
||
'Q', 'R', '!', '$', '*', ']', ';', '_',
|
||
'&', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
|
||
'H', 'I', '?', '.', ')', '[', '<', '"' };
|
||
|
||
/* 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 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, D, ilnt, flags;
|
||
int32 op, xa, t, wm, dev, unit;
|
||
int32 a, b, i, 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 */
|
||
D = 0;
|
||
reason = 0;
|
||
|
||
/* Main instruction fetch/decode loop */
|
||
|
||
while (reason == 0) { /* loop until halted */
|
||
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 */
|
||
|
||
if ((M[IS] & WM) == 0) { /* 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; /* WM? 1 char inst */
|
||
D = 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) { /* WM? 2 char inst */
|
||
AS = AS | BA; /* ASTAR bad */
|
||
if (!(flags & MLS)) BS = AS;
|
||
goto CHECK_LENGTH; }
|
||
AS = AS + ten_table[t]; /* build A addr */
|
||
dev = t; /* save char as dev */
|
||
PP (IS);
|
||
|
||
if ((t = M[IS]) & WM) { /* WM? 3 char inst */
|
||
AS = AS | BA; /* ASTAR bad */
|
||
if (!(flags & MLS)) BS = AS;
|
||
goto CHECK_LENGTH; }
|
||
AS = AS + one_table[t]; /* finish A addr */
|
||
unit = (t == BCD_ZERO)? 0: t; /* save char as unit */
|
||
xa = (AS >> V_INDEX) & M_INDEX; /* get index reg */
|
||
if (xa && (D != 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; /* 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) { /* WM? 5 char inst */
|
||
BS = BS | BA; /* BSTAR bad */
|
||
goto CHECK_LENGTH; }
|
||
BS = BS + ten_table[t]; /* build B addr */
|
||
PP (IS);
|
||
|
||
if ((t = M[IS]) & WM) { /* WM? 6 char inst */
|
||
BS = BS | BA; /* BSTAR bad */
|
||
goto CHECK_LENGTH; }
|
||
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 ((M[IS] & WM) || (flags & NOWM)) goto CHECK_LENGTH; /* WM? 7 chr */
|
||
D = M[IS]; /* last char is D */
|
||
do { PP (IS); } while ((M[IS] & WM) == 0); /* find word mark */
|
||
|
||
CHECK_LENGTH:
|
||
ilnt = IS - saved_IS; /* get lnt */
|
||
if (((flags & len_table [(ilnt <= 8)? ilnt: 8]) == 0) && /* valid lnt? */
|
||
((flags & HNOP) == 0)) reason = STOP_INVL;
|
||
if ((flags & BREQ) && ADDR_ERR (BS)) reason = STOP_INVB; /* valid A? */
|
||
if ((flags & AREQ) && ADDR_ERR (AS)) reason = STOP_INVA; /* valid B? */
|
||
if (reason) break; /* error in fetch? */
|
||
switch (op) { /* case on opcode */
|
||
|
||
/* Move 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
|
||
MCM: copy A to B, preserving B WM, fetch fetch
|
||
until record or group mark
|
||
MSZ: 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
|
||
*/
|
||
|
||
case OP_MCW: /* move char */
|
||
if (ilnt >= 8) { /* I/O form? */
|
||
reason = iodisp (dev, unit, MD_NORM, D);
|
||
break; }
|
||
if (ADDR_ERR (AS)) { /* check A addr */
|
||
reason = STOP_INVA;
|
||
break; }
|
||
do {
|
||
M[BS] = (M[BS] & WM) | (M[AS] & CHAR); /* move char */
|
||
wm = M[AS] | M[BS];
|
||
MM (AS); MM (BS); } /* decr pointers */
|
||
while ((wm & WM) == 0); /* stop on A,B WM */
|
||
break;
|
||
|
||
case OP_LCA: /* load char */
|
||
if (ilnt >= 8) { /* 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;
|
||
|
||
case OP_MCM: /* move to rec/group */
|
||
do {
|
||
M[BS] = (M[BS] & WM) | (M[AS] & CHAR); /* move char */
|
||
t = M[AS];
|
||
PP (AS); PP (BS); } /* incr pointers */
|
||
while (((t & CHAR) != BCD_RECMRK) && (t != (BCD_GRPMRK + WM)));
|
||
break;
|
||
|
||
case OP_MSZ: /* move suppress zero */
|
||
bsave = BS; /* save B start */
|
||
qzero = 1; /* set suppress */
|
||
do {
|
||
M[BS] = M[AS] & ((BS != bsave)? CHAR: DIGIT); /* copy char */
|
||
wm = M[AS];
|
||
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);
|
||
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;
|
||
|
||
/* Compare
|
||
|
||
A and B are checked in fetch
|
||
*/
|
||
|
||
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;
|
||
|
||
/* Branch instructions A check B check
|
||
|
||
B 1/8 char: branch if B char equals d if branch here
|
||
B 4 char: unconditional branch if branch
|
||
B 5 char: branch if indicator[d] is set if branch
|
||
BWZ: branch if (d<0>: B char WM) if branch here
|
||
(d<1>: B char zone = d zone)
|
||
BBE: branch if B char & d non-zero 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 {
|
||
if (ADDR_ERR (BS)) { /* branch char eq */
|
||
reason = STOP_INVB; /* validate B addr */
|
||
break; }
|
||
if ((M[BS] & CHAR) == D) { BRANCH; } /* char equal? */
|
||
else { MM (BS); } }
|
||
break;
|
||
|
||
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
|
||
*/
|
||
|
||
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;
|
||
|
||
/* 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
|
||
MTF: magtape functions
|
||
*/
|
||
|
||
case OP_R: /* read */
|
||
if (reason = iomod (ilnt, D, NULL)) break; /* valid modifier? */
|
||
reason = read_card (ilnt, D); /* read card */
|
||
BS = CDR_BUF + CDR_WIDTH;
|
||
if (ilnt >= 4) { 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) { BRANCH; } /* check for branch */
|
||
break;
|
||
|
||
case OP_P: /* punch */
|
||
if (reason = iomod (ilnt, D, NULL)) break; /* valid modifier? */
|
||
reason = punch_card (ilnt, D); /* punch card */
|
||
BS = CDP_BUF + CDP_WIDTH;
|
||
if (ilnt >= 4) { 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) { 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) { 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) { 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) { 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) { BRANCH; } /* check for branch */
|
||
break;
|
||
|
||
case OP_CC: /* carriage control */
|
||
if (reason = carriage_control (D)) break; /* car ctrl, error? */
|
||
if (ilnt >= 4) { BRANCH; } /* check for branch */
|
||
break;
|
||
|
||
case OP_MTF: /* magtape function */
|
||
if (reason = iomod (ilnt, D, mtf_mod)) break; /* valid modifier? */
|
||
if (reason = mt_func (unit, D)) break; /* 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
|
||
*/
|
||
|
||
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)) break; /* rescan again? */
|
||
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.
|
||
*/
|
||
|
||
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 muchy 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.
|
||
*/
|
||
|
||
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;
|
||
|
||
/* Miscellaneous 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
|
||
CS: clear from B down to nearest hundreds address if branch fetch
|
||
MA: add A addr and B addr, store at B addr fetch fetch
|
||
SAR: store A* at A addr fetch
|
||
SBR: store B* at A addr fetch
|
||
NOP: no operation
|
||
H: halt
|
||
*/
|
||
|
||
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;
|
||
|
||
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;
|
||
|
||
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;
|
||
|
||
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;
|
||
|
||
case OP_NOP: /* nop */
|
||
break;
|
||
|
||
case OP_H: /* halt */
|
||
if (ilnt >= 4) { BRANCH; } /* branch if called */
|
||
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;
|
||
ind[IN_UNC] = 1;
|
||
AS = 0; as_err = 1;
|
||
BS = 0; bs_err = 1;
|
||
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;
|
||
t_addr 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;
|
||
}
|