From b3c0db218d564bbc9a801d8635591fd969c36e82 Mon Sep 17 00:00:00 2001 From: ISSOtm Date: Thu, 11 Feb 2021 12:48:37 +0100 Subject: [PATCH] Remove "EOF-newline" lexer hack In preparation for an upcoming change Makes for nicer error messages, complaining about EOF instead of newlines The hack had to be kept for the lexer raw mode to avoid a bug; see the relevant code comment for more info. --- include/asm/output.h | 3 +- include/asm/section.h | 8 +- include/link/section.h | 1 + include/linkdefs.h | 2 + src/asm/lexer.c | 33 ++-- src/asm/output.c | 11 +- src/asm/parser.y | 164 +++++++++++-------- src/asm/rpn.c | 10 -- src/asm/section.c | 23 ++- src/link/object.c | 13 +- src/link/patch.c | 4 + test/asm/block-comment-termination-error.err | 2 +- test/asm/macro-eof.asm | 6 + test/asm/macro-eof.err | 0 test/asm/macro-eof.out | 1 + test/asm/macro-recursion.asm | 2 +- test/asm/null-in-macro.asm | Bin 23 -> 24 bytes test/asm/pc-operand.asm | 8 +- test/asm/pc-operand.out.bin | Bin 23 -> 27 bytes 19 files changed, 160 insertions(+), 131 deletions(-) create mode 100644 test/asm/macro-eof.asm create mode 100644 test/asm/macro-eof.err create mode 100644 test/asm/macro-eof.out 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 944922e8d78e4c4b242bfc9ae2c3e1fc4c15b35b..cace59bfee5d2f11e11a9de3c1c451cb9cf67f26 100644 GIT binary patch delta 6 NcmWfam>|x`1pot?0O9}u delta 4 Lcmb0TpCAqZ0pI}F 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 7c06a3d5df4320fb6947ea697761bab2a2c0a026..77171a7ed8cd33b48f3aa2555116618740bea12a 100644 GIT binary patch literal 27 hcmX>u$jERyMdIIiJx*>OUIu;!K?Vi}Q6QH12LNB@1(W~) literal 23 dcmX>u$jERyg^m5Z9w#>gF9SaV1A{OS0{}Z#16BY4