/* ibms3_sys.c: IBM System/3 system interface | |
(C) Copyright 2001 by Charles E. Owen | |
Commercial use prohibited | |
*/ | |
#include <ctype.h> | |
#include "s3_defs.h" | |
extern DEVICE cpu_dev; | |
extern DEVICE pkb_dev; | |
extern DEVICE cdr_dev; | |
extern DEVICE cdp_dev; | |
extern DEVICE stack_dev; | |
extern DEVICE lpt_dev; | |
extern DEVICE r1_dev; | |
extern DEVICE f1_dev; | |
extern DEVICE r2_dev; | |
extern DEVICE f2_dev; | |
extern UNIT cpu_unit; | |
extern REG cpu_reg[]; | |
extern unsigned char M[]; | |
extern int32 saved_PC, IAR[]; | |
extern char ebcdic_to_ascii[256]; | |
char *parse_addr(char *cptr, char *gbuf, int32 *addr, int32 *addrtype); | |
int32 printf_sym (FILE *of, char *strg, int32 addr, unsigned int32 *val, | |
UNIT *uptr, int32 sw); | |
/* SCP data structures | |
sim_name simulator name string | |
sim_PC pointer to saved PC register descriptor | |
sim_emax number of words needed for examine | |
sim_devices array of pointers to simulated devices | |
sim_stop_messages array of pointers to stop messages | |
sim_load binary loader | |
*/ | |
char sim_name[] = "System/3"; | |
REG *sim_PC = &cpu_reg[0]; | |
int32 sim_emax = 6; | |
DEVICE *sim_devices[] = { &cpu_dev, | |
&pkb_dev, | |
&cdr_dev, | |
&cdp_dev, | |
&stack_dev, | |
&lpt_dev, | |
&r1_dev, | |
&f1_dev, | |
&r2_dev, | |
&f2_dev, | |
NULL }; | |
const char *sim_stop_messages[] = { | |
"Unknown error", | |
"Unknown I/O Instruction", | |
"HALT instruction", | |
"Breakpoint", | |
"Invalid Opcode", | |
"Invalid Qbyte", | |
"Invalid Address", | |
"Invalid Device Command", | |
"ATTN Card Reader" | |
}; | |
/* This is the opcode master defintion table. Each possible opcode mnemonic | |
is defined here, with enough information to translate to and from | |
symbolic to binary machine code. | |
First field is the opcode's mnemonic | |
Second field is the hex of the right nybble of the binary opcode | |
Third field is the Q code for those with implicit Q codes | |
Fourth field is the symbolic format of the operands: | |
0 - (Q-byte),(R-byte) | |
1 - (Q-byte),(Address) | |
2 - (Address),(Address),(Qbyte) | |
3 - (Address),(Qbyte) | |
4 - (device),(modifier),(function) -- these 3 make up qbyte | |
5 - (device),(modifier),(function),(control) | |
6 - (device),(modifier),(function),(Address) | |
7 - (displacement) -- Q byte is implicit in opcode | |
8 - (address) -- Qbyte is implicit in opcode | |
9 - (Address),(Address) -- Qbyte is implicit in opcode | |
Fifth Field is the group number: | |
0 - Command Group (left op nybble is F) | |
1 - One Address Operations A (Left Nybble C, D, or E) | |
2 - Two Address Operations (Left Nybble 0,1,2,4,5,6,8,9, or A) | |
3 - One Address Operations B (left Nybble 3, 7, or B) | |
There is duplication in this table -- IBM defines different opcodes | |
that resolve to the same binary machine instruction -- e.g. JE and | |
JZ. On input this is no problem, on output, define the one you | |
want to appear first, the second will never appear on output. | |
*/ | |
int32 nopcode = 75; | |
struct opdef opcode[75] = { | |
"HPL", 0x00,0,0,0, /* Halt Program Level */ | |
"A", 0x06,0,1,3, /* Add to Register: A R,AADD */ | |
"ST", 0x04,0,1,3, /* Store Register */ | |
"L", 0x05,0,1,3, /* Load Register */ | |
"LA", 0x02,0,1,1, /* Load Address */ | |
"ZAZ", 0x04,0,2,2, /* Zero and Add Zoned */ | |
"AZ", 0x06,0,2,2, /* Add Zoned Decimal */ | |
"SZ", 0x07,0,2,2, /* Subtract Zoned Decimal */ | |
"ALC", 0x0E,0,2,2, /* Add Logical: ALC BADD,AADD,LEN */ | |
"SLC", 0x0F,0,2,2, /* Sub Logical: SLC BADD,AADD,LEN */ | |
"MVC", 0x0C,0,2,2, /* Move Chars MVX BADD,AADD,LEN */ | |
"ED", 0x0A,0,2,2, /* Edit: ED BADD,AADD,LEN */ | |
"ITC", 0x0B,0,2,2, /* Insert Chars: ITC BADD,AADD,LEN */ | |
"CLC", 0x0D,0,2,2, /* Compare Logical: CLC BADD,AADD,LEN */ | |
"MVI", 0x0C,0,3,3, /* Move Immediate */ | |
"SBN", 0x0A,0,3,3, /* Set Bits On */ | |
"SBF", 0x0B,0,3,3, /* Set Bits Off */ | |
"CLI", 0x0D,0,3,3, /* Compare Immediate */ | |
"TBN", 0x08,0,3,3, /* Test Bits On */ | |
"TBF", 0x09,0,3,3, /* Test Bits Off */ | |
"APL", 0x01,0,4,0, /* Advance Program Level */ | |
"SIO", 0x03,0,5,0, /* Start I/O */ | |
"SNS", 0x00,0,6,3, /* Sense I/O */ | |
"LIO", 0x01,0,6,3, /* Load I/O */ | |
"TIO", 0x01,0,6,1, /* Test I/O */ | |
"J", 0x02,0,7,0, /* Jump Unconditional */ | |
"J", 0x02,0x87,7,0, /* Alternate J */ | |
"JH", 0x02,132,7,0, /* Jump if High */ | |
"JL", 0x02,130,7,0, /* Jump if Low */ | |
"JE", 0x02,129,7,0, /* Jump if Equal */ | |
"JNH", 0x02,4,7,0, /* Jump if Not High */ | |
"JNL", 0x02,2,7,0, /* Jump if Not Low */ | |
"JNE", 0x02,1,7,0, /* Jump if Not Equal */ | |
"JOZ", 0x02,136,7,0, /* Jump if Overflow Zoned */ | |
"JOL", 0x02,160,7,0, /* Jump if Overflow Logical */ | |
"JNOZ", 0x02,8,7,0, /* Jump if No Overflow Zoned */ | |
"JNOL", 0x02,32,7,0, /* Jump if No Overflow Logical */ | |
"JT", 0x02,16,7,0, /* Jump if True */ | |
"JF", 0x02,144,7,0, /* Jump if False */ | |
"JP", 0x02,132,7,0, /* Jump if Plus */ | |
"JM", 0x02,130,7,0, /* Jump if Minus */ | |
"JZ", 0x02,129,7,0, /* Jump if Zero */ | |
"JNP", 0x02,4,7,0, /* Jump if Not Plus */ | |
"JNM", 0x02,2,7,0, /* Jump if Not Minus */ | |
"JNZ", 0x02,1,7,0, /* Jump if Not Zero */ | |
"NOPJ", 0x02,0x80,7,0, /* Never Jump - NOP */ | |
"B", 0x00,0x00,8,1, /* Branch Unconditional */ | |
"B", 0x00,0x87,8,1, /* Alternate B */ | |
"BH", 0x00,0x84,8,1, /* Branch if High */ | |
"BL", 0x00,0x82,8,1, /* Branch if Low */ | |
"BE", 0x00,0x81,8,1, /* Branch if Equal */ | |
"BNH", 0x00,0x04,8,1, /* Branch if Not High */ | |
"BNL", 0x00,0x02,8,1, /* Branch if Not Low */ | |
"BNE", 0x00,0x01,8,1, /* Branch if Not Equal */ | |
"BOZ", 0x00,0x88,8,1, /* Branch if Overflow Zoned */ | |
"BOL", 0x00,0xA0,8,1, /* Branch if Overflow Logical */ | |
"BNOZ", 0x00,0x08,8,1, /* Branch if No Overflow Zoned */ | |
"BNOL", 0x00,0x20,8,1, /* Branch if No Overflow Logical */ | |
"BT", 0x00,0x10,8,1, /* Branch if True */ | |
"BF", 0x00,0x90,8,1, /* Branch if False */ | |
"BP", 0x00,0x84,8,1, /* Branch if Plus */ | |
"BM", 0x00,0x82,8,1, /* Branch if Minus */ | |
"BZ", 0x00,0x81,8,1, /* Branch if Zero */ | |
"BNP", 0x00,0x04,8,1, /* Branch if Not Plus */ | |
"BNM", 0x00,0x02,8,1, /* Branch if Not Minus */ | |
"BNZ", 0x00,0x01,8,1, /* Branch if Not Zero */ | |
"NOPB", 0x00,0x80,8,1, /* Never Branch - NOP */ | |
"MZZ", 0x08,0,9,2, /* Move Zone to Zone */ | |
"MNZ", 0x08,1,9,2, /* Move Numeric to Zone */ | |
"MZN", 0x08,2,9,2, /* Move Zone to Numeric */ | |
"MNN", 0x08,3,9,2, /* Move Numeric to Numeric */ | |
"MVX", 0x08,0,2,2, /* Move Hex: MVX BADD,AADD,CODE */ | |
"JC", 0x02,0,3,0, /* Jump on Specified Condition bits */ | |
"BC", 0x00,0,3,1, /* Branch on Specified Condition */ | |
"***", 0x00,0,0,0 | |
}; | |
int32 regcode[15] = { 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01, | |
0x80, 0xC0, 0xA0, 0x90, 0x88, 0x84, 0x82, 0x81 }; | |
char regname[15][8] = { "(P2IAR)", | |
"(P1IAR)", | |
"(IAR)", | |
"(ARR)", | |
"(PSR)", | |
"(XR2)", | |
"(XR1)", | |
"(IAR0)", | |
"(IAR1)", | |
"(IAR2)", | |
"(IAR3)", | |
"(IAR4)", | |
"(IAR5)", | |
"(IAR6)", | |
"(IAR7)" }; | |
/* This is the binary loader. The input file is considered to be | |
a string of literal bytes with no special format. The | |
load starts at the current value of the P1IAR. | |
*/ | |
int32 sim_load (FILE *fileref, char *cptr, char *fnam, int flag) | |
{ | |
int32 i, addr = 0, cnt = 0; | |
if ((*cptr != 0) || (flag != 0)) return SCPE_ARG; | |
addr = IAR[8]; | |
while ((i = getc (fileref)) != EOF) { | |
M[addr] = i & 0xff; | |
addr++; | |
cnt++; | |
} /* end while */ | |
printf ("%d Bytes loaded.\n", cnt); | |
return (SCPE_OK); | |
} | |
/* Symbolic output | |
Inputs: | |
*of = output stream | |
addr = current PC | |
*val = pointer to values | |
*uptr = pointer to unit | |
sw = switches | |
Outputs: | |
status = error code | |
*/ | |
int32 fprint_sym (FILE *of, int32 addr, unsigned int32 *val, | |
UNIT *uptr, int32 sw) | |
{ | |
int32 r; | |
char strg[256]; | |
strcpy(strg, ""); | |
r = printf_sym(of, strg, addr, val, uptr, sw); | |
if (sw & SWMASK ('A')) | |
strcpy(strg, ""); | |
else | |
fprintf(of, "%s", strg); | |
return (r); | |
} | |
int32 printf_sym (FILE *of, char *strg, int32 addr, unsigned int32 *val, | |
UNIT *uptr, int32 sw) | |
{ | |
int32 cflag, c1, c2, group, len1, len2, inst, aaddr, baddr; | |
int32 oplen, groupno, i, j, vpos, qbyte, da, m, n; | |
char bld[128], bldaddr[32], boperand[32], aoperand[32]; | |
int32 blk[16], blt[16]; | |
int32 blkadd; | |
cflag = (uptr == NULL) || (uptr == &cpu_unit); | |
c1 = val[0] & 0xff; | |
if (sw & SWMASK ('A')) { | |
for (i = 0; i < 16; i++) { | |
blkadd = addr + (i*16); | |
for (j = 0; j < 16; j++) { | |
blk[j] = M[blkadd+j] & 0xff; | |
c2 = ebcdic_to_ascii[blk[j]]; | |
if (c2 < 040 || c2 > 0177 || blk[j] == 07) { | |
blt[j] = '.'; | |
} else { | |
blt[j] = c2; | |
} | |
} | |
if (i == 0) { | |
fprintf(of, "%02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X [%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c]\n ", | |
blk[0], blk[1], blk[2], blk[3], blk[4], blk[5], blk[6], blk[7], | |
blk[8], blk[9], blk[10], blk[11], blk[12], blk[13], blk[14], blk[15], | |
blt[0], blt[1], blt[2], blt[3], blt[4], blt[5], blt[6], blt[7], | |
blt[8], blt[9], blt[10], blt[11], blt[12], blt[13], blt[14], blt[15]); | |
} else { | |
fprintf(of, "%X\t%02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X [%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c]\n ", | |
blkadd, blk[0], blk[1], blk[2], blk[3], blk[4], blk[5], blk[6], blk[7], | |
blk[8], blk[9], blk[10], blk[11], blk[12], blk[13], blk[14], blk[15], | |
blt[0], blt[1], blt[2], blt[3], blt[4], blt[5], blt[6], blt[7], | |
blt[8], blt[9], blt[10], blt[11], blt[12], blt[13], blt[14], blt[15]); | |
} | |
} | |
return SCPE_OK; } | |
if (sw & SWMASK ('C')) { | |
c2 = ebcdic_to_ascii[c1]; | |
if (c2 < 040 || c2 > 0177) { | |
sprintf(strg, "<%02X>", c1 & 0xff); | |
} else { | |
sprintf (strg, "%c", c2 & 0xff); | |
} | |
return SCPE_OK; } | |
if (!(sw & SWMASK ('M'))) return SCPE_ARG; | |
inst = val[0] & 0x0f; | |
len1 = (val[0] >> 6) & 3; | |
len2 = (val[0] >> 4) & 3; | |
group = (val[0] >> 4) & 0x0f; | |
qbyte = val[1]; | |
/* Get total length of instruction */ | |
if (group == 0x0f) { | |
oplen = 3; | |
} else { | |
oplen = 2; | |
if (len1 == 0) oplen += 2; | |
if (len1 == 1 || len1 == 2) oplen++; | |
if (len2 == 0) oplen += 2; | |
if (len2 == 1 || len2 == 2) oplen++; | |
} | |
/* Find which group it belongs to */ | |
switch (group) { | |
case 0x0f: | |
groupno = 0; | |
break; | |
case 0x0c: | |
case 0x0d: | |
case 0x0e: | |
groupno = 1; | |
break; | |
case 0x03: | |
case 0x07: | |
case 0x0b: | |
groupno = 3; | |
break; | |
default: | |
groupno = 2; | |
break; | |
} | |
/* find the table entry */ | |
for (i = 0; i < nopcode; i++) { | |
if (opcode[i].form < 7) { /* Explicit Q */ | |
if (opcode[i].group == groupno && | |
opcode[i].opmask == inst) break; | |
} else { /* Implicit Q */ | |
if (opcode[i].group == groupno && | |
opcode[i].opmask == inst && | |
opcode[i].q == qbyte) break; | |
} | |
} | |
/* print the opcode */ | |
if (i >= nopcode) { | |
sprintf(strg, "%02X", val[0]); | |
oplen = 1; | |
} else { | |
sprintf(bld, "%s ", opcode[i].op); | |
/* Extract the addresses into aaddr and baddr */ | |
strcpy(aoperand, "ERROR"); | |
strcpy(boperand, "ERROR"); | |
vpos = 2; | |
aaddr = baddr = 0; | |
switch (len1) { | |
case 0: | |
baddr = ((val[vpos] << 8) & 0xff00) | (val[vpos + 1] & 0x00ff); | |
sprintf(boperand, "%04X", baddr); | |
vpos = 4; | |
break; | |
case 1: | |
baddr = val[vpos] & 255; | |
sprintf(boperand, "(%02X,XR1)", baddr); | |
vpos = 3; | |
break; | |
case 2: | |
baddr = val[vpos] & 255; | |
sprintf(boperand, "(%02X,XR2)", baddr); | |
vpos = 3; | |
break; | |
default: | |
baddr = 0; | |
break; | |
} | |
switch (len2) { | |
case 0: | |
aaddr = ((val[vpos] << 8) & 0xff00) | (val[vpos + 1] & 0x00ff); | |
if (group == 0x0C || group == 0x0D || group == 0x0E) | |
sprintf(boperand, "%04X", aaddr); | |
else | |
sprintf(aoperand, "%04X", aaddr); | |
break; | |
case 1: | |
aaddr = val[vpos] & 255; | |
if (group == 0x0C || group == 0x0D || group == 0x0E) | |
sprintf(boperand, "(%02X,XR1)", aaddr); | |
else | |
sprintf(aoperand, "(%02X,XR1)", aaddr); | |
break; | |
case 2: | |
aaddr = val[vpos] & 255; | |
if (group == 0x0C || group == 0x0D || group == 0x0E) | |
sprintf(boperand, "(%02X,XR2)", aaddr); | |
else | |
sprintf(aoperand, "(%02X,XR2)", aaddr); | |
break; | |
default: | |
aaddr = 0; | |
break; | |
} | |
/* Display the operands in the correct format */ | |
da = (qbyte >> 4) & 0x0f; | |
m = (qbyte >> 3) & 0x01; | |
n = (qbyte) & 0x07; | |
switch (opcode[i].form) { | |
case 0: | |
sprintf(bldaddr, "%02X,%02X", qbyte, val[2]); | |
break; | |
case 1: | |
if (inst == 2 || inst == 4 || inst == 5 || inst == 6) { | |
for (i = 0; i < 16; i++) { | |
if (regcode[i] == qbyte) | |
break; | |
} | |
if (i < 16) { | |
sprintf(bldaddr, "%s,%s", regname[i], boperand); | |
} else { | |
sprintf(bldaddr, "%02X,%s", qbyte, boperand); | |
} | |
} else { | |
sprintf(bldaddr, "%02X,%s", qbyte, boperand); | |
} | |
break; | |
case 2: | |
if (inst > 9 || inst == 4 || inst == 6 || inst == 7) | |
qbyte++; /* special +1 for length display */ | |
sprintf(bldaddr, "%s,%s,%d", boperand, aoperand, qbyte); | |
break; | |
case 3: | |
if (strcmp(opcode[i].op, "JC") == 0) { | |
sprintf(bldaddr, "%04X,%02X", addr+oplen+val[2], qbyte); | |
} else { | |
sprintf(bldaddr, "%s,%02X", boperand, qbyte); | |
} | |
break; | |
case 4: | |
sprintf(bldaddr, "%d,%d,%d", da, m, n); | |
break; | |
case 5: | |
sprintf(bldaddr, "%d,%d,%d,%02X", da, m, n, val[2]); | |
break; | |
case 6: | |
sprintf(bldaddr, "%d,%d,%d,%s", da, m, n, boperand); | |
break; | |
case 7: | |
sprintf(bldaddr, "%04X", addr+oplen+val[2]); | |
break; | |
case 8: | |
sprintf(bldaddr, "%s", boperand); | |
break; | |
default: | |
sprintf(bldaddr, "%s,%s", boperand, aoperand); | |
break; | |
} | |
sprintf(strg, "%s%s", bld, bldaddr); | |
} | |
return -(oplen - 1); | |
} | |
/* 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 | |
*/ | |
int32 parse_sym (char *cptr, int32 addr, UNIT *uptr, unsigned int32 *val, int32 sw) | |
{ | |
int32 cflag, i = 0, j, r, oplen, addtyp, saveaddr, vptr; | |
char gbuf[CBUFSIZE]; | |
cflag = (uptr == NULL) || (uptr == &cpu_unit); | |
while (isspace (*cptr)) cptr++; /* absorb spaces */ | |
if ((sw & SWMASK ('A')) || ((*cptr == '\'') && cptr++)) { /* ASCII char? */ | |
if (cptr[0] == 0) return SCPE_ARG; /* must have 1 char */ | |
val[0] = (unsigned int) cptr[0]; | |
return SCPE_OK; } | |
if ((sw & SWMASK ('C')) || ((*cptr == '"') && cptr++)) { /* ASCII string? */ | |
if (cptr[0] == 0) return SCPE_ARG; /* must have 1 char */ | |
val[0] = ((unsigned int) cptr[0] << 8) + (unsigned int) cptr[1]; | |
return SCPE_OK; } | |
/* An instruction: get opcode (all characters until null, comma, left paren, | |
or numeric (including spaces). | |
*/ | |
while (1) { | |
if (*cptr == ',' || *cptr == '\0' || *cptr == '(' || | |
isdigit(*cptr)) | |
break; | |
gbuf[i] = toupper(*cptr); | |
cptr++; | |
i++; | |
} | |
/* kill trailing spaces if any */ | |
gbuf[i] = '\0'; | |
for (j = i - 1; gbuf[j] == ' '; j--) { | |
gbuf[j] = '\0'; | |
} | |
/* find opcode in table */ | |
for (j = 0; j < nopcode; j++) { | |
if (strcmp(gbuf, opcode[j].op) == 0) | |
break; | |
} | |
if (j >= nopcode) /* not found */ | |
return SCPE_ARG; | |
oplen = 2; /* start with op & q */ | |
val[0] = opcode[j].opmask; /* store opcode right nybble */ | |
switch (opcode[j].form) { /* Get operands based on operand format */ | |
case 0: /* Single Byte Operand */ | |
if (*cptr == ',') cptr++; | |
cptr = get_glyph(cptr, gbuf, ','); /* Get Q Byte */ | |
sscanf(gbuf, "%x", &r); | |
val[1] = r; | |
if (*cptr == ',') cptr++; | |
cptr = get_glyph(cptr, gbuf, 0); /* Get R Byte */ | |
sscanf(gbuf, "%x", &r); | |
val[2] = r; | |
oplen = 3; | |
val[0] = 0xf0 | opcode[j].opmask; | |
break; | |
case 1: | |
if (*cptr == ',') cptr++; | |
cptr = get_glyph(cptr, gbuf, ','); | |
if (opcode[j].opmask == 2 || | |
opcode[j].opmask == 4 || | |
opcode[j].opmask == 5 || | |
opcode[j].opmask == 6) { | |
if (isdigit(gbuf[0])) { | |
sscanf(gbuf, "%x", &r); | |
} else { | |
for (i = 0; i < 16; i++) { | |
if (strcmp(gbuf, regname[i]) == 0) | |
break; | |
} | |
if (i < 16) { | |
r = regcode[i]; | |
} else { | |
return SCPE_ARG; | |
} | |
} | |
} else { | |
sscanf(gbuf, "%x", &r); | |
} | |
if (r > 255) return SCPE_ARG; | |
val[1] = r; | |
if (*cptr == ',') cptr++; | |
cptr = parse_addr(cptr, gbuf, &addr, &addtyp); | |
switch(addtyp) { | |
case 0: | |
val[2] = (addr >> 8) & 0x00ff; | |
val[3] = addr & 0xff; | |
oplen = 4; | |
if (opcode[j].group == 1) | |
val[0] = 0xC0 | opcode[j].opmask; | |
else | |
val[0] = 0x30 | opcode[j].opmask; | |
break; | |
case 1: | |
val[2] = addr & 0xff; | |
oplen = 3; | |
if (opcode[j].group == 1) | |
val[0] = 0xD0 | opcode[j].opmask; | |
else | |
val[0] = 0x70 | opcode[j].opmask; | |
break; | |
case 2: | |
val[2] = addr & 0xff; | |
oplen = 3; | |
if (opcode[j].group == 1) | |
val[0] = 0xE0 | opcode[j].opmask; | |
else | |
val[0] = 0xB0 | opcode[j].opmask; | |
break; | |
default: | |
return SCPE_ARG; | |
break; | |
} | |
break; | |
case 2: | |
oplen = 2; | |
cptr = parse_addr(cptr, gbuf, &addr, &addtyp); | |
switch(addtyp) { | |
case 0: | |
val[2] = (addr >> 8) & 0xff; | |
val[3] = addr & 0xff; | |
oplen += 2; | |
vptr = 4; | |
val[0] = 0x00 | opcode[j].opmask; | |
break; | |
case 1: | |
val[2] = addr & 0xff; | |
oplen += 1; | |
vptr = 3; | |
val[0] = 0x40 | opcode[j].opmask; | |
break; | |
case 2: | |
val[2] = addr & 0xff; | |
oplen += 1; | |
vptr = 3; | |
val[0] = 0x80 | opcode[j].opmask; | |
break; | |
default: | |
return SCPE_ARG; | |
break; | |
} | |
if (*cptr == ',') cptr++; | |
cptr = parse_addr(cptr, gbuf, &addr, &addtyp); | |
switch(addtyp) { | |
case 0: | |
val[vptr] = (addr >> 8) & 0xff; | |
val[vptr+1] = addr & 0xff; | |
oplen += 2; | |
break; | |
case 1: | |
val[vptr] = addr & 0xff; | |
oplen += 1; | |
val[0] = 0x10 | val[0]; | |
break; | |
case 2: | |
val[vptr] = addr & 0xff; | |
oplen += 1; | |
val[0] = 0x20 | val[0]; | |
break; | |
default: | |
return SCPE_ARG; | |
break; | |
} | |
if (*cptr == ',') cptr++; | |
cptr = get_glyph(cptr, gbuf, 0); | |
sscanf(gbuf, "%d", &r); | |
if (opcode[j].opmask > 9 || | |
opcode[j].opmask == 4 || | |
opcode[j].opmask == 6 || | |
opcode[j].opmask == 7) r--; /* special: length -1 */ | |
val[1] = r; | |
if (*cptr == ',') cptr++; | |
break; | |
case 3: | |
saveaddr = addr; | |
if (*cptr == ',') cptr++; | |
cptr = parse_addr(cptr, gbuf, &addr, &addtyp); | |
switch(addtyp) { | |
case 0: | |
if (opcode[j].group == 0) { /* Group 0 form 3 is JC with explicit Q */ | |
if (*cptr == ',') cptr++; | |
cptr = get_glyph(cptr, gbuf, 0); | |
sscanf(gbuf, "%x", &r); | |
if ((addr - (saveaddr+3)) > 255 || (addr - (saveaddr+3)) < 1) | |
return SCPE_ARG; | |
val[2] = addr - (saveaddr+3); | |
val[1] = r; | |
val[0] = 0xf0 | opcode[j].opmask; | |
oplen = 3; | |
} else { | |
val[2] = (addr >> 8) & 0x00ff; | |
val[3] = addr & 0xff; | |
oplen = 4; | |
if (opcode[j].group == 1) | |
val[0] = 0xC0 | opcode[j].opmask; | |
else | |
val[0] = 0x30 | opcode[j].opmask; | |
} | |
break; | |
case 1: | |
val[2] = addr & 0xff; | |
oplen = 3; | |
if (opcode[j].group == 1) | |
val[0] = 0xD0 | opcode[j].opmask; | |
else | |
val[0] = 0x70 | opcode[j].opmask; | |
break; | |
case 2: | |
val[2] = addr & 0xff; | |
oplen = 3; | |
if (opcode[j].group == 1) | |
val[0] = 0xE0 | opcode[j].opmask; | |
else | |
val[0] = 0xB0 | opcode[j].opmask; | |
break; | |
default: | |
return SCPE_ARG; | |
break; | |
} | |
if (*cptr == ',') cptr++; | |
cptr = get_glyph(cptr, gbuf, 0); | |
sscanf(gbuf, "%x", &r); | |
if (r > 255) return SCPE_ARG; | |
val[1] = r; | |
break; | |
case 4: | |
if (*cptr == ',') cptr++; | |
cptr = get_glyph(cptr, gbuf, ','); | |
sscanf(gbuf, "%d", &r); | |
if (r > 15) return SCPE_ARG; | |
val[1] = (r << 4) & 0xf0; | |
val[0] = 0xf0 | opcode[j].opmask; | |
if (*cptr == ',') cptr++; | |
cptr = get_glyph(cptr, gbuf, ','); | |
sscanf(gbuf, "%d", &r); | |
if (r > 1) return SCPE_ARG; | |
val[1] |= (r << 3) & 0x08; | |
if (*cptr == ',') cptr++; | |
cptr = get_glyph(cptr, gbuf, 0); | |
sscanf(gbuf, "%d", &r); | |
if (r > 7) return SCPE_ARG; | |
val[1] |= r & 0x07; | |
val[2] = 0; | |
oplen = 3; | |
break; | |
case 5: | |
if (*cptr == ',') cptr++; | |
cptr = get_glyph(cptr, gbuf, ','); | |
sscanf(gbuf, "%d", &r); | |
if (r > 15) return SCPE_ARG; | |
val[1] = (r << 4) & 0xf0; | |
val[0] = 0xf0 | opcode[j].opmask; | |
if (*cptr == ',') cptr++; | |
cptr = get_glyph(cptr, gbuf, ','); | |
sscanf(gbuf, "%d", &r); | |
if (r > 1) return SCPE_ARG; | |
val[1] |= (r << 3) & 0x08; | |
if (*cptr == ',') cptr++; | |
cptr = get_glyph(cptr, gbuf, ','); | |
sscanf(gbuf, "%d", &r); | |
if (r > 7) return SCPE_ARG; | |
val[1] |= r & 0x07; | |
if (*cptr == ',') cptr++; | |
cptr = get_glyph(cptr, gbuf, 0); | |
sscanf(gbuf, "%x", &r); | |
if (r > 255) return SCPE_ARG; | |
val[2] = r; | |
oplen = 3; | |
break; | |
case 6: | |
if (*cptr == ',') cptr++; | |
cptr = get_glyph(cptr, gbuf, ','); | |
sscanf(gbuf, "%d", &r); | |
if (r > 15) return SCPE_ARG; | |
val[1] = (r << 4) & 0xf0; | |
if (*cptr == ',') cptr++; | |
cptr = get_glyph(cptr, gbuf, ','); | |
sscanf(gbuf, "%d", &r); | |
if (r > 1) return SCPE_ARG; | |
val[1] |= (r << 3) & 0x08; | |
if (*cptr == ',') cptr++; | |
cptr = get_glyph(cptr, gbuf, ','); | |
sscanf(gbuf, "%d", &r); | |
if (r > 7) return SCPE_ARG; | |
val[1] |= r & 0x07; | |
if (*cptr == ',') cptr++; | |
cptr = parse_addr(cptr, gbuf, &addr, &addtyp); | |
switch(addtyp) { | |
case 0: | |
val[2] = (addr >> 8) & 0x00ff; | |
val[3] = addr & 0xff; | |
oplen = 4; | |
if (opcode[j].group == 1) | |
val[0] = 0xC0 | opcode[j].opmask; | |
else | |
val[0] = 0x30 | opcode[j].opmask; | |
break; | |
case 1: | |
val[2] = addr & 0xff; | |
oplen = 3; | |
if (opcode[j].group == 1) | |
val[0] = 0xD0 | opcode[j].opmask; | |
else | |
val[0] = 0x70 | opcode[j].opmask; | |
break; | |
case 2: | |
val[2] = addr & 0xff; | |
oplen = 3; | |
if (opcode[j].group == 1) | |
val[0] = 0xE0 | opcode[j].opmask; | |
else | |
val[0] = 0xB0 | opcode[j].opmask; | |
break; | |
default: | |
return SCPE_ARG; | |
break; | |
} | |
break; | |
case 7: | |
if (*cptr == ',') cptr++; | |
cptr = get_glyph(cptr, gbuf, 0); | |
sscanf(gbuf, "%x", &r); | |
if ((r - (addr+3)) > 255 || (r - (addr+3)) < 1) return SCPE_ARG; | |
val[2] = r - (addr+3); | |
val[1] = opcode[j].q; | |
val[0] = 0xf0 | opcode[j].opmask; | |
oplen = 3; | |
break; | |
case 8: | |
if (*cptr == ',') cptr++; | |
cptr = parse_addr(cptr, gbuf, &addr, &addtyp); | |
switch(addtyp) { | |
case 0: | |
val[2] = (addr >> 8) & 0x00ff; | |
val[3] = addr & 0xff; | |
oplen = 4; | |
val[0] = 0xC0 | opcode[j].opmask; | |
break; | |
case 1: | |
val[2] = addr & 0xff; | |
oplen = 3; | |
val[0] = 0xD0 | opcode[j].opmask; | |
break; | |
case 2: | |
val[2] = addr & 0xff; | |
oplen = 3; | |
val[0] = 0xE0 | opcode[j].opmask; | |
break; | |
default: | |
return SCPE_ARG; | |
break; | |
} | |
val[1] = opcode[j].q; | |
break; | |
case 9: | |
oplen = 2; | |
val[0] = 0; | |
cptr = parse_addr(cptr, gbuf, &addr, &addtyp); | |
switch(addtyp) { | |
case 0: | |
val[2] = (addr >> 8) & 0xff; | |
val[3] = addr & 0xff; | |
oplen += 2; | |
vptr = 4; | |
val[0] = 0x00 | opcode[j].opmask; | |
break; | |
case 1: | |
val[2] = addr & 0xff; | |
oplen += 1; | |
vptr = 3; | |
val[0] = 0x40 | opcode[j].opmask; | |
break; | |
case 2: | |
val[2] = addr & 0xff; | |
oplen += 1; | |
vptr = 3; | |
val[0] = 0x80 | opcode[j].opmask; | |
break; | |
default: | |
return SCPE_ARG; | |
break; | |
} | |
if (*cptr == ',') cptr++; | |
cptr = parse_addr(cptr, gbuf, &addr, &addtyp); | |
switch(addtyp) { | |
case 0: | |
val[vptr] = (addr >> 8) & 0xff; | |
val[vptr+1] = addr & 0xff; | |
oplen += 2; | |
break; | |
case 1: | |
val[vptr] = addr & 0xff; | |
oplen += 1; | |
val[0] = 0x10 | val[0]; | |
break; | |
case 2: | |
val[vptr] = addr & 0xff; | |
oplen += 1; | |
val[0] = 0x20 | val[0]; | |
break; | |
default: | |
return SCPE_ARG; | |
break; | |
} | |
val[1] = opcode[j].q; | |
break; | |
default: | |
break; | |
} | |
return (-(oplen-1)); | |
} | |
char *parse_addr(char *cptr, char *gbuf, int32 *addr, int32 *addrtype) | |
{ | |
int32 nybble = 0; | |
char temp[32]; | |
cptr = get_glyph(cptr, gbuf, ','); | |
if (gbuf[0] == '(') { /* XR relative */ | |
strcpy(temp, gbuf+1); | |
sscanf(temp, "%x", addr); | |
if (*cptr == ',') cptr++; | |
cptr = get_glyph(cptr, gbuf, ','); | |
nybble = -1; | |
if (strcmp(gbuf, "XR1)") == 0) | |
nybble = 1; | |
if (strcmp(gbuf, "XR2)") == 0) | |
nybble = 2; | |
} else { /* Direct */ | |
sscanf(gbuf, "%x", addr); | |
nybble = 0; | |
} | |
*addrtype = nybble; | |
return cptr; | |
} | |