Files
2026-02-02 04:50:13 +01:00

317 lines
6.9 KiB
C++

/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "glk/glulx/glulx.h"
namespace Glk {
namespace Glulx {
void Glulx::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 Glulx::heap_is_active() const {
return (heap_start != 0);
}
uint Glulx::heap_get_start() const {
return heap_start;
}
uint Glulx::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 Glulx::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 Glulx::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 Glulx::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 Glulx
} // End of namespace Glk