Minix1.5/kernel/keyboard.c

Compare this file to the similar file:
Show the results in this format:

/* Keyboard driver for PC's and AT's. */

#include "kernel.h"
#include <sgtty.h>
#include <signal.h>
#include <minix/callnr.h>
#include <minix/com.h>
#include "tty.h"

/* Standard and AT keyboard.  (PS/2 MCA implies AT throughout.) */
#define KEYBD		0x60	/* I/O port for keyboard data */

/* AT keyboard.  Most of these values are only used for rebooting. */
#define KB_COMMAND	0x64	/* I/O port for commands on AT */
#define KB_GATE_A20	0x02	/* bit in output port to enable A20 line */
#define KB_PULSE_OUTPUT	0xF0	/* base for commands to pulse output port */
#define KB_RESET	0x01	/* bit in output port to reset CPU */
#define KB_STATUS	0x64	/* I/O port for status on AT */

/* PS/2 model 30 keyboard. */
#define PS_KB_STATUS	0x72	/* I/O port for status on ps/2 (???) */
#define PS_KEYBD	0x68	/* I/O port for data on ps/2 */

/* AT and PS/2 model 30 keyboards. */
#define KB_ACK		0xFA	/* keyboard ack response */
#define KB_BUSY		0x02	/* status bit set when KEYBD port ready */
#define LED_CODE	0xED	/* command to keyboard to set LEDs */
#define MAX_KB_ACK_RETRIES 0x1000	/* max #times to wait for kb ack */
#define MAX_KB_BUSY_RETRIES 0x1000	/* max #times to loop while kb busy */

/* All keyboards. */
#define KBIT		0x80	/* bit used to ack characters to keyboard */

/* Scan codes whose action is not completely captured by maps. */
#define DEL_SCAN	  83	/* DEL for use in CTRL-ALT-DEL reboot */
#define DUTCH_EXT_SCAN	  32	/* 'd' */
#define ESCAPE_CODE	0xE0	/* beginning of escape sequence */
#define F1		  59	/* function key F1, others follow */
#define F2		  60
#define F3		  61
#define F4		  62
#define F5		  63
#define F8		  66
#define F9		  67
#define F10		  68
#define MINUS_DU	0x35	/* '-' on Dutch extended keybd */
#define NUM_SLASH_DU	0x57	/* numeric keypad slash on Dutch extended kb */
#define OLIVETTI_SCAN	  12	/* '=' key on olivetti */
#define SCODE1		  71	/* Home on numeric pad */
#define SCODE2		  81	/* PgDn on numeric pad */
#define STANDARD_SCAN	  13	/* '=' key on IBM */
#define TOP_ROW		  14	/* codes below this are shifted if CTRL */
#define US_EXT_SCAN	  22	/* 'u' */

#define NR_SCAN_CODES	0x69	/* Number of scan codes */

/* Keyboard types. */
#define IBM_PC		   1	/* Standard IBM keyboard */
#define OLIVETTI	   2	/* Olivetti keyboard */
#define DUTCH_EXT	   3	/* Dutch extended IBM keyboard */
#define US_EXT		   4	/* U.S. extended keyboard */

/* Miscellaneous. */
#define CTRL_S		  31	/* scan code for letter S (for CRTL-S) */
#define CONSOLE		   0	/* line number for console */
#define MEMCHECK_ADR   0x472	/* address to stop memory check after reboot */
#define MEMCHECK_MAG  0x1234	/* magic number to stop memory check */

#define kb_addr(n)	(&kb_lines[CONSOLE])	/* incorrectly ignore n */
#define KB_IBUFSIZE	  32	/* size of keyboard input buffer */

