blob: 18cb772a2bf34ce245bb129ac9b3dcc2d41bd2d3 [file] [log] [blame] [raw]
/* hp2100_sys.c: HP 2100 system common interface
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.
07-Mar-18 JDB Added the GET_SWITCHES macro from scp.c
22-Feb-18 JDB Added the <dev> option to the LOAD command
07-Sep-17 JDB Replaced "uint16" cast with "MEMORY_WORD" for loader ROM
07-Aug-17 JDB Added "hp_attach" to attach a file for appending
01-Aug-17 JDB Added "ispunct" test for implied mnemonic parse
20-Jul-17 JDB Removed STOP_OFFLINE, STOP_PWROFF messages
14-Jul-17 JDB Interrupt deferral is now calculated in instr postlude
11-Jul-17 JDB Moved "hp_enbdis_pair" here from hp2100_cpu.c
Renamed "ibl_copy" to "cpu_ibl"
16-May-17 JDB Rewrote "fprint_sym" and "parse_sym" for better coverage
03-Apr-17 JDB Rewrote "parse_cpu" to improve efficiency and coverage
25-Mar-17 JDB Increased "sim_emax" from 3 to 10 for VIS instructions
20-Nar-17 JDB Rewrote "fprint_cpu" to improve efficiency and coverage
03-Mar-17 JDB Added binary input parsing
01-Mar-17 JDB Added physical vs. logical address parsing and printing
27-Feb-17 JDB Revised the LOAD command and added the DUMP command
25-Jan-17 JDB Replaced ReadIO with mem_fast_read, added hp_trace routine
22-Jan-17 JDB Separated instruction mnemonic printing
15-Jan-17 JDB Corrected HLT decoding to add the 1060xx and 1070xx ranges
Corrected SFB decoding
14-Jan-17 JDB Removed use of Fprintf functions for version 4.x and on
30-Dec-16 JDB Corrected parsing of memory reference instructions
13-May-16 JDB Modified for revised SCP API function parameter types
19-Jun-15 JDB Conditionally use Fprintf function for version 4.x and on
18-Jun-15 JDB Added cast to int for isspace parameter
24-Dec-14 JDB Added casts to t_addr and t_value for 64-bit compatibility
Made local routines static
05-Feb-13 JDB Added hp_fprint_stopped to handle HLT instruction message
18-Mar-13 JDB Moved CPU state variable declarations to hp2100_cpu.h
09-May-12 JDB Quieted warnings for assignments in conditional expressions
10-Feb-12 JDB Deprecated DEVNO in favor of SC
Added hp_setsc, hp_showsc functions to support SC modifier
15-Dec-11 JDB Added DA and dummy DC devices
29-Oct-10 JDB DMA channels renamed from 0,1 to 1,2 to match documentation
26-Oct-10 JDB Changed DIB access for revised signal model
03-Sep-08 JDB Fixed IAK instruction dual-use mnemonic display
07-Aug-08 JDB Moved hp_setdev, hp_showdev from hp2100_cpu.c
Changed sim_load to use WritePW instead of direct M[] access
18-Jun-08 JDB Added PIF device
17-Jun-08 JDB Moved fmt_char() function from hp2100_baci.c
26-May-08 JDB Added MPX device
24-Apr-08 JDB Changed fprint_sym to handle step with irq pending
07-Dec-07 JDB Added BACI device
27-Nov-07 JDB Added RTE OS/VMA/EMA mnemonics
21-Dec-06 JDB Added "fwanxm" external for sim_load check
19-Nov-04 JDB Added STOP_OFFLINE, STOP_PWROFF messages
25-Sep-04 JDB Added memory protect device
Fixed display of CCA/CCB/CCE instructions
01-Jun-04 RMS Added latent 13037 support
19-Apr-04 RMS Recognize SFS x,C and SFC x,C
22-Mar-02 RMS Revised for dynamically allocated memory
14-Feb-02 RMS Added DMS instructions
04-Feb-02 RMS Fixed bugs in alter/skip display and parsing
01-Feb-02 RMS Added terminal multiplexor support
16-Jan-02 RMS Added additional device support
17-Sep-01 RMS Removed multiconsole support
27-May-01 RMS Added multiconsole support
14-Mar-01 RMS Revised load/dump interface (again)
30-Oct-00 RMS Added examine to file support
15-Oct-00 RMS Added dynamic device number support
27-Oct-98 RMS V2.4 load interface
References:
- HP 1000 M/E/F-Series Computers Technical Reference Handbook
(5955-0282, March 1990)
- RTE-IV Assembler Reference Manual
(92067-90003, October 1980)
This module provides the interface between the Simulation Control Program
(SCP) and the HP 2100 simulator. It includes the required global VM
interface data definitions (e.g., the simulator name, device array, etc.),
symbolic display and parsing routines, utility routines for tracing and
execution support, and SCP command replacement routines.
*/
#include <ctype.h>
#include <stdarg.h>
#include <stdlib.h>
#include "hp2100_defs.h"
#include "hp2100_cpu.h"
/* Command-line switch parsing from scp.c */
#define GET_SWITCHES(cp) \
if ((cp = get_sim_sw (cp)) == NULL) return SCPE_INVSW
/* External I/O data structures */
extern DEVICE mp_dev; /* Memory Protect */
extern DEVICE dma1_dev, dma2_dev; /* Direct Memory Access/Dual-Channel Port Controller */
extern DEVICE ptr_dev; /* Paper Tape Reader */
extern DEVICE ptp_dev; /* Paper Tape Punch */
extern DEVICE tty_dev; /* Teleprinter */
extern DEVICE clk_dev; /* Time Base Generator */
extern DEVICE lps_dev; /* 2767 Line Printer */
extern DEVICE lpt_dev; /* 2607/13/17/18 Line Printer */
extern DEVICE baci_dev; /* Buffered Asynchronous Communication Interface */
extern DEVICE mpx_dev; /* Eight-Channel Asynchronous Multiplexer */
extern DEVICE mtd_dev, mtc_dev; /* 3030 Magnetic Tape Drive */
extern DEVICE msd_dev, msc_dev; /* 7970B/E Magnetic Tape Drive */
extern DEVICE dpd_dev, dpc_dev; /* 2870/7900 Disc Drive */
extern DEVICE dqd_dev, dqc_dev; /* 2883 Disc Drive */
extern DEVICE drd_dev, drc_dev; /* 277x Disc/Drum Drive */
extern DEVICE ds_dev; /* 7905/06/20/25 Disc Drive */
extern DEVICE muxl_dev, muxu_dev, muxc_dev; /* Sixteen-Channel Asynchronous Multiplexer */
extern DEVICE ipli_dev, iplo_dev; /* Processor Interconnect */
extern DEVICE pif_dev; /* Privileged Interrupt Fence */
extern DEVICE da_dev; /* 7906H/20H/25H ICD Disc Drive */
extern DEVICE dc_dev; /* Dummy HP-IB Interface */
/* Program constants */
#define VAL_EMPTY (PA_MAX + 1) /* flag indicating that the value array must be loaded */
/* Symbolic production/consumption values */
#define SCPE_OK_2_WORDS ((t_stat) -1) /* two words produced or consumed */
/* Symbolic mode and format override switches */
#define A_SWITCH SWMASK ('A')
#define B_SWITCH SWMASK ('B')
#define C_SWITCH SWMASK ('C')
#define D_SWITCH SWMASK ('D')
#define H_SWITCH SWMASK ('H')
#define M_SWITCH SWMASK ('M')
#define O_SWITCH SWMASK ('O')
#define MODE_SWITCHES (C_SWITCH | M_SWITCH)
#define FORMAT_SWITCHES (A_SWITCH | B_SWITCH | D_SWITCH | H_SWITCH | O_SWITCH)
#define SYMBOLIC_SWITCHES (C_SWITCH | M_SWITCH | A_SWITCH)
#define ALL_SWITCHES (MODE_SWITCHES | FORMAT_SWITCHES)
/* Operand types.
Operand types indicate how to print or parse instruction mnemonics. There is
a separate operand type for each unique operand interpretation. For
printing, the operand type and associated operand mask indicate which bits
form the operand value and what interpretation is to be imposed on that
value. For parsing, the operand type additionally implies the acceptable
syntax for symbolic entry.
Operand masks are used to isolate the operand value from the instruction
word. As provided, a logical AND removes the operand value; an AND with the
complement leaves only the operand value. The one-for-one correspondence
between operand types and masks must be preserved when adding new operand
types.
Implementation notes:
1. Operand masks are defined as the complements of the operand bits to make
it easier to see which bits will be cleared when the value is ANDed. The
complements are calculated at compile-time and so impose no run-time
penalty.
*/
typedef enum {
opNone, /* no operand */
opMPOI, /* MRG page bit 10, offset 0000-1777 octal, indirect bit 15 */
opSC, /* IOG select code range 00-77 octal */
opSCHC, /* IOG select code range 00-77 octal, hold/clear bit 9 */
opSCOHC, /* IOG optional select code range 00-77 octal, hold/clear bit 9 */
opHC, /* IOG hold/clear bit 9 */
opShift, /* EAU shift/rotate count range 1-16 */
opIOPON, /* IOP index negative offset range 1-20 octal */
opIOPOP, /* IOP index positive offset range 0-17 octal */
opIOPO, /* IOP index offset range 0-37 octal (+20 bias) */
opZA4, /* UIG zero word, four (indirect) memory addresses */
opZA5, /* UIG zero word, five (indirect) memory addresses */
opZA6, /* UIG zero word, six (indirect) memory addresses */
opZA8, /* UIG zero word, eight (indirect) memory addresses */
opV1, /* UIG one data value */
opV2, /* UIG two data values */
opA1V1, /* UIG one (indirect) memory address, one data value */
opV2A1, /* UIG two data values, one (indirect) memory address */
opV1A5, /* UIG one data value, five (indirect) memory addresses */
opMA1ZI, /* One memory address range 00000-77777 octal, zero word, indirect bit 15 */
opMA1I, /* One memory address range 00000-77777 octal, indirect bit 15 */
opMA2I, /* Two memory addresses range 00000-77777 octal, indirect bit 15 */
opMA3I, /* Three memory addresses range 00000-77777 octal, indirect bit 15 */
opMA4I, /* Four memory addresses range 00000-77777 octal, indirect bit 15 */
opMA5I, /* Five memory addresses range 00000-77777 octal, indirect bit 15 */
opMA6I, /* Six memory addresses range 00000-77777 octal, indirect bit 15 */
opMA7I /* Seven memory addresses range 00000-77777 octal, indirect bit 15 */
} OP_TYPE;
typedef struct {
t_value mask; /* operand mask */
int32 count; /* operand count */
uint32 address_set; /* address operand bitset */
} OP_PROP;
static const OP_PROP op_props [] = { /* operand properties, indexed by OP_TYPE */
{ ~0000000u, 0, 000000u }, /* opNone */
{ ~0101777u, 0, 000001u }, /* opMPOI */
{ ~0000077u, 0, 000000u }, /* opSC */
{ ~0000077u, 0, 000000u }, /* opSCHC */
{ ~0000077u, 0, 000000u }, /* opSCOHC */
{ ~0001000u, 0, 000000u }, /* opHC */
{ ~0000017u, 0, 000000u }, /* opShift */
{ ~0000017u, 0, 000000u }, /* opIOPON */
{ ~0000017u, 0, 000000u }, /* opIOPOP */
{ ~0000037u, 0, 000000u }, /* opIOPO */
{ ~0000000u, 5, 000036u }, /* opZA4 */
{ ~0000000u, 6, 000076u }, /* opZA5 */
{ ~0000000u, 7, 000176u }, /* opZA6 */
{ ~0000000u, 9, 000776u }, /* opZA8 */
{ ~0000000u, 1, 000000u }, /* opV1 */
{ ~0000000u, 2, 000000u }, /* opV2 */
{ ~0000000u, 2, 000001u }, /* opA1V1 */
{ ~0000000u, 3, 000004u }, /* opV2A1 */
{ ~0000000u, 6, 000076u }, /* opV1A5 */
{ ~0000000u, 2, 000001u }, /* opMA1ZI */
{ ~0000000u, 1, 000001u }, /* opMA1I */
{ ~0000000u, 2, 000003u }, /* opMA2I */
{ ~0000000u, 3, 000007u }, /* opMA3I */
{ ~0000000u, 4, 000017u }, /* opMA4I */
{ ~0000000u, 5, 000037u }, /* opMA5I */
{ ~0000000u, 6, 000077u }, /* opMA6I */
{ ~0000000u, 7, 000177u } /* opMA7I */
};
/* Instruction classifications.
Machine instructions on the HP 21xx/1000 are identified by a varying number
of bits. In general, the five most-significant bits identify the general
group of instruction, and additional bits form a sub-opcode within a group to
identify an instruction uniquely. However, many instructions are irregular,
and those from two groups -- the Shift-Rotate Group and the Alter-Skip Group
-- are formed from multiple "micro-operations" that may be combined to
perform up to eight operations per instruction. Instructions from the
Extended Arithmetic Group have a number of reserved bits that are defined to
be zero. Correct hardware decoding may or may not depend on these bits being
zero and varies from CPU model to model.
Each instruction is classified by a mnemonic, a base operation code (without
the operand), an operand type, and a mask for the significant bits that
identify the opcode. Classifications are grouped by class of instruction
into arrays that are indexed by sub-opcodes, if applicable. Two-word
instructions will have base operation codes and signifiant bits masks that
extend to 32 bits.
An opcode table consists of two parts, either of which is optional. If a
given class has a sub-opcode that fully or almost fully decodes the class,
the first (primary) part of the table contains the appropriate number of
classification elements. This allows rapid access to a specific instruction
based on its bit pattern. In this primary part, the significant opcode bits
masks are not defined or used.
A number of instructions use bit 11 to indicate whether the instruction
applies to the A-register or B-register. If a class uses a primary part, and
that class also uses the A/B-register indicator, then the primary table is
twice the expected length (based on the number of significant sub-opcode
bits). The first half of the table applies to the sub-opcodes where bit 11
is zero (meaning, "use the A-register"), and the second half applies where
bit 11 is one (meaning, "use the B-register").
If some opcodes in a class are defined by a limited set of significant bits,
if the sub-opcode decoding is not regular, or if the instruction requires two
words to decode, then a second (secondary) part of the table contains
classification elements that specify which bits of the opcode are
significant. This part is searched linearly.
The secondary table may contain dependent or independent entries. In the
former case, an instruction will match only one of the entries, and the
search terminates when the match occurs. An example is the EAG table. In
the latter case, an instruction may independently match multiple entries, and
the search must continue through the end of the table. Examples are the SRG
and ASG tables.
A primary entry that requires additional bits to decode may indicate that the
secondary section is to be searched by setting a null string ("") as the
instruction mnemonic. The end of the opcode table is indicated by a NULL
mnemonic pointer.
As an example, the Memory Reference Group of instructions have bits 14-12
not equal to zero. These seven values are combined with bit 11 to encode one
of 14 instructions. The opcode table consists of 16 primary entries, indexed
by bits 14-11 (the first two entries are not valid MRG instructions).
As a contrasting example, the Shift-Rotate Group instructions contain four
fields. The first field is defined by bits 8-6 that select one of eight
shift or rotate operations, plus bit 11 that selects whether the operation
applies to the A-register or the B-register. This field is decoded by an
opcode table consisting of 16 primary entries in two halves. The first half
is indexed by bits 8-6 when bit 11 is zero; the second half is indexed by
bits 8-6 when bit 11 is one.
The second and third fields use bits 5 and 3 to enable "clear E-register" and
"skip if the A/B-register is zero" operations, respectively. These are
decoded by a secondary table of four entries that is searched linearly. The
significant opcode bits masks are used to mask the instruction word to those
bits that identify the particular instruction.
The fourth field encodes a second shift/rotate operation using bits 2-0. The
opcode table is structured identically to the one for the first field.
A third example is the I/O Group opcode table. IOG instructions are
primarily encoded by bits 8-6. A few of these sub-opcodes additionally use
bit 11 to select the A/B-register or bit 9 for additional instruction
differentiation. The opcode table consists of a two-part primary section,
indexed by bits 8-6 and bit 11, and a secondary section that is searched
linearly to decode the additional bits.
The content of each opcode table is described by an opcode descriptor. This
structure contains the mask and alignment shift to apply to the instruction
to obtain the primary index, the mask to obtain the A/B-register select bit
from the instruction, and the CPU feature that must be enabled to enable the
associated instructions. If the opcode table contains only secondary
entries, the mask field contains the OP_LINEAR value, and the shift field
contains the OP_SINGLE or OP_MULTIPLE value, depending on whether the opcode
entries are dependent or independent, respectively. The register selector
field contains either AB_SELECT or AB_UNUSED values, depending on whether or
not the instruction uses bit 11 to select between the A and B registers. The
required feature field contains a mask that is applied to the user flags
field of the CPU device to select a CPU type and firmware option set. For
example, the descriptor for the I/O Processor opcodes for the 2100 CPU
contains "UNIT_IOP | CPU_2100" as the required feature field value.
Implementation notes:
1. An opcode table containing both primary and secondary entries uses the
"mask" and "shift" fields to obtain the primary index. Therefore, the
alternate use of the "shift" field to designate dependent vs. independent
secondaries is not available, and secondaries are assumed to be
dependent.
2. Two-word instructions have the first word in the lower half of the 32-bit
"opcode" field, and the second word in the upper half. The "op_bits"
field is similarly arranged.
3. An opcode descriptor and its associated opcode table form an integral
object. Ideally, they would be a single structure. However, while C
allows a structure to contain a "flexible array member" as the last
field, which could represent the opcode table array, the array cannot be
initialized statically. Therefore, separate structures and arrays are
used.
4. Opcode entries with lower-case mnemonics can be printed but will never
match when parsing (because the command line is upshifted before calling
the "parse_sym" routine). Lower-case mnemonics are used for instructions
with undocumented names (e.g., self-test instructions) and for
non-canonical opcodes that execute as other, canonical instructions
(e.g., 105700, which executes as XMM).
*/
#define OP_LINEAR 0u /* (mask) table contains linear search entries only */
#define OP_SINGLE 0u /* (shift) match only a single linear entry */
#define OP_MULTIPLE 1u /* (shift) match multiple linear entries */
#define AB_SELECT 0004000u /* mask to select the A or B register */
#define AB_UNUSED 0000000u /* mask to use when the A/B register bit is not used */
#define OPTION_MASK (UNIT_OPTS | UNIT_MODEL_MASK) /* feature flags mask */
#define BASE_SET UNIT_MODEL_MASK /* base set feature flags */
typedef struct { /* opcode descriptor */
uint32 mask; /* mask to get opcode selection */
uint32 shift; /* shift to get the opcode selection */
uint32 ab_selector; /* mask to get the A/B-register selector */
uint32 feature; /* feature set to which opcodes apply */
} OP_DESC;
typedef struct { /* opcode table entry */
const char *mnemonic; /* symbolic name of the opcode */
t_value opcode; /* base value of the opcode */
OP_TYPE type; /* type of operand(s) to the opcode */
t_value op_bits; /* significant opcode bits mask */
} OP_ENTRY;
typedef OP_ENTRY OP_TABLE []; /* a table of opcode entries */
/* Memory Reference Group.
The memory reference instructions are indicated by bits 14-12 not equal to
000. They are are fully decoded by bits 14-11. The table consists of 16
primary entries.
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| I | mem op | C | memory address | MRG
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| I | mem op | B | C | memory address | MRG
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
Where:
I = direct/indirect (0/1)
B = A/B register (0/1)
C = base/current page (0/1)
Memory operation:
0010 = AND
0011 = JDB
0100 = XOR
0101 = JMP
0110 = IOR
0111 = ISZ
100x = ADA/ADB
101x = CPA/CPB
110x = LDA/LDB
111x = STA/STB
Implementation notes:
1. The first two table entries are placeholders for bits 14-11 = 000x.
These represent Shift-Rotate Group instructions, which will not be
decoded with this table.
2. The A/B-register selector descriptor is set to "unused" because the
primary index includes bit 11.
*/
static const OP_DESC mrg_desc = { /* Memory Reference Group descriptor */
0074000u, /* opcode mask */
11u, /* opcode shift */
AB_UNUSED, /* A/B-register selector */
BASE_SET | CPU_ALL /* applicable feature flags */
};
static const OP_TABLE mrg_ops = { /* MRG opcodes, indexed by IR bits 14-11 */
{ "", 0000000u, opNone }, /* SRG */
{ "", 0004000u, opNone }, /* SRG */
{ "AND", 0010000u, opMPOI },
{ "JSB", 0014000u, opMPOI },
{ "XOR", 0020000u, opMPOI },
{ "JMP", 0024000u, opMPOI },
{ "IOR", 0030000u, opMPOI },
{ "ISZ", 0034000u, opMPOI },
{ "ADA", 0040000u, opMPOI },
{ "ADB", 0044000u, opMPOI },
{ "CPA", 0050000u, opMPOI },
{ "CPB", 0054000u, opMPOI },
{ "LDA", 0060000u, opMPOI },
{ "LDB", 0064000u, opMPOI },
{ "STA", 0070000u, opMPOI },
{ "STB", 0074000u, opMPOI },
{ NULL }
};
/* Shift-Rotate Group.
The shift-rotate instructions are indicated by bits 15, 14-12, and 10 all
equal to 0. They are decoded in three parts. First, if bit 9 is 1, then
bits 11 + 8-6 fully decode one of 16 shifts or rotations. Second, bits 5 and
11 + 3 independently decode the CLE and SLA/SLB operations. Third, if bit 4
is 1, then bits 11 + 2-0 fully decode one of 16 shifts or rotations.
Three tables are used: two to decode the shifts or rotates, and a third to
decode the CLE and SLA/SLB operations, plus the NOP that results when all IR
bits are zero. The first and third tables consist of 16 primary entries.
The second consists of four independent secondary entries.
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| 0 | 0 0 0 | B | 0 | E | op 1 | C | E | S | op 2 | SRG
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
Where:
B = A/B register (0/1)
E = disable/enable op
C = CLE
S = SLA/B
Shift/Rotate operation:
000 = ALS/BLS
001 = ARS/BRS
010 = RAL/RBL
011 = RAR/RBR
100 = ALR/BLR
101 = ERA/ERB
110 = ELA/ELB
111 = ALF/BLF
Implementation notes:
1. The srg1_ops and srg2_ops tables contain only primary entries. Normally,
primary entries don't define the op_bits fields. In these cases,
however, they are required to provide A/B conflict detection among the
two SRG operations and the SLA/SLB micro-op when parsing.
2. An SRG instruction executes as NOP if both shift-rotate operation fields
are disabled and the CLE and SLA/B fields are zero. In these cases, all
remaining bits are "don't care." The "NOP" entry in the micro-ops table
catches these cases.
*/
static const OP_DESC srg1_desc = { /* Shift-Rotate Group first descriptor */
0000700u, /* opcode mask */
6u, /* opcode shift */
AB_SELECT, /* A/B-register selector */
BASE_SET | CPU_ALL /* applicable feature flags */
};
static const OP_TABLE srg1_ops = { /* SRG opcodes, indexed by IR bits 11 + 8-6 */
{ "ALS", 0001000u, opNone, 0005700u },
{ "ARS", 0001100u, opNone, 0005700u },
{ "RAL", 0001200u, opNone, 0005700u },
{ "RAR", 0001300u, opNone, 0005700u },
{ "ALR", 0001400u, opNone, 0005700u },
{ "ERA", 0001500u, opNone, 0005700u },
{ "ELA", 0001600u, opNone, 0005700u },
{ "ALF", 0001700u, opNone, 0005700u },
{ "BLS", 0005000u, opNone, 0005700u },
{ "BRS", 0005100u, opNone, 0005700u },
{ "RBL", 0005200u, opNone, 0005700u },
{ "RBR", 0005300u, opNone, 0005700u },
{ "BLR", 0005400u, opNone, 0005700u },
{ "ERB", 0005500u, opNone, 0005700u },
{ "ELB", 0005600u, opNone, 0005700u },
{ "BLF", 0005700u, opNone, 0005700u },
{ NULL }
};
static const OP_DESC srg_udesc = { /* Shift-Rotate Group micro-ops descriptor */
OP_LINEAR, /* linear search only */
OP_MULTIPLE, /* multiple matches allowed */
AB_UNUSED, /* A/B-register selector */
BASE_SET | CPU_ALL /* applicable feature flags */
};
static const OP_TABLE srg_uops = { /* SRG micro-opcodes, searched linearly */
{ "CLE", 0000040u, opNone, 0000040u },
{ "SLA", 0000010u, opNone, 0004010u },
{ "SLB", 0004010u, opNone, 0004010u },
{ "NOP", 0000000u, opNone, 0173070u },
{ NULL }
};
static const OP_DESC srg2_desc = { /* Shift-Rotate Group second descriptor */
0000007u, /* opcode mask */
0u, /* opcode shift */
AB_SELECT, /* A/B-register selector */
BASE_SET | CPU_ALL /* applicable feature flags */
};
static const OP_TABLE srg2_ops = { /* SRG opcodes, indexed by IR bits 11 + 2-0 */
{ "ALS", 0000020u, opNone, 0004027u },
{ "ARS", 0000021u, opNone, 0004027u },
{ "RAL", 0000022u, opNone, 0004027u },
{ "RAR", 0000023u, opNone, 0004027u },
{ "ALR", 0000024u, opNone, 0004027u },
{ "ERA", 0000025u, opNone, 0004027u },
{ "ELA", 0000026u, opNone, 0004027u },
{ "ALF", 0000027u, opNone, 0004027u },
{ "BLS", 0004020u, opNone, 0004027u },
{ "BRS", 0004021u, opNone, 0004027u },
{ "RBL", 0004022u, opNone, 0004027u },
{ "RBR", 0004023u, opNone, 0004027u },
{ "BLR", 0004024u, opNone, 0004027u },
{ "ERB", 0004025u, opNone, 0004027u },
{ "ELB", 0004026u, opNone, 0004027u },
{ "BLF", 0004027u, opNone, 0004027u },
{ NULL }
};
/* Alter-Skip Group.
The alter-skip instructions are indicated by bits 15 and 14-12 equal to 0
and bit 10 equal to 1. All of the operations are independent, and the table
consists of independent secondary entries for each operation, plus a final
entry for the instruction that enables none of the operations.
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| 0 | 0 0 0 | B | 1 | a op | e op | E | S | L | I | Z | V | ASG
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
Where:
B = A/B register (0/1)
E = SEZ
S = SSA/B
L = SLA/B
I = INA/B
Z = SZA/B
V = RSS
Accumulator operation:
00 = NOP
01 = CLA/CLB
10 = CMA/CMB
11 = CCA/CCB
Extend operation:
00 = NOP
01 = CLE
10 = CME
11 = CCE
Implementation notes:
1. The CLE/CME/CCE, SEZ, and RSS fields do not depend on the A/B register
select bit. This bit is "don't care" if only these fields appear in the
instruction. The entries in the table catch these cases.
*/
static const OP_DESC asg_udesc = { /* Alter-Skip Group micro-ops descriptor */
OP_LINEAR, /* linear search only */
OP_MULTIPLE, /* multiple matches allowed */
AB_UNUSED, /* A/B-register selector */
BASE_SET | CPU_ALL /* applicable feature flags */
};
static const OP_TABLE asg_uops = { /* ASG micro-opcodes, searched linearly */
{ "CLA", 0002400u, opNone, 0007400u },
{ "CMA", 0003000u, opNone, 0007400u },
{ "CCA", 0003400u, opNone, 0007400u },
{ "CLB", 0006400u, opNone, 0007400u },
{ "CMB", 0007000u, opNone, 0007400u },
{ "CCB", 0007400u, opNone, 0007400u },
{ "SEZ", 0002040u, opNone, 0002040u },
{ "CLE", 0002100u, opNone, 0002300u },
{ "CME", 0002200u, opNone, 0002300u },
{ "CCE", 0002300u, opNone, 0002300u },
{ "SSA", 0002020u, opNone, 0006020u },
{ "SLA", 0002010u, opNone, 0006010u },
{ "INA", 0002004u, opNone, 0006004u },
{ "SZA", 0002002u, opNone, 0006002u },
{ "SSB", 0006020u, opNone, 0006020u },
{ "SLB", 0006010u, opNone, 0006010u },
{ "INB", 0006004u, opNone, 0006004u },
{ "SZB", 0006002u, opNone, 0006002u },
{ "RSS", 0002001u, opNone, 0002001u },
{ "NOP", 0002000u, opNone, 0173777u },
{ NULL }
};
/* I/O Group.
The I/O instructions are indicated by bits 14-12 equal to 0 and bits 15 and
10 equal to 1. They are fully decoded by bits 11 + 9-6. However, the HP
assembler assigns special mnemonics to the flag instructions that reference
select code 1 (the overflow register). Therefore, secondary entries are used
to provide the flag mnemonics, based on the select code employed.
Because only the flag instructions use bit 9 to differentiate between
instructions, and because the flag instructions are all relegated to
secondary entries, the primary entries need not be indexed with bit 9.
Therefore, the table consists of 16 primary entries and 8 secondary entries
for the flag instructions.
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| 1 | 0 0 0 | B | 1 | C | i/o op | select code | IOG
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
Where:
B = A/B register (0/1)
C = hold/clear flag (0/1)
I/O operation:
000 = HLT
001 = STF/CLF
010 = SFC
011 = SFS
100 = MIA/MIB
101 = LIA/LIB
110 = OTA/OTB
111 = STC/CLC
Implementation notes:
1. Bit 11 is "don't care" for the HLT, STF, CLF, SFC, and SFS instructions.
2. Bit 9, the "hold/clear" bit, is defined as 0 for the SFC and SFS
instructions; however, setting bit 9 to 1 will clear the flag after
testing (RTE depends on this behavior in $CIC, where it is used to test
the state of the interrupt system and turn it off in the same
instruction). Therefore, these entries use the "opSCHC" operand type
instead of the expected "opSC" type.
3. The select code may be omitted when entering the HLT instruction (it
defaults to 0), so a special operand code is needed when parsing this
case.
*/
static const OP_DESC iog_desc = { /* Input/Output Group descriptor */
0000700u, /* opcode mask */
6u, /* opcode shift */
AB_SELECT, /* A/B-register selector */
BASE_SET | CPU_ALL /* applicable feature flags */
};
static const OP_TABLE iog_ops = { /* IOG opcodes, indexed by IR bits 11 + 8-6 */
{ "HLT", 0102000u, opSCOHC },
{ "", 0102100u, opNone }, /* STF/CLF and STO/CLO */
{ "", 0102200u, opNone }, /* SFC and SOC */
{ "", 0102300u, opNone }, /* SFS and SOS */
{ "MIA", 0102400u, opSCHC },
{ "LIA", 0102500u, opSCHC },
{ "OTA", 0102600u, opSCHC },
{ "STC", 0102700u, opSCHC },
{ "HLT", 0106000u, opSCOHC },
{ "", 0106100u, opNone }, /* STF/CLF and STO/CLO */
{ "", 0106200u, opNone }, /* SFC and SOC */
{ "", 0106300u, opNone }, /* SFS and SOS */
{ "MIB", 0106400u, opSCHC },
{ "LIB", 0106500u, opSCHC },
{ "OTB", 0106600u, opSCHC },
{ "CLC", 0106700u, opSCHC },
{ "STO", 0102101u, opNone, 0173777u }, /* STF 01 */
{ "STF", 0102100u, opSC, 0173700u }, /* STF nn */
{ "CLO", 0103101u, opNone, 0173777u }, /* CLF 01 */
{ "CLF", 0103100u, opSC, 0173700u }, /* CLF nn */
{ "SOC", 0102201u, opHC, 0172777u }, /* SFC 01 */
{ "SFC", 0102200u, opSCHC, 0172700u }, /* SFC nn */
{ "SOS", 0102301u, opHC, 0172777u }, /* SFS 01 */
{ "SFS", 0102300u, opSCHC, 0172700u }, /* SFS nn */
{ NULL }
};
/* Extended Arithmetic Group.
The EAG instructions are part of the MAC instruction space, which is
indicated by bit 15 equal to 1 and bits 14-12 and 10 equal to 0. They are
enabled by the Extended Arithmetic Unit feature that is optional for the 2116
and standard for the 2100 and 1000-M/E/F. There are ten canonical EAG
instructions. Four arithmetic instructions are decoded by bits 11 and 9-6
with bits 5-0 equal to 0. Six shift-rotate instructions are decoded by bits
11 and 9-4 with bits 3-0 indicating the shift count. The remaining 118 bit
combinations are undefined.
Three of these undefined combinations are reserved in the 1000 E/F-Series.
Opcode 100000 is the DIAG instruction, 100060 is the TIMER instruction, and
100120 is the EXECUTE instruction. The simulator implements DIAG and TIMER;
EXECUTE does not appear to have been implemented in the original microcode
and is not simulated.
Results of execution of the other undefined instructions are dependent on the
CPU model. Rather than implementing each machine's specific behavior for
each instruction, the simulator follows the 1000 M-Series decoding,
regardless of the CPU model.
The table consists of 11 secondary entries; the final entry allows parsing of
the SWP mnemonic and encoding as RRR 16 as is permitted by the HP Assembler.
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| 1 | 0 0 0 | | 0 | eau op | 0 0 0 0 0 0 | EAG
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| 1 | 0 0 0 | | 0 | eau shift/rotate op | shift count | EAG
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
The MAC instruction space also includes the User Instruction Group. EAG
instructions use the opcode ranges 100000-101377 and 104000-104777. UIG
instructions use the ranges 101400-101477 and 105000-105777.
Implementation notes:
1. The 2100 has the strictest decoding of the EAU set, so that is the
encoding represented in the operations table.
*/
static const OP_DESC eag_desc = { /* Extended Arithmetic Group descriptor */
OP_LINEAR, /* linear search only */
OP_SINGLE, /* single match allowed */
AB_UNUSED, /* A/B-register selector */
UNIT_EAU | CPU_ALL /* applicable feature flags */
};
static const OP_TABLE eag_ops = { /* EAG opcodes, searched linearly */
{ "MPY", 0100200u, opMA1I, 0177760u },
{ "DIV", 0100400u, opMA1I, 0177760u },
{ "DLD", 0104200u, opMA1I, 0177760u },
{ "DST", 0104400u, opMA1I, 0177760u },
{ "ASL", 0100020u, opShift, 0177760u },
{ "LSL", 0100040u, opShift, 0177760u },
{ "RRL", 0100100u, opShift, 0177760u },
{ "ASR", 0101020u, opShift, 0177760u },
{ "LSR", 0101040u, opShift, 0177760u },
{ "RRR", 0101100u, opShift, 0177760u },
{ "SWP", 0101100u, opNone, 0177777u }, /* SWP is equivalent to RRR 16 */
{ NULL }
};
static const OP_DESC eag_ef_desc = { /* Extended Arithmetic Group 1000-E/F descriptor */
OP_LINEAR, /* linear search only */
OP_SINGLE, /* single match allowed */
AB_UNUSED, /* A/B-register selector */
UNIT_EAU | CPU_1000_E | CPU_1000_F /* applicable feature flags */
};
static const OP_TABLE eag_ef_ops = { /* EAG opcodes, searched linearly */
{ "DIAG", 0100000u, opNone, 0177777u },
{ "TIMER", 0100060u, opNone, 0177777u },
{ NULL }
};
/* User Instruction Group.
The UIG instructions are part of the MAC instruction space, which is
indicated by bit 15 equal to 1 and bits 14-12 and 10 equal to 0. They are
divided into two sub-groups. The first, UIG-0, occupies opcodes
105000-105377. The second, UIG-1, occupies opcodes 101400-101777 and
105400-105777. The 2100 decodes only UIG-0 instructions, whereas the 1000s
use both UIG sets. In particular, the 105740-105777 range is used by the
1000 Extended Instruction Group (EIG), which is part of the base set.
All of the optional firmware sets for the 2100 and 1000-M/E/F CPUs implement
UIG instructions. Therefore, there are separate tables for each firmware
feature.
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| 1 | 0 0 0 | B | 0 1 | module | operation | UIG
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
Where:
B = A/B register (0/1)
For instructions in the range 10x400-10x777, bit 11 optionally may be
significant. If it is, then the 101xxx and 105xxx instructions are distinct
and require separate table entries. If it is not, then either 105xxx or
101xxx may be used to invoke the instruction, although 105xxx is the
canonical form.
Implementation notes:
1. If bit 11 is significant for some instructions and not for others, then
the instructions that ignore bit 11 must be duplicated in the table and
given the 105xxx forms in both sets of entries. This ensures that when
used as a parsing table, the canonical form is obtained.
*/
/* 105000-105362 2000 I/O Processor Instructions */
static const OP_DESC iop_2100_desc = { /* 2100 I/O Processor descriptor */
OP_LINEAR, /* linear search only */
OP_SINGLE, /* single match allowed */
AB_UNUSED, /* A/B-register selector */
UNIT_IOP | CPU_2100 /* applicable feature flags */
};
static const OP_TABLE iop_2100_ops = { /* 2100 IOP opcodes, searched linearly */
{ "SBYTE", 0105300u, opNone, 0177777u },
{ "LBYTE", 0105320u, opNone, 0177777u },
{ "MBYTE", 0105120u, opMA1I, 0177777u },
{ "MWORD", 0105200u, opMA1ZI, 0177777u },
{ "LAI", 0105020u, opIOPON, 0177777u }, /* these must be split into negative and positive operands */
{ "LAI", 0105040u, opIOPOP, 0177777u }, /* because the offset does not occupy bits */
{ "SAI", 0105060u, opIOPON, 0177777u }, /* reserved for the value but instead are */
{ "SAI", 0105100u, opIOPOP, 0177777u }, /* ADDED to the base opcode value */
{ "CRC", 0105150u, opV1, 0177777u },
{ "RESTR", 0105340u, opNone, 0177777u },
{ "READF", 0105220u, opNone, 0177777u },
{ "ENQ", 0105240u, opNone, 0177777u },
{ "PENQ", 0105257u, opNone, 0177777u },
{ "DEQ", 0105260u, opNone, 0177777u },
{ "TRSLT", 0105160u, opV1, 0177777u },
{ "ILIST", 0105000u, opA1V1, 0177777u },
{ "PRFEI", 0105222u, opV2A1, 0177777u },
{ "PRFEX", 0105223u, opMA1I, 0177777u },
{ "PRFIO", 0105221u, opV2, 0177777u },
{ "SAVE", 0105362u, opNone, 0177777u },
{ NULL }
};
/* 105000-105137 Single-Precision Floating Point Instructions */
static const OP_DESC fp_desc = { /* Single-Precision Floating Point descriptor */
OP_LINEAR, /* linear search only */
OP_SINGLE, /* single match allowed */
AB_UNUSED, /* A/B-register selector */
UNIT_FP | CPU_2100 | CPU_1000_M | CPU_1000_E /* applicable feature flags */
};
static const OP_TABLE fp_ops = { /* FP opcodes, searched linearly */
{ "FAD", 0105000u, opMA1I, 0177760u }, /* bits 3-0 do not affect decoding */
{ "FSB", 0105020u, opMA1I, 0177760u }, /* bits 3-0 do not affect decoding */
{ "FMP", 0105040u, opMA1I, 0177760u }, /* bits 3-0 do not affect decoding */
{ "FDV", 0105060u, opMA1I, 0177760u }, /* bits 3-0 do not affect decoding */
{ "FIX", 0105100u, opNone, 0177760u }, /* bits 3-0 do not affect decoding */
{ "FLT", 0105120u, opNone, 0177760u }, /* bits 3-0 do not affect decoding */
{ NULL }
};
/* 105000-105137 Floating Point Processor Instructions */
static const OP_DESC fpp_desc = { /* Floating Point Processor descriptor */
0000137u, /* opcode mask */
0u, /* opcode shift */
AB_UNUSED, /* A/B-register selector */
UNIT_FP | CPU_1000_F /* applicable feature flags */
};
static const OP_TABLE fpp_ops = { /* FPP opcodes, indexed by IR bits 6-0 */
{ "FAD", 0105000u, opMA1I },
{ ".XADD", 0105001u, opMA3I },
{ ".TADD", 0105002u, opMA3I },
{ ".EADD", 0105003u, opMA3I },
{ "fptst", 0105004u, opNone }, /* self-test */
{ "xexp", 0105005u, opNone }, /* expand exponent */
{ "reset", 0105006u, opNone }, /* processor reset */
{ "exstk", 0105007u, opNone }, /* execute a stack of operands */
{ "adchk", 0105010u, opNone }, /* addressing check */
{ "", 0105011u, opNone }, /* unimplemented */
{ "", 0105012u, opNone }, /* unimplemented */
{ "", 0105013u, opNone }, /* unimplemented */
{ ".DAD", 0105014u, opMA1I },
{ "", 0105015u, opNone }, /* unimplemented */
{ "", 0105016u, opNone }, /* unimplemented */
{ "", 0105017u, opNone }, /* unimplemented */
{ "FSB", 0105020u, opMA1I },
{ ".XSUB", 0105021u, opMA3I },
{ ".TSUB", 0105022u, opMA3I },
{ ".ESUB", 0105023u, opMA3I },
{ "", 0105024u, opNone }, /* unimplemented */
{ "", 0105025u, opNone }, /* unimplemented */
{ "", 0105026u, opNone }, /* unimplemented */
{ "", 0105027u, opNone }, /* unimplemented */
{ "", 0105030u, opNone }, /* unimplemented */
{ "", 0105031u, opNone }, /* unimplemented */
{ "", 0105032u, opNone }, /* unimplemented */
{ "", 0105033u, opNone }, /* unimplemented */
{ ".DSB", 0105034u, opMA1I },
{ "", 0105035u, opNone }, /* unimplemented */
{ "", 0105036u, opNone }, /* unimplemented */
{ "", 0105037u, opNone }, /* unimplemented */
{ "FMP", 0105040u, opMA1I },
{ ".XMPY", 0105041u, opMA3I },
{ ".TMPY", 0105042u, opMA3I },
{ ".EMPY", 0105043u, opMA3I },
{ "", 0105044u, opNone }, /* unimplemented */
{ "", 0105045u, opNone }, /* unimplemented */
{ "", 0105046u, opNone }, /* unimplemented */
{ "", 0105047u, opNone }, /* unimplemented */
{ "", 0105050u, opNone }, /* unimplemented */
{ "", 0105051u, opNone }, /* unimplemented */
{ "", 0105052u, opNone }, /* unimplemented */
{ "", 0105053u, opNone }, /* unimplemented */
{ ".DMP", 0105054u, opMA1I },
{ "", 0105055u, opNone }, /* unimplemented */
{ "", 0105056u, opNone }, /* unimplemented */
{ "", 0105057u, opNone }, /* unimplemented */
{ "FDV", 0105060u, opMA1I },
{ ".XDIV", 0105061u, opMA3I },
{ ".TDIV", 0105062u, opMA3I },
{ ".EDIV", 0105063u, opMA3I },
{ "", 0105064u, opNone }, /* unimplemented */
{ "", 0105065u, opNone }, /* unimplemented */
{ "", 0105066u, opNone }, /* unimplemented */
{ "", 0105067u, opNone }, /* unimplemented */
{ "", 0105070u, opNone }, /* unimplemented */
{ "", 0105071u, opNone }, /* unimplemented */
{ "", 0105072u, opNone }, /* unimplemented */
{ "", 0105073u, opNone }, /* unimplemented */
{ ".DDI", 0105074u, opMA1I },
{ "", 0105075u, opNone }, /* unimplemented */
{ "", 0105076u, opNone }, /* unimplemented */
{ "", 0105077u, opNone }, /* unimplemented */
{ "FIX", 0105100u, opNone },
{ ".XFXS", 0105101u, opMA1I },
{ ".TXFS", 0105102u, opMA1I },
{ ".EXFS", 0105103u, opMA1I },
{ ".FIXD", 0105104u, opNone },
{ ".XFXD", 0105105u, opMA1I },
{ ".TFXD", 0105106u, opMA1I },
{ ".EFXD", 0105107u, opMA1I },
{ "", 0105110u, opNone }, /* unimplemented */
{ "", 0105111u, opNone }, /* unimplemented */
{ "", 0105112u, opNone }, /* unimplemented */
{ "", 0105113u, opNone }, /* unimplemented */
{ ".DSBR", 0105114u, opMA1I },
{ "", 0105115u, opNone }, /* unimplemented */
{ "", 0105116u, opNone }, /* unimplemented */
{ "", 0105117u, opNone }, /* unimplemented */
{ "FLT", 0105120u, opNone },
{ ".XFTS", 0105121u, opMA1I },
{ ".TFTS", 0105122u, opMA1I },
{ ".EFTS", 0105123u, opMA1I },
{ ".FLTD", 0105124u, opNone },
{ ".XFTD", 0105125u, opMA1I },
{ ".TFTD", 0105126u, opMA1I },
{ ".EFTD", 0105127u, opMA1I },
{ "", 0105130u, opNone }, /* unimplemented */
{ "", 0105131u, opNone }, /* unimplemented */
{ "", 0105132u, opNone }, /* unimplemented */
{ "", 0105133u, opNone }, /* unimplemented */
{ ".DDIR", 0105134u, opMA1I },
{ "", 0105135u, opNone }, /* unimplemented */
{ "", 0105136u, opNone }, /* unimplemented */
{ "", 0105137u, opNone }, /* unimplemented */
{ NULL }
};
/* 105200-105237 Fast FORTRAN Processor Instructions */
static const OP_DESC ffp_2100_desc = { /* 2100 Fast FORTRAN Processor descriptor */
0000037u, /* opcode mask */
0u, /* opcode shift */
AB_UNUSED, /* A/B-register selector */
UNIT_FFP | CPU_2100 /* applicable feature flags */
};
static const OP_TABLE ffp_2100_ops = { /* 2100 FFP opcodes, indexed by IR bits 4-0 */
{ "", 0105200u, opNone }, /* unimplemented */
{ "DBLE", 0105201u, opMA3I },
{ "SNGL", 0105202u, opMA2I },
{ ".XMPY", 0105203u, opMA3I },
{ ".XDIV", 0105204u, opMA3I },
{ ".DFER", 0105205u, opMA2I },
{ "", 0105206u, opNone }, /* unimplemented */
{ "", 0105207u, opNone }, /* unimplemented */
{ "", 0105210u, opNone }, /* unimplemented */
{ "", 0105211u, opNone }, /* unimplemented */
{ "", 0105212u, opNone }, /* unimplemented */
{ ".XADD", 0105213u, opMA3I },
{ ".XSUB", 0105214u, opMA3I },
{ "", 0105215u, opNone }, /* unimplemented */
{ "", 0105216u, opNone }, /* unimplemented */
{ "", 0105217u, opNone }, /* unimplemented */
{ ".XFER", 0105220u, opNone },
{ ".GOTO", 0105221u, opMA2I },
{ "..MAP", 0105222u, opMA4I },
{ ".ENTR", 0105223u, opMA1I },
{ ".ENTP", 0105224u, opMA1I },
{ "", 0105225u, opNone }, /* unimplemented */
{ "", 0105226u, opNone }, /* unimplemented */
{ "$SETP", 0105227u, opMA1I },
{ "", 0105230u, opNone }, /* unimplemented */
{ "", 0105231u, opNone }, /* unimplemented */
{ "", 0105232u, opNone }, /* unimplemented */
{ "", 0105233u, opNone }, /* unimplemented */
{ "", 0105234u, opNone }, /* unimplemented */
{ "", 0105235u, opNone }, /* unimplemented */
{ "", 0105236u, opNone }, /* unimplemented */
{ "", 0105237u, opNone }, /* unimplemented */
{ NULL }
};
static const OP_DESC ffp_m_desc = { /* M-Series Fast FORTRAN Processor descriptor */
0000037u, /* opcode mask */
0u, /* opcode shift */
AB_UNUSED, /* A/B-register selector */
UNIT_FFP | CPU_1000_M /* applicable feature flags */
};
static const OP_TABLE ffp_m_ops = { /* M-Series FFP opcodes, indexed by IR bits 4-0 */
{ "fftst", 0105200u, opNone }, /* self-test */
{ "DBLE", 0105201u, opMA3I },
{ "SNGL", 0105202u, opMA2I },
{ ".XMPY", 0105203u, opMA3I },
{ ".XDIV", 0105204u, opMA3I },
{ ".DFER", 0105205u, opMA2I },
{ ".XPAK", 0105206u, opMA1I },
{ "XADD", 0105207u, opMA4I },
{ "XSUB", 0105210u, opMA4I },
{ "XMPY", 0105211u, opMA4I },
{ "XDIV", 0105212u, opMA4I },
{ ".XADD", 0105213u, opMA3I },
{ ".XSUB", 0105214u, opMA3I },
{ ".XCOM", 0105215u, opMA1I },
{ "..DCM", 0105216u, opMA1I },
{ "DDINT", 0105217u, opMA3I },
{ ".XFER", 0105220u, opNone },
{ ".GOTO", 0105221u, opMA2I },
{ "..MAP", 0105222u, opMA4I },
{ ".ENTR", 0105223u, opMA1I },
{ ".ENTP", 0105224u, opMA1I },
{ ".PWR2", 0105225u, opMA2I },
{ ".FLUN", 0105226u, opMA1I },
{ "$SETP", 0105227u, opMA1I },
{ ".PACK", 0105230u, opMA2I },
{ "", 0105231u, opNone }, /* unimplemented */
{ "", 0105232u, opNone }, /* unimplemented */
{ "", 0105233u, opNone }, /* unimplemented */
{ "", 0105234u, opNone }, /* unimplemented */
{ "", 0105235u, opNone }, /* unimplemented */
{ "", 0105236u, opNone }, /* unimplemented */
{ "", 0105237u, opNone }, /* unimplemented */
{ NULL }
};
static const OP_DESC ffp_e_desc = { /* E-Series Fast FORTRAN Processor descriptor */
0000037u, /* opcode mask */
0u, /* opcode shift */
AB_UNUSED, /* A/B-register selector */
UNIT_FFP | CPU_1000_E /* applicable feature flags */
};
static const OP_TABLE ffp_e_ops = { /* E-Series FFP opcodes, indexed by IR bits 4-0 */
{ "fftst", 0105200u, opNone }, /* self-test */
{ "DBLE", 0105201u, opMA3I },
{ "SNGL", 0105202u, opMA2I },
{ ".XMPY", 0105203u, opMA3I },
{ ".XDIV", 0105204u, opMA3I },
{ ".DFER", 0105205u, opMA2I },
{ ".XPAK", 0105206u, opMA1I },
{ "XADD", 0105207u, opMA4I },
{ "XSUB", 0105210u, opMA4I },
{ "XMPY", 0105211u, opMA4I },
{ "XDIV", 0105212u, opMA4I },
{ ".XADD", 0105213u, opMA3I },
{ ".XSUB", 0105214u, opMA3I },
{ ".XCOM", 0105215u, opMA1I },
{ "..DCM", 0105216u, opMA1I },
{ "DDINT", 0105217u, opMA3I },
{ ".XFER", 0105220u, opNone },
{ ".GOTO", 0105221u, opMA2I },
{ "..MAP", 0105222u, opMA4I },
{ ".ENTR", 0105223u, opMA1I },
{ ".ENTP", 0105224u, opMA1I },
{ ".PWR2", 0105225u, opMA2I },
{ ".FLUN", 0105226u, opMA1I },
{ "$SETP", 0105227u, opMA1I },
{ ".PACK", 0105230u, opMA2I },
{ ".CFER", 0105231u, opMA2I },
{ "", 0105232u, opNone }, /* unimplemented */
{ "", 0105233u, opNone }, /* unimplemented */
{ "", 0105234u, opNone }, /* unimplemented */
{ "", 0105235u, opNone }, /* unimplemented */
{ "", 0105236u, opNone }, /* unimplemented */
{ "", 0105237u, opNone }, /* unimplemented */
{ NULL }
};
static const OP_DESC ffp_f_desc = { /* F-Series Fast FORTRAN Processor descriptor */
0000037u, /* opcode mask */
0u, /* opcode shift */
AB_UNUSED, /* A/B-register selector */
UNIT_FFP | CPU_1000_F /* applicable feature flags */
};
static const OP_TABLE ffp_f_ops = { /* F-Series FFP opcodes, indexed by IR bits 4-0 */
{ "fftst", 0105200u, opNone }, /* self-test */
{ "DBLE", 0105201u, opMA3I },
{ "SNGL", 0105202u, opMA2I },
{ ".DNG", 0105203u, opNone },
{ ".DCO", 0105204u, opNone },
{ ".DFER", 0105205u, opMA2I },
{ ".XPAK", 0105206u, opMA1I },
{ ".BLE", 0105207u, opMA3I },
{ ".DIN", 0105210u, opNone },
{ ".DDE", 0105211u, opNone },
{ ".DIS", 0105212u, opNone },
{ ".DDS", 0105213u, opNone },
{ ".NGL", 0105214u, opMA2I },
{ ".XCOM", 0105215u, opMA1I },
{ "..DCM", 0105216u, opMA1I },
{ "DDINT", 0105217u, opMA3I },
{ ".XFER", 0105220u, opNone },
{ ".GOTO", 0105221u, opMA2I },
{ "..MAP", 0105222u, opMA4I },
{ ".ENTR", 0105223u, opMA1I },
{ ".ENTP", 0105224u, opMA1I },
{ ".PWR2", 0105225u, opMA2I },
{ ".FLUN", 0105226u, opMA1I },
{ "$SETP", 0105227u, opMA1I },
{ ".PACK", 0105230u, opMA2I },
{ ".CFER", 0105231u, opMA2I },
{ "..FCM", 0105232u, opMA1I },
{ "..TCM", 0105233u, opMA1I },
{ "", 0105234u, opNone }, /* unimplemented */
{ "", 0105235u, opNone }, /* unimplemented */
{ "", 0105236u, opNone }, /* unimplemented */
{ "", 0105237u, opNone }, /* unimplemented */
{ NULL }
};
/* 105240-105257 RTE-IVA/B Extended Memory Instructions */
static const OP_DESC ema_desc = { /* Extended Memory Area descriptor */
0000017u, /* opcode mask */
0u, /* opcode shift */
AB_UNUSED, /* A/B-register selector */
UNIT_EMA | CPU_1000_E | CPU_1000_F /* applicable feature flags */
};
static const OP_TABLE ema_ops = { /* EMA opcodes, indexed by IR bits 3-0 */
{ ".EMIO", 0105240u, opMA3I }, /* variable operand count */
{ "MMAP", 0105241u, opMA3I },
{ "emtst", 0105242u, opNone }, /* self-test */
{ "", 0105243u, opNone }, /* unimplemented */
{ "", 0105244u, opNone }, /* unimplemented */
{ "", 0105245u, opNone }, /* unimplemented */
{ "", 0105246u, opNone }, /* unimplemented */
{ "", 0105247u, opNone }, /* unimplemented */
{ "", 0105250u, opNone }, /* unimplemented */
{ "", 0105251u, opNone }, /* unimplemented */
{ "", 0105252u, opNone }, /* unimplemented */
{ "", 0105253u, opNone }, /* unimplemented */
{ "", 0105254u, opNone }, /* unimplemented */
{ "", 0105255u, opNone }, /* unimplemented */
{ "", 0105256u, opNone }, /* unimplemented */
{ ".EMAP", 0105257u, opMA3I }, /* variable operand count */
{ NULL }
};
/* 105240-105257 RTE-6/VM Virtual Memory Instructions */
static const OP_DESC vma_desc = { /* Virtual Memory Area descriptor */
0000017u, /* opcode mask */
0u, /* opcode shift */
AB_UNUSED, /* A/B-register selector */
UNIT_VMAOS | CPU_1000_E | CPU_1000_F /* applicable feature flags */
};
static const OP_TABLE vma_ops = { /* VMA opcodes, indexed by IR bits 3-0 */
{ ".PMAP", 0105240u, opNone },
{ "$LOC", 0105241u, opMA6I },
{ "vmtst", 0105242u, opNone }, /* self-test */
{ ".SWP", 0105243u, opNone },
{ ".STAS", 0105244u, opNone },
{ ".LDAS", 0105245u, opNone },
{ "", 0105246u, opNone }, /* unimplemented */
{ ".UMPY", 0105247u, opMA1I },
{ ".IMAP", 0105250u, opMA1I }, /* operand count varies from 1-n */
{ ".IMAR", 0105251u, opMA1I },
{ ".JMAP", 0105252u, opMA1I }, /* operand count varies from 1-n */
{ ".JMAR", 0105253u, opMA1I },
{ ".LPXR", 0105254u, opMA2I },
{ ".LPX", 0105255u, opMA1I },
{ ".LBPR", 0105256u, opMA1I },
{ ".LBP", 0105257u, opNone },
{ NULL }
};
/* 105320-105337 Double Integer Instructions */
static const OP_DESC dbi_desc = { /* Double Integer Instructions descriptor */
0000017u, /* opcode mask */
0u, /* opcode shift */
AB_UNUSED, /* A/B-register selector */
UNIT_DBI | CPU_1000_E /* applicable feature flags */
};
static const OP_TABLE dbi_ops = { /* DBI opcodes, indexed by IR bits 3-0 */
{ "dbtst", 0105320u, opNone }, /* self-test */
{ ".DAD", 0105321u, opMA1I },
{ ".DMP", 0105322u, opMA1I },
{ ".DNG", 0105323u, opNone },
{ ".DCO", 0105324u, opMA1I },
{ ".DDI", 0105325u, opMA1I },
{ ".DDIR", 0105326u, opMA1I },
{ ".DSB", 0105327u, opMA1I },
{ ".DIN", 0105330u, opNone },
{ ".DDE", 0105331u, opNone },
{ ".DIS", 0105332u, opMA1I },
{ ".DDS", 0105333u, opMA1I },
{ ".DSBR", 0105334u, opMA1I },
{ "", 0105335u, opNone }, /* unimplemented */
{ "", 0105336u, opNone }, /* unimplemented */
{ "", 0105337u, opNone }, /* unimplemented */
{ NULL }
};
/* 105320-105337 Scientific Instruction Set */
static const OP_DESC sis_desc = { /* Scientific Instruction Set descriptor */
0000017u, /* opcode mask */
0u, /* opcode shift */
AB_UNUSED, /* A/B-register selector */
BASE_SET | CPU_1000_F /* applicable feature flags */
};
static const OP_TABLE sis_ops = { /* SIS opcodes, indexed by IR bits 3-0 */
{ "TAN", 0105320u, opNone },
{ "SQRT", 0105321u, opNone },
{ "ALOG", 0105322u, opNone },
{ "ATAN", 0105323u, opNone },
{ "COS", 0105324u, opNone },
{ "SIN", 0105325u, opNone },
{ "EXP", 0105326u, opNone },
{ "ALOGT", 0105327u, opNone },
{ "TANH", 0105330u, opNone },
{ "DPOLY", 0105331u, opV1A5 },
{ "/CMRT", 0105332u, opMA3I },
{ "/ATLG", 0105333u, opMA1I },
{ ".FPWR", 0105334u, opMA1I },
{ ".TPWR", 0105335u, opMA2I },
{ "", 0105336u, opNone }, /* unimplemented */
{ "sitst", 0105337u, opNone }, /* self-test */
{ NULL }
};
/* 105340-105357 RTE-6/VM Operating System Instructions */
static const OP_DESC os_desc = { /* RTE-6/VM Operating System descriptor */
0000017u, /* opcode mask */
0u, /* opcode shift */
AB_UNUSED, /* A/B-register selector */
UNIT_VMAOS | CPU_1000_E | CPU_1000_F /* applicable feature flags */
};
static const OP_TABLE os_ops = { /* RTE-6/VM OS opcodes, indexed by IR bits 3-0 */
{ "$LIBR", 0105340u, opMA1I },
{ "$LIBX", 0105341u, opMA1I },
{ ".TICK", 0105342u, opNone },
{ ".TNAM", 0105343u, opNone },
{ ".STIO", 0105344u, opMA1I }, /* operand count varies from 1-n */
{ ".FNW", 0105345u, opMA1I },
{ ".IRT", 0105346u, opMA1I },
{ ".LLS", 0105347u, opMA2I },
{ ".SIP", 0105350u, opNone },
{ ".YLD", 0105351u, opMA1I },
{ ".CPM", 0105352u, opMA2I },
{ ".ETEQ", 0105353u, opNone },
{ ".ENTN", 0105354u, opMA1I },
{ "ostst", 0105355u, opNone }, /* self-test */
{ ".ENTC", 0105356u, opMA1I },
{ ".DSPI", 0105357u, opNone },
{ NULL }
};
static const OP_DESC trap_desc = { /* RTE-6/VM Operating System trap instructions descriptor */
OP_LINEAR, /* linear search only */
OP_SINGLE, /* single match allowed */
AB_UNUSED, /* A/B-register selector */
UNIT_VMAOS | CPU_1000_E | CPU_1000_F /* applicable feature flags */
};
static const OP_TABLE trap_ops = { /* RTE-6/VM OS trap opcodes, searched linearly */
{ "$DCPC", 0105354u, opNone, 0177777u },
{ "$MPV", 0105355u, opNone, 0177777u },
{ "$DEV", 0105356u, opNone, 0177777u },
{ "$TBG", 0105357u, opNone, 0177777u },
{ NULL }
};
/* 10x400-10x437 2000 I/O Processor Instructions */
static const OP_DESC iop1_1000_desc = { /* M/E-Series I/O Processor Instructions descriptor */
OP_LINEAR, /* linear search only */
OP_SINGLE, /* single match allowed */
AB_UNUSED, /* A/B-register selector */
UNIT_IOP | CPU_1000_M | CPU_1000_E /* applicable feature flags */
};
static const OP_TABLE iop1_1000_ops = { /* M/E-Series IOP opcodes, searched linearly */
{ "SAI", 0101400u, opIOPO, 0177777u },
{ "LAI", 0105400u, opIOPO, 0177777u },
{ NULL }
};
/* 10x460-10x477 2000 I/O Processor Instructions */
static const OP_DESC iop2_1000_desc = { /* M/E-Series I/O Processor Instructions descriptor */
0000017u, /* opcode mask */
0u, /* opcode shift */
AB_UNUSED, /* A/B-register selector */
UNIT_IOP | CPU_1000_M | CPU_1000_E /* applicable feature flags */
};
static const OP_TABLE iop2_1000_ops = { /* M/E-Series IOP opcodes, indexed by IR bits 3-0 */
{ "CRC", 0105460u, opV1 },
{ "RESTR", 0105461u, opNone },
{ "READF", 0105462u, opNone },
{ "INS", 0105463u, opNone },
{ "ENQ", 0105464u, opNone },
{ "PENQ", 0105465u, opNone },
{ "DEQ", 0105466u, opNone },
{ "TRSLT", 0105467u, opV1 },
{ "ILIST", 0105470u, opA1V1 },
{ "PRFEI", 0105471u, opV2A1 },
{ "PRFEX", 0105472u, opMA1I },
{ "PRFIO", 0105473u, opV2 },
{ "SAVE", 0105474u, opNone },
{ "", 0105475u, opNone }, /* unimplemented */
{ "", 0105476u, opNone }, /* unimplemented */
{ "", 0105477u, opNone }, /* unimplemented */
{ NULL }
};
/* 10x460-10x477 Vector Instruction Set */
static const OP_DESC vis_desc = { /* Vector Instruction Set descriptor */
0000017u, /* opcode mask */
0u, /* opcode shift */
AB_SELECT, /* A/B-register selector */
UNIT_VIS | CPU_1000_F /* applicable feature flags */
};
static const OP_TABLE vis_ops = { /* VIS opcodes, indexed by IR bits 11 + 3-0 */
{ "", 0101460u, opNone }, /* .VECT single-precision */
{ "VPIV", 0101461u, opZA8 },
{ "VABS", 0101462u, opZA5 },
{ "VSUM", 0101463u, opZA4 },
{ "VNRM", 0101464u, opZA4 },
{ "VDOT", 0101465u, opZA6 },
{ "VMAX", 0101466u, opZA4 },
{ "VMAB", 0101467u, opZA4 },
{ "VMIN", 0101470u, opZA4 },
{ "VMIB", 0101471u, opZA4 },
{ "VMOV", 0101472u, opZA5 },
{ "VSWP", 0101473u, opZA5 },
{ ".ERES", 0101474u, opMA3I }, /* variable operand count */
{ ".ESEG", 0101475u, opMA2I },
{ ".VSET", 0101476u, opMA7I },
{ "vitst", 0105477u, opNone }, /* self-test */
{ "", 0105460u, opNone }, /* .VECT double-precision */
{ "DVPIV", 0105461u, opZA8 },
{ "DVABS", 0105462u, opZA5 },
{ "DVSUM", 0105463u, opZA4 },
{ "DVNRM", 0105464u, opZA4 },
{ "DVDOT", 0105465u, opZA6 },
{ "DVMAX", 0105466u, opZA4 },
{ "DVMAB", 0105467u, opZA4 },
{ "DVMIN", 0105470u, opZA4 },
{ "DVMIB", 0105471u, opZA4 },
{ "DVMOV", 0105472u, opZA5 },
{ "DVSWP", 0105473u, opZA5 },
{ ".ERES", 0101474u, opMA3I }, /* variable operand count */
{ ".ESEG", 0101475u, opMA2I },
{ ".VSET", 0101476u, opMA7I },
{ "vitst", 0105477u, opNone }, /* self-test */
{ "VADD", TO_DWORD (0000000u, 0101460u), opMA7I, D32_MASK },
{ "VSUB", TO_DWORD (0000020u, 0101460u), opMA7I, D32_MASK },
{ "VMPY", TO_DWORD (0000040u, 0101460u), opMA7I, D32_MASK },
{ "VDIV", TO_DWORD (0000060u, 0101460u), opMA7I, D32_MASK },
{ "VSAD", TO_DWORD (0000400u, 0101460u), opMA6I, D32_MASK },
{ "VSSB", TO_DWORD (0000420u, 0101460u), opMA6I, D32_MASK },
{ "VSMY", TO_DWORD (0000440u, 0101460u), opMA6I, D32_MASK },
{ "VSDV", TO_DWORD (0000460u, 0101460u), opMA6I, D32_MASK },
{ "", 0101460u, opNone, 0177777u }, /* catch unimplemented two-word instructions */
{ "DVADD", TO_DWORD (0004002u, 0105460u), opMA7I, D32_MASK },
{ "DVSUB", TO_DWORD (0004022u, 0105460u), opMA7I, D32_MASK },
{ "DVMPY", TO_DWORD (0004042u, 0105460u), opMA7I, D32_MASK },
{ "DVDIV", TO_DWORD (0004062u, 0105460u), opMA7I, D32_MASK },
{ "DVSAD", TO_DWORD (0004402u, 0105460u), opMA6I, D32_MASK },
{ "DVSSB", TO_DWORD (0004422u, 0105460u), opMA6I, D32_MASK },
{ "DVSMY", TO_DWORD (0004442u, 0105460u), opMA6I, D32_MASK },
{ "DVSDV", TO_DWORD (0004462u, 0105460u), opMA6I, D32_MASK },
{ "", 0105460u, opNone, 0177777u }, /* catch unimplemented two-word instructions */
{ NULL }
};
/* 10x600-10x617 SIGNAL/1000 Instruction Set */
static const OP_DESC sig_desc = { /* SIGNAL/1000 Instruction Set descriptor */
0000017u, /* opcode mask */
0u, /* opcode shift */
AB_UNUSED, /* A/B-register selector */
UNIT_SIGNAL | CPU_1000_F /* applicable feature flags */
};
static const OP_TABLE sig_ops = { /* SIGNAL opcodes, indexed by IR bits 3-0 */
{ "BITRV", 0105600u, opMA4I },
{ "BTRFY", 0105601u, opMA6I },
{ "UNSCR", 0105602u, opMA6I },
{ "PRSCR", 0105603u, opMA6I },
{ "BITR1", 0105604u, opMA5I },
{ "BTRF1", 0105605u, opMA7I },
{ ".CADD", 0105606u, opMA3I },
{ ".CSUB", 0105607u, opMA3I },
{ ".CMPY", 0105610u, opMA3I },
{ ".CDIV", 0105611u, opMA3I },
{ "CONJG", 0105612u, opMA3I },
{ "..CCM", 0105613u, opMA1I },
{ "AIMAG", 0105614u, opMA2I },
{ "CMPLX", 0105615u, opMA4I },
{ "", 0105616u, opNone }, /* unimplemented */
{ "sitst", 0105617u, opNone }, /* self-test */
{ NULL }
};
/* 10x700-10x737 Dynamic Mapping System Instructions */
static const OP_DESC dms_desc = { /* Dynamic Mapping System instructions descriptor */
0000037u, /* opcode mask */
0u, /* opcode shift */
AB_SELECT, /* A/B-register selector */
UNIT_DMS | CPU_1000 /* applicable feature flags */
};
static const OP_TABLE dms_ops = { /* DMS opcodes, indexed by IR bits 11 + 4-0 */
{ "xmm", 0105700u, opNone }, /* decodes as XMM */
{ "dmtst", 0105701u, opNone }, /* self-test (E/F-Series) or NOP (M-Series) */
{ "MBI", 0105702u, opNone }, /* bit 11 is not significant */
{ "MBF", 0105703u, opNone }, /* bit 11 is not significant */
{ "MBW", 0105704u, opNone }, /* bit 11 is not significant */
{ "MWI", 0105705u, opNone }, /* bit 11 is not significant */
{ "MWF", 0105706u, opNone }, /* bit 11 is not significant */
{ "MWW", 0105707u, opNone }, /* bit 11 is not significant */
{ "SYA", 0101710u, opNone },
{ "USA", 0101711u, opNone },
{ "PAA", 0101712u, opNone },
{ "PBA", 0101713u, opNone },
{ "SSM", 0105714u, opMA1I }, /* bit 11 is not significant */
{ "JRS", 0105715u, opMA2I }, /* bit 11 is not significant */
{ "", 0101716u, opNone }, /* unimplemented */
{ "", 0101717u, opNone }, /* unimplemented */
{ "XMM", 0105720u, opNone }, /* bit 11 is not significant */
{ "XMS", 0105721u, opNone }, /* bit 11 is not significant */
{ "XMA", 0101722u, opNone },
{ "", 0101723u, opNone }, /* unimplemented */
{ "XLA", 0101724u, opMA1I },
{ "XSA", 0101725u, opMA1I },
{ "XCA", 0101726u, opMA1I },
{ "LFA", 0101727u, opNone },
{ "RSA", 0101730u, opNone },
{ "RVA", 0101731u, opNone },
{ "DJP", 0105732u, opMA1I }, /* bit 11 is not significant */
{ "DJS", 0105733u, opMA1I }, /* bit 11 is not significant */
{ "SJP", 0105734u, opMA1I }, /* bit 11 is not significant */
{ "SJS", 0105735u, opMA1I }, /* bit 11 is not significant */
{ "UJP", 0105736u, opMA1I }, /* bit 11 is not significant */
{ "UJS", 0105737u, opMA1I }, /* bit 11 is not significant */
{ "xmm", 0105700u, opNone }, /* decodes as XMM */
{ "dmtst", 0105701u, opNone }, /* self-test (E/F-Series) or NOP (M-Series) */
{ "MBI", 0105702u, opNone },
{ "MBF", 0105703u, opNone },
{ "MBW", 0105704u, opNone },
{ "MWI", 0105705u, opNone },
{ "MWF", 0105706u, opNone },
{ "MWW", 0105707u, opNone },
{ "SYB", 0105710u, opNone },
{ "USB", 0105711u, opNone },
{ "PAB", 0105712u, opNone },
{ "PBB", 0105713u, opNone },
{ "SSM", 0105714u, opMA1I },
{ "JRS", 0105715u, opMA2I },
{ "", 0105716u, opNone }, /* unimplemented */
{ "", 0105717u, opNone }, /* unimplemented */
{ "XMM", 0105720u, opNone },
{ "XMS", 0105721u, opNone },
{ "XMB", 0105722u, opNone },
{ "", 0105723u, opNone }, /* unimplemented */
{ "XLB", 0105724u, opMA1I },
{ "XSB", 0105725u, opMA1I },
{ "XCB", 0105726u, opMA1I },
{ "LFB", 0105727u, opNone },
{ "RSB", 0105730u, opNone },
{ "RVB", 0105731u, opNone },
{ "DJP", 0105732u, opMA1I },
{ "DJS", 0105733u, opMA1I },
{ "SJP", 0105734u, opMA1I },
{ "SJS", 0105735u, opMA1I },
{ "UJP", 0105736u, opMA1I },
{ "UJS", 0105737u, opMA1I },
{ NULL }
};
/* 10x740-10x777 Extended Instruction Group */
static const OP_DESC eig_desc = { /* Extended Instruction Group descriptor */
0000037u, /* opcode mask */
0u, /* opcode shift */
AB_SELECT, /* A/B-register selector */
BASE_SET | CPU_1000 /* applicable feature flags */
};
static const OP_TABLE eig_ops = { /* EIG opcodes, indexed by IR bits 11 + 4-0 */
{ "SAX", 0101740u, opMA1I },
{ "CAX", 0101741u, opNone },
{ "LAX", 0101742u, opMA1I },
{ "STX", 0105743u, opMA1I }, /* bit 11 is not significant */
{ "CXA", 0101744u, opNone },
{ "LDX", 0105745u, opMA1I }, /* bit 11 is not significant */
{ "ADX", 0105746u, opMA1I }, /* bit 11 is not significant */
{ "XAX", 0101747u, opNone },
{ "SAY", 0101750u, opMA1I },
{ "CAY", 0101751u, opNone },
{ "LAY", 0101752u, opMA1I },
{ "STY", 0105753u, opMA1I }, /* bit 11 is not significant */
{ "CYA", 0101754u, opNone },
{ "LDY", 0105755u, opMA1I }, /* bit 11 is not significant */
{ "ADY", 0105756u, opMA1I }, /* bit 11 is not significant */
{ "XAY", 0101757u, opNone },
{ "ISX", 0105760u, opNone }, /* bit 11 is not significant */
{ "DSX", 0105761u, opNone }, /* bit 11 is not significant */
{ "JLY", 0105762u, opMA1I }, /* bit 11 is not significant */
{ "LBT", 0105763u, opNone }, /* bit 11 is not significant */
{ "SBT", 0105764u, opNone }, /* bit 11 is not significant */
{ "MBT", 0105765u, opMA1ZI }, /* bit 11 is not significant */
{ "CBT", 0105766u, opMA1ZI }, /* bit 11 is not significant */
{ "SFB", 0105767u, opNone }, /* bit 11 is not significant */
{ "ISY", 0105770u, opNone }, /* bit 11 is not significant */
{ "DSY", 0105771u, opNone }, /* bit 11 is not significant */
{ "JPY", 0105772u, opMA1I }, /* bit 11 is not significant */
{ "SBS", 0105773u, opMA2I }, /* bit 11 is not significant */
{ "CBS", 0105774u, opMA2I }, /* bit 11 is not significant */
{ "TBS", 0105775u, opMA2I }, /* bit 11 is not significant */
{ "CMW", 0105776u, opMA1ZI }, /* bit 11 is not significant */
{ "MVW", 0105777u, opMA1ZI }, /* bit 11 is not significant */
{ "SBX", 0105740u, opMA1I },
{ "CBX", 0105741u, opNone },
{ "LBX", 0105742u, opMA1I },
{ "STX", 0105743u, opMA1I },
{ "CXB", 0105744u, opNone },
{ "LDX", 0105745u, opMA1I },
{ "ADX", 0105746u, opMA1I },
{ "XBX", 0105747u, opNone },
{ "SBY", 0105750u, opMA1I },
{ "CBY", 0105751u, opNone },
{ "LBY", 0105752u, opMA1I },
{ "STY", 0105753u, opMA1I },
{ "CYB", 0105754u, opNone },
{ "LDY", 0105755u, opMA1I },
{ "ADY", 0105756u, opMA1I },
{ "XBY", 0105757u, opNone },
{ "ISX", 0105760u, opNone },
{ "DSX", 0105761u, opNone },
{ "JLY", 0105762u, opMA1I },
{ "LBT", 0105763u, opNone },
{ "SBT", 0105764u, opNone },
{ "MBT", 0105765u, opMA1ZI },
{ "CBT", 0105766u, opMA1ZI },
{ "SFB", 0105767u, opNone },
{ "ISY", 0105770u, opNone },
{ "DSY", 0105771u, opNone },
{ "JPY", 0105772u, opMA1I },
{ "SBS", 0105773u, opMA2I },
{ "CBS", 0105774u, opMA2I },
{ "TBS", 0105775u, opMA2I },
{ "CMW", 0105776u, opMA1ZI },
{ "MVW", 0105777u, opMA1ZI },
{ NULL }
};
/* Parsing tables.
Symbolic entry of instructions requires parsing the command line and matching
the instruction mnemonic to an entry in an opcode table. The parser table
contains pointers to all of the opcode tables and their associated
descriptors. Parsing searches linearly in the order specified, so more
common instructions should appear before less common ones.
*/
typedef struct { /* parser table entry */
const OP_DESC *descriptor; /* opcode descriptor pointer */
const OP_ENTRY *opcodes; /* opcode table pointer */
} PARSER_ENTRY;
static const PARSER_ENTRY parser_table [] = { /* parser table array, searched linearly */
{ &mrg_desc, mrg_ops },
{ &srg1_desc, srg1_ops },
{ &srg_udesc, srg_uops },
{ &asg_udesc, asg_uops },
{ &iog_desc, iog_ops },
{ &eag_desc, eag_ops },
{ &eag_ef_desc, eag_ef_ops },
{ &iop_2100_desc, iop_2100_ops },
{ &fp_desc, fp_ops },
{ &fpp_desc, fpp_ops },
{ &ffp_2100_desc, ffp_2100_ops },
{ &ffp_m_desc, ffp_m_ops },
{ &ffp_e_desc, ffp_e_ops },
{ &ffp_f_desc, ffp_f_ops },
{ &ema_desc, ema_ops },
{ &vma_desc, vma_ops },
{ &dbi_desc, dbi_ops },
{ &sis_desc, sis_ops },
{ &os_desc, os_ops },
{ &trap_desc, trap_ops },
{ &iop1_1000_desc, iop1_1000_ops },
{ &iop2_1000_desc, iop2_1000_ops },
{ &vis_desc, vis_ops },
{ &sig_desc, sig_ops },
{ &dms_desc, dms_ops },
{ &eig_desc, eig_ops },
{ NULL, NULL }
};
/* System interface local SCP support routines */
static void one_time_init (void);
static t_bool fprint_stopped (FILE *st, t_stat reason);
static void fprint_addr (FILE *st, DEVICE *dptr, t_addr addr);
static t_addr parse_addr (DEVICE *dptr, CONST char *cptr, CONST char **tptr);
static t_stat hp_exdep_cmd (int32 arg, CONST char *buf);
static t_stat hp_run_cmd (int32 arg, CONST char *buf);
static t_stat hp_brk_cmd (int32 arg, CONST char *buf);
static t_stat hp_load_cmd (int32 arg, CONST char *buf);
/* System interface local utility routines */
static t_stat fprint_value (FILE *ofile, t_value val, uint32 radix, uint32 width, uint32 format);
static t_stat fprint_instruction (FILE *ofile, t_addr addr, t_value *val, uint32 radix,
const OP_DESC op_desc, const OP_TABLE ops);
static t_value parse_address (CONST char *cptr, t_stat *status);
static t_value parse_value (CONST char *cptr, uint32 radix, t_value max, t_stat *status);
static t_stat parse_cpu (CONST char *cptr, t_addr addr, t_value *val, uint32 radix, SYMBOL_SOURCE target);
static t_stat parse_instruction (CONST char *cptr, t_addr addr, t_value *val, uint32 radix, const OP_ENTRY *optr);
static t_stat parse_micro_ops (const OP_ENTRY *optr, char *gbuf, t_value *val,
CONST char **gptr, uint32 *accumulator);
static int fgetword (FILE *fileref);
static int fputword (int data, FILE *fileref);
/* System interface state */
static size_t device_size = 0; /* the maximum device name size */
static size_t flag_size = 0; /* the maximum trace flag name size */
static t_bool parse_physical = TRUE; /* the address parser configuration */
/* System interface global data structures */
#define E 0400u /* parity bit for even parity */
#define O 0000u /* parity bit for odd parity */
const HP_WORD odd_parity [256] = { /* odd parity table */
E, O, O, E, O, E, E, O, O, E, E, O, E, O, O, E, /* 000-017 */
O, E, E, O, E, O, O, E, E, O, O, E, O, E, E, O, /* 020-037 */
O, E, E, O, E, O, O, E, E, O, O, E, O, E, E, O, /* 040-067 */
E, O, O, E, O, E, E, O, O, E, E, O, E, O, O, E, /* 060-077 */
O, E, E, O, E, O, O, E, E, O, O, E, O, E, E, O, /* 100-117 */
E, O, O, E, O, E, E, O, O, E, E, O, E, O, O, E, /* 120-137 */
E, O, O, E, O, E, E, O, O, E, E, O, E, O, O, E, /* 140-157 */
O, E, E, O, E, O, O, E, E, O, O, E, O, E, E, O, /* 160-177 */
O, E, E, O, E, O, O, E, E, O, O, E, O, E, E, O, /* 200-217 */
E, O, O, E, O, E, E, O, O, E, E, O, E, O, O, E, /* 220-237 */
E, O, O, E, O, E, E, O, O, E, E, O, E, O, O, E, /* 240-267 */
O, E, E, O, E, O, O, E, E, O, O, E, O, E, E, O, /* 260-277 */
E, O, O, E, O, E, E, O, O, E, E, O, E, O, O, E, /* 300-317 */
O, E, E, O, E, O, O, E, E, O, O, E, O, E, E, O, /* 320-337 */
O, E, E, O, E, O, O, E, E, O, O, E, O, E, E, O, /* 340-357 */
E, O, O, E, O, E, E, O, O, E, E, O, E, O, O, E /* 360-377 */
};
/* System interface global SCP data definitions */
char sim_name [] = "HP 2100"; /* the simulator name */
int32 sim_emax = MAX_INSTR_LENGTH; /* the maximum number of words in any instruction */
void (*sim_vm_init) (void) = &one_time_init; /* a pointer to the one-time initializer */
DEVICE *sim_devices [] = { /* an array of pointers to the simulated devices */
&cpu_dev, /* CPU (must be first) */
&mp_dev, /* Memory Protect */
&dma1_dev, &dma2_dev, /* DMA/DCPC */
&ptr_dev, /* 2748 Paper Tape Reader */
&ptp_dev, /* 2895 Paper Tape Punch */
&tty_dev, /* 2752 Teleprinter */
&clk_dev, /* Time-Base Generator */
&lps_dev, /* 2767 Line Printer */
&lpt_dev, /* 2607 Line Printer */
&baci_dev, /* Buffered Asynchronous Communications Interface */
&mpx_dev, /* Eight-Channel Asynchronous Multiplexer */
&dpd_dev, &dpc_dev, /* 2870/7900 Disc Interface */
&dqd_dev, &dqc_dev, /* 2883 Disc Interface */
&drd_dev, &drc_dev, /* 277x Disc/Drum Interface */
&ds_dev, /* 7905/06/20/25 MAC Disc Interface */
&mtd_dev, &mtc_dev, /* 3030 Magnetic Tape Interface */
&msd_dev, &msc_dev, /* 7970B/E Magnetic Tape Interface */
&muxl_dev, &muxu_dev, &muxc_dev, /* Sixteen-Channel Asynchronous Multiplexer */
&ipli_dev, &iplo_dev, /* Processor Interconnect Kit */
&pif_dev, /* Privileged Interrupt Fence */
&da_dev, /* 7906H/20H/25H ICD Disc Interface */
&dc_dev, /* Dummy Disc Interface for diagnostics */
NULL
}; /* end of the device list */
#define DEVICE_COUNT (sizeof sim_devices / sizeof sim_devices [0] - 1) /* the count excludes the NULL pointer */
const char *sim_stop_messages [] = { /* an array of pointers to the stop messages in STOP_nnn order */
"Impossible error", /* 0 (never returned) */
"Unimplemented instruction", /* STOP_UNIMPL */
"Unassigned select code", /* STOP_UNSC */
"Undefined instruction", /* STOP_UNDEF */
"Indirect address loop", /* STOP_INDIR */
"Programmed halt", /* STOP_HALT */
"Breakpoint", /* STOP_BRKPNT */
"Cable not connected to", /* STOP_NOCONN */
"No tape loaded in", /* STOP_NOTAPE */
"End of tape on" /* STOP_EOT */
};
/* Local command table.
This table defines commands and command behaviors that are specific to this
simulator. Specifically:
* EXAMINE, DEPOSIT, IEXAMINE, and IDEPOSIT accept the page/offset physical
address form.
* RUN, GO, BREAK, and NOBREAK accept the logical address form and reject
the page/offset physical address form.
The table is initialized with only those fields that differ from the standard
command table. During one-time simulator initialization, the empty fields
are filled in from the corresponding standard command table entries. This
ensures that the auxiliary table automatically picks up any changes to the
standard commands that it modifies.
Implementation notes:
1. The RESET and BOOT commands are duplicated from the standard SCP command
table so that entering "R" doesn't invoke the RUN command and entering
"B" doesn't invoke the BREAK command. This would otherwise occur because
a VM-specific command table is searched before the standard command
table.
*/
static CTAB aux_cmds [] = {
/* Name Action Routine Argument Help String */
/* ---------- -------------- --------- ----------- */
{ "RESET", NULL, 0, NULL },
{ "BOOT", NULL, 0, NULL },
{ "EXAMINE", &hp_exdep_cmd, 0, NULL },
{ "IEXAMINE", &hp_exdep_cmd, 0, NULL },
{ "DEPOSIT", &hp_exdep_cmd, 0, NULL },
{ "IDEPOSIT", &hp_exdep_cmd, 0, NULL },
{ "RUN", &hp_run_cmd, 0, NULL },
{ "GO", &hp_run_cmd, 0, NULL },
{ "BREAK", &hp_brk_cmd, 0, NULL },
{ "NOBREAK", &hp_brk_cmd, 0, NULL },
{ "LOAD", &hp_load_cmd, 0, NULL },
{ NULL }
};
/* System interface global SCP support routines */
/* Load and dump memory images from and to files.
The LOAD and DUMP commands are intended to provide a basic method of loading
and dumping programs into and from memory. Typically, these commands operate
on a simple, low-level format, e.g., a memory image.
For this simulator, the LOAD command provides a way of installing bootstrap
loaders into the last 64 words of memory. The command format is:
LOAD <image-filename> { <select-code> }
...where <image-filename> is an absolute binary file containing the loader,
and <select-code> is an optional value from 10-77 octal used for configuring
the loader's I/O instructions. If the select code is omitted, the loader is
used as-is. When the command completes, the loader remains enabled so that
it may be executed. The loader should be protected if it will not be used
immediately.
The binary file must be targeted to addresses in the range x7700-x7777, where
"x" is irrelevant. The loaded program will be relocated to the last 64 words
of memory, so the desired memory size must be set before issuing the LOAD
command. If the configuration select code is supplied, all I/O instructions
in the program that reference select codes >= 10 octal will be changed by
adding the supplied value minus 10 to the instruction. The effect is that
instructions that reference select code 10 + n will be changed to reference
the supplied select code + n; this permits configuration of loaders that use
two-card interfaces.
The core-memory 21xx machines reserve the last 64 words in memory for a
access-protected resident boot loader. In hardware, the loaders could be
changed only by entering the replacement loader via the front panel switch
register. In simulation, the LOAD command serves this purpose.
The 1000-series machines used semiconductor memory, so the loaders were
implemented in socketed ROMs that were read into the last 64 words of memory
via the front-panel IBL (Initial Binary Loader) button. For these machines,
the LOAD command serves to install ROM images other than the ones included
with the device simulators. If the CPU is configured with more than 32K of
memory, the loader is installed in the last 64 words of the 32K logical
address space.
The DUMP command writes the bootstrap loader currently residing in memory to
an absolute binary file. The command format is:
DUMP <image-filename>
The loader must be enabled before entering the DUMP command; if the loader
is disabled, the output file will contain all zeros. When the command
completes, the loader remains enabled. The resulting file may be used in a
subsequent LOAD command to reload the bootstrap program.
Implementation notes:
1. Previous simulator versions did not restrict LOAD command addressing.
However, the LOAD command is not a convenient replacement for the ATTACH
PTR and BOOT PTR commands. The bootstrap loaders clear the A and B
registers at completion, and certain HP software depends on this behavior
for proper operation. The LOAD command alters nothing other than the
memory occupied by the program, and its use as a general absolute binary
paper tape loader would result in software failures.
2. The absolute binary format is described in Appendix H of the RTE-IV
Assembler Reference Manual.
3. The LOADed absolute binary file may contain a leader of any length,
including zero length (i.e., omitted). The logical end of file occurs
after reading ten null bytes or the physical EOF, whichever occurs first.
4. The DUMP command writes the 64-word loader in two records of 57 and 7
words, respectively, to conform with the format written by the HP
Assembler.
*/
t_stat sim_load (FILE *fptr, CONST char *cptr, CONST char *fnam, int flag)
{
const int reclen [2] = { TO_WORD (57, 0), /* the two DUMP record length words */
TO_WORD (7, 0) };
const int reccnt [2] = { 57, 7 }; /* the two DUMP record word counts */
int record, count, address, word, checksum;
t_stat result;
int32 trailer = 1; /* > 0 while reading leader, < 0 while reading trailer */
HP_WORD select_code = 0; /* select code to configure; 0 implies no configuration */
LOADER_ARRAY boot = { /* an array of two BOOT_LOADER structures */
{ 000, IBL_NA, IBL_NA, { 0 } }, /* HP 21xx Loader */
{ IBL_START, IBL_DMA, IBL_FWA, { 0 } } /* HP 1000 Loader */
};
if (flag == 0) { /* if this is a LOAD command */
if (*cptr != '\0') { /* then if a parameter follows */
select_code = /* then parse it as an octal number */
(HP_WORD) get_uint (cptr, 8, MAXDEV, &result);
if (result != SCPE_OK) /* if a parse error occurred */
return result; /* then report it */
else if (select_code < VARDEV) /* otherwise if the select code is invalid */
return SCPE_ARG; /* then report a bad argument */
}
while (TRUE) { /* read absolute binary records from the file */
do { /* skip any blank leader or trailer present */
count = fgetc (fptr); /* get the next byte from the tape */
if (count == EOF) /* if an EOF occurred */
if (trailer > 0) /* then if we are reading the leader */
return SCPE_FMT; /* then the tape format is bad */
else /* otherwise we are reading the trailer */
trailer = 0; /* and now we are done */
else if (count == 0) /* otherwise if this is a null value */
trailer = trailer + 1; /* then increment the trailer count */
}
while (count == 0 && trailer != 0); /* continue if a null was read or trailer is exhausted */
if (trailer == 0) /* if the physical EOF was seen */
break; /* then the binary read is complete */
else if (fgetc (fptr) == EOF) /* otherwise discard the unused byte after the record count */
return SCPE_FMT; /* if it is not there, then the tape format is bad */
address = fgetword (fptr); /* get the record load address word */
if (address == EOF) /* if the load address is not present */
return SCPE_FMT; /* then the tape format is bad */
else /* otherwise */
checksum = address; /* start the record checksum with the load address */
if ((address & 0007777u) < 0007700u) /* if the address does not fall at the end of a 4K block */
return SCPE_NXM; /* then report the address error */
else /* otherwise mask the address */
address = address & IBL_MASK; /* to form an index into the loader array */
while (count-- > 0) { /* read the data record */
word = fgetword (fptr); /* get the next data word from the file */
if (word == EOF) /* if the word is not present */
return SCPE_FMT; /* then the tape format is bad */
else { /* otherwise */
boot [0].loader [address] = (MEMORY_WORD) word; /* save the data word in */
boot [1].loader [address++] = (MEMORY_WORD) word; /* both loader arrays */
checksum = checksum + word; /* and include it in the record checksum */
}
}
word = fgetword (fptr); /* read the record checksum word */
if (word == EOF) /* if it is not present */
return SCPE_FMT; /* then the tape format is bad */
else if (word != (int) (checksum & D16_MASK)) /* otherwise if the checksums do not match */
return SCPE_CSUM; /* then report a checksum error */
else /* otherwise the record is good */
trailer = -10; /* so prepare for a potential trailer */
} /* and loop until all records are read */
cpu_copy_loader (boot, select_code, /* install the loader */
IBL_S_NOCLEAR, IBL_S_NOSET); /* and configure the select code if requested */
}
else { /* otherwise this is a DUMP command */
address = MEMSIZE - 1 & ~IBL_MASK & LA_MASK; /* the loader occupies the last 64 words in memory */
for (record = 0; record < 2; record++) { /* write two absolute records */
if (fputword (reclen [record], fptr) == EOF) /* starting with the record length; if it fails */
return SCPE_IOERR; /* then report an I/O error */
if (fputword (address, fptr) == EOF) /* write the starting address; if it fails */
return SCPE_IOERR; /* then report an I/O error */
checksum = address; /* start the record checksum with the load address */
for (count = 0; count < reccnt [record]; count++) { /* write a data record */
word = mem_examine (address++); /* get the next data word from memory */
if (fputword (word, fptr) == EOF) /* write the data word; if it fails */
return SCPE_IOERR; /* then report an I/O error */
else /* otherwise */
checksum = checksum + word; /* include it in the record checksum */
} /* and loop until all words are written */
if (fputword (checksum & D16_MASK, fptr) == EOF) /* write the checksum word; if it fails */
return SCPE_IOERR; /* then report an I/O error */
} /* loop until both records are written */
}
return SCPE_OK;
}
/* Print a value in symbolic format.
This routine prints a data value in the format specified by the optional
switches on the output stream provided. On entry, "ofile" is the opened
output stream, and the other parameters depend on the reason the routine was
called, as follows:
* To print the next instruction mnemonic when the simulator stops:
- addr = the program counter
- val = a pointer to sim_eval [0]
- uptr = NULL
- sw = "-M" | SIM_SW_STOP
* To print the result of EXAMining a register with REG_VMIO or a user flag:
- addr = the ORed register radix and user flags
- val = a pointer to a single t_value
- uptr = NULL
- sw = the command line switches | SIM_SW_REG
* To print the result of EXAMining a memory address:
- addr = the memory address
- val = a pointer to sim_eval [0]
- uptr = a pointer to the named unit
- sw = the command line switches
* To print the result of EVALuating a symbol:
- addr = the symbol index
- val = a pointer to sim_eval [addr]
- uptr = a pointer to the default unit (cpu_unit)
- sw = the command line switches
On exit, a status code is returned to the caller. If the format requested is
not supported, SCPE_ARG status is returned, which causes the caller to print
the value in numeric format with the default radix. Otherwise, SCPE_OK
status is returned if a single-word value was consumed, or the negative
number of extra words (beyond the first) consumed in printing the symbol is
returned. For example, printing a two-word symbol would return
SCPE_OK_2_WORDS (= -1).
The following symbolic modes are supported by including the indicated
switches:
Switch Interpretation
------ -----------------------------------------
-A a single character in the right-hand byte
-C a two-character packed string
-M a CPU instruction mnemonic
In the absence of a mode switch, the value is displayed in a numeric format.
When displaying in the instruction mnemonic form, an additional format switch
may be specified to indicate the desired operand radix, as follows:
Switch Interpretation
------ -----------------------------------------
-B a binary value
-O an octal value
-D a decimal value
-H a hexadecimal value
These switches (except for -B) may be used without a mode switch to display a
numeric value in the specified radix. To summarize, the valid switch
combinations are:
-C
-M [ -A | -B | -O | -D | -H ]
-A | -O | -D | -H
When displaying mnemonics, operand values by default are displayed in a radix
suitable to the type of the value. Address values are displayed in the CPU's
address radix, which is octal, and data values are displayed in the CPU's
data radix, which defaults to octal but may be set to a different radix or
overridden by a switch on the command line.
Implementation notes:
1. If we are being called as a result of a VM stop to display the next
instruction to be executed, a check is made to see if an interrupt is
pending and not deferred. If so, then the interrupt source and the trap
cell instruction are displayed as the next instruction to be executed,
rather than the instruction at the current PC.
2. The trap cell instruction is read by calling "mem_fast_read" directly
into the "val" array (which points to the "sim_eval" array), rather than
by setting the VAL_EMPTY flag and letting the "fprint_instruction"
routine load any required operands, because the instruction must be read
from the system map instead of the current map (which may be the user map
until the interrupt is actually processed).
3. When we are called to format a register, the "val" parameter points at a
single t_value containing the register value. However, the instruction
mnemonic formatter expects to receive a pointer to an array that holds
at least the number of words for the instruction being decoded. For
example, if the register holds the MPY opcode, the formatter will expect
the operand address in the second array word. Multi-word instructions
cannot be displayed correctly if they originate in a register, so the
best we can do is copy the value to "sim_eval [0]" and zero the remaining
words before calling the mnemonic formatter. A call to "memset" is used
because this can be optimized to a single REP STOSD instruction.
4. The "fprint_cpu" routine needs to know whether the "addr" parameter value
represents a CPU memory address in order to interpret MRG instructions
correctly. It does for simulator stops and CPU memory examinations but
not for register or device memory examinations and symbol evaluations.
The symbol source is set to "CPU_Symbol" or "Device_Symbol",
respectively, to reflect these conditions.
5. We return SCPE_INVSW when multiple modes or formats are specified, but
the callers do not act on this; they use the fallback formatter if any
status error is returned. We could work around this by printing "Invalid
switch" to the console and returning SCPE_OK, but this does not stop
IEXAMINE from prompting for the replacement value(s) or EXAMINE from
printing a range.
6. Radix switches and the -C switch are conceptually mutually exclusive.
However, if we return an error when "format" is non-zero, then -C will be
ignored, and the fallback formatter will use the radix switch. The other
choice is to process -C and ignore the radix switch; this is the option
implemented.
*/
t_stat fprint_sym (FILE *ofile, t_addr addr, t_value *val, UNIT *uptr, int32 sw)
{
int32 formats, modes, i;
uint32 irq, radix;
SYMBOL_SOURCE source;
if ((sw & (SIM_SW_REG | ALL_SWITCHES)) == SIM_SW_REG) /* if we are formatting a register without overrides */
if (addr & REG_A) /* then if the default format is character */
sw |= A_SWITCH; /* then set the -A switch */
else if (addr & REG_C) /* otherwise if the default mode is string */
sw |= C_SWITCH; /* then set the -C switch */
else if (addr & REG_M) /* otherwise if the default mode is mnemonic */
sw |= M_SWITCH; /* then set the -M switch */
if ((sw & SYMBOLIC_SWITCHES) == 0) /* if there are no symbolic overrides */
return SCPE_ARG; /* then return an error to use the standard formatter */
formats = sw & FORMAT_SWITCHES; /* separate the format switches */
modes = sw & MODE_SWITCHES; /* from the mode switches */
if (formats == A_SWITCH) /* if the -A switch is specified */
radix = 256; /* then override the radix to character */
else if (formats == B_SWITCH) /* otherwise if the -B switch is specified */
radix = 2; /* then override the radix to binary */
else if (formats == D_SWITCH) /* otherwise if the -D switch is specified */
radix = 10; /* then override the radix to decimal */
else if (formats == H_SWITCH) /* otherwise if the -H switch is specified */
radix = 16; /* then override the radix to hexadecimal */
else if (formats == O_SWITCH) /* otherwise if the -O switch is specified */
radix = 8; /* then override the radix to octal */
else if (formats == 0) /* otherwise if no format switch is specified */
radix = 0; /* then indicate that the default radix is to be used */
else /* otherwise more than one format is specified */
return SCPE_INVSW; /* so return an error */
if (modes == M_SWITCH) { /* if mnemonic mode is specified */
if (sw & SIM_SW_STOP) { /* then if this is a simulator stop */
source = CPU_Symbol; /* then report as a CPU symbol */
irq = calc_int (); /* check for a pending interrupt */
if (irq && !ion_defer) { /* if a pending interrupt is present and not deferred */
addr = irq; /* then set the display address to the trap cell */
for (i = 0; i < sim_emax; i++) /* load the trap cell instruction */
val [i] = mem_fast_read ((HP_WORD) (irq + i), SMAP); /* which might be multi-word (e.g., JLY) */
fprintf (ofile, "IAK %2o: ", irq); /* report that the interrupt will be acknowledged */
}
}
else if (sw & SIM_SW_REG) { /* otherwise if a register value is being formatted */
source = Device_Symbol; /* then report it as a device symbol */
memset (sim_eval, 0, /* clear the sim_eval array */
MAX_INSTR_LENGTH * sizeof (t_value)); /* in case the instruction is multi-word */
sim_eval [0] = *val; /* copy the register value */
val = sim_eval; /* and point at the sim_eval array */
}
else if (uptr == &cpu_unit) /* otherwise if access is to CPU memory */
source = CPU_Symbol; /* then report as a CPU symbol */
else /* otherwise access is to device memory */
source = Device_Symbol; /* so report it as a device symbol */
return fprint_cpu (ofile, addr, val, radix, source); /* format and print the value in mnemonic format */
}
else if (modes == C_SWITCH) { /* otherwise if ASCII string mode is specified */
fputs (fmt_char (UPPER_BYTE (val [0])), ofile); /* then format and print the upper byte */
fputc (',', ofile); /* followed by a separator */
fputs (fmt_char (LOWER_BYTE (val [0])), ofile); /* followed by the lower byte */
return SCPE_OK;
}
else if (modes == 0) /* otherwise if no mode was specified */
return fprint_value (ofile, val [0], radix, /* then format and print it with the specified radix */
DV_WIDTH, PV_RZRO); /* and data width */
else /* otherwise the modes conflict */
return SCPE_INVSW; /* so return an error */
}
/* Parse a value in symbolic format.
Print the data value in the format specified by the optional switches on the
output stream supplied. This routine is called to print:
Parse the input string in the format specified by the optional switches, and
return the resulting value(s). This routine is called to parse an input
string when:
- DEPOSITing into a register marked with REG_VMIO or a user flag
- DEPOSITing into a memory address
- EVALuating a symbol
On entry, "cptr" points at the string to parse, "addr" is the register radix
and flags, memory address, or 0 (respectively), "uptr" is NULL, a pointer to
the named unit, or a pointer to the default unit (respectively), "val" is a
pointer to an array of t_values of depth "sim_emax" representing the value(s)
returned, and "sw" contains any switches passed on the command line. "sw"
also includes SIM_SW_REG for a register call.
On exit, a status code is returned to the caller. If the format requested is
not supported or the parse failed, SCPE_ARG status is returned, which causes
the caller to attempt to parse the value in numeric format. Otherwise,
SCPE_OK status is returned if the parse produced a single-word value, or the
negative number of extra words (beyond the first) produced by parsing the
symbol is returned. For example, parsing a symbol that resulted in two words
being stored (in val [0] and val [1]) would return SCPE_OK_2_WORDS (= -1).
The following symbolic modes are supported by including the indicated
switches:
Switch Interpretation
------ -----------------------------
-C a two-character packed string
-M a CPU instruction mnemonic
When parsing in the instruction mnemonic form, an additional format switch
may be specified to indicate the supplied operand radix, as follows:
Switch Interpretation
------ -----------------------------------------
-A a single character in the right-hand byte
-B a binary value
-O an octal value
-D a decimal value
-H a hexadecimal value
These switches (except for -B) may be used without a mode switch to parse a
numeric value in the specified radix. To summarize, the valid switch
combinations are:
-C
-M [ -A | -B | -O | -D | -H ]
-A | -O | -D | -H
When entering machine instruction mnemonics, operand values are parsed in a
radix suitable to the type of the value. Address values are parsed in the
CPU's address radix, which is octal, and data values are parsed in the CPU's
data radix, which defaults to octal but may be set to a different radix or
overridden by a switch on the command line.
In the absence of switches, a leading ' implies "-A", a leading " implies
"-C", and a leading alphabetic or punctuation character implies "-M". If a
single character is supplied with "-C", the low byte of the resulting value
will be zero; follow the character with a space if the low byte is to be
padded with a space.
The operand format for -A is a single (displayable) character. The operand
format for -C is two (displayable) characters.
Implementation notes:
1. The "cptr" post-increments are logically ANDed with the tests for ' and "
so that the increments are performed only if the tests succeed. The
intent is to skip over the leading ' or " character. The increments
themselves always succeed, so they don't affect the outcome of the tests.
2. A hex value that is also a machine instruction mnemonic (e.g., CCE) is
parsed as the latter unless the -H switch is specified. If both -M and
-H are specified, the mnemonic is parsed as a machine instruction, and
any operand is parsed as a hex value.
3. We return SCPE_INVSW when multiple modes or formats are specified, but
the callers do not act on this; they use the fallback parser if any
status error is returned. We could work around this by printing "Invalid
switch" to the console and returning SCPE_OK, but this does not stop
IDEPOSIT from prompting for the replacement value(s) or DEPOSIT from
printing the error for each address in a range.
4. Radix switches and the -C switch are conceptually mutually exclusive.
However, if we return an error when "format" is non-zero, then -C will be
ignored, and the fallback formatter will use the radix switch. The other
option is to process -C and ignore the radix switch; this is the option
implemented.
*/
t_stat parse_sym (CONST char *cptr, t_addr addr, UNIT *uptr, t_value *val, int32 sw)
{
int32 formats, modes;
uint32 radix;
t_stat status;
SYMBOL_SOURCE target;
if ((sw & (SIM_SW_REG | ALL_SWITCHES)) == SIM_SW_REG) /* if we are parsing a register without overrides */
if (addr & REG_A) /* then if the default format is character */
sw |= A_SWITCH; /* then set the -A switch */
else if (addr & REG_C) /* otherwise if the default mode is string */
sw |= C_SWITCH; /* then set the -C switch */
else if (addr & REG_M) /* otherwise if the default mode is mnemonic */
sw |= M_SWITCH; /* then set the -M switch */
if ((sw & ALL_SWITCHES) == 0) /* if there are no default or explicit overrides */
if (*cptr == '\'' && cptr++) /* then if a character parse is implied */
sw |= A_SWITCH; /* then set the -A switch */
else if (*cptr == '"' && cptr++) /* otherwise if a character string parse is implied */
sw |= C_SWITCH; /* then set the -C switch */
else if (isalpha (*cptr) || ispunct (*cptr)) /* otherwise if an instruction mnemonic parse is implied */
sw |= M_SWITCH; /* then set the -M switch */
if ((sw & SYMBOLIC_SWITCHES) == 0) /* if there are no symbolic overrides */
return SCPE_ARG; /* then return an error to use the standard parser */
formats = sw & FORMAT_SWITCHES; /* separate the format switches */
modes = sw & MODE_SWITCHES; /* from the mode switches */
if (formats == A_SWITCH) /* if the -A switch is specified */
radix = 256; /* then override the radix to character */
else if (formats == B_SWITCH) /* otherwise if the -B switch is specified */
radix = 2; /* then override the radix to binary */
else if (formats == D_SWITCH) /* otherwise if the -D switch is specified */
radix = 10; /* then override the radix to decimal */
else if (formats == H_SWITCH) /* otherwise if the -H switch is specified */
radix = 16; /* then override the radix to hexadecimal */
else if (formats == O_SWITCH) /* otherwise if the -O switch is specified */
radix = 8; /* then override the radix to octal */
else if (formats == 0) /* otherwise if no format switch is specified */
radix = 0; /* then indicate that the default radix is to be used */
else /* otherwise more than one format is specified */
return SCPE_INVSW; /* so return an error */
if (modes == M_SWITCH) { /* if instruction mnemonic mode is specified */
if (uptr == NULL || uptr == &cpu_unit) /* then if access is to a register or CPU memory */
target = CPU_Symbol; /* then report as a CPU symbol */
else /* otherwise access is to device memory */
target = Device_Symbol; /* so report it as a device symbol */
return parse_cpu (cptr, addr, val, radix, target); /* attempt a mnemonic instruction parse */
}
else if (modes == C_SWITCH) /* otherwise if string mode is specified */
if (cptr [0] != '\0') { /* then if characters are present */
val [0] = (t_value) TO_WORD (cptr [0], cptr [1]); /* then convert the character values */
return SCPE_OK; /* and indicate success */
}
else /* otherwise */
return SCPE_ARG; /* report that the line cannot be parsed */
else if (modes == 0) { /* otherwise if no mode was specified */
val [0] = parse_value (cptr, radix, DV_UMAX, &status); /* then parse using the specified radix */
return status; /* and return the parsing status */
}
else /* otherwise the modes conflict */
return SCPE_INVSW; /* so return an error */
}
/* Attach a file for appending.
This routine is called to attach a file to a specified unit and set the file
position to the end for appending. The routine returns the result of the
operation, which will be SCPE_OK if the attach and optional EOF seek succeed,
SCPE_IOERR if the attach succeeds but the EOF seek fails (in this case, the
file will be detached before returning), or an appropriate status code if the
attach fails.
The standard "attach_unit" routine handles the "-N" switch to use an empty
("new") file. If the file exists, "attach_unit" returns with the file
positioned at the start. For write-only devices, such as printers, this is
almost never desirable, as it means that existing content may be only
partially overwritten. This routine provides the proper semantics for these
devices, i.e., if the file exists, position it for writing to the end of the
file.
Implementation notes:
1. "attach_unit" opens the file using one of the following modes:
- "r" (reading) if the -R (read-only) switch or UNIT_RO is present or
the file is marked read-only by the host file system
- "w+" (truncate and update) if the -N (new) switch is present or the
file does not exist
- "r+" (update) otherwise
2. Reopening with mode "a" or "a+" doesn't have the desired semantics.
These modes permit writing only at the EOF, but we want to permit
overwriting if the user explicitly sets POS. The resulting "fseek" on
resumption would be ignored if the mode is "a" or "a+". Therefore, we
accept mode "r+" and explicitly "fseek" to the end of file here.
3. If we are called during a RESTORE command to reattach a file previously
attached when the simulation was SAVEd, the file position is not altered.
4. It is not necessary to report the device or unit if the 'fseek" fails, as
this routine is called only is response to a user command that specifies
the unit to attach.
*/
t_stat hp_attach (UNIT *uptr, CONST char *cptr)
{
t_stat result;
result = attach_unit (uptr, cptr); /* attach the specified image file */
if (result == SCPE_OK /* if the attach was successful */
&& (sim_switches & SIM_SW_REST) == 0) /* and we are not being called during a RESTORE command */
if (fseek (uptr->fileref, 0, SEEK_END) == 0) /* then append by seeking to the end of the file */
uptr->pos = (t_addr) ftell (uptr->fileref); /* and repositioning if the seek succeeded */
else { /* otherwise the seek failed */
cprintf ("%s simulator seek error: %s\n", /* so report the error to the console */
sim_name, strerror (errno));
detach_unit (uptr); /* detach the unit */
result = SCPE_IOERR; /* and report that the seek failed */
}
return result;
}
/* Set a device select code.
This validation routine is called to set a device's select code. The "uptr"
parameter points to the unit being configured, "count" gives the number of
select codes to configure, "cptr" points to the first character of the value
to be set, and "desc" points to the DIB associated with the device.
If the supplied value is acceptable, it is stored in the DIB, and the routine
returns SCPE_OK. Otherwise, an error code is returned.
Some devices (e.g., the DP disc device) use multiple interface cards. These
are assigned sequential select codes. For these devices, "desc" must point
at the first element of an array of DIBs to be assigned, and "count" must be
set to the number of elements. For single-card devices, "desc" points at the
DIB, and "count" is 1.
Implementation notes:
1. The legacy modifier "DEVNO" is supported in addition to the preferred
"SC" modifier, but the corresponding MTAB entry is tagged with MTAB_NMO
to prevent its display. To differentiate between the two MTAB entries,
which is necessary for display, the "DEVNO" entry's "count" parameter is
complemented (the corresponding MTAB "match" field that supplies the
"count" parameter to this routine is unsigned).
*/
t_stat hp_set_dib (UNIT *uptr, int32 count, CONST char *cptr, void *desc)
{
DIB *dibptr = (DIB *) desc; /* a pointer to the associated DIB array */
t_stat status = SCPE_OK;
uint32 value;
int32 index;
if (cptr == NULL || *cptr == '\0') /* if the expected value is missing */
status = SCPE_MISVAL; /* then report the error */
else { /* otherwise a value is present */
if (count < 0) /* if the count has been complemented */
count = ~count; /* then restore it to a positive value */
value = (uint32) get_uint (cptr, SC_BASE, /* parse the supplied device number */
SC_MAX + 1 - count, &status);
if (status == SCPE_OK) { /* if it is valid */
if (value < VARDEV) /* then if it is an internal select code */
return SCPE_ARG; /* then reject it */
for (index = 0; index < count; index++, dibptr++) /* loop through the associated interfaces */
dibptr->select_code = value + index; /* and set the select codes in order */
}
}
return status; /* return the validation result */
}
/* Show a device select code.
This display routine is called to show a device's select code. The "st"
parameter is the open output stream, "uptr" points to the unit being queried,
"count" gives the number of additional select codes to display, and "desc"
points to the DIB associated with the device.
Some devices (e.g., the DP disc device) use multiple interface cards. For
these devices, "desc" must point at the first element of an array of DIBs to
be assigned, and "count" must be set to the index of the last element (note:
not the size) of the array. The select codes will be printed together.
For single-card devices, "desc" points at the DIB, and "count" is zero.
Implementation notes:
1. The legacy modifier "DEVNO" is supported in addition to the preferred
"SC" modifier, but the corresponding MTAB entry is tagged with MTAB_NMO
to prevent its display. To differentiate between the two MTAB entries,
which is necessary for display, the "DEVNO" entry's "count" parameter is
complemented (the corresponding MTAB "match" field that supplies the
"count" parameter to this routine is unsigned).
1. The legacy modifier "DEVNO" is supported in addition to the preferred
"SC" modifier, but the corresponding MTAB entry is tagged with MTAB_NMO
to prevent its display. When displaying an MTAB_NMO entry, a newline is
not automatically added to the end of the line, so we must add it here.
To differentiate between the two MTAB entries, the "DEVNO" entry's
"count" parameter is complemented (the corresponding MTAB "match" field
that supplies the "count" parameter to this routine is unsigned).
*/
t_stat hp_show_dib (FILE *st, UNIT *uptr, int32 count, CONST void *desc)
{
const DIB *dibptr = (const DIB *) desc; /* a pointer to the associated DIB array */
int32 index, limit;
if (count < 0) /* if the count has been complemented */
limit = ~count; /* then restore it to a positive value */
else /* otherwise */
limit = count; /* the value is already positive */
fprintf (st, "select code=%o", dibptr++->select_code); /* print the interface's select code */
for (index = 2; index <= limit; index++, dibptr++) /* if the device uses more than one interface */
fprintf (st, "/%o", dibptr->select_code); /* then append the additional select code(s) */
if (count < 0) /* if this is a DEVNO request (MTAB_NMO) */
fputc ('\n', st); /* then append a newline */
return SCPE_OK; /* return the display result */
}
/* System interface global utility routines */
/* Print a CPU instruction in symbolic format.
This routine is called to format and print an instruction in mnemonic form.
The "ofile" parameter is the opened output stream, "val" is a pointer to an
array of t_values of depth "sim_emax" containing the word(s) comprising the
machine instruction to print, "radix" contains the desired operand radix or
zero if the default radix is to be used, and "source" indicates the source of
the instruction (device, CPU, or trace).
The routine returns a status code to the caller. SCPE_OK status is returned
if a single-word instruction was printed, or the negative number of extra
words (beyond the first) consumed in printing the instruction is returned.
For example, printing a two-word instruction returns SCPE_OK_2_WORDS (= -1).
If the supplied instruction is not a valid opcode, or is not valid for the
current CPU feature set, SCPE_ARG is returned.
The routine determines the instruction group that includes the supplied
instruction value and calls the "fprint_instruction" routine with the
associated opcode descriptor and table. For MRG instructions, the
instruction address is set to an invalid value if the source is not a CPU
memory location. This causes the instruction to be printed as a base-page or
current-page offset, rather than as an absolute address.
SRG instructions consist of two encoded micro-op fields, plus separate
micro-ops for CLE and SLA/SLB. These are printed separately.
MAC instructions must be decoded into the indicated feature groups. Some of
these groups overlap and must be differentiated by the current CPU model or
installed microcode options before the correct opcode descriptor and table
may be determined. Primary MAC decoding is bits 11-8, as follows (note that
bit 10 = 0 for the MAC group):
Bits
11-8 Group
---- -----
0000 EAU
0001 EAU
0010 EAU
0011 UIG-1
1000 EAU
1001 EAU
1010 UIG-0
1011 UIG-1
Bits 7-4 further decode the UIG instruction feature group. UIG-0 pertains
to the 2100 and 1000 M/E/F-Series, as follows:
Instructions IR 7-4 Option Name 2100 1000-M 1000-E 1000-F
------------- ------ -------------------------- ------ ------ ------ ------
105000-105362 00-17 2000 I/O Processor opt - - -
105000-105137 00-05 Floating Point opt std std std
105200-105237 10-11 Fast FORTRAN Processor opt opt opt std
105240-105257 12 RTE-IVA/B Extended Memory - - opt opt
105240-105257 12 RTE-6/VM Virtual Memory - - opt opt
105300-105317 14 Distributed System - - opt opt
105320-105337 15 Double Integer - - opt -
105320-105337 15 Scientific Instruction Set - - - std
105340-105357 16 RTE-6/VM Operating System - - opt opt
UIG-1 pertains only to the 1000 M/E/F-Series machines, as follows:
Instructions IR 7-4 Option Name 2100 1000-M 1000-E 1000-F
------------- ------ -------------------------- ------ ------ ------ ------
10x400-10x437 00-01 2000 I/O Processor - opt opt -
10x460-10x477 03 2000 I/O Processor - opt opt -
10x460-10x477 03 Vector Instruction Set - - - opt
10x520-10x537 05 Distributed System - opt - -
10x600-10x617 10 SIGNAL/1000 Instruction Set - - - opt
10x700-10x737 14-15 Dynamic Mapping System - opt opt std
10x740-10x777 16-17 Extended Instruction Group - std std std
Implementation notes:
1. Indexing is employed where possible, but the 21xx/1000 instruction set is
quite irregular, so conditionals and linear searches are needed to
resolve many of the instructions.
2. The no-operation opcode is handled as a special case within the SRG
micro-ops table.
*/
t_stat fprint_cpu (FILE *ofile, t_addr addr, t_value *val, uint32 radix, SYMBOL_SOURCE source)
{
const t_value opcode = val [0]; /* the instruction opcode */
t_bool separator = FALSE; /* TRUE if a separator between multiple ops is needed */
t_stat status = SCPE_ARG; /* initial return status is "invalid opcode" */
if (MRGOP (opcode)) { /* if this is an MRG instruction */
if (source == Device_Symbol) /* then if the offset must be relative */
addr = PA_MAX + 1; /* then pass an invalid address */
status = fprint_instruction (ofile, addr, val, /* print the instruction */
radix, mrg_desc, mrg_ops);
}
else if (SRGOP (opcode)) { /* otherwise if this is an SRG instruction */
if (opcode & SRG1_DE_MASK) { /* then if the first shift is enabled */
status = fprint_instruction (ofile, addr, val, /* then print the first shift operation */
radix, srg1_desc, srg1_ops);
separator = TRUE; /* we will need a separator */
}
if (opcode == SRG_NOP /* if this is a NOP */
|| (opcode & (SRG_CLE | SRG_SLx))) { /* or a micro-op is present */
if (separator) /* then if a separator is needed */
fputc (',', ofile); /* then print it */
status = fprint_instruction (ofile, addr, val, /* print the micro-op(s) */
radix, srg_udesc, srg_uops);
separator = TRUE; /* we will need a separator */
}
if (opcode & SRG2_DE_MASK) { /* if the second shift is enabled */
if (separator) /* then if a separator is needed */
fputc (',', ofile); /* then print it */
status = fprint_instruction (ofile, addr, val, /* print the second shift operation */
radix, srg2_desc, srg2_ops);
}
}
else if (ASGOP (opcode)) /* otherwise if this is an ASG instruction */
status = fprint_instruction (ofile, addr, val, /* then print it */
radix, asg_udesc, asg_uops);
else if (IOGOP (opcode)) /* otherwise if this is an IOG instruction */
status = fprint_instruction (ofile, addr, val, /* then print it */
radix, iog_desc, iog_ops);
else { /* otherwise this is a MAC group instruction */
if (source == CPU_Trace) /* if this is a CPU trace call */
addr = addr | VAL_EMPTY; /* then indicate that the value array has not been loaded */
if (UIG_0_OP (opcode)) { /* if this is a UIG-0 instruction */
status = fprint_instruction (ofile, addr, val, /* then try to print it as a 2100 IOP instruction */
radix, iop_2100_desc, iop_2100_ops);
if (status == SCPE_UNK) /* if it's not a 2100 IOP instruction */
switch (UIG (opcode)) { /* then dispatch on the feature group */
case 000: /* 105000-105017 */
case 001: /* 105020-105037 */
case 002: /* 105040-105057 */
case 003: /* 105060-105077 */
case 004: /* 105100-105117 */
case 005: /* 105120-105137 */
status = fprint_instruction (ofile, addr, val, /* try to print as an FP instruction */
radix, fp_desc, fp_ops);
if (status == SCPE_UNK) /* if it's not an FP instruction */
status = fprint_instruction (ofile, addr, val, /* then try again as an FPP instruction */
radix, fpp_desc, fpp_ops);
break;
case 010: /* 105200-105217 */
case 011: /* 105220-105237 */
status = fprint_instruction (ofile, addr, val, /* try to print as a 2100 FFP instruction */
radix, ffp_2100_desc, ffp_2100_ops);
if (status == SCPE_UNK) /* if it's not a 2100 FFP opcode */
status = fprint_instruction (ofile, addr, val, /* then try again as a 1000-F FFP opcode */
radix, ffp_f_desc, ffp_f_ops);
if (status == SCPE_UNK) /* if it's not a 1000-F FFP opcode */
status = fprint_instruction (ofile, addr, val, /* then try again as a 1000-E FFP opcode */
radix, ffp_e_desc, ffp_e_ops);
if (status == SCPE_UNK) /* if it's not a 1000-E FFP opcode */
status = fprint_instruction (ofile, addr, val, /* then try again as a 1000-M FFP opcode */
radix, ffp_m_desc, ffp_m_ops);
break;
case 012: /* 105240-105257 */
status = fprint_instruction (ofile, addr, val, /* try to print as a VMA instruction */
radix, vma_desc, vma_ops);
if (status == SCPE_UNK) /* if it's not a VMA instruction */
status = fprint_instruction (ofile, addr, val, /* then try again as an EMA instruction */
radix, ema_desc, ema_ops);
break;
case 014: /* 105300-105317 Distributed System */
break;
case 015: /* 105320-105337 */
status = fprint_instruction (ofile, addr, val, /* try to print as an SIS instruction */
radix, sis_desc, sis_ops);
if (status == SCPE_UNK) /* if it's not an SIS instruction */
status = fprint_instruction (ofile, addr, val, /* then try again as a DBI instruction */
radix, dbi_desc, dbi_ops);
break;
case 016: /* 105340-105357 */
if (opcode >= RTE_IRQ_RANGE /* if the opcode is in the interrupt use range */
&& addr >= OPTDEV && addr <= MAXDEV) /* and it's located in a trap cell */
status = fprint_instruction (ofile, addr, val, /* then print as an IRQ instruction */
radix, trap_desc, trap_ops);
else /* otherwise */
status = fprint_instruction (ofile, addr, val, /* print as an OS-assist instruction */
radix, os_desc, os_ops);
break;
default: /* all other feature groups */
status = SCPE_ARG; /* are unimplemented */
}
}
else if (UIG_1_OP (opcode)) /* otherwise if this is a UIG-1 instruction */
switch (UIG (opcode)) { /* then dispatch on the feature group */
case 000: /* 10x400-10x417 */
case 001: /* 10x420-10x437 */
status = fprint_instruction (ofile, addr, val, /* print as an IOP instruction */
radix, iop1_1000_desc, iop1_1000_ops);
break;
case 003: /* 10x460-10x477 */
status = fprint_instruction (ofile, addr, val, /* try to print as an IOP instruction */
radix, iop2_1000_desc, iop2_1000_ops);
if (status == SCPE_UNK) { /* if it's not an IOP opcode */
if (source == CPU_Trace) /* then if this is a CPU trace call */
val [1] = mem_fast_read (addr + 1 & LA_MASK, dms_ump); /* then load the following word */
status = fprint_instruction (ofile, addr, val, /* print as a VIS instruction */
radix, vis_desc, vis_ops);
}
break;
case 010: /* 10x600-10x617 */
status = fprint_instruction (ofile, addr, val, /* print as a SIGNAL instruction */
radix, sig_desc, sig_ops);
break;
case 014:
case 015: /* 10x700-10x737 */
status = fprint_instruction (ofile, addr, val, /* print as a DMS instruction */
radix, dms_desc, dms_ops);
break;
case 016:
case 017: /* 10x740-10x777 */
status = fprint_instruction (ofile, addr, val, /* print as an EIG instruction */
radix, eig_desc, eig_ops);
break;
default: /* all other feature groups */
status = SCPE_ARG; /* are unimplemented */
}
else { /* otherwise */
status = fprint_instruction (ofile, addr, val, /* print as an EAG instruction */
radix, eag_desc, eag_ops);
if (status == SCPE_ARG) /* if it's not an EAG opcode but the EAU is present */
status = fprint_instruction (ofile, addr, val, /* then try as a 1000-E/F extension instruction */
radix, eag_ef_desc, eag_ef_ops);
}
}
return status; /* return the consumption status */
}
/* Format a character for printing.
This routine formats a single 8-bit character value into a printable string
and returns a pointer to that string. Printable characters retain their
original form but are enclosed in single quotes. Control characters are
translated to readable strings. Characters outside of the ASCII range but
within the full character range (i.e., less than 377 octal) are presented as
escaped octal values. Characters outside of the full character range are
masked to the lower eight bits and printed as a plain octal value.
Implementation notes:
1. The longest string to be returned is a five-character escaped string
consisting of a backslash, three octal digits, and a trailing NUL. The
end-of-buffer pointer has an allowance to ensure that the string will
fit.
2. The routine returns a pointer to a static buffer containing the printable
string. To allow the routine to be called more than once per trace line,
the null-terminated format strings are concatenated in the buffer, and
each call returns a pointer that is offset into the buffer to point at
the latest formatted string.
3. There is no explicit buffer-free action. Instead, newly formatted
strings are appended to the buffer until there is no more space
available. At that point, the pointers are reset to the start of the
buffer. In effect, this provides a circular buffer, as previously
formatted strings are overwritten by subsequent calls.
4. The buffer is sized to hold the maximum number of concurrent strings
needed for a single trace line. If more concurrent strings are used, one
or more strings from the earliest calls will be overwritten.
*/
const char *fmt_char (uint32 charval)
{
static const char *const control [] = {
"NUL", "SOH", "STX", "ETX", "EOT", "ENQ", "ACK", "BEL",
"BS", "HT", "LF", "VT", "FF", "CR", "SO", "SI",
"DLE", "DC1", "DC2", "DC3", "DC4", "NAK", "SYN", "ETB",
"CAN", "EM", "SUB", "ESC", "FS", "GS", "RS", "US"
};
static char fmt_buffer [64]; /* the return buffer */
static char *freeptr = fmt_buffer; /* pointer to the first free character in the buffer */
static char *endptr = fmt_buffer + sizeof fmt_buffer - 5; /* pointer to the end of the buffer (less allowance) */
const char *fmtptr;
if (charval <= 0037u) /* if the value is an ASCII control character */
return control [charval]; /* then return a readable representation */
else if (charval == 0177u) /* otherwise if the value is the delete character */
return "DEL"; /* then return a readable representation */
else {
if (freeptr > endptr) /* if there is not enough room left to append the string */
freeptr = fmt_buffer; /* then reset to point at the start of the buffer */
fmtptr = freeptr; /* initialize the return pointer */
*freeptr = '\0'; /* and the format accumulator */
if (charval > D8_UMAX) /* if the value is beyond the 8-bit character range */
freeptr = freeptr + sprintf (freeptr, "%03o", /* then format the lower byte as octal */
LOWER_BYTE (charval)); /* and update the free pointer */
else if (charval > 0177u) /* otherwise if the value is beyond the printable range */
freeptr = freeptr + sprintf (freeptr, "\\%03o", /* then format as an escaped octal value */
charval); /* and update the free pointer */
else { /* otherwise it's a printable character */
*freeptr++ = '\''; /* so form a representation */
*freeptr++ = (char) charval; /* containing the character */
*freeptr++ = '\''; /* surrounded by single quotes */
*freeptr = '\0';
}
freeptr = freeptr + 1; /* advance past the NUL terminator */
return fmtptr; /* and return the formatted string */
}
}
/* Format a set of named bits.
This routine formats a set of up to 32 named bits into a printable string and
returns a pointer to that string. The names of the active bits are
concatenated and separated by vertical bars. For example:
ready | no error | unit 0
On entry, "bitset" is a value specifying the bits to format, and "bitfmt" is
a BITSET_FORMAT structure describing the format to use. The structure
contains a count and a pointer to an array of character strings specifying
the names of the valid bits in "bitset", the offset in bits from the LSB to
the least-significant named bit, the direction in which to process the bits
(from MSB to LSB, or vice versa), whether or not alternate names are present
in the name array, and whether or not to append a final separator. The names
in the name array appear in the order corresponding to the supplied
direction; invalid bits are indicated by NULL character names. The pointer
returned points at a character buffer containing the names of the valid bits
that are set in the supplied value. If no valid bits are set, then the
buffer contains "(none)" if a trailing separator is omitted, or a null string
("") if a trailing separator is requested.
The name_count and names fields describe the separately defined name string
array. The array must start with the first valid bit but need only contain
entries through the last valid bit; NULL entries for the remaining bits in
the word are not necessary. For example, if bits 14-12 of a word are valid,
then the name string array would have three entries. If bits 14-12 and 10
are valid, then the array would have five entries, with the fourth entry set
to NULL.
The offset field specifies the number of unnamed bits to the right of the
least-significant named bit. Using the same examples as above, the offsets
would be 12 and 10, respectively.
The direction field specifies whether the bits are named from MSB to LSB
(msb_first) or vice versa (lsb_first). The order of the entries in the name
string array must match the direction specified. Continuing with the first
example above, if the direction is msb_first, then the first name is for bit
14; if the direction is lsb_first, then the first name is for bit 12.
The alternate field specifies whether (has_alt) or not (no_alt) alternate
conditions are represented by one or more bits. Generally, bits represent
Boolean conditions, e.g., a condition is present when the bit is 1 and absent
when the bit is zero. In these cases, the corresponding bit name is included
or omitted, respectively, in the return string.
Occasionally, bits will represent alternate conditions, e.g., where condition
A is present when the bit is 1, and condition B is present when the bit is 0.
For these, the bit name string should consist of both condition names in that
order, with the "1" name preceded by the '\1' character and the "0" name
preceded by the '\0' character. For example, if 1 corresponds to "load" and
0 to "store", then the bit name string would be "\1load\0store". If
alternate names are present, the has_alt identifier should be given, so that
the indicated bits are checked for zero conditions. If no_alt is specified,
the routine stops as soon as all of the one-bits have been processed.
The bar field specifies whether (append_bar) or not (no_bar) a vertical bar
separator is appended to the formatted string. Typically, a bitset
represents a peripheral control or status word. If the word also contains
multiple-bit fields, a trailing separator should be requested, and the
decoded fields should be concatenated by the caller with any named bits. If
the bitset is empty, the returned null string will present the proper display
containing just the decoded fields. If the bitset completely describes the
word, then no appended separator is needed.
Peripheral control and status words generally are decoded from MSB to LSB. A
bitset may also represent a set of inbound or outbound signals. These should
be decoded from LSB to MSB, as that is the order in which they are executed
by the device interface routines.
The implementation first generates a mask for the significant bits and
positions the mask with the offset specified. Then a test bit mask is
generated; the bit is either the most- or least-significant bit of the
bitset, depending on the direction indicated.
For each name in the array of names, if the name is defined (not NULL), the
corresponding bit in the bitset is tested. If it is set, the name is
appended to the output buffer; otherwise, it is omitted (unless the name has
an alternate, in which case the alternate is appended). The bitset is then
shifted in the indicated direction, remasking to just the significant bits.
Processing continues until there are no remaining significant bits (if no
alternates are specified), or until there are no remaining names in the array
(if alternates are specified).
Implementation notes:
1. The routine returns a pointer to a static buffer containing the printable
string. To allow the routine to be called more than once per trace line,
the null-terminated format strings are concatenated in the buffer, and
each call returns a pointer that is offset into the buffer to point at
the latest formatted string.
2. There is no explicit buffer-free action. Instead, newly formatted
strings are appended to the buffer until there is no more space
available. At that point, the string currently being assembled is moved
to the start of the buffer, and the pointers are reset. In effect, this
provides a circular buffer, as previously formatted strings are
overwritten by subsequent calls.
3. The buffer is sized to hold the maximum number of concurrent strings
needed for a single trace line. If more concurrent strings are used, one
or more strings from the earliest calls will be overwritten. If an
attempt is made to format a string larger than the buffer, an error
indication string is returned.
4. The location of the end of the buffer used to determine if the next name
will fit includes an allowance for two separators that might be placed on
either side of the name and a terminating NUL character.
*/
const char *fmt_bitset (uint32 bitset, const BITSET_FORMAT bitfmt)
{
static const char separator [] = " | "; /* the separator to use between names */
static char fmt_buffer [1024]; /* the return buffer */
static char *freeptr = fmt_buffer; /* pointer to the first free character in the buffer */
static char *endptr = fmt_buffer + sizeof fmt_buffer /* pointer to the end of the buffer */
- 2 * (sizeof separator - 1) - 1; /* less allowance for two separators and a terminator */
const char *bnptr, *fmtptr;
uint32 test_bit, index, bitmask;
size_t name_length;
if (bitfmt.name_count < D32_WIDTH) /* if the name count is the less than the mask width */
bitmask = (1 << bitfmt.name_count) - 1; /* then create a mask for the name count specified */
else /* otherwise use a predefined value for the mask */
bitmask = D32_MASK; /* to prevent shifting the bit off the MSB end */
bitmask = bitmask << bitfmt.offset; /* align the mask to the named bits */
bitset = bitset & bitmask; /* and mask to just the significant bits */
if (bitfmt.direction == msb_first) /* if the examination is left-to-right */
test_bit = 1 << bitfmt.name_count + bitfmt.offset - 1; /* then create a test bit for the MSB */
else /* otherwise */
test_bit = 1 << bitfmt.offset; /* create a test bit for the LSB */
fmtptr = freeptr; /* initialize the return pointer */
*freeptr = '\0'; /* and the format accumulator */
index = 0; /* and the name index */
while ((bitfmt.alternate || bitset) /* while more bits */
&& index < bitfmt.name_count) { /* and more names exist */
bnptr = bitfmt.names [index]; /* point at the name for the current bit */
if (bnptr) /* if the name is defined */
if (*bnptr == '\1' && bitfmt.alternate) /* then if this name has an alternate */
if (bitset & test_bit) /* then if the bit is asserted */
bnptr++; /* then point at the name for the "1" state */
else /* otherwise */
bnptr = bnptr + strlen (bnptr) + 1; /* point at the name for the "0" state */
else /* otherwise the name is unilateral */
if ((bitset & test_bit) == 0) /* so if the bit is denied */
bnptr = NULL; /* then clear the name pointer */
if (bnptr) { /* if the name pointer is set */
name_length = strlen (bnptr); /* then get the length needed */
if (freeptr + name_length > endptr) { /* if there is not enough room left to append the name */
strcpy (fmt_buffer, fmtptr); /* then move the partial string to the start of the buffer */
freeptr = fmt_buffer + (freeptr - fmtptr); /* point at the new first free character location */
fmtptr = fmt_buffer; /* and reset the return pointer */
if (freeptr + name_length > endptr) /* if there is still not enough room left to append */
return "(buffer overflow)"; /* then this call is requires a larger buffer! */
}
if (*fmtptr != '\0') { /* if this is not the first name added */
strcpy (freeptr, separator); /* then add a separator to the string */
freeptr = freeptr + strlen (separator); /* and move the free pointer */
}
strcpy (freeptr, bnptr); /* append the bit's mnemonic to the accumulator */
freeptr = freeptr + name_length; /* and move the free pointer */
}
if (bitfmt.direction == msb_first) /* if formatting is left-to-right */
bitset = bitset << 1 & bitmask; /* then shift the next bit to the MSB and remask */
else /* otherwise formatting is right-to-left */
bitset = bitset >> 1 & bitmask; /* so shift the next bit to the LSB and remask */
index = index + 1; /* bump the bit name index */
}
if (*fmtptr == '\0') /* if no names were output */
if (bitfmt.bar == append_bar) /* then if concatenating with more information */
return ""; /* then return an empty string */
else /* otherwise it's a standalone format */
return "(none)"; /* so return a placeholder */
else if (bitfmt.bar == append_bar) { /* otherwise if a trailing separator is specified */
strcpy (freeptr, separator); /* then add a separator to the string */
freeptr = freeptr + strlen (separator) + 1; /* and account for it plus the trailing NUL */
}
else /* otherwise */
freeptr = freeptr + 1; /* just account for the trailing NUL */
return fmtptr; /* return a pointer to the formatted string */
}
/* Format and print a trace line to the debug log file.
A formatted line is assembled and sent to the previously opened debug output
stream. On entry, "dptr" points to the device issuing the trace, "flag" is
the trace flag that has enabled the trace, and the remaining parameters
consist of the format string and associated values.
This routine is usually not called directly but rather via the "tprintf"
macro, which tests that tracing is enabled for the specified flag before
calling this function. This eliminates the calling overhead if tracing is
disabled.
This routine prints a prefix before the supplied format string consisting of
the device name (in upper case) and the trace flag name (in lower case),
e.g.:
>>MPX state: Channel SR 3 entered State A
~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
prefix supplied format string
The names are padded to the lengths of the largest device name and trace flag
name among the devices enabled for tracing to ensure that all trace lines
will align for easier reading.
Implementation notes:
1. ISO C99 allows assignment expressions as the bounds for array
declarators. VC++ 2008 requires constant expressions. To accommodate
the latter, we must allocate "sufficiently large" arrays for the flag
name and format, rather than arrays of the exact size required by the
call parameters.
*/
#define FLAG_SIZE 32 /* sufficiently large to accommodate all flag names */
#define FORMAT_SIZE 1024 /* sufficiently large to accommodate all format strings */
void hp_trace (DEVICE *dptr, uint32 flag, ...)
{
const char *nptr;
va_list argptr;
DEBTAB *debptr;
char *format, *fptr;
char flag_name [FLAG_SIZE]; /* desired size is [flag_size + 1] */
char header_fmt [FORMAT_SIZE]; /* desired size is [device_size + flag_size + format_size + 6] */
if (sim_deb != NULL && dptr != NULL) { /* if the output stream and device pointer are valid */
debptr = dptr->debflags; /* then get a pointer to the trace flags table */
if (debptr != NULL) /* if the trace table exists */
while (debptr->name != NULL) /* then search it for an entry with the supplied flag */
if (debptr->mask & flag) { /* if the flag matches this entry */
nptr = debptr->name; /* then get a pointer to the flag name */
fptr = flag_name; /* and the buffer */
do
*fptr++ = (char) tolower (*nptr); /* copy and downshift the flag name */
while (*nptr++ != '\0');
sprintf (header_fmt, ">>%-*s %*s: ", /* format the prefix and store it */
(int) device_size, sim_dname (dptr), /* while padding the device and flag names */
(int) flag_size, flag_name); /* as needed for proper alignment */
va_start (argptr, flag); /* set up the argument list */
format = va_arg (argptr, char *); /* get the format string parameter */
strcat (header_fmt, format); /* append the supplied format */
vfprintf (sim_deb, header_fmt, argptr); /* format and print to the debug stream */
va_end (argptr); /* clean up the argument list */
break; /* and exit with the job complete */
}
else /* otherwise */
debptr++; /* look at the next trace table entry */
}
return;
}
/* Check for device conflicts.
The device information blocks (DIBs) for the set of enabled devices are
checked for consistency. Each select code must be unique among the enabled
devices. This requirement is checked as part of the instruction execution
prelude; this allows the user to exchange two select codes simply by setting
each device to the other's select code. If conflicts were enforced instead
at the time the codes were entered, the first device would have to be set to
an unused select code before the second could be set to the first device's
code.
The routine begins by filling in a DIB value table from all of the device
DIBs to allow indexed access to the values to be checked. Unused DIB values
and values corresponding to devices that have no DIBs or are disabled are set
to the corresponding UNUSED constants.
As part of the device scan, the sizes of the largest device name and active
trace flag name among the devices enabled for tracing are accumulated for use
in aligning the trace statements.
After the DIB value table is filled in, a conflict check is made by building
a conflict table, where each array element is set to the count of devices
that contain DIB value equal to the element index. For example, conflict
table element 6 is set to the count of devices that have dibptr->select_code
set to 6. If any conflict table element is set more than once, the
"conflict_is" variable is set.
If any conflicts exist, the conflict table is scanned. A conflict table
element value greater than 1 indicates a conflict. For each such value, the
DIB value table is scanned to find matching values, and the device names
associated with the matching values are printed.
This routine returns TRUE if any conflicts exist and FALSE there are none.
Implementation notes:
1. When this routine is called, the console and optional log file have
already been put into "raw" output mode. Therefore, newlines are not
translated to the correct line ends on systems that require it. Before
reporting a conflict, "sim_ttcmd" is called to restore the console and
log file translation. This is OK because a conflict will abort the run
and return to the command line anyway.
2. sim_dname is called instead of using dptr->name directly to ensure that
we pick up an assigned logical device name.
3. Only the names of active trace (debug) options are accumulated to produce
the most compact trace log. However, if the CPU device's EXEC option is
enabled, then all of the CPU option names are accumulated, as EXEC
enables all trace options for a given instruction or instruction class.
4. Even though the routine is called only from the sim_instr routine in the
CPU simulator module, it must be located here to use the DEVICE_COUNT
constant to allocate the dib_val matrix. If it were located in the CPU
module, the matrix would have to be allocated dynamically after a
run-time determination of the count of simulator devices.
*/
t_bool hp_device_conflict (void)
{
const DIB *dibptr;
const DEBTAB *tptr;
DEVICE *dptr;
size_t name_length, flag_length;
uint32 dev, val;
int32 count;
int32 dib_val [DEVICE_COUNT];
int32 conflicts [MAXDEV + 1];
t_bool is_conflict = FALSE;
device_size = 0; /* reset the device and flag name sizes */
flag_size = 0; /* to those of the devices actively tracing */
memset (conflicts, 0, sizeof conflicts); /* zero the conflict table */
for (dev = 0; dev < DEVICE_COUNT; dev++) { /* fill in the DIB value table */
dptr = sim_devices [dev]; /* from the device table */
dibptr = (DIB *) dptr->ctxt; /* and the associated DIBs */
if (dibptr && !(dptr->flags & DEV_DIS)) { /* if the DIB is defined and the device is enabled */
dib_val [dev] = dibptr->select_code; /* then copy the values to the DIB table */
if (++conflicts [dibptr->select_code] > 1) /* increment the count of references; if more than one */
is_conflict = TRUE; /* then a conflict occurs */
}
if (sim_deb && dptr->dctrl) { /* if tracing is active for this device */
name_length = strlen (sim_dname (dptr)); /* then get the length of the device name */
if (name_length > device_size) /* if it's greater than the current maximum */
device_size = name_length; /* then reset the size */
if (dptr->debflags) /* if the device has a trace flags table */
for (tptr = dptr->debflags; /* then scan the table */
tptr->name != NULL; tptr++)
if (dev == 0 && dptr->dctrl & TRACE_EXEC /* if the CPU device is tracing executions */
|| tptr->mask & dptr->dctrl) { /* or this trace option is active */
flag_length = strlen (tptr->name); /* then get the flag name length */
if (flag_length > flag_size) /* if it's greater than the current maximum */
flag_size = flag_length; /* then reset the size */
}
}
}
if (is_conflict) { /* if a conflict exists */
sim_ttcmd (); /* then restore the console and log I/O mode */
for (val = 0; val <= MAXDEV; val++) /* search the conflict table for the next conflict */
if (conflicts [val] > 1) { /* if a conflict is present for this value */
count = conflicts [val]; /* then get the number of conflicting devices */
cprintf ("Select code %o conflict (", val); /* report the multiply-assigned select code */
dev = 0; /* search for the devices that conflict */
while (count > 0) { /* search the DIB value table */
if (dib_val [dev] == (int32) val) { /* to find the conflicting entries */
if (count < conflicts [val]) /* and report them to the console */
cputs (" and ");
cputs (sim_dname (sim_devices [dev])); /* report the conflicting device name */
count = count - 1; /* and drop the count of remaining conflicts */
}
dev = dev + 1; /* move to the next device */
} /* and loop until all conflicting devices are reported */
cputs (")\n"); /* tie off the line */
} /* and continue to look for other conflicting select codes */
}
return (is_conflict); /* return TRUE if any conflicts exist */
}
/* Make a pair of devices consistent */
void hp_enbdis_pair (DEVICE *ccptr, DEVICE *dcptr)
{
if (ccptr->flags & DEV_DIS)
dcptr->flags |= DEV_DIS;
else
dcptr->flags &= ~DEV_DIS;
return;
}
/* System interface local SCP support routines */
/* One-time initialization.
This routine is called once by the SCP startup code. It fills in the
auxiliary command table from the corresponding system command table entries,
sets up the VM-specific routine pointers, and registers the supported
breakpoint types.
*/
static void one_time_init (void)
{
CTAB *systab, *auxtab = aux_cmds;
while (auxtab->name != NULL) { /* loop through the auxiliary command table */
systab = find_cmd (auxtab->name); /* find the corresponding system command table entry */
if (systab != NULL) { /* if it is present */
if (auxtab->action == NULL) /* then if the action routine field is empty */
auxtab->action = systab->action; /* then fill it in */
if (auxtab->arg == 0) /* if the command argument field is empty */
auxtab->arg = systab->arg; /* then fill it in */
if (auxtab->help == NULL) /* if the help string field is empty */
auxtab->help = systab->help; /* then fill it in */
auxtab->help_base = systab->help_base; /* fill in the help base and message fields */
auxtab->message = systab->message; /* as we never override them */
}
auxtab++; /* point at the next table entry */
}
sim_vm_cmd = aux_cmds; /* set up the auxiliary command table */
sim_vm_fprint_stopped = &fprint_stopped; /* set up the simulation-stop printer */
sim_vm_fprint_addr = &fprint_addr; /* set up the address printer */
sim_vm_parse_addr = &parse_addr; /* set up the address parser */
sim_vm_post = &cpu_post_cmd; /* set up the command post-processor */
sim_brk_types = BP_SUPPORTED; /* register the supported breakpoint types */
sim_brk_dflt = BP_ENONE; /* the default breakpoint type is "execution" */
return;
}
/* Format and print a VM simulation stop message.
When the instruction loop is exited, a simulation stop message is printed and
control returns to SCP. An SCP stop prints a message with this format:
<reason>, P: <addr> (<inst>)
For example:
SCPE_STOP prints "Simulation stopped, P: 24713 (CLA)"
SCPE_STEP prints "Step expired, P: 24713 (CLA)"
For VM stops, this routine is called after the message has been printed and
before the comma and program counter label and value are printed. Depending
on the reason for the stop, the routine may insert additional information,
and it may request omission of the PC value by returning FALSE instead of
TRUE.
This routine modifies the default output for these stop codes:
STOP_HALT prints "Programmed halt, T: 102077 (HLT 77), P: 24713 (CLA)"
STOP_NOTAPE prints "Tape not loaded in the <dev> device, P: 24713 (CLA)"
STOP_EOT prints "End of tape on the <dev> device, P: 24713 (CLA)"
STOP_NOCONN prints "Cable not connected to the <dev> device, P: 24713 (CLA)"
The HP 21xx/1000 halt instruction opcode includes select code and device flag
hold/clear bit fields. In practice, these are not used to affect the device
interface; rather, they communicate to the operator the significance of the
particular halt encountered.
Under simulation, the halt opcode must be communicated to the user as part of
the stop message. To so do, we define a sim_vm_fprint_stopped handler that
is called for all VM stops. When called for a STOP_HALT, the halt message
has been printed, and we add the opcode value in the T register before
returning TRUE, so that SCP will add the program counter value.
For unreported I/O error stops, the message must include the device name, so
the user will know how to correct the error. For these stops, the global
variable "cpu_ioerr_uptr" will point at the unit that encountered the error.
Implementation notes:
1. Normally, the "sim_eval" global value array either is preloaded with the
instruction and its operand addresses, or the first element is set to the
instruction and the address parameter includes the VAL_EMPTY flag to
indicate that the operands have not yet been loaded. In the STOP_HALT
case, the instruction is always a HLT, which is a single-word
instruction. Therefore, the VAL_EMPTY flag will not be checked and need
not be included.
*/
static t_bool fprint_stopped (FILE *st, t_stat reason)
{
DEVICE *dptr;
if (reason == STOP_HALT) { /* if this is a halt instruction stop */
sim_eval [0] = (t_value) TR; /* then save the opcode for display */
fprintf (st, ", T: %06o (", TR); /* print the T register value */
fprint_cpu (st, MR, sim_eval, 0, CPU_Symbol); /* then format and print the halt mnemonic */
fputc (')', st); /* and finally close the parenthesis */
}
else if (cpu_ioerr_uptr) { /* otherwise if this is an I/O error stop */
dptr = find_dev_from_unit (cpu_ioerr_uptr); /* then get the device pointer from the unit */
if (dptr) /* if the search succeeded */
fprintf (st, " the %s device", sim_dname (dptr)); /* then report the device */
else /* otherwise */
fputs (" an unknown device", st); /* report that the device is unknown */
}
return TRUE; /* return TRUE to append the program counter */
}
/* Format and print a memory address.
This routine is called by SCP to print memory addresses. It is also called
to print the contents of registers tagged with the REG_VMAD flag.
On entry, the "st" parameter is the opened output stream, "dptr" points to
the device to which the address refers, and "addr" contains the address to
print. The routine prints the linear address in <page>.<offset> form for CPU
addresses > 32K and as a scalar value for CPU addresses <= 32K and all other
devices.
*/
static void fprint_addr (FILE *st, DEVICE *dptr, t_addr addr)
{
uint32 page, offset;
if (dptr == &cpu_dev && addr > LA_MAX) { /* if a CPU address is outside of the logical address space */
page = PAGE (addr); /* then separate page and offset */
offset = OFFSET (addr); /* from the linear address */
fprint_val (st, page, dptr->aradix, PG_WIDTH, PV_RZRO); /* print the page address */
fputc ('.', st); /* followed by a period */
fprint_val (st, offset, dptr->aradix, OF_WIDTH, PV_RZRO); /* and concluding with the offset */
}
else /* otherwise print the value */
fprint_val (st, addr, dptr->aradix, dptr->awidth, PV_LEFT); /* as a scalar for all other devices */
return;
}
/* Parse a memory address.
This routine is called by SCP to parse memory addresses. It is also called
to parse values to be stored in registers tagged with the REG_VMAD flag.
On entry, the "dptr" parameter points to the device to which the address
refers, and "cptr" points to the first character of the address operand on
the command line. On exit, the linear address is returned, and the pointer
pointed to by "tptr" is set to point at the first character after the parsed
address. Parsing errors, including use of features disallowed by the command
in process, are indicated by the "tptr" pointer being set to "cptr".
The HP 1000 divides memory into 1K-word pages within a 32K logical address
space. With memory expansion disabled, only the first 32 pages are
accessible. With memory expansion enabled, memory maps are used to translate
logical to physical addresses. Each translation uses one of four possible
maps. Program accesses use the system map or the user map, depending on
which one is designated the "current map." DCPC accesses use the port A or
port B map, depending on whether channel 1 or channel 2, respectively, is
performing the access. Each map translates the 32 logical pages to 32 of the
1024 physical pages.
The HP 2114, 2115, 2116, and 2100 provide up to 32 pages of memory. Memory
expansion is not provided with these machines.
The simulator supports only linear addresses for all devices other than the
CPU. For the CPU, two forms of address entries are allowed:
- a logical address consisting of a 15-bit offset within the 32K logical
address space (e.g., 77777).
- a physical address consisting of a 10-bit page number and a 10-bit
offset within the page, separated by a period (e.g., 1777.1777)
Command line switches modify the interpretation of logical addresses as
follows:
Switch Interpretation
------ --------------------------------------------------
-N Use no mapping
-S If memory expansion is enabled, use the system map
-U If memory expansion is enabled, use the user map
-P If memory expansion is enabled, use the port A map
-Q If memory expansion is enabled, use the port B map
If no switch is specified, the address is interpreted using the current map
if memory expansion is enabled; otherwise, the address is not mapped. If the
current or specified map is used, then the address must lie within the 32K
logical address space.
The "parse_physical" global specifies whether or not a physical address is
permitted. Addresses used by the RUN, GO, BREAK, and NOBREAK commands must
resolve to logical addresses, whereas EXAMINE and DEPOSIT commands allow both
physical and logical address specifications.
*/
static t_addr parse_addr (DEVICE *dptr, CONST char *cptr, CONST char **tptr)
{
CONST char *sptr;
t_addr page;
t_addr address = 0;
if (dptr != &cpu_dev) /* if this is not a CPU memory address */
return (t_addr) strtotv (cptr, tptr, dptr->aradix); /* then parse a scalar and return the value */
address = strtotv (cptr, tptr, dptr->aradix); /* parse the address */
if (cptr != *tptr) /* if the parse succeeded */
if (**tptr == '.') /* then if this a paged address */
if (address > PG_MAX) /* then if the page number is out of range */
*tptr = cptr; /* then report a parse error */
else { /* otherwise the <bank>.<offset> form is allowed */
sptr = *tptr + 1; /* point to the offset */
page = address; /* save the first part as the bank address */
address = strtotv (sptr, tptr, dptr->aradix); /* parse the offset */
if (address > OF_MAX) /* if the offset is too large */
*tptr = cptr; /* then report a parse error */
else /* otherwise it is in range */
address = TO_PA (page, address); /* so form the linear address */
}
else if (address > LA_MAX) /* otherwise if the non-paged offset is too large */
*tptr = cptr; /* then report a parse error */
if (parse_physical == FALSE /* if only logical addresses are permitted */
&& address > LA_MAX) /* and the parsed address is out of range */
*tptr = cptr; /* then report a parse error */
return address; /* return the linear address */
}
/* Execute the EXAMINE, DEPOSIT, IEXAMINE, and IDEPOSIT commands.
These commands are intercepted to configure address parsing. The following
address forms are valid:
EXAMINE <page>.<offset>
EXAMINE <logical-address>
This routine configures the address parser and calls the standard command
handler.
*/
static t_stat hp_exdep_cmd (int32 arg, CONST char *buf)
{
parse_physical = TRUE; /* allow the <page.<offset> address form */
return exdep_cmd (arg, buf); /* return the result of the standard handler */
}
/* Execute the RUN and GO commands.
These commands are intercepted to configure address parsing. The following
address form is valid:
RUN { <logical-address> }
GO { <logical-address> }
If no argument is specified, the breakpoint address defaults to the current
value of P. This routine configures the address parser and calls the
standard command handler.
*/
static t_stat hp_run_cmd (int32 arg, CONST char *buf)
{
parse_physical = FALSE; /* allow the <logical-address> address form only */
return run_cmd (arg, buf); /* return the result of the standard handler */
}
/* Execute the BREAK and NOBREAK commands.
These commands are intercepted to configure address parsing. The following
address forms are valid:
BREAK
BREAK <logical-address>
If no argument is specified, the breakpoint address defaults to the current
value of P. This routine configures the address parser and calls the
standard command handler.
*/
static t_stat hp_brk_cmd (int32 arg, CONST char *buf)
{
parse_physical = FALSE; /* allow the <logical-address> address form only */
return brk_cmd (arg, buf); /* return the result of the standard handler */
}
/* Execute the LOAD command.
This command is intercepted to permit a device boot routine to be loaded
into memory. The following command forms are valid:
LOAD <dev>
LOAD <filename> [ <select-code> ]
If the first form is used, and the device name is valid and bootable, the
corresponding boot loader is copied into memory in the highest 64 locations
of the logical address space. Upon return, the loader will have been
configured for the device's select code or for the device and paper tape
reader select codes, in the case of a dual-use 21xx boot loader, and the P
register will have been set to point at the start of the loader. The loader
then may be executed by a RUN command. If the device name is valid, but the
device is not bootable, or no boot loader exists for the current CPU
configuration, the command is rejected.
If the second form is used, the file containing a loader in absolute binary
form is read into memory and configured as above. See the "sim_load"
comments for details of this operation.
Implementation notes:
1. The "find_dev" routine requires that the device name be in upper case to
match. The "get_glyph" routine performs case-shifting on the input
buffer.
*/
static t_stat hp_load_cmd (int32 arg, CONST char *buf)
{
CONST char *cptr;
char cbuf [CBUFSIZE];
DEVICE *dptr = NULL;
GET_SWITCHES (buf); /* parse any switches present */
cptr = get_glyph (buf, cbuf, '\0'); /* parse a potential device name */
if (cbuf [0] != '\0') { /* if the name is present */
dptr = find_dev (cbuf); /* then see if it matches a device */
if (dptr != NULL) /* if it does */
if (dptr->boot == NULL) /* then if the device is not bootable */
return SCPE_NOFNC; /* then report "Command not allowed" */
else if (*cptr != '\0') /* otherwise if more characters follow */
return SCPE_2MARG; /* then report "Too many arguments" */
else /* otherwise the device name stands alone */
return dptr->boot (0, dptr); /* so load the corresponding boot loader */
}
return load_cmd (arg, buf); /* if it's not a device name, then try loading a file */
}
/* System interface local utility routines */
/* Print a numeric value in a given radix.
This routine prints a numeric value using the specified radix, width, and
output format. If the radix is 256, then the value is printed as a single
character. Otherwise, it is printed as a numeric value.
On entry, the "ofile" parameter is the opened output stream, "val" is the
value to print, "radix" is the desired print radix, "width" is the number of
significant bits in the value, and "format" is a format specifier (PV_RZRO,
PV_RSPC, or PV_LEFT). On exit, the routine returns SCPE_OK if the value was
printed successfully, or SCPE_ARG if the value could not be printed.
*/
static t_stat fprint_value (FILE *ofile, t_value val, uint32 radix, uint32 width, uint32 format)
{
if (radix == 256) /* if ASCII character display is requested */
if (val <= D8_SMAX) { /* then if the value is a single character */
fputs (fmt_char ((uint32) val), ofile); /* then format and print it */
return SCPE_OK; /* and report success */
}
else /* otherwise */
return SCPE_ARG; /* report that it cannot be displayed */
else /* otherwise format and print the value */
return fprint_val (ofile, val, radix, width, format); /* using the supplied radix and format */
}
/* Print a CPU instruction in symbolic format.
This routine is called to decode and print a CPU instruction mnemonic and any
associated operand(s). On entry, "ofile" is the opened output stream, "addr"
is the memory address location of the instruction, "val" is a pointer to an
array of t_values of depth "sim_emax" containing the instruction to be
printed, "radix" is zero to use the default radix for the operand(s) or a
value indicating a requested radix override, "op_desc" is a structure that
describes the characteristics of the instruction opcode, and "ops" is the
table of opcodes that pertain to the class to which the supplied instruction
belongs.
On exit, a status code is returned to the caller. If the instruction is
provided by a firmware option that is not installed, SCPE_UNK status is
returned; this permits the caller to try a different opcode table if the
opcode is in a shared address space (e.g., the EMA and VMA firmware). If the
instruction is not present in the supplied opcode table, SCPE_ARG status is
returned, which causes the caller to print the instruction in numeric format
with the default radix. Otherwise, SCPE_OK status is returned if a
single-word instruction was printed, or the negative number of extra words
(beyond the first) consumed in printing the instruction is returned. For
example, printing a two-word instruction returns SCPE_OK_2_WORDS (= -1).
The "addr" parameter is used in two cases: to supply the operand address(es)
that follow the instruction when the "val" array has not been fully
populated, and to supply the current page for memory reference instructions
that have the C bit set.
If the routine is called to examine a memory location, the "val" array will
be fully populated with the instruction and the values that follow. However,
if the routine is called by an instruction trace, only the first word is set;
this is to avoid the overhead of loading extra operand words for instructions
that do not have operands (the vast majority). In this case, the "addr"
parameter will contain the address of the instruction word ORed with the
VAL_EMPTY flag. Once the instruction is decoded, the routine knows the
number of operand words required, and these are loaded before printing the
operands.
When printing a current-page memory reference instruction residing in CPU
memory, the instruction word supplies the 10-bit offset from the page
containing the instruction. This is merged with the page number from the
"addr" parameter to form the operand address.
However, if the instruction is not in memory (e.g., it resides in a disc data
buffer or device register), then the final load address is indeterminate. In
this case, the "addr" parameter is set to a value greater than the maximum
possible memory address as an indication that only the current-page offset is
valid.
All machine instruction opcodes are single-word, with the exception of the
vector arithmetic instructions in the Vector Instruction Set; these use
two-word opcodes. If the routine is entered to print a VIS instruction, "val
[0]" and "val [1]" must both be set, even if the VAL_EMPTY flag is set.
Upon entry, the required feature set from the opcode description is checked
against the current CPU feature set. If the required option is not
installed, the routine exits with SCPE_UNK. Otherwise, the instruction is
masked and shifted to obtain the primary index into the opcode table. For
instructions that use the A/B-selector bit (bit 11), the index is shifted to
the second half of the table if the bit is set.
If the primary entry exists, then the opcode mnemonic is obtained from the
entry and printed. Otherwise, the secondary table entries must be searched
linearly. Each entry is compared to the instruction with the operand bits
masked off; if a match occurs, then the opcode mnemonic is checked. If it is
empty, then this is a two-word instruction whose second word is invalid. In
this case, the routine prints the two words in numeric format, separated by a
comma, and returns success to indicate that the instruction was printed.
Otherwise the mnemonic is present, so it is printed. If the opcode
descriptor indicates that only a single match is permitted, the loop is
exited to print the operand(s), if any. Otherwise, multiple matches are
allowed (e.g., for SRG instructions), so the secondary entry search is
continued until the end of the table.
If the instruction does not match either a primary or a secondary entry, the
routine exits with SCPE_ARG status. The caller will then print the value in
numeric form.
The type and count of operand(s), if any, are indicated by the "type" and
"count" values in the matched opcode table entry. The entry also indicates
which operand(s) are addresses and which are data values.
If operands are needed but not present in the "val" array, they are loaded
now using the current DMS map. The operand type dispatches to one of several
handlers that obtain the operand(s) from the instruction. Operands that are
extracted from the instruction are placed in "val [1]" for printing. Each
operand is printed using the address radix for addresses and the supplied
data radix for data.
After printing the operand(s), the routine returns the number of words
consumed.
Implementation notes:
1. If the opcode descriptor mask is zero, then the opcode table contains
only secondary entries. The descriptor shift value then determines
whether a single match is allowed (zero) or multiple matches are allowed
(non-zero).
2. If the opcode descriptor A/B-register selector indicates that bit 11 is
decoded, then the opcode primary table will be twice the size implied by
the opcode mask width. The first half of the table decodes instructions
with bit 11 equal to zero; the second half decodes instruction with bit
11 equal to 1.
3. Only the SRG and ASG instructions contain multiple micro-opcodes, and
none of these take operands.
*/
static t_stat fprint_instruction (FILE *ofile, t_addr addr, t_value *val, uint32 radix,
const OP_DESC op_desc, const OP_TABLE ops)
{
OP_TYPE op_type;
uint32 op_index, op_size, op_count, op_radix, op_address_set;
t_value instruction, op_value;
t_stat status;
const char *prefix = NULL; /* label to print before the operand */
t_bool clear = FALSE; /* TRUE if the instruction contains a CLF micro-op */
t_bool separator = FALSE; /* TRUE if a separator between multiple ops is needed */
uint32 op_start = 1; /* the "val" array index of the first operand */
if (!(cpu_configuration & op_desc.feature & OPTION_MASK /* if the required feature set is not enabled */
&& cpu_configuration & op_desc.feature & CPU_MASK)) /* for the current CPU configuration */
return SCPE_UNK; /* then we cannot decode the instruction */
instruction = TO_DWORD (val [1], val [0]); /* merge the two supplied values */
op_size = (op_desc.mask >> op_desc.shift) /* determine the size of the primary table part */
+ (op_desc.mask != 0); /* if it is present */
op_index = ((uint32) instruction & op_desc.mask) >> op_desc.shift; /* extract the opcode primary index */
if (op_desc.ab_selector) { /* if the A/B-register selector is significant */
if (op_desc.ab_selector & instruction) /* then if the A/B-register selector bit is set */
op_index = op_index + op_size; /* then use the second half of the table */
op_size = op_size * 2; /* the primary table is twice the indicated size */
}
if (op_desc.mask && ops [op_index].mnemonic [0]) /* if a primary entry is defined */
fputs (ops [op_index].mnemonic, ofile); /* then print the mnemonic */
else { /* otherwise search through the secondary entries */
for (op_index = op_size; /* starting after the primary entries */
ops [op_index].mnemonic != NULL; /* until the NULL entry at the end */
op_index++)
if (ops [op_index].opcode == /* if the opcode in this table entry */
(instruction /* matches the instruction */
& ops [op_index].op_bits /* masked to the significant opcode bits */
& op_props [ops [op_index].type].mask)) /* and with the operand bits masked off */
if (ops [op_index].mnemonic [0]) { /* then if the entry is defined */
if (separator) /* then if a separator is needed */
fputc (',', ofile); /* then print it */
fputs (ops [op_index].mnemonic, ofile); /* print the opcode mnemonic */
if (op_desc.mask == OP_LINEAR /* if multiple matches */
&& op_desc.shift == OP_MULTIPLE) /* are allowed */
separator = TRUE; /* then separators will be needed between mnemonics */
else /* otherwise */
break; /* the search terminates on the first match */
}
else { /* otherwise this two-word instruction is unimplemented */
fprint_val (ofile, val [0], cpu_dev.dradix, /* so print the first word */
cpu_dev.dwidth, PV_RZRO); /* with the default radix */
fputc (',', ofile); /* add a separator */
fprint_val (ofile, val [1], cpu_dev.dradix, /* print the second word */
cpu_dev.dwidth, PV_RZRO);
return SCPE_OK_2_WORDS; /* return success to indicate printing is complete */
}
if (separator) /* if one or more micro-ops was found */
return SCPE_OK; /* then return, as there are no operands */
else if (ops [op_index].mnemonic == NULL) /* otherwise if the opcode was not found */
return SCPE_ARG; /* then return error status to print it in octal */
}
op_type = ops [op_index].type; /* get the type of the instruction operand(s) */
op_value = val [0] & ~op_props [op_type].mask; /* mask the first instruction word to the operand value */
op_count = (uint32) op_props [op_type].count; /* get the number of operands */
status = (t_stat) - op_props [op_type].count; /* and set the initial number of words consumed */
op_address_set = op_props [op_type].address_set; /* get the address/data-selector bit set */
op_radix = (radix ? radix : cpu_dev.dradix); /* assume that the (only) operand is data */
if (ops [op_index].op_bits > D16_MASK) { /* if this is a two-word instruction */
op_start = 2; /* then the operands start after the second word */
op_count = op_count + 1; /* and extend for an extra word */
status = status - 1; /* and consume an additional word */
}
if (op_count > 0 && (addr & VAL_EMPTY)) { /* if operand words are needed but not loaded */
addr = addr & LA_MASK; /* then restore the logical address */
for (op_index = op_start; op_index <= op_count; op_index++) /* starting with the first operand */
val [op_index] = mem_fast_read ((HP_WORD) (addr + op_index), dms_ump); /* load the operands from memory */
}
switch (op_type) { /* dispatch by the operand type */
/* no operand */
case opNone:
break; /* no formatting needed */
/* MRG page bit 10, offset 0000-1777 octal, indirect bit 15 */
case opMPOI:
if (addr > LA_MAX) /* if the instruction location is indeterminate */
if (instruction & I_CP) /* then if the current-page bit is set */
prefix = " C "; /* then prefix the offset with "C" */
else /* otherwise it's a base-page reference */
prefix = " Z "; /* so prefix the offset with "Z" */
else { /* otherwise the address is valid */
prefix = " "; /* so use a blank separator */
if (instruction & I_CP) /* if the current-page bit is set */
op_value |= addr & I_PAGENO; /* then merge the offset with the current page address */
}
val [1] = op_value; /* set the operand value */
op_count = 1; /* and print only one operand */
break;
/* IOG hold/clear bit 9 */
case opHC:
if (op_value) /* if the clear-flag bit is set */
fputs (" C", ofile); /* then add the "C" to the mnemonic */
break;
/* IOG select code range 00-77 octal, hold/clear bit 9 */
/* IOG optional select code range 00-77 octal, hold/clear bit 9 */
case opSCHC:
case opSCOHC:
clear = (instruction & I_HC); /* set TRUE if the clear-flag bit is set */
/* fall into the opSC case */
/* IOG select code range 00-77 octal */
case opSC:
prefix = " "; /* add a separator */
val [1] = op_value; /* and set the operand value */
op_count = 1; /* and print only one operand */
break;
/* EAU shift/rotate count range 1-16 */
case opShift:
prefix = " "; /* add a separator */
op_radix = (radix ? radix : 10); /* and default the shift counts to decimal */
if (op_value == 0) /* if the operand is zero */
val [1] = 16; /* then the shift count is 16 */
else /* otherwise */
val [1] = op_value; /* then shift count is the operand value */
op_count = 1; /* print only one operand */
break;
/* IOP index negative offset range 1-20 octal */
case opIOPON:
prefix = " -"; /* prefix the operand with the sign */
val [1] = 16 - op_value; /* and set the (absolute) offset */
op_radix = (radix ? radix : 10); /* default the offset to decimal */
op_count = 1; /* and print only one operand */
break;
/* IOP index positive offset range 0-17 octal */
case opIOPOP:
prefix = " "; /* add a separator */
val [1] = op_value; /* and set the (positive) offset */
op_radix = (radix ? radix : 10); /* default the offset to decimal */
op_count = 1; /* and print only one operand */
break;
/* IOP index offset range 0-37 octal (+20 bias) */
case opIOPO:
if (op_value >= 020) { /* if the operand is positive */
prefix = " "; /* then omit the sign */
val [1] = op_value - 020; /* and remove the bias */
}
else { /* otherwise the operand is negative */
prefix = " -"; /* so prefix a minus sign */
val [1] = 020 - op_value; /* and remove the bias to get the absolute value */
}
op_radix = (radix ? radix : 10); /* default the offset to decimal */
op_count = 1; /* and print only one operand */
break;
/* UIG zero word, four (indirect) memory addresses */
/* UIG zero word, five (indirect) memory addresses */
/* UIG zero word, six (indirect) memory addresses */
/* UIG zero word, eight (indirect) memory addresses */
case opZA4:
case opZA5:
case opZA6:
case opZA8:
prefix = " "; /* add a separator */
op_start = 2; /* and skip the all-zeros word */
break;
/* One memory address range 00000-77777 octal, zero word, indirect bit 15 */
case opMA1ZI:
prefix = " "; /* add a separator */
op_count = 1; /* and print only one operand */
break;
/* One to seven memory addresses range 00000-77777 octal, indirect bit 15 */
case opMA1I:
case opMA2I:
case opMA3I:
case opMA4I:
case opMA5I:
case opMA6I:
case opMA7I:
/* UIG one data value */
/* UIG two data values */
/* UIG one (indirect) memory address, one data value */
/* UIG two data values, one (indirect) memory address */
/* UIG one data value, five (indirect) memory addresses */
case opV1:
case opV2:
case opA1V1:
case opV2A1:
case opV1A5:
prefix = " "; /* add a separator */
break;
} /* end of the operand type dispatch */
if (prefix) /* if an operand is present */
for (op_index = op_start; /* then format the operands */
op_index <= op_count; /* starting with the first operand word */
op_index++) {
fputs (prefix, ofile); /* print the operand prefix or separator */
if (op_address_set & 1) { /* if this operand is an address */
fprint_val (ofile, val [op_index] & LA_MASK, /* then print the logical address */
cpu_dev.aradix, LA_WIDTH, PV_LEFT); /* using the address radix */
if (val [op_index] & I_IA) /* add an indirect indicator */
fputs (",I", ofile); /* if specified by the operand */
}
else /* otherwise it's a data value */
fprint_value (ofile, val [op_index], /* so print the full value */
op_radix, DV_WIDTH, PV_LEFT); /* using the specified radix */
op_address_set = op_address_set >> 1; /* shift the next address/data flag bit into place */
} /* and loop until all operands are printed */
if (clear) /* add a clear-flag indicator */
fputs (",C", ofile); /* if specified by the instruction */
return status; /* return the number of words consumed */
}
/* Parse an address operand.
Address operands of the form:
<octal value>[,I]
...are parsed. On entry, "cptr" points at the first character of the octal
value, and "status" points at a variable to hold the return status. If the
parse succeeds, the address with the optional indirect bit (bit 15) is
returned, and the status is set to SCPE_OK. If the parse fails, the value is
out of the logical range, or extraneous characters follow the operand,
then zero is returned, and the status is set to SCPE_ARG.
Implementation notes:
1. The string that "cptr" points to is trimmed of trailing spaces before this
routine is called. Therefore, the trailing NUL test is sufficient to
determine if there are more characters in the input buffer.
*/
static t_value parse_address (CONST char *cptr, t_stat *status)
{
CONST char *iptr;
t_value address;
address = strtotv (cptr, &iptr, cpu_dev.aradix); /* parse the address */
if (cptr == iptr || address > LA_MAX) { /* if a parse error occurred or the value is too big */
*status = SCPE_ARG; /* then return an invalid argument error */
return 0;
}
else if (*iptr == '\0') { /* otherwise if there is no indirect indicator */
*status = SCPE_OK; /* then the parse succeeds */
return address; /* and return the address */
}
else if (strcmp (iptr, ",I") == 0) { /* otherwise if there is an indirect indicator */
*status = SCPE_OK; /* then the parse succeeds */
return address | I_IA; /* and return the address with the indirect bit set */
}
else { /* otherwise there are extraneous characters following */
*status = SCPE_ARG; /* so return an invalid argument error */
return 0;
}
}
/* Parse a character or numeric value.
This routine parses a token pointed to by "cptr" using the supplied "radix"
and with the maximum allowed value "max". If the parse succeeds, the routine
sets "status" to SCPE_OK and returns the parsed value.
Single-character ASCII parsing is performed if the specified radix is 256.
Otherwise, a numeric parse is attempted.
*/
static t_value parse_value (CONST char *cptr, uint32 radix, t_value max, t_stat *status)
{
if (radix == 256) /* if ASCII character parsing is requested */
if (cptr [0] != '\0' && (t_value) cptr [0] < max) { /* then if a character is present and within range */
*status = SCPE_OK; /* then indicate success */
return (t_value) cptr [0]; /* and convert the character value */
}
else { /* otherwise */
*status = SCPE_ARG; /* report that the character parse failed */
return 0;
}
else /* otherwise parse as a number */
return get_uint (cptr, radix, max, status); /* with the specified radix and maximum value */
}
/* Parse a CPU instruction.
This routine parses the command line for a CPU instruction and its
operand(s), if any. On entry, "cptr" points at the instruction mnemonic,
"addr" is the address where the instruction will be stored, "val" points to
an array of t_values of depth "sim_emax" where the word(s) comprising the
machine instruction will be saved, "radix" contains the desired operand radix
or zero if the default radix is to be used, and "target" indicates the target
of the instruction (device, CPU, or trace).
The routine returns a status code to the caller. SCPE_OK status is returned
if a single-word instruction was parsed, or the negative number of extra
words (beyond the first) occupied by the instruction is returned. For
example, parsing a two-word instruction returns SCPE_OK_2_WORDS (= -1). If
"cptr" does not point at a valid mnemonic, or the mnemonic is not valid for
the current CPU feature set, or an operand error is present, SCPE_ARG is
returned.
The routine first separates the instruction mnemonic from its operands, if
any. If the first token is not present or starts with a digit, SCPE_ARG is
returned. If the target for the parsed value is not a CPU memory location,
the address is reset to an invalid value to indicate that a relative offset
parse should be performed for Memory Reference Group instructions. Then the
parser table is scanned to search for a match.
The parser table contains entries pointing to pairs of opcode descriptors and
opcode tables, in order of decreasing frequency of appearance in the typical
instruction mix. For each entry, if the required firmware option is
currently installed in the CPU, then the table is searched linearly for a
match with the mnemonic token. If a match is found, the "parse_instruction"
routine is called to assemble the instruction opcode and any associated
operands into the "val" array for return to the caller.
If the token does not match any legal instruction mnemonic, the routine
returns SCPE_ARG.
Implementation notes:
1. The mnemonic token is delineated by either a space or a comma; the latter
is used to separate the multiple mnemonics of SRG and ASG instructions.
*/
static t_stat parse_cpu (CONST char *cptr, t_addr addr, t_value *val, uint32 radix, SYMBOL_SOURCE target)
{
CONST char *gptr;
const PARSER_ENTRY *pptr;
const OP_ENTRY *eptr;
char gbuf [CBUFSIZE];
gptr = get_glyph (cptr, gbuf, ','); /* parse the opcode and shift to uppercase */
if (gbuf [0] == '\0' || isdigit (gbuf [0])) /* if the glyph is missing or is numeric */
return SCPE_ARG; /* then it is not an instruction mnemonic */
if (target == Device_Symbol) /* if the target is not main memory */
addr = PA_MAX + 1; /* then invalidate the target address */
for (pptr = parser_table; pptr->descriptor != NULL; pptr++) /* search the parser table */
if (cpu_configuration & pptr->descriptor->feature & OPTION_MASK /* if the required feature set is enabled */
&& cpu_configuration & pptr->descriptor->feature & CPU_MASK) /* for the current CPU configuration */
for (eptr = pptr->opcodes; eptr->mnemonic != NULL; eptr++) /* then search the opcode table */
if (strcmp (eptr->mnemonic, gbuf) == 0) /* if a matching mnemonic is found */
return parse_instruction (gptr, addr, val, radix, eptr); /* then parse the operands */
return SCPE_ARG; /* no match was found, so return failure */
}
/* Parse a CPU instruction in symbolic format.
This routine is called to parse and encode a CPU instruction mnemonic and any
associated operand(s). On entry, "cptr" points at the input buffer after the
first mnemonic token, "addr" is the address where the instruction will be
stored, "val" points to an array of t_values of depth "sim_emax" where the
word(s) comprising the machine instruction will be saved, "radix" contains
the desired operand parsing radix or zero if the default radix is to be used,
and "optr" points at the entry in an opcode table corresponding to the
initial instruction mnemonic. If "optr" points at an SRG or ASG opcode
entry, then "cptr" may point to additional micro-ops to be merged with the
original opcode. Otherwise, "cptr" will point to any needed instruction
operands.
On exit, a status code is returned to the caller. SCPE_OK status is returned
if a single-word instruction was parsed, or the negative number of extra
words (beyond the first) occupied by the instruction is returned. For
example, parsing a two-word instruction returns SCPE_OK_2_WORDS (= -1). If
the remainder of the input buffer does not parse correctly, or does not
contain the operands required by the instruction, SCPE_ARG is returned.
The routine begins by saving the initial opcode word(s) from the opcode table
entry. If the opcode belongs to the SRG or ASG, it may be the first of
several micro-ops that must be parsed individually. To ensure that each
micro-op is specified only once and that any A/B-register usage is consistent
between micro-ops, the significant bits of each parsed micro-op are
accumulated. The accumulated bits are passed to the "parse_micro_op"
routine, where the checks are made.
If the initial opcode is from the ASG, any additional micro-op mnemonics in
the input buffer must be in the order they appear in the "asg_uops" table.
The scan starts with the opcode table entry after the one corresponding to
the initial opcode.
Parsing SRG opcodes is more involved. The first opcode may be a shift or
rotate instruction, or it may be an SRG micro-op (CLE and/or SLA/B). In
either case, parsing continues with the (remaining) micro-ops and then with a
second shift or rotate instruction.
A complication is that an initial CLE, SLA, and SLB may be either an SRG or
an ASG micro-op. The determination depends on whether they appear with a
shift or rotate instruction or with a micro-op belonging only to the ASG.
The decision must be deferred until a mnemonic from one of these two groups
is parsed. If CLE and/or SLA/B appears alone, then it is encoded as an SRG
instruction.
Examples of SRG and ASG parsing follow:
Input String Parsed as Reason
--------------- --------- ---------------
CLE SRG
CLE,SLA SRG
CLE,SLA,SSA ASG
CME,SLA ASG
SLA,SSB error mixed A/B
SEZ,ERA error mixed ASG/SRG
SLA,CLE error out of order
RAL SRG
RAL,RAL SRG
RAL,CLE,SLA,ERA SRG
RAL,CLE,SLA,ERB error mixed A/B
RAL,SLB error mixed A/B
RAL,CLE,SSA error mixed SRG/ASG
CLA,CCA error bad combination
CLA,CLA error duplicate
If the initial opcode is not an SRG or ASG opcode, then only a single
mnemonic is allowed, and the operand(s), if any, are now examined. The type
and count of operand(s), if any, are indicated by the "type" and "count"
values in the matched opcode table entry. The entry also indicates which
operand(s) are addresses and which are data values. These values drive the
parsing of the operands. Parsed operand values are placed in the "val" array
in order of appearance after the instruction word.
An MRG memory address operand may be specified either as an absolute address
or as an explicit page indicator ("Z" or "C") and a page offset. An absolute
address is converted into an offset and zero/current page indicator by
examining the address value. If the value is less than 2000 octal, a
base-page reference is encoded. If the value falls within the same page as
the "addr" parameter value, a current-page reference is encoded. If neither
condition is true, then SCPE_ARG is returned.
IOG instructions parse the select code and an optional hold-or-clear-flag
indicator. These values are encoded into the instruction. Parsing is
complicated in that specification of the select code is optional for the HLT
instruction, defaulting to select code 0. If the select code is omitted, the
comma that separates it and the "C" indicator is also omitted.
EAG shift and rotate counts are specified as 1-16 but are encoded in the
instruction as 1-15 and 0, respectively. IOP index instructions offsets are
specified as -16 to 15 (or +15) but are encoded in the instruction in
excess-16 format.
The remaining instructions encode operands in separate memory words following
the opcode. A few instructions that are interruptible require emission of an
all-zeros word following the instruction (either preceding or following the
operands). Address operands are parsed as absolute logical addresses with
optional indirect indicators. Data operands are parsed as absolute values.
Parsing concludes with a check for extraneous characters on the line;
presence causes an SCPE_ARG return.
Implementation notes:
1. The "repeated instruction" test for the second shift/rotate op is needed
to catch the "NOP,ALS" sequence.
2. The select code may be omitted when entering the HLT instruction (it
defaults to 0), so "HLT" and "HLT C" are permissible entries. For the
latter, if the radix is overridden to hexadecimal, the "C" is interpreted
as select code 14 (octal) rather than as a "halt and clear flag"
instruction.
3. Parsing the 2100 IOP instruction mnemonics "LAI" and "SAI" always matches
the "opIOPON" entries. Therefore, the opcodes are those of the negative
index instructions. However, because the negative and positive opcode
values are adjacent, adding (not ORing) the offset value produces the
correct instruction encoding for both (index -16 => encoded 0, -1 => 15,
0 => 16, and +15 => 31).
*/
static t_stat parse_instruction (CONST char *cptr, t_addr addr, t_value *val, uint32 radix, const OP_ENTRY *optr)
{
CONST char *gptr;
const char *mptr;
char gbuf [CBUFSIZE];
OP_TYPE op_type;
uint32 accumulator, op_index, op_count, op_radix, op_address_set;
t_stat status, consumption;
t_value op_value;
t_bool op_implicit;
t_bool op_flag = FALSE;
uint32 op_start = 1; /* the "val" array index of the first operand */
val [0] = LOWER_WORD (optr->opcode); /* set the (initial) opcode */
val [1] = UPPER_WORD (optr->opcode); /* and the subopcode if it is a two-word instruction */
if (*cptr != '\0' /* if there is more to parse */
&& (SRGOP (optr->opcode) || ASGOP (optr->opcode))) { /* and the first opcode is SRG or ASG */
accumulator = (uint32) optr->op_bits; /* then accumulate the significant opcode bits */
gptr = get_glyph (cptr, gbuf, ','); /* parse the next opcode, if present */
if (ASGOP (optr->opcode)) /* if the initial opcode is ASG */
optr++; /* then point at the next table entry for parsing */
else { /* otherwise this is an SRG opcode */
if (optr->opcode & SRG1_DE_MASK) { /* if it is a shift or rotate instruction */
mptr = NULL; /* then it cannot be an ASG instruction */
optr = srg_uops; /* and parsing continues with the micro-ops */
}
else { /* otherwise it's a micro-op that could be ASG */
mptr = optr->mnemonic; /* so save the initial mnemonic pointer */
optr++; /* and start with the next micro-op */
}
status = parse_micro_ops (optr, gbuf, val, /* parse the SRG micro-ops */
&gptr, &accumulator);
if (status != SCPE_INCOMP) /* if the parse is complete */
return status; /* then return success or failure as appropriate */
optr = srg2_ops; /* the mnemonic is not a micro-op, so try a shift/rotate op */
status = parse_micro_ops (optr, gbuf, val, /* parse the SRG shift-rotate opcode */
NULL, &accumulator);
if (status == SCPE_OK && *gptr != '\0') /* if the opcode was found but there is more to parse */
return SCPE_ARG; /* then return failure as only one opcode is allowed */
else if (status != SCPE_INCOMP) /* otherwise if the parse is complete */
return status; /* then return success or failure as appropriate */
if (mptr == NULL) /* the mnemonic is not an SRG, and if it cannot be an ASG */
return SCPE_ARG; /* then fail with an invalid mnemonic */
else { /* otherwise attempt to reparse as ASG instructions */
val [0] = 0; /* clear the assembled opcode */
accumulator = 0; /* and the accumulated significant opcode bits */
strcpy (gbuf, mptr); /* restore the original mnemonic to the buffer */
gptr = cptr; /* and the original remainder-of-line pointer */
optr = asg_uops; /* set to search the ASG micro-ops table */
} /* and fall into the ASG parser */
}
status = parse_micro_ops (optr, gbuf, val, /* parse the ASG micro-ops */
&gptr, &accumulator);
if (status != SCPE_INCOMP) /* if the parse is complete */
return status; /* then return success or failure as appropriate */
}
else { /* otherwise, it's a single opcode */
op_type = optr->type; /* so get the type of the instruction operand(s) */
op_count = (uint32) op_props [op_type].count; /* get the number of operands */
consumption = (t_stat) - op_props [op_type].count; /* and set the initial number of words consumed */
op_address_set = op_props [op_type].address_set; /* get the address/data-selector bit set */
op_radix = (radix ? radix : cpu_dev.dradix); /* assume that the (only) operand is data */
if (optr->op_bits > D16_MASK) { /* if this is a two-word instruction */
op_start = 2; /* then the operands start after the second word */
op_count = op_count + 1; /* and extend for an extra word */
consumption = consumption - 1; /* and consume an additional word */
}
switch (op_type) { /* dispatch by the operand type */
/* no operand */
case opNone:
break; /* no parsing needed */
/* MRG page bit 10, offset 0000-1777 octal, indirect bit 15 */
case opMPOI:
cptr = get_glyph (cptr, gbuf, '\0'); /* get the next token */
if (gbuf [0] == 'C' && gbuf [1] == '\0') { /* if the "C" modifier was specified */
val [0] = val [0] | I_CP; /* then add the current-page flag */
cptr = get_glyph (cptr, gbuf, '\0'); /* get the address */
op_implicit = FALSE; /* and clear the implicit-page flag */
}
else if (gbuf [0] == 'Z' && gbuf [1] == '\0') { /* otherwise if the "Z" modifier was specified */
cptr = get_glyph (cptr, gbuf, '\0'); /* then get the address */
op_implicit = FALSE; /* and clear the implicit-page flag */
}
else /* otherwise neither modifier is present */
op_implicit = TRUE; /* so set the flag to allow implicit-page addressing */
op_value = parse_address (gbuf, &status); /* parse the address and optional indirection indicator */
if (status != SCPE_OK) /* if a parse error occurred */
return status; /* then return an invalid argument error */
if ((op_value & VAMASK) <= I_DISP) /* if a base-page address was given */
val [0] |= op_value; /* then merge the offset into the instruction */
else if (addr <= LA_MAX && op_implicit /* otherwise if an implicit-page address is allowed */
&& ((addr ^ op_value) & I_PAGENO) == 0) /* and the target is in the current page */
val [0] |= I_CP | op_value & (I_IA | I_DISP); /* then merge the offset with the current-page flag */
else /* otherwise the address cannot be reached */
return SCPE_ARG; /* from the current instruction's location */
break;
/* IOG select code range 00-77 octal, hold/clear bit 9 */
/* IOG optional select code range 00-77 octal, hold/clear bit 9 */
case opSCHC:
case opSCOHC:
op_flag = TRUE; /* set a flag to enable an optional ",C" */
/* fall into the opSC case */
/* IOG select code range 00-77 octal */
case opSC:
cptr = get_glyph (cptr, gbuf, (op_flag ? ',' : '\0')); /* get the next glyph */
op_value = get_uint (gbuf, op_radix, I_DEVMASK, &status); /* and parse the select code */
if (status == SCPE_OK) /* if the select code is good */
val [0] |= op_value; /* then merge it into the opcode */
else if (op_type == opSCOHC) /* otherwise if the select code is optional */
if (gbuf [0] == '\0') /* then if it is not supplied */
break; /* then use the base instruction value */
else if (gbuf [0] == 'C' && gbuf [1] == '\0') { /* otherwise if the "C" modifier was specified */
val [0] |= I_HC; /* then merge the H/C bit */
break; /* into the base instruction value */
}
else /* otherwise the select code is bad */
return SCPE_ARG; /* so report failure for a bad argument */
/* fall into opHC case */
/* IOG hold/clear bit 9 */
case opHC:
if (*cptr != '\0') /* if there is more */
if (op_type != opSC) { /* and it is expected */
cptr = get_glyph (cptr, gbuf, '\0'); /* then get the glyph */
if (gbuf [0] == 'C' && gbuf [1] == '\0') /* if the "C" modifier was specified */
val [0] |= I_HC; /* then merge the H/C bit */
else /* otherwise it's something else */
return SCPE_ARG; /* so report failure for a bad argument */
}
else /* otherwise it's not expected */
return SCPE_ARG; /* so report failure for a bad argument */
break;
/* EAU shift/rotate count range 1-16 */
case opShift:
op_radix = (radix ? radix : 10); /* shift counts default to decimal */
cptr = get_glyph (cptr, gbuf, '\0'); /* get the next glyph */
op_value = parse_value (gbuf, op_radix, 16, &status); /* and parse the shift count */
if (status != SCPE_OK || op_value == 0) /* if a parsing error occurred or the count is zero */
return SCPE_ARG; /* then report failure for a bad argument */
else if (op_value < 16) /* otherwise the count is good */
val [0] |= op_value; /* so merge it into the opcode (0 encodes 16) */
break;
/* IOP index negative offset range 1-16 */
/* IOP index positive offset range 0-15 */
/* IOP index offset range -16 to +15 (+16 bias) */
case opIOPON:
case opIOPOP:
case opIOPO:
op_radix = (radix ? radix : 10); /* index offsets default to decimal */
if (*cptr == '+') /* if there is a leading plus sign */
cptr++; /* then skip it */
else if (*cptr == '-') { /* otherwise if there is a leading minus sign */
op_flag = TRUE; /* then set the negative flag */
cptr++; /* and skip it */
}
cptr = get_glyph (cptr, gbuf, '\0'); /* get the next glyph */
op_value = parse_value (gbuf, op_radix, /* and parse the index offset with the appropriate range */
15 + op_flag, &status);
if (status != SCPE_OK) /* if a parsing error or value out of range occurred */
return SCPE_ARG; /* then report failure for a bad argument */
else if (op_flag) /* otherwise the offset is good; if it is negative */
val [0] = val [0] + 16 - op_value; /* then scale and merge the offset */
else /* otherwise it is positive */
val [0] = val [0] + op_value + 16; /* so shift to the positive opcode and merge the offset */
break;
/* UIG zero word, four (indirect) memory addresses */
/* UIG zero word, five (indirect) memory addresses */
/* UIG zero word, six (indirect) memory addresses */
/* UIG zero word, eight (indirect) memory addresses */
case opZA4:
case opZA5:
case opZA6:
case opZA8:
val [1] = 0; /* add the zero word */
op_start = 2; /* and bump the operand starting index */
break;
/* One memory address range 00000-77777 octal, zero word, indirect bit 15 */
case opMA1ZI:
val [2] = 0; /* add the zero word */
op_count = 1; /* and reduce the operand count to 1 */
break;
/* One to seven memory addresses range 00000-77777 octal, indirect bit 15 */
case opMA1I:
case opMA2I:
case opMA3I:
case opMA4I:
case opMA5I:
case opMA6I:
case opMA7I:
/* UIG one data value */
/* UIG two data values */
/* UIG one (indirect) memory address, one data value */
/* UIG two data values, one (indirect) memory address */
/* UIG one data value, five (indirect) memory addresses */
case opV1:
case opV2:
case opA1V1:
case opV2A1:
case opV1A5:
break; /* no special operand handling needed */
} /* end of the operand type dispatch */
if (op_count > 0) /* if one or more operands are required */
for (op_index = op_start; /* then parse and load them */
op_index <= op_count; /* in to the return array */
op_index++) {
cptr = get_glyph (cptr, gbuf, '\0'); /* get the next glyph */
if (op_address_set & 1) /* if this operand is an address */
op_value = parse_address (gbuf, &status); /* then parse it with an optional indirection indicator */
else /* otherwise it's a data value */
op_value = parse_value (gbuf, op_radix, /* so parse it as an unsigned number */
DV_UMAX, &status);
if (status != SCPE_OK) /* if a parse error occurred */
return status; /* then return an invalid argument error */
else /* otherwise */
val [op_index] = op_value; /* store the operand value */
op_address_set = op_address_set >> 1; /* shift the next type bit into place */
} /* and loop until all operands are parsed */
if (*cptr == '\0') /* if parsing is complete */
return consumption; /* then return the number of instruction words */
}
return SCPE_ARG; /* return an error for extraneous characters present */
}
/* Parse micro-ops.
This routine parses a set of micro-operations that form a single instruction.
It is called after a token has matched an entry in the SRG or ASG opcode
tables.
On entry, "optr" points at the opcode table entry after the first token
match, "gbuf" points at the next mnemonic token in the input buffer, "val"
points at the array of t_values that will hold the assembled instruction and
its operands, "gptr" points at the pointer that is set to the next character
to parse, and "accumulator" points at the variable that holds the accumulated
significant opcode bits for the instruction. On exit, the routine returns
SCPE_OK if the input buffer was parsed correctly, SCPE_ARG if conflicting
A/B-register selectors were encountered (e.g., "CLA,CMB"), or SCPE_INCOMP if
the opcode table was exhausted without consuming the input buffer. The
pointer "gptr" points at is updated to point at the first character after the
last token parsed, and the value pointed at by "accumulator" contains the
accumulated significant opcode bits for the set of parsed micro-ops. The
former is used by the caller to continue parsing, while the latter is used to
track the micro-ops that have been specified to ensure that each appears only
once.
The routine searches the supplied table for mnemonics that match the tokens
parsed from the input buffer. For each match, checks are made to ensure that
the same mnemonic has not been specified before and that opcodes that specify
an accumulator are consistent throughout the instruction. The opcode from
each matching entry is then logically ORed into the instruction word, and the
significant opcode bits are accumulated to provide a check for duplicates.
When the input buffer or opcode table is exhausted, or a check fails, the
routine exits with the appropriate status.
Implementation notes:
1. The "repeated operation" significant-bits test is necessary only to catch
two cases. One is the NOP,RAL and RAL,NOP case where NOP must be
specified alone and yet it decodes as an SRG instruction, which allows
micro-ops. The other is the CLA,CMA case where multiple encoded ASG
operations are specified. All other repeated operation cases (e.g.,
CLE,SLA,CLE) are prohibited by the single pass through a table arranged
in the required order (so the second CLE will not be found because SLA
comes after CLE in the table).
The first case could be handled by substituting an operand test for the
group test (e.g., optr->type == opSRG || optr->type == opASG and marking
NOP as opNone and all other SRG opcodes as opSRG, so that NOP will not be
detected as an SRG instruction) in "parse_instruction", placing the NOP
entry first, and starting the search with entry 2 in the SRG micro-ops
table.
The second case could be handled by treating the encoded operations
(CLx/CMx/CCx and CLE/CME/CCE) as indexed searches of separate primary
tables.
The added complexity of these alternates is not worth the effort, given
the relative simplicity of the "repeated operation" test.
2. The A/B-register selector state must be the same for all micro-ops in a
given instruction. That is, all micro-ops that use the A or B registers
must reference the same accumulator. The check is performed by logically
ANDing the accumulated and current significant opcode bits with the
AB_SELECT mask (to determine if the A/B-register select is significant
and has been set), and then with the exclusive-OR of the accumulated and
current micro-ops (to determine if the current A/B-register select is the
same as the prior A/B-register selects). If there is a discrepancy, then
the current micro-op does not use the same register as a previously
specified micro-op.
*/
static t_stat parse_micro_ops (const OP_ENTRY *optr, char *gbuf, t_value *val, CONST char **gptr, uint32 *accumulator)
{
while (optr->mnemonic != NULL) /* search the table until the NULL entry at the end */
if (strcmp (optr->mnemonic, gbuf) == 0) { /* if the mnemonic matches this entry */
if (*accumulator & optr->op_bits & ~(AB_SELECT | ASG)) /* then if this opcode has been entered before */
return SCPE_ARG; /* then fail with a repeated instruction error */
if (*accumulator & optr->op_bits & AB_SELECT /* if the A/B-register selector is significant */
& (optr->opcode ^ val [0])) /* and the new opcode disagrees with the prior ones */
return SCPE_ARG; /* then fail with an A/B inconsistent error */
val [0] |= optr->opcode; /* include the opcode */
*accumulator |= optr->op_bits; /* and accumulate the significant opcode bits */
if (gptr == NULL || **gptr == '\0') /* if the line is fully parsed or only one match allowed */
return SCPE_OK; /* then we are done */
else /* otherwise there is more */
*gptr = get_glyph (*gptr, gbuf, ','); /* so parse the next opcode */
}
else /* otherwise this entry does not match */
optr++; /* so point at the next one */
return SCPE_INCOMP;
}
/* Get a 16-bit word from a file.
This routine gets the next two bytes from the open file stream "fileref" and
returns a 16-bit word with the first byte in the upper half and the second
byte in the lower half. If a file error occurs (either a read error or an
unexpected end-of-file), the routine returns EOF.
*/
static int fgetword (FILE *fileref)
{
int c1, c2;
c1 = fgetc (fileref); /* get the first byte from the file */
if (c1 == EOF) /* if the read failed */
return c1; /* then return EOF */
c2 = fgetc (fileref); /* get the second byte from the file */
if (c2 == EOF) /* if the read failed */
return c2; /* then return EOF */
else /* otherwise */
return (int) TO_WORD (c1, c2); /* return the word formed by the two bytes */
}
/* Put a 16-bit word to a file.
This routine writes two bytes from the 16-bit word "data" into the open file
stream "fileref". The upper half of the word supplies the first byte, and
the lower half supplies the second byte. If a write error occurs, the
routine returns EOF. Otherwise, it returns the second byte written.
*/
static int fputword (int data, FILE *fileref)
{
int status;
status = fputc (UPPER_BYTE (data), fileref); /* write the first byte */
if (status != EOF) /* if the write succeeded */
status = fputc (LOWER_BYTE (data), fileref); /* then write the second byte */
return status; /* return the result of the write */
}