1
0
mirror of https://github.com/open-simh/simh.git synced 2026-01-11 23:53:30 +00:00

HP3000: Release 9

This commit is contained in:
J. David Bryan 2021-01-19 19:30:07 -08:00 committed by Mark Pizzolato
parent 0e119d70bb
commit 6da2ce719e
14 changed files with 9781 additions and 6659 deletions

View File

@ -26,6 +26,9 @@
ATCD,ATCC HP 30032B Asynchronous Terminal Controller
27-Oct-20 JDB Added the SET LINEORDER option
26-Oct-20 JDB Line order now leaves out channel 0
25-Aug-20 JDB Reset routine now sets up VM unit pointer hooks
08-Jan-20 JDB Revised modem control operation
09-Dec-19 JDB Replaced debugging macros with tracing macros
17-Jun-19 JDB Now detaches all TDI lines when entering DIAGNOSTIC mode
@ -817,13 +820,11 @@ DEVICE atcc_dev; /* incomplete device structure *
dedicated to the system console. For convenience, the system console is
connected to the simulation console. As such, it calls the console I/O
routines instead of the terminal multiplexer routines.
User-defined line order is not supported.
*/
static int32 atcd_order [TERM_COUNT] = { /* line connection order */
1, 1, 2, 3, 4, 5, 6, 7,
8, 9, 10, 11, 12, 13, 14, 15 };
1, 2, 3, 4, 5, 6, 7, 8,
9, 10, 11, 12, 13, 14, 15, -1 };
static TMLN atcd_ldsc [TERM_COUNT] = { /* line descriptors */
{ 0 }
@ -915,10 +916,6 @@ static UNIT atcd_unit [UNIT_COUNT] = {
{ UDATA (&poll_service, UNIT_ATTABLE | UNIT_DIS | UNIT_IDLE, 0), POLL_TIME } /* multiplexer poll unit */
};
UNIT *vm_console_input_unit = &atcd_unit [0]; /* console input unit pointer */
UNIT *vm_console_output_unit = &atcd_unit [0]; /* console output unit pointer */
static UNIT atcc_unit [] = { /* a dummy unit to satisfy SCP requirements */
{ UDATA (NULL, 0, 0) }
};
@ -987,7 +984,16 @@ static REG atcc_reg [] = {
};
/* Modifier lists */
/* Modifier lists.
Implementation notes:
1. User-specified line connection orders are supported, but the LINEORDER
modifier entry "value" field restricts the minimum allowed line number
to 1, so that line 0 (the system console) cannot be included in the
connection list.
*/
typedef enum {
Fast_Time,
@ -1014,28 +1020,29 @@ static MTAB atcd_mod [] = {
{ TT_MODE, TT_MODE_7P, "7p output", "7P", NULL, NULL, NULL },
{ TT_MODE, TT_MODE_8B, "8b output", "8B", NULL, NULL, NULL },
/* Entry Flags Value Print String Match String Validation Display Descriptor */
/* -------------------- ----------- ------------- ------------ --------------- ---------------- ------------------- */
{ MTAB_XUN | MTAB_NC, 0, "LOG", "LOG", &tmxr_set_log, &tmxr_show_log, (void *) &atcd_mdsc },
{ MTAB_XUN | MTAB_NC, 0, NULL, "NOLOG", &tmxr_set_nolog, NULL, (void *) &atcd_mdsc },
{ MTAB_XUN, 0, NULL, "DISCONNECT", &tmxr_dscln, NULL, (void *) &atcd_mdsc },
/* Entry Flags Value Print String Match String Validation Display Descriptor */
/* -------------------- ----------- ------------- ------------ ----------------- ------------------ ------------------- */
{ MTAB_XUN | MTAB_NC, 0, "LOG", "LOG", &tmxr_set_log, &tmxr_show_log, (void *) &atcd_mdsc },
{ MTAB_XUN | MTAB_NC, 0, NULL, "NOLOG", &tmxr_set_nolog, NULL, (void *) &atcd_mdsc },
{ MTAB_XUN, 0, NULL, "DISCONNECT", &tmxr_dscln, NULL, (void *) &atcd_mdsc },
{ MTAB_XDV, Fast_Time, NULL, "FASTTIME", &atc_set_mode, NULL, (void *) &atcd_dev },
{ MTAB_XDV, Real_Time, NULL, "REALTIME", &atc_set_mode, NULL, (void *) &atcd_dev },
{ MTAB_XDV, Terminal, NULL, "TERMINAL", &atc_set_mode, NULL, (void *) &atcd_dev },
{ MTAB_XDV, Diagnostic, NULL, "DIAGNOSTIC", &atc_set_mode, NULL, (void *) &atcd_dev },
{ MTAB_XDV, 0, "MODES", NULL, NULL, &atc_show_mode, (void *) &atcd_dev },
{ MTAB_XDV, Fast_Time, NULL, "FASTTIME", &atc_set_mode, NULL, (void *) &atcd_dev },
{ MTAB_XDV, Real_Time, NULL, "REALTIME", &atc_set_mode, NULL, (void *) &atcd_dev },
{ MTAB_XDV, Terminal, NULL, "TERMINAL", &atc_set_mode, NULL, (void *) &atcd_dev },
{ MTAB_XDV, Diagnostic, NULL, "DIAGNOSTIC", &atc_set_mode, NULL, (void *) &atcd_dev },
{ MTAB_XDV, 0, "MODES", NULL, NULL, &atc_show_mode, (void *) &atcd_dev },
{ MTAB_XDV, 0, "", NULL, NULL, &atc_show_status, (void *) &atcd_mdsc },
{ MTAB_XDV | MTAB_NMO, 1, "CONNECTIONS", NULL, NULL, &tmxr_show_cstat, (void *) &atcd_mdsc },
{ MTAB_XDV | MTAB_NMO, 0, "STATISTICS", NULL, NULL, &tmxr_show_cstat, (void *) &atcd_mdsc },
{ MTAB_XDV, 0, "", NULL, NULL, &atc_show_status, (void *) &atcd_mdsc },
{ MTAB_XDV | MTAB_NMO, 1, "CONNECTIONS", NULL, NULL, &tmxr_show_cstat, (void *) &atcd_mdsc },
{ MTAB_XDV | MTAB_NMO, 0, "STATISTICS", NULL, NULL, &tmxr_show_cstat, (void *) &atcd_mdsc },
{ MTAB_XDV | MTAB_NMO, 1, "LINEORDER", "LINEORDER", &tmxr_set_lnorder, &tmxr_show_lnorder, (void *) &atcd_mdsc },
{ MTAB_XDV, VAL_DEVNO, "DEVNO", "DEVNO", &hp_set_dib, &hp_show_dib, (void *) &atcd_dib },
{ MTAB_XDV, VAL_INTMASK, "INTMASK", "INTMASK", &hp_set_dib, &hp_show_dib, (void *) &atcd_dib },
{ MTAB_XDV, VAL_INTPRI, "INTPRI", "INTPRI", &hp_set_dib, &hp_show_dib, (void *) &atcd_dib },
{ MTAB_XDV, VAL_DEVNO, "DEVNO", "DEVNO", &hp_set_dib, &hp_show_dib, (void *) &atcd_dib },
{ MTAB_XDV, VAL_INTMASK, "INTMASK", "INTMASK", &hp_set_dib, &hp_show_dib, (void *) &atcd_dib },
{ MTAB_XDV, VAL_INTPRI, "INTPRI", "INTPRI", &hp_set_dib, &hp_show_dib, (void *) &atcd_dib },
{ MTAB_XDV | MTAB_NMO, 1, NULL, "ENABLED", &atc_set_endis, NULL, NULL },
{ MTAB_XDV | MTAB_NMO, 0, NULL, "DISABLED", &atc_set_endis, NULL, NULL },
{ MTAB_XDV | MTAB_NMO, 1, NULL, "ENABLED", &atc_set_endis, NULL, NULL },
{ MTAB_XDV | MTAB_NMO, 0, NULL, "DISABLED", &atc_set_endis, NULL, NULL },
{ 0 }
};
@ -1745,6 +1752,9 @@ if (sim_switches & SWMASK ('P')) { /* if this is a power-on
sim_rtcn_init (poll_unit.wait, TMR_ATC); /* then initialize the poll timer */
fast_data_time = FAST_IO_TIME; /* restore the initial fast data time */
atcd_ldsc [0].xmte = 1; /* enable transmission on the system console port */
vm_console_input_unit = &atcd_unit [0]; /* set up the console input */
vm_console_output_unit = &atcd_unit [0]; /* and console output unit pointers */
}
if (atc_is_polling) { /* if we're polling for the simulation console */

View File

@ -1,6 +1,6 @@
/* hp3000_cpu.c: HP 3000 Central Processing Unit simulator
Copyright (c) 2016-2019, J. David Bryan
Copyright (c) 2016-2020, 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
@ -25,6 +25,11 @@
CPU HP 3000 Series III Central Processing Unit
11-Oct-20 JDB Moved UNIT_OPTS to hp3000_cpu.h for updating ease
24-Sep-20 JDB Traps now trace the register set
05-Sep-20 JDB Enabled EIS option
26-Aug-20 JDB Corrected line ends for trace to stdout
04-Jul-20 JDB Postlude trace now calls "sim_error_text" unconditionally
09-Dec-19 JDB Replaced debugging macros with tracing macros
20-Jun-19 JDB Added wait time to process clock trace
08-Apr-19 JDB Suppress stop messages for step and breakpoints in DO files
@ -744,8 +749,6 @@
#define PCLK_MULTIPLIER 10 /* number of hardware process clock ticks per service */
#define PCLK_RATE (1000 / PCLK_MULTIPLIER) /* process clock rate in ticks per second */
#define UNIT_OPTS (UNIT_EIS) /* the standard equipment feature set */
#define CPU_IO_RESET 0 /* reset CPU and all I/O devices */
#define IO_RESET 1 /* reset just the I/O devices */
@ -958,10 +961,9 @@ static const char *const trap_name [] = { /* trap names, indexed by TRAP_C
Implementation notes:
1. The EIS was standard equipment for the Series II and III, so UNIT_EIS
should appear in their "typ" fields. However, the EIS instructions are
not currently implemented, so the value is omitted below.
*/
1. The EIS was standard equipment for the Series II and III, althought it
was an option on the 3000 CX and Series I.
*/
struct FEATURE_TABLE {
uint32 typ; /* standard features plus typically configured options */
@ -970,11 +972,11 @@ struct FEATURE_TABLE {
};
static const struct FEATURE_TABLE cpu_features [] = { /* features indexed by CPU_MODEL */
{ 0, /* UNIT_SERIES_III */
UNIT_CIS,
{ UNIT_EIS, /* UNIT_SERIES_III */
UNIT_EIS | UNIT_CIS,
1024 * 1024 },
{ 0, /* UNIT_SERIES_II */
0,
{ UNIT_EIS, /* UNIT_SERIES_II */
UNIT_EIS,
256 * 1024 }
};
@ -1002,6 +1004,7 @@ static t_stat show_speed (FILE *st, UNIT *uptr, int32 val, void *desc);
static t_stat halt_mode_interrupt (HP_WORD device_number);
static t_stat machine_instruction (void);
static void trace_registers (void);
/* CPU SCP data structures */
@ -1095,7 +1098,7 @@ static MTAB cpu_mod [] = {
{ UNIT_MODEL, UNIT_SERIES_II, "Series II", NULL, &set_model, NULL, NULL },
{ UNIT_MODEL, UNIT_SERIES_III, "Series III", "III", &set_model, NULL, NULL },
{ UNIT_EIS, UNIT_EIS, "EIS", NULL, &set_option, NULL, NULL },
{ UNIT_EIS, UNIT_EIS, "EIS", "EIS", &set_option, NULL, NULL },
{ UNIT_EIS, 0, "no EIS", "NOEIS", NULL, NULL, NULL },
{ UNIT_CIS, UNIT_CIS, "CIS", "CIS", &set_option, NULL, NULL },
@ -1399,14 +1402,6 @@ DEVICE cpu_dev = {
t_stat sim_instr (void)
{
static const char *const stack_formats [] = { /* stack register display formats, indexed by SR */
BOV_FORMAT " ", /* SR = 0 format */
BOV_FORMAT " A %06o, ", /* SR = 1 format */
BOV_FORMAT " A %06o, B %06o, ", /* SR = 2 format */
BOV_FORMAT " A %06o, B %06o, C %06o, ", /* SR = 3 format */
BOV_FORMAT " A %06o, B %06o, C %06o, D %06o, " /* SR = 4 format */
};
int abortval;
HP_WORD label, parameter;
TRAP_CLASS trap;
@ -1451,6 +1446,9 @@ if (abortval) { /* if a microcode abort
label = TO_LABEL (LABEL_IRQ, trap); /* form the label from the STT number */
if (cpu_dev.dctrl & DEB_REG) /* if register tracing is enabled */
trace_registers (); /* then show the registers just before the trap */
tprintf (cpu_dev, DEB_INSTR, BOV_FORMAT "%s trap%s\n",
PBANK, P - 1 & R_MASK, parameter, trap_name [trap],
(trap == trap_User && !(STA & STATUS_T) ? " (disabled)" : ""));
@ -1627,23 +1625,8 @@ while (status == SCPE_OK) { /* execute until simulat
cpu_dev.dctrl = DEB_ALL; /* and turn on full tracing */
}
if (cpu_dev.dctrl & DEB_REG) { /* if register tracing is enabled */
hp_trace (&cpu_dev, DEB_REG, /* then output the active TOS registers */
stack_formats [SR],
SBANK, SM, SR, RA, RB, RC, RD);
fprintf (sim_deb, "X %06o, %s\n", /* output the index and status registers */
X, fmt_status (STA));
if (cpu_base_changed) { /* if the base registers have been altered */
hp_trace (&cpu_dev, DEB_REG, /* then output the base register values */
BOV_FORMAT " PB %06o, PL %06o, DL %06o, DB %06o, Q %06o, Z %06o\n",
DBANK, 0, STATUS_CS (STA),
PB, PL, DL, DB, Q, Z);
cpu_base_changed = FALSE; /* clear the base registers changed flag */
}
}
if (cpu_dev.dctrl & DEB_REG) /* if register tracing is enabled */
trace_registers (); /* then print the registers */
if (cpu_dev.dctrl & DEB_EXEC /* if execution tracing is enabled */
&& cpu_dev.dctrl == DEB_ALL /* and is currently active */
@ -1672,6 +1655,9 @@ while (status == SCPE_OK) { /* execute until simulat
fprint_val (sim_deb, sim_eval [0], cpu_dev.dradix, /* then print the numeric */
cpu_dev.dwidth, PV_RZRO); /* value again */
if (sim_deb == stdout) /* if debug output is to the (raw) console */
fputc ('\r', sim_deb); /* then insert a carriage return */
fputc ('\n', sim_deb); /* end the trace with a newline */
}
@ -1723,8 +1709,7 @@ if (TRACING (cpu_dev, cpu_dev.dctrl) /* if instruction tracin
hp_trace (&cpu_dev, cpu_dev.dctrl, /* then output the simulation stop reason */
BOV_FORMAT "simulation stop: %s\n",
PBANK, P, STA,
status >= SCPE_BASE ? sim_error_text (status)
: sim_stop_messages [status]);
sim_error_text (status));
if (sim_switches & SIM_SW_HIDE /* if executing in a non-echoing command file */
&& (status == SCPE_STEP || status == STOP_BRKPNT)) /* and a step or breakpoint stop occurs */
@ -4663,3 +4648,42 @@ if (status == STOP_UNIMPL /* if the instruction is
return status;
}
/* Trace the register set.
This routine outputs the current values of the machine registers to the
debug log. The first output line consists of the SBANK, SM, SR, TOS, index,
and status register values. If the "cpu_base_changed" flag is set, a second
line consisting of the DBANK register, code segment number, and code, data,
and stack base and limit registers is also output.
The routine must not be called unless the debug log is defined, and should
not be called unless the register trace flag is set.
*/
static void trace_registers (void)
{
static const char *const stack_formats [] = { /* stack register display formats, indexed by SR */
BOV_FORMAT " ", /* SR = 0 format */
BOV_FORMAT " A %06o, ", /* SR = 1 format */
BOV_FORMAT " A %06o, B %06o, ", /* SR = 2 format */
BOV_FORMAT " A %06o, B %06o, C %06o, ", /* SR = 3 format */
BOV_FORMAT " A %06o, B %06o, C %06o, D %06o, " /* SR = 4 format */
};
hp_trace (&cpu_dev, DEB_REG, stack_formats [SR], /* output the active TOS registers */
SBANK, SM, SR, RA, RB, RC, RD);
fprintf (sim_deb, "X %06o, %s\n", X, fmt_status (STA)); /* output the index and status registers */
if (cpu_base_changed) { /* if the base registers have been altered */
hp_trace (&cpu_dev, DEB_REG, /* then output the base register values */
BOV_FORMAT " PB %06o, PL %06o, DL %06o, DB %06o, Q %06o, Z %06o\n",
DBANK, 0, STATUS_CS (STA), PB, PL, DL, DB, Q, Z);
cpu_base_changed = FALSE; /* clear the base registers changed flag */
}
return;
}

View File

@ -1,6 +1,6 @@
/* hp3000_cpu.h: HP 3000 CPU declarations
Copyright (c) 2016-2019, J. David Bryan
Copyright (c) 2016-2020, 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
@ -23,10 +23,14 @@
in advertising or otherwise to promote the sale, use or other dealings in
this Software without prior written authorization from the author.
11-Oct-20 JDB Moved UNIT_OPTS here from hp3000_cpu.c for updating ease
09-Oct-20 JDB Renamed trap_Word_Count_Overflow to trap_Invalid_Decimal_Length
07-Oct-20 JDB Corrected EXEC trace to omit PSERV events
05-Sep-20 JDB Added the EIS dispatcher, corrected EIS_SDEC_MASK value
18-Feb-19 JDB Added SS_PAUSE_RESUMED simulation stop condition
25-Jul-18 JDB Fixed typo in "cpu_setup_code_segment" declaration
07-Nov-16 JDB Added SETR and SETR_X for SETR executor use;
renamed cpu_byte_to_word_ea to cpu_byte_ea
07-Nov-16 JDB Added SETR and SETR_X for SETR executor use
Renamed cpu_byte_to_word_ea to cpu_byte_ea
03-Nov-16 JDB Added LABEL_LOCAL for PARC/XBR/ENDP executor use
01-Nov-16 JDB Added debug flag for per-instruction trace capability
22-Oct-16 JDB Added "cpu_interrupt_pending" global from cpu_base.c
@ -82,6 +86,9 @@
#define UNIT_PFARS (1u << UNIT_PFARS_SHIFT) /* the system will auto-restart after a power failure */
#define UNIT_CIS (1u << UNIT_CIS_SHIFT) /* the COBOL II Extended Instruction Set is installed */
#define UNIT_OPTS (UNIT_EIS | UNIT_CIS) /* set of optional features */
#define UNIT_CPU_MODEL (cpu_unit [0].flags & UNIT_MODEL)
#define CPU_MODEL(f) ((f) >> UNIT_MODEL_SHIFT & UNIT_MODEL_MASK)
@ -91,13 +98,13 @@
/* CPU debug flags */
#define DEB_ALL ~0u /* trace everything */
#define DEB_INSTR (1u << 0) /* trace instructions */
#define DEB_REG (1u << 1) /* trace register values */
#define DEB_PSERV (1u << 2) /* trace PCLK service events */
#define DEB_EXEC (1u << 3) /* trace matched instruction executions */
#define DEB_ALL (~DEB_PSERV) /* trace everything except PCLK service events */
#define BOV_FORMAT "%02o.%06o %06o " /* bank-offset-value trace format string */
@ -320,7 +327,7 @@ typedef enum {
#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_Invalid_Decimal_Length 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)
@ -847,6 +854,7 @@ typedef enum {
#define SDEC2_MASK 0000003u /* two-bit S-decrement mask for move instructions */
#define SDEC3_MASK 0000007u /* 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)
@ -910,9 +918,12 @@ typedef enum {
#define CVND_SC_MASK 0000016u /* CVND sign-control mask */
#define CVND_SC_SHIFT 1 /* CVND sign-control field alignment shift */
#define EIS_SDEC_MASK 0000020u /* EIS S-decrement mask */
#define EIS_SDEC_FLAG 0000020u /* EIS S-decrement bit 11 */
#define EIS_SDEC_MASK 0000060u /* EIS S-decrement mask */
#define EIS_SDEC_SHIFT 4 /* EIS S-decrement alignment shift */
#define EIS_SDEC(v) (((v) & EIS_SDEC_MASK) >> EIS_SDEC_SHIFT)
#define CIS_SDEC_MASK 0000001u /* CIS S-decrement mask */
#define CIS_SDEC_SHIFT 0 /* CIS S-decrement alignment shift */
@ -1117,4 +1128,6 @@ 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);
extern t_stat cpu_eis_fp_op (void);
extern t_stat cpu_eis_dec_op (void);
extern t_stat cpu_cis_op (void);

View File

@ -1,6 +1,6 @@
/* hp3000_cpu_base.c: HP 3000 CPU base set instruction simulator
Copyright (c) 2016-2019, J. David Bryan
Copyright (c) 2016-2020, 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
@ -23,6 +23,8 @@
in advertising or otherwise to promote the sale, use or other dealings in
this Software without prior written authorization from the author.
30-Sep-20 JDB Cleaned up QWORD assembly and disassembly in "shift_48_64"
05-Sep-20 JDB Added EIS decoding and "cpu_eis_[fp|dec]_op" calls
09-Dec-19 JDB Replaced debugging macros with tracing macros
27-Dec-18 JDB Revised fall through comments to comply with gcc 7
08-Jan-17 JDB Fixed bug in SCAL 0/PCAL 0 if a stack overflow occurs
@ -2196,10 +2198,10 @@ static const PROPERTY prop [4] = {
uint32 count;
t_uint64 operand, fill, result;
operand = (t_uint64) RC << D32_WIDTH | TO_DWORD (RB, RA); /* merge the first three words of the operand */
if (op_size == size_64) /* if the operand size is 64 bits */
operand = (t_uint64) RD << D48_WIDTH | operand; /* then merge the fourth word of the operand */
if (op_size == size_48) /* if the operand size is 48 bits */
operand = TO_QWORD (0, RC, RB, RA); /* then merge the three words of the operand */
else /* otherwise the operand size is 64 bits */
operand = TO_QWORD (RD, RC, RB, RA); /* so merge all four operand words */
if (shift == arithmetic) { /* if this is an arithmetic shift */
count = SHIFT_COUNT (opcode); /* then the instruction contains the shift count */
@ -2256,13 +2258,13 @@ else /* otherwise the shift t
RA = LOWER_WORD (result); /* restore the */
RB = UPPER_WORD (result); /* lower three words */
RC = LOWER_WORD (result >> D32_WIDTH); /* to the stack */
RC = LOW_UPPER_WORD (result); /* to the stack */
if (op_size == size_48) /* if the operand size is 48 bits */
SET_CCA (RC, RB | RA); /* then set the condition code */
else { /* otherwise the size is 64 bits */
RD = LOWER_WORD (result >> D48_WIDTH); /* so merge the upper word */
RD = HIGH_UPPER_WORD (result); /* so restore the upper word */
SET_CCA (RD, RC | RB | RA); /* and then set the condition code */
}
@ -3412,11 +3414,11 @@ return status; /* return the execution
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| 0 0 1 0 | 0 0 0 1 | 0 1 1 1 | 1 0 0 | x | DMUL/DDIV
| 0 0 1 0 | 0 0 0 1 | 0 0 0 0 | 1 | EIS FP op | EIS FP
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| 0 0 1 0 | 0 0 0 1 | 0 0 0 0 | 1 | ext fp op | Extended FP
| 0 0 1 0 | 0 0 0 1 | 0 0 0 1 | APL op | APL
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
@ -3424,7 +3426,11 @@ return status; /* return the execution
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| 0 0 1 0 | 0 0 0 1 | 1 | options | decimal op | Decimal
| 0 0 1 0 | 0 0 0 1 | 0 1 1 1 | 1 0 0 | x | DMUL/DDIV
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| 0 0 1 0 | 0 0 0 1 | 1 | options | decimal op | EIS Decimal
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
In hardware, optional instructions depend on the presence of the microcode
@ -3473,6 +3479,14 @@ operation = FIRMEXTOP (CIR); /* get the operation fro
switch (operation) { /* dispatch the operation */
case 000: /* Extended Instruction Set (floating point) */
if (cpu_unit [0].flags & UNIT_EIS) /* if the firmware is installed */
status = cpu_eis_fp_op (); /* then call the EIS dispatcher */
else /* otherwise */
status = STOP_UNIMPL; /* the instruction range decodes as unimplemented */
break;
case 003: /* COBOL II Extended Instruction Set */
if (cpu_unit [0].flags & UNIT_CIS) /* if the firmware is installed */
status = cpu_cis_op (); /* then call the CIS dispatcher */
@ -3537,6 +3551,21 @@ switch (operation) { /* dispatch the operatio
break;
case 010: /* Extended Instruction Set (decimal) */
case 011: /* Extended Instruction Set (decimal) */
case 012: /* Extended Instruction Set (decimal) */
case 013: /* Extended Instruction Set (decimal) */
case 014: /* Extended Instruction Set (decimal) */
case 015: /* Extended Instruction Set (decimal) */
case 016: /* Extended Instruction Set (decimal) */
case 017: /* Extended Instruction Set (decimal) */
if (cpu_unit [0].flags & UNIT_EIS) /* if the firmware is installed */
status = cpu_eis_dec_op (); /* then call the EIS dispatcher */
else /* otherwise */
status = STOP_UNIMPL; /* the instruction range decodes as unimplemented */
break;
default:
status = STOP_UNIMPL; /* the firmware extension instruction is unimplemented */
}

View File

@ -1,6 +1,6 @@
/* hp3000_cpu_cis.c: HP 32234A COBOL II Instruction Set simulator
Copyright (c) 2016-2019, J. David Bryan
Copyright (c) 2016-2020, 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
@ -23,6 +23,10 @@
in advertising or otherwise to promote the sale, use or other dealings in
this Software without prior written authorization from the author.
22-Oct-20 JDB Improved the comments describing the instructions
20-Oct-20 JDB CVND now queues down until 3 TOS registers are left
09-Oct-20 JDB Renamed trap_Word_Count_Overflow to trap_Invalid_Decimal_Length
26-Aug-20 JDB Corrected line ends for trace to stdout
09-Dec-19 JDB Replaced debugging macros with tracing macros
22-Apr-17 JDB Corrected the significance flag setting in "edit"
29-Dec-16 JDB Disabled interrupt checks pending test generation
@ -39,89 +43,236 @@
This module implements the HP 32234A COBOL II Extended Instruction
Set firmware, also known as the Language Extension Instructions. The set
contains these instructions in the firmware extension range 020460-020477:
contains these instructions:
Name Description
---- ---------------------------
ABSD Absolute decimal
ABSN Absolute numeric
ALGN Align numeric
CMPS Compare strings
CMPT Compare translated strings
CVND Convert numeric display
EDIT Edited move
ENDP End of paragraph
LDDW Load double-word
LDW Load word
NEGD Negate decimal
PARC Paragraph procedure call
TCCS Test condition code and set
TR Translate
XBR External branch
The instructions occupy the the firmware extension range 020460-020477 and
are encoded as follows:
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| 0 0 1 0 | 0 0 0 1 | 0 0 1 1 | 0 0 0 | S | ALGN
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
S-Decrement:
0 = delete 3 words
1 = delete 4 words
Transfer the external decimal number designated by RA (digit and fraction
count) and RB (address) to an external decimal number designated by RC
(digit and fraction count) and RD (address) while aligning the source number
with the target decimal point.
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| 0 0 1 0 | 0 0 0 1 | 0 0 1 1 | 0 0 1 | S | ABSN
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
S-Decrement:
0 = delete 1 word
1 = delete 2 words
Convert in place the external decimal number RA (count) and RB (address) to
an unsigned value.
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| 0 0 1 0 | 0 0 0 1 | 0 0 1 1 | 1 0 0 | B | EDIT
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
Buffer Location:
0 = edit program address is relative to PB
1 = edit program address is relative to DB
Move a string of characters from the source buffer address contained in RB to
the target buffer address contained in RC under the control of the edit
program addressed by RD. RA contains a zero word used to resume after an
interrupt.
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| 0 0 1 0 | 0 0 0 1 | 0 0 1 1 | 1 0 1 | B | CMPS
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
Buffer Location:
0 = second string address is relative to PB
1 = second string address is relative to DB
Compare the string of characters designated by RA (count) and RB (address) to
the string of characters designated by RC (count) and RD (address) and set
the condition code appropriately.
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| 0 0 1 0 | 0 0 0 1 | 0 0 1 1 | 1 1 0 0 | XBR
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
Jump to the location indicated by the segment number contained in RA and the
PB-relative address contained in RB.
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| 0 0 1 0 | 0 0 0 1 | 0 0 1 1 | 1 1 0 1 | PARC
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
Call the location indicated by the paragraph number contained in RA, the
segment number contained in RB, and the PB-relative address contained in RC.
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| 0 0 1 0 | 0 0 0 1 | 0 0 1 1 | 1 1 1 0 | ENDP
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
Return to the location indicated by the segment number contained in RC and
the PB-relative address contained in RD if the paragraph numbers contained in
RA and RB are identical.
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| 0 0 1 0 | 0 0 0 1 | 0 0 1 1 | 1 1 1 1 | CMPT
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| 0 0 0 0 0 0 0 0 0 0 | 0 0 0 1 1 | B |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
Buffer Location:
0 = second string address is relative to PB
1 = second string address is relative to DB
Compare the string of characters designated by RA (count) and RB (address) to
the string of characters designated by RC (count) and RD (address) using the
translation table addressed by SM and set the condition code appropriately.
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| 0 0 1 0 | 0 0 0 1 | 0 0 1 1 | 1 1 1 1 | TCCS
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| 0 0 0 0 0 0 0 0 0 0 | 0 0 1 | > | = | < |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
Test the condition code in the status register against the set of conditions
specified in bits 13-15 and load -1 to the TOS if the condition matches and 0
if it does not.
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| 0 0 1 0 | 0 0 0 1 | 0 0 1 1 | 1 1 1 1 | CVND
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| 0 0 0 0 0 0 0 0 0 0 | 0 1 | sign op | S |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
Sign Conversion:
000 = source sign is leading separate
001 = source sign is trailing separate
010 = source sign is leading overpunch
011 = source sign is trailing overpunch
1xx = source is unsigned
S-Decrement:
0 = delete 2 words
1 = delete 3 words
Convert the display decimal number designated by RA (count) and RB (address)
to the external decimal number designated by RC (address).
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| 0 0 1 0 | 0 0 0 1 | 0 0 1 1 | 1 1 1 1 | LDW
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| 0 0 0 0 0 0 0 0 0 0 | 1 0 0 0 0 | S |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
S-Decrement:
0 = delete no words
1 = delete 1 word
Load two bytes from the byte address contained in RA to the TOS.
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| 0 0 1 0 | 0 0 0 1 | 0 0 1 1 | 1 1 1 1 | LDDW
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| 0 0 0 0 0 0 0 0 0 0 | 1 0 0 0 1 | S |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
S-Decrement:
0 = delete delete no words
1 = delete 1 word
Load four bytes from the byte address contained in RA to the TOS.
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| 0 0 1 0 | 0 0 0 1 | 0 0 1 1 | 1 1 1 1 | TR
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| 0 0 0 0 0 0 0 0 0 0 | 1 0 0 1 0 | B |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
Buffer Location:
0 = translation table address is relative to PB
1 = translation table address is relative to DB
Convert the string of characters designated by RA (count) and RC (address) to
the target string designated by RB (address) using the translation table
addressed by RD.
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| 0 0 1 0 | 0 0 0 1 | 0 0 1 1 | 1 1 1 1 | ABSD
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| 0 0 0 0 0 0 0 0 0 0 | 1 0 0 1 1 | S |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
S-Decrement:
0 = delete 1 word
1 = delete 2 words
Convert in place the packed decimal number designated by RA (count) and RB
(address) to an unsigned value.
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| 0 0 1 0 | 0 0 0 1 | 0 0 1 1 | 1 1 1 1 | NEGD
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| 0 0 0 0 0 0 0 0 0 0 | 1 0 1 0 0 | S |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
Where:
S-Decrement:
0 = delete 1 word
1 = delete 2 words
Negate in place the packed decimal number designated by RA (count) and RB
(address) to an unsigned value.
S = S-decrement
B = Bank select (0/1 = PB-relative/DB-relative)
The PARC, ENDP, and XBR instructions implement the COBOL "PERFORM" statement.
ABSD and NEGD manipulate packed decimal numbers. ALGN, ABSN, EDIT, and CVND
@ -169,33 +320,38 @@
Numbers may begin at an even or odd byte address, and the size of the number
(in digits) may be even or odd, so there are four possible cases of packing
into 16-bit words:
the starting digits into 16-bit words:
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 addr/size
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| unused | digit | ... | ... | even/even
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| digit | digit | ... | ... | even/odd
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| ... | ... | unused | digit | odd/even
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| ... | digit | digit | odd/odd
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
Numbers always end with the sign in the lower half of the byte, so there are
two possible cases of packing the ending digits into 16-bit words, depending
on the total number of digits:
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| unused/digit | digit | digit | digit | addr even
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| digit | digit | digit | sign | size even
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| unused/digit | digit | digit | digit | addr even
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| digit | sign | ... | size odd
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| ... | unused/digit | digit | addr odd
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| digit | digit | digit | sign | size ?...
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| ... | unused/digit | digit | addr odd
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| digit | sign | ... |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| ... | ... | digit | sign |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
External decimal (also known as DISPLAY, numeric display, and ASCII) values
contain contain from 1 to 28 digits that are stored as ASCII characters in
@ -416,10 +572,12 @@
Two user traps may be taken by these instructions if the T bit is on in the
status register:
- Word Count Overflow (parameter 17)
- Invalid ASCII Digit (parameter 14)
Parameter Description
--------- ------------------------------------------------
000014 Invalid ASCII Digit
000017 Invalid Decimal Length
The Word Count Overflow trap is taken when an instruction is given an
The Invalid Decimal Length trap is taken when an instruction is given an
external decimal number with more than 28 digits. The Invalid ASCII Digit
trap occurs when an external decimal number contains characters other than
"0"-"9", a "+" or "-" sign in any position other than the first or last
@ -435,14 +593,14 @@
1. In several cases noted below, the hardware microcode implementations
differ from the descriptions in the Machine Instruction Set manual.
Also, the comments in the microcode source sometimes do not correctly
described the microcode actions. In all cases of conflict, the simulator
describe the microcode actions. In all cases of conflict, the simulator
follows the microcode implementation.
2. The Machine Instruction Set manual references trap conditions (Invalid
Alphabetic Character, Invalid Operand Length, Invalid Source Character
Count, and Invalid Digit Count) that are not defined in the Series II/III
System Reference Manual. Examination of the microcode indicates that
only the Invalid ASCII Digit and Word Count Overflow traps are taken.
only the Invalid ASCII Digit and Invalid Decimal Length traps are taken.
3. Target operand tracing is not done if a trap occurred, as the result will
be invalid.
@ -630,7 +788,7 @@ switch (opcode) { /* dispatch the opcode *
|| source_length > MAX_DIGITS /* or the source digit count is too large */
|| target_fraction > target_length /* or the target fraction count is invalid */
|| target_length > MAX_DIGITS) /* or the target digit count is too large */
trap = trap_Word_Count_Overflow; /* then trap for a count overflow */
trap = trap_Invalid_Decimal_Length; /* then trap for a count overflow */
else if (source_length > 0 && target_length > 0) { /* otherwise if there is an alignment to do */
sign = Unsigned; /* then assume the source is unsigned */
@ -720,8 +878,11 @@ switch (opcode) { /* dispatch the opcode *
case 002: /* ABSN (CCA, O; INV DIG, WC OVF, STOV, STUN, BNDV) */
case 003:
while (SR > 2) /* if more than two TOS registers are valid */
cpu_queue_down (); /* then queue them down until exactly two are left */
if (RA > MAX_DIGITS) /* if the digit count is too large */
trap = trap_Word_Count_Overflow; /* then trap for a count overflow */
trap = trap_Invalid_Decimal_Length; /* then trap for a count overflow */
else if (RA > 0) { /* otherwise if there are digits to process */
source_rba = RB; /* then use a working source byte address pointer */
@ -907,8 +1068,11 @@ switch (opcode) { /* dispatch the opcode *
case 035:
case 036:
case 037:
while (SR > 3) /* if more than three TOS registers are valid */
cpu_queue_down (); /* then queue them down until exactly three are left */
if (RA > MAX_DIGITS) /* if the digit count is too large */
trap = trap_Word_Count_Overflow; /* then trap for a count overflow */
trap = trap_Invalid_Decimal_Length; /* then trap for a count overflow */
else if (RA > 0) { /* otherwise if there are digits to convert */
sign_cntl = (opcode & CVND_SC_MASK) /* then get the sign control code */
@ -935,6 +1099,9 @@ switch (opcode) { /* dispatch the opcode *
case 040: /* LDW (none; STOV, STUN, BNDV) */
case 041:
while (SR > 1) /* if more than one TOS register is valid */
cpu_queue_down (); /* then queue them down until exactly one is left */
source_rba = RA; /* use a working source byte address pointer */
if ((opcode & CIS_SDEC_MASK) == 0) /* if the S-decrement bit is clear */
@ -952,6 +1119,9 @@ switch (opcode) { /* dispatch the opcode *
case 042: /* LDDW (none; STOV, STUN, BNDV) */
case 043:
while (SR > 1) /* if more than one TOS register is valid */
cpu_queue_down (); /* then queue them down until exactly one is left */
source_rba = RA; /* use a working source byte address pointer */
if ((opcode & CIS_SDEC_MASK) == 0) /* if the S-decrement bit is clear */
@ -1010,8 +1180,11 @@ switch (opcode) { /* dispatch the opcode *
case 047:
case 050: /* NEGD (CCA, O; WC OVF, STOV, STUN, BNDV) */
case 051:
while (SR > 2) /* if more than two TOS registers are valid */
cpu_queue_down (); /* then queue them down until exactly two are left */
if (RA > MAX_DIGITS) /* if the digit count is too large */
trap = trap_Word_Count_Overflow; /* then trap for a count overflow */
trap = trap_Invalid_Decimal_Length; /* then trap for a count overflow */
else { /* otherwise */
mem_init_byte (&source, data, &RB, RA); /* set up a byte accessor for the operand */
@ -1022,7 +1195,7 @@ switch (opcode) { /* dispatch the opcode *
if (TRACING (cpu_dev, DEB_MOPND))
fprint_operand (&source, "source", &fmt_bcd_operand);
byte = mem_read_byte (&target); /* get the sign byte */
byte = mem_read_byte (&target); /* get the sign byte and check the bounds */
if (opcode < 050) { /* if this is an ABSD instruction */
if (IS_NEG (byte)) /* then if the number is negative */
@ -1044,7 +1217,7 @@ switch (opcode) { /* dispatch the opcode *
SET_CCL; /* and set the less-than condition code */
}
mem_modify_byte (&target, byte); /* rewrite the digit */
mem_modify_byte (&target, byte); /* rewrite the digit and check the bounds */
mem_post_byte (&target); /* and post it */
if (TRACING (cpu_dev, DEB_MOPND))
@ -1458,6 +1631,9 @@ do { /* process operations wh
prog.initial_byte_address /* at the current physical byte address */
+ prog.count - 1);
if (sim_deb == stdout) /* if debug output is to the (raw) console */
fputc ('\r', sim_deb); /* then insert a carriage return */
fputc ('\n', sim_deb); /* end the trace with a newline */
}

2489
HP3000/hp3000_cpu_eis.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
/* hp3000_cpu_fp.c: HP 3000 floating-point arithmetic simulator
Copyright (c) 2016, J. David Bryan
Copyright (c) 2016-2020, 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
@ -23,6 +23,10 @@
in advertising or otherwise to promote the sale, use or other dealings in
this Software without prior written authorization from the author.
09-Oct-20 JDB Modified to return Ext_Float traps for extended operands
30-Sep-20 JDB Cleaned up QWORD assembly in "norm_round_pack"
14-Sep-20 JDB Added a guard bit to 56-bit calculations for rounding
"divide" now renormalizes divisors to reduce calculation
11-Jun-16 JDB Bit mask constants are now unsigned
03-Feb-16 JDB First release version
25-Aug-15 JDB Fixed FSUB zero subtrahend bug (from Norwin Malmberg)
@ -96,6 +100,18 @@
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.
Seven user trap values are returned:
Parameter Description
--------- ------------------------------------------------
000001 Integer Overflow
000002 Float Overflow
000003 Float Underflow
000005 Float Zero Divide
000010 Extended Precision Floating Point Overflow
000011 Extended Precision Floating Point Underflow
000012 Extended Precision Floating Point Divide by Zero
*/
@ -120,11 +136,10 @@
#define MANTISSA_SHIFT 0 /* the mantissa alignment shift */
#define UNPACKED_BITS 54 /* the number of significant bits in the unpacked mantissa */
#define GUARD_BITS 1 /* the number of guard bits in the mantissa used for rounding */
#define IMPLIED_BIT ((t_uint64) 1uL << UNPACKED_BITS) /* the implied MSB in the mantissa */
#define CARRY_BIT ((t_uint64) 1uL << 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 */
#define IMPLIED_BIT ((t_uint64) 1uL << UNPACKED_BITS + GUARD_BITS) /* the implied MSB in the mantissa */
#define CARRY_BIT (IMPLIED_BIT << 1) /* the carry from the MSB in the mantissa */
/* Floating-point accessors */
@ -134,7 +149,7 @@
#define TO_EXPONENT(w) ((w) + EXPONENT_BIAS << EXPONENT_SHIFT & EXPONENT_MASK)
#define DENORMALIZED(m) (((m) & IMPLIED_BIT) == 0)
#define DENORMALIZED(m) ((m) > 0 && ((m) & IMPLIED_BIT) == 0)
/* Floating-point unpacked representation */
@ -160,20 +175,20 @@ static const int32 mantissa_bits [] = { /* the number of mantissa bits,
};
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 */
((t_uint64) 1uL << 16) - 1 << 0, /* in_s 16-bit mantissa */
((t_uint64) 1uL << 32) - 1 << 0, /* in_d 32-bit mantissa */
((t_uint64) 1uL << 22) - 1 << 32, /* fp_f 22-bit mantissa */
((t_uint64) 1uL << 38) - 1 << 16, /* fp_x 38-bit mantissa */
((t_uint64) 1uL << 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 */
(t_uint64) 1uL << 54, /* in_d LSB when shifted right by exponent value */
(t_uint64) 1uL << 32, /* fp_f word 2 LSB */
(t_uint64) 1uL << 16, /* fp_x word 3 LSB */
(t_uint64) 1uL << 0 /* fp_e word 4 LSB */
};
@ -275,7 +290,8 @@ return result_op; /* return the result */
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 sign. The mantissa is then shifted left one additional position to add a
guard bit that will be used for rounding.
The absolute values of single and double integers are unpacked into the
mantissas and preshifted by 32 or 16 bits, respectively, to reduce the
@ -300,10 +316,10 @@ return result_op; /* return the result */
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.
as single-precision 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)
@ -324,9 +340,9 @@ switch (packed.precision) { /* dispatch based on the
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 */
unpacked.mantissa = (t_uint64) word << 32 + GUARD_BITS; /* 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;
@ -341,9 +357,9 @@ switch (packed.precision) { /* dispatch based on the
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 */
unpacked.mantissa = (t_uint64) word << 16 + GUARD_BITS; /* 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;
@ -353,12 +369,14 @@ switch (packed.precision) { /* dispatch based on the
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 */
unpacked.mantissa <<= D16_WIDTH; /* 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.mantissa <<= GUARD_BITS; /* add the guard bits */
unpacked.exponent = /* store the exponent */
EXPONENT (packed.words [0]) - EXPONENT_BIAS; /* after removing the bias */
@ -439,10 +457,10 @@ else if (unpacked.precision <= in_d) /* if packin
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 */
else { /* otherwise */
integer = (int32) /* convert the value to an integer */
(unpacked.mantissa >> UNPACKED_BITS + GUARD_BITS - unpacked.exponent /* by shifting right to align */
& mantissa_mask [unpacked.precision]); /* and masking to the precision */
if (unpacked.negative) /* if the value is negative */
integer = - integer; /* then negate the result */
@ -465,29 +483,35 @@ else { /* otherwise a real numb
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 >>= GUARD_BITS; /* remove the guard bits */
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 [0] = HIGH_UPPER_WORD (unpacked.mantissa) /* 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 */
packed.words [1] = LOW_UPPER_WORD (unpacked.mantissa); /* pack the second */
packed.words [2] = UPPER_WORD (unpacked.mantissa); /* and third */
packed.words [3] = LOWER_WORD (unpacked.mantissa); /* 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 */
if (packed.precision == fp_f) /* then if this is a standard operand */
packed.trap = trap_Float_Underflow; /* then report a standard underflow trap */
else /* otherwise */
packed.trap = trap_Ext_Float_Underflow; /* report an extended 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 */
if (packed.precision == fp_f) /* then if this is a standard operand */
packed.trap = trap_Float_Overflow; /* then report a standard overflow trap */
else /* otherwise */
packed.trap = trap_Ext_Float_Overflow; /* report an extended overflow trap */
else /* otherwise */
packed.trap = trap_None; /* report that packing succeeded */
@ -642,10 +666,11 @@ return trap_None; /* report that the subtr
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:
Mantissas are represented internally as fixed-point numbers with 54 data bits
plus one guard bit to the right of the binary point. That is, the real
number represented is the integer mantissa value * (2 ** -55), where the
right-hand term represents the delta for a change of one bit. Multiplication
is therefore:
(product * delta) = (multiplicand * delta) * (multiplier * delta)
@ -657,9 +682,9 @@ return trap_None; /* report that the subtr
product = multiplicand * multiplier * delta
Multiplying the product by (2 ** -54) is equivalent to right-shifting by 54.
Multiplying the product by (2 ** -55) is equivalent to right-shifting by 55.
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.
right-shifting by 64, so the net correction is a left-shift by 9.
Implementation notes:
@ -670,8 +695,10 @@ return trap_None; /* report that the subtr
static TRAP_CLASS multiply (FPU *product, FPU multiplicand, FPU multiplier)
{
uint32 ah, al, bh, bl;
t_uint64 hh, hl, lh, ll, carry;
const uint32 delta_shift = D64_WIDTH - (UNPACKED_BITS + GUARD_BITS);
const uint32 carry_shift = D32_WIDTH - delta_shift;
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 */
@ -684,24 +711,26 @@ else { /* otherwise both operan
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 */
ah = UPPER_DWORD (multiplicand.mantissa); /* split the multiplicand */
al = LOWER_DWORD (multiplicand.mantissa); /* 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 */
bh = UPPER_DWORD (multiplier.mantissa); /* split the multiplier */
bl = LOWER_DWORD (multiplier.mantissa); /* 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 */
hh = (t_uint64) ah * (t_uint64) bh; /* form the */
hl = (t_uint64) ah * (t_uint64) bl; /* four cross products */
lh = (t_uint64) al * (t_uint64) bh; /* using 32 x 32 = 64-bit multiplies */
ll = (t_uint64) al * (t_uint64) 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 */
carry = ((t_uint64) UPPER_DWORD (ll) /* do a 64-bit add of the upper half of "ll" */
+ LOWER_DWORD (hl) /* to the lower halves of "hl" */
+ LOWER_DWORD (lh)) >> carry_shift; /* and "lh" and shift to leave just the carry bits */
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 = hh + UPPER_DWORD (hl) /* add "hh" to the upper halves of "hl" and "lh" */
+ UPPER_DWORD (lh);
product->mantissa <<= DELTA_ALIGNMENT; /* align the result */
product->mantissa <<= delta_shift; /* align the result to the binary point */
product->mantissa += carry; /* and add the carry from the discarded bits */
product->negative = /* set the product sign negative */
(multiplicand.negative != multiplier.negative); /* if the operand signs differ */
@ -724,22 +753,22 @@ return trap_None; /* report that the multi
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
and a 64-bit remainder. A correction is developed by multiplying the second
32-bit digit "bl" of the divisor and the trial quotient. If the remainder is
smaller than the correction, the trial quotient is too large, so it is
decremented, and the (full 64-bit) divisor is added to the remainder. The
correction is then subtracted from the remainder, and the process is 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.
64-bit quotient. 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:
Mantissas are represented internally as fixed-point numbers with 54 data bits
plus one guard bit to the right of the binary point. That is, the real
number represented is the integer mantissa value * (2 ** -55), where the
right-hand term represents the delta for a change of one bit. Division is
therefore:
(quotient * delta) = (dividend * delta) / (divisor * delta)
@ -751,12 +780,33 @@ return trap_None; /* report that the multi
quotient = (dividend / divisor) / delta
Dividing the quotient by (2 ** -54) is equivalent to left-shifting by 54.
Dividing the quotient by (2 ** -55) is equivalent to left-shifting by 55.
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.
right-shifting by 64, so the net correction is a right-shift by 9.
Care must be taken to ensure that the correction product does not overflow.
In hardware, this is detected by examining the ALU carry bit after the
correction multiplication. In simulation, there is no portable way of
determining if the 64 x 32 multiply overflowed. However, we can take
advantage of the fact that the upper eight bits of the mantissa are always
zero (54 data bits plus one guard bit plus one implied bit = 56 significant
bits). By shifting the divisor left by eight bits, we ensure that the
quotient will be eight bits shorter, and so guarantee that the correction
product will not overflow. Moreover, because the divisor is now at least
one-half of the maximum value (because of the implied bit), it ensures that
the trial quotient will either be correct or off by one, so at most a single
correction will be needed.
The final quotient would require a left-shift of 8 to account for the
renormalized divisor, but from the above calculations, we also need a right
shift of 9 to align it. The net result needs only a right shift of one to
correct, which we handle by decrementing the exponent in lieu of additional
shifting.
See "Divide-and-Correct Methods for Multiple Precision Division" by Marvin L.
Stein, Communications of the ACM, August 1964 for background.
Stein, Communications of the ACM, August 1964 and "Multiple-Length Division
Revisited: a Tour of the Minefield" by Per Brinch Hansen, Software Practice
and Experience, June 1994 for background.
Implementation notes:
@ -769,20 +819,20 @@ return trap_None; /* report that the multi
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;
const uint32 renorm_shift = D64_WIDTH - (UNPACKED_BITS + GUARD_BITS + 1);
t_uint64 bh, bl, q1, q2, r1, r2, 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 */
if (dividend.precision == fp_f) /* if this is a standard operand */
return trap_Float_Zero_Divide; /* then report a standard zero-divide trap */
else /* otherwise */
return trap_Ext_Float_Zero_Divide; /* report an extended zero-divide trap */
}
else if (dividend.mantissa == 0) { /* otherwise if the dividend is zero */
@ -793,34 +843,39 @@ else if (dividend.mantissa == 0) { /* otherwise if the divi
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 */
quotient->exponent = dividend.exponent /* the quotient exponent is the difference of the */
- divisor.exponent - 1; /* operand exponents (with alignment correction) */
bh = divisor.mantissa >> D32_WIDTH; /* split the divisor */
bl = divisor.mantissa & D32_MASK; /* into high and low halves */
divisor.mantissa <<= renorm_shift; /* renormalize the divisor */
bh = (t_uint64) UPPER_DWORD (divisor.mantissa); /* split the divisor */
bl = (t_uint64) LOWER_DWORD (divisor.mantissa); /* 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 */
c1 = bl * q1; /* form the first remainder correction */
r1 = r1 << D32_WIDTH; /* and scale the remainder to match */
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 */
if (r1 < c1) { /* if a correction is required */
q1 = q1 - 1; /* then the first trial quotient is too large */
r1 = r1 + divisor.mantissa; /* so reduce it and increase the remainder */
}
q2 = c1 / bh; /* form the second trial quotient */
r2 = c1 % bh; /* and remainder */
r1 = r1 - c1; /* correct the first remainder */
c2 = r2 - bl * q2; /* form the second corrected remainder */
q2 = r1 / bh; /* form the second trial quotient */
r2 = r1 % bh; /* and 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 */
c2 = bl * q2; /* form the second remainder correction */
r2 = r2 << D32_WIDTH; /* and scale the remainder to match */
if (r2 < c2) { /* if a correction is required */
q2 = q2 - 1; /* then the second trial quotient is too large */
r2 = r2 + 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->mantissa = (q1 << D32_WIDTH) + q2; /* position and sum the quotient digits */
quotient->negative = /* set the quotient sign negative */
(dividend.negative != divisor.negative); /* if the operand signs differ */
@ -879,9 +934,8 @@ if (real.exponent < -1) /* if the real value is
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);
if (round && real.exponent < UNPACKED_BITS) /* if rounding is requested and won't overflow */
integer->mantissa += half_lsb [in_d] >> real.exponent; /* then add one-half of the LSB to the value */
}
integer->exponent = real.exponent; /* copy the exponent */

View File

@ -1,6 +1,6 @@
/* hp3000_defs.h: HP 3000 simulator general declarations
Copyright (c) 2016-2019, J. David Bryan
Copyright (c) 2016-2020, 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
@ -23,6 +23,9 @@
in advertising or otherwise to promote the sale, use or other dealings in
this Software without prior written authorization from the author.
30-Sep-20 JDB Added HIGH_UPPER_WORD, LOW_UPPER_WORD for EIS
23-Sep-20 JDB Added HP_BYTE type for EIS
17-Aug-20 JDB Corrected "cputc" definition
09-Dec-19 JDB Replaced debugging macros with tracing macros
28-Mar-19 JDB Added extensions
16-Oct-17 JDB Suppressed logical-not-parentheses warning on clang
@ -268,7 +271,7 @@
#define cputc(ch) \
do { \
putc (ch); \
putchar (ch); \
if (sim_log) \
fputc (ch, sim_log); \
} \
@ -387,6 +390,7 @@
*/
typedef uint32 HP_WORD; /* HP 16-bit data word representation */
typedef uint8 HP_BYTE; /* HP 8-bit data word representation */
#define R_MASK 0177777u /* 16-bit register mask */
@ -582,7 +586,8 @@ typedef enum {
/* Half-byte accessors */
#define UPPER_HALF(b) ((b) >> D4_WIDTH & D4_MASK)
#define LOWER_HALF(b) ((b) & D4_MASK)
#define LOWER_HALF(b) ((b) & D4_MASK)
#define TO_BYTE(u,l) (HP_BYTE) (((u) & D4_MASK) << D4_WIDTH | (l) & D4_MASK)
/* Byte accessors.
@ -607,8 +612,8 @@ typedef enum {
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 UPPER_BYTE(w) (HP_BYTE) ((w) >> D8_WIDTH & D8_MASK)
#define LOWER_BYTE(w) (HP_BYTE) ((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)
@ -619,10 +624,20 @@ typedef enum {
#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))
/* Quad-word accessors */
#define UPPER_DWORD(q) (uint32) ((q) >> D32_WIDTH & D32_MASK)
#define LOWER_DWORD(q) (uint32) ((q) & D32_MASK)
#define HIGH_UPPER_WORD(q) (HP_WORD) ((q) >> D48_WIDTH & D16_MASK)
#define LOW_UPPER_WORD(q) (HP_WORD) ((q) >> D32_WIDTH & D16_MASK)
#define TO_QWORD(h,a,b,l) ((t_uint64) (h) << D48_WIDTH | (t_uint64) (a) << D32_WIDTH \
| (uint32) (b) << D16_WIDTH | (l))
/* Flip-flops */
typedef enum {

View File

@ -1,6 +1,6 @@
/* hp3000_mem.c: HP 3000 main memory simulator
Copyright (c) 2016-2019, J. David Bryan
Copyright (c) 2016-2020, 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
@ -25,6 +25,8 @@
MEM HP 3000 Series III Main Memory
25-Sep-20 JDB Added "mem_reset_byte" routine
23-Sep-20 JDB Changed uint8 uses to HP_BYTE
09-Dec-19 JDB Replaced debugging macros with tracing macros
27-Dec-18 JDB Revised fall through comments to comply with gcc 7
21-May-18 JDB Changed "access" to "mem_access" to avoid clashing
@ -623,6 +625,8 @@ bap->initial_byte_address = 0; /* in an initializat
mem_set_byte (bap); /* set up the access from the initial byte offset */
bap->initial_word_address = bap->word_address; /* save the starting word address */
bap->first_byte_address = bap->initial_byte_address; /* save the lowest byte address */
bap->count = 0; /* and clear the byte access count */
@ -703,6 +707,32 @@ return;
}
/* Reset a byte accessor.
The supplied byte accessor is reset to access the original address specified
in the "mem_init_byte" call. It is used to "rewind" a byte accessor, e.g.,
in preparation to reread the bytes or to rewrite after reading the bytes.
The routine does not alter the address and offset of the lowest byte
accessed, so these values are retained across a reset.
On return, the byte accessor is ready for use with the other byte access
routines.
*/
void mem_reset_byte (BYTE_ACCESS *bap)
{
mem_update_byte (bap); /* flush the last byte if written */
*bap->byte_offset = bap->initial_byte_offset; /* restore the original byte offset */
bap->word_address = bap->initial_word_address; /* and word address */
bap->count = 0; /* clear the byte access count */
return;
}
/* Look up a byte in a table.
The byte located in the table designated by the byte accessor pointer "bap"
@ -717,7 +747,7 @@ return;
penalty.
*/
uint8 mem_lookup_byte (BYTE_ACCESS *bap, uint8 index)
HP_BYTE mem_lookup_byte (BYTE_ACCESS *bap, uint8 index)
{
uint32 byte_offset, word_address;
@ -756,9 +786,9 @@ else /* otherwise */
accessed.
*/
uint8 mem_read_byte (BYTE_ACCESS *bap)
HP_BYTE mem_read_byte (BYTE_ACCESS *bap)
{
uint8 byte;
HP_BYTE byte;
if (*bap->byte_offset & 1) { /* if the byte offset is odd */
if (bap->count == 0) /* then if this is the first access */
@ -805,7 +835,7 @@ return byte;
accessed.
*/
void mem_write_byte (BYTE_ACCESS *bap, uint8 byte)
void mem_write_byte (BYTE_ACCESS *bap, HP_BYTE byte)
{
if (*bap->byte_offset & 1) { /* if the byte offset is odd */
if (bap->count == 0) /* then if this is the first access */
@ -840,7 +870,7 @@ return;
offset is not changed by this routine.
*/
void mem_modify_byte (BYTE_ACCESS *bap, uint8 byte)
void mem_modify_byte (BYTE_ACCESS *bap, HP_BYTE byte)
{
if (*bap->byte_offset & 1) { /* if the last byte offset was even */
bap->data_word = REPLACE_UPPER (bap->data_word, byte); /* then replace the upper byte */

View File

@ -1,6 +1,6 @@
/* hp3000_mem.h: HP 3000 memory subsystem interface declarations
Copyright (c) 2016, J. David Bryan
Copyright (c) 2016-2020, 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
@ -23,6 +23,8 @@
in advertising or otherwise to promote the sale, use or other dealings in
this Software without prior written authorization from the author.
25-Sep-20 JDB Added initial_word_address and mem_reset_byte declarations
23-Sep-20 JDB Changed uint8 uses to HP_BYTE
10-Oct-16 JDB Created
@ -105,9 +107,10 @@ typedef struct { /* byte access descripto
uint32 word_address; /* logical word address containing the next byte */
t_bool write_needed; /* TRUE if the data word must be written to memory */
uint32 count; /* current count of bytes accessed */
uint32 length; /* (trace) length of extent of access */
uint32 initial_word_address; /* initial word address containing the first byte */
uint32 initial_byte_offset; /* initial relative byte offset */
uint32 length; /* (trace) length of the block of bytes accessed */
uint32 initial_byte_address; /* (trace) initial absolute byte address */
uint32 initial_byte_offset; /* (trace) initial relative byte offset */
uint32 first_byte_address; /* (trace) lowest absolute byte address accessed */
uint32 first_byte_offset; /* (trace) lowest relative byte offset accessed */
} BYTE_ACCESS;
@ -130,6 +133,7 @@ t_stat mem_deposit (t_value value, t_addr address, UNIT *uptr, int32 switc
mem_init_byte : initialize a memory byte access structure
mem_set_byte : set the access structure to a new byte offset
mem_reset_byte : reset to the start of the byte access
mem_lookup_byte : return a byte at a specified index in a table
mem_read_byte : read the next byte from memory
mem_write_byte : write the next byte to memory
@ -148,15 +152,16 @@ extern void mem_fill (uint32 starting_address, HP_WORD fill_value);
extern t_bool mem_read (DEVICE *dptr, ACCESS_CLASS classification, uint32 offset, HP_WORD *value);
extern t_bool mem_write (DEVICE *dptr, ACCESS_CLASS classification, uint32 offset, HP_WORD value);
extern void mem_init_byte (BYTE_ACCESS *bap, ACCESS_CLASS class, HP_WORD *byte_offset, uint32 block_length);
extern void mem_set_byte (BYTE_ACCESS *bap);
extern uint8 mem_lookup_byte (BYTE_ACCESS *bap, uint8 index);
extern uint8 mem_read_byte (BYTE_ACCESS *bap);
extern void mem_write_byte (BYTE_ACCESS *bap, uint8 byte);
extern void mem_modify_byte (BYTE_ACCESS *bap, uint8 byte);
extern void mem_post_byte (BYTE_ACCESS *bap);
extern void mem_update_byte (BYTE_ACCESS *bap);
extern void mem_init_byte (BYTE_ACCESS *bap, ACCESS_CLASS class, HP_WORD *byte_offset, uint32 block_length);
extern void mem_set_byte (BYTE_ACCESS *bap);
extern void mem_reset_byte (BYTE_ACCESS *bap);
extern HP_BYTE mem_lookup_byte (BYTE_ACCESS *bap, uint8 index);
extern HP_BYTE mem_read_byte (BYTE_ACCESS *bap);
extern void mem_write_byte (BYTE_ACCESS *bap, HP_BYTE byte);
extern void mem_modify_byte (BYTE_ACCESS *bap, HP_BYTE byte);
extern void mem_post_byte (BYTE_ACCESS *bap);
extern void mem_update_byte (BYTE_ACCESS *bap);
extern char *fmt_byte_operand (uint32 byte_address, uint32 byte_count);
extern char *fmt_translated_byte_operand (uint32 byte_address, uint32 byte_count, uint32 table_address);
extern char *fmt_bcd_operand (uint32 byte_address, uint32 digit_count);
extern char *fmt_byte_operand (uint32 byte_address, uint32 byte_count);
extern char *fmt_translated_byte_operand (uint32 byte_address, uint32 byte_count, uint32 table_address);
extern char *fmt_bcd_operand (uint32 byte_address, uint32 digit_count);

View File

@ -1,6 +1,6 @@
/* hp3000_sys.c: HP 3000 system common interface
Copyright (c) 2016-2019, J. David Bryan
Copyright (c) 2016-2020, 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
@ -23,6 +23,8 @@
in advertising or otherwise to promote the sale, use or other dealings in
this Software without prior written authorization from the author.
26-Aug-20 JDB Modified "hp_trace" to output CR LFs to stdout
25-Aug-20 JDB Now sets SCP hooks in "one_time_init" routine
09-Dec-19 JDB Renamed "hp_debug" to "hp_trace"
07-Apr-19 JDB Added command handler pointers
04-Mar-19 JDB Added "sim_vm_release" character string declaration
@ -85,9 +87,9 @@
/* Global release string */
/* Release string */
char *sim_vm_release = "8"; /* HP 3000 simulator release number */
static char *hp_release = "9"; /* HP 3000 simulator release number */
/* External I/O data structures */
@ -2410,30 +2412,35 @@ return fmtptr; /* return a pointer to t
}
/* Format and print a debugging trace line to the debug log.
/* Format and print a trace line to the debug log file.
A formatted line is assembled and sent to the previously opened debug output
stream. On entry, "dptr" points to the device issuing the trace, "flag" is
the debug flag that has enabled the trace, and the remaining parameters
the trace flag that has enabled the trace, and the remaining parameters
consist of the format string and associated values.
This routine is usually not called directly but rather via the "tprintf"
macro, which tests that debugging is enabled for the specified flag before
calling this function. This eliminates the calling overhead if debugging is
macro, which tests that tracing is enabled for the specified flag before
calling this function. This eliminates the calling overhead if tracing is
disabled.
This routine prints a prefix before the supplied format string consisting of
the device name (in upper case) and the debug flag name (in lower case),
the device name (in upper case) and the trace flag name (in lower case),
e.g.:
>>MPX state: Channel SR 3 entered State A
~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
prefix supplied format string
The names are padded to the lengths of the largest device name and debug flag
name among the devices enabled for debugging to ensure that all trace lines
The names are padded to the lengths of the largest device name and trace flag
name among the devices enabled for tracing to ensure that all trace lines
will align for easier reading.
Because the prefix is output only once, embedded newlines should not be
present in the format string. If multiple output lines are desired, then
this routine should be called multiple times, so that each line receives an
identifying trace prefix.
Implementation notes:
@ -2442,6 +2449,23 @@ return fmtptr; /* return a pointer to t
the latter, we must allocate "sufficiently large" arrays for the flag
name and format, rather than arrays of the exact size required by the
call parameters.
2. If the trace output is being written to stdout, a terminating LF must
be translated to CR LF. This is because the console is in "raw" mode
while the CPU is executing instructions. Output to a file does not need
this processing, as text mode handles the host line-end convention.
3. Handling embedded newlines properly would require multiple calls to
"vfprintf", each preceded by the prefix, and each having a format string
consisting of the next segment that ends with an embedded newline.
However, multiple calls are not allowed. The C standard says:
"As the functions vfprintf [etc.] invoke the va_arg macro, the value of
arg after the return is indeterminate."
So there is no way to have the second (e.g.) call start with those
(variable) parameters not consumed by the prior call. Consequently, the
terminating LF check need only be done at the end of the format string.
*/
#define FLAG_SIZE 32 /* sufficiently large to accommodate all flag names */
@ -2449,17 +2473,17 @@ return fmtptr; /* return a pointer to t
void hp_trace (DEVICE *dptr, uint32 flag, ...)
{
va_list argptr;
DEBTAB *debptr;
char *format, *fptr;
const char *nptr;
char flag_name [FLAG_SIZE]; /* desired size is [flag_size + 1] */
char header_fmt [FORMAT_SIZE]; /* desired size is [device_size + flag_size + format_size + 6] */
va_list argptr;
DEBTAB *debptr;
char *format, *fptr;
char flag_name [FLAG_SIZE]; /* desired size is [flag_size + 1] */
char header_fmt [FORMAT_SIZE]; /* desired size is [device_size + flag_size + format_size + 6] */
if (sim_deb != NULL && dptr != NULL) { /* if the output stream and device pointer are valid */
debptr = dptr->debflags; /* then get a pointer to the debug flags table */
debptr = dptr->debflags; /* then get a pointer to the trace flags table */
if (debptr != NULL) /* if the debug table exists */
if (debptr != NULL) /* if the trace table exists */
while (debptr->name != NULL) /* then search it for an entry with the supplied flag */
if (debptr->mask & flag) { /* if the flag matches this entry */
nptr = debptr->name; /* then get a pointer to the flag name */
@ -2473,19 +2497,29 @@ if (sim_deb != NULL && dptr != NULL) { /* if the output stream
(int) device_size, sim_dname (dptr), /* while padding the device and flag names */
(int) flag_size, flag_name); /* as needed for proper alignment */
va_start (argptr, flag); /* set up the argument list */
va_start (argptr, flag); /* set up the argument list */
format = va_arg (argptr, char *); /* get the format string parameter */
strcat (header_fmt, format); /* append the supplied format */
format = va_arg (argptr, char *); /* get the format string parameter */
strcat (header_fmt, format); /* append the supplied format */
vfprintf (sim_deb, header_fmt, argptr); /* format and print to the debug stream */
if (sim_deb == stdout) { /* if debug output is to the (raw) console */
fptr = header_fmt + strlen (header_fmt) - 1; /* then find the end of the string */
va_end (argptr); /* clean up the argument list */
break; /* and exit with the job complete */
if (*fptr == '\n') { /* if the format ends with a LF */
*fptr++ = '\r'; /* then replace it */
*fptr++ = '\n'; /* with a CR LF sequence */
*fptr = '\0'; /* and terminate with a NUL */
}
}
vfprintf (sim_deb, header_fmt, argptr); /* format and print to the debug stream */
va_end (argptr); /* clean up the argument list */
break; /* and exit with the job complete */
}
else /* otherwise */
debptr++; /* look at the next debug table entry */
debptr++; /* look at the next trace table entry */
}
return;
@ -2713,10 +2747,11 @@ exdep_handler = find_cmd ("EXAMINE")->action; /* set the EXAMINE/DEPOS
run_handler = find_cmd ("RUN")->action; /* and the RUN/GO command handler */
break_handler = find_cmd ("BREAK")->action; /* and the BREAK/NOBREAK command handler */
sim_vm_release = hp_release; /* set up the release string */
sim_vm_cmd = aux_cmds; /* set up the auxiliary command table */
sim_vm_fprint_stopped = &fprint_stopped; /* set up the simulation-stop printer */
sim_vm_fprint_addr = &fprint_addr; /* set up the address printer */
sim_vm_parse_addr = &parse_addr; /* set up the address parser */
sim_vm_fprint_stopped = fprint_stopped; /* set up the simulation-stop printer */
sim_vm_fprint_addr = fprint_addr; /* set up the address printer */
sim_vm_parse_addr = parse_addr; /* set up the address parser */
sim_brk_types = BP_SUPPORTED; /* register the supported breakpoint types */
sim_brk_dflt = BP_EXEC; /* the default breakpoint type is "execution" */

View File

@ -1,6 +1,6 @@
SIMH/HP 3000 DIAGNOSTICS PERFORMANCE
====================================
Last update: 2016-12-01
Last update: 2020-10-22
The HP 32230 diagnostic suite has been run against the SIMH HP 3000 simulation.
@ -52,7 +52,7 @@ The results of the diagnostic runs are summarized below:
PD429A Selector Channel Maintenance Board 01.01 Passed
PD430A Error Correction Memory Series II 01.01 No simulation
PD430B Error Correction Memory Series III 00.00 No simulation
PD431A Extended Instruction Set 01.00 No simulation
PD431A Extended Instruction Set 01.00 Passed
PD432A Hardwired Serial Interface 01.00 No simulation
PD433A 7970B/E Nine-Track Magnetic Tape 01.04 Partial
PD434A Synchronous Line Controller 01.03 No simulation
@ -1130,6 +1130,57 @@ TEST RESULT: Passed.
-----------------------------------------
D431A - Extended Instruction Set Firmware
-----------------------------------------
TESTED DEVICE: CPU (hp3000_cpu_eis.c)
CONFIGURATION: sim> set cpu eis
sim> go
TEST REPORT: [CR entered]
D01 HP30012A EXTENDED-INSTRUCTION SET DIAGNOSTIC (D431.01.00)
(C)COPYRIGHT HEWLETT PACKARD COMPANY 1976.
Q01 SELECT SWREG OPTIONS
Programmed halt, CIR: 030366 (HALT 6), P: 010117 (RSW)
sim> deposit SWCH 140000
sim> go
Q02 SELECT SECTION SWREG. OPTIONS
Programmed halt, CIR: 030365 (HALT 5), P: 010165 (RSW)
sim> deposit SWCH 160000
sim> go
Q03 RESTORE SWREG OPTIONS
Programmed halt, CIR: 030367 (HALT 7), P: 010203 (RSW)
sim> deposit SWCH 100011
sim> go
Q04 ENTER MAXIMUM ERROR COUNT# = 50
Q05 ENTER PASS NUMBER =1
D02 1 PASS COMPLETED
Programmed halt, CIR: 030375 (HALT 15), P: 046030 (BR P+1,I)
TEST RESULT: Passed.
TEST NOTES: The diagnostic nominally executes 200 passes per program cycle.
It is reconfigured to execute a single pass, as multiple passes
are not relevant under simulation.
----------------------------------------------------
D433A - 7970B Nine-Track Magnetic Tape (single unit)
----------------------------------------------------
@ -1858,7 +1909,7 @@ TEST RESULT: Passed.
D441A - COBOL-II A Firmware
---------------------------
TESTED DEVICE: CPU (hp3000_cpu.c)
TESTED DEVICE: CPU (hp3000_cpu_cis.c)
CONFIGURATION: sim> set cpu cis
sim> go
@ -1987,7 +2038,7 @@ TEST RESULT: Passed.
D442A - COBOL-II B Firmware
---------------------------
TESTED DEVICE: CPU (hp3000_cpu.c)
TESTED DEVICE: CPU (hp3000_cpu_cis.c)
CONFIGURATION: sim> set cpu cis
sim> go

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,7 @@
SIMH/HP 3000 RELEASE NOTES
================================
J. David Bryan <jdbryan@acm.org>
Last update: 2020-02-14
Last update: 2020-11-07
This file documents the release history of the simulator for the Hewlett-Packard
@ -249,6 +249,294 @@ the MPE version used:
=====================
Release 9, 2020-11-07
=====================
This release of the HP 3000 simulator adds the following features:
- A new concurrent-mode FLUSH command has been added to flush terminal logs and
attached device files that otherwise would be flushed only when the simulator
is stopped. This allows external examination of these files while the
simulator continues to run.
- Terminal multiplexer line logs are now flushed each time the simulator stops.
Prior to this, closing and then reopening a line log was the only way to post
all buffered writes to disc.
- The Extended Instruction Set firmware is now available. The new SET CPU EIS
option enables the firmware.
- A new LINEORDER option has been added to the ATCD device to allow a
user-specified connection order or to restrict connections to a subset of the
available lines.
- The -N (new file) option has been added to the SET ATCD LOG command to create
a new, blank log file. Without the option, the command will append to an
existing log file.
- Microcode traps, such as bounds violations, now print the register values if
the REG trace option is enabled.
--------------------
Implementation Notes
--------------------
- Prior releases relied upon MPE to trap and simulate EIS instructions. Those
instructions are now implemented directly, resulting in a large reduction in
execution time for the individual instructions. Programs that made heavy use
of double-precision floating-point calculations or decimal (BCD) arithmetic
may see an increase in performance.
- The HP 30012A Extended Instruction Set was standard equipment on the Series
II and III. As such, the simulator should reject a SET CPU NOEIS command
with the "Command not allowed" message. However, the NOEIS option is allowed
to enable performance comparisons between hardware and software instruction
emulations. Executing SET CPU NOEIS will revert to emulation of the EIS
instructions by MPE.
----------
Bugs Fixed
----------
1. PROBLEM: Breakpoint actions after an included true IF are discarded.
VERSION: Release 8.
OBSERVATION: If a breakpoint has actions that include an IF command
followed by additional actions, and the IF command evaluates to TRUE, then
the IF actions executed, but the remaining breakpoint actions are
discarded. For example:
sim> set environment X=0
sim> break 10 ; if "%X%" == "0" echo X is 0 ; echo Done
sim> go
One would expect to see:
Breakpoint, P: 000010 (NOP,NOP)
sim> if "0" == "0" echo X is 0
sim> echo X is 0
X is 0
sim> echo Done
Done
sim>
Instead, only the first ECHO is performed. The second one is discarded.
However, execution is correct if the IF condition is false:
sim> set environment X=1
sim> break 10 ; if "%X%" == "0" echo X is 0 ; echo Done
sim> go
Breakpoint, P: 000010 (NOP,NOP)
sim> if "1" == "0" echo X is 0
sim> echo Done
Done
sim>
CAUSE: IF actions are executed by setting the breakpoint action pointer to
the action list and returning to the command loop. While the IF command is
executing, the pointer is pointing at the "echo Done" part of the
breakpoint action list. However, if the IF condition is true, the pointer
is changed to point at the "echo X is 0" command, and the remaining
breakpoint actions are lost.
RESOLUTION: Modify "if_cmd" (sim_extension.c) to append the remaining
breakpoint actions to the actions specified by the IF command, so that the
former are not lost.
STATUS: Fixed in Release 9.
2. PROBLEM: Linking with recent compilers results in duplicate symbol errors.
VERSION: Release 8.
OBSERVATION: If the simulator is compiled with a recent compiler version,
the link step fails with duplicate symbol errors. The symbols reported are
"sim_vm_release", "vm_sim_vm_init", "vm_console_input_unit", and
"vm_console_output_unit".
CAUSE: The VM hook extension mechanism is implemented with "tentative
definitions" of the hook variables. The C standard says:
"If a translation unit contains one or more tentative definitions for an
identifier, and the translation unit contains no external definition for
that identifier, then the behavior is exactly as if the translation unit
contains a file scope declaration of that identifier, with the composite
type as of the end of the translation unit, with an initializer equal to
0."
This behavior is such that if no module contains a definition with an
initializer, the hook will have a zero value. However, if a module
contains a definition with an initializer, the hook is assigned that value.
This allows hooks to be set without changing the hook's tentative
definition simply by including a VM module that declares it with an
initializer.
This mechanism relies on the linker to resolve the multiple definitions of
a given hook to a single reference. For this to occur, the compiler must
mark tentative definitions as "common" allocations, e.g., with the
"-fcommon" option to gcc. Traditionally, gcc (and clang, etc.) defaults to
common allocations for tentative definitions. However, the gcc manual
claims that, "This behavior is not required by ISO C, and on some targets
may carry a speed or code size penalty on variable references."
Newer versions (starting with gcc 10) default to data allocations instead
("-fno-common"), and multiple tentative definitions now result in duplicate
symbol errors rather than merged accesses.
RESOLUTION: Modify sim_extension.h to declare "vm_sim_vm_init" only if the
USE_VM_INIT symbol is defined. Modify "ex_initialize" (sim_extension.c) to
remove the tentative definition and to make the external "vm_sim_vm_init"
call conditional on USE_VM_INIT. Modify "one_time_init" (hp3000_sys.c) to
set the "sim_vm_release" hook directly. Modify "atcd_reset" (hp3000_atc.c)
to set the console unit hooks directly. This removes all tentative
definitions from the simulators.
STATUS: Fixed in Release 9.
3. PROBLEM: Trace output to stdout on Unix results in stair-step output.
VERSION: Release 8.
OBSERVATION: Directing the trace output to "stdout" on a Unix system
results in lines stair-stepping across the screen. For example:
sim> set console debug=stdout
sim> set cpu debug=instr
sim> step 2
...produces this output:
>>CPU instr: 00.000000 000000 NOP,NOP
>>CPU instr: 00.000001 000000 NOP,NOP
CAUSE: Trace statements are output with LF ('\n') line ends and depend on
host-system translation to the proper line-end convention when the lines
are written to the trace log. However, while the simulator is executing
instructions, the console is placed in "raw" mode so that output
translation, which would interfere with the output from the target
operating system, is not done. As there are no carriage returns in the
trace output stream when writing to stdout, the console cursor simply drops
in place to the next line, so that each line begins at the same column
where the previous line ended.
RESOLUTION: Modify "hp_trace" (hp3000_sys.c) to convert a terminating LF
to a CR LF sequence if output is to stdout. Also modify "sim_instr"
(hp3000_cpu.c) and "edit" (hp3000_cpu_cis.c) to add CR characters to the
stdout stream where line termination is done explicitly.
STATUS: Fixed in Release 9.
4. PROBLEM: CPU EXEC traces can include unrelated process clock events.
VERSION: Release 8.
OBSERVATION: When the SET CPU DEBUG=EXEC command is used to trace specific
CPU instruction executions, process clock event traces may be embedded.
For example, tracing the ABSD instruction is seen to produce:
>>CPU exec: *****************
>>CPU reg: 00.045172 000002 A 000005, B 000316, X 000001, M i t r o C CCG
>>CPU fetch: 00.042440 000047 instruction fetch
>>CPU instr: 00.042437 020477 ABSD 1
>>CPU data: 00.045172 020040 stack read
>>CPU data: 00.045171 020040 stack read
>>CPU fetch: 00.042441 140003 instruction fetch
>>CPU opnd: 00.045113 000316 source 5,"12345D"
>>CPU data: 00.045114 056400 data read
>>CPU data: 00.045114 057400 data write
>>CPU opnd: 00.045113 000316 target 5,"12345F"
>>CPU pserv: Process clock delay 3890 service entered on the user stack
>>CPU pserv: Simulation rate 1x
>>CPU reg: 00.045170 000002 A 020040, B 020040, X 000001, M i t r o C CCL
>>CPU exec: *****************
The PSERV trace is unrelated to instruction execution.
CAUSE: EXEC tracing works by enabling all trace options when the target
instruction is present in the CIR. However, it should enable just the
trace options relevant to execution.
RESOLUTION: Modify the definition of DEB_ALL (hp3000_cpu.h) to exclude the
DEB_PSERV trace option.
STATUS: Fixed in Release 9.
5. PROBLEM: A bounds violation can occur with valid CVND operands.
VERSION: Release 8.
OBSERVATION: The following valid SPL program:
BEGIN
BYTE ARRAY X (0:5) := "-31416";
PROCEDURE CVND (DISPLAY);
BYTE ARRAY DISPLAY;
BEGIN
BYTE ARRAY ASCII (0:5);
TOS := @ASCII;
TOS := DISPLAY;
TOS := 6;
ASSEMBLE (CON %020477; << CVND LS,1 >>
CON %000021);
END;
CVND (X);
END.
...produces this error:
PROGRAM ERROR #24 :BOUNDS VIOLATION
CAUSE: Tracing the instruction execution shows:
>>CPU reg: 01.042453 000003 A 000006, B 000002, C 000042, X 000000, m I T r o c CCG
>>CPU reg: 01.000000 000301 PB 177630, PL 177653, DL 042274, DB 042430, Q 042447, Z 044714
>>CPU fetch: 04.177644 000021 instruction fetch
>>CPU instr: 04.177643 020477 CVND LS,1
>>CPU data: 01.042453 042430 stack read
>>CPU fetch: 04.177645 031401 instruction fetch
>>CPU reg: 01.042452 000004 A 000006, B 000002, C 000042, D 042430, X 000000, m I T r o c CCG
>>CPU instr: 04.177645 000000 bounds violation trap
The HP 3000 microcode preloads four top-of-stack registers before calling
any of the firmware extension instruction routines. However, only three
TOS values are pushed for the CVND instruction, so the fourth register
actually contains a word from whatever was on the stack before the
instruction was executed. If the source or target buffer resides on the
stack immediately below the three CVND parameters, preloading the fourth
TOS register sets SM to point below the last word of the buffer, and a
bounds violation results.
In the above trace, the stack preload shows the fourth TOS register is read
from location 042453. This increments SR and decrements SM to point at
042452, as shown in the REG trace two lines later. The C register contains
the relative byte address of the target array -- in this case, 42 (octal)
relative to the DB register contents. The starting word address is
therefore 42 / 2 + 042430 = 042451. The buffer is 6 bytes or 3 words long,
so the ending address is 042453. But because the preload pulled in a
fourth stack word (that was never pushed), the end of the buffer is under
SM, and a bounds violation occurs.
RESOLUTION: Modify the CVND executor in "cpu_cis_op" (hp3000_cpu_cis.c) to
queue down (i.e., transfer from a register back to memory) the fourth TOS
register value before checking the buffer legality.
STATUS: Fixed in Release 9.
=====================
Release 8, 2020-02-15
=====================