// This may look like C code, but it is really -*- Asm -*-
/*
 *  smoke16/src16/malloc.s - malloc() and friends.
 *  $Revision: 1.1 $
 *  Copyright 1997, Benjamin Sittler
 *
 *  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., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */


// K:
// Free list consists of blocks of memory linked together.
// Each block has a four-byte header; the first two bytes are
// the address? of the header? of the next block, and the second 
// two bytes are the length of the block (including header) in
// four-byte? units.
// The free-list is sorted in address order to enable easy coalescing.


#include <smoke16/asm.h>
	.data
freep:	.word 0 // free list pointer
	.lcomm base, 4 // first block

	.text
	.align 1
// K: Increment the top of memory by nbytes, returning the new
// K: top of memory (which may be unchanged if no more memory is
// K: available.)
// char *sbrk(u16 nbytes)
	FUNCTION(_sbrk)
	push %t0
	mov %a0, %t0    // K: nbytes
	mov %0, %a0     
	call _brk // brk(0) is a hack!
	add %a0, %t0, %a1  // K: %a1 = brk(0) + nbytes;
	mov %a0, %t0       // K: %t0 = brk(0);
	mov %a1, %a0
	call _brk    // K: brk(brk(0) + nbytes);
	cmpl %0, %a0
	bne sbrk0
	mov %t0, %a0       // K: if failed, return the old brk() value,
sbrk0:	pop %t0
	RET                // K: but if succeeded, return the new one.

#define NALLOC 256 /* minimum nblocks to request */

// K: morecore() is called from malloc()? to request more memory from the 
// K: system and put it into the free list.  It is passed a number of (4-byte)
// K: blocks to try to allocate; it allocates that many or NALLOC, whichever
// K: is greater.  If the allocation failed, it returns 0; if it succeeds, it
// K: returns a pointer to the first block in the free list.
	
// char *morecore(u16 nblocks)
	FUNCTION(_morecore)
	mov NALLOC, %a1   // K: %a1 = NALLOC;
	cmpl %a0, %a1
	bge morecore0     
	mov %a1, %a0      // K: if (NALLOC < nblocks) nblocks = NALLOC;
morecore0:
	push %a0
	sll 2, %a0        
	call _sbrk  // K: int sbrkret = sbrk(nblocks * 4);
	mov -1, %a1
	cmpl %a0, %a1
	be morecore1      // K: if (sbrkret != -1) {
	pop %a1
	mov %a1, [%a0 + 2] // K: (u16*)%a0[1] = nblocks;
	add 4, %a0        // K: increment a0 to point to the body of the block
	call _free  // K: put the block on the free list
	mov freep, %a0    
	mov [%a0], %a0    // K: return the contents of the free pointer.
	b morecore2       // K: } else /* sbrkret == -1 */ {
morecore1:
	pop               // K: discard saved %a0 from morecore0:
	mov %0, %a0       // K: return 0 if sbrk() failed
morecore2:
	RET               // K: return freelist pointer on success, 
                          // K: or 0 on failure.

// Insert a block into the freelist.

// void free(char *block)
	FUNCTION(_free)
	push %t0
	sub 4, %a0        // K: find the header for the block
	mov freep, %a1
	mov [%a1], %a2    // K: load the free pointer into %a2
free0:	mov [%a2], %a3    // K: This is the beginning of a loop through the
                          // K: freelist.  At the top of this loop, (after
                          // K: this statement) %a2 contains a pointer to the
                          // K: current block, and %a3 contains a pointer to
                          // K: the next block.
	cmpl %a0, %a2     // K: If the header of this block is greater than
                          // K: the header of the current block in the
                          // K: freelist, we must insert it.  Here we test
                          // K: to see if that's the case.
	bl free1
	be free1
	cmpl %a0, %a3
	bge free1
	b free3
free1:	cmpl %a2, %a3
	bl free2
	cmpl %a0, %a2
	bg free3
	cmpl %a0, %a3
	bl free3
free2:	mov %a3, %a2
	b free0           // K: loop back to free0
free3:	mov [%a0 + 2], %t0
	add %a0, %t0
	cmpl %t0, %a3
	bne free4
	mov [%a3], %t0
	mov %t0, [%a0]
	mov [%a3 + 2], %t0
	mov [%a3 + 2], %a3
	add %a3, %t0
	mov %t0, [%a0 + 2]
	b free5
free4:	mov %a3, [%a0]
free5:	mov [%a2 + 2], %t0
	add %a2, %t0
	cmpl %t0, %a0
	bne free6
	mov [%a0], %t0
	mov %t0, [%a2]
	mov [%a2 + 2], %t0
	mov [%a0 + 2], %a0
	add %a0, %t0
	mov %t0, [%a2 + 2]
	b free7
free6:	mov %a0, [%a2]
free7:	mov %a2, [%a1]
	pop %t0
	RET

// char *malloc(u16 nbytes)
	FUNCTION(_malloc)
	push %t0
	mov freep, %t0
	add 7, %a0
	srl 2, %a0
	mov [%t0], %a1
	cmpl %0, %a1
	bne malloc0
	mov base, %a1
	mov %a1, [%a1]
	mov %a1, [%t0]
	mov %0, [%a1 + 2]
malloc0:
	mov [%a1], %a2
malloc1:
	mov [%a2 + 2], %a3
	cmpl %a3, %a0
	bl malloc4
	bne malloc2
	mov [%a2], %a3
	mov %a3, [%a1]
	b malloc3
malloc2:
	sub %a0, %a3
	mov %a3, [%a2 + 2]
	add %a3, %a2
	mov %a0, [%a2 + 2]
malloc3:
	mov %a1, [%t0]
	add 4, %a2
	mov %a2, %a0
	b mallocN
malloc4:
	mov [%t0], %a3
	cmpl %a2, %a3
	bne malloc5
	push %a0
	push %a1
	call _morecore
	pop %a1
	pop %a3
	cmpl %a0, %0
	be mallocN
	mov %a0, %a2
	mov %a3, %a0
malloc5:
	mov %a2, %a1
	mov [%a2], %a2
	b malloc1
mallocN:
	pop %t0 
	RET

