305 lines
10 KiB
C
305 lines
10 KiB
C
#include "eval.h"
|
|
#include "parse.h"
|
|
#include "report.h"
|
|
#include "resolve.h"
|
|
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
|
|
static inline uint16_t eval_poperandty_unary(POperandTy ty, uint16_t operand)
|
|
{
|
|
switch (ty) {
|
|
case PoTy_Not:
|
|
return ~operand;
|
|
case PoTy_Negate:
|
|
return (uint16_t)-(int16_t)operand;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static inline uint16_t eval_poperandty_binary(
|
|
POperandTy ty, uint16_t left, uint16_t right)
|
|
{
|
|
switch (ty) {
|
|
case PoTy_Or:
|
|
return left | right;
|
|
case PoTy_Xor:
|
|
return left ^ right;
|
|
case PoTy_And:
|
|
return left & right;
|
|
case PoTy_Shl:
|
|
return (uint16_t)(left << right);
|
|
case PoTy_Shr:
|
|
return (uint16_t)(left >> right);
|
|
case PoTy_Add:
|
|
return (uint16_t)((int16_t)left + (int16_t)right);
|
|
case PoTy_Sub:
|
|
return (uint16_t)((int16_t)left - (int16_t)right);
|
|
case PoTy_Mul:
|
|
return (uint16_t)((int16_t)left * (int16_t)right);
|
|
case PoTy_Div:
|
|
return (uint16_t)((int16_t)left / (int16_t)right);
|
|
case PoTy_Mod:
|
|
return (uint16_t)((int16_t)left % (int16_t)right);
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
EvaledOperand eval_operand_to_imm(
|
|
OperandEvaluator* evaluator, POperand* operand)
|
|
{
|
|
switch (operand->ty) {
|
|
case PoTy_Str:
|
|
REPORTF_ERROR("%s", "strings cannot be part of expressions");
|
|
reporter_print_loc(evaluator->rep, operand->loc);
|
|
return (EvaledOperand) { .ty = EoTy_Err };
|
|
case PoTy_Mem8:
|
|
case PoTy_Mem16:
|
|
REPORTF_ERROR("%s", "indirections cannot be part of expressions");
|
|
reporter_print_loc(evaluator->rep, operand->loc);
|
|
return (EvaledOperand) { .ty = EoTy_Err };
|
|
case PoTy_Reg:
|
|
REPORTF_ERROR("%s", "registers cannot be part of expressions");
|
|
reporter_print_loc(evaluator->rep, operand->loc);
|
|
return (EvaledOperand) { .ty = EoTy_Err };
|
|
case PoTy_Imm:
|
|
return (EvaledOperand) { .ty = EoTy_Imm, .imm = operand->imm };
|
|
case PoTy_Ident:
|
|
case PoTy_SubLabel: {
|
|
const IdentResol* re
|
|
= ident_resolver_resolve(evaluator->re, operand->str);
|
|
if (re == NULL) {
|
|
if (!evaluator->second_pass) {
|
|
return (EvaledOperand) { .ty = EoTy_Imm, .imm = 0 };
|
|
}
|
|
REPORTF_ERROR("undefined identifier '%s'", operand->str);
|
|
reporter_print_loc(evaluator->rep, operand->loc);
|
|
return (EvaledOperand) { .ty = EoTy_Err };
|
|
}
|
|
switch (re->ty) {
|
|
case IdentResolTy_None:
|
|
break;
|
|
case IdentResolTy_Label:
|
|
case IdentResolTy_SubLabel:
|
|
return (EvaledOperand) { .ty = EoTy_Imm, .imm = re->ip };
|
|
}
|
|
fprintf(stderr, "unreachable\n");
|
|
exit(1);
|
|
}
|
|
case PoTy_Not:
|
|
case PoTy_Negate: {
|
|
EvaledOperand inner
|
|
= eval_operand_to_imm(evaluator, operand->operand);
|
|
if (inner.ty == EoTy_Err) {
|
|
return inner;
|
|
} else if (inner.ty != EoTy_Imm) {
|
|
REPORTF_ERROR("%s", "operand cannot be used in expressions");
|
|
reporter_print_loc(evaluator->rep, operand->operand->loc);
|
|
return (EvaledOperand) { .ty = EoTy_Err };
|
|
}
|
|
return (EvaledOperand) {
|
|
.ty = EoTy_Imm,
|
|
.imm = eval_poperandty_unary(operand->ty, inner.imm),
|
|
};
|
|
}
|
|
case PoTy_Or:
|
|
case PoTy_Xor:
|
|
case PoTy_And:
|
|
case PoTy_Shl:
|
|
case PoTy_Shr:
|
|
case PoTy_Add:
|
|
case PoTy_Sub:
|
|
case PoTy_Mul:
|
|
case PoTy_Div:
|
|
case PoTy_Mod: {
|
|
EvaledOperand left = eval_operand_to_imm(evaluator, operand->left);
|
|
if (left.ty == EoTy_Err) {
|
|
return left;
|
|
} else if (left.ty != EoTy_Imm) {
|
|
REPORTF_ERROR("%s", "operand cannot be used in expressions");
|
|
reporter_print_loc(evaluator->rep, operand->left->loc);
|
|
return (EvaledOperand) { .ty = EoTy_Err };
|
|
}
|
|
EvaledOperand right
|
|
= eval_operand_to_imm(evaluator, operand->right);
|
|
if (right.ty == EoTy_Err) {
|
|
return right;
|
|
} else if (right.ty != EoTy_Imm) {
|
|
REPORTF_ERROR("%s", "operand cannot be used in expressions");
|
|
reporter_print_loc(evaluator->rep, operand->right->loc);
|
|
return (EvaledOperand) { .ty = EoTy_Err };
|
|
}
|
|
return (EvaledOperand) {
|
|
.ty = EoTy_Imm,
|
|
.imm = eval_poperandty_binary(operand->ty, left.imm, right.imm),
|
|
};
|
|
}
|
|
}
|
|
fprintf(stderr, "unreachable\n");
|
|
exit(1);
|
|
}
|
|
|
|
EvaledOperand eval_operand_indirection_expr(
|
|
OperandEvaluator* evaluator, POperand* operand)
|
|
{
|
|
switch (operand->ty) {
|
|
case PoTy_Reg:
|
|
return (EvaledOperand) {
|
|
.ty = EoTy_Mem8Reg,
|
|
.reg = operand->reg,
|
|
.offset = 0,
|
|
};
|
|
case PoTy_Str:
|
|
REPORTF_ERROR("%s", "strings cannot be part of indirections");
|
|
reporter_print_loc(evaluator->rep, operand->loc);
|
|
return (EvaledOperand) { .ty = EoTy_Err };
|
|
case PoTy_Mem8:
|
|
case PoTy_Mem16:
|
|
REPORTF_ERROR("%s", "indirections cannot be part of indirections");
|
|
reporter_print_loc(evaluator->rep, operand->loc);
|
|
return (EvaledOperand) { .ty = EoTy_Err };
|
|
case PoTy_Imm:
|
|
case PoTy_Ident:
|
|
case PoTy_SubLabel:
|
|
case PoTy_Not:
|
|
case PoTy_Negate:
|
|
case PoTy_Or:
|
|
case PoTy_Xor:
|
|
case PoTy_And:
|
|
case PoTy_Shl:
|
|
case PoTy_Shr:
|
|
case PoTy_Mul:
|
|
case PoTy_Div:
|
|
case PoTy_Mod: {
|
|
EvaledOperand evaled = eval_operand_to_imm(evaluator, operand);
|
|
if (evaled.ty == EoTy_Err) {
|
|
return evaled;
|
|
}
|
|
return (EvaledOperand) {
|
|
.ty = EoTy_Mem8Imm,
|
|
.imm = evaled.imm,
|
|
};
|
|
}
|
|
case PoTy_Add: {
|
|
if (operand->left->ty == PoTy_Reg) {
|
|
EvaledOperand right
|
|
= eval_operand_to_imm(evaluator, operand->right);
|
|
if (right.ty == EoTy_Err) {
|
|
return right;
|
|
}
|
|
return (EvaledOperand) {
|
|
.ty = EoTy_Mem8Imm,
|
|
.reg = operand->left->reg,
|
|
.offset = right.imm,
|
|
};
|
|
} else if (operand->right->ty == PoTy_Reg) {
|
|
EvaledOperand left
|
|
= eval_operand_to_imm(evaluator, operand->left);
|
|
if (left.ty == EoTy_Err) {
|
|
return left;
|
|
}
|
|
return (EvaledOperand) {
|
|
.ty = EoTy_Mem8Imm,
|
|
.reg = operand->right->reg,
|
|
.offset = left.imm,
|
|
};
|
|
} else {
|
|
EvaledOperand evaled = eval_operand_to_imm(evaluator, operand);
|
|
if (evaled.ty == EoTy_Err) {
|
|
return evaled;
|
|
}
|
|
return (EvaledOperand) {
|
|
.ty = EoTy_Mem8Imm,
|
|
.imm = evaled.imm,
|
|
};
|
|
}
|
|
break;
|
|
}
|
|
case PoTy_Sub: {
|
|
if (operand->left->ty == PoTy_Reg) {
|
|
EvaledOperand right
|
|
= eval_operand_to_imm(evaluator, operand->right);
|
|
if (right.ty == EoTy_Err) {
|
|
return right;
|
|
}
|
|
return (EvaledOperand) {
|
|
.ty = EoTy_Mem8Imm,
|
|
.reg = operand->left->reg,
|
|
.offset = (uint16_t)-(int16_t)right.imm,
|
|
};
|
|
} else if (operand->right->ty == PoTy_Reg) {
|
|
EvaledOperand left
|
|
= eval_operand_to_imm(evaluator, operand->left);
|
|
if (left.ty == EoTy_Err) {
|
|
return left;
|
|
}
|
|
return (EvaledOperand) {
|
|
.ty = EoTy_Mem8Imm,
|
|
.reg = operand->right->reg,
|
|
.offset = (uint16_t)-(int16_t)left.imm,
|
|
};
|
|
} else {
|
|
EvaledOperand evaled = eval_operand_to_imm(evaluator, operand);
|
|
if (evaled.ty == EoTy_Err) {
|
|
return evaled;
|
|
}
|
|
return (EvaledOperand) {
|
|
.ty = EoTy_Mem8Imm,
|
|
.imm = evaled.imm,
|
|
};
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
fprintf(stderr, "unreachable\n");
|
|
exit(1);
|
|
}
|
|
|
|
EvaledOperand eval_operand(OperandEvaluator* evaluator, POperand* operand)
|
|
{
|
|
switch (operand->ty) {
|
|
case PoTy_Str:
|
|
return (EvaledOperand) { .ty = EoTy_Str };
|
|
case PoTy_Mem8:
|
|
return eval_operand_indirection_expr(evaluator, operand->operand);
|
|
case PoTy_Mem16: {
|
|
EvaledOperand evaled
|
|
= eval_operand_indirection_expr(evaluator, operand->operand);
|
|
switch (evaled.ty) {
|
|
case EoTy_Mem8Reg:
|
|
evaled.ty = EoTy_MemU16Reg;
|
|
break;
|
|
case EoTy_Mem8Imm:
|
|
evaled.ty = EoTy_MemU16Imm;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return evaled;
|
|
}
|
|
case PoTy_Reg:
|
|
return (EvaledOperand) { .ty = EoTy_Reg, .reg = operand->reg };
|
|
case PoTy_Imm:
|
|
case PoTy_Ident:
|
|
case PoTy_SubLabel:
|
|
case PoTy_Not:
|
|
case PoTy_Negate:
|
|
case PoTy_Or:
|
|
case PoTy_Xor:
|
|
case PoTy_And:
|
|
case PoTy_Shl:
|
|
case PoTy_Shr:
|
|
case PoTy_Add:
|
|
case PoTy_Sub:
|
|
case PoTy_Mul:
|
|
case PoTy_Div:
|
|
case PoTy_Mod:
|
|
return eval_operand_to_imm(evaluator, operand);
|
|
}
|
|
fprintf(stderr, "unreachable\n");
|
|
exit(1);
|
|
}
|