*:ts=8
*****************************************************************************
*                                                                           *
* RES.S                                    (C) Copyright Eddy Carroll 1989  *
*                                                                           *
* This module allows you to make a duplicate copy of your current process.  *
* In fact, both processes actually share the same code. However, the        *
* seglist pointer of the current process is modified so that when the       *
* process terminates, the memory doesn't get freed (if it did, the new      *
* process would suddenly find itself deallocated. Hello guru...)            *
* This code only works when called from CLI processes.                      *
*                                                                           *
* The parameters passed are similar to those for CreateProc(), with the     *
* difference that instead of passing a BPTR to a seglist, you pass the      *
* address of the function the new process should start executing at.        *
*                                                                           *
* When the new process returns from this function, it will be removed from  *
* the system, and its memory (finally) deallocated.                         *
*                                                                           *
* The typical use for a function like this is to allow a program to detach  *
* itself from a CLI (completely, with no trailing console handles etc.)     *
* when it is run. This is a convenient feature for the user, if the program *
* is of the sort designed to sit in the background the whole time, rather   *
* than do something immediately, then exit.                                 *
*                                                                           *
* Lattice provide cback.o which at first glance would seem to provide a     *
* similar solution. However, cback.o makes it difficult to print error      *
* messages to the console if there is an error on the command line - by     *
* the time you spot the error, the CLI prompt has already been printed      *
* and your error message is printed after it. This looks very messy.        *
* Res avoids this problem by not spawning the background process until      *
* after the error checking has been done.                                   *
*                                                                           *
* From C, you call it as follows:                                           *
*                                                                           *
* pid = res(name,pri,func,stacksize)                                        *
*                                                                           *
* name         - pointer to null terminated string                          *
* pri          - integer, priority of the new process                       *
* func         - pointer to the function for new process to call            *
* stacksize    - integer, size of the stack for the new process             *
*                                                                           *
* pid          - Process ID of new process, or 0 if none created            *
*                                                                           *
*****************************************************************************

	INCLUDE "exec/types.i"
        INCLUDE "exec/alerts.i"
        INCLUDE "exec/nodes.i"
        INCLUDE "exec/lists.i"
        INCLUDE "exec/ports.i"
        INCLUDE "exec/libraries.i"
        INCLUDE "exec/tasks.i"
        INCLUDE "libraries/dos.i"
        INCLUDE "libraries/dosextens.i"
        INCLUDE "workbench/startup.i"
        INCLUDE "exec/funcdef.i"
        INCLUDE "exec/exec_lib.i"
        INCLUDE "libraries/dos_lib.i"

	xref	_exit
	xref	_DOSBase
	xdef	_res

AbsExecBase	equ	4
segsize		equ	36		; Size of fake seg. (code = 28 bytes)

        csect   text,0,0,1,2		* xref's after this are 16-bit reloc


callsys macro
        CALLLIB _LVO\1
	endm

_res:
	movem.l	d2-d4/a2/a3/a6,-(a7)	; Save registers
	move.l	AbsExecBase.w,a6	; Get base of Exec library
	moveq	#0,d1			; Any sort of memory will do
	moveq	#segsize,d0		; Get size of fake segment
	callsys	AllocMem		; Grab some memory
	tst.l	d0			; Did we get any?
	beq	fatal			; If not, abort immediately!
	move.l	d0,a3			; Save pointer to memory
	sub.l	a1,a1			; NULL pointer indicates our process
	callsys	FindTask		; Get pointer to our process block
	move.l	d0,a2			; Save it
	move.l	pr_CLI(A2),a0		; Get BPTR to our process's segarray
	add.l	a0,a0			; Convert BPTR to address
	add.l	a0,a0			; 
	move.l	cli_Module(a0),4(a3)	; Make fake segment point to our code
	clr.l	cli_Module(a0)		; Remove process seg. from CLI seglist
	move.l	#segsize,(a3)		; Set size of fake seglist
	lea.l	8(a3),a2		; Get pointer to first code byte

;
; Now a tiny machine code program is constructed. It looks like this:
;
;	move.l	#$xxxxxx,A4	; Initialise A4
;	jsr	$xxxxxx		; Call user program
;	move.l	#$xxxxxx,A6	; Load DOSbase into A6
;	move.l	#$xxxxxx,d1	; Get BPTR to this segment 
;	jmp	UnLoadSeg(A6)	; Unload our process from memory
;
; It's built "on the fly" so to speak, to keep code size down, and also
; because it's a convenient way of initialising all the variables.
; Note that a potential problem exists if DOSBase should somehow alter or
; disappear. We'll assume it will remain relatively stable for the next
; few years anyway :-)
;

	move.l	_DOSBase,a6		; Prepare for DOS call
	move.w	#$287C,(a2)+		; Store MOVE.L $xxxxxx,A4 instruction
	move.l	a4,(a2)+		; Output value of A4 to initialise to
	move.w	#$4EB9,(a2)+		; Store JSR $xxxxxx
	move.l	36(a7),(a2)+		; followed by address of user function
	move.w	#$2C7C,(a2)+		; Store MOVE.L $xxxxxx,A6 instruction
	move.l	a6,(a2)+		; followeds by current DOSbase
;
	lea	4(a3),a3		; Now get seglist ptr to fake segment
	move.l	a3,d3			; and convert it to BPTR
	lsr.l	#2,d3			; D3 now has seglist ptr to fake seg
	move.w	#$223C,(a2)+		; Store MOVE.L $xxxxxx,D1 instruction
	move.l	d3,(a2)+		; Followed by BPTR to the segment
	move.l	#$4EEEFF64,(a2)+	; Store JMP UnLoadSeg(A6)
;
	move.l	28(A7),d1		; Get pointer to name
	move.l	32(A7),d2		; Get process priority
	move.l	40(A7),d4		; Get stacksize
	callsys	CreateProc		; Create new process
	movem.l	(a7)+,d2-d4/a2/a3/a6	; Pop registers
	rts				; Return
fatal:
	moveq	#120,d0			; Set error exit code
	jmp	_exit			; And exit

	end
