HP3000: Initial release of the HP 3000 Series III simulator (from Dave Bryan)

This commit is contained in:
Mark Pizzolato 2016-03-07 20:47:57 -08:00
parent 53b6809e4a
commit 3a4e879c07
29 changed files with 36616 additions and 2 deletions

2849
HP3000/hp3000_atc.c Normal file

File diff suppressed because it is too large Load diff

929
HP3000/hp3000_clk.c Normal file
View file

@ -0,0 +1,929 @@
/* hp3000_clk.c: HP 3000 30135A System Clock/Fault Logging Interface simulator
Copyright (c) 2016, 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
AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the name of the author 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 author.
CLK HP 30135A System Clock/Fault Logging Interface
08-Jun-15 JDB First release version
12-Aug-14 JDB Passed the system clock diagnostic (D426A)
05-Jul-14 JDB Created
References:
- Stand-Alone System Clock Diagnostic
(32230-90005, January 1979)
- HP 3000 Series III Engineering Diagrams Set
(30000-90141, April 1980)
The HP 30135A System Clock/Fault Logging Interface is used with Series II and
III systems and provides two devices on a single I/O card: a programmable
interval clock employed as the MPE system clock and an interface to the ECC
fault logging RAMs on the semiconductor main memory arrays. This replaced
the earlier 30031A System Clock/Console Interface that had been used with the
CX and Series I machines, which used core memory. As part of this change,
the system console moved from the dedicated card to ATC port 0.
The clock provides programmable periods of 10 microseconds to 10 seconds in
decade increments. Each "tick" of the clock increments a presettable counter
that may be compared to a selected limit value. The clock may request an
interrupt when the values are equal, and a status indication is provided if
the counter reaches the limit a second time without acknowledgement.
The clock simulation provides both a REALTIME mode that establishes periods
in terms of event intervals, based on an average instruction time of 2.5
microseconds, and a CALTIME mode that calibrates the time delays to match
wall-clock time. As an example, in the former mode, a 1 millisecond period
will elapse after 400 instructions are executed, whereas in the latter mode,
the same period will elapse after 1 millisecond of wall-clock time. As the
simulator is generally one or two orders of magnitude faster than a real HP
3000, the real-time mode will satisfy the expectations of software that times
external events, such as a disc seek, via a delay loop, whereas the
calibrated mode will update a time-of-day clock as expected by users of the
system. In practice, this means that setting REALTIME mode is necessary to
satisfy the hardware diagnostics, and setting CALTIME mode is necessary when
running MPE.
Currently, the Fault Logging Interface simulator is not implemented. This
interface is accessed via DRT 2 by the MPE memory logging process, MEMLOGP,
but the process is smart enough to terminate if DRT 2 does not respond. As
the simulator relies on a host memory array to simulate RAM and does not
simulate the ECC check bits, an FLI implementation would always return a "no
errors detected" condition.
The clock interface responds only to direct I/O instructions, as follows:
Control Word Format (CIO):
0 | 1 2 3 | 4 5 6 | 7 8 9 |10 11 12 |13 14 15
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| M / rate | E | - | irq reset | C | L | A | - - - - | I |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
Where:
M = master reset (if bit 3 = 0)
E = master reset enable/load count rate (0/1)
C = reset count register after LR=CR interrupt
L = a WIO addresses the limit/count (0/1) register
A = reset all interrupts
I = enable clock interrupts
Count Rate Selection:
000 = unused
001 = 10 microseconds
010 = 100 microseconds
011 = 1 millisecond
100 = 10 milliseconds
101 = 100 milliseconds
110 = 1 second
111 = 10 seconds
IRQ Reset:
000 = none
001 = clear LR = CR interrupt
010 = clear LR = CR overflow interrupt
011 = clear I/O system interrupt (SIN)
100 = unused
101 = unused
110 = unused
111 = unused
Status Word Format (TIO):
0 | 1 2 3 | 4 5 6 | 7 8 9 |10 11 12 |13 14 15
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| S | D | rate | - - - - - | C | F | - | I | L | R |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
Where:
S = SIO OK (always 0)
D = direct read/write I/O OK (always 1)
C = limit register = count register
F = limit register = count register overflow (lost tick)
I = I/O system interrupt request (SIN)
L = limit/count (0/1) register selected
R = reset count register after interrupt
Output Data Word Format (WIO):
0 | 1 2 3 | 4 5 6 | 7 8 9 |10 11 12 |13 14 15
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| limit register value/count register reset |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
If control word bit 9 is 0, the value is written to the limit register. If
control word bit 9 is 1, the count register is cleared to zero; the output
value is ignored.
Input Data Word Format (RIO):
0 | 1 2 3 | 4 5 6 | 7 8 9 |10 11 12 |13 14 15
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| count register value |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
Implementation notes:
1. MPE sets the system clock to a 1 millisecond period and a 100 millisecond
limit to achieve the 10 interrupts per second rate required by the
time-of-day clock maintained by the OS. The short period precludes
idling. Therefore, this configuration is detected and implemented
internally as a 10 millisecond service time with the counter incremented
by 10 for each event service. In addition, the clock service is
synchronized with the CPU process clock service and the ATC poll service
to improve idling.
2. If the clock is calibrated, a prescaler is used to achieve the 1 second
and 10 second periods while the event service time remains at 100
milliseconds. For periods shorter than 1 second, and for all realtime
periods, the prescaler is not used. The prescaler is necessary because
the "sim_rtcn_calb" routine in the sim_timer library requires an integer
ticks-per-second parameter.
*/
#include "hp3000_defs.h"
#include "hp3000_io.h"
/* Program constants */
#define CLK_MULTIPLIER 10 /* number of MPE clock ticks per service */
#define CLK_RATE (1000 / CLK_MULTIPLIER) /* MPE clock rate in ticks per second */
static const int32 delay [8] = { /* clock delays, in event ticks per interval */
0, /* 000 = unused */
uS (10), /* 001 = 10 microseconds */
uS (100), /* 010 = 100 microseconds */
mS (1), /* 011 = 1 millisecond */
mS (10), /* 100 = 10 milliseconds */
mS (100), /* 101 = 100 milliseconds */
S (1), /* 110 = 1 second */
S (10), /* 111 = 10 seconds */
};
static const int32 ticks [8] = { /* clock ticks per second */
0, /* 000 = unused */
100000, /* 001 = 10 microseconds */
10000, /* 010 = 100 microseconds */
1000, /* 011 = 1 millisecond */
100, /* 100 = 10 milliseconds */
10, /* 101 = 100 milliseconds */
10, /* 110 = 1 second */
10 /* 111 = 10 seconds */
};
static const int32 scale [8] = { /* prescaler counts per clock tick */
1, /* 000 = unused */
1, /* 001 = 10 microseconds */
1, /* 010 = 100 microseconds */
1, /* 011 = 1 millisecond */
1, /* 100 = 10 milliseconds */
1, /* 101 = 100 milliseconds */
10, /* 110 = 1 second */
100 /* 111 = 10 seconds */
};
/* Unit flags */
#define UNIT_CALTIME_SHIFT (UNIT_V_UF + 0) /* calibrated timing mode */
#define UNIT_CALTIME (1 << UNIT_CALTIME_SHIFT)
/* Debug flags */
#define DEB_CSRW (1 << 0) /* trace commands received and status returned */
#define DEB_PSERV (1 << 1) /* trace unit service scheduling calls */
#define DEB_IOB (1 << 2) /* trace I/O bus signals and data words exchanged */
/* Control word.
0 | 1 2 3 | 4 5 6 | 7 8 9 |10 11 12 |13 14 15
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| M / rate | E | - | irq reset | C | L | A | - - - - | I |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
*/
#define CN_MR 0100000 /* (M) master reset (if bit 3 = 0) */
#define CN_RATE_MASK 0160000 /* clock rate selector mask (if bit 3 = 1) */
#define CN_RESET_LOAD_SEL 0010000 /* (E) select reset/load rate (0/1) */
#define CN_IRQ_RESET_MASK 0003400 /* interrupt request reset selector mask */
#define CN_COUNT_RESET 0000200 /* (C) reset count register after LR=CR interrupt */
#define CN_LIMIT_COUNT_SEL 0000100 /* (L) select limit/count (0/1) register */
#define CN_IRQ_RESET_ALL 0000040 /* (A) reset all interrupt requests */
#define CN_IRQ_ENABLE 0000001 /* (I) enable clock interrupts */
#define CN_RATE_SHIFT 13 /* clock rate alignment shift */
#define CN_IRQ_RESET_SHIFT 8 /* interrupt request reset alignment shift */
#define CN_RATE(c) (((c) & CN_RATE_MASK) >> CN_RATE_SHIFT)
#define CN_RESET(c) (((c) & CN_IRQ_RESET_MASK) >> CN_IRQ_RESET_SHIFT)
static const char *const rate_name [8] = { /* clock rate selector names */
"unused", /* 000 = unused */
"10 microsecond", /* 001 = 10 microseconds */
"100 microsecond", /* 010 = 100 microseconds */
"1 millisecond", /* 011 = 1 millisecond */
"10 millisecond", /* 100 = 10 milliseconds */
"100 millisecond", /* 101 = 100 milliseconds */
"1 second", /* 110 = 1 second */
"10 second" /* 111 = 10 seconds */
};
static const char *const irq_reset_name [8] = { /* IRQ reset selector names */
NULL, /* 000 = none */
"reset LR = CR irq", /* 001 = LR equal CR */
"reset LR = CR overflow irq", /* 010 = LR equal CR overflow */
"reset SIN irq", /* 011 = I/O system */
NULL, /* 100 = unused */
NULL, /* 101 = unused */
NULL, /* 110 = unused */
NULL, /* 111 = unused */
};
static const BITSET_NAME control_names [] = { /* Control word names */
"master reset", /* bit 0 */
NULL, /* bit 1 */
NULL, /* bit 2 */
"load rate", /* bit 3 */
NULL, /* bit 4 */
NULL, /* bit 5 */
NULL, /* bit 6 */
NULL, /* bit 7 */
"reset count", /* bit 8 */
"\1select count\0select limit", /* bit 9 */
"reset interrupts", /* bit 10 */
NULL, /* bit 11 */
NULL, /* bit 12 */
NULL, /* bit 13 */
NULL, /* bit 14 */
"enable interrupts" /* bit 15 */
};
static const BITSET_FORMAT control_format = /* names, offset, direction, alternates, bar */
{ FMT_INIT (control_names, 0, msb_first, has_alt, no_bar) };
/* Status word.
0 | 1 2 3 | 4 5 6 | 7 8 9 |10 11 12 |13 14 15
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| - | D | rate | - - - - - | C | F | - | I | L | R |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
*/
#define ST_DIO_OK 0040000 /* (D) direct I/O OK to use */
#define ST_RATE_MASK 0034000 /* clock rate mask */
#define ST_LR_EQ_CR 0000040 /* (C) limit register = count register */
#define ST_LR_EQ_CR_OVFL 0000020 /* (F) limit register = count register overflow */
#define ST_SYSTEM_IRQ 0000004 /* (I) I/O system interrupt request */
#define ST_LIMIT_COUNT_SEL 0000002 /* (L) limit/count (0/1) register selected */
#define ST_COUNT_RESET 0000001 /* (R) count register is reset after LR=CR interrupt */
#define ST_RATE_SHIFT 11 /* clock rate alignment shift */
#define ST_RATE(r) ((r) << ST_RATE_SHIFT & ST_RATE_MASK)
#define ST_TO_RATE(s) (((s) & ST_RATE_MASK) >> ST_RATE_SHIFT)
static const BITSET_NAME status_names [] = { /* Status word names */
"DIO OK", /* bit 1 */
NULL, /* bit 2 */
NULL, /* bit 3 */
NULL, /* bit 4 */
NULL, /* bit 5 */
NULL, /* bit 6 */
NULL, /* bit 7 */
NULL, /* bit 8 */
NULL, /* bit 9 */
"LR = CR", /* bit 10 */
"LR = CR overflow", /* bit 11 */
NULL, /* bit 12 */
"system interrupt", /* bit 13 */
"\1count selected\0limit selected", /* bit 14 */
"reset after interrupt" /* bit 15 */
};
static const BITSET_FORMAT status_format = /* names, offset, direction, alternates, bar */
{ FMT_INIT (status_names, 0, msb_first, has_alt, append_bar) };
/* System clock state */
static FLIP_FLOP system_irq = CLEAR; /* SIN interrupt request flip-flop */
static FLIP_FLOP limit_irq = CLEAR; /* limit = count interrupt request flip-flop */
static FLIP_FLOP lost_tick_irq = CLEAR; /* limit = count overflow interrupt request flip-flop */
static uint32 control_word; /* control word */
static uint32 status_word; /* status word */
static uint32 count_register; /* counter register */
static uint32 limit_register; /* limit register */
static uint32 rate; /* clock rate */
static uint32 prescaler; /* clock rate prescaler */
static uint32 increment = 1; /* count register increment */
static t_bool coschedulable = FALSE; /* TRUE if the clock can be coscheduled with PCLK */
static t_bool coscheduled = FALSE; /* TRUE if the clock is coscheduled with PCLK */
/* System clock local SCP support routines */
static CNTLR_INTRF clk_interface;
static t_stat clk_service (UNIT *uptr);
static t_stat clk_reset (DEVICE *dptr);
/* System clock local utility routines */
static void resync_clock (void);
/* System clock SCP interface data structures */
/* Device information block */
static DIB clk_dib = {
&clk_interface, /* device interface */
3, /* device number */
SRNO_UNUSED, /* service request number */
1, /* interrupt priority */
INTMASK_UNUSED /* interrupt mask */
};
/* Unit list */
static UNIT clk_unit = {
UDATA (&clk_service, UNIT_IDLE | UNIT_CALTIME, 0)
};
/* Register list */
static REG clk_reg [] = {
/* Macro Name Location Width Offset Flags */
/* ------ ------ --------------- ----- ------ ------- */
{ ORDATA (CNTL, control_word, 16) },
{ ORDATA (STAT, status_word, 16) },
{ ORDATA (COUNT, count_register, 16) },
{ ORDATA (LIMIT, limit_register, 16) },
{ ORDATA (RATE, rate, 3) },
{ FLDATA (SYSIRQ, system_irq, 0) },
{ FLDATA (LIMIRQ, limit_irq, 0) },
{ FLDATA (OVFIRQ, lost_tick_irq, 0) },
{ DRDATA (SCALE, prescaler, 16), REG_HRO },
{ DRDATA (INCR, increment, 16), REG_HRO },
{ FLDATA (COSOK, coschedulable, 0), REG_HRO },
{ FLDATA (COSCH, coscheduled, 0), REG_HRO },
{ SRDATA (DIB, clk_dib), REG_HRO },
{ NULL }
};
/* Modifier list */
static MTAB clk_mod [] = {
/* Mask Value Match Value Print String Match String Validation Display Descriptor */
/* ------------ ------------ ------------------- ------------ ---------- ------- ---------- */
{ UNIT_CALTIME, UNIT_CALTIME, "calibrated timing", "CALTIME", NULL, NULL, NULL },
{ UNIT_CALTIME, 0, "realistic timing", "REALTIME", NULL, NULL, NULL },
/* Entry Flags Value Print String Match String Validation Display Descriptor */
/* ----------- ----------- ------------ ------------ ------------ ------------- ----------------- */
{ MTAB_XDV, VAL_DEVNO, "DEVNO", "DEVNO", &hp_set_dib, &hp_show_dib, (void *) &clk_dib },
{ MTAB_XDV, VAL_INTPRI, "INTPRI", "INTPRI", &hp_set_dib, &hp_show_dib, (void *) &clk_dib },
{ 0 }
};
/* Debugging trace list */
static DEBTAB clk_deb [] = {
{ "CSRW", DEB_CSRW }, /* interface control, status, read, and write actions */
{ "PSERV", DEB_PSERV }, /* clock unit service scheduling calls */
{ "IOBUS", DEB_IOB }, /* interface I/O bus signals and data words */
{ NULL, 0 }
};
/* Device descriptor */
DEVICE clk_dev = {
"CLK", /* device name */
&clk_unit, /* unit array */
clk_reg, /* register array */
clk_mod, /* modifier array */
1, /* number of units */
8, /* address radix */
PA_WIDTH, /* address width */
1, /* address increment */
8, /* data radix */
DV_WIDTH, /* data width */
NULL, /* examine routine */
NULL, /* deposit routine */
&clk_reset, /* reset routine */
NULL, /* boot routine */
NULL, /* attach routine */
NULL, /* detach routine */
&clk_dib, /* device information block pointer */
DEV_DISABLE | DEV_DEBUG, /* device flags */
0, /* debug control flags */
clk_deb, /* debug flag name array */
NULL, /* memory size change routine */
NULL /* logical device name */
};
/* System clock global routines */
/* Update the counter register.
If the clock is currently coscheduled with the CPU process clock, then the
service interval is actually ten times the programmed rate. To present the
correct value when the counter register is read, this routine is called to
increment the count by an amount proportional to the fraction of the service
interval that has elapsed. In addition, it's called by the CPU instruction
postlude, so that the counter will have the correct value if it's examined
from the SCP command prompt.
This routine is also called when the counter is to be reset. This ensures
that the increment is reduced by the time elapsed before the counter is
zeroed.
*/
void clk_update_counter (void)
{
int32 elapsed, ticks;
if (coscheduled) { /* if the clock is coscheduled, then adjust the count */
elapsed = clk_unit.wait /* the elapsed time is the original wait time */
- sim_activate_time (&clk_unit); /* less the time remaining before the next service */
ticks = (elapsed * CLK_MULTIPLIER) / clk_unit.wait /* the adjustment is the elapsed fraction of the multiplier */
- (CLK_MULTIPLIER - increment); /* less the amount of any adjustment already made */
count_register = count_register + ticks & R_MASK; /* update the clock counter with rollover */
increment = increment - ticks; /* and reduce the amount remaining to add at service */
}
return;
}
/* System clock local SCP support routines */
/* System clock interface.
The system clock is installed on the IOP bus and receives direct I/O commands
from the IOP. It does not respond to Programmed I/O (SIO) commands.
In simulation, the asserted signals on the bus are represented as bits in the
inbound_signals set. Each signal is processed sequentially in numerical
order, and a set of similar outbound_signals is assembled and returned to the
caller, simulating assertion of the corresponding bus signals.
There is no interrupt mask; interrupts are always unmasked, and the interface
does not respond to the SMSK I/O order.
Implementation notes:
1. In hardware, setting the tick rate in the control word addresses a
multiplexer that selects one of the 10 MHz clock division counter outputs
as the clock source for the count register. Setting the rate bits to 0
inhibits the count register, although the division counter continues to
run. In simulation, setting a new rate stops and then restarts the event
service with the new delay time, equivalent in hardware to clearing the
clock division counter.
2. Receipt of a DRESETINT signal clears the interrupt request and active
flip-flops but does not cancel a request that is pending but not yet
serviced by the IOP. However, when the IOP does service the request by
asserting INTPOLLIN, the interface routine returns INTPOLLOUT, which will
cancel the request.
3. The "%.0s" print specification in the DCONTSTB trace call absorbs the
rate name parameter without printing when the rate is not specified.
*/
static SIGNALS_DATA clk_interface (DIB *dibptr, INBOUND_SET inbound_signals, uint16 inbound_value)
{
INBOUND_SIGNAL signal;
INBOUND_SET working_set = inbound_signals;
uint16 outbound_value = 0;
OUTBOUND_SET outbound_signals = NO_SIGNALS;
dprintf (clk_dev, DEB_IOB, "Received data %06o with signals %s\n",
inbound_value, fmt_bitset (inbound_signals, inbound_format));
while (working_set) {
signal = IONEXTSIG (working_set); /* isolate the next signal */
switch (signal) { /* dispatch an I/O signal */
case DCONTSTB:
control_word = inbound_value; /* save the control word */
if (control_word & CN_RESET_LOAD_SEL) { /* if the reset/load selector is set */
rate = CN_RATE (control_word); /* then load the clock rate */
if (clk_unit.flags & UNIT_CALTIME) /* if in calibrated timing mode */
prescaler = scale [rate]; /* then set the prescaler */
else /* otherwise */
prescaler = 1; /* the prescaler isn't used */
sim_cancel (&clk_unit); /* changing the rate restarts the timing divider */
if (rate > 0) { /* if the rate is valid */
clk_unit.wait = delay [rate]; /* then set the initial service delay */
sim_rtcn_init (clk_unit.wait, TMR_CLK); /* initialize the clock */
resync_clock (); /* and reschedule the service */
}
}
else if (control_word & CN_MR) { /* otherwise, if the master reset bit is set */
clk_reset (&clk_dev); /* then reset the interface */
control_word = 0; /* (which clears the other settings) */
}
if (control_word & CN_IRQ_RESET_ALL) { /* if a reset of all interrupts is requested */
limit_irq = CLEAR; /* then clear the limit = count, */
lost_tick_irq = CLEAR; /* limit = count overflow, */
system_irq = CLEAR; /* and system flip-flops */
}
else if (control_word & CN_IRQ_RESET_MASK) /* otherwise if any single resets are requested */
switch (CN_RESET (control_word)) { /* then reset the specified flip-flop */
case 1:
limit_irq = CLEAR; /* clear the limit = count interrupt request */
break;
case 2:
lost_tick_irq = CLEAR; /* clear the limit = count overflow interrupt request */
break;
case 3:
system_irq = CLEAR; /* clear the system interrupt request */
break;
default: /* the rest of the values do nothing */
break;
}
if (dibptr->interrupt_active == CLEAR) /* if no interrupt is active */
working_set |= DRESETINT; /* then recalculate interrupt requests */
dprintf (clk_dev, DEB_CSRW, (control_word & CN_RESET_LOAD_SEL
? "Control is %s | %s rate\n"
: "Control is %s%.0s\n"),
fmt_bitset (inbound_value, control_format),
rate_name [CN_RATE (inbound_value)]);
break;
case DSTATSTB:
status_word = ST_DIO_OK | ST_RATE (rate); /* set the clock rate */
if (limit_irq) /* if the limit = count flip-flop is set */
status_word |= ST_LR_EQ_CR; /* set the corresponding status bit */
if (lost_tick_irq) /* if the limit = count overflow flip-flop is set */
status_word |= ST_LR_EQ_CR_OVFL; /* set the corresponding status bit */
if (system_irq) /* if the system interrupt request flip-flop is set */
status_word |= ST_SYSTEM_IRQ; /* set the corresponding status bit */
if (control_word & CN_LIMIT_COUNT_SEL) /* if the limit/count selector is set */
status_word |= ST_LIMIT_COUNT_SEL; /* set the corresponding status bit */
if (control_word & CN_COUNT_RESET) /* if the reset-after-interrupt selector is set */
status_word |= ST_COUNT_RESET; /* set the corresponding status bit */
outbound_value = (uint16) status_word; /* return the status word */
dprintf (clk_dev, DEB_CSRW, "Status is %s%s rate\n",
fmt_bitset (outbound_value, status_format),
rate_name [ST_TO_RATE (outbound_value)]);
break;
case DREADSTB:
clk_update_counter (); /* update the clock counter register */
outbound_value = LOWER_WORD (count_register); /* and then read it */
dprintf (clk_dev, DEB_CSRW, "Count register value %d returned\n",
count_register);
break;
case DWRITESTB:
if (control_word & CN_LIMIT_COUNT_SEL) { /* if the limit/count selector is set */
clk_update_counter (); /* then update the clock counter register */
count_register = 0; /* and then clear it */
dprintf (clk_dev, DEB_CSRW, "Count register cleared\n");
}
else { /* otherwise */
limit_register = inbound_value; /* set the limit register to the supplied value */
dprintf (clk_dev, DEB_CSRW, "Limit register value %d set\n",
limit_register);
coschedulable = (ticks [rate] == 1000 /* the clock can be coscheduled if the rate */
&& limit_register == 100); /* is 1 msec and the limit is 100 ticks */
}
break;
case DSETINT:
system_irq = SET; /* set the system interrupt request flip-flop */
dibptr->interrupt_request = SET; /* request an interrupt */
outbound_signals |= INTREQ; /* and notify the IOP */
break;
case DRESETINT:
dibptr->interrupt_active = CLEAR; /* clear the Interrupt Active flip-flop */
dibptr->interrupt_request = system_irq /* recalculate the interrupt request signal */
|| control_word & CN_IRQ_ENABLE
&& (limit_irq | lost_tick_irq);
if (dibptr->interrupt_request) /* if a request is pending */
outbound_signals |= INTREQ; /* then notify the IOP */
break;
case INTPOLLIN:
if (dibptr->interrupt_request) { /* if a request is pending */
dibptr->interrupt_request = CLEAR; /* then clear it */
dibptr->interrupt_active = SET; /* and mark it as now active */
outbound_signals |= INTACK; /* acknowledge the interrupt */
outbound_value = dibptr->device_number; /* and return our device number */
}
else /* otherwise the request has been reset */
outbound_signals |= INTPOLLOUT; /* so let the IOP know to cancel it */
break;
case DSTARTIO: /* not used by this interface */
case DSETMASK: /* not used by this interface */
case ACKSR: /* not used by this interface */
case TOGGLESR: /* not used by this interface */
case SETINT: /* not used by this interface */
case PCMD1: /* not used by this interface */
case PCONTSTB: /* not used by this interface */
case SETJMP: /* not used by this interface */
case PSTATSTB: /* not used by this interface */
case PWRITESTB: /* not used by this interface */
case PREADSTB: /* not used by this interface */
case EOT: /* not used by this interface */
case TOGGLEINXFER: /* not used by this interface */
case TOGGLEOUTXFER: /* not used by this interface */
case READNEXTWD: /* not used by this interface */
case TOGGLESIOOK: /* not used by this interface */
case DEVNODB: /* not used by this interface */
case XFERERROR: /* not used by this interface */
case CHANSO: /* not used by this interface */
case PFWARN: /* not used by this interface */
break;
}
IOCLEARSIG (working_set, signal); /* remove the current signal from the set */
}
dprintf (clk_dev, DEB_IOB, "Returned data %06o with signals %s\n",
outbound_value, fmt_bitset (outbound_signals, outbound_format));
return IORETURN (outbound_signals, outbound_value); /* return the outbound signals and value */
}
/* Service the system clock unit.
At each "tick" of the clock, the count register is incremented and compared
to the limit register. If they are equal, then the counter is cleared (if
enabled) and an interrupt is generated (if enabled).
If the clock is calibrated, a prescaler is used to achieve the 1 second and
10 second periods while the event time remains at 100 milliseconds. For
periods shorter than 1 second, and for all realtime periods, the prescaler is
not used (by setting the value to 1).
If the clock is currently coscheduled with the CPU process clock, then the
service interval is actually ten times the programmed rate, so the count
register increment per service entry is 10 instead of 1.
Implementation notes:
1. The count/limit comparison hardware provides only an equal condition. If
the limit register is set to a value below the current count, or the
LR=CR interrupt is not enabled until after the count register value has
exceeded the limit, comparison will not occur until the count register
overflows and again reaches the limit.
*/
static t_stat clk_service (UNIT *uptr)
{
dprintf (clk_dev, DEB_PSERV, "Service entered with counter %d increment %d limit %d\n",
count_register, increment, limit_register);
prescaler = prescaler - 1; /* decrement the prescaler count */
if (prescaler == 0) { /* if the prescaler count has expired */
count_register = count_register + increment & R_MASK; /* then the count register counts up */
if (count_register == limit_register) { /* if the limit has been reached */
if (limit_irq == SET) /* then if the last limit interrupt wasn't serviced */
lost_tick_irq = SET; /* then set the overflow interrupt */
else /* otherwise */
limit_irq = SET; /* set the limit interrupt */
if (control_word & CN_COUNT_RESET) /* if the counter reset option is selected */
count_register = 0; /* then clear the count register */
if (control_word & CN_IRQ_ENABLE /* if clock interrupts are enabled */
&& clk_dib.interrupt_active == CLEAR) { /* and the interrupt active flip-flop is clear */
clk_dib.interrupt_request = SET; /* then request an interrupt */
iop_assert_INTREQ (&clk_dib); /* and notify the IOP of the INTREQ signal */
}
}
if (uptr->flags & UNIT_CALTIME) /* if in calibrated timing mode */
prescaler = scale [rate]; /* then reset the prescaler */
else /* otherwise */
prescaler = 1; /* the prescaler isn't used */
}
if (!(uptr->flags & UNIT_CALTIME)) { /* if the clock is in real timing mode */
uptr->wait = delay [rate]; /* then set an event-based delay */
increment = 1; /* equal to the selected period */
coscheduled = FALSE; /* the clock is not coscheduled with the process clock */
}
else if (coschedulable && cpu_is_calibrated) { /* otherwise if the process clock is calibrated */
uptr->wait = sim_activate_time (cpu_pclk_uptr); /* then synchronize with it */
increment = CLK_MULTIPLIER; /* at one-tenth of the selected period */
coscheduled = TRUE; /* the clock is coscheduled with the process clock */
}
else { /* otherwise */
uptr->wait = sim_rtcn_calb (ticks [rate], TMR_CLK); /* calibrate the clock to a delay */
increment = 1; /* equal to the selected period */
coscheduled = FALSE; /* the clock is not coscheduled with the process clock */
}
dprintf (clk_dev, DEB_PSERV, "Rate %s delay %d service %s\n",
rate_name [rate], uptr->wait,
(coscheduled ? "coscheduled" : "scheduled"));
return sim_activate (uptr, uptr->wait); /* activate the unit and return the status */
}
/* Device reset.
This routine is called for a RESET or RESET CLK command. It is the
simulation equivalent of the IORESET signal, which is asserted by the front
panel LOAD and DUMP switches.
For this interface, IORESET is identical to a Programmed Master Reset
(control word bit 0 set with bit 3 clear).
A master reset is generated either by an IORESET signal or a Direct I/O
Master Reset (control word bit 0 set with bit 3 clear).
Implementation notes:
1. In simulation, the Enable Clock Interrupts flip-flop, the Reset Count
Register after LR=CR Interrupt flip-flop, and the Address Limit/Count
Register flip-flop are maintained in the control word rather than as
separate values.
2. The hardware interrupt circuitry contains an Interrupt Active flip-flop
and an Interrupt Priority latch but no Interrupt Request flip-flop.
Instead, the INTREQ signal is the logical OR of the LR=CR Interrupt and
LR=CR Overflow Interrupt flip-flops (if enabled by the Enable Clock
Interrupts flip-flop) with the the System Interrupt flip-flop. In
simulation, the interrupt_request flip-flop in the Device Information
Block is set explicitly to reflect this logic. Clearing the three
interrupt source flip-flops therefore clears the interrupt_request
flip-flop as well.
3. In simulation, the clock division counters are represented by the event
service delay. Stopping and restarting the delay is equivalent to
clearing the division counters.
*/
static t_stat clk_reset (DEVICE *dptr)
{
count_register = 0; /* clear the count */
limit_register = 0; /* and limit registers */
rate = 0; /* clear the clock rate */
prescaler = 1; /* and set the clock prescaler */
sim_cancel (dptr->units); /* clearing the rate stops the clock */
clk_dib.interrupt_request = CLEAR; /* clear any current */
clk_dib.interrupt_active = CLEAR; /* interrupt request */
system_irq = CLEAR; /* clear the system, */
limit_irq = CLEAR; /* limit = count, */
lost_tick_irq = CLEAR; /* and limit = count overflow flip-flops */
control_word = 0; /* clear the enable, write select, and count reset actions */
return SCPE_OK;
}
/* System clock local utility routines */
/* Resynchronize the clock.
After changing the rate or the limit, the new values are examined to see if
the clock may be coscheduled with the process clock to permit idling. If
coscheduling is possible and both the system clock and the CPU process clock
are calibrated, then the clock event service is synchronized with the process
clock service. Otherwise, the service time is set up but is otherwise
asynchronous with the process clock.
Implementation notes:
1. To synchronize events, the clock must be activated absolutely, as a
service event may already be scheduled, and normal activation will not
disturb an existing event.
*/
static void resync_clock (void)
{
coschedulable = (ticks [rate] == 1000 /* the clock can be coscheduled if the rate */
&& limit_register == 100); /* is 1 msec and the limit is 100 ticks */
if (clk_unit.flags & UNIT_CALTIME /* if the clock is in calibrated timing mode */
&& coschedulable /* and may be coscheduled with the process clock */
&& cpu_is_calibrated) { /* and the process clock is calibrated */
clk_unit.wait = sim_activate_time (cpu_pclk_uptr); /* then synchronize with it */
coscheduled = TRUE; /* the clock is coscheduled with the process clock */
}
else { /* otherwise */
clk_unit.wait = delay [rate]; /* set up an independent clock */
coscheduled = FALSE; /* the clock is not coscheduled with the process clock */
}
dprintf (clk_dev, DEB_PSERV, "Rate %s delay %d service rescheduled\n",
rate_name [rate], clk_unit.wait);
sim_activate_abs (&clk_unit, clk_unit.wait); /* restart the clock */
return;
}

