/*
* linux/kernel/console.c
*
* (C) 1991 Linus Torvalds
*/

/*
* console.c
*
* This module implements the console io functions
* 'void con_init(void)'
* 'void con_write(struct tty_queue * queue)'
* Hopefully this will be a rather complete VT102 implementation.
*
* Beeping thanks to John T Kohl.
*/
/*
* ģʵֿ̨
* 'void con_init(void)'
* 'void con_write(struct tty_queue * queue)'
* ϣһǳVT102 ʵ֡
*
* лJohn T Kohl ʵ˷ָʾ
*/

/*
* NOTE!!! We sometimes disable and enable interrupts for a short while
* (to put a word in video IO), but this will work even for keyboard
* interrupts. We know interrupts aren't enabled when getting a keyboard
* interrupt, as we use trap-gates. Hopefully all is well.
*/
/*
* ע!!! ʱݵؽֹж(ڽһ(word)ŵƵIO)ʹ
* ڼжҲǿԹġΪʹţ֪ڻһ
* жʱжǲġϣһо
*/

/*
* Code to check for different video-cards mostly by Galen Hunt,
* <g-hunt@ee.utah.edu>
*/
/*
* ⲻͬʾĴGalen Hunt дģ
* <g-hunt@ee.utah.edu>
*/

#include <linux/sched.h>	// ȳͷļṹtask_structʼ0 ݣ
// һЩйúͻȡǶʽຯ䡣
#include <linux/tty.h>		// tty ͷļйtty_ioͨŷĲ
#include <asm/io.h>		// io ͷļӲ˿/䡣
#include <asm/system.h>		// ϵͳͷļû޸/жŵȵǶʽꡣ

/*
* These are set up by the setup-routine at boot-time:
*/
/*
* Щӳsetup ϵͳʱõĲ
*/

// μboot/setup.s עͣsetup ȡĲ
#define ORIG_X (*(unsigned char *)0x90000)	// кš
#define ORIG_Y (*(unsigned char *)0x90001)	// кš
#define ORIG_VIDEO_PAGE (*(unsigned short *)0x90004)	// ʾҳ档
#define ORIG_VIDEO_MODE ((*(unsigned short *)0x90006) & 0xff)	// ʾģʽ
#define ORIG_VIDEO_COLS (((*(unsigned short *)0x90006) & 0xff00) >> 8)	// ַ
#define ORIG_VIDEO_LINES (25)	// ʾ
#define ORIG_VIDEO_EGA_AX (*(unsigned short *)0x90008)	// [??]
#define ORIG_VIDEO_EGA_BX (*(unsigned short *)0x9000a)	// ʾڴСɫģʽ
#define ORIG_VIDEO_EGA_CX (*(unsigned short *)0x9000c)	// ʾԲ

// ʾɫ/ɫʾģʽͷų
#define VIDEO_TYPE_MDA 0x10 /* Monochrome Text Display */	/* ɫı */
#define VIDEO_TYPE_CGA 0x11 /* CGA Display */	/* CGA ʾ */
#define VIDEO_TYPE_EGAM 0x20 /* EGA/VGA in Monochrome Mode */	/* EGA/VGA ɫ */
#define VIDEO_TYPE_EGAC 0x21 /* EGA/VGA in Color Mode */	/* EGA/VGA ɫ */

#define NPAR 16

extern void keyboard_interrupt (void);	// жϴ(keyboard.S)

static unsigned char video_type;	/* Type of display being used */
/* ʹõʾ */
static unsigned long video_num_columns;	/* Number of text columns */
/* Ļı */
static unsigned long video_size_row;	/* Bytes per row */
/* ÿʹõֽ */
static unsigned long video_num_lines;	/* Number of test lines */
/* Ļı */
static unsigned char video_page;	/* Initial video page */
/* ʼʾҳ */
static unsigned long video_mem_start;	/* Start of video RAM */
/* ʾڴʼַ */
static unsigned long video_mem_end;	/* End of video RAM (sort of) */
/* ʾڴ(ĩ)ַ */
static unsigned short video_port_reg;	/* Video register select port */
/* ʾĴ˿ */
static unsigned short video_port_val;	/* Video register value port */
/* ʾݼĴ˿ */
static unsigned short video_erase_char;	/* Char+Attrib to erase with */
/* ַַ(0x0720) */

