home *** CD-ROM | disk | FTP | other *** search
- (******************************************************
-
- CONCUR.PAS : CONCURRENT PROGRAMMING EXECUTIVE
-
- AUTHOR : J.F.J. PASSANT
- VERSION : 1.00 {Use with turbo.com, version 3.01A}
- DATE : 3-DEC-86
-
- UPDATE :
- MODIFICATION :
-
- EXTERNALS : biosdec.pas, libdec.pas
-
- USE : 1. taskinit (stacksize);
- 2. installtask; {for each task}
- 3. .....
-
- ENTRIES : - taskinit (stacksize);
- - installtask (ofs (procedure), stack, taskno);
- - switchtask;
- - clearscreen;
- - window (x1, y1, x2, y2);
- - border (taskname);
-
- ******************************************************)
-
-
- {Turbo pascal library variables : }
- var libdata : array [0..filescratch] of byte absolute dseg:0;
-
-
- const maxtask = 16; {Maximal number of tasks + 1}
- framesize = 14; {Initial task stack frame size}
-
- type stack_area = array [0..framesize] of integer;
- stackpointer = ^stack_area;
- address = array [1..2] of integer;
- astring = string [79];
-
- const datasegment : integer = 0; {Turbo DS}
-
-
- var currenttask : integer; {Currently active task}
- sptable : array [0..maxtask] of address; {Task SP's}
- activetasks : array [0..maxtask] of byte; {Install flags}
- taskarea : stackpointer; {Start of task stack area}
- taskareasize : integer; {Size of task stack area}
-
-
-
- (*************************** TASK SWITCHING ************************)
-
-
- procedure taskerror (msg : astring; val : integer);
- {Prints a "fatal error"-message and aborts the program}
- begin
- currenttask := 0; {Use window of main program}
- write (msg);
- if val <> - 1 then write (val:1);
- writeln;
- write ('------ Program Aborted ------');
- halt (1);
- end;
-
-
-
- procedure stackoverflow;
- {Called by <switchtask> when a task stack runs over its boundaries.
- Aborts the program}
- begin
- {$K-}
- taskerror ('Stack overflow in task ', currenttask);
- {$K+}
- end;
-
-
- procedure switchtask;
- {The task switcher}
- {Note : to run this program on 8088/86 systems, replace the PUSHA and POPA
- instructions with the following inline code :
- PUSHA ($60) : inline ($50/$51/$52/$53/$54/$55/$56/$57);
- POPA ($61) : inline ($5F/$5E/$5D/$5B/$5B/$5A/$59/$58);
- ($5B appearing twice is NOT a typo !!!)}
-
- begin
- Inline(
- $5D { POP BP ; UNDO TURBO CODE}
- /$5D { POP BP ; RESTORE BP}
- /$9C { PUSHF ; SAVE REGISTERS}
- /$1E { PUSH DS}
- { ; Replace the next instruction for 8088}
- /$60 { DB $60 ; 286 PUSH ALL !!!}
- /$06 { PUSH ES}
- /$2E { CS:}
- /$A1/>DATASEGMENT { MOV AX,[>DATASEGMENT] ; GET TURBO DS}
- /$8E/$D8 { MOV DS,AX}
- /$FF/$36/>LIBERROR { PUSH [>liberror]}
- /$81/$FC/$00/$02 { CMP SP,$200 ; STACK OVERFLOW ?}
- /$72/<STACKOVERFLOW+3-*{ JB >STACKOVERFLOW+3-* ; THEN ABORT PROGRAM}
- /$BF/>SPTABLE { MOV DI,>SPTABLE ; DI --> SPTABLE}
- /$8B/$1E/>CURRENTTASK { MOV BX,[>CURRENTTASK] ; BX = CURRENT TASK}
- /$B1/$02 { MOV CL,2 ; 4 BYTES PER ENTRY}
- /$D3/$E3 { SHL BX,CL ; IN TASKSTACK TABLE}
- /$89/$21 { MOV [BX+DI],SP ; SAVE CURRENT STACK PTR}
- /$8C/$51/$02 { MOV [BX+DI+2],SS}
- /$BE/>ACTIVETASKS { MOV SI,>ACTIVETASKS}
- /$D3/$EB { SHR BX,CL ; RESTORE BX}
- {FINDNEXT:}
- /$43 { INC BX ; NEXT TASK NUMBER}
- /$81/$E3/$0F/$00 { AND BX,$0F ; MAX 16 TASKS}
- /$80/$38/$00 { CMP BYTE PTR [BX+SI],0 ; TASK INSTALLED ?}
- /$74/$F6 { JZ FINDNEXT ; NO, KEEP INCR. BX}
- /$89/$1E/>CURRENTTASK { MOV [>CURRENTTASK],BX ; SAVE NEW TASK NUMBER}
- /$D3/$E3 { SHL BX,CL ; 4 BYTES PER ENTRY}
- /$8B/$21 { MOV SP,[BX+DI] ; LOAD STACK PTR NEW TASK}
- /$8E/$51/$02 { MOV SS,[BX+DI+2]}
- /$8F/$06/>LIBERROR { POP [>liberror]}
- /$07 { POP ES ; RESTORE REGISTERS}
- { ; Replace the next instruction for 8088}
- /$61 { DB $61 ; 286 POP ALL !!!}
- /$1F { POP DS}
- /$9D { POPF}
- /$C3 { RET ; ACTIVATE NEXT TASK}
- );
- end;
-
-
-
- procedure taskterminate;
- {Activated when an installed task aborts operation. The program is aborted}
- begin
- taskerror ('Unexpected abortion of task ', currenttask);
- end;
-
-
-
- (************************* TASK INSTALLATION ************************)
-
-
- procedure normalize_stackpointer (var p : stackpointer);
- {Normalizes a pointer, making the offset as small as possible}
- var segm, offs : integer;
- begin
- segm := seg (p^);
- offs := ofs (p^);
- segm := segm + (offs shr 4);
- offs := offs and $000F;
- p := ptr (segm, offs);
- end;
-
-
- procedure allocate_taskarea (size : integer);
- {Allocates <size> paragraphs on the stack area to be used by the stacks
- for concurrent tasks. The Turbo stack is consequently moved down in memory.
- <Size> should be equal or higher than the sum of the sizes of the stack
- needed by each installed task}
-
- begin
- Inline(
- $1E { PUSH DS}
- /$8B/$0E/>INITIALSTACK { MOV CX,[>INITIALSTACK] ; DETERMINE CURRENT}
- /$29/$E1 { SUB CX,SP ; STACK FRAME SIZE}
- /$8C/$D0 { MOV AX,SS ; DS --> STACK SEGMENT}
- /$8E/$D8 { MOV DS,AX}
- /$2B/$46/$04 { SUB AX,[BP+4] ; MOVE SS <SIZE> DOWN}
- /$8E/$C0 { MOV ES,AX ; ES --> NEW SS}
- /$89/$E6 { MOV SI,SP ; PREPARE FRAME COPY}
- /$89/$F7 { MOV DI,SI}
- /$FC { CLD}
- /$F2/$A4 { REP MOVSB ; COPY STACK FRAME}
- /$1F { POP DS}
- /$8C/$C0 { MOV AX,ES ; SAVE PTR TO ALLOCATED}
- /$89/$3E/>TASKAREA { MOV [>TASKAREA],DI ; TASK STACK AREA}
- /$A3/>TASKAREA+2 { MOV [>TASKAREA+2],AX ; FOR USE BY installtask}
- /$8C/$C0 { MOV AX,ES ; SS --> NEW STACK SEG}
- /$8E/$D0 { MOV SS,AX}
- );
- normalize_stackpointer (taskarea);
- taskareasize := size;
- end;
-
-
- procedure installtask (address, stacksize, tasknumber : integer);
- {Installs a task with task number <tasknumber> and start address <address>
- and allocates <stacksize> paragraphs for its stack. The stack is initialized,
- ready for <switchtask> to activate the task}
-
- var segm : integer;
- offs : integer;
- sp : stackpointer;
- i : integer;
-
- begin
- if (tasknumber <= 0) or (tasknumber >= maxtask) then
- taskerror ('Illegal task number ', tasknumber);
- if taskareasize - stacksize < 0 then
- taskerror ('Not enough stack to install task ', tasknumber);
- if activetasks [tasknumber] = 1 then
- taskerror ('Double installation of task ', tasknumber);
- taskareasize := taskareasize - stacksize;
-
- sp := taskarea; {SP = bottom of stack area}
- normalize_stackpointer (sp);
- {Reduce available taskarea}
- taskarea := ptr (seg (taskarea^), ofs (taskarea^) + stacksize shl 4);
- offs := ofs (sp^);
- segm := seg (sp^);
-
- {Move SP to end of stack area, adjust for registers (framesize)}
- offs := offs + $200 + stacksize shl 4 - 2*framesize;
- segm := segm - $20; {Adjust for $200 offset}
- sp := ptr (segm, offs);
-
- sptable [tasknumber, 1] := offs; {Write SP in stack table}
- sptable [tasknumber, 2] := segm;
-
- {Initialize registers for the task in the stack frame}
- sp ^[0] := libdata [liberror] + 256*libdata [liberror+1];
- for i := 1 to 9 do sp^ [i] := 0; {General registers}
- i := 9;
- sp^ [i+1] := Dseg; {DS}
- sp^ [i+2] := $0200; {Flags, interrupts enabled}
- sp^ [i+3] := address; {IP}
- sp^ [i+4] := ofs (TaskTerminate); {Task terminate handler}
- activetasks [tasknumber] := 1; {Task enabled}
- end;
-
-
- (************************* SCREEN OUTPUT DRIVER **********************)
-
- {This section replaces the Turbo screen output routines. Multi tasking
- windows are supported. Each task may claim an area of the screen for its
- indivial screen output. All screen related Turbo standard identifiers are
- replaced. Initially, each task has control over the full screen.
- <Window> should be called by each task to avoid overlapping screen output}
-
-
- type windowrec = record
- x1, y1, x2, y2 : byte; {Window position}
- attr : byte; {Attributes}
- border : boolean; {Border shown flag}
- row, col : byte; {Cursor position}
- end;
-
-
- var windows : array [0..maxtask] of windowrec;
-
-
-
- {The following routines replace their equivalent in the Turbo library}
-
-
- procedure lowvideo;
- begin
- windows [currenttask].attr := libdata [lowattr];
- end;
-
- procedure normvideo;
- begin
- windows [currenttask].attr := libdata [normattr];
- end;
-
- procedure textcolor (c : integer);
- begin
- c := c and 31;
- if c and 16 <> 0 then c := (c and 15) or 128;
- with windows [currenttask] do attr := (attr and 112) or c;
- end;
-
- procedure textbackground (c : integer);
- begin
- c := (c and 7) shl 4;
- with windows [currenttask] do attr := (attr and $8f) or c;
- end;
-
- function wherex : integer;
- begin
- wherex := windows [currenttask].col - windows [currenttask].x1 + 1;
- end;
-
- function wherey : integer;
- begin
- wherey := windows [currenttask].row - windows [currenttask].y1 + 1;
- end;
-
- procedure gotoxy (x, y : integer);
- begin
- with windows [currenttask] do
- begin
- x := x + x1 - 1;
- y := y + y1 - 1;
- if (x >= 0) and (y >= 0) and (x < x2) and (y < y2) then
- begin
- row := y; col := x;
- end;
- end;
- end;
-
- procedure scroll (lines : integer; x1, y1, x2, y2, attr : byte);
- var regs : registers;
- begin
- if lines < 0 then regs.ah := 7 else regs.ah := 6;
- regs.al := abs (lines);
- regs.bh := attr;
- regs.ch := y1; regs.cl := x1;
- regs.dh := y2; regs.dl := x2;
- intr ($10, regs);
- end;
-
- procedure clrscr;
- begin
- with windows [currenttask] do scroll (0, x1, y1, x2-1, y2-1, attr);
- end;
-
- procedure clreol;
- begin
- with windows [currenttask] do scroll (0, col, row, x2-1, row, attr);
- end;
-
- procedure clearscreen;
- {Enables clearing the entire screen}
- begin
- scroll (0, 0, 0, nrofcols-1, nrofrows-1, libdata [normattr]);
- end;
-
-
- procedure insline;
- begin
- with windows [currenttask] do scroll (-1, x1, row, x2-1, y2-1, attr);
- end;
-
- procedure delline;
- begin
- with windows [currenttask] do scroll (1, x1, row, x2-1, y2-1, attr);
- end;
-
- procedure window (x3, y3, x4, y4 : integer);
- {Sets up a window for the current task}
- begin
- with windows [currenttask] do
- begin
- if y4 <= nrofrows then y2 := y4;
- if x4 <= nrofcols then x2 := x4;
- if y3 <= y2 then y1 := y3-1;
- if x3 <= x2 then x1 := x3-1;
- row := y1; col := x1;
- end;
- end;
-
-
- (**************************** CONOUTPTR SCREEN DRIVER *******************)
-
-
- procedure conout (ch : char);
- {Replacement CON: output driver}
- var regs : registers;
-
- begin
- with windows [currenttask] do
- begin
- case ch of
- #13 : col := x1; {Carriage return}
- #10 : begin {Line feed}
- row := row+1;
- if row >= y2 then
- begin
- row := row-1;
- scroll (1, x1, y1, x2-1, y2-1, attr);
- end;
- end;
- #8 : if col > x1 then col := col-1; {Back space}
- #7 : ; {Bell}
- else
- begin
- regs.ah := 2; regs.bh := 0; {Set cursor}
- regs.dl := col; regs.dh := row;
- intr ($10, regs);
- regs.ah := 9; regs.al := ord (ch); {Write character}
- regs.bh := 0; regs.bl := attr;
- regs.cx := 1;
- intr ($10, regs);
- col := col + 1; {Update cursor position}
- if col >= x2 then
- begin
- col := x1;
- row := row+1;
- if row >= y2 then
- begin
- row := row-1;
- scroll (1, x1, y1, x2-1, y2-1, attr);
- end;
- end;
- end;
- end;
- regs.ah := 2; regs.bh := 0; {Set cursor}
- regs.dl := col; regs.dh := row;
- intr ($10, regs);
- end;
- end;
-
-
- (************************ CONINPTR INPUT DRIVER *********************)
-
- var inputbusy : boolean; {Keyboard sharing flag}
-
- function conin : char;
- {Calls switchtask if no input character is available}
- var regs : registers;
- ch : char;
-
- begin
- if not keypressed then {Wait for character avail}
- begin
- Inline(
- $FF/$36/>RETADDR { PUSH [>retaddr] ; SAVE LIBRARY DATA}
- /$FF/$36/>FILEPTR { PUSH [>fileptr]}
- /$FF/$36/>FILEPTR+2 { PUSH [>fileptr+2]}
- );
- repeat
- switchtask;
- until keypressed;
- Inline(
- $8F/$06/>FILEPTR+2 { POP [>fileptr+2] ; RELOAD LIB DATA}
- /$8F/$06/>FILEPTR { POP [>fileptr]}
- /$8F/$06/>RETADDR { POP [>retaddr]}
- );
- end;
- ch := chr (libdata [secondchar]); {Any escape sequence char pending?}
- libdata [secondchar] := 0;
- if ch = #0 then {No char pending, read keyboard}
- begin
- regs.ah := 0; {Read kbd}
- intr ($16, regs);
- if regs.al = 0 then {Special key ?}
- begin {Then translate into escape sequence}
- libdata [secondchar] := regs.ah;
- regs.al := 27;
- if regs.ah = 0 then regs.al := 3;
- end;
- if (libdata [ctrlcflag] = 1) and (regs.al = 3) then {^C ?}
- begin
- inline ($E9/CTRLCJUMP-*-2); {Abort program}
- end;
- ch := chr (regs.al);
- end;
- conin := ch;
- end;
-
-
- procedure claiminput;
- {Claims input}
- begin
- while inputbusy do switchtask; {Wait until task releases input}
- inputbusy := true;
- end;
-
-
- procedure releaseinput;
- {Releases input for another task to use}
- begin
- inputbusy := false;
- end;
-
-
- (************************ MULTI-TASKING WINDOW SUPPORT ********************)
-
-
- procedure initwindows;
- {Initializes the screen and window variables. Every concurrent task gets
- control over the full screen. They should call <window> to reserve a
- portion of the screen for their own use}
-
- var i : integer;
-
- begin
- clearscreen;
- for i := 0 to 16 do
- with windows [i] do
- begin
- x1 := 0;
- y1 := 0;
- x2 := nrofcols;
- y2 := nrofrows;
- attr := libdata [normattr];
- row := 0; col := 0;
- end;
- conoutptr := ofs (conout);
- coninptr := ofs (conin);
- inputbusy := false;
- end;
-
-
- {$v-}
- procedure border (name : astring);
- {Displays a window border with <name> for the current task}
- const wchar : array [1..8] of char =
- (#$da, #$c4, #$bf, #$b3, #$c0, #$c4, #$d9, #$b3);
- var wx, w, i : integer;
-
- procedure putchar (ch : char; x, y : byte);
- {Writes <ch> at <x, y>}
- var regs : registers;
- begin
- regs.ah := 2; regs.bh := 0;
- regs.dl := x; regs.dh := y;
- intr ($10, regs);
- regs.ah := 9;
- regs.bh := 0; regs.al := ord (ch);
- regs.cx := 1; regs.bl := windows [currenttask].attr;
- intr ($10, regs);
- end;
-
- procedure edge (x, y : integer);
- var i : integer;
- begin
- with windows [currenttask] do
- begin
- putchar (wchar [wx], x1, y); wx := wx+1;
- for i := x1+1 to x2-2 do putchar (wchar [wx], i, y); wx := wx+1;
- putchar (wchar [wx], x2-1, y); wx := wx+1;
- for i := y1+1 to y2-2 do putchar (wchar [wx], x, i); wx := wx+1;
- end;
- end;
-
- begin
- wx := 1;
- with windows [currenttask] do
- begin
- border := true;
- edge (x2-1, y1);
- edge (x1, y2-1);
- x1 := x1+1; x2 := x2-1;
- y1 := y1+1; y2 := y2-1;
- col := x1; row := y1;
- w := x2-x1;
- if w >= length (name) then
- begin
- w := (w-length (name)) div 2;
- for i := 1 to length (name) do putchar (name [i], x1+w+i-1, y1-1);
- end;
- end;
- end;
- {$v+}
-
-
- (************************** MODULE INITIALIZATION ********************)
-
-
- procedure taskinit (stackreq : integer);
- {Initializes the task variables. Reserves <stackreq> paragraphs in the
- Turbo Pascal stack segment for the stacks of concurrent tasks}
-
- var i : integer;
-
- begin
- datasegment := dseg; {Needed by <switchtask>}
- for i := 0 to maxtask do activetasks [i] := 0;
- activetasks [0] := 1; {Task 0 = main program enabled}
- currenttask := 0; {Main program is now active}
- allocate_taskarea (stackreq); {Allocate stack space}
- initwindows; {Initialize task windows}
- end;