diff --git a/include/asm/output.h b/include/asm/output.h index bad90203..dbd363f3 100644 --- a/include/asm/output.h +++ b/include/asm/output.h @@ -22,8 +22,7 @@ extern struct Section *pSectionList, *pCurrentSection; void out_RegisterNode(struct FileStackNode *node); void out_ReplaceNode(struct FileStackNode *node); void out_SetFileName(char *s); -void out_CreatePatch(uint32_t type, struct Expression const *expr, - uint32_t ofs); +void out_CreatePatch(uint32_t type, struct Expression const *expr, uint32_t ofs, bool isOperand); bool out_CreateAssert(enum AssertionType type, struct Expression const *expr, char const *message, uint32_t ofs); void out_WriteObject(void); diff --git a/include/asm/section.h b/include/asm/section.h index d8c7855a..9f0efd92 100644 --- a/include/asm/section.h +++ b/include/asm/section.h @@ -62,11 +62,11 @@ void out_AbsWordGroup(uint8_t const *s, int32_t length); void out_AbsLongGroup(uint8_t const *s, int32_t length); void out_Skip(int32_t skip, bool ds); void out_String(char const *s); -void out_RelByte(struct Expression *expr); +void out_RelByte(struct Expression *expr, bool isOperand); void out_RelBytes(struct Expression *expr, uint32_t n); -void out_RelWord(struct Expression *expr); -void out_RelLong(struct Expression *expr); -void out_PCRelByte(struct Expression *expr); +void out_RelWord(struct Expression *expr, bool isOperand); +void out_RelLong(struct Expression *expr, bool isOperand); +void out_PCRelByte(struct Expression *expr, bool isOperand); void out_BinaryFile(char const *s, int32_t startPos); void out_BinaryFileSlice(char const *s, int32_t start_pos, int32_t length); diff --git a/include/link/section.h b/include/link/section.h index d9da1a5b..b7cb6eb7 100644 --- a/include/link/section.h +++ b/include/link/section.h @@ -34,6 +34,7 @@ struct Patch { uint32_t pcSectionID; uint32_t pcOffset; enum PatchType type; + bool isOperand; int32_t rpnSize; uint8_t *rpnExpression; diff --git a/include/linkdefs.h b/include/linkdefs.h index d21f61f8..2cdb76e0 100644 --- a/include/linkdefs.h +++ b/include/linkdefs.h @@ -99,6 +99,8 @@ enum ExportLevel { SYMTYPE_EXPORT }; +// Bit 7 is special, not part of the actual patch type +#define PATCH_ISOPERAND 0x80 enum PatchType { PATCHTYPE_BYTE, PATCHTYPE_WORD, diff --git a/src/asm/lexer.c b/src/asm/lexer.c index 706363f6..d2827507 100644 --- a/src/asm/lexer.c +++ b/src/asm/lexer.c @@ -341,7 +341,6 @@ struct LexerState { bool atLineStart; uint32_t lineNo; uint32_t colNo; - int lastToken; bool capturing; /* Whether the text being lexed should be captured */ size_t captureSize; /* Amount of text captured */ @@ -363,7 +362,6 @@ static void initState(struct LexerState *state) { state->mode = LEXER_NORMAL; state->atLineStart = true; /* yylex() will init colNo due to this */ - state->lastToken = 0; state->capturing = false; state->captureBuf = NULL; @@ -1921,9 +1919,13 @@ static int yylex_RAW(void) i--; /* Empty macro args break their expansion, so prevent that */ if (i == 0) { - /* Return the EOF token, and don't shift a non-existent char! */ + // If at EOF, don't shift a non-existent chat + // However, don't return EOF, as this might cause a bug... + // If a macro is invoked on the last line of a file, with no blank + // line afterwards, returning EOF here will cause Bison to stop + // parsing, despite the lexer being ready to output more. if (c == EOF) - return 0; + return T_NEWLINE; shiftChars(1); if (c == '\r' && peek(0) == '\n') shiftChars(1); @@ -2163,21 +2165,16 @@ restart: /* Make sure to terminate files with a line feed */ if (token == 0) { - if (lexerState->lastToken != T_NEWLINE) { - dbgPrint("Forcing EOL at EOF\n"); - token = T_NEWLINE; - } else { /* Try to switch to new buffer; if it succeeds, scan again */ - dbgPrint("Reached EOF!\n"); - /* Captures end at their buffer's boundary no matter what */ - if (!lexerState->capturing) { - if (!yywrap()) - goto restart; - dbgPrint("Reached end of input."); - return 0; - } + /* Try to switch to new buffer; if it succeeds, scan again */ + dbgPrint("Reached EOF!\n"); + /* Captures end at their buffer's boundary no matter what */ + if (!lexerState->capturing) { + if (!yywrap()) + goto restart; + dbgPrint("Reached end of input."); + return 0; } } - lexerState->lastToken = token; lexerState->atLineStart = false; if (token == T_NEWLINE) @@ -2238,7 +2235,6 @@ void lexer_CaptureRept(struct CaptureBody *capture) * We know we have read exactly "ENDR", not e.g. an EQUS */ lexerState->captureSize -= strlen("ENDR"); - lexerState->lastToken = T_POP_ENDR; // Force EOL at EOF goto finish; } level--; @@ -2301,7 +2297,6 @@ void lexer_CaptureMacroBody(struct CaptureBody *capture) * We know we have read exactly "ENDM", not e.g. an EQUS */ lexerState->captureSize -= strlen("ENDM"); - lexerState->lastToken = T_POP_ENDM; // Force EOL at EOF goto finish; } } diff --git a/src/asm/output.c b/src/asm/output.c index 0481bba5..14c466f3 100644 --- a/src/asm/output.c +++ b/src/asm/output.c @@ -39,6 +39,7 @@ struct Patch { struct Section *pcSection; uint32_t pcOffset; uint8_t type; + bool isOperand; // If set, PC is not at the patch's address, but at the byte before uint32_t nRPNSize; uint8_t *pRPN; struct Patch *next; @@ -203,13 +204,17 @@ static uint32_t getSectIDIfAny(struct Section const *sect) static void writepatch(struct Patch const *patch, FILE *f) { assert(patch->src->ID != -1); + uint8_t type = patch->type; + + if (patch->isOperand) + type |= PATCH_ISOPERAND; putlong(patch->src->ID, f); putlong(patch->lineNo, f); putlong(patch->nOffset, f); putlong(getSectIDIfAny(patch->pcSection), f); putlong(patch->pcOffset, f); - putc(patch->type, f); + putc(type, f); putlong(patch->nRPNSize, f); fwrite(patch->pRPN, 1, patch->nRPNSize, f); } @@ -382,6 +387,7 @@ static struct Patch *allocpatch(uint32_t type, struct Expression const *expr, ui fatalerror("No memory for patch's RPN expression: %s\n", strerror(errno)); patch->type = type; + patch->isOperand = false; patch->src = node; out_RegisterNode(node); patch->lineNo = lexer_GetLineNo(); @@ -410,10 +416,11 @@ static struct Patch *allocpatch(uint32_t type, struct Expression const *expr, ui /* * Create a new patch (includes the rpn expr) */ -void out_CreatePatch(uint32_t type, struct Expression const *expr, uint32_t ofs) +void out_CreatePatch(uint32_t type, struct Expression const *expr, uint32_t ofs, bool isOperand) { struct Patch *patch = allocpatch(type, expr, ofs); + patch->isOperand = isOperand; patch->next = pCurrentSection->patches; pCurrentSection->patches = patch; } diff --git a/src/asm/parser.y b/src/asm/parser.y index 52ac6097..4fbbc790 100644 --- a/src/asm/parser.y +++ b/src/asm/parser.y @@ -36,9 +36,6 @@ #include "linkdefs.h" #include "platform.h" // strncasecmp, strdup -int32_t nPCOffset; /* Read by rpn_Symbol */ - -static uint32_t nListCountEmpty; static bool executeElseBlock; /* If this is set, ELIFs cannot be executed anymore */ static struct CaptureBody captureBody; /* Captures a REPT/FOR or MACRO */ @@ -406,6 +403,7 @@ enum { int32_t step; } forArgs; struct StrFmtArgList strfmtArgs; + bool hasEmpty; // Whether `db`, `dw`, `dl` argument lists contain any empty entries } %type relocexpr @@ -420,6 +418,9 @@ enum { %type reloc_16bit %type reloc_16bit_no_str %type sectiontype +%type constlist_8bit constlist_8bit_entry +%type constlist_16bit constlist_16bit_entry +%type constlist_32bit constlist_32bit_entry %type string %type strcat_args @@ -584,21 +585,21 @@ enum { %% -asmfile : lines; +asmfile : lines last_line +; /* Note: The lexer adds T_NEWLINE at the end of the input */ lines : %empty - | lines { - nListCountEmpty = 0; - nPCOffset = 0; - } line + | lines line ; -line : label T_NEWLINE - | label cpu_command T_NEWLINE - | label macro T_NEWLINE - | label directive T_NEWLINE - | assignment_directive T_NEWLINE +last_line : label + | label cpu_command + | label macro + | label directive + | assignment_directive +; +line : last_line T_NEWLINE | line_directive /* Directives that manage newlines themselves */ | error T_NEWLINE { /* Continue parsing the next line on a syntax error */ fstk_StopRept(); @@ -951,7 +952,7 @@ ds : T_POP_DS uconst { out_Skip($2, true); } /* Authorize empty entries if there is only one */ db : T_POP_DB constlist_8bit_entry T_COMMA constlist_8bit { - if (nListCountEmpty > 0) + if ($2 || $4) warning(WARNING_EMPTY_ENTRY, "Empty entry in list of 8-bit elements (treated as padding).\n"); } @@ -959,7 +960,7 @@ db : T_POP_DB constlist_8bit_entry T_COMMA constlist_8bit { ; dw : T_POP_DW constlist_16bit_entry T_COMMA constlist_16bit { - if (nListCountEmpty > 0) + if ($2 || $4) warning(WARNING_EMPTY_ENTRY, "Empty entry in list of 16-bit elements (treated as padding).\n"); } @@ -967,7 +968,7 @@ dw : T_POP_DW constlist_16bit_entry T_COMMA constlist_16bit { ; dl : T_POP_DL constlist_32bit_entry T_COMMA constlist_32bit { - if (nListCountEmpty > 0) + if ($2 || $4) warning(WARNING_EMPTY_ENTRY, "Empty entry in list of 32-bit elements (treated as padding).\n"); } @@ -1110,56 +1111,74 @@ const_3bit : const { ; constlist_8bit : constlist_8bit_entry - | constlist_8bit T_COMMA constlist_8bit_entry + | constlist_8bit T_COMMA constlist_8bit_entry { + $$ = $1 || $3; + } ; constlist_8bit_entry : %empty { out_Skip(1, false); - nListCountEmpty++; + $$ = true; + } + | reloc_8bit_no_str { + out_RelByte(&$1, false); + $$ = false; } - | reloc_8bit_no_str { out_RelByte(&$1); } | 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); + $$ = false; } ; constlist_16bit : constlist_16bit_entry - | constlist_16bit T_COMMA constlist_16bit_entry + | constlist_16bit T_COMMA constlist_16bit_entry { + $$ = $1 || $3; + } ; constlist_16bit_entry : %empty { out_Skip(2, false); - nListCountEmpty++; + $$ = true; + } + | reloc_16bit_no_str { + out_RelWord(&$1, false); + $$ = false; } - | reloc_16bit_no_str { out_RelWord(&$1); } | 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); + $$ = false; } ; constlist_32bit : constlist_32bit_entry - | constlist_32bit T_COMMA constlist_32bit_entry + | constlist_32bit T_COMMA constlist_32bit_entry { + $$ = $1 || $3; + } ; constlist_32bit_entry : %empty { out_Skip(4, false); - nListCountEmpty++; + $$ = true; + } + | relocexpr_no_str { + out_RelLong(&$1, false); + $$ = false; } - | relocexpr_no_str { out_RelLong(&$1); } | 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); + $$ = false; } ; @@ -1500,31 +1519,31 @@ sectattrs : %empty { ; -cpu_command : { nPCOffset = 1; } z80_adc - | { nPCOffset = 1; } z80_add - | { nPCOffset = 1; } z80_and - | { nPCOffset = 1; } z80_bit - | { nPCOffset = 1; } z80_call +cpu_command : z80_adc + | z80_add + | z80_and + | z80_bit + | z80_call | z80_ccf - | { nPCOffset = 1; } z80_cp + | z80_cp | z80_cpl | z80_daa - | { nPCOffset = 1; } z80_dec + | z80_dec | z80_di | z80_ei | z80_halt | z80_inc - | { nPCOffset = 1; } z80_jp - | { nPCOffset = 1; } z80_jr - | { nPCOffset = 1; } z80_ld + | z80_jp + | z80_jr + | z80_ld | z80_ldd | z80_ldi - | { nPCOffset = 1; } z80_ldio + | z80_ldio | z80_nop - | { nPCOffset = 1; } z80_or + | z80_or | z80_pop | z80_push - | { nPCOffset = 1; } z80_res + | z80_res | z80_ret | z80_reti | z80_rl @@ -1536,41 +1555,41 @@ cpu_command : { nPCOffset = 1; } z80_adc | z80_rrc | z80_rrca | /*{ nPCOffset = 0; }*/ z80_rst - | { nPCOffset = 1; } z80_sbc + | z80_sbc | z80_scf - | { nPCOffset = 1; } z80_set + | z80_set | z80_sla | z80_sra | z80_srl - | { nPCOffset = 1; } z80_stop - | { nPCOffset = 1; } z80_sub + | z80_stop + | z80_sub | z80_swap - | { nPCOffset = 1; } z80_xor + | z80_xor ; z80_adc : T_Z80_ADC op_a_n { out_AbsByte(0xCE); - out_RelByte(&$2); + out_RelByte(&$2, true); } | T_Z80_ADC op_a_r { out_AbsByte(0x88 | $2); } ; z80_add : T_Z80_ADD op_a_n { out_AbsByte(0xC6); - out_RelByte(&$2); + out_RelByte(&$2, true); } | 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 T_COMMA reloc_8bit { out_AbsByte(0xE8); - out_RelByte(&$4); + out_RelByte(&$4, true); } ; z80_and : T_Z80_AND op_a_n { out_AbsByte(0xE6); - out_RelByte(&$2); + out_RelByte(&$2, true); } | T_Z80_AND op_a_r { out_AbsByte(0xA0 | $2); } ; @@ -1583,11 +1602,11 @@ z80_bit : T_Z80_BIT const_3bit T_COMMA reg_r { z80_call : T_Z80_CALL reloc_16bit { out_AbsByte(0xCD); - out_RelWord(&$2); + out_RelWord(&$2, true); } | T_Z80_CALL ccode T_COMMA reloc_16bit { out_AbsByte(0xC4 | ($2 << 3)); - out_RelWord(&$4); + out_RelWord(&$4, true); } ; @@ -1596,7 +1615,7 @@ z80_ccf : T_Z80_CCF { out_AbsByte(0x3F); } z80_cp : T_Z80_CP op_a_n { out_AbsByte(0xFE); - out_RelByte(&$2); + out_RelByte(&$2, true); } | T_Z80_CP op_a_r { out_AbsByte(0xB8 | $2); } ; @@ -1630,11 +1649,11 @@ z80_inc : T_Z80_INC reg_r { out_AbsByte(0x04 | ($2 << 3)); } z80_jp : T_Z80_JP reloc_16bit { out_AbsByte(0xC3); - out_RelWord(&$2); + out_RelWord(&$2, true); } | T_Z80_JP ccode T_COMMA reloc_16bit { out_AbsByte(0xC2 | ($2 << 3)); - out_RelWord(&$4); + out_RelWord(&$4, true); } | T_Z80_JP T_MODE_HL { out_AbsByte(0xE9); @@ -1643,11 +1662,11 @@ z80_jp : T_Z80_JP reloc_16bit { z80_jr : T_Z80_JR reloc_16bit { out_AbsByte(0x18); - out_PCRelByte(&$2); + out_PCRelByte(&$2, true); } | T_Z80_JR ccode T_COMMA reloc_16bit { out_AbsByte(0x20 | ($2 << 3)); - out_PCRelByte(&$4); + out_PCRelByte(&$4, true); } ; @@ -1671,13 +1690,13 @@ z80_ldio : T_Z80_LDH T_MODE_A T_COMMA op_mem_ind { rpn_CheckHRAM(&$4, &$4); out_AbsByte(0xF0); - out_RelByte(&$4); + out_RelByte(&$4, true); } | T_Z80_LDH op_mem_ind T_COMMA T_MODE_A { rpn_CheckHRAM(&$2, &$2); out_AbsByte(0xE0); - out_RelByte(&$2); + out_RelByte(&$2, true); } | T_Z80_LDH T_MODE_A T_COMMA c_ind { out_AbsByte(0xF2); @@ -1703,24 +1722,24 @@ z80_ld : z80_ld_mem z80_ld_hl : T_Z80_LD T_MODE_HL T_COMMA T_MODE_SP reloc_8bit { out_AbsByte(0xF8); - out_RelByte(&$5); + out_RelByte(&$5, true); } | T_Z80_LD T_MODE_HL T_COMMA reloc_16bit { out_AbsByte(0x01 | (REG_HL << 4)); - out_RelWord(&$4); + out_RelWord(&$4, true); } ; z80_ld_sp : T_Z80_LD T_MODE_SP T_COMMA T_MODE_HL { out_AbsByte(0xF9); } | T_Z80_LD T_MODE_SP T_COMMA reloc_16bit { out_AbsByte(0x01 | (REG_SP << 4)); - out_RelWord(&$4); + out_RelWord(&$4, true); } ; z80_ld_mem : T_Z80_LD op_mem_ind T_COMMA T_MODE_SP { out_AbsByte(0x08); - out_RelWord(&$2); + out_RelWord(&$2, true); } | T_Z80_LD op_mem_ind T_COMMA T_MODE_A { if (optimizeloads && rpn_isKnown(&$2) @@ -1730,7 +1749,7 @@ z80_ld_mem : T_Z80_LD op_mem_ind T_COMMA T_MODE_SP { rpn_Free(&$2); } else { out_AbsByte(0xEA); - out_RelWord(&$2); + out_RelWord(&$2, true); } } ; @@ -1747,7 +1766,7 @@ z80_ld_rr : T_Z80_LD reg_rr T_COMMA T_MODE_A { z80_ld_r : T_Z80_LD reg_r T_COMMA reloc_8bit { out_AbsByte(0x06 | ($2 << 3)); - out_RelByte(&$4); + out_RelByte(&$4, true); } | T_Z80_LD reg_r T_COMMA reg_r { if (($2 == REG_HL_IND) && ($4 == REG_HL_IND)) @@ -1778,7 +1797,7 @@ z80_ld_a : T_Z80_LD reg_r T_COMMA c_ind { rpn_Free(&$4); } else { out_AbsByte(0xFA); - out_RelWord(&$4); + out_RelWord(&$4, true); } } else { error("Destination operand must be A\n"); @@ -1789,11 +1808,11 @@ z80_ld_a : T_Z80_LD reg_r T_COMMA c_ind { z80_ld_ss : T_Z80_LD T_MODE_BC T_COMMA reloc_16bit { out_AbsByte(0x01 | (REG_BC << 4)); - out_RelWord(&$4); + out_RelWord(&$4, true); } | T_Z80_LD T_MODE_DE T_COMMA reloc_16bit { out_AbsByte(0x01 | (REG_DE << 4)); - out_RelWord(&$4); + out_RelWord(&$4, true); } /* * HL is taken care of in z80_ld_hl @@ -1806,7 +1825,7 @@ z80_nop : T_Z80_NOP { out_AbsByte(0x00); } z80_or : T_Z80_OR op_a_n { out_AbsByte(0xF6); - out_RelByte(&$2); + out_RelByte(&$2, true); } | T_Z80_OR op_a_r { out_AbsByte(0xB0 | $2); } ; @@ -1870,7 +1889,10 @@ z80_rrca : T_Z80_RRCA { out_AbsByte(0x0F); } z80_rst : T_Z80_RST reloc_8bit { rpn_CheckRST(&$2, &$2); if (!rpn_isKnown(&$2)) - out_RelByte(&$2); + // This could be considered as an "operand", but the purpose of the + // "operand" flag is to signal to RGBLINK to correct PC, + // which we don't want here. + out_RelByte(&$2, false); else out_AbsByte(0xC7 | $2.nVal); rpn_Free(&$2); @@ -1879,7 +1901,7 @@ z80_rst : T_Z80_RST reloc_8bit { z80_sbc : T_Z80_SBC op_a_n { out_AbsByte(0xDE); - out_RelByte(&$2); + out_RelByte(&$2, true); } | T_Z80_SBC op_a_r { out_AbsByte(0x98 | $2); } ; @@ -1917,13 +1939,13 @@ z80_stop : T_Z80_STOP { } | T_Z80_STOP reloc_8bit { out_AbsByte(0x10); - out_RelByte(&$2); + out_RelByte(&$2, true); } ; z80_sub : T_Z80_SUB op_a_n { out_AbsByte(0xD6); - out_RelByte(&$2); + out_RelByte(&$2, true); } | T_Z80_SUB op_a_r { out_AbsByte(0x90 | $2); } @@ -1937,7 +1959,7 @@ z80_swap : T_Z80_SWAP reg_r { z80_xor : T_Z80_XOR op_a_n { out_AbsByte(0xEE); - out_RelByte(&$2); + out_RelByte(&$2, true); } | T_Z80_XOR op_a_r { out_AbsByte(0xA8 | $2); } ; diff --git a/src/asm/rpn.c b/src/asm/rpn.c index 21bbc6c9..4c20af86 100644 --- a/src/asm/rpn.c +++ b/src/asm/rpn.c @@ -125,16 +125,6 @@ void rpn_Symbol(struct Expression *expr, char const *tzSym) uint8_t *ptr = reserveSpace(expr, nameLen + 1); *ptr++ = RPN_SYM; memcpy(ptr, sym->name, nameLen); - - /* RGBLINK assumes PC is at the byte being computed... */ - if (sym_IsPC(sym) && nPCOffset) { - struct Expression pc = *expr, offset; - - rpn_Number(&offset, nPCOffset); - rpn_BinaryOp(RPN_SUB, expr, &pc, &offset); - if (!rpn_isKnown(expr)) - expr->isSymbol = true; - } } else { rpn_Number(expr, sym_GetConstantValue(tzSym)); } diff --git a/src/asm/section.c b/src/asm/section.c index 3a6b5ed2..e77018ea 100644 --- a/src/asm/section.c +++ b/src/asm/section.c @@ -488,10 +488,9 @@ static inline void writelong(uint32_t b) writebyte(b >> 24); } -static inline void createPatch(enum PatchType type, - struct Expression const *expr) +static inline void createPatch(enum PatchType type, struct Expression const *expr, bool isOperand) { - out_CreatePatch(type, expr, sect_GetOutputOffset()); + out_CreatePatch(type, expr, sect_GetOutputOffset(), isOperand); } void sect_StartUnion(void) @@ -618,13 +617,13 @@ void out_String(char const *s) * Output a relocatable byte. Checking will be done to see if it * is an absolute value in disguise. */ -void out_RelByte(struct Expression *expr) +void out_RelByte(struct Expression *expr, bool isOperand) { checkcodesection(); reserveSpace(1); if (!rpn_isKnown(expr)) { - createPatch(PATCHTYPE_BYTE, expr); + createPatch(PATCHTYPE_BYTE, expr, isOperand); writebyte(0); } else { writebyte(expr->nVal); @@ -643,7 +642,7 @@ void out_RelBytes(struct Expression *expr, uint32_t n) while (n--) { if (!rpn_isKnown(expr)) { - createPatch(PATCHTYPE_BYTE, expr); + createPatch(PATCHTYPE_BYTE, expr, false); writebyte(0); } else { writebyte(expr->nVal); @@ -656,13 +655,13 @@ void out_RelBytes(struct Expression *expr, uint32_t n) * Output a relocatable word. Checking will be done to see if * it's an absolute value in disguise. */ -void out_RelWord(struct Expression *expr) +void out_RelWord(struct Expression *expr, bool isOperand) { checkcodesection(); reserveSpace(2); if (!rpn_isKnown(expr)) { - createPatch(PATCHTYPE_WORD, expr); + createPatch(PATCHTYPE_WORD, expr, isOperand); writeword(0); } else { writeword(expr->nVal); @@ -674,13 +673,13 @@ void out_RelWord(struct Expression *expr) * Output a relocatable longword. Checking will be done to see if * is an absolute value in disguise. */ -void out_RelLong(struct Expression *expr) +void out_RelLong(struct Expression *expr, bool isOperand) { checkcodesection(); reserveSpace(2); if (!rpn_isKnown(expr)) { - createPatch(PATCHTYPE_LONG, expr); + createPatch(PATCHTYPE_LONG, expr, isOperand); writelong(0); } else { writelong(expr->nVal); @@ -692,14 +691,14 @@ void out_RelLong(struct Expression *expr) * Output a PC-relative relocatable byte. Checking will be done to see if it * is an absolute value in disguise. */ -void out_PCRelByte(struct Expression *expr) +void out_PCRelByte(struct Expression *expr, bool isOperand) { checkcodesection(); reserveSpace(1); struct Symbol const *pc = sym_GetPC(); if (!rpn_IsDiffConstant(expr, pc)) { - createPatch(PATCHTYPE_JR, expr); + createPatch(PATCHTYPE_JR, expr, isOperand); writebyte(0); } else { struct Symbol const *sym = rpn_SymbolOf(expr); diff --git a/src/link/object.c b/src/link/object.c index dcc05824..fa5ffe6f 100644 --- a/src/link/object.c +++ b/src/link/object.c @@ -260,11 +260,11 @@ static void readSymbol(FILE *file, struct Symbol *symbol, * @param fileName The filename to report in errors * @param i The number of the patch to report in errors */ -static void readPatch(FILE *file, struct Patch *patch, char const *fileName, - char const *sectName, uint32_t i, - struct Section *fileSections[], struct FileStackNode fileNodes[]) +static void readPatch(FILE *file, struct Patch *patch, char const *fileName, char const *sectName, + uint32_t i, struct Section *fileSections[], struct FileStackNode fileNodes[]) { uint32_t nodeID; + uint8_t type; tryReadlong(nodeID, file, "%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s node ID: %s", @@ -279,14 +279,15 @@ static void readPatch(FILE *file, struct Patch *patch, char const *fileName, tryReadlong(patch->pcSectionID, file, "%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s PC offset: %s", fileName, sectName, i); - patch->pcSection = patch->pcSectionID == -1 ? NULL - : fileSections[patch->pcSectionID]; + patch->pcSection = patch->pcSectionID == -1 ? NULL : fileSections[patch->pcSectionID]; tryReadlong(patch->pcOffset, file, "%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s PC offset: %s", fileName, sectName, i); - tryGetc(patch->type, file, + tryGetc(type, file, "%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s type: %s", fileName, sectName, i); + patch->type = type & 0x7F; + patch->isOperand = type & PATCH_ISOPERAND; tryReadlong(patch->rpnSize, file, "%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s RPN size: %s", fileName, sectName, i); diff --git a/src/link/patch.c b/src/link/patch.c index 86f85f14..1ded0d8f 100644 --- a/src/link/patch.c +++ b/src/link/patch.c @@ -421,6 +421,10 @@ static int32_t computeRPNExpr(struct Patch const *patch, isError = true; } else { value = patch->pcOffset + patch->pcSection->org; + // If the patch is an operand, PC is not at the patch's + // location, but at the (opcode) byte right before it + if (patch->isOperand) + value--; } } else { symbol = getSymbol(fileSymbols, value); diff --git a/test/asm/block-comment-termination-error.err b/test/asm/block-comment-termination-error.err index f5e82102..e7641ede 100644 --- a/test/asm/block-comment-termination-error.err +++ b/test/asm/block-comment-termination-error.err @@ -1,5 +1,5 @@ ERROR: block-comment-termination-error.asm(1): Unterminated block comment ERROR: block-comment-termination-error.asm(1): - syntax error, unexpected newline + syntax error, unexpected end of file error: Assembly aborted (2 errors)! diff --git a/test/asm/macro-eof.asm b/test/asm/macro-eof.asm new file mode 100644 index 00000000..f5e1e1ea --- /dev/null +++ b/test/asm/macro-eof.asm @@ -0,0 +1,6 @@ +; Macro invocations not followed by a newline may scan an EOF; +; If this is the case, it shouldn't cause the parse to end before the macro is expanded +mac: macro + PRINTLN "Hi beautiful" +endm + mac \ No newline at end of file diff --git a/test/asm/macro-eof.err b/test/asm/macro-eof.err new file mode 100644 index 00000000..e69de29b diff --git a/test/asm/macro-eof.out b/test/asm/macro-eof.out new file mode 100644 index 00000000..b7c1527b --- /dev/null +++ b/test/asm/macro-eof.out @@ -0,0 +1 @@ +Hi beautiful diff --git a/test/asm/macro-recursion.asm b/test/asm/macro-recursion.asm index 25f854e9..d22bc847 100644 --- a/test/asm/macro-recursion.asm +++ b/test/asm/macro-recursion.asm @@ -1,4 +1,4 @@ recurse: MACRO recurse ENDM - recurse \ No newline at end of file + recurse diff --git a/test/asm/null-in-macro.asm b/test/asm/null-in-macro.asm index 944922e8..cace59bf 100644 Binary files a/test/asm/null-in-macro.asm and b/test/asm/null-in-macro.asm differ diff --git a/test/asm/pc-operand.asm b/test/asm/pc-operand.asm index 88175c04..c3b7d3b6 100644 --- a/test/asm/pc-operand.asm +++ b/test/asm/pc-operand.asm @@ -3,11 +3,13 @@ SECTION "fixed", ROM0[0] rst @ ; rst 0 ld de, @ ; ld de, 1 bit @, h ; bit 4, h - db @, @ ; db 6, 7 + jr @ ; jr 6 SECTION "floating", ROM0 rst @ ; rst 8 ld l, @ ; ld l, 9 - dw @, @ ; dw 11, 13 - dl @, @ ; dl 15, 19 + db @, @ ; db 11, 12 + dw @, @ ; dw 13, 15 + dl @, @ ; dl 17, 21 + jr @ ; jr 25 diff --git a/test/asm/pc-operand.out.bin b/test/asm/pc-operand.out.bin index 7c06a3d5..77171a7e 100644 Binary files a/test/asm/pc-operand.out.bin and b/test/asm/pc-operand.out.bin differ