{task #1: keyboard  -> serial out
task #2: serial in ->  video out
     control-C will abort program}
program main;
const   TASKS=2;
       STACKSIZE=70;
{next 7 constants are needed for the Kaypro}
       KDATA=5;
       KSTAT=7;
       BAUDP=0;
       SDATA=4;
       SSTAT=6;
       RMASK=1;
       TMASK=4;

       CC=3;
type    stack = array[0..STACKSIZE] of integer;
       tasknum = -1..TASKS;
var     sp0,sp1,sp2: integer;{when zero, task not initialized}
       oldn: tasknum;
       nextn: tasknum;
Procedure defer; forward;
procedure exit;
         begin
         writeln('TASK #',oldn,' terminated.');
         oldn:=-1;
         defer;
         end;
function keyin:byte;
        begin
          repeat
            defer;
          until (RMASK = (RMASK and port[KSTAT]));
          keyin:= port[KDATA];
        end;
procedure videout(b:byte);
         begin
         bdos(6,b);
         end;
function serin: byte;
        begin
        repeat
          defer;
        until (RMASK = (RMASK and port[SSTAT]));
        serin:= port[SDATA];
        end;
procedure serout(b:byte);
         begin
         repeat
           defer;
         until (TMASK = (TMASK and port[SSTAT]));
         port[SDATA]:=b;
         end;
ne 10
Procedure task1;
var       mystack: stack;
         key: byte;
         begin
         stackptr:=addr(mystack[STACKSIZE]);
         repeat
           key:=keyin;
           if key=CC then exit
           else serout(key);
         until false;{forever}
         exit;
         end;
Procedure task2;
var       mystack: stack;
         begin
         stackptr:=addr(mystack[STACKSIZE]);
         repeat
           videout(serin);
         until false{forever};
         exit;
         end;
