diff --git a/src/emulate.c b/src/emulate.c index fc508a0..631d1f0 100755 --- a/src/emulate.c +++ b/src/emulate.c @@ -1,3 +1,11 @@ +/** @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 #include #include "a64instruction/a64instruction.h" @@ -20,6 +28,7 @@ int main(int argc, char **argv) { 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"); diff --git a/src/emulator/decode.c b/src/emulator/decode.c index 39fde3b..569c63b 100644 --- a/src/emulator/decode.c +++ b/src/emulator/decode.c @@ -17,6 +17,102 @@ // 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 ************************************/ @@ -26,12 +122,10 @@ static void decodeBranch(word wrd, a64inst_instruction *inst); static void decodeDPRegister(word wrd, a64inst_instruction *inst); static void decodeSingleTransfer(word wrd, a64inst_instruction *inst); -/** @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. - */ +/************************************ + * FUNCTIONS + ************************************/ + a64inst_instruction *decode(word wrd) { a64inst_instruction *inst = malloc(sizeof(a64inst_instruction)); diff --git a/src/emulator/decode.h b/src/emulator/decode.h index 668e06e..964e5fc 100644 --- a/src/emulator/decode.h +++ b/src/emulator/decode.h @@ -1,96 +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" -#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 -// Defines the values for fields used for arithmetic/logic DPR instructions -// that are necessary to indicate a multiplication instruction -#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 +/** @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); diff --git a/src/emulator/emulator.h b/src/emulator/emulator.h index 69d1408..33a92b0 100644 --- a/src/emulator/emulator.h +++ b/src/emulator/emulator.h @@ -1,3 +1,11 @@ +/** @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" diff --git a/src/emulator/execute.c b/src/emulator/execute.c index b1a6663..2ac3316 100644 --- a/src/emulator/execute.c +++ b/src/emulator/execute.c @@ -1,3 +1,14 @@ +/** @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 #include #include "execute.h" @@ -40,7 +51,7 @@ const executeFunction EXECUTE_FUNCTIONS[] = &executeBranch, &executeHalt}; -// Main execute function + void execute(Machine *state, a64inst_instruction *inst) { EXECUTE_FUNCTIONS[inst->type](state, inst); } diff --git a/src/emulator/execute.h b/src/emulator/execute.h index 3acc17b..f9aafec 100644 --- a/src/emulator/execute.h +++ b/src/emulator/execute.h @@ -3,5 +3,11 @@ #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 diff --git a/src/emulator/machine_util.c b/src/emulator/machine_util.c index 3a27c89..7899114 100644 --- a/src/emulator/machine_util.c +++ b/src/emulator/machine_util.c @@ -1,4 +1,13 @@ -/** Machine Util */ +/** @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" @@ -6,7 +15,6 @@ #include "emulator.h" #include "../util/binary_util.h" -// Returns the dword starting at address. The value is truncated if regType is a 64-bit register. 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; @@ -20,7 +28,6 @@ dword readMemory(byte *memory, uint32_t address, a64inst_regType regType) { return result; } -// Store into memory starting at address the given dword. void storeMemory(byte *memory, uint32_t address, dword data) { int bytesPerDword = DWORD_BITS / BYTE_BITS - 1; for (int i = 0; i <= bytesPerDword; i++) { @@ -28,9 +35,6 @@ void storeMemory(byte *memory, uint32_t address, dword data) { } } -// Read from processor register, ensuring that a valid register specifier is given -// and accounting for the case where the zero register is accessed. Truncate -// the 32 most significant bits stored in the R register when reading W register. dword readRegister(Machine *state, a64inst_regSpecifier reg, a64inst_regType regType) { assert(reg <= REGISTER_COUNT); if (reg == ZERO_REGISTER) { @@ -40,8 +44,6 @@ dword readRegister(Machine *state, a64inst_regSpecifier reg, a64inst_regType reg } } -// Write to a processor register, ensuring that a valid register specifier is given -// and truncating the value being written when it can't fit in the specified register void writeRegister(Machine *state, a64inst_regSpecifier reg, a64inst_regType regType, dword value) { assert(reg <= REGISTER_COUNT); if (reg != ZERO_REGISTER) { diff --git a/src/emulator/machine_util.h b/src/emulator/machine_util.h index 14306dc..047287e 100644 --- a/src/emulator/machine_util.h +++ b/src/emulator/machine_util.h @@ -1,16 +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 diff --git a/src/emulator/print.c b/src/emulator/print.c index 36c7632..978a74a 100644 --- a/src/emulator/print.c +++ b/src/emulator/print.c @@ -1,3 +1,14 @@ +/** @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 #include #include @@ -8,13 +19,11 @@ #define UNSET_CONDITION_CODE_CHAR '-' -// Prints the current machine state into the provided stream void printState(Machine *state, FILE *stream) { printRegisters(state, stream); printMemory(state, stream); } -// Prints the current machine registers into the provided stream void printRegisters(Machine *state, FILE *stream) { fprintf(stream, "Registers:\n"); for (int i = 0; i < REGISTER_COUNT; i++) { @@ -27,7 +36,6 @@ void printRegisters(Machine *state, FILE *stream) { state->conditionCodes.Overflow ? 'V' : UNSET_CONDITION_CODE_CHAR); } -// Prints all non-zero memory locations into the provided stream void printMemory(Machine *state, FILE *stream) { fprintf(stream, "\nNon-zero memory:\n"); diff --git a/src/emulator/print.h b/src/emulator/print.h index 35001d9..9d946fd 100644 --- a/src/emulator/print.h +++ b/src/emulator/print.h @@ -1,10 +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 #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