4033
HP3000/hp3000_cpu.c Normal file

File diff suppressed because it is too large Load diff

992
HP3000/hp3000_cpu.h Normal file
View file

@ -0,0 +1,992 @@
/* hp3000_cpu.h: HP 3000 CPU declarations
Copyright (c) 2016, 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
AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the name of the author 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 author.
14-Feb-16 JDB First release version
11-Dec-12 JDB Created
This file provides the declarations for interoperation between the CPU and
its supporting modules. It provides the symbols that allow direct
manipulation of the CPU registers and determination of currently installed
features.
*/
#include <setjmp.h>
/* Supported breakpoint switches */
#define BP_EXEC (SWMASK ('E')) /* an execution breakpoint */
#define BP_SUPPORTED (BP_EXEC) /* the list of supported breakpoint types */
/* Unit flags and accessors */
#define UNIT_MODEL_SHIFT (UNIT_V_UF + 0) /* the CPU model (1 bit) */
#define UNIT_EIS_SHIFT (UNIT_V_UF + 1) /* the Extended Instruction Set firmware option */
#define UNIT_CALTIME_SHIFT (UNIT_V_UF + 2) /* the process clock timing mode */
#define UNIT_MODEL_MASK 0000001 /* model ID mask */
#define UNIT_MODEL (UNIT_MODEL_MASK << UNIT_MODEL_SHIFT)
#define UNIT_SERIES_III (0 << UNIT_MODEL_SHIFT) /* the CPU is a Series III */
#define UNIT_SERIES_II (1 << UNIT_MODEL_SHIFT) /* the CPU is a Series II */
#define UNIT_EIS (1 << UNIT_EIS_SHIFT) /* the Extended Instruction Set is installed */
#define UNIT_CALTIME (1 << UNIT_CALTIME_SHIFT) /* the process clock is calibrated to wall time */
#define UNIT_CPU_MODEL (cpu_unit.flags & UNIT_MODEL_MASK)
#define CPU_MODEL(f) ((f) >> UNIT_MODEL_SHIFT & UNIT_MODEL_MASK)
/* CPU debug flags */
#define DEB_MDATA (1 << 0) /* trace memory data accesses */
#define DEB_INSTR (1 << 1) /* trace instruction execution */
#define DEB_FETCH (1 << 2) /* trace instruction fetches */
#define DEB_REG (1 << 3) /* trace register values */
#define DEB_PSERV (1 << 4) /* trace PCLK service events */
#define BOV_FORMAT "%02o.%06o %06o " /* bank-offset-value trace format string */
/* CPU stop flags */
#define SS_LOOP (1u << 0) /* stop on infinite loop */
#define SS_PAUSE (1u << 1) /* stop on PAUS instruction */
#define SS_UNDEF (1u << 2) /* stop on undefined instruction */
#define SS_UNIMPL (1u << 3) /* stop on unimplemented instruction */
#define SS_BYPASSED (1u << 31) /* stops are bypassed for this instruction */
/* Micromachine execution state */
typedef enum {
running, /* the micromachine is running */
paused, /* a PAUS instruction has been executed */
loading, /* a COLD LOAD is in progress */
halted /* a programmed or front panel HALT has been executed */
} EXEC_STATE;
/* Memory access classifications.
The access classification determines which bank register is used with the
supplied offset to access memory, and whether or not the access is bounds
checked.
Implementation notes:
1. The "_iop" and "_sel" classifications are identical. The only difference
is which device's trace flag is checked to print debugging information.
All of the other classifications check the CPU's trace flags.
*/
typedef enum {
absolute_iop, /* absolute bank, IOP request */
dma_iop, /* DMA channel bank, IOP request */
absolute_sel, /* absolute bank, selector channel request */
dma_sel, /* DMA channel bank, selector channel request */
absolute, /* absolute bank */
absolute_checked, /* absolute bank, bounds checked */
fetch, /* program bank, instruction fetch */
fetch_checked, /* program bank, instruction fetch, bounds checked */
program, /* program bank, data access */
program_checked, /* program bank, data access, bounds checked */
data, /* data bank, data access */
data_checked, /* data bank, data access, bounds checked */
stack, /* stack bank, data access */
stack_checked /* stack bank, data access, bounds checked */
} ACCESS_CLASS;
/* CPX register flags.
The CPX1 register contains flags that designate the run-time interrupts. The
CPX2 register contains flags for halt-time interrupts.
Implementation notes:
1. These are implemented as enumeration types to allow the "gdb" debugger to
display the CPX register values as bit sets.
*/
typedef enum {
cpx1_INTOVFL = 0100000, /* integer overflow */
cpx1_BNDVIOL = 0040000, /* bounds violation */
cpx1_ILLADDR = 0020000, /* illegal address */
cpx1_CPUTIMER = 0010000, /* CPU timer */
cpx1_SYSPAR = 0004000, /* system parity error */
cpx1_ADDRPAR = 0002000, /* address parity error */
cpx1_DATAPAR = 0001000, /* data parity error */
cpx1_MODINTR = 0000400, /* module interrupt */
cpx1_EXTINTR = 0000200, /* external interrupt */
cpx1_PFINTR = 0000100, /* power fail interrupt */
/* cpx1_UNUSED = 0000040, unused, always 0 */
cpx1_ICSFLAG = 0000020, /* ICS flag */
cpx1_DISPFLAG = 0000010, /* dispatcher-is-active flag */
cpx1_EMULATOR = 0000004, /* emulator-in-use flag */
cpx1_IOTIMER = 0000002, /* I/O timeout */
cpx1_OPTION = 0000001 /* option present */
} CPX1FLAG;
#define CPX1_IRQ_SET (cpx1_INTOVFL | cpx1_BNDVIOL | \
cpx1_ILLADDR | cpx1_CPUTIMER | \
cpx1_SYSPAR | cpx1_ADDRPAR | \
cpx1_DATAPAR | cpx1_MODINTR | \
cpx1_EXTINTR | cpx1_PFINTR) /* the set of CPX1 interrupt requests */
typedef enum {
cpx2_RUNSWCH = 0100000, /* RUN switch */
cpx2_DUMPSWCH = 0040000, /* DUMP switch */
cpx2_LOADSWCH = 0020000, /* LOAD switch */
cpx2_LOADREG = 0010000, /* load register */
cpx2_LOADADDR = 0004000, /* load address */
cpx2_LOADMEM = 0002000, /* load memory */
cpx2_DISPMEM = 0001000, /* display memory */
cpx2_SNGLINST = 0000400, /* single instruction */
cpx2_EXECSWCH = 0000200, /* EXECUTE switch */
cpx2_INCRADDR = 0000100, /* increment address */
cpx2_DECRADDR = 0000040, /* decrement address */
/* cpx2_UNUSED = 0000020, unused, always 0 */
/* cpx2_UNUSED = 0000010, unused, always 0 */
cpx2_INHPFARS = 0000004, /* inhibit power fail autorestart */
cpx2_SYSHALT = 0000002, /* system halt */
cpx2_RUN = 0000001 /* run flip-flop */
} CPX2FLAG;
#define CPX2_IRQ_SET (cpx2_RUNSWCH | cpx2_DUMPSWCH | \
cpx2_LOADSWCH | cpx2_LOADREG | \
cpx2_LOADADDR | cpx2_LOADMEM | \
cpx2_DISPMEM | cpx2_SNGLINST | \
cpx2_EXECSWCH) /* the set of CPX2 interrupt requests */
/* Interrupt classifications.
Interrupts are generated by setting CPX1 bits, by microcode aborts (traps),
or by the DISP or IXIT instructions to run the OS dispatcher or in response
to an external interrupt. Each interrupt source is serviced by a procedure
in the Segment Transfer Table of segment 1, and the classification values are
chosen to match the STT numbers.
Implementation notes:
1. The STT numbers are relevant only for interrupts that come from setting
the CPX1 bits (except for bit 8). The enumeration values for external,
trap, DISP, and IXIT interrupts are not significant.
*/
typedef enum {
/* identifier STT Source Description */
/* ----------------------- --- ------ ------------------------- */
irq_Integer_Overflow = 000, /* CPX1.0 Integer Overflow */
irq_Bounds_Violation = 001, /* CPX1.1 Bounds Violation */
irq_Illegal_Address = 002, /* CPX1.2 Illegal Memory Address */
irq_Timeout = 003, /* CPX1.3 Non-Responding Module */
irq_System_Parity = 004, /* CPX1.4 System Parity Error */
irq_Address_Parity = 005, /* CPX1.5 Address Parity Error */
irq_Data_Parity = 006, /* CPX1.6 Data Parity Error */
irq_Module = 007, /* CPX1.7 Module Interrupt */
irq_External = 010, /* CPX1.8 External Interrupt */
irq_Power_Fail = 011, /* CPX1.9 Power Fail Interrupt */
irq_Trap = 012, /* uABORT System or user trap */
irq_Dispatch = 013, /* DISP Run the dispatcher */
irq_IXIT = 014 /* IXIT External interrupt */
} IRQ_CLASS;
/* Trap classifications.
Except for the power-on trap, all traps result from microcode aborts. In
hardware, these result in microcode jumps to the appropriate trap handlers.
In simulation, a MICRO_ABORT executes a "longjmp" to the trap handler just
before the main instruction loop. As with interrupts, each trap is serviced
by a procedure in the Segment Transfer Table of segment 1, and the
classification values are chosen to match the STT numbers.
Traps are invoked by the MICRO_ABORT macro, which takes as its parameter a
trap classification value. Some traps require an additional parameter and
must be invoked by the MICRO_ABORTP macro, which takes a trap classification
value and a trap-specific value as parameters.
Accessors are provided to separate the TRAP_CLASS and the parameter from the
combined longjmp value.
Implementation notes:
1. Trap classifications must be > 0 for longjmp compatibility.
2. A System Halt trap does not call an OS procedure but instead stops the
simulator. The parameter number indicates the reason for the halt.
3. The User trap is subdivided into traps for a number of arithmetic
conditions. These are indicated by their corresponding parameter numbers
in the upper words of the longjmp values.
*/
typedef enum {
/* identifier STT Source Description */
/* ------------------------ --- ------ ------------------------- */
trap_None = 000, /* -- (none) */
trap_Bounds_Violation = 001, /* ucode Bounds Violation */
trap_Unimplemented = 020, /* ucode Unimplemented Instruction */
trap_STT_Violation = 021, /* ucode STT Violation */
trap_CST_Violation = 022, /* ucode CST Violation */
trap_DST_Violation = 023, /* ucode DST Violation */
trap_Stack_Underflow = 024, /* ucode Stack Underflow */
trap_Privilege_Violation = 025, /* ucode Privileged Mode Violation */
trap_Stack_Overflow = 030, /* ucode Stack Overflow */
trap_User = 031, /* ucode User Trap */
trap_CS_Absent = 037, /* ucode Absent Code Segment */
trap_Trace = 040, /* ucode Trace */
trap_Uncallable = 041, /* ucode STT Entry Uncallable */
trap_DS_Absent = 042, /* ucode Absent Data Segment */
trap_Power_On = 043, /* hdwe Power On */
trap_Cold_Load = 044, /* ucode Cold Load */
trap_System_Halt = 045, /* ucode System Halt */
} TRAP_CLASS;
#define trap_Integer_Overflow TO_DWORD (001, trap_User)
#define trap_Float_Overflow TO_DWORD (002, trap_User)
#define trap_Float_Underflow TO_DWORD (003, trap_User)
#define trap_Integer_Zero_Divide TO_DWORD (004, trap_User)
#define trap_Float_Zero_Divide TO_DWORD (005, trap_User)
#define trap_Ext_Float_Overflow TO_DWORD (010, trap_User)
#define trap_Ext_Float_Underflow TO_DWORD (011, trap_User)
#define trap_Ext_Float_Zero_Divide TO_DWORD (012, trap_User)
#define trap_Decimal_Overflow TO_DWORD (013, trap_User)
#define trap_Invalid_ASCII_Digit TO_DWORD (014, trap_User)
#define trap_Invalid_Decimal_Digit TO_DWORD (015, trap_User)
#define trap_Invalid_Word_Count TO_DWORD (016, trap_User)
#define trap_Word_Count_Overflow TO_DWORD (017, trap_User)
#define trap_Decimal_Zero_Divide TO_DWORD (020, trap_User)
#define trap_SysHalt_STTV_1 TO_DWORD ( 1, trap_System_Halt)
#define trap_SysHalt_Absent_ICS TO_DWORD ( 2, trap_System_Halt)
#define trap_SysHalt_Absent_1 TO_DWORD ( 3, trap_System_Halt)
#define trap_SysHalt_Overflow_ICS TO_DWORD ( 4, trap_System_Halt)
#define trap_SysHalt_IO_Timeout TO_DWORD ( 6, trap_System_Halt)
#define trap_SysHalt_PSEB_Enabled TO_DWORD ( 9, trap_System_Halt)
#define trap_SysHalt_CSTV_1 TO_DWORD (13, trap_System_Halt)
#define trap_SysHalt_LOCK_EI TO_DWORD (23, trap_System_Halt)
#define trap_SysHalt_Trace_1 TO_DWORD (33, trap_System_Halt)
#define PARAM(i) (uint32) UPPER_WORD (i)
#define TRAP(i) (TRAP_CLASS) LOWER_WORD (i)
#define MICRO_ABORT(t) longjmp (cpu_save_env, (t))
#define MICRO_ABORTP(t,p) longjmp (cpu_save_env, TO_DWORD ((p),(t)))
/* Status register accessors.
The CPU status register, STA, has this format:
0 | 1 2 3 | 4 5 6 | 7 8 9 |10 11 12 |13 14 15
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| M | I | T | R | O | C | ccode | current code segment number |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
Where:
M = user mode/protected mode (0/1)
I = external interrupts are enabled
T = user traps are enabled
R = right-hand stack operation executes next
O = arithmetic overflow has occurred
C = arithmetic carry has occurred
Condition Code:
00 = CCL (less than)
01 = CCE (equal to)
10 = CCG (greater than)
11 = invalid
*/
#define STATUS_M 0100000 /* mode flag */
#define STATUS_I 0040000 /* interrupt flag */
#define STATUS_T 0020000 /* trap flag */
#define STATUS_R 0010000 /* right-hand stack op flag */
#define STATUS_O 0004000 /* overflow flag */
#define STATUS_C 0002000 /* carry flag */
#define STATUS_CCG 0000000 /* condition code greater than */
#define STATUS_CCL 0000400 /* condition code less than */
#define STATUS_CCE 0001000 /* condition code equal to */
#define STATUS_CC_MASK 0001400 /* condition code mask */
#define STATUS_CC_SHIFT 8 /* condition code alignment */
#define STATUS_CS_MASK 0000377 /* code segment mask */
#define STATUS_CS_WIDTH 8 /* code segment mask width */
#define STATUS_OVTRAP (STATUS_T | STATUS_O)
#define STATUS_NPRV (STATUS_T | STATUS_O | STATUS_C | STATUS_CC_MASK)
#define TO_CCN(s) (((s) & STATUS_CC_MASK) >> STATUS_CC_SHIFT)
/* Status register mode tests */
#define PRIV (STA & STATUS_M) /* current mode is privileged */
#define NPRV (!PRIV) /* current mode is non-privileged */
/* Condition code flags.
Several instructions define "condition code flags" that specify condition
code tests to be performed. These flags are ANDed with the condition code
from the status register to establish whether the test passes. CCE and CCL
are encoded in the status register by bits 6 and 7, respectively, but CCG is
encoded as 00, causing a direct AND to fail. This is resolved in microcode
by the "CC" S-bus micro-order, which copies bits 6 and 7 to bits 8 and 9 of
the S-bus register and also sets bit 7 if bits 8 and 9 are zero (CCG). The
result is a single bit set for each condition code that may be ANDed with the
CCF field of the instruction.
The MVBW (Move Bytes While) instruction moves bytes from the source to the
destination while the bytes are alphabetic, numeric, or either, depending on
the A and N bits in the instruction. The CCB classification table is used to
determine the type of each byte moved. If both A and N bits are set, then a
table entry of either CCG or CCE will succeed. Matching via a single AND
operation is possible only if the encoding of all three conditions is
disjoint.
We implement this scheme without the two-bit right-shift, so that ANDing a
CCF with STATUS_CC_MASK will produce the correctly aligned status register
value. The TO_CCF macro converts the current condition code in the status
register to a condition code flag.
*/
#define CFL 0000400 /* condition code flag less than */
#define CFE 0001000 /* condition code flag equal to */
#define CFG 0002000 /* condition code flag greater than */
#define TO_CCF(s) ((s) & STATUS_CC_MASK ? (s) & STATUS_CC_MASK : CFG)
/* Condition code patterns.
Machine instructions typically set the condition code field of the status
register to reflect the values of their operands, which may be on the top of
the stack, in the X register, or in memory. Each instruction that affects
the condition code field commonly uses one of the following patterns to set
the field:
CCA (arithmetic) sets CC to:
- CCG (00) if the operand is > 0
- CCL (01) if the operand is < 0
- CCE (10) if the operand is = 0
CCB (byte) sets CC to:
- CCG (00) if the operand is numeric (byte value is 060-071)
- CCL (01) if the operand is any other character
- CCE (10) if the operand is alphabetic (0101-0132 or 0141-0172)
CCC (comparison) sets CC to:
- CCG (00) if operand 1 is > operand 2
- CCL (01) if operand 1 is < operand 2
- CCE (10) if operand 1 is = operand 2
CCD (direct I/O) sets CC to:
- CCG (00) if the device is not ready
- CCL (01) if the device controller is not responding
- CCE (10) if the device is ready and controller is responding
The operand(s) may be a byte, word, double word, triple word, or quadruple
word, and may be signed (integer) or unsigned (logical).
Statement macros are provided to set the condition code explicitly, as well
as to set the code using patterns CCA, CCB, or CCC. The CCA macro takes two
parameters: an upper word and a lower word. The CCB macro takes a single
byte parameter. The CCC macro takes four parameters: an upper and lower word
for each of the two operands.
No macro is provided for pattern CCD, as that pattern reflects the
operational status of the target device and interface, rather than the
condition of an operand. The I/O instructions use the explicit macros to set
the codes for pattern D.
*/
#define SET_CCG STA = STA & ~STATUS_CC_MASK | STATUS_CCG
#define SET_CCL STA = STA & ~STATUS_CC_MASK | STATUS_CCL
#define SET_CCE STA = STA & ~STATUS_CC_MASK | STATUS_CCE
/* Condition code pattern CCA (arithmetic).
Semantics:
if operand > 0
then CCG
else if operand < 0
then CCL
else
CCE
Macro:
SET_CCA (upper, lower)
Implementation:
if upper = 0 or lower = 0
then CCE
else if upper.sign = 1
then CCL
else CCG
Usage:
SET_CCA (RA, 0) -- a 16-bit integer value
SET_CCA (0, RA) -- a 16-bit logical value
SET_CCA (RB, RA) -- a 32-bit integer value
SET_CCA (0, RB | RA) -- a 32-bit logical value
SET_CCA (RC, RB | RA) -- a 48-bit integer value
SET_CCA (RD, RC | RB | RA) -- a 64-bit integer value
Implementation notes:
1. When either "upper" or "lower" is 0, the corresponding test is optimized
away.
*/
#define SET_CCA(u,l) STA = STA & ~STATUS_CC_MASK | \
(((u) | (l)) == 0 \
? STATUS_CCE \
: (u) & D16_SIGN \
? STATUS_CCL \
: STATUS_CCG)
/* Condition code pattern CCB (byte).
Semantics:
if operand in 060 .. 071
then CCG
else if operand in 0101 .. 0132 or operand in 0141 .. 0172
then CCE
else
CCL
Macro:
SET_CCB (byte)
Implementation:
CCx = ccb_lookup_table [byte]
Usage:
SET_CCB (RA)
Implementation notes:
1. The byte parameter is not masked before being used as an array index, so
the caller must ensure that the value is between 0 and 255.
2. The table value must be masked if it is to be stored in the status
register because CCG is returned as 100 (base 2) to ensure that all
values are disjoint for the MVBW and Bcc instructions.
*/
#define SET_CCB(b) STA = STA & ~STATUS_CC_MASK | \
cpu_ccb_table [(b)] & STATUS_CC_MASK
/* Condition code pattern CCC (conditional).
Semantics:
if operand_1 > operand_2
then CCG
else if operand_1 < operand_2
then CCL
else
CCE
Macro:
SET_CCC (upper_1, lower_1, upper_2, lower_2)
Implementation:
if upper_1 = upper_2
then
if lower_1 = lower_2
then CCE
else if lower_1 < lower_2
then CCL
else CCG
else if |upper_1| < |upper_2|
then CCL
else CCG
Usage:
SET_CCC (RA, 0, RB, 0) -- 16-bit integer comparison
SET_CCC ( 0, RA, 0, RB) -- 16-bit logical comparison
SET_CCC (RB, RA, RD, RC) -- 32-bit integer comparison
Implementation notes:
1. When any of the parameters are 0, the corresponding tests are optimized
away.
2. Complementing the signs of the upper words allows an unsigned comparison
to substitute for a signed comparison.
*/
#define SET_CCC(u1,l1,u2,l2) STA = STA & ~STATUS_CC_MASK | \
((u1) == (u2) \
? ((l1) == (l2) \
? STATUS_CCE \
: ((l1) < (l2) \
? STATUS_CCL \
: STATUS_CCG)) \
: (((u1) ^ D16_SIGN) < ((u2) ^ D16_SIGN) \
? STATUS_CCL \
: STATUS_CCG))
/* Set carry and overflow.
These macros are used by arithmetic operations to set the carry and overflow
bits in the status register. In addition to setting the overflow bit, if
user traps are enabled, the Integer Overflow bit in the CPX1 register is set,
which will cause an interrupt
*/
#define SET_CARRY(b) STA = ((b) ? STA | STATUS_C : STA & ~STATUS_C)
#define SET_OVERFLOW(b) if (b) { \
STA |= STATUS_O; \
if (STA & STATUS_T) \
CPX1 |= cpx1_INTOVFL; \
} \
else \
STA &= ~STATUS_O
/* SR preadjust.
The PREADJUST_SR macro is called to ensure that the correct number of TOS
registers are valid prior to instruction execution.
*/
#define PREADJUST_SR(n) if (SR < (n)) \
cpu_adjust_sr (n)
/* Machine instruction bit-field accessors */
#define BITS_0_3_MASK 0170000 /* bits 0-3 mask */
#define BITS_0_3_SHIFT 12 /* bits 0-3 alignment shift */
#define BITS_0_3(v) (((v) & BITS_0_3_MASK) >> BITS_0_3_SHIFT)
#define BITS_4_5_MASK 0006000 /* bits 4-5 mask */
#define BITS_4_5_SHIFT 10 /* bits 4-5 alignment shift */
#define BITS_4_5(v) (((v) & BITS_4_5_MASK) >> BITS_4_5_SHIFT)
#define BITS_4_7_MASK 0007400 /* bits 4-7 mask */
#define BITS_4_7_SHIFT 8 /* bits 4-7 alignment shift */
#define BITS_4_7(v) (((v) & BITS_4_7_MASK) >> BITS_4_7_SHIFT)
#define BITS_4_9_MASK 0007700 /* bits 4-9 mask */
#define BITS_4_9_SHIFT 6 /* bits 4-9 alignment shift */
#define BITS_4_9(v) (((v) & BITS_4_9_MASK) >> BITS_4_9_SHIFT)
#define BITS_5_9_MASK 0003700 /* bits 5-9 mask */
#define BITS_5_9_SHIFT 6 /* bits 5-9 alignment shift */
#define BITS_5_9(v) (((v) & BITS_5_9_MASK) >> BITS_5_9_SHIFT)
#define BITS_8_11_MASK 0000360 /* bits 8-11 mask */
#define BITS_8_11_SHIFT 4 /* bits 8-11 alignment shift */
#define BITS_8_11(v) (((v) & BITS_8_11_MASK) >> BITS_8_11_SHIFT)
#define BITS_8_12_MASK 0000370 /* bits 8-12 mask */
#define BITS_8_12_SHIFT 3 /* bits 8-12 alignment shift */
#define BITS_8_12(v) (((v) & BITS_8_12_MASK) >> BITS_8_12_SHIFT)
#define BITS_10_15_MASK 0000077 /* bits 10-15 mask */
#define BITS_10_15_SHIFT 0 /* bits 10-15 alignment shift */
#define BITS_10_15(v) (((v) & BITS_10_15_MASK) >> BITS_10_15_SHIFT)
#define BITS_12_15_MASK 0000017 /* bits 12-15 mask */
#define BITS_12_15_SHIFT 0 /* bits 12-15 alignment shift */
#define BITS_12_15(v) (((v) & BITS_12_15_MASK) >> BITS_12_15_SHIFT)
/* Instruction-class accessors */
#define SUBOP_MASK BITS_0_3_MASK /* subopcode mask */
#define SUBOP_SHIFT BITS_0_3_SHIFT /* subopcode alignment shift */
#define SUBOP(v) BITS_0_3(v) /* subopcode accessor */
#define STACKOP_A_MASK BITS_4_9_MASK /* stack operation A mask */
#define STACKOP_A_SHIFT BITS_4_9_SHIFT /* stack operation A alignment shift */
#define STACKOP_A(v) BITS_4_9(v) /* stack operation A accessor */
#define STACKOP_B_MASK BITS_10_15_MASK /* stack operation B mask */
#define STACKOP_B_SHIFT BITS_10_15_SHIFT /* stack operation B alignment shift */
#define STACKOP_B(v) BITS_10_15(v) /* stack operation B accessor */
#define SBBOP_MASK BITS_5_9_MASK /* shift/branch/bit operation mask */
#define SBBOP_SHIFT BITS_5_9_SHIFT /* shift/branch/bit operation alignment shift */
#define SBBOP(v) BITS_5_9(v) /* shift/branch/bit operation accessor */
#define MSFIFROP_MASK BITS_4_7_MASK /* move/special/firmware/immediate/field/register operation mask */
#define MSFIFROP_SHIFT BITS_4_7_SHIFT /* move/special/firmware/immediate/field/register operation alignment shift */
#define MSFIFROP(v) BITS_4_7(v) /* move/special/firmware/immediate/field/register operation accessor */
#define MSSUBOP_MASK BITS_8_12_MASK /* move/special suboperation mask */
#define MSSUBOP_SHIFT BITS_8_12_SHIFT /* move/special suboperation alignment shift */
#define MSSUBOP(v) BITS_8_12(v) /* move/special suboperation accessor */
#define SPECOP_MASK BITS_12_15_MASK /* special operation mask */
#define SPECOP_SHIFT BITS_12_15_SHIFT /* special operation alignment shift */
#define SPECOP(v) BITS_12_15(v) /* special operation accessor */
#define IOCPIMOP_MASK BITS_4_7_MASK /* I-O/control/program/immediate/memory operation mask */
#define IOCPIMOP_SHIFT BITS_4_7_SHIFT /* I-O/control/program/immediate/memory operation alignment shift */
#define IOCPIMOP(v) BITS_4_7(v) /* I-O/control/program/immediate/memory operation accessor */
#define IOCSUBOP_MASK BITS_8_11_MASK /* I-O/control suboperation mask */
#define IOCSUBOP_SHIFT BITS_8_11_SHIFT /* I-O/control suboperation alignment shift */
#define IOCSUBOP(v) BITS_8_11(v) /* I-O/control suboperation accessor */
#define CNTLOP_MASK BITS_12_15_MASK /* control operation mask */
#define CNTLOP_SHIFT BITS_12_15_SHIFT /* control operation alignment shift */
#define CNTLOP(v) BITS_12_15(v) /* control operation accessor */
#define MLBOP_MASK BITS_0_3_MASK /* memory/loop/branch operation mask */
#define MLBOP_SHIFT BITS_0_3_SHIFT /* memory/loop/branch operation alignment shift */
#define MLBOP(v) BITS_0_3(v) /* memory/loop/branch operation accessor */
#define FIRMEXTOP_MASK BITS_8_11_MASK /* firmware extension operation mask */
#define FIRMEXTOP_SHIFT BITS_8_11_SHIFT /* firmware extension operation alignment shift */
#define FIRMEXTOP(v) BITS_8_11(v) /* firmware extension operation accessor */
#define FMEXSUBOP_MASK BITS_12_15_MASK /* firmware extension suboperation mask */
#define FMEXSUBOP_SHIFT BITS_12_15_SHIFT /* firmware extension suboperation alignment shift */
#define FMEXSUBOP(v) BITS_12_15(v) /* firmware extension suboperation accessor */
/* Specific instruction accessors */
#define IOOP_K_MASK 0000017 /* I/O K-field mask */
#define IOOP_K_SHIFT 0000000 /* I/O K-field alignment shift */
#define IO_K(v) (((v) & IOOP_K_MASK) >> IOOP_K_SHIFT)
#define X_FLAG 0004000 /* index flag in bit 4 */
#define I_FLAG_BIT_4 0004000 /* indirect flag in bit 4 */
#define I_FLAG_BIT_5 0002000 /* indirect flag in bit 5 */
#define M_FLAG 0001000 /* memory subop flag in bit 6 */
#define START_BIT_MASK 0000360 /* start bit mask for bit field instructions */
#define START_BIT_SHIFT 4 /* start bit alignment shift */
#define START_BIT(v) (((v) & START_BIT_MASK) >> START_BIT_SHIFT)
#define BIT_COUNT_MASK 0000017 /* bit count mask for bit field instructions */
#define BIT_COUNT_SHIFT 0 /* bit count alignment shift */
#define BIT_COUNT(v) (((v) & BIT_COUNT_MASK) >> BIT_COUNT_SHIFT)
#define BIT_POSITION_MASK 0000077 /* bit position mask for bit test instructions */
#define BIT_POSITION_SHIFT 0 /* bit position alignment shift */
#define BIT_POSITION(v) (((v) & BIT_POSITION_MASK) >> BIT_POSITION_SHIFT)
#define SHIFT_COUNT_MASK 0000077 /* shift count mask for shift instructions */
#define SHIFT_COUNT_SHIFT 0 /* shift count alignment shift */
#define SHIFT_COUNT(v) (((v) & SHIFT_COUNT_MASK) >> SHIFT_COUNT_SHIFT)
#define SHIFT_RIGHT_FLAG 0000100 /* shift instructions left/right (0/1) flag */
#define MODE_DISP_MASK 0001777 /* memory-reference mode and displacement mask */
#define MODE_MASK 0001700 /* memory-reference mode mask */
#define MODE_SHIFT 6 /* memory-reference mode alignment shift */
#define DISPL_31_SIGN 0000040 /* sign bit for 0-31 displacements */
#define DISPL_255_SIGN 0000400 /* sign bit for 0-255 displacements */
#define DISPL_31_MASK 0000037 /* mask for 0-31 displacements */
#define DISPL_63_MASK 0000077 /* mask for 0-63 displacements */
#define DISPL_127_MASK 0000177 /* mask for 0-127 displacements */
#define DISPL_255_MASK 0000377 /* mask for 0-255 displacements */
#define DISPL_P_FLAG 0001000 /* P-relative displacement flag */
#define DISPL_DB_FLAG 0000400 /* DB-relative displacement flag */
#define DISPL_QPOS_FLAG 0000200 /* positive Q-relative displacement flag */
#define DISPL_QNEG_FLAG 0000100 /* negative Q-relative displacement flag */
#define IMMED_MASK 0000377 /* mask for immediate values */
#define SDEC2_MASK 0000003 /* two-bit S-decrement mask for move instructions */
#define SDEC3_MASK 0000007 /* three-bit S-decrement mask for move instructions */
#define SDEC_SHIFT 0 /* S-decrement alignment shift */
#define SDEC2(v) (((v) & SDEC2_MASK) >> SDEC_SHIFT)
#define SDEC3(v) (((v) & SDEC3_MASK) >> SDEC_SHIFT)
#define DB_FLAG 0000020 /* PB/DB base flag */
#define MVBW_CCF 0000030 /* MVBW condition code flags */
#define MVBW_N_FLAG 0000020 /* MVBW numeric flag */
#define MVBW_A_FLAG 0000010 /* MVBW alphabetic flag */
#define MVBW_S_FLAG 0000004 /* MVBW upshift flag */
#define MVBW_CCF_SHIFT 6 /* CCF alignment in MVBW instruction */
#define NABS_FLAG 0000100 /* CVDA negative absolute value flag */
#define ABS_FLAG 0000040 /* CVDA absolute value flag */
#define EIS_SDEC_SHIFT 4 /* EIS S-decrement alignment shift */
/* Explicit instruction opcodes and accessors */
#define NOP 0000000 /* no operation */
#define QASR 0015700 /* quadruple arithmetic right shift */
#define DMUL 0020570 /* double integer multiply */
#define DDIV 0020571 /* double integer divide */
#define SED_1 0030041 /* set enable interrupt */
#define HALT_10 0030370 /* halt 10 */
#define MTFDS_MASK 0177730 /* move to/from data segment mask */
#define MTFDS 0020130 /* move to/from data segment */
#define EXIT_MASK 0177400 /* exit procedure mask */
#define EXIT 0031400 /* exit procedure */
#define PAUS_MASK 0177760 /* pause mask */
#define PAUS 0030020 /* pause */
#define BR_MASK 0173000 /* conditional and unconditional branch mask */
#define BR_DBQS_I 0143000 /* branch unconditionally DB/Q/S-relative indirect */
#define BCC 0141000 /* branch conditionally */
#define BCC_CCF_SHIFT 2 /* CCF alignment in BCC instruction */
#define LSDX_MASK 0175000 /* load/store double-word indexed mask */
#define LDD_X 0155000 /* load double-word indexed */
#define STD_X 0165000 /* store double-word indexed */
#define TBR_MASK 0177000 /* test and branch mask */
#define TBA 0050000 /* test and branch, limit in A */
#define MTBA 0052000 /* modify, test and branch, limit in A */
#define TBX 0054000 /* test and branch, limit in X */
#define MTBX 0056000 /* modify, test and branch, limit in X */
/* PSHR/SETR instruction accessors */
#define PSR_RL_MASK 0000001 /* PSHR/SETR register right-to-left mask */
#define PSR_LR_MASK 0000200 /* PSHR/SETR register left-to-right mask */
#define PSR_SBANK 0000200 /* Stack bank register */
#define PSR_DB_DBANK 0000100 /* Data base and data bank registers */
#define PSR_DL 0000040 /* Data limit register */
#define PSR_Z 0000020 /* Stack limit register */
#define PSR_STA 0000010 /* Status register */
#define PSR_X 0000004 /* Index register */
#define PSR_Q 0000002 /* Frame pointer */
#define PSR_S 0000001 /* Stack pointer */
#define PSR_PRIV (PSR_SBANK | PSR_DB_DBANK | PSR_DL | PSR_Z)
/* Reserved memory addresses */
#define CSTB_POINTER 0000000 /* code segment table base pointer */
#define CSTX_POINTER 0000001 /* code segment table extension pointer */
#define DST_POINTER 0000002 /* data segment table pointer */
#define ICS_Q 0000005 /* interrupt control stack marker pointer (QI) */
#define ICS_Z 0000006 /* interrupt control stack limit (ZI) */
#define INTERRUPT_MASK 0000007 /* interrupt mask */
#define SGT_POINTER 0001000 /* system global tables pointer */
/* Code Segment Table accessors */
#define CST_A_BIT 0100000 /* code segment is absent */
#define CST_M_BIT 0040000 /* code segment is privileged */
#define CST_R_BIT 0020000 /* code segment has been referenced flag */
#define CST_T_BIT 0010000 /* code segment is to be traced */
#define CST_SEGLEN_MASK 0007777 /* code segment length mask */
#define CST_BANK_MASK 0000017 /* code segment bank mask */
#define CST_RESERVED 0000300 /* number of CST entries reserved for the system */
/* Data Segment Table accessors */
#define DST_A_BIT 0100000 /* data segment is absent */
#define DST_C_BIT 0040000 /* data segment is clean (not modified) */
#define DST_R_BIT 0020000 /* data segment has been referenced */
#define DST_SEGLEN_MASK 0017777 /* data segment length mask */
#define DST_BANK_MASK 0000017 /* data segment bank mask */
/* Segment Transfer Table accessors */
#define STT_LENGTH_MASK 0000377 /* STT length mask */
#define STT_LENGTH_SHIFT 0 /* STT length alignment shift */
#define STT_LENGTH(l) (((l) & STT_LENGTH_MASK) >> STT_LENGTH_SHIFT)
/* Program label accessors */
#define LABEL_EXTERNAL 0100000 /* external program label flag */
#define LABEL_STTN_MASK 0077400 /* external program label STT number mask */
#define LABEL_SEGMENT_MASK 0000377 /* external program label segment mask */
#define LABEL_STTN_SHIFT 8 /* STT number alignment shift */
#define LABEL_SEGMENT_SHIFT 0 /* segment number alignment shift */
#define LABEL_UNCALLABLE 0040000 /* local program label uncallable flag */
#define LABEL_ADDRESS_MASK 0037777 /* local program label address mask */
#define STT_NUMBER(l) (((l) & LABEL_STTN_MASK) >> LABEL_STTN_SHIFT)
#define STT_SEGMENT(l) (((l) & LABEL_SEGMENT_MASK) >> LABEL_SEGMENT_SHIFT)
#define ISR_SEGMENT 1 /* segment number containing interrupt service routines */
#define LABEL_IRQ (LABEL_EXTERNAL | ISR_SEGMENT) /* label for interrupt requests */
#define LABEL_STTN_MAX (LABEL_STTN_MASK >> LABEL_STTN_SHIFT) /* STT number maximum value */
#define TO_LABEL(s,n) ((s) | (n) << LABEL_STTN_SHIFT)
/* Stack marker accessors */
#define STMK_D 0100000 /* dispatcher flag */
#define STMK_T 0100000 /* trace flag */
#define STMK_M 0040000 /* mapped flag */
#define STMK_RTN_ADDR 0037777 /* PB-relative return address */
/* CPU registers */
extern HP_WORD CIR; /* Current Instruction Register */
extern HP_WORD NIR; /* Next Instruction Register */
extern HP_WORD PB; /* Program Base Register */
extern HP_WORD P; /* Program Counter */
extern HP_WORD PL; /* Program Limit Register */
extern HP_WORD PBANK; /* Program Segment Bank Register */
extern HP_WORD DL; /* Data Limit Register */
extern HP_WORD DB; /* Data Base Register */
extern HP_WORD DBANK; /* Data Segment Bank Register */
extern HP_WORD Q; /* Stack Marker Register */
extern HP_WORD SM; /* Stack Memory Register */
extern HP_WORD SR; /* Stack Register Counter */
extern HP_WORD Z; /* Stack Limit Register */
extern HP_WORD SBANK; /* Stack Segment Bank Register */
extern HP_WORD TR [4]; /* Top of Stack Registers */
extern HP_WORD X; /* Index Register */
extern HP_WORD STA; /* Status Register */
extern HP_WORD SWCH; /* Switch Register */
extern HP_WORD CPX1; /* Run-Mode Interrupt Flags Register */
extern HP_WORD CPX2; /* Halt-Mode Interrupt Flags Register */
extern HP_WORD PCLK; /* Process Clock Register */
extern HP_WORD CNTR; /* Microcode Counter */
/* Top of stack register names */
#define RA TR [0]
#define RB TR [1]
#define RC TR [2]
#define RD TR [3]
/* CPU state */
extern jmp_buf cpu_save_env; /* saved environment for microcode aborts */
extern EXEC_STATE cpu_micro_state; /* micromachine execution state */
extern uint32 cpu_stop_flags; /* set of simulation stop flags */
extern t_bool cpu_base_changed; /* TRUE if any base register has been changed */
extern UNIT cpu_unit; /* CPU unit structure (needed for memory size) */
/* Condition Code B mapping table */
extern const uint16 cpu_ccb_table [256]; /* byte-value to condition-code map */
/* Global CPU functions */
extern void cpu_push (void);
extern void cpu_pop (void);
extern void cpu_queue_up (void);
extern void cpu_queue_down (void);
extern void cpu_flush (void);
extern void cpu_adjust_sr (uint32 target);
extern void cpu_mark_stack (void);
extern void cpu_ea (HP_WORD mode_disp, ACCESS_CLASS *classification, HP_WORD *offset, BYTE_SELECTOR *selector);
extern void cpu_setup_irq_handler (IRQ_CLASS class, HP_WORD parameter);
extern void cpu_setup_ics_irq (IRQ_CLASS class, TRAP_CLASS trap);
extern void cpu_run_mode_interrupt (HP_WORD device_number);
extern void cpu_setup_code_segment (HP_WORD label, HP_WORD* status, HP_WORD *entry_0);
extern void cpu_setup_data_segment (HP_WORD segment_number, HP_WORD *bank, HP_WORD *address);
extern void cpu_call_procedure (HP_WORD label);
extern void cpu_exit_procedure (HP_WORD new_q, HP_WORD new_sm, HP_WORD parameter);
extern void cpu_start_dispatcher (void);
extern void cpu_update_pclk (void);
/* Global CPU instruction execution routines */
extern t_stat cpu_branch_short (t_bool check_loop);
extern HP_WORD cpu_add_16 (HP_WORD augend, HP_WORD addend);
extern HP_WORD cpu_sub_16 (HP_WORD minuend, HP_WORD subtrahend);
extern HP_WORD cpu_mpy_16 (HP_WORD multiplicand, HP_WORD multiplier);
extern t_stat cpu_stack_op (void);
extern t_stat cpu_shift_branch_bit_op (void);
extern t_stat cpu_move_spec_fw_imm_field_reg_op (void);
extern t_stat cpu_io_cntl_prog_imm_mem_op (void);

