// This is file QL2MAI.BCP // // To be renamed FLP2_KERMAIN_BCPL for QDOS SECTION "Main" /********************************************************************* KK KK EEEEEEEE RRRRRRR MM MM IIIIIIII TTTTTTTT KK KK EEEEEEEE RRRRRRRR MMM MMM IIIIIIII TTTTTTTT KK KK EE RR RR MMMMMMMM II TT KKKK EEEEEE RRRRRRRR MM MM MM II TT KK KK EE RRRRRRR MM MM II TT KK KK EE RR RR MM MM II TT KK KK EEEEEEEE RR RR MM MM IIIIIIII TT KK KK EEEEEEEE RR RR MM MM IIIIIIII TT *********************************************************************/ GET "LIBHDR" GET "FLP2_KERHDR" /* This is QL KERMIT by David Harper Dept of Applied Maths and Theoretical Physics University of Liverpool It is based upon the BCPL implementation written for the Tripos operating system by C.G. Selwyn at Bath University in 1984. I have replaced the finite-state automaton command parser by my own version which allows extra commands/options to be added to the program quite easily. S T A R T of QL K E R M I T Initialise and call the handle routine to execute the current command input stream */ LET start() BE $( LET rp = VEC 100/bytesperword LET pk = VEC 100/bytesperword LET avec = VEC argvl LET c = VEC 80/bytesperword LET tvec = VEC 1 LET setname = VEC 40 LET parser.buffer = VEC 40 LET main.command.table = VEC 20 LET set.command.table = VEC 40 LET set.command.functions = VEC 40 LET rs232.name = VEC 2 // sys.abort := abort // save ABORT routine address abort := kermit.abort // make BCPL abort through our routine ser.name := rs232.name starttime := tvec finishtime := tvec+1 cbuf := c argv := avec parse.buf := parser.buffer main.com.table := main.command.table set.com.table := set.command.table set.function.table := set.command.functions pakcnt := 0 reclevel := 0 erroring := FALSE qcon.init := FALSE // console := open("CON_480x220a26x10_128",0,0) currentin := console selectinput(console) selectoutput(console) finishtime!0 := -1 filecnt := 0 recpkt := rp packet := pk fd := 0 // No file open remfd := 0 // No serial line open yet debug.fd := console // send debugging output to the screen initially // escchr := brkchr remote.delay := 5 image := FALSE quote8ing := FALSE quote8 := myquote8 maxpack := 92 maxtry := 5 reporting := TRUE // s.eol := cr s.packet.length := maxpack s.quote := myquote s.pad := 0 s.padchar := null s.sop := soh s.timeout := 5 // r.eol := myeol r.packet.length := maxpack r.quote := myquote r.pad := mypad r.padchar := mypchar r.sop := soh r.timeout := 5 // local := TRUE remote := \local serving := FALSE debug := FALSE take.echo := FALSE ser.duplex := 'F' ser.escape := kbd.esc ser.handshake := 'I' ser.parity := 'E' ser.pause := 0 ser.line := '2' ser.baud := 4800 ser.corrupt := FALSE // change.my.priority(64) // screen(screen.clear) writef("QL Kermit - Version %N.%N*N",version,update) initialise() // handle() // end.kermit() $) /* H A N D L E This routine handles the parsing and actioning of the current command input stream. Take commands are a recursive call to handle(). */ AND handle() BE $( LET nch = 0 filecnt := 0 erroring := FALSE selectinput(currentin) selectoutput(console) IF currentin = console THEN writef("*NQL-Kermit (%S) > ",remote->"Remote","Local") command := -1 nch := readcommand(cbuf) IF nch<=0 THEN $( TEST reclevel=0 THEN LOOP // Nothing to process ELSE RETURN // End of TAKE file $) IF reclevel>0 & take.echo DO $( writes(cbuf) ; newline() $) nwords := parse.line(cbuf,argv) + 1 TEST do.parse(argv!0,main.com.table) THEN $( SWITCHON command INTO $( CASE w.set : do.set() ENDCASE CASE w.show : do.show() ENDCASE CASE w.c : IF reclevel \= 0 THEN $( writes("Can't connect from take file*N") erroring := TRUE ENDCASE $) IF remote THEN $( writes("Can't connect if remote*N") erroring := TRUE ENDCASE $) connect() ENDCASE CASE w.disconn : TEST remfd \= 0 THEN $( erroring := \disconnect() UNLESS erroring DO remfd := 0 $) ELSE $( writes("*N No serial line open yet *N") erroring := TRUE $) ENDCASE CASE w.s : CASE w.r : handle.sr() ENDCASE CASE w.get : TEST local THEN do.get() ELSE writes("Can't perform get if remote*N") ENDCASE CASE w.close : IF reclevel \= 0 THEN RETURN // If executing file CASE w.e : // Otherwise treat as end command BREAK CASE w.help : TEST nwords=1 THEN show.help() ELSE IF strcomp(argv!1,"SET") THEN show.set() ENDCASE CASE w.server : TEST remfd\=0 THEN TEST serve() THEN BREAK ELSE ENDCASE ELSE $( erroring := TRUE writes("No serial line open yet - can't serve*N") $) // writes("Server mode not yet implemented*N") ENDCASE CASE w.finish : TEST local THEN $(A remote.finish() selectinput(currentin) selectoutput(console) $)A ELSE $( erroring := TRUE writes("Can't issue finish if remote*N") $) ENDCASE CASE w.take : $( LET newin = findinput(argv!1) LET oldin = currentin IF newin < 0 THEN $( writef("Can't find file %S*N",argv!1) erroring := TRUE ENDCASE $) currentin := newin reclevel := reclevel+1 writef(" TAKEing from file %S*N",argv!1) handle() reclevel := reclevel-1 selectinput(currentin) endread() currentin := oldin ENDCASE $) $) $) ELSE $( erroring := TRUE writes("Bad command*N") $) IF erroring & (reclevel \= 0) THEN RETURN $) REPEAT /* s e r v e r Loop collecting commands from the other end and executing them */ AND serve() = VALOF $( LET num,len = ?,? LET r = ? AND local.file.name = VEC 8 AND closed.file = FALSE readchar := (ser.interface=interface.qconnect -> qcon.rdch,raw.rdch) local.fname := local.file.name n := 0 serving := TRUE $( numfiles := 1 filecnt := 0 SWITCHON rpack(@len,@num,recpkt) INTO $( CASE 'I' : spack('Y',num,0,0) ENDCASE CASE 'S' : rpar(recpkt,len) len := spar(packet) report(TRUE) spack('Y',num,len,packet) oldtry := numtry numtry := 0 n := (n+1) REM 64 datstamp(starttime) TEST recsw() THEN datstamp(finishtime) ELSE finishtime!0 := -1 ENDCASE CASE 'R' : FOR i=0 TO len-1 DO local.fname%(i+1) := recpkt%i local.fname%0 := len bytes := 0 TEST sendsw() THEN datstamp(finishtime) ELSE finishtime!0 := -1 ENDCASE CASE 'G' : // Generic commands SWITCHON recpkt%0 INTO $( CASE 'F' : // Finish FOR i = 1 TO 4 DO packet%(i-1) := "OK.*N"%i spack('Y',num,4,packet) r := FALSE // Don't exit BREAK CASE 'L' : // Logout FOR i = 1 TO 4 DO packet%(i-1) := "OK.*N"%i spack('Y',num,4,packet) r := TRUE // Exit BREAK $) DEFAULT : CASE FALSE : ENDCASE $) IF fd \= 0 THEN $( closed.file := close(fd) UNLESS closed.file=0 DO $( selectoutput(console) writes("*N*NFailed to close file at end of serving.*N") writef("Error code is %N*N",closed.file) $) fd := 0 $) $) REPEAT serving := FALSE RESULTIS r $) AND remote.finish() = VALOF $( LET num,len = ?,? IF remfd=0 THEN $(1 WRITES("No serial line open yet*N") RESULTIS FALSE $)1 numtry := 0 n := 0 packet%0 := 'F' $( spack('G',0,1,packet) SWITCHON rpack(@len,@num,recpkt) INTO $( CASE 'Y' : IF len \= 0 THEN message(recpkt,len) RESULTIS TRUE CASE 'N' : CASE FALSE : numtry := numtry+1 IF numtry >= maxtry THEN RESULTIS FALSE ENDCASE DEFAULT : erroring := TRUE RESULTIS FALSE $) $) REPEAT $) AND show.help() BE $( writes("CONNECT - Connect*N") writes("EXIT - Exit*N") writes("FINISH - Finish server mode on a * *remote kermit*N") writes("GET remote-fname local-fname - Get file from a server*N") writes("HELP - This message*N") writes("RECEIVE local-fname - Receive file*N") writes("SEND local-fname remote-fname - Send file*N") writes("SET parameter value - Set various options*N") writes("SERVER - Set server mode*N") writes("SHOW - Show the settable option settings*N" writes("TAKE local-fname - Take commands from a file*N") writes("END - End of command stream*N") writes("DISCONN - Forcibly close serial line*N") $) /* Do.show Show a selection of currently set parameters etc. */ AND do.show() BE $(0 LET option = 0 // screen(screen.clear) writes(" Settable options*N*N") writef(" DEBUGGING : %S*N",(debug -> "ON","OFF")) writef(" DELAY : %N seconds*N",remote.delay) writef(" DUPLEX : %S*N", (ser.duplex='F' -> "FULL","HALF")) writef(" 8BIT-PREFIX : %S*N",(quote8ing -> "ON","OFF")) writef(" END-OF-LINE : %S*N", (r.eol=cr -> "CR","LF")) newline() // SWITCHON ser.escape INTO $(2 // determine terminal escape character CASE kbd.f1 : option := "F1" ; ENDCASE CASE kbd.f2 : option := "F2" ; ENDCASE CASE kbd.f3 : option := "F3" ; ENDCASE CASE kbd.f4 : option := "F4" ; ENDCASE CASE kbd.f5 : option := "F5" ; ENDCASE CASE kbd.esc : option := "ESC" ; ENDCASE CASE kbd.ctrl.esc : option := "CTRL-ESC" ; ENDCASE $)2 writef(" ESCAPE-CHAR : %S*N",option) SWITCHON ser.handshake INTO $(3 // determine handshaking mode CASE 'H' : option := "CTS/RTS" ; ENDCASE CASE 'X' : option := "XON/XOFF" ; ENDCASE CASE 'I' : option := "NONE" ; ENDCASE $)3 writef(" HANDSHAKE : %S*N",option) writef(" MARKER (start of packet) : #X%X2*N",r.sop) writef(" PACKET-LENGTH : %N*N",r.packet.length) writef(" TAKE-ECHO : %S*N*N", (take.echo -> "ON","OFF")) IF reclevel=0 THEN $(B writes("*N*N (Hit any key for next page)") option := rdch() // screen(screen.clear) $)B newline() writef(" PADDING (amount) : %N*N",r.pad) writef(" PAD-CHAR : #X%X2*N",r.padchar) SWITCHON ser.parity INTO $(4 // determine parity CASE 'E' : option := "EVEN" ; ENDCASE CASE 'O' : option := "ODD" ; ENDCASE CASE 'M' : option := "MARK" ; ENDCASE CASE 'S' : option := "SPACE" ; ENDCASE CASE 'N' : option := "NONE" ; ENDCASE $)4 writef(" PARITY : %S*N",option) writef(" PAUSE : %N seconds*N",ser.pause) writef(" PREFIX character : %C*N",quote8) writef(" RETRY limit : %N*N",maxtry) newline() writef(" TIMEOUT : %N seconds*N",r.timeout) writef(" LINE : SER%C*N",ser.line) writef(" BAUD : %N*N",ser.baud) writef(" INTERFACE hardware : %S*N", (ser.interface=interface.qconnect -> "QConnect","None")) newline() writef(" Serial line is currently : %S ", (remfd=0 -> "CLOSED","OPEN")) TEST remfd=0 THEN newline() ELSE writef(" as %S*N",ser.name) $)0 /* Handle the get command */ AND do.get() = VALOF $( LET r = ? LET len,num = ?,? IF remfd=0 THEN $( WRITES("No serial line open yet*N") RESULTIS FALSE $) bytes := 0 numtry := 0 IF nwords<3 THEN $(1 WRITES("Command incomplete *N") RESULTIS FALSE $)1 local.fname := argv!2 filnam := argv!1 FOR j = 0 TO filnam%0 -1 DO packet%j := filnam%(j+1) spack('R',n,filnam%0,packet) r := recsw() UNLESS r THEN $( finishtime!0 := -1 selectoutput(console) writef("Unable to receive %S*N",filnam) RESULTIS FALSE $) selectoutput(console) datstamp(finishtime) writes("*NOK.*N") RESULTIS TRUE $) /* Handle a Send/Receive command */ AND handle.sr() = VALOF $( LET r = ? IF remfd=0 THEN $( WRITES("No serial line open yet*N") RESULTIS FALSE $) bytes := 0 TEST command = w.s THEN $( IF nwords<3 THEN $( WRITES("Command incomplete *N") RESULTIS FALSE $) filnam := argv!2 local.fname := argv!1 r := sendsw() $) ELSE $( IF nwords<2 THEN $( WRITES("Command incomplete *N") RESULTIS FALSE $) local.fname := argv!1 r := recsw() $) selectoutput(console) TEST r THEN $( datstamp(finishtime) IF \remote THEN writef("*NOK.*N") $) ELSE $( IF \remote THEN writef("%S failed.*N",command=w.s->"Send","Receive") finishtime!0 := -1 $) IF fd \= 0 THEN $( close(fd) fd := 0 $) RESULTIS FALSE $) /* The following functions are used in the parsing of the command line and the identification of words therein. PARSE.LINE(line,words) : separates the string 'line' into words i.e. items delimited by spaces. The vector 'words' is set to point to the items found : words!0 points to a string containing the first word in the line, words!1 to the second, etc. The value returned is the highest element of 'words' referred to, and is thus one less than the number of words found. The routine makes use of a vector referred to via the global parse.buf and stores the parsed words in that vector. */ AND parse.line(line,words) = VALOF $(0 LET ch,kwords,lch,thisword = 0,-1,0,0 AND linelength = 0 linelength := getbyte(line,0) thisword := parse.buf FOR K=1 TO linelength DO $(1 // One character at a time ch := getbyte(line,K) IF ch \= SP THEN $(2 // Copy the character lch := lch + 1 putbyte(thisword,lch,ch) $)2 // // Test for the end of a word // IF ((ch = SP) & (lch \= 0)) | ((ch \= SP) & (K = linelength)) THEN $(3 // Found the end of a word putbyte(thisword,0,lch) kwords := kwords + 1 words!kwords := thisword thisword := thisword + 1 + lch/4 lch := 0 $)3 $)1 RESULTIS kwords $)0 // // strcomp compares two strings for equality // AND strcomp(string1,string2) = VALOF $(0 LET length1,length2 = getbyte(string1,0),getbyte(string2,0) AND equality,nch = TRUE,0 TEST length1=length2 THEN $(1 // Strings are of the same length so compare them byte by byte nch := nch + 1 equality := (getbyte(string1,nch) = getbyte(string2,nch)) $)1 REPEATUNTIL ((NOT equality) | (nch = length1)) ELSE $(2 // Strings are of different lengths and so must be different equality := FALSE $)2 RESULTIS equality $)0 /* DO.PARSE(aword,table) : locates the word 'aword' in the parse-table 'table'. If the word is found, the result is TRUE and the global 'command' is set to the position of the word in the table ; otherwise the result id FALSE and 'command' set to -1. */ AND do.parse(aword,wtable) = VALOF $(0 LET k,kwords = 1,0 LET found = FALSE kwords := wtable!0 // The number of words in this table $(1 // Compare each word in turn found := strcomp(aword,wtable!k) k := k + 1 $)1 REPEATUNTIL found | (k > kwords) command := (found -> k-1,-1) RESULTIS found $)0 // initialise() : sets up the command tables AND initialise() BE $(0 // // Set up the main command table first // main.com.table!0 := w.num.commands // Number of commands // main.com.table!w.s := "SEND" main.com.table!w.r := "RECEIVE" main.com.table!w.c := "CONNECT" main.com.table!w.e := "EXIT" main.com.table!w.help := "HELP" main.com.table!w.set := "SET" main.com.table!w.show := "SHOW" main.com.table!w.server := "SERVER" main.com.table!w.finish := "FINISH" main.com.table!w.get := "GET" main.com.table!w.take := "TAKE" main.com.table!w.close := "END" main.com.table!w.disconn := "DISCONN" // // Now set up the SET command table // set.com.table!0 := ws.num.commands // The number of settable // options // set.com.table!ws.bchk := "BLOCK-CHECK" //Not implemented yet set.com.table!ws.debug := "DEBUG" set.com.table!ws.delay := "DELAY" set.com.table!ws.duplex := "DUPLEX" set.com.table!ws.8bitpfx := "8BIT-PREFIX" set.com.table!ws.eol := "END-OF-LINE" set.com.table!ws.escchar := "ESCAPE-CHAR" set.com.table!ws.flowcon := "FLOW-CONTROL" //Not implemented yet set.com.table!ws.handshake := "HANDSHAKE" set.com.table!ws.log := "LOG" //Not implemented yet set.com.table!ws.marker := "MARKER" set.com.table!ws.packetlength := "PACKET-LENGTH" set.com.table!ws.padding := "PADDING" set.com.table!ws.parity := "PARITY" set.com.table!ws.pause := "PAUSE" set.com.table!ws.prefix := "PREFIX" set.com.table!ws.repeatcount := "REPEAT-COUNT" //Not implemented yet set.com.table!ws.retry := "RETRY" set.com.table!ws.timeout := "TIMEOUT" set.com.table!ws.line := "LINE" set.com.table!ws.dir := "DIR" //Not implemented yet set.com.table!ws.overwrite := "OVERWRITE" //Not implemented yet set.com.table!ws.baud := "BAUD" set.com.table!ws.termtype := "TERMINAL-TYPE" //Not implemented yet set.com.table!ws.interface := "INTERFACE" set.com.table!ws.padchar := "PAD-CHAR" set.com.table!ws.take.echo := "TAKE-ECHO" // Set up the set-function table (see "KERSET" for details) set.function.table!ws.bchk := not.yet.implemented set.function.table!ws.debug := set.debug set.function.table!ws.delay := set.delay set.function.table!ws.duplex := set.duplex set.function.table!ws.8bitpfx := set.8bitprefixing set.function.table!ws.eol := set.eol set.function.table!ws.escchar := set.terminal.escape set.function.table!ws.flowcon := not.yet.implemented set.function.table!ws.handshake := set.handshake set.function.table!ws.log := not.yet.implemented set.function.table!ws.marker := set.marker set.function.table!ws.packetlength := set.packetlength set.function.table!ws.padding := set.padding set.function.table!ws.parity := set.parity set.function.table!ws.pause := set.pause set.function.table!ws.prefix := set.prefix set.function.table!ws.repeatcount := not.yet.implemented set.function.table!ws.retry := set.retry set.function.table!ws.timeout := set.timeout set.function.table!ws.line := set.line set.function.table!ws.dir := not.yet.implemented set.function.table!ws.overwrite := not.yet.implemented set.function.table!ws.baud := set.baud set.function.table!ws.termtype := not.yet.implemented set.function.table!ws.interface := set.interface set.function.table!ws.padchar := set.pad.char set.function.table!ws.take.echo := set.take.echo // // // $)0 // AND readcommand(buffer) = VALOF $(0 LET nchs = readline(buffer,72) AND ch = 0 TEST nchs = 0 THEN RESULTIS ENDSTREAMCH ELSE $(1 nchs := nchs - 1 FOR k=nchs-1 TO 0 BY -1 DO $(2 buffer%(k+1) := capitalch(buffer%k) $)2 buffer%0 := nchs RESULTIS nchs $)1 $)0 // AND open.serial.line() BE $(0 LET name = TABLE 3,'S','E','R' AND nptr = 0 nptr := PACKSTRING(name,ser.name) nptr := 4 ser.name%nptr := ser.line // Choose SER1 or SER2 nptr := nptr + 1 TEST ser.interface\=interface.qconnect THEN $(1 // Raw communicatons, no little black boxes UNLESS ser.parity='N' DO $(2 ser.name%nptr := ser.parity nptr := nptr + 1 $)2 UNLESS ser.handshake='X' DO $(3 ser.name%nptr := ser.handshake nptr := nptr + 1 $)3 ser.name%nptr := 'R' // Raw data, no EOF ser.name%0 := nptr // Length of name baud(ser.baud) // Set baud rate remfd := OPEN(ser.name,0,0) // Open the channel IF remfd<0 THEN $(4 // Whoops, we've failed to open the serial line ! WRITEF("*N Unable to open serial line %S (QDOS error code %N)*N", ser.name,remfd) remfd := 0 RETURN $)4 $)1 ELSE $(5 // Communications via a QConnect box UNLESS qcon.init DO qcon.reset() ser.name%nptr := 'H' // CTS/RTS between QL and box nptr := nptr + 1 ser.name%nptr := 'R' // Raw data, no EOF ser.name%0 := nptr baud(9600) remfd := OPEN(ser.name,0,0) IF remfd<0 THEN $(6 WRITEF("*N Unable to open serial line %S (QDOS error %N)*N",ser.name, remfd) remfd := 0 RETURN $)6 qcon.initialise() $)5 $)0 // AND find.new.file(name) = VALOF $(0 LET exists = FINDINPUT(name) debug.report(writef,"*NTrying to open new file %S*N",name) IF exists>0 THEN $(1 // The file already exists close(exists) debug.report(writes,"Failed - file already exists*N") RESULTIS -8 // QDOS ERR.EX code $)1 exists := findoutput(name) TEST exists>0 THEN debug.report(writes,"File opened successfully*N") ELSE debug.report(writef,"Failed - error code is %N*N",exists) RESULTIS exists $)0 // AND find.old.file(name) = VALOF $(0 LET exists = findinput(name) debug.report(writef,"*NTrying to open old file %S*N",name) TEST exists>0 THEN debug.report(writes,"File opened successfully*N") ELSE debug.report(writef,"Failed - error code %N*N",exists) RESULTIS exists $)0 AND message(m,n) BE FOR i=0 TO n-1 DO wrch(m%i) AND end.kermit() BE $(0 screen(screen.clear) writes("QL Kermit : exiting back to SuperBasic*N") STOP(0) $)0 AND datstamp(x) BE !x := time() // AND qcon.reset() BE $(0 IF remfd\=0 DO close(remfd) remfd := OPEN("SER2IR",0,0) selectoutput(remfd) writes("%X1F%X21%X70") close(remfd) qcon.init := TRUE selectoutput(console) ink(red) writes("*N QConnect reset OK*N") ink(green) $)0 // AND qcon.initialise() BE $(0 LET inits = TABLE #X1F164A35, #X00600E00 AND ch = 0 // // Parity // IF ser.parity='E' | ser.parity='O' THEN $(1 ch := 16 + (ser.parity='E' -> 32,0) inits%2 := inits%2 | ch inits%5 := inits%5 | 32 $)1 // // Handshake // UNLESS ser.handshake='N' DO $(2 ch := 2 + (ser.handshake='X' -> 64,1) inits%5 := inits%5 | ch $)2 // // Baud // ch := 0 SWITCHON ser.baud INTO $(3 CASE 9600 : ENDCASE CASE 4800 : ch := 1 ; ENDCASE CASE 2400 : ch := 2 ; ENDCASE CASE 1200 : ch := 3 ; ENDCASE CASE 600 : ch := 4 ; ENDCASE CASE 300 : ch := 5 ; ENDCASE CASE 150 : ch := 6 ; ENDCASE DEFAULT : catastrophe("Illegal baud rate value in qcon.init") $)3 ch := ch + (ch << 3) inits%4 := ch selectoutput(remfd) writebytes(inits,8) selectoutput(console) ink(red) writef("*N QConnect initialised with string %X8 %X8*N",inits!0,inits!1) ink(green) $)0 // AND raw.rdch() = VALOF $(0 LET ch = inkey(0) WHILE ch<0 & time()<=endtime DO ch := inkey(0) RESULTIS (ch<0 -> rpack.timeout,ch) $)0 AND qcon.rdch() = VALOF $(0 LET ch = raw.rdch() UNLESS ch=USC THEN RESULTIS ch ch := inkey(-1) RESULTIS (ch=USC -> USC,rpack.timeout) $)0 // AND BAUD(speed) BE $(0 LET regsin = VEC 7 AND regsout = VEC 7 regsin!0 := #X12 // MT.BAUD regsin!1 := speed qtrap(1,regsin,regsout) $)0 // AND beep() BE $(0 /* LET regsin = VEC 7 AND regsout = VEC 7 AND bparms = TABLE #X0A0B0000, #XAAAA0000, #X00000000, #X00000000 regsin!0 := #X11 // MT.IPCOM regsin!7 := bparms << 2 // MC address of parameters qtrap(1,regsin,regsout) */ ink(red) writes("") ink(green) $)0 // AND glasstty() BE $(0 LET ch,lastch = 0,0 selectoutput(console) screen(screen.cursor) $(1 // Terminal emulation loop selectinput(console) ch := inkey(0) IF ch=ser.escape THEN BREAK IF ch=kbd.left | ch=kbd.ctl.left THEN ch := kbd.del IF ch>0 & ch<128 THEN $(1 selectoutput(remfd) wrch((ch=LF -> CR,ch)) $)1 selectinput(remfd) ch := inkey(0) IF ch<0 THEN LOOP selectoutput(console) ch := ch & #X7F IF ser.interface=interface.qconnect & ch=USC THEN $(5 // Handle USC sequence from QConnect box ch := INKEY(-1) // Get this byte at all costs IF ch\=USC DO $(6 qcon.report(ch) LOOP $)6 $)5 TEST ch",ch) ink(green) $)0 // // Our ABORT exit routine // AND kermit.abort(code) BE $(0 selectoutput(console) screen(screen.clear) sys.abort(code) $)0 // // debug.report : cf. cons in kerproto.bcpl // AND debug.report(f,a1,a2,a3,a4,a5) BE IF debug THEN $(0 LET co = COS selectoutput(debug.fd) f(a1,a2,a3,a4,a5) selectoutput(co) $)0 // // QDOS call to change the priority of the current job // AND change.my.priority(priority) BE $(0 LET regsin = VEC 7 AND regsout = VEc 7 // regsin!0 := #X0B // MT.PRIOR regsin!1 := -1 // change my priority regsin!2 := priority & #X7F // priority must be in range 0 to 127 // qtrap(1,regsin,regsout) $)0 // AND sendchars(buffer,nchars) BE writebytes(buffer,nchars)