ref: 65121e6d5d2b9cb15283a0d5c46addeb67d20a7b
dir: /src/asm/globlex.c/
/*
* This file is part of RGBDS.
*
* Copyright (c) 1997-2018, Carsten Sorensen and RGBDS contributors.
*
* SPDX-License-Identifier: MIT
*/
#include <math.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "asm/asm.h"
#include "asm/lexer.h"
#include "asm/macro.h"
#include "asm/main.h"
#include "asm/rpn.h"
#include "asm/section.h"
#include "asm/warning.h"
#include "helpers.h"
#include "asmy.h"
bool oDontExpandStrings;
int32_t nGBGfxID = -1;
int32_t nBinaryID = -1;
static int32_t gbgfx2bin(char ch)
{
int32_t i;
for (i = 0; i <= 3; i++) {
if (CurrentOptions.gbgfx[i] == ch)
return i;
}
return 0;
}
static int32_t binary2bin(char ch)
{
int32_t i;
for (i = 0; i <= 1; i++) {
if (CurrentOptions.binary[i] == ch)
return i;
}
return 0;
}
static int32_t char2bin(char ch)
{
if (ch >= 'a' && ch <= 'f')
return (ch - 'a' + 10);
if (ch >= 'A' && ch <= 'F')
return (ch - 'A' + 10);
if (ch >= '0' && ch <= '9')
return (ch - '0');
return 0;
}
typedef int32_t(*x2bin) (char ch);
static int32_t ascii2bin(char *s)
{
char *start = s;
uint32_t radix = 10;
uint32_t result = 0;
x2bin convertfunc = char2bin;
switch (*s) {
case '$':
radix = 16;
s++;
convertfunc = char2bin;
break;
case '&':
radix = 8;
s++;
convertfunc = char2bin;
break;
case '`':
radix = 4;
s++;
convertfunc = gbgfx2bin;
break;
case '%':
radix = 2;
s++;
convertfunc = binary2bin;
break;
default:
/* Handle below */
break;
}
const uint32_t max_q = UINT32_MAX / radix;
const uint32_t max_r = UINT32_MAX % radix;
if (*s == '\0') {
/*
* There are no digits after the radix prefix
* (or the string is empty, which shouldn't happen).
*/
yyerror("Invalid integer constant");
} else if (radix == 4) {
int32_t size = 0;
int32_t c;
while (*s != '\0') {
c = convertfunc(*s++);
result = result * 2 + ((c & 2) << 7) + (c & 1);
size++;
}
/*
* Extending a graphics constant longer than 8 pixels,
* the Game Boy tile width, produces a nonsensical result.
*/
if (size > 8) {
warning(WARNING_LARGE_CONSTANT, "Graphics constant '%s' is too long",
start);
}
} else {
bool overflow = false;
while (*s != '\0') {
int32_t digit = convertfunc(*s++);
if (result > max_q
|| (result == max_q && digit > max_r)) {
overflow = true;
}
result = result * radix + digit;
}
if (overflow)
warning(WARNING_LARGE_CONSTANT, "Integer constant '%s' is too large",
start);
}
return result;
}
uint32_t ParseFixedPoint(char *s, uint32_t size)
{
uint32_t i;
uint32_t dot = 0;
for (i = 0; i < size; i++) {
if (s[i] == '.') {
dot++;
if (dot == 2)
break;
}
}
yyskipbytes(i);
yylval.nConstValue = (int32_t)(atof(s) * 65536);
return 1;
}
uint32_t ParseNumber(char *s, uint32_t size)
{
char dest[256];
if (size > 255)
fatalerror("Number token too long");
strncpy(dest, s, size);
dest[size] = 0;
yylval.nConstValue = ascii2bin(dest);
yyskipbytes(size);
return 1;
}
/*
* If the symbol name ends before the end of the macro arg,
* return a pointer to the rest of the macro arg.
* Otherwise, return NULL.
*/
char const *AppendMacroArg(char whichArg, char *dest, size_t *destIndex)
{
char const *marg;
if (whichArg == '@')
marg = macro_GetUniqueIDStr();
else if (whichArg >= '1' && whichArg <= '9')
marg = macro_GetArg(whichArg - '0');
else
fatalerror("Invalid macro argument '\\%c' in symbol", whichArg);
if (!marg)
fatalerror("Macro argument '\\%c' not defined", whichArg);
char ch;
while ((ch = *marg) != 0) {
if ((ch >= 'a' && ch <= 'z')
|| (ch >= 'A' && ch <= 'Z')
|| (ch >= '0' && ch <= '9')
|| ch == '_'
|| ch == '@'
|| ch == '#'
|| ch == '.') {
if (*destIndex >= MAXSYMLEN)
fatalerror("Symbol too long");
dest[*destIndex] = ch;
(*destIndex)++;
} else {
return marg;
}
marg++;
}
return NULL;
}
uint32_t ParseSymbol(char *src, uint32_t size)
{
char dest[MAXSYMLEN + 1];
size_t srcIndex = 0;
size_t destIndex = 0;
char const *rest = NULL;
while (srcIndex < size) {
char ch = src[srcIndex++];
if (ch == '\\') {
/*
* We don't check if srcIndex is still less than size,
* but that can only fail to be true when the
* following char is neither '@' nor a digit.
* In that case, AppendMacroArg() will catch the error.
*/
ch = src[srcIndex++];
rest = AppendMacroArg(ch, dest, &destIndex);
/* If the symbol's end was in the middle of the token */
if (rest)
break;
} else {
if (destIndex >= MAXSYMLEN)
fatalerror("Symbol too long");
dest[destIndex++] = ch;
}
}
dest[destIndex] = 0;
/* Tell the lexer we read all bytes that we did */
yyskipbytes(srcIndex);
/*
* If an escape's expansion left some chars after the symbol's end,
* such as the `::` in a `Backup\1` expanded to `BackupCamX::`,
* put those into the buffer.
* Note that this NEEDS to be done after the `yyskipbytes` above.
*/
if (rest)
yyunputstr(rest);
/* If the symbol is an EQUS, expand it */
if (!oDontExpandStrings) {
struct sSymbol const *sym = sym_FindSymbol(dest);
if (sym && sym->type == SYM_EQUS) {
char *s;
lex_BeginStringExpansion(dest);
/* Feed the symbol's contents into the buffer */
yyunputstr(s = sym_GetStringValue(sym));
/* Lines inserted this way shall not increase nLineNo */
while (*s) {
if (*s++ == '\n')
nLineNo--;
}
return 0;
}
}
strcpy(yylval.tzSym, dest);
return 1;
}
uint32_t PutMacroArg(char *src, uint32_t size)
{
char const *s;
yyskipbytes(size);
if ((size == 2 && src[1] >= '1' && src[1] <= '9')) {
s = macro_GetArg(src[1] - '0');
if (s != NULL)
yyunputstr(s);
else
yyerror("Macro argument '\\%c' not defined", src[1]);
} else {
yyerror("Invalid macro argument '\\%c'", src[1]);
}
return 0;
}
uint32_t PutUniqueID(char *src, uint32_t size)
{
(void)src;
char const *s;
yyskipbytes(size);
s = macro_GetUniqueIDStr();
if (s != NULL)
yyunputstr(s);
else
yyerror("Macro unique label string not defined");
return 0;
}
enum {
T_LEX_MACROARG = 3000,
T_LEX_MACROUNIQUE
};
const struct sLexInitString lexer_strings[] = {
{"adc", T_Z80_ADC},
{"add", T_Z80_ADD},
{"and", T_Z80_AND},
{"bit", T_Z80_BIT},
{"call", T_Z80_CALL},
{"ccf", T_Z80_CCF},
{"cpl", T_Z80_CPL},
{"cp", T_Z80_CP},
{"daa", T_Z80_DAA},
{"dec", T_Z80_DEC},
{"di", T_Z80_DI},
{"ei", T_Z80_EI},
{"halt", T_Z80_HALT},
{"inc", T_Z80_INC},
{"jp", T_Z80_JP},
{"jr", T_Z80_JR},
{"ld", T_Z80_LD},
{"ldi", T_Z80_LDI},
{"ldd", T_Z80_LDD},
{"ldio", T_Z80_LDIO},
{"ldh", T_Z80_LDIO},
{"nop", T_Z80_NOP},
{"or", T_Z80_OR},
{"pop", T_Z80_POP},
{"push", T_Z80_PUSH},
{"res", T_Z80_RES},
{"reti", T_Z80_RETI},
{"ret", T_Z80_RET},
{"rlca", T_Z80_RLCA},
{"rlc", T_Z80_RLC},
{"rla", T_Z80_RLA},
{"rl", T_Z80_RL},
{"rrc", T_Z80_RRC},
{"rrca", T_Z80_RRCA},
{"rra", T_Z80_RRA},
{"rr", T_Z80_RR},
{"rst", T_Z80_RST},
{"sbc", T_Z80_SBC},
{"scf", T_Z80_SCF},
{"set", T_POP_SET},
{"sla", T_Z80_SLA},
{"sra", T_Z80_SRA},
{"srl", T_Z80_SRL},
{"stop", T_Z80_STOP},
{"sub", T_Z80_SUB},
{"swap", T_Z80_SWAP},
{"xor", T_Z80_XOR},
{"nz", T_CC_NZ},
{"z", T_CC_Z},
{"nc", T_CC_NC},
/* Handled in list of registers */
/* { "c", T_TOKEN_C }, */
{"[bc]", T_MODE_BC_IND},
{"[de]", T_MODE_DE_IND},
{"[hl]", T_MODE_HL_IND},
{"[hl+]", T_MODE_HL_INDINC},
{"[hl-]", T_MODE_HL_INDDEC},
{"[hli]", T_MODE_HL_INDINC},
{"[hld]", T_MODE_HL_INDDEC},
{"af", T_MODE_AF},
{"bc", T_MODE_BC},
{"de", T_MODE_DE},
{"hl", T_MODE_HL},
{"sp", T_MODE_SP},
{"[c]", T_MODE_C_IND},
{"[$ff00+c]", T_MODE_C_IND},
{"[$ff00 + c]", T_MODE_C_IND},
{"a", T_TOKEN_A},
{"b", T_TOKEN_B},
{"c", T_TOKEN_C},
{"d", T_TOKEN_D},
{"e", T_TOKEN_E},
{"h", T_TOKEN_H},
{"l", T_TOKEN_L},
{"||", T_OP_LOGICOR},
{"&&", T_OP_LOGICAND},
{"==", T_OP_LOGICEQU},
{">", T_OP_LOGICGT},
{"<", T_OP_LOGICLT},
{">=", T_OP_LOGICGE},
{"<=", T_OP_LOGICLE},
{"!=", T_OP_LOGICNE},
{"!", T_OP_LOGICNOT},
{"|", T_OP_OR},
{"^", T_OP_XOR},
{"&", T_OP_AND},
{"<<", T_OP_SHL},
{">>", T_OP_SHR},
{"+", T_OP_ADD},
{"-", T_OP_SUB},
{"*", T_OP_MUL},
{"/", T_OP_DIV},
{"%", T_OP_MOD},
{"~", T_OP_NOT},
{"def", T_OP_DEF},
{"bank", T_OP_BANK},
{"align", T_OP_ALIGN},
{"round", T_OP_ROUND},
{"ceil", T_OP_CEIL},
{"floor", T_OP_FLOOR},
{"div", T_OP_FDIV},
{"mul", T_OP_FMUL},
{"sin", T_OP_SIN},
{"cos", T_OP_COS},
{"tan", T_OP_TAN},
{"asin", T_OP_ASIN},
{"acos", T_OP_ACOS},
{"atan", T_OP_ATAN},
{"atan2", T_OP_ATAN2},
{"high", T_OP_HIGH},
{"low", T_OP_LOW},
{"isconst", T_OP_ISCONST},
{"strcmp", T_OP_STRCMP},
{"strin", T_OP_STRIN},
{"strsub", T_OP_STRSUB},
{"strlen", T_OP_STRLEN},
{"strcat", T_OP_STRCAT},
{"strupr", T_OP_STRUPR},
{"strlwr", T_OP_STRLWR},
{"include", T_POP_INCLUDE},
{"printt", T_POP_PRINTT},
{"printi", T_POP_PRINTI},
{"printv", T_POP_PRINTV},
{"printf", T_POP_PRINTF},
{"export", T_POP_EXPORT},
{"xdef", T_POP_XDEF},
{"global", T_POP_GLOBAL},
{"ds", T_POP_DS},
{"db", T_POP_DB},
{"dw", T_POP_DW},
{"dl", T_POP_DL},
{"section", T_POP_SECTION},
{"purge", T_POP_PURGE},
{"rsreset", T_POP_RSRESET},
{"rsset", T_POP_RSSET},
{"incbin", T_POP_INCBIN},
{"charmap", T_POP_CHARMAP},
{"newcharmap", T_POP_NEWCHARMAP},
{"setcharmap", T_POP_SETCHARMAP},
{"pushc", T_POP_PUSHC},
{"popc", T_POP_POPC},
{"fail", T_POP_FAIL},
{"warn", T_POP_WARN},
{"fatal", T_POP_FATAL},
{"assert", T_POP_ASSERT},
{"static_assert", T_POP_STATIC_ASSERT},
{"macro", T_POP_MACRO},
/* Not needed but we have it here just to protect the name */
{"endm", T_POP_ENDM},
{"shift", T_POP_SHIFT},
{"rept", T_POP_REPT},
/* Not needed but we have it here just to protect the name */
{"endr", T_POP_ENDR},
{"load", T_POP_LOAD},
{"endl", T_POP_ENDL},
{"if", T_POP_IF},
{"else", T_POP_ELSE},
{"elif", T_POP_ELIF},
{"endc", T_POP_ENDC},
{"union", T_POP_UNION},
{"nextu", T_POP_NEXTU},
{"endu", T_POP_ENDU},
{"wram0", T_SECT_WRAM0},
{"vram", T_SECT_VRAM},
{"romx", T_SECT_ROMX},
{"rom0", T_SECT_ROM0},
{"hram", T_SECT_HRAM},
{"wramx", T_SECT_WRAMX},
{"sram", T_SECT_SRAM},
{"oam", T_SECT_OAM},
{"rb", T_POP_RB},
{"rw", T_POP_RW},
{"equ", T_POP_EQU},
{"equs", T_POP_EQUS},
/* Handled before in list of CPU instructions */
/* {"set", T_POP_SET}, */
{"=", T_POP_EQUAL},
{"pushs", T_POP_PUSHS},
{"pops", T_POP_POPS},
{"pusho", T_POP_PUSHO},
{"popo", T_POP_POPO},
{"opt", T_POP_OPT},
{NULL, 0}
};
const struct sLexFloat tNumberToken = {
ParseNumber,
T_NUMBER
};
const struct sLexFloat tFixedPointToken = {
ParseFixedPoint,
T_NUMBER
};
const struct sLexFloat tIDToken = {
ParseSymbol,
T_ID
};
const struct sLexFloat tMacroArgToken = {
PutMacroArg,
T_LEX_MACROARG
};
const struct sLexFloat tMacroUniqueToken = {
PutUniqueID,
T_LEX_MACROUNIQUE
};
void setup_lexer(void)
{
uint32_t id;
lex_Init();
lex_AddStrings(lexer_strings);
//Macro arguments
id = lex_FloatAlloc(&tMacroArgToken);
lex_FloatAddFirstRange(id, '\\', '\\');
lex_FloatAddSecondRange(id, '1', '9');
id = lex_FloatAlloc(&tMacroUniqueToken);
lex_FloatAddFirstRange(id, '\\', '\\');
lex_FloatAddSecondRange(id, '@', '@');
//Decimal constants
id = lex_FloatAlloc(&tNumberToken);
lex_FloatAddFirstRange(id, '0', '9');
lex_FloatAddSecondRange(id, '0', '9');
lex_FloatAddRange(id, '0', '9');
//Binary constants
id = lex_FloatAlloc(&tNumberToken);
nBinaryID = id;
lex_FloatAddFirstRange(id, '%', '%');
lex_FloatAddSecondRange(id, CurrentOptions.binary[0],
CurrentOptions.binary[0]);
lex_FloatAddSecondRange(id, CurrentOptions.binary[1],
CurrentOptions.binary[1]);
lex_FloatAddRange(id, CurrentOptions.binary[0],
CurrentOptions.binary[0]);
lex_FloatAddRange(id, CurrentOptions.binary[1],
CurrentOptions.binary[1]);
//Octal constants
id = lex_FloatAlloc(&tNumberToken);
lex_FloatAddFirstRange(id, '&', '&');
lex_FloatAddSecondRange(id, '0', '7');
lex_FloatAddRange(id, '0', '7');
//Gameboy gfx constants
id = lex_FloatAlloc(&tNumberToken);
nGBGfxID = id;
lex_FloatAddFirstRange(id, '`', '`');
lex_FloatAddSecondRange(id, CurrentOptions.gbgfx[0],
CurrentOptions.gbgfx[0]);
lex_FloatAddSecondRange(id, CurrentOptions.gbgfx[1],
CurrentOptions.gbgfx[1]);
lex_FloatAddSecondRange(id, CurrentOptions.gbgfx[2],
CurrentOptions.gbgfx[2]);
lex_FloatAddSecondRange(id, CurrentOptions.gbgfx[3],
CurrentOptions.gbgfx[3]);
lex_FloatAddRange(id, CurrentOptions.gbgfx[0], CurrentOptions.gbgfx[0]);
lex_FloatAddRange(id, CurrentOptions.gbgfx[1], CurrentOptions.gbgfx[1]);
lex_FloatAddRange(id, CurrentOptions.gbgfx[2], CurrentOptions.gbgfx[2]);
lex_FloatAddRange(id, CurrentOptions.gbgfx[3], CurrentOptions.gbgfx[3]);
//Hex constants
id = lex_FloatAlloc(&tNumberToken);
lex_FloatAddFirstRange(id, '$', '$');
lex_FloatAddSecondRange(id, '0', '9');
lex_FloatAddSecondRange(id, 'A', 'F');
lex_FloatAddSecondRange(id, 'a', 'f');
lex_FloatAddRange(id, '0', '9');
lex_FloatAddRange(id, 'A', 'F');
lex_FloatAddRange(id, 'a', 'f');
//ID 's
id = lex_FloatAlloc(&tIDToken);
lex_FloatAddFirstRange(id, 'a', 'z');
lex_FloatAddFirstRange(id, 'A', 'Z');
lex_FloatAddFirstRange(id, '_', '_');
lex_FloatAddSecondRange(id, 'a', 'z');
lex_FloatAddSecondRange(id, 'A', 'Z');
lex_FloatAddSecondRange(id, '0', '9');
lex_FloatAddSecondRange(id, '_', '_');
lex_FloatAddSecondRange(id, '\\', '\\');
lex_FloatAddSecondRange(id, '@', '@');
lex_FloatAddSecondRange(id, '#', '#');
lex_FloatAddRange(id, '.', '.');
lex_FloatAddRange(id, 'a', 'z');
lex_FloatAddRange(id, 'A', 'Z');
lex_FloatAddRange(id, '0', '9');
lex_FloatAddRange(id, '_', '_');
lex_FloatAddRange(id, '\\', '\\');
lex_FloatAddRange(id, '@', '@');
lex_FloatAddRange(id, '#', '#');
//Local ID
id = lex_FloatAlloc(&tIDToken);
lex_FloatAddFirstRange(id, '.', '.');
lex_FloatAddSecondRange(id, 'a', 'z');
lex_FloatAddSecondRange(id, 'A', 'Z');
lex_FloatAddSecondRange(id, '_', '_');
lex_FloatAddRange(id, 'a', 'z');
lex_FloatAddRange(id, 'A', 'Z');
lex_FloatAddRange(id, '0', '9');
lex_FloatAddRange(id, '_', '_');
lex_FloatAddRange(id, '\\', '\\');
lex_FloatAddRange(id, '@', '@');
lex_FloatAddRange(id, '#', '#');
// "@"
id = lex_FloatAlloc(&tIDToken);
lex_FloatAddFirstRange(id, '@', '@');
//Fixed point constants
id = lex_FloatAlloc(&tFixedPointToken);
lex_FloatAddFirstRange(id, '.', '.');
lex_FloatAddFirstRange(id, '0', '9');
lex_FloatAddSecondRange(id, '.', '.');
lex_FloatAddSecondRange(id, '0', '9');
lex_FloatAddRange(id, '.', '.');
lex_FloatAddRange(id, '0', '9');
}