diff options
author | Paul Gilbert | 2019-04-16 19:30:44 -0700 |
---|---|---|
committer | Paul Gilbert | 2019-04-17 20:46:06 -0700 |
commit | ee8362cc073a02c91038add400f67dc2e6dba683 (patch) | |
tree | c35a9648b0890f08cc8898d516f5559e76d4ccda /engines | |
parent | 5965b02bcac9dbad6eb2c9b30a59edba0a3b1b5d (diff) | |
download | scummvm-rg350-ee8362cc073a02c91038add400f67dc2e6dba683.tar.gz scummvm-rg350-ee8362cc073a02c91038add400f67dc2e6dba683.tar.bz2 scummvm-rg350-ee8362cc073a02c91038add400f67dc2e6dba683.zip |
GLK: GLULXE: Added vm methods
Diffstat (limited to 'engines')
-rw-r--r-- | engines/glk/glulxe/glulxe.h | 84 | ||||
-rw-r--r-- | engines/glk/glulxe/serial.cpp | 4 | ||||
-rw-r--r-- | engines/glk/glulxe/vm.cpp | 325 | ||||
-rw-r--r-- | engines/glk/module.mk | 1 |
4 files changed, 391 insertions, 23 deletions
diff --git a/engines/glk/glulxe/glulxe.h b/engines/glk/glulxe/glulxe.h index 4044074e37..f7684f1830 100644 --- a/engines/glk/glulxe/glulxe.h +++ b/engines/glk/glulxe/glulxe.h @@ -38,7 +38,11 @@ typedef void (Glulxe::*UnicharHandler)(uint); * Glulxe game interpreter */ class Glulxe : public GlkAPI { -public: +private: + /** + * \defgroup vm fields + * @{ + */ CharHandler stream_char_handler; UnicharHandler stream_unichar_handler, glkio_unichar_han_ptr; @@ -67,6 +71,8 @@ public: uint protectstart, protectend; uint prevpc; + /**@}*/ + /** * \defgroup accel fields * @{ @@ -342,7 +348,7 @@ protected: * @{ */ - void stream_setup_unichar(void); + void stream_setup_unichar(); void nopio_char_han(unsigned char ch); void filio_char_han(unsigned char ch); @@ -385,8 +391,6 @@ public: * \defgroup Main access methods * @{ */ - void set_library_start_hook(void(*)(void)); - void set_library_autorestore_hook(void(*)(void)); /** * Display an error in the error window, and then exit. @@ -414,8 +418,6 @@ public: */ bool is_gamefile_valid(); - int locate_gamefile(int isblorb); - /**@}*/ /** @@ -423,13 +425,53 @@ public: * @{ */ - void setup_vm(void); - void finalize_vm(void); - void vm_restart(void); - uint change_memsize(uint newlen, int internal); + /** + * Read in the game file and build the machine, allocating all the memory necessary. + */ + void setup_vm(); + + /** + * Deallocate all the memory and shut down the machine. + */ + void finalize_vm(); + + /** + * Put the VM into a state where it's ready to begin executing the game. This is called + * both at startup time, and when the machine performs a "restart" opcode. + */ + void vm_restart(); + + /** + * Change the size of the memory map. This may not be available at all; #define FIXED_MEMSIZE + * if you want the interpreter to unconditionally refuse. The internal flag should be true only + * when the heap-allocation system is calling. Returns 0 for success; otherwise, the operation failed. + */ + uint change_memsize(uint newlen, bool internal); + + /** + * If addr is 0, pop N arguments off the stack, and put them in an array. If non-0, take N arguments + * from that main memory address instead. This has to dynamically allocate if there are more than + * 32 arguments, but that shouldn't be a problem. + */ uint *pop_arguments(uint count, uint addr); + + /** + * Make sure that count bytes beginning with addr all fall within the current memory map. + * This is called at every memory (read) access if VERIFY_MEMORY_ACCESS is defined in the header file. + */ void verify_address(uint addr, uint count); + + /** + * Make sure that count bytes beginning with addr all fall within RAM. This is called at every memory + * write if VERIFY_MEMORY_ACCESS is defined in the header file. + */ void verify_address_write(uint addr, uint count); + + /** + * Make sure that an array of count elements (size bytes each), starting at addr, does not fall + * outside the memory map. This goes to some trouble that verify_address() does not, because we need + * to be wary of lengths near -- or beyond -- 0x7FFFFFFF. + */ void verify_array_addresses(uint addr, uint count, uint size); /**@}*/ @@ -527,7 +569,7 @@ public: * Set the heap state to inactive, and free the block lists. This is called when the game * starts or restarts. */ - void heap_clear(void); + void heap_clear(); /** * Returns whether the heap is active. @@ -637,7 +679,7 @@ public: void *glulx_realloc(void *ptr, uint len); void glulx_free(void *ptr); void glulx_setrandom(uint seed); - uint glulx_random(void); + uint glulx_random(); void glulx_sort(void *addr, int count, int size, int(*comparefunc)(void *p1, void *p2)); @@ -723,16 +765,16 @@ public: */ void setup_profile(strid_t stream, char *filename); - int init_profile(void); + int init_profile(); void profile_set_call_counts(int flag); #if VM_PROFILING uint profile_opcount; #define profile_tick() (profile_opcount++) - int profile_profiling_active(void); + int profile_profiling_active(); void profile_in(uint addr, uint stackuse, int accel); void profile_out(uint stackuse); void profile_fail(char *reason); - void profile_quit(void); + void profile_quit(); #else /* VM_PROFILING */ #define profile_tick() (0) #define profile_profiling_active() (0) @@ -751,15 +793,15 @@ public: void debugger_set_start_trap(int flag); void debugger_set_quit_trap(int flag); void debugger_set_crash_trap(int flag); - void debugger_check_story_file(void); - void debugger_setup_start_state(void); - int debugger_ever_invoked(void); + void debugger_check_story_file(); + void debugger_setup_start_state(); + int debugger_ever_invoked(); int debugger_cmd_handler(char *cmd); void debugger_cycle_handler(int cycle); void debugger_check_func_breakpoint(uint addr); void debugger_block_and_debug(char *msg); void debugger_handle_crash(char *msg); - void debugger_handle_quit(void); + void debugger_handle_quit(); #else /* VM_DEBUGGER */ #define debugger_tick() (0) #define debugger_check_story_file() (0) @@ -805,7 +847,7 @@ public: /* #define FLOAT_NOT_NATIVE (1) */ /* float.c */ - int init_float(void); + int init_float(); uint encode_float(gfloat32 val); gfloat32 decode_float(uint val); @@ -884,7 +926,7 @@ public: /** * Get the current table address. */ - uint stream_get_table(void); + uint stream_get_table(); /** * Set the current table address, and rebuild decoding cache. diff --git a/engines/glk/glulxe/serial.cpp b/engines/glk/glulxe/serial.cpp index 66464e80f9..6d662166c4 100644 --- a/engines/glk/glulxe/serial.cpp +++ b/engines/glk/glulxe/serial.cpp @@ -517,7 +517,7 @@ int Glulxe::write_buffer(dest_t *dest, const byte *ptr, uint len) { return 0; } -int Glulxe::read_buffer(dest_t *dest, unsigned char *ptr, uint len) { +int Glulxe::read_buffer(dest_t *dest, byte *ptr, uint len) { uint newlen; if (dest->ismem) { @@ -546,7 +546,7 @@ int Glulxe::write_short(dest_t *dest, uint16 val) { return write_buffer(dest, buf, 2); } -int Glulxe::write_byte(dest_t *dest, unsigned char val) { +int Glulxe::write_byte(dest_t *dest, byte val) { return write_buffer(dest, &val, 1); } diff --git a/engines/glk/glulxe/vm.cpp b/engines/glk/glulxe/vm.cpp new file mode 100644 index 0000000000..f332c92248 --- /dev/null +++ b/engines/glk/glulxe/vm.cpp @@ -0,0 +1,325 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "engines/glk/glulxe/glulxe.h" + +namespace Glk { +namespace Glulxe { + +void Glulxe::setup_vm() { + unsigned char buf[4 * 7]; + int res; + + pc = 0; /* Clear this, so that error messages are cleaner. */ + prevpc = 0; + + /* Read in all the size constants from the game file header. */ + + stream_char_handler = NULL; + stream_unichar_handler = NULL; + + glk_stream_set_position(gamefile, gamefile_start+8, seekmode_Start); + res = glk_get_buffer_stream(gamefile, (char *)buf, 4 * 7); + 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); + origstringtable = Read4(buf+20); + checksum = Read4(buf+24); + + /* Set the protection range to (0, 0), meaning "off". */ + protectstart = 0; + protectend = 0; + + /* Do a few sanity checks. */ + + if ((ramstart & 0xFF) + || (endgamefile & 0xFF) + || (origendmem & 0xFF) + || (stacksize & 0xFF)) { + nonfatal_warning("One of the segment boundaries in the header is not " + "256-byte aligned."); + } + + if (endgamefile != gamefile_len) { + nonfatal_warning("The gamefile length does not match the header " + "endgamefile length."); + } + + 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."); + } + + /* Allocate main memory and the stack. This is where memory allocation + errors are most likely to occur. */ + 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."); + } + stringtable = 0; + + /* Initialize various other things in the terp. */ + init_operands(); + init_serial(); + + /* Set up the initial machine state. */ + vm_restart(); + + /* If the debugger is compiled in, check that the debug data matches + the game. (This only prints warnings for mismatch.) */ + debugger_check_story_file(); + /* Also, set up any start-time debugger state. This may do a block- + and-debug, if the user has requested that. */ + debugger_setup_start_state(); +} + +void Glulxe::finalize_vm() { + stream_set_table(0); + + if (memmap) { + glulx_free(memmap); + memmap = NULL; + } + if (stack) { + glulx_free(stack); + stack = NULL; + } + + final_serial(); +} + +void Glulxe::vm_restart() { + uint lx; + int res; + int bufpos; + char buf[0x100]; + + /* Deactivate the heap (if it was active). */ + heap_clear(); + + /* Reset memory to the original size. */ + lx = change_memsize(origendmem, false); + if (lx) + fatal_error("Memory could not be reset to its original size."); + + /* Load in all of main memory. We do this in 256-byte chunks, because + why rely on OS stream buffering? */ + glk_stream_set_position(gamefile, gamefile_start, seekmode_Start); + bufpos = 0x100; + + for (lx=0; lx<endgamefile; lx++) { + if (bufpos >= 0x100) { + int count = glk_get_buffer_stream(gamefile, buf, 0x100); + if (count != 0x100) { + fatal_error("The game file ended unexpectedly."); + } + bufpos = 0; + } + + res = buf[bufpos++]; + if (lx >= protectstart && lx < protectend) + continue; + memmap[lx] = res; + } + for (lx=endgamefile; lx<origendmem; lx++) { + memmap[lx] = 0; + } + + /* Reset all the registers */ + stackptr = 0; + frameptr = 0; + pc = 0; + prevpc = 0; + stream_set_iosys(0, 0); + stream_set_table(origstringtable); + valstackbase = 0; + localsbase = 0; + + /* Note that we do not reset the protection range. */ + + /* Push the first function call. (No arguments.) */ + enter_function(startfuncaddr, 0, NULL); + + /* We're now ready to execute. */ +} + +uint Glulxe::change_memsize(uint newlen, bool internal) { + uint lx; + unsigned char *newmemmap; + + if (newlen == endmem) + return 0; + +#ifdef FIXED_MEMSIZE + return 1; +#else /* FIXED_MEMSIZE */ + + 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) { + /* The old block is still in place, unchanged. */ + return 1; + } + memmap = newmemmap; + + if (newlen > endmem) { + for (lx=endmem; lx<newlen; lx++) { + memmap[lx] = 0; + } + } + + endmem = newlen; + + return 0; + +#endif /* FIXED_MEMSIZE */ +} + +uint *Glulxe::pop_arguments(uint count, uint addr) { + int ix; + uint argptr; + uint *array; + + #define MAXARGS (32) + static uint statarray[MAXARGS]; + static uint *dynarray = NULL; + static uint dynarray_size = 0; + + if (count == 0) + return NULL; + + if (count <= MAXARGS) { + /* Store in the static array. */ + array = statarray; + } + else { + if (!dynarray) { + dynarray_size = count+8; + dynarray = (uint *)glulx_malloc(sizeof(uint) * dynarray_size); + if (!dynarray) + fatal_error("Unable to allocate function arguments."); + array = dynarray; + } + else { + if (dynarray_size >= count) { + /* It fits. */ + array = dynarray; + } + else { + dynarray_size = count+8; + dynarray = (uint *)glulx_realloc(dynarray, sizeof(uint) * 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 Glulxe::verify_address(uint addr, uint 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 Glulxe::verify_address_write(uint addr, uint 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 Glulxe::verify_array_addresses(uint addr, uint count, uint size) { + uint bytecount; + if (addr >= endmem) + fatal_error_i("Memory access out of range", addr); + + if (count == 0) + return; + bytecount = count*size; + + /* If just multiplying by the element size overflows, we have trouble. */ + if (bytecount < count) + fatal_error_i("Memory access way too long", addr); + + /* If the byte length by itself is too long, or if its end overflows, + we have trouble. */ + if (bytecount > endmem || addr+bytecount < addr) + fatal_error_i("Memory access much too long", addr); + /* The simple length test. */ + if (addr+bytecount > endmem) + fatal_error_i("Memory access too long", addr); +} + +} // End of namespace Glulxe +} // End of namespace Glk diff --git a/engines/glk/module.mk b/engines/glk/module.mk index 247f1d837e..be66cafd41 100644 --- a/engines/glk/module.mk +++ b/engines/glk/module.mk @@ -70,6 +70,7 @@ MODULE_OBJS := \ glulxe/search.o \ glulxe/serial.o \ glulxe/string.o \ + glulxe/vm.o \ magnetic/detection.o \ magnetic/magnetic.o \ scott/detection.o \ |