PROCEDURE	<ALLOC -- ALLOCATE SPACE>,010100
;+
; abstract:	alloc
;
;	This module has generalised allocate/free support for
;	RUNOFF.
;
; calling sequence:
;
;	MOV	#LENGTH_IN_BYTES,R1
;	CALL	ALLOC	; allocate a block
;			; r1 -> allocated block
;
;	MOV	#BLOCK,R1
;	CALL	FREE	; free an allocated block.
;
;	CALL	INIFRE	; initialise free list.
;			; (effectively freeing everything)
;
; externals:
;
;	The free list head has the following format:
;
; FREHD::
;	.WORD	FIRST_FREE_BLOCK
;	.WORD	0	;!! for free subroutine
;
;	Free list elements in general have the format:
;
;	.WORD	NEXT_ENTRY_OR_0_IF_NONE
;	.WORD	LENGTH_OF_THIS_ENTRY
;
; written: 24-jan-80, -1.0.0-, Bruce C. Wright
; modified: 30-jan-80, -1.1.0-, Bruce C. Wright
;	Conditionalise for RSX-11M
; verified:
;-

	CODE	ALLOC
	.MCALL	GTSK$S
	.IF DF	R$$EXT
	.MCALL	EXTK$S
	.ENDC	;R$$EXT
ALLOC::
	MOV	R3,-(SP)	; Save R3
	MOV	R4,-(SP)	; Save R4
	MOV	R5,-(SP)	; Save R5
	ADD	#3,R1		; Increment up to a word boundary.
				; Also account for length word.
	BIC	#1,R1		; Round down to words.
	CMP	R1,#4		; Must allocate at least 4. at once.
	BHIS	5$		; enough, OK
	MOV	#4,R1		; Not enough, make it enough.
5$:	MOV	#FREHD,R5	; Get header for free list.
10$:	MOV	R5,R4		; Remember previous location.
	MOV	(R5),R5		; Get to next block in free list.
	BEQ	90$		; None there, error.
15$:	CMP	2(R5),R1	; Is there enough room here?
	BLO	10$		; No, keep looking.
	MOV	R5,R3		; Remember node address allocated.
	ADD	#4,R1		; Do we get the whole thing?
	CMP	R1,2(R5)	; Can't let < 4 bytes in free list.
	BLOS	20$		; There's enough room for a node.
	MOV	2(R5),R1	; Not enough, get all of it.
	MOV	(R5),(R4)	; Re-link previous into next node.
	BR	25$		; And skip node creation.
20$:	SUB	#4,R1		; Get amount to allocate.
	SUB	R1,2(R5)	; Compute new length of node.
	ADD	R1,R5		; Point to address of new node.
	MOV	R5,(R4)		; Point to this node from previous.
	MOV	(R3),(R5)+	; Point to next node from here.
	MOV	2(R3),(R5)	; Save length word in new node.
25$:	MOV	R1,(R3)+	; Save length allocated, point to returned
	MOV	R3,R1		; Remember address of allocated area.
	MOV	(SP)+,R5	; Recover registers.
	MOV	(SP)+,R4	; ...
	MOV	(SP)+,R3	; ...
	RETURN			; And return to the caller.
90$:
	.IF DF	R$$EXT		; If have EXTK$ ....
	MOV	R1,R5		; Get the size of the area desired.
	ADD	#77,R5		; Round it up to the next 32-word bound.
	BIC	#77,R5		; ...
	MOV	R5,R3		; Save this number.
	.IF DF	R$$EIS		; If EIS is available ...
	ASH	#-6,R5		; Compute number of 32-word blocks.
	.IFF			; Otherwise, ...
	.REPT	6.
	ASR	R5		; Compute number of 32-word blocsk.
	.ENDR
	.ENDC	;R$$EIS
	EXTK$S	R5		; Try to extend the task.
	BCS	99$		; Problems?
	MOV	LSTLOC,R5	; Get old last location.
	ADD	R3,LSTLOC	; Compute new last location.
	CLR	(R5)		; Show it's the end of the chain.
	MOV	R3,2(R5)	; Remember the size gotten.
	MOV	R4,R3		; Get the end of previous node
	ADD	2(R4),R3	; ...
	CMP	R3,R5		; Is it the beginning of this one?
	BEQ	95$		; Yes -- then merge the nodes.
	MOV	R5,(R4)		; Point to node from previous.
	BR	15$		; And allocate the stuff here!
