1965 lines
95 KiB
C
1965 lines
95 KiB
C
/* hp2100_ipl.c: HP 12875A Processor Interconnect simulator
|
|
|
|
Copyright (c) 2002-2016, Robert M. Supnik
|
|
Copyright (c) 2017-2018, J. David Bryan
|
|
|
|
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 THE
|
|
AUTHORS 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 names of the authors shall not be
|
|
used in advertising or otherwise to promote the sale, use or other dealings
|
|
in this Software without prior written authorization from the authors.
|
|
|
|
IPLI, IPLO 12875A Processor Interconnect
|
|
|
|
11-Jul-18 JDB Revised I/O model
|
|
22-May-18 JDB Added process synchronization commands
|
|
01-May-18 JDB Removed ioCRS counter, as consecutive ioCRS calls are no longer made
|
|
26-Mar-18 JDB Converted from socket to shared memory connections
|
|
28-Feb-18 JDB Added the special IOP BBL
|
|
13-Aug-17 JDB Revised so that only IPLI boots
|
|
19-Jul-17 JDB Removed unused "ipl_stopioe" variable and register
|
|
11-Jul-17 JDB Renamed "ibl_copy" to "cpu_ibl"
|
|
15-Mar-17 JDB Trace flags are now global
|
|
Changed DEBUG_PRJ calls to tpprintfs
|
|
10-Mar-17 JDB Added IOBUS to the debug table
|
|
27-Feb-17 JDB ibl_copy no longer returns a status code
|
|
05-Aug-16 JDB Renamed the P register from "PC" to "PR"
|
|
13-May-16 JDB Modified for revised SCP API function parameter types
|
|
14-Sep-15 JDB Exposed "ipl_edtdelay" via a REG_HIDDEN to allow user tuning
|
|
Corrected typos in comments and strings
|
|
05-Jun-15 JDB Merged 3.x and 4.x versions using conditionals
|
|
11-Feb-15 MP Revised ipl_detach and ipl_dscln for new sim_close_sock API
|
|
30-Dec-14 JDB Added S-register parameters to ibl_copy
|
|
12-Dec-12 MP Revised ipl_attach for new socket API
|
|
25-Oct-12 JDB Removed DEV_NET to allow restoration of listening ports
|
|
09-May-12 JDB Separated assignments from conditional expressions
|
|
10-Feb-12 JDB Deprecated DEVNO in favor of SC
|
|
Added CARD_INDEX casts to dib.card_index
|
|
07-Apr-11 JDB A failed STC may now be retried
|
|
28-Mar-11 JDB Tidied up signal handling
|
|
27-Mar-11 JDB Consolidated reporting of consecutive CRS signals
|
|
29-Oct-10 JDB Revised for new multi-card paradigm
|
|
26-Oct-10 JDB Changed I/O signal handler for revised signal model
|
|
07-Sep-08 JDB Changed Telnet poll to connect immediately after reset or attach
|
|
15-Jul-08 JDB Revised EDT handler to refine completion delay conditions
|
|
09-Jul-08 JDB Revised ipl_boot to use ibl_copy
|
|
26-Jun-08 JDB Rewrote device I/O to model backplane signals
|
|
01-Mar-07 JDB IPLI EDT delays DMA completion interrupt for TSB
|
|
Added debug printouts
|
|
28-Dec-06 JDB Added ioCRS state to I/O decoders
|
|
16-Aug-05 RMS Fixed C++ declaration and cast problems
|
|
07-Oct-04 JDB Fixed enable/disable from either device
|
|
26-Apr-04 RMS Fixed SFS x,C and SFC x,C
|
|
Implemented DMA SRQ (follows FLG)
|
|
21-Dec-03 RMS Adjusted ipl_ptime for TSB (from Mike Gemeny)
|
|
09-May-03 RMS Added network device flag
|
|
31-Jan-03 RMS Links are full duplex (found by Mike Gemeny)
|
|
|
|
References:
|
|
- 12875A Processor Interconnect Kit Operating and Service Manual
|
|
(12875-90002, January 1974)
|
|
- 12566B[-001/2/3] Microcircuit Interface Kits Operating and Service Manual
|
|
(12566-90015, April 1976)
|
|
|
|
The 12875A Processor Interconnect Kit consists four 12566A Microcircuit
|
|
Interface cards. Two are used in each processor. One card in each system is
|
|
used to initiate transmissions to the other, and the second card is used to
|
|
receive transmissions from the other. Each pair of cards forms a
|
|
bidirectional link, as the sixteen data lines are cross-connected, so that
|
|
data sent and status returned are supported. In each processor, data is sent
|
|
on the lower priority card and received on the higher priority card. Two
|
|
sets of cards are used to support simultaneous transmission in both
|
|
directions.
|
|
|
|
|
|
Implementation notes:
|
|
|
|
1. The "IPL" ("InterProcessor Link") designation is used throughout this
|
|
file for historical reasons, although HP designates this device as the
|
|
Processor Interconnect Kit.
|
|
*/
|
|
|
|
|
|
|
|
#include <signal.h>
|
|
|
|
#include "hp2100_defs.h"
|
|
#include "hp2100_io.h"
|
|
|
|
#include "sim_timer.h"
|
|
|
|
#if (SIM_MAJOR >= 4)
|
|
#include "sim_fio.h"
|
|
#else
|
|
#include "sim_shmem.h"
|
|
#endif
|
|
|
|
|
|
|
|
/* Process synchronization definitions */
|
|
|
|
|
|
/* Windows process synchronization.
|
|
|
|
|
|
Implementation notes:
|
|
|
|
1. SIMH and the HP2100 simulator define the CONST and INTERFACE macros,
|
|
respectively. Including "windows.h" here redefines these two symbols, so
|
|
we save and then restore their simulator definitions when including the
|
|
Windows header file.
|
|
*/
|
|
|
|
#if defined (_WIN32)
|
|
|
|
#pragma push_macro("CONST")
|
|
#pragma push_macro("INTERFACE")
|
|
|
|
#undef CONST
|
|
#undef INTERFACE
|
|
|
|
#include <windows.h>
|
|
|
|
#pragma pop_macro("CONST")
|
|
#pragma pop_macro("INTERFACE")
|
|
|
|
|
|
typedef HANDLE EVENT; /* the event type */
|
|
|
|
#define NO_EVENT NULL /* the initial (undefined) event value */
|
|
|
|
|
|
/* UNIX process synchronization */
|
|
|
|
#elif defined (HAVE_SEMAPHORE)
|
|
|
|
#include <fcntl.h>
|
|
#include <semaphore.h>
|
|
#include <time.h>
|
|
|
|
typedef sem_t *EVENT; /* the event type */
|
|
|
|
#define NO_EVENT SEM_FAILED /* the initial (undefined) event value */
|
|
|
|
static t_bool event_fallback = FALSE; /* TRUE if semaphores are defined but not supported */
|
|
|
|
|
|
/* Process synchronization stub */
|
|
|
|
#else
|
|
|
|
typedef uint32 EVENT; /* the event type */
|
|
|
|
#define NO_EVENT 0 /* the initial (undefined) event value */
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* Program limits */
|
|
|
|
#define CARD_COUNT 2 /* count of cards supported */
|
|
|
|
|
|
/* ATTACH mode switches */
|
|
|
|
#define SP SWMASK ('S') /* SP switch */
|
|
#define IOP SWMASK ('I') /* IOP switch */
|
|
|
|
#define LISTEN SWMASK ('L') /* listen switch (deprecated) */
|
|
#define CONNECT SWMASK ('C') /* connect switch (deprecated) */
|
|
|
|
|
|
/* Per-unit state variables */
|
|
|
|
#define ID u3 /* session identifying number */
|
|
|
|
|
|
/* Unit flags */
|
|
|
|
#define UNIT_DIAG_SHIFT (UNIT_V_UF + 0) /* diagnostic mode */
|
|
|
|
#define UNIT_DIAG (1u << UNIT_DIAG_SHIFT)
|
|
|
|
|
|
/* Unit references */
|
|
|
|
typedef enum {
|
|
ipli, /* inbound card index */
|
|
iplo /* outbound card index */
|
|
} CARD_INDEX;
|
|
|
|
#define ipli_unit ipl_unit [ipli] /* inbound card unit */
|
|
#define iplo_unit ipl_unit [iplo] /* outbound card unit */
|
|
|
|
|
|
/* Device information block references */
|
|
|
|
#define ipli_dib ipl_dib [ipli] /* inbound card DIB */
|
|
#define iplo_dib ipl_dib [iplo] /* outbound card DIB */
|
|
|
|
|
|
/* IPL state */
|
|
|
|
typedef struct {
|
|
HP_WORD output_word; /* output word register */
|
|
HP_WORD input_word; /* input word register */
|
|
FLIP_FLOP command; /* command flip-flop */
|
|
FLIP_FLOP control; /* control flip-flop */
|
|
FLIP_FLOP flag; /* flag flip-flop */
|
|
FLIP_FLOP flag_buffer; /* flag buffer flip-flop */
|
|
} CARD_STATE;
|
|
|
|
static CARD_STATE ipl [CARD_COUNT]; /* per-card state */
|
|
|
|
|
|
/* IPL I/O device state.
|
|
|
|
The 12566B Microcircuit Interface provides a 16-bit Data Out bus and a 16-bit
|
|
Data In bus, as well as an outbound Device Command signal and an inbound
|
|
Device Flag signal to indicate data availability. The output and input
|
|
states are modelled by a pair of structures that also contain Boolean flags
|
|
to indicate cable connectivity.
|
|
|
|
The two interface cards provided each may be connected in one of four
|
|
possible ways:
|
|
|
|
1. No connection (the I/O cable is not connected).
|
|
|
|
2. Loopback connection (a loopback connector is in place).
|
|
|
|
3. Cross connection (an I/O cable connects one card to the other card in the
|
|
same machine).
|
|
|
|
4. Processor interconnection (an I/O cable connects a card in one machine to
|
|
a card in the other machine).
|
|
|
|
In simulation, these four connection states are modelled by setting input and
|
|
output pointers (accessors) to point at the appropriate state structures, as
|
|
follows:
|
|
|
|
1. The input and output accessors point at separate local input and output
|
|
state structures.
|
|
|
|
2. The input and output accessors point at a single local state structure.
|
|
|
|
3. The input and output accessors of one card point at the separate local
|
|
state structures of the other card.
|
|
|
|
4. The input and output accessors of one card point at the separate shared
|
|
state structures of the other card.
|
|
|
|
Connection is accomplished by having an output accessor and an input accessor
|
|
point at the same state structure. Graphically, the four possibilities are:
|
|
|
|
1. No connection:
|
|
|
|
+------------------+
|
|
card [n].output --> | Data Out |
|
|
+------------------+
|
|
| Device Command |
|
|
+------------------+
|
|
|
|
+------------------+
|
|
card [n].input --> | Data In |
|
|
+------------------+
|
|
| Device Flag |
|
|
+------------------+
|
|
|
|
|
|
2. Loopback connection:
|
|
|
|
+------------------+------------------+
|
|
card [n].output --> | Data Out | Data In | <-- card [n].input
|
|
+------------------+------------------+
|
|
| Device Command | Device Flag |
|
|
+------------------+------------------+
|
|
|
|
|
|
3. Cross connection:
|
|
|
|
+------------------+------------------+
|
|
card [0].output --> | Data Out | Data In | <-- card [1].input
|
|
+------------------+------------------+
|
|
| Device Command | Device Flag |
|
|
+------------------+------------------+
|
|
|
|
+------------------+------------------+
|
|
card [0].input --> | Data In | Data Out | <-- card [1].output
|
|
+------------------+------------------+
|
|
| Device Flag | Device Command |
|
|
+------------------+------------------+
|
|
|
|
|
|
4. Processor interconnection:
|
|
|
|
+------------------+------------------+
|
|
card [0].output --> | Data Out | Data In | <-- card [1].input
|
|
+------------------+------------------+
|
|
| Device Command | Device Flag |
|
|
+------------------+------------------+
|
|
|
|
+------------------+------------------+
|
|
card [0].input --> | Data In | Data Out | <-- card [1].output
|
|
+------------------+------------------+
|
|
| Device Flag | Device Command |
|
|
+------------------+------------------+
|
|
|
|
+------------------+------------------+
|
|
card [1].output --> | Data Out | Data In | <-- card [0].input
|
|
+------------------+------------------+
|
|
| Device Command | Device Flag |
|
|
+------------------+------------------+
|
|
|
|
+------------------+------------------+
|
|
card [1].input --> | Data In | Data Out | <-- card [0].output
|
|
+------------------+------------------+
|
|
| Device Flag | Device Command |
|
|
+------------------+------------------+
|
|
|
|
In all but case 1, two accessors point at the same structure but with
|
|
different views.
|
|
*/
|
|
|
|
typedef struct {
|
|
t_bool cable_connected; /* TRUE if the inbound cable is connected */
|
|
t_bool device_flag_in; /* external DEVICE FLAG signal state */
|
|
HP_WORD data_in; /* external DATA IN signal bus */
|
|
} INPUT_STATE, *INPUT_STATE_PTR;
|
|
|
|
typedef struct {
|
|
t_bool cable_connected; /* TRUE if the outbound cable is connected */
|
|
t_bool device_command_out; /* external DEVICE COMMAND signal state */
|
|
HP_WORD data_out; /* external DATA OUT signal bus */
|
|
} OUTPUT_STATE, *OUTPUT_STATE_PTR;
|
|
|
|
typedef struct { /* the normal ("forward direction") state view */
|
|
INPUT_STATE input;
|
|
OUTPUT_STATE output;
|
|
} FORWARD_STATE;
|
|
|
|
typedef struct { /* the cross-connected ("reverse direction") state view */
|
|
OUTPUT_STATE output;
|
|
INPUT_STATE input;
|
|
} REVERSE_STATE;
|
|
|
|
typedef union { /* the state may be accessed in either direction */
|
|
FORWARD_STATE forward;
|
|
REVERSE_STATE reverse;
|
|
} IO_STATE, *IO_STATE_PTR;
|
|
|
|
typedef IO_STATE IO_ARRAY [CARD_COUNT]; /* an array of I/O states for the two cards */
|
|
|
|
static IO_ARRAY dev_bus; /* the local device I/O bus states */
|
|
|
|
typedef struct {
|
|
INPUT_STATE_PTR input; /* the input accessor */
|
|
OUTPUT_STATE_PTR output; /* the output accessor */
|
|
} STATE_PTRS;
|
|
|
|
static STATE_PTRS io_ptrs [CARD_COUNT] = { /* the card accessors pointing at the local state */
|
|
{ &dev_bus [ipli].forward.input, /* card [0].input */
|
|
&dev_bus [ipli].forward.output }, /* card [0].output */
|
|
|
|
{ &dev_bus [iplo].forward.input, /* card [1].input */
|
|
&dev_bus [iplo].forward.output } /* card [1].output */
|
|
};
|
|
|
|
|
|
/* IPL interface state */
|
|
|
|
static t_bool cpu_is_iop = FALSE; /* TRUE if this is the IOP instance, FALSE if SP instance */
|
|
static int32 edt_delay = 1; /* EDT delay (msec) */
|
|
static int32 poll_wait = 50; /* maximum poll wait time */
|
|
|
|
static char event_name [PATH_MAX]; /* the event name */
|
|
static uint32 event_error = 0; /* the host OS error code from a failed process sync call */
|
|
static t_bool wait_aborted = FALSE; /* TRUE if the user aborted a SET IPL WAIT command */
|
|
static EVENT event_id = NO_EVENT; /* the synchronization event */
|
|
static SHMEM *memory_region = NULL; /* a pointer to the shared memory region descriptor */
|
|
|
|
|
|
/* IPL local SCP support routines */
|
|
|
|
static INTERFACE ipl_interface;
|
|
|
|
|
|
/* IPL local SCP support routines */
|
|
|
|
static t_stat ipl_set_diag (UNIT *uptr, int32 value, CONST char *cptr, void *desc);
|
|
static t_stat ipl_set_sync (UNIT *uptr, int32 value, CONST char *cptr, void *desc);
|
|
|
|
static t_stat ipl_reset (DEVICE *dptr);
|
|
static t_stat ipl_boot (int32 unitno, DEVICE *dptr);
|
|
|
|
static t_stat ipl_attach (UNIT *uptr, CONST char *cptr);
|
|
static t_stat ipl_detach (UNIT *uptr);
|
|
|
|
|
|
/* IPL local utility routines */
|
|
|
|
static t_stat card_service (UNIT *uptr);
|
|
static void activate_unit (UNIT *uptr);
|
|
static void abort_handler (int signal);
|
|
|
|
|
|
/* Process synchronization routines */
|
|
|
|
static uint32 create_event (const char *name, EVENT *event);
|
|
static uint32 destroy_event (const char *name, EVENT *event);
|
|
static t_bool event_is_undefined (EVENT event);
|
|
static uint32 wait_event (EVENT event, uint32 wait_in_ms, t_bool *signaled);
|
|
static uint32 signal_event (EVENT event);
|
|
|
|
|
|
/* IPL SCP data structures */
|
|
|
|
/* Device information blocks */
|
|
|
|
static DIB ipl_dib [CARD_COUNT] = {
|
|
{ &ipl_interface, /* the device's I/O interface function pointer */
|
|
IPLI, /* the device's select code (02-77) */
|
|
0, /* the card index */
|
|
"12875A Processor Interconnect Lower Data PCA", /* the card description */
|
|
"12992K Processor Interconnect Loader" }, /* the ROM description */
|
|
|
|
{ &ipl_interface, /* the device's I/O interface function pointer */
|
|
IPLO, /* the device's select code (02-77) */
|
|
1, /* the card index */
|
|
"12875A Processor Interconnect Upper Data PCA", /* the card description */
|
|
NULL } /* the ROM description */
|
|
};
|
|
|
|
|
|
/* Unit lists */
|
|
|
|
static UNIT ipl_unit [CARD_COUNT] = {
|
|
{ UDATA (&card_service, UNIT_ATTABLE, 0) },
|
|
{ UDATA (&card_service, UNIT_ATTABLE, 0) }
|
|
};
|
|
|
|
|
|
/* Register lists */
|
|
|
|
static REG ipli_reg [] = {
|
|
/* Macro Name Location Width Offset Flags */
|
|
/* ------ -------- ---------------------- ----- ------ -------------------- */
|
|
{ ORDATA (IBUF, ipl [ipli].input_word, 16) },
|
|
{ ORDATA (OBUF, ipl [ipli].output_word, 16) },
|
|
{ FLDATA (CTL, ipl [ipli].control, 0) },
|
|
{ FLDATA (FLG, ipl [ipli].flag, 0) },
|
|
{ FLDATA (FBF, ipl [ipli].flag_buffer, 0) },
|
|
{ DRDATA (TIME, poll_wait, 24), PV_LEFT },
|
|
{ DRDATA (EDTDELAY, edt_delay, 32), PV_LEFT | REG_HIDDEN },
|
|
{ DRDATA (EVTERR, event_error, 32), PV_LEFT | REG_HRO },
|
|
|
|
DIB_REGS (ipli_dib),
|
|
|
|
{ NULL }
|
|
};
|
|
|
|
static REG iplo_reg [] = {
|
|
/* Macro Name Location Width Offset Flags */
|
|
/* ------ -------- ---------------------- ----- ------ -------------------- */
|
|
{ ORDATA (IBUF, ipl [iplo].input_word, 16) },
|
|
{ ORDATA (OBUF, ipl [iplo].output_word, 16) },
|
|
{ FLDATA (CTL, ipl [iplo].control, 0) },
|
|
{ FLDATA (FLG, ipl [iplo].flag, 0) },
|
|
{ FLDATA (FBF, ipl [iplo].flag_buffer, 0) },
|
|
{ DRDATA (TIME, poll_wait, 24), PV_LEFT },
|
|
|
|
DIB_REGS (iplo_dib),
|
|
|
|
{ NULL }
|
|
};
|
|
|
|
|
|
/* Modifier lists */
|
|
|
|
static MTAB ipl_mod [] = {
|
|
/* Mask Value Match Value Print String Match String Validation Display Descriptor */
|
|
/* ---------- ----------- ----------------- ------------ -------------- ------- ---------- */
|
|
{ UNIT_DIAG, UNIT_DIAG, "diagnostic mode", "DIAGNOSTIC", &ipl_set_diag, NULL, NULL },
|
|
{ UNIT_DIAG, 0, "link mode", "LINK", &ipl_set_diag, NULL, NULL },
|
|
|
|
/* Entry Flags Value Print String Match String Validation Display Descriptor */
|
|
/* -------------------- ----- ------------ ------------ -------------- ------------- ----------------- */
|
|
{ MTAB_XDV, 0u, NULL, "WAIT", &ipl_set_sync, NULL, NULL },
|
|
{ MTAB_XDV, 1u, NULL, "SIGNAL", &ipl_set_sync, NULL, NULL },
|
|
|
|
{ MTAB_XDV, 2u, "SC", "SC", &hp_set_dib, &hp_show_dib, (void *) &ipl_dib },
|
|
{ MTAB_XDV | MTAB_NMO, ~2u, "DEVNO", "DEVNO", &hp_set_dib, &hp_show_dib, (void *) &ipl_dib },
|
|
{ 0 }
|
|
};
|
|
|
|
|
|
/* Debugging trace lists */
|
|
|
|
static DEBTAB ipl_deb [] = {
|
|
{ "CMD", TRACE_CMD }, /* trace interface or controller commands */
|
|
{ "CSRW", TRACE_CSRW }, /* trace interface control, status, read, and write actions */
|
|
{ "PSERV", TRACE_PSERV }, /* trace periodic unit service scheduling calls and entries */
|
|
{ "XFER", TRACE_XFER }, /* trace data transmissions */
|
|
{ "IOBUS", TRACE_IOBUS }, /* trace I/O bus signals and data words received and returned */
|
|
{ NULL, 0 }
|
|
};
|
|
|
|
|
|
/* Device descriptors */
|
|
|
|
DEVICE ipli_dev = {
|
|
"IPL", /* device name (logical name "IPLI") */
|
|
&ipli_unit, /* unit array */
|
|
ipli_reg, /* register array */
|
|
ipl_mod, /* modifier array */
|
|
1, /* number of units */
|
|
10, /* address radix */
|
|
31, /* address width */
|
|
1, /* address increment */
|
|
16, /* data radix */
|
|
16, /* data width */
|
|
NULL, /* examine routine */
|
|
NULL, /* deposit routine */
|
|
&ipl_reset, /* reset routine */
|
|
&ipl_boot, /* boot routine */
|
|
&ipl_attach, /* attach routine */
|
|
&ipl_detach, /* detach routine */
|
|
&ipli_dib, /* device information block pointer */
|
|
DEV_DISABLE | DEV_DIS | DEV_DEBUG, /* device flags */
|
|
0, /* debug control flags */
|
|
ipl_deb, /* debug flag name table */
|
|
NULL, /* memory size change routine */
|
|
NULL /* logical device name */
|
|
};
|
|
|
|
DEVICE iplo_dev = {
|
|
"IPLO", /* device name */
|
|
&iplo_unit, /* unit array */
|
|
iplo_reg, /* register array */
|
|
ipl_mod, /* modifier array */
|
|
1, /* number of units */
|
|
10, /* address radix */
|
|
31, /* address width */
|
|
1, /* address increment */
|
|
16, /* data radix */
|
|
16, /* data width */
|
|
NULL, /* examine routine */
|
|
NULL, /* deposit routine */
|
|
&ipl_reset, /* reset routine */
|
|
NULL, /* boot routine */
|
|
&ipl_attach, /* attach routine */
|
|
&ipl_detach, /* detach routine */
|
|
&iplo_dib, /* device information block pointer */
|
|
DEV_DISABLE | DEV_DIS | DEV_DEBUG, /* device flags */
|
|
0, /* debug control flags */
|
|
ipl_deb, /* debug flag name table */
|
|
NULL, /* memory size change routine */
|
|
NULL /* logical device name */
|
|
};
|
|
|
|
static DEVICE *dptrs [CARD_COUNT] = {
|
|
&ipli_dev,
|
|
&iplo_dev
|
|
};
|
|
|
|
|
|
|
|
/* Microcircuit interface.
|
|
|
|
In the link mode, the IPLI and IPLO devices are linked via a shared memory
|
|
region to the corresponding cards in another CPU instance. If only one or
|
|
the other device is in the diagnostic mode, we simulate the attachment of a
|
|
loopback connector to that device. If both devices are in the diagnostic
|
|
mode, we simulate the attachment of the interprocessor cable between IPLI and
|
|
IPLO in this machine.
|
|
|
|
|
|
Implementation notes:
|
|
|
|
1. 2000 Access has a race condition that manifests itself by an apparently
|
|
normal boot and operational system console but no PLEASE LOG IN response
|
|
to terminals connected to the multiplexer. The frequency of occurrence
|
|
is higher on multiprocessor host systems, where the SP and IOP instances
|
|
may execute concurrently.
|
|
|
|
The cause is this code in the SP disc loader source (2883.asm, 7900.asm,
|
|
790X.asm, 79X3.asm, and 79XX.asm):
|
|
|
|
LDA SDVTR REQUEST
|
|
JSB IOPMA,I DEVICE TABLE
|
|
[...]
|
|
STC DMAHS,C TURN ON DMA
|
|
SFS DMAHS WAIT FOR
|
|
JMP *-1 DEVICE TABLE
|
|
STC CH2,C SET CORRECT
|
|
CLC CH2 FLAG DIRECTION
|
|
|
|
The STC/CLC normally would cause a second "request device table" command
|
|
to be recognized by the IOP, except that the IOP DMA setup routine
|
|
"DMAXF" (in D61.asm) has specified an end-of-block CLC that holds off the
|
|
IPL interrupt, and the completion interrupt routine "DMCMP" ends with a
|
|
STC,C that clears the IPL flag.
|
|
|
|
In hardware, the two CPUs are essentially interlocked by the DMA
|
|
transfer, and DMA completion interrupts occur almost simultaneously.
|
|
Therefore, the STC/CLC in the SP is guaranteed to occur before the STC,C
|
|
in the IOP. Under simulation, and especially on multiprocessor hosts,
|
|
that guarantee does not hold. If the STC/CLC occurs after the STC,C,
|
|
then the IOP starts a second device table DMA transfer, which the SP is
|
|
not expecting. The IOP never processes the subsequent "start
|
|
timesharing" command, and the multiplexer is non-responsive.
|
|
|
|
We employ a workaround that decreases the incidence of the problem: DMA
|
|
output completion interrupts are delayed to allow the other SIMH instance
|
|
a chance to process its own DMA completion. We do this by processing the
|
|
EDT (End Data Transfer) I/O backplane signal and "sleep"ing for a short
|
|
time if the transfer was an output transfer to the input channel, i.e.,
|
|
a data response to the SP. This improves the race condition by delaying
|
|
the IOP until the SP has a chance to receive the last word, recognize its
|
|
own DMA input completion, drop out of the SFS loop, and execute the
|
|
STC/CLC. The delay, "edt_delay", is initialized to one millisecond but
|
|
is exposed via a hidden IPLI register, "EDTDELAY", that allows the user
|
|
to lengthen the delay if necessary.
|
|
|
|
The condition is only improved, and not solved, because "sleep"ing the
|
|
IOP doesn't guarantee that the SP will actually execute. It's possible
|
|
that a higher-priority host process will preempt the SP, and that at the
|
|
sleep expiration, the SP still has not executed the STC/CLC. Still, in
|
|
testing, the incidence dropped dramatically, so the problem is much less
|
|
intrusive.
|
|
*/
|
|
|
|
static SIGNALS_VALUE ipl_interface (const DIB *dibptr, INBOUND_SET inbound_signals, HP_WORD inbound_value)
|
|
{
|
|
const char * const iotype [] = { "Status", "Command" };
|
|
const CARD_INDEX card = (CARD_INDEX) dibptr->card_index; /* set card selector */
|
|
UNIT * const uptr = &(ipl_unit [card]); /* associated unit pointer */
|
|
|
|
INBOUND_SIGNAL signal;
|
|
INBOUND_SET working_set = inbound_signals;
|
|
SIGNALS_VALUE outbound = { ioNONE, 0 };
|
|
t_bool irq_enabled = FALSE;
|
|
|
|
while (working_set) { /* while signals remain */
|
|
signal = IONEXTSIG (working_set); /* isolate the next signal */
|
|
|
|
switch (signal) { /* dispatch the I/O signal */
|
|
|
|
case ioCLF: /* Clear Flag flip-flop */
|
|
ipl [card].flag_buffer = CLEAR; /* reset the flag buffer */
|
|
ipl [card].flag = CLEAR; /* and flag flip-flops */
|
|
break;
|
|
|
|
|
|
case ioSTF: /* Set Flag flip-flop */
|
|
ipl [card].flag_buffer = SET; /* set the flag buffer flip-flop */
|
|
break;
|
|
|
|
|
|
case ioENF: /* Enable Flag */
|
|
if (ipl [card].flag_buffer == SET) /* if the flag buffer flip-flop is set */
|
|
ipl [card].flag = SET; /* then set the flag flip-flop */
|
|
break;
|
|
|
|
|
|
case ioSFC: /* Skip if Flag is Clear */
|
|
if (ipl [card].flag == CLEAR) /* if the flag flip-flop is clear */
|
|
outbound.signals |= ioSKF; /* then assert the Skip on Flag signal */
|
|
break;
|
|
|
|
|
|
case ioSFS: /* Skip if Flag is Set */
|
|
if (ipl [card].flag == SET) /* if the flag flip-flop is set */
|
|
outbound.signals |= ioSKF; /* then assert the Skip on Flag signal */
|
|
break;
|
|
|
|
|
|
case ioIOI: /* I/O data input */
|
|
outbound.value = ipl [card].input_word; /* get return data */
|
|
|
|
tpprintf (dptrs [card], TRACE_CSRW, "%s input word is %06o\n",
|
|
iotype [card ^ 1], ipl [card].input_word);
|
|
break;
|
|
|
|
|
|
case ioIOO: /* I/O data output */
|
|
ipl [card].output_word = inbound_value; /* clear supplied status */
|
|
|
|
io_ptrs [card].output->data_out = ipl [card].output_word;
|
|
|
|
tpprintf (dptrs [card], TRACE_CSRW, "%s output word is %06o\n",
|
|
iotype [card], ipl [card].output_word);
|
|
break;
|
|
|
|
|
|
case ioPOPIO: /* Power-On Preset to I/O */
|
|
ipl [card].flag_buffer = SET; /* set the flag buffer flip-flop */
|
|
ipl [card].output_word = 0; /* and clear the output register */
|
|
|
|
io_ptrs [card].output->data_out = 0;
|
|
break;
|
|
|
|
|
|
case ioCRS: /* Control Reset */
|
|
ipl [card].control = CLEAR; /* clear the control flip-flop */
|
|
break;
|
|
|
|
|
|
case ioCLC: /* Clear Control flip-flop */
|
|
ipl [card].control = CLEAR; /* clear the control flip-flop */
|
|
break;
|
|
|
|
|
|
case ioSTC: /* Set Control flip-flop */
|
|
ipl [card].control = SET; /* set the control flip-flop */
|
|
|
|
io_ptrs [card].output->device_command_out = TRUE; /* assert Device Command */
|
|
|
|
if (uptr->flags & UNIT_DIAG) /* if this card is in the diagnostic mode */
|
|
if (ipl_unit [card ^ 1].flags & UNIT_DIAG) { /* then if both cards are in diagnostic mode */
|
|
ipl_unit [card ^ 1].wait = 1; /* then schedule the other card */
|
|
activate_unit (&ipl_unit [card ^ 1]); /* for immediate reception */
|
|
}
|
|
|
|
else { /* otherwise simulate a loopback */
|
|
uptr->wait = 1; /* by scheduling this card */
|
|
activate_unit (uptr); /* for immediate reception */
|
|
}
|
|
|
|
tpprintf (dptrs [card], TRACE_XFER, "Word %06o sent to link\n",
|
|
ipl [card].output_word);
|
|
break;
|
|
|
|
|
|
case ioEDT: /* end data transfer */
|
|
if (cpu_is_iop /* if this is the IOP instance */
|
|
&& inbound_signals & ioIOO /* and the card is doing output */
|
|
&& card == ipli) { /* on the input card */
|
|
|
|
tprintf (ipli_dev, TRACE_CMD, "Delaying DMA completion interrupt for %d msec\n",
|
|
edt_delay);
|
|
|
|
sim_os_ms_sleep (edt_delay); /* then delay DMA completion */
|
|
}
|
|
break;
|
|
|
|
|
|
case ioSIR: /* Set Interrupt Request */
|
|
if (ipl [card].control & ipl [card].flag) /* if the control and flag flip-flops are set */
|
|
outbound.signals |= cnVALID; /* then deny PRL */
|
|
else /* otherwise */
|
|
outbound.signals |= cnPRL | cnVALID; /* conditionally assert PRL */
|
|
|
|
if (ipl [card].control & ipl [card].flag /* if the control and flag */
|
|
& ipl [card].flag_buffer) /* and flag buffer flip-flops are set */
|
|
outbound.signals |= cnIRQ | cnVALID; /* then conditionally assert IRQ */
|
|
|
|
if (ipl [card].flag == SET) /* if the flag flip-flop is set */
|
|
outbound.signals |= ioSRQ; /* then assert SRQ */
|
|
break;
|
|
|
|
|
|
case ioIAK: /* Interrupt Acknowledge */
|
|
ipl [card].flag_buffer = CLEAR; /* clear the flag buffer flip-flop */
|
|
break;
|
|
|
|
|
|
case ioIEN: /* Interrupt Enable */
|
|
irq_enabled = TRUE; /* permit IRQ to be asserted */
|
|
break;
|
|
|
|
|
|
case ioPRH: /* Priority High */
|
|
if (irq_enabled && outbound.signals & cnIRQ) /* if IRQ is enabled and conditionally asserted */
|
|
outbound.signals |= ioIRQ | ioFLG; /* then assert IRQ and FLG */
|
|
|
|
if (!irq_enabled || outbound.signals & cnPRL) /* if IRQ is disabled or PRL is conditionally asserted */
|
|
outbound.signals |= ioPRL; /* then assert it unconditionally */
|
|
break;
|
|
|
|
|
|
case ioPON: /* not used by this interface */
|
|
break;
|
|
}
|
|
|
|
IOCLEARSIG (working_set, signal); /* remove the current signal from the set */
|
|
} /* and continue until all signals are processed */
|
|
|
|
return outbound; /* return the outbound signals and value */
|
|
}
|
|
|
|
|
|
/* Unit service - poll for input */
|
|
|
|
static t_stat card_service (UNIT *uptr)
|
|
{
|
|
static uint32 delta [CARD_COUNT] = { 0, 0 }; /* per-card accumulated time between receptions */
|
|
const CARD_INDEX card = (CARD_INDEX) (uptr == &iplo_unit); /* set card selector */
|
|
t_stat status = SCPE_OK;
|
|
|
|
tpprintf (dptrs [card], TRACE_PSERV, "Poll delay %d service entered\n",
|
|
uptr->wait);
|
|
|
|
delta [card] = delta [card] + uptr->wait; /* update the accumulated time */
|
|
|
|
if (io_ptrs [card].input->device_flag_in == TRUE) { /* if the Device Flag is asserted */
|
|
io_ptrs [card].input->device_flag_in = FALSE; /* then clear it */
|
|
|
|
ipl [card].input_word = io_ptrs [card].input->data_in; /* read the data input lines */
|
|
|
|
tpprintf (dptrs [card], TRACE_XFER, "Word %06o delta %u received from link\n",
|
|
ipl [card].input_word, delta [card]);
|
|
|
|
ipl [card].flag_buffer = SET; /* set the flag buffer */
|
|
io_assert (dptrs [card], ioa_ENF); /* and flag flip-flops */
|
|
|
|
io_ptrs [card].output->device_command_out = FALSE; /* reset Device Command */
|
|
|
|
uptr->wait = 1; /* restart polling at the minimum time */
|
|
delta [card] = 0; /* and clear the accumulated time */
|
|
}
|
|
|
|
else { /* otherwise Device Flag is denied */
|
|
uptr->wait = uptr-> wait * 2; /* so double the wait time for the next check */
|
|
|
|
if (uptr->wait > poll_wait) /* if the new time is greater than the maximum time */
|
|
uptr->wait = poll_wait; /* then limit it to the maximum */
|
|
|
|
if (io_ptrs [card].input->cable_connected == FALSE /* if the interconnecting cable is not present */
|
|
&& cpu_io_stop (uptr)) /* and the I/O error stop is enabled */
|
|
status = STOP_NOCONN; /* then report the disconnection */
|
|
}
|
|
|
|
if (uptr->flags & UNIT_ATT) /* if the link is active */
|
|
activate_unit (uptr); /* then continue to poll for input */
|
|
|
|
return status; /* return the event service status */
|
|
}
|
|
|
|
|
|
/* Reset the IPL.
|
|
|
|
This routine is called for a RESET, RESET IPLI, or RESET IPLO command. It is
|
|
the simulation equivalent of the POPIO signal, which is asserted by the front
|
|
panel PRESET switch.
|
|
|
|
For a power-on reset, the logical name "IPLI" is assigned to the first
|
|
processor interconnect card, so that it may referenced either as that name or
|
|
as "IPL" for use when a SET command affects both interfaces.
|
|
*/
|
|
|
|
static t_stat ipl_reset (DEVICE *dptr)
|
|
{
|
|
UNIT *uptr = dptr->units;
|
|
DIB *dibptr = (DIB *) dptr->ctxt; /* DIB pointer */
|
|
CARD_INDEX card = (CARD_INDEX) dibptr->card_index; /* card number */
|
|
|
|
hp_enbdis_pair (dptr, dptrs [card ^ 1]); /* ensure that the pair state is consistent */
|
|
|
|
if (sim_switches & SWMASK ('P')) { /* initialization reset? */
|
|
ipl [card].input_word = 0;
|
|
ipl [card].output_word = 0;
|
|
|
|
if (ipli_dev.lname == NULL) /* logical name unassigned? */
|
|
ipli_dev.lname = strdup ("IPLI"); /* allocate and initialize the name */
|
|
}
|
|
|
|
io_assert (dptr, ioa_POPIO); /* PRESET the device */
|
|
|
|
if (uptr->flags & UNIT_ATT) { /* if the link is active */
|
|
uptr->wait = poll_wait; /* then continue to poll for input */
|
|
activate_unit (uptr); /* at the idle rate */
|
|
}
|
|
|
|
else /* otherwise the link is inactive */
|
|
sim_cancel (uptr); /* so stop input polling */
|
|
|
|
return SCPE_OK;
|
|
}
|
|
|
|
|
|
/* Attach one end of the interconnecting cables.
|
|
|
|
This routine connects the IPL device pair to a shared memory region. This
|
|
simulates connecting one end of the processor interconnect kit cables to the
|
|
card pair in this CPU. The command is:
|
|
|
|
ATTACH [ -S | -I ] IPL <code>
|
|
|
|
...where <code> is a user-selected decimal number between 1 and 65535 that
|
|
uniquely identifies the instance pair to interconnect. The -S or -I switch
|
|
indicates whether this instance is acting as the System Processor or the I/O
|
|
Processor. The command will be rejected if either device is in diagnostic
|
|
mode, or if the <code> is omitted, malformed, or out of range.
|
|
|
|
For backward compatibility with prior IPL implementations that used network
|
|
interconnections, the following commands are also accepted:
|
|
|
|
ATTACH [ -L ] [ IPLI | IPLO] <port-1>
|
|
ATTACH -C [ IPLI | IPLO] <port-2>
|
|
|
|
For these commands, -L or no switch indicates the SP instance, and -C
|
|
indicates the IOP instance. <port-1> and <port-2> used to indicate the
|
|
network port numbers to use, but now it serves only to supply the code
|
|
number from the lesser of the two values.
|
|
|
|
Local memory is allocated to hold the code number string, which serves as the
|
|
"attached file name" for the SCP SHOW command. If memory allocation fails,
|
|
the command is rejected.
|
|
|
|
This routine creates a shared memory region and an event (semaphore) that are
|
|
used to coordinate a data exchange with the other simulator instance. If -S
|
|
or -I is specified, then creation occurs after the ATTACH command is given
|
|
for either the IPLI or IPLO device, and both devices are marked as attached.
|
|
If -L or -C is specified, then both devices must be attached before creation
|
|
occurs using the lower port number.
|
|
|
|
Two object names that identify the shared memory region and synchronization
|
|
event are derived from the <code> (or lower <port>) number:
|
|
|
|
/HP 2100-MEM-<code>
|
|
/HP 2100-EVT-<code>
|
|
|
|
Each simulator instance must use the same <code> (or <port> pair) when
|
|
attaching for the interconnection to occur. This permits multiple instance
|
|
pairs to operate simultaneously and independently, if desired.
|
|
|
|
Once shared memory is allocated, pointers to the region for the SP and IOP
|
|
instances are set so that the output card pointer of one instance and the
|
|
input card pointer of the other instance reference the same memory structure.
|
|
This accomplishes the interconnection, as a write to one instance's card will
|
|
be seen by a read from the other instance's card.
|
|
|
|
If the shared memory allocation succeeds but the process synchronization
|
|
event creation fails, the routine returns "Command not completed" status to
|
|
indicate that interconnection without synchronization is permitted.
|
|
|
|
|
|
Implementation notes:
|
|
|
|
1. The implementation supports process synchronization only on the local
|
|
system.
|
|
|
|
2. The object names begin with slashes to conform to POSIX requirements to
|
|
guarantee that multiple instances to refer to the same shared memory
|
|
region. Omitting the slash results in implementation-defined behavior on
|
|
POSIX systems.
|
|
*/
|
|
|
|
static t_stat ipl_attach (UNIT *uptr, CONST char *cptr)
|
|
{
|
|
t_stat status;
|
|
int32 id_number;
|
|
char object_name [PATH_MAX];
|
|
CONST char *zptr;
|
|
char *tptr;
|
|
IO_STATE_PTR isp;
|
|
UNIT *optr;
|
|
|
|
if ((ipli_unit.flags | iplo_unit.flags) & UNIT_DIAG) /* if either unit is in diagnostic mode */
|
|
return SCPE_NOFNC; /* then the command is not allowed */
|
|
|
|
else if (uptr->flags & UNIT_ATT) /* otherwise if the unit is currently attached */
|
|
ipl_detach (uptr); /* then detach it first */
|
|
|
|
id_number = (int32) strtotv (cptr, &zptr, 10); /* parse the command for the ID number */
|
|
|
|
if (cptr == zptr || *zptr != '\0' || id_number == 0) /* if the parse failed or extra characters or out of range */
|
|
return SCPE_ARG; /* then reject the attach with an invalid argument error */
|
|
|
|
else { /* otherwise a single number was specified */
|
|
tptr = (char *) malloc (strlen (cptr) + 1); /* so allocate a string buffer to hold the ID */
|
|
|
|
if (tptr == NULL) /* if the allocation failed */
|
|
return SCPE_MEM; /* then reject the attach with an out-of-memory error */
|
|
|
|
else { /* otherwise */
|
|
strcpy (tptr, cptr); /* copy the ID number to the buffer */
|
|
uptr->filename = tptr; /* and assign it as the attached object name */
|
|
|
|
uptr->flags |= UNIT_ATT; /* set the unit attached flag */
|
|
uptr->ID = id_number; /* and save the ID number */
|
|
|
|
uptr->wait = poll_wait; /* set up the initial poll time */
|
|
activate_unit (uptr); /* and activate the unit to poll */
|
|
}
|
|
|
|
if ((sim_switches & (SP | IOP)) == 0) /* if this is not a single-device attach */
|
|
if (ipli_unit.ID == 0 || iplo_unit.ID == 0) /* then if both devices have not been attached yet */
|
|
return SCPE_OK; /* then we've done all we can do */
|
|
|
|
else if (ipli_unit.ID < iplo_unit.ID) /* otherwise */
|
|
id_number = ipli_unit.ID; /* determine */
|
|
else /* the lower */
|
|
id_number = iplo_unit.ID; /* ID number */
|
|
|
|
else { /* otherwise this is a single-device attach */
|
|
if (uptr == &ipli_unit) /* so if we are attaching the input unit */
|
|
optr = &iplo_unit; /* then point at the output unit */
|
|
else /* otherwise we are attaching the output unit */
|
|
optr = &ipli_unit; /* so point at the input unit */
|
|
|
|
optr->filename = tptr; /* assign the ID as the attached object name */
|
|
|
|
optr->flags |= UNIT_ATT; /* set the unit attached flag */
|
|
optr->ID = id_number; /* and save the ID number */
|
|
|
|
optr->wait = poll_wait; /* set up the initial poll time */
|
|
activate_unit (optr); /* and activate the unit to poll */
|
|
}
|
|
|
|
sprintf (object_name, "/%s-MEM-%d", /* generate the shared memory area name */
|
|
sim_name, id_number);
|
|
|
|
status = sim_shmem_open (object_name, sizeof dev_bus, /* allocate the shared memory area */
|
|
&memory_region, (void **) &isp);
|
|
|
|
if (status != SCPE_OK) { /* if the allocation failed */
|
|
ipl_detach (uptr); /* then detach this unit */
|
|
return status; /* and report the error */
|
|
}
|
|
|
|
else { /* otherwise */
|
|
cpu_is_iop = ((sim_switches & (CONNECT | IOP)) != 0); /* -C or -I imply that this is the I/O Processor */
|
|
|
|
if (cpu_is_iop) { /* if this is the IOP instance */
|
|
io_ptrs [ipli].input = &isp [iplo].reverse.input; /* then cross-connect */
|
|
io_ptrs [ipli].output = &isp [iplo].reverse.output; /* the input and output */
|
|
io_ptrs [iplo].input = &isp [ipli].reverse.input; /* interface cards to the */
|
|
io_ptrs [iplo].output = &isp [ipli].reverse.output; /* SP interface cards */
|
|
}
|
|
|
|
else { /* otherwise this is the SP instance */
|
|
io_ptrs [ipli].input = &isp [ipli].forward.input; /* so connect */
|
|
io_ptrs [ipli].output = &isp [ipli].forward.output; /* the interface cards */
|
|
io_ptrs [iplo].input = &isp [iplo].forward.input; /* to the I/O cables */
|
|
io_ptrs [iplo].output = &isp [iplo].forward.output; /* directly */
|
|
}
|
|
|
|
io_ptrs [ipli].output->cable_connected = TRUE; /* indicate that the cables */
|
|
io_ptrs [iplo].output->cable_connected = TRUE; /* have been connected */
|
|
}
|
|
|
|
sprintf (event_name, "/%s-EVT-%d", /* generate the process synchronization event name */
|
|
sim_name, id_number);
|
|
|
|
event_error = create_event (event_name, &event_id); /* create the event */
|
|
|
|
if (event_error == 0) /* if event creation succeeded */
|
|
return SCPE_OK; /* then report a successful attach */
|
|
else /* otherwise */
|
|
return SCPE_INCOMP; /* report that the command did not complete */
|
|
}
|
|
}
|
|
|
|
|
|
/* Detach the interconnecting cables.
|
|
|
|
This routine disconnects the IPL device pair from the shared memory region.
|
|
This simulates disconnecting the processor interconnect kit cables from the
|
|
card pair in this CPU. The command is:
|
|
|
|
DETACH IPL
|
|
|
|
For backward compatibility with prior IPL implementations that used network
|
|
interconnections, the following commands are also accepted:
|
|
|
|
DETACH IPLI
|
|
DETACH IPLO
|
|
|
|
In either case, the shared memory region and process synchronization event
|
|
are destroyed, and the card state pointers are reset to point at the local
|
|
memory structure. If a single ATTACH was done, a single DETACH will detach
|
|
both devices and free the allocated "file name" memory. The input poll is
|
|
also stopped.
|
|
|
|
If the event destruction failed, the routine returns "Command not completed"
|
|
status to indicate that the error code register should be checked.
|
|
*/
|
|
|
|
static t_stat ipl_detach (UNIT *uptr)
|
|
{
|
|
UNIT *optr;
|
|
|
|
if ((uptr->flags & UNIT_ATT) == 0) /* if the unit is not attached */
|
|
if (sim_switches & SIM_SW_REST) /* then if this is a restoration call */
|
|
return SCPE_OK; /* then return success */
|
|
else /* otherwise this is a manual request */
|
|
return SCPE_UNATT; /* so complain that the unit is not attached */
|
|
|
|
if (ipli_unit.filename == iplo_unit.filename) { /* if both units are attached to the same object */
|
|
if (uptr == &ipli_unit) /* then if we are detaching the input unit */
|
|
optr = &iplo_unit; /* then point at the output unit */
|
|
else /* otherwise we are detaching the output unit */
|
|
optr = &ipli_unit; /* so point at the input unit */
|
|
|
|
optr->filename = NULL; /* clear the other unit's attached object name */
|
|
|
|
optr->flags &= ~UNIT_ATT; /* clear the other unit's attached flag */
|
|
optr->ID = 0; /* and the ID number */
|
|
|
|
sim_cancel (optr); /* cancel the other unit's poll */
|
|
}
|
|
|
|
free (uptr->filename); /* free the memory holding the ID number */
|
|
uptr->filename = NULL; /* and clear the attached object name */
|
|
|
|
uptr->flags &= ~UNIT_ATT; /* clear the unit attached flag */
|
|
uptr->ID = 0; /* and the ID number */
|
|
|
|
sim_cancel (uptr); /* cancel the poll */
|
|
|
|
io_ptrs [ipli].output->cable_connected = FALSE; /* disconnect the cables */
|
|
io_ptrs [iplo].output->cable_connected = FALSE; /* from both cards */
|
|
|
|
io_ptrs [ipli].input = &dev_bus [ipli].forward.input; /* restore local control */
|
|
io_ptrs [ipli].output = &dev_bus [ipli].forward.output; /* over the I/O state */
|
|
io_ptrs [iplo].input = &dev_bus [iplo].forward.input; /* for both cards */
|
|
io_ptrs [iplo].output = &dev_bus [iplo].forward.output;
|
|
|
|
sim_shmem_close (memory_region); /* deallocate the shared memory region */
|
|
memory_region = NULL; /* and clear the region pointer */
|
|
|
|
event_error = destroy_event (event_name, &event_id); /* destroy the event */
|
|
|
|
if (event_error == 0) /* if the destruction succeeded */
|
|
return SCPE_OK; /* then report success */
|
|
else /* otherwise */
|
|
return SCPE_INCOMP; /* report that the command did not complete */
|
|
}
|
|
|
|
|
|
/* Set the diagnostic or link mode.
|
|
|
|
This validation routine is entered with the "value" parameter set to zero if
|
|
the unit is to be set into the link (normal) mode or non-zero if the unit is
|
|
to be set into the diagnostic mode. The character and descriptor pointers
|
|
are not used.
|
|
|
|
In addition to setting or clearing the UNIT_DIAG flag, the I/O state pointers
|
|
are set to point at the appropriate state structure. The selected pointer
|
|
configuration depends on whether none, one, or both the IPLI and IPLO devices
|
|
are in diagnostic mode.
|
|
|
|
If both devices are in diagnostic mode, the pointers are set to point at
|
|
their respective state structures but with the input and output pointers
|
|
reversed. This simulates connecting one of the interprocessor cables between
|
|
the two cards within the same CPU, permitting the Processor Interconnect
|
|
Cable Diagnostic to be run.
|
|
|
|
If only one of the devices is in diagnostic mode, the pointers are set to
|
|
point at the device's state structure with the input and output pointers
|
|
reversed. This simulates connected a loopback connector to the card,
|
|
permitting the General Register Diagnostic to be run.
|
|
|
|
If a device is in link mode, that device's pointers are set to point at the
|
|
corresponding parts of the device's state structure. This simulates a card
|
|
with no cable connected.
|
|
|
|
If the device is attached, setting it into diagnostic mode will detach it
|
|
first.
|
|
*/
|
|
|
|
static t_stat ipl_set_diag (UNIT *uptr, int32 value, CONST char *cptr, void *desc)
|
|
{
|
|
if (value) { /* if this is an entry into diagnostic mode */
|
|
ipl_detach (uptr); /* then detach it first */
|
|
uptr->flags |= UNIT_DIAG; /* before setting the flag */
|
|
}
|
|
|
|
else /* otherwise this is an entry into link mode */
|
|
uptr->flags &= ~UNIT_DIAG; /* so clear the flag */
|
|
|
|
if (ipli_unit.flags & iplo_unit.flags & UNIT_DIAG) { /* if both devices are now in diagnostic mode */
|
|
io_ptrs [ipli].input = &dev_bus [iplo].reverse.input; /* then connect the cable */
|
|
io_ptrs [ipli].output = &dev_bus [ipli].forward.output; /* so that the outputs of one card */
|
|
io_ptrs [iplo].input = &dev_bus [ipli].reverse.input; /* are connected to the inputs of the other card */
|
|
io_ptrs [iplo].output = &dev_bus [iplo].forward.output; /* and vice versa */
|
|
|
|
io_ptrs [ipli].output->cable_connected = TRUE; /* indicate that the cable */
|
|
io_ptrs [iplo].output->cable_connected = TRUE; /* has been connected between the cards */
|
|
}
|
|
|
|
else { /* otherwise */
|
|
if (ipli_unit.flags & UNIT_DIAG) { /* if the input card is in diagnostic mode */
|
|
io_ptrs [ipli].input = &dev_bus [ipli].reverse.input; /* then loop the card outputs */
|
|
io_ptrs [ipli].output = &dev_bus [ipli].forward.output; /* back to the inputs and vice versa */
|
|
io_ptrs [ipli].output->cable_connected = TRUE; /* and indicate that the card is connected */
|
|
}
|
|
|
|
else { /* otherwise the card is in link mode */
|
|
io_ptrs [ipli].input = &dev_bus [ipli].forward.input; /* so point at the card state */
|
|
io_ptrs [ipli].output = &dev_bus [ipli].forward.output; /* in the normal direction */
|
|
io_ptrs [ipli].output->cable_connected = FALSE; /* and indicate that the card is not connected */
|
|
}
|
|
|
|
if (iplo_unit.flags & UNIT_DIAG) { /* otherwise */
|
|
io_ptrs [iplo].input = &dev_bus [iplo].reverse.input; /* if the output card is in diagnostic mode */
|
|
io_ptrs [iplo].output = &dev_bus [iplo].forward.output; /* then loop the card outputs */
|
|
io_ptrs [iplo].output->cable_connected = TRUE; /* back to the inputs and vice versa */
|
|
} /* and indicate that the card is connected */
|
|
|
|
else {
|
|
io_ptrs [iplo].input = &dev_bus [iplo].forward.input; /* otherwise the card is in link mode */
|
|
io_ptrs [iplo].output = &dev_bus [iplo].forward.output; /* so point at the card state */
|
|
io_ptrs [iplo].output->cable_connected = FALSE; /* in the normal direction */
|
|
} /* and indicate that the card is not connected */
|
|
}
|
|
|
|
return SCPE_OK;
|
|
}
|
|
|
|
|
|
/* Synchronize the simulator instance.
|
|
|
|
This validation routine is entered with the "value" parameter set to zero to
|
|
wait on the synchronization event or non-zero to signal the synchronization
|
|
event. The unit, character, and descriptor pointers are not used.
|
|
|
|
This routine is called for the following commands:
|
|
|
|
SET IPLI WAIT
|
|
SET IPLI SIGNAL
|
|
|
|
If the event object has not been created yet by attaching the IPL device, the
|
|
routine returns "Command not allowed" status. For either command, the
|
|
routine returns "Command not completed" if the signal or wait function
|
|
returned an error to indicate that the error code register should be checked.
|
|
|
|
To permit the user to abort the wait command, a CTRL+C handler is installed,
|
|
and waits of one second each are performed in a loop. The handler will set
|
|
the "wait_aborted" variable TRUE if it is called, and the loop then will
|
|
terminate and return with success status.
|
|
|
|
|
|
Implementation notes:
|
|
|
|
1. The "wait_event" routine returns TRUE if the event is signaled and FALSE
|
|
if it times out while waiting.
|
|
*/
|
|
|
|
static t_stat ipl_set_sync (UNIT *uptr, int32 value, CONST char *cptr, void *desc)
|
|
{
|
|
const uint32 wait_time = 1000; /* the wait time in milliseconds */
|
|
t_bool signaled;
|
|
|
|
if (event_is_undefined (event_id)) /* if the event has not been defined yet */
|
|
return SCPE_NOFNC; /* then the command is not allowed */
|
|
|
|
if (value) { /* if this is a SIGNAL command */
|
|
event_error = signal_event (event_id); /* then signal the event */
|
|
|
|
if (event_error == 0) /* if signaling succeeded */
|
|
return SCPE_OK; /* then report command success */
|
|
else /* otherwise */
|
|
return SCPE_INCOMP; /* report that the command did not complete */
|
|
}
|
|
|
|
else { /* otherwise it's a WAIT command */
|
|
wait_aborted = FALSE; /* clear the abort flag */
|
|
signal (SIGINT, abort_handler); /* and set up the CTRL+C handler */
|
|
|
|
do
|
|
event_error = wait_event (event_id, wait_time, /* wait for the event */
|
|
&signaled); /* to be signaled */
|
|
while (! (event_error || wait_aborted || signaled)); /* unless an error or user abort occurs */
|
|
|
|
signal (SIGINT, SIG_DFL); /* restore the default CTRL+C handler */
|
|
|
|
if (event_error == 0) /* if the wait function succeeded */
|
|
return SCPE_OK; /* then report command success */
|
|
else /* otherwise */
|
|
return SCPE_INCOMP; /* report that the command did not complete */
|
|
}
|
|
}
|
|
|
|
|
|
/* Handler for the CTRL+C signal.
|
|
|
|
This handler is installed while waiting for a synchronization event. It is
|
|
called if the user presses CTRL+C to abort the wait command.
|
|
*/
|
|
|
|
static void abort_handler (int signal)
|
|
{
|
|
wait_aborted = TRUE; /* the user has aborted the event wait */
|
|
return;
|
|
}
|
|
|
|
|
|
/* Activate a unit.
|
|
|
|
The specified unit is added to the event queue with the delay specified by
|
|
the unit wait field.
|
|
|
|
|
|
Implementation notes:
|
|
|
|
1. This routine may be called with wait = 0, which will expire immediately
|
|
and enter the service routine with the next sim_process_event call.
|
|
Activation is required in this case to allow the service routine to
|
|
return an error code to stop the simulation. If the service routine was
|
|
called directly, any returned error would be lost.
|
|
*/
|
|
|
|
static void activate_unit (UNIT *uptr)
|
|
{
|
|
const CARD_INDEX card = (CARD_INDEX) (uptr == &iplo_unit); /* set card selector */
|
|
|
|
tpprintf (dptrs [card], TRACE_PSERV, "Poll delay %u service scheduled\n",
|
|
uptr->wait);
|
|
|
|
sim_activate (uptr, uptr->wait); /* activate the unit with the specified wait */
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/* Processor interconnect bootstrap loaders (special BBL and 12992K).
|
|
|
|
The special Basic Binary Loader (BBL) used by the 2000 Access system loads
|
|
absolute binary programs into memory from either the processor interconnect
|
|
interface or the paper tape reader interface. Two program entry points are
|
|
provided. Starting the loader at address x7700 loads from the processor
|
|
interconnect, while starting at address x7750 loads from the paper tape
|
|
reader. The S register setting does not affect loader operation.
|
|
|
|
For a 2100/14/15/16 CPU, entering a LOAD IPLI or BOOT IPLI command loads the
|
|
special BBL into memory and executes the processor interconnect portion
|
|
starting at x7700. Loader execution ends with one of the following halt
|
|
instructions:
|
|
|
|
* HLT 11 - a checksum error occurred; A/B = the calculated/tape value.
|
|
* HLT 55 - the program load address would overlay the loader.
|
|
* HLT 77 - the end of input with successful read; A = the paper tape select
|
|
code, B = the processor interconnect select code.
|
|
|
|
The 12992K boot loader ROM reads an absolute program from the processor
|
|
interconnect or paper tape interfaces into memory. The S register setting
|
|
does not affect loader operation. Loader execution ends with one of the
|
|
following halt instructions:
|
|
|
|
* HLT 11 - a checksum error occurred; A/B = the calculated/tape value.
|
|
* HLT 55 - the program load address would overlay the ROM loader.
|
|
* HLT 77 - the end of tape was reached with a successful read.
|
|
|
|
|
|
Implementation notes:
|
|
|
|
1. After the BMDL has been loaded into memory, the paper tape portion may be
|
|
executed manually by setting the P register to the starting address
|
|
(x7750).
|
|
|
|
2. For compatibility with the "cpu_copy_loader" routine, the BBL device I/O
|
|
instructions address select code 10.
|
|
|
|
3. For 2000B, C, and F versions that use dual CPUs, the I/O Processor is
|
|
loaded with the standard BBL configured for the select codes of the
|
|
processor interconnect interface. 2000 Access must use the special BBL
|
|
because the paper tape reader is connected to the IOP in this version; in
|
|
prior versions, it was connected to the System Processor and could use
|
|
the paper-tape portion of the BMDL that was installed in the SP.
|
|
*/
|
|
|
|
static const LOADER_ARRAY ipl_loaders = {
|
|
{ /* HP 21xx 2000/Access special Basic Binary Loader */
|
|
000, /* loader starting index */
|
|
IBL_NA, /* DMA index */
|
|
073, /* FWA index */
|
|
{ 0163774, /* 77700: PI LDA 77774,I Processor Interconnect start */
|
|
0027751, /* 77701: JMP 77751 */
|
|
0107700, /* 77702: START CLC 0,C */
|
|
0002702, /* 77703: CLA,CCE,SZA */
|
|
0063772, /* 77704: LDA 77772 */
|
|
0002307, /* 77705: CCE,INA,SZA,RSS */
|
|
0027760, /* 77706: JMP 77760 */
|
|
0017736, /* 77707: JSB 77736 */
|
|
0007307, /* 77710: CMB,CCE,INB,SZB,RSS */
|
|
0027705, /* 77711: JMP 77705 */
|
|
0077770, /* 77712: STB 77770 */
|
|
0017736, /* 77713: JSB 77736 */
|
|
0017736, /* 77714: JSB 77736 */
|
|
0074000, /* 77715: STB 0 */
|
|
0077771, /* 77716: STB 77771 */
|
|
0067771, /* 77717: LDB 77771 */
|
|
0047773, /* 77720: ADB 77773 */
|
|
0002040, /* 77721: SEZ */
|
|
0102055, /* 77722: HLT 55 */
|
|
0017736, /* 77723: JSB 77736 */
|
|
0040001, /* 77724: ADA 1 */
|
|
0177771, /* 77725: STB 77771,I */
|
|
0037771, /* 77726: ISZ 77771 */
|
|
0000040, /* 77727: CLE */
|
|
0037770, /* 77730: ISZ 77770 */
|
|
0027717, /* 77731: JMP 77717 */
|
|
0017736, /* 77732: JSB 77736 */
|
|
0054000, /* 77733: CPB 0 */
|
|
0027704, /* 77734: JMP 77704 */
|
|
0102011, /* 77735: HLT 11 */
|
|
0000000, /* 77736: NOP */
|
|
0006600, /* 77737: CLB,CME */
|
|
0103700, /* 77740: STC 0,C */
|
|
0102300, /* 77741: SFS 0 */
|
|
0027741, /* 77742: JMP 77741 */
|
|
0106400, /* 77743: MIB 0 */
|
|
0002041, /* 77744: SEZ,RSS */
|
|
0127736, /* 77745: JMP 77736,I */
|
|
0005767, /* 77746: BLF,CLE,BLF */
|
|
0027740, /* 77747: JMP 77740 */
|
|
0163775, /* 77750: PTAPE LDA 77775,I Paper tape start */
|
|
0043765, /* 77751: CONFG ADA 77765 */
|
|
0073741, /* 77752: STA 77741 */
|
|
0043766, /* 77753: ADA 77766 */
|
|
0073740, /* 77754: STA 77740 */
|
|
0043767, /* 77755: ADA 77767 */
|
|
0073743, /* 77756: STA 77743 */
|
|
0027702, /* 77757: EOT JMP 77702 */
|
|
0063777, /* 77760: LDA 77777 */
|
|
0067776, /* 77761: LDB 77776 */
|
|
0102077, /* 77762: HLT 77 */
|
|
0027702, /* 77763: JMP 77702 */
|
|
0000000, /* 77764: NOP */
|
|
0102300, /* 77765: SFS 0 */
|
|
0001400, /* 77766: OCT 1400 */
|
|
0002500, /* 77767: OCT 2500 */
|
|
0000000, /* 77770: OCT 0 */
|
|
0000000, /* 77771: OCT 0 */
|
|
0177746, /* 77772: DEC -26 */
|
|
0100100, /* 77773: ABS -PI */
|
|
0077776, /* 77774: DEF *+2 */
|
|
0077777, /* 77775: DEF *+2 */
|
|
0000010, /* 77776: PISC OCT 10 */
|
|
0000010 } }, /* 77777: PTRSC OCT 10 */
|
|
|
|
{ /* HP 1000 Loader ROM (12992K) */
|
|
IBL_START, /* loader starting index */
|
|
IBL_DMA, /* DMA index */
|
|
IBL_FWA, /* FWA index */
|
|
{ 0107700, /* 77700: ST CLC 0,C ; intr off */
|
|
0002401, /* 77701: CLA,RSS ; skip in */
|
|
0063756, /* 77702: CN LDA M11 ; feed frame */
|
|
0006700, /* 77703: CLB,CCE ; set E to rd byte */
|
|
0017742, /* 77704: JSB READ ; get #char */
|
|
0007306, /* 77705: CMB,CCE,INB,SZB ; 2's comp */
|
|
0027713, /* 77706: JMP *+5 ; non-zero byte */
|
|
0002006, /* 77707: INA,SZA ; feed frame ctr */
|
|
0027703, /* 77710: JMP *-3 */
|
|
0102077, /* 77711: HLT 77B ; stop */
|
|
0027700, /* 77712: JMP ST ; next */
|
|
0077754, /* 77713: STA WC ; word in rec */
|
|
0017742, /* 77714: JSB READ ; get feed frame */
|
|
0017742, /* 77715: JSB READ ; get address */
|
|
0074000, /* 77716: STB 0 ; init csum */
|
|
0077755, /* 77717: STB AD ; save addr */
|
|
0067755, /* 77720: CK LDB AD ; check addr */
|
|
0047777, /* 77721: ADB MAXAD ; below loader */
|
|
0002040, /* 77722: SEZ ; E =0 => OK */
|
|
0027740, /* 77723: JMP H55 */
|
|
0017742, /* 77724: JSB READ ; get word */
|
|
0040001, /* 77725: ADA 1 ; cont checksum */
|
|
0177755, /* 77726: STA AD,I ; store word */
|
|
0037755, /* 77727: ISZ AD */
|
|
0000040, /* 77730: CLE ; force wd read */
|
|
0037754, /* 77731: ISZ WC ; block done? */
|
|
0027720, /* 77732: JMP CK ; no */
|
|
0017742, /* 77733: JSB READ ; get checksum */
|
|
0054000, /* 77734: CPB 0 ; ok? */
|
|
0027702, /* 77735: JMP CN ; next block */
|
|
0102011, /* 77736: HLT 11 ; bad csum */
|
|
0027700, /* 77737: JMP ST ; next */
|
|
0102055, /* 77740: H55 HLT 55 ; bad address */
|
|
0027700, /* 77741: JMP ST ; next */
|
|
0000000, /* 77742: RD NOP */
|
|
0006600, /* 77743: CLB,CME ; E reg byte ptr */
|
|
0103710, /* 77744: STC RDR,C ; start reader */
|
|
0102310, /* 77745: SFS RDR ; wait */
|
|
0027745, /* 77746: JMP *-1 */
|
|
0106410, /* 77747: MIB RDR ; get byte */
|
|
0002041, /* 77750: SEZ,RSS ; E set? */
|
|
0127742, /* 77751: JMP RD,I ; no, done */
|
|
0005767, /* 77752: BLF,CLE,BLF ; shift byte */
|
|
0027744, /* 77753: JMP RD+2 ; again */
|
|
0000000, /* 77754: WC 000000 ; word count */
|
|
0000000, /* 77755: AD 000000 ; address */
|
|
0177765, /* 77756: M11 DEC -11 ; feed count */
|
|
0000000, /* 77757: NOP */
|
|
0000000, /* 77760: NOP */
|
|
0000000, /* 77761: NOP */
|
|
0000000, /* 77762: NOP */
|
|
0000000, /* 77763: NOP */
|
|
0000000, /* 77764: NOP */
|
|
0000000, /* 77765: NOP */
|
|
0000000, /* 77766: NOP */
|
|
0000000, /* 77767: NOP */
|
|
0000000, /* 77770: NOP */
|
|
0000000, /* 77771: NOP */
|
|
0000000, /* 77772: NOP */
|
|
0000000, /* 77773: NOP */
|
|
0000000, /* 77774: NOP */
|
|
0000000, /* 77775: NOP */
|
|
0000000, /* 77776: NOP */
|
|
0100100 } } /* 77777: MAXAD ABS -ST ; max addr */
|
|
};
|
|
|
|
|
|
/* Device boot routine.
|
|
|
|
This routine is called directly by the BOOT IPLI and LOAD IPLI commands to
|
|
copy the device bootstrap into the upper 64 words of the logical address
|
|
space. It is also called indirectly by a BOOT CPU or LOAD CPU command when
|
|
the specified HP 1000 loader ROM socket contains a 12992K ROM.
|
|
|
|
When called in response to a BOOT IPLI or LOAD IPLI command, the "unitno"
|
|
parameter indicates the unit number specified in the BOOT command or is zero
|
|
for the LOAD command, and "dptr" points at the IPLI device structure.
|
|
Depending on the current CPU model, the special BBL or 12992K loader ROM will
|
|
be copied into memory and configured for the IPLI select code. If the CPU is
|
|
a 1000, the S register will be set as it would be by the front-panel
|
|
microcode.
|
|
|
|
When called for a BOOT/LOAD CPU command, the "unitno" parameter indicates the
|
|
select code to be used for configuration, and "dptr" will be NULL. As above,
|
|
the special BBL or 12992K loader ROM will be copied into memory and
|
|
configured for the specified select code. The S register is assumed to be
|
|
set correctly on entry and is not modified.
|
|
|
|
For the 12992K boot loader ROM, the S register will be set as follows:
|
|
|
|
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
| ROM # | 0 0 | IPLI select code | 0 0 0 0 0 0 |
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
*/
|
|
|
|
static t_stat ipl_boot (int32 unitno, DEVICE *dptr)
|
|
{
|
|
static const HP_WORD ipl_ptx = 074u; /* the index of the pointer to the IPL select code */
|
|
static const HP_WORD ptr_ptx = 075u; /* the index of the pointer to the PTR select code */
|
|
static const HP_WORD ipl_scx = 076u; /* the index of the IPL select code */
|
|
static const HP_WORD ptr_scx = 077u; /* the index of the PTR select code */
|
|
uint32 start;
|
|
uint32 ptr_sc, ipl_sc;
|
|
DEVICE *ptr_dptr;
|
|
|
|
ptr_dptr = find_dev ("PTR"); /* get a pointer to the paper tape reader device */
|
|
|
|
if (ptr_dptr == NULL) /* if the paper tape device is not present */
|
|
return SCPE_IERR; /* then something is seriously wrong */
|
|
else /* otherwise */
|
|
ptr_sc = ((DIB *) ptr_dptr->ctxt)->select_code; /* get the select code from the device's DIB */
|
|
|
|
if (dptr == NULL) /* if we are being called for a BOOT/LOAD CPU command */
|
|
ipl_sc = (uint32) unitno; /* then get the select code from the "unitno" parameter */
|
|
else /* otherwise */
|
|
ipl_sc = ipli_dib.select_code; /* use the device select code from the DIB */
|
|
|
|
start = cpu_copy_loader (ipl_loaders, ipl_sc, /* copy the boot loader to memory */
|
|
IBL_S_NOCLEAR, IBL_S_NOSET); /* but do not alter the S register */
|
|
|
|
if (start == 0) /* if the copy failed */
|
|
return SCPE_NOFNC; /* then reject the command */
|
|
|
|
else { /* otherwise */
|
|
if (mem_examine (start + ptr_scx) <= SC_MAX) { /* if this is the special BBL */
|
|
mem_deposit (start + ipl_ptx, (HP_WORD) start + ipl_scx); /* then configure */
|
|
mem_deposit (start + ptr_ptx, (HP_WORD) start + ptr_scx); /* the pointers */
|
|
mem_deposit (start + ipl_scx, (HP_WORD) ipli_dib.select_code); /* and select codes */
|
|
mem_deposit (start + ptr_scx, (HP_WORD) ptr_sc); /* for the loader */
|
|
}
|
|
|
|
return SCPE_OK; /* the boot loader was successfully copied */
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/* Process synchronization functions */
|
|
|
|
|
|
|
|
#if defined (_WIN32)
|
|
|
|
/* Windows process synchronization */
|
|
|
|
|
|
/* Create a synchronization event.
|
|
|
|
This routine creates a synchronization event using the supplied name and
|
|
returns the event handle to the caller. If creation succeeds, the routine
|
|
returns 0. Otherwise, the error value is returned.
|
|
|
|
The event is created with these attributes: no security, automatic reset, and
|
|
initially non-signaled.
|
|
*/
|
|
|
|
static uint32 create_event (const char *name, EVENT *event)
|
|
{
|
|
*event = CreateEvent (NULL, FALSE, FALSE, name); /* create an auto-reset, initially not-signaled event */
|
|
|
|
tprintf (ipli_dev, TRACE_CMD, "Created event %p with identifier \"%s\"\n",
|
|
(void *) *event, name);
|
|
|
|
if (*event == NULL) /* if event creation failed */
|
|
return (uint32) GetLastError (); /* then return the error code */
|
|
else /* otherwise the creation succeeded */
|
|
return 0; /* so return success */
|
|
}
|
|
|
|
|
|
/* Destroy a synchronization event.
|
|
|
|
This routine destroys the synchronization event specified by the supplied
|
|
event handle. If destruction succeeds, the event handle is invalidated, and
|
|
the routine returns 0. Otherwise, the error value is returned.
|
|
|
|
The event name parameter is not used but is present for interoperability.
|
|
*/
|
|
|
|
static uint32 destroy_event (const char *name, EVENT *event)
|
|
{
|
|
BOOL status;
|
|
|
|
if (*event == NULL) /* if the event does not exist */
|
|
return 0; /* then indicate that it has already been destroyed */
|
|
|
|
else { /* otherwise the event exists */
|
|
status = CloseHandle (*event); /* so close it */
|
|
*event = NULL; /* and clear the event handle */
|
|
|
|
if (status == FALSE) /* if the close failed */
|
|
return (uint32) GetLastError (); /* then return the error code */
|
|
else /* otherwise the close succeeded */
|
|
return 0; /* so return success */
|
|
}
|
|
}
|
|
|
|
|
|
/* Test if the synchronization event exists.
|
|
|
|
This routine returns TRUE if the supplied event handle does not exist and
|
|
FALSE if the handle refers to a defined event.
|
|
*/
|
|
|
|
static t_bool event_is_undefined (EVENT event)
|
|
{
|
|
return (event == NULL); /* return TRUE if the event does not exist */
|
|
}
|
|
|
|
|
|
/* Wait for a synchronization event.
|
|
|
|
This routine waits for a synchronization event to be signaled or for the
|
|
supplied maximum wait time to elapse. If the event identified by the
|
|
supplied handle is signaled, the routine returns 0 and sets the "signaled"
|
|
flag to TRUE. If the timeout expires without the event being signaled, the
|
|
routine returns 0 with the "signaled" flag set to FALSE. If the event wait
|
|
fails, the routine returns the error value.
|
|
|
|
|
|
Implementation notes:
|
|
|
|
1. The maximum wait time may be zero to test the signaled state and return
|
|
immediately, or may be set to "INFINITE" to wait forever. The latter is
|
|
not recommended, as it provides the user with no means to cancel the wait
|
|
and return to the SCP prompt.
|
|
*/
|
|
|
|
static uint32 wait_event (EVENT event, uint32 wait_in_ms, t_bool *signaled)
|
|
{
|
|
const DWORD wait_time = (DWORD) wait_in_ms; /* interval wait time in milliseconds */
|
|
DWORD status;
|
|
|
|
status = WaitForSingleObject (event, wait_time); /* wait for the event, but not forever */
|
|
|
|
tprintf (ipli_dev, TRACE_CMD, "Wait status is %lu\n", status);
|
|
|
|
if (status == WAIT_FAILED) /* if the wait failed */
|
|
return (uint32) GetLastError (); /* then return the error code */
|
|
|
|
else { /* otherwise the wait completed */
|
|
*signaled = (status != WAIT_TIMEOUT); /* so set the flag TRUE if the wait did not time out */
|
|
return 0; /* and return success */
|
|
}
|
|
}
|
|
|
|
|
|
/* Signal the synchronization event.
|
|
|
|
This routine signals a the synchronization event specified by the supplied
|
|
event handle. If signaling succeeds, the routine returns 0. Otherwise, the
|
|
error value is returned.
|
|
*/
|
|
|
|
static uint32 signal_event (EVENT event)
|
|
{
|
|
BOOL status;
|
|
|
|
status = SetEvent (event); /* signal the event */
|
|
|
|
if (status == FALSE) /* if the call failed */
|
|
return (uint32) GetLastError (); /* then return the error code */
|
|
else /* otherwise the signal succeeded */
|
|
return 0; /* so return success */
|
|
}
|
|
|
|
|
|
|
|
#elif defined (HAVE_SEMAPHORE)
|
|
|
|
/* UNIX process synchronization */
|
|
|
|
|
|
/* Create the synchronization event.
|
|
|
|
This routine creates a synchronization event using the supplied name and
|
|
returns an event object to the caller. If creation succeeds, the routine
|
|
returns 0. Otherwise, the error value is returned.
|
|
|
|
Systems that define the semaphore functions but implement them as stubs will
|
|
return ENOSYS. We handle this case by enabling fallback to the unimplemented
|
|
behavior, i.e., emulating a process wait by a two-second sleep. All other
|
|
error returns are reported back to the caller.
|
|
|
|
Regarding the choice of event name, the Single Unix Standard says:
|
|
|
|
If [the] name begins with the <slash> character, then processes calling
|
|
sem_open() with the same value of name shall refer to the same semaphore
|
|
object, as long as that name has not been removed. If name does not begin
|
|
with the <slash> character, the effect is implementation-defined.
|
|
|
|
Therefore, event names passed to this routine should begin with a slash
|
|
character.
|
|
|
|
The event is created as initially not-signaled.
|
|
*/
|
|
|
|
static uint32 create_event (const char *name, EVENT *event)
|
|
{
|
|
*event = sem_open (name, O_CREAT, S_IRWXU, 0); /* create an initially not-signaled event */
|
|
|
|
if (*event == SEM_FAILED) /* if event creation failed */
|
|
if (errno == ENOSYS) { /* then if the function is not implemented */
|
|
tprintf (ipli_dev, TRACE_CMD, "sem_open is unsupported on this system; using fallback\n");
|
|
|
|
event_fallback = TRUE; /* then fall back to event emulation */
|
|
return 0; /* and claim that the open succeeded */
|
|
}
|
|
|
|
else { /* otherwise it is an unexpected error */
|
|
tprintf (ipli_dev, TRACE_CMD, "sem_open error is %u\n", errno);
|
|
return (uint32) errno; /* so return the error code */
|
|
}
|
|
|
|
else { /* otherwise the creation succeeded */
|
|
tprintf (ipli_dev, TRACE_CMD, "Created event %p with identifier \"%s\"\n",
|
|
(void *) *event, name);
|
|
return 0; /* so return success */
|
|
}
|
|
}
|
|
|
|
|
|
/* Destroy the synchronization event.
|
|
|
|
This routine destroys the synchronization event specified by the supplied
|
|
event name. If destruction succeeds, the event object is invalidated, and
|
|
the routine returns 0. Otherwise, the error value is returned.
|
|
|
|
|
|
Implementation notes:
|
|
|
|
1. If the other simulator instance destroys the event first, our
|
|
"sem_unlink" call will fail with ENOENT. This is an expected error, and
|
|
the routine returns success in this case.
|
|
*/
|
|
|
|
static uint32 destroy_event (const char *name, EVENT *event)
|
|
{
|
|
int status;
|
|
|
|
if (*event == SEM_FAILED) /* if the event does not exist */
|
|
return 0; /* then indicate that it has already been deleted */
|
|
|
|
else { /* otherwise the event exists */
|
|
status = sem_unlink (name); /* so delete it */
|
|
*event = SEM_FAILED; /* and clear the event handle */
|
|
|
|
if (status != 0 && errno != ENOENT) { /* if the deletion failed */
|
|
tprintf (ipli_dev, TRACE_CMD, "sem_unlink error is %u\n", errno);
|
|
return (uint32) errno; /* then return the error code */
|
|
}
|
|
|
|
else /* otherwise the deletion succeeded */
|
|
return 0; /* so return success */
|
|
}
|
|
}
|
|
|
|
|
|
/* Test if the synchronization event exists.
|
|
|
|
This routine returns TRUE if the supplied event object does not exist and
|
|
FALSE if the object refers to a defined event.
|
|
*/
|
|
|
|
static t_bool event_is_undefined (EVENT event)
|
|
{
|
|
if (event_fallback) /* if events are being emulated */
|
|
return FALSE; /* then claim that the event is defined */
|
|
else /* otherwise */
|
|
return (event == SEM_FAILED); /* return TRUE if the event does not exist */
|
|
}
|
|
|
|
|
|
/* Wait for the synchronization event.
|
|
|
|
This routine waits for a synchronization event to be signaled or for the
|
|
supplied maximum wait time to elapse. If the event identified by the
|
|
supplied event object is signaled, the routine returns 0 and sets the
|
|
"signaled" flag to TRUE. If the timeout expires without the event being
|
|
signaled, the routine returns 0 with the "signaled" flag set to FALSE. If
|
|
the event wait fails, the routine returns the error value.
|
|
|
|
|
|
Implementation notes:
|
|
|
|
1. The maximum wait time may be zero to test the signaled state and return
|
|
immediately, or may be set to a large value to wait forever. The latter
|
|
is not recommended, as it provides the user with no means to cancel the
|
|
wait and return to the SCP prompt.
|
|
*/
|
|
|
|
static uint32 wait_event (EVENT event, uint32 wait_in_ms, t_bool *signaled)
|
|
{
|
|
const int wait_time = (int) wait_in_ms / 1000; /* interval wait time in seconds */
|
|
struct timespec until_time;
|
|
int status;
|
|
|
|
if (event_fallback) { /* if events are being emulated */
|
|
sim_os_sleep (2); /* then wait for two seconds */
|
|
|
|
*signaled = TRUE; /* indicate a signaled completion */
|
|
return 0; /* and return success */
|
|
}
|
|
|
|
else if (clock_gettime (CLOCK_REALTIME, &until_time)) { /* get the current time; if it failed */
|
|
tprintf (ipli_dev, TRACE_CMD, "clock_gettime error is %u\n", errno);
|
|
return (uint32) errno; /* then return the error number */
|
|
}
|
|
|
|
else /* otherwise */
|
|
until_time.tv_sec = until_time.tv_sec + wait_time; /* set the (absolute) timeout expiration */
|
|
|
|
status = sem_timedwait (event, &until_time); /* wait for the event, but not forever */
|
|
|
|
*signaled = (status == 0); /* set the flag TRUE if the wait did not time out */
|
|
|
|
if (status) /* if the wait terminated */
|
|
if (errno == ETIMEDOUT || errno == EINTR) /* then if it timed out or was manually aborted */
|
|
return 0; /* then return success */
|
|
|
|
else { /* otherwise it's an unexpected error */
|
|
tprintf (ipli_dev, TRACE_CMD, "sem_timedwait error is %u\n", errno);
|
|
return (uint32) errno; /* so return the error code */
|
|
}
|
|
|
|
else /* otherwise the event is signaled */
|
|
return 0; /* so return success */
|
|
}
|
|
|
|
|
|
/* Signal the synchronization event.
|
|
|
|
This routine signals a the synchronization event specified by the supplied
|
|
event handle. If signaling succeeds, the routine returns 0. Otherwise, the
|
|
error value is returned.
|
|
*/
|
|
|
|
static uint32 signal_event (EVENT event)
|
|
{
|
|
int status;
|
|
|
|
if (event_fallback) /* if events are being emulated */
|
|
return 0; /* then claim that the event was signaled */
|
|
else /* otherwise */
|
|
status = sem_post (event); /* signal the event */
|
|
|
|
if (status) { /* if the call failed */
|
|
tprintf (ipli_dev, TRACE_CMD, "sem_post error is %u\n", errno);
|
|
return (uint32) errno; /* then return the error code */
|
|
}
|
|
|
|
else /* otherwise the event was signaled */
|
|
return 0; /* so return success */
|
|
}
|
|
|
|
|
|
|
|
#else
|
|
|
|
/* Process synchronization stubs.
|
|
|
|
The stubs return success, rather than failure, because we want the callers to
|
|
continue as though synchronization is occurring, even though the host system
|
|
does not support the operations necessary to implement this. Without host
|
|
system synchronization support, the simulated system's OS might work, but if
|
|
the routines returned failure, then the simulator command files running on
|
|
such systems would refuse to run.
|
|
|
|
|
|
Implementation notes:
|
|
|
|
1. We provide an event wait by sleeping for a few seconds to give the other
|
|
simulator instance a chance to catch up. This has a reasonable chance of
|
|
working, provided the other instance isn't preempted during the sleep.
|
|
*/
|
|
|
|
static uint32 create_event (const char *name, EVENT *event)
|
|
{
|
|
tprintf (ipli_dev, TRACE_CMD, "Synchronization is unsupported on this system; using fallback\n");
|
|
return 0;
|
|
}
|
|
|
|
|
|
static uint32 destroy_event (const char *name, EVENT *event)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
|
|
static t_bool event_is_undefined (EVENT event)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
static uint32 wait_event (EVENT event, uint32 wait_in_ms, t_bool *signaled)
|
|
{
|
|
sim_os_sleep (2); /* wait for two seconds */
|
|
|
|
*signaled = TRUE; /* then indicate a signaled completion */
|
|
return 0; /* and return success */
|
|
}
|
|
|
|
|
|
static uint32 signal_event (EVENT event)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
|
|
#endif
|