PRIVATE int alt;		/* alt key state */
PRIVATE int capslock;		/* caps lock key state */
PRIVATE int esc;		/* escape scan code detected? */
PRIVATE int control;		/* control key state */
PRIVATE int caps_off;		/* 1 = normal position, 0 = depressed */
PRIVATE int keyb_type;		/* type of keyboard attached */
PRIVATE int minus_code;		/* numeric minus on dutch extended keyboard */
PRIVATE int numlock;		/* number lock key state */
PRIVATE int num_off;		/* 1 = normal position, 0 = depressed */
PRIVATE int num_slash;		/* numeric slash on dutch extended keyboard */
PRIVATE int shift1;		/* left shift key state */
PRIVATE int shift2;		/* right shift key state */

/* Scan codes to ASCII for alt keys (IBM Extended keyboard) */
PRIVATE char alt_c[NR_SCAN_CODES];

/* Scan codes to ASCII for unshifted keys for IBM-PC (default) */
PRIVATE char unsh[NR_SCAN_CODES] = {
 0,033,'1','2','3','4','5','6',        '7','8','9','0','-','=','\b','\t',
 'q','w','e','r','t','y','u','i',      'o','p','[',']',015,0202,'a','s',
 'd','f','g','h','j','k','l',';',      047,0140,0200,0134,'z','x','c','v',
 'b','n','m',',','.','/',0201,'*',     0203,' ',0204,0241,0242,0243,0244,0245,
 0246,0247,0250,0251,0252,0205,0210,0267,  0270,0271,0211,0264,0265,0266,0214
,0261,0262,0263,'0',0177,0,0,0,0,	0,0,0,0,0,0,0,0,
 0,0,0,0,0,0,0,0
};

/* Scan codes to ASCII for shifted keys */
PRIVATE char sh[NR_SCAN_CODES] = {
 0,033,'!','@','#','$','%','^',        '&','*','(',')','_','+','\b','\t',
 'Q','W','E','R','T','Y','U','I',      'O','P','{','}',015,0202,'A','S',
 'D','F','G','H','J','K','L',':',      042,'~',0200,'|','Z','X','C','V',
 'B','N','M','<','>','?',0201,'*',    0203,' ',0204,0221,0222,0223,0224,0225,
 0226,0227,0230,0231,0232,0204,0213,'7',  '8','9',0211,'4','5','6',0214,'1',
 '2','3','0','.',0,0,0,0,		0,0,0,0,0,0,0,0,
 0,0,0,0,0,0,0,0
};

/* Scan codes to ASCII for Olivetti M24 for unshifted keys. */
PRIVATE char unm24[NR_SCAN_CODES] = {
 0,033,'1','2','3','4','5','6',        '7','8','9','0','-','^','\b','\t',
 'q','w','e','r','t','y','u','i',      'o','p','@','[','\r',0202,'a','s',
 'd','f','g','h','j','k','l',';',      ':',']',0200,'\\','z','x','c','v',
 'b','n','m',',','.','/',0201,'*',     0203,' ',0204,0241,0242,0243,0244,0245,
0246,0247,0250,0251,0252,023,0210,0267,0270,0271,0211,0264,0265,0266,0214,0261,
0262,0263,'0','.',' ',014,0212,'\r',   0264,0262,0266,0270,032,0213,' ','/',
0253,0254,0255,0256,0257,0215,0216,0217
};

/* Scan codes to ASCII for Olivetti M24 for shifted keys. */
PRIVATE char m24[NR_SCAN_CODES] = {
 0,033,'!','"','#','$','%','&',        047,'(',')','_','=','~','\b','\t',
 'Q','W','E','R' ,'T','Y','U','I',     'O','P',0140,'{','\r',0202,'A','S',
 'D','F','G','H','J','K','L','+',      '*','}',0200,'|','Z','X','C','V',
 'B','N','M','<','>','?',0201,'*',     0203,' ',0204,0221,0222,0223,0224,0225,
 0226,0227,0230,0231,0232,0270,023,'7', '8','9',0211,'4','5','6',0214,'1',
 '2','3',0207,0177,0271,014,0272,'\r', '\b','\n','\f',036,032,0273,0274,'/',
 0233,0234,0235,0236,0237,0275,0276,0277
};

