mirror of
https://github.com/gbdev/rgbds.git
synced 2025-11-24 20:12:07 +00:00
When '@' is used as argument of any instruction (LD, JR, JP, etc), the
address it refers to is the address of the first byte of the
instruction. When '@' is used with DB/DW/DL, it refers to the same
address it is being placed at. This means that instructions need an
offset of 1 byte and others need an offset of 0 bytes.
The assembler doesn't evaluate anything related to '@' because it would
only work in sections with a fixed base address. It is left to the
linker. This means that the offset needs to be added to the RPN
expression of the patch that is saved in the linker. It isn't enough by
adding an offset to the final expresion. The following value wouldn't be
calculated correctly (even if it doesn't make sense):
JP @ * @
The correct patch is `(@ - 1) * (@ - 1)`, not `(@ * @) - 1`.
This patch introduces an offset on 1 byte by default in every line, and
sets it to 0 only if a DB/DW/DL is detected.
Signed-off-by: Antonio Niño Díaz <antonio_nd@outlook.com>
2099 lines
39 KiB
Plaintext
2099 lines
39 KiB
Plaintext
/*
|
|
* This file is part of RGBDS.
|
|
*
|
|
* Copyright (c) 1997-2018, Carsten Sorensen and RGBDS contributors.
|
|
*
|
|
* SPDX-License-Identifier: MIT
|
|
*/
|
|
|
|
%{
|
|
#include <ctype.h>
|
|
#include <errno.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 "common.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)
|
|
{
|
|
int32_t minbank = 0, maxbank = 0;
|
|
char *stype = NULL;
|
|
|
|
switch (secttype) {
|
|
case SECT_ROMX:
|
|
stype = "ROMX";
|
|
minbank = BANK_MIN_ROMX;
|
|
maxbank = BANK_MAX_ROMX;
|
|
break;
|
|
case SECT_SRAM:
|
|
stype = "SRAM";
|
|
minbank = BANK_MIN_SRAM;
|
|
maxbank = BANK_MAX_SRAM;
|
|
break;
|
|
case SECT_WRAMX:
|
|
stype = "WRAMX";
|
|
minbank = BANK_MIN_WRAMX;
|
|
maxbank = BANK_MAX_WRAMX;
|
|
break;
|
|
case SECT_VRAM:
|
|
stype = "VRAM";
|
|
minbank = BANK_MIN_VRAM;
|
|
maxbank = BANK_MAX_VRAM;
|
|
break;
|
|
default:
|
|
yyerror("BANK only allowed for ROMX, WRAMX, SRAM, or VRAM sections");
|
|
}
|
|
|
|
if (stype && (bank < minbank || bank > maxbank)) {
|
|
yyerror("%s bank value $%x out of range ($%x to $%x)",
|
|
stype, bank, minbank, maxbank);
|
|
}
|
|
|
|
out_NewAbsSection(name, secttype, org, bank);
|
|
}
|
|
|
|
size_t symvaluetostring(char *dest, size_t maxLength, char *sym)
|
|
{
|
|
size_t length;
|
|
|
|
if (sym_isString(sym)) {
|
|
char *src = sym_GetStringValue(sym);
|
|
size_t i;
|
|
|
|
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(sym);
|
|
int32_t fullLength = snprintf(dest, maxLength + 1, "$%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 str2int(char *s)
|
|
{
|
|
uint32_t r = 0;
|
|
|
|
while (*s) {
|
|
r <<= 8;
|
|
r |= (uint8_t)(*s++);
|
|
}
|
|
|
|
return r;
|
|
}
|
|
|
|
static uint32_t str2int2(char *s, int32_t length)
|
|
{
|
|
int32_t i;
|
|
uint32_t r = 0;
|
|
|
|
i = ((length - 4) < 0) ? 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 += 1;
|
|
src += 4;
|
|
} else if (isEndr(src)) {
|
|
level -= 1;
|
|
src += 4;
|
|
} else {
|
|
if (*src == '\"')
|
|
instring = 1;
|
|
src += 1;
|
|
}
|
|
} else {
|
|
if (*src == '\\') {
|
|
src += 2;
|
|
} else if (*src == '\"') {
|
|
src += 1;
|
|
instring = 0;
|
|
} else {
|
|
src += 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
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 += 1) {
|
|
tzNewMacro[i] = src[i];
|
|
if (src[i] == '\n')
|
|
nLineNo+=1;
|
|
}
|
|
|
|
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 += 1;
|
|
src += 4;
|
|
} else if (isEndm(src)) {
|
|
level -= 1;
|
|
src += 4;
|
|
} else {
|
|
if(*src == '\"')
|
|
instring = 1;
|
|
src += 1;
|
|
}
|
|
} else {
|
|
if (*src == '\\') {
|
|
src += 2;
|
|
} else if (*src == '\"') {
|
|
src += 1;
|
|
instring = 0;
|
|
} else {
|
|
src += 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (level != 0)
|
|
fatalerror("Unterminated MACRO definition.");
|
|
|
|
len = src - pCurrentBuffer->pBuffer - 4;
|
|
|
|
src = pCurrentBuffer->pBuffer;
|
|
ulNewMacroSize = len;
|
|
|
|
tzNewMacro = (char *)malloc(ulNewMacroSize+2);
|
|
if (tzNewMacro == NULL)
|
|
fatalerror("Not enough memory for MACRO definition.");
|
|
|
|
uint32_t i;
|
|
|
|
tzNewMacro[ulNewMacroSize] = '\n';
|
|
tzNewMacro[ulNewMacroSize+1] = 0;
|
|
for (i = 0; i < ulNewMacroSize; i += 1) {
|
|
tzNewMacro[i] = src[i];
|
|
if (src[i] == '\n')
|
|
nLineNo += 1;
|
|
}
|
|
|
|
yyskipbytes(ulNewMacroSize + 4);
|
|
}
|
|
|
|
static uint32_t isIf(char *s)
|
|
{
|
|
return (strncasecmp(s, "IF", 2) == 0)
|
|
&& isWhiteSpace(s[-1]) && isWhiteSpace(s[2]);
|
|
}
|
|
|
|
static uint32_t isElif(char *s)
|
|
{
|
|
return (strncasecmp(s, "ELIF", 4) == 0)
|
|
&& isWhiteSpace(s[-1]) && isWhiteSpace(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 {
|
|
switch (*src) {
|
|
case '\\':
|
|
src += 2;
|
|
break;
|
|
|
|
case '\"':
|
|
src++;
|
|
inString = false;
|
|
|
|
default:
|
|
src++;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
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 {
|
|
switch (*src) {
|
|
|
|
case '\\':
|
|
src += 2;
|
|
break;
|
|
|
|
case '\"':
|
|
src++;
|
|
inString = false;
|
|
break;
|
|
|
|
default:
|
|
src++;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
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];
|
|
}
|
|
|
|
%}
|
|
|
|
%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 T_OP_LOGICNOT
|
|
%left T_OP_LOGICOR T_OP_LOGICAND T_OP_LOGICEQU
|
|
%left T_OP_LOGICGT T_OP_LOGICLT T_OP_LOGICGE T_OP_LOGICLE T_OP_LOGICNE
|
|
%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
|
|
|
|
%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_POP_EQU
|
|
%token <tzSym> T_POP_SET
|
|
%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_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 += 1;
|
|
nTotalLines += 1;
|
|
}
|
|
;
|
|
|
|
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
|
|
| 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("%s", $2); }
|
|
;
|
|
|
|
shift : T_POP_SHIFT { sym_ShiftCurrentMacroArgs(); }
|
|
;
|
|
|
|
rept : T_POP_REPT uconst
|
|
{
|
|
copyrept();
|
|
fstk_RunRept($2);
|
|
}
|
|
;
|
|
|
|
macrodef : T_LABEL ':' T_POP_MACRO
|
|
{
|
|
copymacro();
|
|
sym_AddMacro($1);
|
|
}
|
|
;
|
|
|
|
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 ((nPass == 1) && (nListCountEmpty > 0)) {
|
|
warning("Empty entry in list of 8-bit elements (treated as 0).");
|
|
}
|
|
}
|
|
| T_POP_DB constlist_8bit_entry
|
|
;
|
|
|
|
dw : T_POP_DW constlist_16bit_entry comma constlist_16bit {
|
|
if ((nPass == 1) && (nListCountEmpty > 0)) {
|
|
warning("Empty entry in list of 16-bit elements (treated as 0).");
|
|
}
|
|
}
|
|
| T_POP_DW constlist_16bit_entry
|
|
;
|
|
|
|
dl : T_POP_DL constlist_32bit_entry comma constlist_32bit {
|
|
if ((nPass == 1) && (nListCountEmpty > 0)) {
|
|
warning("Empty entry in list of 32-bit elements (treated as 0).");
|
|
}
|
|
}
|
|
| 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.
|
|
*/
|
|
if (nPass == 1)
|
|
warning("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_Global($1);
|
|
}
|
|
;
|
|
|
|
equ : T_LABEL T_POP_EQU const
|
|
{
|
|
sym_AddEqu($1, $3);
|
|
}
|
|
;
|
|
|
|
set : T_LABEL T_POP_SET const
|
|
{
|
|
sym_AddSet($1, $3);
|
|
}
|
|
;
|
|
|
|
include : T_POP_INCLUDE string
|
|
{
|
|
fstk_RunInclude($2);
|
|
}
|
|
;
|
|
|
|
incbin : T_POP_INCBIN string
|
|
{
|
|
out_BinaryFile($2);
|
|
}
|
|
| T_POP_INCBIN string comma uconst comma uconst
|
|
{
|
|
out_BinaryFileSlice($2, $4, $6);
|
|
}
|
|
;
|
|
|
|
charmap : T_POP_CHARMAP string comma string
|
|
{
|
|
if (charmap_Add($2, $4[0] & 0xFF) == -1) {
|
|
fprintf(stderr, "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));
|
|
yyerror("Error parsing charmap.");
|
|
}
|
|
}
|
|
| T_POP_CHARMAP string comma const
|
|
{
|
|
if (charmap_Add($2, $4 & 0xFF) == -1) {
|
|
fprintf(stderr, "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));
|
|
yyerror("Error parsing charmap.");
|
|
}
|
|
}
|
|
;
|
|
|
|
printt : T_POP_PRINTT string
|
|
{
|
|
if (nPass == 1)
|
|
printf("%s", $2);
|
|
}
|
|
;
|
|
|
|
printv : T_POP_PRINTV const
|
|
{
|
|
if (nPass == 1)
|
|
printf("$%X", $2);
|
|
}
|
|
;
|
|
|
|
printi : T_POP_PRINTI const
|
|
{
|
|
if (nPass == 1)
|
|
printf("%d", $2);
|
|
}
|
|
;
|
|
|
|
printf : T_POP_PRINTF const
|
|
{
|
|
if (nPass == 1)
|
|
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
|
|
{
|
|
if (($1 < 0) || ($1 > 7))
|
|
yyerror("Immediate value must be 3-bit");
|
|
else
|
|
$$ = $1 & 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_isReloc(&$1)) && (($1.nVal < -128) || ($1.nVal > 255)) )
|
|
yyerror("Expression must be 8-bit");
|
|
$$ = $1;
|
|
}
|
|
;
|
|
|
|
const_16bit : relocconst
|
|
{
|
|
if ((!rpn_isReloc(&$1)) && (($1.nVal < -32768) || ($1.nVal > 65535)))
|
|
yyerror("Expression must be 16-bit");
|
|
$$ = $1;
|
|
}
|
|
;
|
|
|
|
|
|
relocconst : T_ID
|
|
{
|
|
/*
|
|
* The value of @ needs to be evaluated by the linker,
|
|
* it can only be calculated by the assembler in very
|
|
* few cases (when the base address of a section is
|
|
* known).
|
|
*
|
|
* '@' is a bit special in that it means different
|
|
* things depending on when it is used:
|
|
*
|
|
* - JR/LD/ADD/etc: It refers to the first byte of the
|
|
* instruction (1 byte offset relative to the value
|
|
* stored in the ROM).
|
|
* - DB/DW/DL: It refers to the address of the value
|
|
* that is being saved (0 byte offset relative to the
|
|
* value stored in the ROM.
|
|
*
|
|
* This offset must be added whenever '@' is added to a
|
|
* RPN expression so that the linker can calculate the
|
|
* correct result of any expression that uses '@'.
|
|
*/
|
|
if ((strcmp($1, "@") == 0) && (nPCOffset != 0)) {
|
|
struct Expression sTemp, sOffset;
|
|
|
|
rpn_Symbol(&sTemp, $1);
|
|
sTemp.nVal = sym_GetValue($1);
|
|
|
|
rpn_Number(&sOffset, nPCOffset);
|
|
|
|
rpn_SUB(&$$, &sTemp, &sOffset);
|
|
} else {
|
|
rpn_Symbol(&$$, $1);
|
|
$$.nVal = sym_GetValue($1);
|
|
}
|
|
}
|
|
| T_NUMBER
|
|
{
|
|
rpn_Number(&$$, $1);
|
|
$$.nVal = $1;
|
|
}
|
|
| string
|
|
{
|
|
char *s = $1;
|
|
int32_t length = charmap_Convert(&s);
|
|
uint32_t r = str2int2(s, length);
|
|
|
|
free(s);
|
|
rpn_Number(&$$, r);
|
|
$$.nVal = r;
|
|
}
|
|
| T_OP_LOGICNOT relocconst %prec NEG { rpn_LOGNOT(&$$, &$2); }
|
|
| relocconst T_OP_LOGICOR relocconst { rpn_LOGOR(&$$, &$1, &$3); }
|
|
| relocconst T_OP_LOGICAND relocconst { rpn_LOGAND(&$$, &$1, &$3); }
|
|
| relocconst T_OP_LOGICEQU relocconst { rpn_LOGEQU(&$$, &$1, &$3); }
|
|
| relocconst T_OP_LOGICGT relocconst { rpn_LOGGT(&$$, &$1, &$3); }
|
|
| relocconst T_OP_LOGICLT relocconst { rpn_LOGLT(&$$, &$1, &$3); }
|
|
| relocconst T_OP_LOGICGE relocconst { rpn_LOGGE(&$$, &$1, &$3); }
|
|
| relocconst T_OP_LOGICLE relocconst { rpn_LOGLE(&$$, &$1, &$3); }
|
|
| relocconst T_OP_LOGICNE relocconst { rpn_LOGNE(&$$, &$1, &$3); }
|
|
| relocconst T_OP_ADD relocconst { rpn_ADD(&$$, &$1, &$3); }
|
|
| relocconst T_OP_SUB relocconst { rpn_SUB(&$$, &$1, &$3); }
|
|
| relocconst T_OP_XOR relocconst { rpn_XOR(&$$, &$1, &$3); }
|
|
| relocconst T_OP_OR relocconst { rpn_OR(&$$, &$1, &$3); }
|
|
| relocconst T_OP_AND relocconst { rpn_AND(&$$, &$1, &$3); }
|
|
| relocconst T_OP_SHL relocconst { rpn_SHL(&$$, &$1, &$3); }
|
|
| relocconst T_OP_SHR relocconst { rpn_SHR(&$$, &$1, &$3); }
|
|
| relocconst T_OP_MUL relocconst { rpn_MUL(&$$, &$1, &$3); }
|
|
| relocconst T_OP_DIV relocconst { rpn_DIV(&$$, &$1, &$3); }
|
|
| relocconst T_OP_MOD relocconst { 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);
|
|
$$.nVal = 0;
|
|
}
|
|
| T_OP_BANK '(' string ')'
|
|
{
|
|
rpn_BankSection(&$$, $3);
|
|
$$.nVal = 0;
|
|
}
|
|
| T_OP_DEF {
|
|
oDontExpandStrings = true;
|
|
} '(' T_ID ')'
|
|
{
|
|
rpn_Number(&$$, sym_isConstDefined($4));
|
|
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(&$$, strlen($3)); }
|
|
| '(' relocconst ')' { $$ = $2; }
|
|
;
|
|
|
|
uconst : const
|
|
{
|
|
if ($1 < 0)
|
|
fatalerror("Constant mustn't be negative: %d", $1);
|
|
$$ = $1;
|
|
}
|
|
;
|
|
|
|
const : T_ID { $$ = sym_GetConstantValue($1); }
|
|
| T_NUMBER { $$ = $1; }
|
|
| T_OP_HIGH '(' const ')' { $$ = ($3 >> 8) & 0xFF; }
|
|
| T_OP_LOW '(' const ')' { $$ = $3 & 0xFF; }
|
|
| string { $$ = str2int($1); }
|
|
| T_OP_LOGICNOT const %prec NEG { $$ = !$2; }
|
|
| const T_OP_LOGICOR const { $$ = $1 || $3; }
|
|
| const T_OP_LOGICAND const { $$ = $1 && $3; }
|
|
| const T_OP_LOGICEQU const { $$ = $1 == $3; }
|
|
| const T_OP_LOGICGT const { $$ = $1 > $3; }
|
|
| const T_OP_LOGICLT const { $$ = $1 < $3; }
|
|
| const T_OP_LOGICGE const { $$ = $1 >= $3; }
|
|
| const T_OP_LOGICLE const { $$ = $1 <= $3; }
|
|
| const T_OP_LOGICNE const { $$ = $1 != $3; }
|
|
| const T_OP_ADD const { $$ = $1 + $3; }
|
|
| const T_OP_SUB const { $$ = $1 - $3; }
|
|
| T_ID T_OP_SUB T_ID
|
|
{
|
|
if (sym_IsRelocDiffDefined($1, $3) == 0)
|
|
fatalerror("'%s - %s' not defined.", $1, $3);
|
|
$$ = sym_GetDefinedValue($1) - sym_GetDefinedValue($3);
|
|
}
|
|
| const T_OP_XOR const { $$ = $1 ^ $3; }
|
|
| const T_OP_OR const { $$ = $1 | $3; }
|
|
| const T_OP_AND const { $$ = $1 & $3; }
|
|
| const T_OP_SHL const { $$ = $1 << $3; }
|
|
| const T_OP_SHR const { $$ = $1 >> $3; }
|
|
| const T_OP_MUL const { $$ = $1 * $3; }
|
|
| const T_OP_DIV const
|
|
{
|
|
if ($3 == 0)
|
|
fatalerror("division by zero");
|
|
$$ = $1 / $3;
|
|
}
|
|
| const T_OP_MOD const
|
|
{
|
|
if ($3 == 0)
|
|
fatalerror("division by zero");
|
|
$$ = $1 % $3;
|
|
}
|
|
| T_OP_ADD const %prec NEG { $$ = +$2; }
|
|
| T_OP_SUB const %prec NEG { $$ = -$2; }
|
|
| T_OP_NOT const %prec NEG { $$ = ~$2; }
|
|
| T_OP_ROUND '(' const ')' { $$ = math_Round($3); }
|
|
| T_OP_CEIL '(' const ')' { $$ = math_Ceil($3); }
|
|
| T_OP_FLOOR '(' const ')' { $$ = math_Floor($3); }
|
|
| T_OP_FDIV '(' const comma const ')' { $$ = math_Div($3,$5); }
|
|
| T_OP_FMUL '(' const comma const ')' { $$ = math_Mul($3,$5); }
|
|
| T_OP_SIN '(' const ')' { $$ = math_Sin($3); }
|
|
| T_OP_COS '(' const ')' { $$ = math_Cos($3); }
|
|
| T_OP_TAN '(' const ')' { $$ = math_Tan($3); }
|
|
| T_OP_ASIN '(' const ')' { $$ = math_ASin($3); }
|
|
| T_OP_ACOS '(' const ')' { $$ = math_ACos($3); }
|
|
| T_OP_ATAN '(' const ')' { $$ = math_ATan($3); }
|
|
| T_OP_ATAN2 '(' const comma const ')' { $$ = math_ATan2($3,$5); }
|
|
| T_OP_DEF {
|
|
oDontExpandStrings = true;
|
|
} '(' T_ID ')'
|
|
{
|
|
$$ = sym_isConstDefined($4);
|
|
oDontExpandStrings = false;
|
|
}
|
|
| T_OP_STRCMP '(' string comma string ')'
|
|
{
|
|
$$ = strcmp($3, $5);
|
|
}
|
|
| T_OP_STRIN '(' string comma string ')'
|
|
{
|
|
char *p = strstr($3, $5);
|
|
|
|
if (p != NULL)
|
|
$$ = p - $3 + 1;
|
|
else
|
|
$$ = 0;
|
|
}
|
|
| T_OP_STRLEN '(' string ')' { $$ = strlen($3); }
|
|
| '(' const ')' { $$ = $2; }
|
|
;
|
|
|
|
string : T_STRING
|
|
{
|
|
if (snprintf($$, MAXSTRLEN + 1, "%s", $1) > MAXSTRLEN)
|
|
warning("String is too long '%s'", $1);
|
|
}
|
|
| T_OP_STRSUB '(' string comma uconst comma uconst ')'
|
|
{
|
|
uint32_t len = $7;
|
|
if (len > MAXSTRLEN) {
|
|
warning("STRSUB: Length too big: %u", len);
|
|
len = MAXSTRLEN;
|
|
}
|
|
|
|
if (snprintf($$, len + 1, "%s", $3 + $5 - 1) > MAXSTRLEN)
|
|
warning("STRSUB: String too long '%s'", $$);
|
|
}
|
|
| T_OP_STRCAT '(' string comma string ')'
|
|
{
|
|
if (snprintf($$, MAXSTRLEN + 1, "%s%s", $3, $5) > MAXSTRLEN)
|
|
warning("STRCAT: String too long '%s%s'", $3, $5);
|
|
}
|
|
| T_OP_STRUPR '(' string ')'
|
|
{
|
|
if (snprintf($$, MAXSTRLEN + 1, "%s", $3) > MAXSTRLEN)
|
|
warning("STRUPR: String too long '%s'", $3);
|
|
|
|
upperstring($$);
|
|
}
|
|
| T_OP_STRLWR '(' string ')'
|
|
{
|
|
if (snprintf($$, MAXSTRLEN + 1, "%s", $3) > MAXSTRLEN)
|
|
warning("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 { $$ = SECT_WRAM0; }
|
|
| T_SECT_VRAM { $$ = SECT_VRAM; }
|
|
| T_SECT_ROMX { $$ = SECT_ROMX; }
|
|
| T_SECT_ROM0 { $$ = SECT_ROM0; }
|
|
| T_SECT_HRAM { $$ = SECT_HRAM; }
|
|
| T_SECT_WRAMX { $$ = SECT_WRAMX; }
|
|
| T_SECT_SRAM { $$ = SECT_SRAM; }
|
|
| T_SECT_OAM { $$ = SECT_OAM; }
|
|
| T_SECT_HOME
|
|
{
|
|
warning("HOME section name is deprecated, use ROM0 instead.");
|
|
$$ = SECT_ROM0;
|
|
}
|
|
| T_SECT_DATA
|
|
{
|
|
warning("DATA section name is deprecated, use ROMX instead.");
|
|
$$ = SECT_ROMX;
|
|
}
|
|
| T_SECT_CODE
|
|
{
|
|
warning("CODE section name is deprecated, use ROMX instead.");
|
|
$$ = SECT_ROMX;
|
|
}
|
|
| T_SECT_BSS
|
|
{
|
|
warning("BSS section name is deprecated, use WRAM0 instead.");
|
|
$$ = SECT_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);
|
|
if (nPass == 1)
|
|
warning("'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));
|
|
if (nPass == 1)
|
|
warning("'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));
|
|
if (nPass == 1)
|
|
warning("'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_isReloc(&$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_isReloc(&$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);
|
|
}
|
|
;
|
|
|
|
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("'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_isReloc(&$2)) && ($2.nVal >= 0xFF00)) {
|
|
out_AbsByte(0xE0);
|
|
out_AbsByte($2.nVal & 0xFF);
|
|
} 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_isReloc(&$4)) && ($4.nVal >= 0xFF00)) {
|
|
out_AbsByte(0xF0);
|
|
out_AbsByte($4.nVal & 0xFF);
|
|
} else {
|
|
out_AbsByte(0xFA);
|
|
out_RelWord(&$4);
|
|
}
|
|
} else {
|
|
yyerror("Destination operand must be A");
|
|
}
|
|
}
|
|
;
|
|
|
|
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_isReloc(&$2))
|
|
yyerror("Address for RST must be absolute");
|
|
else if (($2.nVal & 0x38) != $2.nVal)
|
|
yyerror("Invalid address $%x for RST", $2.nVal);
|
|
else
|
|
out_AbsByte(0xC7 | $2.nVal);
|
|
}
|
|
;
|
|
|
|
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);
|
|
}
|
|
;
|
|
|
|
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; }
|
|
;
|
|
|
|
%%
|