#include "glulxe.h"
unsigned char *memmap = NULL;
unsigned char *stack = NULL;
glui32 ramstart;
glui32 endgamefile;
glui32 origendmem;
glui32 stacksize;
glui32 startfuncaddr;
glui32 checksum;
glui32 stackptr;
glui32 frameptr;
glui32 pc;
glui32 valstackbase;
glui32 localsbase;
glui32 endmem;
glui32 prevpc;
void setup_vm()
{
unsigned char buf[4 * 7];
int res;
pc = 0;
prevpc = 0;
fseek(gamefile, 8, SEEK_SET);
res = fread(buf, 1, 4 * 7, gamefile);
if (res != 4 * 7) {
fatal_error("The game file header is too short.");
}
ramstart = Read4(buf+0);
endgamefile = Read4(buf+4);
origendmem = Read4(buf+8);
stacksize = Read4(buf+12);
startfuncaddr = Read4(buf+16);
checksum = Read4(buf+24);
if ((ramstart & 0xFF)
|| (endgamefile & 0xFF)
|| (origendmem & 0xFF)
|| (stacksize & 0xFF)) {
fatal_error("One of the segment boundaries in the header is not "
"256-byte aligned.");
}
if (ramstart < 0x100 || endgamefile < ramstart || origendmem < endgamefile) {
fatal_error("The segment boundaries in the header are in an impossible "
"order.");
}
if (stacksize < 0x100) {
fatal_error("The stack size in the header is too small.");
}
endmem = origendmem;
memmap = (unsigned char *)glulx_malloc(origendmem);
if (!memmap) {
fatal_error("Unable to allocate Glulx memory space.");
}
stack = (unsigned char *)glulx_malloc(stacksize);
if (!stack) {
glulx_free(memmap);
memmap = NULL;
fatal_error("Unable to allocate Glulx stack space.");
}
init_operands();
vm_restart();
}
void finalize_vm()
{
if (memmap) {
glulx_free(memmap);
memmap = NULL;
}
if (stack) {
glulx_free(stack);
stack = NULL;
}
}
void vm_restart()
{
glui32 lx;
int res;
int bufpos;
char buf[0x100];
heap_clear();
lx = change_memsize(origendmem, FALSE);
if (lx)
fatal_error("Memory could not be reset to its original size.");
rewind(gamefile);
bufpos = 0x100;
for (lx=0; lx<endgamefile; lx++) {
if (bufpos >= 0x100) {
int count = fread(buf, 1, 0x100, gamefile);
if (count != 0x100) {
fatal_error("The game file ended unexpectedly.");
}
bufpos = 0;
}
res = buf[bufpos++];
memmap[lx] = res;
}
for (lx=endgamefile; lx<origendmem; lx++) {
memmap[lx] = 0;
}
stackptr = 0;
frameptr = 0;
pc = 0;
prevpc = 0;
valstackbase = 0;
localsbase = 0;
enter_function(startfuncaddr, 0, NULL);
}
glui32 change_memsize(glui32 newlen, int internal)
{
long lx;
unsigned char *newmemmap;
if (newlen == endmem)
return 0;
#ifdef FIXED_MEMSIZE
return 1;
#else
if ((!internal) && heap_is_active())
fatal_error("Cannot resize Glulx memory space while heap is active.");
if (newlen < origendmem)
fatal_error("Cannot resize Glulx memory space smaller than it started.");
if (newlen & 0xFF)
fatal_error("Can only resize Glulx memory space to a 256-byte boundary.");
newmemmap = (unsigned char *)glulx_realloc(memmap, newlen);
if (!newmemmap) {
return 1;
}
memmap = newmemmap;
if (newlen > endmem) {
for (lx=endmem; lx<newlen; lx++) {
memmap[lx] = 0;
}
}
endmem = newlen;
return 0;
#endif
}
glui32 *pop_arguments(glui32 count, glui32 addr)
{
int ix;
glui32 argptr;
glui32 *array;
if (count & 0x80000000)
fatal_error("Argument count is negative");
#define MAXARGS (32)
static glui32 statarray[MAXARGS];
static glui32 *dynarray = NULL;
static glui32 dynarray_size = 0;
if (count == 0)
return NULL;
if (count <= MAXARGS) {
array = statarray;
}
else {
if (!dynarray) {
dynarray_size = count+8;
dynarray = glulx_malloc(sizeof(glui32) * dynarray_size);
if (!dynarray)
fatal_error("Unable to allocate function arguments.");
array = dynarray;
}
else {
if (dynarray_size >= count) {
array = dynarray;
}
else {
dynarray_size = count+8;
dynarray = glulx_realloc(dynarray, sizeof(glui32) * dynarray_size);
if (!dynarray)
fatal_error("Unable to reallocate function arguments.");
array = dynarray;
}
}
}
if (!addr) {
if (stackptr < valstackbase+4*count)
fatal_error("Stack underflow in arguments.");
stackptr -= 4*count;
for (ix=0; ix<count; ix++) {
argptr = stackptr+4*((count-1)-ix);
array[ix] = Stk4(argptr);
}
}
else {
for (ix=0; ix<count; ix++) {
array[ix] = Mem4(addr);
addr += 4;
}
}
return array;
}
void verify_address(glui32 addr, glui32 count)
{
if (addr >= endmem)
fatal_error_i("Memory access out of range", addr);
if (count > 1) {
addr += (count-1);
if (addr >= endmem)
fatal_error_i("Memory access out of range", addr);
}
}
void verify_address_write(glui32 addr, glui32 count)
{
if (addr < ramstart)
fatal_error_i("Memory write to read-only address", addr);
if (addr >= endmem)
fatal_error_i("Memory access out of range", addr);
if (count > 1) {
addr += (count-1);
if (addr >= endmem)
fatal_error_i("Memory access out of range", addr);
}
}
void verify_address_stack(glui32 stackpos, glui32 count)
{
if (stackpos >= stacksize || stackpos+count > stacksize)
trap(TRAP_STACK_EXHAUSTED);
switch (count) {
case 1:
break;
case 2:
if (stackpos & 1)
fatal_error_i("Unaligned stack access (2)", stackpos);
break;
case 4:
if (stackpos & 3)
fatal_error_i("Unaligned stack access (4)", stackpos);
break;
default:
fatal_error_i("Invalid stack access size", count);
}
}
void verify_array_addresses(glui32 addr, glui32 count, glui32 size)
{
glui32 bytecount;
if (addr >= endmem)
fatal_error_i("Memory access out of range", addr);
if (count == 0)
return;
bytecount = count*size;
if (bytecount < count)
fatal_error_i("Memory access way too long", addr);
if (bytecount > endmem || addr+bytecount < addr)
fatal_error_i("Memory access much too long", addr);
if (addr+bytecount > endmem)
fatal_error_i("Memory access too long", addr);
}