diff --git a/src/Makefile b/src/Makefile index 7106e44..dd827a8 100755 --- a/src/Makefile +++ b/src/Makefile @@ -10,8 +10,7 @@ CFLAGS ?= -std=c17 -g\ all: assemble emulate assemble: assemble.o -emulate: emulate.o +emulate: emulate.o emulator/fileio.o emulator/execute.o emulator/decode.o emulator/print.o clean: - $(RM) *.o assemble emulate - + $(RM) *.o assemble emulate emulator/fileio.o emulator/execute.o emulator/decode.o emulator/print.o diff --git a/src/emulate.c b/src/emulate.c index 82245e9..ba6bbea 100755 --- a/src/emulate.c +++ b/src/emulate.c @@ -1,12 +1,14 @@ #include #include -#include "a64instruction.h" -#include "emulator.h" -#include "fileio.h" +#include "shared/a64instruction/a64instruction.h" +#include "shared/a64instruction/a64instruction_global.h" +#include "emulator/emulator.h" +#include "emulator/fileio.h" #include "global.h" -#include "print.h" -#include "decode.h" -#include "execute.h" +#include "emulator/print.h" +#include "emulator/decode.h" +#include "emulator/execute.h" +#include "emulator/machine_util.h" extern a64inst_instruction *decode(word w); @@ -40,7 +42,7 @@ int main(int argc, char **argv) { do { // Step 1: Fetch instruction at PC's address - wrd = readWord(state.memory, state.pc); + wrd = readMemory(state.memory, state.pc, a64inst_W); // Step 2: Decode instruction to internal representation inst = decode(wrd); diff --git a/src/decode.c b/src/emulator/decode.c similarity index 95% rename from src/decode.c rename to src/emulator/decode.c index ce69c35..ad09429 100644 --- a/src/decode.c +++ b/src/emulator/decode.c @@ -1,19 +1,7 @@ -#include #include #include #include "decode.h" -#include "emulator.h" - -// Retrieve the bits between positions 'lsb' (inclusive) and 'msb' (exclusive) from a given word -// as a new zero-extended word. -static 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; -} +#include "../shared/binary_util.h" // Given a binary word, return its internal representation as an a64instruction struct encoding the same // information. diff --git a/src/decode.h b/src/emulator/decode.h similarity index 97% rename from src/decode.h rename to src/emulator/decode.h index 132130e..b5982be 100644 --- a/src/decode.h +++ b/src/emulator/decode.h @@ -1,5 +1,5 @@ -#include "global.h" -#include "a64instruction.h" +#include "../global.h" +#include "../shared/a64instruction/a64instruction.h" #define HALT_WORD 0x8a000000 diff --git a/src/emulator.h b/src/emulator/emulator.h similarity index 62% rename from src/emulator.h rename to src/emulator/emulator.h index facbfd7..69d1408 100644 --- a/src/emulator.h +++ b/src/emulator/emulator.h @@ -1,16 +1,8 @@ #ifndef __EMULATOR__ #define __EMULATOR__ -#include "global.h" +#include "../global.h" #include -/************************************ - * DEFINITIONS - ************************************/ - -#define BYTE_BITS 8 -#define WORD_BITS (BYTE_BITS * sizeof(word)) -#define DWORD_BITS (BYTE_BITS * sizeof(dword)) - /************************************ * STRUCTS ************************************/ diff --git a/src/execute.c b/src/emulator/execute.c similarity index 85% rename from src/execute.c rename to src/emulator/execute.c index d5e60a6..fddd50f 100644 --- a/src/execute.c +++ b/src/emulator/execute.c @@ -2,6 +2,8 @@ #include #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) @@ -19,76 +21,6 @@ void execute_SDT(Machine *state, a64inst_instruction *inst); void execute_Branch(Machine *state, a64inst_instruction *inst); void executeMultiply(Machine *state, a64inst_instruction *inst); -// Return maximum of two dwords -static dword max(dword a, dword b) { - return a > b ? a : b; -} - -// Truncate a given value to the size of a word or dword depending on the register type -static dword truncateValue(dword value, a64inst_regType regType) { - if (regType == a64inst_X) { - return value; - } else { - return (word)value; - //return value & (dword)(((dword)1 << WORD_BITS) - 1); - } -} - -// Sign extend a given value to a 64-bit signed integer given the number of bits -static 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); - } -} - -// 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. -static 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); - } -} - -// TODO: - -// 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 -static 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); - } -} - -// Returns the position of the MSB of the given register type -inline static dword getMSBPos(a64inst_regType regType) { - return (regType ? DWORD_BITS : WORD_BITS) - 1; -} - -// Returns the MSB of the given value assuming it's of the size stored in the given register type -inline static uint8_t getMSB(dword value, a64inst_regType regType) { - return (value >> getMSBPos(regType)) & 1u; -} - // 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); @@ -383,12 +315,7 @@ void execute_SDT(Machine *state, a64inst_instruction *inst) { } if (isLoad) { - if (inst->data.SingleTransferData.regType == a64inst_W) { - // 32 bit access - state->registers[inst->data.SingleTransferData.target] = readWord(state->memory, address); - } else { - state->registers[inst->data.SingleTransferData.target] = readDoubleWord(state->memory, address); - } + 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; diff --git a/src/execute.h b/src/emulator/execute.h similarity index 70% rename from src/execute.h rename to src/emulator/execute.h index fcf39ec..be82de6 100644 --- a/src/execute.h +++ b/src/emulator/execute.h @@ -1,6 +1,6 @@ #ifndef __EXECUTE__ #define __EXECUTE__ -#include "a64instruction.h" +#include "../shared/a64instruction/a64instruction.h" #include "emulator.h" void execute(Machine *state, a64inst_instruction *inst); diff --git a/src/fileio.c b/src/emulator/fileio.c similarity index 98% rename from src/fileio.c rename to src/emulator/fileio.c index 1dcdd77..41df962 100644 --- a/src/fileio.c +++ b/src/emulator/fileio.c @@ -2,7 +2,7 @@ #include #include #include "fileio.h" -#include "global.h" +#include "../global.h" /* Loads a binary file located at filePath to memory, taking up a block of exactly memorySize bytes, and returns the starting address of the data. If memorySize is insufficient to store the entire file, diff --git a/src/fileio.h b/src/emulator/fileio.h similarity index 87% rename from src/fileio.h rename to src/emulator/fileio.h index a2d4262..47aa858 100644 --- a/src/fileio.h +++ b/src/emulator/fileio.h @@ -1,7 +1,7 @@ #ifndef __FILEIO__ #define __FILEIO__ #include -#include "global.h" +#include "../global.h" #define EXIT_FAILURE 1 diff --git a/src/emulator/machine_util.c b/src/emulator/machine_util.c new file mode 100644 index 0000000..9db8525 --- /dev/null +++ b/src/emulator/machine_util.c @@ -0,0 +1,50 @@ +/** Machine Util */ +#include "assert.h" +#include "machine_util.h" +#include "../shared/a64instruction/a64instruction_global.h" +#include "../global.h" +#include "emulator.h" +#include "../shared/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; + 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; +} + +// 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++) { + memory[address + i] = (byte)((data >> (BYTE_BITS * i)) & 0xFF); + } +} + +// 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) { + return 0; + } else { + return truncateValue(state->registers[reg], regType); + } +} + +// 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) { + state->registers[reg] = truncateValue(value, regType); + } +} diff --git a/src/emulator/machine_util.h b/src/emulator/machine_util.h new file mode 100644 index 0000000..89733e6 --- /dev/null +++ b/src/emulator/machine_util.h @@ -0,0 +1,16 @@ +#ifndef __MACHINE_UTIL__ +#define __MACHINE_UTIL__ +#include "../global.h" +#include "../shared/a64instruction/a64instruction_global.h" +#include "emulator.h" + + +dword readMemory(byte *memory, uint32_t address, a64inst_regType regType); + +void storeMemory(byte *memory, uint32_t address, dword data); + +dword readRegister(Machine *state, a64inst_regSpecifier reg, a64inst_regType regType); + +void writeRegister(Machine *state, a64inst_regSpecifier reg, a64inst_regType regType, dword value); + +#endif diff --git a/src/print.c b/src/emulator/print.c similarity index 59% rename from src/print.c rename to src/emulator/print.c index 88ed6f9..457aa7e 100644 --- a/src/print.c +++ b/src/emulator/print.c @@ -1,9 +1,10 @@ #include #include #include -#include #include "print.h" +#include "../shared/a64instruction/a64instruction_global.h" #include "emulator.h" +#include "machine_util.h" #define UNSET_CONDITION_CODE_CHAR '-' @@ -26,39 +27,13 @@ void printRegisters(Machine *state, FILE *stream) { state->conditionCodes.Overflow ? 'V' : UNSET_CONDITION_CODE_CHAR); } -// Returns the word starting at the provided address -word readWord(byte *memory, uint32_t address) { - word result = 0; - int bytesPerWord = WORD_BITS / BYTE_BITS - 1; - for (int i = 0; i <= bytesPerWord; i++) - result |= (word) memory[address + i] << (BYTE_BITS * i); - return result; -} - -// Returns the double word starting at the provided address -dword readDoubleWord(byte *memory, uint32_t address) { - dword result = 0; - int bytesPerDword = DWORD_BITS / BYTE_BITS - 1; - for (int i = 0; i <= bytesPerDword; i++) - result |= (dword) memory[address + i] << (BYTE_BITS * i); - 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++) { - memory[address + i] = (byte)((data >> (BYTE_BITS * i)) & 0xFF); - } -} - // Prints all non-zero memory locations into the provided stream 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 = readWord(state->memory, addr); + word data = readMemory(state->memory, addr, a64inst_W); if (data != 0) { fprintf(stream, "0x%08x: %08x\n", addr, data); } diff --git a/src/print.h b/src/emulator/print.h similarity index 58% rename from src/print.h rename to src/emulator/print.h index 5a64a4e..35001d9 100644 --- a/src/print.h +++ b/src/emulator/print.h @@ -3,9 +3,6 @@ #include #include "emulator.h" -word readWord(byte *memory, uint32_t address); -dword readDoubleWord(byte *memory, uint32_t address); -void storeMemory(byte *memory, uint32_t address, dword data); void printState(Machine *state, FILE *stream); void printRegisters(Machine *state, FILE *stream); void printMemory(Machine *state, FILE *stream); diff --git a/src/global.h b/src/global.h index 1cf226e..5d792e4 100644 --- a/src/global.h +++ b/src/global.h @@ -9,6 +9,11 @@ #define __GLOBAL__ #include + +/************************************ + * DEFINITIONS + ************************************/ + // Number of General Purpose Registers. #define REGISTER_COUNT 31 // Register identifier interpreted as the 'zero register' @@ -27,4 +32,8 @@ typedef uint32_t word; // Double word is a 64-bit unsigned integer. typedef uint64_t dword; +#define BYTE_BITS 8 +#define WORD_BITS (BYTE_BITS * sizeof(word)) +#define DWORD_BITS (BYTE_BITS * sizeof(dword)) + #endif diff --git a/src/a64instruction.h b/src/shared/a64instruction/a64instruction.h similarity index 100% rename from src/a64instruction.h rename to src/shared/a64instruction/a64instruction.h diff --git a/src/a64instruction_Branch.h b/src/shared/a64instruction/a64instruction_Branch.h similarity index 98% rename from src/a64instruction_Branch.h rename to src/shared/a64instruction/a64instruction_Branch.h index b732d6d..d280973 100644 --- a/src/a64instruction_Branch.h +++ b/src/shared/a64instruction/a64instruction_Branch.h @@ -1,6 +1,5 @@ #include #include "a64instruction_global.h" -#include "global.h" typedef enum { a64inst_UNCONDITIONAL = 0, diff --git a/src/a64instruction_DP.h b/src/shared/a64instruction/a64instruction_DP.h similarity index 100% rename from src/a64instruction_DP.h rename to src/shared/a64instruction/a64instruction_DP.h diff --git a/src/a64instruction_DPImmediate.h b/src/shared/a64instruction/a64instruction_DPImmediate.h similarity index 100% rename from src/a64instruction_DPImmediate.h rename to src/shared/a64instruction/a64instruction_DPImmediate.h diff --git a/src/a64instruction_DPRegister.h b/src/shared/a64instruction/a64instruction_DPRegister.h similarity index 100% rename from src/a64instruction_DPRegister.h rename to src/shared/a64instruction/a64instruction_DPRegister.h diff --git a/src/a64instruction_Directive.h b/src/shared/a64instruction/a64instruction_Directive.h similarity index 61% rename from src/a64instruction_Directive.h rename to src/shared/a64instruction/a64instruction_Directive.h index 5c70dd4..fa2faaa 100644 --- a/src/a64instruction_Directive.h +++ b/src/shared/a64instruction/a64instruction_Directive.h @@ -1,4 +1,4 @@ -#include "global.h" +#include "./a64instruction_global.h" typedef struct { dword value; diff --git a/src/a64instruction_Label.h b/src/shared/a64instruction/a64instruction_Label.h similarity index 100% rename from src/a64instruction_Label.h rename to src/shared/a64instruction/a64instruction_Label.h diff --git a/src/a64instruction_SingleTransfer.h b/src/shared/a64instruction/a64instruction_SingleTransfer.h similarity index 98% rename from src/a64instruction_SingleTransfer.h rename to src/shared/a64instruction/a64instruction_SingleTransfer.h index f661116..3e3da2b 100644 --- a/src/a64instruction_SingleTransfer.h +++ b/src/shared/a64instruction/a64instruction_SingleTransfer.h @@ -1,6 +1,5 @@ #include #include "a64instruction_global.h" -#include "global.h" typedef enum { a64inst_SINGLE_TRANSFER_SINGLE_DATA_TRANSFER = 1, diff --git a/src/a64instruction_global.h b/src/shared/a64instruction/a64instruction_global.h similarity index 92% rename from src/a64instruction_global.h rename to src/shared/a64instruction/a64instruction_global.h index ff748a6..629843d 100644 --- a/src/a64instruction_global.h +++ b/src/shared/a64instruction/a64instruction_global.h @@ -1,6 +1,7 @@ #ifndef __A64INSTRUCTION_GLOBAL__ #define __A64INSTRUCTION_GLOBAL__ #include +#include "../../global.h" // Specifies the register being referred to typedef uint8_t a64inst_regSpecifier; diff --git a/src/shared/binary_util.c b/src/shared/binary_util.c new file mode 100644 index 0000000..78bf11d --- /dev/null +++ b/src/shared/binary_util.c @@ -0,0 +1,57 @@ +/** Binary Util */ +#include +#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; + //return value & (dword)(((dword)1 << WORD_BITS) - 1); + } +} + +// Sign extend a given value to a 64-bit signed integer given the number of bits +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); + } +} + +// Returns the position of the MSB of the given register type +dword getMSBPos(a64inst_regType regType) { + return (regType ? DWORD_BITS : WORD_BITS) - 1; +} + +// Returns the MSB of the given value assuming it's of the size stored in the given register type +uint8_t getMSB(dword value, a64inst_regType regType) { + return (value >> getMSBPos(regType)) & 1u; +} diff --git a/src/shared/binary_util.h b/src/shared/binary_util.h new file mode 100644 index 0000000..8438ef9 --- /dev/null +++ b/src/shared/binary_util.h @@ -0,0 +1,19 @@ +#ifndef __BINARY_UTIL__ +#define __BINARY_UTIL__ + +#include "../global.h" +#include "a64instruction/a64instruction_global.h" + +word getBits(word wrd, uint8_t lsb, uint8_t msb); + +dword max(dword a, dword b); + +dword truncateValue(dword value, a64inst_regType regType); + +int64_t signExtend(dword value, unsigned int n); + +dword getMSBPos(a64inst_regType regType); + +uint8_t getMSB(dword value, a64inst_regType regType); + +#endif diff --git a/test.sh b/test.sh old mode 100644 new mode 100755