Commit: 3e0ecb4ad9e25690a1ca9779d4f6ea4c26961b9d Author: Vi Grey Date: 2023-09-15 15:28 UTC Summary: Fork commit README.TXT | 311 ++++++++++++ asm6.c | 2193 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 2504 insertions(+) diff --git a/README.TXT b/README.TXT new file mode 100644 index 0000000..a437eaa --- /dev/null +++ b/README.TXT @@ -0,0 +1,311 @@ +-------------------------------------------------------------- +ASM6 (v1.6) +A 6502 assembler by loopy (loopy at mm.st) +-------------------------------------------------------------- + +Yes, it's another 6502 assembler. I built it to do NES development, but you +can probably use it for just about anything. Why use this instead of one of +the other zillion assemblers out there? I don't know, but choice is good, +right? :) I wrote it because I thought most others were either too finicky, +had weird syntax, took too much work to set up, or too bug-ridden to be useful. + +This is free software. You may use, modify, and / or redistribute any part +of this software in any fashion. + +-------------------------------------------------------------- +Command line +-------------------------------------------------------------- + +Usage: + + asm6 [-options] sourcefile [outputfile] [listfile] + +Options: + + -? Show some help + -l Create listing + -L Create verbose listing (expand REPT, MACRO) + -d: Define a symbol and make it equal to 1 + -q Quiet mode (suppress all output unless there's an error) + Default output is .bin + Default listing is .lst + +-------------------------------------------------------------- +Syntax +-------------------------------------------------------------- + +Comments begin with a semicolon (;). A colon (:) following a label is +optional. + + examples: + + lda #$00 ;hi there + label1: jmp label2 + label2 beq label1 + +-------------------------------------------------------------- +Numbers and expressions +-------------------------------------------------------------- + +Hexadecimal numbers begin with '$' or end with 'h'. Binary numbers begin +with '%' or end with 'b'. Characters and strings are surrounded by +single or double quotes. The characters (' " \) within quotes must be +preceded by a backslash (\). + + examples: + + 12345 + '12345' + $ABCD + 0ABCDh + %01010101 + 01010101b + +Supported operators (listed by precedence): + + ( ) + (unary) + - ~ ! < > + * / % + + - + << >> + < > <= >= + = == != <> + & + ^ + | + && + || + +'=' and '<>' are equivalent to C's '==' and '!=' operators. The unary '<' +and '>' operators give the lower and upper byte of a 16-bit word (respectively). +All other operators function like their C equivalents. + +-------------------------------------------------------------- +Labels +-------------------------------------------------------------- + +Labels are case sensitive. The special '$' label holds the current program +address. Labels beginning with '@' are local labels. They have limited scope, +visible only between non-local labels. Names of local labels may be reused. + + label1: + @tmp1: + @tmp2: + label2: + @tmp1: + @tmp2: + +Labels beginning with one or more '+' or '-' characters are nameless labels, +especially useful for forward and reverse branches. + + example: + + -- ldx #0 + - lda $2002 ;loop (wait for vblank) + bne - + - lda $2002 ;nameless labels are easy to reuse.. + bne - + + cpx #69 + beq + ;forward branch.. + cpx #96 + beq +here ;use more characters to make more unique + + jmp -- ;multiple --'s handy for nested loops + + ldx #0 + +here nop + +-------------------------------------------------------------- +Assembler directives (in no particular order) +-------------------------------------------------------------- + +All directives are case insensitive and can also be preceded by a period (.) + + +EQU + + For literal string replacement, similar to #define in C. + + one EQU 1 + plus EQU + + DB one plus one ;DB 1 + 1 + += + + Unlike EQU, statements with '=' are evaluated to a number first. + Also unlike EQU, symbols created with '=' can be reused. + + i=1 + j EQU i+1 + k=i+1 ;k=1+1 + i=j+1 ;i=i+1+1 + i=k+1 ;i=2+1 + +INCLUDE (also INCSRC) + + Assemble another source file as if it were part of the current + source. + + INCLUDE whatever.asm + +INCBIN (also BIN) + + Add the contents of a file to the assembly output. + + moredata: INCBIN whatever.bin + + An optional file offset and size can be specified. + + INCBIN foo.bin, $400 ;read foo.bin from $400 to EOF + INCBIN foo.bin, $200, $2000 ;read $2000 bytes, starting from $200 + +DB, DW (also BYTE/WORD, DCB/DCW, DC.B/DC.W) + + Emit byte(s) or word(s). Multiple arguments are separated by + commas. Strings can be "shifted" by adding a value to them (see + example). + + DB $01,$02,$04,$08 + DB "ABCDE"+1 ;equivalent to DB "BCDEF" + DB "ABCDE"-"A"+32 ;equivalent to DB 32,33,34,35,36 + +DL, DH + + Similar to DB, outputting only the LSB or MSB of a value. + + DL a,b,c,d ;equivalent to DB a, >b, >c, >d + +HEX + + Compact way of laying out a table of hex values. Only raw hex values + are allowed, no expressions. Spaces can be used to separate numbers. + + HEX 456789ABCDEF ;equivalent to DB $45,$67,$89,$AB,$CD,$EF + HEX 0 1 23 4567 ;equivalent to DB $00,$01,$23,$45,$67 + +DSB, DSW (also DS.B/DS.W) + + Define storage (bytes or words). The size argument may be followed + by a fill value (default filler is 0). + + DSB 4 ;equivalent to DB 0,0,0,0 + DSB 8,1 ;equivalent to DB 1,1,1,1,1,1,1,1 + DSW 4,$ABCD ;equivalent to DW $ABCD,$ABCD,$ABCD,$ABCD + +PAD + + Fill memory from the current address to a specified address. A fill + value may also be specified. + + PAD $FFFA ;equivalent to DSB $FFFA-$ + PAD $FFFA,$EA ;equivalent to DSB $FFFA-$,$EA + +ORG + + Set the starting address if it hasn't been assigned yet, otherwise + ORG functions like PAD. + + ORG $E000 ;start assembling at $E000 + . + . + . + ORG $FFFA,$80 ;equivalent to PAD $FFFA,$80 + +ALIGN + + Fill memory from the current address to an N byte boundary. A fill + value may also be specified. + + ALIGN 256,$EA + +FILLVALUE + + Change the default filler for PAD, ALIGN, etc. + + FILLVALUE $FF + +BASE + + Set the program address. This is useful for relocatable code, + multiple code banks, etc. The same can also be accomplished by + assigning the '$' symbol directly (i.e. '$=9999'). + + oldaddr=$ + BASE $6000 + stuff: + . + . + . + BASE oldaddr+$-stuff + +IF / ELSEIF / ELSE / ENDIF + + Process a block of code if an expression is true (nonzero). + + IF j>0 + DB i/j + ELSE + DB 0 + ENDIF + +IFDEF / IFNDEF + + Process a block of code if a symbol has been defined / not defined. + + IFDEF _DEBUG_ + . + . + . + ENDIF + +MACRO / ENDM + + MACRO name args... + + Define a macro. Macro arguments are comma separated. + Labels defined inside macros are local (visible only to that macro). + + MACRO setAXY x,y,z + LDA #x + LDX #y + LDY #z + ENDM + + setAXY $12,$34,$56 + ;expands to LDA #$12 + ; LDX #$34 + ; LDY #$56 + +REPT / ENDR + + Repeat a block of code a specified number of times. + Labels defined inside REPT are local. + + i=0 + REPT 256 + DB i + i=i+1 + ENDR + +ENUM / ENDE + + Reassign PC and suppress assembly output. Useful for defining + variables in RAM. + + ENUM $200 + foo: db 0 + foo2: db 0 + ENDE + +ERROR + + Stop assembly and display a message. + + IF x>100 + ERROR "X is out of range :(" + ENDIF + + +-------------------------------------------------------------- + \ No newline at end of file diff --git a/asm6.c b/asm6.c new file mode 100644 index 0000000..f8f9307 --- /dev/null +++ b/asm6.c @@ -0,0 +1,2193 @@ +/* History: +1.6 + Prevent error overload by emitting 2 bytes when branch instructions fail to parse + Bugfix for negative numbers being parsed incorrectly after too many passes are made + Compatible with big-endian and 64-bit machines + -q (quiet mode) command line option added + Lots of miscellaneous code cleanup +1.51 + Added extra INCBIN args +1.5 + Added local labels + Added DL, DH directives + Added ERROR directive + Bugfix for ACC opcode type +1.4 + Decided to start keeping a history. + Added some special handling of IFDEF in expandline() + Changed macro usage: no parentheses around arg list, and args must be comma separated. +*/ + +//todo - do NOT open source files in update mode, since we do not want to modify them in any way +//todo - don't open text files in binary mode +//todo - thoroughly verify operation on big-endian machine +//todo - avoid putting special values into pointers, like (char*)1 +//todo - don't depend on platform supporting unaligned objects +//todo - make everything static +//todo - redundant parsing code is all over the place, try clean it up / consolidate + +#include +#include +#include +#include +#include +#include + +#define VERSION "1.6" + +#define addr firstlabel.value//'$' value +#define NOORIGIN -0x40000000//nice even number so aligning works before origin is defined +#define INITLISTSIZE 128//initial label list size +#define BUFFSIZE 8192//file buffer (inputbuff, outputbuff) size +#define WORDMAX 128 //used with getword() +#define LINEMAX 2048//plenty of room for nested equates +#define MAXPASSES 7//# of tries before giving up +#define IFNESTS 32//max nested IF levels +#define DEFAULTFILLER 0 //default fill value +#define LOCALCHAR '@' + +static void* true_ptr = &true_ptr; + +enum labeltypes {LABEL,VALUE,EQUATE,MACRO,RESERVED}; +//LABEL: known address +//VALUE: defined with '=' +//EQUATE: made with EQU +//MACRO: macro (duh) +//RESERVED: reserved word +typedef struct { + const char *name; //label name + // ptrdiff_t so it can hold function pointer on 64-bit machines + ptrdiff_t value; //PC (label), value (equate), param count (macro), funcptr (reserved) + char *line; //for macro or equate, also used to mark unknown label + //*next:text->*next:text->.. + //for macros, the first lines hold param names + //for opcodes (reserved), this holds opcode definitions, see initlabels + int type; //labeltypes enum (see above) + int used; //for EQU and MACRO recursion check + int pass; //when label was last defined + int scope; //where visible (0=global, nonzero=local) + void *link; //labels that share the same name (local labels) are chained together +} label; + +label firstlabel={ //'$' label + "$",//*name + 0,//value + (char*)&true_ptr,//*line + VALUE,//type + 0,//used + 0,//pass + 0,//scope + 0,//link +}; + +typedef unsigned char byte; +typedef void (*icfn)(label*,char**); + +label *findlabel(char*); +void initlabels(); +label *newlabel(); +void getword(char*,char**,int); +int getvalue(char**); +int getoperator(char**); +int eval(char**,int); +label *getreserved(char**); +int getlabel(char*,char**); +void processline(char*,char*,int); +void listline(char*,char*); +void endlist(); +void opcode(label*,char**); +void org(label*,char**); +void base(label*,char**); +void pad(label*,char**); +void equ(label*,char**); +void equal(label*,char**); +void nothing(label*,char**); +void include(label*,char**); +void incbin(label*,char**); +void dw(label*,char**); +void db(label*,char**); +void dl(label*,char**); +void dh(label*,char**); +void hex(label*,char**); +void dsw(label*,char**); +void dsb(label*,char**); +void align(label*,char**); +void _if(label*,char**); +void ifdef(label*,char**); +void ifndef(label*,char**); +void elseif(label*,char**); +void _else(label*,char**); +void endif(label*,char**); +void macro(label*,char**); +void endm(label*,char**); +void endr(label*,char**); +void rept(label*,char**); +void _enum(label*,char**); +void ende(label*,char**); +void fillval(label*,char**); +void expandmacro(label*,char**,int,char*); +void expandrept(int,char*); +void make_error(label*,char**); + +enum optypes {ACC,IMM,IND,INDX,INDY,ZPX,ZPY,ABSX,ABSY,ZP,ABS,REL,IMP}; +int opsize[]={0,1,2,1,1,1,1,2,2,1,2,1,0}; +char ophead[]={0,'#','(','(','(',0,0,0,0,0,0,0,0}; +char *optail[]={"A","",")",",X)","),Y",",X",",Y",",X",",Y","","","",""}; +byte brk[]={0x00,IMM,0x00,ZP,0x00,IMP,-1}; +byte ora[]={0x09,IMM,0x01,INDX,0x11,INDY,0x15,ZPX,0x1d,ABSX,0x19,ABSY,0x05,ZP,0x0d,ABS,-1}; +byte asl[]={0x0a,ACC,0x16,ZPX,0x1e,ABSX,0x06,ZP,0x0e,ABS,0x0a,IMP,-1}; +byte php[]={0x08,IMP,-1}; +byte bpl[]={0x10,REL,-1}; +byte clc[]={0x18,IMP,-1}; +byte jsr[]={0x20,ABS,-1}; +byte and[]={0x29,IMM,0x21,INDX,0x31,INDY,0x35,ZPX,0x3d,ABSX,0x39,ABSY,0x25,ZP,0x2d,ABS,-1}; +byte bit[]={0x24,ZP,0x2c,ABS,-1}; +byte rol[]={0x2a,ACC,0x36,ZPX,0x3e,ABSX,0x26,ZP,0x2e,ABS,0x2a,IMP,-1}; +byte plp[]={0x28,IMP,-1}; +byte bmi[]={0x30,REL,-1}; +byte sec[]={0x38,IMP,-1}; +byte rti[]={0x40,IMP,-1}; +byte eor[]={0x49,IMM,0x41,INDX,0x51,INDY,0x55,ZPX,0x5d,ABSX,0x59,ABSY,0x45,ZP,0x4d,ABS,-1}; +byte lsr[]={0x4a,ACC,0x56,ZPX,0x5e,ABSX,0x46,ZP,0x4e,ABS,0x4a,IMP,-1}; +byte pha[]={0x48,IMP,-1}; +byte jmp[]={0x6c,IND,0x4c,ABS,-1}; +byte bvc[]={0x50,REL,-1}; +byte cli[]={0x58,IMP,-1}; +byte rts[]={0x60,IMP,-1}; +byte adc[]={0x69,IMM,0x61,INDX,0x71,INDY,0x75,ZPX,0x7d,ABSX,0x79,ABSY,0x65,ZP,0x6d,ABS,-1}; +byte ror[]={0x6a,ACC,0x76,ZPX,0x7e,ABSX,0x66,ZP,0x6e,ABS,0x6a,IMP,-1}; +byte pla[]={0x68,IMP,-1}; +byte bvs[]={0x70,REL,-1}; +byte sei[]={0x78,IMP,-1}; +byte sta[]={0x81,INDX,0x91,INDY,0x95,ZPX,0x9d,ABSX,0x99,ABSY,0x85,ZP,0x8d,ABS,-1}; +byte sty[]={0x94,ZPX,0x84,ZP,0x8c,ABS,-1}; +byte stx[]={0x96,ZPY,0x86,ZP,0x8e,ABS,-1}; +byte dey[]={0x88,IMP,-1}; +byte txa[]={0x8a,IMP,-1}; +byte bcc[]={0x90,REL,-1}; +byte tya[]={0x98,IMP,-1}; +byte txs[]={0x9a,IMP,-1}; +byte ldy[]={0xa0,IMM,0xb4,ZPX,0xbc,ABSX,0xa4,ZP,0xac,ABS,-1}; +byte lda[]={0xa9,IMM,0xa1,INDX,0xb1,INDY,0xb5,ZPX,0xbd,ABSX,0xb9,ABSY,0xa5,ZP,0xad,ABS,-1}; +byte ldx[]={0xa2,IMM,0xb6,ZPY,0xbe,ABSY,0xa6,ZP,0xae,ABS,-1}; +byte tay[]={0xa8,IMP,-1}; +byte tax[]={0xaa,IMP,-1}; +byte bcs[]={0xb0,REL,-1}; +byte clv[]={0xb8,IMP,-1}; +byte tsx[]={0xba,IMP,-1}; +byte cpy[]={0xc0,IMM,0xc4,ZP,0xcc,ABS,-1}; +byte cmp[]={0xc9,IMM,0xc1,INDX,0xd1,INDY,0xd5,ZPX,0xdd,ABSX,0xd9,ABSY,0xc5,ZP,0xcd,ABS,-1}; +byte dec[]={0xd6,ZPX,0xde,ABSX,0xc6,ZP,0xce,ABS,-1}; +byte iny[]={0xc8,IMP,-1}; +byte dex[]={0xca,IMP,-1}; +byte bne[]={0xd0,REL,-1}; +byte cld[]={0xd8,IMP,-1}; +byte cpx[]={0xe0,IMM,0xe4,ZP,0xec,ABS,-1}; +byte sbc[]={0xe9,IMM,0xe1,INDX,0xf1,INDY,0xf5,ZPX,0xfd,ABSX,0xf9,ABSY,0xe5,ZP,0xed,ABS,-1}; +byte inc[]={0xf6,ZPX,0xfe,ABSX,0xe6,ZP,0xee,ABS,-1}; +byte inx[]={0xe8,IMP,-1}; +byte nop[]={0xea,IMP,-1}; +byte beq[]={0xf0,REL,-1}; +byte sed[]={0xf8,IMP,-1}; + +void *rsvdlist[]={ //all reserved words + "BRK",brk, + "PHP",php, + "BPL",bpl, + "CLC",clc, + "JSR",jsr, + "PLP",plp, + "BMI",bmi, + "SEC",sec, + "RTI",rti, + "PHA",pha, + "BVC",bvc, + "CLI",cli, + "RTS",rts, + "PLA",pla, + "BVS",bvs, + "SEI",sei, + "DEY",dey, + "BCC",bcc, + "TYA",tya, + "LDY",ldy, + "TAY",tay, + "BCS",bcs, + "CLV",clv, + "CPY",cpy, + "INY",iny, + "BNE",bne, + "CLD",cld, + "CPX",cpx, + "INX",inx, + "BEQ",beq, + "SED",sed, + "ORA",ora, + "AND",and, + "EOR",eor, + "ADC",adc, + "STA",sta, + "LDA",lda, + "CMP",cmp, + "SBC",sbc, + "ASL",asl, + "ROL",rol, + "LSR",lsr, + "ROR",ror, + "TXA",txa, + "TXS",txs, + "LDX",ldx, + "TAX",tax, + "TSX",tsx, + "DEX",dex, + "NOP",nop, + "BIT",bit, + "JMP",jmp, + "STY",sty, + "STX",stx, + "DEC",dec, + "INC",inc, + 0, 0 +}; + +struct { + char* name; + void (*func)( label*, char** ); +} directives[]={ + "",nothing, + "IF",_if, + "ELSEIF",elseif, + "ELSE",_else, + "ENDIF",endif, + "IFDEF",ifdef, + "IFNDEF",ifndef, + "=",equal, + "EQU",equ, + "ORG",org, + "BASE",base, + "PAD",pad, + "INCLUDE",include,"INCSRC",include, + "INCBIN",incbin,"BIN",incbin, + "HEX",hex, + "WORD",dw,"DW",dw,"DCW",dw,"DC.W",dw, + "BYTE",db,"DB",db,"DCB",db,"DC.B",db, + "DSW",dsw,"DS.W",dsw, + "DSB",dsb,"DS.B",dsb, + "ALIGN",align, + "MACRO",macro, + "REPT",rept, + "ENDM",endm, + "ENDR",endr, + "ENUM",_enum, + "ENDE",ende, + "FILLVALUE",fillval, + "DL",dl, + "DH",dh, + "ERROR",make_error, + 0, 0 +}; + +char OutOfRange[]="Value out of range."; +char SeekOutOfRange[]="Seek position out of range."; +char BadIncbinSize[]="INCBIN size is out of range."; +char NotANumber[]="Not a number."; +char UnknownLabel[]="Unknown label."; +char Illegal[]="Illegal instruction."; +char IncompleteExp[]="Incomplete expression."; +char LabelDefined[]="Label already defined."; +char MissingOperand[]="Missing operand."; +char DivZero[]="Divide by zero."; +char BadAddr[]="Can't determine address."; +char NeedName[]="Need a name."; +char CantOpen[]="Can't open file."; +char ExtraENDM[]="ENDM without MACRO."; +char ExtraENDR[]="ENDR without REPT."; +char ExtraENDE[]="ENDE without ENUM."; +char RecurseMACRO[]="Recursive MACRO not allowed."; +char RecurseEQU[]="Recursive EQU not allowed."; +char NoENDIF[]="Missing ENDIF."; +char NoENDM[]="Missing ENDM."; +char NoENDR[]="Missing ENDR."; +char NoENDE[]="Missing ENDE."; +char IfNestLimit[]="Too many nested IFs."; +char undefinedPC[]="PC is undefined (use ORG first)"; + +char whitesp[]=" \t\r\n:"; //treat ":" like whitespace (for labels) +char whitesp2[]=" \t\r\n\""; //(used for filename processing) +char tmpstr[LINEMAX]; //all purpose big string + +int pass=0; +int scope;//current scope, 0=global +int nextscope;//next nonglobal scope (increment on each new block of localized code) +int lastchance=0;//set on final attempt +int needanotherpass;//still need to take care of some things.. +int error=0;//hard error (stop assembly after this pass) +char **makemacro=0;//(during macro creation) where next macro line will go. 1 to skip past macro +char **makerept;//like makemacro.. points to end of string chain +int reptcount=0;//counts rept statements during rept string storage +int iflevel=0;//index into ifdone[],skipline[] +int ifdone[IFNESTS];//nonzero if current IF level has been true +int skipline[IFNESTS];//1 on an IF statement that is false +const char *errmsg; +char *inputfilename=0; +char *outputfilename=0; +char *listfilename=0; +int verboselisting=0;//expand REPT loops in listing +const char *listerr=0;//error message for list file +label *labelhere;//points to the label being defined on the current line (for EQU, =, etc) +FILE *listfile=0; +FILE *outputfile=0; +byte outputbuff[BUFFSIZE]; +byte inputbuff[BUFFSIZE]; +int outcount;//bytes waiting in outputbuff +label **labellist; //array of label pointers (list starts from center and grows outward) +int labels;//# of labels in labellist +int maxlabels;//max # of labels labellist can hold +int labelstart;//index of first label +int labelend;//index of last label +label *lastlabel;//last label created +int nooutput=0;//supress output (use with ENUM) +int defaultfiller;//default fill value +int insidemacro=0;//macro/rept is being expanded +int verbose=1; + +static void* ptr_from_bool( int b ) +{ + if ( b ) + return true_ptr; + + return NULL; +} + +// Prints printf-style message to stderr, then exits. +// Closes and deletes output file. +static void fatal_error( const char fmt [], ... ) +{ + va_list args; + + if ( outputfile != NULL ) { + fclose( outputfile ); + remove( outputfilename ); + } + + va_start( args, fmt ); + fprintf( stderr, "\nError: " ); + vfprintf( stderr, fmt, args ); + fprintf( stderr, "\n\n" ); + va_end( args ); + + exit( EXIT_FAILURE ); +} + +// Prints printf-style message if verbose mode is enabled. +static void message( const char fmt [], ... ) +{ + if ( verbose ) { + va_list args; + va_start( args, fmt ); + vprintf( fmt, args ); + va_end( args ); + } +} + +// Same as malloc(), but prints error and exits if allocation fails +static char* my_malloc( size_t s ) +{ + char* p = malloc( s ? s : 1 ); + if ( p == NULL ) + fatal_error( "out of memory" ); + + return p; +} + +// Same as common strdup(), but prints error and exits if allocation fails +static char* my_strdup(const char *in) +{ + size_t size = strlen( in ) + 1; + char* out = my_malloc( size ); + memcpy( out, in, size ); + return out; +} + +//------------------------------------------------------- +//parsing functions +//------------------------------------------------------- + +// Not all systems support this, so we implement our own always. +// More problematic to try to use the system's version rather than +// ours in all cases. +char *my_strupr(char *string) +{ + char *s; + + if (string == NULL) { + return (char *)NULL; + } + + + for (s = string; *s; ++s) { + *s = toupper((unsigned char) *s); + } + + return string; +} + +int hexify(int i) { + if(i>='0' && i<='9') { + return i-'0'; + } else if(i>='a' && i<='f') { + return i-('a'-10); + } else if(i>='A' && i<='F') { + return i-('A'-10); + } else { + errmsg=NotANumber; + return 0; + } +} + +#define eatwhitespace(str) (*str+=strspn(*str,whitesp)) + +//find end of str, excluding any chars in whitespace +char *strend(char *str, char *whitespace) { + char c; + char *w=whitespace; + char *end=str+strlen(str); + while(*w && end!=str) { + for(w=whitespace, c=end[-1]; *w; w++) { + if(*w==c) { + end--; + break; + } + } + } + return end; +} + +//decode str into a number +//set errmsg on error +char gvline[WORDMAX]; +int dependant;//set to nonzero if symbol couldn't be resolved +int getvalue(char **str) { + char *s,*end; + int ret,chars,j; + label *p; + + getword(gvline,str,1); + + s=gvline; + if(!*s) { + errmsg=MissingOperand; + return 0; + } + + ret=chars=0; + if(*s=='$') { //hex--------------------- + s++; + if(!*s) { + ret=addr;//$ by itself is the PC + } else do { +hexi: j=hexify(*s); + s++; + chars++; + ret=(ret<<4)|j; + } while(*s); + if(chars>8) + errmsg=OutOfRange; + } else if(*s=='%') { //binary---------------------- + s++; + do { +bin: j=*s; + s++; + chars++; + j-='0'; + if(j>1) { + errmsg=NotANumber; + } + ret=(ret<<1)|j; + } while(*s); + if(chars>32) + errmsg=OutOfRange; + } else if(*s=='\'') { //char----------------- + s++; + if(*s=='\\') s++; + ret=*s; + s++; + if(*s!='\'') + errmsg=NotANumber; + } else if(*s=='"') { //char 2----------------- + s++; + if(*s=='\\') s++; + ret=*s; + s++; + if(*s!='"') + errmsg=NotANumber; + } else if(*s>='0' && *s<='9') {//number-------------- + end=s+strlen(s)-1; + if(strspn(s,"0123456789")==strlen(s)) + ret=atoi(s); + else if(*end=='b' || *end=='B') { + *end=0; + goto bin; + } else if(*end=='h' || *end=='H') { + *end=0; + goto hexi; + } else + errmsg=NotANumber; + } else { //label--------------- + p=findlabel(gvline); + if(!p) {//label doesn't exist (yet?) + needanotherpass=dependant=1; + if(lastchance) {//only show error once we're certain label will never exist + errmsg=UnknownLabel; + } + } else { + dependant|=!(*p).line; + needanotherpass|=!(*p).line; + if((*p).type==LABEL || (*p).type==VALUE) { + ret=(*p).value; + } else if((*p).type==MACRO) { + errmsg="Can't use macro in expression."; + } else {//what else is there? + errmsg=UnknownLabel; + } + } + } + return ret; +} + +char mathy[]="!^&|+-*/%()<>=,"; +enum prectypes {WHOLEEXP,ORORP,ANDANDP,ORP,XORP,ANDP,EQCOMPARE,COMPARE,SHIFT,PLUSMINUS,MULDIV,UNARY};//precedence levels +enum operators {NOOP,EQUAL,NOTEQUAL,GREATER,GREATEREQ,LESS,LESSEQ,PLUS,MINUS,MUL,DIV,MOD,AND,XOR,OR,ANDAND,OROR,LEFTSHIFT,RIGHTSHIFT};//all operators +char prec[]={WHOLEEXP,EQCOMPARE,EQCOMPARE,COMPARE,COMPARE,COMPARE,COMPARE,PLUSMINUS,PLUSMINUS,MULDIV,MULDIV,MULDIV,ANDP,XORP,ORP,ANDANDP,ORORP,SHIFT,SHIFT};//precedence of each operator +//get operator from str and advance str +int getoperator(char **str) { + *str+=strspn(*str,whitesp); //eatwhitespace + (*str)++; + switch(*(*str-1)) { + case '&': + if(**str=='&') { + (*str)++; + return ANDAND; + } else + return AND; + case '|': + if(**str=='|') { + (*str)++; + return OROR; + } else + return OR; + case '^': + return XOR; + case '+': + return PLUS; + case '-': + return MINUS; + case '*': + return MUL; + case '%': + return MOD; + case '/': + return DIV; + case '=': + if(**str=='=') + (*str)++; + return EQUAL; + case '>': + if(**str=='=') { + (*str)++; + return GREATEREQ; + } else if(**str=='>') { + (*str)++; + return RIGHTSHIFT; + } else + return GREATER; + case '<': + if(**str=='=') { + (*str)++; + return LESSEQ; + } else if(**str=='>') { + (*str)++; + return NOTEQUAL; + } else if(**str=='<') { + (*str)++; + return LEFTSHIFT; + } else + return LESS; + case '!': + if(**str=='=') { + (*str)++; + return NOTEQUAL; + } + //(to default) + default: + (*str)--; + return NOOP; + } +} + +//evaluate expression in str and advance str +int eval(char **str,int precedence) { + char unary; + char *s,*s2; + int ret,val2; + int op; + + s=*str+strspn(*str,whitesp); //eatwhitespace + unary=*s; + switch(unary) { + case '(': + s++; + ret=eval(&s,WHOLEEXP); + s+=strspn(s,whitesp); //eatwhitespace + if(*s==')') + s++; + else + errmsg=IncompleteExp; + break; + case '#': + s++; + ret=eval(&s,WHOLEEXP); + break; + case '~': + s++; + ret=~eval(&s,UNARY); + break; + case '!': + s++; + ret=!eval(&s,UNARY); + break; + case '<': + s++; + ret=eval(&s,UNARY)&0xff; + break; + case '>': + s++; + ret=(eval(&s,UNARY)>>8)&0xff; + break; + case '+': + case '-': + //careful.. might be +-label + s2=s; + s++; + op=dependant;//eval() is reentrant so don't mess up dependant + val2=needanotherpass; + dependant=0; + ret=getvalue(&s2); + if (errmsg == UnknownLabel) + errmsg=0; + if(!dependant || s2==s) {//found something or single + - + s=s2; + s2=0;//flag that we got something + dependant|=op; + } else {//not a label after all.. + dependant=op; + needanotherpass=val2; + } + if(s2) {//if it wasn't a +-label + ret=eval(&s,UNARY); + if(unary=='-') ret=-ret; + } + break; + default: + ret=getvalue(&s); + } + do { + *str=s; + op=getoperator(&s); + if(precedenceval2; + break; + case GREATEREQ: + ret=ret>=val2; + break; + case LESS: + ret=ret>=val2; + break; + } else ret=0; + } + } while(precedence='A' && c<='Z') || (c>='a' && c<='z')) {//label can start with these + return 1; + } else { + errmsg=Illegal;//fucked up instruction + return 0; + } +} + +//Expand all equates from src into dst, and remove comment +//returns a pointer to the comment in src or null. +//CRIPES what a mess... +char *expandline(char *dst,char *src) { + char *start; + char *comment=0; + char c,c2; + label *p; + int def_skip=0; + char upp[WORDMAX]; + + do { + c=*src; + if(c=='$' || (c>='0' && c<='9')) {//read past numbers (could be mistaken for a symbol, i.e. $BEEF) + do { + *dst=c; + src++; + dst++; + c=*src; + } while((c>='0' && c<='9') || (c>='A' && c<='H') || (c>='a' && c<='h')); + c=1;//don't terminate yet + } else if(c=='"' || c=='\'') {//read past quotes + *dst=c; + dst++; src++; + do { + *dst=c2=*src; + if(c2=='\\') { + dst++; src++; + *dst=*src; + } + dst++; src++; + } while(c2 && c2!=c); + c=c2; + } else if(c=='_' || c=='.' || c==LOCALCHAR || (c>='A' && c<='Z') || (c>='a' && c<='z')) {//symbol + start=src; + do {//scan to end of symbol + src++; + c=*src; + } while(c=='_' || c=='.' || c==LOCALCHAR || (c>='0' && c<='9') || (c>='A' && c<='Z') || (c>='a' && c<='z')); + + *src=0; //terminate @ end of word (temporarily) + + /* + ghey hack. + expandline() is called quite early during parsing, so + + FOO equ xxxx + ifdef FOO + + becomes + + FOO equ xxxx + ifdef xxxx + + rendering IFDEF useless, so we will bypass expansion in this special case. + I'm avoiding getreserved() because it does searching and other unnecessary crap. + */ + p=0; + if(!def_skip) { + strcpy(upp,start+(*start=='.')); + my_strupr(upp); + if(!strcmp(upp,"IFDEF") || !strcmp(upp,"IFNDEF")) { + def_skip=1; + } else { + p=findlabel(start); + } + } + + if(p) { + if((*p).type!=EQUATE || (*p).pass!=pass)//equates MUST be defined before being used otherwise they will be expanded in their own definition + p=0;//i.e. (label equ whatever) gets translated to (whatever equ whatever) + else { + if((*p).used) { + p=0; + errmsg=RecurseEQU; + } + } + } + if(p) { + (*p).used=1; + expandline(dst,(*p).line); + (*p).used=0; + } else { + strcpy(dst,start); + } + dst+=strlen(dst); + *src=c; + } else { + if(c==';') {//comment + c=0; + comment=src; + } + *dst=c; + dst++; + src++; + } + } while(c); + return comment; +} + +int eatchar(char **str,char c) { + if(c) { + *str+=strspn(*str,whitesp); //eatwhitespace + if(**str==c) { + (*str)++; + return 1; + } else + return 0; + } + return 1; +} + +//reverse string +void reverse(char *dst,char *src) { + dst+=strlen(src); + *dst=0; + while(*src) + *(--dst)=*(src++); +} + +//=========================================================================================================== + +//local: +// false: if label starts with LOCALCHAR, make it local, otherwise it's global +// true: force label to be local (used for macros) +void addlabel(char *word, int local) { + char c=*word; + label *p=findlabel(word); + if(p && local && !(*p).scope && (*p).type!=VALUE) //if it's global and we're local + p=0;//pretend we didn't see it (local label overrides global of the same name) + //global labels advance scope + if(c!=LOCALCHAR && !local) { + scope=nextscope++; + } + if(!p) {//new label + labelhere=newlabel(); + if(!(*labelhere).name)//name already set if it's a duplicate + (*labelhere).name=my_strdup(word); + (*labelhere).type=LABEL;//assume it's a label.. could mutate into something else later + (*labelhere).pass=pass; + (*labelhere).value=addr; + (*labelhere).line=ptr_from_bool(addr>=0); + (*labelhere).used=0; + if(c==LOCALCHAR || local) { //local + (*labelhere).scope=scope; + } else { //global + (*labelhere).scope=0; + } + lastlabel=labelhere; + } else {//old label + labelhere=p; + if((*p).pass==pass && c!='-') {//if this label already encountered + if((*p).type==VALUE) + return; + else + errmsg=LabelDefined; + } else {//first time seen on this pass or (-) label + (*p).pass=pass; + if((*p).type==LABEL) { + if((*p).value!=addr && c!='-') { + needanotherpass=1;//label position is still moving around + if(lastchance) + errmsg=BadAddr; + } + (*p).value=addr; + (*p).line=ptr_from_bool(addr>=0); + if(lastchance && addr<0) + errmsg=BadAddr; + } + } + } +} + +//initialize label list +void initlabels(void) { + label *p; + int i=0; + + labels=1; + labellist=(label**)my_malloc(INITLISTSIZE*sizeof(label*)); + labelstart=INITLISTSIZE/2; + labelend=labelstart; + maxlabels=INITLISTSIZE; + labellist[labelstart]=&firstlabel;//'$' label + + //add reserved words to label list + + do {//opcodes first + findlabel(rsvdlist[i]);//must call findlabel before using newlabel + p=newlabel(); + (*p).name=rsvdlist[i]; + (*p).value=(ptrdiff_t)opcode; + (*p).line=rsvdlist[i+1]; + (*p).type=RESERVED; + i+=2; + } while(rsvdlist[i]); + + i = 0; + do {//other reserved words now + findlabel(directives[i].name); + p=newlabel(); + (*p).name=directives[i].name; + (*p).value=(ptrdiff_t)directives[i].func; + (*p).type=RESERVED; + i++; + } while(directives[i].name); + lastlabel=p; +} + +//find label with this name +//returns label* if found (and scope/etc is correct), returns NULL if nothing found +//if name wasn't found, findindex points to where name would be inserted (name0) { + head=findindex+1; + findindex+=(tail-head)/2+1; + } + } while(findcmp && (tail-head)>=0); + if(findcmp) { + if(findcmp<0) + findindex++;//position findindex so the label it points to needs to shift right + return 0; + } + p=labellist[findindex]; + + //check scope: label only visible if p.scope=(scope or 0) + global=0; + if(*name=='+') {//forward labels need special treatment :P + do { + if((*p).pass!=pass) { + if(!(*p).scope) + global=p; + if((*p).scope==scope) + return p; + } + p=(*p).link; + } while(p); + } else { + do { + if(!(*p).scope) + global=p; + if((*p).scope==scope) + return p; + p=(*p).link; + } while(p); + } + return global; //return global label only if no locals were found +} + +//double list capacity +void growlist(void) { + label **tmp; + int newhead; + + maxlabels<<=1; + newhead=maxlabels/2-labels/2; + tmp=(label**)my_malloc(maxlabels*sizeof(label*)); + memcpy(tmp+newhead,labellist+labelstart,labels*sizeof(label*)); + free(labellist); + labellist=tmp; + findindex=findindex-labelstart+newhead; + labelstart=newhead; + labelend=newhead+labels-1; +} + +//make new empty label and add it to list using result from last findlabel +//ONLY use after calling findlabel +label *newlabel(void) { + label **start,**end; + label *p; + + p=(label*)my_malloc(sizeof(label)); + (*p).link=0; + (*p).scope=0; + (*p).name=0; + + if(!findcmp) {//new label with same name + (*p).name=(*labellist[findindex]).name;//share old name + //if(!scope) {//global always goes at the end + // (*lastfindlink).link=p;//add to the chain.. + //} else {//insert into the front + (*p).link=labellist[findindex]; + labellist[findindex]=p; + //} + return p; + } + if(!labelstart || labelend>=maxlabels-1)//make sure there's room to add + growlist(); + + end=&labellist[findindex]; + if(findindex>(labelstart+labels/2)) { //shift up + start=&labellist[labelend]; + for(;start>=end;start--) + *(start+1)=*start; + labelend++; + } else { //shift down + end--; + start=&labellist[labelstart]; + for(;start<=end;start++) + *(start-1)=*start; + labelstart--; + } + *end=p; + labels++; + return p; +} + +//============================================================================================================== + +void showerror(char *errsrc,int errline) { + error=1; + fprintf(stderr,"%s(%i): %s\n",errsrc,errline,errmsg); + + if(!listerr)//only list the first error for this line + listerr=errmsg; +} + +//process the open file f +char fileline[LINEMAX]; +void processfile(FILE *f, char* name) { + static int nest=0; + int nline=0; + int eof; + nest++;//count nested include()s + do { + nline++; + eof=!fgets(fileline,LINEMAX,f); + if(!eof) + processline(fileline,name,nline); + } while(!eof); + nest--; + nline--; + if(!nest) {//if main source file (not included) + errmsg=0; + if(iflevel) + errmsg=NoENDIF; + if(reptcount) + errmsg=NoENDR; + if(makemacro) + errmsg=NoENDM; + if(nooutput) + errmsg=NoENDE; + if(errmsg) + showerror(name,nline); + } +} + +//process single line +//src=source line +//errsrc=source file name +//errline=source file line number +void processline(char *src,char *errsrc,int errline) { + char line[LINEMAX];//expanded line + char word[WORDMAX]; + char *s,*s2,*comment; + char *endmac; + label *p; + + errmsg=0; + comment=expandline(line,src); + if(!insidemacro || verboselisting) + listline(line,comment); + + s=line; + if(errmsg) { //expandline error? + showerror(errsrc,errline); + } else do { + if(makemacro) { //we're inside a macro definition + p=getreserved(&s); + errmsg=endmac=0; + if(!p) {//skip over label if there is one, we're looking for "ENDM" + endmac=s; + p=getreserved(&s); + } + if(p) if((*p).value==(ptrdiff_t)endm) { + comment=0; + if(endmac) { + endmac[0]='\n'; + endmac[1]=0;//hide "ENDM" in case of "label: ENDM" + } else + makemacro=0;//don't bother adding the last line + } + if(makemacro&&makemacro!=true_ptr) { + if(comment) + strcat(line,comment); //keep comment for listing + *makemacro=my_malloc(strlen(line)+sizeof(char*)+1); + makemacro=(char**)*makemacro; + *makemacro=0; + strcpy((char*)&makemacro[1],line); + } + if(p) if((*p).value==(ptrdiff_t)endm) + makemacro=0; + break; + }//makemacro + if(reptcount) {//REPT definition is in progress? + p=getreserved(&s); + errmsg=endmac=0; + if(!p) { + endmac=s; + p=getreserved(&s); + } + if(p) { + if((*p).value==(ptrdiff_t)rept) { + ++reptcount;//keep track of how many ENDR's are needed to finish + } else if((*p).value==(ptrdiff_t)endr) { + if(!(--reptcount)) { + comment=0; + if(endmac) { + endmac[0]='\n';//hide "ENDR" in case of "label: ENDR" + endmac[1]=0; + } + } + } + } + if(reptcount || endmac) { //add this line to REPT body + if(comment) + strcat(line,comment); //keep comment for listing + *makerept=my_malloc(strlen(line)+sizeof(char*)+1); + makerept=(char**)*makerept; + *makerept=0; + strcpy((char*)&makerept[1],line); + } + if(!reptcount) {//end of REPT, expand the whole thing right now + expandrept(errline,errsrc); + } + break; + } + labelhere=0; //for non-label symbol definitions (EQU,=,etc) + s2=s; + p=getreserved(&s); + errmsg=0; + if(skipline[iflevel]) {//conditional assembly.. no code generation + if(!p) { //it was a label... ignore it and move on + p=getreserved(&s); + if(!p) break; + } + if((*p).value!=(ptrdiff_t)_else && (*p).value!=(ptrdiff_t)elseif && (*p).value!=(ptrdiff_t)endif + && (*p).value!=(ptrdiff_t)_if && (*p).value!=(ptrdiff_t)ifdef && (*p).value!=(ptrdiff_t)ifndef) + break; + } + if(!p) {//maybe a label? + if(getlabel(word,&s2)) addlabel(word,insidemacro); + if(errmsg) goto badlabel;//fucked up label + p=getreserved(&s); + } + if(p) { + if((*p).type==MACRO) + expandmacro(p,&s,errline,errsrc); + else + ((icfn)(*p).value)(p,&s); + } + if(!errmsg) {//check extra garbage + s+=strspn(s,whitesp); + if(*s) + errmsg="Extra characters on line."; + } +badlabel: + if(errmsg) { + showerror(errsrc,errline); + } + } while(0); +} + +void showhelp(void) { + puts(""); + puts("asm6 " VERSION "\n"); + puts("Usage: asm6 [-options] sourcefile [outputfile] [listfile]\n"); + puts(" -? show this help"); + puts(" -l create listing"); + puts(" -L create verbose listing (expand REPT, MACRO)"); + puts(" -d define symbol"); + puts(" -q quiet mode (no output unless error)\n"); + puts("See README.TXT for more info.\n"); +} + +//-------------------------------------------------------------------------------------------- + +int main(int argc,char **argv) { + char str[512]; + int i,notoption; + char *nameptr; + label *p; + FILE *f; + + if(argc<2) { + showhelp(); + return EXIT_FAILURE; + } + initlabels(); + notoption=0; + for(i=1;i=BUFFSIZE) { + if(fwrite(outputbuff,1,BUFFSIZE,outputfile)> 8; + output( b, size ); +} + +//end listing when src=0 +char srcbuff[LINEMAX]; +void listline(char *src,char *comment) { + static int oldpass=0; + int i; + if(!listfilename) + return; + if(oldpass!=pass) {//new pass = new listfile + oldpass=pass; + if(listfile) fclose(listfile); + listfile=fopen(listfilename,"w"); + if(!listfile) { + listfilename=0;//stop trying + // todo - if user wants a listing, this SHOULD be an error, otherwise + // he might still have old listing and think it's the current one. + // For example, he might have had it open in a text editor, preventing its + // creation here. + fputs("Can't create list file.", stderr);//not critical, just give a warning + return; + } + } else {//finish previous line + for(i=0;iLISTMAX?".. ":" ",listfile); + fputs(srcbuff,listfile); + if(listerr) { + fprintf(listfile,"*** %s\n",listerr); + listerr=0; + } + } + listcount=0; + if(src) { + if(addr<0) + fprintf(listfile," "); + else + fprintf(listfile,"%05X",(int)addr); + strcpy(srcbuff,src);//make a copy of the original source line + if(comment) strcat(srcbuff,comment); + } else { + fclose(listfile); + message("%s written.\n",listfilename); + } +} +//------------------------------------------------------ +//directive(label *id, char **next) +// +// id=reserved word +// **next=source line (ptr gets moved past directive on exit) +//------------------------------------------------------ +void equ(label *id, char **next) { + char str[LINEMAX]; + char *s=*next; + if(!labelhere) + errmsg=NeedName;//EQU without a name + else { + if((*labelhere).type==LABEL) {//new EQU.. good + reverse(str,s+strspn(s,whitesp)); //eat whitesp off both ends + reverse(s,str+strspn(str,whitesp)); + if(*s) { + (*labelhere).line=my_strdup(s); + (*labelhere).type=EQUATE; + } else { + errmsg=IncompleteExp; + } + } else if((*labelhere).type!=EQUATE) { + errmsg=LabelDefined; + } + *s=0;//end line + } +} + +void equal(label *id,char **next) { + if(!labelhere) //labelhere=index+1 + errmsg=NeedName; //(=) without a name + else { + (*labelhere).type=VALUE; + dependant=0; + (*labelhere).value=eval(next,WHOLEEXP); + (*labelhere).line=ptr_from_bool(!dependant); + } +} + +void base(label *id, char **next) { + int val; + dependant=0; + val=eval(next,WHOLEEXP); + if(!dependant && !errmsg) + addr=val; + else + addr=NOORIGIN;//undefine origin +} + +//nothing to do (empty line) +void nothing(label *id, char **next) { +} + +void include(label *id,char **next) { + char *np; + FILE *f; + + np=*next; + reverse(tmpstr,np+strspn(np,whitesp2)); //eat whitesp off both ends + reverse(np,tmpstr+strspn(tmpstr,whitesp2)); + f=fopen(np,"r+"); //read as text, the + makes recursion not possible + if(!f) { + errmsg=CantOpen; + error=1; + } else { + processfile(f,np); + fclose(f); + errmsg=0;//let main() know file was ok + } + *next=np+strlen(np);//need to play safe because this could be the main srcfile +} + +void incbin(label *id,char **next) { + int filesize, seekpos, bytesleft, i; + FILE *f=0; + + do { + //file open: + getfilename(tmpstr,next); + if(!(f=fopen(tmpstr,"rb"))) { + errmsg=CantOpen; + break; + } + fseek(f,0,SEEK_END); + filesize=ftell(f); + //file seek: + seekpos=0; + if(eatchar(next,',')) + seekpos=eval(next,WHOLEEXP); + if(!errmsg && !dependant) if(seekpos<0 || seekpos>filesize) + errmsg=SeekOutOfRange; + if(errmsg) break; + fseek(f,seekpos,SEEK_SET); + //get size: + if(eatchar(next,',')) { + bytesleft=eval(next,WHOLEEXP); + if(!errmsg && !dependant) if(bytesleft<0 || bytesleft>(filesize-seekpos)) + errmsg=BadIncbinSize; + if(errmsg) break; + } else { + bytesleft=filesize-seekpos; + } + //read file: + while(bytesleft) { + if(bytesleft>BUFFSIZE) i=BUFFSIZE; + else i=bytesleft; + fread(inputbuff,1,i,f); + output(inputbuff,i); + bytesleft-=i; + } + } while(0); + if(f) fclose(f); +} + +void hex(label *id,char **next) { + char buff[LINEMAX]; + char *src; + int dst; + char c1,c2; + getword(buff,next,0); + if(!*buff) errmsg=MissingOperand; + else do { + src=buff; + dst=0; + do { + c1=hexify(*src); + src++; + if(*src) { + c2=hexify(*src); + src++; + } else {//deal with odd number of chars + c2=c1; + c1=0; + } + buff[dst++]=(c1<<4)+c2; + } while(*src); + output((byte*)buff,dst); + getword(buff,next,0); + } while(*buff); +} + +void dw(label *id, char **next) { + int val; + do { + val=eval(next,WHOLEEXP); + if(!errmsg) { + if(val>65535 || val<-65536) + errmsg=OutOfRange; + else + output_le(val,2); + } + } while(!errmsg && eatchar(next,',')); +} + +void dl(label *id, char **next) { + byte val; + do { + val=eval(next,WHOLEEXP) & 0xff; + if(!errmsg) + output(&val,1); + } while(!errmsg && eatchar(next,',')); +} + +void dh(label *id, char **next) { + byte val; + do { + val=eval(next,WHOLEEXP)>>8; + if(!errmsg) + output(&val,1); + } while(!errmsg && eatchar(next,',')); +} + +void db(label *id,char **next) { + int val,val2; + byte *s,*start; + char c,quote; + + do { + *next+=strspn(*next,whitesp); //eatwhitespace + quote=**next; + if(quote=='"' || quote=='\'') { //string + s=start=(byte*)*next+1; + do { + c=*s; + s++; + if(!c) errmsg=IncompleteExp; + if(c=='\\') s++; + } while(!errmsg && c!=quote); + if(errmsg) continue; + s--; //point to the " + *s='0'; + *next=(char*)s; + val2=eval(next,WHOLEEXP); + if(errmsg) continue; + while(start!=s) { + if(*start=='\\') + start++; + val=*start+val2; + start++; + output_le(val,1); + } + } else { + val=eval(next,WHOLEEXP); + if(!errmsg) { + if(val>255 || val<-128) + errmsg=OutOfRange; + else + output_le(val,1); + } + } + } while(!errmsg && eatchar(next,',')); +} + +void dsw(label *id,char **next) { + int count,val=defaultfiller; + dependant=0; + count=eval(next,WHOLEEXP); + if(dependant || (count<0 && needanotherpass))//unknown count! don't do anything + count=0; + if(eatchar(next,',')) + val=eval(next,WHOLEEXP); + if(!errmsg && !dependant) if(val>65535 || val<-32768 || count<0) + errmsg=OutOfRange; + if(errmsg) return; + while(count--) + output_le(val,2); +} + +void filler(int count,char **next) { + int val=defaultfiller; + if(dependant || (count<0 && needanotherpass)) //unknown count! don't do anything + count=0; + if(eatchar(next,',')) + val=eval(next,WHOLEEXP); + if(!errmsg && !dependant) if(val>255 || val<-128 || count<0 || count>0x100000) + errmsg=OutOfRange; + if(errmsg) return; + while(count--)//!#@$ + output_le(val,1); +} + +void dsb(label *id,char **next) { + int count; + dependant=0; + count=eval(next,WHOLEEXP); + filler(count,next); +} + +void align(label *id,char **next) { + int count; + dependant=0; + count=eval(next,WHOLEEXP); + if(count>=0) { + if((unsigned int)addr%count) count-=(unsigned int)addr%count; + else count=0; + } else count=0; + filler(count,next); +} + +void pad(label *id, char **next) { + int count; + if(addr<0) { + errmsg=undefinedPC; + } else { + dependant=0; + count=eval(next,WHOLEEXP)-addr; + filler(count,next); + } +} + +void org(label *id, char **next) { + if(addr<0) base(id,next); //this is the first ORG; PC hasn't been set yet + else pad(id,next); +} + +void opcode(label *id, char **next) { + char *s,*s2; + int type,val = 0; + byte *op; + int oldstate=needanotherpass; + int forceRel = 0; + + for(op=(byte*)(*id).line;*op!=0xff;op+=2) {//loop through all addressing modes for this instruction + needanotherpass=oldstate; + strcpy(tmpstr,*next); + dependant=0; + errmsg=0; + type=op[1]; + s=tmpstr; + if(type!=IMP && type!=ACC) {//get operand + if(!eatchar(&s,ophead[type])) continue; + val=eval(&s,WHOLEEXP); + if(type==REL) { + if(!dependant) { + val-=addr+2; + if(val>127 || val<-128) { + needanotherpass=1;//give labels time to sort themselves out.. + if(lastchance) + { + errmsg="Branch out of range."; + forceRel = 1; + } + } + } + } else { + if(opsize[type]==1) { + if(!dependant) { + if(val>255 || val<-128) + errmsg=OutOfRange; + } else { + if(type!=IMM) + continue;//default to non-ZP instruction + } + } else {//opsize[type]==2 + if((val<0 || val>0xffff) && !dependant) + errmsg=OutOfRange; + } + } + if(errmsg && !dependant && !forceRel) continue; + } + + my_strupr(s); + s2=optail[type]; + while(*s2) { //opcode tail should match input: + if(!eatchar(&s,*s2)) + break; + s2++; + } + s+=strspn(s,whitesp); + if(*s || *s2) continue; + + if(addr>0xffff) + errmsg="PC out of range."; + output(op,1); + output_le(val,opsize[type]); + *next+=s-tmpstr; + return; + } + if(!errmsg) + errmsg=Illegal; +} + +void _if(label *id,char **next) { + int val; + if(iflevel>=IFNESTS-1) + errmsg=IfNestLimit; + else + iflevel++; + dependant=0; + val=eval(next,WHOLEEXP); + if(dependant || errmsg) {//don't process yet + ifdone[iflevel]=1; + skipline[iflevel]=1; + } else { + skipline[iflevel]=!val || skipline[iflevel-1]; + ifdone[iflevel]=!skipline[iflevel]; + } +} + +void ifdef(label *id,char **next) { + char s[WORDMAX]; + if(iflevel>=IFNESTS-1) + errmsg=IfNestLimit; + else + iflevel++; + getlabel(s,next); + skipline[iflevel]=!(ptrdiff_t)findlabel(s) || skipline[iflevel-1]; + ifdone[iflevel]=!skipline[iflevel]; +} + +void ifndef(label *id,char **next) { + char s[WORDMAX]; + if(iflevel>=IFNESTS-1) + errmsg=IfNestLimit; + else + iflevel++; + getlabel(s,next); + skipline[iflevel]=(ptrdiff_t)findlabel(s) || skipline[iflevel-1]; + ifdone[iflevel]=!skipline[iflevel]; +} + +void elseif(label *id,char **next) { + int val; + if(iflevel) { + dependant=0; + val=eval(next,WHOLEEXP); + if(!ifdone[iflevel]) {//no previous true statements + if(dependant || errmsg) {//don't process yet + ifdone[iflevel]=1; + skipline[iflevel]=1; + } else { + skipline[iflevel]=!val || skipline[iflevel-1]; + ifdone[iflevel]=!skipline[iflevel]; + } + } else { + skipline[iflevel]=1; + } + } else + errmsg="ELSEIF without IF."; +} + +void _else(label *id,char **next) { + if(iflevel) + skipline[iflevel]=ifdone[iflevel] || skipline[iflevel-1]; + else + errmsg="ELSE without IF."; +} + +void endif(label *id,char **next) { + if(iflevel) --iflevel; + else errmsg="ENDIF without IF."; +} + +void endm(label *id, char **next) {//ENDM is handled during macro definition (see processline) + errmsg=ExtraENDM; +} +void endr(label *id, char **next) {//ENDR is handled during macro definition (see processline) + errmsg=ExtraENDR; +} + +void macro(label *id, char **next) { + char *src; + char word[WORDMAX]; + int params; + + labelhere=0; + if(getlabel(word,next)) + addlabel(word,0); + else + errmsg=NeedName; + + makemacro=true_ptr;//flag for processline to skip to ENDM + if(errmsg) {//no valid macro name + return; + } else if((*labelhere).type==LABEL) {//new macro + (*labelhere).type=MACRO; + (*labelhere).line=0; + makemacro=&(*labelhere).line; + //build param list + params=0; + src=*next; + while(getlabel(word,&src)) {//don't affect **next directly, make sure it's a good name first + *next=src; + *makemacro=my_malloc(strlen(word)+sizeof(char*)+1); + makemacro=(char**)*makemacro; + strcpy((char*)&makemacro[1],word); + ++params; + eatchar(&src,','); + } + errmsg=0;//remove getlabel's errmsg + (*labelhere).value=params;//set param count + *makemacro=0; + } else if((*labelhere).type!=MACRO) { + errmsg=LabelDefined; + } else {//macro was defined on a previous pass.. skip past params + **next=0; + } +} + +//errline=source file line number +//errsrc=source file name +void expandmacro(label *id,char **next,int errline,char *errsrc) { + //char argname[8]; + char macroerr[WORDMAX*2];//this should be enough, i hope.. + char **line; + int linecount=0; + int oldscope; + int arg, args; + char c,c2,*s,*s2,*s3; + + if((*id).used) { + errmsg=RecurseMACRO; + return; + } + + oldscope=scope;//watch those nested macros.. + scope=nextscope++; + insidemacro++; + (*id).used=1; + sprintf(macroerr,"%s(%i):%s",errsrc,errline,(*id).name); + line=(char**)((*id).line); + + //define macro params + s=*next; + args=(*id).value; //(named args) + arg=0; + do { + s+=strspn(s,whitesp);//eatwhitespace s=param start + s2=s; //s2=param end + s3=strpbrk(s2,",'\""); //stop at param end or string definition + if(!s3) s3=strchr(s2,0); + c=*s3; + if(c=='"' || c=='\'') {//go to end of string + s3++; + do { + c2=*s3; + s3++; + if(c2=='\\') s3++; + } while(c2 && c2!=c); + if(!c2) s3--;//oops..too far + c=*s3; + } + s2=s3; + *s2=0; + if(*s) {//arg not empty + // sprintf(argname,"\\%i",arg); //make indexed arg + // addlabel(argname,1); + // equ(0,&s); + if(arg