From 84953ccabd76e3465148d74fb4cb48490b54a0e0 Mon Sep 17 00:00:00 2001 From: XOR Date: Thu, 18 Jan 2024 20:36:14 +0100 Subject: [PATCH] version bump 0.0.2, dynamic memory, minor debugging capabilities --- design.txt | 10 ++++++++ header/common.h | 8 ++++--- header/dyn_buf.h | 21 +++++++++++++++++ header/execute.h | 4 +++- header/helpers.h | 9 ++++++++ src/dyn_buf.c | 43 +++++++++++++++++++++++++++++++++++ src/execute.c | 59 ++++++++++++++++++++++++++++++++++++++++-------- src/helpers.c | 26 +++++++++++++++++++++ src/main.c | 54 ++++++++++++++++++++++++++++++++++++++------ 9 files changed, 214 insertions(+), 20 deletions(-) create mode 100644 header/dyn_buf.h create mode 100644 src/dyn_buf.c diff --git a/design.txt b/design.txt index 35e6114..e2306e6 100755 --- a/design.txt +++ b/design.txt @@ -22,3 +22,13 @@ Instruction Layout: XXXXYYYYYYYYYYYYYYYYYYYYYYYYYYYY X: Instruction Opcode (4bit) Y: Memory Adress (28bit) + + +Debugging capabilities: + specify breakpoints using the -b option + At each breakpoint, you can use the following commands: + 'h' - help - Show a list of all commands + 'c' - continue - Run the program normally to the next breakpoint + 'p' - print - Print a single memory adress + 'l' - list - show a list from memory adress 0 to the last used adress + 's' - step - run only this instrucion and stop before the next diff --git a/header/common.h b/header/common.h index 318569c..64a8c3a 100755 --- a/header/common.h +++ b/header/common.h @@ -10,13 +10,15 @@ Here, common macros are specified // Maximum Memory Adresses #define MAX_MEMORY 268435456 +#define MAX_BREAKPOINTS 1000 + // Intitial size and grow size for dynamic buffers -#define TABLE_INIT_SIZE 50 -#define TABLE_GROW_SIZE 30 +#define TABLE_INIT_SIZE 10 +#define TABLE_GROW_SIZE 5 #define VER_MAJOR "0" #define VER_MINOR "0" -#define VER_PATCH "1" +#define VER_PATCH "2" // alpha, beta or stable #define TAG "alpha" diff --git a/header/dyn_buf.h b/header/dyn_buf.h new file mode 100644 index 0000000..eda1cbc --- /dev/null +++ b/header/dyn_buf.h @@ -0,0 +1,21 @@ +#ifndef _DYN_BUF_H_ +#define _DYN_BUF_H_ + +#include + +typedef struct +{ + void *buffer; + unsigned int init_size; + unsigned int grow_size; + unsigned long int size; + unsigned long int used; +} dynamic_buffer_t; + +//Intitialize an empty dynamic buffer *buffer. Returns 0 on success. +int init_dynamic_buffer(dynamic_buffer_t *buffer, unsigned int init_size, unsigned int grow_size); + +// Resize a dynamic buffer to size elements. When size is 0, it is resized by TABLE_GROW_SIZE. Returns 0 on sucess +int resize_dynamic_buffer(dynamic_buffer_t *buffer, uint32_t size); + +#endif diff --git a/header/execute.h b/header/execute.h index 8076118..0fa9acb 100644 --- a/header/execute.h +++ b/header/execute.h @@ -6,8 +6,10 @@ #include #include "common.h" +#include "dyn_buf.h" +#include "helpers.h" // Executes given memory -int execute(uint32_t *memory); +int execute(dynamic_buffer_t *memory, int *breakpoints, int breakpoint_count); #endif diff --git a/header/helpers.h b/header/helpers.h index 03a3715..62c0d15 100644 --- a/header/helpers.h +++ b/header/helpers.h @@ -6,4 +6,13 @@ void print_target_code(uint32_t *target_code); +// Returns how often an array contains the value val +unsigned int array_contains(int *array, unsigned int size, int val); + + +// Returns how many values of size type_size +unsigned int fsize(FILE *fp, size_t type_size); + +void clean_in(); + #endif diff --git a/src/dyn_buf.c b/src/dyn_buf.c new file mode 100644 index 0000000..9f0acca --- /dev/null +++ b/src/dyn_buf.c @@ -0,0 +1,43 @@ +#include "../header/dyn_buf.h" +#include +#include + +int init_dynamic_buffer(dynamic_buffer_t *buffer, unsigned int init_size, unsigned int grow_size) +{ + buffer->init_size = init_size; + buffer->size = (unsigned long int) init_size; + buffer->grow_size = grow_size; + buffer->used = 0; + + buffer->buffer = malloc(init_size); + memset(buffer->buffer, 0, buffer->size); + + if(buffer->buffer == NULL) return 1; + return 0; +} + +int resize_dynamic_buffer(dynamic_buffer_t *buffer, uint32_t size) +{ + // Maintain a copy of the buffers adress in case realloc returns NULL + void *buffer_cpy = buffer->buffer; + + uint32_t grow_size; + uint32_t old_buffer_size = buffer->size; + if(size <= 0) grow_size = buffer->grow_size; + else grow_size = size - buffer->size; + + buffer->buffer = realloc(buffer->buffer, buffer->size + grow_size); + buffer->size = buffer->size + grow_size; + + if(buffer->buffer == NULL){ + free(buffer_cpy); + return 1; + } + + if(grow_size > 0) + { + memset(buffer->buffer + old_buffer_size, 0 , grow_size); + } + + return 0; +} diff --git a/src/execute.c b/src/execute.c index 16d39d3..b139cb2 100644 --- a/src/execute.c +++ b/src/execute.c @@ -1,14 +1,53 @@ #include "../header/execute.h" +#include -int execute(uint32_t *memory) +// Write val at adress into memory while making sure the dynamic buffer is correctly sized +static void write_mem(uint32_t adress, uint32_t val, dynamic_buffer_t *memory) +{ + if(adress >= memory->size) + { + if(resize_dynamic_buffer(memory, adress + TABLE_GROW_SIZE) != 0) + { + printf("ERROR: Failed to resize the emulated memory"); + exit(1); + } + } + ((uint32_t *)memory->buffer)[adress] = val; +} + +// Read adress in memory while making sure the dynamic buffer is correctly sized +static uint32_t read_mem(uint32_t adress, dynamic_buffer_t *memory) +{ + if(adress >= memory->size) + { + if(resize_dynamic_buffer(memory, adress + TABLE_GROW_SIZE) != 0) + { + printf("ERROR: Failed to resize the emulated memory"); + exit(1); + } + } + return ((uint32_t *)memory->buffer)[adress]; +} + +int execute(dynamic_buffer_t *memory, int *breakpoints, int breakpoint_count) { uint32_t instr_ptr = 0; uint32_t instr_curr; uint32_t cpu_register = 0; - while((instr_curr = memory[instr_ptr]) >> 28 != INSTR_EOJ) + while((instr_curr = ((uint32_t *) memory->buffer)[instr_ptr]) >> 28 != INSTR_EOJ) { - uint8_t op_code = memory[instr_ptr] >> 28; // Get only the first 4 bits - uint32_t adress = memory[instr_ptr] & ADRESS_BITS; // Discard the first 4 bits + // Debug handling + if(array_contains(breakpoints, breakpoint_count, instr_ptr) > 0) + { + printf("Breakpoint"); + char command; + scanf(""); + clean_in(); + } + + + uint8_t op_code = ((uint32_t *) memory->buffer)[instr_ptr] >> 28; // Get only the first 4 bits + uint32_t adress = ((uint32_t *) memory->buffer)[instr_ptr] & ADRESS_BITS; // Discard the first 4 bits // This check should be unnecessary when using the official EIPA Assembler, but // some people might want to hand-craft their EIPA Images @@ -24,26 +63,28 @@ int execute(uint32_t *memory) case INSTR_INP: { printf("Input: "); - int check = scanf("%u", memory + adress); - fflush(stdin); + uint32_t input; + int check = scanf("%u", &input); + clean_in(); if(!check) { printf("ERROR: Received invalid input on a INP instruction\n"); return 1; } + write_mem(adress, input, memory); instr_ptr++; break; } case INSTR_OUT: - printf("%u\n", memory[adress]); + printf("%u\n", read_mem(adress, memory)); instr_ptr++; break; case INSTR_LDA: - cpu_register = memory[adress]; + cpu_register = read_mem(adress, memory); instr_ptr++; break; case INSTR_STA: - memory[adress] = cpu_register; + write_mem(adress, cpu_register, memory); instr_ptr++; break; case INSTR_INC: diff --git a/src/helpers.c b/src/helpers.c index ed94b88..770d27e 100644 --- a/src/helpers.c +++ b/src/helpers.c @@ -1,4 +1,5 @@ #include "../header/helpers.h" +#include void print_target_code(uint32_t *target_code) { @@ -14,3 +15,28 @@ void print_target_code(uint32_t *target_code) instruction_index++; } } + +unsigned int array_contains(int *array, unsigned int size, int val) +{ + unsigned int count = 0; + for(unsigned int i = 0; i < size; i++) + { + if(array[i] == val) count++; + } + return count; +} + +void clean_in() +{ + char c; + while((c=getchar()) != '\n' && c != EOF); +} + +unsigned int fsize(FILE *fp, size_t type_size) +{ + fseek(fp, 0, SEEK_END); + long size_4 = ftell(fp); // Seems to return size in nibbles... TODO: Check if this is always the case + long size_8 = size_4 / 2; // TODO: Can this be an odd number? + rewind(fp); + return size_8 / type_size; +} diff --git a/src/main.c b/src/main.c index f354d3f..a34504b 100755 --- a/src/main.c +++ b/src/main.c @@ -2,6 +2,7 @@ // Read an EIPA image and print it out in Binary form +#include #ifndef WIN_COMPILE #include #else @@ -13,8 +14,9 @@ #include "../header/argp_commons.h" #include "../header/common.h" -#include "../header/helpers.h" +#include "../header/dyn_buf.h" #include "../header/execute.h" +#include "../header/helpers.h" const char *argp_program_version = VER_MAJOR "." VER_MINOR "." VER_PATCH " || " TAG; @@ -25,6 +27,9 @@ const char *argp_program_bug_address = EIPA_BUG_ADRESS; struct cmd_arguments { char *input_file; + int breakpoints[MAX_BREAKPOINTS]; + // This isn't an argument, but it is used by the parser_function + int breakpoint_count; }; // Parses the command line options & arguments @@ -32,6 +37,9 @@ static error_t parser_function(int key, char *arg, struct argp_state *state) { struct cmd_arguments *save_arguments = state->input; + // Return value for sscanf + int scan_count; + switch (key) { case ARGP_KEY_ARG: @@ -48,6 +56,22 @@ static error_t parser_function(int key, char *arg, struct argp_state *state) { argp_usage(state); } + break; + case 'b': + scan_count = sscanf(arg, "%d[1]", &save_arguments->breakpoints[save_arguments->breakpoint_count]); + + if(save_arguments->breakpoint_count >= MAX_BREAKPOINTS) + { + printf("ERROR: Only up to %d breakpoints supported\n", MAX_BREAKPOINTS); + exit(EXIT_FAILURE); + } + + if (scan_count < 1) + { + argp_usage(state); + } + save_arguments->breakpoint_count++; + break; default: return ARGP_ERR_UNKNOWN; @@ -56,7 +80,16 @@ static error_t parser_function(int key, char *arg, struct argp_state *state) } // This array stores structs containing information about the cli options -static struct argp_option argp_options[] = {{0}}; +static struct argp_option argp_options[] = + { + {.name = "breakpoint", + .key = 'b', + .arg = "BREAK_INSTR", + .flags = 0, + .doc = "Set a breakpoint", + .group = 0}, + {0} +}; // Contains the neccesary stuff for argp static struct argp argument_parser = @@ -71,6 +104,7 @@ int main(int argc, char **argv) // Parse CLI Arguments // set defaults struct cmd_arguments arguments; + arguments.breakpoint_count = 0; arguments.input_file = ""; if (argp_parse(&argument_parser, argc, argv, 0, 0, &arguments) != 0) @@ -82,15 +116,21 @@ int main(int argc, char **argv) FILE *input = fopen(arguments.input_file, "r"); if (input == NULL) return -1; + uint32_t input_instr_count = fsize(input, sizeof(uint32_t)); // Allocate the memory of the Emulated device - uint32_t *memory = malloc(MAX_MEMORY * sizeof(memory)); - + dynamic_buffer_t dyn_mem; + init_dynamic_buffer(&dyn_mem, input_instr_count * sizeof(uint32_t), TABLE_GROW_SIZE * sizeof(uint32_t)); + if(dyn_mem.buffer == NULL) + { + printf("ERROR: Could not allocate memory for the emulator"); + } + // Load the binary into memory - fread(memory, sizeof(memory), MAX_MEMORY, input); + fread(dyn_mem.buffer, sizeof(uint32_t), input_instr_count, input); - execute(memory); + execute(&dyn_mem, arguments.breakpoints, arguments.breakpoint_count); - free(memory); + free(dyn_mem.buffer); return 0; }