/joh'liks/ n.,adj. 386BSD

Porting Unix to the 386: A Practical Approach



William & Lynne Jolitz


Since we had a program running in an empty PC, we needed primative input with a keyboard driver for polled input. Used by standalone programs to boot and test parts of the kernel.




Standalone Keyboard Driver
Listing Four outlines code for a simple driver, which extracts ASCII characters from the PC keyboard on demand by grabbing display codes from the 8042 keyboard interface, consulting a table of actions for the given key press display code, and returning the appropriate value out of the key table. It does not even bother to initialize the keyboard controller, since we know that MS-DOS already did that for us before we loaded the program with our absolute loader.

Listing 4: Standalone Keyboard Driver
/* kbd.c: Copyright (c) 1989, 1990 William Jolitz. All rights reserved.
 * Written by William Jolitz 9/89
 * Redistribution and use in source and binary forms are freely permitted
 * provided that the above copyright notice and attribution and date of work
 * and this paragraph are duplicated in all such forms.
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 * Standalone driver for IBM PC keyboards.
 */

#define   L      0x001   /* locking function */
#define   SHF      0x002   /* keyboard shift */
#define   ALT      0x004   /* alternate shift -- alternate chars */
#define   NUM      0x008   /* numeric shift  cursors vs. numeric */
#define   CTL      0x010   /* control shift  -- allows ctl function */
#define   CPS      0x020   /* caps shift -- swaps case of letter */
#define   ASCII      0x040   /* ascii code for this key */
#define   STP      0x080   /* stop output */
#define   BREAK      0x100   /* key breaking contact */

typedef unsigned char u_char;

u_char inb();

u_char action[] = {
0,     ASCII, ASCII, ASCII, ASCII, ASCII, ASCII, ASCII,    /* scan  0- 7 */
ASCII, ASCII, ASCII, ASCII, ASCII, ASCII, ASCII, ASCII,    /* scan  8-15 */
ASCII, ASCII, ASCII, ASCII, ASCII, ASCII, ASCII, ASCII,    /* scan 16-23 */
ASCII, ASCII, ASCII, ASCII, ASCII,   CTL, ASCII, ASCII,    /* scan 24-31 */
ASCII, ASCII, ASCII, ASCII, ASCII, ASCII, ASCII, ASCII,    /* scan 32-39 */
ASCII, ASCII, SHF  , ASCII, ASCII, ASCII, ASCII, ASCII,    /* scan 40-47 */
ASCII, ASCII, ASCII, ASCII, ASCII, ASCII,  SHF,  ASCII,    /* scan 48-55 */
  ALT, ASCII, CPS|L,     0,     0, ASCII,     0,     0,    /* scan 56-63 */
    0,     0,     0,     0,     0, NUM|L, STP|L, ASCII,    /* scan 64-71 */
ASCII, ASCII, ASCII, ASCII, ASCII, ASCII, ASCII, ASCII,    /* scan 72-79 */
ASCII, ASCII, ASCII, ASCII,     0,     0,     0,     0,    /* scan 80-87 */
0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,   } ;

u_char unshift[] = {   /* no shift */
0,     033  , '1'  , '2'  , '3'  , '4'  , '5'  , '6'  ,    /* scan  0- 7 */
'7'  , '8'  , '9'  , '0'  , '-'  , '='  , 0177 ,'\t'  ,    /* scan  8-15 */

'q'  , 'w'  , 'e'  , 'r'  , 't'  , 'y'  , 'u'  , 'i'  ,    /* scan 16-23 */
'o'  , 'p'  , '['  , ']'  , '\r' , CTL  , 'a'  , 's'  ,    /* scan 24-31 */

'd'  , 'f'  , 'g'  , 'h'  , 'j'  , 'k'  , 'l'  , ';'  ,  /* scan 32-39 */
'\'' , '`'  , SHF  , '\\' , 'z'  , 'x'  , 'c'  , 'v'  ,    /* scan 40-47 */

'b'  , 'n'  , 'm'  , ','  , '.'  , '/'  , SHF  ,   '*',    /* scan 48-55 */
ALT  , ' '  , CPS|L,     0,     0, ' '  ,     0,     0,    /* scan 56-63 */

    0,     0,     0,     0,     0, NUM|L, STP|L,   '7',    /* scan 64-71 */
  '8',   '9',   '-',   '4',   '5',   '6',   '+',   '1',    /* scan 72-79 */

  '2',   '3',   '0',   '.',     0,     0,     0,     0,    /* scan 80-87 */
0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,   } ;

u_char shift[] = {   /* shift shift */
0,     033  , '!'  , '@'  , '#'  , '$'  , '%'  , '^'  ,    /* scan  0- 7 */
'&'  , '*'  , '('  , ')'  , '_'  , '+'  , 0177 ,'\t'  ,    /* scan  8-15 */
'Q'  , 'W'  , 'E'  , 'R'  , 'T'  , 'Y'  , 'U'  , 'I'  ,    /* scan 16-23 */
'O'  , 'P'  , '['  , ']'  , '\r' , CTL  , 'A'  , 'S'  ,    /* scan 24-31 */
'D'  , 'F'  , 'G'  , 'H'  , 'J'  , 'K'  , 'L'  , ':'  ,    /* scan 32-39 */
'"'  , '~'  , SHF  , '|'  , 'Z'  , 'X'  , 'C'  , 'V'  ,    /* scan 40-47 */
'B'  , 'N'  , 'M'  , '<'  , '>'  , '?'  , SHF  ,   '*',    /* scan 48-55 */
ALT  , ' '  , CPS|L,     0,     0, ' '  ,     0,     0,    /* scan 56-63 */
    0,     0,     0,     0,     0, NUM|L, STP|L,   '7',    /* scan 64-71 */
  '8',   '9',   '-',   '4',   '5',   '6',   '+',   '1',    /* scan 72-79 */
  '2',   '3',   '0',   '.',     0,     0,     0,     0,    /* scan 80-87 */
0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,   } ;

