/* | |
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_sym.c: symbolic assembler input for "deposit" command | |
*/ | |
#include "cdc1700_defs.h" | |
#include <ctype.h> | |
extern UNIT cpu_unit; | |
extern uint16 doADDinternal(uint16, uint16); | |
/* | |
* Symbol tables | |
*/ | |
#define I_DATA 0x10000 /* Data transmission */ | |
#define I_ARITH 0x20000 /* Arithmetic */ | |
#define I_LOG 0x30000 /* Logical */ | |
#define I_JUMP 0x40000 /* Jumps */ | |
#define I_REG 0x50000 /* Register reference */ | |
#define I_SKIP 0x60000 /* Skip */ | |
#define I_INTER 0x70000 /* Inter-register */ | |
#define I_SHIFT 0x80000 /* Shift */ | |
#define I_MASK 0xF0000 | |
#define I_DMASK 0xFFFF | |
/* | |
* Modifiers for I_REG addressing. | |
*/ | |
#define I_NONE 0x000000 /* No argument expected */ | |
#define I_REL 0x100000 /* 8-bit relative address */ | |
#define I_ABS 0x200000 /* 8-bit absolute value */ | |
#define I_SIGNED 0x300000 /* 8-bit signed value */ | |
#define I_MASK2 0x300000 | |
#define I_NOARG I_REG + I_NONE | |
static const char *opcode[] = { | |
"ADQ", "LDQ", "RAO", "LDA", | |
"EOR", "AND", "SUB", "ADD", | |
"SPA", "STA", "RTJ", "STQ", | |
"DVI", "MUI", "JMP", "SLS", | |
"SAZ", "SAN", "SAP", "SAM", | |
"SQZ", "SQN", "SQP", "SQM", | |
"SWS", "SWN", "SOV", "SNO", | |
"SPE", "SNP", "SPF", "SNF", | |
"INP", "OUT", "EIN", "IIN", | |
"ECA", "DCA", "SPB", "CPB", | |
"AAM", "AAQ", "AAB", "CLR", | |
"TCM", "TCQ", "TCB", "TCA", | |
"EAM", "EAQ", "EAB", "SET", | |
"TRM", "TRQ", "TRB", "TRA", | |
"LAM", "LAQ", "LAB", "CAM", | |
"CAQ", "CAB", "INA", "ENA", | |
"NOP", "ENQ", "INQ", "EXI", | |
"QRS", "ARS", "LRS", "QLS", | |
"ALS", "LLS", "ECA", "DCA", | |
NULL | |
}; | |
static const int32 opc_val[] = { | |
OPC_ADQ + I_ARITH, OPC_LDQ + I_DATA, OPC_RAO + I_ARITH, OPC_LDA + I_DATA, | |
OPC_EOR + I_LOG, OPC_AND + I_LOG, OPC_SUB + I_ARITH, OPC_ADD + I_ARITH, | |
OPC_SPA + I_DATA, OPC_STA + I_DATA, OPC_RTJ + I_JUMP, OPC_STQ + I_DATA, | |
OPC_DVI + I_ARITH, OPC_MUI + I_ARITH, OPC_JMP + I_JUMP, OPC_SLS + I_NOARG, | |
OPC_SAZ + I_SKIP, OPC_SAN + I_SKIP, OPC_SAP + I_SKIP, OPC_SAM + I_SKIP, | |
OPC_SQZ + I_SKIP, OPC_SQN + I_SKIP, OPC_SQP + I_SKIP, OPC_SQM + I_SKIP, | |
OPC_SWS + I_SKIP, OPC_SWN + I_SKIP, OPC_SOV + I_SKIP, OPC_SNO + I_SKIP, | |
OPC_SPE + I_SKIP, OPC_SNP + I_SKIP, OPC_SPF + I_SKIP, OPC_SNF + I_SKIP, | |
OPC_INP + I_REG + I_REL, OPC_OUT + I_REG + I_REL, OPC_EIN + I_NOARG, OPC_IIN + I_NOARG, | |
OPC_ECA + I_NOARG, OPC_DCA + I_NOARG, OPC_SPB + I_NOARG, OPC_CPB + I_NOARG, | |
OPC_AAM + I_INTER, OPC_AAQ + I_INTER, OPC_AAB + I_INTER, OPC_CLR + I_INTER, | |
OPC_TCM + I_INTER, OPC_TCQ + I_INTER, OPC_TCB + I_INTER, OPC_TCA + I_INTER, | |
OPC_EAM + I_INTER, OPC_EAQ + I_INTER, OPC_EAB + I_INTER, OPC_SET + I_INTER, | |
OPC_TRM + I_INTER, OPC_TRQ + I_INTER, OPC_TRB + I_INTER, OPC_TRA + I_INTER, | |
OPC_LAM + I_INTER, OPC_LAQ + I_INTER, OPC_LAB + I_INTER, OPC_CAM + I_INTER, | |
OPC_CAQ + I_INTER, OPC_CAB + I_INTER, OPC_INA + I_REG + I_SIGNED, OPC_ENA + I_REG + I_SIGNED, | |
OPC_NOP + I_NOARG, OPC_ENQ + I_REG + I_SIGNED, OPC_INQ + I_REG + I_SIGNED, OPC_EXI + I_REG + I_ABS, | |
OPC_QRS + I_SHIFT, OPC_ARS + I_SHIFT, OPC_LRS + I_SHIFT, OPC_QLS + I_SHIFT, | |
OPC_ALS + I_SHIFT, OPC_LLS + I_SHIFT, OPC_ECA + I_NOARG, OPC_DCA + I_NOARG | |
}; | |
/* | |
* Register (and pseudo-register) names. | |
*/ | |
static const char *regname[] = { | |
"A", "Q", "M", "I", "B", | |
NULL | |
}; | |
/* | |
* Usage value for each usage type (0 means invalid). | |
*/ | |
static uint16 instIndex[] = { | |
0x0000, MOD_I1, 0x0000, MOD_I2, MOD_I1 | MOD_I2 | |
}; | |
static uint16 instInter[] = { | |
MOD_D_A, MOD_D_Q, MOD_D_M, 0x0000, 0x0000 | |
}; | |
#define NEXTSYMBOL(mchar) \ | |
cptr = get_glyph(cptr, gbuf, mchar); \ | |
for (j = 0; (regname[j] != NULL) && (strcmp(regname[j], gbuf) != 0); j++); \ | |
if (regname[j] == NULL) return SCPE_ARG | |
t_stat parse_sym(CONST char *cptr, t_addr addr, UNIT *uptr, t_value *val, int32 sw) | |
{ | |
int32 cflag, i, j, l, rdx; | |
t_bool neg, cons; | |
t_value temp; | |
t_stat r, sta = SCPE_OK; | |
char gbuf[CBUFSIZE], mode; | |
const char *cptr2; | |
cflag = (uptr == NULL) || (uptr == &cpu_unit); | |
while (isspace(*cptr)) | |
cptr++; | |
if ((sw & SWMASK('A')) || ((*cptr == '\'') && cptr++)) { | |
/* ASCII character */ | |
if (cptr[0] == 0) | |
return SCPE_ARG; | |
val[0] = (t_value)cptr[0] | 0200; | |
return SCPE_OK; | |
} | |
if ((sw & SWMASK('C')) || ((*cptr == '"') && cptr++)) { | |
/* Packed ASCII characters (2 to a word) */ | |
if (cptr[0] == 0) | |
val[0] = (((t_value)cptr[0] | 0200) << 8) | ((t_value)cptr[1] | 0200); | |
return SCPE_OK; | |
} | |
cptr = get_glyph(cptr, gbuf, 0); | |
l = strlen(gbuf); | |
if ((gbuf[l - 1] == '*') || (gbuf[l - 1] == '-') || (gbuf[l - 1] == '+')) { | |
mode = gbuf[l - 1]; | |
gbuf[l - 1] = '\0'; | |
} else mode = 0; | |
for (i = 0; (opcode[i] != NULL) && (strcmp(opcode[i], gbuf) != 0); i++); | |
if (opcode[i] == NULL) | |
return SCPE_ARG; | |
val[0] = opc_val[i] & I_DMASK; | |
while (isspace(*cptr)) | |
cptr++; | |
cons = neg = FALSE; | |
rdx = 10; | |
switch (opc_val[i] & I_MASK) { | |
case I_DATA: | |
case I_ARITH: | |
case I_LOG: | |
if (*cptr == '=') { | |
cons = TRUE; | |
cptr++; | |
if (*cptr == '-') { | |
neg = TRUE; | |
cptr++; | |
} | |
if (*cptr == '$') { | |
rdx = 16; | |
cptr++; | |
} | |
temp = get_uint(cptr, rdx, MAXNEG, &r); | |
if (r != SCPE_OK) | |
return r; | |
if (neg) { | |
if (temp > MAXPOS) | |
return SCPE_ARG; | |
temp = (~temp) & 0xFFFF; | |
} | |
if ((mode == '*') || (mode == '-')) | |
return SCPE_ARG; | |
/* | |
* Constant addressing mode always occupies 2 words. | |
*/ | |
val[1] = temp; | |
return -1; | |
} | |
/* FALLTHROUGH */ | |
case I_JUMP: | |
if (*cptr == '(') { | |
cptr++; | |
if (*cptr == '$') { | |
rdx = 16; | |
cptr++; | |
} | |
temp = strtotv(cptr, &cptr2, rdx); | |
if ((cptr == cptr2) || (*cptr2++ != ')')) | |
return SCPE_ARG; | |
cptr = (char *)cptr2; | |
val[0] |= MOD_IN; | |
} else { | |
if (*cptr == '$') { | |
rdx = 16; | |
cptr++; | |
} | |
temp = strtotv(cptr, &cptr2, rdx); | |
if (cptr == cptr2) | |
return SCPE_ARG; | |
cptr = (char *)cptr2; | |
} | |
if (mode == '*') { | |
temp = doADDinternal(temp, ~addr); | |
if (CANEXTEND8(temp)) | |
temp &= 0xFF; | |
val[0] |= MOD_RE; | |
} | |
if ((mode == '-') && ((temp & 0xFF00) != 0)) | |
return SCPE_ARG; | |
/* | |
* Check for indexing modifier | |
*/ | |
if (*cptr++ == ',') { | |
NEXTSYMBOL(0); | |
if (instIndex[j] == 0) | |
return SCPE_ARG; | |
val[0] |= instIndex[j]; | |
} | |
if (((temp & 0xFF00) != 0) || (mode == '+')) { | |
val[1] = temp; | |
return -1; | |
} | |
val[0] |= temp; | |
return SCPE_OK; | |
case I_REG: | |
switch (opc_val[i] & I_MASK2) { | |
case I_NONE: | |
return SCPE_OK; | |
case I_REL: | |
case I_SIGNED: | |
if (*cptr == '-') { | |
neg = TRUE; | |
cptr++; | |
} | |
if (*cptr == '$') { | |
rdx = 16; | |
cptr++; | |
} | |
temp = get_uint(cptr, rdx, 127, &r); | |
if (r != SCPE_OK) | |
return r; | |
if (neg) | |
temp = (~temp) & 0xFF; | |
val[0] |= temp; | |
return SCPE_OK; | |
case I_ABS: | |
if (*cptr == '$') { | |
rdx = 16; | |
cptr++; | |
} | |
temp = get_uint(cptr, rdx, 255, &r); | |
if (r != SCPE_OK) | |
return r; | |
val[0] |= temp; | |
return SCPE_OK; | |
} | |
break; | |
case I_SKIP: | |
if (*cptr == '$') { | |
rdx = 16; | |
cptr++; | |
} | |
temp = get_uint(cptr, rdx, 15, &r); | |
if (r != SCPE_OK) | |
return r; | |
val[0] |= temp; | |
return SCPE_OK; | |
case I_INTER: | |
if (*cptr != 0) { | |
do { | |
NEXTSYMBOL(','); | |
if (instInter[j] == 0) | |
return SCPE_ARG; | |
val[0] |= instInter[j]; | |
} while (*cptr != 0); | |
} | |
return SCPE_OK; | |
case I_SHIFT: | |
if (*cptr == '$') { | |
rdx = 16; | |
cptr++; | |
} | |
temp = get_uint(cptr, rdx, 31, &r); | |
if (r != SCPE_OK) | |
return r; | |
val[0] |= temp; | |
return SCPE_OK; | |
} | |
return SCPE_ARG; | |
} |