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.