PRIVATE char dutch_unsh[NR_SCAN_CODES] = {
 0,033,'1','2','3','4','5','6',		'7','8','9','0','/',0370,'\b','\t',
 'q','w','e','r','t','y','u','i',	'o','p',0,'*','\r',0202,'a','s',
 'd','f','g','h','j','k','l','+',	'\'',0100,0200,'<','z','x','c','v',
 'b','n','m',',','.','-',0201,'*',	0203,' ',0204,0,0,0,0,0,
 0,0,0,0,0,0205,0,183,			184,185,137,180,'5',182,140,177,
 178,179,0,0177,0,0,']','/',0,		0,0,0,0,0,0,0,0,
 0,0,0,0,0,0,0,0
};

PRIVATE char dutch_sh[NR_SCAN_CODES] = {
 0,033,'!','\"','#','$','%','&',	'_','(',')','\'','?',0176,'\b','\t',
 'Q','W','E','R','T','Y','U','I',	'O','P','^','|','\r',0202,'A','S',
 'D','F','G','H','J','K','L',0361,	0,025,0200,'>','Z','X','C','V',
 'B','N','M',';',':','=',0201,'*',	0203,' ',0204,0,0,0,0,0,
 0,0,0,0,0,0205,0,'7',			'8','9',137,'4','5','6',140,'1',
 '2','3','0',',',0,0,'[','/',0,		0,0,0,0,0,0,0,0,
 0,0,0,0,0,0,0,0
};

/* Code table for alt key */
PRIVATE char dutch_alt[NR_SCAN_CODES] = {
 0,0,0,253,0,172,171,0,		156,'{','}',0,'\\',0,0,0,
 0,0,0,0,0,0,0,0,		0,0,0,0,0,0202,0,0,
 0,0,0,0,0,0,0,0,		0,170,0200,0,174,175,0,0,
 0,0,0,0,0,0,0201,0,		0203,0,0204,0,0,0,0,0,
 0,0,0,0,0,0205,0,0,		0,0,0,0,0,0,0,0,
 0,0,0,0177,0,0,'|',0,0,	0,0,0,0,0,0,0,0,
 0,0,0,0,0,0,0,0
};

/* Unshifted U.S. extended kbd */
PRIVATE char unsh_usx[NR_SCAN_CODES] = {
 0,'`','1','2','3','4','5','6',        '7','8','9','0','-','=','\b','\t',
 'q','w','e','r','t','y','u','i',      'o','p','[',']',015,0202,'a','s',
 'd','f','g','h','j','k','l',';',      047,033,0200,0134,'z','x','c','v',
 'b','n','m',',','.','/',0201,'*',     0203,' ',0202,0241,0242,0243,0244,0245,
 0246,0247,0250,0251,0252,0205,0210,0267,  0270,0271,0211,0264,0265,0266,0214
,0261,0262,0263,'0',0177,0,0,0,0,	0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0
};

/* Shifted U.S. extended kbd */
PRIVATE char sh_usx[NR_SCAN_CODES] = {
 0,033,'!','@','#','$','%','^',        '&','*','(',')','_','+','\b','\t',
 'Q','W','E','R','T','Y','U','I',      'O','P','{','}',015,0202,'A','S',
 'D','F','G','H','J','K','L',':',      042,'~',0200,'|','Z','X','C','V',
 'B','N','M','<','>','?',0201,'*',    0203,' ',0202,0221,0222,0223,0224,0225,
 0226,0227,0230,0231,0232,0204,0213,'7',  '8','9',0211,'4','5','6',0214,'1',
 '2','3','0','.',0,0,0,0,		0,0,0,0,0,0,0,0,
 0,0,0,0,0,0,0,0
};

PRIVATE char scode_map[] =
		{'H', 'A', 'V', 'S', 'D', 'G', 'C', 'T', 'Y', 'B', 'U'};

/* Keyboard structure, 1 per console. */
struct kb_s {
  int minor;			/* minor number of this line (base 0) */

  char *ibuf;			/* start of input buffer */
  char *ibufend;		/* end of input buffer */
  char *iptr;			/* next free spot in input buffer */