4003
HP3000/hp3000_cpu_base.c Normal file

File diff suppressed because it is too large Load diff

891
HP3000/hp3000_cpu_fp.c Normal file
View file

@ -0,0 +1,891 @@
/* hp3000_cpu_fp.c: HP 3000 floating-point arithmetic simulator
Copyright (c) 2016, 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
AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the name of the author 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 author.
03-Feb-16 JDB First release version
25-Aug-15 JDB Fixed FSUB zero subtrahend bug (from Norwin Malmberg)
01-Apr-15 JDB Passes the floating point tests in the CPU diagnostic (D420A1)
29-Mar-15 JDB Created
References:
- HP 3000 Series II System Microprogram Listing
(30000-90023, August 1976)
- HP 3000 Series II/III System Reference Manual
(30000-90020, July 1978)
- Machine Instruction Set Reference Manual
(30000-90022, June 1984)
This module implements multiple-precision floating-point operations to
support the HP 3000 CPU instruction set. It employs 64-bit integer
arithmetic for speed and simplicity of implementation. The host compiler
must support 64-bit integers.
HP 3000 computers use a proprietary floating-point format. All 3000s
support two-word "single-precision" floating-point arithmetic as standard
equipment. The original HP 3000 CX and Series I CPUs support three-word
"extended-precision" floating-point arithmetic when the optional HP 30011A
Extended Instruction Set microcode was installed. The Series II and later
machines replace the three-word instructions with four-word "double-
precision" floating-point arithmetic and include the EIS as part of the
standard equipment.
Floating-point numbers have this format:
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| S | exponent biased by +256 | positive mantissa |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| positive mantissa |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| positive mantissa | (extended)
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| positive mantissa | (double)
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
That is, the three- and four-word formats merely extended the mantissa with
no change to the exponent range.
The mantissa is represented in sign-magnitude format. The mantissa is always
positive, with an assumed "1" to the left of the MSB, and the sign bit is set
for negative values. The exponent is in "excess-256" format, i.e.,
represented as an unsigned value biased by +256, giving an unbiased range of
-256 to +255. The binary point is assumed to be between the leading "1" and
the MSB, so a zero value must be handled as a special case of all bits equal
to zero, which otherwise would represent the value +1.0 * 2 ** -256.
Normalization shifts the mantissa left and decrements the exponent until a
"1" bit appears in bit 9.
The use of sign-magnitude format means that floating-point negation merely
complements the sign bit, and floating-point comparison simply checks the
signs and, if they are the same, then applies an integer comparison to the
packed values. However, it also implies the existence of a "negative zero"
value, represented by all zeros except for the sign bit. This value is
undefined; if a negative zero is supplied as an operand to one of the
arithmetic routines, it is treated as positive zero. Negative zero is never
returned even if, e.g., it is supplied as the dividend or multiplier.
This implementation provides add, subtract, multiply, divide, float, and fix
operations on two-, three-, and four-word floating point operands. The
routines are called via a common floating-point executor ("fp_exec") by
supplying the operation to be performed and the operand(s) on which to act.
An operand contains the packed (i.e., in-memory) representation and the
precision of the value. The returned value includes the packed
representation and the precision, along with a value that indicates whether
or not the operation resulted in an arithmetic trap. It is the
responsibility of the caller to take the trap if it is indicated.
*/
#include "hp3000_defs.h"
#include "hp3000_cpu.h"
#include "hp3000_cpu_fp.h"
/* Program constants */
#define EXPONENT_BIAS 256 /* the exponent is biased by +256 */
#define MIN_EXPONENT -256 /* the smallest representable exponent */
#define MAX_EXPONENT +255 /* the largest representable exponent */
#define EXPONENT_MASK 0077700 /* the mask to isolate the exponent in the first word */
#define MANTISSA_MASK 0000077 /* the mask to isolate the mantissa in the first word */
#define EXPONENT_SHIFT 6 /* the exponent alignment shift */
#define MANTISSA_SHIFT 0 /* the mantissa alignment shift */
#define UNPACKED_BITS 54 /* the number of significant bits in the unpacked mantissa */
#define IMPLIED_BIT ((t_uint64) 1 << UNPACKED_BITS) /* the implied MSB in the mantissa */
#define CARRY_BIT ((t_uint64) 1 << UNPACKED_BITS + 1) /* the carry from the MSB in the mantissa */
#define DELTA_ALIGNMENT (D64_WIDTH - UNPACKED_BITS) /* net shift to align the binary point */
/* Floating-point accessors */
#define MANTISSA(w) ((t_uint64) (((w) & MANTISSA_MASK) >> MANTISSA_SHIFT))
#define EXPONENT(w) ((int32) (((w) & EXPONENT_MASK) >> EXPONENT_SHIFT))
#define TO_EXPONENT(w) ((w) + EXPONENT_BIAS << EXPONENT_SHIFT & EXPONENT_MASK)
#define DENORMALIZED(m) (((m) & IMPLIED_BIT) == 0)
/* Floating-point unpacked representation */
typedef struct {
t_uint64 mantissa; /* the unsigned mantissa */
int32 exponent; /* the unbiased exponent */
t_bool negative; /* TRUE if the mantissa is negative */
FP_OPSIZE precision; /* the precision currently expressed by the value */
} FPU;
static const FPU zero = { 0, 0, FALSE, fp_f }; /* an unpacked zero value */
/* Floating-point descriptors */
static const int32 mantissa_bits [] = { /* the number of mantissa bits, indexed by FP_OPSIZE */
16 - 1, /* in_s bits available - sign bit */
32 - 1, /* in_d bits available - sign bit */
22 + 1, /* fp_f bits explicit + bits implied */
38 + 1, /* fp_x bits explicit + bits implied */
54 + 1 /* fp_e bits explicit + bits implied */
};
static const t_uint64 mantissa_mask [] = { /* the mask to the mantissa bits, indexed by FP_OPSIZE */
((t_uint64) 1 << 16) - 1 << 0, /* in_s 16-bit mantissa */
((t_uint64) 1 << 32) - 1 << 0, /* in_d 32-bit mantissa */
((t_uint64) 1 << 22) - 1 << 32, /* fp_f 22-bit mantissa */
((t_uint64) 1 << 38) - 1 << 16, /* fp_x 38-bit mantissa */
((t_uint64) 1 << 54) - 1 << 0 /* fp_e 54-bit mantissa */
};
static const t_uint64 half_lsb [] = { /* half of the LSB for rounding, indexed by FP_OPSIZE */
0, /* in_s not used */
0, /* in_d not used */
(t_uint64) 1 << 31, /* fp_f word 2 LSB */
(t_uint64) 1 << 15, /* fp_x word 3 LSB */
(t_uint64) 1 << 0 /* fp_e word 4 LSB */
};
/* Floating-point local utility routine declarations */
static FPU unpack (FP_OPND packed);
static FP_OPND norm_round_pack (FPU unpacked);
static TRAP_CLASS add (FPU *sum, FPU augend, FPU addend);
static TRAP_CLASS subtract (FPU *difference, FPU minuend, FPU subtrahend);
static TRAP_CLASS multiply (FPU *product, FPU multiplicand, FPU multiplier);
static TRAP_CLASS divide (FPU *quotient, FPU dividend, FPU divisor);
static TRAP_CLASS ffloat (FPU *real, FPU integer);
static TRAP_CLASS fix (FPU *integer, FPU real, t_bool round);
/* Floating-point global routines */
/* Execute a floating-point operation.
The operator specified by the "operation" parameter is applied to the
"left_op" and to the "right_op" (if applicable), and the result is returned.
The "precision" fields of the operands must be set to the representations
stored within before calling this routine.
On entry, the left and right (if needed) operands are unpacked, and the
executor for the specified operation is called. The result is normalized,
rounded, and packed. Any trap condition detected by the operator routine is
set into the packed operand, unless the normalize/round/pack routine detected
its own trap condition. Finally, the packed result is returned.
*/
FP_OPND fp_exec (FP_OPR operator, FP_OPND left_op, FP_OPND right_op)
{
FPU left, right, result;
FP_OPND result_op;
TRAP_CLASS trap;
left = unpack (left_op); /* unpack the left-hand operand */
if (operator <= fp_div) /* if the operator requires two operands */
right = unpack (right_op); /* then unpack the right-hand operation */
switch (operator) { /* dispatch the floating-point operation */
case fp_add:
trap = add (&result, left, right); /* add the two operands */
break;
case fp_sub:
trap = subtract (&result, left, right); /* subtract the two operands */
break;
case fp_mpy:
trap = multiply (&result, left, right); /* multiply the two operands */
break;
case fp_div:
trap = divide (&result, left, right); /* divide the two operands */
break;
case fp_flt:
trap = ffloat (&result, left); /* convert the integer operand to a floating-point number */
break;
case fp_fixr:
trap = fix (&result, left, TRUE); /* round the floating-point operand to an integer */
break;
case fp_fixt:
trap = fix (&result, left, FALSE); /* truncate the floating-point operand to an integer */
break;
default:
result = zero; /* if an unimplemented operation is requested */
result.precision = left.precision; /* then return a zero of the appropriate precision */
trap = trap_Unimplemented; /* and trap for an Unimplemented Instruction */
break;
} /* all cases are handled */
result_op = norm_round_pack (result); /* normalize, round, and pack the result */
if (result_op.trap == trap_None) /* if the pack operation succeeded */
result_op.trap = trap; /* then set any arithmetic trap returned by the operation */
return result_op; /* return the result */
}
/* Floating-point local utility routine declarations */
/* Unpack a packed operand.
A packed integer or floating-point value is split into separate mantissa and
exponent variables. The multiple words of the mantissa are concatenated into
a single 64-bit unsigned value, and the exponent is shifted with recovery of
the sign.
The absolute values of single and double integers are unpacked into the
mantissas and preshifted by 32 or 16 bits, respectively, to reduce the
shifting needed for normalization. The resulting value is unnormalized, but
the exponent is set correctly to reflect the preshift. The precisions for
unpacked integers are set to single-precision but are valid for extended- and
double-precision, as the unpacked representations are identical.
The packed floating-point representation contains an implied "1" bit
preceding the binary point in the mantissa, except if the floating-point
value is zero. The unpacked mantissa includes the implied bit. The bias is
removed from the exponent, producing a signed value, and the sign of the
mantissa is set from the sign of the packed value.
A packed zero value is represented by all words set to zero. In the unpacked
representation, the mantissa is zero, the exponent is the minimum value
(-256), and the sign is always positive (as "negative zero" is undefined).
Implementation notes:
1. Integers could have been copied directly to the mantissa with the
exponents set to the appropriate values (54 in this case). However, the
current implementation unpacks integers only in preparation for repacking
as floating-point numbers i.e., to implement the "float" operator. This
would require a larger number of shifts to normalize the values -- as
many as 54 to normalize the value 1. Preshifting reduces the number of
normalizing shifts needed to between 6 and 22.
*/
static FPU unpack (FP_OPND packed)
{
FPU unpacked;
uint32 word;
switch (packed.precision) { /* dispatch based on the operand precision */
case in_s: /* unpack a single integer */
word = packed.words [0]; /* from the first word */
if (word & D16_SIGN) { /* if the value is negative */
word = NEG16 (word); /* then make it positive */
unpacked.negative = TRUE; /* and set the mantissa sign flag */
}
else /* otherwise the value is positive */
unpacked.negative = FALSE; /* so clear the sign flag */
unpacked.mantissa = (t_uint64) word << 32; /* store the preshifted value as the mantissa */
unpacked.exponent = UNPACKED_BITS - 32; /* and set the exponent to account for the shift */
unpacked.precision = fp_f; /* set the precision */
break;
case in_d: /* unpack a double integer */
word = TO_DWORD (packed.words [0], packed.words [1]); /* from the first two words */
if (word & D32_SIGN) { /* if the value is negative */
word = NEG32 (word); /* then make it positive */
unpacked.negative = TRUE; /* and set the mantissa sign flag */
}
else /* otherwise the value is positive */
unpacked.negative = FALSE; /* so clear the sign flag */
unpacked.mantissa = (t_uint64) word << 16; /* store the preshifted value as the mantissa */
unpacked.exponent = UNPACKED_BITS - 16; /* and set the exponent to account for the shift */
unpacked.precision = fp_f; /* set the precision */
break;
case fp_f: /* unpack a single-precision */
case fp_x: /* extended-precision */
case fp_e: /* or double-precision floating-point number */
unpacked.mantissa = MANTISSA (packed.words [0]); /* starting with the first word */
for (word = 1; word <= 3; word++) { /* unpack from one to three more words */
unpacked.mantissa <<= 16; /* shift the accumulated value */
if (word < TO_COUNT (packed.precision)) /* if all words are not included yet */
unpacked.mantissa |= packed.words [word]; /* then merge the next word into value */
}
unpacked.exponent = /* store the exponent */
EXPONENT (packed.words [0]) - EXPONENT_BIAS; /* after removing the bias */
if (unpacked.exponent == MIN_EXPONENT /* if the biased exponent and mantissa are zero */
&& unpacked.mantissa == 0) /* then the mantissa is positive */
unpacked.negative = FALSE; /* regardless of the packed sign */
else { /* otherwise the value is non-zero */
unpacked.mantissa |= IMPLIED_BIT; /* so add back the implied "1" bit */
unpacked.negative = ((packed.words [0] & D16_SIGN) != 0); /* and set the sign as directed */
}
unpacked.precision = packed.precision; /* set the precision */
break;
} /* all cases are handled */
return unpacked; /* return the unpacked value */
}
/* Normalize, round, and pack an unpacked value.
An unpacked value is normalized, rounded, and packed into the representation
indicated by the operand precision. If the supplied value cannot be
represented, the appropriate trap indication is returned.
A single- or double-integer is packed into the first word or two words of the
result as a twos-complement value. If the value is too large for the result
precision, an Integer Overflow trap is indicated, and a zero value is
returned.
For a real of any precision, the mantissa is first normalized by shifting
right if the carry bit is set, or by shifting left until the implied bit is
set. The exponent is adjusted for any shifts performed. The value is then
rounded by adding one-half of the least-significant bit; if that causes a
carry, the exponent is adjusted again. Finally, the mantissa is masked to
the number of bits corresponding to the desired precision and packed into the
in-memory representation. The exponent is checked, and it exceeds the
permitted range, the appropriate trap indication is returned.
Implementation notes:
1. If a carry occurs due to rounding, the mantissa is not shifted because
the carry bit will be masked off during packing. Incrementing the
exponent in this case is sufficient.
2. Masking the mantissa is required to remove the carry and implied bits
before packing. Masking the value bits in excess of the specified
precision is not required but is desirable to avoid implying more
precision than actually is present.
3. The result value +/-1 x 2 ** -256 is considered an underflow, as the
packed representation is identical to the zero representation, i.e., an
all-zeros value.
*/
static FP_OPND norm_round_pack (FPU unpacked)
{
FP_OPND packed;
int32 integer;
packed.precision = unpacked.precision; /* set the precision */
if (unpacked.mantissa == 0) { /* if the mantissa is zero */
packed.words [0] = 0; /* then set */
packed.words [1] = 0; /* the packed */
packed.words [2] = 0; /* representation to */
packed.words [3] = 0; /* all zeros */
packed.trap = trap_None; /* report that packing succeeded */
}
else if (unpacked.precision <= in_d) /* if packing a single or double integer */
if (unpacked.exponent >= mantissa_bits [unpacked.precision]) { /* then if the value is too large to fit */
packed.words [0] = 0; /* then return */
packed.words [1] = 0; /* a zero value */
packed.trap = trap_Integer_Overflow; /* and an overflow trap */
}
else { /* otherwise */
integer = (int32) /* convert the value to an integer */
(unpacked.mantissa >> UNPACKED_BITS - unpacked.exponent /* by shifting right to align */
& mantissa_mask [unpacked.precision]); /* and masking to the desired precision */
if (unpacked.negative) /* if the value is negative */
integer = - integer; /* then negate the result */
packed.words [0] = UPPER_WORD (integer); /* split the result */
packed.words [1] = LOWER_WORD (integer); /* into the first two words */
packed.trap = trap_None; /* report that packing succeeded */
}
else { /* otherwise a real number is to be packed */
if (unpacked.mantissa & CARRY_BIT) { /* if a carry out of the MSB has occurred */
unpacked.mantissa >>= 1; /* then shift the mantissa to normalize */
unpacked.exponent += 1; /* and increment the exponent to compensate */
}
else /* otherwise */
while (DENORMALIZED (unpacked.mantissa)) { /* while the mantissa is not in normal form */
unpacked.mantissa <<= 1; /* shift the mantissa toward the implied-bit position */
unpacked.exponent -= 1; /* and decrement the exponent to compensate */
}
unpacked.mantissa += half_lsb [unpacked.precision]; /* round the mantissa by adding one-half of the LSB */
if (unpacked.mantissa & CARRY_BIT) /* if rounding caused a carry out of the MSB */
unpacked.exponent = unpacked.exponent + 1; /* then increment the exponent to compensate */
unpacked.mantissa &= mantissa_mask [unpacked.precision]; /* mask the mantissa to the specified precision */
packed.words [0] = (HP_WORD) (unpacked.mantissa >> 48) & DV_MASK /* pack the first word of the mantissa */
| TO_EXPONENT (unpacked.exponent) /* with the exponent */
| (unpacked.negative ? D16_SIGN : 0); /* and the sign bit */
packed.words [1] = (HP_WORD) (unpacked.mantissa >> 32) & DV_MASK; /* pack the second */
packed.words [2] = (HP_WORD) (unpacked.mantissa >> 16) & DV_MASK; /* and third */
packed.words [3] = (HP_WORD) (unpacked.mantissa >> 0) & DV_MASK; /* and fourth words */
if (unpacked.exponent < MIN_EXPONENT /* if the exponent is too small */
|| unpacked.exponent == MIN_EXPONENT && unpacked.mantissa == 0) /* or the result would be all zeros */
packed.trap = trap_Float_Underflow; /* then report an underflow trap */
else if (unpacked.exponent > MAX_EXPONENT) /* otherwise if the exponent is too large */
packed.trap = trap_Float_Overflow; /* then report an overflow trap */
else /* otherwise */
packed.trap = trap_None; /* report that packing succeeded */
}
return packed; /* return the packed value */
}
/* Add two unpacked numbers.
The sum of the two operands is returned. If one operand is zero and the
other is not, the non-zero operand is returned. If both operand are zero, a
"defined zero" is returned in case one or both operands are "negative zeros."
Otherwise, the difference between the operand exponents is determined. If
the magnitude of the difference between the exponents is greater than the
number of significant bits, then the smaller number has been scaled to zero
(swamped), and so the sum is simply the larger operand. However, if the sum
will be significant, the smaller mantissa is shifted to align with the larger
mantissa, and the larger exponent is used (as, after the scaling shift, the
smaller value has the same exponent). Finally, if the operand signs are the
same, the result is the sum of the mantissas. If the signs are different,
then the sum is the smaller value subtracted from the larger value, and the
result adopts the sign of the larger value.
Implementation notes:
1. If the addend is zero, the microcode converts the undefined value
"negative zero" to the defined positive zero if it is passed as the
augend. This also applies to the subtraction operator, which passes a
negative zero if the subtrahend is zero.
*/
static TRAP_CLASS add (FPU *sum, FPU augend, FPU addend)
{
int32 magnitude;
if (addend.mantissa == 0) /* if the addend is zero */
if (augend.mantissa == 0) { /* then if the augend is also zero */
*sum = zero; /* then the sum is (positive) zero */
sum->precision = augend.precision; /* with the appropriate precision */
}
else /* otherwise the augend is non-zero */
*sum = augend; /* so the sum is just the augend */
else if (augend.mantissa == 0) /* otherwise if the augend is zero */
*sum = addend; /* then the sum is just the addend */
else { /* otherwise both operands are non-zero */
magnitude = augend.exponent - addend.exponent; /* so determine the magnitude of the difference */
if (abs (magnitude) > mantissa_bits [augend.precision]) /* if one of the operands is swamped */
if (magnitude > 0) /* then if the addend is smaller */
*sum = augend; /* then return the augend */
else /* otherwise */
*sum = addend; /* return the addend */
else { /* otherwise the addition is required */
sum->precision = addend.precision; /* so set the precision to that of the operands */
if (magnitude > 0) { /* if the addend is smaller */
addend.mantissa >>= magnitude; /* then shift right to align the addend */
sum->exponent = augend.exponent; /* and use the augend's exponent */
}
else { /* otherwise the augend is smaller or the same */
augend.mantissa >>= - magnitude; /* shift right to align the augend */
sum->exponent = addend.exponent; /* and use the addend's exponent */
}
if (addend.negative == augend.negative) { /* if the mantissa signs are the same */
sum->mantissa = addend.mantissa + augend.mantissa; /* then add the mantissas */
sum->negative = addend.negative; /* and use the addend sign for the sum */
}
else if (addend.mantissa > augend.mantissa) { /* otherwise if the addend is larger */
sum->mantissa = addend.mantissa - augend.mantissa; /* then subtract the augend */
sum->negative = addend.negative; /* and use the addend sign */
}
else { /* otherwise the augend is larger */
sum->mantissa = augend.mantissa - addend.mantissa; /* so subtract the addend */
sum->negative = augend.negative; /* and use the augend sign */
}
}
}
return trap_None; /* report that the addition succeeded */
}
/* Subtract two unpacked numbers.
The difference of the two operands is returned. Subtraction is implemented
by negating the subtrahend and then adding the minuend.
Implementation notes:
1. If the subtrahend is zero, negating produces the undefined "negative
zero." However, the "add" routine handles this as positive zero, so we
do not need to worry about this condition.
*/
static TRAP_CLASS subtract (FPU *difference, FPU minuend, FPU subtrahend)
{
subtrahend.negative = ! subtrahend.negative; /* invert the sign of the subtrahend */
add (difference, minuend, subtrahend); /* add to obtain the difference */
return trap_None; /* report that the subtraction succeeded */
}
/* Multiply two unpacked numbers.
The product of the two operands is returned. Conceptually, the
implementation requires a 64 x 64 = 128-bit multiply, rounded to the upper 64
bits. Instead of implementing the FMPY or EMPY firmware algorithm directly,
which uses 16 x 16 = 32-bit partial-product multiplies, it is more efficient
under simulation to use 32 x 32 = 64-bit multiplications by splitting the
operand mantissas ("a" and "b") into 32-bit high and low ("h" and "l") parts
and forming the cross-products:
64-bit operands
ah al
+-------+-------+
bh bl
+-------+-------+
_________________
al * bl [ll]
+-------+-------+
ah * bl [hl]
+-------+-------+
al * bh [lh]
+-------+-------+
ah * bh [hh]
+-------+-------+
_________________________________
64-bit product
+-------+-------+
If either operand is zero, a "defined zero" is returned in case one or both
operands are "negative zeros." Otherwise, the product exponent is set to the
sum of the operand exponents, and the four 64-bit cross-products are formed.
The lower 64 bits of the products are summed to form the carry into the upper
64 bits, which are summed to produce the product. The product mantissa is
aligned, and the product sign is set negative if the operand signs differ.
Mantissas are represented internally as fixed-point numbers with 54 bits to
the right of the binary point. That is, the real number represented is the
integer mantissa value * (2 ** -54), where the right-hand term represents the
delta for a change of one bit. Multiplication is therefore:
(product * delta) = (multiplicand * delta) * (multiplier * delta)
The product is:
product = (multiplicand * multiplier) * (delta * delta) / delta
...which reduces to:
product = multiplicand * multiplier * delta
Multiplying the product by (2 ** -54) is equivalent to right-shifting by 54.
However, using only the top 64 bits of the 128-bit product is equivalent to
right-shifting by 64, so the net correction is a left-shift by 10.
Implementation notes:
1. 32 x 32 = 64-bit multiplies use intrinsic instructions on the IA-32
processor family.
*/
static TRAP_CLASS multiply (FPU *product, FPU multiplicand, FPU multiplier)
{
uint32 ah, al, bh, bl;
t_uint64 hh, hl, lh, ll, carry;
if (multiplicand.mantissa == 0 || multiplier.mantissa == 0) { /* if either operand is zero */
*product = zero; /* then the product is (positive) zero */
product->precision = multiplicand.precision; /* with the appropriate precision */
}
else { /* otherwise both operands are non-zero */
product->precision = multiplicand.precision; /* so set the precision to that of the operands */
product->exponent = multiplicand.exponent /* the product exponent */
+ multiplier.exponent; /* is the sum of the operand exponents */
ah = (uint32) (multiplicand.mantissa >> D32_WIDTH); /* split the multiplicand */
al = (uint32) (multiplicand.mantissa & D32_MASK); /* into high and low double-words */
bh = (uint32) (multiplier.mantissa >> D32_WIDTH); /* split the multiplier */
bl = (uint32) (multiplier.mantissa & D32_MASK); /* into high and low double-words */
hh = ((t_uint64) ah * bh); /* form the */
hl = ((t_uint64) ah * bl); /* four cross products */
lh = ((t_uint64) al * bh); /* using 32 x 32 = 64-bit multiplies */
ll = ((t_uint64) al * bl); /* for efficiency */
carry = ((ll >> D32_WIDTH) + (hl & D32_MASK) /* add the upper half of "ll" to the lower halves of "hl" */
+ (lh & D32_MASK)) >> D32_WIDTH; /* and "lh" and shift to leave just the carry bit */
product->mantissa = hh + (hl >> D32_WIDTH) /* add "hh" to the upper halves of "hl" and "lh" */
+ (lh >> D32_WIDTH) + carry; /* and the carry bit */
product->mantissa <<= DELTA_ALIGNMENT; /* align the result */
product->negative = /* set the product sign negative */
(multiplicand.negative != multiplier.negative); /* if the operand signs differ */
}
return trap_None; /* report that the multiplication succeeded */
}
/* Divide two unpacked numbers.
The quotient of the two operands is returned, and the remainder is discarded.
Conceptually, the implementation requires a 128 / 64 = 64-bit division, with
64 bits of zeros appended to the dividend to get the required precision.
However, instead of implementing the FDIV or EDIV firmware algorithm
directly, which uses 32 / 16 = 16-bit trial divisions, it is more efficient
under simulation to use 64 / 32 = 64-bit divisions with the classic
divide-and-correct method.
This method considers the 64-bit dividend and divisor each to consist of two
32-bit "digits." The 64-bit dividend "ah,al" is divided by the first 32-bit
digit "bh" of the 64-bit divisor "bh,bl", yielding a 64-bit trial quotient
and a 64-bit remainder. A correction is developed by subtracting the product
of the second 32-bit digit "bl" of the divisor and the trial quotient from
the remainder. If the remainder is negative, the trial quotient is too
large, so it is decremented, and the (full 64-bit) divisor is added to the
correction. This is repeated until the correction is non-negative,
indicating that the first quotient digit is correct. The process is then
repeated using the corrected remainder as the dividend to develop the second
64-bit trial quotient and second quotient digit. The first quotient digit is
positioned, and the two quotient digits are then added to produce the final
64-bit quotient. The quotient mantissa is aligned, and the quotient sign is
set negative if the operand signs differ.
Mantissas are represented internally as fixed-point numbers with 54 bits to
the right of the binary point. That is, the real number represented is the
integer mantissa value * (2 ** -54), where the right-hand term represents the
delta for a change of one bit. Division is therefore:
(quotient * delta) = (dividend * delta) / (divisor * delta)
The quotient is:
quotient = (dividend / divisor) * (delta / (delta * delta))
...which reduces to:
quotient = (dividend / divisor) / delta
Dividing the quotient by (2 ** -54) is equivalent to left-shifting by 54.
However, using only the top 64 bits of the 128-bit product is equivalent to
right-shifting by 64, so the net correction is a right-shift by 10.
See "Divide-and-Correct Methods for Multiple Precision Division" by Marvin L.
Stein, Communications of the ACM, August 1964 for background.
Implementation notes:
1. IA-32 processors do not have a 64 / 32 = 64-bit divide instruction (they
have a 64 / 32 = 32 instruction instead). Therefore, a run-time library
routine for 64 / 64 = 64 is generated. Consequently, "bh" and "bl" are
declared as 64-bit variables, as this produces simpler code than if they
were declared as 32-bit variables.
2. "bh" is guaranteed to be non-zero because the divisor mantissa is
normalized on entry. Therefore, no division-by-zero check is needed.
3. The quotient alignment shift logically expresses ((q1 << 32) + q2) >> 10,
but it must be implemented as (q1 << 22) + (q2 >> 10) as otherwise the
left-shift would lose significant bits.
*/
static TRAP_CLASS divide (FPU *quotient, FPU dividend, FPU divisor)
{
t_uint64 bh, bl, q1, q2, r1, r2;
t_int64 c1, c2;
if (divisor.mantissa == 0) { /* if the divisor is zero */
*quotient = dividend; /* then return the dividend */
return trap_Float_Zero_Divide; /* and report the error */
}
else if (dividend.mantissa == 0) { /* otherwise if the dividend is zero */
*quotient = zero; /* then the quotient is (positive) zero */
quotient->precision = dividend.precision; /* with the appropriate precision */
}
else { /* otherwise both operands are non-zero */
quotient->precision = dividend.precision; /* so set the precision to that of the operands */
quotient->exponent = dividend.exponent /* the quotient exponent */
- divisor.exponent; /* is the difference of the operand exponents */
bh = divisor.mantissa >> D32_WIDTH; /* split the divisor */
bl = divisor.mantissa & D32_MASK; /* into high and low halves */
q1 = dividend.mantissa / bh; /* form the first trial quotient */
r1 = dividend.mantissa % bh; /* and remainder */
c1 = r1 - bl * q1; /* form the first corrected remainder */
while (c1 < 0) { /* while a correction is required */
q1 = q1 - 1; /* the first trial quotient is too large */
c1 = c1 + divisor.mantissa; /* so reduce it and increase the remainder */
}
q2 = c1 / bh; /* form the second trial quotient */
r2 = c1 % bh; /* and remainder */
c2 = r2 - bl * q2; /* form the second corrected remainder */
while (c2 < 0) { /* while a correction is required */
q2 = q2 - 1; /* the second trial quotient is too large */
c2 = c2 + divisor.mantissa; /* so reduce it and increase the remainder */
}
quotient->mantissa = (q1 << D32_WIDTH - DELTA_ALIGNMENT) /* sum the quotient digits */
+ (q2 >> DELTA_ALIGNMENT); /* and align the result */
quotient->negative = /* set the quotient sign negative */
(dividend.negative != divisor.negative); /* if the operand signs differ */
}
return trap_None; /* report that the division succeeded */
}
/* Float an integer to a floating-point value.
The integer operand is converted to a floating-point value and returned. The
desired precision of the result must be set before calling.
Conversion is simply a matter of copying the integer value. When the
unpacked value is normalized, it will be converted to floating-point format.
Implementation notes:
1. The incoming integer has already been unpacked into fp_f format, so we do
not need to set the precision here.
*/
static TRAP_CLASS ffloat (FPU *real, FPU integer)
{
*real = integer; /* copy the unpacked value */
return trap_None; /* report that the float succeeded */
}
/* Fix an unpacked floating-point value to an integer.
A floating-point value is converted to a double-word integer. If the "round"
parameter is true, the value is rounded before conversion; otherwise, it is
truncated.
If the real value is less than 0.5, then the integer value is zero.
Otherwise, if rounding is requested, add 0.5 (created by shifting a "1" into
the position immediately to the right of the least significant bit of the
integer result) to the value. Finally, the result precision is set. The
remaining conversion occurs when the result is packed.
Implementation notes:
1. The FIXR/FIXT microcode gives an Integer Overflow for exponent > 30, even
though -2 ** 31 (143700 000000) does fit in the result.
*/
static TRAP_CLASS fix (FPU *integer, FPU real, t_bool round)
{
if (real.exponent < -1) /* if the real value is < 0.5 */
integer->mantissa = 0; /* then the integer value is 0 */
else { /* otherwise the value is convertible */
integer->mantissa = real.mantissa; /* so set the mantissa */
if (round && real.exponent < UNPACKED_BITS) /* if rounding is requested and the value won't overflow */
integer->mantissa += /* then add one-half of the LSB to the value */
(t_uint64) 1 << (UNPACKED_BITS - real.exponent - 1);
}
integer->exponent = real.exponent; /* copy the exponent */
integer->negative = real.negative; /* and sign */
integer->precision = in_d; /* and set to pack to a double integer */
return trap_None; /* report that the fix succeeded */
}

