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:
committed by
Mark Pizzolato
parent
2288fa348f
commit
324fba049a
1748
HP2100/hp2100_baci.c
Normal file
1748
HP2100/hp2100_baci.c
Normal file
File diff suppressed because it is too large
Load Diff
5234
HP2100/hp2100_cpu.c
Normal file
5234
HP2100/hp2100_cpu.c
Normal file
File diff suppressed because it is too large
Load Diff
645
HP2100/hp2100_cpu.h
Normal file
645
HP2100/hp2100_cpu.h
Normal 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
801
HP2100/hp2100_cpu0.c
Normal 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
759
HP2100/hp2100_cpu1.c
Normal 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
803
HP2100/hp2100_cpu2.c
Normal 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
840
HP2100/hp2100_cpu3.c
Normal 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
1136
HP2100/hp2100_cpu4.c
Normal file
File diff suppressed because it is too large
Load Diff
2531
HP2100/hp2100_cpu5.c
Normal file
2531
HP2100/hp2100_cpu5.c
Normal file
File diff suppressed because it is too large
Load Diff
1076
HP2100/hp2100_cpu6.c
Normal file
1076
HP2100/hp2100_cpu6.c
Normal file
File diff suppressed because it is too large
Load Diff
1013
HP2100/hp2100_cpu7.c
Normal file
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
207
HP2100/hp2100_cpu_dmm.h
Normal 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
417
HP2100/hp2100_cpu_fp.c
Normal 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
98
HP2100/hp2100_cpu_fp.h
Normal 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
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
944
HP2100/hp2100_defs.h
Normal 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
1880
HP2100/hp2100_di.c
Normal file
File diff suppressed because it is too large
Load Diff
262
HP2100/hp2100_di.h
Normal file
262
HP2100/hp2100_di.h
Normal 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
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
2412
HP2100/hp2100_disclib.c
Normal file
File diff suppressed because it is too large
Load Diff
387
HP2100/hp2100_disclib.h
Normal file
387
HP2100/hp2100_disclib.h
Normal 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
1063
HP2100/hp2100_dma.c
Normal file
File diff suppressed because it is too large
Load Diff
1789
HP2100/hp2100_dp.c
Normal file
1789
HP2100/hp2100_dp.c
Normal file
File diff suppressed because it is too large
Load Diff
1361
HP2100/hp2100_dq.c
Normal file
1361
HP2100/hp2100_dq.c
Normal file
File diff suppressed because it is too large
Load Diff
1054
HP2100/hp2100_dr.c
Normal file
1054
HP2100/hp2100_dr.c
Normal file
File diff suppressed because it is too large
Load Diff
1684
HP2100/hp2100_ds.c
Normal file
1684
HP2100/hp2100_ds.c
Normal file
File diff suppressed because it is too large
Load Diff
458
HP2100/hp2100_io.h
Normal file
458
HP2100/hp2100_io.h
Normal 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
2049
HP2100/hp2100_ipl.c
Normal file
File diff suppressed because it is too large
Load Diff
723
HP2100/hp2100_lps.c
Normal file
723
HP2100/hp2100_lps.c
Normal 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
2210
HP2100/hp2100_lpt.c
Normal file
File diff suppressed because it is too large
Load Diff
620
HP2100/hp2100_mc.c
Normal file
620
HP2100/hp2100_mc.c
Normal 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
2388
HP2100/hp2100_mem.c
Normal file
File diff suppressed because it is too large
Load Diff
3226
HP2100/hp2100_mpx.c
Normal file
3226
HP2100/hp2100_mpx.c
Normal file
File diff suppressed because it is too large
Load Diff
1685
HP2100/hp2100_ms.c
Normal file
1685
HP2100/hp2100_ms.c
Normal file
File diff suppressed because it is too large
Load Diff
898
HP2100/hp2100_mt.c
Normal file
898
HP2100/hp2100_mt.c
Normal 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
2019
HP2100/hp2100_mux.c
Normal file
File diff suppressed because it is too large
Load Diff
439
HP2100/hp2100_pif.c
Normal file
439
HP2100/hp2100_pif.c
Normal 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
1191
HP2100/hp2100_pt.c
Normal file
File diff suppressed because it is too large
Load Diff
5087
HP2100/hp2100_sys.c
Normal file
5087
HP2100/hp2100_sys.c
Normal file
File diff suppressed because it is too large
Load Diff
624
HP2100/hp2100_tbg.c
Normal file
624
HP2100/hp2100_tbg.c
Normal 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
999
HP2100/hp2100_tty.c
Normal 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
7121
doc/hp2100_bugfixes.txt
Normal file
File diff suppressed because it is too large
Load Diff
4640
doc/hp2100_diag.txt
Normal file
4640
doc/hp2100_diag.txt
Normal file
File diff suppressed because it is too large
Load Diff
32313
doc/hp2100_guide.pdf
Normal file
32313
doc/hp2100_guide.pdf
Normal file
File diff suppressed because it is too large
Load Diff
1712
doc/hp2100_release.txt
Normal file
1712
doc/hp2100_release.txt
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user