  char ibuf1[KB_IBUFSIZE + 1];	/* 1st input buffer, guard at end */
  char ibuf2[KB_IBUFSIZE + 1];	/* 2nd input buffer (for swapping) */
};

PRIVATE struct kb_s kb_lines[NR_CONS];

FORWARD int kb_ack();
FORWARD int kb_wait();
FORWARD void load_dutch_table();
FORWARD void load_olivetti();
FORWARD void load_us_ext();
FORWARD int scan_keyboard();
FORWARD void set_leds();

/*===========================================================================*
 *				keyboard				     *
 *===========================================================================*/
PUBLIC void keyboard()
{
/* A keyboard interrupt has occurred.  Process it. */

  int code, k;
  register struct kb_s *kb;

  /* Fetch the character from the keyboard hardware and acknowledge it. */
  code = scan_keyboard();

  /* The IBM keyboard interrupts twice per key, once when depressed, once when
   * released.  Filter out the latter, ignoring all but the shift-type keys.
   * The shift-type keys 29, 42, 54, 56, 58, and 69 must be processed normally.
   */
  if (keyb_type == DUTCH_EXT)
	if (esc) {
		/* Numeric slash gives scan codes 0xE0 0x35. */
		if (code == minus_code) code = num_slash;
		esc = FALSE;
	} else
		esc = (code == ESCAPE_CODE);

  k = code - 0200;		/* codes > 0200 mean key release */
  if (k > 0) {
	/* A key has been released. */
	if (k != 29 && k != 42 && k != 54 && k != 56 && k != 58 && k != 69)
		return;		/* don't call tty_task() */
  } else {
	/* Check to see if character is CTRL-S, to stop output. Setting xoff
	 * to anything other than CTRL-S will not be detected here, but will
	 * be detected later, in the driver.  A general routine to detect any
	 * xoff character here would be complicated since we only have the
	 * scan code here, not the ASCII character.
	 */
	if (!(tty_struct[CONSOLE].tty_mode & RAW) &&
	    control && code == CTRL_S &&
	    tty_struct[CONSOLE].tty_xoff == XOFF_CHAR) {
		tty_struct[CONSOLE].tty_inhibited = STOPPED;
		return;
	}
  }

  /* Call debugger? (as early as practical, not in TTY which may be hung) */
  if (code == F10 && db_exists) {
	db_enabled = TRUE;
	db();
  }

  /* Store the character in memory so the task can get at it later. */
  kb = kb_addr(-NR_CONS);
  *kb->iptr = code;
  if (kb->iptr < kb->ibufend) {
	lock();			/* protect shared variable */
	tty_events += EVENT_THRESHOLD;	/* C doesn't guarantee atomic */
	unlock();
	++kb->iptr;
  }
  /* Else it doesn't fit - discard it. */
}


/*==========================================================================*
 *				kb_read					    *
 *==========================================================================*/
PUBLIC int kb_read(minor, bufindirect, odoneindirect)
int minor;
char **bufindirect;
unsigned char *odoneindirect;
{
/* Swap the keyboard input buffers, giving the old one to TTY. */

  register char *ibuf;
  register struct kb_s *kb;
  int nread;

  kb = kb_addr(minor);
  *odoneindirect = FALSE;
  if (kb->iptr == (ibuf = kb->ibuf)) return 0;
  *bufindirect = ibuf;
  lock();
  nread = kb->iptr - ibuf;
  tty_events -= nread * EVENT_THRESHOLD;
  if (ibuf == kb->ibuf1)
	ibuf = kb->ibuf2;
  else
	ibuf = kb->ibuf1;
  kb->ibufend = ibuf + KB_IBUFSIZE;
  kb->iptr = ibuf;
  unlock();
  kb->ibuf = ibuf;
  return nread;
}


/*===========================================================================*
 *				letter_code				     *
 *===========================================================================*/
