HP3000: Initial release of the HP 3000 Series III simulator (from Dave Bryan)
This commit is contained in:
parent
53b6809e4a
commit
3a4e879c07
29 changed files with 36616 additions and 2 deletions
2849
HP3000/hp3000_atc.c
Normal file
2849
HP3000/hp3000_atc.c
Normal file
File diff suppressed because it is too large
Load diff
929
HP3000/hp3000_clk.c
Normal file
929
HP3000/hp3000_clk.c
Normal 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
4033
HP3000/hp3000_cpu.c
Normal file
File diff suppressed because it is too large
Load diff
992
HP3000/hp3000_cpu.h
Normal file
992
HP3000/hp3000_cpu.h
Normal 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
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
891
HP3000/hp3000_cpu_fp.c
Normal 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
111
HP3000/hp3000_cpu_fp.h
Normal 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
200
HP3000/hp3000_cpu_ims.h
Normal 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
536
HP3000/hp3000_defs.h
Normal 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
1564
HP3000/hp3000_diag.txt
Normal file
File diff suppressed because it is too large
Load diff
1343
HP3000/hp3000_ds.c
Normal file
1343
HP3000/hp3000_ds.c
Normal file
File diff suppressed because it is too large
Load diff
363
HP3000/hp3000_io.h
Normal file
363
HP3000/hp3000_io.h
Normal 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
844
HP3000/hp3000_iop.c
Normal 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
1941
HP3000/hp3000_mpx.c
Normal file
File diff suppressed because it is too large
Load diff
1337
HP3000/hp3000_ms.c
Normal file
1337
HP3000/hp3000_ms.c
Normal file
File diff suppressed because it is too large
Load diff
213
HP3000/hp3000_release.txt
Normal file
213
HP3000/hp3000_release.txt
Normal 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
1222
HP3000/hp3000_scmb.c
Normal file
File diff suppressed because it is too large
Load diff
1319
HP3000/hp3000_sel.c
Normal file
1319
HP3000/hp3000_sel.c
Normal file
File diff suppressed because it is too large
Load diff
2908
HP3000/hp3000_sys.c
Normal file
2908
HP3000/hp3000_sys.c
Normal file
File diff suppressed because it is too large
Load diff
4286
HP3000/hp_disclib.c
Normal file
4286
HP3000/hp_disclib.c
Normal file
File diff suppressed because it is too large
Load diff
551
HP3000/hp_disclib.h
Normal file
551
HP3000/hp_disclib.h
Normal 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
3176
HP3000/hp_tapelib.c
Normal file
File diff suppressed because it is too large
Load diff
537
HP3000/hp_tapelib.h
Normal file
537
HP3000/hp_tapelib.h
Normal 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);
|
|
@ -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.
|
||||||
|
|
385
Visual Studio Projects/HP3000.vcproj
Normal file
385
Visual Studio Projects/HP3000.vcproj
Normal 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 & 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/;"../../windows-build/PCRE/include/""
|
||||||
|
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 & 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/;"../../windows-build/PCRE/include/""
|
||||||
|
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>
|
|
@ -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
|
||||||
|
|
58
descrip.mms
58
descrip.mms
|
@ -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
BIN
doc/hp3000_doc.doc
Normal file
Binary file not shown.
16
makefile
16
makefile
|
@ -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}
|
||||||
|
|
Loading…
Add table
Reference in a new issue