#include "eval.h" #include "parse.h" #include "report.h" #include "resolve.h" #include #include #include #include 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 }; case IdentResolTy_Const: return (EvaledOperand) { .ty = EoTy_Imm, .imm = re->value }; break; } 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); }