?? console.c.bak
字號:
/** linux/kernel/console.c** (C) 1991 Linus Torvalds*/#include <set_seg.h>
/** console.c** 該模塊實現控制臺輸入輸出功能* 'void con_init(void)'* 'void con_write(struct tty_queue * queue)'* 希望這是一個非常完整的VT102 實現。** 感謝John T Kohl 實現了蜂鳴指示。*//** 注意!!! 我們有時短暫地禁止和允許中斷(在將一個字(word)放到視頻IO),但即使* 對于鍵盤中斷這也是可以工作的。因為我們使用陷阱門,所以我們知道在獲得一個* 鍵盤中斷時中斷是不允許的。希望一切均正常。*//** 檢測不同顯示卡的代碼大多數是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> // 系統頭文件。定義了設置或修改描述符/中斷門等的嵌入式匯編宏。/** 這些是設置子程序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 16extern void keyboard_interrupt (void); // 鍵盤中斷處理程序(keyboard.S)。static unsigned char video_type = 0; /* Type of display being used *//* 使用的顯示類型 */static unsigned long video_num_columns = 0; /* Number of text columns *//* 屏幕文本列數 */static unsigned long video_size_row = 0; /* Bytes per row *//* 每行使用的字節數 */static unsigned long video_num_lines = 0; /* Number of test lines *//* 屏幕文本行數 */static unsigned char video_page = 0; /* Initial video page *//* 初始顯示頁面 */static unsigned long video_mem_start = 0; /* Start of video RAM *//* 顯示內存起始地址 */static unsigned long video_mem_end = 0; /* End of video RAM (sort of) *//* 顯示內存結束(末端)地址 */static unsigned short video_port_reg = 0; /* Video register select port *//* 顯示控制索引寄存器端口 */static unsigned short video_port_val = 0; /* Video register value port *//* 顯示控制數據寄存器端口 */static unsigned short video_erase_char = 0; /* Char+Attrib to erase with *//* 擦除字符屬性與字符(0x0720) */// 以下這些變量用于屏幕卷屏操作。static unsigned long origin = 0; /* Used for EGA/VGA fast scroll */// scr_start。/* 用于EGA/VGA 快速滾屏 */// 滾屏起始內存地址。static unsigned long scr_end = 0; /* Used for EGA/VGA fast scroll *//* 用于EGA/VGA 快速滾屏 */// 滾屏末端內存地址。static unsigned long pos = 0; // 當前光標對應的顯示內存位置。static unsigned long x = 0, y = 0; // 當前光標位置。static unsigned long top = 0, bottom = 0; // 滾動時頂行行號;底行行號。// state 用于標明處理ESC 轉義序列時的當前步驟。npar,par[]用于存放ESC 序列的中間處理參數。static unsigned long state = 0; // ANSI 轉義字符序列處理狀態。static unsigned long npar = 0, par[NPAR] = {0}; // 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,y,并修正pos 指向光標在顯示內存中的對應位置。static _inline voidgotoxy (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 voidset_origin (void){ cli ();// 首先選擇顯示控制數據寄存器r12,然后寫入卷屏起始地址高字節。向右移動9 位,表示向右移動// 8 位,再除以2(2 字節代表屏幕上1 字符)。是相對于默認顯示內存操作的。 outb_p (12, video_port_reg); outb_p ((unsigned char)(0xff & ((origin - video_mem_start) >> 9)), video_port_val);// 再選擇顯示控制數據寄存器r13,然后寫入卷屏起始地址底字節。向右移動1 位表示除以2。 outb_p (13, video_port_reg); outb_p ((unsigned char)(0xff & ((origin - video_mem_start) >> 1)), video_port_val); sti ();}//// 向上卷動一行(屏幕窗口向下移動)。// 將屏幕窗口向下移動一行。參見程序列表后說明。static voidscrup (void){
unsigned long t1,t2,t3;
// 如果顯示類型是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 個長字。
t1 = (video_num_lines - 1) * video_num_columns >> 1;
_asm { pushf
mov ecx,t1;
// mov ecx,((video_num_lines - 1) * video_num_columns >> 1);
mov ax,video_erase_char;
mov edi,video_mem_start;
mov esi,origin;
cld; // 清方向位。
rep movsd; // 重復操作,將當前屏幕內存數據移動到顯示內存起始處。
mov ecx,video_num_columns; // ecx=1 行字符數。
rep stosw; // 在新行上填入空格字符。 popf
}/* __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。 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(屏幕對應內存最后一行開始處); t1 = scr_end - video_size_row;
_asm { pushf
mov ax,video_erase_char;
mov ecx,video_num_columns;
mov edi,t1;
// mov edi,(scr_end - video_size_row);
cld; // 清方向位。
rep stosw; // 重復操作,在新出現行上填入擦除字符(空格字符)。 popf
}
/* __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 {
t1 = (bottom - top - 1) * video_num_columns >> 1;
t2 = origin + video_size_row * top;
t3 = origin + video_size_row * (top + 1);
_asm { pushf
mov ecx,t1;
// mov ecx,((bottom - top - 1) * video_num_columns >> 1);
mov edi,t2;
// mov edi,(origin + video_size_row * top);
mov esi,t3;
// mov esi,(origin + video_size_row * (top + 1));
mov ax,video_erase_char;
cld; // 清方向位。
rep movsd; // 循環操作,將top+1 到bottom 行 所對應的內存塊移到top 行開始處。
mov ecx,video_num_columns; // ecx = 1 行字符數。
rep stosw; // 在新行上填入擦除字符。 popf
}
/* __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");*/ } }// 如果顯示類型不是EGA(是MDA),則執行下面移動操作。因為MDA 顯示控制卡會自動調整超出顯示范圍// 的情況,也即會自動翻卷指針,所以這里不對屏幕內容對應內存超出顯示內存的情況單獨處理。處理// 方法與EGA 非整屏移動情況完全一樣。 else /* Not EGA/VGA */ {
t1 = (bottom - top - 1) * video_num_columns >> 1;
t2 = origin + video_size_row * top;
t3 = origin + video_size_row * (top + 1);
_asm { pushf
mov ecx,t1;
// mov ecx,((bottom - top - 1) * video_num_columns >> 1);
mov edi,t2;
// mov edi,(origin + video_size_row * top);
mov esi,t3;
// mov esi,(origin + video_size_row * (top + 1));
mov ax,video_erase_char;
cld;
rep movsd;
mov ecx,video_num_columns;
rep stosw; popf
}/* __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 voidscrdown (void){
unsigned long t1,t2,t3;// 如果顯示類型是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 個長字。
t1 = (bottom - top - 1) * video_num_columns >> 1;
t2 = origin + video_size_row * bottom - 4;
t3 = origin + video_size_row * (bottom - 1) - 4;
_asm {
mov ecx,t1;
// mov ecx,((bottom - top - 1) * video_num_columns >> 1);
mov edi,t2;
// mov edi,(origin + video_size_row * bottom - 4);
mov esi,t3;
// mov esi,(origin + video_size_row * (bottom - 1) - 4);
mov ax,video_erase_char;
std; // 置方向位。
rep movsd; // 重復操作,向下移動從top 行到bottom-1 行對應的內存數據。
add edi,2; /* %edi 已經減4,因為也是方向填擦除字符 */
mov ecx,video_num_columns; // 置ecx=1 行字符數。
rep stosw; // 將擦除字符填入上方新行中。
}/* __asm__ ("std\n\t" "rep\n\t" "movsl\n\t" // "addl $2,%%edi\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 * bottom - 4), "S" (origin + video_size_row * (bottom - 1) - 4):"ax", "cx", "di", "si");*/ }// 如果不是EGA 顯示類型,則執行以下操作(目前與上面完全一樣)。 else /* Not EGA/VGA */ {
t1 = (bottom - top - 1) * video_num_columns >> 1;
t2 = origin + video_size_row * bottom - 4;
t3 = origin + video_size_row * (bottom - 1) - 4;
_asm {
mov ecx,t1;
// mov ecx,((bottom - top - 1) * video_num_columns >> 1);
mov edi,t2;
// mov edi,(origin + video_size_row * bottom - 4);
mov esi,t3;
// mov esi,(origin + video_size_row * (bottom - 1) - 4);
mov ax,video_erase_char;
std;
rep movsd;
add edi,2;/* %edi has been decremented by 4 */
mov ecx,video_num_columns;
rep stosw;
} /* __asm__ ("std\n\t" "rep\n\t" "movsl\n\t" "addl $2,%%edi\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 * bottom - 4), "S" (origin + video_size_row * (bottom - 1) - 4):"ax", "cx", "di", "si");*/ }}//// 光標位置下移一行(lf - line feed 換行)。static voidlf (void){// 如果光標沒有處在倒數第2 行之后,則直接修改光標當前行變量y++,并調整光標對應顯示內存位置// pos(加上屏幕一行字符所對應的內存長度)。 if (y + 1 < bottom) { y++; pos += video_size_row; return; }// 否則需要將屏幕內容上移一行。 scrup ();}
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -