6502 Assembler (part 2 of 3)

Eric C. Brown brownc at utah-cs.UUCP
Sat May 4 01:51:52 AEST 1985


#!/bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #!/bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#	as6502.1
#	assm.d1
#	assm.d2
#	assm0.c
#	assm1.c
# This archive created: Tue Apr  2 14:21:10 1985
# By:	James Van Ornum (AT&T-Bell Laboratories)
export PATH; PATH=/bin:$PATH
if test -f 'as6502.1'
then
	echo shar: over-writing existing file "'as6502.1'"
fi
cat << \SHAR_EOF > 'as6502.1'
.TH AS6502 1 11/5/84 
.SH NAME
as6502 \- assembler for MOS Technology 650X microprocessors
.SH SYNOPSIS
.B as6502
[ option ] file ...
.SH DESCRIPTION
.I As6502
assembles the named files and produces
a listing on the standard output.
Available options are:
.TP 4
.B \-i
ignore .nlst pseudo ops in the source file.
.TP
.B \-l
produce only an error listing on the standard output.
.TP
.B \-n
print address in normal <high byte><low byte> format,
rather than split <low byte>:<high byte> format.
.TP
.B \-o
generate the ASCII object file
.B "6502.out."
The per-line format of that file is:
.br
    ;<address low byte><address high byte><data>...
.TP
.B \-s
print symbol table at the end of the assembly listing.
.TP
Symbol conventions:
.br
.tr ~|
Up to 19 alphanumeric (a-z ~ A-Z ~ 0-9 ~ . ~ _) characters,
.tr ~~
with initial character non-numeric.
.sp
Asterisk (*) is symbolic name of the location counter.
.TP
Op code mnemonics (upper and/or lower case):
.br
ADC   BIT   BVS   CPX   INC   LDX   PHP   RTI   SEI  TAY
.br
AND   BMI   CLC   CPY   INX   LDY   PLA   RTS   STA  TSX
.br
ASL   BNE   CLD   DEC   INY   LSR   PLP   SBC   STX  TXA
.br
BCC   BPL   CLI   DEX   JMP   NOP   ROL   SEC   STY  TXS
.br
BCS   BRK   CLV   DEY   JSR   ORA   ROR   SED   TAX  TYA
.br
BEQ   BVC   CMP   EOR   LDA   PHA
.TP
Pseudo op mnemonics:
.br
=      equate label to operand value.
.br
*=     equate location counter to operand value.
.br
 .WORD  assign 16 bit operand value to next 2 locations.
.br
 .DBYT  assign 16 bit value to next 2 locations, reverse.
.br
 .BYTE  assign 8 bit operand value to next location.
.br
 .NLST  turn listing mode off.
.br
 .LIST  turn listing mode on.
.TP
Constant types:
.br
%       binary number prefix.
.br
@ or 0  octal number prefix.
.br
$       hexadecimal number prefix.
.br
 '       ASCII character prefix.
.br
 "       ASCII character string prefix and suffix.
.br
default (leading digit 1 through 9)  decimal number.
.TP
Operand field operators:
.br
+     addition               ^     logical exclusive OR
.br
-     subtraction            ~     logical 1's complement
.br
/     integer division       $     logical AND
.br
.tr ||
*     multiplication         |     logical OR
.br
%     modulo                 <     low byte
.br
                             >     high byte
