version bump 0.0.2, dynamic memory, minor debugging capabilities
This commit is contained in:
parent
b42d5e7e67
commit
84953ccabd
9 changed files with 214 additions and 20 deletions
10
design.txt
10
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
|
||||
|
|
|
@ -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
21
header/dyn_buf.h
Normal 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
|
|
@ -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
|
||||
|
|
|
@ -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
43
src/dyn_buf.c
Normal 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;
|
||||
}
|
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
54
src/main.c
54
src/main.c
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue