- removed cycle counting in CPU, the sim_interval issue - fixed the Console multiplexer logic; it now works with SET REMOTE TELNET= and SET CONSOLE TELNET= - commented testhdt.sim - changed run.cmd to try to execute either VStudio or mingw executable, in this order. - changed printf messages to use sim_printf Possible issues remaining: - timer device polling, not yet debugged, the rate of 1.25MHz is likely still incorrect, so UCSD wall clock is surely wrong - few instructions still to be trapped by opcode.debug - I haven't seen them in life code yet - HD device still not yet finished; I'll pick this up again soon
618 lines
22 KiB
C
618 lines
22 KiB
C
/* pdq3_sys.c: PDQ3 simulator interface
|
|
|
|
Work derived from Copyright (c) 2004-2012, Robert M. Supnik
|
|
Copyright (c) 2013 Holger Veit
|
|
|
|
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 names of Robert M Supnik and Holger Veit
|
|
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 and Holger Veit.
|
|
|
|
2013xxxx hv initial version (written up to the leval to test against bootloader)
|
|
20130907 hv added VIEWSEG command
|
|
20130925 hv added CALL and NAME command
|
|
20130927 hv wrong disassembly of LDC instr
|
|
20141003 hv compiler suggested warnings (vc++2013, gcc)
|
|
*/
|
|
#include "pdq3_defs.h"
|
|
#include <ctype.h>
|
|
|
|
t_stat parse_sym_m (char *cptr, t_value *val, int32 sw);
|
|
void pdq3_vm_init (void);
|
|
static t_stat pdq3_cmd_exstack(int32 arg, char *buf);
|
|
static t_stat pdq3_cmd_exmscw(int32 arg, char *buf);
|
|
static t_stat pdq3_cmd_extib(int32 arg, char *buf);
|
|
static t_stat pdq3_cmd_exseg(int32 arg, char *buf);
|
|
static t_stat pdq3_cmd_calltree(int32 arg, char *buf);
|
|
static t_stat pdq3_cmd_namealias(int32 arg, char *buf);
|
|
|
|
extern DEVICE cpu_dev;
|
|
extern UNIT cpu_unit;
|
|
extern DEVICE tty_dev;
|
|
extern DEVICE fdc_dev;
|
|
extern DEVICE tim_dev;
|
|
extern REG cpu_reg[];
|
|
extern uint16 M[];
|
|
extern uint16 reg_pc;
|
|
|
|
/* 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[] = "PDQ3";
|
|
REG *sim_PC = &cpu_reg[0]; /* note this is the artifical register PCX */
|
|
int32 sim_emax = 6;
|
|
DEVICE *sim_devices[] = {
|
|
&cpu_dev,
|
|
&con_dev,
|
|
&fdc_dev,
|
|
&tim_dev,
|
|
NULL
|
|
};
|
|
const char *sim_stop_messages[] = {
|
|
"---",
|
|
"PC Breakpoint",
|
|
"MEM Breakpoint",
|
|
"Invalid Opcode",
|
|
"Invalid MEM Access",
|
|
"Invalid I/O Access",
|
|
"Not yet implemented",
|
|
"BPT instruction",
|
|
"DEBUG PRE exec stop",
|
|
"DEBUG POST exec stop",
|
|
"HALT on Pascal Exception",
|
|
};
|
|
|
|
CTAB pdq3_cmds[] = {
|
|
{ "VSTACK", &pdq3_cmd_exstack, 0, "Display last N elements of stack. Top is where SP points to" },
|
|
{ "VMSCW", &pdq3_cmd_exmscw, 0, "Display current MSCW" },
|
|
{ "VTIB", &pdq3_cmd_extib, 0, "Display current TIB" },
|
|
{ "VSEG", &pdq3_cmd_exseg, 0, "Display a segment table entry" },
|
|
{ "VCALL", &pdq3_cmd_calltree, 0, "Display the call tree" },
|
|
{ "NAME", &pdq3_cmd_namealias, 0, "Define a name" },
|
|
{ NULL, NULL, 0, NULL }
|
|
};
|
|
|
|
void (*sim_vm_init)(void) = &pdq3_vm_init;
|
|
|
|
/* Loader proper */
|
|
t_stat sim_load (FILE *fi, char *cptr, char *fnam, int flag)
|
|
{
|
|
int rombase;
|
|
int c1, c2, i;
|
|
if (flag == 1) /* don't dump */
|
|
return SCPE_ARG;
|
|
/* this assumes a HDT style ROM, where the first 2 bytes refer to the
|
|
* actual word start of the ROM, e.g. with PDQ-3 the HDT ROM has 0xf401
|
|
* as the first word, so it will load at word address 0xf400, and 0xfc68
|
|
* will be preset to 0xf401
|
|
*/
|
|
c1 = fgetc(fi);
|
|
c2 = fgetc(fi);
|
|
rombase = c1 + c2 * 256;
|
|
rom_write(rombase & 0xfffe, rombase);
|
|
reg_fc68 = rombase;
|
|
i = 0;
|
|
while (!feof(fi) && i<0x1ff) {
|
|
c1 = fgetc(fi);
|
|
c2 = fgetc(fi);
|
|
rom_write(rombase+i, (uint16)(c1 + c2*256));
|
|
i++;
|
|
}
|
|
reg_romsize = i;
|
|
/* preset the cpu_serial number from ROM, may be overwritten manually for special purposes */
|
|
rom_read(rombase+i-1, ®_cpuserial);
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Note: this simh handles ABSOLUTE word addresses and segmented byte addresses.
|
|
* A word address addresses a single cell in memory (up to 65536 cells).
|
|
* A byte address only occurs in IPC context, it is relative to the content of
|
|
* the reg_segb register.
|
|
* Convention:
|
|
* $xxxx = word address
|
|
* xxxx:yyyy = byte address yyyy relative to segment xxxx
|
|
* #yyyy = byte address relative to current reg_segb
|
|
* The t_addr type must be 32 bit, the upper half contains the segment, the lower
|
|
* half contains the offset. If the upper half is NIL, it is a word address
|
|
*/
|
|
void pdq3_fprint_addr (FILE *st, DEVICE *dptr, t_addr addr)
|
|
{
|
|
if (ADDR_ISWORD(addr))
|
|
fprintf(st,"$");
|
|
else if (ADDR_SEG(addr) == reg_segb)
|
|
fprintf(st,"#");
|
|
else {
|
|
fprint_val (st, ADDR_SEG(addr), dptr->dradix, dptr->dwidth, PV_LEFT);
|
|
fprintf(st,":");
|
|
}
|
|
fprint_val (st, ADDR_OFF(addr), dptr->dradix, dptr->dwidth, PV_LEFT);
|
|
return;
|
|
}
|
|
|
|
t_addr pdq3_parse_addr (DEVICE *dptr, char *cptr, char **tptr)
|
|
{
|
|
t_addr seg, off;
|
|
if (cptr[0] == '#') {
|
|
off = strtotv(cptr+1, tptr, dptr->aradix);
|
|
return MAKE_BADDR(reg_segb,off);
|
|
} else if (cptr[0] == '$') {
|
|
off = strtotv(cptr+1, tptr, dptr->aradix);
|
|
return MAKE_WADDR(off);
|
|
} else {
|
|
char gbuf[CBUFSIZE];
|
|
get_glyph (cptr, gbuf, 0);
|
|
if (!strncmp(gbuf,"SEGB",4)) {
|
|
seg = reg_segb; *tptr = cptr+4;
|
|
} else
|
|
seg = strtotv(cptr, tptr, dptr->aradix);
|
|
if (*tptr[0] == ':') {
|
|
cptr = *tptr + 1;
|
|
off = strtotv(cptr, tptr, dptr->aradix);
|
|
return MAKE_BADDR(seg,off);
|
|
} else
|
|
return MAKE_WADDR(seg);
|
|
}
|
|
}
|
|
|
|
void pdq3_vm_init (void)
|
|
{
|
|
sim_vm_fprint_addr = &pdq3_fprint_addr;
|
|
sim_vm_parse_addr = &pdq3_parse_addr;
|
|
sim_vm_cmd = pdq3_cmds;
|
|
return;
|
|
}
|
|
|
|
static t_stat pdq3_cmd_exstack(int32 arg, char *buf)
|
|
{
|
|
t_stat rc;
|
|
uint16 data;
|
|
int i;
|
|
int n = buf[0] ? atol(buf) : 0;
|
|
if (n < 0) n = 0;
|
|
sim_printf("SP: $%04x LOW: $%04x UPR: $%04x\n",
|
|
reg_sp, reg_splow, reg_spupr);
|
|
for (i=n; i>=0; i--) {
|
|
if ((rc=Read(reg_sp+i, 0, &data, 0)) != SCPE_OK) continue;
|
|
if (i==0) sim_printf(" TOS: "); else sim_printf(" %3d: ",i);
|
|
sim_printf("%04x ($%04x)\n", data, reg_sp+i);
|
|
}
|
|
return SCPE_OK;
|
|
}
|
|
|
|
static t_stat pdq3_cmd_exmscw(int32 arg, char *buf)
|
|
{
|
|
char* next;
|
|
return dbg_dump_mscw(stdout, buf[0] ? pdq3_parse_addr(&cpu_dev, buf, &next) : reg_mp);
|
|
}
|
|
|
|
static t_stat pdq3_cmd_extib(int32 arg, char *buf)
|
|
{
|
|
char* next;
|
|
return dbg_dump_tib(stdout, buf[0] ? pdq3_parse_addr(&cpu_dev, buf, &next) : reg_ctp);
|
|
}
|
|
|
|
static t_stat pdq3_cmd_exseg(int32 arg, char *buf)
|
|
{
|
|
t_stat rc;
|
|
uint16 nsegs;
|
|
uint16 segnum, segptr;
|
|
char* next;
|
|
FILE* fd = stdout; /* XXX */
|
|
|
|
if (reg_ssv < 0x2030 || reg_ssv > 0xf000) {
|
|
fprintf(fd, "Cannot list segments in bootloader: incomplete tables\n");
|
|
return SCPE_NXM;
|
|
}
|
|
|
|
if ((rc=Read(reg_ssv, -1, &nsegs, 0)) != SCPE_OK) return rc;
|
|
|
|
if (buf[0]) {
|
|
segnum = pdq3_parse_addr(&cpu_dev, buf, &next);
|
|
fprintf(fd, "Segment $%02x\n", segnum);
|
|
if (segnum > nsegs) {
|
|
fprintf(fd, "Too high: maxsegs=$%02x\n",nsegs);
|
|
return SCPE_ARG;
|
|
}
|
|
if ((rc=Read(reg_ssv, segnum, &segptr, 0)) != SCPE_OK) return rc;
|
|
rc = dbg_dump_seg(fd, segptr);
|
|
} else
|
|
rc = dbg_dump_segtbl(fd);
|
|
return rc;
|
|
}
|
|
|
|
static t_stat pdq3_cmd_calltree(int32 arg, char *buf) {
|
|
return dbg_calltree(stdout);
|
|
}
|
|
|
|
static t_stat pdq3_cmd_namealias(int32 arg, char *buf) {
|
|
char* name, *alias;
|
|
|
|
if (buf[0]==0)
|
|
return dbg_listalias(stdout);
|
|
|
|
name = strtok(buf, " \t");
|
|
alias = strtok(NULL, " \t\n");
|
|
return dbg_enteralias(name,alias);
|
|
}
|
|
|
|
/**************************************************************************************
|
|
* PDQ utility functions
|
|
*************************************************************************************/
|
|
OPTABLE optable[] = {
|
|
/*00*/ { "SLDC0", OP_NULL }, { "SLDC1", OP_NULL },
|
|
/*02*/ { "SLDC2", OP_NULL }, { "SLDC3", OP_NULL },
|
|
/*04*/ { "SLDC4", OP_NULL }, { "SLDC5", OP_NULL },
|
|
/*06*/ { "SLDC6", OP_NULL }, { "SLDC7", OP_NULL },
|
|
/*08*/ { "SLDC8", OP_NULL }, { "SLDC9", OP_NULL },
|
|
/*0a*/ { "SLDC10", OP_NULL }, { "SLDC11", OP_NULL },
|
|
/*0c*/ { "SLDC12", OP_NULL }, { "SLDC13", OP_NULL },
|
|
/*0e*/ { "SLDC14", OP_NULL }, { "SLDC15", OP_NULL },
|
|
/*10*/ { "SLDC16", OP_NULL }, { "SLDC17", OP_NULL },
|
|
/*12*/ { "SLDC18", OP_NULL }, { "SLDC19", OP_NULL },
|
|
/*14*/ { "SLDC20", OP_NULL }, { "SLDC21", OP_NULL },
|
|
/*16*/ { "SLDC22", OP_NULL }, { "SLDC23", OP_NULL },
|
|
/*18*/ { "SLDC24", OP_NULL }, { "SLDC25", OP_NULL },
|
|
/*1a*/ { "SLDC26", OP_NULL }, { "SLDC27", OP_NULL },
|
|
/*1c*/ { "SLDC28", OP_NULL }, { "SLDC29", OP_NULL },
|
|
/*1e*/ { "SLDC30", OP_NULL }, { "SLDC31", OP_NULL },
|
|
/*20*/ { "SLDL1", OP_NULL }, { "SLDL2", OP_NULL },
|
|
/*22*/ { "SLDL3", OP_NULL }, { "SLDL4", OP_NULL },
|
|
/*24*/ { "SLDL5", OP_NULL }, { "SLDL6", OP_NULL },
|
|
/*26*/ { "SLDL7", OP_NULL }, { "SLDL8", OP_NULL },
|
|
/*28*/ { "SLDL9", OP_NULL }, { "SLDL10", OP_NULL },
|
|
/*2a*/ { "SLDL11", OP_NULL }, { "SLDL12", OP_NULL },
|
|
/*2c*/ { "SLDL13", OP_NULL }, { "SLDL14", OP_NULL },
|
|
/*2e*/ { "SLDL15", OP_NULL }, { "SLDL16", OP_NULL },
|
|
/*30*/ { "SLDO1", OP_NULL }, { "SLDO2", OP_NULL },
|
|
/*32*/ { "SLDO3", OP_NULL }, { "SLDO4", OP_NULL },
|
|
/*34*/ { "SLDO5", OP_NULL }, { "SLDO6", OP_NULL },
|
|
/*36*/ { "SLDO7", OP_NULL }, { "SLDO8", OP_NULL },
|
|
/*38*/ { "SLDO9", OP_NULL }, { "SLDO10", OP_NULL },
|
|
/*3a*/ { "SLDO11", OP_NULL }, { "SLDO12", OP_NULL },
|
|
/*3c*/ { "SLDO13", OP_NULL }, { "SLDO14", OP_NULL },
|
|
/*3e*/ { "SLDO15", OP_NULL }, { "SLDO16", OP_NULL },
|
|
/*40*/ { "", OP_ERROR }, { "", OP_ERROR },
|
|
/*42*/ { "", OP_ERROR }, { "", OP_ERROR },
|
|
/*44*/ { "", OP_ERROR }, { "", OP_ERROR },
|
|
/*46*/ { "", OP_ERROR }, { "", OP_ERROR },
|
|
/*48*/ { "", OP_ERROR }, { "", OP_ERROR },
|
|
/*4a*/ { "", OP_ERROR }, { "", OP_ERROR },
|
|
/*4c*/ { "", OP_ERROR }, { "", OP_ERROR },
|
|
/*4e*/ { "", OP_ERROR }, { "", OP_ERROR },
|
|
/*50*/ { "", OP_ERROR }, { "", OP_ERROR },
|
|
/*52*/ { "", OP_ERROR }, { "", OP_ERROR },
|
|
/*54*/ { "", OP_ERROR }, { "", OP_ERROR },
|
|
/*56*/ { "", OP_ERROR }, { "", OP_ERROR },
|
|
/*58*/ { "", OP_ERROR }, { "", OP_ERROR },
|
|
/*5a*/ { "", OP_ERROR }, { "", OP_ERROR },
|
|
/*5c*/ { "", OP_ERROR }, { "", OP_ERROR },
|
|
/*5e*/ { "", OP_ERROR }, { "", OP_ERROR },
|
|
/*60*/ { "", OP_ERROR }, { "", OP_ERROR },
|
|
/*62*/ { "", OP_ERROR }, { "", OP_ERROR },
|
|
/*64*/ { "", OP_ERROR }, { "", OP_ERROR },
|
|
/*66*/ { "", OP_ERROR }, { "", OP_ERROR },
|
|
/*68*/ { "", OP_ERROR }, { "", OP_ERROR },
|
|
/*6a*/ { "", OP_ERROR }, { "", OP_ERROR },
|
|
/*6c*/ { "", OP_ERROR }, { "", OP_ERROR },
|
|
/*6e*/ { "", OP_ERROR }, { "", OP_ERROR },
|
|
/*70*/ { "", OP_ERROR }, { "", OP_ERROR },
|
|
/*72*/ { "", OP_ERROR }, { "", OP_ERROR },
|
|
/*74*/ { "", OP_ERROR }, { "", OP_ERROR },
|
|
/*76*/ { "", OP_ERROR }, { "", OP_ERROR },
|
|
/*78*/ { "SIND0", OP_NULL }, { "SIND1", OP_NULL },
|
|
/*7a*/ { "SIND2", OP_NULL }, { "SIND3", OP_NULL },
|
|
/*7c*/ { "SIND4", OP_NULL }, { "SIND5", OP_NULL },
|
|
/*7e*/ { "SIND6", OP_NULL }, { "SIND7", OP_NULL },
|
|
/*80*/ { "LDCB", OP_UB }, { "LDCI", OP_W },
|
|
/*82*/ { "LCA", OP_AB }, { "LDC", OP_BUB },
|
|
/*84*/ { "LLA", OP_B }, { "LDO", OP_B },
|
|
/*86*/ { "LAO", OP_B }, { "LDL", OP_B },
|
|
/*88*/ { "LDA", OP_DBB }, { "LOD", OP_DBB },
|
|
/*8a*/ { "UJP", OP_SB }, { "UJPL", OP_SW },
|
|
/*8c*/ { "MPI", OP_NULL }, { "DVI", OP_NULL },
|
|
/*8e*/ { "STM", OP_UB }, { "MODI", OP_NULL },
|
|
/*90*/ { "CPL", OP_UB }, { "CPG", OP_UB },
|
|
/*92*/ { "CPI", OP_DBUB }, { "CXL", OP_UBUB },
|
|
/*94*/ { "CXG", OP_UBUB }, { "CXI", OP_UBDBUB },
|
|
/*96*/ { "RPU", OP_B }, { "CPF", OP_NULL },
|
|
/*98*/ { "LDCN", OP_NULL }, { "LSL", OP_DB },
|
|
/*9a*/ { "LDE", OP_UBB }, { "LAE", OP_UBB },
|
|
/*9c*/ { "NOP", OP_NULL }, { "LPR", OP_NULL },
|
|
/*9e*/ { "BPT", OP_NULL }, { "BNOT", OP_NULL },
|
|
/*a0*/ { "LOR", OP_NULL }, { "LAND", OP_NULL },
|
|
/*a2*/ { "ADI", OP_NULL }, { "SBI", OP_NULL },
|
|
/*a4*/ { "STL", OP_B }, { "SRO", OP_B },
|
|
/*a6*/ { "STR", OP_DBB }, { "LDB", OP_NULL },
|
|
/*a8*/ { "LHO", OP_NULL }, { "LVO", OP_NULL },
|
|
/*aa*/ { "", OP_ERROR }, { "", OP_ERROR },
|
|
/*ac*/ { "", OP_ERROR }, { "", OP_ERROR },
|
|
/*ae*/ { "", OP_ERROR }, { "", OP_ERROR },
|
|
/*b0*/ { "EQUI", OP_NULL }, { "NEQI", OP_NULL },
|
|
/*b2*/ { "LEQI", OP_NULL }, { "GEQI", OP_NULL },
|
|
/*b4*/ { "LEUSW", OP_NULL }, { "GEUSW", OP_NULL },
|
|
/*b6*/ { "EQUPWR", OP_NULL }, { "LEQPWR", OP_NULL },
|
|
/*b8*/ { "GEQPWR", OP_NULL }, { "EQUBYT", OP_B },
|
|
/*ba*/ { "LEQBYT", OP_B }, { "GEQBYT", OP_B },
|
|
/*bc*/ { "SRS", OP_NULL }, { "SWAP", OP_NULL },
|
|
/*be*/ { "TNC", OP_NULL }, { "RND", OP_NULL },
|
|
/*c0*/ { "ADR", OP_NULL }, { "SBR", OP_NULL },
|
|
/*c2*/ { "MPR", OP_NULL }, { "DVR", OP_NULL },
|
|
/*c4*/ { "STO", OP_NULL }, { "MOV", OP_B },
|
|
/*c6*/ { "DUP2", OP_NULL }, { "ADJ", OP_UB },
|
|
/*c8*/ { "STB", OP_NULL }, { "LDP", OP_NULL },
|
|
/*ca*/ { "STP", OP_NULL }, { "CHK", OP_NULL },
|
|
/*cc*/ { "FLT", OP_NULL }, { "EQUREAL",OP_NULL },
|
|
/*ce*/ { "LEQREAL",OP_NULL }, { "GEQREAL",OP_NULL },
|
|
/*d0*/ { "LDM", OP_UB }, { "SPR", OP_NULL },
|
|
/*d2*/ { "EFJ", OP_SB }, { "NFJ", OP_SB },
|
|
/*d4*/ { "FJP", OP_SB }, { "FJPL", OP_SW },
|
|
/*d6*/ { "XJP", OP_B }, { "IXA", OP_B },
|
|
/*d8*/ { "IXP", OP_UBUB }, { "STE", OP_UBB },
|
|
/*da*/ { "INN", OP_NULL }, { "UNI", OP_NULL },
|
|
/*dc*/ { "INT", OP_NULL }, { "DIF", OP_NULL },
|
|
/*de*/ { "SIGNAL", OP_NULL }, { "WAIT", OP_NULL },
|
|
/*e0*/ { "ABI", OP_NULL }, { "NGI", OP_NULL },
|
|
/*e2*/ { "DUP1", OP_NULL }, { "ABR", OP_NULL },
|
|
/*e4*/ { "NGR", OP_NULL }, { "LNOT", OP_NULL },
|
|
/*e6*/ { "IND", OP_B }, { "INC", OP_B },
|
|
};
|
|
|
|
static uint16 UB(t_value arg)
|
|
{
|
|
return arg & 0xff;
|
|
}
|
|
static uint16 DB(t_value arg)
|
|
{
|
|
return UB(arg);
|
|
}
|
|
static int16 W(t_value arg1, t_value arg2)
|
|
{
|
|
uint16 wl = arg1 & 0xff;
|
|
uint16 wh = arg2 & 0xff;
|
|
return wl | ((wh << 8) & 0xff00);
|
|
}
|
|
|
|
static int16 SW(t_value arg1, t_value arg2)
|
|
{
|
|
return W(arg1,arg2);
|
|
}
|
|
static int16 SB(t_value arg)
|
|
{
|
|
int16 w = arg & 0xff;
|
|
if (w & 0x80) w |= 0xff00;
|
|
return w;
|
|
}
|
|
static uint16 B(t_value arg1, t_value arg2, int* sz) {
|
|
uint16 wh = arg1 & 0xff;
|
|
uint16 wl;
|
|
if (wh & 0x80) {
|
|
wl = arg2 & 0xff;
|
|
wl |= ((wh & 0x7f) << 8);
|
|
*sz = 2;
|
|
return wl;
|
|
} else {
|
|
*sz = 1;
|
|
return wh;
|
|
}
|
|
}
|
|
|
|
t_stat print_hd(FILE *of, t_value val, t_bool hexdec, t_bool isbyte)
|
|
{
|
|
uint16 data = isbyte ? (val & 0xff) : (val & 0xffff);
|
|
|
|
if (hexdec)
|
|
fprintf(of,"%0xh",data);
|
|
else
|
|
fprintf(of,"%d",data);
|
|
return SCPE_OK;
|
|
}
|
|
|
|
t_stat fprint_sym_m (FILE *of, t_addr addr, t_value *val,
|
|
UNIT *uptr, int32 sw)
|
|
{
|
|
uint16 op, arg1, arg2, arg3;
|
|
int16 sarg;
|
|
t_stat size = 0;
|
|
int optype, sz;
|
|
t_bool hexdec = (sw & SWMASK('H')) ? TRUE : FALSE;
|
|
addr = ADDR_OFF(addr);
|
|
|
|
op = val[0];
|
|
if (op > 0xe7) return SCPE_ARG;
|
|
|
|
optype = optable[op].flags;
|
|
if (optype > OP_ERROR) {
|
|
fprintf(of,"%-8s", optable[op].name);
|
|
switch (optype) {
|
|
case OP_NULL:
|
|
break;
|
|
case OP_UB:
|
|
size = 1; arg1 = UB(val[1]);
|
|
print_hd(of, arg1, hexdec, FALSE);
|
|
break;
|
|
case OP_W:
|
|
size = 2; sarg = W(val[1],val[2]);
|
|
print_hd(of, sarg, hexdec, FALSE);
|
|
break;
|
|
case OP_AB:
|
|
arg1 = B(val[1],val[2], &sz); size = sz;
|
|
fprintf(of,"#%x", arg1*2);
|
|
break;
|
|
case OP_B:
|
|
arg1 = B(val[1],val[2], &sz); size = sz;
|
|
print_hd(of, arg1, hexdec, FALSE);
|
|
break;
|
|
case OP_DBB:
|
|
arg1 = DB(val[1]);
|
|
arg2 = B(val[2],val[3], &sz); size = sz+1;
|
|
print_hd(of, arg1, hexdec, TRUE); fputc(',',of);
|
|
print_hd(of, arg2, hexdec, FALSE);
|
|
break;
|
|
case OP_UBB:
|
|
arg1 = UB(val[1]);
|
|
arg2 = B(val[2],val[3], &sz); size = sz+1;
|
|
print_hd(of, arg1, hexdec, TRUE); fputc(',',of);
|
|
print_hd(of, arg2, hexdec, FALSE);
|
|
break;
|
|
case OP_BUB:
|
|
arg1 = B(val[1],val[2], &sz); size = sz+1;
|
|
arg2 = UB(val[sz+1]);
|
|
print_hd(of, arg1, hexdec, FALSE); fputc(',',of);
|
|
print_hd(of, arg2, hexdec, TRUE);
|
|
break;
|
|
case OP_SB:
|
|
size = 1; sarg = SB(val[1]);
|
|
fprintf(of,"#%x", addr+sarg+2);
|
|
break;
|
|
case OP_SW:
|
|
size = 2; sarg = SW(val[1],val[2]);
|
|
fprintf(of,"#%x", addr+sarg+3);
|
|
break;
|
|
case OP_DBUB:
|
|
size = 2; arg1 = DB(val[1]);
|
|
arg2 = UB(val[2]);
|
|
print_hd(of, arg1, hexdec, TRUE); fputc(',',of);
|
|
print_hd(of, arg2, hexdec, TRUE);
|
|
break;
|
|
case OP_UBUB:
|
|
size = 2; arg1 = UB(val[1]);
|
|
arg2 = UB(val[2]);
|
|
print_hd(of, arg1, hexdec, TRUE); fputc(',',of);
|
|
print_hd(of, arg2, hexdec, TRUE);
|
|
break;
|
|
case OP_UBDBUB:
|
|
size = 3; arg1 = UB(val[1]);
|
|
arg2 = DB(val[2]);
|
|
arg3 = UB(val[3]);
|
|
print_hd(of, arg1, hexdec, TRUE); fputc(',',of);
|
|
print_hd(of, arg2, hexdec, TRUE); fputc(',',of);
|
|
print_hd(of, arg3, hexdec, TRUE);
|
|
break;
|
|
case OP_DB:
|
|
size = 1; arg1 = DB(val[1]);
|
|
print_hd(of, arg1, hexdec, TRUE);
|
|
break;
|
|
}
|
|
return -size;
|
|
} else {
|
|
fprintf(of,"%-8s","DB"); print_hd(of, op, hexdec, TRUE);
|
|
return SCPE_OK;
|
|
}
|
|
}
|
|
|
|
/* Symbolic decode
|
|
Inputs:
|
|
*of = output stream
|
|
addr = current PC
|
|
*val = pointer to data
|
|
*uptr = pointer to unit
|
|
sw = switches
|
|
Outputs:
|
|
return = status code
|
|
*/
|
|
t_stat fprint_sym (FILE *of, t_addr addr, t_value *val,
|
|
UNIT *uptr, int32 sw)
|
|
{
|
|
t_addr off;
|
|
T_FLCVT t;
|
|
int ch;
|
|
|
|
if (sw & SWMASK('M') && !ADDR_ISWORD(addr)) {
|
|
return fprint_sym_m(of, addr, val, uptr, sw);
|
|
}
|
|
if (sw & SWMASK('B')) { /* as BYTE */
|
|
if (ADDR_ISWORD(addr)) {
|
|
fprint_val(of, (val[0]>>8) & 0xff, cpu_dev.dradix, 8, PV_RZRO);
|
|
fprintf(of, ",");
|
|
fprint_val(of, val[0] & 0xff, cpu_dev.dradix, 8, PV_RZRO);
|
|
} else
|
|
fprint_val(of, val[0], cpu_dev.dradix, 8, PV_RZRO);
|
|
return SCPE_OK;
|
|
}
|
|
if (sw & SWMASK('C')) { /* as CHAR */
|
|
if (ADDR_ISWORD(addr)) {
|
|
ch = val[0] & 0xff;
|
|
fprintf(of, isprint(ch) ? "%c," : "%02x,", ch);
|
|
ch = val[0]>>8;
|
|
fprintf(of, isprint(ch) ? "%c" : "%02x", ch);
|
|
} else {
|
|
ch = val[0] & 0xff;
|
|
fprintf(of, isprint(ch) ? "%c" : "%02x", ch);
|
|
}
|
|
return SCPE_OK;
|
|
}
|
|
if (sw & SWMASK('W')) { /* as WORD */
|
|
if (ADDR_ISWORD(addr)) {
|
|
fprint_val(of, val[0], cpu_dev.dradix, 16, PV_RZRO);
|
|
off = ADDR_OFF(addr);
|
|
if (off > (t_addr)(reg_bp+MSCW_SZ-1))
|
|
fprintf(of," (GLOBAL+%d)", off - reg_bp - MSCW_SZ + 1);
|
|
else if (off >= reg_mp && off <= (t_addr)(reg_mp+OFFB_MSSEG))
|
|
fprintf(of," (MP+%d)", off - reg_mp);
|
|
else if (off > (t_addr)(reg_mp+MSCW_SZ-1))
|
|
fprintf(of," (LOCAL+%d)", off - reg_mp - MSCW_SZ + 1);
|
|
else if (off >= reg_sp && off < reg_spupr)
|
|
fprintf(of," (SP+%d)", off - reg_sp);
|
|
} else {
|
|
fprint_val(of, val[0], cpu_dev.dradix, 8, PV_RZRO);
|
|
fprint_val(of, val[1], cpu_dev.dradix, 8, PV_RZRO);
|
|
}
|
|
return SCPE_OK;
|
|
}
|
|
if (sw & SWMASK('F')) { /* as FLOAT */
|
|
t.i[0] = val[1];
|
|
t.i[1] = val[0];
|
|
fprintf(of, "%12.6e", t.f);
|
|
return -1;
|
|
}
|
|
if (sw & SWMASK('S')) { /* as semaphore */
|
|
fprintf(of, "SEM(count=%d, waitq=$%04x)", val[0], val[1]);
|
|
return -1;
|
|
}
|
|
if (sw & SWMASK('M')) { /* as MSCW */
|
|
dbg_dump_mscw(of, val[0]);
|
|
return SCPE_OK;
|
|
}
|
|
if (sw & SWMASK('T')) { /* as TIB */
|
|
dbg_dump_tib(of, addr);
|
|
return SCPE_OK;
|
|
}
|
|
return SCPE_ARG;
|
|
}
|
|
|
|
/* Symbolic input
|
|
Inputs:
|
|
*cptr = 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 *cptr, t_addr addr, UNIT *uptr, t_value *val, int32 sw)
|
|
{
|
|
return SCPE_ARG;
|
|
}
|