##
## ppc.library emulation
## (c)1998-99 Frank Wille <frank@phoenix.owl.de>
##
## PPC assembler stub for calling PowerUp kernel functions.
## Will be used by the "dynamic PowerUp kernel linker".
##
## V0.5d (04.04.1999) phx
##       Calls PPCTaskInit() before actually launching the task and
##       PPCTaskExit() when the task has finished.
## V0.4c (23.01.1999) phx
##       Created. This module provides launch and exit functions for
##       synchronous and asynchronous PPC tasks. The kernel_launch
##       function is usually called from the 68k-side by PPCCreateTask().
##

	.file	"kernlaunch.s"
	.include "warpos_lvo.i"
	.include "taskobject.i"

.set	CACHE_DCACHEFLUSH,5

.macro	reginit
	lis	\1,((\1<<24)|(\1<<16)|(\1<<8)|\1)@h
	ori	\1,\1,((\1<<24)|(\1<<16)|(\1<<8)|\1)@l
.endm

	.extern	_PPCTaskInit
	.extern	_PPCTaskExit


	.text


	.globl	_kernel_launch
_kernel_launch:
# A new PPC task is launched by PPCCreateTask().
# r2 = TaskObject pointer
# r3 = freetaskobj 68k-function pointer, used to remove the TaskObject
# r4 = Signal bit != -1, if the parent task wants to be notified
.set	tskobjptr,44
.set	ppcargs,48
.set	ppcargsSIZE,144
.set	initsfSIZE,192

	li	r11,0
	stwu	r11,-16(r1)	# hand-craft initial SVR4 stack frame
	mflr	r0
	stw	r0,4(r1)	# save LR
	stwu	r1,-initsfSIZE(r1)
	stw	r1,to_initialstack(r2)
	stw	r2,tskobjptr(r1)# save TaskObject ptr in 1st stack frame
	stw	r3,ppcargs(r1)	# PP_Code: ptr to 68k task-removal function
	mr	r30,r4		# sig bit

	lwz	r31,to_PPCBase(r2)
	li	r4,0
	CALLWOS	FindTaskPPC,r31	# get our TaskPPC pointer and
	stw	r2,88(r3)	# save TaskObject in tc_UserData
	stw	r3,to_this(r2)

	li	r4,CACHE_DCACHEFLUSH
	li	r5,0
	li	r6,0
	CALLWOS	SetCache,r31	# flush whole data cache

	mr	r3,r31
	mr	r4,r2
	mr	r5,r30
	bl	_PPCTaskInit	# PPCTaskInit(PPCBase,TaskObject,SigBit)

# init registers
	lwz	r3,to_args+0(r2)
	lwz	r4,to_args+4(r2)
	lwz	r5,to_args+8(r2)
	lwz	r6,to_args+12(r2)
	lwz	r7,to_args+16(r2)
	lwz	r8,to_args+20(r2)
	lwz	r9,to_args+24(r2)
	lwz	r10,to_args+28(r2)
	reginit	r11
	reginit	r12
	reginit	r13
	reginit	r14
	reginit	r15
	reginit	r16
	reginit	r17
	reginit	r18
	reginit	r19
	reginit	r20
	reginit	r21
	reginit	r22
	reginit	r23
	reginit	r24
	reginit	r25
	reginit	r26
	reginit	r27
	reginit	r28
	reginit	r29
	reginit	r30
	reginit	r31

# start
	lwz	r0,to_start_addr(r2)
	mtctr	r0
	lbz	r0,to_flags(r2)
	andi.	r0,r0,TskObjF_Async
	bne	async_launch
	bl	Start

# Synchronous Exit
# r3 = ResultCode
# r2 = TaskObject pointer - it's contents must not be changed under V.4!
#      (let's hope that no erroneous V.4 function killed it)
	bl	tskobj_sanitycheck
	cmpwi	r12,0
	beq	sto_r2corrupted
	lwz	r1,to_initialstack(r2)
sto_ok:
	mr	r31,r3
	lwz	r3,to_PPCBase(r2)
	mr	r4,r2
	bl	_PPCTaskExit	# PPCTaskExit(PPCBase,TaskObject)

	lwz	r0,initsfSIZE+4(r1)
	mr	r3,r31		# return code
	addi	r1,r1,initsfSIZE+16
	mtlr	r0
	blr			# finish synchronous task, return to 68k

sto_r2corrupted:
# Shit, somebody destroyed r2. Try to get it from the stack frame...
	lwz	r2,tskobjptr(r1)
	bl	tskobj_sanitycheck
	cmpwi	r12,0
	bne	sto_ok

Crash:
# Oh no! The stack frame is also wrong! There's nothing we can do.
# 'Gracefully' crash the task to prevent further damage. :)
# Indicate this special crash by setting r0 to 0xABADC0DE
	lis	r0,0xabadc0de@h
	ori	r0,r0,0xabadc0de@l
	.long	0		# illegal instruction exception


Start:
# Jumps via CTR to a task's start address. Saves LR as exit-function first.
	mflr	r0
	stw	r0,to_exit_addr(r2)
	li	r0,0
	bctr


async_launch:
	bl	Start

	.globl	_kernel_exit
_kernel_exit:
# Exit function of an asynchronous task.
# r2 = TaskObject pointer - it's contents must not be changed under V.4!
#      (let's hope that no erroneous V.4 function killed it)
	bl	tskobj_sanitycheck
	cmpwi	r12,0
	bne	to_ok

# Shit, somebody destroyed r2. Try to get it from the stack frame...
	lwz	r2,tskobjptr(r1)
	bl	tskobj_sanitycheck
	cmpwi	r12,0
	beq	Crash

to_ok:
# *phew*, r2 still seems to be valid
	lwz	r1,to_initialstack(r2)
	lwz	r31,to_PPCBase(r2)
	mr	r3,r31
	mr	r4,r2
	bl	_PPCTaskExit	# PPCTaskExit(PPCBase,TaskObject)

	li	r0,(ppcargsSIZE-4)/4
	mtctr	r0
	addi	r3,r1,ppcargs
	li	r0,0
to_initpa:			# initialize PPCArgs struct
	stwu	r0,4(r3)
	bdnz	to_initpa
	addi	r4,r1,ppcargs
	stw	r31,52(r4)	# a0 = PPCBase
	stw	r2,56(r4)	# a1 = TaskObject
	CALLWOS	Run68K,r31	# remove and delete our TaskObject

	li	r4,0
	CALLWOS	DeleteTaskPPC,r31 # that's it...
	.long	0		# <- this should never be reached (?)


tskobj_sanitycheck:
# -> r12 = TaskObject pointer valid (boolean)
	li	r12,0
	cmpwi	r2,0		# first some simple sanity checks
	beqlr
	lbz	r11,8(r2)
	cmpwi	r11,1		# ln_Type == NT_TASK?
	bnelr
	lbz	r11,to_id(r2)
	cmpwi	r11,TskObj_idMagic # id == idMagic?
	bnelr
	li	r12,1
	blr
