home *** CD-ROM | disk | FTP | other *** search
- Path: sparky!uunet!noc.near.net!hri.com!spool.mu.edu!sdd.hp.com!scd.hp.com!hpscdm!cupnews0.cup.hp.com!hppad.waterloo.hp.com!hppad!hpfcso!hpcss01!hpcuhe!cary
- From: cary@hpcuhe.cup.hp.com (Cary Coutant)
- Newsgroups: comp.sys.hp
- Subject: Re: Threading, cthreads, and PA Risc Asm
- Message-ID: <31480276@hpcuhe.cup.hp.com>
- Date: 9 Nov 92 23:40:11 GMT
- References: <1992Nov2.091258.6912@codex.oz.au>
- Organization: Hewlett Packard, Cupertino
- Lines: 215
-
- 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
-