| /* 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 | |
| */ | |
| /* Use scp.c provided fprintf function */ | |
| #define fprintf Fprintf | |
| #define fputs(_s,f) Fprintf(f,"%s",_s) | |
| #define fputc(_c,f) Fprintf(f,"%c",_c) | |
| 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, const char *cptr, const 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) | |
| { | |
| const 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) | |
| { | |
| const 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; | |
| const 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; | |
| } |