segment SOFTemulator; const { $I MACHINE.CST 2.0A-} {$I ISO.CST } {$I SOFT.CST } {$I SSMP.CST } userline = 22; { Line on which user's keystrokes are echoed } splitline = 17; { Line between current screen and scrolling buffer } splitchar = 45; { Character displayed for splitline } msgline = 23; { Messages for paused, escape and insert mode here } sbufflen = 16383; { Length of scrolling buffer in characters } pagesize = 20; { Page size for auto-pausing } maxreplay = 5; { Maximum number of old lines for replay - 1 } type {$I ISO.TYP } {$I HARDWARE.TYP } {$I SOFT.TYP } {$I SSMP.TYP } rawfile = file of byte; {DOS} sbufftype = 0..sbufflen; ttype = (shost,msg,user); domain = record col : coltype; row : rowtype; gr : highlight end; textline = packed array[0..maxcol] of character; var lines : byte; { Counts lines for auto-pausing } ftemp : rawfile; { Temporary data file for chain to TNX } {DOS} prefix, { Current column position of host data } splitcol, { Current column position of user data } prevptr : coltype; { Column position within previous line } withuser, { flags that screen is in user state } theuser, { Used to flag that the user has typed data } split, { Used to flag split-scroll mode } esckeymode, { Escape key has been pressed } paused, { Host display paused by user or auto } pagestop, endline, { Host data has reached column 80 } insmode, { Character insert mode on user line } reenter, { TRUE whilst still copying previous line } transmode, { Transparent mode, keystrokes forward imm. } quietmode : boolean; { Toggle keystroke echoing to screen } hostline : rowtype; { the row on which host data is displayed } termtype : ZEUStype; { Current terminal type, if <> SOFT exit } skeybuffer : textline; { Buffer for keystrokes whilst editing } prevbuffer : array [0..maxreplay] of textline; { Previous line keystr. } current : ttype; { Pointer to current process environment } environment : array [shost..user] of domain; { Storage for env. par. } userdata : array [0..89] of character; { Holds keystrokes thru SETUP } col : integer; maxprefix : integer; replay : byte; { For replaying old user lines } { Globals for scrolling buffer } sbuffer : packed array [sbufftype] of character; { Scrolling buffer } donescroll, { Scroll has been performed => pointers OK } scrollwrap : boolean; { TRUE after scrolling buffer has wrapped } dptr, { Scrolling buffer pointer to cursor line } bptr, { " " " to start of buffer } rptr, { " " " to ruler line } sptr : sbufftype; { New data enters at this pointer position } previous : integer; { Last line visisted in scrolling buffer } lastrow : rowtype; { Last row visited by cursor in scroll area } { EXTERNAL procedure definitions } {$I SOFT2.EXT } {$I SSMP.EXT } {$I DRIVERS.EXT } { ******************* SHARED PROCEDURES ******************* } { PUBLIC procedure SOFT declared below } {#PUBLIC softexit -- For security empty all the buffers maintained by SOFT } procedure softexit; var col : coltype; p : sbufftype; r : byte; begin for col := 0 to maxcol do skeybuffer[col] := 0; for r := 0 to maxreplay do for col := 0 to maxcol do prevbuffer[r,col] := 0; for p := 0 to sbufflen do sbuffer[p] := 0 end; { softexit } { ******************* END OF SHARED PROCEDURES ************ } procedure putsbuffer(c : character); begin sbuffer[sptr] := c; if (sptr = sbufflen) then begin scrollwrap := true; sptr := 0 end else sptr := sptr + 1; if scrollwrap then bptr := sptr end; { putsbuffer } procedure tabnext; begin ZSGOXY(8*((ZSCOL div 8) + 1),ZSROW) end; { tabnext } procedure tabprev; var col : integer; begin if (ZSCOL = 0) then ZSGOXY(0,ZSROW) else ZSGOXY(8*((ZSCOL - 1) div 8),ZSROW) end; procedure newdomain(new : ttype); begin if (new <> current) then begin with environment[current] do begin col := ZSCOL; row := ZSROW; gr := ZSGSET end; with environment[new] do begin ZSGOXY(col,row); if ZSSETG(gr) then { done } end; current := new { environments swapped } end end; { newdomain } function cpalastns(row : rowtype) : coltype; {2.0B+} var col : coltype; begin { Find the column after last non space } col := maxcol; while (col > 0) and (ZSGET(col,row) = BLANK) do col := col - 1; if (col < maxcol) and (ZSGET(col,row) <> BLANK) then col := col + 1; cpalastns := col end; { cpalastns } procedure pcalastns; begin { Put the cursor after last non space } ZSGOXY(cpalastns(ZSROW),ZSROW) end; { pcalastns } procedure formfeed; begin ZSWIN(0,maxcol,0,hostline); ZSGOXY(0,hostline); ZSUPSC(3); ZSWIN(0,maxcol,0,maxrow) end; { formfeed } procedure pcfirstns; var col : coltype; row : rowtype; begin row := ZSROW; {if (row = userline) then col := prefix else col := 0; 2.11} col := prefix; {2.11} while (col < maxcol) and (ZSGET(col,row) = BLANK) do col := col + 1; if (col = maxcol) and (ZSGET(col,row) = BLANK) then begin if (row = userline) then col := prefix else col := 0 end; ZSGOXY(col,row) end; { pcfirstns } procedure stransmit(c : character); begin repeat until ZLRDY; ZLPUT(c) end; { stransmit } procedure message(n : smallint; s : string20; display : boolean); var i : byte; junk : boolean; temp : ttype; begin temp := current; newdomain(msg); if (n > 3) then n := 3; ZSGOXY(20*n,msgline); if display then begin junk := ZSSETG(NEGATIVEIMAGE); for i := 1 to 19 do ZSPUT(ord(s[i])) end else begin junk := ZSSETG(DEFAULTREND); for i := 1 to 20 do ZSPUT(BLANK) end; newdomain(temp) end; { message } procedure pause(onoff : boolean; reason : string20); begin {2.0A+ restructured, little change in logic } pagestop := false; if paused or onoff then { Optimised 2.0-B, DJS ?? } begin paused := onoff; if paused then begin message(0, reason, true); ZLXOFF end else begin message(0,' ',false); ZLXON; {lines := 0 } end end; if not onoff then lines := 0 {2.0C} end; { pause } procedure escmessage(onoff : boolean); begin if esckeymode or onoff then { Optimised 2.0-B, DJS ?? } begin esckeymode := onoff; if esckeymode then message(1,' ESC: ! 0..9 ^? ',true) else message(1,' ',false) end end; { escmessage } procedure insmessage(onoff : boolean); begin if insmode or onoff then { Optimised 2.0-B, DJS ?? } begin insmode := onoff; if insmode then message(2,' Insertion: ^W ',true) else message(2,' ',false) end end; { insmessage } procedure transmessage(onoff : boolean); begin if transmode or onoff then { Optimised 2.0-B, DJS ?? } begin transmode := onoff; if transmode then message(1,' Transparent ',true) {2.0C} else message(1,' ',false) end end; { transmessage } procedure printmessage(device : byte; log : boolean); var p : sbufftype; c : character; begin setprint(device,log); if logging then begin if (device = 1) then message(3,' No log device ',true) else if (device = 2) then message(3,' Printer ',true) else if (device = 3) then message(3,' SOFT.LOG ',true); { Perform print immediately if in scrolling buffer 2.2 } if (ZSROW <> userline) and ((device = 2) or (device = 3)) then begin p := dptr; { Start at cursor line } while (p <> sptr) do begin c := sbuffer[p]; logchar(c); if (c = CTRLM) then logchar(CTRLJ); if (p = sbufflen) then p := 0 else p := p + 1 end; device := 0; setprint(device,FALSE); message(3,' ',false); end end else message(3,' ',false); end; { printmessage } procedure linetobuffer; var col, endcol, oldcol : coltype; begin oldcol := ZSCOL; endcol := cpalastns(ZSROW); {2.1} for col := 0 to endcol do putsbuffer(ZSGET(col,hostline)); putsbuffer(CTRLM); ZSGOXY(oldcol,ZSROW) end; { linetobuffer } procedure snewline; begin if autopause then begin if (lines = pagesize) then begin pause(true,' Paused: ^A '); pagestop := true end else lines := lines + 1 {2.0A+} end; previous := previous - 1; { Move pointer to previous screen back one line } linetobuffer; if split and (not theuser) then begin hostline := 22; ZSGOXY(ZSCOL,hostline); split := false end else begin ZSWIN(0,maxcol,0,hostline); ZSUPSC(1); ZSWIN(0,maxcol,0,maxrow) end; maxprefix := 0; { 2.1 } endline := false { for double wrap prevention in softFSM } end; { snewline } procedure emptykeybuffer; var k : byte; begin for k := 0 to maxcol do skeybuffer[k] := BLANK; { ZSGOXY(prefix,userline); ZSERAL optimise } end; { emptykeybuffer } function keyptr : coltype; begin if (prefix > ZSCOL) then keyptr := 0 else keyptr := ZSCOL - prefix end; { keyptr } procedure insertsp; var col : coltype; begin ZSINS(1); for col := maxcol-1 downto keyptr do skeybuffer[col+1] := skeybuffer[col]; skeybuffer[keyptr] := BLANK end; { insertsp } procedure pcadata; var col : coltype; searching : boolean; begin col := maxcol; searching := true; while searching do begin if (col = 0) then searching := false else if (ZSGET(col-1,ZSROW) <> BLANK) then searching := false else {15/1/86} col := col - 1 end; ZSGOXY(col,ZSROW) { 16/1/86 } end; { pcadata } procedure sflush; var k, head, tail : coltype; a, b, c : character; last, r : byte; col : byte; same, duplicate : boolean; begin head := keyptr; { current cursor position } pcadata; tail := keyptr; { end of printable text } if (tail > head) then head := tail; for k := 0 to head-1 do stransmit(skeybuffer[k]); { only store up to tail in previous userline buffer } skeybuffer[tail] := 0; if (tail > 0) then { Not a null line } begin duplicate := false; r := 0; { Search for duplicate lines in buffer } while not duplicate and (r <= maxreplay) do begin same := true; col := 0; while same and (col <= maxcol) do begin a := skeybuffer[col]; b := prevbuffer[r,col]; if (a = 0) and (b = 0) then col := maxcol+1 else if (skeybuffer[col] <> prevbuffer[r,col]) then same := false else col := col + 1 end; if same then duplicate := true else r := r + 1 end; { If duplicate then swap to last postion in buffer } { Else push into buffer LIFO action, ditch oldest line } if duplicate then last := r else last := maxreplay; for r := last downto 1 do prevbuffer[r] := prevbuffer[r-1]; if not quietmode then prevbuffer[0] := skeybuffer end; prevptr := 0; replay := 0; reenter := true; emptykeybuffer; ZSGOXY(prefix,userline); ZSERAL end; { sflush } procedure swallow(n : coltype); var col : coltype; begin if (n > 0) then begin ZSDEL(n); for col := keyptr to maxcol-n do skeybuffer[col] := skeybuffer[col+n]; for col := maxcol-n+1 to maxcol do skeybuffer[col] := BLANK end end; { swallow } procedure pcnextword; var col, ncol : coltype; begin {2.0B} col := ZSCOL; while (ZSGET(col,ZSROW) <> BLANK) and (col < maxcol) do col := col + 1; ncol := col; while (ZSGET(col,ZSROW) = BLANK) and (col < maxcol) do col := col + 1; if (col = maxcol) and (ZSGET(col, ZSROW) = BLANK) then col := ncol; ZSGOXY(col,ZSROW) end; { pcnextword } procedure pcprevword; var col, ncol : coltype; begin col := ZSCOL; ncol := col; while (ZSGET(col,ZSROW) <> BLANK) and (col > prefix) do col := col - 1; while (ZSGET(col,ZSROW) = BLANK) and (col > prefix) do col := col - 1; while (ZSGET(col,ZSROW) <> BLANK) and (col > prefix) do col := col - 1; if (ZSGET(col,ZSROW) = BLANK) then begin col := col + 1; if (ZSGET(col,ZSROW) = BLANK) then col := ncol end; ZSGOXY(col,ZSROW) end; { pcprevword } procedure delword; var col : coltype; begin if (ZSGET(ZSCOL,ZSROW) = BLANK) then {ZSBELL} else begin pcnextword; col := ZSCOL; pcprevword; swallow(col - ZSCOL) end end; {delword } procedure delstline; var n : coltype; begin n := keyptr + 1; ZSGOXY(prefix,userline); swallow(n) end; { delstline } procedure eraseline; var col : coltype; begin ZSERAL; for col := keyptr to maxcol do skeybuffer[col] := BLANK end; { eraseline } procedure posnline(var n : integer); var no : integer; done, startofline : boolean; solptr, ptr : sbufftype; begin done := false; solptr := dptr; ptr := dptr; if (n > 0) then begin no := 0; repeat startofline := (sbuffer[ptr] = CTRLM); if (ptr = sptr) then done := true else if (ptr = sbufflen) then ptr := 0 else ptr := ptr + 1; if startofline then begin solptr := ptr; no := no + 1; if (no = n) then done := true end until done end else begin { First find the previous CR } no := 1; { because we need to skip current start of line } repeat if (ptr = 0) then ptr := sbufflen else ptr := ptr - 1; if (ptr = bptr) then done := true else if (sbuffer[ptr] = CTRLM) then begin { Should happen at least once } solptr := ptr; no := no - 1; if (no = n) then done := true end until done; { Move pointer on to next printable } if (solptr = sbufflen) then solptr := 0 else solptr := solptr + 1 end; dptr := solptr; n := no end; { posnline } procedure displayoneline(row : rowtype; var ptr : sbufftype); var c : character; begin ZSGOXY(0,row); ZSERAL; repeat c := sbuffer[ptr]; if (c <> CTRLM) then ZSPUT(c); if (ptr = sbufflen) then ptr := 0 else ptr := ptr + 1 until (c = CTRLM) end; { displayoneline } procedure displayline(n : byte); var c : character; ptr : sbufftype; line, direction : integer; col : coltype; row : rowtype; i, fill : rowtype; begin ptr := dptr; row := ZSROW; line := 0; repeat if donescroll and (ptr = rptr) then begin { Move correctly written lines into position } ZSWIN(0,maxcol,0,splitline); fill := splitline - ZSROW -1; if (fill > 0) then ZSDNSC(fill); ZSWIN(0,maxcol,0,maxrow); ZSGOXY(0,splitline); displayoneline(splitline,ptr); { Re-write the ruler line } { Fill in empty lines at top of screen } ZSGOXY(0,0); dptr := rptr; direction := -splitline; posnline(direction); ptr := dptr; for i := 0 to fill-1 do displayoneline(i,ptr); { Reset dptr correct as at splitline } dptr := rptr; ZSGOXY(0,splitline); donescroll := false; { now that its up to date } line := n end else begin displayoneline(row+line,ptr); line := line + 1 end until (line = n) end; { displayline } procedure gotouser; var col, oldcol : coltype; begin if not withuser then { why was this commented out? } begin if (ZSCOL < prefix) then ZSGOXY(prefix,userline) else ZSGOXY(ZSCOL,userline); if split then begin { Put split user line back } oldcol := ZSCOL; ZSWIN(0,maxcol,0,maxrow); ZSUPSC(1); ZSGOXY(0,userline); for col := 0 to maxcol do ZSPUT(skeybuffer[col]); ZSGOXY(oldcol,userline); split := false; { Fixes F1 after CTRL/O bug } theuser := false; {????????????} hostline := 22; {2.0B} end; pause(false,' '); withuser := true end end; { gotouser } procedure currentscreen; var next : integer; col, oldcol : coltype; line : rowtype; begin oldcol := ZSCOL; { correct screen image } dptr := sptr; next := -userline; posnline(next); ZSGOXY(0,0); for line := 0 to userline-1 do displayoneline(line,dptr); ZSGOXY(oldcol,userline); donescroll := false; gotouser end; { currentscreen } procedure splitscreen; var col : coltype; begin { Zero the previously logged screen position so we count back from end ok } previous := 0; { Draw the split ruler across the screen } ZSGOXY(0,splitline); for col := 0 to maxcol do ZSPUT(splitchar); donescroll := true end; { splitscreen } procedure scroll(n : integer); var i : byte; oldcol : coltype; next : integer; begin next := 1; oldcol := ZSCOL; if (n > 0) then { Scroll down the buffer } begin if (n > splitline) then n := splitline; { Scroll remnants of screen and re-write new lines } next := 1; posnline(next); if (dptr = rptr) then displayline(1) { to replace ruler line } else begin ZSWIN(0,maxcol,0,splitline-1); ZSUPSC(n); ZSGOXY(0,splitline-n); displayline(n) end; if donescroll {still} then begin n := n - 1; posnline(n); previous := previous + n + 1; ZSGOXY(oldcol,splitline - 1) end else ZSGOXY(oldcol,splitline) { Current screen } end else if (n < 0) then begin if not donescroll then splitscreen; posnline(n); previous := previous + n; n := -n; ZSGOXY(0,0); if (n > 0) then begin if (n > splitline) then n := splitline; ZSWIN(0,maxcol,0,splitline-1); ZSDNSC(n); displayline(n) end; ZSGOXY(oldcol,0) end; ZSWIN(0,maxcol,0,maxrow) end; { scroll } procedure copyscreen; var col, start : coltype; begin start := ZSCOL; for col := start to maxcol do skeybuffer[col - start] := ZSGET(col,ZSROW); for col := 1 to start do skeybuffer[maxcol - start + col] := BLANK; ZSGOXY(prefix,userline); for col := 0 to maxcol-prefix do ZSPUT(skeybuffer[col]); {2.0C} ZSGOXY(prefix,userline); end; { copyscreen } procedure immediate; begin pause(false,' '); escmessage(false); insmessage(false); transmessage(true); { For immediate keystroke forwarding to pad } emptykeybuffer; ZSGOXY(prefix,userline); ZSERAL end; { immediate } procedure makecopy; var col, oldcol : coltype; begin { line re-entry added 15/1/86 } oldcol := ZSCOL; emptykeybuffer; pcadata; for col := prefix to ZSCOL do skeybuffer[col - prefix] := ZSGET(col,ZSROW); ZSGOXY(prefix,userline); for col := 0 to (maxcol - prefix) do ZSPUT(skeybuffer[col]); if (oldcol > prefix) then ZSGOXY(oldcol,userline) else ZSGOXY(prefix,userline) end; { makecopy } procedure splituser; var x, usercol : coltype; junkc : character; begin { Split user line and host screen } if (splitcol >= prefix) then usercol := splitcol - prefix else usercol := 0; {2.1} { Form new host screen } ZSGOXY(maxprefix,ZSROW); {2.1} ZSERAL; ZSGOXY(0,ZSROW); {2.1} ZSWIN(0,maxcol,0,hostline); ZSUPSC(1); ZSWIN(0,maxcol,0,maxrow); hostline := 21; ZSGOXY(prefix,hostline); {2.1} { Form new user line } newdomain(user); { if ZSSETG(BOLD) then done }; prefix := 0; maxprefix := 0; ZSGOXY(prefix,userline); for x := 0 to maxcol do ZSPUT(skeybuffer[x]); ZSGOXY(usercol,userline); { Switch context back to host } newdomain(shost); split := true end; { splituser } { SOFTfsm -- finite state machine emulation of a SOFTY } procedure SOFTfsm(c : character); begin newdomain(shost); if theuser and (not split) then splituser; if (c >= BLANK) and (c < DEL) then begin { if (ZSCOL = maxcol) then begin snewline; ZSGOXY(0,hostline) end; ZSPUT(c) } {2.0B+} if (ZSCOL = maxcol) then begin if endline then begin snewline; ZSGOXY(0,hostline); ZSPUT(c) end else begin ZSPUT(c); endline := true end end else ZSPUT(c) end else if (c = CTRLG) then ZSBELL else if (c = CTRLH) and (ZSCOL > 0) then ZSGOXY(ZSCOL-1,hostline) else if (c = CTRLI) then tabnext else if (c = CTRLJ) then snewline else if (c = CTRLL) then formfeed else if (c = CTRLM) then begin {if not split then environment[user].col := 0; ???PMB BUG???} ZSGOXY(0,hostline) end; { otherwise character is ignored } if not split then prefix := ZSCOL; if (prefix > maxprefix) then maxprefix := prefix { 2.1 } end; { softfsm } procedure putkey(c : character); begin if (keyptr < (maxcol - prefix - 1)) and (ZSCOL >= maxprefix) {2.1} then begin if insmode then insertsp; skeybuffer[keyptr] := c; if quietmode then ZSPUT(BLANK) else ZSPUT(c) end else begin { Mod 12/6/86 } if split then begin skeybuffer[keyptr] := c; if quietmode then ZSPUT(BLANK) else ZSPUT(c); sflush; immediate end else begin { Call split user normally only called by SOFTfsm} newdomain(shost); { Since splituser assumes environment of SOFTfsm } splituser; { Give the user some more space to type into } newdomain(user); { Put environment back to user } skeybuffer[keyptr] := c; if quietmode then ZSPUT(BLANK) else ZSPUT(c); {!!!! 13/10/87 mod CG} sflush; immediate end end end; { putkey } procedure hideuserline; var next : integer; oldcol : coltype; begin oldcol := ZSCOL; { Scroll user line off bottom of screen } ZSWIN(0,maxcol,0,maxrow); ZSDNSC(1); { Restore top line of screen from buffer } dptr := sptr; next := -userline; posnline(next); ZSGOXY(0,0); displayline(1); ZSGOXY(oldcol,userline) end; { hideuserline } procedure page(n : integer); var direction : integer; begin { If not scrolled set display pointer to current scrolling buffer pointer } if (not donescroll) and (n < 0) then begin if split then hideuserline; pause(true,' Paused: ESC 9 '); withuser := false; { Calculate buffer pointer for line hidden by ruler } dptr := sptr; direction := splitline - userline; posnline(direction); rptr := dptr; { Calculate buffer pointer for top of screen } dptr := sptr; direction := -userline; posnline(direction); ZSGOXY(ZSCOL,0) end else if donescroll and (ZSROW = userline) then begin { Hop back into the scrolling buffer at last row } ZSGOXY(ZSCOL,lastrow); end else if (n < 0) and (ZSROW <> 0) then { Calculate buffer pointer for top of screen, having already scrolled } begin direction := -ZSROW; posnline(direction); ZSGOXY(ZSCOL,0) end else if (n > 0) and (ZSROW < (splitline-1)) then { Calculate buffer pointer for bottom of screen, having already scrolled } begin direction := splitline -1 - ZSROW; posnline(direction); ZSGOXY(ZSCOL,splitline-1) end; { Final trap to ensure user can't scroll down from user line } if (n > 0) and (not donescroll) then begin if (ZSCOL < prefix) then ZSGOXY(prefix,userline) else ZSGOXY(ZSCOL,userline); pause(false,' ') end else scroll(n) end; { page } function prevcmd(var c : character) : boolean; begin if (prevptr < (maxcol-1)) then begin c := prevbuffer[replay,prevptr]; { 0 is end marker for prevbuffer } if (c = 0) then prevptr := maxcol else prevptr := prevptr + 1; prevcmd := (c <> 0) end else prevcmd := false { force end of line } end; { prevcmd } procedure home; begin { Log row in case we want to go back into the scrolling buffer } lastrow := ZSROW; { Home to userline, force to current screen if required } if donescroll then begin if ((ZSROW = userline) or split) then currentscreen else begin gotouser; withuser := false; {2.0B !!!!!} pause(true,' Paused: ESC 9 ') { Continue pause (gotouser normally unpauses) } end end else gotouser; { Finally, put the cursor at the users prefix column } ZSGOXY(prefix,userline) end; { home } procedure keystroke(c : character); var oldcol, n, col : coltype; oldrow : rowtype; junk, continue : boolean; {keystroke processing continues on to userline } i : integer; direction : integer; qptr : sbufftype; ctemp : character; begin newdomain(user); if not theuser then begin if (c <> (128+CTRLA)) and not (paused and (c = (128+CTRLM))) then { the first time on this line the user types a key } begin theuser := true; ZSGOXY(prefix,userline); end end; if ((c < BLANK) or (c = DEL)) then begin { ESC control forwards immediately } if (ZSROW = userline) then begin if donescroll then home; pause(false,' '); sflush; { and keep copy of line for possible re-entry } stransmit(c) end end else if (c <= DEL) then begin if (ZSROW <> userline) then makecopy; putkey(c); replay := 0; reenter := false {2.0A+} end else begin { a single control key is coded as 128 + code, so } c := c - 128; continue := false; oldrow := userline; if (ZSROW = userline) then continue := true else begin if (c = CTRLD) or (c = CTRLE) or (c = CTRLR) or (c = CTRLS) or (c = CTRLT) or (c = CTRLU) or (c = CTRLW) or (c = CTRLY) or (c = DEL) then begin lastrow := ZSROW; { Log lastrow in scrolling buffer } makecopy; continue := true; replay := 0; reenter := false {2.0A+} end else if (c = CTRLC) or (c = CTRLG) or (c = CTRLF) or (c = CTRLV) or (c = CTRLX) or (c = CTRLZ) or (c = CTRLI) {2.0A+} then continue := true else if (c = CTRLH) then begin if (ZSCOL > 0) then ZSGOXY(ZSCOL - 1, ZSROW) end else if (c = CTRLJ) then begin if (ZSROW = (userline - 1)) then gotouser else if (ZSROW < (splitline-1)) or (not donescroll) then begin ZSGOXY(ZSCOL,ZSROW+1); direction := 1; posnline(direction) end else scroll(1) end else if (c = CTRLK) then begin if (ZSROW = 0) then scroll(-1) else begin ZSGOXY(ZSCOL,ZSROW-1); direction := -1; posnline(direction); if (ZSROW = splitline) then rptr := dptr end end else if (c = CTRLL) then begin if (ZSCOL < maxcol) then ZSGOXY(ZSCOL+1,ZSROW) end else if (c = CTRLM) then begin oldrow := ZSROW; oldcol := ZSCOL; copyscreen; {pause(false); 2.0A+} continue := true end else if (c = CTRLN) then begin lastrow := ZSROW; { Log last row in scrolling buffer } emptykeybuffer; copyscreen; { copyscreen does all of this !!! 2.0C ZSGOXY(prefix,userline); for n := 0 to maxcol do ZSPUT(skeybuffer[n]); pause(false); 2.0A+ ZSGOXY(prefix,userline) 2.0C} end else if (c = CTRLO) then begin oldcol := ZSCOL; pcalastns; for col := oldcol to ZSCOL do stransmit(ZSGET(col,ZSROW)); stransmit(CTRLM); ZSGOXY(oldcol,ZSROW); if (ZSROW = (userline - 1)) then gotouser else if (ZSROW < (splitline-1)) or (not donescroll) then begin ZSGOXY(ZSCOL,ZSROW+1); direction := 1; posnline(direction) end else scroll(1) end else if (c = CTRLQ) then begin if (ZSCOL < maxcol) then begin qptr := (dptr + ZSCOL) mod sbufflen; if (sbuffer[qptr] <> CTRLM) then begin sbuffer[qptr] := BLANK; ZSPUT(BLANK) end end end end; if continue then begin if not ((c = CTRLA) or (c = CTRLC) or (c = CTRLL) or paused and (c = CTRLM)) then begin replay := 0; reenter := false end; if (c = DEL) then begin if (keyptr > 0) then begin if insmode then begin ZSGOXY(ZSCOL - 1,ZSROW); swallow(1) end else begin ZSGOXY(ZSCOL - 1,ZSROW); skeybuffer[keyptr] := BLANK; ZSPUT(BLANK); ZSGOXY(ZSCOL - 1,ZSROW) end end end else if (c >= CTRLA) and (c <= CTRLZ) then case c of CTRLA : begin pause(not paused,' Paused: ^A '); pagestop := paused {2.0A+} end; CTRLB : ; CTRLC : begin if reenter then begin emptykeybuffer; {2.0A+} prevptr := 0; {2.0A+} ZSGOXY(prefix,ZSROW); ZSERAL; { Prevent too long a line being re-entered 2.2 } while (ZSCOL < (maxcol - 1)) and prevcmd(ctemp) do putkey(ctemp); { Keep track of multiple re-entered lines 2.2 } if (replay < maxreplay) then replay := replay + 1 else replay := 0 end; { and in any case ... } pcalastns; if (ZSCOL < prefix) then ZSGOXY(prefix,ZSROW) {2.0A+} end; CTRLD : begin sflush; immediate end; CTRLE : swallow(1); CTRLF : begin {2.0B+} tabprev; if (ZSCOL < prefix) then ZSGOXY(prefix,ZSROW) end; CTRLG : home; CTRLH : if (keyptr > 0) then ZSGOXY(ZSCOL-1,ZSROW); CTRLI : tabnext; CTRLJ : pause(false,' '); CTRLK : begin if donescroll then ZSGOXY(ZSCOL,lastrow) else begin if split then hideuserline; pause(true,' Paused: ESC 9 '); ZSGOXY(ZSCOL,ZSROW-1); dptr := sptr; direction := -1; posnline(direction); withuser := false end end; CTRLL : begin if reenter and prevcmd(ctemp) then putkey(ctemp) else if (ZSCOL < maxcol) then ZSGOXY(ZSCOL + 1, ZSROW) end; CTRLM : begin { if pagestop removed for 2.0B then pause(false,' ') else } begin {2.0A changes} pause(false,' '); sflush; { & copy line for possible re-entry } stransmit(CTRLM); if donescroll then currentscreen; {2.0B} {pause(false); 2.0A-} quietmode := false; insmessage(false); theuser := false end end; CTRLN : begin theuser := false; {2.0A+} if donescroll then currentscreen; {2.1} pcadata; environment[shost].gr := BOLD; oldcol := environment[shost].col-1; { Log prefix } { Feed in user keystrokes as if line data } for col := 0 to keyptr do SOFTfsm(skeybuffer[col]); junk := ZSSETG(DEFAULTREND); SOFTfsm(CTRLM); SOFTfsm(CTRLJ); { Newline } for col := 0 to oldcol do SOFTfsm(ZSGET(col,hostline-1)); newdomain(user); { Because SOFTFSM changed it } emptykeybuffer; ZSGOXY(prefix,userline); ZSERAL; end; CTRLO : putkey(CTRLM); CTRLP : begin immediate; stransmit(CTRLP) end; CTRLQ : quietmode := not quietmode; CTRLR : delword; CTRLS : insertsp; CTRLT : delstline; CTRLU : begin reenter := true; { re-allow entry of previous cmd } prevptr := 0; { reset pointer for previous cmd } emptykeybuffer; ZSGOXY(prefix,userline); ZSERAL; replay := 0 end; CTRLV : pcnextword; CTRLW : insmessage(not insmode); CTRLX : pcfirstns; CTRLY : eraseline; CTRLZ : ZSGOXY(prefix,ZSROW); end; { case } if (c = BLANK) then escmessage(false) end; { Do these irrespective of cursor position } if (c >= DIG0) and (c <= DIG9) then case c of DIG0 : oldterminal(termtype); DIG1 : page(8); DIG2 : page(16); DIG3 : page(ZSROW); DIG4 : page(-8); DIG5 : page(-16); DIG6 : printmessage(0,not logging); DIG7 : page(previous); DIG8 : page(-10000); DIG9 : currentscreen; end { case } end; splitcol := ZSCOL { Log users column location } end; { keystroke } {#PUBLIC SOFT -- Soft terminal emulator } procedure SOFT; { NB uses global termtype } var c : character; i, leadin, leadinptr : integer; col, endcol : coltype; row : rowtype; lastsetup, copy, junk : boolean; leadinlog : array [0..7] of character; r : byte; procedure softbreak; {2.0C} begin pause(false,' '); {2.0C} ZSWAIT(100); ZLBRK end; procedure exitleadin; var i : integer; begin for i := 0 to leadin do begin SOFTfsm(leadinlog[i]); logchar(leadinlog[i]) { Log to print device } end; leadin := 0; leadinptr := 0 end; { exitleadin } procedure lineprocess(c : character); begin ZSFLSH(false,5000); if (c > 127) then c := c - 128; { mask top bit } leadinlog[leadinptr] := c; if (leadinptr < 7) then leadinptr := leadinptr + 1; case leadin of 0 : if (c = SSMPleadin) then leadin := 1 else begin SOFTfsm(c); logchar(c); { Log to print device } leadinptr := 0 end; 1 : if (c = EQUALS) then leadin := 2 else exitleadin; 2 : if (c = HSESSION) then leadin := 3 else exitleadin; 3 : if (c = DIG0) then leadin := 4 else if (c = DIG2) then leadin := 8 else if (c = DIG3) then leadin := 5 else if (c = DIG4) then leadin := 6 else {DOS} if (c = DIG7) or (c = DIG8) or (c = DIG9) then begin if (c = DIG7) then printmessage(1,false) { Turn log device off } else printmessage(c-DIG6,true); { Turn log device on } leadin := 7 end else exitleadin; 4 : if (c = SEMICOL) then begin termtype := typeSSMP; newterminal(termtype) end else exitleadin; 5 : if (c = SEMICOL) then begin termtype := typeISO6429; newterminal(termtype) end else exitleadin; 6 : if (c = SEMICOL) then {DOS} begin {DOS} ZLXOFF; { Shut up the line immediately! } {DOS} {DOS} if fstat('TNX.EXE') then termtype := typeTNX else {DOS} if fstat('A:TNX.EXE') then termtype := typeTNX; {DOS} {DOS} if (termtype = typeTNX) then newterminal(termtype) else {DOS} begin { there's no TNX available } {DOS} leadin := 0; {DOS} ZLXON { carry on as before } {DOS} end {DOS} end else exitleadin; {DOS} 7 : leadin := 0; { End whatever from print command! } 8 : if (c = SEMICOL) then begin termtype := typeTRANST; newterminal(termtype) end else exitleadin; end {case} end; { lineprocess } begin { Main body of procedure SOFT } donescroll := false; lines := 0; { For auto pause } endline := false; { Not at end of line yet! } theuser := false; split := false {true}; hostline := 22; termtype := typeSOFT; paused := false; esckeymode := false; setresume(false); { forces a proper level 0 entry to SSMP } junk := ZKSETK(KENTER, 0); junk := ZKSETK(KCSRUP, fmapkey(CTRLK)); junk := ZKSETK(KCSRDOWN, fmapkey(CTRLJ)); junk := ZKSETK(KCSRLEFT, fmapkey(CTRLH)); junk := ZKSETK(KCSRRIGHT, fmapkey(CTRLL)); junk := ZKSETK(KNEXTTAB, fmapkey(CTRLI)); junk := ZKSETK(KPREVTAB, fmapkey(CTRLF)); junk := ZKSETK(KLEFTUPD, fmapkey(CTRLZ)); junk := ZKSETK(KFIRSTNS, fmapkey(CTRLX)); junk := ZKSETK(KALASTNS, fmapkey(CTRLC)); junk := ZKSETK(KINSMODE, fmapkey(CTRLW)); junk := ZKSETK(KERARIGHT, fmapkey(CTRLY)); junk := ZKSETK(KINSSPAC, fmapkey(CTRLS)); junk := ZKSETK(KDELCHAR, fmapkey(CTRLE)); junk := ZKSETK(KERAPREV, fmapkey(DEL)); junk := ZKSETK(KINSLINE, 0); junk := ZKSETK(KDELLINE, 0); junk := ZKSETK(KAPPLINE, 0); junk := ZKSETK(KSPLLINE, 0); junk := ZKSETK(KNEXTFLD, 0); junk := ZKSETK(KPREVFLD, 0); junk := ZKSETK(KHOMEFLD, 0); junk := ZKSETK(KNEWLINE, 0); junk := ZKSETK(KEXIT, DIG0); ZSWIN(0,maxcol,0,maxrow); prefix := 0; leadin := 0; leadinptr := 0; quietmode := false; emptykeybuffer; paused := false; pagestop := false; esckeymode := false; insmode := false; transmode := false; maxprefix := 0; { 2.1 } previous := 0; replay := 0; { Initialise three domains : SHOST, MSG and USER } with environment[msg] do begin col := 0; row := msgline; gr := BOLD end; with environment[shost] do begin col := 0; row := hostline; gr := DEFAULTREND end; with environment[user] do begin col := prefix; row := userline; gr := DEFAULTREND end; current := user; if ZSSETG(DEFAULTREND) then { done }; ZSGOXY(0,msgline); ZSERAL; if firstsoft then begin lastsetup := lastinsetup; { To force side effect in fn lastinsetup } scrollwrap := false; sptr := 0; bptr := 0; dptr := 0; for i := 1 to maxrow+1 do putsbuffer(CTRLM); userdata[0] := 0; userdata[1] := 0; userdata[2] := 0; for r := 0 to maxreplay do begin for col := 0 to maxcol do prevbuffer[r,col] := BLANK; {2.1 moved } prevbuffer[r,0] := 0 end; reenter := false {2.1 moved from above} end else begin lastsetup := lastinsetup; reenter := true; {2.1 to allow re-entry over SSMP sessions etc } if lastsetup then copy := false else copy := copytobuffer; if copy then begin { Copy screen to buffer and log device } { Ensure new line for log device } logchar(CTRLM); logchar(CTRLJ); for row := 0 to maxrow do begin endcol := cpalastns(row); {2.0B} for col := 0 to endcol do begin c := ZSGET(col,row); if (c < BLANK) or (c >= DEL) then c := GRAVE; {2.0A+} putsbuffer(c); logchar(c) end; if (row <> maxrow) then putsbuffer(CTRLM); { Ensure new line for log device } logchar(CTRLM); logchar(CTRLJ) end; ZSUPSC(1); ZSGOXY(prefix,userline); ZSERAL end else begin ZSCLR; currentscreen; { Correct users current line } ZSGOXY(0,userline); if lastsetup then { only re-constitute last user line from SETUP } begin prefix := userdata[0]; environment[shost].col := prefix; for col := 1 to prefix do ZSPUT(userdata[2+col]); for col := prefix+1 to userdata[1] do begin ZSPUT(userdata[2+col]); skeybuffer[col-prefix-1] := userdata[2+col] end; { Recover old column position } col := userdata[2]; ZSGOXY(col,userline); environment[user].col := ZSCOL; theuser := true { Since we've faked up user data } end end end; printmessage(0,logging); { Restore logging message } while keychar(c) do keystroke(fkeymap(c)); if chainsoft then {DOS} if fstat('ZLDATA.$$$') then {DOS} begin {DOS} stransmit(CTRLQ); { Start comms line } {DOS} assign(ftemp,'ZLDATA.$$$'); {DOS} reset(ftemp); {DOS} while not EOF(ftemp) do {DOS} begin {DOS} read(ftemp,c); {DOS} lineprocess(c) {DOS} end; {DOS} erase(ftemp) {DOS} end; {DOS} repeat if ZKDATA then begin ZSFLSH(false,5000); c := ZKGET; if (c = KBREAK) then softbreak else {2.0C} begin if (c > 127) then c := c - 128; if transmode then begin stransmit(c); if (c = CTRLM) then transmessage(false) end else begin if esckeymode then begin c := c + 128; escmessage(false); end else if (c = ESC) then escmessage(true); if not esckeymode then 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; { After these mappings : 0 - 31 => 128 - 159 32 - 126 => 32 - 126 127 => 255 ESC 0 - 31 => 0 - 31 ESC 32 - 126 => 160 - 254 ESC 127 => 127 } if (c = (128 + ATSIGN)) then softbreak else {2.0C} if (c = (128 + EXCLAM)) then begin if donescroll then currentscreen; {2.0A+} theuser := false; {2.0A+} {if not paused then pause(true,' '); } while ZLDATA do c := ZLGET; txpadbreak; pause(false,' ') {2.0C} end else if (c = (128 + enqmode(KRESTART))) then begin ZSBELL; pause(false,' '); insmessage(false); termtype := typeSSMP; newterminal(termtype); setresume(true) { attempt to resume an SSMP session } end else keystroke(fkeymap(c)) end end end end; if (not paused) and ZLDATA then lineprocess(ZLGET); ZSFLSH(true, 5000); { flash the cursor } ZSUPDT(false); { software clock } until (termtype <> typeSOFT); { test comment } { Copy any remnants on user line to scrolling buffer } userdata[0] := prefix; userdata[2] := ZSCOL; pcalastns; userdata[1] := ZSCOL; for col := 0 to userdata[1]-1 do userdata[3+col] := ZSGET(col,userline); pause(false,' '); { 2.1 } end; { SOFT } begin end.