/* altair_sys.c: MITS Altair system interface

   (C) Copyright 1997 by Charles E. Owen
   Commercial use prohibited
*/


#include <ctype.h>

#include "altair_defs.h"


extern DEVICE cpu_dev;
extern DEVICE dsk_dev;
extern UNIT cpu_unit;
extern REG cpu_reg[];
extern DEVICE sio_dev;
extern DEVICE ptr_dev;
extern DEVICE ptp_dev;
extern DEVICE lpt_dev;
extern unsigned char M[];
extern int32 saved_PC;
extern char *get_glyph (char *cptr, char *gbuf, char term);
extern unsigned int32 get_uint (char *cptr, int32 radix, unsigned int32 max,
	int32 *status);

/* 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[] = "Altair 8800";

REG *sim_PC = &cpu_reg[0];

int32 sim_emax = 4;

DEVICE *sim_devices[] = { &cpu_dev, &sio_dev, &ptr_dev,
	&ptp_dev, &dsk_dev, NULL };

const char *sim_stop_messages[] = {
	"Unknown error",
    "Unknown I/O Instruction",
	"HALT instruction",
    "Breakpoint",
    "Invalid Opcode" };

static const char *opcode[] = {
"NOP", "LXI B", "STAX B", "INX B",				/* 000-003 */
"INR B", "DCR B", "MVI B", "RLC",       		/* 004-007 */
"???", "DAD B", "LDAX B", "DCX B",      		/* 010-013 */
"INR C", "DCR C", "MVI C", "RRC",       		/* 014-017 */
"???", "LXI D", "STAX D", "INX D",      		/* 020-023 */
"INR D", "DCR D", "MVI D", "RAL",      			/* 024-027 */
"???", "DAD D", "LDAX D", "DCX D",      		/* 030-033 */
"INR E", "DCR E", "MVI E", "RAR",      			/* 034-037 */
"???", "LXI H", "SHLD", "INX H",      			/* 040-043 */
"INR H", "DCR H", "MVI H", "DAA",      			/* 044-047 */
"???", "DAD H", "LHLD", "DCX H",      			/* 050-053 */
"INR L", "DCR L", "MVI L", "CMA",      			/* 054-057 */
"???", "LXI SP", "STA", "INX SP",      			/* 060-063 */
"INR M", "DCR M", "MVI M", "STC",      			/* 064-067 */
"???", "DAD SP", "LDA", "DCX SP",      			/* 070-073 */
"INR A", "DCR A", "MVI A", "CMC",      			/* 074-077 */
"MOV B,B", "MOV B,C", "MOV B,D", "MOV B,E",		/* 100-103 */
"MOV B,H", "MOV B,L", "MOV B,M", "MOV B,A",     /* 104-107 */
"MOV C,B", "MOV C,C", "MOV C,D", "MOV C,E",     /* 110-113 */
"MOV C,H", "MOV C,L", "MOV C,M", "MOV C,A",     /* 114-117 */
"MOV D,B", "MOV D,C", "MOV D,D", "MOV D,E",     /* 120-123 */
"MOV D,H", "MOV D,L", "MOV D,M", "MOV D,A",     /* 124-127 */
"MOV E,B", "MOV E,C", "MOV E,D", "MOV E,E",     /* 130-133 */
"MOV E,H", "MOV E,L", "MOV E,M", "MOV E,A",     /* 134-137 */
"MOV H,B", "MOV H,C", "MOV H,D", "MOV H,E",     /* 140-143 */
"MOV H,H", "MOV H,L", "MOV H,M", "MOV H,A",     /* 144-147 */
"MOV L,B", "MOV L,C", "MOV L,D", "MOV L,E",     /* 150-153 */
"MOV L,H", "MOV L,L", "MOV L,M", "MOV L,A",     /* 154-157 */
"MOV M,B", "MOV M,C", "MOV M,D", "MOV M,E",     /* 160-163 */
"MOV M,H", "MOV M,L", "HLT", "MOV M,A",      	/* 164-167 */
"MOV A,B", "MOV A,C", "MOV A,D", "MOV A,E",     /* 170-173 */
"MOV A,H", "MOV A,L", "MOV A,M", "MOV A,A",     /* 174-177 */
"ADD B", "ADD C", "ADD D", "ADD E",				/* 200-203 */
"ADD H", "ADD L", "ADD M", "ADD A",       		/* 204-207 */
"ADC B", "ADC C", "ADC D", "ADC E",      		/* 210-213 */
"ADC H", "ADC L", "ADC M", "ADC A",       		/* 214-217 */
"SUB B", "SUB C", "SUB D", "SUB E",      		/* 220-223 */
"SUB H", "SUB L", "SUB M", "SUB A",      		/* 224-227 */
"SBB B", "SBB C", "SBB D", "SBB E",      		/* 230-233 */
"SBB H", "SBB L", "SBB M", "SBB A",      		/* 234-237 */
"ANA B", "ANA C", "ANA D", "ANA E",      		/* 240-243 */
"ANA H", "ANA L", "ANA M", "ANA A",      		/* 244-247 */
"XRA B", "XRA C", "XRA D", "XRA E",      		/* 250-253 */
"XRA H", "XRA L", "XRA M", "XRA A",      		/* 254-257 */
"ORA B", "ORA C", "ORA D", "ORA E",      		/* 260-263 */
"ORA H", "ORA L", "ORA M", "ORA A",      		/* 264-267 */
"CMP B", "CMP C", "CMP D", "CMP E",      		/* 270-273 */
"CMP H", "CMP L", "CMP M", "CMP A",      		/* 274-277 */
"RNZ", "POP B", "JNZ", "JMP",					/* 300-303 */
"CNZ", "PUSH B", "ADI", "RST 0",       			/* 304-307 */
"RZ", "RET", "JZ", "???",      					/* 310-313 */
"CZ", "CALL", "ACI", "RST 1",       			/* 314-317 */
"RNC", "POP D", "JNC", "OUT",      				/* 320-323 */
"CNC", "PUSH D", "SUI", "RST 2",      			/* 324-327 */
"RC", "???", "JC", "IN",      					/* 330-333 */
"CC", "???", "SBI", "RST 3",      				/* 334-337 */
"RPO", "POP H", "JPO", "XTHL",      			/* 340-343 */
"CPO", "PUSH H", "ANI", "RST 4",      			/* 344-347 */
"RPE", "PCHL", "JPE", "XCHG",     				/* 350-353 */
"CPE", "???", "XRI", "RST 5",      				/* 354-357 */
"RP", "POP PSW", "JP", "DI",      				/* 360-363 */
"CP", "PUSH PSW", "ORI", "RST 6",      			/* 364-367 */
"RM", "SPHL", "JM", "EI",      					/* 370-373 */
"CM", "???", "CPI", "RST 7",      				/* 374-377 */
 };

