1 /* vm.c: Glulxe code related to the VM overall. Also miscellaneous stuff.
2 Designed by Andrew Plotkin <erkyrath@eblong.com>
3 http://eblong.com/zarf/glulx/index.html
9 /* The memory blocks which contain VM main memory and the stack. */
10 unsigned char *memmap = NULL;
11 unsigned char *stack = NULL;
13 /* Various memory addresses which are useful. These are loaded in from
14 the game file header. */
20 glui32 origstringtable;
23 /* The VM registers. */
31 glui32 protectstart, protectend;
33 void (*stream_char_handler)(unsigned char ch);
34 void (*stream_unichar_handler)(glui32 ch);
37 Read in the game file and build the machine, allocating all the memory
42 unsigned char buf[4 * 7];
45 pc = 0; /* Clear this, so that error messages are cleaner. */
47 /* Read in all the size constants from the game file header. */
49 stream_char_handler = NULL;
50 stream_unichar_handler = NULL;
52 glk_stream_set_position(gamefile, gamefile_start+8, seekmode_Start);
53 res = glk_get_buffer_stream(gamefile, (char *)buf, 4 * 7);
55 ramstart = Read4(buf+0);
56 endgamefile = Read4(buf+4);
57 origendmem = Read4(buf+8);
58 stacksize = Read4(buf+12);
59 startfuncaddr = Read4(buf+16);
60 origstringtable = Read4(buf+20);
61 checksum = Read4(buf+24);
63 /* Set the protection range to (0, 0), meaning "off". */
67 /* Do a few sanity checks. */
70 || (endgamefile & 0xFF)
71 || (origendmem & 0xFF)
72 || (stacksize & 0xFF)) {
73 nonfatal_warning("One of the segment boundaries in the header is not "
77 if (ramstart < 0x100 || endgamefile < ramstart || origendmem < endgamefile) {
78 fatal_error("The segment boundaries in the header are in an impossible "
81 if (stacksize < 0x100) {
82 fatal_error("The stack size in the header is too small.");
85 /* Allocate main memory and the stack. This is where memory allocation
86 errors are most likely to occur. */
88 memmap = (unsigned char *)glulx_malloc(origendmem);
90 fatal_error("Unable to allocate Glulx memory space.");
92 stack = (unsigned char *)glulx_malloc(stacksize);
96 fatal_error("Unable to allocate Glulx stack space.");
100 /* Initialize various other things in the terp. */
105 /* Set up the initial machine state. */
110 Deallocate all the memory and shut down the machine.
125 Put the VM into a state where it's ready to begin executing the
126 game. This is called both at startup time, and when the machine
127 performs a "restart" opcode.
134 /* Deactivate the heap (if it was active). */
137 /* Reset memory to the original size. */
138 lx = change_memsize(origendmem, FALSE);
140 fatal_error("Memory could not be reset to its original size.");
142 /* Load in all of main memory */
143 glk_stream_set_position(gamefile, gamefile_start, seekmode_Start);
144 for (lx=0; lx<endgamefile; lx++) {
145 res = glk_get_char_stream(gamefile);
147 fatal_error("The game file ended unexpectedly.");
149 if (lx >= protectstart && lx < protectend)
153 for (lx=endgamefile; lx<origendmem; lx++) {
157 /* Reset all the registers */
161 stream_set_iosys(0, 0);
162 stream_set_table(origstringtable);
166 /* Note that we do not reset the protection range. */
168 /* Push the first function call. (No arguments.) */
169 enter_function(startfuncaddr, 0, NULL);
171 /* We're now ready to execute. */
175 Change the size of the memory map. This may not be available at
176 all; #define FIXED_MEMSIZE if you want the interpreter to
177 unconditionally refuse. The internal flag should be true only when
178 the heap-allocation system is calling.
179 Returns 0 for success; otherwise, the operation failed.
181 glui32 change_memsize(glui32 newlen, int internal)
184 unsigned char *newmemmap;
186 if (newlen == endmem)
191 #else /* FIXED_MEMSIZE */
193 if ((!internal) && heap_is_active())
194 fatal_error("Cannot resize Glulx memory space while heap is active.");
196 if (newlen < origendmem)
197 fatal_error("Cannot resize Glulx memory space smaller than it started.");
200 fatal_error("Can only resize Glulx memory space to a 256-byte boundary.");
202 newmemmap = (unsigned char *)glulx_realloc(memmap, newlen);
204 /* The old block is still in place, unchanged. */
209 if (newlen > endmem) {
210 for (lx=endmem; lx<newlen; lx++) {
219 #endif /* FIXED_MEMSIZE */
223 If addr is 0, pop N arguments off the stack, and put them in an array.
224 If non-0, take N arguments from that main memory address instead.
225 This has to dynamically allocate if there are more than 32 arguments,
226 but that shouldn't be a problem.
228 glui32 *pop_arguments(glui32 count, glui32 addr)
235 static glui32 statarray[MAXARGS];
236 static glui32 *dynarray = NULL;
237 static glui32 dynarray_size = 0;
242 if (count <= MAXARGS) {
243 /* Store in the static array. */
248 dynarray_size = count+8;
249 dynarray = glulx_malloc(sizeof(glui32) * dynarray_size);
251 fatal_error("Unable to allocate function arguments.");
255 if (dynarray_size >= count) {
260 dynarray_size = count+8;
261 dynarray = glulx_realloc(dynarray, sizeof(glui32) * dynarray_size);
263 fatal_error("Unable to reallocate function arguments.");
270 if (stackptr < valstackbase+4*count)
271 fatal_error("Stack underflow in arguments.");
273 for (ix=0; ix<count; ix++) {
274 argptr = stackptr+4*((count-1)-ix);
275 array[ix] = Stk4(argptr);
279 for (ix=0; ix<count; ix++) {
280 array[ix] = Mem4(addr);
289 Make sure that count bytes beginning with addr all fall within the
290 current memory map. This is called at every memory access if
291 VERIFY_MEMORY_ACCESS is defined in the header file.
293 void verify_address(glui32 addr, glui32 count)
296 fatal_error_i("Memory access out of range", addr);
300 fatal_error_i("Memory access out of range", addr);