&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
Last modified : 10-Feb-82 kb
File : /kb/kbrddsgn.text

&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&

This is the desgin document for the interrupt driven keyboard.

&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&

Each table that is user definable has a pointer in a table which has a pointer
to it in the Systems Communcations Area of Merlin.  The pointers in this table
(called Translation Table) point to the following tables :
      1) SHIFT_TABLE
      2) REGULAR_TABLE
      3) CAPS_QUALIFIER FLAG TABLE
      4) ESCAPE # SEQUENCE TABLE
      5) STANDARD MULTIPLE CHARACTER SEQUENCE TABLE
      6) RELEASE TABLE
      7) BREAK KEYCODE TABLE
The pointers are in the above order.

Problem :
   If a qualifier key (e.g. a shift key) and a key which generates a character
   sequence are depressed and the qualifier key is released then the repeat will be
   of the qualified character sequence not the unqualified sequence.  The Fast
   key is an exception to this condition.
   Example of this problem is :  the shift key and the A key are depressed together.
   Then the shift key is released but the A key is still depressed.  The repeat
   character does not change to "a" but remains as "A".

Consider :
   Since have XON & XOFF capabilities may want to drop BREAK and use the key
   for context switching.

&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&

---------------------------------------------------
pseudo-code for the interrupt service routine.
---------------------------------------------------
save registers (all or some?) on stack

Had_interrupt := true; <-- tell slave to restart if running.

get KEYCODE --> must be in a register
if (KBRD data transmission error) then begin
   set Error flag.
   if (NOT only an OVERRUN error) then GOTO EXIT.
   {character in data port is valid last character or new char (?) if overrun only}
 end;

get Primary and Secondary state record Base pointers.

(* the interrupt service routine always uses the Primary state record. *)

