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;
}
轉載于:https://www.cnblogs.com/xuqiang/archive/2010/02/01/1953774.html
總結
以上是生活随笔為你收集整理的linux 0.11 内核学习 -- console.c,控制台的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux——线程(2)
- 下一篇: 抓取新浪的每日星座运势