Merge branch 'emulator' into 'master'
Merge emulator into the master branch See merge request lab2324_summer/armv8_43!19
This commit is contained in:
commit
b725033422
@ -10,8 +10,7 @@ CFLAGS ?= -std=c17 -g\
|
||||
all: assemble emulate
|
||||
|
||||
assemble: assemble.o
|
||||
emulate: emulate.o
|
||||
emulate: emulate.o util/fileio.o emulator/execute.o emulator/decode.o emulator/print.o emulator/machine_util.o util/binary_util.o
|
||||
|
||||
clean:
|
||||
$(RM) *.o assemble emulate
|
||||
|
||||
$(RM) *.o assemble emulate emulator/execute.o emulator/decode.o
|
||||
|
||||
36
src/a64instruction/a64instruction.h
Normal file
36
src/a64instruction/a64instruction.h
Normal file
@ -0,0 +1,36 @@
|
||||
#ifndef __A64INSTRUCTION__
|
||||
#define __A64INSTRUCTION__
|
||||
#include "a64instruction_DPImmediate.h"
|
||||
#include "a64instruction_DPRegister.h"
|
||||
#include "a64instruction_Branch.h"
|
||||
#include "a64instruction_SingleTransfer.h"
|
||||
#include "a64instruction_Label.h"
|
||||
#include "a64instruction_Directive.h"
|
||||
|
||||
// Define the types of instructions in subset of the AArch64 Instruction Set implemented.
|
||||
// Each type is defined by the format of the instruction's operand(s).
|
||||
typedef enum {
|
||||
a64inst_DPIMMEDIATE,
|
||||
a64inst_DPREGISTER,
|
||||
a64inst_SINGLETRANSFER,
|
||||
a64inst_LOADLITERAL,
|
||||
a64inst_BRANCH,
|
||||
a64inst_HALT,
|
||||
a64inst_LABEL,
|
||||
a64inst_DIRECTIVE
|
||||
} a64inst_type;
|
||||
|
||||
// Structure the holds the type and operand data of an instruction
|
||||
typedef struct {
|
||||
a64inst_type type;
|
||||
union {
|
||||
a64inst_DPImmediateData DPImmediateData;
|
||||
a64inst_DPRegisterData DPRegisterData;
|
||||
a64inst_BranchData BranchData;
|
||||
a64inst_SingleTransferData SingleTransferData;
|
||||
a64inst_LabelData LabelData;
|
||||
a64inst_DirectiveData DirectiveData;
|
||||
} data;
|
||||
} a64inst_instruction;
|
||||
|
||||
#endif
|
||||
40
src/a64instruction/a64instruction_Branch.h
Normal file
40
src/a64instruction/a64instruction_Branch.h
Normal file
@ -0,0 +1,40 @@
|
||||
#include <stdbool.h>
|
||||
#include "a64instruction_global.h"
|
||||
|
||||
typedef enum {
|
||||
a64inst_UNCONDITIONAL = 0,
|
||||
a64inst_REGISTER = 3,
|
||||
a64inst_CONDITIONAL = 1
|
||||
} a64inst_BranchType;
|
||||
|
||||
typedef struct {
|
||||
word unconditionalOffset;
|
||||
} a64inst_Branch_UnconditionalData;
|
||||
|
||||
typedef struct {
|
||||
a64inst_regSpecifier src;
|
||||
} a64inst_Branch_RegisterData;
|
||||
|
||||
typedef enum {
|
||||
EQ = 0, // Equal
|
||||
NE = 1, // Not Equal
|
||||
GE = 10, // Signed greater or equal
|
||||
LT = 11, // Signed less than
|
||||
GT = 12, // Signed greater than
|
||||
LE = 13, // signed less than or equal
|
||||
AL = 14 // Always
|
||||
} a64inst_ConditionType; //a64inst_Branch_ConditionType?
|
||||
|
||||
typedef struct {
|
||||
a64inst_ConditionType cond;
|
||||
word offset;
|
||||
} a64inst_Branch_ConditionalData;
|
||||
|
||||
typedef struct {
|
||||
a64inst_BranchType BranchType;
|
||||
union {
|
||||
a64inst_Branch_UnconditionalData unconditionalData;
|
||||
a64inst_Branch_RegisterData registerData;
|
||||
a64inst_Branch_ConditionalData conditionalData;
|
||||
} processOpData;
|
||||
} a64inst_BranchData;
|
||||
12
src/a64instruction/a64instruction_DP.h
Normal file
12
src/a64instruction/a64instruction_DP.h
Normal file
@ -0,0 +1,12 @@
|
||||
#ifndef __A64INSTRUCTION_DP__
|
||||
#define __A64INSTRUCTION_DP__
|
||||
|
||||
// Denotes the type of arithmetic operations supported by the architecture
|
||||
typedef enum {
|
||||
a64inst_ADD = 0,
|
||||
a64inst_ADDS = 1,
|
||||
a64inst_SUB = 2,
|
||||
a64inst_SUBS = 3
|
||||
} a64inst_arithmOp;
|
||||
|
||||
#endif
|
||||
42
src/a64instruction/a64instruction_DPImmediate.h
Normal file
42
src/a64instruction/a64instruction_DPImmediate.h
Normal file
@ -0,0 +1,42 @@
|
||||
#include <stdbool.h>
|
||||
#include "a64instruction_global.h"
|
||||
#include "a64instruction_DP.h"
|
||||
|
||||
// Denotes the type of data processing operation
|
||||
typedef enum {
|
||||
a64inst_DPI_ARITHM,
|
||||
a64inst_DPI_WIDEMOV
|
||||
} a64inst_DPIOpType;
|
||||
|
||||
// Denotes the type of wide move operations supported by the architecture
|
||||
typedef enum {
|
||||
a64inst_MOVN = 0,
|
||||
a64inst_UNDEFINED = 1,
|
||||
a64inst_MOVZ = 2,
|
||||
a64inst_MOVK = 3
|
||||
} a64inst_wideMovOp;
|
||||
|
||||
// Holds data specific to arithmetic immediate data processing instructions
|
||||
typedef struct {
|
||||
bool shiftImmediate;
|
||||
uint16_t immediate;
|
||||
a64inst_regSpecifier src;
|
||||
} a64inst_DPImmediate_ArithmData;
|
||||
|
||||
// Holds data specific to wide move immediate data processing instructions
|
||||
typedef struct {
|
||||
uint8_t shiftScalar;
|
||||
uint16_t immediate;
|
||||
} a64inst_DPImmediate_WideMovData;
|
||||
|
||||
// Holds data for immediate data processing instructions
|
||||
typedef struct {
|
||||
a64inst_regType regType;
|
||||
a64inst_DPIOpType DPIOpType;
|
||||
unsigned int processOp;
|
||||
union {
|
||||
a64inst_DPImmediate_ArithmData arithmData;
|
||||
a64inst_DPImmediate_WideMovData wideMovData;
|
||||
} processOpData;
|
||||
a64inst_regSpecifier dest;
|
||||
} a64inst_DPImmediateData;
|
||||
56
src/a64instruction/a64instruction_DPRegister.h
Normal file
56
src/a64instruction/a64instruction_DPRegister.h
Normal file
@ -0,0 +1,56 @@
|
||||
#include <stdbool.h>
|
||||
#include "a64instruction_global.h"
|
||||
#include "a64instruction_DP.h"
|
||||
|
||||
// Denotes the type of data processing operation
|
||||
typedef enum {
|
||||
a64inst_DPR_ARITHMLOGIC = 0,
|
||||
a64inst_DPR_MULTIPLY = 1
|
||||
} a64inst_DPROpType;
|
||||
|
||||
// Denotes the logical operations supported by the architecture
|
||||
typedef enum {
|
||||
a64inst_AND = 0,
|
||||
a64inst_OR = 1,
|
||||
a64inst_XOR = 2,
|
||||
a64inst_AND_FLAGGED = 3
|
||||
} a64inst_logicOp;
|
||||
|
||||
// Denotes the different kinds of shifts supported by the architecture
|
||||
typedef enum {
|
||||
a64inst_LSL = 0,
|
||||
a64inst_LSR = 1,
|
||||
a64inst_ASR = 2,
|
||||
a64inst_ROR = 3
|
||||
} a64inst_ShiftType;
|
||||
|
||||
// Holds data specific to arithmetic/logic register data processing instructions
|
||||
typedef struct {
|
||||
enum {
|
||||
a64inst_DPR_ARITHM = 1,
|
||||
a64inst_DPR_LOGIC = 0
|
||||
} type;
|
||||
a64inst_ShiftType shiftType;
|
||||
uint8_t shiftAmount;
|
||||
bool negShiftedSrc2; // Guaranteed to be 0 for arithmetic instructions
|
||||
} a64inst_DPRegister_ArithmLogicData;
|
||||
|
||||
// Holds data specific to multiply register data processing instructions
|
||||
typedef struct {
|
||||
bool negProd;
|
||||
a64inst_regSpecifier summand;
|
||||
} a64inst_DPRegister_MultiplyData;
|
||||
|
||||
// Holds data for register data processing instructions
|
||||
typedef struct {
|
||||
a64inst_regType regType;
|
||||
a64inst_DPROpType DPROpType;
|
||||
uint8_t processOp;
|
||||
a64inst_regSpecifier src2;
|
||||
union {
|
||||
a64inst_DPRegister_ArithmLogicData arithmLogicData;
|
||||
a64inst_DPRegister_MultiplyData multiplydata;
|
||||
} processOpData;
|
||||
a64inst_regSpecifier src1;
|
||||
a64inst_regSpecifier dest;
|
||||
} a64inst_DPRegisterData;
|
||||
5
src/a64instruction/a64instruction_Directive.h
Normal file
5
src/a64instruction/a64instruction_Directive.h
Normal file
@ -0,0 +1,5 @@
|
||||
#include "./a64instruction_global.h"
|
||||
|
||||
typedef struct {
|
||||
dword value;
|
||||
} a64inst_DirectiveData;
|
||||
3
src/a64instruction/a64instruction_Label.h
Normal file
3
src/a64instruction/a64instruction_Label.h
Normal file
@ -0,0 +1,3 @@
|
||||
typedef struct {
|
||||
char* label;
|
||||
} a64inst_LabelData;
|
||||
46
src/a64instruction/a64instruction_SingleTransfer.h
Normal file
46
src/a64instruction/a64instruction_SingleTransfer.h
Normal file
@ -0,0 +1,46 @@
|
||||
#include <stdbool.h>
|
||||
#include "a64instruction_global.h"
|
||||
|
||||
typedef enum {
|
||||
a64inst_SINGLE_TRANSFER_SINGLE_DATA_TRANSFER = 1,
|
||||
a64inst_SINGLE_TRANSFER_LOAD_LITERAL = 0
|
||||
} a64inst_SingleTransferType;
|
||||
|
||||
typedef enum {
|
||||
a64inst_STORE,
|
||||
a64inst_LOAD
|
||||
} a64inst_TransferType;
|
||||
|
||||
typedef enum {
|
||||
a64inst_REGISTER_OFFSET = 2,
|
||||
a64inst_PRE_INDEXED = 1,
|
||||
a64inst_POST_INDEXED = 0,
|
||||
a64inst_UNSIGNED_OFFSET = 3
|
||||
} a64inst_AddressingMode;
|
||||
|
||||
typedef struct {
|
||||
a64inst_TransferType transferType;
|
||||
a64inst_AddressingMode addressingMode;
|
||||
|
||||
union {
|
||||
a64inst_regSpecifier offsetReg;
|
||||
uint16_t indexedOffset;
|
||||
uint16_t unsignedOffset;
|
||||
} a64inst_addressingModeData;
|
||||
|
||||
a64inst_regSpecifier base;
|
||||
} a64inst_SingleDataTransferData;
|
||||
|
||||
typedef struct {
|
||||
uint32_t offset;
|
||||
} a64inst_LoadLiteralData;
|
||||
|
||||
typedef struct {
|
||||
a64inst_SingleTransferType SingleTransferOpType;
|
||||
a64inst_regType regType;
|
||||
a64inst_regSpecifier target;
|
||||
union {
|
||||
a64inst_SingleDataTransferData singleDataTransferData;
|
||||
a64inst_LoadLiteralData loadLiteralData;
|
||||
} processOpData;
|
||||
} a64inst_SingleTransferData;
|
||||
15
src/a64instruction/a64instruction_global.h
Normal file
15
src/a64instruction/a64instruction_global.h
Normal file
@ -0,0 +1,15 @@
|
||||
#ifndef __A64INSTRUCTION_GLOBAL__
|
||||
#define __A64INSTRUCTION_GLOBAL__
|
||||
#include <stdint.h>
|
||||
#include "../global.h"
|
||||
|
||||
// Specifies the register being referred to
|
||||
typedef uint8_t a64inst_regSpecifier;
|
||||
|
||||
// Denotes the type of register being referred to
|
||||
typedef enum {
|
||||
a64inst_W = 0,
|
||||
a64inst_X = 1
|
||||
} a64inst_regType;
|
||||
|
||||
#endif
|
||||
@ -1,5 +1,72 @@
|
||||
/** @file emulate.c
|
||||
* @brief The main file for the ARMv8 emulator. Reads a binary file and outputs
|
||||
* the state of the machine after executing the instructions in the file.
|
||||
*
|
||||
* @author Saleh Bubshait
|
||||
* @author Themis Demetriades
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include "a64instruction/a64instruction.h"
|
||||
#include "a64instruction/a64instruction_global.h"
|
||||
#include "emulator/emulator.h"
|
||||
#include "util/fileio.h"
|
||||
#include "global.h"
|
||||
#include "emulator/print.h"
|
||||
#include "emulator/decode.h"
|
||||
#include "emulator/execute.h"
|
||||
#include "emulator/machine_util.h"
|
||||
|
||||
extern a64inst_instruction *decode(word w);
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
|
||||
// Check the arguments
|
||||
if (argc == 1) {
|
||||
fprintf(stderr, "Error: An object file is required. Syntax: ./emulate <file_in> [<file_out>]");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// If a second argument is provided, use it as output file instead of stdout.
|
||||
FILE *out = stdout;
|
||||
if (argc > 2) {
|
||||
out = fopen(argv[2], "w");
|
||||
if (out == NULL) {
|
||||
fprintf(stderr, "Error: Could not open file %s\n", argv[2]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
// Initialising the machine state
|
||||
Machine state = {0};
|
||||
state.memory = fileio_loadBin(argv[1], MEMORY_SIZE);
|
||||
state.conditionCodes = (PState){0, 1, 0, 0};
|
||||
state.pc = 0x0;
|
||||
|
||||
|
||||
// Fetch-decode-execute cycle
|
||||
word wrd;
|
||||
a64inst_instruction *inst;
|
||||
do {
|
||||
|
||||
// Step 1: Fetch instruction at PC's address
|
||||
wrd = readMemory(state.memory, state.pc, a64inst_W);
|
||||
|
||||
// Step 2: Decode instruction to internal representation
|
||||
inst = decode(wrd);
|
||||
|
||||
// Step 3: Update processor state to reflect executing the instruction, and increment PC
|
||||
execute(&state, inst);
|
||||
|
||||
if (inst->type != a64inst_BRANCH)
|
||||
state.pc += sizeof(word);
|
||||
} while (inst->type != a64inst_HALT);
|
||||
|
||||
state.pc -= sizeof(word);
|
||||
|
||||
printState(&state, out);
|
||||
free(state.memory);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
294
src/emulator/decode.c
Normal file
294
src/emulator/decode.c
Normal file
@ -0,0 +1,294 @@
|
||||
/** @file decode.c
|
||||
* @brief Decode Function from binary words to internal representation structs.
|
||||
*
|
||||
* This defines the decode function which takes a binary word (uint32_t) and
|
||||
* decodes it into an a64inst_instruction conveying the same information.
|
||||
*
|
||||
* @author Saleh Bubshait
|
||||
* @author Themis Demetriades
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "decode.h"
|
||||
#include "../util/binary_util.h"
|
||||
|
||||
// Macro that calls getBit() for a bitfield whose constants follow the format
|
||||
// FIELDNAME_LSB and FIELDNAME_MSB, storing the result in the variable wrd
|
||||
#define getField(fieldname) getBits(wrd, fieldname##_LSB, fieldname##_MSB)
|
||||
|
||||
/************************************
|
||||
* CONSTANTS
|
||||
************************************/
|
||||
|
||||
#define HALT_WORD 0x8a000000
|
||||
|
||||
#define TYPE_ID_LSB 26
|
||||
#define TYPE_ID_MSB 29
|
||||
#define DP_IMM_ID 4
|
||||
#define BRANCH_ID 5
|
||||
|
||||
#define DP_REG_FLAG_LSB 25
|
||||
#define DP_REG_FLAG_MSB 26
|
||||
|
||||
#define DP_WIDTH_LSB 31
|
||||
#define DP_WIDTH_MSB 32
|
||||
#define DP_OP_LSB 29
|
||||
#define DP_OP_MSB 31
|
||||
#define DP_DEST_LSB 0
|
||||
#define DP_DEST_MSB 5
|
||||
|
||||
#define DP_IMM_OPTYPE_LSB 23
|
||||
#define DP_IMM_OPTYPE_MSB 26
|
||||
#define DP_IMM_OPTYPE_ARITHM 2
|
||||
#define DP_IMM_OPTYPE_WIDEMOV 5
|
||||
#define DP_IMM_ARITHM_SHIFTFLAG_LSB 22
|
||||
#define DP_IMM_ARITHM_SHIFTFLAG_MSB 23
|
||||
#define DP_IMM_ARITHM_IMMVAL_LSB 10
|
||||
#define DP_IMM_ARITHM_IMMVAL_MSB 22
|
||||
#define DP_IMM_ARITHM_DEST_LSB 5
|
||||
#define DP_IMM_ARITHM_DEST_MSB 10
|
||||
#define DP_IMM_WIDEMOV_SHIFTSCALAR_LSB 21
|
||||
#define DP_IMM_WIDEMOV_SHIFTSCALAR_MSB 23
|
||||
#define DP_IMM_WIDEMOV_IMMVAL_LSB 5
|
||||
#define DP_IMM_WIDEMOV_IMMVAL_MSB 21
|
||||
|
||||
#define DP_REG_SRC1_LSB 5
|
||||
#define DP_REG_SRC1_MSB 10
|
||||
#define DP_REG_SRC2_LSB 16
|
||||
#define DP_REG_SRC2_MSB 21
|
||||
#define DP_REG_OPTYPE_LSB 28
|
||||
#define DP_REG_OPTYPE_MSB 29
|
||||
#define DP_REG_ARITHMLOGIC_ARITHMFLAG_LSB 24
|
||||
#define DP_REG_ARITHMLOGIC_ARITHMFLAG_MSB 25
|
||||
#define DP_REG_ARITHMLOGIC_SHIFTTYPE_LSB 22
|
||||
#define DP_REG_ARITHMLOGIC_SHIFTTYPE_MSB 24
|
||||
#define DP_REG_ARITHMLOGIC_NEGSRC2FLAG_LSB 21
|
||||
#define DP_REG_ARITHMLOGIC_NEGSRC2FLAG_MSB 22
|
||||
#define DP_REG_ARITHMLOGIC_SHIFTAMOUNT_LSB 10
|
||||
#define DP_REG_ARITHMLOGIC_SHIFTAMOUNT_MSB 16
|
||||
#define DP_REG_MULTIPLY_SUMMAND_LSB 10
|
||||
#define DP_REG_MULTIPLY_SUMMAND_MSB 15
|
||||
#define DP_REG_MULTIPLY_NEGPROD_LSB 15
|
||||
#define DP_REG_MULTIPLY_NEGPROD_MSB 16
|
||||
#define DP_REG_MULTIPLY_PROCESSOP 0
|
||||
#define DP_REG_MULTIPLY_ARITHMFLAG 1
|
||||
#define DP_REG_MULTIPLY_SHIFTTYPE 0
|
||||
#define DP_REG_MULTIPLY_NEGSRC2FLAG 0
|
||||
|
||||
#define SDT_OPTYPE_FLAG_LSB 31
|
||||
#define SDT_OPTYPE_FLAG_MSB 32
|
||||
#define SDT_REGTYPE_FLAG_LSB 30
|
||||
#define SDT_REGTYPE_FLAG_MSB 31
|
||||
#define SDT_TARGET_REG_LSB 0
|
||||
#define SDT_TARGET_REG_MSB 5
|
||||
|
||||
#define SDT_BASE_REG_LSB 5
|
||||
#define SDT_BASE_REG_MSB 10
|
||||
#define SDT_OFFSET_LSB 10
|
||||
#define SDT_OFFSET_MSB 22
|
||||
#define SDT_TRANSFER_TYPE_LSB 22
|
||||
#define SDT_TRANSFER_TYPE_MSB 23
|
||||
#define SDT_UNSIGNED_FLAG_LSB 24
|
||||
#define SDT_UNSIGNED_FLAG_MSB 25
|
||||
#define SDT_REGISTER_FLAG_LSB 21
|
||||
#define SDT_REGISTER_FLAG_MSB 22
|
||||
#define SDT_REGISTER_REG_LSB 16
|
||||
#define SDT_REGISTER_REG_MSB 21
|
||||
#define SDT_INDEXED_ADDRMODE_LSB 11
|
||||
#define SDT_INDEXED_ADDRMODE_MSB 12
|
||||
#define SDT_INDEXED_OFFSET_LSB 12
|
||||
#define SDT_INDEXED_OFFSET_MSB 21
|
||||
#define SDT_LOAD_LITERAL_OFFSET_LSB 5
|
||||
#define SDT_LOAD_LITERAL_OFFSET_MSB 24
|
||||
|
||||
#define BRANCH_TYPE_LSB 30
|
||||
#define BRANCH_TYPE_MSB 32
|
||||
#define BRANCH_UNCONDITIONAL_OFFSET_LSB 0
|
||||
#define BRANCH_UNCONDITIONAL_OFFSET_MSB 26
|
||||
#define BRANCH_REGISTER_SRC_LSB 5
|
||||
#define BRANCH_REGISTER_SRC_MSB 10
|
||||
#define BRANCH_CONDITIONAL_COND_LSB 0
|
||||
#define BRANCH_CONDITIONAL_COND_MSB 4
|
||||
#define BRANCH_CONDITIONAL_OFFSET_LSB 5
|
||||
#define BRANCH_CONDITIONAL_OFFSET_MSB 24
|
||||
|
||||
/************************************
|
||||
* PROTOTYPES
|
||||
************************************/
|
||||
|
||||
static void decodeDPI(word wrd, a64inst_instruction *inst);
|
||||
static void decodeBranch(word wrd, a64inst_instruction *inst);
|
||||
static void decodeDPRegister(word wrd, a64inst_instruction *inst);
|
||||
static void decodeSingleTransfer(word wrd, a64inst_instruction *inst);
|
||||
|
||||
/************************************
|
||||
* FUNCTIONS
|
||||
************************************/
|
||||
|
||||
a64inst_instruction *decode(word wrd) {
|
||||
|
||||
a64inst_instruction *inst = malloc(sizeof(a64inst_instruction));
|
||||
if (inst == NULL) {
|
||||
fprintf(stderr, "Error: Could not allocate memory for an instruction\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
word typeId = getField(TYPE_ID);
|
||||
|
||||
if (wrd == HALT_WORD) {
|
||||
inst->type = a64inst_HALT;
|
||||
|
||||
} else if (typeId == DP_IMM_ID) {
|
||||
inst->type = a64inst_DPIMMEDIATE;
|
||||
decodeDPI(wrd, inst);
|
||||
|
||||
} else if (typeId == BRANCH_ID) {
|
||||
inst->type = a64inst_BRANCH;
|
||||
decodeBranch(wrd, inst);
|
||||
|
||||
} else if (getField(DP_REG_FLAG) == 1) {
|
||||
inst->type = a64inst_DPREGISTER;
|
||||
decodeDPRegister(wrd, inst);
|
||||
|
||||
} else {
|
||||
inst->type = a64inst_SINGLETRANSFER;
|
||||
decodeSingleTransfer(wrd, inst);
|
||||
}
|
||||
|
||||
return inst;
|
||||
}
|
||||
|
||||
/************************************
|
||||
* HELPER FUNCTIONS
|
||||
************************************/
|
||||
|
||||
static void decodeDPI(word wrd, a64inst_instruction *inst) {
|
||||
inst->data.DPImmediateData.regType = getField(DP_WIDTH);
|
||||
inst->data.DPImmediateData.processOp = getField(DP_OP);
|
||||
inst->data.DPImmediateData.dest = getField(DP_DEST);
|
||||
|
||||
switch(getField(DP_IMM_OPTYPE)) {
|
||||
|
||||
case DP_IMM_OPTYPE_ARITHM:
|
||||
inst->data.DPImmediateData.DPIOpType = a64inst_DPI_ARITHM;
|
||||
inst->data.DPImmediateData.processOpData.arithmData.shiftImmediate = getField(DP_IMM_ARITHM_SHIFTFLAG);
|
||||
inst->data.DPImmediateData.processOpData.arithmData.immediate = getField(DP_IMM_ARITHM_IMMVAL);
|
||||
inst->data.DPImmediateData.processOpData.arithmData.src = getField(DP_IMM_ARITHM_DEST);
|
||||
break;
|
||||
|
||||
case DP_IMM_OPTYPE_WIDEMOV:
|
||||
inst->data.DPImmediateData.DPIOpType = a64inst_DPI_WIDEMOV;
|
||||
inst->data.DPImmediateData.processOpData.wideMovData.shiftScalar = getField(DP_IMM_WIDEMOV_SHIFTSCALAR);
|
||||
inst->data.DPImmediateData.processOpData.wideMovData.immediate = getField(DP_IMM_WIDEMOV_IMMVAL);
|
||||
break;
|
||||
|
||||
default:
|
||||
fprintf(stderr, "Unknown immediate data processing operation type found!\n");
|
||||
exit(1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void decodeBranch(word wrd, a64inst_instruction *inst) {
|
||||
word branchTypeFlag = getField(BRANCH_TYPE);
|
||||
inst->data.BranchData.BranchType = branchTypeFlag;
|
||||
|
||||
switch (branchTypeFlag) {
|
||||
case a64inst_UNCONDITIONAL:
|
||||
inst->data.BranchData.processOpData.unconditionalData.unconditionalOffset = getField(BRANCH_UNCONDITIONAL_OFFSET);
|
||||
break;
|
||||
|
||||
case a64inst_CONDITIONAL:
|
||||
inst->data.BranchData.processOpData.conditionalData.offset = getField(BRANCH_CONDITIONAL_OFFSET);
|
||||
|
||||
word conditionFlag = getField(BRANCH_CONDITIONAL_COND);
|
||||
|
||||
if(conditionFlag <= 1 || (conditionFlag >= 10 && conditionFlag <= 14)) {
|
||||
inst->data.BranchData.processOpData.conditionalData.cond = conditionFlag;
|
||||
|
||||
} else {
|
||||
fprintf(stderr, "Unknown condition detected!\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case a64inst_REGISTER:
|
||||
inst->data.BranchData.processOpData.registerData.src = getField(BRANCH_REGISTER_SRC);
|
||||
break;
|
||||
|
||||
default:
|
||||
fprintf(stderr, "Undefined branch type detected!\n");
|
||||
exit(1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void decodeDPRegister(word wrd, a64inst_instruction *inst) {
|
||||
inst->data.DPRegisterData.regType = getField(DP_WIDTH);
|
||||
inst->data.DPRegisterData.processOp = getField(DP_OP);
|
||||
inst->data.DPRegisterData.dest = getField(DP_DEST);
|
||||
inst->data.DPRegisterData.src1 = getField(DP_REG_SRC1);
|
||||
inst->data.DPRegisterData.src2 = getField(DP_REG_SRC2);
|
||||
inst->data.DPRegisterData.DPROpType = getField(DP_REG_OPTYPE);
|
||||
|
||||
a64inst_DPRegister_ArithmLogicData *arithmLogicData = &inst->data.DPRegisterData.processOpData.arithmLogicData;
|
||||
|
||||
arithmLogicData->type = getField(DP_REG_ARITHMLOGIC_ARITHMFLAG);
|
||||
arithmLogicData->shiftType = getField(DP_REG_ARITHMLOGIC_SHIFTTYPE);
|
||||
arithmLogicData->negShiftedSrc2 = getField(DP_REG_ARITHMLOGIC_NEGSRC2FLAG);
|
||||
|
||||
switch(inst->data.DPRegisterData.DPROpType) {
|
||||
|
||||
case a64inst_DPR_ARITHMLOGIC:
|
||||
if (arithmLogicData->type == a64inst_DPR_ARITHM && (arithmLogicData->negShiftedSrc2 || arithmLogicData->shiftType == a64inst_ROR)) {
|
||||
fprintf(stderr, "Attempting to decode arithmetic DPR instruction with invalid format!\n");
|
||||
}
|
||||
arithmLogicData->shiftAmount = getField(DP_REG_ARITHMLOGIC_SHIFTAMOUNT);
|
||||
break;
|
||||
|
||||
case a64inst_DPR_MULTIPLY:;
|
||||
if (!(inst->data.DPRegisterData.processOp == DP_REG_MULTIPLY_PROCESSOP &&
|
||||
arithmLogicData->type == DP_REG_MULTIPLY_ARITHMFLAG &&
|
||||
arithmLogicData->shiftType == DP_REG_MULTIPLY_SHIFTTYPE &&
|
||||
arithmLogicData->negShiftedSrc2 == DP_REG_MULTIPLY_NEGSRC2FLAG)) {
|
||||
fprintf(stderr, "Attempting to decode multiply DPR instruction with invalid format!\n");
|
||||
}
|
||||
inst->data.DPRegisterData.processOpData.multiplydata.summand = getField(DP_REG_MULTIPLY_SUMMAND);
|
||||
inst->data.DPRegisterData.processOpData.multiplydata.negProd = getField(DP_REG_MULTIPLY_NEGPROD);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void decodeSingleTransfer(word wrd, a64inst_instruction *inst) {
|
||||
inst->data.SingleTransferData.regType = getField(SDT_REGTYPE_FLAG);
|
||||
inst->data.SingleTransferData.target = getField(SDT_TARGET_REG);
|
||||
|
||||
bool singleTransferOptype = getField(SDT_OPTYPE_FLAG);
|
||||
if(singleTransferOptype == a64inst_SINGLE_TRANSFER_SINGLE_DATA_TRANSFER) {
|
||||
// Single Data Transfer
|
||||
inst->data.SingleTransferData.SingleTransferOpType = a64inst_SINGLE_TRANSFER_SINGLE_DATA_TRANSFER;
|
||||
inst->data.SingleTransferData.processOpData.singleDataTransferData.transferType = getField(SDT_TRANSFER_TYPE);
|
||||
inst->data.SingleTransferData.processOpData.singleDataTransferData.base = getField(SDT_BASE_REG);
|
||||
|
||||
if (getField(SDT_UNSIGNED_FLAG) == 1) {
|
||||
// Unsigned offset
|
||||
inst->data.SingleTransferData.processOpData.singleDataTransferData.addressingMode = a64inst_UNSIGNED_OFFSET;
|
||||
inst->data.SingleTransferData.processOpData.singleDataTransferData.a64inst_addressingModeData.unsignedOffset = getField(SDT_OFFSET);
|
||||
} else if (getField(SDT_REGISTER_FLAG) == 1) {
|
||||
// Register Offset
|
||||
inst->data.SingleTransferData.processOpData.singleDataTransferData.addressingMode = a64inst_REGISTER_OFFSET;
|
||||
inst->data.SingleTransferData.processOpData.singleDataTransferData.a64inst_addressingModeData.offsetReg = getField(SDT_REGISTER_REG);
|
||||
} else {
|
||||
// Pre-Indexed or Post-Indexed
|
||||
inst->data.SingleTransferData.processOpData.singleDataTransferData.addressingMode = getField(SDT_INDEXED_ADDRMODE);
|
||||
inst->data.SingleTransferData.processOpData.singleDataTransferData.a64inst_addressingModeData.indexedOffset = getField(SDT_INDEXED_OFFSET);
|
||||
}
|
||||
|
||||
} else {
|
||||
// Load Literal
|
||||
inst->data.SingleTransferData.SingleTransferOpType = a64inst_SINGLE_TRANSFER_LOAD_LITERAL;
|
||||
inst->data.SingleTransferData.processOpData.loadLiteralData.offset = getField(SDT_LOAD_LITERAL_OFFSET);
|
||||
}
|
||||
}
|
||||
21
src/emulator/decode.h
Normal file
21
src/emulator/decode.h
Normal file
@ -0,0 +1,21 @@
|
||||
/** @file decode.h
|
||||
* @brief Decode Function from binary words to a special internal
|
||||
* representation of instructions, a64inst_instruction.
|
||||
*
|
||||
* This defines the decode function which takes a binary word (uint32_t) and
|
||||
* decodes it into an a64inst_instruction conveying the same information.
|
||||
*
|
||||
* @author Saleh Bubshait
|
||||
* @author Themis Demetriades
|
||||
*/
|
||||
|
||||
#include "../global.h"
|
||||
#include "../a64instruction/a64instruction.h"
|
||||
|
||||
/** @brief The main decode function. Takes a word (uint32_t) representing the
|
||||
* instruction and decodes it into an a64inst_instruction struct.
|
||||
* @param wrd The word (uint32_t) to be decoded in binary.
|
||||
* @return a pointer to the heap-allocated a64inst_instruction struct
|
||||
* representing the decoded instruction if successful.
|
||||
*/
|
||||
a64inst_instruction *decode(word wrd);
|
||||
32
src/emulator/emulator.h
Normal file
32
src/emulator/emulator.h
Normal file
@ -0,0 +1,32 @@
|
||||
/** @file emulator.h
|
||||
* @brief Structures to represent the state of the ARMv8 machine to be used
|
||||
* in the emulator.
|
||||
*
|
||||
* @author Saleh Bubshait
|
||||
* @author Themis Demetriades
|
||||
*/
|
||||
|
||||
#ifndef __EMULATOR__
|
||||
#define __EMULATOR__
|
||||
#include "../global.h"
|
||||
#include <stdbool.h>
|
||||
|
||||
/************************************
|
||||
* STRUCTS
|
||||
************************************/
|
||||
|
||||
typedef struct {
|
||||
bool Negative;
|
||||
bool Zero;
|
||||
bool Carry;
|
||||
bool Overflow;
|
||||
} PState;
|
||||
|
||||
typedef struct {
|
||||
dword registers[REGISTER_COUNT];
|
||||
dword pc;
|
||||
byte *memory;
|
||||
PState conditionCodes;
|
||||
} Machine;
|
||||
|
||||
#endif
|
||||
380
src/emulator/execute.c
Normal file
380
src/emulator/execute.c
Normal file
@ -0,0 +1,380 @@
|
||||
/** @file execute.c
|
||||
* @brief Implementation of the execute function for the ARMv8 emulator.
|
||||
*
|
||||
* This defines the execute function which takes a pointer to the machine state
|
||||
* and a pointer to an a64inst_instruction struct and executes the instruction
|
||||
* updating the machine state accordingly.
|
||||
*
|
||||
* @author Saleh Bubshait
|
||||
* @author Themis Demetriades
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include "execute.h"
|
||||
#include "print.h"
|
||||
#include "../util/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};
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
13
src/emulator/execute.h
Normal file
13
src/emulator/execute.h
Normal file
@ -0,0 +1,13 @@
|
||||
#ifndef __EXECUTE__
|
||||
#define __EXECUTE__
|
||||
#include "../a64instruction/a64instruction.h"
|
||||
#include "emulator.h"
|
||||
|
||||
/** @brief Executes the given instruction on the given machine state.
|
||||
* Updates the machine state to reflect the execution of the instruction.
|
||||
*
|
||||
* @param state The machine state to execute the instruction on.
|
||||
* @param inst The instruction to execute.
|
||||
*/
|
||||
void execute(Machine *state, a64inst_instruction *inst);
|
||||
#endif
|
||||
52
src/emulator/machine_util.c
Normal file
52
src/emulator/machine_util.c
Normal file
@ -0,0 +1,52 @@
|
||||
/** @file machine_util.c
|
||||
* @brief Implementation of utility functions for manipulating the machine state.
|
||||
*
|
||||
* This defines utility functions for reading and writing memory and registers,
|
||||
* which are used by the emulator to execute instructions.
|
||||
*
|
||||
* @author Saleh Bubshait
|
||||
* @author Themis Demetriades
|
||||
*/
|
||||
|
||||
#include "assert.h"
|
||||
#include "machine_util.h"
|
||||
#include "../a64instruction/a64instruction_global.h"
|
||||
#include "../global.h"
|
||||
#include "emulator.h"
|
||||
#include "../util/binary_util.h"
|
||||
|
||||
dword readMemory(byte *memory, uint32_t address, a64inst_regType regType) {
|
||||
dword result = 0;
|
||||
int bytesPerWord = (regType == a64inst_W ? WORD_BITS : DWORD_BITS) / BYTE_BITS - 1;
|
||||
for (int i = 0; i <= bytesPerWord; i++) {
|
||||
if (regType == a64inst_W) {
|
||||
result |= (word) memory[address + i] << (BYTE_BITS * i);
|
||||
} else {
|
||||
result |= (dword) memory[address + i] << (BYTE_BITS * i);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void storeMemory(byte *memory, uint32_t address, dword data) {
|
||||
int bytesPerDword = DWORD_BITS / BYTE_BITS - 1;
|
||||
for (int i = 0; i <= bytesPerDword; i++) {
|
||||
memory[address + i] = (byte)((data >> (BYTE_BITS * i)) & 0xFF);
|
||||
}
|
||||
}
|
||||
|
||||
dword readRegister(Machine *state, a64inst_regSpecifier reg, a64inst_regType regType) {
|
||||
assert(reg <= REGISTER_COUNT);
|
||||
if (reg == ZERO_REGISTER) {
|
||||
return 0;
|
||||
} else {
|
||||
return truncateValue(state->registers[reg], regType);
|
||||
}
|
||||
}
|
||||
|
||||
void writeRegister(Machine *state, a64inst_regSpecifier reg, a64inst_regType regType, dword value) {
|
||||
assert(reg <= REGISTER_COUNT);
|
||||
if (reg != ZERO_REGISTER) {
|
||||
state->registers[reg] = truncateValue(value, regType);
|
||||
}
|
||||
}
|
||||
55
src/emulator/machine_util.h
Normal file
55
src/emulator/machine_util.h
Normal file
@ -0,0 +1,55 @@
|
||||
/** @file machine_util.h
|
||||
* @brief Utility functions for manipulating the machine state.
|
||||
*
|
||||
* This defines utility functions for reading and writing memory and registers,
|
||||
* which are used by the emulator to execute instructions.
|
||||
*
|
||||
* @author Saleh Bubshait
|
||||
* @author Themis Demetriades
|
||||
*/
|
||||
|
||||
#ifndef __MACHINE_UTIL__
|
||||
#define __MACHINE_UTIL__
|
||||
#include "../global.h"
|
||||
#include "../a64instruction/a64instruction_global.h"
|
||||
#include "emulator.h"
|
||||
|
||||
/** @brief Reads a (double) word from memory starting at the given address.
|
||||
* If regType is a64inst_W, the value is truncated to 32 bits.
|
||||
*
|
||||
* @param memory The starting address byte-addressable memory block.
|
||||
* @param address The address to read from.
|
||||
* @param regType The register type to truncate the value to if necessary.
|
||||
* @return The (double) word read from memory.
|
||||
*/
|
||||
dword readMemory(byte *memory, uint32_t address, a64inst_regType regType);
|
||||
|
||||
/** @brief Stores a double word into memory starting at the given address.
|
||||
*
|
||||
* @param memory The starting address byte-addressable memory block.
|
||||
* @param address The address to store the data at.
|
||||
* @param data The double word to store.
|
||||
*/
|
||||
void storeMemory(byte *memory, uint32_t address, dword data);
|
||||
|
||||
/** @brief Reads a register from the machine state.
|
||||
* If regType is a64inst_W, the value is truncated to 32 bits.
|
||||
*
|
||||
* @param state The machine state to read the register from.
|
||||
* @param reg The register specifier to read.
|
||||
* @param regType The register type to truncate the value to if necessary.
|
||||
* @return The value stored in the register, truncated if necessary.
|
||||
*/
|
||||
dword readRegister(Machine *state, a64inst_regSpecifier reg, a64inst_regType regType);
|
||||
|
||||
/** @brief Writes a value to a register in the machine state.
|
||||
* If regType is a64inst_W, the value is truncated to 32 bits.
|
||||
*
|
||||
* @param state The machine state to write the register to.
|
||||
* @param reg The register specifier to write to.
|
||||
* @param regType The register type to truncate the value to if necessary.
|
||||
* @param value The value to write to the register.
|
||||
*/
|
||||
void writeRegister(Machine *state, a64inst_regSpecifier reg, a64inst_regType regType, dword value);
|
||||
|
||||
#endif
|
||||
49
src/emulator/print.c
Normal file
49
src/emulator/print.c
Normal file
@ -0,0 +1,49 @@
|
||||
/** @file print.c
|
||||
* @brief Implementation of functions to print the machine state.
|
||||
*
|
||||
* The function prints the current machine state, including the registers,
|
||||
* memory and condition codes. This is used to print the final state machine
|
||||
* in the emulator after all instructions have been executed.
|
||||
*
|
||||
* @author Saleh Bubshait
|
||||
* @author Themis Demetriades
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <inttypes.h>
|
||||
#include "print.h"
|
||||
#include "../a64instruction/a64instruction_global.h"
|
||||
#include "emulator.h"
|
||||
#include "machine_util.h"
|
||||
|
||||
#define UNSET_CONDITION_CODE_CHAR '-'
|
||||
|
||||
void printState(Machine *state, FILE *stream) {
|
||||
printRegisters(state, stream);
|
||||
printMemory(state, stream);
|
||||
}
|
||||
|
||||
void printRegisters(Machine *state, FILE *stream) {
|
||||
fprintf(stream, "Registers:\n");
|
||||
for (int i = 0; i < REGISTER_COUNT; i++) {
|
||||
fprintf(stream, "X%02d\t= %016" PRIx64 "\n", i, state->registers[i]);
|
||||
}
|
||||
fprintf(stream, "PC\t= %016" PRIx64 "\n", state->pc);
|
||||
fprintf(stream, "PSTATE\t: %c%c%c%c", state->conditionCodes.Negative ? 'N' : UNSET_CONDITION_CODE_CHAR,
|
||||
state->conditionCodes.Zero ? 'Z' : UNSET_CONDITION_CODE_CHAR,
|
||||
state->conditionCodes.Carry ? 'C' : UNSET_CONDITION_CODE_CHAR,
|
||||
state->conditionCodes.Overflow ? 'V' : UNSET_CONDITION_CODE_CHAR);
|
||||
}
|
||||
|
||||
void printMemory(Machine *state, FILE *stream) {
|
||||
fprintf(stream, "\nNon-zero memory:\n");
|
||||
|
||||
// print memory 4 byte aligned
|
||||
for (int addr = 0; addr < MEMORY_SIZE; addr+= 4) {
|
||||
word data = readMemory(state->memory, addr, a64inst_W);
|
||||
if (data != 0) {
|
||||
fprintf(stream, "0x%08x: %08x\n", addr, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
48
src/emulator/print.h
Normal file
48
src/emulator/print.h
Normal file
@ -0,0 +1,48 @@
|
||||
/** @file print.h
|
||||
* @brief Functions to print the machine state, inc registers and memory.
|
||||
*
|
||||
* The function prints the current machine state, including the registers,
|
||||
* memory and condition codes. This is used to print the final state machine
|
||||
* in the emulator after all instructions have been executed.
|
||||
*
|
||||
* @author Saleh Bubshait
|
||||
* @author Themis Demetriades
|
||||
*/
|
||||
|
||||
#ifndef __PRINT__
|
||||
#define __PRINT__
|
||||
#include <stdio.h>
|
||||
#include "emulator.h"
|
||||
|
||||
/** @brief Prints the current machine state into the provided stream.
|
||||
*
|
||||
* @param state The machine state to print.
|
||||
* @param stream The stream to print the state to.
|
||||
*
|
||||
* @see printRegisters
|
||||
* @see printMemory
|
||||
*/
|
||||
void printState(Machine *state, FILE *stream);
|
||||
|
||||
/** @brief Prints the current machine registers into the provided stream.
|
||||
* The registers are printed in the format "Xn = value" where n is the register
|
||||
* number and value is the value stored in the register, in hexadecimal. The
|
||||
* program counter (PC) and condition codes are also printed. SP is ignored.
|
||||
*
|
||||
* @param state The machine state to print the registers of.
|
||||
* @param stream The stream to print the registers to.
|
||||
*/
|
||||
void printRegisters(Machine *state, FILE *stream);
|
||||
|
||||
/** @brief Prints all non-zero memory locations into the provided stream.
|
||||
* The memory is printed in the format "0xaddress: data" where address is the
|
||||
* memory address and data is the data stored at that address, in hexadecimal.
|
||||
* Memory locations are printed 4 bytes at a time.
|
||||
* Note. Only non-zero memory locations are printed!
|
||||
*
|
||||
* @param state The machine state to print the memory of.
|
||||
* @param stream The stream to print the memory to.
|
||||
*/
|
||||
void printMemory(Machine *state, FILE *stream);
|
||||
|
||||
#endif
|
||||
13
src/global.h
13
src/global.h
@ -9,8 +9,15 @@
|
||||
#define __GLOBAL__
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
/************************************
|
||||
* DEFINITIONS
|
||||
************************************/
|
||||
|
||||
// Number of General Purpose Registers.
|
||||
#define REGISTER_COUNT 31
|
||||
// Register identifier interpreted as the 'zero register'
|
||||
#define ZERO_REGISTER 31
|
||||
// Size of the memory in bytes.
|
||||
#define MEMORY_SIZE 2097152
|
||||
// Length of the memory address in bits.
|
||||
@ -25,4 +32,8 @@ typedef uint32_t word;
|
||||
// Double word is a 64-bit unsigned integer.
|
||||
typedef uint64_t dword;
|
||||
|
||||
#endif
|
||||
#define BYTE_BITS 8
|
||||
#define WORD_BITS (BYTE_BITS * sizeof(word))
|
||||
#define DWORD_BITS (BYTE_BITS * sizeof(dword))
|
||||
|
||||
#endif
|
||||
|
||||
59
src/util/binary_util.c
Normal file
59
src/util/binary_util.c
Normal file
@ -0,0 +1,59 @@
|
||||
/** @file binary_util.c
|
||||
* @brief Implementation of utility functions for binary manipulation.
|
||||
*
|
||||
* @author Saleh Bubshait
|
||||
* @author Themis Demetriades
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include "binary_util.h"
|
||||
|
||||
word getBits(word wrd, uint8_t lsb, uint8_t msb) {
|
||||
|
||||
// Ensure LSB and MSB are within range of word size, and in the correct order
|
||||
assert(lsb < msb && msb <= WORD_BITS);
|
||||
|
||||
wrd &= ((dword) 1 << msb) - 1;
|
||||
return wrd >> lsb;
|
||||
}
|
||||
|
||||
dword max(dword a, dword b) {
|
||||
return a > b ? a : b;
|
||||
}
|
||||
|
||||
dword truncateValue(dword value, a64inst_regType regType) {
|
||||
if (regType == a64inst_X) {
|
||||
return value;
|
||||
} else {
|
||||
return (word)value;
|
||||
}
|
||||
}
|
||||
|
||||
int64_t signExtend(dword value, unsigned int n) {
|
||||
if (n == 0 || n >= 64) {
|
||||
// If n_bits is 0 or greater than or equal to 64, return the value as is
|
||||
return (int64_t)value;
|
||||
}
|
||||
|
||||
uint64_t sign_bit_mask = (uint64_t)1 << (n - 1);
|
||||
|
||||
// Mask to isolate the n-bit value
|
||||
uint64_t n_bit_mask = (sign_bit_mask << 1) - 1;
|
||||
|
||||
// Check if the sign bit is set
|
||||
if (value & sign_bit_mask) {
|
||||
// Sign bit is set, extend the sign
|
||||
return (int64_t)(value | ~n_bit_mask);
|
||||
} else {
|
||||
// Sign bit is not set, return the value as is
|
||||
return (int64_t)(value & n_bit_mask);
|
||||
}
|
||||
}
|
||||
|
||||
dword getMSBPos(a64inst_regType regType) {
|
||||
return (regType ? DWORD_BITS : WORD_BITS) - 1;
|
||||
}
|
||||
|
||||
uint8_t getMSB(dword value, a64inst_regType regType) {
|
||||
return (value >> getMSBPos(regType)) & 1u;
|
||||
}
|
||||
62
src/util/binary_util.h
Normal file
62
src/util/binary_util.h
Normal file
@ -0,0 +1,62 @@
|
||||
/** @file binary_util.h
|
||||
* @brief Utility functions for binary manipulation.
|
||||
*
|
||||
* @author Saleh Bubshait
|
||||
* @author Themis Demetriades
|
||||
*/
|
||||
#ifndef __BINARY_UTIL__
|
||||
#define __BINARY_UTIL__
|
||||
|
||||
#include "../global.h"
|
||||
#include "../a64instruction/a64instruction_global.h"
|
||||
|
||||
/** @brief Extracts the bits between the given indices from a word as an
|
||||
* unsigned integer, uint32_t (word).
|
||||
*
|
||||
* @param wrd The input word (uint32_t) to extract bits from.
|
||||
* @param lsb The index of the least significant bit to extract.
|
||||
* @param msb The index of the most significant bit to extract.
|
||||
* @return The extracted bits as a word.
|
||||
*/
|
||||
word getBits(word wrd, uint8_t lsb, uint8_t msb);
|
||||
|
||||
/** @brief Returns the maximum of two given two double words (uint64_t).
|
||||
*
|
||||
* @param a The first double word.
|
||||
* @param b The second double word.
|
||||
* @return The maximum of the two given double words.
|
||||
*/
|
||||
dword max(dword a, dword b);
|
||||
|
||||
/** @brief Truncates a given value to the size of the given register type.
|
||||
*
|
||||
* @param value The value to truncate.
|
||||
* @param regType The register type to truncate the value to.
|
||||
* @return The truncated value.
|
||||
*/
|
||||
dword truncateValue(dword value, a64inst_regType regType);
|
||||
|
||||
/** @brief Sign extend a n-bit given value to a signed integer.
|
||||
*
|
||||
* @param value The value to sign extend.
|
||||
* @param n The number of bits of the signed value. E.g., 16 for simm16.
|
||||
*/
|
||||
int64_t signExtend(dword value, unsigned int n);
|
||||
|
||||
/** @brief Returns the position of the most significant bit of the given register type.
|
||||
*
|
||||
* @param regType The register type to get the most significant bit position of.
|
||||
* @return The position of the most significant bit. This is either 31 or 63.
|
||||
*/
|
||||
dword getMSBPos(a64inst_regType regType);
|
||||
|
||||
/** @brief Returns the most significant bit of the given value assuming it's of
|
||||
* the size stored in the given register type.
|
||||
*
|
||||
* @param value The value to get the most significant bit of.
|
||||
* @param regType The register type to get the most significant bit of.
|
||||
* @return The most significant bit of the given value.
|
||||
*/
|
||||
uint8_t getMSB(dword value, a64inst_regType regType);
|
||||
|
||||
#endif
|
||||
51
src/util/fileio.c
Normal file
51
src/util/fileio.c
Normal file
@ -0,0 +1,51 @@
|
||||
/** @file fileio.c
|
||||
* @brief Implementation of file input/output functions.
|
||||
*
|
||||
* @author Saleh Bubshait
|
||||
* @author Themis Demetriades
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "fileio.h"
|
||||
#include "../global.h"
|
||||
|
||||
byte *fileio_loadBin(const char *filePath, size_t memorySize) {
|
||||
FILE *file = fopen(filePath, "rb");
|
||||
if (file == NULL) {
|
||||
fprintf(stderr, "Couldn't open %s!\n", filePath);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
byte *fileData = malloc(memorySize);
|
||||
if (fileData == NULL) {
|
||||
fprintf(stderr, "Ran out of memory attempting to load %s!\n", filePath);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// Loop while reading from the file yields data. Only terminates if EOF is reached or ERROR occurs.
|
||||
// Explicitly deal with attempting to write too much data to memory block, rather than allow segfault.
|
||||
const size_t byteCount = memorySize/sizeof(byte);
|
||||
int i = 0;
|
||||
while (fread(fileData + i, sizeof(byte), 1, file)) {
|
||||
if (i >= byteCount) {
|
||||
fprintf(stderr, "Attempting to load binary %s to memory of smaller size %zu!\n", filePath, memorySize);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
if (ferror(file)) {
|
||||
fprintf(stderr, "Encountered error attempting to read %s!\n", filePath);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
assert(fclose(file) != EOF);
|
||||
|
||||
// If part of memory block was left uninitialized, initialize it to zero.
|
||||
if (i < byteCount) {
|
||||
memset(fileData + i, 0, (byteCount - i) * sizeof(byte));
|
||||
}
|
||||
return fileData;
|
||||
}
|
||||
26
src/util/fileio.h
Normal file
26
src/util/fileio.h
Normal file
@ -0,0 +1,26 @@
|
||||
/** @file fileio.h
|
||||
* @brief File input/output functions, used in both the emulator and assembler.
|
||||
*
|
||||
* @author Saleh Bubshait
|
||||
* @author Themis Demetriades
|
||||
*/
|
||||
|
||||
#ifndef __FILEIO__
|
||||
#define __FILEIO__
|
||||
#include <stdlib.h>
|
||||
#include "../global.h"
|
||||
|
||||
#define EXIT_FAILURE 1
|
||||
|
||||
/** @brief Loads a binary file located at filePath to memory, taking up a block
|
||||
* of exactly memorySize bytes.
|
||||
* If memorySize is insufficient to store the entire file, an appropriate error
|
||||
* is reported. Excess memory is set to 0 bit values.
|
||||
*
|
||||
* @param filePath The path to the binary file to load.
|
||||
* @param memorySize The size of the memory block to allocate.
|
||||
* @return The starting address of the data loaded from the file.
|
||||
*/
|
||||
byte *fileio_loadBin(const char *filePath, size_t memorySize);
|
||||
|
||||
#endif
|
||||
24
test.sh
Executable file
24
test.sh
Executable file
@ -0,0 +1,24 @@
|
||||
echo "Trying to compile the program..."
|
||||
make -C src
|
||||
|
||||
echo "Fetching the test suite..."
|
||||
git clone git@gitlab.doc.ic.ac.uk:teaching-fellows/armv8_testsuite.git
|
||||
mkdir armv8_testsuite/solution
|
||||
cp -r src/. ./armv8_testsuite/solution
|
||||
cd armv8_testsuite
|
||||
python3 -m pip install -r requirements.txt
|
||||
./install
|
||||
|
||||
echo "Running the test suite..."
|
||||
./run -s
|
||||
|
||||
echo "Test Suite Completed"
|
||||
cd ..
|
||||
|
||||
printf "%s " "Press enter to continue"
|
||||
read ans
|
||||
|
||||
echo "Cleaning Up..."
|
||||
make -C src clean
|
||||
rm -rf armv8_testsuite
|
||||
echo "Done"
|
||||
Loading…
Reference in New Issue
Block a user