.SH FILES
.PD 0
.TP
/BIN/as6502   the assembler
.TP
6502.out      object (with -o option)
.SH "SEE ALSO"
.TP
J. H. Van Ornum, "as6502 User Notes"
.SH DIAGNOSTICS
.in 0
File handling diagnostics:
.in 15
.ti 5
Invalid argument count - as6502 invoked without a source file or
with too many source files.
.ti 5
Open error for file <name> - as6502 cannot open source file.
.ti 5
Create error (6502.out) - as6502 cannot create object file.
.ti 5
Close error - as6502 cannot close the source file.
.ti 5
Close error (6502.out) - as6502 cannot close object file.
.in 0
Assembly error diagnostics:
.in 15
.ti 5
Symbol table full - symbol table overflowed allotted space.
Number of symbols is a function of symbol sizes.
.ti 5
Label multiply defined - symbol defined more than once.
.ti 5
Sync error - the pass 2 value of symbol in the label field is different
than the pass 1 value.
.ti 5
Invalid operation code - op code mnemonic is invalid.
.ti 5
Operand field missing - the op code mnemonic requires an operand, and none was
found.
.ti 5
Invalid operand field - operand field contains an operator which is not
recognized by as6502.
.ti 5
Invalid branch address - branch instruction to a location which is out of range.
.ti 5
Invalid addressing mode - tried to use an addressing mode which is not
available to the operation code.
.ti 5
Operand field size error - operand is larger than hex FF.
.ti 5
Undefined symbol in operand field - a symbol in the operand field never
appeared in the label field.
.SH BUGS
SHAR_EOF
if test -f 'assm.d1'
then
	echo shar: over-writing existing file "'assm.d1'"
fi
cat << \SHAR_EOF > 'assm.d1'
#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
SHAR_EOF
if test -f 'assm.d2'
then
	echo shar: over-writing existing file "'assm.d2'"
fi
cat << \SHAR_EOF > 'assm.d2'
extern	FILE	*optr;
extern	FILE	*iptr;
extern	int	dflag;		/* debug flag */
extern	int	errcnt;		/* error counter */
extern	int	hash_tbl[];	/* pointers to starting links in symtab */
extern	char	hex[];		/* hexadecimal character buffer */
extern	int	iflag;		/* ignore .nlst flag */
extern	int	lablptr;	/* label pointer into symbol table */
extern	int	lflag;		/* disable listing flag */
extern	int	loccnt;		/* location counter	*/
extern	int	nflag;		/* normal/split address mode */
extern	int	nxt_free;	/* next free location in symtab */
extern	int	objcnt;		/* object byte counter */
extern	int	oflag;		/* object output flag */
extern	int	opflg;		/* operation code flags */
extern	int	opval;		/* operation code value */
extern	int	pass;		/* pass counter		*/
extern	char	prlnbuf[];	/* print line buffer	*/
extern	int	sflag;		/* symbol table output flag */
extern	int	slnum;		/* source line number counter */
extern	char	symtab[];	/* symbol table		*/
extern	char	symbol[];	/* temporary symbol storage	*/
extern	int	udtype;		/* undefined symbol type	*/
extern	int	undef;		/* undefined symbol in expression flg  */
extern	int	value;		/* operand field value */
extern	char	zpref;		/* zero page reference flag	*/
SHAR_EOF
if test -f 'assm0.c'
then
	echo shar: over-writing existing file "'assm0.c'"
fi
cat << \SHAR_EOF > 'assm0.c'
#include "stdio.h"
#include "assm.d1"

/*  Assembler for the MOS Technology 650X series of microprocessors
 *  Written by J. H. Van Ornum (201) 949-1781
 *		AT&T Bell Laboratories
 *		 Holmdel, NJ
 */

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	symbol[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
};
SHAR_EOF
if test -f 'assm1.c'
then
	echo shar: over-writing existing file "'assm1.c'"
fi
cat << \SHAR_EOF > 'assm1.c'
#include "stdio.h"
#include "assm.d1"
#include "assm.d2"

#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
 */

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(stderr, "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(stderr, "Invalid argument count (%d).\n", argc);
		exit(1);
	}
	if ((iptr = fopen(*av, "r")) == NULL) {
		fprintf(stderr, "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(stderr, "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') {
		if(ch == '\r')
			continue;
		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;
	}
}
SHAR_EOF
#	End of shell archive
exit 0



More information about the Comp.sources.unix mailing list