ref: 01637768cf19aedf85882b0d9f53284299239037
parent: 6a8ae643d5e900b2dbe7d741a1f14df5912cede8
author: ISSOtm <[email protected]>
date: Sun Oct 11 17:03:41 EDT 2020
Rename `asmy` to more explicit `parser` This should make the purpose of that file clearer to newcomers
--- a/Makefile
+++ b/Makefile
@@ -53,7 +53,6 @@
all: rgbasm rgblink rgbfix rgbgfx
rgbasm_obj := \
- src/asm/asmy.o \
src/asm/charmap.o \
src/asm/fstack.o \
src/asm/lexer.o \
@@ -60,6 +59,7 @@
src/asm/macro.o \
src/asm/main.o \
src/asm/math.o \
+ src/asm/parser.o \
src/asm/output.o \
src/asm/rpn.o \
src/asm/section.o \
@@ -72,7 +72,7 @@
src/hashmap.o \
src/linkdefs.o
-src/asm/lexer.o src/asm/main.o: src/asm/asmy.h
+src/asm/lexer.o src/asm/main.o: src/asm/parser.h
rgblink_obj := \
src/link/assign.o \
--- a/include/asm/rpn.h
+++ b/include/asm/rpn.h
@@ -27,7 +27,7 @@
uint32_t nRPNPatchSize; // Size the expression will take in the obj file
};
-/* FIXME: Should be defined in `asmy.h`, but impossible with POSIX Yacc */
+/* FIXME: Should be defined in `parser.h`, but impossible with POSIX Yacc */
extern int32_t nPCOffset;
/*
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -22,13 +22,13 @@
pkg_check_modules(LIBPNG REQUIRED libpng)
endif()
-BISON_TARGET(ASMy "asm/asmy.y"
- "${PROJECT_SOURCE_DIR}/src/asm/asmy.c"
- DEFINES_FILE "${PROJECT_SOURCE_DIR}/src/asm/asmy.h"
+BISON_TARGET(PARSER "asm/parser.y"
+ "${PROJECT_SOURCE_DIR}/src/asm/parser.c"
+ DEFINES_FILE "${PROJECT_SOURCE_DIR}/src/asm/parser.h"
)
set(rgbasm_src
- "${BISON_ASMy_OUTPUT_SOURCE}"
+ "${BISON_PARSER_OUTPUT_SOURCE}"
"asm/charmap.c"
"asm/fstack.c"
"asm/lexer.c"
--- a/src/asm/.gitignore
+++ b/src/asm/.gitignore
@@ -1,2 +1,2 @@
-asmy.c
-asmy.h
+/parser.c
+/parser.h
--- a/src/asm/asmy.y
+++ /dev/null
@@ -1,1631 +1,0 @@
-/*
- * This file is part of RGBDS.
- *
- * Copyright (c) 1997-2019, Carsten Sorensen and RGBDS contributors.
- *
- * SPDX-License-Identifier: MIT
- */
-
-%{
-#include <ctype.h>
-#include <errno.h>
-#include <inttypes.h>
-#include <stdbool.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "asm/asm.h"
-#include "asm/charmap.h"
-#include "asm/fstack.h"
-#include "asm/lexer.h"
-#include "asm/macro.h"
-#include "asm/main.h"
-#include "asm/mymath.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
-
-uint32_t nListCountEmpty;
-char *tzNewMacro;
-uint32_t ulNewMacroSize;
-int32_t nPCOffset;
-bool executeElseBlock; /* If this is set, ELIFs cannot be executed anymore */
-
-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 size_t strlenUTF8(const char *s)
-{
- size_t len = 0;
- uint32_t state = 0;
- uint32_t codep = 0;
-
- while (*s) {
- switch (decode(&state, &codep, (uint8_t)*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, 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, (uint8_t)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 < MAXSTRLEN && curLen < len) {
- switch (decode(&state, &codep, (uint8_t)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 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;
- }
-}
-
-#define yyerror(str) error(str "\n")
-
-%}
-
-%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;
-}
-
-%type <sVal> relocexpr
-%type <sVal> relocexpr_no_str
-%type <nConstValue> const
-%type <nConstValue> uconst
-%type <nConstValue> const_3bit
-%type <sVal> reloc_8bit
-%type <sVal> reloc_8bit_no_str
-%type <sVal> reloc_16bit
-%type <nConstValue> sectiontype
-
-%type <tzString> string
-
-%type <nConstValue> sectorg
-%type <sectSpec> sectattrs
-
-%token <nConstValue> T_NUMBER
-%token <tzString> T_STRING
-
-%left T_OP_LOGICNOT
-%left T_OP_LOGICOR 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
-%left T_OP_NOT
-%left T_OP_DEF
-%left T_OP_BANK T_OP_ALIGN
-%left T_OP_SIN
-%left T_OP_COS
-%left T_OP_TAN
-%left T_OP_ASIN
-%left T_OP_ACOS
-%left T_OP_ATAN
-%left T_OP_ATAN2
-%left T_OP_FDIV
-%left T_OP_FMUL
-%left T_OP_ROUND
-%left T_OP_CEIL
-%left T_OP_FLOOR
-
-%token T_OP_HIGH T_OP_LOW
-%token T_OP_ISCONST
-
-%left T_OP_STRCMP
-%left T_OP_STRIN
-%left T_OP_STRSUB
-%left T_OP_STRLEN
-%left T_OP_STRCAT
-%left T_OP_STRUPR
-%left T_OP_STRLWR
-
-%left NEG /* negation -- unary minus */
-
-%token <tzSym> T_LABEL
-%token <tzSym> T_ID
-%token <tzSym> T_LOCAL_ID
-%type <tzSym> scoped_id
-%token T_POP_EQU
-%token T_POP_SET
-%token T_POP_EQUAL
-%token T_POP_EQUS
-
-%token T_POP_INCLUDE T_POP_PRINTF T_POP_PRINTT T_POP_PRINTV T_POP_PRINTI
-%token T_POP_IF T_POP_ELIF T_POP_ELSE T_POP_ENDC
-%token T_POP_EXPORT T_POP_GLOBAL T_POP_XDEF
-%token T_POP_DB T_POP_DS T_POP_DW T_POP_DL
-%token T_POP_SECTION T_POP_FRAGMENT
-%token T_POP_RB
-%token T_POP_RW
-%token T_POP_RL
-%token T_POP_MACRO
-%token T_POP_ENDM
-%token T_POP_RSRESET T_POP_RSSET
-%token T_POP_UNION T_POP_NEXTU T_POP_ENDU
-%token T_POP_INCBIN T_POP_REPT
-%token T_POP_CHARMAP
-%token T_POP_NEWCHARMAP
-%token T_POP_SETCHARMAP
-%token T_POP_PUSHC
-%token T_POP_POPC
-%token T_POP_SHIFT
-%token T_POP_ENDR
-%token T_POP_LOAD T_POP_ENDL
-%token T_POP_FAIL
-%token T_POP_WARN
-%token T_POP_FATAL
-%token T_POP_ASSERT T_POP_STATIC_ASSERT
-%token T_POP_PURGE
-%token T_POP_POPS
-%token T_POP_PUSHS
-%token T_POP_POPO
-%token T_POP_PUSHO
-%token T_POP_OPT
-%token T_SECT_WRAM0 T_SECT_VRAM T_SECT_ROMX T_SECT_ROM0 T_SECT_HRAM
-%token T_SECT_WRAMX T_SECT_SRAM T_SECT_OAM
-
-%type <sectMod> sectmod
-%type <macroArg> macroargs
-
-%token T_Z80_ADC T_Z80_ADD T_Z80_AND
-%token T_Z80_BIT
-%token T_Z80_CALL T_Z80_CCF T_Z80_CP T_Z80_CPL
-%token T_Z80_DAA T_Z80_DEC T_Z80_DI
-%token T_Z80_EI
-%token T_Z80_HALT
-%token T_Z80_INC
-%token T_Z80_JP T_Z80_JR
-%token T_Z80_LD
-%token T_Z80_LDI
-%token T_Z80_LDD
-%token T_Z80_LDIO
-%token T_Z80_NOP
-%token T_Z80_OR
-%token T_Z80_POP T_Z80_PUSH
-%token T_Z80_RES T_Z80_RET T_Z80_RETI T_Z80_RST
-%token T_Z80_RL T_Z80_RLA T_Z80_RLC T_Z80_RLCA
-%token T_Z80_RR T_Z80_RRA T_Z80_RRC T_Z80_RRCA
-%token T_Z80_SBC T_Z80_SCF T_Z80_STOP
-%token T_Z80_SLA T_Z80_SRA T_Z80_SRL T_Z80_SUB T_Z80_SWAP
-%token T_Z80_XOR
-
-%token T_TOKEN_A T_TOKEN_B T_TOKEN_C T_TOKEN_D T_TOKEN_E T_TOKEN_H T_TOKEN_L
-%token T_MODE_AF
-%token T_MODE_BC
-%token T_MODE_DE
-%token T_MODE_SP
-%token T_MODE_HW_C
-%token T_MODE_HL T_MODE_HL_DEC T_MODE_HL_INC
-%token T_CC_NZ T_CC_Z T_CC_NC
-
-%type <nConstValue> reg_r
-%type <nConstValue> reg_ss
-%type <nConstValue> reg_rr
-%type <nConstValue> reg_tt
-%type <nConstValue> ccode
-%type <sVal> op_a_n
-%type <nConstValue> op_a_r
-%type <nConstValue> op_hl_ss
-%type <sVal> op_mem_ind
-%type <assertType> assert_type
-%start asmfile
-
-%%
-
-asmfile : lines;
-
-/* Note: The lexer adds '\n' at the end of the input */
-lines : /* empty */
- | lines {
- nListCountEmpty = 0;
- nPCOffset = 0;
- } line {
- nTotalLines++;
- }
-;
-
-line : label '\n'
- | label cpu_command '\n'
- | label macro '\n'
- | label simple_pseudoop '\n'
- | pseudoop '\n'
- | conditional /* May not necessarily be followed by a newline, see below */
-;
-
-/*
- * For "logistical" reasons, conditionals 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.
- */
-conditional : if
- /* It's important that all of these require being at line start for `skipIfBlock` */
- | elif
- | else
- | endc
-;
-
-if : T_POP_IF const '\n' {
- nIFDepth++;
- executeElseBlock = !$2;
- if (executeElseBlock)
- lexer_SetMode(LEXER_SKIP_TO_ELIF);
- }
-;
-
-elif : T_POP_ELIF const '\n' {
- if (nIFDepth <= 0)
- fatalerror("Found ELIF outside an IF construct\n");
-
- if (!executeElseBlock) {
- lexer_SetMode(LEXER_SKIP_TO_ENDC);
- } else {
- executeElseBlock = !$2;
- if (executeElseBlock)
- lexer_SetMode(LEXER_SKIP_TO_ELIF);
- }
- }
-;
-
-else : T_POP_ELSE '\n' {
- if (nIFDepth <= 0)
- fatalerror("Found ELSE outside an IF construct\n");
-
- if (!executeElseBlock)
- lexer_SetMode(LEXER_SKIP_TO_ENDC);
- }
-;
-
-endc : T_POP_ENDC '\n' {
- if (nIFDepth <= 0)
- fatalerror("Found ENDC outside an IF construct\n");
-
- nIFDepth--;
- executeElseBlock = false;
- }
-;
-
-scoped_id : T_ID | T_LOCAL_ID ;
-
-label : /* empty */
- | T_LOCAL_ID {
- sym_AddLocalLabel($1);
- }
- | T_LABEL {
- warning(WARNING_OBSOLETE, "Non-local labels without a colon are deprecated\n");
- sym_AddLabel($1);
- }
- | T_LOCAL_ID ':' {
- sym_AddLocalLabel($1);
- }
- | T_LABEL ':' {
- sym_AddLabel($1);
- }
- | T_LOCAL_ID ':' ':' {
- sym_AddLocalLabel($1);
- sym_Export($1);
- }
- | T_LABEL ':' ':' {
- sym_AddLabel($1);
- sym_Export($1);
- }
-;
-
-macro : T_ID {
- lexer_SetMode(LEXER_RAW);
- } macroargs {
- lexer_SetMode(LEXER_NORMAL);
- fstk_RunMacro($1, $3);
- }
-;
-
-macroargs : /* empty */ {
- $$ = macro_NewArgs();
- }
- | T_STRING {
- $$ = macro_NewArgs();
- macro_AppendArg(&($$), strdup($1));
- }
- | macroargs ',' T_STRING {
- macro_AppendArg(&($$), strdup($3));
- }
-;
-
-pseudoop : equ
- | set
- | rb
- | rw
- | rl
- | equs
- | macrodef
-;
-
-simple_pseudoop : include
- | printf
- | printt
- | printv
- | printi
- | export
- | db
- | dw
- | dl
- | ds
- | section
- | rsreset
- | rsset
- | union
- | nextu
- | endu
- | incbin
- | charmap
- | newcharmap
- | setcharmap
- | pushc
- | popc
- | load
- | rept
- | shift
- | fail
- | warn
- | assert
- | purge
- | pops
- | pushs
- | popo
- | pusho
- | opt
- | align
-;
-
-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 ',' 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 {
- lexer_SetMode(LEXER_RAW);
- } opt_list {
- lexer_SetMode(LEXER_NORMAL);
- }
-;
-
-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 ',' { $$ = ASSERT_WARN; }
- | T_POP_FAIL ',' { $$ = ASSERT_ERROR; }
- | T_POP_FATAL ',' { $$ = 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 ',' 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 ',' string
- {
- if ($3 == 0)
- failAssertMsg($2, $5);
- }
-;
-
-shift : T_POP_SHIFT { macro_ShiftCurrentArgs(); }
- | T_POP_SHIFT uconst
- {
- int32_t i = $2;
- while (i--)
- macro_ShiftCurrentArgs();
- }
-;
-
-load : T_POP_LOAD string ',' sectiontype sectorg sectattrs {
- out_SetLoadSection($2, $4, $5, &$6);
- }
- | T_POP_ENDL { out_EndLoadSection(); }
-;
-
-rept : T_POP_REPT uconst {
- uint32_t nDefinitionLineNo = lexer_GetLineNo();
- char *body;
- size_t size;
- lexer_CaptureRept(&body, &size);
- fstk_RunRept($2, nDefinitionLineNo, body, size);
- }
-;
-
-macrodef : T_LABEL ':' T_POP_MACRO {
- int32_t nDefinitionLineNo = lexer_GetLineNo();
- char *body;
- size_t size;
- lexer_CaptureMacroBody(&body, &size);
- sym_AddMacro($1, nDefinitionLineNo, body, size);
- }
-;
-
-equs : T_LABEL T_POP_EQUS string { sym_AddString($1, $3); }
-;
-
-rsset : T_POP_RSSET uconst { sym_AddSet("_RS", $2); }
-;
-
-rsreset : T_POP_RSRESET { sym_AddSet("_RS", 0); }
-;
-
-rl : T_LABEL T_POP_RL uconst {
- sym_AddEqu($1, sym_GetConstantValue("_RS"));
- sym_AddSet("_RS", sym_GetConstantValue("_RS") + 4 * $3);
- }
-;
-
-rw : T_LABEL T_POP_RW uconst {
- sym_AddEqu($1, sym_GetConstantValue("_RS"));
- sym_AddSet("_RS", sym_GetConstantValue("_RS") + 2 * $3);
- }
-;
-
-rb : T_LABEL T_POP_RB uconst {
- sym_AddEqu($1, sym_GetConstantValue("_RS"));
- sym_AddSet("_RS", sym_GetConstantValue("_RS") + $3);
- }
-;
-
-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 ',' reloc_8bit {
- out_RelBytes(&$4, $2);
- }
-;
-
-/* Authorize empty entries if there is only one */
-db : T_POP_DB constlist_8bit_entry ',' constlist_8bit {
- if (nListCountEmpty > 0)
- warning(WARNING_EMPTY_ENTRY,
- "Empty entry in list of 8-bit elements (treated as padding).\n");
- }
- | T_POP_DB constlist_8bit_entry
-;
-
-dw : T_POP_DW constlist_16bit_entry ',' constlist_16bit {
- if (nListCountEmpty > 0)
- warning(WARNING_EMPTY_ENTRY,
- "Empty entry in list of 16-bit elements (treated as padding).\n");
- }
- | T_POP_DW constlist_16bit_entry
-;
-
-dl : T_POP_DL constlist_32bit_entry ',' constlist_32bit {
- if (nListCountEmpty > 0)
- warning(WARNING_EMPTY_ENTRY,
- "Empty entry in list of 32-bit elements (treated as padding).\n");
- }
- | T_POP_DL constlist_32bit_entry
-;
-
-purge : T_POP_PURGE {
- lexer_ToggleStringExpansion(false);
- } purge_list {
- lexer_ToggleStringExpansion(true);
- }
-;
-
-purge_list : purge_list_entry
- | purge_list ',' purge_list_entry
-;
-
-purge_list_entry : scoped_id { sym_Purge($1); }
-;
-
-export : export_token export_list
-;
-
-export_token : T_POP_EXPORT
- | T_POP_GLOBAL {
- warning(WARNING_OBSOLETE,
- "`GLOBAL` is a deprecated synonym for `EXPORT`\n");
- }
- | T_POP_XDEF {
- warning(WARNING_OBSOLETE, "`XDEF` is a deprecated synonym for `EXPORT`\n");
- }
-;
-
-export_list : export_list_entry
- | export_list ',' export_list_entry
-;
-
-export_list_entry : scoped_id { sym_Export($1); }
-;
-
-equ : T_LABEL T_POP_EQU const { sym_AddEqu($1, $3); }
-;
-
-set : T_LABEL T_POP_SET const { sym_AddSet($1, $3); }
- | T_LABEL T_POP_EQUAL const { sym_AddSet($1, $3); }
-;
-
-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 ',' const {
- out_BinaryFile($2, $4);
- if (oFailedOnMissingInclude)
- YYACCEPT;
- }
- | T_POP_INCBIN string ',' const ',' const {
- out_BinaryFileSlice($2, $4, $6);
- if (oFailedOnMissingInclude)
- YYACCEPT;
- }
-;
-
-charmap : T_POP_CHARMAP string ',' 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_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(); }
-;
-
-printt : T_POP_PRINTT string { printf("%s", $2); }
-;
-
-printv : T_POP_PRINTV const { printf("$%" PRIX32, $2); }
-;
-
-printi : T_POP_PRINTI const { printf("%" PRId32, $2); }
-;
-
-printf : T_POP_PRINTF const { math_Print($2); }
-;
-
-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 ',' constlist_8bit_entry
-;
-
-constlist_8bit_entry : /* empty */ {
- out_Skip(1, false);
- nListCountEmpty++;
- }
- | 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);
- }
-;
-
-constlist_16bit : constlist_16bit_entry
- | constlist_16bit ',' constlist_16bit_entry
-;
-
-constlist_16bit_entry : /* empty */ {
- out_Skip(2, false);
- nListCountEmpty++;
- }
- | reloc_16bit { out_RelWord(&$1); }
-;
-
-constlist_32bit : constlist_32bit_entry
- | constlist_32bit ',' constlist_32bit_entry
-;
-
-constlist_32bit_entry : /* empty */ {
- out_Skip(4, false);
- nListCountEmpty++;
- }
- | relocexpr { out_RelLong(&$1); }
-;
-
-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;
- }
-;
-
-
-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_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);
- }
- | 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 '(' relocexpr ')' { rpn_HIGH(&$$, &$3); }
- | T_OP_LOW '(' relocexpr ')' { rpn_LOW(&$$, &$3); }
- | T_OP_ISCONST '(' relocexpr ')'{ rpn_ISCONST(&$$, &$3); }
- | T_OP_BANK '(' scoped_id ')' {
- /* '@' is also a T_ID, it is handled here. */
- rpn_BankSymbol(&$$, $3);
- }
- | T_OP_BANK '(' string ')' { rpn_BankSection(&$$, $3); }
- | T_OP_DEF {
- lexer_ToggleStringExpansion(false);
- } '(' scoped_id ')' {
- struct Symbol const *sym = sym_FindSymbol($4);
-
- rpn_Number(&$$, !!sym);
-
- lexer_ToggleStringExpansion(true);
- }
- | T_OP_ROUND '(' const ')' {
- rpn_Number(&$$, math_Round($3));
- }
- | T_OP_CEIL '(' const ')' {
- rpn_Number(&$$, math_Ceil($3));
- }
- | T_OP_FLOOR '(' const ')' {
- rpn_Number(&$$, math_Floor($3));
- }
- | T_OP_FDIV '(' const ',' const ')' {
- rpn_Number(&$$, math_Div($3, $5));
- }
- | T_OP_FMUL '(' const ',' const ')' {
- rpn_Number(&$$, math_Mul($3, $5));
- }
- | T_OP_SIN '(' const ')' {
- rpn_Number(&$$, math_Sin($3));
- }
- | T_OP_COS '(' const ')' {
- rpn_Number(&$$, math_Cos($3));
- }
- | T_OP_TAN '(' const ')' {
- rpn_Number(&$$, math_Tan($3));
- }
- | T_OP_ASIN '(' const ')' {
- rpn_Number(&$$, math_ASin($3));
- }
- | T_OP_ACOS '(' const ')' {
- rpn_Number(&$$, math_ACos($3));
- }
- | T_OP_ATAN '(' const ')' {
- rpn_Number(&$$, math_ATan($3));
- }
- | T_OP_ATAN2 '(' const ',' const ')' {
- rpn_Number(&$$, math_ATan2($3, $5));
- }
- | T_OP_STRCMP '(' string ',' string ')' {
- rpn_Number(&$$, strcmp($3, $5));
- }
- | T_OP_STRIN '(' string ',' string ')' {
- char *p = strstr($3, $5);
-
- rpn_Number(&$$, p ? p - $3 + 1 : 0);
- }
- | T_OP_STRLEN '(' string ')' {
- rpn_Number(&$$, strlenUTF8($3));
- }
- | '(' relocexpr ')' { $$ = $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;
- }
- }
-;
-
-string : T_STRING {
- if (snprintf($$, MAXSTRLEN + 1, "%s", $1) > MAXSTRLEN)
- warning(WARNING_LONG_STR, "String is too long '%s'\n", $1);
- }
- | T_OP_STRSUB '(' string ',' uconst ',' uconst ')' {
- strsubUTF8($$, $3, $5, $7);
- }
- | T_OP_STRCAT '(' string ',' string ')' {
- if (snprintf($$, MAXSTRLEN + 1, "%s%s", $3, $5) > MAXSTRLEN)
- warning(WARNING_LONG_STR, "STRCAT: String too long '%s%s'\n",
- $3, $5);
- }
- | T_OP_STRUPR '(' string ')' {
- if (snprintf($$, MAXSTRLEN + 1, "%s", $3) > MAXSTRLEN)
- warning(WARNING_LONG_STR, "STRUPR: String too long '%s'\n", $3);
-
- upperstring($$);
- }
- | T_OP_STRLWR '(' string ')' {
- if (snprintf($$, MAXSTRLEN + 1, "%s", $3) > MAXSTRLEN)
- warning(WARNING_LONG_STR, "STRUPR: String too long '%s'\n", $3);
-
- lowerstring($$);
- }
-;
-
-section : T_POP_SECTION sectmod string ',' 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; }
- | '[' uconst ']' {
- 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_OP_ALIGN '[' uconst ']' {
- if ($5 > 16)
- error("Alignment must be between 0 and 16, not %u\n", $5);
- else
- $$.alignment = $5;
- }
- | sectattrs ',' T_OP_ALIGN '[' uconst ',' uconst ']' {
- if ($5 > 16) {
- error("Alignment must be between 0 and 16, not %u\n", $5);
- } else {
- $$.alignment = $5;
- if ($7 >= 1 << $$.alignment)
- error("Alignment offset must not be greater than alignment (%u < %u)\n",
- $7, 1 << $$.alignment);
- else
- $$.alignOfs = $7;
- }
- }
- | sectattrs ',' T_OP_BANK '[' uconst ']' {
- /* We cannot check the validity of this now */
- $$.bank = $5;
- }
-;
-
-
-cpu_command : { nPCOffset = 1; } z80_adc
- | { nPCOffset = 1; } z80_add
- | { nPCOffset = 1; } z80_and
- | { nPCOffset = 1; } z80_bit
- | { nPCOffset = 1; } z80_call
- | z80_ccf
- | { nPCOffset = 1; } z80_cp
- | z80_cpl
- | z80_daa
- | { nPCOffset = 1; } z80_dec
- | z80_di
- | z80_ei
- | z80_halt
- | z80_inc
- | { nPCOffset = 1; } z80_jp
- | { nPCOffset = 1; } z80_jr
- | { nPCOffset = 1; } z80_ld
- | z80_ldd
- | z80_ldi
- | { nPCOffset = 1; } z80_ldio
- | z80_nop
- | { nPCOffset = 1; } z80_or
- | z80_pop
- | z80_push
- | { nPCOffset = 1; } z80_res
- | z80_ret
- | z80_reti
- | z80_rl
- | z80_rla
- | z80_rlc
- | z80_rlca
- | z80_rr
- | z80_rra
- | z80_rrc
- | z80_rrca
- | /*{ nPCOffset = 0; }*/ z80_rst
- | { nPCOffset = 1; } z80_sbc
- | z80_scf
- | { nPCOffset = 1; } z80_set
- | z80_sla
- | z80_sra
- | z80_srl
- | { nPCOffset = 1; } z80_stop
- | { nPCOffset = 1; } z80_sub
- | z80_swap
- | { nPCOffset = 1; } z80_xor
-;
-
-z80_adc : T_Z80_ADC op_a_n {
- out_AbsByte(0xCE);
- out_RelByte(&$2);
- }
- | T_Z80_ADC op_a_r { out_AbsByte(0x88 | $2); }
-;
-
-z80_add : T_Z80_ADD op_a_n {
- out_AbsByte(0xC6);
- out_RelByte(&$2);
- }
- | T_Z80_ADD op_a_r { out_AbsByte(0x80 | $2); }
- | T_Z80_ADD op_hl_ss { out_AbsByte(0x09 | ($2 << 4)); }
- | T_Z80_ADD T_MODE_SP ',' reloc_8bit {
- out_AbsByte(0xE8);
- out_RelByte(&$4);
- }
-
-;
-
-z80_and : T_Z80_AND op_a_n {
- out_AbsByte(0xE6);
- out_RelByte(&$2);
- }
- | T_Z80_AND op_a_r { out_AbsByte(0xA0 | $2); }
-;
-
-z80_bit : T_Z80_BIT const_3bit ',' reg_r {
- out_AbsByte(0xCB);
- out_AbsByte(0x40 | ($2 << 3) | $4);
- }
-;
-
-z80_call : T_Z80_CALL reloc_16bit {
- out_AbsByte(0xCD);
- out_RelWord(&$2);
- }
- | T_Z80_CALL ccode ',' reloc_16bit {
- out_AbsByte(0xC4 | ($2 << 3));
- out_RelWord(&$4);
- }
-;
-
-z80_ccf : T_Z80_CCF { out_AbsByte(0x3F); }
-;
-
-z80_cp : T_Z80_CP op_a_n {
- out_AbsByte(0xFE);
- out_RelByte(&$2);
- }
- | T_Z80_CP op_a_r { out_AbsByte(0xB8 | $2); }
-;
-
-z80_cpl : T_Z80_CPL { out_AbsByte(0x2F); }
-;
-
-z80_daa : T_Z80_DAA { out_AbsByte(0x27); }
-;
-
-z80_dec : T_Z80_DEC reg_r { out_AbsByte(0x05 | ($2 << 3)); }
- | T_Z80_DEC reg_ss { out_AbsByte(0x0B | ($2 << 4)); }
-;
-
-z80_di : T_Z80_DI { out_AbsByte(0xF3); }
-;
-
-z80_ei : T_Z80_EI { out_AbsByte(0xFB); }
-;
-
-z80_halt : T_Z80_HALT {
- out_AbsByte(0x76);
- if (haltnop)
- out_AbsByte(0x00);
- }
-;
-
-z80_inc : T_Z80_INC reg_r { out_AbsByte(0x04 | ($2 << 3)); }
- | T_Z80_INC reg_ss { out_AbsByte(0x03 | ($2 << 4)); }
-;
-
-z80_jp : T_Z80_JP reloc_16bit {
- out_AbsByte(0xC3);
- out_RelWord(&$2);
- }
- | T_Z80_JP ccode ',' reloc_16bit {
- out_AbsByte(0xC2 | ($2 << 3));
- out_RelWord(&$4);
- }
- | T_Z80_JP T_MODE_HL {
- out_AbsByte(0xE9);
- }
-;
-
-z80_jr : T_Z80_JR reloc_16bit {
- out_AbsByte(0x18);
- out_PCRelByte(&$2);
- }
- | T_Z80_JR ccode ',' reloc_16bit {
- out_AbsByte(0x20 | ($2 << 3));
- out_PCRelByte(&$4);
- }
-;
-
-z80_ldi : T_Z80_LDI '[' T_MODE_HL ']' ',' T_MODE_A {
- out_AbsByte(0x02 | (2 << 4));
- }
- | T_Z80_LDI T_MODE_A ',' '[' T_MODE_HL ']' {
- out_AbsByte(0x0A | (2 << 4));
- }
-;
-
-z80_ldd : T_Z80_LDD '[' T_MODE_HL ']' ',' T_MODE_A {
- out_AbsByte(0x02 | (3 << 4));
- }
- | T_Z80_LDD T_MODE_A ',' '[' T_MODE_HL ']' {
- out_AbsByte(0x0A | (3 << 4));
- }
-;
-
-z80_ldio : T_Z80_LDIO T_MODE_A ',' op_mem_ind {
- rpn_CheckHRAM(&$4, &$4);
-
- out_AbsByte(0xF0);
- out_RelByte(&$4);
- }
- | T_Z80_LDIO op_mem_ind ',' T_MODE_A {
- rpn_CheckHRAM(&$2, &$2);
-
- out_AbsByte(0xE0);
- out_RelByte(&$2);
- }
- | T_Z80_LDIO T_MODE_A ',' c_ind {
- out_AbsByte(0xF2);
- }
- | T_Z80_LDIO c_ind ',' T_MODE_A {
- out_AbsByte(0xE2);
- }
-;
-
-c_ind : '[' T_MODE_C ']'
- | '[' T_MODE_HW_C ']'
-;
-
-z80_ld : z80_ld_mem
- | z80_ld_cind
- | z80_ld_rr
- | z80_ld_ss
- | z80_ld_hl
- | z80_ld_sp
- | z80_ld_r
- | z80_ld_a
-;
-
-z80_ld_hl : T_Z80_LD T_MODE_HL ',' T_MODE_SP reloc_8bit {
- out_AbsByte(0xF8);
- out_RelByte(&$5);
- }
- | T_Z80_LD T_MODE_HL ',' reloc_16bit {
- out_AbsByte(0x01 | (REG_HL << 4));
- out_RelWord(&$4);
- }
-;
-
-z80_ld_sp : T_Z80_LD T_MODE_SP ',' T_MODE_HL { out_AbsByte(0xF9); }
- | T_Z80_LD T_MODE_SP ',' reloc_16bit {
- out_AbsByte(0x01 | (REG_SP << 4));
- out_RelWord(&$4);
- }
-;
-
-z80_ld_mem : T_Z80_LD op_mem_ind ',' T_MODE_SP {
- out_AbsByte(0x08);
- out_RelWord(&$2);
- }
- | T_Z80_LD op_mem_ind ',' T_MODE_A {
- if (optimizeloads && rpn_isKnown(&$2)
- && $2.nVal >= 0xFF00) {
- out_AbsByte(0xE0);
- out_AbsByte($2.nVal & 0xFF);
- rpn_Free(&$2);
- } else {
- out_AbsByte(0xEA);
- out_RelWord(&$2);
- }
- }
-;
-
-z80_ld_cind : T_Z80_LD c_ind ',' T_MODE_A {
- out_AbsByte(0xE2);
- }
-;
-
-z80_ld_rr : T_Z80_LD reg_rr ',' T_MODE_A {
- out_AbsByte(0x02 | ($2 << 4));
- }
-;
-
-z80_ld_r : T_Z80_LD reg_r ',' reloc_8bit {
- out_AbsByte(0x06 | ($2 << 3));
- out_RelByte(&$4);
- }
- | T_Z80_LD reg_r ',' reg_r {
- if (($2 == REG_HL_IND) && ($4 == REG_HL_IND))
- error("LD [HL],[HL] not a valid instruction\n");
- else
- out_AbsByte(0x40 | ($2 << 3) | $4);
- }
-;
-
-z80_ld_a : T_Z80_LD reg_r ',' c_ind {
- if ($2 == REG_A)
- out_AbsByte(0xF2);
- else
- error("Destination operand must be A\n");
- }
- | T_Z80_LD reg_r ',' reg_rr {
- if ($2 == REG_A)
- out_AbsByte(0x0A | ($4 << 4));
- else
- error("Destination operand must be A\n");
- }
- | T_Z80_LD reg_r ',' op_mem_ind {
- if ($2 == REG_A) {
- if (optimizeloads && rpn_isKnown(&$4)
- && $4.nVal >= 0xFF00) {
- out_AbsByte(0xF0);
- out_AbsByte($4.nVal & 0xFF);
- rpn_Free(&$4);
- } else {
- out_AbsByte(0xFA);
- out_RelWord(&$4);
- }
- } else {
- error("Destination operand must be A\n");
- rpn_Free(&$4);
- }
- }
-;
-
-z80_ld_ss : T_Z80_LD T_MODE_BC ',' reloc_16bit {
- out_AbsByte(0x01 | (REG_BC << 4));
- out_RelWord(&$4);
- }
- | T_Z80_LD T_MODE_DE ',' reloc_16bit {
- out_AbsByte(0x01 | (REG_DE << 4));
- out_RelWord(&$4);
- }
- /*
- * HL is taken care of in z80_ld_hl
- * SP is taken care of in z80_ld_sp
- */
-;
-
-z80_nop : T_Z80_NOP { out_AbsByte(0x00); }
-;
-
-z80_or : T_Z80_OR op_a_n {
- out_AbsByte(0xF6);
- out_RelByte(&$2);
- }
- | T_Z80_OR op_a_r { out_AbsByte(0xB0 | $2); }
-;
-
-z80_pop : T_Z80_POP reg_tt { out_AbsByte(0xC1 | ($2 << 4)); }
-;
-
-z80_push : T_Z80_PUSH reg_tt { out_AbsByte(0xC5 | ($2 << 4)); }
-;
-
-z80_res : T_Z80_RES const_3bit ',' reg_r {
- out_AbsByte(0xCB);
- out_AbsByte(0x80 | ($2 << 3) | $4);
- }
-;
-
-z80_ret : T_Z80_RET { out_AbsByte(0xC9);
- }
- | T_Z80_RET ccode { out_AbsByte(0xC0 | ($2 << 3)); }
-;
-
-z80_reti : T_Z80_RETI { out_AbsByte(0xD9); }
-;
-
-z80_rl : T_Z80_RL reg_r {
- out_AbsByte(0xCB);
- out_AbsByte(0x10 | $2);
- }
-;
-
-z80_rla : T_Z80_RLA { out_AbsByte(0x17); }
-;
-
-z80_rlc : T_Z80_RLC reg_r {
- out_AbsByte(0xCB);
- out_AbsByte(0x00 | $2);
- }
-;
-
-z80_rlca : T_Z80_RLCA { out_AbsByte(0x07); }
-;
-
-z80_rr : T_Z80_RR reg_r {
- out_AbsByte(0xCB);
- out_AbsByte(0x18 | $2);
- }
-;
-
-z80_rra : T_Z80_RRA { out_AbsByte(0x1F); }
-;
-
-z80_rrc : T_Z80_RRC reg_r {
- out_AbsByte(0xCB);
- out_AbsByte(0x08 | $2);
- }
-;
-
-z80_rrca : T_Z80_RRCA { out_AbsByte(0x0F); }
-;
-
-z80_rst : T_Z80_RST reloc_8bit {
- rpn_CheckRST(&$2, &$2);
- if (!rpn_isKnown(&$2))
- out_RelByte(&$2);
- else
- out_AbsByte(0xC7 | $2.nVal);
- rpn_Free(&$2);
- }
-;
-
-z80_sbc : T_Z80_SBC op_a_n {
- out_AbsByte(0xDE);
- out_RelByte(&$2);
- }
- | T_Z80_SBC op_a_r { out_AbsByte(0x98 | $2); }
-;
-
-z80_scf : T_Z80_SCF { out_AbsByte(0x37); }
-;
-
-z80_set : T_POP_SET const_3bit ',' reg_r {
- out_AbsByte(0xCB);
- out_AbsByte(0xC0 | ($2 << 3) | $4);
- }
-;
-
-z80_sla : T_Z80_SLA reg_r {
- out_AbsByte(0xCB);
- out_AbsByte(0x20 | $2);
- }
-;
-
-z80_sra : T_Z80_SRA reg_r {
- out_AbsByte(0xCB);
- out_AbsByte(0x28 | $2);
- }
-;
-
-z80_srl : T_Z80_SRL reg_r {
- out_AbsByte(0xCB);
- out_AbsByte(0x38 | $2);
- }
-;
-
-z80_stop : T_Z80_STOP {
- out_AbsByte(0x10);
- out_AbsByte(0x00);
- }
- | T_Z80_STOP reloc_8bit {
- out_AbsByte(0x10);
- out_RelByte(&$2);
- }
-;
-
-z80_sub : T_Z80_SUB op_a_n {
- out_AbsByte(0xD6);
- out_RelByte(&$2);
- }
- | T_Z80_SUB op_a_r { out_AbsByte(0x90 | $2);
- }
-;
-
-z80_swap : T_Z80_SWAP reg_r {
- out_AbsByte(0xCB);
- out_AbsByte(0x30 | $2);
- }
-;
-
-z80_xor : T_Z80_XOR op_a_n {
- out_AbsByte(0xEE);
- out_RelByte(&$2);
- }
- | T_Z80_XOR op_a_r { out_AbsByte(0xA8 | $2); }
-;
-
-op_mem_ind : '[' reloc_16bit ']' { $$ = $2; }
-;
-
-op_hl_ss : reg_ss { $$ = $1; }
- | T_MODE_HL ',' reg_ss { $$ = $3; }
-;
-
-op_a_r : reg_r { $$ = $1; }
- | T_MODE_A ',' reg_r { $$ = $3; }
-;
-
-op_a_n : reloc_8bit { $$ = $1; }
- | T_MODE_A ',' reloc_8bit { $$ = $3; }
-;
-
-T_MODE_A : T_TOKEN_A
- | T_OP_HIGH '(' T_MODE_AF ')'
-;
-
-T_MODE_B : T_TOKEN_B
- | T_OP_HIGH '(' T_MODE_BC ')'
-;
-
-T_MODE_C : T_TOKEN_C
- | T_OP_LOW '(' T_MODE_BC ')'
-;
-
-T_MODE_D : T_TOKEN_D
- | T_OP_HIGH '(' T_MODE_DE ')'
-;
-
-T_MODE_E : T_TOKEN_E
- | T_OP_LOW '(' T_MODE_DE ')'
-;
-
-T_MODE_H : T_TOKEN_H
- | T_OP_HIGH '(' T_MODE_HL ')'
-;
-
-T_MODE_L : T_TOKEN_L
- | T_OP_LOW '(' T_MODE_HL ')'
-;
-
-ccode : T_CC_NZ { $$ = CC_NZ; }
- | T_CC_Z { $$ = CC_Z; }
- | T_CC_NC { $$ = CC_NC; }
- | T_TOKEN_C { $$ = CC_C; }
-;
-
-reg_r : T_MODE_B { $$ = REG_B; }
- | T_MODE_C { $$ = REG_C; }
- | T_MODE_D { $$ = REG_D; }
- | T_MODE_E { $$ = REG_E; }
- | T_MODE_H { $$ = REG_H; }
- | T_MODE_L { $$ = REG_L; }
- | '[' T_MODE_HL ']' { $$ = REG_HL_IND; }
- | T_MODE_A { $$ = REG_A; }
-;
-
-reg_tt : T_MODE_BC { $$ = REG_BC; }
- | T_MODE_DE { $$ = REG_DE; }
- | T_MODE_HL { $$ = REG_HL; }
- | T_MODE_AF { $$ = REG_AF; }
-;
-
-reg_ss : T_MODE_BC { $$ = REG_BC; }
- | T_MODE_DE { $$ = REG_DE; }
- | T_MODE_HL { $$ = REG_HL; }
- | T_MODE_SP { $$ = REG_SP; }
-;
-
-reg_rr : '[' T_MODE_BC ']' { $$ = REG_BC_IND; }
- | '[' T_MODE_DE ']' { $$ = REG_DE_IND; }
- | hl_ind_inc { $$ = REG_HL_INDINC; }
- | hl_ind_dec { $$ = REG_HL_INDDEC; }
-;
-
-hl_ind_inc : '[' T_MODE_HL_INC ']'
- | '[' T_MODE_HL T_OP_ADD ']'
-;
-
-hl_ind_dec : '[' T_MODE_HL_DEC ']'
- | '[' T_MODE_HL T_OP_SUB ']'
-;
-
-%%
--- a/src/asm/lexer.c
+++ b/src/asm/lexer.c
@@ -36,7 +36,7 @@
#include "asm/util.h"
#include "asm/warning.h"
/* Include this last so it gets all type & constant definitions */
-#include "asmy.h" /* For token definitions, generated from asmy.y */
+#include "parser.h" /* For token definitions, generated from parser.y */
#ifdef LEXER_DEBUG
#define dbgPrint(...) fprintf(stderr, "[lexer] " __VA_ARGS__)
--- a/src/asm/main.c
+++ b/src/asm/main.c
@@ -26,7 +26,7 @@
#include "asm/rpn.h"
#include "asm/symbol.h"
#include "asm/warning.h"
-#include "asmy.h"
+#include "parser.h"
#include "extern/err.h"
#include "extern/getopt.h"
--- /dev/null
+++ b/src/asm/parser.y
@@ -1,0 +1,1631 @@
+/*
+ * This file is part of RGBDS.
+ *
+ * Copyright (c) 1997-2019, Carsten Sorensen and RGBDS contributors.
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+%{
+#include <ctype.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "asm/asm.h"
+#include "asm/charmap.h"
+#include "asm/fstack.h"
+#include "asm/lexer.h"
+#include "asm/macro.h"
+#include "asm/main.h"
+#include "asm/mymath.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
+
+uint32_t nListCountEmpty;
+char *tzNewMacro;
+uint32_t ulNewMacroSize;
+int32_t nPCOffset;
+bool executeElseBlock; /* If this is set, ELIFs cannot be executed anymore */
+
+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 size_t strlenUTF8(const char *s)
+{
+ size_t len = 0;
+ uint32_t state = 0;
+ uint32_t codep = 0;
+
+ while (*s) {
+ switch (decode(&state, &codep, (uint8_t)*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, 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, (uint8_t)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 < MAXSTRLEN && curLen < len) {
+ switch (decode(&state, &codep, (uint8_t)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 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;
+ }
+}
+
+#define yyerror(str) error(str "\n")
+
+%}
+
+%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;
+}
+
+%type <sVal> relocexpr
+%type <sVal> relocexpr_no_str
+%type <nConstValue> const
+%type <nConstValue> uconst
+%type <nConstValue> const_3bit
+%type <sVal> reloc_8bit
+%type <sVal> reloc_8bit_no_str
+%type <sVal> reloc_16bit
+%type <nConstValue> sectiontype
+
+%type <tzString> string
+
+%type <nConstValue> sectorg
+%type <sectSpec> sectattrs
+
+%token <nConstValue> T_NUMBER
+%token <tzString> T_STRING
+
+%left T_OP_LOGICNOT
+%left T_OP_LOGICOR 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
+%left T_OP_NOT
+%left T_OP_DEF
+%left T_OP_BANK T_OP_ALIGN
+%left T_OP_SIN
+%left T_OP_COS
+%left T_OP_TAN
+%left T_OP_ASIN
+%left T_OP_ACOS
+%left T_OP_ATAN
+%left T_OP_ATAN2
+%left T_OP_FDIV
+%left T_OP_FMUL
+%left T_OP_ROUND
+%left T_OP_CEIL
+%left T_OP_FLOOR
+
+%token T_OP_HIGH T_OP_LOW
+%token T_OP_ISCONST
+
+%left T_OP_STRCMP
+%left T_OP_STRIN
+%left T_OP_STRSUB
+%left T_OP_STRLEN
+%left T_OP_STRCAT
+%left T_OP_STRUPR
+%left T_OP_STRLWR
+
+%left NEG /* negation -- unary minus */
+
+%token <tzSym> T_LABEL
+%token <tzSym> T_ID
+%token <tzSym> T_LOCAL_ID
+%type <tzSym> scoped_id
+%token T_POP_EQU
+%token T_POP_SET
+%token T_POP_EQUAL
+%token T_POP_EQUS
+
+%token T_POP_INCLUDE T_POP_PRINTF T_POP_PRINTT T_POP_PRINTV T_POP_PRINTI
+%token T_POP_IF T_POP_ELIF T_POP_ELSE T_POP_ENDC
+%token T_POP_EXPORT T_POP_GLOBAL T_POP_XDEF
+%token T_POP_DB T_POP_DS T_POP_DW T_POP_DL
+%token T_POP_SECTION T_POP_FRAGMENT
+%token T_POP_RB
+%token T_POP_RW
+%token T_POP_RL
+%token T_POP_MACRO
+%token T_POP_ENDM
+%token T_POP_RSRESET T_POP_RSSET
+%token T_POP_UNION T_POP_NEXTU T_POP_ENDU
+%token T_POP_INCBIN T_POP_REPT
+%token T_POP_CHARMAP
+%token T_POP_NEWCHARMAP
+%token T_POP_SETCHARMAP
+%token T_POP_PUSHC
+%token T_POP_POPC
+%token T_POP_SHIFT
+%token T_POP_ENDR
+%token T_POP_LOAD T_POP_ENDL
+%token T_POP_FAIL
+%token T_POP_WARN
+%token T_POP_FATAL
+%token T_POP_ASSERT T_POP_STATIC_ASSERT
+%token T_POP_PURGE
+%token T_POP_POPS
+%token T_POP_PUSHS
+%token T_POP_POPO
+%token T_POP_PUSHO
+%token T_POP_OPT
+%token T_SECT_WRAM0 T_SECT_VRAM T_SECT_ROMX T_SECT_ROM0 T_SECT_HRAM
+%token T_SECT_WRAMX T_SECT_SRAM T_SECT_OAM
+
+%type <sectMod> sectmod
+%type <macroArg> macroargs
+
+%token T_Z80_ADC T_Z80_ADD T_Z80_AND
+%token T_Z80_BIT
+%token T_Z80_CALL T_Z80_CCF T_Z80_CP T_Z80_CPL
+%token T_Z80_DAA T_Z80_DEC T_Z80_DI
+%token T_Z80_EI
+%token T_Z80_HALT
+%token T_Z80_INC
+%token T_Z80_JP T_Z80_JR
+%token T_Z80_LD
+%token T_Z80_LDI
+%token T_Z80_LDD
+%token T_Z80_LDIO
+%token T_Z80_NOP
+%token T_Z80_OR
+%token T_Z80_POP T_Z80_PUSH
+%token T_Z80_RES T_Z80_RET T_Z80_RETI T_Z80_RST
+%token T_Z80_RL T_Z80_RLA T_Z80_RLC T_Z80_RLCA
+%token T_Z80_RR T_Z80_RRA T_Z80_RRC T_Z80_RRCA
+%token T_Z80_SBC T_Z80_SCF T_Z80_STOP
+%token T_Z80_SLA T_Z80_SRA T_Z80_SRL T_Z80_SUB T_Z80_SWAP
+%token T_Z80_XOR
+
+%token T_TOKEN_A T_TOKEN_B T_TOKEN_C T_TOKEN_D T_TOKEN_E T_TOKEN_H T_TOKEN_L
+%token T_MODE_AF
+%token T_MODE_BC
+%token T_MODE_DE
+%token T_MODE_SP
+%token T_MODE_HW_C
+%token T_MODE_HL T_MODE_HL_DEC T_MODE_HL_INC
+%token T_CC_NZ T_CC_Z T_CC_NC
+
+%type <nConstValue> reg_r
+%type <nConstValue> reg_ss
+%type <nConstValue> reg_rr
+%type <nConstValue> reg_tt
+%type <nConstValue> ccode
+%type <sVal> op_a_n
+%type <nConstValue> op_a_r
+%type <nConstValue> op_hl_ss
+%type <sVal> op_mem_ind
+%type <assertType> assert_type
+%start asmfile
+
+%%
+
+asmfile : lines;
+
+/* Note: The lexer adds '\n' at the end of the input */
+lines : /* empty */
+ | lines {
+ nListCountEmpty = 0;
+ nPCOffset = 0;
+ } line {
+ nTotalLines++;
+ }
+;
+
+line : label '\n'
+ | label cpu_command '\n'
+ | label macro '\n'
+ | label simple_pseudoop '\n'
+ | pseudoop '\n'
+ | conditional /* May not necessarily be followed by a newline, see below */
+;
+
+/*
+ * For "logistical" reasons, conditionals 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.
+ */
+conditional : if
+ /* It's important that all of these require being at line start for `skipIfBlock` */
+ | elif
+ | else
+ | endc
+;
+
+if : T_POP_IF const '\n' {
+ nIFDepth++;
+ executeElseBlock = !$2;
+ if (executeElseBlock)
+ lexer_SetMode(LEXER_SKIP_TO_ELIF);
+ }
+;
+
+elif : T_POP_ELIF const '\n' {
+ if (nIFDepth <= 0)
+ fatalerror("Found ELIF outside an IF construct\n");
+
+ if (!executeElseBlock) {
+ lexer_SetMode(LEXER_SKIP_TO_ENDC);
+ } else {
+ executeElseBlock = !$2;
+ if (executeElseBlock)
+ lexer_SetMode(LEXER_SKIP_TO_ELIF);
+ }
+ }
+;
+
+else : T_POP_ELSE '\n' {
+ if (nIFDepth <= 0)
+ fatalerror("Found ELSE outside an IF construct\n");
+
+ if (!executeElseBlock)
+ lexer_SetMode(LEXER_SKIP_TO_ENDC);
+ }
+;
+
+endc : T_POP_ENDC '\n' {
+ if (nIFDepth <= 0)
+ fatalerror("Found ENDC outside an IF construct\n");
+
+ nIFDepth--;
+ executeElseBlock = false;
+ }
+;
+
+scoped_id : T_ID | T_LOCAL_ID ;
+
+label : /* empty */
+ | T_LOCAL_ID {
+ sym_AddLocalLabel($1);
+ }
+ | T_LABEL {
+ warning(WARNING_OBSOLETE, "Non-local labels without a colon are deprecated\n");
+ sym_AddLabel($1);
+ }
+ | T_LOCAL_ID ':' {
+ sym_AddLocalLabel($1);
+ }
+ | T_LABEL ':' {
+ sym_AddLabel($1);
+ }
+ | T_LOCAL_ID ':' ':' {
+ sym_AddLocalLabel($1);
+ sym_Export($1);
+ }
+ | T_LABEL ':' ':' {
+ sym_AddLabel($1);
+ sym_Export($1);
+ }
+;
+
+macro : T_ID {
+ lexer_SetMode(LEXER_RAW);
+ } macroargs {
+ lexer_SetMode(LEXER_NORMAL);
+ fstk_RunMacro($1, $3);
+ }
+;
+
+macroargs : /* empty */ {
+ $$ = macro_NewArgs();
+ }
+ | T_STRING {
+ $$ = macro_NewArgs();
+ macro_AppendArg(&($$), strdup($1));
+ }
+ | macroargs ',' T_STRING {
+ macro_AppendArg(&($$), strdup($3));
+ }
+;
+
+pseudoop : equ
+ | set
+ | rb
+ | rw
+ | rl
+ | equs
+ | macrodef
+;
+
+simple_pseudoop : include
+ | printf
+ | printt
+ | printv
+ | printi
+ | export
+ | db
+ | dw
+ | dl
+ | ds
+ | section
+ | rsreset
+ | rsset
+ | union
+ | nextu
+ | endu
+ | incbin
+ | charmap
+ | newcharmap
+ | setcharmap
+ | pushc
+ | popc
+ | load
+ | rept
+ | shift
+ | fail
+ | warn
+ | assert
+ | purge
+ | pops
+ | pushs
+ | popo
+ | pusho
+ | opt
+ | align
+;
+
+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 ',' 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 {
+ lexer_SetMode(LEXER_RAW);
+ } opt_list {
+ lexer_SetMode(LEXER_NORMAL);
+ }
+;
+
+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 ',' { $$ = ASSERT_WARN; }
+ | T_POP_FAIL ',' { $$ = ASSERT_ERROR; }
+ | T_POP_FATAL ',' { $$ = 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 ',' 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 ',' string
+ {
+ if ($3 == 0)
+ failAssertMsg($2, $5);
+ }
+;
+
+shift : T_POP_SHIFT { macro_ShiftCurrentArgs(); }
+ | T_POP_SHIFT uconst
+ {
+ int32_t i = $2;
+ while (i--)
+ macro_ShiftCurrentArgs();
+ }
+;
+
+load : T_POP_LOAD string ',' sectiontype sectorg sectattrs {
+ out_SetLoadSection($2, $4, $5, &$6);
+ }
+ | T_POP_ENDL { out_EndLoadSection(); }
+;
+
+rept : T_POP_REPT uconst {
+ uint32_t nDefinitionLineNo = lexer_GetLineNo();
+ char *body;
+ size_t size;
+ lexer_CaptureRept(&body, &size);
+ fstk_RunRept($2, nDefinitionLineNo, body, size);
+ }
+;
+
+macrodef : T_LABEL ':' T_POP_MACRO {
+ int32_t nDefinitionLineNo = lexer_GetLineNo();
+ char *body;
+ size_t size;
+ lexer_CaptureMacroBody(&body, &size);
+ sym_AddMacro($1, nDefinitionLineNo, body, size);
+ }
+;
+
+equs : T_LABEL T_POP_EQUS string { sym_AddString($1, $3); }
+;
+
+rsset : T_POP_RSSET uconst { sym_AddSet("_RS", $2); }
+;
+
+rsreset : T_POP_RSRESET { sym_AddSet("_RS", 0); }
+;
+
+rl : T_LABEL T_POP_RL uconst {
+ sym_AddEqu($1, sym_GetConstantValue("_RS"));
+ sym_AddSet("_RS", sym_GetConstantValue("_RS") + 4 * $3);
+ }
+;
+
+rw : T_LABEL T_POP_RW uconst {
+ sym_AddEqu($1, sym_GetConstantValue("_RS"));
+ sym_AddSet("_RS", sym_GetConstantValue("_RS") + 2 * $3);
+ }
+;
+
+rb : T_LABEL T_POP_RB uconst {
+ sym_AddEqu($1, sym_GetConstantValue("_RS"));
+ sym_AddSet("_RS", sym_GetConstantValue("_RS") + $3);
+ }
+;
+
+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 ',' reloc_8bit {
+ out_RelBytes(&$4, $2);
+ }
+;
+
+/* Authorize empty entries if there is only one */
+db : T_POP_DB constlist_8bit_entry ',' constlist_8bit {
+ if (nListCountEmpty > 0)
+ warning(WARNING_EMPTY_ENTRY,
+ "Empty entry in list of 8-bit elements (treated as padding).\n");
+ }
+ | T_POP_DB constlist_8bit_entry
+;
+
+dw : T_POP_DW constlist_16bit_entry ',' constlist_16bit {
+ if (nListCountEmpty > 0)
+ warning(WARNING_EMPTY_ENTRY,
+ "Empty entry in list of 16-bit elements (treated as padding).\n");
+ }
+ | T_POP_DW constlist_16bit_entry
+;
+
+dl : T_POP_DL constlist_32bit_entry ',' constlist_32bit {
+ if (nListCountEmpty > 0)
+ warning(WARNING_EMPTY_ENTRY,
+ "Empty entry in list of 32-bit elements (treated as padding).\n");
+ }
+ | T_POP_DL constlist_32bit_entry
+;
+
+purge : T_POP_PURGE {
+ lexer_ToggleStringExpansion(false);
+ } purge_list {
+ lexer_ToggleStringExpansion(true);
+ }
+;
+
+purge_list : purge_list_entry
+ | purge_list ',' purge_list_entry
+;
+
+purge_list_entry : scoped_id { sym_Purge($1); }
+;
+
+export : export_token export_list
+;
+
+export_token : T_POP_EXPORT
+ | T_POP_GLOBAL {
+ warning(WARNING_OBSOLETE,
+ "`GLOBAL` is a deprecated synonym for `EXPORT`\n");
+ }
+ | T_POP_XDEF {
+ warning(WARNING_OBSOLETE, "`XDEF` is a deprecated synonym for `EXPORT`\n");
+ }
+;
+
+export_list : export_list_entry
+ | export_list ',' export_list_entry
+;
+
+export_list_entry : scoped_id { sym_Export($1); }
+;
+
+equ : T_LABEL T_POP_EQU const { sym_AddEqu($1, $3); }
+;
+
+set : T_LABEL T_POP_SET const { sym_AddSet($1, $3); }
+ | T_LABEL T_POP_EQUAL const { sym_AddSet($1, $3); }
+;
+
+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 ',' const {
+ out_BinaryFile($2, $4);
+ if (oFailedOnMissingInclude)
+ YYACCEPT;
+ }
+ | T_POP_INCBIN string ',' const ',' const {
+ out_BinaryFileSlice($2, $4, $6);
+ if (oFailedOnMissingInclude)
+ YYACCEPT;
+ }
+;
+
+charmap : T_POP_CHARMAP string ',' 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_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(); }
+;
+
+printt : T_POP_PRINTT string { printf("%s", $2); }
+;
+
+printv : T_POP_PRINTV const { printf("$%" PRIX32, $2); }
+;
+
+printi : T_POP_PRINTI const { printf("%" PRId32, $2); }
+;
+
+printf : T_POP_PRINTF const { math_Print($2); }
+;
+
+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 ',' constlist_8bit_entry
+;
+
+constlist_8bit_entry : /* empty */ {
+ out_Skip(1, false);
+ nListCountEmpty++;
+ }
+ | 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);
+ }
+;
+
+constlist_16bit : constlist_16bit_entry
+ | constlist_16bit ',' constlist_16bit_entry
+;
+
+constlist_16bit_entry : /* empty */ {
+ out_Skip(2, false);
+ nListCountEmpty++;
+ }
+ | reloc_16bit { out_RelWord(&$1); }
+;
+
+constlist_32bit : constlist_32bit_entry
+ | constlist_32bit ',' constlist_32bit_entry
+;
+
+constlist_32bit_entry : /* empty */ {
+ out_Skip(4, false);
+ nListCountEmpty++;
+ }
+ | relocexpr { out_RelLong(&$1); }
+;
+
+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;
+ }
+;
+
+
+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_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);
+ }
+ | 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 '(' relocexpr ')' { rpn_HIGH(&$$, &$3); }
+ | T_OP_LOW '(' relocexpr ')' { rpn_LOW(&$$, &$3); }
+ | T_OP_ISCONST '(' relocexpr ')'{ rpn_ISCONST(&$$, &$3); }
+ | T_OP_BANK '(' scoped_id ')' {
+ /* '@' is also a T_ID, it is handled here. */
+ rpn_BankSymbol(&$$, $3);
+ }
+ | T_OP_BANK '(' string ')' { rpn_BankSection(&$$, $3); }
+ | T_OP_DEF {
+ lexer_ToggleStringExpansion(false);
+ } '(' scoped_id ')' {
+ struct Symbol const *sym = sym_FindSymbol($4);
+
+ rpn_Number(&$$, !!sym);
+
+ lexer_ToggleStringExpansion(true);
+ }
+ | T_OP_ROUND '(' const ')' {
+ rpn_Number(&$$, math_Round($3));
+ }
+ | T_OP_CEIL '(' const ')' {
+ rpn_Number(&$$, math_Ceil($3));
+ }
+ | T_OP_FLOOR '(' const ')' {
+ rpn_Number(&$$, math_Floor($3));
+ }
+ | T_OP_FDIV '(' const ',' const ')' {
+ rpn_Number(&$$, math_Div($3, $5));
+ }
+ | T_OP_FMUL '(' const ',' const ')' {
+ rpn_Number(&$$, math_Mul($3, $5));
+ }
+ | T_OP_SIN '(' const ')' {
+ rpn_Number(&$$, math_Sin($3));
+ }
+ | T_OP_COS '(' const ')' {
+ rpn_Number(&$$, math_Cos($3));
+ }
+ | T_OP_TAN '(' const ')' {
+ rpn_Number(&$$, math_Tan($3));
+ }
+ | T_OP_ASIN '(' const ')' {
+ rpn_Number(&$$, math_ASin($3));
+ }
+ | T_OP_ACOS '(' const ')' {
+ rpn_Number(&$$, math_ACos($3));
+ }
+ | T_OP_ATAN '(' const ')' {
+ rpn_Number(&$$, math_ATan($3));
+ }
+ | T_OP_ATAN2 '(' const ',' const ')' {
+ rpn_Number(&$$, math_ATan2($3, $5));
+ }
+ | T_OP_STRCMP '(' string ',' string ')' {
+ rpn_Number(&$$, strcmp($3, $5));
+ }
+ | T_OP_STRIN '(' string ',' string ')' {
+ char *p = strstr($3, $5);
+
+ rpn_Number(&$$, p ? p - $3 + 1 : 0);
+ }
+ | T_OP_STRLEN '(' string ')' {
+ rpn_Number(&$$, strlenUTF8($3));
+ }
+ | '(' relocexpr ')' { $$ = $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;
+ }
+ }
+;
+
+string : T_STRING {
+ if (snprintf($$, MAXSTRLEN + 1, "%s", $1) > MAXSTRLEN)
+ warning(WARNING_LONG_STR, "String is too long '%s'\n", $1);
+ }
+ | T_OP_STRSUB '(' string ',' uconst ',' uconst ')' {
+ strsubUTF8($$, $3, $5, $7);
+ }
+ | T_OP_STRCAT '(' string ',' string ')' {
+ if (snprintf($$, MAXSTRLEN + 1, "%s%s", $3, $5) > MAXSTRLEN)
+ warning(WARNING_LONG_STR, "STRCAT: String too long '%s%s'\n",
+ $3, $5);
+ }
+ | T_OP_STRUPR '(' string ')' {
+ if (snprintf($$, MAXSTRLEN + 1, "%s", $3) > MAXSTRLEN)
+ warning(WARNING_LONG_STR, "STRUPR: String too long '%s'\n", $3);
+
+ upperstring($$);
+ }
+ | T_OP_STRLWR '(' string ')' {
+ if (snprintf($$, MAXSTRLEN + 1, "%s", $3) > MAXSTRLEN)
+ warning(WARNING_LONG_STR, "STRUPR: String too long '%s'\n", $3);
+
+ lowerstring($$);
+ }
+;
+
+section : T_POP_SECTION sectmod string ',' 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; }
+ | '[' uconst ']' {
+ 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_OP_ALIGN '[' uconst ']' {
+ if ($5 > 16)
+ error("Alignment must be between 0 and 16, not %u\n", $5);
+ else
+ $$.alignment = $5;
+ }
+ | sectattrs ',' T_OP_ALIGN '[' uconst ',' uconst ']' {
+ if ($5 > 16) {
+ error("Alignment must be between 0 and 16, not %u\n", $5);
+ } else {
+ $$.alignment = $5;
+ if ($7 >= 1 << $$.alignment)
+ error("Alignment offset must not be greater than alignment (%u < %u)\n",
+ $7, 1 << $$.alignment);
+ else
+ $$.alignOfs = $7;
+ }
+ }
+ | sectattrs ',' T_OP_BANK '[' uconst ']' {
+ /* We cannot check the validity of this now */
+ $$.bank = $5;
+ }
+;
+
+
+cpu_command : { nPCOffset = 1; } z80_adc
+ | { nPCOffset = 1; } z80_add
+ | { nPCOffset = 1; } z80_and
+ | { nPCOffset = 1; } z80_bit
+ | { nPCOffset = 1; } z80_call
+ | z80_ccf
+ | { nPCOffset = 1; } z80_cp
+ | z80_cpl
+ | z80_daa
+ | { nPCOffset = 1; } z80_dec
+ | z80_di
+ | z80_ei
+ | z80_halt
+ | z80_inc
+ | { nPCOffset = 1; } z80_jp
+ | { nPCOffset = 1; } z80_jr
+ | { nPCOffset = 1; } z80_ld
+ | z80_ldd
+ | z80_ldi
+ | { nPCOffset = 1; } z80_ldio
+ | z80_nop
+ | { nPCOffset = 1; } z80_or
+ | z80_pop
+ | z80_push
+ | { nPCOffset = 1; } z80_res
+ | z80_ret
+ | z80_reti
+ | z80_rl
+ | z80_rla
+ | z80_rlc
+ | z80_rlca
+ | z80_rr
+ | z80_rra
+ | z80_rrc
+ | z80_rrca
+ | /*{ nPCOffset = 0; }*/ z80_rst
+ | { nPCOffset = 1; } z80_sbc
+ | z80_scf
+ | { nPCOffset = 1; } z80_set
+ | z80_sla
+ | z80_sra
+ | z80_srl
+ | { nPCOffset = 1; } z80_stop
+ | { nPCOffset = 1; } z80_sub
+ | z80_swap
+ | { nPCOffset = 1; } z80_xor
+;
+
+z80_adc : T_Z80_ADC op_a_n {
+ out_AbsByte(0xCE);
+ out_RelByte(&$2);
+ }
+ | T_Z80_ADC op_a_r { out_AbsByte(0x88 | $2); }
+;
+
+z80_add : T_Z80_ADD op_a_n {
+ out_AbsByte(0xC6);
+ out_RelByte(&$2);
+ }
+ | T_Z80_ADD op_a_r { out_AbsByte(0x80 | $2); }
+ | T_Z80_ADD op_hl_ss { out_AbsByte(0x09 | ($2 << 4)); }
+ | T_Z80_ADD T_MODE_SP ',' reloc_8bit {
+ out_AbsByte(0xE8);
+ out_RelByte(&$4);
+ }
+
+;
+
+z80_and : T_Z80_AND op_a_n {
+ out_AbsByte(0xE6);
+ out_RelByte(&$2);
+ }
+ | T_Z80_AND op_a_r { out_AbsByte(0xA0 | $2); }
+;
+
+z80_bit : T_Z80_BIT const_3bit ',' reg_r {
+ out_AbsByte(0xCB);
+ out_AbsByte(0x40 | ($2 << 3) | $4);
+ }
+;
+
+z80_call : T_Z80_CALL reloc_16bit {
+ out_AbsByte(0xCD);
+ out_RelWord(&$2);
+ }
+ | T_Z80_CALL ccode ',' reloc_16bit {
+ out_AbsByte(0xC4 | ($2 << 3));
+ out_RelWord(&$4);
+ }
+;
+
+z80_ccf : T_Z80_CCF { out_AbsByte(0x3F); }
+;
+
+z80_cp : T_Z80_CP op_a_n {
+ out_AbsByte(0xFE);
+ out_RelByte(&$2);
+ }
+ | T_Z80_CP op_a_r { out_AbsByte(0xB8 | $2); }
+;
+
+z80_cpl : T_Z80_CPL { out_AbsByte(0x2F); }
+;
+
+z80_daa : T_Z80_DAA { out_AbsByte(0x27); }
+;
+
+z80_dec : T_Z80_DEC reg_r { out_AbsByte(0x05 | ($2 << 3)); }
+ | T_Z80_DEC reg_ss { out_AbsByte(0x0B | ($2 << 4)); }
+;
+
+z80_di : T_Z80_DI { out_AbsByte(0xF3); }
+;
+
+z80_ei : T_Z80_EI { out_AbsByte(0xFB); }
+;
+
+z80_halt : T_Z80_HALT {
+ out_AbsByte(0x76);
+ if (haltnop)
+ out_AbsByte(0x00);
+ }
+;
+
+z80_inc : T_Z80_INC reg_r { out_AbsByte(0x04 | ($2 << 3)); }
+ | T_Z80_INC reg_ss { out_AbsByte(0x03 | ($2 << 4)); }
+;
+
+z80_jp : T_Z80_JP reloc_16bit {
+ out_AbsByte(0xC3);
+ out_RelWord(&$2);
+ }
+ | T_Z80_JP ccode ',' reloc_16bit {
+ out_AbsByte(0xC2 | ($2 << 3));
+ out_RelWord(&$4);
+ }
+ | T_Z80_JP T_MODE_HL {
+ out_AbsByte(0xE9);
+ }
+;
+
+z80_jr : T_Z80_JR reloc_16bit {
+ out_AbsByte(0x18);
+ out_PCRelByte(&$2);
+ }
+ | T_Z80_JR ccode ',' reloc_16bit {
+ out_AbsByte(0x20 | ($2 << 3));
+ out_PCRelByte(&$4);
+ }
+;
+
+z80_ldi : T_Z80_LDI '[' T_MODE_HL ']' ',' T_MODE_A {
+ out_AbsByte(0x02 | (2 << 4));
+ }
+ | T_Z80_LDI T_MODE_A ',' '[' T_MODE_HL ']' {
+ out_AbsByte(0x0A | (2 << 4));
+ }
+;
+
+z80_ldd : T_Z80_LDD '[' T_MODE_HL ']' ',' T_MODE_A {
+ out_AbsByte(0x02 | (3 << 4));
+ }
+ | T_Z80_LDD T_MODE_A ',' '[' T_MODE_HL ']' {
+ out_AbsByte(0x0A | (3 << 4));
+ }
+;
+
+z80_ldio : T_Z80_LDIO T_MODE_A ',' op_mem_ind {
+ rpn_CheckHRAM(&$4, &$4);
+
+ out_AbsByte(0xF0);
+ out_RelByte(&$4);
+ }
+ | T_Z80_LDIO op_mem_ind ',' T_MODE_A {
+ rpn_CheckHRAM(&$2, &$2);
+
+ out_AbsByte(0xE0);
+ out_RelByte(&$2);
+ }
+ | T_Z80_LDIO T_MODE_A ',' c_ind {
+ out_AbsByte(0xF2);
+ }
+ | T_Z80_LDIO c_ind ',' T_MODE_A {
+ out_AbsByte(0xE2);
+ }
+;
+
+c_ind : '[' T_MODE_C ']'
+ | '[' T_MODE_HW_C ']'
+;
+
+z80_ld : z80_ld_mem
+ | z80_ld_cind
+ | z80_ld_rr
+ | z80_ld_ss
+ | z80_ld_hl
+ | z80_ld_sp
+ | z80_ld_r
+ | z80_ld_a
+;
+
+z80_ld_hl : T_Z80_LD T_MODE_HL ',' T_MODE_SP reloc_8bit {
+ out_AbsByte(0xF8);
+ out_RelByte(&$5);
+ }
+ | T_Z80_LD T_MODE_HL ',' reloc_16bit {
+ out_AbsByte(0x01 | (REG_HL << 4));
+ out_RelWord(&$4);
+ }
+;
+
+z80_ld_sp : T_Z80_LD T_MODE_SP ',' T_MODE_HL { out_AbsByte(0xF9); }
+ | T_Z80_LD T_MODE_SP ',' reloc_16bit {
+ out_AbsByte(0x01 | (REG_SP << 4));
+ out_RelWord(&$4);
+ }
+;
+
+z80_ld_mem : T_Z80_LD op_mem_ind ',' T_MODE_SP {
+ out_AbsByte(0x08);
+ out_RelWord(&$2);
+ }
+ | T_Z80_LD op_mem_ind ',' T_MODE_A {
+ if (optimizeloads && rpn_isKnown(&$2)
+ && $2.nVal >= 0xFF00) {
+ out_AbsByte(0xE0);
+ out_AbsByte($2.nVal & 0xFF);
+ rpn_Free(&$2);
+ } else {
+ out_AbsByte(0xEA);
+ out_RelWord(&$2);
+ }
+ }
+;
+
+z80_ld_cind : T_Z80_LD c_ind ',' T_MODE_A {
+ out_AbsByte(0xE2);
+ }
+;
+
+z80_ld_rr : T_Z80_LD reg_rr ',' T_MODE_A {
+ out_AbsByte(0x02 | ($2 << 4));
+ }
+;
+
+z80_ld_r : T_Z80_LD reg_r ',' reloc_8bit {
+ out_AbsByte(0x06 | ($2 << 3));
+ out_RelByte(&$4);
+ }
+ | T_Z80_LD reg_r ',' reg_r {
+ if (($2 == REG_HL_IND) && ($4 == REG_HL_IND))
+ error("LD [HL],[HL] not a valid instruction\n");
+ else
+ out_AbsByte(0x40 | ($2 << 3) | $4);
+ }
+;
+
+z80_ld_a : T_Z80_LD reg_r ',' c_ind {
+ if ($2 == REG_A)
+ out_AbsByte(0xF2);
+ else
+ error("Destination operand must be A\n");
+ }
+ | T_Z80_LD reg_r ',' reg_rr {
+ if ($2 == REG_A)
+ out_AbsByte(0x0A | ($4 << 4));
+ else
+ error("Destination operand must be A\n");
+ }
+ | T_Z80_LD reg_r ',' op_mem_ind {
+ if ($2 == REG_A) {
+ if (optimizeloads && rpn_isKnown(&$4)
+ && $4.nVal >= 0xFF00) {
+ out_AbsByte(0xF0);
+ out_AbsByte($4.nVal & 0xFF);
+ rpn_Free(&$4);
+ } else {
+ out_AbsByte(0xFA);
+ out_RelWord(&$4);
+ }
+ } else {
+ error("Destination operand must be A\n");
+ rpn_Free(&$4);
+ }
+ }
+;
+
+z80_ld_ss : T_Z80_LD T_MODE_BC ',' reloc_16bit {
+ out_AbsByte(0x01 | (REG_BC << 4));
+ out_RelWord(&$4);
+ }
+ | T_Z80_LD T_MODE_DE ',' reloc_16bit {
+ out_AbsByte(0x01 | (REG_DE << 4));
+ out_RelWord(&$4);
+ }
+ /*
+ * HL is taken care of in z80_ld_hl
+ * SP is taken care of in z80_ld_sp
+ */
+;
+
+z80_nop : T_Z80_NOP { out_AbsByte(0x00); }
+;
+
+z80_or : T_Z80_OR op_a_n {
+ out_AbsByte(0xF6);
+ out_RelByte(&$2);
+ }
+ | T_Z80_OR op_a_r { out_AbsByte(0xB0 | $2); }
+;
+
+z80_pop : T_Z80_POP reg_tt { out_AbsByte(0xC1 | ($2 << 4)); }
+;
+
+z80_push : T_Z80_PUSH reg_tt { out_AbsByte(0xC5 | ($2 << 4)); }
+;
+
+z80_res : T_Z80_RES const_3bit ',' reg_r {
+ out_AbsByte(0xCB);
+ out_AbsByte(0x80 | ($2 << 3) | $4);
+ }
+;
+
+z80_ret : T_Z80_RET { out_AbsByte(0xC9);
+ }
+ | T_Z80_RET ccode { out_AbsByte(0xC0 | ($2 << 3)); }
+;
+
+z80_reti : T_Z80_RETI { out_AbsByte(0xD9); }
+;
+
+z80_rl : T_Z80_RL reg_r {
+ out_AbsByte(0xCB);
+ out_AbsByte(0x10 | $2);
+ }
+;
+
+z80_rla : T_Z80_RLA { out_AbsByte(0x17); }
+;
+
+z80_rlc : T_Z80_RLC reg_r {
+ out_AbsByte(0xCB);
+ out_AbsByte(0x00 | $2);
+ }
+;
+
+z80_rlca : T_Z80_RLCA { out_AbsByte(0x07); }
+;
+
+z80_rr : T_Z80_RR reg_r {
+ out_AbsByte(0xCB);
+ out_AbsByte(0x18 | $2);
+ }
+;
+
+z80_rra : T_Z80_RRA { out_AbsByte(0x1F); }
+;
+
+z80_rrc : T_Z80_RRC reg_r {
+ out_AbsByte(0xCB);
+ out_AbsByte(0x08 | $2);
+ }
+;
+
+z80_rrca : T_Z80_RRCA { out_AbsByte(0x0F); }
+;
+
+z80_rst : T_Z80_RST reloc_8bit {
+ rpn_CheckRST(&$2, &$2);
+ if (!rpn_isKnown(&$2))
+ out_RelByte(&$2);
+ else
+ out_AbsByte(0xC7 | $2.nVal);
+ rpn_Free(&$2);
+ }
+;
+
+z80_sbc : T_Z80_SBC op_a_n {
+ out_AbsByte(0xDE);
+ out_RelByte(&$2);
+ }
+ | T_Z80_SBC op_a_r { out_AbsByte(0x98 | $2); }
+;
+
+z80_scf : T_Z80_SCF { out_AbsByte(0x37); }
+;
+
+z80_set : T_POP_SET const_3bit ',' reg_r {
+ out_AbsByte(0xCB);
+ out_AbsByte(0xC0 | ($2 << 3) | $4);
+ }
+;
+
+z80_sla : T_Z80_SLA reg_r {
+ out_AbsByte(0xCB);
+ out_AbsByte(0x20 | $2);
+ }
+;
+
+z80_sra : T_Z80_SRA reg_r {
+ out_AbsByte(0xCB);
+ out_AbsByte(0x28 | $2);
+ }
+;
+
+z80_srl : T_Z80_SRL reg_r {
+ out_AbsByte(0xCB);
+ out_AbsByte(0x38 | $2);
+ }
+;
+
+z80_stop : T_Z80_STOP {
+ out_AbsByte(0x10);
+ out_AbsByte(0x00);
+ }
+ | T_Z80_STOP reloc_8bit {
+ out_AbsByte(0x10);
+ out_RelByte(&$2);
+ }
+;
+
+z80_sub : T_Z80_SUB op_a_n {
+ out_AbsByte(0xD6);
+ out_RelByte(&$2);
+ }
+ | T_Z80_SUB op_a_r { out_AbsByte(0x90 | $2);
+ }
+;
+
+z80_swap : T_Z80_SWAP reg_r {
+ out_AbsByte(0xCB);
+ out_AbsByte(0x30 | $2);
+ }
+;
+
+z80_xor : T_Z80_XOR op_a_n {
+ out_AbsByte(0xEE);
+ out_RelByte(&$2);
+ }
+ | T_Z80_XOR op_a_r { out_AbsByte(0xA8 | $2); }
+;
+
+op_mem_ind : '[' reloc_16bit ']' { $$ = $2; }
+;
+
+op_hl_ss : reg_ss { $$ = $1; }
+ | T_MODE_HL ',' reg_ss { $$ = $3; }
+;
+
+op_a_r : reg_r { $$ = $1; }
+ | T_MODE_A ',' reg_r { $$ = $3; }
+;
+
+op_a_n : reloc_8bit { $$ = $1; }
+ | T_MODE_A ',' reloc_8bit { $$ = $3; }
+;
+
+T_MODE_A : T_TOKEN_A
+ | T_OP_HIGH '(' T_MODE_AF ')'
+;
+
+T_MODE_B : T_TOKEN_B
+ | T_OP_HIGH '(' T_MODE_BC ')'
+;
+
+T_MODE_C : T_TOKEN_C
+ | T_OP_LOW '(' T_MODE_BC ')'
+;
+
+T_MODE_D : T_TOKEN_D
+ | T_OP_HIGH '(' T_MODE_DE ')'
+;
+
+T_MODE_E : T_TOKEN_E
+ | T_OP_LOW '(' T_MODE_DE ')'
+;
+
+T_MODE_H : T_TOKEN_H
+ | T_OP_HIGH '(' T_MODE_HL ')'
+;
+
+T_MODE_L : T_TOKEN_L
+ | T_OP_LOW '(' T_MODE_HL ')'
+;
+
+ccode : T_CC_NZ { $$ = CC_NZ; }
+ | T_CC_Z { $$ = CC_Z; }
+ | T_CC_NC { $$ = CC_NC; }
+ | T_TOKEN_C { $$ = CC_C; }
+;
+
+reg_r : T_MODE_B { $$ = REG_B; }
+ | T_MODE_C { $$ = REG_C; }
+ | T_MODE_D { $$ = REG_D; }
+ | T_MODE_E { $$ = REG_E; }
+ | T_MODE_H { $$ = REG_H; }
+ | T_MODE_L { $$ = REG_L; }
+ | '[' T_MODE_HL ']' { $$ = REG_HL_IND; }
+ | T_MODE_A { $$ = REG_A; }
+;
+
+reg_tt : T_MODE_BC { $$ = REG_BC; }
+ | T_MODE_DE { $$ = REG_DE; }
+ | T_MODE_HL { $$ = REG_HL; }
+ | T_MODE_AF { $$ = REG_AF; }
+;
+
+reg_ss : T_MODE_BC { $$ = REG_BC; }
+ | T_MODE_DE { $$ = REG_DE; }
+ | T_MODE_HL { $$ = REG_HL; }
+ | T_MODE_SP { $$ = REG_SP; }
+;
+
+reg_rr : '[' T_MODE_BC ']' { $$ = REG_BC_IND; }
+ | '[' T_MODE_DE ']' { $$ = REG_DE_IND; }
+ | hl_ind_inc { $$ = REG_HL_INDINC; }
+ | hl_ind_dec { $$ = REG_HL_INDDEC; }
+;
+
+hl_ind_inc : '[' T_MODE_HL_INC ']'
+ | '[' T_MODE_HL T_OP_ADD ']'
+;
+
+hl_ind_dec : '[' T_MODE_HL_DEC ']'
+ | '[' T_MODE_HL T_OP_SUB ']'
+;
+
+%%