SECTION "COHAND" // TRIPOS console handler version 3.3 // A smaller version may be produced by exhanging /**/ for //// // and vice versa. GET "COHANHDR" GET "IOHDR" LET actendinput(scb) = VALOF $( IF scb.buf ! scb \= -1 THEN freevec(scb.buf ! scb - buf.data.size) scb.buf ! scb := -1 RESULTIS TRUE $) AND actread(scb) = VALOF $( actendinput(scb) /**/ // Check for @q typed previously /**/ IF scb.arg1 ! scb = 0 THEN /**/ RESULTIS FALSE scb.buf!scb := sendpkt(-1,-scb.type!scb, act.read) /**/ // Check for @q typed now /**/ IF result2 <= 0 THEN /**/ $( scb.arg1 ! scb := 0 /**/ result2 := - result2 /**/ $) scb.end ! scb := result2 RESULTIS result2 > 0 $) LET actendoutput(scb) = VALOF $( IF scb.buf ! scb \= -1 THEN sendpkt(-1,-scb.type!scb,act.write,?, ?, scb.buf ! scb, scb.pos ! scb) RESULTIS TRUE $) AND actwrite(scb) = VALOF $( // Get new buffer for next line actendoutput() scb.buf ! scb := getvec(console.line.words) scb.end ! scb := console.line.chars RESULTIS scb.buf ! scb \= 0 $) LET start(parm.pkt) BE $( LET ttyin.pkts = VEC n.ttyin.pkts * [ttyin.pkt.upb+1] -1 LET ttyout.pkt.space = VEC ttyout.pkt.upb LET ibuf = VEC (input.buffer.upb + 1) / bytesperword LET obuf = VEC echo.buffer.upb / bytesperword /**/ LET self.immolation.pkt = 0 input.buffer, echo.buffer := ibuf, obuf pending.input.queue, pending.output.queue := 0,0 out.pkt.back := TRUE ttyout.pkt := ttyout.pkt.space out.coroutine := createco(check.tty.output,out.stsiz) in.coroutine := createco(handle.input, in.stsiz) input.devtaskid := pkt.arg1 ! parm.pkt output.devtaskid := pkt.arg2 ! parm.pkt pkt.taskid ! ttyout.pkt := output.devtaskid pkt.link ! ttyout.pkt := notinuse pkt.type ! ttyout.pkt := act.ttyout input.pkts := ttyin.pkts FOR j=1 TO n.ttyin.pkts DO $( pkt.taskid ! ttyin.pkts := input.devtaskid pkt.link ! ttyin.pkts := notinuse pkt.type ! ttyin.pkts := act.ttyin qpkt(ttyin.pkts) ttyin.pkts := ttyin.pkts + ttyin.pkt.upb + 1 $) /**/ original.string := "@BV<>/-()LN*NUYRSTXFQZ01234567" //// original.string := "@BV<>/-()LN*NUYRSTXF" escape.table := TABLE 0, '@', '*B', #X7C, #X5F, #X5E, '\', '?', #X7B, #X7D, -at.l, -at.ncr, -at.ncr, -at.uy, -at.uy, -at.r, -at.st, -at.st, -at.x, -at.f /**/ ,-at.q, -at.z, /**/ -at.octdig, -at.octdig, -at.octdig, -at.octdig, /**/ -at.octdig, -at.octdig, -at.octdig, -at.octdig number.of.escapes := original.string % 0 // Finished with parameter packet...send back... qpkt(parm.pkt) // Current task is 1: current.task.number := 1 // Initialise states, etc... terminal.width := default.width carriage.position := 0 /**/ print.table:= TABLE #B0011111110000000, // SI to NUL /**/ #B0000100000000000, // DLE to US /**/ #B1111111111111111, // / to SP /**/ #B1111111111111111, // ? to 0 /**/ #B1111111111111111, // O to @ /**/ #B1111111111111111, // ← to P /**/ #B1111111111111111, // o to ` /**/ #B0111111111111111 // DEL to p char.bell := #X07 /**/ do.tabs := TRUE /**/ escapeout := TRUE /**/ rubout.vdu := TRUE /**/ print.check := TRUE shared.output := TRUE /**/ reflect.on := TRUE /**/ tagged.messages := FALSE bell.pending := FALSE force.case := FALSE /**/ rubout.started := FALSE echo.iptr, echo.optr := -1, -1 input.ptr := -1 pending.line.queue := 0 // Initialise coroutines callco(in.coroutine) callco(out.coroutine) $( LET pkt = taskwait() SWITCHON pkt.type ! pkt INTO $( DEFAULT: qpkt(pkt); LOOP CASE act.findinput: pkt!pkt.arg1!scb.func1 := actread pkt!pkt.arg1!scb.func3 := actendinput returnpkt(pkt, -1, 0) LOOP CASE act.findoutput: pkt!pkt.arg1!scb.func2 := actwrite pkt!pkt.arg1!scb.func3 := actendoutput returnpkt(pkt, -1, 0) LOOP CASE act.read: $( LET qb = findpkt(@ pending.line.queue, pkt.taskid ! pkt) LET qp = add.to.queue(@ pending.input.queue, pkt) IF !qb \= 0 THEN // Line waiting transmit(qp,qb) ENDCASE $) CASE act.write: add.to.queue(@ pending.output.queue,pkt) ENDCASE CASE act.ttyin: $( LET char = (pkt.chres ! pkt) & #177 LET res2 = pkt.res2 ! pkt qpkt(pkt) IF res2 = 0 THEN callco(in.coroutine,char) ENDCASE $) CASE act.ttyout: out.pkt.back := TRUE ENDCASE /**/ CASE act.self.immolation: /**/ // Suicide order. /**/ // Allow shared output, to clear the queue. /**/ shared.output := TRUE /**/ self.immolation.pkt := pkt /**/ ENDCASE $) IF out.pkt.back THEN callco(out.coroutine) /**/ // If the packet is now here, then suicide can /**/ // be done safely, since nothing is being output /**/ // at the moment. /**/ IF out.pkt.back & self.immolation.pkt \= 0 THEN /**/ // See the plot of "Ruddigore". /**/ $( (pkt.arg1!self.immolation.pkt)(self.immolation.pkt) /**/ RETURN /**/ $) $) REPEAT $) // End of START AND read() = VALOF $( LET char = cowait() IF char = char.lf THEN char := char.cr cr.or.esc := char = char.cr | char = char.esc1 | char = char.esc2 IF cr.or.esc THEN char := (char = char.cr -> '*N', '*E') // Check for CTRL/A, B, C, D or E // CTRL/A Break the task // CTRL/B Set flag 1 // CTRL/C Set flag 2 // CTRL/D Set flag 4 // CTRL/E set flag 8 IF char = char.ctrla THEN $( abort(0, current.task.number) LOOP $) IF char.ctrlb <= char <= char.ctrle THEN $( setflags(current.task.number, 1 << (char - char.ctrlb)) LOOP $) /**/ // Check for end of rubout verify sequence /**/ IF char \= char.rubout & rubout.started THEN /**/ $( rubout.started := FALSE /**/ put.echo(']') /**/ $) // Check for @U and @Y: $( LET c = char & case.mask IF force.case & ('A' <= c <= 'Z') THEN char := (force.lower -> c - case.offset,c) $) // Put in echo buffer. put.echo(char) IF /**/ reflect.on & ((echo.optr - echo.iptr) & echo.mask) <= safety.area THEN $( signal.error() LOOP $) RESULTIS char $) REPEAT AND readesc() = VALOF $( LET c = capitalch(read()) /**/ IF c = 'N' THEN /**/ longjump(esc.done.p, esc.done) RESULTIS c $) AND readnum(radix,n) = VALOF $( LET i = 2 WHILE i > 0 DO $( LET c = readesc() LET v = '0' <= c <= '9' -> c - '0', 'A' <= c <= 'F' -> c - 'A' + 10, 100 TEST v < radix THEN $( n := n * radix + v i := i - 1 $) ELSE signal.error() $) RESULTIS n $) AND handle.input() BE $( LET char = read() /**/ LET stream.ended = FALSE /**/ esc.done.p := level() IF char = '@' THEN // Escape combination $( LET radix = 8 char := readesc() FOR j = 1 TO number.of.escapes DO IF char = original.string % j THEN $( LET item = escape.table ! j SWITCHON -item INTO $( CASE at.f: // Throw away lines. // for segmented version unloadseg in next line // has been changed to unloadpending unloadpending(pending.line.queue) pending.line.queue := 0 CASE at.l: // Throw away line. /**/ reflect.on := TRUE input.ptr:=-1 /**/ TEST rubout.vdu THEN /**/ put.echo(line.delete.char) /**/ ELSE put.echo('*N') CASE at.r: force.case := FALSE CASE at.ncr: ENDCASE CASE at.uy: force.case := TRUE force.lower := char = 'Y' ENDCASE CASE at.st: current.task.number:=readnum(10,0) shared.output := char = 'S' ENDCASE /**/ CASE at.q: /**/ cr.or.esc := TRUE /**/ stream.ended := TRUE /**/ put.echo('*N') /**/ IF input.ptr >= 0 THEN /**/ put.input.char('*N') /**/ GOTO not.escape /**/ CASE at.z: /**/ reflect.on := NOT reflect.on /**/ ENDCASE CASE at.x: radix := 16 char := '0' /**/ CASE at.octdig: item := readnum(radix,char - '0') DEFAULT: // Normal escape put.input.char(item) $) GOTO esc.done $) signal.error() $) REPEAT // Until legal escape IF char = char.rubout THEN $( unecho() /**/ IF rubout.vdu THEN /**/ $( put.echo(char.delete.char) /**/ IF input.ptr >= 0 THEN /**/ input.ptr := input.ptr - 1 /**/ LOOP /**/ $) IF input.ptr >= 0 THEN $( /**/ UNLESS rubout.started THEN /**/ $( rubout.started := TRUE /**/ put.echo('[') /**/ $) /**/ put.echo(input.buffer%input.ptr) //// put.echo('←') input.ptr := input.ptr - 1 $) LOOP $) /**/ IF char = char.tab & do.tabs THEN /**/ $( LET n = ((input.ptr + 1) & #177770) + 6 /**/ TEST n < input.buffer.upb THEN /**/ $( unecho() /**/ FOR j = input.ptr TO n DO /**/ $( put.echo(' ') /**/ put.input.char(' ') /**/ $) /**/ $) /**/ ELSE /**/ signal.error() /**/ LOOP /**/ $) UNLESS put.input.char(char) THEN LOOP not.escape: IF cr.or.esc THEN $( LET buffer = getvec(input.ptr/bytesperword+ buf.data.size) LET char.buffer = buffer + buf.data.size IF buffer \= 0 THEN $( LET qp=findpkt(@ pending.input.queue, current.task.number) LET qb = add.to.queue(@ pending.line.queue, buffer) buffer!buf.task := current.task.number buffer!buf.end := input.ptr + 1 FOR j=0 TO input.ptr DO char.buffer % j := input.buffer % j /**/ IF stream.ended THEN /**/ buffer ! buf.end := - buffer ! buf.end IF !qp \= 0 THEN transmit(qp,qb) force.case := FALSE input.ptr:=-1 LOOP $) signal.error() input.ptr:=input.ptr - 1 $) esc.done: LOOP $) REPEAT AND check.tty.output() BE $( // This routine outputs one buffer or one echo // line each time round its main loop TEST echo.iptr = echo.optr THEN // Try for task buffer. $( LET q = (shared.output -> @ pending.output.queue, findpkt(@ pending.output.queue, current.task.number)) LET p = !q IF p \= 0 THEN // Output request! $( LET buf = pkt.bufarg ! p LET end = pkt.endarg ! p !q := !p; !p := notinuse /**/ IF tagged.messages THEN /**/ writef("%N: ", pkt.taskid ! p) qpkt(p) FOR i = 0 TO end - 1 DO wrch(buf % i) freevec(buf) LOOP $) $) ELSE // Echo line waiting: output it: $( LET c = ? $( WHILE echo.optr = echo.iptr DO workwait() echo.optr := echo.optr + 1 c := echo.buffer % (echo.optr & echo.mask) /**/ IF c = char.delete.char & rubout.vdu THEN /**/ $( IF carriage.position > 0 THEN /**/ writes("*X08 *X08") /**/ LOOP /**/ $) /**/ IF c = line.delete.char & rubout.vdu THEN /**/ $( LET cp = carriage.position /**/ print(char.cr) /**/ FOR j = 1 TO cp DO print(' ') /**/ print(char.cr) /**/ BREAK /**/ $) wrch(c) IF c = '*N' | c = '*E' THEN BREAK $) REPEAT LOOP $) // No work whatsoever: wait workwait() $) REPEAT AND wrch(ch) BE // Higher level output routine $( TEST ch = '*N' THEN $( print(char.cr) print(char.lf) $) ELSE $( /**/ LET c = ch & #177 // No parity /**/ LET word, bit = c >> 4, c & 15 /**/ IF ch = '*P' & print.check THEN /**/ print('*N') /**/ TEST ((print.table ! word >> bit) & 1) = 0 & /**/ print.check THEN /**/ TEST escapeout THEN /**/ writef("@X%X2",ch) /**/ ELSE /**/ print('?') /**/ ELSE print(ch) $) $) AND print(ch) BE // Lower level output routine $( LET ci = 1 IF ch = '*E' THEN RETURN IF ch = char.lf | ch = #X07 THEN ci := 0 IF ch = char.bs THEN ci := -1 IF terminal.width <= 0 THEN terminal.width := default.width TEST ch = char.cr THEN carriage.position := 0 ELSE $( IF /**/ print.check & carriage.position+ci > terminal.width THEN wrch('*N') carriage.position := carriage.position + ci $) pkt.charg ! ttyout.pkt := ch pkt.taskid ! ttyout.pkt := output.devtaskid qpkt(ttyout.pkt) out.pkt.back := FALSE workwait() $) AND workwait() BE // Waits for PKT or more work for output. $( cowait() IF bell.pending THEN $( bell.pending := FALSE print(char.bell) $) $) AND findpkt(lv.queue,task) = VALOF $( UNTIL !lv.queue = 0 | pkt.taskid ! (!lv.queue) = task DO lv.queue := !lv.queue RESULTIS lv.queue $) AND add.to.queue(lv.q,item) = VALOF $( LET q = findpkt(lv.q,-1) !q := item; !item := 0 RESULTIS q $) AND transmit(lv.pq,lv.bq) BE $( LET b = !lv.bq LET p = !lv.pq !lv.bq, !lv.pq := !b, !p !p := notinuse pkt.bufres ! p := b + buf.data.size pkt.endres ! p := b ! buf.end qpkt(p) $) AND put.echo(char) BE /**/IF reflect.on | char = '*N' | char = '*E' THEN $( echo.iptr := echo.iptr + 1 echo.buffer % (echo.iptr & echo.mask) := char /**/ reflect.on := TRUE $) AND unecho() BE /**/IF reflect.on THEN echo.iptr := echo.iptr - 1 AND put.input.char(char) = VALOF $( // Puts character into buffer. IF input.ptr>=input.buffer.upb & NOT cr.or.esc THEN $( signal.error() RESULTIS FALSE $) input.ptr:=input.ptr+1 input.buffer % input.ptr := char RESULTIS TRUE $) AND signal.error() BE $( bell.pending := TRUE unecho() $) AND unloadpending(list) BE $( UNTIL list=0 DO $( LET s = !list freevec(list) list := s $) $)