mirror of
https://github.com/libretro/scummvm.git
synced 2025-01-05 17:20:30 +00:00
ff17899d8e
svn-id: r38746
287 lines
6.6 KiB
C++
287 lines
6.6 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 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.
|
|
*
|
|
* $URL$
|
|
* $Id$
|
|
*
|
|
*/
|
|
|
|
#include "sci/include/engine.h"
|
|
#include "sci/include/sciconsole.h"
|
|
#include "sci/engine/heap.h"
|
|
|
|
namespace Sci {
|
|
|
|
#define assert_in_range(pos) assert(pos >= 1000 && pos <= 0xffff)
|
|
|
|
static void set_size(heap_t *h, int block_pos, int size) {
|
|
assert_in_range(block_pos);
|
|
assert(size <= 0xffff - 1000);
|
|
putInt16(h->start + block_pos, size);
|
|
}
|
|
|
|
static void set_next(heap_t *h, int block_pos, int next) {
|
|
assert_in_range(block_pos);
|
|
assert_in_range(next);
|
|
putInt16(h->start + block_pos + 2, next);
|
|
}
|
|
|
|
|
|
static unsigned int get_size(heap_t *h, int block_pos) {
|
|
assert_in_range(block_pos);
|
|
return (uint16)getInt16(h->start + block_pos);
|
|
}
|
|
|
|
static unsigned int get_next(heap_t *h, int block_pos) {
|
|
assert_in_range(block_pos);
|
|
return (uint16)getInt16(h->start + block_pos + 2);
|
|
}
|
|
|
|
// Allocates a new heap
|
|
heap_t* heap_new() {
|
|
heap_t* h;
|
|
|
|
if ((h = (heap_t*)sci_malloc(sizeof(heap_t))) == 0)
|
|
return 0;
|
|
|
|
if ((h->start = (byte *)sci_calloc(SCI_HEAP_SIZE, 1)) == 0) {
|
|
free(h);
|
|
return 0;
|
|
}
|
|
|
|
h->base = h->start + 1000;
|
|
h->first_free = 1000;
|
|
h->old_ff = -1;
|
|
set_size(h, 1000, 0xffff - 1000);
|
|
set_next(h, 1000, 0xffff);
|
|
|
|
return h;
|
|
}
|
|
|
|
// Deletes a heap
|
|
void heap_del(heap_t *h) {
|
|
free(h->start);
|
|
free(h);
|
|
}
|
|
|
|
int heap_meminfo(heap_t *h) {
|
|
heap_ptr current = h->first_free;
|
|
int total = 0;
|
|
|
|
while (current != 0xffff) {
|
|
total += get_size(h, current);
|
|
current = get_next(h, current);
|
|
}
|
|
|
|
return total;
|
|
}
|
|
|
|
|
|
int heap_largest(heap_t *h) {
|
|
int current = h->first_free;
|
|
int best_pos = -1, best_size = 0;
|
|
|
|
while (current != 0xffff) {
|
|
int size = get_size(h, current);
|
|
int next = get_next(h, current);
|
|
|
|
if (size > best_size) {
|
|
best_pos = current;
|
|
best_size = size;
|
|
}
|
|
|
|
current = next;
|
|
}
|
|
|
|
return best_size;
|
|
}
|
|
|
|
heap_ptr heap_allocate(heap_t *h, int size) {
|
|
unsigned int previous = h->first_free;
|
|
unsigned int current = previous;
|
|
|
|
if (!size) {
|
|
fprintf(stderr, "Warning: heap_alloc'd zero bytes!\n");
|
|
size += 2;
|
|
}
|
|
|
|
size += 2 + (size & 1);
|
|
|
|
while (current < 0xffff) {
|
|
int block_size = get_size(h, current);
|
|
int next = get_next(h, current);
|
|
|
|
// Is this block large enough?
|
|
if (block_size >= size) {
|
|
// Swallow the block whole
|
|
if (block_size <= size + 4) {
|
|
size = block_size;
|
|
set_next(h, previous, next);
|
|
} else {
|
|
// Split the block
|
|
int rest = current + size;
|
|
|
|
set_next(h, previous, rest);
|
|
set_size(h, rest, block_size - size);
|
|
set_next(h, rest, next);
|
|
next = rest;
|
|
}
|
|
set_size(h, current, size);
|
|
if (current == h->first_free) h->first_free = next;
|
|
return current;
|
|
}
|
|
previous = current;
|
|
current = next;
|
|
}
|
|
|
|
// No large enough block was found.
|
|
return 0;
|
|
}
|
|
|
|
void heap_free(heap_t *h, unsigned int m) {
|
|
unsigned int previous, next;
|
|
assert_in_range(m);
|
|
previous = next = h->first_free;
|
|
|
|
// Find the previous and next blocks
|
|
while (next < m) {
|
|
previous = next;
|
|
assert(previous < 0xffff);
|
|
next = get_next(h, previous);
|
|
if (next <= previous) {
|
|
sciprintf("Heap corrupt. Aborting heap_free()...\n");
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (h->first_free > m)
|
|
h->first_free = m; // Guarantee that first_free is correct
|
|
|
|
if (previous == next) {
|
|
if (m < previous) {
|
|
h->first_free = m;
|
|
if (m + get_size(h, m) == previous) {
|
|
set_size(h, m, get_size(h, m) + get_size(h, previous));
|
|
set_next(h, m, get_next(h, previous));
|
|
} else
|
|
set_next(h, m, previous);
|
|
} else {
|
|
if (previous + get_size(h, previous) == m) {
|
|
set_size(h, previous, get_size(h, previous) + get_size(h, m));
|
|
set_next(h, previous, 0xffff);
|
|
} else {
|
|
set_next(h, previous, m);
|
|
set_next(h, m, next);
|
|
}
|
|
}
|
|
} else {
|
|
set_next(h, previous, m);
|
|
set_next(h, m, next);
|
|
|
|
// Try to merge with previous
|
|
if (previous + get_size(h, previous) == m) {
|
|
set_size(h, previous, get_size(h, previous) + get_size(h, m));
|
|
set_next(h, previous, next);
|
|
m = previous;
|
|
}
|
|
|
|
// Try to merge with next
|
|
if (m + get_size(h, m) == next) {
|
|
set_size(h, m, get_size(h, m) + get_size(h, next));
|
|
set_next(h, m, get_next(h, next));
|
|
}
|
|
}
|
|
}
|
|
|
|
void save_ff(heap_t *h) {
|
|
h->old_ff = h->first_free;
|
|
}
|
|
|
|
void restore_ff(heap_t *h) {
|
|
h->first_free = h->old_ff;
|
|
set_size(h, h->first_free, 0xffff - h->first_free);
|
|
set_next(h, h->first_free, 0xffff);
|
|
}
|
|
|
|
void heap_dump_free(heap_t *h) {
|
|
int freedomseeker;
|
|
|
|
printf("\tfirst_free= %#x (oldff= %#x)\n\tFree Blocks:\n", h->first_free, h->old_ff);
|
|
|
|
freedomseeker = h->first_free;
|
|
while (freedomseeker != 0xffff) {
|
|
printf("\t %#04x: size: %#04x\n", freedomseeker, get_size(h, freedomseeker));
|
|
freedomseeker = get_next(h, freedomseeker);
|
|
}
|
|
}
|
|
|
|
void heap_dump_all(heap_t *h) {
|
|
int seeker = 1000;
|
|
int free_seeker = h->first_free;
|
|
|
|
while (seeker < 0xffff) {
|
|
int is_free = (seeker == free_seeker);
|
|
int size = get_size(h, seeker);
|
|
|
|
if (is_free)
|
|
free_seeker = get_next(h, free_seeker);
|
|
|
|
printf("%04x\t%d\t%s\n", seeker, size, is_free ? "FREE" : "");
|
|
seeker += size;
|
|
}
|
|
}
|
|
|
|
/*
|
|
int main(int argc, char **argv) {
|
|
heap_t *h = heap_new();
|
|
int a, b, c, d, e;
|
|
|
|
printf("Running heap tests:\nHeap initialization:\n");
|
|
heap_dump_free(h);
|
|
|
|
printf("[a] Allocating 0x1: position is %#x\n", a = heap_allocate(h, 1));
|
|
heap_dump_free(h);
|
|
|
|
printf("[b] Allocating 0x10: position is %#x\n", b = heap_allocate(h, 0x10));
|
|
printf("[c] Allocating 0x10: position is %#x\n", c = heap_allocate(h, 0x10));
|
|
printf("[d] Allocating 0x10: position is %#x\n", d = heap_allocate(h, 0x10));
|
|
printf("[e] Allocating 0x1000: position is %#x\n", e = heap_allocate(h, 0x1000));
|
|
heap_dump_free(h);
|
|
|
|
printf("Freeing [b]:\n");
|
|
heap_free(h, b);
|
|
heap_dump_free(h);
|
|
|
|
printf("Freeing [d]:\n");
|
|
heap_free(h, d);
|
|
heap_dump_free(h);
|
|
|
|
printf("Freeing [c]:\n");
|
|
heap_free(h, c);
|
|
heap_dump_free(h);
|
|
|
|
heap_del(h);
|
|
|
|
return 0;
|
|
}
|
|
*/
|
|
|
|
} // End of namespace Sci
|