1
0
mirror of https://github.com/pevans/erc-c.git synced 2024-12-21 23:29:16 +00:00
erc-c/src/vm_segment.c

207 lines
5.3 KiB
C
Raw Normal View History

2017-12-02 19:05:53 +00:00
/*
* vm_segment.c
*
2017-12-09 04:12:31 +00:00
* The functions here allow you to allocate generic blocks of memory (or
* "segments") for use anywhere else in the software. They can be used
* to represent machine memory, removable media (like floppy disks),
* etc.
2017-12-02 19:05:53 +00:00
*/
#include <stdio.h>
2017-11-22 05:24:51 +00:00
#include <stdlib.h>
#include <string.h>
#include "log.h"
#include "vm_segment.h"
2017-12-02 19:05:53 +00:00
/*
* Create a new segment, such that it contains a number of bytes indicated
* by `size`.
*/
2017-11-22 05:24:51 +00:00
vm_segment *
vm_segment_create(size_t size)
{
2017-12-02 19:05:53 +00:00
vm_segment *segment;
2017-11-22 05:24:51 +00:00
// Allocate memory for the current memory segment.
2017-12-02 19:05:53 +00:00
segment = malloc(sizeof(vm_segment));
2017-11-22 05:24:51 +00:00
// Ack! We couldn't get the memory we wanted. Let's bail.
2017-12-02 19:05:53 +00:00
if (segment == NULL) {
log_critical("Couldn't allocate enough space for vm_segment");
return NULL;
}
segment->memory = malloc(sizeof(vm_8bit) * size);
if (segment->memory == NULL) {
2017-11-22 05:24:51 +00:00
log_critical("Couldn't allocate enough space for vm_segment");
return NULL;
}
// We should zero out memory and make explicit that any new segment
// begins life in that state.
memset(segment->memory, 0, sizeof(vm_8bit) * size);
segment->read_table = malloc(sizeof(vm_segment_read_fn) * size);
if (segment->read_table == NULL) {
log_critical("Couldn't allocate enough space for segment read_table");
return NULL;
}
segment->write_table = malloc(sizeof(vm_segment_write_fn) * size);
if (segment->write_table == NULL) {
log_critical("Couldn't allocate enough space for segment write_table");
return NULL;
}
2017-12-12 20:35:13 +00:00
// Let's NULL-out the read and write tables. If we don't do so, they
// may have some bits of garbage in it, and could cause the
// read/write mapper code to attempt to a run a function with
// garbage. We could have undefined garbage! We can only properly
// work with defined garbage.
memset(segment->read_table, (int)NULL, sizeof(vm_segment_read_fn) * size);
memset(segment->write_table, (int)NULL, sizeof(vm_segment_write_fn) * size);
2017-12-02 19:05:53 +00:00
segment->size = size;
2017-11-22 05:24:51 +00:00
2017-12-02 19:05:53 +00:00
return segment;
2017-11-22 05:24:51 +00:00
}
2017-12-02 19:05:53 +00:00
/*
* Free the memory consumed by a given segment.
*/
2017-11-22 05:24:51 +00:00
void
2017-12-02 19:05:53 +00:00
vm_segment_free(vm_segment *segment)
{
free(segment->memory);
free(segment);
}
/*
* Set the byte in `segment`, at `index`, to the given `value`. Our
* bounds-checking here will _crash_ the program if we are
* out-of-bounds.
*/
2017-12-09 21:16:56 +00:00
int
2017-12-02 19:05:53 +00:00
vm_segment_set(vm_segment *segment, size_t index, vm_8bit value)
2017-11-22 05:24:51 +00:00
{
// Some bounds checking.
if (!vm_segment_bounds_check(segment, index)) {
log_critical(
"Attempt to set segment index (%d) greater than bounds (%d)",
index,
segment->size);
2017-12-09 21:16:56 +00:00
return ERR_OOB;
2017-11-22 05:24:51 +00:00
}
2017-12-12 21:00:47 +00:00
// Check if we have a write mapper
if (segment->write_table[index]) {
segment->write_table[index](segment, index, value);
return OK;
}
2017-11-22 05:24:51 +00:00
segment->memory[index] = value;
2017-12-09 21:16:56 +00:00
return OK;
2017-11-22 05:24:51 +00:00
}
2017-12-02 19:05:53 +00:00
/*
* Return the byte in `segment` at the given `index` point. Our
* bounds-checking will _crash_ the program if an index is requested out
* of bounds.
*/
vm_8bit
2017-11-22 05:24:51 +00:00
vm_segment_get(vm_segment *segment, size_t index)
{
if (!vm_segment_bounds_check(segment, index)) {
log_critical(
"Attempt to set segment index (%d) greater than bounds (%d)",
index,
segment->size);
// See vm_segment_set() for a justification of this behavior.
exit(1);
}
2017-12-12 21:00:47 +00:00
// We may have a read mapper for this address
if (segment->read_table[index]) {
return segment->read_table[index](segment, index);
}
2017-11-22 05:24:51 +00:00
return segment->memory[index];
}
2017-12-02 19:05:53 +00:00
/*
* Copy a set of bytes from `src` (at `src_index`) to `dest` (at
2017-12-12 20:59:00 +00:00
* `dest_index`), such that the range is `length` bytes long. Note that
* this function presently bypasses our mapper function code... we may
* need to implement such in the future.
2017-12-02 19:05:53 +00:00
*/
2017-12-09 21:16:56 +00:00
int
2017-12-02 19:05:53 +00:00
vm_segment_copy(vm_segment *dest,
vm_segment *src,
2017-11-22 05:24:51 +00:00
size_t dest_index,
2017-12-02 19:05:53 +00:00
size_t src_index,
2017-11-22 05:24:51 +00:00
size_t length)
{
if (src_index + length >= src->size) {
log_critical(
"Attempt to copy beyond bounds of vm_segment (%d + %d >= %d)",
src_index,
length,
src->size);
2017-12-09 21:16:56 +00:00
return ERR_OOB;
2017-11-22 05:24:51 +00:00
}
if (dest_index + length >= dest->size) {
log_critical(
"Attempt to copy beyond bounds of vm_segment (%d + %d >= %d)",
dest_index,
length,
dest->size);
2017-12-09 21:16:56 +00:00
return ERR_OOB;
2017-11-22 05:24:51 +00:00
}
memcpy(dest->memory + dest_index,
src->memory + src_index,
length * sizeof(src->memory[src_index]));
2017-12-09 21:16:56 +00:00
return OK;
2017-11-22 05:24:51 +00:00
}
/*
* Set the read mapper for a given address. We'll use this function
* instead of the normal logic on a get for that address.
*/
int
vm_segment_read_map(vm_segment *segment,
size_t addr,
vm_segment_read_fn fn)
{
if (addr >= segment->size) {
return ERR_OOB;
}
segment->read_table[addr] = fn;
return OK;
}
/*
* Here we set the map function for a given address to use on writes,
* which is to say, when we use the `vm_segment_set()` function.
*/
int
vm_segment_write_map(vm_segment *segment,
size_t addr,
vm_segment_write_fn fn)
{
if (addr >= segment->size) {
return ERR_OOB;
}
segment->write_table[addr] = fn;
return OK;
}