PUBLIC int letter_code(scode)
int scode;			/* scan code from key press */
{
/* Convert scan codes from numeric keypad to letters for use in escape seqs. */

  if (scode >= SCODE1 && scode <= SCODE2 && (shift1 || shift2 || !numlock))
	return scode_map[scode - SCODE1];
  return 0;
}


/*===========================================================================*
 *				make_break				     *
 *===========================================================================*/
PUBLIC int make_break(ch)
char ch;			/* scan code of key just struck or released */
{
/* This routine can handle keyboards that interrupt only on key depression,
 * as well as keyboards that interrupt on key depression and key release.
 * For efficiency, the interrupt routine filters out most key releases.
 */

  int c, make, code;

  /* Check for CTRL-ALT-DEL, and if found, reboot the computer. This would
   * be better done in keyboard() in case TTY is hung, except control and
   * alt are set in the high level code.
   */
  if (control && alt && ch == DEL_SCAN) reboot();	/* CTRL-ALT-DEL */

  c = ch & 0177;		/* high-order bit set on key release */
  make = (ch & 0200 ? 0 : 1);	/* 1 when key depressed, 0 when key released */

  if (alt && keyb_type == DUTCH_EXT)
	code = alt_c[c];
  else
	code = (shift1 || shift2 ? sh[c] : unsh[c]);

  if (control && c < TOP_ROW) code = sh[c];	/* CTRL-(top row) */

  if (c >= SCODE1 && c <= SCODE2 + 2)	/* numeric pad including DEL, INS */
	code = (shift1 || shift2 || !numlock ? unsh[c] : sh[c]);

  code &= BYTE;
  if (code < 0200 || code >= 0206) {
	/* Ordinary key, i.e. not shift, control, alt, etc. */
	if (capslock)
		if (code >= 'A' && code <= 'Z')
			code += 'a' - 'A';
		else if (code >= 'a' && code <= 'z')
			code -= 'a' - 'A';
	if (alt && keyb_type != DUTCH_EXT) code |= 0200;  /* alt ORs in 0200 */
	if (control) code &= 037;
	if (make == 0) code = -1;	/* key release */
	return(code);
  }

  /* Table entries 0200 - 0206 denote special actions. */
  switch(code - 0200) {
    case 0:	shift1 = make;		break;	/* shift key on left */
    case 1:	shift2 = make;		break;	/* shift key on right */
    case 2:
#if KEYBOARD_84
/* Until IBM invented the 101-key keyboard, the CTRL key was always to the
 * left of the 'A'.  This fix puts it back there on the 101-key keyboard.
 */
		if (make && caps_off) {
			capslock = 1 - capslock;
			set_leds();
		}
		caps_off = 1 - make;    break;	/* caps lock */
#else
		control = make;		break;	/* control */
#endif
    case 3:	alt = make;		break;	/* alt key */
    case 4:	
#if KEYBOARD_84
		control = make;		break;	/* control */
#else
		if (make && caps_off) {
			capslock = 1 - capslock;
			set_leds();
		}
		caps_off = 1 - make;    break;	/* caps lock */
#endif
    case 5:	if (make && num_off) {
			numlock  = 1 - numlock;
			set_leds();
		}
		num_off = 1 - make;
		break;	/* num lock */
  }
  return(-1);
}


/*===========================================================================*
 *				set_leds				     *
 *===========================================================================*/
PRIVATE void set_leds()
{
/* Set the LEDs on the caps lock and num lock keys */

  int leds, data_port, status_port;

  if (!pc_at && !ps) return;	/* PC/XT doesn't have LEDs */
  leds = (numlock << 1) | (capslock << 2);	/* encode LED bits */

  if (ps) {
	data_port = PS_KEYBD;
	status_port = PS_KB_STATUS;
  } else {
	data_port = KEYBD;
	status_port = KB_STATUS;
  }

  kb_wait(status_port);		/* wait for buffer empty  */
  out_byte(data_port, LED_CODE);   /* prepare keyboard to accept LED values */
  kb_ack(data_port);		/* wait for ack response  */

  kb_wait(status_port);		/* wait for buffer empty  */
  out_byte(data_port, leds);	/* give keyboard LED values */
  kb_ack(data_port);		/* wait for ack response  */
}


