/* Simple 6502 Assembler for CC02 Compiler      *
 * Uses DASM Syntax but supports 65C02 Op Codes */

#include <stddef.h02>
#include <stdlib.h02>
#include <args.h02>
#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();
  }
}

/* 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");

}

main:
  symbgn = &$8000; symend = &$A000; symptr = symbgn; //Set up Symbol Table
  symblk($FF); blkset(0); //Initialize Symbol Table
  doargs(); //Read File Names and Options from Command Line Arguments
  inpfil = opnfil(0,inpnam);
  asmfil(0);
  fclose(inpfil);
  prttbl();
  //if (lstfil && symcnt) prttbl();     //Print Symbol Table
  putln("ASSEMBLY COMPLETE");

  goto exit;
  