segment SSMPemulator;
 
 const
 
    {$I ISO.CST }
    {$I SOFT.CST }
    {$I SSMP.CST }
 
 type
    {$I ISO.TYP }
    {$I HARDWARE.TYP }
    {$I SOFT.TYP }
    {$I SSMP.TYP }
 
 var
    SSMPst : SSMPtype;    { used by SSMPfsm and SSMP  }
    SSMPstream, SSMPasmp : boolean;     { used by SSMP              }
    table, elbat     : array [smallint] of smallint;   
    mask             : array [smallint] of boolean;
 
    boxleft, boxright : coltype;
    boxtop, boxbottom : rowtype;
    txbuffer : array [1..fwdlen] of smallint;
    txptr    : smallint;
 
    keyinptr, keyoutptr : smallint;
    keybuffer : array [1..64] of character;
    insbuffer : array [1..optinsert] of character;
    esckeymode : boolean;
    ooc, started, active, hadssmp : boolean;
 
 
    termtype  : ZEUStype;    { used all over             }
    token     : status;
    mode      : array [TLEVEL..KNEWLINE] of smallint; 
    modefail  : array [TLEVEL..KNEWLINE] of boolean;
 
    sum, par  : word;
    p         : array [1..4] of word;
    cdeleted, ldeleted, cinserted, sinserted, linserted, erased : word;
    modecurrent : modetype;
 
 
    xtime, time  : word;
    timing, alarm : boolean; 
        
    msgrestart, newsession, tryrestart, ressuspend : boolean;
    earlynotify        : array [0..7] of boolean;
    
    curfield, maxfield : fieldindex;
    field : array [fieldindex] of savedfield; 
    tabs : array [coltype] of tabstop;
 
 
   { EXTERNAL procedures }
 
   {$I SOFT2.EXT }
   {$I PAD.EXT }
   {$I TNX834.EXT } 
   {$I DRIVERS.EXT }
   {$I ZSLAZY.EXT  }
 
 { procedure ZSLIGHTS;                EXTERNAL; }         {FAWN}
 { Special for Fawn box, other implementations ignore } 
 
 
 
 function bump(k : byte) : byte;
 begin
    if (k < 64) then bump := k + 1 else bump := 1
 end; { bump }
 
 
 { *********** PROCEDURES SHARED WITH OTHER MODULES ************* }
 
 
 {#PUBLIC psetmode -- set a mode array value }
 procedure psetmode(element, value : smallint);
 begin
    mode[element] := value;
    if (element = ICHARMODE) then 
    begin
       if (mode[ICHARMODE] = 0)      {2.0B+}
          then ZSINFO(false,35,'          ') 
          else ZSINFO(true,35, '  Insert  ')
    end
 end;
 
 
 {#PUBLIC enqmode -- enquire the value of a mode array element }
 function enqmode(element : smallint) : smallint;
 begin
    enqmode := mode[element]
 end;
 
 
 {#PUBLIC keychar -- provides a (buffered) user keystroke }
 function keychar(var c : character) : boolean;
 begin
    { NB.  a character with top bit set indicates ESC character }
    if (keyinptr <> keyoutptr) then 
    begin
       c := keybuffer[keyoutptr];
       keyoutptr := bump(keyoutptr);
       keychar := true
    end else keychar := false
 end;
 
 
 { PUBLIC procedure SSMP declared below                            } 
 
 { *************** END OF SHARED PROCEDURES ********************** }
 
 { timer routines added 15/1/86 }
 
 procedure setalarm(timeset : word);
 begin 
    time   := timeset;
    timing := true;
    alarm  := false
 end;
 
 
 procedure cancelalarm;
 begin
    time   := 1;
    timing := false;
    alarm  := false 
 end;
 
 
 procedure tick;
 begin
    if timing then 
    begin
       if (xtime > 0) then xtime := xtime - 1 else
       begin
          xtime := ZSLOOP;
          time  := time - 1;
          if  (time = 0)  then 
          begin
             alarm := true;
             timing := false
          end
       end
    end
 end;
 
 
 { txsession -- session level PAD echo on/off }
 procedure txsession(start : boolean);
 begin
    if start then txbgnsession else txendsession 
 end;
 
 
 { txframe -- frame level PAD echo on/off }
 procedure txframe(start : boolean);
 begin
    if start then txbgnframe else txendframe 
 end;
 
 
 { ***************************************************************** }
 
 
 { flush -- forward all buffered data in txbuffer to host }
 procedure flush;
 var p : smallint;
 begin
    if (txptr >1) then
    begin
       txframe(true);
       for p := 1 to txptr-1 do putc(txbuffer[p]);
       putc(DOLLAR);
       txframe(false);
       putc(CTRLM);
       ZSWAIT(100);
       txptr := 1 
    end
 end;
 
 
 { transmit -- add a character to txbuffer, flush to host if full }
 procedure transmit(c : character);
 begin
    txbuffer[txptr] := c;
    txptr := txptr + 1; 
    if (txptr > fwdlen) then flush
 end;
 
 
 
 { sendnumber -- send a number to host }
 procedure sendnumber(n : smallint);
 begin
    if (n > 99) then transmit(DIG0 +  n div 100);
    if (n > 9)  then transmit(DIG0 + (n div 10) mod 10);
                     transmit(DIG0 +  n mod 10)
 end;
 
 
 { break -- send a break indication to network }
 procedure break;
 begin
    keyoutptr := keyinptr;       { empty key buffer }
    if (mode[INTSIGNAL] > 0) then putc(mode[INTSIGNAL]) else txpadbreak
 end;
 
 
 { parsenum -- parse a stream of characters into n numbers }
 function parsenum(n : word; c : character; var SSMPst : SSMPtype):boolean; 
 var parsed, ok  : boolean;
 begin
    parsed := false;
 
    if (c >= DIG0) and (c <= DIG9) then 
    begin
       sum := 10*sum + (c - DIG0);  
       ok := (sum < 256) 
    end else
 
    if (c = COMMA) then
    begin
       par := par + 1;
       p[par] := sum;
       sum := 0;
       ok := (par < n)
    end else 
 
    if (c = SEMICOL) then
    begin
       par := par + 1;
       p[par] := sum;
       ok := (par = n);
       if ok then parsed := true 
    end else ok := false;
   
    if parsed then SSMPst := DISPLAY else
    if not ok then SSMPst := PRINTIT;   
 
    parsenum := parsed 
 end;
 
 
 { note -- take note of HSETMODE command, also note failure }
 procedure note(element, value : smallint; ok : boolean);
 var i : smallint;
     junk : boolean;
 begin
    if ok then 
    begin
       if (value = 0) then
       begin
          if (element = KINTHOST) then value := cKINTHOST else 
          if (element = KSUSPEND) then value := cKSUSPEND else
          if (element = KRESTART) then value := cKRESTART
       end;
 
       psetmode(element,value);
       modefail[element] := false;
 
       if (element = NOTIFY) then
       for i := 0 to 7 do
       begin  
          earlynotify[i] := odd(value);
          value := value div 2 
       end else
 
       if (element >= KINTHOST) and (element <= KNEWLINE) then 
       begin    {2.0B+}
          if (value <> 0) then junk := ZKSETK(element,value) 
       end else
 
       if (element = SELECTGR) then
       begin
          modefail[element] :=  not ZSSETG(value);
          if modefail[element] then mode[element] := 0 
       end 
    end else modefail[element] := true
 end;
  
 
 
 { pTSETMODE -- report any failed SETMODEs }
 procedure pTSETMODE(element, value : smallint);
 begin
    transmit(COMMAND);
    transmit(TSETMODE);
    sendnumber(element);
    transmit(SEPARATOR);
    sendnumber(value);
    transmit(ENDCMD);
    modefail[element] := false
 end;
 
 
 
 { report -- report any failed HSETMODEs }
 procedure report;
 var element : smallint;
 begin
    for element := TLEVEL to KNEWLINE do 
    if modefail[element] then pTSETMODE(element, mode[element])
 end;
 
 
 { sendchar -- send data to H-end }
 procedure sendchar(kcode : character);
 begin
    { Encode escaped characters }
    if (elbat[kcode] = BLANK) then transmit(kcode) else
    begin
       transmit(ENCODED);
       transmit(elbat[kcode])
    end     
 end;
 
 
 { ********************************************************************** }
 { **                                                                  ** }
 { **   Procedures for H-command processing.                           ** }
 { **                                                                  ** }
 { ********************************************************************** }
 
 
 { fwdcmd -- T-end command generator and optimiser } 
 procedure fwdcmd(keymode : modetype);
 var i : smallint;
 begin
    if (mode[ICHARMODE] <> 0) and (modecurrent = instype) and 
       (cinserted > 0) then
    begin
       transmit(COMMAND);
       transmit(TINSERTBLANK);
       if (sinserted > 0)     { since TINSERT  and ICHARMODE share instype }
          then sendnumber(cinserted + sinserted) 
          else sendnumber(cinserted);
       transmit(ENDCMD);
       for i := 1 to cinserted do sendchar(insbuffer[i])
    end else
 
    if (modecurrent = fldtype) and (keymode = fldtype) then { do nothing } else
    if (modecurrent = xytype)  and (keymode = fldtype) then { do nothing } else
 
    if (modecurrent <> chrtype) and (modecurrent <> fwdtype) then
    begin 
       transmit(COMMAND);
       case modecurrent of
          xytype  : begin
                       transmit(TSETCURSOR);
                       sendnumber(ZSROW);
                       transmit(SEPARATOR);
                       sendnumber(ZSCOL);
                    end;
          deltype : begin
                       transmit(TDELETECHAR);
                       sendnumber(cdeleted);
                    end;
          instype : begin
                       transmit(TINSERTBLANK);
                       sendnumber(sinserted)
                    end;
          eratype : begin
                       transmit(TERASEPREV);
                       sendnumber(erased)
                    end;
          ctrtype : transmit(TERASETORIGHT);
          insline : begin
                       transmit(TINSERTLINE);
                       sendnumber(linserted)
                    end; 
          delline : begin
                       transmit(TDELETELINE);
                       sendnumber(ldeleted)
                    end;
          fldtype : begin
                       transmit(TSETFIELD);
                       sendnumber(curfield)
                    end 
       end {case};
       transmit(ENDCMD)
    end;
 
    ldeleted  := 0;
    linserted := 0;
    cdeleted  := 0;
    cinserted := 0;
    sinserted := 0;
    erased   := 0;
    modecurrent := keymode
 end;
 
 
 { Added 13/1/86 }
 procedure sendtoken;
 begin
    token := host;
    ZSINFO(true,70,'   Host   ')
 end;
 
        
 { TTOKEN -- send token to H-end }
 procedure pTTOKEN(n : character);
 begin
    fwdcmd(fwdtype);
    transmit(COMMAND);
    transmit(TTOKEN);
    sendnumber(n);
    transmit(ENDCMD);
    flush;
    sendtoken    { 13/1/86 }
 end;
 
 
 { phsetmode -- set the mode element specified }
 procedure phsetmode(index : modeindex;
                     value : smallint);
 begin
    if (index <= KNEWLINE) then
    begin
       if (index = TLEVEL)    then note(index, value,
                                      ((value > 0) and (value<=maxnest))) else
       if (index = TMAXROW)   then note(index,value,(value = 23))     else
       if (index = TMAXCOL)   then note(index,value,(value = 79))     else
       if (index = DSINVALID) then note(index,value,(value = 0))      else 
       if (index = NOTIFY)    then note(index,value,true)             else
       if (index = SELECTGR)  then note(index,value,(value <= 9))     else
       if (index = HSHIFTREQ) then note(index,value,(value <= 1))     else
       if (index = CURSOR)    then note(index,value,(value <= 1))     else
       if (index = ICHARMODE) then note(index,value,(value <= 1))     else
       if (index = ILINEROW)  then note(index,value,(value <= 23))    else
       if (index = INTSIGNAL) then note(index,value,(value <= 31))    else
       if (index >= KINTHOST) then note(index,value,true {12/3/87}
                                                    {(value < 27) or 
                                                     (value >= BLANK)}   ) 
    end else pTSETMODE(index,0) 
 end; { phsetmode }
 
 
 procedure phtoken(reqcode : smallint);
 begin
    tryrestart := false;   { 15/1/86 }
 
    if newsession then
    begin
       detectpad;    { Auto PAD recognition added 4/3/86 }
       txsession(true);
       SSMPstream := false;  
       started := true;
       newsession := false
    end; 
 
    pHSETMODE(TLEVEL,0);
    report;                 { Any failed HSETMODEs }
    token := terminal;
    ZSINFO(false,70,'          ');      { added  13/1/86 }
 
    { If DSINVALID false and unconditional keep the token }
    if (mode[DSINVALID] = 0) and (reqcode = 0) then  
    begin
       { Bound cursor => Constrain cursor within update limits } 
       if (mode[CURSOR] = 0) then ZSWIN(boxleft,boxright,boxtop,boxbottom);
       ZSWIN(0,maxcol,0,maxrow)
    end else 
    begin   { 2.1 mod 30/3/1987 }
       { Mod suggested in response to *FORUM item 1024/37 }
       if (mode[DSINVALID] <> 0) then pTSETMODE(DSINVALID, mode[DSINVALID]);
       pTTOKEN(0)
    end
 end; { phtoken }
 
 
 procedure phsetcursor(newrow : rowtype; newcol : coltype);
 begin
    ZSGOXY(newcol, newrow)
 end; { phsetcursor }
 
 
 procedure pheratoright;
 begin
    ZSERAL
 end; { pheratoright }
 
 
 procedure phinsspace(nsp : smallint);
 begin
    ZSINS(nsp);
 end; { phinsspace }
 
 
 procedure phdelchar(nch : smallint);
 begin
    ZSDEL(nch)
 end; { phdelchar }
 
 
 procedure phupscroll(rowa, rowb : smallint;
                      nrows      : smallint);
 var oldcol, oldrow : byte;  {2.0E}
 begin
    oldcol := ZSCOL;         {2.0E}
    oldrow := ZSROW;         {2.0E}
    ZSWIN(0,maxcol,rowa,rowb);
    ZSUPSC(nrows);
    ZSWIN(0,maxcol,0,maxrow);
    {ZSGOXY(0,rowa);}        {2.0E}
    ZSGOXY(oldcol,oldrow)    {2.0E}
 end; { phupscroll } 
 
 
 procedure phdownscroll(rowa, rowb : smallint;
                        nrows      : smallint);
 var oldcol, oldrow : byte;  {2.0E}
 begin 
    oldcol := ZSCOL;         {2.0E}
    oldrow := ZSROW;         {2.0E}
    ZSWIN(0,maxcol,rowa,rowb);
    ZSDNSC(nrows);
    ZSWIN(0,maxcol,0,maxrow);
    {ZSGOXY(0,rowa);}        {2.0E}
    ZSGOXY(oldcol,oldrow)    {2.0E}
 end; { phdownscroll }
 
 
 procedure pheradisplay;
 begin
    ZSCLR
 end; { pherdisplay }
 
 
 procedure pherafields;
 var ftemp : fieldindex;
 begin
    curfield := 0; 
    maxfield := 0;
    for ftemp := 0 to fieldlimit do
       with field[ftemp] do
    begin
       fldtop  := 0;  fldbottom := 0;
       fldleft := 0;  fldright  := 0
    end;
    boxtop  := 0;  boxbottom := 0;  
    boxleft := 0;  boxright  := 0
 end; { pherafields }
 
 
 procedure phsetfield(fidx : fieldindex);
 begin
    if (fidx > maxfield) then maxfield := fidx;
    curfield := fidx;
    with field[curfield] do
    begin
       boxtop  := fldtop;   boxbottom := fldbottom;
       boxleft := fldleft;  boxright  := fldright
    end;
    ZSGOXY(boxleft, boxtop) 
 end; { phsetfield }
 
 
 procedure phsetupdate(rowa, rowb : rowtype;
                       cola, colb : coltype);
 begin
    with field[curfield] do
    begin
       boxtop    := rowa;   fldtop    := boxtop;
       boxbottom := rowb;   fldbottom := boxbottom;
       boxleft   := cola;   fldleft   := boxleft;
       boxright  := colb;   fldright  := boxright
    end
 end; { phsetupdate }
 
 
 procedure pheratabs;
 var ctemp : coltype;
 begin
    for ctemp := 0 to maxcol do tabs[ctemp] := notab
 end; { pheratabs }
 
 
 procedure phsettab(tcol : coltype);
 begin
    if (tcol <= maxcol) then tabs[tcol] := tabset 
 end; { phsettab }
 
 
 { pcontrol -- display control or escape sequences }
 procedure pcontrol(c : word);    { V2.1 8/6/87 }
 var restart, suspend : string30;
 begin
    if (c >= 0) then
    begin
       ZKREST(restart);
       ZKSUSP(suspend);
 
       if (c = mode[KRESTART]) and 
          (restart <> '~                             ') 
          then putstr(restart) else
 
       if (c = mode[KSUSPEND]) and 
          (suspend <> '~                             ') 
          then putstr(suspend) else
 
       if (c < BLANK) then 
       begin
          putstr('CTRL/~                        ');
          ZSPUT(c + 64)
       end else
 
       begin
          putstr('ESC "~                        ');
          ZSPUT(c);
          ZSPUT(DQUOTE)
       end
    end  
 end;
 
 
 { ssmpmessage -- for ssmp messages }
 procedure ssmpmessage(s0, s1 : string30; v1 : word; 
                           s2 : string30; v2 : word);
 var col  : coltype;
     junk : boolean;
 begin
    ZSUPSC(4);
    junk := ZSSETG(DEFAULTREND);
 
    ZSGOXY(0,20);
    for col := 0 to 79 do ZSPUT(MINUS);
    ZSGOXY(0,22);
    for col := 0 to 79 do ZSPUT(MINUS);
 
    ZSGOXY(0,21);
 
    junk := ZSSETG(NEGATIVEIMAGE);
    ZSPUT(BLANK);
    putstr(s0);
    ZSPUT(BLANK);
 
    junk := ZSSETG(DEFAULTREND);
    putstr(s1);   pcontrol(v1);
    putstr(s2);   pcontrol(v2);
    
    ZSGOXY(0,23)
 end;
 
 
 { SSMPexit -- exit an SSMP session } 
 procedure SSMPexit(route : exittype; c : character);
 var junk : boolean; 
 begin
    ZSWIN(0,maxcol,0,maxrow);
    junk := ZSSETG(DEFAULTREND);
 
    case route of
    MSGDISPLAY : begin
                    ZSBELL;
                    ssmpmessage(
                       'Message display follows ...   ',    { 17/1/86 }
                       '    Restart: ~                ', mode[KRESTART],
                       '    Suspend: ~                ', mode[KSUSPEND]);
 
                    junk := ZSSETG(BOLD);
                    ZSPUT(c);      { the character that caused ooc }
                    { empty keyboard buffer }
                    keyinptr  := 1; 
                    keyoutptr := 1;
                    sendtoken;     { 13/1/86 }
                    msgrestart := true;   { 16/1/86 }
                    setalarm(msgtime); { 16/1/86 }
                    ooc       := true
                    { message display mode doesn't exit SSMP directly }
                 end;
 
       SUSPEND : begin
                    ZSBELL; 
                    ssmpmessage(  
                       'Normal terminal operation     ', 
                       '    To attempt restart: ~     ', mode[KRESTART],
                       '~                             ', -1);                    { setresume(true); }
                    ZSGOXY(0,23);
                    txsession(false);
                    oldterminal(termtype)
                 end; 
 
        NORMAL : begin
                    { setresume(false); }
                    ZSGOXY(0,23);  
                    txsession(false);
                    oldterminal(termtype)
                 end
 
    end { case }
 end;
 
 
 procedure phsession(reqcode : smallint); { NB sets global SSMPst }
 var index : smallint;
 begin
    if (reqcode = 0) then 
    begin
       mode[TLEVEL] := mode[TLEVEL] + 1;
       for index := TLEVEL to KNEWLINE do phsetmode(index,0);
       pherafields;  {2.0B}
       pheratabs;    {2.0B}
       SSMPst := INVISIBLE
    end else
    if (reqcode = 1) then
    begin
       mode[TLEVEL] := mode[TLEVEL] - 1;
       mode[DSINVALID] := 1;
       phsetmode(DSINVALID,1);
       if (mode[TLEVEL] = 0) then SSMPexit(NORMAL,BLANK);
       SSMPst := DISPLAY    
    end
 end; { phsession }
 
 
 { ********************************************************************** }
 
 
 { SSMPfsm -- finite state machine emulation for SSMP }
 procedure SSMPfsm(c : character);
 var { NB uses global SSMPst, SSMPstream }
     ok : boolean;
 begin
    case SSMPst of
       DISPLAY :    
          SSMPst := PRINTIT;
    
 
       ENCODED :   { An encoded character follows }
          begin
             if (token = host) then 
                if (table[c] <> BLANK) then ZSPUT(table[c]);
             SSMPst := DISPLAY
          end;
 
       INVISIBLE : { Blank out SSMP identifying message, look for commands }
          if (c = COMMAND) then SSMPst := COMMAND;
   
 
       COMMAND :   { Start of an SSMP command sequence }
          if (c >= HTOKEN) and (c <= HREQTOKEN) then 
          begin
             msgrestart := false;  { bug fix 8/4/86 } 
             cancelalarm;    { bug fix 29/1/86 }
             SSMPst := c;
 
             { Initialise number parser }
             par := 0; 
             sum := 0
          end else SSMPst := PRINTIT;
 
       HTOKEN :    { The token from the host }
          if parsenum(1,c,SSMPst) then
          begin
             if (token = host) and (p[1] <= 1) 
                then phtoken(p[1])
          end;
 
       HSETCURSOR :   { Set the cursor position }
          if parsenum(2,c,SSMPst) then 
          begin
             if (token = host) and (p[1] <= maxrow) and (p[2] <= maxcol)
                then phsetcursor(p[1],p[2]) 
          end; 
 
       HSETMODE :     { Set a mode array element }
          if parsenum(2,c,SSMPst) then
          begin
             if (token = host) and (p[1] <= 37) and (p[2] <= 255)
                then phsetmode(p[1],p[2])
          end;
 
       HERASETORIGHT :   { Erase to the end of line }
          begin
             if (token = host) then pheratoright;
             SSMPst := DISPLAY
          end;
 
       HINSERTBLANK :    { Insert a BLANK at the cursor position }
          if parsenum(1,c,SSMPst) then
          begin
             if (token = host) then phinsspace(p[1])
          end;
 
       HDELETECHAR :    { Delete a character at the cursor position } 
          if parsenum(1,c,SSMPst) then
          begin
             if (token = host) then phdelchar(p[1]) 
          end;
 
       HSOUNDALARM :    { Sound the BELL! }
          begin
            if (token = host) then ZSBELL;
             SSMPst := DISPLAY
          end;
 
       HSCROLLUP :      { Perform a partial screen scroll up }
          if parsenum(3,c,SSMPst) then
          begin
             if (token = host) and 
             (p[1] <= maxrow) and (p[2] <= maxrow) and (p[3] <= maxrow) 
                then phupscroll(p[1],p[2],p[3])
          end;
 
       HSCROLLDOWN :   { Perform a partial screen scroll down }
          if parsenum(3,c,SSMPst) then
          begin
             if (token = host) and
             (p[1] <= maxrow) and (p[2] <= maxrow) and (p[3] <= maxrow) 
                then phdownscroll(p[1],p[2],p[3])
          end;
 
       HERASEDISPLAY :    { Blank out the whole display }
          begin
             if (token = host) then pheradisplay;
             SSMPst := DISPLAY
          end;
 
       HERASEFIELDS :     { Clear all the field settings }
          begin
             if (token = host) then pherafields;
             SSMPst := DISPLAY           
          end;
 
       HSETFIELD :       { Select current field }
          if parsenum(1,c,SSMPst) then
          begin
             if (token = host) and (p[1] <= fieldlimit) 
                then phsetfield(p[1]);
             SSMPst := DISPLAY
          end;          
 
       HSETUPDATE :      { Set the cursor box limits }
          if parsenum(4,c,SSMPst) then
          begin
             if (token = host) and
             (p[1] <= maxrow) and (p[2] <= maxrow) and
             (p[3] <= maxcol) and (p[4] <= maxcol) 
                then phsetupdate(p[1],p[2],p[3],p[4])
          end;
     
       HERASETABS :   { Clear all the tab settings }
          begin
        if (token = host) then pheratabs;
             SSMPst := DISPLAY
          end;
  
       HSETTAB :         { Set a tab position }
          if parsenum(1,c,SSMPst) then
          begin
             if (token = host) and (p[1] <= maxcol) then phsettab(p[1])
          end;
 
       HSESSION :      { Enter (or leave) an  SSMP session (possibly nested)}
          if parsenum(1,c,SSMPst) then
          begin
             if (token = host) then phsession(p[1])
          end;
        
       HREQTOKEN :    { Host requests token, T-end MUST comply }   
          begin
             { always respond to this } 
             pttoken(128);
             SSMPst := DISPLAY
          end
 
    end; { case SSMPst }           
 
    if (SSMPst = PRINTIT) then 
    begin
       if (c = COMMAND) then SSMPst := COMMAND else
       if (c = ENCODED) then SSMPst := ENCODED else
       if (c <> ENDCMD) and (token = host) then 
       begin
          ZSPUT(c);
          SSMPst := DISPLAY
       end else SSMPst := DISPLAY
    end;
 end;
 
 
 { ********************************************************************** }
 { **                                                                  ** }
 { **   Procedures for local action                                    ** }
 { **                                                                  ** }
 { ********************************************************************** }
 
 procedure ptappendline;
 var newrow : rowtype;
 begin
    if (modecurrent <> fwdtype) then fwdcmd(fwdtype);
    transmit(COMMAND);
    transmit(TAPPENDLINE);
    transmit(ENDCMD);
 
    if (ZSROW >= mode[ILINEROW]) or (ZSROW = boxbottom) then
    begin
       { On or below prefered line, scroll area above up one line }
       newrow := ZSROW;
       ZSWIN(boxleft,boxright,boxtop,ZSROW);
       ZSUPSC(1);
       ZSWIN(0,maxcol,0,maxrow);
    end else
    begin
       { Above prefered line, scroll area below down one line }
       newrow := ZSROW + 1;
       ZSWIN(boxleft,boxright,ZSROW+1,boxbottom);
       ZSDNSC(1);
       ZSWIN(0,maxcol,0,maxrow);
    end;
    ZSGOXY(boxleft,newrow); 
    if earlynotify[ENAPPLINE] then setalarm(fwdtime)
 end; { ptappendline }
 
 
 procedure splitline;        
 var rtemp   : rowtype;
     cinit,col   : coltype;
     rowch   : chrow;
     junk    : boolean;
 begin
    { Bug fix 17/2/86 : four lines below were omitted }
    if (modecurrent <> fwdtype) then fwdcmd(fwdtype);
    transmit(COMMAND);
    transmit(TSPLITLINE);
    transmit(ENDCMD);
 
    cinit := ZSCOL;
    rtemp := ZSROW;
 
    { Turn the cursor off so it isnt copied }
    ZSCURS(false);    
 
    { make copy of entire current line within box }
    for col := boxleft to boxright do rowch[col] := ZSGET(col,rtemp);
 
    { Clear remainder of split line to blanks }
    ZSWIN(boxleft,boxright,ZSROW,ZSROW);
    ZSERAL;
 
    if (ZSROW >= mode[ILINEROW]) or (ZSROW = boxbottom) then
    { Below preferred line, scroll current line and area above up one line }
    begin
       ZSWIN(boxleft,boxright,boxtop,rtemp);
       ZSUPSC(1);
       ZSGOXY(boxleft,rtemp)    
    end else
    { Above prefered line, scroll area below down one line }
    begin
       ZSWIN(boxleft,boxright,rtemp + 1,boxbottom);
       ZSDNSC(1);
       ZSGOXY(boxleft,rtemp + 1)
    end;
 
    { copy tail to new line in the *** CURRENT RENDITION *** }
    for col := cinit to boxright do ZSPUT(rowch[col]);
      
    { cursor to start of split text }
    ZSGOXY(boxleft,rtemp+1);              
 
    { window back to whole screen }
    ZSWIN(0,maxcol,0,maxrow);
 
    if earlynotify[ENSPLITLINE] then setalarm(fwdtime)    
 end; { splitline }
 
 
 procedure ptsetfield(fidx : fieldindex);
 begin
    { Note: fidx <= maxfield }
    curfield := fidx;
    with field[curfield] do
    begin
       boxtop  := fldtop;  boxbottom := fldbottom;
       boxleft := fldleft; boxright  := fldright 
    end;
    ZSGOXY(boxleft,boxtop);
    fwdcmd(fldtype) 
 end; { ptsetfield }
 
 
 procedure ptsetcursor(row : rowtype; col : coltype);
 begin
    if (modecurrent <> xytype) then fwdcmd(xytype);
    ZSGOXY(col,row)
 end; { ptsetcursor }
 
 
 function checkfield(warnuser : boolean) : boolean;  
 var found, seeking : boolean;
     tfield : fieldindex;
     trow   : rowtype;
     tcol   : coltype;
 begin
    found := (ZSROW >= boxtop)  and (ZSROW <= boxbottom) and
             (ZSCOL >= boxleft) and (ZSCOL <= boxright);
 
    if not found then
    begin
      seeking := true;
      tfield := 0;
      while seeking do
         with field[tfield] do
      begin
         if (ZSROW >= fldtop)  and (ZSROW <= fldbottom) and
            (ZSCOL >= fldleft) and (ZSCOL <= fldright) then
         begin
            found := true;
            seeking := false
         end else
         if (tfield < maxfield) 
            then tfield  := tfield + 1
            else seeking := false
       end;
 
       if found then
       begin
          trow := ZSROW;    tcol := ZSCOL;
          ptsetfield(tfield);                    { select new field }
          ptsetcursor(trow,tcol)                 { report cursor position }
       end else
       if warnuser then ZSBELL
    end;
    checkfield := found
 end; { checkfield }
 
 
 procedure cursorup;
 begin
    if (mode[cursor] = 0) then
    begin
       if (ZSROW > boxtop)     
          then ptsetcursor(ZSROW-1,ZSCOL) 
          else pttoken(mode[KCSRUP])
    end else
    begin { Free ranging cursor }
       if (ZSROW > 0) 
          then ptsetcursor(ZSROW-1,ZSCOL)
          else ptsetcursor(maxrow,ZSCOL)
    end
 end; { cursorup }
 
 
 procedure cursordown;
 begin
    if (mode[cursor] = 0) then
    begin
       if (ZSROW < boxbottom) 
          then ptsetcursor(ZSROW+1,ZSCOL) 
          else pTTOKEN(mode[KCSRDOWN])
    end else
    begin { Free ranging cursor }
       if (ZSROW < maxrow) 
          then ptsetcursor(ZSROW+1,ZSCOL)
          else ptsetcursor(0,ZSCOL)
    end
 end; { cursordown } 
 
 
 procedure cursorleft;
 begin
    if (mode[cursor] = 0) then
    begin
       if (ZSCOL > boxleft)   
          then ptsetcursor(ZSROW,ZSCOL-1) 
          else pTTOKEN(mode[KCSRLEFT])
    end else
    begin { Free ranging cursor }
       if (ZSCOL >0) then ptsetcursor(ZSROW,ZSCOL-1) else
       begin
          if (ZSROW > 0) 
             then ptsetcursor(ZSROW-1,maxcol)  
             else ptsetcursor(maxrow,maxcol) 
       end
    end
 end; { cursorleft }
 
 
 procedure cursorright;
 begin
    if (mode[cursor] = 0) then 
    begin
       if (ZSCOL < boxright)  
          then ptsetcursor(ZSROW,ZSCOL+1)
          else pTTOKEN(mode[KCSRRIGHT])
    end else
    begin { Free ranging cursor }
       if (ZSCOL < maxcol) then  ptsetcursor(ZSROW,ZSCOL+1) else
       begin
          if (ZSROW < maxrow) 
             then ptsetcursor(ZSROW+1,0)
             else ptsetcursor(0,0) 
       end
    end 
 end; { cursorright }
 
 
 procedure enterkey;
 begin
    ZSLAZY(true);                                 
    while not checkfield(false) do cursorright;
    ZSLAZY(false);                                
    ptsetcursor(ZSROW,boxleft);
    pttoken(mode[KENTER])
 end; { enterkey }
 
 
 { nextstop -- forward tab keystroke processing }
 procedure nextstop;
 var atlimit, seeking : boolean;
     tcol, rlimit     : coltype;
 begin
    if (mode[cursor] = 0) then rlimit := boxright else rlimit := maxcol;
    tcol    := ZSCOL;
    atlimit := false;
    seeking := true;
    
    while seeking do
    begin
       if (tcol < rlimit) then
       begin
          tcol := tcol + 1;
          if (tabs[tcol] = tabset) then seeking := false
       end else
       begin
          seeking := false;
          atlimit := true
       end
    end;
 
    ptsetcursor(ZSROW,tcol);
 
    if atlimit then 
    begin
       if (mode[cursor] = 0) 
          then pTTOKEN(mode[KNEXTTAB])
          else cursorright
    end
 end; { nextstop } 
 
 
 { prevstop -- backward tab keystroke processing }
 procedure prevstop;
 var atlimit, seeking : boolean;
     tcol, llimit     : coltype;
 begin
    if (mode[cursor] = 0) then llimit := boxleft else llimit := 0;
    tcol    := ZSCOL;
    atlimit := false;
    seeking := true;
    
    while seeking do
    begin
       if (tcol > llimit) then
       begin
          tcol := tcol - 1;
          if (tabs[tcol] = tabset) then seeking := false
       end else
       begin
          seeking := false;
          atlimit := true
       end
    end;
 
    ptsetcursor(ZSROW,tcol);
 
    if atlimit then 
    begin
       if (mode[cursor] = 0) 
          then pTTOKEN(mode[KPREVTAB])
          else cursorleft
    end
 end; { prevstop } 
 
 
 procedure leftupdate;
 begin
    ZSLAZY(true);                                
    while not checkfield(false) do cursorright;
    ZSLAZY(false);                             
    ptsetcursor(ZSROW,boxleft)
 end; { leftupdate }
 
 
 procedure firstnonspace;
 var trow : rowtype; tcol : coltype;
 begin
    ZSLAZY(true);                                
    while not checkfield(false) do cursorright;
    ZSLAZY(false);                            
    trow := ZSROW;
    tcol := boxleft;    
    while (tcol < boxright) and (ZSGET(tcol,trow) = BLANK) 
       do tcol := tcol + 1;
    if (ZSGET(tcol,trow) = BLANK) 
       then tcol := boxleft;
    ptsetcursor(trow,tcol)
 end; { firstnonspace }
 
 
 procedure afterlastnonspace;
 var trow : rowtype; tcol : coltype;
 begin
    ZSLAZY(true);                                
    while not checkfield(false) do cursorright;
    ZSLAZY(false);                            
    trow := ZSROW;
    tcol := boxright;
    while (tcol > boxleft) and (ZSGET(tcol,trow) = BLANK) 
       do  tcol := tcol - 1;
    if (tcol < boxright) and (ZSGET(tcol,trow) <> BLANK) 
       then tcol := tcol + 1;
    ptsetcursor(trow,tcol) 
 end; { afterlastnonspace }
 
 
 procedure nextfield;
 begin
    ZSLAZY(true);                                
    while not checkfield(false) do cursorleft;
    ZSLAZY(false);                          
    if (curfield < maxfield) 
       then ptsetfield(curfield+1)
       else ptsetfield(0) 
 end; { nextfield }
 
 
 procedure prevfield;
 begin
    ZSLAZY(true);                                
    while not checkfield(false) do cursorright;
    ZSLAZY(false);
    if (curfield > 0) 
       then ptsetfield(curfield-1)
       else ptsetfield(maxfield) 
 end; { prevfield }
 
 
 procedure homefield;
 begin
    ptsetfield(0)
 end; { phomefield }
 
 
 procedure newline;
 begin
    ZSLAZY(true);                                
    while not checkfield(false) do cursorleft;
    ZSLAZY(false);
    if (ZSROW < boxbottom) 
       then ptsetcursor(ZSROW+1,boxleft)
       else nextfield
 end; { newline } 
 
  
 procedure ptcharacter(kcode : character);
 var i, tosend : smallint;
 begin
    if (mode[ICHARMODE] <> 0) and (modecurrent = instype) then
    begin
       cinserted := cinserted + 1;
       insbuffer[cinserted] := kcode;  
       if (cinserted = optinsert) then fwdcmd(instype)
    end else
    if (modecurrent <> chrtype) then fwdcmd(chrtype);
 
    ZSWIN(boxleft, boxright, boxtop, boxbottom); 
    ZSPUT(kcode);
    ZSWIN(0, maxcol, 0, maxrow); 
 
    if (mode[ICHARMODE] = 0) or (modecurrent <> instype) then sendchar(kcode)
 end; { ptcharacter }
 
 
 procedure pteraprev;
 begin
    if (modecurrent <> eratype) then fwdcmd(eratype);
    ZSGOXY(ZSCOL - 1, ZSROW);
    ZSPUT(BLANK);
    ZSGOXY(ZSCOL - 1, ZSROW);
    erased := erased + 1;
    if earlynotify[ENERAPREV] then setalarm(fwdtime)
 end; { pteraprev }
 
 
 procedure eraseprevious;
 
    procedure tryprevious;
    var trow : rowtype;
        tcol : coltype;
    begin
       cursorleft;
       if checkfield(true) then
       begin
          trow := ZSROW;  tcol := ZSCOL;
          ptcharacter(BLANK);
          ptsetcursor(trow,tcol); 
       end
    end; { tryprevious }
 
 begin
    
    if checkfield(false) then 
    begin
       if (ZSCOL = boxleft) then 
       begin   
          if (mode[cursor] = 0) 
             then pttoken(mode[keraprev])
             else tryprevious
       end else pteraprev 
    end else 
    if (mode[cursor] = 1) then tryprevious
 end; { eraprevious } 
 
         
 procedure ptinsspace(truespace : boolean);
 begin
    if (modecurrent <> instype) then fwdcmd(instype);
    ZSWIN(boxleft,boxright,boxtop,boxbottom);
    ZSINS(1); 
    ZSWIN(0,maxcol,0,maxrow);
    if truespace then sinserted := sinserted + 1;
    if earlynotify[ENINSSPACE] then setalarm(fwdtime) 
 end; { ptinsspace } 
 
 
 procedure pteratoright;
 begin
    if (modecurrent <> ctrtype) then fwdcmd(ctrtype);
    ZSWIN(boxleft,boxright,boxtop,boxbottom);
    ZSERAL;
    ZSWIN(0,maxcol,0,maxrow);
    if earlynotify[ENERARIGHT] then setalarm(fwdtime)
 end; { pteratoright }
 
 
 procedure ptinsline;
 begin
    if (modecurrent <> insline) then fwdcmd(insline);
    ZSWIN(boxleft,boxright,ZSROW,boxbottom);
    ZSDNSC(1);
    ZSWIN(0,maxcol,0,maxrow);
    linserted := linserted + 1; 
    if earlynotify[ENINSLINE] then setalarm(fwdtime)
 end; { ptinsline }
 
 
 procedure ptdelchar;
 begin
    if (modecurrent <> deltype) then fwdcmd(deltype);
    ZSWIN(boxleft,boxright,ZSROW,boxbottom);
    ZSDEL(1);  
    ZSWIN(0,maxcol,0,maxrow);
    cdeleted  := cdeleted  + 1;
    if earlynotify[ENDELCHAR] then setalarm(fwdtime)
 end; { ptdelchar } 
 
 
 procedure ptdelline;
 begin
    if (modecurrent <> delline) then fwdcmd(delline);
    ZSWIN(boxleft,boxright,ZSROW,boxbottom);
    ZSUPSC(1);
    ZSWIN(0,maxcol,0,maxrow);
    ldeleted := ldeleted + 1;
    if earlynotify[ENDELLINE] then setalarm(fwdtime)
 end; { ptdelline }
 
 
 procedure datachar(chkey : character);
 var atboxright : boolean;
 begin
    if checkfield(true) then 
    begin
       if (mode[ICHARMODE] = 1) then ptinsspace(false);    { Insert a space }
       atboxright := (ZSCOL = boxright);
       ptcharacter(chkey);     { replaces character and moves cursor right }
 
       if atboxright then
       begin
          if (mode[cursor] = 0) 
             then pttoken(0) 
             else cursorright      
       end
    end 
 end; { datachar }
 
 
 { ************************************************************************ }
 
 { execute -- an SSMP keystroke }
 procedure execute(x : character);
 begin
    case x of
      { KINTHOST :  pre-empted by keyboard scanner}
                      
      { KSUSPEND :  pre-empted by keyboard scanner} 
                      
      { KRESTART :  pre-empted by keyboard scanner} 
 
       KENTER    : enterkey;
       KCSRUP    : cursorup;
       KCSRDOWN  : cursordown;
       KCSRLEFT  : cursorleft;
       KCSRRIGHT : cursorright;
       KNEXTTAB  : nextstop;
       KPREVTAB  : prevstop;
       KLEFTUPD : leftupdate;
       KFIRSTNS : firstnonspace;
       KALASTNS : afterlastnonspace;
       KERARIGHT: if checkfield(true) then pteratoright;
       KINSMODE : begin
                     if (mode[ICHARMODE] = 1) then fwdcmd(fwdtype); {bug fix 10/4/86}
                     psetmode(ICHARMODE, 1 - mode[ICHARMODE]);
                     ptsetmode(ICHARMODE,mode[ICHARMODE])  {2.1 30/4/87}
                  end;
       KINSSPAC : if checkfield(true) then ptinsspace(true);
       KDELCHAR : if checkfield(true) then ptdelchar;
       KERAPREV : begin
                     eraseprevious;
                     if (token = terminal)     and 
                        (mode[KDELCHAR] <> 0)  and 
                        (mode[ICHARMODE] <> 0) then 
                     begin   { 4/6/86 }
                        if checkfield(true) then ptdelchar
                     end
                  end; 
       KINSLINE : if checkfield(true) then ptinsline;
       KDELLINE : if checkfield(true) then ptdelline;
       KERALINE : if checkfield(true) then 
                  begin
                     ptsetcursor(ZSROW,boxleft);
                     pteratoright
                  end;
       KAPPLINE : if checkfield(true) then ptappendline; 
       KSPLLINE : if checkfield(true) then splitline;
       KNEXTFLD : nextfield; 
       KPREVFLD : prevfield;
       KHOMEFLD : homefield; 
       KNEWLINE : newline
    end { case x }       
 end;
 
 
 { buffkey -- buffer a user keystroke }
 procedure buffkey(c : character);
 begin
    { NB.  a character with top bit set indicates ESC character } 
    keybuffer[keyinptr] := c;
    if (bump(keyinptr) = keyoutptr) then ZSBELL else keyinptr := bump(keyinptr)
 end;
 
 
 
 { pRESTART -- restart an SSMP session }
 procedure pRESTART;
 var i : smallint;
     junk : boolean;
 begin
    { code to flush stored primitives added 13/1/86 }
    fwdcmd(fwdtype);
    flush;
 
    junk := ZSSETG(DEFAULTREND);
    ssmpmessage('Attempting to restart session ',
                '    Please wait ... ~         ',-1,
                '~                             ',-1);
 
 
    { Detect PAD *** only ***  if RESTART from non SSMP mode }
    if newsession then detectpad;
    if not ooc then txsession(true);
 
    tryrestart := true;
    setalarm(restime); 
    newsession := false; 
    started := true;
    active := false;
    ooc := false;
 
    mode[TLEVEL] := 1;
    for i := TLEVEL to KNEWLINE do pHSETMODE(i,0);
    SSMPst := INVISIBLE;
    mode[DSINVALID] := 1;    { the only way to set it }
    pHSETMODE(DSINVALID,1); 
    report;
    pTTOKEN(129);
    { setresume(false); }
    SSMPstream := false;
 end;
 
 
 { maketables - construct tables for SSMP character escaping }
 procedure maketables;
 var i : smallint;
 begin
    for i := 0 to 127 do 
    begin
       table[i] := 32;
       elbat[i] := 32
    end;
 
    table[48] := 0;
    for i := 0 to 25 do table[65+i]  := i+1;
    for i := 0 to 4  do table[49+i]  := i+27;
    for i := 0 to 6  do table[97+i]  := i+33;
    for i := 0 to 5  do table[104+i] := i+58;
    table[110] := 64;
    for i := 0 to 4  do table[111+i] := i+91;
    table[116] := 96;
    for i := 0 to 4  do table[117+i] := i+123;
 
    for i := 0 to 127 do 
    { Generate inverse table }
    elbat[table[i]] := i;
    elbat[32] := 32;
 
    for i := 0 to 127 do mask[i] := (elbat[i] = 32);
    mask[COLON]   := true;
    mask[SEMICOL] := true;
    mask[EQUALS]  := true
 end;
 
 
 procedure qcmd(kcode : character);
 var mindex : smallint;
     seeking : boolean;
 begin 
    mindex  := KENTER;
    seeking := true;
    if (kcode <= DEL) then datachar(kcode) else
    begin
       kcode := kcode - 128;
       while seeking and (mindex <= KNEWLINE) do
       begin
          if (kcode = mode[mindex]) then 
          begin
             execute(mindex);
             seeking := false
          end else mindex := mindex + 1
       end;
       if seeking then pTTOKEN(kcode)
    end 
 end; { qcmd }
 
 
 procedure ssmptimeout;
 begin
    if (token = terminal) then
    begin
       fwdcmd(fwdtype);      { force commands out   }
       flush                 { flush buffer to line }
    end;
 
    if msgrestart then
    begin
       prestart;
       msgrestart := false
    end else
 
    if tryrestart then
    begin
       ssmpmessage('WARNING: no response from host',
                   '    Attempt will continue for ',-1,
                   '40 seconds ... ~              ',-1);
       tryrestart := false;
       ressuspend := true;
       setalarm(40) 
    end else
 
    if ressuspend then
    begin
       ssmpmessage('No response from host         ',
                   '    Abandoning restart attempt',-1,
                   ' ... ~                        ',-1);
       SSMPexit(SUSPEND,BLANK);
       putc(CTRLM)       { Carriage Return after timedout suspension }
    end  
 end; { ssmptimeout }
 
 
 procedure lineprocess;
 var c : character;
 begin
    if ZLDATA then
    begin
       ZSFLSH(false,5000);
       c := ZLGET;
       if (c > 127) then c := c - 128;
 
       if SSMPstream then   { Within SSMP data stream } 
       begin
          if (not  ooc) and mask[c] then SSMPfsm(c) else
          if (c = CTRLM) then 
          begin
             SSMPstream := false;
             hadssmp := true 
          end
       end else  
 
       begin                { Not within SSMP data stream }  
          if (c = SSMPleadin) then 
          begin
             SSMPstream := true;
             if ooc then ZSGOXY(0, ZSROW);
             if started and (not active) then active := true
          end else
 
          if ooc then { Already processing out of context message }
          begin
             if (c = CTRLM) then ZSGOXY(0,ZSROW) else
             if (not hadssmp) and (c = CTRLJ) then ZSUPSC(1) else
             if (c >= BLANK) and (c < DEL) then ZSPUT(c);
             hadssmp := false 
          end else
 
          if (c > BLANK) and (c < DEL) then 
          begin
             { Start processing out of context message }    
             if active then SSMPexit(MSGDISPLAY,c)
          end 
       end 
    end;
 end; { lineprocess }
 
 
 procedure keyprocess;
 var c : character;
 begin
    if ZKDATA then
    begin
       ZSFLSH(false,5000); 
       c := ZKGET;
       if (c = KBREAK) then break else
       begin
          if (c > 127) then c := c - 128;
   
          if esckeymode then 
          begin
             c := c + 128;
             esckeymode := false;
             ZSINFO(false,0,'          ')     { escmessage added 13/1/86 }
          end else esckeymode := (c = ESC);
 
          { escmessage added 13/1/86 }
          if esckeymode then ZSINFO(true,0,'  Escape  ') else
          begin
             if (c < BLANK) or (c = DEL)  then c := c + 128 else
             if (c > DEL) then 
             begin
                c := c - 128;
                if (c >= BLANK) and (c < DEL)  then c := c + 128 
             end;
 
             if (c = (128 + mode[KINTHOST])) then break               else
             if (c = (128 + 126))            then TNX834(3,0)         else
             if (c = (128 + mode[KSUSPEND])) then SSMPexit(SUSPEND,c) else 
             if (c = (128 + mode[KRESTART])) then pRESTART 
                else buffkey(c)
          end
       end
    end
 end; { keyprocess }
 
 {#PUBLIC initkeybuffer }
 procedure initkeybuffer;   {2.0C}
 begin
    keyinptr   := 1;
    keyoutptr  := 1
 end;   
  
 
 procedure initshared;
 var fidx : fieldindex;
     col : coltype;
 begin
    SSMPstream := true;
    SSMPst     := DISPLAY; 
    SSMPasmp   := false; 
    ooc        := false;
    hadssmp    := false;
    started    := false;
    active     := false;  
    newsession := true;
    sendtoken;              { 13/1/86 }
    txptr      := 1;
    initkeybuffer;
    modecurrent:= chrtype;
    ldeleted    := 0;
    linserted   := 0;
    cdeleted    := 0;
    cinserted   := 0;
    sinserted   := 0;
    erased     := 0;
 
    msgrestart := false;
    tryrestart := true;
    ressuspend := false;
 
    xtime := ZSLOOP;
    time  := 0;
    alarm := false;
 
    { for col := 0 to maxcol do tabs[col] := notab;  2.0C}
 
    maketables; 
 
    mode[TLEVEL]  := 0;       { will be corrected if wrong }
    mode[TMAXROW] := 23;      { this can never be altered  }
    mode[TMAXCOL] := 79;      { this can never be altered  }
 
    esckeymode := false;
 
    pherafields;  {2.0B}
    pheratabs     {2.0B}
 end; { initshared }
 
 
 {#PUBLIC SSMP -- SSMP terminal emulator Final Draft } 
 procedure SSMP;
 var { NB uses global SSMPst }
     c : character;
     i : word;
     scanning : boolean; 
     ch : char;
 begin
    termtype := typeSSMP;
 
    initshared;
 
    if enqresume then pRESTART else
    begin
       SSMPfsm(EQUALS);
       SSMPfsm(CAPP);
       SSMPfsm(DIG0);
       SSMPfsm(SEMICOL)
    end;
 
    repeat
       lineprocess;   { communications port scanner }
 
       keyprocess;    { keyboard scanner }
 
       if (termtype { still } = typeSSMP) then
       begin 
          if (token = terminal) then
             if keychar(c) then qcmd(c);     { process keystroke }
 
          ZSFLSH(true,5000);
          ZSUPDT(false);
 
          if alarm then  { SSMP timeout on something }
          begin     
             ssmptimeout;
             alarm := false
          end;
 
          tick;
 
          {ZSLIGHTS;}                                 {FAWN}
       end
    until (termtype <> typeSSMP);
 
    ZSINFO(false,70,'          ');        { 14/1/86 blank out token message }
    psetmode(ICHARMODE,0);                 {2.0B+}
 end;
 
 
 begin
 end.