if (KEYCODE=BREAK Table keycode (currently : $DF - only on closure)) then
   BEGIN
     check Break flag in qualifier flag word (bit D4)
     if (Break flag is set) then (* process is currently stopped *)
       BEGIN
        clear Break flag
        restore registers from stack
        do RTE
       END
     else BEGIN (* process is currently running, stop it *)
        set Break flag
        set status register interrupt status to allow Timer interrupt.
              (* interrupt level #6, so set int stat to 5 *)
        while (Break flag is set) do; (* wait until break key closure *)
        restore registers from stack
        do RTE
      END
   END
else
   if (Break flag set) then
     BEGIN
       restore registers from stack
       do RTE  (* ignore all keys except break *)
     END
   else
     BEGIN
       if (Front=Rear) and (NOT Buffer_empty) then Buffer_full := true
                                              else Buffer_full := false;

       if (key closure (bit D7 of keycode = 1)) then
           BEGIN
           (* process key closure keycode *)

             clear keycode bit D7
             check Qualifiers flag word
             if (Shift bit flag set) then
               BEGIN
                use SHIFT TABLE to get Table_code.
                Index table by Keycode.
                Table_code := SHIFT_TABLE[ Keycode ];
               END
             else BEGIN
                use REGULAR TABLE to get Table_code.
                Index by Keycode.
                Table_code := REGULAR_TABLE[ Keycode ];
               END;

             CASE Table_code OF
               ($9D) : This is esc # multiple character code sequence key.
                       Search ESCAPE # SEQUENCE TABLE by Keycode. Find table
                       entry, there will always be one.
                    ___
                   |   Select correct Character_pair from table by using the
                   |    FLAG byte of the table entry and the Qualifier flag
                   |    word.
                   |
 This must be a    |   if (Command & Shift flags set in Qualifier flag word) then
 subroutine for    |      Character_pair := Command/Shift character pair;
 use by the key----|   else if ( Command flag in Qualifier word is set) then
 closure rtn       |           Character_pair := Command character pair.
 also.             |        else if (Shift flag in Qualifier word is set) then
                   |                  Character_pair := Shift character pair.
                   |             else Character_pair := Unshifted character pair;
                   |____              (* always ok to use Unshifted pair *)

                       Put esc # plus Character_pair in buffer and New.key_seq
                         in the primary state record.
                       #_chars_put := 4;  <--- must be in a register

                           | to place chars in buffer :
                           |
                           | Old_rear := rear;
                           | call PutCharsInQueue (#_chars_put,#_chars_did);
                           |      (* call put #_chars_put times or until *)
    ***********************|      (* Buffer_full return.  returns the # of *)
                           |      (* chars actually put in buffer.  *)
                           | if (#_chars_put <> #_chars_did) then
                           |    Rear := Old_rear; (* reset pointer and
                           |                         ignore the characters *)

                       if (successfully put all chars in buffer) then
                           BEGIN
                             New.length (*in primary state record*) := #_chars_put;
                             New.new_change := true; <-- tell slave new repeat sequence
                             Save Keycode in Previous_keycode.
                             Wait := 0;  {Reset timer}
                             clear Buffer_empty flag.
                           END;

               ($9E) : This is a STANDARD multiple character code sequence
                       key.
                       Search STANDARD MULTIPLE CHARACTER SEQUENCE TABLE by
                       keycode for keycode.
                       Put character string in buffer and New.key_seq in the
                          primary state record.
                       Update buffer pointers.
                       #_chars_put := string length from table entry.

                       if (successfully put all chars in buffer) then
                           BEGIN
                             New.length (*in primary state record*) := #_chars_put;
                             New.new_change := true; <-- tell slave new repeat sequence
                             Save Keycode in Previous_keycode.
                             Wait := 0;  {Reset timer}
                             clear Buffer_empty flag.
                           END;

               ($9F) : This is a QUALIFIER Keycode.
                       Get FLAG byte from CAPS_QUALIFIER TABLE.
                       Index by Keycode.
                       Flag := (CAPS_QUALIFIER_TABLE[Keycode]) & $7F;
                         (* remove D7 - caps lock flag bit *)
                       if (Flag <> 0) then (* this is a qualifier with a flag *)
                         begin
                           Find Qualifier bit that is set.
                           Set corresponding bit in Qualifier byte.
                           if (Fast) then set Fast bit in Shared word flag.
                           if (Qualifier has ESC # Seq flag is set) then begin
                              Get ESC # sequence.
                              put it in the buffer.
                              if (put it in the buffer) then begin
                                 update buffer pointers.
                                 Wait := 0;  {Reset timer}
                                 clear Buffer_empty flag.
                              end;
                       end else ignore Keycode.
                       #_chars_put := 0;

               Otherwise: The Table_code is the character code for a SINGLE
                          character code key.
                          check for control and caps lock.
                          #_chars_put := 1;
                          if ( (Control flag is set) AND (Char < $3F - a "?") then begin
                             clear bits D7,D6 & D5 of the Table_code.
                             if (ALTERNATE character set flag is clear) then begin
                               if Changed Table_code = control-Q ($11) then begin
                                 clear Stop_Go flag in the Shared CRT/KBRD
                                   flag byte
                                 #_chars_put := 0;
                               end;
                               if Changed Table_code = control-S ($13) then begin
                                 set Stop_Go flag in the Shared CRT/KBRD
                                   flag byte
                                 #_chars_put := 0;
                               end;
                             end;
                          end else
                             if (Caps_lock flag is set) AND
                                (CAPS_QUALIFIER_FLAG_TABLE[Keycode] bit D7 is set )then
                                      Table_code := SHIFT_TABLE [Keycode];

                          if #_chars_put <> 0 then begin  { if not control S or Q}
                            if (ALTERNATE character set flag is set) then
                               set bit D7 of character code (Table_code).
                            Put character code (Table_code) in buffer and
                              New.key_seq in primary state record.
                            Update buffer pointers.

                            if (successfully put all chars in buffer) then
                                BEGIN
                                  New.length (*in primary state record*) := #_chars_put;
                                  New.new_change := true; <-- tell slave new repeat sequence
                                  Save Keycode in Previous_keycode.
                                  Wait := 0;  {Reset timer}
                                  clear Buffer_empty flag.
                                END;
                          end;

             END CASE;
           ENDTHEN;
  end of |
  closure|---------------------------------
  code   |
       else
       (* process key release *)
           BEGIN
             if (Keycode = Previous_keycode ) then begin
                Previous_keycode := $FF <--- the null keycode
                New.length (*in primary state record*) := 0; <-- null key seq
                New.new_change := true;
               end;

             if (Previous_keycode = $FF) then Wait := 0;    {reset timer}

             Search RELEASE TABLE. each entry is 2 bytes.
             1st byte = keycode; 2nd byte = action code.
             Not all keycodes are in the table.
             (* release key search - table is sorted by keycode in ascending *)
             (* order.  End of table is a null keycode of $FF, greater than  *)
             (* any real keycode. *)

             i := 0;
             while (RELEASE_TABLE[i] < Keycode) do i := i+2;

             if (RELEASE_TABLE[i] = Keycode) then (* found an entry in the release table *)
               BEGIN
                 Actioncode := RELEASE_TABLE[i+1];
                 CASE Actioncode OF
                   ($9E) : This is a qualifier. So, clear qualifier flag.
                           Get FLAG byte from CAPS_QUALIFIER TABLE.
                           Index by Keycode.
                           Flag := (CAPS_QUALIFIER_TABLE[Keycode]) & $7F;
                             (* remove D7 - caps lock flag bit *)
                           if (Flag <> 0) then (* this is a qualifier with a flag *)
                             begin
                               Find Qualifier bit that is set.
                               Clear corresponding bit in Qualifier byte.
                               if (Fast) then clear Fast bit in Shared word flag.
                               if (Qualifier has ESC # Seq flag is set) then begin
                                   do as a subroutine the $9D action code case.
                           end else ignore Keycode.

                   ($9D) : This is a release key esc # sequence keycode.
                         __Set bit D7 of keycode.
  same character code   |  Find character code sequence to follow esc # codes
  definition as ($9D)   |   by search ESCAPE # SEQ TABLE.
  section of closure----|  use qualifiers to determine which element of entry
  CASE Table_code.      |___to use.
                           put esc # plus character pair in buffer & update
                            pointers
                           clear Buffer_empty flag. (* don't reset timer or save keycode*)

                   Otherwise : The Actioncode is a character code.
                               Put character in buffer & update ptrs.
                               clear Buffer_empty flag.
                 ENDCASE
               END;
           ENDELSE;
         END;

       copy primary state record into secondary state record.
EXIT : restore registers.  *****************************************EXIT
       do RTE. <--- normal return from keyboard interrupt

---------------------------------------------------------------------------
 (* END OF KEYBOARD INTERRUPT SERVICE ROUTINE *)
---------------------------------------------------------------------------

{$P}
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&

---------------------------------------------------------------------------
KEYBOARD TIMER INTERRUPT SERVICE ROUTINE
---------------------------------------------------------------------------

 if Wait <> $FFFF then Wait := Wait + 1;
 Repeat := true;  {show timer ticked}
 do RTE.

 -------------------------------------------------------------------
 END OF TIMER INTERRUPT KEYBOARD REPEAT SERVICE ROUTINE DESIGN
 -------------------------------------------------------------------

&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&

 -------------------------------------------------------------------
 UNITREAD routine. pseudo-code design.
 -------------------------------------------------------------------

 (* unitread uses the secondary state record only. If interrupted by master *)
 (* process (the interrupt handler) then the unitread process will restart. *)

 For i:= 1 to (number of characters user wants) do begin

  repeat
  LOOP: Had_interrupt := false; (* if there was an interrupt ignore it *)
        setup Primary and Secondary state record Base pointers.
          test PTRBIT. if set then record A is primary.
                              else record B is primary.
        copy Primary state record into Secondary state record.

        if (cont_rpt) then use_key_seq := true (* in middle of repeat seq *)
        else if (Buffer_empty) then (* test for repeat *)
                if (Not Repeat) then GOTO LOOP <-- no chars & no repeat
                                                   so wait.
                else begin
                  Repeat := false;
                  if (Not Fast) then if (Wait<5) then GOTO LOOP
                   else BEGIN  (* start a repeat sequence *)

                     if (New.new_change) then begin (*use new key seq *)
                        New.new_change := false;
                        Current.length := New.length;
                        Current.key_seq := New.key_seq;
                     end;

                     if (Current.length=0) then GOTO LOOP; <--- only null repeat
                                                                sequence available
                     cont_rpt := true; (* starting new repeat sequence *)
                     use_key_seq := true;
                     Current.index := 1; (* start at first character *)
                     if (Fast) then rpt_cnt := 4
                               else rpt_cnt := 1;
                   END;
                  end;
             else begin
               use_key_seq := false; (* use chars in buffer *)
               Repeat := false;
             end;

        if (use_key_seq) then BEGIN
           Next_char := Current.key_seq[ Current.index ];
           Current.index := Current.index + 1;
           if (Current.index > Current.length) then BEGIN
              rpt_cnt := rpt_cnt - 1;
              if (rpt_cnt=0) then cont_rpt := false (* done with repeat *)
                             else Current.index := 1; (* restart key seq *)
             END;
        END else BEGIN
           Next_char := character pointed at by Front; (* next char in buffer *)
           Update pointer :  Front := Front + 1;
           if (Front > EndBuffer) then Front := @Buffer;
           if (Front=Rear) then Buffer_empty := true;
         END;

       if (NOT Had_interrupt) then FLIP:
          (* flip procedure *) if (PTRBIT set) then PTRBIT := false <- clear
                                               else PTRBIT := true; <- set
          (* if (PTRBIT is set) then state record A is primary, *)
          (*                    else state record B is primary. *)

  until (NOT Had_interrupt);  (* restart because of interrupt *)

  put Next_char in user buffer.
  if (Error flag set) then begin
      IORESULT := 'KBRD data transmission error';
      clear Error flag.
    end;
 END of for loop;

------------------------------------------------------------------
END of unitread design.
------------------------------------------------------------------
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&

---------------------------------------------------------------------------
pseudo code for UNITBUSY, UNITCLEAR, UNITSTATUS, UNITINSTALL &  UNITUNMOUNT
---------------------------------------------------------------------------

(* unitbusy *)

repeat
  Had_interrupt := false;
  get address of state records.
  copy Primary state record into secondary state record;
  if (Fast flag) then Waited := (Wait>0)  {only need to wait one tick if fast}
                 else Waited := (Wait>=5);
  New_Repeat character available := (Repeat flag) and (Waited) and (Buffer_empty flag)
                            and (New.Length<>0) and (NOT Continue repeat flag);
  if (New_Repeat character available) then begin
     ReturnFlag := true;
     {put repeat sequence in buffer so guarantee unitbusy}
      Use primary state record (register A4).
      Get Rear pointer into register A3.
      Get Front pointer into register A2.
      Get address of repeat chars in A6.
      Get # of characters in repeat sequence in D3.
       if (New.New_change) then begin
         (D3) := New.length;
         (A6) := @New.repeat_sequence;
       end else begin
         (D3) := Current.length;
         (A6) := @Current.repeat_sequence;
       end;
      if (Fast flag) then #_sequences := 4
                     else #_sequences := 1;
      Disable Keyboard interrupt. { ORI.W  #$0700,SR }
      if (NOT Had_interrupt) then begin
        Buffer_full := false;   {necessary for PUTCHRS routine}
        Closure := false;   {so BUFUPDAT does not reset Wait timer}
        for i := 1 to #_sequences do begin
           {will not have buffer full error}
          call PUTCHRS routine to put characters in buffer.
           {PUTCHRS saves A6 & D3}
          Update state record (call BUFUPDAT).
        end;
        Enable interrupts.  { ANDI.W  #$F500,SR  }
        EXIT <-- do RTS & return ReturnFlag.
      end;
      Enable interrupts.  { ANDI.W  #$F500,SR  }
  end else ReturnFlag := (NOT Buffer_empty flag) or (Continue repeat flag);
until (NOT Had_interrupt);
Return to caller ( ReturnFlag );

(* unitclear *)

repeat
  Had_interrupt := false;
  get Base pointers to the primary and secondary state records.
  copy into the secondary state record the initial values for the state
    records.
  FLIP: --> if PTRBIT then PTRBIT := false
                      else PTRBIT := true;
until (NOT Had_interrupt);
return;

(* unitstatus *)

repeat
  Had_interrupt := false;
  get the values of the keyboard's UART's Status, Command, & Control registers.
  put the 3 bytes into a long word's least significant 3 bytes.
  store the long word in the user's buffer.
until (NOT Had_interrupt);

Return to caller ( long word );

(* unitinstall *)

(* reset the keyboard *)

turn off keyboard interrupt and transmission by setting the UART Command Register
  to $02.   (* call Unitunmount *)
copy initial state record into primary state record. (* call Unitclear *)
clear interrupt handler Internal Flag word, which includes Qualifier byte.
clear Shared Flags word and CRT/KBRD Shared Memory byte.
put addresses of Keyboard Interrupt Service Routine's Tables into pointer
 table.   The following tables :
            SHIFT TABLE
            REGULAR TABLE
            ESCAPE # TABLE
            STANDARD MULTIPLE CHARACTER TABLE
            CAPS LOCK & QUALIFIER FLAG TABLE
            RELEASE TABLE
            BREAK KEYCODE TABLE

create a Timer Table Entry for the Keyboard Timer Interrupt Service Routine.
 find unit number of Timer driver.
 if (did not find Timer driver in device table) then ERROR <-- no repeat
 else begin
   Call UnitStatus((timer unit #),buffer_address,control);
     control = 1 for Create procedure.
     buffer_address := @parameterBlock
       parameterBlock has fields :
         address = pointer to Keyboard Timer Interrupt Service Routine (long word).
         count = 2 for 100 milliseconds between calls (word).
         flags = 0 for call every time and continuous mode (word).
         tableID = don't care-Output value (word).
     if (IORESULT <> 0) then ERROR <-- no repeat
 end;
setup KBRD UART Control Register for communication type and baud rate (600 baud).
 (* write a $17 *)
force a Break transmit to reset the Keyboard hardware.  Put a $0D in the
  command register.
turn on keyboard transmission by setting the UART Command Register to $09.

(* unitunmount *)

turn off keyboard interrupt and transmission by setting the UART Command Register
  to $02.
delete the Timer Table Entry for the Keyboard Timer Interrupt Service Routine.
 find unit number of Timer driver.
 if (did not find Timer driver in device table) then ERROR <-- ignore.
 else begin
   Call UnitStatus((timer unit #),buffer_address,control);
     control = 2 for Delete procedure.
     buffer_address := @parameterBlock
       parameterBlock has fields :
         tableID = tableID value returned by Create procedure in it's parameter block (word).
     if (IORESULT <> 0) then ERROR <-- ignore
 end;
set the Keyboard interrupt vector to point at a RTE instruction.
--------------------------------------------------------------------------------
END of pseudo code for UNITBUSY, UNITCLEAR, UNITSTATUS, UNITINSTALL &  UNITUNMOUNT
--------------------------------------------------------------------------------

{$P}
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&


 -------------------------------------------------------------------
 Tables for kbrd routines

     - tables are constants at run time.
     - tables are pre-sorted and their values organized in ascending
       order by keycode (or other key field).
 -------------------------------------------------------------------

 $$$$$$$$$$ BREAK KEY CODE TABLE $$$$$$$$$$$$$

 This table consists of one byte.  It is the Keycode for the key which performs
 the start/stop toggle.  The value for the version 00 keyboard is : $5F.
 This is the Keycode for BREAK closure.

 $$$$$$$$$$ CAPS LOCK & QUALIFIER FLAG TABLE $$$$$$$$$$$$$

 One single byte entry for each Keycode.  The Keycode is a direct index into
 the table.  Each byte is a set of possible flags.  All unused bits are
 cleared (value = 0).  The high order bit is always the Caps lock flag for
 the corresponding Keycode.  The remaining bits are possible qualifier flags.
 Only one of these bits can be set at a time.

 The bits currently defined are :
              D7 - Caps lock flag : when set means this keycode generates a
                   shifted character when the Caps lock qualifier flag is set.
              D6 - Qualifier has an ESC # sequence flag.  When set then must process
                   the keycode as a non-repeating ESC # sequence. Also has a Release
                   sequence.
                    ______________
              D5 - Command        |
              D4 - Alternate      |
              D3 - Fast           |      This bit says which type of Qualifier
              D2 - Caps lock      |_____ key the Keycode represents.
              D1 - Control        |
              D0 - Shift  ________|

 The values for the version 00 keyboard is external to this document; see
 the table sheet for it.

 $$$$$$$$$$ INTERNAL FLAG WORD $$$$$$$$$$$$$

 16 Bits available.  All unused bits are 0.  The low order byte is the Qualifier
flag byte.  The high order byte is other miscellaneous internal flags.
5 bits defined :

  Qualifier byte:

      D0 = Shift flag
      D1 = Control flag
      D2 = Caps_lock flag
      D3 = Fast flag
      D4 = Alternate character set flag
      D5 = Command flag

 Other internal flags:

      D8 = BREAK flag - used as start/stop toggle indicator.
      D9 = Buffer_full flag
      D10 = Closure flag - set means processing a key closure
                           clear means processing a key release

      D6 -D7  = unused (clear to 0)
      D10-D15 = unused (clear to 0)

 The Alternate character set flag is used to create the 8 bit character codes.

 Initial value for all bits of the Internal flag word is clear (0).

 $$$$$$$$$$ STANDARD MULTIPLE CHARACTER SEQUENCE TABLE $$$$$$$$$$$$$

 Each entry is composed of 3 fields. 1) the key field is the keycode.
 2) the string length and 3) the string, a variable length field.
 The string is the sequence of character codes placed in the buffer for
 this key.
 The value of $9E as a Table_code signifies to use this table.  Every entry
 with a $9E Table_code must be in this table.

 Search algorithm:

 i := 0;
 while ( (Keycode <> SMCS_Table[i]) & (SMCS_Table[i]<>$FF) ) do
   i := i + 2 + SMCS_Table[i+1];
 if (SMCS_Table[i] = $FF) then ERROR-value not in table
                          else i points to SMCS Table entry.

 Values for the version 00 keyboard (programmers) :

 KEYCODES   |   STRING LENGTH | STRING
 -----------+-----------------+ ---------------------------------------
 $00 (cursor|        2        |  $1B $42 (esc B)
      down) |                 |
 $03 (cursor|        2        |  $1B $43 (esc C)
      right)|                 |
 $05 (clear |        2        |  $1B $4B (esc K)
      line) |                 |
 $07 (enter)|        2        |  $1B $64 (esc d)
            |                 |
 $08 (cursor|        2        |  $1B $44 (esc D)
      left) |                 |
 $0B (cursor|        2        |  $1B $41 (esc A)
      up)   |                 |
 $3A (back  |        2        |  $1B $69 (esc i)
      tab)  |                 |
 $5D (home  |        2        |  $1B $48 (esc H)
      up)   |                 |
 $5E (clear |        2        |  $1B $49 (esc J)
     screen)|                 |
 $FF        |        0        |  END OF THE TABLE
------------------------------------------------------------------------
{$P}
$$$$$$$$$$ RELEASE TABLE $$$$$$$$$$$$$

This table specifies which keycodes have an action on key release. Each table
has 2 fields. 1) the key field which is the keycode; 2) the action code.

The action code has 3 different value types. First, $9D this specifies a key
with a ESCAPE # SEQUENCE TABLE entry to be put in the buffer (must set bit
D7 of keycode before search). Second, $9E this specifies a qualifier keycode.
Any other action code is a character code to be placed in the buffer. The end
of the table is specified by a NULL keycode of $FF.  The value of the NULL
keycode is not generated by the keyboard.

 Values for the version 00 keyboard (programmers) :

 __________________________________________________
| KEYCODE | ACTION CODE |  KEY NAME
|---------+-------------+--------------------------
|   $1F   |    $9E      |  Right shift
|   $3C   |    $9E      |  Caps lock
|   $3E   |    $9E      |  Left shift
|   $48   |    $9E      |  Control
|   $49   |    $9E      |  Fast
|   $4A   |    $9E      |  Alternate
|   $4C   |    $9E      |  Right command
|   $FF   |    $00      |  NULL keycode - END OF TABLE
|_________|_____________|____________________________

{$P}
$$$$$$$$$$ ESCAPE # SEQUENCE TABLE $$$$$$$$$$$$$

This table is used when a Table_code of $9D is found in key closure or a
Action code of $9D is found in key release.  It specifies a key which has a
ESC # AH AH multiple character sequence.  AH stands for ASCII Hex, which is a
representation of hexidecimal digits in ASCII (0-9 & A-F, capitals only).
Each keycode may have a different ASCII Hex character pair when the Qualifier
keys change (shift and control).

Each table entry has the form (entry length = 10 bytes) :
    1) The key search field - Keycode. (1 byte)
    2) filler byte : it's value is 0 (zero).  It is used to keep each entrytion.
                     on a word boundary.
    3) The 4 ASCII Hex character pairs in priority order.
        a) Command & Shift together (2 bytes)
        b) Command only (2 bytes)
        c) Shift only (2 bytes)
        d) Unshifted & Uncommanded (2 bytes)

If a Qualifier state is not allowed then use the ASCII Hex character pair of
the next lowest priority (a is highest and d is lowest) to fill in that
character pair.

 Values for the version 00 keyboard (programmers) :

 __________________________________________________________________
| KEYCODE | NULL | US/UC  | S only | C only | C/S | KEY NAME
|---------+------+--------+--------+--------+-----+----------------
|   $20   |  00  |   00   |   0A   |   14   | 1E  | Function key 1
|   $21   |  00  |   01   |   0B   |   15   | 1F  | Function key 2
|   $22   |  00  |   02   |   0C   |   16   | 20  | Function key 3
|   $23   |  00  |   03   |   0D   |   17   | 21  | Function key 4
|   $24   |  00  |   04   |   0E   |   18   | 22  | Function key 5
|   $4C   |  00  |   FF   |   FF   |   FF   | FF  | Right command (closure)
|   $58   |  00  |   05   |   0F   |   19   | 23  | Function key 6
|   $59   |  00  |   06   |   10   |   1A   | 24  | Function key 7
|   $5A   |  00  |   07   |   11   |   1B   | 25  | Function key 8
|   $5B   |  00  |   08   |   12   |   1C   | 26  | Function key 9
|   $5C   |  00  |   09   |   13   |   1D   | 27  | Function key 10
|   $CC   |  00  |   FE   |   FE   |   FE   | FE  | Right command (release)
|_________|______|________|________|________|_____|________________
  Search algorithm:
  i := 0;
  while ( (Keycode <> ESC#_Table[i]) & (ESC#_Table[i] <> $FF) ) do
    i := i + 10;
  if (ESC#_Table[i] = $FF) then ERROR-value not in table
                           else i points to ESC# Table entry.

$$$$$$$$$$ SHARED FLAG BYTE BETWEEN CRT DRIVER AND KEYBOARD DRIVER $$$$$$$$$$$$$

This single byte allows the Keyboard to communicate with the CRT.  The flags
are used by the CRT to control the display.  These flags are set and cleared by
the Keyboard interrupt service routine depending on user input.

Currently, 1 bit is defined.  All undefined bits are 0.

Defined Bits :

     STOP_GO flag : bit D7  where,
            1 means stop. It is set when a control-S ($13) character code is
            detected.  The character is not placed in the buffer.
            0 means go.  It is cleared when a control-Q ($11) character is
            detected.  The character is not placed in the buffer.

$$$$$$$$$$ TRANSLATION TABLE $$$$$$$$$$$$$

Each entry in this table is a pointer to a translation table.  There are 7
entries.  They are pointers to the following tables :
      1) SHIFT_TABLE
      2) REGULAR_TABLE
      3) CAPS_QUALIFIER FLAG TABLE
      4) ESCAPE # SEQUENCE TABLE
      5) STANDARD MULTIPLE CHARACTER SEQUENCE TABLE
      6) RELEASE TABLE
      7) BREAK KEYCODE TABLE

