aboutsummaryrefslogtreecommitdiff
path: root/engines/glk/glulxe/heap.cpp
diff options
context:
space:
mode:
authorPaul Gilbert2019-04-14 21:41:16 -0700
committerPaul Gilbert2019-04-17 20:46:06 -0700
commit9ceb83972adc62aff28618c196f0a473fb85b1a0 (patch)
tree42f8573cb9d73307a9a28b472da381dfb5451f34 /engines/glk/glulxe/heap.cpp
parent0b628784bd9a65322fc6c04be41267310f497dea (diff)
downloadscummvm-rg350-9ceb83972adc62aff28618c196f0a473fb85b1a0.tar.gz
scummvm-rg350-9ceb83972adc62aff28618c196f0a473fb85b1a0.tar.bz2
scummvm-rg350-9ceb83972adc62aff28618c196f0a473fb85b1a0.zip
GLK: GLULXE: Added heap methods
Diffstat (limited to 'engines/glk/glulxe/heap.cpp')
-rw-r--r--engines/glk/glulxe/heap.cpp322
1 files changed, 322 insertions, 0 deletions
diff --git a/engines/glk/glulxe/heap.cpp b/engines/glk/glulxe/heap.cpp
new file mode 100644
index 0000000000..29d9405aca
--- /dev/null
+++ b/engines/glk/glulxe/heap.cpp
@@ -0,0 +1,322 @@
+/* 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::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