C02/apps/a02.c02

940 lines
29 KiB
Plaintext

/* Simple 6502 Assembler *
* for C02 Compiler *
* Uses DASM Syntax but *
* supports 65C02 Op Codes */
/* DEBUG LEVELS *
* $01 - Echo Input Lines *
* $02 - Show Labels, Menonics, Operands *
* $04 - Symbol Search/Set Details *
* $08 - Individual Mnemonic Assembly *
* $10 - Term Evaluation Details *
^ $20 - IF and SWITCH details *
* $40 - Show Binary Output * `
* $80 - Parse Word and Lookup Details * `
*/
#define DEBUG $00 //Print Debug Information
//#include "a02lib.h02" - This will replace all the defs below
#include <stddef.h02>
#include <stdlib.h02>
#include <stdio.h02>
#include <stdiox.h02>
#include <ctype.h02>
#include <intlib.h02>
#include <memory.h02>
#include <string.h02>
alias char symtbl = $E000; //Global Symbol Table
/* Address Mode Constants, Bit Masks, and Descriptions */
// 0 1 2 3 4 5 6 7 8 9 10 11
enum {ACMLT, IMMDT, ZPAGE, ZPAGX, ABSLT, ABSLX, ABSLY, IMPLD, INDCT, INDCX, INDCY, RELTV};
// 0 1 2 3 4 5 6 7 8 9 10 11
const char amodlo = {$01, $02, $04, $08, $20, $40, $80, $00, $00, $00, $00, $00};
const char amodhi = {$00, $00, $00, $00, $00, $00, $00, $01, $02, $04, $08, $10};
const char amdesc = {"Accumulator", "Immediate", "Zero Page", "Zero Page,X",
"Absolute", "Absolute,X", "Absolute,Y", "Implied",
"(Indirect)", "(Indirect,X)", "(Indirect),Y", "Relative", 0};
const char amdess = {"ACC","IMM","ZPG","ZPX","ABS","ABX","ABY","IMP",
"IND","INX","INY","REL"};
/* Pseudo-Op Lookup Table */
struct pso {char token, name[5];};
const char psolst = {'B', "BYTE", 'H', "HEX", 'W', "WORD", '=', "EQU", 'F', "FILL",
'I', "INCL", 'S', "SUBR", 'B', "DC", 'F', "DS", 'A', "ALIG",
'*', "ORG", 'P', "PROC", 'M', "ENDS", 'E', "END", $FF};
/* Op Code Lookup Table */
struct opc {char opcode, amdidx, name[5];};
const char opcalo = {$00, $00, $FE, $7D, $04, $04, $24, $26, $A6, $6E, $6C, $20, $20, $6E, $2C};
const char opcahi = {$01, $10, $0E, $00, $00, $10, $00, $00, $00, $00, $00, $06, $00, $00, $00};
const char opclst = {
$00, 0, "BRK", $EA, 0, "NOP", $DB, 0, "STP", $CB, 0, "WAI", $CA, 0, "DEX",
$88, 0, "DEY", $E8, 0, "INX", $C8, 0, "INY", $48, 0, "PHA", $08, 0, "PHP",
$DA, 0, "PHX", $5A, 0, "PHY", $68, 0, "PLA", $28, 0, "PLP", $FA, 0, "PLX",
$7A, 0, "PLY", $18, 0, "CLC", $D8, 0, "CLD", $58, 0, "CLI", $B8, 0, "CLV",
$38, 0, "SEC", $F8, 0, "SED", $78, 0, "SEI", $AA, 0, "TAX", $A8, 0, "TAY",
$BA, 0, "TSX", $8A, 0, "TXA", $98, 0, "TYA", $9A, 0, "TXS", $40, 0, "RTI",
$60, 0, "RTS", $B0, 1, "BCS", $F0, 1, "BEQ", $30, 1, "BMI", $70, 1, "BVS",
$90, 1, "BCC", $D0, 1, "BNE", $10, 1, "BPL", $50, 1, "BVC", $80, 1, "BRA",
$A1, 2, "LDA", $61, 2, "ADC", $21, 2, "AND", $C1, 2, "CMP", $81, 2, "STA",
$E1, 2, "SBC", $01, 2, "ORA", $41, 2, "EOR", $02, 3, "ASL", $22, 3, "ROL",
$E2, 3, "INC", $42, 3, "LSR", $62, 3, "ROR", $C2, 3, "DEC", $07, 4, "RMB",
$87, 4, "SMB", $0F, 5, "BBR", $8F, 5, "BBS", $10, 6, "TRB", $00, 6, "TSB",
$E0, 7, "CPX", $C0, 7, "CPY", $A2, 8, "LDX", $82, 8, "STX", $20, 9, "BIT",
$60, 10,"STZ", $4C, 11,"JMP", $14, 12,"JSR", $A0, 13,"LDY", $80, 14,"STY",
$FF
};
/* Op-Code Fixup Table */
struct opf {char token, opmod, opcode;};
const char opfix = {
$20, $08, $89, $E0, $08, $E0, $C0, $08, $C0, $A0, $08, $A0, $A2, $08, $A2,
$C2, $08, $3A, $E2, $08, $1A, $60, $0C, $9C, $60, $1C, $9E, $A2, $18, $BE,
$82, $18, $BC, $4C, $0C, $4C, $4C, $11, $6C, $4C, $00, $7C, 0
};
/* Temporary and Index Variables */
char b, c, f, i, j, m, n, o, t, u;
int v, w;
/* Function Variables */
char found, match, opcode, opdfnd; //lookup(), asmpso(), asmopc()
int opdval; //evlopd() values
char hilo, opdrqd, prns, reqbyt, result; //evlopd() flags
int addend; //adrslt()
char digit; //evlhex()
int term; //evltrm()
/* Symbol Table Entries */
struct sym {char block, name[8], bytes; int value;};
struct sym symbol; //Current Symbol
struct sym varble; //Symbol to Lookup
int symcnt; //Number of Symbols
int symptr; //Pointer to Next Table Entry
char symflg; //Valid Symbol Value
char blkcnt; //Number of Block Labels Generated
char blknum; //Local Label Block Number (0 = Global)
/* Input Variables */
char inplin[128]; //Input Buffer
char inpidx; //Index into Input Buffer
char eoinp; //End of Input
int lineno; //Input File Line Number
/* Output Variables */
char outlin[128]; //Output Buffer
char outidx; //Index into Output Buffer
/* Assembly Variables */
char label[8]; //Assembly Line Label
char mcode[8]; //Generated Machine Code
char mnmnc[12]; //Opcode Mnemonic
char oprnd[128]; //Opcode Mnemonic
char cmmnt[128]; //Assembly Line Comment
char strng[128]; //Parsed String
char word[128]; //Parsed Word
char wrdlen; //Length of Parsed Word
int mcdadr; //Machine Code Address
char mcdidx; //Index into mcode[]
char opridx; //Index into oprnd[]
char endasm; //End Assembly
char local,dot; //Local Symbol, Dot Prefix Flags
char colon; //Colon after Label
char token, opmod; //OpCode Token, Modifier
char amflg, amidx; //Flag and Index - amodlo[], amodhi[]
char amdlo, amdhi; //Addressing Modes
char opvlo, opvhi; //Operand Value
char zpflg; //ZeroPage Flag
char hexadr[6]; //Current Address in Hexadecimal
char bytstr[5]; //String Representation of Byte
char orgset; //Origin Address Set
int orgadr; //Origin Address
int curadr; //Current Address
int lstadr; //List Address
/* Print Error Message and Exit *
* Args: Y,X = Address of Eror Message */
void error() {
putc('!'); putstr(); putln(" ERROR");
goto exit;
}
void lenerr(w) {setdst(word); strcpy(w); strcat(" TOO LONG"); error(word);}
/* Read Line of Input from Console *
* Populates: inplin[] - Line of Input *
* Sets: inpidx - Length of Line *
* Destroys: c - Input Character *
* Returns: True if End of Input */
char readln() {
inpidx = 0;
do {
c = getchr();
select (c) {
case #RTNKEY: c = 0;
case #ESCKEY: if (inpidx) c = 0; else return #TRUE;
default:
}
inplin[inpidx] = c;
inpidx++;
if (inpidx:-) error("INPUT OVERFLOW");
} while (c);
return #FALSE;
}
void prtchr(c) {if (c) putchr(c);} //Print Non-Null Character
void prtbyt() {prbyte(); putchr(' ');} //Print Hexadecimal Word
void echoln() {putchr('<'); putwrd(lineno); putln(inplin);}
void init() { if (#DEBUG&2) putln("#STARTING ASSEMBLY");
lineno = 1; //Initialize Input File Line Number
orgset = #FALSE; //Origin Not Set
endasm = #FALSE; //End Assembly Fl
symptr = &symtbl;
blkcnt = 1; //Top level locals are Block 1
blknum = blkcnt; //because Block 0 is a global
}
/* Parse Word from Input Line *
* Args: m = Maximum Length *
* w = &Description of Word *
* Populates: word[] *
* Sets: wrdlen *
* Updates: inpidx *
* Returns: Word Found (TRUE/FALSE) */
char pword(m, w) {
wrdlen = 0;
while () {
if (#DEBUG:-) putchr(inplin[inpidx]);
c = touppr(inplin[inpidx]);
if (c < 'A' or c > 'Z' and c <> '_') break;
word[wrdlen] = c; wrdlen++;
inpidx++;
}
word[wrdlen] = 0; //Terminate String
if (m and wrdlen > m) lenerr(w);
if (#DEBUG&4) {puts("PARSED "); puts(w); printf(setdst(word)," \"%s\"%n");}
if (wrdlen) return #TRUE; else return #FALSE;
}
/* Skip Character in Input Line *
* Args: c - Character to Skip *
* Updates: inpidx *
* Returns: c if found, else #FALSE */
int skpchr(c) {
if (inplin[inpidx] == c) {inpidx++; return c;}
else return #FALSE;
}
/* Skip Spaces in Input Line *
* Updates: inpidx */
void skpspc() {
while (inplin[inpidx]) {
if (A > ' ') break;
inpidx++;
}
}
/* Copy Character from Input Line to Operand *
* Args: c = character to copy (0 = any) *
* Updates: inpidx *
* Returns: character matched */
char cpychr(c) {
o = touppr(inplin[inpidx]);
if (c and o <> c) return #FALSE;
if (opridx:+) {oprnd[opridx] = o; opridx++; }
inpidx++;
return #TRUE;
}
/* Check Origin Address */
void chkorg() {
if (!orgset) error("ORIGIN NOT SET");
}
/* Write Line of Machine Language Bytes */
void wrtmcd() {
putwrd(mcdadr);
for (i=0; i<mcdidx; i++) puthex(mcode[i]);
mcdidx = 0;
}
/* Output Machine Language Character */
void outchr(b) {
chkorg();
//if (mcdidx > 7) {wrtmcd(); newlin();}
//if (!mcdidx) mcdadr = curadr;
if (#DEBUG&64) printf(setdst(b,curadr),"#OUT: %w $%x%n");
if (outidx<127) {outlin[outidx] = b; outidx++;}
if (mcdidx<7) {mcode[mcdidx] = b; mcdidx++;}
curadr++;
}
/* Output Machine Language Byte */
void outbyt() {
outchr();
}
/* Write Word to Output File */
void outwrd(w) {
outchr(<w); outchr(>w);
}
/* Print Symbol Table */
void prtsym() {
printf("%nSYMBOLS%nBLK SZ VALUE NAME%n");
usrptr = &symtbl;
iacc(symptr); //end of symbol table for icmp()
while (icmp(iacc(usrptr),symptr):-) {
for (i=0; i<@symbol; i++) {symbol[i] = *usrptr; usrptr++;}
putder(symbol.block); setdst(symbol.value); printf(symbol.bytes," %d %J ");
if (symbol.block) putchr('.'); putln(symbol.name);
}
}
/* Find Symbol in Table *
* Uses: word = Symbol Name *
* local = Local Symbol Flag *
* blknum = Subroutine Block # *
* Sets: varble = Contents of Entry *
* Destroys: b, dstptr *
* Returns: TRUE if Symbol was Found */
char fndsym() {
if (#DEBUG&4) printf(setdst(local,word),"FNDSYM: WORD=\"%S\", LOCAL=%d - ");
b = (local) ? blknum : 0; //Block level to match
setdst(word); //For strcmp
usrptr = &symtbl;
iacc(symptr); //end of symbol table for icmp()
while (icmp(iacc(usrptr),symptr):-) {
varble.block = *usrptr;
if (varble.block <> b) {usrptr = iaddc(@varble,usrptr); continue;}
usrptr++; if (strcmp(usrptr)) {usrptr = iaddc(@varble-1,usrptr); continue;}
for (i=1; i<@varble; i++) {varble[i] = *usrptr; usrptr++;}
if (#DEBUG&4) putln("FOUND");
return #TRUE;
}
if (#DEBUG&4) putln("NOT FOUND");
return #FALSE;
}
/* Set Symbol Value and Size */
void setsym(b, v) {
symbol.value = v;
if (!b) b = (>symbol.value) ? 2 : 1;
symbol.bytes = b;
if (#DEBUG&4) {
printf(setdst(symbol.block,symbol.name),"#SYMBOL %s IN BLOCK %d ");
printf(setdst(symbol.bytes,symbol.value),"SET TO VALUE %i, SIZE %d%n");
}
}
/* Parse Label from Input Line
* Sets: label[] = Label Name
* local = '.' if Local else 0.
^ symbol.name = Label Name
* symbol.value = Current Address
* symbol.bytes = Size of Symbol Value
* Updates: c */
void plabel() {
local = (skpchr('.')) ? '.' : 0; //Check for Local Label
if (#DEBUG&4) {puts("#PARSING "); if (local) puts("LOCAL "); putln("LABEL");}
if (pword(8,"SYMBOL")) {
setdst(label); strcpy(word);
//if (#DEBUG&2) {putstr("#FOUND "); if (local) putstr("LOCAL "); putstr("LABEL "); putln(label);}
symbol.block = (local) ? blknum : 0;
setdst(symbol.name);strcpy(label);
setsym(0, curadr); symflg = orgset;
} else {
label[0] = 0;
if (#DEBUG&2) putln("#NO LABEL FOUND");
}
colon = skpchr(':'); //Optional Colon after Label
}
/* Parse Mnemonic from Input Line *
* Sets: mnmnc = Mnemonic *
* word = Mnemonic *
* wrdlen = Mnemonic Length *
* Updates: inpidx *
* Returns: Mnemonic Found (TRUE/FALSE) */
char pmnmnc() {
skpspc(); //Skip Leading Spaces
dot = skpchr('.'); //Optional Dot before Pseudo-Op
if (pword(12,"MNEMONIC")) {
for (i=0; i<=wrdlen; i++) mnmnc[i] = word[i];
if (#DEBUG&2) {putstr("#FOUND MNEMONIC "); putln(mnmnc);}
} else {
mnmnc[0] = 0;
if (#DEBUG&2) putln("#NO MNEMONIC FOUND");
}
}
/* Look Up List Entry *
* Args: amflg = get address modes *
* srcptr = address of list *
* Uses: mnmnc = mnemonic *
* Sets: token = op code token *
* amidx = address mode index *
* Destroys: match *
* Returns: TRUE if found */
char lookup(amflg, srcptr) {
if (#DEBUG:-) printf(setdst(amflg,srcptr), "LOOKUP(%x,%w)");
while () { //Search Op-Code List
if (#DEBUG:-) printf(setdst(srcptr)," SRCPTR=%w");
token = *srcptr; srcptr++; if (#DEBUG:-) printf(token, ", TOKEN=%d");
if (token == $FF) break;
if (amflg) {amidx = *srcptr; srcptr++; if (#DEBUG:-) printf(amidx, ", AMDIDX=%d");}
match = #TRUE; if (#DEBUG:-) newlin();
i = 0; //Offset into mnmnc
while (*srcptr) {
if (#DEBUG:-) {
printf(setdst(*srcptr,srcptr)," (%w)=%d ");
printf(i,", MNMNC[%d]="); putchr(mnmnc[i]); newlin();
}
if (*srcptr <> mnmnc[i]) match = #FALSE;
if (#DEBUG:-) prtbyt(match);
i++; srcptr++;
}
if (match) return #TRUE;
srcptr++;
}
return #FALSE;
}
/* Evaluate Binary Number */
void evlbin() {
if (#DEBUG&16) putln("#EVALUATING BINARY NUMBER");
term = 0;
cpychr('%');
while (isbdgt(inplin[inpidx])) {
term<<; if (inplin[inpidx] & 1) term++;
cpychr(0);
}
}
/* Evaluate Character Constant */
void evlchr() {
if (#DEBUG&16) putln("#EVALUATING QUOTED CHARACTER");
cpychr('\'');
term = int(inplin[inpidx]);
cpychr(0);
cpychr('\'');
}
/* Evaluate Decimal Number */
int evldec() {
if (#DEBUG&16) putln("#EVALUATING DECIMAL NUMBER");
term = 0;
while (isdigt(inplin[inpidx])) {
term = imultc(10, term);
term = iaddc(inplin[inpidx] - '0', term);
cpychr(0);
}
}
/* Evaluate Hexadecimal Number */
int evlhex() {
if (#DEBUG&16) putln("#EVALUATING HEXADECIMAL NUMBER");
term = 0;
cpychr('$');
while (ishdgt(inplin[inpidx])) {
digit = inplin[inpidx] - '0';
if (digit > 9) digit = digit - 7;
term<<; term<<; term<<; term<<;
term = iaddc(digit, term);
cpychr(0);
}
}
char evlsym() {
local = cpychr('.'); //Check for Local Symbol
if (!pword(8,"SYMBOL")) {if (local) error("INVALID SYMBOL"); else return;}
if (#DEBUG&16) printf(setdst(word),"#EVALUATING SYMBOL \"%s\"%n");
for (i=0; i<wrdlen; i++) {
if (opridx:+) {oprnd[opridx] = word[i]; opridx++;}
}
found = fndsym();
if (!found) error("UNDEFINED SYMBOL");
return #TRUE;
}
/* Evaluation Routine Dispatch */
void evldsp() {
found = #TRUE;
if (inplin[inpidx]=='.' or isalph(A)) {
if (#DEBUG&32) putln("#ALUMUNDOT");
if (evlsym()) term = varble.value; else term = &$0100;
return;
}
if (isdigt(inplin[inpidx])) {
if (#DEBUG&32) putln("#DIGIT");
evldec();
return;
}
if (#DEBUG&32) putln("#SOMETHING ELSE");
select(inplin[inpidx]) {
case '$': evlhex(); break;
case '%': evlbin(); break;
case '\'': evlchr(); break;
default: found = #FALSE;
}
}
/* Evaluate Term in Operand *
* Updates: inpidx
* Sets: term
* Returns: #TRUE if a term was found */
char evltrm() {
skpspc();
if (#DEBUG&16) printf(inplin[inpidx],"#EVALUATING TERM BEGINNING WITH '%c'%n");
evldsp();
skpspc();
if (#DEBUG&16) {if (found) printf(setdst(term),"#TERM=%i%n"); else putln("#NO TERM");}
return found;
}
/* Evaluate Operand *
* Args: opdrqd = Operation Required *
* reqbyt = Value Must Be Byte *
* Sets: opdval = Operand Value
* Returns: Operand Found */
char evlopd(opdrqd, reqbyt) {
hilo = 0; //Return LSB (1) or MSB ($FF)
prns = 0; //Optional Parenthesis after Hi/Low Operator
skpspc();
if (#DEBUG&16) printf(inplin[inpidx],"#EVALUATING OPERAND BEGINNING WITH '%c'%n");
if (cpychr('<')) hilo = 1; else {if (cpychr('>')) hilo = $FF;}
if (hilo) prns = cpychr('(');
if (#DEBUG&16) printf(hilo,"#HILO SET TO $%x%n");
result = evltrm();
if (#DEBUG&16) printf(setdst(result,term),"#EVLTRM RETURNED $%d, TERM=%i%n");
if (result) {
opdval = term;
while (cpychr('+')) {
if (!evltrm()) break;
opdval = iadd(setdst(opdval),term);
}
}
if (#DEBUG&16) printf(setdst(opdval),"#CALCULATED OPDVAL %i%n");
if (hilo) {
if (!result) error("ILLEGAL OPERAND");
if (prns) cpychr(')'); //Skip End Parenthesis
if (hilo:-) opdval = int(>opdval); //Copy MSB to LSB
opdval = int(<opdval); //Return LSB
if (#DEBUG&16) printf(setdst(hilo,opdval),"#HILO $%x, OPDVAL SET TO %i%n");
}
if (result) {
if (#DEBUG&2) printf(setdst(opdval), "#OPDVAL SET TO %i%n");
if (reqbyt & >opdval) error("VALUE TOO LARGE");
} else {
if (#DEBUG&2) putln("#NO OPERAND");
if (opdrqd) error("OPERAND REQUIRED");
}
return result, opdval;
}
/* Assemble EQU Pseudo-Op */
void asmequ() {
if (#DEBUG&8) putln("#ASSEMBLING EQU PSEUDO-OP");
if (!label[0]) error("LABEL REQUIRED");
evlopd(#TRUE, #FALSE);
setsym(0,opdval); symflg = #TRUE;
}
/* Assemble ORG Pseudo-Op */
void asmorg() {
if (evlopd(#TRUE, #FALSE)) orgadr = opdval;
if (symbol.name[0]) {
symbol.value = orgadr;
symbol.bytes = 2;
}
curadr = orgadr;
lstadr = orgadr;
orgset = #TRUE;
symflg = #TRUE;
}
/* Assemble BYTE Pseudo-Op */
void asmbyt() {
do {
skpspc();
if (cpychr('"')) { //String Operand
if (#DEBUG&2) putln("#ASSEMBLING .BYTE STRING OPERAND");
while (!cpychr('"')) {outchr(inplin[inpidx]); cpychr(0); }
skpspc();
} else {
if (evlopd(#TRUE, #TRUE)) outbyt(<opdval); //Evaluate Operand
}
} while (cpychr(','));
}
/* Assemble HEX Pseudo-Op */
void asmhex() {
do {
skpspc();
evlhex(); outbyt(<term); if (>term) outbyt(>term);
skpspc();
} while (ishdgt(inplin[inpidx]));
}
/* Assemble SUBROUTINE Pseudo-Op */
void asmsub() {
blkcnt++; blknum = blkcnt;
if (#DEBUG&2) printf(blknum,"#BLKNUM SET TO %d%n");
if (pword(7, "SUBROUTINE NAME")) {
opridx=strcpy(setdst(oprnd),word);
} else {
if (#DEBUG&2) putln("NO SUBROUTINE NAME");
}
}
/* Assemble ENDSUBROUTINE Pseudo-Op */
void asmens() {
blknum = 1;
if (#DEBUG&2) printf(blknum,"#BLKNUM RESET TO %d%n");
}
/* Assemble WORD Pseudo-Op */
void asmwrd() {
do {
evlopd(#TRUE, #FALSE); outwrd(opdval);
} while (cpychr(','));
}
/* Assemble ALIGN Pseudo-Op */
void asmaln() {
//v = size, w = fill
v = evlopd(#TRUE, #FALSE); if (icmp(iacc(v),&2):-) return;
if (#DEBUG&2) printf(setdst(v),"ALIGNING TO %i BYTES%n");
w = imod(iacc(curadr),v); w = isub(iacc(v),w); //w = w - (curadr % v)
if (#DEBUG&2) printf(setdst(w),"FILLING %i BYTES%n");
if (!icmp(w)) return;
for (v=0; icmp(iacc(v),w):-; v++) outbyt(0);
}
/* Assemble FILL Pseudo-Op */
void asmfll() {
w = evlopd(#TRUE, #FALSE);
if (#DEBUG&2) printf(setdst(w),"FILLING %i BYTES%n");
for (v=0; icmp(iacc(v),w):-; v++) outbyt(0);
}
/* Assemble END Pseudo-Op */
void asmend() {
endasm = #TRUE;
if (#DEBUG&2) printf(endasm,"#ENDASM SET TO $%x%n");
}
/* Assemble Pseudo-Op */
/* Uses: mnmnc = mnemonic *
* Sets: token = op code token *
* found = pseudo-op found *
* Destroys: match *
* Returns: TRUE if found */
char asmpso() {
found = lookup(#FALSE, psolst); //Lookup Pseudo-Op
if (!found) {if (dot) error("ILLEGAL PSEUDO-OP"); else return #FALSE;}
if (#DEBUG&2) printf(token,"#ASSEMBLING PSEUDO-OP TOKEN='%c'%n");
skpspc();
select (token) {
case '=': asmequ(); break; //EQU
case '*': asmorg(); //ORG
case 'B': asmbyt(); //BYTE
case 'H': asmhex(); //HEX
case 'W': asmwrd(); //WORD
case 'F': asmfll(); //FILL
case 'S': asmsub(); //SUBRoutine
case 'M': asmens(); //ENDSubroutine
//case 'I': asminf(); //INCLude
//case 'P': asmprc(); //PROCessor
case 'A': asmaln(); //ALIGn
case 'E': asmend(); //END
default: error("UNIMPLEMENTED");
}
return #TRUE;
}
/* Check for Valid Addressing Mode */
int chkmod(m, f) {
if (#DEBUG&2) {
i = m; i<<; i<<;
puts("#CHECKING ADDR MODE %"); putbin(amodhi[m]); putbin(amodlo[m]);
putspc(); putsub(i,amdess); newlin();
}
if (amodlo[m] & amdlo or amodhi[m] & amdhi) return #TRUE;
if (f) error("ILLEGAL ADDRESS MODE");
return #FALSE;
}
/* Assemble Absolute/ZeroPage Indexed */
void asmidx() {
if (cpychr('X')) {
if (zpflg and chkmod(#ZPAGX)) {opmod = $14; return;} //ZeroPage,X
if (chkmod(#ABSLX)) {opmod = $1C; return;} //Absolute,X
}
if (cpychr('Y')) {
if (zpflg) {
if (token == $82 or token == $A2) {opmod = $14; return;} //ZeroPage,Y
}
zpflg = #FALSE; opmod = $18; return; //Absolute,Y
}
chkmod(0); //Illegal Addressing Mode
}
/* Assemble Implied/Accumulator/Absolute/ZeroPage Mode Instruction */
void asmiaz() {
opdfnd = evlopd(#FALSE, #FALSE);
if (!opdfnd) {
if (amidx and chkmod(#ACMLT, #TRUE)) opmod = $08; //Accumulator
return;
}
if (>opdval) zpflg = #FALSE; else zpflg = #TRUE;
if (#DEBUG&2) printf(zpflg,"#ZPFLG SET TO $%x%n");
if (zpflg and chkmod(#ZPAGE, #FALSE)) {opmod = $04; return;} //ZeroPage
if (chkmod(#ABSLT, #TRUE)) {opmod = $0C; zpflg = #FALSE;} //Absolute
if (cpychr(',')) asmidx(); //Indexed
}
/* Assemble Immediate Mode Instruction */
void asmimd() {
if (#DEBUG&2) putln("#IMMEDIATE MODE");
evlopd(#TRUE, #TRUE);
zpflg = #TRUE;
opmod = $08; //Immediate
}
/* Assemble Indirect Mode Instruction */
void asmind() {
if (#DEBUG&2) putln("#INDIRECT MODE");
zpflg = #TRUE; opdval = evlopd(#TRUE,#FALSE);
if (cpychr(',') and cpychr('X') and chkmod(#INDCX)) {cpychr(')'); return;}
if (!cpychr(')')) chkmod(0); //If no end paren - Illegal Addressing Mode
if (cpychr(',') and cpychr('Y') and chkmod(#INDCY)) {opmod = $10; return;}
if (chkmod(#INDCT)) opmod = $11; //(Indirect)
if (token == $4C) {zpflg = #FALSE;} //JMP (Indirect Absolute)
}
/* Calculate Branch Offset */
void asmbro() {
if (#DEBUG&2) putln("#CALCULATING BRANCH OFFSET");
skpspc();
if (inplin[inpidx]=='.' or isalph(A)) {
if (#DEBUG&4) putln("#PARSING LABEL");
if (evlsym()) w = isub(isub(iacc(varble.value),curadr),v);
return;
}
if (cpychr('+')) {w = evlopd(#TRUE,#FALSE); return;}
if (cpychr('-')) {w = ineg(evlopd(#TRUE,#FALSE)); return;}
if (#DEBUG&4) putln("#PARSING ADDRESS");
w = isub(isub(iacc(evlopd(#TRUE,#FALSE)),curadr),v);
}
/* Assemble Branch Opcode */
void asmbrn(zpflg) {
if (#DEBUG&2) putln("#RELATIVE MODE");
w = 0; //Offset
if (zpflg) v = 2; else v = 3; //Offset Adjustment
asmbro();
if (#DEBUG&2) printf(setdst(w),"#BRANCH OFFSET IS $%w%n");
if (>w and A <> $FF) error("BRANCH OUT OF RANGE");
opdval = int(<w);
}
/* Fix Opcode (if needed) */
char fixopc() {
i = 0;
while(opfix[i]) {
t = opfix[i]; i++; m = opfix[i]; i++; o = opfix[i]; i++;
if (t == token and m == opmod) return o;
}
return token + opmod;
}
/* Assemble Token */
void asmtok() {
skpspc();
if (amidx == 1) {asmbrn(#TRUE); return;} //Branch (Relative) Instruction
if (cpychr('#')) {asmimd(); return;} //Assemble Immediate Instruction
if (cpychr('(')) {asmind(); return;} //Assemble Indirect Instruction
asmiaz(); //Assemble Implied/Accumulator/Absolute/ZeroPage Instruction
}
/* Assemble Opcode */
void asmopc() {
opmod = 0;
found = lookup(#TRUE, opclst); //Lookup OpCode
if (!found) error("ILLEGAL MNEMONIC");
if (#DEBUG&2) printf(token, "#TOKEN SET TO $%x%n");
amdlo = opcalo[amidx]; amdhi = opcahi[amidx];
if (#DEBUG&2) {
puts("#ADDRESS MODE MASK: %"); putbin(amdhi); putbin(amdlo); newlin();
//prtbyt(amidx);
}
asmtok();
if (zpflg and >opdval ) error("OPERAND TOO LARGSE");
opcode = fixopc(); //Fixup Opcode
if (#DEBUG&2) printf(opcode, "#WRITING OPCODE $%x%n");
outchr(opcode);
if (#DEBUG&2 and opdfnd) printf(setdst(opdval), "#WRITING OPDVAL %i%n");
if (opdfnd) {if (zpflg) outbyt(<opdval); else outwrd(opdval);}
}
/* Parse Comment from Input Line
* Sets: cmmnt[]
* Updates: inpidx */
void pcmmnt() {
skpspc();
i = 0;
while (inplin[inpidx] >= ' ') {
cmmnt[i] = inplin[inpidx];
i++; inpidx++;
}
cmmnt[i] = 0; //Terminate Comment
if (#DEBUG&2) {if (i) {putstr("#COMMENT: "); putln(cmmnt);} else putln("#NO COMMENT FOUND");}
}
/* Append Symbol` to Symbol Table *
* Uses: symbol *
* Sets: i = @symbol *
* dstptr = End of Table *
* Updates: symptr = End of Table *
* Returns: End of Table Address */
void addsym() {
if (!<symbol.value|>symbol.value) error("ORIGIN NOT SET");
dstptr = symptr;
for (i=0; i<@symbol; i++) {
*dstptr = symbol[i];
dstptr++;
}
symptr = dstptr;
if (#DEBUG&4) printf(setdst(symbol.name),"#ADDED SYMBOL \"%s\" TO TABLE%n");
}
/* Print Listing Line */
void prlist() {
putc('|');
prtchr(local); putstr(label); prtchr(colon); putspc();
prtchr(dot); putstr(mnmnc); putspc(' ');
if (opridx) putstr(oprnd);
putln(cmmnt);
}
void asmlin() {
lstadr = curadr; //Set List Address
opridx = 0; //Clear Operand String
outidx = 0; //Clear Output Buffer
mcdidx = 0; //Clear Macbine Code
inpidx = 0; //Initialize Line Buffer Index
plabel(); //Parse Label
pmnmnc(); //Parse Mnemonic
if (mnmnc[0]) {
if (!asmpso()) asmopc(); //Assemble Pseudo-Op or Op-Code
oprnd[opridx] = 0; //Terminste oprnd
}
pcmmnt(); //Parse Comment
if (label[0]) {
if (!symflg) error("INVALID ADDRESS");
addsym(); //Add Label to Table
}
//if (mcdidx) wrtmcd(); //Write Machine Code
prlist();
}
main:
init(); //Initialize Assembly Variables
goto tests;
goto tests;
while (!readln()) {
if (#DEBUG&1) echoln();
asmlin(); if (endasm) break;
lineno++;
}
prtsym(&symtbl);
goto exit;
/*======================================================*/
/* Everything below here is tests for various functions */
//Copy Arg String to inplin
void setln(w) {
inpidx = 0;
setdst(inplin);
strcpy(w);
putstr(">>"); putln(inplin);
}
void slabel() {setln(); plabel();}
void pout() {
puts(" OUTLIN: ");
for (i=0; i<outidx; i++) printf(outlin[i],"%x ");
newlin();
}
void tpso() {
opridx = 0; outidx = 0;
setln();
pmnmnc();
asmpso();
if (outidx) pout();
printf(setdst(curadr),"#CURADR=$%w%n");
}
void tasm() {
setln();
asmlin();
if (outidx) pout();
printf(setdst(curadr),"#CURADR=$%w%n");
}
tests:
tasm(" ORG $0300");
tasm("ONE: EQU 1");
tasm("CEE: EQU 'C'");
tasm("BIG: EQU $ABCD");
tasm("TOP: EQU $FFFF");
tasm(".YOKEL BYTE 1,1,1,1");
tasm(" BYTE 0, ONE ,%10 ,$3,'4',CEE");
tasm(" .DC <BIG, >BIG, \"STRING\" ");
tasm(" HEX 0 1 2 F 10 F0 FEEF ");
tasm(" WORD 1, 1001, %11111, $F00F, TOP");
tasm(" FILL 300");
tasm(" ALIGN 256");
tasm(" DS 20");
tasm(" SUBR MYLIB");
tasm(".YOKEL BYTE 2,2,2,2");
tasm(" SUBROUTINE");
tasm(".YOKEL BYTE 3,3,3,3");
tasm(" ENDSUB");
tasm(" END");
tasm(" BRK ;$00");
tasm(" RTS ;$60");
tasm(" NOP ;$EA");
tasm(" JMP $C4 ;$4C $C4 $00");
tasm(" JMP $C44C ;$4C $C4 $4C");
tasm(" JMP ($C6) ;$6C $C6 $00");
tasm(" JMP ($C66C) ;$6C $C6 $6C");
tasm(" LDA $01 ;$A5 $01");
tasm(" LDA $0400 ;$AD $00 $04");
tasm(" LDA #89 ;$A9 $59");
tasm(" LDA ($1A,X) ;$A1 $1A");
tasm(" LDA ($1B),Y ;$B1 $1B");
tasm(" LDA $5B,X ;$B5 $5B");
tasm(" LDA $B99B,Y ;$B9 $9B $B9");
tasm(" LDA $BDDB,X ;$BD $DB $BD");
tasm("BRANCH:");
tasm(" BCS BRANCH ;$B0 $FE");
tasm(" BCC BRANCH ;$90 $FB");
tasm(" BEQ -128 ;$F0 $80");
tasm(" BNE +127 ;$D9 $7F");
tasm(" BPL $0500 ;$10 $??");
tasm(" LSR ;$4A");
tasm(" LSR $64 ;$46 $64");
prtsym();
goto exit;
// $A1, 2, "LDA", $61, 2, "ADC", $21, 2, "AND", $C1, 2, "CMP", $81, 2, "STA",
// $E1, 2, "SBC", $01, 2, "ORA", $41, 2, "EOR", $02, 3, "ASL", $22, 3, "ROL",
// $E2, 3, "INC", $42, 3, "LSR", $62, 3, "ROR", $C2, 3, "DEC", $07, 4, "RMB",
// $87, 4, "SMB", $0F, 5, "BBR", $8F, 5, "BBS", $10, 6, "TRB", $00, 6, "TSB",
// $E0, 7, "CPX", $C0, 7, "CPY", $A2, 8, "LDX", $82, 8, "STX", $20, 9, "BIT",
// $60, 10,"STZ", $4C, 11,"JMP", $14, 12,"JSR", $A0, 13,"LDY", $80, 14,"STY",
// 'I', "INCL", 'S', 'F', "DS", 'P', "PROC",