// ЩĻ
static unsigned long origin;	/* Used for EGA/VGA fast scroll */// scr_start
/* EGA/VGA ٹ */// ʼڴַ
static unsigned long scr_end;	/* Used for EGA/VGA fast scroll */
/* EGA/VGA ٹ */// ĩڴַ
static unsigned long pos;	// ǰӦʾڴλá
static unsigned long x, y;	// ǰλá
static unsigned long top, bottom;	// ʱкţкš
// state ڱESC תʱĵǰ衣npar,par[]ڴESC ем䴦
static unsigned long state = 0;	// ANSI תַд״̬
static unsigned long npar, par[NPAR];	// ANSI תַвͲ顣
static unsigned long ques = 0;
static unsigned char attr = 0x07;	// ַ(ڵװ)

static void sysbeep (void);	// ϵͳ

/*
* this is what the terminal answers to a ESC-Z or csi0c
* query (= vt100 response).
*/
/*
* ն˻ӦESC-Z csi0c Ӧ(=vt100 Ӧ)
*/
// csi - (Control Sequence Introducer)
#define RESPONSE "\033[?1;2c"

/* NOTE! gotoxy thinks x==video_num_columns is ok */
/* ע⣡gotoxy Ϊx==video_num_columnsȷ */
//// ٹ굱ǰλá
// new_x - кţnew_y - кš
// µǰλñx,ypos ָʾڴеĶӦλá
static inline void
gotoxy (unsigned int new_x, unsigned int new_y)
{
// Ĺкųʾ߹кųʾ˳
  if (new_x > video_num_columns || new_y >= video_num_lines)
    return;
// µǰ¹λöӦʾڴλñpos
  x = new_x;
  y = new_y;
  pos = origin + y * video_size_row + (x << 1);
}

//// ùʼʾڴַ
static inline void
set_origin (void)
{
  cli ();
// ѡʾݼĴr12Ȼдʼַֽڡƶ9 λʾƶ
// 8 λٳ2(2 ֽڴĻ1 ַ)Ĭʾڴġ
  outb_p (12, video_port_reg);
  outb_p (0xff & ((origin - video_mem_start) >> 9), video_port_val);
// ѡʾݼĴr13Ȼдʼַֽڡƶ1 λʾ2
  outb_p (13, video_port_reg);
  outb_p (0xff & ((origin - video_mem_start) >> 1), video_port_val);
  sti ();
}

//// ϾһУĻƶ
// ĻƶһСμб˵
static void
scrup (void)
{
// ʾEGAִ²
  if (video_type == VIDEO_TYPE_EGAC || video_type == VIDEO_TYPE_EGAM)
    {
// ƶʼtop=0ƶbottom=video_num_lines=25ʾƶ
      if (!top && bottom == video_num_lines)
	{
// ĻʾӦڴʼλָorigin ΪһĻַӦڴλãͬʱҲ
// ǰӦڴλԼĻĩĩַָscr_end λá
	  origin += video_size_row;
	  pos += video_size_row;
	  scr_end += video_size_row;
// ĻĩһʾַӦʾڴָscr_end ʵʾڴĩˣ
// Ļڴƶʾڴʼλvideo_mem_start ڳֵոַ
	  if (scr_end > video_mem_end)
	    {
// %0 - eax(ַ+)%1 - ecx((ʾַ-1)Ӧַ/2Գƶ)
// %2 - edi(ʾڴʼλvideo_mem_start)%3 - esi(ĻݶӦڴʼλorigin)
// ƶ[edi]??[esi]ƶecx ֡
	      __asm__ ("cld\n\t"	// 巽λ
		       "rep\n\t"	// ظǰĻڴ
		       "movsl\n\t"	// ƶʾڴʼ
		       "movl _video_num_columns,%1\n\t"	// ecx=1 ַ
		       "rep\n\t"	// ոַ
	    "stosw"::"a" (video_erase_char), "c" ((video_num_lines - 1) * video_num_columns >> 1), "D" (video_mem_start), "S" (origin):"cx", "di",
		       "si");
// ĻڴƶµǰĻӦڴʼָ롢λָĻĩ
// Ӧڴָscr_end
	      scr_end -= origin - video_mem_start;
	      pos -= origin - video_mem_start;
	      origin = video_mem_start;
	    }
	  else
	    {
// Ļĩ˶Ӧڴָscr_end ûгʾڴĩvideo_mem_endֻ
// ַ(ոַ)
// %0 - eax(ַ+)%1 - ecx(ʾַ)%2 - edi(ĻӦڴһпʼ)
	      __asm__ ("cld\n\t"	// 巽λ
		       "rep\n\t"	// ظ³
		       "stosw"	// ַ(ոַ)
	    ::"a" (video_erase_char), "c" (video_num_columns), "D" (scr_end - video_size_row):"cx",
		       "di");
	    }
// ʾдµĻݶӦڴʼλֵ
	  set_origin ();
// ʾƶҲʾָtop ʼƶ1 (ɾ1 )ʱֱ
// Ļָtop ĻĩжӦʾڴƶ1 У³ֵ
// ַ
// %0-eax(ַ+)%1-ecx(top 1 пʼĻĩеӦڴ泤)
// %2-edi(top ڴλ)%3-esi(top+1 ڴλ)
	}
      else
	{
	  __asm__ ("cld\n\t"	// 巽λ
		   "rep\n\t"	// ѭtop+1 bottom 
		   "movsl\n\t"	// ӦڴƵtop пʼ
		   "movl _video_num_columns,%%ecx\n\t"	// ecx = 1 ַ
		   "rep\n\t"	// ַ
	"stosw"::"a" (video_erase_char), "c" ((bottom - top - 1) * video_num_columns >> 1), "D" (origin + video_size_row * top), "S" (origin + video_size_row * (top + 1)):"cx", "di",
		   "si");
	}
    }
// ʾͲEGA(MDA)ִƶΪMDA ʾƿԶʾΧ
// ҲԶָ룬ﲻĻݶӦڴ泬ʾڴ
// EGA ƶȫһ
  else				/* Not EGA/VGA */
    {
    __asm__ ("cld\n\t" "rep\n\t" "movsl\n\t" "movl _video_num_columns,%%ecx\n\t" "rep\n\t" "stosw"::"a" (video_erase_char), "c" ((bottom - top - 1) * video_num_columns >> 1), "D" (origin + video_size_row * top), "S" (origin + video_size_row * (top + 1)):"cx", "di",
	       "si");
    }
}

