370 lines
17 KiB
C
370 lines
17 KiB
C
#include <stdlib.h>
|
|
#include <assert.h>
|
|
#include "execute.h"
|
|
#include "print.h"
|
|
#include "../shared/binary_util.h"
|
|
#include "machine_util.h"
|
|
|
|
// Defines the maximum value that can be held in a register
|
|
#define MAX_REG_VAL ((1 << DWORD_BITS) - 1)
|
|
|
|
// The number of bits to shift the immediate value in an arithmetic immediate data processing
|
|
// instruction if the shift flag is enabled.
|
|
#define DPI_ARITHM_SHIFT 12
|
|
|
|
// The number of bits to shift the immediate value in a wide move immediate data processing
|
|
// instruction if the shift flag is enabled.
|
|
#define DPI_WIDEMOV_SHIFT 16
|
|
|
|
// Type definition for a pointer to a function that executes a particular type
|
|
// of a64instruction (requires pointer to the processor state to update, and a
|
|
// pointer to the instruction struct to execute).
|
|
typedef void (*executeFunction)(Machine *, a64inst_instruction *);
|
|
|
|
// Prototypes
|
|
static void executeSDT(Machine *state, a64inst_instruction *inst);
|
|
static void executeBranch(Machine *state, a64inst_instruction *inst);
|
|
static void executeDPImmediate(Machine *state, a64inst_instruction *inst);
|
|
static void executeDPRegister(Machine *state, a64inst_instruction *inst);
|
|
|
|
// Halt function definition
|
|
static void executeHalt(Machine *state, a64inst_instruction *inst) { /*NOP*/ }
|
|
|
|
// Define a constant array mapping instruction type enum values to their
|
|
// corresponding execute functions
|
|
const executeFunction EXECUTE_FUNCTIONS[] =
|
|
{&executeDPImmediate,
|
|
&executeDPRegister,
|
|
&executeSDT,
|
|
&executeSDT,
|
|
&executeBranch,
|
|
&executeHalt};
|
|
|
|
// Main execute function
|
|
void execute(Machine *state, a64inst_instruction *inst) {
|
|
EXECUTE_FUNCTIONS[inst->type](state, inst);
|
|
}
|
|
|
|
// Updates N and Z condition codes given the machine and a result value
|
|
static void updateCondNZ(Machine *state, dword result, a64inst_regType regType) {
|
|
state->conditionCodes.Negative = getMSB(result, regType);
|
|
state->conditionCodes.Zero = result == 0;
|
|
}
|
|
|
|
// Execute a data processing immediate instruction
|
|
static void executeDPImmediate(Machine *state, a64inst_instruction *inst) {
|
|
assert(inst->type == a64inst_DPIMMEDIATE);
|
|
|
|
a64inst_regType regType = inst->data.DPImmediateData.regType;
|
|
a64inst_regSpecifier dest = inst->data.DPImmediateData.dest;
|
|
switch(inst->data.DPImmediateData.DPIOpType) {
|
|
|
|
// Execute an arithmetic immediate data processing instruction
|
|
case a64inst_DPI_ARITHM:;
|
|
|
|
// If shift flag is enabled, logical left shift by the number of bits specified by the architecture
|
|
dword arithmImm = inst->data.DPImmediateData.processOpData.arithmData.immediate;
|
|
dword srcVal = state->registers[inst->data.DPImmediateData.processOpData.arithmData.src];
|
|
if (inst->data.DPImmediateData.processOpData.arithmData.shiftImmediate) {
|
|
arithmImm = truncateValue(arithmImm << DPI_ARITHM_SHIFT, regType);
|
|
}
|
|
|
|
switch(inst->data.DPImmediateData.processOp) {
|
|
|
|
dword result;
|
|
case(a64inst_ADDS):
|
|
result = truncateValue(srcVal + arithmImm, regType);
|
|
writeRegister(state, dest, regType, result);
|
|
|
|
updateCondNZ(state, result, regType);
|
|
state->conditionCodes.Overflow = getMSB(srcVal, regType) == getMSB(arithmImm, regType) && getMSB(result, regType) != getMSB(srcVal, regType);
|
|
state->conditionCodes.Carry = max(truncateValue(srcVal, regType), truncateValue(arithmImm, regType)) > result;
|
|
break;
|
|
|
|
case(a64inst_ADD):
|
|
writeRegister(state, dest, regType, srcVal + arithmImm);
|
|
break;
|
|
|
|
case(a64inst_SUBS):
|
|
result = srcVal - arithmImm;
|
|
writeRegister(state, dest, regType, result);
|
|
|
|
updateCondNZ(state, result, regType);
|
|
state->conditionCodes.Overflow = getMSB(srcVal, regType) != getMSB(arithmImm, regType) && getMSB(arithmImm, regType) == getMSB(result, regType);
|
|
state->conditionCodes.Carry = srcVal >= arithmImm;
|
|
break;
|
|
|
|
case(a64inst_SUB):
|
|
writeRegister(state, dest, regType, srcVal - arithmImm);
|
|
break;
|
|
|
|
// Unknown opcode detected!
|
|
default:
|
|
fprintf(stderr, "Unknown opcode detected in a DPI arithmetic instruction!\n");
|
|
break;
|
|
}
|
|
break;
|
|
|
|
// Execute a wide move immediate data processing instruction
|
|
case a64inst_DPI_WIDEMOV:;
|
|
uint8_t shiftScalar = inst->data.DPImmediateData.processOpData.wideMovData.shiftScalar;
|
|
dword wideMovImm = inst->data.DPImmediateData.processOpData.wideMovData.immediate;
|
|
|
|
// NOTE: Not checking that shiftScalar has valid value for 32bit registers. Possibly add explicit error.
|
|
//printf("%x\n", wideMovImm << (shiftScalar * DPI_WIDEMOV_SHIFT) & );
|
|
wideMovImm = truncateValue(wideMovImm << (shiftScalar * DPI_WIDEMOV_SHIFT), regType);
|
|
switch(inst->data.DPImmediateData.processOp) {
|
|
|
|
case(a64inst_MOVN):
|
|
writeRegister(state, dest, regType, ~wideMovImm);
|
|
break;
|
|
|
|
case(a64inst_MOVZ):
|
|
writeRegister(state, dest, regType, wideMovImm);
|
|
break;
|
|
|
|
case(a64inst_MOVK):;
|
|
dword result = readRegister(state, dest, regType);
|
|
result = (result & ~((((dword)1 << DPI_WIDEMOV_SHIFT) - 1) << shiftScalar * DPI_WIDEMOV_SHIFT)) | wideMovImm;
|
|
writeRegister(state, dest, regType, result);
|
|
break;
|
|
|
|
default:
|
|
fprintf(stderr, "Unknown opcode detected in a DPI wide move instruction!\n");
|
|
break;
|
|
}
|
|
break;
|
|
|
|
// Unknown instruction detected!
|
|
default:
|
|
fprintf(stderr, "Attempting to execute instruction with unknown DPI operand type!\n");
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Execute a data processing register instruction
|
|
static void executeDPRegister(Machine *state, a64inst_instruction *inst) {
|
|
assert(inst->type == a64inst_DPREGISTER);
|
|
|
|
a64inst_regType regType = inst->data.DPRegisterData.regType;
|
|
a64inst_regSpecifier dest = inst->data.DPRegisterData.dest;
|
|
dword src1Val = readRegister(state, inst->data.DPRegisterData.src1, regType);
|
|
dword src2Val = readRegister(state, inst->data.DPRegisterData.src2, regType);
|
|
|
|
switch(inst->data.DPRegisterData.DPROpType) {
|
|
|
|
// Execute an arithmetic or logic register data processing instruction
|
|
case a64inst_DPR_ARITHMLOGIC:;
|
|
|
|
// Apply shift to value held in second register
|
|
a64inst_DPRegister_ArithmLogicData *arithmLogicData = &inst->data.DPRegisterData.processOpData.arithmLogicData;
|
|
uint8_t shiftAmount = arithmLogicData->shiftAmount;
|
|
switch(arithmLogicData->shiftType) {
|
|
|
|
case a64inst_LSL:
|
|
src2Val = truncateValue(src2Val << shiftAmount, regType);
|
|
break;
|
|
|
|
case a64inst_LSR:
|
|
src2Val = truncateValue(src2Val >> shiftAmount, regType);
|
|
break;
|
|
|
|
case a64inst_ASR:
|
|
if (regType == a64inst_X) {
|
|
src2Val = truncateValue((int64_t)src2Val >> shiftAmount, regType);
|
|
} else {
|
|
src2Val = truncateValue((int32_t)src2Val >> shiftAmount, regType);
|
|
}
|
|
break;
|
|
|
|
case a64inst_ROR:
|
|
if (arithmLogicData->type != a64inst_DPR_LOGIC) {
|
|
fprintf(stderr, "Attempting to perform ROR shift on non-logic register data processing instruction!\n");
|
|
}
|
|
src2Val = truncateValue((src2Val >> shiftAmount) | (src2Val << (getMSBPos(regType) + 1 - shiftAmount)), regType);
|
|
break;
|
|
|
|
default:
|
|
fprintf(stderr, "Attempting to execute arithmetic/logic register data processing instruction with invalid shift type!\n");
|
|
break;
|
|
}
|
|
|
|
// Negate second operand if negShiftedSrc2 flag is enabled
|
|
if (arithmLogicData->negShiftedSrc2) {
|
|
src2Val = truncateValue(~src2Val, regType);
|
|
}
|
|
|
|
dword result;
|
|
switch(arithmLogicData->type) {
|
|
|
|
case a64inst_DPR_ARITHM:
|
|
switch(inst->data.DPRegisterData.processOp) {
|
|
|
|
case(a64inst_ADDS):
|
|
result = truncateValue(src1Val + src2Val, regType);
|
|
writeRegister(state, dest, regType, result);
|
|
|
|
updateCondNZ(state, result, regType);
|
|
state->conditionCodes.Overflow = getMSB(src1Val, regType) == getMSB(src2Val, regType) && getMSB(result, regType) != getMSB(src1Val, regType);
|
|
state->conditionCodes.Carry = max(truncateValue(src1Val, regType), truncateValue(src2Val, regType)) > result;
|
|
break;
|
|
|
|
case(a64inst_ADD):
|
|
writeRegister(state, dest, regType, src1Val + src2Val);
|
|
break;
|
|
|
|
case(a64inst_SUBS):
|
|
result = src1Val - src2Val;
|
|
writeRegister(state, dest, regType, result);
|
|
|
|
updateCondNZ(state, result, regType);
|
|
state->conditionCodes.Overflow = getMSB(src1Val, regType) != getMSB(src2Val, regType) && getMSB(src2Val, regType) == getMSB(result, regType);
|
|
state->conditionCodes.Carry = src1Val >= src2Val;
|
|
break;
|
|
|
|
case(a64inst_SUB):
|
|
writeRegister(state, dest, regType, src1Val - src2Val);
|
|
break;
|
|
|
|
// Unknown opcode detected!
|
|
default:
|
|
fprintf(stderr, "Unknown opcode detected in a DPI arithmetic instruction!\n");
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case a64inst_DPR_LOGIC:
|
|
switch(inst->data.DPRegisterData.processOp) {
|
|
|
|
case a64inst_AND:
|
|
writeRegister(state, dest, regType, src1Val & src2Val);
|
|
break;
|
|
|
|
case a64inst_OR:
|
|
writeRegister(state, dest, regType, src1Val | src2Val);
|
|
break;
|
|
|
|
case a64inst_XOR:
|
|
writeRegister(state, dest, regType, src1Val ^ src2Val);
|
|
break;
|
|
|
|
case a64inst_AND_FLAGGED:;
|
|
result = src1Val & src2Val;
|
|
writeRegister(state, dest, regType, result);
|
|
state->conditionCodes.Overflow = 0;
|
|
state->conditionCodes.Carry = 0;
|
|
updateCondNZ(state, result, regType);
|
|
break;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
fprintf(stderr, "Attempting to execute an instruction with an unknown DPR arithmetic or logic subtype!\n");
|
|
break;
|
|
}
|
|
break;
|
|
|
|
// Execute a multiply register data processing instruction
|
|
case a64inst_DPR_MULTIPLY:;
|
|
dword product = state->registers[inst->data.DPRegisterData.src1] * state->registers[inst->data.DPRegisterData.src2];
|
|
dword sum = readRegister(state, inst->data.DPRegisterData.processOpData.multiplydata.summand, inst->data.DPRegisterData.regType) + (inst->data.DPRegisterData.processOpData.multiplydata.negProd ? -product : product);
|
|
writeRegister(state, inst->data.DPRegisterData.dest, inst->data.DPRegisterData.regType, sum);
|
|
break;
|
|
|
|
// Unknown instruction detected!
|
|
default:
|
|
fprintf(stderr, "Attempting to execute instruction with unknown DPR operand type!\n");
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void executeSDT(Machine *state, a64inst_instruction *inst) {
|
|
word address;
|
|
bool isLoad;
|
|
if (inst->data.SingleTransferData.SingleTransferOpType == a64inst_SINGLE_TRANSFER_LOAD_LITERAL) {
|
|
// Load Literal
|
|
isLoad = true;
|
|
address = state->pc + signExtend(inst->data.SingleTransferData.processOpData.loadLiteralData.offset, 19) * 4;
|
|
} else {
|
|
address = state->registers[inst->data.SingleTransferData.processOpData.singleDataTransferData.base];
|
|
isLoad = inst->data.SingleTransferData.processOpData.singleDataTransferData.transferType == a64inst_LOAD;
|
|
switch (inst->data.SingleTransferData.processOpData.singleDataTransferData.addressingMode) {
|
|
case a64inst_UNSIGNED_OFFSET:
|
|
address += inst->data.SingleTransferData.processOpData.singleDataTransferData.a64inst_addressingModeData.unsignedOffset * (inst->data.SingleTransferData.regType == a64inst_W ? 4 : 8);
|
|
break;
|
|
case a64inst_REGISTER_OFFSET:
|
|
address += state->registers[inst->data.SingleTransferData.processOpData.singleDataTransferData.a64inst_addressingModeData.offsetReg];
|
|
break;
|
|
case a64inst_PRE_INDEXED:
|
|
address += signExtend(inst->data.SingleTransferData.processOpData.singleDataTransferData.a64inst_addressingModeData.indexedOffset, 9);
|
|
state->registers[inst->data.SingleTransferData.processOpData.singleDataTransferData.base] = address;
|
|
break;
|
|
case a64inst_POST_INDEXED:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (isLoad) {
|
|
state->registers[inst->data.SingleTransferData.target] = readMemory(state->memory, address, inst->data.SingleTransferData.regType);
|
|
|
|
// Update base register if post indexed
|
|
bool isSDT = inst->data.SingleTransferData.SingleTransferOpType == a64inst_SINGLE_TRANSFER_SINGLE_DATA_TRANSFER;
|
|
if (isSDT && inst->data.SingleTransferData.processOpData.singleDataTransferData.addressingMode == a64inst_POST_INDEXED) {
|
|
dword result = address + signExtend(inst->data.SingleTransferData.processOpData.singleDataTransferData.a64inst_addressingModeData.indexedOffset, 9);
|
|
writeRegister(state, inst->data.SingleTransferData.processOpData.singleDataTransferData.base, inst->data.SingleTransferData.regType, result);
|
|
}
|
|
} else {
|
|
storeMemory(state->memory, address, state->registers[inst->data.SingleTransferData.target]);
|
|
|
|
// Update base register if post indexed
|
|
bool isSDT = inst->data.SingleTransferData.SingleTransferOpType == a64inst_SINGLE_TRANSFER_SINGLE_DATA_TRANSFER;
|
|
if (isSDT && inst->data.SingleTransferData.processOpData.singleDataTransferData.addressingMode == a64inst_POST_INDEXED) {
|
|
dword result = address + signExtend(inst->data.SingleTransferData.processOpData.singleDataTransferData.a64inst_addressingModeData.indexedOffset, 9);
|
|
writeRegister(state, inst->data.SingleTransferData.processOpData.singleDataTransferData.base, inst->data.SingleTransferData.regType, result);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
static bool isConditionMet(Machine* state, a64inst_ConditionType cond) {
|
|
switch(cond) {
|
|
case EQ:
|
|
return state->conditionCodes.Zero;
|
|
case NE:
|
|
return !state->conditionCodes.Zero;
|
|
case GE:
|
|
return state->conditionCodes.Negative == state->conditionCodes.Overflow;
|
|
case LT:
|
|
return state->conditionCodes.Negative != state->conditionCodes.Overflow;
|
|
case GT:
|
|
return !state->conditionCodes.Zero && (state->conditionCodes.Negative == state->conditionCodes.Overflow);
|
|
case LE:
|
|
return state->conditionCodes.Zero || (state->conditionCodes.Negative != state->conditionCodes.Overflow);
|
|
case AL:
|
|
return true;
|
|
default:
|
|
fprintf(stderr, "Unknown condition specified!\n");
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
static void executeBranch(Machine *state, a64inst_instruction *inst) {
|
|
switch (inst->data.BranchData.BranchType) {
|
|
case a64inst_UNCONDITIONAL:
|
|
state->pc += signExtend(inst->data.BranchData.processOpData.unconditionalData.unconditionalOffset * 4, 26);
|
|
break;
|
|
|
|
case a64inst_REGISTER:
|
|
state->pc = state->registers[inst->data.BranchData.processOpData.registerData.src];
|
|
break;
|
|
|
|
case a64inst_CONDITIONAL:
|
|
if (isConditionMet(state, inst->data.BranchData.processOpData.conditionalData.cond)) {
|
|
state->pc += signExtend(inst->data.BranchData.processOpData.conditionalData.offset * 4, 19);
|
|
} else {
|
|
state->pc += sizeof(word);
|
|
}
|
|
break;
|
|
}
|
|
}
|