The entries must also be in the above order.

The address of this table is kept in the Systems Communications Area.
$$$$$$$$$$ STATE RECORDS $$$$$$$$$$$$$

This data structure is used by the interrupt handler and the unitread processes.
It allows the unitread process to restart it's operations on the sharred
memory when the interrupt handler process (the master) interrupts it.

A state records can be either the primary or secondary state record.  During
processing of the shared memory (mainly the buffer & repeat key sequence)
the records will swap their state of which one is primary and which is
secondary.  The flag PTRBIT tells the software which of the two state records
is currently the primary record.  If PTRBIT is set then record A is the primary
state record, else PTRBIT is clear and record B is the primary state record.

A state record has the form :
  1) Front : pointer to first byte in circular queue buffer.
  2) Rear : ponter to last byte in circular queue buffer.
  3) Buffer_empty : bit flag - set means circular queue buffer is empty.
  4) New : a record of the form :
      a) length : unsigned integer byte describing the length of the new repeat
                  key sequence.  length = 0 means null key sequence.
      b) key_seq : packed array [1..10] of char, the new repeat key sequence.
      c) new_change : bit flag - set means interrupt handler placed a new
                                 key sequence in the array New.key_seq.
  5) Current : a record of the form :
      a) length : Length of current repeat key sequence. A length of 0 means
                  null key sequence.
      b) index : index into current key sequence when processing a repeat
                 in unitread.
      c) key_seq : the current key sequence. (same as New)
      d) rpt_cnt : Unsigned integer byte stating the current number of repeat
                   key sequences to be used while unitread processes a repeat.
      e) cont_rpt : bit flag - set means in the middle of processing a repeat
                    in the unitread process.