111
HP3000/hp3000_cpu_fp.h Normal file
View file

@ -0,0 +1,111 @@
/* hp3000_cpu_fp.h: HP 3000 floating-point interface declarations
Copyright (c) 2016, 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
AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the name of the author 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 author.
21-Jan-16 JDB First release version
29-Mar-15 JDB Created
This file contains declarations used by the CPU to interface with the
floating-point operations executor.
*/
/* Program constants */
#define SIGN_BIT 0100000
#define EXPONENT_BITS 0077700
#define ASSUMED_BIT 0000100
#define FRACTION_BITS 0000077
/* Operand precisions:
- S = 1-word integer
- D = 2-word integer
- F = 2-word single-precision floating-point
- X = 3-word extended-precision floating-point
- E = 4-word double-precision floating-point
Implementation notes:
1. The ordering of the enumeration constants is important, as we depend on
the "fp" type codes to reflect the number of words used by the packed
representation.
*/
typedef enum {
in_s = 0, /* 1-word integer */
in_d = 1, /* 2-word integer */
fp_f = 2, /* 2-word single-precision floating-point */
fp_x = 3, /* 3-word extended-precision floating-point */
fp_e = 4 /* 4-word double-precision floating-point */
} FP_OPSIZE;
/* Conversion from operand size to word count */
#define TO_COUNT(s) ((uint32) (s + (s < fp_f)))
/* Floating point operations */
typedef enum {
fp_add,
fp_sub,
fp_mpy,
fp_div,
fp_flt,
fp_fixr,
fp_fixt
} FP_OPR;
/* General operand.
An in-memory representation of an integer or packed floating-point number.
An actual value will use one, two, three, or four words, as indicated by the
"precision" specified.
"trap" is significant only for result values; it is ignored for operand
values. A good result will have trap_None.
*/
typedef struct {
HP_WORD words [4]; /* integer or floating-point value */
FP_OPSIZE precision; /* operand size descriptor */
uint32 trap; /* validity of the result */
} FP_OPND;
/* Special operands */
static const FP_OPND FP_NOP = { { 0, 0, 0, 0 }, in_s, trap_None }; /* an unneeded operand */
/* Floating-point global routines */
extern FP_OPND fp_exec (FP_OPR operator, FP_OPND left, FP_OPND right);

200
HP3000/hp3000_cpu_ims.h Normal file
View file

@ -0,0 +1,200 @@
/* hp3000_cpu_ims.h: HP 3000 CPU-to-IOP/MPX/SEL interface declarations
Copyright (c) 2016, 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
AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the name of the author 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 author.
05-Sep-15 JDB First release version
11-Dec-12 JDB Created
This file contains declarations used by the CPU to interface with the HP 3000
I/O Processor, Multiplexer Channel, and Selector Channel.
*/
/* Global data structures */
/* I/O commands.
The enumeration values correspond to the IOP bus IOCMD0-2 signal
representations.
*/
typedef enum {
ioSIN = 0, /* set interrupt */
ioCIO = 1, /* control I/O */
ioSIO = 2, /* start I/O */
ioWIO = 3, /* write I/O */
ioRIN = 4, /* reset interrupt */
ioTIO = 5, /* test I/O */
ioSMSK = 6, /* set interrupt mask */
ioRIO = 7 /* read I/O */
} IO_COMMAND;
/* SIO program orders.
32-bit I/O program words are formed from a 16-bit I/O control word (IOCW) and
a 16-bit I/O address word (IOAW). The Interrupt, Control, Sense, Write, and
Read orders use this format:
0 | 1 2 3 | 4 5 6 | 7 8 9 |10 11 12 |13 14 15
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| C | order | control word 1/word count | IOCW
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| control word 2/status/address | IOAW
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
For the Write and Read orders only, bit 0 of the IOCW is the "data chain"
flag. If it is set, then this transfer is a continuation of the previous
Write or Read transfer.
The Jump, End, Return Residue, and Set Bank orders require an additional bit
(IOCW bit 4) to define their orders fully:
0 | 1 2 3 | 4 5 6 | 7 8 9 |10 11 12 |13 14 15
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| - | order | - - - - - - - - - - - | IOCW
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| address/status/count | IOAW
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| - - - - - - - - - - - - | bank | IOAW
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
In simulation, IOCW bits 0-4 are used to index into a 32-element lookup table
to produce the final I/O order (because some of the orders define IOCW bit 4
as "don't care", there are only thirteen distinct orders).
Implementation notes:
1. The IOCW_COUNT(w) macro sign-extends the 12-bit two's-complement word
count in the IOCW for the Read and Write orders.
2. The sioWRITE, sioWRITEC, sioREAD, and sioREADC enumeration constants must
be contiguous and the final four values, so that a ">= sioWRITE" test
identifies all four cases.
*/
#define IOCW_DC 0100000 /* data chain */
#define IOCW_SIO_MASK 0070000 /* general SIO order mask */
#define IOCW_ORDER_MASK 0174000 /* fully decoded I/O order mask */
#define IOCW_CNTL_MASK 0007777 /* control word mask */
#define IOCW_WCNT_MASK 0007777 /* word count mask */
#define IOAW_BANK_MASK 0000017 /* bank number mask */
#define IOCW_ORDER_SHIFT 11 /* I/O order alignment shift */
#define IOCW_CNTL_SHIFT 0 /* control word alignment shift */
#define IOCW_WCNT_SHIFT 0 /* word count alignment shift */
#define IOAW_BANK_SHIFT 0 /* bank number alignment shift */
#define IOCW_ORDER(w) to_sio_order [((w) & IOCW_ORDER_MASK) >> IOCW_ORDER_SHIFT]
#define IOCW_CNTL(w) (((w) & IOCW_CNTL_MASK) >> IOCW_CNTL_SHIFT)
#define IOCW_WCNT(w) (((w) & IOCW_WCNT_MASK) >> IOCW_WCNT_SHIFT)
#define IOCW_COUNT(w) (- (int32) (~(w) + 1 & IOCW_WCNT_MASK))
#define IOAW_BANK(w) (((w) & IOAW_BANK_MASK) >> IOAW_BANK_SHIFT)
typedef enum {
sioJUMP, /* Jump unconditionally */
sioJUMPC, /* Jump conditionally */
sioRTRES, /* Return residue */
sioSBANK, /* Set bank */
sioINTRP, /* Interrupt */
sioEND, /* End */
sioENDIN, /* End with interrupt */
sioCNTL, /* Control */
sioSENSE, /* Sense */
sioWRITE, /* Write */
sioWRITEC, /* Write chained */
sioREAD, /* Read */
sioREADC /* Read chained */
} SIO_ORDER;
/* Global CPU routine declarations */
extern t_bool cpu_read_memory (ACCESS_CLASS classification, uint32 offset, HP_WORD *value);
extern t_bool cpu_write_memory (ACCESS_CLASS classification, uint32 offset, HP_WORD value);
/* Global SIO order structures.
to_sio_order : translates IOCW bits 1-4 to an SIO_ORDER
sio_order_name : the name of the orders indexed by SIO_ORDER
*/
extern const SIO_ORDER to_sio_order [];
extern const char *const sio_order_name [];
/* Global I/O processor state and functions.
iop_interrupt_request_set : the set of devices requesting interrupts
iop_initialize : initialize the I/O processor
iop_poll : poll the interfaces for an active interrupt request
iop_read_memory : read memory via the module control unit
iop_write_memory : write memory via the module control unit
iop_direct_io : dispatch an I/O command to an interface
*/
extern uint32 iop_interrupt_request_set;
extern uint32 iop_initialize (void);
extern uint32 iop_poll (void);
extern HP_WORD iop_direct_io (HP_WORD device_number, IO_COMMAND io_cmd, HP_WORD write_value);
/* Global multiplexer channel state and functions.
mpx_request_set : the set of pending channel service requests
mpx_initialize : initialize the multiplexer channel
mpx_service : poll the interfaces for an active service request
*/
extern uint32 mpx_request_set;
extern void mpx_initialize (void);
extern void mpx_service (uint32 ticks_elapsed);
/* Global selector channel state and functions
sel_request : TRUE if a pending channel service request
sel_initialize : initialize the selector channel
sel_service : service the interface with an active service request
*/
extern t_bool sel_request;
extern void sel_initialize (void);
extern void sel_service (uint32 ticks_elapsed);

536
HP3000/hp3000_defs.h Normal file
View file

@ -0,0 +1,536 @@
/* hp3000_defs.h: HP 3000 simulator general declarations
Copyright (c) 2016, 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
AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the name of the author 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 author.
04-Feb-16 JDB First release version
11-Dec-12 JDB Created
This file provides the general declarations used throughout the HP 3000
simulator. It is required by all modules.
The author gratefully acknowledges the help of Frank McConnell in answering
questions about the HP 3000.
*/
#ifndef HP3000_DEFS_H_
#define HP3000_DEFS_H_
#include "sim_defs.h"
#include "sim_rev.h"
/* The following pragmas quell clang and Microsoft Visual C++ warnings that are
on by default but should not be, in my opinion. They warn about the use of
perfectly valid code and require the addition of redundant parentheses and
braces to silence them. Rather than clutter up the code with scores of extra
symbols that, in my view, make the code harder to read and maintain, I elect
to suppress these warnings.
VC++ 2008 warning descriptions:
- 4114: "same type qualifier used more than once" [legal per C99]
- 4554: "check operator precedence for possible error; use parentheses to
clarify precedence"
- 4996: "function was declared deprecated"
*/
#if defined (__clang__)
#pragma clang diagnostic ignored "-Wlogical-op-parentheses"
#pragma clang diagnostic ignored "-Wbitwise-op-parentheses"
#pragma clang diagnostic ignored "-Wshift-op-parentheses"
#pragma clang diagnostic ignored "-Wdangling-else"
#elif defined (_MSC_VER)
#pragma warning (disable: 4114 4554 4996)
#endif
/* Device register display mode flags */
#define REG_A (1 << REG_V_UF + 0) /* permit any display */
#define REG_B (1 << REG_V_UF + 1) /* permit binary display */
#define REG_M (1 << REG_V_UF + 2) /* default to instruction mnemonic display */
#define REG_S (1 << REG_V_UF + 3) /* default to status mnemonic display */
/* Register macros.
These additional register definition macros are used to define:
FBDATA -- a one-bit flag in an arrayed register
SRDATA -- an array of bytes large enough to hold a structure
YRDATA -- a binary register
The FBDATA macro defines flag bits that are replicated in the same place in
each element of an array; the array element size is assumed to be the minimum
necessary to hold the bit at the given offset. The SRDATA macro is used
solely to SAVE data stored in a structure so that it may be RESTOREd later.
The YRDATA macro extends the functionality of the ORDATA, DRDATA, and HRDATA
macros to registers with binary (base 2) representation.
Implementation notes:
1. Use caution that multiple FBDATA registers referencing the same array
have offsets that imply the same array element size. For example,
offsets of 3 and 5 can be used with an array of 8-bit elements, and
offsets 13 and 15 can be used with an array of 16-bit elements. However,
offsets 3 and 13 cannot be used, as the first implies 8-bit elements, and
the second implies 16-bit elements.
*/
/* Macro name loc radix width offset depth desc fields */
/* ---------------------- ---- ------- ----- ----- ------ ---------- ---- ------ */
#define FBDATA(nm,loc,ofs,dep) #nm, &(loc), 2, 1, (ofs), (dep), NULL, NULL
#define SRDATA(nm,loc) #nm, &(loc), 8, 8, 0, sizeof loc, NULL, NULL
#define YRDATA(nm,loc,wid) #nm, &(loc), 2, (wid), 0, 1, NULL, NULL
/* Debugging and console output.
"dprintf" is used to write debugging messages. It does an "fprintf" to the
debug output stream if the stream is open and the debug "flag" is currently
enabled in device "dev". Otherwise, it's a NOP. "..." is the format string
and associated values.
"dpprintf" is identical to "dprintf", except that a device pointer is passed
instead of a device structure.
"DPRINTING" and "DPPRINTING" implement the test conditions for device and
device pointer debugging, respectively. They are used explicitly only when
several debug statements employing the same flag are required, and it is
desirable to avoid repeating the stream and flag test for each one.
"cprintf", "cputs", and "cputc" are used to write messages to the console
and, if console logging is enabled, to the log output stream. They do
"(f)printf", "fputs", or "(f)putc", respectively. "..." is the format string
and associated values, "str" is the string to write, and "ch" is the
character to write.
Implementation notes:
1. The "cputs" macro uses "fputs" for both console and log file output
because "puts" appends a newline, whereas "fputs" does not.
*/
#define DPRINTING(d,f) (sim_deb && ((d).dctrl & (f)))
#define DPPRINTING(d,f) (sim_deb && ((d)->dctrl & (f)))
#define dprintf(dev, flag, ...) \
if (DPRINTING (dev, flag)) \
hp_debug (&(dev), (flag), __VA_ARGS__); \
else \
(void) 0
#define dpprintf(dptr, flag, ...) \
if (DPPRINTING (dptr, flag)) \
hp_debug ((dptr), (flag), __VA_ARGS__); \
else \
(void) 0
#define cprintf(...) \
do { \
printf (__VA_ARGS__); \
if (sim_log) \
fprintf (sim_log, __VA_ARGS__); \
} while (0)
#define cputs(str) \
do { \
fputs (str, stdout); \
if (sim_log) \
fputs (str, sim_log); \
} while (0)
#define cputc(ch) \
do { \
putc (ch); \
if (sim_log) \
fputc (ch, sim_log); \
} while (0)
/* Simulation stop codes.
These VM-specific status codes stop the simulator. The "sim_stop_messages"
array in "hp3000_sys.c" contains the message strings that correspond
one-for-one with the stop codes.
Implementation notes:
1. Codes before STOP_RERUN cause the instruction to be rerun, so P is backed
up twice. For codes after, P points to the next instruction to be
executed (which is the current instruction for an infinite loop stop).
*/
#define STOP_SYSHALT 1 /* system halt */
#define STOP_UNIMPL 2 /* unimplemented instruction stop */
#define STOP_UNDEF 3 /* undefined instruction stop */
#define STOP_PAUS 4 /* PAUS instruction stop */
#define STOP_RERUN 4 /* stops above here cause the instruction to be re-run */
#define STOP_HALT 5 /* programmed halt */
#define STOP_BRKPNT 6 /* breakpoint */
#define STOP_INFLOOP 7 /* infinite loop stop */
#define STOP_CLOAD 8 /* cold load complete */
#define STOP_CDUMP 9 /* cold dump complete */
/* Modifier validation identifiers */
#define MTAB_XDV (MTAB_XTD | MTAB_VDV)
#define MTAB_XUN (MTAB_XTD | MTAB_VUN)
#define VAL_DEVNO 0 /* validate DEVNO=0-127 */
#define VAL_INTMASK 1 /* validate INTMASK=0-15/E/D */
#define VAL_INTPRI 2 /* validate INTPRI=0-31 */
#define VAL_SRNO 3 /* validate SRNO=0-15 */
/* I/O event timing.
I/O events are scheduled for future service by specifying the desired delay
in units of event ticks. Typically, one event tick represents the execution
of one CPU instruction, and this is the way event ticks are defined in the
current simulator implementation. However, while the average execution time
of a typical instruction mix on a Series II is given as 2.57 microseconds,
actual instruction times vary greatly, due to the presence of block move and
loop instructions. Variations of an order of magnitude are common, and two
orders or more are possible for longer blocks.
To accommodate possible future variable instruction timing, I/O service
activation times must not assume a constant 2.57 microseconds per event tick.
Delays should be defined in terms of the "uS" (microseconds), "mS"
(milliseconds), and "S" (seconds) macros below.
*/
#define USEC_PER_EVENT 2.57 /* average CPU instruction time in microseconds */
#define uS(t) (uint32) ((t) > USEC_PER_EVENT ? (t) / USEC_PER_EVENT + 0.5 : 1)
#define mS(t) (uint32) (((t) * 1000.0) / USEC_PER_EVENT + 0.5)
#define S(t) (uint32) (((t) * 1000000.0) / USEC_PER_EVENT + 0.5)
/* Architectural constants.
These macros specify the width, sign location, value mask, and minimum and
maximum signed and unsigned values for the data sizes supported by the
simulator. In addition, masks for 16-bit and 32-bit overflow are defined (an
overflow is indicated if the masked bits are not all ones or all zeros).
Implementation notes:
1. The HP_WORD type is a 32-bit unsigned type, instead of the more logical
16-bit unsigned type. This is because IA-32 processors execute
instructions with 32-bit operands much faster than those with 16-bit
operands.
Using 16-bit operands omits the masking required for 32-bit values. For
example, the code generated for the following operations is as follows:
uint16 a, b, c;
a = b + c & 0xFFFF;
movzwl _b, %eax
addw _c, %ax
movw %ax, _a
uint32 x, y, z;
x = y + z & 0xFFFF;
movl _z, %eax
addl _y, %eax
andl $65535, %eax
movl %eax, _x
However, the first case uses operand override prefixes, which require
substantially more time to decode (6 clock cycles vs. 1 clock cycle).
This time outweighs the additional 32-bit AND instruction, which executes
in 1 clock cycle.
On an Intel Core 2 Duo processor, defining HP_WORD as uint16 causes the
HP 3000 memory diagnostic to run about 10% slower.
*/
#define HP_WORD uint32 /* HP 16-bit word representation */
#define R_MASK 0177777u /* 16-bit register mask */
#define D8_WIDTH 8 /* 8-bit data bit width */
#define D8_MASK 0377u /* 8-bit data mask */
#define D8_UMAX 0377u /* 8-bit unsigned maximum value */
#define D8_SMAX 0177u /* 8-bit signed maximum value */
#define D8_SMIN 0200u /* 8-bit signed minimum value */
#define D8_SIGN 0200u /* 8-bit sign */
#define D16_WIDTH 16 /* 16-bit data bit width */
#define D16_MASK 0177777u /* 16-bit data mask */
#define D16_UMAX 0177777u /* 16-bit unsigned maximum value */
#define D16_SMAX 0077777u /* 16-bit signed maximum value */
#define D16_SMIN 0100000u /* 16-bit signed minimum value */
#define D16_SIGN 0100000u /* 16-bit sign */
#define D32_WIDTH 32 /* 32-bit data bit width */
#define D32_MASK 037777777777u /* 32-bit data mask */
#define D32_UMAX 037777777777u /* 32-bit unsigned maximum value */
#define D32_SMAX 017777777777u /* 32-bit signed maximum value */
#define D32_SMIN 020000000000u /* 32-bit signed minimum value */
#define D32_SIGN 020000000000u /* 32-bit sign */
#define D48_WIDTH 48 /* 48-bit data bit width */
#define D48_MASK 07777777777777777uL /* 48-bit data mask */
#define D48_UMAX 07777777777777777uL /* 48-bit unsigned maximum value */
#define D48_SMAX 03777777777777777uL /* 48-bit signed maximum value */
#define D48_SMIN 04000000000000000uL /* 48-bit signed minimum value */
#define D48_SIGN 04000000000000000uL /* 48-bit sign */
#define D64_WIDTH 64 /* 64-bit data bit width */
#define D64_MASK 01777777777777777777777uL /* 64-bit data mask */
#define D64_UMAX 01777777777777777777777uL /* 64-bit unsigned maximum value */
#define D64_SMAX 00777777777777777777777uL /* 64-bit signed maximum value */
#define D64_SMIN 01000000000000000000000uL /* 64-bit signed minimum value */
#define D64_SIGN 01000000000000000000000uL /* 64-bit sign */
#define S16_OVFL_MASK ((uint32) D16_UMAX << D16_WIDTH | \
D16_SIGN) /* 16-bit signed overflow mask */
#define S32_OVFL_MASK ((t_uint64) D32_UMAX << D32_WIDTH | \
D32_SIGN) /* 32-bit signed overflow mask */
/* Memory constants */
#define LA_WIDTH 16 /* logical address bit width */
#define LA_MASK ((1 << LA_WIDTH) - 1) /* logical address mask (2 ** 16 - 1) */
#define LA_MAX ((1 << LA_WIDTH) - 1) /* logical address maximum (2 ** 16 - 1) */
#define BA_WIDTH 4 /* bank address bit width */
#define BA_MASK ((1 << BA_WIDTH) - 1) /* bank address mask (2 ** 4 - 1) */
#define BA_MAX ((1 << BA_WIDTH) - 1) /* bank address maximum (2 ** 4 - 1) */
#define PA_WIDTH (LA_WIDTH + BA_WIDTH) /* physical address bit width */
#define PA_MASK ((1 << PA_WIDTH) - 1) /* physical address mask (2 ** 20 - 1) */
#define PA_MAX ((1 << PA_WIDTH) - 1) /* physical address maximum (2 ** 20 - 1) */
#define DV_WIDTH 16 /* data value bit width */
#define DV_MASK ((1 << DV_WIDTH) - 1) /* data value mask (2 ** 16 - 1) */
#define DV_SIGN (1 << (DV_WIDTH - 1)) /* data value sign (2 ** 15) */
#define DV_UMAX ((1 << DV_WIDTH) - 1) /* data value unsigned maximum (2 ** 16 - 1) */
#define DV_SMAX ((1 << (DV_WIDTH - 1)) - 1) /* data value signed maximum (2 ** 15 - 1) */
/* Memory address macros.
These macros convert between logical and physical addresses. The functions
provided are:
- TO_PA -- merge a bank number and offset into a physical address
- TO_BANK -- extract the bank number part of a physical address
- TO_OFFSET -- extract the offset part of a physical address
Implementation notes:
1. The TO_PA offset parameter is not masked to 16 bits, as this value is
almost always derived from a value that is inherently 16 bits in size. In
the few cases where it is not, explicit masking is required.
*/
#define TO_PA(b,o) (((uint32) (b) & BA_MASK) << LA_WIDTH | (uint32) (o))
#define TO_BANK(p) ((p) >> LA_WIDTH & BA_MASK)
#define TO_OFFSET(p) ((p) & LA_MASK)
/* Portable conversions.
SIMH is written with the assumption that the defined-size types (e.g.,
uint16) are at least the required number of bits but may be larger.
Conversions that otherwise would make inherent size assumptions must instead
be coded explicitly. For example, doing:
negative_value_32 = (int32) negative_value_16;
...will not guarantee that bits 0-15 of "negative_value_32" are ones, whereas
the supplied sign-extension macro will.
The conversions available are:
- SEXT -- int16 sign-extended to int32
- NEG16 -- int16 negated
- NEG32 -- int32 negated
- INT16 -- uint16 to int16
- INT32 -- uint32 to int32
Implementation notes:
1. The routines assume that 16-bit values are masked to exactly 16 bits
before invoking.
*/
#define SEXT(x) (int32) ((x) & D16_SIGN ? (x) | ~D16_MASK : (x))
#define NEG16(x) ((~(x) + 1) & D16_MASK)
#define NEG32(x) ((~(x) + 1) & D32_MASK)
#define INT16(u) ((u) > D16_SMAX ? (-(int16) (D16_UMAX - (u)) - 1) : (int16) (u))
#define INT32(u) ((u) > D32_SMAX ? (-(int32) (D32_UMAX - (u)) - 1) : (int32) (u))
/* Byte accessors.
These macros extract the upper and lower bytes from a word and form a word
from upper and lower bytes. Replacement of a byte within a word is also
provided, as is an enumeration type that defines byte selection.
The accessors are:
- UPPER_BYTE -- return the byte from the upper position of a word value
- LOWER_BYTE -- return the byte from the lower position of a word value
- TO_WORD -- return a word with the specified upper and lower bytes
- REPLACE_UPPER -- replace the upper byte of the word value
- REPLACE_LOWER -- replace the lower byte of the word value
*/
typedef enum {
upper, /* upper byte selected */
lower /* lower byte selected */
} BYTE_SELECTOR;
#define UPPER_BYTE(w) (uint8) ((w) >> D8_WIDTH & D8_MASK)
#define LOWER_BYTE(w) (uint8) ((w) & D8_MASK)
#define TO_WORD(u,l) (HP_WORD) (((u) & D8_MASK) << D8_WIDTH | (l) & D8_MASK)
#define REPLACE_UPPER(w,b) ((w) & D8_MASK | ((b) & D8_MASK) << D8_WIDTH)
#define REPLACE_LOWER(w,b) ((w) & D8_MASK << D8_WIDTH | (b) & D8_MASK)
/* Double-word accessors */
#define UPPER_WORD(d) (HP_WORD) ((d) >> D16_WIDTH & D16_MASK)
#define LOWER_WORD(d) (HP_WORD) ((d) & D16_MASK)
#define TO_DWORD(u,l) ((uint32) (u) << D16_WIDTH | (l))
/* Flip-flops */
typedef enum {
CLEAR = 0, /* the flip-flop is clear */
SET = 1 /* the flip-flop is set */
} FLIP_FLOP;
#define TOGGLE(ff) ff = (ff ^ 1) /* toggle a flip-flop variable */
#define D_FF(b) ((b) != 0) /* use a Boolean expression for a D flip-flop */
/* Bitset formatting.
See the comments at the "fmt_bitset" function (hp3000_sys.c) for details of
the specification of bitset names and format structures.
*/
typedef enum { /* direction of interpretation */
msb_first, /* left-to-right */
lsb_first /* right-to-left */
} BITSET_DIRECTION;
typedef enum { /* alternate names */
no_alt, /* no alternates are present in the name array */
has_alt /* the name array contains alternates */
} BITSET_ALTERNATE;
typedef enum { /* trailing separator */
no_bar, /* omit a trailing separator */
append_bar /* append a trailing separator */
} BITSET_BAR;
typedef const char *const BITSET_NAME; /* a bit name string pointer */
typedef struct { /* bit set format descriptor */
uint32 name_count; /* count of bit names */
BITSET_NAME *names; /* pointer to an array of bit names */
uint32 offset; /* offset from LSB to first bit */
BITSET_DIRECTION direction; /* direction of interpretation */
BITSET_ALTERNATE alternate; /* alternate interpretations presence */
BITSET_BAR bar; /* trailing separator choice */
} BITSET_FORMAT;
/* Bitset format specifier initialization */
#define FMT_INIT(names,offset,dir,alt,bar) \
sizeof (names) / sizeof (names) [0], \
(names), (offset), (dir), (alt), (bar)
/* System interface global data structures */
extern const uint16 odd_parity [256]; /* a table of parity bits for odd parity */
extern const BITSET_FORMAT inbound_format; /* the inbound signal format structure */
extern const BITSET_FORMAT outbound_format; /* the outbound signal format structure */
/* System interface global SCP support routines previously declared in scp.h */
/*
extern t_stat sim_load (FILE *fptr, char *cptr, char *fnam, int flag);
extern t_stat fprint_sym (FILE *ofile, t_addr addr, t_value *val, UNIT *uptr, int32 sw);
extern t_stat parse_sym (char *cptr, t_addr addr, UNIT *uptr, t_value *val, int32 sw);
*/
/* System interface global SCP support routines */
extern t_stat hp_set_dib (UNIT *uptr, int32 code, char *cptr, void *desc);
extern t_stat hp_show_dib (FILE *st, UNIT *uptr, int32 code, void *desc);
/* System interface global utility routines */
extern t_bool hp_device_conflict (void);
extern t_stat fprint_cpu (FILE *ofile, t_value *val, uint32 radix, int32 switches);
extern const char *fmt_status (uint32 status);
extern const char *fmt_char (uint32 charval);
extern const char *fmt_bitset (uint32 bitset, const BITSET_FORMAT bitfmt);
extern void hp_debug (DEVICE *dptr, uint32 flag, ...);
#endif

