; Source: kickproc.s
; Author: JF Fabre
; Purpose: provides full DOS environment with process structure
; Use as follows in slave:
;
;	include "kickproc.s"
;	include "kickdos.s"
;

	include	"lvo/dos.i"

CREATE_PROC_DEFINED = 1	; tell OSEmu dos.s source (included in kickdos.s) that we have
			; defined an entry for _CreateProc

;insert the following line in your sourcecode to make main process quit WHDload
;(else, infinite Wait is called)
;(this may be useful to activate original quit option of a game)
;MAIN_PROCESS_QUITS = 1

;<D1:NAME
;<D2:PRIORITY
;<D3:SEGLIST
;<D4:STACKSIZE
; this function never returns
;
; ATM this function is designed to handle
; autodetachable code, not for managing multiple tasks!!
; Successful with delphine games (Operation Stealth, Future Wars, Cruise For A Corpse)

_InternalCreateProc
	bsr	_init_process_structure
	bra.b	__CreateCliProc
_CreateProc:
	bsr	_init_process_structure
	move.l	D0,A0
	clr.l	(pr_CLI,A0)		; child process
__CreateCliProc
	movem.l	D1-D4/A2/A3/A6,-(A7)
	move.l	D0,A2			; process structure

	move.l	D4,D0
	move.l	#MEMF_CLEAR,D1
	bsr	ForeignAllocMem
	tst.l	D0
	beq	_cpfail		; not enough mem for stack

	move.l	D0,(TC_SPLOWER,A2)	; stack lower bound
	add.l	D4,D0
	move.l	D0,(TC_SPUPPER,A2)	; stack upper bound

	LSL.L	#2,D3		; BCPL -> normal pointer
	MOVE.L	D3,A1
	ADDQ.L	#4,A1		; initial PC

	; adapted from AROS.dos.AddProcess()
	
	move.l	D0,A3		; upper bound
	move.l	$4.W,-(A3)	; execbase
	move.l	A1,-(A3)	; initial PC
	bsr	.getargslen
	move.l	D0,-(A3)	; arguments length
	jsr	_LVOGetArgStr(a6)
	move.l	D0,-(A3)	; arguments pointer

	lea	-4(A3),A3
	move.l	A3,(pr_ReturnAddr,A2)	
	lea	-10(A3),A3	; safety ?
	move.l	A3,(TC_SPREG,A2)

	move.l	A2,A1	; task pointer
	sub.l	A3,A3
	lea	.start_process(pc),A2	; start PC
	tst.l	(pr_CLI,a1)
	beq.b	.noquit
	IFD	MAIN_PROCESS_QUITS
	lea	.quit(pc),A3	; end PC, only for main task
	ENDC
.noquit
	move.l	$4.W,A6
	jsr	_LVOAddTask(a6)

	movem.l	(A7)+,D1-D4/A2/A3/A6
	rts

.start_process:
	move.l	$4.W,A6
	sub.l	A1,A1
	jsr	_LVOFindTask(a6)		; find ourselves
	move.l	D0,A2
	move.l	(TC_SPUPPER,A2),D2
	sub.l	(TC_SPLOWER,A2),D2		; stack size in D2

	lea	(18,A7),A1
	move.l	(A1)+,A0			; args ptr in A0
	move.l	(A1)+,D0			; args length in D0
	move.l	D2,-(A7)			; stack size
	pea	.taskend(pc)	; return address
	move.l	(A1)+,-(A7)			; entry point

	
	; clear other registers than A0/D0/D2
	; (some 1.3 programs rely on that, like Buck Rogers)

	sub.l	A6,A6
	sub.l	A4,A4
	sub.l	A3,A3
	sub.l	A2,A2
	sub.l	A1,A1
	moveq.l	#0,D1
	moveq.l	#0,D3
	moveq.l	#0,D4
	moveq.l	#0,D5
	moveq.l	#0,D6
	moveq.l	#0,D7
	rts
.quit:
	PEA	TDREASON_OK
	MOVE.L	_resload(PC),-(A7)
	add.l	#resload_Abort,(a7)
	rts
.taskend
	addq.l	#4,A7
	rts

; > D0: args len
.getargslen
	movem.l	D1/A0/A1,-(A7)
	JSR	_LVOGetArgStr(A6)
	move.l	D0,A0
	moveq.l	#0,D0

	; computes string length
.galoop
	tst.b	(A0)+
	beq.b	.end
	addq.l	#1,D0
	bra.b	.galoop
.end
	movem.l	(A7)+,D1/A0/A1
	rts

_init_process_structure:
	movem.l	D1-D3/A0-A2,-(A7)
	move.l	#_EXECLIBPROCESS_SIZEOF-_EXECLIBTASK,D0	; sizeof process (1.3)
	move.l	#MEMF_PUBLIC|MEMF_CLEAR,D1
	bsr	ForeignAllocMem
	movem.l	(A7),D1-D3/A0-A2	; restore registers
	tst.l	D0
	beq	_cpfail

	; copy template structure

	move.l	D0,A1
	move.l	#_EXECLIBPROCESS_SIZEOF-_EXECLIBTASK-1,D0	; sizeof process (1.3)
	move.l	A1,A2		; save new process pointer
	lea	_EXECLIBTASK(pc),A0
.copy
	move.b	(A0)+,(A1)+
	dbf	D0,.copy

	; now fill in the required fields

	; process

	move.l	D4,(pr_StackSize,A2)

	bsr	.init_seglist

	; task, done later

	; node
	move.l	D1,(LN_NAME,A2)
	move.b	#NT_PROCESS,(LN_TYPE,A2)
	move.b	D2,(LN_PRI,A2)

	move.l	A2,D0			; D0: pointer on process structure
	movem.l	(A7)+,D1-D3/A0-A2
	rts


