ref: c424a9bf5aef77a26f59e6b15dec8c00e43f28f0
dir: /src/asm/constexpr.c/
/*
* This file is part of RGBDS.
*
* Copyright (c) 1997-2018, Carsten Sorensen and RGBDS contributors.
*
* SPDX-License-Identifier: MIT
*/
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include "asm/asm.h"
#include "asm/constexpr.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/warning.h"
#include "asmy.h"
void constexpr_Symbol(struct ConstExpression *expr, char *tzSym)
{
struct sSymbol *sym = sym_FindSymbol(tzSym);
if (!sym) {
fatalerror("'%s' not defined", tzSym);
} else if (!sym_IsConstant(sym)) {
expr->u.pSym = sym;
expr->isSym = 1;
} else {
constexpr_Number(expr, sym_GetConstantValue(tzSym));
}
}
void constexpr_BankSymbol(struct ConstExpression *expr, char *tzSym)
{
constexpr_Number(expr, 0);
struct sSymbol *sym = sym_FindSymbol(tzSym);
if (!sym) {
yyerror("BANK argument doesn't exist");
} else if (sym == pPCSymbol) {
if (pCurrentSection->nBank == -1)
yyerror("Current bank is not known yet");
else
constexpr_Number(expr, pCurrentSection->nBank);
} else if (sym->type != SYM_LABEL) {
yyerror("BANK argument must be a label");
} else if (sym->pSection->nBank == -1) {
yyerror("BANK argument's bank is not known yet'");
} else {
constexpr_Number(expr, sym->pSection->nBank);
}
}
void constexpr_BankSection(struct ConstExpression *expr, char *tzSectionName)
{
constexpr_Number(expr, 0);
struct Section *pSection = out_FindSectionByName(tzSectionName);
if (!pSection)
yyerror("Section \"%s\" doesn't exist", tzSectionName);
else if (pSection->nBank == -1)
yyerror("Section \"%s\"'s bank is not known yet",
tzSectionName);
else
constexpr_Number(expr, pSection->nBank);
}
void constexpr_Number(struct ConstExpression *expr, int32_t i)
{
expr->u.nVal = i;
expr->isSym = 0;
}
void constexpr_UnaryOp(struct ConstExpression *expr,
int32_t op,
const struct ConstExpression *src)
{
if (src->isSym)
fatalerror("Non-constant operand in constant expression");
int32_t value = src->u.nVal;
int32_t result = 0;
switch (op) {
case T_OP_HIGH:
result = (value >> 8) & 0xFF;
break;
case T_OP_LOW:
result = value & 0xFF;
break;
case T_OP_LOGICNOT:
result = !value;
break;
case T_OP_ADD:
result = value;
break;
case T_OP_SUB:
result = -(uint32_t)value;
break;
case T_OP_NOT:
result = ~value;
break;
case T_OP_ROUND:
result = math_Round(value);
break;
case T_OP_CEIL:
result = math_Ceil(value);
break;
case T_OP_FLOOR:
result = math_Floor(value);
break;
case T_OP_SIN:
result = math_Sin(value);
break;
case T_OP_COS:
result = math_Cos(value);
break;
case T_OP_TAN:
result = math_Tan(value);
break;
case T_OP_ASIN:
result = math_ASin(value);
break;
case T_OP_ACOS:
result = math_ACos(value);
break;
case T_OP_ATAN:
result = math_ATan(value);
break;
default:
fatalerror("Unknown unary op");
}
constexpr_Number(expr, result);
}
void constexpr_BinaryOp(struct ConstExpression *expr,
int32_t op,
const struct ConstExpression *src1,
const struct ConstExpression *src2)
{
int32_t value1;
int32_t value2;
int32_t result = 0;
if (op == T_OP_SUB && src1->isSym && src2->isSym) {
char *symName1 = src1->u.pSym->tzName;
char *symName2 = src2->u.pSym->tzName;
if (!sym_IsRelocDiffDefined(symName1, symName2))
fatalerror("'%s - %s' not defined", symName1, symName2);
value1 = sym_GetDefinedValue(symName1);
value2 = sym_GetDefinedValue(symName2);
result = value1 - value2;
} else if (src1->isSym || src2->isSym) {
fatalerror("Non-constant operand in constant expression");
} else {
value1 = src1->u.nVal;
value2 = src2->u.nVal;
switch (op) {
case T_OP_LOGICOR:
result = value1 || value2;
break;
case T_OP_LOGICAND:
result = value1 && value2;
break;
case T_OP_LOGICEQU:
result = value1 == value2;
break;
case T_OP_LOGICGT:
result = value1 > value2;
break;
case T_OP_LOGICLT:
result = value1 < value2;
break;
case T_OP_LOGICGE:
result = value1 >= value2;
break;
case T_OP_LOGICLE:
result = value1 <= value2;
break;
case T_OP_LOGICNE:
result = value1 != value2;
break;
case T_OP_ADD:
result = (uint32_t)value1 + (uint32_t)value2;
break;
case T_OP_SUB:
result = (uint32_t)value1 - (uint32_t)value2;
break;
case T_OP_XOR:
result = value1 ^ value2;
break;
case T_OP_OR:
result = value1 | value2;
break;
case T_OP_AND:
result = value1 & value2;
break;
case T_OP_SHL:
case T_OP_SHR:
if (value2 < 0)
warning(WARNING_SHIFT_AMOUNT, "Shifting %s by negative amount %d",
op == T_OP_SHL ? "left" : "right",
value2);
if (op == T_OP_SHR) {
value2 = -value2; /* Right shift == neg left */
if (value1 < 0)
warning(WARNING_SHIFT, "Shifting negative value %d",
value1);
}
if (value2 >= 0) {
// Shift left
if (value2 >= 32) {
warning(WARNING_SHIFT_AMOUNT, "Shifting left by large amount %d",
value2);
result = 0;
} else {
/*
* Use unsigned to force a bitwise shift
* Casting back is OK because the types
* implement two's complement behavior
*/
result = (uint32_t)value1 << value2;
}
} else {
// Shift right
value2 = -value2;
if (value2 >= 32) {
warning(WARNING_SHIFT_AMOUNT, "Shifting right by large amount %d",
value2);
result = value1 < 0 ? -1 : 0;
} else if (value1 >= 0) {
result = value1 >> value2;
} else {
/*
* The C standard leaves shifting right
* negative values undefined, so use a
* left shift manually sign-extended
*/
result = (uint32_t)value1 >> value2 |
-((uint32_t)1 << (32 - value2));
}
}
break;
case T_OP_MUL:
result = (uint32_t)value1 * (uint32_t)value2;
break;
case T_OP_DIV:
if (value2 == 0)
fatalerror("Division by zero");
if (value1 == INT32_MIN && value2 == -1) {
warning(WARNING_DIV, "Division of min value by -1");
result = INT32_MIN;
} else {
result = value1 / value2;
}
break;
case T_OP_MOD:
if (value2 == 0)
fatalerror("Division by zero");
if (value1 == INT32_MIN && value2 == -1)
result = 0;
else
result = value1 % value2;
break;
case T_OP_FDIV:
result = math_Div(value1, value2);
break;
case T_OP_FMUL:
result = math_Mul(value1, value2);
break;
case T_OP_ATAN2:
result = math_ATan2(value1, value2);
break;
default:
fatalerror("Unknown binary op");
}
}
constexpr_Number(expr, result);
}
int32_t constexpr_GetConstantValue(struct ConstExpression *expr)
{
if (expr->isSym)
fatalerror("Non-constant expression");
return expr->u.nVal;
}