1564
HP3000/hp3000_diag.txt Normal file

File diff suppressed because it is too large Load diff

1343
HP3000/hp3000_ds.c Normal file

File diff suppressed because it is too large Load diff

363
HP3000/hp3000_io.h Normal file
View file

@ -0,0 +1,363 @@
/* hp3000_io.h: HP 3000 device-to-IOP/MPX/SEL interface declarations
Copyright (c) 2016, 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
AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the name of the author 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 author.
20-Jan-16 JDB First release version
11-Dec-12 JDB Created
This file contains declarations used by I/O devices to interface with the HP
3000 I/O Processor, Multiplexer Channel, and Selector Channel. It is
required by any module that uses Device Information Blocks (DIBs).
*/
/* I/O bus signals.
The INBOUND_SIGNAL and OUTBOUND_SIGNAL declarations mirror the hardware
signals that are received and asserted, respectively, by the I/O interfaces
on the IOP, selector/multiplexer channel, and power buses. A set of one or
more signals forms an INBOUND_SET or OUTBOUND_SET that is sent to or returned
from a device interface. Under simulation, the IOP and channels dispatch one
INBOUND_SET to the target device interface per I/O cycle. The interface
returns a combined OUTBOUND_SET and data value to the caller.
Hardware allows parallel action for concurrent signals. Under simulation, a
"concurrent" set of signals is processed sequentially by the interface in
order of ascending numerical value.
In addition, some signals must be asserted asynchronously, e.g., in response
to an event service call. The IOP and channels provide asynchronous
assertion via function calls for the INTREQ, REQ, SRn, and CHANSR signals.
Implementation notes:
1. The enumerations describe signals. A set of signals normally would be
modeled as an unsigned integer, as a set may contain more than one
signal. However, we define a set as the enumeration, as the "gdb"
debugger has special provisions for an enumeration of discrete bit values
and will display the set in "ORed" form.
2. The null set -- NO_SIGNALS -- cannot be defined as an enumeration
constant because the C language has a single name space for all
enumeration constants, so separate "no inbound signals" and "no outbound
signals" identifiers would be required, and because including them would
require handlers for them in "switch" statements, which is undesirable.
Therefore, we define NO_SIGNALS as an explicit integer 0, so that it is
compatible with both enumerations.
3. Outbound signal values are restricted to the upper 16 bits to allow the
combined signal/data value to fit in 32 bits.
4. Inbound and outbound signal definitions are separated to allow for future
inbound expansion, if necessary.
5. In hardware, the IOP encodes direct I/O commands as a 3-bit IOCMD signal
set on the IOP bus. Each device interface decodes these signals into
individual strobes to control the logic. Under simulation, the IOCMD
values are decoded by the IOP into individual signals for inclusion in
the INBOUND_SIGNAL set that is passed to the interfaces.
6. The ACKSR signal must come before the programmed I/O and TOGGLESR
signals, as they may set an interface's Service Request flip-flop.
7. The READNEXTWD signal must come after PREADSTB, as the former overwrites
the input data word used by the latter.
8. The TOGGLEnXFER signals must come after PREADSTB and PWRITESTB and before
READNEXTWD, so that the strobes can test the interface's Device End
flip-flop before the toggles can reset it.
9. The EOT signal must come after PREADSTB and PWRITESTB and before the
TOGGLEnXFER signals. The former condition is required for the SCMB to
return the correct EOT count, and the latter is required for the DS to
set its End-of-Data flip-flop correctly.
10. The SETINT signal must come before, and the TOGGLESIOOK signal must come
after, the PSTATSTB signal so that the status of the interrupt request
and SIO Busy flip-flops can be reported correctly.
11. The CHANSO signal must come after all programmed I/O signals, as it is
used by channel devices to assert CHANSR when needed.
*/
#define NO_SIGNALS 0 /* a universal "no signals are asserted" value */
typedef enum { /* --- source of signal --- */
DSETINT = 000000000001, /* SIN instruction */
DCONTSTB = 000000000002, /* CIO instruction */
DSTARTIO = 000000000004, /* SIO instruction */
DWRITESTB = 000000000010, /* WIO instruction */
DRESETINT = 000000000020, /* IXIT instruction */
DSTATSTB = 000000000040, /* TIO instruction */
DSETMASK = 000000000100, /* SMSK instruction */
DREADSTB = 000000000200, /* RIO instruction */
ACKSR = 000000000400, /* Multiplexer SR response */
TOGGLESR = 000000001000, /* Read/Write/Control/End order */
SETINT = 000000002000, /* Interrupt/End channel order */
PCMD1 = 000000004000, /* Control channel order */
PCONTSTB = 000000010000, /* Control channel order */
SETJMP = 000000020000, /* Jump channel order */
PSTATSTB = 000000040000, /* Sense channel order */
PWRITESTB = 000000100000, /* Write channel order */
PREADSTB = 000000200000, /* Read channel order */
EOT = 000000400000, /* Read/Write channel order */
TOGGLEINXFER = 000001000000, /* Read channel order */
TOGGLEOUTXFER = 000002000000, /* Write channel order */
READNEXTWD = 000004000000, /* Read channel order */
TOGGLESIOOK = 000010000000, /* End channel order */
DEVNODB = 000020000000, /* Multiplexer DRT Fetch */
INTPOLLIN = 000040000000, /* IOP interrupt poll */
XFERERROR = 000100000000, /* Multiplexer channel abort */
CHANSO = 000200000000, /* Channel service call to interface */
PFWARN = 000400000000 /* SET CPU POWERFAIL */
/* = 001000000000 (available) */
/* = 002000000000 (available) */
/* = 004000000000 (available) */
/* = 010000000000 (available) */
/* = 020000000000 (available) */
} INBOUND_SIGNAL;
typedef INBOUND_SIGNAL INBOUND_SET; /* a set of INBOUND_SIGNALs */
typedef enum { /* --- destination of signal --- */
INTREQ = 000000200000, /* IOP, to request an external interrupt */
INTACK = 000000400000, /* IOP, to acknowledge an external interrupt request */
INTPOLLOUT = 000001000000, /* IOP, to clear an external interrupt request */
DEVEND = 000002000000, /* Channel, to terminate a read/write order */
JMPMET = 000004000000, /* Channel, to enable a Conditional Jump order */
CHANACK = 000010000000, /* Channel, to acknowledge interface call */
CHANSR = 000020000000, /* Selector channel, to request service */
SRn = 000040000000 /* Multiplexer channel, to request service */
/* = 000100000000 (available) */
/* = 000200000000 (available) */
/* = 000400000000 (available) */
/* = 001000000000 (available) */
/* = 002000000000 (available) */
/* = 004000000000 (available) */
/* = 010000000000 (available) */
/* = 020000000000 (available) */
} OUTBOUND_SIGNAL;
typedef OUTBOUND_SIGNAL OUTBOUND_SET; /* a set of OUTBOUND_SIGNALs */
typedef uint32 SIGNALS_DATA; /* a combined outbound signal set and data value */
/* I/O macros.
The following macros are useful in device interface signal handlers and unit
service routines. The parameter definition symbols employed are:
P = a priority set value
S = an INBOUND_SET or OUTBOUND_SET value
L = an INBOUND_SIGNAL value
D = an outbound 16-bit data value
C = a SIGNALS_DATA value
B = a DIB value
A priority set is an unsigned value, where each bit represents an assertion
of some nature (e.g., I/O signals, interrupt requests, etc.), and the
position of the bit represents its priority, which increases from LSB to MSB.
The IOPRIORITY macro isolates the highest-priority bit from the set. It does
this by ANDing the value with its two's complement; only the lowest-order bit
will differ. For example (bits are numbered here from the LSB):
priority set : ...0 0 1 1 0 1 0 0 0 0 0 0 (bits 6, 8, and 9 are asserted)
one's compl : ...1 1 0 0 1 0 1 1 1 1 1 1
two's compl : ...1 1 0 0 1 1 0 0 0 0 0 0
ANDed value : ...0 0 0 0 0 1 0 0 0 0 0 0 (bit 6 is highest priority)
If the request set indicates requests by 0 bits, rather than 1 bits, the
IOPRIORITY macro must be called with the one's complement of the bits.
The IONEXTSIG macro isolates the next inbound signal in sequence to process
from the inbound signal set S.
The IOCLEARSIG macro removes the processed signal L from the inbound signal
set S.
The IORETURN macro forms the 32-bit combined outbound signal set and data
value to be returned by an interface from the signal set S and the 16-bit
data value D.
The IOSIGNALS macro isolates the outbound signal set from a 32-bit combined
status and data value value C.
The IODATA macro isolates the 16-bit data value from a 32-bit combined signal
set and data value value C.
Implementation notes:
1. The IOPRIORITY macro implements two's complement explicitly, rather than
using a signed negation, to be compatible with machines using a
sign-magnitude integer format. "gcc" and "clang" optimize the complement
and add to a single NEG instruction on x86 machines.
*/
#define IOPRIORITY(P) ((P) & ~(P) + 1)
#define IONEXTSIG(S) ((INBOUND_SIGNAL) IOPRIORITY (S))
#define IOCLEARSIG(S,L) S ^= (L)
#define IORETURN(S,D) ((SIGNALS_DATA) ((S) & ~D16_MASK | (D) & D16_MASK))
#define IOSIGNALS(C) ((OUTBOUND_SET) ((C) & ~D16_MASK))
#define IODATA(C) ((uint16) ((C) & D16_MASK))
/* I/O structures.
The Device Information Block (DIB) allows devices to be relocated in the
machine's I/O space. Each DIB contains a pointer to the device controller
interface routine, values corresponding to hardware jumpers on the controller
(e.g., device number), and flip-flops that indicate the interrupt and channel
service states.
For fast access during I/O, interrupt, and channel service requests, devices
are accessed via indexed tables. The index employed depends on the
application. For example, I/O commands are routed via a table that is
indexed by device number. The tables are built during the instruction
execution prelude by scanning the DIBs of all devices and placing pointers to
the DIBs into the tables at the entries associated with the index values.
Between execution runs, the user may reassign device properties, so the
tables must be rebuilt each time.
Implementation notes:
1. The device number (DEVNO) bus is eight bits in width, and the CPU
microcode, the IOP, and the device controllers all support device numbers
up to 255. However, MPE limits the size of the device reference table to
correspond with a device number of 127, while the CPU reserves memory
that would correspond to device numbers 0-2. As a result, most device
controllers provide only seven-bit configurable device numbers. One
exception is the Selector Channel Maintenance Board. The Selector
Channel diagnostic tests programmable device numbers > 127, which the
SCMB provides via bits 8-15 of the counter/buffer register, although only
seven preset jumpers are provided to set the standard device number for
the board.
2. The device_number, service_request_number, and interrupt_priority fields
could be smaller than the defined 32-bit sizes, but IA-32 processors
execute instructions with 32-bit operands much faster than those with
16- or 8-bit operands.
*/
#define DEVNO_MAX 127 /* the maximum device number */
#define DEVNO_MASK 0177 /* the mask for the device number */
#define DEVNO_BASE 10 /* the radix for the device number */
#define DEVNO_UNUSED D32_UMAX /* the unused device number indicator */
#define INTMASK_MAX 15 /* the maximum interrupt mask number */
#define INTMASK_MASK 017 /* the mask for the interrupt mask number */
#define INTMASK_BASE 10 /* the radix for the interrupt mask number */
#define INTMASK_D 0000000 /* the interrupt mask disabled always value */
#define INTMASK_E 0177777 /* the interrupt mask enabled always value */
#define INTMASK_UNUSED D32_UMAX /* the unused interrupt mask indicator */
#define INTPRI_MAX 31 /* the maximum interrupt priority */
#define INTPRI_MASK 037 /* the mask for the interrupt priority */
#define INTPRI_BASE 10 /* the radix for the interrupt priority */
#define INTPRI_UNUSED D32_UMAX /* the unused interrupt priority indicator */
#define SRNO_MAX 15 /* the maximum service request number */
#define SRNO_MASK 017 /* the mask for the service request number */
#define SRNO_BASE 10 /* the radix for the service request number */
#define SRNO_UNUSED D32_UMAX /* the unused service request number indicator */
typedef struct dib DIB; /* an incomplete definition */
typedef SIGNALS_DATA CNTLR_INTRF /* the I/O device controller interface function prototype */
(DIB *dibptr, /* a pointer to the device information block */
INBOUND_SET inbound_signals, /* a set of inbound signals */
uint16 inbound_value); /* a 16-bit inbound value */
struct dib { /* the Device Information Block */
CNTLR_INTRF *io_interface; /* the controller I/O interface function pointer */
uint32 device_number; /* the device number 0-255 */
uint32 service_request_number; /* the service request number 0-15 */
uint32 interrupt_priority; /* the interrupt priority 0-31 */
uint32 interrupt_mask; /* the interrupt mask (16 bits) */
uint32 card_index; /* the card index if multiple interfaces are supported */
FLIP_FLOP interrupt_request; /* an interrupt has been requested */
FLIP_FLOP interrupt_active; /* an interrupt is active */
t_bool service_request; /* channel service has been requested */
};
/* Calibrated timer numbers */
#define TMR_PCLK 0 /* the CPU process clock timer */
#define TMR_CLK 1 /* the CLK system clock timer */
#define TMR_ATC 2 /* the ATC input polling timer */
/* CPU front panel command identifiers */
typedef enum {
Run, /* a run request */
Cold_Load, /* a cold load request */
Cold_Dump /* a cold dump request */
} PANEL_TYPE;
/* Global CPU state and functions */
extern UNIT *cpu_pclk_uptr; /* pointer to the process clock unit */
extern t_bool cpu_is_calibrated; /* TRUE if the process clock is calibrated */
extern void cpu_front_panel (HP_WORD switch_reg, /* set the front panel switches as directed */
PANEL_TYPE request);
/* Global asynchronous signal assertion functions */
extern void iop_assert_INTREQ (DIB *dib_pointer); /* assert the interrupt request signal */
extern void mpx_assert_REQ (DIB *dib_pointer); /* assert the multiplexer channel request signal */
extern void mpx_assert_SRn (DIB *dib_pointer); /* assert the multiplexer channel service request signal */
extern void sel_assert_REQ (DIB *dib_pointer); /* assert the selector channel request signal */
extern void sel_assert_CHANSR (DIB *dib_pointer); /* assert the selector channel service request signal */
/* Global channel state */
extern t_bool mpx_is_idle; /* TRUE if the multiplexer channel is idle */
extern t_bool sel_is_idle; /* TRUE if the selector channel is idle */
/* Global ATC state */
extern t_bool atc_is_polling; /* TRUE if the ATC is polling for the simulation console */
/* Global CLK functions */
extern void clk_update_counter (void); /* update the system clock counter register */

844
HP3000/hp3000_iop.c Normal file
View file