procedure initall;
var       i: integer;
         Begin
         sp1:=0;
         sp2:=0;
         oldn:=0;
         {initialize Kaypro's SIO}
         port[BAUDP]:=14;{9600 Baud}
         port[SSTAT]:=24;
         port[SSTAT]:=4;
         port[SSTAT]:=68;
         port[SSTAT]:=1;
         port[SSTAT]:=0;
         port[SSTAT]:=3;
         port[SSTAT]:=193;
         port[SSTAT]:=5;
         port[SSTAT]:=234;
         end;
Procedure schedule;
         begin
         if oldn=TASKS then nextn:=1
         else nextn:=oldn+1;
         end;
bp
procedure defer;
var sp: integer;
       begin
       case oldn of
          0: sp0:=stackptr;
          1: sp1:=stackptr;
          2: sp2:=stackptr;
          end{case};
       schedule;
       oldn:=nextn;
       case nextn of
       0: sp:=sp0;
       1: sp:=sp1;
       2: sp:=sp2;
       end{case};
       if sp<>0 {initialized}
       then begin
         stackptr:=sp;
            end
       else {not initialized}
            begin
            writeln('Starting task #',nextn);
            case nextn of
            1: task1;
            2: task2;
            end{case};
            end;
    end{defer};
begin{main}
initall;
writeln('Multitasking version of simple terminal program');
writeln('Control-C will terminate it');
writeln;
defer;
writeln('Main: done');
end.


{task #1: keyboard -> fifo1
task #2: fifo1    -> filter -> fifo2
task #3: fifo2    -> slow display    }
program main;
const   TASKS=3;
       STACKSIZE=20;
       NFIFOS=2;{#1 is for input and #2 for output}
       PRATE=300;{SLOWs the display function}
{the following three constants are for the Kaypro Computer}
       KDATA=5; KSTAT=7; RMASK=1;
       CR=13;
       LF=10;
       CC=3;
       BS=8;
       RUB=127;
       SPACE=32;
       CQ=17;{XON}
       CS=19;{XOFF}
type    stack = array[0..STACKSIZE] of integer;
       fifo = record
              buf: array[0..255] of byte;
              inptr: byte;
              outptr: byte;
              flow: boolean;{for flow control}
              end;
       fifon = 1..NFIFOS;
       tasknum = -1.. TASKS;
var     sp0,sp1,sp2,sp3: integer;{when zero, task not initialized}
       oldn: tasknum;
       nextn: tasknum;
       fifos: array[1..NFIFOS] of fifo;
Procedure defer; forward;
function occupancy(p: fifon):byte;
        begin with fifos[p] do
        occupancy:= inptr-outptr;
        end;
function vacancy(p: fifon): byte;
        begin with fifos[p] do
        vacancy:=outptr-inptr-1;
        end;
function dequeue1: byte;
        begin with fifos[1] do
        begin
        while (occupancy(1)=0) or not flow
              do defer;
        dequeue1:= buf[outptr];
        outptr:=outptr+1;
        end;
        end;
function dequeue2: byte;
        begin with fifos[2] do
        begin
        while (occupancy(2)=0) or not flow
              do defer;
        dequeue2:= buf[outptr];
        outptr:=outptr+1;
        end;
        end;
procedure exit;
         begin
         writeln('JOB #',oldn,' terminated.');
         oldn:=-1;
         defer;
         end;
procedure enqueue1(b:byte);
         begin with fifos[1] do
         begin
         buf[inptr]:=b;
         while vacancy(1)=0 do
            defer;{hang while full}
         inptr:=inptr+1;
         end;
         end;
procedure enqueue2(b:byte);
         begin with fifos[2] do
         begin
         buf[inptr]:=b;
         while vacancy(2)=0 do
            defer;{hang while full}
         inptr:=inptr+1;
         end;
         end;
function keyin:byte;
        begin
          repeat until (RMASK = (RMASK and port[KSTAT]));
          keyin:= port[KDATA];
        end;
procedure vout(b:byte);
         begin
         bdos(6,b);
         end;
Procedure print;{task#3}
var       mystack: stack;
         i: integer;
         begin
         stackptr:=addr(mystack[STACKSIZE]);
         i:=0;
         {initialize fifo#2}
         with fifos[2] do
              begin
              outptr:=0;
              inptr:=0;
              flow:=true;
              end;
         repeat
           i:=i+1;
           if i=PRATE then
              begin
              i:=0;
              vout(dequeue2);
              end
           else
              defer;
         until false;{forever}
         exit;
         end;
Procedure keyboard;{task #1}
var       mystack: stack;
         cb: byte;
         begin
         stackptr:=addr(mystack[STACKSIZE]);
         {initialize fifo #1}
         with fifos[1] do
              begin
              inptr:=0;
              outptr:=0;
              flow:=true;
              end;
         repeat
           if (1 = (1 and port[KSTAT]))
           then
             begin
             cb:= port[KDATA];
             enqueue1(cb);
             end
           else defer;
         until false{forever};
         exit;
         end;
Procedure filter;{task #2}
var       mystack: stack;
         b: byte;
         begin
         stackptr:=addr(mystack[STACKSIZE]);
         repeat
           b:=dequeue1;
           case b of
           CR: begin
               enqueue2(CR);
               enqueue2(LF);
               end;
           LF: {ignore};
           CC: exit;
           BS,RUB:
              begin
              enqueue2(BS);
              enqueue2(SPACE);
              enqueue2(BS);
              end;
           CQ: fifos[2].flow:=true;
           CS: fifos[2].flow:=false;
           else enqueue2(b);
           end{case};
         until false;{forever!}
         exit;
         end;
procedure initall;
var       i: integer;
         Begin
         sp1:=0;
         sp2:=0;
         sp3:=0;
         oldn:=0;
         end;
Procedure schedule;
         begin
         if oldn=TASKS then nextn:=1
         else nextn:=oldn+1;
         end;
procedure defer;
var sp: integer;
         begin
          case oldn of
          0: sp0:=stackptr;
          1: sp1:=stackptr;
          2: sp2:=stackptr;
          3: sp3:=stackptr;
          end{case};
       schedule;
       oldn:=nextn;
       case nextn of
       0: sp:=sp0;
       1: sp:=sp1;
       2: sp:=sp2;
       3: sp:=sp3;
       end{case};
       if sp<>0 {initialized}
       then begin
         stackptr:=sp;
            end
       else {not initialized}
            begin
            case nextn of
            1: keyboard;
            2: filter;
            3: print;
            end{case};
            end;
    end{defer};
begin{main}
initall;
writeln('<Demonstration of multitasking with queues (FIFOs)>');
writeln;
writeln('Control-S stops output (you can still type ahead!)');
writeln('Control-Q restarts output (you can see what you have typed ahead)');
writeln('RUB or BACKSPACE will "undo" on screen the last letter');
writeln('Control-C terminates this program');
writeln;
defer;
writeln('main: done');
end.