//// ¾һУĻƶ
// ĻƶһУĻʾƶ1 УڱƶʼеϷһСμ
// б˵scrup()ƣֻΪƶʾڴʱݸǴ
// ԷеģҲĻ2 еһַʼ
static void
scrdown (void)
{
// ʾEGAִв
// [??if else Ĳȫһ!ΪʲôҪֱأѵлйأ]
  if (video_type == VIDEO_TYPE_EGAC || video_type == VIDEO_TYPE_EGAM)
    {
// %0-eax(ַ+)%1-ecx(top пʼĻĩ-1 еӦڴ泤)
// %2-edi(Ļ½һλ)%3-esi(Ļ2 һλ)
// ƶ[esi]??[edi]ƶecx ֡
      __asm__ ("std\n\t"	// ÷λ
	       "rep\n\t"	// ظƶtop еbottom-1 
	       "movsl\n\t"	// Ӧڴݡ
	       "addl $2,%%edi\n\t"	/* %edi has been decremented by 4 */
/* %edi Ѿ4ΪҲǷַ */
	       "movl _video_num_columns,%%ecx\n\t"	// ecx=1 ַ
	       "rep\n\t"	// ַϷС
    "stosw"::"a" (video_erase_char), "c" ((bottom - top - 1) * video_num_columns >> 1), "D" (origin + video_size_row * bottom - 4), "S" (origin + video_size_row * (bottom - 1) - 4):"ax", "cx", "di",
	       "si");
    }
// EGA ʾִͣ²Ŀǰȫһ
  else				/* Not EGA/VGA */
    {
      __asm__ ("std\n\t" "rep\n\t" "movsl\n\t" "addl $2,%%edi\n\t"	/* %edi has been decremented by 4 */
    "movl _video_num_columns,%%ecx\n\t" "rep\n\t" "stosw"::"a" (video_erase_char), "c" ((bottom - top - 1) * video_num_columns >> 1), "D" (origin + video_size_row * bottom - 4), "S" (origin + video_size_row * (bottom - 1) - 4):"ax", "cx", "di",
	       "si");
    }
}

//// λһ(lf - line feed )
static void
lf (void)
{
// ûдڵ2 ֱ֮޸Ĺ굱ǰбy++Ӧʾڴλ
// pos(ĻһַӦڴ泤)
  if (y + 1 < bottom)
    {
      y++;
      pos += video_size_row;
      return;
    }
// ҪĻһС
  scrup ();
}

//// һ(ri - reverse line feed )
static void
ri (void)
{
// 겻ڵ1 ϣֱ޸Ĺ굱ǰбy--Ӧʾڴλposȥ
// ĻһַӦڴ泤ֽ
  if (y > top)
    {
      y--;
      pos -= video_size_row;
      return;
    }
// ҪĻһС
  scrdown ();
}

