| /* | |
| Copyright (c) 2015-2016, 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" | |
| }; | |
| /* | |
| * Generate a single line of text for an instruction. Format is: | |
| * | |
| * c xxxx yyyy zzzz <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 inctruction 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 mode[8], optional[8], temp[8], decoded[64]; | |
| const char *spc, *shift, *inter, *dest; | |
| uint16 instr = LoadFromMem(addr); | |
| uint16 delta = instr & OPC_ADDRMASK; | |
| uint16 t; | |
| strcpy(optional, " "); | |
| strcpy(decoded, "UNDEF"); | |
| if ((instr & OPC_MASK) != 0) { | |
| strcpy(decoded, opName[(instr & OPC_MASK) >> 12]); | |
| if ((instr & MOD_RE) == 0) | |
| strcpy(mode, delta == 0 ? "+ " : "- "); | |
| else strcpy(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)) | |
| strcat(mode, "="); | |
| break; | |
| } | |
| if (delta == 0) { | |
| consumed++; | |
| sprintf(optional, "%04X", LoadFromMem(addr + 1)); | |
| sprintf(temp, "$%04X", LoadFromMem(addr + 1)); | |
| } else sprintf(temp, "$%02X", delta); | |
| strcat(decoded, mode); | |
| if ((instr & MOD_IN) != 0) | |
| strcat(decoded, "("); | |
| strcat(decoded, temp); | |
| if ((instr & MOD_IN) != 0) | |
| strcat(decoded, ")"); | |
| strcat(decoded, idxName[(instr & (MOD_I1 | MOD_I2)) >> 8]); | |
| } else { | |
| spc = spcName[(instr & OPC_SPECIALMASK) >> 8]; | |
| switch (instr & OPC_SPECIALMASK) { | |
| case OPC_EIN: | |
| case OPC_IIN: | |
| case OPC_SPB: | |
| case OPC_CPB: | |
| case OPC_NOP: | |
| sprintf(decoded, "%s", spc); | |
| break; | |
| case OPC_EXI: | |
| sprintf(decoded, "%s $%02X", spc, instr & OPC_MODMASK); | |
| break; | |
| case OPC_SKIPS: | |
| sprintf(decoded, "%s $%01X", | |
| skpName[(instr & OPC_SKIPMASK) >> 4], instr & OPC_SKIPCOUNT); | |
| break; | |
| case OPC_SLS: | |
| 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) { | |
| sprintf(buf, "%c %04X %04X %s %s", | |
| prot, addr, instr, optional, decoded); | |
| } else { | |
| sprintf(buf, "%c %04X %s %s", | |
| prot, instr, optional, decoded); | |
| } | |
| if (targ) { | |
| const char *rel = ""; | |
| t_bool indJmp = FALSE; | |
| uint8 more = 0; | |
| 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: | |
| 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 { | |
| if (more == 1) | |
| sprintf(buf, "[ => %04X%s %s {%04X}", taddr2, rel, | |
| P[MEMADDR(taddr)] ? "(P)" : "", | |
| LoadFromMem(taddr)); | |
| else sprintf(buf, "[ => %04X%s (B:%04X%s) %s {%04X}", | |
| taddr2, rel, base, rel, | |
| P[MEMADDR(taddr)] ? "(P)" : "", | |
| LoadFromMem(taddr)); | |
| } | |
| } | |
| } | |
| return consumed; | |
| } |