home *** CD-ROM | disk | FTP | other *** search
/ NetNews Usenet Archive 1992 #26 / NN_1992_26.iso / spool / comp / sys / hp / 12726 < prev    next >
Encoding:
Internet Message Format  |  1992-11-09  |  6.2 KB

  1. 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
  2. From: cary@hpcuhe.cup.hp.com (Cary Coutant)
  3. Newsgroups: comp.sys.hp
  4. Subject: Re: Threading, cthreads, and PA Risc Asm
  5. Message-ID: <31480276@hpcuhe.cup.hp.com>
  6. Date: 9 Nov 92 23:40:11 GMT
  7. References: <1992Nov2.091258.6912@codex.oz.au>
  8. Organization: Hewlett Packard, Cupertino
  9. Lines: 215
  10.  
  11. Actually, the register save and restore is done quite easily
  12. by letting the assembler do it for you!  A minimalist approach
  13. to switching stacks is this:
  14.  
  15.     .proc
  16.     .callinfo calls,save_rp,entry_gr=18,entry_fr=15,entry_sr=3
  17.     .export switch,entry
  18. switch
  19.     .enter            ; this saves the callee-saves registers
  20.     copy    %sp,%ret0    ; return old thread's sp
  21.     copy    %arg0,%sp    ; switch to new thread's sp
  22.     .leave            ; this restores the callee-saves registers
  23.     .procend
  24.  
  25. This function takes a stack pointer as its argument, and returns
  26. the old stack pointer.  The tricky part is initializing each new
  27. thread's stack to make it look like it has already called switch,
  28. but that's not too difficult.
  29.  
  30. Below is a slightly more complete thread package, with stack overflow
  31. checking.  The manual page is left as an exercise for the reader.
  32. (N.B.:  This code will not work with shared libraries!)
  33.  
  34. Cary Coutant
  35. Hewlett-Packard
  36. California Language Lab
  37.  
  38. ----------------------------------------------------------------------
  39.     .copyright "Hewlett Packard, 1992"
  40.  
  41. ; Thread Control Block structure
  42. ;
  43. ; struct tcb {
  44. ;     long tcb_sp;
  45. ;     long tcb_stackbase;
  46. ;     long tcb_stacklimit;
  47. ;     /* anything else you may want */
  48. ;     }
  49.  
  50. TCB_SP        .equ    0
  51. TCB_STACKBASE    .equ    4
  52. TCB_STACKLIMIT    .equ    8
  53.  
  54.  
  55. psp        .equ    64        ; pseudo-register for previous sp
  56.  
  57.  
  58.  
  59. ; long *init_thread_stack(
  60. ;        void (*code)(void),
  61. ;        void *base,
  62. ;        long size)
  63. ;
  64. ; Initializes a new thread's stack and returns the value of the
  65. ; stack pointer that the thread should initially use.
  66. ;
  67. ; <code> is the address of the function where the new thread will start.
  68. ;
  69. ; <base> is the base of the stack that has been allocated for the thread.
  70. ;
  71. ; <size> is the size of the stack.
  72. ;
  73. ; The stack frame is arranged so that it looks like the new thread
  74. ; has suspended execution at the beginning of __thread_init.  When
  75. ; the new thread is resumed, __thread_init (below) will then call
  76. ; the function <code>.
  77.  
  78.     .code
  79.  
  80.     .proc
  81.  
  82.     ; ------------------------NOTE--------------------------
  83.     ; Use the first .callinfo for PA-RISC 1.0 machines.
  84.     ; Use the second for PA-RISC 1.1 machines.
  85.     ; .callinfo calls,save_rp,entry_gr=18,entry_fr=15,entry_sr=3
  86.     ; .callinfo calls,save_rp,entry_gr=18,entry_fr=21,entry_sr=3
  87.     ; ------------------------------------------------------
  88.  
  89.     .callinfo calls,save_rp,entry_gr=18,entry_fr=15,entry_sr=3
  90.     .export init_thread_stack,entry
  91.  
  92. init_thread_stack
  93.     .enter
  94.  
  95.     ; Calculate far end of stack and align to 64-byte boundary
  96.     add    %arg1,%arg2,%r22
  97.     depi    0,31,6,%r22
  98.  
  99.     ; Align base of stack to 64-byte boundary
  100.     ; and allocate a 64-byte stack frame
  101.     ; (This will be __thread_init's stack frame)
  102.     ldo    64+63(%arg1),%arg1
  103.     depi    0,31,6,%arg1
  104.  
  105.     ; Store "magic number" at far end of stack for overflow detection
  106.     mfctl    %cr16,%r21        ; use interval timer as random no.
  107.     uaddcm    %r0,%r21,%r20
  108.     stw    %r21,-32(%r22)         ; Store Cr16 to AlgnLimit-32
  109.     stw    %r20,-16(%r22)        ; Store ~Cr16 to AlgnLimit-16
  110.  
  111.     ; Mark the bottom of the new stack by storing zero in
  112.     ; the saved sp location (sp-4 of the new stack)
  113.     stw    %r0,-4(%arg1)
  114.  
  115.     ; Set the saved rp in __thread_init's stack frame so that execution
  116.     ; begins in the new thread at __thread_init
  117.     ldil    L'__thread_init,%r1
  118.     ldo    R'__thread_init(%r1),%r1
  119.     stw    %r1,-20(%arg1)        ; store at -20(new psp)
  120.  
  121.     ; Save the <code> parameter in the new stack frame
  122.     ; (in __thread_init's parameter area)
  123.     stw    %arg0,-36(%arg1)
  124.  
  125.     ; Create a second stack frame on the new stack the same size
  126.     ; as our stack frame
  127.     ; (This will look like switch_thread's stack frame)
  128.     ; (The register save area will be garbage, but it doesn't matter)
  129.     ldo    0(psp),%r19
  130.     sub    %sp,%r19,%r20
  131.     add    %r20,%arg1,%arg1
  132.  
  133.     ; return the top of stack for the new thread
  134.     copy    %arg1,%ret0
  135.  
  136.     .leave
  137.  
  138.     .procend
  139.  
  140.  
  141.  
  142. ; __thread_init(void (*code)(void))
  143. ;
  144. ; Thread execution begins here upon return from switch_thread.
  145. ; The <code> parameter was saved in the parameter save area by
  146. ; init_thread_stack, so it must be picked up again from there.
  147. ;
  148. ; This is an internal routine.  It is never really "called";
  149. ; instead, it is "returned into" from switch_thread the first
  150. ; time that a thread is resumed.
  151.  
  152.     .proc
  153.     .callinfo calls,save_sp
  154.     .export    __thread_init,entry
  155.  
  156. __thread_init
  157.     ; No entry sequence -- the stack frame was already initialized
  158.     ; for us by init_thread_stack
  159.  
  160.     ; Retrieve the <code> parameter from the stack frame
  161.     ldw    -36(%sp),%arg0
  162.  
  163.     ; Call <code> (in r3) to initiate new thread
  164.     bl    .+8,%rp
  165.     bv,n    0(%arg0)
  166.  
  167.     ; control should never return here
  168.     break    0,0
  169.  
  170.     .procend
  171.  
  172.  
  173.  
  174. ; void switch_thread(struct tcb *new_thread)
  175. ;
  176. ; Transfers control from one thread to another.
  177. ; Saves minimal state of the old thread and restores
  178. ; minimal state for the new thread.
  179. ;
  180. ; init_thread_stack and switch_thread must have identical frame sizes.
  181.  
  182.     .proc
  183.  
  184.     ; ------------------------NOTE--------------------------
  185.     ; Use the first .callinfo for PA-RISC 1.0 machines.
  186.     ; Use the second for PA-RISC 1.1 machines.
  187.     ; .callinfo calls,save_rp,entry_gr=18,entry_fr=15,entry_sr=3
  188.     ; .callinfo calls,save_rp,entry_gr=18,entry_fr=21,entry_sr=3
  189.     ; ------------------------------------------------------
  190.  
  191.     .callinfo calls,save_rp,entry_gr=18,entry_fr=15,entry_sr=3
  192.     .export switch_thread,entry
  193.  
  194. switch_thread
  195.     .enter
  196.  
  197.     ; Get address of current thread's TCB from <cur_thread>
  198.     .import cur_thread,data
  199.     .import    $global$,data
  200.     addil    L'cur_thread-$global$,%dp
  201.     ldw    R'cur_thread-$global$(%r1),%r19
  202.  
  203.     ; Check for stack overflow
  204.     ldw    TCB_STACKLIMIT(%r19),%r20
  205.     depi    0,31,6,%r20    
  206.     ldw    -16(%r20),%r22
  207.     ldw    -32(%r20),%r21
  208.     uaddcm    %r0,%r22,%r22
  209.     xor,=    %r22,%r21,%r0
  210.     break    0,0
  211.  
  212.     ; Switch stacks
  213.     stw    %sp,TCB_SP(%r19)    ; save old thread's sp in its TCB
  214.     ldw    TCB_SP(%arg0),%sp    ; get new thread's sp from <new_thread>
  215.  
  216.     ; We continue here, now on the new thread's stack.
  217.     ; Put address of new TCB in <cur_thread>
  218.     ;addil    L'cur_thread-$global$,%dp ; (r1 is still live from above)
  219.     stw    %arg0,R'cur_thread-$global$(%r1)
  220.     
  221.     .leave
  222.  
  223.     .procend
  224.  
  225.     .end
  226.