.init_seglist:
	movem.l	D0-D1/A0-A1,-(A7)
	move.l	D3,A0
	moveq.l	#0,D0
.count
	add.l	A0,A0
	add.l	A0,A0
	cmp.l	#0,A0
	beq.b	.end
	addq.l	#1,D0
	move.l	(A0),A0
	bra.b	.count
.end
	addq.l	#1,D0	; +1
	add.l	D0,D0
	add.l	D0,D0	; *4
	move.l	#MEMF_CLEAR,D1
	move.l	A6,-(A7)
	move.l	$4.W,A6
	bsr	_AllocVec
	move.l	(A7)+,A6
	move.l	D0,A1
	tst.l	D0
	beq.b	_cpfail
	lsr.l	#2,D0		: to BPTR
	move.l	D0,(pr_SegList,A2)

	; loop again

	move.l	D3,A0
	moveq.l	#0,D0
.loop
	move.l	A0,(A1)+
	add.l	A0,A0
	add.l	A0,A0
	cmp.l	#0,A0
	beq.b	.out
	move.l	(A0),A0
	bra.b	.loop
.out
	movem.l	(A7)+,D0-D1/A0-A1
	rts

_cpfail
		pea	_LVOCreateProc
		pea	_dosname
		bra	_emufail

; < A0: arguments (linefeed+0 terminated)
; < D0: BPTR seglist
; < D1: stack size (in bytes)

StartDosProcess:
	move.l	_dosbase(pc),A6

	bsr	SetArgsPtr	; kickdos.SetArgsPtr to set arguments
	bsr	InitProcessTemplate

	move.l	D0,D3		; seglist
	move.l	D1,D4		; stack size

	jsr	_LVOCli(a6)	; gets current CLI structure
	move.l	D0,A0
	move.l	D3,(cli_Module,A0)	; sets module (program seglist)	

	lea	.process_name(pc),A1
	move.l	A1,D1		; process name
	moveq.l	#0,D2		; priority
	bsr	_InternalCreateProc
	moveq.l	#0,D0
	move.l	$4.W,A6
	jsr	_LVOWait(a6)		; wait forever

.process_name:
	dc.b	"KickDos process",0
	even

InitProcessTemplate:
	movem.l	D0-D7/A0-A6,-(A7)
	lea	_EXECLIBTASK(pc),A3
	
	jsr	_LVOCli(a6)
	lsr.l	#2,D0		; APTR -> BPTR
	move.l	D0,(pr_CLI,A3)

	movem.l	(A7)+,D0-D7/A0-A6
	rts

;process structure template

_EXECLIBTASK
		; node
		DC.L	0
		DC.L	0
		DC.B	NT_PROCESS
		DC.B	0
		DC.L	$DEAD0000	; taskname *to set in AddTask*

		; task
		DC.B	0		; flags
		DC.B	TS_RUN		; state
		DC.B	0
		DC.B	0		;nest count
		DC.L	0		;TC_SIGALLOC
		DC.L	0		;TC_SIGWAIT
		DC.L	0		;TC_SIGRECVD
		DC.L	0		;TC_SIGEXCEPT
		DC.W	0		;TC_TRAPALLOC
		DC.W	0		;TC_TRAPABLE
		DC.L	0		; except data
		DC.L	0		; except code
		DC.L	0		; trap data
		DC.L	0		; trap code
		DC.L	0		; tc_SPReg
		DC.L	0		; tc_SPLower
		DC.L	0		; tc_SPUpper
		DC.L	0		; tc_Switch
		DC.L	0		; tc_Launch

		DC.L	0		; tc_MemEntry
		DC.L	0
		DC.L	0
		DC.B	0
		DC.B	0

		DC.L	0		; tc_UserData

		; end of task, start of process fields

_EXECLIBMSGPORT	DC.L	0
		DC.L	0
		DC.B	NT_MSGPORT
		DC.B	0
		DC.L	0

		DC.B	PA_SIGNAL
		DC.B	0		;BIT 0 AS SIGNALBIT
		DC.L	$DEAD0003	; *to set* to _EXECLIBTASK

		DC.L	0		;NO MESSAGES YET
		DC.L	0
		DC.L	0
		DC.B	NT_MESSAGE
		DC.B	0


		DC.W	0

		DC.L	$DEAD0001/4	;seglist *to set*

		DC.L	USPLENGTH-$20	;StackSize
		DC.L	$EEEEEEEC	;INVALID GLOBVEC
		DC.L	1		;task number=1
		DC.L	$DEAD0004	;stackbase
		DC.L	0		; result 2

		DC.L	0	;CurrentDir, change if necessary
		DC.L	0			; Current Input Stream
		DC.L	OUTPUT_HANDLER_MAGIC	; Current Output Stream (JOTD)
		DC.L	$EEEEEEEE
		DC.L	$EEEEEEEE
		DC.L	$DEAD0002/4		; BPTR on a CLI structure *to set*
		DC.L	$EEEEEEEE
		DC.L	0
		DC.L	-1			; no window
_EXECLIBPROCESS_SIZEOF

	IFNE	_EXECLIBMSGPORT-_EXECLIBTASK-$5C
	FAIL
	ENDC

	IFNE	_EXECLIBPROCESS_SIZEOF-_EXECLIBTASK-$BC
	FAIL
	ENDC
