/* 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 "glk/glulxe/glulxe.h" namespace Glk { namespace Glulxe { void Glulxe::heap_clear() { while (heap_head) { heapblock_t *blo = heap_head; heap_head = blo->next; blo->next = nullptr; blo->prev = nullptr; glulx_free(blo); } heap_tail = nullptr; if (heap_start) { uint res = change_memsize(heap_start, true); if (res) fatal_error_i("Unable to revert memory size when deactivating heap.", heap_start); } heap_start = 0; alloc_count = 0; /* heap_sanity_check(); */ } int Glulxe::heap_is_active() const { return (heap_start != 0); } uint Glulxe::heap_get_start() const { return heap_start; } uint Glulxe::heap_alloc(uint len) { heapblock_t *blo, *newblo; #ifdef FIXED_MEMSIZE return 0; #else /* FIXED_MEMSIZE */ if (len <= 0) fatal_error("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 = nullptr; heap_tail = blo; } newblo->next = nullptr; newblo->prev = nullptr; glulx_free(newblo); newblo = nullptr; 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. */ uint res; uint extension; uint oldendmem = endmem; extension = 0; if (heap_start) extension = endmem - 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) & (~(uint)0xFF); res = change_memsize(endmem + extension, true); 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 = (heapblock_t *)glulx_malloc(sizeof(heapblock_t)); if (!newblo) fatal_error("Unable to allocate record for heap block."); newblo->addr = oldendmem; newblo->len = extension; newblo->isfree = true; newblo->next = nullptr; newblo->prev = nullptr; 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 = nullptr; } /* 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 = (heapblock_t *)glulx_malloc(sizeof(heapblock_t)); if (!newblo) fatal_error("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; #endif /* FIXED_MEMSIZE */ } void Glulxe::heap_free(uint addr) { heapblock_t *blo; for (blo = heap_head; blo; blo = blo->next) { if (blo->addr == addr) break; }; if (!blo || blo->isfree) fatal_error_i("Attempt to free unallocated address from heap.", addr); blo->isfree = true; alloc_count--; if (alloc_count <= 0) { heap_clear(); } /* heap_sanity_check(); */ } int Glulxe::heap_get_summary(uint *valcount, uint **summary) { uint *arr, len, pos; heapblock_t *blo; *valcount = 0; *summary = nullptr; if (heap_start == 0) return 0; len = 2 + 2 * alloc_count; arr = (uint *)glulx_malloc(len * sizeof(uint)); if (!arr) return 1; pos = 0; arr[pos++] = heap_start; arr[pos++] = alloc_count; for (blo = heap_head; blo; blo = blo->next) { if (blo->isfree) continue; arr[pos++] = blo->addr; arr[pos++] = blo->len; } if (pos != len) fatal_error("Wrong number of active blocks in heap"); *valcount = len; *summary = arr; return 0; } int Glulxe::heap_apply_summary(uint valcount, uint *summary) { uint lx, jx, lastend; if (heap_start) fatal_error("Heap active when heap_apply_summary called"); if (valcount == 0 || summary == nullptr) return 0; if (valcount == 2 && summary[0] == 0 && summary[1] == 0) return 0; #ifdef FIXED_MEMSIZE return 1; #else /* FIXED_MEMSIZE */ lx = 0; heap_start = summary[lx++]; alloc_count = summary[lx++]; for (jx = lx; jx + 2 < valcount; jx += 2) { if (summary[jx] >= summary[jx + 2]) fatal_error("Heap block summary is out of order."); } lastend = heap_start; while (lx < valcount || lastend < endmem) { heapblock_t *blo; blo = (heapblock_t *)glulx_malloc(sizeof(heapblock_t)); if (!blo) fatal_error("Unable to allocate record for heap block."); if (lx >= valcount) { blo->addr = lastend; blo->len = endmem - lastend; blo->isfree = true; } else { if (lastend < summary[lx]) { blo->addr = lastend; blo->len = summary[lx] - lastend; blo->isfree = true; } else { blo->addr = summary[lx++]; blo->len = summary[lx++]; blo->isfree = false; } } blo->prev = nullptr; blo->next = nullptr; if (!heap_head) { heap_head = blo; heap_tail = blo; } else { heap_tail->next = blo; blo->prev = heap_tail; heap_tail = blo; } lastend = blo->addr + blo->len; } /* heap_sanity_check(); */ return 0; #endif /* FIXED_MEMSIZE */ } } // End of namespace Glulxe } // End of namespace Glk