Merge branch 'assembler' into 'Assembler-G'

Assembler into Assembler-G

See merge request lab2324_summer/armv8_43!9
This commit is contained in:
Dias Alberto, Ethan 2024-06-12 14:55:07 +00:00
commit c31ba19684
21 changed files with 343 additions and 629 deletions

View File

@ -9,7 +9,7 @@ CFLAGS ?= -std=c17 -g\
all: assemble emulate
assemble: assemble.o
assemble: assemble.o parser.o fileio.o
emulate: emulate.o
clean:

View File

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

View File

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

View File

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

View File

@ -1,7 +1,35 @@
#include <stdlib.h>
#include <stdio.h>
#include "a64instruction/a64instruction.h"
#include "parser.h"
#include "fileio.h"
#include "parser.h"
#include "twopassassembly.c"
int main(int argc, char **argv) {
// Check the arguments
if (argc < 3) {
fprintf(stderr, "Error: A source file and an object output file are required. Syntax: ./assemble <file_in> <file_out>");
return EXIT_FAILURE;
}
// Load the source file into memory
int lineCount = countLines(argv[1]);
char **source = readAssemblyFile(argv[1], lineCount);
// Parse the source file
a64inst_instruction *instructions = parse(source, lineCount);
// First Pass: Create the symbol table
st *table = firstPass(instructions, lineCount);
// Second Pass: Assemble the instructions
word *binary = secondPass(instructions, lineCount, table); // 1000 is just a temp fix.
// Write the binary to the output file
writeBinaryFile(binary, argv[2], lineCount); // 1000 is just a temp fix.
/* TODO: FREE MEMORY!! */
return EXIT_SUCCESS;
}

View File

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

8
src/emulate.c Executable file → Normal file
View File

@ -1,6 +1,6 @@
#include <stdlib.h>
#include <stdio.h>
#include "a64instruction.h"
#include "a64instruction/a64instruction.h"
#include "emulator.h"
#include "fileio.h"
#include "global.h"
@ -8,6 +8,11 @@
#include "decode.h"
#include "execute.h"
int main(int arg, char **argv){
return EXIT_SUCCESS;
}
/*
extern a64inst_instruction *decode(word w);
int main(int argc, char **argv) {
@ -59,3 +64,4 @@ int main(int argc, char **argv) {
return EXIT_SUCCESS;
}
*/

View File