There are two state records. They are A and B.

Initial values for the state records at cold or warm start or for UNITCLEAR are :

 1) Front := address of first byte in buffer space
 2) Rear := Front (the buffer is empty)
 3) Buffer_empty := true (set)
 4) New.length := 0 (null key sequence)
 5) New.key_seq := don't care
 6) New.new_change := false (clear)
 7) Current.length := 0 (null key sequence)
 8) Current.index := 0
 9) Current.key_seq := don't care
 10) Current.rpt_seq := 0
 11) Current.cont_rpt := false

The other flags shared by the three processes are not in the state record
They are in the Shared flags word.  All unused bits are clear (= to 0).
The bits defined are :
   D0 = Had_interrupt flag - tells keyboard unit I/O if interrupt handler
                             has interrupted.
   D2 = Repeat flag - when set shows timer has ticked.
   D3 = Fast flag - a exact copy of the interrupt handler's Fast flag in the
                    Qualifier word.
   D4 = PTRBIT - points to primary state record. Set means A is primary.
   D5 = Error flag - Data transmission error from Keyboard to Computer when
                     set.

Initial values for Shared flags word is :
 D15-D6 = 0 (unused)
     D5 = false (clear)
     D4 = set (A is primary)
     D3 = false (clear)
     D2 = false (clear)
     D1 = 0 (unused)
     D0 = false (clear)