int32 oplen[256] = {
1,3,1,1,1,1,2,1,0,1,1,1,1,1,2,1,0,3,1,1,1,1,2,1,0,1,1,1,1,1,2,1,
0,3,3,1,1,1,2,1,0,1,3,1,1,1,2,1,0,3,3,1,1,1,2,1,0,1,3,1,1,1,2,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,3,3,3,1,2,1,1,1,3,0,3,3,2,1,1,1,3,2,3,1,2,1,1,0,3,2,3,0,2,1,
1,1,3,1,3,1,2,1,1,1,3,1,3,0,2,1,1,1,3,1,3,1,2,1,1,1,3,1,3,0,2,1 };

/* This is the binary loader.  The input file is considered to be
   a string of literal bytes with no format special format. The
   load starts at the current value of the PC.
*/

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 = saved_PC;
while ((i = getc (fileref)) != EOF) {
		M[addr] = i;
		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 cflag, c1, c2, inst, adr;

cflag = (uptr == NULL) || (uptr == &cpu_unit);
c1 = (val[0] >> 8) & 0177;
c2 = val[0] & 0177;
if (sw & SWMASK ('A')) {
	fprintf (of, (c2 < 040)? "<%03o>": "%c", c2);
	return SCPE_OK;  }
if (sw & SWMASK ('C')) {
	fprintf (of, (c1 < 040)? "<%03o>": "%c", c1);
	fprintf (of, (c2 < 040)? "<%03o>": "%c", c2);
	return SCPE_OK;  }
if (!(sw & SWMASK ('M'))) return SCPE_ARG;
inst = val[0];
fprintf (of, "%s", opcode[inst]);
if (oplen[inst] == 2) {
	if (strchr(opcode[inst], ' ') != NULL)
    		fprintf (of, ",");
      else fprintf (of, " ");
	fprintf (of, "%o", val[1]);
}
if (oplen[inst] == 3) {
    adr = val[1] & 0xFF;
    adr |= (val[2] << 8) & 0xff00;
	if (strchr(opcode[inst], ' ') != NULL)
    		fprintf (of, ",");
       else fprintf (of, " ");
    fprintf (of, "%o", adr);
}
return -(oplen[inst] - 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;
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,
   or numeric (including spaces).
*/

while (1) {
	if (*cptr == ',' || *cptr == '\0' ||
    	 isdigit(*cptr))
         	break;
	gbuf[i] = toupper(*cptr);
    cptr++;
    i++;
}

/* Allow for RST which has numeric as part of opcode */

if (toupper(gbuf[0]) == 'R' &&
	toupper(gbuf[1]) == 'S' &&
    toupper(gbuf[2]) == 'T') {
	gbuf[i] = toupper(*cptr);
    cptr++;
    i++;
}

/* Allow for 'MOV' which is only opcode that has comma in it. */

if (toupper(gbuf[0]) == 'M' &&
	toupper(gbuf[1]) == 'O' &&
    toupper(gbuf[2]) == 'V') {
	gbuf[i] = toupper(*cptr);
    cptr++;
    i++;
    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 < 256; j++) {
	if (strcmp(gbuf, opcode[j]) == 0)
    	break;
}
if (j > 255)        /* not found */
	return SCPE_ARG;

val[0] = j;         /* store opcode */
if (oplen[j] < 2)   /* if 1-byter we are done */
	return SCPE_OK;
if (*cptr == ',') cptr++;
cptr = get_glyph(cptr, gbuf, 0);	/* get address */
sscanf(gbuf, "%o", &r);
if (oplen[j] == 2) {
	val[1] = r & 0xFF;
    return (-1);
}
val[1] = r & 0xFF;
val[2] = (r >> 8) & 0xFF;
return (-2);
}
