
--{{{  Special FFI Linkage 
INLINE PROC init.input ()
  #PRAGMA EXTERNAL "PROC C.tvmspecial.0.init.input () = 0"
  C.tvmspecial.0.init.input ()
:

PROC read.char (RESULT INT ch)
  #PRAGMA EXTERNAL "PROC C.tvmspecial.1.read.char (RESULT INT ch) = 0"
  C.tvmspecial.1.read.char (ch)
:

INLINE PROC write.screen (VAL []BYTE buffer)
  #PRAGMA EXTERNAL "PROC C.tvmspecial.2.write.screen (VAL []BYTE buffer) = 0"
  C.tvmspecial.2.write.screen (buffer)
:

INLINE PROC write.error (VAL BYTE ch)
  #PRAGMA EXTERNAL "PROC C.tvmspecial.3.write.error (VAL BYTE ch) = 0"
  C.tvmspecial.3.write.error (ch)
:
--}}}

-- The actual keyboard handler, please note that it takes INTegers in, so that
-- we can reliably detect when the external keyboard channel has no input for
-- us, without using any of the possible values available in a byte.

PROC keyboard (CHAN BYTE out!)
  VAL INT poll.delay IS 10000:
  SEQ
    init.input ()
    WHILE TRUE
      SEQ
        -- We want to to send as much data as there is available in the
        -- input buffer, this should make I/O from pipes perform better, 
        -- but should not affect keyboard interactive input.
        INT ch:
        SEQ
          read.char (ch)                -- Poll the external keyboard channel
          WHILE ch <> (-1)              -- Check if there was actual key data on it
            SEQ
              out ! (BYTE (ch /\ #FF))  -- The user program will get scheduled now
              read.char (ch)            -- Check if there is more data

        -- Wait for a while, if there were no (more) key events
        TIMER tim:
        INT t:
        SEQ
          tim ? t
          tim ? AFTER t PLUS poll.delay
:

PROC screen (CHAN BYTE in?)
  VAL INT buffer.size IS 64:
  INITIAL INT len IS 0:
  [buffer.size]BYTE buffer:
  WHILE TRUE
    BOOL flush:
    SEQ
      -- Extended rendezvous here. If there is no extended rendezvous we might
      -- loose one character of output. This might happen if:
      --  * A process send a character on screen, blocks
      --  * It gets picked up by screen handler, which runs the process again
      --    (no further code in screen handler than in ? ch is executed)
      --  * The process had sent its last character, does no more
      --    communication and does the final return. Ie the program quits
      --    but the part of the screen handler which does output has never
      --    been executed and the last character is lost
#IF DEFINED (OCCAMPI)
      in ?? buffer[len]
        SEQ
#ELSE
      in ? buffer[len]
      SEQ
        SEQ
#ENDIF
          CASE buffer[len]
            #FF
              flush := TRUE
            '*n', '*c'
              flush, len := TRUE, (len + 1)
            ELSE
              SEQ
                len   := len + 1
                flush := len >= buffer.size
          IF
            flush
              SEQ
                #PRAGMA DEFINED buffer
                write.screen ([ buffer FOR len ])
                len := 0
            TRUE
              SKIP
:

PROC error (CHAN BYTE in?)
  WHILE TRUE
    BYTE ch:
    SEQ
      in ? ch
      IF
        ch <> #FF
          write.error (ch)
        TRUE
          SKIP
:

PROC firmware (CHAN BYTE kyb!, scr?, err?)
  PAR
    keyboard (kyb!)
    screen (scr?)
    error (err?)
:

