`
ladymaidu
  • 浏览: 679600 次
文章分类
社区版块
存档分类
最新评论

linux 0.11 内核学习 -- console.c,控制台

 
阅读更多

参考《linux内核完全注释》和网上相关文章

/*

* 控制台显示操作

*/

/*

* 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.

*/

/**********************

* con_init con_write *

**********************/

/*

* 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.

*/

/*

* Code to check for different video-cards mostly by Galen Hunt,

* <g-hunt@ee.utah.edu>

*/

#include <linux/sched.h>

#include <linux/tty.h> // 定义tty_struct结构,串行通信方面的参数和常量

#include <asm/io.h> // 硬件对应的汇编io指令

#include <asm/system.h>

/*

* These are set up by the setup-routine at boot-time:

*/

#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); // 键盘中断处理程序

/* 全局变量,指示显示器的相关信息 */

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,擦除字符属性与字符 */

/* 下面的全局变量是屏幕卷屏操作所使用 */

static unsigned long origin; /* Used for EGA/VGA fast scroll */ // 滚屏起始内存地址

static unsigned long scr_end; /* Used for EGA/VGA fast scroll */ // 滚屏末端内存地址

static unsigned long pos; // 当前光标位置相对于显存的位置

static unsigned long x,y; // 当前光标

static unsigned long top,bottom; // 滚动时顶行行号和底行行号

/**************************************************************/

static unsigned long state=0; // 处理ESC 转义序列时的当前进行到哪

static unsigned long npar,par[NPAR]; // 放ESC 序列的中间处理参数

/************************************************************/

static unsigned long ques=0;

static unsigned char attr=0x07; // char

static void sysbeep(void);

/*

* this is what the terminal answers to a ESC-Z or csi0c

* query (= vt100 response).

*/

#define RESPONSE "/033[?1;2c"

/* NOTE! gotoxy thinks x==video_num_columns is ok */

/* 更新当前光标位置 */

/*

* |----------------------------------------------------------> x

* | video_size_row

* |--------------------------------------| -|------> top顶行行号

* |--------------------------------------| |

* | | |

* | * * * | | --> video_num_row

* P(x, y) |

* |--------------------------------------| -|------> button底行行号

* |--------------------------------------|

* | |

* | video_num_columns

* |

* | y

*

* all video memory = video_size_row * video_num_row

* the screen point P(x, y) is mapping the main memory

* video_mem_start + y * video_size_row + (x << 1)

*/

static inline void gotoxy(unsigned int new_x,unsigned int new_y)

{

if (new_x > video_num_columns || new_y >= video_num_lines)

return; // 错误检查

x=new_x; // 更新x,y

y=new_y;

pos=origin + y*video_size_row + (x<<1); // 重新计算全局变量pos值

}

/* 设置滚屏起始显示内存地址 */

static inline void set_origin(void)

{

cli();

outb_p(12, video_port_reg); // 向择显示控制数据寄存器r12写入卷屏起始地址高字节

outb_p(0xff&((origin-video_mem_start)>>9), video_port_val);

outb_p(13, video_port_reg); // 向显示控制数据寄存器r13写入卷屏起始地址底字节

outb_p(0xff&((origin-video_mem_start)>>1), video_port_val);

sti();

}

/* 向上卷动一行 */

static void scrup(void)

{

/*

* 我们可以认为的是显存就是一片内存区域,显示器在决定显示部分时,根据的是

* 显示控制器的的内存起始位置值。但是在实际的操作系统中,显存是固定在一个

* 区域内的。

*

* 将屏幕看作是显示内存上对应屏幕内容的一个窗口

*/

/* EGA/VGA */

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 += video_size_row;

pos += video_size_row;

scr_end += video_size_row;

// 如果屏幕末端最后一个显示字符所对应的显示内存指针scr_end

// 超出了实际显示内存的末端。保证所有当前屏幕数据都落在显示

// 内存范围内

if (scr_end > video_mem_end)

{

// 屏幕内容内存数据移动到显示内存的起始位置video_mem_start

// 处,并在出现的新行上填入空格字符

__asm__("cld/n/t"

"rep/n/t"

"movsl/n/t" // 移动到显示内存起始处

"movl _video_num_columns,%1/n/t"

"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 -= origin-video_mem_start;

pos -= origin-video_mem_start;

origin = video_mem_start;

}

else // scr_end 没有超出显示内存的末端video_mem_end

{

// 新行上填入擦除字符(空格字符)

__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(); // 向显示控制器中写入新的屏幕内容对应的内存起始位置值

}

else // 表示不是整屏移动

{

__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");

}

}

else /* Not EGA/VGA */

{

// 因为MDA 显示控制卡会自动调整超出显示范围的情况,也即会自动

// 翻卷指针,所以这里不对屏幕内容对应内存超出显示内存的情况单独处理

__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");

}

}

/* 向下卷动一行,该函数只是根据top和button来实现的卷屏 */

static void scrdown(void)

{

if (video_type == VIDEO_TYPE_EGAC || video_type == VIDEO_TYPE_EGAM)

{

__asm__("std/n/t" // 置方向位

"rep/n/t" // 重复操作,向下移动从top 行到bottom-1 行

"movsl/n/t" // 对应的内存数据

"addl $2,%%edi/n/t" /* %edi has been decremented by 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");

}

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");

}

}

/* 光标位置下移一行 */

static void lf(void)

{

if (y+1<bottom)

{

y++;

pos += video_size_row;

return;

}

scrup(); // 函数还没有退出,将屏幕内容上移一行

}

/* 光标上移一行,同上lf */

static void ri(void)

{

if (y>top) {

y--;

pos -= video_size_row;

return;

}

scrdown();

}

/* 光标回到第1 列(0 列)左端 */

static void cr(void)

{

pos -= x<<1; // x / 2

x=0;

}

/* 擦除光标前一字符 */

static void del(void)

{

if (x) //如果光标没有处在0 列

{

pos -= 2; // 光标对应内存位置指针pos 后退2 字节

x--; // 当前光标变量列值减1

*(unsigned short *)pos = video_erase_char; // 光标所在位置字符擦除

}

}

/* 删除屏幕上的相关区域 */

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");

}

/* 根据par的不同,删除与行相关的字符,并填充其他字符 */

static void csi_K(int par)

{

long count __asm__("cx"); // 设置寄存器变量

long start __asm__("di");

/* 计算count和start的值,以便在下面实现字符的擦除 */

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;

}

// 使用擦除字符填写删除字符的地方

__asm__("cld/n/t"

"rep/n/t"

"stosw/n/t"

::"c" (count),

"D" (start),"a" (video_erase_char)

:"cx","di");

}

/* 重新设置字符显示方式 */

void csi_m(void)

{

int i;

for (i=0;i<=npar;i++)

switch (par[i])

{

case 0:attr=0x07;break; // n = 0 正常显示

case 1:attr=0x0f;break; // 1 加粗

case 4:attr=0x0f;break; // 4 加下划线

case 7:attr=0x70;break; // 7 反显

case 27:attr=0x07;break; // 27 正常显示

}

}

/* 根据显示内存光标对应位置pos,设置显示控制器光标的显示位置 */

static inline void set_cursor(void)

{

cli();

// 使用索引寄存器端口选择显示控制数据寄存器r14(光标当前显示位置高字节)

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();

}

/* ,终端相应外部命令,将响应序列放入读缓冲队列中 */

/*

* console (tty同时对应screen)

*

* |---struct tty_queue read_q

* console --> tty_struct -|

* |---struct tty_queue write_q

*

*/

static void respond(struct tty_struct * tty)

{

char * p = RESPONSE; // "/033[?1;2c"

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;

/*

* |----------|-|----------------------------|

* |-----swap---->|

* 光标开始的所有字符右移一格,并将擦除字符插入在光标所在处

*/

while (i++<video_num_columns)

{

tmp=*p;

*p=old;

old=tmp;

p++;

}

}

/* 在光标处插入一行,并且光标将处在新的空行上 */

static void insert_line(void)

{

int oldtop,oldbottom;

oldtop=top;

oldbottom=bottom;

top=y; // 设置屏幕卷动开始行

bottom = video_num_lines; // 设置屏幕卷动最后行

scrdown();

top=oldtop;

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; // 最后一个字符处填入擦除字符(空格字符)

}

/* 删除光标所在行,同insert_line */

static void delete_line(void)

{

int oldtop,oldbottom;

oldtop=top;

oldbottom=bottom;

top=y;

bottom = video_num_lines;

scrup();

top=oldtop;

bottom=oldbottom;

}

/* 在光标处插入nr 个字符 */

static void csi_at(unsigned int nr)

{

if (nr > video_num_columns) // 如果插入的字符数大于一行字符数

nr = video_num_columns; // 截为一行字符数

else if (!nr) // nr == 0 ?

nr = 1; // nr = 1,插入1 个字符

while (nr--) // 循环插入指定的字符数

insert_char();

}

/* 在光标位置处插入nr 行 */

static void csi_L(unsigned int nr)

{

if (nr > video_num_lines)

nr = video_num_lines;

else if (!nr)

nr = 1;

while (nr--)

insert_line();

}

/* 删除光标处的nr 个字符,调用函数delete_char */

static void csi_P(unsigned int nr)

{

if (nr > video_num_columns)

nr = video_num_columns;

else if (!nr)

nr = 1;

while (nr--)

delete_char();

}

/* 删除光标处的nr 行 */

static void csi_M(unsigned int nr)

{

if (nr > video_num_lines)

nr = video_num_lines;

else if (!nr)

nr=1;

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 = CHARS(tty->write_q); // 首先取得写缓冲队列中现有字符数nr

while (nr--) // 针对每个字符进行处理

{

// 从写队列中取一字符c,根据前面所处理字符的状态state 分别处理

GETCH(tty->write_q,c);

switch(state)

{

case 0:

if (c>31 && c<127)

{

// 符不是控制字符(c>31),并且也不是扩展字符(c<127)

if (x>=video_num_columns)

{

// 若当前光标处在行末端或末端以外,则将光标移到下行头列。

// 并调整光标位置对应的内存指针pos

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;

if (c=='[') // 如果字符c 是'[',则将状态state 转到2

state=2;

else if (c=='E') // 如果字符c 是'E',则光标移到下一行开始处(0 列)

gotoxy(0,y+1);

else if (c=='M') // 如果字符c 是'M',则光标上移一行

ri();

else if (c=='D') // 如果字符c 是'D',则光标下移一行

lf();

else if (c=='Z') // 如果字符c 是'Z',则发送终端应答字符序列

respond(tty);

else if (x=='7') // 如果字符c 是'7',则保存当前光标位置

save_cur();

else if (x=='8') // 如果字符c 是'8',则恢复到原保存的光标位置

restore_cur();

break;

// 如果原状态是1,并且上一字符是'[',则转到状态2 来处理

case 2:

// 首先对ESC 转义字符序列参数使用的处理数组par[]清零

for(npar=0;npar<NPAR;npar++)

par[npar]=0;

npar=0; // 索引变量npar 指向首项

state=3; // 设置状态3

if (ques=(c=='?')) // 若此时字符不是'?',则直接转到状态3 去处理

break;

// 如果原来是状态2;或者原来就是状态3,但原字符是';'或数字,则在下面处理

case 3:

if (c==';' && npar<NPAR-1)

{ // 如果字符c 是分号';',并且数组par 未满,则索引值加1

npar++;

break;

}

else if (c>='0' && c<='9')

{

// 如果字符c 是数字字符'0'-'9',则将该字符转换成数值并与npar

// 所索引的项组成10 进制数

par[npar]=10*par[npar]+c-'0';

break;

}

else // 否则转到状态4

state=4;

// 如果原状态是状态3,并且字符不是';'或数字,则转到状态4 处理

case 4:

state=0; // 首先复位状态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',则第一个参数代表以光标所处位置清屏的方式

case 'J':

csi_J(par[0]);

break;

// 如果字符c 是'K',则第一个参数代表以光标所在位置对行中字符进行

// 删除处理的方式

case 'K':

csi_K(par[0]);

break;

// 如果字符c 是'L',表示在光标位置处插入n 行

case 'L':

csi_L(par[0]);

break;

// 如果字符c 是'M',表示在光标位置处删除n 行

case 'M':

csi_M(par[0]);

break;

// 如果字符c 是'P',表示在光标位置处删除n 个字符

case 'P':

csi_P(par[0]);

break;

// 如果字符c 是'@',表示在光标位置处插入n 个字符

case '@':

csi_at(par[0]);

break;

// 如果字符c 是'm',表示改变光标处字符的显示属性

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.

*/

/*

* 读取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 是属性)

// 是单色显示器?

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 获得的显示模式信息,判断

* 显示卡单色显示卡还是彩色显示卡

*/

if ((ORIG_VIDEO_EGA_BX & 0xff) != 0x10)

{

video_type = VIDEO_TYPE_EGAM; // 设置显示类型(EGA 单色)

video_mem_end = 0xb8000; // 设置显示内存末端地址

display_desc = "EGAm"; // 设置显示描述字符串

}

else

{

video_type = VIDEO_TYPE_MDA; // 设置显示类型(MDA 单色)

video_mem_end = 0xb2000; // 设置显示内存末端地址

display_desc = "*MDA"; // 设置显示描述字符串

}

}

else /* If not, it is color. */

{

video_mem_start = 0xb8000; // 显示内存起始地址

video_port_reg = 0x3d4; // 设置彩色显示索引寄存器端口

video_port_val = 0x3d5; // 设置彩色显示数据寄存器端口

// 再判断显示卡类别

if ((ORIG_VIDEO_EGA_BX & 0xff) != 0x10)

{

video_type = VIDEO_TYPE_EGAC;

video_mem_end = 0xbc000;

display_desc = "EGAc";

}

else

{

video_type = VIDEO_TYPE_CGA;

video_mem_end = 0xba000;

display_desc = "*CGA";

}

}

/* Let the user known what kind of display driver we are using */

/* 在屏幕的右上角显示显示描述字符串。采用的方法是直接将字符串写到显示内存的相应位置处 */

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) */

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 中对键盘中断的屏蔽

a=inb_p(0x61); // // 延迟读取键盘端口0x61

outb_p(a|0x80,0x61); // 设置禁止键盘工作

outb(a,0x61); // 再允许键盘工作,用以复位键盘操作

}

/* from bsd-net-2: */

/* 停止蜂鸣 */

void sysbeepstop(void)

{

/* disable counter 2 */

outb(inb_p(0x61)&0xFC, 0x61);

}

int beepcount = 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 */

beepcount = HZ/8;

}

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics