static void release_temp_i_array(glui32 *arr, glui32 addr, glui32 len, int passout) { arrayref_t *arref = NULL; arrayref_t **aptr; glui32 ix, val, addr2; if (arr) { for (aptr=(&arrays); (*aptr); aptr=(&((*aptr)->next))) { if ((*aptr)->array == arr) break; } arref = *aptr; if (!arref) fatalError("Unable to re-find array argument in Glk call."); if (arref->addr != addr || arref->len != len) fatalError("Mismatched array argument in Glk call."); if (arref->retained) { return; } *aptr = arref->next; arref->next = NULL; if (passout) { for (ix=0, addr2=addr; ix<len; ix++, addr2+=4) { val = arr[ix]; memWrite32(addr2, val); } } glulx_free(arr); glulx_free(arref); } }
void Glulxe::release_temp_c_array(char *arr, uint addr, uint len, int passout) { arrayref_t *arref = nullptr; arrayref_t **aptr; uint ix, val, addr2; if (arr) { for (aptr = (&arrays); (*aptr); aptr = (&((*aptr)->next))) { if ((*aptr)->array == arr) break; } arref = *aptr; if (!arref) error("Unable to re-find array argument in Glk call."); if (arref->addr != addr || arref->len != len) error("Mismatched array argument in Glk call."); if (arref->retained) { return; } *aptr = arref->next; arref->next = nullptr; if (passout) { for (ix = 0, addr2 = addr; ix < len; ix++, addr2 += 1) { val = arr[ix]; MemW1(addr2, val); } } glulx_free(arr); glulx_free(arref); } }
static void glulxe_retained_unregister(void *array, glui32 len, char *typecode, gidispatch_rock_t objrock) { arrayref_t *arref = NULL; arrayref_t **aptr; glui32 ix, addr2, val; int elemsize = 0; if (typecode[4] == 'C') elemsize = 1; else if (typecode[4] == 'I') elemsize = 4; if (!elemsize || array == NULL) { return; } for (aptr=(&arrays); (*aptr); aptr=(&((*aptr)->next))) { if ((*aptr)->array == array) break; } arref = *aptr; if (!arref) { if (objrock.num == 0) return; fatalError("Unable to re-find array argument in Glk call."); } if (arref != objrock.ptr) fatalError("Mismatched array reference in Glk call."); if (!arref->retained) fatalError("Unretained array reference in Glk call."); if (arref->elemsize != elemsize || arref->len != len) fatalError("Mismatched array argument in Glk call."); *aptr = arref->next; arref->next = NULL; if (elemsize == 1) { for (ix=0, addr2=arref->addr; ix<arref->len; ix++, addr2+=1) { val = ((char *)array)[ix]; memWrite8(addr2, val); } } else if (elemsize == 4) { for (ix=0, addr2=arref->addr; ix<arref->len; ix++, addr2+=4) { val = ((glui32 *)array)[ix]; memWrite32(addr2, val); } } glulx_free(array); glulx_free(arref); }
/* final_serial(): Clean up memory when the VM shuts down. */ void final_serial() { if (undo_chain) { int ix; for (ix=0; ix<undo_chain_num; ix++) { glulx_free(undo_chain[ix]); } glulx_free(undo_chain); } undo_chain = NULL; undo_chain_size = 0; undo_chain_num = 0; }
void Glulxe::glulxe_retained_unregister(void *array, uint len, const char *typecode, gidispatch_rock_t objrock) { arrayref_t *arref = nullptr; arrayref_t **aptr; uint ix, addr2, val; uint elemsize = 0; // TODO: See if original GLULXE has code I'm overlooking to cleanly close everything before freeing memmap if (!memmap) return; if (typecode[4] == 'C') elemsize = 1; else if (typecode[4] == 'I') elemsize = 4; if (!elemsize || array == nullptr) { return; } for (aptr = (&arrays); (*aptr); aptr = (&((*aptr)->next))) { if ((*aptr)->array == array) break; } arref = *aptr; if (!arref) error("Unable to re-find array argument in Glk call."); if (arref != objrock.ptr) error("Mismatched array reference in Glk call."); if (!arref->retained) error("Unretained array reference in Glk call."); if (arref->elemsize != elemsize || arref->len != len) error("Mismatched array argument in Glk call."); *aptr = arref->next; arref->next = nullptr; if (elemsize == 1) { for (ix = 0, addr2 = arref->addr; ix < arref->len; ix++, addr2 += 1) { val = ((char *)array)[ix]; MemW1(addr2, val); } } else if (elemsize == 4) { for (ix = 0, addr2 = arref->addr; ix < arref->len; ix++, addr2 += 4) { val = ((uint *)array)[ix]; MemW4(addr2, val); } } glulx_free(array); glulx_free(arref); }
void Glulxe::classes_remove(int classid, void *obj) { classtable_t *ctab; classref_t *cref; classref_t **crefp; gidispatch_rock_t objrock; if (classid < 0 || classid >= num_classes) return; ctab = classes[classid]; objrock = gidispatch_get_objrock(obj, classid); cref = (classref_t *)objrock.ptr; if (!cref) return; crefp = &(ctab->bucket[cref->bucknum]); for (; *crefp; crefp = &((*crefp)->next)) { if ((*crefp) == cref) { *crefp = cref->next; if (!cref->obj) { nonfatal_warning("attempt to free nullptr object!"); } cref->obj = nullptr; cref->id = 0; cref->next = nullptr; glulx_free(cref); return; } } return; }
/* Delete a Glk object from the appropriate hash table. */ static void classes_remove(int classid, void *obj) { classtable_t *ctab; classref_t *cref; classref_t **crefp; gidispatch_rock_t objrock; if (classid < 0 || classid >= num_classes) return; ctab = git_classes[classid]; objrock = gidispatch_get_objrock(obj, classid); cref = objrock.ptr; if (!cref) return; crefp = &(ctab->bucket[cref->bucknum]); for (; *crefp; crefp = &((*crefp)->next)) { if ((*crefp) == cref) { *crefp = cref->next; cref->obj = NULL; cref->id = 0; cref->next = NULL; glulx_free(cref); return; } } return; }
/* perform_restoreundo(): Pull a state pointer from the undo chain. This returns 0 on success, 1 on failure. Note that if it succeeds, the frameptr, localsbase, and valstackbase registers are invalid; they must be rebuilt from the stack. */ glui32 perform_restoreundo() { dest_t dest; glui32 res, val; glui32 heapsumlen = 0; glui32 *heapsumarr = NULL; if (undo_chain_size == 0 || undo_chain_num == 0) return 1; dest.ismem = TRUE; dest.size = 0; dest.pos = 0; dest.ptr = undo_chain[0]; dest.str = NULL; res = 0; if (res == 0) { res = read_long(&dest, &val); } if (res == 0) { res = read_memstate(&dest, val); } if (res == 0) { res = read_long(&dest, &val); } if (res == 0) { res = read_heapstate(&dest, val, FALSE, &heapsumlen, &heapsumarr); } if (res == 0) { res = read_long(&dest, &val); } if (res == 0) { res = read_stackstate(&dest, val, FALSE); } /* ### really, many of the failure modes of those calls ought to cause fatal errors. The stack or main memory may be damaged now. */ if (res == 0) { if (heapsumarr) res = heap_apply_summary(heapsumlen, heapsumarr); } if (res == 0) { /* It worked. */ if (undo_chain_size > 1) memmove(undo_chain, undo_chain+1, (undo_chain_size-1) * sizeof(unsigned char *)); undo_chain_num -= 1; glulx_free(dest.ptr); dest.ptr = NULL; } else { /* It didn't work. */ dest.ptr = NULL; } return res; }
void profile_out(glui32 stackuse) { frame_t *fra; function_t *func; struct timeval now, runtime; glui32 runops; if (!profiling_active) return; /* printf("### OUT\n"); */ if (!current_frame) fatal_error("Profiler: stack underflow."); gettimeofday(&now, NULL); fra = current_frame; func = fra->func; timersub(&now, &fra->entry_time, &runtime); runops = profile_opcount - fra->entry_op; timeradd(&runtime, &func->self_time, &func->self_time); timersub(&func->self_time, &fra->children_time, &func->self_time); func->self_ops += runops; func->self_ops -= fra->children_ops; if (func->max_depth < fra->depth) func->max_depth = fra->depth; if (func->max_stack_use < stackuse) func->max_stack_use = stackuse; if (fra->parent) { timeradd(&runtime, &fra->parent->children_time, &fra->parent->children_time); fra->parent->children_ops += runops; } if (!func->entry_depth) fatal_error("Profiler: function entry underflow."); func->entry_depth -= 1; if (!func->entry_depth) { timersub(&now, &func->entry_start_time, &runtime); timerclear(&func->entry_start_time); runops = profile_opcount - func->entry_start_op; func->entry_start_op = 0; timeradd(&runtime, &func->total_time, &func->total_time); func->total_ops += runops; } current_frame = fra->parent; fra->parent = NULL; glulx_free(fra); }
static void release_temp_ptr_array(void **arr, glui32 addr, glui32 len, int objclass, int passout) { arrayref_t *arref = NULL; arrayref_t **aptr; glui32 ix, val, addr2; if (arr) { for (aptr=(&arrays); (*aptr); aptr=(&((*aptr)->next))) { if ((*aptr)->array == arr) break; } arref = *aptr; if (!arref) fatalError("Unable to re-find array argument in Glk call."); if (arref->addr != addr || arref->len != len) fatalError("Mismatched array argument in Glk call."); if (arref->retained) { return; } *aptr = arref->next; arref->next = NULL; if (passout) { for (ix=0, addr2=addr; ix<len; ix++, addr2+=4) { void *opref = arr[ix]; if (opref) { gidispatch_rock_t objrock = gidispatch_get_objrock(opref, objclass); val = ((classref_t *)objrock.ptr)->id; } else { val = 0; } memWrite32(addr2, val); } } glulx_free(arr); glulx_free(arref); } }
/* final_serial(): Clean up memory when the VM shuts down. */ void final_serial() { if (undo_chain) { int ix; for (ix=0; ix<undo_chain_num; ix++) { glulx_free(undo_chain[ix]); } glulx_free(undo_chain); } undo_chain = NULL; undo_chain_size = 0; undo_chain_num = 0; #ifdef SERIALIZE_CACHE_RAM if (ramcache) { glulx_free(ramcache); ramcache = NULL; } #endif /* SERIALIZE_CACHE_RAM */ }
void profile_quit() { int bucknum; function_t *func; char linebuf[512]; frefid_t profref; strid_t profstr; while (current_frame) { profile_out(); } profref = glk_fileref_create_by_name(fileusage_BinaryMode|fileusage_Data, "profile-raw", 0); if (!profref) fatal_error("Profiler: unable to create profile-raw file"); profstr = glk_stream_open_file(profref, filemode_Write, 0); glk_put_string_stream(profstr, "<profile>\n"); for (bucknum=0; bucknum<FUNC_HASH_SIZE; bucknum++) { char total_buf[20], self_buf[20]; for (func = functions[bucknum]; func; func=func->hash_next) { /* ### sprintf(linebuf, "function %lx called %ld times, total ops %ld, total time %s, self ops %ld, self time %s\n", func->addr, func->call_count, func->total_ops, timeprint(&func->total_time, total_buf), func->self_ops, timeprint(&func->self_time, self_buf)); ### */ sprintf(linebuf, " <function addr=\"%lx\" call_count=\"%ld\" accel_count=\"%ld\" total_ops=\"%ld\" total_time=\"%s\" self_ops=\"%ld\" self_time=\"%s\" />\n", func->addr, func->call_count, func->accel_count, func->total_ops, timeprint(&func->total_time, total_buf), func->self_ops, timeprint(&func->self_time, self_buf)); glk_put_string_stream(profstr, linebuf); } } glk_put_string_stream(profstr, "</profile>\n"); glk_stream_close(profstr, NULL); glulx_free(functions); functions = NULL; }
static glui32 write_heapstate(dest_t *dest, int portable) { glui32 res; glui32 sumlen; glui32 *sumarray; res = heap_get_summary(&sumlen, &sumarray); if (res) return res; if (!sumarray) return 0; /* no heap */ res = write_heapstate_sub(sumlen, sumarray, dest, portable); glulx_free(sumarray); return res; }
/* heap_clear(): Set the heap state to inactive, and free the block lists. This is called when the game starts or restarts. */ void heap_clear() { while (heap_head) { heapblock_t *blo = heap_head; heap_head = blo->next; blo->next = NULL; blo->prev = NULL; glulx_free(blo); } heap_tail = NULL; if (heap_start) { glui32 res = resizeMemory(heap_start, 1); if (res) fatalError("Unable to revert memory size when deactivating heap."); } heap_start = 0; alloc_count = 0; /* heap_sanity_check(); */ }
/* perform_saveundo(): Add a state pointer to the undo chain. This returns 0 on success, 1 on failure. */ glui32 perform_saveundo() { dest_t dest; glui32 res; glui32 memstart, memlen, heapstart, heaplen, stackstart, stacklen; /* The format for undo-saves is simpler than for saves on disk. We just have a memory chunk, a heap chunk, and a stack chunk, in that order. We skip the IFF chunk headers (although the size fields are still there.) We also don't bother with IFF's 16-bit alignment. */ if (undo_chain_size == 0) return 1; dest.ismem = TRUE; dest.size = 0; dest.pos = 0; dest.ptr = NULL; dest.str = NULL; res = 0; if (res == 0) { res = write_long(&dest, 0); /* space for chunk length */ } if (res == 0) { memstart = dest.pos; res = write_memstate(&dest); memlen = dest.pos - memstart; } if (res == 0) { res = write_long(&dest, 0); /* space for chunk length */ } if (res == 0) { heapstart = dest.pos; res = write_heapstate(&dest, FALSE); heaplen = dest.pos - heapstart; } if (res == 0) { res = write_long(&dest, 0); /* space for chunk length */ } if (res == 0) { stackstart = dest.pos; res = write_stackstate(&dest, FALSE); stacklen = dest.pos - stackstart; } if (res == 0) { /* Trim it down to the perfect size. */ dest.ptr = glulx_realloc(dest.ptr, dest.pos); if (!dest.ptr) res = 1; } if (res == 0) { res = reposition_write(&dest, memstart-4); } if (res == 0) { res = write_long(&dest, memlen); } if (res == 0) { res = reposition_write(&dest, heapstart-4); } if (res == 0) { res = write_long(&dest, heaplen); } if (res == 0) { res = reposition_write(&dest, stackstart-4); } if (res == 0) { res = write_long(&dest, stacklen); } if (res == 0) { /* It worked. */ if (undo_chain_num >= undo_chain_size) { glulx_free(undo_chain[undo_chain_num-1]); undo_chain[undo_chain_num-1] = NULL; } if (undo_chain_size > 1) memmove(undo_chain+1, undo_chain, (undo_chain_size-1) * sizeof(unsigned char *)); undo_chain[0] = dest.ptr; if (undo_chain_num < undo_chain_size) undo_chain_num += 1; dest.ptr = NULL; } else { /* It didn't work. */ if (dest.ptr) { glulx_free(dest.ptr); dest.ptr = NULL; } } return res; }
static void ReleaseVMString (char * ptr) { glulx_free (ptr); }
static void ReleaseVMUstring (glui32 * ptr) { glulx_free (ptr); }
void Glulxe::prepare_glk_args(const char *proto, dispatch_splot_t *splot) { static gluniversal_t *garglist = nullptr; static int garglist_size = 0; int ix; int numwanted, numvargswanted, maxargs; const char *cx; cx = proto; numwanted = 0; while (*cx >= '0' && *cx <= '9') { numwanted = 10 * numwanted + (*cx - '0'); cx++; } splot->numwanted = numwanted; maxargs = 0; numvargswanted = 0; for (ix = 0; ix < numwanted; ix++) { int isref, passin, passout, nullok, isarray, isretained, isreturn; cx = read_prefix(cx, &isref, &isarray, &passin, &passout, &nullok, &isretained, &isreturn); if (isref) { maxargs += 2; } else { maxargs += 1; } if (!isreturn) { if (isarray) { numvargswanted += 2; } else { numvargswanted += 1; } } if (*cx == 'I' || *cx == 'C') { cx += 2; } else if (*cx == 'Q') { cx += 2; } else if (*cx == 'S' || *cx == 'U') { cx += 1; } else if (*cx == '[') { int refdepth, nwx; cx++; nwx = 0; while (*cx >= '0' && *cx <= '9') { nwx = 10 * nwx + (*cx - '0'); cx++; } maxargs += nwx; /* This is *only* correct because all structs contain plain values. */ refdepth = 1; while (refdepth > 0) { if (*cx == '[') refdepth++; else if (*cx == ']') refdepth--; cx++; } } else { error("Illegal format string."); } } if (*cx != ':' && *cx != '\0') error("Illegal format string."); splot->maxargs = maxargs; if (splot->numvargs != numvargswanted) error("Wrong number of arguments to Glk function."); if (garglist && garglist_size < maxargs) { glulx_free(garglist); garglist = nullptr; garglist_size = 0; } if (!garglist) { garglist_size = maxargs + 16; garglist = (gluniversal_t *)glulx_malloc(garglist_size * sizeof(gluniversal_t)); } if (!garglist) error("Unable to allocate storage for Glk arguments."); splot->garglist = garglist; }
void profile_quit() { int bucknum; function_t *func; char linebuf[512]; strid_t profstr; if (!profiling_active) return; while (current_frame) { profile_out(0); } if (profiling_stream) { profstr = profiling_stream; } else if (profiling_filename) { frefid_t profref = glk_fileref_create_by_name(fileusage_BinaryMode|fileusage_Data, profiling_filename, 0); if (!profref) fatal_error_2("Profiler: unable to create profile output fileref", profiling_filename); profstr = glk_stream_open_file(profref, filemode_Write, 0); } else { fatal_error("Profiler: no profile output handle!"); } glk_put_string_stream(profstr, "<profile>\n"); for (bucknum=0; bucknum<FUNC_HASH_SIZE; bucknum++) { char total_buf[20], self_buf[20]; callcount_t *cc; for (func = functions[bucknum]; func; func=func->hash_next) { /* ### sprintf(linebuf, "function %lx called %ld times, total ops %ld, total time %s, self ops %ld, self time %s\n", func->addr, func->call_count, func->total_ops, timeprint(&func->total_time, total_buf), func->self_ops, timeprint(&func->self_time, self_buf)); ### */ sprintf(linebuf, " <function addr=\"%lx\" call_count=\"%ld\" accel_count=\"%ld\" total_ops=\"%ld\" total_time=\"%s\" self_ops=\"%ld\" self_time=\"%s\" max_depth=\"%ld\" max_stack_use=\"%ld\" />\n", (unsigned long)func->addr, (long)func->call_count, (long)func->accel_count, (long)func->total_ops, timeprint(&func->total_time, total_buf), (long)func->self_ops, timeprint(&func->self_time, self_buf), (long)func->max_depth, (long)func->max_stack_use); glk_put_string_stream(profstr, linebuf); for (cc = func->outcalls; cc; cc = cc->next) { sprintf(linebuf, " <calls fromaddr=\"%lx\" toaddr=\"%lx\" count=\"%ld\" />\n", (unsigned long)func->addr, (unsigned long)cc->toaddr, (long)cc->count); glk_put_string_stream(profstr, linebuf); } } } glk_put_string_stream(profstr, "</profile>\n"); glk_stream_close(profstr, NULL); /* ### Ought to free the function structures, not just the hash array. */ glulx_free(functions); functions = NULL; }
/* heap_alloc(): Allocate a block. If necessary, activate the heap and/or extend memory. Returns the memory address of the block, or 0 if the operation failed. */ glui32 heap_alloc(glui32 len) { heapblock_t *blo, *newblo; if (len <= 0) fatalError("Heap allocation length must be positive."); blo = heap_head; while (blo) { if (blo->isfree && blo->len >= len) break; if (!blo->isfree) { blo = blo->next; continue; } if (!blo->next || !blo->next->isfree) { blo = blo->next; continue; } /* This is a free block, but the next block in the list is also free, so we "advance" by merging rather than by going to blo->next. */ newblo = blo->next; blo->len += newblo->len; if (newblo->next) { blo->next = newblo->next; newblo->next->prev = blo; } else { blo->next = NULL; heap_tail = blo; } newblo->next = NULL; newblo->prev = NULL; glulx_free(newblo); newblo = NULL; continue; } if (!blo) { /* No free area is visible on the list. Try extending memory. How much? Double the heap size, or by 256 bytes, or by the memory length requested -- whichever is greatest. */ glui32 res; glui32 extension; glui32 oldendmem = gEndMem; extension = 0; if (heap_start) extension = gEndMem - heap_start; if (extension < len) extension = len; if (extension < 256) extension = 256; /* And it must be rounded up to a multiple of 256. */ extension = (extension + 0xFF) & (~(glui32)0xFF); res = resizeMemory(gEndMem+extension, 1); if (res) return 0; /* If we just started the heap, note that. */ if (heap_start == 0) heap_start = oldendmem; if (heap_tail && heap_tail->isfree) { /* Append the new space to the last block. */ blo = heap_tail; blo->len += extension; } else { /* Append the new space to the block list, as a new block. */ newblo = glulx_malloc(sizeof(heapblock_t)); if (!newblo) fatalError("Unable to allocate record for heap block."); newblo->addr = oldendmem; newblo->len = extension; newblo->isfree = TRUE; newblo->next = NULL; newblo->prev = NULL; if (!heap_tail) { heap_head = newblo; heap_tail = newblo; } else { blo = heap_tail; heap_tail = newblo; blo->next = newblo; newblo->prev = blo; } blo = newblo; newblo = NULL; } /* and continue forwards, using this new block (blo). */ } /* Something strange happened. */ if (!blo || !blo->isfree || blo->len < len) return 0; /* We now have a free block of size len or longer. */ if (blo->len == len) { blo->isfree = FALSE; } else { newblo = glulx_malloc(sizeof(heapblock_t)); if (!newblo) fatalError("Unable to allocate record for heap block."); newblo->isfree = TRUE; newblo->addr = blo->addr + len; newblo->len = blo->len - len; blo->len = len; blo->isfree = FALSE; newblo->next = blo->next; if (newblo->next) newblo->next->prev = newblo; newblo->prev = blo; blo->next = newblo; if (heap_tail == blo) heap_tail = newblo; } alloc_count++; /* heap_sanity_check(); */ return blo->addr; }