MuckyFoot-UrbanChaos/MuckyBasic/vm.cpp
2017-05-20 11:14:17 +10:00

4436 lines
80 KiB
C++

//
// The virtual machine to run our basic programs.
//
#include "always.h"
#include "console.h"
#include "key.h"
#include "mem.h"
#include "ml.h"
#include "sysvar.h"
//
// Instruction memory.
//
SLONG *VM_code;
SLONG VM_code_max;
SLONG VM_code_upto;
SLONG *VM_code_pointer; // The instruction we are executing...
//
// The stack.
//
ML_Data *VM_stack;
SLONG VM_stack_max;
ML_Data *VM_stack_top; // The top of the stack
ML_Data *VM_stack_base; // The current stack frame
//
// The globals.
//
ML_Data *VM_global;
SLONG VM_global_max;
//
// The static data table.
//
UBYTE *VM_data;
SLONG VM_data_max;
//
// Extra globals we add onto the end of the program.
//
#define VM_EXTRA_GLOBAL_KEY(key) (VM_global_extra + (key))
#define VM_EXTRA_GLOBAL_INKEY (VM_global_extra + 256)
#define VM_EXTRA_GLOBAL_NUMBER 257
SLONG VM_global_extra; // The index of the first extra global.
//
// OS_ticks() the last time we did a flip.
//
SLONG VM_flip_tick;
//
// Checks for sufficient stack space to push on another
// item of data.
//
#define VM_CHECK_STACK_PUSH() {ASSERT(WITHIN(VM_stack_top, VM_stack, VM_stack + VM_stack_max - 1));}
//
// Pops two items off the stack (just decreases the stack pointer!)
//
#define VM_POP_STACK(n) {VM_stack_top -= (n); ASSERT(WITHIN(VM_stack_top, VM_stack, VM_stack + VM_stack_max - 1));}
//
// Frees memory used by the given bit of data.
//
void VM_data_free(ML_Data md)
{
SLONG i;
switch(md.type)
{
case ML_TYPE_STRVAR:
MEM_free(md.strvar);
break;
case ML_TYPE_STRUCTURE:
//
// Free all the fields.
//
for (i = 0; i < md.structure->num_fields; i++)
{
VM_data_free(md.structure->field[i].data);
}
//
// Now free the structure.
//
MEM_free(md.structure);
break;
case ML_TYPE_ARRAY:
//
// Free all the elements of the array.
//
for (i = 0; i < md.array->length; i++)
{
VM_data_free(md.array->data[i]);
}
MEM_free(md.array->data);
//
// Now free the array.
//
MEM_free(md.array);
break;
case ML_TYPE_TEXTURE:
md.lt->ref_count -= 1;
if (md.lt->ref_count == 0)
{
//
// Free the texture.
//
LL_free_texture(md.lt);
}
break;
case ML_TYPE_BUFFER:
md.lb->ref_count -= 1;
if (md.lb->ref_count == 0)
{
//
// Free the buffer.
//
LL_free_buffer(md.lb);
}
break;
case ML_TYPE_MATRIX:
MEM_free(md.matrix);
break;
case ML_TYPE_VECTOR:
MEM_free(md.vector);
break;
default:
break;
}
}
//
// Creates a copy of the given bit of data.
//
ML_Data VM_data_copy(ML_Data original)
{
ML_Data ans;
switch(original.type)
{
//
// Types that need extra allocation.
//
case ML_TYPE_STRVAR:
{
SLONG length = MEM_block_size(original.strvar);
ans.type = ML_TYPE_STRVAR;
ans.strvar = (CBYTE *) MEM_alloc(length);
memcpy(ans.strvar, original.strvar, length);
}
return ans;
case ML_TYPE_STRUCTURE:
{
//
// Create another copy of the structure.
//
SLONG length = sizeof(ML_Structure) + original.structure->num_fields * sizeof(ML_Field);
ans.type = ML_TYPE_STRUCTURE;
ans.structure = (ML_Structure *) MEM_alloc(length);
SLONG i;
ans.structure->num_fields = original.structure->num_fields;
for (i = 0; i < ans.structure->num_fields; i++)
{
ans.structure->field[i].field_id = original.structure->field[i].field_id;
ans.structure->field[i].data = VM_data_copy(original.structure->field[i].data);
}
}
return ans;
case ML_TYPE_ARRAY:
{
//
// Create another copy of the array.
//
SLONG length = sizeof(ML_Array) + original.array->num_dimensions * sizeof(ML_Dimension);
ans.type = ML_TYPE_ARRAY;
ans.array = (ML_Array *) MEM_alloc(length);
ans.array->data = (ML_Data *) MEM_alloc(sizeof(ML_Data) * original.array->length);
ans.array->length = original.array->length;
ans.array->num_dimensions = original.array->num_dimensions;
SLONG i;
for (i = 0; i < original.array->num_dimensions; i++)
{
ans.array->dimension[i] = original.array->dimension[i];
}
for (i = 0; i < original.array->length; i++)
{
ans.array->data[i] = VM_data_copy(original.array->data[i]);
}
}
return ans;
case ML_TYPE_TEXTURE:
//
// Increase the reference count of the texture.
//
original.lt->ref_count += 1;
return original;
case ML_TYPE_BUFFER:
//
// Increase the reference count of the buffer.
//
original.lb->ref_count += 1;
return original;
case ML_TYPE_POINTER:
//
// Push on the derefenced value.
//
return VM_data_copy(*original.data);
case ML_TYPE_MATRIX:
ans.type = ML_TYPE_MATRIX;
ans.matrix = (ML_Matrix *) MEM_alloc(sizeof(ML_Matrix));
*(ans.matrix) = *original.matrix;
return ans;
case ML_TYPE_VECTOR:
ans.type = ML_TYPE_VECTOR;
ans.vector = (ML_Vector *) MEM_alloc(sizeof(ML_Vector));
*(ans.vector) = *original.vector;
return ans;
//
// Simple types.
//
default:
return original;
}
}
//
// Convert the given bit of data to a string. If the input is a
// string variable or string constant then the original is returned,
// not a copy.
//
void VM_convert_to_string(ML_Data *original)
{
ML_Data ans;
ans.type = ML_TYPE_STRVAR;
switch(original->type)
{
case ML_TYPE_UNDEFINED:
ans.strvar = (CBYTE *) MEM_alloc(16); // Enough to hold the string "<UNDEFINED>"
memcpy(ans.strvar, "<UNDEFINED>", 12);
break;
case ML_TYPE_SLUMBER:
ans.strvar = (CBYTE *) MEM_alloc(16); // Enough to hold the number -2 ^ 32 and a NULL.
sprintf(ans.strvar, "%d", original->slumber);
break;
case ML_TYPE_FLUMBER:
ans.strvar = (CBYTE *) MEM_alloc(32); // Enough to hold the number -2 ^ 32 and a NULL.
sprintf(ans.strvar, "%f", original->flumber);
break;
case ML_TYPE_STRCONST:
case ML_TYPE_STRVAR:
//
// Should we be calling this function?
//
return;
case ML_TYPE_BOOLEAN:
ans.strvar = (CBYTE *) MEM_alloc(6); // Enough to hold the string "TRUE" or "FALSE"
if (original->boolean)
{
memcpy(ans.strvar, "TRUE", 5);
}
else
{
memcpy(ans.strvar, "FALSE", 6);
}
break;
default:
ASSERT(0);
break;
}
*original = ans;
return;
}
//
// Returns the string held by the given string variable.
//
CBYTE *VM_get_string(ML_Data string)
{
switch(string.type)
{
case ML_TYPE_STRCONST:
ASSERT(WITHIN(string.strconst, 0, VM_data_max - 2));
return (CBYTE *) (VM_data + string.strconst);
case ML_TYPE_STRVAR:
return string.strvar;
default:
ASSERT(0);
return NULL;
}
}
//
// Makes VM_stack_top[0] and VM_stack_top[1] have the same type.
// VM_stack_top[0] must be a FLUMBER or a SLUMBER.
//
void VM_convert_stack_top_to_same_numerical_type()
{
switch(VM_stack_top[0].type)
{
case ML_TYPE_SLUMBER:
if (VM_stack_top[1].type == ML_TYPE_FLUMBER)
{
//
// Convert first argument to a float.
//
VM_stack_top[0].type = ML_TYPE_FLUMBER;
VM_stack_top[0].flumber = float(VM_stack_top[0].slumber);
}
break;
case ML_TYPE_FLUMBER:
if (VM_stack_top[1].type == ML_TYPE_SLUMBER)
{
//
// Convert second argument to a float.
//
VM_stack_top[1].type = ML_TYPE_FLUMBER;
VM_stack_top[1].flumber = float(VM_stack_top[1].slumber);
}
break;
default:
ASSERT(0);
break;
}
}
//
// Makes sure the array is big enough to for the indices
// given in the md[] array (an array of SLUMBERs)
//
void VM_grow_array(ML_Array *ma, ML_Data *md)
{
SLONG i;
SLONG j;
SLONG bigger_index;
SLONG bigger_length;
#define VM_MAX_DIMENSIONS 64
ASSERT(WITHIN(ma->num_dimensions, 1, VM_MAX_DIMENSIONS));
SLONG bigger_size [VM_MAX_DIMENSIONS];
SLONG bigger_stride[VM_MAX_DIMENSIONS];
SLONG index [VM_MAX_DIMENSIONS];
//
// Build the array of the new sizes of each dimension.
//
for (i = 0; i < ma->num_dimensions; i++)
{
if (md[i].slumber > ma->dimension[i].size)
{
//
// Double the dimension of the array.
//
bigger_size[i] = ma->dimension[i].size * 2;
if (md[i].slumber > bigger_size[i])
{
//
// Doubling wasn't good enough! Make it as big as need be.
//
bigger_size[i] = md[i].slumber;
}
}
else
{
bigger_size[i] = ma->dimension[i].size;
}
}
//
// The new stride in each dimension.
//
for (i = 0; i < ma->num_dimensions; i++)
{
bigger_stride[i] = 1;
for (j = i + 1; j < ma->num_dimensions; j++)
{
bigger_stride[i] *= bigger_size[j];
}
}
//
// How long is the new array?
//
bigger_length = bigger_size[0];
for (i = 1; i < ma->num_dimensions; i++)
{
bigger_length *= bigger_size[1];
}
//
// Allocate a new area of memory.
//
ML_Data *bigger_data = (ML_Data *) MEM_alloc(sizeof(ML_Data) * bigger_length);
if (ma->num_dimensions == 1)
{
//
// Easy to copy over data...
//
memcpy(bigger_data, ma->data, sizeof(ML_Data) * ma->length);
memset(bigger_data + ma->length, 0, sizeof(ML_Data) * (bigger_length - ma->length));
}
else
{
//
// Nightmare to copy over data. Initialise new memory to <UNDEFINED>
//
memset(bigger_data, 0, sizeof(ML_Data) * bigger_length);
//
// Clear the index array.
//
for (i = 0; i < ma->num_dimensions; i++)
{
index[i] = 0;
}
//
// Copy over all old data in a horribly inefficient manner.
//
for (i = 0; i < ma->length; i++)
{
//
// What's the new index of this element?
//
bigger_index = 0;
for (j = 0; j < ma->num_dimensions; j++)
{
bigger_index += bigger_stride[j] * index[j];
}
//
// Copy over this element.
//
bigger_data[bigger_index] = ma->data[i];
//
// Update our indices...
//
for (j = ma->num_dimensions - 1; j >= 1; j--)
{
index[j] += 1;
if (index[j] >= ma->dimension[j].size)
{
//
// Roll-over.
//
index[j ] = 0;
index[j - 1] += 1;
}
}
}
}
//
// Free old memory.
//
MEM_free(ma->data);
ma->data = bigger_data;
ma->length = bigger_length;
for (i = 0; i < ma->num_dimensions; i++)
{
ma->dimension[i].size = bigger_size[i];
ma->dimension[i].stride = bigger_stride[i];
}
}
//
// Complicated instructions that needn't be fast
// get their own function.
//
void VM_do_texture()
{
VM_POP_STACK(2);
//
// We don't support USER textures now, so the
// first argument must be the filename and the
// second should be UNDEFINED.
//
if (VM_stack_top[0].type != ML_TYPE_STRCONST &&
VM_stack_top[0].type != ML_TYPE_STRVAR)
{
//
// ERROR!
//
ASSERT(0);
}
if (VM_stack_top[1].type != ML_TYPE_UNDEFINED)
{
//
// ERROR!
//
ASSERT(0);
}
//
// Create the texture.
//
ML_Data texture;
texture.type = ML_TYPE_TEXTURE;
texture.lt = LL_create_texture(VM_get_string(VM_stack_top[0]));
//
// Free the arguments.
//
VM_data_free(VM_stack_top[0]);
VM_data_free(VM_stack_top[1]);
VM_stack_top[0] = texture;
VM_stack_top += 1;
}
void VM_do_buffer()
{
SLONG i;
SLONG j;
SLONG num_verts;
SLONG num_indices;
ULONG found;
SLONG slumber;
float flumber;
ML_Data *vert_array;
ML_Structure *vert_struct;
ML_Field *field;
ML_Data *index_array;
LL_Tlvert *tl;
UWORD *index;
VM_POP_STACK(4);
//
// The first argument should be an 1d array of structures!
//
if (VM_stack_top[0].type != ML_TYPE_ARRAY)
{
//
// ERROR!
//
ASSERT(0);
}
else
if (VM_stack_top[0].array->num_dimensions != 1)
{
//
// ERROR!
//
ASSERT(0);
}
//
// The next argument should be the number of vertices.
//
if (VM_stack_top[1].type == ML_TYPE_SLUMBER)
{
num_verts = VM_stack_top[1].slumber;
}
else
if (VM_stack_top[1].type == ML_TYPE_FLUMBER)
{
float integer;
float fraction;
fraction = modff(VM_stack_top[1].flumber, &integer);
if (fraction != 0.0F)
{
//
// ERROR! A fractional number of verts?
//
ASSERT(0);
}
num_verts = ftol(integer);
}
//
// The next two arguments are optional depending on whether
// this is a triangle list or an indexed list.
//
if (VM_stack_top[3].type == ML_TYPE_UNDEFINED)
{
//
// Ok... just means no indices.
//
num_indices = 0;
}
else
if (VM_stack_top[3].type == ML_TYPE_SLUMBER)
{
num_indices = VM_stack_top[3].slumber;
}
else
if (VM_stack_top[3].type == ML_TYPE_FLUMBER)
{
float integer;
float fraction;
fraction = modff(VM_stack_top[1].flumber, &integer);
if (fraction != 0.0F)
{
//
// ERROR! Oh dear! A fractional number of indices!
//
ASSERT(0);
}
num_indices = ftol(integer);
}
else
{
//
// ERROR!
//
ASSERT(0);
}
if (VM_stack_top[2].type == ML_TYPE_ARRAY)
{
//
// Should be an 1d array of numbers.
//
if (VM_stack_top[2].array->num_dimensions != 1)
{
//
// ERROR!
//
ASSERT(0);
}
if (num_indices == 0 || num_indices % 3 != 0)
{
//
// ERROR!
//
ASSERT(0);
}
}
else
if (VM_stack_top[2].type == ML_TYPE_UNDEFINED)
{
//
// Can't have indices without an array.
//
if (num_indices != 0)
{
//
// ERROR!
//
ASSERT(0);
}
}
//
// Is our vert array big enough?
//
if (num_verts > VM_stack_top[0].array->length)
{
//
// ERROR!
//
ASSERT(0);
}
//
// Create the vert buffer.
//
tl = (LL_Tlvert *) MEM_alloc(num_verts * sizeof(LL_Tlvert));
vert_array = VM_stack_top[0].array->data;
for (i = 0; i < num_verts; i++)
{
if (vert_array[i].type != ML_TYPE_STRUCTURE)
{
//
// ERROR!
//
ASSERT(0);
}
vert_struct = vert_array[i].structure;
//
// Extract the various components of the LL_Tlvert.
//
found = 0;
for (j = 0; j < vert_struct->num_fields; j++)
{
field = &vert_struct->field[j];
if (field->field_id < SYSVAR_FIELD_NUMBER)
{
//
// This is a system field so it could be part of our
// Tlvert structure. Type check the data if it is.
//
switch(field->field_id)
{
case SYSVAR_FIELD_X:
case SYSVAR_FIELD_Y:
case SYSVAR_FIELD_U:
case SYSVAR_FIELD_V:
//
// Must be numbers.
//
if (field->data.type == ML_TYPE_SLUMBER)
{
flumber = float(field->data.slumber);
}
else
if (field->data.type == ML_TYPE_FLUMBER)
{
flumber = field->data.flumber;
}
else
{
//
// ERROR!
//
ASSERT(0);
}
break;
case SYSVAR_FIELD_Z:
case SYSVAR_FIELD_RHW:
//
// Must be floating point numbers between 0 and 1.
//
if (field->data.type == ML_TYPE_SLUMBER)
{
flumber = float(field->data.slumber);
}
else
if (field->data.type == ML_TYPE_FLUMBER)
{
flumber = field->data.flumber;
}
else
{
//
// ERROR!
//
ASSERT(0);
}
if (!WITHIN(flumber, 0.0F, 1.0F))
{
//
// ERROR!
//
ASSERT(0);
}
if (field->field_id == SYSVAR_FIELD_RHW)
{
//
// If RHW is too small it fucks up... in general!
//
if (flumber < 1.0F / 1024.0F)
{
flumber = 1.0F / 1024.0F;
}
}
break;
case SYSVAR_FIELD_COLOUR:
case SYSVAR_FIELD_SPECULAR:
//
// Must be numbers... or maybe string colours?!
//
if (field->data.type == ML_TYPE_SLUMBER)
{
slumber = field->data.slumber;
}
else
{
//
// ERROR!
//
ASSERT(0);
}
break;
default:
//
// Not a field we are interested in.
//
continue;
}
//
// This is one of our fields.
//
found |= 1 << field->field_id;
switch(field->field_id)
{
case SYSVAR_FIELD_X: tl[i].x = flumber; break;
case SYSVAR_FIELD_Y: tl[i].y = flumber; break;
case SYSVAR_FIELD_Z: tl[i].z = flumber; break;
case SYSVAR_FIELD_RHW: tl[i].x = flumber; break;
case SYSVAR_FIELD_U: tl[i].u = flumber; break;
case SYSVAR_FIELD_V: tl[i].v = flumber; break;
case SYSVAR_FIELD_COLOUR: tl[i].colour = slumber; break;
case SYSVAR_FIELD_SPECULAR: tl[i].specular = slumber; break;
default:
ASSERT(0);
break;
}
}
}
//
// Fill in missing fields with default values.
//
if (!(found & (1 << SYSVAR_FIELD_Z ))) {tl[i].z = 0.0F; found |= 1 << SYSVAR_FIELD_Z; }
if (!(found & (1 << SYSVAR_FIELD_RHW ))) {tl[i].rhw = 0.5F; found |= 1 << SYSVAR_FIELD_RHW; }
if (!(found & (1 << SYSVAR_FIELD_U ))) {tl[i].u = 0.0F; found |= 1 << SYSVAR_FIELD_U; }
if (!(found & (1 << SYSVAR_FIELD_V ))) {tl[i].v = 0.0F; found |= 1 << SYSVAR_FIELD_V; }
if (!(found & (1 << SYSVAR_FIELD_COLOUR ))) {tl[i].colour = 0xffffffff; found |= 1 << SYSVAR_FIELD_COLOUR; }
if (!(found & (1 << SYSVAR_FIELD_SPECULAR))) {tl[i].specular = 0xff000000; found |= 1 << SYSVAR_FIELD_SPECULAR;}
//
// Make sure we have initialised every member.
//
const SLONG found_every_tlvert_field =
(1 << SYSVAR_FIELD_X ) |
(1 << SYSVAR_FIELD_Y ) |
(1 << SYSVAR_FIELD_Z ) |
(1 << SYSVAR_FIELD_RHW ) |
(1 << SYSVAR_FIELD_U ) |
(1 << SYSVAR_FIELD_V ) |
(1 << SYSVAR_FIELD_COLOUR ) |
(1 << SYSVAR_FIELD_SPECULAR);
if (found != found_every_tlvert_field)
{
//
// ERROR!
//
ASSERT(0);
}
}
//
// Now create the index buffer.
//
if (num_indices == 0)
{
index = NULL;
}
else
{
//
// Is our index array big enough?
//
if (num_indices > VM_stack_top[2].array->length)
{
//
// ERROR!
//
ASSERT(0);
}
index = (UWORD *) MEM_alloc(num_indices * sizeof(UWORD));
index_array = VM_stack_top[2].array->data;
for (i = 0; i < num_indices; i++)
{
if (index_array[i].type == ML_TYPE_SLUMBER)
{
slumber = index_array[i].slumber;
}
else
if (index_array[i].type == ML_TYPE_FLUMBER)
{
float integer;
float fraction;
fraction = modff(index_array[i].flumber, &integer);
if (fraction != 0.0F)
{
//
// ERROR! Oh dear! A fractional number of indices!
//
ASSERT(0);
}
slumber = ftol(integer);
}
else
{
//
// ERROR!
//
ASSERT(0);
}
if (!WITHIN(slumber, 1, num_verts))
{
//
// ERROR!
//
ASSERT(0);
}
//
// Convert from 1-based indexing to 0-based indexing.
//
index[i] = slumber - 1;
}
}
//
// Free the arguments.
//
VM_data_free(VM_stack_top[0]);
VM_data_free(VM_stack_top[1]);
VM_data_free(VM_stack_top[2]);
VM_data_free(VM_stack_top[3]);
VM_stack_top[0].type = ML_TYPE_BUFFER;
VM_stack_top[0].lb = LL_create_buffer(
LL_BUFFER_TYPE_TLV,
tl,
num_verts,
index,
num_indices);
VM_stack_top += 1;
}
void VM_do_draw()
{
VM_POP_STACK(3);
LL_Buffer *lb;
LL_Texture *lt;
ULONG rs;
//
// We should have a buffer then a texture and a renderstate.
//
if (VM_stack_top[0].type == ML_TYPE_BUFFER)
{
lb = VM_stack_top[0].lb;
}
else
{
//
// ERROR!
//
ASSERT(0);
}
if (VM_stack_top[1].type == ML_TYPE_TEXTURE)
{
lt = VM_stack_top[1].lt;
}
else
if (VM_stack_top[1].type == ML_TYPE_UNDEFINED)
{
//
// Undefined => draw untextured.
//
lt = NULL;
}
else
{
//
// ERROR!
//
ASSERT(0);
}
if (VM_stack_top[2].type == ML_TYPE_SLUMBER)
{
rs = VM_stack_top[2].slumber;
}
else
if (VM_stack_top[2].type == ML_TYPE_UNDEFINED)
{
//
// Default renderstate.
//
rs = LL_RS_NORMAL;
}
else
{
//
// ERROR!
//
ASSERT(0);
}
//
// Do the draw!
//
LL_draw_buffer(lb,lt,rs);
//
// Free the arguments.
//
VM_data_free(VM_stack_top[0]);
VM_data_free(VM_stack_top[1]);
VM_data_free(VM_stack_top[2]);
}
void VM_do_cls()
{
ULONG colour;
float zsort;
VM_POP_STACK(2);
//
// The colour.
//
if (VM_stack_top[0].type == ML_TYPE_SLUMBER)
{
colour = VM_stack_top[0].slumber;
}
else
if (VM_stack_top[0].type == ML_TYPE_UNDEFINED)
{
colour = 0;
}
else
{
//
// ERROR!
//
ASSERT(0);
}
//
// The z-value.
//
if (VM_stack_top[1].type == ML_TYPE_SLUMBER)
{
zsort = float(VM_stack_top[1].slumber);
}
else
if (VM_stack_top[1].type == ML_TYPE_FLUMBER)
{
zsort = VM_stack_top[1].flumber;
}
else
if (VM_stack_top[1].type == ML_TYPE_UNDEFINED)
{
zsort = 1.0F;
}
if (!WITHIN(zsort, 0.0F, 1.0F))
{
//
// ERROR!
//
ASSERT(0);
}
LL_cls(colour, zsort);
}
//
// Executes the program!
//
void VM_execute()
{
while(1)
{
ASSERT(WITHIN(VM_code_pointer, VM_code, VM_code + VM_code_upto - 1));
switch(*VM_code_pointer++)
{
case ML_DO_PUSH_CONSTANT:
VM_CHECK_STACK_PUSH();
ASSERT(WITHIN(VM_code_pointer + 1, VM_code, VM_code + VM_code_upto - 1));
VM_stack_top->type = *VM_code_pointer++;
VM_stack_top->value = *VM_code_pointer++;
VM_stack_top += 1;
break;
case ML_DO_ADD:
VM_POP_STACK(2);
if (VM_stack_top[0].type != VM_stack_top[1].type)
{
//
// Must do type conversion...
//
switch(VM_stack_top[0].type)
{
case ML_TYPE_BOOLEAN:
if (VM_stack_top[1].type == ML_TYPE_STRVAR ||
VM_stack_top[1].type == ML_TYPE_STRCONST)
{
//
// Convert first argument to a string.
//
VM_convert_to_string(&VM_stack_top[0]);
}
else
{
//
// ERROR!
//
ASSERT(0);
}
break;
case ML_TYPE_SLUMBER:
if (VM_stack_top[1].type == ML_TYPE_FLUMBER)
{
//
// Convert first argument to a float.
//
VM_stack_top[0].type = ML_TYPE_FLUMBER;
VM_stack_top[0].flumber = float(VM_stack_top[0].slumber);
}
else
if (VM_stack_top[1].type == ML_TYPE_STRVAR ||
VM_stack_top[1].type == ML_TYPE_STRCONST)
{
//
// Convert first argument to a string.
//
VM_convert_to_string(&VM_stack_top[0]);
}
else
{
//
// ERROR!
//
ASSERT(0);
}
break;
case ML_TYPE_FLUMBER:
if (VM_stack_top[1].type == ML_TYPE_SLUMBER)
{
//
// Convert second argument to a float.
//
VM_stack_top[1].type = ML_TYPE_FLUMBER;
VM_stack_top[1].flumber = float(VM_stack_top[1].slumber);
}
else
if (VM_stack_top[1].type == ML_TYPE_STRVAR ||
VM_stack_top[1].type == ML_TYPE_STRCONST)
{
//
// Convert first argument to a string.
//
VM_convert_to_string(&VM_stack_top[0]);
}
else
{
//
// ERROR!
//
ASSERT(0);
}
break;
case ML_TYPE_STRVAR:
case ML_TYPE_STRCONST:
//
// Converts to a string representing the data.
//
VM_convert_to_string(&VM_stack_top[1]);
break;
default:
//
// ERROR!
//
ASSERT(0);
break;
}
}
switch(VM_stack_top[0].type)
{
case ML_TYPE_SLUMBER:
VM_stack_top[0].slumber = VM_stack_top[0].slumber + VM_stack_top[1].slumber;
break;
case ML_TYPE_FLUMBER:
VM_stack_top[0].flumber = VM_stack_top[0].flumber + VM_stack_top[1].flumber;
break;
case ML_TYPE_STRVAR:
case ML_TYPE_STRCONST:
{
ML_Data result;
SLONG length;
CBYTE *str1;
CBYTE *str2;
//
// How long is the resulting string?
//
str1 = VM_get_string(VM_stack_top[0]);
str2 = VM_get_string(VM_stack_top[1]);
length = strlen(str1) + strlen(str2) + 1; // + 1 for the terminating NULL.
//
// Build the result.
//
result.type = ML_TYPE_STRVAR;
result.strvar = (CBYTE *) MEM_alloc(length);
strcpy(result.strvar, str1);
strcat(result.strvar, str2);
//
// Free memory.
//
VM_data_free(VM_stack_top[0]);
VM_data_free(VM_stack_top[1]);
//
// Put new data on the stack.
//
VM_stack_top[0] = result;
}
break;
case ML_TYPE_VECTOR:
VM_stack_top[0].vector->x += VM_stack_top[1].vector->x;
VM_stack_top[0].vector->y += VM_stack_top[1].vector->y;
VM_stack_top[0].vector->z += VM_stack_top[1].vector->z;
VM_data_free(VM_stack_top[1]);
break;
default:
ASSERT(0);
break;
}
VM_stack_top += 1;
break;
case ML_DO_PRINT:
VM_POP_STACK(1);
VM_convert_to_string(&VM_stack_top[0]);
switch(VM_stack_top[0].type)
{
case ML_TYPE_STRVAR:
CONSOLE_print(VM_stack_top[0].strvar);
VM_data_free(VM_stack_top[0]);
break;
case ML_TYPE_STRCONST:
ASSERT(WITHIN(VM_stack_top[0].strconst, 0, VM_data_max - 2));
CONSOLE_print((CBYTE *) (VM_data + VM_stack_top[0].strconst));
break;
default:
ASSERT(0);
break;
}
break;
case ML_DO_EXIT:
//
// Free memory.
//
{
SLONG i;
for (i = 0; i < VM_global_max; i++)
{
VM_data_free(VM_global[i]);
}
ASSERT(MEM_total_bytes_allocated() == 0);
}
return;
case ML_DO_GOTO:
//
// Valid address?
//
ASSERT(WITHIN(VM_code_pointer, VM_code, VM_code + VM_code_upto - 1));
ASSERT(WITHIN(VM_code_pointer[0], 0, VM_code_upto - 1));
VM_code_pointer = &VM_code[VM_code_pointer[0]];
break;
case ML_DO_UMINUS:
VM_POP_STACK(1);
switch(VM_stack_top[0].type)
{
case ML_TYPE_SLUMBER:
VM_stack_top[0].slumber = -VM_stack_top[0].slumber;
break;
case ML_TYPE_FLUMBER:
VM_stack_top[0].flumber = -VM_stack_top[0].flumber;
break;
case ML_TYPE_VECTOR:
VM_stack_top[0].vector->x = -VM_stack_top[0].vector->x;
VM_stack_top[0].vector->y = -VM_stack_top[0].vector->y;
VM_stack_top[0].vector->z = -VM_stack_top[0].vector->z;
break;
default:
//
// ERROR!
//
ASSERT(0);
break;
}
VM_stack_top += 1;
break;
case ML_DO_PUSH_GLOBAL_VALUE:
VM_CHECK_STACK_PUSH();
ASSERT(WITHIN(VM_code_pointer, VM_code, VM_code + VM_code_upto - 1));
ASSERT(WITHIN(VM_code_pointer[0], 0, VM_global_max - 1));
//
// Push a copy of the global onto the stack. Any memory allocated
// by the original will be duplicated.
//
*VM_stack_top++ = VM_data_copy(VM_global[*VM_code_pointer++]);
break;
case ML_DO_PUSH_GLOBAL_QUICK:
VM_CHECK_STACK_PUSH();
ASSERT(WITHIN(VM_code_pointer, VM_code, VM_code + VM_code_upto - 1));
ASSERT(WITHIN(VM_code_pointer[0], 0, VM_global_max - 1));
//
// Push a copy of the global onto the stack - don't copy data.
//
*VM_stack_top++ = VM_global[*VM_code_pointer++];
break;
case ML_DO_MINUS:
VM_POP_STACK(2);
if (VM_stack_top[0].type != VM_stack_top[1].type)
{
//
// Must do type conversion...
//
VM_convert_stack_top_to_same_numerical_type();
}
ASSERT(VM_stack_top[0].type == VM_stack_top[1].type);
switch(VM_stack_top[0].type)
{
case ML_TYPE_SLUMBER:
VM_stack_top[0].slumber = VM_stack_top[0].slumber - VM_stack_top[1].slumber;
break;
case ML_TYPE_FLUMBER:
VM_stack_top[0].flumber = VM_stack_top[0].flumber - VM_stack_top[1].flumber;
break;
case ML_TYPE_VECTOR:
VM_stack_top[0].vector->x -= VM_stack_top[1].vector->x;
VM_stack_top[0].vector->y -= VM_stack_top[1].vector->y;
VM_stack_top[0].vector->z -= VM_stack_top[1].vector->z;
VM_data_free(VM_stack_top[1]);
break;
default:
ASSERT(0);
break;
}
VM_stack_top += 1;
break;
case ML_DO_TIMES:
VM_POP_STACK(2);
switch(VM_stack_top[0].type)
{
case ML_TYPE_SLUMBER:
switch(VM_stack_top[1].type)
{
case ML_TYPE_SLUMBER:
VM_stack_top[0].slumber *= VM_stack_top[1].slumber;
break;
case ML_TYPE_FLUMBER:
//
// Answer becomes a float.
//
VM_stack_top[0].type = ML_TYPE_FLUMBER;
VM_stack_top[0].flumber = float(VM_stack_top[0].slumber) * VM_stack_top[1].flumber;
break;
case ML_TYPE_VECTOR:
{
float fmul = float(VM_stack_top[0].slumber);
VM_stack_top[0] = VM_stack_top[1];
VM_stack_top[0].vector->x *= fmul;
VM_stack_top[0].vector->y *= fmul;
VM_stack_top[0].vector->z *= fmul;
}
break;
case ML_TYPE_MATRIX:
{
float fmul = float(VM_stack_top[0].slumber);
VM_stack_top[0] = VM_stack_top[1];
VM_stack_top[0].matrix->vector[0].x *= fmul;
VM_stack_top[0].matrix->vector[0].y *= fmul;
VM_stack_top[0].matrix->vector[0].z *= fmul;
VM_stack_top[0].matrix->vector[1].x *= fmul;
VM_stack_top[0].matrix->vector[1].y *= fmul;
VM_stack_top[0].matrix->vector[1].z *= fmul;
VM_stack_top[0].matrix->vector[2].x *= fmul;
VM_stack_top[0].matrix->vector[2].y *= fmul;
VM_stack_top[0].matrix->vector[2].z *= fmul;
}
break;
default:
//
// ERROR!
//
ASSERT(0);
break;
}
break;
case ML_TYPE_FLUMBER:
switch(VM_stack_top[1].type)
{
case ML_TYPE_SLUMBER:
VM_stack_top[0].flumber *= (float) VM_stack_top[1].slumber;
break;
case ML_TYPE_FLUMBER:
VM_stack_top[0].flumber *= VM_stack_top[1].flumber;
break;
case ML_TYPE_VECTOR:
{
float fmul = VM_stack_top[0].flumber;
VM_stack_top[0] = VM_stack_top[1];
VM_stack_top[0].vector->x *= fmul;
VM_stack_top[0].vector->y *= fmul;
VM_stack_top[0].vector->z *= fmul;
}
break;
case ML_TYPE_MATRIX:
{
float fmul = VM_stack_top[0].flumber;
VM_stack_top[0] = VM_stack_top[1];
VM_stack_top[0].matrix->vector[0].x *= fmul;
VM_stack_top[0].matrix->vector[0].y *= fmul;
VM_stack_top[0].matrix->vector[0].z *= fmul;
VM_stack_top[0].matrix->vector[1].x *= fmul;
VM_stack_top[0].matrix->vector[1].y *= fmul;
VM_stack_top[0].matrix->vector[1].z *= fmul;
VM_stack_top[0].matrix->vector[2].x *= fmul;
VM_stack_top[0].matrix->vector[2].y *= fmul;
VM_stack_top[0].matrix->vector[2].z *= fmul;
}
break;
default:
//
// ERROR!
//
ASSERT(0);
break;
}
break;
case ML_TYPE_MATRIX:
switch(VM_stack_top[1].type)
{
case ML_TYPE_MATRIX:
//
// Matrix multiplication.
//
{
ML_Matrix ans;
ML_Matrix *a = VM_stack_top[0].matrix;
ML_Matrix *b = VM_stack_top[1].matrix;
ans.vector[0].x = a->vector[0].x * b->vector[0].x + a->vector[0].y * b->vector[1].x + a->vector[0].z * b->vector[2].x;
ans.vector[0].y = a->vector[0].x * b->vector[0].y + a->vector[0].y * b->vector[1].y + a->vector[0].z * b->vector[2].y;
ans.vector[0].z = a->vector[0].x * b->vector[0].z + a->vector[0].y * b->vector[1].z + a->vector[0].z * b->vector[2].z;
ans.vector[1].x = a->vector[1].x * b->vector[0].x + a->vector[1].y * b->vector[1].x + a->vector[1].z * b->vector[2].x;
ans.vector[1].y = a->vector[1].x * b->vector[0].y + a->vector[1].y * b->vector[1].y + a->vector[1].z * b->vector[2].y;
ans.vector[1].z = a->vector[1].x * b->vector[0].z + a->vector[1].y * b->vector[1].z + a->vector[1].z * b->vector[2].z;
ans.vector[2].x = a->vector[2].x * b->vector[0].x + a->vector[2].y * b->vector[1].x + a->vector[2].z * b->vector[2].x;
ans.vector[2].y = a->vector[2].x * b->vector[0].y + a->vector[2].y * b->vector[1].y + a->vector[2].z * b->vector[2].y;
ans.vector[2].z = a->vector[2].x * b->vector[0].z + a->vector[2].y * b->vector[1].z + a->vector[2].z * b->vector[2].z;
*VM_stack_top[0].matrix = ans;
//
// Don't need the second matrix any more.
//
VM_data_free(VM_stack_top[1]);
}
break;
case ML_TYPE_VECTOR:
//
// Matrix * vector.
//
{
ML_Vector ans;
ans.x = VM_stack_top[0].matrix->vector[0].x * VM_stack_top[1].vector->x + VM_stack_top[0].matrix->vector[0].y * VM_stack_top[1].vector->y + VM_stack_top[0].matrix->vector[0].z * VM_stack_top[1].vector->z;
ans.y = VM_stack_top[0].matrix->vector[1].x * VM_stack_top[1].vector->x + VM_stack_top[0].matrix->vector[1].y * VM_stack_top[1].vector->y + VM_stack_top[0].matrix->vector[1].z * VM_stack_top[1].vector->z;
ans.z = VM_stack_top[0].matrix->vector[2].x * VM_stack_top[1].vector->x + VM_stack_top[0].matrix->vector[2].y * VM_stack_top[1].vector->y + VM_stack_top[0].matrix->vector[2].z * VM_stack_top[1].vector->z;
VM_data_free(VM_stack_top[0]);
VM_stack_top[0] = VM_stack_top[1];
VM_stack_top[0].vector->x = ans.x;
VM_stack_top[0].vector->y = ans.y;
VM_stack_top[0].vector->z = ans.z;
}
break;
default:
//
// ERROR!
//
ASSERT(0);
break;
}
break;
case ML_TYPE_VECTOR:
switch(VM_stack_top[1].type)
{
case ML_TYPE_SLUMBER:
{
float fmul = float(VM_stack_top[1].slumber);
VM_stack_top[0].vector->x *= fmul;
VM_stack_top[0].vector->y *= fmul;
VM_stack_top[0].vector->z *= fmul;
}
break;
case ML_TYPE_FLUMBER:
{
float fmul = VM_stack_top[1].flumber;
VM_stack_top[0].vector->x *= fmul;
VM_stack_top[0].vector->y *= fmul;
VM_stack_top[0].vector->z *= fmul;
}
break;
default:
//
// ERROR!
//
ASSERT(0);
break;
}
break;
default:
//
// ERROR!
//
ASSERT(0);
break;
}
VM_stack_top += 1;
break;
case ML_DO_DIVIDE:
VM_POP_STACK(2);
if (VM_stack_top[0].type != VM_stack_top[1].type)
{
//
// Must do type conversion...
//
VM_convert_stack_top_to_same_numerical_type();
}
ASSERT(VM_stack_top[0].type == VM_stack_top[1].type);
switch(VM_stack_top[0].type)
{
case ML_TYPE_SLUMBER:
VM_stack_top[0].slumber = VM_stack_top[0].slumber / VM_stack_top[1].slumber;
break;
case ML_TYPE_FLUMBER:
VM_stack_top[0].flumber = VM_stack_top[0].flumber / VM_stack_top[1].flumber;
break;
default:
ASSERT(0);
break;
}
VM_stack_top += 1;
break;
case ML_DO_MOD:
VM_POP_STACK(2);
if (VM_stack_top[0].type != VM_stack_top[1].type)
{
//
// Must do type conversion...
//
VM_convert_stack_top_to_same_numerical_type();
}
ASSERT(VM_stack_top[0].type == VM_stack_top[1].type);
switch(VM_stack_top[0].type)
{
case ML_TYPE_SLUMBER:
VM_stack_top[0].slumber = VM_stack_top[0].slumber % VM_stack_top[1].slumber;
break;
case ML_TYPE_FLUMBER:
VM_stack_top[0].flumber = fmodf(VM_stack_top[0].flumber,VM_stack_top[1].flumber);
break;
default:
ASSERT(0);
break;
}
VM_stack_top += 1;
break;
case ML_DO_IF_FALSE_GOTO:
VM_POP_STACK(1);
if (VM_stack_top[0].type != ML_TYPE_BOOLEAN)
{
//
// ERROR! Or maybe type conversion? What if there is a string
// that says "YES", for instance?
//
ASSERT(0);
}
if (!VM_stack_top[0].boolean)
{
//
// Valid address?
//
ASSERT(WITHIN(VM_code_pointer, VM_code, VM_code + VM_code_upto - 1));
ASSERT(WITHIN(VM_code_pointer[0], 0, VM_code_upto - 1));
VM_code_pointer = &VM_code[VM_code_pointer[0]];
}
else
{
//
// Skip over the address instruction.
//
VM_code_pointer++;
}
break;
case ML_DO_AND:
VM_POP_STACK(2);
if (VM_stack_top[0].type != VM_stack_top[1].type)
{
//
// ERROR! AND only works on the same values!
//
ASSERT(0);
}
switch(VM_stack_top[0].type)
{
case ML_TYPE_BOOLEAN: VM_stack_top[0].boolean = VM_stack_top[0].boolean && VM_stack_top[1].boolean; break;
default:
ASSERT(0);
break;
}
VM_stack_top++;
break;
case ML_DO_OR:
VM_POP_STACK(2);
if (VM_stack_top[0].type != VM_stack_top[1].type)
{
//
// ERROR! AND only works on the same values!
//
ASSERT(0);
}
switch(VM_stack_top[0].type)
{
case ML_TYPE_BOOLEAN: VM_stack_top[0].boolean = VM_stack_top[0].boolean || VM_stack_top[1].boolean; break;
default:
ASSERT(0);
break;
}
VM_stack_top++;
break;
case ML_DO_EQUALS:
case ML_DO_NOTEQUAL:
case ML_DO_JNEQ_POP_1:
{
SLONG are_equal = FALSE;
VM_POP_STACK(2);
if (VM_stack_top[0].type != VM_stack_top[1].type)
{
//
// Must do some type conversion???
//
if ((VM_stack_top[0].type == ML_TYPE_STRVAR || VM_stack_top[0].type == ML_TYPE_STRCONST) &&
(VM_stack_top[1].type == ML_TYPE_STRVAR || VM_stack_top[1].type == ML_TYPE_STRCONST))
{
//
// Both are strings...
//
}
else
{
//
// Different types aren't equal.
//
are_equal = FALSE;
goto found_out_equality;
}
}
switch(VM_stack_top[0].type)
{
case ML_TYPE_BOOLEAN: are_equal = VM_stack_top[0].boolean == VM_stack_top[1].boolean; break;
case ML_TYPE_SLUMBER: are_equal = VM_stack_top[0].slumber == VM_stack_top[1].slumber; break;
case ML_TYPE_FLUMBER: are_equal = VM_stack_top[0].flumber == VM_stack_top[1].flumber; break;
case ML_TYPE_STRCONST:
case ML_TYPE_STRVAR:
{
if (strcmp(
VM_get_string(VM_stack_top[0]),
VM_get_string(VM_stack_top[1])) == 0)
{
are_equal = TRUE;
}
else
{
are_equal = FALSE;
}
}
break;
case ML_TYPE_UNDEFINED:
are_equal = TRUE;
break;
default:
//
// ERROR!
//
ASSERT(0);
break;
}
found_out_equality:;
switch(VM_code_pointer[-1])
{
case ML_DO_NOTEQUAL:
VM_data_free(VM_stack_top[0]);
VM_data_free(VM_stack_top[1]);
VM_stack_top[0].type = ML_TYPE_BOOLEAN;
VM_stack_top[0].boolean = !are_equal;
VM_stack_top++;
break;
case ML_DO_JNEQ_POP_1:
if (VM_stack_top[0].boolean)
{
//
// Pop both values.
//
VM_data_free(VM_stack_top[0]);
VM_data_free(VM_stack_top[1]);
//
// Step over the jump address.
//
VM_code_pointer += 1;
}
else
{
//
// Pop just one value and jump.
//
VM_data_free(VM_stack_top[1]);
VM_stack_top++;
//
// Valid address?
//
ASSERT(WITHIN(VM_code_pointer, VM_code, VM_code + VM_code_upto - 1));
ASSERT(WITHIN(VM_code_pointer[0], 0, VM_code_upto - 1));
VM_code_pointer = &VM_code[VM_code_pointer[0]];
}
break;
case ML_DO_EQUALS:
VM_data_free(VM_stack_top[0]);
VM_data_free(VM_stack_top[1]);
VM_stack_top[0].type = ML_TYPE_BOOLEAN;
VM_stack_top[0].boolean = are_equal;
VM_stack_top++;
break;
default:
ASSERT(0);
break;
}
}
break;
case ML_DO_GT:
VM_POP_STACK(2);
if (VM_stack_top[0].type != VM_stack_top[1].type)
{
//
// Must do some type conversion.
//
VM_convert_stack_top_to_same_numerical_type();
}
switch(VM_stack_top[0].type)
{
case ML_TYPE_SLUMBER: VM_stack_top[0].boolean = VM_stack_top[0].slumber > VM_stack_top[1].slumber; break;
case ML_TYPE_FLUMBER: VM_stack_top[0].boolean = VM_stack_top[0].flumber > VM_stack_top[1].flumber; break;
default:
//
// ERROR!
//
ASSERT(0);
break;
}
VM_stack_top[0].type = ML_TYPE_BOOLEAN;
VM_stack_top++;
break;
case ML_DO_LT:
VM_POP_STACK(2);
if (VM_stack_top[0].type != VM_stack_top[1].type)
{
//
// Must do some type conversion.
//
VM_convert_stack_top_to_same_numerical_type();
}
switch(VM_stack_top[0].type)
{
case ML_TYPE_SLUMBER: VM_stack_top[0].boolean = VM_stack_top[0].slumber < VM_stack_top[1].slumber; break;
case ML_TYPE_FLUMBER: VM_stack_top[0].boolean = VM_stack_top[0].flumber < VM_stack_top[1].flumber; break;
default:
//
// ERROR!
//
ASSERT(0);
break;
}
VM_stack_top[0].type = ML_TYPE_BOOLEAN;
VM_stack_top++;
break;
case ML_DO_GTEQ:
VM_POP_STACK(2);
if (VM_stack_top[0].type != VM_stack_top[1].type)
{
//
// Must do some type conversion.
//
VM_convert_stack_top_to_same_numerical_type();
}
switch(VM_stack_top[0].type)
{
case ML_TYPE_SLUMBER: VM_stack_top[0].boolean = VM_stack_top[0].slumber >= VM_stack_top[1].slumber; break;
case ML_TYPE_FLUMBER: VM_stack_top[0].boolean = VM_stack_top[0].flumber >= VM_stack_top[1].flumber; break;
default:
//
// ERROR!
//
ASSERT(0);
break;
}
VM_stack_top[0].type = ML_TYPE_BOOLEAN;
VM_stack_top++;
break;
case ML_DO_LTEQ:
VM_POP_STACK(2);
if (VM_stack_top[0].type != VM_stack_top[1].type)
{
//
// Must do some type conversion.
//
VM_convert_stack_top_to_same_numerical_type();
}
switch(VM_stack_top[0].type)
{
case ML_TYPE_SLUMBER: VM_stack_top[0].boolean = VM_stack_top[0].slumber <= VM_stack_top[1].slumber; break;
case ML_TYPE_FLUMBER: VM_stack_top[0].boolean = VM_stack_top[0].flumber <= VM_stack_top[1].flumber; break;
default:
//
// ERROR!
//
ASSERT(0);
break;
}
VM_stack_top[0].type = ML_TYPE_BOOLEAN;
VM_stack_top++;
break;
case ML_DO_NOT:
VM_POP_STACK(1);
switch (VM_stack_top[0].type)
{
case ML_TYPE_BOOLEAN: VM_stack_top[0].boolean = !VM_stack_top[0].boolean; break;
default:
ASSERT(0);
break;
}
VM_stack_top++;
break;
case ML_DO_SQRT:
VM_POP_STACK(1);
switch(VM_stack_top[0].type)
{
case ML_TYPE_SLUMBER: VM_stack_top[0].slumber = (SLONG) sqrtf(float(VM_stack_top[0].slumber)); break;
case ML_TYPE_FLUMBER: VM_stack_top[0].flumber = sqrtf(float(VM_stack_top[0].flumber)); break;
default:
//
// ERROR!
//
ASSERT(0);
break;
}
VM_stack_top += 1;
break;
case ML_DO_NEWLINE:
CONSOLE_print("");
break;
case ML_DO_ABS:
VM_POP_STACK(1);
switch(VM_stack_top[0].type)
{
case ML_TYPE_SLUMBER: VM_stack_top[0].slumber = abs (VM_stack_top[0].slumber); break;
case ML_TYPE_FLUMBER: VM_stack_top[0].flumber = fabsf(VM_stack_top[0].flumber); break;
default:
//
// ERROR!
//
ASSERT(0);
break;
}
VM_stack_top += 1;
break;
case ML_DO_PUSH_GLOBAL_ADDRESS:
VM_CHECK_STACK_PUSH();
ASSERT(WITHIN(VM_code_pointer + 1, VM_code, VM_code + VM_code_upto - 1));
ASSERT(WITHIN(VM_code_pointer[0], 0, VM_global_max - 1));
VM_stack_top[0].type = ML_TYPE_POINTER;
VM_stack_top[0].data = &VM_global[*VM_code_pointer++];
VM_stack_top++;
break;
case ML_DO_ASSIGN:
VM_POP_STACK(2);
ASSERT(VM_stack_top[1].type == ML_TYPE_POINTER);
//
// Free any memory used by the current variable.
//
VM_data_free(*VM_stack_top[1].data);
//
// Overwrite old value.
//
*VM_stack_top[1].data = VM_stack_top[0];
break;
case ML_DO_PUSH_FIELD_ADDRESS:
VM_POP_STACK(1);
ASSERT(WITHIN(VM_code_pointer + 1, VM_code, VM_code + VM_code_upto - 1));
if (VM_stack_top[0].type == ML_TYPE_POINTER)
{
SLONG field_id;
ML_Data *data;
field_id = *VM_code_pointer++;
data = VM_stack_top[0].data;
switch(data->type)
{
case ML_TYPE_STRUCTURE:
//
// Does this variable already have this field?
//
SLONG i;
for (i = 0; i < data->structure->num_fields; i++)
{
if (data->structure->field[i].field_id == field_id)
{
//
// Found our field. Push the address onto the stack.
//
VM_stack_top[0].type = ML_TYPE_POINTER;
VM_stack_top[0].data = &data->structure->field[i].data;
VM_stack_top++;
break;
}
}
//
// Must add another field.
//
ML_Structure *ms;
ms = (ML_Structure *) MEM_alloc(sizeof(ML_Structure) + (data->structure->num_fields + 1) * sizeof(ML_Field));
memcpy(ms, data->structure, sizeof(ML_Structure) + data->structure->num_fields * sizeof(ML_Field));
ms->field[ms->num_fields].field_id = field_id;
ms->field[ms->num_fields].data.type = ML_TYPE_UNDEFINED;
//
// Push address onto the stack.
//
VM_stack_top[0].type = ML_TYPE_POINTER;
VM_stack_top[0].data = &ms->field[ms->num_fields].data;
VM_stack_top++;
ms->num_fields += 1;
//
// Get rid of old memory- point to new memory.
//
MEM_free(data->structure);
data->structure = ms;
break;
case ML_TYPE_MATRIX:
//
// Can only access fields (x,y,z)
//
if (!WITHIN(field_id, SYSVAR_FIELD_X, SYSVAR_FIELD_Z))
{
//
// ERROR!
//
ASSERT(0);
}
//
// Push address onto the stack.
//
VM_stack_top[0].type = ML_TYPE_VOINTER;
VM_stack_top[0].vector = &data->matrix->vector[field_id];
VM_stack_top++;
break;
case ML_TYPE_VECTOR:
//
// Can only access fields (x,y,z)
//
if (!WITHIN(field_id, SYSVAR_FIELD_X, SYSVAR_FIELD_Z))
{
//
// ERROR!
//
ASSERT(0);
}
//
// Push address onto the stack.
//
VM_stack_top[0].type = ML_TYPE_FLOINTER;
VM_stack_top[0].flointer = &(&data->vector->x)[field_id];
VM_stack_top++;
break;
default:
//
// Create a struture with just one field marked as undefined.
//
VM_data_free(*data);
data->type = ML_TYPE_STRUCTURE;
data->structure = (ML_Structure *) MEM_alloc(sizeof(ML_Structure) + sizeof(ML_Field) * 1);
data->structure->num_fields = 1;
data->structure->field[0].field_id = field_id;
data->structure->field[0].data.type = ML_TYPE_UNDEFINED;
//
// Push the address of this field onto the stack.
//
VM_stack_top[0].type = ML_TYPE_POINTER;
VM_stack_top[0].data = &data->structure->field[0].data;
VM_stack_top++;
break;
}
}
else
if (VM_stack_top[0].type == ML_TYPE_VOINTER)
{
SLONG field_id;
ML_Data *data;
field_id = *VM_code_pointer++;
//
// Can only access fields (x,y,z)
//
if (!WITHIN(field_id, SYSVAR_FIELD_X, SYSVAR_FIELD_Z))
{
//
// ERROR!
//
ASSERT(0);
}
//
// Push address onto the stack.
//
VM_stack_top[0].type = ML_TYPE_FLOINTER;
VM_stack_top[0].flointer = &(&VM_stack_top[0].vector->x)[field_id];
VM_stack_top++;
}
else
if (VM_stack_top[0].type == ML_TYPE_FLOINTER)
{
//
// ERROR!
//
ASSERT(0);
}
else
{
//
// ERROR! This is an internal error - not a real error.
//
ASSERT(0);
}
break;
case ML_DO_PUSH_FIELD_VALUE:
case ML_DO_PUSH_FIELD_QUICK:
VM_POP_STACK(1);
ASSERT(WITHIN(VM_code_pointer + 1, VM_code, VM_code + VM_code_upto - 1));
{
SLONG field_id;
field_id = *VM_code_pointer++;
//
// Is this variable already a structure?
//
if (VM_stack_top[0].type != ML_TYPE_STRUCTURE)
{
if (VM_stack_top[0].type == ML_TYPE_MATRIX)
{
if (WITHIN(field_id, SYSVAR_FIELD_X, SYSVAR_FIELD_Z))
{
ML_Data ans;
if (VM_code_pointer[-2] == ML_DO_PUSH_FIELD_QUICK)
{
//
// Point to the same data as the matrix.
//
ans.type = ML_TYPE_VECTOR;
ans.vector = &VM_stack_top[0].matrix->vector[field_id];
}
else
{
//
// Copy the matrix data to create a new vector.
//
ans.type = ML_TYPE_VECTOR;
ans.vector = (ML_Vector *) MEM_alloc(sizeof(ML_Vector));
*(ans.vector) = VM_stack_top[0].matrix->vector[field_id];
VM_stack_top[0] = ans;
VM_stack_top++;
}
}
else
{
//
// No such field... push <UNDEFINED> onto the stack.
//
VM_stack_top[0].type = ML_TYPE_UNDEFINED;
VM_stack_top++;
}
}
else
if (VM_stack_top[0].type == ML_TYPE_VECTOR)
{
if (WITHIN(field_id, SYSVAR_FIELD_X, SYSVAR_FIELD_Z))
{
ML_Data ans;
//
// Point to the same data as the matrix.
//
ans.type = ML_TYPE_FLUMBER;
ans.flumber = (&VM_stack_top[0].vector->x)[field_id];
VM_stack_top[0] = ans;
VM_stack_top++;
}
else
{
//
// No such field... push <UNDEFINED> onto the stack.
//
VM_stack_top[0].type = ML_TYPE_UNDEFINED;
VM_stack_top++;
}
}
else
{
//
// No such field... push <UNDEFINED> onto the stack.
//
VM_stack_top[0].type = ML_TYPE_UNDEFINED;
VM_stack_top++;
}
}
else
{
//
// Does this variable already have this field?
//
SLONG i;
for (i = 0; i < VM_stack_top[0].structure->num_fields; i++)
{
if (VM_stack_top[0].structure->field[i].field_id == field_id)
{
//
// Found our field. Push a copy of the value onto the stack.
//
if (VM_code_pointer[-2] == ML_DO_PUSH_FIELD_QUICK)
{
//
// Push on the actual data rather than a copy.
//
VM_stack_top[0] = VM_stack_top[0].structure->field[i].data;
}
else
{
//
// Push on a copy of the data.
//
VM_stack_top[0] = VM_data_copy(VM_stack_top[0].structure->field[i].data);
}
VM_stack_top++;
goto pushed_field_value;
}
}
VM_stack_top[0].type = ML_TYPE_UNDEFINED;
VM_stack_top++;
pushed_field_value:;
}
}
break;
case ML_DO_PUSH_ARRAY_ADDRESS:
{
SLONG i;
SLONG j;
SLONG array_length;
SLONG num_dimensions;
ASSERT(WITHIN(VM_code_pointer + 1, VM_code, VM_code + VM_code_upto - 1));
num_dimensions = *VM_code_pointer++;
VM_POP_STACK(num_dimensions + 1);
//
// Make sure all the indices are given by integers!
//
for (i = 0; i < num_dimensions; i++)
{
if (VM_stack_top[i + 1].type != ML_TYPE_SLUMBER)
{
//
// ERROR!
//
ASSERT(0);
}
if (VM_stack_top[i + 1].slumber <= 0)
{
//
// ERROR!
//
ASSERT(0);
}
if (VM_stack_top[i + 1].slumber >= 1024 * 1024 * 2 / 8)
{
//
// ERROR! Limit to 2meg arrays to guard against ridiculous memory usage?
//
ASSERT(0);
}
}
//
// Make sure we have a pointer on the stack.
//
if (VM_stack_top[0].type != ML_TYPE_POINTER)
{
//
// ERROR!
//
ASSERT(0);
}
ML_Data *data = VM_stack_top[0].data;
//
// Do we already have an array on the stack of the correct
// dimensions?
//
if (data->type != ML_TYPE_ARRAY || data->array->num_dimensions != num_dimensions)
{
//
// Free old value.
//
VM_data_free(*data);
//
// How long should the array be?
//
array_length = VM_stack_top[1].slumber;
for (i = 1; i < num_dimensions; i++)
{
array_length *= VM_stack_top[i + 1].slumber;
}
//
// Create the array.
//
data->type = ML_TYPE_ARRAY;
data->array = (ML_Array *) MEM_alloc(sizeof(ML_Array) + sizeof(ML_Dimension) * num_dimensions);
data->array->length = array_length;
data->array->num_dimensions = num_dimensions;
data->array->data = (ML_Data *) MEM_alloc(sizeof(ML_Data) * array_length);
for (i = 0; i < num_dimensions; i++)
{
data->array->dimension[i].size = VM_stack_top[i + 1].slumber;
data->array->dimension[i].stride = 1;
for (j = i + 1; j < num_dimensions; j++)
{
data->array->dimension[i].stride *= VM_stack_top[j + 1].slumber;
}
}
//
// Make all elements of the array undefined.
//
memset(data->array->data, 0, sizeof(ML_Data) * array_length);
}
//
// Should have a compatible data type by now...
//
ASSERT(data->type == ML_TYPE_ARRAY);
ASSERT(data->array->num_dimensions == num_dimensions);
//
// Is this array big enough?
//
for (i = 0; i < num_dimensions; i++)
{
if (VM_stack_top[i + 1].slumber > data->array->dimension[i].size)
{
VM_grow_array(data->array, VM_stack_top + 1);
goto grown_array;
}
}
grown_array:;
//
// What's this index of this element into the array?
//
SLONG index = 0;
for (i = 0; i < num_dimensions; i++)
{
index += data->array->dimension[i].stride * (VM_stack_top[i + 1].slumber - 1);
}
ASSERT(WITHIN(index, 0, data->array->length - 1));
//
// Push the address of the element onto the stack.
//
VM_stack_top[0].type = ML_TYPE_POINTER;
VM_stack_top[0].data = data->array->data + index;
VM_stack_top++;
}
break;
case ML_DO_PUSH_ARRAY_VALUE:
case ML_DO_PUSH_ARRAY_QUICK:
{
SLONG i;
SLONG index;
SLONG num_dimensions;
ASSERT(WITHIN(VM_code_pointer + 1, VM_code, VM_code + VM_code_upto - 1));
num_dimensions = *VM_code_pointer++;
VM_POP_STACK(num_dimensions + 1);
//
// Make sure all the indices are given by integers!
//
for (i = 0; i < num_dimensions; i++)
{
if (VM_stack_top[i + 1].type != ML_TYPE_SLUMBER)
{
//
// ERROR!
//
ASSERT(0);
}
if (VM_stack_top[i + 1].slumber <= 0)
{
//
// ERROR!
//
ASSERT(0);
}
if (VM_stack_top[i + 1].slumber >= 1024 * 1024 * 2 / 8)
{
//
// ERROR! Limit to 2meg arrays to guard against ridiculous memory usage?
//
ASSERT(0);
}
}
if (VM_stack_top[0].type != ML_TYPE_ARRAY || VM_stack_top[0].array->num_dimensions != num_dimensions)
{
//
// Incompatible data type- push <UNDEFINED>
//
VM_stack_top[0].type = ML_TYPE_UNDEFINED;
VM_stack_top++;
}
else
{
//
// Are all indices in range?
//
for (i = 0; i < num_dimensions; i++)
{
if (VM_stack_top[i + 1].slumber > VM_stack_top[0].array->dimension[i].size)
{
//
// Referencing outside the array.
//
VM_stack_top[0].type = ML_TYPE_UNDEFINED;
VM_stack_top++;
goto outside_the_array;
}
}
//
// What's this index of this element into the array?
//
index = 0;
for (i = 0; i < num_dimensions; i++)
{
index += VM_stack_top[0].array->dimension[i].stride * (VM_stack_top[i + 1].slumber - 1);
}
//
// Push a copy of this element onto the stack.
//
ASSERT(WITHIN(index, 0, VM_stack_top[0].array->length - 1));
if (VM_code_pointer[-2] == ML_DO_PUSH_ARRAY_QUICK)
{
//
// Push the actual value rather than a copy.
//
VM_stack_top[0] = VM_stack_top[0].array->data[index];
}
else
{
//
// Push a copy of the data.
//
VM_stack_top[0] = VM_data_copy(VM_stack_top[0].array->data[index]);
}
VM_stack_top++;
outside_the_array:;
}
}
break;
case ML_DO_PUSH_INPUT:
{
CBYTE *string;
//
// Get user input!
//
string = CONSOLE_input();
VM_CHECK_STACK_PUSH();
VM_stack_top[0].type = ML_TYPE_STRVAR;
VM_stack_top[0].strvar = (CBYTE *) MEM_alloc(strlen(string) + 1);
strcpy(VM_stack_top[0].strvar, string);
VM_stack_top++;
}
break;
case ML_DO_GOSUB:
VM_CHECK_STACK_PUSH();
//
// Push the return address onto the stack.
//
VM_stack_top[0].type = ML_TYPE_CODE_POINTER;
VM_stack_top[0].code_pointer = VM_code_pointer + 1;
VM_stack_top++;
//
// Valid address?
//
ASSERT(WITHIN(VM_code_pointer, VM_code, VM_code + VM_code_upto - 1));
ASSERT(WITHIN(VM_code_pointer[0], 0, VM_code_upto - 1));
VM_code_pointer = &VM_code[VM_code_pointer[0]];
break;
case ML_DO_RETURN:
VM_POP_STACK(1);
ASSERT(VM_stack_top[0].type == ML_TYPE_CODE_POINTER);
ASSERT(WITHIN(VM_stack_top[0].code_pointer, VM_code, VM_code + VM_code_upto - 1));
VM_code_pointer = VM_stack_top[0].code_pointer;
break;
case ML_DO_XOR:
VM_POP_STACK(2);
if (VM_stack_top[0].type != VM_stack_top[1].type)
{
//
// ERROR! AND only works on the same values!
//
ASSERT(0);
}
switch(VM_stack_top[0].type)
{
case ML_TYPE_BOOLEAN: VM_stack_top[0].boolean = VM_stack_top[0].boolean ^ VM_stack_top[1].boolean; break;
default:
ASSERT(0);
break;
}
VM_stack_top++;
break;
case ML_DO_IF_TRUE_GOTO:
VM_POP_STACK(1);
if (VM_stack_top[0].type != ML_TYPE_BOOLEAN)
{
//
// ERROR! Or maybe type conversion? What if there is a string
// that says "YES", for instance?
//
ASSERT(0);
}
if (VM_stack_top[0].boolean)
{
//
// Valid address?
//
ASSERT(WITHIN(VM_code_pointer, VM_code, VM_code + VM_code_upto - 1));
ASSERT(WITHIN(VM_code_pointer[0], 0, VM_code_upto - 1));
VM_code_pointer = &VM_code[VM_code_pointer[0]];
}
else
{
//
// Skip over the address instruction.
//
VM_code_pointer++;
}
break;
case ML_DO_PUSH_RANDOM_SLUMBER:
VM_CHECK_STACK_PUSH();
VM_stack_top[0].type = ML_TYPE_SLUMBER;
VM_stack_top[0].slumber = rand();
VM_stack_top++;
break;
case ML_DO_SWAP:
VM_POP_STACK(2);
ASSERT(VM_stack_top[0].type == ML_TYPE_POINTER);
ASSERT(VM_stack_top[1].type == ML_TYPE_POINTER);
{
ML_Data swap_spare;
swap_spare = *VM_stack_top[0].data;
*VM_stack_top[0].data = *VM_stack_top[1].data;
*VM_stack_top[1].data = swap_spare;
}
break;
case ML_DO_ENTERFUNC:
{
SLONG i;
//
// Do we have the right number of arguments?
//
ASSERT(VM_stack_top[-2].type == ML_TYPE_NUM_ARGS ); // Num args to functions
ASSERT(VM_stack_top[-1].type == ML_TYPE_CODE_POINTER); // Return address
SLONG args_to_function = VM_stack_top[-2].args;
if (args_to_function == *VM_code_pointer)
{
//
// Get rid of the number of args to the function.
//
VM_POP_STACK(1);
VM_stack_top[-1] = VM_stack_top[0];
}
else
{
if (args_to_function > *VM_code_pointer)
{
//
// ERROR! Too many argument passed to the function!
//
ASSERT(0);
}
else
{
//
// Remember the code pointer so we wont overwrite it.
//
SLONG *code_pointer = VM_stack_top[-1].code_pointer;
//
// Must insert extra undefined arguments.
//
VM_POP_STACK(2);
for (i = args_to_function; i < *VM_code_pointer; i++)
{
VM_CHECK_STACK_PUSH();
VM_stack_top[0].type = ML_TYPE_UNDEFINED;
VM_stack_top[0].value = 0; // Not required.
VM_stack_top++;
}
//
// Now push the return address.
//
VM_CHECK_STACK_PUSH();
VM_stack_top[0].type = ML_TYPE_CODE_POINTER;
VM_stack_top[0].code_pointer = code_pointer;
VM_stack_top++;
}
}
}
//
// Push the old base pointer.
//
VM_CHECK_STACK_PUSH();
VM_stack_top[0].type = ML_TYPE_STACK_BASE;
VM_stack_top[0].stack_base = VM_stack_base;
VM_stack_top++;
//
// Work out the new stack base.
//
ASSERT(WITHIN( VM_code_pointer, VM_code, VM_code + VM_code_upto - 1));
ASSERT(WITHIN(*VM_code_pointer, 0, 256)); // Sensible numbers of arguments?
//
// Minus 2 to skip over the return address and the old stack base.
//
VM_stack_base = VM_stack_top - *VM_code_pointer++ - 2;
break;
case ML_DO_ENDFUNC:
{
//
// On the stack there should be the return value of the function,
// the old base pointer and the return address.
//
VM_POP_STACK(3);
ASSERT(VM_stack_top[0].type == ML_TYPE_CODE_POINTER);
ASSERT(VM_stack_top[1].type == ML_TYPE_STACK_BASE );
SLONG *return_instruction = VM_stack_top[0].code_pointer;
//
// Pop the locals off the stack.
//
ASSERT(WITHIN( VM_code_pointer, VM_code, VM_code + VM_code_upto - 1));
ASSERT(WITHIN(*VM_code_pointer, 0, 256)); // Sensible numbers of arguments?
VM_POP_STACK(*VM_code_pointer);
//
// Free all the locals.
//
SLONG i;
for (i = 0; i < *VM_code_pointer; i++)
{
VM_data_free(VM_stack_top[i]);
}
//
// Push the return value onto the bottom of the stack.
//
VM_stack_top[0] = VM_stack_top[*VM_code_pointer + 2];
VM_stack_top++;
//
// Continue execution from the return address.
//
VM_code_pointer = return_instruction;
}
break;
case ML_DO_POP:
VM_POP_STACK(1);
break;
case ML_DO_PUSH_LOCAL_VALUE:
VM_CHECK_STACK_PUSH();
ASSERT(WITHIN( VM_code_pointer, VM_code, VM_code + VM_code_upto - 1));
ASSERT(WITHIN(*VM_code_pointer, 0, 256)); // Sensible local index?
ASSERT(VM_stack_base);
ASSERT(VM_stack_base + *VM_code_pointer <= VM_stack_top - 2);
ASSERT(VM_stack_base[*VM_code_pointer].type != ML_TYPE_CODE_POINTER);
ASSERT(VM_stack_base[*VM_code_pointer].type != ML_TYPE_STACK_BASE);
//
// Push a copy of the local onto the stack. Any memory allocated
// by the original will be duplicated.
//
*VM_stack_top++ = VM_data_copy(VM_stack_base[*VM_code_pointer++]);
break;
case ML_DO_PUSH_LOCAL_ADDRESS:
VM_CHECK_STACK_PUSH();
ASSERT(WITHIN( VM_code_pointer, VM_code, VM_code + VM_code_upto - 1));
ASSERT(WITHIN(*VM_code_pointer, 0, 256)); // Sensible local index?
ASSERT(VM_stack_base);
ASSERT(VM_stack_base + *VM_code_pointer < VM_stack_top - 2);
ASSERT(VM_stack_base[*VM_code_pointer].type != ML_TYPE_CODE_POINTER);
ASSERT(VM_stack_base[*VM_code_pointer].type != ML_TYPE_STACK_BASE);
if (VM_stack_base[*VM_code_pointer].type == ML_TYPE_POINTER)
{
//
// This local is already a pointer, just push on
// it's current value.
//
VM_stack_top[0] = VM_stack_base[*VM_code_pointer++];
}
else
{
//
// Push on the address of this local.
//
VM_stack_top[0].type = ML_TYPE_POINTER;
VM_stack_top[0].data = VM_stack_base + *VM_code_pointer++;
}
VM_stack_top++;
break;
case ML_DO_PUSH_LOCAL_QUICK:
VM_CHECK_STACK_PUSH();
ASSERT(WITHIN( VM_code_pointer, VM_code, VM_code + VM_code_upto - 1));
ASSERT(WITHIN(*VM_code_pointer, 0, 256)); // Sensible local index?
ASSERT(VM_stack_base);
ASSERT(VM_stack_base + *VM_code_pointer < VM_stack_top - 2);
ASSERT(VM_stack_base[*VM_code_pointer].type != ML_TYPE_CODE_POINTER);
ASSERT(VM_stack_base[*VM_code_pointer].type != ML_TYPE_STACK_BASE);
//
// Push a copy of the local onto the stack - don't copy data.
//
*VM_stack_top++ = VM_stack_base[*VM_code_pointer++];
break;
case ML_DO_TEXTURE: VM_do_texture(); break;
case ML_DO_BUFFER: VM_do_buffer(); break;
case ML_DO_DRAW: VM_do_draw(); break;
case ML_DO_CLS: VM_do_cls(); break;
case ML_DO_FLIP:
//
// Less than 10 milliseconds since our last FLIP?
//
while(VM_flip_tick > OS_ticks() - 10);
LL_flip();
VM_flip_tick = OS_ticks();
break;
case ML_DO_KEY_VALUE:
VM_POP_STACK(1);
if (VM_stack_top[0].type != ML_TYPE_SLUMBER)
{
//
// ERROR!
//
ASSERT(0);
}
if (!WITHIN(VM_stack_top[0].slumber, 1, 255))
{
//
// Outside the array...
//
VM_stack_top[0].type = ML_TYPE_UNDEFINED;
}
else
{
if (KEY_on[VM_stack_top[0].slumber] == 0 ||
KEY_on[VM_stack_top[0].slumber] == 1)
{
//
// This key has been pressed since the last time
// it was assigned to. Free the current key value.
//
VM_data_free(VM_global[VM_EXTRA_GLOBAL_KEY(VM_stack_top[0].slumber)]);
//
// Create a new value.
//
VM_global[VM_EXTRA_GLOBAL_KEY(VM_stack_top[0].slumber)].type = ML_TYPE_BOOLEAN;
VM_global[VM_EXTRA_GLOBAL_KEY(VM_stack_top[0].slumber)].boolean = KEY_on[VM_stack_top[0].slumber];
//
// Push it onto the stack.
//
VM_stack_top[0] = VM_data_copy(VM_global[VM_EXTRA_GLOBAL_KEY(VM_stack_top[0].slumber)]);
}
else
{
//
// Use the value the user wrote to the KEY[] array...
//
VM_stack_top[0] = VM_data_copy(VM_global[VM_EXTRA_GLOBAL_KEY(VM_stack_top[0].slumber)]);
}
}
VM_stack_top++;
break;
case ML_DO_KEY_ASSIGN:
VM_POP_STACK(2);
if (VM_stack_top[1].type != ML_TYPE_SLUMBER)
{
//
// ERROR!
//
ASSERT(0);
}
if (!WITHIN(VM_stack_top[1].slumber, 1, 255))
{
//
// ERROR!
//
ASSERT(0);
}
//
// Free the current value.
//
VM_data_free(VM_global[VM_EXTRA_GLOBAL_KEY(VM_stack_top[1].slumber)]);
//
// Assign the new number.
//
VM_global[VM_EXTRA_GLOBAL_KEY(VM_stack_top[1].slumber)] = VM_stack_top[0];
//
// Overwrite the real KEY_on array with a special value so we know
// if the key has been pressed or release since it was assigned to...
//
KEY_on[VM_stack_top[1].slumber] = 42;
break;
case ML_DO_INKEY_VALUE:
VM_CHECK_STACK_PUSH();
if (KEY_inkey)
{
VM_data_free(VM_global[VM_EXTRA_GLOBAL_INKEY]);
VM_global[VM_EXTRA_GLOBAL_INKEY].type = ML_TYPE_SLUMBER;
VM_global[VM_EXTRA_GLOBAL_INKEY].slumber = KEY_inkey;
KEY_inkey = 0;
}
VM_stack_top[0] = VM_data_copy(VM_global[VM_EXTRA_GLOBAL_INKEY]);
VM_stack_top++;
break;
case ML_DO_INKEY_ASSIGN:
VM_POP_STACK(1);
//
// Free memory used by the current inkey variable.
//
VM_data_free(VM_global[VM_EXTRA_GLOBAL_INKEY]);
//
// Assign the new value.
//
VM_global[VM_EXTRA_GLOBAL_INKEY] = VM_stack_top[0];
//
// Overwrite the real inkey too!
//
KEY_inkey = 0;
break;
case ML_DO_TIMER:
VM_CHECK_STACK_PUSH();
VM_stack_top[0].type = ML_TYPE_FLUMBER;
VM_stack_top[0].flumber = float(OS_ticks()) * 0.001F;
VM_stack_top++;
break;
case ML_DO_SIN:
VM_POP_STACK(1);
{
float val;
switch(VM_stack_top[0].type)
{
case ML_TYPE_SLUMBER: val = float(VM_stack_top[0].slumber); break;
case ML_TYPE_FLUMBER: val = VM_stack_top[0].flumber; break;
default:
ASSERT(0);
break;
}
VM_stack_top[0].type = ML_TYPE_FLUMBER;
VM_stack_top[0].flumber = sinf(val);
}
VM_stack_top++;
break;
case ML_DO_COS:
VM_POP_STACK(1);
{
float val;
switch(VM_stack_top[0].type)
{
case ML_TYPE_SLUMBER: val = float(VM_stack_top[0].slumber); break;
case ML_TYPE_FLUMBER: val = VM_stack_top[0].flumber; break;
default:
ASSERT(0);
break;
}
VM_stack_top[0].type = ML_TYPE_FLUMBER;
VM_stack_top[0].flumber = cosf(val);
}
VM_stack_top++;
break;
case ML_DO_TAN:
VM_POP_STACK(1);
{
float val;
switch(VM_stack_top[0].type)
{
case ML_TYPE_SLUMBER: val = float(VM_stack_top[0].slumber); break;
case ML_TYPE_FLUMBER: val = VM_stack_top[0].flumber; break;
default:
ASSERT(0);
break;
}
VM_stack_top[0].type = ML_TYPE_FLUMBER;
VM_stack_top[0].flumber = tanf(val);
}
VM_stack_top++;
break;
case ML_DO_ASIN:
VM_POP_STACK(1);
{
float val;
switch(VM_stack_top[0].type)
{
case ML_TYPE_SLUMBER: val = float(VM_stack_top[0].slumber); break;
case ML_TYPE_FLUMBER: val = VM_stack_top[0].flumber; break;
default:
ASSERT(0);
break;
}
VM_stack_top[0].type = ML_TYPE_FLUMBER;
VM_stack_top[0].flumber = asinf(val);
}
VM_stack_top++;
break;
case ML_DO_ACOS:
VM_POP_STACK(1);
{
float val;
switch(VM_stack_top[0].type)
{
case ML_TYPE_SLUMBER: val = float(VM_stack_top[0].slumber); break;
case ML_TYPE_FLUMBER: val = VM_stack_top[0].flumber; break;
default:
ASSERT(0);
break;
}
VM_stack_top[0].type = ML_TYPE_FLUMBER;
VM_stack_top[0].flumber = acosf(val);
}
VM_stack_top++;
break;
case ML_DO_ATAN:
VM_POP_STACK(1);
{
float val;
switch(VM_stack_top[0].type)
{
case ML_TYPE_SLUMBER: val = float(VM_stack_top[0].slumber); break;
case ML_TYPE_FLUMBER: val = VM_stack_top[0].flumber; break;
default:
ASSERT(0);
break;
}
VM_stack_top[0].type = ML_TYPE_FLUMBER;
VM_stack_top[0].flumber = atanf(val);
}
VM_stack_top++;
break;
case ML_DO_ATAN2:
VM_POP_STACK(2);
{
float val1;
float val2;
switch(VM_stack_top[0].type)
{
case ML_TYPE_SLUMBER: val1 = float(VM_stack_top[0].slumber); break;
case ML_TYPE_FLUMBER: val1 = VM_stack_top[0].flumber; break;
default:
ASSERT(0);
break;
}
switch(VM_stack_top[1].type)
{
case ML_TYPE_SLUMBER: val2 = float(VM_stack_top[1].slumber); break;
case ML_TYPE_FLUMBER: val2 = VM_stack_top[1].flumber; break;
default:
ASSERT(0);
break;
}
VM_stack_top[0].type = ML_TYPE_FLUMBER;
VM_stack_top[0].flumber = atan2f(val1,val2);
}
VM_stack_top++;
break;
case ML_DO_NOP:
break;
case ML_DO_LEFT:
VM_POP_STACK(2);
//
// Check types.
//
if (VM_stack_top[0].type != ML_TYPE_STRVAR &&
VM_stack_top[0].type != ML_TYPE_STRCONST)
{
//
// ERROR!
//
ASSERT(0);
}
if (VM_stack_top[1].type != ML_TYPE_SLUMBER)
{
//
// ERROR!
//
ASSERT(0);
}
{
ML_Data ans;
CBYTE *input = VM_get_string(VM_stack_top[0]);
SLONG left = VM_stack_top[1].slumber;
if (left < 0)
{
//
// ERROR!
//
ASSERT(0);
}
ans.type = ML_TYPE_STRVAR;
ans.strvar = (CBYTE *) MEM_alloc(left + 1);
CBYTE *src = input;
CBYTE *dst = ans.strvar;
while(left > 0)
{
if (*src == '\000')
{
break;
}
*dst++ = *src++;
left--;
}
*dst = '\000';
//
// Free args.
//
VM_data_free(VM_stack_top[0]);
VM_data_free(VM_stack_top[1]);
//
// Push answer onto the stack.
//
VM_CHECK_STACK_PUSH();
VM_stack_top[0] = ans;
VM_stack_top++;
}
break;
case ML_DO_MID:
VM_POP_STACK(3);
//
// Check types.
//
if (VM_stack_top[0].type != ML_TYPE_STRVAR &&
VM_stack_top[0].type != ML_TYPE_STRCONST)
{
//
// ERROR!
//
ASSERT(0);
}
if (VM_stack_top[1].type != ML_TYPE_SLUMBER ||
VM_stack_top[2].type != ML_TYPE_SLUMBER)
{
//
// ERROR!
//
ASSERT(0);
}
{
ML_Data ans;
CBYTE *input = VM_get_string(VM_stack_top[0]);
SLONG in = VM_stack_top[1].slumber - 1; // - 1 because BASIC is 1-based not zero based.
SLONG num = VM_stack_top[2].slumber;
SLONG len = strlen(input);
if (num < 0)
{
//
// ERROR! Bad number of characters.
//
ASSERT(0);
}
ans.type = ML_TYPE_STRVAR;
ans.strvar = (CBYTE *) MEM_alloc(num + 1);
if (!WITHIN(in, 0, len - 1))
{
ans.strvar[0] = '\000';
}
else
{
CBYTE *src = input + in;
CBYTE *dst = ans.strvar;
while(num > 0)
{
if (*src == '\000')
{
break;
}
*dst++ = *src++;
num--;
}
*dst = '\000';
}
//
// Free args.
//
VM_data_free(VM_stack_top[0]);
VM_data_free(VM_stack_top[1]);
VM_data_free(VM_stack_top[2]);
//
// Push answer onto the stack.
//
VM_CHECK_STACK_PUSH();
VM_stack_top[0] = ans;
VM_stack_top++;
}
break;
case ML_DO_RIGHT:
VM_POP_STACK(2);
//
// Check types.
//
if (VM_stack_top[0].type != ML_TYPE_STRVAR &&
VM_stack_top[0].type != ML_TYPE_STRCONST)
{
//
// ERROR!
//
ASSERT(0);
}
if (VM_stack_top[1].type != ML_TYPE_SLUMBER)
{
//
// ERROR!
//
ASSERT(0);
}
{
ML_Data ans;
CBYTE *input = VM_get_string(VM_stack_top[0]);
SLONG len = VM_stack_top[1].slumber;
ans.type = ML_TYPE_STRVAR;
ans.strvar = (CBYTE *) MEM_alloc(len + 1);
CBYTE *src = input;
CBYTE *dst = ans.strvar;
while(*src) {src++;}
src -= len;
if (src < input)
{
//
// We want a segment that is bigger that the string!
//
src = input;
}
while(len > 0)
{
if (*src == '\000')
{
break;
}
*dst++ = *src++;
len--;
}
*dst = '\000';
//
// Free args.
//
VM_data_free(VM_stack_top[0]);
VM_data_free(VM_stack_top[1]);
//
// Push answer onto the stack.
//
VM_CHECK_STACK_PUSH();
VM_stack_top[0] = ans;
VM_stack_top++;
}
break;
case ML_DO_LEN:
VM_POP_STACK(1);
if (VM_stack_top[0].type != ML_TYPE_STRVAR &&
VM_stack_top[0].type != ML_TYPE_STRCONST)
{
//
// ERROR!
//
ASSERT(0);
}
else
{
SLONG len = strlen(VM_get_string(VM_stack_top[0]));
VM_data_free(VM_stack_top[0]);
VM_CHECK_STACK_PUSH();
VM_stack_top[0].type = ML_TYPE_SLUMBER;
VM_stack_top[0].slumber = len;
VM_stack_top++;
}
break;
case ML_DO_PUSH_IDENTITY_MATRIX:
{
const ML_Vector right = {1.0F, 0.0F, 0.0F};
const ML_Vector up = {0.0F, 1.0F, 0.0F};
const ML_Vector forwards = {0.0F, 0.0F, 1.0F};
VM_CHECK_STACK_PUSH();
VM_stack_top[0].type = ML_TYPE_MATRIX;
VM_stack_top[0].matrix = (ML_Matrix *) MEM_alloc(sizeof(ML_Matrix));
VM_stack_top[0].matrix->vector[0] = right;
VM_stack_top[0].matrix->vector[1] = up;
VM_stack_top[0].matrix->vector[2] = forwards;
VM_stack_top++;
}
break;
case ML_DO_PUSH_ZERO_VECTOR:
VM_CHECK_STACK_PUSH();
VM_stack_top[0].type = ML_TYPE_VECTOR;
VM_stack_top[0].vector = (ML_Vector *) MEM_alloc(sizeof(ML_Vector));
VM_stack_top[0].vector->x = 0.0F;
VM_stack_top[0].vector->y = 0.0F;
VM_stack_top[0].vector->z = 0.0F;
break;
case ML_DO_MATRIX:
VM_POP_STACK(3);
//
// Make sure all the arguments are vectors.
//
if (VM_stack_top[0].type != ML_TYPE_VECTOR ||
VM_stack_top[1].type != ML_TYPE_VECTOR ||
VM_stack_top[2].type != ML_TYPE_VECTOR)
{
//
// ERROR!
//
ASSERT(0);
}
{
ML_Data res;
res.type = ML_TYPE_MATRIX;
res.matrix = (ML_Matrix *) MEM_alloc(sizeof(ML_Matrix));
res.matrix->vector[0] = *VM_stack_top[0].vector;
res.matrix->vector[1] = *VM_stack_top[1].vector;
res.matrix->vector[2] = *VM_stack_top[2].vector;
VM_data_free(VM_stack_top[0]);
VM_data_free(VM_stack_top[1]);
VM_data_free(VM_stack_top[2]);
VM_stack_top[0] = res;
VM_stack_top++;
}
break;
case ML_DO_VECTOR:
VM_POP_STACK(3);
{
ML_Data res;
res.type = ML_TYPE_VECTOR;
res.vector = (ML_Vector *) MEM_alloc(sizeof(ML_Vector));
if (VM_stack_top[0].type == ML_TYPE_FLUMBER) {res.vector->x = VM_stack_top[0].flumber;}
else if (VM_stack_top[0].type == ML_TYPE_SLUMBER) {res.vector->x = (float) VM_stack_top[0].slumber;}
else
{
//
// ERROR!
//
ASSERT(0);
}
if (VM_stack_top[1].type == ML_TYPE_FLUMBER) {res.vector->y = VM_stack_top[1].flumber;}
else if (VM_stack_top[1].type == ML_TYPE_SLUMBER) {res.vector->y = (float) VM_stack_top[1].slumber;}
else
{
//
// ERROR!
//
ASSERT(0);
}
if (VM_stack_top[2].type == ML_TYPE_FLUMBER) {res.vector->z = VM_stack_top[2].flumber;}
else if (VM_stack_top[2].type == ML_TYPE_SLUMBER) {res.vector->z = (float) VM_stack_top[2].slumber;}
else
{
//
// ERROR!
//
ASSERT(0);
}
//
// No need to free the arguments... they are all just numbers.
//
VM_stack_top[0] = res;
VM_stack_top++;
}
break;
default:
ASSERT(0);
break;
}
}
}
void VM_run(CBYTE *fname)
{
SLONG i;
FILE *handle;
handle = fopen(fname, "rb");
if (handle == NULL)
{
fprintf(stderr, "Unable to open file \"%s\"\n", fname);
return;
}
//
// Load in the file header.
//
ML_Header mh;
if (fread(&mh, sizeof(mh), 1, handle) != 1) {goto file_error;}
//
// Valid file?
//
if (mh.version != ML_VERSION_NUMBER)
{
//
// This is an old file.
//
fprintf(stderr, "File \"%s\" has an old version number.\n", fname);
fclose(handle);
return;
}
//
// Allocate instruction memory and read in the instructions
// from the file.
//
ASSERT(mh.instructions_memory_in_bytes % sizeof(SLONG) == 0);
VM_code_max = (mh.instructions_memory_in_bytes + 32) / sizeof(SLONG);
VM_code = (SLONG *) malloc(sizeof(SLONG) * VM_code_max);
VM_code_upto = mh.instructions_memory_in_bytes / sizeof(SLONG);
VM_code_pointer = VM_code;
if (fread(VM_code, sizeof(UBYTE), mh.instructions_memory_in_bytes, handle) != mh.instructions_memory_in_bytes) goto file_error;
//
// Allocate static data and read in data from the file.
//
VM_data_max = mh.data_table_length_in_bytes;
VM_data = (UBYTE *) malloc(sizeof(UBYTE) * mh.data_table_length_in_bytes);
if (fread(VM_data, sizeof(UBYTE), mh.data_table_length_in_bytes, handle) != mh.data_table_length_in_bytes) goto file_error;
//
// Allocate the stack.
//
VM_stack_max = 2048;
VM_stack = (ML_Data *) malloc(sizeof(ML_Data) * VM_stack_max);
VM_stack_top = VM_stack;
VM_stack_base = NULL;
//
// Allocate the globals and initialise all of them to undefined.
//
VM_global_max = mh.num_globals + VM_EXTRA_GLOBAL_NUMBER;
VM_global_extra = mh.num_globals;
VM_global = (ML_Data *) malloc(sizeof(ML_Data) * VM_global_max);
memset(VM_global, 0, sizeof(ML_Data) * VM_global_max);
//
// Initialise the KEY[] array to FALSE.
//
for (i = 0; i < 256; i++)
{
VM_global[VM_EXTRA_GLOBAL_KEY(i)].type = ML_TYPE_BOOLEAN;
VM_global[VM_EXTRA_GLOBAL_KEY(i)].boolean = FALSE;
}
//
// Initialise the flip tick.
//
VM_flip_tick = 0;
//
// Start execution!
//
VM_execute();
return;
file_error:;
fclose(handle);
if (VM_code) {free(VM_code); VM_code = NULL;}
if (VM_stack) {free(VM_stack); VM_stack = NULL;}
return;
}