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

View File

@ -1,12 +1,14 @@
#include <stdlib.h>
#include <stdio.h>
#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);

View File

@ -1,19 +1,7 @@
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#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.

View File

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

View File

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

View File

@ -2,6 +2,8 @@
#include <assert.h>
#include "execute.h"
#include "print.h"
#include "../shared/binary_util.h"
#include "machine_util.h"
// Defines the maximum value that can be held in a register
#define MAX_REG_VAL ((1 << DWORD_BITS) - 1)
@ -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;

View File

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

View File

@ -2,7 +2,7 @@
#include <stdio.h>
#include <string.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,
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__
#define __FILEIO__
#include <stdlib.h>
#include "global.h"
#include "../global.h"
#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 <stdint.h>
#include <inttypes.h>
#include <string.h>
#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);
}

View File

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

View File

@ -9,6 +9,11 @@
#define __GLOBAL__
#include <stdint.h>
/************************************
* 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

View File

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

View File

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

View File

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

View File

@ -1,6 +1,7 @@
#ifndef __A64INSTRUCTION_GLOBAL__
#define __A64INSTRUCTION_GLOBAL__
#include <stdint.h>
#include "../../global.h"
// Specifies the register being referred to
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