mirror of
https://github.com/gbdev/rgbds.git
synced 2025-11-20 10:12:06 +00:00
This avoids redundancy between them (and also having to port fixes and features) The error messages have been preserved through a string reporting mechanism
2145 lines
39 KiB
Plaintext
2145 lines
39 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/main.h"
|
|
#include "asm/mymath.h"
|
|
#include "asm/output.h"
|
|
#include "asm/rpn.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;
|
|
|
|
static void bankrangecheck(char *name, uint32_t secttype, int32_t org,
|
|
int32_t bank)
|
|
{
|
|
if (secttype != SECTTYPE_ROMX && secttype != SECTTYPE_VRAM
|
|
&& secttype != SECTTYPE_SRAM && secttype != SECTTYPE_WRAMX)
|
|
yyerror("BANK only allowed for ROMX, WRAMX, SRAM, or VRAM sections");
|
|
else if (bank < bankranges[secttype][0] || bank > bankranges[secttype][1])
|
|
yyerror("%s bank value $%x out of range ($%x to $%x)",
|
|
typeNames[secttype], bank, bankranges[secttype][0],
|
|
bankranges[secttype][1]);
|
|
|
|
out_NewAbsSection(name, secttype, org, bank);
|
|
}
|
|
|
|
size_t symvaluetostring(char *dest, size_t maxLength, char *symName,
|
|
const char *mode)
|
|
{
|
|
size_t length;
|
|
struct sSymbol *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] = nPC;
|
|
unionSize[unionIndex] = 0;
|
|
}
|
|
|
|
static void updateUnion(void)
|
|
{
|
|
uint32_t unionIndex = nUnionDepth - 1;
|
|
uint32_t size = nPC - unionStart[unionIndex];
|
|
|
|
if (size > unionSize[unionIndex])
|
|
unionSize[unionIndex] = size;
|
|
|
|
nPC = unionStart[unionIndex];
|
|
pCurrentSection->nPC = unionStart[unionIndex];
|
|
pPCSymbol->nValue = 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;
|
|
}
|
|
|
|
%type <sVal> relocconst
|
|
%type <nConstValue> const
|
|
%type <nConstValue> uconst
|
|
%type <nConstValue> const_3bit
|
|
%type <sVal> const_8bit
|
|
%type <sVal> const_16bit
|
|
%type <nConstValue> sectiontype
|
|
|
|
%type <tzString> string
|
|
|
|
%token <nConstValue> T_NUMBER
|
|
%token <tzString> T_STRING
|
|
|
|
%left <nConstValue> T_OP_LOGICNOT
|
|
%left <nConstValue> T_OP_LOGICOR T_OP_LOGICAND
|
|
%left <nConstValue> T_OP_LOGICGT T_OP_LOGICLT T_OP_LOGICGE T_OP_LOGICLE T_OP_LOGICNE T_OP_LOGICEQU
|
|
%left <nConstValue> T_OP_ADD T_OP_SUB
|
|
%left <nConstValue> T_OP_OR T_OP_XOR T_OP_AND
|
|
%left <nConstValue> T_OP_SHL T_OP_SHR
|
|
%left <nConstValue> T_OP_MUL T_OP_DIV T_OP_MOD
|
|
%left <nConstValue> T_OP_NOT
|
|
%left <nConstValue> T_OP_DEF
|
|
%left <nConstValue> T_OP_BANK T_OP_ALIGN
|
|
%left <nConstValue> T_OP_SIN
|
|
%left <nConstValue> T_OP_COS
|
|
%left <nConstValue> T_OP_TAN
|
|
%left <nConstValue> T_OP_ASIN
|
|
%left <nConstValue> T_OP_ACOS
|
|
%left <nConstValue> T_OP_ATAN
|
|
%left <nConstValue> T_OP_ATAN2
|
|
%left <nConstValue> T_OP_FDIV
|
|
%left <nConstValue> T_OP_FMUL
|
|
%left <nConstValue> T_OP_ROUND
|
|
%left <nConstValue> T_OP_CEIL
|
|
%left <nConstValue> T_OP_FLOOR
|
|
|
|
%token <nConstValue> T_OP_HIGH T_OP_LOW
|
|
|
|
%left <nConstValue> T_OP_STRCMP
|
|
%left <nConstValue> T_OP_STRIN
|
|
%left <nConstValue> T_OP_STRSUB
|
|
%left <nConstValue> T_OP_STRLEN
|
|
%left <nConstValue> T_OP_STRCAT
|
|
%left <nConstValue> T_OP_STRUPR
|
|
%left <nConstValue> T_OP_STRLWR
|
|
|
|
%left NEG /* negation -- unary minus */
|
|
|
|
%token <tzSym> T_LABEL
|
|
%token <tzSym> T_ID
|
|
%token <tzSym> T_POP_EQU
|
|
%token <tzSym> T_POP_SET
|
|
%token <tzSym> T_POP_EQUAL
|
|
%token <tzSym> 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_IMPORT T_POP_EXPORT T_POP_GLOBAL
|
|
%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_FAIL
|
|
%token T_POP_WARN
|
|
%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
|
|
%token T_SECT_HOME T_SECT_DATA T_SECT_CODE T_SECT_BSS
|
|
|
|
%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 T_MODE_SP_IND
|
|
%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
|
|
%start asmfile
|
|
|
|
%%
|
|
|
|
asmfile : lines;
|
|
|
|
/* Note: The lexer adds '\n' at the end of the input */
|
|
lines : /* empty */
|
|
| lines {
|
|
nListCountEmpty = 0;
|
|
nPCOffset = 1;
|
|
} line '\n' {
|
|
nLineNo++;
|
|
nTotalLines++;
|
|
}
|
|
;
|
|
|
|
line : label
|
|
| label cpu_command
|
|
| label macro
|
|
| label simple_pseudoop
|
|
| pseudoop
|
|
;
|
|
|
|
label : /* empty */
|
|
| T_LABEL
|
|
{
|
|
if ($1[0] == '.')
|
|
sym_AddLocalReloc($1);
|
|
else
|
|
sym_AddReloc($1);
|
|
}
|
|
| T_LABEL ':'
|
|
{
|
|
if ($1[0] == '.')
|
|
sym_AddLocalReloc($1);
|
|
else
|
|
sym_AddReloc($1);
|
|
}
|
|
| T_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);
|
|
if (!fstk_RunMacro($1))
|
|
fatalerror("Macro '%s' not defined", $1);
|
|
}
|
|
;
|
|
|
|
macroargs : /* empty */
|
|
| macroarg
|
|
| macroarg comma macroargs
|
|
;
|
|
|
|
macroarg : T_STRING { sym_AddNewMacroArg($1); }
|
|
;
|
|
|
|
pseudoop : equ
|
|
| set
|
|
| rb
|
|
| rw
|
|
| rl
|
|
| equs
|
|
| macrodef
|
|
;
|
|
|
|
simple_pseudoop : include
|
|
| printf
|
|
| printt
|
|
| printv
|
|
| printi
|
|
| if
|
|
| elif
|
|
| else
|
|
| endc
|
|
| import
|
|
| export
|
|
| global
|
|
| { nPCOffset = 0; } db
|
|
| { nPCOffset = 0; } dw
|
|
| { nPCOffset = 0; } dl
|
|
| ds
|
|
| section
|
|
| rsreset
|
|
| rsset
|
|
| union
|
|
| nextu
|
|
| endu
|
|
| incbin
|
|
| charmap
|
|
| newcharmap
|
|
| setcharmap
|
|
| pushc
|
|
| popc
|
|
| rept
|
|
| shift
|
|
| fail
|
|
| warn
|
|
| purge
|
|
| pops
|
|
| pushs
|
|
| popo
|
|
| pusho
|
|
| opt
|
|
;
|
|
|
|
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_entry comma opt_list
|
|
;
|
|
|
|
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); }
|
|
;
|
|
|
|
shift : T_POP_SHIFT { sym_ShiftCurrentMacroArgs(); }
|
|
;
|
|
|
|
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--;
|
|
nPC = unionStart[nUnionDepth] + unionSize[nUnionDepth];
|
|
pCurrentSection->nPC = nPC;
|
|
pPCSymbol->nValue = nPC;
|
|
}
|
|
;
|
|
|
|
ds : T_POP_DS uconst
|
|
{
|
|
out_Skip($2);
|
|
}
|
|
;
|
|
|
|
db : T_POP_DB constlist_8bit_entry comma 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 comma 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 comma 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_entry comma purge_list
|
|
;
|
|
|
|
purge_list_entry : T_ID
|
|
{
|
|
sym_Purge($1);
|
|
}
|
|
;
|
|
|
|
import : T_POP_IMPORT import_list
|
|
;
|
|
|
|
import_list : import_list_entry
|
|
| import_list_entry comma import_list
|
|
;
|
|
|
|
import_list_entry : T_ID
|
|
{
|
|
/*
|
|
* This is done automatically if the label isn't found
|
|
* in the list of defined symbols.
|
|
*/
|
|
warning(WARNING_OBSOLETE, "IMPORT is a deprecated keyword with no effect: %s", $1);
|
|
}
|
|
;
|
|
|
|
export : T_POP_EXPORT export_list
|
|
;
|
|
|
|
export_list : export_list_entry
|
|
| export_list_entry comma export_list
|
|
;
|
|
|
|
export_list_entry : T_ID
|
|
{
|
|
sym_Export($1);
|
|
}
|
|
;
|
|
|
|
global : T_POP_GLOBAL global_list
|
|
;
|
|
|
|
global_list : global_list_entry
|
|
| global_list_entry comma global_list
|
|
;
|
|
|
|
global_list_entry : T_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 comma uconst comma uconst
|
|
{
|
|
out_BinaryFileSlice($2, $4, $6);
|
|
if (oFailedOnMissingInclude)
|
|
YYACCEPT;
|
|
}
|
|
;
|
|
|
|
charmap : T_POP_CHARMAP string comma 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 comma T_ID
|
|
{
|
|
charmap_New($2, $4);
|
|
}
|
|
;
|
|
|
|
setcharmap : T_POP_SETCHARMAP T_ID
|
|
{
|
|
charmap_Set($2);
|
|
}
|
|
;
|
|
|
|
pushc : T_POP_PUSHC { charmap_Push(); }
|
|
;
|
|
|
|
popc : T_POP_POPC { charmap_Pop(); }
|
|
;
|
|
|
|
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) {
|
|
/*
|
|
* Continue parsing after ELSE, or at ELIF or
|
|
* ENDC keyword.
|
|
*/
|
|
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");
|
|
else
|
|
$$ = value & 0x7;
|
|
}
|
|
;
|
|
|
|
constlist_8bit : constlist_8bit_entry
|
|
| constlist_8bit_entry comma constlist_8bit
|
|
;
|
|
|
|
constlist_8bit_entry : /* empty */
|
|
{
|
|
out_Skip(1);
|
|
nListCountEmpty++;
|
|
}
|
|
| const_8bit
|
|
{
|
|
out_RelByte(&$1);
|
|
}
|
|
| string
|
|
{
|
|
char *s = $1;
|
|
int32_t length = charmap_Convert(&s);
|
|
|
|
out_AbsByteGroup(s, length);
|
|
free(s);
|
|
}
|
|
;
|
|
|
|
constlist_16bit : constlist_16bit_entry
|
|
| constlist_16bit_entry comma constlist_16bit
|
|
;
|
|
|
|
constlist_16bit_entry : /* empty */
|
|
{
|
|
out_Skip(2);
|
|
nListCountEmpty++;
|
|
}
|
|
| const_16bit
|
|
{
|
|
out_RelWord(&$1);
|
|
}
|
|
;
|
|
|
|
constlist_32bit : constlist_32bit_entry
|
|
| constlist_32bit_entry comma constlist_32bit
|
|
;
|
|
|
|
constlist_32bit_entry : /* empty */
|
|
{
|
|
out_Skip(4);
|
|
nListCountEmpty++;
|
|
}
|
|
| relocconst
|
|
{
|
|
out_RelLong(&$1);
|
|
}
|
|
;
|
|
|
|
const_8bit : relocconst
|
|
{
|
|
if( (rpn_isKnown(&$1)) && (($1.nVal < -128) || ($1.nVal > 255)) )
|
|
warning(WARNING_TRUNCATION, "Expression must be 8-bit");
|
|
$$ = $1;
|
|
}
|
|
;
|
|
|
|
const_16bit : relocconst
|
|
{
|
|
if ((rpn_isKnown(&$1)) && (($1.nVal < -32768) || ($1.nVal > 65535)))
|
|
warning(WARNING_TRUNCATION, "Expression must be 16-bit");
|
|
$$ = $1;
|
|
}
|
|
;
|
|
|
|
|
|
relocconst : T_ID
|
|
{
|
|
rpn_Symbol(&$$, $1);
|
|
}
|
|
| T_NUMBER
|
|
{
|
|
rpn_Number(&$$, $1);
|
|
}
|
|
| string
|
|
{
|
|
char *s = $1;
|
|
int32_t length = charmap_Convert(&s);
|
|
uint32_t r = str2int2(s, length);
|
|
|
|
free(s);
|
|
rpn_Number(&$$, r);
|
|
}
|
|
| T_OP_LOGICNOT relocconst %prec NEG { rpn_LOGNOT(&$$, &$2); }
|
|
| relocconst T_OP_LOGICOR relocconst { rpn_BinaryOp(RPN_LOGOR, &$$, &$1, &$3); }
|
|
| relocconst T_OP_LOGICAND relocconst { rpn_BinaryOp(RPN_LOGAND, &$$, &$1, &$3); }
|
|
| relocconst T_OP_LOGICEQU relocconst { rpn_BinaryOp(RPN_LOGEQ, &$$, &$1, &$3); }
|
|
| relocconst T_OP_LOGICGT relocconst { rpn_BinaryOp(RPN_LOGGT, &$$, &$1, &$3); }
|
|
| relocconst T_OP_LOGICLT relocconst { rpn_BinaryOp(RPN_LOGLT, &$$, &$1, &$3); }
|
|
| relocconst T_OP_LOGICGE relocconst { rpn_BinaryOp(RPN_LOGGE, &$$, &$1, &$3); }
|
|
| relocconst T_OP_LOGICLE relocconst { rpn_BinaryOp(RPN_LOGLE, &$$, &$1, &$3); }
|
|
| relocconst T_OP_LOGICNE relocconst { rpn_BinaryOp(RPN_LOGNE, &$$, &$1, &$3); }
|
|
| relocconst T_OP_ADD relocconst { rpn_BinaryOp(RPN_ADD, &$$, &$1, &$3); }
|
|
| relocconst T_OP_SUB relocconst { rpn_BinaryOp(RPN_SUB, &$$, &$1, &$3); }
|
|
| relocconst T_OP_XOR relocconst { rpn_BinaryOp(RPN_XOR, &$$, &$1, &$3); }
|
|
| relocconst T_OP_OR relocconst { rpn_BinaryOp(RPN_OR, &$$, &$1, &$3); }
|
|
| relocconst T_OP_AND relocconst { rpn_BinaryOp(RPN_AND, &$$, &$1, &$3); }
|
|
| relocconst T_OP_SHL relocconst { rpn_BinaryOp(RPN_SHL, &$$, &$1, &$3); }
|
|
| relocconst T_OP_SHR relocconst { rpn_BinaryOp(RPN_SHR, &$$, &$1, &$3); }
|
|
| relocconst T_OP_MUL relocconst { rpn_BinaryOp(RPN_MUL, &$$, &$1, &$3); }
|
|
| relocconst T_OP_DIV relocconst { rpn_BinaryOp(RPN_DIV, &$$, &$1, &$3); }
|
|
| relocconst T_OP_MOD relocconst { rpn_BinaryOp(RPN_MOD, &$$, &$1, &$3); }
|
|
| T_OP_ADD relocconst %prec NEG { $$ = $2; }
|
|
| T_OP_SUB relocconst %prec NEG { rpn_UNNEG(&$$, &$2); }
|
|
| T_OP_NOT relocconst %prec NEG { rpn_UNNOT(&$$, &$2); }
|
|
| T_OP_HIGH '(' relocconst ')' { rpn_HIGH(&$$, &$3); }
|
|
| T_OP_LOW '(' relocconst ')' { rpn_LOW(&$$, &$3); }
|
|
| T_OP_BANK '(' T_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;
|
|
} '(' T_ID ')'
|
|
{
|
|
struct sSymbol const *sym = sym_FindSymbol($4);
|
|
if (sym && !(sym_IsDefined(sym) && sym->type != SYM_LABEL))
|
|
yyerror("Label \"%s\" is not a valid argument to DEF",
|
|
$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 comma const ')'
|
|
{
|
|
rpn_Number(&$$,
|
|
math_Div($3,
|
|
$5));
|
|
}
|
|
| T_OP_FMUL '(' const comma 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 comma const ')'
|
|
{
|
|
rpn_Number(&$$,
|
|
math_ATan2($3,
|
|
$5));
|
|
}
|
|
| T_OP_STRCMP '(' string comma string ')'
|
|
{
|
|
rpn_Number(&$$, strcmp($3, $5));
|
|
}
|
|
| T_OP_STRIN '(' string comma string ')'
|
|
{
|
|
char *p = strstr($3, $5);
|
|
|
|
if (p != NULL)
|
|
rpn_Number(&$$, p - $3 + 1);
|
|
else
|
|
rpn_Number(&$$, 0);
|
|
}
|
|
| T_OP_STRLEN '(' string ')' { rpn_Number(&$$, strlenUTF8($3)); }
|
|
| '(' relocconst ')' { $$ = $2; }
|
|
;
|
|
|
|
uconst : const
|
|
{
|
|
int32_t value = $1;
|
|
if (value < 0)
|
|
fatalerror("Constant mustn't be negative: %d", value);
|
|
$$ = value;
|
|
}
|
|
;
|
|
|
|
const : relocconst
|
|
{
|
|
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 comma uconst comma uconst ')'
|
|
{
|
|
strsubUTF8($$, $3, $5, $7);
|
|
}
|
|
| T_OP_STRCAT '(' string comma 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 string comma sectiontype
|
|
{
|
|
out_NewSection($2, $4);
|
|
}
|
|
| T_POP_SECTION string comma sectiontype '[' uconst ']'
|
|
{
|
|
if (($6 >= 0) && ($6 < 0x10000))
|
|
out_NewAbsSection($2, $4, $6, -1);
|
|
else
|
|
yyerror("Address $%x not 16-bit", $6);
|
|
}
|
|
| T_POP_SECTION string comma sectiontype comma T_OP_ALIGN '[' uconst ']'
|
|
{
|
|
out_NewAlignedSection($2, $4, $8, -1);
|
|
}
|
|
| T_POP_SECTION string comma sectiontype comma T_OP_BANK '[' uconst ']'
|
|
{
|
|
bankrangecheck($2, $4, -1, $8);
|
|
}
|
|
| T_POP_SECTION string comma sectiontype '[' uconst ']' comma T_OP_BANK '[' uconst ']'
|
|
{
|
|
if (($6 < 0) || ($6 > 0x10000))
|
|
yyerror("Address $%x not 16-bit", $6);
|
|
bankrangecheck($2, $4, $6, $11);
|
|
}
|
|
| T_POP_SECTION string comma sectiontype comma T_OP_ALIGN '[' uconst ']' comma T_OP_BANK '[' uconst ']'
|
|
{
|
|
out_NewAlignedSection($2, $4, $8, $13);
|
|
}
|
|
| T_POP_SECTION string comma sectiontype comma T_OP_BANK '[' uconst ']' comma T_OP_ALIGN '[' uconst ']'
|
|
{
|
|
out_NewAlignedSection($2, $4, $13, $8);
|
|
}
|
|
;
|
|
|
|
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; }
|
|
| T_SECT_HOME
|
|
{
|
|
warning(WARNING_OBSOLETE, "HOME section name is deprecated, use ROM0 instead.");
|
|
$$ = SECTTYPE_ROM0;
|
|
}
|
|
| T_SECT_DATA
|
|
{
|
|
warning(WARNING_OBSOLETE, "DATA section name is deprecated, use ROMX instead.");
|
|
$$ = SECTTYPE_ROMX;
|
|
}
|
|
| T_SECT_CODE
|
|
{
|
|
warning(WARNING_OBSOLETE, "CODE section name is deprecated, use ROMX instead.");
|
|
$$ = SECTTYPE_ROMX;
|
|
}
|
|
| T_SECT_BSS
|
|
{
|
|
warning(WARNING_OBSOLETE, "BSS section name is deprecated, use WRAM0 instead.");
|
|
$$ = SECTTYPE_WRAM0;
|
|
}
|
|
;
|
|
|
|
|
|
cpu_command : z80_adc
|
|
| z80_add
|
|
| z80_and
|
|
| z80_bit
|
|
| z80_call
|
|
| z80_ccf
|
|
| z80_cp
|
|
| z80_cpl
|
|
| z80_daa
|
|
| z80_dec
|
|
| z80_di
|
|
| z80_ei
|
|
| z80_halt
|
|
| z80_inc
|
|
| z80_jp
|
|
| z80_jr
|
|
| z80_ld
|
|
| z80_ldd
|
|
| z80_ldi
|
|
| z80_ldio
|
|
| z80_nop
|
|
| z80_or
|
|
| z80_pop
|
|
| z80_push
|
|
| z80_res
|
|
| z80_ret
|
|
| z80_reti
|
|
| z80_rl
|
|
| z80_rla
|
|
| z80_rlc
|
|
| z80_rlca
|
|
| z80_rr
|
|
| z80_rra
|
|
| z80_rrc
|
|
| z80_rrca
|
|
| z80_rst
|
|
| z80_sbc
|
|
| z80_scf
|
|
| z80_set
|
|
| z80_sla
|
|
| z80_sra
|
|
| z80_srl
|
|
| z80_stop
|
|
| z80_sub
|
|
| z80_swap
|
|
| 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 comma const_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 comma reg_r
|
|
{
|
|
out_AbsByte(0xCB);
|
|
out_AbsByte(0x40 | ($2 << 3) | $4);
|
|
}
|
|
;
|
|
|
|
z80_call : T_Z80_CALL const_16bit
|
|
{
|
|
out_AbsByte(0xCD);
|
|
out_RelWord(&$2);
|
|
}
|
|
| T_Z80_CALL ccode comma const_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 const_16bit
|
|
{
|
|
out_AbsByte(0xC3);
|
|
out_RelWord(&$2);
|
|
}
|
|
| T_Z80_JP ccode comma const_16bit
|
|
{
|
|
out_AbsByte(0xC2 | ($2 << 3));
|
|
out_RelWord(&$4);
|
|
}
|
|
| T_Z80_JP T_MODE_HL_IND
|
|
{
|
|
out_AbsByte(0xE9);
|
|
warning(WARNING_OBSOLETE, "'JP [HL]' is obsolete, use 'JP HL' instead.");
|
|
}
|
|
| T_Z80_JP T_MODE_HL
|
|
{
|
|
out_AbsByte(0xE9);
|
|
}
|
|
;
|
|
|
|
z80_jr : T_Z80_JR const_16bit
|
|
{
|
|
out_AbsByte(0x18);
|
|
out_PCRelByte(&$2);
|
|
}
|
|
| T_Z80_JR ccode comma const_16bit
|
|
{
|
|
out_AbsByte(0x20 | ($2 << 3));
|
|
out_PCRelByte(&$4);
|
|
}
|
|
;
|
|
|
|
z80_ldi : T_Z80_LDI T_MODE_HL_IND comma T_MODE_A
|
|
{
|
|
out_AbsByte(0x02 | (2 << 4));
|
|
}
|
|
| T_Z80_LDI T_MODE_A comma T_MODE_HL
|
|
{
|
|
out_AbsByte(0x0A | (2 << 4));
|
|
warning(WARNING_OBSOLETE, "'LDI A,HL' is obsolete, use 'LDI A,[HL]' or 'LD A,[HL+] instead.");
|
|
}
|
|
| T_Z80_LDI T_MODE_A comma T_MODE_HL_IND
|
|
{
|
|
out_AbsByte(0x0A | (2 << 4));
|
|
}
|
|
;
|
|
|
|
z80_ldd : T_Z80_LDD T_MODE_HL_IND comma T_MODE_A
|
|
{
|
|
out_AbsByte(0x02 | (3 << 4));
|
|
}
|
|
| T_Z80_LDD T_MODE_A comma T_MODE_HL
|
|
{
|
|
out_AbsByte(0x0A | (3 << 4));
|
|
warning(WARNING_OBSOLETE, "'LDD A,HL' is obsolete, use 'LDD A,[HL]' or 'LD A,[HL-] instead.");
|
|
}
|
|
| T_Z80_LDD T_MODE_A comma T_MODE_HL_IND
|
|
{
|
|
out_AbsByte(0x0A | (3 << 4));
|
|
}
|
|
;
|
|
|
|
z80_ldio : T_Z80_LDIO T_MODE_A comma op_mem_ind
|
|
{
|
|
rpn_CheckHRAM(&$4, &$4);
|
|
|
|
if ((rpn_isKnown(&$4)) && ($4.nVal < 0 || ($4.nVal > 0xFF && $4.nVal < 0xFF00) || $4.nVal > 0xFFFF))
|
|
yyerror("Source address $%x not in $FF00 to $FFFF", $4.nVal);
|
|
|
|
out_AbsByte(0xF0);
|
|
$4.nVal &= 0xFF;
|
|
out_RelByte(&$4);
|
|
}
|
|
| T_Z80_LDIO op_mem_ind comma T_MODE_A
|
|
{
|
|
rpn_CheckHRAM(&$2, &$2);
|
|
|
|
if ((rpn_isKnown(&$2)) && ($2.nVal < 0 || ($2.nVal > 0xFF && $2.nVal < 0xFF00) || $2.nVal > 0xFFFF))
|
|
yyerror("Destination address $%x not in $FF00 to $FFFF", $2.nVal);
|
|
|
|
out_AbsByte(0xE0);
|
|
$2.nVal &= 0xFF;
|
|
out_RelByte(&$2);
|
|
}
|
|
| T_Z80_LDIO T_MODE_A comma T_MODE_C_IND
|
|
{
|
|
out_AbsByte(0xF2);
|
|
}
|
|
| T_Z80_LDIO T_MODE_C_IND comma 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 comma '[' T_MODE_SP const_8bit ']'
|
|
{
|
|
out_AbsByte(0xF8);
|
|
out_RelByte(&$6);
|
|
warning(WARNING_OBSOLETE, "'LD HL,[SP+e8]' is obsolete, use 'LD HL,SP+e8' instead.");
|
|
}
|
|
| T_Z80_LD T_MODE_HL comma T_MODE_SP const_8bit
|
|
{
|
|
out_AbsByte(0xF8);
|
|
out_RelByte(&$5);
|
|
}
|
|
| T_Z80_LD T_MODE_HL comma const_16bit
|
|
{
|
|
out_AbsByte(0x01 | (REG_HL << 4));
|
|
out_RelWord(&$4);
|
|
}
|
|
;
|
|
|
|
z80_ld_sp : T_Z80_LD T_MODE_SP comma T_MODE_HL
|
|
{
|
|
out_AbsByte(0xF9);
|
|
}
|
|
| T_Z80_LD T_MODE_SP comma const_16bit
|
|
{
|
|
out_AbsByte(0x01 | (REG_SP << 4));
|
|
out_RelWord(&$4);
|
|
}
|
|
;
|
|
|
|
z80_ld_mem : T_Z80_LD op_mem_ind comma T_MODE_SP
|
|
{
|
|
out_AbsByte(0x08);
|
|
out_RelWord(&$2);
|
|
}
|
|
| T_Z80_LD op_mem_ind comma 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 comma T_MODE_A
|
|
{
|
|
out_AbsByte(0xE2);
|
|
}
|
|
;
|
|
|
|
z80_ld_rr : T_Z80_LD reg_rr comma T_MODE_A
|
|
{
|
|
out_AbsByte(0x02 | ($2 << 4));
|
|
}
|
|
;
|
|
|
|
z80_ld_r : T_Z80_LD reg_r comma const_8bit
|
|
{
|
|
out_AbsByte(0x06 | ($2 << 3));
|
|
out_RelByte(&$4);
|
|
}
|
|
| T_Z80_LD reg_r comma 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 comma T_MODE_C_IND
|
|
{
|
|
if ($2 == REG_A)
|
|
out_AbsByte(0xF2);
|
|
else
|
|
yyerror("Destination operand must be A");
|
|
}
|
|
| T_Z80_LD reg_r comma reg_rr
|
|
{
|
|
if ($2 == REG_A)
|
|
out_AbsByte(0x0A | ($4 << 4));
|
|
else
|
|
yyerror("Destination operand must be A");
|
|
}
|
|
| T_Z80_LD reg_r comma 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 comma const_16bit
|
|
{
|
|
out_AbsByte(0x01 | (REG_BC << 4));
|
|
out_RelWord(&$4);
|
|
}
|
|
| T_Z80_LD T_MODE_DE comma const_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 comma 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 const_8bit
|
|
{
|
|
if (!rpn_isKnown(&$2)) {
|
|
rpn_CheckRST(&$2, &$2);
|
|
out_RelByte(&$2);
|
|
} else if (($2.nVal & 0x38) != $2.nVal) {
|
|
yyerror("Invalid address $%x for RST", $2.nVal);
|
|
} 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 comma 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 const_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 : '[' const_16bit ']' { $$ = $2; }
|
|
;
|
|
|
|
op_hl_ss : reg_ss { $$ = $1; }
|
|
| T_MODE_HL comma reg_ss { $$ = $3; }
|
|
;
|
|
|
|
op_a_r : reg_r { $$ = $1; }
|
|
| T_MODE_A comma reg_r { $$ = $3; }
|
|
;
|
|
|
|
op_a_n : const_8bit { $$ = $1; }
|
|
| T_MODE_A comma const_8bit { $$ = $3; }
|
|
;
|
|
|
|
comma : ','
|
|
;
|
|
|
|
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; }
|
|
;
|
|
|
|
%%
|