home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD1.mdf / magazine / drdobbs / 1991 / 06 / mulitask.asc < prev    next >
Text File  |  1991-05-02  |  7KB  |  200 lines

  1. _A MEDIUMWEIGHT-HEAVYWEIGHT FORTH MULTITASKER_
  2. by Jack Woehr
  3.  
  4.  
  5. [LISTING ONE]
  6.  
  7. \ prempt96.f ... a "medium-weight" pre-emptive multitasker for the
  8. \ Vesta SBC196 running Forth-83i96.
  9. \ Copyright *C* 1991 jack j. woehr
  10. \ jax@well.UUCP JAX on GEnie
  11. \ SYSOP RealTime Control & Forth Board (303) 278-0364 3/12/24 24 hrs.
  12.  
  13. \ *** Data Objects
  14.  
  15. \ Register Aliases
  16.  
  17. $ 00 constant zero  \ Symbolic Name for the Zero Register
  18. $ 20 constant up    \ Forth83i96 User Pointer
  19. $ 26 constant entry-reg \ Forth83i96 Multitasker assumes ENTRY in this reg.
  20.  
  21. \ Register Variables
  22.  
  23. $ 90 constant clicks    \ clicks left to execute on current task
  24. $ 92 constant last-up   \ task that was executing last time interrupt fired
  25. $ 94 constant period    \ how often the preemptive multitasker should fire
  26. $ 96 constant reg-temp  \ a temporary register, used as a pointer and a holder
  27. $ 98 constant save-stat \ PSW+INT_MASK+WSR+INT_MASK1 .. double length
  28. $ 9C constant ax    \ Symbolic Name for a Scratch Register
  29.  
  30. \ Special Function Registers (SFRs) for Hardware Control
  31. \ See _80C196KB USER'S GUIDE_, Intel 1990.
  32.  
  33. $ 08 constant int_mask  \ Int Mask containing Timer1 Int
  34. $ 09 constant int_pend  \ Interrupt Pending register for Timer1 Overflow
  35. $ 0A constant timer1    \ base address of Timer1
  36. $ 14 constant wsr   \ Window Status Register, controls register windowing
  37. $ 16 constant ioc1  \ Input/Output Control1 governs Overflow Int Enable
  38.  
  39. \ Bit Masks for Hardware Control
  40.  
  41. $ 04 constant enable    \ IOC1.2, Timer1 Overflow Interrupt Enable
  42. $ 01 constant ov-int    \ INT_MASK.0 Timer1 Overflow
  43.  
  44. \ Interrupt Handle
  45. \ Forth-83i96 ROM vectors ints thru regs
  46.  
  47. $ 48 constant timerov-handle    \ Vector for Interrupt 00
  48.  
  49. \ Declare a USER VARIABLE of which all tasks will possess an instance.
  50. \ The local instance is the task's priority.
  51.  
  52. user variable my-pri
  53. forth
  54.  
  55. \ Value 0 - 255 (since DJNZ instruction is used ... substitute DJNZW
  56. \ on the 80C196KC part in the routine ?PREEMPT for greater range of
  57. \ possible priority values).
  58. \ 1 ... Task will execute one click maximum
  59. \ 0 ... Task will execute 256 times
  60.  
  61. \ *** Preemptor Routines
  62.  
  63. \ Here is the return from pre-emption:  
  64.  
  65. label reclaim   \ entry-reg == ENTRY
  66.     up entry-reg ' ENTRY >body @ # sub3 \ load user pointer
  67.     sp ' TOP >body @ up [+s] ld     \ get task's stack
  68.     ' ENTRY >body @ up [+s] pop     \ get previous restart routine
  69.     reg-temp $ 1A # ld          \ first register to restore
  70.     ax $ 34 $ 1A - 2/ # ld          \ number of registers to restore
  71.     dp@                 \ (resolve addr for DJNZ)
  72.         reg-temp []+ pop        \ restore reg and postinc ptr
  73.     ax djnz                 \ loop 'til done
  74.     popa    \ restore flags from when task was interrupted preemptively
  75.     ret c;  \ return address last thing waiting on stack
  76.  
  77. \ Here is the preemption:
  78.  
  79. label preempt   \ User Pointer still points to current task
  80.         \ Ret addr & Proc Flags already on stack
  81.     reg-temp $ 32 # ld      \ first register pair to preserve
  82.     ax $ 34 $ 1A - 2/ # ld      \ number of register pairs to preserve
  83.     dp@
  84.         reg-temp [] push    \ save contents of a reg
  85.         reg-temp 2 # sub2   \ "manual post decrement" mode!
  86.     ax djnz
  87.     ' ENTRY >body @ up [+s] push    \ save the restart routine
  88.     sp ' TOP >body @ up [+s] st \ save address of TOP of stack
  89.     reg-temp reclaim # ld       \ address of preempted task restarter
  90.     reg-temp ' ENTRY >body @ up [+s] st \ install reclaim routine ...
  91.     reg-temp ' LINK >body @ up [+s] ld  \ == ENTRY of next task
  92.     ax reg-temp [] ld       \ @ENTRY == restart routine of next task
  93.     ax reclaim # cmp        \ is new task's restart routine RECLAIM?
  94.     0<> if          \ no, so we must restore intmask & psw "by hand"
  95.         save-stat push
  96.         save-stat 2+ push
  97.         popa
  98.     then
  99.     entry-reg reg-temp ld
  100.     ax br c;        \ start next task!
  101.  
  102. \ A label to DJNZ to while we wait for task slice to expire.
  103.  
  104. label one-more-goround popa ret c;
  105.  
  106. \ Timer Interrupt
  107.  
  108. label ?preempt
  109.     pusha               \ save processor flags
  110.     wsr $ 0f # ldb          \ switch Register Window to write timer
  111.     timer1 period ld        \ set up next timer int
  112.     wsr clrb            \ switch back
  113.     up last-up cmp          \ executing same task as at last int?
  114.     0<> if              \ no
  115.         up last-up st       \ mark new task
  116.         clicks ' my-pri >body @ up [+s] ld  \ get "priority"
  117.         popa            \ restore processor flags
  118.         ret         \ return from interrupt
  119.     then                \ same task, decrement clicks
  120.     one-more-goround clicks djnz    \ return if time not yet expired
  121.     preempt sjmp c;         \ and if zero fall-thru, preempt
  122.  
  123. \ *** Hardware Setup
  124.  
  125. \ Install Interrupt Handler
  126.  
  127. : install-timer-int ( ---)  ?preempt timerov-handle ! ;
  128.  
  129.  
  130. \ Control Counter and Interrupt Masks
  131.  
  132. code setup ( --)
  133.     last-up clr         \ so that int will do setup first time
  134.     pusha               \ save setup while changing wsr
  135.     wsr $ 0F # ldb          \ change WSR to read IOC1, write timer
  136.     timer1 period ld        \ write timer period to Timer1
  137.     ax ioc1 ldb         \ get current IOC1 mask
  138.     ax enable # orb         \ mask in Timer1 Overflow Int Enable
  139.     popa                \ restore
  140.     ioc1 ax ldb         \ store resultant mask to IOC1
  141.     zero int_pend ldb       \ clear pending interrupts
  142.     int_mask ov-int # orb       \ set Timer Overflow Int
  143.     pusha               \ get int mask and psw
  144.     save-stat 2+ sp [] ld       \ save "normal" processor status
  145.     save-stat 2 sp [+s] ld      \ second word of same
  146.     popa                \ restore
  147.     tonext c;
  148.  
  149.  
  150. \ Timer1 counts up and interrupts on overflow ( FFFF/0000 boundary)
  151.  
  152. : set-period ( CPU-state-times-desired/8 --) negate period ! ;
  153.  
  154. \ How many timer ints go by before a task is forcibly pre-empted?
  155.  
  156. : set-pri ( clicks-before-preemption task --) my-pri local ! ;
  157.  
  158. \ *** Sample Tasks That Don't Behave Themselves
  159.  
  160. \ A "naughty" task that doesn't PAUSE very often!
  161.  
  162. variable zotz
  163. background: sample-task0 ( --)
  164.     begin $ 1 zotz +! zotz @ 0= if pause then again ;
  165.  
  166. \ A "wicked" task that doesn't PAUSE at all!
  167.  
  168. variable foof
  169. background: sample-task1 ( --) begin 1 foof +! again ;
  170.  
  171. \ Typical usage: HEX 100 100 TEST-SAMPLE-TASKS
  172.  
  173. : test-sample-tasks ( clicks period --)
  174.     set-period
  175.     dup
  176.     foreground set-pri  \ set priority of foreground task
  177.     dup         \ "naughty" task gets same CPU as FOREGROUND
  178.     sample-task0 set-pri    \ set priority of "naughty" task
  179.     4 /         \ we'll give "wicked" task less CPU
  180.     sample-task1 set-pri    \ set priority of "wicked" task
  181.     install-timer-int   \ install handler in vector handle
  182.     setup           \ turn on interrupt
  183.     sample-task0 wake   \ enable tasks to do other than "pass"
  184.     sample-task1 wake
  185.     ( multi)
  186.     \ MULTI optional, since FOREGROUND task will be preempted anyway
  187.     \ If MULTI not set, PAUSE won't work and "naughty" and "wicked"
  188.     \ task become equivalent (except for priority).
  189. ;
  190.  
  191. \ Try this after you have executed TEST-SAMPLE-TASKS
  192.  
  193. : watch-sample-tasks ( --)
  194.     zotz off foof off
  195.     begin zotz @ u. foof @ u. key? until
  196.     key drop ;
  197.  
  198. \ *** End of PREEMPT96.F
  199.  
  200.