@ -0,0 +1,844 @@
/* hp3000_iop.c: HP 3000 30003B I/O Processor simulator
Copyright (c) 2016, 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
AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the name of the author 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 author.
IOP HP 3000 Series III I/O Processor
28-Aug-15 JDB First release version
11-Dec-12 JDB Created
References:
- HP 3000 Series II/III System Reference Manual
(30000-90020, July 1978)
- HP 3000 Series III Engineering Diagrams Set
(30000-90141, April 1980)
The HP 30003B I/O Processor is an integral part of the HP 3000 system. It
works in conjunction with the CPU and Multiplexer Channel to service the
device interfaces. All I/O interfaces are connected to the IOP bus, which
transfers programmed I/O orders to the interfaces and handles memory reads
and writes between the interfaces and the CPU stack. In addition, it
provides the memory interface for multiplexer channel transfers and fetches
I/O program orders from main memory for the channel.
Interrupt requests are serviced by the IOP, which asserts an external
interrupt signal to the CPU. Device controllers request interrupts via the
IOP, which prioritizes the requests and grants service to the
highest-priority interrupt. While that interrupt is active, lower-priority
requests are held off until it becomes inactive, whereupon the next
highest-priority request is granted. The device number of the interrupting
device is stored in the IOP's address register; this is used by the CPU
microcode to access the proper entry in the Device Reference Table, which
contains the starting address of the I/O handler.
In hardware, a device requests an interrupt by asserting INTREQ to the IOP.
In response, the IOP polls the interfaces by asserting INTPOLLIN to determine
the highest-priority request. The INTPOLLIN and INTPOLLOUT signals are
daisy-chained between interfaces, with the position of the interface in the
chain establishing its priority. Interfaces that are not requesting or
processing interrupts pass INTPOLLIN to INTPOLLOUT. The first interface in
the chain that has an interrupt request pending will inhibit INTPOLLOUT and
will set its Interrupt Active flip-flop. As long as the interrupt is active,
an interface will break the poll chain by denying INTPOLLOUT. This holds off
requests from lower-priority devices.
To avoid scanning each interface's DIB for interrupt requests, the IOP
simulator maintains two 32-bit vectors: a global "iop_interrupt_request_set"
and a local "interrupt_poll_set". Each bit is associated with an interrupt
priority number from 0-31. The bits of the request set indicate which
interfaces are requesting interrupts, and the bits of the poll set indicate
which interfaces will break the poll chain when they are polled. The lowest
set bit in each indicates the highest-priority interrupting device and the
highest-priority device handler currently executing, respectively. An
interface requests an interrupt by asserting INTREQ to the IOP. The IOP then
sets the request and poll bits corresponding to that interface's interrupt
priority number. The CPU checks the request set periodically to determine if
an external interrupt is present.
A device's DIB (Device Information Block) contains three values that pertain
to interrupts: the "interrupt_priority" value determines which bit is set or
cleared in the bit vectors, the "interrupt_request" flip-flop indicates that
the interface is requesting an interrupt from the IOP, and the
"interrupt_active" flip-flop indicates that the device's interrupt handler is
executing. The two flip-flop values indicate one of four possible interrupt
states that are reflected in the associated bit of the bit vectors:
Interrupt Interrupt Request Poll
Request Active Set Set Interrupt State
--------- --------- ------- ---- ------------------------------------
CLEAR CLEAR 0 0 Not interrupting
SET CLEAR 1 1 Interrupt requested
CLEAR SET 0 1 Interrupt acknowledged
SET SET 1 1 Interrupt requested while in handler
The "requested" state corresponds to the device interface asserting the
INTREQ signal to the IOP. The "acknowledged" state corresponds to the IOP
conducting a poll via INTPOLL IN and INTPOLL OUT, and the device interface
responding by inhibiting INTPOLL OUT and asserting INTACK to the IOP.
If both the request and active flip-flops are set, the device has requested a
second interrupt while the first is still being processed. The ATC TDI, for
example, does this when a CIO is issued to acknowledge an interrupt before
the IXIT sends an ioRIN to the interface to reset the active flip-flop.
Device interfaces maintain the states of their interrupt flip-flops for the
benefit of IOP initialization. During the instruction execution prelude, the
IOP will reconstruct its bit vectors from the DIB values. Thereafter, the
interfaces change their interrupt states in response to signals, and the IOP
adjusts the bit vectors as needed. The only direct interaction needed is an
"iop_assert_INTREQ" call from the device interface when an interrupt is
initially requested.
The IOP does not have a programmable interface. It is manipulated directly
by the CPU microcode to issue direct I/O commands to the device interfaces,
and by the multiplexer channel to transfer data and I/O programs to and from
memory.
Direct I/O instructions are sent via the IOP Bus to all device interfaces.
When executing I/O instruc tions, the CPU microcode writes a 16-bit command
word to the IOP, which then places bits 5-7 of that word onto the IOP Bus as
IOCMD0-2 as follows:
CPU Command IOCMD Generated Action
Instruction Word 0 1 2 Signal Performed
----------- ------- ----- --------- --------------------------------
SIN 100000 0 0 0 DSETINT Set interrupt request flip-flop
CIO 100400 0 0 1 DCONTSTB Write a control word
SIO 101000 0 1 0 DSTARTIO Start a channel program
WIO 101400 0 1 1 DWRITESTB Write a data word
IXIT 102000 1 0 0 DRESETINT Reset interrupt active flip-flop
TIO 102400 1 0 1 DSTATSTB Read a status word
SMSK 103000 1 1 0 DSETMASK Set the interrupt mask flip-flop
RIO 103400 1 1 1 DREADSTB Read a data word
The SIO instruction sends an SIO IOCMD to the device interface via the IOP
to begin execution of a channel program. The program consists of two-word
programmed I/O orders, with each pair consisting of an I/O Control Word and
an I/O Address Word. They are encoded as follows:
IOCW IOCW IOAW Generated Action
0 1 2 3 4-15 0-15 Signal Performed
------- -------------- -------------- ----------- ---------------------
0 0 0 0 0 XXXXXXXXXXX Jump Address -- Unconditional Jump
0 0 0 0 1 XXXXXXXXXXX Jump Address SETJMP Conditional Jump
0 0 0 1 0 XXXXXXXXXXX Residue Count -- Return Residue
0 0 0 1 1 XXXXXXXXXXX Bank Address -- Set Bank
0 0 1 0 X XXXXXXXXXXX --- XXXXXX --- SETINT Interrupt
0 0 1 1 0 XXXXXXXXXXX Status Value TOGGLESIOOK End without Interrupt
0 0 1 1 1 XXXXXXXXXXX Status Value SETINT End with Interrupt
0 1 0 0 Control Word 1 Control Word 2 PCONTSTB Control
0 1 0 1 X XXXXXXXXXXX Status Value PSTATSTB Sense
C 1 1 0 Neg Word Count Write Address PWRITESTB Write
C 1 1 1 Neg Word Count Read Address PREADSTB Read
The "Unconditional Jump," "Return Residue," and "Set Bank" orders are
executed entirely by the channel and are not sent to the device interface.
The IOP simulator provides the capability to trace direct I/O commands and
interrupt requests, as well as memory accesses made on behalf of the
multiplexer channel. Devices that periodically interrupt, such as the system
clock, may generate a large number of trace events. To accommodate this, a
filter may be applied to remove trace events from devices that are not of
interest.
*/
#include "hp3000_defs.h"
#include "hp3000_cpu.h"
#include "hp3000_cpu_ims.h"
#include "hp3000_io.h"
/* Program constants */
#define TRACE_ALL D32_UMAX /* enable tracing of all devices */
/* Debug flags.
The FILTER macro tests if the supplied device number is to be filtered out of
the trace stream. It returns the bit in the filter array corresponding to
the device number. If the bit is set, the trace will be generated;
otherwise, it will be suppressed.
Implementation notes:
1. Bit 0 is reserved for the memory data trace flag.
*/
#define DEB_DIO (1 << 1) /* trace direct I/O commands */
#define DEB_IRQ (1 << 2) /* trace interrupt requests */
#define FILTER(d) (1 << (d) % 32 & filter [(d) / 32])
/* IOP global data structures */
const SIO_ORDER to_sio_order [] = { /* translation of IOCW bits 0-4 to SIO_ORDER */
sioJUMP, /* 00000 = Jump unconditionally */
sioJUMPC, /* 00001 = Jump conditionally */
sioRTRES, /* 00010 = Return residue */
sioSBANK, /* 00011 = Set bank */
sioINTRP, /* 00100 = Interrupt */
sioINTRP, /* 00101 = Interrupt */
sioEND, /* 00110 = End */
sioENDIN, /* 00111 = End with interrupt */
sioCNTL, /* 01000 = Control */
sioCNTL, /* 01001 = Control */
sioSENSE, /* 01010 = Sense */
sioSENSE, /* 01011 = Sense */
sioWRITE, /* 01100 = Write */
sioWRITE, /* 01101 = Write */
sioREAD, /* 01110 = Read */
sioREAD, /* 01111 = Read */
sioJUMP, /* 10000 = Jump unconditionally */
sioJUMPC, /* 10001 = Jump conditionally */
sioRTRES, /* 10010 = Return residue */
sioSBANK, /* 10011 = Set bank */
sioINTRP, /* 10100 = Interrupt */
sioINTRP, /* 10101 = Interrupt */
sioEND, /* 10110 = End */
sioENDIN, /* 10111 = End with interrupt */
sioCNTL, /* 11000 = Control */
sioCNTL, /* 11001 = Control */
sioSENSE, /* 11010 = Sense */
sioSENSE, /* 11011 = Sense */
sioWRITEC, /* 11100 = Write Chained */
sioWRITEC, /* 11101 = Write Chained */
sioREADC, /* 11110 = Read Chained */
sioREADC /* 11111 = Read Chained */
};
const char *const sio_order_name [] = { /* indexed by SIO_ORDER */
"Jump", /* sioJUMP */
"Conditional Jump", /* sioJUMPC */
"Return Residue", /* sioRTRES */
"Set Bank", /* sioSBANK */
"Interrupt", /* sioINTRP */
"End", /* sioEND */
"End with Interrupt", /* sioENDIN */
"Control", /* sioCNTL */
"Sense", /* sioSENSE */
"Write", /* sioWRITE */
"Write Chained", /* sioWRITEC */
"Read", /* sioREAD */
"Read Chained" /* sioREADC */
};
/* Global IOP state */
uint32 iop_interrupt_request_set = 0; /* the set of interfaces requesting interrupts */
/* Local IOP state */
static uint32 IOA = 0; /* I/O Address Register */
static uint32 interrupt_poll_set = 0; /* the set of interfaces breaking the poll chain */
static DIB *devs [DEVNO_MAX + 1]; /* index by device number for I/O instruction dispatch */
static DIB *irqs [INTPRI_MAX + 1]; /* index by interrupt priority number for interrupt requests */
static uint32 filter [4] = { /* filter bitmap for device numbers 0-127 */
TRACE_ALL,
TRACE_ALL,
TRACE_ALL,
TRACE_ALL
};
/* IOP local SCP support routines */
static t_stat iop_set_filter (UNIT *uptr, int32 value, char *cptr, void *desc);
static t_stat iop_show_filter (FILE *st, UNIT *uptr, int32 value, void *desc);
/* IOP SCP data structures */
/* Unit list */
static UNIT iop_unit [] = { /* a dummy unit to satisfy SCP requirements */
{ UDATA (NULL, 0, 0) }
};
/* Register list.
Implementation notes:
1. The "interrupt_poll_set", "devs", and "irqs" variables need not be SAVEd
or RESTOREd, as they are rebuilt during the instruction execution
prelude.
*/
static REG iop_reg [] = {
/* Macro Name Location Width Flags */
/* ------ ------ -------- ----- ------- */
{ ORDATA (IOA, IOA, 8), REG_RO }, /* I/O Address Register */
{ SRDATA (FILTER, filter), REG_HRO },
{ NULL }
};
/* Modifier list */
static MTAB iop_mod [] = {
/* Entry Flags Value Print String Match String Validation Display Descriptor */
/* ------------------- ----- ------------ ------------ --------------- ---------------- ---------- */
{ MTAB_XDV | MTAB_NMO, 1, "FILTER", "FILTER", &iop_set_filter, &iop_show_filter, NULL },
{ MTAB_XDV | MTAB_NMO, 0, "", "NOFILTER", &iop_set_filter, NULL, NULL },
{ 0 }
};
/* Debugging trace list */
static DEBTAB iop_deb [] = {
{ "DIO", DEB_DIO }, /* direct I/O commands issued */
{ "IRQ", DEB_IRQ }, /* interrupt requests received */
{ "DATA", DEB_MDATA }, /* I/O data accesses to memory */
{ NULL, 0 }
};
/* Device descriptor */
DEVICE iop_dev = {
"IOP", /* device name */
iop_unit, /* unit array */
iop_reg, /* register array */
iop_mod, /* modifier array */
1, /* number of units */
8, /* address radix */
PA_WIDTH, /* address width */
1, /* address increment */
8, /* data radix */
DV_WIDTH, /* data width */
NULL, /* examine routine */
NULL, /* deposit routine */
NULL, /* reset routine */
NULL, /* boot routine */
NULL, /* attach routine */
NULL, /* detach routine */
NULL, /* device information block pointer */
DEV_DEBUG, /* device flags */
0, /* debug control flags */
iop_deb, /* debug flag name array */
NULL, /* memory size change routine */
NULL /* logical device name */
};
/* IOP global routines */
/* Initialize the I/O processor.
This routine is called in the instruction prelude to set up the IOP data
structures prior to beginning execution. It sets up two tables of DIB
pointers -- one indexed by device number, and a second indexed by interrupt
request number. This allows fast access to the device interface routine by
the direct I/O instruction and interrupt poll processors, respectively.
It also sets the interrupt request and poll bit vectors from the interrupt
flip-flop values in the device DIBs and clears the external interrupt flag if
there are no devices with active interrupts (as the user may have set the
flag or reset the interrupting device during a simulation stop).
*/
uint32 iop_initialize (void)
{
const DEVICE *dptr;
DIB *dibptr;
uint32 i, irq;
iop_interrupt_request_set = 0; /* set all interrupt requests inactive */
interrupt_poll_set = 0; /* set all poll continuity bits inactive */
memset (devs, 0, sizeof devs); /* clear the device number table */
memset (irqs, 0, sizeof irqs); /* and the interrupt request table */
for (i = 0; sim_devices [i] != NULL; i++) { /* loop through all of the devices */
dptr = sim_devices [i]; /* get a pointer to the device */
dibptr = (DIB *) dptr->ctxt; /* and to that device's DIB */
if (dibptr && !(dptr->flags & DEV_DIS)) { /* if the DIB exists and the device is enabled */
if (dibptr->device_number != DEVNO_UNUSED) /* then if the device number is valid */
devs [dibptr->device_number] = dibptr; /* then set the DIB pointer into the device dispatch table */
if (dibptr->interrupt_priority != INTPRI_UNUSED) { /* if the interrupt priority is valid */
irqs [dibptr->interrupt_priority] = dibptr; /* then set the pointer into the interrupt dispatch table */
irq = 1 << dibptr->interrupt_priority; /* get the associated interrupt request bit */
if (dibptr->interrupt_request) { /* if the interface is requesting an interrupt */
iop_interrupt_request_set |= irq; /* then set the request bit */
interrupt_poll_set |= irq; /* and the poll bit */
}
else if (dibptr->interrupt_active) /* otherwise if the interface has acknowledged an interrupt */
interrupt_poll_set |= irq; /* then just set the poll bit */
}
}
}
if (interrupt_poll_set == 0 || IOA == 0) /* if no device has an active interrupt in progress */
CPX1 &= ~cpx1_EXTINTR; /* then clear the interrupt flag */
return IOA;
}
/* Poll the interfaces for an active interrupt request.
This routine is called in the instruction loop when the request set indicates
that one or more interrupt requests are pending. It polls the interface
asserting the highest-priority request. If the interface acknowledges the
interrupt, the routine sets the "external interrupt" bit in the CPU's CPX1
register to initiate interrupt processing, sets the IOP's IOA register to the
the device number of the interrupting device, and returns that value to the
caller.
In hardware, an interface requesting an interrupt with its Interrupt Mask
flip-flop set will assert a common INTREQ to the IOP. In response, the IOP
polls the interfaces to determine the highest-priority request by asserting
INTPOLLIN. The INTPOLLIN and INTPOLLOUT signals are daisy-chained between
interfaces, with the position of the interface in the chain establishing its
priority. Interfaces that are not requesting interrupts pass INTPOLLIN to
INTPOLLOUT. The first interface in the chain that has its Interrupt Request
flip-flop set will inhibit INTPOLLOUT, set its Interrupt Active flip-flop,
and assert INTACK to the IOP.
To avoid polling interfaces in simulation, an interface will set the
Interrupt Request flip-flop in its DIB and then call iop_assert_INTREQ. That
routine sets the request set and poll set bits corresponding to the interrupt
priority value in the DIB.
In the instruction execution loop, if external interrupts are enabled (i.e.,
the I bit in the status word is set), and iop_interrupt_request_set has one
or more bits set, this routine is called to select the interrupt request to
service.
The end of priority chain is marked by the highest-priority (lowest-order)
poll bit that is set. When a poll is performed, a priority mask is generated
that contains just the highest-priority bit. The device corresponding to
that bit will then be the recipient of the current interrupt acknowledgement
cycle. After the interrupt request has been cleared, the poll bit will
prevent lower-priority interrupts from being serviced.
For example:
poll set : ...0 0 1 0 0 1 0 0 0 0 0 0 (poll denied at INTPRI 6 and 9)
priority mask : ...0 0 0 0 0 1 0 0 0 0 0 0 (poll stops at INTPRI 6)
The request is then ANDed with the priority mask to determine if a request is
to be granted:
pri mask : ...0 0 0 0 0 1 0 0 0 0 0 0 (allowed interrupt source)
request set : ...0 0 1 0 0 1 0 0 0 0 0 0 (devices requesting interrupts)
ANDed value : ...0 0 0 0 0 1 0 0 0 0 0 0 (request to grant = INTPRI 6)
Once the interrupt request has been cleared, the poll state is:
poll set : ...0 0 1 0 0 1 0 0 0 0 0 0 (poll denied at INTPRI 6 and 9)
priority mask : ...0 0 0 0 0 1 0 0 0 0 0 0 (poll stops at INTPRI 6)
request set : ...0 0 1 0 0 0 0 0 0 0 0 0 (devices requesting interrupts)
ANDed value : ...0 0 0 0 0 0 0 0 0 0 0 0 (request to grant = none)
INTPRI 9 will continue to be held off until INTPRI 6 completes its interrupt
handler and resets the Interrupt Active flip-flop on its interface, which
also clears the associated poll set bit. At the next poll, INTPRI 9 will be
granted.
This routine determines the request to grant, converts that back to an
interrupt priority number, and uses that to index into the table of DIBs.
The interface routine associated with the DIB is called with INTPOLLIN
asserted.
If the interface still has its Interrupt Request flip-flop set, it
will assert INTACK and return the device number. In response, the IOP will
save the device number in IOA, set the external interrupt bit in CPX1, and
return the device number to the CPU. This will cause the CPU to service the
interrupt.
However, if some condition has occurred between the time of the original
request and this poll, the interface will assert INTPOLLOUT. In response,
the IOP will clear IOA and the associated bit in the poll set to cancel the
request.
In either case, the associated bit in the request set is cleared.
Implementation notes:
1. The hardware inhibits the interrupt poll if the EXTINT flip-flop is set.
This prevents a second interrupt from changing IOA until the microcode
signals its readiness by clearing EXTINT. In simulation, entry with
cpx1_EXTINTR set returns IOA in lieu of conducting a poll.
*/
uint32 iop_poll (void)
{
DIB *dibptr;
SIGNALS_DATA outbound;
uint32 ipn, priority_mask, request_granted;
if (CPX1 & cpx1_EXTINTR) /* if an external interrupt has been requested */
return IOA; /* then return the device number in lieu of polling */
priority_mask = IOPRIORITY (interrupt_poll_set); /* calculate the priority mask */
request_granted = priority_mask & iop_interrupt_request_set; /* and determine the request to grant */
if (request_granted == 0) /* if no request has been granted */
return 0; /* then return */
for (ipn = 0; !(request_granted & 1); ipn++) /* determine the interrupt priority number */
request_granted = request_granted >> 1; /* by counting the bits until the set bit is reached */
dibptr = irqs [ipn]; /* get the DIB pointer for the request */
outbound = dibptr->io_interface (dibptr, INTPOLLIN, 0); /* poll the interface that requested the interrupt */
if (outbound & INTACK) { /* if the interface acknowledged the interrupt */
IOA = IODATA (outbound); /* then save the returned device number */
CPX1 |= cpx1_EXTINTR; /* and tell the CPU */
dprintf (iop_dev, FILTER (dibptr->device_number) ? DEB_IRQ : 0,
"Device number %d acknowledged interrupt request at priority %d\n",
dibptr->device_number, ipn);
}
else if (outbound & INTPOLLOUT) { /* otherwise if the interface cancelled the request */
IOA = 0; /* then clear the device number */
interrupt_poll_set &= ~priority_mask; /* and the associated bit in the poll set */
dprintf (iop_dev, FILTER (dibptr->device_number) ? DEB_IRQ : 0,
"Device number %d canceled interrupt request at priority %d\n",
dibptr->device_number, ipn);
}
iop_interrupt_request_set &= ~priority_mask; /* clear the request */
return IOA; /* return the interrupting device number */
}
/* Dispatch an I/O command to an interface.
This routine is called by the CPU when executing direct I/O instructions
to send I/O orders to the indicated device interface. It translates the
"io_cmd" value to the appropriate I/O signal and calls the signal handler of
the device interface indicated by the "device_number" with the supplied
"write_value". The handler return value, if any, is returned as the function
value. If the supplied device number does not correspond to an enabled
device, the I/O Timeout bit in CPX1 is set.
A "Set Interrupt Mask" order is sent to all active interfaces; the supplied
device number is ignored. If there are none, the I/O Timeout bit is set.
All of the other orders are sent only to the specified device. A "Reset
Interrupt" order clears the corresponding bit from the poll set, unless there
is a request pending on the device (which may occur if a second interrupt was
requested while the first was still being processed).
Implementation notes:
1. For a "Set Interrupt Mask" order, it would be faster to cycle through the
sim_devices array to find the active devices. However, we use the devs
array so that interfaces are accessed in DEVNO order, which makes traces
easier to follow. This is an acceptable tradeoff, as the SMSK
instruction is used infrequently.
*/
HP_WORD iop_direct_io (HP_WORD device_number, IO_COMMAND io_cmd, HP_WORD write_value)
{
static const INBOUND_SIGNAL cmd_to_signal [] = { /* indexed by IO_COMMAND */
DSETINT, /* ioSIN = set interrupt */
DCONTSTB, /* ioCIO = control I/O */
DSTARTIO, /* ioSIO = start I/O */
DWRITESTB, /* ioWIO = write I/O */
DRESETINT, /* ioRIN = reset interrupt */
DSTATSTB, /* ioTIO = test I/O */
DSETMASK, /* ioSMSK = set interrupt mask */
DREADSTB /* ioRIO = read I/O */
};
static const char *const io_command_name [] = { /* indexed by IO_COMMAND */
"Set Interrupt", /* ioSIN */
"Control I/O", /* ioCIO */
"Start I/O", /* ioSIO */
"Write I/O", /* ioWIO */
"Reset Interrupt", /* ioRIN */
"Test I/O", /* ioTIO */
"Set Interrupt Mask", /* ioSMSK */
"Read I/O" /* ioRIO */
};
uint32 irq, devno;
t_bool no_response;
DIB *dibptr;
SIGNALS_DATA outbound = NO_SIGNALS;
if (io_cmd == ioSMSK) { /* if the I/O order is "Set Interrupt Mask" */
no_response = TRUE; /* then check for responding devices */
for (devno = 0; devno <= DEVNO_MAX; devno++) { /* loop through the device number list */
dibptr = devs [devno]; /* to get a device information block pointer */
if (dibptr /* if this device is defined */
&& dibptr->interrupt_mask != INTMASK_UNUSED) { /* and uses the interrupt mask */
dprintf (iop_dev, FILTER (devno) ? DEB_DIO : 0,
"%s order sent to device number %d\n",
io_command_name [io_cmd], devno);
outbound =
dibptr->io_interface (dibptr, DSETMASK, /* send the SET MASK signal to the device */
write_value); /* and supply the new mask value */
if (outbound & INTREQ) /* if an interrupt request was asserted */
iop_assert_INTREQ (dibptr); /* then set it up */
no_response = FALSE; /* at least one device has responded */
}
}
if (no_response) /* if no devices responded */
CPX1 |= cpx1_IOTIMER; /* then indicate an I/O timeout */
}
else { /* otherwise a device-specific order is present */
device_number = device_number & DEVNO_MASK; /* restrict the device number to 0-127 */
dprintf (iop_dev, FILTER (device_number) ? DEB_DIO : 0,
"%s order sent to device number %d\n",
io_command_name [io_cmd], device_number);
dibptr = devs [device_number]; /* get the device information block pointer */
if (dibptr == NULL) /* if the device not present */
CPX1 |= cpx1_IOTIMER; /* then indicate an I/O timeout on access */
else { /* otherwise call the device interface */
outbound = /* with the indicated signal and write value */
dibptr->io_interface (dibptr, cmd_to_signal [io_cmd],
write_value);
if (outbound & INTREQ) /* if an interrupt request was asserted */
iop_assert_INTREQ (dibptr); /* then set it up */
if (outbound & SRn) /* if a service request was asserted */
mpx_assert_SRn (dibptr); /* then set it up */
if (io_cmd == ioRIN /* if this a "Reset Interrupt" order */
&& dibptr->interrupt_priority != INTPRI_UNUSED) { /* and the interrupt priority is valid */
irq = 1 << dibptr->interrupt_priority; /* then calculate the device bit */
if ((iop_interrupt_request_set & irq) == 0) /* if no request is pending for this device */
interrupt_poll_set &= ~irq; /* then clear the associated poll bit */
}
}
}
return IODATA (outbound); /* return the outbound data value */
}
/* Request an interrupt.
This routine is called by device interfaces to request an external interrupt.
It corresponds in hardware to asserting the INTREQ signal. The routine sets
the request and poll set bits corresponding to the interrupt priority number.
*/
void iop_assert_INTREQ (DIB *dibptr)
{
uint32 irq;
dprintf (iop_dev, FILTER (dibptr->device_number) ? DEB_IRQ : 0,
"Device number %d asserted INTREQ at priority %d\n",
dibptr->device_number, dibptr->interrupt_priority);
if (dibptr->interrupt_priority != INTPRI_UNUSED) { /* if the interrupt priority is valid */
irq = 1 << dibptr->interrupt_priority; /* then calculate the corresponding priority bit */
iop_interrupt_request_set |= irq; /* set the request */
interrupt_poll_set |= irq; /* and the poll bits */
}
return;
}
/* IOP local SCP support routines */
/* Set the trace omission filter.
If the "value" parameter is 1, the filter array bits corresponding to the
device number(s) in the buffer referenced by the "cptr" parameter are set to
exclude those devices from the trace listing. If the "value" parameter is 0,
the filter array is reset to include all devices. The unit and descriptor
pointer parameters are not used.
Each bit of the four, 32-bit filter array elements corresponds to a device
number from 0-127, with the LSB of the first element representing device 0,
and the MSB of the last element representing device 127. A set bit enables
tracing of that device. The filter starts out with all bits set, implying
that all devices are traced. Specifying device numbers to filter out clears
the corresponding bits.
Example filter commands:
SET IOP FILTER=3 -- omit tracing for device 3.
SET IOP FILTER=4;7-9;11 -- omit tracing for devices 4, 7, 8, 9, and 11.
SET IOP FILTER=ALL -- omit tracing for all devices
SET IOP NOFILTER -- restore tracing for all devices
On entry, the "cptr" parameter points to the first character of the range
specification, which may be either a semicolon-separated list of device
number ranges or the keyword ALL. Each range is parsed and added to the new
filter array. Once the entire array has been set, it is copied over the old
filter. If an error occurs during parsing, the original filter set is not
disturbed.
*/
static t_stat iop_set_filter (UNIT *uptr, int32 value, char *cptr, void *desc)
{
const char *sptr;
char *tptr;
t_addr dev, low, high;
t_stat result = SCPE_OK;
uint32 new_filter [4] = { TRACE_ALL, TRACE_ALL, TRACE_ALL, TRACE_ALL };
if (value == 1) { /* if we are setting the filter */
if ((cptr == NULL) || (*cptr == '\0')) /* then if a line range was not supplied */
return SCPE_MISVAL; /* then report a "Missing value" error */
tptr = cptr + strlen (cptr); /* append a semicolon */
*tptr++ = ';'; /* to the command string */
*tptr = '\0'; /* to make parsing easier for get_range */
sptr = cptr; /* start at the character after the equals sign */
while (*sptr) { /* parse the command string until it is exhausted */
sptr = get_range (NULL, sptr, &low, &high, /* get a semicolon-separated device number range */
10, 127, ';'); /* in radix 10 with a maximum value of 127 */
if (sptr == NULL || low > 127 || high > 127) { /* if a parsing error occurred or a number was out of range */
result = SCPE_ARG; /* then report an "Invalid argument" error */
break; /* and quit at this point */
}
else for (dev = low; dev <= high; dev++) /* otherwise loop through the range of device numbers */
new_filter [dev / 32] &= ~(1 << dev % 32); /* and clear each corresponding bit in the array */
}
}
else if (cptr != NULL) /* otherwise we are clearing the filter */
return SCPE_2MARG; /* and no arguments are allowed or needed */
if (result == SCPE_OK) { /* if the filter assignment was successful */
filter [0] = new_filter [0]; /* then copy */
filter [1] = new_filter [1]; /* the new filter set */
filter [2] = new_filter [2]; /* in place of */
filter [3] = new_filter [3]; /* the current filter set */
}
return result; /* return the result of the command */
}
/* Show the omission filter.
The device numbers in the filter array are printed as a semicolon-separated
list on the stream designated by the "st" parameter. The "uptr", "value",
and "desc" parameters are not used.
Ranges are printed where possible to shorten the output. This is
accomplished by tracking the starting and ending device numbers of a range of
bits in the filter and then printing that range when a device number bit not
in the filter is encountered.
*/
static t_stat iop_show_filter (FILE *st, UNIT *uptr, int32 value, void *desc)
{
int32 group, low, high;
uint32 test_filter;
t_bool first = TRUE, in_range = FALSE;
low = 0; /* initialize the current starting value */
for (group = 0; group < 4; group++) { /* the filter values are stored in four elements */
test_filter = filter [group]; /* get the set of devices from current element */
for (high = group * 32; high < group * 32 + 32; high++) { /* loop through the represented device numbers */
if ((test_filter & 1) == 0) { /* if the current device is filtered out */
in_range = TRUE; /* then accumulate the omission range */
}
else if (in_range) { /* otherwise if an omission range was accumulated */
if (first) { /* then if this is the first range to be printed */
fputs ("filter=", st); /* then print a header to start */
first = FALSE;
}
else /* otherwise this is not the first range to be printed */
fputc (';', st); /* so print a separator after the prior range */
if (low == high - 1) /* if the range is empty */
fprintf (st, "%d", low); /* then print the single device number */
else /* otherwise a range was established */
fprintf (st, "%d-%d", low, high - 1); /* so print the starting and ending device numbers */
in_range = FALSE; /* start a new range */
low = high + 1; /* from this device number onward */
}
else /* otherwise we are between ranges */
low = low + 1; /* so increment the current starting value */
test_filter = test_filter >> 1; /* shift the next filter bit into place for testing */
}
}
if (first == TRUE) /* if there is only a single range */
if (in_range) /* then if it's an omission range */
fprintf (st, "filter=%d-127\n", low); /* then report it */
else /* otherwise it's an inclusion range */
fputs ("no filter\n", st); /* so report that no devices are filtered out */
else /* otherwise one or more ranges has been printed */
fputc ('\n', st); /* so add a line terminator */
return SCPE_OK;
}

1941
HP3000/hp3000_mpx.c Normal file

File diff suppressed because it is too large Load diff

1337
HP3000/hp3000_ms.c Normal file

File diff suppressed because it is too large Load diff

213
HP3000/hp3000_release.txt Normal file
View file

@ -0,0 +1,213 @@
SIMH/HP 3000 RELEASE NOTES
==========================
Last update: 2016-03-06
This file documents the release history of the Hewlett-Packard 3000 simulator.
The SIMH project does not issue discrete releases. Instead, the current
simulator code base is available at:
https://github.com/simh/simh
...and may be downloaded at any time. A code snapshot is identified by the "git
commit ID" that is displayed by the simulator welcome banner.
Therefore, HP 3000 releases are simply documentation checkpoints that describe
the changes that have occurred since the last checkpoint. Generally, a release
is written when one or more major changes have been incorporated. Minor bug
fixes will be available immediately but only noted as part of the next release
document.
===================
General Information
===================
The simulator passes the HP 32230 offline diagnostic suite with some expected
failures due to unimplemented features. For example, the disc diagnostic
error-correction logic tests and the tape diagnostic CRCC and LRCC tests fail,
as these features are not supported. However, all features that are required
for MPE operation pass their respective diagnostic tests.
The simulator has been tested with MPE-V/R version E.01.00. Specifically:
- MPE can be RELOADed to generate a new disc-based system from a FOS tape.
- MPE can be COOLSTARTed to run from a previously generated disc.
- The MPE system console operates (by default) through the simulation console,
and additional sessions may be connected to the ATC via Telnet or host
serial ports.
- MPE FOS programs (EDITOR, QUERY) and SUBSYS programs (SPL, BASIC, BASICOMP)
run properly.
- The SYSDUMP program produces a valid tape image, and the system may be
COLDSTARTED from it.
- The operator and system manager can be logged in and out, and MPE can be
SHUTDOWN through to a HALT 17.
The user's manual for the simulator is provided in Microsoft Word format in the
"doc" subdirectory of the code base snapshot downloaded from the github site. A
PDF version of the same manual is available at:
http://alum.mit.edu/www/jdbryan/hp3000_doc.pdf
A preconfigured MPE-V/R disc image containing the Fundamental Operating Software
(FOS), selected SUBSYS language processors (BASIC, BASICOMP, COBOL, COBOLII,
FORTRAN, PASCAL, RPG, and SPL), and example programs is available from Bitsavers
at:
http://www.bitsavers.org/bits/HP/HP_3000/
The archive contains instructions and simulator command files that allow
ready-to-run operation.
Manuals describing MPE operation are also available from Bitsavers at:
http://www.bitsavers.org/pdf/hp/3000/
HP created MPE-V/R-specific manuals. However, very few of them survive. In
general, the MPE-IV manuals will describe a subset of MPE-V/R commands, whereas
the MPE-V/E manuals describe a superset. Relying on the MPE-IV manuals and the
online help available within MPE for those commands that do not appear in the
manuals is perhaps the best compromise.
-----------------------
Bugs in MPE V/R E.01.00
-----------------------
Testing during simulator development revealed the presence of several bugs in
the MPE version used:
- After a cold load from tape (COLDSTART/RELOAD/UPDATE), if a non-HP terminal
such as the simulation console is used as the system console, MPE prints DATE
(M/D/Y)? and then WED, NOV 1, 1972, 12:00 AM, as though RETURN had been
entered, but it wasn't. If an HP terminal emulator is used instead, MPE
waits for the user to enter the date before proceeding.
The problem is incorrect coding in the SPEEDSENSE procedure in module
INITIAL. As a result, the console baud rate is set to an invalid value, so
console reads fail. The resulting zero-length read is interpreted as though
RETURN had been entered.
This is MPE V/R SSB KPR Number 5000187104, "Foreign devices as SIII system
consoles do not work correctly on V/R." HP issued a patch for this, but it
does not seem to have survived. A simple workaround is to set local ENQ/ACK
processing on ATC channel 0 (SET ATCD0 LOCALACK) when the system console is
not an HP terminal. This is the default setting, so the bug only manifests
itself when SET ATCD0 REMOTEACK is done before booting. An alternate
workaround that does not depend on the ATC setting is to set memory location
01.112247 to octal value 021360. This changes the "LOAD P+22,I,X"
instruction at that location to "LDI 360" to set the detected speed to 2400
baud unconditionally.
- If a SHUTDOWN is performed while a logon read is pending on the system
console, e.g., by pressing RETURN to obtain the colon prompt after logging
OPERATOR.SYS off, the expected "ALL JOBS LOGGED OFF" message does not print.
Instead, the first few characters of the message (which begins with the
current time) are printed, followed as expected by SHUT and a HALT %17. If
no read is pending, either because RETURN was not pressed before entering the
SHUTDOWN command or because the read timed out, the message is printed
normally.
The problem is that while the message is being send to the ATC character-by-
character, the I/O abort issued to cancel the logon read also cancels the
message write. The timing is such that only the first few characters of the
message are printed before the rest of the output is cancelled.
No SSB KPR has been located, but a later MPE version inserts an ABORTIO call
for the system console immediately before logging all sessions off. This
clears any logon read that might exist, and therefore an abort will not be
performed after the "ALL JOBS LOGGED OFF" message is output. It is
impossible to patch memory to add this call, so the only workarounds when
shutting down are to avoid requesting the logon prompt, wait until the logon
timeout expires (nominally two minutes), log on and then back off again,
enter "=ABORTIO 20" to abort the read before entering SHUTDOWN, or accept
that the message will be truncated. The only consequence of this bug is the
partial message; MPE shuts down properly otherwise, so it may be safely
ignored.
- After a RELOAD, running DPAN4.PUB.SYS produces a "CODE SEGMENT TOO LARGE
(LOAD ERR 33)" error. This is because MPE defaults to an 8K code segment
size limit, and DPAN4 has three segments between 8K and 12K in size. If the
limit is subsequently raised via a SYSDUMP and COLDSTART reconfiguration,
running DPAN4 produces a "FILE IS NOT A VALID PROGRAM FILE" error. However,
if the reconfiguration is done before running DPAN4, it will run properly
thereafter.
The problem is that MPE incorrectly modifies the executable file's Segment
Transfer Tables when it encounters a code segment that is larger than the
configured limit. This leaves the file in an inconsistent state, leading to
the "NOT A VALID PROGRAM FILE" message after reconfiguration to raise the
segment size limit. If the limit is raised before running DPAN4, the file is
internally consistent when the STTs are patched, and each segment's load
succeeds, allowing the program to run.
No SSB KPR has been located, but a later MPE version ensures that code
segment size aborts occur before any of the STTs are modified, so the program
file remains internally consistent. A memory patch is impossible, but
workarounds are to increase the code segment limit before running DPAN4 and
to restore DPAN4.PUB.SYS from the FOS tape if it has been damaged.
=====================
Release 1, 2016-03-07
=====================
This is the initial release of the HP 3000 simulator. The following devices are
currently simulated:
- 30003B Series III computer with up to 1024 KW of memory
- 30003B I/O Processor
- 30036B Multiplexer Channel
- 30030C Selector Channel
- 30033A Selector Channel Maintenance Board
- 30032B Asynchronous Terminal Controller data interface
- 30061B Asynchronous Terminal Controller control interface
- 30135A System Clock/Fault Logging Interface
- 30215A Tape Controller with four 7970B/E drives
- 30229B Disc Controller with eight 7905/7906/7920/7925 drives
The "HP 3000 Simulator User's Guide" manual describes the configuration and
operation of each of these devices in detail.
--------------------
Implementation Notes
--------------------
- IMPORTANT: There is no line printer simulation. MPE cannot be configured to
run without a printer; attempting to delete LDEV 6 produces "ERROR #115
UNDEFINED CLASS LP USED AS OUTPUT DEVICE", and class LP cannot be deleted.
With LDEV 6 present, MPE will boot and run, but doing, e.g., :STOPSPOOL 6
causes "NON-RESPONDING DRT #14" and "SYSTEM FAILURE #201" when the printer
doesn't respond. Entering :OUTFENCE 14 at the console operator's session
immediately after bootup is a workaround. The LP simulator should be present
in the next release.
- The CPU is a hybrid of the Series II instruction set microcode and the Series
III memory size and hardware behavior, because the Series III microcode is
not available.
- The CPU is currently missing a few "difficult" instructions (the CMD
instruction, the Series II LOCK and UNLK instructions, and the entire
Extended Instruction Set). Although the EIS is not present, MPE has a
software emulator for these instructions that is invoked transparently by the
Unimplemented Instruction traps that occur when attempted execution of EIS
instructions occurs.
- The main memory Fault Logging Interface section of the 30135A is currently
not simulated. Although fault-control memory was standard on the Series II
and later, the memory fault logger is smart enough to realize that the FLI is
not there, so MPE will run without it.
- Symbolic entry of CPU instructions, CPU status, and I/O instructions are not
currently supported.

1222
HP3000/hp3000_scmb.c Normal file

File diff suppressed because it is too large Load diff

1319
HP3000/hp3000_sel.c Normal file

File diff suppressed because it is too large Load diff

2908
HP3000/hp3000_sys.c Normal file

File diff suppressed because it is too large Load diff

4286
HP3000/hp_disclib.c Normal file

File diff suppressed because it is too large Load diff

551
HP3000/hp_disclib.h Normal file
View file

