317 lines
6.9 KiB
C++
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
|