/* * This file is part of RGBDS. * * Copyright (c) 1997-2019, Carsten Sorensen and RGBDS contributors. * * SPDX-License-Identifier: MIT */ %{ #include #include #include #include #include #include #include #include #include "asm/charmap.h" #include "asm/fixpoint.h" #include "asm/format.h" #include "asm/fstack.h" #include "asm/lexer.h" #include "asm/macro.h" #include "asm/main.h" #include "asm/opt.h" #include "asm/output.h" #include "asm/rpn.h" #include "asm/section.h" #include "asm/symbol.h" #include "asm/util.h" #include "asm/warning.h" #include "extern/utf8decoder.h" #include "linkdefs.h" #include "platform.h" // strncasecmp, strdup static struct CaptureBody captureBody; /* Captures a REPT/FOR or MACRO */ static void upperstring(char *dest, char const *src) { while (*src) *dest++ = toupper(*src++); *dest = '\0'; } static void lowerstring(char *dest, char const *src) { while (*src) *dest++ = tolower(*src++); *dest = '\0'; } static uint32_t str2int2(uint8_t *s, int32_t length) { int32_t i; uint32_t r = 0; i = length < 4 ? 0 : length - 4; while (i < length) { r <<= 8; r |= s[i]; i++; } return r; } static char *strrstr(char *s1, char *s2) { size_t len1 = strlen(s1); size_t len2 = strlen(s2); if (len2 > len1) return NULL; for (char *p = s1 + len1 - len2; p >= s1; p--) if (!strncmp(p, s2, len2)) return p; return NULL; } static size_t strlenUTF8(const char *s) { size_t len = 0; uint32_t state = 0; uint32_t codep = 0; while (*s) { switch (decode(&state, &codep, *s)) { case 1: fatalerror("STRLEN: Invalid UTF-8 character\n"); break; case 0: len++; break; } s++; } /* Check for partial code point. */ if (state != 0) fatalerror("STRLEN: Invalid UTF-8 character\n"); return len; } static void strsubUTF8(char *dest, size_t destLen, const char *src, uint32_t pos, uint32_t len) { size_t srcIndex = 0; size_t destIndex = 0; uint32_t state = 0; uint32_t codep = 0; uint32_t curPos = 1; uint32_t curLen = 0; if (pos < 1) { warning(WARNING_BUILTIN_ARG, "STRSUB: Position starts at 1\n"); pos = 1; } /* Advance to starting position in source string. */ while (src[srcIndex] && curPos < pos) { switch (decode(&state, &codep, src[srcIndex])) { case 1: fatalerror("STRSUB: Invalid UTF-8 character\n"); break; case 0: curPos++; break; } srcIndex++; } if (!src[srcIndex] && len) warning(WARNING_BUILTIN_ARG, "STRSUB: Position %lu is past the end of the string\n", (unsigned long)pos); /* Copy from source to destination. */ while (src[srcIndex] && destIndex < destLen - 1 && curLen < len) { switch (decode(&state, &codep, src[srcIndex])) { case 1: fatalerror("STRSUB: Invalid UTF-8 character\n"); break; case 0: curLen++; break; } dest[destIndex++] = src[srcIndex++]; } if (curLen < len) warning(WARNING_BUILTIN_ARG, "STRSUB: Length too big: %lu\n", (unsigned long)len); /* Check for partial code point. */ if (state != 0) fatalerror("STRSUB: Invalid UTF-8 character\n"); dest[destIndex] = '\0'; } static void strrpl(char *dest, size_t destLen, char const *src, char const *old, char const *new) { size_t oldLen = strlen(old); size_t newLen = strlen(new); size_t i = 0; if (!oldLen) { warning(WARNING_EMPTY_STRRPL, "STRRPL: Cannot replace an empty string\n"); strcpy(dest, src); return; } for (char const *next = strstr(src, old); next && *next; next = strstr(src, old)) { // Copy anything before the substring to replace unsigned int lenBefore = next - src; memcpy(dest + i, src, lenBefore < destLen - i ? lenBefore : destLen - i); i += next - src; if (i >= destLen) break; // Copy the replacement substring memcpy(dest + i, new, newLen < destLen - i ? newLen : destLen - i); i += newLen; if (i >= destLen) break; src = next + oldLen; } if (i < destLen) { size_t srcLen = strlen(src); // Copy anything after the last replaced substring memcpy(dest + i, src, srcLen < destLen - i ? srcLen : destLen - i); i += srcLen; } if (i >= destLen) { warning(WARNING_LONG_STR, "STRRPL: String too long, got truncated\n"); i = destLen - 1; } dest[i] = '\0'; } static void initStrFmtArgList(struct StrFmtArgList *args) { args->nbArgs = 0; args->capacity = INITIAL_STRFMT_ARG_SIZE; args->args = malloc(args->capacity * sizeof(*args->args)); if (!args->args) fatalerror("Failed to allocate memory for STRFMT arg list: %s\n", strerror(errno)); } static size_t nextStrFmtArgListIndex(struct StrFmtArgList *args) { if (args->nbArgs == args->capacity) { args->capacity = (args->capacity + 1) * 2; args->args = realloc(args->args, args->capacity * sizeof(*args->args)); if (!args->args) fatalerror("realloc error while resizing STRFMT arg list: %s\n", strerror(errno)); } return args->nbArgs++; } static void freeStrFmtArgList(struct StrFmtArgList *args) { free(args->format); for (size_t i = 0; i < args->nbArgs; i++) if (!args->args[i].isNumeric) free(args->args[i].string); free(args->args); } static void strfmt(char *dest, size_t destLen, char const *fmt, size_t nbArgs, struct StrFmtArg *args) { size_t a = 0; size_t i = 0; while (i < destLen) { int c = *fmt++; if (c == '\0') { break; } else if (c != '%') { dest[i++] = c; continue; } c = *fmt++; if (c == '%') { dest[i++] = c; continue; } struct FormatSpec spec = fmt_NewSpec(); while (c != '\0') { fmt_UseCharacter(&spec, c); if (fmt_IsFinished(&spec)) break; c = *fmt++; } if (fmt_IsEmpty(&spec)) { error("STRFMT: Illegal '%%' at end of format string\n"); dest[i++] = '%'; break; } else if (!fmt_IsValid(&spec)) { error("STRFMT: Invalid format spec for argument %zu\n", a + 1); dest[i++] = '%'; a++; continue; } else if (a >= nbArgs) { // Will warn after formatting is done. dest[i++] = '%'; a++; continue; } struct StrFmtArg *arg = &args[a++]; static char buf[MAXSTRLEN + 1]; if (arg->isNumeric) fmt_PrintNumber(buf, sizeof(buf), &spec, arg->number); else fmt_PrintString(buf, sizeof(buf), &spec, arg->string); i += snprintf(&dest[i], destLen - i, "%s", buf); } if (a < nbArgs) error("STRFMT: %zu unformatted argument(s)\n", nbArgs - a); else if (a > nbArgs) error("STRFMT: Not enough arguments for format spec, got: %zu, need: %zu\n", nbArgs, a); if (i > destLen - 1) { warning(WARNING_LONG_STR, "STRFMT: String too long, got truncated\n"); i = destLen - 1; } dest[i] = '\0'; } static void initDsArgList(struct DsArgList *args) { args->nbArgs = 0; args->capacity = INITIAL_DS_ARG_SIZE; args->args = malloc(args->capacity * sizeof(*args->args)); if (!args->args) fatalerror("Failed to allocate memory for ds arg list: %s\n", strerror(errno)); } static size_t nextDsArgListIndex(struct DsArgList *args) { if (args->nbArgs == args->capacity) { args->capacity = (args->capacity + 1) * 2; args->args = realloc(args->args, args->capacity * sizeof(*args->args)); if (!args->args) fatalerror("realloc error while resizing ds arg list: %s\n", strerror(errno)); } return args->nbArgs++; } static void freeDsArgList(struct DsArgList *args) { free(args->args); } static inline void failAssert(enum AssertionType type) { switch (type) { case ASSERT_FATAL: fatalerror("Assertion failed\n"); case ASSERT_ERROR: error("Assertion failed\n"); break; case ASSERT_WARN: warning(WARNING_ASSERT, "Assertion failed\n"); break; } } static inline void failAssertMsg(enum AssertionType type, char const *msg) { switch (type) { case ASSERT_FATAL: fatalerror("Assertion failed: %s\n", msg); case ASSERT_ERROR: error("Assertion failed: %s\n", msg); break; case ASSERT_WARN: warning(WARNING_ASSERT, "Assertion failed: %s\n", msg); break; } } void yyerror(char const *str) { error("%s\n", str); } // The CPU encodes instructions in a logical way, so most instructions actually follow patterns. // These enums thus help with bit twiddling to compute opcodes enum { REG_B = 0, REG_C, REG_D, REG_E, REG_H, REG_L, REG_HL_IND, REG_A }; enum { REG_BC_IND = 0, REG_DE_IND, REG_HL_INDINC, REG_HL_INDDEC, }; enum { REG_BC = 0, REG_DE = 1, REG_HL = 2, REG_SP = 3, REG_AF = 3 }; enum { CC_NZ = 0, CC_Z, CC_NC, CC_C }; %} %union { char tzSym[MAXSYMLEN + 1]; char tzString[MAXSTRLEN + 1]; struct Expression sVal; int32_t nConstValue; enum SectionModifier sectMod; struct SectionSpec sectSpec; struct MacroArgs *macroArg; enum AssertionType assertType; struct DsArgList dsArgs; struct { int32_t start; int32_t stop; int32_t step; } forArgs; struct StrFmtArgList strfmtArgs; } %type relocexpr %type relocexpr_no_str %type const %type const_no_str %type uconst %type rs_uconst %type slice_const %type const_1bit %type const_3bit %type reloc_8bit %type reloc_8bit_no_str %type reloc_16bit %type reloc_16bit_no_str %type sectiontype %type string %type strcat_args %type strfmt_args %type strfmt_va_args %type sectorg %type sectattrs %token T_NUMBER "number" %token T_STRING "string" %token T_PERIOD "." %token T_ELLIPSIS "..." %token T_COMMA "," %token T_COLON ":" %token T_LBRACK "[" T_RBRACK "]" %token T_LPAREN "(" T_RPAREN ")" %token T_NEWLINE "newline" %token T_PRIME "'" %token T_QUESTION "?" %token T_OP_LOGICNOT "!" %token T_OP_LOGICAND "&&" T_OP_LOGICOR "||" %token T_OP_LOGICGT ">" T_OP_LOGICLT "<" %token T_OP_LOGICGE ">=" T_OP_LOGICLE "<=" %token T_OP_LOGICNE "!=" T_OP_LOGICEQU "==" %token T_OP_ADD "+" T_OP_SUB "-" %token T_OP_OR "|" T_OP_XOR "^" T_OP_AND "&" %token T_OP_SHL "<<" T_OP_SHR ">>" T_OP_SHRL ">>>" %token T_OP_MUL "*" T_OP_DIV "/" T_OP_MOD "%" %token T_OP_NOT "~" %left T_OP_LOGICOR %left T_OP_LOGICAND %left T_OP_LOGICGT T_OP_LOGICLT T_OP_LOGICGE T_OP_LOGICLE T_OP_LOGICNE T_OP_LOGICEQU %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 %precedence NEG /* negation -- unary minus */ %token T_OP_EXP "**" %left T_OP_EXP %token T_OP_DEF "DEF" %token T_OP_BANK "BANK" %token T_OP_ALIGN "ALIGN" %token T_OP_SIN "SIN" T_OP_COS "COS" T_OP_TAN "TAN" %token T_OP_ASIN "ASIN" T_OP_ACOS "ACOS" T_OP_ATAN "ATAN" T_OP_ATAN2 "ATAN2" %token T_OP_FDIV "FDIV" %token T_OP_FMUL "FMUL" %token T_OP_POW "POW" %token T_OP_LOG "LOG" %token T_OP_ROUND "ROUND" %token T_OP_CEIL "CEIL" T_OP_FLOOR "FLOOR" %token T_OP_HIGH "HIGH" T_OP_LOW "LOW" %token T_OP_ISCONST "ISCONST" %token T_OP_STRCMP "STRCMP" %token T_OP_STRIN "STRIN" T_OP_STRRIN "STRRIN" %token T_OP_STRSUB "STRSUB" %token T_OP_STRLEN "STRLEN" %token T_OP_STRCAT "STRCAT" %token T_OP_STRUPR "STRUPR" T_OP_STRLWR "STRLWR" %token T_OP_STRRPL "STRRPL" %token T_OP_STRFMT "STRFMT" %token T_LABEL "label" %token T_ID "identifier" %token T_LOCAL_ID "local identifier" %token T_ANON "anonymous label" %token T_TOKEN_AT "@" %type def_id %type redef_id %type scoped_id %type scoped_anon_id %token T_POP_EQU "EQU" %token T_POP_SET "SET" %token T_POP_EQUAL "=" %token T_POP_EQUS "EQUS" %token T_POP_INCLUDE "INCLUDE" %token T_POP_PRINT "PRINT" T_POP_PRINTLN "PRINTLN" %token T_POP_PRINTF "PRINTF" T_POP_PRINTT "PRINTT" T_POP_PRINTV "PRINTV" T_POP_PRINTI "PRINTI" %token T_POP_IF "IF" T_POP_ELIF "ELIF" T_POP_ELSE "ELSE" T_POP_ENDC "ENDC" %token T_POP_EXPORT "EXPORT" %token T_POP_DB "DB" T_POP_DS "DS" T_POP_DW "DW" T_POP_DL "DL" %token T_POP_SECTION "SECTION" T_POP_FRAGMENT "FRAGMENT" %token T_POP_RB "RB" T_POP_RW "RW" // There is no T_POP_RL, only T_Z80_RL %token T_POP_MACRO "MACRO" %token T_POP_ENDM "ENDM" %token T_POP_RSRESET "RSRESET" T_POP_RSSET "RSSET" %token T_POP_UNION "UNION" T_POP_NEXTU "NEXTU" T_POP_ENDU "ENDU" %token T_POP_INCBIN "INCBIN" T_POP_REPT "REPT" T_POP_FOR "FOR" %token T_POP_CHARMAP "CHARMAP" %token T_POP_NEWCHARMAP "NEWCHARMAP" %token T_POP_SETCHARMAP "SETCHARMAP" %token T_POP_PUSHC "PUSHC" %token T_POP_POPC "POPC" %token T_POP_SHIFT "SHIFT" %token T_POP_ENDR "ENDR" %token T_POP_BREAK "BREAK" %token T_POP_LOAD "LOAD" T_POP_ENDL "ENDL" %token T_POP_FAIL "FAIL" %token T_POP_WARN "WARN" %token T_POP_FATAL "FATAL" %token T_POP_ASSERT "ASSERT" T_POP_STATIC_ASSERT "STATIC_ASSERT" %token T_POP_PURGE "PURGE" %token T_POP_REDEF "REDEF" %token T_POP_POPS "POPS" %token T_POP_PUSHS "PUSHS" %token T_POP_POPO "POPO" %token T_POP_PUSHO "PUSHO" %token T_POP_OPT "OPT" %token T_SECT_ROM0 "ROM0" T_SECT_ROMX "ROMX" %token T_SECT_WRAM0 "WRAM0" T_SECT_WRAMX "WRAMX" T_SECT_HRAM "HRAM" %token T_SECT_VRAM "VRAM" T_SECT_SRAM "SRAM" T_SECT_OAM "OAM" %type sectmod %type macroargs %type ds_args %type for_args %token T_Z80_ADC "adc" T_Z80_ADD "add" T_Z80_AND "and" %token T_Z80_BIT "bit" // There is no T_Z80_SET, only T_POP_SET %token T_Z80_CALL "call" T_Z80_CCF "ccf" T_Z80_CP "cp" T_Z80_CPL "cpl" %token T_Z80_DAA "daa" T_Z80_DEC "dec" T_Z80_DI "di" %token T_Z80_EI "ei" %token T_Z80_HALT "halt" %token T_Z80_INC "inc" %token T_Z80_JP "jp" T_Z80_JR "jr" %token T_Z80_LD "ld" %token T_Z80_LDI "ldi" %token T_Z80_LDD "ldd" %token T_Z80_LDH "ldh" %token T_Z80_NOP "nop" %token T_Z80_OR "or" %token T_Z80_POP "pop" T_Z80_PUSH "push" %token T_Z80_RES "res" T_Z80_RET "ret" T_Z80_RETI "reti" T_Z80_RST "rst" %token T_Z80_RL "rl" T_Z80_RLA "rla" T_Z80_RLC "rlc" T_Z80_RLCA "rlca" %token T_Z80_RR "rr" T_Z80_RRA "rra" T_Z80_RRC "rrc" T_Z80_RRCA "rrca" %token T_Z80_SBC "sbc" T_Z80_SCF "scf" T_Z80_STOP "stop" %token T_Z80_SLA "sla" T_Z80_SRA "sra" T_Z80_SRL "srl" T_Z80_SUB "sub" %token T_Z80_SWAP "swap" %token T_Z80_XOR "xor" %token T_TOKEN_A "a" T_TOKEN_F "f" %token T_TOKEN_B "b" T_TOKEN_C "c" %token T_TOKEN_D "d" T_TOKEN_E "e" %token T_TOKEN_H "h" T_TOKEN_L "l" %token T_TOKEN_W "w" %token T_MODE_AF "af" T_MODE_BC "bc" T_MODE_DE "de" T_MODE_SP "sp" T_MODE_PC "pc" %token T_MODE_IME "ime" %token T_MODE_HL "hl" T_MODE_HL_DEC "hld/hl-" T_MODE_HL_INC "hli/hl+" %token T_CC_NZ "nz" T_CC_Z "z" T_CC_NC "nc" // There is no T_CC_C, only T_TOKEN_C %type reg_nac %type reg_na %type reg_nc %type reg_r %type reg_ss %type reg_rr %type reg_tt %type ccode %type op_mem_ind %type assert_type %token T_EOF 0 "end of file" %start asmfile %% asmfile : lines ; /* * The lexer adds T_NEWLINE at the end of the file if one was not * already present, so we can rely on it to end a line. */ lines : %empty | lines line ; plain_directive : label | label cpu_command | label macro | label directive | assignment_directive ; line : plain_directive T_NEWLINE | line_directive /* Directives that manage newlines themselves */ | error T_NEWLINE { /* Continue parsing the next line on a syntax error */ fstk_StopRept(); } ; /* * For "logistical" reasons, these directives must manage newlines themselves. * This is because we need to switch the lexer's mode *after* the newline has been read, * and to avoid causing some grammar conflicts (token reducing is finicky). * This is DEFINITELY one of the more FRAGILE parts of the codebase, handle with care. */ line_directive : macrodef | rept | for | break | if /* It's important that all of these require being at line start for `skipIfBlock` */ | elif | else ; if : T_POP_IF const T_NEWLINE { lexer_IncIFDepth(); if ($2) lexer_RunIFBlock(); else lexer_SetMode(LEXER_SKIP_TO_ELIF); } ; elif : T_POP_ELIF const T_NEWLINE { if (lexer_GetIFDepth() == 0) fatalerror("Found ELIF outside an IF construct\n"); if (lexer_RanIFBlock()) { if (lexer_ReachedELSEBlock()) fatalerror("Found ELIF after an ELSE block\n"); lexer_SetMode(LEXER_SKIP_TO_ENDC); } else if ($2) { lexer_RunIFBlock(); } else { lexer_SetMode(LEXER_SKIP_TO_ELIF); } } ; else : T_POP_ELSE T_NEWLINE { if (lexer_GetIFDepth() == 0) fatalerror("Found ELSE outside an IF construct\n"); if (lexer_RanIFBlock()) { if (lexer_ReachedELSEBlock()) fatalerror("Found ELSE after an ELSE block\n"); lexer_SetMode(LEXER_SKIP_TO_ENDC); } else { lexer_RunIFBlock(); lexer_ReachELSEBlock(); } } ; endc : T_POP_ENDC { lexer_DecIFDepth(); } ; def_id : T_OP_DEF { lexer_ToggleStringExpansion(false); } T_ID { lexer_ToggleStringExpansion(true); strcpy($$, $3); } ; redef_id : T_POP_REDEF { lexer_ToggleStringExpansion(false); } T_ID { lexer_ToggleStringExpansion(true); strcpy($$, $3); } ; scoped_id : T_ID | T_LOCAL_ID; scoped_anon_id : scoped_id | T_ANON | T_TOKEN_AT; label : %empty | T_COLON { sym_AddAnonLabel(); } | T_LOCAL_ID { sym_AddLocalLabel($1); } | T_LOCAL_ID T_COLON { sym_AddLocalLabel($1); } | T_LABEL T_COLON { sym_AddLabel($1); } | T_LOCAL_ID T_COLON T_COLON { sym_AddLocalLabel($1); sym_Export($1); } | T_LABEL T_COLON T_COLON { sym_AddLabel($1); sym_Export($1); } ; macro : T_ID { // Parsing 'macroargs' will restore the lexer's normal mode lexer_SetMode(LEXER_RAW); } macroargs { fstk_RunMacro($1, $3); } ; macroargs : %empty { $$ = macro_NewArgs(); } | macroargs T_STRING { macro_AppendArg(&($$), strdup($2)); } ; /* These commands start with a T_LABEL. */ assignment_directive : equ | set | rb | rw | rl | equs ; directive : include | endc | print | println | printf | printt | printv | printi | export | db | dw | dl | ds | section | rsreset | rsset | union | nextu | endu | incbin | charmap | newcharmap | setcharmap | pushc | popc | load | shift | fail | warn | assert | def_equ | def_set | def_rb | def_rw | def_rl | def_equs | redef_equs | purge | pops | pushs | popo | pusho | opt | align ; trailing_comma : %empty | T_COMMA ; optional_ellipsis : %empty | T_ELLIPSIS ; equ : T_LABEL T_POP_EQU const { sym_AddEqu($1, $3); } ; set_or_equal : T_POP_SET | T_POP_EQUAL ; set : T_LABEL set_or_equal const { sym_AddSet($1, $3); } ; equs : T_LABEL T_POP_EQUS string { sym_AddString($1, $3); } ; rb : T_LABEL T_POP_RB rs_uconst { sym_AddEqu($1, sym_GetConstantValue("_RS")); sym_AddSet("_RS", sym_GetConstantValue("_RS") + $3); } ; rw : T_LABEL T_POP_RW rs_uconst { sym_AddEqu($1, sym_GetConstantValue("_RS")); sym_AddSet("_RS", sym_GetConstantValue("_RS") + 2 * $3); } ; rl : T_LABEL T_Z80_RL rs_uconst { sym_AddEqu($1, sym_GetConstantValue("_RS")); sym_AddSet("_RS", sym_GetConstantValue("_RS") + 4 * $3); } ; align : T_OP_ALIGN uconst { if ($2 > 16) error("Alignment must be between 0 and 16, not %u\n", $2); else sect_AlignPC($2, 0); } | T_OP_ALIGN uconst T_COMMA uconst { if ($2 > 16) error("Alignment must be between 0 and 16, not %u\n", $2); else if ($4 >= 1 << $2) error("Offset must be between 0 and %u, not %u\n", (1 << $2) - 1, $4); else sect_AlignPC($2, $4); } ; opt : T_POP_OPT { // Parsing 'opt_list' will restore the lexer's normal mode lexer_SetMode(LEXER_RAW); } opt_list ; opt_list : opt_list_entry | opt_list opt_list_entry ; 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\n", $2); } ; warn : T_POP_WARN string { warning(WARNING_USER, "%s\n", $2); } ; assert_type : %empty { $$ = ASSERT_ERROR; } | T_POP_WARN T_COMMA { $$ = ASSERT_WARN; } | T_POP_FAIL T_COMMA { $$ = ASSERT_ERROR; } | T_POP_FATAL T_COMMA { $$ = ASSERT_FATAL; } ; assert : T_POP_ASSERT assert_type relocexpr { if (!rpn_isKnown(&$3)) { if (!out_CreateAssert($2, &$3, "", sect_GetOutputOffset())) error("Assertion creation failed: %s\n", strerror(errno)); } else if ($3.nVal == 0) { failAssert($2); } rpn_Free(&$3); } | T_POP_ASSERT assert_type relocexpr T_COMMA string { if (!rpn_isKnown(&$3)) { if (!out_CreateAssert($2, &$3, $5, sect_GetOutputOffset())) error("Assertion creation failed: %s\n", strerror(errno)); } else if ($3.nVal == 0) { failAssertMsg($2, $5); } rpn_Free(&$3); } | T_POP_STATIC_ASSERT assert_type const { if ($3 == 0) failAssert($2); } | T_POP_STATIC_ASSERT assert_type const T_COMMA string { if ($3 == 0) failAssertMsg($2, $5); } ; shift : T_POP_SHIFT { macro_ShiftCurrentArgs(1); } | T_POP_SHIFT const { macro_ShiftCurrentArgs($2); } ; load : T_POP_LOAD sectmod string T_COMMA sectiontype sectorg sectattrs { out_SetLoadSection($3, $5, $6, &$7, $2); } | T_POP_ENDL { out_EndLoadSection(); } ; rept : T_POP_REPT uconst T_NEWLINE { lexer_CaptureRept(&captureBody); } T_NEWLINE { fstk_RunRept($2, captureBody.lineNo, captureBody.body, captureBody.size); } ; for : T_POP_FOR { lexer_ToggleStringExpansion(false); } T_ID { lexer_ToggleStringExpansion(true); } T_COMMA for_args T_NEWLINE { lexer_CaptureRept(&captureBody); } T_NEWLINE { fstk_RunFor($3, $6.start, $6.stop, $6.step, captureBody.lineNo, captureBody.body, captureBody.size); } for_args : const { $$.start = 0; $$.stop = $1; $$.step = 1; } | const T_COMMA const { $$.start = $1; $$.stop = $3; $$.step = 1; } | const T_COMMA const T_COMMA const { $$.start = $1; $$.stop = $3; $$.step = $5; } ; break : T_POP_BREAK T_NEWLINE { if (fstk_Break()) lexer_SetMode(LEXER_SKIP_TO_ENDR); } ; macrodef : T_POP_MACRO { lexer_ToggleStringExpansion(false); } T_ID { lexer_ToggleStringExpansion(true); } T_NEWLINE { lexer_CaptureMacroBody(&captureBody); } T_NEWLINE { sym_AddMacro($3, captureBody.lineNo, captureBody.body, captureBody.size); } | T_LABEL T_COLON T_POP_MACRO T_NEWLINE { lexer_CaptureMacroBody(&captureBody); } T_NEWLINE { sym_AddMacro($1, captureBody.lineNo, captureBody.body, captureBody.size); } ; rsset : T_POP_RSSET uconst { sym_AddSet("_RS", $2); } ; rsreset : T_POP_RSRESET { sym_AddSet("_RS", 0); } ; rs_uconst : %empty { $$ = 1; } | uconst ; union : T_POP_UNION { sect_StartUnion(); } ; nextu : T_POP_NEXTU { sect_NextUnionMember(); } ; endu : T_POP_ENDU { sect_EndUnion(); } ; ds : T_POP_DS uconst { out_Skip($2, true); } | T_POP_DS uconst T_COMMA ds_args trailing_comma { out_RelBytes($2, $4.args, $4.nbArgs); freeDsArgList(&$4); } ; ds_args : reloc_8bit { initDsArgList(&$$); size_t i = nextDsArgListIndex(&$$); $$.args[i] = $1; } | ds_args T_COMMA reloc_8bit { size_t i = nextDsArgListIndex(&$1); $1.args[i] = $3; $$ = $1; } ; db : T_POP_DB { out_Skip(1, false); } | T_POP_DB constlist_8bit trailing_comma ; dw : T_POP_DW { out_Skip(2, false); } | T_POP_DW constlist_16bit trailing_comma ; dl : T_POP_DL { out_Skip(4, false); } | T_POP_DL constlist_32bit trailing_comma ; def_equ : def_id T_POP_EQU const { sym_AddEqu($1, $3); } ; def_set : def_id set_or_equal const { sym_AddSet($1, $3); } | redef_id set_or_equal const { sym_AddSet($1, $3); } ; def_rb : def_id T_POP_RB rs_uconst { sym_AddEqu($1, sym_GetConstantValue("_RS")); sym_AddSet("_RS", sym_GetConstantValue("_RS") + $3); } ; def_rw : def_id T_POP_RW rs_uconst { sym_AddEqu($1, sym_GetConstantValue("_RS")); sym_AddSet("_RS", sym_GetConstantValue("_RS") + 2 * $3); } ; def_rl : def_id T_Z80_RL rs_uconst { sym_AddEqu($1, sym_GetConstantValue("_RS")); sym_AddSet("_RS", sym_GetConstantValue("_RS") + 4 * $3); } ; def_equs : def_id T_POP_EQUS string { sym_AddString($1, $3); } ; redef_equs : redef_id T_POP_EQUS string { sym_RedefString($1, $3); } ; purge : T_POP_PURGE { lexer_ToggleStringExpansion(false); } purge_list trailing_comma { lexer_ToggleStringExpansion(true); } ; purge_list : purge_list_entry | purge_list T_COMMA purge_list_entry ; purge_list_entry : scoped_id { sym_Purge($1); } ; export : T_POP_EXPORT export_list trailing_comma ; export_list : export_list_entry | export_list T_COMMA export_list_entry ; export_list_entry : scoped_id { sym_Export($1); } ; include : T_POP_INCLUDE string { fstk_RunInclude($2); if (oFailedOnMissingInclude) YYACCEPT; } ; incbin : T_POP_INCBIN string { out_BinaryFile($2, 0); if (oFailedOnMissingInclude) YYACCEPT; } | T_POP_INCBIN string T_COMMA const { out_BinaryFile($2, $4); if (oFailedOnMissingInclude) YYACCEPT; } | T_POP_INCBIN string T_COMMA const T_COMMA const { out_BinaryFileSlice($2, $4, $6); if (oFailedOnMissingInclude) YYACCEPT; } ; charmap : T_POP_CHARMAP string T_COMMA const { if ($4 < INT8_MIN || $4 > UINT8_MAX) warning(WARNING_TRUNCATION, "Expression must be 8-bit\n"); charmap_Add($2, (uint8_t)$4); } ; newcharmap : T_POP_NEWCHARMAP T_ID { charmap_New($2, NULL); } | T_POP_NEWCHARMAP T_ID T_COMMA T_ID { charmap_New($2, $4); } ; setcharmap : T_POP_SETCHARMAP T_ID { charmap_Set($2); } ; pushc : T_POP_PUSHC { charmap_Push(); } ; popc : T_POP_POPC { charmap_Pop(); } ; print : T_POP_PRINT print_exprs trailing_comma ; println : T_POP_PRINTLN { putchar('\n'); } | T_POP_PRINTLN print_exprs trailing_comma { putchar('\n'); } ; print_exprs : print_expr | print_exprs T_COMMA print_expr ; print_expr : const_no_str { printf("$%" PRIX32, $1); } | string { printf("%s", $1); } ; printt : T_POP_PRINTT string { warning(WARNING_OBSOLETE, "`PRINTT` is deprecated; use `PRINT`\n"); printf("%s", $2); } ; printv : T_POP_PRINTV const { warning(WARNING_OBSOLETE, "`PRINTV` is deprecated; use `PRINT`\n"); printf("$%" PRIX32, $2); } ; printi : T_POP_PRINTI const { warning(WARNING_OBSOLETE, "`PRINTI` is deprecated; use `PRINT` with `STRFMT`\n"); printf("%" PRId32, $2); } ; printf : T_POP_PRINTF const { warning(WARNING_OBSOLETE, "`PRINTF` is deprecated; use `PRINT` with `STRFMT`\n"); fix_Print($2); } ; const_1bit : const { int32_t value = $1; if ((value != 0) && (value != 1)) { error("Immediate value must be 1-bit\n"); $$ = 0; } else { $$ = value & 0x1; } } ; const_3bit : const { int32_t value = $1; if ((value < 0) || (value > 7)) { error("Immediate value must be 3-bit\n"); $$ = 0; } else { $$ = value & 0x7; } } ; constlist_8bit : constlist_8bit_entry | constlist_8bit T_COMMA constlist_8bit_entry ; constlist_8bit_entry : reloc_8bit_no_str { out_RelByte(&$1, 0); } | string { uint8_t *output = malloc(strlen($1)); /* Cannot be larger than that */ int32_t length = charmap_Convert($1, output); out_AbsByteGroup(output, length); free(output); } ; constlist_16bit : constlist_16bit_entry | constlist_16bit T_COMMA constlist_16bit_entry ; constlist_16bit_entry : reloc_16bit_no_str { out_RelWord(&$1, 0); } | string { uint8_t *output = malloc(strlen($1)); /* Cannot be larger than that */ int32_t length = charmap_Convert($1, output); out_AbsWordGroup(output, length); free(output); } ; constlist_32bit : constlist_32bit_entry | constlist_32bit T_COMMA constlist_32bit_entry ; constlist_32bit_entry : relocexpr_no_str { out_RelLong(&$1, 0); } | string { uint8_t *output = malloc(strlen($1)); /* Cannot be larger than that */ int32_t length = charmap_Convert($1, output); out_AbsLongGroup(output, length); free(output); } ; reloc_8bit : relocexpr { if(rpn_isKnown(&$1) && ($1.nVal < -128 || $1.nVal > 255)) warning(WARNING_TRUNCATION, "Expression must be 8-bit\n"); $$ = $1; } ; reloc_8bit_no_str : relocexpr_no_str { if(rpn_isKnown(&$1) && ($1.nVal < -128 || $1.nVal > 255)) warning(WARNING_TRUNCATION, "Expression must be 8-bit\n"); $$ = $1; } ; reloc_16bit : relocexpr { if (rpn_isKnown(&$1) && ($1.nVal < -32768 || $1.nVal > 65535)) warning(WARNING_TRUNCATION, "Expression must be 16-bit\n"); $$ = $1; } ; reloc_16bit_no_str : relocexpr_no_str { if (rpn_isKnown(&$1) && ($1.nVal < -32768 || $1.nVal > 65535)) warning(WARNING_TRUNCATION, "Expression must be 16-bit\n"); $$ = $1; } ; relocexpr : relocexpr_no_str | string { uint8_t *output = malloc(strlen($1)); /* Cannot be longer than that */ int32_t length = charmap_Convert($1, output); uint32_t r = str2int2(output, length); free(output); rpn_Number(&$$, r); } ; relocexpr_no_str : scoped_anon_id { rpn_Symbol(&$$, $1); } | T_NUMBER { rpn_Number(&$$, $1); } | T_OP_LOGICNOT relocexpr %prec NEG { rpn_LOGNOT(&$$, &$2); } | relocexpr T_OP_LOGICOR relocexpr { rpn_BinaryOp(RPN_LOGOR, &$$, &$1, &$3); } | relocexpr T_OP_LOGICAND relocexpr { rpn_BinaryOp(RPN_LOGAND, &$$, &$1, &$3); } | relocexpr T_OP_LOGICEQU relocexpr { rpn_BinaryOp(RPN_LOGEQ, &$$, &$1, &$3); } | relocexpr T_OP_LOGICGT relocexpr { rpn_BinaryOp(RPN_LOGGT, &$$, &$1, &$3); } | relocexpr T_OP_LOGICLT relocexpr { rpn_BinaryOp(RPN_LOGLT, &$$, &$1, &$3); } | relocexpr T_OP_LOGICGE relocexpr { rpn_BinaryOp(RPN_LOGGE, &$$, &$1, &$3); } | relocexpr T_OP_LOGICLE relocexpr { rpn_BinaryOp(RPN_LOGLE, &$$, &$1, &$3); } | relocexpr T_OP_LOGICNE relocexpr { rpn_BinaryOp(RPN_LOGNE, &$$, &$1, &$3); } | relocexpr T_OP_ADD relocexpr { rpn_BinaryOp(RPN_ADD, &$$, &$1, &$3); } | relocexpr T_OP_SUB relocexpr { rpn_BinaryOp(RPN_SUB, &$$, &$1, &$3); } | relocexpr T_OP_XOR relocexpr { rpn_BinaryOp(RPN_XOR, &$$, &$1, &$3); } | relocexpr T_OP_OR relocexpr { rpn_BinaryOp(RPN_OR, &$$, &$1, &$3); } | relocexpr T_OP_AND relocexpr { rpn_BinaryOp(RPN_AND, &$$, &$1, &$3); } | relocexpr T_OP_SHL relocexpr { rpn_BinaryOp(RPN_SHL, &$$, &$1, &$3); } | relocexpr T_OP_SHR relocexpr { rpn_BinaryOp(RPN_SHR, &$$, &$1, &$3); } | relocexpr T_OP_MUL relocexpr { rpn_BinaryOp(RPN_MUL, &$$, &$1, &$3); } | relocexpr T_OP_DIV relocexpr { rpn_BinaryOp(RPN_DIV, &$$, &$1, &$3); } | relocexpr T_OP_MOD relocexpr { rpn_BinaryOp(RPN_MOD, &$$, &$1, &$3); } | relocexpr T_OP_EXP relocexpr { rpn_BinaryOp(RPN_EXP, &$$, &$1, &$3); } | T_OP_ADD relocexpr %prec NEG { $$ = $2; } | T_OP_SUB relocexpr %prec NEG { rpn_UNNEG(&$$, &$2); } | T_OP_NOT relocexpr %prec NEG { rpn_UNNOT(&$$, &$2); } | T_OP_HIGH T_LPAREN relocexpr T_RPAREN { rpn_HIGH(&$$, &$3); } | T_OP_LOW T_LPAREN relocexpr T_RPAREN { rpn_LOW(&$$, &$3); } | T_OP_ISCONST T_LPAREN relocexpr T_RPAREN{ rpn_ISCONST(&$$, &$3); } | T_OP_BANK T_LPAREN scoped_anon_id T_RPAREN { /* '@' is also a T_ID, it is handled here. */ rpn_BankSymbol(&$$, $3); } | T_OP_BANK T_LPAREN string T_RPAREN { rpn_BankSection(&$$, $3); } | T_OP_DEF { lexer_ToggleStringExpansion(false); } T_LPAREN scoped_anon_id T_RPAREN { struct Symbol const *sym = sym_FindScopedSymbol($4); rpn_Number(&$$, !!sym); lexer_ToggleStringExpansion(true); } | T_OP_ROUND T_LPAREN const T_RPAREN { rpn_Number(&$$, fix_Round($3)); } | T_OP_CEIL T_LPAREN const T_RPAREN { rpn_Number(&$$, fix_Ceil($3)); } | T_OP_FLOOR T_LPAREN const T_RPAREN { rpn_Number(&$$, fix_Floor($3)); } | T_OP_FDIV T_LPAREN const T_COMMA const T_RPAREN { rpn_Number(&$$, fix_Div($3, $5)); } | T_OP_FMUL T_LPAREN const T_COMMA const T_RPAREN { rpn_Number(&$$, fix_Mul($3, $5)); } | T_OP_POW T_LPAREN const T_COMMA const T_RPAREN { rpn_Number(&$$, fix_Pow($3, $5)); } | T_OP_LOG T_LPAREN const T_COMMA const T_RPAREN { rpn_Number(&$$, fix_Log($3, $5)); } | T_OP_SIN T_LPAREN const T_RPAREN { rpn_Number(&$$, fix_Sin($3)); } | T_OP_COS T_LPAREN const T_RPAREN { rpn_Number(&$$, fix_Cos($3)); } | T_OP_TAN T_LPAREN const T_RPAREN { rpn_Number(&$$, fix_Tan($3)); } | T_OP_ASIN T_LPAREN const T_RPAREN { rpn_Number(&$$, fix_ASin($3)); } | T_OP_ACOS T_LPAREN const T_RPAREN { rpn_Number(&$$, fix_ACos($3)); } | T_OP_ATAN T_LPAREN const T_RPAREN { rpn_Number(&$$, fix_ATan($3)); } | T_OP_ATAN2 T_LPAREN const T_COMMA const T_RPAREN { rpn_Number(&$$, fix_ATan2($3, $5)); } | T_OP_STRCMP T_LPAREN string T_COMMA string T_RPAREN { rpn_Number(&$$, strcmp($3, $5)); } | T_OP_STRIN T_LPAREN string T_COMMA string T_RPAREN { char *p = strstr($3, $5); rpn_Number(&$$, p ? p - $3 + 1 : 0); } | T_OP_STRRIN T_LPAREN string T_COMMA string T_RPAREN { char *p = strrstr($3, $5); rpn_Number(&$$, p ? p - $3 + 1 : 0); } | T_OP_STRLEN T_LPAREN string T_RPAREN { rpn_Number(&$$, strlenUTF8($3)); } | T_LPAREN relocexpr T_RPAREN { $$ = $2; } ; uconst : const { $$ = $1; if ($$ < 0) fatalerror("Constant mustn't be negative: %d\n", $1); } ; const : relocexpr { if (!rpn_isKnown(&$1)) { error("Expected constant expression: %s\n", $1.reason); $$ = 0; } else { $$ = $1.nVal; } } ; const_no_str : relocexpr_no_str { if (!rpn_isKnown(&$1)) { error("Expected constant expression: %s\n", $1.reason); $$ = 0; } else { $$ = $1.nVal; } } ; string : T_STRING | T_OP_STRSUB T_LPAREN string T_COMMA uconst T_COMMA uconst T_RPAREN { strsubUTF8($$, sizeof($$), $3, $5, $7); } | T_OP_STRCAT T_LPAREN T_RPAREN { $$[0] = '\0'; } | T_OP_STRCAT T_LPAREN strcat_args T_RPAREN { strcpy($$, $3); } | T_OP_STRUPR T_LPAREN string T_RPAREN { upperstring($$, $3); } | T_OP_STRLWR T_LPAREN string T_RPAREN { lowerstring($$, $3); } | T_OP_STRRPL T_LPAREN string T_COMMA string T_COMMA string T_RPAREN { strrpl($$, sizeof($$), $3, $5, $7); } | T_OP_STRFMT T_LPAREN strfmt_args T_RPAREN { strfmt($$, sizeof($$), $3.format, $3.nbArgs, $3.args); freeStrFmtArgList(&$3); } ; strcat_args : string | strcat_args T_COMMA string { int ret = snprintf($$, sizeof($$), "%s%s", $1, $3); if (ret == -1) fatalerror("snprintf error in STRCAT: %s\n", strerror(errno)); else if ((unsigned int)ret >= sizeof($$)) warning(WARNING_LONG_STR, "STRCAT: String too long '%s%s'\n", $1, $3); } ; strfmt_args : string strfmt_va_args { $$.format = strdup($1); $$.capacity = $2.capacity; $$.nbArgs = $2.nbArgs; $$.args = $2.args; } ; strfmt_va_args : %empty { initStrFmtArgList(&$$); } | strfmt_va_args T_COMMA relocexpr_no_str { int32_t value; if (!rpn_isKnown(&$3)) { error("Expected constant expression: %s\n", $3.reason); value = 0; } else { value = $3.nVal; } size_t i = nextStrFmtArgListIndex(&$1); $1.args[i].number = value; $1.args[i].isNumeric = true; $$ = $1; } | strfmt_va_args T_COMMA string { size_t i = nextStrFmtArgListIndex(&$1); $1.args[i].string = strdup($3); $1.args[i].isNumeric = false; $$ = $1; } ; section : T_POP_SECTION sectmod string T_COMMA sectiontype sectorg sectattrs { out_NewSection($3, $5, $6, &$7, $2); } ; sectmod : %empty { $$ = SECTION_NORMAL; } | T_POP_UNION { $$ = SECTION_UNION; } | T_POP_FRAGMENT{ $$ = SECTION_FRAGMENT; } ; sectiontype : T_SECT_WRAM0 { $$ = SECTTYPE_WRAM0; } | T_SECT_VRAM { $$ = SECTTYPE_VRAM; } | T_SECT_ROMX { $$ = SECTTYPE_ROMX; } | T_SECT_ROM0 { $$ = SECTTYPE_ROM0; } | T_SECT_HRAM { $$ = SECTTYPE_HRAM; } | T_SECT_WRAMX { $$ = SECTTYPE_WRAMX; } | T_SECT_SRAM { $$ = SECTTYPE_SRAM; } | T_SECT_OAM { $$ = SECTTYPE_OAM; } ; sectorg : %empty { $$ = -1; } | T_LBRACK uconst T_RBRACK { if ($2 < 0 || $2 >= 0x10000) { error("Address $%x is not 16-bit\n", $2); $$ = -1; } else { $$ = $2; } } ; sectattrs : %empty { $$.alignment = 0; $$.alignOfs = 0; $$.bank = -1; } | sectattrs T_COMMA T_OP_ALIGN T_LBRACK uconst T_RBRACK { $$.alignment = $5; } | sectattrs T_COMMA T_OP_ALIGN T_LBRACK uconst T_COMMA uconst T_RBRACK { $$.alignment = $5; $$.alignOfs = $7; } | sectattrs T_COMMA T_OP_BANK T_LBRACK uconst T_RBRACK { /* We cannot check the validity of this now */ $$.bank = $5; } ; slice_const : T_LBRACK T_TOKEN_B T_TOKEN_AT T_COLON const T_RBRACK { $$ = $5; } ; cpu_command : T_Z80_LD z80_ld_args | T_Z80_LD T_MODE_A T_COMMA z80_ld_a_comma_args | T_Z80_LD T_MODE_BC T_COMMA z80_ld_bc_comma_args | T_Z80_LD T_MODE_DE T_COMMA z80_ld_de_comma_args | T_Z80_LD T_MODE_HL T_COMMA z80_ld_hl_comma_args | T_Z80_LD T_MODE_SP T_COMMA z80_ld_sp_comma_args | T_Z80_LD T_LBRACK T_TOKEN_B T_TOKEN_AT T_COLON optional_ellipsis T_RBRACK T_POP_EQUAL z80_ld_incbin_args ; z80_ld_args : T_MODE_PC T_COMMA T_MODE_PC { out_AbsByte(0x00); } // $00: nop ==> ld pc, pc | T_MODE_F T_COMMA T_MODE_F { out_AbsByte(0x00); } // $00: nop ==> ld f, f | reg_rr T_COMMA T_MODE_A { // $02,$12,$22,$32: ld [], a out_AbsByte(0x02 | ($1 << 4)); } | reg_ss T_OP_ADD { // $03,$13,$23,$33: inc ==> ld + out_AbsByte(0x03 | ($1 << 4)); } | reg_r T_OP_ADD { // $04,$0C,$14,$1C,$24,$2C,$34,$3C: inc ==> ld + out_AbsByte(0x04 | ($1 << 3)); } | reg_r T_OP_SUB { // $05,$0D,$15,$1D,$25,$2D,$35,$3D: dec ==> ld - out_AbsByte(0x05 | ($1 << 3)); } | reg_na T_COMMA reloc_8bit { // $06,$0E,$16,$1E,$26,$2E,$36: ld , out_AbsByte(0x06 | ($1 << 3)); out_RelByte(&$3, 1); } | T_PRIME T_PRIME T_MODE_A { out_AbsByte(0x07); } // $07: rlca ==> ld ''a | op_mem_ind T_COMMA T_MODE_SP { // $08: ld [], sp out_AbsByte(0x08); out_RelWord(&$1, 1); } | reg_ss T_OP_SUB { // $0B,$1B,$2B,$3B: dec ==> ld - out_AbsByte(0x09 | ($1 << 4)); } | T_MODE_A T_PRIME T_PRIME { out_AbsByte(0x0F); } // $0F: rrca ==> ld a'' | T_COMMA { // $10: stop ==> ld , out_AbsByte(0x10); out_AbsByte(0x00); } | T_COMMA reloc_8bit { // $10: stop ==> ld , out_AbsByte(0x10); out_RelByte(&$2, 1); } | T_PRIME T_MODE_A { out_AbsByte(0x17); } // $17: rla ==> ld 'a | T_MODE_PC T_COMMA T_TOKEN_B reloc_16bit { // $18: jr ==> ld pc, b out_AbsByte(0x18); out_PCRelByte(&$4, 1); } | T_MODE_A T_PRIME { out_AbsByte(0x1F); } // $1F: rra ==> ld a' | ccode T_MODE_PC T_COMMA T_TOKEN_B reloc_16bit { // $20,$28,$30,$38: jr ==> ld pc, b out_AbsByte(0x20 | ($1 << 3)); out_PCRelByte(&$5, 1); } | T_MODE_A T_QUESTION { out_AbsByte(0x27); } // $27: daa ==> ld a? | T_OP_NOT T_MODE_A { out_AbsByte(0x2F); } // $2F: cpl ==> ld ~a | T_MODE_F T_PERIOD const_3bit T_COMMA const_1bit { // $37: scf ==> ld f.4, 1 if ($3 != 4) error("Bit field of F must be 4\n"); else if ($5 != 1) error("LD value of F.4 must be 1\n"); else out_AbsByte(0x37); } | T_MODE_F T_PERIOD const_3bit T_COMMA T_OP_LOGICNOT T_MODE_F T_PERIOD const_3bit { // $3F: ccf ==> ld f.4, !f.4 if (($3 == 4) && ($8 == 4)) out_AbsByte(0x3F); else error("Bit field of F must be 4\n"); } | reg_na T_COMMA reg_r { // $40-$77: ld , (halt ==> ld [hl], [hl]) out_AbsByte(0x40 | ($1 << 3) | $3); } | T_MODE_F T_PERIOD const_3bit T_COMMA T_MODE_A T_OP_SUB reg_r { // $B8-$BF: cp a, ==> ld f.4, a - if ($3 == 4) out_AbsByte(0xB8 | $7); else error("Bit field of F must be 4\n"); } | ccode T_MODE_PC T_COMMA sp_inc_inc_ind { // $C0,$C8,$D0,$D8: ret ==> ld pc, [sp++] out_AbsByte(0xC0 | ($1 << 3)); } | ccode T_MODE_PC T_COMMA reloc_16bit { // $C2,$CA,$D2,$DA: jp , ==> ld pc, out_AbsByte(0xC2 | ($1 << 3)); out_RelWord(&$4, 1); } | T_MODE_PC T_COMMA reloc_16bit { // $C3: jp ==> ld pc, out_AbsByte(0xC3); } | ccode dec_dec_sp_ind T_COMMA T_MODE_PC T_COMMA reloc_16bit { // $C4,$CC,$D4,$DC: call , ==> ld [--sp], pc, out_AbsByte(0xC4 | ($1 << 3)); out_RelWord(&$6, 1); } | dec_dec_sp_ind T_COMMA reg_tt { // $C5,$D5,$E5,$F5: push ==> ld [--sp], out_AbsByte(0xC5 | ($3 << 4)); } | dec_dec_sp_ind T_COMMA T_MODE_PC T_COMMA T_TOKEN_B reloc_16bit { // $C7,$CF,$D7,$DF,$E7,$EF,$F7,$FF: rst ==> ld [--sp], pc, b rpn_CheckRST(&$6, &$6); if (!rpn_isKnown(&$6)) out_RelByte(&$6, 0); else out_AbsByte(0xC7 | $6.nVal); rpn_Free(&$6); } | T_MODE_PC T_COMMA sp_inc_inc_ind { out_AbsByte(0xC9); } // $C9: ret ==> ld pc, [sp++] | dec_dec_sp_ind T_COMMA T_MODE_PC T_COMMA reloc_16bit { // $CD: call ==> ld [--sp], pc, out_AbsByte(0xCD); out_RelWord(&$5, 1); } | T_MODE_PC T_COMMA sp_inc_inc_ind T_OP_DIV T_Z80_LD T_MODE_IME T_COMMA const_1bit { // $D9: reti ==> ld pc, [sp++] / ld ime, 1 if ($8 == 1) out_AbsByte(0xD9); else error("LD value of F.4 must be 1\n"); } | T_LBRACK T_TOKEN_H reloc_16bit T_RBRACK T_COMMA T_MODE_A { // $E0: ldh [], a ==> ld [h ], a rpn_CheckHRAM(&$3, &$3); out_AbsByte(0xE0); out_RelByte(&$3, 1); } | T_LBRACK T_TOKEN_H T_MODE_C T_RBRACK T_COMMA T_MODE_A { // $E2: ldh [c], a ==> ld [h c], a out_AbsByte(0xE2); } | T_LBRACK T_MODE_C T_RBRACK T_COMMA T_MODE_A { // $E2: ldh [c], a ==> ld [c], a out_AbsByte(0xE2); } | T_MODE_SP T_COMMA T_MODE_SP T_OP_ADD reloc_8bit { // $E8: add sp, ==> ld sp, sp+ out_AbsByte(0xE8); out_RelByte(&$5, 1); } | T_MODE_PC T_COMMA T_MODE_HL { out_AbsByte(0xE9); } // $E9: jp hl ==> ld pc, hl | op_mem_ind T_COMMA T_MODE_A { // $EA: ld [], a ==> ld [], a if (optimizeloads && rpn_isKnown(&$1) && $1.nVal >= 0xFF00) { out_AbsByte(0xE0); out_AbsByte($1.nVal & 0xFF); rpn_Free(&$1); } else { out_AbsByte(0xEA); out_RelWord(&$1, 1); } } | T_MODE_AF T_COMMA sp_inc_inc_ind { out_AbsByte(0xF1); } // $F1: pop af ==> ld af, [sp++] | T_MODE_IME T_COMMA const_1bit { // $F3: di ==> ld ime, 0; $FB: ei ==> ld ime, 1 out_AbsByte(0xF3 | ($3 << 3)); } | T_MODE_F T_PERIOD const_3bit T_COMMA T_MODE_A T_OP_SUB reloc_8bit { // $FE: cp a, ==> ld f.4, a - if ($3 == 4) { out_AbsByte(0xFE); out_RelByte(&$7, 1); } else { error("Bit field of F must be 4\n"); } } | reg_na T_COMMA T_PRIME T_PRIME reg_r { // $CB $00-$06: rlc ==> ld , '' if ($1 == $5) { out_AbsByte(0xCB); out_AbsByte(0x00 | $1); } else { error("Destination and source registers must match\n"); } } | reg_na T_COMMA reg_r T_PRIME T_PRIME { // $CB $08-$0E: rrc ==> ld , '' if ($1 == $3) { out_AbsByte(0xCB); out_AbsByte(0x08 | $1); } else { error("Destination and source registers must match\n"); } } | reg_na T_COMMA T_PRIME reg_r { // $CB $10-$16: rl ==> ld , ' if ($1 == $4) { out_AbsByte(0xCB); out_AbsByte(0x10 | $1); } else { error("Destination and source registers must match\n"); } } | reg_na T_COMMA reg_r T_PRIME { // $CB $18-$1E: rr ==> ld , ' if ($1 == $3) { out_AbsByte(0xCB); out_AbsByte(0x18 | $1); } else { error("Destination and source registers must match\n"); } } | reg_na T_COMMA T_OP_SHL reg_r { // $CB $20-$26: sla ==> ld , << if ($1 == $4) { out_AbsByte(0xCB); out_AbsByte(0x20 | $1); } else { error("Destination and source registers must match\n"); } } | reg_na T_COMMA T_OP_SHR reg_r { // $CB $28-$2E: sra ==> ld , >> if ($1 == $4) { out_AbsByte(0xCB); out_AbsByte(0x28 | $1); } else { error("Destination and source registers must match\n"); } } | reg_na T_COMMA T_PRIME T_PRIME reg_r T_PRIME T_PRIME { // $CB $30-$36: swap ==> ld , '''' if ($1 == $5) { out_AbsByte(0xCB); out_AbsByte(0x30 | $1); } else { error("Destination and source registers must match\n"); } } | reg_na T_COMMA T_OP_SHRL reg_r { // $CB $38-$3E: srl ==> ld , >>> if ($1 == $4) { out_AbsByte(0xCB); out_AbsByte(0x38 | $1); } else { error("Destination and source registers must match\n"); } } | T_MODE_F T_PERIOD const_3bit T_COMMA reg_r T_PERIOD const_3bit { // $CB $40-$7F: bit , ==> ld f.7, . if ($3 == 7) { out_AbsByte(0xCB); out_AbsByte(0x40 | ($7 << 3) | $5); } else { error("Bit field of F must be 7\n"); } } | reg_r T_PERIOD const_3bit T_COMMA const_1bit { // $CB $80-$BF: res , ==> ld ., 0; // $CB $C0-$FF: set , ==> ld ., 1 out_AbsByte(0xCB); out_AbsByte(0x80 | ($5 << 6) | ($3 << 3) | $1); } | T_LBRACK T_TOKEN_B T_TOKEN_AT T_RBRACK T_COMMA T_QUESTION { // db ==> ld [b @], ? out_Skip(1, false); } | T_LBRACK T_TOKEN_B T_TOKEN_AT T_RBRACK T_COMMA reloc_8bit_no_str { // db ==> ld [b @], out_RelByte(&$6, 0); } | T_LBRACK T_TOKEN_B T_TOKEN_AT T_COLON optional_ellipsis T_RBRACK T_COMMA constlist_8bit trailing_comma // db ==> ld [b @:...], | T_LBRACK T_TOKEN_W T_TOKEN_AT T_RBRACK T_COMMA T_QUESTION { // dw ==> ld [w @], ? out_Skip(2, false); } | T_LBRACK T_TOKEN_W T_TOKEN_AT T_RBRACK T_COMMA reloc_16bit_no_str { // dw ==> ld [w @], out_RelWord(&$6, 0); } | T_LBRACK T_TOKEN_W T_TOKEN_AT T_COLON optional_ellipsis T_RBRACK T_COMMA constlist_16bit trailing_comma // dw ==> ld [w @:...], | T_LBRACK T_TOKEN_L T_TOKEN_AT T_RBRACK T_COMMA T_QUESTION { // dl ==> ld [l @], ? out_Skip(4, false); } | T_LBRACK T_TOKEN_L T_TOKEN_AT T_RBRACK T_COMMA relocexpr_no_str { // dl ==> ld [l @], out_RelLong(&$6, 0); } | T_LBRACK T_TOKEN_L T_TOKEN_AT T_COLON optional_ellipsis T_RBRACK T_COMMA constlist_32bit trailing_comma // dl ==> ld [l @:...], | slice_const T_COMMA ds_args trailing_comma { // ds , ==> ld [b @:], out_RelBytes($1, $3.args, $3.nbArgs); freeDsArgList(&$3); } | slice_const T_COMMA T_QUESTION { // ds ==> ld [b @:], ? out_Skip($1, true); } | slice_const T_POP_EQUAL string T_LBRACK const T_COLON optional_ellipsis T_RBRACK { // INCBIN "file.bin", , ==> ld [b @:] = "file.bin"[:...] if ($1 < 0) fatalerror("Constant mustn't be negative: %d\n", $1); out_BinaryFileSlice($3, $5, $1); if (oFailedOnMissingInclude) YYACCEPT; } ; z80_ld_a_comma_args : reg_rr { // $0A,$1A,$2A,$3A: ld a, [] out_AbsByte(0x0A | ($1 << 4)); } | T_MODE_A T_QUESTION { out_AbsByte(0x27); } // $27: daa ==> ld a, a? | T_OP_NOT T_MODE_A { out_AbsByte(0x2F); } // $2F: cpl ==> ld a, ~a | reloc_8bit { // $3E: ld a, out_AbsByte(0x3E); out_RelByte(&$1, 1); } | reg_r { out_AbsByte(0x78 | $1); } // $78-$7F: ld a, | T_MODE_A T_OP_ADD reg_r { // $80-$87: add a, ==> ld a, a + out_AbsByte(0x80 | $3); } | T_MODE_A T_OP_ADD reg_nc T_OP_ADD T_TOKEN_C { // $88-$8F: adc a, ==> ld a, a + + c (except $89: adc a, c) out_AbsByte(0x88 | $3); } | T_MODE_A T_OP_ADD T_OP_LOW T_LPAREN T_MODE_BC T_RPAREN T_OP_ADD T_TOKEN_C { // $89: adc a, c ==> ld a, a + HIGH(bc) + c out_AbsByte(0x89); } | T_MODE_A T_OP_SUB reg_r { // $90-$97: sub a, ==> ld a, a - out_AbsByte(0x90 | $3); } | T_MODE_A T_OP_SUB reg_nc T_OP_SUB T_TOKEN_C { // $98-$9F: sbc a, ==> ld a, a - - c (except $99: sbc a, c) out_AbsByte(0x98 | $3); } | T_MODE_A T_OP_SUB T_OP_LOW T_LPAREN T_MODE_BC T_RPAREN T_OP_SUB T_TOKEN_C { // $99: sbc a, c ==> ld a, a - LOW(bc) - c out_AbsByte(0x99); } | T_MODE_A T_OP_AND reg_r { // $A0-$A7: and a, ==> ld a, a & out_AbsByte(0xA0 | $3); } | T_MODE_A T_OP_XOR reg_r { // $A8-$AF: xor a, ==> ld a, a ^ out_AbsByte(0xA8 | $3); } | T_MODE_A T_OP_OR reg_r { // $B0-$B7: or a, ==> ld a, a | out_AbsByte(0xB0 | $3); } | T_MODE_A T_OP_ADD reloc_8bit { // $C6: add a, ==> ld a, a + out_AbsByte(0xC6); out_RelByte(&$3, 1); } | T_MODE_A T_OP_SUB reloc_8bit { // $D6: sub a, ==> ld a, a - out_AbsByte(0xD6); out_RelByte(&$3, 1); } | T_MODE_A T_OP_AND reloc_8bit { // $E6: and a, ==> ld a, a & out_AbsByte(0xE6); out_RelByte(&$3, 1); } | T_MODE_A T_OP_XOR reloc_8bit { // $EE: xor a, ==> ld a, a ^ out_AbsByte(0xEE); out_RelByte(&$3, 1); } | T_LBRACK T_TOKEN_H reloc_16bit T_RBRACK { // $F0: ldh a, [] ==> ld a, [h ] rpn_CheckHRAM(&$3, &$3); out_AbsByte(0xF0); out_RelByte(&$3, 1); } | T_LBRACK T_TOKEN_H T_MODE_C T_RBRACK { // $F2: ldh a, [c] ==> ld a, [c] out_AbsByte(0xF2); } | T_LBRACK T_MODE_C T_RBRACK { // $F2: ldh a, [c] ==> ld a, [h c] out_AbsByte(0xF2); } | T_MODE_A T_OP_OR reloc_8bit { // $F6: or a, ==> ld a, a | out_AbsByte(0xF6); out_RelByte(&$3, 1); } | op_mem_ind { // $FA: ld a, [] if (optimizeloads && rpn_isKnown(&$1) && $1.nVal >= 0xFF00) { out_AbsByte(0xF0); out_AbsByte($1.nVal & 0xFF); rpn_Free(&$1); } else { out_AbsByte(0xFA); out_RelWord(&$1, 1); } } | T_PRIME T_PRIME T_MODE_A { // $CB $07: rlc a ==> ld a, ''a out_AbsByte(0xCB); out_AbsByte(0x07); } | T_MODE_A T_PRIME T_PRIME { // $CB $0F: rrc a ==> ld a, a'' out_AbsByte(0xCB); out_AbsByte(0x0F); } | T_PRIME T_MODE_A { // $CB $17: rl a ==> ld a, 'a out_AbsByte(0xCB); out_AbsByte(0x17); } | T_MODE_A T_PRIME { // $CB $1F: rr a ==> ld a, a' out_AbsByte(0xCB); out_AbsByte(0x1F); } | T_OP_SHL T_MODE_A { // $CB $27: sla a ==> ld a, << a out_AbsByte(0xCB); out_AbsByte(0x27); } | T_OP_SHR T_MODE_A { // $CB $2F: sra a ==> ld a, >> a out_AbsByte(0xCB); out_AbsByte(0x2F); } | T_PRIME T_PRIME T_MODE_A T_PRIME T_PRIME { // $CB $37: swap a ==> ld a, ''a'' out_AbsByte(0xCB); out_AbsByte(0x37); } | T_OP_SHRL T_MODE_A { // $CB $3F: srl a ==> ld a, >>> a out_AbsByte(0xCB); out_AbsByte(0x3F); } | T_MODE_A T_OP_ADD T_TOKEN_C T_OP_ADD z80_ld_a_comma_a_plus_c_plus_args | T_MODE_A T_OP_SUB T_TOKEN_C T_OP_SUB z80_ld_a_comma_a_minus_c_minus_args ; z80_ld_a_comma_a_plus_c_plus_args : reg_nc { // $88-$8F: adc a, ==> ld a, a + c + (except $89: adc a, c) out_AbsByte(0x88 | $1); } | T_MODE_C { out_AbsByte(0x89); } // $89: adc a, c ==> ld a, a + c + c | reloc_8bit { // $CE: adc a, ==> ld a, a + c + out_AbsByte(0xCE); out_RelByte(&$1, 1); } ; z80_ld_a_comma_a_minus_c_minus_args : reg_nc { // $98-$9F: sbc a, ==> ld a, a - c - (except $99: sbc a, c) out_AbsByte(0x98 | $1); } | T_MODE_C { out_AbsByte(0x99); } // $99: sbc a, c ==> ld a, a - c - c | reloc_8bit { // $DE: sbc a, ==> ld a, a - c - out_AbsByte(0xDE); out_RelByte(&$1, 1); } ; z80_ld_bc_comma_args : reloc_16bit { // $01: ld bc, out_AbsByte(0x01); out_RelWord(&$1, 1); } | sp_inc_inc_ind { out_AbsByte(0xC1); } // $C1: pop bc ==> ld bc, [sp++] ; z80_ld_de_comma_args : reloc_16bit { // $11: ld de, out_AbsByte(0x11); out_RelWord(&$1, 1); } | sp_inc_inc_ind { out_AbsByte(0xD1); } // $D1: pop de ==> ld de, [sp++] ; z80_ld_hl_comma_args : reloc_16bit { // $21: ld hl, out_AbsByte(0x21); out_RelWord(&$1, 1); } | T_MODE_HL T_OP_ADD reg_ss { // $09,$19,$29,$39: add hl, ==> ld hl, hl + out_AbsByte(0x09 | ($3 << 4)); } | sp_inc_inc_ind { out_AbsByte(0xE1); } // $E1: pop hl ==> ld hl, [sp++] | T_MODE_SP T_OP_ADD reloc_8bit { // $F8: ld hl, sp + out_AbsByte(0xF8); out_RelByte(&$3, 1); } | T_MODE_SP { // $F8: ld hl, sp+0 ==> ld hl, sp out_AbsByte(0xF8); out_AbsByte(0x00); } ; z80_ld_sp_comma_args : reloc_16bit { // $31: ld sp, out_AbsByte(0x31); out_RelWord(&$1, 1); } | T_MODE_HL { out_AbsByte(0xF9); } // $F9: ld sp, hl ; z80_ld_incbin_args : string { // INCBIN "file.bin" ==> ld [b @:...] = "file.bin" out_BinaryFile($1, 0); if (oFailedOnMissingInclude) YYACCEPT; } | string T_LBRACK const T_COLON optional_ellipsis T_RBRACK { // INCBIN "file.bin", ==> ld [b @:...] = "file.bin"[:...] out_BinaryFile($1, $3); if (oFailedOnMissingInclude) YYACCEPT; } | string T_LBRACK const T_COLON const T_RBRACK { // INCBIN "file.bin", , ==> ld [b @:...] = "file.bin"[:] out_BinaryFileSlice($1, $3, $5); if (oFailedOnMissingInclude) YYACCEPT; } ; op_mem_ind : T_LBRACK reloc_16bit T_RBRACK { $$ = $2; } ; T_MODE_A : T_TOKEN_A | T_OP_HIGH T_LPAREN T_MODE_AF T_RPAREN ; T_MODE_F : T_TOKEN_F | T_OP_LOW T_LPAREN T_MODE_AF T_RPAREN ; T_MODE_B : T_TOKEN_B | T_OP_HIGH T_LPAREN T_MODE_BC T_RPAREN ; T_MODE_C : T_TOKEN_C | T_OP_LOW T_LPAREN T_MODE_BC T_RPAREN ; T_MODE_D : T_TOKEN_D | T_OP_HIGH T_LPAREN T_MODE_DE T_RPAREN ; T_MODE_E : T_TOKEN_E | T_OP_LOW T_LPAREN T_MODE_DE T_RPAREN ; T_MODE_H : T_TOKEN_H | T_OP_HIGH T_LPAREN T_MODE_HL T_RPAREN ; T_MODE_L : T_TOKEN_L | T_OP_LOW T_LPAREN T_MODE_HL T_RPAREN ; ccode : T_CC_NZ { $$ = CC_NZ; } | T_CC_Z { $$ = CC_Z; } | T_CC_NC { $$ = CC_NC; } | T_TOKEN_C { $$ = CC_C; } ; reg_nac : T_MODE_B { $$ = REG_B; } | T_MODE_D { $$ = REG_D; } | T_MODE_E { $$ = REG_E; } | T_MODE_H { $$ = REG_H; } | T_MODE_L { $$ = REG_L; } | T_LBRACK T_MODE_HL T_RBRACK { $$ = REG_HL_IND; } ; reg_na : reg_nac | T_MODE_C { $$ = REG_C; } ; reg_nc : reg_nac | T_MODE_A { $$ = REG_A; } ; reg_r : reg_nac | T_MODE_C { $$ = REG_C; } | 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_LBRACK T_MODE_BC T_RBRACK { $$ = REG_BC_IND; } | T_LBRACK T_MODE_DE T_RBRACK { $$ = REG_DE_IND; } | hl_ind_inc { $$ = REG_HL_INDINC; } | hl_ind_dec { $$ = REG_HL_INDDEC; } ; hl_ind_inc : T_LBRACK T_MODE_HL_INC T_RBRACK | T_LBRACK T_MODE_HL T_OP_ADD T_RBRACK ; hl_ind_dec : T_LBRACK T_MODE_HL_DEC T_RBRACK | T_LBRACK T_MODE_HL T_OP_SUB T_RBRACK ; sp_inc_inc_ind : T_LBRACK T_MODE_SP T_OP_ADD T_OP_ADD T_RBRACK ; dec_dec_sp_ind : T_LBRACK T_OP_SUB T_OP_SUB T_MODE_SP T_RBRACK ; %%