{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.

