home *** CD-ROM | disk | FTP | other *** search
/ Frostbyte's 1980s DOS Shareware Collection / floppyshareware.zip / floppyshareware / DOOG / CTASK.ZIP / TSKASM.ASM < prev    next >
Assembly Source File  |  1989-12-22  |  14KB  |  670 lines

  1. ;
  2. ;    --- Version 2.0 89-12-14 12:24 ---
  3. ;
  4. ;    CTask - Scheduler and miscellaneous utilities
  5. ;
  6. ;    Public Domain Software written by
  7. ;        Thomas Wagner
  8. ;        Patschkauer Weg 31
  9. ;        D-1000 Berlin 33
  10. ;        West Germany
  11. ;
  12. ;
  13. ;**************************  CAUTION:  ***********************************
  14. ;
  15. ;    If the DOS flag is set, the scheduler uses an undocumented feature
  16. ;    of DOS versions >= 3.10 to save and restore certain variables
  17. ;    local to the DOS kernel. This allows for fast, secure task switching
  18. ;    with tasks owning different PSP's. To save and restore the complete
  19. ;    state of DOS on every task switch would not be feasible with any
  20. ;    other documented method.
  21. ;
  22. ;    NOTE that this method relies on certain inner workings of DOS
  23. ;    that may not be compatible with future releases or "compatible"
  24. ;    operating systems. It has been tested with all DOS versions
  25. ;    from 3.10 through 3.20, 3.30, up to PC-DOS 4.00. It
  26. ;    has not been tested with any DOS-clones like those from DRI.
  27. ;
  28. ;*************************************************************************
  29. ;
  30.     name    tskasm
  31.     .model    large,c
  32. ;
  33.     public    tsk_scheduler
  34.     public    schedule
  35.     public    yield
  36.     public    c_schedule
  37.     public    sched_int
  38. ;
  39.     public    tsk_dis_int
  40.     public    tsk_ena_int
  41.     public    tsk_cli
  42.     public    tsk_sti
  43.     public    tsk_dseg
  44.     public    tsk_flags
  45.     public    tsk_outp
  46.     public    tsk_inp
  47.     public    tsk_inpw
  48.     public    tsk_nop
  49. ;
  50.     public    tsk_dgroup
  51. ;
  52.     include    tsk.mac
  53. ;
  54. LSTACK_SIZE    =    256    ; local stack size in words
  55. ;
  56. INT_FLAG    =    2h    ; Int enable flag in upper byte of flag reg
  57. ;
  58. sr_flags    =    8    ; Offset of flag register on task stack
  59. sr_ds        =    2    ; Offset of DS on task stack
  60. ;
  61. psp_offset    =    10h    ; Offset of current PSP in DOS save area
  62. psp_savoff    =    2eh    ; Offset of PSP stack pointer save dword
  63. ;
  64.     .tsk_data
  65. ;
  66.     extrn    tsk_global: dword
  67. ;
  68.     dw    LSTACK_SIZE dup(?)
  69. slocal_stack    label    word
  70. ;
  71.     .tsk_edata
  72.     .tsk_code
  73. ;
  74. tsk_dgroup    dw    @CTASK_DATA
  75. ;
  76. ;------------------------------------------------------------------------
  77. ;
  78. ;    enq    Enqueue tcb in queue. For local use only.
  79. ;        entry:    es:di = tcb to enqueue
  80. ;        exit:    -
  81. ;        uses:    ax, cx, si
  82. ;
  83. enq    macro
  84. ;
  85.     push    ds
  86.     lds    si,es:qhead[di]        ; queue head pointer
  87.     mov    ax,ds
  88.     or    ax,si    
  89.     jz    enq_end            ; nothing left to do if queue null
  90. ;
  91.     mov    cx,es:cqueue.q_el.q_prior[di]
  92.     mov    ax,es:cqueue.q_el.q_ini_prior[di]
  93.     mov    es:cqueue.q_el.q_prior[di],ax
  94.     lds    si,q_last[si]        ; last queue element
  95. ;
  96. enq_loop:
  97.     cmp    q_kind[si],0        ; at head?
  98.     je    enq_found        ; then insert
  99.     cmp    q_el.q_prior[si],cx    ; else check priority
  100.     jae    enq_found        ; if above or equal, insert
  101. ;
  102.     lds    si,q_prev[si]        ; backup one element
  103.     jmp    enq_loop        ; and try again
  104. ;
  105. enq_found:
  106.     mov    word ptr es:q_prev[di],si    ; elem->prev = curr
  107.     mov    word ptr es:q_prev+2[di],ds
  108.     mov    ax,word ptr q_next[si]        ; elem->next = curr->next;
  109.     mov    word ptr es:q_next[di],ax
  110.     mov    cx,word ptr q_next+2[si]
  111.     mov    word ptr es:q_next+2[di],cx
  112.     mov    word ptr q_next[si],di        ; curr->next = elem;
  113.     mov    word ptr q_next+2[si],es
  114.     mov    si,ax
  115.     mov    ds,cx
  116.     mov    word ptr q_prev[si],di        ; elem->next->prev = elem
  117.     mov    word ptr q_prev+2[si],es
  118. ;
  119. enq_end:
  120.     pop    ds
  121. ;
  122.     endm
  123. ;
  124. ;    upd_prior: Update priority of tasks in eligible queue.
  125. ;               Only activated if tsk_var_prior is nonzero.
  126. ;
  127. ;    entry:    ds:bx = global variable block
  128. ;    exit:    -
  129. ;    uses:    ax,di,es
  130. ;
  131. ;    NOTE:    Contrary to what earlier versions said, this loop
  132. ;        must be protected by interrupt disable.
  133. ;        Since it steps through a pointer chain, and this
  134. ;        pointer chain might be modified by external events
  135. ;        (a task becoming eligible to run), there is indeed
  136. ;        a danger of race conditions.
  137. ;
  138. upd_prior    macro
  139. ;
  140.     les    di,eligible_queue.q_first[bx]
  141. ;
  142. pinc_loop:
  143.     cmp    es:q_kind[di],0        ; queue head?
  144.     je    updp_end
  145.     inc    es:q_el.q_prior[di]
  146.     jnz    pinc_nxt
  147.     dec    es:q_el.q_prior[di]
  148. pinc_nxt:
  149.     les    di,es:q_next[di]
  150.     jmp    pinc_loop
  151. ;
  152. updp_end:
  153. ;
  154.     endm
  155. ;
  156. ;-------------------------------------------------------------------------
  157. ;
  158. ;    The scheduler. Note that this routine is entered with the stack
  159. ;    set up as for an interrupt handler.
  160. ;
  161. tsk_scheduler    proc    far
  162. ;
  163.     cli            ; better safe than sorry
  164. ;
  165. ;    First, check if wer're already in the scheduler. This might
  166. ;    happen if an interrupt handler schedules, and it would have
  167. ;    catastrophic results if the scheduler would be re-entered.
  168. ;    Previous versions used a code-segment based variable to store
  169. ;    this flag. This version stores the flag in the global variable
  170. ;    block, to support ROM-based implementations.
  171. ;
  172.     push    ds
  173.     push    bx
  174.     mov    ds,cs:tsk_dgroup
  175.     lds    bx,tsk_global
  176.     cmp    in_sched[bx],0    ; already in the scheduler?
  177.     je    sched_ok    ; continue if not
  178.     pop    bx
  179.     pop    ds        ; else return immediately
  180.     iret
  181. ;
  182. ;    store registers in TCB to keep stack usage minimal
  183. ;
  184. sched_ok:
  185.     inc    in_sched[bx]
  186.     push    ax
  187.     lds    bx,current_task[bx]
  188.     mov    ax,ds
  189.     or    ax,bx
  190.     pop    ax
  191. ;
  192. ;    if there is no current task (i.e. it has been killed),
  193. ;    we can't store the registers.
  194. ;
  195.     jz    no_rsave
  196.     mov    t_sp[bx],sp
  197.     mov    t_ss[bx],ss
  198.     mov    t_ax[bx],ax
  199.     mov    t_cx[bx],cx
  200.     mov    t_dx[bx],dx
  201.     mov    t_bp[bx],bp
  202.     mov    t_si[bx],si
  203.     mov    t_di[bx],di
  204.     mov    t_es[bx],es
  205.     mov    bp,sp
  206.     or    byte ptr sr_flags+1[bp],INT_FLAG ; enable interrupts on return
  207.     mov    ax,sr_ds[bp]
  208.     mov    t_ds[bx],ax
  209. ;
  210. no_rsave:
  211.     mov    ss,cs:tsk_dgroup    ; switch to local stack
  212.     mov    sp,offset slocal_stack
  213.     cld
  214.     mov    ds,cs:tsk_dgroup    ; establish addressing of our vars
  215.     lds    bx,tsk_global
  216. ;
  217.     cmp    var_prior[bx],0
  218.     je    no_var_pri
  219. ;
  220.     upd_prior            ; update priorities in eligible queue
  221. ;
  222. no_var_pri:
  223. ;
  224.     les    di,current_task[bx]    ; get current tcb
  225.     mov    ax,es            ; check if NULL (current task killed)
  226.     or    ax,di
  227.     jz    no_current
  228. ;
  229.     cmp    es:state[di],ST_RUNNING
  230.     jne    not_eligible
  231.     mov    es:state[di],ST_ELIGIBLE
  232. ;
  233. not_eligible:
  234. ;
  235.     enq                ; Enqueue current task
  236. ;
  237. no_current:
  238.     mov    pretick[bx],0        ; No preemption tick
  239.     and    preempt[bx],1        ; Turn off temp preempt flag
  240. ;
  241.     lea    si,eligible_queue[bx]
  242. ;
  243. ;    Now we enter an enabled loop if there is no task in the
  244. ;    eligible queue.
  245. ;
  246. wait_elig:
  247.     les    di,q_first[si]
  248.     cmp    es:q_kind[di],0
  249.     jne    not_empty        ; jump if not
  250. ;
  251.     sti                       ; enable interrupts
  252.     nop
  253.     nop
  254.     cli
  255.     jmp    wait_elig
  256. ;
  257. ;    Eligible queue not empty, activate first eligible task.
  258. ;
  259. not_empty:
  260.     push    di
  261.     push    es
  262.     mov    bp,sp            ; address new pointer thru BP
  263. ;
  264.     mov    ax,word ptr es:cqueue.q_next[di] ; next in eligible queue
  265.     mov    cx,word ptr es:cqueue.q_next+2[di]
  266.     mov    word ptr es:cqueue.q_next[di],0    ; mark not in queue
  267.     mov    word ptr es:cqueue.q_next+2[di],0
  268.     mov    di,ax
  269.     mov    es,cx
  270.     mov    word ptr q_first[si],di
  271.     mov    word ptr q_first+2[si],es    ; eligible_queue.first = next
  272.     mov    word ptr es:q_prev[di],si
  273.     mov    word ptr es:q_prev+2[di],ds    ; next->prev = eligible_queue
  274. ;
  275. ;    Now check if the new task is the same as the old one.
  276. ;    If that's the case, we skip the save/restore function calls,
  277. ;    and the DOS variable copy.
  278. ;
  279.     les    di,current_task[bx]    ; the previously running task
  280.     mov    ax,es
  281.     cmp    ax,[bp]            ; same as the new one ?
  282.     jne    chk_sav            ; jump if not same segment
  283.     cmp    di,2[bp]
  284.     jne    chk_sav            ; jump if not same offset
  285.     jmp    no_setdos        ; don't save/restore if same task
  286. chk_sav:
  287.     or    ax,di
  288.     jz    no_savedos        ; don't save if no previous
  289. ;
  290. ;    First, call the save function if one is defined in the old TCB.
  291. ;
  292.     mov    ax,word ptr es:save_func[di]
  293.     or    ax,word ptr es:save_func+2[di]
  294.     jz    no_savfcall        ; jump if no save function
  295. ;
  296.     push    ds            ; save our registers
  297.     push    bx
  298.     push    es
  299.     push    di
  300.     push    word ptr es:save_func+2[di]    ; push address to call
  301.     push    word ptr es:save_func[di]
  302.     mov    bp,sp
  303.     push    es            ; push TCB address
  304.     push    di
  305.     mov    ds,es:t_ds[di]        ; load current DS
  306.     mov    es,es:t_es[di]
  307.     call    dword ptr [bp]        ; call function
  308.         add     sp,8
  309.         pop     di            ; restore registers
  310.         pop     es
  311.         pop     bx
  312.         pop     ds
  313. ;
  314. ;    Save the DOS variables, and the DOS-related interrupt vectors
  315. ;    in the old TCB.
  316. ;
  317. no_savfcall:
  318.     IF    DOS
  319.     push    ds
  320.     mov    ax,ds
  321.     mov    es:t_new[di],0        ; task is no longer new
  322.     mov    ds,es:base_psp[di]    ; get base PSP address
  323.     mov    si,psp_savoff        ; offset to save (caller's SS:SP)
  324.     add    di,psp_sssp        ; destination
  325.     movsw                ; save two words
  326.     movsw
  327.     mov    ds,ax
  328.     mov    cx,l_swap[bx]        ; swap area addr & size
  329.     jcxz    no_swap_sav
  330.     lds    si,dos_vars[bx]
  331.     rep movsb
  332. ;
  333. no_swap_sav:
  334.     xor    cx,cx            ; copy int21-24 interrupt vectors
  335.     mov    ds,cx
  336.     mov    si,21h*4
  337.     mov    cx,8
  338.     rep movsw
  339. ;
  340.     pop    ds
  341.     ENDIF
  342. ;
  343. ;    Save complete. Now check for a restore function in the new TCB.
  344. ;
  345. no_savedos:
  346.     pop    es
  347.     pop    di
  348.     mov    word ptr current_task[bx],di     ; set tcb into current
  349.     mov    word ptr current_task+2[bx],es
  350. ;
  351.     mov    ax,word ptr es:rest_func[di]
  352.     or    ax,word ptr es:rest_func+2[di]
  353.     jz    no_resfcall            ; jump if no restore function
  354. ;
  355.     push    bx                ; save our regs
  356.     push    ds
  357.     push    di
  358.     push    es
  359.     push    word ptr es:rest_func+2[di]    ; push addr to call
  360.     push    word ptr es:rest_func[di]
  361.     mov    bp,sp
  362.     push    es                ; push TCB addr
  363.     push    di
  364.     mov    ds,es:t_ds[di]            ; load current DS
  365.     mov    es,es:t_es[di]
  366.     call    dword ptr [bp]            ; call restore function
  367.         add     sp,8
  368.         pop     es
  369.         pop     di
  370.         pop     ds
  371.         pop     bx
  372. ;
  373. ;    Restore DOS variables and int vectors from new TCB.
  374. ;
  375. no_resfcall:
  376.     IF    DOS
  377.     cmp    es:t_new[di],0        ; is this TCB a fresh one?
  378.     jne    no_setdos        ; then the DOS info isn't valid.
  379. ;
  380.     push    di
  381.     push    es
  382.     push    ds
  383.     mov    dx,ds
  384.     mov    ax,es
  385.     mov    ds,ax
  386.     mov    si,di
  387.     add    si,base_psp
  388.     lodsw
  389.     mov    es,ax
  390.     mov    di,psp_savoff        ; offset to restore (caller's SS:SP)
  391.     movsw                ; restore two words
  392.     movsw
  393. ;
  394.     mov    ax,ds
  395.     mov    ds,dx
  396.     mov    cx,l_swap[bx]
  397.     jcxz    no_swap_rest
  398.     les    di,dos_vars[bx]
  399.     mov    ds,ax
  400.     rep movsb
  401. ;
  402. no_swap_rest:
  403.     mov    ds,ax
  404.     mov    di,21h*4        ; restore int21-24
  405.     xor    cx,cx
  406.     mov    es,cx
  407.     mov    cx,8
  408.     rep movsw
  409. ;
  410.     pop    ds
  411.     pop    es
  412.     pop    di
  413.     ENDIF
  414. ;
  415. ;    And that's it. Wrap it up by resetting the priority, resetting
  416. ;    the in_sched flag, restoring the registers, and returning to 
  417. ;    the task.
  418. ;
  419. no_setdos:
  420.     mov    ax,es:q_el.q_ini_prior[di]    ; reset current tasks priority
  421.     mov    es:q_el.q_prior[di],ax
  422.     mov    es:state[di],ST_RUNNING        ; set task state
  423. ;
  424.     mov    in_sched[bx],0            ; reset scheduler active flag
  425. ;
  426.     push    es
  427.     push    di
  428.     pop    bx
  429.     pop    ds
  430.     mov    es,t_es[bx]            ; restore all registers
  431.     mov    di,t_di[bx]
  432.     mov    si,t_si[bx]
  433.     mov    bp,t_bp[bx]
  434.     mov    dx,t_dx[bx]
  435.     mov    cx,t_cx[bx]
  436.     mov    ax,t_ax[bx]
  437.     mov    ss,t_ss[bx]
  438.     mov    sp,t_sp[bx]
  439.     pop    bx
  440.     pop    ds
  441.     iret
  442. ;
  443. tsk_scheduler    endp
  444. ;
  445. ;
  446. ;--------------------------------------------------------------------------
  447. ;
  448. ;
  449. ;    sched_int  
  450. ;
  451. ;    Is the scheduler entry for interrupt handlers.
  452. ;    It checks if preemption is allowed, returning if not.
  453. ;    The stack is assumed to be set up as on interrupt entry.
  454. ;    
  455. sched_int    proc    far
  456. ;
  457.     push    ds
  458.     push    bx
  459.     mov    ds,cs:tsk_dgroup
  460.         lds     bx,tsk_global
  461.     cmp    preempt[bx],0        ; preempt flags 0?
  462.     jne    no_sched1        ; no scheduling if set
  463.     lds    bx,current_task[bx]    ; current running task
  464.     test    flags[bx],F_CRIT    ; preemption allowed for this task?
  465.     jnz    no_sched        ; no scheduling if flag set
  466.     IF    DOS
  467.     cmp    t_indos[bx],OWN_UPPER or SPAWNING
  468.     je    no_sched        ; no scheduling if spawning
  469.     ENDIF
  470.     pop    bx            ; else go schedule
  471.     pop    ds
  472.     jmp    tsk_scheduler
  473. ;
  474. no_sched:
  475.     mov    ds,cs:tsk_dgroup
  476.         lds     bx,tsk_global
  477. no_sched1:
  478.     mov    pretick[bx],1        ; Mark preemption pending
  479.     pop    bx
  480.     pop    ds
  481.     iret
  482. ;
  483. sched_int    endp
  484. ;
  485. ;
  486. ;    void far schedule (void)
  487. ;
  488. ;    Entry for calling the scheduler. Rearranges the stack to
  489. ;    contain flags.
  490. ;    NOTE: Uses ax,bx.
  491. ;
  492. schedule    proc    far
  493. ;
  494.     pop    ax
  495.     pop    bx
  496.     pushf
  497.     push    bx
  498.     push    ax
  499.     cli
  500.     jmp    tsk_scheduler
  501. ;
  502. schedule    endp
  503. ;
  504. ;
  505. ;    void far yield (void)
  506. ;
  507. ;    Entry for calling the scheduler with priority temporarily
  508. ;       set to zero. Rearranges the stack to contain flags.
  509. ;    NOTE: Uses ax,bx.
  510. ;
  511. yield    proc    far
  512. ;
  513.     pop    ax
  514.     pop    bx
  515.     pushf
  516.     push    bx
  517.     push    ax
  518.         push    es
  519.     mov    es,cs:tsk_dgroup
  520.     les    bx,es:tsk_global
  521.         les     bx,es:current_task[bx]
  522.         cli
  523.         mov     es:q_el.q_prior[bx],0
  524.         pop     es
  525.     jmp    tsk_scheduler
  526. ;
  527. yield    endp
  528. ;
  529. ;
  530. ;    void far c_schedule (void)
  531. ;
  532. ;    Entry for conditionally calling the scheduler. Rearranges 
  533. ;    the stack to contain flags, then jumps to _sched_int.
  534. ;    NOTE: Uses ax,bx.
  535. ;
  536. c_schedule    proc    far
  537. ;
  538.     pop    ax
  539.     pop    bx
  540.     pushf
  541.     push    bx
  542.     push    ax
  543.     cli
  544.     jmp    sched_int
  545. ;
  546. c_schedule    endp
  547. ;
  548. ;--------------------------------------------------------------------------
  549. ;
  550. ;    word tsk_dseg (void)
  551. ;
  552. ;    Returns current contents of DS register.
  553. ;
  554. tsk_dseg    proc    near
  555.     mov    ax,ds
  556.     ret
  557. tsk_dseg    endp
  558. ;
  559. ;
  560. ;    word tsk_flags (void)
  561. ;
  562. ;    Returns current contents of Flag register.
  563. ;
  564. tsk_flags    proc    near
  565.     pushf
  566.     pop    ax
  567.     ret
  568. tsk_flags    endp
  569. ;
  570. ;
  571. ;    int tsk_dis_int (void)
  572. ;
  573. ;    Returns current state of the interrupt flag (1 if ints were 
  574. ;    enabled), then disables interrupts.
  575. ;
  576. tsk_dis_int    proc
  577. ;
  578.     pushf
  579.     pop    ax
  580.     mov    al,ah
  581.     shr    al,1
  582.     and    ax,1
  583.     cli
  584.     ret
  585. ;
  586. tsk_dis_int    endp
  587. ;
  588. ;
  589. ;    void far tsk_ena_int (int state)
  590. ;
  591. ;    Enables interrupts if 'state' is nonzero.
  592. ;
  593. tsk_ena_int    proc    istate: word
  594. ;
  595.     cmp    istate,0
  596.     je    teiend
  597.     sti
  598. teiend:
  599.     ret
  600. ;
  601. tsk_ena_int    endp
  602. ;
  603. ;
  604. ;    tsk_cli/tsk_sti: disable/enable int
  605. ;    NOTE: These routines are normally replaced by intrinsics.
  606. ;
  607. tsk_cli    proc
  608.     cli
  609.     ret
  610. tsk_cli    endp
  611. ;
  612. ;
  613. tsk_sti    proc
  614.     sti
  615.     ret
  616. tsk_sti    endp
  617. ;
  618. ;
  619. ;    tsk_inp/tsk_outp: input/output from/to port
  620. ;    NOTE: These routines are normally replaced by intrinsics,
  621. ;          except for Turbo C tsk_inpw.
  622. ;
  623. tsk_inp    proc    port: word
  624. ;
  625.     mov    dx,port
  626.     in    al,dx
  627.     xor    ah,ah
  628.     ret
  629. ;
  630. tsk_inp    endp
  631. ;
  632. ;
  633. tsk_inpw    proc    port: word
  634. ;
  635.     mov    dx,port
  636.     in    ax,dx
  637.     ret
  638. ;
  639. tsk_inpw    endp
  640. ;
  641. ;
  642. tsk_outp    proc    port: word, val: word
  643. ;
  644.     mov    dx,port
  645.     mov    al,byte ptr(val)
  646.     out    dx,al
  647.     ret
  648. ;
  649. tsk_outp    endp
  650. ;
  651. ;
  652. ;    void tsk_nop (void)
  653. ;
  654. ;    Do nothing. Used for very short delays.
  655. ;
  656. tsk_nop    proc
  657. ;
  658.     jmp    short tnop1
  659. tnop1:
  660.     jmp    short tnop2
  661. tnop2:
  662.     ret
  663. ;
  664. tsk_nop    endp
  665. ;
  666.     .tsk_ecode
  667.     end
  668.  
  669.  
  670.