Split utils into machine_util and binary_util modules
This commit is contained in:
parent
a42576b4ed
commit
c4e3493fdc
@ -1,12 +1,14 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include "emulator/a64instruction/a64instruction.h"
|
||||
#include "emulator/a64instruction/a64instruction_global.h"
|
||||
#include "emulator/emulator.h"
|
||||
#include "emulator/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);
|
||||
|
||||
@ -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);
|
||||
|
||||
57
src/emulator/binary_util.c
Normal file
57
src/emulator/binary_util.c
Normal 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;
|
||||
}
|
||||
20
src/emulator/binary_util.h
Normal file
20
src/emulator/binary_util.h
Normal file
@ -0,0 +1,20 @@
|
||||
#ifndef __BINARY_UTIL__
|
||||
#define __BINARY_UTIL__
|
||||
|
||||
#include "emulator.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
|
||||
@ -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 "binary_util.h"
|
||||
|
||||
// Given a binary word, return its internal representation as an a64instruction struct encoding the same
|
||||
// information.
|
||||
|
||||
@ -2,6 +2,8 @@
|
||||
#include <assert.h>
|
||||
#include "execute.h"
|
||||
#include "print.h"
|
||||
#include "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;
|
||||
|
||||
50
src/emulator/machine_util.c
Normal file
50
src/emulator/machine_util.c
Normal file
@ -0,0 +1,50 @@
|
||||
/** Machine Util */
|
||||
#include "assert.h"
|
||||
#include "machine_util.h"
|
||||
#include "a64instruction/a64instruction_global.h"
|
||||
#include "../global.h"
|
||||
#include "emulator.h"
|
||||
#include "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);
|
||||
}
|
||||
}
|
||||
16
src/emulator/machine_util.h
Normal file
16
src/emulator/machine_util.h
Normal file
@ -0,0 +1,16 @@
|
||||
#ifndef __MACHINE_UTIL__
|
||||
#define __MACHINE_UTIL__
|
||||
#include "../global.h"
|
||||
#include "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
|
||||
@ -2,7 +2,9 @@
|
||||
#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 '-'
|
||||
|
||||
@ -25,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);
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user