// ص1 (0 )(cr - carriage return س)
static void
cr (void)
{
// ڵк*2 0 ежӦڴֽڳȡ
  pos -= x << 1;
  x = 0;
}

// ǰһַ(ÿո)(del - delete ɾ)
static void
del (void)
{
// ûд0 У򽫹Ӧڴλָpos 2 ֽ(ӦĻһַ)Ȼ
// ǰֵ1λַ
  if (x)
    {
      pos -= 2;
      x--;
      *(unsigned short *) pos = video_erase_char;
    }
}

//// ɾĻλصĲ֣ĻΪλcsi - (Control Sequence
// Introducer)
// ANSI תУ'ESC [sJ'(s = 0 ɾ굽Ļ׶ˣ1 ɾĻʼ괦2 ɾ)
// par - Ӧs
static void
csi_J (int par)
{
  long count __asm__ ("cx");	// ΪĴ
  long start __asm__ ("di");

// ȸֱҪɾַɾʼʾڴλá
  switch (par)
    {
    case 0:			/* erase from cursor to end of display *//* 굽Ļ׶ */
      count = (scr_end - pos) >> 1;
      start = pos;
      break;
    case 1:			/* erase from start to cursor *//* ɾĻʼ괦ַ */
      count = (pos - origin) >> 1;
      start = origin;
      break;
    case 2:			/* erase whole display *//* ɾĻϵַ */
      count = video_num_columns * video_num_lines;
      start = origin;
      break;
    default:
      return;
    }
// Ȼʹòַдɾַĵط
// %0 - ecx(Ҫɾַcount)%1 - edi(ɾʼַ)%2 - eaxĲַ
__asm__ ("cld\n\t" "rep\n\t" "stosw\n\t"::"c" (count), "D" (start), "a" (video_erase_char):"cx", "di");
}

//// ɾλصĲ֣һΪλ
// ANSI תַУ'ESC [sK'(s = 0 ɾβ1 ӿʼɾ2 жɾ)
static void
csi_K (int par)
{
  long count __asm__ ("cx");	// üĴ
  long start __asm__ ("di");

// ȸֱҪɾַɾʼʾڴλá
  switch (par)
    {
    case 0:			/* erase from cursor to end of line *//* ɾ굽βַ */
      if (x >= video_num_columns)
	return;
      count = video_num_columns - x;
      start = pos;
      break;
    case 1:			/* erase from start of line to cursor *//* ɾпʼ괦 */
      start = pos - (x << 1);
      count = (x < video_num_columns) ? x : video_num_columns;
      break;
    case 2:			/* erase whole line *//* ַȫɾ */
      start = pos - (x << 1);
      count = video_num_columns;
      break;
    default:
      return;
    }
// Ȼʹòַдɾַĵط
// %0 - ecx(Ҫɾַcount)%1 - edi(ɾʼַ)%2 - eaxĲַ
__asm__ ("cld\n\t" "rep\n\t" "stosw\n\t"::"c" (count), "D" (start), "a" (video_erase_char):"cx", "di");
}

//// ()ַʾʽӴ֡»ߡ˸Եȣ
// ANSI תַУ'ESC [nm'n = 0 ʾ1 Ӵ֣4 »ߣ7 ԣ27 ʾ
void
csi_m (void)
{
  int i;

  for (i = 0; i <= npar; i++)
    switch (par[i])
      {
      case 0:
	attr = 0x07;
	break;
      case 1:
	attr = 0x0f;
	break;
      case 4:
	attr = 0x0f;
	break;
      case 7:
	attr = 0x70;
	break;
      case 27:
	attr = 0x07;
	break;
      }
}

//// ʾꡣ
// ʾڴӦλposʾʾλá
static inline void
set_cursor (void)
{
  cli ();
// ʹĴ˿ѡʾݼĴr14(굱ǰʾλøֽ)Ȼд
// ǰλøֽ(ƶ9 λʾֽƵֽٳ2)Ĭʾڴġ
  outb_p (14, video_port_reg);
  outb_p (0xff & ((pos - video_mem_start) >> 9), video_port_val);
// ʹĴѡr15굱ǰλõֽдС
  outb_p (15, video_port_reg);
  outb_p (0xff & ((pos - video_mem_start) >> 1), video_port_val);
  sti ();
}

//// ͶնVT100 ӦС
// ӦзС
static void
respond (struct tty_struct *tty)
{
  char *p = RESPONSE;

  cli ();			// жϡ
  while (*p)
    {				// ַздС
      PUTCH (*p, tty->read_q);
      p++;
    }
  sti ();			// жϡ
  copy_to_cooked (tty);		// תɹ淶ģʽ(븨)
}