95$:	ADD	2(R5),2(R4)	; Compute new free length.
	BR	5$		; And go allocate all over.
99$:				; Here on extend failure
	.ENDC	;R$$EXT
	DIAG	CORERR		; Tell user no memory left.
	JMP	ENDFIL		; And go finish up what we've got.

FREE::
	MOV	R3,-(SP)	; save R3
	MOV	R4,-(SP)	; save registers.
	MOV	R5,-(SP)	; ...
	SUB	#2,R1		; create a dummy free list entry.
	MOV	(R1),2(R1)	; Move over the length word.
	MOV	#FREHD,R5	; point to free list header.
10$:	MOV	R5,R4		; remember previous free list pointer.
	MOV	(R5),R5		; Get to next address in free list.
	BEQ	80$		; to end of list, link in at end.
	CMP	R1,R5		; freed area > free list entry?
	BHI	10$		; Yes, loop until found good entry.
	MOV	R5,(R1)		; link the new node into list.
	MOV	R1,(R4)		; point to it from previous node.
	MOV	R1,R3		; Get freed area into R3.
	ADD	2(R1),R3	; Get to the end of the freed area.
	CMP	R3,R5		; is it equal?
	BHI	99$		; corrupt free list!
	BNE	30$		; no, skip merging of nodes.
	ADD	2(R5),2(R1)	; Add in the length of the node.
	MOV	(R5),(R1)	; point to the next node from here.
30$:	MOV	R4,R3		; Does R4 pointer match?
	ADD	2(R4),R3	; point to end of R4 area.
	CMP	R3,R1		; is it the same?
	BHI	99$		; corrupt free list!
	BNE	40$		; no, skip merging of nodes.
	ADD	2(R1),2(R4)	; Arrange amount of free space.
	MOV	(R1),(R4)	; fixup the next pointer.
40$:	MOV	(SP)+,R5	; recover registers
	MOV	(SP)+,R4	; ...
	MOV	(SP)+,R3	; ...
	RETURN			; And return to the caller.
80$:	MOV	R1,(R4)		; link into the chain.
	CLR	(R1)		; show that we're at end of line.
	BR	30$		; And try to merge with previous node.
99$:	FATAL	BADFRE		; something horrible happened.

INIFRE::
	MOV	R4,-(SP)	; save R4
	MOV	R5,-(SP)	; save R5
	SUB	#16.*2,SP	; Get space for GTSK$S
	MOV	SP,R4		; Get space.
	GTSK$S	R4		; Get the task size.
	MOV	G.TSTS(R4),R4	; Get size of task.
	ADD	#$DSW,R4	; Compute last address in task.
	.IF DF	R$$EXT		; Need to keep track of
				; the amount of space currently
				; allocated for expand.
	MOV	R4,LSTLOC	; Remember last location.
	.ENDC	;R$$EXT
	ADD	#16.*2,SP	; pop space for GTSK$S
	CLR	FREHD+2		; init free head.
	MOV	LIMIT+2,R5	; point to first free area.
	MOV	R5,FREHD	; point to it.
	SUB	R5,R4		; Compute length of free area.
	CMP	R4,#4		; Is there any room in free area?
	BHIS	10$		; Yes -- take it
	CLR	FREHD		; No -- clear out buffer area.
	BR	20$		; And return without any area.
10$:	CLR	(R5)+		; Show end of free area.
	MOV	R4,(R5)+	; Indicate size of free area.
20$:	MOV	(SP)+,R5	; recover R5
	MOV	(SP)+,R4	; recover R4
	RETURN			; and return to the caller.
;
	DATA	ALLOCD
LIMIT:	.LIMIT			; Get limits
FREHD:	.WORD	0,0		; free listhead
	.IF DF	R$$EXT
LSTLOC:	.WORD	0		; Next location to be allocated.
				; (last location in task + 1, for
				; dynamic task extension support)
	.ENDC	;R$$EXT
	.END
