mirror of
https://github.com/gbdev/rgbds.git
synced 2025-11-23 11:32:07 +00:00
Get rid of Hungarian notation Improve encapsulation (the rest of the world should not touch PC directly)
2018 lines
40 KiB
Plaintext
2018 lines
40 KiB
Plaintext
/*
|
|
* 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 <stdbool.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <strings.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"
|
|
|
|
uint32_t nListCountEmpty;
|
|
char *tzNewMacro;
|
|
uint32_t ulNewMacroSize;
|
|
int32_t nPCOffset;
|
|
|
|
size_t symvaluetostring(char *dest, size_t maxLength, char *symName,
|
|
const char *mode)
|
|
{
|
|
size_t length;
|
|
struct Symbol *sym = sym_FindSymbol(symName);
|
|
|
|
if (sym && sym->type == SYM_EQUS) {
|
|
char const *src = sym_GetStringValue(sym);
|
|
size_t i;
|
|
|
|
if (mode)
|
|
yyerror("Print types are only allowed for numbers");
|
|
|
|
for (i = 0; src[i] != 0; i++) {
|
|
if (i >= maxLength)
|
|
fatalerror("Symbol value too long to fit buffer");
|
|
|
|
dest[i] = src[i];
|
|
}
|
|
|
|
length = i;
|
|
|
|
} else {
|
|
uint32_t value = sym_GetConstantValue(symName);
|
|
int32_t fullLength;
|
|
|
|
/* Special cheat for binary */
|
|
if (mode && !mode[0]) {
|
|
char binary[33]; /* 32 bits + 1 terminator */
|
|
char *write_ptr = binary + 32;
|
|
fullLength = 0;
|
|
binary[32] = 0;
|
|
do {
|
|
*(--write_ptr) = (value & 1) + '0';
|
|
value >>= 1;
|
|
fullLength++;
|
|
} while(value);
|
|
strncpy(dest, write_ptr, maxLength + 1);
|
|
} else {
|
|
fullLength = snprintf(dest, maxLength + 1,
|
|
mode ? mode : "$%X",
|
|
value);
|
|
}
|
|
|
|
if (fullLength < 0) {
|
|
fatalerror("snprintf encoding error");
|
|
} else {
|
|
length = (size_t)fullLength;
|
|
if (length > maxLength)
|
|
fatalerror("Symbol value too long to fit buffer");
|
|
}
|
|
}
|
|
|
|
return length;
|
|
}
|
|
|
|
static uint32_t str2int2(char *s, int32_t length)
|
|
{
|
|
int32_t i;
|
|
uint32_t r = 0;
|
|
|
|
i = length < 4 ? 0 : length - 4;
|
|
while (i < length) {
|
|
r <<= 8;
|
|
r |= (uint8_t)s[i];
|
|
i++;
|
|
}
|
|
|
|
return r;
|
|
}
|
|
|
|
static uint32_t isWhiteSpace(char s)
|
|
{
|
|
return (s == ' ') || (s == '\t') || (s == '\0') || (s == '\n');
|
|
}
|
|
|
|
static uint32_t isRept(char *s)
|
|
{
|
|
return (strncasecmp(s, "REPT", 4) == 0)
|
|
&& isWhiteSpace(*(s - 1)) && isWhiteSpace(s[4]);
|
|
}
|
|
|
|
static uint32_t isEndr(char *s)
|
|
{
|
|
return (strncasecmp(s, "ENDR", 4) == 0)
|
|
&& isWhiteSpace(*(s - 1)) && isWhiteSpace(s[4]);
|
|
}
|
|
|
|
static void copyrept(void)
|
|
{
|
|
int32_t level = 1, len, instring = 0;
|
|
char *src = pCurrentBuffer->pBuffer;
|
|
char *bufferEnd = pCurrentBuffer->pBufferStart
|
|
+ pCurrentBuffer->nBufferSize;
|
|
|
|
while (src < bufferEnd && level) {
|
|
if (instring == 0) {
|
|
if (isRept(src)) {
|
|
level++;
|
|
src += 4;
|
|
} else if (isEndr(src)) {
|
|
level--;
|
|
src += 4;
|
|
} else {
|
|
if (*src == '\"')
|
|
instring = 1;
|
|
src++;
|
|
}
|
|
} else {
|
|
if (*src == '\\') {
|
|
src += 2;
|
|
} else if (*src == '\"') {
|
|
src++;
|
|
instring = 0;
|
|
} else {
|
|
src++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (level != 0)
|
|
fatalerror("Unterminated REPT block");
|
|
|
|
len = src - pCurrentBuffer->pBuffer - 4;
|
|
|
|
src = pCurrentBuffer->pBuffer;
|
|
ulNewMacroSize = len;
|
|
|
|
tzNewMacro = malloc(ulNewMacroSize + 1);
|
|
|
|
if (tzNewMacro == NULL)
|
|
fatalerror("Not enough memory for REPT block.");
|
|
|
|
uint32_t i;
|
|
|
|
tzNewMacro[ulNewMacroSize] = 0;
|
|
for (i = 0; i < ulNewMacroSize; i++) {
|
|
tzNewMacro[i] = src[i];
|
|
if (src[i] == '\n')
|
|
nLineNo++;
|
|
}
|
|
|
|
yyskipbytes(ulNewMacroSize + 4);
|
|
|
|
}
|
|
|
|
static uint32_t isMacro(char *s)
|
|
{
|
|
return (strncasecmp(s, "MACRO", 4) == 0)
|
|
&& isWhiteSpace(*(s - 1)) && isWhiteSpace(s[5]);
|
|
}
|
|
|
|
static uint32_t isEndm(char *s)
|
|
{
|
|
return (strncasecmp(s, "ENDM", 4) == 0)
|
|
&& isWhiteSpace(*(s - 1)) && isWhiteSpace(s[4]);
|
|
}
|
|
|
|
static void copymacro(void)
|
|
{
|
|
int32_t level = 1, len, instring = 0;
|
|
char *src = pCurrentBuffer->pBuffer;
|
|
char *bufferEnd = pCurrentBuffer->pBufferStart
|
|
+ pCurrentBuffer->nBufferSize;
|
|
|
|
while (src < bufferEnd && level) {
|
|
if (instring == 0) {
|
|
if (isMacro(src)) {
|
|
level++;
|
|
src += 4;
|
|
} else if (isEndm(src)) {
|
|
level--;
|
|
src += 4;
|
|
} else {
|
|
if(*src == '\"')
|
|
instring = 1;
|
|
src++;
|
|
}
|
|
} else {
|
|
if (*src == '\\') {
|
|
src += 2;
|
|
} else if (*src == '\"') {
|
|
src++;
|
|
instring = 0;
|
|
} else {
|
|
src++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (level != 0)
|
|
fatalerror("Unterminated MACRO definition.");
|
|
|
|
len = src - pCurrentBuffer->pBuffer - 4;
|
|
|
|
src = pCurrentBuffer->pBuffer;
|
|
ulNewMacroSize = len;
|
|
|
|
tzNewMacro = (char *)malloc(ulNewMacroSize + 1);
|
|
if (tzNewMacro == NULL)
|
|
fatalerror("Not enough memory for MACRO definition.");
|
|
|
|
uint32_t i;
|
|
|
|
tzNewMacro[ulNewMacroSize] = 0;
|
|
for (i = 0; i < ulNewMacroSize; i++) {
|
|
tzNewMacro[i] = src[i];
|
|
if (src[i] == '\n')
|
|
nLineNo++;
|
|
}
|
|
|
|
yyskipbytes(ulNewMacroSize + 4);
|
|
}
|
|
|
|
static bool endsIf(char c)
|
|
{
|
|
return isWhiteSpace(c) || c == '(' || c == '{';
|
|
}
|
|
|
|
static uint32_t isIf(char *s)
|
|
{
|
|
return (strncasecmp(s, "IF", 2) == 0)
|
|
&& isWhiteSpace(s[-1]) && endsIf(s[2]);
|
|
}
|
|
|
|
static uint32_t isElif(char *s)
|
|
{
|
|
return (strncasecmp(s, "ELIF", 4) == 0)
|
|
&& isWhiteSpace(s[-1]) && endsIf(s[4]);
|
|
}
|
|
|
|
static uint32_t isElse(char *s)
|
|
{
|
|
return (strncasecmp(s, "ELSE", 4) == 0)
|
|
&& isWhiteSpace(s[-1]) && isWhiteSpace(s[4]);
|
|
}
|
|
|
|
static uint32_t isEndc(char *s)
|
|
{
|
|
return (strncasecmp(s, "ENDC", 4) == 0)
|
|
&& isWhiteSpace(s[-1]) && isWhiteSpace(s[4]);
|
|
}
|
|
|
|
static void if_skip_to_else(void)
|
|
{
|
|
int32_t level = 1;
|
|
bool inString = false;
|
|
char *src = pCurrentBuffer->pBuffer;
|
|
|
|
while (*src && level) {
|
|
if (*src == '\n')
|
|
nLineNo++;
|
|
|
|
if (!inString) {
|
|
if (isIf(src)) {
|
|
level++;
|
|
src += 2;
|
|
|
|
} else if (level == 1 && isElif(src)) {
|
|
level--;
|
|
skipElif = false;
|
|
|
|
} else if (level == 1 && isElse(src)) {
|
|
level--;
|
|
src += 4;
|
|
|
|
} else if (isEndc(src)) {
|
|
level--;
|
|
if (level != 0)
|
|
src += 4;
|
|
|
|
} else {
|
|
if (*src == '\"')
|
|
inString = true;
|
|
src++;
|
|
}
|
|
} else {
|
|
if (*src == '\"') {
|
|
inString = false;
|
|
} else if (*src == '\\') {
|
|
/* Escaped quotes don't end the string */
|
|
if (*++src != '\"')
|
|
src--;
|
|
}
|
|
src++;
|
|
}
|
|
}
|
|
|
|
if (level != 0)
|
|
fatalerror("Unterminated IF construct");
|
|
|
|
int32_t len = src - pCurrentBuffer->pBuffer;
|
|
|
|
yyskipbytes(len);
|
|
yyunput('\n');
|
|
nLineNo--;
|
|
}
|
|
|
|
static void if_skip_to_endc(void)
|
|
{
|
|
int32_t level = 1;
|
|
bool inString = false;
|
|
char *src = pCurrentBuffer->pBuffer;
|
|
|
|
while (*src && level) {
|
|
if (*src == '\n')
|
|
nLineNo++;
|
|
|
|
if (!inString) {
|
|
if (isIf(src)) {
|
|
level++;
|
|
src += 2;
|
|
} else if (isEndc(src)) {
|
|
level--;
|
|
if (level != 0)
|
|
src += 4;
|
|
} else {
|
|
if (*src == '\"')
|
|
inString = true;
|
|
src++;
|
|
}
|
|
} else {
|
|
if (*src == '\"') {
|
|
inString = false;
|
|
} else if (*src == '\\') {
|
|
/* Escaped quotes don't end the string */
|
|
if (*++src != '\"')
|
|
src--;
|
|
}
|
|
src++;
|
|
}
|
|
}
|
|
|
|
if (level != 0)
|
|
fatalerror("Unterminated IF construct");
|
|
|
|
int32_t len = src - pCurrentBuffer->pBuffer;
|
|
|
|
yyskipbytes(len);
|
|
yyunput('\n');
|
|
nLineNo--;
|
|
}
|
|
|
|
static void startUnion(void)
|
|
{
|
|
if (!pCurrentSection)
|
|
fatalerror("UNIONs must be inside a SECTION");
|
|
|
|
uint32_t unionIndex = nUnionDepth;
|
|
|
|
nUnionDepth++;
|
|
if (nUnionDepth > MAXUNIONS)
|
|
fatalerror("Too many nested UNIONs");
|
|
|
|
unionStart[unionIndex] = curOffset;
|
|
unionSize[unionIndex] = 0;
|
|
}
|
|
|
|
static void updateUnion(void)
|
|
{
|
|
uint32_t unionIndex = nUnionDepth - 1;
|
|
uint32_t size = curOffset - unionStart[unionIndex];
|
|
|
|
if (size > unionSize[unionIndex])
|
|
unionSize[unionIndex] = size;
|
|
|
|
curOffset = unionStart[unionIndex];
|
|
}
|
|
|
|
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");
|
|
break;
|
|
case 0:
|
|
len++;
|
|
break;
|
|
}
|
|
s++;
|
|
}
|
|
|
|
/* Check for partial code point. */
|
|
if (state != 0)
|
|
fatalerror("STRLEN: Invalid UTF-8 character");
|
|
|
|
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");
|
|
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");
|
|
break;
|
|
case 0:
|
|
curPos++;
|
|
break;
|
|
}
|
|
srcIndex++;
|
|
}
|
|
|
|
if (!src[srcIndex])
|
|
warning(WARNING_BUILTIN_ARG, "STRSUB: Position %lu is past the end of the string",
|
|
(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");
|
|
break;
|
|
case 0:
|
|
curLen++;
|
|
break;
|
|
}
|
|
dest[destIndex++] = src[srcIndex++];
|
|
}
|
|
|
|
if (curLen < len)
|
|
warning(WARNING_BUILTIN_ARG, "STRSUB: Length too big: %lu", (unsigned long)len);
|
|
|
|
/* Check for partial code point. */
|
|
if (state != 0)
|
|
fatalerror("STRSUB: Invalid UTF-8 character");
|
|
|
|
dest[destIndex] = 0;
|
|
}
|
|
|
|
%}
|
|
|
|
%union
|
|
{
|
|
char tzSym[MAXSYMLEN + 1];
|
|
char tzString[MAXSTRLEN + 1];
|
|
struct Expression sVal;
|
|
int32_t nConstValue;
|
|
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
|
|
%type <tzSym> scoped_label
|
|
%type <tzSym> scoped_label_bare
|
|
%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
|
|
%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 <nConstValue> sectunion
|
|
%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 T_MODE_BC_IND
|
|
%token T_MODE_DE T_MODE_DE_IND
|
|
%token T_MODE_SP
|
|
%token T_MODE_C_IND
|
|
%token T_MODE_HL T_MODE_HL_IND T_MODE_HL_INDDEC T_MODE_HL_INDINC
|
|
%token T_CC_NZ T_CC_Z T_CC_NC
|
|
|
|
%type <nConstValue> reg_r
|
|
%type <nConstValue> reg_ss
|
|
%type <nConstValue> reg_rr
|
|
%type <nConstValue> reg_tt
|
|
%type <nConstValue> ccode
|
|
%type <sVal> op_a_n
|
|
%type <nConstValue> op_a_r
|
|
%type <nConstValue> op_hl_ss
|
|
%type <sVal> op_mem_ind
|
|
%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 '\n' {
|
|
nLineNo++;
|
|
nTotalLines++;
|
|
}
|
|
;
|
|
|
|
line : label
|
|
| label cpu_command
|
|
| label macro
|
|
| label simple_pseudoop
|
|
| pseudoop
|
|
;
|
|
|
|
scoped_label_bare : T_LABEL {
|
|
warning(WARNING_OBSOLETE, "Non-local labels without a colon are deprecated");
|
|
strcpy($$, $1);
|
|
}
|
|
| T_LOCAL_ID {
|
|
strcpy($$, $1);
|
|
}
|
|
;
|
|
scoped_label : T_LABEL ':' {
|
|
strcpy($$, $1);
|
|
}
|
|
| T_LOCAL_ID ':' {
|
|
strcpy($$, $1);
|
|
}
|
|
;
|
|
scoped_id : T_ID | T_LOCAL_ID ;
|
|
|
|
label : /* empty */
|
|
| scoped_label_bare {
|
|
if ($1[0] == '.')
|
|
sym_AddLocalReloc($1);
|
|
else
|
|
sym_AddReloc($1);
|
|
}
|
|
| scoped_label {
|
|
if ($1[0] == '.')
|
|
sym_AddLocalReloc($1);
|
|
else
|
|
sym_AddReloc($1);
|
|
}
|
|
| scoped_label ':' {
|
|
if ($1[0] == '.')
|
|
sym_AddLocalReloc($1);
|
|
else
|
|
sym_AddReloc($1);
|
|
sym_Export($1);
|
|
}
|
|
;
|
|
|
|
macro : T_ID {
|
|
yy_set_state(LEX_STATE_MACROARGS);
|
|
} macroargs {
|
|
yy_set_state(LEX_STATE_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
|
|
| if
|
|
| elif
|
|
| else
|
|
| endc
|
|
| 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)
|
|
yyerror("Alignment must be between 0 and 16, not %u",
|
|
$2);
|
|
else
|
|
sect_AlignPC($2, 0);
|
|
}
|
|
| T_OP_ALIGN uconst ',' uconst {
|
|
if ($2 > 16)
|
|
yyerror("Alignment must be between 0 and 16, not %u",
|
|
$2);
|
|
else if ($4 >= 1 << $2)
|
|
yyerror("Offset must be between 0 and %u, not %u",
|
|
(1 << $2) - 1, $4);
|
|
else
|
|
sect_AlignPC($2, $4);
|
|
}
|
|
;
|
|
|
|
opt : T_POP_OPT {
|
|
yy_set_state(LEX_STATE_MACROARGS);
|
|
} opt_list {
|
|
yy_set_state(LEX_STATE_NORMAL);
|
|
}
|
|
;
|
|
|
|
opt_list : opt_list_entry
|
|
| opt_list ',' 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", $2); }
|
|
;
|
|
|
|
warn : T_POP_WARN string { warning(WARNING_USER, "%s", $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()))
|
|
yyerror("Assertion creation failed: %s",
|
|
strerror(errno));
|
|
} else if ($3.nVal == 0) {
|
|
switch ($2) {
|
|
case ASSERT_FATAL:
|
|
fatalerror("Assertion failed");
|
|
case ASSERT_ERROR:
|
|
yyerror("Assertion failed");
|
|
break;
|
|
case ASSERT_WARN:
|
|
warning(WARNING_ASSERT,
|
|
"Assertion failed");
|
|
break;
|
|
}
|
|
}
|
|
rpn_Free(&$3);
|
|
}
|
|
| T_POP_ASSERT assert_type relocexpr ',' string
|
|
{
|
|
if (!rpn_isKnown(&$3)) {
|
|
if (!out_CreateAssert($2, &$3, $5,
|
|
sect_GetOutputOffset()))
|
|
yyerror("Assertion creation failed: %s",
|
|
strerror(errno));
|
|
} else if ($3.nVal == 0) {
|
|
switch ($2) {
|
|
case ASSERT_FATAL:
|
|
fatalerror("Assertion failed: %s",
|
|
$5);
|
|
case ASSERT_ERROR:
|
|
yyerror("Assertion failed: %s",
|
|
$5);
|
|
break;
|
|
case ASSERT_WARN:
|
|
warning(WARNING_ASSERT,
|
|
"Assertion failed: %s",
|
|
$5);
|
|
break;
|
|
}
|
|
}
|
|
rpn_Free(&$3);
|
|
}
|
|
| T_POP_STATIC_ASSERT assert_type const
|
|
{
|
|
if ($3 == 0) {
|
|
switch ($2) {
|
|
case ASSERT_FATAL:
|
|
fatalerror("Assertion failed");
|
|
case ASSERT_ERROR:
|
|
yyerror("Assertion failed");
|
|
break;
|
|
case ASSERT_WARN:
|
|
warning(WARNING_ASSERT,
|
|
"Assertion failed");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
| T_POP_STATIC_ASSERT assert_type const ',' string
|
|
{
|
|
if ($3 == 0) {
|
|
switch ($2) {
|
|
case ASSERT_FATAL:
|
|
fatalerror("Assertion failed: %s",
|
|
$5);
|
|
case ASSERT_ERROR:
|
|
yyerror("Assertion failed: %s",
|
|
$5);
|
|
break;
|
|
case ASSERT_WARN:
|
|
warning(WARNING_ASSERT,
|
|
"Assertion failed: %s",
|
|
$5);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
;
|
|
|
|
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 = nLineNo;
|
|
copyrept();
|
|
fstk_RunRept($2, nDefinitionLineNo);
|
|
}
|
|
;
|
|
|
|
macrodef : T_LABEL ':' T_POP_MACRO {
|
|
int32_t nDefinitionLineNo = nLineNo;
|
|
copymacro();
|
|
sym_AddMacro($1, nDefinitionLineNo);
|
|
}
|
|
;
|
|
|
|
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 { startUnion(); }
|
|
;
|
|
|
|
nextu : T_POP_NEXTU {
|
|
if (nUnionDepth <= 0)
|
|
fatalerror("Found NEXTU outside of a UNION construct");
|
|
|
|
updateUnion();
|
|
}
|
|
;
|
|
|
|
endu : T_POP_ENDU {
|
|
if (nUnionDepth <= 0)
|
|
fatalerror("Found ENDU outside of a UNION construct");
|
|
|
|
updateUnion();
|
|
|
|
nUnionDepth--;
|
|
curOffset = unionStart[nUnionDepth] + unionSize[nUnionDepth];
|
|
}
|
|
;
|
|
|
|
ds : T_POP_DS uconst { out_Skip($2); }
|
|
| 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).");
|
|
}
|
|
| 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).");
|
|
}
|
|
| 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).");
|
|
}
|
|
| T_POP_DL constlist_32bit_entry
|
|
;
|
|
|
|
purge : T_POP_PURGE {
|
|
oDontExpandStrings = true;
|
|
} purge_list {
|
|
oDontExpandStrings = false;
|
|
}
|
|
;
|
|
|
|
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`");
|
|
}
|
|
| T_POP_XDEF {
|
|
warning(WARNING_OBSOLETE, "`XDEF` is a deprecated synonym for `EXPORT`");
|
|
}
|
|
;
|
|
|
|
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);
|
|
if (oFailedOnMissingInclude)
|
|
YYACCEPT;
|
|
}
|
|
| T_POP_INCBIN string ',' uconst ',' uconst {
|
|
out_BinaryFileSlice($2, $4, $6);
|
|
if (oFailedOnMissingInclude)
|
|
YYACCEPT;
|
|
}
|
|
;
|
|
|
|
charmap : T_POP_CHARMAP string ',' const {
|
|
if (($4 & 0xFF) != $4)
|
|
warning(WARNING_TRUNCATION, "Expression must be 8-bit");
|
|
|
|
if (charmap_Add($2, $4 & 0xFF) == -1)
|
|
yyerror("Error parsing charmap. Either you've added too many (%i), or the input character length is too long (%i)' : %s\n", MAXCHARMAPS, CHARMAPLENGTH, strerror(errno));
|
|
}
|
|
;
|
|
|
|
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("$%X", $2); }
|
|
;
|
|
|
|
printi : T_POP_PRINTI const { printf("%d", $2); }
|
|
;
|
|
|
|
printf : T_POP_PRINTF const { math_Print($2); }
|
|
;
|
|
|
|
if : T_POP_IF const {
|
|
nIFDepth++;
|
|
if (!$2)
|
|
if_skip_to_else();
|
|
}
|
|
;
|
|
|
|
elif : T_POP_ELIF const {
|
|
if (nIFDepth <= 0)
|
|
fatalerror("Found ELIF outside an IF construct");
|
|
|
|
if (skipElif) {
|
|
/*
|
|
* Executed when ELIF is reached at the end of
|
|
* an IF or ELIF block for which the condition
|
|
* was true.
|
|
*
|
|
* Continue parsing at ENDC keyword
|
|
*/
|
|
if_skip_to_endc();
|
|
} else {
|
|
/*
|
|
* Executed when ELIF is skipped to because the
|
|
* condition of the previous IF or ELIF block
|
|
* was false.
|
|
*/
|
|
skipElif = true;
|
|
|
|
if (!$2) {
|
|
/*
|
|
* Continue parsing after ELSE, or at
|
|
* ELIF or ENDC keyword.
|
|
*/
|
|
if_skip_to_else();
|
|
}
|
|
}
|
|
}
|
|
;
|
|
|
|
else : T_POP_ELSE {
|
|
if (nIFDepth <= 0)
|
|
fatalerror("Found ELSE outside an IF construct");
|
|
|
|
/* Continue parsing at ENDC keyword */
|
|
if_skip_to_endc();
|
|
}
|
|
;
|
|
|
|
endc : T_POP_ENDC {
|
|
if (nIFDepth <= 0)
|
|
fatalerror("Found ENDC outside an IF construct");
|
|
|
|
nIFDepth--;
|
|
}
|
|
;
|
|
|
|
const_3bit : const {
|
|
int32_t value = $1;
|
|
|
|
if ((value < 0) || (value > 7)) {
|
|
yyerror("Immediate value must be 3-bit");
|
|
$$ = 0;
|
|
} else {
|
|
$$ = value & 0x7;
|
|
}
|
|
}
|
|
;
|
|
|
|
constlist_8bit : constlist_8bit_entry
|
|
| constlist_8bit ',' constlist_8bit_entry
|
|
;
|
|
|
|
constlist_8bit_entry : /* empty */ {
|
|
out_Skip(1);
|
|
nListCountEmpty++;
|
|
}
|
|
| reloc_8bit_no_str { out_RelByte(&$1); }
|
|
| string {
|
|
char *s = $1;
|
|
int32_t length = charmap_Convert(&s);
|
|
|
|
out_AbsByteGroup((uint8_t*)s, length);
|
|
free(s);
|
|
}
|
|
;
|
|
|
|
constlist_16bit : constlist_16bit_entry
|
|
| constlist_16bit ',' constlist_16bit_entry
|
|
;
|
|
|
|
constlist_16bit_entry : /* empty */ {
|
|
out_Skip(2);
|
|
nListCountEmpty++;
|
|
}
|
|
| reloc_16bit { out_RelWord(&$1); }
|
|
;
|
|
|
|
constlist_32bit : constlist_32bit_entry
|
|
| constlist_32bit ',' constlist_32bit_entry
|
|
;
|
|
|
|
constlist_32bit_entry : /* empty */ {
|
|
out_Skip(4);
|
|
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");
|
|
$$ = $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");
|
|
$$ = $1;
|
|
}
|
|
;
|
|
|
|
reloc_16bit : relocexpr {
|
|
if (rpn_isKnown(&$1)
|
|
&& ($1.nVal < -32768 || $1.nVal > 65535))
|
|
warning(WARNING_TRUNCATION, "Expression must be 16-bit");
|
|
$$ = $1;
|
|
}
|
|
;
|
|
|
|
|
|
relocexpr : relocexpr_no_str
|
|
| string {
|
|
char *s = $1;
|
|
int32_t length = charmap_Convert(&s);
|
|
uint32_t r = str2int2(s, length);
|
|
|
|
free(s);
|
|
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 {
|
|
oDontExpandStrings = true;
|
|
} '(' scoped_id ')' {
|
|
struct Symbol const *sym = sym_FindSymbol($4);
|
|
|
|
rpn_Number(&$$, !!sym);
|
|
|
|
oDontExpandStrings = false;
|
|
}
|
|
| T_OP_ROUND '(' const ')' {
|
|
rpn_Number(&$$, math_Round($3));
|
|
}
|
|
| T_OP_CEIL '(' const ')' {
|
|
rpn_Number(&$$, math_Ceil($3));
|
|
}
|
|
| T_OP_FLOOR '(' const ')' {
|
|
rpn_Number(&$$, math_Floor($3));
|
|
}
|
|
| T_OP_FDIV '(' const ',' const ')' {
|
|
rpn_Number(&$$, math_Div($3, $5));
|
|
}
|
|
| T_OP_FMUL '(' const ',' const ')' {
|
|
rpn_Number(&$$, math_Mul($3, $5));
|
|
}
|
|
| T_OP_SIN '(' const ')' {
|
|
rpn_Number(&$$, math_Sin($3));
|
|
}
|
|
| T_OP_COS '(' const ')' {
|
|
rpn_Number(&$$, math_Cos($3));
|
|
}
|
|
| T_OP_TAN '(' const ')' {
|
|
rpn_Number(&$$, math_Tan($3));
|
|
}
|
|
| T_OP_ASIN '(' const ')' {
|
|
rpn_Number(&$$, math_ASin($3));
|
|
}
|
|
| T_OP_ACOS '(' const ')' {
|
|
rpn_Number(&$$, math_ACos($3));
|
|
}
|
|
| T_OP_ATAN '(' const ')' {
|
|
rpn_Number(&$$, math_ATan($3));
|
|
}
|
|
| T_OP_ATAN2 '(' const ',' const ')' {
|
|
rpn_Number(&$$, math_ATan2($3, $5));
|
|
}
|
|
| T_OP_STRCMP '(' string ',' string ')' {
|
|
rpn_Number(&$$, strcmp($3, $5));
|
|
}
|
|
| T_OP_STRIN '(' string ',' string ')' {
|
|
char *p = 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",
|
|
$1);
|
|
}
|
|
;
|
|
|
|
const : relocexpr {
|
|
if (!rpn_isKnown(&$1)) {
|
|
yyerror("Expected constant expression: %s",
|
|
$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'",
|
|
$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'",
|
|
$3, $5);
|
|
}
|
|
| T_OP_STRUPR '(' string ')' {
|
|
if (snprintf($$, MAXSTRLEN + 1, "%s", $3) > MAXSTRLEN)
|
|
warning(WARNING_LONG_STR, "STRUPR: String too long '%s'",
|
|
$3);
|
|
|
|
upperstring($$);
|
|
}
|
|
| T_OP_STRLWR '(' string ')' {
|
|
if (snprintf($$, MAXSTRLEN + 1, "%s", $3) > MAXSTRLEN)
|
|
warning(WARNING_LONG_STR, "STRUPR: String too long '%s'",
|
|
$3);
|
|
|
|
lowerstring($$);
|
|
}
|
|
;
|
|
|
|
section : T_POP_SECTION sectunion string ',' sectiontype sectorg sectattrs {
|
|
out_NewSection($3, $5, $6, &$7, $2);
|
|
}
|
|
;
|
|
|
|
sectunion : /* empty */ { $$ = false; }
|
|
| T_POP_UNION { $$ = true; }
|
|
;
|
|
|
|
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) {
|
|
yyerror("Address $%x is not 16-bit", $2);
|
|
$$ = -1;
|
|
} else {
|
|
$$ = $2;
|
|
}
|
|
}
|
|
;
|
|
|
|
sectattrs : /* empty */ {
|
|
$$.alignment = 0;
|
|
$$.alignOfs = 0;
|
|
$$.bank = -1;
|
|
}
|
|
| sectattrs ',' T_OP_ALIGN '[' uconst ']' {
|
|
if ($5 > 16)
|
|
yyerror("Alignment must be between 0 and 16, not %u",
|
|
$5);
|
|
else
|
|
$$.alignment = $5;
|
|
}
|
|
| sectattrs ',' T_OP_ALIGN '[' uconst ',' uconst ']' {
|
|
if ($5 > 16) {
|
|
yyerror("Alignment must be between 0 and 16, not %u",
|
|
$5);
|
|
} else {
|
|
$$.alignment = $5;
|
|
if ($7 >= 1 << $$.alignment)
|
|
yyerror("Alignment offset must not be greater than alignment (%u < %u)",
|
|
$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 (CurrentOptions.haltnop)
|
|
out_AbsByte(0x00);
|
|
}
|
|
;
|
|
|
|
z80_inc : T_Z80_INC reg_r { out_AbsByte(0x04 | ($2 << 3)); }
|
|
| T_Z80_INC reg_ss { out_AbsByte(0x03 | ($2 << 4)); }
|
|
;
|
|
|
|
z80_jp : T_Z80_JP 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_IND ',' T_MODE_A {
|
|
out_AbsByte(0x02 | (2 << 4));
|
|
}
|
|
| T_Z80_LDI T_MODE_A ',' T_MODE_HL_IND {
|
|
out_AbsByte(0x0A | (2 << 4));
|
|
}
|
|
;
|
|
|
|
z80_ldd : T_Z80_LDD T_MODE_HL_IND ',' T_MODE_A {
|
|
out_AbsByte(0x02 | (3 << 4));
|
|
}
|
|
| T_Z80_LDD T_MODE_A ',' T_MODE_HL_IND {
|
|
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 ',' T_MODE_C_IND {
|
|
out_AbsByte(0xF2);
|
|
}
|
|
| T_Z80_LDIO T_MODE_C_IND ',' T_MODE_A {
|
|
out_AbsByte(0xE2);
|
|
}
|
|
;
|
|
|
|
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 (CurrentOptions.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 T_MODE_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))
|
|
yyerror("LD [HL],[HL] not a valid instruction");
|
|
else
|
|
out_AbsByte(0x40 | ($2 << 3) | $4);
|
|
}
|
|
;
|
|
|
|
z80_ld_a : T_Z80_LD reg_r ',' T_MODE_C_IND {
|
|
if ($2 == REG_A)
|
|
out_AbsByte(0xF2);
|
|
else
|
|
yyerror("Destination operand must be A");
|
|
}
|
|
| T_Z80_LD reg_r ',' reg_rr {
|
|
if ($2 == REG_A)
|
|
out_AbsByte(0x0A | ($4 << 4));
|
|
else
|
|
yyerror("Destination operand must be A");
|
|
}
|
|
| T_Z80_LD reg_r ',' op_mem_ind {
|
|
if ($2 == REG_A) {
|
|
if (CurrentOptions.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 {
|
|
yyerror("Destination operand must be A");
|
|
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_IND { $$ = REG_HL_IND; }
|
|
| T_MODE_A { $$ = REG_A; }
|
|
;
|
|
|
|
reg_tt : T_MODE_BC { $$ = REG_BC; }
|
|
| T_MODE_DE { $$ = REG_DE; }
|
|
| T_MODE_HL { $$ = REG_HL; }
|
|
| T_MODE_AF { $$ = REG_AF; }
|
|
;
|
|
|
|
reg_ss : T_MODE_BC { $$ = REG_BC; }
|
|
| T_MODE_DE { $$ = REG_DE; }
|
|
| T_MODE_HL { $$ = REG_HL; }
|
|
| T_MODE_SP { $$ = REG_SP; }
|
|
;
|
|
|
|
reg_rr : T_MODE_BC_IND { $$ = REG_BC_IND; }
|
|
| T_MODE_DE_IND { $$ = REG_DE_IND; }
|
|
| T_MODE_HL_INDINC { $$ = REG_HL_INDINC; }
|
|
| T_MODE_HL_INDDEC { $$ = REG_HL_INDDEC; }
|
|
;
|
|
|
|
%%
|