simh-testsetgenerator/Ibm1130/ibm1130_cpu.c
Bob Supnik 2bcd1e7c4c Notes For V2.10-2
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.
2011-04-15 08:33:56 -07:00

1874 lines
58 KiB
C

/* ibm1130_cpu.c: IBM 1130 CPU simulator
Based on the SIMH package written by Robert M Supnik
* (C) Copyright 2002, Brian Knittel.
* You may freely use this program, but: it offered strictly on an AS-IS, AT YOUR OWN
* RISK basis, there is no warranty of fitness for any purpose, and the rest of the
* usual yada-yada. Please keep this notice and the copyright in any distributions
* or modifications.
*
* This is not a supported product, but I welcome bug reports and fixes.
* Mail to sim@ibm1130.org
25-Jun-01 BLK Written
10-May-02 BLK Fixed bug in MDX instruction
27-Mar-02 BLK Made BOSC work even in short form
16-Aug-02 BLK Fixed bug in multiply instruction; didn't work with negative values
The register state for the IBM 1130 CPU is:
IAR instruction address register
ACC accumulator
EXT accumulator extension
Oflow overflow bit
Carry carry bit
CES console entry switches
ipl current interrupt level, -1 = non interrupt
iplpending bitmap of pending interrupts
wait_state current CPU state: running or waiting
DSW console run/stop switch device status word
RUNMODE processor step/run mode (may also imply IntRun)
BREAK breakpoint address
WRU simulator-break character
IntRun Int Run flag (causes level 5 interrupt after every instruction)
ILSW0..5 interrupt level status words
The SAR (storage address register) and SBR (storage buffer register) are updated
but not saved in the CPU state; they matter only to the GUI.
Interrupt handling: interrupts occur when any device on any level has an
active interrupt. XIO commands can clear specific IRQ bits. When this
happens, we have to evaluate all devices on the same IRQ level for remaining
indicators. The flag int_req is set with a bit corresponding to the IRQ level
when any interrupt indicator is activated.
The 1130 console has a switch that controls several run modes: SS (single processor
step), SCLK (single clock step), SINST (single instruction step), INT_RUN
(IRQ 5 after each non interrupt-handler instruction) and RUN (normal operation).
This simulator does not implement SS and SCLK. The simulator GUI console handles
SINST, so we only have to worry about INT_RUN. The console command SET CPU IntRun sets
the tmode (trace mode) flag; this causes a level 5 interrupt after each
instruction.
The IBM 1130 instruction formats are
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| opcode | F| T | | general format
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| opcode | 0| T | DISPLACEMENT | short instruction
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| opcode | 1| T | I| MODIFIER | long instruction
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| ADDRESS |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
opcode in MSBits
F = format. 0 = short (1 word), 1 = long (2 word) instruction
T = Tag 00 = no index register (e.g. IAR relative)
01 = use index register 1 (e.g. core address 1 = M[1])
02 = use index register 2 (e.g. core address 2 = M[2])
03 = use index register 3 (e.g. core address 3 = M[3])
DISPLACEMENT = two's complement (must be sign-extended)
I = Indirect
Note that IAR = instruction address+1 when instruction is being decoded.
In normal addressing mode, effective address (EA) is computed as follows:
F = 0 T = 0 EA = IAR + DISPLACEMENT
0 1 IAR + DISPLACEMENT + M[1]
0 2 IAR + DISPLACEMENT + M[2]
0 3 IAR + DISPLACEMENT + M[3]
F = 1 T = 0 I = 0 EA = ADDRESS
1 1 0 ADDRESS + M[1]
1 2 0 ADDRESS + M[2]
1 3 0 ADDRESS + M[3]
1 0 1 M[ADDRESS]
1 1 1 M[ADDRESS + M[1]]
1 2 1 M[ADDRESS + M[2]]
1 3 1 M[ADDRESS + M[3]]
Loads or stores are then made to/from MEM[EA]. Some instructions have special
weird addressing modes. Simulator code precomputes standard addressing for
all instructions though it's not always used.
General notes:
Adding I/O devices requires modifications to three modules:
ibm1130_defs.h add interrupt request definitions
ibm1130_cpu.c add XIO command linkages
ibm1130_sys.c add to sim_devices
*/
/* ------------------------------------------------------------------------
* Definitions
* ------------------------------------------------------------------------ */
#include <stdarg.h>
#include "ibm1130_defs.h"
#define save_ibkpt (cpu_unit.u3) /* will be SAVEd */
#define UPDATE_BY_TIMER
#define ENABLE_BACKTRACE
#define CGI_SUPPORT
static void cgi_start(void);
static void cgi_stop(t_stat reason);
// hook pointers from scp.c
void (*sim_vm_init) (void) = &sim_init;
extern char* (*sim_vm_read) (char *ptr, int32 size, FILE *stream);
extern void (*sim_vm_post) (t_bool from_scp);
extern CTAB *sim_vm_cmd;
// space to store extra simulator-specific commands
#define MAX_EXTRA_COMMANDS 10
CTAB x_cmds[MAX_EXTRA_COMMANDS];
#ifdef WIN32
# define CRLF "\r\n"
#else
# define CRLF "\n"
#endif
/* ------------------------------------------------------------------------
* initializers for globals
* ------------------------------------------------------------------------ */
#define SIGN_BIT(v) ((v) & 0x8000)
#define DWSIGN_BIT(v) ((v) & 0x80000000)
uint16 M[MAXMEMSIZE]; /* core memory, up to 32Kwords (note: don't even think about trying 64K) */
uint16 ILSW[6] = {0,0,0,0,0,0}; /* interrupt level status words */
int32 IAR; /* instruction address register */
int32 prev_IAR; /* instruction address register at start of current instruction */
int32 SAR, SBR; /* storage address/buffer registers */
int32 OP, TAG, CCC; /* instruction decoded pieces */
int32 CES; /* console entry switches */
int32 ACC, EXT; /* accumulator and extension */
int32 RUNMODE; /* processor run/step mode */
int32 ipl = -1; /* current interrupt level (-1 = not handling irq) */
int32 iplpending = 0; /* interrupted IPL's */
int32 tbit = 0; /* trace flag (causes level 5 IRQ after each instr) */
int32 V = 0, C = 0; /* condition codes */
int32 wait_state = 0; /* wait state (waiting for an IRQ) */
int32 wait_lamp = TRUE; /* alternate indicator to light the wait lamp on the GUI */
int32 int_req = 0; /* sum of interrupt request levels active */
int32 int_lamps = 0; /* accumulated version of int_req - gives lamp persistence */
int32 int_mask; /* current active interrupt mask (ipl sensitive) */
int32 mem_mask;
int32 cpu_dsw = 0; /* CPU device status word */
int32 ibkpt_addr = -1; /* breakpoint addr */
int32 sim_gui = TRUE; /* enable gui */
t_bool running = FALSE; /* TRUE if CPU is running */
t_bool power = TRUE; /* TRUE if CPU power is on */
t_bool cgi = FALSE; /* TRUE if we are running as a CGI program */
t_stat reason; /* CPU execution loop control */
static int32 int_masks[6] = {
0x00, 0x20, 0x30, 0x38, 0x3C, 0x3E /* IPL 0 is highest prio (sees no other interrupts) */
};
/* ------------------------------------------------------------------------
* Function declarations
* ------------------------------------------------------------------------ */
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_svc (UNIT *uptr);
t_stat cpu_set_size (UNIT *uptr, int32 value, char *cptr, void *desc);
void calc_ints (void);
extern t_stat ts_wr (int32 data, int32 addr, int32 access);
extern t_stat detach_cmd (int flags, char *cptr);
extern UNIT cr_unit;
extern int32 sim_switches;
#ifdef ENABLE_BACKTRACE
static void archive_backtrace(char *inst);
static void reset_backtrace (void);
static void show_backtrace (int nshow);
static t_stat backtrace_cmd (int flag, char *cptr);
#else
#define archive_backtrace(inst)
#define reset_backtrace()
#define show_backtrace(ntrace)
#endif
static void init_console_window (void);
static void destroy_console_window (void);
static t_stat view_cmd (int flag, char *cptr);
static t_stat cgi_cmd (int flag, char *cptr);
static t_stat cpu_attach (UNIT *uptr, char *cptr);
static t_bool bsctest (int32 DSPLC, t_bool reset_V);
static void exit_irq (void);
static void trace_instruction (void);
/* ------------------------------------------------------------------------
* 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 (&cpu_svc, UNIT_FIX | UNIT_BINK | UNIT_ATTABLE | UNIT_SEQ, INIMEMSIZE) };
REG cpu_reg[] = {
{ HRDATA (IAR, IAR, 32) },
{ HRDATA (ACC, ACC, 32) },
{ HRDATA (EXT, EXT, 32) },
{ FLDATA (Oflow, V, 1) },
{ FLDATA (Carry, C, 1) },
{ HRDATA (CES, CES, 32) },
{ HRDATA (ipl, ipl, 32), REG_RO },
{ HRDATA (iplpending, iplpending, 32), REG_RO },
{ HRDATA (wait_state, wait_state, 32)},
{ HRDATA (DSW, cpu_dsw, 32), REG_RO },
{ HRDATA (RUNMODE, RUNMODE, 32) },
{ HRDATA (BREAK, ibkpt_addr, 32) },
{ ORDATA (WRU, sim_int_char, 8) },
{ FLDATA (IntRun, tbit, 1) },
{ HRDATA (ILSW0, ILSW[0], 32), REG_RO },
{ HRDATA (ILSW1, ILSW[1], 32), REG_RO },
{ HRDATA (ILSW2, ILSW[2], 32), REG_RO },
{ HRDATA (ILSW3, ILSW[3], 32), REG_RO },
{ HRDATA (ILSW4, ILSW[4], 32), REG_RO },
{ HRDATA (ILSW5, ILSW[5], 32), REG_RO },
{ NULL}
};
MTAB cpu_mod[] = {
{ UNIT_MSIZE, 4096, NULL, "4KW", &cpu_set_size},
{ UNIT_MSIZE, 8192, NULL, "8KW", &cpu_set_size},
{ UNIT_MSIZE, 16384, NULL, "16KW", &cpu_set_size},
{ UNIT_MSIZE, 32768, NULL, "32KW", &cpu_set_size},
{ 0 } };
DEVICE cpu_dev = {
"CPU", &cpu_unit, cpu_reg, cpu_mod,
1, 16, 16, 1, 16, 16,
&cpu_ex, &cpu_dep, &cpu_reset,
NULL, cpu_attach, NULL}; // attaching to CPU creates cpu log file
/* ------------------------------------------------------------------------
Memory read/write -- save SAR and SBR on the way in and out
* ------------------------------------------------------------------------ */
int32 ReadW (int32 a)
{
SAR = a;
SBR = (int32) M[(a) & mem_mask];
return SBR;
}
void WriteW (int32 a, int32 d)
{
SAR = a;
SBR = d;
M[a & mem_mask] = (int16) d;
}
/* ------------------------------------------------------------------------
* upcase - force a string to uppercase (ASCII)
* ------------------------------------------------------------------------ */
char *upcase (char *str)
{
char *s;
for (s = str; *s; s++) {
if (*s >= 'a' && *s <= 'z')
*s -= 32;
}
return str;
}
/* ------------------------------------------------------------------------
* calc_ints - set appropriate bits in int_req if any interrupts are pending on given levels
*
* int_req:
* bit 5 4 3 2 1 0
* \ \ \ \ \ \
* \ \ \ \ \ interrupt level 5 pending (lowest priority)
* \ . . .
* interrupt level 0 pending (highest priority)
*
* int_mask is set according to current interrupt level (ipl)
*
* 0 0 0 0 0 0 ipl = 0 (currently servicing highest priority interrupt)
* 1 0 0 0 0 0 1
* 1 1 0 0 0 0 2
* 1 1 1 0 0 0 3
* 1 1 1 1 0 0 4
* 1 1 1 1 1 0 5 (currently servicing lowest priority interrupt)
* 1 1 1 1 1 1 -1 (not servicing an interrupt)
* ------------------------------------------------------------------------ */
void calc_ints (void)
{
register int i;
register int32 newbits = 0;
GUI_BEGIN_CRITICAL_SECTION // using critical section here so we don't mislead the GUI thread
for (i = 6; --i >= 0; ) {
newbits >>= 1;
if (ILSW[i])
newbits |= 0x20;
}
int_req = newbits;
int_lamps |= int_req;
int_mask = (ipl < 0) ? 0xFFFF : int_masks[ipl]; /* be sure this is set correctly */
GUI_END_CRITICAL_SECTION
}
/* ------------------------------------------------------------------------
* instruction processor
* ------------------------------------------------------------------------ */
#define INCREMENT_IAR IAR = (IAR + 1) & mem_mask
#define DECREMENT_IAR IAR = (IAR - 1) & mem_mask
void bail (char *msg)
{
printf("%s\n", msg);
exit(1);
}
static void weirdop (char *msg, int offset)
{
printf("Weird opcode: %s at %04x\n", msg, IAR+offset);
}
static char *xio_devs[] = {
"0?", "console", "1142card", "1134papertape",
"dsk0", "1627plot", "1132print", "switches",
"1231omr", "2501card", "comm", "b?",
"sys7", "d?", "e?", "f?",
"10?", "dsk1", "dsk2", "dsk3",
"dsk4", "dsk5", "dsk6", "dsk7+",
"18?", "2250disp", "1a?", "1b",
"1c?", "1d?", "1e?", "1f?"
};
static char *xio_funcs[] = {
"0?", "write", "read", "sense_irq",
"control", "initw", "initr", "sense"
};
t_stat sim_instr (void)
{
extern int32 sim_interval;
extern UNIT *sim_clock_queue;
int32 i, eaddr, INDIR, IR, F, DSPLC, word2, oldval, newval, src, src2, dst, abit, xbit;
int32 iocc_addr, iocc_op, iocc_dev, iocc_func, iocc_mod;
char msg[50];
int cwincount = 0, status;
static long ninstr = 0;
static char *intlabel[] = {"INT0","INT1","INT2","INT3","INT4","INT5"};
#ifdef CGI_SUPPORT
if (cgi)
cgi_start();
#endif
if (running) /* this is definitely not reentrant */
return -1;
if (! power) /* this matters only to the GUI */
return STOP_POWER_OFF;
running = TRUE;
mem_mask = MEMSIZE - 1; /* set other useful variables */
calc_ints();
/* Main instruction fetch/decode loop */
reason = 0;
wait_lamp = 0; /* release lock on wait lamp */
#ifdef GUI_SUPPORT
update_gui(TRUE);
gui_run(TRUE);
#endif
while (reason == 0) {
IAR &= mem_mask;
#ifdef GUI_SUPPORT
#ifndef UPDATE_BY_TIMER
#if (UPDATE_INTERVAL > 0)
if (--cwincount <= 0) {
update_gui(FALSE); /* update console lamps only every so many instructions */
cwincount = UPDATE_INTERVAL + (rand() % MIN(UPDATE_INTERVAL, 32));
}
#else
update_gui(FALSE);
#endif // ifdef UPDATE_INTERVAL
#endif // ifndef UPDATE_BY_TIMER
#endif // ifdef GUI_SUPPORT
if (sim_interval <= 0) { /* any events timed out? */
if (sim_clock_queue != NULL) {
if ((status = sim_process_event()) != 0)
reason = status;
calc_ints();
continue;
}
}
if (int_req & int_mask) { /* any pending interrupts? */
for (i = 0; i <= 5; i++) /* find highest pending interrupt */
if ((int_req & int_mask) & (0x20 >> i))
break;
if (i >= 6) { /* nothing to do? */
calc_ints(); /* weird. recalculate */
continue; /* back to fetch */
}
GUI_BEGIN_CRITICAL_SECTION
if (ipl >= 0) /* save previous IPL in bit stack */
iplpending |= (0x20 >> ipl);
ipl = i; /* set new interrupt level */
int_mask = int_masks[i]; /* set appropriate mask */
GUI_END_CRITICAL_SECTION
wait_state = 0; /* exit wait state */
eaddr = ReadW(8+i); /* get IRQ vector */
archive_backtrace(intlabel[i]);
WriteW(eaddr, IAR); /* save IAR */
IAR = (eaddr+1) & mem_mask; /* go to next address */
continue; /* now continue processing */
} /* end if int_req */
if (wait_state) { /* waiting? */
sim_interval = 0; /* run the clock out */
if (sim_qcount() <= 1) { /* waiting for keyboard only */
if (keyboard_is_locked()) { /* CPU is not expecting a keystroke */
if (wait_state == WAIT_OP)
reason = STOP_WAIT; /* end the simulation */
else
reason = STOP_INVALID_INSTR;
}
else { /* we are actually waiting for a keystroke */
if ((status = sim_process_event()) != 0) /* get it with wait_state still set */
reason = status;
}
}
else if (sim_clock_queue == NULL) { /* not waiting for anything */
if (wait_state == WAIT_OP)
reason = STOP_WAIT; /* end the simulation */
else
reason = STOP_INVALID_INSTR;
}
if (gdu_active()) /* but don't stop simulator if 2250 GDU is running */
reason = 0;
continue;
}
if (IAR == ibkpt_addr) { /* simulator breakpoint? */
save_ibkpt = ibkpt_addr; /* save bkpt */
ibkpt_addr = ibkpt_addr | ILL_ADR_FLAG; /* disable */
sim_activate(&cpu_unit, 1); /* sched re-enable after next instruction */
reason = STOP_IBKPT; /* stop simulation */
cwincount = 0;
continue;
}
ninstr++;
if (cpu_unit.flags & UNIT_ATT)
trace_instruction(); /* log CPU details if logging is enabled */
prev_IAR = IAR; /* save IAR before incrementing it */
IR = ReadW(IAR); /* fetch 1st word of instruction */
INCREMENT_IAR;
sim_interval = sim_interval - 1; /* this constitutes one tick of the simulation clock */
OP = (IR >> 11) & 0x1F; /* opcode */
F = IR & 0x0400; /* format bit: 1 = long instr */
TAG = IR & 0x0300; /* tag bits: index reg x */
if (TAG)
TAG >>= 8;
// here I compute the usual effective address on the assumption that the instruction will need it. Some don't.
if (F) { /* long instruction, ASSUME it's valid (have to decrement IAR if not) */
INDIR = IR & 0x0080; /* indirect bit */
DSPLC = IR & 0x007F; /* displacement or modifier */
if (DSPLC & 0x0040)
DSPLC |= ~ 0x7F; /* sign extend */
word2 = ReadW(IAR); /* get reference address */
INCREMENT_IAR; /* bump the instruction address register */
eaddr = word2; /* assume standard addressing & compute effective address */
if (TAG) /* if indexed */
eaddr += ReadW(TAG); /* add index register value (stored in core) */
if (INDIR) /* if indirect addressing */
eaddr = ReadW(eaddr); /* pick up referenced address */
}
else { /* short instruction, use displacement */
INDIR = 0; /* never indirect */
DSPLC = IR & 0x00FF; /* get displacement */
if (DSPLC & 0x0080)
DSPLC |= ~ 0xFF;
if (TAG) /* if indexed */
eaddr = ReadW(TAG) + DSPLC; /* add index register value (stored in core) */
else
eaddr = IAR + DSPLC; /* otherwise relative to IAR after fetch */
}
switch (OP) { /* decode instruction */
case 0x01: /* --- XIO --- */
iocc_addr = ReadW(eaddr); /* get IOCC packet */
iocc_op = ReadW(eaddr|1); /* note 'or' not plus, address must be even for proper operation */
iocc_dev = (iocc_op >> 11) & 0x001F;
iocc_func = (iocc_op >> 8) & 0x0007;
iocc_mod = iocc_op & 0x00FF;
if (cpu_unit.flags & UNIT_ATT)
trace_io("* XIO %s %s mod %02x addr %04x", xio_funcs[iocc_func], xio_devs[iocc_dev], iocc_mod, iocc_addr);
ACC = 0; /* ACC is destroyed, and default XIO_SENSE_DEV result is 0 */
switch (iocc_func) {
case XIO_UNUSED:
sprintf(msg, "Unknown op %x on device %02x", iocc_func, iocc_dev);
xio_error(msg);
break;
case XIO_SENSE_IRQ: /* examine current Interrupt Level Status Word */
ACC = (ipl >= 0) ? ILSW[ipl] : 0;
break;
default: /* perform device-specific operation */
switch (iocc_dev) {
case 0x01: /* console keyboard and printer */
xio_1131_console(iocc_addr, iocc_func, iocc_mod);
break;
case 0x02: /* 1142 card reader/punch */
xio_1142_card(iocc_addr, iocc_func, iocc_mod);
break;
case 0x03: /* 1134 paper tape reader/punch */
xio_1134_papertape(iocc_addr, iocc_func, iocc_mod);
break;
case 0x04: /* CPU disk storage */
xio_disk(iocc_addr, iocc_func, iocc_mod, 0);
break;
case 0x05: /* 1627 plotter */
xio_1627_plotter(iocc_addr, iocc_func, iocc_mod);
break;
case 0x06: /* 1132 Printer */
xio_1132_printer(iocc_addr, iocc_func, iocc_mod);
break;
case 0x07: /* console switches, stop key, run mode */
xio_1131_switches(iocc_addr, iocc_func, iocc_mod);
break;
case 0x08: /* 1231 optical mark reader */
xio_1231_optical(iocc_addr, iocc_func, iocc_mod);
break;
case 0x09: /* 2501 card reader */
xio_2501_card(iocc_addr, iocc_func, iocc_mod);
break;
case 0x0a: /* synchronous comm adapter */
xio_1131_synch(iocc_addr, iocc_func, iocc_mod);
break;
case 0x0c: /* IBM System/7 interprocessor link */
xio_system7(iocc_addr, iocc_func, iocc_mod);
break;
case 0x11: /* 2310 Disk Storage, Drive 1, or 2311 Disk Storage Drive. Drive 1, Disk 1 */
xio_disk(iocc_addr, iocc_func, iocc_mod, 1);
break;
case 0x12: /* 2310 Disk Storage, Drive 2, or 2311 Disk Storage Drive. Drive 1, Disk 2 */
xio_disk(iocc_addr, iocc_func, iocc_mod, 2);
break;
case 0x13: /* 2310 Disk Storage, Drive 3, or 2311 Disk Storage Drive. Drive 1, Disk 3 */
xio_disk(iocc_addr, iocc_func, iocc_mod, 3);
break;
case 0x14: /* 2310 Disk Storage, Drive 4, or 2311 Disk Storage Drive. Drive 1, Disk 4 */
xio_disk(iocc_addr, iocc_func, iocc_mod, 4);
break;
case 0x15: /* 1403 Printer */
xio_1403_printer(iocc_addr, iocc_func, iocc_mod);
break;
case 0x16: /* 2311 Disk Storage Drive. Drive 1, Disk 5 */
xio_disk(iocc_addr, iocc_func, iocc_mod, -1);
break;
case 0x17: /* 2311 Disk Storage Drive, Drive 2, Disk 1 through 5 */
xio_disk(iocc_addr, iocc_func, iocc_mod, -1);
break;
case 0x19: /* 2250 Display Unit */
xio_2250_display(iocc_addr, iocc_func, iocc_mod);
break;
default:
sprintf(msg, "unknown device %02x", iocc_dev);
xio_error(msg);
break;
}
}
calc_ints(); /* after every XIO, reset int_mask just in case */
break;
case 0x02: /* --- SLA,SLT,SLC,SLCA,NOP - Shift Left family --- */
if (F) {
weirdop("Long Left Shift", -2);
DECREMENT_IAR;
}
CCC = ((TAG == 0) ? DSPLC : ReadW(TAG)) & 0x003F;
if (CCC == 0)
break; /* shift of zero is a NOP */
switch (IR & 0x00C0) {
case 0x0040: /* SLCA */
if (TAG) {
while (CCC > 0 && (ACC & 0x8000) == 0) {
ACC <<= 1;
CCC--;
}
C = (CCC != 0);
WriteW(TAG, ReadW(TAG) & 0xFF00 | CCC); /* put low 6 bits back into index register and zero bits 8 and 9 */
break;
}
/* if TAG == 0, fall through and treat like normal shift SLA */
case 0x0000: /* SLA */
while (CCC > 0) {
C = (ACC & 0x8000);
ACC = (ACC << 1) & 0xFFFF;
CCC--;
}
break;
case 0x00C0: /* SLC */
if (TAG) {
while (CCC > 0 && (ACC & 0x8000) == 0) {
abit = (EXT & 0x8000) >> 15;
ACC = ((ACC << 1) & 0xFFFF) | abit;
EXT = (EXT << 1);
CCC--;
}
C = (CCC != 0);
WriteW(TAG, ReadW(TAG) & 0xFF00 | CCC); /* put 6 bits back into low byte of index register */
break;
}
/* if TAG == 0, fall through and treat like normal shift SLT */
case 0x0080: /* SLT */
while (CCC > 0) {
C = (ACC & 0x8000);
abit = (EXT & 0x8000) >> 15;
ACC = ((ACC << 1) & 0xFFFF) | abit;
EXT = (EXT << 1) & 0xFFFF;
CCC--;
}
break;
default:
bail("SLA switch, can't happen");
break;
}
break;
case 0x03: /* --- SRA, SRT, RTE - Shift Right family --- */
if (F) {
weirdop("Long Right Shift", -2);
DECREMENT_IAR;
}
CCC = ((TAG == 0) ? DSPLC : ReadW(TAG)) & 0x3F;
if (CCC == 0)
break; /* NOP */
switch (IR & 0x00C0) {
case 0x0000: /* SRA */
ACC = (CCC < 16) ? ((ACC & 0xFFFF) >> CCC) : 0;
CCC = 0;
break;
case 0x0040: /* invalid */
wait_state = WAIT_INVALID_OP;
break;
case 0x0080: /* SRT */
while (CCC > 0) {
xbit = (ACC & 0x0001) << 15;
abit = (ACC & 0x8000);
ACC = (ACC >> 1) & 0x7FFF | abit;
EXT = (EXT >> 1) & 0x7FFF | xbit;
CCC--;
}
break;
case 0x00C0: /* RTE */
while (CCC > 0) {
abit = (EXT & 0x0001) << 15;
xbit = (ACC & 0x0001) << 15;
ACC = (ACC >> 1) & 0x7FFF | abit;
EXT = (EXT >> 1) & 0x7FFF | xbit;
CCC--;
}
break;
default:
bail("SRA switch, can't happen");
break;
}
break;
case 0x04: /* --- LDS - Load Status --- */
if (F) { /* never fetches second word */
weirdop("Long LDS", -2);
DECREMENT_IAR;
}
V = (DSPLC & 1);
C = (DSPLC & 2) >> 1;
break;
case 0x05: /* --- STS - Store Status --- */
newval = ReadW(eaddr) & 0xFF00;
if (C)
newval |= 2;
if (V)
newval |= 1;
WriteW(eaddr, newval);
C = V = 0; /* clear flags after storing */
break;
case 0x06: /* --- WAIT --- */
wait_state = WAIT_OP;
/* note: not valid in long mode, but what happens if we try? */
if (F) {
weirdop("Long WAIT", -2);
DECREMENT_IAR; /* assume it wouldn't have fetched 2nd word */
}
break;
case 0x08: /* --- BSI - Branch and store IAR --- */
if (F) {
if (bsctest(IR, F)) /* do standard BSC long format testing */
break; /* if any condition is true, do nothing */
}
WriteW(eaddr, IAR); /* do subroutine call */
archive_backtrace("BSI"); /* save info in back-trace buffer */
IAR = (eaddr + 1) & mem_mask;
break;
case 0x09: /* --- BSC - Branch and skip on Condition --- */
if (F) {
if (bsctest(IR, F)) /* long format; any indicator cancels branch */
break;
archive_backtrace((DSPLC & 0x40) ? "BOSC" : "BSC"); /* save info in back-trace buffer */
IAR = eaddr; /* no indicator means branch taken */
}
else { /* short format: skip if any indicator hits */
if (bsctest(IR, F)) {
archive_backtrace((DSPLC & 0x40) ? "BOSC" : "BSC"); /* save info in back-trace buffer */
INCREMENT_IAR;
}
}
// 27Mar02: moved this test out of the (F) condition; BOSC works even in the
// short form. The displacement field in this instruction is always the set of
// condition bits, and the interrupt clear bit doesn't collide.
if (DSPLC & 0x40) { /* BOSC = exit from interrupt handler */
exit_irq();
cwincount = 0;
}
break;
case 0x0c: /* --- LDX - Load Index --- */
if (F)
eaddr = (INDIR) ? ReadW(word2) : word2;
else
eaddr = DSPLC;
if (TAG)
WriteW(TAG, eaddr);
else {
archive_backtrace("LDX"); /* save info in back-trace buffer */
IAR = eaddr; /* what happens in short form? can onlyjump to low addresses? */
}
break;
case 0x0d: /* --- STX - Store Index --- */
if (F) { /* compute EA without any indexing */
eaddr = (INDIR) ? ReadW(word2) : word2;
}
else {
eaddr = IAR + DSPLC;
}
WriteW(eaddr, TAG ? ReadW(TAG) : IAR);
break;
case 0x0e: /* --- MDX - Modify Index and Skip --- */
if (F) { /* long mode: adjust memory location */
if (TAG) {
oldval = ReadW(TAG); /* add word2 to index */
newval = oldval + (INDIR ? ReadW(word2) : word2);
WriteW(TAG, newval);
}
else {
oldval = ReadW(word2);
DSPLC = IR & 0x00FF; /* use extended displacement (includes INDIR bit) */
if (DSPLC & 0x0080)
DSPLC |= ~ 0xFF;
newval = oldval + DSPLC; /* add modifier to @word2 */
WriteW(word2, newval);
}
}
else { /* short mode: adust IAR or index */
if (TAG) {
oldval = ReadW(TAG);/* add displacement to index */
newval = oldval + DSPLC;
WriteW(TAG, newval);
}
else {
oldval = IAR; /* add displacement to IAR */
newval = IAR + DSPLC;
archive_backtrace("MDX");
IAR = newval & mem_mask;
}
}
if ((F || TAG) && (((newval & 0xFFFF) == 0) || ((oldval & 0x8000) != (newval & 0x8000)))) {
archive_backtrace("SKP");
INCREMENT_IAR; /* skip if index sign change or zero */
}
break;
case 0x10: /* --- A - Add --- */
/* in adds and subtracts, carry is set or cleared, overflow is set only */
src = ReadW(eaddr);
src2 = ACC;
ACC = (ACC + src) & 0xFFFF;
C = ACC < src;
if (! V)
V = SIGN_BIT((~src ^ src2) & (src ^ ACC));
break;
case 0x11: /* --- AD - Add Double --- */
src = ((ACC << 16) + (EXT & 0xFFFF));
src2 = (ReadW(eaddr) << 16) + ReadW(eaddr|1);
dst = src + src2;
ACC = (dst >> 16) & 0xFFFF;
EXT = dst & 0xFFFF;
C = (unsigned int32) dst < (unsigned int32) src;
if (! V)
V = DWSIGN_BIT((~src ^ src2) & (src ^ dst));
break;
case 0x12: /* --- S - Subtract --- */
src = ACC;
src2 = ReadW(eaddr);
ACC = (ACC-src2) & 0xFFFF;
C = src2 < src;
if (! V)
V = SIGN_BIT((src ^ src2) & (src ^ ACC));
break;
case 0x13: /* --- SD - Subtract Double --- */
src = ((ACC << 16) + (EXT & 0xFFFF));
src2 = (ReadW(eaddr) << 16) + ReadW(eaddr|1);
dst = src - src2;
ACC = (dst >> 16) & 0xFFFF;
EXT = dst & 0xFFFF;
C = (unsigned int32) src2 < (unsigned int32) src;
if (! V)
V = DWSIGN_BIT((src ^ src2) & (src ^ dst));
break;
case 0x14: /* --- M - Multiply --- */
if ((src = ACC & 0xFFFF) & 0x8000) /* sign extend the values */
src |= 0xFFFF0000;
if ((src2 = ReadW(eaddr)) & 0x8000)
src2 |= 0xFFFF0000;
dst = src * src2;
ACC = (dst >> 16) & 0xFFFF; /* split the results */
EXT = dst & 0xFFFF;
break;
case 0x15: /* --- D - Divide --- */
src = ((ACC << 16) + EXT);
src2 = ReadW(eaddr);
if (src2 == 0)
V = 1; /* divide by zero just sets overflow, ACC & EXT are undefined */
else {
ACC = (src / src2) & 0xFFFF;
EXT = (src % src2) & 0xFFFF;
}
break;
case 0x18: /* --- LD - Load ACC --- */
ACC = ReadW(eaddr);
break;
case 0x19: /* --- LDD - Load Double --- */
ACC = ReadW(eaddr);
EXT = ReadW(eaddr|1); /* notice address is |1 not +1 */
break;
case 0x1a: /* --- STO - Store ACC --- */
WriteW(eaddr, ACC);
break;
case 0x1b: /* --- STD - Store Double --- */
WriteW(eaddr|1, EXT);
WriteW(eaddr, ACC); /* order is important: if odd addr, only ACC is stored */
break;
case 0x1c: /* --- AND - Logical AND --- */
ACC &= ReadW(eaddr);
break;
case 0x1d: /* --- OR - Logical OR --- */
ACC |= ReadW(eaddr);
break;
case 0x1e: /* --- EOR - Logical Excl OR --- */
ACC ^= ReadW(eaddr);
break;
default:
/* all invalid instructions act like waits */
/* case 0x00: */
/* case 0x07: */
/* case 0x0a: */
/* case 0x0b: */
/* case 0x0e: */
/* case 0x0f: */
/* case 0x16: */
/* case 0x17: */
/* case 0x1f: */
wait_state = WAIT_INVALID_OP;
if (F)
DECREMENT_IAR; /* assume it wouldn't have fetched 2nd word? */
break;
} /* end instruction decode switch */
if (RUNMODE != MODE_RUN && RUNMODE != MODE_INT_RUN)
reason = STOP_WAIT;
if (tbit && (ipl < 0)) { /* if INT_RUN mode, set IRQ5 after this instr */
GUI_BEGIN_CRITICAL_SECTION
SETBIT(cpu_dsw, CPU_DSW_INT_RUN);
SETBIT(ILSW[5], ILSW_5_INT_RUN);
int_req |= INT_REQ_5;
GUI_END_CRITICAL_SECTION
}
} /* end main loop */
#ifdef GUI_SUPPORT
gui_run(FALSE);
#endif
running = FALSE;
int_lamps = 0; /* display only currently active interrupts while halted */
if (reason == STOP_WAIT || reason == STOP_INVALID_INSTR) {
wait_state = 0; // on resume, don't wait
wait_lamp = TRUE; // but keep the lamp lit on the GUI
}
#ifdef CGI_SUPPORT
if (cgi)
cgi_stop(reason);
#endif
return reason;
}
/* ------------------------------------------------------------------------
* bsctest - perform standard set of condition tests. We return TRUE if any
* of the condition bits specified in DSPLC test positive, FALSE if none are true.
* If reset_V is TRUE, we reset the oVerflow flag after testing it.
* ------------------------------------------------------------------------ */
static t_bool bsctest (int32 DSPLC, t_bool reset_V)
{
if (DSPLC & 0x01) { /* Overflow off (note inverted sense) */
if (! V)
return TRUE;
else if (reset_V) /* reset after testing */
V = 0;
}
if (DSPLC & 0x02) { /* Carry off (note inverted sense) */
if (! C)
return TRUE;
}
if (DSPLC & 0x04) /* Even */
if ((ACC & 1) == 0)
return TRUE;
if (DSPLC & 0x08) /* Positive */
if ((ACC & 0x8000) == 0 && ACC != 0)
return TRUE;
if (DSPLC & 0x10) /* Negative */
if (ACC & 0x8000)
return TRUE;
if (DSPLC & 0x20) /* Zero */
if ((ACC & 0xFFFF) == 0)
return TRUE;
return FALSE;
}
/* ------------------------------------------------------------------------
* exit_irq - pop interrupt stack as part of return from subroutine (BOSC)
* ------------------------------------------------------------------------ */
static void exit_irq (void)
{
int i, bit;
GUI_BEGIN_CRITICAL_SECTION
ipl = -1; /* default: return to main processor level */
int_mask = 0xFFFF;
if (iplpending) { /* restore previous interrupt status */
for (i = 0, bit = 0x20; i < 6; i++, bit >>= 1) {
if (iplpending & bit) {
iplpending &= ~bit;
ipl = i;
int_mask = int_masks[i];
break;
}
}
}
GUI_END_CRITICAL_SECTION
calc_ints(); /* recompute pending interrupt mask */
} /* because we probably cleared some ILSW bits before this instruction */
/* let a device halt the simulation */
void break_simulation (t_stat stopreason)
{
reason = stopreason;
}
/* ------------------------------------------------------------------------
* SIMH required routines
* ------------------------------------------------------------------------ */
/* ------------------------------------------------------------------------
* Reset routine
* ------------------------------------------------------------------------ */
t_stat cpu_reset (DEVICE *dptr)
{
wait_state = 0; /* cancel wait */
wait_lamp = TRUE; /* but keep the wait lamp lit on the GUI */
if (cpu_unit.flags & UNIT_ATT) { /* record reset in CPU log */
fseek(cpu_unit.fileref, 0, SEEK_END);
fprintf(cpu_unit.fileref, "---RESET---" CRLF);
}
GUI_BEGIN_CRITICAL_SECTION
reset_backtrace();
ipl = -1;
int_mask = 0xFFFF;
int_req = 0; /* hmmm, it SHOULD reset the int req, right? */
int_lamps = 0;
iplpending = 0;
memset(ILSW, 0, sizeof(ILSW));
cpu_dsw = 0; /* clear int req and prot stop bits */
tbit = 0; /* cancel INT_RUN mode */
C = V = 0; /* clear processor flags */
IAR = SAR = SBR = 0; /* clear IAR and other registers */
ACC = EXT = OP = TAG = CCC = C = V = 0;
mem_mask = MEMSIZE - 1; /* wraparound mask */
GUI_END_CRITICAL_SECTION
return cpu_svc(&cpu_unit); /* reset breakpoint */
}
/* ------------------------------------------------------------------------
* Memory examine
* ------------------------------------------------------------------------ */
t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw)
{
if (vptr == NULL) return SCPE_ARG;
/* check this out -- save command hits it in weird way */
/* I wish I remembered what I meant when I wrote that */
if (addr < MEMSIZE) {
*vptr = M[addr] & 0xFFFF;
return SCPE_OK;
}
return SCPE_NXM;
}
/* ------------------------------------------------------------------------
* Memory deposit
* ------------------------------------------------------------------------ */
t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw)
{
if (addr < MEMSIZE) {
M[addr] = val & 0xFFFF;
return SCPE_OK;
}
return SCPE_NXM;
}
/* ------------------------------------------------------------------------
* Breakpoint service
* ------------------------------------------------------------------------ */
t_stat cpu_svc (UNIT *uptr)
{
if ((ibkpt_addr & ~ILL_ADR_FLAG) == save_ibkpt)
ibkpt_addr = save_ibkpt;
save_ibkpt = -1;
return SCPE_OK;
}
/* ------------------------------------------------------------------------
* Memory allocation
* ------------------------------------------------------------------------ */
t_stat cpu_set_size (UNIT *uptr, int32 value, char *cptr, void *desc)
{
t_bool used;
int32 i;
if ((value <= 0) || (value > MAXMEMSIZE) || ((value & 0xFFF) != 0))
return SCPE_ARG;
for (i = value, used = FALSE; i < (int32) MEMSIZE; i++) {
if (M[i] != 0) {
used = TRUE;
break;
}
}
if (used && ! get_yn ("Really truncate memory [N]?", FALSE))
return SCPE_OK;
for (i = MEMSIZE; i < value; i++) /* clear expanded area */
M[i] = 0;
MEMSIZE = value;
mem_mask = MEMSIZE - 1;
return SCPE_OK;
}
/* ------------------------------------------------------------------------
* IO function for console switches
* ------------------------------------------------------------------------ */
void xio_1131_switches (int32 addr, int32 func, int32 modify)
{
char msg[80];
switch (func) {
case XIO_READ:
WriteW(addr, CES);
break;
case XIO_SENSE_DEV:
ACC = cpu_dsw;
if (modify & 0x01) { /* reset interrupts */
CLRBIT(cpu_dsw, CPU_DSW_PROGRAM_STOP|CPU_DSW_INT_RUN);
CLRBIT(ILSW[5], ILSW_5_INT_RUN); /* (these bits are set in the keyboard handler in 1130_stddev.c) */
}
break;
default:
sprintf(msg, "Invalid console switch function %x", func);
xio_error(msg);
}
}
/* ------------------------------------------------------------------------
* Illegal IO operation. Not yet sure what the actual CPU does in this case
* ------------------------------------------------------------------------ */
void xio_error (char *msg)
{
printf("*** XIO error at %04x: %s\n", prev_IAR, msg);
if (cgi)
break_simulation(STOP_CRASH);
}
/* ------------------------------------------------------------------------
* register_cmd - add a command to the extensible command table
* ------------------------------------------------------------------------ */
t_stat register_cmd (char *name, t_stat (*action)(), int arg, char *help)
{
int i;
for (i = 0; i < MAX_EXTRA_COMMANDS; i++) { // find end of command table
if (x_cmds[i].action == action)
return SCPE_OK; // command is already there, just return
if (x_cmds[i].name == NULL)
break;
}
if (i >= (MAX_EXTRA_COMMANDS-1)) { // no more room (we need room for the NULL)
fprintf(stderr, "The command table is full - rebuild the simulator with more free slots\n");
return SCPE_ARG;
}
x_cmds[i].action = action; // add new command
x_cmds[i].name = name;
x_cmds[i].arg = arg;
x_cmds[i].help = help;
i++;
x_cmds[i].action = NULL; // move the NULL terminator
x_cmds[i].name = NULL;
return SCPE_OK;
}
/* ------------------------------------------------------------------------
* echo_cmd - just echo the command line
* ------------------------------------------------------------------------ */
static t_stat echo_cmd (int flag, char *cptr)
{
printf("%s\n", cptr);
return SCPE_OK;
}
/* ------------------------------------------------------------------------
* sim_init - initialize simulator upon startup of scp, before reset
* ------------------------------------------------------------------------ */
void sim_init (void)
{
sim_gui = ! (sim_switches & SWMASK('G')); /* -g means no GUI */
sim_vm_cmd = x_cmds; /* provide list of additional commands */
#ifdef GUI_SUPPORT
// set hook routines for GUI command processing
if (sim_gui) {
sim_vm_read = &read_cmdline;
sim_vm_post = &update_gui;
}
#endif
#ifdef ENABLE_BACKTRACE
// add the BACKTRACE command
register_cmd("BACKTRACE", &backtrace_cmd, 0, "ba{cktrace} {n} list last n branches/skips/interrupts\n");
#endif
register_cmd("VIEW", &view_cmd, 0, "v{iew} filename view a text file with notepad\n");
#ifdef CGI_SUPPORT
register_cmd("CGI", &cgi_cmd, 0, "cgi run simulator in CGI mode\n");
#endif
register_cmd("ECHO", &echo_cmd, 0, "echo args... echo arguments passed to command\n");
}
/* ------------------------------------------------------------------------
* archive_backtrace - record a jump, skip, branch or whatever
* ------------------------------------------------------------------------ */
#ifdef ENABLE_BACKTRACE
#define MAXARCHIVE 16
static struct tag_arch {
int iar;
char *inst;
} arch[MAXARCHIVE];
int narchived = 0, archind = 0;
static void archive_backtrace (char *inst)
{
static int prevind;
if (narchived < MAXARCHIVE)
narchived++;
if (narchived > 0 && arch[prevind].iar == prev_IAR)
return;
arch[archind].iar = prev_IAR;
arch[archind].inst = inst;
prevind = archind;
archind = (archind+1) % MAXARCHIVE;
}
static void reset_backtrace (void)
{
narchived = 0;
archind = 0;
}
void void_backtrace (int afrom, int ato)
{
int i;
afrom &= mem_mask;
ato &= mem_mask;
for (i = 0; i < narchived; i++)
if (arch[i].iar >= afrom && arch[i].iar <= ato)
arch[i].inst = "OVERWRITTEN";
}
static void show_backtrace (int nshow)
{
int n = narchived, i = archind;
if (n > nshow) n = nshow;
while (--n >= 0) {
i = (i > 0) ? (i-1) : (MAXARCHIVE-1);
printf("from %04x (%s) ", arch[i].iar, arch[i].inst);
}
if (narchived)
putchar('\n');
}
static t_stat backtrace_cmd (int flag, char *cptr)
{
int n;
if ((n = atoi(cptr)) <= 0)
n = 6;
show_backtrace(n);
return SCPE_OK;
}
#else
// stub this for the disk routine
void void_backtrace (int afrom, int ato)
{
}
#endif
// CPU log routines -- attaching a file to the CPU creates a trace of instructions and register values
//
// Syntax is WEIRD:
//
// attach cpu logfile log instructions and registers to file "logfile"
// attach -f cpu cpu.log log instructions, registers and floating point acc
// attach -m cpu mapfile logfile read addresses from "mapfile", log instructions to "logfile"
// attach -f -m cpu mapfile logfile same and log floating point stuff too
//
// mapfile if specified is a list of symbols and addresses of the form:
// symbol hexval
//
// e.g.
// FSIN 082E
// FARC 09D4
// FMPY 09A4
// NORM 0976
// XMDS 095A
// START 021A
//
// These values are easily obtained from a load map created by
// XEQ L
//
// The log output is of the form
//
// IAR ACC EXT (flt) XR1 XR2 XR3 CVI FAC OPERATION
// --------------- ---- ---- -------- ---- ---- ---- --- ------------- -----------------------
// 002a 002a 1234 5381 0.14222 00b3 0236 3f7e CV 1.04720e+000 4c80 BSC I ,0028
// 081d PAUSE+000d 1234 5381 0.14222 00b3 0236 3f7e CV 1.04720e+000 7400 MDM L 00f0,0 (0)
// 0820 PAUSE+0010 1234 5381 0.14222 00b3 0236 3f7e CV 1.04720e+000 7201 MDX 2 0001
// 0821 PAUSE+0011 1234 5381 0.14222 00b3 0237 3f7e CV 1.04720e+000 6a03 STX 2 0003
// 0822 PAUSE+0012 1234 5381 0.14222 00b3 0237 3f7e CV 1.04720e+000 6600 LDX L2 0231
// 0824 PAUSE+0014 1234 5381 0.14222 00b3 0231 3f7e CV 1.04720e+000 4c00 BSC L ,0237
// 0237 START+001d 1234 5381 0.14222 00b3 0231 3f7e CV 1.04720e+000 4480 BSI I ,3fff
// 082f FSIN +0001 1234 5381 0.14222 00b3 0231 3f7e CV 1.04720e+000 4356 BSI 3 0056
// 3fd5 ILS01+35dd 1234 5381 0.14222 00b3 0231 3f7e CV 1.04720e+000 4c00 BSC L ,08de
//
// IAR - instruction address register value, optionally including symbol and offset
// ACC - accumulator
// EXT - extension
// flt - ACC+EXT interpreted as the mantissa of a floating pt number (value 0.5 -> 1)
// XR* - index registers
// CVI - carry, overflow and interrupt indicators
// FAC - floating point accumulator (exponent at 125+XR3, mantissa at 126+XR3 and 127+XR3)
// OP - opcode value and crude disassembly
//
// flt and FAC are displayed only when the -f flag is specified in the attach command
// The label and offset and displayed only when the -m flag is specified in the attach command
//
// The register values shown are the values BEFORE the instruction is executed.
//
t_stat fprint_sym (FILE *of, t_addr addr, t_value *val, UNIT *uptr, int32 sw);
typedef struct tag_symentry {
struct tag_symentry *next;
int addr;
char sym[6];
} SYMENTRY, *PSYMENTRY;
static PSYMENTRY syms = NULL;
static t_bool new_log, log_fac;
static t_stat cpu_attach (UNIT *uptr, char *cptr)
{
char mapfile[200], buf[200], sym[100];
int addr;
PSYMENTRY n, prv, s;
FILE *fd;
unlink(cptr); // delete old log file, if present
new_log = TRUE;
log_fac = sim_switches & SWMASK ('F'); // display the FAC and the ACC/EXT as fixed point.
for (s = syms; s != NULL; s = n) { // free any old map entries
n = s->next;
free(s);
}
syms = NULL;
if (sim_switches & SWMASK('M')) { // use a map file to display relative addresses
cptr = get_glyph(cptr, mapfile, 0);
if (! *mapfile) {
printf("/m must be followed by a filename\n");
return SCPE_ARG;
}
if ((fd = fopen(mapfile, "r")) == NULL) {
perror(mapfile);
return SCPE_OPENERR;
}
while (fgets(buf, sizeof(buf), fd) != NULL) { // read symbols & addresses, link in descending address order
if (sscanf(buf, "%s %x", sym, &addr) != 2)
continue;
if (*buf == ';')
continue;
for (prv = NULL, s = syms; s != NULL; prv = s, s = s->next) {
if (s->addr < addr)
break;
}
if ((n = malloc(sizeof(SYMENTRY))) == NULL) {
printf("out of memory reading map!\n");
break;
}
sym[5] = '\0';
strcpy(n->sym, sym);
upcase(n->sym);
n->addr = addr;
if (prv == NULL) {
n->next = syms;
syms = n;
}
else {
n->next = prv->next;
prv ->next = n;
}
}
fclose(fd);
}
return attach_unit(uptr, cptr);
}
static void trace_instruction (void)
{
t_value v[2];
float fac;
short exp;
int addr;
PSYMENTRY s;
long mant, sign;
char facstr[20], fltstr[20];
if ((cpu_unit.flags & UNIT_ATT) == 0)
return;
if (new_log) {
fseek(cpu_unit.fileref, 0, SEEK_END);
new_log = FALSE;
fprintf(cpu_unit.fileref, " IAR%s ACC EXT %s XR1 XR2 XR3 CVI %sOPERATION" CRLF,
syms ? " " : "", log_fac ? " (flt) " : "", log_fac ? " FAC " : "");
fprintf(cpu_unit.fileref, "----%s ---- ---- %s---- ---- ---- --- %s-----------------------" CRLF,
syms ? "-----------" : "", log_fac ? "-------- " : "", log_fac ? "------------- " : "");
}
if (! log_fac)
facstr[0] = fltstr[0] = '\0';
else {
mant = ((ACC & 0xFFFF) << 16) | (EXT & 0xFFFF);
if (mant == 0x80000000) {
sign = TRUE;
fac = 1.f;
}
else {
if ((sign = mant & 0x80000000))
mant = -mant;
fac = (float) mant * ((float) 1./ (float) (unsigned long) 0x80000000);
}
sprintf(fltstr, "%c%.5f ", sign ? '-' : ' ', fac);
if (BETWEEN(M[3], 0x300, MEMSIZE-128)) {
exp = (M[M[3]+125] & 0xFF) - 128;
mant = (M[M[3]+126] << 8) | ((M[M[3]+127] >> 8) & 0xFF);
if ((sign = (mant & 0x00800000)))
mant = (-mant) & 0x00FFFFFF;
fac = (float) mant * ((float) 1. / (float) 0x00800000);
if (exp > 30) {
fac *= (float) (1 << 30);
exp -= 30;
while (exp > 0)
fac *= 2;
}
else if (exp > 0)
fac *= (float) (1 << exp);
else if (exp < -30) {
fac /= (float) (1 << 30);
exp += 30;
while (exp < 0)
fac /= 2;
}
else if (exp < 0)
fac /= (float) (1 << -exp);
sprintf(facstr, "%c%.5e ", sign ? '-' : ' ', fac);
}
else
strcpy(facstr, " ");
}
addr = IAR & 0xFFFF;
fprintf(cpu_unit.fileref, "%04x ", addr);
if (syms) {
for (s = syms; s != NULL; s = s->next)
if (s->addr <= addr)
break;
if (s == NULL)
fprintf(cpu_unit.fileref, " %04x ", addr);
else
fprintf(cpu_unit.fileref, "%-5s+%04x ", s->sym, addr - s->addr);
}
fprintf(cpu_unit.fileref, "%04x %04x %s%04x %04x %04x %c%c%c %s",
ACC & 0xFFFF, EXT & 0xFFFF, fltstr, M[1] & 0xFFFF, M[2] & 0xFFFF, M[3] & 0xFFFF,
C ? 'C' : ' ', V ? 'V' : ' ', (ipl < 0) ? ' ' : (ipl+'0'), facstr);
v[0] = M[ IAR & mem_mask];
v[1] = M[(IAR+1) & mem_mask];
fprint_sym(cpu_unit.fileref, IAR & mem_mask, v, NULL, SWMASK('M')); /* disassemble instruction */
fputs(CRLF, cpu_unit.fileref);
}
void trace_io (char *fmt, ...)
{
va_list args;
if ((cpu_unit.flags & UNIT_ATT) == 0)
return;
va_start(args, fmt); // get pointer to argument list
vfprintf(cpu_unit.fileref, fmt, args); // write errors to terminal (stderr)
va_end(args);
fputs(CRLF, cpu_unit.fileref);
}
/* debugging */
void debug_print (char *fmt, ...)
{
va_list args;
va_start(args, fmt);
vprintf(fmt, args);
if (cpu_unit.flags & UNIT_ATT)
vfprintf(cpu_unit.fileref, fmt, args);
va_end(args);
if (strchr(fmt, '\n') == NULL) { // be sure to emit a newline
putchar('\n');
if (cpu_unit.flags & UNIT_ATT)
putc('\n', cpu_unit.fileref);
}
}
#ifdef WIN32
#include <windows.h>
#endif
// view_cmd - let user view and/or edit a file (e.g. a printer output file, script, or source deck)
static t_stat view_cmd (int flag, char *cptr)
{
#ifdef WIN32
char cmdline[256];
sprintf(cmdline, "notepad %s", cptr);
WinExec(cmdline, SW_SHOWNORMAL);
#endif
return SCPE_OK;
}
#ifdef CGI_SUPPORT
int cgi_maxsec = 0; // default run time limit
// cgi_cmd - enable cgi mode. Specify time limit on command line if desired
static t_stat cgi_cmd (int flag, char *cptr)
{
cgi = TRUE; // set CGI flag
while (*cptr && *cptr <= ' ')
cptr++;
if (*cptr)
cgi_maxsec = atoi(cptr); // set time limit, if specified
return SCPE_OK;
}
// cgi_timeout - called when timer runs out
static void cgi_timeout (int dummy)
{
break_simulation(STOP_TIMED_OUT); // stop the simulator
}
// cgi_clockfail - report failure to set alarm
static void cgi_clockfail (void)
{
printf("<B>Set CGI time limit failed!</B>");
}
// cgi_start_timer - OS dependent routine to set things up so that
// cgi_timeout() will be called after cgi_maxsec seconds.
#if defined(WIN32)
static DWORD WINAPI cgi_timer_thread (LPVOID arg)
{
Sleep(cgi_maxsec*1000); // timer thread -- wait, then call timeout routine
cgi_timeout(0);
return 0;
}
static void cgi_start_timer (void)
{
DWORD dwThreadID;
if (CreateThread(NULL, 0, cgi_timer_thread, NULL, 0, &dwThreadID) == NULL)
cgi_clockfail();
}
#else
#include <signal.h>
#if defined(SIGVTALRM) && defined(ITMER_VIRTUAL)
// setitimer counts actual runtime CPU seconds, so is insensitive to
// system load and is a better timer to use. Be sure to check, though,
// that it actually works on your OS. Note that time spent performing
// I/O does not count -- this counts user mode CPU time only, so
// the elapsed time it allows could be much larger, especially if
// the job is spewing output.
#include <sys/time.h>
static void cgi_start_timer (void)
{
struct itimerval rtime, otime;
rtime.it_value.tv_sec = cgi_maxsec;
rtime.it_value.tv_usec = 0;
rtime.it_interval.tv_sec = cgi_maxsec;
rtime.it_interval.tv_usec = 0;
if (signal(SIGVTALRM, cgi_timeout) == SIG_ERR) // set alarm handler
cgi_clockfail();
else if (setitimer(ITIMER_VIRTUAL, &rtime, &otime)) // start timer
cgi_clockfail();
}
#elif defined(SIGALRM)
#include <unistd.h>
// if it's all we have, standard POSIX alarm will do the trick too
static void cgi_start_timer (void)
{
if (signal(SIGALRM, cgi_timeout) == SIG_ERR) // set alarm handler
cgi_clockfail();
else if (alarm(cgi_maxsec)) // start timer
cgi_clockfail();
}
#else
// don't seem to have a clock available. Better say so.
static void cgi_start_timer (void)
{
printf("<B>CGI time limit is not supported by this build</B>\n");
}
#endif
#endif
static void cgi_start(void)
{
if (cgi_maxsec > 0) // if time limit was specified, set timer now
cgi_start_timer();
// printf("Content-type: text/html\n\n<HTML>\n<HEAD>\n <TITLE>IBM 1130 Simulation Results</TITLE>\n</HEAD>\n<BODY background=\"greenbar.gif\">\n<PRE>");
}
static void cgi_stop(t_stat reason)
{
typedef enum {O_END, O_FORTRAN, O_MONITOR} ORIGIN;
char *errmsg = "";
static struct tag_pretstop {
int acc;
ORIGIN orig;
char *msg;
} pretstop[] = {
0xF000, O_FORTRAN, "No *IOCS was specified but I/O was attempted",
0xF001, O_FORTRAN, "Local unit defined incorrectly, or no *IOCS for specified device",
0xF002, O_FORTRAN, "Requested record exceeds buffer size",
0xF003, O_FORTRAN, "Illegal character encountered in input record",
0xF004, O_FORTRAN, "Exponent too large or too small in input",
0xF005, O_FORTRAN, "More than one exponent encountered in input",
0xF006, O_FORTRAN, "More than one sign encountered in input",
0xF007, O_FORTRAN, "More than one decimal point encountered in input",
0xF008, O_FORTRAN, "Read of output-only device, or write to input-only device",
0xF009, O_FORTRAN, "Real variable transmitted with I format or integer transmitted with E or F",
0xF020, O_FORTRAN, "Illegal unit reference",
0xF021, O_FORTRAN, "Read list exceeds length of write list",
0xF022, O_FORTRAN, "Record does not exist in read list",
0xF023, O_FORTRAN, "Maximum length of $$$$$ area on disk has been exceeded",
0xF024, O_FORTRAN, "*IOCS (UDISK) was not specified",
0xF100, O_FORTRAN, "File not defined by DEFINE FILE statement",
0xF101, O_FORTRAN, "File record number too large, zero or negative",
0xF103, O_FORTRAN, "*IOCS(DISK) was not specified",
0xF105, O_FORTRAN, "Length of a list element exceeds record length in DEFINE FILE",
0xF107, O_FORTRAN, "Attempt to read or write an invalid sector address (may occur if a core image program is run with too little room in working storage)",
0xF10A, O_FORTRAN, "Define file table and/or core image header corrupted, probably by an out-of-bounds array subscript",
0x1000, O_MONITOR, "1442 card read/punch or 1442 punch: not ready or hopper empty. [emulator: attach a file to CR or CP and go]",
0x1001, O_MONITOR, "Illegal device, function or word count",
0x100F, O_MONITOR, "Occurs in a DUP operation after DUP error D112",
0x2000, O_MONITOR, "Keyboard/Console Printer not ready",
0x2001, O_MONITOR, "Illegal device, function or word count",
0x3000, O_MONITOR, "1134/1055 Paper Tape not ready",
0x3001, O_MONITOR, "Illegal device, function or word count, or invalid check digit",
0x4000, O_MONITOR, "2501 Card Reader not ready",
0x4001, O_MONITOR, "Illegal device, function or word count",
0x5000, O_MONITOR, "Disk not ready",
0x5001, O_MONITOR, "Illegal device, function or word count, or attempt to write in protected area",
0x5002, O_MONITOR, "Write select or power unsafe",
0x5003, O_MONITOR, "Read/write/seek failure after 16 attempts or disk overflow. Extension may display logical drive number in bits 0..3 and working storage address in bits 4..15. Program Start retries 16 more times.",
0x5004, O_MONITOR, "Same as above from routine DISK1 and DISKN, or, an uninitialized cartridge is online during a cold start.",
0x6000, O_MONITOR, "1132 Printer not ready or out of paper",
0x6001, O_MONITOR, "Illegal device, function or word count",
0x7000, O_MONITOR, "1627 Plotter not ready",
0x7001, O_MONITOR, "Illegal device, function or word count",
0x8001, O_MONITOR, "SCA error: Illegal function or word count",
0x8002, O_MONITOR, "SCA error: if STR mode, receive or transmit operation not completed;\n"
"if BSC mode, invalid start characters in the I/O area for a transmit operation",
0x8003, O_MONITOR, "SCA error: if STR mode, failed to synchronize before attempt to read or write, or, attempted to receive before receiving INQ sequence;\n"
"if BSC mode, invalid number of identification characters for an identification specification operation",
0x9000, O_MONITOR, "1403 printer no ready or out of paper",
0x9001, O_MONITOR, "Illegal device, function or word count",
0x9002, O_MONITOR, "Parity check, scan check or ring check",
0xA000, O_MONITOR, "1231 Optical Mark Reader not ready",
0xA001, O_MONITOR, "Illegal device, function or word count",
0xA002, O_MONITOR, "Feed check, last document was processed. Clear jam, do not refeed",
0xA003, O_MONITOR, "Feed check, last document not processed. Clear jam and refeed",
0, O_END, NULL
};
int i;
detach_cmd(0, "prt"); /* flush last print line */
if (reason == STOP_TIMED_OUT)
printf("\n<HR><B>Sorry, emulation run time exceeded %d second%s</B>\n", cgi_maxsec, (cgi_maxsec == 1) ? "" : "s");
else if (IAR != 0x2a || ACC != 0x1000) {
ACC &= 0xFFFF;
for (i = 0; pretstop[i].orig != O_END; i++) {
if (pretstop[i].acc == ACC) {
errmsg = pretstop[i].msg;
break;
}
}
printf("\n<HR><B>Abnormal exit: %s</B>\nIAR = %04x, ACC = %04x\n", errmsg, IAR, ACC);
}
// printf("</PRE>\n</BODY>\n</HTML>\n");
exit(0); /* save w/o writing disk image */
}
#endif // ifdef CGI_SUPPORT