linux 0.11 内核学习 -- console.c,控制台
来源:互联网 发布:税友软件下载 编辑:程序博客网 时间:2024/06/07 03:12
参考《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_MDA0x10/* Monochrome Text Display,单色文本*/
#define VIDEO_TYPE_CGA0x11/* CGA Display,CGA 显示器 */
#define VIDEO_TYPE_EGAM0x20/* EGA/VGA in Monochrome Mode,EGA/VGA 单色*/
#define VIDEO_TYPE_EGAC0x21/* EGA/VGA in Color Mode,EGA/VGA 彩色*/
#define NPAR 16
extern void keyboard_interrupt(void);// 键盘中断处理程序
/*全局变量,指示显示器的相关信息 */
static unsigned charvideo_type;/* Type of display being used*/
static unsigned longvideo_num_columns;/* Number of text columns*/
static unsigned longvideo_size_row;/* Bytes per row,每行使用的字节数 */
static unsigned longvideo_num_lines;/* Number of test lines,屏幕文本行数*/
static unsigned charvideo_page;/* Initial video page,初始显示页面 */
static unsigned longvideo_mem_start;/* Start of video RAM*/
static unsigned longvideo_mem_end;/* End of video RAM (sort of)*/
static unsigned shortvideo_port_reg;/* Video register select port*/
/* 显示控制索引寄存器端口 */
static unsigned shortvideo_port_val;/* Video register value port*/
static unsigned shortvideo_erase_char;/* Char+Attrib to erase with,擦除字符属性与字符 */
/*下面的全局变量是屏幕卷屏操作所使用 */
static unsigned longorigin;/* Used for EGA/VGA fast scroll*/// 滚屏起始内存地址
static unsigned longscr_end;/* Used for EGA/VGA fast scroll*/// 滚屏末端内存地址
static unsigned longpos;// 当前光标位置相对于显存的位置
static unsigned longx,y;// 当前光标
static unsigned longtop,bottom;// 滚动时顶行行号和底行行号
/**************************************************************/
static unsigned longstate=0;// 处理ESC 转义序列时的当前进行到哪
static unsigned longnpar,par[NPAR];// 放ESC 序列的中间处理参数
/************************************************************/
static unsigned longques=0;
static unsigned charattr=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;
}
- linux 0.11 内核学习 -- console.c,控制台
- linux 0.11 内核学习 -- namei.c。
- linux 0.11 内核学习 -- truncate.c
- linux 0.11 内核学习 -- read_write.c
- linux 0.11 内核学习 -- char_dev.c
- linux 0.11 内核学习 -- buffer.c
- linux 0.11 内核学习 -- pipe.c
- linux 0.11 内核学习 -- file_dev.c
- linux 0.11 内核学习 -- exec.c
- linux 0.11 内核学习 -- bitmap.c
- linux 0.11 内核学习 -- block_dev.c
- linux 0.11 内核学习 -- fcntl.c
- linux 0.11 内核学习 -- inode.c
- linux 0.11 内核学习 -- ioctl.c
- C#WinForm程序显示控制台窗口Console
- Linux重定向console口控制台
- linux中 终端terminal 控制台console区别
- 修改Linux控制台(console)的显示信息。
- java.sql.Date与java.util.date
- EasyEclipse PHP自动提示扩展配置
- 骆驼命名法,帕斯卡命名法和匈牙利命名法
- A word for WinDbg
- Bourne Shell 编程入门(转)
- linux 0.11 内核学习 -- console.c,控制台
- Lucene中文分词 庖丁解牛 2.0.0版本发布
- 没有什么可以阻挡
- 黑盒测试、白盒测试和灰盒测试的基本概念
- A word for WinDbg (2)
- VS2008创建或加载Silverlight项目出错的解决办法
- oracle 物化视图 -循序渐进MView(三) 基于ROWID的MView
- 离职
- XML 的转移字符