//// ڹ괦һոַ
static void
insert_char (void)
{
  int i = x;
  unsigned short tmp, old = video_erase_char;
  unsigned short *p = (unsigned short *) pos;

// 꿪ʼַһ񣬲ַڹڴ
// һ϶ַĻһַ??
  while (i++ < video_num_columns)
    {
      tmp = *p;
      *p = old;
      old = tmp;
      p++;
    }
}

//// ڹ괦һУ꽫µĿϣ
// ĻӹеĻ¾һС
static void
insert_line (void)
{
  int oldtop, oldbottom;

  oldtop = top;			// ԭtopbottom ֵ
  oldbottom = bottom;
  top = y;			// ĻʼС
  bottom = video_num_lines;	// ĻС
  scrdown ();			// ӹ꿪ʼĻ¹һС
  top = oldtop;			// ָԭtopbottom ֵ
  bottom = oldbottom;
}

//// ɾ괦һַ
static void
delete_char (void)
{
  int i;
  unsigned short *p = (unsigned short *) pos;

// 곬ĻУ򷵻ء
  if (x >= video_num_columns)
    return;
// ӹһַʼĩַһ
  i = x;
  while (++i < video_num_columns)
    {
      *p = *(p + 1);
      p++;
    }
// һַַ(ոַ)
  *p = video_erase_char;
}

//// ɾС
// ӹпʼĻϾһС
static void
delete_line (void)
{
  int oldtop, oldbottom;

  oldtop = top;			// ԭtopbottom ֵ
  oldbottom = bottom;
  top = y;			// ĻʼС
  bottom = video_num_lines;	// ĻС
  scrup ();			// ӹ꿪ʼĻϹһС
  top = oldtop;			// ָԭtopbottom ֵ
  bottom = oldbottom;
}

//// ڹ괦nr ַ
// ANSI תַУ'ESC [n@ '
//  nr = n
static void
csi_at (unsigned int nr)
{
// ַһַΪһַַnr Ϊ01 ַ
  if (nr > video_num_columns)
    nr = video_num_columns;
  else if (!nr)
    nr = 1;
// ѭַָ
  while (nr--)
    insert_char ();
}

//// ڹλônr С
// ANSI תַ'ESC [nL'
static void
csi_L (unsigned int nr)
{
// ĻΪĻʾnr Ϊ01 С
  if (nr > video_num_lines)
    nr = video_num_lines;
  else if (!nr)
    nr = 1;
// ѭָnr
  while (nr--)
    insert_line ();
}

//// ɾ괦nr ַ
// ANSI תУ'ESC [nP'
static void
csi_P (unsigned int nr)
{
// ɾַһַΪһַɾַnr Ϊ0ɾ1 ַ
  if (nr > video_num_columns)
    nr = video_num_columns;
  else if (!nr)
    nr = 1;
// ѭɾַָnr
  while (nr--)
    delete_char ();
}

//// ɾ괦nr С
// ANSI תУ'ESC [nM'
static void
csi_M (unsigned int nr)
{
// ɾĻΪĻʾɾnr Ϊ0ɾ1 С
  if (nr > video_num_lines)
    nr = video_num_lines;
  else if (!nr)
    nr = 1;
// ѭɾָnr
  while (nr--)
    delete_line ();
}

static int saved_x = 0;		// Ĺкš
static int saved_y = 0;		// Ĺкš

//// 浱ǰλá
static void
save_cur (void)
{
  saved_x = x;
  saved_y = y;
}

//// ָĹλá
static void
restore_cur (void)
{
  gotoxy (saved_x, saved_y);
}

