/* ---------------------------------------------------------------------- * FILE: parser.y * PACKAGE: as31 - 8031/8051 Assembler. * * DESCRIPTION: * This file contains the yacc parser for the assembler. * Related to this are the following: * error(), warn(), yyerror() * genbyte(), genword(), genstr(), makeop() * * * REVISION HISTORY: * Jan. 19, 1990 - Created. (Ken Stauffer) * * AUTHOR: * All code in this file written by Ken Stauffer (University of Calgary). * January 1990. * */ %{ #include #include #define NOPE #include "as31.h" #undef NOPE #define YYSTYPE union ystack static unsigned char bytebuf[1024]; /* used by dumplist() */ static int bytecount; void yyerror(const char *s); int makeop(struct opcode * op, struct mode *m, int add); void inclc(int i); char *padline(char *line); void dumplist(char *txt, int show); void genbyte(int b); void genstr(const char *s); void genword(unsigned long w); /* ------------------------ G R A M M E R ----------------------------- */ %} %token STRING %token D_ORG %token D_BYTE %token D_WORD %token D_SKIP %token D_EQU %token D_FLAG %token D_END %token ACALL %token ADD %token ADDC %token AJMP %token ANL %token CJNE %token CLR %token CPL %token DA %token DEC %token DIV %token DJNZ %token INC %token JB %token JBC %token JC %token JMP %token JNB %token JNC %token JNZ %token JZ %token LCALL %token LJMP %token MOV %token MOVC %token MOVX %token NOP %token MUL %token ORL %token POP %token PUSH %token RET %token RETI %token RL %token RLC %token RR %token RRC %token SETB %token SJMP %token SUBB %token SWAP %token XCH %token XCHD %token XRL %token AB %token A %token C %token PC %token DPTR %token BITPOS %token R0 %token R1 %token R2 %token R3 %token R4 %token R5 %token R6 %token R7 %token VALUE %token SYMBOL %left '+' '-' %left '*' '/' '%' %left '|' '&' %left '>' '<' %start program %% program : linelist { } ; linelist : linelist line | line ; line : undefsym ':' linerest { if (abort_asap) {YYABORT;} if( pass1 ) { $1.sym->type = LABEL; $1.sym->value = lc; } inclc($3.value); bytecount = 0; } | linerest { inclc($1.value); bytecount = 0; } ; linerest : directive '\n' { $$.value = $1.value; if( dashl && pass2 ) dumplist($2.str,1); } | instr '\n' { $$.value = $1.value; if( dashl && pass2 ) dumplist($2.str,1); } | '\n' { $$.value = 0; if( dashl && pass2 ) dumplist($1.str,0); } | error { seek_eol(); } '\n' { $$.value = 0; if( dashl && pass2 ) dumplist($1.str,0); } ; /* -------------------- * DIRECTIVES: * */ directive : '.' D_ORG defexpr { lc = $3.val.v; if( pass2 ) emitaddr(lc); bytecount = 0; $$.value = 0; } | '.' D_BYTE blist { $$.value = $3.value; } | '.' D_WORD wlist { $$.value = $3.value; } | '.' D_SKIP defexpr { $$.value = $3.val.v; if( pass2 ) emitaddr(lc+$$.value); } | '.' D_EQU undefsym ',' expr { if( $5.val.d == 0 ) warn("Expression is undefined in pass 1"); $3.sym->type = LABEL; $3.sym->value = $5.val.v; $$.value = 0; } | '.' D_FLAG SYMBOL ',' flag { $3.sym->type = LABEL; $3.sym->value = $5.value; $$.value = 0; } | '.' D_END { $$.value = 0; } ; defexpr : expr { if( $1.val.d == 0 ) warn("Expression is undefined in pass 1"); if( !(isbit16($1.val.v)) ) warn("Value greater than 16-bits"); $$.value = $1.val.v; } ; flag : flagv BITPOS { if( !isbit8($1.value) ) warn("Bit address exceeds 8-bits"); if( isbmram($1.value) ) $$.value = ($1.value-0x20)*8+ $2.value; else if( isbmsfr($1.value) ) $$.value = $1.value + $2.value; else warn("Invalid bit addressable RAM location"); } ; flagv : SYMBOL { if( $1.sym->type == UNDEF ) warn("Symbol %s must be defined in pass 1",$1.sym->name); $$.value = $1.sym->value; } | VALUE { $$.value = $1.value; } ; undefsym : SYMBOL { if( $1.sym->type != UNDEF && pass1) warn("Attempt to redefine symbol: %s",$1.sym->name); $$.sym = $1.sym; } ; blist : blist ',' data8 { if( pass2 ) genbyte($3.value); $$.value = $1.value + 1; } | blist ',' STRING { if( pass1 ) $$.value = $1.value + $3.value; else { $$.value = $1.value + strlen($3.str); genstr($3.str); free($3.str); } } | data8 { if( pass2 ) genbyte($1.value); $$.value = 1; } | STRING { if( pass1 ) $$.value = $1.value; else { $$.value = strlen($1.str); genstr($1.str); free($1.str); } } ; wlist : wlist ',' data16 { if( pass2 ) genword($3.value); $$.value = $1.value + 2; } | data16 { if( pass2 ) genword($1.value); $$.value = 2; } ; /* -------------------- * EXPRESSIONS: * */ expr : '*' { $$.val.v = lc; $$.val.d = 1; } | '(' expr ')' { $$.val.v = $2.val.v; $$.val.d = $2.val.d; } | '-' expr %prec '*' { $$.val.v = -$2.val.v; $$.val.d = $2.val.d; } | expr '|' expr { $$.val.v = $1.val.v | $3.val.v; $$.val.d = $1.val.d && $3.val.d; } | expr '&' expr { $$.val.v = $1.val.v & $3.val.v; $$.val.d = $1.val.d && $3.val.d; } | expr '*' expr { $$.val.v = $1.val.v * $3.val.v; $$.val.d = $1.val.d && $3.val.d; } | expr '/' expr { $$.val.v = $1.val.v / $3.val.v; $$.val.d = $1.val.d && $3.val.d; } | expr '%' expr { $$.val.v = $1.val.v % $3.val.v; $$.val.d = $1.val.d && $3.val.d; } | expr '-' expr { $$.val.v = $1.val.v - $3.val.v; $$.val.d = $1.val.d && $3.val.d; } | expr '+' expr { $$.val.v = $1.val.v + $3.val.v; $$.val.d = $1.val.d && $3.val.d; } | expr '>' '>' expr { $$.val.v = $1.val.v >> $4.val.v; $$.val.d = $1.val.d && $4.val.d; } | expr '<' '<' expr { $$.val.v = $1.val.v << $4.val.v; $$.val.d = $1.val.d && $4.val.d; } | SYMBOL { if( pass1 ) { $$.val.v = $1.sym->value; $$.val.d = ($1.sym->type != UNDEF); } else { if( $1.sym->type == UNDEF ) warn("Undefined symbol %s",$1.sym->name); $$.val.v = $1.sym->value; $$.val.d = 1; } } | VALUE { $$.val.v = $1.val.v; $$.val.d=1; } ; /* -------------------- * INSTRUCTIONS: * */ instr : NOP { $$.value = makeop($1.op,NULL,0); } | ACALL addr11 { $$.value = makeop($1.op,&$2.mode,0); } | AJMP addr11 { $$.value = makeop($1.op,&$2.mode,0); } | ADD two_op1 { $$.value = makeop($1.op,&$2.mode,0); } | ADDC two_op1 { $$.value = makeop($1.op,&$2.mode,0); } | SUBB two_op1 { $$.value = makeop($1.op,&$2.mode,0); } | XRL two_op1 { $$.value = makeop($1.op,&$2.mode,0); } | XRL two_op2 { $$.value = makeop($1.op,&$2.mode,4); } | ANL two_op1 { $$.value = makeop($1.op,&$2.mode,0); } | ANL two_op2 { $$.value = makeop($1.op,&$2.mode,4); } | ANL two_op3 { $$.value = makeop($1.op,&$2.mode,6); } | ORL two_op1 { $$.value = makeop($1.op,&$2.mode,0); } | ORL two_op2 { $$.value = makeop($1.op,&$2.mode,4); } | ORL two_op3 { $$.value = makeop($1.op,&$2.mode,6); } | XCH two_op1 { if( get_md($2.mode) == 3 ) warn("Immediate mode is illegal"); $$.value = makeop($1.op,&$2.mode,0); } | INC single_op1 { $$.value = makeop($1.op,&$2.mode,0); } | INC DPTR { $$.value = makeop($1.op,NULL,4); } | DEC single_op1 { $$.value = makeop($1.op,&$2.mode,0); } | DA A { $$.value = makeop($1.op,NULL,0); } | DIV AB { $$.value = makeop($1.op,NULL,0); } | JMP '@' A '+' DPTR { $$.value = makeop($1.op,NULL,0); } | JMP '@' DPTR '+' A { $$.value = makeop($1.op,NULL,0); } | MUL AB { $$.value = makeop($1.op,NULL,0); } | RET { $$.value = makeop($1.op,NULL,0); } | RETI { $$.value = makeop($1.op,NULL,0); } | RL A { $$.value = makeop($1.op,NULL,0); } | RLC A { $$.value = makeop($1.op,NULL,0); } | RR A { $$.value = makeop($1.op,NULL,0); } | RRC A { $$.value = makeop($1.op,NULL,0); } | SWAP A { $$.value = makeop($1.op,NULL,0); } | XCHD two_op1 { if( get_md($2.mode) != 2 ) warn("Invalid addressing mode"); $$.value = makeop($1.op,&$2.mode,-2); } | CLR single_op2 { $$.value = makeop($1.op,&$2.mode,0); } | CPL single_op2 { $$.value = makeop($1.op,&$2.mode,0); } | SETB single_op2 { if( get_md($2.mode) == 0 ) warn("Invalid addressing mode"); $$.value = makeop($1.op,&$2.mode,-1); } | PUSH data8 { struct mode tmp; set_md(tmp,0); set_ov(tmp,0); set_sz(tmp,1); set_b1(tmp,$2.value); $$.value = makeop($1.op,&tmp,0); } | POP data8 { struct mode tmp; set_md(tmp,0); set_ov(tmp,0); set_sz(tmp,1); set_b1(tmp,$2.value); $$.value = makeop($1.op,&tmp,0); } | LJMP addr16 { $$.value = makeop($1.op,&$2.mode,0); } | LCALL addr16 { $$.value = makeop($1.op,&$2.mode,0); } | JC relative { $$.value = makeop($1.op,&$2.mode,0); } | JNC relative { $$.value = makeop($1.op,&$2.mode,0); } | JNZ relative { $$.value = makeop($1.op,&$2.mode,0); } | JZ relative { $$.value = makeop($1.op,&$2.mode,0); } | SJMP relative { $$.value = makeop($1.op,&$2.mode,0); } | CJNE three_op1 { $$.value = makeop($1.op,&$2.mode,0); } | JB two_op4 { $$.value = makeop($1.op,&$2.mode,0); } | JNB two_op4 { $$.value = makeop($1.op,&$2.mode,0); } | JBC two_op4 { $$.value = makeop($1.op,&$2.mode,0); } | DJNZ two_op5 { $$.value = makeop($1.op,&$2.mode,0); } | MOV two_op1 { $$.value = makeop($1.op,&$2.mode,0); } | MOV two_op2 { $$.value = makeop($1.op,&$2.mode,4); } | MOV two_op6 { $$.value = makeop($1.op,&$2.mode,6); } | MOVC A ',' '@' A '+' DPTR { $$.value = makeop($1.op,NULL,0); } | MOVC A ',' '@' DPTR '+' A { $$.value = makeop($1.op,NULL,0); } | MOVC A ',' '@' A '+' PC { $$.value = makeop($1.op,NULL,1); } | MOVC A ',' '@' PC '+' A { $$.value = makeop($1.op,NULL,1); } | MOVX A ',' '@' regi { $$.value = makeop($1.op,NULL,$5.value); } | MOVX A ',' '@' DPTR { $$.value = makeop($1.op,NULL,2); } | MOVX '@' regi ',' A { $$.value = makeop($1.op,NULL,$3.value+3); } | MOVX '@' DPTR ',' A { $$.value = makeop($1.op,NULL,5); } ; /* -------------------- * ADDRESSING MODES: * */ two_op1 : A ',' reg { set_md($$.mode,0); set_ov($$.mode, $3.value); set_sz($$.mode, 0); } | A ',' data8 { set_md($$.mode,1); set_ov($$.mode,0); set_sz($$.mode,1); set_b1($$.mode,$3.value); } | A ',' '@' regi { set_md($$.mode,2); set_ov($$.mode,$4.value); set_sz($$.mode,0); } | A ',' '#' data8 { set_md($$.mode,3); set_ov($$.mode,0); set_sz($$.mode,1); set_b1($$.mode,$4.value); } ; two_op2 : data8 ',' A { set_md($$.mode,0); set_ov($$.mode,0); set_sz($$.mode,1); set_b1($$.mode,$1.value); } | data8 ',' '#' data8 { set_md($$.mode,1); set_ov($$.mode,0); set_sz($$.mode,2); set_b1($$.mode,$1.value); set_b2($$.mode,$4.value); } ; two_op3 : C ',' bit { set_md($$.mode,0); set_ov($$.mode,0); set_sz($$.mode,1); set_b1($$.mode,$3.value); } | C ',' '/' bit { set_md($$.mode,1); set_ov($$.mode,0); set_sz($$.mode,1); set_b1($$.mode,$4.value); } | C ',' '!' bit { set_md($$.mode,1); set_ov($$.mode,0); set_sz($$.mode,1); set_b1($$.mode,$4.value); } ; two_op4 : bit ',' rel { set_md($$.mode,0); set_ov($$.mode,0); set_sz($$.mode,2); set_b1($$.mode,$1.value); set_b2($$.mode,$3.value); } ; two_op5 : reg ',' rel2 { set_md($$.mode,0); set_ov($$.mode,$1.value); set_sz($$.mode,1); set_b1($$.mode,$3.value); } | data8 ',' rel { set_md($$.mode,1); set_ov($$.mode,0); set_sz($$.mode,2); set_b1($$.mode,$1.value); set_b2($$.mode,$3.value); } ; two_op6 : reg ',' A { set_md($$.mode,0); set_ov($$.mode,$1.value); set_sz($$.mode,0); } | reg ',' data8 { set_md($$.mode,1); set_ov($$.mode,$1.value); set_sz($$.mode,1); set_b1($$.mode,$3.value); } | reg ',' '#' data8 { set_md($$.mode,2); set_ov($$.mode,$1.value); set_sz($$.mode,1); set_b1($$.mode,$4.value); } | data8 ',' reg { set_md($$.mode,3); set_ov($$.mode,$3.value); set_sz($$.mode,1); set_b1($$.mode,$1.value); } | data8 ',' data8 { set_md($$.mode,4); set_ov($$.mode,0); set_sz($$.mode,2); set_b1($$.mode,$3.value); set_b2($$.mode,$1.value); } | data8 ',' '@' regi { set_md($$.mode,5); set_ov($$.mode,$4.value); set_sz($$.mode,1); set_b1($$.mode,$1.value); } | '@' regi ',' A { set_md($$.mode,6); set_ov($$.mode,$2.value); set_sz($$.mode,0); } | '@' regi ',' data8 { set_md($$.mode,7); set_ov($$.mode,$2.value); set_sz($$.mode,1); set_b1($$.mode,$4.value); } | '@' regi ',' '#' data8 { set_md($$.mode,8); set_ov($$.mode,$2.value); set_sz($$.mode,1); set_b1($$.mode,$5.value); } | DPTR ',' '#' data16 { set_md($$.mode,9); set_ov($$.mode,0); set_sz($$.mode,2); set_b1($$.mode, ($4.value & 0xff00) >> 8 ); set_b2($$.mode, ($4.value & 0x00ff) ); } | C ',' bit { set_md($$.mode,10); set_ov($$.mode,0); set_sz($$.mode,1); set_b1($$.mode,$3.value); } /* * Following two productions cannot be represented by: * * bit ',' C * * Because yacc gives tons of reduce/reduce errors if * that is attempted. * */ | data8 ',' C { set_md($$.mode,11); set_ov($$.mode,0); set_sz($$.mode,1); set_b1($$.mode,$1.value); } | data8 BITPOS ',' C { if( pass2 ) { if( !isbit8($1.value) ) warn("Bit address exceeds 8-bits"); if( isbmram($1.value) ) set_b1($$.mode, ($1.value-0x20)*8+ $2.value ); else if( isbmsfr($1.value) ) set_b1($$.mode, $1.value + $2.value ); else warn("Invalid bit addressable RAM location"); } set_md($$.mode,11); set_ov($$.mode,0); set_sz($$.mode,1); } ; single_op1 : A { set_md($$.mode,0); set_ov($$.mode,0); set_sz($$.mode,0); } | reg { set_md($$.mode,1); set_ov($$.mode,$1.value); set_sz($$.mode,0); } | data8 { set_md($$.mode,2); set_ov($$.mode,0); set_sz($$.mode,1); set_b1($$.mode,$1.value); } | '@' regi { set_md($$.mode,3); set_ov($$.mode,$2.value); set_sz($$.mode,0); } ; single_op2 : A { set_md($$.mode,0); set_ov($$.mode,0); set_sz($$.mode,0); } | C { set_md($$.mode,1); set_ov($$.mode,0); set_sz($$.mode,0); } | bit { set_md($$.mode,2); set_ov($$.mode,0); set_sz($$.mode,1); set_b1($$.mode,$1.value); } ; three_op1 : A ',' data8 ',' rel { set_md($$.mode,0); set_ov($$.mode,0); set_sz($$.mode,2); set_b1($$.mode,$3.value); set_b2($$.mode,$5.value); } | A ',' '#' data8 ',' rel { set_md($$.mode,1); set_ov($$.mode,0); set_sz($$.mode,2); set_b1($$.mode,$4.value); set_b2($$.mode,$6.value); } | reg ',' '#' data8 ',' rel { set_md($$.mode,2); set_ov($$.mode,$1.value); set_sz($$.mode,2); set_b1($$.mode,$4.value); set_b2($$.mode,$6.value); } | '@' regi ',' '#' data8 ',' rel { set_md($$.mode,3); set_ov($$.mode,$2.value); set_sz($$.mode,2); set_b1($$.mode,$5.value); set_b2($$.mode,$7.value); } ; rel : expr { long offset; if( pass2 ) { offset = $1.val.v - (lc+3); if( offset > 127 || offset < -128 ) warn("Relative offset exceeds -128 / +127"); $$.value = offset; } } ; /* * This production differs from the above, by 1 number! * */ rel2 : expr { long offset; if( pass2 ) { offset = $1.val.v - (lc+2); /* different! */ if( offset > 127 || offset < -128 ) warn("Relative offset exceeds -128 / +127"); $$.value = offset; } } ; bit : bitv BITPOS { if( pass2 ) { if( !isbit8($1.value) ) warn("Bit address exceeds 8-bits"); if( isbmram($1.value) ) $$.value = ($1.value-0x20)*8+$2.value; else if( isbmsfr($1.value) ) $$.value = $1.value + $2.value; else warn("Invalid bit addressable RAM location"); } } | bitv { if( pass2 ) { if( !isbit8($1.value) ) warn("Bit address exceeds 8-bits"); $$.value = $1.value; } } ; bitv : SYMBOL { if( $1.sym->type == UNDEF && pass2 ) warn("Symbol %s undefined",$1.sym->name); $$.value = $1.sym->value; } | VALUE { $$.value = $1.value; } ; reg : R0 { $$.value = 0; } | R1 { $$.value = 1; } | R2 { $$.value = 2; } | R3 { $$.value = 3; } | R4 { $$.value = 4; } | R5 { $$.value = 5; } | R6 { $$.value = 6; } | R7 { $$.value = 7; } ; regi : R0 { $$.value = 0; } | R1 { $$.value = 1; } | R2 { $$.value = 0; warn("Illegal indirect register: @r2"); } | R3 { $$.value = 0; warn("Illegal indirect register: @r3"); } | R4 { $$.value = 0; warn("Illegal indirect register: @r4"); } | R5 { $$.value = 0; warn("Illegal indirect register: @r5"); } | R6 { $$.value = 0; warn("Illegal indirect register: @r6"); } | R7 { $$.value = 0; warn("Illegal indirect register: @r7"); } ; data8 : expr { if( pass2 ) { if( !isbit8($1.val.v) ) warn("Expression greater than 8-bits"); } $$.value = $1.val.v; } ; data16 : expr { if( pass2 ) { if( !isbit16($1.val.v) ) warn("Expression greater than 16-bits"); } $$.value = $1.val.v; } ; addr11 : expr { if( pass2 ) { if( !isbit16($1.val.v) ) warn("Address greater than 16-bits"); if( ($1.val.v & size11) != ((lc+2) & size11) ) warn("Address outside current 2K page"); } set_md($$.mode,0); set_ov($$.mode, ($1.val.v&0x0700)>>3 ); set_sz($$.mode,1); set_b1($$.mode,$1.val.v&0x00ff); } ; addr16 : expr { if( pass2 ) { if( !isbit16($1.val.v) ) warn("Address greater than 16-bits"); } set_md($$.mode,0); set_ov($$.mode, 0 ); set_sz($$.mode,2); set_b1($$.mode, ($1.val.v & 0xff00 ) >> 8 ); set_b2($$.mode, ($1.val.v & 0x00ff ) ); } ; relative : expr { long offset=0; if( pass2 ) { offset = $1.val.v - (lc+2); if( offset>127 || offset<-128 ) warn("Relative offset exceeds -128 / +127"); } set_md($$.mode,0); set_ov($$.mode,0); set_sz($$.mode,1); set_b1($$.mode,offset); } ; %% /* ---------------------------------------------------------------------- */ void yyerror(const char *s) { warn("%s near \"%s\"", s, get_last_token()); } /* ---------------------------------------------------------------------- * makeop: * This function makes an opcode based on the instruction symbol table * entry, and an addressing mode structure. * This function is called from both passes, but * only generates code in pass 2. * * Resultant opcode bytes are passed to genbyte(). * * Returns the nuumber of bytes that the instruction * occupies. * */ int makeop(struct opcode * op, struct mode *m, int add) { register unsigned int newop; if( m == NULL ) { if(pass2) genbyte(op->bytes[0+add]); return(1); } if( pass2 ) { newop = op->bytes[ get_md(*m)+add ] | get_ov(*m); genbyte(newop); if( get_sz(*m) > 0 ) genbyte( get_b1(*m) ); if( get_sz(*m) > 1 ) genbyte( get_b2(*m) ); } return( get_sz(*m)+1 ); } /* ---------------------------------------------------------------------- * inclc: * Increments the Location Counter by 'i' amount. * Check to see if 'i' overflows 64K. * Checks to see if assembler is overlapping previous sections * of code. (using a large bit field). * */ #define indx(a) ( (a)/(32) ) #define bit(a) ( 1 << ((a)%(32)) ) #define getloc(a) (regions[indx(a)] & bit(a)) #define setloc(a) (regions[indx(a)] |= bit(a)) static unsigned long regions[ 0x10000/(32) ]; void inclc(int i) { while (i-- > 0) { if( pass2 && getloc(lc) ) error("Location counter overlaps"); if( pass2 ) setloc(lc); lc += 1; } if( lc > 0xffff ) error("Location counter has exceeded 16-bits"); } void clear_location_counter(void) { memset(regions, 0, sizeof(regions)); } /* ---------------------------------------------------------------------- * padline: * This routine returns a new string, which is equivilant to * 'line' except that all tabs have been expanded to spaces, and * the total length has been truncated to 60 chars. */ char *padline(char *line) { static char newline[61]; char *p1; int pos=0,nxtpos; for(p1=line; pos= 4 ) { j = 0; fprintf(listing,"\n "); } } while(++j <= 4) fprintf(listing," "); fprintf(listing," %s\n",padline(txt)); } /* ---------------------------------------------------------------------- * gen* routines: * Place information into the bytebuf[] array, and also * call emitbyte with the byte. * */ void genbyte(int b) { if( bytecount < sizeof(bytebuf) ) bytebuf[bytecount++] = b; emitbyte(b); } void genstr(const char *s) { while( *s ) genbyte(*s++); } void genword(unsigned long w) { genbyte( (w & 0xff00) >> 8 ); genbyte( (w & 0x00ff) ); }