/*==========================================================================*
 *				kb_wait					    *
 *==========================================================================*/
PRIVATE int kb_wait(status_port)
int status_port;
{
/* Wait until the controller is ready; return zero if this times out. */

  int retries;

  retries = MAX_KB_BUSY_RETRIES + 1;
  while (--retries != 0 && in_byte(status_port) & KB_BUSY)
	;			/* wait until not busy */
  return(retries);		/* nonzero if ready */
}


/*==========================================================================*
 *				kb_ack					    *
 *==========================================================================*/
PRIVATE int kb_ack(data_port)
int data_port;
{
/* Wait until kbd acknowledges last command; return zero if this times out. */

  int retries;

  retries = MAX_KB_ACK_RETRIES + 1;
  while (--retries != 0 && in_byte(data_port) != KB_ACK)
	;			/* wait for ack */
  return(retries);		/* nonzero if ack received */
}

/*===========================================================================*
 *				kb_init					     *
 *===========================================================================*/
PUBLIC void kb_init(minor)
int minor;
{
/* Initialize the keyboard driver. */

  register struct kb_s *kb;

  kb = kb_addr(minor);

  /* Record minor number. */
  kb->minor = minor;

  /* Set up input queue. */
  kb->iptr = kb->ibuf = kb->ibuf1;
  kb->ibufend = kb->ibuf1 + KB_IBUFSIZE;
  kb->iptr = kb->ibuf1;

  /* Set initial values. */
  caps_off = 1;
  num_off = 1;

  set_leds();			/* turn off numlock led */

  /* Determine which keyboard type is attached.  The bootstrap program asks
   * the user to type an '='.  The scan codes for '=' differ depending on the
   * keyboard in use.
   */
  switch(scan_code) {
	case STANDARD_SCAN:	keyb_type = IBM_PC; break;
	case OLIVETTI_SCAN:	keyb_type = OLIVETTI; load_olivetti(); break;
	case DUTCH_EXT_SCAN:	keyb_type = DUTCH_EXT;
				load_dutch_table(); break;
	case US_EXT_SCAN:	keyb_type = US_EXT;
				load_us_ext(); break;
  }

  scan_keyboard();		/* stop lockup from leftover keystroke */
  enable_irq(KEYBOARD_IRQ);	/* safe now everything initialised! */
}

/*===========================================================================*
 *				load_dutch_table			     *
 *===========================================================================*/
PRIVATE void load_dutch_table()
{
/* Load the scan code to ASCII table for extended dutch keyboard. */

  register int i;

  for (i = 0; i < NR_SCAN_CODES; i++) {
	sh[i] = dutch_sh[i];
	unsh[i] = dutch_unsh[i];
	alt_c[i] = dutch_alt[i];
  }

  minus_code = MINUS_DU;
  num_slash = NUM_SLASH_DU;
}

/*===========================================================================*
 *				load_olivetti				     *
 *===========================================================================*/
PRIVATE void load_olivetti()
{
/* Load the scan code to ASCII table for olivetti type keyboard. */

  register int i;

  for (i = 0; i < NR_SCAN_CODES; i++) {
	sh[i] = m24[i];
	unsh[i] = unm24[i];
  }
}

/*===========================================================================*
 *				load_us_ext				     *
 *===========================================================================*/
PRIVATE void load_us_ext()
{
/* Load the scan code to ASCII table for US extended keyboard. */

  register int i;

  for (i = 0; i < NR_SCAN_CODES; i++) {
	sh[i] = sh_usx[i];
	unsh[i] = unsh_usx[i];
  }
}

/*===========================================================================*
 *				func_key				     *
 *===========================================================================*/