@ -1,448 +0,0 @@
#include <stdlib.h>
#include <assert.h>
#include "execute.h"
#include "print.h"
// Defines the maximum value that can be held in a register
#define MAX_REG_VAL ((1 << DWORD_BITS) - 1)
// The number of bits to shift the immediate value in an arithmetic immediate data processing
// instruction if the shift flag is enabled.
#define DPI_ARITHM_SHIFT 12
// The number of bits to shift the immediate value in a wide move immediate data processing
// instruction if the shift flag is enabled.
#define DPI_WIDEMOV_SHIFT 16
// Prototypes
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);
}
// 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);
state->conditionCodes.Zero = result == 0;
}
// Execute a data processing immediate instruction
static void executeDPImmediate(Machine *state, a64inst_instruction *inst) {
assert(inst->type == a64inst_DPIMMEDIATE);
a64inst_regType regType = inst->data.DPImmediateData.regType;
a64inst_regSpecifier dest = inst->data.DPImmediateData.dest;
switch(inst->data.DPImmediateData.DPIOpType) {
// Execute an arithmetic immediate data processing instruction
case a64inst_DPI_ARITHM:;
// If shift flag is enabled, logical left shift by the number of bits specified by the architecture
dword arithmImm = inst->data.DPImmediateData.processOpData.arithmData.immediate;
dword srcVal = state->registers[inst->data.DPImmediateData.processOpData.arithmData.src];
if (inst->data.DPImmediateData.processOpData.arithmData.shiftImmediate) {
arithmImm = truncateValue(arithmImm << DPI_ARITHM_SHIFT, regType);
}
switch(inst->data.DPImmediateData.processOp) {
dword result;
case(a64inst_ADDS):
result = srcVal + arithmImm;
writeRegister(state, dest, regType, result);
updateCondNZ(state, result, regType);
state->conditionCodes.Overflow = max(srcVal, arithmImm) > result;
state->conditionCodes.Carry = state->conditionCodes.Overflow;
break;
case(a64inst_ADD):
writeRegister(state, dest, regType, srcVal + arithmImm);
break;
case(a64inst_SUBS):
result = srcVal - arithmImm;
writeRegister(state, dest, regType, result);
updateCondNZ(state, result, regType);
state->conditionCodes.Overflow = srcVal < result;
state->conditionCodes.Carry = state->conditionCodes.Overflow;
break;
case(a64inst_SUB):
writeRegister(state, dest, regType, srcVal - arithmImm);
break;
// Unknown opcode detected!
default:
fprintf(stderr, "Unknown opcode detected in a DPI arithmetic instruction!\n");
break;
}
break;
// Execute a wide move immediate data processing instruction
case a64inst_DPI_WIDEMOV:;
uint8_t shiftScalar = inst->data.DPImmediateData.processOpData.wideMovData.shiftScalar;
dword wideMovImm = inst->data.DPImmediateData.processOpData.wideMovData.immediate;
// NOTE: Not checking that shiftScalar has valid value for 32bit registers. Possibly add explicit error.
//printf("%x\n", wideMovImm << (shiftScalar * DPI_WIDEMOV_SHIFT) & );
wideMovImm = truncateValue(wideMovImm << (shiftScalar * DPI_WIDEMOV_SHIFT), regType);
switch(inst->data.DPImmediateData.processOp) {
case(a64inst_MOVN):
writeRegister(state, dest, regType, ~wideMovImm);
break;
case(a64inst_MOVZ):
writeRegister(state, dest, regType, wideMovImm);
break;
case(a64inst_MOVK):;
dword result = readRegister(state, dest, regType);
result = (result & ~(((1lu << DPI_WIDEMOV_SHIFT) - 1) << shiftScalar * DPI_WIDEMOV_SHIFT)) | wideMovImm;
writeRegister(state, dest, regType, result);
break;
default:
fprintf(stderr, "Unknown opcode detected in a DPI wide move instruction!\n");
break;
}
break;
// Unknown instruction detected!
default:
fprintf(stderr, "Attempting to execute instruction with unknown DPI operand type!\n");
break;
}
}
// Execute a data processing register instruction
static void executeDPRegister(Machine *state, a64inst_instruction *inst) {
assert(inst->type == a64inst_DPREGISTER);
a64inst_regType regType = inst->data.DPRegisterData.regType;
a64inst_regSpecifier dest = inst->data.DPRegisterData.dest;
dword src1Val = readRegister(state, inst->data.DPRegisterData.src1, regType);
dword src2Val = readRegister(state, inst->data.DPRegisterData.src2, regType);
switch(inst->data.DPRegisterData.DPROpType) {
// Execute an arithmetic or logic register data processing instruction
case a64inst_DPR_ARITHMLOGIC:;
// Apply shift to value held in second register
a64inst_DPRegister_ArithmLogicData *arithmLogicData = &inst->data.DPRegisterData.processOpData.arithmLogicData;
uint8_t shiftAmount = arithmLogicData->shiftAmount;
switch(arithmLogicData->shiftType) {
case a64inst_LSL:
src2Val = truncateValue(src2Val << shiftAmount, regType);
break;
case a64inst_LSR:
src2Val = truncateValue(src2Val >> shiftAmount, regType);
break;
case a64inst_ASR:
if (regType == a64inst_X) {
src2Val = truncateValue((int64_t)src2Val >> shiftAmount, regType);
} else {
src2Val = truncateValue((int32_t)src2Val >> shiftAmount, regType);
}
break;
case a64inst_ROR:
if (arithmLogicData->type != a64inst_DPR_LOGIC) {
fprintf(stderr, "Attempting to perform ROR shift on non-logic register data processing instruction!\n");
}
src2Val = truncateValue(src2Val >> shiftAmount | src2Val << (getMSBPos(regType) - shiftAmount), regType);
break;
default:
fprintf(stderr, "Attempting to execute arithmetic/logic register data processing instruction with invalid shift type!\n");
break;
}
// Negate second operand if negShiftedSrc2 flag is enabled
if (arithmLogicData->negShiftedSrc2) {
src2Val = truncateValue(~src2Val, regType);
}
dword result;
switch(arithmLogicData->type) {
case a64inst_DPR_ARITHM:
switch(inst->data.DPRegisterData.processOp) {
case(a64inst_ADDS):
result = src1Val + src2Val;
writeRegister(state, dest, regType, result);
updateCondNZ(state, result, regType);
state->conditionCodes.Overflow = max(src1Val, src2Val) > result;
state->conditionCodes.Carry = state->conditionCodes.Overflow;
break;
case(a64inst_ADD):
writeRegister(state, dest, regType, src1Val + src2Val);
break;
case(a64inst_SUBS):
result = src1Val - src2Val;
writeRegister(state, dest, regType, result);
updateCondNZ(state, result, regType);
state->conditionCodes.Overflow = getMSB(src1Val, regType) != getMSB(src2Val, regType) && getMSB(src1Val, regType) != getMSB(result, regType);
state->conditionCodes.Carry = src1Val >= src2Val;
break;
case(a64inst_SUB):
writeRegister(state, dest, regType, src1Val - src2Val);
break;
// Unknown opcode detected!
default:
fprintf(stderr, "Unknown opcode detected in a DPI arithmetic instruction!\n");
break;
}
break;
case a64inst_DPR_LOGIC:
switch(inst->data.DPRegisterData.processOp) {
case a64inst_AND:
writeRegister(state, dest, regType, src1Val & src2Val);
break;
case a64inst_OR:
writeRegister(state, dest, regType, src1Val | src2Val);
break;
case a64inst_XOR:
writeRegister(state, dest, regType, src1Val ^ src2Val);
break;
case a64inst_AND_FLAGGED:;
result = src1Val & src2Val;
writeRegister(state, dest, regType, result);
state->conditionCodes.Overflow = 0;
state->conditionCodes.Carry = 0;
updateCondNZ(state, result, regType);
break;
}
break;
default:
fprintf(stderr, "Attempting to execute an instruction with an unknown DPR arithmetic or logic subtype!\n");
break;
}
break;
// Execute a multiply register data processing instruction
case a64inst_DPR_MULTIPLY:
break;
// Unknown instruction detected!
default:
fprintf(stderr, "Attempting to execute instruction with unknown DPR operand type!\n");
break;
}
}
void execute(Machine *state, a64inst_instruction *inst) {
switch (inst->type) {
// Halt the program
case a64inst_HALT:
break;
// Execute a data processing immediate instruction
case a64inst_DPIMMEDIATE:
executeDPImmediate(state, inst);
break;
// Execute a branch instruction
case a64inst_BRANCH:
execute_Branch(state, inst);
break;
// Execute a data processing register instruction
case a64inst_DPREGISTER:
if (inst->data.DPRegisterData.DPROpType == a64inst_DPR_MULTIPLY)
executeMultiply(state, inst);
else
executeDPRegister(state, inst);
break;
case a64inst_SINGLETRANSFER:
execute_SDT(state, inst);
break;
// Unknown instruction
default:
break;
}
}
void execute_SDT(Machine *state, a64inst_instruction *inst) {
word address;
bool isLoad;
if (inst->data.SingleTransferData.SingleTransferOpType == a64inst_SINGLE_TRANSFER_LOAD_LITERAL) {
// Load Literal
isLoad = true;
address = state->pc + inst->data.SingleTransferData.processOpData.loadLiteralData.offset * 4;
} else {
address = state->registers[inst->data.SingleTransferData.processOpData.singleDataTransferData.base];
isLoad = inst->data.SingleTransferData.processOpData.singleDataTransferData.transferType == a64inst_LOAD;
switch (inst->data.SingleTransferData.processOpData.singleDataTransferData.addressingMode) {
case a64inst_UNSIGNED_OFFSET:
address += inst->data.SingleTransferData.processOpData.singleDataTransferData.a64inst_addressingModeData.unsignedOffset * (inst->data.SingleTransferData.regType == a64inst_W ? 4 : 8);
break;
case a64inst_REGISTER_OFFSET:
address += state->registers[inst->data.SingleTransferData.processOpData.singleDataTransferData.a64inst_addressingModeData.offsetReg];
break;
case a64inst_PRE_INDEXED:
address += inst->data.SingleTransferData.processOpData.singleDataTransferData.a64inst_addressingModeData.indexedOffset;
state->registers[inst->data.SingleTransferData.processOpData.singleDataTransferData.base] = address;
break;
case a64inst_POST_INDEXED:
state->registers[inst->data.SingleTransferData.processOpData.singleDataTransferData.base] = address + inst->data.SingleTransferData.processOpData.singleDataTransferData.a64inst_addressingModeData.indexedOffset;
break;
}
}
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);
}
} else {
*(word *)(state->memory + address) = state->registers[inst->data.SingleTransferData.target];
// Update base register if post indexed
if (inst->data.SingleTransferData.processOpData.singleDataTransferData.addressingMode == a64inst_POST_INDEXED) {
writeRegister(state, inst->data.SingleTransferData.processOpData.singleDataTransferData.base, inst->data.SingleTransferData.regType == a64inst_W, address + inst->data.SingleTransferData.processOpData.singleDataTransferData.a64inst_addressingModeData.indexedOffset);
}
}
}
static bool isConditionMet(Machine* state, a64inst_ConditionType cond) {
switch(cond) {
case EQ:
return state->conditionCodes.Zero;
case NE:
return !state->conditionCodes.Zero;
case GE:
return state->conditionCodes.Negative == state->conditionCodes.Overflow;
case LT:
return state->conditionCodes.Negative != state->conditionCodes.Overflow;
case GT:
return !state->conditionCodes.Zero && (state->conditionCodes.Negative == state->conditionCodes.Overflow);
case LE:
return state->conditionCodes.Zero || (state->conditionCodes.Negative != state->conditionCodes.Overflow);
case AL:
return true;
default:
fprintf(stderr, "Unknown condition specified!\n");
exit(1);
}
}
void execute_Branch(Machine *state, a64inst_instruction *inst) {
switch (inst->data.BranchData.BranchType) {
case a64inst_UNCONDITIONAL:
state->pc += signExtend(inst->data.BranchData.processOpData.unconditionalData.unconditionalOffset * 4, 26);
break;
case a64inst_REGISTER:
state->pc = state->registers[inst->data.BranchData.processOpData.registerData.src];
break;
case a64inst_CONDITIONAL:
if (isConditionMet(state, inst->data.BranchData.processOpData.conditionalData.cond)) {
state->pc += signExtend(inst->data.BranchData.processOpData.conditionalData.offset * 4, 19);
}
break;
}
}
void executeMultiply(Machine *state, a64inst_instruction *inst) {
dword product = state->registers[inst->data.DPRegisterData.src1] * state->registers[inst->data.DPRegisterData.src2];
dword sum = readRegister(state, inst->data.DPRegisterData.processOpData.multiplydata.summand, inst->data.DPRegisterData.regType) + (inst->data.DPRegisterData.processOpData.multiplydata.negProd ? -product : product);
writeRegister(state, inst->data.DPRegisterData.dest, inst->data.DPRegisterData.regType, sum);
}

