2021-09-25 19:30:18 +00:00
|
|
|
/* Simple 6502 Assembler for CC02 Compiler *
|
|
|
|
* Uses DASM Syntax but supports 65C02 Op Codes */
|
|
|
|
|
|
|
|
#include <stddef.h02>
|
|
|
|
#include <stdlib.h02>
|
2021-09-25 19:56:12 +00:00
|
|
|
#include <args.h02>
|
2021-09-25 19:30:18 +00:00
|
|
|
#include <intlib.h02>
|
|
|
|
#include <ctype.h02>
|
|
|
|
#include <stdio.h02>
|
|
|
|
#include <stdiox.h02>
|
|
|
|
#include <memory.h02>
|
|
|
|
#include <string.h02>
|
|
|
|
#include <stringl.h02>
|
|
|
|
#include <stringm.h02>
|
|
|
|
#include <block.h02>
|
|
|
|
#include <fileio.h02>
|
|
|
|
|
|
|
|
#define LBLSIZ 8 //Maximum Symbol Length
|
|
|
|
#define WRDSIZ 16 //Maximum Parsed Word Length
|
|
|
|
|
|
|
|
const char debug = #TRUE;
|
|
|
|
|
|
|
|
char inplin[#STRSIZ]; //Input Buffer
|
|
|
|
char inpidx; //Pointer into Input Buffer
|
|
|
|
int lineno; //Input File Line Number
|
|
|
|
int savlno; //Line Number (Saved)
|
|
|
|
|
|
|
|
char pass2; //Assembler Pass 2 flag
|
|
|
|
char endasm; //End Assembly Flag
|
|
|
|
|
|
|
|
char label[#WRDSIZ]; //Assembly Line Label
|
|
|
|
char mnmnc[#WRDSIZ]; //Opcode Mnemonic
|
|
|
|
char oprnd[#STRSIZ]; //Operand Text
|
|
|
|
char cmmnt[#STRSIZ]; //Assembly Line Comment
|
|
|
|
char mcode[#STRSIZ]; //Generated Bytes
|
|
|
|
char strng[#STRSIZ]; //Parsed String
|
|
|
|
|
|
|
|
char inpnam[#STRSIZ]; //Input File Name
|
|
|
|
char outnam[#STRSIZ]; //Output File Name
|
|
|
|
char lstnam[#STRSIZ]; //List File Name
|
|
|
|
char incnam[#STRSIZ]; //Include File Name
|
|
|
|
char inpfil; //Input File Channnel
|
|
|
|
char outfil; //Output File Channnel
|
|
|
|
char lstfil; //List File Channnel
|
|
|
|
char incfil; //Include File Channnel
|
|
|
|
|
|
|
|
int orgadr; //Origin Address
|
|
|
|
int curadr; //Current Address
|
|
|
|
int lstadr; //List Address
|
|
|
|
char orgflg; //Origin Address Flag
|
|
|
|
char curflg; //Current Address Flag
|
|
|
|
|
|
|
|
|
|
|
|
struct sym {
|
|
|
|
char name[#LBLSIZ]; //Symbol Name
|
|
|
|
char block; //Local Lable Block ID
|
|
|
|
char bytes; //Value Size, High Bit set if Referenced
|
|
|
|
int value; //Symbol Value
|
|
|
|
char refrd; //Referenced
|
|
|
|
};
|
|
|
|
struct sym symbol; //Current Symbol
|
|
|
|
struct sym symfnd; //Symbol found by fndsym
|
|
|
|
struct sym symprt; //Symbol to print
|
|
|
|
int symcnt; //Number of Global Labels
|
|
|
|
char blockc; //Number of Block Labels Generated
|
|
|
|
char blockn; //Local Label Block ID (0 = Global)
|
|
|
|
int symbgn; //Start Address of Symbol Table
|
|
|
|
int symend; //End Address of Symbol Table
|
|
|
|
int symptr; //Pointer to entry in Symbol Table
|
|
|
|
|
|
|
|
|
|
|
|
char token, opmod; //OpCode Token, Modifier
|
|
|
|
char zpage; //Zero Page Flag
|
|
|
|
int amode, opval; //Address Mode Mask, Operand Value
|
|
|
|
|
|
|
|
char aa,bb,cc,dd,ff,mm,xx,yy,zz;
|
|
|
|
int ii, jj, yx;
|
|
|
|
|
|
|
|
/* Print Error Message and Exit */
|
|
|
|
void xerror() {
|
|
|
|
printf(); //Implicit Arguments
|
|
|
|
dstptr = lineno; if (<dstptr or >dstptr) printf(" IN LINE %d");
|
|
|
|
newlin();
|
|
|
|
goto exit;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Open File with Error Checking */
|
|
|
|
char opnfil(aa, yx) {
|
|
|
|
if (debug) {setdst(yx); printf(aa, "OPENING FILE '%s' WITH MODE %x%n");}
|
|
|
|
zz = fopen(aa, yx);
|
|
|
|
if (!zz) {setdst(yx); xerror("ERROR OPENING FILE '%s'%n");}
|
|
|
|
if (debug) printf(zz, "FILE OPENED ON CHANNEL %d%n");
|
|
|
|
return zz;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Copy Character to Operand *
|
|
|
|
* Args: cc - Character to Skip *
|
|
|
|
* Updates: inpidx *
|
|
|
|
* Returns: #TRUE if copied */
|
|
|
|
char cpychr(cc) {
|
|
|
|
if (cc and touppr(inplin[inpidx]) <> cc) return #FALSE;
|
|
|
|
aa = inplin[inpidx];
|
|
|
|
strapd(touppr(aa), oprnd);
|
|
|
|
inpidx++;
|
|
|
|
return #TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Skip Character in Input Line *
|
|
|
|
* Args: c - Character to Skip *
|
|
|
|
* Updates: inpidx */
|
|
|
|
int skpchr(cc) {
|
|
|
|
if (inplin[inpidx] == cc) {inpidx++; return #TRUE;}
|
|
|
|
else return #FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Skip Spaces in Input Line *
|
|
|
|
* Updates: inpidx */
|
|
|
|
void skpspc() {
|
|
|
|
do {} while (skpchr(' '));
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Parse Word from Input Line *
|
|
|
|
* Args: skip - Skip Spaces Flag *
|
|
|
|
* &word - Buffer for Word *
|
|
|
|
* Sets: dstptr - Buffer Address *
|
|
|
|
* Updates: inpidx *
|
|
|
|
* Affects: aa, zz *
|
|
|
|
* Returns: Word Found (TRUE/FALSE) */
|
|
|
|
char pword(aa, yx) {
|
|
|
|
zz = 0; dstptr = yx;
|
|
|
|
if (aa) skpspc();
|
|
|
|
while (isalnu(inplin[inpidx])) {
|
|
|
|
*dstptr = touppr(inplin[inpidx]);
|
|
|
|
inpidx++; dstptr++; zz++;
|
|
|
|
}
|
|
|
|
*dstptr = 0; //Terminate String
|
|
|
|
dstptr = yx;
|
|
|
|
if (zz) return #TRUE; else return #FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Print Symbol Structure *
|
|
|
|
* Args: &sym - Address of sym Struct *
|
|
|
|
* Sets: symprt - Contents of struct *
|
|
|
|
* Affects: dtsptr, jj */
|
|
|
|
void prtsym(jj) {
|
|
|
|
setdst(symprt); memcpy(@symprt, jj);
|
|
|
|
setdSt(symprt.name); printf(symprt.block,"SYMBOL[BLOCK=%d, NAME=%s, ");
|
|
|
|
setdst(symprt.value); printf(symprt.bytes,"SIZE=%d, VALUE=$%w");
|
|
|
|
printf(symprt.refrd,", REFRD=%x]%n");
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Set memory block to symmtbl *
|
|
|
|
* Args: A = 0 - Add Symbol *
|
|
|
|
* <> 0 - Read Symbols *
|
|
|
|
* Sets: blkbgn, blkend *
|
|
|
|
* blklen, blkptr */
|
|
|
|
void symblk(blkend) {
|
|
|
|
if (A|0) {blkptr = symbgn; blkend = symptr;}
|
|
|
|
else {blkptr = symptr; blkend = symend;}
|
|
|
|
blkbgn = symbgn; blklen = @symbol;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Find Symbol in Symbol Table *
|
|
|
|
* Args: char aa - Local Label Block Number *
|
|
|
|
* int yx - Pointer to Symbol Name *
|
|
|
|
* Sets: blkptr - Pointer to Entry in Table *
|
|
|
|
* Populates: symfnd - Matching Symbol from Table *
|
|
|
|
* Preserves: dstptr; *
|
|
|
|
* Affects: aa, ff, ii, yx *
|
|
|
|
* Returns: #TRUE if found, otherwise #FALSE */
|
|
|
|
char fndsym(aa, yx) {
|
|
|
|
ii = dstptr; setdst(yx); //Set srring for printf
|
|
|
|
if (debug) printf(aa, " SEARCHING FOR SYMBOL %s IN BLOCK %d\n");
|
|
|
|
setdst(symfnd); //Set Struct for blkstr() to populate
|
|
|
|
symblk($FF); blkrst(); //Reading Symbol Table
|
|
|
|
while() {
|
|
|
|
ff = blkstr(@symfnd,yx);
|
|
|
|
if (!ff) break;
|
|
|
|
if (symfnd.block == aa) break;
|
|
|
|
}
|
|
|
|
if (debug) {
|
|
|
|
if (ff) {puts(" FOUND "); prtsym(symfnd);}
|
|
|
|
else putln(" NOT FOUND");
|
|
|
|
}
|
|
|
|
dstptr = ii; return ff;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Set Symbol Value and Size */
|
|
|
|
void setsym(aa, jj) {
|
|
|
|
symbol.value = jj;
|
|
|
|
if (aa) symbol.bytes = aa; else symbol.bytes = (>jj) ? 2 : 1;
|
|
|
|
symbol.refrd = curflg ^ $FF;
|
|
|
|
if (debug) {puts(" SET "); prtsym(symbol);}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Parse Label from Input Line *
|
|
|
|
* Sets: label - parsed label *
|
|
|
|
* Updates: inoidx *
|
|
|
|
* Affects: aa, bb, ff, ii, jj, yx *
|
|
|
|
* Returns: Label Found (TRUE/FALSE) */
|
|
|
|
char plabel() {
|
|
|
|
if (debug) putln("PARSING LABEL");
|
|
|
|
bb = (skpchr('.')) ? blockn : 0; //Local Label Block Number
|
|
|
|
ff = pword(#FALSE, label); //Sets dstptr to &label
|
|
|
|
if (debug) {
|
|
|
|
if (ff) printf(bb," FOUND BLOCK %d LABEL %s%n");
|
|
|
|
else putln(" NO LABEL FOUND");
|
|
|
|
}
|
|
|
|
skpchr(':'); //Skip Optional Label Terminator
|
|
|
|
if (ff and !pass2) {
|
|
|
|
if (label[0] and fndsym(bb, label))
|
|
|
|
xerror("DUPLICATE LABEL %S ENCOUNTERED%n");
|
|
|
|
if (debug) printf(" INITIALIZING SYMBOL %s%n");
|
|
|
|
symbol.block = bb;
|
|
|
|
if (strlen(label) > #LBLSIZ) xerror("LABEL %S TOO LONG\n");
|
|
|
|
setdst(symbol.name); strcpy(label);
|
|
|
|
setsym(0, curadr);
|
|
|
|
}
|
|
|
|
if (bb) strppd('.', label);
|
|
|
|
skpspc(); //Skip to Mnemonic, Comment, or EOL
|
|
|
|
return ff;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Assemble Pseudo-Op */
|
|
|
|
int asmpso(dd) {
|
|
|
|
setdst(mnmnc); if (strcmp("END")) return #FALSE;
|
|
|
|
endasm = #TRUE; return;
|
|
|
|
// if (!lkpopc(psolst) and !dd ) return #FALSE;
|
|
|
|
// skpspc();
|
|
|
|
// setdst(mnmnc); if (debug) printf(token, "ASSEMBLING PSEUDO-OP %s, TOKEN '%c'%n");
|
|
|
|
// select (token) {
|
|
|
|
// case '=': asmequ(); //EQU
|
|
|
|
// case 'B': asmbyt(); //BYTE or DC
|
|
|
|
// case 'H': asmhex(); //HEX
|
|
|
|
// case 'W': asmwrd(); //WORD
|
|
|
|
// case 'F': asmfll(); //FILL or DS
|
|
|
|
// case 'S': asmsub(); //SUBRoutine
|
|
|
|
// case 'M': asmens(); //ENDSubroutine
|
|
|
|
// case 'I': asminf(); //INCLude
|
|
|
|
// case '*': asmorg(); //ORG
|
|
|
|
// case 'P': asmprc(); //PROCessor
|
|
|
|
// case 'E': asmend(); //END
|
|
|
|
// case 'A': asmaln(); //ALIGn
|
|
|
|
// default: xerror("Undefined Pseudo-Op %s%n");
|
|
|
|
// }
|
|
|
|
// if (dd) strppd('.', mnmnc);
|
|
|
|
// return #TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Assemble Opcode
|
|
|
|
* Uses: dd - Dot Prefix*/
|
|
|
|
char asmopc() {
|
|
|
|
if (debug) printf(" ASSEMBLING MNEMONIC %s%n");
|
|
|
|
opmod = 0;
|
|
|
|
if (asmpso()) return; //Check For/Assemble Pseudo-Op
|
|
|
|
// if (lkpopc(opclst) == FALSE) xerror("Invalid Mnemonic %s\n", mnmnc);
|
|
|
|
// if (debug) printf("Assembling Opcode Token 0x%02X, ", token);
|
|
|
|
// if (debug) printf("Addressing Mode Mask 0x%04X\n", amode);
|
|
|
|
// skpspc();
|
|
|
|
// if (amode == RELTV) asmbrn(TRUE); //Branch (Relative) Instruction
|
|
|
|
// else if (amode == 0x0004 || amode == 0x1004) asmzpr(); //Branch (Relative) Instruction
|
|
|
|
// else if (cpychr('#')) asmimd(); //Assemble Implied Instruction
|
|
|
|
// else if (cpychr('(')) asmind(); //Assemble Indirect Instruction
|
|
|
|
// else asmiaz(); //Assemble Implied/Accumulator/Absolute/ZeroPage Instruction
|
|
|
|
// if (debug) dbgopc();
|
|
|
|
// int opcode = fixopc();
|
|
|
|
// if (debug) printf("Writing OpCode $%02X\n", opcode);
|
|
|
|
// outbyt(opcode);
|
|
|
|
// if (debug) printf("Writing %s Operand %d\n", zpgabs[-zpage], opval);
|
|
|
|
// if (opval >= 0) {
|
|
|
|
// if (zpage) outbyt(opval); //Byte Operand
|
|
|
|
// else outwrd(opval); //Word Operand
|
|
|
|
// }
|
|
|
|
// return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Parse Opcode Mnemonic from Input Line
|
|
|
|
* Sets: mnmnc
|
|
|
|
* Updates: inpidx
|
|
|
|
* Returns: Mnemonic Found (TRUE/FALSE) */
|
|
|
|
char pmnmnc() {
|
|
|
|
if (debug) putln("PARSING MNEMONIC");
|
|
|
|
dd = cpychr('.'); //Optional Dot before Pseudo-Op
|
|
|
|
mm = pword(#TRUE, mnmnc);
|
|
|
|
oprnd[0] = 0; //Initialize Operand
|
|
|
|
if (mm) asmopc(dd); //Assemble Opcode
|
|
|
|
else putln(" NONE FOUND");
|
|
|
|
return mm;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Print Input Line for Debugging */
|
|
|
|
void pinlin() {
|
|
|
|
dstptr = lineno; printf(pass2+1, "> %d%j");
|
|
|
|
dstptr = curadr; printf(" %w ");
|
|
|
|
puts(inplin);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Add Label to Symbol Table */
|
|
|
|
void addsym() {
|
|
|
|
if (symbol.refrd) xerror("ORIGIN NOT SET");
|
|
|
|
symblk(0); blkput(@symbol, symbol); symptr = blkptr;
|
|
|
|
if (debug) {puts("ADDED "); prtsym(symbol);}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Assemble Input File (Two Pass) *
|
|
|
|
* Args: pass - Assembly Pass (1 or 2) *
|
|
|
|
* Requires: inpfil - Input File Pointer *
|
|
|
|
* Uses: inplin - Input Line Buffer *
|
|
|
|
* lineno - Input File Line Number */
|
|
|
|
void asmfil(pass2) {
|
|
|
|
endasm = #FALSE; //Reset End Assembly Flag
|
|
|
|
if (debug) {printf(pass2+1,"Assembling Pass %d%n");}
|
|
|
|
lineno = 1; //Initialize Input File Line Number
|
|
|
|
blockc = 0; blockn = 0; //Initialize Local Block Count and Number
|
|
|
|
orgadr = 0; orgflg = 0; //Origin Address Not Set
|
|
|
|
curadr = &$1000; curflg = $FF; //Current Address Not Set
|
|
|
|
while () {
|
|
|
|
aa = (incfil) ? incfil : inpfil;
|
|
|
|
zz = fgets(aa, inplin);
|
|
|
|
if (endasm or !zz) break; //{if (incfil) {clsinc(); continue;} else break;}
|
|
|
|
if (debug) pinlin();
|
|
|
|
inpidx = 0;
|
|
|
|
lstadr = curadr; //Set List Address
|
|
|
|
mcode[0] = 0; //Clear Generated Macbine Code
|
|
|
|
plabel(); //Parse Label
|
|
|
|
pmnmnc(); //Parse Mnemonic
|
|
|
|
// pcmmnt(); //Parse Comment
|
|
|
|
if (!pass2 and label[0]) addsym(); //Add Label to Table
|
|
|
|
// if (passno == 2) {
|
|
|
|
// if (lstadr < 0) hexadr[0] = 0; else sprintf(hexadr, "%04X", lstadr);
|
|
|
|
// fprintf(lstfil, "%05d %-4s %-9s%-7s %-5s %-16s %s\n", lineno, hexadr, mcode, label, mnmnc, oprnd, cmmnt );
|
|
|
|
// fflush(lstfil); //Flush Output Buffer in case of Segmentation Fault
|
|
|
|
// }
|
|
|
|
lineno++;
|
|
|
|
// if (incnam[0] && incfil == NULL) opninc(); //Open Include File
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Print Symbol Table */
|
|
|
|
/* Print Symbol Table */
|
|
|
|
void prttbl() {
|
|
|
|
putln("Symbol Table");
|
|
|
|
putln("Block Name Size Value Rfd");
|
|
|
|
blkptr = symbgn;
|
|
|
|
while () {
|
|
|
|
blkget(@symprt, symprt);
|
|
|
|
if (!symprt.name[0]) break;
|
|
|
|
cc = (symprt.refrd) ? '*' : ' ';
|
|
|
|
printf(symprt.block,"%r ");
|
|
|
|
putpad(9, symprt.name);
|
|
|
|
setdst(symfnd.value); printf(symfnd.bytes," %r %j ");
|
|
|
|
putchr(cc);
|
|
|
|
newlin();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-25 19:56:12 +00:00
|
|
|
/* Read Command Line Arguments and Open Files */
|
|
|
|
void doargs() {
|
|
|
|
if (debug) {setdst(sysbfr); printf("PARSING ARGUMENTS \"%s\"%n");}
|
|
|
|
aa = argset(); if (aa) xerror("NO ARGUMENTS SPECIFIED");
|
|
|
|
|
|
|
|
aa = argget(inpnam); if(!aa) xerror("INPUT FILE NOT SPECIFIED");
|
|
|
|
if (debug) printf(" PARSED INPNAM \"%s\"%n");
|
|
|
|
|
|
|
|
aa = argget(outnam); //if(!aa) xerror("OUTPUT FILE NOT SPECIFIED");
|
|
|
|
if (debug) printf(" PARSED OUTNAM \"%s\"%n");
|
|
|
|
|
|
|
|
aa = argget(lstnam);
|
|
|
|
if (debug) printf(" PARSED LSTNAM \"%s\"%n");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2021-09-25 19:30:18 +00:00
|
|
|
main:
|
|
|
|
symbgn = &$8000; symend = &$A000; symptr = symbgn; //Set up Symbol Table
|
|
|
|
symblk($FF); blkset(0); //Initialize Symbol Table
|
2021-09-25 19:56:12 +00:00
|
|
|
doargs(); //Read File Names and Options from Command Line Arguments
|
2021-09-25 19:30:18 +00:00
|
|
|
inpfil = opnfil(0,inpnam);
|
|
|
|
asmfil(0);
|
|
|
|
fclose(inpfil);
|
|
|
|
prttbl();
|
|
|
|
//if (lstfil && symcnt) prttbl(); //Print Symbol Table
|
|
|
|
putln("ASSEMBLY COMPLETE");
|
|
|
|
|
|
|
|
goto exit;
|
|
|
|
|