ref: 0d80f60fcc14ad5efbf0234d2c27dde877623100
dir: /src/asm/asmy.y/
%{ #include <ctype.h> #include <errno.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <strings.h> #include "asm/symbol.h" #include "asm/asm.h" #include "asm/charmap.h" #include "asm/output.h" #include "asm/mylink.h" #include "asm/fstack.h" #include "asm/mymath.h" #include "asm/rpn.h" #include "asm/main.h" #include "asm/lexer.h" char *tzNewMacro; ULONG ulNewMacroSize; void bankrangecheck(char *name, ULONG secttype, SLONG org, SLONG bank) { SLONG minbank, maxbank; char *stype = NULL; switch (secttype) { case SECT_ROMX: stype = "ROMX"; minbank = 1; maxbank = 0x1ff; break; case SECT_SRAM: stype = "SRAM"; minbank = 0; maxbank = 0x1ff; break; case SECT_WRAMX: stype = "WRAMX"; minbank = 1; maxbank = 7; break; case SECT_VRAM: stype = "VRAM"; minbank = 0; maxbank = 1; break; default: yyerror("BANK only allowed for " "ROMX, WRAMX, SRAM, or VRAM sections"); } if (stype && (bank < minbank || bank > maxbank)) { yyerror("%s bank value $%x out of range ($%x to $%x)", stype, bank, minbank, maxbank); } if (secttype == SECT_WRAMX) { bank -= minbank; } out_NewAbsSection(name, secttype, org, bank); } size_t symvaluetostring(char *dest, size_t maxLength, char *sym) { size_t length; if (sym_isString(sym)) { char *src = sym_GetStringValue(sym); size_t i; for (i = 0; src[i] != 0; i++) { if (i >= maxLength) { fatalerror("Symbol value too long to fit buffer"); } dest[i] = src[i]; } length = i; } else { ULONG value = sym_GetConstantValue(sym); int fullLength = snprintf(dest, maxLength + 1, "$%lX", value); if (fullLength < 0) { fatalerror("snprintf encoding error"); } else { length = (size_t)fullLength; if (length > maxLength) { fatalerror("Symbol value too long to fit buffer"); } } } return length; } ULONG str2int( char *s ) { ULONG r=0; while( *s ) { r<<=8; r|=(UBYTE)(*s++); } return( r ); } ULONG str2int2( char *s, int length ) { int i; ULONG r=0; i = (length - 4 < 0 ? 0 : length - 4); while(i < length) { r<<=8; r|=(UBYTE)(s[i]); i++; } return( r ); } ULONG isWhiteSpace( char s ) { return( s==' ' || s=='\t' || s=='\0' || s=='\n' ); } ULONG isRept( char *s ) { return( (strncasecmp(s,"REPT",4)==0) && isWhiteSpace(*(s-1)) && isWhiteSpace(s[4]) ); } ULONG isEndr( char *s ) { return( (strncasecmp(s,"Endr",4)==0) && isWhiteSpace(*(s-1)) && isWhiteSpace(s[4]) ); } void copyrept( void ) { SLONG level=1, len, instring=0; char *src=pCurrentBuffer->pBuffer; char *bufferEnd = pCurrentBuffer->pBufferStart + pCurrentBuffer->nBufferSize; while( src < bufferEnd && level ) { if( instring==0 ) { if( isRept(src) ) { level+=1; src+=4; } else if( isEndr(src) ) { level-=1; src+=4; } else { if( *src=='\"' ) instring=1; src+=1; } } else { if( *src=='\\' ) { src+=2; } else if( *src=='\"' ) { src+=1; instring=0; } else { src+=1; } } } if (level != 0) { fatalerror("Unterminated REPT block"); } len=src-pCurrentBuffer->pBuffer-4; src=pCurrentBuffer->pBuffer; ulNewMacroSize=len; if ((tzNewMacro = malloc(ulNewMacroSize + 1)) != NULL) { ULONG i; tzNewMacro[ulNewMacroSize]=0; for( i=0; i<ulNewMacroSize; i+=1 ) { if( (tzNewMacro[i]=src[i])=='\n' ) nLineNo+=1; } } else fatalerror( "No mem for REPT block" ); yyskipbytes( ulNewMacroSize+4 ); } ULONG isMacro( char *s ) { return( (strncasecmp(s,"MACRO",4)==0) && isWhiteSpace(*(s-1)) && isWhiteSpace(s[5]) ); } ULONG isEndm( char *s ) { return( (strncasecmp(s,"Endm",4)==0) && isWhiteSpace(*(s-1)) && isWhiteSpace(s[4]) ); } void copymacro( void ) { SLONG level=1, len, instring=0; char *src=pCurrentBuffer->pBuffer; char *bufferEnd = pCurrentBuffer->pBufferStart + pCurrentBuffer->nBufferSize; while( src < bufferEnd && level ) { if( instring==0 ) { if( isMacro(src) ) { level+=1; src+=4; } else if( isEndm(src) ) { level-=1; src+=4; } else { if( *src=='\"' ) instring=1; src+=1; } } else { if( *src=='\\' ) { src+=2; } else if( *src=='\"' ) { src+=1; instring=0; } else { src+=1; } } } if (level != 0) { fatalerror("Unterminated MACRO definition"); } len=src-pCurrentBuffer->pBuffer-4; src=pCurrentBuffer->pBuffer; ulNewMacroSize=len; if( (tzNewMacro=(char *)malloc(ulNewMacroSize+2))!=NULL ) { ULONG i; tzNewMacro[ulNewMacroSize]='\n'; tzNewMacro[ulNewMacroSize+1]=0; for( i=0; i<ulNewMacroSize; i+=1 ) { if( (tzNewMacro[i]=src[i])=='\n' ) nLineNo+=1; } } else fatalerror( "No mem for MACRO definition" ); yyskipbytes( ulNewMacroSize+4 ); } ULONG isIf( char *s ) { return( (strncasecmp(s,"If",2)==0) && isWhiteSpace(*(s-1)) && isWhiteSpace(s[2]) ); } ULONG isElse( char *s ) { return( (strncasecmp(s,"Else",4)==0) && isWhiteSpace(*(s-1)) && isWhiteSpace(s[4]) ); } ULONG isEndc( char *s ) { return( (strncasecmp(s,"Endc",4)==0) && isWhiteSpace(*(s-1)) && isWhiteSpace(s[4]) ); } void if_skip_to_else( void ) { SLONG level=1, len, instring=0; char *src=pCurrentBuffer->pBuffer; while( *src && level ) { if( *src=='\n' ) nLineNo+=1; if( instring==0 ) { if( isIf(src) ) { level+=1; src+=2; } else if( level==1 && isElse(src) ) { level-=1; src+=4; } else if( isEndc(src) ) { level-=1; if( level!=0 ) src+=4; } else { if( *src=='\"' ) instring=1; src+=1; } } else { if( *src=='\\' ) { src+=2; } else if( *src=='\"' ) { src+=1; instring=0; } else { src+=1; } } } if (level != 0) { fatalerror("Unterminated IF construct"); } len=src-pCurrentBuffer->pBuffer; yyskipbytes( len ); yyunput( '\n' ); nLineNo-=1; } void if_skip_to_endc( void ) { SLONG level=1, len, instring=0; char *src=pCurrentBuffer->pBuffer; while( *src && level ) { if( *src=='\n' ) nLineNo+=1; if( instring==0 ) { if( isIf(src) ) { level+=1; src+=2; } else if( isEndc(src) ) { level-=1; if( level!=0 ) src+=4; } else { if( *src=='\"' ) instring=1; src+=1; } } else { if( *src=='\\' ) { src+=2; } else if( *src=='\"' ) { src+=1; instring=0; } else { src+=1; } } } if (level != 0) { fatalerror("Unterminated IF construct"); } len=src-pCurrentBuffer->pBuffer; yyskipbytes( len ); yyunput( '\n' ); nLineNo-=1; } %} %union { char tzSym[MAXSYMLEN + 1]; char tzString[MAXSTRLEN + 1]; struct Expression sVal; SLONG nConstValue; } %type <sVal> relocconst %type <nConstValue> const %type <nConstValue> const_3bit %type <sVal> const_8bit %type <sVal> const_16bit %type <sVal> const_PCrel %type <nConstValue> sectiontype %type <tzString> string %token <nConstValue> T_NUMBER %token <tzString> T_STRING %left T_OP_LOGICNOT %left T_OP_LOGICOR T_OP_LOGICAND T_OP_LOGICEQU %left T_OP_LOGICGT T_OP_LOGICLT T_OP_LOGICGE T_OP_LOGICLE T_OP_LOGICNE %left T_OP_ADD T_OP_SUB %left T_OP_OR T_OP_XOR T_OP_AND %left T_OP_SHL T_OP_SHR %left T_OP_MUL T_OP_DIV T_OP_MOD %left T_OP_NOT %left T_OP_DEF %left T_OP_BANK T_OP_ALIGN %left T_OP_SIN %left T_OP_COS %left T_OP_TAN %left T_OP_ASIN %left T_OP_ACOS %left T_OP_ATAN %left T_OP_ATAN2 %left T_OP_FDIV %left T_OP_FMUL %left T_OP_ROUND %left T_OP_CEIL %left T_OP_FLOOR %token T_OP_HIGH T_OP_LOW %left T_OP_STRCMP %left T_OP_STRIN %left T_OP_STRSUB %left T_OP_STRLEN %left T_OP_STRCAT %left T_OP_STRUPR %left T_OP_STRLWR %left NEG /* negation--unary minus */ %token <tzSym> T_LABEL %token <tzSym> T_ID %token <tzSym> T_POP_EQU %token <tzSym> T_POP_SET %token <tzSym> T_POP_EQUS %token T_POP_INCLUDE T_POP_PRINTF T_POP_PRINTT T_POP_PRINTV T_POP_IF T_POP_ELSE T_POP_ENDC %token T_POP_IMPORT T_POP_EXPORT T_POP_GLOBAL %token T_POP_DB T_POP_DS T_POP_DW T_POP_DL %token T_POP_SECTION %token T_POP_RB %token T_POP_RW %token T_POP_RL %token T_POP_MACRO %token T_POP_ENDM %token T_POP_RSRESET T_POP_RSSET %token T_POP_INCBIN T_POP_REPT %token T_POP_CHARMAP %token T_POP_SHIFT %token T_POP_ENDR %token T_POP_FAIL %token T_POP_WARN %token T_POP_PURGE %token T_POP_POPS %token T_POP_PUSHS %token T_POP_POPO %token T_POP_PUSHO %token T_POP_OPT %token T_SECT_WRAM0 T_SECT_VRAM T_SECT_ROMX T_SECT_ROM0 T_SECT_HRAM T_SECT_WRAMX T_SECT_SRAM T_SECT_OAM %token T_SECT_HOME T_SECT_DATA T_SECT_CODE T_SECT_BSS %token T_Z80_ADC T_Z80_ADD T_Z80_AND %token T_Z80_BIT %token T_Z80_CALL T_Z80_CCF T_Z80_CP T_Z80_CPL %token T_Z80_DAA T_Z80_DEC T_Z80_DI %token T_Z80_EI %token T_Z80_HALT %token T_Z80_INC %token T_Z80_JP T_Z80_JR %token T_Z80_LD %token T_Z80_LDI %token T_Z80_LDD %token T_Z80_LDIO %token T_Z80_NOP %token T_Z80_OR %token T_Z80_POP T_Z80_PUSH %token T_Z80_RES T_Z80_RET T_Z80_RETI T_Z80_RST %token T_Z80_RL T_Z80_RLA T_Z80_RLC T_Z80_RLCA %token T_Z80_RR T_Z80_RRA T_Z80_RRC T_Z80_RRCA %token T_Z80_SBC T_Z80_SCF T_Z80_STOP %token T_Z80_SLA T_Z80_SRA T_Z80_SRL T_Z80_SUB T_Z80_SWAP %token T_Z80_XOR %token T_TOKEN_A T_TOKEN_B T_TOKEN_C T_TOKEN_D T_TOKEN_E T_TOKEN_H T_TOKEN_L %token T_MODE_AF %token T_MODE_BC T_MODE_BC_IND %token T_MODE_DE T_MODE_DE_IND %token T_MODE_SP T_MODE_SP_IND %token T_MODE_C_IND %token T_MODE_HL T_MODE_HL_IND T_MODE_HL_INDDEC T_MODE_HL_INDINC %token T_CC_NZ T_CC_Z T_CC_NC %type <nConstValue> reg_r %type <nConstValue> reg_ss %type <nConstValue> reg_rr %type <nConstValue> reg_tt %type <nConstValue> ccode %type <sVal> op_a_n %type <nConstValue> op_a_r %type <nConstValue> op_hl_ss %type <sVal> op_mem_ind %start asmfile %% asmfile : lines; /* Note: The lexer add '\n' at the end of the input */ lines : /* empty */ | lines line '\n' { nLineNo += 1; nTotalLines += 1; }; line : label | label cpu_command | label macro | label simple_pseudoop | pseudoop; label : /* empty */ | T_LABEL { if ($1[0] == '.') sym_AddLocalReloc($1); else sym_AddReloc($1); } | T_LABEL ':' { if ($1[0] == '.') sym_AddLocalReloc($1); else sym_AddReloc($1); } | T_LABEL ':' ':' { sym_AddReloc($1); sym_Export($1); }; macro : T_ID { yy_set_state(LEX_STATE_MACROARGS); } macroargs { yy_set_state(LEX_STATE_NORMAL); if (!fstk_RunMacro($1)) { fatalerror("Macro '%s' not defined", $1); } }; macroargs : /* empty */ | macroarg | macroarg ',' macroargs; macroarg : T_STRING { sym_AddNewMacroArg($1); }; pseudoop : equ | set | rb | rw | rl | equs | macrodef; simple_pseudoop : include | printf | printt | printv | if | else | endc | import | export | global | db | dw | dl | ds | section | rsreset | rsset | incbin | charmap | rept | shift | fail | warn | purge | pops | pushs | popo | pusho | opt; opt : T_POP_OPT { yy_set_state(LEX_STATE_MACROARGS); } opt_list { yy_set_state(LEX_STATE_NORMAL); }; opt_list : opt_list_entry | opt_list_entry ',' opt_list; opt_list_entry : T_STRING { opt_Parse($1); }; popo : T_POP_POPO { opt_Pop(); }; pusho : T_POP_PUSHO { opt_Push(); }; pops : T_POP_POPS { out_PopSection(); }; pushs : T_POP_PUSHS { out_PushSection(); }; fail : T_POP_FAIL string { fatalerror("%s", $2); }; warn : T_POP_WARN string { warning("%s", $2); }; shift : T_POP_SHIFT { sym_ShiftCurrentMacroArgs(); } ; rept : T_POP_REPT const { copyrept(); fstk_RunRept( $2 ); } ; macrodef : T_LABEL ':' T_POP_MACRO { copymacro(); sym_AddMacro($1); } ; equs : T_LABEL T_POP_EQUS string { sym_AddString( $1, $3 ); } ; rsset : T_POP_RSSET const { sym_AddSet( "_RS", $2 ); } ; rsreset : T_POP_RSRESET { sym_AddSet( "_RS", 0 ); } ; rl : T_LABEL T_POP_RL const { sym_AddEqu( $1, sym_GetConstantValue("_RS") ); sym_AddSet( "_RS", sym_GetConstantValue("_RS")+4*$3 ); } ; rw : T_LABEL T_POP_RW const { sym_AddEqu( $1, sym_GetConstantValue("_RS") ); sym_AddSet( "_RS", sym_GetConstantValue("_RS")+2*$3 ); } ; rb : T_LABEL T_POP_RB const { sym_AddEqu( $1, sym_GetConstantValue("_RS") ); sym_AddSet( "_RS", sym_GetConstantValue("_RS")+$3 ); } ; ds : T_POP_DS const { out_Skip( $2 ); } ; db : T_POP_DB constlist_8bit ; dw : T_POP_DW constlist_16bit ; dl : T_POP_DL constlist_32bit ; purge : T_POP_PURGE { oDontExpandStrings = true; } purge_list { oDontExpandStrings = false; } ; purge_list : purge_list_entry | purge_list_entry ',' purge_list ; purge_list_entry : T_ID { sym_Purge($1); } ; import : T_POP_IMPORT import_list ; import_list : import_list_entry | import_list_entry ',' import_list ; import_list_entry : T_ID { /* This is done automatically if * the label isn't found in the * list of defined symbols. */ if( nPass==1 ) warning("IMPORT is a deprecated keyword with no effect: %s", $1); } ; export : T_POP_EXPORT export_list ; export_list : export_list_entry | export_list_entry ',' export_list ; export_list_entry : T_ID { sym_Export($1); } ; global : T_POP_GLOBAL global_list ; global_list : global_list_entry | global_list_entry ',' global_list ; global_list_entry : T_ID { sym_Global($1); } ; equ : T_LABEL T_POP_EQU const { sym_AddEqu( $1, $3 ); } ; set : T_LABEL T_POP_SET const { sym_AddSet( $1, $3 ); } ; include : T_POP_INCLUDE string { fstk_RunInclude($2); } ; incbin : T_POP_INCBIN string { out_BinaryFile( $2 ); } | T_POP_INCBIN string ',' const ',' const { out_BinaryFileSlice( $2, $4, $6 ); } ; charmap : T_POP_CHARMAP string ',' string { if(charmap_Add($2, $4[0] & 0xFF) == -1) { fprintf(stderr, "Error parsing charmap. Either you've added too many (%i), or the input character length is too long (%i)' : %s\n", MAXCHARMAPS, CHARMAPLENGTH, strerror(errno)); yyerror("Error parsing charmap."); } } | T_POP_CHARMAP string ',' const { if(charmap_Add($2, $4 & 0xFF) == -1) { fprintf(stderr, "Error parsing charmap. Either you've added too many (%i), or the input character length is too long (%i)' : %s\n", MAXCHARMAPS, CHARMAPLENGTH, strerror(errno)); yyerror("Error parsing charmap."); } } ; printt : T_POP_PRINTT string { if( nPass==1 ) printf( "%s", $2 ); } ; printv : T_POP_PRINTV const { if( nPass==1 ) printf( "$%lX", $2 ); } ; printf : T_POP_PRINTF const { if( nPass==1 ) math_Print( $2 ); } ; if : T_POP_IF const { nIFDepth+=1; if( !$2 ) { if_skip_to_else(); /* will continue parsing just after ELSE or just at ENDC keyword */ } } else : T_POP_ELSE { if_skip_to_endc(); /* will continue parsing just at ENDC keyword */ } ; endc : T_POP_ENDC { nIFDepth-=1; } ; const_3bit : const { if( ($1<0) || ($1>7) ) { yyerror("Immediate value must be 3-bit"); } else $$=$1&0x7; } ; constlist_8bit : constlist_8bit_entry | constlist_8bit_entry ',' constlist_8bit ; constlist_8bit_entry : { out_Skip( 1 ); } | const_8bit { out_RelByte( &$1 ); } | string { char *s; int length; s = $1; length = charmap_Convert(&s); out_AbsByteGroup(s, length); free(s); } ; constlist_16bit : constlist_16bit_entry | constlist_16bit_entry ',' constlist_16bit ; constlist_16bit_entry : { out_Skip( 2 ); } | const_16bit { out_RelWord( &$1 ); } ; constlist_32bit : constlist_32bit_entry | constlist_32bit_entry ',' constlist_32bit ; constlist_32bit_entry : { out_Skip( 4 ); } | relocconst { out_RelLong( &$1 ); } ; const_PCrel : relocconst { $$ = $1; if( !rpn_isPCRelative(&$1) ) yyerror("Expression must be PC-relative"); } ; const_8bit : relocconst { if( (!rpn_isReloc(&$1)) && (($1.nVal<-128) || ($1.nVal>255)) ) { yyerror("Expression must be 8-bit"); } $$=$1; } ; const_16bit : relocconst { if( (!rpn_isReloc(&$1)) && (($1.nVal<-32768) || ($1.nVal>65535)) ) { yyerror("Expression must be 16-bit"); } $$=$1; } ; relocconst : T_ID { rpn_Symbol(&$$,$1); $$.nVal = sym_GetValue($1); } | T_NUMBER { rpn_Number(&$$,$1); $$.nVal = $1; } | string { char *s; int length; ULONG r; s = $1; length = charmap_Convert(&s); r = str2int2(s, length); free(s); rpn_Number(&$$,r); $$.nVal=r; } | T_OP_LOGICNOT relocconst %prec NEG { rpn_LOGNOT(&$$,&$2); } | relocconst T_OP_LOGICOR relocconst { rpn_LOGOR(&$$,&$1,&$3); } | relocconst T_OP_LOGICAND relocconst { rpn_LOGAND(&$$,&$1,&$3); } | relocconst T_OP_LOGICEQU relocconst { rpn_LOGEQU(&$$,&$1,&$3); } | relocconst T_OP_LOGICGT relocconst { rpn_LOGGT(&$$,&$1,&$3); } | relocconst T_OP_LOGICLT relocconst { rpn_LOGLT(&$$,&$1,&$3); } | relocconst T_OP_LOGICGE relocconst { rpn_LOGGE(&$$,&$1,&$3); } | relocconst T_OP_LOGICLE relocconst { rpn_LOGLE(&$$,&$1,&$3); } | relocconst T_OP_LOGICNE relocconst { rpn_LOGNE(&$$,&$1,&$3); } | relocconst T_OP_ADD relocconst { rpn_ADD(&$$,&$1,&$3); } | relocconst T_OP_SUB relocconst { rpn_SUB(&$$,&$1,&$3); } | relocconst T_OP_XOR relocconst { rpn_XOR(&$$,&$1,&$3); } | relocconst T_OP_OR relocconst { rpn_OR(&$$,&$1,&$3); } | relocconst T_OP_AND relocconst { rpn_AND(&$$,&$1,&$3); } | relocconst T_OP_SHL relocconst { rpn_SHL(&$$,&$1,&$3); } | relocconst T_OP_SHR relocconst { rpn_SHR(&$$,&$1,&$3); } | relocconst T_OP_MUL relocconst { rpn_MUL(&$$,&$1,&$3); } | relocconst T_OP_DIV relocconst { rpn_DIV(&$$,&$1,&$3); } | relocconst T_OP_MOD relocconst { rpn_MOD(&$$,&$1,&$3); } | T_OP_ADD relocconst %prec NEG { $$ = $2; } | T_OP_SUB relocconst %prec NEG { rpn_UNNEG(&$$,&$2); } | T_OP_NOT relocconst %prec NEG { rpn_UNNOT(&$$,&$2); } | T_OP_HIGH '(' relocconst ')' { rpn_HIGH(&$$, &$3); } | T_OP_LOW '(' relocconst ')' { rpn_LOW(&$$, &$3); } | T_OP_BANK '(' T_ID ')' { rpn_Bank(&$$,$3); $$.nVal = 0; } | T_OP_DEF { oDontExpandStrings = true; } '(' T_ID ')' { rpn_Number(&$$,sym_isConstDefined($4)); oDontExpandStrings = false; } | T_OP_ROUND '(' const ')' { rpn_Number(&$$,math_Round($3)); } | T_OP_CEIL '(' const ')' { rpn_Number(&$$,math_Ceil($3)); } | T_OP_FLOOR '(' const ')' { rpn_Number(&$$,math_Floor($3)); } | T_OP_FDIV '(' const ',' const ')' { rpn_Number(&$$,math_Div($3,$5)); } | T_OP_FMUL '(' const ',' const ')' { rpn_Number(&$$,math_Mul($3,$5)); } | T_OP_SIN '(' const ')' { rpn_Number(&$$,math_Sin($3)); } | T_OP_COS '(' const ')' { rpn_Number(&$$,math_Cos($3)); } | T_OP_TAN '(' const ')' { rpn_Number(&$$,math_Tan($3)); } | T_OP_ASIN '(' const ')' { rpn_Number(&$$,math_ASin($3)); } | T_OP_ACOS '(' const ')' { rpn_Number(&$$,math_ACos($3)); } | T_OP_ATAN '(' const ')' { rpn_Number(&$$,math_ATan($3)); } | T_OP_ATAN2 '(' const ',' const ')' { rpn_Number(&$$,math_ATan2($3,$5)); } | T_OP_STRCMP '(' string ',' string ')' { rpn_Number(&$$,strcmp($3,$5)); } | T_OP_STRIN '(' string ',' string ')' { char *p; if( (p=strstr($3,$5))!=NULL ) { rpn_Number(&$$,p-$3+1); } else { rpn_Number(&$$,0); } } | T_OP_STRLEN '(' string ')' { rpn_Number(&$$,strlen($3)); } | '(' relocconst ')' { $$ = $2; } ; const : T_ID { $$ = sym_GetConstantValue($1); } | T_NUMBER { $$ = $1; } | string { $$ = str2int($1); } | T_OP_LOGICNOT const %prec NEG { $$ = !$2; } | const T_OP_LOGICOR const { $$ = $1 || $3; } | const T_OP_LOGICAND const { $$ = $1 && $3; } | const T_OP_LOGICEQU const { $$ = $1 == $3; } | const T_OP_LOGICGT const { $$ = $1 > $3; } | const T_OP_LOGICLT const { $$ = $1 < $3; } | const T_OP_LOGICGE const { $$ = $1 >= $3; } | const T_OP_LOGICLE const { $$ = $1 <= $3; } | const T_OP_LOGICNE const { $$ = $1 != $3; } | const T_OP_ADD const { $$ = $1 + $3; } | const T_OP_SUB const { $$ = $1 - $3; } | T_ID T_OP_SUB T_ID { if (sym_IsRelocDiffDefined($1, $3) == 0) fatalerror("'%s - %s' not defined.", $1, $3); $$ = sym_GetDefinedValue($1) - sym_GetDefinedValue($3); } | const T_OP_XOR const { $$ = $1 ^ $3; } | const T_OP_OR const { $$ = $1 | $3; } | const T_OP_AND const { $$ = $1 & $3; } | const T_OP_SHL const { $$ = $1 << $3; } | const T_OP_SHR const { $$ = $1 >> $3; } | const T_OP_MUL const { $$ = $1 * $3; } | const T_OP_DIV const { if ($3 == 0) fatalerror("division by zero"); $$ = $1 / $3; } | const T_OP_MOD const { if ($3 == 0) fatalerror("division by zero"); $$ = $1 % $3; } | T_OP_ADD const %prec NEG { $$ = +$2; } | T_OP_SUB const %prec NEG { $$ = -$2; } | T_OP_NOT const %prec NEG { $$ = 0xFFFFFFFF^$2; } | T_OP_ROUND '(' const ')' { $$ = math_Round($3); } | T_OP_CEIL '(' const ')' { $$ = math_Ceil($3); } | T_OP_FLOOR '(' const ')' { $$ = math_Floor($3); } | T_OP_FDIV '(' const ',' const ')' { $$ = math_Div($3,$5); } | T_OP_FMUL '(' const ',' const ')' { $$ = math_Mul($3,$5); } | T_OP_SIN '(' const ')' { $$ = math_Sin($3); } | T_OP_COS '(' const ')' { $$ = math_Cos($3); } | T_OP_TAN '(' const ')' { $$ = math_Tan($3); } | T_OP_ASIN '(' const ')' { $$ = math_ASin($3); } | T_OP_ACOS '(' const ')' { $$ = math_ACos($3); } | T_OP_ATAN '(' const ')' { $$ = math_ATan($3); } | T_OP_ATAN2 '(' const ',' const ')' { $$ = math_ATan2($3,$5); } | T_OP_DEF { oDontExpandStrings = true; } '(' T_ID ')' { $$ = sym_isConstDefined($4); oDontExpandStrings = false; } | T_OP_STRCMP '(' string ',' string ')' { $$ = strcmp( $3, $5 ); } | T_OP_STRIN '(' string ',' string ')' { char *p; if( (p=strstr($3,$5))!=NULL ) { $$ = p-$3+1; } else { $$ = 0; } } | T_OP_STRLEN '(' string ')' { $$ = strlen($3); } | '(' const ')' { $$ = $2; } ; string : T_STRING { strcpy($$,$1); } | T_OP_STRSUB '(' string ',' const ',' const ')' { strncpy($$,$3+$5-1,$7); $$[$7]=0; } | T_OP_STRCAT '(' string ',' string ')' { strcpy($$,$3); strcat($$,$5); } | T_OP_STRUPR '(' string ')' { strcpy($$,$3); upperstring($$); } | T_OP_STRLWR '(' string ')' { strcpy($$,$3); lowerstring($$); } ; section: T_POP_SECTION string ',' sectiontype { out_NewSection($2,$4); } | T_POP_SECTION string ',' sectiontype '[' const ']' { if( $6>=0 && $6<0x10000 ) out_NewAbsSection($2,$4,$6,-1); else yyerror("Address $%x not 16-bit", $6); } | T_POP_SECTION string ',' sectiontype ',' T_OP_ALIGN '[' const ']' { out_NewAlignedSection($2, $4, $8, -1); } | T_POP_SECTION string ',' sectiontype ',' T_OP_BANK '[' const ']' { bankrangecheck($2, $4, -1, $8); } | T_POP_SECTION string ',' sectiontype '[' const ']' ',' T_OP_BANK '[' const ']' { if ($6 < 0 || $6 > 0x10000) { yyerror("Address $%x not 16-bit", $6); } bankrangecheck($2, $4, $6, $11); } | T_POP_SECTION string ',' sectiontype ',' T_OP_ALIGN '[' const ']' ',' T_OP_BANK '[' const ']' { out_NewAlignedSection($2, $4, $8, $13); } | T_POP_SECTION string ',' sectiontype ',' T_OP_BANK '[' const ']' ',' T_OP_ALIGN '[' const ']' { out_NewAlignedSection($2, $4, $13, $8); } ; sectiontype: T_SECT_WRAM0 { $$=SECT_WRAM0; } | T_SECT_VRAM { $$=SECT_VRAM; } | T_SECT_ROMX { $$=SECT_ROMX; } | T_SECT_ROM0 { $$=SECT_ROM0; } | T_SECT_HRAM { $$=SECT_HRAM; } | T_SECT_WRAMX { $$=SECT_WRAMX; } | T_SECT_SRAM { $$=SECT_SRAM; } | T_SECT_OAM { $$=SECT_OAM; } | T_SECT_HOME { warning("HOME section name is deprecated, use ROM0 instead."); $$=SECT_ROM0; } | T_SECT_DATA { warning("DATA section name is deprecated, use ROMX instead."); $$=SECT_ROMX; } | T_SECT_CODE { warning("CODE section name is deprecated, use ROMX instead."); $$=SECT_ROMX; } | T_SECT_BSS { warning("BSS section name is deprecated, use WRAM0 instead."); $$=SECT_WRAM0; } ; cpu_command : z80_adc | z80_add | z80_and | z80_bit | z80_call | z80_ccf | z80_cp | z80_cpl | z80_daa | z80_dec | z80_di | z80_ei | z80_halt | z80_inc | z80_jp | z80_jr | z80_ld | z80_ldd | z80_ldi | z80_ldio | z80_nop | z80_or | z80_pop | z80_push | z80_res | z80_ret | z80_reti | z80_rl | z80_rla | z80_rlc | z80_rlca | z80_rr | z80_rra | z80_rrc | z80_rrca | z80_rst | z80_sbc | z80_scf | z80_set | z80_sla | z80_sra | z80_srl | z80_stop | z80_sub | z80_swap | z80_xor ; z80_adc : T_Z80_ADC op_a_n { out_AbsByte(0xCE); out_RelByte(&$2); } | T_Z80_ADC op_a_r { out_AbsByte(0x88|$2); } ; z80_add : T_Z80_ADD op_a_n { out_AbsByte(0xC6); out_RelByte(&$2); } | T_Z80_ADD op_a_r { out_AbsByte(0x80|$2); } | T_Z80_ADD op_hl_ss { out_AbsByte(0x09|($2<<4)); } | T_Z80_ADD T_MODE_SP comma const_8bit { out_AbsByte(0xE8); out_RelByte(&$4); } ; z80_and : T_Z80_AND op_a_n { out_AbsByte(0xE6); out_RelByte(&$2); } | T_Z80_AND op_a_r { out_AbsByte(0xA0|$2); } ; z80_bit : T_Z80_BIT const_3bit comma reg_r { out_AbsByte(0xCB); out_AbsByte(0x40|($2<<3)|$4); } ; z80_call : T_Z80_CALL const_16bit { out_AbsByte(0xCD); out_RelWord(&$2); } | T_Z80_CALL ccode comma const_16bit { out_AbsByte(0xC4|($2<<3)); out_RelWord(&$4); } ; z80_ccf : T_Z80_CCF { out_AbsByte(0x3F); } ; z80_cp : T_Z80_CP op_a_n { out_AbsByte(0xFE); out_RelByte(&$2); } | T_Z80_CP op_a_r { out_AbsByte(0xB8|$2); } ; z80_cpl : T_Z80_CPL { out_AbsByte(0x2F); } ; z80_daa : T_Z80_DAA { out_AbsByte(0x27); } ; z80_dec : T_Z80_DEC reg_r { out_AbsByte(0x05|($2<<3)); } | T_Z80_DEC reg_ss { out_AbsByte(0x0B|($2<<4)); } ; z80_di : T_Z80_DI { out_AbsByte(0xF3); } ; z80_ei : T_Z80_EI { out_AbsByte(0xFB); } ; z80_halt: T_Z80_HALT { out_AbsByte(0x76); if (CurrentOptions.haltnop) { out_AbsByte(0x00); } } ; z80_inc : T_Z80_INC reg_r { out_AbsByte(0x04|($2<<3)); } | T_Z80_INC reg_ss { out_AbsByte(0x03|($2<<4)); } ; z80_jp : T_Z80_JP const_16bit { out_AbsByte(0xC3); out_RelWord(&$2); } | T_Z80_JP ccode comma const_16bit { out_AbsByte(0xC2|($2<<3)); out_RelWord(&$4); } | T_Z80_JP T_MODE_HL_IND { out_AbsByte(0xE9); if( nPass==1 ) warning("'JP [HL]' is obsolete, use 'JP HL' instead."); } | T_Z80_JP T_MODE_HL { out_AbsByte(0xE9); } ; z80_jr : T_Z80_JR const_PCrel { out_AbsByte(0x18); out_PCRelByte(&$2); } | T_Z80_JR ccode comma const_PCrel { out_AbsByte(0x20|($2<<3)); out_PCRelByte(&$4); } ; z80_ldi : T_Z80_LDI T_MODE_HL_IND comma T_MODE_A { out_AbsByte(0x02|(2<<4)); } | T_Z80_LDI T_MODE_A comma T_MODE_HL { out_AbsByte(0x0A|(2<<4)); if( nPass==1 ) warning("'LDI A,HL' is obsolete, use 'LDI A,[HL]' or 'LD A,[HL+] instead."); } | T_Z80_LDI T_MODE_A comma T_MODE_HL_IND { out_AbsByte(0x0A|(2<<4)); } ; z80_ldd : T_Z80_LDD T_MODE_HL_IND comma T_MODE_A { out_AbsByte(0x02|(3<<4)); } | T_Z80_LDD T_MODE_A comma T_MODE_HL { out_AbsByte(0x0A|(3<<4)); if( nPass==1 ) warning("'LDD A,HL' is obsolete, use 'LDD A,[HL]' or 'LD A,[HL-] instead."); } | T_Z80_LDD T_MODE_A comma T_MODE_HL_IND { out_AbsByte(0x0A|(3<<4)); } ; z80_ldio : T_Z80_LDIO T_MODE_A comma op_mem_ind { rpn_CheckHRAM(&$4,&$4); if( (!rpn_isReloc(&$4)) && ($4.nVal<0 || ($4.nVal>0xFF && $4.nVal<0xFF00) || $4.nVal>0xFFFF) ) { yyerror("Source address $%x not in HRAM ($FF00 to $FFFE)", $4.nVal); } out_AbsByte(0xF0); $4.nVal&=0xFF; out_RelByte(&$4); } | T_Z80_LDIO op_mem_ind comma T_MODE_A { rpn_CheckHRAM(&$2,&$2); if( (!rpn_isReloc(&$2)) && ($2.nVal<0 || ($2.nVal>0xFF && $2.nVal<0xFF00) || $2.nVal>0xFFFF) ) { yyerror("Destination address $%x not in HRAM ($FF00 to $FFFE)", $2.nVal); } out_AbsByte(0xE0); $2.nVal&=0xFF; out_RelByte(&$2); } ; z80_ld : z80_ld_mem | z80_ld_cind | z80_ld_rr | z80_ld_ss | z80_ld_hl | z80_ld_sp | z80_ld_r | z80_ld_a ; z80_ld_hl : T_Z80_LD T_MODE_HL comma '[' T_MODE_SP const_8bit ']' { out_AbsByte(0xF8); out_RelByte(&$6); } | T_Z80_LD T_MODE_HL comma T_MODE_SP const_8bit { out_AbsByte(0xF8); out_RelByte(&$5); } | T_Z80_LD T_MODE_HL comma const_16bit { out_AbsByte(0x01|(REG_HL<<4)); out_RelWord(&$4); } ; z80_ld_sp : T_Z80_LD T_MODE_SP comma T_MODE_HL { out_AbsByte(0xF9); } | T_Z80_LD T_MODE_SP comma const_16bit { out_AbsByte(0x01|(REG_SP<<4)); out_RelWord(&$4); } ; z80_ld_mem : T_Z80_LD op_mem_ind comma T_MODE_SP { out_AbsByte(0x08); out_RelWord(&$2); } | T_Z80_LD op_mem_ind comma T_MODE_A { if( (!rpn_isReloc(&$2)) && $2.nVal>=0xFF00) { out_AbsByte(0xE0); out_AbsByte($2.nVal&0xFF); } else { out_AbsByte(0xEA); out_RelWord(&$2); } } ; z80_ld_cind : T_Z80_LD T_MODE_C_IND comma T_MODE_A { out_AbsByte(0xE2); } ; z80_ld_rr : T_Z80_LD reg_rr comma T_MODE_A { out_AbsByte(0x02|($2<<4)); } ; z80_ld_r : T_Z80_LD reg_r comma const_8bit { out_AbsByte(0x06|($2<<3)); out_RelByte(&$4); } | T_Z80_LD reg_r comma reg_r { if( ($2==REG_HL_IND) && ($4==REG_HL_IND) ) { yyerror("LD [HL],[HL] not a valid instruction"); } else out_AbsByte(0x40|($2<<3)|$4); } ; z80_ld_a : T_Z80_LD reg_r comma T_MODE_C_IND { if( $2==REG_A ) out_AbsByte(0xF2); else { yyerror("Destination operand must be A"); } } | T_Z80_LD reg_r comma reg_rr { if( $2==REG_A ) out_AbsByte(0x0A|($4<<4)); else { yyerror("Destination operand must be A"); } } | T_Z80_LD reg_r comma op_mem_ind { if( $2==REG_A ) { if( (!rpn_isReloc(&$4)) && $4.nVal>=0xFF00 ) { out_AbsByte(0xF0); out_AbsByte($4.nVal&0xFF); } else { out_AbsByte(0xFA); out_RelWord(&$4); } } else { yyerror("Destination operand must be A"); } } ; z80_ld_ss : T_Z80_LD reg_ss comma const_16bit { out_AbsByte(0x01|($2<<4)); out_RelWord(&$4); } ; z80_nop : T_Z80_NOP { out_AbsByte(0x00); } ; z80_or : T_Z80_OR op_a_n { out_AbsByte(0xF6); out_RelByte(&$2); } | T_Z80_OR op_a_r { out_AbsByte(0xB0|$2); } ; z80_pop : T_Z80_POP reg_tt { out_AbsByte(0xC1|($2<<4)); } ; z80_push : T_Z80_PUSH reg_tt { out_AbsByte(0xC5|($2<<4)); } ; z80_res : T_Z80_RES const_3bit comma reg_r { out_AbsByte(0xCB); out_AbsByte(0x80|($2<<3)|$4); } ; z80_ret : T_Z80_RET { out_AbsByte(0xC9); } | T_Z80_RET ccode { out_AbsByte(0xC0|($2<<3)); } ; z80_reti : T_Z80_RETI { out_AbsByte(0xD9); } ; z80_rl : T_Z80_RL reg_r { out_AbsByte(0xCB); out_AbsByte(0x10|$2); } ; z80_rla : T_Z80_RLA { out_AbsByte(0x17); } ; z80_rlc : T_Z80_RLC reg_r { out_AbsByte(0xCB); out_AbsByte(0x00|$2); } ; z80_rlca : T_Z80_RLCA { out_AbsByte(0x07); } ; z80_rr : T_Z80_RR reg_r { out_AbsByte(0xCB); out_AbsByte(0x18|$2); } ; z80_rra : T_Z80_RRA { out_AbsByte(0x1F); } ; z80_rrc : T_Z80_RRC reg_r { out_AbsByte(0xCB); out_AbsByte(0x08|$2); } ; z80_rrca : T_Z80_RRCA { out_AbsByte(0x0F); } ; z80_rst : T_Z80_RST const_8bit { if( rpn_isReloc(&$2) ) { yyerror("Address for RST must be absolute"); } else if( ($2.nVal&0x38)!=$2.nVal ) { yyerror("Invalid address $%x for RST", $2.nVal); } else out_AbsByte(0xC7|$2.nVal); } ; z80_sbc : T_Z80_SBC op_a_n { out_AbsByte(0xDE); out_RelByte(&$2); } | T_Z80_SBC op_a_r { out_AbsByte(0x98|$2); } ; z80_scf : T_Z80_SCF { out_AbsByte(0x37); } ; z80_set : T_POP_SET const_3bit comma reg_r { out_AbsByte(0xCB); out_AbsByte(0xC0|($2<<3)|$4); } ; z80_sla : T_Z80_SLA reg_r { out_AbsByte(0xCB); out_AbsByte(0x20|$2); } ; z80_sra : T_Z80_SRA reg_r { out_AbsByte(0xCB); out_AbsByte(0x28|$2); } ; z80_srl : T_Z80_SRL reg_r { out_AbsByte(0xCB); out_AbsByte(0x38|$2); } ; z80_stop : T_Z80_STOP { out_AbsByte(0x10); out_AbsByte(0x00); } ; z80_sub : T_Z80_SUB op_a_n { out_AbsByte(0xD6); out_RelByte(&$2); } | T_Z80_SUB op_a_r { out_AbsByte(0x90|$2); } ; z80_swap : T_Z80_SWAP reg_r { out_AbsByte(0xCB); out_AbsByte(0x30|$2); } ; z80_xor : T_Z80_XOR op_a_n { out_AbsByte(0xEE); out_RelByte(&$2); } | T_Z80_XOR op_a_r { out_AbsByte(0xA8|$2); } ; op_mem_ind : '[' const_16bit ']' { $$ = $2; } ; op_hl_ss : reg_ss { $$ = $1; } | T_MODE_HL comma reg_ss { $$ = $3; } ; op_a_r : reg_r { $$ = $1; } | T_MODE_A comma reg_r { $$ = $3; } ; op_a_n : const_8bit { $$ = $1; } | T_MODE_A comma const_8bit { $$ = $3; } ; comma : ',' ; T_MODE_A : T_TOKEN_A | T_OP_HIGH '(' T_MODE_AF ')' ; T_MODE_B : T_TOKEN_B | T_OP_HIGH '(' T_MODE_BC ')' ; T_MODE_C : T_TOKEN_C | T_OP_LOW '(' T_MODE_BC ')' ; T_MODE_D : T_TOKEN_D | T_OP_HIGH '(' T_MODE_DE ')' ; T_MODE_E : T_TOKEN_E | T_OP_LOW '(' T_MODE_DE ')' ; T_MODE_H : T_TOKEN_H | T_OP_HIGH '(' T_MODE_HL ')' ; T_MODE_L : T_TOKEN_L | T_OP_LOW '(' T_MODE_HL ')' ; ccode : T_CC_NZ { $$ = CC_NZ; } | T_CC_Z { $$ = CC_Z; } | T_CC_NC { $$ = CC_NC; } | T_TOKEN_C { $$ = CC_C; } ; reg_r : T_MODE_B { $$ = REG_B; } | T_MODE_C { $$ = REG_C; } | T_MODE_D { $$ = REG_D; } | T_MODE_E { $$ = REG_E; } | T_MODE_H { $$ = REG_H; } | T_MODE_L { $$ = REG_L; } | T_MODE_HL_IND { $$ = REG_HL_IND; } | T_MODE_A { $$ = REG_A; } ; reg_tt : T_MODE_BC { $$ = REG_BC; } | T_MODE_DE { $$ = REG_DE; } | T_MODE_HL { $$ = REG_HL; } | T_MODE_AF { $$ = REG_AF; } ; reg_ss : T_MODE_BC { $$ = REG_BC; } | T_MODE_DE { $$ = REG_DE; } | T_MODE_HL { $$ = REG_HL; } | T_MODE_SP { $$ = REG_SP; } ; reg_rr : T_MODE_BC_IND { $$ = REG_BC_IND; } | T_MODE_DE_IND { $$ = REG_DE_IND; } | T_MODE_HL_INDINC { $$ = REG_HL_INDINC; } | T_MODE_HL_INDDEC { $$ = REG_HL_INDDEC; } ; %%