mirror of
https://github.com/RevCurtisP/C02.git
synced 2024-11-25 21:33:44 +00:00
1358 lines
27 KiB
C
1358 lines
27 KiB
C
/* Assembler for the MOS Technology 650X series of microprocessors
|
|
* Written by J. H. Van Ornum (201) 949-1781
|
|
* AT&T Bell Laboratories
|
|
* Holmdel, NJ
|
|
*
|
|
*
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <ctype.h>
|
|
|
|
#define LAST_CH_POS 132
|
|
#define SFIELD 23
|
|
#define STABSZ 6000
|
|
#define SBOLSZ 20
|
|
|
|
/*
|
|
* symbol flags
|
|
*/
|
|
#define DEFZRO 2 /* defined - page zero address */
|
|
#define MDEF 3 /* multiply defined */
|
|
#define UNDEF 1 /* undefined - may be zero page */
|
|
#define DEFABS 4 /* defined - two byte address */
|
|
#define UNDEFAB 5 /* undefined - two byte address */
|
|
|
|
/*
|
|
* operation code flags
|
|
*/
|
|
#define PSEUDO 0x6000
|
|
#define CLASS1 0x2000
|
|
#define CLASS2 0x4000
|
|
#define IMM1 0x1000 /* opval + 0x00 2 byte */
|
|
#define IMM2 0x0800 /* opval + 0x08 2 byte */
|
|
#define ABS 0x0400 /* opval + 0x0C 3 byte */
|
|
#define ZER 0x0200 /* opval + 0x04 2 byte */
|
|
#define INDX 0x0100 /* opval + 0x00 2 byte */
|
|
#define ABSY2 0x0080 /* opval + 0x1C 3 byte */
|
|
#define INDY 0x0040 /* opval + 0x10 2 byte */
|
|
#define ZERX 0x0020 /* opval + 0x14 2 byte */
|
|
#define ABSX 0x0010 /* opval + 0x1C 3 byte */
|
|
#define ABSY 0x0008 /* opval + 0x18 3 byte */
|
|
#define ACC 0x0004 /* opval + 0x08 1 byte */
|
|
#define IND 0x0002 /* opval + 0x2C 3 byte */
|
|
#define ZERY 0x0001 /* opval + 0x14 2 byte */
|
|
|
|
/*
|
|
* pass flags
|
|
*/
|
|
#define FIRST_PASS 0
|
|
#define LAST_PASS 1
|
|
#define DONE 2
|
|
|
|
FILE *optr;
|
|
FILE *iptr;
|
|
int dflag; /* debug flag */
|
|
int errcnt; /* error counter */
|
|
int hash_tbl[128]; /* pointers to starting links in symtab */
|
|
char hex[5]; /* hexadecimal character buffer */
|
|
int iflag; /* ignore .nlst flag */
|
|
int lablptr; /* label pointer into symbol table */
|
|
int lflag; /* disable listing flag */
|
|
int loccnt; /* location counter */
|
|
int nflag; /* normal/split address mode */
|
|
int nxt_free; /* next free location in symtab */
|
|
int objcnt; /* object byte counter */
|
|
int oflag; /* object output flag */
|
|
int opflg; /* operation code flags */
|
|
int opval; /* operation code value */
|
|
int pass; /* pass counter */
|
|
char prlnbuf[LAST_CH_POS+1]; /* print line buffer */
|
|
int sflag; /* symbol table output flag */
|
|
int slnum; /* source line number counter */
|
|
char symtab[STABSZ]; /* symbol table */
|
|
/* struct sym_tab */
|
|
/* { char size; */
|
|
/* char chars[size]; */
|
|
/* char flag; */
|
|
/* int value; */
|
|
/* int next_pointer */
|
|
/* } */
|
|
char symbol0[SBOLSZ]; /* temporary symbol storage */
|
|
int udtype; /* undefined symbol type */
|
|
int undef; /* undefined symbol in expression flg */
|
|
int value; /* operand field value */
|
|
char zpref; /* zero page reference flag */
|
|
|
|
|
|
#define A 0x20)+('A'&0x1f))
|
|
#define B 0x20)+('B'&0x1f))
|
|
#define C 0x20)+('C'&0x1f))
|
|
#define D 0x20)+('D'&0x1f))
|
|
#define E 0x20)+('E'&0x1f))
|
|
#define F 0x20)+('F'&0x1f))
|
|
#define G 0x20)+('G'&0x1f))
|
|
#define H 0x20)+('H'&0x1f))
|
|
#define I 0x20)+('I'&0x1f))
|
|
#define J 0x20)+('J'&0x1f))
|
|
#define K 0x20)+('K'&0x1f))
|
|
#define L 0x20)+('L'&0x1f))
|
|
#define M 0x20)+('M'&0x1f))
|
|
#define N 0x20)+('N'&0x1f))
|
|
#define O 0x20)+('O'&0x1f))
|
|
#define P 0x20)+('P'&0x1f))
|
|
#define Q 0x20)+('Q'&0x1f))
|
|
#define R 0x20)+('R'&0x1f))
|
|
#define S 0x20)+('S'&0x1f))
|
|
#define T 0x20)+('T'&0x1f))
|
|
#define U 0x20)+('U'&0x1f))
|
|
#define V 0x20)+('V'&0x1f))
|
|
#define W 0x20)+('W'&0x1f))
|
|
#define X 0x20)+('X'&0x1f))
|
|
#define Y 0x20)+('Y'&0x1f))
|
|
#define Z 0x20)+('Z'&0x1f))
|
|
|
|
#define OPSIZE 63
|
|
|
|
int optab[] = /* nmemonic operation code table */
|
|
{ /* '.' = 31, '*' = 30, '=' = 29 */
|
|
((0*0x20)+(29)),PSEUDO,1,
|
|
((((0*0x20)+(30))*0x20)+(29)),PSEUDO,3,
|
|
((((((0*A*D*C,IMM2|ABS|ZER|INDX|INDY|ZERX|ABSX|ABSY,0x61,
|
|
((((((0*A*N*D,IMM2|ABS|ZER|INDX|INDY|ZERX|ABSX|ABSY,0x21,
|
|
((((((0*A*S*L,ABS|ZER|ZERX|ABSX|ACC,0x02,
|
|
((((((0*B*C*C,CLASS2,0x90,
|
|
((((((0*B*C*S,CLASS2,0xb0,
|
|
((((((0*B*E*Q,CLASS2,0xf0,
|
|
((((((0*B*I*T,ABS|ZER,0x20,
|
|
((((((0*B*M*I,CLASS2,0x30,
|
|
((((((0*B*N*E,CLASS2,0xd0,
|
|
((((((0*B*P*L,CLASS2,0x10,
|
|
((((((0*B*R*K,CLASS1,0x00,
|
|
((((((0*B*V*C,CLASS2,0x50,
|
|
((((((0*B*V*S,CLASS2,0x70,
|
|
((((((0*C*L*C,CLASS1,0x18,
|
|
((((((0*C*L*D,CLASS1,0xd8,
|
|
((((((0*C*L*I,CLASS1,0x58,
|
|
((((((0*C*L*V,CLASS1,0xb8,
|
|
((((((0*C*M*P,IMM2|ABS|ZER|INDX|INDY|ZERX|ABSX|ABSY,0xc1,
|
|
((((((0*C*P*X,IMM1|ABS|ZER,0xe0,
|
|
((((((0*C*P*Y,IMM1|ABS|ZER,0xc0,
|
|
((((((0*D*E*C,ABS|ZER|ZERX|ABSX,0xc2,
|
|
((((((0*D*E*X,CLASS1,0xca,
|
|
((((((0*D*E*Y,CLASS1,0x88,
|
|
((((((0*E*O*R,IMM2|ABS|ZER|INDX|INDY|ZERX|ABSX|ABSY,0x41,
|
|
((((((0*I*N*C,ABS|ZER|ZERX|ABSX,0xe2,
|
|
((((((0*I*N*X,CLASS1,0xe8,
|
|
((((((0*I*N*Y,CLASS1,0xc8,
|
|
((((((0*J*M*P,ABS|IND,0x40,
|
|
((((((0*J*S*R,ABS,0x14,
|
|
((((((0*L*D*A,IMM2|ABS|ZER|INDX|INDY|ZERX|ABSX|ABSY,0xa1,
|
|
((((((0*L*D*X,IMM1|ABS|ZER|ABSY2|ZERY,0xa2,
|
|
((((((0*L*D*Y,IMM1|ABS|ZER|ABSX|ZERX,0xa0,
|
|
((((((0*L*S*R,ABS|ZER|ZERX|ABSX|ACC,0x42,
|
|
((((((0*N*O*P,CLASS1,0xea,
|
|
((((((0*O*R*A,IMM2|ABS|ZER|INDX|INDY|ZERX|ABSX|ABSY,0x01,
|
|
((((((0*P*H*A,CLASS1,0x48,
|
|
((((((0*P*H*P,CLASS1,0x08,
|
|
((((((0*P*L*A,CLASS1,0x68,
|
|
((((((0*P*L*P,CLASS1,0x28,
|
|
((((((0*R*O*L,ABS|ZER|ZERX|ABSX|ACC,0x22,
|
|
((((((0*R*O*R,ABS|ZER|ZERX|ABSX|ACC,0x62,
|
|
((((((0*R*T*I,CLASS1,0x40,
|
|
((((((0*R*T*S,CLASS1,0x60,
|
|
((((((0*S*B*C,IMM2|ABS|ZER|INDX|INDY|ZERX|ABSX|ABSY,0xe1,
|
|
((((((0*S*E*C,CLASS1,0x38,
|
|
((((((0*S*E*D,CLASS1,0xf8,
|
|
((((((0*S*E*I,CLASS1,0x78,
|
|
((((((0*S*T*A,ABS|ZER|INDX|INDY|ZERX|ABSX|ABSY,0x81,
|
|
((((((0*S*T*X,ABS|ZER|ZERY,0x82,
|
|
((((((0*S*T*Y,ABS|ZER|ZERX,0x80,
|
|
((((((0*T*A*X,CLASS1,0xaa,
|
|
((((((0*T*A*Y,CLASS1,0xa8,
|
|
((((((0*T*S*X,CLASS1,0xba,
|
|
((((((0*T*X*A,CLASS1,0x8a,
|
|
((((((0*T*X*S,CLASS1,0x9a,
|
|
((((((0*T*Y*A,CLASS1,0x98,
|
|
((((((0*0x20)+(31))*W*O^((((0*R*D,PSEUDO,2, /* 0x7cab */
|
|
((((((0*0x20)+(31))*B*Y^((((0*T*E,PSEUDO,0, /* 0x7edc */
|
|
((((((0*0x20)+(31))*D*B^((((0*Y*T,PSEUDO,6, /* 0x7fb6 */
|
|
((((((0*0x20)+(31))*N*L^((((0*S*T,PSEUDO,5, /* 0x7fb8 */
|
|
((((((0*0x20)+(31))*L*I^((((0*S*T,PSEUDO,4, /* 0x7ffd */
|
|
0x7fff,0,0,
|
|
0x7fff,0,0
|
|
};
|
|
|
|
int step[] =
|
|
{
|
|
3*((OPSIZE+1)/2),
|
|
3*((((OPSIZE+1)/2)+1)/2),
|
|
3*((((((OPSIZE+1)/2)+1)/2)+1)/2),
|
|
3*((((((((OPSIZE+1)/2)+1)/2)+1)/2)+1)/2),
|
|
3*(2),
|
|
3*(1),
|
|
0
|
|
};
|
|
|
|
|
|
#define CPMEOF EOF
|
|
|
|
/*
|
|
* Two changes to version 1.4 have been made to "port" as6502 to CP/M(tm).
|
|
* A "tolower()" function call was add to the command line processing
|
|
* code to (re)map the command line arguments to lower case (CP/M
|
|
* converts all command line arguments to upper case). The readline()
|
|
* function has code added to "ignore" the '\r' character (CP/M includes
|
|
* the \r character along with \n).
|
|
*
|
|
* Also, the ability to process multiple files on the command line has been
|
|
* added. Now one can do, for example:
|
|
*
|
|
* as6502 -nisvo header.file source.file data.file ...
|
|
*
|
|
* George V. Wilder
|
|
* IX 1A-360 x1937
|
|
* ihuxp!gvw1
|
|
*/
|
|
|
|
/*
|
|
* This Macintosh port (1.41) by RTK, Feb. 27, 1995. Added Mac console
|
|
* control and removed CP/M checks.
|
|
*
|
|
*/
|
|
|
|
main(argc, argv)
|
|
int argc;
|
|
char *argv[];
|
|
{
|
|
char c;
|
|
int cnt;
|
|
int i;
|
|
int ac;
|
|
char **av;
|
|
|
|
while (--argc > 0 && (*++argv)[0] == '-') {
|
|
for (i = 1; (c = tolower((*argv)[i])) != '\0'; i++) {
|
|
if (c == 'd') /* debug flag */
|
|
dflag++;
|
|
if (c == 'i') /* ignore .nlst flag */
|
|
iflag++;
|
|
if (c == 'l') /* disable listing flag */
|
|
lflag--;
|
|
if (c == 'n') /* normal/split address mode */
|
|
nflag++;
|
|
if (c == 'o') /* object output flag */
|
|
oflag++;
|
|
if (c == 's') /* list symbol table flag */
|
|
sflag++;
|
|
if (c == 'v') /* print assembler version */
|
|
fprintf(stdout, "as6502 - version 4.1b - 11/22/84 - JHV [gvw]\n");
|
|
}
|
|
}
|
|
ac = argc;
|
|
av = argv;
|
|
pass = FIRST_PASS;
|
|
for (i = 0; i < 128; i++)
|
|
hash_tbl[i] = -1;
|
|
errcnt = loccnt = slnum = 0;
|
|
while (pass != DONE) {
|
|
initialize(ac, av, argc);
|
|
if(pass == LAST_PASS && ac == argc)
|
|
errcnt = loccnt = slnum = 0;
|
|
while (readline() != -1)
|
|
assemble();
|
|
if (errcnt != 0) {
|
|
pass = DONE;
|
|
fprintf(stdout, "Terminated with error counter = %d\n", errcnt);
|
|
}
|
|
switch (pass) {
|
|
case FIRST_PASS:
|
|
--ac;
|
|
++av;
|
|
if(ac == 0) {
|
|
pass = LAST_PASS;
|
|
if (lflag == 0)
|
|
lflag++;
|
|
ac = argc;
|
|
av = argv;
|
|
}
|
|
break;
|
|
case LAST_PASS:
|
|
--ac;
|
|
++av;
|
|
if(ac == 0) {
|
|
pass = DONE;
|
|
if (sflag != 0)
|
|
stprnt();
|
|
}
|
|
}
|
|
wrapup();
|
|
if ((dflag != 0) && (pass == LAST_PASS)) {
|
|
fprintf(stdout, "nxt_free = %d\n", nxt_free);
|
|
cnt = 0;
|
|
for (i = 0; i < 128; i++)
|
|
if (hash_tbl[i] == -1)
|
|
cnt++;
|
|
fprintf(stdout, "%d unused hash table pointers out of 128\n", cnt);
|
|
}
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
/* initialize opens files */
|
|
|
|
initialize(ac, av, argc)
|
|
int ac;
|
|
char *av[];
|
|
int argc;
|
|
{
|
|
|
|
if (ac == 0) {
|
|
fprintf(stdout, "Invalid argument count (%d).\n", argc);
|
|
exit(1);
|
|
}
|
|
if ((iptr = fopen(*av, "r")) == NULL) {
|
|
fprintf(stdout, "Open error for file '%s'.\n", *av);
|
|
exit(1);
|
|
}
|
|
if ((pass == LAST_PASS) && (oflag != 0) && ac == argc) {
|
|
if ((optr = fopen("6502.out", "w")) == NULL) {
|
|
fprintf(stdout, "Create error for object file 6502.out.\n");
|
|
exit(1);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* readline reads and formats an input line */
|
|
|
|
int field[] =
|
|
{
|
|
SFIELD,
|
|
SFIELD + 8,
|
|
SFIELD + 14,
|
|
SFIELD + 23,
|
|
SFIELD + 43,
|
|
SFIELD + 75
|
|
};
|
|
|
|
readline()
|
|
{
|
|
int i; /* pointer into prlnbuf */
|
|
int j; /* pointer to current field start */
|
|
int ch; /* current character */
|
|
int cmnt; /* comment line flag */
|
|
int spcnt; /* consecutive space counter */
|
|
int string; /* ASCII string flag */
|
|
int temp1; /* temp used for line number conversion */
|
|
|
|
temp1 = ++slnum;
|
|
for (i = 0; i < LAST_CH_POS; i++)
|
|
prlnbuf[i] = ' ';
|
|
i = 3;
|
|
while (temp1 != 0) { /* put source line number into prlnbuf */
|
|
prlnbuf[i--] = temp1 % 10 + '0';
|
|
temp1 /= 10;
|
|
}
|
|
i = SFIELD;
|
|
cmnt = spcnt = string = 0;
|
|
j = 1;
|
|
while ((ch = getc(iptr)) != '\n') {
|
|
prlnbuf[i++] = ch;
|
|
if ((ch == ' ') && (string == 0)) {
|
|
if (spcnt != 0)
|
|
--i;
|
|
else if (cmnt == 0) {
|
|
++spcnt;
|
|
if (i < field[j])
|
|
i = field[j];
|
|
if (++j > 3) {
|
|
spcnt = 0;
|
|
++cmnt;
|
|
}
|
|
}
|
|
}
|
|
else if (ch == '\t') {
|
|
prlnbuf[i - 1] = ' ';
|
|
spcnt = 0;
|
|
if (cmnt == 0) {
|
|
if (i < field[j])
|
|
i = field[j];
|
|
if (++j > 3)
|
|
++cmnt;
|
|
}
|
|
else i = (i + 8) & 0x78;
|
|
}
|
|
else if ((ch == ';') && (string == 0)) {
|
|
spcnt = 0;
|
|
if (i == SFIELD + 1)
|
|
++cmnt;
|
|
else if (prlnbuf[i - 2] != '\'') {
|
|
++cmnt;
|
|
prlnbuf[i-1] = ' ';
|
|
if (i < field[3])
|
|
i = field[3];
|
|
prlnbuf[i++] = ';';
|
|
}
|
|
}
|
|
else if (ch == EOF || ch == CPMEOF)
|
|
return(-1);
|
|
else {
|
|
if ((ch == '"') && (cmnt == 0))
|
|
string = string ^ 1;
|
|
spcnt = 0;
|
|
if (i >= LAST_CH_POS - 1)
|
|
--i;
|
|
}
|
|
}
|
|
prlnbuf[i] = 0;
|
|
return(0);
|
|
}
|
|
|
|
/*
|
|
* wrapup() closes the source file
|
|
*/
|
|
|
|
wrapup()
|
|
{
|
|
|
|
fclose(iptr);
|
|
if ((pass == DONE) && (oflag != 0)) {
|
|
fputc('\n', optr);
|
|
fclose(optr);
|
|
}
|
|
return;
|
|
}
|
|
|
|
/* symbol table print
|
|
*/
|
|
|
|
stprnt()
|
|
{
|
|
int i;
|
|
int j;
|
|
int k;
|
|
|
|
fputc('\014', stdout);
|
|
fputc('\n', stdout);
|
|
i = 0;
|
|
while ((j = symtab[i++]) != 0) {
|
|
for (k = j; k > 0; k--)
|
|
fputc(symtab[i++], stdout);
|
|
for (k = 20 - j; k > 0; k--)
|
|
fputc(' ', stdout);
|
|
++i;
|
|
j = (symtab[i++] & 0xff);
|
|
j += (symtab[i++] << 8);
|
|
hexcon(4, j);
|
|
fprintf(stdout, "\t%c%c:%c%c\t%c%c%c%c\n",
|
|
hex[3], hex[4], hex[1], hex[2],
|
|
hex[1], hex[2], hex[3], hex[4]);
|
|
i += 2;
|
|
}
|
|
}
|
|
|
|
|
|
int optab[];
|
|
int step[];
|
|
|
|
/* translate source line to machine language */
|
|
|
|
assemble()
|
|
{
|
|
int flg;
|
|
int i; /* prlnbuf pointer */
|
|
|
|
if ((prlnbuf[SFIELD] == ';') | (prlnbuf[SFIELD] == 0)) {
|
|
if (pass == LAST_PASS)
|
|
println();
|
|
return;
|
|
}
|
|
lablptr = -1;
|
|
i = SFIELD;
|
|
udtype = UNDEF;
|
|
if (colsym(&i) != 0 && (lablptr = stlook()) == -1)
|
|
return;
|
|
while (prlnbuf[++i] == ' '); /* find first non-space */
|
|
if ((flg = oplook(&i)) < 0) { /* collect operation code */
|
|
labldef(loccnt);
|
|
if (flg == -1)
|
|
error("Invalid operation code");
|
|
if ((flg == -2) && (pass == LAST_PASS)) {
|
|
if (lablptr != -1)
|
|
loadlc(loccnt, 1, 0);
|
|
println();
|
|
}
|
|
return;
|
|
}
|
|
if (opflg == PSEUDO)
|
|
pseudo(&i);
|
|
else if (labldef(loccnt) == -1)
|
|
return;
|
|
else {
|
|
if (opflg == CLASS1)
|
|
class1();
|
|
else if (opflg == CLASS2)
|
|
class2(&i);
|
|
else class3(&i);
|
|
}
|
|
}
|
|
|
|
/****************************************************************************/
|
|
|
|
/* printline prints the contents of prlnbuf */
|
|
|
|
println()
|
|
{
|
|
if (lflag > 0)
|
|
fprintf(stdout, "%s\n", prlnbuf);
|
|
}
|
|
|
|
/* colsym() collects a symbol from prlnbuf into symbol0[],
|
|
* leaves prlnbuf pointer at first invalid symbol character,
|
|
* returns 0 if no symbol collected
|
|
*/
|
|
|
|
colsym(ip)
|
|
int *ip;
|
|
{
|
|
int valid;
|
|
int i;
|
|
char ch;
|
|
|
|
valid = 1;
|
|
i = 0;
|
|
while (valid == 1) {
|
|
ch = prlnbuf[*ip];
|
|
if (ch == '_' || ch == '.');
|
|
else if (ch >= 'a' && ch <= 'z');
|
|
else if (ch >= 'A' && ch <= 'Z');
|
|
else if (i >= 1 && ch >= '0' && ch <= '9');
|
|
else if (i == 1 && ch == '=');
|
|
else valid = 0;
|
|
if (valid == 1) {
|
|
if (i < SBOLSZ - 1)
|
|
symbol0[++i] = ch;
|
|
(*ip)++;
|
|
}
|
|
}
|
|
if (i == 1) {
|
|
switch (symbol0[1]) {
|
|
case 'A': case 'a':
|
|
case 'X': case 'x':
|
|
case 'Y': case 'y':
|
|
error("Symbol is reserved (A, X or Y)");
|
|
i = 0;
|
|
}
|
|
}
|
|
symbol0[0] = i;
|
|
return(i);
|
|
}
|
|
|
|
/* symbol table lookup
|
|
* if found, return pointer to symbol
|
|
* else, install symbol as undefined, and return pointer
|
|
*/
|
|
|
|
stlook()
|
|
{
|
|
int found;
|
|
int hptr;
|
|
int j;
|
|
int nptr;
|
|
int pptr;
|
|
int ptr;
|
|
|
|
hptr = 0;
|
|
for (j = 0; j < symbol0[0]; j++)
|
|
hptr += symbol0[j];
|
|
hptr %= 128;
|
|
ptr = hash_tbl[hptr];
|
|
if (ptr == -1) { /* no entry for this link */
|
|
hash_tbl[hptr] = nxt_free;
|
|
return(stinstal());
|
|
}
|
|
while (symtab[ptr] != 0) { /* 0 count = end of table */
|
|
found = 1;
|
|
for (j = 0; j <= symbol0[0]; j++) {
|
|
if (symbol0[j] != symtab[ptr + j]) {
|
|
found = 0;
|
|
pptr = ptr + symtab[ptr] + 4;
|
|
nptr = (symtab[pptr + 1] << 8) + (symtab[pptr] & 0xff);
|
|
nptr &= 0xffff;
|
|
if (nptr == 0) {
|
|
symtab[ptr + symtab[ptr] + 4] = nxt_free & 0xff;
|
|
symtab[ptr + symtab[ptr] + 5] = (nxt_free >> 8) & 0xff;
|
|
return(stinstal());
|
|
}
|
|
ptr = nptr;
|
|
break;
|
|
}
|
|
}
|
|
if (found == 1)
|
|
return(ptr);
|
|
}
|
|
error("Symbol not found");
|
|
return(-1);
|
|
}
|
|
|
|
/* instal symbol into symtab
|
|
*/
|
|
stinstal()
|
|
{
|
|
register int j;
|
|
register int ptr1;
|
|
register int ptr2;
|
|
|
|
ptr1 = ptr2 = nxt_free;
|
|
if ((ptr1 + symbol0[0] + 6) >= STABSZ) {
|
|
error("Symbol table full");
|
|
return(-1);
|
|
}
|
|
for (j = 0; j <= symbol0[0]; j++)
|
|
symtab[ptr1++] = symbol0[j];
|
|
symtab[ptr1] = udtype;
|
|
nxt_free = ptr1 + 5;
|
|
return(ptr2);
|
|
}
|
|
|
|
/* operation code table lookup
|
|
* if found, return pointer to symbol,
|
|
* else, return -1
|
|
*/
|
|
|
|
oplook(ip)
|
|
int *ip;
|
|
{
|
|
register char ch;
|
|
register int i;
|
|
register int j;
|
|
int k;
|
|
int temp[2];
|
|
|
|
i = j = 0;
|
|
temp[0] = temp[1] = 0;
|
|
while((ch=prlnbuf[*ip])!= ' ' && ch!= 0 && ch!= '\t' && ch!= ';') {
|
|
if (ch >= 'A' && ch <= 'Z')
|
|
ch &= 0x1f;
|
|
else if (ch >= 'a' && ch <= 'z')
|
|
ch &= 0x1f;
|
|
else if (ch == '.')
|
|
ch = 31;
|
|
else if (ch == '*')
|
|
ch = 30;
|
|
else if (ch == '=')
|
|
ch = 29;
|
|
else return(-1);
|
|
temp[j] = (temp[j] * 0x20) + (ch & 0xff);
|
|
if (ch == 29)
|
|
break;
|
|
++(*ip);
|
|
if (++i >= 3) {
|
|
i = 0;
|
|
if (++j >= 2) {
|
|
return(-1);
|
|
}
|
|
}
|
|
}
|
|
if ((j = temp[0]^temp[1]) == 0)
|
|
return(-2);
|
|
k = 0;
|
|
i = step[k] - 3;
|
|
do {
|
|
if (j == optab[i]) {
|
|
opflg = optab[++i];
|
|
opval = optab[++i];
|
|
return(i);
|
|
}
|
|
else if (j < optab[i])
|
|
i -= step[++k];
|
|
else i += step[++k];
|
|
} while (step[k] != 0);
|
|
return(-1);
|
|
}
|
|
|
|
/* error printing routine */
|
|
|
|
error(stptr)
|
|
char *stptr;
|
|
{
|
|
loadlc(loccnt, 0, 1);
|
|
loccnt += 3;
|
|
loadv(0,0,0);
|
|
loadv(0,1,0);
|
|
loadv(0,2,0);
|
|
fprintf(stdout, "%s\n", prlnbuf);
|
|
fprintf(stdout, "%s\n", stptr);
|
|
errcnt++;
|
|
}
|
|
|
|
/* load 16 bit value in printable form into prlnbuf */
|
|
|
|
loadlc(val, f, outflg)
|
|
int val;
|
|
int f;
|
|
int outflg;
|
|
{
|
|
int i;
|
|
|
|
i = 6 + 7*f;
|
|
hexcon(4, val);
|
|
if (nflag == 0) {
|
|
prlnbuf[i++] = hex[3];
|
|
prlnbuf[i++] = hex[4];
|
|
prlnbuf[i++] = ':';
|
|
prlnbuf[i++] = hex[1];
|
|
prlnbuf[i] = hex[2];
|
|
}
|
|
else {
|
|
prlnbuf[i++] = hex[1];
|
|
prlnbuf[i++] = hex[2];
|
|
prlnbuf[i++] = hex[3];
|
|
prlnbuf[i] = hex[4];
|
|
}
|
|
if ((pass == LAST_PASS)&&(oflag != 0)&&(objcnt <= 0)&&(outflg != 0)) {
|
|
fprintf(optr, "\n;%c%c%c%c", hex[3], hex[4], hex[1], hex[2]);
|
|
objcnt = 16;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/* load value in hex into prlnbuf[contents[i]] */
|
|
/* and output hex characters to obuf if LAST_PASS & oflag == 1 */
|
|
|
|
loadv(val,f,outflg)
|
|
int val;
|
|
int f; /* contents field subscript */
|
|
int outflg; /* flag to output object bytes */
|
|
{
|
|
|
|
hexcon(2, val);
|
|
prlnbuf[13 + 3*f] = hex[1];
|
|
prlnbuf[14 + 3*f] = hex[2];
|
|
if ((pass == LAST_PASS) && (oflag != 0) && (outflg != 0)) {
|
|
fputc(hex[1], optr);
|
|
fputc(hex[2], optr);
|
|
--objcnt;
|
|
}
|
|
}
|
|
|
|
/* convert number supplied as argument to hexadecimal in hex[digit] (lsd)
|
|
through hex[1] (msd) */
|
|
|
|
hexcon(digit, num)
|
|
int digit;
|
|
int num;
|
|
{
|
|
|
|
for (; digit > 0; digit--) {
|
|
hex[digit] = (num & 0x0f) + '0';
|
|
if (hex[digit] > '9')
|
|
hex[digit] += 'A' -'9' - 1;
|
|
num >>= 4;
|
|
}
|
|
}
|
|
|
|
/* assign <value> to label pointed to by lablptr,
|
|
* checking for valid definition, etc.
|
|
*/
|
|
|
|
labldef(lval)
|
|
int lval;
|
|
{
|
|
int i;
|
|
|
|
if (lablptr != -1) {
|
|
lablptr += symtab[lablptr] + 1;
|
|
if (pass == FIRST_PASS) {
|
|
if (symtab[lablptr] == UNDEF) {
|
|
symtab[lablptr + 1] = lval & 0xff;
|
|
i = symtab[lablptr + 2] = (lval >> 8) & 0xff;
|
|
if (i == 0)
|
|
symtab[lablptr] = DEFZRO;
|
|
else symtab[lablptr] = DEFABS;
|
|
}
|
|
else if (symtab[lablptr] == UNDEFAB) {
|
|
symtab[lablptr] = DEFABS;
|
|
symtab[lablptr + 1] = lval & 0xff;
|
|
symtab[lablptr + 2] = (lval >> 8) & 0xff;
|
|
}
|
|
else {
|
|
symtab[lablptr] = MDEF;
|
|
symtab[lablptr + 1] = 0;
|
|
symtab[lablptr + 2] = 0;
|
|
error("Label multiply defined");
|
|
return(-1);
|
|
}
|
|
}
|
|
else {
|
|
i = (symtab[lablptr + 2] << 8) +
|
|
(symtab[lablptr+1] & 0xff);
|
|
i &= 0xffff;
|
|
if (i != lval && pass == LAST_PASS) {
|
|
error("Sync error");
|
|
return(-1);
|
|
}
|
|
}
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
/* determine the value of the symbol,
|
|
* given pointer to first character of symbol in symtab
|
|
*/
|
|
|
|
symval(ip)
|
|
int *ip;
|
|
{
|
|
int ptr;
|
|
int svalue;
|
|
|
|
svalue = 0;
|
|
colsym(ip);
|
|
if ((ptr = stlook()) == -1)
|
|
undef = 1; /* no room error */
|
|
else if (symtab[ptr + symtab[ptr] + 1] == UNDEF)
|
|
undef = 1;
|
|
else if (symtab[ptr + symtab[ptr] + 1] == UNDEFAB)
|
|
undef = 1;
|
|
else svalue = ((symtab[ptr + symtab[ptr] + 3] << 8) +
|
|
(symtab[ptr + symtab[ptr] + 2] & 0xff)) & 0xffff;
|
|
if (symtab[ptr + symtab[ptr] + 1] == DEFABS)
|
|
zpref = 1;
|
|
if (undef != 0)
|
|
zpref = 1;
|
|
return(svalue);
|
|
}
|
|
|
|
|
|
/* class 1 machine operations processor - 1 byte, no operand field */
|
|
|
|
class1()
|
|
{
|
|
if (pass == LAST_PASS) {
|
|
loadlc(loccnt, 0, 1);
|
|
loadv(opval, 0, 1);
|
|
println();
|
|
}
|
|
loccnt++;
|
|
}
|
|
|
|
|
|
/* class 2 machine operations processor - 2 byte, relative addressing */
|
|
|
|
class2(ip)
|
|
int *ip;
|
|
{
|
|
|
|
if (pass == LAST_PASS) {
|
|
loadlc(loccnt, 0, 1);
|
|
loadv(opval, 0, 1);
|
|
while (prlnbuf[++(*ip)] == ' ');
|
|
if (evaluate(ip) != 0) {
|
|
loccnt += 2;
|
|
return;
|
|
}
|
|
loccnt += 2;
|
|
if ((value -= loccnt) >= -128 && value < 128) {
|
|
loadv(value, 1, 1);
|
|
println();
|
|
}
|
|
else error("Invalid branch address");
|
|
}
|
|
else loccnt += 2;
|
|
}
|
|
|
|
|
|
/* class 3 machine operations processor - various addressing modes */
|
|
|
|
class3(ip)
|
|
int *ip;
|
|
{
|
|
char ch;
|
|
int code;
|
|
int flag;
|
|
int i;
|
|
int ztmask;
|
|
|
|
while ((ch = prlnbuf[++(*ip)]) == ' ');
|
|
switch(ch) {
|
|
case 0:
|
|
case ';':
|
|
error("Operand field missing");
|
|
return;
|
|
case 'A':
|
|
case 'a':
|
|
if ((ch = prlnbuf[*ip + 1]) == ' ' || ch == 0) {
|
|
flag = ACC;
|
|
break;
|
|
}
|
|
default:
|
|
switch(ch = prlnbuf[*ip]) {
|
|
case '#': case '=':
|
|
flag = IMM1 | IMM2;
|
|
++(*ip);
|
|
break;
|
|
case '(':
|
|
flag = IND | INDX | INDY;
|
|
++(*ip);
|
|
break;
|
|
default:
|
|
flag = ABS | ZER | ZERX | ABSX | ABSY | ABSY2 | ZERY;
|
|
}
|
|
if ((flag & (INDX | INDY | ZER | ZERX | ZERY) & opflg) != 0)
|
|
udtype = UNDEFAB;
|
|
if (evaluate(ip) != 0)
|
|
return;
|
|
if (zpref != 0) {
|
|
flag &= (ABS | ABSX | ABSY | ABSY2 | IND | IMM1 | IMM2);
|
|
ztmask = 0;
|
|
}
|
|
else ztmask = ZER | ZERX | ZERY;
|
|
code = 0;
|
|
i = 0;
|
|
while (( ch = prlnbuf[(*ip)++]) != ' ' && ch != 0 && i++ < 4) {
|
|
code *= 8;
|
|
switch(ch) {
|
|
case ')': /* ) = 4 */
|
|
++code;
|
|
case ',': /* , = 3 */
|
|
++code;
|
|
case 'X': /* X = 2 */
|
|
case 'x':
|
|
++code;
|
|
case 'Y': /* Y = 1 */
|
|
case 'y':
|
|
++code;
|
|
break;
|
|
default:
|
|
flag = 0;
|
|
}
|
|
}
|
|
switch(code) {
|
|
case 0: /* no termination characters */
|
|
flag &= (ABS | ZER | IMM1 | IMM2);
|
|
break;
|
|
case 4: /* termination = ) */
|
|
flag &= IND;
|
|
break;
|
|
case 25: /* termination = ,Y */
|
|
flag &= (ABSY | ABSY2 | ZERY);
|
|
break;
|
|
case 26: /* termination = ,X */
|
|
flag &= (ABSX | ZERX);
|
|
break;
|
|
case 212: /* termination = ,X) */
|
|
flag &= INDX;
|
|
break;
|
|
case 281: /* termination = ),Y */
|
|
flag &= INDY;
|
|
break;
|
|
default:
|
|
flag = 0;
|
|
}
|
|
}
|
|
if ((opflg &= flag) == 0) {
|
|
error("Invalid addressing mode");
|
|
return;
|
|
}
|
|
if ((opflg & ztmask) != 0)
|
|
opflg &= ztmask;
|
|
switch(opflg) {
|
|
case ACC: /* single byte - class 3 */
|
|
if (pass == LAST_PASS) {
|
|
loadlc(loccnt, 0, 1);
|
|
loadv(opval + 8, 0, 1);
|
|
println();
|
|
}
|
|
loccnt++;
|
|
return;
|
|
case ZERX: case ZERY: /* double byte - class 3 */
|
|
opval += 4;
|
|
case INDY:
|
|
opval += 8;
|
|
case IMM2:
|
|
opval += 4;
|
|
case ZER:
|
|
opval += 4;
|
|
case INDX: case IMM1:
|
|
if (pass == LAST_PASS) {
|
|
loadlc(loccnt, 0, 1);
|
|
loadv(opval, 0, 1);
|
|
loadv(value, 1, 1);
|
|
println();
|
|
}
|
|
loccnt += 2;
|
|
return;
|
|
case IND: /* triple byte - class 3 */
|
|
opval += 16;
|
|
case ABSX:
|
|
case ABSY2:
|
|
opval += 4;
|
|
case ABSY:
|
|
opval += 12;
|
|
case ABS:
|
|
if (pass == LAST_PASS) {
|
|
opval += 12;
|
|
loadlc(loccnt, 0, 1);
|
|
loadv(opval, 0, 1);
|
|
loadv(value, 1, 1);
|
|
loadv(value >> 8, 2, 1);
|
|
println();
|
|
}
|
|
loccnt += 3;
|
|
return;
|
|
default:
|
|
error("Invalid addressing mode");
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* pseudo operations processor */
|
|
|
|
pseudo(ip)
|
|
int *ip;
|
|
{
|
|
int count;
|
|
int i;
|
|
int tvalue;
|
|
|
|
switch(opval) {
|
|
case 0: /* .byte pseudo */
|
|
labldef(loccnt);
|
|
loadlc(loccnt, 0, 1);
|
|
while (prlnbuf[++(*ip)] == ' '); /* field */
|
|
count = 0;
|
|
do {
|
|
if (prlnbuf[*ip] == '"') {
|
|
while ((tvalue = prlnbuf[++(*ip)]) != '"') {
|
|
if (tvalue == 0) {
|
|
error("Unterminated ASCII string");
|
|
return;
|
|
}
|
|
if (tvalue == '\\')
|
|
switch(tvalue = prlnbuf[++(*ip)]) {
|
|
case 'n':
|
|
tvalue = '\n';
|
|
break;
|
|
case 't':
|
|
tvalue = '\t';
|
|
break;
|
|
}
|
|
loccnt++;
|
|
if (pass == LAST_PASS) {
|
|
loadv(tvalue, count, 1);
|
|
if (++count >= 3) {
|
|
println();
|
|
for (i = 0; i < SFIELD; i++)
|
|
prlnbuf[i] = ' ';
|
|
prlnbuf[i] = 0;
|
|
count = 0;
|
|
loadlc(loccnt, 0, 1);
|
|
}
|
|
}
|
|
}
|
|
++(*ip);
|
|
}
|
|
else {
|
|
if (evaluate(ip) != 0) {
|
|
loccnt++;
|
|
return;
|
|
}
|
|
loccnt++;
|
|
if (value > 0xff) {
|
|
error("Operand field size error");
|
|
return;
|
|
}
|
|
else if (pass == LAST_PASS) {
|
|
loadv(value, count, 1);
|
|
if (++count >= 3) {
|
|
println();
|
|
for (i = 0; i < SFIELD; i++)
|
|
prlnbuf[i] = ' ';
|
|
prlnbuf[i] = 0;
|
|
count = 0;
|
|
loadlc(loccnt, 0, 1);
|
|
}
|
|
}
|
|
}
|
|
} while (prlnbuf[(*ip)++] == ',');
|
|
if ((pass == LAST_PASS) && (count != 0))
|
|
println();
|
|
return;
|
|
case 1: /* = pseudo */
|
|
while (prlnbuf[++(*ip)] == ' ');
|
|
if (evaluate(ip) != 0)
|
|
return;
|
|
labldef(value);
|
|
if (pass == LAST_PASS) {
|
|
loadlc(value, 1, 0);
|
|
println();
|
|
}
|
|
return;
|
|
case 2: /* .word pseudo */
|
|
labldef(loccnt);
|
|
loadlc(loccnt, 0, 1);
|
|
while (prlnbuf[++(*ip)] == ' ');
|
|
do {
|
|
if (evaluate(ip) != 0) {
|
|
loccnt += 2;
|
|
return;
|
|
}
|
|
loccnt += 2;
|
|
if (pass == LAST_PASS) {
|
|
loadv(value, 0, 1);
|
|
loadv(value>>8, 1, 1);
|
|
println();
|
|
for (i = 0; i < SFIELD; i++)
|
|
prlnbuf[i] = ' ';
|
|
prlnbuf[i] = 0;
|
|
loadlc(loccnt, 0, 1);
|
|
}
|
|
} while (prlnbuf[(*ip)++] == ',');
|
|
return;
|
|
case 3: /* *= pseudo */
|
|
while (prlnbuf[++(*ip)] == ' ');
|
|
if (prlnbuf[*ip] == '*') {
|
|
if (evaluate(ip) != 0)
|
|
return;
|
|
if (undef != 0) {
|
|
error("Undefined symbol in operand field.");
|
|
return;
|
|
}
|
|
tvalue = loccnt;
|
|
}
|
|
else {
|
|
if (evaluate(ip) != 0)
|
|
return;
|
|
if (undef != 0) {
|
|
error("Undefined symbol in operand field.");
|
|
return;
|
|
}
|
|
tvalue = value;
|
|
}
|
|
loccnt = value;
|
|
labldef(tvalue);
|
|
if (pass == LAST_PASS) {
|
|
objcnt = 0;
|
|
loadlc(tvalue, 1, 0);
|
|
println();
|
|
}
|
|
return;
|
|
case 4: /* .list pseudo */
|
|
if (lflag >= 0)
|
|
lflag = 1;
|
|
return;
|
|
case 5: /* .nlst pseudo */
|
|
if (lflag >= 0)
|
|
lflag = iflag;
|
|
return;
|
|
case 6: /* .dbyt pseudo */
|
|
labldef(loccnt);
|
|
loadlc(loccnt, 0, 1);
|
|
while (prlnbuf[++(*ip)] == ' ');
|
|
do {
|
|
if (evaluate(ip) != 0) {
|
|
loccnt += 2;
|
|
return;
|
|
}
|
|
loccnt += 2;
|
|
if (pass == LAST_PASS) {
|
|
loadv(value>>8, 0, 1);
|
|
loadv(value, 1, 1);
|
|
println();
|
|
for (i = 0; i < SFIELD; i++)
|
|
prlnbuf[i] = ' ';
|
|
prlnbuf[i] = 0;
|
|
loadlc(loccnt, 0, 1);
|
|
}
|
|
} while (prlnbuf[(*ip)++] == ',');
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* evaluate expression */
|
|
|
|
evaluate(ip)
|
|
int *ip;
|
|
{
|
|
int tvalue;
|
|
int invalid;
|
|
int parflg, value2;
|
|
char ch;
|
|
char op;
|
|
char op2;
|
|
|
|
op = '+';
|
|
parflg = zpref = undef = value = invalid = 0;
|
|
/* hcj: zpref should reflect the value of the expression, not the value of
|
|
the intermediate symbols
|
|
*/
|
|
while ((ch=prlnbuf[*ip]) != ' ' && ch != ')' && ch != ',' && ch != ';') {
|
|
tvalue = 0;
|
|
if (ch == '$' || ch == '@' || ch == '%')
|
|
tvalue = colnum(ip);
|
|
else if (ch >= '0' && ch <= '9')
|
|
tvalue = colnum(ip);
|
|
else if (ch >= 'a' && ch <= 'z')
|
|
tvalue = symval(ip);
|
|
else if (ch >= 'A' && ch <= 'Z')
|
|
tvalue = symval(ip);
|
|
else if ((ch == '_') || (ch == '.'))
|
|
tvalue = symval(ip);
|
|
else if (ch == '*') {
|
|
tvalue = loccnt;
|
|
++(*ip);
|
|
}
|
|
else if (ch == '\'') {
|
|
++(*ip);
|
|
tvalue = prlnbuf[*ip] & 0xff;
|
|
++(*ip);
|
|
}
|
|
else if (ch == '[') {
|
|
if (parflg == 1) {
|
|
error("Too many ['s in expression");
|
|
invalid++;
|
|
}
|
|
else {
|
|
value2 = value;
|
|
op2 = op;
|
|
value = tvalue = 0;
|
|
op = '+';
|
|
parflg = 1;
|
|
}
|
|
goto next;
|
|
}
|
|
else if (ch == ']') {
|
|
if (parflg == 0) {
|
|
error("No matching [ for ] in expression");
|
|
invalid++;
|
|
}
|
|
else {
|
|
parflg = 0;
|
|
tvalue = value;
|
|
value = value2;
|
|
op = op2;
|
|
}
|
|
++(*ip);
|
|
}
|
|
switch(op) {
|
|
case '+':
|
|
value += tvalue;
|
|
break;
|
|
case '-':
|
|
value -= tvalue;
|
|
break;
|
|
case '/':
|
|
value = (unsigned) value/tvalue;
|
|
break;
|
|
case '*':
|
|
value *= tvalue;
|
|
break;
|
|
case '%':
|
|
value = (unsigned) value%tvalue;
|
|
break;
|
|
case '^':
|
|
value ^= tvalue;
|
|
break;
|
|
case '~':
|
|
value = ~tvalue;
|
|
break;
|
|
case '&':
|
|
value &= tvalue;
|
|
break;
|
|
case '|':
|
|
value |= tvalue;
|
|
break;
|
|
case '>':
|
|
tvalue >>= 8; /* fall through to '<' */
|
|
case '<':
|
|
if (value != 0) {
|
|
error("High or low byte operator not first in operand field");
|
|
}
|
|
value = tvalue & 0xff;
|
|
zpref = 0;
|
|
break;
|
|
default:
|
|
invalid++;
|
|
}
|
|
if ((op=prlnbuf[*ip]) == ' '
|
|
|| op == ')'
|
|
|| op == ','
|
|
|| op == ';')
|
|
break;
|
|
else if (op != ']')
|
|
next: ++(*ip);
|
|
}
|
|
if (parflg == 1) {
|
|
error("Missing ] in expression");
|
|
return(1);
|
|
}
|
|
if (value < 0 || value >= 256) {
|
|
zpref = 1;
|
|
}
|
|
if (undef != 0) {
|
|
if (pass != FIRST_PASS) {
|
|
error("Undefined symbol in operand field");
|
|
invalid++;
|
|
}
|
|
value = 0;
|
|
}
|
|
else if (invalid != 0)
|
|
{
|
|
error("Invalid operand field");
|
|
}
|
|
else {
|
|
/*
|
|
This is the only way out that may not signal error
|
|
*/
|
|
if (value < 0 || value >= 256) {
|
|
zpref = 1;
|
|
}
|
|
else {
|
|
zpref = 0;
|
|
}
|
|
}
|
|
return(invalid);
|
|
}
|
|
|
|
/* collect number operand */
|
|
|
|
colnum(ip)
|
|
int *ip;
|
|
{
|
|
int mul;
|
|
int nval;
|
|
char ch;
|
|
|
|
nval = 0;
|
|
if ((ch = prlnbuf[*ip]) == '$')
|
|
mul = 16;
|
|
else if (ch >= '1' && ch <= '9') {
|
|
mul = 10;
|
|
nval = ch - '0';
|
|
}
|
|
else if (ch == '@' || ch == '0')
|
|
mul = 8;
|
|
else if (ch == '%')
|
|
mul = 2;
|
|
while ((ch = prlnbuf[++(*ip)] - '0') >= 0) {
|
|
if (ch > 9) {
|
|
ch -= ('A' - '9' - 1);
|
|
if (ch > 15)
|
|
ch -= ('a' - 'A');
|
|
if (ch > 15)
|
|
break;
|
|
if (ch < 10)
|
|
break;
|
|
}
|
|
if (ch >= mul)
|
|
break;
|
|
nval = (nval * mul) + ch;
|
|
}
|
|
return(nval);
|
|
}
|