@ -0,0 +1,551 @@
/* hp_disclib.h: HP MAC/ICD disc controller simulator library definitions
Copyright (c) 2011-2016, J. David Bryan
Copyright (c) 2004-2011, Robert M. Supnik
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 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 name 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.
27-Jul-15 JDB First revised release version
21-Feb-15 JDB Revised for new controller interface model
24-Oct-12 JDB Changed CNTLR_OPCODE to title case to avoid name clash
07-May-12 JDB Added end-of-track delay time as a controller variable
02-May-12 JDB First release
09-Nov-11 JDB Created disc controller common library from DS simulator
This file provides the declarations for interoperation between interface
simulators and the simulation library for the HP 13037 and 13365 disc
controllers. It must be included by the interface-specific modules.
*/
#include "hp3000_defs.h" /* this must reflect the machine used */
/* Program limits */
#define DL_MAXDRIVE 7 /* last valid drive number */
#define DL_AUXUNITS 1 /* number of MAC auxiliary units required */
#define DL_BUFSIZE 138 /* required buffer size in words (full sector) */
/* Program constants (cylinders * heads * sectors * words per sector) */
#define WORDS_7905 (411 * 3 * 48 * 128) /* 7905 capacity = 15 MB */
#define WORDS_7906 (411 * 4 * 48 * 128) /* 7906 capacity = 20 MB */
#define WORDS_7920 (823 * 5 * 48 * 128) /* 7920 capacity = 50 MB */
#define WORDS_7925 (823 * 9 * 64 * 128) /* 7925 capacity = 120 MB */
/* Debug flags */
#define DL_DEB_CMD (1 << 0) /* trace controller commands */
#define DL_DEB_INCO (1 << 1) /* trace command initiations and completions */
#define DL_DEB_STATE (1 << 2) /* trace command execution state changes */
#define DL_DEB_SERV (1 << 3) /* trace unit service scheduling calls */
#define DL_DEB_XFER (1 << 4) /* trace data reads and writes */
#define DL_DEB_IOB (1 << 5) /* trace I/O bus signals and data words */
#define DL_DEB_V_UF 6 /* first free debug flag bit */
/* Common per-unit disc drive state variables */
#define CYL u3 /* drive cylinder */
#define STATUS u4 /* drive status (Status 2) */
#define OPCODE u5 /* drive current operation */
#define PHASE u6 /* drive current operation phase */
/* Device flags and accessors */
#define DEV_REALTIME_SHIFT (DEV_V_UF + 0) /* bits 0-0: timing mode is realistic */
#define DEV_REALTIME (1 << DEV_REALTIME_SHIFT) /* realistic timing flag */
/* Unit flags and accessors */
#define UNIT_MODEL_SHIFT (UNIT_V_UF + 0) /* bits 0-1: drive model ID */
#define UNIT_PROT_SHIFT (UNIT_V_UF + 2) /* bits 2-3 write protection */
#define UNIT_UNLOAD_SHIFT (UNIT_V_UF + 4) /* bits 4-4: heads unloaded */
#define UNIT_FMT_SHIFT (UNIT_V_UF + 5) /* bits 5-5: format enabled */
#define DL_V_UF (UNIT_V_UF + 6) /* first free unit flag bit */
#define UNIT_MODEL_MASK 0000003 /* model ID mask */
#define UNIT_PROT_MASK 0000003 /* head protection mask */
#define UNIT_MODEL (UNIT_MODEL_MASK << UNIT_MODEL_SHIFT)
#define UNIT_PROT (UNIT_PROT_MASK << UNIT_PROT_SHIFT)
#define UNIT_PROT_L (1 << UNIT_PROT_SHIFT + 0)
#define UNIT_PROT_U (1 << UNIT_PROT_SHIFT + 1)
#define UNIT_UNLOAD (1 << UNIT_UNLOAD_SHIFT)
#define UNIT_FMT (1 << UNIT_FMT_SHIFT)
#define UNIT_7905 (HP_7905 << UNIT_MODEL_SHIFT)
#define UNIT_7906 (HP_7906 << UNIT_MODEL_SHIFT)
#define UNIT_7920 (HP_7920 << UNIT_MODEL_SHIFT)
#define UNIT_7925 (HP_7925 << UNIT_MODEL_SHIFT)
/* Controller flag and function accessors */
#define DLIFN(C) ((CNTLR_IFN_SET) ((C) & ~D16_MASK))
#define DLIBUS(C) ((CNTLR_IBUS) ((C) & D16_MASK))
#define DLNEXTIFN(S) ((CNTLR_IFN) IOPRIORITY (S))
/* Disc drive types */
typedef enum {
HP_All = -1,
HP_7906 = 0, /* these values */
HP_7920 = 1, /* are hard-coded */
HP_7905 = 2, /* in the 13037 */
HP_7925 = 3 /* controller microcode */
} DRIVE_TYPE;
/* Controller types */
typedef enum {
MAC = 0,
ICD,
CS80
} CNTLR_TYPE;
#define LAST_CNTLR CS80
#define CNTLR_COUNT (LAST_CNTLR + 1)
/* Interface flags and function bus orders.
The CNTLR_FLAG and CNTLR_IFN declarations mirror the hardware signals that
are received and asserted, respectively, by the 13037 disc controller. In
simulation, the interface to which the controller is connected sends a set of
one or more flags that indicate the state of the interface to the controller.
The controller then returns a set of one or more functions that request the
interface to perform certain actions.
In hardware, a series of functions are sent sequentially to the interface as
encoded values accompanied by IFVLD (interface function is valid) or IFCLK
(interface function clock) signals. In simulation, the functions are decoded
into a set of function identifiers that are returned to, and then processed
sequentially by, the interface in order of ascending numerical value.
Implementation notes:
1. The enumerations describe signals. A set of signals normally would be
modeled as an unsigned integer, as a set may contain more than one
signal. However, we define a set as the enumeration, as the "gdb"
debugger has special provisions for an enumeration of discrete bit values
and will display the set in "ORed" form.
2. The null sets -- NO_FLAGS and NO_FUNCTIONS -- cannot be defined as
enumeration constants, as including them would require handlers for them
in "switch" statements, which is undesirable. Therefore, we define them
as an explicit integer zero values compatible with the enumerations.
3. Function bus values are restricted to the upper 16 bits to allow the
combined function and data value to fit in 32 bits.
4. The hardware signal CLEAR is renamed CLEARF in simulation to avoid the
name clash with the flip-flop CLEAR enumeration value. (A sensible
language would allow a definition of CLEAR in each of the enumerations.
However, C has a single name space for all enumeration constants.)
5. In hardware, the BUSY command is used in combination with the LSB of the
data bus to set or clear the interface busy flip-flop. In simulation,
separate BUSY (set busy) and FREE (clear busy) commands are used to allow
the data bus to carry a full 16-bit value.
6. The DVEND function must come numerically after RQSRV, as DVEND will abort
the transfer if no retries are left.
*/
typedef enum { /* interface flags */
CLEARF = 0000001,
CMRDY = 0000002,
DTRDY = 0000004,
EOD = 0000010,
INTOK = 0000020,
OVRUN = 0000040,
XFRNG = 0000100
} CNTLR_FLAG;
#define NO_FLAGS ((CNTLR_FLAG) 0) /* no flags are asserted */
typedef CNTLR_FLAG CNTLR_FLAG_SET; /* a set of CNTLR_FLAGs */
typedef enum { /* interface function bus orders */
BUSY = 000000200000,
DSCIF = 000000400000,
SELIF = 000001000000,
IFIN = 000002000000,
IFOUT = 000004000000,
IFGTC = 000010000000,
IFPRF = 000020000000,
RQSRV = 000040000000,
DVEND = 000100000000,
SRTRY = 000200000000,
STDFL = 000400000000,
STINT = 001000000000,
WRTIO = 002000000000,
FREE = 004000000000
} CNTLR_IFN;
#define NO_FUNCTIONS ((CNTLR_IFN) 0) /* no functions are asserted */
typedef CNTLR_IFN CNTLR_IFN_SET; /* a set of CNTLR_IFNs */
typedef uint16 CNTLR_IBUS; /* the interface data bus */
#undef NO_DATA /* remove winsock definition */
#define NO_DATA (CNTLR_IBUS) 0 /* no data asserted */
typedef uint32 CNTLR_IFN_IBUS; /* a combined interface function set and data bus value */
/* Controller opcodes */
typedef enum {
Cold_Load_Read = 000,
Recalibrate = 001,
Seek = 002,
Request_Status = 003,
Request_Sector_Address = 004,
Read = 005,
Read_Full_Sector = 006,
Verify = 007,
Write = 010,
Write_Full_Sector = 011,
Clear = 012,
Initialize = 013,
Address_Record = 014,
Request_Syndrome = 015,
Read_With_Offset = 016,
Set_File_Mask = 017,
Invalid_Opcode = 020,
Read_Without_Verify = 022,
Load_TIO_Register = 023,
Request_Disc_Address = 024,
End = 025,
Wakeup = 026
} CNTLR_OPCODE;
#define LAST_OPCODE Wakeup
/* Controller command classifications */
typedef enum {
Class_Invalid, /* invalid classification */
Class_Read, /* read classification */
Class_Write, /* write classification */
Class_Control, /* control classification */
Class_Status /* status classification */
} CNTLR_CLASS;
/* Controller status.
Not all status values are returned by the library. The values not currently
returned are:
- Illegal_Drive_Type
- Cylinder_Miscompare
- Head_Sector_Miscompare
- Sync_Timeout
- Correctable_Data_Error
- Illegal_Spare_Access
- Defective_Track
In hardware, all of the above errors, except for Illegal_Drive_Type and
Correctable_Data_Error, potentially could occur during the address
verification that precedes normal reads and writes. In simulation, these
cannot occur, as sector address headers are not simulated. However, any
error may be returned if diagnostic overrides are used.
In simulation, Uncorrectable_Data_Error is returned by default by the Request
Syndrome command. Uncorrectable_Data_Error is also returned if a host I/O
error occurs on reading or writing.
*/
typedef enum {
Normal_Completion = 000,
Illegal_Opcode = 001,
Unit_Available = 002,
Illegal_Drive_Type = 003,
Cylinder_Miscompare = 007,
Uncorrectable_Data_Error = 010,
Head_Sector_Miscompare = 011,
IO_Program_Error = 012,
Sync_Timeout = 013,
End_of_Cylinder = 014,
Data_Overrun = 016,
Correctable_Data_Error = 017,
Illegal_Spare_Access = 020,
Defective_Track = 021,
Access_Not_Ready = 022,
Status_2_Error = 023,
Protected_Track = 026,
Unit_Unavailable = 027,
Drive_Attention = 037
} CNTLR_STATUS;
#define LAST_STATUS Drive_Attention
/* Controller execution states */
typedef enum {
Idle_State, /* idle */
Wait_State, /* command wait */
Busy_State /* busy */
} CNTLR_STATE;
/* Unit command phases */
typedef enum {
Idle_Phase = 0,
Parameter_Phase,
Seek_Phase,
Rotate_Phase,
Data_Phase,
Intersector_Phase,
End_Phase
} CNTLR_PHASE;
#define LAST_PHASE End_Phase
/* Diagnostic override entries.
Diagnostic overrides are used to return controller status values that
otherwise are not simulated to a diagnostic program. A table of override
entries may be set up by the interface simulator if this facility is desired.
*/
typedef struct {
uint32 cylinder; /* matching cylinder address */
uint32 head; /* matching head address */
uint32 sector; /* matching sector address */
CNTLR_OPCODE opcode; /* matching controller opcode */
uint32 spd; /* returned S/P/D flags */
CNTLR_STATUS status; /* returned controller status */
} DIAG_ENTRY;
#define DL_OVEND D32_UMAX /* marker for the end of the current override set */
/* Disc access delays */
typedef struct {
CNTLR_TYPE type; /* controller type */
DRIVE_TYPE drive; /* drive type */
int32 seek_one; /* track-to-track seek time */
int32 seek_full; /* full-stroke seek time */
int32 sector_full; /* full sector rotation time */
int32 data_xfer; /* per-word data transfer time */
int32 intersector_gap; /* intersector gap time */
int32 overhead; /* controller execution overhead */
} DELAY_PROPS;
#define DELAY_INIT(sk1,skf,scf,dxfr,isg,ovhd) \
0, 0, \
(sk1), (skf), (scf), (dxfr), (isg), (ovhd)
/* Disc controller state */
typedef struct {
CNTLR_TYPE type; /* controller type */
DEVICE *device; /* controlling device pointer */
CNTLR_STATE state; /* controller state */
CNTLR_OPCODE opcode; /* controller opcode */
CNTLR_STATUS status; /* controller status */
FLIP_FLOP eoc; /* end-of-cylinder flag */
t_bool verify; /* address verification required */
uint32 spd_unit; /* S/P/D flags and unit number */
uint32 file_mask; /* file mask */
uint32 cylinder; /* cylinder address */
uint32 head; /* head address */
uint32 sector; /* sector address */
uint32 count; /* count of words transferred or to verify */
uint32 poll_unit; /* last unit polled for attention */
uint16 *buffer; /* data buffer pointer */
uint32 index; /* data buffer current index */
uint32 length; /* data buffer valid length */
DIAG_ENTRY *dop_base; /* pointer to the diagnostic override array */
DIAG_ENTRY *dop; /* current diagnostic override entry pointer */
DELAY_PROPS *fastptr; /* pointer to the FASTTIME delays */
const DELAY_PROPS *dlyptr; /* current delay property pointer */
} CNTLR_VARS;
typedef CNTLR_VARS *CVPTR; /* pointer to a controller state variable structure */
/* Controller state variable structure initialization.
The supplied parameters are:
ctype - the type of the controller (CNTLR_TYPE)
dev - the device on which the controller operates (DEVICE)
bufptr - a pointer to the data buffer (array of uint16)
doa - a pointer to the diagnostic override array (array of DIAG_ENTRY)
or NULL if this facility is not used
fast - a pointer to the fast timing values (DELAY_PROPS)
*/
#define CNTLR_INIT(ctype,dev,bufptr,doa,fast) \
(ctype), &(dev), Idle_State, End, Normal_Completion, \
CLEAR, FALSE, \
0, 0, 0, 0, 0, 0, 0, \
(bufptr), 0, 0, \
(doa), NULL, \
&(fast), &(fast)
/* Disc controller device register definitions.
The supplied parameters are:
cntlr - the controller state variable structure (CNTLR_VARS)
units - the unit array (array of UNIT)
numunits - the number of units in the unit array
buffer - the sector buffer (array of uint16)
times - the fast timing values structure (DELAY_PROPS)
These definitions should be included AFTER any interface-specific registers.
Implementation notes:
1. The "CNTLR" register is present to ensure that the entire CNTLR_VARS
structure is saved and restored.
2. The fast timing structure does not use the controller and drive type
fields, so they do not need to be SAVEd or RESTOREd, so they do not need
to appear in hidden registers.
*/
#define DL_REGS(cntlr,units,numunits,buffer,times) \
/* Macro Name Location Radix Width Depth Flags */ \
/* ------ -------- ------------------ ----- ----- ----------------- ---------------- */ \
{ ORDATA (OPCODE, (cntlr).opcode, 5), REG_RO }, \
{ ORDATA (CSTATS, (cntlr).status, 5), REG_RO }, \
{ DRDATA (CSTATE, (cntlr).state, 2), PV_LEFT | REG_RO }, \
{ FLDATA (EOC, (cntlr).eoc, 0) }, \
{ FLDATA (VERIFY, (cntlr).verify, 0) }, \
{ ORDATA (SPDU, (cntlr).spd_unit, 16) }, \
{ ORDATA (FLMASK, (cntlr).file_mask, 4) }, \
{ DRDATA (CYL, (cntlr).cylinder, 16), PV_LEFT }, \
{ DRDATA (HEAD, (cntlr).head, 6), PV_LEFT }, \
{ DRDATA (SECTOR, (cntlr).sector, 8), PV_LEFT }, \
{ DRDATA (COUNT, (cntlr).count, 16), PV_LEFT }, \
{ BRDATA (SECBUF, (buffer), 8, 16, DL_BUFSIZE), REG_A }, \
{ DRDATA (INDEX, (cntlr).index, 8), PV_LEFT }, \
{ DRDATA (LENGTH, (cntlr).length, 8), PV_LEFT }, \
{ SRDATA (CNTLR, (cntlr)), REG_HRO }, \
\
/* Macro Name Location Width Flags */ \
/* ------ ------ ------------------------ ----- ---------------- */ \
{ DRDATA (TTIME, (times).seek_one, 24), PV_LEFT | REG_NZ }, \
{ DRDATA (FTIME, (times).seek_full, 24), PV_LEFT | REG_NZ }, \
{ DRDATA (STIME, (times).sector_full, 24), PV_LEFT | REG_NZ }, \
{ DRDATA (XTIME, (times).data_xfer, 24), PV_LEFT | REG_NZ }, \
{ DRDATA (GTIME, (times).intersector_gap, 24), PV_LEFT | REG_NZ }, \
{ DRDATA (OTIME, (times).overhead, 24), PV_LEFT | REG_NZ }, \
\
/* Macro Name Location Radix Width Offset Depth Flags */ \
/* ------ ------- ------------------ ----- --------- ------ ----------- ----------------- */ \
{ URDATA (UCYL, (units)[0].CYL, 10, 10, 0, (numunits), PV_LEFT) }, \
{ URDATA (UOPCODE, (units)[0].OPCODE, 8, 6, 0, (numunits), PV_RZRO | REG_RO) }, \
{ URDATA (USTATUS, (units)[0].STATUS, 2, 32, 0, (numunits), PV_RZRO) }, \
{ URDATA (USTATE, (units)[0].PHASE, 10, 4, 0, (numunits), PV_RZRO | REG_RO) }, \
{ URDATA (UPOS, (units)[0].pos, 10, T_ADDR_W, 0, (numunits), PV_LEFT | REG_RO) }, \
{ URDATA (UWAIT, (units)[0].wait, 8, 32, 0, (numunits), PV_LEFT) }
/* Disc controller device modifier definitions.
These definitions should be included BEFORE any device-specific modifiers.
*/
#define DL_MODS(cntlr,loadvalid,ovcount) \
/* Mask Value Match Value Print String Match String Validation Display Descriptor */ \
/* ------------ ----------- ----------------- ------------ ------------- ------- ---------- */ \
{ UNIT_MODEL, UNIT_7905, "7905", "7905", &dl_set_model, NULL, NULL }, \
{ UNIT_MODEL, UNIT_7906, "7906", "7906", &dl_set_model, NULL, NULL }, \
{ UNIT_MODEL, UNIT_7920, "7920", "7920", &dl_set_model, NULL, NULL }, \
{ UNIT_MODEL, UNIT_7925, "7925", "7925", &dl_set_model, NULL, NULL }, \
\
{ UNIT_UNLOAD, 0, "heads loaded", "LOAD", &(loadvalid), NULL, NULL }, \
{ UNIT_UNLOAD, UNIT_UNLOAD, "heads unloaded", "UNLOAD", &(loadvalid), NULL, NULL }, \
\
{ UNIT_FMT, UNIT_FMT, "format enabled", "FORMAT", NULL, NULL, NULL }, \
{ UNIT_FMT, 0, "format disabled", "NOFORMAT", NULL, NULL, NULL }, \
\
/* Entry Flags Value Print String Match String Validation Display Descriptor */ \
/* ------------------- ---------- ------------ -------------- ---------------- ----------------- ----------------- */ \
{ MTAB_XUN, 1, "", "PROTECT", &dl_set_protect, &dl_show_protect, NULL }, \
{ MTAB_XUN, 0, NULL, "UNPROTECT", &dl_set_protect, NULL, NULL }, \
\
{ MTAB_XDV, 0, NULL, "FASTTIME", &dl_set_timing, NULL, (void *) &(cntlr) }, \
{ MTAB_XDV, 1, NULL, "REALTIME", &dl_set_timing, NULL, (void *) &(cntlr) }, \
{ MTAB_XDV, 0, "TIMING", NULL, NULL, &dl_show_timing, (void *) &(cntlr) }, \
\
{ MTAB_XDV | MTAB_NMO, (ovcount), "DIAG", "DIAGNOSTIC", &dl_set_diag, &dl_show_diag, (void *) &(cntlr) }, \
{ MTAB_XDV, 0, "", "NODIAGNOSTIC", &dl_set_diag, &dl_show_diag, (void *) &(cntlr) }
/* Disc library global controller routines */
extern CNTLR_IFN_IBUS dl_controller (CVPTR cvptr, UNIT *uptr, CNTLR_FLAG_SET flags, CNTLR_IBUS data);
extern t_stat dl_load_unload (CVPTR cvptr, UNIT *uptr, t_bool load);
/* Disc library global utility routines */
extern const char *dl_opcode_name (CNTLR_TYPE controller, CNTLR_OPCODE opcode);
extern const char *dl_status_name (CNTLR_STATUS status);
/* Disc library global SCP support routines */
extern t_stat dl_attach (CVPTR cvptr, UNIT *uptr, char *cptr);
extern t_stat dl_detach (CVPTR cvptr, UNIT *uptr);
extern t_stat dl_set_model (UNIT *uptr, int32 value, char *cptr, void *desc);
extern t_stat dl_set_protect (UNIT *uptr, int32 value, char *cptr, void *desc);
extern t_stat dl_set_diag (UNIT *uptr, int32 value, char *cptr, void *desc);
extern t_stat dl_set_timing (UNIT *uptr, int32 value, char *cptr, void *desc);
extern t_stat dl_show_protect (FILE *st, UNIT *uptr, int32 value, void *desc);
extern t_stat dl_show_diag (FILE *st, UNIT *uptr, int32 value, void *desc);
extern t_stat dl_show_timing (FILE *st, UNIT *uptr, int32 value, void *desc);

3176
HP3000/hp_tapelib.c Normal file

File diff suppressed because it is too large Load diff

537
HP3000/hp_tapelib.h Normal file
View file

