Compare commits

...

3 commits

Author SHA1 Message Date
XOR
08d2ae1ad1 Update gitignore 2024-01-18 20:42:22 +01:00
XOR
d5458eb310 Update test program 2024-01-18 20:41:43 +01:00
XOR
84953ccabd version bump 0.0.2, dynamic memory, minor debugging capabilities 2024-01-18 20:36:14 +01:00
11 changed files with 215 additions and 21 deletions

2
.gitignore vendored
View file

@ -66,6 +66,6 @@ compile_commands.json
.cache/*
.#*
*.*~
launch.json
#Code analysis
softwipe*.*

View file

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

View file

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

21
header/dyn_buf.h Normal file
View file

@ -0,0 +1,21 @@
#ifndef _DYN_BUF_H_
#define _DYN_BUF_H_
#include <stdint.h>
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

View file

@ -6,8 +6,10 @@
#include <stdlib.h>
#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

View file

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

43
src/dyn_buf.c Normal file
View file

@ -0,0 +1,43 @@
#include "../header/dyn_buf.h"
#include <stdlib.h>
#include <string.h>
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;
}

View file

@ -1,14 +1,53 @@
#include "../header/execute.h"
#include <stdio.h>
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:

View file

@ -1,4 +1,5 @@
#include "../header/helpers.h"
#include <stdio.h>
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;
}

View file

@ -2,6 +2,7 @@
// Read an EIPA image and print it out in Binary form
#include <stdint.h>
#ifndef WIN_COMPILE
#include <argp.h>
#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;
}

Binary file not shown.