View File

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

View File

@ -1,48 +1,90 @@
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include "fileio.h"
#include "global.h"
#include "fileio.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,
an appropriate error is reported. Excess memory is set to 0 bit values. */
#define MAX_ASM_LINE_LENGTH 300
byte *fileio_loadBin(const char *filePath, size_t memorySize) {
FILE *file = fopen(filePath, "rb");
int isValidFileFormat(char filename[], char expectedExtension[]){
char *pointLoc = strrchr(filename, '.');
if(pointLoc != NULL){
if(strcmp(pointLoc, expectedExtension)==0){
return(1);
}
}
return(0);
}
void writeBinaryFile(word instrs[], char outputFile[], int numInstrs) {
FILE *fp = fopen(outputFile, "wb");
if (fp == NULL) {
fprintf(stderr, "Error: Could not open file %s\n", outputFile);
exit(EXIT_FAILURE);
}
fwrite(instrs, sizeof(word), numInstrs, fp);
fclose(fp);
}
int countLines(char *filename) {
FILE *file = fopen(filename, "r");
if (file == NULL) {
fprintf(stderr, "Couldn't open %s!\n", filePath);
fprintf(stderr, "Error: Could not read file %s\n", filename);
exit(EXIT_FAILURE);
}
byte *fileData = malloc(memorySize);
if (fileData == NULL) {
fprintf(stderr, "Ran out of memory attempting to load %s!\n", filePath);
int count = 0;
char c;
while ((c = fgetc(file)) != EOF) {
if (c == '\n') {
count++;
}
}
return count;
}
char **readAssemblyFile(char filename[], int lineCount) {
FILE *fp = fopen(filename, "r");
if (fp == NULL) {
fprintf(stderr, "Error: Could not read file %s\n", filename);
exit(EXIT_FAILURE);
}
// Loop while reading from the file yields data. Only terminates if EOF is reached or ERROR occurs.
// Explicitly deal with attempting to write too much data to memory block, rather than allow segfault.
const size_t byteCount = memorySize/sizeof(byte);
int i = 0;
while (fread(fileData + i, sizeof(byte), 1, file)) {
if (i >= byteCount) {
fprintf(stderr, "Attempting to load binary %s to memory of smaller size %zu!\n", filePath, memorySize);
char **lines = malloc(sizeof(char *) * lineCount + 1);
if (lines == NULL) {
fprintf(stderr, "Error: Could not allocate memory to store the assembly lines");
exit(EXIT_FAILURE);
}
rewind(fp); // Back to the beginning of the file.
char buffer[MAX_ASM_LINE_LENGTH];
int currentLine = 0;
while (fgets(buffer, MAX_ASM_LINE_LENGTH, fp) != NULL) {
if (buffer[strlen(buffer) - 1] != '\n') {
// It was actually longer than the maximum.
// NOTE: I believe this must mean that this is a malformed line, so throw an error.
fprintf(stderr, "Error: Line %d in the file %s is too long\n", currentLine, filename);
exit(EXIT_FAILURE);
}
i++;
}
lines[currentLine] = malloc(strlen(buffer) + 1);
if (lines[currentLine] == NULL) {
fprintf(stderr, "Error: Could not allocate memory to store the assembly line");
exit(EXIT_FAILURE);
}
if (ferror(file)) {
fprintf(stderr, "Encountered error attempting to read %s!\n", filePath);
strcpy(lines[currentLine], buffer);
currentLine++;
}
if (ferror(fp)) {
fprintf(stderr, "Error: Could not read file %s", filename);
exit(EXIT_FAILURE);
}
assert(fclose(file) != EOF);
// If part of memory block was left uninitialized, initialize it to zero.
if (i < byteCount) {
memset(fileData + i, 0, (byteCount - i) * sizeof(byte));
}
return fileData;
return lines;
}

View File

@ -1,9 +1,13 @@
#ifndef __FILEIO__
#define __FILEIO__
#include <stdio.h>
#include <stdlib.h>
#include "global.h"
#define EXIT_FAILURE 1
extern byte *fileio_loadBin(const char *filePath, size_t memorySize);
char **readAssemblyFile(char filename[], int lineCount);
void writeBinaryFile(word instrs[], char outputFile[], int numInstrs);
int countLines(char *filename);
#endif

View File

@ -1,8 +1,10 @@
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include "parser.h"
#include "a64instruction.h"
#include "a64instruction/a64instruction.h"
//takes input string, read from asm file and returns
//input as an a64 instruction
@ -11,79 +13,111 @@
// - use string matching to get opcode, and operands (DONE)
// - check operand count (DONE)
// - match opcode to a64 struct types (DONE)
// - count operands and match type/values
// - generate final a64inst and return
// - count operands and match type/values (DONE)
// - generate final a64inst and return (TODO: DP instrs)
// - ASK ABOUT OFFSET CALCULATION
// - CREATE FUNC TO TIDY UP OPERANDS IN DP
int isOperandRegister(char *operand){
return((strcmp(&(operand[0]), "x")==0) || (strcmp(&(operand[0]), "w")==0));
}
//calculate offsets from string
void calcluateAddressFormat(a64inst_instruction *instr, char *operandList[]){
void calcluateAddressFormat(a64inst_instruction *instr, char *operandList[], int numOperands){
char *endptr;
uint8_t base = strtol(&(operandList[1][2]), &endptr, 10);
instr->data.SingleTransferData.processOpData.singleDataTransferData.base = base;
if(strcmp(operandList[2][strlen(operandList[1])-1], "!")==0){
instr->data.processOpData.addressingMode = a64inst_PRE_INDEXED;
} else if(strcmp(operandList[1][strlen(operandList[0])-1], "]") == 0) {
if(strcmp(&(operandList[2][strlen(operandList[1])-1]), "!")==0){
instr->data.SingleTransferData.processOpData.singleDataTransferData.addressingMode = a64inst_PRE_INDEXED;
instr->data.SingleTransferData.processOpData.singleDataTransferData.a64inst_addressingModeData.indexedOffset = strtol(&(operandList[2][1]), &endptr, 10);
} else if(strcmp(&(operandList[1][strlen(operandList[0])-1]), "]") == 0) {
//post-indexed
instr->data.processOpData.addressingMode = a64inst_POST_INDEXED;
} else if( (strcmp(operandList[2][strlen(operandList[1])-1], "x") == 0)
|| (strcmp(operandList[2][strlen(operandList[1])-1], "w") == 0)){
instr->data.SingleTransferData.processOpData.singleDataTransferData.addressingMode = a64inst_POST_INDEXED;
instr->data.SingleTransferData.processOpData.singleDataTransferData.a64inst_addressingModeData.indexedOffset = strtol(&(operandList[2][1]), &endptr, 10);
} else if( (isOperandRegister(&(operandList[2][0])) == 1)
|| (isOperandRegister(&(operandList[2][0])) == 1)){
//register
instr->data.processOpData.addressingMode = a64inst_REGISTER_OFFSET;
instr->data.SingleTransferData.processOpData.singleDataTransferData.addressingMode = a64inst_REGISTER_OFFSET;
instr->data.SingleTransferData.processOpData.singleDataTransferData.a64inst_addressingModeData.offsetReg = strtol(&(operandList[2][1]), &endptr, 10);
} else {
instr->data.processOpData.addressingMode = a64inst_UNSIGNED_OFFSET;
instr->data.SingleTransferData.processOpData.singleDataTransferData.addressingMode = a64inst_UNSIGNED_OFFSET;
if(numOperands==3){
int offset = strtol(&(operandList[2][1]), &endptr, 10);
if(instr->data.SingleTransferData.regType == 1){
instr->data.SingleTransferData.processOpData.singleDataTransferData.a64inst_addressingModeData.unsignedOffset = offset/8;
} else {
instr->data.SingleTransferData.processOpData.singleDataTransferData.a64inst_addressingModeData.unsignedOffset = offset/4;
}
}
}
}
void generateLoadStoreOperands(a64inst_instruction *instr, char *opcode, char *operandList[]){
switch(instr->data.type){
void generateLoadStoreOperands(a64inst_instruction *instr, char *opcode, char *operandList[], int numOperands){
switch(instr->type){
case a64inst_SINGLETRANSFER:
if(strcmp(operandList[0][0], "x")==0){
if(strcmp(&(operandList[0][0]), "x")==0){
//x-register
instr->data.regType = 1;
instr->data.SingleTransferData.regType = 1;
} else {
instr->data.regType = 0;
instr->data.SingleTransferData.regType = 0;
}
char *endptr;
instr->data.target = strtol(operandList[0][0]+1, endptr, 2);
calcluateAddressFormat(instr, operandList);
instr->data.SingleTransferData.target = strtol(&(operandList[0][0])+1, &endptr, 10);
calcluateAddressFormat(instr, operandList, numOperands);
break;
case a64inst_LOADLITERAL:
break;
default:
break;
}
}
void generateBranchOperands(a64inst_instruction *instr, char* opcode, char *operandList[]){
switch(instr->data.BranchType){
char *endptr;
switch(instr->data.BranchData.BranchType){
case a64inst_UNCONDITIONAL:
//define and sign extend immediate offset
//use symbol table
printf("unconditional");
break;
case a64inst_REGISTER:
char *endptr;
instr->data.processOpData.src = strtol(operandList[0] + 1, endptr, 2)
instr->data.BranchData.processOpData.registerData.src = strtol(operandList[0] + 1, &endptr, 10);
break;
case a64inst_CONDITIONAL:
char* condition = strtok(strdup(opcode), "b.");
condition = strtok(NULL, "");
if(strcmp(condition, "eq")==0){
instr->data.processOpData.cond = EQ;
} else if (strcmp(condition, "ne")==0){
instr->data.processOpData.cond = NE;
} else if (strcmp(condition, "ge")==0){
instr->data.processOpData.cond = GE;
} else if (strcmp(condition, "lt")==0){
instr->data.processOpData.cond = LT;
} else if (strcmp(condition, "gt")==0){
instr->data.processOpData.cond = GT;
} else if (strcmp(condition, "le")==0){
instr->data.processOpData.cond = LE;
} else if (srtcmp(condition, "al")==0){
instr->data.processOpData.cond = AL;
{
char *condition = NULL;
condition = strcpy(condition, opcode);
condition += 2;
if(strcmp(condition, "eq")==0){
instr->data.BranchData.processOpData.conditionalData.cond = EQ;
} else if (strcmp(condition, "ne")==0){
instr->data.BranchData.processOpData.conditionalData.cond = NE;
} else if (strcmp(condition, "ge")==0){
instr->data.BranchData.processOpData.conditionalData.cond = GE;
} else if (strcmp(condition, "lt")==0){
instr->data.BranchData.processOpData.conditionalData.cond = LT;
} else if (strcmp(condition, "gt")==0){
instr->data.BranchData.processOpData.conditionalData.cond = GT;
} else if (strcmp(condition, "le")==0){
instr->data.BranchData.processOpData.conditionalData.cond = LE;
} else if (strcmp(condition, "al")==0){
instr->data.BranchData.processOpData.conditionalData.cond = AL;
}
break;
//calculate offset from symbol table.
}
break;
//calculate offset from symbol table.
}
}
void classifyOpcode(char* opcode, a64inst_instruction *instr, char *operandList[]){
int classifyDPInst(char *operandList[]){
return(isOperandRegister(operandList[0]) &&
isOperandRegister(operandList[1]) &&
isOperandRegister(operandList[2]));
}
void classifyOpcode(char* opcode, a64inst_instruction *instr, char *operandList[], int numOperands){
int isUnconditional = strcmp(opcode, "b");
int isRegister = strcmp(opcode, "br");
int isLoad = strcmp(opcode, "ldr");
@ -99,7 +133,6 @@ void classifyOpcode(char* opcode, a64inst_instruction *instr, char *operandList[
instr->data.BranchData.BranchType = a64inst_REGISTER;
} else {
instr->data.BranchData.BranchType = a64inst_CONDITIONAL;
//instr->data.branchData.processOpData.cond = {remove first two chars of opcode}
}
generateBranchOperands(instr, opcode, operandList);
} else if(isLoad == 0 || isStore == 0){
@ -108,20 +141,29 @@ void classifyOpcode(char* opcode, a64inst_instruction *instr, char *operandList[
if( *address == '['){
//type is register
instr->type = a64inst_SINGLETRANSFER;
instr->data.SingleTransferOpType = a64inst_SINGLE_TRANSFER_SINGLE_DATA_TRANSFER;
instr->data.SingleTransferData.SingleTransferOpType = a64inst_SINGLE_TRANSFER_SINGLE_DATA_TRANSFER;
if(isLoad == 0){
instr->data.processOpData.transferType = a64inst_LOAD;
instr->data.SingleTransferData.processOpData.singleDataTransferData.transferType = a64inst_LOAD;
} else {
instr->data.processOpData.transferType = a64inst_STORE;
instr->data.SingleTransferData.processOpData.singleDataTransferData.transferType = a64inst_STORE;
}
} else {
instr->type = a64inst_LOADLITERAL;
//instr->data.processOpData.offset = {} to be defined by symbol table
if(operandList[0][0] =='#'){
//offset is immediate
char *immOffset = NULL;
immOffset = strcpy(immOffset, operandList[0]);
immOffset++;
char *endptr = NULL;
int offset = strtol(immOffset, &endptr, 10);
instr->data.SingleTransferData.processOpData.loadLiteralData.offset = offset;
} else {
//offset is literal, use symbol table and calculate difference
}
}
} else {
int numOperands = sizeof(operandList) / sizeof(operandList[0])
if(numOperands==3){
if(classifyDPInst(operandList)){
instr->type = a64inst_DPREGISTER;
} else {
instr->type = a64inst_DPIMMEDIATE;
@ -130,34 +172,38 @@ void classifyOpcode(char* opcode, a64inst_instruction *instr, char *operandList[
}
}
char *tokeniseOperands(char* str, int operandCount, char *operands[]){
char *operandsDupe = strdup(str);
void tokeniseOperands(char* str, int *operandCount, char *operands[], int *numOperands){
assert(str != NULL);
char operandsDupe[strlen(str)+1];
strcpy(operandsDupe, str);
char *operand = strtok(operandsDupe, OPERAND_DELIMITER);
operands[0] = operand;
while (operand != NULL){
operandCount++;
*operandCount = *(operandCount)+1;
operand = strtok(NULL, OPERAND_DELIMITER);
operands[operandCount] = operand;
operands[*(operandCount)] = operand;
}
*(numOperands) = *(operandCount)+1;
}
//takes inputted assembly line and returns a
//pointer to an abstract representation of the instruction
a64inst_instruction *parser(char asmLine[]){
a64inst_instruction *instr = malloc(sizeof(a64inst_instruction));
void parser_instruction(char asmLine[], a64inst_instruction *instr) {
int numOperands = 0;
if (instr == NULL){
exit(EXIT_FAILURE);
}
if(strcmp(asmLine, HALT_ASM_CMD) == 0){
instr->type = a64inst_HALT;
return(instr);
return;
}
//"opcode operand1, {operand2}, ..."
//duplicated as strtok modifies the input string
char *stringptr = strdup(asmLine);
char stringptr[strlen(asmLine) + 1];
strcpy(stringptr, asmLine);
char *opcode = strtok(stringptr, " ");
char *operands = strtok(NULL, "");
@ -166,22 +212,58 @@ a64inst_instruction *parser(char asmLine[]){
//type is directive
instr->type = a64inst_DIRECTIVE;
} else if(strcmp(opcode[strlen(opcode)-1], ":") == 0) {
} else if(opcode[strlen(opcode)-1]== ':') {
//type is label
//add to symbol table
instr->type = a64inst_LABEL;
char *opcodeCpy = strdup(opcode);
char *opcodeCpy = NULL;
opcodeCpy = strcpy(opcodeCpy, opcode);
char *labelData = strtok(opcodeCpy, ":");
instr->data.label = labelData;
instr->data.LabelData.label = labelData;
} else {
//type is instruction
int operandCount = 0;
const char *operandList[4];
tokeniseOperands(operands, &operandCount, operandList);
char *operandList[4];
//generate list of operands
tokeniseOperands(operands, &operandCount, operandList, &numOperands);
//categorise instruction type from opcode and operands
classifyOpcode(opcode, instr, operandList, operandCount);
//define struct values according to operands and type
switch(instr->type){
case a64inst_BRANCH:
generateBranchOperands(instr, opcode, operandList);
break;
case a64inst_SINGLETRANSFER:
generateLoadStoreOperands(instr, opcode, operandList, numOperands);
break;
case a64inst_LOADLITERAL:
generateLoadStoreOperands(instr, opcode, operandList, numOperands);
break;
case a64inst_DPREGISTER:
//generate DP operands;
break;
case a64inst_DPIMMEDIATE:
//generate DP operands;
break;
default:
printf("INVALID INSTRUCTION");
break;
}
}
return(instr);
}
// Takes an array of strings, each string representing an assembly instruction.
// Returns an array of a64inst_instruction pointers, each representing an instruction.
a64inst_instruction *parse(char **asmLines, int lineCount) {
a64inst_instruction *instructions = malloc(sizeof(a64inst_instruction) * lineCount);
int i = 0;
while (asmLines[i] != NULL) {
parser_instruction(asmLines[i], &instructions[i]);
i++;
}
return instructions;
}

View File

@ -1,5 +1,6 @@
#ifndef __PARSERCONSTS__
#define __PARSERCONSTS__
#include "a64instruction/a64instruction.h"
#define OPERAND_DELIMITER ", "
#define HALT_ASM_CMD "and x0, x0, x0"
#endif
a64inst_instruction *parse(char **asmLines, int lineCount);

View File

@ -1,10 +1,9 @@
#include <stdio.h>
typedef struct st st;
typedef struct node node; // forward declaration
typedef struct {
typedef struct node {
const void* key;
void* value;
node* prev;
@ -29,11 +28,6 @@ void st_add(st table, void* key, void* value) {
}
}
// returns the pointer to key of the specified node, or null, if it does not exist
void* st_search(st table, void* key) {
return nodeSearch(table.head, key);
}
void* nodeSearch(node* n, void* key) {
if (n != NULL) {
if ((*n).key == key) {
@ -46,4 +40,9 @@ void* nodeSearch(node* n, void* key) {
else {
return NULL;
}
}
}
// returns the pointer to key of the specified node, or null, if it does not exist
void* st_search(st table, void* key) {
return nodeSearch(table.head, key);
}

View File

@ -1,33 +1,34 @@
# include "global.h"
# include "a64instruction.h"
# include "symboltable.h"
//generates assembled code based on two pass assembly method
#include "global.h"
#include "a64instruction/a64instruction.h"
#include "symboltable.c"
#include <stdlib.h>
#include <limits.h>
// Generates assembled code based on the two-pass assembly method
word assembleBranch(a64inst_instruction *instr){
word assembleBranch(a64inst_instruction *instr) {
word binInstr = 0;
binInstr += (5^28); //101 start of branch instr
switch (instr->data.BranchData.BranchType)
{
binInstr += (5 << 28); // 101 start of branch instr
switch (instr->data.BranchData.BranchType) {
case a64inst_UNCONDITIONAL:
//000101
//25-0: sign extended simm26
binInstr += instr->data.processOpData.unconditionalOffset;
// 000101
// 25-0: sign extended simm26
binInstr += instr->data.BranchData.processOpData.unconditionalData.unconditionalOffset;
break;
case a64inst_REGISTER:
//10000
//11111
//000000
//9-5: address from register
//0000
binInstr += ((instr->processOpData.src)^5);
// 10000
// 11111
// 000000
// 9-5: address from register
// 0000
binInstr += ((instr->data.BranchData.processOpData.registerData.src) << 5);
break;
case a64inst_CONDITIONAL:
// 01010100
// 01010100
// 25-5: sign extended offset
// 4-0: 0{condition}
binInstr += ((instr->processOpData.offset)^5);
binInstr += instr->processOpData.cond;
binInstr += ((instr->data.BranchData.processOpData.conditionalData.offset) << 5);
binInstr += instr->data.BranchData.processOpData.conditionalData.cond;
break;
default:
break;
@ -35,43 +36,43 @@ word assembleBranch(a64inst_instruction *instr){
return binInstr;
}
st* firstPass(a64inst_instruction instrs[], int numInstrs){
//TODO:
st* firstPass(a64inst_instruction instrs[], int numInstrs) {
// TODO:
// -iterate over instructions, adding to symbol table
// create symbol table and map labels to addresses/lines
struct st table;
for(int i=0; i<numInstrs; i++){
st *table = (st*)malloc(sizeof(st));
for (int i = 0; i < numInstrs; i++) {
// discuss defining a LABEL type
if(instrs[i].type==a64inst_LABEL){
st_add(table, &(instrs[i].data.LabelData.label), &i);
if (instrs[i].type == a64inst_LABEL) {
st_add(*table, &(instrs[i].data.LabelData.label), &i);
}
}
return &table;
return table;
}
word dpi(a64inst_instruction cI) {
word out = 0;
a64inst_DPImmediateData data = cI.data.DPImmediateData;
//sf
out += data.regType*(2^31);
out += data.processOp*(2^29);
out += 2^28;
// sf
out += data.regType * (1 << 31);
out += data.processOp * (1 << 29);
out += 1 << 28;
// if arithmetic
if (data.DPIOpType == a64inst_DPI_ARITHM) {
out += 2^24;
out += 1 << 24;
// shift
if (data.processOpData.arithmData.shiftImmediate){
out += 2^22;
if (data.processOpData.arithmData.shiftImmediate) {
out += 1 << 22;
}
out += data.processOpData.arithmData.immediate*(2^10);
out += data.processOpData.arithmData.src*(2^5);
out += data.processOpData.arithmData.immediate * (1 << 10);
out += data.processOpData.arithmData.src * (1 << 5);
}
// if wide move
else {
out += 5*(2^23);
out += 5 * (1 << 23);
// hw
out += data.processOpData.wideMovData.shiftScalar*(2^21);
out += data.processOpData.wideMovData.immediate*(2^5);
out += data.processOpData.wideMovData.shiftScalar * (1 << 21);
out += data.processOpData.wideMovData.immediate * (1 << 5);
}
// destination register
out += data.dest;
@ -84,7 +85,7 @@ word dpr(a64inst_instruction cI) {
// sf
int sf = data.regType;
// bits 27-25
out += 5*(2^25);
out += 5 * (1 << 25);
int m = data.DPROpType;
int opc = 0;
int opr = 0;
@ -94,7 +95,7 @@ word dpr(a64inst_instruction cI) {
int rd = 0;
// multiply
if (m == 1) {
//opc = 0;
// opc = 0;
opr = 8;
if (data.processOpData.multiplydata.negProd) {
operand += 32;
@ -104,9 +105,9 @@ word dpr(a64inst_instruction cI) {
// arithmetic and logical
else {
// shift
opr += 2*data.processOpData.arithmLogicData.shiftType;
opr += 2 * data.processOpData.arithmLogicData.shiftType;
// arithmetic
if (data.processOpData.arithmLogicData.type == 1){
if (data.processOpData.arithmLogicData.type == 1) {
opr += 8;
}
// logical
@ -120,11 +121,11 @@ word dpr(a64inst_instruction cI) {
rm += data.src1;
rn += data.src2;
rd += data.dest;
out += sf*(2^31);
out += opc * (2^29);
out += m* (2^28);
out += opr * (2^21);
out += rm * (2^16);
out += sf * (1 << 31);
out += opc * (1 << 29);
out += m * (1 << 28);
out += opr * (1 << 21);
out += rm * (1 << 16);
out += operand * 1024;
out += rn * 32;
out += rd;
@ -136,17 +137,16 @@ word sts(a64inst_instruction cI) {
word out = 0;
a64inst_SingleDataTransferData data2 = data.processOpData.singleDataTransferData;
// this deals with every bit in the 31-23 range apart from sf and U
out += (512+128+64+32)*(2^23);
out += (512 + 128 + 64 + 32U) * (1 << 23);
int sf = data.regType;
int u = 0;
int l = data2.transferType;
int offset = 0;
int xn = data2.base;
int rt = data.target;
switch (data2.addressingMode) {
// register offset
case 2:
offset += 2074 + 64*data2.a64inst_addressingModeData.offsetReg;
offset += 2074 + 64 * data2.a64inst_addressingModeData.offsetReg;
break;
// unsigned offset
case 3:
@ -155,37 +155,37 @@ word sts(a64inst_instruction cI) {
break;
// pre/post indexed
default:
offset = 1 + data2.addressingMode*2 + data2.a64inst_addressingModeData.indexedOffset*4;
offset = 1 + data2.addressingMode * 2 + data2.a64inst_addressingModeData.indexedOffset * 4;
break;
}
out += sf*(2^30);
out += u*(2^22);
out += offset*1024;
out += sf * (1 << 30);
out += u * (1 << 22);
out += offset * 1024;
out += xn * 32;
out += rt;
return out;
}
word ldl(a64inst_instruction cI) {
word out = 3*(2^27);
word out = 3 * (1 << 27);
a64inst_SingleTransferData data = cI.data.SingleTransferData;
int sf = data.regType;
int simm19 = data.processOpData.loadLiteralData.offset;
int rt = data.target;
out += sf * (2^30);
out += simm19*32;
out += sf * (1 << 30);
out += simm19 * 32;
out += rt;
return out;
}
void secondPass(a64inst_instruction instrs[], int numInstrs, st* table, word arr[]){
//TODO:
word *secondPass(a64inst_instruction instrs[], int numInstrs, st* table) {
// TODO:
// iterate over instructions again, this time replacing labels
// with values from symbol table
// after a line has had all the values replaced, assemble it and append
word *arr = (word*)malloc(sizeof(word) * numInstrs);
int index = 0;
int lbl = 0;
for (int i=0; i<numInstrs; i++) {
for (int i = 0; i < numInstrs; i++) {
a64inst_instruction cI = instrs[i];
switch (cI.type) {
case a64inst_DPIMMEDIATE:
@ -209,18 +209,18 @@ void secondPass(a64inst_instruction instrs[], int numInstrs, st* table, word arr
index++;
break;
case a64inst_HALT:
arr[index] = 69*(2^25);
arr[index] = 69U * (1 << 25);
index++;
break;
case a64inst_LABEL:
lbl++;
// Labels are handled in the first pass and used for addressing.
break;
case a64inst_BRANCH:
arr[index] = assembleBranch(&cI, table, lbl);
arr[index] = assembleBranch(&cI);
index++;
default:
break;
}
}
return;
}
return arr;
}