//// ̨д
// ն˶Ӧtty дȡַʾĻϡ
void
con_write (struct tty_struct *tty)
{
  int nr;
  char c;

// ȡдַnrȻÿַд
  nr = CHARS (tty->write_q);
  while (nr--)
    {
// дȡһַcǰַ״̬state ֱ״̬֮תϵΪ
// state = 0ʼ״̬ԭ״̬4ԭ״̬1ַ'['
// 1ԭ״̬0ַתַESC(0x1b = 033 = 27)
// 2ԭ״̬1ַ'['
// 3ԭ״̬2ԭ״̬3ַ';'֡
// 4ԭ״̬3ַ';'֣
      GETCH (tty->write_q, c);
      switch (state)
	{
	case 0:
// ַǿַ(c>31)Ҳչַ(c<127)
	  if (c > 31 && c < 127)
	    {
// ǰ괦ĩ˻ĩ⣬򽫹ƵͷСλöӦڴָpos
	      if (x >= video_num_columns)
		{
		  x -= video_num_columns;
		  pos -= video_size_row;
		  lf ();
		}
// ַc дʾڴpos 1 УͬʱҲpos Ӧƶ2 ֽڡ
	    __asm__ ("movb _attr,%%ah\n\t" "movw %%ax,%1\n\t"::"a" (c), "m" (*(short *) pos):"ax");
	      pos += 2;
	      x++;
// ַc תַESCת״̬state 1
	    }
	  else if (c == 27)
	    state = 1;
// ַc ǻз(10)ǴֱƱVT(11)ǻҳFF(12)ƶ굽һС
	  else if (c == 10 || c == 11 || c == 12)
	    lf ();
// ַc ǻسCR(13)򽫹ƶͷ(0 )
	  else if (c == 13)
	    cr ();
// ַc DEL(127)򽫹ұһַ(ÿոַ)Ƶλá
	  else if (c == ERASE_CHAR (tty))
	    del ();
// ַc BS(backspace,8)򽫹1 񣬲ӦӦڴλָpos
	  else if (c == 8)
	    {
	      if (x)
		{
		  x--;
		  pos -= 2;
		}
// ַc ˮƽƱTAB(9)򽫹Ƶ8 ıϡʱĻ
// 򽫹Ƶһϡ
	    }
	  else if (c == 9)
	    {
	      c = 8 - (x & 7);
	      x += c;
	      pos += c << 1;
	      if (x > video_num_columns)
		{
		  x -= video_num_columns;
		  pos -= video_size_row;
		  lf ();
		}
	      c = 9;
// ַc BEL(7)÷
	    }
	  else if (c == 7)
	    sysbeep ();
	  break;
// ԭ״̬0ַתַESC(0x1b = 033 = 27)ת״̬1 
	case 1:
	  state = 0;
// ַc '['״̬state ת2
	  if (c == '[')
	    state = 2;
// ַc 'E'Ƶһпʼ(0 )
	  else if (c == 'E')
	    gotoxy (0, y + 1);
// ַc 'M'һС
	  else if (c == 'M')
	    ri ();
// ַc 'D'һС
	  else if (c == 'D')
	    lf ();
// ַc 'Z'նӦַС
	  else if (c == 'Z')
	    respond (tty);
// ַc '7'򱣴浱ǰλáעдӦ(c=='7')
	  else if (x == '7')
	    save_cur ();
// ַc '8'ָԭĹλáעдӦ(c=='8')
	  else if (x == '8')
	    restore_cur ();
	  break;
// ԭ״̬1һַ'['ת״̬2 
	case 2:
// ȶESC תַвʹõĴpar[]㣬npar ָ״̬
// Ϊ3ʱַ'?'ֱת״̬3 ȥȥһַٵ״̬3 봦
	  for (npar = 0; npar < NPAR; npar++)
	    par[npar] = 0;
	  npar = 0;
	  state = 3;
	  if (ques = (c == '?'))
	    break;
// ԭ״̬2ԭ״̬3ԭַ';'֣洦
	case 3:
// ַc Ƿֺ';'par δֵ1
	  if (c == ';' && npar < NPAR - 1)
	    {
	      npar++;
	      break;
// ַc ַ'0'-'9'򽫸ַתֵnpar 10 
	    }
	  else if (c >= '' && c <= '9')
	    {
	      par[npar] = 10 * par[npar] + c - '';
	      break;
// ת״̬4
	    }
	  else
	    state = 4;
// ԭ״̬״̬3ַ';'֣ת״̬4 ȸλ״̬state=0
	case 4:
	  state = 0;
	  switch (c)
	    {
// ַc 'G''`'par[]еһкšкŲΪ㣬򽫹һ
	    case 'G':
	    case '`':
	      if (par[0])
		par[0]--;
	      gotoxy (par[0], y);
	      break;
// ַc 'A'һƵΪ0 һС
	    case 'A':
	      if (!par[0])
		par[0]++;
	      gotoxy (x, y - par[0]);
	      break;
// ַc 'B''e'һƵΪ0 һС
	    case 'B':
	    case 'e':
	      if (!par[0])
		par[0]++;
	      gotoxy (x, y + par[0]);
	      break;
// ַc 'C''a'һƵĸΪ0 һ
	    case 'C':
	    case 'a':
	      if (!par[0])
		par[0]++;
	      gotoxy (x + par[0], y);
	      break;
// ַc 'D'һƵĸΪ0 һ
	    case 'D':
	      if (!par[0])
		par[0]++;
	      gotoxy (x - par[0], y);
	      break;
// ַc 'E'һƶص0 СΪ0 һС
	    case 'E':
	      if (!par[0])
		par[0]++;
	      gotoxy (0, y + par[0]);
	      break;
// ַc 'F'һƶص0 СΪ0 һС
	    case 'F':
	      if (!par[0])
		par[0]++;
	      gotoxy (0, y - par[0]);
	      break;
// ַc 'd'һڵк(0 )
	    case 'd':
	      if (par[0])
		par[0]--;
	      gotoxy (x, par[0]);
	      break;
// ַc 'H''f'һƵкţڶƵкš
	    case 'H':
	    case 'f':
	      if (par[0])
		par[0]--;
	      if (par[1])
		par[1]--;
	      gotoxy (par[1], par[0]);
	      break;
// ַc 'J'һԹλķʽ
// ANSI תУ'ESC [sJ'(s = 0 ɾ굽Ļ׶ˣ1 ɾĻʼ괦2 ɾ)
	    case 'J':
	      csi_J (par[0]);
	      break;
// ַc 'K'һԹλöַɾķʽ
// ANSI תַУ'ESC [sK'(s = 0 ɾβ1 ӿʼɾ2 жɾ)
	    case 'K':
	      csi_K (par[0]);
	      break;
// ַc 'L'ʾڹλôn (ANSI תַ'ESC [nL')
	    case 'L':
	      csi_L (par[0]);
	      break;
// ַc 'M'ʾڹλôɾn (ANSI תַ'ESC [nM')
	    case 'M':
	      csi_M (par[0]);
	      break;
// ַc 'P'ʾڹλôɾn ַ(ANSI תַ'ESC [nP')
	    case 'P':
	      csi_P (par[0]);
	      break;
// ַc '@'ʾڹλôn ַ(ANSI תַ'ESC [n@')
	    case '@':
	      csi_at (par[0]);
	      break;
// ַc 'm'ʾı괦ַʾԣӴ֡»ߡ˸Եȡ
// ANSI תַУ'ESC [nm'n = 0 ʾ1 Ӵ֣4 »ߣ7 ԣ27 ʾ
	    case 'm':
	      csi_m ();
	      break;
// ַc 'r'ʾùʼкźֹкš
	    case 'r':
	      if (par[0])
		par[0]--;
	      if (!par[1])
		par[1] = video_num_lines;
	      if (par[0] < par[1] && par[1] <= video_num_lines)
		{
		  top = par[0];
		  bottom = par[1];
		}
	      break;
// ַc 's'ʾ浱ǰλá
	    case 's':
	      save_cur ();
	      break;
// ַc 'u'ʾָ굽ԭλô
	    case 'u':
	      restore_cur ();
	      break;
	    }
	}
    }
// õĹλãʾ͹ʾλá
  set_cursor ();
}

