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:
parent
0e119d70bb
commit
6da2ce719e
@ -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 */
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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 */
|
||||
}
|
||||
|
||||
@ -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
2489
HP3000/hp3000_cpu_eis.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -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 */
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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 */
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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" */
|
||||
|
||||
@ -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
|
||||
|
||||
12657
doc/hp3000_guide.pdf
12657
doc/hp3000_guide.pdf
File diff suppressed because it is too large
Load Diff
@ -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
|
||||
=====================
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user