Merge branch 'emulator-s' into 'emulator'

Restructuring of files

See merge request lab2324_summer/armv8_43!10
This commit is contained in:
Demetriades, Themis 2024-06-12 15:00:27 +00:00
commit 075953b06e
26 changed files with 177 additions and 147 deletions

View File

@ -10,8 +10,7 @@ CFLAGS ?= -std=c17 -g\
all: assemble emulate all: assemble emulate
assemble: assemble.o assemble: assemble.o
emulate: emulate.o emulate: emulate.o emulator/fileio.o emulator/execute.o emulator/decode.o emulator/print.o
clean: clean:
$(RM) *.o assemble emulate $(RM) *.o assemble emulate emulator/fileio.o emulator/execute.o emulator/decode.o emulator/print.o

View File

@ -1,12 +1,14 @@
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
#include "a64instruction.h" #include "shared/a64instruction/a64instruction.h"
#include "emulator.h" #include "shared/a64instruction/a64instruction_global.h"
#include "fileio.h" #include "emulator/emulator.h"
#include "emulator/fileio.h"
#include "global.h" #include "global.h"
#include "print.h" #include "emulator/print.h"
#include "decode.h" #include "emulator/decode.h"
#include "execute.h" #include "emulator/execute.h"
#include "emulator/machine_util.h"
extern a64inst_instruction *decode(word w); extern a64inst_instruction *decode(word w);
@ -40,7 +42,7 @@ int main(int argc, char **argv) {
do { do {
// Step 1: Fetch instruction at PC's address // 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 // Step 2: Decode instruction to internal representation
inst = decode(wrd); inst = decode(wrd);

View File

@ -1,19 +1,7 @@
#include <assert.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include "decode.h" #include "decode.h"
#include "emulator.h" #include "../shared/binary_util.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;
}
// Given a binary word, return its internal representation as an a64instruction struct encoding the same // Given a binary word, return its internal representation as an a64instruction struct encoding the same
// information. // information.

View File

@ -1,5 +1,5 @@
#include "global.h" #include "../global.h"
#include "a64instruction.h" #include "../shared/a64instruction/a64instruction.h"
#define HALT_WORD 0x8a000000 #define HALT_WORD 0x8a000000

View File

@ -1,16 +1,8 @@
#ifndef __EMULATOR__ #ifndef __EMULATOR__
#define __EMULATOR__ #define __EMULATOR__
#include "global.h" #include "../global.h"
#include <stdbool.h> #include <stdbool.h>
/************************************
* DEFINITIONS
************************************/
#define BYTE_BITS 8
#define WORD_BITS (BYTE_BITS * sizeof(word))
#define DWORD_BITS (BYTE_BITS * sizeof(dword))
/************************************ /************************************
* STRUCTS * STRUCTS
************************************/ ************************************/

View File

@ -2,6 +2,8 @@
#include <assert.h> #include <assert.h>
#include "execute.h" #include "execute.h"
#include "print.h" #include "print.h"
#include "../shared/binary_util.h"
#include "machine_util.h"
// Defines the maximum value that can be held in a register // Defines the maximum value that can be held in a register
#define MAX_REG_VAL ((1 << DWORD_BITS) - 1) #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 execute_Branch(Machine *state, a64inst_instruction *inst);
void executeMultiply(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 // Updates N and Z condition codes given the machine and a result value
static void updateCondNZ(Machine *state, dword result, a64inst_regType regType) { static void updateCondNZ(Machine *state, dword result, a64inst_regType regType) {
state->conditionCodes.Negative = getMSB(result, regType); state->conditionCodes.Negative = getMSB(result, regType);
@ -383,12 +315,7 @@ void execute_SDT(Machine *state, a64inst_instruction *inst) {
} }
if (isLoad) { if (isLoad) {
if (inst->data.SingleTransferData.regType == a64inst_W) { state->registers[inst->data.SingleTransferData.target] = readMemory(state->memory, address, inst->data.SingleTransferData.regType);
// 32 bit access
state->registers[inst->data.SingleTransferData.target] = readWord(state->memory, address);
} else {
state->registers[inst->data.SingleTransferData.target] = readDoubleWord(state->memory, address);
}
// Update base register if post indexed // Update base register if post indexed
bool isSDT = inst->data.SingleTransferData.SingleTransferOpType == a64inst_SINGLE_TRANSFER_SINGLE_DATA_TRANSFER; bool isSDT = inst->data.SingleTransferData.SingleTransferOpType == a64inst_SINGLE_TRANSFER_SINGLE_DATA_TRANSFER;

View File

@ -1,6 +1,6 @@
#ifndef __EXECUTE__ #ifndef __EXECUTE__
#define __EXECUTE__ #define __EXECUTE__
#include "a64instruction.h" #include "../shared/a64instruction/a64instruction.h"
#include "emulator.h" #include "emulator.h"
void execute(Machine *state, a64inst_instruction *inst); void execute(Machine *state, a64inst_instruction *inst);

View File

@ -2,7 +2,7 @@
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include "fileio.h" #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, /* 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, and returns the starting address of the data. If memorySize is insufficient to store the entire file,

View File

@ -1,7 +1,7 @@
#ifndef __FILEIO__ #ifndef __FILEIO__
#define __FILEIO__ #define __FILEIO__
#include <stdlib.h> #include <stdlib.h>
#include "global.h" #include "../global.h"
#define EXIT_FAILURE 1 #define EXIT_FAILURE 1

View File

@ -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);
}
}

View File

@ -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

View File

@ -1,9 +1,10 @@
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#include <inttypes.h> #include <inttypes.h>
#include <string.h>
#include "print.h" #include "print.h"
#include "../shared/a64instruction/a64instruction_global.h"
#include "emulator.h" #include "emulator.h"
#include "machine_util.h"
#define UNSET_CONDITION_CODE_CHAR '-' #define UNSET_CONDITION_CODE_CHAR '-'
@ -26,39 +27,13 @@ void printRegisters(Machine *state, FILE *stream) {
state->conditionCodes.Overflow ? 'V' : UNSET_CONDITION_CODE_CHAR); 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 // Prints all non-zero memory locations into the provided stream
void printMemory(Machine *state, FILE *stream) { void printMemory(Machine *state, FILE *stream) {
fprintf(stream, "\nNon-zero memory:\n"); fprintf(stream, "\nNon-zero memory:\n");
// print memory 4 byte aligned // print memory 4 byte aligned
for (int addr = 0; addr < MEMORY_SIZE; addr+= 4) { 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) { if (data != 0) {
fprintf(stream, "0x%08x: %08x\n", addr, data); fprintf(stream, "0x%08x: %08x\n", addr, data);
} }

View File

@ -3,9 +3,6 @@
#include <stdio.h> #include <stdio.h>
#include "emulator.h" #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 printState(Machine *state, FILE *stream);
void printRegisters(Machine *state, FILE *stream); void printRegisters(Machine *state, FILE *stream);
void printMemory(Machine *state, FILE *stream); void printMemory(Machine *state, FILE *stream);

View File

@ -9,6 +9,11 @@
#define __GLOBAL__ #define __GLOBAL__
#include <stdint.h> #include <stdint.h>
/************************************
* DEFINITIONS
************************************/
// Number of General Purpose Registers. // Number of General Purpose Registers.
#define REGISTER_COUNT 31 #define REGISTER_COUNT 31
// Register identifier interpreted as the 'zero register' // Register identifier interpreted as the 'zero register'
@ -27,4 +32,8 @@ typedef uint32_t word;
// Double word is a 64-bit unsigned integer. // Double word is a 64-bit unsigned integer.
typedef uint64_t dword; typedef uint64_t dword;
#define BYTE_BITS 8
#define WORD_BITS (BYTE_BITS * sizeof(word))
#define DWORD_BITS (BYTE_BITS * sizeof(dword))
#endif #endif

View File

@ -1,6 +1,5 @@
#include <stdbool.h> #include <stdbool.h>
#include "a64instruction_global.h" #include "a64instruction_global.h"
#include "global.h"
typedef enum { typedef enum {
a64inst_UNCONDITIONAL = 0, a64inst_UNCONDITIONAL = 0,

View File

@ -1,4 +1,4 @@
#include "global.h" #include "./a64instruction_global.h"
typedef struct { typedef struct {
dword value; dword value;

View File

@ -1,6 +1,5 @@
#include <stdbool.h> #include <stdbool.h>
#include "a64instruction_global.h" #include "a64instruction_global.h"
#include "global.h"
typedef enum { typedef enum {
a64inst_SINGLE_TRANSFER_SINGLE_DATA_TRANSFER = 1, a64inst_SINGLE_TRANSFER_SINGLE_DATA_TRANSFER = 1,

View File

@ -1,6 +1,7 @@
#ifndef __A64INSTRUCTION_GLOBAL__ #ifndef __A64INSTRUCTION_GLOBAL__
#define __A64INSTRUCTION_GLOBAL__ #define __A64INSTRUCTION_GLOBAL__
#include <stdint.h> #include <stdint.h>
#include "../../global.h"
// Specifies the register being referred to // Specifies the register being referred to
typedef uint8_t a64inst_regSpecifier; typedef uint8_t a64inst_regSpecifier;

57
src/shared/binary_util.c Normal file
View File

@ -0,0 +1,57 @@
/** Binary Util */
#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;
//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;
}

19
src/shared/binary_util.h Normal file
View File

@ -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

0
test.sh Normal file → Executable file
View File