title TASK - Multiple Tasks for MicroSoft C ; LARGE Model Only - November 1990, Steve Geller ; mods Sept 1992 - samples stack range, uses PUSHA/POPA .286 .MODEL LARGE ; How to use: ; 1. Initialize by calling deftask(proc,stack,stacksize); ; for each task. "proc" is the procedure which gets ; control when the task starts (same proc may be used ; for more than one task). ; 2. Begin scheduling by calling task(). deftask() may ; be called again to add tasks after scheduling has begun. ; 3. Each task, as part of its execution, should ; call task() at points where it wishes to delay ; or to break up a long computation. ; It is possible to test programs with imbedded TASK calls ; without actually doing any scheduling. If no calls ; to DEFTASK are ever made, calls to TASK do nothing. ; Layout of a Task Control Block (TCB) TCB STRUC TLINK DW ? ; address of next TCB TSP DW ? ; Stack Pointer TSS DW ? ; Stack Segment TSPMIN DW ? ; lowest SP sampled TSPSTART DW ? ; starting SP TCB ENDS COMM NEAR STKHQQ:WORD .DATA MULTI DB 0 ; switch: becomes 1 when more than one task exists ; Pointer to TCB of current Task CTASK DW ? ; Pointer to last-allocated TCB LAST_TCB DW ? CALLER_DS DW ? ; TCB for Initial Task (main) MAINTCB DB SIZE TCB DUP(?) ; TCB for "main program" ; Allocate TCB's of other tasks DB SIZE TCB DUP(?) DB SIZE TCB DUP(?) DB SIZE TCB DUP(?) DB SIZE TCB DUP(?) DB SIZE TCB DUP(?) DB SIZE TCB DUP(?) DB SIZE TCB DUP(?) DB SIZE TCB DUP(?) DB SIZE TCB DUP(?) DB SIZE TCB DUP(?) DB SIZE TCB DUP(?) DB SIZE TCB DUP(?) .CODE ; DEFTASK - procedure for defining a new task ; void deftask(proc,stackarea,stacksize) ; void proc(); ; short far *stackarea; ; int stacksize; /* number of words */ ; **Note-- this call returns without changing which ; task is running. During initialization, the main program ; will call DEFTASK to define all the desired tasks. ; The first time it calls TASK, scheduling will commence. FRAME STRUC DW 3 DUP(?) PROCP DW ? DW ? STAKP DW ? DW ? STSIZ DW ? FRAME ENDS PUBLIC _DEFTASK _DEFTASK PROC FAR PUSH BP MOV BP,SP PUSH BX PUSH SI PUSH DI PUSH DS ; address local data segment MOV SI,DS MOV AX,@DATA MOV DS,AX MOV CALLER_DS,SI ; remember caller's original DS ; is it first call ? CMP MULTI,0 JNE L1 ; no ; Defeat C's stack checker push ds mov ax,seg stkhqq mov ds,ax lea bx,stkhqq mov word ptr [bx],0 pop ds ; First call - set up TCB for "main program" LEA BX,MAINTCB MOV TLINK[BX],BX ; link to itself MOV CTASK,BX ; define current TCB pointer MOV LAST_TCB,BX ; define last TCB allocated MOV MULTI,1 ; scheduling may commence MOV AX,SS MOV TSS[BX],AX ; stack segment ; set stack excursion statistics for Main task MOV TSPSTART[BX],SP MOV TSPMIN[BX],SP ; Not 1st call -- Chain new TCB to TCB last defined. L1: MOV BX,CTASK ; current TCB MOV AX,TLINK[BX] ; original link(Current) MOV DI,LAST_TCB ADD DI,SIZE TCB ; new TCB MOV LAST_TCB,DI MOV TLINK[BX],DI ; link(Current) <-- new TCB MOV TLINK[DI],AX ; link(New TCB) <-- original link(Current) MOV DX,STAKP+2[BP] ; segment of new stack MOV TSS[DI],DX ; Set up new stack so that calling TASK will give ; control to the designated procedure. MOV BX,STAKP[BP] ; offset of new stack ADD BX,STSIZ[BP] ; add number of words ADD BX,STSIZ[BP] ; point to highest addr + 1 word ; save current stack pointer MOV CX,SS ; stack segment MOV AX,SP ; stack pointer ; get pointer to task procedure MOV SI,PROCP+2[BP] ; segment MOV DI,PROCP[BP] ; offset ; Switch to the new Stack MOV DX,STAKP+2[BP] ; segment of new stack MOV SP,BX MOV SS,DX ; push the return-address (designated procedure) PUSH SI ; Segment PUSH DI ; Offset ; push a set of registers MOV DX,CALLER_DS PUSH DX PUSHA PUSHF PUSH ES ; set the resulting SP in new TCB MOV DI,LAST_TCB MOV TSP[DI],SP ; set stack excursion statistics MOV TSPSTART[DI],BX MOV TSPMIN[DI],BX ; restore original stack MOV SP,AX MOV SS,CX ; Return normally. POP DS POP DI POP SI POP BX POP BP RET _DEFTASK ENDP ; Call TASK to give up control to another task ; void task() PUBLIC _TASK _TASK PROC FAR PUSH DS ; local data PUSH AX MOV AX,@DATA MOV DS,AX POP AX ; If no current Task defined, do nothing CMP MULTI,0 ; more than one Task defined JNE SCHED ; yes, do scheduling POP DS RET ; no, do nothing ; Save all Registers SCHED: PUSHA PUSHF PUSH ES MOV BX,CTASK ; addr TCB of current Task MOV TSP[BX],SP ; save Stack Pointer MOV AX,SS ; save stack segment MOV TSS[BX],AX MOV BX,TLINK[BX] ; chain to next Task CLI ; no interrupts for a moment ... MOV CTASK,BX ; make this the new Current Task MOV SP,TSP[BX] ; set Stack Pointer for new Task MOV AX,TSS[BX] ; new stack segment MOV SS,AX STI ; re-enable interrupts ; Restore registers (of new Task) POP ES POPF POPA POP DS RET ; return to new Task _TASK ENDP ; (to be called from an Interrupt Service Routine) ; Sample stack usage ; void task_sp() PUBLIC _TASK_SP _TASK_SP PROC FAR PUSH DS ; local data PUSH AX MOV AX,@DATA MOV DS,AX POP AX ; If no current Task defined, do nothing CMP MULTI,0 ; more than one Task defined JNE DOIT ; yes, do something POP DS RET ; no, do nothing DOIT: PUSH BX push ax MOV BX,CTASK ; addr TCB of current Task ; is this the right Stack Segment for the task? MOV AX,SS CMP AX,TSS[BX] JNE ONWARD ; not same stack, ignore ; identify minimum SP encountered CMP SP,TSPMIN[BX] JAE ONWARD MOV TSPMIN[BX],SP ONWARD: pop ax POP BX POP DS RET _TASK_SP ENDP ; report max SP excursion for this task ; short task_x() PUBLIC _TASK_X _TASK_X PROC FAR PUSH DS ; local data MOV AX,@DATA MOV DS,AX ; If no current Task defined, do nothing mov ax,2222 CMP MULTI,0 ; more than one Task defined JNE DOITX ; yes, do something POP DS RET ; no, do nothing DOITX: PUSH BX MOV BX,CTASK ; addr TCB of current Task ; report max stack excursion MOV AX,TSPSTART[BX] SUB AX,TSPMIN[BX] SHR AX,1 ; result in words POP BX POP DS RET _TASK_X ENDP END