/*
* void con_init(void);
*
* This routine initalizes console interrupts, and does nothing
* else. If you want the screen to clear, call tty_write with
* the appropriate escape-sequece.
*
* Reads the information preserved by setup.s to determine the current display
* type and sets everything accordingly.
*/
/*
* void con_init(void);
* ӳʼ̨жϣʲôĻɾĻʹ
* ʵתַеtty_write()
*
* ȡsetup.s 򱣴Ϣȷǰʾͣز
*/
void
con_init (void)
{
  register unsigned char a;
  char *display_desc = "????";
  char *display_ptr;

  video_num_columns = ORIG_VIDEO_COLS;	// ʾʾַ
  video_size_row = video_num_columns * 2;	// ÿʹֽ
  video_num_lines = ORIG_VIDEO_LINES;	// ʾʾַ
  video_page = ORIG_VIDEO_PAGE;	// ǰʾҳ档
  video_erase_char = 0x0720;	// ַ(0x20 ʾַ 0x07 )

// ԭʼʾģʽ7ʾǵɫʾ
  if (ORIG_VIDEO_MODE == 7)	/* Is this a monochrome display? */
    {
      video_mem_start = 0xb0000;	// õӳڴʼַ
      video_port_reg = 0x3b4;	// õĴ˿ڡ
      video_port_val = 0x3b5;	// õݼĴ˿ڡ
// BIOS жint 0x10 0x12 õʾģʽϢжʾɫʾǲɫʾ
// ʹжϹõBX Ĵֵ0x10˵EGA ˳ʼ
// ʾΪEGA ɫʹӳڴĩ˵ַΪ0xb8000ʾַΪ'EGAm'
// ϵͳʼڼʾַʾĻϽǡ
      if ((ORIG_VIDEO_EGA_BX & 0xff) != 0x10)
	{
	  video_type = VIDEO_TYPE_EGAM;	// ʾ(EGA ɫ)
	  video_mem_end = 0xb8000;	// ʾڴĩ˵ַ
	  display_desc = "EGAm";	// ʾַ
	}
// BX Ĵֵ0x10˵ǵɫʾMDAӦ
      else
	{
	  video_type = VIDEO_TYPE_MDA;	// ʾ(MDA ɫ)
	  video_mem_end = 0xb2000;	// ʾڴĩ˵ַ
	  display_desc = "*MDA";	// ʾַ
	}
    }
// ʾģʽΪ7ΪɫģʽʱõʾڴʼַΪ0xb800ʾĴ
// ˿ڵַΪ0x3d4ݼĴ˿ڵַΪ0x3d5
  else				/* If not, it is color. */
    {
      video_mem_start = 0xb8000;	// ʾڴʼַ
      video_port_reg = 0x3d4;	// òɫʾĴ˿ڡ
      video_port_val = 0x3d5;	// òɫʾݼĴ˿ڡ
// жʾBX 0x10˵EGA ʾ
      if ((ORIG_VIDEO_EGA_BX & 0xff) != 0x10)
	{
	  video_type = VIDEO_TYPE_EGAC;	// ʾ(EGA ɫ)
	  video_mem_end = 0xbc000;	// ʾڴĩ˵ַ
	  display_desc = "EGAc";	// ʾַ
	}
// BX Ĵֵ0x10˵CGA ʾӦ
      else
	{
	  video_type = VIDEO_TYPE_CGA;	// ʾ(CGA)
	  video_mem_end = 0xba000;	// ʾڴĩ˵ַ
	  display_desc = "*CGA";	// ʾַ
	}
    }

/* Let the user known what kind of display driver we are using */
/* û֪ʹһʾ */

// ĻϽʾʾַõķֱӽַдʾڴӦλô
// Ƚʾָdisplay_ptr ָĻһҶ˲4 ַ(ÿַ2 ֽڣ˼8)
  display_ptr = ((char *) video_mem_start) + video_size_row - 8;
// Ȼѭַеַÿһַտһֽڡ
  while (*display_desc)
    {
      *display_ptr++ = *display_desc++;	// ַ
      display_ptr++;		// տֽλá
    }

/* Initialize the variables used for scrolling (mostly EGA/VGA) */
/* ʼڹı(ҪEGA/VGA) */

  origin = video_mem_start;	// ʼʾڴַ
  scr_end = video_mem_start + video_num_lines * video_size_row;	// ڴַ
  top = 0;			// кš
  bottom = video_num_lines;	// кš

  gotoxy (ORIG_X, ORIG_Y);	// ʼλx,y ͶӦڴλpos
  set_trap_gate (0x21, &keyboard_interrupt);	// üжš
  outb_p (inb_p (0x21) & 0xfd, 0x21);	// ȡ8259A жԼжϵΣIRQ1
  a = inb_p (0x61);		// ӳٶȡ̶˿0x61(8255A ˿PB)
  outb_p (a | 0x80, 0x61);	// ýֹ̹(λ7 λ)
  outb (a, 0x61);		// ̹Ըλ̲
}

/* from bsd-net-2: */

//// ֹͣ
// λ8255A PB ˿ڵλ1 λ0
void
sysbeepstop (void)
{
/* disable counter 2 *//* ֹʱ2 */
  outb (inb_p (0x61) & 0xFC, 0x61);
}

int beepcount = 0;

// ͨ
// 8255A оƬPB ˿ڵλ1 Ŀźţλ0 8253 ʱ2 źţöʱ
// ΪƵʡҪʹҪȿPB ˿
// λ1 λ0λȻöʱһĶʱƵʼɡ
static void
sysbeep (void)
{
/* enable counter 2 *//* ʱ2 */
  outb_p (inb_p (0x61) | 3, 0x61);
/* set command for counter 2, 2 byte write *//* öʱ2  */
  outb_p (0xB6, 0x43);
/* send 0x637 for 750 HZ *//* ƵΪ750HZͶʱֵ0x637 */
  outb_p (0x37, 0x42);
  outb (0x06, 0x42);
/* 1/8 second *//* ʱΪ1/8  */
  beepcount = HZ / 8;
}
