Category : Files from Magazines
Archive   : DDJ0691.ZIP
Filename : MULITASK.ASC

 
Output of file : MULITASK.ASC contained in archive : DDJ0691.ZIP

_A MEDIUMWEIGHT-HEAVYWEIGHT FORTH MULTITASKER_
by Jack Woehr


[LISTING ONE]

\ prempt96.f ... a "medium-weight" pre-emptive multitasker for the
\ Vesta SBC196 running Forth-83i96.
\ Copyright *C* 1991 jack j. woehr
\ [email protected] JAX on GEnie
\ SYSOP RealTime Control & Forth Board (303) 278-0364 3/12/24 24 hrs.

\ *** Data Objects

\ Register Aliases

$ 00 constant zero \ Symbolic Name for the Zero Register
$ 20 constant up \ Forth83i96 User Pointer
$ 26 constant entry-reg \ Forth83i96 Multitasker assumes ENTRY in this reg.

\ Register Variables

$ 90 constant clicks \ clicks left to execute on current task
$ 92 constant last-up \ task that was executing last time interrupt fired
$ 94 constant period \ how often the preemptive multitasker should fire
$ 96 constant reg-temp \ a temporary register, used as a pointer and a holder
$ 98 constant save-stat \ PSW+INT_MASK+WSR+INT_MASK1 .. double length
$ 9C constant ax \ Symbolic Name for a Scratch Register

\ Special Function Registers (SFRs) for Hardware Control
\ See _80C196KB USER'S GUIDE_, Intel 1990.

$ 08 constant int_mask \ Int Mask containing Timer1 Int
$ 09 constant int_pend \ Interrupt Pending register for Timer1 Overflow
$ 0A constant timer1 \ base address of Timer1
$ 14 constant wsr \ Window Status Register, controls register windowing
$ 16 constant ioc1 \ Input/Output Control1 governs Overflow Int Enable

\ Bit Masks for Hardware Control

$ 04 constant enable \ IOC1.2, Timer1 Overflow Interrupt Enable
$ 01 constant ov-int \ INT_MASK.0 Timer1 Overflow

\ Interrupt Handle
\ Forth-83i96 ROM vectors ints thru regs

$ 48 constant timerov-handle \ Vector for Interrupt 00

\ Declare a USER VARIABLE of which all tasks will possess an instance.
\ The local instance is the task's priority.

user variable my-pri
forth

\ Value 0 - 255 (since DJNZ instruction is used ... substitute DJNZW
\ on the 80C196KC part in the routine ?PREEMPT for greater range of
\ possible priority values).
\ 1 ... Task will execute one click maximum
\ 0 ... Task will execute 256 times

\ *** Preemptor Routines

\ Here is the return from pre-emption:

label reclaim \ entry-reg == ENTRY
up entry-reg ' ENTRY >body @ # sub3 \ load user pointer
sp ' TOP >body @ up [+s] ld \ get task's stack
' ENTRY >body @ up [+s] pop \ get previous restart routine
reg-temp $ 1A # ld \ first register to restore
ax $ 34 $ 1A - 2/ # ld \ number of registers to restore
[email protected] \ (resolve addr for DJNZ)
reg-temp []+ pop \ restore reg and postinc ptr
ax djnz \ loop 'til done
popa \ restore flags from when task was interrupted preemptively
ret c; \ return address last thing waiting on stack

\ Here is the preemption:

label preempt \ User Pointer still points to current task
\ Ret addr & Proc Flags already on stack
reg-temp $ 32 # ld \ first register pair to preserve
ax $ 34 $ 1A - 2/ # ld \ number of register pairs to preserve
[email protected]
reg-temp [] push \ save contents of a reg
reg-temp 2 # sub2 \ "manual post decrement" mode!
ax djnz
' ENTRY >body @ up [+s] push \ save the restart routine
sp ' TOP >body @ up [+s] st \ save address of TOP of stack
reg-temp reclaim # ld \ address of preempted task restarter
reg-temp ' ENTRY >body @ up [+s] st \ install reclaim routine ...
reg-temp ' LINK >body @ up [+s] ld \ == ENTRY of next task
ax reg-temp [] ld \ @ENTRY == restart routine of next task
ax reclaim # cmp \ is new task's restart routine RECLAIM?
0<> if \ no, so we must restore intmask & psw "by hand"
save-stat push
save-stat 2+ push
popa
then
entry-reg reg-temp ld
ax br c; \ start next task!

\ A label to DJNZ to while we wait for task slice to expire.

label one-more-goround popa ret c;

\ Timer Interrupt

label ?preempt
pusha \ save processor flags
wsr $ 0f # ldb \ switch Register Window to write timer
timer1 period ld \ set up next timer int
wsr clrb \ switch back
up last-up cmp \ executing same task as at last int?
0<> if \ no
up last-up st \ mark new task
clicks ' my-pri >body @ up [+s] ld \ get "priority"
popa \ restore processor flags
ret \ return from interrupt
then \ same task, decrement clicks
one-more-goround clicks djnz \ return if time not yet expired
preempt sjmp c; \ and if zero fall-thru, preempt

\ *** Hardware Setup

\ Install Interrupt Handler

: install-timer-int ( ---) ?preempt timerov-handle ! ;


\ Control Counter and Interrupt Masks

code setup ( --)
last-up clr \ so that int will do setup first time
pusha \ save setup while changing wsr
wsr $ 0F # ldb \ change WSR to read IOC1, write timer
timer1 period ld \ write timer period to Timer1
ax ioc1 ldb \ get current IOC1 mask
ax enable # orb \ mask in Timer1 Overflow Int Enable
popa \ restore
ioc1 ax ldb \ store resultant mask to IOC1
zero int_pend ldb \ clear pending interrupts
int_mask ov-int # orb \ set Timer Overflow Int
pusha \ get int mask and psw
save-stat 2+ sp [] ld \ save "normal" processor status
save-stat 2 sp [+s] ld \ second word of same
popa \ restore
tonext c;


\ Timer1 counts up and interrupts on overflow ( FFFF/0000 boundary)

: set-period ( CPU-state-times-desired/8 --) negate period ! ;

\ How many timer ints go by before a task is forcibly pre-empted?

: set-pri ( clicks-before-preemption task --) my-pri local ! ;

\ *** Sample Tasks That Don't Behave Themselves

\ A "naughty" task that doesn't PAUSE very often!

variable zotz
background: sample-task0 ( --)
begin $ 1 zotz +! zotz @ 0= if pause then again ;

\ A "wicked" task that doesn't PAUSE at all!

variable foof
background: sample-task1 ( --) begin 1 foof +! again ;

\ Typical usage: HEX 100 100 TEST-SAMPLE-TASKS

: test-sample-tasks ( clicks period --)
set-period
dup
foreground set-pri \ set priority of foreground task
dup \ "naughty" task gets same CPU as FOREGROUND
sample-task0 set-pri \ set priority of "naughty" task
4 / \ we'll give "wicked" task less CPU
sample-task1 set-pri \ set priority of "wicked" task
install-timer-int \ install handler in vector handle
setup \ turn on interrupt
sample-task0 wake \ enable tasks to do other than "pass"
sample-task1 wake
( multi)
\ MULTI optional, since FOREGROUND task will be preempted anyway
\ If MULTI not set, PAUSE won't work and "naughty" and "wicked"
\ task become equivalent (except for priority).
;

\ Try this after you have executed TEST-SAMPLE-TASKS

: watch-sample-tasks ( --)
zotz off foof off
begin zotz @ u. foof @ u. key? until
key drop ;

\ *** End of PREEMPT96.F