/* memory.c */

#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include "project.h"

/* Memory file */
static unsigned* memory = NULL;
static unsigned memory_size = 0;
static int virgin = 1;

/* Cleanup function. */
static void cleanup(void)
{
	free(memory);
}

/* Returns the value of memory address 'addr'. */
unsigned mem_read(unsigned addr)
{
	addr &= 0xffff;
	return addr >= memory_size ? 0xdeadbeef : memory[addr];
}

/* Ensures that memory is large enough to hold this address. */
static void grow_memory(int min_size)
{
	int i;
	int old_memory_size = memory_size;
	if (memory_size >= min_size) return;

	memory_size = memory_size == 0 ? 64 : memory_size * 2;
	if (memory_size < min_size) memory_size = min_size;
	memory = realloc(memory, memory_size * 4);
	if (memory == NULL) die("Out of memory\n");
	if (virgin) {
		virgin = 0;
		if (atexit(cleanup) != 0) {
			perror("atexit");
			exit(EXIT_FAILURE);
		}
	}
	for (i = old_memory_size; i < memory_size; i++) memory[i] = 0xdeadbeef;
}

/* Sets the value of memory address 'addr'. */
void mem_write(unsigned addr, unsigned val)
{
	addr &= 0xffff;
	grow_memory(addr);
	memory[addr] = val & 0xffffffff;
}

/* Loads a memory image from a file. */
void mem_load_image(char* filename)
{
	struct stat stat_buf;
	int file_size;
	FILE* handle;
	int i;

	/* Determine how many words are in the image file */
	if (stat(filename, &stat_buf) == -1) {
		perror("stat");
		exit(EXIT_FAILURE);
	}
	file_size = stat_buf.st_size / 4;
	if (file_size > 0xffff) die("Memory image too large\n");

	/* Make sure there's enough memory handy */
	grow_memory(file_size);

	/* Open the file */
	handle = fopen(filename, "rb");
	if (handle == NULL) {
		perror("fopen");
		exit(EXIT_FAILURE);
	}

	/* Load the image */
	for (i = 0; i < file_size; i++) {
		unsigned char buf[4];
		if (fread(buf, 4, 1, handle) != 1) {
			perror("fread");
			exit(EXIT_FAILURE);
		}
		memory[i] = (buf[0] << 24) | (buf[1] << 16) |
				(buf[2] << 8) | (buf[3]);
	}

	/* Clean up */
	if (fclose(handle) == EOF) {
		perror("fclose");
		exit(EXIT_FAILURE);
	}
}

/* EOF */