u_char ctl[] = {   /* CTL shift */
0,     033  , '!'  , 000  , '#'  , '$'  , '%'  , 036  ,    /* scan  0- 7 */
'&'  , '*'  , '('  , ')'  , 037  , '+'  , 034  ,'\177',    /* scan  8-15 */
021  , 027  , 005  , 022  , 024  , 031  , 025  , 011  ,    /* scan 16-23 */
017  , 020  , 033  , 035  , '\r' , CTL  , 001  , 013  ,    /* scan 24-31 */
004  , 006  , 007  , 010  , 012  , 013  , 014  , ';'  ,    /* scan 32-39 */
'\'' , '`'  , SHF  , 034  , 032  , 030  , 003  , 026  ,    /* scan 40-47 */
002  , 016  , 015  , '<'  , '>'  , '?'  , SHF  ,   '*',    /* scan 48-55 */
ALT  , ' '  , CPS|L,     0,     0, ' '  ,     0,     0,    /* scan 56-63 */
CPS|L,     0,     0,     0,     0,     0,     0,     0,    /* scan 64-71 */
    0,     0,     0,     0,     0,     0,     0,     0,    /* scan 72-79 */
    0,     0,     0,     0,     0,     0,     0,     0,    /* scan 80-87 */
    0,     0,   033, '7'  , '4'  , '1'  ,     0, NUM|L,    /* scan 88-95 */
'8'  , '5'  , '2'  ,     0, STP|L, '9'  , '6'  , '3'  ,    /*scan  96-103*/
'.'  ,     0, '*'  , '-'  , '+'  ,     0,     0,     0,    /*scan 104-111*/
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,   } ;

#define   KBSTATP      0x64      /* kbd status port */
#define   KBS_RDY      0x02      /* kbd char ready */
#define   KBDATAP      0x60      /* kbd data port */
#define   KBSTATUSPORT   0x61      /* kbd status */
#define   KBD_BRK   0x80 /* key is breaking contact, not making contact */
#define   KBD_KEY(s)   ((s) & 0x7f)   /* key that has changed */

/* Return an ASCII character from the keyboard.  */
u_char kbd() {
   u_char dt, act;
   static u_char odt, shfts, ctls, alts, caps, num, stp;

   do {
      do {
         while (inb(KBSTATP)&KBS_RDY) ;
         dt = inb(KBDATAP);
      } while (dt == odt);

      odt = dt;
      dt = KBD_KEY(dt);
      act = action[dt];
      if (odt & KBD_BRK) act |= BREAK;

      /* kinds of shift keys */
      if (act&SHF) actl (act, &shfts);
      if (act&ALT) actl (act, &alts);
      if (act&NUM) actl (act, &num);
      if (act&CTL) actl (act, &ctls);
      if (act&CPS) actl (act, &caps);
      if (act&STP) actl (act, &stp);

      if (act&(ASCII|BREAK) == ASCII) {
         u_char chr;

         if (shfts)
             chr = shift[dt] ;
         else {
            if (ctls) chr = ctl[dt] ;
            else chr = unshift[dt] ;
         }
         if (caps && (chr >= 'a' && chr <= 'z'))
            chr -= 'a' - 'A' ;
         return(chr);
      }
   } while (1);
}

/* Handle shift key actions */
actl(act, v, brk) char *v; {

   /* are we locking ... */
   if (act&L) {
      if((act&BREAK) == 0) *v ^= 1;

   /* ... or single - action ? */
   } else
      if(act&BREAK) *v = 0; else *v = 1;
}
This is the first place where we are hit with variations in PC keyboard interfaces, all of which are hidden from applications programs and MS-DOS by appropriate BIOS ROM drivers. It is possible to dance back and forth between real and protected mode (thankfully made easier on the 386 than was the case on the 286), "translating" the BIOS calls into BSD standalone driver requests. This method was intended for the PC, if one examines its real-mode ancestry, and also addresses a nest of manufacturer idiosyncrasies. However, it goes against the grain of our project in three basic ways:
  • performance degrades in getting away from direct interaction with the hardware;
  • incompatibility with previous BSD systems develops; and
  • implementation becomes a bigger project than the port itself.
In addition, it perpetuates the intertwining of MS-DOS and UNIX to the point where it becomes a significant future liability. To resolve this dilemma, we must choose to support the "raw" machine in its entirety, with the result that undocumented or "secret" proprietary hardware features must be ignored. This is not as great a burden as it may first appear, because a considerable body of code already exists for this purpose, and the great bulk of 386 AT platforms already conform to de facto hardware standards. So, we're fighting the PC concept of the BIOS being used to "hide" the underlying hardware. Where it may bite us later is when the standards change, like if an 8042 isn't used to work the keyboard.


<<BACK NEXT >>



Copyright 1989, 1990, 2006 TeleMuse Partners, William Jolitz and Lynne Jolitz