ref: 9687e6e1dd071ccceb427cc5d6ac03f2d313e79f
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; }