&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&

---------------------------------------------------------------------------
The BUFFER
---------------------------------------------------------------------------

The buffer is a sequence of bytes used as a circular queue.  It has two
pointers, Front & Rear, which tell where to get characters from and where to
put characters into the buffer.

A Pascal-like program fragment to describe the buffer data structure.

DataStructure Circular_Queue;
 (* the circular queue is the organization and access to the buffer *)

 const N = (length of the buffer in bytes);

 var Buffer : array[0..(N-1)] of Byte;
     a : Byte;
     Front,Rear,EndBuffer : pointer to Byte;
     Buffer_empty,Buffer_full : Boolean;

INIT Section;
  EndBuffer := @Buffer + (N-1); (* address of last byte of Buffer *)
  Front := @Buffer;
  Rear := @Buffer;
  Buffer_empty := true;
  Buffer_full := false;

To GET a character from the Circular Queue;

  if (Buffer_empty) then (no characters to get)  ERROR
  else begin
     a := ^Front;
     Front := Front + 1;
     if (Front > EndBuffer) then Front := @Buffer;
     if (Front = Rear) then Buffer_empty := true;
   end;

To PUT a character into the Circular Queue;

  if (Buffer_full) then (no more space) ERROR
  else begin
     ^Rear := a;
     Rear := Rear + 1;
     if (Rear > EndBuffer) then Rear := @Buffer;
     if (Front = Rear) then Buffer_full := true;
   end;

END DataStructure.

