diff --git a/a02.c b/a02.c new file mode 100644 index 0000000..7c77403 --- /dev/null +++ b/a02.c @@ -0,0 +1,572 @@ +/* Simple 6502 Assembler * + * for C02 Compiler * + * Uses DASM Syntax but * + * supports 65C02 Op Codes */ + +#include +#include +#include +#include +#include "a02.h" + +#define DEBUG TRUE + +enum otypes {BINFIL, PRGFIL}; //Object File Types + +char objtyp; //Object File Type +int orgadr; //Origin Address +int curadr; //Current Address +int lstadr; //List Address + +struct sym {int block; char name[MAXSYM]; int bytes, value;}; +struct sym symbol; //Current Symbol +struct sym symtbl[MAXGLB]; //Global Symbol Table +int symcnt; //Number of Global Labels +int blknum; //Local Label Block Number (0 = Global) + +char label[MAXSTR]; //Assembly Line Label +char mnmnc[MAXSTR]; //Opcode Mnemonic +char oprnd[MAXSTR]; //Opcode Mnemonic +char cmmnt[MAXSTR]; //Assembly Line Comment +char mcode[MAXSTR]; //Generated Bytes +char strng[MAXSTR]; //Parsed String + +unsigned char token, opmod; //OpCode Token, Modifier +unsigned int amode; //Addressing Modes +int zpage, opval; //ZeroPage Flag, Operand Value + +char hexadr[6]; //Current Address in Hexadecimal +char bytstr[5]; //String Representation of Byte + +char inplin[MAXSTR]; //Input Buffer +char *linptr; //Pointer into Input Buffer +int lineno; //Input File Line Number +int opridx; //Index into Operand +int passno; //Assembler Pass Number (1 or 2) + +char prgnam[256]; //Assembler Path and Name (from Command Line) +char inpnam[256]; //Input File Name +char outnam[256]; //Output File Name +char lstnam[256]; //List File Name +FILE *inpfil; //Input File Pointer +FILE *outfil; //Output File Pointer +FILE *lstfil; //List File Pointer + +/* Print Error Message and Exit */ +void xerror(char* format, char *s) { + if (lineno) fprintf(stderr, "%04d: ", lineno); + fprintf(stderr, format, s); + exit(EXIT_FAILURE); +} + +/* Skip Character in Input Line * + * Args: c - Character to Skip * + * Updates: linptr */ +int skpchr(char c) { + if (*linptr == c) {linptr++; return TRUE;} + else return FALSE; +} + +/* Skip Spaces in Input Line * + * Updates: linptr */ +void skpspc(void) { + while (*linptr && *linptr <= ' ') linptr++; +} + +/* Parse Word from Input Line * + * Args: skip - Skip Spaces Flag * + * *word - Buffer for Word * + * Updates: linptr * + * Returns: Word Found (TRUE/FALSE) */ +int pword(int skip, char* word) { + int wrdlen = 0; + if (skip) skpspc(); + while (isalnum(*linptr)) { + word[wrdlen++] = toupper(*linptr); + linptr++; + } + word[wrdlen] = 0; //Terminate String + if (wrdlen) return TRUE; else return FALSE; +} + +struct sym *fndsym(int block, char* name) { + for (int i=0; i < symcnt; i++) { + if (symtbl[i].block != block || strcmp(symtbl[i].name,name)) continue; + return &symtbl[i]; + } + return NULL; +} + +/* Set Symbol Value and Size */ +void setsym(int value, int bytes) { + if (DEBUG) printf("Setting Symbol %s to %d\n", symbol.name, value); + symbol.value = value; + if (bytes) symbol.bytes = bytes; + else symbol.bytes = (value > 0xFF) ? 2 : 1; +} + +/* Parse Label from Input Line + * Sets: label + * Updates: linptr + * Returns: Label Found (TRUE/FALSE) */ +int plabel(void) { + if (DEBUG) puts("Parsing Label"); + int block = (skpchr('.')) ? blknum : 0; //Local Label Block Number + int found = pword(FALSE, label); //Parse Word without Skipping Spaces + if (DEBUG) { + if (found) printf("Found Label %s\n", label); + else puts("No Label Found"); + } + skpchr(':'); //Skip Optional Label Terminator + if (found && passno == 1) { + if (label[0] && fndsym(block, label)) xerror("Duplicate Label %s Encountered\n", label); + if (DEBUG) printf("Initializing Symbol %s\n", label); + symbol.block = block; + strcpy(symbol.name, label); + setsym(curadr, 0); + } + if (block) { + for (int i=strlen(label)+1; i; i--) label[i] = label[i-1]; + label[0] = '.'; + } + return found; +} + +/* Copy Character to Operand and Increment */ +int cpychr(int c) { + if (c && toupper(*linptr) != c) return FALSE; + oprnd[opridx++] = toupper(*linptr++); + return TRUE; +} + +/* Evaluate Binary Number */ +int evlbin() { + int result = 0; + cpychr('%'); + while (isdigit(*linptr)) { + if (*linptr > '1') break; + result = (result << 1) + *linptr - '0'; + cpychr(0); + } + return result; +} + +/* Evaluate Binary Number */ +int evlchr() { + int result = 0; + cpychr('\''); + result = *linptr; + cpychr(0); + cpychr('\''); + return result; +} + +/* Evaluate Decimal Number */ +int evldec() { + int result = 0; + while (isdigit(*linptr)) { + result = result * 10 + *linptr - '0'; + cpychr(0); + } + return result; +} + +/* Evaluate Hexadecimal Number */ +int evlhex() { + int result = 0; + cpychr('$'); + while (isxdigit(*linptr)) { + int digit = *linptr - '0'; + if (digit > 9) digit = digit - 7; + result = (result << 4) + digit; + cpychr(0); + } + return result; +} + +/* Evaluate Symbol */ +struct sym *evlsym() { + char name[MAXSTR]; + int block = (cpychr('.')) ? blknum : 0; + pword(TRUE, name); + for (int i=0; name[i]; i++) oprnd[opridx++] = name[i]; + struct sym *result = fndsym(block, name); + if (passno == 2 && result == NULL) xerror("Undefined Symbol %s\n", name); + return result; +} + +/* Evaluate Term in Operand */ +int evltrm() { + int result; + skpspc(); + if (isalpha(*linptr) || *linptr == '.') { + struct sym *target = evlsym(); + result = (target) ? target->value : 0x100; + } + else if (isdigit(*linptr)) + result = evldec(); + else switch(*linptr) { + case '$': result = evlhex(); break; + case '%': result = evlbin(); break; + case '\'': result = evlchr(); break; + default: result = -1; + } + skpspc(); + if (DEBUG) printf("Term Evaluated to %d\n", result); + return result; +} + +/* Evaluate Operand */ +int evlopd(int maxsiz) { + int result = 0; + int hilo = 0; //Return LSB (1) or MSB (2) + int prns; //Optional Parenthesis after Hi/Low Operator + if (DEBUG) puts("Evaluating Operand"); + skpspc(); + if (cpychr('<')) hilo = 1; + else if (cpychr('>')) hilo = 2; + if (hilo) prns = cpychr('('); + result = evltrm(); + if (result >= 0) + while (cpychr('+')) { + int opdval = evltrm(); + if (opdval < 0) break; + result += opdval; + } + if (hilo) { + if (result < 0) xerror("Hi/Low Operator Requires Operand", ""); + if (prns) cpychr(')'); // + switch (hilo) { + case 1: result = result & 0xFF; break; //LSB + case 2: result = result >> 8; break; //MSB + } + } + if (DEBUG) printf("Operand Evaluated to %d\n", result); + if (result > maxsiz) xerror("Operand Value too Large\n", ""); + return result; +} + +/* Write Byte to Output File */ +void outbyt(int b) { + if (curadr > -1) curadr++; + if (passno != 2) return; + fputc(b & 0xFF, outfil); + sprintf(bytstr, "%02X ", b); + strcat(mcode, bytstr); +} + +/* Write Word to Output File */ +void outwrd(int w) { + outbyt(w & 0xff); + outbyt(w >> 8); +} + +/* Assemble BYTE Pseudo-Op */ +void asmbyt(void) { + do { + if (cpychr('"')) { //String Operand + while (!cpychr('"')) {outbyt(*linptr); cpychr(0); } + skpspc(); + } else + outbyt(evlopd(0xFF)); //Evaluate Operand + } while (cpychr(',')); +} + +/* Assemble WORD Pseudo-Op */ +void asmwrd(void) { + do { + outwrd(evlopd(0xFFFF)); //Evaluate Operand + } while (cpychr(',')); +} + +/* Assemble EQU Pseudo-Op */ +void asmequ(void) { + if (label[0] == 0) xerror("EQUate without Label", 0); + setsym(evlopd(0xFFFF), 0); +} + +/* Assemble ORG Pseudo-Op */ +void asmorg(void) { + orgadr = evlopd(0xFFFF); + curadr = orgadr; + lstadr = orgadr; + if (passno == 1 && symbol.name[0]) { + symbol.value = orgadr; + symbol.bytes = 2; + } + if (passno == 2 && objtyp == PRGFIL) + outwrd(orgadr); +} + +/* Assemble PROCESSOR Pseudo-Op */ +void asmprc(void) { + skpspc(); + while (isalnum(*linptr)) cpychr(0); + if (DEBUG) printf("Ignoring Operand %s\n", oprnd); +} + +/* Assemble SUBROUTINE Pseudo-Op */ +void asmsub(void) { + blknum++; + sprintf(oprnd, "%d", blknum); opridx = strlen(oprnd); + if (DEBUG) printf("Block Number set to %s\n", oprnd); +} + +/* Assemble FILL Pseudo-Op */ +void asmfll(void) { + int size = evlopd(0xFFFF); + for (int i=0; ivalue - curadr - 2); + } + else if (cpychr('+')) offset = evlopd(0xFF); + else if (cpychr('-')) offset = -evlopd(0xFF); + else xerror("Illegal Branch Operand\n", ""); + if ((offset > 127 || offset < -128) && passno == 2) + xerror("Branch Out of Range\n", ""); + if (DEBUG) printf("Branch Offset %d\n", offset); + opval = offset & 0xFF; +} + +/* Assemble Immediate Mode Instruction */ +void asmimd(void) { + opval = evlopd(0xFF); + zpage = TRUE; + opmod = 0x08; //Immediate +} + +/* Assemble Indirect Mode Instruction */ +void asmind(void) { + if (DEBUG) puts("Assembling Indirect Mode Instruction"); + zpage = TRUE; opval = evlopd(0xFFFF); + if (cpychr(',') && cpychr('X') && chkmod(INDCX)) cpychr(')'); ////(Indirect,X) opmod=0 + else if (cpychr(')')) { + if (cpychr(',') && cpychr('Y') && chkmod(INDCY)) opmod = 0x10; //(Indirect),Y + else if (chkmod(INDCT)) opmod = 0x11; //(Indirect) + if (token == 0x4C) zpage = FALSE; //JMP (Indirect Absolute) + } + else chkmod(0); //Illegal Addressing Mode + if (zpage && opval > 0x00FF) xerror("Operand Value too Large\n", ""); +} + +/* Assemble Implied/Accumulator/Absolute/ZeroPage Mode Instruction */ +void asmiaz(void) { + opval = evlopd(0xFFFF); + if (opval < 0) { + if (amode != IMPLD) //Implied + if (chkmod(ACMLT)) opmod = 0x08; //Accumulator + return; + } + zpage = (opval <= 0xff) ? TRUE : FALSE; + if (zpage && chkmod(ZPAGE)) opmod = 0x04; //ZeroPage + else if (chkmod(ABSLT)) opmod = 0x0C; //Absolute + if (cpychr(',')) { + if (cpychr('X')) { + if (zpage && chkmod(ZPAGX)) opmod = 0x14; //ZeroPage,X + else if (chkmod(ABSLX)) opmod = 0x1C; //Absolute,X + } else if (cpychr('Y')) { + if (zpage && (token == 0x82 || token == 0xA2)) + opmod = 0x14; //ZeroPage,Y + else {zpage = FALSE; opmod = 0x18;} //Absolute,Y + } else chkmod(0); //Illegal Addressing Mode + } +} + +/* Fix Opcode (if needed) */ +unsigned char fixopc(void) { + if (DEBUG) printf("Fixing OpCode $%02X+$%02X\n", token, opmod); + for (int i=0; opfix[i].token; i++) + if (opfix[i].token == token && opfix[i].opmod == opmod) + return opfix[i].opcode; + return token + opmod; +} + +/* Assemble Opcode */ +int asmopc(void) { + opmod = 0; + if (lkpopc(opclst) == FALSE) xerror("Invalid Mnemonic %s\n", mnmnc); + if (DEBUG) printf("Assembling Opcode Token 0x%02X\n", token); + if (DEBUG) printf("Addressing Mode Mask 0x%04X\n", amode); + skpspc(); + if (amode == 0) return asmpso(); //Pseudo-Op + else if (amode == RELTV) asmbrn(); //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 + int opcode = fixopc(); + if (DEBUG) printf("Writing OpCode $%02X\n", opcode); + outbyt(opcode); + 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: linptr + * Returns: Label Found (TRUE/FALSE) */ +int pmnmnc(void) { + if (DEBUG) puts("Parsing Mnemonic"); + int found = pword(TRUE, mnmnc); + opridx = 0; //Initialize Operand Index + if (found) asmopc(); + oprnd[opridx] = 0; //Terminate Operand String + return found; +} + +/* Parse Comment from Input Line + * Sets: cmmnt + * Updates: linptr */ +void pcmmnt(void) { + skpspc(); + int i = 0; + while (*linptr >= ' ') cmmnt[i++] = *linptr++; + cmmnt[i] = 0; //Terminate Comment + if (DEBUG) {if (i) printf("Comment: %s\n"); else puts("No Comment Found");} +} + +/* Add Label to Symbol Table */ +void addsym() { + if (symbol.value<0) xerror("Origin Not Set", ""); + memcpy(&symtbl[symcnt++], &symbol, sizeof(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(int pass) { + passno = pass; //Assembly Pass Number + if (DEBUG) printf("Assembling Pass %d\n", pass); + lineno = 1; //Initialize Input File Line Number + blknum = 1; //Initialize Local Block Number + orgadr = -1; //Origin Address Not Set + curadr = orgadr; //Set Current Address to Origin + rewind(inpfil); //Start at Beginning of Input File + while (!feof(inpfil)) { + linptr = fgets(inplin, MAXSTR, inpfil); + if (linptr == NULL) break; + if (DEBUG) printf("%05d %04X: %s", lineno, curadr, inplin); + lstadr = curadr; //Set List Address + mcode[0] = 0; //Clear Generated Macbine Code + plabel(); //Parse Label + pmnmnc(); //Parse Mnemonic + pcmmnt(); //Parse Comment + if (passno == 1 && 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 ); + } + lineno++; + } +} + +/* Print Symbol Table */ +void prtsym(void) { + fprintf(lstfil, "\n%s Symbol Table\nBlock Name Size Value\n", "Global"); + for (int i=0; i