855 lines
32 KiB
C
855 lines
32 KiB
C
/* hp2100_sys.c: HP 2100 simulator interface
|
|
|
|
Copyright (c) 1993-2013, Robert M. Supnik
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a
|
|
copy of this software and associated documentation files (the "Software"),
|
|
to deal in the Software without restriction, including without limitation
|
|
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
and/or sell copies of the Software, and to permit persons to whom the
|
|
Software is furnished to do so, subject to the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be included in
|
|
all copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
Except as contained in this notice, the name of Robert M Supnik shall not be
|
|
used in advertising or otherwise to promote the sale, use or other dealings
|
|
in this Software without prior written authorization from Robert M Supnik.
|
|
|
|
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
|
|
*/
|
|
|
|
#include "hp2100_defs.h"
|
|
#include "hp2100_cpu.h"
|
|
#include <ctype.h>
|
|
|
|
|
|
extern DEVICE mp_dev;
|
|
extern DEVICE dma1_dev, dma2_dev;
|
|
extern DEVICE ptr_dev, ptp_dev;
|
|
extern DEVICE tty_dev, clk_dev;
|
|
extern DEVICE lps_dev;
|
|
extern DEVICE lpt_dev;
|
|
extern DEVICE baci_dev;
|
|
extern DEVICE mpx_dev;
|
|
extern DEVICE mtd_dev, mtc_dev;
|
|
extern DEVICE msd_dev, msc_dev;
|
|
extern DEVICE dpd_dev, dpc_dev;
|
|
extern DEVICE dqd_dev, dqc_dev;
|
|
extern DEVICE drd_dev, drc_dev;
|
|
extern DEVICE ds_dev;
|
|
extern DEVICE muxl_dev, muxu_dev, muxc_dev;
|
|
extern DEVICE ipli_dev, iplo_dev;
|
|
extern DEVICE pif_dev;
|
|
extern DEVICE da_dev, dc_dev;
|
|
|
|
/* SCP data structures and interface routines
|
|
|
|
sim_name simulator name string
|
|
sim_PC pointer to saved PC register descriptor
|
|
sim_emax maximum number of words for examine/deposit
|
|
sim_devices array of pointers to simulated devices
|
|
sim_stop_messages array of pointers to stop messages
|
|
sim_load binary loader
|
|
*/
|
|
|
|
char sim_name[] = "HP 2100";
|
|
|
|
char halt_msg[] = "HALT instruction xxxxxx";
|
|
|
|
REG *sim_PC = &cpu_reg[0];
|
|
|
|
int32 sim_emax = 3;
|
|
|
|
DEVICE *sim_devices[] = {
|
|
&cpu_dev,
|
|
&mp_dev,
|
|
&dma1_dev, &dma2_dev,
|
|
&ptr_dev,
|
|
&ptp_dev,
|
|
&tty_dev,
|
|
&clk_dev,
|
|
&lps_dev,
|
|
&lpt_dev,
|
|
&baci_dev,
|
|
&mpx_dev,
|
|
&dpd_dev, &dpc_dev,
|
|
&dqd_dev, &dqc_dev,
|
|
&drd_dev, &drc_dev,
|
|
&ds_dev,
|
|
&mtd_dev, &mtc_dev,
|
|
&msd_dev, &msc_dev,
|
|
&muxl_dev, &muxu_dev, &muxc_dev,
|
|
&ipli_dev, &iplo_dev,
|
|
&pif_dev,
|
|
&da_dev, &dc_dev,
|
|
NULL
|
|
};
|
|
|
|
const char *sim_stop_messages[] = {
|
|
"Unknown error",
|
|
"Unimplemented instruction",
|
|
"Non-existent I/O device",
|
|
halt_msg,
|
|
"Breakpoint",
|
|
"Indirect address loop",
|
|
"Indirect address interrupt (should not happen!)",
|
|
"No connection on interprocessor link",
|
|
"Device/unit offline",
|
|
"Device/unit powered off"
|
|
};
|
|
|
|
/* Binary loader
|
|
|
|
The binary loader consists of blocks preceded and trailed by zero frames.
|
|
A block consists of 16b words (punched big endian), as follows:
|
|
|
|
count'xxx
|
|
origin
|
|
word 0
|
|
:
|
|
word count-1
|
|
checksum
|
|
|
|
The checksum includes the origin but not the count.
|
|
*/
|
|
|
|
int32 fgetw (FILE *fileref)
|
|
{
|
|
int c1, c2;
|
|
|
|
if ((c1 = fgetc (fileref)) == EOF) return -1;
|
|
if ((c2 = fgetc (fileref)) == EOF) return -1;
|
|
return ((c1 & 0377) << 8) | (c2 & 0377);
|
|
}
|
|
|
|
t_stat sim_load (FILE *fileref, char *cptr, char *fnam, int flag)
|
|
{
|
|
int32 origin, csum, zerocnt, count, word, i;
|
|
|
|
if ((*cptr != 0) || (flag != 0)) return SCPE_ARG;
|
|
for (zerocnt = 1;; zerocnt = -10) { /* block loop */
|
|
for (;; zerocnt++) { /* skip 0's */
|
|
if ((count = fgetc (fileref)) == EOF) return SCPE_OK;
|
|
else if (count) break;
|
|
else if (zerocnt == 0) return SCPE_OK;
|
|
}
|
|
if (fgetc (fileref) == EOF) return SCPE_FMT;
|
|
if ((origin = fgetw (fileref)) < 0) return SCPE_FMT;
|
|
csum = origin; /* seed checksum */
|
|
for (i = 0; i < count; i++) { /* get data words */
|
|
if ((word = fgetw (fileref)) < 0) return SCPE_FMT;
|
|
WritePW (origin, word);
|
|
origin = origin + 1;
|
|
csum = csum + word;
|
|
}
|
|
if ((word = fgetw (fileref)) < 0) return SCPE_FMT;
|
|
if ((word ^ csum) & DMASK) return SCPE_CSUM;
|
|
}
|
|
}
|
|
|
|
/* Symbol tables */
|
|
|
|
#define I_V_FL 16 /* flag start */
|
|
#define I_M_FL 017 /* flag mask */
|
|
#define I_V_NPN 0 /* no operand */
|
|
#define I_V_NPC 1 /* no operand + C */
|
|
#define I_V_MRF 2 /* mem ref */
|
|
#define I_V_ASH 3 /* alter/skip, shift */
|
|
#define I_V_ESH 4 /* extended shift */
|
|
#define I_V_EMR 5 /* extended mem ref */
|
|
#define I_V_IO1 6 /* I/O + HC */
|
|
#define I_V_IO2 7 /* I/O only */
|
|
#define I_V_EGZ 010 /* ext grp, 1 op + 0 */
|
|
#define I_V_EG2 011 /* ext grp, 2 op */
|
|
#define I_V_ALT 012 /* alternate use instr */
|
|
#define I_NPN (I_V_NPN << I_V_FL)
|
|
#define I_NPC (I_V_NPC << I_V_FL)
|
|
#define I_MRF (I_V_MRF << I_V_FL)
|
|
#define I_ASH (I_V_ASH << I_V_FL)
|
|
#define I_ESH (I_V_ESH << I_V_FL)
|
|
#define I_EMR (I_V_EMR << I_V_FL)
|
|
#define I_IO1 (I_V_IO1 << I_V_FL)
|
|
#define I_IO2 (I_V_IO2 << I_V_FL)
|
|
#define I_EGZ (I_V_EGZ << I_V_FL)
|
|
#define I_EG2 (I_V_EG2 << I_V_FL)
|
|
#define I_ALT (I_V_ALT << I_V_FL)
|
|
|
|
static const int32 masks[] = {
|
|
0177777, 0176777, 0074000, 0170000,
|
|
0177760, 0177777, 0176700, 0177700,
|
|
0177777, 0177777, 0177777
|
|
};
|
|
|
|
static const char *opcode[] = {
|
|
|
|
/* These mnemonics are used by debug printouts, so put them first. */
|
|
|
|
"$LIBR", "$LIBX", ".TICK", ".TNAM", /* RTE-6/VM OS firmware */
|
|
".STIO", ".FNW", ".IRT", ".LLS",
|
|
".SIP", ".YLD", ".CPM", ".ETEQ",
|
|
".ENTN", "$OTST", ".ENTC", ".DSPI",
|
|
"$DCPC", "$MPV", "$DEV", "$TBG", /* alternates for dual-use */
|
|
|
|
".PMAP", "$LOC", "$VTST",/* --- */ /* RTE-6/VM VMA firmware */
|
|
/* --- --- --- --- */
|
|
".IMAP", ".IMAR", ".JMAP", ".JMAR",
|
|
".LPXR", ".LPX", ".LBPR", ".LBP",
|
|
|
|
".EMIO", "MMAP", "$ETST",/* --- */ /* RTE-IV EMA firmware */
|
|
/* --- --- --- --- */
|
|
/* --- --- --- --- */
|
|
/* --- --- --- */ ".EMAP",
|
|
|
|
/* Regular mnemonics. */
|
|
|
|
"NOP", "NOP", "AND", "JSB",
|
|
"XOR", "JMP", "IOR", "ISZ",
|
|
"ADA", "ADB" ,"CPA", "CPB",
|
|
"LDA", "LDB", "STA", "STB",
|
|
"DIAG", "ASL", "LSL", "TIMER",
|
|
"RRL", "ASR", "LSR", "RRR",
|
|
"MPY", "DIV", "DLD", "DST",
|
|
"FAD", "FSB", "FMP", "FDV",
|
|
"FIX", "FLT",
|
|
"STO", "CLO", "SOC", "SOS",
|
|
"HLT", "STF", "CLF",
|
|
"SFC", "SFS", "MIA", "MIB",
|
|
"LIA", "LIB", "OTA", "OTB",
|
|
"STC", "CLC",
|
|
"SYA", "USA", "PAA", "PBA",
|
|
"XMA",
|
|
"XLA", "XSA", "XCA", "LFA",
|
|
"RSA", "RVA",
|
|
"MBI", "MBF",
|
|
"MBW", "MWI", "MWF", "MWW",
|
|
"SYB", "USB", "PAB", "PBB",
|
|
"SSM", "JRS",
|
|
"XMM", "XMS", "XMB",
|
|
"XLB", "XSB", "XCB", "LFB",
|
|
"RSB", "RVB", "DJP", "DJS",
|
|
"SJP", "SJS", "UJP", "UJS",
|
|
"SAX", "SBX", "CAX", "CBX",
|
|
"LAX", "LBX", "STX",
|
|
"CXA", "CXB", "LDX",
|
|
"ADX", "XAX", "XBX",
|
|
"SAY", "SBY", "CAY", "CBY",
|
|
"LAY", "LBY", "STY",
|
|
"CYA", "CYB", "LDY",
|
|
"ADY", "XAY", "XBY",
|
|
"ISX", "DSX", "JLY", "LBT",
|
|
"SBT", "MBT", "CBT", "SBT",
|
|
"ISY", "DSY", "JPY", "SBS",
|
|
"CBS", "TBS", "CMW", "MVW",
|
|
NULL, /* decode only */
|
|
NULL
|
|
};
|
|
|
|
static const int32 opc_val[] = {
|
|
0105340+I_NPN, 0105341+I_NPN, 0105342+I_NPN, 0105343+I_NPN, /* RTE-6/VM OS */
|
|
0105344+I_NPN, 0105345+I_NPN, 0105346+I_NPN, 0105347+I_NPN,
|
|
0105350+I_NPN, 0105351+I_NPN, 0105352+I_NPN, 0105353+I_NPN,
|
|
0105354+I_ALT, 0105355+I_ALT, 0105356+I_ALT, 0105357+I_ALT,
|
|
0105354+I_NPN, 0105355+I_NPN, 0105356+I_NPN, 0105357+I_NPN, /* alternates */
|
|
|
|
0105240+I_ALT, 0105241+I_ALT, 0105242+I_ALT, /* --- */ /* RTE-6/VM VMA */
|
|
/* --- --- --- --- */
|
|
0105250+I_NPN, 0105251+I_NPN, 0105252+I_NPN, 0105253+I_NPN,
|
|
0105254+I_NPN, 0105255+I_NPN, 0105256+I_NPN, 0105257+I_ALT,
|
|
|
|
0105240+I_NPN, 0105241+I_NPN, 0105242+I_NPN, /* RTE-IV EMA */
|
|
/* --- --- --- --- */
|
|
/* --- --- --- --- */
|
|
/* --- --- --- */ 0105257+I_NPN,
|
|
|
|
0000000+I_NPN, 0002000+I_NPN, 0010000+I_MRF, 0014000+I_MRF,
|
|
0020000+I_MRF, 0024000+I_MRF, 0030000+I_MRF, 0034000+I_MRF,
|
|
0040000+I_MRF, 0044000+I_MRF, 0050000+I_MRF, 0054000+I_MRF,
|
|
0060000+I_MRF, 0064000+I_MRF, 0070000+I_MRF, 0074000+I_MRF,
|
|
0100000+I_NPN, 0100020+I_ESH, 0100040+I_ESH, 0100060+I_NPN,
|
|
0100100+I_ESH, 0101020+I_ESH, 0101040+I_ESH, 0101100+I_ESH,
|
|
0100200+I_EMR, 0100400+I_EMR, 0104200+I_EMR, 0104400+I_EMR,
|
|
0105000+I_EMR, 0105020+I_EMR, 0105040+I_EMR, 0105060+I_EMR,
|
|
0105100+I_NPN, 0105120+I_NPN,
|
|
0102101+I_NPN, 0103101+I_NPN, 0102201+I_NPC, 0102301+I_NPC,
|
|
0102000+I_IO1, 0102100+I_IO2, 0103100+I_IO2,
|
|
0102200+I_IO1, 0102300+I_IO1, 0102400+I_IO1, 0106400+I_IO1,
|
|
0102500+I_IO1, 0106500+I_IO1, 0102600+I_IO1, 0106600+I_IO1,
|
|
0102700+I_IO1, 0106700+I_IO1,
|
|
0101710+I_NPN, 0101711+I_NPN, 0101712+I_NPN, 0101713+I_NPN,
|
|
0101722+I_NPN,
|
|
0101724+I_EMR, 0101725+I_EMR, 0101726+I_EMR, 0101727+I_NPN,
|
|
0101730+I_NPN, 0101731+I_NPN,
|
|
0105702+I_NPN, 0105703+I_NPN,
|
|
0105704+I_NPN, 0105705+I_NPN, 0105706+I_NPN, 0105707+I_NPN,
|
|
0105710+I_NPN, 0105711+I_NPN, 0105712+I_NPN, 0105713+I_NPN,
|
|
0105714+I_EMR, 0105715+I_EG2,
|
|
0105720+I_NPN, 0105721+I_NPN, 0105722+I_NPN,
|
|
0105724+I_EMR, 0105725+I_EMR, 0105726+I_EMR, 0105727+I_NPN,
|
|
0105730+I_NPN, 0105731+I_NPN, 0105732+I_EMR, 0105733+I_EMR,
|
|
0105734+I_EMR, 0105735+I_EMR, 0105736+I_EMR, 0105737+I_EMR,
|
|
0101740+I_EMR, 0105740+I_EMR, 0101741+I_NPN, 0105741+I_NPN,
|
|
0101742+I_EMR, 0105742+I_EMR, 0105743+I_EMR,
|
|
0101744+I_NPN, 0105744+I_NPN, 0105745+I_EMR,
|
|
0105746+I_EMR, 0101747+I_NPN, 0105747+I_NPN,
|
|
0101750+I_EMR, 0105750+I_EMR, 0101751+I_NPN, 0105751+I_NPN,
|
|
0101752+I_EMR, 0105752+I_EMR, 0105753+I_EMR,
|
|
0101754+I_NPN, 0105754+I_NPN, 0105755+I_EMR,
|
|
0105756+I_EMR, 0101757+I_NPN, 0105757+I_NPN,
|
|
0105760+I_NPN, 0105761+I_NPN, 0105762+I_EMR, 0105763+I_NPN,
|
|
0105764+I_NPN, 0105765+I_EGZ, 0105766+I_EGZ, 0105767+I_NPN,
|
|
0105770+I_NPN, 0105771+I_NPN, 0105772+I_EMR, 0105773+I_EG2,
|
|
0105774+I_EG2, 0105775+I_EG2, 0105776+I_EGZ, 0105777+I_EGZ,
|
|
0000000+I_ASH, /* decode only */
|
|
-1
|
|
};
|
|
|
|
/* Decode tables for shift and alter/skip groups */
|
|
|
|
static const char *stab[] = {
|
|
"ALS", "ARS", "RAL", "RAR", "ALR", "ERA", "ELA", "ALF",
|
|
"BLS", "BRS", "RBL", "RBR", "BLR", "ERB", "ELB", "BLF",
|
|
"CLA", "CMA", "CCA", "CLB", "CMB", "CCB",
|
|
"SEZ", "CLE", "CLE", "CME", "CCE",
|
|
"SSA", "SSB", "SLA", "SLB",
|
|
"ALS", "ARS", "RAL", "RAR", "ALR", "ERA", "ELA", "ALF",
|
|
"BLS", "BRS", "RBL", "RBR", "BLR", "ERB", "ELB", "BLF",
|
|
"INA", "INB", "SZA", "SZB", "RSS",
|
|
NULL
|
|
};
|
|
|
|
static const int32 mtab[] = {
|
|
0007700, 0007700, 0007700, 0007700, 0007700, 0007700, 0007700, 0007700,
|
|
0007700, 0007700, 0007700, 0007700, 0007700, 0007700, 0007700, 0007700,
|
|
0007400, 0007400, 0007400, 0007400, 0007400, 0007400,
|
|
0002040, 0002040, 0002300, 0002300, 0002300,
|
|
0006020, 0006020, 0004010, 0004010,
|
|
0006027, 0006027, 0006027, 0006027, 0006027, 0006027, 0006027, 0006027,
|
|
0006027, 0006027, 0006027, 0006027, 0006027, 0006027, 0006027, 0006027,
|
|
0006004, 0006004, 0006002, 0006002, 0002001,
|
|
0
|
|
};
|
|
|
|
static const int32 vtab[] = {
|
|
0001000, 0001100, 0001200, 0001300, 0001400, 0001500, 0001600, 0001700,
|
|
0005000, 0005100, 0005200, 0005300, 0005400, 0005500, 0005600, 0005700,
|
|
0002400, 0003000, 0003400, 0006400, 0007000, 0007400,
|
|
0002040, 0000040, 0002100, 0002200, 0002300,
|
|
0002020, 0006020, 0000010, 0004010,
|
|
0000020, 0000021, 0000022, 0000023, 0000024, 0000025, 0000026, 0000027,
|
|
0004020, 0004021, 0004022, 0004023, 0004024, 0004025, 0004026, 0004027,
|
|
0002004, 0006004, 0002002, 0006002, 0002001,
|
|
-1
|
|
};
|
|
|
|
/* Symbolic decode
|
|
|
|
Inputs:
|
|
*of = output stream
|
|
addr = current PC
|
|
*val = pointer to data
|
|
*uptr = pointer to unit
|
|
sw = switches
|
|
Outputs:
|
|
return = status code
|
|
*/
|
|
|
|
#define FMTASC(x) ((x) < 040)? "<%03o>": "%c", (x)
|
|
|
|
t_stat fprint_sym (FILE *of, t_addr addr, t_value *val,
|
|
UNIT *uptr, int32 sw)
|
|
{
|
|
int32 cflag, cm, i, j, inst, disp;
|
|
uint32 irq;
|
|
|
|
cflag = (uptr == NULL) || (uptr == &cpu_unit);
|
|
inst = val[0];
|
|
if (sw & SWMASK ('A')) { /* ASCII? */
|
|
if (inst > 0377) return SCPE_ARG;
|
|
fprintf (of, FMTASC (inst & 0177));
|
|
return SCPE_OK;
|
|
}
|
|
if (sw & SWMASK ('C')) { /* characters? */
|
|
fprintf (of, FMTASC ((inst >> 8) & 0177));
|
|
fprintf (of, FMTASC (inst & 0177));
|
|
return SCPE_OK;
|
|
}
|
|
if (!(sw & SWMASK ('M'))) return SCPE_ARG;
|
|
|
|
/* If we are being called as a result of a VM stop to display the next
|
|
instruction to be executed, check to see if an interrupt is pending and not
|
|
deferred. If so, then display the interrupt source and the trap cell
|
|
instruction as the instruction to be executed, rather than the instruction at
|
|
the current PC.
|
|
*/
|
|
|
|
if (sw & SIM_SW_STOP) { /* simulator stop? */
|
|
irq = calc_int (); /* check interrupt */
|
|
|
|
if (irq && (!ion_defer || !calc_defer())) { /* pending interrupt and not deferred? */
|
|
addr = irq; /* set display address to trap cell */
|
|
inst = val[0] = ReadIO (irq, SMAP); /* load trap cell instruction */
|
|
val[1] = ReadIO (irq + 1, SMAP); /* might be multi-word */
|
|
val[2] = ReadIO (irq + 2, SMAP); /* although it's unlikely */
|
|
fprintf (of, "IAK %2o: ", irq); /* report acknowledged interrupt */
|
|
}
|
|
}
|
|
|
|
for (i = 0; opc_val[i] >= 0; i++) { /* loop thru ops */
|
|
j = (opc_val[i] >> I_V_FL) & I_M_FL; /* get class */
|
|
if ((opc_val[i] & DMASK) == (inst & masks[j])) { /* match? */
|
|
switch (j) { /* case on class */
|
|
|
|
case I_V_NPN: /* no operands */
|
|
fprintf (of, "%s", opcode[i]); /* opcode */
|
|
break;
|
|
|
|
case I_V_NPC: /* no operands + C */
|
|
fprintf (of, "%s", opcode[i]);
|
|
if (inst & I_HC) fprintf (of, " C");
|
|
break;
|
|
|
|
case I_V_MRF: /* mem ref */
|
|
disp = inst & I_DISP; /* displacement */
|
|
fprintf (of, "%s ", opcode[i]); /* opcode */
|
|
if (inst & I_CP) { /* current page? */
|
|
if (cflag) fprintf (of, "%-o", (addr & I_PAGENO) | disp);
|
|
else fprintf (of, "C %-o", disp);
|
|
}
|
|
else fprintf (of, "%-o", disp); /* page zero */
|
|
if (inst & I_IA) fprintf (of, ",I");
|
|
break;
|
|
|
|
case I_V_ASH: /* shift, alter-skip */
|
|
cm = FALSE;
|
|
for (i = 0; mtab[i] != 0; i++) {
|
|
if ((inst & mtab[i]) == vtab[i]) {
|
|
inst = inst & ~(vtab[i] & 01777);
|
|
if (cm) fprintf (of, ",");
|
|
cm = TRUE;
|
|
fprintf (of, "%s", stab[i]);
|
|
}
|
|
}
|
|
if (!cm) return SCPE_ARG; /* nothing decoded? */
|
|
break;
|
|
|
|
case I_V_ESH: /* extended shift */
|
|
disp = inst & 017; /* shift count */
|
|
if (disp == 0) disp = 16;
|
|
fprintf (of, "%s %d", opcode[i], disp);
|
|
break;
|
|
|
|
case I_V_EMR: /* extended mem ref */
|
|
fprintf (of, "%s %-o", opcode[i], val[1] & VAMASK);
|
|
if (val[1] & I_IA) fprintf (of, ",I");
|
|
return -1; /* extra word */
|
|
|
|
case I_V_IO1: /* IOT with H/C */
|
|
fprintf (of, "%s %-o", opcode[i], inst & I_DEVMASK);
|
|
if (inst & I_HC) fprintf (of, ",C");
|
|
break;
|
|
|
|
case I_V_IO2: /* IOT */
|
|
fprintf (of, "%s %-o", opcode[i], inst & I_DEVMASK);
|
|
break;
|
|
|
|
case I_V_EGZ: /* ext grp 1 op + 0 */
|
|
fprintf (of, "%s %-o", opcode[i], val[1] & VAMASK);
|
|
if (val[1] & I_IA) fprintf (of, ",I");
|
|
return -2; /* extra words */
|
|
|
|
case I_V_EG2: /* ext grp 2 op */
|
|
fprintf (of, "%s %-o", opcode[i], val[1] & VAMASK);
|
|
if (val[1] & I_IA) fprintf (of, ",I");
|
|
fprintf (of, " %-o", val[2] & VAMASK);
|
|
if (val[2] & I_IA) fprintf (of, ",I");
|
|
return -2; /* extra words */
|
|
|
|
case I_V_ALT: /* alternate use instr */
|
|
if ((inst >= 0105354) &&
|
|
(inst <= 0105357) && /* RTE-6/VM OS range? */
|
|
(addr >= 2) &&
|
|
(addr <= 077)) /* in trap cell? */
|
|
continue; /* use alternate mnemonic */
|
|
|
|
else if ((inst >= 0105240) && /* RTE-6/VM VMA range? */
|
|
(inst <= 0105257) &&
|
|
(cpu_unit.flags & UNIT_EMA)) /* EMA enabled? */
|
|
continue; /* use EMA mnemonics */
|
|
|
|
else
|
|
fprintf (of, "%s", opcode[i]); /* print opcode */
|
|
break;
|
|
}
|
|
|
|
return SCPE_OK;
|
|
} /* end if */
|
|
} /* end for */
|
|
return SCPE_ARG;
|
|
}
|
|
|
|
/* Get address with indirection
|
|
|
|
Inputs:
|
|
*cptr = pointer to input string
|
|
Outputs:
|
|
val = address
|
|
-1 if error
|
|
*/
|
|
|
|
int32 get_addr (char *cptr)
|
|
{
|
|
int32 d;
|
|
t_stat r;
|
|
char gbuf[CBUFSIZE];
|
|
|
|
cptr = get_glyph (cptr, gbuf, ','); /* get next field */
|
|
d = get_uint (gbuf, 8, VAMASK, &r); /* construe as addr */
|
|
if (r != SCPE_OK) return -1;
|
|
if (*cptr != 0) { /* more? */
|
|
cptr = get_glyph (cptr, gbuf, 0); /* look for indirect */
|
|
if (*cptr != 0) return -1; /* should be done */
|
|
if (strcmp (gbuf, "I")) return -1; /* I? */
|
|
d = d | I_IA;
|
|
}
|
|
return d;
|
|
}
|
|
|
|
/* Symbolic input
|
|
|
|
Inputs:
|
|
*iptr = pointer to input string
|
|
addr = current PC
|
|
*uptr = pointer to unit
|
|
*val = pointer to output values
|
|
sw = switches
|
|
Outputs:
|
|
status = error status
|
|
*/
|
|
|
|
t_stat parse_sym (char *iptr, t_addr addr, UNIT *uptr, t_value *val, int32 sw)
|
|
{
|
|
int32 cflag, d, i, j, k, clef, tbits;
|
|
t_stat r, ret;
|
|
char *cptr, gbuf[CBUFSIZE];
|
|
|
|
cflag = (uptr == NULL) || (uptr == &cpu_unit);
|
|
while (isspace (*iptr)) iptr++; /* absorb spaces */
|
|
if ((sw & SWMASK ('A')) || ((*iptr == '\'') && iptr++)) { /* ASCII char? */
|
|
if (iptr[0] == 0) return SCPE_ARG; /* must have 1 char */
|
|
val[0] = (t_value) iptr[0] & 0177;
|
|
return SCPE_OK;
|
|
}
|
|
if ((sw & SWMASK ('C')) || ((*iptr == '"') && iptr++)) { /* char string? */
|
|
if (iptr[0] == 0) return SCPE_ARG; /* must have 1 char */
|
|
val[0] = (((t_value) iptr[0] & 0177) << 8) |
|
|
((t_value) iptr[1] & 0177);
|
|
return SCPE_OK;
|
|
}
|
|
|
|
ret = SCPE_OK;
|
|
cptr = get_glyph (iptr, gbuf, 0); /* get opcode */
|
|
for (i = 0; (opcode[i] != NULL) && (strcmp (opcode[i], gbuf) != 0) ; i++) ;
|
|
if (opcode[i]) { /* found opcode? */
|
|
val[0] = opc_val[i] & DMASK; /* get value */
|
|
j = (opc_val[i] >> I_V_FL) & I_M_FL; /* get class */
|
|
switch (j) { /* case on class */
|
|
|
|
case I_V_NPN: /* no operand */
|
|
break;
|
|
|
|
case I_V_NPC: /* no operand + C */
|
|
if (*cptr != 0) {
|
|
cptr = get_glyph (cptr, gbuf, 0);
|
|
if (strcmp (gbuf, "C")) return SCPE_ARG;
|
|
val[0] = val[0] | I_HC;
|
|
}
|
|
break;
|
|
|
|
case I_V_MRF: /* mem ref */
|
|
cptr = get_glyph (cptr, gbuf, 0); /* get next field */
|
|
if ((k = (strcmp (gbuf, "C") == 0))) { /* C specified? */
|
|
val[0] = val[0] | I_CP;
|
|
cptr = get_glyph (cptr, gbuf, 0);
|
|
}
|
|
else if ((k = (strcmp (gbuf, "Z") == 0))) { /* Z specified? */
|
|
cptr = get_glyph (cptr, gbuf, ',');
|
|
}
|
|
if ((d = get_addr (gbuf)) < 0) return SCPE_ARG;
|
|
if ((d & VAMASK) <= I_DISP) val[0] = val[0] | d;
|
|
else if (cflag && !k && (((addr ^ d) & I_PAGENO) == 0))
|
|
val[0] = val[0] | (d & (I_IA | I_DISP)) | I_CP;
|
|
else return SCPE_ARG;
|
|
break;
|
|
|
|
case I_V_ESH: /* extended shift */
|
|
cptr = get_glyph (cptr, gbuf, 0);
|
|
d = get_uint (gbuf, 10, 16, &r);
|
|
if ((r != SCPE_OK) || (d == 0)) return SCPE_ARG;
|
|
val[0] = val[0] | (d & 017);
|
|
break;
|
|
|
|
case I_V_EMR: /* extended mem ref */
|
|
cptr = get_glyph (cptr, gbuf, 0); /* get next field */
|
|
if ((d = get_addr (gbuf)) < 0) return SCPE_ARG;
|
|
val[1] = d;
|
|
ret = -1;
|
|
break;
|
|
|
|
case I_V_IO1: /* IOT + optional C */
|
|
cptr = get_glyph (cptr, gbuf, ','); /* get device */
|
|
d = get_uint (gbuf, 8, I_DEVMASK, &r);
|
|
if (r != SCPE_OK) return SCPE_ARG;
|
|
val[0] = val[0] | d;
|
|
if (*cptr != 0) {
|
|
cptr = get_glyph (cptr, gbuf, 0);
|
|
if (strcmp (gbuf, "C")) return SCPE_ARG;
|
|
val[0] = val[0] | I_HC;
|
|
}
|
|
break;
|
|
|
|
case I_V_IO2: /* IOT */
|
|
cptr = get_glyph (cptr, gbuf, 0); /* get device */
|
|
d = get_uint (gbuf, 8, I_DEVMASK, &r);
|
|
if (r != SCPE_OK) return SCPE_ARG;
|
|
val[0] = val[0] | d;
|
|
break;
|
|
|
|
case I_V_EGZ: /* ext grp 1 op + 0 */
|
|
cptr = get_glyph (cptr, gbuf, 0); /* get next field */
|
|
if ((d = get_addr (gbuf)) < 0) return SCPE_ARG;
|
|
val[1] = d;
|
|
val[2] = 0;
|
|
ret = -2;
|
|
break;
|
|
|
|
case I_V_EG2: /* ext grp 2 op */
|
|
cptr = get_glyph (cptr, gbuf, 0); /* get next field */
|
|
if ((d = get_addr (gbuf)) < 0) return SCPE_ARG;
|
|
cptr = get_glyph (cptr, gbuf, 0); /* get next field */
|
|
if ((k = get_addr (gbuf)) < 0) return SCPE_ARG;
|
|
val[1] = d;
|
|
val[2] = k;
|
|
ret = -2;
|
|
break;
|
|
} /* end case */
|
|
|
|
if (*cptr != 0) return SCPE_ARG; /* junk at end? */
|
|
return ret;
|
|
} /* end if opcode */
|
|
|
|
/* Shift or alter-skip
|
|
|
|
Each opcode is matched by a mask, specifiying the bits affected, and
|
|
the value, specifying the value. As opcodes are processed, the mask
|
|
values are used to specify which fields have already been filled in.
|
|
|
|
The mask has two subfields, the type bits (A/B and A/S), and the field
|
|
bits. The type bits, once specified by any instruction, must be
|
|
consistent in all other instructions. The mask bits assure that no
|
|
field is filled in twice.
|
|
|
|
Two special cases:
|
|
|
|
1. The dual shift field in shift requires checking how much of the
|
|
target word has been filled in before assigning the shift value.
|
|
To implement this, shifts are listed twice is the decode table.
|
|
If the current subopcode is a shift in the first part of the table
|
|
(entries 0..15), and CLE has been seen or the first shift field is
|
|
filled in, the code forces a mismatch. The glyph will match in
|
|
the second part of the table.
|
|
|
|
2. CLE processing must be deferred until the instruction can be
|
|
classified as shift or alter-skip, since it has two different
|
|
bit values in the two classes. To implement this, CLE seen is
|
|
recorded as a flag and processed after all other subopcodes.
|
|
*/
|
|
|
|
clef = FALSE;
|
|
tbits = 0;
|
|
val[0] = 0;
|
|
for (cptr = get_glyph (iptr, gbuf, ','); gbuf[0] != 0;
|
|
cptr = get_glyph (cptr, gbuf, ',')) { /* loop thru glyphs */
|
|
if (strcmp (gbuf, "CLE") == 0) { /* CLE? */
|
|
if (clef) return SCPE_ARG; /* already seen? */
|
|
clef = TRUE; /* set flag */
|
|
continue;
|
|
}
|
|
for (i = 0; stab[i] != NULL; i++) { /* find subopcode */
|
|
if ((strcmp (gbuf, stab[i]) == 0) &&
|
|
((i >= 16) || (!clef && ((val[0] & 001710) == 0)))) break;
|
|
}
|
|
if (stab[i] == NULL) return SCPE_ARG;
|
|
if (tbits & mtab[i] & (I_AB | I_ASKP) & (vtab[i] ^ val[0]))
|
|
return SCPE_ARG;
|
|
if (tbits & mtab[i] & ~(I_AB | I_ASKP)) return SCPE_ARG;
|
|
tbits = tbits | mtab[i]; /* fill type+mask */
|
|
val[0] = val[0] | vtab[i]; /* fill value */
|
|
}
|
|
if (clef) { /* CLE seen? */
|
|
if (val[0] & I_ASKP) { /* alter-skip? */
|
|
if (tbits & 0100) return SCPE_ARG; /* already filled in? */
|
|
else val[0] = val[0] | 0100;
|
|
}
|
|
else val[0] = val[0] | 040; /* fill in shift */
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
/* Format a character into a printable string.
|
|
|
|
Control characters are translated to readable strings. Printable characters
|
|
retain their original form but are enclosed in single quotes. Characters
|
|
outside of the ASCII range are represented as escaped octal values.
|
|
*/
|
|
|
|
const char *fmt_char (uint8 ch)
|
|
{
|
|
static const char *const ctl [] = { "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 rep [5];
|
|
|
|
if (ch <= '\037') /* ASCII control character? */
|
|
return ctl [ch]; /* return string representation */
|
|
|
|
else if (ch == '\177') /* ASCII delete? */
|
|
return "DEL"; /* return string representation */
|
|
|
|
else if (ch > '\177') { /* beyond printable range? */
|
|
sprintf (rep, "\\%03o", ch); /* format value */
|
|
return rep; /* return escaped octal code */
|
|
}
|
|
|
|
else { /* printable character */
|
|
rep [0] = '\''; /* form string */
|
|
rep [1] = ch; /* containing character */
|
|
rep [2] = '\'';
|
|
rep [3] = '\0';
|
|
return rep; /* return quoted character */
|
|
}
|
|
}
|
|
|
|
|
|
/* Set select code */
|
|
|
|
t_stat hp_setsc (UNIT *uptr, int32 num, char *cptr, void *desc)
|
|
{
|
|
DEVICE *dptr = (DEVICE *) desc;
|
|
DIB *dibptr;
|
|
int32 i, newdev;
|
|
t_stat r;
|
|
|
|
if (cptr == NULL)
|
|
return SCPE_ARG;
|
|
|
|
if ((desc == NULL) || (num > 1))
|
|
return SCPE_IERR;
|
|
|
|
dibptr = (DIB *) dptr->ctxt;
|
|
|
|
if (dibptr == NULL)
|
|
return SCPE_IERR;
|
|
|
|
newdev = get_uint (cptr, 8, I_DEVMASK - num, &r);
|
|
|
|
if (r != SCPE_OK)
|
|
return r;
|
|
|
|
if (newdev < VARDEV)
|
|
return SCPE_ARG;
|
|
|
|
for (i = 0; i <= num; i++, dibptr++)
|
|
dibptr->select_code = newdev + i;
|
|
|
|
return SCPE_OK;
|
|
}
|
|
|
|
|
|
/* Show select code */
|
|
|
|
t_stat hp_showsc (FILE *st, UNIT *uptr, int32 num, void *desc)
|
|
{
|
|
DEVICE *dptr = (DEVICE *) desc;
|
|
DIB *dibptr;
|
|
int32 i;
|
|
|
|
if ((desc == NULL) || (num > 1))
|
|
return SCPE_IERR;
|
|
|
|
dibptr = (DIB *) dptr->ctxt;
|
|
|
|
if (dibptr == NULL)
|
|
return SCPE_IERR;
|
|
|
|
fprintf (st, "select code=%o", dibptr->select_code);
|
|
|
|
for (i = 1; i <= num; i++)
|
|
fprintf (st, "/%o", dibptr->select_code + i);
|
|
|
|
return SCPE_OK;
|
|
}
|
|
|
|
|
|
/* Set device number */
|
|
|
|
t_stat hp_setdev (UNIT *uptr, int32 num, char *cptr, void *desc)
|
|
{
|
|
return hp_setsc (uptr, num, cptr, desc);
|
|
}
|
|
|
|
|
|
/* Show device number */
|
|
|
|
t_stat hp_showdev (FILE *st, UNIT *uptr, int32 num, void *desc)
|
|
{
|
|
t_stat result;
|
|
|
|
result = hp_showsc (st, uptr, num, desc);
|
|
|
|
if (result == SCPE_OK)
|
|
fputc ('\n', st);
|
|
|
|
return result;
|
|
}
|