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
|
XXXXYYYYYYYYYYYYYYYYYYYYYYYYYYYY
|
||||||
X: Instruction Opcode (4bit)
|
X: Instruction Opcode (4bit)
|
||||||
Y: Memory Adress (28bit)
|
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
|
// Maximum Memory Adresses
|
||||||
#define MAX_MEMORY 268435456
|
#define MAX_MEMORY 268435456
|
||||||
|
|
||||||
|
#define MAX_BREAKPOINTS 1000
|
||||||
|
|
||||||
// Intitial size and grow size for dynamic buffers
|
// Intitial size and grow size for dynamic buffers
|
||||||
#define TABLE_INIT_SIZE 50
|
#define TABLE_INIT_SIZE 10
|
||||||
#define TABLE_GROW_SIZE 30
|
#define TABLE_GROW_SIZE 5
|
||||||
|
|
||||||
#define VER_MAJOR "0"
|
#define VER_MAJOR "0"
|
||||||
#define VER_MINOR "0"
|
#define VER_MINOR "0"
|
||||||
#define VER_PATCH "1"
|
#define VER_PATCH "2"
|
||||||
|
|
||||||
// alpha, beta or stable
|
// alpha, beta or stable
|
||||||
#define TAG "alpha"
|
#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 <stdlib.h>
|
||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
#include "dyn_buf.h"
|
||||||
|
#include "helpers.h"
|
||||||
|
|
||||||
// Executes given memory
|
// Executes given memory
|
||||||
int execute(uint32_t *memory);
|
int execute(dynamic_buffer_t *memory, int *breakpoints, int breakpoint_count);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -6,4 +6,13 @@
|
||||||
|
|
||||||
void print_target_code(uint32_t *target_code);
|
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
|
#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 "../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_ptr = 0;
|
||||||
uint32_t instr_curr;
|
uint32_t instr_curr;
|
||||||
uint32_t cpu_register = 0;
|
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
|
// Debug handling
|
||||||
uint32_t adress = memory[instr_ptr] & ADRESS_BITS; // Discard the first 4 bits
|
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
|
// This check should be unnecessary when using the official EIPA Assembler, but
|
||||||
// some people might want to hand-craft their EIPA Images
|
// some people might want to hand-craft their EIPA Images
|
||||||
|
|
@ -24,26 +63,28 @@ int execute(uint32_t *memory)
|
||||||
case INSTR_INP:
|
case INSTR_INP:
|
||||||
{
|
{
|
||||||
printf("Input: ");
|
printf("Input: ");
|
||||||
int check = scanf("%u", memory + adress);
|
uint32_t input;
|
||||||
fflush(stdin);
|
int check = scanf("%u", &input);
|
||||||
|
clean_in();
|
||||||
if(!check)
|
if(!check)
|
||||||
{
|
{
|
||||||
printf("ERROR: Received invalid input on a INP instruction\n");
|
printf("ERROR: Received invalid input on a INP instruction\n");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
write_mem(adress, input, memory);
|
||||||
instr_ptr++;
|
instr_ptr++;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case INSTR_OUT:
|
case INSTR_OUT:
|
||||||
printf("%u\n", memory[adress]);
|
printf("%u\n", read_mem(adress, memory));
|
||||||
instr_ptr++;
|
instr_ptr++;
|
||||||
break;
|
break;
|
||||||
case INSTR_LDA:
|
case INSTR_LDA:
|
||||||
cpu_register = memory[adress];
|
cpu_register = read_mem(adress, memory);
|
||||||
instr_ptr++;
|
instr_ptr++;
|
||||||
break;
|
break;
|
||||||
case INSTR_STA:
|
case INSTR_STA:
|
||||||
memory[adress] = cpu_register;
|
write_mem(adress, cpu_register, memory);
|
||||||
instr_ptr++;
|
instr_ptr++;
|
||||||
break;
|
break;
|
||||||
case INSTR_INC:
|
case INSTR_INC:
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
#include "../header/helpers.h"
|
#include "../header/helpers.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
void print_target_code(uint32_t *target_code)
|
void print_target_code(uint32_t *target_code)
|
||||||
{
|
{
|
||||||
|
|
@ -14,3 +15,28 @@ void print_target_code(uint32_t *target_code)
|
||||||
instruction_index++;
|
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
|
// Read an EIPA image and print it out in Binary form
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
#ifndef WIN_COMPILE
|
#ifndef WIN_COMPILE
|
||||||
#include <argp.h>
|
#include <argp.h>
|
||||||
#else
|
#else
|
||||||
|
|
@ -13,8 +14,9 @@
|
||||||
|
|
||||||
#include "../header/argp_commons.h"
|
#include "../header/argp_commons.h"
|
||||||
#include "../header/common.h"
|
#include "../header/common.h"
|
||||||
#include "../header/helpers.h"
|
#include "../header/dyn_buf.h"
|
||||||
#include "../header/execute.h"
|
#include "../header/execute.h"
|
||||||
|
#include "../header/helpers.h"
|
||||||
|
|
||||||
const char *argp_program_version =
|
const char *argp_program_version =
|
||||||
VER_MAJOR "." VER_MINOR "." VER_PATCH " || " TAG;
|
VER_MAJOR "." VER_MINOR "." VER_PATCH " || " TAG;
|
||||||
|
|
@ -25,6 +27,9 @@ const char *argp_program_bug_address = EIPA_BUG_ADRESS;
|
||||||
struct cmd_arguments
|
struct cmd_arguments
|
||||||
{
|
{
|
||||||
char *input_file;
|
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
|
// 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;
|
struct cmd_arguments *save_arguments = state->input;
|
||||||
|
|
||||||
|
// Return value for sscanf
|
||||||
|
int scan_count;
|
||||||
|
|
||||||
switch (key)
|
switch (key)
|
||||||
{
|
{
|
||||||
case ARGP_KEY_ARG:
|
case ARGP_KEY_ARG:
|
||||||
|
|
@ -48,6 +56,22 @@ static error_t parser_function(int key, char *arg, struct argp_state *state)
|
||||||
{
|
{
|
||||||
argp_usage(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;
|
break;
|
||||||
default:
|
default:
|
||||||
return ARGP_ERR_UNKNOWN;
|
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
|
// 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
|
// Contains the neccesary stuff for argp
|
||||||
static struct argp argument_parser =
|
static struct argp argument_parser =
|
||||||
|
|
@ -71,6 +104,7 @@ int main(int argc, char **argv)
|
||||||
// Parse CLI Arguments
|
// Parse CLI Arguments
|
||||||
// set defaults
|
// set defaults
|
||||||
struct cmd_arguments arguments;
|
struct cmd_arguments arguments;
|
||||||
|
arguments.breakpoint_count = 0;
|
||||||
arguments.input_file = "";
|
arguments.input_file = "";
|
||||||
|
|
||||||
if (argp_parse(&argument_parser, argc, argv, 0, 0, &arguments) != 0)
|
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");
|
FILE *input = fopen(arguments.input_file, "r");
|
||||||
if (input == NULL)
|
if (input == NULL)
|
||||||
return -1;
|
return -1;
|
||||||
|
uint32_t input_instr_count = fsize(input, sizeof(uint32_t));
|
||||||
|
|
||||||
// Allocate the memory of the Emulated device
|
// 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
|
// 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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue