; GrowStack 1.1 : increase the size of a task's stack
; by Kyzer/CSG (idea stolen from Andreas R. Kleinert's C program)
; $VER: GrowStack.asm 1.1 (10.08.98)
;
	incdir	include:
	include	dos/dos.i
	include	dos/dosextens.i
	include	exec/memory.i
	include	exec/tasks.i
	include	lvo/dos_lib.i
	include	lvo/exec_lib.i

stackf	MACRO	; stack_symbol, stackelement_symbol
	IFND	\1
\1	set	0
	ENDC
\1	set	\1-4
\2	equ	\1
	ENDM

	stackf	stk, stacksize	; these three filled
	stackf	stk, iprefs	; by ReadArgs()
	stackf	stk, ramlib

args=ramlib

GrowStk	link	a5,#stk
	move.l	4.w,a6
	lea	dosname(pc),a1	; dos.library v37+
	moveq.l	#37,d0
	jsr	_LVOOpenLibrary(a6)
	move.l	d0,a6
	tst.l	d0
	beq.s	.nodos

	lea	templat(pc),a0
	move.l	a0,d1
	lea	args(a5),a0
	move.l	a0,d2
	clr.l	(a0)+
	clr.l	(a0)+
	clr.l	(a0)+
	moveq	#0,d3
	jsr	_LVOReadArgs(a6)
	move.l	d0,-(sp)
	beq.s	.noargs

	move.l	a6,-(sp)
	move.l	4.w,a6
	moveq	#0,d5			; d5 = new IoErr (if we set it)

	lea	tramlib(pc),a1
	tst.l	ramlib(a5)
	beq.s	.not1
	bsr.s	FixTask
.not1	lea	tiprefs(pc),a1
	tst.l	iprefs(a5)
	beq.s	.not2
	bsr.s	FixTask
.not2

	move.l	(sp)+,a6
	move.l	d5,d1
	jsr	_LVOSetIoErr(a6)

.noargs	move.l	(sp)+,d1
	jsr	_LVOFreeArgs(a6)

	jsr	_LVOIoErr(a6)
	move.l	d0,d1
	moveq	#0,d2
	jsr	_LVOPrintFault(a6)

	move.l	a6,a1
	move.l	4.w,a6
	jsr	_LVOCloseLibrary(a6)
.nodos	unlk	a5
	moveq	#0,d0
	rts

; NOTE:	This is NOT a good example of stack swapping, since the OLD stack's
; buffer will _NEVER_ be deallocated. However, there is no other way to
; achieve a larger stack for IPrefs/ramlib, and since IPrefs/ramlib usually
; never will end, this perhaps isn't a problem...

; A1 = taskname to change
FixTask	jsr	_LVOForbid(a6)
	jsr	_LVOFindTask(a6)
	tst.l	d0
	beq.s	.notask
	move.l	d0,a2

; check if stack size is already big enough

	move.l	stacksize(a5),a0
	move.l	(a0),d7			; d7 = STACKSIZE
	addq.l	#3,d7
	and.w	#-4,d7			; longword aligned size

	move.l	TC_SPUPPER(a2),d0
	move.l	d0,d6			; d6 = TC_UPPER
	sub.l	TC_SPLOWER(a2),d0	; d0 = oldsize = TC_UPPER-TC_LOWER
	cmp.l	d7,d0			; cmp STACKSIZE, oldsize
	bcc.s	.done			; if oldsize >= STACKSIZE then EXIT

; allocate new stack and install

	move.l	d7,d0
	move.l	d0,pr_StackSize(a2)	; store as process stacksize
	moveq	#MEMF_PUBLIC,d1
	jsr	_LVOAllocVec(a6)	; allocate STACKSIZE bytes
	tst.l	d0
	beq.s	.nomem
	add.l	d0,d7			; d7 = upper = (lower+STACKSIZE)
	move.l	d7,TC_SPUPPER(a2)	; set new upper
	move.l	d0,TC_SPLOWER(a2)	; and lower stack limit
	asr.l	#2,d7			; convert to BPTR
	move.l	d7,pr_StackBase(a2)	; store as process stackbase

; copy old stack contents (not freed) to new stack

	move.l	TC_SPREG(a2),a0		; a0 = TC_SPREG
	sub.l	a0,d6			; d6 = inuse = (TC_UPPER-TC_SPREG)
	sub.l	d6,d7			; d7 = pointer = (upper - inuse)
	move.l	d7,TC_SPREG(a2)
	move.l	d7,a1
	move.l	d6,d0
	jsr	_LVOCopyMem(a6)		; a0=TC_SPREG, a1=pointer, d0=inuse
	bra.s	.done

.notask	moveq	#0,d1
	move.b	#ERROR_OBJECT_NOT_FOUND,d5
	bra.s	.done
.nomem	moveq	#ERROR_NO_FREE_STORE,d5
.done	jmp	_LVOPermit(a6)

dosname	dc.b	"dos.library",0
tiprefs	dc.b	"« IPrefs »",0
tramlib	dc.b	"ramlib",0

templat	dc.b	"RAMLIB/S,IPREFS/S,STACKSIZE/N/A",0
	dc.b	"$VER: GrowStack 1.1 (10.08.98)",0