PUBLIC int func_key(ch)
char ch;			/* scan code for a function key */
{
/* This procedure traps function keys for debugging and control purposes. */

  if (ch < F1 || ch > F10) return(FALSE);	/* not our job */
  if (ch == F1) p_dmp();	/* print process table */
  if (ch == F2) map_dmp();	/* print memory map */
  if (ch == F3) toggle_scroll();	/* hardware vs. software scrolling */

#if AM_KERNEL
#if !NONET
  if (ch == F4) net_init();	/* Re-initialise the ethernet card */
#endif
  if (ch == F5) amdump();	/* Dump Amoeba statistics. */
#endif /* AM_KERNEL */

  if (ch == F8 && control) sigchar(&tty_struct[CONSOLE], SIGINT);
  if (ch == F9 && control) sigchar(&tty_struct[CONSOLE], SIGKILL);
  return(TRUE);
}


/*==========================================================================*
 *				scan_keyboard				    *
 *==========================================================================*/
PRIVATE int scan_keyboard()
{
/* Fetch the character from the keyboard hardware and acknowledge it. */

  int code;
  int val;

  if (ps) {
	code = in_byte(PS_KEYBD);	/* get the scan code for key struck */
	val = in_byte(0x69);	/* acknowledge it in mysterious ways */
	out_byte(0x69, val ^ 0x10);	/* 0x69 should be equiv to PORT_B */
	out_byte(0x69, val);	/* XOR looks  fishy */
	val = in_byte(0x66);	/* what is 0x66? */
	out_byte(0x66, val & ~0x10);	/* 0x72 for PS_KB_STATUS is fishier */
	out_byte(0x66, val | 0x10);
	out_byte(0x66, val & ~0x10);
  } else {
	code = in_byte(KEYBD);	/* get the scan code for the key struck */
	val = in_byte(PORT_B);	/* strobe the keyboard to ack the char */
	out_byte(PORT_B, val | KBIT);	/* strobe the bit high */
	out_byte(PORT_B, val);	/* now strobe it low */
  }
  return code;
}


/*==========================================================================*
 *				reboot					    *
 *==========================================================================*/
PUBLIC void reboot()
{
/* Reboot the machine. */

  static u16_t magic = MEMCHECK_MAG;

  lock();
  eth_stp();			/* stop ethernet (may be unnecessary) */

  /* Stop BIOS memory test. */
  phys_copy(numap(TTY, (vir_bytes) &magic, sizeof magic),
	    (phys_bytes) MEMCHECK_ADR, (phys_bytes) sizeof magic);
  if (protected_mode) {
	/* Rebooting is nontrivial because the BIOS reboot code is in real
	 * mode and there is no sane way to return to real mode on 286's.
	 */
	if (pc_at) {
		/* Use the AT keyboard controller to reset the processor.
		 * The A20 line is kept enabled in case this code is ever
		 * run from extended memory, and because some machines
		 * appear to drive the fake A20 high instead of low just
		 * after reset, leading to an illegal opode trap.  This bug
		 * is more of a problem if the fake A20 is in use, as it
		 * would be if the keyboard reset were used for real mode.
		 */
		kb_wait();
		out_byte(KB_COMMAND,
			 KB_PULSE_OUTPUT | (0x0F & ~(KB_GATE_A20 | KB_RESET)));
	} else {
		printf("No way to reboot from protected mode on this machine ");
	}
	while (TRUE)
		;		/* no way to recover if the above fails */
  }

  /* In real mode, jumping to the reset address is good enough. */
  reset();
}


/*==========================================================================*
 *				wreboot					    *
 *==========================================================================*/
PUBLIC void wreboot()
{
/* Wait for a keystroke, then reboot the machine.  Don't rely on interrupt
 * to provide the keystroke, since this is usually called after a crash,
 * and possibly before interrupts are initialized.
 */

  register int scancode;

  lock();
  milli_delay(1000);		/* pause for a second to ignore key release */
  scan_keyboard();		/* ack any old input */
  printf("Type any key to reboot\r\n");
  scancode = scan_keyboard();	/* quiescent value (0 on PC, last code on AT)*/
  while(scan_keyboard() == scancode)
	;			/* loop until new keypress or any release */
  reboot();
}