1
0
mirror of https://github.com/open-simh/simh.git synced 2026-03-25 17:58:54 +00:00

HP2100: Release 29

This commit is contained in:
J. David Bryan
2020-02-19 17:04:20 -08:00
committed by Mark Pizzolato
parent 2288fa348f
commit 324fba049a
45 changed files with 104241 additions and 0 deletions

1748
HP2100/hp2100_baci.c Normal file

File diff suppressed because it is too large Load Diff

5234
HP2100/hp2100_cpu.c Normal file

File diff suppressed because it is too large Load Diff

645
HP2100/hp2100_cpu.h Normal file
View File

@@ -0,0 +1,645 @@
/* hp2100_cpu.h: HP 2100 CPU declarations
Copyright (c) 2005-2016, Robert M. Supnik
Copyright (c) 2017-2019, J. David Bryan
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the names of the authors shall not be
used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from the authors.
23-Jan-19 JDB Removed fmt_ab global declaration; now local to hp2100_cpu5.c
removed cpu_user_20 global declaration
02-Aug-18 JDB Removed cpu_ema_* helper routine global declarations
20-Jul-18 JDB Incorporated declarations and changelog from hp2100_cpu1.h
10-Jul-18 JDB Moved IBL info to hp2100_io.h
14-Jun-18 JDB Renamed MST_PRO to MST_PROT
05-Jun-18 JDB Revised I/O model
22-Feb-18 JDB Reworked "cpu_ibl" into "cpu_copy_loader"
Cleaned up IBL definitions, added loader structure
22-Jul-17 JDB Renamed "intaddr" to CIR; added IR
14-Jul-17 JDB Removed calc_defer() prototype
11-Jul-17 JDB Moved "ibl_copy" and renamed to "cpu_ibl"
10-Jul-17 JDB Renamed the global routine "iogrp" to "cpu_iog"
07-Jul-17 JDB Changed "iotrap" from uint32 to t_bool
07-Jun-17 JDB Added maximum instruction length for sim_emax definition
06-Jun-17 JDB Added instruction group decoding macros
04-Apr-17 JDB Added "cpu_configuration" for symbolic ex/dep validation
08-Mar-17 JDB Added "cpu_speed" for TBG service access
15-Feb-17 JDB Deleted unneeded guard macro definition
26-Jan-17 JDB Removed debug parameters from cpu_ema_* routines
17-Jan-17 JDB Removed register print encoding constants (now redundant)
05-Aug-16 JDB Renamed the P register from "PC" to "PR"
24-Dec-14 JDB Added casts for explicit downward conversions
18-Mar-13 JDB Added declarations for the MP abort handler and CPU registers
Added externs for microcode helper functions
14-Mar-13 MP Changed guard macro name to avoid reserved namespace
03-Jan-10 RMS Changed declarations of mp_control, mp_mefvv, for VMS compiler
11-Sep-08 JDB Moved microcode function prototypes here
15-Jul-08 JDB Rearranged declarations with hp2100_cpu.c and hp2100_defs.h
26-Jun-08 JDB Added mp_control to CPU state externals
30-Apr-08 JDB Corrected OP_AFF to OP_AAFF for SIGNAL/1000
Removed unused operand patterns
24-Apr-08 JDB Added calc_defer() prototype
20-Apr-08 JDB Added DEB_VIS and DEB_SIG debug flags
23-Feb-08 HV Added more OP_* for SIGNAL/1000 and VIS
28-Nov-07 JDB Added fprint_ops, fprint_regs for debug printouts
26-Nov-07 JDB Added extern sim_deb, cpu_dev, DEB flags for debug printouts
05-Nov-07 JDB Added extern intaddr, mp_viol, mp_mevff, calc_int, dev_ctl,
ReadIO, WriteIO for RTE-6/VM microcode support
19-Oct-07 JDB Revised OP_KKKAKK operand profile to OP_CCCACC for $LOC
16-Dec-06 JDB Added UNIT_2115 and UNIT_2114
16-Oct-06 JDB Moved ReadF to hp2100_cpu1.c
Generalized operands for F-Series FP types
26-Sep-06 JDB Added CPU externs for microcode simulators
Split from hp2100_cpu1.c
16-Aug-06 JDB Added UNIT_EMA for future RTE-4 EMA microcode
Added UNIT_VMA for future RTE-6 VMA and OS microcode
Added UNIT_1000_F for future F-Series support
09-Aug-06 JDB Added UNIT_DBI for double integer microcode
21-Jan-05 JDB Reorganized CPU option flags
14-Jan-05 RMS Cloned from hp2100_cpu.c
CPU models are broken down into family, type, and series to facilitate option
validation. Bit 3 encodes the family, bit 2 encodes the type, and bits 1:0
encode the series within the type.
*/
/* Memory access macros.
These macros provide simplified function call sequences for memory reads and
writes by the CPU. They supply the correct access classification. The
following macro routines are provided:
Name Action
------- ------------------------------------------------------------
ReadF Read an instruction word using the current map
ReadW Read a data word using the current map
ReadWA Read a data word using the alternate map
ReadS Read a data word using the system map
ReadU Read a data word using the user map
ReadB Read a data byte using the current map
ReadBA Read a data byte using the alternate map
WriteW Write a data word using the current map
WriteWA Write a data word using the alternate map
WriteS Write a data word using the system map
WriteU Write a data word using the user map
WriteB Write a data byte using the current map
WriteBA Write a data byte using the alternate map
*/
#define ReadF(a) mem_read (&cpu_dev, Fetch, a)
#define ReadW(a) mem_read (&cpu_dev, Data, a)
#define ReadWA(a) mem_read (&cpu_dev, Data_Alternate, a)
#define ReadS(a) mem_read (&cpu_dev, Data_System, a)
#define ReadU(a) mem_read (&cpu_dev, Data_User, a)
#define ReadB(a) mem_read_byte (&cpu_dev, Data, a)
#define ReadBA(a) mem_read_byte (&cpu_dev, Data_Alternate, a)
#define WriteW(a,v) mem_write (&cpu_dev, Data, a, v)
#define WriteWA(a,v) mem_write (&cpu_dev, Data_Alternate, a, v)
#define WriteS(a,v) mem_write (&cpu_dev, Data_System, a, v)
#define WriteU(a,v) mem_write (&cpu_dev, Data_User, a, v)
#define WriteB(a,v) mem_write_byte (&cpu_dev, Data, a, v)
#define WriteBA(a,v) mem_write_byte (&cpu_dev, Data_Alternate, a, v)
/* CPU private tracing flags.
Private flags are allocated in descending order to avoid conflicts
with global flags that are allocated in ascending order.
*/
#define DEBUG_NOOS (1u << 31) /* configure RTE-6/VM not to use OS firmware */
#define TRACE_INSTR (1u << 30) /* trace instruction executions */
#define TRACE_DATA (1u << 29) /* trace memory data accesses */
#define TRACE_FETCH (1u << 28) /* trace memory instruction fetches */
#define TRACE_REG (1u << 27) /* trace register values */
#define TRACE_OPND (1u << 26) /* trace instruction operands */
#define TRACE_EXEC (1u << 25) /* trace matching instruction execution states */
#define TRACE_SR (1u << 24) /* trace DMA service requests received */
#define TRACE_ALL ~DEBUG_NOOS /* trace everything */
#define REGA_FORMAT "%c **** %05o %06o " /* protection | fence | S register format for working registers */
#define REGB_FORMAT "%c **** ***** ****** " /* protection format for MP/MEM registers */
#define OPND_FORMAT "* **** %05o %06o " /* address | data format for operands */
#define EXEC_FORMAT "******************** " /* null format for EXEC separation */
#define DMS_FORMAT "%c %04o %05o %06o " /* map | physical page | logical address | value format */
/* CPU stop flags */
#define SS_INHIBIT (t_stat) (~0u) /* inhibit stops for the first instruction executed */
#define STOP(s) ((s) & ~cpu_ss_inhibit) /* stop if the condition is enabled and not inhibited */
/* Supported breakpoint switches */
#define BP_EXEC (SWMASK ('E')) /* an execution breakpoint */
#define BP_ENONE (SWMASK ('N')) /* an execution breakpoint when mapping is off */
#define BP_ESYS (SWMASK ('S')) /* an execution breakpoint in the system map */
#define BP_EUSER (SWMASK ('U')) /* an execution breakpoint in the user map */
#define BP_SUPPORTED (BP_EXEC | BP_ENONE | BP_ESYS | BP_EUSER)
/* PC queue */
#define PCQ_SIZE 64 /* must be 2 ** n */
#define PCQ_MASK (PCQ_SIZE - 1)
#define PCQ_ENTRY pcq [pcq_p = (pcq_p - 1) & PCQ_MASK] = (uint16) err_PR
/* Maximum instruction length.
This value is the length in words of the longest machine instruction. It is
used to set "sim_emax", which, in turn, is used to allocate the "sim_eval"
array. This array holds the words of a machine instruction to be formatted
and printed or to be parsed and stored.
The longest instruction in the 21xx/1000 family is the [D]VPIV (vector pivot)
instruction in the Vector Instruction Set.
*/
#define MAX_INSTR_LENGTH 10
/* Instruction group decoding.
The HP 21xx/1000 instruction set consists of five groups: the Memory
Reference Group (MRG), the Shift-Rotate Group (SRG), the Alter-Skip Group
(ASG), the I/O Group (IOG), and the Macro Group (MAC). Group membership is
determined by a multi-level decoding of bits 15-10, as follows:
Bits
15-10 Group
------ -----
xnnnx MRG
00000 SRG
00001 ASG
10001 IOG
10000 MAC
Where:
x = 0 or 1
n = any collective value other than 0
The MAC group is subdivided into the Extended Arithmetic Group (EAG), the
first User Instruction Group (UIG-0), and the second User Instruction Group
(UIG-1). Decoding is by bits 11-8, as follows (note that bit 10 = 0 for the
MAC group):
Bits
11-8 Group
---- -----
0000 EAG
0001 EAG
0010 EAG
0011 UIG-1
1000 EAG
1001 EAG
1010 UIG-0
1011 UIG-1
Bits 7-4 further decode the UIG instruction feature group.
*/
#define GROUP_MASK 0172000u /* instruction group mask */
#define MRG 0070000u /* Memory Reference Group indicator */
#define SRG 0000000u /* Shift-Rotate Group indicator */
#define ASG 0002000u /* Alter-Skip Group indicator */
#define IOG 0102000u /* I/O Group indicator */
#define MRGOP(v) (((v) & MRG) != 0) /* MRG membership test */
#define SRGOP(v) (((v) & GROUP_MASK) == SRG) /* SRG membership test */
#define ASGOP(v) (((v) & GROUP_MASK) == ASG) /* ASG membership test */
#define IOGOP(v) (((v) & GROUP_MASK) == IOG) /* IOG membership test */
#define SRG_CLE 0000040u /* SRG CLE opcode */
#define SRG_SLx 0000010u /* SRG SLA/SLB opcode */
#define SRG_NOP 0000000u /* SRG no-operation opcode */
#define SRG1_DE_MASK 0001000u /* SRG disable/enable first micro-op field bit */
#define SRG1_MASK 0001700u
#define SRG1_SHIFT 6
#define SRG2_DE_MASK 0000020u /* SRG disable/enable second micro-op field bit */
#define SRG2_MASK 0000027u
#define SRG2_SHIFT 0
#define SRG1(u) (((u) & SRG1_MASK) >> SRG1_SHIFT)
#define SRG2(u) (((u) & SRG2_MASK) >> SRG2_SHIFT)
#define IOG_MASK 0001700u
#define IOG_SHIFT 6
#define IOG_OP(v) ((IO_GROUP_OP) (((v) & IOG_MASK) >> IOG_SHIFT))
#define UIG_MASK 0000360u /* UIG feature group mask */
#define UIG_SHIFT 4 /* UIG feature group alignment shift */
#define UIG(i) (((i) & UIG_MASK) >> UIG_SHIFT)
#define UIG_0_MASK 0177400u /* UIG-0 opcode mask */
#define UIG_0_RANGE 0105000u /* UIG-0 opcode range */
#define UIG_1_MASK 0173400u /* UIG-1 opcode mask */
#define UIG_1_RANGE 0101400u /* UIG-1 opcode range */
#define UIG_0_OP(i) (((i) & UIG_0_MASK) == UIG_0_RANGE) /* UIG-0 membership test */
#define UIG_1_OP(i) (((i) & UIG_1_MASK) == UIG_1_RANGE) /* UIG-1 membership test */
#define RTE_IRQ_RANGE 0105354u /* RTE-6/VM interrupt request instructions range */
/* Memory Reference Group instruction register fields */
#define IR_IND 0100000u /* indirect address bit */
#define IR_CP 0002000u /* current page bit */
#define IR_OFFSET 0001777u /* page offset */
#define MR_PAGE 0076000u /* page number bits within a memory address */
/* I/O Group instruction register fields */
#define IR_HCF 0001000u /* hold/clear flag bit */
/* General instruction register fields */
#define AB_MASK 0004000u /* A or B register select bit */
#define AB_SHIFT 11
#define AB_SELECT(i) (((i) & AB_MASK) >> AB_SHIFT)
/* Instruction operand processing encoding */
/* Base operand types. Note that all address encodings must be grouped together
after OP_ADR.
*/
#define OP_NUL 0 /* no operand */
#define OP_IAR 1 /* 1-word int in A reg */
#define OP_JAB 2 /* 2-word int in A/B regs */
#define OP_FAB 3 /* 2-word FP const in A/B regs */
#define OP_CON 4 /* inline 1-word constant */
#define OP_VAR 5 /* inline 1-word variable */
#define OP_ADR 6 /* inline address */
#define OP_ADK 7 /* addr of 1-word int const */
#define OP_ADD 8 /* addr of 2-word int const */
#define OP_ADF 9 /* addr of 2-word FP const */
#define OP_ADX 10 /* addr of 3-word FP const */
#define OP_ADT 11 /* addr of 4-word FP const */
#define OP_ADE 12 /* addr of 5-word FP const */
#define OP_N_FLAGS 4 /* number of bits needed for flags */
#define OP_M_FLAGS ((1 << OP_N_FLAGS) - 1) /* mask for flag bits */
#define OP_N_F (8 * sizeof (uint32) / OP_N_FLAGS) /* max number of op fields */
#define OP_V_F1 (0 * OP_N_FLAGS) /* 1st operand field */
#define OP_V_F2 (1 * OP_N_FLAGS) /* 2nd operand field */
#define OP_V_F3 (2 * OP_N_FLAGS) /* 3rd operand field */
#define OP_V_F4 (3 * OP_N_FLAGS) /* 4th operand field */
#define OP_V_F5 (4 * OP_N_FLAGS) /* 5th operand field */
#define OP_V_F6 (5 * OP_N_FLAGS) /* 6th operand field */
#define OP_V_F7 (6 * OP_N_FLAGS) /* 7th operand field */
#define OP_V_F8 (7 * OP_N_FLAGS) /* 8th operand field */
/* Operand processing patterns */
#define OP_N (OP_NUL << OP_V_F1)
#define OP_I (OP_IAR << OP_V_F1)
#define OP_J (OP_JAB << OP_V_F1)
#define OP_R (OP_FAB << OP_V_F1)
#define OP_C (OP_CON << OP_V_F1)
#define OP_V (OP_VAR << OP_V_F1)
#define OP_A (OP_ADR << OP_V_F1)
#define OP_K (OP_ADK << OP_V_F1)
#define OP_D (OP_ADD << OP_V_F1)
#define OP_X (OP_ADX << OP_V_F1)
#define OP_T (OP_ADT << OP_V_F1)
#define OP_E (OP_ADE << OP_V_F1)
#define OP_IA ((OP_IAR << OP_V_F1) | (OP_ADR << OP_V_F2))
#define OP_JA ((OP_JAB << OP_V_F1) | (OP_ADR << OP_V_F2))
#define OP_JD ((OP_JAB << OP_V_F1) | (OP_ADD << OP_V_F2))
#define OP_RC ((OP_FAB << OP_V_F1) | (OP_CON << OP_V_F2))
#define OP_RK ((OP_FAB << OP_V_F1) | (OP_ADK << OP_V_F2))
#define OP_RF ((OP_FAB << OP_V_F1) | (OP_ADF << OP_V_F2))
#define OP_CV ((OP_CON << OP_V_F1) | (OP_VAR << OP_V_F2))
#define OP_AC ((OP_ADR << OP_V_F1) | (OP_CON << OP_V_F2))
#define OP_AA ((OP_ADR << OP_V_F1) | (OP_ADR << OP_V_F2))
#define OP_AK ((OP_ADR << OP_V_F1) | (OP_ADK << OP_V_F2))
#define OP_AX ((OP_ADR << OP_V_F1) | (OP_ADX << OP_V_F2))
#define OP_AT ((OP_ADR << OP_V_F1) | (OP_ADT << OP_V_F2))
#define OP_KV ((OP_ADK << OP_V_F1) | (OP_VAR << OP_V_F2))
#define OP_KA ((OP_ADK << OP_V_F1) | (OP_ADR << OP_V_F2))
#define OP_KK ((OP_ADK << OP_V_F1) | (OP_ADK << OP_V_F2))
#define OP_IIF ((OP_IAR << OP_V_F1) | (OP_IAR << OP_V_F2) | \
(OP_ADF << OP_V_F3))
#define OP_IAT ((OP_IAR << OP_V_F1) | (OP_ADR << OP_V_F2) | \
(OP_ADT << OP_V_F3))
#define OP_CVA ((OP_CON << OP_V_F1) | (OP_VAR << OP_V_F2) | \
(OP_ADR << OP_V_F3))
#define OP_AAA ((OP_ADR << OP_V_F1) | (OP_ADR << OP_V_F2) | \
(OP_ADR << OP_V_F3))
#define OP_AAF ((OP_ADR << OP_V_F1) | (OP_ADR << OP_V_F2) | \
(OP_ADF << OP_V_F3))
#define OP_AAX ((OP_ADR << OP_V_F1) | (OP_ADR << OP_V_F2) | \
(OP_ADX << OP_V_F3))
#define OP_AAT ((OP_ADR << OP_V_F1) | (OP_ADR << OP_V_F2) | \
(OP_ADT << OP_V_F3))
#define OP_AKA ((OP_ADR << OP_V_F1) | (OP_ADK << OP_V_F2) | \
(OP_ADR << OP_V_F3))
#define OP_AKK ((OP_ADR << OP_V_F1) | (OP_ADK << OP_V_F2) | \
(OP_ADK << OP_V_F3))
#define OP_AXX ((OP_ADR << OP_V_F1) | (OP_ADX << OP_V_F2) | \
(OP_ADX << OP_V_F3))
#define OP_ATT ((OP_ADR << OP_V_F1) | (OP_ADT << OP_V_F2) | \
(OP_ADT << OP_V_F3))
#define OP_AEE ((OP_ADR << OP_V_F1) | (OP_ADE << OP_V_F2) | \
(OP_ADE << OP_V_F3))
#define OP_AAXX ((OP_ADR << OP_V_F1) | (OP_ADR << OP_V_F2) | \
(OP_ADX << OP_V_F3) | (OP_ADX << OP_V_F4))
#define OP_AAFF ((OP_ADR << OP_V_F1) | (OP_ADR << OP_V_F2) | \
(OP_ADF << OP_V_F3) | (OP_ADF << OP_V_F4))
#define OP_AAKK ((OP_ADR << OP_V_F1) | (OP_ADR << OP_V_F2) | \
(OP_ADK << OP_V_F3) | (OP_ADK << OP_V_F4))
#define OP_KKKK ((OP_ADK << OP_V_F1) | (OP_ADK << OP_V_F2) | \
(OP_ADK << OP_V_F3) | (OP_ADK << OP_V_F4))
#define OP_AAAKK ((OP_ADR << OP_V_F1) | (OP_ADR << OP_V_F2) | \
(OP_ADR << OP_V_F3) | (OP_ADK << OP_V_F4) | \
(OP_ADK << OP_V_F5))
#define OP_AKAKK ((OP_ADR << OP_V_F1) | (OP_ADK << OP_V_F2) | \
(OP_ADR << OP_V_F3) | (OP_ADK << OP_V_F4) | \
(OP_ADK << OP_V_F5))
#define OP_AAACCC ((OP_ADR << OP_V_F1) | (OP_ADR << OP_V_F2) | \
(OP_ADR << OP_V_F3) | (OP_CON << OP_V_F4) | \
(OP_CON << OP_V_F5) | (OP_CON << OP_V_F6))
#define OP_AAFFKK ((OP_ADR << OP_V_F1) | (OP_ADR << OP_V_F2) | \
(OP_ADF << OP_V_F3) | (OP_ADF << OP_V_F4) | \
(OP_ADK << OP_V_F5) | (OP_ADK << OP_V_F6))
#define OP_AAKAKK ((OP_ADR << OP_V_F1) | (OP_ADR << OP_V_F2) | \
(OP_ADK << OP_V_F3) | (OP_ADR << OP_V_F4) | \
(OP_ADK << OP_V_F5) | (OP_ADK << OP_V_F6))
#define OP_CATAKK ((OP_CON << OP_V_F1) | (OP_ADR << OP_V_F2) | \
(OP_ADT << OP_V_F3) | (OP_ADR << OP_V_F4) | \
(OP_ADK << OP_V_F5) | (OP_ADK << OP_V_F6))
#define OP_CCCACC ((OP_CON << OP_V_F1) | (OP_CON << OP_V_F2) | \
(OP_CON << OP_V_F3) | (OP_ADR << OP_V_F4) | \
(OP_CON << OP_V_F5) | (OP_CON << OP_V_F6))
#define OP_AAAFFKK ((OP_ADR << OP_V_F1) | (OP_ADR << OP_V_F2) | \
(OP_ADR << OP_V_F3) | (OP_ADF << OP_V_F4) | \
(OP_ADF << OP_V_F5) | (OP_ADK << OP_V_F6) | \
(OP_ADK << OP_V_F7))
#define OP_AKAKAKK ((OP_ADR << OP_V_F1) | (OP_ADK << OP_V_F2) | \
(OP_ADR << OP_V_F3) | (OP_ADK << OP_V_F4) | \
(OP_ADR << OP_V_F5) | (OP_ADK << OP_V_F6) | \
(OP_ADK << OP_V_F7))
#define OP_AAKAKAKK ((OP_ADR << OP_V_F1) | (OP_ADR << OP_V_F2) | \
(OP_ADK << OP_V_F3) | (OP_ADR << OP_V_F4) | \
(OP_ADK << OP_V_F5) | (OP_ADR << OP_V_F6) | \
(OP_ADK << OP_V_F7) | (OP_ADK << OP_V_F8))
#define OP_CCACACCA ((OP_CON << OP_V_F1) | (OP_CON << OP_V_F2) | \
(OP_ADR << OP_V_F3) | (OP_CON << OP_V_F4) | \
(OP_ADR << OP_V_F5) | (OP_CON << OP_V_F6) | \
(OP_CON << OP_V_F7) | (OP_ADR << OP_V_F8))
/* Operand precisions (compatible with F-Series FPP):
- S = 1-word integer
- D = 2-word integer
- F = 2-word single-precision floating-point
- X = 3-word extended-precision floating-point
- T = 4-word double-precision floating-point
- E = 5-word expanded-exponent floating-point
- A = null operand (operand is in the FPP accumulator)
5-word floating-point numbers are supported by the F-Series Floating-Point
Processor hardware, but the instruction codes are not documented.
Implementation notes:
1. The enumeration rdering is important, as we depend on the "fp"
type values to reflect the number of words needed (2-5).
*/
typedef enum { /* operand precision */
in_s, /* 1-word integer */
in_d, /* 2-word integer */
fp_f, /* 2-word single-precision floating-point */
fp_x, /* 3-word extended-precision floating-point */
fp_t, /* 4-word double-precision floating-point */
fp_e, /* 5-word expanded-exponent floating-point */
fp_a /* null operand (operand is in FPP accumulator) */
} OPSIZE;
/* Conversion from operand size to word count */
#define TO_COUNT(s) ((s == fp_a) ? 0 : (uint32) (s + (s < fp_f)))
/* HP in-memory representation of a packed floating-point number.
Actual value will use two, three, four, or five words, as needed.
*/
typedef HP_WORD FPK [5];
/* Operand processing types.
NOTE: Microsoft VC++ 6.0 does not support the C99 standard, so we cannot
initialize unions by arbitrary variant ("designated initializers").
Therefore, we follow the C90 form of initializing via the first named
variant. The FPK variant must appear first in the OP structure, as we define
a number of FPK constants in other modules.
*/
typedef union { /* general operand */
FPK fpk; /* floating-point value */
HP_WORD word; /* 16-bit integer */
uint32 dword; /* 32-bit integer */
} OP;
typedef OP OPS[OP_N_F]; /* operand array */
typedef uint32 OP_PAT; /* operand pattern */
/* Microcode abort reasons */
typedef enum { /* Abort reason passed via "longjmp" */
Initialized = 0,
Memory_Protect,
Interrupt,
Indirect_Loop
} MICRO_ABORT;
/* CPU global register declarations */
#define AR ABREG [0] /* A register = memory location 0 */
#define BR ABREG [1] /* B register = memory location 1 */
extern HP_WORD ABREG [2]; /* A and B registers (use AR/BR to reference) */
extern HP_WORD PR; /* P register */
extern HP_WORD SR; /* S register */
extern HP_WORD MR; /* M register */
extern HP_WORD TR; /* T register */
extern HP_WORD XR; /* X register */
extern HP_WORD YR; /* Y register */
extern uint32 E; /* E register */
extern uint32 O; /* O register */
extern HP_WORD IR; /* Instruction Register */
extern HP_WORD CIR; /* Central Interrupt Register */
extern HP_WORD SPR; /* Stack Pointer Register (F-register for 2100) */
/* CPU global state */
extern DEVICE cpu_dev; /* CPU device structure */
extern HP_WORD err_PR; /* P register error value */
extern FLIP_FLOP cpu_interrupt_enable; /* interrupt enable flip-flop */
extern uint16 pcq [PCQ_SIZE]; /* PC queue (must be 16-bits wide for REG array entry) */
extern uint32 pcq_p; /* PC queue pointer */
extern t_stat cpu_ss_unimpl; /* status return for unimplemented instruction execution */
extern t_stat cpu_ss_undef; /* status return for undefined instruction execution */
extern t_stat cpu_ss_unsc; /* status return for I/O to an unassigned select code */
extern t_stat cpu_ss_indir; /* status return for indirect loop execution */
extern t_stat cpu_ss_inhibit; /* simulation stop inhibition mask */
extern t_stat cpu_ss_ioerr; /* status return for an unreported I/O error */
extern UNIT *cpu_ioerr_uptr; /* pointer to a unit with an unreported I/O error */
extern uint32 cpu_speed; /* the CPU speed, expressed as a multiplier of a real machine */
extern uint32 cpu_pending_interrupt; /* the select code of a pending interrupt or zero if none */
/* Microcode dispatcher functions (grouped by cpu module number) */
extern t_stat cpu_uig_0 (uint32 intrq, t_bool int_ack); /* [0] UIG group 0 dispatcher */
extern t_stat cpu_uig_1 (uint32 intrq); /* [0] UIG group 1 dispatcher */
extern t_stat cpu_ds (void); /* [0] Distributed System stub */
extern t_stat cpu_user (void); /* [0] User firmware dispatcher */
extern t_stat cpu_eau (void); /* [1] EAU group simulator */
extern t_stat cpu_iop (uint32 intrq); /* [1] 2000 I/O Processor */
#if !defined (HAVE_INT64) /* int64 support unavailable */
extern t_stat cpu_fp (void); /* [1] Firmware Floating Point */
#endif
extern t_stat cpu_dms (uint32 intrq); /* [2] Dynamic mapping system */
extern t_stat cpu_eig (HP_WORD IR, uint32 intrq); /* [2] Extended instruction group */
extern t_stat cpu_ffp (uint32 intrq); /* [3] Fast FORTRAN Processor */
extern t_stat cpu_dbi (HP_WORD IR); /* [3] Double-Integer instructions */
#if defined (HAVE_INT64) /* int64 support available */
extern t_stat cpu_fpp (HP_WORD IR); /* [4] Floating Point Processor */
extern t_stat cpu_sis (HP_WORD IR); /* [4] Scientific Instruction Set */
#endif
extern t_stat cpu_rte_ema (void); /* [5] RTE-IV EMA */
extern t_stat cpu_signal (void); /* [5] SIGNAL/1000 Instructions */
#if defined (HAVE_INT64) /* int64 support available */
extern t_stat cpu_vis (void); /* [5] Vector Instruction Set */
#endif
extern t_stat cpu_rte_os (t_bool int_ack); /* [6] RTE-6 OS */
extern t_stat cpu_rte_vma (void); /* [7] RTE-6 VMA */
/* Microcode helper functions */
extern OP ReadOp (HP_WORD va, OPSIZE precision); /* generalized operand read */
extern void WriteOp (HP_WORD va, OP operand, OPSIZE precision); /* generalized operand write */
extern t_stat cpu_ops (OP_PAT pattern, OPS op); /* operand processor */
/* CPU global SCP support routine declarations declared in scp.h
extern t_stat sim_instr (void);
*/
/* CPU global SCP support routine declarations */
extern void cpu_post_cmd (t_bool from_scp);
/* CPU global utility routine declarations */
extern t_stat cpu_iog (HP_WORD instruction);
extern t_stat cpu_resolve_indirects (t_bool is_interruptible);
extern void cpu_microcode_abort (MICRO_ABORT abort_reason);
/* I/O subsystem global utility routine declarations */
extern uint32 io_poll_interrupts (FLIP_FLOP interrupt_system);

801
HP2100/hp2100_cpu0.c Normal file
View File

@@ -0,0 +1,801 @@
/* hp2100_cpu0.c: HP 2100/1000 UIG dispatcher, user microcode, and unimplemented instruction stubs
Copyright (c) 2006-2018, J. David Bryan
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the name of the author shall not be
used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from the author.
CPU0 UIG dispatcher, user microcode, and unimplemented firmware options
02-Aug-18 JDB Moved UIG dispatcher from hp2100_cpu1.c
25-Jul-18 JDB Use cpu_configuration instead of cpu_unit.flags for tests
01-Aug-17 JDB Changed .FLUN and self-tests to test for unimplemented stops
09-May-12 JDB Separated assignments from conditional expressions
04-Nov-10 JDB Removed DS note regarding PIF card (is now implemented)
18-Sep-08 JDB .FLUN and self-tests for VIS and SIGNAL are NOP if not present
11-Sep-08 JDB Moved microcode function prototypes to hp2100_cpu1.h
05-Sep-08 JDB Removed option-present tests (now in UIG dispatchers)
Added "user microcode" dispatcher for unclaimed instructions
26-Feb-08 HV Removed and implemented "cpu_vis" and "cpu_signal"
22-Nov-07 JDB Removed and implemented "cpu_rte_ema"
12-Nov-07 JDB Removed and implemented "cpu_rte_vma" and "cpu_rte_os"
01-Dec-06 JDB Removed and implemented "cpu_sis".
26-Sep-06 JDB Created
Primary references:
- HP 1000 M/E/F-Series Computers Technical Reference Handbook
(5955-0282, March 1980)
- HP 1000 M/E/F-Series Computers Engineering and Reference Documentation
(92851-90001, March 1981)
- Macro/1000 Reference Manual
(92059-90001, December 1992)
Additional references are listed with the associated firmware
implementations, as are the HP option model numbers pertaining to the
applicable CPUs.
This module contains the User Instruction Group (a.k.a. "Macro") dispatcher
for the 2100 and 1000 (21MX) CPUs. The UIG simulators reside in separate
modules, due to the large number of firmware options available for these
machines. Unit flags indicate which options are present in the current
system.
It also contains a user-microprogram dispatcher to allow simulation of
site-specific firmware. All UIG instructions unclaimed by installed firmware
options are directed here and may be simulated by writing the appropriate
code.
The module also contains template simulations for the firmware options that
have not yet been implemented. When a given firmware option is implemented,
it should be moved out of this file and into another (or its own, depending
on complexity).
Finally, this module provides generalized instruction operand processing.
The 2100 and 1000 machines were microprogrammable; the 2116/15/14 machines
were not. Both user- and HP-written microprograms were supported. The
microcode address space of the 2100 encompassed four modules of 256 words
each. The 1000 M-series expanded that to sixteen modules, and the 1000
E/F-series expanded that still further to sixty-four modules. Each CPU had
its own microinstruction set, although the micromachines of the various 1000
models were similar internally.
The UIG instructions were divided into ranges assigned to HP firmware
options, reserved for future HP use, and reserved for user microprograms.
User microprograms could occupy any range not already used on a given
machine, but in practice, some effort was made to avoid the HP-reserved
ranges.
User microprogram simulation is supported by routing any UIG instruction not
allocated to an installed firmware option to a user-firmware dispatcher.
Site-specific microprograms may be simulated there. In the absence of such a
simulation, an unimplemented instruction stop will occur.
Regarding option instruction sets, there was some commonality across CPU
types. EAU instructions were identical across all models, and the floating
point set was the same on the 2100 and 1000. Other options implemented
proper instruction supersets (e.g., the Fast FORTRAN Processor from 2100 to
1000-M to 1000-E to 1000-F) or functional equivalence with differing code
points (the 2000 I/O Processor from 2100 to 1000, and the extended-precision
floating-point instructions from 1000-E to 1000-F).
The 2100 decoded the EAU and UIG sets separately in hardware and supported
only the UIG 0 code points. Bits 7-4 of a UIG instruction decoded one of
sixteen entry points in the lowest-numbered module after module 0. Those
entry points could be used directly (as for the floating-point instructions),
or additional decoding based on bits 3-0 could be implemented.
The 1000 generalized the instruction decoding to a series of microcoded
jumps, based on the bits in the instruction. Bits 15-8 indicated the group
of the current instruction: EAU (200, 201, 202, 210, and 211), UIG 0 (212),
or UIG 1 (203 and 213). UIG 0, UIG 1, and some EAU instructions were decoded
further by selecting one of sixteen modules within the group via bits 7-4.
Finally, each UIG module decoded up to sixteen instruction entry points via
bits 3-0. Jump tables for all firmware options were contained in the base
set, so modules needed only to be concerned with decoding their individual
entry points within the module.
While the 2100 and 1000 hardware decoded these instruction sets differently,
the decoding mechanism of the simulation follows that of the 1000 E/F-series.
Where needed, CPU type- or model-specific behavior is simulated.
The design of the 1000 microinstruction set was such that executing an
instruction for which no microcode was present (e.g., executing a FFP
instruction when the FFP firmware was not installed) resulted in a NOP.
Under simulation, such execution causes an unimplemented instruction stop if
"STOP (cpu_ss_unimpl)" is non-zero and a no-operation otherwise.
*/
#include "hp2100_defs.h"
#include "hp2100_cpu.h"
#include "hp2100_cpu_dmm.h"
/* UIG 0
The first User Instruction Group (UIG) encodes firmware options for the 2100
and 1000. Instruction codes 105000-105377 are assigned to microcode options
as follows:
Instructions Option Name 2100 1000-M 1000-E 1000-F
------------- -------------------------- ------ ------ ------ ------
105000-105362 2000 I/O Processor opt - - -
105000-105137 Floating Point opt std std std
105200-105237 Fast FORTRAN Processor opt opt opt std
105240-105257 RTE-IVA/B Extended Memory - - opt opt
105240-105257 RTE-6/VM Virtual Memory - - opt opt
105300-105317 Distributed System - - opt opt
105320-105337 Double Integer - - opt -
105320-105337 Scientific Instruction Set - - - std
105340-105357 RTE-6/VM Operating System - - opt opt
If the 2100 IOP is installed, the only valid UIG instructions are IOP
instructions, as the IOP used the full 2100 microcode addressing space. The
IOP dispatcher remaps the 2100 codes to 1000 codes for execution.
The F-Series moved the three-word extended real instructions from the FFP
range to the base floating-point range and added four-word double real and
two-word double integer instructions. The double integer instructions
occupied some of the vacated extended real instruction codes in the FFP, with
the rest assigned to the floating-point range. Consequently, many
instruction codes for the F-Series are different from the E-Series.
Implementation notes:
1. Product 93585A, available from the "Specials" group, added double integer
microcode to the E-Series. The instruction codes were different from
those in the F-Series to avoid conflicting with the E-Series FFP.
2. To run the double-integer instructions diagnostic in the absence of
64-bit integer support (and therefore of F-Series simulation), a special
DBI dispatcher may be enabled by defining ENABLE_DIAG during compilation.
This dispatcher will remap the F-Series DBI instructions to the E-Series
codes, so that the F-Series diagnostic may be run. Because several of
the F-Series DBI instruction codes replace M/E-Series FFP codes, this
dispatcher will only operate if FFP is disabled.
Note that enabling the dispatcher will produce non-standard FP behavior.
For example, any code in the range 105000-105017 normally would execute a
FAD instruction. With the dispatcher enabled, 105014 would execute a
.DAD, while the other codes would execute a FAD. Therefore, ENABLE_DIAG
should only be used to run the diagnostic and is not intended for general
use.
3. Any instruction not claimed by an installed option will be sent to the
user microcode dispatcher.
*/
t_stat cpu_uig_0 (uint32 intrq, t_bool int_ack)
{
const CPU_OPTION_SET cpu_2100_iop = CPU_2100 | CPU_IOP;
if ((cpu_configuration & cpu_2100_iop) == cpu_2100_iop) /* if the CPU is a 2100 with IOP firmware installed */
return cpu_iop (intrq); /* then dispatch to the IOP executor */
#if !defined (HAVE_INT64) && defined (ENABLE_DIAG) /* special DBI diagnostic dispatcher */
if ((cpu_configuration & (CPU_FFP | CPU_DBI)) == CPU_DBI) /* if FFP is absent and DBI is present */
switch (IR & 0377) { /* then remap the F-series codes to the E-series */
case 0014: /* .DAD 105014 */
return cpu_dbi (0105321u);
case 0034: /* .DSB 105034 */
return cpu_dbi (0105327u);
case 0054: /* .DMP 105054 */
return cpu_dbi (0105322u);
case 0074: /* .DDI 105074 */
return cpu_dbi (0105325u);
case 0114: /* .DSBR 105114 */
return cpu_dbi (0105334u);
case 0134: /* .DDIR 105134 */
return cpu_dbi (0105326u);
case 0203: /* .DNG 105203 */
return cpu_dbi (0105323u);
case 0204: /* .DCO 105204 */
return cpu_dbi (0105324u);
case 0210: /* .DIN 105210 */
return cpu_dbi (0105330u);
case 0211: /* .DDE 105211 */
return cpu_dbi (0105331u);
case 0212: /* .DIS 105212 */
return cpu_dbi (0105332u);
case 0213: /* .DDS 105213 */
return cpu_dbi (0105333u);
} /* otherwise, continue */
#endif /* end of special DBI dispatcher */
switch ((IR >> 4) & 017) { /* decode IR<7:4> */
case 000: /* 105000-105017 */
case 001: /* 105020-105037 */
case 002: /* 105040-105057 */
case 003: /* 105060-105077 */
case 004: /* 105100-105117 */
case 005: /* 105120-105137 */
if (cpu_configuration & CPU_FP) /* FP option installed? */
#if defined (HAVE_INT64) /* int64 support available */
return cpu_fpp (IR); /* Floating Point Processor */
#else /* int64 support unavailable */
return cpu_fp (); /* Firmware Floating Point */
#endif /* end of int64 support */
else
break;
case 010: /* 105200-105217 */
case 011: /* 105220-105237 */
if (cpu_configuration & CPU_FFP) /* FFP option installed? */
return cpu_ffp (intrq); /* Fast FORTRAN Processor */
else
break;
case 012: /* 105240-105257 */
if (cpu_configuration & CPU_VMAOS) /* VMA/OS option installed? */
return cpu_rte_vma (); /* RTE-6 VMA */
else if (cpu_configuration & CPU_EMA) /* EMA option installed? */
return cpu_rte_ema (); /* RTE-4 EMA */
else
break;
case 014: /* 105300-105317 */
if (cpu_configuration & CPU_DS) /* DS option installed? */
return cpu_ds (); /* Distributed System */
else
break;
case 015: /* 105320-105337 */
#if defined (HAVE_INT64) /* int64 support available */
if (cpu_configuration & CPU_1000_F) /* F-series? */
return cpu_sis (IR); /* Scientific Instruction is standard */
else /* M/E-series */
#endif /* end of int64 support */
if (cpu_configuration & CPU_DBI) /* DBI option installed? */
return cpu_dbi (IR); /* Double integer */
else
break;
case 016: /* 105340-105357 */
if (cpu_configuration & CPU_VMAOS) /* VMA/OS option installed? */
return cpu_rte_os (int_ack); /* RTE-6 OS */
else
break;
}
return cpu_user (); /* try user microcode */
}
/* UIG 1
The second User Instruction Group (UIG) encodes firmware options for the
1000. Instruction codes 101400-101777 and 105400-105777 are assigned to
microcode options as follows ("x" is "1" or "5" below):
Instructions Option Name 1000-M 1000-E 1000-F
------------- ---------------------------- ------ ------ ------
10x400-10x437 2000 IOP opt opt -
10x460-10x477 2000 IOP opt opt -
10x460-10x477 Vector Instruction Set - - opt
10x520-10x537 Distributed System opt - -
10x600-10x617 SIGNAL/1000 Instruction Set - - opt
10x700-10x737 Dynamic Mapping System opt opt std
10x740-10x777 Extended Instruction Group std std std
Only 1000 systems execute these instructions.
Implementation notes:
1. The Distributed System (DS) microcode was mapped to different instruction
ranges for the M-Series and the E/F-Series. The sequence of instructions
was identical, though, so we remap the former range to the latter before
dispatching.
2. Any instruction not claimed by an installed option will be sent to the
user microcode dispatcher.
*/
t_stat cpu_uig_1 (uint32 intrq)
{
if (!(cpu_configuration & CPU_1000)) /* if the CPU is not a 1000 */
return STOP (cpu_ss_unimpl); /* the the instruction is unimplemented */
switch ((IR >> 4) & 017) { /* decode IR<7:4> */
case 000: /* 105400-105417 */
case 001: /* 105420-105437 */
if (cpu_configuration & CPU_IOP) /* IOP option installed? */
return cpu_iop (intrq); /* 2000 I/O Processor */
else
break;
case 003: /* 105460-105477 */
#if defined (HAVE_INT64) /* int64 support available */
if (cpu_configuration & CPU_VIS) /* VIS option installed? */
return cpu_vis (); /* Vector Instruction Set */
else
#endif /* end of int64 support */
if (cpu_configuration & CPU_IOP) /* IOP option installed? */
return cpu_iop (intrq); /* 2000 I/O Processor */
else
break;
case 005: /* 105520-105537 */
if (cpu_configuration & CPU_DS) { /* DS option installed? */
IR = IR ^ 0000620; /* remap to 105300-105317 */
return cpu_ds (); /* Distributed System */
}
else
break;
#if defined (HAVE_INT64) /* int64 support available */
case 010: /* 105600-105617 */
if (cpu_configuration & CPU_SIGNAL) /* SIGNAL option installed? */
return cpu_signal (); /* SIGNAL/1000 Instructions */
else
break;
#endif /* end of int64 support */
case 014: /* 105700-105717 */
case 015: /* 105720-105737 */
if (cpu_configuration & CPU_DMS) /* DMS option installed? */
return cpu_dms (intrq); /* Dynamic Mapping System */
else
break;
case 016: /* 105740-105757 */
case 017: /* 105760-105777 */
return cpu_eig (IR, intrq); /* Extended Instruction Group */
}
return cpu_user (); /* try user microcode */
}
/* Distributed System.
Distributed System firmware was provided with the HP 91740A DS/1000 product
for use with the HP 12771A (12665A) Serial Interface and 12773A Modem
Interface system interconnection kits. Firmware permitted high-speed
transfers with minimum impact to the processor. The advent of the
"intelligent" 12794A and 12825A HDLC cards, the 12793A and 12834A Bisync
cards, and the 91750A DS-1000/IV software obviated the need for CPU firmware,
as essentially the firmware was moved onto the I/O cards.
Primary documentation for the DS instructions has not been located. However,
examination of the DS/1000 sources reveals that two instruction were used by
the DVA65 Serial Interface driver (91740-18071) and placed in the trap cells
of the communications interfaces. Presumably they handled interrupts from
the cards.
Implementation of the DS instructions will also require simulation of the
12665A Hardwired Serial Data Interface Card.
Option implementation by CPU was as follows:
2114 2115 2116 2100 1000-M 1000-E 1000-F
------ ------ ------ ------ ------ ------ ------
N/A N/A N/A N/A 91740A 91740B 91740B
The routines are mapped to instruction codes as follows:
Instr. 1000-M 1000-E/F Description
------ ------ -------- ----------------------------------------------
105520 105300 "Open loop" (trap cell handler)
105521 105301 "Closed loop" (trap cell handler)
105522 105302 [unknown]
[test] 105524 105304 [self test]
-- 105310 7974 boot loader ROM extension
Notes:
1. The E/F-Series opcodes were moved from 105340-357 to 105300-317 at
revision 1813.
2. DS/1000 ROM data are available from Bitsavers.
Additional references (documents unavailable):
- HP 91740A M-Series Distributed System (DS/1000) Firmware Installation
Manual (91740-90007).
- HP 91740B Distributed System (DS/1000) Firmware Installation Manual
(91740-90009).
*/
static const OP_PAT op_ds[16] = {
OP_N, OP_N, OP_N, OP_N, /* --- --- --- --- */
OP_N, OP_N, OP_N, OP_N, /* --- --- --- --- */
OP_N, OP_N, OP_N, OP_N, /* --- --- --- --- */
OP_N, OP_N, OP_N, OP_N /* --- --- --- --- */
};
t_stat cpu_ds (void)
{
t_stat reason = SCPE_OK;
OPS op;
uint32 entry;
entry = IR & 017; /* mask to entry point */
if (op_ds [entry] != OP_N) {
reason = cpu_ops (op_ds[entry], op); /* get instruction operands */
if (reason != SCPE_OK) /* did the evaluation fail? */
return reason; /* return the reason for failure */
}
switch (entry) { /* decode IR<3:0> */
default: /* others unimplemented */
reason = STOP (cpu_ss_unimpl);
}
return reason;
}
/* User firmware dispatcher.
All UIG instructions unclaimed by installed firmware options are directed
here. User- or site-specific firmware may be simulated by dispatching to the
appropriate simulator routine. Unimplemented instructions should return
"STOP (cpu_ss_unimpl)" to cause a simulator stop if enabled.
Implementation notes:
1. This routine may be passed any opcode in the ranges 101400-101737 and
105000-105737. The 10x740-777 range is dedicated to the EIG instructions
and is unavailable for user microprograms.
2. HP operating systems and subsystems depend on the following instructions
to execute as NOP and return success if the corresponding firmware is not
installed:
105226 -- Fast FORTRAN Processor .FLUN instruction
105355 -- RTE-6/VM OS self-test instruction
105477 -- Vector Instruction Set self-test
105617 -- SIGNAL/1000 self-test
These instructions are executed to determine firmware configuration
dynamically. If you use any of these opcodes for your own use, be aware
that certain HP programs may fail.
3. User microprograms occupied one or more firmware modules, each containing
16 potential instruction entry points. A skeleton dispatcher for the 32
possible modules is implemented below, along with a sample module.
*/
static t_stat cpu_user_20 (void); /* [0] Module 20 user microprograms stub */
t_stat cpu_user (void)
{
t_stat reason = SCPE_OK;
if (cpu_configuration & CPU_211X) /* 2116/15/14 CPU? */
return STOP (cpu_ss_unimpl); /* user microprograms not supported */
switch ((IR >> 4) & 037) { /* decode IR<8:4> */
/* case 000: ** 105000-105017 */
/* return cpu_user_00 (); ** uncomment to handle instruction */
/* case 001: ** 105020-105037 */
/* return cpu_user_01 (); ** uncomment to handle instruction */
/* case 0nn: ** other cases as needed */
/* return cpu_user_nn (); ** uncomment to handle instruction */
case 020: /* 10x400-10x417 */
return cpu_user_20 (); /* call sample dispatcher */
/* case 021: ** 10x420-10x437 */
/* return cpu_user_21 (); ** uncomment to handle instruction */
/* case 0nn: ** other cases as needed */
/* return cpu_user_nn (); ** uncomment to handle instruction */
default: /* others unimplemented */
reason = STOP (cpu_ss_unimpl);
}
return reason;
}
/* Example user microprogram simulator.
User- or site-specific firmware may be simulated by writing the appropriate
code below. Unimplemented instructions should return "STOP (cpu_ss_unimpl)"
to cause a simulator stop if enabled.
For information on the operand patterns used in the "op_user" array, see the
comments preceding the "cpu_ops" routine in "hp2100_cpu1.c" and the "operand
processing encoding" constants in "hp2100_cpu1.h".
*/
static const OP_PAT op_user_20[16] = {
OP_N, OP_N, OP_N, OP_N, /* --- --- --- --- */
OP_N, OP_N, OP_N, OP_N, /* --- --- --- --- */
OP_N, OP_N, OP_N, OP_N, /* --- --- --- --- */
OP_N, OP_N, OP_N, OP_N /* --- --- --- --- */
};
static t_stat cpu_user_20 (void)
{
t_stat reason = SCPE_OK;
OPS op;
uint32 entry;
entry = IR & 017; /* mask to entry point */
if (op_user_20 [entry] != OP_N) {
reason = cpu_ops (op_user_20 [entry], op); /* get instruction operands */
if (reason != SCPE_OK) /* did the evaluation fail? */
return reason; /* return the reason for failure */
}
switch (entry) { /* decode IR<4:0> */
case 000: /* 10x400 */
/* break; ** uncomment to handle instruction */
case 001: /* 10x401 */
/* break; ** uncomment to handle instruction */
/* case 0nn: ** other cases as needed */
/* break; ** uncomment to handle instruction */
default: /* others unimplemented */
reason = STOP (cpu_ss_unimpl);
}
return reason;
}
/* Read a multiple-precision operand value */
OP ReadOp (HP_WORD va, OPSIZE precision)
{
OP operand;
uint32 i;
if (precision == in_s)
operand.word = ReadW (va); /* read single integer */
else if (precision == in_d)
operand.dword = ReadW (va) << 16 | /* read double integer */
ReadW ((va + 1) & LA_MASK); /* merge high and low words */
else
for (i = 0; i < (uint32) precision; i++) { /* read fp 2 to 5 words */
operand.fpk[i] = ReadW (va);
va = (va + 1) & LA_MASK;
}
return operand;
}
/* Write a multiple-precision operand value */
void WriteOp (HP_WORD va, OP operand, OPSIZE precision)
{
uint32 i;
if (precision == in_s)
WriteW (va, operand.word); /* write single integer */
else if (precision == in_d) {
WriteW (va, UPPER_WORD (operand.dword)); /* write double integer */
WriteW (va + 1 & LA_MASK, LOWER_WORD (operand.dword)); /* high word, then low word */
}
else
for (i = 0; i < (uint32) precision; i++) { /* write fp 2 to 5 words */
WriteW (va, operand.fpk[i]);
va = (va + 1) & LA_MASK;
}
return;
}
/* Get instruction operands.
Operands for a given instruction are specifed by an "operand pattern"
consisting of flags indicating the types and storage methods. The pattern
directs how each operand is to be retrieved and whether the operand value or
address is returned in the operand array.
Typically, a microcode simulation handler will define an OP_PAT array, with
each element containing an operand pattern corresponding to the simulated
instruction. Operand patterns are defined in the header file accompanying
this source file. After calling this function with the appropriate operand
pattern and a pointer to an array of OPs, operands are decoded and stored
sequentially in the array.
The following operand encodings are defined:
Code Operand Description Example Return
------ ---------------------------------------- ----------- ------------
OP_NUL No operand present [inst] None
OP_IAR Integer constant in A register LDA I Value of I
[inst]
...
I DEC 0
OP_JAB Double integer constant in A/B registers DLD J Value of J
[inst]
...
J DEC 0,0
OP_FAB 2-word FP constant in A/B registers DLD F Value of F
[inst]
...
F DEC 0.0
OP_CON Inline 1-word constant [inst] Value of C
C DEC 0
...
OP_VAR Inline 1-word variable [inst] Address of V
V BSS 1
...
OP_ADR Inline address [inst] Address of A
DEF A
...
A EQU *
OP_ADK Address of integer constant [inst] Value of K
DEF K
...
K DEC 0
OP_ADD Address of double integer constant [inst] Value of D
DEF D
...
D DEC 0,0
OP_ADF Address of 2-word FP constant [inst] Value of F
DEF F
...
F DEC 0.0
OP_ADX Address of 3-word FP constant [inst] Value of X
DEF X
...
X DEX 0.0
OP_ADT Address of 4-word FP constant [inst] Value of T
DEF T
...
T DEY 0.0
OP_ADE Address of 5-word FP constant [inst] Value of E
DEF E
...
E DEC 0,0,0,0,0
Address operands, i.e., those having a DEF to the operand, will be resolved
to direct addresses. If an interrupt is pending and more than three levels
of indirection are used, the routine returns without completing operand
retrieval (the instruction will be retried after interrupt servicing).
Addresses are always resolved in the current DMS map.
An operand pattern consists of one or more operand encodings, corresponding
to the operands required by a given instruction. Values are returned in
sequence to the operand array.
*/
t_stat cpu_ops (OP_PAT pattern, OPS op)
{
OP_PAT flags;
uint32 i;
t_stat reason = SCPE_OK;
for (i = 0; i < OP_N_F; i++) {
flags = pattern & OP_M_FLAGS; /* get operand pattern */
if (flags >= OP_ADR) { /* address operand? */
MR = ReadW (PR); /* get the pointer */
reason = cpu_resolve_indirects (TRUE); /* resolve indirects */
if (reason != SCPE_OK) /* resolution failed? */
return reason;
}
switch (flags) {
case OP_NUL: /* null operand */
return reason; /* no more, so quit */
case OP_IAR: /* int in A */
(*op++).word = AR; /* get one-word value */
break;
case OP_JAB: /* dbl-int in A/B */
(*op++).dword = (AR << 16) | BR; /* get two-word value */
break;
case OP_FAB: /* 2-word FP in A/B */
(*op).fpk[0] = AR; /* get high FP word */
(*op++).fpk[1] = BR; /* get low FP word */
break;
case OP_CON: /* inline constant operand */
*op++ = ReadOp (PR, in_s); /* get value */
break;
case OP_VAR: /* inline variable operand */
(*op++).word = PR; /* get pointer to variable */
break;
case OP_ADR: /* inline address operand */
(*op++).word = MR; /* get address (set by "resolve" above) */
break;
case OP_ADK: /* address of int constant */
*op++ = ReadOp (MR, in_s); /* get value */
break;
case OP_ADD: /* address of dbl-int constant */
*op++ = ReadOp (MR, in_d); /* get value */
break;
case OP_ADF: /* address of 2-word FP const */
*op++ = ReadOp (MR, fp_f); /* get value */
break;
case OP_ADX: /* address of 3-word FP const */
*op++ = ReadOp (MR, fp_x); /* get value */
break;
case OP_ADT: /* address of 4-word FP const */
*op++ = ReadOp (MR, fp_t); /* get value */
break;
case OP_ADE: /* address of 5-word FP const */
*op++ = ReadOp (MR, fp_e); /* get value */
break;
default:
return SCPE_IERR; /* not implemented */
}
if (flags >= OP_CON) /* operand after instruction? */
PR = (PR + 1) & LA_MASK; /* yes, so bump to next */
pattern = pattern >> OP_N_FLAGS; /* move next pattern into place */
}
return reason;
}

759
HP2100/hp2100_cpu1.c Normal file
View File

@@ -0,0 +1,759 @@
/* hp2100_cpu1.c: HP 2100/1000 EAU/FP/IOP microcode simulator
Copyright (c) 2005-2016, Robert M. Supnik
Copyright (c) 2017-2019, J. David Bryan
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the names of the authors shall not be
used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from the authors.
CPU1 Extended Arithmetic Unit, Floating Point, and I/O Processor
instructions
23-Jan-19 JDB Moved fmt_ab to hp2100_cpu5.c
02-Oct-18 JDB Replaced DMASK with D16_MASK or R_MASK as appropriate
02-Aug-18 JDB Moved UIG dispatcher to hp2100_cpu0.c
Moved FP and IOP dispatchers from hp2100_cpu2.c
25-Jul-18 JDB Use cpu_configuration instead of cpu_unit.flags for tests
07-Sep-17 JDB Removed unnecessary "uint16" casts
01-Aug-17 JDB Changed TIMER and RRR 16 to test for undefined stops
07-Jul-17 JDB Changed "iotrap" from uint32 to t_bool
26-Jun-17 JDB Replaced SEXT with SEXT16
22-Apr-17 JDB Improved the EAU shift/rotate instructions
21-Mar-17 JDB Fixed UIG 1 comment regarding 2000 IOP and F-Series
31-Jan-17 JDB Added fmt_ab to print A/B-register error codes
30-Jan-17 JDB Removed fprint_ops, fprint_regs (now redundant)
05-Aug-16 JDB Renamed the P register from "PC" to "PR"
24-Dec-14 JDB Added casts for explicit downward conversions
05-Apr-14 JDB Corrected typo in comments for cpu_ops
09-May-12 JDB Separated assignments from conditional expressions
11-Sep-08 JDB Moved microcode function prototypes to hp2100_cpu1.h
05-Sep-08 JDB Moved option-present tests to UIG dispatchers
Call "user microcode" dispatcher for unclaimed UIG instructions
20-Apr-08 JDB Fixed VIS and SIGNAL to depend on the FPP and HAVE_INT64
28-Nov-07 JDB Added fprint_ops, fprint_regs for debug printouts
17-Nov-07 JDB Enabled DIAG as NOP on 1000 F-Series
04-Jan-07 JDB Added special DBI dispatcher for non-INT64 diagnostic
29-Dec-06 JDB Allows RRR as NOP if 2114 (diag config test)
01-Dec-06 JDB Substitutes FPP for firmware FP if HAVE_INT64
16-Oct-06 JDB Generalized operands for F-Series FP types
26-Sep-06 JDB Split hp2100_cpu1.c into multiple modules to simplify extensions
Added iotrap parameter to UIG dispatchers for RTE microcode
22-Feb-05 JDB Removed EXECUTE instruction (is NOP in actual microcode)
21-Jan-05 JDB Reorganized CPU option and operand processing flags
Split code along microcode modules
15-Jan-05 RMS Cloned from hp2100_cpu.c
Primary references:
- HP 1000 M/E/F-Series Computers Technical Reference Handbook
(5955-0282, March 1980)
- HP 1000 M/E/F-Series Computers Engineering and Reference Documentation
(92851-90001, March 1981)
- Macro/1000 Reference Manual
(92059-90001, December 1992)
Additional references are listed with the associated firmware
implementations, as are the HP option model numbers pertaining to the
applicable CPUs.
This source file contains the Extended Arithmetic Unit simulator, the
single-precision floating-pointer simulator, and the HP 2000 I/O Processor
instructions simulator.
*/
#include "hp2100_defs.h"
#include "hp2100_cpu.h"
#include "hp2100_cpu_dmm.h"
#if !defined (HAVE_INT64) /* int64 support unavailable */
#include "hp2100_cpu_fp.h"
#endif
/* EAU.
The Extended Arithmetic Unit (EAU) adds ten instructions with double-word
operands, including multiply, divide, shifts, and rotates. Option
implementation by CPU was as follows:
2114 2115 2116 2100 1000-M 1000-E 1000-F
------ ------ ------ ------ ------ ------ ------
N/A 12579A 12579A std std std std
The instruction codes are mapped to routines as follows:
Instr. Bits
Code 15-8 7-4 2116 2100 1000-M 1000-E 1000-F Note
------ ---- --- ------ ------ ------ ------ ------ ---------------------
100000 200 00 [diag] [diag] [self test]
100020 200 01 ASL ASL ASL ASL ASL Bits 3-0 encode shift
100040 200 02 LSL LSL LSL LSL LSL Bits 3-0 encode shift
100060 200 03 TIMER TIMER [deterministic delay]
100100 200 04 RRL RRL RRL RRL RRL Bits 3-0 encode shift
100200 200 10 MPY MPY MPY MPY MPY
100400 201 xx DIV DIV DIV DIV DIV
101020 202 01 ASR ASR ASR ASR ASR Bits 3-0 encode shift
101040 202 02 LSR LSR LSR LSR LSR Bits 3-0 encode shift
101100 202 04 RRR RRR RRR RRR RRR Bits 3-0 encode shift
104200 210 xx DLD DLD DLD DLD DLD
104400 211 xx DST DST DST DST DST
The remaining codes for bits 7-4 are undefined and will cause a simulator
stop if enabled. On a real 1000-M, all undefined instructions in the 200
group decode as MPY, and all in the 202 group decode as NOP. On a real
1000-E, instruction patterns 200/05 through 200/07 and 202/03 decode as NOP;
all others cause erroneous execution.
EAU instruction decoding on the 1000 M-series is convoluted. The JEAU
microorder maps IR bits 11, 9-7 and 5-4 to bits 2-0 of the microcode jump
address. The map is detailed on page IC-84 of the ERD.
The 1000 E/F-series add two undocumented instructions to the 200 group: TIMER
and DIAG. These are described in the ERD on page IA 5-5, paragraph 5-7. The
M-series executes these as MPY and RRL, respectively. A third instruction,
EXECUTE (100120), is also described but was never implemented, and the
E/F-series microcode execute a NOP for this instruction code.
If the EAU is not installed in a 2115 or 2116, EAU instructions execute as
NOPs or cause unimplemented instruction stops if enabled.
Implementation notes:
1. Under simulation, TIMER and DIAG cause undefined instruction stops if the
CPU is not an E/F-Series. Note that TIMER is intentionally executed by
several HP programs to differentiate between M- and E/F-series machines.
2. DIAG is not implemented under simulation. On the E/F, it performs a
destructive test of all installed memory. Because of this, it is only
functional if the machine is halted, i.e., if the instruction is
executed with the INSTR STEP button. If it is executed in a program,
the result is NOP.
3. The RRR 16 instruction is intentionally executed by the diagnostic
configurator on the 2114, which does not have an EAU, to differentiate
between 2114 and 2100/1000 CPUs.
4. The shift count is calculated unconditionally, as six of the ten
instructions will be using the value.
5. An arithmetic left shift must be handled as a special case because the
shifted operand bits "skip over" the sign bit. That is, the bits are
lost from the next-most-significant bit while preserving the MSB. For
all other shifts, including the arithmetic right shift, the operand may
be shifted and then merged with the appropriate fill bits.
6. The C standard specifies that the results of bitwise shifts with negative
signed operands are undefined (for left shifts) or implementation-defined
(for right shifts). Therefore, we must use unsigned operands and handle
arithmetic shifts explicitly.
*/
t_stat cpu_eau (void)
{
t_stat reason = SCPE_OK;
OPS op;
uint32 rs, qs, v1, v2, operand, fill, mask, shift;
int32 sop1, sop2;
if (!(cpu_configuration & CPU_EAU)) /* if the EAU is not installed */
return STOP (cpu_ss_unimpl); /* then the instructions execute as NOPs */
if (IR & 017) /* if the shift count is 1-15 */
shift = IR & 017; /* then use it verbatim */
else /* otherwise the count iz zero */
shift = 16; /* so use a shift count of 16 */
switch ((IR >> 8) & 0377) { /* decode IR<15:8> */
case 0200: /* EAU group 0 */
switch ((IR >> 4) & 017) { /* decode IR<7:4> */
case 000: /* DIAG 100000 */
if (!(cpu_configuration & CPU_1000_E_F)) /* if the CPU is not an E- or F-series */
return STOP (cpu_ss_undef); /* then the instruction is undefined */
break; /* and executes as NOP */
case 001: /* ASL 100020-100037 */
operand = TO_DWORD (BR, AR); /* form the double-word operand */
mask = D32_UMAX << 31 - shift; /* form a mask for the bits that will be lost */
if (operand & D32_SIGN) /* if the operand is negative */
O = (~operand & mask & D32_MASK) != 0; /* then set overflow if any of the lost bits are zeros */
else /* otherwise it's positive */
O = (operand & mask & D32_MASK) != 0; /* so set overflow if any of the lost bits are ones */
operand = operand << shift & D32_SMAX /* shift the operand left */
| operand & D32_SIGN; /* while keeping the original sign bit */
BR = UPPER_WORD (operand); /* split the operand */
AR = LOWER_WORD (operand); /* into its constituent parts */
break;
case 002: /* LSL 100040-100057 */
operand = TO_DWORD (BR, AR) << shift; /* shift the double-word operand left */
BR = UPPER_WORD (operand); /* split the operand */
AR = LOWER_WORD (operand); /* into its constituent parts */
break;
case 004: /* RRL 100100-100117 */
operand = TO_DWORD (BR, AR); /* form the double-word operand */
fill = operand; /* and fill with operand bits */
operand = operand << shift /* rotate the operand left */
| fill >> 32 - shift; /* while filling in on the right */
BR = UPPER_WORD (operand); /* split the operand */
AR = LOWER_WORD (operand); /* into its constituent parts */
break;
case 003: /* TIMER 100060 */
if (cpu_configuration & CPU_1000_E_F) { /* if the CPU is an E- or F-series */
BR = BR + 1 & R_MASK; /* then increment B */
if (BR != 0) /* if B did not roll over */
PR = err_PR; /* then repeat the instruction */
break;
}
else { /* otherwise it's a 21xx or 1000 M-Series */
reason = STOP (cpu_ss_undef); /* and the instruction is undefined */
if (reason != SCPE_OK /* if a stop is indicated */
|| cpu_configuration & CPU_21XX) /* or the CPU is a 21xx */
break; /* then the instruction executes as NOP */
}
/* fall through into the MPY case if 1000 M-Series */
case 010: /* MPY 100200 (OP_K) */
reason = cpu_ops (OP_K, op); /* get operand */
if (reason == SCPE_OK) { /* successful eval? */
sop1 = SEXT16 (AR); /* sext AR */
sop2 = SEXT16 (op[0].word); /* sext mem */
sop1 = sop1 * sop2; /* signed mpy */
BR = UPPER_WORD (sop1); /* to BR'AR */
AR = LOWER_WORD (sop1);
O = 0; /* no overflow */
}
break;
default: /* others undefined */
return STOP (cpu_ss_unimpl);
}
break;
case 0201: /* DIV 100400 (OP_K) */
reason = cpu_ops (OP_K, op); /* get operand */
if (reason != SCPE_OK) /* eval failed? */
break;
rs = qs = BR & D16_SIGN; /* save divd sign */
if (rs) { /* neg? */
AR = (~AR + 1) & R_MASK; /* make B'A pos */
BR = (~BR + (AR == 0)) & R_MASK; /* make divd pos */
}
v2 = op[0].word; /* divr = mem */
if (v2 & D16_SIGN) { /* neg? */
v2 = (~v2 + 1) & D16_MASK; /* make divr pos */
qs = qs ^ D16_SIGN; /* sign of quotient */
}
if (BR >= v2) /* if the divisor is too small */
O = 1; /* then set overflow */
else { /* maybe... */
O = 0; /* assume ok */
v1 = (BR << 16) | AR; /* 32b divd */
AR = (v1 / v2) & R_MASK; /* quotient */
BR = (v1 % v2) & R_MASK; /* remainder */
if (AR) { /* quotient > 0? */
if (qs) /* apply quo sign */
AR = NEG16 (AR);
if ((AR ^ qs) & D16_SIGN) /* still wrong? ovflo */
O = 1;
}
if (rs)
BR = NEG16 (BR); /* apply rem sign */
}
break;
case 0202: /* EAU group 2 */
switch ((IR >> 4) & 017) { /* decode IR<7:4> */
case 001: /* ASR 101020-101037 */
O = 0; /* clear ovflo */
operand = TO_DWORD (BR, AR); /* form the double-word operand */
fill = (operand & D32_SIGN ? ~0 : 0); /* and fill with copies of the sign bit */
operand = operand >> shift /* shift the operand right */
| fill << 32 - shift; /* while filling in with sign bits */
BR = UPPER_WORD (operand); /* split the operand */
AR = LOWER_WORD (operand); /* into its constituent parts */
break;
case 002: /* LSR 101040-101057 */
operand = TO_DWORD (BR, AR) >> shift; /* shift the double-word operand right */
BR = UPPER_WORD (operand); /* split the operand */
AR = LOWER_WORD (operand); /* into its constituent parts */
break;
case 004: /* RRR 101100-101117 */
operand = TO_DWORD (BR, AR); /* form the double-word operand */
fill = operand; /* and fill with operand bits */
operand = operand >> shift /* rotate the operand right */
| fill << 32 - shift; /* while filling in on the left */
BR = UPPER_WORD (operand); /* split the operand */
AR = LOWER_WORD (operand); /* into its constituent parts */
break;
default: /* others undefined */
return STOP (cpu_ss_undef);
}
break;
case 0210: /* DLD 104200 (OP_D) */
reason = cpu_ops (OP_D, op); /* get operand */
if (reason == SCPE_OK) { /* successful eval? */
AR = UPPER_WORD (op[0].dword); /* load AR */
BR = LOWER_WORD (op[0].dword); /* load BR */
}
break;
case 0211: /* DST 104400 (OP_A) */
reason = cpu_ops (OP_A, op); /* get operand */
if (reason == SCPE_OK) { /* successful eval? */
WriteW (op[0].word, AR); /* store AR */
WriteW ((op[0].word + 1) & LA_MASK, BR); /* store BR */
}
break;
default: /* should never get here */
return SCPE_IERR; /* bad call from cpu_instr */
}
return reason;
}
#if !defined (HAVE_INT64) /* int64 support unavailable */
/* Single-Precision Floating Point Instructions.
The 2100 and 1000 CPUs share the single-precision (two word) floating-point
instruction codes. Floating-point firmware was an option on the 2100 and was
standard on the 1000-M and E. The 1000-F had a standard hardware Floating
Point Processor that executed these six instructions and added extended- and
double-precision floating- point instructions, as well as double-integer
instructions (the FPP is simulated separately).
Option implementation by CPU was as follows:
2114 2115 2116 2100 1000-M 1000-E 1000-F
------ ------ ------ ------ ------ ------ ------
N/A N/A N/A 12901A std std N/A
The instruction codes for the 2100 and 1000-M/E systems are mapped to
routines as follows:
Instr. 2100/1000-M/E Description
------ ------------- -----------------------------------
105000 FAD Single real add
105020 FSB Single real subtract
105040 FMP Single real multiply
105060 FDV Single real divide
105100 FIX Single integer to single real fix
105120 FLT Single real to single integer float
Bits 3-0 are not decoded by these instructions, so FAD (e.g.) would be
executed by any instruction in the range 105000-105017.
Implementation note: rather than have two simulators that each executes the
single-precision FP instruction set, we compile conditionally, based on the
availability of 64-bit integer support in the host compiler. 64-bit integers
are required for the FPP, so if they are available, then the FPP is used to
handle the six single-precision instructions for the 2100 and M/E-Series, and
this function is omitted. If support is unavailable, this function is used
instead.
Implementation note: the operands to FAD, etc. are floating-point values, so
OP_F would normally be used. However, the firmware FP support routines want
floating-point operands as 32-bit integer values, so OP_D is used to achieve
this.
*/
static const OP_PAT op_fp[8] = {
OP_D, OP_D, OP_D, OP_D, /* FAD FSB FMP FDV */
OP_N, OP_N, OP_N, OP_N /* FIX FLT --- --- */
};
t_stat cpu_fp (void)
{
t_stat reason = SCPE_OK;
OPS op;
uint32 entry;
entry = (IR >> 4) & 017; /* mask to entry point */
if (op_fp [entry] != OP_N) {
reason = cpu_ops (op_fp [entry], op); /* get instruction operands */
if (reason != SCPE_OK) /* evaluation failed? */
return reason; /* return reason for failure */
}
switch (entry) { /* decode IR<7:4> */
case 000: /* FAD 105000 (OP_D) */
O = f_as (op[0].dword, 0); /* add, upd ovflo */
break;
case 001: /* FSB 105020 (OP_D) */
O = f_as (op[0].dword, 1); /* sub, upd ovflo */
break;
case 002: /* FMP 105040 (OP_D) */
O = f_mul (op[0].dword); /* mul, upd ovflo */
break;
case 003: /* FDV 105060 (OP_D) */
O = f_div (op[0].dword); /* div, upd ovflo */
break;
case 004: /* FIX 105100 (OP_N) */
O = f_fix (); /* fix, upd ovflo */
break;
case 005: /* FLT 105120 (OP_N) */
O = f_flt (); /* float, upd ovflo */
break;
default: /* should be impossible */
return SCPE_IERR;
}
return reason;
}
#endif /* int64 support unavailable */
/* HP 2000 I/O Processor.
The IOP accelerates certain operations of the HP 2000 Time-Share BASIC system
I/O processor. Most 2000 systems were delivered with 2100 CPUs, although IOP
microcode was developed for the 1000-M and 1000-E. As the I/O processors
were specific to the 2000 system, general compatibility with other CPU
microcode options was unnecessary, and indeed no other options were possible
for the 2100.
Option implementation by CPU was as follows:
2114 2115 2116 2100 1000-M 1000-E 1000-F
------ ------ ------ ------ ------ ------ ------
N/A N/A N/A 13206A 13207A 22702A N/A
The routines are mapped to instruction codes as follows:
Instr. 2100 1000-M/E Description
------ ---------- ---------- --------------------------------------------
SAI 105060-117 101400-037 Store A indexed by B (+/- offset in IR<4:0>)
LAI 105020-057 105400-037 Load A indexed by B (+/- offset in IR<4:0>)
CRC 105150 105460 Generate CRC
REST 105340 105461 Restore registers from stack
READF 105220 105462 Read F register (stack pointer)
INS -- 105463 Initialize F register (stack pointer)
ENQ 105240 105464 Enqueue
PENQ 105257 105465 Priority enqueue
DEQ 105260 105466 Dequeue
TRSLT 105160 105467 Translate character
ILIST 105000 105470 Indirect address list (similar to $SETP)
PRFEI 105222 105471 Power fail exit with I/O
PRFEX 105223 105472 Power fail exit
PRFIO 105221 105473 Power fail I/O
SAVE 105362 105474 Save registers to stack
MBYTE 105120 105765 Move bytes (MBT)
MWORD 105200 105777 Move words (MVW)
SBYTE 105300 105764 Store byte (SBT)
LBYTE 105320 105763 Load byte (LBT)
The INS instruction was not required in the 2100 implementation because the
stack pointer was actually the memory protect fence register and so could be
loaded directly with an OTA/B 05. Also, the 1000 implementation did not
offer the MBYTE, MWORD, SBYTE, and LBYTE instructions because the equivalent
instructions from the standard Extended Instruction Group were used instead.
Note that the 2100 MBYTE and MWORD instructions operate slightly differently
from the 1000 MBT and MVW instructions. Specifically, the move count is
signed on the 2100 and unsigned on the 1000. A negative count on the 2100
results in a NOP.
The simulator remaps the 2100 instructions to the 1000 codes. The four EIG
equivalents are dispatched to the EIG simulator. The rest are handled here.
Additional reference:
- HP 2000 Computer System Sources and Listings Documentation
(22687-90020, undated), section 3, pages 2-74 through 2-91
Implementation notes:
1. The SAVE and RESTR instructions use the (otherwise unused) SP register on
the 1000 as the stack pointer. On the 2100, there is no SP register, so
the instructions use the memory protect fence register as the stack
pointer. We update the 2100 fence because it could affect CPU operation
if MP is turned on (although, in practice, the 2100 IOP does not use
memory protect and so never enables it).
*/
static const OP_PAT op_iop[16] = {
OP_V, OP_N, OP_N, OP_N, /* CRC RESTR READF INS */
OP_N, OP_N, OP_N, OP_V, /* ENQ PENQ DEQ TRSLT */
OP_AC, OP_CVA, OP_A, OP_CV, /* ILIST PRFEI PRFEX PRFIO */
OP_N, OP_N, OP_N, OP_N /* SAVE --- --- --- */
};
t_stat cpu_iop (uint32 intrq)
{
t_stat reason = SCPE_OK;
OPS op;
uint8 byte;
uint32 entry, i;
HP_WORD hp, tp, t, wc, MA;
if (cpu_configuration & CPU_2100) { /* 2100 IOP? */
if ((IR >= 0105020) && (IR <= 0105057)) /* remap LAI */
IR = 0105400 | (IR - 0105020);
else if ((IR >= 0105060) && (IR <= 0105117)) /* remap SAI */
IR = 0101400 | (IR - 0105060);
else {
switch (IR) { /* remap others */
case 0105000: IR = 0105470; break; /* ILIST */
case 0105120: return cpu_eig (0105765, intrq); /* MBYTE (maps to MBT) */
case 0105150: IR = 0105460; break; /* CRC */
case 0105160: IR = 0105467; break; /* TRSLT */
case 0105200: return cpu_eig (0105777, intrq); /* MWORD (maps to MVW) */
case 0105220: IR = 0105462; break; /* READF */
case 0105221: IR = 0105473; break; /* PRFIO */
case 0105222: IR = 0105471; break; /* PRFEI */
case 0105223: IR = 0105472; break; /* PRFEX */
case 0105240: IR = 0105464; break; /* ENQ */
case 0105257: IR = 0105465; break; /* PENQ */
case 0105260: IR = 0105466; break; /* DEQ */
case 0105300: return cpu_eig (0105764, intrq); /* SBYTE (maps to SBT) */
case 0105320: return cpu_eig (0105763, intrq); /* LBYTE (maps to LBT) */
case 0105340: IR = 0105461; break; /* REST */
case 0105362: IR = 0105474; break; /* SAVE */
default: /* all others invalid */
return STOP (cpu_ss_unimpl);
}
}
}
entry = IR & 077; /* mask to entry point */
if (entry <= 037) { /* LAI/SAI 10x400-437 */
MA = ((entry - 020) + BR) & LA_MASK; /* +/- offset */
if (IR & AB_MASK) /* if this is an LAI instruction */
AR = ReadW (MA); /* then load the A register */
else /* otherwise */
WriteW (MA, AR); /* store the A register */
return reason;
}
else if (entry <= 057) /* IR = 10x440-457? */
return STOP (cpu_ss_unimpl); /* not part of IOP */
entry = entry - 060; /* offset 10x460-477 */
if (op_iop [entry] != OP_N) {
reason = cpu_ops (op_iop [entry], op); /* get instruction operands */
if (reason != SCPE_OK) /* evaluation failed? */
return reason; /* return reason for failure */
}
switch (entry) { /* decode IR<5:0> */
case 000: /* CRC 105460 (OP_V) */
t = ReadW (op[0].word) ^ (AR & 0377); /* xor prev CRC and char */
for (i = 0; i < 8; i++) { /* apply polynomial */
t = (t >> 1) | ((t & 1) << 15); /* rotate right */
if (t & D16_SIGN) t = t ^ 020001; /* old t<0>? xor */
}
WriteW (op[0].word, t); /* rewrite CRC */
break;
case 001: /* RESTR 105461 (OP_N) */
SPR = (SPR - 1) & LA_MASK; /* decr stack ptr */
t = ReadW (SPR); /* get E and O */
O = ((t >> 1) ^ 1) & 1; /* restore O */
E = t & 1; /* restore E */
SPR = (SPR - 1) & LA_MASK; /* decr sp */
BR = ReadW (SPR); /* restore B */
SPR = (SPR - 1) & LA_MASK; /* decr sp */
AR = ReadW (SPR); /* restore A */
if (cpu_configuration & CPU_2100) /* 2100 keeps sp in MP FR */
mp_fence = SPR; /* (in case MP is turned on) */
break;
case 002: /* READF 105462 (OP_N) */
AR = SPR; /* copy stk ptr */
break;
case 003: /* INS 105463 (OP_N) */
SPR = AR; /* init stk ptr */
break;
case 004: /* ENQ 105464 (OP_N) */
hp = ReadW (AR & LA_MASK); /* addr of head */
tp = ReadW ((AR + 1) & LA_MASK); /* addr of tail */
WriteW ((BR - 1) & LA_MASK, 0); /* entry link */
WriteW ((tp - 1) & LA_MASK, BR); /* tail link */
WriteW ((AR + 1) & LA_MASK, BR); /* queue tail */
if (hp != 0) PR = (PR + 1) & LA_MASK; /* q not empty? skip */
break;
case 005: /* PENQ 105465 (OP_N) */
hp = ReadW (AR & LA_MASK); /* addr of head */
WriteW ((BR - 1) & LA_MASK, hp); /* becomes entry link */
WriteW (AR & LA_MASK, BR); /* queue head */
if (hp == 0) /* q empty? */
WriteW ((AR + 1) & LA_MASK, BR); /* queue tail */
else PR = (PR + 1) & LA_MASK; /* skip */
break;
case 006: /* DEQ 105466 (OP_N) */
BR = ReadW (AR & LA_MASK); /* addr of head */
if (BR) { /* queue not empty? */
hp = ReadW ((BR - 1) & LA_MASK); /* read hd entry link */
WriteW (AR & LA_MASK, hp); /* becomes queue head */
if (hp == 0) /* q now empty? */
WriteW ((AR + 1) & LA_MASK, (AR + 1) & R_MASK);
PR = (PR + 1) & LA_MASK; /* skip */
}
break;
case 007: /* TRSLT 105467 (OP_V) */
wc = ReadW (op[0].word); /* get count */
if (wc & D16_SIGN) break; /* cnt < 0? */
while (wc != 0) { /* loop */
MA = (AR + AR + ReadB (BR)) & LA_MASK;
byte = ReadB (MA); /* xlate */
WriteB (BR, byte); /* store char */
BR = (BR + 1) & R_MASK; /* incr ptr */
wc = (wc - 1) & D16_MASK; /* decr cnt */
if (wc && intrq) { /* more and intr? */
WriteW (op[0].word, wc); /* save count */
PR = err_PR; /* stop for now */
break;
}
}
break;
case 010: /* ILIST 105470 (OP_AC) */
do { /* for count */
WriteW (op[0].word, AR); /* write AR to mem */
AR = (AR + 1) & R_MASK; /* incr AR */
op[0].word = (op[0].word + 1) & LA_MASK; /* incr MA */
op[1].word = (op[1].word - 1) & D16_MASK; /* decr count */
}
while (op[1].word != 0);
break;
case 011: /* PRFEI 105471 (OP_CVA) */
WriteW (op[1].word, 1); /* set flag */
reason = cpu_iog (op[0].word); /* execute I/O instr */
op[0].word = op[2].word; /* set rtn and fall through */
case 012: /* PRFEX 105472 (OP_A) */
PCQ_ENTRY;
PR = ReadW (op[0].word) & LA_MASK; /* jump indirect */
WriteW (op[0].word, 0); /* clear exit */
break;
case 013: /* PRFIO 105473 (OP_CV) */
WriteW (op[1].word, 1); /* set flag */
reason = cpu_iog (op[0].word); /* execute instr */
break;
case 014: /* SAVE 105474 (OP_N) */
WriteW (SPR, AR); /* save A */
SPR = (SPR + 1) & LA_MASK; /* incr stack ptr */
WriteW (SPR, BR); /* save B */
SPR = (SPR + 1) & LA_MASK; /* incr stack ptr */
t = (HP_WORD) ((O ^ 1) << 1 | E); /* merge E and O */
WriteW (SPR, t); /* save E and O */
SPR = (SPR + 1) & LA_MASK; /* incr stack ptr */
if (cpu_configuration & CPU_2100) /* 2100 keeps sp in MP FR */
mp_fence = SPR; /* (in case MP is turned on) */
break;
default: /* instruction unimplemented */
return STOP (cpu_ss_unimpl);
}
return reason;
}

803
HP2100/hp2100_cpu2.c Normal file
View File

@@ -0,0 +1,803 @@
/* hp2100_cpu2.c: HP 1000 DMS and EIG microcode simulator
Copyright (c) 2005-2016, Robert M. Supnik
Copyright (c) 2017-2018, J. David Bryan
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the names of the authors shall not be
used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from the authors.
CPU2 Dynamic Mapping System and Extended Instruction Set instructions
02-Oct-18 JDB Replaced DMASK with D16_MASK or R_MASK as appropriate
02-Aug-18 JDB Moved FP and IOP dispatchers to hp2100_cpu1.c
30-Jul-18 JDB Renamed "dms_[rw]map" to "meu_read_map", "meu_write_map"
24-Jul-18 JDB Removed unneeded "iotrap" parameter from "cpu_iog" routine
07-Sep-17 JDB Removed unnecessary "uint16" casts
10-Jul-17 JDB Renamed the global routine "iogrp" to "cpu_iog"
26-Jun-17 JDB Replaced SEXT with SEXT16
22-Mar-17 JDB Corrected comments regarding IR bit 11 selecting A/B
25-Jan-17 JDB Set mp_mem_changed whenever MEM registers are changed
05-Aug-16 JDB Renamed the P register from "PC" to "PR"
24-Dec-14 JDB Added casts for explicit downward conversions
09-May-12 JDB Separated assignments from conditional expressions
11-Sep-08 JDB Moved microcode function prototypes to hp2100_cpu1.h
05-Sep-08 JDB Removed option-present tests (now in UIG dispatchers)
05-Aug-08 JDB Updated mp_dms_jmp calling sequence
Fixed DJP, SJP, and UJP jump target validation
30-Jul-08 JDB RVA/B conditionally updates dms_vr before returning value
19-Dec-06 JDB DMS self-test now executes as NOP on 1000-M
01-Dec-06 JDB Substitutes FPP for firmware FP if HAVE_INT64
26-Sep-06 JDB Moved from hp2100_cpu1.c to simplify extensions
22-Feb-05 JDB Fixed missing MPCK on JRS target
21-Jan-05 JDB Reorganized CPU option and operand processing flags
Split code along microcode modules
15-Jan-05 RMS Cloned from hp2100_cpu.c
Primary references:
- HP 1000 M/E/F-Series Computers Technical Reference Handbook
(5955-0282, March 1980)
- HP 1000 M/E/F-Series Computers Engineering and Reference Documentation
(92851-90001, March 1981)
- Macro/1000 Reference Manual
(92059-90001, December 1992)
Additional references are listed with the associated firmware
implementations, as are the HP option model numbers pertaining to the
applicable CPUs.
*/
#include "hp2100_defs.h"
#include "hp2100_cpu.h"
#include "hp2100_cpu_dmm.h"
/* Dynamic Mapping System.
The 1000 Dynamic Mapping System (DMS) consisted of the 12731A Memory
Expansion Module (MEM) card and 38 instructions to expand the basic 32K
logical address space to a 1024K physical space. The MEM provided four maps
of 32 mapping registers each: a system map, a user map, and two DCPC maps.
DMS worked in conjunction with memory protect to provide a "protected mode"
in which memory read and write violations could be trapped, and that
inhibited "privileged" instruction execution that attempted to alter the
memory mapping.
Option implementation by CPU was as follows:
2114 2115 2116 2100 1000-M 1000-E 1000-F
------ ------ ------ ------ ------ ------ ------
N/A N/A N/A N/A 12976B 13307B std
The instruction codes are mapped to routines as follows:
Instr. 1000-M 1000-E/F Instr. 1000-M 1000-E/F
------ ------ -------- ------ ------ --------
10x700 [xmm] [xmm] 10x720 XMM XMM
10x701 [nop] [test] 10x721 XMS XMS
10x702 MBI MBI 10x722 XM* XM*
10x703 MBF MBF 10x723 [nop] [nop]
10x704 MBW MBW 10x724 XL* XL*
10x705 MWI MWI 10x725 XS* XS*
10x706 MWF MWF 10x726 XC* XC*
10x707 MWW MWW 10x727 LF* LF*
10x710 SY* SY* 10x730 RS* RS*
10x711 US* US* 10x731 RV* RV*
10x712 PA* PA* 10x732 DJP DJP
10x713 PB* PB* 10x733 DJS DJS
10x714 SSM SSM 10x734 SJP SJP
10x715 JRS JRS 10x735 SJS SJS
10x716 [nop] [nop] 10x736 UJP UJP
10x717 [nop] [nop] 10x737 UJS UJS
Instructions that use IR bit 11 to select the A or B register are designated
with a * above (e.g., 101710 is SYA, and 105710 is SYB). For those that do
not use this feature, either the 101xxx or 105xxx code will execute the
corresponding instruction, although the 105xxx form is the documented
instruction code.
Implementation notes:
1. Instruction code 10x700 will execute the XMM instruction, although 10x720
is the documented instruction value.
2. Instruction code 10x701 will complement the A or B register, as
indicated, on 1000-E and F-Series machines. This instruction is a NOP on
M-Series machines.
3. The DMS privilege violation rules are:
- load map and CTL5 set (XMM, XMS, XM*, SY*, US*, PA*, PB*)
- load state or fence and UMAP set (JRS, DJP, DJS, SJP, SJS, UJP, UJS, LF*)
4. DM (write) violations for the use of the MBI, MWI, MBW, MWW, XSA, and XSB
instructions in protected mode are generated by the mem_write routine.
5. The protected memory lower bound for the DJP, SJP, UJP, and JRS
instructions is 2.
*/
static const OP_PAT op_dms[32] = {
OP_N, OP_N, OP_N, OP_N, /* [xmm] [test] MBI MBF */
OP_N, OP_N, OP_N, OP_N, /* MBW MWI MWF MWW */
OP_N, OP_N, OP_N, OP_N, /* SYA/B USA/B PAA/B PBA/B */
OP_A, OP_KA, OP_N, OP_N, /* SSM JRS nop nop */
OP_N, OP_N, OP_N, OP_N, /* XMM XMS XMA/B nop */
OP_A, OP_A, OP_A, OP_N, /* XLA/B XSA/B XCA/B LFA/B */
OP_N, OP_N, OP_A, OP_A, /* RSA/B RVA/B DJP DJS */
OP_A, OP_A, OP_A, OP_A /* SJP SJS UJP UJS */
};
t_stat cpu_dms (uint32 intrq)
{
t_stat reason = SCPE_OK;
OPS op;
uint8 byte;
uint32 entry, absel, i;
HP_WORD t;
MEU_STATE operation;
MEU_MAP_SELECTOR mapi, mapj;
absel = AB_SELECT (IR); /* get the A/B register selector */
entry = IR & 037; /* mask to entry point */
if (op_dms [entry] != OP_N) {
reason = cpu_ops (op_dms [entry], op); /* get instruction operands */
if (reason != SCPE_OK) /* evaluation failed? */
return reason; /* return reason for failure */
}
switch (entry) { /* decode IR<3:0> */
/* DMS module 1 */
case 000: /* [undefined] 105700 (OP_N) */
goto XMM; /* decodes as XMM */
case 001: /* [self test] 105701 (OP_N) */
if (!(cpu_configuration & CPU_1000_M)) /* executes as NOP on 1000-M */
ABREG[absel] = ~ABREG[absel]; /* CMA or CMB */
break;
case 002: /* MBI 105702 (OP_N) */
AR = AR & ~1; /* force A, B even */
BR = BR & ~1;
while (XR != 0) { /* loop */
byte = ReadB (AR); /* read curr */
WriteBA (BR, byte); /* write alt */
AR = (AR + 1) & R_MASK; /* incr ptrs */
BR = (BR + 1) & R_MASK;
XR = (XR - 1) & R_MASK;
if (XR && intrq && !(AR & 1)) { /* more, int, even? */
PR = err_PR; /* stop for now */
break;
}
}
break;
case 003: /* MBF 105703 (OP_N) */
AR = AR & ~1; /* force A, B even */
BR = BR & ~1;
while (XR != 0) { /* loop */
byte = ReadBA (AR); /* read alt */
WriteB (BR, byte); /* write curr */
AR = (AR + 1) & R_MASK; /* incr ptrs */
BR = (BR + 1) & R_MASK;
XR = (XR - 1) & R_MASK;
if (XR && intrq && !(AR & 1)) { /* more, int, even? */
PR = err_PR; /* stop for now */
break;
}
}
break;
case 004: /* MBW 105704 (OP_N) */
AR = AR & ~1; /* force A, B even */
BR = BR & ~1;
while (XR != 0) { /* loop */
byte = ReadBA (AR); /* read alt */
WriteBA (BR, byte); /* write alt */
AR = (AR + 1) & R_MASK; /* incr ptrs */
BR = (BR + 1) & R_MASK;
XR = (XR - 1) & R_MASK;
if (XR && intrq && !(AR & 1)) { /* more, int, even? */
PR = err_PR; /* stop for now */
break;
}
}
break;
case 005: /* MWI 105705 (OP_N) */
while (XR != 0) { /* loop */
t = ReadW (AR & LA_MASK); /* read curr */
WriteWA (BR & LA_MASK, t); /* write alt */
AR = (AR + 1) & R_MASK; /* incr ptrs */
BR = (BR + 1) & R_MASK;
XR = (XR - 1) & R_MASK;
if (XR && intrq) { /* more and intr? */
PR = err_PR; /* stop for now */
break;
}
}
break;
case 006: /* MWF 105706 (OP_N) */
while (XR != 0) { /* loop */
t = ReadWA (AR & LA_MASK); /* read alt */
WriteW (BR & LA_MASK, t); /* write curr */
AR = (AR + 1) & R_MASK; /* incr ptrs */
BR = (BR + 1) & R_MASK;
XR = (XR - 1) & R_MASK;
if (XR && intrq) { /* more and intr? */
PR = err_PR; /* stop for now */
break;
}
}
break;
case 007: /* MWW 105707 (OP_N) */
while (XR != 0) { /* loop */
t = ReadWA (AR & LA_MASK); /* read alt */
WriteWA (BR & LA_MASK, t); /* write alt */
AR = (AR + 1) & R_MASK; /* incr ptrs */
BR = (BR + 1) & R_MASK;
XR = (XR - 1) & R_MASK;
if (XR && intrq) { /* more and intr? */
PR = err_PR; /* stop for now */
break;
}
}
break;
case 010: /* SYA, SYB 10x710 (OP_N) */
case 011: /* USA, USB 10x711 (OP_N) */
case 012: /* PAA, PAB 10x712 (OP_N) */
case 013: /* PBA, PBB 10x713 (OP_N) */
mapi = TO_MAP_SELECTOR (IR); /* map base */
if (ABREG[absel] & D16_SIGN) { /* store? */
for (i = 0; i < MEU_REG_COUNT; i++) {
t = meu_read_map (mapi, i); /* map to memory */
WriteW ((ABREG[absel] + i) & LA_MASK, t);
}
}
else { /* load */
meu_privileged (Always); /* priv if PRO */
for (i = 0; i < MEU_REG_COUNT; i++) {
t = ReadW ((ABREG[absel] + i) & LA_MASK);
meu_write_map (mapi, i, t); /* mem to map */
}
}
ABREG[absel] = (ABREG[absel] + MEU_REG_COUNT) & R_MASK;
break;
case 014: /* SSM 105714 (OP_A) */
WriteW (op[0].word, meu_update_status ()); /* store stat */
break;
case 015: /* JRS 105715 (OP_KA) */
meu_privileged (If_User_Map); /* priv viol if prot */
if (op[0].word & 0100000) /* bit 15 0/1 = disable/enable MEM */
operation = ME_Enabled;
else
operation = ME_Disabled;
if (op[0].word & 0040000) /* if bit 14 is set */
meu_set_state (operation, User_Map); /* then select the user map */
else /* otherwise */
meu_set_state (operation, System_Map); /* select the system map */
mp_check_jmp (op[1].word, 2); /* mpck jmp target */
PCQ_ENTRY; /* save old P */
PR = op[1].word; /* jump */
cpu_interrupt_enable = CLEAR; /* disable interrupts */
break;
/* DMS module 2 */
case 020: /* XMM 105720 (OP_N) */
XMM:
if (XR == 0) break; /* nop? */
while (XR != 0) { /* loop */
if (XR & D16_SIGN) { /* store? */
t = meu_read_map (Linear_Map, AR); /* map to mem */
WriteW (BR & LA_MASK, t);
XR = (XR + 1) & R_MASK;
}
else { /* load */
meu_privileged (Always); /* priv viol if prot */
t = ReadW (BR & LA_MASK); /* mem to map */
meu_write_map (Linear_Map, AR, t);
XR = (XR - 1) & R_MASK;
}
AR = (AR + 1) & R_MASK;
BR = (BR + 1) & R_MASK;
if (intrq && ((XR & 017) == 017)) { /* intr, grp of 16? */
PR = err_PR; /* stop for now */
break;
}
}
break;
case 021: /* XMS 105721 (OP_N) */
if ((XR & D16_SIGN) || (XR == 0)) break; /* nop? */
meu_privileged (Always); /* priv viol if prot */
while (XR != 0) {
meu_write_map (Linear_Map, AR, BR); /* AR to map */
XR = (XR - 1) & R_MASK;
AR = (AR + 1) & R_MASK;
BR = (BR + 1) & R_MASK;
if (intrq && ((XR & 017) == 017)) { /* intr, grp of 16? */
PR = err_PR;
break;
}
}
break;
case 022: /* XMA, XMB 10x722 (OP_N) */
meu_privileged (Always); /* priv viol if prot */
if (ABREG [absel] & 0100000)
mapi = User_Map;
else
mapi = System_Map;
if (ABREG [absel] & 0000001)
mapj = Port_B_Map;
else
mapj = Port_A_Map;
for (i = 0; i < MEU_REG_COUNT; i++) {
t = meu_read_map (mapi, i); /* read map */
meu_write_map (mapj, i, t); /* write map */
}
break;
case 024: /* XLA, XLB 10x724 (OP_A) */
ABREG[absel] = ReadWA (op[0].word); /* load alt */
break;
case 025: /* XSA, XSB 10x725 (OP_A) */
WriteWA (op[0].word, ABREG[absel]); /* store alt */
break;
case 026: /* XCA, XCB 10x726 (OP_A) */
if (ABREG[absel] != ReadWA (op[0].word)) /* compare alt */
PR = (PR + 1) & LA_MASK;
break;
case 027: /* LFA, LFB 10x727 (OP_N) */
meu_privileged (If_User_Map); /* priv viol if prot */
meu_set_fence (ABREG [absel]); /* load the MEM fence register */
break;
case 030: /* RSA, RSB 10x730 (OP_N) */
ABREG [absel] = meu_update_status (); /* save stat */
break;
case 031: /* RVA, RVB 10x731 (OP_N) */
ABREG [absel] = meu_update_violation (); /* return updated violation register */
break;
case 032: /* DJP 105732 (OP_A) */
meu_privileged (If_User_Map); /* priv viol if prot */
meu_set_state (ME_Disabled, System_Map); /* disable MEM and switch to the system map */
mp_check_jmp (op[0].word, 2); /* validate jump addr */
PCQ_ENTRY; /* save curr P */
PR = op[0].word; /* new P */
cpu_interrupt_enable = CLEAR; /* disable interrupts */
break;
case 033: /* DJS 105733 (OP_A) */
meu_privileged (If_User_Map); /* priv viol if prot */
WriteW (op[0].word, PR); /* store ret addr */
PCQ_ENTRY; /* save curr P */
PR = (op[0].word + 1) & LA_MASK; /* new P */
meu_set_state (ME_Disabled, System_Map); /* disable MEM and switch to the system map */
cpu_interrupt_enable = CLEAR; /* disable interrupts */
break;
case 034: /* SJP 105734 (OP_A) */
meu_privileged (If_User_Map); /* priv viol if prot */
meu_set_state (ME_Enabled, System_Map); /* enable MEM and switch to the system map */
mp_check_jmp (op[0].word, 2); /* validate jump addr */
PCQ_ENTRY; /* save curr P */
PR = op[0].word; /* jump */
cpu_interrupt_enable = CLEAR; /* disable interrupts */
break;
case 035: /* SJS 105735 (OP_A) */
meu_privileged (If_User_Map); /* priv viol if prot */
t = PR; /* save retn addr */
PCQ_ENTRY; /* save curr P */
PR = (op[0].word + 1) & LA_MASK; /* new P */
meu_set_state (ME_Enabled, System_Map); /* enable MEM and switch to the system map */
WriteW (op[0].word, t); /* store ret addr */
cpu_interrupt_enable = CLEAR; /* disable interrupts */
break;
case 036: /* UJP 105736 (OP_A) */
meu_privileged (If_User_Map); /* priv viol if prot */
meu_set_state (ME_Enabled, User_Map); /* enable MEM and switch to the user map */
mp_check_jmp (op[0].word, 2); /* validate jump addr */
PCQ_ENTRY; /* save curr P */
PR = op[0].word; /* jump */
cpu_interrupt_enable = CLEAR; /* disable interrupts */
break;
case 037: /* UJS 105737 (OP_A) */
meu_privileged (If_User_Map); /* priv viol if prot */
t = PR; /* save retn addr */
PCQ_ENTRY; /* save curr P */
PR = (op[0].word + 1) & LA_MASK; /* new P */
meu_set_state (ME_Enabled, User_Map); /* enable MEM and switch to the user map */
WriteW (op[0].word, t); /* store ret addr */
cpu_interrupt_enable = CLEAR; /* disable interrupts */
break;
default: /* others NOP */
break;
}
return reason;
}
/* Extended Instruction Group
The Extended Instruction Group (EIG) adds 32 index and 10 bit/byte/word
manipulation instructions to the 1000 base set. These instructions
use the new X and Y index registers that were added to the 1000.
Option implementation by CPU was as follows:
2114 2115 2116 2100 1000-M 1000-E 1000-F
------ ------ ------ ------ ------ ------ ------
N/A N/A N/A N/A std std std
The instruction codes are mapped to routines as follows:
Instr. 1000-M/E/F Instr. 1000-M/E/F
------ ---------- ------ ----------
10x740 S*X 10x760 ISX
10x741 C*X 10x761 DSX
10x742 L*X 10x762 JLY
10x743 STX 10x763 LBT
10x744 CX* 10x764 SBT
10x745 LDX 10x765 MBT
10x746 ADX 10x766 CBT
10x747 X*X 10x767 SFB
10x750 S*Y 10x770 ISY
10x751 C*Y 10x771 DSY
10x752 L*Y 10x772 JPY
10x753 STY 10x773 SBS
10x754 CY* 10x774 CBS
10x755 LDY 10x775 TBS
10x756 ADY 10x776 CMW
10x757 X*Y 10x777 MVW
Instructions that use IR bit 11 to select the A or B register are designated
with a * above (e.g., 101740 is SAX, and 105740 is SBX). For those that do
not use this feature, either the 101xxx or 105xxx code will execute the
corresponding instruction, although the 105xxx form is the documented
instruction code.
Implementation notes:
1. The LBT, SBT, MBT, and MVW instructions are used as part of the 2100 IOP
implementation. When so called, the MBT and MVW instructions have the
additional restriction that the count must be positive.
2. The protected memory lower bound for the JLY and JPY instructions is 0.
*/
static const OP_PAT op_eig[32] = {
OP_A, OP_N, OP_A, OP_A, /* S*X C*X L*X STX */
OP_N, OP_K, OP_K, OP_N, /* CX* LDX ADX X*X */
OP_A, OP_N, OP_A, OP_A, /* S*Y C*Y L*Y STY */
OP_N, OP_K, OP_K, OP_N, /* CY* LDY ADY X*Y */
OP_N, OP_N, OP_A, OP_N, /* ISX DSX JLY LBT */
OP_N, OP_KV, OP_KV, OP_N, /* SBT MBT CBT SFB */
OP_N, OP_N, OP_C, OP_KA, /* ISY DSY JPY SBS */
OP_KA, OP_KK, OP_KV, OP_KV /* CBS TBS CMW MVW */
};
t_stat cpu_eig (HP_WORD IR, uint32 intrq)
{
t_stat reason = SCPE_OK;
OPS op;
uint8 byte, b1, b2;
uint32 entry, absel, sum;
HP_WORD t, v1, v2, wc;
int32 sop1, sop2;
absel = AB_SELECT (IR); /* get the A/B register selector */
entry = IR & 037; /* mask to entry point */
if (op_eig [entry] != OP_N) {
reason = cpu_ops (op_eig [entry], op); /* get instruction operands */
if (reason != SCPE_OK) /* evaluation failed? */
return reason; /* return reason for failure */
}
switch (entry) { /* decode IR<4:0> */
/* EIG module 1 */
case 000: /* SAX, SBX 10x740 (OP_A) */
op[0].word = (op[0].word + XR) & LA_MASK; /* indexed addr */
WriteW (op[0].word, ABREG[absel]); /* store */
break;
case 001: /* CAX, CBX 10x741 (OP_N) */
XR = ABREG[absel]; /* copy to XR */
break;
case 002: /* LAX, LBX 10x742 (OP_A) */
op[0].word = (op[0].word + XR) & LA_MASK; /* indexed addr */
ABREG[absel] = ReadW (op[0].word); /* load */
break;
case 003: /* STX 105743 (OP_A) */
WriteW (op[0].word, XR); /* store XR */
break;
case 004: /* CXA, CXB 10x744 (OP_N) */
ABREG [absel] = XR; /* copy from XR */
break;
case 005: /* LDX 105745 (OP_K)*/
XR = op[0].word; /* load XR */
break;
case 006: /* ADX 105746 (OP_K) */
sum = XR + op[0].word; /* add to XR */
if (sum > D16_UMAX) E = 1; /* set E, O */
if (((~XR ^ op[0].word) & (XR ^ sum)) & D16_SIGN) O = 1;
XR = sum & R_MASK;
break;
case 007: /* XAX, XBX 10x747 (OP_N) */
t = XR; /* exchange XR */
XR = ABREG [absel];
ABREG [absel] = t;
break;
case 010: /* SAY, SBY 10x750 (OP_A) */
op[0].word = (op[0].word + YR) & LA_MASK; /* indexed addr */
WriteW (op[0].word, ABREG[absel]); /* store */
break;
case 011: /* CAY, CBY 10x751 (OP_N) */
YR = ABREG[absel]; /* copy to YR */
break;
case 012: /* LAY, LBY 10x752 (OP_A) */
op[0].word = (op[0].word + YR) & LA_MASK; /* indexed addr */
ABREG[absel] = ReadW (op[0].word); /* load */
break;
case 013: /* STY 105753 (OP_A) */
WriteW (op[0].word, YR); /* store YR */
break;
case 014: /* CYA, CYB 10x754 (OP_N) */
ABREG [absel] = YR; /* copy from YR */
break;
case 015: /* LDY 105755 (OP_K) */
YR = op[0].word; /* load YR */
break;
case 016: /* ADY 105756 (OP_K) */
sum = YR + op[0].word; /* add to YR */
if (sum > D16_UMAX) E = 1; /* set E, O */
if (((~YR ^ op[0].word) & (YR ^ sum)) & D16_SIGN) O = 1;
YR = sum & R_MASK;
break;
case 017: /* XAY, XBY 10x757 (OP_N) */
t = YR; /* exchange YR */
YR = ABREG [absel];
ABREG [absel] = t;
break;
/* EIG module 2 */
case 020: /* ISX 105760 (OP_N) */
XR = (XR + 1) & R_MASK; /* incr XR */
if (XR == 0) PR = (PR + 1) & LA_MASK; /* skip if zero */
break;
case 021: /* DSX 105761 (OP_N) */
XR = (XR - 1) & R_MASK; /* decr XR */
if (XR == 0) PR = (PR + 1) & LA_MASK; /* skip if zero */
break;
case 022: /* JLY 105762 (OP_A) */
mp_check_jmp (op[0].word, 0); /* validate jump addr */
PCQ_ENTRY;
YR = PR; /* ret addr to YR */
PR = op[0].word; /* jump */
break;
case 023: /* LBT 105763 (OP_N) */
AR = ReadB (BR); /* load byte */
BR = (BR + 1) & R_MASK; /* incr ptr */
break;
case 024: /* SBT 105764 (OP_N) */
WriteB (BR, LOWER_BYTE (AR)); /* store byte */
BR = (BR + 1) & R_MASK; /* incr ptr */
break;
case 025: /* MBT 105765 (OP_KV) */
wc = ReadW (op[1].word); /* get continuation count */
if (wc == 0) wc = op[0].word; /* none? get initiation count */
if ((wc & D16_SIGN) &&
cpu_configuration & CPU_2100)
break; /* < 0 is NOP for 2100 IOP */
while (wc != 0) { /* while count */
WriteW (op[1].word, wc); /* for MP abort */
byte = ReadB (AR); /* move byte */
WriteB (BR, byte);
AR = (AR + 1) & R_MASK; /* incr src */
BR = (BR + 1) & R_MASK; /* incr dst */
wc = (wc - 1) & D16_MASK; /* decr cnt */
if (intrq && wc) { /* intr, more to do? */
PR = err_PR; /* back up P */
break;
}
}
WriteW (op[1].word, wc); /* clean up inline */
break;
case 026: /* CBT 105766 (OP_KV) */
wc = ReadW (op[1].word); /* get continuation count */
if (wc == 0) wc = op[0].word; /* none? get initiation count */
while (wc != 0) { /* while count */
WriteW (op[1].word, wc); /* for MP abort */
b1 = ReadB (AR); /* get src1 */
b2 = ReadB (BR); /* get src2 */
if (b1 != b2) { /* compare */
PR = (PR + 1 + (b1 > b2)) & LA_MASK;
BR = (BR + wc) & R_MASK; /* update BR */
wc = 0; /* clr interim */
break;
}
AR = (AR + 1) & R_MASK; /* incr src1 */
BR = (BR + 1) & R_MASK; /* incr src2 */
wc = (wc - 1) & D16_MASK; /* decr cnt */
if (intrq && wc) { /* intr, more to do? */
PR = err_PR; /* back up P */
break;
}
}
WriteW (op[1].word, wc); /* clean up inline */
break;
case 027: /* SFB 105767 (OP_N) */
b1 = LOWER_BYTE (AR); /* test byte */
b2 = UPPER_BYTE (AR); /* term byte */
for (;;) { /* scan */
byte = ReadB (BR); /* read byte */
if (byte == b1) break; /* test match? */
BR = (BR + 1) & R_MASK;
if (byte == b2) { /* term match? */
PR = (PR + 1) & LA_MASK;
break;
}
if (intrq) { /* int pending? */
PR = err_PR; /* back up P */
break;
}
}
break;
case 030: /* ISY 105770 (OP_N) */
YR = (YR + 1) & R_MASK; /* incr YR */
if (YR == 0) PR = (PR + 1) & LA_MASK; /* skip if zero */
break;
case 031: /* DSY 105771 (OP_N) */
YR = (YR - 1) & R_MASK; /* decr YR */
if (YR == 0) PR = (PR + 1) & LA_MASK; /* skip if zero */
break;
case 032: /* JPY 105772 (OP_C) */
op[0].word = (op[0].word + YR) & LA_MASK; /* index, no indir */
mp_check_jmp (op[0].word, 0); /* validate jump addr */
PCQ_ENTRY;
PR = op[0].word; /* jump */
break;
case 033: /* SBS 105773 (OP_KA) */
WriteW (op[1].word, /* set bits */
ReadW (op[1].word) | op[0].word);
break;
case 034: /* CBS 105774 (OP_KA) */
WriteW (op[1].word, /* clear bits */
ReadW (op[1].word) & ~op[0].word);
break;
case 035: /* TBS 105775 (OP_KK) */
if ((op[1].word & op[0].word) != op[0].word) /* test bits */
PR = (PR + 1) & LA_MASK;
break;
case 036: /* CMW 105776 (OP_KV) */
wc = ReadW (op[1].word); /* get continuation count */
if (wc == 0) wc = op[0].word; /* none? get initiation count */
while (wc != 0) { /* while count */
WriteW (op[1].word, wc); /* for abort */
v1 = ReadW (AR & LA_MASK); /* first op */
v2 = ReadW (BR & LA_MASK); /* second op */
sop1 = SEXT16 (v1); /* signed */
sop2 = SEXT16 (v2);
if (sop1 != sop2) { /* compare */
PR = (PR + 1 + (sop1 > sop2)) & LA_MASK;
BR = (BR + wc) & R_MASK; /* update BR */
wc = 0; /* clr interim */
break;
}
AR = (AR + 1) & R_MASK; /* incr src1 */
BR = (BR + 1) & R_MASK; /* incr src2 */
wc = (wc - 1) & D16_MASK; /* decr cnt */
if (intrq && wc) { /* intr, more to do? */
PR = err_PR; /* back up P */
break;
}
}
WriteW (op[1].word, wc); /* clean up inline */
break;
case 037: /* MVW 105777 (OP_KV) */
wc = ReadW (op[1].word); /* get continuation count */
if (wc == 0) wc = op[0].word; /* none? get initiation count */
if ((wc & D16_SIGN) &&
cpu_configuration & CPU_2100)
break; /* < 0 is NOP for 2100 IOP */
while (wc != 0) { /* while count */
WriteW (op[1].word, wc); /* for abort */
t = ReadW (AR & LA_MASK); /* move word */
WriteW (BR & LA_MASK, t);
AR = (AR + 1) & R_MASK; /* incr src */
BR = (BR + 1) & R_MASK; /* incr dst */
wc = (wc - 1) & D16_MASK; /* decr cnt */
if (intrq && wc) { /* intr, more to do? */
PR = err_PR; /* back up P */
break;
}
}
WriteW (op[1].word, wc); /* clean up inline */
break;
}
return reason;
}

840
HP2100/hp2100_cpu3.c Normal file
View File

@@ -0,0 +1,840 @@
/* hp2100_cpu3.c: HP 2100/1000 FFP and DBI microcode simulator
Copyright (c) 2005-2019, J. David Bryan
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the name of the author shall not be
used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from the author.
CPU3 Fast FORTRAN Processor and Double Integer instructions
26-Mar-19 JDB Fixed interrupted .ENTR bug
28-Jul-18 JDB Renamed hp2100_fp1.h to hp2100_cpu_fp.h
24-Aug-17 JDB op_ffp_f definition is now conditional on HAVE_INT64
27-Mar-17 JDB Improved the comments for the FFP instructions
05-Aug-16 JDB Renamed the P register from "PC" to "PR"
24-Dec-14 JDB Added casts for explicit downward conversions
09-May-12 JDB Separated assignments from conditional expressions
11-Sep-08 JDB Moved microcode function prototypes to hp2100_cpu1.h
05-Sep-08 JDB Removed option-present tests (now in UIG dispatchers)
05-Aug-08 JDB Updated mp_dms_jmp calling sequence
27-Feb-08 JDB Added DBI self-test instruction
23-Oct-07 JDB Fixed unsigned-divide bug in .DDI
17-Oct-07 JDB Fixed unsigned-multiply bug in .DMP
16-Oct-06 JDB Calls FPP for extended-precision math
12-Oct-06 JDB Altered DBLE, DDINT for F-Series FFP compatibility
26-Sep-06 JDB Moved from hp2100_cpu1.c to simplify extensions
09-Aug-06 JDB Added double-integer instruction set
18-Feb-05 JDB Add 2100/21MX Fast FORTRAN Processor instructions
Primary references:
- HP 1000 M/E/F-Series Computers Technical Reference Handbook
(5955-0282, March 1980)
- HP 1000 M/E/F-Series Computers Engineering and Reference Documentation
(92851-90001, March 1981)
- Macro/1000 Reference Manual
(92059-90001, December 1992)
Firmware-specific references:
- DOS/RTE Relocatable Library Reference Manual
(24998-90001, October 1981)
- Implementing the HP 2100 Fast FORTRAN Processor
(12907-90010, November 1974)
- 93585A Microcode Source
(93585-18002 Rev. 2005)
- 93585A Double Integer Instructions Installation and Reference Manual
(93585-90007, February 1984)
*/
#include "hp2100_defs.h"
#include "hp2100_cpu.h"
#include "hp2100_cpu_dmm.h"
#include "hp2100_cpu_fp.h"
/* Fast FORTRAN Processor.
The Fast FORTRAN Processor (FFP) is a set of FORTRAN language accelerators
and extended-precision (three-word) floating point routines. Although the
FFP is an option for the 2100 and later CPUs, each implements the FFP in a
slightly different form.
Option implementation by CPU was as follows:
2114 2115 2116 2100 1000-M 1000-E 1000-F
------ ------ ------ ------ ------ ------ ------
N/A N/A N/A 12907A 12977B 13306B std
The instruction codes are mapped to routines as follows:
Instr. 2100 1000-M 1000-E 1000-F Instr. 2100 1000-M 1000-E 1000-F
------ ------ ------ ------ ------ ------ ------ ------ ------ ------
105200 -- [nop] [nop] [test] 105220 .XFER .XFER .XFER .XFER
105201 DBLE DBLE DBLE DBLE 105221 .GOTO .GOTO .GOTO .GOTO
105202 SNGL SNGL SNGL SNGL 105222 ..MAP ..MAP ..MAP ..MAP
105203 .XMPY .XMPY .XMPY .DNG 105223 .ENTR .ENTR .ENTR .ENTR
105204 .XDIV .XDIV .XDIV .DCO 105224 .ENTP .ENTP .ENTP .ENTP
105205 .DFER .DFER .DFER .DFER 105225 -- .PWR2 .PWR2 .PWR2
105206 -- .XPAK .XPAK .XPAK 105226 -- .FLUN .FLUN .FLUN
105207 -- XADD XADD .BLE 105227 $SETP $SETP $SETP $SETP
105210 -- XSUB XSUB .DIN 105230 -- .PACK .PACK .PACK
105211 -- XMPY XMPY .DDE 105231 -- -- .CFER .CFER
105212 -- XDIV XDIV .DIS 105232 -- -- -- ..FCM
105213 .XADD .XADD .XADD .DDS 105233 -- -- -- ..TCM
105214 .XSUB .XSUB .XSUB .NGL 105234 -- -- -- --
105215 -- .XCOM .XCOM .XCOM 105235 -- -- -- --
105216 -- ..DCM ..DCM ..DCM 105236 -- -- -- --
105217 -- DDINT DDINT DDINT 105237 -- -- -- --
The F-Series maps different instructions to several of the standard FFP
opcodes. We first look for these and dispatch them appropriately before
falling into the handler for the common instructions.
The math functions use the F-Series FPP for implementation. The FPP requires
that the host compiler support 64-bit integers. Therefore, if 64-bit
integers are not available, the math instructions of the FFP are disabled.
We allow this partial implementation as an aid in running systems generated
for the FFP. Most system programs did not use the math instructions, but
almost all use .ENTR. Supporting the latter even on systems that do not
support the former still allows such systems to boot.
Implementation notes:
1. The "$SETP" instruction is sometimes listed as ".SETP" in the
documentation.
2. Extended-precision arithmetic routines (e.g., .XMPY) exist on the 1000-F,
but they are assigned instruction codes in the single-precision
floating-point module range. They are replaced by several double integer
instructions, which we dispatch to the double integer handler.
3. The software implementation of ..MAP supports 1-, 2-, or 3-dimensional
arrays, designated by setting A = -1, 0, and +1, respectively. The
firmware implementation supports only 2- and 3-dimensional access.
4. The documentation for ..MAP for the 2100 FFP shows A = 0 or -1 for two or
three dimensions, respectively, but the 1000 FFP shows A = 0 or +1. The
firmware actually only checks the LSB of A.
5. The .DFER and .XFER implementations for the 2100 FFP return X+4 and Y+4
in the A and B registers, whereas the 1000 FFP returns X+3 and Y+3.
6. The .XFER implementation for the 2100 FFP returns to P+2, whereas the
1000 implementation returns to P+1.
7. The firmware implementations of DBLE, .BLE, and DDINT clear the overflow
flag. The software implementations do not change overflow.
8. The M/E-Series FFP arithmetic instructions (.XADD, etc.) return negative
infinity on negative overflow and positive infinity on positive overflow.
The equivalent F-Series instructions return positive infinity on both.
9. The protected memory lower bound for the .GOTO instruction is 2.
10. The OP_N (none) operand pattern is used here for all double-integer
instructions. They are dispatched to the DBI handler for execution,
where the correct operands will be retrieved
*/
#if defined (HAVE_INT64) /* int64 support available */
static const OP_PAT op_ffp_f [32] = { /* patterns for F-Series only */
OP_N, OP_AAF, OP_AX, OP_N, /* [tst] DBLE SNGL .DNG */
OP_N, OP_AA, OP_A, OP_AAF, /* .DCO .DFER .XPAK .BLE */
OP_N, OP_N, OP_N, OP_N, /* .DIN .DDE .DIS .DDS */
OP_AT, OP_A, OP_A, OP_AAX, /* .NGL .XCOM ..DCM DDINT */
OP_N, OP_AK, OP_KKKK, OP_A, /* .XFER .GOTO ..MAP .ENTR */
OP_A, OP_RK, OP_R, OP_K, /* .ENTP .PWR2 .FLUN $SETP */
OP_RC, OP_AA, OP_R, OP_A, /* .PACK .CFER ..FCM ..TCM */
OP_N, OP_N, OP_N, OP_N /* --- --- --- --- */
};
#endif /* end of int64 support */
static const OP_PAT op_ffp_e [32] = { /* patterns for 2100/M/E-Series */
OP_N, OP_AAF, OP_AX, OP_AXX, /* [nop] DBLE SNGL .XMPY */
OP_AXX, OP_AA, OP_A, OP_AAXX, /* .XDIV .DFER .XPAK XADD */
OP_AAXX, OP_AAXX, OP_AAXX, OP_AXX, /* XSUB XMPY XDIV .XADD */
OP_AXX, OP_A, OP_A, OP_AAX, /* .XSUB .XCOM ..DCM DDINT */
OP_N, OP_AK, OP_KKKK, OP_A, /* .XFER .GOTO ..MAP .ENTR */
OP_A, OP_RK, OP_R, OP_K, /* .ENTP .PWR2 .FLUN $SETP */
OP_RC, OP_AA, OP_N, OP_N, /* .PACK .CFER --- --- */
OP_N, OP_N, OP_N, OP_N /* --- --- --- --- */
};
t_stat cpu_ffp (uint32 intrq)
{
OP fpop;
OPS op, op2;
uint32 entry;
HP_WORD j, sa, sb, sc, da, dc, ra, MA;
int32 expon;
t_stat reason = SCPE_OK;
#if defined (HAVE_INT64) /* int64 support available */
int32 i;
#endif /* end of int64 support */
entry = IR & 037; /* mask to entry point */
if (!(cpu_configuration & CPU_1000_F)) { /* 2100/M/E-Series? */
if (op_ffp_e [entry] != OP_N) {
reason = cpu_ops (op_ffp_e [entry], op); /* get instruction operands */
if (reason != SCPE_OK) /* evaluation failed? */
return reason; /* return reason for failure */
}
}
#if defined (HAVE_INT64) /* int64 support available */
else { /* F-Series */
if (op_ffp_f [entry] != OP_N) {
reason = cpu_ops (op_ffp_f [entry], op); /* get instruction operands */
if (reason != SCPE_OK) /* evaluation failed? */
return reason; /* return reason for failure */
}
switch (entry) { /* decode IR<4:0> */
case 000: /* [tst] 105200 (OP_N) */
XR = 4; /* firmware revision */
SR = 0102077; /* test passed code */
AR = 0; /* test clears A/B */
BR = 0;
PR = (PR + 1) & LA_MASK; /* P+2 return for firmware w/DBI */
return reason;
case 003: /* .DNG 105203 (OP_N) */
return cpu_dbi (0105323u); /* remap to double int handler */
case 004: /* .DCO 105204 (OP_N) */
return cpu_dbi (0105324u); /* remap to double int handler */
case 007: /* .BLE 105207 (OP_AAF) */
O = fp_cvt (&op[2], fp_f, fp_t); /* convert value and clear overflow */
WriteOp (op[1].word, op[2], fp_t); /* write double-precision value */
return reason;
case 010: /* .DIN 105210 (OP_N) */
return cpu_dbi (0105330u); /* remap to double int handler */
case 011: /* .DDE 105211 (OP_N) */
return cpu_dbi (0105331u); /* remap to double int handler */
case 012: /* .DIS 105212 (OP_N) */
return cpu_dbi (0105332u); /* remap to double int handler */
case 013: /* .DDS 105213 (OP_N) */
return cpu_dbi (0105333u); /* remap to double int handler */
case 014: /* .NGL 105214 (OP_AT) */
O = fp_cvt (&op[1], fp_t, fp_f); /* convert value */
AR = op[1].fpk[0]; /* move MSB to A */
BR = op[1].fpk[1]; /* move LSB to B */
return reason;
case 032: /* ..FCM 105232 (OP_R) */
O = fp_pcom (&op[0], fp_f); /* complement value */
AR = op[0].fpk[0]; /* return result */
BR = op[0].fpk[1]; /* to A/B registers */
return reason;
case 033: /* ..TCM 105233 (OP_A) */
fpop = ReadOp (op[0].word, fp_t); /* read 4-word value */
O = fp_pcom (&fpop, fp_t); /* complement it */
WriteOp (op[0].word, fpop, fp_t); /* write 4-word value */
return reason;
} /* fall thru if not special to F */
}
#endif /* end of int64 support */
switch (entry) { /* decode IR<4:0> */
/* FFP module 1 */
case 000: /* [nop] 105200 (OP_N) */
if (!(cpu_configuration & CPU_1000)) /* must be 1000 M/E-series */
return STOP (cpu_ss_unimpl); /* trap if not */
break;
#if defined (HAVE_INT64) /* int64 support available */
case 001: /* DBLE 105201 (OP_AAF) */
O = fp_cvt (&op[2], fp_f, fp_x); /* convert value and clear overflow */
WriteOp (op[1].word, op[2], fp_x); /* write extended-precision value */
break;
case 002: /* SNGL 105202 (OP_AX) */
O = fp_cvt (&op[1], fp_x, fp_f); /* convert value */
AR = op[1].fpk[0]; /* move MSB to A */
BR = op[1].fpk[1]; /* move LSB to B */
break;
case 003: /* .XMPY 105203 (OP_AXX) */
i = 0; /* params start at op[0] */
goto XMPY; /* process as XMPY */
case 004: /* .XDIV 105204 (OP_AXX) */
i = 0; /* params start at op[0] */
goto XDIV; /* process as XDIV */
#endif /* end of int64 support */
case 005: /* .DFER 105205 (OP_AA) */
BR = op[0].word; /* get destination address */
AR = op[1].word; /* get source address */
goto XFER; /* do transfer */
#if defined (HAVE_INT64) /* int64 support available */
case 006: /* .XPAK 105206 (OP_A) */
if (!(cpu_configuration & CPU_1000)) /* must be 1000 */
return STOP (cpu_ss_unimpl); /* trap if not */
if (intrq) { /* interrupt pending? */
PR = err_PR; /* restart instruction */
break;
}
fpop = ReadOp (op[0].word, fp_x); /* read unpacked */
O = fp_nrpack (&fpop, fpop, SEXT16 (AR), fp_x); /* nrm/rnd/pack mantissa, exponent */
WriteOp (op[0].word, fpop, fp_x); /* write result */
break;
case 007: /* XADD 105207 (OP_AAXX) */
i = 1; /* params start at op[1] */
XADD: /* enter here from .XADD */
if (intrq) { /* interrupt pending? */
PR = err_PR; /* restart instruction */
break;
}
O = fp_exec (001, &fpop, op[i + 1], op[i + 2]); /* three-word add */
WriteOp (op[i].word, fpop, fp_x); /* write sum */
break;
case 010: /* XSUB 105210 (OP_AAXX) */
i = 1; /* params start at op[1] */
XSUB: /* enter here from .XSUB */
if (intrq) { /* interrupt pending? */
PR = err_PR; /* restart instruction */
break;
}
O = fp_exec (021, &fpop, op[i + 1], op[i + 2]); /* three-word subtract */
WriteOp (op[i].word, fpop, fp_x); /* write difference */
break;
case 011: /* XMPY 105211 (OP_AAXX) */
i = 1; /* params start at op[1] */
XMPY: /* enter here from .XMPY */
if (intrq) { /* interrupt pending? */
PR = err_PR; /* restart instruction */
break;
}
O = fp_exec (041, &fpop, op[i + 1], op[i + 2]); /* three-word multiply */
WriteOp (op[i].word, fpop, fp_x); /* write product */
break;
case 012: /* XDIV 105212 (OP_AAXX) */
i = 1; /* params start at op[1] */
XDIV: /* enter here from .XDIV */
if (intrq) { /* interrupt pending? */
PR = err_PR; /* restart instruction */
break;
}
O = fp_exec (061, &fpop, op[i + 1], op[i + 2]); /* three-word divide */
WriteOp (op[i].word, fpop, fp_x); /* write quotient */
break;
case 013: /* .XADD 105213 (OP_AXX) */
i = 0; /* params start at op[0] */
goto XADD; /* process as XADD */
case 014: /* .XSUB 105214 (OP_AXX) */
i = 0; /* params start at op[0] */
goto XSUB; /* process as XSUB */
case 015: /* .XCOM 105215 (OP_A) */
if (!(cpu_configuration & CPU_1000)) /* must be 1000 */
return STOP (cpu_ss_unimpl); /* trap if not */
fpop = ReadOp (op[0].word, fp_x); /* read unpacked */
AR = fp_ucom (&fpop, fp_x); /* complement and rtn exp adj */
WriteOp (op[0].word, fpop, fp_x); /* write result */
break;
case 016: /* ..DCM 105216 (OP_A) */
if (!(cpu_configuration & CPU_1000)) /* must be 1000 */
return STOP (cpu_ss_unimpl); /* trap if not */
if (intrq) { /* interrupt pending? */
PR = err_PR; /* restart instruction */
break;
}
fpop = ReadOp (op[0].word, fp_x); /* read operand */
O = fp_pcom (&fpop, fp_x); /* complement (can't ovf neg) */
WriteOp (op[0].word, fpop, fp_x); /* write result */
break;
case 017: /* DDINT 105217 (OP_AAX) */
if (!(cpu_configuration & CPU_1000)) /* must be 1000 */
return STOP (cpu_ss_unimpl); /* trap if not */
if (intrq) { /* interrupt pending? */
PR = err_PR; /* restart instruction */
break;
}
O = fp_trun (&fpop, op[2], fp_x); /* truncate operand (can't ovf) */
WriteOp (op[1].word, fpop, fp_x); /* write result */
break;
#endif /* end of int64 support */
/* FFP module 2 */
case 020: /* .XFER 105220 (OP_N) */
if (cpu_configuration & CPU_2100)
PR = (PR + 1) & LA_MASK; /* 2100 .XFER returns to P+2 */
XFER: /* enter here from .DFER */
sc = 3; /* set count for 3-wd xfer */
goto CFER; /* do transfer */
case 021: /* .GOTO 105221 (OP_AK) */
if (SEXT16 (op[1].word) < 1) /* index < 1? */
op[1].word = 1; /* reset min */
sa = PR + op[1].word - 1; /* point to jump target */
if (sa >= op[0].word) /* must be <= last target */
sa = op[0].word - 1;
MR = ReadW (sa); /* get jump target */
reason = cpu_resolve_indirects (TRUE); /* resolve indirects */
if (reason != SCPE_OK) { /* resolution failed? */
PR = err_PR; /* irq restarts instruction */
break;
}
mp_check_jmp (MR, 2); /* validate jump addr */
PCQ_ENTRY; /* record last P */
PR = MR; /* jump */
BR = op[0].word; /* (for 2100 FFP compat) */
break;
case 022: /* ..MAP 105222 (OP_KKKK) */
op[1].word = op[1].word - 1; /* decrement 1st subscr */
if ((AR & 1) == 0) /* 2-dim access? */
op[1].word = op[1].word + /* compute element offset */
(op[2].word - 1) * op[3].word;
else { /* 3-dim access */
reason = cpu_ops (OP_KK, op2); /* get 1st, 2nd ranges */
if (reason != SCPE_OK) { /* evaluation failed? */
PR = err_PR; /* irq restarts instruction */
break;
}
op[1].word = op[1].word + /* offset */
((op[3].word - 1) * op2[1].word +
op[2].word - 1) * op2[0].word;
}
AR = (op[0].word + op[1].word * BR) & R_MASK; /* return element address */
break;
case 023: /* .ENTR 105223 (OP_A) */
MA = PR - 3; /* get addr of entry point */
ENTR: /* enter here from .ENTP */
da = op[0].word; /* get addr of 1st formal */
dc = MA - da; /* get count of formals */
sa = ReadW (MA); /* get addr of return point */
ra = ReadW (sa++); /* get rtn, ptr to 1st actual */
sc = ra - sa; /* get count of actuals */
if (sc > dc) /* use min (actuals, formals) */
sc = dc;
for (j = 0; j < sc; j++) {
MR = ReadW (sa++); /* get addr of actual */
reason = cpu_resolve_indirects (TRUE); /* resolve indirects */
if (reason != SCPE_OK) { /* resolution failed? */
PR = err_PR; /* irq restarts instruction */
break;
}
WriteW (da++, MR); /* put addr into formal */
}
if (reason == SCPE_OK) /* if the instruction ended normally */
WriteW (MA, ra); /* then update the return address */
AR = (HP_WORD) ra; /* return address */
BR = (HP_WORD) da; /* addr of 1st unused formal */
break;
case 024: /* .ENTP 105224 (OP_A) */
MA = PR - 5; /* get addr of entry point */
goto ENTR;
case 025: /* .PWR2 105225 (OP_RK) */
if (!(cpu_configuration & CPU_1000)) /* must be 1000 */
return STOP (cpu_ss_unimpl); /* trap if not */
fp_unpack (&fpop, &expon, op[0], fp_f); /* unpack value */
expon = expon + SEXT16 (op[1].word); /* multiply by 2**n */
fp_pack (&fpop, fpop, expon, fp_f); /* repack value */
AR = fpop.fpk[0]; /* return result */
BR = fpop.fpk[1]; /* to A/B registers */
break;
case 026: /* .FLUN 105226 (OP_R) */
if (!(cpu_configuration & CPU_1000)) /* must be 1000 */
return STOP (cpu_ss_unimpl); /* trap if not */
fp_unpack (&fpop, &expon, op[0], fp_f); /* unpack value */
AR = expon & R_MASK; /* return expon to A */
BR = fpop.fpk[1]; /* and low mant to B */
break;
case 027: /* $SETP 105227 (OP_K) */
j = sa = AR; /* save initial value */
sb = BR; /* save initial address */
AR = 0; /* AR will return = 0 */
BR = BR & LA_MASK; /* addr must be direct */
do {
WriteW (BR, j); /* write value to address */
j = (j + 1) & D16_MASK; /* incr value */
BR = (BR + 1) & LA_MASK; /* incr address */
op[0].word = op[0].word - 1; /* decr count */
if (op[0].word && intrq) { /* more and intr? */
AR = sa; /* restore A */
BR = sb; /* restore B */
PR = err_PR; /* restart instruction */
break;
}
}
while (op[0].word != 0); /* loop until count exhausted */
break;
case 030: /* .PACK 105230 (OP_RC) */
if (!(cpu_configuration & CPU_1000)) /* must be 1000 */
return STOP (cpu_ss_unimpl); /* trap if not */
O = fp_nrpack (&fpop, op[0], /* nrm/rnd/pack value */
SEXT16 (op[1].word), fp_f);
AR = fpop.fpk[0]; /* return result */
BR = fpop.fpk[1]; /* to A/B registers */
break;
case 031: /* .CFER 105231 (OP_AA) */
if (!(cpu_configuration & CPU_1000_E_F)) /* must be 1000 E- or F-series */
return STOP (cpu_ss_unimpl); /* trap if not */
BR = op[0].word; /* get destination address */
AR = op[1].word; /* get source address */
sc = 4; /* set for 4-wd xfer */
CFER: /* enter here from .XFER */
for (j = 0; j < sc; j++) { /* xfer loop */
WriteW (BR, ReadW (AR)); /* transfer word */
AR = (AR + 1) & LA_MASK; /* bump source addr */
BR = (BR + 1) & LA_MASK; /* bump destination addr */
}
E = 0; /* routine clears E */
if (cpu_configuration & CPU_2100) { /* 2100 (and .DFER/.XFER)? */
AR = (AR + 1) & LA_MASK; /* 2100 FFP returns X+4, Y+4 */
BR = (BR + 1) & LA_MASK;
}
break;
default: /* others unimplemented */
reason = STOP (cpu_ss_unimpl);
}
return reason;
}
/* Double-Integer Instructions.
The double-integer instructions were added to the HP instruction set at
revision 1920 of the 1000-F. They were immediately adopted in a number of HP
software products, most notably the RTE file management package (FMP)
routines. As these routines are used in nearly every RTE program, F-Series
programs were almost always a few hundred bytes smaller than their M- and
E-Series counterparts. This became significant as RTE continued to grow in
size, and some customer programs ran out of address space on E-Series
machines.
While HP never added double-integer instructions to the standard E-Series, a
product from the HP "specials group," HP 93585A, provided microcoded
replacements for the E-Series. This could provide just enough address-space
savings to allow programs to load in E-Series systems, in addition to
accelerating these common operations.
There was no equivalent M-Series microcode, due to the limited micromachine
address space on that system.
Option implementation by CPU was as follows:
2114 2115 2116 2100 1000-M 1000-E 1000-F
------ ------ ------ ------ ------ ------ ------
N/A N/A N/A N/A N/A 93585A std
The routines are mapped to instruction codes as follows:
Instr. 1000-E 1000-F Description
------ ------ ------ -----------------------------------------
[test] 105320 -- [self test]
.DAD 105321 105014 Double integer add
.DMP 105322 105054 Double integer multiply
.DNG 105323 105203 Double integer negate
.DCO 105324 105204 Double integer compare
.DDI 105325 105074 Double integer divide
.DDIR 105326 105134 Double integer divide (reversed)
.DSB 105327 105034 Double integer subtract
.DIN 105330 105210 Double integer increment
.DDE 105331 105211 Double integer decrement
.DIS 105332 105212 Double integer increment and skip if zero
.DDS 105333 105213 Double integer decrement and skip if zero
.DSBR 105334 105114 Double integer subtraction (reversed)
On the F-Series, the double-integer instruction codes are split among the
floating-point processor and the Fast FORTRAN Processor ranges. They are
dispatched from those respective simulators for processing here.
Implementation notes:
1. Opcodes 105335-105337 are NOPs in the microcode. They generate
unimplemented instructions stops under simulation.
2. This is an implementation of Revision 2 of the microcode, which was
released as ROM part numbers 93585-80003, 93585-80005, and 93585-80001
(Revision 1 substituted -80002 for -80005).
3. The F-Series firmware executes .DMP and .DDI/.DDIR by floating the 32-bit
double integer to a 48-bit extended-precision number, calling the FPP to
execute the extended-precision multiply/divide, and then fixing the
product to a 32-bit double integer. We simulate these directly with 64-
or 32-bit integer arithmetic.
*/
static const OP_PAT op_dbi[16] = {
OP_N, OP_JD, OP_JD, OP_J, /* [test] .DAD .DMP .DNG */
OP_JD, OP_JD, OP_JD, OP_JD, /* .DCO .DDI .DDIR .DSB */
OP_J, OP_J, OP_A, OP_A, /* .DIN .DDE .DIS .DDS */
OP_JD, OP_N, OP_N, OP_N /* .DSBR --- --- --- */
};
t_stat cpu_dbi (HP_WORD IR)
{
OP din;
OPS op;
uint32 entry, t;
t_stat reason = SCPE_OK;
entry = IR & 017; /* mask to entry point */
if (op_dbi[entry] != OP_N) {
reason = cpu_ops (op_dbi [entry], op); /* get instruction operands */
if (reason != SCPE_OK) /* evaluation failed? */
return reason; /* return reason for failure */
}
switch (entry) { /* decode IR<3:0> */
case 000: /* [test] 105320 (OP_N) */
XR = 2; /* set revision */
BR = 0377; /* side effect of microcode */
SR = 0102077; /* set "pass" code */
PR = (PR + 1) & LA_MASK; /* return to P+1 */
t = (AR << 16) | BR; /* set t for return */
break;
case 001: /* .DAD 105321 (OP_JD) */
t = op[0].dword + op[1].dword; /* add values */
E = E | (t < op[0].dword); /* carry if result smaller */
O = (((~op[0].dword ^ op[1].dword) & /* overflow if sign wrong */
(op[0].dword ^ t) & D32_SIGN) != 0);
break;
case 002: /* .DMP 105322 (OP_JD) */
{
#if defined (HAVE_INT64) /* int64 support available */
t_int64 t64;
t64 = (t_int64) INT32 (op[0].dword) * /* multiply signed values */
(t_int64) INT32 (op[1].dword);
O = ((t64 < -(t_int64) 0x80000000) || /* overflow if out of range */
(t64 > (t_int64) 0x7FFFFFFF));
if (O)
t = D32_SMAX; /* if overflow, rtn max pos */
else
t = (uint32) (t64 & D32_MASK); /* else lower 32 bits of result */
#else /* int64 support unavailable */
uint32 sign, xu, yu, rh, rl;
sign = ((int32) op[0].dword < 0) ^ /* save sign of result */
((int32) op[1].dword < 0);
xu = (uint32) abs ((int32) op[0].dword); /* make operands pos */
yu = (uint32) abs ((int32) op[1].dword);
if ((xu & 0xFFFF0000) == 0 && /* 16 x 16 multiply? */
(yu & 0xFFFF0000) == 0) {
t = xu * yu; /* do it */
O = 0; /* can't overflow */
}
else if ((xu & 0xFFFF0000) != 0 && /* 32 x 32 multiply? */
(yu & 0xFFFF0000) != 0)
O = 1; /* always overflows */
else { /* 16 x 32 or 32 x 16 */
rl = (xu & 0xFFFF) * (yu & 0xFFFF); /* form 1st partial product */
if ((xu & 0xFFFF0000) == 0)
rh = xu * (yu >> 16) + (rl >> 16); /* 16 x 32 2nd partial */
else
rh = (xu >> 16) * yu + (rl >> 16); /* 32 x 16 2nd partial */
O = (rh > 0x7FFF + sign); /* check for out of range */
if (O == 0)
t = (rh << 16) | (rl & 0xFFFF); /* combine partials */
}
if (O) /* if overflow occurred */
t = D32_SMAX; /* then return the largest positive number */
else if (sign) /* otherwise if the result is negative */
t = ~t + 1; /* then return the twos complement (set if O = 0 above) */
#endif /* end of int64 support */
}
break;
case 003: /* .DNG 105323 (OP_J) */
t = ~op[0].dword + 1; /* negate value */
O = (op[0].dword == D32_SIGN); /* overflow if max neg */
if (op[0].dword == 0) /* borrow if result zero */
E = 1;
break;
case 004: /* .DCO 105324 (OP_JD) */
t = op[0].dword; /* copy for later store */
if ((int32) op[0].dword < (int32) op[1].dword)
PR = (PR + 1) & LA_MASK; /* < rtns to P+2 */
else if ((int32) op[0].dword > (int32) op[1].dword)
PR = (PR + 2) & LA_MASK; /* > rtns to P+3 */
break; /* = rtns to P+1 */
case 005: /* .DDI 105325 (OP_JD) */
DDI:
O = ((op[1].dword == 0) || /* overflow if div 0 */
((op[0].dword == D32_SMIN) && /* or max neg div -1 */
((int32) op[1].dword == -1)));
if (O)
t = D32_SMAX; /* rtn max pos for ovf */
else
t = (uint32) (INT32 (op[0].dword) / /* else return quotient */
INT32 (op[1].dword));
break;
case 006: /* .DDIR 105326 (OP_JD) */
t = op[0].dword; /* swap operands */
op[0].dword = op[1].dword;
op[1].dword = t;
goto DDI; /* continue at .DDI */
case 007: /* .DSB 105327 (OP_JD) */
DSB:
t = op[0].dword - op[1].dword; /* subtract values */
E = E | (op[0].dword < op[1].dword); /* borrow if minu < subtr */
O = (((op[0].dword ^ op[1].dword) & /* overflow if sign wrong */
(op[0].dword ^ t) & D32_SIGN) != 0);
break;
case 010: /* .DIN 105330 (OP_J) */
t = op[0].dword + 1; /* increment value */
O = (t == D32_SMIN); /* overflow if sign flipped */
if (t == 0)
E = 1; /* carry if result zero */
break;
case 011: /* .DDE 105331 (OP_J) */
t = op[0].dword - 1; /* decrement value */
O = (t == D32_SMAX); /* overflow if sign flipped */
if ((int32) t == -1)
E = 1; /* borrow if result -1 */
break;
case 012: /* .DIS 105332 (OP_A) */
din = ReadOp (op[0].word, in_d); /* get value */
t = din.dword = din.dword + 1; /* increment value */
WriteOp (op[0].word, din, in_d); /* store it back */
if (t == 0)
PR = (PR + 1) & LA_MASK; /* skip if result zero */
break;
case 013: /* .DDS 105333 (OP_A) */
din = ReadOp (op[0].word, in_d); /* get value */
t = din.dword = din.dword - 1; /* decrement value */
WriteOp (op[0].word, din, in_d); /* write it back */
if (t == 0)
PR = (PR + 1) & LA_MASK; /* skip if result zero */
break;
case 014: /* .DSBR 105334 (OP_JD) */
t = op[0].dword; /* swap operands */
op[0].dword = op[1].dword;
op[1].dword = t;
goto DSB; /* continue at .DSB */
default: /* others unimplemented */
t = (AR << 16) | BR; /* set t for NOP */
reason = STOP (cpu_ss_unimpl);
}
if (reason == SCPE_OK) { /* if return OK */
AR = UPPER_WORD (t); /* break result */
BR = LOWER_WORD (t); /* into A and B */
}
return reason;
}

1136
HP2100/hp2100_cpu4.c Normal file

File diff suppressed because it is too large Load Diff

2531
HP2100/hp2100_cpu5.c Normal file

File diff suppressed because it is too large Load Diff

1076
HP2100/hp2100_cpu6.c Normal file

File diff suppressed because it is too large Load Diff

1013
HP2100/hp2100_cpu7.c Normal file

File diff suppressed because it is too large Load Diff

207
HP2100/hp2100_cpu_dmm.h Normal file
View File

@@ -0,0 +1,207 @@
/* hp2100_cpu_dmm.h: HP 2100 CPU-to-DMA/MEM/MP interface declarations
Copyright (c) 2018, J. David Bryan
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the name of the author shall not be used
in advertising or otherwise to promote the sale, use or other dealings in
this Software without prior written authorization from the author.
26-Jul-18 JDB Created
This file contains declarations used by the CPU to interface with the Direct
Memory Access/Dual-Channel Port Controller, Memory Expansion Module, and
Memory Protect accessories.
*/
#include "hp2100_io.h" /* include the I/O definitions for the I/O dispatcher */
/* I/O dispatcher return value */
typedef struct { /* the I/O dispatcher return structure */
t_bool skip; /* TRUE if the interface asserted the SKF signal */
HP_WORD data; /* the data value returned from the interface */
} SKPF_DATA;
/* I/O Group control operations */
typedef enum { /* derived from IR bits 11-6 */
iog_HLT, /* xx0000 HLT */
iog_STF, /* xx0001 STF */
iog_SFC, /* xx0010 SFC */
iog_SFS, /* xx0011 SFS */
iog_MIx, /* xx0100 MIA/B */
iog_LIx, /* xx0101 LIA/B */
iog_OTx, /* xx0110 OTA/B */
iog_STC, /* xx0111 STC */
iog_HLT_C, /* xx1000 HLT,C */
iog_CLF, /* xx1001 CLF */
iog_SFC_C, /* xx1010 SFC,C */
iog_SFS_C, /* xx1011 SFS,C */
iog_MIx_C, /* xx1100 MIA/B,C */
iog_LIx_C, /* xx1101 LIA/B,C */
iog_OTx_C, /* xx1110 OTA/B,C */
iog_STC_C, /* xx1111 STC,C */
iog_CLC, /* 1x0111 CLC */
iog_CLC_C /* 1x1111 CLC,C */
} IO_GROUP_OP;
/* DMA global state */
extern uint32 dma_request_set;
/* DMA global utility routine declarations */
extern void dma_configure (void);
extern void dma_assert_SRQ (uint32 select_code);
extern void dma_service (void);
/* MEU Maps.
Implementation notes:
1. The System_Map and User_Map enumeration values must be 0 and 1, so that
switching to the alternate map may be performed by an XOR with 1.
Additionally, the four map enumeration values must be 0-3 to permit
selection by the lower two bits of the instruction register for the SYx,
USx, PAx, and PBx instructions.
*/
#define MEU_MAP_MASK 3
#define MEU_REG_COUNT (1u << LP_WIDTH) /* count of mapping registers -- one for each logical page */
typedef enum { /* MEM map selector */
System_Map = 0, /* system map */
User_Map = 1, /* user map */
Port_A_Map = 2, /* port A map */
Port_B_Map = 3, /* port B map */
Current_Map, /* current map (system or user) */
Linear_Map /* linear access to all maps */
} MEU_MAP_SELECTOR;
#define TO_MAP_SELECTOR(i) ((MEU_MAP_SELECTOR) (i & MEU_MAP_MASK))
/* Memory definitions */
typedef enum {
From_Memory, /* copy from memory to a buffer */
To_Memory /* copy from a buffer to memory */
} COPY_DIRECTION;
/* Memory global data */
extern uint32 mem_size; /* size of memory in words */
extern uint32 mem_end; /* address of the first word beyond memory */
/* Memory Expansion Unit definitions */
typedef enum { /* MEM privileged instruction violation conditions */
Always,
If_User_Map
} MEU_CONDITION;
typedef enum { /* MEM operational state */
ME_Disabled,
ME_Enabled
} MEU_STATE;
/* Memory Expansion Unit global state */
extern char meu_indicator; /* last map access indicator (S | U | A | B | -) */
extern uint32 meu_page; /* last physical page number accessed */
/* Memory Protect global state */
extern HP_WORD mp_fence; /* memory protect fence register */
/* I/O subsystem global utility routine declarations */
extern t_bool io_control (uint32 select_code, IO_GROUP_OP micro_op);
extern SKPF_DATA io_dispatch (uint32 select_code, INBOUND_SET inbound_signals, HP_WORD inbound_value);
/* Main memory global utility routine declarations */
extern t_stat mem_initialize (uint32 memory_size);
extern HP_WORD mem_read (DEVICE *dptr, ACCESS_CLASS classification, HP_WORD address);
extern void mem_write (DEVICE *dptr, ACCESS_CLASS classification, HP_WORD address, HP_WORD value);
extern uint8 mem_read_byte (DEVICE *dptr, ACCESS_CLASS classification, HP_WORD byte_address);
extern void mem_write_byte (DEVICE *dptr, ACCESS_CLASS classification, HP_WORD byte_address, uint8 value);
extern HP_WORD mem_fast_read (HP_WORD address, MEU_MAP_SELECTOR map);
extern void mem_zero (uint32 starting_address, uint32 fill_count);
extern t_bool mem_is_empty (uint32 starting_address);
extern void mem_copy_loader (MEMORY_WORD *buffer, uint32 starting_address, COPY_DIRECTION mode);
extern t_bool mem_is_idle_loop (void);
extern void mem_trace_registers (FLIP_FLOP interrupt_system);
/* Main memory global utility routines declared in io.h
extern HP_WORD mem_examine (uint32 address);
extern void mem_deposit (uint32 address, HP_WORD value);
*/
/* Memory Expansion Unit global utility routine declarations */
extern void meu_configure (MEU_STATE configuration);
extern HP_WORD meu_read_map (MEU_MAP_SELECTOR map, uint32 index);
extern void meu_write_map (MEU_MAP_SELECTOR map, uint32 index, uint32 value);
extern void meu_set_fence (HP_WORD new_fence);
extern void meu_set_state (MEU_STATE operation, MEU_MAP_SELECTOR map);
extern HP_WORD meu_update_status (void);
extern HP_WORD meu_update_violation (void);
extern void meu_assert_IAK (void);
extern void meu_privileged (MEU_CONDITION condition);
extern uint32 meu_breakpoint_type (t_bool is_iak);
extern uint32 meu_map_address (HP_WORD logical, int32 switches);
/* Memory Protect global utility routine declarations */
extern t_bool mp_initialize (void);
extern void mp_configure (t_bool is_enabled, t_bool is_optional);
extern void mp_check_jmp (HP_WORD address, uint32 lower_bound);
extern void mp_check_jsb (HP_WORD address);
extern void mp_check_io (uint32 select_code, IO_GROUP_OP micro_op);
extern void mp_violation (void);
extern void mp_disable (void);
extern t_bool mp_is_on (void);
extern t_bool mp_reenable_interrupts (void);
extern t_bool mp_trace_violation (void);

417
HP2100/hp2100_cpu_fp.c Normal file
View File

@@ -0,0 +1,417 @@
/* hp2100_cpu_fp.c: HP 2100 firmware floating point instructions
Copyright (c) 2002-2015, Robert M. Supnik
Copyright (c) 2017-2018, J. David Bryan
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the names of the authors shall not be
used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from the authors.
30-Sep-18 JDB Replaced explicit complements/increments with NEG16 and NEG32
28-Jul-18 JDB Renamed source file from hp2100_fp1.c
26-Jun-17 JDB Replaced SEXT with SEXT16
03-Jan-15 JDB Made the utility routines static
21-Jan-08 JDB Corrected fp_unpack mantissa high-word return
(from Mark Pizzolato)
01-Dec-06 JDB Reworked FFP helpers for 1000-F support, deleted f_pwr2
22-Jul-05 RMS Fixed compiler warning in Solaris (from Doug Glyn)
25-Feb-05 JDB Added FFP helpers f_pack, f_unpack, f_pwr2
11-Feb-05 JDB Fixed missing negative overflow renorm in StoreFP
26-Dec-04 RMS Separated A/B from M[0/1] for DMA IO (from Dave Bryan)
15-Jul-03 RMS Fixed signed/unsigned warning
21-Oct-02 RMS Recoded for compatibility with 21MX microcode algorithms
The HP2100 uses a unique binary floating point format:
15 14 0
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|S | fraction high | : A
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| fraction low | exponent |XS| : A + 1
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
15 8 7 1 0
where S = 0 for plus fraction, 1 for minus fraction
fraction = s.bbbbb..., 24 binary digits
exponent = 2**+/-n
XS = 0 for plus exponent, 1 for minus exponent
Numbers can be normalized or unnormalized but are always normalized
when loaded.
Unpacked floating point numbers are stored in structure ufp
exp = exponent, 2's complement
h'l = fraction, 2's comp, left justified
This routine tries to reproduce the algorithms of the 2100/21MX
microcode in order to achieve 'bug-for-bug' compatibility. In
particular,
- The FIX code produces various results in B.
- The fraction multiply code uses 16b x 16b multiplies to produce
a 31b result. It always loses the low order bit of the product.
- The fraction divide code is an approximation that may produce
an error of 1 LSB.
- Signs are tracked implicitly as part of the fraction. Unnormalized
inputs may cause the packup code to produce the wrong sign.
- "Unclean" zeros (zero fraction, non-zero exponent) are processed
like normal operands.
Implementation notes:
1. The 2100/1000-M/E Fast FORTRAN Processor (FFP) and 1000 F-Series Floating
Point Processor (FPP) simulations require that the host compiler support
64-bit integers and the HAVE_INT64 symbol be defined during compilation.
If this symbol is defined, two-word floating-point operations are handled
in the FPP code, and this module is not used. If it is not defined, then
FFP and FPP operations are not available, and this module provides the
floating-point support.
*/
#include "hp2100_defs.h"
#include "hp2100_cpu.h"
#include "hp2100_cpu_fp.h"
#if !defined (HAVE_INT64) /* int64 support unavailable */
struct ufp { /* unpacked fp */
int32 exp; /* exp */
uint32 fr; /* frac */
};
#define FP_V_SIGN 31 /* sign */
#define FP_M_SIGN 01
#define FP_V_FR 8 /* fraction */
#define FP_M_FR 077777777
#define FP_V_EXP 1 /* exponent */
#define FP_M_EXP 0177
#define FP_V_EXPS 0 /* exp sign */
#define FP_M_EXPS 01
#define FP_SIGN (FP_M_SIGN << FP_V_SIGN)
#define FP_FR (FP_M_FR << FP_V_FR)
#define FP_EXP (FP_M_EXP << FP_V_EXP)
#define FP_EXPS (FP_M_EXPS << FP_V_EXPS)
#define FP_GETSIGN(x) (((x) >> FP_V_SIGN) & FP_M_SIGN)
#define FP_GETEXP(x) (((x) >> FP_V_EXP) & FP_M_EXP)
#define FP_GETEXPS(x) (((x) >> FP_V_EXPS) & FP_M_EXPS)
#define FP_NORM (1 << (FP_V_SIGN - 1)) /* normalized */
#define FP_LOW (1 << FP_V_FR)
#define FP_RNDP (1 << (FP_V_FR - 1)) /* round for plus */
#define FP_RNDM (FP_RNDP - 1) /* round for minus */
#define FPAB ((((uint32) AR) << 16) | ((uint32) BR))
/* Fraction shift; 0 < shift < 32 */
#define FR_ARS(v,s) (((v) >> (s)) | (((v) & FP_SIGN)? \
(((uint32) D32_MASK) << (32 - (s))): 0)) & D32_MASK
#define FR_NEG(v) ((~(v) + 1) & D32_MASK)
/* Utility routines */
static uint32 UnpackFP (struct ufp *fop, uint32 opnd);
static void NormFP (struct ufp *fop);
static uint32 PackFP (struct ufp *fop);
static uint32 StoreFP (struct ufp *fop);
/* Floating to integer conversion */
uint32 f_fix (void)
{
struct ufp fop;
uint32 res = 0;
UnpackFP (&fop, FPAB); /* unpack op */
if (fop.exp < 0) { /* exp < 0? */
AR = 0; /* result = 0 */
return 0; /* B unchanged */
}
if (fop.exp > 15) { /* exp > 15? */
BR = AR; /* B has high bits */
AR = 077777; /* result = 77777 */
return 1; /* overflow */
}
if (fop.exp < 15) { /* if not aligned */
res = FR_ARS (fop.fr, 15 - fop.exp); /* shift right */
AR = UPPER_WORD (res); /* AR gets result */
}
BR = AR;
if ((AR & D16_SIGN) && ((fop.fr | res) & D16_MASK)) /* any low bits lost? */
AR = AR + 1 & R_MASK; /* round up */
return 0;
}
/* Integer to floating conversion */
uint32 f_flt (void)
{
struct ufp res = { 15, 0 }; /* +, 2**15 */
res.fr = ((uint32) AR) << 16; /* left justify */
StoreFP (&res); /* store result */
return 0; /* clr overflow */
}
/* Floating point add/subtract */
uint32 f_as (uint32 opnd, t_bool sub)
{
struct ufp fop1, fop2, t;
int32 ediff;
UnpackFP (&fop1, FPAB); /* unpack A-B */
UnpackFP (&fop2, opnd); /* get op */
if (sub) { /* subtract? */
fop2.fr = FR_NEG (fop2.fr); /* negate frac */
if (fop2.fr == ((uint32) FP_SIGN)) { /* -1/2? */
fop2.fr = fop2.fr >> 1; /* special case */
fop2.exp = fop2.exp + 1;
}
}
if (fop1.fr == 0) fop1 = fop2; /* op1 = 0? res = op2 */
else if (fop2.fr != 0) { /* op2 = 0? no add */
if (fop1.exp < fop2.exp) { /* |op1| < |op2|? */
t = fop2; /* swap operands */
fop2 = fop1;
fop1 = t;
}
ediff = fop1.exp - fop2.exp; /* get exp diff */
if (ediff <= 24) {
if (ediff) fop2.fr = FR_ARS (fop2.fr, ediff); /* denorm, signed */
if ((fop1.fr ^ fop2.fr) & FP_SIGN) /* unlike signs? */
fop1.fr = fop1.fr + fop2.fr; /* eff subtract */
else { /* like signs */
fop1.fr = fop1.fr + fop2.fr; /* eff add */
if (fop2.fr & FP_SIGN) { /* both -? */
if ((fop1.fr & FP_SIGN) == 0) { /* overflow? */
fop1.fr = FP_SIGN | (fop1.fr >> 1); /* renormalize */
fop1.exp = fop1.exp + 1; /* incr exp */
}
}
else if (fop1.fr & FP_SIGN) { /* both +, cry out? */
fop1.fr = fop1.fr >> 1; /* renormalize */
fop1.exp = fop1.exp + 1; /* incr exp */
}
} /* end else like */
} /* end if ediff */
} /* end if fop2 */
return StoreFP (&fop1); /* store result */
}
/* Floating point multiply - passes diagnostic */
uint32 f_mul (uint32 opnd)
{
struct ufp fop1, fop2;
struct ufp res = { 0, 0 };
int32 shi1, shi2, t1, t2, t3, t4, t5;
UnpackFP (&fop1, FPAB); /* unpack A-B */
UnpackFP (&fop2, opnd); /* unpack op */
if (fop1.fr && fop2.fr) { /* if both != 0 */
res.exp = fop1.exp + fop2.exp + 1; /* exp = sum */
shi1 = SEXT16 (UPPER_WORD (fop1.fr)); /* mpy hi */
shi2 = SEXT16 (UPPER_WORD (fop2.fr)); /* mpc hi */
t1 = shi2 * ((int32) ((fop1.fr >> 1) & 077600)); /* mpc hi * (mpy lo/2) */
t2 = shi1 * ((int32) ((fop2.fr >> 1) & 077600)); /* mpc lo * (mpy hi/2) */
t3 = t1 + t2; /* cross product */
t4 = (shi1 * shi2) & ~1; /* mpy hi * mpc hi */
t5 = SEXT16 (UPPER_WORD (t3)) << 1; /* add in cross */
res.fr = (t4 + t5) & D32_MASK; /* bit<0> is lost */
}
return StoreFP (&res); /* store */
}
/* Floating point divide - reverse engineered from diagnostic */
static uint32 divx (uint32 ba, uint32 dvr, uint32 *rem)
{
int32 sdvd = 0, sdvr = 0;
uint32 q, r;
if (ba & FP_SIGN) sdvd = 1; /* 32b/16b signed dvd */
if (dvr & D16_SIGN) sdvr = 1; /* use old-fashioned */
if (sdvd) ba = NEG32 (ba); /* unsigned divides, */
if (sdvr) dvr = NEG16 (dvr); /* as results may ovflo */
q = ba / dvr;
r = ba % dvr;
if (sdvd ^ sdvr) q = NEG16 (q);
if (sdvd) r = NEG16 (r);
if (rem) *rem = r;
return q;
}
uint32 f_div (uint32 opnd)
{
struct ufp fop1, fop2;
struct ufp quo = { 0, 0 };
uint32 ba, q0, q1, q2, dvrh;
UnpackFP (&fop1, FPAB); /* unpack A-B */
UnpackFP (&fop2, opnd); /* unpack op */
dvrh = UPPER_WORD (fop2.fr); /* high divisor */
if (dvrh == 0) { /* div by zero? */
AR = 0077777; /* return most pos */
BR = 0177776;
return 1;
}
if (fop1.fr) { /* dvd != 0? */
quo.exp = fop1.exp - fop2.exp + 1; /* exp = diff */
ba = FR_ARS (fop1.fr, 2); /* prevent ovflo */
q0 = divx (ba, dvrh, &ba); /* Q0 = dvd / dvrh */
ba = (ba & ~1) << 16; /* remainder */
ba = FR_ARS (ba, 1); /* prevent ovflo */
q1 = divx (ba, dvrh, NULL); /* Q1 = rem / dvrh */
ba = (fop2.fr & 0xFF00) << 13; /* dvrl / 8 */
q2 = divx (ba, dvrh, NULL); /* dvrl / dvrh */
ba = - SEXT16 (LOWER_WORD (q2)) * SEXT16 (LOWER_WORD (q0)); /* -Q0 * Q2 */
ba = (ba >> 16) & 0xFFFF; /* save ms half */
if (q1 & D16_SIGN) quo.fr = quo.fr - 0x00010000; /* Q1 < 0? -1 */
if (ba & D16_SIGN) quo.fr = quo.fr - 0x00010000; /* -Q0*Q2 < 0? */
quo.fr = quo.fr + ((ba << 2) & 0xFFFF) + q1; /* rest prod, add Q1 */
quo.fr = quo.fr << 1; /* shift result */
quo.fr = quo.fr + (q0 << 16); /* add Q0 */
} /* end if fop1.h */
return StoreFP (&quo); /* store result */
}
/* Utility routines */
/* Unpack operand */
static uint32 UnpackFP (struct ufp *fop, uint32 opnd)
{
fop->fr = opnd & FP_FR; /* get frac */
fop->exp = FP_GETEXP (opnd); /* get exp */
if (FP_GETEXPS (opnd)) fop->exp = fop->exp | ~FP_M_EXP; /* < 0? sext */
return FP_GETSIGN (opnd); /* return sign */
}
/* Normalize unpacked floating point number */
static void NormFP (struct ufp *fop)
{
if (fop->fr) { /* any fraction? */
uint32 test = (fop->fr >> 1) & FP_NORM;
while ((fop->fr & FP_NORM) == test) { /* until norm */
fop->exp = fop->exp - 1;
fop->fr = (fop->fr << 1);
}
}
else fop->exp = 0; /* clean 0 */
return;
}
/* Pack fp number */
static uint32 PackFP (struct ufp *fop)
{
return (fop->fr & FP_FR) | /* merge frac */
((fop->exp & FP_M_EXP) << FP_V_EXP) | /* and exp */
((fop->exp < 0)? (1 << FP_V_EXPS): 0); /* add exp sign */
}
/* Round fp number, store, generate overflow */
static uint32 StoreFP (struct ufp *fop)
{
uint32 sign, svfr, hi, ov = 0;
NormFP (fop); /* normalize */
svfr = fop->fr; /* save fraction */
sign = FP_GETSIGN (fop->fr); /* save sign */
fop->fr = (fop->fr + (sign? FP_RNDM: FP_RNDP)) & FP_FR; /* round */
if ((fop->fr ^ svfr) & FP_SIGN) { /* sign change? */
fop->fr = fop->fr >> 1; /* renormalize */
fop->exp = fop->exp + 1;
}
else NormFP (fop); /* check for norm */
if (fop->fr == 0) hi = 0; /* result 0? */
else if (fop->exp < -(FP_M_EXP + 1)) { /* underflow? */
hi = 0; /* store clean 0 */
ov = 1;
}
else if (fop->exp > FP_M_EXP) { /* overflow? */
hi = 0x7FFFFFFE; /* all 1's */
ov = 1;
}
else hi = PackFP (fop); /* pack mant and exp */
AR = UPPER_WORD (hi);
BR = LOWER_WORD (hi);
return ov;
}
/* Single-precision Fast FORTRAN Processor helpers */
/* Pack mantissa and exponent and return fp value */
uint32 fp_pack (OP *result, OP mantissa, int32 exponent, OPSIZE precision)
{
struct ufp fop;
uint32 val;
fop.fr = ((uint32) mantissa.fpk[0] << 16) | mantissa.fpk[1];
fop.exp = exponent;
val = PackFP (&fop);
result->fpk[0] = UPPER_WORD (val);
result->fpk[1] = LOWER_WORD (val);
return 0;
}
/* Normalize, round, and pack mantissa and exponent and return fp value */
uint32 fp_nrpack (OP *result, OP mantissa, int32 exponent, OPSIZE precision)
{
struct ufp fop;
uint32 ovf;
fop.fr = ((uint32) mantissa.fpk[0] << 16) | mantissa.fpk[1];
fop.exp = exponent;
ovf = StoreFP (&fop);
result->fpk[0] = AR;
result->fpk[1] = BR;
return ovf;
}
/* Unpack fp number in into mantissa and exponent */
uint32 fp_unpack (OP *mantissa, int32 *exponent, OP packed, OPSIZE precision)
{
struct ufp fop;
uint32 operand;
operand = ((uint32) packed.fpk[0] << 16) | packed.fpk[1];
UnpackFP (&fop, operand);
mantissa->fpk[0] = UPPER_WORD (fop.fr);
mantissa->fpk[1] = LOWER_WORD (fop.fr);
*exponent = fop.exp;
return 0;
}
#endif /* int64 support unavailable */

98
HP2100/hp2100_cpu_fp.h Normal file
View File

@@ -0,0 +1,98 @@
/* hp2100_cpu_fp.h: HP 2100 floating point declarations
Copyright (c) 2002-2013, Robert M. Supnik
Copyright (c) 2005-2018, J. David Bryan
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the names of the authors shall not be
used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from the authors.
02-Oct-18 JDB Moved ZERO definition from hp2100_cpu5.c
28-Jul-18 JDB Combined hp2100_fp.h and hp2100_fp1.h
15-Feb-17 JDB Deleted unneeded guard macro definition
24-Dec-14 JDB Changed fp_ucom return from uint32 to uint16
14-Mar-13 MP Changed guard macro name to avoid reserved namespace
01-Dec-06 JDB Reworked FFP helpers for 1000-F support, deleted f_pwr2
16-Oct-06 JDB Generalized FP calling sequences for F-Series
12-Oct-06 JDB Altered x_trun for F-Series FFP compatibility
26-Sep-06 JDB Moved from hp2100_fp.c to simplify extensions
This file contains declarations used by the CPU to interface to the firmware
and hardware floating-point operations in the 2100 and 1000 computers..
Implementation notes:
1. The 2100/1000-M/E Fast FORTRAN Processor (FFP) and 1000 F-Series Floating
Point Processor (FPP) simulations require that the host compiler supports
64-bit integers and that the HAVE_INT64 symbol is defined during
compilation. If this symbol is defined, two-word floating-point
operations are handled in the FPP code. If it is not defined, then FFP
and FPP operations are not available, and floating-point support is
limited to the firmware implementation of the six basic FP instructions
(add, subtract, multiply, divide, fix, and float)..
*/
#if defined (HAVE_INT64) /* int64 support is available */
/* Special operands */
#define ACCUM NULL /* result not returned */
#define NOP ZERO /* operand not needed */
static const OP ZERO = { { 0, 0, 0, 0, 0 } }; /* zero operand */
/* Floating-Point Processor routines */
extern void fp_prec (uint16 opcode, OPSIZE *operand_l, OPSIZE *operand_r, OPSIZE *result);
extern uint32 fp_exec (uint16 opcode, OP *result, OP operand_l, OP operand_r);
extern OP fp_accum (const OP *operand, OPSIZE precision);
extern uint16 fp_ucom (OP *mantissa, OPSIZE precision);
extern uint32 fp_pcom (OP *packed, OPSIZE precision);
extern uint32 fp_trun (OP *result, OP source, OPSIZE precision);
extern uint32 fp_cvt (OP *result, OPSIZE source_precision, OPSIZE dest_precision);
#else /* int64 support is not available */
/* Firmware floating-point routines */
extern uint32 f_as (uint32 op, t_bool sub); /* FAD/FSB */
extern uint32 f_mul (uint32 op); /* FMP */
extern uint32 f_div (uint32 op); /* FDV */
extern uint32 f_fix (void); /* FIX */
extern uint32 f_flt (void); /* FLT */
#endif /* int64 conditional */
/* Fast FORTRAN Processor helpers */
extern uint32 fp_pack (OP *result, OP mantissa, int32 exponent, OPSIZE precision);
extern uint32 fp_nrpack (OP *result, OP mantissa, int32 exponent, OPSIZE precision);
extern uint32 fp_unpack (OP *mantissa, int32 *exponent, OP packed, OPSIZE precision);

1471
HP2100/hp2100_cpu_fpp.c Normal file

File diff suppressed because it is too large Load Diff

944
HP2100/hp2100_defs.h Normal file
View File

@@ -0,0 +1,944 @@
/* hp2100_defs.h: HP 2100 simulator architectural declarations
Copyright (c) 1993-2016, Robert M. Supnik
Copyright (c) 2017-2019, J. David Bryan
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the names of the authors shall not be
used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from the authors.
08-Dec-19 JDB Added "hp_reset_poll" routine declaration
28-Mar-19 JDB Added extensions
18-Mar-19 JDB Added include for SCP extensions
20-Sep-18 JDB Moved "hp_device_conflict" code into "initialize_io" in cpu.c
25-Jul-18 JDB Added CPU configuration declarations
29-Jun-18 JDB Changed "sync_poll" to "hp_sync_poll"
14-Jun-18 JDB Renamed PRO to MPPE
05-Jun-18 JDB Revised I/O model
02-May-18 JDB Added "SIRDEV" for first device to receive the SIR signal
16-Oct-17 JDB Suppressed logical-not-parentheses warning on clang
30-Aug-17 JDB Replaced POLL_WAIT with POLL_PERIOD
07-Aug-17 JDB Added "hp_attach"
20-Jul-17 JDB Removed STOP_OFFLINE, STOP_PWROFF stop codes
11-Jul-17 JDB Moved "ibl_copy" to hp2100_cpu.h
26-Jun-17 JDB Moved I/O instruction subopcode constants to hp2100_cpu.c
14-Jun-17 JDB Renamed STOP_RSRV to STOP_UNIMPL (unimplemented instruction)
15-Mar-17 JDB Added global trace flags
27-Feb-17 JDB ibl_copy no longer returns a status code
15-Feb-17 JDB Deleted unneeded guard macro definition
16-Jan-17 JDB Added tracing and console output macros
13-Jan-17 JDB Added fprint_cpu
10-Jan-17 JDB Added architectural constants
05-Aug-16 JDB Removed PC_Global renaming; P register is now "PR"
13-May-16 JDB Modified for revised SCP API function parameter types
19-Jun-15 JDB Conditionally use PC_Global for PC for version 4.0 and on
30-Dec-14 JDB Added S-register parameters to ibl_copy, more IBL constants
28-Dec-14 JDB Changed suppression from #pragma GCC to #pragma clang
05-Feb-13 JDB Added declaration for hp_fprint_stopped
18-Mar-13 JDB Added "-Wdangling-else" to the suppression pragmas
Removed redundant extern declarations
14-Mar-13 MP Changed guard macro name to avoid reserved namespace
14-Dec-12 JDB Added "-Wbitwise-op-parentheses" to the suppression pragmas
12-May-12 JDB Added pragmas to suppress logical operator precedence warnings
10-Feb-12 JDB Added hp_setsc, hp_showsc functions to support SC modifier
28-Mar-11 JDB Tidied up signal handling
29-Oct-10 JDB DMA channels renamed from 0,1 to 1,2 to match documentation
27-Oct-10 JDB Revised I/O signal enum values for concurrent signals
Revised I/O macros for new signal handling
09-Oct-10 JDB Added DA and DC device select code assignments
07-Sep-08 JDB Added POLL_FIRST to indicate immediate connection attempt
15-Jul-08 JDB Rearranged declarations with hp2100_cpu.h
26-Jun-08 JDB Rewrote device I/O to model backplane signals
25-Jun-08 JDB Added PIF device
17-Jun-08 JDB Declared fmt_char() function
26-May-08 JDB Added MPX device
24-Apr-08 JDB Added I_MRG_I, I_JSB, I_JSB_I, and I_JMP instruction masks
14-Apr-08 JDB Changed TMR_MUX to TMR_POLL for idle support
Added POLLMODE, sync_poll() declaration
Added I_MRG, I_ISZ, I_IOG, I_STF, and I_SFS instruction masks
07-Dec-07 JDB Added BACI device
10-Nov-07 JDB Added 16/32-bit unsigned-to-signed conversions
11-Jan-07 JDB Added 12578A DMA byte packing to DMA structure
28-Dec-06 JDB Added CRS backplane signal as I/O pseudo-opcode
Added DMASK32 32-bit mask value
21-Dec-06 JDB Changed MEM_ADDR_OK for 21xx loader support
12-Sep-06 JDB Define NOTE_IOG to recalc interrupts after instr exec
Rename STOP_INDINT to NOTE_INDINT (not a stop condition)
30-Dec-04 JDB Added IBL_DS_HEAD head number mask
19-Nov-04 JDB Added STOP_OFFLINE, STOP_PWROFF stop codes
25-Apr-04 RMS Added additional IBL definitions
Added DMA EDT I/O pseudo-opcode
25-Apr-03 RMS Revised for extended file support
24-Oct-02 RMS Added indirect address interrupt
08-Feb-02 RMS Added DMS definitions
01-Feb-02 RMS Added terminal multiplexor support
16-Jan-02 RMS Added additional device support
30-Nov-01 RMS Added extended SET/SHOW support
15-Oct-00 RMS Added dynamic device numbers
14-Apr-99 RMS Changed t_addr to unsigned
The [original] author gratefully acknowledges the help of Jeff Moffat in
answering questions about the HP2100, and of Dave Bryan in adding features
and correcting errors throughout the simulator.
This file provides the general declarations used throughout the HP 2100
simulator. It is required by all modules.
-----------------------------------------------------
Implementation Note -- Compiling the Simulator as C++
-----------------------------------------------------
This simulator is written in ISO-standard C, specifically to the ISO/IEC
9899:1999 standard. While it might be desirable to compile the simulator
with a C++ compiler to obtain more careful type checking, the simulator would
have to be be written in the subset of C that is also valid C++. Using valid
C features beyond that subset, as the HP 3000 simulator does, will produce
C++ compiler errors.
The standard C features used by the simulator that prevent error-free C++
compilation are:
1. Incomplete types.
In C, mutually recursive type definitions are allowed by the use of
incomplete type declarations, such as "DEVICE ms_dev;" followed later by
"DEVICE ms_dev {...};". Several HP device simulators use this feature to
place a pointer to the device structure in the "desc" field of an MTAB
array element, typically when the associated validation or display
routine handles multiple devices. As the DEVICE contains a pointer to
the MTAB array, and an MTAB array element contains a pointer to the
DEVICE, the definitions are mutually recursive, and incomplete types are
employed. C++ does not permit incomplete types.
2. Implicit conversion of ints to enums.
In C, enumeration types are compatible with integer types, and its
members are constants having type "int". As such, they are semantically
equivalent to and may be used interchangeably with integers. For the
developer, though, C enumerations have some advantages. In particular,
the compiler may check a "switch" statement to ensure that all of the
enumeration cases are covered. Also, a mathematical set may be modeled
by an enumeration type with disjoint enumerator values, with the bitwise
integer OR and AND operators modeling the set union and intersection
operations. The latter has direct support in the "gdb" debugger, which
will display an enumerated type value as a union of the various
enumerators. The HP simulator makes extensive use of both features to
model hardware signal buses (e.g., INBOUND_SET, OUTBOUND_SET) and so
performs bitwise integer operations on the enumerations to model signal
assertion and denial. In C++, implicit conversion from enumerations to
integers is allowed, but conversion from integers to enumerations is
illegal without explicit casts. Therefore, the idiom employed by the
simulator to assert a signal (e.g., "outbound_signals |= ioIRQ") is
rejected by the C++ compiler.
3. Implicit increment operations on enums.
Because enums are compatible with integers in C, no special enumerator
increment operator is provided. To cycle through the range of an
enumeration type, e.g. in a "for" statement, the standard integer
increment operator, "++", is used. In C++, the "++" operator must be
overloaded with a version specific to the enumeration type; applying the
integer "++" to an enumeration is illegal.
4. Use of C++ keywords as variable names.
C++ reserves a number of additional keywords beyond those reserved by C.
Use of any of these keywords as a variable or type name is legal C but
illegal C++. The HP simulator uses variables named "class" and
"operator", which are keywords in C++.
The HP simulator will compile cleanly with a compiler implementing the 1999 C
standard. Compilation as C++ is not a goal of the simulator and cannot work,
given the incompatibilities listed above.
*/
#include "sim_rev.h"
#include "sim_defs.h"
#include "sim_extension.h"
/* The following pragmas quell clang and Microsoft Visual C++ warnings that are
on by default but should not be, in my opinion. They warn about the use of
perfectly valid code and require the addition of redundant parentheses and
braces to silence them. Rather than clutter up the code with scores of extra
symbols that, in my view, make the code harder to read and maintain, I elect
to suppress these warnings.
VC++ 2008 warning descriptions:
- 4114: "same type qualifier used more than once" [legal per C99]
- 4554: "check operator precedence for possible error; use parentheses to
clarify precedence"
- 4996: "function was declared deprecated"
*/
#if defined (__clang__)
#pragma clang diagnostic ignored "-Wlogical-not-parentheses"
#pragma clang diagnostic ignored "-Wlogical-op-parentheses"
#pragma clang diagnostic ignored "-Wbitwise-op-parentheses"
#pragma clang diagnostic ignored "-Wshift-op-parentheses"
#pragma clang diagnostic ignored "-Wdangling-else"
#elif defined (_MSC_VER)
#pragma warning (disable: 4114 4554 4996)
#endif
/* Device register display mode flags */
#define REG_X REG_VMIO /* permit symbolic display overrides */
#define REG_A (1u << REG_V_UF + 0) /* default format is -A (one ASCII character) */
#define REG_C (1u << REG_V_UF + 1) /* default format is -C (two ASCII characters) */
#define REG_M (1u << REG_V_UF + 2) /* default format is -M (mnemonic) */
/* Global tracing flags.
Global tracing flags are allocated in ascending order, as they may be used
by modules that allocate their own private flags in descending order. No
check is made for overlapping values.
*/
#define TRACE_CMD (1u << 0) /* trace interface or controller commands */
#define TRACE_INCO (1u << 1) /* trace interface or controller command initiations and completions */
#define TRACE_CSRW (1u << 2) /* trace interface control, status, read, and write actions */
#define TRACE_STATE (1u << 3) /* trace state changes */
#define TRACE_SERV (1u << 4) /* trace unit service scheduling calls and entries */
#define TRACE_PSERV (1u << 5) /* trace periodic unit service scheduling calls and entries */
#define TRACE_XFER (1u << 6) /* trace data receptions and transmissions */
#define TRACE_IOBUS (1u << 7) /* trace I/O bus signals and data words received and returned */
#define DEB_CMDS (1u << 8) /* (old) trace command initiations and completions */
#define DEB_CPU (1u << 9) /* (old) trace words received from and sent to the CPU */
#define DEB_BUF (1u << 10) /* (old) trace data read from and written to the FIFO */
#define DEB_XFER (1u << 11) /* (old) trace data receptions and transmissions */
#define DEB_RWS (1u << 12) /* (old) trace tape reads, writes, and status returns */
#define DEB_RWSC (1u << 13) /* (old) trace disc read/write/status/control commands */
#define DEB_SERV (1u << 14) /* (old) trace unit service scheduling calls and entries */
/* Tracing and console output.
"tprintf" is used to write tracing messages. It does an "fprintf" to the
debug output stream if the stream is open and the trace "flag" is currently
enabled in device "dev". Otherwise, it's a NOP. "..." is the format string
and associated values.
"tpprintf" is identical to "tprintf", except that a device pointer is passed
instead of a device structure.
"TRACING" and "TRACINGP" implement the test conditions for device and device
pointer tracing, respectively. They are used explicitly only when several
trace statements employing the same flag are required, and it is desirable to
avoid repeating the stream and flag test for each one.
"cprintf", "cputs", and "cputc" are used to write messages to the console
and, if console logging is enabled, to the log output stream. They do
"(f)printf", "fputs", or "(f)putc", respectively. "..." is the format string
and associated values, "str" is the string to write, and "ch" is the
character to write.
Implementation notes:
1. The "cputs" macro uses "fputs" for both console and log file output
because "puts" appends a newline, whereas "fputs" does not.
*/
#define TRACING(d,f) (sim_deb != NULL && ((d).dctrl & (f)))
#define TRACINGP(d,f) (sim_deb != NULL && ((d)->dctrl & (f)))
#define tprintf(dev, flag, ...) \
if (TRACING (dev, flag)) \
hp_trace (&(dev), (flag), __VA_ARGS__); \
else \
(void) 0
#define tpprintf(dptr, flag, ...) \
if (TRACINGP (dptr, flag)) \
hp_trace ((dptr), (flag), __VA_ARGS__); \
else \
(void) 0
#define cprintf(...) \
do { \
printf (__VA_ARGS__); \
if (sim_log) \
fprintf (sim_log, __VA_ARGS__); \
} \
while (0)
#define cputs(str) \
do { \
fputs (str, stdout); \
if (sim_log) \
fputs (str, sim_log); \
} \
while (0)
#define cputc(ch) \
do { \
putc (ch); \
if (sim_log) \
fputc (ch, sim_log); \
} \
while (0)
/* Simulation stop and notification codes.
The STOP_* status codes stop the simulator. The "sim_stop_messages" array in
"hp2100_sys.c" contains the message strings that correspond one-for-one with
the stop codes.
The NOTE_* status codes do not stop the simulator. Instead, they inform the
instruction execution loop of special situations that occurred while
processing the current machine instruction..
Implementation notes:
1. Codes before STOP_RERUN cause the instruction to be rerun, so P is backed
up twice. For codes after, P points to the next instruction to be
executed (which is the current instruction for an infinite loop stop).
*/
#define STOP_UNIMPL 1 /* unimplemented instruction stop */
#define STOP_UNSC 2 /* stop on I/O to an unassigned select code */
#define STOP_UNDEF 3 /* undefined instruction stop */
#define STOP_INDIR 4 /* stop on an indirect loop */
#define STOP_RERUN 4 /* stops above here cause the instruction to be re-run */
#define STOP_HALT 5 /* programmed halt */
#define STOP_BRKPNT 6 /* breakpoint */
#define STOP_NOCONN 7 /* no connection */
#define STOP_NOTAPE 8 /* no tape */
#define STOP_EOT 9 /* end of tape */
#define NOTE_INDINT 10 /* an interrupt occurred while resolving an indirect address */
/* Modifier validation identifiers */
#define MTAB_XDV (MTAB_XTD | MTAB_VDV)
#define MTAB_XUN (MTAB_XTD | MTAB_VUN)
/* I/O event timing.
I/O events are scheduled for future service by specifying the desired delay
in units of event ticks. Typically, one event tick represents the execution
of one CPU instruction, and this is the way event ticks are defined in the
current simulator implementation. However, the various CPUs themselves not
only vary in speed, but the actual instruction times vary greatly, due to the
presence of block move, compare, and scan instructions. Variations of an
order of magnitude are common, and two orders or more are possible for longer
blocks.
The 24296-90001 Diagnostic Configurator provides a one millisecond timer for
use by the diagnostic programs. The timer is a two-instruction software
loop, plus four instructions of entry/exit overhead, based on the processor
type. The values provided are:
Loop Instr
CPU Count /msec
------ ----- -----
2114 246 496
2115 246 496
2116 309 622
2100 252 508
1000-M 203 410
1000-E 1573 * 1577
* The E-Series TIMER instruction is used instead of a software loop. TIMER
re-executes an internal decrement until the supplied value reaches zero.
To pass diagnostics that time peripheral operations, the simulator assumes
the E-Series execution rate for all devices (0.634 microseconds per event
tick), although this results in needlessly long delays for normal operation.
A correct implementation would change the timing base depending on the
currently selected CPU.
To accommodate possible future variable instruction timing, I/O service
activation times must not assume a constant 0.634 microseconds per event
tick. Delays should be defined in terms of the "uS" (microseconds), "mS"
(milliseconds), and "S" (seconds) macros below.
*/
#define USEC_PER_EVENT 0.634 /* average CPU instruction time in microseconds */
#define uS(t) (uint32) ((t) > USEC_PER_EVENT ? (t) / USEC_PER_EVENT + 0.5 : 1)
#define mS(t) (uint32) (((t) * 1000.0) / USEC_PER_EVENT + 0.5)
#define S(t) (uint32) (((t) * 1000000.0) / USEC_PER_EVENT + 0.5)
/* Architectural data constants.
These macros specify the width, sign location, value mask, and minimum and
maximum signed and unsigned values for the data sizes supported by the
simulator. In addition, masks for 16-bit and 32-bit overflow are defined (an
overflow is indicated if the masked bits are not all ones or all zeros).
The HP_WORD type is used to declare variables that represent 16-bit registers
or buses in hardware.
Implementation notes:
1. The HP_WORD type is a 32-bit unsigned type, instead of the more logical
16-bit unsigned type. There are two reasons for this. First, SCP
requires that scalars referenced by REG (register) entries be 32 bits in
size. Second, IA-32 processors execute instructions with 32-bit operands
much faster than those with 16-bit operands.
Using 16-bit operands omits the masking required for 32-bit values. For
example, the code generated by gcc for the following operations is as
follows:
uint16 a, b, c;
a = b + c & 0xFFFF;
movzwl _b, %eax
addw _c, %ax
movw %ax, _a
uint32 x, y, z;
x = y + z & 0xFFFF;
movl _z, %eax
addl _y, %eax
andl $65535, %eax
movl %eax, _x
However, the first case uses operand override prefixes, which require
substantially more time to decode (6 clock cycles vs. 1 clock cycle).
This time outweighs the additional 32-bit AND instruction, which executes
in 1 clock cycle.
*/
typedef uint32 HP_WORD; /* HP 16-bit data word representation */
#define D4_WIDTH 4 /* 4-bit data bit width */
#define D4_MASK 0017u /* 4-bit data mask */
#define D8_WIDTH 8 /* 8-bit data bit width */
#define D8_MASK 0377u /* 8-bit data mask */
#define D8_UMAX 0377u /* 8-bit unsigned maximum value */
#define D8_SMAX 0177u /* 8-bit signed maximum value */
#define D8_SMIN 0200u /* 8-bit signed minimum value */
#define D8_SIGN 0200u /* 8-bit sign */
#define D16_WIDTH 16 /* 16-bit data bit width */
#define D16_MASK 0177777u /* 16-bit data mask */
#define D16_UMAX 0177777u /* 16-bit unsigned maximum value */
#define D16_SMAX 0077777u /* 16-bit signed maximum value */
#define D16_SMIN 0100000u /* 16-bit signed minimum value */
#define D16_SIGN 0100000u /* 16-bit sign */
#define D32_WIDTH 32 /* 32-bit data bit width */
#define D32_MASK 037777777777u /* 32-bit data mask */
#define D32_UMAX 037777777777u /* 32-bit unsigned maximum value */
#define D32_SMAX 017777777777u /* 32-bit signed maximum value */
#define D32_SMIN 020000000000u /* 32-bit signed minimum value */
#define D32_SIGN 020000000000u /* 32-bit sign */
#define D48_WIDTH 48 /* 48-bit data bit width */
#define D48_MASK 07777777777777777uL /* 48-bit data mask */
#define D48_UMAX 07777777777777777uL /* 48-bit unsigned maximum value */
#define D48_SMAX 03777777777777777uL /* 48-bit signed maximum value */
#define D48_SMIN 04000000000000000uL /* 48-bit signed minimum value */
#define D48_SIGN 04000000000000000uL /* 48-bit sign */
#define D64_WIDTH 64 /* 64-bit data bit width */
#define D64_MASK 01777777777777777777777uL /* 64-bit data mask */
#define D64_UMAX 01777777777777777777777uL /* 64-bit unsigned maximum value */
#define D64_SMAX 00777777777777777777777uL /* 64-bit signed maximum value */
#define D64_SMIN 01000000000000000000000uL /* 64-bit signed minimum value */
#define D64_SIGN 01000000000000000000000uL /* 64-bit sign */
#define S16_OVFL_MASK ((uint32) D16_UMAX << D16_WIDTH | \
D16_SIGN) /* 16-bit signed overflow mask */
#define S32_OVFL_MASK ((t_uint64) D32_UMAX << D32_WIDTH | \
D32_SIGN) /* 32-bit signed overflow mask */
#define LSB 1u /* least-significant bit */
#define D16_SIGN_LSB (D16_SIGN | LSB) /* bit 15 and bit 0 */
#define R_MASK D16_MASK /* 16-bit register mask */
/* Architectural memory constants.
These macros specify the width, data mask, and maximum unsigned values for
the implementation of logical and physical addresses.
HP 21xx and 1000 CPUs address a maximum of 32K words with 15-bit addresses.
This is the logical address space. A logical address contains a 5-bit page
number, designating one of 32 1K-word pages, and a 10-bit offset into that
page. 1000-series machines may employ an optional Memory Expansion Module to
map the logical address space anywhere with a 1M-word physical memory on a
per-page basis. The MEM translates a logical address to a physical address
by mapping the 5-bit logical page number to a 10-bit physical page number
while retaining the logical page offset. Physical addresses therefore
support 1024 pages of 1024 words each.
The logical address form is:
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| - | page number | page offset |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
...where bit 15 is not used (it is reserved for an indirect address
indicator).
The physical address form is:
19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| page number | page offset |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
The MEMORY_WORD type is used to declare variables that represent 16-bit main
memory locations in hardware.
Implementation notes:
1. The MEMORY_WORD type is a 16-bit unsigned type, corresponding with the
16-bit main memory in the HP 21xx/1000. Unlike the general data type,
which is a 32-bit type for speed, main memory does not benefit from the
faster 32-bit execution on IA-32 processors, as only one instruction in
the "mem_read" and "mem_write" routines has an operand override that
invokes the slower instruction fetch path. There is a negligible
difference in the Memory Pattern Test diagnostic execution speeds for the
uint32 vs. uint16 definition, whereas the VM requirements are doubled for
the former.
*/
typedef uint16 MEMORY_WORD; /* HP 16-bit memory word representation */
#define OF_WIDTH 10 /* offset bit width */
#define OF_MASK ((1u << OF_WIDTH) - 1) /* offset mask (2 ** 10 - 1) */
#define OF_MAX ((1u << OF_WIDTH) - 1) /* offset maximum (2 ** 10 - 1) */
#define LP_WIDTH 5 /* logical page bit width */
#define LP_MASK ((1u << LP_WIDTH) - 1) /* logical page mask (2 ** 5 - 1) */
#define LP_MAX ((1u << LP_WIDTH) - 1) /* logical page maximum (2 ** 5 - 1) */
#define PP_WIDTH 10 /* physical page bit width */
#define PP_MASK ((1u << PP_WIDTH) - 1) /* physical page mask (2 ** 10 - 1) */
#define PP_MAX ((1u << PP_WIDTH) - 1) /* physical page maximum (2 ** 10 - 1) */
#define LA_WIDTH (LP_WIDTH + OF_WIDTH) /* logical address bit width */
#define LA_MASK ((1u << LA_WIDTH) - 1) /* logical address mask (2 ** 15 - 1) */
#define LA_MAX ((1u << LA_WIDTH) - 1) /* logical address maximum (2 ** 15 - 1) */
#define PA_WIDTH (PP_WIDTH + OF_WIDTH) /* physical address bit width */
#define PA_MASK ((1u << PA_WIDTH) - 1) /* physical address mask (2 ** 20 - 1) */
#define PA_MAX ((1u << PA_WIDTH) - 1) /* physical address maximum (2 ** 20 - 1) */
#define DV_WIDTH 16 /* data value bit width */
#define DV_MASK ((1u << DV_WIDTH) - 1) /* data value mask (2 ** 16 - 1) */
#define DV_SIGN ( 1u << (DV_WIDTH - 1)) /* data value sign (2 ** 15) */
#define DV_UMAX ((1u << DV_WIDTH) - 1) /* data value unsigned maximum (2 ** 16 - 1) */
#define DV_SMAX ((1u << (DV_WIDTH - 1)) - 1) /* data value signed maximum (2 ** 15 - 1) */
/* Memory address macros.
These macros convert between logical and physical addresses. The functions
provided are:
- P_PAGE -- extract the page number part of a physical address
- PAGE -- extract the page number part of a logical address
- OFFSET -- extract the offset part of a logical or physical address
- TO_PA -- merge a page number and offset into a physical address
*/
#define P_PAGE(p) ((p) >> OF_WIDTH & PP_MASK)
#define PAGE(p) ((p) >> OF_WIDTH & LP_MASK)
#define OFFSET(p) ((p) & OF_MASK)
#define TO_PA(p,o) (((uint32) (p) & PP_MASK) << PP_WIDTH | (uint32) (o) & OF_MASK)
/* Memory access classifications.
The access classification determines the DMS map set and protections to use
when reading or writing memory words. The classification also is used to
label each data access when tracing is enabled. When DMS is disabled, or
when the CPU is one of the 21xx models, the classification is irrelevant.
*/
typedef enum {
Fetch, /* instruction fetch, current map */
Data, /* data access, current map */
Data_Alternate, /* data access, alternate map */
Data_System, /* data access, system map */
Data_User, /* data access, user map */
DMA_Channel_1, /* DMA channel 1, port A map */
DMA_Channel_2 /* DMA channel 2, port B map */
} ACCESS_CLASS;
/* Portable conversions.
SIMH is written with the assumption that the defined-size types (e.g.,
uint16) are at least the required number of bits but may be larger.
Conversions that otherwise would make inherent size assumptions must instead
be coded explicitly. For example, doing:
negative_value_32 = (int32) negative_value_16;
...will not guarantee that the upper 16 bits of "negative_value_32" are all
ones, whereas the supplied sign-extension macro will.
The conversions available are:
- SEXT8 -- signed 8-bit value sign-extended to int32
- SEXT16 -- signed 16-bit value sign-extended to int32
- NEG8 -- signed 8-bit value negated
- NEG16 -- signed 16-bit value negated
- NEG32 -- signed 32-bit value negated
- INT16 -- uint16 to int16
- INT32 -- uint32 to int32
Implementation notes:
1. The SEXTn and INTn routines assume that their values are masked to
exactly n bits before invoking.
*/
#define SEXT8(x) ((int32) ((x) & D8_SIGN ? (x) | ~D8_MASK : (x)))
#define SEXT16(x) ((int32) ((x) & D16_SIGN ? (x) | ~D16_MASK : (x)))
#define NEG8(x) ((~(x) + 1) & D8_MASK)
#define NEG16(x) ((~(x) + 1) & D16_MASK)
#define NEG32(x) ((~(x) + 1) & D32_MASK)
#define INT16(u) ((u) > D16_SMAX ? (-(int16) (D16_UMAX - (u)) - 1) : (int16) (u))
#define INT32(u) ((u) > D32_SMAX ? (-(int32) (D32_UMAX - (u)) - 1) : (int32) (u))
/* Byte accessors.
These macros extract the upper and lower bytes from a word and form a word
from upper and lower bytes. Replacement of a byte within a word is also
provided, as is an enumeration type that defines byte selection.
The accessors are:
- UPPER_BYTE -- return the byte from the upper position of a word value
- LOWER_BYTE -- return the byte from the lower position of a word value
- TO_WORD -- return a word with the specified upper and lower bytes
- REPLACE_UPPER -- replace the upper byte of the word value
- REPLACE_LOWER -- replace the lower byte of the word value
*/
typedef enum {
upper, /* upper byte selected */
lower /* lower byte selected */
} BYTE_SELECTOR;
#define UPPER_BYTE(w) (uint8) ((w) >> D8_WIDTH & D8_MASK)
#define LOWER_BYTE(w) (uint8) ((w) & D8_MASK)
#define TO_WORD(u,l) (HP_WORD) (((u) & D8_MASK) << D8_WIDTH | (l) & D8_MASK)
#define REPLACE_UPPER(w,b) ((w) & D8_MASK | ((b) & D8_MASK) << D8_WIDTH)
#define REPLACE_LOWER(w,b) ((w) & D8_MASK << D8_WIDTH | (b) & D8_MASK)
/* Double-word accessors */
#define UPPER_WORD(d) (HP_WORD) ((d) >> D16_WIDTH & D16_MASK)
#define LOWER_WORD(d) (HP_WORD) ((d) & D16_MASK)
#define TO_DWORD(u,l) ((uint32) (u) << D16_WIDTH | (l))
/* CPU instruction symbolic source.
The memory-reference group (MRG) instructions do not specify full logical
addresses of their targets. Instead, they specify offsets from either the
base page or the current page. Instructions specifying base-page offsets are
always displayed with target addresses between 0000-1777. The display and
parsing of instructions specifying current-page offsets depends on the source
of the instructions.
If the current-page CPU instruction is contained in main memory, the current
page is taken from the address of the word containing the instruction, and
the full target address between 00000-77777 is displayed or parsed. However,
if the instruction is contained in a device buffer, e.g., a disc drive sector
buffer, the destination memory address is unknown until the instruction is
transferred to memory. In this case, the target address is displayed or
parsed as the offset prefixed with the letter "C" (e.g., "LDA C 1200"). In
order to present the proper symbolic behavior, the mnemonic formatter and
parser must know the source of the request.
Additionally, display requests from the EXAMINE command have preloaded a
value array with the maximum number of words required to encode the longest
instruction. This is inefficient, as only a fraction of the instruction set
requires more than one word. For EXAMINE commands entered by the user at the
SCP prompt, this is unimportant. However, for calls from the CPU instruction
trace routine, the overhead is significant. In the latter case, the array is
loaded only with a single word, and the mnemonic formatter loads additional
words if the specific instruction to be displayed requires them.
*/
typedef enum {
Device_Symbol, /* called for an EXAMINE <device> or DEPOSIT <device> command */
CPU_Symbol, /* called for an EXAMINE CPU or DEPOSIT CPU command */
CPU_Trace /* called for a CPU trace command */
} SYMBOL_SOURCE;
/* Calibrated timer numbers */
#define TMR_TBG 0 /* the time base generator timer */
#define TMR_POLL 1 /* the input polling timer */
#define POLL_RATE 100 /* poll 100 times per second (unless synchronized) */
#define POLL_PERIOD mS (10) /* poll period is 10 milliseconds */
#define POLL_FIRST 1 /* first poll is "immediate" */
typedef enum { /* poll synchronization modes */
INITIAL, /* initial synchronization call */
SERVICE /* event service synchronization call */
} POLLMODE;
/* Flip-flops */
typedef enum { /* flip-flop values */
CLEAR = 0, /* the flip-flop is clear */
SET = 1 /* the flip-flop is set */
} FLIP_FLOP;
/* Bitset formatting.
See the comments at the "fmt_bitset" function (hp2100_sys.c) for details of
the specification of bitset names and format structures.
*/
typedef enum { /* direction of interpretation */
msb_first, /* left-to-right */
lsb_first /* right-to-left */
} BITSET_DIRECTION;
typedef enum { /* alternate names */
no_alt, /* no alternates are present in the name array */
has_alt /* the name array contains alternates */
} BITSET_ALTERNATE;
typedef enum { /* trailing separator */
no_bar, /* omit a trailing separator */
append_bar /* append a trailing separator */
} BITSET_BAR;
typedef const char * const BITSET_NAME; /* a bit name string pointer */
typedef struct { /* bit set format descriptor */
uint32 name_count; /* count of bit names */
BITSET_NAME *names; /* pointer to an array of bit names */
uint32 offset; /* offset from LSB to first bit */
BITSET_DIRECTION direction; /* direction of interpretation */
BITSET_ALTERNATE alternate; /* alternate interpretations presence */
BITSET_BAR bar; /* trailing separator choice */
} BITSET_FORMAT;
/* Bitset format specifier initialization */
#define FMT_INIT(names,offset,dir,alt,bar) \
sizeof (names) / sizeof (names) [0], \
(names), (offset), (dir), (alt), (bar)
/* CPU configuration.
The CPU exports a "cpu_configuration" word that indicates the current
CPU model and firmware option configuration. It is used by the
symbolic examine and deposit routines and instruction tracing to
determine whether the firmware implementing a given opcode is
present. It is a copy of the CPU unit option flags with the encoded
CPU model decoded into individual model flag bits. This allows a
simple (and fast) AND operation with a firmware feature word to
determine applicability, saving the multiple masks and comparisons
that would otherwise be required.
Additionally, the configuration word has the unit CPU model bits set
on permanently to permit a base-set feature test for those CPUs that
have no options currently enabled (at least one non-option bit must
be on for the test to succeed, and the model bits are not otherwise
used).
The 32-bit encoding is:
31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| - | - | - | - | - | - | - | - | - | - | - | - | - | - | G | V |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| O | E | D | F | M | I | P | U | B | g | f | e | d | c | b | a |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
Options:
G = SIGNAL/1000 firmware is present
V = Vector Instruction Set firmware is present
O = RTE-6/VM VMA and OS firmware is present
E = RTE-IV EMA firmware is present
D = Double Integer firmware is present
F = Fast FORTRAN Processor firmware is present
M = Dynamic Mapping System firmware is present
I = 2000 I/O Processor firmware is present
P = Floating Point hardware or firmware is present
U = Extended Arithmetic Unit is present
CPU Models:
a = HP 2116
b = HP 2115
c = HP 2114
d = HP 2100
e = HP 1000 M-Series
f = HP 1000 E-Series
g = HP 1000 F-Series
*/
typedef enum { /* CPU option identifiers */
Option_2116, /* model 2116 CPU */
Option_2115, /* model 2115 CPU */
Option_2114, /* model 2114 CPU */
Option_2100, /* model 2100 CPU */
Option_1000_M, /* model 1000 M-Series CPU */
Option_1000_E, /* model 1000 E-Series CPU */
Option_1000_F, /* model 1000 F-Series CPU */
Option_BASE, /* Base Set firmware */
Option_EAU, /* Extended Arithmetic Unit hardware or firmware */
Option_FP, /* Floating Point hardware or firmware */
Option_IOP, /* 2000 I/O Processor firmware */
Option_DMS, /* Dynamic Mapping System firmware */
Option_FFP, /* Fast FORTRAN Processor firmware */
Option_DBI, /* Double Integer firmware */
Option_EMA, /* RTE-IV EMA firmware */
Option_VMAOS, /* RTE-6/VM VMA and OS firmware */
Option_VIS, /* Vector Instruction Set firmware */
Option_SIGNAL, /* SIGNAL/1000 firmware */
Option_DS /* Distributed Systems firmware */
} OPTION_ID;
typedef enum { /* CPU options currently installed */
CPU_2116 = (1u << Option_2116), /* model 2116 CPU */
CPU_2115 = (1u << Option_2115), /* model 2115 CPU */
CPU_2114 = (1u << Option_2114), /* model 2114 CPU */
CPU_2100 = (1u << Option_2100), /* model 2100 CPU */
CPU_1000_M = (1u << Option_1000_M), /* model 1000 M-Series CPU */
CPU_1000_E = (1u << Option_1000_E), /* model 1000 E-Series CPU */
CPU_1000_F = (1u << Option_1000_F), /* model 1000 F-Series CPU */
CPU_BASE = (1u << Option_BASE), /* Base Set firmware */
CPU_EAU = (1u << Option_EAU), /* Extended Arithmetic Unit hardware or firmware */
CPU_FP = (1u << Option_FP), /* Floating Point hardware or firmware */
CPU_IOP = (1u << Option_IOP), /* 2000 I/O Processor firmware */
CPU_DMS = (1u << Option_DMS), /* Dynamic Mapping System firmware */
CPU_FFP = (1u << Option_FFP), /* Fast FORTRAN Processor firmware */
CPU_DBI = (1u << Option_DBI), /* Double Integer firmware */
CPU_EMA = (1u << Option_EMA), /* RTE-IV EMA firmware */
CPU_VMAOS = (1u << Option_VMAOS), /* RTE-6/VM VMA and OS firmware */
CPU_VIS = (1u << Option_VIS), /* Vector Instruction Set firmware */
CPU_SIGNAL = (1u << Option_SIGNAL), /* SIGNAL/1000 firmware */
CPU_DS = (1u << Option_DS) /* Distributed Systems firmware */
} CPU_OPTION;
#define CPU_OPTION_SET CPU_OPTION /* a set of CPU_OPTIONs */
#define CPU_OPTION_SHIFT (Option_BASE + 1)
#define CPU_211X (CPU_2116 | CPU_2115 | CPU_2114)
#define CPU_21XX (CPU_2116 | CPU_2115 | CPU_2114 | CPU_2100)
#define CPU_1000_E_F (CPU_1000_E | CPU_1000_F)
#define CPU_1000 (CPU_1000_M | CPU_1000_E | CPU_1000_F)
#define CPU_ALL (CPU_211X | CPU_2100 | CPU_1000)
#define CPU_MODEL_MASK CPU_ALL /* a mask for just the CPU model bits */
#define CPU_OPTION_MASK (~CPU_ALL) /* a mask for just the option bits */
/* CPU global state */
extern CPU_OPTION_SET cpu_configuration; /* the current CPU option set and model */
/* System interface global data structures */
extern const HP_WORD odd_parity [256]; /* a table of parity bits for odd parity */
/* System interface global SCP support routines declared in scp.h
extern t_stat sim_load (FILE *fptr, char *cptr, char *fnam, int flag);
extern t_stat fprint_sym (FILE *ofile, t_addr addr, t_value *val, UNIT *uptr, int32 sw);
extern t_stat parse_sym (char *cptr, t_addr addr, UNIT *uptr, t_value *val, int32 sw);
*/
/* System interface global SCP support routine declarations */
extern t_stat hp_attach (UNIT *uptr, char *cptr);
extern t_stat hp_set_dib (UNIT *uptr, int32 count, char *cptr, void *desc);
extern t_stat hp_show_dib (FILE *st, UNIT *uptr, int32 count, void *desc);
/* System interface global utility routine declarations */
extern t_stat fprint_cpu (FILE *ofile, t_addr addr, t_value *val, uint32 radix, SYMBOL_SOURCE source);
extern const char *fmt_char (uint32 charval);
extern const char *fmt_bitset (uint32 bitset, const BITSET_FORMAT bitfmt);
extern void hp_initialize_trace (uint32 device_max, uint32 flag_max);
extern void hp_trace (DEVICE *dptr, uint32 flag, ...);
extern void hp_enbdis_pair (DEVICE *ccptr, DEVICE *dcptr);
extern void hp_reset_poll (void);
extern int32 hp_sync_poll (POLLMODE poll_mode);

1880
HP2100/hp2100_di.c Normal file

File diff suppressed because it is too large Load Diff

262
HP2100/hp2100_di.h Normal file
View File

@@ -0,0 +1,262 @@
/* hp2100_di.h: HP 12821A HP-IB Disc Interface simulator declarations
Copyright (c) 2010-2018, J. David Bryan
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the name of the author shall not be
used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from the author.
DI 12821A Disc Interface
11-Jul-18 JDB Revised I/O model
15-Mar-17 JDB Trace flags are now global
09-Mar-17 JDB Changed the DIAG option to DIAGNOSTIC
10-Jan-17 JDB Moved byte accessors to hp2100_defs.h
13-May-16 JDB Modified for revised SCP API function parameter types
14-Feb-12 JDB First release
16-Nov-10 JDB Created DI common definitions file
This file defines the interface between HP-IB device simulators and the
12821A Disc Interface simulator. It must be included by the device-specific
modules (hp2100_di_da.c, etc.).
Implementation notes:
1. Three CARD_ID values are defined, corresponding to the Amigo disc (DA),
CS/80 disc (DC), and Amigo mag tape (MA) simulators. At first release,
only the DA device is implemented. However, as the 12821A diagnostic
requires two cards to test I/O fully, a dummy DC device is provided by
the DA simulator. It is enabled only when the DA card is configured for
diagnostic mode. This dummy device should be removed when either the DC
or MA device is implemented.
*/
/* Program constants */
#define FIFO_SIZE 16 /* FIFO depth */
typedef enum {
da, dc, ma, /* card IDs */
first_card = da, /* first card ID */
last_card = ma, /* last card ID */
card_count /* count of card IDs */
} CARD_ID;
/* Device flags and accessors (bits 7-0 are reserved for disc/tape flags) */
#define DEV_V_BUSADR (DEV_V_UF + 8) /* bits 10-8: interface HP-IB address */
#define DEV_V_DIAG (DEV_V_UF + 11) /* bit 11: diagnostic mode */
#define DEV_V_W1 (DEV_V_UF + 12) /* bit 12: DCPC pacing jumper */
#define DEV_M_BUSADR 07 /* bus address mask */
#define DEV_BUSADR (DEV_M_BUSADR << DEV_V_BUSADR)
#define DEV_DIAG (1 << DEV_V_DIAG)
#define DEV_W1 (1 << DEV_V_W1)
#define GET_DIADR(f) (((f) >> DEV_V_BUSADR) & DEV_M_BUSADR)
#define SET_DIADR(f) (((f) & DEV_M_BUSADR) << DEV_V_BUSADR)
/* Unit flags and accessors (bits 7-0 are reserved for disc/tape flags) */
#define UNIT_V_BUSADR (UNIT_V_UF + 8) /* bits 10-8: unit HP-IB address */
#define UNIT_M_BUSADR 07 /* bus address mask */
#define UNIT_BUSADR (UNIT_M_BUSADR << UNIT_V_BUSADR)
#define GET_BUSADR(f) (((f) >> UNIT_V_BUSADR) & UNIT_M_BUSADR)
#define SET_BUSADR(f) (((f) & UNIT_M_BUSADR) << UNIT_V_BUSADR)
/* HP-IB control line state bit flags.
NOTE that these flags align with the corresponding flags in the DI status
register, so don't change the numerical values!
*/
#define BUS_ATN 0001 /* attention */
#define BUS_EOI 0002 /* end or identify */
#define BUS_DAV 0004 /* data available */
#define BUS_NRFD 0010 /* not ready for data */
#define BUS_NDAC 0020 /* not data accepted */
#define BUS_REN 0040 /* remote enable */
#define BUS_IFC 0100 /* interface clear */
#define BUS_SRQ 0200 /* service request */
#define BUS_PPOLL (BUS_ATN | BUS_EOI) /* parallel poll */
/* HP-IB data */
#define BUS_ADDRESS 0037 /* bus address mask */
#define BUS_GROUP 0140 /* bus group mask */
#define BUS_COMMAND 0160 /* bus command type mask */
#define BUS_DATA 0177 /* bus data mask */
#define BUS_PARITY 0200 /* bus parity mask */
#define BUS_PCG 0000 /* primary command group */
#define BUS_LAG 0040 /* listen address group */
#define BUS_TAG 0100 /* talk address group */
#define BUS_SCG 0140 /* secondary command group */
#define BUS_UCG 0020 /* universal command group */
#define BUS_ACG 0000 /* addressed command group */
#define BUS_UNADDRESS 0037 /* unlisten and untalk addresses */
#define PPR(a) (uint8) (1 << (7 - (a))) /* parallel poll response */
/* Per-card state variables */
typedef struct {
FLIP_FLOP control; /* control flip-flop */
FLIP_FLOP flag; /* flag flip-flop */
FLIP_FLOP flag_buffer; /* flag buffer flip-flop */
FLIP_FLOP srq; /* SRQ flip-flop */
FLIP_FLOP edt; /* EDT flip-flop */
FLIP_FLOP eor; /* EOR flip-flop */
BYTE_SELECTOR ibp; /* input byte pointer selector */
BYTE_SELECTOR obp; /* output byte pointer selector */
uint16 cntl_register; /* control word register */
uint16 status_register; /* status word register */
uint16 input_data_register; /* input data register */
uint32 fifo [FIFO_SIZE]; /* FIFO buffer */
uint32 fifo_count; /* FIFO occupancy counter */
REG *fifo_reg; /* FIFO register pointer */
uint32 acceptors; /* unit bitmap of the bus acceptors */
uint32 listeners; /* unit bitmap of the bus listeners */
uint32 talker; /* unit bitmap of the bus talker */
uint8 bus_cntl; /* HP-IB bus control state (ATN, EOI, etc.) */
uint8 poll_response; /* address bitmap of parallel poll responses */
double ifc_timer; /* 100 microsecond IFC timer */
} DI_STATE;
/* Disc interface VM global register definitions.
These definitions should be included before any device-specific registers.
Implementation notes:
1. The TMR register is included to ensure that the IFC timer is saved by a
SAVE command. It is declared as a hidden, read-only byte array of a size
compatible with a double-precision floating-point value, as there is no
appropriate macro for the double type.
*/
#define DI_REGS(dev) \
{ ORDATA (CWR, di [dev].cntl_register, 16), REG_FIT }, \
{ ORDATA (SWR, di [dev].status_register, 16), REG_FIT }, \
{ ORDATA (IDR, di [dev].input_data_register, 16), REG_FIT }, \
\
{ DRDATA (FCNT, di [dev].fifo_count, 5) }, \
{ BRDATA (FIFO, di [dev].fifo, 8, 20, FIFO_SIZE), REG_CIRC }, \
\
{ GRDATA (ACPT, di [dev].acceptors, 2, 4, 0) }, \
{ GRDATA (LSTN, di [dev].listeners, 2, 4, 0) }, \
{ GRDATA (TALK, di [dev].talker, 2, 4, 0) }, \
{ GRDATA (PPR, di [dev].poll_response, 2, 8, 0), REG_FIT }, \
{ GRDATA (BUSCTL, di [dev].bus_cntl, 2, 8, 0), REG_FIT }, \
\
{ FLDATA (CTL, di [dev].control, 0) }, \
{ FLDATA (FLG, di [dev].flag, 0) }, \
{ FLDATA (FBF, di [dev].flag_buffer, 0) }, \
{ FLDATA (SRQ, di [dev].srq, 0) }, \
{ FLDATA (EDT, di [dev].edt, 0) }, \
{ FLDATA (EOR, di [dev].eor, 0) }, \
\
{ BRDATA (TMR, &di [dev].ifc_timer, 10, CHAR_BIT, sizeof (double)), REG_HRO }, \
\
{ ORDATA (SC, dev##_dib.select_code, 6), REG_HRO }
/* Disc interface VM global modifier definitions.
These definitions should be included before any device-specific modifiers.
*/
#define DI_MODS(dev,dib) \
{ MTAB_XTD | MTAB_VDV, 1, "ADDRESS", "ADDRESS", &di_set_address, &di_show_address, &dev }, \
\
{ MTAB_XTD | MTAB_VDV, 1, NULL, "DIAGNOSTIC", &di_set_cable, NULL, &dev }, \
{ MTAB_XTD | MTAB_VDV, 0, NULL, "HPIB", &di_set_cable, NULL, &dev }, \
{ MTAB_XTD | MTAB_VDV, 0, "CABLE", NULL, NULL, &di_show_cable, &dev }, \
\
{ MTAB_XTD | MTAB_VDV, 1, "SC", "SC", &hp_set_dib, &hp_show_dib, (void *) &dib }, \
{ MTAB_XTD | MTAB_VDV | MTAB_NMO, ~1u, "DEVNO", "DEVNO", &hp_set_dib, &hp_show_dib, (void *) &dib }, \
\
{ MTAB_XTD | MTAB_VUN, 0, "BUS", "BUS", &di_set_address, &di_show_address, &dev }
/* Disc interface global bus routine definitions */
typedef t_bool ACCEPTOR (uint32 unit, uint8 data);
typedef void RESPONDER (CARD_ID card, uint32 unit, uint8 new_cntl);
/* Disc interface global variables */
extern DI_STATE di [];
extern DEBTAB di_deb [];
/* Disc interface global VM routines */
extern INTERFACE di_interface;
extern t_stat di_reset (DEVICE *dptr);
/* Disc interface global SCP routines */
extern t_stat di_set_address (UNIT *uptr, int32 value, char *cptr, void *desc);
extern t_stat di_set_cable (UNIT *uptr, int32 value, char *cptr, void *desc);
extern t_stat di_show_address (FILE *st, UNIT *uptr, int32 value, void *desc);
extern t_stat di_show_cable (FILE *st, UNIT *uptr, int32 value, void *desc);
/* Disc interface global bus routines */
extern t_bool di_bus_source (CARD_ID card, uint8 data);
extern void di_bus_control (CARD_ID card, uint32 unit, uint8 assert, uint8 deny);
extern void di_poll_response (CARD_ID card, uint32 unit, FLIP_FLOP response);
/* Amigo disc global bus routines */
extern ACCEPTOR da_bus_accept;
extern RESPONDER da_bus_respond;
/* Amigo mag tape global bus routines */
extern ACCEPTOR ma_bus_accept;
extern RESPONDER ma_bus_respond;

2224
HP2100/hp2100_di_da.c Normal file

File diff suppressed because it is too large Load Diff

2412
HP2100/hp2100_disclib.c Normal file

File diff suppressed because it is too large Load Diff

387
HP2100/hp2100_disclib.h Normal file
View File

@@ -0,0 +1,387 @@
/* hp_disclib.h: HP MAC/ICD disc controller simulator library declarations
Copyright (c) 2011-2017, J. David Bryan
Copyright (c) 2004-2011, Robert M. Supnik
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the names of the authors shall not be
used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from the authors.
17-Jan-17 JDB Moved "hp2100_defs.h" inclusion to "hp2100_disclib.c"
13-May-16 JDB Modified for revised SCP API function parameter types
24-Oct-12 JDB Changed CNTLR_OPCODE to title case to avoid name clash
07-May-12 JDB Added end-of-track delay time as a controller variable
02-May-12 JDB First release
09-Nov-11 JDB Created disc controller common library from DS simulator
This file defines the interface between interface simulators and the
simulation library for the HP 13037 and 13365 disc controllers. It must be
included by the interface-specific modules (DA, DS, etc.).
*/
/* Program limits */
#define DL_MAXDRIVE 7 /* last valid drive number */
#define DL_MAXUNIT 10 /* last legal unit number */
#define DL_AUXUNITS 2 /* number of MAC auxiliary units required */
#define DL_WPSEC 128 /* words per normal sector */
#define DL_WPFSEC 138 /* words per full sector */
#define DL_BUFSIZE DL_WPFSEC /* required buffer size in words */
/* Default controller times */
#define DL_EOT_TIME 160 /* end-of-track delay time */
#define DL_SEEK_TIME 100 /* seek delay time (per cylinder) */
#define DL_SECTOR_TIME 27 /* intersector delay time */
#define DL_CMD_TIME 3 /* command start delay time */
#define DL_DATA_TIME 1 /* data transfer delay time */
#define DL_WAIT_TIME 2749200 /* command wait timeout (1.74 seconds) */
/* Common per-unit disc drive state variables */
#define CYL u3 /* current drive cylinder */
#define STAT u4 /* current drive status (Status 2) */
#define OP u5 /* current drive operation in process */
#define PHASE u6 /* current drive operation phase */
/* Unit flags and accessors */
#define UNIT_V_MODEL (UNIT_V_UF + 0) /* bits 1-0: model ID */
#define UNIT_V_WLK (UNIT_V_UF + 2) /* bits 2-2: write locked (protect switch) */
#define UNIT_V_UNLOAD (UNIT_V_UF + 3) /* bits 3-3: heads unloaded */
#define UNIT_V_FMT (UNIT_V_UF + 4) /* bits 4-4: format enabled */
#define UNIT_V_AUTO (UNIT_V_UF + 5) /* bits 5-5: autosize */
#define DL_V_UF (UNIT_V_UF + 6) /* first free unit flag bit */
#define UNIT_M_MODEL 03 /* model ID mask */
#define UNIT_MODEL (UNIT_M_MODEL << UNIT_V_MODEL)
#define UNIT_WLK (1 << UNIT_V_WLK)
#define UNIT_UNLOAD (1 << UNIT_V_UNLOAD)
#define UNIT_FMT (1 << UNIT_V_FMT)
#define UNIT_AUTO (1 << UNIT_V_AUTO)
#define UNIT_WPROT (UNIT_WLK | UNIT_RO) /* write protected if locked or read-only */
#define GET_MODEL(t) (((t) >> UNIT_V_MODEL) & UNIT_M_MODEL)
#define SET_MODEL(t) (((t) & UNIT_M_MODEL) << UNIT_V_MODEL)
/* Status-1 accessors */
#define DL_V_S1SPD 13 /* bits 15-13: S/P/D flags */
#define DL_V_S1STAT 8 /* bits 12- 8: controller status */
#define DL_V_S1UNIT 0 /* bits 3- 0: last unit number */
#define DL_M_S1UNIT 017 /* unit number mask */
#define GET_S1UNIT(v) (((v) >> DL_V_S1UNIT) & DL_M_S1UNIT)
#define SET_S1SPD(v) ((v) << DL_V_S1SPD)
#define SET_S1STAT(v) ((v) << DL_V_S1STAT)
#define SET_S1UNIT(v) ((v) << DL_V_S1UNIT)
/* Status-2 accessors (+ = kept in unit status, - = determined dynamically) */
#define DL_V_S2ERR 15 /* bits 15-15: (-) any error flag */
#define DL_V_S2DTYP 9 /* bits 12- 9: (-) drive type */
#define DL_V_S2ATN 7 /* bits 7- 7: (+) attention flag */
#define DL_V_S2RO 6 /* bits 6- 6: (-) read only flag */
#define DL_V_S2FMT 5 /* bits 5- 5: (-) format enabled flag */
#define DL_V_S2FAULT 4 /* bits 4- 4: (+) drive fault flag */
#define DL_V_S2FS 3 /* bits 3- 3: (+) first status flag */
#define DL_V_S2SC 2 /* bits 2- 2: (+) seek check flag */
#define DL_V_S2NR 1 /* bits 1- 1: (-) not ready flag */
#define DL_V_S2BUSY 0 /* bits 0- 1: (-) drive busy flag */
#define DL_S2ERR (1 << DL_V_S2ERR)
#define DL_S2DTYP (1 << DL_V_S2DTYP)
#define DL_S2ATN (1 << DL_V_S2ATN)
#define DL_S2RO (1 << DL_V_S2RO)
#define DL_S2FMT (1 << DL_V_S2FMT)
#define DL_S2FAULT (1 << DL_V_S2FAULT)
#define DL_S2FS (1 << DL_V_S2FS)
#define DL_S2SC (1 << DL_V_S2SC)
#define DL_S2NR (1 << DL_V_S2NR)
#define DL_S2BUSY (1 << DL_V_S2BUSY)
#define DL_S2STOPS (DL_S2FAULT | DL_S2SC | DL_S2NR) /* bits that stop drive access */
#define DL_S2ERRORS (DL_S2FAULT | DL_S2SC | DL_S2NR | DL_S2BUSY) /* bits that set S2ERR */
#define DL_S2CPS (DL_S2ATN | DL_S2FAULT | DL_S2FS | DL_S2SC) /* bits cleared by Controller Preset */
/* Drive properties.
The controller library supports four different disc drive models with these
properties:
Drive Model Drive Sectors Heads per Cylinders Megabytes
Model ID Type per Head Cylinder per Drive per Drive
----- ----- ----- -------- --------- --------- ---------
7905 0 2 48 3 411 15
7906 1 0 48 4 411 20
7920 2 1 48 5 823 50
7925 3 3 64 9 823 120
The Drive Type is reported by the controller in the second status word
(Status-2) returned by the Request Status command.
Model IDs are used in the unit flags to identify the unit's model. For the
autosizing feature to work, models must be assigned ascending IDs in order of
ascending drive sizes.
*/
#define D7905_MODEL 0
#define D7905_SECTS 48
#define D7905_HEADS 3
#define D7905_CYLS 411
#define D7905_TYPE (2 << DL_V_S2DTYP)
#define D7905_WORDS (D7905_SECTS * D7905_HEADS * D7905_CYLS * DL_WPSEC)
#define D7906_MODEL 1
#define D7906_SECTS 48
#define D7906_HEADS 4
#define D7906_CYLS 411
#define D7906_TYPE (0 << DL_V_S2DTYP)
#define D7906_WORDS (D7906_SECTS * D7906_HEADS * D7906_CYLS * DL_WPSEC)
#define D7920_MODEL 2
#define D7920_SECTS 48
#define D7920_HEADS 5
#define D7920_CYLS 823
#define D7920_TYPE (1 << DL_V_S2DTYP)
#define D7920_WORDS (D7920_SECTS * D7920_HEADS * D7920_CYLS * DL_WPSEC)
#define D7925_MODEL 3
#define D7925_SECTS 64
#define D7925_HEADS 9
#define D7925_CYLS 823
#define D7925_TYPE (3 << DL_V_S2DTYP)
#define D7925_WORDS (D7925_SECTS * D7925_HEADS * D7925_CYLS * DL_WPSEC)
#define MODEL_7905 SET_MODEL (D7905_MODEL)
#define MODEL_7906 SET_MODEL (D7906_MODEL)
#define MODEL_7920 SET_MODEL (D7920_MODEL)
#define MODEL_7925 SET_MODEL (D7925_MODEL)
/* Controller types */
typedef enum {
MAC = 0,
ICD,
last_type = ICD, /* last valid type */
type_count /* count of controller types */
} CNTLR_TYPE;
/* Controller opcodes */
typedef enum {
Cold_Load_Read = 000,
Recalibrate = 001,
Seek = 002,
Request_Status = 003,
Request_Sector_Address = 004,
Read = 005,
Read_Full_Sector = 006,
Verify = 007,
Write = 010,
Write_Full_Sector = 011,
Clear = 012,
Initialize = 013,
Address_Record = 014,
Request_Syndrome = 015,
Read_With_Offset = 016,
Set_File_Mask = 017,
Invalid_Opcode = 020,
Read_Without_Verify = 022,
Load_TIO_Register = 023,
Request_Disc_Address = 024,
End = 025,
Wakeup = 026,
Last_Opcode = Wakeup /* last valid opcode */
} CNTLR_OPCODE;
#define DL_OPCODE_MASK 037
/* Controller command phases */
typedef enum {
start_phase = 0,
data_phase,
end_phase,
last_phase = end_phase /* last valid phase */
} CNTLR_PHASE;
/* Controller status.
Not all status values are returned by the library. The values not currently
returned are:
- illegal_drive_type
- cylinder_miscompare
- head_sector_miscompare
- io_program_error
- sync_timeout
- correctable_data_error
- illegal_spare_access
- defective_track
- protected_track
*/
typedef enum {
normal_completion = 000,
illegal_opcode = 001,
unit_available = 002,
illegal_drive_type = 003,
cylinder_miscompare = 007,
uncorrectable_data_error = 010,
head_sector_miscompare = 011,
io_program_error = 012,
sync_timeout = 013,
end_of_cylinder = 014,
data_overrun = 016,
correctable_data_error = 017,
illegal_spare_access = 020,
defective_track = 021,
access_not_ready = 022,
status_2_error = 023,
protected_track = 026,
unit_unavailable = 027,
drive_attention = 037
} CNTLR_STATUS;
/* Controller execution states */
typedef enum {
cntlr_idle, /* idle */
cntlr_wait, /* command wait */
cntlr_busy /* busy */
} CNTLR_STATE;
/* Controller command classifications */
typedef enum {
class_invalid, /* invalid classification */
class_read, /* read classification */
class_write, /* write classification */
class_control, /* control classification */
class_status /* status classification */
} CNTLR_CLASS;
/* Controller clear types */
typedef enum {
hard_clear, /* power-on/preset hard clear */
soft_clear /* programmed soft clear */
} CNTLR_CLEAR;
/* Controller state variables */
typedef struct {
CNTLR_TYPE type; /* controller type */
CNTLR_STATE state; /* controller state */
CNTLR_OPCODE opcode; /* controller opcode */
CNTLR_STATUS status; /* controller status */
FLIP_FLOP eoc; /* end-of-cylinder flag */
FLIP_FLOP eod; /* end-of-data flag */
uint32 spd_unit; /* S/P/D flags and unit number */
uint32 file_mask; /* file mask */
uint32 retry; /* retry counter */
uint32 cylinder; /* cylinder address */
uint32 head; /* head address */
uint32 sector; /* sector address */
uint32 verify_count; /* count of sectors to verify */
uint32 poll_unit; /* last unit polled for attention */
uint16 *buffer; /* data buffer pointer */
uint32 index; /* data buffer current index */
uint32 length; /* data buffer valid length */
UNIT *aux; /* MAC auxiliary units (controller and timer) */
int32 eot_time; /* end-of-track read delay time */
int32 seek_time; /* per-cylinder seek delay time */
int32 sector_time; /* intersector delay time */
int32 cmd_time; /* command response time */
int32 data_time; /* data transfer response time */
int32 wait_time; /* command wait time */
} CNTLR_VARS;
typedef CNTLR_VARS *CVPTR; /* pointer to controller state variables */
/* Controller state variables initialization.
The parameters are:
ctype - type of the controller (CNTLR_TYPE)
bufptr - pointer to the data buffer
auxptr - pointer to the auxiliary units (MAC only; NULL for ICD)
*/
#define CNTLR_INIT(ctype,bufptr,auxptr) \
(ctype), cntlr_idle, End, normal_completion, \
CLEAR, CLEAR, \
0, 0, 0, 0, 0, 0, 0, 0, \
(bufptr), 0, 0, (auxptr), \
DL_EOT_TIME, DL_SEEK_TIME, DL_SECTOR_TIME, \
DL_CMD_TIME, DL_DATA_TIME, DL_WAIT_TIME
/* Disc library global controller routines */
extern t_bool dl_prepare_command (CVPTR cvptr, UNIT *units, uint32 unit_limit);
extern UNIT *dl_start_command (CVPTR cvptr, UNIT *units, uint32 unit_limit);
extern void dl_end_command (CVPTR cvptr, CNTLR_STATUS status);
extern t_bool dl_poll_drives (CVPTR cvptr, UNIT *units, uint32 unit_limit);
extern t_stat dl_service_drive (CVPTR cvptr, UNIT *uptr);
extern t_stat dl_service_controller (CVPTR cvptr, UNIT *uptr);
extern t_stat dl_service_timer (CVPTR cvptr, UNIT *uptr);
extern void dl_idle_controller (CVPTR cvptr);
extern t_stat dl_clear_controller (CVPTR cvptr, UNIT *uptr, CNTLR_CLEAR clear_type);
extern t_stat dl_load_unload (CVPTR cvptr, UNIT *uptr, t_bool load);
/* Disc library global utility routines */
extern CNTLR_CLASS dl_classify (CNTLR_VARS cntlr);
extern const char *dl_opcode_name (CNTLR_TYPE controller, CNTLR_OPCODE opcode);
extern const char *dl_phase_name (CNTLR_PHASE phase);
/* Disc library global VM routines */
extern t_stat dl_attach (CVPTR cvptr, UNIT *uptr, char *cptr);
extern t_stat dl_detach (CVPTR cvptr, UNIT *uptr);
extern t_stat dl_set_model (UNIT *uptr, int32 value, char *cptr, void *desc);

1063
HP2100/hp2100_dma.c Normal file

File diff suppressed because it is too large Load Diff

1789
HP2100/hp2100_dp.c Normal file

File diff suppressed because it is too large Load Diff

1361
HP2100/hp2100_dq.c Normal file

File diff suppressed because it is too large Load Diff

1054
HP2100/hp2100_dr.c Normal file

File diff suppressed because it is too large Load Diff

1684
HP2100/hp2100_ds.c Normal file

File diff suppressed because it is too large Load Diff

458
HP2100/hp2100_io.h Normal file
View File

@@ -0,0 +1,458 @@
/* hp2100_io.h: HP 2100 device-to-CPU interface declarations
Copyright (c) 2018, J. David Bryan
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the name of the author shall not be used
in advertising or otherwise to promote the sale, use or other dealings in
this Software without prior written authorization from the author.
04-Sep-18 JDB Added card_description and rom_description to the DIB
10-Jul-18 JDB Created
This file contains declarations used by I/O devices to interface with
the HP 21xx/1000 CPU. It is required by any module that uses Device
Information Blocks (DIBs), i.e., is addressed by an I/O select code.
*/
/* I/O devices - fixed select code assignments */
#define CPU 000 /* interrupt control */
#define OVF 001 /* overflow */
#define DMALT1 002 /* DMA 1 alternate */
#define DMALT2 003 /* DMA 2 alternate */
#define PWR 004 /* power fail */
#define MPPE 005 /* memory protect/parity error */
#define DMA1 006 /* DMA channel 1 */
#define DMA2 007 /* DMA channel 2 */
/* I/O devices - variable select code assignment defaults */
#define PTR 010 /* 12597A-002 paper tape reader */
#define TTY 011 /* 12531C teleprinter */
#define PTP 012 /* 12597A-005 paper tape punch */
#define TBG 013 /* 12539C time-base generator */
#define LPS 014 /* 12653A line printer */
#define LPT 015 /* 12845A line printer */
#define MTD 020 /* 12559A data */
#define MTC 021 /* 12559A control */
#define DPD 022 /* 12557A data */
#define DPC 023 /* 12557A control */
#define DQD 024 /* 12565A data */
#define DQC 025 /* 12565A control */
#define DRD 026 /* 12610A data */
#define DRC 027 /* 12610A control */
#define MSD 030 /* 13181A data */
#define MSC 031 /* 13181A control */
#define IPLI 032 /* 12566B link in */
#define IPLO 033 /* 12566B link out */
#define DS 034 /* 13037A control */
#define BACI 035 /* 12966A Buffered Async Comm Interface */
#define MPX 036 /* 12792A/B/C 8-channel multiplexer */
#define PIF 037 /* 12620A/12936A Privileged Interrupt Fence */
#define MUXL 040 /* 12920A lower data */
#define MUXU 041 /* 12920A upper data */
#define MUXC 042 /* 12920A control */
#define DI_DA 043 /* 12821A Disc Interface with Amigo disc devices */
#define DI_DC 044 /* 12821A Disc Interface with CS/80 disc and tape devices */
#define MC1 045 /* 12566B Microcircuit Interface */
#define MC2 046 /* 12566B Microcircuit Interface */
#define SC_OPT 002 /* start of optional devices */
#define SC_CRS 006 /* start of devices that receive CRS */
#define SC_VAR 010 /* start of variable assignments */
#define SC_MAX 077 /* the maximum select code */
#define SC_MASK 077u /* the mask for the select code */
#define SC_BASE 8 /* the radix for the select code */
/* I/O backplane signals.
The INBOUND_SIGNAL and OUTBOUND_SIGNAL declarations mirror the hardware
signals that are received and asserted, respectively, by the interfaces on
the I/O backplane. A set of one or more signals forms an INBOUND_SET or
OUTBOUND_SET that is sent to or returned from a device interface. Under
simulation, the CPU and DMA dispatch one INBOUND_SET to the target device
interface per I/O cycle. The interface returns an OUTBOUND_SET and a
data value combined into a SIGNALS_VALUE structure to the caller.
In addition, some signals must be asserted asynchronously, e.g., in response
to an event service call. The "io_assert" function, provided by the CPU
module, provides asynchronous assertion for the ENF, SIR, PON, POPIO,
CRS, and IAK signals.
In hardware, signals are assigned to one or more specific I/O T-periods, and
some signals are asserted concurrently. For example, a programmed STC sc,C
instruction asserts the STC and CLF signals together in period T4. Under
simulation, signals are ORed to form an I/O cycle; in this example, the
signal handler would receive an INBOUND_SET value of "ioSTC | ioCLF".
Hardware allows parallel action for concurrent signals. Under simulation, a
"concurrent" set of signals is processed sequentially by the signal handler
in order of ascending numerical value. Although assigned T-periods differ
between programmed I/O and DMA I/O cycles, a single processing order is used.
The order of execution generally follows the order of T-period assertion,
except that SIR is processed after all other signals that may affect the
interrupt request chain.
Implementation notes:
1. The enumerations describe signals. A set of signals normally would be
modeled as an unsigned integer, as a set may contain more than one
signal. However, we define a set as the enumeration, as the "gdb"
debugger has special provisions for an enumeration of discrete bit values
and will display the set in "ORed" form.
2. The null signal ("ioNONE") cannot be defined as an enumeration constant
because the C language has a single name space for all enumeration
constants, so separate "no inbound signals" and "no outbound signals"
identifiers would be required, and because including them would require
handlers for them in "switch" statements, which is undesirable.
Therefore, we define "ioNONE" as an explicit integer 0, so that it is
compatible with both enumerations.
3. The ioCLF signal must be processed after ioSFS/ioSFC to ensure that a
true skip test generates ioSKF before the flag is cleared, and after
ioIOI/ioIOO/ioSTC/ioCLC to meet the requirement that executing an
instruction having the H/C bit set is equivalent to executing the same
instruction with the H/C bit clear and then a CLF instruction.
4. The ioENF signal must be processed after ioSTF and ioPOPIO to ensure that
the flag buffer flip-flop has been set before it is used to set the flag
flip-flop.
5. The ioPRH signal must be processed after ioIEN. The latter sets an
internal "interrupts are enabled" flag that must be tested before
interrupts are asserted by PRH.
6. An I/O handler will receive ioCRS as a result of a CLC 0 instruction,
ioPOPIO and ioCRS as a result of a RESET command, and ioPON, ioPOPIO, and
ioCRS as a result of a RESET -P command.
7. An I/O handler will receive ioNONE when a HLT instruction is executed
that has the H/C bit clear (i.e., no CLF generated).
8. In hardware, the SIR signal is generated unconditionally every T5 period
to time the setting of the IRQ flip-flop. Under simulation, ioSIR
indicates that the I/O handler must set the PRL, IRQ, and SRQ signals as
required by the interface logic. ioSIR will be included in the I/O cycle
if any of the flip-flops affecting these signals are changed and the
interface supports interrupts or DMA transfers.
9. In hardware, the ENF signal is unconditionally generated every T2 period
to time the setting of the flag flip-flop and to reset the IRQ flip-flop.
If the flag buffer flip-flip is set, then flag will be set by ENF. If
the flag buffer is clear, ENF will not affect flag. Under simulation,
ioENF is sent to set the flag flip-flop after the flag buffer flip-flop
is set explicitly.
10. In hardware, the PON signal is asserted continuously while the CPU is
operating. Under simulation, ioPON is asserted only at simulator
initialization or when processing a RESET -P command.
11. To avoid polling each interface when IEN or PRH asserts from a prior
denied condition, interfaces return two conditional signals: cnIRQ and
cnPRL. These are identical in meaning to the ioIRQ and ioPRL signals,
except their assertions do not depend on IEN or PRH. To identify an
interrupting interface rapidly, the CPU keeps these conditional signals
in the "interrupt_request_set" and "priority_holdoff_set" arrays. These
bit vectors are examined when a higher-priority interface reasserts PRL
or when the interrupt system is reenabled.
12. An interface will assert cnVALID if the conditional PRL and IRQ were
determined. If cnVALID is not asserted by the interface, then the states
of the cnPRL and cnIRQ signals cannot be inferred from their presence or
absence in the oubound signal set. The cnVALID pseudo-signal is required
because although most interfaces determine the PRL and IRQ states in
response to an SIR assertion, not all do. In particular, the 12936A
Privileged Interrupt Fence determines PRL in response to an IOO signal.
*/
typedef enum { /* I/O T-Period Description */
/* ============== ============================= */
ioPON = 000000000001, /* T2 T3 T4 T5 T6 Power On Normal */
ioIOI = 000000000002, /* -- -- T4 T5 -- I/O Data Input (CPU) */
/* T2 T3 -- -- -- I/O Data Input (DMA) */
ioIOO = 000000000004, /* -- T3 T4 -- -- I/O Data Output */
ioSFS = 000000000010, /* -- T3 T4 T5 -- Skip if Flag is Set */
ioSFC = 000000000020, /* -- T3 T4 T5 -- Skip if Flag is Clear */
ioSTC = 000000000040, /* -- -- T4 -- -- Set Control flip-flop (CPU) */
/* -- T3 -- -- -- Set Control flip-flop (DMA) */
ioCLC = 000000000100, /* -- -- T4 -- -- Clear Control flip-flop (CPU) */
/* -- T3 T4 -- -- Clear Control flip-flop (DMA) */
ioSTF = 000000000200, /* -- T3 -- -- -- Set Flag flip-flop */
ioCLF = 000000000400, /* -- -- T4 -- -- Clear Flag flip-flop (CPU) */
/* -- T3 -- -- -- Clear Flag flip-flop (DMA) */
ioEDT = 000000001000, /* -- -- T4 -- -- End Data Transfer */
ioCRS = 000000002000, /* -- -- -- T5 -- Control Reset */
ioPOPIO = 000000004000, /* -- -- -- T5 -- Power-On Preset to I/O */
ioIAK = 000000010000, /* -- -- -- -- T6 Interrupt Acknowledge */
ioENF = 000000020000, /* T2 -- -- -- -- Enable Flag */
ioSIR = 000000040000, /* -- -- -- T5 -- Set Interrupt Request */
ioIEN = 000000100000, /* T2 T3 T4 T5 T6 Interrupt system Enable */
ioPRH = 000000200000 /* T2 T3 T4 T5 T6 Priority High */
} INBOUND_SIGNAL;
typedef INBOUND_SIGNAL INBOUND_SET; /* a set of INBOUND_SIGNALs */
typedef enum { /* I/O T-Period Description */
/* ============== ================= */
ioSKF = 000000000001, /* -- T3 T4 T5 -- skip on flag */
ioPRL = 000000000002, /* T2 T3 T4 T5 T6 priority low */
ioFLG = 000000000004, /* -- -- T4 T5 -- flag */
ioIRQ = 000000000010, /* -- -- T4 T5 -- interrupt request */
ioSRQ = 000000000020, /* T2 T3 T4 T5 T6 service request */
cnIRQ = 000000000040, /* conditional interrupt request */
cnPRL = 000000000100, /* conditional priority low */
cnVALID = 000000000200 /* conditional signals are valid */
} OUTBOUND_SIGNAL;
typedef OUTBOUND_SIGNAL OUTBOUND_SET; /* a set of OUTBOUND_SIGNALs */
#define ioNONE 0 /* a universal "no signals are asserted" value */
typedef struct { /* the I/O interface return structure */
OUTBOUND_SET signals; /* the outbound signal set */
HP_WORD value; /* the outbound value */
} SIGNALS_VALUE;
/* I/O backplane signal assertions */
typedef enum { /* assertions passed to the "io_assert" call */
ioa_ENF, /* Enable Flag */
ioa_SIR, /* Set Interrupt Request */
ioa_PON, /* Power On Normal */
ioa_POPIO, /* Power-On Preset to I/O */
ioa_CRS, /* Control Reset */
ioa_IAK /* Interrupt Acknowledge */
} IO_ASSERTION;
/* I/O macros.
The following macros are useful in device interface signal handlers and unit
service routines. The parameter definition symbols employed are:
P = a priority set value
S = an INBOUND_SET or OUTBOUND_SET value
L = an INBOUND_SIGNAL value
A priority set is an unsigned value, where each bit represents an assertion
of some nature (e.g., I/O signals, interrupt requests, etc.), and the
position of the bit represents its priority, which decreases from LSB to MSB.
The IOPRIORITY macro isolates the highest-priority bit from the set. It does
this by ANDing the value with its two's complement; only the lowest-order bit
will differ. For example (bits are numbered here from the LSB):
priority set : ...0 0 1 1 0 1 0 0 0 0 0 0 (bits 6, 8, and 9 are asserted)
one's compl : ...1 1 0 0 1 0 1 1 1 1 1 1
two's compl : ...1 1 0 0 1 1 0 0 0 0 0 0
ANDed value : ...0 0 0 0 0 1 0 0 0 0 0 0 (bit 6 is highest priority)
If the request set indicates requests by 0 bits, rather than 1 bits, the
IOPRIORITY macro must be called with the one's complement of the bits.
The IONEXTSIG macro isolates the next inbound signal in sequence to process
from the inbound signal set S.
The IOCLEARSIG macro removes the processed signal L from the inbound signal
set S.
Implementation notes:
1. The IOPRIORITY macro implements two's complement explicitly, rather than
using a signed negation, to be compatible with machines using a
sign-magnitude integer format. "gcc" and "clang" optimize the complement
and add to a single NEG instruction on x86 machines.
*/
#define IOPRIORITY(P) ((P) & ~(P) + 1)
#define IONEXTSIG(S) ((INBOUND_SIGNAL) IOPRIORITY (S))
#define IOCLEARSIG(S,L) S = (INBOUND_SIGNAL) ((S) ^ (L))
/* I/O structures.
The Device Information Block (DIB) allows devices to be relocated in the
machine's I/O space. Each DIB contains a pointer to the device interface
routine, a value corresponding to the location of the interface card in the
CPU's I/O card cage (which determines the card's select code), a card index
that is non-zero if the interface routine services multiple cards, a pointer
to a string that describes the card, and an optional pointer to a string
that describes the 1000-series boot loader ROM that boots from the device
associated with the interface card. The card description is printed by the
SHOW CPU IOCAGE command, while the ROM description is printed by the SHOW CPU
ROMS command. All devices should have card descriptions, but only those
devices having boot loader ROMs will have ROM descriptions. The latter will
be NULL if no there is no associated ROM.
Implementation notes:
1. The select_code and card_index fields could be smaller than the defined
32-bit sizes, but IA-32 processors execute instructions with 32-bit
operands much faster than those with 16- or 8-bit operands.
2. The DIB_REGS macro provides hidden register entries needed to save and
restore the state of a DIB. Only the potentially variable fields are
referenced. In particular, the "io_interface" field must not be saved,
as the address of the device's interface routine may change from version
to version of the simulator.
3. The Windows SDK defines an INTERFACE macro. Because "sim_defs.h"
includes "windows.h", a name clash occurs, even though we never use any
Windows features. So we undefine INTERFACE here to repurpose it for our
own use.
*/
#undef INTERFACE /* remove any previous definition */
typedef struct dib DIB; /* an incomplete definition */
typedef SIGNALS_VALUE INTERFACE /* the I/O device interface function prototype */
(const DIB *dibptr, /* a pointer to the constant device information block */
INBOUND_SET inbound_signals, /* a set of inbound signals */
HP_WORD inbound_value); /* a 16-bit inbound value */
struct dib { /* the Device Information Block */
INTERFACE *io_interface; /* the controller I/O interface function pointer */
uint32 select_code; /* the device's select code (02-77) */
uint32 card_index; /* the card index if multiple interfaces are supported */
const char *card_description; /* the card description (model number and name) */
const char *rom_description; /* the 1000 boot loader ROM description (model number and name) */
};
#define DIB_REGS(dib) \
/* Macro Name Location Width Flags */ \
/* ------ ------- -------------------------- ----- ----------------- */ \
{ ORDATA (DIBSC, dib.select_code, 32), PV_LEFT | REG_HRO }
/* Initial Binary Loader.
HP 1000-series CPUs contain from one to four bootstrap loader ROMs that
contain the 64-word initial binary loaders for the associated devices. The
loader program to use is selected by setting the S-register as follows:
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| ROM # | - - | select code | - - - - - - |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
...and then pressing the front panel IBL button to copy the program into
main memory for execution.
Bits 15-14 select one of four loader ROM sockets on the CPU board that may
contain ROMs, and bits 11-6 specify the select code of the associated
interface card. If the specified socket does, the contents of the ROM are
copied into the upper 64 words of memory and configured to use the specified
select code. The unspecified bits of the S register are available for use by
the bootstrap program.
The 21xx CPUs store their initial binary loaders in the last 64 words of
available memory. This memory is protected by a LOADER ENABLE switch on the
front panel. When the switch is off (disabled), main memory effectively ends
64 locations earlier, i.e., the loader area is treated as non-existent.
Because these are core machines, the loader is retained when system power is
off.
In simulation, we define a BOOT_LOADER structure containing three indicies
and a 64-word array that holds the bootstrap program. The start_index gives
the array index of the first program word to execute, the dma_index gives the
array index of the DMA control word that specifies the interface select code,
and the fwa_index gives the array index of the word containing the negative
starting address of the loader in memory; this is used to ensure that the
bootstrap does not overlay itself when reading from the device.
A LOADER_ARRAY consists of two BOOT_LOADER structures: one for 21xx-series
machines, and one for 1000-series machines. If either BOOT_LOADER is not
applicable, e.g., because the CPU series does not support booting from the
given device, then the associated start_index is set to IBL_NA. If the boot
loader exists but does not use DMA and/or does not configure a starting
address word, the associated indicies are set to IBL_NA.
*/
#define IBL_WIDTH 6 /* loader ROM address width */
#define IBL_MASK ((1u << IBL_WIDTH) - 1) /* loader ROM address mask (2 ** 6 - 1) */
#define IBL_MAX ((1u << IBL_WIDTH) - 1) /* loader ROM address maximum (2 ** 6 - 1) */
#define IBL_SIZE (IBL_MAX + 1) /* loader ROM size in words */
#define IBL_START 0 /* ROM array index of the program start */
#define IBL_DMA (IBL_MAX - 1) /* ROM array index of the DMA configuration word */
#define IBL_FWA (IBL_MAX - 0) /* ROM array index of the negative starting address */
#define IBL_NA (IBL_MAX + 1) /* "not-applicable" ROM array index */
#define IBL_S_CLEAR 0000000u /* cpu_copy_loader mask to clear the S register */
#define IBL_S_NOCLEAR 0177777u /* cpu_copy_loader mask to preserve the S register */
#define IBL_S_NOSET 0000000u /* cpu_copy_loader mask to preserve the S register */
#define IBL_ROM_MASK 0140000u /* ROM socket selector mask */
#define IBL_SC_MASK 0007700u /* device select code mask */
#define IBL_USER_MASK ~(IBL_ROM_MASK | IBL_SC_MASK)
#define IBL_ROM_SHIFT 14
#define IBL_SC_SHIFT 6
#define IBL_ROM(s) (((s) & IBL_ROM_MASK) >> IBL_ROM_SHIFT)
#define IBL_SC(s) (((s) & IBL_SC_MASK) >> IBL_SC_SHIFT)
#define IBL_TO_SC(c) ((c) << IBL_SC_SHIFT & IBL_SC_MASK)
typedef struct {
uint32 start_index; /* the array index of the start of the program */
uint32 dma_index; /* the array index of the DMA configuration word */
uint32 fwa_index; /* the array index of the negative starting address */
MEMORY_WORD loader [IBL_SIZE]; /* the 64-word bootstrap loader program */
} BOOT_LOADER;
typedef BOOT_LOADER LOADER_ARRAY [2]; /* array (21xx, 1000) of bootstrap loaders */
/* CPU global utility routine declarations */
extern uint32 cpu_copy_loader (const LOADER_ARRAY boot, uint32 sc, HP_WORD sr_clear, HP_WORD sr_set);
extern t_bool cpu_io_stop (UNIT *uptr);
/* I/O subsystem global utility routine declarations */
extern void io_assert (DEVICE *dptr, IO_ASSERTION assertion);
/* Main memory global utility routine declarations */
extern HP_WORD mem_examine (uint32 address);
extern void mem_deposit (uint32 address, HP_WORD value);

2049
HP2100/hp2100_ipl.c Normal file

File diff suppressed because it is too large Load Diff

723
HP2100/hp2100_lps.c Normal file
View File

@@ -0,0 +1,723 @@
/* hp2100_lps.c: HP 2100 12653A Line Printer Interface simulator
Copyright (c) 1993-2016, Robert M. Supnik
Copyright (c) 2017-2019, J. David Bryan
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the names of the authors shall not be
used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from the authors.
LPS HP 12653A Line Printer Interface
09-Dec-19 JDB Removed redundant SIM_SW_REST check in "lps_attach"
14-Nov-18 JDB Diagnostic mode now uses GP Register jumper configuration
26-Jun-18 JDB Revised I/O model
03-Aug-17 JDB Changed perror call for I/O errors to cprintf
20-Jul-17 JDB Removed "lps_stopioe" variable and register
17-Mar-17 JDB Added "-N" handling to the attach routine
15-Mar-17 JDB Changed DEBUG_PRS calls to tprintfs
13-May-16 JDB Modified for revised SCP API function parameter types
10-Feb-12 JDB Deprecated DEVNO in favor of SC
28-Mar-11 JDB Tidied up signal handling
26-Oct-10 JDB Changed I/O signal handler for revised signal model
Revised detection of CLC at last DMA cycle
19-Oct-10 JDB Corrected 12566B (DIAG mode) jumper settings
26-Jun-08 JDB Rewrote device I/O to model backplane signals
10-May-07 RMS Added UNIT_TEXT flag
11-Jan-07 JDB CLC cancels I/O event if DIAG (jumper W9 in "A" pos)
Added ioCRS state to I/O decoders
19-Nov-04 JDB Added restart when set online, etc.
Fixed col count for non-printing chars
01-Oct-04 JDB Added SET OFFLINE/ONLINE, POWEROFF/POWERON
Fixed status returns for error conditions
Fixed handling of non-printing characters
Fixed handling of characters after column 80
Improved timing model accuracy for RTE
Added fast/realistic timing
Added debug printouts
03-Jun-04 RMS Fixed timing (found by Dave Bryan)
26-Apr-04 RMS Fixed SFS x,C and SFC x,C
Implemented DMA SRQ (follows FLG)
25-Apr-03 RMS Revised for extended file support
24-Oct-02 RMS Added microcircuit test features
30-May-02 RMS Widened POS to 32b
03-Dec-01 RMS Changed DEVNO to use extended SET/SHOW
07-Sep-01 RMS Moved function prototypes
21-Nov-00 RMS Fixed flag, fbf power up state
Added command flop
15-Oct-00 RMS Added variable device number support
References:
- 2767A Line Printer Operating and Service Manual
(02767-90002, October 1973)
- 12653A Line Printer Interface Kit
(12653-90002, October 1971)
- General Purpose Register Diagnostic Reference Manual
(24391-90001, April 1982)
The HP 12653A Line Printer Interface Kit connects the 2767A printer to the HP
1000 family. The subsystem consists of an interface card employing TTL-level
line drivers and receivers, an interconnecting cable, and an HP 2767A (from
356 to 1110 lines per minute) line printer. The interface is supported by
RTE and DOS drivers DVR12. The interface supports DMA transfers, but the OS
drivers do not use them.
The 2767 impact printer has a rotating drum with 80 columns of 64 raised
characters. ASCII codes 32 through 95 (SPACE through "_") form the print
repertoire. The printer responds to the control characters FF, LF, and CR.
The 80 columns are divided into four zones of 20 characters each that are
addressed sequentially. Received characters are buffered in a 20-character
memory. When the 20th printable character is received, the current zone is
printed, and the memory is reset. In the absence of print command
characters, a zone print operation will commence after each group of 20
printable characters is transmitted to the printer.
The print command characters have these actions:
* CR -- print the characters in the current zone, reset to zone 1, and clear
the buffer memory.
* LF -- same as CR, plus advances the paper one line.
* FF -- same as CR, plus advances the paper to the top of the next form.
The 2767 provides two status bits via the interface:
bit 15 -- printer not ready
bit 0 -- printer busy
The expected status returns are:
100001 -- power off or cable disconnected
100001 -- initial power on, then changes to 000001 within sixty
seconds of initial power on
000001 -- power on, paper unloaded or printer offline or not idle
000000 -- power on, paper loaded and printer online and idle
These simulator commands provide the listed printer states:
SET LPS POWEROFF --> power off or cable disconnected
SET LPS POWERON --> power on
SET LPS OFFLINE --> printer offline
SET LPS ONLINE --> printer online
ATT LPS <file> --> paper loaded
DET LPS --> paper out
The following implemented behaviors have been inferred from secondary sources
(diagnostics, operating system drivers, etc.), due to absent or contradictory
authoritative information; future correction may be needed:
1. Paper out sets BUSY instead of NOT READY.
2. Print operation in progress sets BUSY instead of NOT READY.
3. Characters not in the print repertoire are replaced with blanks.
4. The 81st and succeeding characters overprint the current line.
A diagnostic mode is provided to simulate the installation of the 1251-0332
loopback connecctor, modified to connect pins Z/22 to pins AA/23 as required
by the General Purpose Register Diagnostic. This ties the output data lines
to the input data lines and the device command output to the device flag
input. Entering diagnostic mode also configures the jumpers correctly for
the diagnostic.
*/
#include "hp2100_defs.h"
#include "hp2100_io.h"
/* Printer program constants */
#define CR '\r' /* carriage return */
#define LF '\n' /* line feed */
#define FF '\f' /* form feed */
#define DATA_MASK 0177u /* printer uses only 7 bits for data */
#define LPS_ZONECNT 20 /* zone char count */
#define LPS_PAGECNT 80 /* page char count */
#define LPS_PAGELNT 60 /* page line length */
#define LPS_FORMLNT 66 /* form line length */
/* Printer power states */
#define LPS_ON 0 /* power is on */
#define LPS_OFF 1 /* power is off */
#define LPS_TURNING_ON 2 /* power is turning on */
#define LPS_BUSY 0000001 /* busy status */
#define LPS_NRDY 0100000 /* not ready status */
#define LPS_PWROFF LPS_BUSY | LPS_NRDY /* power-off status */
#define UNIT_V_DIAG (UNIT_V_UF + 0) /* diagnostic mode */
#define UNIT_V_POWEROFF (UNIT_V_UF + 1) /* unit powered off */
#define UNIT_V_OFFLINE (UNIT_V_UF + 2) /* unit offline */
#define UNIT_DIAG (1 << UNIT_V_DIAG)
#define UNIT_POWEROFF (1 << UNIT_V_POWEROFF)
#define UNIT_OFFLINE (1 << UNIT_V_OFFLINE)
static struct {
FLIP_FLOP control; /* control flip-flop */
FLIP_FLOP flag; /* flag flip-flop */
FLIP_FLOP flag_buffer; /* flag buffer flip-flop */
} lps = { CLEAR, CLEAR, CLEAR };
static int32 lps_ccnt = 0; /* character count */
static int32 lps_lcnt = 0; /* line count */
static int32 lps_sta = 0; /* printer status */
static t_bool lps_fast_timing = TRUE; /* timing type */
static uint32 lps_power = LPS_ON; /* power state */
/* Hardware timing:
(based on 1580 instr/msec) instr msec calc msec
------------------------
- character transfer time : ctime = 2 2 us
- per-zone printing time : ptime = 55300 35 40
- per-line paper slew time : stime = 17380 11 13
- power-on ready delay time : rtime = 158000 100
NOTE: the printer acknowledges before the print motion has stopped to allow
for continuous slew, so the set times are a bit less than the calculated
operation time from the manual.
NOTE: the 2767 diagnostic checks completion times, so the realistic timing
must be used. Because simulator timing is in instructions, and because the
diagnostic uses the TIMER instruction (~1580 executions per millisecond) when
running on a 1000-E/F but a software timing loop (~400-600 executions per
millisecond) when running on anything else, realistic timings are decreased by
three-fourths when not executing on an E/F.
*/
static int32 lps_ctime = 0; /* char xfer time */
static int32 lps_ptime = 0; /* zone printing time */
static int32 lps_stime = 0; /* paper slew time */
static int32 lps_rtime = 0; /* power-on ready time */
typedef int32 TIMESET[4]; /* set of controller times */
static int32 * const lps_timers[] = { &lps_ctime, &lps_ptime, &lps_stime, &lps_rtime };
static const TIMESET lps_times[2] = {
{ 2, 55300, 17380, 158000 }, /* REALTIME */
{ 2, 1000, 1000, 1000 } /* FASTTIME */
};
static INTERFACE lps_interface;
static t_stat lps_svc (UNIT *uptr);
static t_stat lps_reset (DEVICE *dptr);
static t_stat lps_restart (UNIT *uptr, int32 value, char *cptr, void *desc);
static t_stat lps_poweroff (UNIT *uptr, int32 value, char *cptr, void *desc);
static t_stat lps_poweron (UNIT *uptr, int32 value, char *cptr, void *desc);
static t_stat lps_attach (UNIT *uptr, char *cptr);
static t_stat lps_set_timing (UNIT *uptr, int32 val, char *cptr, void *desc);
static t_stat lps_show_timing (FILE *st, UNIT *uptr, int32 val, void *desc);
/* LPS data structures
lps_dev LPS device descriptor
lps_unit LPS unit descriptor
lps_reg LPS register list
*/
static DIB lps_dib = {
&lps_interface, /* the device's I/O interface function pointer */
LPS, /* the device's select code (02-77) */
0, /* the card index */
"12653A Line Printer Interface", /* the card description */
NULL }; /* the ROM description */
static UNIT lps_unit = {
UDATA (&lps_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_DISABLE+UNIT_TEXT, 0)
};
static REG lps_reg[] = {
/* Macro Name Location Width Offset Flags */
/* ------ ------ ------------------- --------- ------ ------- */
{ ORDATA (BUF, lps_unit.buf, 16), REG_X },
{ ORDATA (STA, lps_sta, 16) },
{ ORDATA (POWER, lps_power, 2), REG_RO },
{ FLDATA (CTL, lps.control, 0) },
{ FLDATA (FLG, lps.flag, 0) },
{ FLDATA (FBF, lps.flag_buffer, 0) },
{ DRDATA (CCNT, lps_ccnt, 7), PV_LEFT },
{ DRDATA (LCNT, lps_lcnt, 7), PV_LEFT },
{ DRDATA (POS, lps_unit.pos, T_ADDR_W), PV_LEFT },
{ DRDATA (CTIME, lps_ctime, 24), PV_LEFT },
{ DRDATA (PTIME, lps_ptime, 24), PV_LEFT },
{ DRDATA (STIME, lps_stime, 24), PV_LEFT },
{ DRDATA (RTIME, lps_rtime, 24), PV_LEFT },
{ FLDATA (TIMING, lps_fast_timing, 0), REG_HRO },
DIB_REGS (lps_dib),
{ NULL }
};
static MTAB lps_mod[] = {
/* Mask Value Match Value Print String Match String Validation Display Descriptor */
/* ------------- ------------- ----------------- ------------- ------------------ ------- ---------- */
{ UNIT_DIAG, UNIT_DIAG, "diagnostic mode", "DIAGNOSTIC", NULL, NULL, NULL },
{ UNIT_DIAG, 0, "printer mode", "PRINTER", NULL, NULL, NULL },
{ UNIT_OFFLINE, UNIT_OFFLINE, "offline", "OFFLINE", NULL, NULL, NULL },
{ UNIT_OFFLINE, 0, "online", "ONLINE", &lps_restart, NULL, NULL },
{ UNIT_POWEROFF, UNIT_POWEROFF, "power off", "POWEROFF", &lps_poweroff, NULL, NULL },
{ UNIT_POWEROFF, 0, "power on", "POWERON", &lps_poweron, NULL, NULL },
/* Entry Flags Value Print String Match String Validation Display Descriptor */
/* ------------------- ----- ------------ ------------ ---------------- ----------------- ----------------- */
{ MTAB_XDV, 1, NULL, "FASTTIME", &lps_set_timing, NULL, NULL },
{ MTAB_XDV, 0, NULL, "REALTIME", &lps_set_timing, NULL, NULL },
{ MTAB_XDV, 0, "TIMING", NULL, NULL, &lps_show_timing, NULL },
{ MTAB_XDV, 1u, "SC", "SC", &hp_set_dib, &hp_show_dib, (void *) &lps_dib },
{ MTAB_XDV | MTAB_NMO, ~1u, "DEVNO", "DEVNO", &hp_set_dib, &hp_show_dib, (void *) &lps_dib },
{ 0 }
};
static DEBTAB lps_deb [] = {
{ "CMDS", DEB_CMDS },
{ "CPU", DEB_CPU },
{ "XFER", DEB_XFER },
{ "STATE", TRACE_STATE },
{ "IOBUS", TRACE_IOBUS }, /* interface I/O bus signals and data words */
{ NULL, 0 }
};
DEVICE lps_dev = {
"LPS", /* device name */
&lps_unit, /* unit array */
lps_reg, /* register array */
lps_mod, /* modifier array */
1, /* number of units */
10, /* address radix */
31, /* address width */
1, /* address increment */
8, /* data radix */
8, /* data width */
NULL, /* examine routine */
NULL, /* deposit routine */
&lps_reset, /* reset routine */
NULL, /* boot routine */
&lps_attach, /* attach routine */
NULL, /* detach routine */
&lps_dib, /* device information block */
DEV_DISABLE | DEV_DIS | DEV_DEBUG, /* device flags */
0, /* debug control flags */
lps_deb, /* debug flag name table */
NULL, /* memory size change routine */
NULL /* logical device name */
};
/* I/O signal handler */
static SIGNALS_VALUE lps_interface (const DIB *dibptr, INBOUND_SET inbound_signals, HP_WORD inbound_value)
{
INBOUND_SIGNAL signal;
INBOUND_SET working_set = inbound_signals;
SIGNALS_VALUE outbound = { ioNONE, 0 };
t_bool irq_enabled = FALSE;
int32 current_line, current_char;
while (working_set) { /* while signals remain */
signal = IONEXTSIG (working_set); /* isolate the next signal */
switch (signal) { /* dispatch the I/O signal */
case ioCLF: /* Clear Flag flip-flop */
lps.flag_buffer = CLEAR; /* reset the flag buffer */
lps.flag = CLEAR; /* and flag flip-flops */
break;
case ioSTF: /* Set Flag flip-flop */
lps.flag_buffer = SET; /* set the flag buffer flip-flop */
break;
case ioENF: /* Enable Flag */
if (lps.flag_buffer == SET) /* if the flag buffer flip-flop is set */
lps.flag = SET; /* then set the flag flip-flop */
break;
case ioSFC: /* Skip if Flag is Clear */
if (lps.flag == CLEAR) /* if the flag flip-flop is clear */
outbound.signals |= ioSKF; /* then assert the Skip on Flag signal */
break;
case ioSFS: /* Skip if Flag is Set */
if (lps.flag == SET) /* if the flag flip-flop is set */
outbound.signals |= ioSKF; /* then assert the Skip on Flag signal */
break;
case ioIOI: /* I/O Data Input */
if ((lps_unit.flags & UNIT_DIAG) == 0) { /* real lpt? */
if (lps_power == LPS_ON) { /* power on? */
if (((lps_unit.flags & UNIT_ATT) == 0) || /* paper out? */
(lps_unit.flags & UNIT_OFFLINE) || /* offline? */
sim_is_active (&lps_unit)) lps_sta = LPS_BUSY;
else
lps_sta = 0;
}
else
lps_sta = LPS_PWROFF;
}
outbound.value = lps_sta;
tprintf (lps_dev, DEB_CPU, "Status %06o returned\n", lps_sta);
break;
case ioIOO: /* I/O Data Output */
lps_unit.buf = inbound_value;
tprintf (lps_dev, DEB_CPU, "Control %06o (%s) output\n",
lps_unit.buf, fmt_char (lps_unit.buf & DATA_MASK));
break;
case ioPOPIO: /* Power-On Preset to I/O */
lps.flag_buffer = SET; /* set the flag buffer flip-flop */
lps_unit.buf = 0; /* and clear the output register */
break;
case ioCRS: /* Control Reset */
lps.control = CLEAR; /* clear the control flip-flop */
sim_cancel (&lps_unit); /* and cancel any printing in progress */
break;
case ioCLC: /* Clear Control flip-flop */
lps.control = CLEAR; /* clear the control flip-flop */
break;
case ioSTC: /* Set Control flip-flop */
lps.control = SET; /* set the control flip-flop */
if (lps_unit.flags & UNIT_DIAG) { /* diagnostic? */
lps_sta = lps_unit.buf; /* loop back data */
sim_activate_abs (&lps_unit, 1); /* and set the flag after the next instruction */
}
else { /* real lpt, sched */
current_char = lps_ccnt + 1;
current_line = lps_lcnt + 1;
if ((lps_unit.buf != FF) &&
(lps_unit.buf != LF) &&
(lps_unit.buf != CR)) { /* normal char */
lps_ccnt = lps_ccnt + 1; /* incr char counter */
if (lps_ccnt % LPS_ZONECNT == 0) /* end of zone? */
lps_unit.wait = lps_ptime; /* print zone */
else
lps_unit.wait = lps_ctime; /* xfer char */
}
else { /* print cmd */
if (lps_ccnt % LPS_ZONECNT == 0) /* last zone printed? */
lps_unit.wait = lps_ctime; /* yes, so just char time */
else
lps_unit.wait = lps_ptime; /* no, so print needed */
lps_ccnt = 0; /* reset char counter */
if (lps_unit.buf == LF) { /* line advance */
lps_lcnt = (lps_lcnt + 1) % LPS_PAGELNT;
if (lps_lcnt > 0)
lps_unit.wait += lps_stime;
else
lps_unit.wait += /* allow for perf skip */
lps_stime * (LPS_FORMLNT - LPS_PAGELNT);
}
else if (lps_unit.buf == FF) { /* form advance */
lps_unit.wait += lps_stime * (LPS_FORMLNT - lps_lcnt);
lps_lcnt = 0;
}
}
sim_activate (&lps_unit, lps_unit.wait);
tprintf (lps_dev, DEB_CMDS, "Character %s scheduled for line %d, column %d, time = %d\n",
fmt_char (lps_unit.buf & DATA_MASK), current_line, current_char, lps_unit.wait);
}
break;
case ioSIR: /* Set Interrupt Request */
if (lps.control & lps.flag) /* if the control and flag flip-flops are set */
outbound.signals |= cnVALID; /* then deny PRL */
else /* otherwise */
outbound.signals |= cnPRL | cnVALID; /* conditionally assert PRL */
if (lps.control & lps.flag & lps.flag_buffer) /* if the control, flag, and flag buffer flip-flops are set */
outbound.signals |= cnIRQ | cnVALID; /* then conditionally assert IRQ */
if (lps.flag == SET) /* if the flag flip-flop is set */
outbound.signals |= ioSRQ; /* then assert SRQ */
break;
case ioIAK: /* Interrupt Acknowledge */
lps.flag_buffer = CLEAR; /* clear the flag buffer flip-flop */
break;
case ioIEN: /* Interrupt Enable */
irq_enabled = TRUE; /* permit IRQ to be asserted */
break;
case ioPRH: /* Priority High */
if (irq_enabled && outbound.signals & cnIRQ) /* if IRQ is enabled and conditionally asserted */
outbound.signals |= ioIRQ | ioFLG; /* then assert IRQ and FLG */
if (!irq_enabled || outbound.signals & cnPRL) /* if IRQ is disabled or PRL is conditionally asserted */
outbound.signals |= ioPRL; /* then assert it unconditionally */
break;
case ioEDT: /* not used by this interface */
case ioPON: /* not used by this interface */
break;
}
IOCLEARSIG (working_set, signal); /* remove the current signal from the set */
}
return outbound; /* return the outbound signals and value */
}
/* Unit service.
As a convenience to the user, the printer output file is flushed when a TOF
operation is performed.
*/
static t_stat lps_svc (UNIT *uptr)
{
int32 c = uptr->buf & DATA_MASK;
if (lps_power == LPS_TURNING_ON) { /* printer warmed up? */
lps_power = LPS_ON; /* change state */
lps_restart (uptr, 0, NULL, NULL); /* restart I/O if hung */
tprintf (lps_dev, TRACE_STATE, "Power state is ON\n");
return SCPE_OK; /* done */
}
if (uptr->flags & UNIT_DIAG) { /* diagnostic? */
lps.flag_buffer = SET;
io_assert (&lps_dev, ioa_ENF); /* set flag */
return SCPE_OK; /* done */
}
if ((uptr->flags & (UNIT_ATT | UNIT_OFFLINE | UNIT_POWEROFF)) != UNIT_ATT) /* not ready? */
return SCPE_OK;
lps.flag_buffer = SET;
io_assert (&lps_dev, ioa_ENF); /* set flag */
if (((c < ' ') || (c > '_')) && /* non-printing char? */
(c != FF) && (c != LF) && (c != CR)) {
tprintf (lps_dev, DEB_XFER, "Character %s erased\n", fmt_char (c));
c = ' '; /* replace with blank */
}
if (lps_ccnt > LPS_PAGECNT) { /* 81st character? */
fputc (CR, uptr->fileref); /* return to line start */
uptr->pos = uptr->pos + 1; /* update pos */
lps_ccnt = 1; /* reset char counter */
tprintf (lps_dev, DEB_XFER, "Line wraparound to column 1\n");
}
fputc (c, uptr->fileref); /* "print" char */
uptr->pos = uptr->pos + 1; /* update pos */
tprintf (lps_dev, DEB_XFER, "Character %s printed\n", fmt_char (c));
if (lps_lcnt == 0) { /* if the printer is at the TOF */
fflush (uptr->fileref); /* then flush the file buffer for inspection */
if (c == LF) { /* LF did TOF? */
fputc (FF, uptr->fileref); /* do perf skip */
uptr->pos = uptr->pos + 1; /* update pos */
tprintf (lps_dev, DEB_XFER, "Perforation skip to TOF\n");
}
}
if (ferror (uptr->fileref)) { /* if a host file I/O error occurred */
cprintf ("%s simulator printer I/O error: %s\n", /* then report the error to the console */
sim_name, strerror (errno));
clearerr (uptr->fileref); /* clear the error */
lps_unit.flags |= UNIT_OFFLINE; /* set offline */
return SCPE_IOERR;
}
return SCPE_OK;
}
/* Reset routine */
static t_stat lps_reset (DEVICE *dptr)
{
if (sim_switches & SWMASK ('P')) { /* power-on reset? */
lps_power = LPS_ON; /* power is on */
lps_set_timing (NULL, lps_fast_timing, NULL, NULL); /* init timing set */
}
io_assert (dptr, ioa_POPIO); /* PRESET the device */
lps_sta = 0; /* clear status */
sim_cancel (&lps_unit); /* deactivate unit */
return SCPE_OK;
}
/* Restart I/O routine
If I/O is started via STC, and the printer is powered off, offline,
or out of paper, the CTL and CMD flip-flops will set, a service event
will be scheduled, and the service routine will be entered. If
STOP_IOE is not set, the I/O operation will "hang" at that point
until the printer is powered on, set online, or paper is supplied
(attached).
If a pending operation is "hung" when this routine is called, it is
restarted, which clears CTL and sets FBF and FLG, completing the
original I/O request.
*/
static t_stat lps_restart (UNIT *uptr, int32 value, char *cptr, void *desc)
{
if (lps.control && !sim_is_active (uptr))
sim_activate (uptr, 1); /* reschedule I/O */
return SCPE_OK;
}
/* Printer power off */
static t_stat lps_poweroff (UNIT *uptr, int32 value, char *cptr, void *desc)
{
lps_power = LPS_OFF; /* change state */
tprintf (lps_dev, TRACE_STATE, "Power state is OFF\n");
return SCPE_OK;
}
/* Printer power on */
static t_stat lps_poweron (UNIT *uptr, int32 value, char *cptr, void *desc)
{
if (lps_unit.flags & UNIT_DIAG) { /* diag mode? */
lps_power = LPS_ON; /* no delay */
tprintf (lps_dev, TRACE_STATE, "Power state is ON\n");
}
else {
lps_power = LPS_TURNING_ON; /* change state */
lps_unit.flags |= UNIT_OFFLINE; /* set offline */
sim_activate (&lps_unit, lps_rtime); /* schedule ready */
tprintf (lps_dev, TRACE_STATE, "Power state is TURNING ON, scheduled time = %d\n",
lps_rtime);
}
return SCPE_OK;
}
/* Attach the printer image file.
The specified file is attached to the indicated unit. This is the simulation
equivalent of loading paper into the printer and pressing the ONLINE button.
The transition from offline to online typically generates an interrupt.
A new image file may be requested by giving the "-N" switch to the ATTACH
command. If an existing file is specified with "-N", it will be cleared; if
specified without "-N", printer output will be appended to the end of the
existing file content. In all cases, the paper is positioned at the top of
the form.
*/
static t_stat lps_attach (UNIT *uptr, char *cptr)
{
t_stat result;
result = hp_attach (uptr, cptr); /* attach the specified printer image file for appending */
if (result == SCPE_OK) { /* if the attach was successful */
lps_ccnt = 0; /* then clear the character counter */
lps_lcnt = 0; /* and set top of form */
lps_restart (uptr, 0, NULL, NULL); /* restart I/O if hung */
}
return result;
}
/* Set printer timing
Realistic timing is factored, depending on CPU model, to account for the
timing method employed by the diagnostic. In realistic timing mode, the
diagnostic executes fewer instructions per interval if the CPU is not a 1000
E or F series machine.
*/
static t_stat lps_set_timing (UNIT *uptr, int32 val, char *cptr, void *desc)
{
uint32 i, factor;
lps_fast_timing = (val != 0); /* determine choice */
if (lps_fast_timing /* if optimized timing is used */
|| cpu_configuration & (CPU_1000_E | CPU_1000_F)) /* or this is a 1000 E or F CPU */
factor = 1; /* then no time correction is needed */
else /* otherwise */
factor = 4; /* the times will be slower */
for (i = 0; i < (sizeof (lps_timers) / sizeof (lps_timers [0])); i++)
*lps_timers [i] = lps_times [lps_fast_timing] [i] / factor; /* assign times */
return SCPE_OK;
}
/* Show printer timing */
static t_stat lps_show_timing (FILE *st, UNIT *uptr, int32 val, void *desc)
{
if (lps_fast_timing)
fputs ("fast timing", st);
else
fputs ("realistic timing", st);
return SCPE_OK;
}

2210
HP2100/hp2100_lpt.c Normal file

File diff suppressed because it is too large Load Diff

620
HP2100/hp2100_mc.c Normal file
View File

@@ -0,0 +1,620 @@
/* hp2100_mc.c: HP 12566B Microcircuit Interface simulator
Copyright (c) 2018, J. David Bryan
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the name of the author shall not be
used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from the author.
MC 12566B Microcircuit Interface
09-Jul-18 JDB Created
References:
- 12566B[-001/2/3] Microcircuit Interface Kits Operating and Service Manual
(12566-90015, April 1976)
The 12566B Microcircuit Interface provides a general-purpose 16-bit
bidirectional data path between the CPU and an I/O device that supports both
programmed I/O and DMA transfers. A simple, two-wire handshake provides the
control signals to coordinate data transfers. All device signals are
TTL-compatible, and transfer rates up to one-half of the DMA bandwidth are
possible.
The 12566B supplies 16 data bits and asserts a Device Command signal to the
device to indicate the start of a transfer. The device returns 16 data bits
and asserts a Device Flag signal to indicate completion of the transfer.
Assertion of Device Flag causes the interface to set its Flag flip-flop and
assert an interupt request to the CPU and a service request to the DMA
controller.
This simulation primarily provides a target interface for several diagnostic
programs. In the DIAGNOSTIC mode, a loopback connector is installed in place
of the usual device cable, and the interface provides a general data source
and sink, as well as a source of interrupts and a break in the I/O priority
chain. In the DEVICE mode, the simulation provides a model to illustrate the
required interface to the CPU's I/O backplane, as no peripheral device is
simulated.
Befitting its general purpose, the hardware interface has nine jumpers,
labelled W1-W9, that may be positioned to configure the electrical polarity
and behavior of the Device Command and Device Flag signals. The lettered
jumper positions and their effects are:
Jumper Pos Action
------ --- ------------------------------------------------------------
W1 A Device Command signal is ground true asserted with STC
B Device Command signal is positive true asserted with STC
C Device Command signal is ground true asserted for T6 and T2
W2 A Device Command flip-flop clears on positive edge of Device Flag
B Device Command flip-flop clears on negative edge of Device Flag
C Device Command flip-flop clears on ENF (T2)
W3 A Device Flag signal sets Flag Buffer and strobes data on positive edge
B Device Flag signal sets Flag Buffer and strobes data on negative edge
W4 A Output Data Register is gated by the data flip-flop
B Output Data Register is continuously available
W5 IN Input Data Register bits 0-3 are latched by Device Flag
OUT Input Data Register bits 0-3 are transparent
W6 IN Input Data Register bits 4-7 are latched by Device Flag
OUT Input Data Register bits 4-7 are transparent
W7 IN Input Data Register bits 8-11 are latched by Device Flag
OUT Input Data Register bits 8-11 are transparent
W8 IN Input Data Register bits 12-15 are latched by Device Flag
OUT Input Data Register bits 12-15 are transparent
W9 A Device Command flip-flop cleared by CLC, CRS, and Device Flag
B Device Command flip-flop cleared by CRS and Device Flag
The electrical characteristics of the device being interfaced dictates the
jumper settings used. The jumper settings required for the standard HP
peripherals that use the microcircuit card are:
W1 W2 W3 W4 W5 W6 W7 W8 W9 Device
--- --- --- --- --- --- --- --- --- ----------------------------------------
A B A B OUT IN IN IN A 12566B-004 Line Printer Interface (9866)
B A B B OUT IN IN OUT A 12653A Line Printer Interface (2767)
A B B B IN IN IN OUT B 12732A Flexible Disc Subsystem (Control)
A A B B IN IN IN IN B 12732A Flexible Disc Subsystem (Data)
A B B B IN IN IN IN A 12875A Processor Interconnect Kit
For diagnostic use, the required jumper settings are:
W1 W2 W3 W4 W5 W6 W7 W8 W9 DSN Diagnostic
--- --- --- --- --- --- --- --- --- ------ ---------------------------------
C B B B IN IN IN IN A 143300 General Purpose Register
C B B B IN IN IN IN A 141203 I/O Instruction Group
C B B B IN IN IN IN A 102103 Memory Expansion Unit
C B B B IN IN IN IN A 101220 DMA/DCPC for 2100/1000
B A A B IN IN IN IN A -- DMA for 2100 (HP 24195)
B A A B IN IN IN IN A 101105 DMA for 2114/2115/2116 (HP 24322)
B C A A/B IN IN IN IN A 101105 DMA for 2114/2115/2116 (HP 24322)
B C B B IN IN IN IN A -- DMA for 2115/2116 (HP 24185)
(not relevant; interrupt only) 101112 Extended Instruction Group
(not relevant; interrupt only) 101213 M/E-Series Fast FORTRAN Package 1
(not relevant; interrupt only) 101115 M/E-Series Fast FORTRAN Package 2
(not relevant; interrupt only) 101121 F-Series FPP-SIS-FFP
(not relevant; interrupt only) 102305 Memory Protect/Parity Error
The diagnostics that specify jumper settings above test data writing and
reading and so require the installation of the HP 1251-0332 diagnostic test
(loopback) connector in place of the normal device cable connector. This
test connector connects each data output bit with its corresponding data
input bit and connects the Device Command output signal to the Device Flag
input signal.
The diagnostics that test the HP 12607B DMA card for the 2115 and 2116 CPUs
require an unusual jumper configuration. The card provides hardware byte
packing and unpacking during DMA transfers. The diagnostics test this
hardware by strapping the microcircuit interface so that the Device Flag
signal sets the Flag flip-flop for a CPU cycle but not for a DMA cycle. This
allows the diagnostic to advance the DMA byte transfer hardware sequence
cycle-by-cycle under program control.
In hardware, this works only because the 2115/2116 DMA cycle asserts the STC
and CLF signals for two staggered T-periods, with CLF remaining asserted for
one T-period after STC denies. The 2115/2116 CPU cycle, as well as the CPU
and DMA cycles of all other 21xx/1000 machines, assert STC and CLF
coincidently for one T-period.
Under simulation, this action cannot be derived by simulating the jumper
behaviors directly, because the I/O backplane signal timing relationships are
not simulated. Instead, Device Flag assertion is omitted for 2115/2116 DMA
cycles when DIAGNOSTIC mode is selected.
This module does not simulate the individual jumper settings, for two
reasons. First, with no peripheral device connected to the interface, the
jumper settings are irrelevant. Should this module be used as the basis for
a specific device interface, that device would dictate the jumper settings
required. As the settings would be fixed, having a user-configurable set of
jumpers would serve no purpose. Second, while user-configurable jumpers
would be useful to configure the card for diagnostics, the fact that the I/O
backplane signal timing is not simulated means that the interface behavior
cannot be derived from the jumper settings alone. Therefore, entering the
DIAGNOSTIC mode simulates the proper jumper settings for the various
diagnostics listed above.
Implementation notes:
1. Two identical interfaces are provided: MC1 and MC2. Both interfaces are
used by the 12936A Privileged Interrupt Fence diagnostic. They also
serve as an illustration of how to model multiple interfaces in the HP
2100 simulator.
2. The microcircuit interfaces are disabled by default, as they are only
used during diagnostic execution.
*/
#include "hp2100_defs.h"
#include "hp2100_io.h"
/* Program limits */
#define CARD_COUNT 2 /* count of cards supported */
/* Device property constant declarations */
#define LOOPBACK_DELAY (int32) uS (1) /* diagnostic loopback flag assertion delay */
/* Unit flags */
#define UNIT_V_DIAG (UNIT_V_UF + 0) /* diagnostic mode is selected */
#define UNIT_DIAG (1 << UNIT_V_DIAG)
/* Unit references */
typedef enum {
mc1, /* first microcircuit card index */
mc2 /* second microcircuit card index */
} CARD_INDEX;
/* Interface state */
typedef struct {
HP_WORD output_data; /* output data register */
HP_WORD input_data; /* input data register */
FLIP_FLOP command; /* command flip-flop */
FLIP_FLOP control; /* control flip-flop */
FLIP_FLOP flag; /* flag flip-flop */
FLIP_FLOP flag_buffer; /* flag buffer flip-flop */
} CARD_STATE;
static CARD_STATE mc [CARD_COUNT]; /* per-card state */
/* Interface local SCP support routines */
static INTERFACE mc_interface;
/* Interface local SCP support routines */
static t_stat mc_service (UNIT *uptr);
static t_stat mc_reset (DEVICE *dptr);
/* Interface SCP data structures */
/* Device information block */
static DIB mc_dib [CARD_COUNT] = {
{ &mc_interface, /* the device's I/O interface function pointer */
MC1, /* the device's select code (02-77) */
0, /* the card index */
"12566B Microcircuit Interface", /* the card description */
NULL }, /* the ROM description */
{ &mc_interface, /* the device's I/O interface function pointer */
MC2, /* the device's select code (02-77) */
1, /* the card index */
"12566B Microcircuit Interface", /* the card description */
NULL } /* the ROM description */
};
/* Unit list */
#define UNIT_FLAGS (0)
static UNIT mc_unit [CARD_COUNT] = {
/* Event Routine Unit Flags Capacity Delay */
/* ------------- ---------- -------- ----- */
{ UDATA (&mc_service, UNIT_FLAGS, 0), 0 },
{ UDATA (&mc_service, UNIT_FLAGS, 0), 0 }
};
/* Register lists.
Implementation notes:
1. The two REG arrays would be more conveniently declared as:
static REG mc_reg [CARD_COUNT] [] = { { ... }, { ... } };
Unfortunately, this is illegal in C ("a multidimensional array must have
bounds for all dimensions except the first").
*/
static REG mc1_reg [] = {
/* Macro Name Location Width Offset Flags */
/* ------ ------ ------------------------ ---------- ------ ----------------- */
{ ORDATA (IN, mc [mc1].input_data, 16) },
{ ORDATA (OUT, mc [mc1].output_data, 16) },
{ FLDATA (CTL, mc [mc1].control, 0) },
{ FLDATA (FLG, mc [mc1].flag, 0) },
{ FLDATA (FBF, mc [mc1].flag_buffer, 0) },
{ FLDATA (CMD, mc [mc1].command, 0) },
DIB_REGS (mc_dib [mc1]),
{ NULL }
};
static REG mc2_reg [] = {
/* Macro Name Location Width Offset Flags */
/* ------ ------ ------------------------ ---------- ------ ----------------- */
{ ORDATA (IN, mc [mc2].input_data, 16) },
{ ORDATA (OUT, mc [mc2].output_data, 16) },
{ FLDATA (CTL, mc [mc2].control, 0) },
{ FLDATA (FLG, mc [mc2].flag, 0) },
{ FLDATA (FBF, mc [mc2].flag_buffer, 0) },
{ FLDATA (CMD, mc [mc2].command, 0) },
DIB_REGS (mc_dib [mc2]),
{ NULL }
};
/* Modifier lists */
static MTAB mc1_mod [] = {
/* Mask Value Match Value Print String Match String Validation Display Descriptor */
/* ---------- ----------- ----------------- ------------ ----------- ------- ---------- */
{ UNIT_DIAG, UNIT_DIAG, "diagnostic mode", "DIAGNOSTIC", NULL, NULL, NULL },
{ UNIT_DIAG, 0, "device mode", "DEVICE", NULL, NULL, NULL },
/* Entry Flags Value Print String Match String Validation Display Descriptor */
/* ----------- ----- ------------ ------------ ------------ ------------- ---------------------- */
{ MTAB_XDV, 1u, "SC", "SC", &hp_set_dib, &hp_show_dib, (void *) &mc_dib [mc1] },
{ 0 }
};
static MTAB mc2_mod [] = {
/* Mask Value Match Value Print String Match String Validation Display Descriptor */
/* ---------- ----------- ----------------- ------------ ----------- ------- ---------- */
{ UNIT_DIAG, UNIT_DIAG, "diagnostic mode", "DIAGNOSTIC", NULL, NULL, NULL },
{ UNIT_DIAG, 0, "device mode", "DEVICE", NULL, NULL, NULL },
/* Entry Flags Value Print String Match String Validation Display Descriptor */
/* ----------- ----- ------------ ------------ ------------ ------------- ---------------------- */
{ MTAB_XDV, 1u, "SC", "SC", &hp_set_dib, &hp_show_dib, (void *) &mc_dib [mc2] },
{ 0 }
};
/* Debugging trace list */
static DEBTAB mc_deb [] = {
{ "XFER", TRACE_XFER }, /* trace data transmissions and receptions */
{ "IOBUS", TRACE_IOBUS }, /* trace I/O bus signals and data words received and returned */
{ NULL, 0 }
};
/* Device descriptors */
DEVICE mc1_dev = {
"MC1", /* device name */
&mc_unit [mc1], /* unit array */
mc1_reg, /* register array */
mc1_mod, /* modifier array */
1, /* number of units */
10, /* address radix */
31, /* address width */
1, /* address increment */
8, /* data radix */
16, /* data width */
NULL, /* examine routine */
NULL, /* deposit routine */
&mc_reset, /* reset routine */
NULL, /* boot routine */
NULL, /* attach routine */
NULL, /* detach routine */
&mc_dib [mc1], /* device information block pointer */
DEV_DISABLE | DEV_DIS | DEV_DEBUG, /* device flags */
0, /* debug control flags */
mc_deb, /* debug flag name array */
NULL, /* memory size change routine */
NULL /* logical device name */
};
DEVICE mc2_dev = {
"MC2", /* device name */
&mc_unit [mc2], /* unit array */
mc2_reg, /* register array */
mc2_mod, /* modifier array */
1, /* number of units */
10, /* address radix */
31, /* address width */
1, /* address increment */
8, /* data radix */
16, /* data width */
NULL, /* examine routine */
NULL, /* deposit routine */
&mc_reset, /* reset routine */
NULL, /* boot routine */
NULL, /* attach routine */
NULL, /* detach routine */
&mc_dib [mc2], /* device information block pointer */
DEV_DISABLE | DEV_DIS | DEV_DEBUG, /* device flags */
0, /* debug control flags */
mc_deb, /* debug flag name array */
NULL, /* memory size change routine */
NULL /* logical device name */
};
static DEVICE *dptrs [CARD_COUNT] = { /* device pointer array, indexed by CARD_INDEX */
&mc1_dev,
&mc2_dev
};
/* Microcircuit interface.
The microcircuit interface is installed on the I/O bus and receives I/O
commands from the CPU and DMA/DCPC channels. In simulation, the asserted
signals on the bus are represented as bits in the inbound_signals set. Each
signal is processed sequentially in ascending numerical order. The outbound
signals and optional data value are returned after inbound signal processing
is complete.
In DIAGNOSTIC mode, the interface behaves as though a loopback connector
is installed. In addition, for all accesses other than DMA cycles for a 2115 or
2116 CPU, it behaves as though jumpers W1-W3 are installed in locations
C-B-B, respectively. In this case, the Flag flip-flop sets one I/O cycle
after STC signal assertion. For 2115/2116 DMA cycles, it behaves as though
the jumpers are installed in locations B-C-A, which suppresses setting the
Flag flip-flop.
Because there is no attached peripheral, the Flag flip-flop never sets in
DEVICE mode in response to a programmed STC instruction.
Implementation notes:
1. The B-C-B jumper setting used by the HP 24185 DMA diagnostic causes the
Flag flip-flop to set two I/O cycles after STC assertion. However, the
diagnostic executes an STC,C instruction at that point that clears the
Flag flip-flop explictly. This has the same effect as if the Flag had
never set and so is functionally identical to the B-C-A jumper setting.
2. The 12195 DMA diagnostic depends on the input data register being clocked
by an STC instruction. W1 = B and W3 = A asserts Device Command positive
true and strobes the input register on the positive edge of Device Flag.
This is simulated by copying the output data register to the input data
register in the STC handler if DIAGNOSTIC mode is enabled.
*/
static SIGNALS_VALUE mc_interface (const DIB *dibptr, INBOUND_SET inbound_signals, HP_WORD inbound_value)
{
const CARD_INDEX card = (CARD_INDEX) dibptr->card_index; /* the card selector */
UNIT * const uptr = &(mc_unit [card]); /* the associated unit pointer */
INBOUND_SIGNAL signal;
INBOUND_SET working_set = inbound_signals;
SIGNALS_VALUE outbound = { ioNONE, 0 };
t_bool irq_enabled = FALSE;
while (working_set) { /* while signals remain */
signal = IONEXTSIG (working_set); /* isolate the next signal */
switch (signal) { /* dispatch the I/O signal */
case ioCLF: /* Clear Flag flip-flop */
mc [card].flag_buffer = CLEAR; /* clear the flag buffer */
mc [card].flag = CLEAR; /* and flag flip-flops */
break;
case ioSTF: /* Set Flag flip-flop */
mc [card].flag_buffer = SET; /* set the flag buffer flip-flop */
break;
case ioENF: /* Enable Flag */
if (mc [card].flag_buffer == SET) /* if the flag buffer flip-flop is set */
mc [card].flag = SET; /* then set the flag flip-flop */
break;
case ioSFC: /* Skip if Flag is Clear */
if (mc [card].flag == CLEAR) /* if the flag flip-flop is clear */
outbound.signals |= ioSKF; /* then assert the Skip on Flag signal */
break;
case ioSFS: /* Skip if Flag is Set */
if (mc [card].flag == SET) /* if the flag flip-flop is set */
outbound.signals |= ioSKF; /* then assert the Skip on Flag signal */
break;
case ioIOI: /* I/O data input */
outbound.value = mc [card].input_data; /* set the outbound data value from the input buffer */
break;
case ioIOO: /* I/O data output */
mc [card].output_data = inbound_value; /* store the inbound data value in the output buffer */
break;
case ioPOPIO: /* Power-On Preset to I/O */
mc [card].flag_buffer = SET; /* set the flag buffer flip-flop */
mc [card].output_data = 0; /* and clear the output register */
break;
case ioCRS: /* Control Reset */
mc [card].control = CLEAR; /* clear the control flip-flop */
mc [card].command = CLEAR; /* and the command flip-flop */
sim_cancel (uptr); /* cancel any operation in progress */
break;
case ioCLC: /* Clear Control flip-flop */
mc [card].control = CLEAR; /* clear the control flip-flop */
mc [card].command = CLEAR; /* and the command flip-flop */
if (sim_activate_time (uptr) > LOOPBACK_DELAY) /* if Device Flag assertion is still in the future */
sim_cancel (uptr); /* then cancel it */
break;
case ioSTC: /* Set Control flip-flop */
mc [card].control = SET; /* set the control flip-flop */
mc [card].command = SET; /* and the command flip-flop */
if (uptr->flags & UNIT_DIAG /* if the card is in diagnostic mode */
&& (cpu_configuration & ~(CPU_2116 | CPU_2115) /* and this is not a 2116 or 2115 CPU */
|| (inbound_signals & (ioIOI | ioIOO)) == ioNONE)) { /* or not a DMA cycle */
mc [card].input_data = mc [card].output_data; /* then loop the data back */
tpprintf (dptrs [card], TRACE_XFER, "Output data word %06o looped back to input\n",
mc [card].output_data);
sim_activate_abs (uptr, LOOPBACK_DELAY); /* schedule Device Flag assertion */
}
break;
case ioSIR: /* Set Interrupt Request */
if (mc [card].control & mc [card].flag) /* if the control and flag flip-flops are set */
outbound.signals |= cnVALID; /* then deny PRL */
else /* otherwise */
outbound.signals |= cnPRL | cnVALID; /* conditionally assert PRL */
if (mc [card].control & mc [card].flag /* if the control and flag */
& mc [card].flag_buffer) /* and flag buffer flip-flops are set */
outbound.signals |= cnIRQ | cnVALID; /* then conditionally assert IRQ */
if (mc [card].flag == SET) /* if the flag flip-flop is set */
outbound.signals |= ioSRQ; /* then assert SRQ */
break;
case ioIAK: /* Interrupt Acknowledge */
mc [card].flag_buffer = CLEAR; /* clear the flag buffer flip-flop */
break;
case ioIEN: /* Interrupt Enable */
irq_enabled = TRUE; /* permit IRQ to be asserted */
break;
case ioPRH: /* Priority High */
if (irq_enabled && outbound.signals & cnIRQ) /* if IRQ is enabled and conditionally asserted */
outbound.signals |= ioIRQ | ioFLG; /* then assert IRQ and FLG */
if (!irq_enabled || outbound.signals & cnPRL) /* if IRQ is disabled or PRL is conditionally asserted */
outbound.signals |= ioPRL; /* then assert it unconditionally */
break;
case ioEDT: /* not used by this interface */
case ioPON: /* not used by this interface */
break;
}
IOCLEARSIG (working_set, signal); /* remove the current signal from the set */
} /* and continue until all signals are processed */
return outbound; /* return the outbound signals and value */
}
/* Unit service */
static t_stat mc_service (UNIT *uptr)
{
const CARD_INDEX card = (CARD_INDEX) (uptr - mc_unit); /* the card selector */
if (uptr->flags & UNIT_DIAG) { /* if the card is in diagnostic mode */
mc [card].command = CLEAR; /* then clear the command flip-flop */
mc [card].flag_buffer = SET; /* and set the flag buffer */
io_assert (dptrs [card], ioa_ENF); /* and flag flip-flops */
}
return SCPE_OK;
}
/* Reset routine */
static t_stat mc_reset (DEVICE *dptr)
{
UNIT * const uptr = dptr->units; /* a pointer to the device's unit */
if (sim_switches & SWMASK ('P')) { /* if this is the power-on reset */
} /* then perform any power-up processing */
io_assert (dptr, ioa_POPIO); /* PRESET the device */
sim_cancel (uptr); /* cancel any I/O in progress */
return SCPE_OK;
}

2388
HP2100/hp2100_mem.c Normal file

File diff suppressed because it is too large Load Diff

3226
HP2100/hp2100_mpx.c Normal file

File diff suppressed because it is too large Load Diff

1685
HP2100/hp2100_ms.c Normal file

File diff suppressed because it is too large Load Diff

898
HP2100/hp2100_mt.c Normal file
View File

@@ -0,0 +1,898 @@
/* hp2100_mt.c: HP 2100 12559C 9-Track Magnetic Tape Unit Interface
Copyright (c) 1993-2016, Robert M. Supnik
Copyright (c) 2017-2019, J. David Bryan
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the names of the authors shall not be
used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from the authors.
MT 12559C 9-Track Magnetic Tape Unit Interface
18-Mar-19 JDB Reordered SCP includes
24-Jan-19 JDB Removed DEV_TAPE from DEVICE flags
11-Jul-18 JDB Revised I/O model
20-Jul-17 JDB Removed "mtc_stopioe" variable and register
13-Mar-17 JDB Deprecated LOCKED/WRITEENABLED for ATTACH -R
13-May-16 JDB Modified for revised SCP API function parameter types
24-Dec-14 JDB Added casts for explicit downward conversions
10-Jan-13 MP Added DEV_TAPE to DEVICE flags
09-May-12 JDB Separated assignments from conditional expressions
25-Mar-12 JDB Removed redundant MTAB_VUN from "format" MTAB entry
10-Feb-12 JDB Deprecated DEVNO in favor of SC
28-Mar-11 JDB Tidied up signal handling
29-Oct-10 JDB Fixed command scanning error in mtcio ioIOO handler
26-Oct-10 JDB Changed I/O signal handler for revised signal model
04-Sep-08 JDB Fixed missing flag after CLR command
02-Sep-08 JDB Moved write enable and format commands from MTD to MTC
26-Jun-08 JDB Rewrote device I/O to model backplane signals
28-Dec-06 JDB Added ioCRS state to I/O decoders
07-Oct-04 JDB Allow enable/disable from either device
14-Aug-04 RMS Modified handling of end of medium (suggested by Dave Bryan)
06-Jul-04 RMS Fixed spurious timing error after CLC (found by Dave Bryan)
26-Apr-04 RMS Fixed SFS x,C and SFC x,C
Implemented DMA SRQ (follows FLG)
21-Dec-03 RMS Adjusted msc_ctime for TSB (from Mike Gemeny)
25-Apr-03 RMS Revised for extended file support
28-Mar-03 RMS Added multiformat support
28-Feb-03 RMS Revised for magtape library
30-Sep-02 RMS Revamped error handling
28-Aug-02 RMS Added end of medium support
30-May-02 RMS Widened POS to 32b
22-Apr-02 RMS Added maximum record length test
20-Jan-02 RMS Fixed bug on last character write
03-Dec-01 RMS Added read only unit, extended SET/SHOW support
07-Sep-01 RMS Moved function prototypes
30-Nov-00 RMS Made variable names unique
04-Oct-98 RMS V2.4 magtape format
References:
- 12559A 9-Track Magnetic Tape Unit Interface Kit Operating and Service Manual
(12559-9001, July 1970)
- SIMH Magtape Representation and Handling
(Bob Supnik, 30-Aug-2006)
The 3030 was one of HP's earliest tape drives. The 12559A controller
supported a single 800 bpi, 9-track drive, operating at 75 inches per second.
It had two unusual characteristics:
- The controller accepted only one byte per I/O word, rather than packing
two bytes per word.
- The drive could not read or write fewer than 12 bytes per record.
The first behavior meant that DMA operation required the byte-unpacking
feature of the 12578A DMA card for the 2116 computer. The second meant that
software drivers had to pad short records with blanks or nulls.
Implementation notes:
1. The HP 3030 Magnetic Tape Subsystem diagnostic, part number 20433-60001,
has never been located, so this simulator has not been fully tested. It
does pass a functional test under DOS-III using driver DVR22.
*/
#include "sim_defs.h"
#include "sim_tape.h"
#include "hp2100_defs.h"
#include "hp2100_io.h"
#define DB_V_SIZE 16 /* max data buf */
#define DBSIZE (1 << DB_V_SIZE) /* max data cmd */
/* Command - mtc_fnc */
#define FNC_CLR 0300 /* clear */
#define FNC_WC 0031 /* write */
#define FNC_RC 0023 /* read */
#define FNC_GAP 0011 /* write gap */
#define FNC_FSR 0003 /* forward space */
#define FNC_BSR 0041 /* backward space */
#define FNC_REW 0201 /* rewind */
#define FNC_RWS 0101 /* rewind and offline */
#define FNC_WFM 0035 /* write file mark */
/* Status - stored in mtc_sta, (d) = dynamic */
#define STA_LOCAL 0400 /* local (d) */
#define STA_EOF 0200 /* end of file */
#define STA_BOT 0100 /* beginning of tape */
#define STA_EOT 0040 /* end of tape */
#define STA_TIM 0020 /* timing error */
#define STA_REJ 0010 /* programming error */
#define STA_WLK 0004 /* write locked (d) */
#define STA_PAR 0002 /* parity error */
#define STA_BUSY 0001 /* busy (d) */
/* Interface state */
typedef struct {
FLIP_FLOP control; /* control flip-flop */
FLIP_FLOP flag; /* flag flip-flop */
FLIP_FLOP flag_buffer; /* flag buffer flip-flop */
} CARD_STATE;
static CARD_STATE mtd; /* data per-card state */
static CARD_STATE mtc; /* command per-card state */
/* Interface local SCP support routines */
static INTERFACE mtd_interface;
static INTERFACE mtc_interface;
static int32 mtc_fnc = 0; /* function */
static int32 mtc_sta = 0; /* status register */
static int32 mtc_dtf = 0; /* data xfer flop */
static int32 mtc_1st = 0; /* first svc flop */
static int32 mtc_ctime = 40; /* command wait */
static int32 mtc_gtime = 1000; /* gap stop time */
static int32 mtc_xtime = 15; /* data xfer time */
static uint8 mtxb[DBSIZE] = { 0 }; /* data buffer */
static t_mtrlnt mt_ptr = 0, mt_max = 0; /* buffer ptrs */
static const uint32 mtc_cmd[] = {
FNC_WC, FNC_RC, FNC_GAP, FNC_FSR, FNC_BSR, FNC_REW, FNC_RWS, FNC_WFM };
static const uint32 mtc_cmd_count = sizeof (mtc_cmd) / sizeof (mtc_cmd[0]);
static t_stat mtc_svc (UNIT *uptr);
static t_stat mt_reset (DEVICE *dptr);
static t_stat mtc_attach (UNIT *uptr, char *cptr);
static t_stat mtc_detach (UNIT *uptr);
static t_stat mt_map_err (UNIT *uptr, t_stat st);
static t_stat mt_clear (void);
/* Device information blocks */
static DIB mt_dib [] = {
{ &mtd_interface, /* the device's I/O interface function pointer */
MTD, /* the device's select code (02-77) */
0, /* the card index */
"12559C 9-Track Magnetic Tape Unit Interface Data Channel", /* the card description */
NULL }, /* the ROM description */
{ &mtc_interface, /* the device's I/O interface function pointer */
MTC, /* the device's select code (02-77) */
0, /* the card index */
"12559C 9-Track Magnetic Tape Unit Interface Command Channel", /* the card description */
NULL } /* the ROM description */
};
#define mtd_dib mt_dib [0]
#define mtc_dib mt_dib [1]
/* Data card SCP data structures */
/* Unit list */
static UNIT mtd_unit [] = {
/* Event Routine Unit Flags Capacity Delay */
/* ------------- ---------- -------- ----- */
{ UDATA (NULL, 0, 0) }
};
/* Register list */
static REG mtd_reg [] = {
{ FLDATA (FLG, mtd.flag, 0) },
{ FLDATA (FBF, mtd.flag_buffer, 0) },
{ BRDATA (DBUF, mtxb, 8, 8, DBSIZE) },
{ DRDATA (BPTR, mt_ptr, DB_V_SIZE + 1) },
{ DRDATA (BMAX, mt_max, DB_V_SIZE + 1) },
DIB_REGS (mtd_dib),
{ NULL }
};
/* Modifier list */
static MTAB mtd_mod [] = {
/* Entry Flags Value Print String Match String Validation Display Descriptor */
/* ------------------- ----- ------------ ------------ ------------ ------------- ---------------- */
{ MTAB_XDV, 2u, "SC", "SC", &hp_set_dib, &hp_show_dib, (void *) &mt_dib },
{ MTAB_XDV | MTAB_NMO, ~2u, "DEVNO", "DEVNO", &hp_set_dib, &hp_show_dib, (void *) &mt_dib },
{ 0 }
};
/* Debugging trace list */
static DEBTAB mt_deb [] = {
{ "IOBUS", TRACE_IOBUS }, /* trace I/O bus signals and data words received and returned */
{ NULL, 0 }
};
/* Device descriptor */
DEVICE mtd_dev = {
"MTD", /* device name */
mtd_unit, /* unit array */
mtd_reg, /* register array */
mtd_mod, /* modifier array */
1, /* number of units */
10, /* address radix */
16, /* address width */
1, /* address increment */
8, /* data radix */
8, /* data width */
NULL, /* examine routine */
NULL, /* deposit routine */
&mt_reset, /* reset routine */
NULL, /* boot routine */
NULL, /* attach routine */
NULL, /* detach routine */
&mtd_dib, /* device information block pointer */
DEV_DISABLE | DEV_DIS | DEV_DEBUG, /* device flags */
0, /* debug control flags */
mt_deb, /* debug flag name array */
NULL, /* memory size change routine */
NULL /* logical device name */
};
/* Command card SCP data structures */
/* Unit list */
#define UNIT_FLAGS (UNIT_ATTABLE | UNIT_ROABLE)
static UNIT mtc_unit [] = {
/* Event Routine Unit Flags Capacity Delay */
/* ------------- ---------- -------- ----- */
{ UDATA (&mtc_svc, UNIT_FLAGS, 0) },
};
/* Register list */
static REG mtc_reg [] = {
{ ORDATA (FNC, mtc_fnc, 8) },
{ ORDATA (STA, mtc_sta, 9) },
{ ORDATA (BUF, mtc_unit [0].buf, 8) },
{ FLDATA (CTL, mtc.control, 0) },
{ FLDATA (FLG, mtc.flag, 0) },
{ FLDATA (FBF, mtc.flag_buffer, 0) },
{ FLDATA (DTF, mtc_dtf, 0) },
{ FLDATA (FSVC, mtc_1st, 0) },
{ DRDATA (POS, mtc_unit [0].pos, T_ADDR_W), PV_LEFT },
{ DRDATA (CTIME, mtc_ctime, 24), REG_NZ + PV_LEFT },
{ DRDATA (GTIME, mtc_gtime, 24), REG_NZ + PV_LEFT },
{ DRDATA (XTIME, mtc_xtime, 24), REG_NZ + PV_LEFT },
DIB_REGS (mtc_dib),
{ NULL }
};
/* Modifier list.
The LOCKED and WRITEENABLED modifiers are deprecated. The supported method
of write-protecting a tape drive is to attach the tape image with the -R
(read-only) switch or by setting the host operating system's read-only
attribute on the tape image file. This simulates removing the write ring
from the tape reel before mounting it on the drive. There is no hardware
method of write-protecting a mounted and positioned tape reel.
Implementation notes:
1. The UNIT_RO modifier displays "write ring" if the flag is not set. There
is no corresponding entry for the opposite condition because "read only"
is automatically printed after the attached filename.
2. FORMAT is really a unit option, but as there is only one unit, it is
specified as MTAB_XDV so that SHOW MTC FORMAT is accepted, rather than
requiring SHOW MTC0 FORMAT.
*/
static MTAB mtc_mod [] = {
/* Mask Value Match Value Print String Match String Validation Display Descriptor */
/* ------------- ------------- ---------------- --------------- ------------ ------- ---------- */
{ UNIT_RO, 0, "write ring", NULL, NULL, NULL, NULL },
{ MTUF_WLK, 0, NULL, "WRITEENABLED", NULL, NULL, NULL },
{ MTUF_WLK, MTUF_WLK, NULL, "LOCKED", NULL, NULL, NULL },
/* Entry Flags Value Print String Match String Validation Display Descriptor */
/* ------------------- ----- ------------ ------------ ----------------- ------------------ ---------------- */
{ MTAB_XDV, 0, "FORMAT", "FORMAT", &sim_tape_set_fmt, &sim_tape_show_fmt, NULL },
{ MTAB_XDV, 2u, "SC", "SC", &hp_set_dib, &hp_show_dib, (void *) &mt_dib },
{ MTAB_XDV | MTAB_NMO, ~2u, "DEVNO", "DEVNO", &hp_set_dib, &hp_show_dib, (void *) &mt_dib },
{ 0 }
};
/* Device descriptor */
DEVICE mtc_dev = {
"MTC", /* device name */
mtc_unit, /* unit array */
mtc_reg, /* register array */
mtc_mod, /* modifier array */
1, /* number of units */
10, /* address radix */
31, /* address width */
1, /* address increment */
8, /* data radix */
8, /* data width */
NULL, /* examine routine */
NULL, /* deposit routine */
&mt_reset, /* reset routine */
NULL, /* boot routine */
&mtc_attach, /* attach routine */
&mtc_detach, /* detach routine */
&mtc_dib, /* device information block pointer */
DEV_DISABLE | DEV_DIS | DEV_DEBUG, /* device flags */
0, /* debug control flags */
mt_deb, /* debug flag name array */
NULL, /* memory size change routine */
NULL /* logical device name */
};
/* Data channel interface.
The 12559A data channel interface has a number of non-standard features:
- The card does not drive PRL or IRQ.
- The card does not respond to IAK.
- There is no control flip-flop; CLC resets the data transfer flip-flop.
- POPIO issues a CLR command and clears the flag and flag buffer flip-flops.
- CRS is not used.
Implementation notes:
1. The data channel has a flag buffer flip-flop (necessary for the proper
timing of the flag flip-flop), but the data channel does not interrupt,
so the flag buffer serves no other purpose.
*/
static SIGNALS_VALUE mtd_interface (const DIB *dibptr, INBOUND_SET inbound_signals, HP_WORD inbound_value)
{
INBOUND_SIGNAL signal;
INBOUND_SET working_set = inbound_signals;
SIGNALS_VALUE outbound = { ioNONE, 0 };
while (working_set) { /* while signals remain */
signal = IONEXTSIG (working_set); /* isolate the next signal */
switch (signal) { /* dispatch the I/O signal */
case ioCLF: /* Clear Flag flip-flop */
mtd.flag_buffer = CLEAR; /* reset the flag buffer */
mtd.flag = CLEAR; /* and flag flip-flops */
break;
case ioSTF: /* Set Flag flip-flop */
mtd.flag_buffer = SET; /* set the flag buffer flip-flop */
break;
case ioENF: /* Enable Flag */
if (mtd.flag_buffer == SET) /* if the flag buffer flip-flop is set */
mtd.flag = SET; /* then set the flag flip-flop */
break;
case ioSFC: /* Skip if Flag is Clear */
if (mtd.flag == CLEAR) /* if the flag flip-flop is clear */
outbound.signals |= ioSKF; /* then assert the Skip on Flag signal */
break;
case ioSFS: /* Skip if Flag is Set */
if (mtd.flag == SET) /* if the flag flip-flop is set */
outbound.signals |= ioSKF; /* then assert the Skip on Flag signal */
break;
case ioIOI: /* I/O data input */
outbound.value = mtc_unit [0].buf; /* merge in return status */
break;
case ioIOO: /* I/O data output */
mtc_unit [0].buf = inbound_value & D8_MASK; /* store data */
break;
case ioPOPIO: /* Power-On Preset to I/O */
mt_clear (); /* issue CLR to controller */
mtd.flag_buffer = CLEAR; /* clear the flag buffer flip-flop */
break;
case ioCLC: /* Clear Control flip-flop */
mtd.flag_buffer = CLEAR; /* reset the flag buffer */
mtd.flag = CLEAR; /* and flag flip-flops */
mtc_dtf = 0; /* clr xfer flop */
break;
case ioSIR: /* Set Interrupt Request */
if (mtd.flag == SET) /* if the flag flip-flop is set */
outbound.signals |= ioSRQ; /* then assert SRQ */
break;
case ioPRH: /* Priority High */
outbound.signals |= ioPRL | cnPRL | cnVALID; /* PRL is tied to PRH */
break;
case ioSTC: /* not used by this interface */
case ioCRS: /* not used by this interface */
case ioIAK: /* not used by this interface */
case ioIEN: /* not used by this interface */
case ioEDT: /* not used by this interface */
case ioPON: /* not used by this interface */
break;
}
IOCLEARSIG (working_set, signal); /* remove the current signal from the set */
} /* and continue until all signals are processed */
return outbound; /* return the outbound signals and value */
}
/* Command channel interface.
The 12559A command interface is reasonably standard, although POPIO clears,
rather than sets, the flag and flag buffer flip-flops. One unusual feature
is that commands are initiated when they are output to the interface with
OTA/B, rather than waiting until control is set with STC. STC simply enables
command-channel interrupts.
Implementation notes:
1. In hardware, the command channel card passes PRH to PRL. The data card
actually drives PRL with the command channel's control and flag states.
That is, the priority chain is broken at the data card, although the
command card is interrupting. This works in hardware, but we must break
PRL at the command card under simulation to allow the command card to
interrupt.
2. In hardware, the CLR command takes 5 milliseconds to complete. During
this time, the BUSY bit is set in the status word. Under simulation, we
complete immediately, and the BUSY bit never sets..
*/
static SIGNALS_VALUE mtc_interface (const DIB *dibptr, INBOUND_SET inbound_signals, HP_WORD inbound_value)
{
uint32 i, data;
int32 valid;
INBOUND_SIGNAL signal;
INBOUND_SET working_set = inbound_signals;
SIGNALS_VALUE outbound = { ioNONE, 0 };
t_bool irq_enabled = FALSE;
while (working_set) { /* while signals remain */
signal = IONEXTSIG (working_set); /* isolate the next signal */
switch (signal) { /* dispatch the I/O signal */
case ioCLF: /* Clear Flag flip-flop */
mtc.flag_buffer = CLEAR; /* reset the flag buffer */
mtc.flag = CLEAR; /* and flag flip-flops */
break;
case ioSTF: /* Set Flag flip-flop */
mtc.flag_buffer = SET; /* set the flag buffer flip-flop */
break;
case ioENF: /* Enable Flag */
if (mtc.flag_buffer == SET) /* if the flag buffer flip-flop is set */
mtc.flag = SET; /* then set the flag flip-flop */
break;
case ioSFC: /* Skip if Flag is Clear */
if (mtc.flag == CLEAR) /* if the flag flip-flop is clear */
outbound.signals |= ioSKF; /* then assert the Skip on Flag signal */
break;
case ioSFS: /* Skip if Flag is Set */
if (mtc.flag == SET) /* if the flag flip-flop is set */
outbound.signals |= ioSKF; /* then assert the Skip on Flag signal */
break;
case ioIOI: /* I/O data input */
outbound.value = mtc_sta & ~(STA_LOCAL | STA_WLK | STA_BUSY);
if (mtc_unit [0].flags & UNIT_ATT) { /* construct status */
if (sim_is_active (mtc_unit))
outbound.value |= STA_BUSY;
if (sim_tape_wrp (mtc_unit))
outbound.value |= STA_WLK;
}
else
outbound.value |= STA_BUSY | STA_LOCAL;
break;
case ioIOO: /* I/O data output */
data = inbound_value & D8_MASK; /* only the lower 8 bits are connected */
mtc_sta = mtc_sta & ~STA_REJ; /* clear reject */
if (data == FNC_CLR) { /* clear? */
mt_clear (); /* send CLR to controller */
mtd.flag_buffer = mtd.flag = CLEAR; /* clear data flag buffer and flag */
mtc.flag_buffer = mtc.flag = SET; /* set command flag buffer and flag */
break; /* command completes immediately */
}
for (i = valid = 0; i < mtc_cmd_count; i++) /* is fnc valid? */
if (data == mtc_cmd[i]) {
valid = 1;
break;
}
if (!valid || sim_is_active (mtc_unit) || /* is cmd valid? */
((mtc_sta & STA_BOT) && (data == FNC_BSR)) ||
(sim_tape_wrp (mtc_unit) &&
((data == FNC_WC) || (data == FNC_GAP) || (data == FNC_WFM))))
mtc_sta = mtc_sta | STA_REJ;
else {
sim_activate (mtc_unit, mtc_ctime); /* start tape */
mtc_fnc = data; /* save function */
mtc_sta = STA_BUSY; /* unit busy */
mt_ptr = 0; /* init buffer ptr */
mtd.flag_buffer = mtd.flag = CLEAR; /* clear data flag buffer and flag */
mtc.flag_buffer = mtc.flag = CLEAR; /* and command flag buffer and flag */
mtc_1st = 1; /* set 1st flop */
mtc_dtf = 1; /* set xfer flop */
}
break;
case ioPOPIO: /* Power-On Preset to I/O */
mtc.flag_buffer = CLEAR; /* clear the flag buffer flip-flop */
mtc.flag = CLEAR; /* and flag flip-flops */
break;
case ioCRS: /* Control Reset */
mtc.control = CLEAR; /* clear the control flip-flop */
break;
case ioCLC: /* Clear Control flip-flop */
mtc.control = CLEAR;
break;
case ioSTC: /* Set Control flip-flop */
mtc.control = SET;
break;
case ioSIR: /* Set Interrupt Request */
if (mtc.control & mtc.flag) /* if the control and flag flip-flops are set */
outbound.signals |= cnVALID; /* then deny PRL */
else /* otherwise */
outbound.signals |= cnPRL | cnVALID; /* conditionally assert PRL */
if (mtc.control & mtc.flag & mtc.flag_buffer) /* if the control, flag, and flag buffer flip-flops are set */
outbound.signals |= cnIRQ | cnVALID; /* then conditionally assert IRQ */
if (mtc.flag == SET) /* if the flag flip-flop is set */
outbound.signals |= ioSRQ; /* then assert SRQ */
break;
case ioIAK: /* Interrupt Acknowledge */
mtc.flag_buffer = CLEAR; /* clear the flag buffer flip-flop */
break;
case ioIEN: /* Interrupt Enable */
irq_enabled = TRUE; /* permit IRQ to be asserted */
break;
case ioPRH: /* Priority High */
if (irq_enabled && outbound.signals & cnIRQ) /* if IRQ is enabled and conditionally asserted */
outbound.signals |= ioIRQ | ioFLG; /* then assert IRQ and FLG */
if (!irq_enabled || outbound.signals & cnPRL) /* if IRQ is disabled or PRL is conditionally asserted */
outbound.signals |= ioPRL; /* then assert it unconditionally */
break;
case ioEDT: /* not used by this interface */
case ioPON: /* not used by this interface */
break;
}
IOCLEARSIG (working_set, signal); /* remove the current signal from the set */
} /* and continue until all signals are processed */
return outbound; /* return the outbound signals and value */
}
/* Unit service
If rewind done, reposition to start of tape, set status
else, do operation, set done, interrupt
Can't be write locked, can only write lock detached unit
*/
static t_stat mtc_svc (UNIT *uptr)
{
t_mtrlnt tbc;
t_stat st, r = SCPE_OK;
if ((mtc_unit [0].flags & UNIT_ATT) == 0) { /* offline? */
mtc_sta = STA_LOCAL | STA_REJ; /* rejected */
mtc.flag_buffer = SET; /* set the flag buffer */
io_assert (&mtc_dev, ioa_ENF); /* and flag flip-flops */
return SCPE_OK;
}
switch (mtc_fnc) { /* case on function */
case FNC_REW: /* rewind */
sim_tape_rewind (uptr); /* BOT */
mtc_sta = STA_BOT; /* update status */
break;
case FNC_RWS: /* rewind and offline */
sim_tape_rewind (uptr); /* clear position */
return sim_tape_detach (uptr); /* don't set cch flg */
case FNC_WFM: /* write file mark */
st = sim_tape_wrtmk (uptr); /* write tmk */
if (st != MTSE_OK) /* error? */
r = mt_map_err (uptr, st); /* map error */
mtc_sta = STA_EOF; /* set EOF status */
break;
case FNC_GAP: /* erase gap */
break;
case FNC_FSR: /* space forward */
st = sim_tape_sprecf (uptr, &tbc); /* space rec fwd */
if (st != MTSE_OK) /* error? */
r = mt_map_err (uptr, st); /* map error */
break;
case FNC_BSR: /* space reverse */
st = sim_tape_sprecr (uptr, &tbc); /* space rec rev */
if (st != MTSE_OK) /* error? */
r = mt_map_err (uptr, st); /* map error */
break;
case FNC_RC: /* read */
if (mtc_1st) { /* first svc? */
mtc_1st = mt_ptr = 0; /* clr 1st flop */
st = sim_tape_rdrecf (uptr, mtxb, &mt_max, DBSIZE); /* read rec */
if (st == MTSE_RECE) mtc_sta = mtc_sta | STA_PAR; /* rec in err? */
else if (st != MTSE_OK) { /* other error? */
r = mt_map_err (uptr, st); /* map error */
if (r == SCPE_OK) { /* recoverable? */
sim_activate (uptr, mtc_gtime); /* sched IRG */
mtc_fnc = 0; /* NOP func */
return SCPE_OK;
}
break; /* non-recov, done */
}
if (mt_max < 12) { /* record too short? */
mtc_sta = mtc_sta | STA_PAR; /* set flag */
break;
}
}
if (mtc_dtf && (mt_ptr < mt_max)) { /* more chars? */
if (mtd.flag) mtc_sta = mtc_sta | STA_TIM;
mtc_unit [0].buf = mtxb [mt_ptr++]; /* fetch next */
mtd.flag_buffer = SET; /* set the flag buffer */
io_assert (&mtd_dev, ioa_ENF); /* and flag flip-flops */
sim_activate (uptr, mtc_xtime); /* re-activate */
return SCPE_OK;
}
sim_activate (uptr, mtc_gtime); /* schedule gap */
mtc_fnc = 0; /* nop */
return SCPE_OK;
case FNC_WC: /* write */
if (mtc_1st) mtc_1st = 0; /* no xfr on first */
else {
if (mt_ptr < DBSIZE) { /* room in buffer? */
mtxb[mt_ptr++] = (uint8) mtc_unit [0].buf;
mtc_sta = mtc_sta & ~STA_BOT; /* clear BOT */
}
else mtc_sta = mtc_sta | STA_PAR;
}
if (mtc_dtf) { /* xfer flop set? */
mtd.flag_buffer = SET; /* set the flag buffer */
io_assert (&mtd_dev, ioa_ENF); /* and flag flip-flops */
sim_activate (uptr, mtc_xtime); /* re-activate */
return SCPE_OK;
}
if (mt_ptr) { /* write buffer */
st = sim_tape_wrrecf (uptr, mtxb, mt_ptr); /* write */
if (st != MTSE_OK) { /* error? */
r = mt_map_err (uptr, st); /* map error */
break; /* done */
}
}
sim_activate (uptr, mtc_gtime); /* schedule gap */
mtc_fnc = 0; /* nop */
return SCPE_OK;
default: /* unknown */
break;
}
mtc.flag_buffer = SET; /* set the flag buffer */
io_assert (&mtc_dev, ioa_ENF); /* and flag flip-flops */
mtc_sta = mtc_sta & ~STA_BUSY; /* not busy */
return r;
}
/* Map tape error status */
static t_stat mt_map_err (UNIT *uptr, t_stat st)
{
switch (st) {
case MTSE_FMT: /* illegal fmt */
case MTSE_UNATT: /* unattached */
mtc_sta = mtc_sta | STA_REJ; /* reject */
return SCPE_IERR; /* never get here! */
case MTSE_OK: /* no error */
return SCPE_IERR; /* never get here! */
case MTSE_EOM: /* end of medium */
case MTSE_TMK: /* end of file */
mtc_sta = mtc_sta | STA_EOF; /* eof */
break;
case MTSE_IOERR: /* IO error */
mtc_sta = mtc_sta | STA_PAR; /* error */
return SCPE_IOERR;
break;
case MTSE_INVRL: /* invalid rec lnt */
mtc_sta = mtc_sta | STA_PAR;
return SCPE_MTRLNT;
case MTSE_RECE: /* record in error */
mtc_sta = mtc_sta | STA_PAR; /* error */
break;
case MTSE_BOT: /* reverse into BOT */
mtc_sta = mtc_sta | STA_BOT; /* set status */
break;
case MTSE_WRP: /* write protect */
mtc_sta = mtc_sta | STA_REJ; /* reject */
break;
}
return SCPE_OK;
}
/* Controller clear */
static t_stat mt_clear (void)
{
t_stat st;
if (sim_is_active (mtc_unit) && /* write in prog? */
(mtc_fnc == FNC_WC) && (mt_ptr > 0)) { /* yes, bad rec */
st = sim_tape_wrrecf (mtc_unit, mtxb, mt_ptr | MTR_ERF);
if (st != MTSE_OK)
mt_map_err (mtc_unit, st);
}
if (((mtc_fnc == FNC_REW) || (mtc_fnc == FNC_RWS)) && sim_is_active (mtc_unit))
sim_cancel (mtc_unit);
mtc_1st = mtc_dtf = 0;
mtc_sta = mtc_sta & STA_BOT;
return SCPE_OK;
}
/* Reset routine */
static t_stat mt_reset (DEVICE *dptr)
{
hp_enbdis_pair (dptr, /* make pair cons */
(dptr == &mtd_dev) ? &mtc_dev : &mtd_dev);
io_assert (dptr, ioa_POPIO); /* PRESET the device */
mtc_fnc = 0;
mtc_1st = mtc_dtf = 0;
sim_cancel (mtc_unit); /* cancel activity */
sim_tape_reset (mtc_unit);
if (mtc_unit [0].flags & UNIT_ATT)
mtc_sta = (sim_tape_bot (mtc_unit)? STA_BOT: 0) |
(sim_tape_wrp (mtc_unit)? STA_WLK: 0);
else
mtc_sta = STA_LOCAL | STA_BUSY;
return SCPE_OK;
}
/* Attach routine */
static t_stat mtc_attach (UNIT *uptr, char *cptr)
{
t_stat r;
r = sim_tape_attach (uptr, cptr); /* attach unit */
if (r != SCPE_OK) return r; /* update status */
mtc_sta = STA_BOT;
return r;
}
/* Detach routine */
static t_stat mtc_detach (UNIT* uptr)
{
mtc_sta = 0; /* update status */
return sim_tape_detach (uptr); /* detach unit */
}

2019
HP2100/hp2100_mux.c Normal file

File diff suppressed because it is too large Load Diff

439
HP2100/hp2100_pif.c Normal file
View File

@@ -0,0 +1,439 @@
/* hp2100_pif.c: HP 12620A/12936A Privileged Interrupt Fence simulator
Copyright (c) 2008-2018, J. David Bryan
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the name of the author shall not be
used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from the author.
PIF 12620A/12936A Privileged Interrupt Fence
11-Jun-18 JDB Revised I/O model
15-Mar-17 JDB Trace flags are now global
11-Mar-17 JDB Revised the trace outputs
17-Jan-17 JDB Changed "hp_---sc" and "hp_---dev" to "hp_---_dib"
13-May-16 JDB Modified for revised SCP API function parameter types
10-Feb-12 JDB Deprecated DEVNO in favor of SC
28-Mar-11 JDB Tidied up signal handling
26-Oct-10 JDB Changed I/O signal handler for revised signal model
26-Jun-08 JDB Rewrote device I/O to model backplane signals
18-Jun-08 JDB Created PIF device
References:
- 12620A Breadboard Interface Kit Operating and Service Manual
(12620-90001, May 1978)
- 12936A Privileged Interrupt Fence Accessory Installation and Service Manual
(12936-90001, March 1974)
The Privileged Interupt Fence (PIF) was used in DOS and RTE systems to
provide privileged interrupt capability. In non-privileged systems, DOS and
RTE vectored all interrupts though the Central Interrupt Control (CIC)
routine. Within CIC, the interrupt system was turned off, the interrupt was
categorized, the associated driver was identified and mapped into logical
memory (if necessary), and the driver entered to handle the device service.
When the driver exited, the interrupt system was turned on before returning
to the point of interruption in the user's program. In addition, the DOS and
RTE operating systems themselves executed with the interrupt system off, as
they were not reentrant.
This process proved too lengthy for certain devices, which would lose
interrupts or be forced to limit I/O speeds as a result. To allow faster
service, a driver could be written as a "privileged" driver and generated
into a privileged system. A privileged system operated with the interrupt
system on when handling unprivileged device interrupts or executing within
the operating system. The PIF card was installed in the I/O backplane to
separate privileged from unprivileged devices by controlling the interrupt
priority chain signal (PRL) to lower-priority devices. The privileged cards
located below the fence were allowed to interrupt the service routines of the
unprivileged cards that were located above the fence.
When an unprivileged device interrupted, CIC would be entered as usual, and
the interrupt system would be turned off. However, after the system state
was saved, the PIF would be configured to break the priority chain (deny
PRL), so that subsequent interrupts from all unprivileged devices would be
deferred. Then the interrupt system would be turned on before normal CIC
processing continued. Interrupts from additional unprivileged devices would
be held off by the PIF until the driver completed and CIC returned, just as
in a non-privileged system.
However, if a privileged device interrupted, the interrupt would be allowed,
because the interrupt system was on, and the priority chain was intact for
the devices below the fence. A privileged device bypassed CIC and entered
the associated device driver directly, and this would occur even if an
unprivileged device driver or the operating system itself were executing.
This provided very fast interrupt service time.
HP produced two PIF cards: the 12936A Privileged Interrupt Fence Accessory
for DOS, and the 12620A Breadboard Interface for RTE. They behaved quite
differently and were not interchangeable.
The 12620A had the standard control and flag circuitry. It behaved as most
cards did; setting control and flag together lowered PRL and generated an
interrupt. The control and flag flip-flops were set and cleared with STC/CLC
and STF/CLF instructions. The SFS/SFC instructions could be used to test the
flag state.
The 12936A had a unique behavior. Setting either control or flag lowered
PRL. An interrupt occurred when flag was set and control was clear. The
control flip-flop was controlled with STC/CLC. The flag flip-flop was set
with OTA/B and cleared with CLF. SFC and SFS were not implemented and never
skipped.
*/
#include "hp2100_defs.h"
#include "hp2100_io.h"
/* Device flags */
#define DEV_V_12936 (DEV_V_UF + 0) /* 12936A card */
#define DEV_12936 (1 << DEV_V_12936)
/* Interface state */
typedef struct {
FLIP_FLOP control; /* control flip-flop */
FLIP_FLOP flag; /* flag flip-flop */
FLIP_FLOP flag_buffer; /* flag buffer flip-flop */
} CARD_STATE;
static CARD_STATE pif; /* per-card state */
/* Interface local SCP support routines */
static INTERFACE pif_interface;
/* Interface local SCP support routines */
static t_stat pif_reset (DEVICE *dptr);
static t_stat pif_set_card (UNIT *uptr, int32 val, char *cptr, void *desc);
static t_stat pif_show_card (FILE *st, UNIT *uptr, int32 val, void *desc);
/* Interface SCP data structures */
/* Device information block */
static DIB pif_dib = {
&pif_interface, /* the device's I/O interface function pointer */
PIF, /* the device's select code (02-77) */
0, /* the card index */
"12620A/12936A Privileged Interrupt Fence", /* the card description */
NULL /* the ROM description */
};
/* Unit list.
Implementation notes:
1. The SIMH developer's manual says that a device's unit list may be NULL.
However, if this is done, the register state cannot be examined or
altered via SCP. To work around this problem, we define a dummy unit
that is not used otherwise.
*/
static UNIT pif_unit [] = {
{ UDATA (NULL, 0, 0) }
};
/* Register list */
static REG pif_reg [] = {
/* Macro Name Location Offset */
/* ------ ------ ------------------- ------ */
{ FLDATA (CTL, pif.control, 0) },
{ FLDATA (FLG, pif.flag, 0) },
{ FLDATA (FBF, pif.flag_buffer, 0) },
DIB_REGS (pif_dib),
{ NULL }
};
/* Modifier list */
static MTAB pif_mod [] = {
/* Entry Flags Value Print String Match String Validation Display Descriptor */
/* ------------------- ----- ------------ ------------ -------------- -------------- ----------------- */
{ MTAB_XDV, 0, NULL, "12620A", &pif_set_card, NULL, NULL },
{ MTAB_XDV, 1, NULL, "12936A", &pif_set_card, NULL, NULL },
{ MTAB_XDV, 0, "TYPE", NULL, NULL, &pif_show_card, NULL },
{ MTAB_XDV, 1u, "SC", "SC", &hp_set_dib, &hp_show_dib, (void *) &pif_dib },
{ MTAB_XDV | MTAB_NMO, ~1u, "DEVNO", "DEVNO", &hp_set_dib, &hp_show_dib, (void *) &pif_dib },
{ 0 }
};
/* Debugging trace list */
static DEBTAB pif_deb [] = {
{ "CMD", TRACE_CMD }, /* interface commands */
{ "IOBUS", TRACE_IOBUS }, /* interface I/O bus signals and data words */
{ NULL, 0 }
};
/* Device descriptor */
DEVICE pif_dev = {
"PIF", /* device name */
pif_unit, /* unit array */
pif_reg, /* register array */
pif_mod, /* modifier array */
1, /* number of units */
10, /* address radix */
31, /* address width */
1, /* address increment */
8, /* data radix */
8, /* data width */
NULL, /* examine routine */
NULL, /* deposit routine */
&pif_reset, /* reset routine */
NULL, /* boot routine */
NULL, /* attach routine */
NULL, /* detach routine */
&pif_dib, /* device information block */
DEV_DISABLE | DEV_DEBUG, /* device flags */
0, /* debug control flags */
pif_deb, /* debug flag name table */
NULL, /* memory size change routine */
NULL }; /* logical device name */
/* Interface local SCP support routines */
/* Privileged interrupt fence interface.
Operation of the 12620A and the 12936A is different. The I/O responses of
the two cards are summarized below:
Signal 12620A Action 12936A Action
------ -------------------- --------------------
POPIO Set FBF, FLG Clear FBF, FLG
CRS Clear CTL Clear CTL
CLC Clear CTL Clear CTL
STC Set CTL Set CTL
CLF Clear FBF, FLG Clear FBF, FLG
STF Set FBF, FLG none
SFC Skip if FLG clear none
SFS Skip if FLG set none
IOI none none
IOO none Set FBF, FLG
PRL ~(CTL * FLG) ~(CTL + FLG)
IRQ CTL * FLG * FBF ~CTL * FLG * FBF
IAK Clear FBF Clear FBF
SRQ Follows FLG Not driven
Note that PRL and IRQ are non-standard for the 12936A.
*/
static SIGNALS_VALUE pif_interface (const DIB *dibptr, INBOUND_SET inbound_signals, HP_WORD inbound_value)
{
const t_bool is_rte_pif = (pif_dev.flags & DEV_12936) == 0; /* TRUE if 12620A card */
INBOUND_SIGNAL signal;
INBOUND_SET working_set = inbound_signals;
SIGNALS_VALUE outbound = { ioNONE, 0 };
t_bool irq_enabled = FALSE;
while (working_set) {
signal = IONEXTSIG (working_set); /* isolate the next signal */
switch (signal) { /* dispatch I/O signal */
case ioCLF: /* clear flag flip-flop */
pif.flag_buffer = CLEAR; /* clear flag buffer and flag */
pif.flag = CLEAR;
break;
case ioSTF: /* set flag flip-flop */
if (is_rte_pif) /* RTE PIF? */
pif.flag_buffer = SET; /* set flag buffer */
break;
case ioENF: /* enable flag */
if (pif.flag_buffer == SET) /* if the flag buffer flip-flop is set */
pif.flag = SET; /* then set the flag flip-flop */
break;
case ioSFC: /* skip if flag is clear */
if (is_rte_pif && pif.flag == CLEAR) /* only the 12620A card */
outbound.signals |= ioSKF; /* responds to SFC */
break;
case ioSFS: /* skip if flag is set */
if (is_rte_pif && pif.flag == SET) /* only the 12620A card */
outbound.signals |= ioSKF; /* responds to SFS */
break;
case ioIOO: /* I/O data output */
if (is_rte_pif == FALSE) { /* DOS PIF? */
pif.flag_buffer = SET; /* set flag buffer */
working_set |= ioENF | ioSIR; /* set ENF and SIR (not normally done for IOO) */
}
break;
case ioPOPIO: /* power-on preset to I/O */
if (is_rte_pif)
pif.flag_buffer = SET;
else {
pif.flag_buffer = CLEAR;
pif.flag = CLEAR;
}
tprintf (pif_dev, TRACE_CMD, "Power-on reset\n");
break;
case ioCRS: /* control reset */
pif.control = CLEAR; /* clear control */
tprintf (pif_dev, TRACE_CMD, "Control reset\n");
break;
case ioCLC: /* clear control flip-flop */
pif.control = CLEAR; /* clear control */
break;
case ioSTC: /* set control flip-flop */
pif.control = SET; /* set control */
break;
case ioSIR: /* set interrupt request */
if (is_rte_pif & pif.control & pif.flag /* if control and flag are set (12620A) */
|| !is_rte_pif & (pif.control | pif.flag)) /* or control or flag are clear (12936A) */
outbound.signals |= cnVALID; /* then deny PRL */
else /* otherwise */
outbound.signals |= cnPRL | cnVALID; /* conditionally assert PRL */
if (~(is_rte_pif ^ pif.control) /* if control is set (12620A) or clear (12936A) */
& pif.flag & pif.flag_buffer) /* and flag and flag buffer are set */
outbound.signals |= cnIRQ | cnVALID; /* then conditionally assert IRQ */
if (is_rte_pif && pif.flag == SET) /* if 12620A and flag is set */
outbound.signals |= ioSRQ; /* then assert SRQ */
tprintf (pif_dev, TRACE_CMD, "Fence %s%s lower-priority interrupts\n",
(outbound.signals & cnIRQ ? "requests an interrupt and " : ""),
(outbound.signals & cnPRL ? "allows" : "inhibits"));
break;
case ioIAK: /* interrupt acknowledge */
pif.flag_buffer = CLEAR;
break;
case ioIEN: /* interrupt enable */
irq_enabled = TRUE;
break;
case ioPRH: /* priority high */
if (irq_enabled && outbound.signals & cnIRQ) /* if IRQ is enabled and conditionally asserted */
outbound.signals |= ioIRQ | ioFLG; /* then assert IRQ and FLG */
if (!irq_enabled || outbound.signals & cnPRL) /* if IRQ is disabled or PRL is conditionally asserted */
outbound.signals |= ioPRL; /* then assert it unconditionally */
break;
case ioIOI: /* not used by this interface */
case ioEDT: /* not used by this interface */
case ioPON: /* not used by this interface */
break;
}
IOCLEARSIG (working_set, signal); /* remove the current signal from the set */
}
return outbound; /* return the outbound signals and value */
}
/* Simulator reset routine */
static t_stat pif_reset (DEVICE *dptr)
{
io_assert (dptr, ioa_POPIO); /* PRESET the device */
return SCPE_OK;
}
/* Privileged interrupt fence local utility routines */
/* Set card type.
val == 0 --> set to 12936A (DOS PIF)
val == 1 --> set to 12620A (RTE PIF)
*/
static t_stat pif_set_card (UNIT *uptr, int32 val, char *cptr, void *desc)
{
if ((val < 0) || (val > 1) || (cptr != NULL)) /* sanity check */
return SCPE_ARG; /* bad argument */
if (val) /* DOS PIF selected? */
pif_dev.flags = pif_dev.flags | DEV_12936; /* set to 12936A */
else /* RTE PIF selected */
pif_dev.flags = pif_dev.flags & ~DEV_12936; /* set to 12620A */
return SCPE_OK;
}
/* Show card type */
static t_stat pif_show_card (FILE *st, UNIT *uptr, int32 val, void *desc)
{
if (pif_dev.flags & DEV_12936)
fputs ("12936A", st);
else
fputs ("12620A", st);
return SCPE_OK;
}

1191
HP2100/hp2100_pt.c Normal file

File diff suppressed because it is too large Load Diff

5087
HP2100/hp2100_sys.c Normal file

File diff suppressed because it is too large Load Diff

624
HP2100/hp2100_tbg.c Normal file
View File

@@ -0,0 +1,624 @@
/* hp2100_tbg.c: HP 12539C Time Base Generator Interface simulator
Copyright (c) 1993-2016, Robert M. Supnik
Copyright (c) 2017-2018, J. David Bryan
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the names of the authors shall not be
used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from the authors.
TBG 12539C Time Base Generator Interface
10-Jun-18 JDB Revised I/O model
04-Jun-18 JDB Split out from hp2100_stddev.c
Trimmed revisions to current file applicability
08-Mar-17 JDB Added REALTIME, W1A, W1B, W2A, and W2B options to the TBG
Replaced IPTICK with a CPU speed calculation
17-Jan-17 JDB Changed "hp_---sc" and "hp_---dev" to "hp_---_dib"
13-May-16 JDB Modified for revised SCP API function parameter types
24-Dec-14 JDB Added casts for explicit downward conversions
28-Dec-12 JDB Allocate the TBG logical name during power-on reset
09-May-12 JDB Separated assignments from conditional expressions
12-Feb-12 JDB Add TBG as a logical name for the CLK device
10-Feb-12 JDB Deprecated DEVNO in favor of SC
28-Mar-11 JDB Tidied up signal handling
26-Oct-10 JDB Changed I/O signal handler for revised signal model
26-Jun-08 JDB Rewrote device I/O to model backplane signals
14-Apr-08 JDB Synchronized CLK with TTY if set for 10 msec.
Added UNIT_IDLE to TTY and CLK
31-Dec-07 JDB Added IPTICK register to CLK to display CPU instr/tick
Corrected and verified ioCRS actions
28-Dec-06 JDB Added ioCRS state to I/O decoders
13-Sep-04 JDB Modified CLK to permit disable
26-Apr-04 RMS Fixed SFS x,C and SFC x,C
01-Nov-02 RMS Fixed clock to add calibration, proper start/stop
22-Mar-02 RMS Revised for dynamically allocated memory
03-Nov-01 RMS Changed DEVNO to use extended SET/SHOW
24-Nov-01 RMS Changed TIME to an array
07-Sep-01 RMS Moved function prototypes
21-Nov-00 RMS Fixed flag, buffer power up state
15-Oct-00 RMS Added dynamic device number support
References:
- 12539C Time Base Generator Interface Kit Operating and Service Manual
(12539-90008, January 1975)
The time base generator interface responds to I/O instructions as follows:
Output Data Word format (OTA and OTB):
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| - - - - - - - - - - - - - | tick rate |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
Tick Rate Selection:
000 = 100 microseconds
001 = 1 millisecond
010 = 10 milliseconds
011 = 100 milliseconds
100 = 1 second
101 = 10 seconds
110 = 100 seconds
111 = 1000 seconds
If jumper W2 is in position B, the last four rates are divided by 1000,
producing rates of 1, 10, 100, and 1000 milliseconds, respectively.
Input Data Word format (LIA, LIB, MIA, and MIB):
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| - - - - - - - - - - | e | E | - - - - |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
Where:
E = At least one tick has been lost
If jumper W1 is in position B, bit 5 also indicates a lost tick.
In hardware, the two configuration jumpers perform these functions:
Jumper Interpretation in position A Interpretation in position B
------ ---------------------------- ---------------------------------
W1 Input bit 5 is always zero Input bit 5 indicates a lost tick
W2 Last four rates are seconds Last four rates are milliseconds
The time base generator autocalibrates. If the TBG is set to a ten
millisecond period (e.g., as under RTE), it is synchronized to the console
poll. Otherwise (e.g., as under DOS or TSB, which use 100 millisecond
periods), it runs asynchronously. If the specified clock frequency is below
10Hz, the clock service routine runs at 10Hz and counts down a repeat counter
before generating an interrupt. Autocalibration will not work if the clock
is running at 1Hz or less.
*/
#include "hp2100_defs.h"
#include "hp2100_io.h"
/* Program constants */
static const int32 delay [8] = { /* clock delays, in event ticks per interval */
uS (100), /* 000 = 100 microseconds */
mS (1), /* 001 = 1 millisecond */
mS (10), /* 010 = 10 milliseconds */
mS (100), /* 011 = 100 milliseconds */
S (1), /* 100 = 1 second */
S (10), /* 101 = 10 seconds */
S (100), /* 110 = 100 seconds */
S (1000) /* 111 = 1000 seconds */
};
static const int32 ticks [8] = { /* clock ticks per second */
10000, /* 000 = 100 microseconds */
1000, /* 001 = 1 millisecond */
100, /* 010 = 10 milliseconds */
10, /* 011 = 100 milliseconds */
10, /* 100 = 1 second */
10, /* 101 = 10 seconds */
10, /* 110 = 100 seconds */
10 /* 111 = 1000 seconds */
};
static const int32 scale [8] = { /* prescaler counts per clock tick */
1, /* 000 = 100 microseconds */
1, /* 001 = 1 millisecond */
1, /* 010 = 10 milliseconds */
1, /* 011 = 100 milliseconds */
10, /* 100 = 1 second */
100, /* 101 = 10 seconds */
1000, /* 110 = 100 seconds */
10000 /* 111 = 1000 seconds */
};
/* Unit flags */
#define UNIT_CALTIME_SHIFT (UNIT_V_UF + 0) /* calibrated timing mode */
#define UNIT_W1B_SHIFT (UNIT_V_UF + 1) /* jumper W1 in position B */
#define UNIT_W2B_SHIFT (UNIT_V_UF + 2) /* jumper W2 in position B */
#define UNIT_CALTIME (1u << UNIT_CALTIME_SHIFT)
#define UNIT_W1B (1u << UNIT_W1B_SHIFT)
#define UNIT_W2B (1u << UNIT_W2B_SHIFT)
/* Control word.
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| - - - - - - - - - - - - - | tick rate |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
*/
#define CN_RATE_MASK 0000007u /* clock rate selector mask */
#define CN_RATE_SHIFT 0 /* clock rate alignment shift */
#define CN_RATE(c) (((c) & CN_RATE_MASK) >> CN_RATE_SHIFT)
static const char * const rate_name [8] = { /* clock rate selector names */
"100 microsecond", /* 000 = 100 microseconds */
"1 millisecond", /* 001 = 1 millisecond */
"10 millisecond", /* 010 = 10 milliseconds */
"100 millisecond", /* 011 = 100 milliseconds */
"1 second", /* 100 = 1 second */
"10 second", /* 101 = 10 seconds */
"100 second", /* 110 = 100 seconds */
"1000 second" /* 111 = 1000 seconds */
};
/* Status word.
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| - - - - - - - - - - | e | E | - - - - |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
*/
#define ST_ERROR 0000020u /* lost tick error */
#define ST_ERROR_W1B 0000040u /* lost tick error if W1 in position B */
static const BITSET_NAME status_names [] = { /* Status word names */
"lost tick" /* bit 4 */
};
static const BITSET_FORMAT status_format = /* names, offset, direction, alternates, bar */
{ FMT_INIT (status_names, 4, msb_first, no_alt, no_bar) };
/* Interface state */
typedef struct {
HP_WORD output_data; /* output data register */
HP_WORD input_data; /* input data register */
FLIP_FLOP control; /* control flip-flop */
FLIP_FLOP flag; /* flag flip-flop */
FLIP_FLOP flag_buffer; /* flag buffer flip-flop */
} CARD_STATE;
static CARD_STATE tbg; /* per-card state */
/* Time base generator state */
static int32 rate; /* clock rate */
static int32 prescaler; /* clock rate prescaler */
static FLIP_FLOP lost_tick; /* lost tick error flip-flop */
/* Interface local SCP support routines */
static INTERFACE tbg_interface;
/* Time base generator local SCP support routines */
static t_stat tbg_service (UNIT *uptr);
static t_stat tbg_reset (DEVICE *dptr);
/* Time base generator local utility routines */
typedef enum {
Clock_Time,
Prescaler_Count
} DELAY_TYPE;
static int32 get_delay (DELAY_TYPE selector);
/* Interface SCP data structures */
/* Device information block */
static DIB tbg_dib = {
&tbg_interface, /* the device's I/O interface function pointer */
TBG, /* the device's select code (02-77) */
0, /* the card index */
"12539C Time Base Generator Interface", /* the card description */
NULL /* the ROM description */
};
/* Unit list */
static UNIT tbg_unit [] = {
{ UDATA (&tbg_service, UNIT_IDLE | UNIT_CALTIME, 0) }
};
/* Register list */
static REG tbg_reg [] = {
/* Macro Name Location Width Offset Flags */
/* ------ ------ ------------------- ----- ------ ------- */
{ ORDATA (SEL, rate, 3) },
{ DRDATA (CTR, prescaler, 14) },
{ FLDATA (CTL, tbg.control, 0) },
{ FLDATA (FLG, tbg.flag, 0) },
{ FLDATA (FBF, tbg.flag_buffer, 0) },
{ FLDATA (ERR, lost_tick, 0) },
DIB_REGS (tbg_dib),
{ NULL }
};
/* Modifier list */
static MTAB tbg_mod [] = {
/* Mask Value Match Value Print String Match String Validation Display Descriptor */
/* ------------ ------------ ------------------- ------------ ---------- ------- ---------- */
{ UNIT_CALTIME, UNIT_CALTIME, "calibrated timing", "CALTIME", NULL, NULL, NULL },
{ UNIT_CALTIME, 0, "realistic timing", "REALTIME", NULL, NULL, NULL },
{ UNIT_W1B, UNIT_W1B, "W1 position B", "W1B", NULL, NULL, NULL },
{ UNIT_W1B, 0, "W1 position A", "W1A", NULL, NULL, NULL },
{ UNIT_W2B, UNIT_W2B, "W2 position B", "W2B", NULL, NULL, NULL },
{ UNIT_W2B, 0, "W2 position A", "W2A", NULL, NULL, NULL },
/* Entry Flags Value Print String Match String Validation Display Descriptor */
/* ------------------- ----- ------------ ------------ ------------ ------------- ----------------- */
{ MTAB_XDV, 1u, "SC", "SC", &hp_set_dib, &hp_show_dib, (void *) &tbg_dib },
{ MTAB_XDV | MTAB_NMO, ~1u, "DEVNO", "DEVNO", &hp_set_dib, &hp_show_dib, (void *) &tbg_dib },
{ 0 }
};
/* Debugging trace list */
static DEBTAB tbg_deb [] = {
{ "CSRW", TRACE_CSRW }, /* interface control, status, read, and write actions */
{ "PSERV", TRACE_PSERV }, /* clock unit service scheduling calls */
{ "IOBUS", TRACE_IOBUS }, /* interface I/O bus signals and data words */
{ NULL, 0 }
};
/* Device descriptor */
DEVICE tbg_dev = {
"CLK", /* device name (deprecated) */
tbg_unit, /* unit array */
tbg_reg, /* register array */
tbg_mod, /* modifier array */
1, /* number of units */
0, /* address radix */
0, /* address width */
0, /* address increment */
0, /* data radix */
0, /* data width */
NULL, /* examine routine */
NULL, /* deposit routine */
&tbg_reset, /* reset routine */
NULL, /* boot routine */
NULL, /* attach routine */
NULL, /* detach routine */
&tbg_dib, /* device information block pointer */
DEV_DISABLE | DEV_DEBUG, /* device flags */
0, /* debug control flags */
tbg_deb, /* debug flag name array */
NULL, /* memory size change routine */
NULL /* logical device name */
};
/* Interface local SCP support routines */
/* Time base generator interface.
The time base generator (TBG) provides periodic interrupts from 100
microseconds to 1000 seconds. The TBG uses a calibrated timer to provide the
time base. For periods ranging from 1 to 1000 seconds, a 100 millisecond
timer is used, and 10 to 10000 ticks are counted before setting the device
flag to indicate that the period has expired.
If the period is set to ten milliseconds, the console poll timer is used
instead of an independent timer. This is to maximize the idle period.
In diagnostic mode, the clock period is set to the expected number of CPU
instructions, rather than wall-clock time, so that the diagnostic executes as
expected.
*/
static SIGNALS_VALUE tbg_interface (const DIB *dibptr, INBOUND_SET inbound_signals, HP_WORD inbound_value)
{
INBOUND_SIGNAL signal;
INBOUND_SET working_set = inbound_signals;
SIGNALS_VALUE outbound = { ioNONE, 0 };
t_bool irq_enabled = FALSE;
int32 tick_count;
while (working_set) { /* while signals remain */
signal = IONEXTSIG (working_set); /* isolate the next signal */
switch (signal) { /* dispatch the I/O signal */
case ioCLF: /* Clear Flag flip-flop */
tbg.flag_buffer = CLEAR; /* reset the flag buffer */
tbg.flag = CLEAR; /* and flag flip-flops */
break;
case ioSTF: /* Set Flag flip-flop */
tbg.flag_buffer = SET; /* set the flag buffer flip-flop */
break;
case ioENF: /* Enable Flag */
if (tbg.flag_buffer == SET) /* if the flag buffer flip-flop is set */
tbg.flag = SET; /* then set the flag flip-flop */
break;
case ioSFC: /* Skip if Flag is Clear */
if (tbg.flag == CLEAR) /* if the flag flip-flop is clear */
outbound.signals |= ioSKF; /* then assert the Skip on Flag signal */
break;
case ioSFS: /* Skip if Flag is Set */
if (tbg.flag == SET) /* if the flag flip-flop is set */
outbound.signals |= ioSKF; /* then assert the Skip on Flag signal */
break;
case ioIOI: /* I/O Data Input */
if (lost_tick == SET) { /* if the lost-tick flip-flop is set */
outbound.value = ST_ERROR; /* then indicate an error */
if (tbg_unit [0].flags & UNIT_W1B) /* if W1 is in position B */
outbound.value |= ST_ERROR_W1B; /* then set the status in bit 5 as well */
}
else /* otherwise the error flip-flop is clear */
outbound.value = 0; /* so clear the error status */
tprintf (tbg_dev, TRACE_CSRW, "Status is %s\n",
fmt_bitset (outbound.value, status_format));
break;
case ioIOO: /* I/O Data Output */
rate = CN_RATE (inbound_value); /* save select */
tbg.control = CLEAR; /* clear control */
sim_cancel (&tbg_unit [0]); /* stop the clock */
working_set = working_set | ioSIR; /* set interrupt request (IOO normally doesn't) */
tprintf (tbg_dev, TRACE_CSRW, "Control is %s rate\n",
rate_name [rate]);
break;
case ioPOPIO: /* Power-On Preset to I/O */
tbg.flag_buffer = SET; /* set the flag buffer flip-flop */
break;
case ioCRS: /* Control Reset */
case ioCLC: /* Clear Control flip-flop */
tbg.control = CLEAR; /* clear the control flip-flop */
sim_cancel (&tbg_unit [0]); /* and stop the clock */
break;
case ioSTC: /* Set Control flip-flop */
tbg.control = SET; /* set the control flip-flop */
if (!sim_is_active (&tbg_unit [0])) { /* if the TBG is not running */
tick_count = get_delay (Clock_Time); /* then get the programmed tick count */
if (tbg_unit [0].flags & UNIT_CALTIME) /* if the TBG is calibrated */
if (rate == 2) /* then if the rate is 10 milliseconds */
tick_count = hp_sync_poll (INITIAL); /* then synchronize with the poll timer */
else /* otherwise */
sim_rtcn_init (tick_count, TMR_TBG); /* calibrate the TBG timer independently */
tprintf (tbg_dev, TRACE_PSERV, "Rate %s delay %d service rescheduled\n",
rate_name [rate], tick_count);
sim_activate (&tbg_unit [0], tick_count); /* start the TBG */
prescaler = get_delay (Prescaler_Count); /* and set the prescaler */
}
lost_tick = CLEAR; /* clear the lost tick flip-flop */
break;
case ioSIR: /* Set Interrupt Request */
if (tbg.control & tbg.flag) /* if the control and flag flip-flops are set */
outbound.signals |= cnVALID; /* then deny PRL */
else /* otherwise */
outbound.signals |= cnPRL | cnVALID; /* conditionally assert PRL */
if (tbg.control & tbg.flag & tbg.flag_buffer) /* if the control, flag, and flag buffer flip-flops are set */
outbound.signals |= cnIRQ | cnVALID; /* then conditionally assert IRQ */
if (tbg.flag == SET) /* if the flag flip-flop is set */
outbound.signals |= ioSRQ; /* then assert SRQ */
break;
case ioIAK: /* Interrupt Acknowledge */
tbg.flag_buffer = CLEAR; /* clear the flag buffer flip-flop */
break;
case ioIEN: /* Interrupt Enable */
irq_enabled = TRUE; /* permit IRQ to be asserted */
break;
case ioPRH: /* Priority High */
if (irq_enabled && outbound.signals & cnIRQ) /* if IRQ is enabled and conditionally asserted */
outbound.signals |= ioIRQ | ioFLG; /* then assert IRQ and FLG */
if (!irq_enabled || outbound.signals & cnPRL) /* if IRQ is disabled or PRL is conditionally asserted */
outbound.signals |= ioPRL; /* then assert it unconditionally */
break;
case ioEDT: /* not used by this interface */
case ioPON: /* not used by this interface */
break;
}
IOCLEARSIG (working_set, signal); /* remove the current signal from the set */
}
return outbound; /* return the outbound signals and value */
}
/* TBG unit service.
As with the I/O handler, if the time base period is set to ten milliseconds,
the console poll timer is used instead of an independent timer.
Implementation notes:
1. If the TBG is calibrated, it is synchronized with the TTY keyboard poll
service to permit idling.
*/
static t_stat tbg_service (UNIT *uptr)
{
int32 tick_count;
tprintf (tbg_dev, TRACE_PSERV, "Service entered with prescaler %d\n",
prescaler);
if (tbg.control == CLEAR) /* control clear? */
return SCPE_OK; /* done */
if (tbg_unit [0].flags & UNIT_CALTIME) /* cal mode? */
if (rate == 2) /* 10 msec period? */
tick_count = hp_sync_poll (SERVICE); /* sync poll */
else
tick_count = sim_rtcn_calb (ticks [rate], /* calibrate delay */
TMR_TBG);
else /* otherwise the TBG is in real-time mode */
tick_count = get_delay (Clock_Time); /* so get the delay directly */
prescaler = prescaler - 1; /* decrement the prescaler count */
if (prescaler <= 0) { /* if the prescaler count has expired */
if (tbg.flag) {
lost_tick = SET; /* overrun? error */
tprintf (tbg_dev, TRACE_PSERV, "Clock tick lost\n");
}
else { /* otherwise */
tbg.flag_buffer = SET; /* set the flag buffer */
io_assert (&tbg_dev, ioa_ENF); /* and the flag */
}
prescaler = get_delay (Prescaler_Count); /* reset the prescaler */
}
tprintf (tbg_dev, TRACE_PSERV, "Rate %s delay %d service %s\n",
rate_name [rate], tick_count,
(rate == 2 ? "coscheduled" : "scheduled"));
return sim_activate (uptr, tick_count); /* reactivate */
}
/* Reset routine */
static t_stat tbg_reset (DEVICE *dptr)
{
if (sim_switches & SWMASK ('P')) { /* initialization reset? */
lost_tick = CLEAR; /* clear error */
rate = 0; /* clear rate */
prescaler = 0; /* clear prescaler */
if (tbg_dev.lname == NULL) /* logical name unassigned? */
tbg_dev.lname = strdup ("TBG"); /* allocate and initialize the name */
}
io_assert (dptr, ioa_POPIO); /* PRESET the device */
return SCPE_OK;
}
/* Time base generator local utility routines */
/* Clock delay routine */
static int32 get_delay (DELAY_TYPE selector)
{
int32 rate_index;
if (tbg_unit [0].flags & UNIT_W2B && rate >= 4) /* if jumper W2 is in position B */
rate_index = rate - 3; /* then rates 4-7 rescale to 1-4 */
else /* otherwise */
rate_index = rate; /* the rate selector is used as is */
if (selector == Clock_Time) /* if the clock time is wanted */
return delay [rate_index]; /* return the tick delay count */
else /* otherwise */
return scale [rate_index]; /* return the prescale count */
}

999
HP2100/hp2100_tty.c Normal file
View File

@@ -0,0 +1,999 @@
/* hp2100_tty.c: HP 12531C Buffered Teleprinter Interface simulator
Copyright (c) 1993-2016, Robert M. Supnik
Copyright (c) 2017-2019, J. David Bryan
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the names of the authors shall not be
used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from the authors.
TTY 12531C Buffered Teleprinter Interface
03-May-19 JDB Output stall now doubles rescheduling wait
26-Apr-19 JDB Added "vm_console_[in/out]put_unit" declarations
22-Sep-18 JDB RESET -P now resets to original FASTTIME timing
29-Jun-18 JDB TTY may now be disabled; added "set_endis" routine
27-Jun-18 JDB Added REALTIME/FASTTIME modes and tracing
05-Jun-18 JDB Revised I/O model
04-Jun-18 JDB Split out from hp2100_stddev.c
Trimmed revisions to current file applicability
22-Nov-17 JDB Fixed TTY serial output buffer overflow handling
03-Aug-17 JDB PTP and TTY now append to existing file data
01-May-17 JDB Deleted ttp_stopioe, as a detached punch is no longer an error
17-Jan-17 JDB Changed "hp_---sc" and "hp_---dev" to "hp_---_dib"
30-Dec-16 JDB Modified the TTY to print if the punch is not attached
13-May-16 JDB Modified for revised SCP API function parameter types
24-Dec-14 JDB Added casts for explicit downward conversions
18-Dec-12 MP Now calls sim_activate_time to get remaining poll time
09-May-12 JDB Separated assignments from conditional expressions
10-Feb-12 JDB Deprecated DEVNO in favor of SC
28-Mar-11 JDB Tidied up signal handling
26-Oct-10 JDB Changed I/O signal handler for revised signal model
26-Jun-08 JDB Rewrote device I/O to model backplane signals
25-Apr-08 JDB Changed TTY output wait from 100 to 200 for MSU BASIC
18-Apr-08 JDB Removed redundant control char handling definitions
14-Apr-08 JDB Changed TTY console poll to 10 msec. real time
Added UNIT_IDLE to TTY and CLK
31-Dec-07 JDB Corrected and verified ioCRS actions
28-Dec-06 JDB Added ioCRS state to I/O decoders
22-Nov-05 RMS Revised for new terminal processing routines
15-Aug-04 RMS Added tab to control char set (from Dave Bryan)
14-Jul-04 RMS Generalized handling of control char echoing
(from Dave Bryan)
26-Apr-04 RMS Fixed SFS x,C and SFC x,C
Fixed input behavior during typeout for RTE-IV
Suppressed nulls on TTY output for RTE-IV
Implemented DMA SRQ (follows FLG)
29-Mar-03 RMS Added support for console backpressure
22-Dec-02 RMS Added break support
01-Nov-02 RMS Fixed bug in TTY reset, TTY starts in input mode
Fixed bug in TTY mode OTA, stores data as well
Added UC option to TTY output
30-May-02 RMS Widened POS to 32b
22-Mar-02 RMS Revised for dynamically allocated memory
03-Nov-01 RMS Changed DEVNO to use extended SET/SHOW
29-Nov-01 RMS Added read only unit support
07-Sep-01 RMS Moved function prototypes
21-Nov-00 RMS Fixed flag, buffer power up state
Added status input for ptp, tty
15-Oct-00 RMS Added dynamic device number support
References:
- 12531C Buffered Teleprinter Interface Kit Operating and Service Manual
(12531-90033, November 1972)
The 12531C Buffered Teleprinter Interface connects current-loop devices, such
as the HP 2752A (ASR33) and 2754A (ASR35) teleprinters, as well as EIA RS-232
devices, such as the HP 2749A (ASR33) teleprinter and HP 2600 terminal, to
the CPU. Teleprinters are often used as the system consoles for the various
HP operating systems. By default, the system console is connected to the
simulation console, so that SCP and operating system commands may be entered
from the same window.
The interface responds to I/O instructions as follows:
Output Data Word format (OTA and OTB):
15 |14 13 12 |11 10 9 | 8 7 6 | 5 4 3 | 2 1 0
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| 0 | - - - - - - - | output character | data
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| 1 | I | P | N | - - - - - - - - - - - - | control
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
Where:
I = set the interface to output/input mode (0/1)
P = enable the printer for output
N = enable the punch for output
Input Data Word format (LIA and LIB):
15 |14 13 12 |11 10 9 | 8 7 6 | 5 4 3 | 2 1 0
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| B | - - - - - - - | input character |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
Where:
B = interface is idle/busy (0/1)
To support CPU idling, the teleprinter interface polls for input by
coscheduling with the system poll timer, a calibrated timer with a ten
millisecond period. Other polled-keyboard input devices (multiplexers and
the BACI card) synchronize with the poll timer to ensure maximum available
idle time.
*/
#include "hp2100_defs.h"
#include "hp2100_io.h"
/* TTY program constants */
#define NUL '\000' /* null */
#define CR '\r' /* carriage return */
#define LF '\n' /* line feed */
#define MARK '\377' /* all bits marking */
/* TTY device flags */
#define DEV_REALTIME_SHIFT (DEV_V_UF + 0) /* realistic timing mode */
#define DEV_REALTIME (1u << DEV_REALTIME_SHIFT) /* realistic timing mode flag */
/* TTY unit flags */
#define UNIT_AUTOLF_SHIFT (TTUF_V_UF + 0) /* automatic line feed mode */
#define UNIT_AUTOLF (1u << UNIT_AUTOLF_SHIFT) /* automatic line feed mode flag */
/* TTY unit references */
typedef enum {
keyboard, /* teleprinter keyboard unit index */
printer, /* teleprinter printer unit index */
punch /* teleprinter punch unit index */
} UNIT_INDEX;
/* TTY device properties */
#define TTY_FAST_TIME 200 /* teleprinter optimized timing delay */
#define TTY_REAL_TIME mS (100) /* teleprinter realistic timing delay */
/* TTY output data and control words.
15 |14 13 12 |11 10 9 | 8 7 6 | 5 4 3 | 2 1 0
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| 0 | - - - - - - - | output character | data
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| 1 | I | P | N | - - - - - - - - - - - - | control
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
*/
#define CN_CONTROL 0100000u /* output is a data/control (0/1) word */
#define CN_INPUT 0040000u /* set the interface to output/input (0/1) mode */
#define CN_PRINT 0020000u /* enable the printer for output */
#define CN_PUNCH 0010000u /* enable the punch for output */
static const BITSET_NAME tty_control_names [] = { /* Teleprinter control word names */
"\1input\0output", /* bit 14 */
"printer enabled", /* bit 13 */
"punch enabled" /* bit 12 */
};
static const BITSET_FORMAT tty_control_format = /* names, offset, direction, alternates, bar */
{ FMT_INIT (tty_control_names, 12, msb_first, has_alt, no_bar) };
/* TTY input data word.
15 |14 13 12 |11 10 9 | 8 7 6 | 5 4 3 | 2 1 0
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| B | - - - - - - - | input character |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
*/
#define ST_BUSY 0100000u /* interface is idle/busy (0/1) */
static const BITSET_NAME tty_status_names [] = { /* Teleprinter status word names */
"\1busy\0idle" /* bit 15 */
};
static const BITSET_FORMAT tty_status_format = /* names, offset, direction, alternates, bar */
{ FMT_INIT (tty_status_names, 15, msb_first, has_alt, no_bar) };
/* TTY local state declarations */
typedef struct {
char io_data; /* input/output data register */
char shift_in_data; /* shift-in data */
t_bool cr_seen; /* carriage return has been seen on input */
HP_WORD mode; /* control mode register */
FLIP_FLOP control; /* control flip-flop */
FLIP_FLOP flag; /* flag flip-flop */
FLIP_FLOP flag_buffer; /* flag buffer flip-flop */
} CARD_STATE;
static CARD_STATE tty; /* per-card state */
static int32 fast_data_time = TTY_FAST_TIME; /* fast receive/send time */
/* TTY I/O interface routine declarations */
static INTERFACE tty_interface;
/* TTY local SCP support routine declarations */
static t_stat set_filter (UNIT *uptr, int32 value, char *cptr, void *desc);
static t_stat set_auto (UNIT *uptr, int32 value, char *cptr, void *desc);
static t_stat set_mode (UNIT *uptr, int32 value, char *cptr, void *desc);
static t_stat set_endis (UNIT *uptr, int32 value, char *cptr, void *desc);
static t_stat show_mode (FILE *st, UNIT *uptr, int32 value, void *desc);
static t_stat tty_reset (DEVICE *dptr);
/* TTY local utility routine declarations */
static t_stat keyboard_service (UNIT *uptr);
static t_stat print_punch_service (UNIT *uptr);
static t_stat output (int32 character);
/* TTY SCP data declarations */
/* Unit lists.
Implementation notes:
1. The print unit service handles both printing and punching. The punch
unit is never scheduled, so its event routine is NULL.
*/
static UNIT tty_unit [] = {
/* Event Routine Unit Flags Capacity Delay */
/* -------------------- ------------------------------------ -------- ------------- */
{ UDATA (&keyboard_service, TT_MODE_UC | UNIT_IDLE, 0), POLL_PERIOD },
{ UDATA (&print_punch_service, TT_MODE_UC, 0), TTY_FAST_TIME },
{ UDATA (NULL, TT_MODE_8B | UNIT_SEQ | UNIT_ATTABLE, 0), 0 }
};
#define key_unit tty_unit [keyboard] /* teleprinter keyboard unit */
#define print_unit tty_unit [printer] /* teleprinter printer unit */
#define punch_unit tty_unit [punch] /* teleprinter punch unit */
UNIT *vm_console_input_unit = &key_unit; /* console input unit pointer */
UNIT *vm_console_output_unit = &print_unit; /* console output unit pointer */
/* Device information block */
static DIB tty_dib = {
&tty_interface, /* the device's I/O interface function pointer */
TTY, /* the device's select code (02-77) */
0, /* the card index */
"12531C Buffered Teleprinter Interface", /* the card description */
NULL /* the ROM description */
};
/* Register list */
static REG tty_reg [] = {
/* Macro Name Location Radix Width Offset Depth Flags */
/* ------ ------ ------------------------ ----- ---------- ------ ------------- ----------------- */
{ ORDATA (BUF, tty.io_data, 8) },
{ GRDATA (MODE, tty.mode, 2, 3, 12), PV_RZRO },
{ ORDATA (SHIN, tty.shift_in_data, 8), REG_HRO },
{ FLDATA (CTL, tty.control, 0) },
{ FLDATA (FLG, tty.flag, 0) },
{ FLDATA (FBF, tty.flag_buffer, 0) },
{ FLDATA (KLFP, tty.cr_seen, 0), REG_HRO },
{ DRDATA (KPOS, key_unit.pos, T_ADDR_W), PV_LEFT },
{ DRDATA (TPOS, print_unit.pos, T_ADDR_W), PV_LEFT },
{ DRDATA (PPOS, punch_unit.pos, T_ADDR_W), PV_LEFT },
{ DRDATA (TTIME, fast_data_time, 24), PV_LEFT | REG_NZ },
DIB_REGS (tty_dib),
{ NULL }
};
/* Modifier list */
static MTAB tty_mod [] = {
/* Mask Value Match Value Print String Match String Validation Display Descriptor */
/* ----------- ----------- --------------- ------------ ----------- ------- ---------- */
{ TT_MODE, TT_MODE_UC, "UC", "UC", &set_filter, NULL, NULL },
{ TT_MODE, TT_MODE_7B, "7b", "7B", &set_filter, NULL, NULL },
{ TT_MODE, TT_MODE_8B, "8b", "8B", &set_filter, NULL, NULL },
{ TT_MODE, TT_MODE_7P, "7p", "7P", &set_filter, NULL, NULL },
{ UNIT_AUTOLF, UNIT_AUTOLF, "auto linefeed", "AUTOLF", &set_auto, NULL, NULL },
{ UNIT_AUTOLF, 0, NULL, "NOAUTOLF", &set_auto, NULL, NULL },
/* Entry Flags Value Print String Match String Validation Display Descriptor */
/* ------------------- ------------ ------------ ------------ ------------ ------------ ----------------- */
{ MTAB_XDV, 0, NULL, "FASTTIME", &set_mode, NULL, NULL },
{ MTAB_XDV, DEV_REALTIME, NULL, "REALTIME", &set_mode, NULL, NULL },
{ MTAB_XDV, 0, "MODES", NULL, NULL, &show_mode, NULL },
{ MTAB_XDV | MTAB_NMO, 1, NULL, "ENABLED", &set_endis, NULL, NULL },
{ MTAB_XDV | MTAB_NMO, 0, NULL, "DISABLED", &set_endis, NULL, NULL },
{ MTAB_XDV, 1u, "SC", "SC", &hp_set_dib, &hp_show_dib, (void *) &tty_dib },
{ MTAB_XDV | MTAB_NMO, ~1u, "DEVNO", "DEVNO", &hp_set_dib, &hp_show_dib, (void *) &tty_dib },
{ 0 }
};
/* Trace list */
static DEBTAB tty_deb [] = {
{ "CSRW", TRACE_CSRW }, /* trace interface control, status, read, and write actions */
{ "SERV", TRACE_SERV }, /* trace unit service scheduling calls and entries */
{ "PSERV", TRACE_PSERV }, /* trace periodic unit service scheduling calls and entries */
{ "XFER", TRACE_XFER }, /* trace data transmissions */
{ "IOBUS", TRACE_IOBUS }, /* trace I/O bus signals and data words received and returned */
{ NULL, 0 }
};
/* Device descriptor */
DEVICE tty_dev = {
"TTY", /* device name */
tty_unit, /* unit array */
tty_reg, /* register array */
tty_mod, /* modifier array */
3, /* number of units */
10, /* address radix */
31, /* address width */
1, /* address increment */
8, /* data radix */
8, /* data width */
NULL, /* examine routine */
NULL, /* deposit routine */
&tty_reset, /* reset routine */
NULL, /* boot routine */
&hp_attach, /* attach routine */
NULL, /* detach routine */
&tty_dib, /* device information block pointer */
DEV_DISABLE | DEV_DEBUG, /* device flags */
0, /* debug control flags */
tty_deb, /* debug flag name array */
NULL, /* memory size change routine */
NULL /* logical device name */
};
/* TTY I/O interface routine */
/* Teleprinter interface.
The teleprinter interface is installed on the I/O bus and receives I/O
commands from the CPU and DMA/DCPC channels. In simulation, the asserted
signals on the bus are represented as bits in the inbound_signals set. Each
signal is processed sequentially in ascending numerical order.
The interface mode (output or input) determines whether STC transmits the
content of the output register or merely enables reception interrupts. While
in output mode, the input shift register is active and will reflect the state
of the serial input line while printing occurs. RTE uses this behavior to
detect a keypress during output on the system console and so to initiate
command input.
Implementation notes:
1. The print unit service time has to be set on each output request, even
though the time does not change unless a SET TTY REALTIME/FASTTIME
command is issued. We want to be able to respond immediately to a change
in the FASTTIME register setting via a DEPOSIT TTY TTIME <new-time>
command without requiring a subsequent SET TTY FASTTIME command to
recognize the change. However, there is no VM notification that the
value was changed unless all DEPOSIT commands are intercepted. The
straightforward solution of having SET change a pointer to the selected
value, i.e., to point at the REALTIME value or the FASTTIME value, and
then using that pointer indirectly to get the service time won't work
across a SAVE/RESTORE. The pointer itself cannot be saved, as the
restoration might be done on a different SIMH version, and there's no VM
notification that a RESTORE was done unless the device was attached when
the SAVE was done, so there's no way to set the pointer during a RESTORE.
*/
static SIGNALS_VALUE tty_interface (const DIB *dibptr, INBOUND_SET inbound_signals, HP_WORD inbound_value)
{
INBOUND_SIGNAL signal;
INBOUND_SET working_set = inbound_signals;
SIGNALS_VALUE outbound = { ioNONE, 0 };
t_bool irq_enabled = FALSE;
while (working_set) { /* while signals remain */
signal = IONEXTSIG (working_set); /* isolate the next signal */
switch (signal) { /* dispatch the I/O signal */
case ioCLF: /* Clear Flag flip-flop */
tty.flag_buffer = CLEAR; /* reset the flag buffer */
tty.flag = CLEAR; /* and flag flip-flops */
break;
case ioSTF: /* Set Flag flip-flop */
tty.flag_buffer = SET; /* set the flag buffer flip-flop */
break;
case ioENF: /* Enable Flag */
if (tty.flag_buffer == SET) /* if the flag buffer flip-flop is set */
tty.flag = SET; /* then set the flag flip-flop */
break;
case ioSFC: /* Skip if Flag is Clear */
if (tty.flag == CLEAR) /* if the flag flip-flop is clear */
outbound.signals |= ioSKF; /* then assert the Skip on Flag signal */
break;
case ioSFS: /* Skip if Flag is Set */
if (tty.flag == SET) /* if the flag flip-flop is set */
outbound.signals |= ioSKF; /* then assert the Skip on Flag signal */
break;
case ioIOI: /* I/O Data Input */
outbound.value = TO_WORD (0, tty.io_data); /* get the data register value */
if ((tty.mode & CN_INPUT) == 0 /* if the card is in output mode */
&& sim_is_active (&print_unit)) /* and the printer is active */
outbound.value |= ST_BUSY; /* then set the busy status bit */
tprintf (tty_dev, TRACE_CSRW, "Status is %s | %s\n",
fmt_bitset (outbound.value, tty_status_format),
fmt_char (outbound.value));
break;
case ioIOO: /* I/O Data Output */
if (inbound_value & CN_CONTROL) { /* if this is a control word */
tty.mode = inbound_value; /* then set the mode register */
tprintf (tty_dev, TRACE_CSRW, "Control is %s\n",
fmt_bitset (inbound_value, tty_control_format));
}
else
tprintf (tty_dev, TRACE_CSRW, "Output data is %s\n",
fmt_char (inbound_value));
tty.io_data = LOWER_BYTE (inbound_value); /* set the data register from the lower byte */
break;
case ioPOPIO: /* Power-On Preset to I/O */
tty.flag_buffer = SET; /* set the flag buffer flip-flop */
break;
case ioCRS: /* Control Reset */
tty.control = CLEAR; /* clear the control flip-flop */
tty.flag_buffer = SET; /* and set the flag buffer flip-flop */
tty.mode = CN_INPUT; /* set the input mode and clear the print and punch modes */
tty.shift_in_data = MARK; /* indicate that the serial line is marking */
tty.cr_seen = FALSE; /* and that a CR character has not been seen */
break;
case ioCLC: /* Clear Control flip-flop */
tty.control = CLEAR; /* clear the control flip-flop */
break;
case ioSTC: /* Set Control flip-flop */
tty.control = SET; /* set the control flip-flop */
if ((tty.mode & CN_INPUT) == 0) { /* if the card is in output mode */
if (tty_dev.flags & DEV_REALTIME) /* then if the device is set for REALTIME mode */
print_unit.wait = TTY_REAL_TIME; /* then use the realistic timing delay */
else /* otherwise */
print_unit.wait = fast_data_time; /* use the optimized timing delay */
sim_activate (&print_unit, print_unit.wait); /* schedule the printer with selective punching */
tprintf (tty_dev, TRACE_SERV, "Unit delay %d service scheduled\n",
print_unit.wait);
}
break;
case ioSIR: /* Set Interrupt Request */
if (tty.control & tty.flag) /* if the control and flag flip-flops are set */
outbound.signals |= cnVALID; /* then deny PRL */
else /* otherwise */
outbound.signals |= cnPRL | cnVALID; /* conditionally assert PRL */
if (tty.control & tty.flag & tty.flag_buffer) /* if the control, flag, and flag buffer flip-flops are set */
outbound.signals |= cnIRQ | cnVALID; /* then conditionally assert IRQ */
if (tty.flag == SET) /* if the flag flip-flop is set */
outbound.signals |= ioSRQ; /* then assert SRQ */
break;
case ioIAK: /* Interrupt Acknowledge */
tty.flag_buffer = CLEAR; /* clear the flag buffer flip-flop */
break;
case ioIEN: /* Interrupt Enable */
irq_enabled = TRUE; /* permit IRQ to be asserted */
break;
case ioPRH: /* Priority High */
if (irq_enabled && outbound.signals & cnIRQ) /* if IRQ is enabled and conditionally asserted */
outbound.signals |= ioIRQ | ioFLG; /* then assert IRQ and FLG */
if (!irq_enabled || outbound.signals & cnPRL) /* if IRQ is disabled or PRL is conditionally asserted */
outbound.signals |= ioPRL; /* then assert it unconditionally */
break;
case ioEDT: /* not used by this interface */
case ioPON: /* not used by this interface */
break;
}
IOCLEARSIG (working_set, signal); /* remove the current signal from the set */
} /* and continue until all signals are processed */
return outbound; /* return the outbound signals and value */
}
/* TTY local SCP support routines */
/* Set the keyboad input and print output filters.
This validation routine is called to configure the character input filter for
the keyboard unit and the output filter for the print unit. The "value"
parameter is one of the TT_MODE flags to indicate the type of filter desired.
"uptr" points at the unit being configured and is used to prohibit setting a
filter on the punch unit. The character and description pointers are not
used.
The routine processes commands of the form:
SET TTYn UC
SET TTYn 7B
SET TTYn 8B
SET TTYn 7P
...where "n" is 0 or 1 to set the filter on the keyboard or print units,
respectively. Mode 7P (7 bit with non-printing character suppression) isn't
valid for the keyboard and is changed to mode 7B (7 bit) if specified.
*/
static t_stat set_filter (UNIT *uptr, int32 value, char *cptr, void *desc)
{
if (uptr == &punch_unit) /* filters are not valid */
return SCPE_NOFNC; /* for the punch */
else {
if (uptr == &key_unit && value == TT_MODE_7P) /* non-printing character suppression */
value = TT_MODE_7B; /* isn't appropriate for the keyboard */
uptr->flags = uptr->flags & ~TT_MODE | value; /* set the filter to the requested mode */
return SCPE_OK; /* and return success */
}
}
/* Set the automatic line feed mode.
This validation routine is called when configuring the automatic line feed
mode. Some HP software systems expect the console terminal to transmit line
feed characters automatically following each carriage return. As an aid to
avoid typing LF characters after pressing ENTER, the SET TTY AUTOLF command
may be specified for the keyboard unit. This simulates pressing the AUTO LF
latching key on an HP 264x terminal. Specifying the SET TTY NOAUTOLF command
reverts to normal keyboard operation.
*/
static t_stat set_auto (UNIT *uptr, int32 value, char *cptr, void *desc)
{
if (uptr == &key_unit) /* if this is the keyboard unit */
return SCPE_OK; /* then allow the setting */
else /* otherwise auto LF mode is not valid */
return SCPE_NOFNC; /* for other units */
}
/* Set the timing mode.
This validation routine is called to set the printer and punch timing modes
to realistic or optimized timing. On entry, the "value" parameter is
DEV_REALTIME if real time mode is being set and zero if optimized ("fast")
timing mode is being set. The unit, character, and descriptor pointers are
not used.
*/
static t_stat set_mode (UNIT *uptr, int32 value, char *cptr, void *desc)
{
if (value == DEV_REALTIME) /* if realistic timing mode is selected */
tty_dev.flags |= DEV_REALTIME; /* then set the real-time flag */
else /* otherwise optimized timing mode is selected */
tty_dev.flags &= ~DEV_REALTIME; /* so clear the real-time flag */
return SCPE_OK; /* mode changes always succeed */
}
/* Enable or disable the TTY.
This validation routine is entered with "value" set to 1 for an ENABLE and 0
for a DISABLE. The unit, character, and descriptor pointers are not used.
If the TTY is already enabled or disabled, respectively, the routine returns
with no further action. Otherwise, if "value" is 1, the device is enabled by
clearing the DEV_DIS flag. If "value" is 0, a check is made to see if the
punch unit is attached to an output file. If it is, the disable request is
rejected; the unit must be detached first. Otherwise, the device is disabled
by setting the DEV_DIS flag.
In either case, the device is reset, which will restart or cancel the keyboad
poll, as appropriate.
*/
static t_stat set_endis (UNIT *uptr, int32 value, char *cptr, void *desc)
{
if (value) /* if this is an ENABLE request */
if (tty_dev.flags & DEV_DIS) /* then if the device is disabled */
tty_dev.flags &= ~DEV_DIS; /* then reenable it */
else /* otherwise the device is already enabled */
return SCPE_OK; /* so there's nothing to do */
else /* otherwise this is a DISABLE request */
if (tty_dev.flags & DEV_DIS) /* so if the device is already disabled */
return SCPE_OK; /* so there's nothing to do */
else if (punch_unit.flags & UNIT_ATT) /* otherwise if the punch is still attached */
return SCPE_ALATT; /* then it cannot be disabled */
else /* otherwise */
tty_dev.flags |= DEV_DIS; /* disable the device */
return tty_reset (&tty_dev); /* reset the TTY and restart or cancel polling */
}
/* Show the timing mode.
This display routine is called to show the current timing mode. The output
stream is passed in the "st" parameter, and the other parameters are ignored.
*/
static t_stat show_mode (FILE *st, UNIT *uptr, int32 value, void *desc)
{
if (tty_dev.flags & DEV_REALTIME) /* if the current mode is real time */
fputs ("realistic timing", st); /* then report it */
else /* otherwise */
fputs ("fast timing", st); /* report that the optimized timing mode is active */
return SCPE_OK; /* the display routine always succeeds */
}
/* Reset the TTY.
This routine is called for a RESET, RESET TTY, RUN, or BOOT command. It is
the simulation equivalent of an initial power-on condition (corresponding to
PON, POPIO, and CRS signal assertion in the CPU) or a front-panel PRESET
button press (corresponding to POPIO and CRS assertion). SCP delivers a
power-on reset to all devices when the simulator is started.
If this is a power-on reset, the data buffer is cleared, the input shift
register is preset, and the default optimized output time is restored. If
the device is disabled, then the keyboard poll service is cancelled.
Otherwise, the poll is (re)started and coscheduled with the master keyboard
poll timer, and POPIO is asserted to the interface. In either case, any
print or punch operation in progress is cancelled.
*/
static t_stat tty_reset (DEVICE *dptr)
{
if (sim_switches & SWMASK ('P')) { /* if this is a power-on reset */
tty.io_data = NUL; /* then clear the data buffer */
tty.shift_in_data = MARK; /* and preset the input shift register */
fast_data_time = TTY_FAST_TIME; /* restore the initial fast data time */
}
if (tty_dev.flags & DEV_DIS) /* if the device is disabled */
sim_cancel (&key_unit); /* then cancel the keyboard poll */
else { /* otherwise */
key_unit.wait = hp_sync_poll (INITIAL); /* coschedule the poll with the poll timer */
sim_activate_abs (&key_unit, key_unit.wait); /* and begin keyboard polling */
io_assert (dptr, ioa_POPIO); /* PRESET the device */
}
sim_cancel (&print_unit); /* cancel any pending print or punch output */
return SCPE_OK;
}
/* TTY local utility routines */
/* TTY input service routine.
The console input poll routine runs continuously while the device is enabled.
It is coscheduled with the general keyboard poll timer, which is a ten
millisecond calibrated timer that provides event timing for all of the
keyboard polling routines. Synchronizing the console poll with other
keyboard polls ensures maximum idle time.
Several HP operating systems require a CR and LF sequence for line
termination. This is awkward on a PC, as there is no LF key (CTRL+J is
needed instead). We provide an AUTOLF mode to add a LF automatically to each
CR input. When this mode is set, entering CR will set a flag, which will
cause a LF to be supplied automatically at the next input poll.
The 12531C teleprinter interface and the later 12880A CRT interface provide a
clever mechanism to detect a keypress during output. This is used by DOS and
RTE to allow the user to interrupt lengthy output operations to enter system
commands.
Referring to the 12531C schematic, the terminal input enters on pin X
("DATA FROM EIA COMPATIBLE DEVICE"). The signal passes through four
transistor inversions (Q8, Q1, Q2, and Q3) to appear on pin 12 of NAND gate
U104C. If the flag flip-flop is not set, the terminal input passes to the
(inverted) output of U104C and thence to the D input of the first of the
flip-flops forming the data register.
In the idle condition (no key pressed), the terminal input line is marking
(voltage negative), so in passing through a total of five inversions, a
logic one is presented at the serial input of the data register. During an
output operation, the register is parallel loaded and serially shifted,
sending the output data through the register to the device and -- this is
the crux -- filling the register with logic ones from U104C.
At the end of the output operation, the card flag is set, an interrupt
occurs, and the RTE driver is entered. The driver then does an LIA SC to
read the contents of the data register. If no key has been pressed during
the output operation, the register will read as all ones (octal 377). If,
however, any key was struck, at least one zero bit will be present. If the
register value doesn't equal 377, the driver sets the system "operator
attention" flag, which will cause DOS or RTE to output an asterisk prompt and
initiate a terminal read when the current output line is completed.
*/
static t_stat keyboard_service (UNIT *uptr)
{
t_stat status;
char input;
tprintf (tty_dev, TRACE_PSERV, "Poll delay %d service entered\n",
uptr->wait);
uptr->wait = hp_sync_poll (SERVICE); /* coschedule with the poll timer */
sim_activate (uptr, uptr->wait); /* and continue the poll */
if (tty.cr_seen && key_unit.flags & UNIT_AUTOLF) { /* if a CR was seen and auto-linefeed mode is active */
input = LF; /* then simulate the input of a LF */
tprintf (tty_dev, TRACE_XFER, "Character LF generated internally\n");
}
else { /* otherwise */
status = sim_poll_kbd (); /* poll the simulation console keyboard for input */
if (status < SCPE_KFLAG) /* if a character was not present or an error occurred */
return status; /* then return the poll status */
if (status & SCPE_BREAK) { /* if a break was detected */
input = NUL; /* then the character reads as NUL */
tprintf (tty_dev, TRACE_XFER, "Break detected\n");
}
else { /* otherwise */
input = (char) sim_tt_inpcvt ((int32) status, /* convert the character using the input mode */
TT_GET_MODE (uptr->flags));
tprintf (tty_dev, TRACE_XFER, "Character %s entered at keyboard\n",
fmt_char ((uint32) input));
}
}
tty.cr_seen = (input == CR); /* set the CR seen flag if a CR was entered */
if (tty.mode & CN_INPUT) { /* if the card is set for input */
tty.io_data = input; /* then store the character in the data register */
tty.shift_in_data = MARK; /* and indicate that the serial line is marking */
uptr->pos = uptr->pos + 1; /* count the character */
tty.flag_buffer = SET; /* set the flag buffer */
io_assert (&tty_dev, ioa_ENF); /* and the flag */
if (tty.mode & (CN_PRINT | CN_PUNCH)) /* if the printer or punch is enabled */
status = output ((int32) input); /* then scho the received character */
else /* otherwise */
status = SCPE_OK; /* silently indicate success */
}
else { /* otherwise the card is set for output */
tty.shift_in_data = input; /* and the received character will be shifted in */
status = SCPE_OK;
}
return status; /* return the status of the operation */
}
/* TTY output service routine.
The output service routine is scheduled when the interface receives an STC
signal. On entry, the data output register contains the character to output.
A prior control word will have enabled either the printer, the punch, both,
or neither. This selective output is handled by the "output" routine.
If the output is stalled, the current wait time is doubled, and the service
is rescheduled. However, if the new wait time would be longer than the
current poll time, the poll time is used. This permits rapid recovery from a
temporary stall but preserves idling if the stall persists.
As noted in the comments above, if no key is pressed while output is in
progress, the input shift register will contain all ones, which is the
marking condition of the serial input line. However, if a key is pressed,
the register will contain something other than all ones; the exact value is
indeterminate, as it depends on the timing between the keypress and the print
operation.
*/
static t_stat print_punch_service (UNIT *uptr)
{
t_stat status;
tprintf (tty_dev, TRACE_SERV, "Printer and punch service entered\n");
status = output ((int32) tty.io_data); /* output the character if enabled */
if (status == SCPE_OK) { /* if the output succeeded */
tty.io_data = tty.shift_in_data; /* then shift the input line data into the buffer */
tty.shift_in_data = MARK; /* and indicate that the serial line is marking */
tty.flag_buffer = SET; /* set the flag buffer */
io_assert (&tty_dev, ioa_ENF); /* and the flag */
return SCPE_OK; /* return success */
}
else { /* otherwise an error occurred */
uptr->wait = uptr-> wait * 2; /* so double the current wait time for the next check */
if (uptr->wait > key_unit.wait) /* if the wait is longer than the keyboard poll wait */
uptr->wait = sim_activate_time (&key_unit); /* then use the remaining poll time as the wait */
sim_activate (uptr, uptr->wait); /* reschedule the output */
if (status == SCPE_STALL) { /* if an output stall occurred */
status = SCPE_OK; /* then retry it */
tprintf (tty_dev, TRACE_XFER, "Character %s print transmission rescheduled for stall\n",
fmt_char (sim_tt_outcvt ((int32) tty.io_data, TT_GET_MODE (print_unit.flags))));
}
return status; /* return the operation status */
}
}
/* TTY print/punch output routine.
This routine outputs the supplied character to the print and/or punch unit as
directed by a prior control word sent to the interface. For output, the
control word may set the print flip-flop, the punch flip-flop, or both
flip-flops. These flip-flops generate the PRINT COMMAND and PUNCH COMMAND
output signals, respectively. Setting either one enables data transmission.
Only the HP 2754A (ASR35) teleprinter responds to the PRINT and PUNCH COMMAND
signals. All other terminals ignore these signals and respond only to the
serial data out signal (the paper tape punches on the 2749A and 2752A
teleprinters must be enabled manually at the console and operate concurrently
with the printers).
This routine simulates a 2754A when the punch unit (TTY unit 2) is attached
and a generic terminal when the unit is detached. With the punch unit
attached, the punch flip-flop must be set to punch, and the print flip-flop
must be set to print. These flip-flops, and therefore their respective
operations, are independent. When the punch unit is detached, printing will
occur if either the print or punch flip-flop is set. If neither flip-flop is
set, no output occurs. Therefore, the logic is:
if punch-flip-flop and punch-attached
then punch character
if print-flip-flop or punch-flip-flop and not punch-attached
then print character
Certain HP programs, e.g., HP 2000F BASIC FOR DOS-M/DOS III, depend on the
generic (2752A et. al.) behavior. The DOS and RTE teleprinter drivers
support text and binary output modes. Text mode sets the print flip-flop,
and binary mode sets the punch flip-flop. These programs use binary mode to
write single characters to the teleprinter and expect that they will be
printed. The simulator follows this behavior.
*/
static t_stat output (int32 character)
{
int32 print_char;
t_stat status = SCPE_OK;
if (tty.mode & CN_PUNCH /* if punching is enabled */
&& punch_unit.flags & UNIT_ATT) /* and the punch is attached */
if (fputc ((int) character, punch_unit.fileref) == EOF) { /* then write the byte; if the write fails */
cprintf ("%s simulator teleprinter punch I/O error: %s\n", /* then report the error to the console */
sim_name, strerror (errno));
clearerr (punch_unit.fileref); /* clear the error */
status = SCPE_IOERR; /* and stop the simulator */
}
else { /* otherwise the output succeeded */
punch_unit.pos = ftell (punch_unit.fileref); /* so update the file position */
tprintf (tty_dev, TRACE_XFER, "Data %03o character %s sent to punch\n",
character, fmt_char ((uint32) character));
}
if (tty.mode & CN_PRINT /* if printing is enabled */
|| tty.mode & CN_PUNCH /* or punching is enabled */
&& (punch_unit.flags & UNIT_ATT) == 0) { /* and the punch is not attached */
print_char = sim_tt_outcvt (character, /* then convert the character using the output mode */
TT_GET_MODE (print_unit.flags));
if (print_char >= 0) { /* if the character is valid */
status = sim_putchar_s (print_char); /* then output it to the simulation console */
if (status == SCPE_OK) { /* if the output succeeded */
print_unit.pos = print_unit.pos + 1; /* then update the file position */
tprintf (tty_dev, TRACE_XFER, "Character %s sent to printer\n",
fmt_char ((uint32) print_char));
}
}
else /* otherwise the character was filtered out */
tprintf (tty_dev, TRACE_XFER, "Character %s discarded by output filter\n",
fmt_char ((uint32) character));
}
return status; /* return the status of the operation */
}

7121
doc/hp2100_bugfixes.txt Normal file

File diff suppressed because it is too large Load Diff

4640
doc/hp2100_diag.txt Normal file

File diff suppressed because it is too large Load Diff

32313
doc/hp2100_guide.pdf Normal file

File diff suppressed because it is too large Load Diff

1712
doc/hp2100_release.txt Normal file

File diff suppressed because it is too large Load Diff