From: cary@hpcuhe.cup.hp.com (Cary Coutant)
Newsgroups: comp.sys.hp
Subject: Re: Threading, cthreads, and PA Risc Asm
Date: 9 Nov 92 23:40:11 GMT
Organization: Hewlett Packard, Cupertino

Actually, the register save and restore is done quite easily
by letting the assembler do it for you!  A minimalist approach
to switching stacks is this:

	.proc
	.callinfo calls,save_rp,entry_gr=18,entry_fr=15,entry_sr=3
	.export switch,entry
switch
	.enter			; this saves the callee-saves registers
	copy	%sp,%ret0	; return old thread's sp
	copy	%arg0,%sp	; switch to new thread's sp
	.leave			; this restores the callee-saves registers
	.procend

This function takes a stack pointer as its argument, and returns
the old stack pointer.  The tricky part is initializing each new
thread's stack to make it look like it has already called switch,
but that's not too difficult.

Below is a slightly more complete thread package, with stack overflow
checking.  The manual page is left as an exercise for the reader.
(N.B.:  This code will not work with shared libraries!)

Cary Coutant
Hewlett-Packard
California Language Lab

----------------------------------------------------------------------
	.copyright "Hewlett Packard, 1992"

; Thread Control Block structure
;
; struct tcb {
;     long tcb_sp;
;     long tcb_stackbase;
;     long tcb_stacklimit;
;     /* anything else you may want */
;     }

TCB_SP		.equ	0
TCB_STACKBASE	.equ	4
TCB_STACKLIMIT	.equ	8


psp		.equ	64		; pseudo-register for previous sp



; long *init_thread_stack(
;		void (*code)(void),
;		void *base,
;		long size)
;
; Initializes a new thread's stack and returns the value of the
; stack pointer that the thread should initially use.
;
; <code> is the address of the function where the new thread will start.
;
; <base> is the base of the stack that has been allocated for the thread.
;
; <size> is the size of the stack.
;
; The stack frame is arranged so that it looks like the new thread
; has suspended execution at the beginning of __thread_init.  When
; the new thread is resumed, __thread_init (below) will then call
; the function <code>.

	.code

	.proc

	; ------------------------NOTE--------------------------
	; Use the first .callinfo for PA-RISC 1.0 machines.
	; Use the second for PA-RISC 1.1 machines.
	; .callinfo calls,save_rp,entry_gr=18,entry_fr=15,entry_sr=3
	; .callinfo calls,save_rp,entry_gr=18,entry_fr=21,entry_sr=3
	; ------------------------------------------------------

	.callinfo calls,save_rp,entry_gr=18,entry_fr=15,entry_sr=3
	.export init_thread_stack,entry

init_thread_stack
	.enter

	; Calculate far end of stack and align to 64-byte boundary
	add	%arg1,%arg2,%r22
	depi	0,31,6,%r22

	; Align base of stack to 64-byte boundary
	; and allocate a 64-byte stack frame
	; (This will be __thread_init's stack frame)
	ldo	64+63(%arg1),%arg1
	depi	0,31,6,%arg1

	; Store "magic number" at far end of stack for overflow detection
	mfctl	%cr16,%r21		; use interval timer as random no.
	uaddcm	%r0,%r21,%r20
	stw	%r21,-32(%r22) 		; Store Cr16 to AlgnLimit-32
	stw	%r20,-16(%r22)		; Store ~Cr16 to AlgnLimit-16

	; Mark the bottom of the new stack by storing zero in
	; the saved sp location (sp-4 of the new stack)
	stw	%r0,-4(%arg1)

	; Set the saved rp in __thread_init's stack frame so that execution
	; begins in the new thread at __thread_init
	ldil	L'__thread_init,%r1
	ldo	R'__thread_init(%r1),%r1
	stw	%r1,-20(%arg1)		; store at -20(new psp)

	; Save the <code> parameter in the new stack frame
	; (in __thread_init's parameter area)
	stw	%arg0,-36(%arg1)

	; Create a second stack frame on the new stack the same size
	; as our stack frame
	; (This will look like switch_thread's stack frame)
	; (The register save area will be garbage, but it doesn't matter)
	ldo	0(psp),%r19
	sub	%sp,%r19,%r20
	add	%r20,%arg1,%arg1

	; return the top of stack for the new thread
	copy	%arg1,%ret0

	.leave

	.procend



; __thread_init(void (*code)(void))
;
; Thread execution begins here upon return from switch_thread.
; The <code> parameter was saved in the parameter save area by
; init_thread_stack, so it must be picked up again from there.
;
; This is an internal routine.  It is never really "called";
; instead, it is "returned into" from switch_thread the first
; time that a thread is resumed.

	.proc
	.callinfo calls,save_sp
	.export	__thread_init,entry

__thread_init
	; No entry sequence -- the stack frame was already initialized
	; for us by init_thread_stack

	; Retrieve the <code> parameter from the stack frame
	ldw	-36(%sp),%arg0

	; Call <code> (in r3) to initiate new thread
	bl	.+8,%rp
	bv,n	0(%arg0)

	; control should never return here
	break	0,0

	.procend



; void switch_thread(struct tcb *new_thread)
;
; Transfers control from one thread to another.
; Saves minimal state of the old thread and restores
; minimal state for the new thread.
;
; init_thread_stack and switch_thread must have identical frame sizes.

	.proc

	; ------------------------NOTE--------------------------
	; Use the first .callinfo for PA-RISC 1.0 machines.
	; Use the second for PA-RISC 1.1 machines.
	; .callinfo calls,save_rp,entry_gr=18,entry_fr=15,entry_sr=3
	; .callinfo calls,save_rp,entry_gr=18,entry_fr=21,entry_sr=3
	; ------------------------------------------------------

	.callinfo calls,save_rp,entry_gr=18,entry_fr=15,entry_sr=3
	.export switch_thread,entry

switch_thread
	.enter

	; Get address of current thread's TCB from <cur_thread>
	.import cur_thread,data
	.import	$global$,data
	addil	L'cur_thread-$global$,%dp
	ldw	R'cur_thread-$global$(%r1),%r19

	; Check for stack overflow
	ldw	TCB_STACKLIMIT(%r19),%r20
	depi	0,31,6,%r20	
	ldw	-16(%r20),%r22
	ldw	-32(%r20),%r21
	uaddcm	%r0,%r22,%r22
	xor,=	%r22,%r21,%r0
	break	0,0

	; Switch stacks
	stw	%sp,TCB_SP(%r19)	; save old thread's sp in its TCB
	ldw	TCB_SP(%arg0),%sp	; get new thread's sp from <new_thread>

	; We continue here, now on the new thread's stack.
	; Put address of new TCB in <cur_thread>
	;addil	L'cur_thread-$global$,%dp ; (r1 is still live from above)
	stw	%arg0,R'cur_thread-$global$(%r1)
	
	.leave

	.procend

	.end
From: jeff@hpfcmgw.FC.HP.COM (Jeff Taylor)
Date: Fri, 6 Nov 1992 20:33:11 GMT
Subject: Re: SCSI, SCSI-II, Differential SCSI
Organization: HP Fort Collins, CO