/* | |
Copyright (c) 2015-2017, John Forecast | |
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 | |
JOHN FORECAST 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 John Forecast shall not | |
be used in advertising or otherwise to promote the sale, use or other dealings | |
in this Software without prior written authorization from John Forecast. | |
*/ | |
/* cdc1700_dis.c: CDC1700 disassembler | |
*/ | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include "cdc1700_defs.h" | |
extern uint16 Areg, Mreg, Preg, Qreg, RelBase; | |
extern uint16 LoadFromMem(uint16); | |
extern uint8 P[]; | |
extern DEVICE cpu_dev; | |
extern UNIT cpu_unit; | |
extern t_stat disEffectiveAddr(uint16, uint16, uint16 *, uint16 *); | |
extern uint16 doADDinternal(uint16, uint16); | |
const char *opName[] = { | |
"???", "JMP", "MUI", "DVI", "STQ", "RTJ", "STA", "SPA", | |
"ADD", "SUB", "AND", "EOR", "LDA", "RAO", "LDQ", "ADQ" | |
}; | |
const char *idxName[] = { | |
"", ",I", ",Q", ",B" | |
}; | |
const char *spcName[] = { | |
"SLS", "???", "INP", "OUT", "EIN", "IIN", "SPB", "CPB", | |
"???", "INA", "ENA", "NOP", "ENQ", "INQ", "EXI", "???" | |
}; | |
const char *skpName[] = { | |
"SAZ", "SAN", "SAP", "SAM", "SQZ", "SQN", "SQP", "SQM", | |
"SWS", "SWN", "SOV", "SNO", "SPE", "SNP", "SPF", "SNF" | |
}; | |
const char *interName[] = { | |
"SET", "TRM", "TRQ", "TRB", "TRA", "AAM", "AAQ", "AAB", | |
"CLR", "TCM", "TCQ", "TCB", "TCA", "EAM", "EAQ", "EAB", | |
"SET", "TRM", "TRQ", "TRB", "TRA", "LAM", "LAQ", "LAB", | |
"NOOP", NULL, NULL, NULL, NULL, "CAM", "CAQ", "CAB" | |
}; | |
const char *destName[] = { | |
"", "M", "Q", "Q,M", "A", "A,M", "A,Q", "A,Q,M" | |
}; | |
const char *shiftName[] = { | |
NULL, "QRS", "ARS", "LRS", NULL, "QLS", "ALS", "LLS" | |
}; | |
/* | |
* Enhanced instruction set mnemonics | |
*/ | |
char enhRegChar[] = { | |
' ', '1', '2', '3', '4', 'Q', 'A', 'I' | |
}; | |
const char *enhIdxName[] = { | |
"", ",1", ",2", ",3", ",4", ",Q", ",A", ",I" | |
}; | |
char enhSkipType[] = { | |
'Z', 'N', 'P', 'M' | |
}; | |
char enhSkipReg[] = { | |
'4', '1', '2', '3' | |
}; | |
const char *enhMiscName0[] = { | |
"???", "LMM", "LRG", "SRG", "SIO", "SPS", "DMI", "CBP", | |
"GPE", "GPO", "ASC", "APM", "PM0", "PM1" | |
}; | |
#define ENH_MAXMISC0 0xD | |
const char *enhMiscName1[] = { | |
"LUB", "LLB", "EMS", "WPR", "RPR", "ECC" | |
}; | |
#define ENH_MAXMISC1 0x5 | |
const char *enhFldName[] = { | |
"???", "???", "SFZ", "SFN", "LFA", "SFA", "CLF", "SEF" | |
}; | |
/* | |
* Generate a single line of text for an instruction. Format is: | |
* | |
* c xxxx yyyy zzzz <instr> <targ> | |
* | |
* or if enhanced instruction set is enabled | |
* | |
* c xxxx yyyy zzzz aaaa <instr> <targ> | |
* | |
* where: | |
* | |
* |P Normal/Protected location | |
* xxxx Memory address of instruction in hex | |
* yyyy First word of instruction in hex | |
* zzzz Second word of instruction in hex, replaced by spaces if | |
* not present | |
* aaaa Third word of instruction in hex, replaced by spaces if | |
* not present | |
* <instr> Disassmbled instruction | |
* <targ> Optional target address and contents | |
* | |
* Returns: | |
* # of words consumed by the instruction | |
*/ | |
int disassem(char *buf, uint16 addr, t_bool dbg, t_bool targ, t_bool exec) | |
{ | |
int consumed = 1; | |
char prot = ISPROTECTED(addr) ? 'P' : ' '; | |
char optional[8], optional2[8], temp[8], decoded[64], enhInstr[8]; | |
const char *mode, *spc, *shift, *inter, *dest; | |
uint16 instr = LoadFromMem(addr); | |
uint16 instr2, enhMode; | |
uint16 delta = instr & OPC_ADDRMASK; | |
uint8 more = 0, isconst = 0, enhRB; | |
uint16 t; | |
t_bool enhValid = FALSE, enhChar = FALSE; | |
strcpy(optional, " "); | |
strcpy(optional2, " "); | |
strcpy(decoded, "UNDEF"); | |
if ((instr & OPC_MASK) != 0) { | |
if ((instr & MOD_RE) == 0) | |
mode = delta == 0 ? "+ " : "- "; | |
else mode = "* "; | |
switch (instr & OPC_MASK) { | |
case OPC_ADQ: | |
case OPC_LDQ: | |
case OPC_LDA: | |
case OPC_EOR: | |
case OPC_AND: | |
case OPC_SUB: | |
case OPC_ADD: | |
case OPC_DVI: | |
case OPC_MUI: | |
if (ISCONSTANT(instr)) | |
isconst = 1; | |
break; | |
} | |
if (delta == 0) { | |
consumed++; | |
sprintf(optional, "%04X", LoadFromMem(addr + 1)); | |
sprintf(temp, "$%04X", LoadFromMem(addr + 1)); | |
} else sprintf(temp, "$%02X", delta); | |
sprintf(decoded, "%s%s%s%s%s%s%s", | |
opName[(instr & OPC_MASK) >> 12], | |
mode, | |
isconst ? "=" : "", | |
(instr & MOD_IN) != 0 ? "(" : "", | |
temp, | |
(instr & MOD_IN) != 0 ? ")" : "", | |
idxName[(instr & (MOD_I1 | MOD_I2)) >> 8]); | |
} else { | |
spc = spcName[(instr & OPC_SPECIALMASK) >> 8]; | |
switch (instr & OPC_SPECIALMASK) { | |
case OPC_IIN: | |
case OPC_EIN: | |
case OPC_SPB: | |
case OPC_CPB: | |
switch (INSTR_SET) { | |
case INSTR_ORIGINAL: | |
/* | |
* Character addressing enable/disable is only available as | |
* an extension to the original set. | |
*/ | |
switch (instr) { | |
case OPC_ECA: | |
sprintf(decoded, "%s", "ECA"); | |
break; | |
case OPC_DCA: | |
sprintf(decoded, "%s", "DCA"); | |
break; | |
default: | |
sprintf(decoded, "%s", spc); | |
break; | |
} | |
break; | |
case INSTR_BASIC: | |
if (delta == 0) { | |
sprintf(decoded, "%s", spc); | |
} else { | |
sprintf(decoded, "%s", "NOP [ Possible enhanced instruction"); | |
} | |
break; | |
case INSTR_ENHANCED: | |
if (delta != 0) { | |
switch (instr & OPC_SPECIALMASK) { | |
case OPC_IIN: | |
if (((instr & OPC_FLDF3A) != OPC_FLDRSV1) && | |
((instr & OPC_FLDF3A) != OPC_FLDRSV2)) { | |
instr2 = LoadFromMem(addr + 1); | |
delta = instr2 & OPC_ADDRMASK; | |
if (((instr2 & OPC_FLDSTR) - (instr2 & OPC_FLDLTH)) >= 0) { | |
consumed++; | |
if ((instr & MOD_ENHRE) == 0) | |
mode = delta == 0 ? "+ " : "- "; | |
else mode = "* "; | |
sprintf(optional, "%04X", instr2); | |
if (delta == 0) { | |
consumed++; | |
sprintf(optional2, "%04X", LoadFromMem(addr + 2)); | |
sprintf(temp, "$%04X", LoadFromMem(addr + 2)); | |
} else sprintf(temp, "$%02X", delta); | |
sprintf(decoded, "%s%s%s%s%s,%d,%d%s", | |
enhFldName[instr & OPC_FLDF3A], | |
mode, | |
(instr & MOD_ENHIN) != 0 ? "(" : "", | |
temp, | |
(instr & MOD_ENHIN) != 0 ? ")" : "", | |
(instr2 & OPC_FLDSTR) >> 12, | |
((instr2 & OPC_FLDLTH) >> 8) + 1, | |
enhIdxName[(instr & MOD_ENHRA) >> 3]); | |
break; | |
} | |
} | |
strcpy(decoded, "UNDEF"); | |
targ = FALSE; | |
break; | |
case OPC_EIN: | |
instr2 = LoadFromMem(addr + 1); | |
enhMode = (instr2 & OPC_ENHF5) >> 8; | |
enhRB = instr & MOD_ENHRB; | |
switch (instr2 & OPC_ENHF4) { | |
case OPC_STOSJMP: | |
if (enhMode == 0) { | |
enhValid = TRUE; | |
if (enhRB == REG_NOREG) | |
strcpy(enhInstr, "SJE"); | |
else sprintf(enhInstr, "SJ%c", enhRegChar[enhRB]); | |
} | |
break; | |
case OPC_STOADD: | |
if ((enhMode == 0) && (enhRB != REG_NOREG)) { | |
enhValid = TRUE; | |
sprintf(enhInstr, "AR%c", enhRegChar[enhRB]); | |
} | |
break; | |
case OPC_STOSUB: | |
if ((enhMode == 0) && (enhRB != REG_NOREG)) { | |
enhValid = TRUE; | |
sprintf(enhInstr, "SB%c", enhRegChar[enhRB]); | |
} | |
break; | |
case OPC_STOAND: | |
if (enhRB != REG_NOREG) | |
switch (enhMode) { | |
case WORD_REG: | |
enhValid = TRUE; | |
sprintf(enhInstr, "AN%c", enhRegChar[enhRB]); | |
break; | |
case WORD_MEM: | |
enhValid = TRUE; | |
sprintf(enhInstr, "AM%c", enhRegChar[enhRB]); | |
break; | |
} | |
break; | |
case OPC_STOLOADST: | |
switch (enhMode) { | |
case WORD_REG: | |
if (enhRB != REG_NOREG) { | |
enhValid = TRUE; | |
sprintf(enhInstr, "LR%c", enhRegChar[enhRB]); | |
} | |
break; | |
case WORD_MEM: | |
if (enhRB != REG_NOREG) { | |
enhValid = TRUE; | |
sprintf(enhInstr, "SR%c", enhRegChar[enhRB]); | |
} | |
break; | |
case CHAR_REG: | |
if (enhRB != REG_NOREG) { | |
enhValid = TRUE; | |
enhChar = TRUE; | |
strcpy(enhInstr, "LCA"); | |
} | |
break; | |
case CHAR_MEM: | |
if (enhRB != REG_NOREG) { | |
enhValid = TRUE; | |
enhChar = TRUE; | |
strcpy(enhInstr, "SCA"); | |
} | |
break; | |
} | |
break; | |
case OPC_STOOR: | |
if (enhRB != REG_NOREG) | |
switch (enhMode) { | |
case WORD_REG: | |
enhValid = TRUE; | |
sprintf(enhInstr, "OR%c", enhRegChar[enhRB]); | |
break; | |
case WORD_MEM: | |
enhValid = TRUE; | |
sprintf(enhInstr, "OM%c", enhRegChar[enhRB]); | |
break; | |
} | |
break; | |
case OPC_STOCRE: | |
switch (enhMode) { | |
case WORD_REG: | |
if (enhRB != REG_NOREG) { | |
enhValid = TRUE; | |
sprintf(enhInstr, "C%cE", enhRegChar[enhRB]); | |
} | |
break; | |
case CHAR_REG: | |
if (enhRB != REG_NOREG) { | |
enhValid = TRUE; | |
enhChar = TRUE; | |
strcpy(enhInstr, "CCE"); | |
} | |
break; | |
} | |
break; | |
} | |
if (enhValid) { | |
delta = instr2 & OPC_ADDRMASK; | |
consumed++; | |
if ((instr & MOD_ENHRE) == 0) | |
mode = delta == 0 ? "+ " : "- "; | |
else mode = "* "; | |
sprintf(optional, "%04X", instr2); | |
if (delta == 0) { | |
consumed++; | |
sprintf(optional2, "%04X", LoadFromMem(addr + 2)); | |
sprintf(temp, "$%04X", LoadFromMem(addr + 2)); | |
} else sprintf(temp, "$%02X", delta); | |
if (!enhChar) { | |
if ((delta == 0) && | |
(instr & (MOD_ENHRE | MOD_ENHIN)) == 0) | |
isconst = 1; | |
sprintf(decoded, "%s%s%s%s%s%s%s", | |
enhInstr, | |
mode, | |
isconst ? "=" : "", | |
(instr & MOD_ENHIN) != 0 ? "(" : "", | |
temp, | |
(instr & MOD_ENHIN) != 0 ? ")" : "", | |
enhIdxName[(instr & MOD_ENHRA) >> 3]); | |
} else { | |
sprintf(decoded, "%s%s%s%s%s%s%s", | |
enhInstr, | |
mode, | |
(instr & MOD_ENHIN) != 0 ? "(" : "", | |
temp, | |
(instr & MOD_ENHIN) != 0 ? ")" : "", | |
enhIdxName[enhRB], | |
enhIdxName[(instr & MOD_ENHRA) >> 3]); | |
} | |
} else { | |
strcpy(decoded, "UNDEF"); | |
targ = FALSE; | |
} | |
break; | |
case OPC_SPB: | |
if ((instr & OPC_DRPMBZ) == 0) { | |
char reg = enhRegChar[(instr & OPC_DRPRA) >> 5]; | |
uint8 sk = instr & OPC_DRPSK; | |
sprintf(decoded, "D%cP $%1X", reg, sk); | |
break; | |
} | |
break; | |
case OPC_CPB: | |
if ((instr & OPC_ENHXFRF2A) == 0) { | |
if ((instr & (OPC_ENHXFRRA | OPC_ENHXFRRB)) != 0) { | |
char ra = enhRegChar[(instr & OPC_ENHXFRRA) >> 5]; | |
char rb = enhRegChar[instr & OPC_ENHXFRRB]; | |
sprintf(decoded, "XF%c %c", ra, rb); | |
break; | |
} | |
} | |
strcpy(decoded, "UNDEF"); | |
targ = FALSE; | |
break; | |
} | |
} else { | |
sprintf(decoded, "%s", spc); | |
targ = FALSE; | |
} | |
break; | |
} | |
break; | |
case OPC_NOP: | |
switch (INSTR_SET) { | |
case INSTR_ORIGINAL: | |
sprintf(decoded, "%s", spc); | |
break; | |
case INSTR_BASIC: | |
if (delta != 0) { | |
sprintf(decoded, "%s", "NOP [ Possible enhanced instruction"); | |
} else sprintf(decoded, "%s", spc); | |
break; | |
case INSTR_ENHANCED: | |
if (delta != 0) { | |
uint8 miscFN = instr & OPC_MISCF3; | |
char reg = enhRegChar[(instr & OPC_MISCRA) >> 5]; | |
targ = FALSE; | |
if ((instr & OPC_MISCRA) == 0) { | |
if (miscFN <= ENH_MAXMISC0) | |
spc = enhMiscName0[miscFN]; | |
else spc = "UNDEF"; | |
sprintf(decoded, "%s", spc); | |
} else { | |
if (miscFN <= ENH_MAXMISC1) { | |
spc = enhMiscName1[miscFN]; | |
sprintf(decoded, "%s %c", spc, reg); | |
} else strcpy(decoded, "UNDEF"); | |
} | |
} else sprintf(decoded, "%s", spc); | |
break; | |
} | |
break; | |
case OPC_EXI: | |
sprintf(decoded, "%s $%02X", spc, delta); | |
break; | |
case OPC_SKIPS: | |
sprintf(decoded, "%s $%01X", | |
skpName[(instr & OPC_SKIPMASK) >> 4], instr & OPC_SKIPCOUNT); | |
break; | |
case OPC_SLS: | |
if (delta != 0) { | |
switch (INSTR_SET) { | |
case INSTR_BASIC: | |
sprintf(decoded, "%s", "NOP [ Possible enhanced instruction"); | |
break; | |
case INSTR_ORIGINAL: | |
sprintf(decoded, "%s $%02X", spc, delta); | |
break; | |
case INSTR_ENHANCED: | |
{ | |
char reg = enhSkipReg[(instr & OPC_ENHSKIPREG) >> 6]; | |
char type = enhSkipType[(instr & OPC_ENHSKIPTY) >> 4]; | |
uint8 sk = instr & OPC_ENHSKIPCNT; | |
sprintf(decoded, "S%c%c $%1X", reg, type, sk); | |
} | |
break; | |
} | |
break; | |
} | |
case OPC_INP: | |
case OPC_OUT: | |
case OPC_INA: | |
case OPC_ENA: | |
case OPC_ENQ: | |
case OPC_INQ: | |
sprintf(decoded, "%s $%02X", spc, delta); | |
break; | |
case OPC_INTER: | |
t = instr & (MOD_LP | MOD_XR | MOD_O_A | MOD_O_Q | MOD_O_M); | |
inter = interName[t >> 3]; | |
dest = destName[instr & (MOD_D_A | MOD_D_Q | MOD_D_M)]; | |
if (inter != NULL) | |
sprintf(decoded, "%s %s", inter, dest); | |
break; | |
case OPC_SHIFTS: | |
shift = shiftName[(instr & OPC_SHIFTMASK) >> 5]; | |
if (shift != NULL) | |
sprintf(decoded, "%s $%X", shift, instr & OPC_SHIFTCOUNT); | |
break; | |
} | |
} | |
if (dbg) { | |
if (INSTR_SET == INSTR_ENHANCED) | |
sprintf(buf, "%c %04X %04X %s %s %s", | |
prot, addr, instr, optional, optional2, decoded); | |
else | |
sprintf(buf, "%c %04X %04X %s %s", | |
prot, addr, instr, optional, decoded); | |
} else { | |
if (INSTR_SET == INSTR_ENHANCED) | |
sprintf(buf, "%c %04X %s %s %s", | |
prot, instr, optional, optional2, decoded); | |
else | |
sprintf(buf, "%c %04X %s %s", | |
prot, instr, optional, decoded); | |
} | |
if (targ) { | |
const char *rel = ""; | |
t_bool indJmp = FALSE; | |
uint16 taddr, taddr2, base; | |
switch (instr & OPC_MASK) { | |
case OPC_ADQ: | |
case OPC_LDQ: | |
case OPC_RAO: | |
case OPC_LDA: | |
case OPC_EOR: | |
case OPC_AND: | |
case OPC_SUB: | |
case OPC_ADD: | |
case OPC_SPA: | |
case OPC_STA: | |
case OPC_STQ: | |
case OPC_DVI: | |
case OPC_MUI: | |
if (((instr & (MOD_IN | MOD_I1 | MOD_I2)) == 0) || exec) | |
if (disEffectiveAddr(addr, instr, &base, &taddr) == SCPE_OK) { | |
more = cpu_dev.dctrl & DBG_FULL ? 2 : 1; | |
taddr2 = taddr; | |
if (((instr & (MOD_RE | MOD_IN | MOD_I1 | MOD_I2)) == MOD_RE) && | |
((sim_switches & SWMASK('R')) != 0)) { | |
taddr2 -= RelBase; | |
rel = "*"; | |
} | |
} | |
break; | |
case OPC_JMP: | |
if (((instr & (MOD_IN | MOD_I1 | MOD_I2)) == MOD_IN) & !dbg) { | |
if (disEffectiveAddr(addr, instr & ~MOD_IN, &base, &taddr) == SCPE_OK) { | |
taddr2 = taddr; | |
indJmp = TRUE; | |
if (((instr & MOD_RE) != 0) && ((sim_switches & SWMASK('R')) != 0)) { | |
taddr2 -= RelBase; | |
rel = "*"; | |
} | |
break; | |
} | |
} | |
/* FALLTHROUGH */ | |
case OPC_RTJ: | |
if (((instr & (MOD_IN | MOD_I1 | MOD_I2)) != 0) & !dbg) | |
break; | |
if (disEffectiveAddr(addr, instr, &base, &taddr) == SCPE_OK) { | |
more = cpu_dev.dctrl & DBG_FULL ? 2 : 1; | |
taddr2 = taddr; | |
if (((instr & (MOD_RE | MOD_IN | MOD_I1 | MOD_I2)) == MOD_RE) && | |
((sim_switches & SWMASK('R')) != 0)) { | |
taddr2 -= RelBase; | |
rel = "*"; | |
} | |
} | |
break; | |
case OPC_SPECIAL: | |
switch (instr & OPC_SPECIALMASK) { | |
case OPC_SLS: | |
break; | |
case OPC_SKIPS: | |
taddr = doADDinternal(MEMADDR(addr + 1), instr & OPC_SKIPCOUNT); | |
taddr2 = taddr; | |
if ((sim_switches & SWMASK('R')) != 0) | |
taddr2 -= RelBase; | |
more = 1; | |
break; | |
case OPC_SPB: | |
case OPC_CPB: | |
if (exec) { | |
taddr = Qreg; | |
taddr2 = taddr; | |
if ((sim_switches & SWMASK('R')) != 0) | |
taddr2 -= RelBase; | |
more = 1; | |
} | |
break; | |
case OPC_INP: | |
case OPC_OUT: | |
taddr = doADDinternal(addr, EXTEND8(instr & OPC_MODMASK)); | |
taddr2 = taddr; | |
if ((sim_switches & SWMASK('R')) != 0) | |
taddr2 -= RelBase; | |
more = 1; | |
break; | |
case OPC_EIN: | |
case OPC_IIN: | |
case OPC_INTER: | |
case OPC_INA: | |
case OPC_ENA: | |
case OPC_NOP: | |
case OPC_ENQ: | |
case OPC_INQ: | |
case OPC_EXI: | |
case OPC_SHIFTS: | |
break; | |
} | |
break; | |
} | |
if (more || indJmp) { | |
int i, count = 48 - strlen(buf); | |
for (i = 0; i < count; i++) | |
strcat(buf, " "); | |
buf += strlen(buf); | |
if (indJmp) { | |
sprintf(buf, "[ => (%04X%s)", taddr2, rel); | |
} else { | |
switch (more) { | |
case 1: | |
sprintf(buf, "[ => %04X%s %s {%04X}", taddr2, rel, | |
P[MEMADDR(taddr)] ? "(P)" : "", | |
LoadFromMem(taddr)); | |
break; | |
case 2: | |
sprintf(buf, "[ => %04X%s (B:%04X%s) %s {%04X}", | |
taddr2, rel, base, rel, | |
P[MEMADDR(taddr)] ? "(P)" : "", | |
LoadFromMem(taddr)); | |
break; | |
} | |
} | |
} | |
} | |
return consumed; | |
} |