@ -0,0 +1,537 @@
/* hp_tapelib.h: HP magnetic tape controller simulator library definitions
Copyright (c) 2013-2016, J. David Bryan
Copyright (c) 2004-2011, Robert M. Supnik
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 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 name 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.
11-Nov-15 JDB First release version
24-Mar-13 JDB Created tape controller common library from MS simulator
This file defines the interface between machine-specific tape drive
simulators and the common HP tape controller simulation library. It must be
included by the controller-specific modules.
*/
#include "hp3000_defs.h" /* this must reflect the machine used */
#include "sim_tape.h"
/* Program limits */
#define TL_MAXDRIVE 3 /* last valid drive number */
#define TL_AUXUNITS 1 /* number of auxiliary units required */
#define TL_CNTLR_UNIT (TL_MAXDRIVE + 1) /* controller unit number */
#define TL_MAXREC (DV_UMAX + 1) /* maximum supported tape record size in bytes */
#define TL_BUFSIZE (TL_MAXREC + 2) /* buffer size in bytes (including space for CRCC/LRCC) */
/* Debug flags */
#define TL_DEB_CMD (1 << 0) /* trace controller commands */
#define TL_DEB_INCO (1 << 1) /* trace command initiations and completions */
#define TL_DEB_STATE (1 << 2) /* trace command execution state changes */
#define TL_DEB_SERV (1 << 3) /* trace unit service scheduling calls */
#define TL_DEB_XFER (1 << 4) /* trace data reads and writes */
#define TL_DEB_IOB (1 << 5) /* trace I/O bus signals and data words */
#define TL_DEB_V_UF 6 /* first free debug flag bit */
/* Common per-unit tape drive state variables */
#define PROP u3 /* drive properties */
#define STATUS u4 /* drive status */
#define OPCODE u5 /* drive current operation */
#define PHASE u6 /* drive current operation phase */
/* Device flags and accessors */
#define DEV_REALTIME_SHIFT (DEV_V_UF + 0) /* bits 0-0: timing mode is realistic */
#define DEV_REALTIME (1 << DEV_REALTIME_SHIFT) /* realistic timing flag */
/* Unit flags and accessors.
The user-defined unit flags are used to store tape unit status that may be
modified by the user, as follows:
+---+---+---+---+---+---+---+---+
[...] - - - | R | O | model |
+---+---+---+---+---+---+---+---+
Where:
R = the unit is rewinding
O = the unit is offline
model = the DRIVE_TYPE enumeration constant for the simulated drive
Implementation notes:
1. The hardware REWIND STATUS (SRW) signal is implemented as a unit flag,
although it may be inferred from unit.OPCODE = Rewind or Rewind_Offline
and unit.PHASE = Traverse_Phase. This is for the convenience of testing
the "unit busy" condition, which exists when a unit is offline or
rewinding.
*/
#define UNIT_MODEL_SHIFT (MTUF_V_UF + 0) /* bits 0-2: drive model ID */
#define UNIT_OFFLINE_SHIFT (MTUF_V_UF + 3) /* bits 3-3: unit is offline */
#define UNIT_REW_SHIFT (MTUF_V_UF + 4) /* bits 4-4: unit is rewinding */
#define TL_UNIT_V_UF (MTUF_V_UF + 5) /* first free unit flag bit */
#define UNIT_MODEL_MASK 0000007 /* model ID mask */
#define UNIT_MODEL (UNIT_MODEL_MASK << UNIT_MODEL_SHIFT)
#define UNIT_OFFLINE (1 << UNIT_OFFLINE_SHIFT)
#define UNIT_REWINDING (1 << UNIT_REW_SHIFT)
#define UNIT_7970B (HP_7970B << UNIT_MODEL_SHIFT)
#define UNIT_7970E (HP_7970E << UNIT_MODEL_SHIFT)
#define UNIT_7974 (HP_7974 << UNIT_MODEL_SHIFT)
#define UNIT_7978 (HP_7978 << UNIT_MODEL_SHIFT)
/* Controller flag and function accessors */
#define TLIFN(C) ((CNTLR_IFN_SET) ((C) & ~D16_MASK))
#define TLIBUS(C) ((CNTLR_IBUS) ((C) & D16_MASK))
#define TLNEXTIFN(S) ((CNTLR_IFN) IOPRIORITY (S))
/* Tape drive types */
typedef enum {
HP_7970B, /* HP 7970B 800 bpi NRZI */
HP_7970E, /* HP 7970E 1600 bpi PE */
HP_7974, /* HP 7974A 800/1600 bpi NRZI/PE */
HP_7978 /* HP 7978A 1600/6250 bpi PE/GCR */
} DRIVE_TYPE;
/* Controller types */
typedef enum {
HP_13181, /* HP 1000 NRZI controller */
HP_13183, /* HP 1000 PE controller */
HP_30215, /* HP 3000 NRZI/PE controller */
HP_IB /* HP-IB controller */
} CNTLR_TYPE;
#define LAST_CNTLR HP_IB
#define CNTLR_COUNT (LAST_CNTLR + 1)
/* Interface flags and function bus orders.
The CNTLR_FLAG and CNTLR_IFN declarations simulate hardware signals that are
received and asserted, respectively, by the abstract tape controller. In
simulation, the interface sends a set of one or more flags that indicates the
state of the interface to the controller. The controller then returns a set
of one or more functions that requests the interface to perform certain
actions.
The function set is decoded into a set of function identifiers that are
returned to, and then processed sequentially by, the interface in order of
ascending numerical value.
Implementation notes:
1. The enumerations describe signals. A set of signals normally would be
modeled as an unsigned integer, as a set may contain more than one
signal. However, we define a set as the enumeration, as the "gdb"
debugger has special provisions for an enumeration of discrete bit values
and will display the set in "ORed" form.
2. The null sets -- NO_FLAGS and NO_FUNCTIONS -- cannot be defined as
enumeration constants, as including them would require handlers for them
in "switch" statements, which is undesirable. Therefore, we define them
as an explicit integer zero values compatible with the enumerations.
3. Function bus values are restricted to the upper 16 bits to allow the
combined function and data value to fit in 32 bits.
*/
typedef enum { /* interface flags */
CMRDY = 0000001, /* Command Ready */
CMXEQ = 0000002, /* Command Execute */
DTRDY = 0000004, /* Data Ready */
EOD = 0000010, /* End of Data */
INTOK = 0000020, /* Interrupt OK */
OVRUN = 0000040, /* Data Overrun */
XFRNG = 0000100 /* Data Transfer No Good */
} CNTLR_FLAG;
#define NO_FLAGS (CNTLR_FLAG) 0 /* no flags are asserted */
typedef CNTLR_FLAG CNTLR_FLAG_SET; /* a set of CNTLR_FLAGs */
typedef enum { /* interface function bus orders */
IFIN = 000000200000, /* Interface In */
IFOUT = 000000400000, /* Interface Out */
IFGTC = 000001000000, /* Interface Get Command */
SCPE = 000002000000, /* SCP Error Status */
RQSRV = 000004000000, /* Request Service */
DVEND = 000010000000, /* Device End */
STCFL = 000020000000, /* Set Control Flag */
STDFL = 000040000000, /* Set Data Flag */
STINT = 000100000000, /* Set Interrupt */
DATTN = 000200000000 /* Drive Attention */
} CNTLR_IFN;
#define NO_FUNCTIONS (CNTLR_IFN) 0 /* no functions are asserted */
typedef CNTLR_IFN CNTLR_IFN_SET; /* a set of CNTLR_IFNs */
typedef uint16 CNTLR_IBUS; /* the interface data bus */
#undef NO_DATA /* remove winsock definition */
#define NO_DATA (CNTLR_IBUS) 0 /* no data asserted */
typedef uint32 CNTLR_IFN_IBUS; /* a combined interface function set and data bus value */
/* Controller opcodes.
Each tape interface uses its own encoding for tape commands that must be
translated to the appropriate CNTLR_OPCODEs of the common set, typically via
a lookup table.
Implementation notes:
1. The Select Unit 0-3 opcodes must have contiguous values, so that the
value of the selected unit may be added to the Select_Unit_0 value to
obtain the correct opcode.
2. The Invalid Opcode must be the last enumeration value.
*/
typedef enum {
Select_Unit_0,
Select_Unit_1,
Select_Unit_2,
Select_Unit_3,
Clear_Controller,
Read_Record,
Read_Record_with_CRCC,
Read_Record_Backward,
Read_File_Forward,
Write_Record,
Write_Record_without_Parity,
Write_File_Mark,
Write_Gap,
Write_Gap_and_File_Mark,
Forward_Space_Record,
Forward_Space_File,
Backspace_Record,
Backspace_File,
Rewind,
Rewind_Offline,
Invalid_Opcode
} CNTLR_OPCODE;
/* Controller opcode classifications */
typedef enum {
Class_Invalid, /* invalid classification */
Class_Read, /* read classification */
Class_Write, /* write classification */
Class_Rewind, /* rewind classification */
Class_Control /* control classification */
} CNTLR_CLASS;
/* Controller execution states.
The controller is in the idle state while it is awaiting a command. It is in
the busy state while it is executing a command. The end and error states are
busy states in which a device end or a device error, respectively, has
occurred while executing the command. A device end occurs when a record
shorter than the requested length is read. A device error occurs when a
simulator tape support library routine returns an error (e.g., a read of a
tape mark or of a record that is marked bad), or a data overrun is detected
by the interface. In these cases, command execution completes normally, but
notification is given that the channel order or program should be aborted.
Implementation notes:
1. The error states (End_State and Error_State) must be numerically greater
than the non-error states (Idle_State and Busy_State).
*/
typedef enum {
Idle_State, /* idle */
Busy_State, /* busy */
End_State, /* device end */
Error_State /* device error */
} CNTLR_STATE;
/* Tape activation delays structure.
The simulation models the mechanical delays of the tape drive as timed events
that are scheduled by unit command phase transitions. For example, a tape
record read is modeled as a start phase, a data transfer phase, and a stop
phase. These correspond to the motion of the physical tape as it starts and
ramps up to speed while crossing the interrecord gap, passes the data record
over the read head, and then slows the tape to a stop in the next interrecord
gap. Separate structures contain the delays for realistic and optimized
timing for the various tape drive types.
The structure contains these fields:
rewind_start -- the time from rewind initiation to controller idle
rewind_rate -- the travel time per inch during rewinding
rewind_stop -- the time from BOT detection to load point search completion
bot_start -- the time starting from the BOT marker to the data block
ir_start -- the time starting from the IR gap to the data block
data_xfer -- the travel time from one data byte to the next
overhead -- the controller execution time from command to first motion
The bot_start and ir_start values include the drive start/stop time and the
traverse time across the initial gap and one-half of the interrecord gap,
respectively. The ir_start value doubles as the stop time.
*/
typedef struct {
int32 rewind_start; /* rewind initiation time */
int32 rewind_rate; /* rewind time per inch */
int32 rewind_stop; /* rewind completion time */
int32 bot_start; /* beginning of tape gap traverse time */
int32 ir_start; /* interrecord traverse time */
int32 data_xfer; /* per-byte data transfer time */
int32 overhead; /* controller execution overhead */
} DELAY_PROPS;
#define DELAY_INIT(rstart,rrate,rstop,bot,ir,dxfr,ovhd) \
(rstart), (rrate), (rstop), (bot), (ir), (dxfr), (ovhd)
/* Tape controller state variable structure */
typedef struct {
CNTLR_TYPE type; /* controller type */
DEVICE *device; /* controlling device pointer */
CNTLR_STATE state; /* controller state */
uint32 status; /* controller status */
uint32 unit_selected; /* unit number currently selected */
uint32 unit_attention; /* bitmap of units needing attention */
uint8 *buffer; /* data buffer pointer */
t_stat call_status; /* simulator tape support library call status */
t_mtrlnt length; /* data buffer valid length */
t_mtrlnt index; /* data buffer current index */
t_mtrlnt gaplen; /* current record erase gap length */
t_addr initial_position; /* tape motion initial position */
DELAY_PROPS *fastptr; /* pointer to the FASTTIME delays */
const DELAY_PROPS *dlyptr; /* current delay property pointer */
} CNTLR_VARS;
typedef CNTLR_VARS *CVPTR; /* a pointer to a controller state variable structure */
/* Controller state variable structure initialization.
The supplied parameters are:
ctype - the type of the controller (CNTLR_TYPE)
dev - the device on which the controller operates (DEVICE)
bufptr - a pointer to the data buffer (array of uint8)
fast - a pointer to the fast timing values (DELAY_PROPS)
*/
#define CNTLR_INIT(ctype,dev,bufptr,fast) \
(ctype), &(dev), Idle_State, 0, 0, 0, \
(bufptr), MTSE_OK, 0, 0, 0, 0, \
&(fast), &(fast)
/* Tape controller device register definitions.
These definitions should be included AFTER any interface-specific registers.
The supplied parameters are:
cntlr -- the controller state variable structure (CNTLR_VARS)
units -- the unit array (array of UNIT)
numunits -- the number of tape drive units
buffer -- the buffer array (array of uint8)
times -- the structure containing the fast delay time values (DELAY_PROPS)
Implementation notes:
1. The "CNTLR" register is present to ensure that the entire CNTLR_VARS
structure is saved and restored.
*/
#define TL_REGS(cntlr,units,numunits,buffer,times) \
/* Macro Name Location Radix Width Depth Flags */ \
/* ------ ------ ------------------------ ----- -------- --------------- ----------------- */ \
{ DRDATA (CSTATE, (cntlr).state, 4), PV_LEFT | REG_RO }, \
{ ORDATA (STATUS, (cntlr).status, 16), REG_RO }, \
{ DRDATA (USEL, (cntlr).unit_selected, 4), PV_LEFT | REG_RO }, \
{ YRDATA (UATTN, (cntlr).unit_attention, 4) }, \
{ BRDATA (RECBUF, (buffer), 8, 8, TL_BUFSIZE), REG_A }, \
{ DRDATA (LIBSTA, (cntlr).call_status, 16), PV_LEFT }, \
{ DRDATA (LENGTH, (cntlr).length, 24), PV_LEFT }, \
{ DRDATA (INDEX, (cntlr).index, 24), PV_LEFT }, \
{ DRDATA (GAPLEN, (cntlr).gaplen, 32), PV_LEFT }, \
{ DRDATA (INPOS, (cntlr).initial_position, T_ADDR_W), PV_LEFT }, \
{ SRDATA (CNTLR, (cntlr)), REG_HRO }, \
\
/* Macro Name Location Width Flags */ \
/* ------ ------- -------------------- ----- ---------------- */ \
{ DRDATA (RSTART, (times).rewind_start, 24), PV_LEFT | REG_NZ }, \
{ DRDATA (RRATE, (times).rewind_rate, 24), PV_LEFT | REG_NZ }, \
{ DRDATA (RSTOP, (times).rewind_stop, 24), PV_LEFT | REG_NZ }, \
{ DRDATA (BTIME, (times).bot_start, 24), PV_LEFT | REG_NZ }, \
{ DRDATA (ITIME, (times).ir_start, 24), PV_LEFT | REG_NZ }, \
{ DRDATA (DTIME, (times).data_xfer, 24), PV_LEFT | REG_NZ }, \
{ DRDATA (OTIME, (times).overhead, 24), PV_LEFT | REG_NZ }, \
\
/* Macro Name Location Radix Width Offset Depth Flags */ \
/* ------ ------- ----------------- ----- -------- ------ ---------- ------------------ */ \
{ URDATA (UPROP, (units)[0].PROP, 8, 16, 0, (numunits), PV_RZRO) }, \
{ URDATA (USTATUS, (units)[0].STATUS, 2, 16, 0, (numunits), PV_RZRO) }, \
{ URDATA (UOPCODE, (units)[0].OPCODE, 10, 6, 0, (numunits), PV_LEFT | REG_RO) }, \
{ URDATA (USTATE, (units)[0].PHASE, 10, 4, 0, (numunits), PV_LEFT | REG_RO) }, \
{ URDATA (UPOS, (units)[0].pos, 10, T_ADDR_W, 0, (numunits), PV_LEFT | REG_RO) }, \
{ URDATA (UWAIT, (units)[0].wait, 10, 32, 0, (numunits), PV_LEFT | REG_HRO) }
/* Tape controller device modifier structure initialization.
This initialization should be included BEFORE any device-specific modifiers.
The supplied parameters are:
cntlr -- the controller state variable structure (CNTLR_VARS)
typeset -- the set of drive type flags supported by the interface
densset -- the set of drive density flags supported by the interface
offvalid -- the interface SET ONLINE/OFFLINE validation function
Implementation notes:
1. The IFTYPE and IFDENSITY macros test their respective flag sets and
enable or disable the associated modifier entries as indicated. An entry
is disabled by setting the print and match strings to NULL; this ensures
that the modifier is neither printed nor matched against any user input.
2. The UNIT_RO modifier displays "write ring" if the flag is not set. There
is no corresponding entry for the opposite condition because "read only"
is automatically printed after the attached filename.
*/
/* Selectable drive type flags */
#define TL_7970B (1 << HP_7970B)
#define TL_7970E (1 << HP_7970E)
#define TL_7974 (1 << HP_7974)
#define TL_7978 (1 << HP_7978)
/* Selectable drive density flags */
#define TL_FIXED 0
#define TL_800 (1 << MT_DENS_800)
#define TL_1600 (1 << MT_DENS_1600)
#define TL_6250 (1 << MT_DENS_6250)
#define IFTYPE(t,s) (TL_##t & (s) ? #t : NULL), \
(TL_##t & (s) ? #t : NULL)
#define IFDENSITY(s) ((s) ? "DENSITY" : NULL), \
((s) ? "DENSITY" : NULL)
#define TL_MODS(cntlr,typeset,densset,offvalid) \
/* Mask Value Match Value Print String Match String Validation Display Descriptor */ \
/* ----------- ----------- ------------ ------------ ------------- ------- ----------------- */ \
{ UNIT_MODEL, UNIT_7970B, IFTYPE (7970B, typeset), &tl_set_model, NULL, (void *) &(cntlr) }, \
{ UNIT_MODEL, UNIT_7970E, IFTYPE (7970E, typeset), &tl_set_model, NULL, (void *) &(cntlr) }, \
{ UNIT_MODEL, UNIT_7974, IFTYPE (7974, typeset), &tl_set_model, NULL, (void *) &(cntlr) }, \
{ UNIT_MODEL, UNIT_7978, IFTYPE (7978, typeset), &tl_set_model, NULL, (void *) &(cntlr) }, \
\
/* Entry Flags Value Print String Match String Validation Display Descriptor */ \
/* ------------------- ----- ------------ ------------ ---------------- ----------------- ----------------- */ \
{ MTAB_XUN, 0, IFDENSITY (densset), &tl_set_density, &tl_show_density, (void *) &(cntlr) }, \
{ MTAB_XUN, 0, "CAPACITY", "CAPACITY", &tl_set_reelsize, &tl_show_reelsize, NULL }, \
{ MTAB_XUN | MTAB_NMO, 1, "REEL", "REEL", &tl_set_reelsize, &tl_show_reelsize, NULL }, \
\
/* Mask Value Match Value Print String Match String Validation Display Descriptor */ \
/* ------------ ------------ ------------ ------------ ----------- ------- ----------------- */ \
{ UNIT_OFFLINE, UNIT_OFFLINE, "offline", "OFFLINE", &(offvalid), NULL, NULL }, \
{ UNIT_OFFLINE, 0, "online", "ONLINE", &(offvalid), NULL, (void *) &(cntlr) }, \
\
{ UNIT_RO, 0, "write ring", NULL, NULL, NULL, NULL }, \
\
/* Entry Flags Value Print String Match String Validation Display Descriptor */ \
/* ------------ ----- ------------ ------------ ----------------- ------------------ ----------------- */ \
{ MTAB_XDV, 0, "TIMING", "FASTTIME", &tl_set_timing, &tl_show_timing, (void *) &(cntlr) }, \
{ MTAB_XDV, 1, NULL, "REALTIME", &tl_set_timing, NULL, (void *) &(cntlr) }, \
\
{ MTAB_XUN, 0, "FORMAT", "FORMAT", &sim_tape_set_fmt, &sim_tape_show_fmt, NULL }
/* Tape library global controller routines */
extern CNTLR_IFN_IBUS tl_controller (CVPTR cvptr, UNIT *uptr, CNTLR_FLAG_SET flags, CNTLR_IBUS data);
extern t_stat tl_onoffline (CVPTR cvptr, UNIT *uptr, t_bool online);
extern uint16 tl_status (CVPTR cvptr);
extern t_stat tl_reset (CVPTR cvptr);
extern void tl_clear (CVPTR cvptr);
/* Tape library global utility routines */
extern const char *tl_opcode_name (CNTLR_OPCODE opcode);
extern const char *tl_unit_name (uint32 unit);
/* Tape library global SCP support routines */
extern t_stat tl_attach (CVPTR cvptr, UNIT *uptr, char *cptr);
extern t_stat tl_detach (UNIT *uptr);
extern t_stat tl_set_timing (UNIT *uptr, int32 value, char *cptr, void *desc);
extern t_stat tl_set_model (UNIT *uptr, int32 value, char *cptr, void *desc);
extern t_stat tl_set_density (UNIT *uptr, int32 value, char *cptr, void *desc);
extern t_stat tl_set_reelsize (UNIT *uptr, int32 value, char *cptr, void *desc);
extern t_stat tl_show_timing (FILE *st, UNIT *uptr, int32 value, void *desc);
extern t_stat tl_show_density (FILE *st, UNIT *uptr, int32 value, void *desc);
extern t_stat tl_show_reelsize (FILE *st, UNIT *uptr, int32 value, void *desc);

View file

@ -20,6 +20,8 @@
#### Richard Cornwell has implemented a Burroughs B5500 simulator. #### Richard Cornwell has implemented a Burroughs B5500 simulator.
#### Dave Bryan has implemented an HP-3000 Series III simulator.
#### Updated AltairZ80 simulator from Peter Schorn. #### Updated AltairZ80 simulator from Peter Schorn.
#### Updated HP2100 simulator from Dave Bryan. #### Updated HP2100 simulator from Dave Bryan.

View file

@ -0,0 +1,385 @@
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="9.00"
Name="HP3000"
ProjectGUID="{B3E35063-CB41-4F77-BFCA-49BB316B0EDB}"
RootNamespace="HP3000"
Keyword="Win32Proj"
TargetFrameworkVersion="131072"
>
<Platforms>
<Platform
Name="Win32"
/>
</Platforms>
<ToolFiles>
</ToolFiles>
<Configurations>
<Configuration
Name="Debug|Win32"
OutputDirectory="..\BIN\NT\$(PlatformName)-$(ConfigurationName)"
IntermediateDirectory="..\BIN\NT\Project\simh\$(ProjectName)\$(PlatformName)-$(ConfigurationName)"
ConfigurationType="1"
InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"
CharacterSet="0"
>
<Tool
Name="VCPreBuildEventTool"
Description="Check for required build dependencies &amp; git commit id"
CommandLine="Pre-Build-Event.cmd LIBPCRE"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories="./;../;../HP3000/;&quot;../../windows-build/PCRE/include/&quot;"
PreprocessorDefinitions="HAVE_INT64;_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;_WINSOCK_DEPRECATED_NO_WARNINGS;SIM_NEED_GIT_COMMIT_ID;HAVE_PCREPOSIX_H;PCRE_STATIC"
KeepComments="false"
MinimalRebuild="true"
BasicRuntimeChecks="0"
RuntimeLibrary="1"
UsePrecompiledHeader="0"
WarningLevel="3"
DebugInformationFormat="3"
CompileAs="1"
ShowIncludes="false"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="wsock32.lib winmm.lib pcrestaticd.lib pcreposixstaticd.lib"
LinkIncremental="2"
AdditionalLibraryDirectories="../../windows-build/PCRE/lib/"
GenerateDebugInformation="true"
SubSystem="1"
StackReserveSize="10485760"
StackCommitSize="10485760"
RandomizedBaseAddress="1"
DataExecutionPrevention="0"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory="..\BIN\NT\$(PlatformName)-$(ConfigurationName)"
IntermediateDirectory="..\BIN\NT\Project\simh\$(ProjectName)\$(PlatformName)-$(ConfigurationName)"
ConfigurationType="1"
InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"
CharacterSet="0"
>
<Tool
Name="VCPreBuildEventTool"
Description="Check for required build dependencies &amp; git commit id"
CommandLine="Pre-Build-Event.cmd LIBPCRE"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="2"
InlineFunctionExpansion="1"
OmitFramePointers="true"
AdditionalIncludeDirectories="./;../;../HP3000/;&quot;../../windows-build/PCRE/include/&quot;"
PreprocessorDefinitions="HAVE_INT64;_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;_WINSOCK_DEPRECATED_NO_WARNINGS;SIM_NEED_GIT_COMMIT_ID;HAVE_PCREPOSIX_H;PCRE_STATIC"
StringPooling="true"
RuntimeLibrary="0"
EnableFunctionLevelLinking="true"
UsePrecompiledHeader="0"
WarningLevel="3"
DebugInformationFormat="3"
CompileAs="1"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="wsock32.lib winmm.lib pcrestatic.lib pcreposixstatic.lib"
LinkIncremental="1"
AdditionalLibraryDirectories="../../windows-build/PCRE/lib/"
GenerateDebugInformation="false"
SubSystem="1"
StackReserveSize="10485760"
StackCommitSize="10485760"
OptimizeReferences="2"
EnableCOMDATFolding="2"
RandomizedBaseAddress="1"
DataExecutionPrevention="0"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<Filter
Name="Source Files"
Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm"
>
<File
RelativePath="..\HP3000\hp3000_atc.c"
>
</File>
<File
RelativePath="..\HP3000\hp3000_clk.c"
>
</File>
<File
RelativePath="..\HP3000\hp3000_cpu.c"
>
</File>
<File
RelativePath="..\HP3000\hp3000_cpu_base.c"
>
</File>
<File
RelativePath="..\HP3000\hp3000_cpu_fp.c"
>
</File>
<File
RelativePath="..\HP3000\hp3000_ds.c"
>
</File>
<File
RelativePath="..\HP3000\hp3000_iop.c"
>
</File>
<File
RelativePath="..\HP3000\hp3000_mpx.c"
>
</File>
<File
RelativePath="..\HP3000\hp3000_ms.c"
>
</File>
<File
RelativePath="..\HP3000\hp3000_scmb.c"
>
</File>
<File
RelativePath="..\HP3000\hp3000_sel.c"
>
</File>
<File
RelativePath="..\HP3000\hp3000_sys.c"
>
</File>
<File
RelativePath="..\HP3000\hp_disclib.c"
>
</File>
<File
RelativePath="..\HP3000\hp_tapelib.c"
>
</File>
<File
RelativePath="..\scp.c"
>
</File>
<File
RelativePath="..\sim_console.c"
>
</File>
<File
RelativePath="..\sim_disk.c"
>
</File>
<File
RelativePath="..\sim_ether.c"
>
</File>
<File
RelativePath="..\sim_fio.c"
>
</File>
<File
RelativePath="..\sim_serial.c"
>
</File>
<File
RelativePath="..\sim_sock.c"
>
</File>
<File
RelativePath="..\sim_tape.c"
>
</File>
<File
RelativePath="..\sim_timer.c"
>
</File>
<File
RelativePath="..\sim_tmxr.c"
>
</File>
<File
RelativePath="..\sim_video.c"
>
</File>
</Filter>
<Filter
Name="Header Files"
Filter="h;hpp;hxx;hm;inl;inc"
>
<File
RelativePath="..\HP3000\hp3000_cpu.h"
>
</File>
<File
RelativePath="..\HP3000\hp3000_cpu_fp.h"
>
</File>
<File
RelativePath="..\HP3000\hp3000_cpu_ims.h"
>
</File>
<File
RelativePath="..\HP3000\hp3000_defs.h"
>
</File>
<File
RelativePath="..\HP3000\hp3000_io.h"
>
</File>
<File
RelativePath="..\HP3000\hp_disclib.h"
>
</File>
<File
RelativePath="..\HP3000\hp_tapelib.h"
>
</File>
<File
RelativePath="..\scp.h"
>
</File>
<File
RelativePath="..\sim_console.h"
>
</File>
<File
RelativePath="..\sim_defs.h"
>
</File>
<File
RelativePath="..\sim_disk.h"
>
</File>
<File
RelativePath="..\sim_ether.h"
>
</File>
<File
RelativePath="..\sim_fio.h"
>
</File>
<File
RelativePath="..\sim_rev.h"
>
</File>
<File
RelativePath="..\sim_serial.h"
>
</File>
<File
RelativePath="..\sim_sock.h"
>
</File>
<File
RelativePath="..\sim_tape.h"
>
</File>
<File
RelativePath="..\sim_timer.h"
>
</File>
<File
RelativePath="..\sim_tmxr.h"
>
</File>
<File
RelativePath="..\sim_video.h"
>
</File>
</Filter>
<Filter
Name="Resource Files"
Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
>
</Filter>
</Files>
<Globals>
</Globals>
</VisualStudioProject>

View file

@ -223,6 +223,11 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "B5500", "B5500.vcproj", "{B
{D40F3AF1-EEE7-4432-9807-2AD287B490F8} = {D40F3AF1-EEE7-4432-9807-2AD287B490F8} {D40F3AF1-EEE7-4432-9807-2AD287B490F8} = {D40F3AF1-EEE7-4432-9807-2AD287B490F8}
EndProjectSection EndProjectSection
EndProject EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "HP3000", "HP3000.vcproj", "{B3E35063-CB41-4F77-BFCA-49BB316B0EDB}"
ProjectSection(ProjectDependencies) = postProject
{D40F3AF1-EEE7-4432-9807-2AD287B490F8} = {D40F3AF1-EEE7-4432-9807-2AD287B490F8}
EndProjectSection
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32 Debug|Win32 = Debug|Win32
@ -409,6 +414,10 @@ Global
{BA514FA1-D029-4D25-91CC-255E5953FF6E}.Debug|Win32.Build.0 = Debug|Win32 {BA514FA1-D029-4D25-91CC-255E5953FF6E}.Debug|Win32.Build.0 = Debug|Win32
{BA514FA1-D029-4D25-91CC-255E5953FF6E}.Release|Win32.ActiveCfg = Release|Win32 {BA514FA1-D029-4D25-91CC-255E5953FF6E}.Release|Win32.ActiveCfg = Release|Win32
{BA514FA1-D029-4D25-91CC-255E5953FF6E}.Release|Win32.Build.0 = Release|Win32 {BA514FA1-D029-4D25-91CC-255E5953FF6E}.Release|Win32.Build.0 = Release|Win32
{B3E35063-CB41-4F77-BFCA-49BB316B0EDB}.Debug|Win32.ActiveCfg = Debug|Win32
{B3E35063-CB41-4F77-BFCA-49BB316B0EDB}.Debug|Win32.Build.0 = Debug|Win32
{B3E35063-CB41-4F77-BFCA-49BB316B0EDB}.Release|Win32.ActiveCfg = Release|Win32
{B3E35063-CB41-4F77-BFCA-49BB316B0EDB}.Release|Win32.Build.0 = Release|Win32
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE

View file

@ -25,6 +25,7 @@
# LGP Just Build The Royal-McBee LGP-30. # LGP Just Build The Royal-McBee LGP-30.
# H316 Just Build The Honewell 316/516. # H316 Just Build The Honewell 316/516.
# HP2100 Just Build The Hewlett-Packard HP-2100. # HP2100 Just Build The Hewlett-Packard HP-2100.
# HP3000 Just Build The Hewlett-Packard HP-3000.
# I1401 Just Build The IBM 1401. # I1401 Just Build The IBM 1401.
# I1620 Just Build The IBM 1620. # I1620 Just Build The IBM 1620.
# I7094 Just Build The IBM 7094. # I7094 Just Build The IBM 7094.
@ -423,6 +424,26 @@ HP2100_OPTIONS = /INCL=($(SIMH_DIR),$(HP2100_DIR))\
HP2100_OPTIONS = /INCL=($(SIMH_DIR),$(HP2100_DIR))/DEF=($(CC_DEFS)) HP2100_OPTIONS = /INCL=($(SIMH_DIR),$(HP2100_DIR))/DEF=($(CC_DEFS))
.ENDIF .ENDIF
#
# Hewlett-Packard HP-3000 Simulator Definitions.
#
HP3000_DIR = SYS$DISK:[.HP3000]
HP3000_LIB1 = $(LIB_DIR)HP3000L1-$(ARCH).OLB
HP3000_SOURCE1 = $(HP3000_DIR)HP3000_ATC.C,$(HP3000_DIR)HP3000_CLK.C,\
$(HP3000_DIR)HP3000_CPU.C,$(HP3000_DIR)HP3000_CPU_BASE.C,\
$(HP3000_DIR)HP3000_CPU_FP.C,$(HP3000_DIR)HP3000_DS.C,\
$(HP3000_DIR)HP3000_IOP.C,$(HP3000_DIR)HP3000_MPX.C,\
$(HP3000_DIR)HP3000_MS.C,$(HP3000_DIR)HP3000_SCMB.C,\
$(HP3000_DIR)HP3000_SEL.C,$(HP3000_DIR)HP3000_SYS.C
HP3000_LIB2 = $(LIB_DIR)HP3000L2-$(ARCH).OLB
HP3000_SOURCE2 = $(HP3000_DIR)HP_TAPELIB.C,$(HP3000_DIR)HP_DISCLIB.C
.IFDEF ALPHA_OR_IA64
HP3000_OPTIONS = /INCL=($(SIMH_DIR),$(HP3000_DIR))\
/DEF=($(CC_DEFS),"HAVE_INT64=1")
.ELSE
HP3000_OPTIONS = /INCL=($(SIMH_DIR),$(HP3000_DIR))/DEF=($(CC_DEFS))
.ENDIF
# #
# Interdata 16-bit CPU. # Interdata 16-bit CPU.
# #
@ -911,7 +932,7 @@ I7094_OPTIONS = /INCL=($(SIMH_DIR),$(I7094_DIR))/DEF=($(CC_DEFS))
# If we're not a VAX, Build Everything # If we're not a VAX, Build Everything
# #
.IFDEF ALPHA_OR_IA64 .IFDEF ALPHA_OR_IA64
ALL : ALTAIR ALTAIRZ80 ECLIPSE GRI LGP H316 HP2100 I1401 I1620 IBM1130 ID16 \ ALL : ALTAIR ALTAIRZ80 ECLIPSE GRI LGP H316 HP2100 HP3000 I1401 I1620 IBM1130 ID16 \
ID32 NOVA PDP1 PDP4 PDP7 PDP8 PDP9 PDP10 PDP11 PDP15 S3 \ ID32 NOVA PDP1 PDP4 PDP7 PDP8 PDP9 PDP10 PDP11 PDP15 S3 \
VAX MICROVAX3900 MICROVAX1 RTVAX1000 MICROVAX2 VAX730 VAX750 VAX780 VAX8600 \ VAX MICROVAX3900 MICROVAX1 RTVAX1000 MICROVAX2 VAX730 VAX750 VAX780 VAX8600 \
SDS I7094 SWTP6800MP-A SWTP6800MP-A2 SSEM BESM6 B5500 SDS I7094 SWTP6800MP-A SWTP6800MP-A2 SSEM BESM6 B5500
@ -1107,6 +1128,28 @@ $(HP2100_LIB2) : $(HP2100_SOURCE2)
$ LIBRARY/REPLACE $(MMS$TARGET) $(BLD_DIR)*.OBJ $ LIBRARY/REPLACE $(MMS$TARGET) $(BLD_DIR)*.OBJ
$ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;*
$(HP3000_LIB1) : $(HP3000_SOURCE1)
$!
$! Building The $(HP3000_LIB1) Library.
$!
$ $(CC)$(HP3000_OPTIONS) -
/OBJ=$(BLD_DIR) $(MMS$CHANGED_LIST)
$ IF (F$SEARCH("$(MMS$TARGET)").EQS."") THEN -
LIBRARY/CREATE $(MMS$TARGET)
$ LIBRARY/REPLACE $(MMS$TARGET) $(BLD_DIR)*.OBJ
$ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;*
$(HP3000_LIB2) : $(HP3000_SOURCE2)
$!
$! Building The $(HP3000_LIB2) Library.
$!
$ $(CC)$(HP3000_OPTIONS) -
/OBJ=$(BLD_DIR) $(MMS$CHANGED_LIST)
$ IF (F$SEARCH("$(MMS$TARGET)").EQS."") THEN -
LIBRARY/CREATE $(MMS$TARGET)
$ LIBRARY/REPLACE $(MMS$TARGET) $(BLD_DIR)*.OBJ
$ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;*
$(I1401_LIB) : $(I1401_SOURCE) $(I1401_LIB) : $(I1401_SOURCE)
$! $!
$! Building The $(I1401_LIB) Library. $! Building The $(I1401_LIB) Library.
@ -1706,6 +1749,19 @@ $(BIN_DIR)HP2100-$(ARCH).EXE : $(SIMH_MAIN) $(SIMH_NONET_LIB) $(HP2100_LIB1) $(H
$(HP2100_LIB2)/LIBRARY,$(SIMH_NONET_LIB)/LIBRARY $(HP2100_LIB2)/LIBRARY,$(SIMH_NONET_LIB)/LIBRARY
$ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;*
HP3000 : $(BIN_DIR)HP3000-$(ARCH).EXE
$! HP3000 done
$(BIN_DIR)HP3000-$(ARCH).EXE : $(SIMH_MAIN) $(SIMH_NONET_LIB) $(HP3000_LIB1) $(HP3000_LIB2)
$!
$! Building The $(BIN_DIR)HP3000-$(ARCH).EXE Simulator.
$!
$ $(CC)$(HP3000_OPTIONS)/OBJ=$(BLD_DIR) SCP.C
$ LINK $(LINK_DEBUG)/EXE=$(BIN_DIR)HP3000-$(ARCH).EXE -
$(BLD_DIR)SCP.OBJ,$(HP3000_LIB1)/LIBRARY, -
$(HP3000_LIB2)/LIBRARY,$(SIMH_NONET_LIB)/LIBRARY
$ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;*
I1401 : $(BIN_DIR)I1401-$(ARCH).EXE I1401 : $(BIN_DIR)I1401-$(ARCH).EXE
$! I1401 done $! I1401 done

BIN
doc/hp3000_doc.doc Normal file

Binary file not shown.

View file

@ -1101,6 +1101,14 @@ HP2100 = ${HP2100D}/hp2100_stddev.c ${HP2100D}/hp2100_dp.c ${HP2100D}/hp2100_dq.
${HP2100D}/hp2100_di_da.c ${HP2100D}/hp2100_disclib.c ${HP2100D}/hp2100_di_da.c ${HP2100D}/hp2100_disclib.c
HP2100_OPT = -DHAVE_INT64 -I ${HP2100D} HP2100_OPT = -DHAVE_INT64 -I ${HP2100D}
HP3000D = HP3000
HP3000 = ${HP3000D}/hp_disclib.c ${HP3000D}/hp_tapelib.c ${HP3000D}/hp3000_atc.c \
${HP3000D}/hp3000_clk.c ${HP3000D}/hp3000_cpu.c ${HP3000D}/hp3000_cpu_base.c \
${HP3000D}/hp3000_cpu_fp.c ${HP3000D}/hp3000_ds.c ${HP3000D}/hp3000_iop.c \
${HP3000D}/hp3000_mpx.c ${HP3000D}/hp3000_ms.c \
${HP3000D}/hp3000_scmb.c ${HP3000D}/hp3000_sel.c ${HP3000D}/hp3000_sys.c
HP3000_OPT = -I ${HP3000D}
I1401D = I1401 I1401D = I1401
I1401 = ${I1401D}/i1401_lp.c ${I1401D}/i1401_cpu.c ${I1401D}/i1401_iq.c \ I1401 = ${I1401D}/i1401_lp.c ${I1401D}/i1401_cpu.c ${I1401D}/i1401_iq.c \
@ -1340,7 +1348,7 @@ PDQ3_OPT = -I ${PDQ3D} -DUSE_SIM_IMD
# #
ALL = pdp1 pdp4 pdp7 pdp8 pdp9 pdp15 pdp11 pdp10 \ ALL = pdp1 pdp4 pdp7 pdp8 pdp9 pdp15 pdp11 pdp10 \
vax microvax3900 microvax1 rtvax1000 microvax2 vax730 vax750 vax780 vax8600 \ vax microvax3900 microvax1 rtvax1000 microvax2 vax730 vax750 vax780 vax8600 \
nova eclipse hp2100 i1401 i1620 s3 altair altairz80 gri \ nova eclipse hp2100 hp3000 i1401 i1620 s3 altair altairz80 gri \
i7094 ibm1130 id16 id32 sds lgp h316 \ i7094 ibm1130 id16 id32 sds lgp h316 \
swtp6800mp-a swtp6800mp-a2 tx-0 ssem isys8010 isys8020 \ swtp6800mp-a swtp6800mp-a2 tx-0 ssem isys8010 isys8020 \
b5500 b5500
@ -1503,6 +1511,12 @@ ${BIN}hp2100${EXE} : ${HP2100} ${SIM}
${MKDIRBIN} ${MKDIRBIN}
${CC} ${HP2100} ${SIM} ${HP2100_OPT} $(CC_OUTSPEC) ${LDFLAGS} ${CC} ${HP2100} ${SIM} ${HP2100_OPT} $(CC_OUTSPEC) ${LDFLAGS}
hp3000 : ${BIN}hp3000${EXE}
${BIN}hp3000${EXE} : ${HP3000} ${SIM}
${MKDIRBIN}
${CC} ${HP3000} ${SIM} ${HP3000_OPT} $(CC_OUTSPEC) ${LDFLAGS}
i1401 : ${BIN}i1401${EXE} i1401 : ${BIN}i1401${EXE}
${BIN}i1401${EXE} : ${I1401} ${SIM} ${BIN}i1401${EXE} : ${I1401} ${SIM}